simh-3.8.1/0000755000175000017500000000000011147615611010644 5ustar vlmvlmsimh-3.8.1/H316/0000755000175000017500000000000011111656424011263 5ustar vlmvlmsimh-3.8.1/H316/h316_sys.c0000644000175000017500000003460411107406106013010 0ustar vlmvlm/* h316_sys.c: Honeywell 316/516 simulator interface Copyright (c) 1999-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 01-Dec-04 RMS Fixed fprint_opr calling sequence 24-Oct-03 RMS Added DMA/DMC support 17-Sep-01 RMS Removed multiconsole support */ #include "h316_defs.h" #include extern DEVICE cpu_dev; extern UNIT cpu_unit; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE tty_dev; extern DEVICE lpt_dev; extern DEVICE clk_dev; extern DEVICE dp_dev; extern DEVICE fhd_dev; extern DEVICE mt_dev; extern REG cpu_reg[]; extern uint16 M[]; 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 maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "H316"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &ptr_dev, &ptp_dev, &tty_dev, &lpt_dev, &clk_dev, &dp_dev, &fhd_dev, &mt_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unimplemented instruction", "Unimplemented I/O device", "HALT instruction", "Breakpoint", "Indirect address loop", "DMA error", "MT write protected", "DP write overrun, track destroyed", "DP track format invalid" }; /* Binary loader Tbs. */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { return SCPE_FMT; } /* Symbol tables */ #define I_V_FL 16 /* flag start */ #define I_M_FL 07 /* flag mask */ #define I_V_NPN 0 /* no operand */ #define I_V_MRF 1 /* mem ref */ #define I_V_MRX 2 /* mem ref, no idx */ #define I_V_IOT 3 /* I/O */ #define I_V_SHF 4 /* shift */ #define I_V_SK0 5 /* skip 0 */ #define I_V_SK1 6 /* skip 1 */ #define I_NPN (I_V_NPN << I_V_FL) #define I_MRF (I_V_MRF << I_V_FL) #define I_MRX (I_V_MRX << I_V_FL) #define I_IOT (I_V_IOT << I_V_FL) #define I_SHF (I_V_SHF << I_V_FL) #define I_SK0 (I_V_SK0 << I_V_FL) #define I_SK1 (I_V_SK1 << I_V_FL) static const int32 masks[] = { 0177777, 0136000, 0176000, 0176000, 0177700, 0177000, 0177000 }; static const char *opcode[] = { "HLT", "SGL", "DBL", "DXA", "EXA", "RMP", "SCA", "INK", "NRM", "IAB", "ENB", "INH", "ERM", "CHS", "CRA", "SSP", "RCB", "CSA", "CMA", "TCA", "SSM", "SCB", "CAR", "CAL", "ICL", "AOA", "ACA", "ICR", "ICA", "NOP", "SKP", "SSR", "SSS", "JMP", "JMP*", "LDA", "LDA*", "ANA", "ANA*", "STA", "STA*", "ERA", "ERA*", "ADD", "ADD*", "SUB", "SUB*", "JST", "JST*", "CAS", "CAS*", "IRS", "IRS*", "IMA", "IMA*", "MPY", "MPY*", "DIV", "DIV*", "STX", "STX*", "LDX", "LDX*", "LRL", "LRS", "LRR", "LGR", "ARS", "ARR", "LLL", "LLS", "LLR", "LGL", "ALS", "ALR", "OCP", "SKS", "INA", "OTA", "SMK", "SPL", "SPN", "SLZ", /* encode only */ "SZE", "SR1", "SR2", "SR3", "SR4", "SRC", "SMI", "SPS", "SLN", "SNZ", "SS1", "SS2", "SS3", "SS4", "SSC", NULL, NULL, /* decode only */ NULL }; static const int32 opc_val[] = { 0000000+I_NPN, 0000005+I_NPN, 0000007+I_NPN, 0000011+I_NPN, 0000013+I_NPN, 0000021+I_NPN, 0000041+I_NPN, 0000043+I_NPN, 0000101+I_NPN, 0000201+I_NPN, 0000401+I_NPN, 0001001+I_NPN, 0001401+I_NPN, 0140024+I_NPN, 0140040+I_NPN, 0140100+I_NPN, 0140200+I_NPN, 0140320+I_NPN, 0140401+I_NPN, 0140407+I_NPN, 0140500+I_NPN, 0140600+I_NPN, 0141044+I_NPN, 0141050+I_NPN, 0141140+I_NPN, 0141206+I_NPN, 0141216+I_NPN, 0141240+I_NPN, 0141340+I_NPN, 0101000+I_NPN, 0100000+I_NPN, 0100036+I_NPN, 0101036+I_NPN, 0002000+I_MRF, 0102000+I_MRF, 0004000+I_MRF, 0104000+I_MRF, 0006000+I_MRF, 0106000+I_MRF, 0010000+I_MRF, 0110000+I_MRF, 0012000+I_MRF, 0112000+I_MRF, 0014000+I_MRF, 0114000+I_MRF, 0016000+I_MRF, 0116000+I_MRF, 0020000+I_MRF, 0120000+I_MRF, 0022000+I_MRF, 0122000+I_MRF, 0024000+I_MRF, 0124000+I_MRF, 0026000+I_MRF, 0126000+I_MRF, 0034000+I_MRF, 0134000+I_MRF, 0036000+I_MRF, 0136000+I_MRF, 0032000+I_MRX, 0132000+I_MRX, 0072000+I_MRX, 0172000+I_MRX, 0040000+I_SHF, 0040100+I_SHF, 0040200+I_SHF, 0040400+I_SHF, 0040500+I_SHF, 0040600+I_SHF, 0041000+I_SHF, 0041100+I_SHF, 0041200+I_SHF, 0041400+I_SHF, 0041500+I_SHF, 0041600+I_SHF, 0030000+I_IOT, 0070000+I_IOT, 0130000+I_IOT, 0170000+I_IOT, 0170000+I_IOT, 0100400+I_SK0, 0100200+I_SK0, 0100100+I_SK0, /* encode only */ 0100040+I_SK0, 0100020+I_SK0, 0100010+I_SK0, 0100004+I_SK0, 0100002+I_SK0, 0100001+I_SK0, 0101400+I_SK1, 0101200+I_SK1, 0101100+I_SK1, 0101040+I_SK1, 0101020+I_SK1, 0101010+I_SK1, 0101004+I_SK1, 0101002+I_SK1, 0101001+I_SK1, 0100000+I_SK0, 0101000+I_SK1, /* decode only */ -1 }; /* Operate decode Inputs: *of = output stream inst = mask bits class = instruction class code sp = space needed? Outputs: status = space needed */ void fprint_opr (FILE *of, int32 inst, int32 class) { int32 i, j, sp; for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((j == class) && (opc_val[i] & inst)) { /* same class? */ inst = inst & ~opc_val[i]; /* mask bit set? */ fprintf (of, (sp? " %s": "%s"), opcode[i]); sp = 1; } } return; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to data *uptr = pointer to unit sw = switches Outputs: return = status code */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, inst, disp; cflag = (uptr == NULL) || (uptr == &cpu_unit); inst = val[0]; if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (sw & SWMASK ('C')) { /* characters? */ fprintf (of, FMTASC ((inst >> 8) & 0177)); fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: /* no operands */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_MRF: case I_V_MRX: /* mem ref */ disp = inst & DISP; /* displacement */ fprintf (of, "%s ", opcode[i]); /* opcode */ if (inst & SC) { /* current sector? */ if (cflag) fprintf (of, "%-o", (addr & PAGENO) | disp); else fprintf (of, "C %-o", disp); } else fprintf (of, "%-o", disp); /* sector zero */ if ((j == I_V_MRF) && (inst & IDX)) fprintf (of, ",1"); break; case I_V_IOT: /* I/O */ disp = inst & 01777; /* pulse+dev */ fprintf (of, "%s %o", opcode[i], disp); break; case I_V_SHF: /* shift */ disp = -inst & SHFMASK; /* shift count */ fprintf (of, "%s %o", opcode[i], disp); break; case I_V_SK0: case I_V_SK1: /* skips */ fprint_opr (of, inst & 0777, j); /* print skips */ break; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* 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) { int32 cflag, d, i, j, k; t_stat r; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0] & 0177; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* char string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) cptr[0] & 0177) << 8) | ((t_value) cptr[1] & 0177); return SCPE_OK; } /* Instruction parse */ 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_NPN: /* no operand */ break; case I_V_IOT: /* IOT */ cptr = get_glyph (cptr, gbuf, 0); /* get pulse+dev */ d = get_uint (gbuf, 8, 01777, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; break; case I_V_SHF: /* shift */ cptr = get_glyph (cptr, gbuf, 0); /* get shift count */ d = get_uint (gbuf, 8, SHFMASK, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (-d & SHFMASK); /* store 2's comp */ break; case I_V_MRF: case I_V_MRX: /* mem ref */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ if (k = (strcmp (gbuf, "C") == 0)) { /* C specified? */ val[0] = val[0] | SC; cptr = get_glyph (cptr, gbuf, 0); } else if (k = (strcmp (gbuf, "Z") == 0)) { /* Z specified? */ cptr = get_glyph (cptr, gbuf, ','); } d = get_uint (gbuf, 8, X_AMASK, &r); /* construe as addr */ if (r != SCPE_OK) return SCPE_ARG; if (d <= DISP) /* fits? */ val[0] = val[0] | d; else if (cflag && !k && (((addr ^ d) & PAGENO) == 0)) val[0] = val[0] | (d & DISP) | SC; else return SCPE_ARG; if ((j == I_V_MRX) || (*cptr == 0)) /* indexed? */ break; cptr = get_glyph (cptr, gbuf, 0); d = get_uint (gbuf, 8, 1, &r); /* get tag */ if (r != SCPE_OK) return SCPE_ARG; if (d) /* or in index */ val[0] = val[0] | IDX; break; case I_V_SK0: case I_V_SK1: /* skips */ 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++) ; k = opc_val[i] & DMASK; if ((opcode[i] == NULL) || (((k ^ val[0]) & 0177000) != 0)) return SCPE_ARG; val[0] = val[0] | k; } break; } /* end case */ if (*cptr != 0) /* junk at end? */ return SCPE_ARG; return SCPE_OK; } simh-3.8.1/H316/h316_stddev.c0000644000175000017500000010436611107406106013466 0ustar vlmvlm/* h316_stddev.c: Honeywell 316/516 standard devices Copyright (c) 1999-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr 316/516-50 paper tape reader ptp 316/516-52 paper tape punch tty 316/516-33 teleprinter clk/options 316/516-12 real time clocks/internal options 09-Jun-07 RMS Fixed bug in clock increment (found by Theo Engel) 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 03-Apr-06 RMS Fixed bugs in punch state handling (from Theo Engel) 22-Nov-05 RMS Revised for new terminal processing routines 05-Feb-05 RMS Fixed bug in OCP '0001 (found by Philipp Hachtmann) 31-Jan-05 RMS Fixed bug in TTY print (found by Philipp Hachtmann) 01-Dec-04 RMS Fixed problem in SKS '104 (reported by Philipp Hachtmann) Fixed bug in SKS '504 Added PTR detach routine, stops motion Added PTR/PTP ASCII file support Added TTR/TTP support 24-Oct-03 RMS Added DMA/DMC support 25-Apr-03 RMS Revised for extended file support 01-Mar-03 RMS Added SET/SHOW CLK FREQ support 22-Dec-02 RMS Added break support 01-Nov-02 RMS Added 7b/8b support to terminal 30-May-02 RMS Widened POS to 32b 03-Nov-01 RMS Implemented upper case for console output 29-Nov-01 RMS Added read only unit support 07-Sep-01 RMS Moved function prototypes The ASR-33/35 reader/punch logic, and the ASCII file support for all paper tape devices, logic is taken, with grateful thanks, from Adrian Wise's H316 emulator. Teletype reader transitions: - SET TTY2 START puts the reader in RUN - XOFF from keyboard/reader stops the reader after 1-2 more characters are read - XON from program starts the reader - Detach, SET TTY2 STOP, or end of file stops the reader Teletype punch transitions: - SET TTY3 START puts the punch in RUN - XOFF from program stops the punch after 1 more character is punched - TAPE from program starts the punch after 1 character delay - Detach or SET TTY3 STOP stops the punch */ #include "h316_defs.h" #include #define UNIT_V_ASC (TTUF_V_UF + 0) /* ASCII */ #define UNIT_V_UASC (TTUF_V_UF + 1) /* Unix ASCII */ #define UNIT_ASC (1 << UNIT_V_ASC) #define UNIT_UASC (1 << UNIT_V_UASC) #define STA u3 /* state bits */ #define LF_PEND 01 /* lf pending */ #define RUNNING 02 /* tape running */ #define XON 0021 #define TAPE 0022 #define XOFF 0023 #define RUBOUT 0377 extern uint16 M[]; extern int32 PC; extern int32 stop_inst; extern int32 C, dp, ext, extoff_pending, sc; extern int32 dev_int, dev_enb; extern int32 sim_switches; extern UNIT cpu_unit; uint32 ptr_motion = 0; /* read motion */ uint32 ptr_stopioe = 0; /* stop on error */ uint32 ptp_stopioe = 0; uint32 ptp_power = 0; /* punch power, time */ int32 ptp_ptime; uint32 ttr_stopioe = 0; uint32 tty_mode = 0; /* input (0), output (1) */ uint32 tty_buf = 0; /* tty buffer */ uint32 ttr_xoff_read = 0; uint32 ttp_tape_rcvd = 0; uint32 ttp_xoff_rcvd = 0; int32 clk_tps = 60; /* ticks per second */ int32 ptrio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat ptr_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); int32 ptpio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); int32 ttyio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tty_reset (DEVICE *dptr); t_stat ttio_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ttrp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ttrp_set_start_stop (UNIT *uptr, int32 val, char *cptr, void *desc); int32 clkio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat pt_attach (UNIT *uptr, char *cptr); t_stat pt_detach (UNIT *uptr); t_stat tto_write (int32 c); t_stat ttp_write (int32 c); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_mod PTR modifiers ptr_reg PTR register list */ DIB ptr_dib = { PTR, IOBUS, 1, &ptrio }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 8) }, { FLDATA (READY, dev_int, INT_V_PTR) }, { FLDATA (ENABLE, dev_enb, INT_V_PTR) }, { FLDATA (MOTION, ptr_motion, 0) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { ORDATA (RSTATE, ptr_unit.STA, 2), REG_HIDDEN }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB pt_mod[] = { { UNIT_ATT+UNIT_ASC+UNIT_UASC, UNIT_ATT+UNIT_ASC, "ASCII", NULL }, { UNIT_ATT+UNIT_ASC+UNIT_UASC, UNIT_ATT+UNIT_ASC+UNIT_UASC, "Unix ASCII", NULL }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, pt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, &pt_attach, &pt_detach, &ptr_dib, 0 }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_mod PTP modifiers ptp_reg PTP register list */ DIB ptp_dib = { PTP, IOBUS, 1, &ptpio }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (READY, dev_int, INT_V_PTP) }, { FLDATA (ENABLE, dev_enb, INT_V_PTP) }, { FLDATA (POWER, ptp_power, 0) }, { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { ORDATA (PSTATE, ptp_unit.STA, 2), REG_HIDDEN }, { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, { DRDATA (PWRTIME, ptp_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptp_stopioe, 0) }, { NULL } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, pt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, &pt_attach, NULL, &ptp_dib, 0 }; /* TTY data structures tty_dev TTY device descriptor tty_unit TTY unit descriptor tty_reg TTY register list tty_mod TTy modifiers list */ #define TTI 0 #define TTO 1 #define TTR 2 #define TTP 3 DIB tty_dib = { TTY, IOBUS, 1, &ttyio }; UNIT tty_unit[] = { { UDATA (&tti_svc, TT_MODE_KSR, 0), KBD_POLL_WAIT }, { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }, { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) }, { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } }; REG tty_reg[] = { { ORDATA (BUF, tty_buf, 8) }, { FLDATA (MODE, tty_mode, 0) }, { FLDATA (READY, dev_int, INT_V_TTY) }, { FLDATA (ENABLE, dev_enb, INT_V_TTY) }, { DRDATA (KPOS, tty_unit[TTI].pos, T_ADDR_W), PV_LEFT }, { DRDATA (KTIME, tty_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPOS, tty_unit[TTO].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, { ORDATA (RXOFF, ttr_xoff_read, 2), REG_HIDDEN }, { ORDATA (RSTATE, tty_unit[TTR].STA, 2), REG_HIDDEN }, { DRDATA (RPOS, tty_unit[TTR].pos, T_ADDR_W), PV_LEFT }, { ORDATA (PTAPE, ttp_tape_rcvd, 2), REG_HIDDEN }, { ORDATA (PXOFF, ttp_xoff_rcvd, 2), REG_HIDDEN }, { ORDATA (PSTATE, tty_unit[TTP].STA, 2), REG_HIDDEN }, { DRDATA (PPOS, tty_unit[TTP].pos, T_ADDR_W), PV_LEFT }, { FLDATA (STOP_IOE, ttr_stopioe, 0) }, { NULL } }; MTAB tty_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &ttio_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &ttio_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &ttio_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &ttio_set_mode }, { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE, NULL, "BINARY", &ttrp_set_mode }, { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE+UNIT_ASC, "ASCII", "ASCII", &ttrp_set_mode }, { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, "Unix ASCII", "UASCII", &ttrp_set_mode }, { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, NULL, "START", &ttrp_set_start_stop }, { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, NULL, "STOP", &ttrp_set_start_stop }, { 0 } }; DEVICE tty_dev = { "TTY", tty_unit, tty_reg, tty_mod, 4, 10, 31, 1, 8, 8, NULL, NULL, &tty_reset, NULL, &pt_attach, &pt_detach, &tty_dib, 0 }; /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_mod CLK modifiers clk_reg CLK register list */ DIB clk_dib = { CLK_KEYS, IOBUS, 1, &clkio }; UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; REG clk_reg[] = { { FLDATA (READY, dev_int, INT_V_CLK) }, { FLDATA (ENABLE, dev_enb, INT_V_CLK) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &clk_show_freq, NULL }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, 0 }; /* Paper tape reader: IO routine */ int32 ptrio (int32 inst, int32 fnc, int32 dat, int32 dev) { switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ if (fnc & 016) /* only fnc 0,1 */ return IOBADFNC (dat); ptr_motion = fnc ^ 1; if (fnc) /* fnc 1? stop */ sim_cancel (&ptr_unit); else sim_activate (&ptr_unit, ptr_unit.wait); /* fnc 0? start */ break; case ioSKS: /* SKS */ if (fnc & 013) /* only fnc 0,4 */ return IOBADFNC (dat); if (((fnc == 000) && TST_INT (INT_PTR)) || /* fnc 0? skip rdy */ ((fnc == 004) && !TST_INTREQ (INT_PTR))) /* fnc 4? skip !int */ return IOSKIP (dat); break; case ioINA: /* INA */ if (fnc) /* only fnc 0 */ return IOBADFNC (dat); if (TST_INT (INT_PTR)) { /* ready? */ CLR_INT (INT_PTR); /* clear ready */ if (ptr_motion) /* if motion, restart */ sim_activate (&ptr_unit, ptr_unit.wait); return IOSKIP (ptr_unit.buf | dat); /* ret buf, skip */ } break; } /* end case op */ return dat; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 c; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); if (uptr->STA & LF_PEND) { /* lf pending? */ uptr->STA &= ~LF_PEND; /* clear flag */ c = 0212; /* insert LF */ } else { if ((c = getc (uptr->fileref)) == EOF) { /* read byte */ if (feof (uptr->fileref)) { if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } if ((uptr->flags & UNIT_UASC) && (c == '\n')) { /* Unix newline? */ c = 0215; /* insert CR */ uptr->STA |= LF_PEND; /* lf pending */ } else if ((uptr->flags & UNIT_ASC) && (c != 0)) /* ASCII? */ c = c | 0200; uptr->pos = ftell (uptr->fileref); /* update pos */ } SET_INT (INT_PTR); /* set ready flag */ uptr->buf = c & 0377; /* get byte */ return SCPE_OK; } /* Paper tape attach routine - set or clear ASC/UASC flags if specified */ t_stat pt_attach (UNIT *uptr, char *cptr) { t_stat r; if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOFNC; if (r = attach_unit (uptr, cptr)) return r; if (sim_switches & SWMASK ('A')) /* -a? ASCII */ uptr->flags |= UNIT_ASC; else if (sim_switches & SWMASK ('U')) /* -u? Unix ASCII */ uptr->flags |= (UNIT_ASC|UNIT_UASC); else if (sim_switches & SWMASK ('B')) /* -b? binary */ uptr->flags &= ~(UNIT_ASC|UNIT_UASC); uptr->STA = 0; return r; } /* Detach routine - stop motion if not restore */ t_stat pt_detach (UNIT *uptr) { if (!(sim_switches & SIM_SW_REST)) sim_cancel (uptr); /* stop motion */ uptr->STA = 0; return detach_unit (uptr); } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { CLR_INT (INT_PTR); /* clear ready, enb */ CLR_ENB (INT_PTR); ptr_unit.buf = 0; /* clear buffer */ ptr_unit.STA = 0; ptr_motion = 0; /* unit stopped */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Paper tape reader bootstrap routine */ #define PBOOT_START 1 #define PBOOT_SIZE (sizeof (pboot) / sizeof (int32)) static const int32 pboot[] = { 0010057, /* STA 57 */ 0030001, /* OCP 1 */ 0131001, /* READ, INA 1001 */ 0002003, /* JMP READ */ 0101040, /* SNZ */ 0002003, /* JMP READ */ 0010000, /* STA 0 */ 0131001, /* READ1, INA 1001 */ 0002010, /* JMP READ1 */ 0041470, /* LGL 8 */ 0130001, /* READ2, INA 1 */ 0002013, /* JMP READ2 */ 0110000, /* STA* 0 */ 0024000, /* IRS 0 */ 0100040 /* SZE */ }; t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 i; for (i = 0; i < PBOOT_SIZE; i++) /* copy bootstrap */ M[PBOOT_START + i] = pboot[i]; PC = PBOOT_START; return SCPE_OK; } /* Paper tape punch: IO routine */ int32 ptpio (int32 inst, int32 fnc, int32 dat, int32 dev) { switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ if (fnc & 016) /* only fnc 0,1 */ return IOBADFNC (dat); if (fnc) { /* fnc 1? pwr off */ CLR_INT (INT_PTP); /* not ready */ ptp_power = 0; /* turn off power */ sim_cancel (&ptp_unit); /* stop punch */ } else if (ptp_power == 0) /* fnc 0? start */ sim_activate (&ptp_unit, ptp_ptime); break; case ioSKS: /* SKS */ if ((fnc & 012) || (fnc == 005)) /* only 0, 1, 4 */ return IOBADFNC (dat); if (((fnc == 000) && TST_INT (INT_PTP)) || /* fnc 0? skip rdy */ ((fnc == 001) && /* fnc 1? skip ptp on */ (ptp_power || sim_is_active (&ptp_unit))) || ((fnc == 004) && !TST_INTREQ (INT_PTP))) /* fnc 4? skip !int */ return IOSKIP (dat); break; case ioOTA: /* OTA */ if (fnc) /* only fnc 0 */ return IOBADFNC (dat); if (TST_INT (INT_PTP)) { /* if ptp ready */ CLR_INT (INT_PTP); /* clear ready */ ptp_unit.buf = dat & 0377; /* store byte */ sim_activate (&ptp_unit, ptp_unit.wait); return IOSKIP (dat); /* skip return */ } break; } return dat; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { int32 c; SET_INT (INT_PTP); /* set flag */ if (ptp_power == 0) { /* power on? */ ptp_power = 1; /* ptp is ready */ return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (uptr->flags & UNIT_ASC) { /* ASCII? */ c = uptr->buf & 0177; /* mask to 7b */ if ((uptr->flags & UNIT_UASC) && (c == 015)) /* cr? drop if Unix */ return SCPE_OK; else if (c == 012) /* lf? cvt to nl */ c = '\n'; } else c = uptr->buf & 0377; /* no, binary */ if (putc (c, uptr->fileref) == EOF) { /* output byte */ perror ("PTP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } uptr->pos = ftell (uptr->fileref); /* update pos */ return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { CLR_INT (INT_PTP); /* clear ready, enb */ CLR_ENB (INT_PTP); ptp_power = 0; /* power off */ ptp_unit.buf = 0; /* clear buffer */ ptp_unit.STA = 0; sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Terminal: IO routine */ int32 ttyio (int32 inst, int32 fnc, int32 dat, int32 dev) { switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ if (fnc & 016) /* only fnc 0,1 */ return IOBADFNC (dat); if (fnc && (tty_mode == 0)) { /* input to output? */ if (!sim_is_active (&tty_unit[TTO])) /* set ready */ SET_INT (INT_TTY); tty_mode = 1; /* mode is output */ } else if ((fnc == 0) && tty_mode) { /* output to input? */ CLR_INT (INT_TTY); /* clear ready */ tty_mode = 0; /* mode is input */ } break; case ioSKS: /* SKS */ if (fnc & 012) /* fnc 0,1,4,5 */ return IOBADFNC (dat); if (((fnc == 000) && TST_INT (INT_TTY)) || /* fnc 0? skip rdy */ ((fnc == 001) && /* fnc 1? skip !busy */ (!tty_mode || !sim_is_active (&tty_unit[TTO]))) || ((fnc == 004) && !TST_INTREQ (INT_TTY)) || /* fnc 4? skip !int */ ((fnc == 005) && (tty_mode || /* fnc 5? skip !xoff */ ((tty_buf & 0177) != XOFF)))) /* input & XOFF char */ return IOSKIP (dat); break; case ioINA: /* INA */ if (fnc & 005) /* only 0,2 */ return IOBADFNC (dat); if (TST_INT (INT_TTY)) { /* ready? */ if (tty_mode == 0) /* inp? clear rdy */ CLR_INT (INT_TTY); return IOSKIP (dat | (tty_buf & ((fnc & 002)? 077: 0377))); } break; case ioOTA: if (fnc & 015) /* only 0,2 */ return IOBADFNC (dat); if (TST_INT (INT_TTY)) { /* ready? */ tty_buf = dat & 0377; /* store char */ if (fnc & 002) { /* binary mode? */ tty_buf = tty_buf | 0100; /* set ch 7 */ if (tty_buf & 040) tty_buf = tty_buf & 0277; } if (tty_mode) { sim_activate (&tty_unit[TTO], tty_unit[TTO].wait); CLR_INT (INT_TTY); } return IOSKIP (dat); } break; } /* end case op */ return dat; } /* Input service - keyboard and reader */ t_stat tti_svc (UNIT *uptr) { int32 out, c; UNIT *ruptr = &tty_unit[TTR]; sim_activate (uptr, uptr->wait); /* continue poll */ if ((c = sim_poll_kbd ()) >= SCPE_KFLAG) { /* character? */ out = c & 0177; /* mask echo to 7b */ if (c & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); uptr->pos = uptr->pos + 1; } else if (c != SCPE_OK) /* error? */ return c; else if ((ruptr->flags & UNIT_ATT) && /* TTR attached */ (ruptr->STA & RUNNING)) { /* and running? */ if (ruptr->STA & LF_PEND) { /* lf pending? */ c = 0212; /* char is lf */ ruptr->STA &= ~LF_PEND; /* clear flag */ } else { /* normal read */ if ((c = getc (ruptr->fileref)) == EOF) { /* read byte */ if (feof (ruptr->fileref)) { /* EOF? */ ruptr->STA &= ~RUNNING; /* stop reader */ if (ttr_stopioe) printf ("TTR end of file\n"); else return SCPE_OK; } else perror ("TTR I/O error"); clearerr (ruptr->fileref); return SCPE_IOERR; } if ((ruptr->flags & UNIT_UASC) && (c == '\n')) { c = 0215; /* Unix ASCII NL? */ ruptr->STA |= LF_PEND; /* LF pending */ } else if ((ruptr->flags & UNIT_ASC) && (c != 0)) c = c | 0200; /* ASCII nz? cvt */ ruptr->pos = ftell (ruptr->fileref); } if (ttr_xoff_read != 0) { /* reader stopping? */ if (c == RUBOUT) /* rubout? stop */ ttr_xoff_read = 0; else ttr_xoff_read--; /* else decr state */ if (ttr_xoff_read == 0) /* delay done? */ ruptr->STA &= ~RUNNING; /* stop reader */ } else if ((c & 0177) == XOFF) /* XOFF read? */ ttr_xoff_read = 2; out = c; /* echo char */ } else return SCPE_OK; /* no char */ if (tty_mode == 0) { /* input mode? */ tty_buf = c & 0377; /* put char in buf */ SET_INT (INT_TTY); /* set flag */ } tto_write (out); /* echo to printer */ return ttp_write (out); /* and punch */ } /* Output service - printer and punch */ t_stat tto_svc (UNIT *uptr) { uint32 c7b; UNIT *ruptr = &tty_unit[TTR]; UNIT *puptr = &tty_unit[TTP]; t_stat r; c7b = tty_buf & 0177; if (ttp_tape_rcvd != 0) { /* prev = tape? */ ttp_tape_rcvd--; /* decrement state */ if ((ttp_tape_rcvd == 0) && (puptr->flags & UNIT_ATT)) puptr->STA |= RUNNING; /* start after delay */ } else if (c7b == TAPE) /* char = TAPE? */ ttp_tape_rcvd = 2; if (ttp_xoff_rcvd != 0) { /* prev = XOFF? */ ttp_xoff_rcvd--; /* decrement state */ if (ttp_xoff_rcvd == 0) /* stop after delay */ puptr->STA &= ~RUNNING; } else if (c7b == XOFF) /* char = XOFF? */ ttp_xoff_rcvd = 2; if ((c7b == XON) && (ruptr->flags & UNIT_ATT)) { /* char = XON? */ ruptr->STA |= RUNNING; /* start reader */ ttr_xoff_read = 0; /* cancel stop */ } if ((r = tto_write (tty_buf)) != SCPE_OK) { /* print; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } if ((r = ttp_write (tty_buf)) != SCPE_OK) /* punch; error? */ return r; SET_INT (INT_TTY); /* set done flag */ return SCPE_OK; } /* Output to printer */ t_stat tto_write (int32 c) { UNIT *tuptr = &tty_unit[TTO]; c = sim_tt_outcvt (c, TT_GET_MODE (tuptr->flags) | TTUF_KSR); tuptr->pos = tuptr->pos + 1; if (c >= 0) return sim_putchar_s (c); else return SCPE_OK; } /* Output to punch */ t_stat ttp_write (int32 c) { uint32 p, c7b; UNIT *puptr = &tty_unit[TTP]; if ((puptr->flags & UNIT_ATT) && /* TTP attached */ (puptr->STA & RUNNING)) { /* and running? */ c7b = c & 0177; if (!(puptr->flags & UNIT_UASC) || (c7b != 015)) { if (puptr->flags & UNIT_ASC) { /* ASCII? */ if (c7b == 012) p = '\n'; /* cvt LF */ else p = c7b; /* else 7b */ } else p = c; /* untouched */ if (putc (p, puptr->fileref) == EOF) { /* output byte */ perror ("TTP I/O error"); clearerr (puptr->fileref); return SCPE_IOERR; } puptr->pos = ftell (puptr->fileref); /* update pos */ } } return SCPE_OK; } /* Reset routine */ t_stat tty_reset (DEVICE *dptr) { CLR_INT (INT_TTY); /* clear ready, enb */ CLR_ENB (INT_TTY); tty_mode = 0; /* mode = input */ tty_buf = 0; ttr_xoff_read = 0; /* clr TTR, TTP flags */ ttp_tape_rcvd = 0; ttp_xoff_rcvd = 0; tty_unit[TTR].STA = 0; tty_unit[TTP].STA = 0; sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* activate poll */ sim_cancel (&tty_unit[TTO]); /* cancel output */ return SCPE_OK; } /* Set keyboard/printer mode - make sure flags agree */ t_stat ttio_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATTABLE) /* not TTR, TTP */ return SCPE_NOFNC; tty_unit[TTO].flags = (tty_unit[TTO].flags & ~TT_MODE) | val; if (val == TT_MODE_7P) val = TT_MODE_7B; tty_unit[TTI].flags = (tty_unit[TTI].flags & ~TT_MODE) | val; return SCPE_OK; } /* Set reader/punch mode */ t_stat ttrp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { if (!(uptr->flags & UNIT_ATTABLE)) /* TTR, TTP only */ return SCPE_NOFNC; if (!(val & UNIT_UASC)) uptr->STA &= ~LF_PEND; return SCPE_OK; } /* Set reader/punch start/stop */ t_stat ttrp_set_start_stop (UNIT *uptr, int32 val, char *cptr, void *desc) { if (!(uptr->flags & UNIT_ATTABLE)) /* TTR, TTP only */ return SCPE_NOFNC; if (!(uptr->flags & UNIT_ATT)) /* must be attached */ return SCPE_UNATT; if (val) /* start? set running */ uptr->STA |= RUNNING; else uptr->STA &= ~RUNNING; /* stop? clr running */ if (uptr->flags & UNIT_ROABLE) /* TTR? cancel stop */ ttr_xoff_read = 0; else ttp_tape_rcvd = ttp_xoff_rcvd = 0; /* TTP? cancel all */ return SCPE_OK; } /* Clock/options: IO routine */ int32 clkio (int32 inst, int32 fnc, int32 dat, int32 dev) { switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ if (fnc & 015) /* only fnc 0,2 */ return IOBADFNC (dat); CLR_INT (INT_CLK); /* reset ready */ if (fnc) /* fnc = 2? stop */ sim_cancel (&clk_unit); else { /* fnc = 0? */ if (!sim_is_active (&clk_unit)) sim_activate (&clk_unit, /* activate */ sim_rtc_init (clk_unit.wait)); /* init calibr */ } break; case ioSKS: /* SKS */ if (fnc == 000) { /* clock skip !int */ if (!TST_INTREQ (INT_CLK)) return IOSKIP (dat); } else if ((fnc & 007) == 002) { /* mem parity? */ if (((fnc == 002) && !TST_INT (INT_MPE)) || ((fnc == 012) && TST_INT (INT_MPE))) return IOSKIP (dat); } else return IOBADFNC (dat); /* invalid fnc */ break; case ioOTA: /* OTA */ if (fnc == 000) /* SMK */ dev_enb = dat; else if (fnc == 010) { /* OTK */ C = (dat >> 15) & 1; /* set C */ if (cpu_unit.flags & UNIT_HSA) /* HSA included? */ dp = (dat >> 14) & 1; /* set dp */ if (cpu_unit.flags & UNIT_EXT) { /* ext opt? */ if (dat & 020000) { /* ext set? */ ext = 1; /* yes, set */ extoff_pending = 0; } else extoff_pending = 1; /* no, clr later */ } sc = dat & 037; /* set sc */ } else return IOBADFNC (dat); break; } return dat; } /* Unit service */ t_stat clk_svc (UNIT *uptr) { M[M_CLK] = (M[M_CLK] + 1) & DMASK; /* increment mem ctr */ if (M[M_CLK] == 0) /* = 0? set flag */ SET_INT (INT_CLK); sim_activate (&clk_unit, sim_rtc_calb (clk_tps)); /* reactivate */ return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { CLR_INT (INT_CLK); /* clear ready, enb */ CLR_ENB (INT_CLK); sim_cancel (&clk_unit); /* deactivate unit */ return SCPE_OK; } /* Set frequency */ t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; clk_tps = val; return SCPE_OK; } /* Show frequency */ t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); return SCPE_OK; } simh-3.8.1/H316/h316_cpu.c0000644000175000017500000017546111111656200012765 0ustar vlmvlm/* h316_cpu.c: Honeywell 316/516 CPU simulator Copyright (c) 1999-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu H316/H516 CPU 28-Apr-07 RMS Removed clock initialization 03-Apr-06 RMS Fixed bugs in LLL, LRL (from Theo Engel) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 15-Feb-05 RMS Added start button interrupt 01-Dec-04 RMS Fixed bug in DIV 06-Nov-04 RMS Added =n to SHOW HISTORY 04-Jan-04 RMS Removed unnecessary compare 31-Dec-03 RMS Fixed bug in cpu_set_hist 24-Oct-03 RMS Added DMA/DMC support, instruction history 30-Dec-01 RMS Added old PC queue 03-Nov-01 RMS Fixed NOHSA modifier 30-Nov-01 RMS Added extended SET/SHOW support The register state for the Honeywell 316/516 CPU is: AR<1:16> A register BR<1:16> B register XR<1:16> X register PC<1:16> P register (program counter) Y<1:16> memory address register MB<1:16> memory data register C overflow flag EXT extend mode flag DP double precision mode flag SC<1:5> shift count SR[1:4]<0> sense switches 1-4 The Honeywell 316/516 has six instruction formats: memory reference, I/O, control, shift, skip, and operate. The memory reference format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in|xr| op |sc| offset | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <13:10> mnemonic action 0000 (other) see control, shift, skip, operate instructions 0001 JMP P = MA 0010 LDA A = M[MA] 0011 ANA A = A & M[MA] 0100 STA M[MA] = A 0101 ERA A = A ^ M[MA] 0110 ADD A = A + M[MA] 0111 SUB A = A - M[MA] 1000 JST M[MA] = P, P = MA + 1 1001 CAS skip if A == M[MA], double skip if A < M[MA] 1010 IRS M[MA] = M[MA] + 1, skip if M[MA] == 0 1011 IMA A <=> M[MA] 1100 (I/O) see I/O instructions 1101 LDX/STX X = M[MA] (xr = 1), M[MA] = x (xr = 0) 1110 MPY multiply 1111 DIV divide In non-extend mode, memory reference instructions can access an address space of 16K words. Multiple levels of indirection are supported, and each indirect word supplies its own indirect and index bits. <1,2,7> mode action 0,0,0 sector zero direct MA = IR<8:0> 0,0,1 current direct MA = P<13:9>'IR<8:0> 0,1,0 sector zero indexed MA = IR<8:0> + X 0,1,1 current direct MA = P<13:9>'IR<8:0> + X 1,0,0 sector zero indirect MA = M[IR<8:0>] 1,0,1 current indirect MA = M[P<13:9>'IR<8:0>] 1,1,0 sector zero indirect indexed MA = M[IR<8:0> + X] 1,1,1 current indirect indexed MA = M[MA = P<13:9>'IR<8:0> + X] In extend mode, memory reference instructions can access an address space of 32K words. Multiple levels of indirection are supported, but only post-indexing, based on the original instruction word index flag, is allowed. The control format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 0 0 0 0 0| opcode | control +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The shift format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 1 0 0 0 0|dr|sz|type | shift count | shift +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | \-+-/ | | | | | +--------------------- type | +------------------------- long/A only +---------------------------- right/left The skip format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 0 0 0 0 0|rv|po|pe|ev|ze|s1|s2|s3|s4|cz| skip +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | | | | | +- skip if C = 0 | | | | | | | | +---- skip if ssw 4 = 0 | | | | | | | +------- skip if ssw 3 = 0 | | | | | | +---------- skip if ssw 2 = 0 | | | | | +------------- skip if ssw 1 = 0 | | | | +---------------- skip if A == 0 | | | +------------------- skip if A<0> == 0 | | +---------------------- skip if mem par err | +------------------------- skip if A<15> = 0 +---------------------------- reverse skip sense The operate format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 0 0 0 0| opcode | operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The I/O format is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | 1 1 0 0| function | device | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The IO transfer instruction controls the specified device. Depending on the opcode, the instruction may set or clear the device flag, start or stop I/O, or read or write data. This routine is the instruction decode routine for the Honeywell 316/516. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered infinite indirection loop unimplemented instruction and stop_inst flag set unknown I/O device and stop_dev flag set I/O error in I/O simulator 2. Interrupts. Interrupts are maintained by two parallel variables: dev_int device interrupt flags dev_enb device interrupt enable flags In addition, dev_int contains the interrupt enable and interrupt no defer flags. If interrupt enable and interrupt no defer are set, and at least one interrupt request is pending, then an interrupt occurs. The order of flags in these variables corresponds to the order in the SMK instruction. 3. Non-existent memory. On the H316/516, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: h316_defs.h add interrupt request definition h316_cpu.c add device dispatch table entry h316_sys.c add sim_devices table entry */ #include "h316_defs.h" #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 PCQ_TOP pcq[pcq_p] #define m7 0001000 /* for generics */ #define m8 0000400 #define m9 0000200 #define m10 0000100 #define m11 0000040 #define m12 0000020 #define m13 0000010 #define m14 0000004 #define m15 0000002 #define m16 0000001 #define HIST_PC 0x40000000 #define HIST_C 0x20000000 #define HIST_EA 0x10000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { int32 pc; int32 ir; int32 ar; int32 br; int32 xr; int32 ea; int32 opnd; } InstHistory; uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 saved_AR = 0; /* A register */ int32 saved_BR = 0; /* B register */ int32 saved_XR = 0; /* X register */ int32 PC = 0; /* P register */ int32 C = 0; /* C register */ int32 ext = 0; /* extend mode */ int32 pme = 0; /* prev mode extend */ int32 extoff_pending = 0; /* extend off pending */ int32 dp = 0; /* double mode */ int32 sc = 0; /* shift count */ int32 ss[4]; /* sense switches */ int32 dev_int = 0; /* dev ready */ int32 dev_enb = 0; /* dev enable */ int32 ind_max = 8; /* iadr nest limit */ int32 stop_inst = 1; /* stop on ill inst */ int32 stop_dev = 2; /* stop on ill dev */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ uint32 dma_nch = DMA_MAX; /* number of chan */ uint32 dma_ad[DMA_MAX] = { 0 }; /* DMA addresses */ uint32 dma_wc[DMA_MAX] = { 0 }; /* DMA word count */ uint32 dma_eor[DMA_MAX] = { 0 }; /* DMA end of range */ uint32 chan_req = 0; /* channel requests */ uint32 chan_map[DMA_MAX + DMC_MAX] = { 0 }; /* chan->dev map */ int32 (*iotab[DEV_MAX])(int32 inst, int32 fnc, int32 dat, int32 dev) = { NULL }; int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ extern int32 sim_int_char; extern int32 sim_interval; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern FILE *sim_log; extern DEVICE *sim_devices[]; t_bool devtab_init (void); int32 dmaio (int32 inst, int32 fnc, int32 dat, int32 dev); int32 undio (int32 inst, int32 fnc, int32 dat, int32 dev); 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_noext (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_show_dma (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ DIB cpu_dib = { DMA, IOBUS, 1, &dmaio }; UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_EXT+UNIT_HSA+UNIT_DMC, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (P, PC, 15) }, { ORDATA (A, saved_AR, 16) }, { ORDATA (B, saved_BR, 16) }, { ORDATA (X, XR, 16) }, { ORDATA (SC, sc, 16) }, { FLDATA (C, C, 0) }, { FLDATA (EXT, ext, 0) }, { FLDATA (PME, pme, 0) }, { FLDATA (EXT_OFF, extoff_pending, 0) }, { FLDATA (DP, dp, 0) }, { FLDATA (SS1, ss[0], 0) }, { FLDATA (SS2, ss[1], 0) }, { FLDATA (SS3, ss[2], 0) }, { FLDATA (SS4, ss[3], 0) }, { FLDATA (ION, dev_int, INT_V_ON) }, { FLDATA (INODEF, dev_int, INT_V_NODEF) }, { FLDATA (START, dev_int, INT_V_START) }, { ORDATA (DEVINT, dev_int, 16), REG_RO }, { ORDATA (DEVENB, dev_enb, 16), REG_RO }, { ORDATA (CHREQ, chan_req, DMA_MAX + DMC_MAX) }, { BRDATA (DMAAD, dma_ad, 8, 16, DMA_MAX) }, { BRDATA (DMAWC, dma_wc, 8, 16, DMA_MAX) }, { BRDATA (DMAEOR, dma_eor, 8, 1, DMA_MAX) }, { ORDATA (DMANCH, dma_nch, 3), REG_HRO }, { FLDATA (MPERDY, dev_int, INT_V_MPE) }, { FLDATA (MPEENB, dev_enb, INT_V_MPE) }, { FLDATA (STOP_INST, stop_inst, 0) }, { FLDATA (STOP_DEV, stop_dev, 1) }, { DRDATA (INDMAX, ind_max, 8), REG_NZ + PV_LEFT }, { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO + REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_EXT, 0, "no extend", "NOEXTEND", &cpu_set_noext }, { UNIT_EXT, UNIT_EXT, "extend", "EXTEND", NULL }, { UNIT_HSA, 0, "no HSA", "NOHSA", NULL }, { UNIT_HSA, UNIT_HSA, "HSA", "HSA", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { MTAB_XTD | MTAB_VDV, 0, "channels", "CHANNELS", &cpu_set_nchan, &cpu_show_nchan, NULL }, { UNIT_DMC, 0, "no DMC", "NODMC", NULL }, { UNIT_DMC, UNIT_DMC, "DMC", "DMC", NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DMA1", NULL, NULL, &cpu_show_dma, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DMA2", NULL, NULL, &cpu_show_dma, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 2, "DMA3", NULL, NULL, &cpu_show_dma, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 3, "DMA4", NULL, NULL, &cpu_show_dma, NULL }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 15, 1, 8, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, &cpu_dib, 0 }; t_stat sim_instr (void) { int32 AR, BR, MB, Y, t1, t2, t3, skip, dev; uint32 ut; t_stat reason; t_stat Ea (int32 inst, int32 *addr); void Write (int32 addr, int32 val); int32 Add16 (int32 val1, int32 val2); int32 Add31 (int32 val1, int32 val2); int32 Operate (int32 MB, int32 AR); #define Read(ad) M[(ad)] #define GETDBL_S(h,l) (((h) << 15) | ((l) & MMASK)) #define GETDBL_U(h,l) (((h) << 16) | (l)) #define PUTDBL_S(x) AR = ((x) >> 15) & DMASK; \ BR = (BR & SIGN) | ((x) & MMASK) #define PUTDBL_U(x) AR = ((x) >> 16) & DMASK; \ BR = (x) & DMASK #define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK)) #define NEWA(c,n) (ext? (((c) & ~X_AMASK) | ((n) & X_AMASK)): \ (((c) & ~NX_AMASK) | ((n) & NX_AMASK))) /* Restore register state */ if (devtab_init ()) /* init tables */ return SCPE_STOP; AR = saved_AR & DMASK; /* restore reg */ BR = saved_BR & DMASK; XR = saved_XR & DMASK; PC = PC & ((cpu_unit.flags & UNIT_EXT)? X_AMASK: NX_AMASK); /* mask PC */ reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } /* Channel breaks (DMA and DMC) */ if (chan_req) { /* channel request? */ int32 i, t, ch, dev, st, end, ad, dmcad; t_stat r; for (i = 0, ch = chan_req; ch != 0; i++, ch = ch >> 1) { if (ch & 1) { /* req on chan i? */ dev = chan_map[i]; /* get dev for chan */ if (iotab[dev] == &undio) return SCPE_IERR; chan_req = chan_req & ~(1 << i); /* clear req */ if (Q_DMA (i)) /* DMA? */ st = dma_ad[i]; else { /* DMC */ dmcad = DMC_BASE + ((i - DMC_V_DMC1) << 1); st = Read (dmcad); /* DMC ctrl word */ } ad = st & X_AMASK; /* get curr addr */ if (st & DMA_IN) { /* input? */ t = iotab[dev] (ioINA, 0, 0, dev); /* input word */ if ((t & IOT_SKIP) == 0) return STOP_DMAER; if ((r = t >> IOT_V_REASON) != 0) return r; Write (ad, t & DMASK); /* write to mem */ } else { /* no, output */ t = iotab[dev] (ioOTA, 0, Read (ad), dev); /* output word */ if ((t & IOT_SKIP) == 0) return STOP_DMAER; if (r = (t >> IOT_V_REASON)) return r; } if (Q_DMA (i)) { /* DMA? */ dma_ad[i] = (dma_ad[i] & DMA_IN) | ((ad + 1) & X_AMASK); dma_wc[i] = (dma_wc[i] + 1) & 077777; /* update wc */ if (dma_wc[i] == 0) { /* done? */ dma_eor[i] = 1; /* set end of range */ t = iotab[dev] (ioEND, 0, 0, dev); /* send end range */ if ((r = t >> IOT_V_REASON) != 0) return r; } } else { /* DMC */ st = (st & DMA_IN) | ((ad + 1) & X_AMASK); Write (dmcad, st); /* update start */ end = Read (dmcad + 1); /* get end */ if (((ad ^ end) & X_AMASK) == 0) { /* start == end? */ t = iotab[dev] (ioEND, 0, 0, dev); /* send end range */ if ((r = t >> IOT_V_REASON) != 0) return r; } /* end if end range */ } /* end else DMC */ } /* end if chan i */ } /* end for */ } /* end if chan_req */ /* Interrupts */ if ((dev_int & (INT_PEND|INT_NMI|dev_enb)) > INT_PEND) {/* int req? */ pme = ext; /* save extend */ if (cpu_unit.flags & UNIT_EXT) /* ext opt? extend on */ ext = 1; dev_int = dev_int & ~INT_ON; /* intr off */ MB = 0120000 | M_INT; /* inst = JST* 63 */ } /* Instruction fetch */ else { if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } Y = PC; /* set mem addr */ MB = Read (Y); /* fetch instr */ PC = NEWA (Y, Y + 1); /* incr PC */ dev_int = dev_int | INT_NODEF; } dev_int = dev_int & ~INT_START; /* clr start button int */ sim_interval = sim_interval - 1; if (hst_lnt) { /* instr hist? */ hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = Y | HIST_PC | (C? HIST_C: 0); /* fill slots */ hst[hst_p].ir = MB; hst[hst_p].ar = AR; hst[hst_p].br = BR; hst[hst_p].xr = XR; } /* Memory reference instructions */ switch (I_GETOP (MB)) { /* case on <1:6> */ case 001: case 021: case 041: case 061: /* JMP */ if (reason = Ea (MB, &Y)) /* eff addr */ break; PCQ_ENTRY; /* save PC */ PC = NEWA (PC, Y); /* set new PC */ if (extoff_pending) /* cond ext off */ ext = extoff_pending = 0; break; case 002: case 022: case 042: case 062: /* LDA */ if (reason = Ea (MB, &Y)) /* eff addr */ break; if (dp) { /* double prec? */ AR = Read (Y & ~1); /* get doubleword */ BR = Read (Y | 1); sc = 0; } else AR = Read (Y); /* no, get word */ break; case 003: case 023: case 043: case 063: /* ANA */ if (reason = Ea (MB, &Y)) /* eff addr */ break; AR = AR & Read (Y); break; case 004: case 024: case 044: case 064: /* STA */ if (reason = Ea (MB, &Y)) /* eff addr */ break; Write (Y, AR); /* store A */ if (dp) { /* double prec? */ Write (Y | 1, BR); /* store B */ sc = 0; } break; case 005: case 025: case 045: case 065: /* ERA */ if (reason = Ea (MB, &Y)) /* eff addr */ break; AR = AR ^ Read (Y); break; case 006: case 026: case 046: case 066: /* ADD */ if (reason = Ea (MB, &Y)) /* eff addr */ break; if (dp) { /* double prec? */ t1 = GETDBL_S (AR, BR); /* get A'B */ t2 = GETDBL_S (Read (Y & ~1), Read (Y | 1)); t1 = Add31 (t1, t2); /* 31b add */ PUTDBL_S (t1); sc = 0; } else AR = Add16 (AR, Read (Y)); /* no, 16b add */ break; case 007: case 027: case 047: case 067: /* SUB */ if (reason = Ea (MB, &Y)) /* eff addr */ break; if (dp) { /* double prec? */ t1 = GETDBL_S (AR, BR); /* get A'B */ t2 = GETDBL_S (Read (Y & ~1), Read (Y | 1)); t1 = Add31 (t1, -t2); /* 31b sub */ PUTDBL_S (t1); sc = 0; } else AR = Add16 (AR, (-Read (Y)) & DMASK); /* no, 16b sub */ break; case 010: case 030: case 050: case 070: /* JST */ if (reason = Ea (MB, &Y)) /* eff addr */ break; MB = NEWA (Read (Y), PC); /* merge old PC */ Write (Y, MB); PCQ_ENTRY; PC = NEWA (PC, Y + 1); /* set new PC */ break; case 011: case 031: case 051: case 071: /* CAS */ if (reason = Ea (MB, &Y)) /* eff addr */ break; MB = Read (Y); if (AR == MB) PC = NEWA (PC, PC + 1); else if (SEXT (AR) < SEXT (MB)) PC = NEWA (PC, PC + 2); break; case 012: case 032: case 052: case 072: /* IRS */ if (reason = Ea (MB, &Y)) /* eff addr */ break; MB = (Read (Y) + 1) & DMASK; /* incr, rewrite */ Write (Y, MB); if (MB == 0) /* skip if zero */ PC = NEWA (PC, PC + 1); break; case 013: case 033: case 053: case 073: /* IMA */ if (reason = Ea (MB, &Y)) /* eff addr */ break; MB = Read (Y); Write (Y, AR); /* A to mem */ AR = MB; /* mem to A */ break; case 015: case 055: /* STX */ if (reason = Ea (MB, &Y)) /* eff addr */ break; Write (Y, XR); /* store XR */ break; case 035: case 075: /* LDX */ if (reason = Ea (MB, &Y)) /* eff addr */ break; XR = Read (Y); /* load XR */ break; case 016: case 036: case 056: case 076: /* MPY */ if (cpu_unit.flags & UNIT_HSA) { /* installed? */ if (reason = Ea (MB, &Y)) /* eff addr */ break; t1 = SEXT (AR) * SEXT (Read (Y)); PUTDBL_S (t1); sc = 0; } else reason = stop_inst; break; case 017: case 037: case 057: case 077: /* DIV */ if (cpu_unit.flags & UNIT_HSA) { /* installed? */ if (reason = Ea (MB, &Y)) /* eff addr */ break; t2 = SEXT (Read (Y)); /* divr */ if (t2) { /* divr != 0? */ t1 = GETDBL_S (SEXT (AR), BR); /* get A'B signed */ BR = (t1 % t2) & DMASK; /* remainder */ t1 = t1 / t2; /* quotient */ AR = t1 & DMASK; if ((t1 > MMASK) || (t1 < (-SIGN))) C = 1; else C = 0; sc = 0; } else C = 1; } else reason = stop_inst; break; /* I/O instructions */ case 014: /* OCP */ dev = MB & DEVMASK; t2 = iotab[dev] (ioOCP, I_GETFNC (MB), AR, dev); reason = t2 >> IOT_V_REASON; break; case 034: /* SKS */ dev = MB & DEVMASK; t2 = iotab[dev] (ioSKS, I_GETFNC (MB), AR, dev); reason = t2 >> IOT_V_REASON; if (t2 & IOT_SKIP) /* skip? */ PC = NEWA (PC, PC + 1); break; case 054: /* INA */ dev = MB & DEVMASK; if (MB & INCLRA) AR = 0; t2 = iotab[dev] (ioINA, I_GETFNC (MB & ~INCLRA), AR, dev); reason = t2 >> IOT_V_REASON; if (t2 & IOT_SKIP) /* skip? */ PC = NEWA (PC, PC + 1); AR = t2 & DMASK; /* data */ break; case 074: /* OTA */ dev = MB & DEVMASK; t2 = iotab[dev] (ioOTA, I_GETFNC (MB), AR, dev); reason = t2 >> IOT_V_REASON; if (t2 & IOT_SKIP) /* skip? */ PC = NEWA (PC, PC + 1); break; /* Control */ case 000: if ((MB & 1) == 0) { /* HLT */ if ((reason = sim_process_event ()) != SCPE_OK) break; reason = STOP_HALT; break; } if (MB & m14) { /* SGL, DBL */ if (cpu_unit.flags & UNIT_HSA) dp = (MB & m15)? 1: 0; else reason = stop_inst; } if (MB & m13) { /* DXA, EXA */ if (!(cpu_unit.flags & UNIT_EXT)) reason = stop_inst; else if (MB & m15) { /* EXA */ ext = 1; extoff_pending = 0; /* DXA */ } else extoff_pending = 1; } if (MB & m12) /* RMP */ CLR_INT (INT_MPE); if (MB & m11) { /* SCA, INK */ if (MB & m15) /* INK */ AR = (C << 15) | (dp << 14) | (pme << 13) | (sc & 037); else if (cpu_unit.flags & UNIT_HSA) /* SCA */ AR = sc & 037; else reason = stop_inst; } else if (MB & m10) { /* NRM */ if (cpu_unit.flags & UNIT_HSA) { for (sc = 0; (sc <= 32) && ((AR & SIGN) != ((AR << 1) & SIGN)); sc++) { AR = (AR & SIGN) | ((AR << 1) & MMASK) | ((BR >> 14) & 1); BR = (BR & SIGN) | ((BR << 1) & MMASK); } sc = sc & 037; } else reason = stop_inst; } else if (MB & m9) { /* IAB */ sc = BR; BR = AR; AR = sc; } if (MB & m8) /* ENB */ dev_int = (dev_int | INT_ON) & ~INT_NODEF; if (MB & m7) /* INH */ dev_int = dev_int & ~INT_ON; break; /* Shift Shifts are microcoded as follows: op<7> = right/left op<8> = long/short op<9> = shift/rotate (rotate bits "or" into new position) op<10> = logical/arithmetic If !op<7> && op<10> (right arithmetic), A<1> propagates rightward If op<7> && op<10> (left arithmetic), C is set if A<1> changes state If !op<8> && op<10> (long arithmetic), B<1> is skipped This microcoding "explains" how the 4 undefined opcodes actually work 003 = long arith rotate right, skip B<1>, propagate A<1>, bits rotated out "or" into A<1> 007 = short arith rotate right, propagate A<1>, bits rotated out "or" into A<1> 013 = long arith rotate left, skip B<1>, C = overflow 017 = short arith rotate left, C = overflow */ case 020: C = 0; /* clear C */ sc = 0; /* clear sc */ if ((t1 = (-MB) & SHFMASK) == 0) /* shift count */ break; switch (I_GETFNC (MB)) { /* case shift fnc */ case 000: /* LRL */ if (t1 > 32) /* >32? all 0 */ ut = 0; else { ut = GETDBL_U (AR, BR); /* get A'B */ C = (ut >> (t1 - 1)) & 1; /* C = last out */ if (t1 == 32) /* =32? all 0 */ ut = 0; else ut = ut >> t1; /* log right */ } PUTDBL_U (ut); /* store A,B */ break; case 001: /* LRS */ if (t1 > 31) /* limit to 31 */ t1 = 31; t2 = GETDBL_S (SEXT (AR), BR); /* get A'B signed */ C = (t2 >> (t1 - 1)) & 1; /* C = last out */ t2 = t2 >> t1; /* arith right */ PUTDBL_S (t2); /* store A,B */ break; case 002: /* LRR */ t2 = t1 % 32; /* mod 32 */ ut = GETDBL_U (AR, BR); /* get A'B */ ut = (ut >> t2) | (ut << (32 - t2)); /* rot right */ C = (ut >> 31) & 1; /* C = A<1> */ PUTDBL_U (ut); /* store A,B */ break; case 003: /* "long right arot" */ if (reason = stop_inst) /* stop on undef? */ break; for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ C = BR & 1; /* C = last out */ BR = (BR & SIGN) | ((AR & 1) << 14) | ((BR & MMASK) >> 1); AR = ((AR & SIGN) | (C << 15)) | (AR >> 1); } break; case 004: /* LGR */ if (t1 > 16) /* > 16? all 0 */ AR = 0; else { C = (AR >> (t1 - 1)) & 1; /* C = last out */ AR = (AR >> t1) & DMASK; /* log right */ } break; case 005: /* ARS */ if (t1 > 16) /* limit to 16 */ t1 = 16; C = ((SEXT (AR)) >> (t1 - 1)) & 1; /* C = last out */ AR = ((SEXT (AR)) >> t1) & DMASK; /* arith right */ break; case 006: /* ARR */ t2 = t1 % 16; /* mod 16 */ AR = ((AR >> t2) | (AR << (16 - t2))) & DMASK; C = (AR >> 15) & 1; /* C = A<1> */ break; case 007: /* "short right arot" */ if (reason = stop_inst) /* stop on undef? */ break; for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ C = AR & 1; /* C = last out */ AR = ((AR & SIGN) | (C << 15)) | (AR >> 1); } break; case 010: /* LLL */ if (t1 > 32) /* > 32? all 0 */ ut = 0; else { ut = GETDBL_U (AR, BR); /* get A'B */ C = (ut >> (32 - t1)) & 1; /* C = last out */ if (t1 == 32) /* =32? all 0 */ ut = 0; else ut = ut << t1; /* log left */ } PUTDBL_U (ut); /* store A,B */ break; case 011: /* LLS */ if (t1 > 31) /* limit to 31 */ t1 = 31; t2 = GETDBL_S (SEXT (AR), BR); /* get A'B */ t3 = t2 << t1; /* "arith" left */ PUTDBL_S (t3); /* store A'B */ if ((t2 >> (31 - t1)) != /* shf out = sgn? */ ((AR & SIGN)? -1: 0)) C = 1; break; case 012: /* LLR */ t2 = t1 % 32; /* mod 32 */ ut = GETDBL_U (AR, BR); /* get A'B */ ut = (ut << t2) | (ut >> (32 - t2)); /* rot left */ C = ut & 1; /* C = B<16> */ PUTDBL_U (ut); /* store A,B */ break; case 013: /* "long left arot" */ if (reason = stop_inst) /* stop on undef? */ break; for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ AR = (AR << 1) | ((BR >> 14) & 1); BR = (BR & SIGN) | ((BR << 1) & MMASK) | ((AR >> 16) & 1); if ((AR & SIGN) != ((AR >> 1) & SIGN)) C = 1; AR = AR & DMASK; } break; case 014: /* LGL */ if (t1 > 16) /* > 16? all 0 */ AR = 0; else { C = (AR >> (16 - t1)) & 1; /* C = last out */ AR = (AR << t1) & DMASK; /* log left */ } break; case 015: /* ALS */ if (t1 > 16) /* limit to 16 */ t1 = 16; t2 = SEXT (AR); /* save AR */ AR = (AR << t1) & DMASK; /* "arith" left */ if ((t2 >> (16 - t1)) != /* shf out + sgn */ ((AR & SIGN)? -1: 0)) C = 1; break; case 016: /* ALR */ t2 = t1 % 16; /* mod 16 */ AR = ((AR << t2) | (AR >> (16 - t2))) & DMASK; C = AR & 1; /* C = A<16> */ break; case 017: /* "short left arot" */ if (reason = stop_inst) /* stop on undef? */ break; for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ if ((AR & SIGN) != ((AR << 1) & SIGN)) C = 1; AR = ((AR << 1) | (AR >> 15)) & DMASK; } break; /* end case fnc */ } break; /* Skip */ case 040: skip = 0; if (((MB & 000001) && C) || /* SSC */ ((MB & 000002) && ss[3]) || /* SS4 */ ((MB & 000004) && ss[2]) || /* SS3 */ ((MB & 000010) && ss[1]) || /* SS2 */ ((MB & 000020) && ss[0]) || /* SS1 */ ((MB & 000040) && AR) || /* SNZ */ ((MB & 000100) && (AR & 1)) || /* SLN */ ((MB & 000200) && (TST_INTREQ (INT_MPE))) || /* SPS */ ((MB & 000400) && (AR & SIGN))) /* SMI */ skip = 1; if ((MB & 001000) == 0) /* reverse? */ skip = skip ^ 1; PC = NEWA (PC, PC + skip); break; /* Operate */ case 060: if (MB == 0140024) /* CHS */ AR = AR ^ SIGN; else if (MB == 0140040) /* CRA */ AR = 0; else if (MB == 0140100) /* SSP */ AR = AR & ~SIGN; else if (MB == 0140200) /* RCB */ C = 0; else if (MB == 0140320) { /* CSA */ C = (AR & SIGN) >> 15; AR = AR & ~SIGN; } else if (MB == 0140401) /* CMA */ AR = AR ^ DMASK; else if (MB == 0140407) { /* TCA */ AR = (-AR) & DMASK; sc = 0; } else if (MB == 0140500) /* SSM */ AR = AR | SIGN; else if (MB == 0140600) /* SCB */ C = 1; else if (MB == 0141044) /* CAR */ AR = AR & 0177400; else if (MB == 0141050) /* CAL */ AR = AR & 0377; else if (MB == 0141140) /* ICL */ AR = AR >> 8; else if (MB == 0141206) /* AOA */ AR = Add16 (AR, 1); else if (MB == 0141216) /* ACA */ AR = Add16 (AR, C); else if (MB == 0141240) /* ICR */ AR = (AR << 8) & DMASK; else if (MB == 0141340) /* ICA */ AR = ((AR << 8) | (AR >> 8)) & DMASK; else if (reason = stop_inst) break; else AR = Operate (MB, AR); /* undefined */ break; } /* end case op */ } /* end while */ saved_AR = AR & DMASK; saved_BR = BR & DMASK; saved_XR = XR & DMASK; pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Effective address The effective address calculation consists of three phases: - base address calculation: 0/pagenumber'displacement - (extend): indirect address resolution (non-extend): pre-indexing - (extend): post-indexing (non-extend): indirect address/post-indexing resolution In extend mode, address calculations are carried out to 16b and masked to 15b at exit. In non-extend mode, address bits <1:2> are preserved by the NEWA macro; address bit <1> is masked at exit. */ t_stat Ea (int32 IR, int32 *addr) { int32 i; int32 Y = IR & (IA | DISP); /* ind + disp */ if (IR & SC) Y = ((PC - 1) & PAGENO) | Y; /* cur sec? + pageno */ if (ext) { /* extend mode? */ for (i = 0; (i < ind_max) && (Y & IA); i++) { /* resolve ind addr */ Y = Read (Y & X_AMASK); /* get ind addr */ } if (IR & IDX) Y = Y + XR; /* post-index */ } /* end if ext */ else { /* non-extend */ Y = NEWA (PC, Y + ((IR & IDX)? XR: 0)); /* pre-index */ for (i = 0; (i < ind_max) && (IR & IA); i++) { /* resolve ind addr */ IR = Read (Y & X_AMASK); /* get ind addr */ Y = NEWA (Y, IR + ((IR & IDX)? XR: 0)); /* post-index */ } } /* end else */ *addr = Y = Y & X_AMASK; /* return addr */ if (hst_lnt) { /* history? */ hst[hst_p].pc = hst[hst_p].pc | HIST_EA; hst[hst_p].ea = Y; hst[hst_p].opnd = Read (Y); } if (i >= ind_max) return STOP_IND; /* too many ind? */ return SCPE_OK; } /* Write memory */ void Write (int32 addr, int32 val) { if (((addr == 0) || (addr >= 020)) && MEM_ADDR_OK (addr)) M[addr] = val; return; } /* Add */ int32 Add16 (int32 v1, int32 v2) { int32 r = v1 + v2; if (((v1 ^ ~v2) & (v1 ^ r)) & SIGN) C = 1; else C = 0; return (r & DMASK); } int32 Add31 (int32 v1, int32 v2) { int32 r = v1 + v2; if (((v1 ^ ~v2) & (v1 ^ r)) & DP_SIGN) C = 1; else C = 0; return r; } /* Unimplemented I/O device */ int32 undio (int32 op, int32 fnc, int32 val, int32 dev) { return ((stop_dev << IOT_V_REASON) | val); } /* DMA control */ int32 dmaio (int32 inst, int32 fnc, int32 dat, int32 dev) { int32 ch = (fnc - 1) & 03; switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ if ((fnc >= 001) && (fnc <= 004)) { /* load addr ctr */ dma_ad[ch] = dat; dma_wc[ch] = 0; dma_eor[ch] = 0; } else if ((fnc >= 011) && (fnc <= 014)) /* load range ctr */ dma_wc[ch] = (dma_wc[ch] | dat) & 077777; else return IOBADFNC (dat); /* undefined */ break; case ioINA: /* INA */ if ((fnc >= 011) && (fnc <= 014)) { if (dma_eor[ch]) /* end range? nop */ return dat; return IOSKIP (0100000 | dma_wc[ch]); /* return range */ } else return IOBADFNC (dat); } return dat; } /* Undefined operate instruction. This code is reached when the opcode does not correspond to a standard operate instruction. It simulates the behavior of the actual logic. An operate instruction executes in 4 or 6 phases. A 'normal' instruction takes 4 phases: t1 t1 t2/tlate t2/t2 extended into t3 t3/tlate t3 t4 t4 A '1.5 cycle' instruction takes 6 phases: t1 t1 t2/tlate t2/t2 extended into t3 t3/tlate t3 t2/tlate 'special' t2/t2 extended into t3 t3/tlate t3 t4 t4 The key signals, by phase, are the following tlate EASTL enable A to sum leg 1 (else 0) (((m12+m16)x!azzzz)+(m9+m11+azzzz) EASBM enable 0 to sum leg 2 (else 177777) (m9+m11+azzzz) JAMKN jam carry network to 0 = force XOR ((m12+m16)x!azzzz) EIKI7 force carry into adder ((m15x(C+!m13))x!JAMKN) t3 CLDTR set D to 177777 (always) ESDTS enable adder sum to D (always) SETAZ enable repeat cycle = set azzzz (m8xm15) if azzzz { t2 CLATR clear A register (due to azzzz) EDAHS enable D high to A high register (due to azzzz) EDALS enable D low to A low register (due to azzzz) tlate, t3 as above } t4 CLATR clear A register (m11+m15+m16) CLA1R clear A1 register (m10+m14) EDAHS enable D high to A high register ((m11xm14)+m15+m16) EDALS enable D low to A low register ((m11xm13)+m15+m16) ETAHS enable D transposed to A high register (m9xm11) ETALS enable D transposed to A low register (m10xm11) EDA1R enable D1 to A1 register ((m8xm10)+m14) CBITL clear C, conditionally set C from adder output (m9x!m11) CBITG conditionally set C if D1 (m10xm12xD1) CBITE unconditionally set C (m8xm9) */ int32 Operate (int32 MB, int32 AR) { int32 D, jamkn, eiki7, easbm, eastl, setaz; int32 clatr, cla1r, edahs, edals, etahs, etals, eda1r; int32 cbitl, cbitg, cbite; int32 aleg, bleg, ARx; /* Phase tlate */ ARx = AR; /* default */ jamkn = (MB & (m12+m16)) != 0; /* m12+m16 */ easbm = (MB & (m9+m11)) != 0; /* m9+m11 */ eastl = jamkn || easbm; /* m9+m11+m12+m16 */ setaz = (MB & (m8+m15)) == (m8+m15); /* m8xm15*/ eiki7 = (MB & m15) && (C || !(MB & m13)); /* cin */ aleg = eastl? AR: 0; /* a input */ bleg = easbm? 0: DMASK; /* b input */ if (jamkn) /* jammin? xor */ D = aleg ^ bleg; else D = (aleg + bleg + eiki7) & DMASK; /* else add */ /* Possible repeat at end of tlate - special t2, repeat tlate */ if (setaz) { ARx = D; /* forced: t2 */ aleg = ARx; /* forced: tlate */ bleg = 0; /* forced */ jamkn = 0; /* forced */ D = (aleg + bleg + eiki7) & DMASK; /* forced add */ sc = 0; /* ends repeat */ } /* Phase t4 */ clatr = (MB & (m11+m15+m16)) != 0; /* m11+m15+m16 */ cla1r = (MB & (m10+m14)) != 0; /* m10+m14 */ edahs = ((MB & (m11+m14)) == (m11+m14)) || /* (m11xm14)+m15+m16 */ (MB & (m15+m16)); edals = ((MB & (m11+m13)) == (m11+m13)) || /* (m11xm13)+m15+m16 */ (MB & (m15+m16)); etahs = (MB & (m9+m11)) == (m9+m11); /* m9xm11 */ etals = (MB & (m10+m11)) == (m10+m11); /* m10xm11 */ eda1r = ((MB & (m8+m10)) == (m8+m10)) || (MB & m14); /* (m8xm10)+m14 */ cbitl = (MB & (m9+m11)) == m9; /* m9x!m11 */ cbite = (MB & (m8+m9)) == (m8+m9); /* m8xm9 */ cbitg = (MB & (m10+m12)) == (m10+m12); /* m10xm12 */ if (clatr) /* clear A */ ARx = 0; if (cla1r) /* clear A1 */ ARx = ARx & ~SIGN; if (edahs) /* D hi to A hi */ ARx = ARx | (D & 0177400); if (edals) /* D lo to A lo */ ARx = ARx | (D & 0000377); if (etahs) /* D lo to A hi */ ARx = ARx | ((D << 8) & 0177400); if (etals) /* D hi to A lo */ ARx = ARx | ((D >> 8) & 0000377); if (eda1r) /* D1 to A1 */ ARx = ARx | (D & SIGN); if (cbitl) { /* ovflo to C */ /* Overflow calculation. Cases: aleg bleg cin overflow 0 x x can't overflow A 0 0 can't overflow A -1 1 can't overflow A 0 1 overflow if 77777->100000 A -1 0 overflow if 100000->77777 */ if (!jamkn && ((bleg && !eiki7 && (D == 0077777)) || (!bleg && eiki7 && (D == 0100000)))) C = 1; else C = 0; } if (cbite || (cbitg && (D & SIGN))) /* C = 1 */ C = 1; return ARx; } /* Reset routines */ t_stat cpu_reset (DEVICE *dptr) { int32 i; saved_AR = saved_BR = saved_XR = 0; C = 0; dp = 0; ext = pme = extoff_pending = 0; dev_int = dev_int & ~(INT_PEND|INT_NMI); dev_enb = 0; for (i = 0; i < DMA_MAX; i++) dma_ad[i] = dma_wc[i] = dma_eor[i] = 0; chan_req = 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) { int32 d; if (addr >= MEMSIZE) return SCPE_NXM; if (addr == 0) d = saved_XR; else d = M[addr]; if (vptr != NULL) *vptr = d & 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; if (addr == 0) saved_XR = val & DMASK; else M[addr] = val & DMASK; return SCPE_OK; } /* Option processors */ t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc) { if (MEMSIZE > (NX_AMASK + 1)) return SCPE_ARG; return SCPE_OK; } 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) || (((cpu_unit.flags & UNIT_EXT) == 0) && (val > (NX_AMASK + 1)))) 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; } t_stat cpu_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i, newmax; t_stat r; if (cptr == NULL) return SCPE_ARG; newmax = get_uint (cptr, 10, DMA_MAX, &r); /* get new max */ if ((r != SCPE_OK) || (newmax == dma_nch)) /* err or no chg? */ return r; dma_nch = newmax; /* set new max */ for (i = newmax; i < DMA_MAX; i++) { /* reset chan */ dma_ad[i] = dma_wc[i] = dma_eor[i] = 0; chan_req = chan_req & ~(1 << i); } return SCPE_OK; } /* Show DMA channels */ t_stat cpu_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc) { if (dma_nch) fprintf (st, "DMA channels = %d", dma_nch); else fprintf (st, "no DMA channels"); return SCPE_OK; } /* Show channel state */ t_stat cpu_show_dma (FILE *st, UNIT *uptr, int32 val, void *desc) { if ((val < 0) || (val >= DMA_MAX)) return SCPE_IERR; fputs ((dma_ad[val] & DMA_IN)? "Input": "Output", st); fprintf (st, ", addr = %06o, count = %06o, ", dma_ad[val] & X_AMASK, dma_wc[val]); fprintf (st, "end of range %s\n", (dma_eor[val]? "set": "clear")); return SCPE_OK; } /* Set I/O device to IOBUS / DMA channel / DMC channel */ t_stat io_set_iobus (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; if (val || cptr || (uptr == NULL)) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; dibp->chan = 0; return SCPE_OK; } t_stat io_set_dma (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newc; t_stat r; if ((cptr == NULL) || (uptr == NULL)) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; if (dma_nch == 0) return SCPE_NOFNC; newc = get_uint (cptr, 10, DMA_MAX, &r); /* get new */ if ((r != SCPE_OK) || (newc == 0) || (newc > dma_nch)) return SCPE_ARG; dibp->chan = (newc - DMA_MIN) + DMA_V_DMA1 + 1; /* store */ return SCPE_OK; } t_stat io_set_dmc (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newc; t_stat r; if ((cptr == NULL) || (uptr == NULL)) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; if (!(cpu_unit.flags & UNIT_DMC)) return SCPE_NOFNC; newc = get_uint (cptr, 10, DMC_MAX, &r); /* get new */ if ((r != SCPE_OK) || (newc == 0)) return SCPE_ARG; dibp->chan = (newc - DMC_MIN) + DMC_V_DMC1 + 1; /* store */ return SCPE_OK; } /* Show channel configuration */ t_stat io_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; if (dibp->chan == 0) fprintf (st, "IO bus"); else if (dibp->chan < (DMC_V_DMC1 + 1)) fprintf (st, "DMA channel %d", dibp->chan); else fprintf (st, "DMC channel %d", dibp->chan - DMC_V_DMC1); return SCPE_OK; } /* Set up I/O dispatch and channel maps */ t_bool devtab_init (void) { DEVICE *dptr; DIB *dibp; uint32 i, j, dno, chan; for (i = 0; i < DEV_MAX; i++) iotab[i] = NULL; for (i = 0; i < (DMA_MAX + DMC_MAX); i++) chan_map[i] = 0; for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((dibp == NULL) || (dptr->flags & DEV_DIS)) /* exist, enabled? */ continue; dno = dibp->dev; /* device number */ for (j = 0; j < dibp->num; j++) { /* repeat for slots */ if (iotab[dno + j]) { /* conflict? */ printf ("%s device number conflict, devno = %02o\n", sim_dname (dptr), dno + j); if (sim_log) fprintf (sim_log, "%s device number conflict, devno = %02o\n", sim_dname (dptr), dno + j); return TRUE; } iotab[dno + j] = dibp->io; /* set I/O routine */ } /* end for */ if (dibp->chan) { /* DMA/DMC? */ chan = dibp->chan - 1; if ((chan < DMC_V_DMC1) && (chan >= dma_nch)) { printf ("%s configured for DMA channel %d\n", sim_dname (dptr), chan + 1); if (sim_log) fprintf (sim_log, "%s configured for DMA channel %d\n", sim_dname (dptr), chan + 1); return TRUE; } if ((chan >= DMC_V_DMC1) && !(cpu_unit.flags & UNIT_DMC)) { printf ("%s configured for DMC, option disabled\n", sim_dname (dptr)); if (sim_log) fprintf (sim_log, "%s configured for DMC, option disabled\n", sim_dname (dptr)); return TRUE; } if (chan_map[chan]) { /* channel conflict? */ printf ("%s DMA/DMC channel conflict, devno = %02o\n", sim_dname (dptr), dno); if (sim_log) fprintf (sim_log, "%s DMA/DMC channel conflict, devno = %02o\n", sim_dname (dptr), dno); return TRUE; } chan_map[chan] = dno; /* channel back map */ } } /* end for */ for (i = 0; i < DEV_MAX; i++) { /* fill in blanks */ if (iotab[i] == NULL) iotab[i] = &undio; } return FALSE; } /* 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 cr, k, di, op, lnt; char *cptr = (char *) desc; t_value sim_eval; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); static uint8 has_opnd[16] = { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 }; if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 C A B X 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? */ cr = (h->pc & HIST_C)? 1: 0; /* carry */ fprintf (st, "%05o %o %06o %06o %06o ", h->pc & X_AMASK, cr, h->ar, h->br, h->xr); if (h->pc & HIST_EA) fprintf (st, "%05o ", h->ea); else fprintf (st, " "); sim_eval = h->ir; if ((fprint_sym (st, h->pc & X_AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %06o", h->ir); op = I_GETOP (h->ir) & 017; /* base op */ if (has_opnd[op]) fprintf (st, " [%06o]", h->opnd); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/H316/h316_lp.c0000644000175000017500000003541411107404320012601 0ustar vlmvlm/* h316_lp.c: Honeywell 316/516 line printer Copyright (c) 1999-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt line printer 09-Jun-07 RMS Fixed lost last print line (from Theo Engel) 19-Jan-06 RMS Added UNIT_TEXT flag 03-Apr-06 RMS Fixed bug in blanks backscanning (from Theo Engel) 01-Dec-04 RMS Fixed bug in DMA/DMC support 24-Oct-03 RMS Added DMA/DMC support 25-Apr-03 RMS Revised for extended file support 30-May-02 RMS Widened POS to 32b The Series 16 line printer is an unbuffered Analex shuttle printer. Because it was unbuffered, the CPU had to scan out an entire line's worth of characters (60 words) for every character on the print drum (64 characters). Because it was a shuttle printer, the entire process must be repeated first for the odd columns and then for the even columns. After scanning the odd columns, the printer carriage shuttled right by one column; after scanning the even columns, the carriage shuttled left. This halved the number of hammers required, reducing cost but increasing mechanical complexity. The real printer is very timing dependent. If the CPU misses a scan, then the wrong characters are printed. If the printer protocol is violated, then results are unpredictable. The simulated printer is much more forgiving. Rather than simulating the fixed drum and hammer timing of the real printer, the simulator is driven by the program's OTA instructions. If the program misses a time slot, the simulator will still print the "correct" result. A timing based simulation would be very hard to do in the absense of accurate instruction timing. Printer state is maintained in a set of position and state variables: lpt_wdpos word count within a line scan (0-59) lpt_drpos drum position (0-63) lpt_crpos carriage position (0-1) lpt_svcst service state (shuttle, paper advance) lpt_svcch channel for paper advance (0 = no adv) lpt_rdy transfer ready flag lpt_prdn printing done flag lpt_dma use DMA/DMC lpt_eor DMA/DMC end of range */ #include "h316_defs.h" #define LPT_WIDTH 120 /* width */ #define LPT_SCAN (LPT_WIDTH / 2) /* words/scan */ #define LPT_DRUM 64 /* drum rows */ #define LPT_SVCSH 01 /* shuttle */ #define LPT_SVCPA 02 /* paper advance */ extern int32 dev_int, dev_enb; extern int32 stop_inst; extern uint32 chan_req; int32 lpt_wdpos = 0; /* word position */ int32 lpt_drpos = 0; /* drum position */ int32 lpt_crpos = 0; /* carriage position */ int32 lpt_svcst = 0; /* service state */ int32 lpt_svcch = 0; /* service channel */ int32 lpt_rdy = 0; /* transfer flag */ int32 lpt_prdn = 1; /* printing done */ int32 lpt_dma = 0; /* use DMA/DMC */ int32 lpt_eor = 0; /* DMA/DMC end range */ char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */ int32 lpt_xtime = 5; /* transfer time */ int32 lpt_etime = 50; /* end of scan time */ int32 lpt_ptime = 5000; /* paper adv time */ int32 lpt_stopioe = 0; /* stop on error */ int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_mod LPT modifiers lpt_reg LPT register list */ DIB lpt_dib = { LPT, IOBUS, 1, &lptio }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG lpt_reg[] = { { DRDATA (WDPOS, lpt_wdpos, 6) }, { DRDATA (DRPOS, lpt_drpos, 6) }, { FLDATA (CRPOS, lpt_crpos, 0) }, { FLDATA (RDY, lpt_rdy, 0) }, { FLDATA (EOR, lpt_eor, 0) }, { FLDATA (DMA, lpt_dma, 0) }, { FLDATA (PRDN, lpt_prdn, 0) }, { FLDATA (INTREQ, dev_int, INT_V_LPT) }, { FLDATA (ENABLE, dev_enb, INT_V_LPT) }, { ORDATA (SVCST, lpt_svcst, 2) }, { ORDATA (SVCCH, lpt_svcch, 2) }, { BRDATA (BUF, lpt_buf, 8, 8, 120) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (XTIME, lpt_xtime, 24), PV_LEFT }, { DRDATA (ETIME, lpt_etime, 24), PV_LEFT }, { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { NULL } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &lpt_reset, NULL, NULL, NULL, &lpt_dib, DEV_DISABLE }; /* IO routine */ int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev) { int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */ int32 chr; switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ switch (fnc) { /* case on fnc */ case 000: case 002: case 004: /* paper adv */ lpt_svcst = lpt_svcst | LPT_SVCPA; /* set state */ lpt_svcch = fnc >> 1; /* save channel */ sim_activate (&lpt_unit, lpt_ptime); CLR_INT (INT_LPT); /* clear int */ break; case 003: /* init scan DMA/DMC */ lpt_prdn = 0; /* clear pr done */ lpt_wdpos = 0; /* init scan pos */ lpt_eor = 0; if (ch >= 0) /* try for DMA/DMC */ lpt_dma = 1; else lpt_dma = 0; if (!sim_is_active (&lpt_unit)) { lpt_rdy = 1; if (lpt_dma) SET_CH_REQ (ch); } CLR_INT (INT_LPT); /* clear int */ break; case 007: /* init scan IO bus */ lpt_prdn = 0; /* clear pr done */ lpt_wdpos = 0; /* init scan pos */ lpt_eor = 0; lpt_dma = 0; /* IO bus */ if (!sim_is_active (&lpt_unit)) lpt_rdy = 1; CLR_INT (INT_LPT); /* clear int */ break; default: return IOBADFNC (dat); } break; case ioSKS: /* SKS */ switch (fnc) { /* case on fnc */ case 000: /* if xfer rdy */ if (lpt_rdy) return IOSKIP (dat); break; case 002: /* if !alarm */ if (lpt_unit.flags & UNIT_ATT) return IOSKIP (dat); break; case 003: /* if odd col */ if (lpt_crpos) return IOSKIP (dat); break; case 004: /* if !interrupt */ if (!TST_INTREQ (INT_LPT)) return IOSKIP (dat); break; case 011: /* if line printed */ if (lpt_prdn) return IOSKIP (dat); break; case 012: /* if !shuttling */ if (!(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat); break; case 013: if (lpt_prdn && !(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat); break; case 014: /* if !advancing */ if (!(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat); break; case 015: if (lpt_prdn && !(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat); break; case 016: if (!(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) return IOSKIP (dat); break; case 017: if (lpt_prdn && !(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) return IOSKIP (dat); break; default: return IOBADFNC (dat); } break; case ioOTA: /* OTA */ if (fnc) /* only fnc 0 */ return IOBADFNC (dat); if (lpt_rdy) { /* xfer ready? */ lpt_rdy = 0; /* clear xfer */ chr = (dat >> (lpt_crpos? 0: 8)) & 077; /* get 6b char */ if (chr == lpt_drpos) { /* match drum pos? */ if (chr < 040) chr = chr | 0100; lpt_buf[2 * lpt_wdpos + lpt_crpos] = chr; } lpt_wdpos++; /* adv scan pos */ if (lpt_wdpos >= LPT_SCAN) { /* end of scan? */ lpt_wdpos = 0; /* reset scan pos */ lpt_drpos++; /* adv drum pos */ if (lpt_drpos >= LPT_DRUM) { /* end of drum? */ lpt_drpos = 0; /* reset drum pos */ lpt_crpos = lpt_crpos ^ 1; /* shuttle */ lpt_svcst = lpt_svcst | LPT_SVCSH; sim_activate (&lpt_unit, lpt_ptime); } /* end if shuttle */ else sim_activate (&lpt_unit, lpt_etime); } /* end if endscan */ else sim_activate (&lpt_unit, lpt_xtime); return IOSKIP (dat); /* skip return */ } break; case ioEND: /* end DMA/DMC */ lpt_eor = 1; /* set end range */ break; } /* end case op */ return dat; } /* Unit service */ t_stat lpt_svc (UNIT *uptr) { int32 i; int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */ static const char *lpt_cc[] = { "\r", "\n", "\n\f", "\n" }; if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); if (lpt_dma) { /* DMA/DMC? */ if (lpt_eor) /* end range? intr */ SET_INT (INT_LPT); else { lpt_rdy = 1; /* set ready */ SET_CH_REQ (ch); /* get more data */ } } else lpt_rdy = 1; /* IO, continue scan */ if (lpt_svcst & LPT_SVCSH) { /* shuttling? */ SET_INT (INT_LPT); /* interrupt */ if (lpt_crpos == 0) { /* done shuttling? */ for (i = LPT_WIDTH - 1; i >= 0; i--) { /* backscan for blanks */ if (lpt_buf[i] != ' ') break; } lpt_buf[i + 1] = 0; fputs (lpt_buf, uptr->fileref); /* output buf */ uptr->pos = ftell (uptr->fileref); /* update pos */ for (i = 0; i < LPT_WIDTH; i++) /* clear buf */ lpt_buf[i] = ' '; lpt_prdn = 1; /* print done */ } } if (lpt_svcst & LPT_SVCPA) { /* paper advance */ SET_INT (INT_LPT); /* interrupt */ fputs (lpt_cc[lpt_svcch & 03], uptr->fileref); /* output eol */ uptr->pos = ftell (uptr->fileref); /* update pos */ } lpt_svcst = 0; return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { int32 i; lpt_wdpos = lpt_drpos = lpt_crpos = 0; /* clear positions */ lpt_svcst = lpt_svcch = 0; /* idle state */ lpt_rdy = 0; /* not rdy to xfer */ lpt_prdn = 1; /* printing done */ lpt_eor = 0; lpt_dma = 0; for (i = 0; i < LPT_WIDTH; i++) /* clear buffer */ lpt_buf[i] = ' '; lpt_buf[LPT_WIDTH] = 0; CLR_INT (INT_LPT); /* clear int, enb */ CLR_ENB (INT_LPT); sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/H316/h316_dp.c0000644000175000017500000013124411111656424012600 0ustar vlmvlm/* h316_dp.c: Honeywell 4623, 4651, 4720 disk simulator Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dp 4623 disk subsystem 4651 disk subsystem 4720 disk subsystem 04-Sep-05 RMS Fixed missing return (found by Peter Schorn) 15-Jul-05 RMS Fixed bug in attach routine 01-Dec-04 RMS Fixed bug in skip on !seeking The Honeywell disks have the unique characteristic of supporting variable formatting, on a per track basis. To accomodate this, each track is simulated as 2048 words, divided into records. (2048 words accomodates the largest record of 1891 + 8 overhead words.) A record is structured as follows: word 0 record length n (0 = end of track) word 1 record address (16b, uninterpreted by the simulator) word 2 record extension (0 to 4 words of permitted 'overwrite') word 3 first data word : word 3+n-1 last data word word 3+n checksum word word 4+n first extension word : word 7+n fourth extension word word 8+n start of next record Formatting is done in two ways. The SET DPn FORMAT=k command formats unit n with k records per track, each record having the maximum allowable record size and a standard record address; or with k words per record. Alternately, the simulator allows programmatic formating. When a track is formated, the program supplies record parameters as follows: word 0 record address words 1-n data words word n+1 gap size in bits To make this work, the simulator tracks the consumption of bits in the track, against the track capacity in bits. Bit consumption is: 16.5 * 16 for overhead (including address and checksum) n * 16 for data 'gap' for gap, which must be at least 5% of the record length */ #include "h316_defs.h" #include #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_WLK (1 << UNIT_V_WLK) #define FNC u3 /* saved function */ #define CYL u4 /* actual cylinder */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ #define DP_TRKLEN 2048 /* track length, words */ #define DP_NUMDRV 8 /* max # drives */ #define DP_NUMTYP 3 /* # controller types */ /* Record format */ #define REC_LNT 0 /* length (unextended) */ #define REC_ADDR 1 /* address */ #define REC_EXT 2 /* extension (0-4) */ #define REC_DATA 3 /* start of data */ #define REC_OVHD 8 /* overhead words */ #define REC_MAXEXT 4 /* maximum extension */ #define REC_OVHD_WRDS 16.5 /* 16.5 words */ #define REC_OVHD_BITS ((16 * 16) + 8) /* Status word, ^ = dynamic */ #define STA_BUSY 0100000 /* busy */ #define STA_RDY 0040000 /* ready */ #define STA_ADRER 0020000 /* address error */ #define STA_FMTER 0010000 /* format error */ #define STA_HNLER 0004000 /* heads not loaded (NI) */ #define STA_OFLER 0002000 /* offline */ #define STA_SEKER 0001000 /* seek error */ #define STA_MBZ 0000700 #define STA_WPRER 0000040 /* write prot error */ #define STA_UNSER 0000020 /* unsafe */ #define STA_CSMER 0000010 /* checksum error */ #define STA_DTRER 0000004 /* transfer rate error */ #define STA_ANYER 0000002 /* any error^ */ #define STA_EOR 0000001 /* end of record */ #define STA_ALLERR (STA_ADRER|STA_FMTER|STA_HNLER|STA_OFLER|STA_SEKER|\ STA_WPRER|STA_UNSER|STA_DTRER) /* Functions */ #define FNC_SK0 0000 /* recalibrate */ #define FNC_SEEK 0001 /* seek */ #define FNC_RCA 0002 /* read current */ #define FNC_UNL 0004 /* unload */ #define FNC_FMT 0005 /* format */ #define FNC_RW 0006 /* read/write */ #define FNC_STOP 0010 /* stop format */ #define FNC_RDS 0011 /* read status */ #define FNC_DMA 0013 /* DMA/DMC */ #define FNC_AKI 0014 /* acknowledge intr */ #define FNC_IOBUS 0017 /* IO bus */ #define FNC_2ND 0020 /* second state */ #define FNC_3RD 0040 /* third state */ #define FNC_4TH 0060 /* fourth state */ #define FNC_5TH 0100 /* fifth state */ /* Command word 1 */ #define CW1_RW 0100000 /* read/write */ #define CW1_DIR 0000400 /* seek direction */ #define CW1_V_UNIT 11 /* unit select */ #define CW1_V_HEAD 6 /* head select */ #define CW1_V_OFFS 0 /* seek offset */ #define CW1_GETUNIT(x) (((x) >> CW1_V_UNIT) & dp_tab[dp_ctype].umsk) #define CW1_GETHEAD(x) (((x) >> CW1_V_HEAD) & dp_tab[dp_ctype].hmsk) #define CW1_GETOFFS(x) (((x) >> CW1_V_OFFS) & dp_tab[dp_ctype].cmsk) /* OTA states */ #define OTA_NOP 0 /* normal */ #define OTA_CW1 1 /* expecting CW1 */ #define OTA_CW2 2 /* expecting CW2 */ /* Transfer state */ #define XIP_UMSK 007 /* unit mask */ #define XIP_SCHED 010 /* scheduled */ #define XIP_WRT 020 /* write */ #define XIP_FMT 040 /* format */ /* The H316/516 disk emulator supports three disk controllers: controller units cylinders surfaces data words per track 4651 4 203 2 1908.25 4623 8 203 10 1816.5 4720 8 203 20 1908.25 Disk types may not be intermixed on the same controller. */ #define TYPE_4651 0 #define UNIT_4651 4 #define CYL_4651 203 #define SURF_4651 2 #define WRDS_4651 1908.25 #define UMSK_4651 0003 #define HMSK_4651 0001 #define CMSK_4651 0377 #define CAP_4651 (CYL_4651*SURF_4651*DP_TRKLEN) #define TYPE_4623 1 #define UNIT_4623 8 #define CYL_4623 203 #define SURF_4623 10 #define WRDS_4623 1816.5 #define UMSK_4623 0007 #define HMSK_4623 0017 #define CMSK_4623 0377 #define CAP_4623 (CYL_4623*SURF_4623*DP_TRKLEN) #define TYPE_4720 2 #define UNIT_4720 8 #define CYL_4720 203 #define SURF_4720 20 #define WRDS_4720 1908.25 #define UMSK_4720 0007 #define HMSK_4720 0037 #define CMSK_4720 0377 #define CAP_4720 (CYL_4720*SURF_4720*DP_TRKLEN) struct drvtyp { char *name; uint32 numu; uint32 cyl; uint32 surf; uint32 cap; uint32 umsk; uint32 hmsk; uint32 cmsk; float wrds; }; #define DP_DRV(d) \ #d, \ UNIT_##d, CYL_##d, SURF_##d, CAP_##d, \ UMSK_##d, HMSK_##d, CMSK_##d, WRDS_##d static struct drvtyp dp_tab[] = { { DP_DRV (4651) }, { DP_DRV (4623) }, { DP_DRV (4720) } }; extern int32 dev_int, dev_enb, chan_req; extern int32 stop_inst; extern uint32 dma_ad[DMA_MAX]; extern int32 sim_switches; uint32 dp_cw1 = 0; /* cmd word 1 */ uint32 dp_cw2 = 0; /* cmd word 2 */ uint32 dp_fnc = 0; /* saved function */ uint32 dp_buf = 0; /* buffer */ uint32 dp_otas = 0; /* state */ uint32 dp_sta = 0; /* status */ uint32 dp_defint = 0; /* deferred seek int */ uint32 dp_ctype = TYPE_4651; /* controller type */ uint32 dp_dma = 0; /* DMA/DMC */ uint32 dp_eor = 0; /* end of range */ uint32 dp_xip = 0; /* transfer in prog */ uint32 dp_csum = 0; /* parity checksum */ uint32 dp_rptr = 0; /* start of record */ uint32 dp_wptr = 0; /* word ptr in record */ uint32 dp_bctr = 0; /* format bit cntr */ uint32 dp_gap = 0; /* format gap size */ uint32 dp_stopioe = 1; /* stop on error */ int32 dp_stime = 1000; /* seek per cylinder */ int32 dp_xtime = 10; /* xfer per word */ int32 dp_btime = 30; /* busy time */ uint16 dpxb[DP_TRKLEN]; /* track buffer */ int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat dp_svc (UNIT *uptr); t_stat dp_reset (DEVICE *dptr); t_stat dp_attach (UNIT *uptr, char *cptr); t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat dp_go (uint32 dma); t_stat dp_go1 (uint32 dat); t_stat dp_go2 (uint32 dat); t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd); t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd); t_bool dp_findrec (uint32 addr); t_stat dp_wrwd (UNIT *uptr, uint32 dat); t_stat dp_wrdone (UNIT *uptr, uint32 flg); t_stat dp_done (uint32 req, uint32 f); t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc); /* DP data structures dp_dev DP device descriptor dp_unit DP unit list dp_reg DP register list dp_mod DP modifier list */ DIB dp_dib = { DP, DMC1, 1, &dpio }; UNIT dp_unit[] = { { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, CAP_4651) } }; REG dp_reg[] = { { ORDATA (STA, dp_sta, 16) }, { ORDATA (BUF, dp_buf, 16) }, { ORDATA (FNC, dp_fnc, 4) }, { ORDATA (CW1, dp_cw1, 16) }, { ORDATA (CW2, dp_cw2, 16) }, { ORDATA (CSUM, dp_csum, 16) }, { FLDATA (BUSY, dp_sta, 15) }, { FLDATA (RDY, dp_sta, 14) }, { FLDATA (EOR, dp_eor, 0) }, { FLDATA (DEFINT, dp_defint, 0) }, { FLDATA (INTREQ, dev_int, INT_V_DP) }, { FLDATA (ENABLE, dev_enb, INT_V_DP) }, { BRDATA (TBUF, dpxb, 8, 16, DP_TRKLEN) }, { ORDATA (RPTR, dp_rptr, 11), REG_RO }, { ORDATA (WPTR, dp_wptr, 11), REG_RO }, { ORDATA (BCTR, dp_bctr, 15), REG_RO }, { ORDATA (GAP, dp_gap, 16), REG_RO }, { DRDATA (STIME, dp_stime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, dp_xtime, 24), REG_NZ + PV_LEFT }, { DRDATA (BTIME, dp_btime, 24), REG_NZ + PV_LEFT }, { FLDATA (CTYPE, dp_ctype, 0), REG_HRO }, { URDATA (UCYL, dp_unit[0].CYL, 10, 8, 0, DP_NUMDRV, PV_LEFT | REG_HRO) }, { URDATA (UFNC, dp_unit[0].FNC, 8, 7, 0, DP_NUMDRV, REG_HRO) }, { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0, DP_NUMDRV, PV_LEFT | REG_HRO) }, { ORDATA (OTAS, dp_otas, 2), REG_HRO }, { ORDATA (XIP, dp_xip, 6), REG_HRO }, { ORDATA (CHAN, dp_dib.chan, 5), REG_HRO }, { FLDATA (STOP_IOE, dp_stopioe, 0) }, { NULL } }; MTAB dp_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV, TYPE_4623, NULL, "4623", &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, TYPE_4651, NULL, "4651", &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, TYPE_4720, NULL, "4720", &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &dp_showtype, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", &io_set_dmc, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", &io_set_dma, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &io_show_chan, NULL }, { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "FORMAT", "FORMAT", &dp_setformat, &dp_showformat, NULL }, { 0 } }; DEVICE dp_dev = { "DP", dp_unit, dp_reg, dp_mod, DP_NUMDRV, 8, 24, 1, 8, 16, NULL, NULL, &dp_reset, NULL, &dp_attach, NULL, &dp_dib, DEV_DISABLE }; /* IOT routines */ int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev) { int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ int32 u; UNIT *uptr; switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ switch (fnc) { /* case on function */ case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */ case FNC_UNL: case FNC_FMT: case FNC_RW: dp_go (fnc); /* if !busy, start */ break; case FNC_STOP: /* stop transfer */ if (dp_xip) { /* transfer in prog? */ uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */ sim_cancel (uptr); /* stop operation */ if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */ dp_wrdone (uptr, /* write track */ ((dp_xip & XIP_FMT) && /* check fmt state */ (uptr->FNC != (FNC_FMT|FNC_2ND)))? STA_DTRER: 0); else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */ dp_xip = 0; /* clear flag */ } dp_otas = OTA_NOP; /* clear state */ dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ break; case FNC_RDS: /* read status */ if (dp_sta & STA_BUSY) /* ignore if busy */ return dat; dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER); if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER; dp_buf = dp_sta; if (dp_dma && Q_DMA (ch)) /* DMA? set chan req */ SET_CH_REQ (ch); break; case FNC_DMA: /* set DMA/DMC */ dp_dma = 1; break; case FNC_IOBUS: /* set IO bus */ dp_dma = 0; break; case FNC_AKI: /* ack intr */ CLR_INT (INT_DP); break; default: /* undefined */ return IOBADFNC (dat); } break; case ioINA: /* INA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (dp_sta & STA_RDY) { /* ready? */ dp_sta = dp_sta & ~STA_RDY; /* clear ready */ return IOSKIP (dat | dp_buf); /* ret buf, skip */ } break; case ioOTA: /* OTA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (dp_sta & STA_RDY) { /* ready? */ dp_sta = dp_sta & ~STA_RDY; /* clear ready */ dp_buf = dat; /* store buf */ if (dp_otas == OTA_CW1) /* expecting CW1? */ dp_go1 (dat); else if (dp_otas == OTA_CW2) /* expecting CW2? */ dp_go2 (dat); return IOSKIP (dat); } break; case ioSKS: /* SKS */ u = 7; /* assume unit 7 */ switch (fnc) { case 000: /* ready */ if (dp_sta & STA_RDY) return IOSKIP (dat); break; case 001: /* !interrupting */ if (!TST_INTREQ (INT_DP)) return IOSKIP (dat); break; case 002: /* operational */ if (!(dp_sta & (STA_BUSY | STA_ALLERR))) return IOSKIP (dat); break; case 003: /* !error */ if (!(dp_sta & STA_ALLERR)) return IOSKIP (dat); break; case 004: /* !busy */ if (!(dp_sta & STA_BUSY)) return IOSKIP (dat); break; case 011: case 012: case 013: /* !not seeking 0-6 */ case 014: case 015: case 016: case 017: u = fnc - 011; case 007: /* !not seeking 7 */ if (!sim_is_active (&dp_unit[u]) || /* quiescent? */ (dp_unit[u].FNC != (FNC_SEEK | FNC_2ND))) return IOSKIP (dat); /* seeking sets late */ break; } break; case ioEND: /* end of range */ dp_eor = 1; /* transfer done */ break; } return dat; } /* Start new operation - recal, seek, read address, format, read/write */ t_stat dp_go (uint32 fnc) { int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ if (dp_sta & STA_BUSY) /* ignore if busy */ return SCPE_OK; dp_fnc = fnc; /* save function */ dp_xip = 0; /* transfer not started */ dp_eor = 0; /* not end of range */ dp_csum = 0; /* init checksum */ dp_otas = OTA_CW1; /* expect CW1 */ dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR); if (dp_dma && Q_DMA (ch)) { /* DMA and DMA channel? */ SET_CH_REQ (ch); /* set channel request */ dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */ } return SCPE_OK; } /* Process command word 1 - recal, seek, read address, format, read/write */ t_stat dp_go1 (uint32 dat) { int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ uint32 u = CW1_GETUNIT (dat); UNIT *uptr = dp_dev.units + u; dp_cw1 = dat; /* store CW1 */ dp_otas = OTA_NOP; /* assume no CW2 */ uptr->FNC = dp_fnc; if (sim_is_active (uptr)) /* still seeking? */ return dp_done (1, STA_UNSER); /* unsafe */ if (!(uptr->flags & UNIT_ATT)) /* not attached? */ return dp_done (1, STA_OFLER); /* offline */ switch (dp_fnc) { /* case on function */ case FNC_SEEK: /* seek */ case FNC_SK0: /* recalibrate */ case FNC_UNL: /* unload */ sim_activate (uptr, dp_btime); /* quick timeout */ break; case FNC_FMT: /* format */ if (uptr->flags & UNIT_WPRT) /* write protect? */ return dp_done (1, STA_WPRER); /* stop now */ case FNC_RCA: /* read current addr */ dp_xip = u | XIP_SCHED; /* operation started */ sim_activate (uptr, dp_xtime * 10); /* rotation timeout */ break; case FNC_RW: /* read/write */ dp_otas = OTA_CW2; /* expect CW2 */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma && Q_DMA (ch)) /* DMA? set chan request */ SET_CH_REQ (ch); break; } return SCPE_OK; } /* Process command word 2 - read/write only */ t_stat dp_go2 (uint32 dat) { uint32 u = CW1_GETUNIT (dp_cw1); UNIT *uptr = dp_dev.units + u; dp_cw2 = dat; /* store CW2 */ dp_otas = OTA_NOP; /* normal state */ sim_activate (uptr, dp_xtime * 10); /* rotation timeout */ dp_xip = u | XIP_SCHED; /* operation started */ return SCPE_OK; } /* Unit service */ t_stat dp_svc (UNIT *uptr) { int32 dcyl = 0; /* assume recalibrate */ int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ uint32 h = CW1_GETHEAD (dp_cw1); /* head */ int32 st; uint32 i, offs, lnt, ming, tpos; t_stat r; if (!(uptr->flags & UNIT_ATT)) { /* not attached? */ dp_done (1, STA_OFLER); /* offline */ return IORETURN (dp_stopioe, SCPE_UNATT); } switch (uptr->FNC) { /* case on function */ case FNC_SEEK: /* seek, need cyl */ offs = CW1_GETOFFS (dp_cw1); /* get offset */ if (dp_cw1 & CW1_DIR) /* get desired cyl */ dcyl = uptr->CYL - offs; else dcyl = uptr->CYL + offs; if ((offs == 0) || (dcyl < 0) || (dcyl >= (int32) dp_tab[dp_ctype].cyl)) return dp_done (1, STA_SEKER); /* bad seek? */ case FNC_SK0: /* recalibrate */ dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */ st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */ if (st == 0) st = dp_stime; uptr->CYL = dcyl; /* put on cylinder */ sim_activate (uptr, st); return SCPE_OK; case FNC_SEEK | FNC_2ND: /* seek, 2nd state */ if (dp_sta & STA_BUSY) /* busy? queue intr */ dp_defint = 1; else SET_INT (INT_DP); /* no, req intr */ return SCPE_OK; case FNC_UNL: /* unload */ detach_unit (uptr); /* detach unit */ return dp_done (0, 0); /* clear busy, no intr */ case FNC_RCA: /* read current addr */ if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ return dp_done (1, STA_ADRER); /* error */ if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ return r; dp_rptr = 0; /* init rec ptr */ if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */ return dp_done (1, STA_ADRER); /* error */ tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN)); do { /* scan down track */ dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */ dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD; } while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0)); if (dp_dma) { /* DMA/DMC? */ if (Q_DMA (ch)) /* DMA? */ dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ SET_CH_REQ (ch); /* request chan */ } return dp_done (1, STA_RDY); /* clr busy, set rdy */ /* Formating takes place in five states: init - clear track buffer, start at first record address - store address word data - store data word(s) until end of range pause - wait for gap word or stop command gap - validate gap word, advance to next record Note that formating is stopped externally by an OCP command; the track buffer is flushed at that point. If the stop does not occur in the proper state (gap word received), a format error occurs. */ case FNC_FMT: /* format */ for (i = 0; i < DP_TRKLEN; i++) /* clear track */ dpxb[i] = 0; dp_xip = dp_xip | XIP_FMT; /* format in progress */ dp_rptr = 0; /* init record ptr */ dp_gap = 0; /* no gap before first */ dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */ uptr->FNC = uptr->FNC | FNC_2ND; /* address state */ break; /* set up next word */ case FNC_FMT | FNC_2ND: /* format, address word */ dp_wptr = 0; /* clear word ptr */ if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */ return dp_wrdone (uptr, STA_FMTER); /* no, format error */ dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */ dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */ uptr->FNC = FNC_FMT | FNC_3RD; /* data state */ if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) /* DMA/DMC? intr */ SET_INT (INT_DP); } break; /* set up next word */ case FNC_FMT | FNC_3RD: /* format, data word */ if (dp_sta & STA_RDY) /* timing failure? */ return dp_wrdone (uptr, STA_DTRER); /* write trk, err */ else { /* no, have word */ if (dp_bctr < 16) /* room for it? */ return dp_wrdone (uptr, STA_FMTER); /* no, error */ dp_bctr = dp_bctr - 16; /* charge for word */ dp_csum = dp_csum ^ dp_buf; /* update checksum */ dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */ dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */ dp_wptr++; /* incr word ptr */ } if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) /* DMA/DMC? intr */ SET_INT (INT_DP); dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */ uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */ sim_activate (uptr, 5 * dp_xtime); /* schedule pause */ return SCPE_OK; /* don't request word */ } break; /* set up next word */ case FNC_FMT | FNC_4TH: /* format, pause */ uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */ break; /* request word */ case FNC_FMT | FNC_5TH: /* format, gap word */ ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */ if (dp_buf < ming) /* too small? */ return dp_wrdone (uptr, STA_FMTER); /* yes, format error */ dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */ uptr->FNC = FNC_FMT | FNC_2ND; /* address state */ if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */ } dp_gap = dp_buf; /* save gap */ dp_csum = 0; /* clear checksum */ break; /* set up next word */ /* Read and write take place in two states: init - read track into buffer, find record, validate parameters data - (read) fetch data from buffer, stop on end of range - (write) write data into buffer, flush on end of range */ case FNC_RW: /* read/write */ if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ return dp_done (1, STA_ADRER); /* error */ if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ return r; if (!dp_findrec (dp_cw2)) /* find rec; error? */ return dp_done (1, STA_ADRER); /* address error */ if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) || (dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */ dp_done (1, STA_UNSER); /* stop simulation */ return STOP_DPFMT; /* bad format */ } uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ if (dp_cw1 & CW1_RW) { /* write? */ if (uptr->flags & UNIT_WPRT) /* write protect? */ return dp_done (1, STA_WPRER); /* error */ dp_xip = dp_xip | XIP_WRT; /* write in progress */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); } else if (Q_DMA (ch)) /* read; DMA? */ dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ sim_activate (uptr, dp_xtime); /* schedule word */ dp_wptr = 0; /* init word pointer */ return SCPE_OK; case FNC_RW | FNC_2ND: /* read/write, word */ if (dp_cw1 & CW1_RW) { /* write? */ if (dp_sta & STA_RDY) /* timing failure? */ return dp_wrdone (uptr, STA_DTRER); /* yes, error */ if (r = dp_wrwd (uptr, dp_buf)) /* wr word, error? */ return r; if (dp_eor) { /* transfer done? */ dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; return dp_wrdone (uptr, 0); /* clear busy, intr req */ } } else { /* read? */ lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT]; dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */ dp_csum = dp_csum ^ dp_buf; /* xor to csum */ if ((dp_wptr > lnt) || dp_eor) /* transfer done? */ return dp_done (1, (dp_csum? STA_CSMER: 0) | ((dp_wptr >= lnt)? STA_EOR: 0)); if (dp_sta & STA_RDY) /* data buf full? */ return dp_done (1, STA_DTRER); /* no, underrun */ dp_wptr++; /* next word */ } break; default: return SCPE_IERR; } /* end case */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); sim_activate (uptr, dp_xtime); /* schedule word */ return SCPE_OK; } /* Read track */ t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h) { uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN; int32 l; fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref); for ( ; l < DP_TRKLEN; l++) buf[l] = 0; if (ferror (uptr->fileref)) { perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (1, STA_UNSER); return SCPE_IOERR; } return SCPE_OK; } /* Write track */ t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h) { uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN; fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref); if (ferror (uptr->fileref)) { perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (1, STA_UNSER); return SCPE_IOERR; } return SCPE_OK; } /* Find record; true if found, false if not found */ t_bool dp_findrec (uint32 addr) { dp_rptr = 0; do { if (dpxb[dp_rptr + REC_LNT] == 0) return FALSE; if (dpxb[dp_rptr + REC_LNT] >= DP_TRKLEN) return TRUE; if (dpxb[dp_rptr + REC_ADDR] == addr) return TRUE; dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD; } while (dp_rptr < DP_TRKLEN); return FALSE; } /* Write next word to track buffer; return TRUE if ok, FALSE if next record trashed */ t_stat dp_wrwd (UNIT *uptr, uint32 dat) { uint32 lnt = dpxb[dp_rptr + REC_LNT]; t_stat r; dp_csum = dp_csum ^ dat; if (dp_wptr < lnt) { dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat; return SCPE_OK; } if (dp_wptr < (lnt + REC_MAXEXT)) { dpxb[dp_rptr + REC_EXT]++; dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat; return SCPE_OK; } dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */ dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */ if (r = dp_wrdone (uptr, STA_UNSER)) /* dump track */ return r; return STOP_DPOVR; } /* Write done, dump track, clear busy */ t_stat dp_wrdone (UNIT *uptr, uint32 flg) { dp_done (1, flg); return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1)); } /* Clear busy, set errors, request interrupt if required */ t_stat dp_done (uint32 req, uint32 flg) { dp_xip = 0; /* clear xfr in prog */ dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */ if (req || dp_defint) /* if req, set intr */ SET_INT (INT_DP); dp_defint = 0; /* clr def intr */ return SCPE_OK; } /* Reset routine */ t_stat dp_reset (DEVICE *dptr) { int32 i; dp_fnc = 0; dp_cw1 = 0; dp_cw2 = 0; dp_sta = 0; dp_buf = 0; dp_xip = 0; dp_eor = 0; dp_dma = 0; dp_csum = 0; dp_rptr = 0; dp_wptr = 0; dp_bctr = 0; dp_gap = 0; dp_defint = 0; for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ sim_cancel (&dp_unit[i]); /* cancel activity */ dp_unit[i].FNC = 0; /* clear function */ dp_unit[i].CYL = 0; } CLR_INT (INT_DP); /* clear int, enb */ CLR_ENB (INT_DP); return SCPE_OK; } /* Attach routine, test formating */ t_stat dp_attach (UNIT *uptr, char *cptr) { t_stat r; r = attach_unit (uptr, cptr); if (r != SCPE_OK) return r; return dp_showformat (stdout, uptr, 0, NULL); } /* Set controller type */ t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL)) return SCPE_ARG; for (i = 0; i < DP_NUMDRV; i++) { if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } for (i = 0; i < DP_NUMDRV; i++) dp_unit[i].capac = dp_tab[val].cap; dp_ctype = val; return SCPE_OK; } /* Show controller type */ t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) { if (dp_ctype >= DP_NUMTYP) return SCPE_IERR; fprintf (st, "%s", dp_tab[dp_ctype].name); return SCPE_OK; } /* Set drive format There is no standard format for record addresses. This routine provides two schemes: -S sequential addressing (starting from 0) default geometric addressing (8b: cylinder, 5b: head, 3b: sector) This routine also supports formatting by record count or word count: -R argument is records per track default argument is words per record The relationship between words per record (W), bits per track (B), and records per track (R), is as follows: W = (B / (R + ((R - 1) / 20))) - 16.5 where (R - 1) / 20 is the "5% gap" and 16.5 is the overhead, in words, per record. */ t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 h, c, cntr, rptr; int32 i, nr, nw, inp; uint16 tbuf[DP_TRKLEN]; float finp; t_stat r; if (uptr == NULL) return SCPE_IERR; if (cptr == NULL) return SCPE_ARG; if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; inp = (int32) get_uint (cptr, 10, 2048, &r); if (r != SCPE_OK) return r; if (inp == 0) return SCPE_ARG; finp = (float) inp; if (sim_switches & SWMASK ('R')) { /* format records? */ nr = inp; nw = (int32) ((dp_tab[dp_ctype].wrds / (finp + ((finp - 1.0) / 20.0))) - REC_OVHD_WRDS); if (nw <= 0) return SCPE_ARG; } else { nw = inp; /* format words */ nr = (int32) ((((20.0 * dp_tab[dp_ctype].wrds) / (finp + REC_OVHD_WRDS)) + 1.0) / 21.0); if (nr <= 0) return SCPE_ARG; } printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw); if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE)) return SCPE_OK; for (c = cntr = 0; c < dp_tab[dp_ctype].cyl; c++) { for (h = 0; h < dp_tab[dp_ctype].surf; h++) { for (i = 0; i < DP_TRKLEN; i++) tbuf[i] = 0; rptr = 0; for (i = 0; i < nr; i++) { tbuf[rptr + REC_LNT] = nw & DMASK; if (sim_switches & SWMASK ('S')) tbuf[rptr + REC_ADDR] = cntr++; else tbuf[rptr + REC_ADDR] = (c << 8) + (h << 3) + i; rptr = rptr + nw + REC_OVHD; } if (r = dp_wrtrk (uptr, tbuf, c, h)) return r; } } printf ("Formatting complete\n"); return SCPE_OK; } /* Show format */ t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 c, h, rptr, rlnt, sec; uint32 minrec = DP_TRKLEN; uint32 maxrec = 0; uint32 minsec = DP_TRKLEN; uint32 maxsec = 0; uint16 tbuf[DP_TRKLEN]; t_stat r; if (uptr == NULL) return SCPE_IERR; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; for (c = 0; c < dp_tab[dp_ctype].cyl; c++) { for (h = 0; h < dp_tab[dp_ctype].surf; h++) { if (r = dp_rdtrk (uptr, tbuf, c, h)) return r; rptr = 0; rlnt = tbuf[rptr + REC_LNT]; if (rlnt == 0) { if (c || h) fprintf (st, "Unformatted track, cyl = %d, head = %d\n", c, h); else fprintf (st, "Disk is unformatted\n"); return SCPE_OK; } for (sec = 0; rlnt != 0; sec++) { if ((rptr + rlnt + REC_OVHD) >= DP_TRKLEN) { fprintf (st, "Invalid record length %d, cyl = %d, head = %d, sect = %d\n", rlnt, c, h, sec); return SCPE_OK; } if (tbuf[rptr + REC_EXT] >= REC_MAXEXT) { fprintf (st, "Invalid record extension %d, cyl = %d, head = %d, sect = %d\n", tbuf[rptr + REC_EXT], c, h, sec); return SCPE_OK; } if (rlnt > maxrec) maxrec = rlnt; if (rlnt < minrec) minrec = rlnt; rptr = rptr + rlnt + REC_OVHD; rlnt = tbuf[rptr + REC_LNT]; } if (sec > maxsec) maxsec = sec; if (sec < minsec) minsec = sec; } } if ((minrec == maxrec) && (minsec == maxsec)) fprintf (st, "Valid fixed format, records/track = %d, record size = %d\n", minsec, minrec); else if (minrec == maxrec) fprintf (st, "Valid variable format, records/track = %d-%d, record size = %d\n", minsec, maxsec, minrec); else if (minsec == maxsec) fprintf (st, "Valid variable format, records/track = %d, record sizes = %d-%d\n", minsec, minrec, maxrec); else fprintf (st, "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n", minsec, maxsec, minrec, maxrec); return SCPE_OK; } simh-3.8.1/H316/h316_fhd.c0000644000175000017500000004575611107404042012742 0ustar vlmvlm/* h316_fhd.c: H316/516 fixed head simulator Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. fhd 516-4400 fixed head disk 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) 04-Jan-04 RMS Changed sim_fsize calling sequence These head-per-track devices are buffered in memory, to minimize overhead. */ #include "h316_defs.h" #include /* Constants */ #define FH_NUMWD 1536 /* words/track */ #define FH_NUMTK 64 /* tracks/surface */ #define FH_WDPSF (FH_NUMWD * FH_NUMTK) /* words/surface */ #define FH_NUMSF 16 /* surfaces/ctlr */ #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_SF (UNIT_V_UF + 1) /* #surfaces - 1 */ #define UNIT_M_SF 017 #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_SF (UNIT_M_SF << UNIT_V_SF) #define UNIT_GETSF(x) ((((x) >> UNIT_V_SF) & UNIT_M_SF) + 1) /* Command word 1 */ #define CW1_RW 0100000 /* read vs write */ #define CW1_V_SF 10 /* surface */ #define CW1_M_SF 017 #define CW1_GETSF(x) (((x) >> CW1_V_SF) & CW1_M_SF) #define CW1_V_TK 4 /* track */ #define CW1_M_TK 077 #define CW1_GETTK(x) (((x) >> CW1_V_TK) & CW1_M_TK) /* Command word 2 */ #define CW2_V_CA 0 /* character addr */ #define CW2_M_CA 07777 #define CW2_GETCA(x) (((x) >> CW2_V_CA) & CW2_M_CA) #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) FH_NUMWD))) /* OTA states */ #define OTA_NOP 0 /* normal */ #define OTA_CW1 1 /* expecting CW1 */ #define OTA_CW2 2 /* expecting CW2 */ extern int32 dev_int, dev_enb, chan_req; extern int32 stop_inst; extern uint32 dma_ad[DMA_MAX]; uint32 fhd_cw1 = 0; /* cmd word 1 */ uint32 fhd_cw2 = 0; /* cmd word 2 */ uint32 fhd_buf = 0; /* buffer */ uint32 fhd_otas = 0; /* state */ uint32 fhd_busy = 0; /* busy */ uint32 fhd_rdy = 0; /* word ready */ uint32 fhd_dte = 0; /* data err */ uint32 fhd_ace = 0; /* access error */ uint32 fhd_dma = 0; /* DMA/DMC */ uint32 fhd_eor = 0; /* end of range */ uint32 fhd_csum = 0; /* parity checksum */ uint32 fhd_stopioe = 1; /* stop on error */ int32 fhd_time = 10; /* time per word */ int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev); t_stat fhd_svc (UNIT *uptr); t_stat fhd_reset (DEVICE *dptr); t_stat fhd_attach (UNIT *uptr, char *cptr); t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); void fhd_go (uint32 dma); void fhd_go1 (uint32 dat); void fhd_go2 (uint32 dat); t_bool fhd_getc (UNIT *uptr, uint32 *ch); t_bool fhd_putc (UNIT *uptr, uint32 ch); t_bool fhd_bad_wa (uint32 wa); uint32 fhd_csword (uint32 cs, uint32 ch); /* FHD data structures fhd_dev device descriptor fhd_unit unit descriptor fhd_mod unit modifiers fhd_reg register list */ DIB fhd_dib = { FHD, IOBUS, 1, &fhdio }; UNIT fhd_unit = { UDATA (&fhd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, FH_WDPSF) }; REG fhd_reg[] = { { ORDATA (CW1, fhd_cw1, 16) }, { ORDATA (CW2, fhd_cw2, 16) }, { ORDATA (BUF, fhd_buf, 16) }, { FLDATA (BUSY, fhd_busy, 0) }, { FLDATA (RDY, fhd_rdy, 0) }, { FLDATA (DTE, fhd_dte, 0) }, { FLDATA (ACE, fhd_ace, 0) }, { FLDATA (EOR, fhd_eor, 0) }, { FLDATA (DMA, fhd_dma, 0) }, { FLDATA (CSUM, fhd_csum, 7) }, { FLDATA (INTREQ, dev_int, INT_V_MT) }, { FLDATA (ENABLE, dev_enb, INT_V_MT) }, { DRDATA (TIME, fhd_time, 31), REG_NZ + PV_LEFT }, { ORDATA (OTAS, fhd_otas, 2), REG_HRO }, { ORDATA (CHAN, fhd_dib.chan, 5), REG_HRO }, { FLDATA (STOP_IOE, fhd_stopioe, 0) }, { NULL } }; MTAB fhd_mod[] = { { UNIT_SF, (0 << UNIT_V_SF), NULL, "1S", &fhd_set_size }, { UNIT_SF, (1 << UNIT_V_SF), NULL, "2S", &fhd_set_size }, { UNIT_SF, (2 << UNIT_V_SF), NULL, "3S", &fhd_set_size }, { UNIT_SF, (3 << UNIT_V_SF), NULL, "4S", &fhd_set_size }, { UNIT_SF, (4 << UNIT_V_SF), NULL, "5S", &fhd_set_size }, { UNIT_SF, (5 << UNIT_V_SF), NULL, "6S", &fhd_set_size }, { UNIT_SF, (6 << UNIT_V_SF), NULL, "7S", &fhd_set_size }, { UNIT_SF, (7 << UNIT_V_SF), NULL, "8S", &fhd_set_size }, { UNIT_SF, (8 << UNIT_V_SF), NULL, "9S", &fhd_set_size }, { UNIT_SF, (9 << UNIT_V_SF), NULL, "10S", &fhd_set_size }, { UNIT_SF, (10 << UNIT_V_SF), NULL, "11S", &fhd_set_size }, { UNIT_SF, (11 << UNIT_V_SF), NULL, "12S", &fhd_set_size }, { UNIT_SF, (12 << UNIT_V_SF), NULL, "13S", &fhd_set_size }, { UNIT_SF, (13 << UNIT_V_SF), NULL, "14S", &fhd_set_size }, { UNIT_SF, (14 << UNIT_V_SF), NULL, "15S", &fhd_set_size }, { UNIT_SF, (15 << UNIT_V_SF), NULL, "16S", &fhd_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS", &io_set_iobus, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", &io_set_dmc, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", &io_set_dma, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &io_show_chan, NULL }, { 0 } }; DEVICE fhd_dev = { "FHD", &fhd_unit, fhd_reg, fhd_mod, 1, 8, 22, 1, 8, 16, NULL, NULL, &fhd_reset, NULL, &fhd_attach, NULL, &fhd_dib, DEV_DISABLE }; /* IO routines */ int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev) { switch (inst) { /* case on opcode */ case ioOCP: /* control */ if (fnc == 04) { /* terminate output? */ fhd_eor = 1; /* stop */ CLR_INT (INT_FHD); /* clear int req */ } else if (fnc == 003) /* start, DMA */ fhd_go (1); else if (fnc == 007) /* start, IO bus */ fhd_go (0); else return IOBADFNC (dat); break; case ioOTA: /* output */ if (fnc) /* only fnc 0 */ return IOBADFNC (dat); if (fhd_rdy) { /* ready? */ fhd_buf = dat; /* store data */ if (fhd_otas == OTA_CW1) /* expecting CW1? */ fhd_go1 (dat); else if (fhd_otas == OTA_CW2) /* expecting CW2? */ fhd_go2 (dat); else fhd_rdy = 0; /* normal, clr ready */ return IOSKIP (dat); } break; case ioINA: /* input */ if (fnc) /* only fnc 0 */ return IOBADFNC (dat); if (fhd_rdy) { /* ready? */ fhd_rdy = 0; /* clear ready */ return IOSKIP (dat | fhd_buf); /* return data */ } break; case ioSKS: /* sense */ if (((fnc == 000) && fhd_rdy) || /* 0 = skip if ready */ ((fnc == 001) && !fhd_busy) || /* 1 = skip if !busy */ ((fnc == 002) && !fhd_dte) || /* 2 = skip if !data err */ ((fnc == 003) && !fhd_ace) || /* 3 = skip if !access err */ ((fnc == 004) && !TST_INTREQ (INT_FHD))) /* 4 = skip if !interrupt */ return IOSKIP (dat); break; case ioEND: fhd_eor = 1; break; } return dat; } /* Start new operation */ void fhd_go (uint32 dma) { int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ if (fhd_busy) /* ignore if busy */ return; fhd_busy = 1; /* ctlr is busy */ fhd_eor = 0; /* transfer not done */ fhd_csum = 0; /* init checksum */ fhd_dte = 0; /* clear errors */ fhd_ace = 0; if (ch >= 0) /* DMA allowed? */ fhd_dma = dma; else fhd_dma = 0; /* no, force IO bus */ fhd_otas = OTA_CW1; /* expect CW1 */ fhd_rdy = 1; /* set ready */ if (fhd_dma && Q_DMA (ch)) { /* DMA and DMA channel? */ SET_CH_REQ (ch); /* set channel request */ dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */ } return; } /* Process command word 1 */ void fhd_go1 (uint32 dat) { int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ fhd_cw1 = dat; /* store CW1 */ fhd_otas = OTA_CW2; /* expect CW2 */ fhd_rdy = 1; /* set ready */ if (fhd_dma && Q_DMA (ch)) /* DMA? set chan request */ SET_CH_REQ (ch); return; } /* Process command word 2 - initiate seek */ void fhd_go2 (uint32 dat) { int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ int32 t, wa; fhd_cw2 = dat; /* store CW2 */ fhd_otas = OTA_NOP; /* next state */ wa = CW2_GETCA (fhd_cw2) >> 1; /* word addr */ if ((wa >= FH_NUMWD) || /* if bad char addr */ ((fhd_unit.flags & UNIT_ATT) == 0) || /* or unattached */ (sf >= UNIT_GETSF (fhd_unit.flags))) { /* or bad surface */ fhd_ace = 1; /* access error */ fhd_busy = 0; /* abort operation */ SET_INT (INT_FHD); return; } if (fhd_cw1 & CW1_RW) { /* write? */ fhd_rdy = 1; /* set ready */ if (fhd_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); } else { fhd_rdy = 0; /* read, clear ready */ if (fhd_dma && (ch < DMC_V_DMC1)) /* read and DMA chan? */ dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ } t = wa - GET_POS (fhd_time); /* delta to new loc */ if (t < 0) /* wrap around? */ t = t + FH_NUMWD; sim_activate (&fhd_unit, t * fhd_time); /* schedule op */ return; } /* Unit service */ t_stat fhd_svc (UNIT *uptr) { int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan (-1 if IO bus) */ uint32 c1, c2; if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ fhd_ace = 1; /* access error */ fhd_busy = 0; /* abort operation */ SET_INT (INT_FHD); return IORETURN (fhd_stopioe, SCPE_UNATT); } if (fhd_eor || fhd_rdy) { /* done or ready set? */ if (fhd_rdy) /* if ready set, data err */ fhd_dte = 1; if (fhd_cw1 & CW1_RW) { /* write? */ if (!fhd_rdy) { /* buffer full? */ fhd_putc (uptr, fhd_buf >> 8); /* store last word */ fhd_putc (uptr, fhd_buf); } fhd_putc (uptr, fhd_csum); /* store csum */ } else { /* read */ fhd_getc (uptr, &c1); /* get csum */ if (fhd_csum) /* if csum != 0, err */ fhd_dte = 1; } fhd_busy = 0; /* operation complete */ SET_INT (INT_FHD); return SCPE_OK; } if (fhd_cw1 & CW1_RW) { /* write? */ if (fhd_putc (uptr, fhd_buf >> 8)) return SCPE_OK; if (fhd_putc (uptr, fhd_buf)) return SCPE_OK; } else { /* read */ if (fhd_getc (uptr, &c1)) return SCPE_OK; if (fhd_getc (uptr, &c2)) return SCPE_OK; fhd_buf = (c1 << 8) | c2; } sim_activate (uptr, fhd_time); /* next word */ fhd_rdy = 1; /* set ready */ if (fhd_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); return SCPE_OK; } /* Read character from disk */ t_bool fhd_getc (UNIT *uptr, uint32 *ch) { uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ uint32 tk = CW1_GETTK (fhd_cw1); /* track */ uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */ uint32 wa = ca >> 1; /* word addr */ uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */ uint16 *fbuf = uptr->filebuf; /* buffer base */ uint32 wd; if (fhd_bad_wa (wa)) /* addr bad? */ return TRUE; fhd_cw2 = fhd_cw2 + 1; /* incr char addr */ if (ca & 1) /* select char */ wd = fbuf[ba] & 0377; else wd = (fbuf[ba] >> 8) & 0377; fhd_csum = fhd_csword (fhd_csum, wd); /* put in csum */ *ch = wd; /* return */ return FALSE; } /* Write character to disk */ t_bool fhd_putc (UNIT *uptr, uint32 ch) { uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ uint32 tk = CW1_GETTK (fhd_cw1); /* track */ uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */ uint32 wa = ca >> 1; /* word addr */ uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */ uint16 *fbuf = uptr->filebuf; /* buffer base */ ch = ch & 0377; /* mask char */ if (fhd_bad_wa (wa)) /* addr bad? */ return TRUE; fhd_cw2 = fhd_cw2 + 1; /* incr char addr */ if (ca & 1) /* odd? low char */ fbuf[ba] = (fbuf[ba] & ~0377) | ch; else fbuf[ba] = (fbuf[ba] & 0377) | (ch << 8); /* even, hi char */ fhd_csum = fhd_csword (fhd_csum, ch); /* put in csum */ if (ba >= uptr->hwmark) /* update hwmark */ uptr->hwmark = ba + 1; return FALSE; } /* Check word address */ t_bool fhd_bad_wa (uint32 wa) { if (wa >= FH_NUMWD) { /* bad address? */ fhd_ace = 1; /* access error */ fhd_busy = 0; /* abort operation */ SET_INT (INT_FHD); return TRUE; } return FALSE; } /* Add character to checksum (parity) */ uint32 fhd_csword (uint32 cs, uint32 ch) { while (ch) { /* count bits */ ch = ch & ~(ch & (-(int32) ch)); cs = cs ^ 0200; /* invert cs for each 1 */ } return cs; } /* Reset routine */ t_stat fhd_reset (DEVICE *dptr) { fhd_busy = 0; /* reset state */ fhd_rdy = 0; fhd_ace = 0; fhd_dte = 0; fhd_eor = 0; fhd_otas = OTA_NOP; fhd_cw1 = fhd_cw2 = fhd_buf = 0; CLR_INT (INT_FHD); /* clear int, enb */ CLR_ENB (INT_FHD); sim_cancel (&fhd_unit); /* cancel operation */ return SCPE_OK; } /* Attach routine */ t_stat fhd_attach (UNIT *uptr, char *cptr) { uint32 sz, sf; uint32 ds_bytes = FH_WDPSF * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { sf = (sz + ds_bytes - 1) / ds_bytes; if (sf >= FH_NUMSF) sf = FH_NUMSF - 1; uptr->flags = (uptr->flags & ~UNIT_SF) | (sf << UNIT_V_SF); } uptr->capac = UNIT_GETSF (uptr->flags) * FH_WDPSF; return attach_unit (uptr, cptr); } /* Set size routine */ t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETSF (val) * FH_WDPSF; return SCPE_OK; } simh-3.8.1/H316/h316_defs.h0000644000175000017500000002333711111655550013125 0ustar vlmvlm/* h316_defs.h: Honeywell 316/516 simulator definitions Copyright (c) 1999-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 15-Feb-05 RMS Added start button interrupt 01-Dec-04 RMS Added double precision constants 24-Oct-03 RMS Added DMA/DMC support 25-Apr-03 RMS Revised for extended file support */ #ifndef _H316_DEFS_H_ #define _H316_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_IODV 2 /* must be 2 */ #define STOP_HALT 3 /* HALT */ #define STOP_IBKPT 4 /* breakpoint */ #define STOP_IND 5 /* indirect loop */ #define STOP_DMAER 6 /* DMA error */ #define STOP_MTWRP 7 /* MT write protected */ #define STOP_DPOVR 8 /* DP write overrun */ #define STOP_DPFMT 9 /* DP invalid format */ /* Memory */ #define MAXMEMSIZE 32768 /* max memory size */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define X_AMASK (MAXMEMSIZE - 1) /* ext address mask */ #define NX_AMASK ((MAXMEMSIZE / 2) - 1) /* nx address mask */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* Architectural constants */ #define SIGN 0100000 /* sign */ #define DP_SIGN 010000000000 #define DMASK 0177777 /* data mask */ #define MMASK (DMASK & ~SIGN) /* magnitude mask */ #define XR M[0] #define M_CLK 061 /* clock location */ #define M_RSTINT 062 /* restrict int */ #define M_INT 063 /* int location */ /* CPU options */ #define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ #define UNIT_V_EXT (UNIT_V_UF + 1) /* extended mem */ #define UNIT_V_HSA (UNIT_V_UF + 2) /* high speed arith */ #define UNIT_V_DMC (UNIT_V_UF + 3) /* DMC */ #define UNIT_MSIZE (1u << UNIT_V_MSIZE) #define UNIT_EXT (1u << UNIT_V_EXT) #define UNIT_HSA (1u << UNIT_V_HSA) #define UNIT_DMC (1u << UNIT_V_DMC) /* Instruction format */ #define I_M_OP 077 /* opcode */ #define I_V_OP 10 #define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) #define I_M_FNC 017 /* function */ #define I_V_FNC 6 #define I_GETFNC(x) (((x) >> I_V_FNC) & I_M_FNC) #define IA 0100000 /* indirect address */ #define IDX 0040000 /* indexed */ #define SC 0001000 /* sector */ #define DISP 0000777 /* page displacement */ #define PAGENO 0077000 /* page number */ #define INCLRA (010 << I_V_FNC) /* INA clear A */ #define DEVMASK 0000077 /* device mask */ #define SHFMASK 0000077 /* shift mask */ /* I/O opcodes */ #define ioOCP 0 /* output control */ #define ioSKS 1 /* skip if set */ #define ioINA 2 /* input to A */ #define ioOTA 3 /* output from A */ #define ioEND 4 /* channel end */ /* Device information block */ struct h316_dib { uint32 dev; /* device number */ uint32 chan; /* dma/dmc channel */ uint32 num; /* number of slots */ int32 (*io) (int32 inst, int32 fnc, int32 dat, int32 dev); }; typedef struct h316_dib DIB; /* DMA/DMC channel numbers */ #define IOBUS 0 /* IO bus */ #define DMA_MIN 1 /* 4 DMA channels */ #define DMA_MAX 4 #define DMC_MIN 1 /* 16 DMC channels */ #define DMC_MAX 16 #define DMA1 (DMA_MIN) #define DMC1 (DMA_MAX+DMC_MIN) /* DMA/DMC bit assignments in channel request word */ #define DMA_V_DMA1 0 /* DMA channels */ #define DMC_V_DMC1 4 /* DMC channels */ #define SET_CH_REQ(x) chan_req = chan_req | (1 << (x)) #define Q_DMA(x) (((x) >= 0) && ((x) < DMC_V_DMC1)) /* DMA/DMC definitions */ #define DMA_IN 0100000 /* input flag */ #define DMC_BASE 020 /* DMC memory base */ /* I/O device codes */ #define PTR 001 /* paper tape reader */ #define PTP 002 /* paper tape punch */ #define LPT 003 /* line printer */ #define TTY 004 /* console */ #define CDR 005 /* card reader */ #define MT 010 /* mag tape data */ #define CLK_KEYS 020 /* clock/keys (CPU) */ #define FHD 022 /* fixed head disk */ #define DMA 024 /* DMA control */ #define DP 025 /* moving head disk */ #define DEV_MAX 64 /* Interrupt flags, definitions correspond to SMK bits */ #define INT_V_CLK 0 /* clock */ #define INT_V_MPE 1 /* parity error */ #define INT_V_LPT 2 /* line printer */ #define INT_V_CDR 4 /* card reader */ #define INT_V_TTY 5 /* teletype */ #define INT_V_PTP 6 /* paper tape punch */ #define INT_V_PTR 7 /* paper tape reader */ #define INT_V_FHD 8 /* fixed head disk */ #define INT_V_DP 12 /* moving head disk */ #define INT_V_MT 15 /* mag tape */ #define INT_V_START 16 /* start button */ #define INT_V_NODEF 17 /* int not deferred */ #define INT_V_ON 18 /* int on */ /* I/O macros */ #define IOT_V_REASON 17 #define IOT_V_SKIP 16 #define IOT_SKIP (1u << IOT_V_SKIP) #define IORETURN(f,v) (((f)? (v): SCPE_OK) << IOT_V_REASON) #define IOBADFNC(x) (((stop_inst) << IOT_V_REASON) | (x)) #define IOSKIP(x) (IOT_SKIP | (x)) #define INT_CLK (1u << INT_V_CLK) #define INT_MPE (1u << INT_V_MPE) #define INT_LPT (1u << INT_V_LPT) #define INT_CDR (1u << INT_V_CDR) #define INT_TTY (1u << INT_V_TTY) #define INT_PTP (1u << INT_V_PTP) #define INT_PTR (1u << INT_V_PTR) #define INT_FHD (1u << INT_V_FHD) #define INT_DP (1u << INT_V_DP) #define INT_MT (1u << INT_V_MT) #define INT_START (1u << INT_V_START) #define INT_NODEF (1u << INT_V_NODEF) #define INT_ON (1u << INT_V_ON) #define INT_NMI (INT_START) #define INT_PEND (INT_ON | INT_NODEF) #define SET_INT(x) dev_int = dev_int | (x) #define CLR_INT(x) dev_int = dev_int & ~(x) #define TST_INT(x) ((dev_int & (x)) != 0) #define CLR_ENB(x) dev_enb = dev_enb & ~(x) #define TST_INTREQ(x) ((dev_int & dev_enb & (x)) != 0) /* Prototypes */ t_stat io_set_iobus (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat io_set_dma (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat io_set_dmc (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat io_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/H316/h316_mt.c0000644000175000017500000006136111107406106012612 0ustar vlmvlm/* h316_mt.c: H316/516 magnetic tape simulator Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt 516-4100 seven track magnetic tape 09-Jun-07 RMS Fixed bug in write without stop (from Theo Engel) 16-Feb-06 RMS Added tape capacity checking 26-Aug-05 RMS Revised to use API for write lock check 08-Feb-05 RMS Fixed error reporting from OCP (found by Philipp Hachtmann) 01-Dec-04 RMS Fixed bug in DMA/DMC support Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. */ #include "h316_defs.h" #include "sim_tape.h" #define MT_NUMDR 4 /* number of drives */ #define DB_N_SIZE 16 /* max data buf */ #define DBSIZE (1 << DB_N_SIZE) /* max data cmd */ #define FNC u3 /* function */ #define UST u4 /* unit status */ /* Function codes */ #define FNC_RBCD2 000 #define FNC_RBIN2 001 #define FNC_RBIN3 002 #define FNC_DMANM 003 #define FNC_WBCD2 004 #define FNC_WBIN2 005 #define FNC_WEOF 006 #define FNC_IOBUS 007 #define FNC_WBIN3 010 #define FNC_FSR 011 #define FNC_FSF 012 #define FNC_DMAAU 013 #define FNC_REW 014 #define FNC_BSR 015 #define FNC_BSF 016 #define FNC_STOPW 017 #define FNC_2ND 020 /* second state */ #define FNC_NOP (FNC_STOPW|FNC_2ND) #define FNC_EOM 040 /* end of motion */ /* Status - unit.UST */ #define STA_BOT 0000002 /* beg of tape */ #define STA_EOT 0000001 /* end of tape */ extern int32 dev_int, dev_enb, chan_req; extern int32 stop_inst; uint32 mt_buf = 0; /* data buffer */ uint32 mt_usel = 0; /* unit select */ uint32 mt_busy = 0; /* ctlr busy */ uint32 mt_mdirq = 0; /* motion done int req */ uint32 mt_rdy = 0; /* transfer ready (int) */ uint32 mt_err = 0; /* error */ uint32 mt_eof = 0; /* end of file */ uint32 mt_eor = 0; /* transfer done */ uint32 mt_dma = 0; /* DMA/DMC */ uint32 mt_xtime = 16; /* transfer time */ uint32 mt_ctime = 3000; /* start/stop time */ uint32 mt_stopioe = 1; /* stop on I/O error */ uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */ t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */ int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev); void mt_updint (uint32 rdy, uint32 mdone); t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_detach (UNIT *uptr); t_stat mt_map_err (UNIT *uptr, t_stat st); void mt_wrwd (UNIT *uptr, uint32 dat); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ DIB mt_dib = { MT, IOBUS, MT_NUMDR, &mtio }; UNIT mt_unit[] = { { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } }; REG mt_reg[] = { { ORDATA (BUF, mt_buf, 16) }, { ORDATA (USEL, mt_usel, 2) }, { FLDATA (BUSY, mt_busy, 0) }, { FLDATA (RDY, mt_rdy, 0) }, { FLDATA (ERR, mt_err, 0) }, { FLDATA (EOF, mt_eof, 0) }, { FLDATA (EOR, mt_eor, 0) }, { FLDATA (MDIRQ, mt_mdirq, 0) }, { FLDATA (DMA, mt_dma, 0) }, { FLDATA (INTREQ, dev_int, INT_V_MT) }, { FLDATA (ENABLE, dev_enb, INT_V_MT) }, { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) }, { DRDATA (BPTR, mt_ptr, DB_N_SIZE + 1) }, { DRDATA (BMAX, mt_max, DB_N_SIZE + 1) }, { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, mt_xtime, 24), REG_NZ + PV_LEFT }, { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT) }, { URDATA (FNC, mt_unit[0].FNC, 8, 8, 0, MT_NUMDR, REG_HRO) }, { URDATA (UST, mt_unit[0].UST, 8, 2, 0, MT_NUMDR, REG_HRO) }, { ORDATA (CHAN, mt_dib.chan, 5), REG_HRO }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS", &io_set_iobus, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", &io_set_dmc, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", &io_set_dma, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &io_show_chan, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach, &mt_dib, DEV_DISABLE }; /* IO routine */ int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev) { uint32 i, u = dev & 03; UNIT *uptr = mt_dev.units + u; static uint8 wrt_fnc[16] = { /* >0 = wr, 1 = chan op */ 0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0 }; switch (inst) { /* case on opcode */ case ioOCP: mt_updint (mt_rdy, 0); /* clear motion intr */ mt_eof = 0; /* clear eof */ switch (fnc) { /* case on function */ case FNC_DMANM: /* set DMA/DMC */ case FNC_DMAAU: mt_usel = u; /* save unit select */ if (mt_dib.chan) /* set DMA if configured */ mt_dma = 1; else mt_dma = 0; break; case FNC_IOBUS: /* set IOBUS */ mt_usel = u; /* save unit select */ mt_dma = 0; break; case FNC_STOPW: /* stop write */ mt_usel = u; /* save unit select */ mt_updint (0, mt_mdirq); /* clear ready */ if (wrt_fnc[uptr->FNC & 017] == 1) /* writing? */ mt_eor = 1; /* set transfer done */ break; default: /* motion command */ if (mt_busy) return dat; /* nop if ctlr busy */ mt_eor = 0; /* clr transfer done */ mt_err = 0; /* clr error */ mt_usel = u; /* save unit select */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return (((mt_stopioe? SCPE_UNATT: SCPE_OK) << IOT_V_REASON) | dat); if (sim_is_active (uptr)) /* nop if busy */ return dat; if (wrt_fnc[fnc] && sim_tape_wrp (uptr)) return ((STOP_MTWRP << IOT_V_REASON) | dat); uptr->FNC = fnc; uptr->UST = 0; mt_busy = 1; for (i = 0; i < MT_NUMDR; i++) /* clear all EOT flags */ mt_unit[i].UST = mt_unit[i].UST & ~STA_EOT; sim_activate (uptr, mt_ctime); /* schedule */ break; } break; case ioINA: /* INA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (mt_rdy) { /* ready? */ mt_rdy = 0; /* clear ready */ return IOSKIP (dat | mt_buf); /* ret buf, skip */ } break; case ioOTA: /* OTA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (mt_rdy) { /* ready? */ mt_rdy = 0; /* clear ready */ mt_buf = dat; /* store buf */ return IOSKIP (dat); /* skip */ } break; case ioSKS: uptr = mt_dev.units + mt_usel; /* use saved unit sel */ switch (fnc) { case 000: /* ready */ if (mt_rdy) return IOSKIP (dat); break; case 001: /* !busy */ if (!mt_busy) return IOSKIP (dat); break; case 002: /* !error */ if (!mt_err) return IOSKIP (dat); break; case 003: /* !BOT */ if (!(uptr->UST & STA_BOT)) return IOSKIP (dat); break; case 004: /* !interrupting */ if (!TST_INTREQ (INT_MT)) return IOSKIP (dat); break; case 005: /* !EOT */ if (!(uptr->UST & STA_EOT)) return IOSKIP (dat); break; case 006: /* !EOF */ if (!mt_eof) return IOSKIP (dat); break; case 007: /* !write prot */ if (!sim_tape_wrp (uptr)) return IOSKIP (dat); break; case 011: /* operational */ if ((uptr->flags & UNIT_ATT) && ((uptr->FNC & 017) != FNC_REW)) return IOSKIP (dat); break; case 012: /* skip if !chan 2 */ return IOSKIP (dat); case 013: /* skip if !auto */ return IOSKIP (dat); case 014: /* !rewinding */ uptr = mt_dev.units + (dev & 03); /* use specified unit */ if ((uptr->FNC & 017) != FNC_REW) return IOSKIP (dat); break; } break; case ioEND: /* end of range */ mt_eor = 1; /* transfer done */ break; } return dat; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt Can't be write locked, can only write lock detached unit */ t_stat mt_svc (UNIT *uptr) { int32 ch = mt_dib.chan - 1; /* DMA/DMC ch */ uint32 i, c1, c2, c3; t_mtrlnt tbc; t_bool passed_eot; t_stat st, r = SCPE_OK; if ((uptr->flags & UNIT_ATT) == 0) { /* offline? */ mt_err = 1; mt_busy = 0; mt_updint (0, 1); /* cmd done */ return IORETURN (mt_stopioe, SCPE_UNATT); } passed_eot = sim_tape_eot (uptr); /* passed EOT? */ switch (uptr->FNC) { /* case on function */ case FNC_REW: /* rewind (initial) */ mt_busy = 0; /* ctlr not busy */ uptr->FNC = uptr->FNC | FNC_2ND; sim_activate (uptr, mt_ctime); return SCPE_OK; /* continue */ case FNC_REW | FNC_2ND: /* rewind done */ uptr->pos = 0; /* reposition file */ uptr->UST = STA_BOT; /* set BOT */ uptr->FNC = FNC_NOP; /* nop function */ for (i = 0; i < MT_NUMDR; i++) { /* last rewind? */ if ((mt_unit[i].FNC & 017) == FNC_REW) return SCPE_OK; } mt_updint (mt_rdy, 1); /* yes, motion done */ return SCPE_OK; case FNC_WEOF: /* write file mark */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ case FNC_FSR: /* space fwd rec */ if (st = sim_tape_sprecf (uptr, &tbc)) /* space fwd, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ case FNC_BSR: /* space rev rec */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ case FNC_FSF: /* space fwd file */ while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ case FNC_BSF: /* space rev file */ while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ case FNC_EOM: /* end of motion */ uptr->FNC = FNC_NOP; /* nop function */ mt_busy = 0; /* not busy */ mt_updint (mt_rdy, 1); /* end of motion */ return SCPE_OK; /* done! */ case FNC_RBCD2: case FNC_RBIN2: case FNC_RBIN3: /* read first */ mt_ptr = 0; /* clr buf ptr */ st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */ if (st != MTSE_OK) { /* error? */ r = mt_map_err (uptr, st); /* map error */ break; /* sched end motion */ } uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ sim_activate (uptr, mt_xtime); /* sched xfer */ return SCPE_OK; case FNC_RBCD2 | FNC_2ND: /* read, word */ case FNC_RBIN2 | FNC_2ND: case FNC_RBIN3 | FNC_2ND: if (mt_ptr >= mt_max) /* record done? */ break; c1 = mtxb[mt_ptr++] & 077; /* get 2 chars */ c2 = mtxb[mt_ptr++] & 077; if (uptr->FNC == (FNC_RBCD2 | FNC_2ND)) { /* BCD? */ if (c1 == 012) c1 = 0; /* change 12 to 0 */ if (c2 == 012) c2 = 0; } if (uptr->FNC == (FNC_RBIN3 | FNC_2ND)) { /* read 3? */ if (mt_ptr >= mt_max) break; /* lose wd if not enuf */ c3 = mtxb[mt_ptr++] & 017; /* get 3rd char */ } else c3 = 0; sim_activate (uptr, mt_xtime); /* no, sched word */ if (mt_eor) /* xfer done? */ return SCPE_OK; mt_buf = (c1 << 10) | (c2 << 4) | c3; /* pack chars */ if (mt_rdy) mt_err = 1; /* buf full? err */ mt_updint (1, mt_mdirq); /* set ready */ if (mt_dma) /* DMC/DMA? req chan */ SET_CH_REQ (ch); return SCPE_OK; /* continue */ case FNC_WBCD2: case FNC_WBIN2: case FNC_WBIN3: /* write first */ mt_ptr = 0; /* clear buf ptr */ mt_updint (1, mt_mdirq); /* set ready */ if (mt_dma) /* DMC/DMA? req chan */ SET_CH_REQ (ch); uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ sim_activate (uptr, mt_xtime); /* sched xfer */ return SCPE_OK; /* continue */ case FNC_WBCD2 | FNC_2ND: /* write, word */ case FNC_WBIN2 | FNC_2ND: case FNC_WBIN3 | FNC_2ND: if (mt_eor || mt_rdy) { /* done or no data? */ if (!mt_rdy) /* write last word */ mt_wrwd (uptr, mt_buf); else mt_rdy = 0; /* rdy must be clr */ if (mt_ptr) { /* any data? */ if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) /* write, err? */ r = mt_map_err (uptr, st); /* map error */ } break; /* sched end motion */ } mt_wrwd (uptr, mt_buf); /* write word */ sim_activate (uptr, mt_xtime); /* no, sched word */ mt_updint (1, mt_mdirq); /* set ready */ if (mt_dma) /* DMC/DMA? req chan */ SET_CH_REQ (ch); return SCPE_OK; /* continue */ default: /* unknown */ break; } /* End of command, process error or schedule end of motion */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ uptr->UST = uptr->UST | STA_EOT; if (r != SCPE_OK) { uptr->FNC = FNC_NOP; /* nop function */ mt_busy = 0; /* not busy */ mt_updint (mt_rdy, 1); /* end of motion */ return r; } uptr->FNC = FNC_EOM; /* sched end motion */ sim_activate (uptr, mt_ctime); return SCPE_OK; } /* Write word to buffer */ void mt_wrwd (UNIT *uptr, uint32 dat) { uint32 c1, c2; c1 = (dat >> 10) & 077; /* get 2 chars */ c2 = (dat >> 4) & 077; if (uptr->FNC == (FNC_WBCD2 | FNC_2ND)) { /* BCD? */ if (c1 == 0) c1 = 012; /* change 0 to 12 */ if (c2 == 0) c2 = 012; } if (mt_ptr < DBSIZE) /* store 2 char */ mtxb[mt_ptr++] = c1; if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c2; if ((uptr->FNC == (FNC_WBIN3 | FNC_2ND)) && /* write 3? */ (mt_ptr < DBSIZE)) mtxb[mt_ptr++] = mt_buf & 017; return; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* unattached */ mt_err = 1; /* reject */ case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_TMK: /* end of file */ mt_eof = 1; /* eof */ break; case MTSE_INVRL: /* invalid rec lnt */ mt_err = 1; return SCPE_MTRLNT; case MTSE_IOERR: /* IO error */ mt_err = 1; /* error */ if (mt_stopioe) return SCPE_IOERR; break; case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ mt_err = 1; /* error */ break; case MTSE_BOT: /* reverse into BOT */ uptr->UST = STA_BOT; /* set status */ break; case MTSE_WRP: /* write protect */ mt_err = 1; /* error */ return STOP_MTWRP; } return SCPE_OK; } /* Update interrupts */ void mt_updint (uint32 rdy, uint32 mdirq) { mt_rdy = rdy; /* store new ready */ mt_mdirq = mdirq; /* store new motion irq */ if ((mt_rdy && !mt_dma) || mt_mdirq) /* update int request */ SET_INT (INT_MT); else CLR_INT (INT_MT); return; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { int32 i; UNIT *uptr; mt_buf = 0; /* clear state */ mt_usel = 0; mt_mdirq = 0; mt_eor = 0; mt_busy = 0; mt_rdy = 0; mt_eof = 0; mt_err = 0; mt_dma = 0; CLR_INT (INT_MT); /* clear int, enb */ CLR_ENB (INT_MT); for (i = 0; i < MT_NUMDR; i++) { /* loop thru units */ uptr = mt_dev.units + i; sim_tape_reset (uptr); /* reset tape */ sim_cancel (uptr); /* cancel op */ uptr->UST = uptr->pos? 0: STA_BOT; /* update status */ uptr->FNC = FNC_NOP; } return SCPE_OK; } /* Attach routine */ t_stat mt_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* update status */ return r; uptr->UST = STA_BOT; return r; } /* Detach routine */ t_stat mt_detach (UNIT* uptr) { uptr->UST = 0; /* update status */ uptr->FNC = FNC_NOP; /* nop function */ return sim_tape_detach (uptr); /* detach unit */ } simh-3.8.1/0readme_38.txt0000644000175000017500000000233611112567040013232 0ustar vlmvlmNotes For V3.8 The makefile now works for Linux and most Unix's. Howevr, for Solaris and MacOS, you must first export the OSTYPE environment variable: > export OSTYPE > make Otherwise, you will get build errors. 1. New Features 1.1 3.8-0 1.1.1 SCP and Libraries - BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and show (respectively) a breakpoint at the current PC. 1.1.2 GRI - Added support for the GRI-99 processor. 1.1.3 HP2100 - Added support for the BACI terminal interface. - Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. 1.1.4 Nova - Added support for 64KW memory (implemented in third-party CPU's). 1.1.5 PDP-11 - Added support for DC11, RC11, KE11A, KG11A. - Added modem control support for DL11. - Added ASCII character support for all 8b devices. 1.2 3.8-1 1.2.1 SCP and libraries - Added capability to set line connection order for terminal multiplexers. 1.2.2 HP2100 - Added support for 12620A/12936A privileged interrupt fence. - Added support for 12792C eight-channel asynchronous multiplexer. 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h. simh-3.8.1/PDP10/0000755000175000017500000000000011113312510011411 5ustar vlmvlmsimh-3.8.1/PDP10/pdp10_tim.c0000644000175000017500000002505311112112574013366 0ustar vlmvlm/* pdp10_tim.c: PDP-10 tim subsystem simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tim timer subsystem 18-Jun-07 RMS Added UNIT_IDLE flag 03-Nov-06 RMS Rewritten to support idling 29-Oct-06 RMS Added clock coscheduling function 02-Feb-04 RMS Exported variables needed by Ethernet simulator 29-Jan-02 RMS New data structures 06-Jan-02 RMS Added enable/disable support 02-Dec-01 RMS Fixed bug in ITS PC sampling (found by Dave Conroy) 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 17-Jul-01 RMS Moved function prototype 04-Jul-01 RMS Added DZ11 support */ #include "pdp10_defs.h" #include /* Invariants */ #define TIM_HW_FREQ 4100000 /* 4.1Mhz */ #define TIM_HWRE_MASK 07777 #define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */ #define UNIT_Y2K (1u << UNIT_V_Y2K) /* Clock mode TOPS-10/ITS */ #define TIM_TPS_T10 60 #define TIM_WAIT_T10 8000 #define TIM_MULT_T10 1 #define TIM_ITS_QUANT (TIM_HW_FREQ / TIM_TPS_T10) /* Clock mode TOPS-20/KLAD */ #define TIM_TPS_T20 1001 #define TIM_WAIT_T20 500 #define TIM_MULT_T20 16 /* Probability function for TOPS-20 idlelock */ #define PROB(x) (((rand() * 100) / RAND_MAX) >= (x)) d10 tim_base[2] = { 0, 0 }; /* 71b timebase */ d10 tim_ttg = 0; /* time to go */ d10 tim_period = 0; /* period */ d10 quant = 0; /* ITS quantum */ int32 tim_mult = TIM_MULT_T10; /* tmxr poll mult */ int32 tim_t20_prob = 33; /* TOPS-20 prob */ /* Exported variables */ int32 clk_tps = TIM_TPS_T10; /* clock ticks/sec */ int32 tmr_poll = TIM_WAIT_T10; /* clock poll */ int32 tmxr_poll = TIM_WAIT_T10 * TIM_MULT_T10; /* term mux poll */ extern int32 apr_flg, pi_act; extern UNIT cpu_unit; extern d10 pcst; extern a10 pager_PC; extern int32 t20_idlelock; DEVICE tim_dev; t_stat tcu_rd (int32 *data, int32 PA, int32 access); t_stat tim_svc (UNIT *uptr); t_stat tim_reset (DEVICE *dptr); void tim_incr_base (d10 *base, d10 incr); extern d10 Read (a10 ea, int32 prv); extern d10 ReadM (a10 ea, int32 prv); extern void Write (a10 ea, d10 val, int32 prv); extern void WriteP (a10 ea, d10 val); extern int32 pi_eval (void); extern t_stat wr_nop (int32 data, int32 PA, int32 access); /* TIM data structures tim_dev TIM device descriptor tim_unit TIM unit descriptor tim_reg TIM register list */ DIB tcu_dib = { IOBA_TCU, IOLN_TCU, &tcu_rd, &wr_nop, 0 }; UNIT tim_unit = { UDATA (&tim_svc, UNIT_IDLE, 0), TIM_WAIT_T10 }; REG tim_reg[] = { { BRDATA (TIMEBASE, tim_base, 8, 36, 2) }, { ORDATA (TTG, tim_ttg, 36) }, { ORDATA (PERIOD, tim_period, 36) }, { ORDATA (QUANT, quant, 36) }, { DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (PROB, tim_t20_prob, 6), REG_NZ + PV_LEFT + REG_HIDDEN }, { DRDATA (POLL, tmr_poll, 32), REG_HRO + PV_LEFT }, { DRDATA (MUXPOLL, tmxr_poll, 32), REG_HRO + PV_LEFT }, { DRDATA (MULT, tim_mult, 6), REG_HRO + PV_LEFT }, { DRDATA (TPS, clk_tps, 12), REG_HRO + PV_LEFT }, { NULL } }; MTAB tim_mod[] = { { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL }, { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL }, { MTAB_XTD|MTAB_VDV, 000, "ADDRESS", NULL, NULL, &show_addr, NULL }, { 0 } }; DEVICE tim_dev = { "TIM", &tim_unit, tim_reg, tim_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &tim_reset, NULL, NULL, NULL, &tcu_dib, DEV_UBUS }; /* Timer instructions */ /* Timer - if the timer is running at less than hardware frequency, need to interpolate the value by calculating how much of the current clock tick has elapsed, and what that equates to in msec. */ t_bool rdtim (a10 ea, int32 prv) { d10 tempbase[2]; ReadM (INCA (ea), prv); /* check 2nd word */ tempbase[0] = tim_base[0]; /* copy time base */ tempbase[1] = tim_base[1]; if (tim_mult != TIM_MULT_T20) { /* interpolate? */ int32 used; d10 incr; used = tmr_poll - (sim_is_active (&tim_unit) - 1); incr = (d10) (((double) used * TIM_HW_FREQ) / ((double) tmr_poll * (double) clk_tps)); tim_incr_base (tempbase, incr); } tempbase[0] = tempbase[0] & ~((d10) TIM_HWRE_MASK); /* clear low 12b */ Write (ea, tempbase[0], prv); Write (INCA(ea), tempbase[1], prv); return FALSE; } t_bool wrtim (a10 ea, int32 prv) { tim_base[0] = Read (ea, prv); tim_base[1] = CLRS (Read (INCA (ea), prv)); return FALSE; } t_bool rdint (a10 ea, int32 prv) { Write (ea, tim_period, prv); return FALSE; } t_bool wrint (a10 ea, int32 prv) { tim_period = Read (ea, prv); tim_ttg = tim_period; return FALSE; } /* Timer service - the timer is only serviced when the 'ttg' register has reached 0 based on the expected frequency of clock interrupts. */ t_stat tim_svc (UNIT *uptr) { if (cpu_unit.flags & UNIT_KLAD) /* diags? */ tmr_poll = uptr->wait; /* fixed clock */ else tmr_poll = sim_rtc_calb (clk_tps); /* else calibrate */ sim_activate (uptr, tmr_poll); /* reactivate unit */ tmxr_poll = tmr_poll * tim_mult; /* set mux poll */ tim_incr_base (tim_base, tim_period); /* incr time base */ tim_ttg = tim_period; /* reload */ apr_flg = apr_flg | APRF_TIM; /* request interrupt */ if (Q_ITS) { /* ITS? */ if (pi_act == 0) quant = (quant + TIM_ITS_QUANT) & DMASK; if (TSTS (pcst)) { /* PC sampling? */ WriteP ((a10) pcst & AMASK, pager_PC); /* store sample */ pcst = AOB (pcst); /* add 1,,1 */ } } /* end ITS */ else if (t20_idlelock && PROB (100 - tim_t20_prob)) t20_idlelock = 0; return SCPE_OK; } /* Clock coscheduling routine */ int32 clk_cosched (int32 wait) { int32 t; if (tim_mult == TIM_MULT_T20) return wait; t = sim_is_active (&tim_unit); return (t? t - 1: wait); } void tim_incr_base (d10 *base, d10 incr) { base[1] = base[1] + incr; /* add on incr */ base[0] = base[0] + (base[1] >> 35); /* carry to high */ base[0] = base[0] & DMASK; /* mask high */ base[1] = base[1] & MMASK; /* mask low */ return; } /* Timer reset */ t_stat tim_reset (DEVICE *dptr) { tim_period = 0; /* clear timer */ tim_ttg = 0; apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */ tmr_poll = sim_rtc_init (tim_unit.wait); /* init timer */ sim_activate_abs (&tim_unit, tmr_poll); /* activate unit */ tmxr_poll = tmr_poll * tim_mult; /* set mux poll */ return SCPE_OK; } /* Set timer parameters from CPU model */ t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val & (UNIT_T20|UNIT_KLAD)) { clk_tps = TIM_TPS_T20; uptr->wait = TIM_WAIT_T20; tmr_poll = TIM_WAIT_T20; tim_mult = TIM_MULT_T20; uptr->flags = uptr->flags | UNIT_Y2K; } else { clk_tps = TIM_TPS_T10; uptr->wait = TIM_WAIT_T10; tmr_poll = TIM_WAIT_T10; tim_mult = TIM_MULT_T10; if (Q_ITS) uptr->flags = uptr->flags | UNIT_Y2K; else uptr->flags = uptr->flags & ~UNIT_Y2K; } tmxr_poll = tmr_poll * tim_mult; return SCPE_OK; } /* Time of year clock */ t_stat tcu_rd (int32 *data, int32 PA, int32 access) { time_t curtim; struct tm *tptr; curtim = time (NULL); /* get time */ tptr = localtime (&curtim); /* decompose */ if (tptr == NULL) return SCPE_NXM; if ((tptr->tm_year > 99) && !(tim_unit.flags & UNIT_Y2K)) tptr->tm_year = 99; /* Y2K prob? */ switch ((PA >> 1) & 03) { /* decode PA<3:1> */ case 0: /* year/month/day */ *data = (((tptr->tm_year) & 0177) << 9) | (((tptr->tm_mon + 1) & 017) << 5) | ((tptr->tm_mday) & 037); return SCPE_OK; case 1: /* hour/minute */ *data = (((tptr->tm_hour) & 037) << 8) | ((tptr->tm_min) & 077); return SCPE_OK; case 2: /* second */ *data = (tptr->tm_sec) & 077; return SCPE_OK; case 3: /* status */ *data = CSR_DONE; return SCPE_OK; } return SCPE_NXM; /* can't get here */ } simh-3.8.1/PDP10/pdp10_xtnd.c0000644000175000017500000007432711112112574013562 0ustar vlmvlm/* pdp10_xtnd.c: PDP-10 extended instruction simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 12-May-01 RMS Fixed compiler warning in xlate Instructions handled in this module: MOVSLJ move string left justified MOVSO move string offset MOVST move string translated MOVSRJ move string right justified CMPSL compare string, skip on less CMPSE compare string, skip on equal CMPSLE compare string, skip on less or equal CMPSGE compare string, skip on greater or equal CMPSN compare string, skip on unequal CMPSG compare string, skip on greater CVTDBO convert decimal to binary offset CVTDBT convert decimal to binary translated CVTBDO convert binary to decimal offset CVTBDT convert binary to decimal translated EDIT edit The PDP-10 extended instructions deal with non-binary data types, particularly byte strings and decimal strings. (In the KL10, the extended instructions include G floating support as well.) They are very complicated microcoded subroutines that can potentially run for a very long time. Accordingly, the instructions must test for interrupts as well as page faults, and be prepared to restart from either. In general, the simulator attempts to keep the AC block up to date, so that page fails and interrupts can be taken directly at any point. If the AC block is not up to date, memory accessibility must be tested before the actual read or write is done. The extended instruction routine returns a status code as follows: XT_NOSK no skip completion XT_SKIP skip completion XT_MUUO invalid extended instruction */ #include "pdp10_defs.h" #include #define MM_XSRC (pflgs & XSRC_PXCT) #define MM_XDST (pflgs & XDST_PXCT) #define MM_EA_XSRC ((pflgs & EA_PXCT) && MM_XSRC) #define MM_EA_XDST ((pflgs & EA_PXCT) && MM_XDST) #define XT_CMPSL 001 /* opcodes */ #define XT_CMPSE 002 #define XT_CMPSLE 003 #define XT_EDIT 004 #define XT_CMPSGE 005 #define XT_CMPSN 006 #define XT_CMPSG 007 #define XT_CVTDBO 010 #define XT_CVTDBT 011 #define XT_CVTBDO 012 #define XT_CVTBDT 013 #define XT_MOVSO 014 #define XT_MOVST 015 #define XT_MOVSLJ 016 #define XT_MOVSRJ 017 /* Translation control */ #define XT_LFLG 0400000000000 /* L flag */ #define XT_SFLG 0400000000000 /* S flag */ #define XT_NFLG 0200000000000 /* N flag */ #define XT_MFLG 0100000000000 /* M flag */ /* Translation table */ #define XT_V_CODE 15 /* translation op */ #define XT_M_CODE 07 #define XT_BYMASK 07777 /* byte mask */ #define XT_DGMASK 017 /* digit mask */ #define XT_GETCODE(x) ((int32) (((x) >> XT_V_CODE) & XT_M_CODE)) /* AC masks */ #define XLNTMASK 0000777777777 /* length */ #define XFLGMASK 0700000000000 /* flags */ #define XT_MBZ 0777000000000 /* must be zero */ #define XT_MBZE 0047777000000 /* must be zero, edit */ /* Register change log */ #define XT_N_RLOG 5 /* entry width */ #define XT_M_RLOG ((1 << XT_N_RLOG) - 1) /* entry mask */ #define XT_O_RLOG 1 /* entry offset */ #define XT_INSRLOG(x,v) v = ((v << XT_N_RLOG) | (((x) + XT_O_RLOG) & XT_M_RLOG)) #define XT_REMRLOG(x,v) x = (v & XT_M_RLOG) - XT_O_RLOG; \ v = v >> XT_N_RLOG /* Edit */ #define ED_V_PBYN 30 /* pattern byte # */ #define ED_M_PBYN 03 #define ED_PBYNO 0040000000000 /* overflow bit */ #define ED_GETPBYN(x) ((int32) (((x) >> ED_V_PBYN) & ED_M_PBYN)) #define ED_V_POPC 6 /* pattern byte opcode */ #define ED_M_PAT 0777 /* pattern byte mask */ #define ED_M_NUM 0077 /* number for msg, etc */ #define ED_PBYTE(x,y) ((int32) (((x) >> (27 - (ED_GETPBYN (y) * 9))) & ED_M_PAT)) #define ED_STOP 0000 /* stop */ #define ED_SELECT 0001 /* select source */ #define ED_SIGST 0002 /* start significance */ #define ED_FLDSEP 0003 /* field separator */ #define ED_EXCHMD 0004 /* exchange mark, dst */ #define ED_MESSAG 0100 /* message */ #define ED_SKPM 0500 /* skip if M */ #define ED_SKPN 0600 /* skip if N */ #define ED_SKPA 0700 /* skip always */ extern d10 *ac_cur; /* current AC block */ extern const d10 bytemask[64]; extern int32 flags; extern int32 rlog; extern jmp_buf save_env; extern d10 Read (int32 ea, int32 prv); extern void Write (int32 ea, d10 val, int32 prv); extern a10 calc_ea (d10 inst, int32 prv); extern int32 test_int (void); d10 incbp (d10 bp); d10 incloadbp (int32 ac, int32 pflgs); void incstorebp (d10 val, int32 ac, int32 pflgs); d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 pflgs); void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs); static const d10 pwrs10[23][2] = { 0, 0, 0, 1, 0, 10, 0, 100, 0, 1000, 0, 10000, 0, 100000, 0, 1000000, 0, 10000000, 0, 100000000, 0, 1000000000, 0, 10000000000, 2, 31280523264, 29, 3567587328, 291, 1316134912, 2910, 13161349120, 29103, 28534276096, 291038, 10464854016, 2910383, 1569325056, 29103830, 15693250560, 291038304, 19493552128, 2910383045, 23136829440, 29103830456, 25209864192 }; int xtend (int32 ac, int32 ea, int32 pflgs) { d10 b1, b2, ppi; d10 xinst, xoff, digit, f1, f2, rs[2]; d10 xflgs = 0; a10 e1, entad; int32 p1 = ADDAC (ac, 1); int32 p3 = ADDAC (ac, 3); int32 p4 = ADDAC (ac, 4); int32 flg, i, s2, t, pp, pat, xop, xac, ret; xinst = Read (ea, MM_OPND); /* get extended instr */ xop = GET_OP (xinst); /* get opcode */ xac = GET_AC (xinst); /* get AC */ if (xac || (xop == 0) || (xop > XT_MOVSRJ)) return XT_MUUO; rlog = 0; /* clear log */ switch (xop) { /* case on opcode */ /* String compares - checked against KS10 ucode If both strings are zero length, they are considered equal. Both source and destination lengths are MBZ checked. AC = source1 length AC + 1 = source1 byte pointer AC + 3 = source2 length AC + 4 = source2 byte pointer */ case XT_CMPSL: /* CMPSL */ case XT_CMPSE: /* CMPSE */ case XT_CMPSLE: /* CMPSLE */ case XT_CMPSGE: /* CMPSGE */ case XT_CMPSN: /* CMPSN */ case XT_CMPSG: /* CMPSG */ if ((AC(ac) | AC(p3)) & XT_MBZ) /* check length MBZ */ return XT_MUUO; f1 = Read (ADDA (ea, 1), MM_OPND) & bytemask[GET_S (AC(p1))]; f2 = Read (ADDA (ea, 2), MM_OPND) & bytemask[GET_S (AC(p4))]; b1 = b2 = 0; for (flg = 0; (AC(ac) | AC(p3)) && (b1 == b2); flg++) { if (flg && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ if (AC(ac)) /* src1 */ b1 = incloadbp (p1, pflgs); else b1 = f1; if (AC(p3)) /* src2 */ b2 = incloadbp (p4, pflgs); else b2 = f2; if (AC(ac)) AC(ac) = (AC(ac) - 1) & XLNTMASK; if (AC(p3)) AC(p3) = (AC(p3) - 1) & XLNTMASK; } switch (xop) { case XT_CMPSL: return (b1 < b2)? XT_SKIP: XT_NOSK; case XT_CMPSE: return (b1 == b2)? XT_SKIP: XT_NOSK; case XT_CMPSLE: return (b1 <= b2)? XT_SKIP: XT_NOSK; case XT_CMPSGE: return (b1 >= b2)? XT_SKIP: XT_NOSK; case XT_CMPSN: return (b1 != b2)? XT_SKIP: XT_NOSK; case XT_CMPSG: return (b1 > b2)? XT_SKIP: XT_NOSK; } return XT_MUUO; /* Convert binary to decimal instructions - checked against KS10 ucode There are no MBZ tests. AC'AC + 1 = double precision integer source AC + 3 = flags and destination length AC + 4 = destination byte pointer */ case XT_CVTBDO: /* CVTBDO */ case XT_CVTBDT: /* CVTBDT */ e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ if (xop == XT_CVTBDO) /* offset? */ xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ rs[0] = AC(ac); /* get src opnd */ rs[1] = CLRS (AC(p1)); if (!TSTF (F_FPD)) { /* set up done yet? */ if (TSTS (AC(ac))) { /* get abs value */ DMOVN (rs); } for (i = 22; i > 1; i--) { /* find field width */ if (DCMPGE (rs, pwrs10[i])) break; } if (i > (AC(p3) & XLNTMASK)) return XT_NOSK; if ((i < (AC(p3) & XLNTMASK)) && (AC(p3) & XT_LFLG)) { f1 = Read (ADDA (ea, 1), MM_OPND); filldst (f1, p3, (AC(p3) & XLNTMASK) - i, pflgs); } else AC(p3) = (AC(p3) & XFLGMASK) | i; if (TSTS (AC(ac))) AC(p3) = AC(p3) | XT_MFLG; if (AC(ac) | AC(p1)) AC(p3) = AC(p3) | XT_NFLG; AC(ac) = rs[0]; /* update state */ AC(p1) = rs[1]; SETF (F_FPD); /* mark set up done */ } /* Now do actual binary to decimal conversion */ for (flg = 0; AC(p3) & XLNTMASK; flg++) { if (flg && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ i = (int32) AC(p3) & XLNTMASK; /* get length */ if (i > 22) /* put in range */ i = 22; for (digit = 0; (digit < 10) && DCMPGE (rs, pwrs10[i]); digit++) { rs[0] = rs[0] - pwrs10[i][0] - (rs[1] < pwrs10[i][1]); rs[1] = (rs[1] - pwrs10[i][1]) & MMASK; } if (xop == XT_CVTBDO) digit = (digit + xoff) & DMASK; else { f1 = Read (e1 + (int32) digit, MM_OPND); if ((i == 1) && (AC(p3) & XT_LFLG)) f1 = f1 >> 18; digit = f1 & RMASK; } incstorebp (digit, p4, pflgs); /* store digit */ AC(ac) = rs[0]; /* mem access ok */ AC(p1) = rs[1]; /* update state */ AC(p3) = (AC(p3) & XFLGMASK) | ((AC(p3) - 1) & XLNTMASK); } CLRF (F_FPD); /* clear FPD */ return XT_SKIP; /* Convert decimal to binary instructions - checked against KS10 ucode There are no MBZ tests. AC = flags and source length AC + 1 = source byte pointer AC + 3'AC + 4 = double precision integer result */ case XT_CVTDBT: /* CVTDBT */ case XT_CVTDBO: /* CVTDBO */ e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ if ((AC(ac) & XT_SFLG) == 0) /* !S? clr res */ AC(p3) = AC(p4) = 0; else AC(p4) = CLRS (AC(p4)); /* clear low sign */ if (xop == XT_CVTDBO) { /* offset? */ xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ AC(ac) = AC(ac) | XT_SFLG; /* set S flag */ } xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ for (flg = 0; AC(ac) & XLNTMASK; flg++) { if (flg && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ b1 = incloadbp (p1, pflgs); /* get byte */ if (xop == XT_CVTDBO) b1 = (b1 + xoff) & DMASK; else { b1 = xlate (b1, e1, &xflgs, MM_OPND); if (b1 < 0) { /* terminated? */ AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); return XT_NOSK; } if (xflgs & XT_SFLG) b1 = b1 & XT_DGMASK; else b1 = 0; } AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); if ((b1 < 0) || (b1 > 9)) { /* bad digit? done */ if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); return XT_NOSK; } AC(p4) = (AC(p4) * 10) + b1; /* base * 10 + digit */ AC(p3) = ((AC(p3) * 10) + (AC(p4) >> 35)) & DMASK; AC(p4) = AC(p4) & MMASK; } if (AC(ac) & XT_MFLG) { AC(p4) = -AC(p4) & MMASK; AC(p3) = (~AC(p3) + (AC(p4) == 0)) & DMASK; } if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); return XT_SKIP; /* String move instructions - checked against KS10 ucode Only the destination length is MBZ checked. AC = flags (MOVST only) and source length AC + 1 = source byte pointer AC + 3 = destination length AC + 4 = destination byte pointer */ case XT_MOVSO: /* MOVSO */ case XT_MOVST: /* MOVST */ case XT_MOVSRJ: /* MOVSRJ */ case XT_MOVSLJ: /* MOVSLJ */ if (AC(p3) & XT_MBZ) /* test dst lnt MBZ */ return XT_MUUO; f1 = Read (ADDA (ea, 1), MM_OPND); /* get fill */ switch (xop) { /* case on instr */ case XT_MOVSO: /* MOVSO */ AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ xoff = calc_ea (xinst, MM_EA); /* get offset */ if (xoff & RSIGN) /* sign extend 18b */ xoff = xoff | LMASK; s2 = GET_S (AC(p4)); /* get dst byte size */ break; case XT_MOVST: /* MOVST */ e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ break; case XT_MOVSRJ: /* MOVSRJ */ AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); if (AC(ac) > AC(p3)) { /* adv src ptr */ for (flg = 0; AC(ac) > AC(p3); flg++) { if (flg && (t = test_int ())) ABORT (t); AC(p1) = incbp (AC(p1)); AC(ac) = (AC(ac) - 1) & XLNTMASK; } } else if (AC(ac) < AC(p3)) filldst (f1, p3, AC(p3) - AC(ac), pflgs); break; case XT_MOVSLJ: /* MOVSLJ */ AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ break; } /* end case xop */ xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); for (flg = 0; AC(p3) & XLNTMASK; flg++) { if (flg && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ if (AC(ac) & XLNTMASK) { /* any source? */ b1 = incloadbp (p1, pflgs); /* src byte */ if (xop == XT_MOVSO) { /* offset? */ b1 = (b1 + xoff) & DMASK; /* test fit */ if (b1 & ~bytemask[s2]) { AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); return XT_NOSK; } } else if (xop == XT_MOVST) { /* translate? */ b1 = xlate (b1, e1, &xflgs, MM_OPND); if (b1 < 0) { /* upd flags in AC */ AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); return XT_NOSK; } if (xflgs & XT_SFLG) b1 = b1 & XT_BYMASK; else b1 = -1; } } else b1 = f1; if (b1 >= 0) { /* valid byte? */ incstorebp (b1, p4, pflgs); /* store byte */ AC(p3) = (AC(p3) - 1) & XLNTMASK; /* update state */ } if (AC(ac) & XLNTMASK) AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); } return (AC(ac) & XLNTMASK)? XT_NOSK: XT_SKIP; /* Edit - checked against KS10 ucode Only the flags/pattern pointer word is MBZ checked. AC = flags, pattern pointer AC + 1 = source byte pointer AC + 3 = mark address AC + 4 = destination byte pointer */ case XT_EDIT: /* EDIT */ if (AC(ac) & XT_MBZE) /* check pattern MBZ */ return XT_MUUO; xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ for (ppi = 1, ret = -1, flg = 0; ret < 0; flg++, ppi = 1) { if (flg && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ pp = (int32) AC(ac) & AMASK; /* get pattern ptr */ b1 = Read (pp, MM_OPND); /* get pattern word */ pat = ED_PBYTE (b1, AC(ac)); /* get pattern byte */ switch ((pat < 0100)? pat: ((pat >> ED_V_POPC) + 0100)) { case ED_STOP: /* stop */ ret = XT_SKIP; /* exit loop */ break; case ED_SELECT: /* select source */ b1 = incloadbp (p1, pflgs); /* get src */ entad = (e1 + ((int32) b1 >> 1)) & AMASK; f1 = ((Read (entad, MM_OPND) >> ((b1 & 1)? 0: 18)) & RMASK); i = XT_GETCODE (f1); if (i & 2) xflgs = (i & 1)? xflgs | XT_MFLG: xflgs & ~XT_MFLG; switch (i) { case 00: case 02: case 03: if (xflgs & XT_SFLG) f1 = f1 & XT_BYMASK; else { f1 = Read (INCA (ea), MM_OPND); if (f1 == 0) break; } incstorebp (f1, p4, pflgs); break; case 01: ret = XT_NOSK; /* exit loop */ break; case 04: case 06: case 07: xflgs = xflgs | XT_NFLG; f1 = f1 & XT_BYMASK; if ((xflgs & XT_SFLG) == 0) { f2 = Read (ADDA (ea, 2), MM_OPND); Write ((a10) AC(p3), AC(p4), MM_OPND); if (f2) incstorebp (f2, p4, pflgs); xflgs = xflgs | XT_SFLG; } incstorebp (f1, p4, pflgs); break; case 05: xflgs = xflgs | XT_NFLG; ret = XT_NOSK; /* exit loop */ break; } /* end case xlate op */ break; case ED_SIGST: /* start significance */ if ((xflgs & XT_SFLG) == 0) { f2 = Read (ADDA (ea, 2), MM_OPND); Write ((a10) AC(p3), AC(p4), MM_OPND); if (f2) incstorebp (f2, p4, pflgs); xflgs = xflgs | XT_SFLG; } break; case ED_FLDSEP: /* separate fields */ xflgs = 0; break; case ED_EXCHMD: /* exchange */ f2 = Read ((int32) (AC(p3) & AMASK), MM_OPND); Write ((int32) (AC(p3) & AMASK), AC(p4), MM_OPND); AC(p4) = f2; break; case (0100 + (ED_MESSAG >> ED_V_POPC)): /* message */ if (xflgs & XT_SFLG) f1 = Read (ea + (pat & ED_M_NUM) + 1, MM_OPND); else { f1 = Read (ea + 1, MM_OPND); if (f1 == 0) break; } incstorebp (f1, p4, pflgs); break; case (0100 + (ED_SKPM >> ED_V_POPC)): /* skip on M */ if (xflgs & XT_MFLG) ppi = (pat & ED_M_NUM) + 2; break; case (0100 + (ED_SKPN >> ED_V_POPC)): /* skip on N */ if (xflgs & XT_NFLG) ppi = (pat & ED_M_NUM) + 2; break; case (0100 + (ED_SKPA >> ED_V_POPC)): /* skip always */ ppi = (pat & ED_M_NUM) + 2; break; default: /* NOP or undefined */ break; } /* end case pttrn op */ AC(ac) = AC(ac) + ((ppi & ED_M_PBYN) << ED_V_PBYN); AC(ac) = AC(ac) + (ppi >> 2) + ((AC(ac) & ED_PBYNO)? 1: 0); AC(ac) = xflgs | (AC(ac) & ~(XT_MBZE | XFLGMASK)); } return ret; } /* end case xop */ return XT_MUUO; } /* Supporting subroutines */ /* Increment byte pointer, register version */ d10 incbp (d10 bp) { int32 p, s; p = GET_P (bp); /* get P and S */ s = GET_S (bp); p = p - s; /* adv P */ if (p < 0) { /* end of word? */ bp = (bp & LMASK) | (INCR (bp)); /* increment addr */ p = (36 - s) & 077; /* reset P */ } bp = PUT_P (bp, p); /* store new P */ return bp; } /* Increment and load byte, extended version - uses register log */ d10 incloadbp (int32 ac, int32 pflgs) { a10 ba; d10 bp, wd; int32 p, s; bp = AC(ac) = incbp (AC(ac)); /* increment bp */ XT_INSRLOG (ac, rlog); /* log change */ p = GET_P (bp); /* get P and S */ s = GET_S (bp); ba = calc_ea (bp, MM_EA_XSRC); /* calc bp eff addr */ wd = Read (ba, MM_XSRC); /* read word */ wd = (wd >> p) & bytemask[s]; /* get byte */ return wd; } /* Increment and deposit byte, extended version - uses register log */ void incstorebp (d10 val, int32 ac, int32 pflgs) { a10 ba; d10 bp, wd, mask; int32 p, s; bp = AC(ac) = incbp (AC(ac)); /* increment bp */ XT_INSRLOG (ac, rlog); /* log change */ p = GET_P (bp); /* get P and S */ s = GET_S (bp); ba = calc_ea (bp, MM_EA_XDST); /* calc bp eff addr */ wd = Read (ba, MM_XDST); /* read, write test */ mask = bytemask[s] << p; /* shift mask, val */ val = val << p; wd = (wd & ~mask) | (val & mask); /* insert byte */ Write (ba, wd & DMASK, MM_XDST); return; } /* Translate byte Arguments by = byte to translate tblad = virtual address of translation table *xflgs = pointer to word containing translation flags prv = previous mode flag for table lookup Returns xby = >= 0, translated byte < 0, terminate translation */ d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 prv) { a10 ea; int32 tcode; d10 tblent; ea = (tblad + ((int32) by >> 1)) & AMASK; tblent = ((Read (ea, prv) >> ((by & 1)? 0: 18)) & RMASK); tcode = XT_GETCODE (tblent); /* get xlate code */ switch (tcode) { case 00: return (*xflgs & XT_SFLG)? tblent: by; case 01: break; case 02: *xflgs = *xflgs & ~XT_MFLG; return (*xflgs & XT_SFLG)? tblent: by; case 03: *xflgs = *xflgs | XT_MFLG; return (*xflgs & XT_SFLG)? tblent: by; case 04: *xflgs = *xflgs | XT_SFLG | XT_NFLG; return tblent; case 05: *xflgs = *xflgs | XT_NFLG; break; case 06: *xflgs = (*xflgs | XT_SFLG | XT_NFLG) & ~XT_MFLG; return tblent; case 07: *xflgs = *xflgs | XT_SFLG | XT_NFLG | XT_MFLG; return tblent; } /* end case */ return -1; } /* Fill out the destination string Arguments: fill = fill ac = 2 word AC block (length, byte pointer) cnt = fill count pflgs = PXCT flags */ void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs) { int32 i, t; int32 p1 = ADDA (ac, 1); for (i = 0; i < cnt; i++) { if (i && (t = test_int ())) ABORT (t); rlog = 0; /* clear log */ incstorebp (fill, p1, pflgs); AC(ac) = (AC(ac) & XFLGMASK) | ((AC(ac) - 1) & XLNTMASK); } rlog = 0; return; } /* Clean up after page fault Arguments: logv = register change log For each register in logv, decrement the register's contents as though it were a byte pointer. Note that the KS10 does do a full decrement calculation but merely adds S to P. */ void xtcln (int32 logv) { int32 p, reg; while (logv) { XT_REMRLOG (reg, logv); /* get next reg */ if ((reg >= 0) && (reg < AC_NUM)) { p = GET_P (AC(reg)) + GET_S (AC(reg)); /* get p + s */ AC(reg) = PUT_P (AC(reg), p); /* p <- p + s */ } } return; } simh-3.8.1/PDP10/pdp10_fe.c0000644000175000017500000001442011107655434013176 0ustar vlmvlm/* pdp10_fe.c: PDP-10 front end (console terminal) simulator 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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. fe KS10 console front end 18-Jun-07 RMS Added UNIT_IDLE flag to console input 17-Oct-06 RMS Synced keyboard to clock for idling 28-May-04 RMS Removed SET FE CTRL-C 29-Dec-03 RMS Added console backpressure support 25-Apr-03 RMS Revised for extended file support 22-Dec-02 RMS Added break support 30-May-02 RMS Widened COUNT to 32b 30-Nov-01 RMS Added extended SET/SHOW support 23-Oct-01 RMS New IO page address constants 07-Sep-01 RMS Moved function prototypes */ #include "pdp10_defs.h" #define UNIT_DUMMY (1 << UNIT_V_UF) extern d10 *M; extern int32 apr_flg; extern int32 tmxr_poll; t_stat fei_svc (UNIT *uptr); t_stat feo_svc (UNIT *uptr); t_stat fe_reset (DEVICE *dptr); t_stat fe_stop_os (UNIT *uptr, int32 val, char *cptr, void *desc); /* FE data structures fe_dev FE device descriptor fe_unit FE unit descriptor fe_reg FE register list */ #define fei_unit fe_unit[0] #define feo_unit fe_unit[1] UNIT fe_unit[] = { { UDATA (&fei_svc, UNIT_IDLE, 0), 0 }, { UDATA (&feo_svc, 0, 0), SERIAL_OUT_WAIT } }; REG fe_reg[] = { { ORDATA (IBUF, fei_unit.buf, 8) }, { DRDATA (ICOUNT, fei_unit.pos, T_ADDR_W), REG_RO + PV_LEFT }, { DRDATA (ITIME, fei_unit.wait, 24), PV_LEFT }, { ORDATA (OBUF, feo_unit.buf, 8) }, { DRDATA (OCOUNT, feo_unit.pos, T_ADDR_W), REG_RO + PV_LEFT }, { DRDATA (OTIME, feo_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB fe_mod[] = { { UNIT_DUMMY, 0, NULL, "STOP", &fe_stop_os }, { 0 } }; DEVICE fe_dev = { "FE", fe_unit, fe_reg, fe_mod, 2, 10, 31, 1, 8, 8, NULL, NULL, &fe_reset, NULL, NULL, NULL }; /* Front end processor (console terminal) Communications between the KS10 and its front end is based on an in-memory status block and two interrupt lines: interrupt-to-control (APR_ITC) and interrupt-from-console (APR_CON). When the KS10 wants to print a character on the terminal, 1. It places a character, plus the valid flag, in FE_CTYOUT. 2. It interrupts the front end processor. 3. The front end processor types the character and then zeroes FE_CTYOUT. 4. The front end procesor interrupts the KS10. When the front end wants to send an input character to the KS10, 1. It places a character, plus the valid flag, in FE_CTYIN. 2. It interrupts the KS10. 3. It waits for the KS10 to take the character and clear the valid flag. 4. It can then send more input (the KS10 may signal this by interrupting the front end). Note that the protocol has both ambiguity (interrupt to the KS10 may mean character printed, or input character available, or both) and lack of symmetry (the KS10 does not inform the front end that it has taken an input character). */ void fe_intr (void) { if (M[FE_CTYOUT] & FE_CVALID) { /* char to print? */ feo_unit.buf = (int32) M[FE_CTYOUT] & 0177; /* pick it up */ feo_unit.pos = feo_unit.pos + 1; sim_activate (&feo_unit, feo_unit.wait); /* sched completion */ } else if ((M[FE_CTYIN] & FE_CVALID) == 0) { /* input char taken? */ sim_cancel (&fei_unit); /* sched immediate */ sim_activate (&fei_unit, 0); /* keyboard poll */ } return; } t_stat feo_svc (UNIT *uptr) { t_stat r; if ((r = sim_putchar_s (uptr->buf)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } M[FE_CTYOUT] = 0; /* clear char */ apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ return SCPE_OK; } t_stat fei_svc (UNIT *uptr) { int32 temp; sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return temp; if (temp & SCPE_BREAK) /* ignore break */ return SCPE_OK; uptr->buf = temp & 0177; uptr->pos = uptr->pos + 1; M[FE_CTYIN] = uptr->buf | FE_CVALID; /* put char in mem */ apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ return SCPE_OK; } /* Reset */ t_stat fe_reset (DEVICE *dptr) { fei_unit.buf = feo_unit.buf = 0; M[FE_CTYIN] = M[FE_CTYOUT] = 0; apr_flg = apr_flg & ~(APRF_ITC | APRF_CON); sim_activate_abs (&fei_unit, KBD_WAIT (fei_unit.wait, tmxr_poll)); return SCPE_OK; } /* Stop operating system */ t_stat fe_stop_os (UNIT *uptr, int32 val, char *cptr, void *desc) { M[FE_SWITCH] = IOBA_RP; /* tell OS to stop */ return SCPE_OK; } simh-3.8.1/PDP10/pdp10_ksio.c0000644000175000017500000006555011113312510013541 0ustar vlmvlm/* pdp10_ksio.c: PDP-10 KS10 I/O subsystem simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. uba Unibus adapters 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 25-Jan-04 RMS Added stub floating address routine 12-Mar-03 RMS Added logical name support 10-Oct-02 RMS Revised for dynamic table generation Added SHOW IOSPACE routine 29-Sep-02 RMS Added variable vector, central map support 25-Jan-02 RMS Revised for multiple DZ11's 06-Jan-02 RMS Revised enable/disable support 23-Sep-01 RMS New IO page address constants 07-Sep-01 RMS Revised device disable mechanism 25-Aug-01 RMS Enabled DZ11 21-Aug-01 RMS Updated DZ11 disable 01-Jun-01 RMS Updated DZ11 vectors 12-May-01 RMS Fixed typo The KS10 uses the PDP-11 Unibus for its I/O, via adapters. While nominally four adapters are supported, in practice only 1 and 3 are implemented. The disks are placed on adapter 1, the rest of the I/O devices on adapter 3. In theory, we should maintain completely separate Unibuses, with distinct PI systems. In practice, this simulator has so few devices that we can get away with a single PI system, masking for which devices are on adapter 1, and which on adapter 3. The Unibus implementation is modeled on the Qbus in the PDP-11 simulator and is described there. The I/O subsystem is programmed by I/O instructions which create Unibus operations (read, read pause, write, write byte). DMA is the responsibility of the I/O device simulators, which also implement Unibus to physical memory mapping. The priority interrupt subsystem (and other privileged functions) is programmed by I/O instructions with internal devices codes (opcodes 700-702). These are dispatched here, although many are handled in the memory management unit or elsewhere. The ITS instructions are significantly different from the TOPS-10/20 instructions. They do not use the extended address calculation but instead provide instruction variants (Q for Unibus adapter 1, I for Unibus adapter 3) which insert the Unibus adapter number into the effective address. */ #include "pdp10_defs.h" #include #include "sim_sock.h" #include "sim_tmxr.h" #define XBA_MBZ 0400000 /* ba mbz */ #define eaRB (ea & ~1) #define GETBYTE(ea,x) ((((ea) & 1)? (x) >> 8: (x)) & 0377) #define UBNXM_FAIL(pa,op) \ n = iocmap[GET_IOUBA (pa)]; \ if (n >= 0) \ ubcs[n] = ubcs[n] | UBCS_TMO | UBCS_NXD; \ pager_word = PF_HARD | PF_VIRT | PF_IO | \ ((op == WRITEB)? PF_BYTE: 0) | \ (TSTF (F_USR)? PF_USER: 0) | (pa); \ ABORT (PAGE_FAIL) /* Unibus adapter data */ int32 ubcs[UBANUM] = { 0 }; /* status registers */ int32 ubmap[UBANUM][UMAP_MEMSIZE] = { 0 }; /* Unibus maps */ int32 int_req = 0; /* interrupt requests */ /* Map IO controller numbers to Unibus adapters: -1 = non-existent */ static int iocmap[IO_N_UBA] = { /* map I/O ext to UBA # */ -1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static const int32 ubabr76[UBANUM] = { INT_UB1 & (INT_IPL7 | INT_IPL6), INT_UB3 & (INT_IPL7 | INT_IPL6) }; static const int32 ubabr54[UBANUM] = { INT_UB1 & (INT_IPL5 | INT_IPL4), INT_UB3 & (INT_IPL5 | INT_IPL4) }; static const int32 ubashf[4] = { 18, 26, 0, 8 }; extern d10 *M; /* main memory */ extern d10 *ac_cur; extern d10 pager_word; extern int32 flags; extern const int32 pi_l2bit[8]; extern UNIT cpu_unit; extern FILE *sim_log; extern jmp_buf save_env; extern DEVICE *sim_devices[]; extern int32 pi_eval (void); extern int32 rp_inta (void); extern int32 tu_inta (void); extern int32 lp20_inta (void); extern int32 dz_rxinta (void); extern int32 dz_txinta (void); t_stat ubmap_rd (int32 *data, int32 addr, int32 access); t_stat ubmap_wr (int32 data, int32 addr, int32 access); t_stat ubs_rd (int32 *data, int32 addr, int32 access); t_stat ubs_wr (int32 data, int32 addr, int32 access); t_stat rd_zro (int32 *data, int32 addr, int32 access); t_stat wr_nop (int32 data, int32 addr, int32 access); t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat uba_reset (DEVICE *dptr); d10 ReadIO (a10 ea); void WriteIO (a10 ea, d10 val, int32 mode); /* Unibus adapter data structures uba_dev UBA device descriptor uba_unit UBA units uba_reg UBA register list */ DIB ubmp1_dib = { IOBA_UBMAP1, IOLN_UBMAP1, &ubmap_rd, &ubmap_wr, 0 }; DIB ubmp3_dib = { IOBA_UBMAP3, IOLN_UBMAP3, &ubmap_rd, &ubmap_wr, 0 }; DIB ubcs1_dib = { IOBA_UBCS1, IOLN_UBCS1, &ubs_rd, &ubs_wr, 0 }; DIB ubcs3_dib = { IOBA_UBCS3, IOLN_UBCS3, &ubs_rd, &ubs_wr, 0 }; DIB ubmn1_dib = { IOBA_UBMNT1, IOLN_UBMNT1, &rd_zro, &wr_nop, 0 }; DIB ubmn3_dib = { IOBA_UBMNT3, IOLN_UBMNT3, &rd_zro, &wr_nop, 0 }; DIB msys_dib = { 00100000, 1, &rd_zro, &wr_nop, 0 }; UNIT uba_unit[] = { { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) }, { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) } }; REG uba_reg[] = { { ORDATA (INTREQ, int_req, 32), REG_RO }, { ORDATA (UB1CS, ubcs[0], 18) }, { ORDATA (UB3CS, ubcs[1], 18) }, { NULL } }; DEVICE uba_dev = { "UBA", uba_unit, uba_reg, NULL, UBANUM, 8, UMAP_ASIZE, 1, 8, 32, &uba_ex, &uba_dep, &uba_reset, NULL, NULL, NULL, NULL, 0 }; /* PDP-11 I/O structures */ DIB *dib_tab[DIB_MAX]; /* run-time DIBs */ int32 (*int_ack[32])(void); /* int ack routines */ int32 int_vec[32]; /* int vectors */ DIB *std_dib[] = { /* standard DIBs */ &ubmp1_dib, &ubmp3_dib, &ubcs1_dib, &ubcs3_dib, &ubmn1_dib, &ubmn3_dib, &msys_dib, NULL }; /* IO 710 (DEC) TIOE - test I/O word, skip if zero (ITS) IORDI - read word from Unibus 3 returns TRUE if skip, FALSE otherwise */ t_bool io710 (int32 ac, a10 ea) { d10 val; if (Q_ITS) /* IORDI */ AC(ac) = ReadIO (IO_UBA3 | ea); else { /* TIOE */ val = ReadIO (ea); /* read word */ if ((AC(ac) & val) == 0) return TRUE; } return FALSE; } /* IO 711 (DEC) TION - test I/O word, skip if non-zero (ITS) IORDQ - read word from Unibus 1 returns TRUE if skip, FALSE otherwise */ t_bool io711 (int32 ac, a10 ea) { d10 val; if (Q_ITS) /* IORDQ */ AC(ac) = ReadIO (IO_UBA1 | ea); else { /* TION */ val = ReadIO (ea); /* read word */ if ((AC(ac) & val) != 0) return TRUE; } return FALSE; } /* IO 712 (DEC) RDIO - read I/O word, addr in ea (ITS) IORD - read I/O word, addr in M[ea] */ d10 io712 (a10 ea) { return ReadIO (ea); /* RDIO, IORD */ } /* IO 713 (DEC) WRIO - write I/O word, addr in ea (ITS) IOWR - write I/O word, addr in M[ea] */ void io713 (d10 val, a10 ea) { WriteIO (ea, val & 0177777, WRITE); /* WRIO, IOWR */ return; } /* IO 714 (DEC) BSIO - set bit in I/O address (ITS) IOWRI - write word to Unibus 3 */ void io714 (d10 val, a10 ea) { d10 temp; val = val & 0177777; if (Q_ITS) /* IOWRI */ WriteIO (IO_UBA3 | ea, val, WRITE); else { temp = ReadIO (ea); /* BSIO */ temp = temp | val; WriteIO (ea, temp, WRITE); } return; } /* IO 715 (DEC) BCIO - clear bit in I/O address (ITS) IOWRQ - write word to Unibus 1 */ void io715 (d10 val, a10 ea) { d10 temp; val = val & 0177777; if (Q_ITS) /* IOWRQ */ WriteIO (IO_UBA1 | ea, val, WRITE); else { temp = ReadIO (ea); /* BCIO */ temp = temp & ~val; WriteIO (ea, temp, WRITE); } return; } /* IO 720 (DEC) TIOEB - test I/O byte, skip if zero (ITS) IORDBI - read byte from Unibus 3 returns TRUE if skip, FALSE otherwise */ t_bool io720 (int32 ac, a10 ea) { d10 val; if (Q_ITS) { /* IORDBI */ val = ReadIO (IO_UBA3 | eaRB); AC(ac) = GETBYTE (ea, val); } else { /* TIOEB */ val = ReadIO (eaRB); val = GETBYTE (ea, val); if ((AC(ac) & val) == 0) return TRUE; } return FALSE; } /* IO 721 (DEC) TIONB - test I/O word, skip if non-zero (ITS) IORDBQ - read word from Unibus 1 returns TRUE if skip, FALSE otherwise */ t_bool io721 (int32 ac, a10 ea) { d10 val; if (Q_ITS) { /* IORDBQ */ val = ReadIO (IO_UBA1 | eaRB); AC(ac) = GETBYTE (ea, val); } else { /* TIONB */ val = ReadIO (eaRB); val = GETBYTE (ea, val); if ((AC(ac) & val) != 0) return TRUE; } return FALSE; } /* IO 722 (DEC) RDIOB - read I/O byte, addr in ea (ITS) IORDB - read I/O byte, addr in M[ea] */ d10 io722 (a10 ea) { d10 val; val = ReadIO (eaRB); /* RDIOB, IORDB */ return GETBYTE (ea, val); } /* IO 723 (DEC) WRIOB - write I/O byte, addr in ea (ITS) IOWRB - write I/O byte, addr in M[ea] */ void io723 (d10 val, a10 ea) { WriteIO (ea, val & 0377, WRITEB); /* WRIOB, IOWRB */ return; } /* IO 724 (DEC) BSIOB - set bit in I/O byte address (ITS) IOWRBI - write byte to Unibus 3 */ void io724 (d10 val, a10 ea) { d10 temp; val = val & 0377; if (Q_ITS) /* IOWRBI */ WriteIO (IO_UBA3 | ea, val, WRITEB); else { temp = ReadIO (eaRB); /* BSIOB */ temp = GETBYTE (ea, temp); temp = temp | val; WriteIO (ea, temp, WRITEB); } return; } /* IO 725 (DEC) BCIOB - clear bit in I/O byte address (ITS) IOWRBQ - write byte to Unibus 1 */ void io725 (d10 val, a10 ea) { d10 temp; val = val & 0377; if (Q_ITS) /* IOWRBQ */ WriteIO (IO_UBA1 | ea, val, WRITEB); else { temp = ReadIO (eaRB); /* BCIOB */ temp = GETBYTE (ea, temp); temp = temp & ~val; WriteIO (ea, temp, WRITEB); } return; } /* Read and write I/O devices. These routines are the linkage between the 64b world of the main simulator and the 32b world of the device simulators. */ d10 ReadIO (a10 ea) { uint32 pa = (uint32) ea; int32 i, n, val; DIB *dibp; for (i = 0; dibp = dib_tab[i]; i++ ) { if ((pa >= dibp->ba) && (pa < (dibp->ba + dibp->lnt))) { dibp->rd (&val, pa, READ); pi_eval (); return ((d10) val); } } UBNXM_FAIL (pa, READ); } void WriteIO (a10 ea, d10 val, int32 mode) { uint32 pa = (uint32) ea; int32 i, n; DIB *dibp; for (i = 0; dibp = dib_tab[i]; i++ ) { if ((pa >= dibp->ba) && (pa < (dibp->ba + dibp->lnt))) { dibp->wr ((int32) val, pa, mode); pi_eval (); return; } } UBNXM_FAIL (pa, mode); } /* Mapped read and write routines - used by standard Unibus devices on Unibus 1 */ a10 Map_Addr10 (a10 ba, int32 ub) { a10 pa10; int32 vpn = PAG_GETVPN (ba >> 2); /* get PDP-10 page number */ if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || /* invalid map? */ ((ubmap[ub][vpn] & UMAP_VLD) == 0)) return -1; pa10 = (ubmap[ub][vpn] + PAG_GETOFF (ba >> 2)) & PAMASK; return pa10; } int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) { uint32 lim; a10 pa10; lim = ba + bc; for ( ; ba < lim; ba++) { /* by bytes */ pa10 = Map_Addr10 (ba, 1); /* map addr */ if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ return (lim - ba); /* return bc */ } *buf++ = (uint8) ((M[pa10] >> ubashf[ba & 3]) & 0377); } return 0; } int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) { uint32 lim; a10 pa10; ba = ba & ~01; /* align start */ lim = ba + (bc & ~01); for ( ; ba < lim; ba = ba + 2) { /* by words */ pa10 = Map_Addr10 (ba, 1); /* map addr */ if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ return (lim - ba); /* return bc */ } *buf++ = (uint16) ((M[pa10] >> ((ba & 2)? 0: 18)) & 0177777); } return 0; } int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) { uint32 lim; a10 pa10; d10 mask; lim = ba + bc; for ( ; ba < lim; ba++) { /* by bytes */ pa10 = Map_Addr10 (ba, 1); /* map addr */ if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ return (lim - ba); /* return bc */ } mask = 0377; M[pa10] = (M[pa10] & ~(mask << ubashf[ba & 3])) | (((d10) *buf++) << ubashf[ba & 3]); } return 0; } int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) { uint32 lim; a10 pa10; d10 val; ba = ba & ~01; /* align start */ lim = ba + (bc & ~01); for ( ; ba < lim; ba++) { /* by bytes */ pa10 = Map_Addr10 (ba, 1); /* map addr */ if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ return (lim - ba); /* return bc */ } val = *buf++; /* get data */ if (ba & 2) M[pa10] = (M[pa10] & 0777777600000) | val; else M[pa10] = (M[pa10] & 0600000777777) | (val << 18); } return 0; } /* Evaluate Unibus priority interrupts */ int32 pi_ub_eval () { int32 i, lvl; for (i = lvl = 0; i < UBANUM; i++) { if (int_req & ubabr76[i]) lvl = lvl | pi_l2bit[UBCS_GET_HI (ubcs[i])]; if (int_req & ubabr54[i]) lvl = lvl | pi_l2bit[UBCS_GET_LO (ubcs[i])]; } return lvl; } /* Return Unibus device vector Takes as input the request level calculated by pi_eval If there is an interrupting Unibus device at that level, return its vector, otherwise, returns 0 */ int32 pi_ub_vec (int32 rlvl, int32 *uba) { int32 i, masked_irq; for (i = masked_irq = 0; i < UBANUM; i++) { if ((rlvl == UBCS_GET_HI (ubcs[i])) && /* req on hi level? */ (masked_irq = int_req & ubabr76[i])) break; if ((rlvl == UBCS_GET_LO (ubcs[i])) && /* req on lo level? */ (masked_irq = int_req & ubabr54[i])) break; } *uba = (i << 1) + 1; /* store uba # */ for (i = 0; (i < 32) && masked_irq; i++) { /* find hi pri req */ if ((masked_irq >> i) & 1) { int_req = int_req & ~(1u << i); /* clear req */ if (int_ack[i]) return int_ack[i](); return int_vec[i]; /* return vector */ } } return 0; } /* Unibus adapter map routines */ t_stat ubmap_rd (int32 *val, int32 pa, int32 mode) { int32 n = iocmap[GET_IOUBA (pa)]; if (n < 0) ABORT (STOP_ILLIOC); *val = ubmap[n][pa & UMAP_AMASK]; return SCPE_OK; } t_stat ubmap_wr (int32 val, int32 pa, int32 mode) { int32 n = iocmap[GET_IOUBA (pa)]; if (n < 0) ABORT (STOP_ILLIOC); ubmap[n][pa & UMAP_AMASK] = UMAP_POSFL (val) | UMAP_POSPN (val); return SCPE_OK; } /* Unibus adapter control/status routines */ t_stat ubs_rd (int32 *val, int32 pa, int32 mode) { int32 n = iocmap[GET_IOUBA (pa)]; if (n < 0) ABORT (STOP_ILLIOC); if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; *val = ubcs[n] = ubcs[n] & ~UBCS_RDZ; return SCPE_OK; } t_stat ubs_wr (int32 val, int32 pa, int32 mode) { int32 n = iocmap[GET_IOUBA (pa)]; if (n < 0) ABORT (STOP_ILLIOC); if (val & UBCS_INI) { reset_all (5); /* start after UBA */ ubcs[n] = val & UBCS_DXF; } else ubcs[n] = val & UBCS_RDW; if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; return SCPE_OK; } /* Unibus adapter read zero/write ignore routines */ t_stat rd_zro (int32 *val, int32 pa, int32 mode) { *val = 0; return SCPE_OK; } t_stat wr_nop (int32 val, int32 pa, int32 mode) { return SCPE_OK; } /* Simulator interface routines */ t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { int32 uba = uptr - uba_unit; if (addr >= UMAP_MEMSIZE) return SCPE_NXM; *vptr = ubmap[uba][addr]; return SCPE_OK; } t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { int32 uba = uptr - uba_unit; if (addr >= UMAP_MEMSIZE) return SCPE_NXM; ubmap[uba][addr] = (int32) val & UMAP_MASK; return SCPE_OK; } t_stat uba_reset (DEVICE *dptr) { int32 i, uba; int_req = 0; for (uba = 0; uba < UBANUM; uba++) { ubcs[uba] = 0; for (i = 0; i < UMAP_MEMSIZE; i++) ubmap[uba][i] = 0; } pi_eval (); return SCPE_OK; } /* Change device address */ t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newba; t_stat r; if (cptr == NULL) return SCPE_ARG; if ((val == 0) || (uptr == NULL)) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newba = (uint32) get_uint (cptr, 8, PAMASK, &r); /* get new */ if ((r != SCPE_OK) || (newba == dibp->ba)) return r; if (GET_IOUBA (newba) != GET_IOUBA (dibp->ba)) return SCPE_ARG; if (newba % ((uint32) val)) /* check modulus */ return SCPE_ARG; dibp->ba = newba; /* store */ return SCPE_OK; } /* Show device address */ t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; fprintf (st, "address=%07o", dibp->ba); if (dibp->lnt > 1) fprintf (st, "-%07o", dibp->ba + dibp->lnt - 1); return SCPE_OK; } /* Change device vector */ t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newvec; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newvec = (uint32) get_uint (cptr, 8, VEC_Q + 01000, &r); if ((r != SCPE_OK) || (newvec == VEC_Q) || ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) || (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; dibp->vec = newvec; return SCPE_OK; } /* Show device vector */ t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) { DEVICE *dptr; DIB *dibp; uint32 vec, numvec; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; vec = dibp->vec; if (arg) numvec = arg; else numvec = dibp->vnum; if (vec == 0) fprintf (st, "no vector"); else { fprintf (st, "vector=%o", vec); if (numvec > 1) fprintf (st, "-%o", vec + (4 * (numvec - 1))); } return SCPE_OK; } /* Show vector for terminal multiplexor */ t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 arg, void *desc) { TMXR *mp = (TMXR *) desc; if ((mp == NULL) || (arg == 0)) return SCPE_IERR; return show_vec (st, uptr, ((mp->lines * 2) / arg), desc); } /* Test for conflict in device addresses */ t_bool dev_conflict (DIB *curr) { uint32 i, end; DEVICE *dptr; DIB *dibp; end = curr->ba + curr->lnt - 1; /* get end */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((dibp == NULL) || (dibp == curr) || (dptr->flags & DEV_DIS)) continue; if (((curr->ba >= dibp->ba) && /* overlap start? */ (curr->ba < (dibp->ba + dibp->lnt))) || ((end >= dibp->ba) && /* overlap end? */ (end < (dibp->ba + dibp->lnt)))) { printf ("Device %s address conflict at %08o\n", sim_dname (dptr), dibp->ba); if (sim_log) fprintf (sim_log, "Device %s address conflict at %08o\n", sim_dname (dptr), dibp->ba); return TRUE; } } return FALSE; } /* Build interrupt tables */ void build_int_vec (int32 vloc, int32 ivec, int32 (*iack)(void) ) { if (iack != NULL) int_ack[vloc] = iack; else int_vec[vloc] = ivec; return; } /* Build dib_tab from device list */ t_bool build_dib_tab (void) { int32 i, j, k; DEVICE *dptr; DIB *dibp; for (i = 0; i < 32; i++) { /* clear intr tables */ int_vec[i] = 0; int_ack[i] = NULL; } for (i = j = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; for (k = 0; k < dibp->vnum; k++) /* loop thru vec */ build_int_vec (dibp->vloc + k, /* add vector */ dibp->vec + (k * 4), dibp->ack[k]); if (dibp->lnt != 0) { /* I/O addresses? */ dib_tab[j++] = dibp; /* add DIB to dib_tab */ if (j >= DIB_MAX) /* too many? */ return SCPE_IERR; } } /* end if enabled */ } /* end for */ for (i = 0; (dibp = std_dib[i]) != NULL; i++) { /* loop thru std */ dib_tab[j++] = dibp; /* add to dib_tab */ if (j >= DIB_MAX) /* too many? */ return SCPE_IERR; } dib_tab[j] = NULL; /* end with NULL */ for (i = 0; (dibp = dib_tab[i]) != NULL; i++) { /* test built dib_tab */ if (dev_conflict (dibp)) /* for conflicts */ return SCPE_STOP; } return SCPE_OK; } /* Show dib_tab */ t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 i, j, done = 0; DEVICE *dptr; DIB *dibt; build_dib_tab (); /* build table */ while (done == 0) { /* sort ascending */ done = 1; /* assume done */ for (i = 0; dib_tab[i + 1] != NULL; i++) { /* check table */ if (dib_tab[i]->ba > dib_tab[i + 1]->ba) { /* out of order? */ dibt = dib_tab[i]; /* interchange */ dib_tab[i] = dib_tab[i + 1]; dib_tab[i + 1] = dibt; done = 0; /* not done */ } } } /* end while */ for (i = 0; dib_tab[i] != NULL; i++) { /* print table */ for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { if (((DIB*) sim_devices[j]->ctxt) == dib_tab[i]) { dptr = sim_devices[j]; break; } } fprintf (st, "%07o - %07o\t%s\n", dib_tab[i]->ba, dib_tab[i]->ba + dib_tab[i]->lnt - 1, dptr? sim_dname (dptr): "CPU"); } return SCPE_OK; } /* Stub auto-configure */ t_stat auto_config (char *name, int32 num) { return SCPE_OK; } /* Stub floating address */ t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) { return SCPE_OK; } simh-3.8.1/PDP10/pdp10_mdfp.c0000644000175000017500000010201111112112574013511 0ustar vlmvlm/* pdp10_mdfp.c: PDP-10 multiply/divide and floating point simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 2-Apr-04 RMS Fixed bug in floating point unpack Fixed bug in FIXR (found by Phil Stone, fixed by Chris Smith) 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 10-Aug-01 RMS Removed register in declarations Instructions handled in this module: imul integer multiply idiv integer divide mul multiply div divide dmul double precision multiply ddiv double precision divide fad(r) floating add (and round) fsb(r) floating subtract (and round) fmp(r) floating multiply (and round) fdv(r) floating divide and round fsc floating scale fix(r) floating to fixed (and round) fltr fixed to floating and round dfad double precision floating add/subtract dfmp double precision floating multiply dfdv double precision floating divide The PDP-10 stores double (quad) precision integers in sequential AC's or memory locations. Integers are stored in 2's complement form. Only the sign of the high order word matters; the signs in low order words are ignored on input and set to the sign of the result on output. Quad precision integers exist only in the AC's as the result of a DMUL or the dividend of a DDIV. 0 00000000011111111112222222222333333 0 12345678901234567890123456789012345 +-+-----------------------------------+ |S| high order integer | AC(n), A +-+-----------------------------------+ |S| low order integer | AC(n + 1), A + 1 +-+-----------------------------------+ |S| low order integer | AC(n + 2) +-+-----------------------------------+ |S| low order integer | AC(n + 3) +-+-----------------------------------+ The PDP-10 supports two floating point formats: single and double precision. In both, the exponent is 8 bits, stored in excess 128 notation. The fraction is expected to be normalized. A single precision floating point number has 27 bits of fraction; a double precision number has 62 bits of fraction (the sign bit of the second word is ignored and is set to zero). In a negative floating point number, the exponent is stored in one's complement form, the fraction in two's complement form. 0 00000000 011111111112222222222333333 0 12345678 901234567890123456789012345 +-+--------+---------------------------+ |S|exponent| high order fraction | AC(n), A +-+--------+---------------------------+ |0| low order fraction | AC(n + 1), A + 1 +-+------------------------------------+ Note that treatment of the sign is different for double precision integers and double precision floating point. DMOVN (implemented as an inline macro) follows floating point conventions. The original PDP-10 CPU (KA10) used a different format for double precision numbers and included certain instructions to make software support easier. These instructions were phased out in the KL10 and KS10 and are treated as MUUO's. The KL10 added extended precision (11-bit exponent) floating point format (so-called G floating). These instructions were not implemented in the KS10 and are treated as MUUO's. */ #include "pdp10_defs.h" #include typedef struct { /* unpacked fp number */ int32 sign; /* sign */ int32 exp; /* exponent */ t_uint64 fhi; /* fraction high */ t_uint64 flo; /* for double prec */ } UFP; #define MSK32 0xFFFFFFFF #define FIT27 (DMASK - 0x07FFFFFF) #define FIT32 (DMASK - MSK32) #define SFRC TRUE /* frac 2's comp */ #define AFRC FALSE /* frac abs value */ /* In packed floating point number */ #define FP_BIAS 0200 /* exponent bias */ #define FP_N_FHI 27 /* # of hi frac bits */ #define FP_V_FHI 0 /* must be zero */ #define FP_M_FHI 0000777777777 #define FP_N_EXP 8 /* # of exp bits */ #define FP_V_EXP (FP_V_FHI + FP_N_FHI) #define FP_M_EXP 0377 #define FP_V_SIGN (FP_V_EXP + FP_N_EXP) /* sign */ #define FP_N_FLO 35 /* # of lo frac bits */ #define FP_V_FLO 0 /* must be zero */ #define FP_M_FLO 0377777777777 #define GET_FPSIGN(x) ((int32) (((x) >> FP_V_SIGN) & 1)) #define GET_FPEXP(x) ((int32) (((x) >> FP_V_EXP) & FP_M_EXP)) #define GET_FPHI(x) ((x) & FP_M_FHI) #define GET_FPLO(x) ((x) & FP_M_FLO) /* In unpacked floating point number */ #define FP_N_GUARD 1 /* # of guard bits */ #define FP_V_UFLO FP_N_GUARD /* <35:1> */ #define FP_V_URNDD (FP_V_UFLO - 1) /* dp round bit */ #define FP_V_UFHI (FP_V_UFLO + FP_N_FLO) /* <62:36> */ #define FP_V_URNDS (FP_V_UFHI - 1) /* sp round bit */ #define FP_V_UCRY (FP_V_UFHI + FP_N_FHI) /* <63> */ #define FP_V_UNORM (FP_V_UCRY - 1) /* normalized bit */ #define FP_UFHI 0x7FFFFFF000000000 #define FP_UFLO 0x0000000FFFFFFFFE #define FP_UFRAC 0x7FFFFFFFFFFFFFFE #define FP_URNDD 0x0000000000000001 #define FP_URNDS 0x0000000800000000 #define FP_UNORM 0x4000000000000000 #define FP_UCRY 0x8000000000000000 #define FP_ONES 0xFFFFFFFFFFFFFFFF #define UNEG(x) ((~x) + 1) #define DUNEG(x) x.flo = UNEG (x.flo); x.fhi = ~x.fhi + (x.flo == 0) extern d10 *ac_cur; /* current AC block */ extern int32 flags; /* flags */ void mul (d10 a, d10 b, d10 *rs); void funpack (d10 h, d10 l, UFP *r, t_bool sgn); void fnorm (UFP *r, t_int64 rnd); d10 fpack (UFP *r, d10 *lo, t_bool fdvneg); /* Integer multiply - checked against KS-10 ucode */ d10 imul (d10 a, d10 b) { d10 rs[2]; if ((a == SIGN) && (b == SIGN)) { /* KS10 hack */ SETF (F_AOV | F_T1); /* -2**35 squared */ return SIGN; } mul (a, b, rs); /* mpy, dprec result */ if (rs[0] && (rs[0] != ONES)) { /* high not all sign? */ rs[1] = TSTS (a ^ b)? SETS (rs[1]): CLRS (rs[1]); /* set sign */ SETF (F_AOV | F_T1); /* overflow */ } return rs[1]; } /* Integer divide, return quotient, remainder - checked against KS10 ucode The KS10 does not recognize -2^35/-1 as an error. Instead, it produces 2^35 (that is, -2^35) as the incorrect result. */ t_bool idiv (d10 a, d10 b, d10 *rs) { d10 dvd = ABS (a); /* make ops positive */ d10 dvr = ABS (b); if (dvr == 0) { /* divide by 0? */ SETF (F_DCK | F_AOV | F_T1); /* set flags, return */ return FALSE; } rs[0] = dvd / dvr; /* get quotient */ rs[1] = dvd % dvr; /* get remainder */ if (TSTS (a ^ b)) /* sign of result */ rs[0] = NEG (rs[0]); if (TSTS (a)) /* sign of remainder */ rs[1] = NEG (rs[1]); return TRUE; } /* Multiply, return double precision result - checked against KS10 ucode */ void mul (d10 s1, d10 s2, d10 *rs) { t_uint64 a = ABS (s1); t_uint64 b = ABS (s2); t_uint64 t, u, r; if ((a == 0) || (b == 0)) { /* operand = 0? */ rs[0] = rs[1] = 0; /* result 0 */ return; } if ((a & FIT32) || (b & FIT32)) { /* fit in 64b? */ t = a >> 18; /* no, split in half */ a = a & RMASK; /* "dp" multiply */ u = b >> 18; b = b & RMASK; r = (a * b) + (((a * u) + (b * t)) << 18); /* low is only 35b */ rs[0] = ((t * u) << 1) + (r >> 35); /* so lsh hi 1 */ rs[1] = r & MMASK; } else { r = a * b; /* fits, native mpy */ rs[0] = r >> 35; /* split at bit 35 */ rs[1] = r & MMASK; } if (TSTS (s1 ^ s2)) { /* result -? */ MKDNEG (rs); } else if (TSTS (rs[0])) { /* result +, 2**70? */ SETF (F_AOV | F_T1); /* overflow */ rs[1] = SETS (rs[1]); /* consistent - */ } return; } /* Divide, return quotient and remainder - checked against KS10 ucode Note that the initial divide check catches the case -2^70/-2^35; thus, the quotient can have at most 35 bits. */ t_bool divi (int32 ac, d10 b, d10 *rs) { int32 p1 = ADDAC (ac, 1); d10 dvr = ABS (b); /* make divr positive */ t_int64 t; int32 i; d10 dvd[2]; dvd[0] = AC(ac); /* divd high */ dvd[1] = CLRS (AC(p1)); /* divd lo, clr sgn */ if (TSTS (AC(ac))) { /* make divd positive */ DMOVN (dvd); } if (dvd[0] >= dvr) { /* divide fail? */ SETF (F_AOV | F_DCK | F_T1); /* set flags, return */ return FALSE; } if (dvd[0] & FIT27) { /* fit in 63b? */ for (i = 0, rs[0] = 0; i < 35; i++) { /* 35 quotient bits */ dvd[0] = (dvd[0] << 1) | ((dvd[1] >> 34) & 1); dvd[1] = (dvd[1] << 1) & MMASK; /* shift dividend */ rs[0] = rs[0] << 1; /* shift quotient */ if (dvd[0] >= dvr) { /* subtract work? */ dvd[0] = dvd[0] - dvr; /* quo bit is 1 */ rs[0] = rs[0] + 1; } } rs[1] = dvd[0]; /* store remainder */ } else { t = (dvd[0] << 35) | dvd[1]; /* concatenate */ rs[0] = t / dvr; /* quotient */ rs[1] = t % dvr; /* remainder */ } if (TSTS (AC(ac) ^ b)) /* sign of result */ rs[0] = NEG (rs[0]); if (TSTS (AC(ac))) /* sign of remainder */ rs[1] = NEG (rs[1]); return TRUE; } /* Double precision multiply. This is done the old fashioned way. Cross product multiplies would be a lot faster but would require more code. */ void dmul (int32 ac, d10 *mpy) { int32 p1 = ADDAC (ac, 1); int32 p2 = ADDAC (ac, 2); int32 p3 = ADDAC (ac, 3); int32 i; d10 mpc[2], sign; mpc[0] = AC(ac); /* mplcnd hi */ mpc[1] = CLRS (AC(p1)); /* mplcnd lo, clr sgn */ sign = mpc[0] ^ mpy[0]; /* sign of result */ if (TSTS (mpc[0])) { /* get abs (mpcnd) */ DMOVN (mpc); } if (TSTS (mpy[0])) { /* get abs (mpyer) */ DMOVN (mpy); } else mpy[1] = CLRS (mpy[1]); /* clear mpy lo sign */ AC(ac) = AC(p1) = AC(p2) = AC(p3) = 0; /* clear AC's */ if (((mpy[0] | mpy[1]) == 0) || ((mpc[0] | mpc[1]) == 0)) return; for (i = 0; i < 71; i++) { /* 71 mpyer bits */ if (i) { /* shift res, mpy */ AC(p3) = (AC(p3) >> 1) | ((AC(p2) & 1) << 34); AC(p2) = (AC(p2) >> 1) | ((AC(p1) & 1) << 34); AC(p1) = (AC(p1) >> 1) | ((AC(ac) & 1) << 34); AC(ac) = AC(ac) >> 1; mpy[1] = (mpy[1] >> 1) | ((mpy[0] & 1) << 34); mpy[0] = mpy[0] >> 1; } if (mpy[1] & 1) { /* if mpy lo bit = 1 */ AC(p1) = AC(p1) + mpc[1]; AC(ac) = AC(ac) + mpc[0] + (TSTS (AC(p1) != 0)); AC(p1) = CLRS (AC(p1)); } } if (TSTS (sign)) { /* result minus? */ AC(p3) = (-AC(p3)) & MMASK; /* quad negate */ AC(p2) = (~AC(p2) + (AC(p3) == 0)) & MMASK; AC(p1) = (~AC(p1) + (AC(p2) == 0)) & MMASK; AC(ac) = (~AC(ac) + (AC(p1) == 0)) & DMASK; } else if (TSTS (AC(ac))) /* wrong sign */ SETF (F_AOV | F_T1); if (TSTS (AC(ac))) { /* if result - */ AC(p1) = SETS (AC(p1)); /* make signs consistent */ AC(p2) = SETS (AC(p2)); AC(p3) = SETS (AC(p3)); } return; } /* Double precision divide - checked against KS10 ucode */ void ddiv (int32 ac, d10 *dvr) { int32 i, cryin; d10 sign, qu[2], dvd[4]; dvd[0] = AC(ac); /* save dividend */ for (i = 1; i < 4; i++) dvd[i] = CLRS (AC(ADDAC (ac, i))); sign = AC(ac) ^ dvr[0]; /* sign of result */ if (TSTS (AC(ac))) { /* get abs (dividend) */ for (i = 3, cryin = 1; i > 0; i--) { /* negate quad */ dvd[i] = (~dvd[i] + cryin) & MMASK; /* comp + carry in */ if (dvd[i]) /* next carry in */ cryin = 0; } dvd[0] = (~dvd[0] + cryin) & DMASK; } if (TSTS (dvr[0])) { /* get abs (divisor) */ DMOVN (dvr); } else dvr[1] = CLRS (dvr[1]); if (DCMPGE (dvd, dvr)) { /* will divide work? */ SETF (F_AOV | F_DCK | F_T1); /* no, set flags */ return; } qu[0] = qu[1] = 0; /* clear quotient */ for (i = 0; i < 70; i++) { /* 70 quotient bits */ dvd[0] = ((dvd[0] << 1) | ((dvd[1] >> 34) & 1)) & DMASK;; dvd[1] = ((dvd[1] << 1) | ((dvd[2] >> 34) & 1)) & MMASK; dvd[2] = ((dvd[2] << 1) | ((dvd[3] >> 34) & 1)) & MMASK; dvd[3] = (dvd[3] << 1) & MMASK; /* shift dividend */ qu[0] = (qu[0] << 1) | ((qu[1] >> 34) & 1); /* shift quotient */ qu[1] = (qu[1] << 1) & MMASK; if (DCMPGE (dvd, dvr)) { /* subtract work? */ dvd[0] = dvd[0] - dvr[0] - (dvd[1] < dvr[1]); dvd[1] = (dvd[1] - dvr[1]) & MMASK; /* do subtract */ qu[1] = qu[1] + 1; /* set quotient bit */ } } if (TSTS (sign) && (qu[0] | qu[1])) { MKDNEG (qu); } if (TSTS (AC(ac)) && (dvd[0] | dvd[1])) { MKDNEG (dvd); } AC(ac) = qu[0]; /* quotient */ AC(ADDAC(ac, 1)) = qu[1]; AC(ADDAC(ac, 2)) = dvd[0]; /* remainder */ AC(ADDAC(ac, 3)) = dvd[1]; return; } /* Single precision floating add/subtract - checked against KS10 ucode The KS10 shifts the smaller operand regardless of the exponent diff. This code will not shift more than 63 places; shifts beyond that cannot change the value of the smaller operand. If the signs of the operands are the same, the result sign is the same as the source sign; the sign of the result fraction is actually part of the data. If the signs of the operands are different, the result sign is determined by the fraction sign. */ d10 fad (d10 op1, d10 op2, t_bool rnd, int32 inv) { int32 ediff; UFP a, b, t; if (inv) /* subtract? -b */ op2 = NEG (op2); if (op1 == 0) /* a = 0? result is b */ funpack (op2, 0, &a, AFRC); else if (op2 == 0) /* b = 0? result is a */ funpack (op1, 0, &a, AFRC); else { funpack (op1, 0, &a, SFRC); /* unpack operands */ funpack (op2, 0, &b, SFRC); /* fracs are 2's comp */ ediff = a.exp - b.exp; /* get exp diff */ if (ediff < 0) { /* a < b? switch */ t = a; a = b; b = t; ediff = -ediff; } if (ediff > 63) /* cap diff at 63 */ ediff = 63; if (ediff) /* shift b (signed) */ b.fhi = (t_int64) b.fhi >> ediff; a.fhi = a.fhi + b.fhi; /* add fractions */ if (a.sign ^ b.sign) { /* add or subtract? */ if (a.fhi & FP_UCRY) { /* subtract, frac -? */ a.fhi = UNEG (a.fhi); /* complement result */ a.sign = 1; /* result is - */ } else a.sign = 0; /* result is + */ } else { if (a.sign) /* add, src -? comp */ a.fhi = UNEG (a.fhi); if (a.fhi & FP_UCRY) { /* check for carry */ a.fhi = a.fhi >> 1; /* flo won't be used */ a.exp = a.exp + 1; } } } fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ return fpack (&a, NULL, FALSE); } /* Single precision floating multiply. Because the fractions are 27b, a 64b multiply can be used for the fraction multiply. The 27b fractions are positioned 0'frac'0000, resulting in 00'hifrac'0..0. The extra 0 is accounted for by biasing the result exponent. */ #define FP_V_SPM (FP_V_UFHI - (32 - FP_N_FHI - 1)) d10 fmp (d10 op1, d10 op2, t_bool rnd) { UFP a, b; funpack (op1, 0, &a, AFRC); /* unpack operands */ funpack (op2, 0, &b, AFRC); /* fracs are abs val */ if ((a.fhi == 0) || (b.fhi == 0)) /* either 0? */ return 0; a.sign = a.sign ^ b.sign; /* result sign */ a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ a.fhi = (a.fhi >> FP_V_SPM) * (b.fhi >> FP_V_SPM); /* high 27b of result */ fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ return fpack (&a, NULL, FALSE); } /* Single precision floating divide. Because the fractions are 27b, a 64b divide can be used for the fraction divide. Note that 28b-29b of fraction are developed; the code will do one special normalize to make sure that the 28th bit is not lost. Also note the special treatment of negative quotients with non-zero remainders; this implements the note on p2-23 of the Processor Reference Manual. */ t_bool fdv (d10 op1, d10 op2, d10 *rs, t_bool rnd) { UFP a, b; t_uint64 savhi; t_bool rem = FALSE; funpack (op1, 0, &a, AFRC); /* unpack operands */ funpack (op2, 0, &b, AFRC); /* fracs are abs val */ if (a.fhi >= 2 * b.fhi) { /* will divide work? */ SETF (F_AOV | F_DCK | F_FOV | F_T1); return FALSE; } if (savhi = a.fhi) { /* dvd = 0? quo = 0 */ a.sign = a.sign ^ b.sign; /* result sign */ a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ a.fhi = a.fhi / (b.fhi >> (FP_N_FHI + 1)); /* do divide */ if (a.sign && (savhi != (a.fhi * (b.fhi >> (FP_N_FHI + 1))))) rem = TRUE; /* KL/KS hack */ a.fhi = a.fhi << (FP_V_UNORM - FP_N_FHI - 1); /* put quo in place */ if ((a.fhi & FP_UNORM) == 0) { /* normalize 1b */ a.fhi = a.fhi << 1; /* before masking */ a.exp = a.exp - 1; } a.fhi = a.fhi & (FP_UFHI | FP_URNDS); /* mask quo to 28b */ } fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ *rs = fpack (&a, NULL, rem); /* pack result */ return TRUE; } /* Single precision floating scale. */ d10 fsc (d10 val, a10 ea) { int32 sc = LIT8 (ea); UFP a; if (val == 0) return 0; funpack (val, 0, &a, AFRC); /* unpack operand */ if (ea & RSIGN) /* adjust exponent */ a.exp = a.exp - sc; else a.exp = a.exp + sc; fnorm (&a, 0); /* renormalize */ return fpack (&a, NULL, FALSE); /* pack result */ } /* Float integer operand and round */ d10 fltr (d10 mb) { UFP a; d10 val = ABS (mb); a.sign = GET_FPSIGN (mb); /* get sign */ a.exp = FP_BIAS + 36; /* initial exponent */ a.fhi = val << (FP_V_UNORM - 35); /* left justify op */ a.flo = 0; fnorm (&a, FP_URNDS); /* normalize, round */ return fpack (&a, NULL, FALSE); /* pack result */ } /* Fix and truncate/round floating operand */ void fix (int32 ac, d10 mb, t_bool rnd) { int32 sc; t_uint64 so; UFP a; funpack (mb, 0, &a, AFRC); /* unpack operand */ if (a.exp > (FP_BIAS + FP_N_FHI + FP_N_EXP)) SETF (F_AOV | F_T1); else if (a.exp < FP_BIAS) /* < 1/2? */ AC(ac) = 0; else { sc = FP_V_UNORM - (a.exp - FP_BIAS) + 1; AC(ac) = a.fhi >> sc; if (rnd) { so = a.fhi << (64 - sc); if (so >= (0x8000000000000000 + a.sign)) AC(ac) = AC(ac) + 1; } if (a.sign) AC(ac) = NEG (AC(ac)); } return; } /* Double precision floating add/subtract Since a.flo is 0, adding b.flo is just a copy - this is incorporated into the denormalization step. If there's no denormalization, bflo is zero too. */ void dfad (int32 ac, d10 *rs, int32 inv) { int32 p1 = ADDAC (ac, 1); int32 ediff; UFP a, b, t; if (inv) { /* subtract? -b */ DMOVN (rs); } if ((AC(ac) | AC(p1)) == 0) /* a == 0? sum = b */ funpack (rs[0], rs[1], &a, AFRC); else if ((rs[0] | rs[1]) == 0) /* b == 0? sum = a */ funpack (AC(ac), AC(p1), &a, AFRC); else { funpack (AC(ac), AC(p1), &a, SFRC); /* unpack operands */ funpack (rs[0], rs[1], &b, SFRC); ediff = a.exp - b.exp; /* get exp diff */ if (ediff < 0) { /* a < b? switch */ t = a; a = b; b = t; ediff = -ediff; } if (ediff > 127) /* cap diff at 127 */ ediff = 127; if (ediff > 63) { /* diff > 63? */ a.flo = (t_int64) b.fhi >> (ediff - 64); /* b hi to a lo */ b.fhi = b.sign? FP_ONES: 0; /* hi = all sign */ } else if (ediff) { /* diff <= 63 */ a.flo = (b.flo >> ediff) | (b.fhi << (64 - ediff)); b.fhi = (t_int64) b.fhi >> ediff; /* shift b (signed) */ } a.fhi = a.fhi + b.fhi; /* do add */ if (a.sign ^ b.sign) { /* add or subtract? */ if (a.fhi & FP_UCRY) { /* subtract, frac -? */ DUNEG (a); /* complement result */ a.sign = 1; /* result is - */ } else a.sign = 0; /* result is + */ } else { if (a.sign) { /* add, src -? comp */ DUNEG (a); }; if (a.fhi & FP_UCRY) { /* check for carry */ a.fhi = a.fhi >> 1; /* flo won't be used */ a.exp = a.exp + 1; } } } fnorm (&a, FP_URNDD); /* normalize, round */ AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ return; } /* Double precision floating multiply The 62b fractions are multiplied, with cross products, to produce a 124b fraction with two leading and two trailing 0's. Because the product has 2 leading 0's, instead of the normal 1, an extra normalization step is needed. Accordingly, the exponent calculation increments the result exponent, to compensate for normalization. */ void dfmp (int32 ac, d10 *rs) { int32 p1 = ADDAC (ac, 1); t_uint64 xh, xl, yh, yl, mid; UFP a, b; funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ funpack (rs[0], rs[1], &b, AFRC); if ((a.fhi == 0) || (b.fhi == 0)) { /* either 0? result 0 */ AC(ac) = AC(p1) = 0; return; } a.sign = a.sign ^ b.sign; /* result sign */ a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ xh = a.fhi >> 32; /* split 62b fracs */ xl = a.fhi & MSK32; /* into 32b halves */ yh = b.fhi >> 32; yl = b.fhi & MSK32; a.fhi = xh * yh; /* hi xproduct */ a.flo = xl * yl; /* low xproduct */ mid = (xh * yl) + (yh * xl); /* fits in 64b */ a.flo = a.flo + (mid << 32); /* add mid lo to lo */ a.fhi = a.fhi + ((mid >> 32) & MSK32) + (a.flo < (mid << 32)); fnorm (&a, FP_URNDD); /* normalize, round */ AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ return; } /* Double precision floating divide This algorithm develops a full 62 bits of quotient, plus one rounding bit, in the low order 63b of a 64b number. To do this, we must assure that the initial divide step generates a 1. If it would fail, shift the dividend left and decrement the result exponent accordingly. */ void dfdv (int32 ac, d10 *rs) { int32 p1 = ADDAC (ac, 1); int32 i; t_uint64 qu = 0; UFP a, b; funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ funpack (rs[0], rs[1], &b, AFRC); if (a.fhi >= 2 * b.fhi) { /* will divide work? */ SETF (F_AOV | F_DCK | F_FOV | F_T1); return; } if (a.fhi) { /* dvd = 0? quo = 0 */ a.sign = a.sign ^ b.sign; /* result sign */ a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ if (a.fhi < b.fhi) { /* make sure initial */ a.fhi = a.fhi << 1; /* divide step will work */ a.exp = a.exp - 1; } for (i = 0; i < 63; i++) { /* 63b of quotient */ qu = qu << 1; /* shift quotient */ if (a.fhi >= b.fhi) { /* will div work? */ a.fhi = a.fhi - b.fhi; /* sub, quo = 1 */ qu = qu + 1; } a.fhi = a.fhi << 1; /* shift dividend */ } a.fhi = qu; } fnorm (&a, FP_URNDD); /* normalize, round */ AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ return; } /* Unpack floating point operand */ void funpack (d10 h, d10 l, UFP *r, t_bool sgn) { d10 fphi, fplo; r->sign = GET_FPSIGN (h); r->exp = GET_FPEXP (h); fphi = GET_FPHI (h); fplo = GET_FPLO (l); r->fhi = (fphi << FP_V_UFHI) | (fplo << FP_V_UFLO); r->flo = 0; if (r->sign) { r->exp = r->exp ^ FP_M_EXP; /* 1s comp exp */ if (sgn) { /* signed frac? */ if (r->fhi) /* extend sign */ r->fhi = r->fhi | FP_UCRY; else { r->exp = r->exp + 1; r->fhi = FP_UCRY | FP_UNORM; } } else { /* abs frac */ if (r->fhi) r->fhi = UNEG (r->fhi) & FP_UFRAC; else { r->exp = r->exp + 1; r->fhi = FP_UNORM; } } } return; } /* Normalize and optionally round floating point operand */ void fnorm (UFP *a, t_int64 rnd) { int32 i; static t_uint64 normmask[6] = { 0x6000000000000000, 0x7800000000000000, 0x7F80000000000000, 0x7FFF800000000000, 0x7FFFFFFF80000000, 0x7FFFFFFFFFFFFFFF }; static int32 normtab[7] = { 1, 2, 4, 8, 16, 32, 63 }; extern a10 pager_PC; if (a->fhi & FP_UCRY) { /* carry set? */ printf ("%%PDP-10 FP: carry bit set at normalization, PC = %o\n", pager_PC); a->flo = (a->flo >> 1) | ((a->fhi & 1) << 63); /* try to recover */ a->fhi = a->fhi >> 1; /* but root cause */ a->exp = a->exp + 1; /* should be fixed! */ } if ((a->fhi | a->flo) == 0) { /* if fraction = 0 */ a->sign = a->exp = 0; /* result is 0 */ return; } while ((a->fhi & FP_UNORM) == 0) { /* normalized? */ for (i = 0; i < 6; i++) { if (a->fhi & normmask[i]) break; } a->fhi = (a->fhi << normtab[i]) | (a->flo >> (64 - normtab[i])); a->flo = a->flo << normtab[i]; a->exp = a->exp - normtab[i]; } if (rnd) { /* rounding? */ a->fhi = a->fhi + rnd; /* add round const */ if (a->fhi & FP_UCRY) { /* if carry out, */ a->fhi = a->fhi >> 1; /* renormalize */ a->exp = a->exp + 1; } } return; } /* Pack floating point result */ d10 fpack (UFP *r, d10 *lo, t_bool fdvneg) { d10 val[2]; if (r->exp < 0) SETF (F_AOV | F_FOV | F_FXU | F_T1); else if (r->exp > FP_M_EXP) SETF (F_AOV | F_FOV | F_T1); val[0] = (((((d10) r->exp) & FP_M_EXP) << FP_V_EXP) | ((r->fhi & FP_UFHI) >> FP_V_UFHI)) & DMASK; if (lo) val[1] = ((r->fhi & FP_UFLO) >> FP_V_UFLO) & MMASK; else val[1] = 0; if (r->sign) { /* negate? */ if (fdvneg) { /* fdvr special? */ val[1] = ~val[1] & MMASK; /* 1's comp */ val[0] = ~val[0] & DMASK; } else { /* 2's comp */ DMOVN (val); } } if (lo) *lo = val[1]; return val[0]; } simh-3.8.1/PDP10/pdp10_cpu.c0000644000175000017500000031554111112112750013364 0ustar vlmvlm/* pdp10_cpu.c: PDP-10 CPU simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu KS10 central processor 17-Jul-07 RMS Fixed non-portable usage in SHOW HISTORY 28-Apr-07 RMS Removed clock initialization 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) Fixed warning in MOVNI 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Nov-04 RMS Added instruction history 08-Oct-02 RMS Revised to build dib_tab dynamically Added SHOW IOSPACE 30-Dec-01 RMS Added old PC queue 25-Dec-01 RMS Cleaned up sim_inst declarations 07-Dec-01 RMS Revised to use new breakpoint package 21-Nov-01 RMS Implemented ITS 1-proceed hack 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 10-Aug-01 RMS Removed register in declarations 17-Jul-01 RMS Moved function prototype 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug 29-Apr-01 RMS Fixed modifier naming conflict Fixed XCTR/XCTRI, UMOVE/UMOVEM, BLTUB/BLTBU for ITS Added CLRCSH for ITS The 36b system family had six different implementions: PDP-6, KA10, KI10, L10, KL10 extended, and KS10. This simulator implements the KS10. The register state for the KS10 is: AC[8][16] accumulators PC program counter flags<0:11> state flags pi_enb<1:7> enabled PI levels pi_act<1:7> active PI levels pi_prq<1:7> program PI requests apr_enb<0:7> enabled system flags apr_flg<0:7> system flags ebr executive base register ubr user base register hsb halt status block address spt SPT base cst CST base pur process use register cstm CST mask The PDP-10 had just two instruction formats: memory reference and I/O. 000000000 0111 1 1111 112222222222333333 012345678 9012 3 4567 890123456789012345 +---------+----+-+----+------------------+ | opcode | ac |i| idx| address | memory reference +---------+----+-+----+------------------+ 000 0000000 111 1 1111 112222222222333333 012 3456789 012 3 4567 890123456789012345 +---+-------+---+-+----+------------------+ |111|device |iop|i| idx| address | I/O +---+-------+---+-+----+------------------+ This routine is the instruction decode routine for the PDP-10. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until an abort occurs. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction MUUO instruction in executive mode pager error in interrupt sequence invalid vector table in interrupt sequence illegal instruction in interrupt sequence breakpoint encountered nested indirects exceeding limit nested XCT's exceeding limit I/O error in I/O simulator 2. Interrupts. PDP-10's have a seven level priority interrupt system. Interrupt requests can come from internal sources, such as APR program requests, or external sources, such as I/O devices. The requests are stored in pi_prq for program requests, pi_apr for other internal flags, and pi_ioq for I/O device flags. Internal and device (but not program) interrupts must be enabled on a level by level basis. When an interrupt is granted on a level, interrupts at that level and below are masked until the interrupt is dismissed. The I/O device interrupt system is taken from the PDP-11. int_req stores the interrupt requests for Unibus I/O devices. Routines in the Unibus adapter map requests in int_req to PDP-10 levels. The Unibus adapter also calculates which device to get a vector from when a PDP-10 interrupt is granted. 3. Arithmetic. The PDP-10 is a 2's complement system. 4. Adding I/O devices. These modules must be modified: pdp10_defs.h add device address and interrupt definitions pdp10_sys.c add sim_devices table entry A note on ITS 1-proceed. The simulator follows the implementation on the KS10, keeping 1-proceed as a side flag (its_1pr) rather than as flags<8>. This simplifies the flag saving instructions, which don't have to clear flags<8> before saving it. Instead, the page fail and interrupt code must restore flags<8> from its_1pr. Unlike the KS10, the simulator will not lose the 1-proceed trap if the 1-proceeded instructions clears 1-proceed. */ #include "pdp10_defs.h" #include #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 HIST_PC 0x40000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { a10 pc; a10 ea; d10 ir; d10 ac; } InstHistory; d10 *M = NULL; /* memory */ d10 acs[AC_NBLK * AC_NUM] = { 0 }; /* AC blocks */ d10 *ac_cur, *ac_prv; /* AC cur, prv (dyn) */ a10 epta, upta; /* proc tbl addr (dyn) */ a10 saved_PC = 0; /* scp: saved PC */ d10 pager_word = 0; /* pager: error word */ a10 pager_PC = 0; /* pager: saved PC */ int32 pager_flags = 0; /* pager: trap flags */ t_bool pager_pi = FALSE; /* pager: in pi seq */ t_bool pager_tc = FALSE; /* pager: trap cycle */ d10 ebr = 0; /* exec base reg */ d10 ubr = 0; /* user base reg */ d10 hsb = 0; /* halt status block */ d10 spt = 0; /* TOPS20 paging regs */ d10 cst = 0; d10 pur = 0; d10 cstm = 0; a10 dbr1 = 0; /* ITS paging regs */ a10 dbr2 = 0; a10 dbr3 = 0; a10 dbr4 = 0; d10 pcst = 0; /* ITS PC sampling */ int32 pi_on = 0; /* pi system enable */ int32 pi_enb = 0; /* pi enabled levels */ int32 pi_act = 0; /* pi active levels */ int32 pi_ioq = 0; /* pi io requests */ int32 pi_apr = 0; /* pi apr requests */ int32 pi_prq = 0; /* pi prog requests */ int32 apr_enb = 0; /* apr enables */ int32 apr_flg = 0; /* apr flags */ int32 apr_lvl = 0; /* apr level */ int32 qintr = 0; /* interrupt pending */ int32 flags = 0; /* flags */ int32 its_1pr = 0; /* ITS 1-proceed */ int32 stop_op0 = 0; /* stop on 0 */ int32 rlog = 0; /* extend fixup log */ int32 ind_max = 32; /* nested ind limit */ int32 xct_max = 32; /* nested XCT limit */ int32 t20_idlelock = 0; /* TOPS-20 idle lock */ a10 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ jmp_buf save_env; int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ extern int32 sim_int_char; extern int32 sim_interval; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ /* Forward and external declarations */ 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); d10 adjsp (d10 val, a10 ea); void ibp (a10 ea, int32 pflgs); d10 ldb (a10 ea, int32 pflgs); void dpb (d10 val, a10 ea, int32 pflgs); void adjbp (int32 ac, a10 ea, int32 pflgs); d10 add (d10 val, d10 mb); d10 sub (d10 val, d10 mb); void dadd (int32 ac, d10 *rs); void dsub (int32 ac, d10 *rs); int32 jffo (d10 val); d10 lsh (d10 val, a10 ea); d10 rot (d10 val, a10 ea); d10 ash (d10 val, a10 ea); void lshc (int32 ac, a10 ea); void rotc (int32 ac, a10 ea); void ashc (int32 ac, a10 ea); void circ (int32 ac, a10 ea); void blt (int32 ac, a10 ea, int32 pflgs); void bltu (int32 ac, a10 ea, int32 pflgs, int dir); a10 calc_ea (d10 inst, int32 prv); a10 calc_ioea (d10 inst, int32 prv); d10 calc_jrstfea (d10 inst, int32 pflgs); void pi_dismiss (void); void set_newflags (d10 fl, t_bool jrst); extern t_bool aprid (a10 ea, int32 prv); t_bool wrpi (a10 ea, int32 prv); t_bool rdpi (a10 ea, int32 prv); t_bool czpi (a10 ea, int32 prv); t_bool copi (a10 ea, int32 prv); t_bool wrapr (a10 ea, int32 prv); t_bool rdapr (a10 ea, int32 prv); t_bool czapr (a10 ea, int32 prv); t_bool coapr (a10 ea, int32 prv); int32 pi_eval (void); int32 test_int (void); void set_ac_display (d10 *acbase); extern t_stat build_dib_tab (void); extern t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); extern d10 Read (a10 ea, int32 prv); /* read, read check */ extern d10 ReadM (a10 ea, int32 prv); /* read, write check */ extern d10 ReadE (a10 ea); /* read, exec */ extern d10 ReadP (a10 ea); /* read, physical */ extern void Write (a10 ea, d10 val, int32 prv); /* write */ extern void WriteE (a10 ea, d10 val); /* write, exec */ extern void WriteP (a10 ea, d10 val); /* write, physical */ extern t_bool AccViol (a10 ea, int32 prv, int32 mode); /* access check */ extern void set_dyn_ptrs (void); extern a10 conmap (a10 ea, int32 mode, int32 sw); extern void fe_intr (); extern void dfad (int32 ac, d10 *rs, int32 inv); extern void dfmp (int32 ac, d10 *rs); extern void dfdv (int32 ac, d10 *rs); extern void dmul (int32 ac, d10 *rs); extern void ddiv (int32 ac, d10 *rs); extern void fix (int32 ac, d10 mb, t_bool rnd); extern d10 fad (d10 val, d10 mb, t_bool rnd, int32 inv); extern d10 fmp (d10 val, d10 mb, t_bool rnd); extern t_bool fdv (d10 val, d10 mb, d10 *rs, t_bool rnd); extern d10 fsc (d10 val, a10 ea); extern d10 fltr (d10 mb); extern int xtend (int32 ac, a10 ea, int32 pflgs); extern void xtcln (int32 rlog); extern d10 map (a10 ea, int32 prv); extern d10 imul (d10 val, d10 mb); extern t_bool idiv (d10 val, d10 mb, d10 *rs); extern void mul (d10 val, d10 mb, d10 *rs); extern t_bool divi (int32 ac, d10 mb, d10 *rs); extern t_bool io710 (int32 ac, a10 ea); extern t_bool io711 (int32 ac, a10 ea); extern d10 io712 (a10 ea); extern void io713 (d10 val, a10 ea); extern void io714 (d10 val, a10 ea); extern void io715 (d10 val, a10 ea); extern t_bool io720 (int32 ac, a10 ea); extern t_bool io721 (int32 ac, a10 ea); extern d10 io722 (a10 ea); extern void io723 (d10 val, a10 ea); extern void io724 (d10 val, a10 ea); extern void io725 (d10 val, a10 ea); extern t_bool clrcsh (a10 ea, int32 prv); extern t_bool clrpt (a10 ea, int32 prv); extern t_bool wrubr (a10 ea, int32 prv); extern t_bool wrebr (a10 ea, int32 prv); extern t_bool wrhsb (a10 ea, int32 prv); extern t_bool wrspb (a10 ea, int32 prv); extern t_bool wrcsb (a10 ea, int32 prv); extern t_bool wrpur (a10 ea, int32 prv); extern t_bool wrcstm (a10 ea, int32 prv); extern t_bool ldbr1 (a10 ea, int32 prv); extern t_bool ldbr2 (a10 ea, int32 prv); extern t_bool ldbr3 (a10 ea, int32 prv); extern t_bool ldbr4 (a10 ea, int32 prv); extern t_bool rdubr (a10 ea, int32 prv); extern t_bool rdebr (a10 ea, int32 prv); extern t_bool rdhsb (a10 ea, int32 prv); extern t_bool rdspb (a10 ea, int32 prv); extern t_bool rdcsb (a10 ea, int32 prv); extern t_bool rdpur (a10 ea, int32 prv); extern t_bool rdcstm (a10 ea, int32 prv); extern t_bool sdbr1 (a10 ea, int32 prv); extern t_bool sdbr2 (a10 ea, int32 prv); extern t_bool sdbr3 (a10 ea, int32 prv); extern t_bool sdbr4 (a10 ea, int32 prv); extern t_bool rdtim (a10 ea, int32 prv); extern t_bool rdint (a10 ea, int32 prv); extern t_bool wrtim (a10 ea, int32 prv); extern t_bool wrint (a10 ea, int32 prv); extern t_bool rdpcst (a10 ea, int32 prv); extern t_bool wrpcst (a10 ea, int32 prv); extern t_bool spm (a10 ea, int32 prv); extern t_bool lpmr (a10 ea, int32 prv); extern int32 pi_ub_vec (int32 lvl, int32 *uba); extern t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc); /* 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, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, VASIZE) }, { ORDATA (FLAGS, flags, 18) }, { ORDATA (AC0, acs[0], 36) }, /* addr in memory */ { ORDATA (AC1, acs[1], 36) }, /* modified at exit */ { ORDATA (AC2, acs[2], 36) }, /* to SCP */ { ORDATA (AC3, acs[3], 36) }, { ORDATA (AC4, acs[4], 36) }, { ORDATA (AC5, acs[5], 36) }, { ORDATA (AC6, acs[6], 36) }, { ORDATA (AC7, acs[7], 36) }, { ORDATA (AC10, acs[10], 36) }, { ORDATA (AC11, acs[11], 36) }, { ORDATA (AC12, acs[12], 36) }, { ORDATA (AC13, acs[13], 36) }, { ORDATA (AC14, acs[14], 36) }, { ORDATA (AC15, acs[15], 36) }, { ORDATA (AC16, acs[16], 36) }, { ORDATA (AC17, acs[17], 36) }, { ORDATA (PFW, pager_word, 36) }, { ORDATA (EBR, ebr, EBR_N_EBR) }, { FLDATA (PGON, ebr, EBR_V_PGON) }, { FLDATA (T20P, ebr, EBR_V_T20P) }, { ORDATA (UBR, ubr, 36) }, { GRDATA (CURAC, ubr, 8, 3, UBR_V_CURAC), REG_RO }, { GRDATA (PRVAC, ubr, 8, 3, UBR_V_PRVAC) }, { ORDATA (SPT, spt, 36) }, { ORDATA (CST, cst, 36) }, { ORDATA (PUR, pur, 36) }, { ORDATA (CSTM, cstm, 36) }, { ORDATA (HSB, hsb, 36) }, { ORDATA (DBR1, dbr1, PASIZE) }, { ORDATA (DBR2, dbr2, PASIZE) }, { ORDATA (DBR3, dbr3, PASIZE) }, { ORDATA (DBR4, dbr4, PASIZE) }, { ORDATA (PCST, pcst, 36) }, { ORDATA (PIENB, pi_enb, 7) }, { FLDATA (PION, pi_on, 0) }, { ORDATA (PIACT, pi_act, 7) }, { ORDATA (PIPRQ, pi_prq, 7) }, { ORDATA (PIIOQ, pi_ioq, 7), REG_RO }, { ORDATA (PIAPR, pi_apr, 7), REG_RO }, { ORDATA (APRENB, apr_enb, 8) }, { ORDATA (APRFLG, apr_flg, 8) }, { ORDATA (APRLVL, apr_lvl, 3) }, { ORDATA (RLOG, rlog, 10) }, { FLDATA (F1PR, its_1pr, 0) }, { BRDATA (PCQ, pcq, 8, VASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { DRDATA (INDMAX, ind_max, 8), PV_LEFT + REG_NZ }, { DRDATA (XCTMAX, xct_max, 8), PV_LEFT + REG_NZ }, { ORDATA (WRU, sim_int_char, 8) }, { FLDATA (STOP_ILL, stop_op0, 0) }, { BRDATA (REG, acs, 8, 36, AC_NUM * AC_NBLK) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_KLAD+UNIT_ITS+UNIT_T20, 0, "TOPS-10", "TOPS-10", &tim_set_mod }, { UNIT_KLAD+UNIT_ITS+UNIT_T20, 0, NULL , "TOPS10", &tim_set_mod }, { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_T20, "TOPS-20", "TOPS-20", &tim_set_mod }, { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_T20, NULL, "TOPS20", &tim_set_mod }, { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_ITS, "ITS", "ITS", &tim_set_mod }, { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_KLAD, "diagnostic mode", "KLAD", &tim_set_mod }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, NULL, &show_iospace }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, PASIZE, 1, 8, 36, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; /* Data arrays */ const int32 pi_l2bit[8] = { 0, 0100, 0040, 0020, 0010, 0004, 0002, 0001 }; const int32 pi_m2lvl[128] = { 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; const d10 bytemask[64] = { 0, 01, 03, 07, 017, 037, 077, 0177, 0377, 0777, 01777, 03777, 07777, 017777, 037777, 077777, 0177777, 0377777, 0777777, 01777777, 03777777, 07777777, 017777777, 037777777, 077777777, 0177777777, 0377777777, 0777777777, 01777777777, 03777777777, 07777777777, 017777777777, 037777777777, 077777777777, 0177777777777, 0377777777777, 0777777777777, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES }; static t_bool (*io700d[16])() = { &aprid, NULL, NULL, NULL, &wrapr, &rdapr, &czapr, &coapr, NULL, NULL, NULL, NULL, &wrpi, &rdpi, &czpi, &copi }; static t_bool (*io701d[16])() = { NULL, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static t_bool (*io702d[16])() = { &rdspb, &rdcsb, &rdpur, &rdcstm, &rdtim, &rdint, &rdhsb, NULL, &wrspb, &wrcsb, &wrpur, &wrcstm, &wrtim, &wrint, &wrhsb, NULL }; #define io700i io700d static t_bool (*io701i[16])() = { &clrcsh, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, NULL, &rdpcst, NULL, &wrpcst, NULL, NULL, NULL, NULL }; static t_bool (*io702i[16])() = { &sdbr1, &sdbr2, &sdbr3, &sdbr4, &rdtim, &rdint, &rdhsb, &spm, &ldbr1, &ldbr2, &ldbr3, &ldbr4, &wrtim, &wrint, &wrhsb, &lpmr }; /* JRST classes and validation table */ #define JRST_U 1 /* ok anywhere */ #define JRST_E 2 /* ok exec mode */ #define JRST_UIO 3 /* ok user I/O mode */ static t_stat jrst_tab[16] = { JRST_U, JRST_U, JRST_U, 0, JRST_E, JRST_U, JRST_E, JRST_E, JRST_UIO, 0, JRST_UIO, 0, JRST_E, JRST_U, 0, 0 }; /* Address operations */ #define IM ((d10) ea) #define IMS (((d10) ea) << 18) #define JUMP(x) PCQ_ENTRY, PC = ((a10) (x)) & AMASK #define SUBJ(x) CLRF (F_AFI | F_FPD | F_TR); JUMP (x) #define INCPC PC = INCA (PC) /* AC operations */ #define AOBAC AC(ac) = AOB (AC(ac)) #define SOBAC AC(ac) = SOB (AC(ac)) #define G2AC rs[0] = AC(ac), rs[1] = AC(P1) #define S1AC AC(ac) = rs[0] #define S2AC S1AC, AC(P1) = rs[1] #define LAC if (ac) AC(ac) = mb /* Memory operations */ #define RD mb = Read (ea, MM_OPND) #define RDAC AC(ac) = Read (ea, MM_OPND) #define RM mb = ReadM (ea, MM_OPND) #define RMAC AC(ac) = ReadM (ea, MM_OPND) #define RDP mb = Read (((a10) AC(ac)) & AMASK, MM_BSTK) #define RD2 rs[0] = Read (ea, MM_OPND); \ rs[1] = Read (INCA (ea), MM_OPND) #define WR Write (ea, mb, MM_OPND) #define WRAC Write (ea, AC(ac), MM_OPND) #define WRP(x) Write (((a10) INCA (AC(ac))), (x), MM_BSTK) #define WR1 Write (ea, rs[0], MM_OPND) #define WR2 ReadM (INCA (ea), MM_OPND); \ Write (ea, rs[0], MM_OPND); \ Write (INCA (ea), rs[1], MM_OPND) /* Tests and compares */ #define TL(a) (TSTS (a) != 0) #define TE(a) ((a) == 0) #define TLE(a) (TL (a) || TE (a)) #define TGE(a) (TSTS (a) == 0) #define TN(a) ((a) != 0) #define TG(a) (TGE (a) && TN (a)) #define CL(a) ((TSTS (AC(ac) ^ a))? (a < AC(ac)): (AC(ac) < a)) #define CE(a) (AC(ac) == a) #define CLE(a) (CL (a) || CE (a)) #define CGE(a) (!CL (a)) #define CN(a) (AC(ac) != a) #define CG(a) (CGE (a) && CN (a)) /* Word assemblies */ #define FLPC XWD (flags, PC) #define UUOWORD (((d10) op) << INST_V_OP) | (((d10) ac) << INST_V_AC) | ea #define APRHWORD ((apr_flg << APR_V_FLG) | (apr_lvl & APR_M_LVL) | \ ((apr_flg & apr_enb)? APR_IRQ: 0)) #define APRWORD ((apr_enb << (APR_V_FLG + 18)) | APRHWORD) #define PIHWORD ((pi_act << PI_V_ACT) | (pi_on << PI_V_ON) | \ (pi_enb << PI_V_ENB)) #define PIWORD ((pi_prq << PI_V_PRQ) | PIHWORD) /* Instruction operations */ #define CIBP if (!TSTF (F_FPD)) { ibp (ea, pflgs); SETF (F_FPD); } #define LDB AC(ac) = ldb (ea, pflgs) #define DPB dpb (AC(ac), ea, pflgs) #define FAD(s) fad (AC(ac), s, FALSE, 0) #define FADR(s) fad (AC(ac), s, TRUE, 0) #define FSB(s) fad (AC(ac), s, FALSE, 1) #define FSBR(s) fad (AC(ac), s, TRUE, 1) #define FMP(s) fmp (AC(ac), s, FALSE) #define FMPR(s) fmp (AC(ac), s, TRUE) #define FDV(s) fdv (AC(ac), s, rs, FALSE) #define FDVR(s) fdv (AC(ac), s, rs, TRUE) #define MOVN(s) NEG (s); MOVNF(s) #define MOVM(s) ABS (s); MOVMF(s) #define ADD(s) add (AC(ac), s) #define SUB(s) sub (AC(ac), s) #define IMUL(s) imul (AC(ac), s) #define IDIV(s) idiv (AC(ac), s, rs) #define MUL(s) mul (AC(ac), s, rs) #define DIV(s) divi (ac, s, rs) #define AOJ AC(ac) = INC (AC(ac)); INCF (AC(ac)) #define AOS RM; mb = INC (mb); WR; INCF (mb); LAC #define SOJ AC(ac) = DEC (AC(ac)); DECF (AC(ac)) #define SOS RM; mb = DEC (mb); WR; DECF (mb); LAC #define SETCA(s) ~AC(ac) & DMASK #define SETCM(s) ~(s) & DMASK; #define AND(s) AC(ac) & (s) #define ANDCA(s) ~AC(ac) & (s) #define ANDCM(s) AC(ac) & ~(s) #define ANDCB(s) (~AC(ac) & ~(s)) & DMASK #define IOR(s) AC(ac) | (s) #define ORCA(s) (~AC(ac) | (s)) & DMASK #define ORCM(s) (AC(ac) | ~(s)) & DMASK #define ORCB(s) (~AC(ac) | ~(s)) & DMASK #define XOR(s) AC(ac) ^ (s) #define EQV(s) (~(AC(ac) ^ (s))) & DMASK #define LL(s,d) ((s) & LMASK) | ((d) & RMASK) #define RL(s,d) (((s) << 18) & LMASK) | ((d) & RMASK) #define RR(s,d) ((s) & RMASK) | ((d) & LMASK) #define LR(s,d) (((s) >> 18) & RMASK) | ((d) & LMASK) #define LLO(s) ((s) & LMASK) | RMASK #define RLO(s) (((s) << 18) & LMASK) | RMASK #define RRO(s) ((s) & RMASK) | LMASK #define LRO(s) (((s) >> 18) & RMASK) | LMASK #define LLE(s) ((s) & LMASK) | (((s) & LSIGN)? RMASK: 0) #define RLE(s) (((s) << 18) & LMASK) | (((s) & RSIGN)? RMASK: 0) #define RRE(s) ((s) & RMASK) | (((s) & RSIGN)? LMASK: 0) #define LRE(s) (((s) >> 18) & RMASK) | (((s) & LSIGN)? LMASK: 0) #define TD_ RD #define TS_ RD; mb = SWP (mb) #define TL_ mb = IMS #define TR_ mb = IM #define T_Z AC(ac) = AC(ac) & ~mb #define T_O AC(ac) = AC(ac) | mb #define T_C AC(ac) = AC(ac) ^ mb #define T__E if ((AC(ac) & mb) == 0) INCPC #define T__N if ((AC(ac) & mb) != 0) INCPC #define T__A INCPC #define IOC if (TSTF (F_USR) && !TSTF (F_UIO)) goto MUUO; #define IO7(x,y) IOC; fptr = ((Q_ITS)? x[ac]: y[ac]); \ if (fptr == NULL) goto MUUO; \ if (fptr (ea, MM_OPND)) INCPC; break; #define IOA IOC; if (!Q_ITS) ea = calc_ioea (inst, pflgs) #define IOAM IOC; ea = ((Q_ITS)? ((a10) Read (ea, MM_OPND)): \ calc_ioea (inst, pflgs)) /* Flag tests */ #define MOVNF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1); \ else if ((x) == 0) SETF (F_C0 | F_C1) #define MOVMF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) #define INCF(x) if ((x) == 0) SETF (F_C0 | F_C1); \ else if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) #define DECF(x) if ((x) == MAXPOS) SETF (F_C0 | F_AOV | F_T1); \ else if ((x) != ONES) SETF (F_C0 | F_C1) #define PUSHF if (LRZ (AC(ac)) == 0) SETF (F_T2) #define POPF if (LRZ (AC(ac)) == RMASK) SETF (F_T2) #define DMOVNF if (rs[1] == 0) { MOVNF (rs[0]); } t_stat sim_instr (void) { a10 PC; /* set by setjmp */ int abortval = 0; /* abort value */ t_stat r; /* Restore register state */ if ((r = build_dib_tab ()) != SCPE_OK) /* build, chk dib_tab */ return r; pager_PC = PC = saved_PC & AMASK; /* load local PC */ set_dyn_ptrs (); /* set up local ptrs */ pager_tc = FALSE; /* not in trap cycle */ pager_pi = FALSE; /* not in pi sequence */ rlog = 0; /* not in extend */ pi_eval (); /* eval pi system */ if (!Q_ITS) /* ~ITS, clr 1-proc */ its_1pr = 0; t20_idlelock = 0; /* clr T20 idle lock */ /* Abort handling Aborts may come from within the simulator to stop simulation (values > 0), for page fails (values < 0), or for an interrupt check (value = 0). */ abortval = setjmp (save_env); /* set abort hdlr */ if ((abortval > 0) || pager_pi) { /* stop or pi err? */ if (pager_pi && (abortval == PAGE_FAIL)) abortval = STOP_PAGINT; /* stop for pi err */ saved_PC = pager_PC & AMASK; /* failing instr PC */ set_ac_display (ac_cur); /* set up AC display */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return abortval; /* return to SCP */ } /* Page fail - checked against KS10 ucode All state variables MUST be declared global for GCC optimization to work */ else if (abortval == PAGE_FAIL) { /* page fail */ d10 mb; if (rlog) /* clean up extend */ xtcln (rlog); rlog = 0; /* clear log */ if (pager_tc) /* trap? get flags */ flags = pager_flags; if (T20PAG) { /* TOPS-20 paging? */ WriteP (upta + UPT_T20_PFL, pager_word); /* write page fail wd */ WriteP (upta + UPT_T20_OFL, XWD (flags, 0)); WriteP (upta + UPT_T20_OPC, pager_PC); mb = ReadP (upta + UPT_T20_NPC); } else { a10 ea; /* TOPS-10 or ITS */ if (Q_ITS) { /* ITS? */ ea = epta + EPT_ITS_PAG + (pi_m2lvl[pi_act] * 3); if (its_1pr) /* store 1-proc */ flags = flags | F_1PR; its_1pr = 0; /* clear 1-proc */ } else ea = upta + UPT_T10_PAG; WriteP (ea, pager_word); /* write page fail wd */ WriteP (ADDA (ea, 1), XWD (flags, pager_PC)); mb = ReadP (ADDA (ea, 2)); } JUMP (mb); /* set new PC */ set_newflags (mb, FALSE); /* set new flags */ pi_eval (); /* eval pi system */ } else PC = pager_PC; /* intr, restore PC */ /* Main instruction fetch/decode loop: check clock queue, intr, trap, bkpt */ for ( ;; ) { /* loop until ABORT */ int32 op, ac, i, st, xr, xct_cnt, its_2pr, pflgs; a10 ea; d10 inst, mb, indrct, rs[2]; t_bool (*fptr)(); pager_PC = PC; /* update pager PC */ pager_tc = FALSE; /* not in trap cycle */ pflgs = 0; /* not in PXCT */ xct_cnt = 0; /* count XCT's */ if (sim_interval <= 0) { /* check clock queue */ if (i = sim_process_event ()) /* error? stop sim */ ABORT (i); pi_eval (); /* eval pi system */ } /* PI interrupt (Unibus or system flags). On the KS10, only JSR and XPCW are allowed as interrupt instructions. Because of exec mode addressing, and unconditional processing of flags, they are explicitly emulated here. */ if (qintr) { int32 vec, uba; pager_pi = TRUE; /* flag in pi seq */ if (vec = pi_ub_vec (qintr, &uba)) { /* Unibus interrupt? */ mb = ReadP (epta + EPT_UBIT + uba); /* get dispatch table */ if (mb == 0) /* invalid? stop */ ABORT (STOP_ZERINT); inst = ReadE ((((a10) mb) + (vec / 4)) & AMASK); if (inst == 0) ABORT (STOP_ZERINT); } else inst = ReadP (epta + EPT_PIIT + (2 * qintr)); op = GET_OP (inst); /* get opcode */ ac = GET_AC (inst); /* get ac */ if (its_1pr && Q_ITS) { /* 1-proc set? */ flags = flags | F_1PR; /* store 1-proc */ its_1pr = 0; /* clear 1-proc */ } if (op == OP_JSR) { /* JSR? */ ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ WriteE (ea, FLPC); /* save flags+PC, exec */ JUMP (INCA (ea)); /* PC = ea + 1 */ set_newflags (0, FALSE); /* set new flags */ } else if ((op == OP_JRST) && (ac == AC_XPCW)) { /* XPCW? */ ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ WriteE (ea, XWD (flags, 0)); /* write flags, exec */ WriteE (ADDA (ea, 1), PC); /* write PC, exec */ rs[0] = ReadE (ADDA (ea, 2)); /* read new flags */ rs[1] = ReadE (ADDA (ea, 3)); /* read new PC */ JUMP (rs[1]); /* set new PC */ set_newflags (rs[0], FALSE); /* set new flags */ } else ABORT (STOP_ILLINT); /* invalid instr */ pi_act = pi_act | pi_l2bit[qintr]; /* set level active */ pi_eval (); /* eval pi system */ pager_pi = FALSE; /* end of sequence */ if (sim_interval) /* charge for instr */ sim_interval--; continue; } /* end if interrupt */ /* Traps fetch and execute an instruction from the current mode process table. On the KS10, the fetch of the next instruction has started, and a page fail trap on the instruction fetch takes precedence over the trap. During a trap, flags are cleared before the execute, but if the execute aborts, they must be restored. Also, the MUUO processor needs to know whether we are in a trap sequence. Hence, trap in progress is recorded in pflgs, and the traps for pager restoration are recorded in pager_flags. */ if (TSTF (F_T1 | F_T2) && PAGING) { Read (pager_PC = PC, MM_CUR); /* test fetch */ pager_tc = TRUE; /* in a trap sequence */ pager_flags = flags; /* save flags */ ea = (TSTF (F_USR)? upta + UPT_TRBASE: epta + EPT_TRBASE) + GET_TRAPS (flags); inst = ReadP (ea); /* get trap instr */ CLRF (F_T1 | F_T2); /* clear flags */ } /* Test for instruction breakpoint */ else { if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ ABORT (STOP_IBKPT); /* stop simulation */ } /* Ready (at last) to get an instruction */ inst = Read (pager_PC = PC, MM_CUR); /* get instruction */ INCPC; sim_interval = sim_interval - 1; } its_2pr = its_1pr; /* save 1-proc flag */ /* Execute instruction. XCT and PXCT also return here. */ XCT: op = GET_OP (inst); /* get opcode */ ac = GET_AC (inst); /* get AC */ for (indrct = inst, i = 0; i < ind_max; i++) { /* calc eff addr */ ea = GET_ADDR (indrct); xr = GET_XR (indrct); if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; if (TST_IND (indrct)) indrct = Read (ea, MM_EA); else break; } if (i >= ind_max) ABORT (STOP_IND); /* too many ind? stop */ if (hst_lnt) { /* history enabled? */ hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = pager_PC | HIST_PC; hst[hst_p].ea = ea; hst[hst_p].ir = inst; hst[hst_p].ac = AC(ac); } switch (op) { /* case on opcode */ /* UUO's (0000 - 0077) - checked against KS10 ucode */ case 0000: if (stop_op0) { ABORT (STOP_ILLEG); } goto MUUO; case 0001: /* local UUO's */ case 0002: case 0003: case 0004: case 0005: case 0006: case 0007: case 0010: case 0011: case 0012: case 0013: case 0014: case 0015: case 0016: case 0017: case 0020: case 0021: case 0022: case 0023: case 0024: case 0025: case 0026: case 0027: case 0030: case 0031: case 0032: case 0033: case 0034: case 0035: case 0036: case 0037: Write (040, UUOWORD, MM_CUR); /* store op, ac, ea */ inst = Read (041, MM_CUR); /* get new instr */ goto XCT; /* case 0040 - 0077: MUUO's, handled by default at end of case */ /* Floating point, bytes, multiple precision (0100 - 0177) */ /* case 0100: MUUO /* UJEN */ /* case 0101: MUUO /* unassigned */ case 0102: if (Q_ITS && !TSTF (F_USR)) { /* GFAD (KL), XCTRI (ITS) */ inst = Read (ea, MM_OPND); pflgs = pflgs | ac; goto XCT; } goto MUUO; case 0103: if (Q_ITS && !TSTF (F_USR)) { /* GFSB (KL), XCTR (ITS) */ inst = Read (ea, MM_OPND); pflgs = pflgs | ac; goto XCT; } goto MUUO; /* case 0104: MUUO /* JSYS (T20) */ case 0105: AC(ac) = adjsp (AC(ac), ea); break; /* ADJSP */ /* case 0106: MUUO /* GFMP (KL)*/ /* case 0107: MUUO /* GFDV (KL) */ case 0110: RD2; dfad (ac, rs, 0); break; /* DFAD */ case 0111: RD2; dfad (ac, rs, 1); break; /* DFSB */ case 0112: RD2; dfmp (ac, rs); break; /* DFMP */ case 0113: RD2; dfdv (ac, rs); break; /* DFDV */ case 0114: RD2; dadd (ac, rs); break; /* DADD */ case 0115: RD2; dsub (ac, rs); break; /* DSUB */ case 0116: RD2; dmul (ac, rs); break; /* DMUL */ case 0117: RD2; ddiv (ac, rs); break; /* DDIV */ case 0120: RD2; S2AC; break; /* DMOVE */ case 0121: RD2; DMOVN (rs); S2AC; DMOVNF; break; /* DMOVN */ case 0122: RD; fix(ac, mb, 0); break; /* FIX */ case 0123: st = xtend (ac, ea, pflgs); /* EXTEND */ rlog = 0; /* clear log */ switch (st) { case XT_SKIP: INCPC; case XT_NOSK: break; default: goto MUUO; } break; case 0124: G2AC; WR2; break; /* DMOVEM */ case 0125: G2AC; DMOVN (rs); WR2; DMOVNF; break; /* DMOVNM */ case 0126: RD; fix (ac, mb, 1); break; /* FIXR */ case 0127: RD; AC(ac) = fltr (mb); break; /* FLTR */ /* case 0130: MUUO /* UFA */ /* case 0131: MUUO /* DFN */ case 0132: AC(ac) = fsc (AC(ac), ea); break; /* FSC */ case 0133: if (!ac) /* IBP */ ibp (ea, pflgs); else adjbp (ac, ea, pflgs); break; case 0134: CIBP; LDB; CLRF (F_FPD); break; /* ILBP */ case 0135: LDB; break; /* LDB */ case 0136: CIBP; DPB; CLRF (F_FPD); break; /* IDBP */ case 0137: DPB; break; /* DPB */ case 0140: RD; AC(ac) = FAD (mb); break; /* FAD */ /* case 0141: MUUO /* FADL */ case 0142: RM; mb = FAD (mb); WR; break; /* FADM */ case 0143: RM; AC(ac) = FAD (mb); WRAC; break; /* FADB */ case 0144: RD; AC(ac) = FADR (mb); break; /* FADR */ case 0145: AC(ac) = FADR (IMS); break; /* FADRI */ case 0146: RM; mb = FADR (mb); WR; break; /* FADRM */ case 0147: RM; AC(ac) = FADR (mb); WRAC; break; /* FADRB */ case 0150: RD; AC(ac) = FSB (mb); break; /* FSB */ /* case 0151: MUUO /* FSBL */ case 0152: RM; mb = FSB (mb); WR; break; /* FSBM */ case 0153: RM; AC(ac) = FSB (mb); WRAC; break; /* FSBB */ case 0154: RD; AC(ac) = FSBR (mb); break; /* FSBR */ case 0155: AC(ac) = FSBR (IMS); break; /* FSBRI */ case 0156: RM; mb = FSBR (mb); WR; break; /* FSBRM */ case 0157: RM; AC(ac) = FSBR (mb); WRAC; break; /* FSBRB */ case 0160: RD; AC(ac) = FMP (mb); break; /* FMP */ /* case 0161: MUUO /* FMPL */ case 0162: RM; mb = FMP (mb); WR; break; /* FMPM */ case 0163: RM; AC(ac) = FMP (mb); WRAC; break; /* FMPB */ case 0164: RD; AC(ac) = FMPR (mb); break; /* FMPR */ case 0165: AC(ac) = FMPR (IMS); break; /* FMPRI */ case 0166: RM; mb = FMPR (mb); WR; break; /* FMPRM */ case 0167: RM; AC(ac) = FMPR (mb); WRAC; break; /* FMPRB */ case 0170: RD; if (FDV (mb)) S1AC; break; /* FDV */ /* case 0171: MUUO /* FDVL */ case 0172: RM; if (FDV (mb)) WR1; break; /* FDVM */ case 0173: RM; if (FDV (mb)) { S1AC; WRAC; } break; /* FDVB */ case 0174: RD; if (FDVR (mb)) S1AC; break; /* FDVR */ case 0175: if (FDVR (IMS)) S1AC; break; /* FDVRI */ case 0176: RM; if (FDVR (mb)) WR1; break; /* FDVRM */ case 0177: RM; if (FDVR (mb)) { S1AC; WRAC; } break; /* FDVRB */ /* Move, arithmetic, shift, and jump (0200 - 0277) Note that instructions which modify the flags and store a result in memory must prove the writeability of the result location before modifying the flags. Also, 0247 and 0257, if not implemented, are nops, not MUUO's. */ case 0200: RDAC; break; /* MOVE */ case 0201: AC(ac) = ea; break; /* MOVEI */ case 0202: WRAC; break; /* MOVEM */ case 0203: RM; LAC; break; /* MOVES */ case 0204: RD; AC(ac) = SWP (mb); break; /* MOVS */ case 0205: AC(ac) = IMS; break; /* MOVSI */ case 0206: mb = SWP (AC(ac)); WR; break; /* MOVSM */ case 0207: RM; mb = SWP (mb); WR; LAC; break; /* MOVSS */ case 0210: RD; AC(ac) = MOVN (mb); break; /* MOVN */ case 0211: AC(ac) = NEG (IM); /* MOVNI */ if (AC(ac) == 0) SETF (F_C0 | F_C1); break; case 0212: RM; mb = MOVN (AC(ac)); WR; break; /* MOVNM */ case 0213: RM; mb = MOVN (mb); WR; LAC; break; /* MOVNS */ case 0214: RD; AC(ac) = MOVM (mb); break; /* MOVM */ case 0215: AC(ac) = ea; break; /* MOVMI */ case 0216: RM; mb = MOVM (AC(ac)); WR; break; /* MOVMM */ case 0217: RM; mb = MOVM (mb); WR; LAC; break; /* MOVMS */ case 0220: RD; AC(ac) = IMUL (mb); break; /* IMUL */ case 0221: AC(ac) = IMUL (IM); break; /* IMULI */ case 0222: RM; mb = IMUL (mb); WR; break; /* IMULM */ case 0223: RM; AC(ac) = IMUL (mb); WRAC; break; /* IMULB */ case 0224: RD; MUL (mb); S2AC; break; /* MUL */ case 0225: MUL (IM); S2AC; break; /* MULI */ case 0226: RM; MUL (mb); WR1; break; /* MULM */ case 0227: RM; MUL (mb); WR1; S2AC; break; /* MULB */ case 0230: RD; if (IDIV (mb)) S2AC; break; /* IDIV */ case 0231: if (IDIV (IM)) S2AC; break; /* IDIVI */ case 0232: RM; if (IDIV (mb)) WR1; break; /* IDIVM */ case 0233: RM; if (IDIV (mb)) { WR1; S2AC; } break; /* IDIVB */ case 0234: RD; if (DIV (mb)) S2AC; break; /* DIV */ case 0235: if (DIV (IM)) S2AC; break; /* DIVI */ case 0236: RM; if (DIV (mb)) WR1; break; /* DIVM */ case 0237: RM; if (DIV (mb)) { WR1; S2AC; } break; /* DIVB */ case 0240: AC(ac) = ash (AC(ac), ea); break; /* ASH */ case 0241: AC(ac) = rot (AC(ac), ea); break; /* ROT */ case 0242: AC(ac) = lsh (AC(ac), ea); break; /* LSH */ case 0243: AC(P1) = jffo (AC(ac)); /* JFFO */ if (AC(ac)) JUMP (ea); break; case 0244: ashc (ac, ea); break; /* ASHC */ case 0245: rotc (ac, ea); break; /* ROTC */ case 0246: lshc (ac, ea); break; /* LSHC */ case 0247: if (Q_ITS) circ (ac, ea); break; /* (ITS) CIRC */ case 0250: RM; WRAC; AC(ac) = mb; break; /* EXCH */ case 0251: blt (ac, ea, pflgs); break; /* BLT */ case 0252: AOBAC; if (TGE (AC(ac))) JUMP (ea); break; /* AOBJP */ case 0253: AOBAC; if (TL (AC(ac))) JUMP (ea); break; /* AOBJN */ /* case 0254: /* shown later /* JRST */ case 0255: if (flags & (ac << 14)) { /* JFCL */ JUMP (ea); CLRF (ac << 14); } break; case 0256: if (xct_cnt++ >= xct_max) /* XCT */ ABORT (STOP_XCT); inst = Read (ea, MM_OPND); if (ac && !TSTF (F_USR) && !Q_ITS) pflgs = pflgs | ac; goto XCT; case 0257: if (Q_ITS) goto MUUO; /* MAP */ AC(ac) = map (ea, MM_OPND); break; case 0260: WRP (FLPC); AOBAC; /* PUSHJ */ SUBJ (ea); PUSHF; break; case 0261: RD; WRP (mb); AOBAC; PUSHF; break; /* PUSH */ case 0262: RDP; WR; SOBAC; POPF; break; /* POP */ case 0263: RDP; JUMP (mb); SOBAC; POPF; break; /* POPJ */ case 0264: Write (ea, FLPC, MM_OPND); /* JSR */ SUBJ (INCR (ea)); break; case 0265: AC(ac) = FLPC; SUBJ (ea); break; /* JSP */ case 0266: WRAC; AC(ac) = XWD (ea, PC); /* JSA */ JUMP (INCR (ea)); break; case 0267: AC(ac) = Read ((a10) LRZ (AC(ac)), MM_OPND);/* JRA */ JUMP (ea); break; case 0270: RD; AC(ac) = ADD (mb); break; /* ADD */ case 0271: AC(ac) = ADD (IM); break; /* ADDI */ case 0272: RM; mb = ADD (mb); WR; break; /* ADDM */ case 0273: RM; AC(ac) = ADD (mb); WRAC; break; /* ADDB */ case 0274: RD; AC(ac) = SUB (mb); break; /* SUB */ case 0275: AC(ac) = SUB (IM); break; /* SUBI */ case 0276: RM; mb = SUB (mb); WR; break; /* SUBM */ case 0277: RM; AC(ac) = SUB (mb); WRAC; break; /* SUBB */ /* Compare, jump, skip instructions (0300 - 0377) - checked against KS10 ucode */ case 0300: break; /* CAI */ case 0301: if (CL (IM)) INCPC; break; /* CAIL */ case 0302: if (CE (IM)) INCPC; break; /* CAIE */ case 0303: if (CLE (IM)) INCPC; break; /* CAILE */ case 0304: INCPC; break; /* CAIA */ case 0305: if (CGE (IM)) INCPC; break; /* CAIGE */ case 0306: if (CN (IM)) INCPC; break; /* CAIN */ case 0307: if (CG (IM)) INCPC; break; /* CAIG */ case 0310: RD; break; /* CAM */ case 0311: RD; if (CL (mb)) INCPC; break; /* CAML */ case 0312: RD; if (CE (mb)) INCPC; break; /* CAME */ case 0313: RD; if (CLE (mb)) INCPC; break; /* CAMLE */ case 0314: RD; INCPC; break; /* CAMA */ case 0315: RD; if (CGE (mb)) INCPC; break; /* CAMGE */ case 0316: RD; if (CN (mb)) INCPC; break; /* CAMN */ case 0317: RD; if (CG (mb)) INCPC; break; /* CAMG */ case 0320: break; /* JUMP */ case 0321: if (TL (AC(ac))) JUMP (ea); break; /* JUMPL */ case 0322: if (TE (AC(ac))) JUMP (ea); break; /* JUMPE */ case 0323: if (TLE( AC(ac))) JUMP (ea); break; /* JUMPLE */ case 0324: JUMP (ea); break; /* JUMPA */ case 0325: if (TGE (AC(ac))) JUMP (ea); break; /* JUMPGE */ case 0326: if (TN (AC(ac))) JUMP (ea); break; /* JUMPN */ case 0327: if (TG (AC(ac))) JUMP (ea); break; /* JUMPG */ case 0330: RD; LAC; break; /* SKIP */ case 0331: RD; LAC; if (TL (mb)) INCPC; break; /* SKIPL */ case 0332: RD; LAC; if (TE (mb)) INCPC; break; /* SKIPE */ case 0333: RD; LAC; if (TLE (mb)) INCPC; break; /* SKIPLE */ case 0334: RD; LAC; INCPC; break; /* SKIPA */ case 0335: RD; LAC; if (TGE (mb)) INCPC; break; /* SKIPGE */ case 0336: RD; LAC; if (TN (mb)) INCPC; break; /* SKIPN */ case 0337: RD; LAC; if (TG (mb)) INCPC; break; /* SKIPG */ case 0340: AOJ; break; /* AOJ */ case 0341: AOJ; if (TL (AC(ac))) JUMP (ea); break; /* AOJL */ case 0342: AOJ; if (TE (AC(ac))) JUMP (ea); break; /* AOJE */ case 0343: AOJ; if (TLE (AC(ac))) JUMP (ea); break; /* AOJLE */ case 0344: AOJ; JUMP(ea); /* AOJA */ if (Q_ITS && Q_IDLE && /* ITS idle? */ TSTF (F_USR) && (pager_PC == 017) && /* user mode, loc 17? */ (ac == 0) && (ea == 017)) /* AOJA 0,17? */ sim_idle (0, FALSE); break; case 0345: AOJ; if (TGE (AC(ac))) JUMP (ea); break; /* AOJGE */ case 0346: AOJ; if (TN (AC(ac))) JUMP (ea); break; /* AOJN */ case 0347: AOJ; if (TG (AC(ac))) JUMP (ea); break; /* AOJG */ case 0350: AOS; break; /* AOS */ case 0351: AOS; if (TL (mb)) INCPC; break; /* AOSL */ case 0352: AOS; if (TE (mb)) INCPC; break; /* AOSE */ case 0353: AOS; if (TLE (mb)) INCPC; break; /* AOSLE */ case 0354: AOS; INCPC; break; /* AOSA */ case 0355: AOS; if (TGE (mb)) INCPC; break; /* AOSGE */ case 0356: AOS; if (TN (mb)) INCPC; break; /* AOSN */ case 0357: AOS; if (TG (mb)) INCPC; break; /* AOSG */ case 0360: SOJ; break; /* SOJ */ case 0361: SOJ; if (TL (AC(ac))) JUMP (ea); break; /* SOJL */ case 0362: SOJ; if (TE (AC(ac))) JUMP (ea); break; /* SOJE */ case 0363: SOJ; if (TLE (AC(ac))) JUMP (ea); break; /* SOJLE */ case 0364: SOJ; JUMP(ea); break; /* SOJA */ case 0365: SOJ; if (TGE (AC(ac))) JUMP (ea); break; /* SOJGE */ case 0366: SOJ; if (TN (AC(ac))) JUMP (ea); break; /* SOJN */ case 0367: SOJ; if (TG (AC(ac))) JUMP (ea); /* SOJG */ if ((ea == pager_PC) && Q_IDLE) { /* to self, idle enab? */ extern int32 tmr_poll; if ((ac == 6) && (ea == 1) && /* SOJG 6,1? */ TSTF (F_USR) && Q_T10) /* T10, user mode? */ sim_idle (0, FALSE); else if (!t20_idlelock && /* interlock off? */ (ac == 2) && (ea == 3) && /* SOJG 2,3? */ !TSTF (F_USR) && Q_T20 && /* T20, mon mode? */ (sim_interval > (tmr_poll >> 1))) { /* >= half clock? */ t20_idlelock = 1; /* set interlock */ if (sim_os_ms_sleep (1)) /* sleep 1ms */ sim_interval = 0; /* if ok, sched event */ } } break; case 0370: SOS; break; /* SOS */ case 0371: SOS; if (TL (mb)) INCPC; break; /* SOSL */ case 0372: SOS; if (TE (mb)) INCPC; break; /* SOSE */ case 0373: SOS; if (TLE (mb)) INCPC; break; /* SOSLE */ case 0374: SOS; INCPC; break; /* SOSA */ case 0375: SOS; if (TGE (mb)) INCPC; break; /* SOSGE */ case 0376: SOS; if (TN (mb)) INCPC; break; /* SOSN */ case 0377: SOS; if (TG (mb)) INCPC; break; /* SOSG */ /* Boolean instructions (0400 - 0477) - checked against KS10 ucode Note that for boolean B, the initial read checks writeability of the memory operand; hence, it is safe to modify the AC. */ case 0400: AC(ac) = 0; break; /* SETZ */ case 0401: AC(ac) = 0; break; /* SETZI */ case 0402: mb = 0; WR; break; /* SETZM */ case 0403: mb = 0; WR; AC(ac) = 0; break; /* SETZB */ case 0404: RD; AC(ac) = AND (mb); break; /* AND */ case 0405: AC(ac) = AND (IM); break; /* ANDI */ case 0406: RM; mb = AND (mb); WR; break; /* ANDM */ case 0407: RM; AC(ac) = AND (mb); WRAC; break; /* ANDB */ case 0410: RD; AC(ac) = ANDCA (mb); break; /* ANDCA */ case 0411: AC(ac) = ANDCA (IM); break; /* ANDCAI */ case 0412: RM; mb = ANDCA (mb); WR; break; /* ANDCAM */ case 0413: RM; AC(ac) = ANDCA (mb); WRAC; break; /* ANDCAB */ case 0414: RDAC; break; /* SETM */ case 0415: AC(ac) = ea; break; /* SETMI */ case 0416: RM; WR; break; /* SETMM */ case 0417: RMAC; WRAC; break; /* SETMB */ case 0420: RD; AC(ac) = ANDCM (mb); break; /* ANDCM */ case 0421: AC(ac) = ANDCM (IM); break; /* ANDCMI */ case 0422: RM; mb = ANDCM (mb); WR; break; /* ANDCMM */ case 0423: RM; AC(ac) = ANDCM (mb); WRAC; break; /* ANDCMB */ case 0424: break; /* SETA */ case 0425: break; /* SETAI */ case 0426: WRAC; break; /* SETAM */ case 0427: WRAC; break; /* SETAB */ case 0430: RD; AC(ac) = XOR (mb); break; /* XOR */ case 0431: AC(ac) = XOR (IM); break; /* XORI */ case 0432: RM; mb = XOR (mb); WR; break; /* XORM */ case 0433: RM; AC(ac) = XOR (mb); WRAC; break; /* XORB */ case 0434: RD; AC(ac) = IOR (mb); break; /* IOR */ case 0435: AC(ac) = IOR (IM); break; /* IORI */ case 0436: RM; mb = IOR (mb); WR; break; /* IORM */ case 0437: RM; AC(ac) = IOR (mb); WRAC; break; /* IORB */ case 0440: RD; AC(ac) = ANDCB (mb); break; /* ANDCB */ case 0441: AC(ac) = ANDCB (IM); break; /* ANDCBI */ case 0442: RM; mb = ANDCB (mb); WR; break; /* ANDCBM */ case 0443: RM; AC(ac) = ANDCB (mb); WRAC; break; /* ANDCBB */ case 0444: RD; AC(ac) = EQV (mb); break; /* EQV */ case 0445: AC(ac) = EQV (IM); break; /* EQVI */ case 0446: RM; mb = EQV (mb); WR; break; /* EQVM */ case 0447: RM; AC(ac) = EQV (mb); WRAC; break; /* EQVB */ case 0450: RD; AC(ac) = SETCA (mb); break; /* SETCA */ case 0451: AC(ac) = SETCA (IM); break; /* SETCAI */ case 0452: RM; mb = SETCA (mb); WR; break; /* SETCAM */ case 0453: RM; AC(ac) = SETCA (mb); WRAC; break; /* SETCAB */ case 0454: RD; AC(ac) = ORCA (mb); break; /* ORCA */ case 0455: AC(ac) = ORCA (IM); break; /* ORCAI */ case 0456: RM; mb = ORCA (mb); WR; break; /* ORCAM */ case 0457: RM; AC(ac) = ORCA (mb); WRAC; break; /* ORCAB */ case 0460: RD; AC(ac) = SETCM (mb); break; /* SETCM */ case 0461: AC(ac) = SETCM (IM); break; /* SETCMI */ case 0462: RM; mb = SETCM (mb); WR; break; /* SETCMM */ case 0463: RM; AC(ac) = SETCM (mb); WRAC; break; /* SETCMB */ case 0464: RD; AC(ac) = ORCM (mb); break; /* ORCM */ case 0465: AC(ac) = ORCM (IM); break; /* ORCMI */ case 0466: RM; mb = ORCM (mb); WR; break; /* ORCMM */ case 0467: RM; AC(ac) = ORCM (mb); WRAC; break; /* ORCMB */ case 0470: RD; AC(ac) = ORCB (mb); break; /* ORCB */ case 0471: AC(ac) = ORCB (IM); break; /* ORCBI */ case 0472: RM; mb = ORCB (mb); WR; break; /* ORCBM */ case 0473: RM; AC(ac) = ORCB (mb); WRAC; break; /* ORCBB */ case 0474: AC(ac) = ONES; break; /* SETO */ case 0475: AC(ac) = ONES; break; /* SETOI */ case 0476: mb = ONES; WR; break; /* SETOM */ case 0477: mb = ONES; WR; AC(ac) = ONES; break; /* SETOB */ /* Halfword instructions (0500 - 0577) - checked against KS10 ucode */ case 0500: RD; AC(ac) = LL (mb, AC(ac)); break; /* HLL */ case 0501: AC(ac) = LL (IM, AC(ac)); break; /* HLLI */ case 0502: RM; mb = LL (AC(ac), mb); WR; break; /* HLLM */ case 0503: RM; mb = LL (mb, mb); WR; LAC; break; /* HLLS */ case 0504: RD; AC(ac) = RL (mb, AC(ac)); break; /* HRL */ case 0505: AC(ac) = RL (IM, AC(ac)); break; /* HRLI */ case 0506: RM; mb = RL (AC(ac), mb); WR; break; /* HRLM */ case 0507: RM; mb = RL (mb, mb); WR; LAC; break; /* HRLS */ case 0510: RD; AC(ac) = LLZ (mb); break; /* HLLZ */ case 0511: AC(ac) = LLZ (IM); break; /* HLLZI */ case 0512: mb = LLZ (AC(ac)); WR; break; /* HLLZM */ case 0513: RM; mb = LLZ (mb); WR; LAC; break; /* HLLZS */ case 0514: RD; AC(ac) = RLZ (mb); break; /* HRLZ */ case 0515: AC(ac) = RLZ (IM); break; /* HRLZI */ case 0516: mb = RLZ (AC(ac)); WR; break; /* HRLZM */ case 0517: RM; mb = RLZ (mb); WR; LAC; break; /* HRLZS */ case 0520: RD; AC(ac) = LLO (mb); break; /* HLLO */ case 0521: AC(ac) = LLO (IM); break; /* HLLOI */ case 0522: mb = LLO (AC(ac)); WR; break; /* HLLOM */ case 0523: RM; mb = LLO (mb); WR; LAC; break; /* HLLOS */ case 0524: RD; AC(ac) = RLO (mb); break; /* HRLO */ case 0525: AC(ac) = RLO (IM); break; /* HRLOI */ case 0526: mb = RLO (AC(ac)); WR; break; /* HRLOM */ case 0527: RM; mb = RLO (mb); WR; LAC; break; /* HRLOS */ case 0530: RD; AC(ac) = LLE (mb); break; /* HLLE */ case 0531: AC(ac) = LLE (IM); break; /* HLLEI */ case 0532: mb = LLE (AC(ac)); WR; break; /* HLLEM */ case 0533: RM; mb = LLE (mb); WR; LAC; break; /* HLLES */ case 0534: RD; AC(ac) = RLE (mb); break; /* HRLE */ case 0535: AC(ac) = RLE (IM); break; /* HRLEI */ case 0536: mb = RLE (AC(ac)); WR; break; /* HRLEM */ case 0537: RM; mb = RLE (mb); WR; LAC; break; /* HRLES */ case 0540: RD; AC(ac) = RR (mb, AC(ac)); break; /* HRR */ case 0541: AC(ac) = RR (IM, AC(ac)); break; /* HRRI */ case 0542: RM; mb = RR (AC(ac), mb); WR; break; /* HRRM */ case 0543: RM; mb = RR (mb, mb); WR; LAC; break; /* HRRS */ case 0544: RD; AC(ac) = LR (mb, AC(ac)); break; /* HLR */ case 0545: AC(ac) = LR (IM, AC(ac)); break; /* HLRI */ case 0546: RM; mb = LR (AC(ac), mb); WR; break; /* HLRM */ case 0547: RM; mb = LR (mb, mb); WR; LAC; break; /* HLRS */ case 0550: RD; AC(ac) = RRZ (mb); break; /* HRRZ */ case 0551: AC(ac) = RRZ (IM); break; /* HRRZI */ case 0552: mb = RRZ (AC(ac)); WR; break; /* HRRZM */ case 0553: RM; mb = RRZ(mb); WR; LAC; break; /* HRRZS */ case 0554: RD; AC(ac) = LRZ (mb); break; /* HLRZ */ case 0555: AC(ac) = LRZ (IM); break; /* HLRZI */ case 0556: mb = LRZ (AC(ac)); WR; break; /* HLRZM */ case 0557: RM; mb = LRZ (mb); WR; LAC; break; /* HLRZS */ case 0560: RD; AC(ac) = RRO (mb); break; /* HRRO */ case 0561: AC(ac) = RRO (IM); break; /* HRROI */ case 0562: mb = RRO (AC(ac)); WR; break; /* HRROM */ case 0563: RM; mb = RRO (mb); WR; LAC; break; /* HRROS */ case 0564: RD; AC(ac) = LRO (mb); break; /* HLRO */ case 0565: AC(ac) = LRO (IM); break; /* HLROI */ case 0566: mb = LRO (AC(ac)); WR; break; /* HLROM */ case 0567: RM; mb = LRO (mb); WR; LAC; break; /* HLROS */ case 0570: RD; AC(ac) = RRE (mb); break; /* HRRE */ case 0571: AC(ac) = RRE (IM); break; /* HRREI */ case 0572: mb = RRE (AC(ac)); WR; break; /* HRREM */ case 0573: RM; mb = RRE (mb); WR; LAC; break; /* HRRES */ case 0574: RD; AC(ac) = LRE (mb); break; /* HLRE */ case 0575: AC(ac) = LRE (IM); break; /* HLREI */ case 0576: mb = LRE (AC(ac)); WR; break; /* HLREM */ case 0577: RM; mb = LRE (mb); WR; LAC; break; /* HLRES */ /* Test instructions (0600 - 0677) - checked against KS10 ucode In the KS10 ucode, TDN and TSN do not fetch an operand; the Processor Reference Manual describes them as NOPs that reference memory. */ case 0600: break; /* TRN */ case 0601: break; /* TLN */ case 0602: TR_; T__E; break; /* TRNE */ case 0603: TL_; T__E; break; /* TLNE */ case 0604: T__A; break; /* TRNA */ case 0605: T__A; break; /* TLNA */ case 0606: TR_; T__N; break; /* TRNN */ case 0607: TL_; T__N; break; /* TLNN */ case 0610: TD_; break; /* TDN */ case 0611: TS_; break; /* TSN */ case 0612: TD_; T__E; break; /* TDNE */ case 0613: TS_; T__E; break; /* TSNE */ case 0614: TD_; T__A; break; /* TDNA */ case 0615: TS_; T__A; break; /* TSNA */ case 0616: TD_; T__N; break; /* TDNN */ case 0617: TS_; T__N; break; /* TSNN */ case 0620: TR_; T_Z; break; /* TRZ */ case 0621: TL_; T_Z; break; /* TLZ */ case 0622: TR_; T__E; T_Z; break; /* TRZE */ case 0623: TL_; T__E; T_Z; break; /* TLZE */ case 0624: TR_; T__A; T_Z; break; /* TRZA */ case 0625: TL_; T__A; T_Z; break; /* TLZA */ case 0626: TR_; T__N; T_Z; break; /* TRZN */ case 0627: TL_; T__N; T_Z; break; /* TLZN */ case 0630: TD_; T_Z; break; /* TDZ */ case 0631: TS_; T_Z; break; /* TSZ */ case 0632: TD_; T__E; T_Z; break; /* TDZE */ case 0633: TS_; T__E; T_Z; break; /* TSZE */ case 0634: TD_; T__A; T_Z; break; /* TDZA */ case 0635: TS_; T__A; T_Z; break; /* TSZA */ case 0636: TD_; T__N; T_Z; break; /* TDZN */ case 0637: TS_; T__N; T_Z; break; /* TSZN */ case 0640: TR_; T_C; break; /* TRC */ case 0641: TL_; T_C; break; /* TLC */ case 0642: TR_; T__E; T_C; break; /* TRCE */ case 0643: TL_; T__E; T_C; break; /* TLCE */ case 0644: TR_; T__A; T_C; break; /* TRCA */ case 0645: TL_; T__A; T_C; break; /* TLCA */ case 0646: TR_; T__N; T_C; break; /* TRCN */ case 0647: TL_; T__N; T_C; break; /* TLCN */ case 0650: TD_; T_C; break; /* TDC */ case 0651: TS_; T_C; break; /* TSC */ case 0652: TD_; T__E; T_C; break; /* TDCE */ case 0653: TS_; T__E; T_C; break; /* TSCE */ case 0654: TD_; T__A; T_C; break; /* TDCA */ case 0655: TS_; T__A; T_C; break; /* TSCA */ case 0656: TD_; T__N; T_C; break; /* TDCN */ case 0657: TS_; T__N; T_C; break; /* TSCN */ case 0660: TR_; T_O; break; /* TRO */ case 0661: TL_; T_O; break; /* TLO */ case 0662: TR_; T__E; T_O; break; /* TROE */ case 0663: TL_; T__E; T_O; break; /* TLOE */ case 0664: TR_; T__A; T_O; break; /* TROA */ case 0665: TL_; T__A; T_O; break; /* TLOA */ case 0666: TR_; T__N; T_O; break; /* TRON */ case 0667: TL_; T__N; T_O; break; /* TLON */ case 0670: TD_; T_O; break; /* TDO */ case 0671: TS_; T_O; break; /* TSO */ case 0672: TD_; T__E; T_O; break; /* TDOE */ case 0673: TS_; T__E; T_O; break; /* TSOE */ case 0674: TD_; T__A; T_O; break; /* TDOA */ case 0675: TS_; T__A; T_O; break; /* TSOA */ case 0676: TD_; T__N; T_O; break; /* TDON */ case 0677: TS_; T__N; T_O; break; /* TSON */ /* I/O instructions (0700 - 0777) Only the defined I/O instructions have explicit case labels; the rest default to unimplemented (monitor UUO). Note that 710-715 and 720-725 have different definitions under ITS and use normal effective addresses instead of the special address calculation required by TOPS-10 and TOPS-20. */ case 0700: IO7 (io700i, io700d); break; /* I/O 0 */ case 0701: IO7 (io701i, io701d); break; /* I/O 1 */ case 0702: IO7 (io702i, io702d); break; /* I/O 2 */ case 0704: IOC; AC(ac) = Read (ea, OPND_PXCT); break; /* UMOVE */ case 0705: IOC; Write (ea, AC(ac), OPND_PXCT); break; /* UMOVEM */ case 0710: IOA; if (io710 (ac, ea)) INCPC; break; /* TIOE, IORDI */ case 0711: IOA; if (io711 (ac, ea)) INCPC; break; /* TION, IORDQ */ case 0712: IOAM; AC(ac) = io712 (ea); break; /* RDIO, IORD */ case 0713: IOAM; io713 (AC(ac), ea); break; /* WRIO, IOWR */ case 0714: IOA; io714 (AC(ac), ea); break; /* BSIO, IOWRI */ case 0715: IOA; io715 (AC(ac), ea); break; /* BCIO, IOWRQ */ case 0716: IOC; bltu (ac, ea, pflgs, 0); break; /* BLTBU */ case 0717: IOC; bltu (ac, ea, pflgs, 1); break; /* BLTUB */ case 0720: IOA; if (io720 (ac, ea)) INCPC; break; /* TIOEB, IORDBI */ case 0721: IOA; if (io721 (ac, ea)) INCPC; break; /* TIONB, IORDBQ */ case 0722: IOAM; AC(ac) = io722 (ea); break; /* RDIOB, IORDB */ case 0723: IOAM; io723 (AC(ac), ea); break; /* WRIOB, IOWRB */ case 0724: IOA; io724 (AC(ac), ea); break; /* BSIOB, IOWRBI */ case 0725: IOA; io725 (AC(ac), ea); break; /* BCIOB, IOWRBQ */ /* If undefined, monitor UUO - checked against KS10 ucode The KS10 implements a much more limited version of MUUO flag handling. In the KS10, the trap ucode checks for opcodes 000-077. If the opcode is in that range, the trap flags are not cleared. Instead, the MUUO microcode stores the flags with traps cleared, and uses the trap flags to determine how to vector. Thus, MUUO's >= 100 will vector incorrectly. */ default: MUUO: its_2pr = 0; /* clear trap */ if (T20PAG) { /* TOPS20 paging? */ int32 tf = (op << (INST_V_OP - 18)) | (ac << (INST_V_AC - 18)); WriteP (upta + UPT_MUUO, XWD ( /* store flags,,op+ac */ flags & ~(F_T2 | F_T1), tf)); /* traps clear */ WriteP (upta + UPT_MUPC, PC); /* store PC */ WriteP (upta + UPT_T20_UEA, ea); /* store eff addr */ WriteP (upta + UPT_T20_CTX, UBRWORD); /* store context */ } else { /* TOPS10/ITS */ WriteP (upta + UPT_MUUO, UUOWORD); /* store instr word */ WriteP (upta + UPT_MUPC, XWD ( /* store flags,,PC */ flags & ~(F_T2 | F_T1), PC)); /* traps clear */ WriteP (upta + UPT_T10_CTX, UBRWORD); /* store context */ } ea = upta + (TSTF (F_USR)? UPT_UNPC: UPT_ENPC) + (pager_tc? UPT_NPCT: 0); /* calculate vector */ mb = ReadP (ea); /* new flags, PC */ JUMP (mb); /* set new PC */ if (TSTF (F_USR)) /* set PCU */ mb = mb | XWD (F_UIO, 0); set_newflags (mb, FALSE); /* set new flags */ break; /* JRST - checked against KS10 ucode Differences from the KS10: the KS10 - (JRSTF, JEN) refetches the base instruction from PC - 1 - (XJEN) dismisses interrupt before reading the new flags and PC - (XPCW) writes the old flags and PC before reading the new ITS microcode includes extended JRST's, although they are not used */ case 0254: /* JRST */ i = jrst_tab[ac]; /* get subop flags */ if ((i == 0) || ((i == JRST_E) && TSTF (F_USR)) || ((i == JRST_UIO) && TSTF (F_USR) && !TSTF (F_UIO))) goto MUUO; /* not legal */ switch (ac) { /* case on subopcode */ case 000: /* JRST 0 = jump */ case 001: /* JRST 1 = portal */ JUMP (ea); break; case 002: /* JRST 2 = JRSTF */ mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ JUMP (ea); /* set new PC */ set_newflags (mb, TRUE); /* set new flags */ break; case 004: /* JRST 4 = halt */ JUMP (ea); /* old_PC = halt + 1 */ pager_PC = PC; /* force right PC */ ABORT (STOP_HALT); /* known to be exec */ break; case 005: /* JRST 5 = XJRSTF */ RD2; /* read doubleword */ JUMP (rs[1]); /* set new PC */ set_newflags (rs[0], TRUE); /* set new flags */ break; case 006: /* JRST 6 = XJEN */ RD2; /* read doubleword */ pi_dismiss (); /* page ok, dismiss */ JUMP (rs[1]); /* set new PC */ set_newflags (rs[0], FALSE); /* known to be exec */ break; case 007: /* JRST 7 = XPCW */ ea = ADDA (i = ea, 2); /* new flags, PC */ RD2; /* read, test page fail */ ReadM (INCA (i), MM_OPND); /* test PC write */ Write (i, XWD (flags, 0), MM_OPND); /* write flags */ Write (INCA (i), PC, MM_OPND); /* write PC */ JUMP (rs[1]); /* set new PC */ set_newflags (rs[0], FALSE); /* known to be exec */ break; case 010: /* JRST 10 = dismiss */ pi_dismiss (); /* dismiss int */ JUMP (ea); /* set new PC */ break; case 012: /* JRST 12 = JEN */ mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ JUMP (ea); /* set new PC */ set_newflags (mb, TRUE); /* set new flags */ pi_dismiss (); /* dismiss int */ break; case 014: /* JRST 14 = SFM */ Write (ea, XWD (flags, 0), MM_OPND); break; case 015: /* JRST 15 = XJRST */ if (!T20PAG) /* only in TOPS20 paging */ goto MUUO; JUMP (Read (ea, MM_OPND)); /* jump to M[ea] */ break; } /* end case subop */ break; } /* end case op */ if (its_2pr) { /* 1-proc trap? */ its_1pr = its_2pr = 0; /* clear trap */ if (Q_ITS) { /* better be ITS */ WriteP (upta + UPT_1PO, FLPC); /* wr old flgs, PC */ mb = ReadP (upta + UPT_1PN); /* rd new flgs, PC */ JUMP (mb); /* set PC */ set_newflags (mb, TRUE); /* set new flags */ } } /* end if 2-proc */ } /* end for */ /* Should never get here */ ABORT (STOP_UNKNOWN); } /* Single word integer routines */ /* Integer add Truth table for integer add case a b r flags 1 + + + none 2 + + - AOV + C1 3 + - + C0 + C1 4 + - - - 5 - + + C0 + C1 6 - + - - 7 - - + AOV + C0 8 - - - C0 + C1 */ d10 add (d10 a, d10 b) { d10 r; r = (a + b) & DMASK; if (TSTS (a & b)) { /* cases 7,8 */ if (TSTS (r)) /* case 8 */ SETF (F_C0 | F_C1); else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ return r; } if (!TSTS (a | b)) { /* cases 1,2 */ if (TSTS (r)) /* case 2 */ SETF (F_C1 | F_AOV | F_T1); return r; /* case 1 */ } if (!TSTS (r)) /* cases 3,5 */ SETF (F_C0 | F_C1); return r; } /* Integer subtract - actually ac + ~op + 1 */ d10 sub (d10 a, d10 b) { d10 r; r = (a - b) & DMASK; if (TSTS (a & ~b)) { /* cases 7,8 */ if (TSTS (r)) /* case 8 */ SETF (F_C0 | F_C1); else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ return r; } if (!TSTS (a | ~b)) { /* cases 1,2 */ if (TSTS (r)) /* case 2 */ SETF (F_C1 | F_AOV | F_T1); return r; /* case 1 */ } if (!TSTS (r)) /* cases 3,5 */ SETF (F_C0 | F_C1); return r; } /* Logical shift */ d10 lsh (d10 val, a10 ea) { int32 sc = LIT8 (ea); if (sc > 35) return 0; if (ea & RSIGN) return (val >> sc); return ((val << sc) & DMASK); } /* Rotate */ d10 rot (d10 val, a10 ea) { int32 sc = LIT8 (ea) % 36; if (sc == 0) return val; if (ea & RSIGN) sc = 36 - sc; return (((val << sc) | (val >> (36 - sc))) & DMASK); } /* Double word integer instructions */ /* Double add - see case table for single add */ void dadd (int32 ac, d10 *rs) { d10 r; int32 p1 = ADDAC (ac, 1); AC(p1) = CLRS (AC(p1)) + CLRS (rs[1]); /* add lo */ r = (AC(ac) + rs[0] + (TSTS (AC(p1))? 1: 0)) & DMASK; /* add hi+cry */ if (TSTS (AC(ac) & rs[0])) { /* cases 7,8 */ if (TSTS (r)) /* case 8 */ SETF (F_C0 | F_C1); else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ } else if (!TSTS (AC(ac) | rs[0])) { /* cases 1,2 */ if (TSTS (r)) /* case 2 */ SETF (F_C1 | F_AOV | F_T1); } else if (!TSTS (r)) /* cases 3,5 */ SETF (F_C0 | F_C1); AC(ac) = r; AC(p1) = TSTS (r)? SETS (AC(p1)): CLRS (AC(p1)); return; } /* Double subtract - see comments for single subtract */ void dsub (int32 ac, d10 *rs) { d10 r; int32 p1 = ADDAC (ac, 1); AC(p1) = CLRS (AC(p1)) - CLRS (rs[1]); /* sub lo */ r = (AC(ac) - rs[0] - (TSTS (AC(p1))? 1: 0)) & DMASK; /* sub hi,borrow */ if (TSTS (AC(ac) & ~rs[0])) { /* cases 7,8 */ if (TSTS (r)) /* case 8 */ SETF (F_C0 | F_C1); else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ } else if (!TSTS (AC(ac) | ~rs[0])) { /* cases 1,2 */ if (TSTS (r)) /* case 2 */ SETF (F_C1 | F_AOV | F_T1); } else if (!TSTS (r)) /* cases 3,5 */ SETF (F_C0 | F_C1); AC(ac) = r; AC(p1) = (TSTS (r)? SETS (AC(p1)): CLRS (AC(p1))) & DMASK; return; } /* Logical shift combined */ void lshc (int32 ac, a10 ea) { int32 p1 = ADDAC (ac, 1); int32 sc = LIT8 (ea); if (sc > 71) AC(ac) = AC(p1) = 0; else if (ea & RSIGN) { if (sc >= 36) { AC(p1) = AC(ac) >> (sc - 36); AC(ac) = 0; } else { AC(p1) = ((AC(p1) >> sc) | (AC(ac) << (36 - sc))) & DMASK; AC(ac) = AC(ac) >> sc; } } else { if (sc >= 36) { AC(ac) = (AC(p1) << (sc - 36)) & DMASK; AC(p1) = 0; } else { AC(ac) = ((AC(ac) << sc) | (AC(p1) >> (36 - sc))) & DMASK; AC(p1) = (AC(p1) << sc) & DMASK; } } return; } /* Rotate combined */ void rotc (int32 ac, a10 ea) { int32 p1 = ADDAC (ac, 1); int32 sc = LIT8 (ea) % 72; d10 t = AC(ac); if (sc == 0) return; if (ea & RSIGN) sc = 72 - sc; if (sc >= 36) { AC(ac) = ((AC(p1) << (sc - 36)) | (t >> (72 - sc))) & DMASK; AC(p1) = ((t << (sc - 36)) | (AC(p1) >> (72 - sc))) & DMASK; } else { AC(ac) = ((t << sc) | (AC(p1) >> (36 - sc))) & DMASK; AC(p1) = ((AC(p1) << sc) | (t >> (36 - sc))) & DMASK; } return; } /* Arithmetic shifts */ d10 ash (d10 val, a10 ea) { int32 sc = LIT8 (ea); d10 sign = TSTS (val); d10 fill = sign? ONES: 0; d10 so; if (sc == 0) return val; if (sc > 35) /* cap sc at 35 */ sc = 35; if (ea & RSIGN) return (((val >> sc) | (fill << (36 - sc))) & DMASK); so = val >> (35 - sc); /* bits lost left + sign */ if (so != (sign? bytemask[sc + 1]: 0)) SETF (F_AOV | F_T1); return (sign | ((val << sc) & MMASK)); } void ashc (int32 ac, a10 ea) { int32 sc = LIT8 (ea); int32 p1 = ADDAC (ac, 1); d10 sign = TSTS (AC(ac)); d10 fill = sign? ONES: 0; d10 so; if (sc == 0) return; if (sc > 70) /* cap sc at 70 */ sc = 70; AC(ac) = CLRS (AC(ac)); /* clear signs */ AC(p1) = CLRS (AC(p1)); if (ea & RSIGN) { if (sc >= 35) { /* right 36..70 */ AC(p1) = ((AC(ac) >> (sc - 35)) | (fill << (70 - sc))) & DMASK; AC(ac) = fill; } else { AC(p1) = sign | /* right 1..35 */ (((AC(p1) >> sc) | (AC(ac) << (35 - sc))) & MMASK); AC(ac) = ((AC(ac) >> sc) | (fill << (35 - sc))) & DMASK; } } else { if (sc >= 35) { /* left 36..70 */ so = AC(p1) >> (70 - sc); /* bits lost left */ if ((AC(ac) != (sign? MMASK: 0)) || (so != (sign? bytemask[sc - 35]: 0))) SETF (F_AOV | F_T1); AC(ac) = sign | ((AC(p1) << (sc - 35)) & MMASK); AC(p1) = sign; } else { so = AC(ac) >> (35 - sc); /* bits lost left */ if (so != (sign? bytemask[sc]: 0)) SETF (F_AOV | F_T1); AC(ac) = sign | (((AC(ac) << sc) | (AC(p1) >> (35 - sc))) & MMASK); AC(p1) = sign | ((AC(p1) << sc) & MMASK); } } return; } /* Effective address routines */ /* Calculate effective address - used by byte instructions, extended instructions, and interrupts to get a different mapping context from the main loop. prv is either EABP_PXCT or MM_CUR. */ a10 calc_ea (d10 inst, int32 prv) { int32 i, ea, xr; d10 indrct; for (indrct = inst, i = 0; i < ind_max; i++) { ea = GET_ADDR (indrct); xr = GET_XR (indrct); if (xr) ea = (ea + ((a10) XR (xr, prv))) & AMASK; if (TST_IND (indrct)) indrct = Read (ea, prv); else break; } if (i >= ind_max) ABORT (STOP_IND); return ea; } /* Calculate I/O effective address. Cases: - No index or indirect, return addr from instruction - Index only, index >= 0, return 36b sum of addr + index - Index only, index <= 0, return 18b sum of addr + index - Indirect, calculate 18b sum of addr + index, return entire word fetch (single level) */ a10 calc_ioea (d10 inst, int32 pflgs) { int32 xr; a10 ea; xr = GET_XR (inst); ea = GET_ADDR (inst); if (TST_IND (inst)) { /* indirect? */ if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; ea = (a10) Read (ea, MM_EA); } else if (xr) { /* direct + idx? */ ea = ea + ((a10) XR (xr, MM_EA)); if (TSTS (XR (xr, MM_EA))) ea = ea & AMASK; } return ea; } /* Calculate JRSTF effective address. This routine preserves the left half of the effective address, to be the new flags. */ d10 calc_jrstfea (d10 inst, int32 pflgs) { int32 i, xr; d10 mb; for (i = 0; i < ind_max; i++) { mb = inst; xr = GET_XR (inst); if (xr) mb = (mb & AMASK) + XR (xr, MM_EA); if (TST_IND (inst)) inst = Read (((a10) mb) & AMASK, MM_EA); else break; } if (i >= ind_max) ABORT (STOP_IND); return (mb & DMASK); } /* Byte pointer routines */ /* Increment byte pointer - checked against KS10 ucode */ void ibp (a10 ea, int32 pflgs) { int32 p, s; d10 bp; bp = ReadM (ea, MM_OPND); /* get byte ptr */ p = GET_P (bp); /* get P and S */ s = GET_S (bp); p = p - s; /* adv P */ if (p < 0) { /* end of word? */ bp = (bp & LMASK) | (INCR (bp)); /* incr addr */ p = (36 - s) & 077; /* reset P */ } bp = PUT_P (bp, p); /* store new P */ Write (ea, bp, MM_OPND); /* store byte ptr */ return; } /* Load byte */ d10 ldb (a10 ea, int32 pflgs) { a10 ba; int32 p, s; d10 bp, wd; bp = Read (ea, MM_OPND); /* get byte ptr */ p = GET_P (bp); /* get P and S */ s = GET_S (bp); ba = calc_ea (bp, MM_EABP); /* get addr of byte */ wd = Read (ba, MM_BSTK); /* read word */ wd = (wd >> p); /* align byte */ wd = wd & bytemask[s]; /* mask to size */ return wd; } /* Deposit byte - must use read and write to get page fail correct */ void dpb (d10 val, a10 ea, int32 pflgs) { a10 ba; int32 p, s; d10 bp, wd, mask; bp = Read (ea, MM_OPND); /* get byte ptr */ p = GET_P (bp); /* get P and S */ s = GET_S (bp); ba = calc_ea (bp, MM_EABP); /* get addr of byte */ wd = Read (ba, MM_BSTK); /* read word */ mask = bytemask[s] << p; /* shift mask, val */ val = val << p; wd = (wd & ~mask) | (val & mask); /* insert byte */ Write (ba, wd & DMASK, MM_BSTK); return; } /* Adjust byte pointer - checked against KS10 ucode The KS10 divide checks if the bytes per word = 0, which is a simpler formulation of the processor reference manual check. */ void adjbp (int32 ac, a10 ea, int32 pflgs) { int32 p, s; d10 bp, newby, left, byadj, bywrd, val, wdadj; val = AC(ac); /* get adjustment */ bp = Read (ea, MM_OPND); /* get byte pointer */ p = GET_P (bp); /* get p */ s = GET_S (bp); /* get s */ if (s) { left = (36 - p) / s; /* bytes to left of p */ bywrd = left + (p / s); /* bytes per word */ if (bywrd == 0) { /* zero bytes? */ SETF (F_AOV | F_T1 | F_DCK); /* set flags */ return; /* abort operation */ } newby = left + SXT (val); /* adjusted byte # */ wdadj = newby / bywrd; /* word adjustment */ byadj = (newby >= 0)? newby % bywrd: -((-newby) % bywrd); if (byadj <= 0) { byadj = byadj + bywrd; /* make adj positive */ wdadj = wdadj - 1; } p = (36 - ((int32) byadj) * s) - ((36 - p) % s); /* new p */ bp = (PUT_P (bp, p) & LMASK) | ((bp + wdadj) & RMASK); } AC(ac) = bp; return; } /* Block transfer - checked against KS10 ucode The KS10 uses instruction specific recovery code in page fail to set the AC properly for restart. Lacking this mechanism, the simulator must test references in advance. The clocking test guarantees forward progress under single step. */ void blt (int32 ac, a10 ea, int32 pflgs) { a10 srca = (a10) LRZ (AC(ac)); a10 dsta = (a10) RRZ (AC(ac)); a10 lnt = ea - dsta + 1; d10 srcv; int32 flg, t; AC(ac) = XWD (srca + lnt, dsta + lnt); for (flg = 0; dsta <= ea; flg++) { /* loop */ if (flg && (t = test_int ())) { /* timer event? */ AC(ac) = XWD (srca, dsta); /* AC for intr */ ABORT (t); } if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ AC(ac) = XWD (srca, dsta); /* AC for page fail */ Read (srca & AMASK, MM_BSTK); /* force trap */ } if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ AC(ac) = XWD (srca, dsta); /* AC for page fail */ ReadM (dsta & AMASK, MM_OPND); /* force trap */ } srcv = Read (srca & AMASK, MM_BSTK); /* read */ Write (dsta & AMASK, srcv, MM_OPND); /* write */ srca = srca + 1; /* incr addr */ dsta = dsta + 1; } return; } /* I/O block transfers - byte to Unibus (0) and Unibus to byte (1) */ #define BYTE1 0776000000000 #define BYTE2 0001774000000 #define BYTE3 0000003770000 #define BYTE4 0000000007760 /* unused 0000000000017 */ void bltu (int32 ac, a10 ea, int32 pflgs, int dir) { a10 srca = (a10) LRZ (AC(ac)); a10 dsta = (a10) RRZ (AC(ac)); a10 lnt = ea - dsta + 1; d10 srcv, dstv; int32 flg, t; AC(ac) = XWD (srca + lnt, dsta + lnt); for (flg = 0; dsta <= ea; flg++) { /* loop */ if (flg && (t = test_int ())) { /* timer event? */ AC(ac) = XWD (srca, dsta); /* AC for intr */ ABORT (t); } if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ AC(ac) = XWD (srca, dsta); /* AC for page fail */ Read (srca & AMASK, MM_BSTK); /* force trap */ } if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ AC(ac) = XWD (srca, dsta); /* AC for page fail */ ReadM (dsta & AMASK, MM_OPND); /* force trap */ } srcv = Read (srca & AMASK, MM_BSTK); /* read */ if (dir) dstv = ((srcv << 10) & BYTE1) | ((srcv >> 6) & BYTE2) | ((srcv << 12) & BYTE3) | ((srcv >> 4) & BYTE4); else dstv = ((srcv & BYTE1) >> 10) | ((srcv & BYTE2) << 6) | ((srcv & BYTE3) >> 12) | ((srcv & BYTE4) << 4); Write (dsta & AMASK, dstv, MM_OPND); /* write */ srca = srca + 1; /* incr addr */ dsta = dsta + 1; } return; } /* Utility routine to test for I/O event and interrupt */ int32 test_int (void) { int32 t; if (sim_interval <= 0) { /* check queue */ if (t = sim_process_event ()) /* IO event? */ return t; if (pi_eval ()) /* interrupt? */ return (INTERRUPT); } else sim_interval--; /* count clock */ return 0; } /* Adjust stack pointer The reference manual says to trap on: 1) E < 0, left changes from + to - 2) E >= 0, left changes from - to + This is the same as trap on: 1) E and left result have same signs 2) initial value and left result have different signs */ d10 adjsp (d10 val, a10 ea) { d10 imm = ea; d10 left, right; left = ADDL (val, imm); right = ADDR (val, imm); if (TSTS ((val ^ left) & (~left ^ RLZ (imm)))) SETF (F_T2); return (left | right); } /* Jump if find first ones Takes advantage of 7 bit find first table for priority interrupts. */ int32 jffo (d10 val) { int32 i, by; if ((val & DMASK) == 0) return 0; for (i = 0; i <= 28; i = i + 7) { /* scan five bytes */ by = (int32) ((val >> (29 - i)) & 0177); if (by) return (pi_m2lvl[by] + i - 1); } return 35; /* must be bit 35 */ } /* Circulate - ITS only instruction Bits rotated out of AC are rotated into the opposite end of AC+1 - why? No attempt is made to optimize this instruction. */ void circ (int32 ac, int32 ea) { int32 sc = LIT8 (ea) % 72; int32 p1 = ADDAC (ac,1); int32 i; d10 val; if (sc == 0) /* any shift? */ return; if (ea & RSIGN) /* if right, make left */ sc = 72 - sc; for (i = 0; i < sc; i++) { /* one bit at a time */ val = TSTS (AC(ac)); /* shift out */ AC(ac) = ((AC(ac) << 1) | (AC(p1) & 1)) & DMASK; AC(p1) = (AC(p1) >> 1) | val; /* shift in */ } return; } /* Arithmetic processor (APR) The APR subsystem includes miscellaneous interrupts that are individually maskable but which interrupt on a single, selectable level Instructions for the arithmetic processor: APRID read system id WRAPR (CONO APR) write system flags RDAPR (CONI APR) read system flags (CONSO APR) test system flags (CONSZ APR) test system flags */ t_bool aprid (a10 ea, int32 prv) { Write (ea, (Q_ITS)? UC_AIDITS: UC_AIDDEC, prv); return FALSE; } /* Checked against KS10 ucode */ t_bool wrapr (a10 ea, int32 prv) { int32 bits = APR_GETF (ea); apr_lvl = ea & APR_M_LVL; if (ea & APR_SENB) /* set enables? */ apr_enb = apr_enb | bits; if (ea & APR_CENB) /* clear enables? */ apr_enb = apr_enb & ~bits; if (ea & APR_CFLG) /* clear flags? */ apr_flg = apr_flg & ~bits; if (ea & APR_SFLG) /* set flags? */ apr_flg = apr_flg | bits; if (apr_flg & APRF_ITC) { /* interrupt console? */ fe_intr (); /* explicit callout */ apr_flg = apr_flg & ~APRF_ITC; /* interrupt clears */ } pi_eval (); /* eval pi system */ return FALSE; } t_bool rdapr (a10 ea, int32 prv) { Write (ea, (d10) APRWORD, prv); return FALSE; } t_bool czapr (a10 ea, int32 prv) { return ((APRHWORD & ea)? FALSE: TRUE); } t_bool coapr (a10 ea, int32 prv) { return ((APRHWORD & ea)? TRUE: FALSE); } /* Routine to change the processor flags, called from JRST, MUUO, interrupt. If jrst is TRUE, must munge flags for executive security. Because the KS10 lacks the public flag, these checks are simplified. */ void set_newflags (d10 newf, t_bool jrst) { int32 fl = (int32) LRZ (newf); if (jrst && TSTF (F_USR)) { /* if in user now */ fl = fl | F_USR; /* can't clear user */ if (!TSTF (F_UIO)) /* if !UIO, can't set */ fl = fl & ~F_UIO; } if (Q_ITS && (fl & F_1PR)) { /* ITS 1-proceed? */ its_1pr = 1; /* set flag */ fl = fl & ~F_1PR; /* vanish bit */ } flags = fl & F_MASK; /* set new flags */ set_dyn_ptrs (); /* set new ptrs */ return; } /* Priority interrupt system (PI) The priority interrupt system has three sources of requests (pi_apr) system flags - synthesized on the fly (pi_ioq) I/O interrupts - synthesized on the fly pi_prq program requests APR and I/O requests are masked with the PI enable mask; the program requests are not. If priority interrupts are enabled, and there is a request at a level exceeding the currently active level, then an interrupt occurs. Instructions for the priority interrupt system: WRPI (CONO PI) write pi system RDPI (CONI PI) read pi system (CONSO PI) test pi system (CONSZ PI) test pi system Routines for the priority interrupt system: pi_eval return level number of highest interrupt pi_dismiss dismiss highest outstanding interrupt Checked against KS10 ucode - KS10 UUO's if <18:21> are non-zero */ t_bool wrpi (a10 ea, int32 prv) { int32 lvl = ea & PI_M_LVL; if (ea & PI_INIT) pi_on = pi_enb = pi_act = pi_prq = 0; if (ea & PI_CPRQ) /* clear prog reqs? */ pi_prq = pi_prq & ~lvl; if (ea & PI_SPRQ) /* set prog reqs? */ pi_prq = pi_prq | lvl; if (ea & PI_SENB) /* enable levels? */ pi_enb = pi_enb | lvl; if (ea & PI_CENB) /* disable levels? */ pi_enb = pi_enb & ~lvl; if (ea & PI_SON) /* enable pi? */ pi_on = 1; if (ea & PI_CON) /* disable pi? */ pi_on = 0; pi_eval (); /* eval pi system */ return FALSE; } t_bool rdpi (a10 ea, int32 prv) { Write (ea, (d10) PIWORD, prv); return FALSE; } t_bool czpi (a10 ea, int32 prv) { return ((PIHWORD & ea)? FALSE: TRUE); } t_bool copi (a10 ea, int32 prv) { return ((PIHWORD & ea)? TRUE: FALSE); } /* Priority interrupt evaluation The Processor Reference Manuals says that program interrupt requests occur whether the corresponding level is enabled or not. However, the KS10, starting with microcode edit 47, masked program requests under the enable mask, just like APR and I/O requests. This is not formally documented but appears to be necessary for the TOPS20 console port to run correclty. */ int32 pi_eval (void) { int32 reqlvl, actlvl; extern int32 pi_ub_eval (); qintr = 0; if (pi_on) { pi_apr = (apr_flg & apr_enb)? pi_l2bit[apr_lvl]: 0; pi_ioq = pi_ub_eval (); reqlvl = pi_m2lvl[((pi_apr | pi_ioq | pi_prq) & pi_enb)]; actlvl = pi_m2lvl[pi_act]; if ((actlvl == 0) || (reqlvl < actlvl)) qintr = reqlvl; } return qintr; } void pi_dismiss (void) { pi_act = pi_act & ~pi_l2bit[pi_m2lvl[pi_act]]; /* clr left most bit */ pi_eval (); /* eval pi system */ return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { flags = 0; /* clear flags */ its_1pr = 0; /* clear 1-proceed */ ebr = ubr = 0; /* clear paging */ pi_enb = pi_act = pi_prq = 0; /* clear PI */ apr_enb = apr_flg = apr_lvl = 0; /* clear APR */ pcst = 0; /* clear PC samp */ rlog = 0; /* clear reg log */ hsb = (Q_ITS)? UC_HSBITS: UC_HSBDEC; /* set HSB */ set_dyn_ptrs (); set_ac_display (ac_cur); pi_eval (); if (M == NULL) M = (d10 *) calloc (MAXMEMSIZE, sizeof (d10)); if (M == NULL) return SCPE_MEM; 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 ea, UNIT *uptr, int32 sw) { if (vptr == NULL) return SCPE_ARG; if (ea < AC_NUM) *vptr = AC(ea) & DMASK; else { if (sw & SWMASK ('V')) { ea = conmap (ea, PTF_CON, sw); if (ea >= MAXMEMSIZE) return SCPE_REL; } if (ea >= MEMSIZE) return SCPE_NXM; *vptr = M[ea] & DMASK; } return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) { if (ea < AC_NUM) AC(ea) = val & DMASK; else { if (sw & SWMASK ('V')) { ea = conmap (ea, PTF_CON | PTF_WR, sw); if (ea >= MAXMEMSIZE) return SCPE_REL; } if (ea >= MEMSIZE) return SCPE_NXM; M[ea] = val & DMASK; } return SCPE_OK; } /* Set current AC pointers for SCP */ void set_ac_display (d10 *acbase) { extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); REG *rptr; int i; rptr = find_reg ("AC0", NULL, &cpu_dev); if (rptr == NULL) return; for (i = 0; i < AC_NUM; i++, rptr++) rptr->loc = (void *) (acbase + i); return; } /* 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 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) /* enabled? */ return SCPE_NOFNC; 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 AC 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? */ fprintf (st, "%06o ", h->pc & AMASK); fprint_val (st, h->ac, 8, 36, PV_RZRO); fputs (" ", st); fprintf (st, "%06o ", h->ea); sim_eval = h->ir; if ((fprint_sym (st, h->pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { fputs ("(undefined) ", st); fprint_val (st, h->ir, 8, 36, PV_RZRO); } fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/PDP10/pdp10_rp.c0000644000175000017500000016476111112112574013230 0ustar vlmvlm/* pdp10_rp.c - RH11/RP04/05/06/07 RM02/03/05/80 "Massbus" disk controller Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rp RH/RP/RM moving head disks 12-Nov-05 RMS Fixed DCLR not to clear drive address 07-Jul-05 RMS Removed extraneous externs 18-Mar-05 RMS Added attached test to detach routine 20-Sep-04 RMS Fixed bugs in replicated state, RP vs RM accuracy 04-Jan-04 RMS Changed sim_fsize calling sequence 23-Jul-03 RMS Fixed bug in read header stub 25-Apr-03 RMS Revised for extended file support 21-Nov-02 RMS Fixed bug in bootstrap (reported by Michael Thompson) 29-Sep-02 RMS Added variable vector support New data structures 30-Nov-01 RMS Added read only unit, extended SET/SHOW support support 24-Nov-01 RMS Changed RPER, RPDS, FNC, FLG to arrays 23-Oct-01 RMS Fixed bug in error interrupts New IO page address constants 05-Oct-01 RMS Rewrote interrupt handling from schematics 02-Oct-01 RMS Revised CS1 write code 30-Sep-01 RMS Moved CS1<5:0> into drives 28-Sep-01 RMS Fixed interrupt handling for SC/ATA 23-Aug-01 RMS Added read/write header stubs for ITS (found by Mirian Crzig Lennox) 13-Jul-01 RMS Changed fread call to fxread (found by Peter Schorn) 14-May-01 RMS Added check for unattached drive The "Massbus style" disks consisted of several different large capacity drives interfaced through a reasonably common (but not 100% compatible) family of interfaces into the KS10 Unibus via the RH11 disk controller. WARNING: The interupt logic of the RH11/RH70 is unusual and must be simulated with great precision. The RH11 has an internal interrupt request flop, CSTB INTR, which is controlled as follows: - Writing IE and DONE simultaneously sets CSTB INTR - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR (and also clear IE) - A transition of DONE from 0 to 1 sets CSTB from INTR The output of INTR is OR'd with the AND of RPCS1 to create the interrupt request signal. Thus, - The DONE interrupt is edge sensitive, but the SC interrupt is level sensitive. - The DONE interrupt, once set, is not disabled if IE is cleared, but the SC interrupt is. */ #include "pdp10_defs.h" #include #define RP_NUMDR 8 /* #drives */ #define RP_NUMWD 128 /* 36b words/sector */ #define RP_MAXFR 32768 /* max transfer */ #define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) drv_tab[d].sect))) #define MBA_RP_CTRL 0 /* RP drive */ #define MBA_RM_CTRL 1 /* RM drive */ /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_M_DTYPE 7 #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ #define UNIT_V_DUMMY (UNIT_V_UF + 5) /* dummy flag */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_DUMMY (1 << UNIT_V_DUMMY) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ #define FUNC u4 /* function */ /* RPCS1 - 176700 - control/status 1 */ #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) #define FNC_NOP 000 /* no operation */ #define FNC_UNLOAD 001 /* unload */ #define FNC_SEEK 002 /* seek */ #define FNC_RECAL 003 /* recalibrate */ #define FNC_DCLR 004 /* drive clear */ #define FNC_RELEASE 005 /* port release */ #define FNC_OFFSET 006 /* offset */ #define FNC_RETURN 007 /* return to center */ #define FNC_PRESET 010 /* read-in preset */ #define FNC_PACK 011 /* pack acknowledge */ #define FNC_SEARCH 014 /* search */ #define FNC_XFER 024 /* >=? data xfr */ #define FNC_WCHK 024 /* write check */ #define FNC_WRITE 030 /* write */ #define FNC_WRITEH 031 /* write w/ headers */ #define FNC_READ 034 /* read */ #define FNC_READH 035 /* read w/ headers */ #define CS1_IE CSR_IE /* int enable */ #define CS1_DONE CSR_DONE /* ready */ #define CS1_V_UAE 8 /* Unibus addr ext */ #define CS1_M_UAE 03 #define CS1_UAE (CS1_M_UAE << CS1_V_UAE) #define CS1_DVA 0004000 /* drive avail NI */ #define CS1_MCPE 0020000 /* Mbus par err NI */ #define CS1_TRE 0040000 /* transfer err */ #define CS1_SC 0100000 /* special cond */ #define CS1_MBZ 0012000 #define CS1_DRV (CS1_FNC | CS1_GO) #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) #define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) /* RPWC - 176702 - word count */ /* RPBA - 176704 - base address */ #define BA_MBZ 0000001 /* must be zero */ /* RPDA - 176706 - sector/track */ #define DA_V_SC 0 /* sector pos */ #define DA_M_SC 077 /* sector mask */ #define DA_V_SF 8 /* track pos */ #define DA_M_SF 077 /* track mask */ #define DA_MBZ 0140300 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) /* RPCS2 - 176710 - control/status 2 */ #define CS2_V_UNIT 0 /* unit pos */ #define CS2_M_UNIT 07 /* unit mask */ #define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) #define CS2_UAI 0000010 /* addr inhibit */ #define CS2_PAT 0000020 /* parity test NI */ #define CS2_CLR 0000040 /* controller clear */ #define CS2_IR 0000100 /* input ready */ #define CS2_OR 0000200 /* output ready */ #define CS2_MDPE 0000400 /* Mbus par err NI */ #define CS2_MXF 0001000 /* missed xfer NI */ #define CS2_PGE 0002000 /* program err */ #define CS2_NEM 0004000 /* nx mem err */ #define CS2_NED 0010000 /* nx drive err */ #define CS2_PE 0020000 /* parity err NI */ #define CS2_WCE 0040000 /* write check err */ #define CS2_DLT 0100000 /* data late NI */ #define CS2_MBZ (CS2_CLR) #define CS2_RW (CS2_UNIT | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) #define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ CS2_NED | CS2_PE | CS2_WCE | CS2_DLT ) #define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) /* RPDS - 176712 - drive status */ #define DS_OF 0000001 /* offset mode */ #define DS_VV 0000100 /* volume valid */ #define DS_RDY 0000200 /* drive ready */ #define DS_DPR 0000400 /* drive present */ #define DS_PGM 0001000 /* programable NI */ #define DS_LST 0002000 /* last sector */ #define DS_WRL 0004000 /* write locked */ #define DS_MOL 0010000 /* medium online */ #define DS_PIP 0020000 /* pos in progress */ #define DS_ERR 0040000 /* error */ #define DS_ATA 0100000 /* attention active */ #define DS_MBZ 0000076 /* RPER1 - 176714 - error status 1 */ #define ER1_ILF 0000001 /* illegal func */ #define ER1_ILR 0000002 /* illegal register */ #define ER1_RMR 0000004 /* reg mod refused */ #define ER1_PAR 0000010 /* parity err */ #define ER1_FER 0000020 /* format err NI */ #define ER1_WCF 0000040 /* write clk fail NI */ #define ER1_ECH 0000100 /* ECC hard err NI */ #define ER1_HCE 0000200 /* hdr comp err NI */ #define ER1_HCR 0000400 /* hdr CRC err NI */ #define ER1_AOE 0001000 /* addr ovflo err */ #define ER1_IAE 0002000 /* invalid addr err */ #define ER1_WLE 0004000 /* write lock err */ #define ER1_DTE 0010000 /* drive time err NI */ #define ER1_OPI 0020000 /* op incomplete */ #define ER1_UNS 0040000 /* drive unsafe */ #define ER1_DCK 0100000 /* data check NI */ /* RPAS - 176716 - attention summary */ #define AS_U0 0000001 /* unit 0 flag */ /* RPLA - 176720 - look ahead register */ #define LA_V_SC 6 /* sector pos */ /* RPDB - 176722 - data buffer */ /* RPMR - 176724 - maintenace register */ /* RPDT - 176726 - drive type */ /* RPSN - 176730 - serial number */ /* RPOF - 176732 - offset register */ #define OF_HCI 0002000 /* hdr cmp inh NI */ #define OF_ECI 0004000 /* ECC inhibit NI */ #define OF_F22 0010000 /* format NI */ #define OF_MBZ 0161400 /* RPDC - 176734 - desired cylinder */ #define DC_V_CY 0 /* cylinder pos */ #define DC_M_CY 01777 /* cylinder mask */ #define DC_MBZ 0176000 #define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) #define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \ GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs)) /* RPCC - 176736 - current cylinder */ /* RPER2 - 176740 - error status 2 - drive unsafe conditions - unimplemented */ /* RPER3 - 176742 - error status 3 - more unsafe conditions - unimplemented */ /* RPEC1 - 176744 - ECC status 1 - unimplemented */ /* RPEC2 - 176746 - ECC status 2 - unimplemented */ /* This controller supports many different disk drive types. These drives are operated in 576 bytes/sector (128 36b words/sector) mode, which gives them somewhat different geometry from the PDP-11 variants: type #sectors/ #surfaces/ #cylinders/ surface cylinder drive RM02/3 30 5 823 =67MB RP04/5 20 19 411 =88MB RM80 30 14 559 =124MB RP06 20 19 815 =176MB RM05 30 19 823 =256MB RP07 43 32 630 =516MB In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. The RP07, despite its name, uses an RM-style controller. */ #define RM03_DTYPE 0 #define RM03_SECT 30 #define RM03_SURF 5 #define RM03_CYL 823 #define RM03_DEV 020024 #define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD) #define RP04_DTYPE 1 #define RP04_SECT 20 #define RP04_SURF 19 #define RP04_CYL 411 #define RP04_DEV 020020 #define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) #define RM80_DTYPE 2 #define RM80_SECT 30 #define RM80_SURF 14 #define RM80_CYL 559 #define RM80_DEV 020026 #define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD) #define RP06_DTYPE 3 #define RP06_SECT 20 #define RP06_SURF 19 #define RP06_CYL 815 #define RP06_DEV 020022 #define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) #define RM05_DTYPE 4 #define RM05_SECT 30 #define RM05_SURF 19 #define RM05_CYL 823 #define RM05_DEV 020027 #define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD) #define RP07_DTYPE 5 #define RP07_SECT 43 #define RP07_SURF 32 #define RP07_CYL 630 #define RP07_DEV 020042 #define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) struct drvtyp { int32 sect; /* sectors */ int32 surf; /* surfaces */ int32 cyl; /* cylinders */ int32 size; /* #blocks */ int32 devtype; /* device type */ int32 ctrl; /* ctrl type */ }; struct drvtyp drv_tab[] = { { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, MBA_RM_CTRL }, { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, MBA_RP_CTRL }, { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, MBA_RM_CTRL }, { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, MBA_RP_CTRL }, { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, MBA_RM_CTRL }, { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, MBA_RM_CTRL }, { 0 } }; extern d10 *M; /* memory */ extern int32 int_req; extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus maps */ extern int32 ubcs[UBANUM]; extern UNIT cpu_unit; int32 rpcs1 = 0; /* control/status 1 */ int32 rpwc = 0; /* word count */ int32 rpba = 0; /* bus address */ int32 rpcs2 = 0; /* control/status 2 */ int32 rpdb = 0; /* data buffer */ uint16 rpda[RP_NUMDR] = { 0 }; /* track/sector */ uint16 rpds[RP_NUMDR] = { 0 }; /* drive status */ uint16 rper1[RP_NUMDR] = { 0 }; /* error status 1 */ uint16 rmhr[RP_NUMDR] = { 0 }; /* holding reg */ uint16 rpmr[RP_NUMDR] = { 0 }; /* maint reg */ uint16 rmmr2[RP_NUMDR] = { 0 }; /* maint reg 2 */ uint16 rpof[RP_NUMDR] = { 0 }; /* offset */ uint16 rpdc[RP_NUMDR] = { 0 }; /* cylinder */ uint16 rper2[RP_NUMDR] = { 0 }; /* error status 2 */ uint16 rper3[RP_NUMDR] = { 0 }; /* error status 3 */ uint16 rpec1[RP_NUMDR] = { 0 }; /* ECC correction 1 */ uint16 rpec2[RP_NUMDR] = { 0 }; /* ECC correction 2 */ int32 rpiff = 0; /* INTR flip/flop */ int32 rp_stopioe = 1; /* stop on error */ int32 rp_swait = 10; /* seek time */ int32 rp_rwait = 10; /* rotate time */ static int32 reg_in_drive[32] = { 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; t_stat rp_rd (int32 *data, int32 PA, int32 access); t_stat rp_wr (int32 data, int32 PA, int32 access); int32 rp_inta (void); t_stat rp_svc (UNIT *uptr); t_stat rp_reset (DEVICE *dptr); t_stat rp_boot (int32 unitno, DEVICE *dptr); t_stat rp_attach (UNIT *uptr, char *cptr); t_stat rp_detach (UNIT *uptr); void set_rper (int32 flag, int32 drv); void update_rpcs (int32 flags, int32 drv); void rp_go (int32 drv, int32 fnc); t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* RP data structures rp_dev RP device descriptor rp_unit RP unit list rp_reg RP register list rp_mod RP modifier list */ DIB rp_dib = { IOBA_RP, IOLN_RP, &rp_rd, &rp_wr, 1, IVCL (RP), VEC_RP, { &rp_inta } }; UNIT rp_unit[] = { { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) } }; REG rp_reg[] = { { ORDATA (RPCS1, rpcs1, 16) }, { ORDATA (RPWC, rpwc, 16) }, { ORDATA (RPBA, rpba, 16) }, { ORDATA (RPCS2, rpcs2, 16) }, { ORDATA (RPDB, rpdb, 16) }, { BRDATA (RPDA, rpda, 8, 16, RP_NUMDR) }, { BRDATA (RPDS, rpds, 8, 16, RP_NUMDR) }, { BRDATA (RPER1, rper1, 8, 16, RP_NUMDR) }, { BRDATA (RPHR, rmhr, 8, 16, RP_NUMDR) }, { BRDATA (RPOF, rpof, 8, 16, RP_NUMDR) }, { BRDATA (RPDC, rpdc, 8, 16, RP_NUMDR) }, { BRDATA (RPER2, rper2, 8, 16, RP_NUMDR) }, { BRDATA (RPER3, rper3, 8, 16, RP_NUMDR) }, { BRDATA (RPEC1, rpec1, 8, 16, RP_NUMDR) }, { BRDATA (RPEC2, rpec2, 8, 16, RP_NUMDR) }, { BRDATA (RMMR, rpmr, 8, 16, RP_NUMDR) }, { BRDATA (RMMR2, rmmr2, 8, 16, RP_NUMDR) }, { FLDATA (IFF, rpiff, 0) }, { FLDATA (INT, int_req, INT_V_RP) }, { FLDATA (SC, rpcs1, CSR_V_ERR) }, { FLDATA (DONE, rpcs1, CSR_V_DONE) }, { FLDATA (IE, rpcs1, CSR_V_IE) }, { DRDATA (STIME, rp_swait, 24), REG_NZ + PV_LEFT }, { DRDATA (RTIME, rp_rwait, 24), REG_NZ + PV_LEFT }, { URDATA (FNC, rp_unit[0].FUNC, 8, 5, 0, RP_NUMDR, REG_HRO) }, { URDATA (CAPAC, rp_unit[0].capac, 10, T_ADDR_W, 0, RP_NUMDR, PV_LEFT | REG_HRO) }, { FLDATA (STOP_IOE, rp_stopioe, 0) }, { NULL } }; MTAB rp_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { (UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM03", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP04", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM80", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP06", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM05", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP07", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE), "RM03", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE), "RP04", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE), "RM80", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE), "RP06", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE), "RM05", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE), "RP07", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), (RM03_DTYPE << UNIT_V_DTYPE), NULL, "RM03", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP04_DTYPE << UNIT_V_DTYPE), NULL, "RP04", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RM80_DTYPE << UNIT_V_DTYPE), NULL, "RM80", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP06_DTYPE << UNIT_V_DTYPE), NULL, "RP06", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RM05_DTYPE << UNIT_V_DTYPE), NULL, "RM05", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP07_DTYPE << UNIT_V_DTYPE), NULL, "RP07", &rp_set_size }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE rp_dev = { "RP", rp_unit, rp_reg, rp_mod, RP_NUMDR, 8, 30, 1, 8, 36, NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, &rp_dib, DEV_UBUS }; /* I/O dispatch routines, I/O addresses 17776700 - 17776776 */ t_stat rp_rd (int32 *data, int32 PA, int32 access) { int32 drv, dtype, i, j; drv = GET_UNIT (rpcs2); /* get current unit */ dtype = GET_DTYPE (rp_unit[drv].flags); /* get drive type */ j = (PA >> 1) & 037; /* get reg offset */ if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ rpcs2 = rpcs2 | CS2_NED; /* set error flag */ update_rpcs (CS1_SC, drv); /* request intr */ *data = 0; return SCPE_OK; } update_rpcs (0, drv); /* update status */ switch (j) { /* decode PA<5:1> */ case 000: /* RPCS1 */ *data = rpcs1; break; case 001: /* RPWC */ *data = rpwc; break; case 002: /* RPBA */ *data = rpba = rpba & ~BA_MBZ; break; case 003: /* RPDA */ *data = rpda[drv] = rpda[drv] & ~DA_MBZ; break; case 004: /* RPCS2 */ *data = rpcs2 = (rpcs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; break; case 005: /* RPDS */ *data = rpds[drv]; break; case 006: /* RPER1 */ *data = rper1[drv]; break; case 007: /* RPAS */ *data = 0; for (i = 0; i < RP_NUMDR; i++) if (rpds[i] & DS_ATA) *data = *data | (AS_U0 << i); break; case 010: /* RPLA */ *data = GET_SECTOR (rp_rwait, dtype) << LA_V_SC; break; case 011: /* RPDB */ *data = rpdb; break; case 012: /* RPMR */ *data = rpmr[drv]; break; case 013: /* RPDT */ *data = drv_tab[dtype].devtype; break; case 014: /* RPSN */ *data = 020 | (drv + 1); break; case 015: /* RPOF */ *data = rpof[drv] = rpof[drv] & ~OF_MBZ; break; case 016: /* RPDC */ *data = rpdc[drv] = rpdc[drv] & ~DC_MBZ; break; case 017: /* RPCC, RMHR */ if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is CC */ *data = rp_unit[drv].CYL; else *data = rmhr[drv] ^ 0177777; /* RM is HR */ break; case 020: /* RPER2, RMMR2 */ if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is ER2 */ *data = rper2[drv]; else *data = rmmr2[drv]; /* RM is MR2 */ break; case 021: /* RPER3, RMER2 */ if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is ER3 */ *data = rper3[drv]; else *data = rper2[drv]; /* RM is ER2 */ break; case 022: /* RPEC1 */ *data = rpec1[drv]; break; case 023: /* RPEC2 */ *data = rpec2[drv]; break; default: /* all others */ set_rper (ER1_ILR, drv); update_rpcs (0, drv); break; } return SCPE_OK; } t_stat rp_wr (int32 data, int32 PA, int32 access) { int32 cs1f, drv, dtype, i, j; UNIT *uptr; cs1f = 0; /* no int on cs1 upd */ drv = GET_UNIT (rpcs2); /* get current unit */ dtype = GET_DTYPE (rp_unit[drv].flags); /* get drive type */ uptr = rp_dev.units + drv; /* get unit */ j = (PA >> 1) & 037; /* get reg offset */ if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ rpcs2 = rpcs2 | CS2_NED; /* set error flag */ update_rpcs (CS1_SC, drv); /* request intr */ return SCPE_OK; } if (reg_in_drive[j] && sim_is_active (&rp_unit[drv])) { /* unit busy? */ set_rper (ER1_RMR, drv); /* won't write */ update_rpcs (0, drv); return SCPE_OK; } rmhr[drv] = data; switch (j) { /* decode PA<5:1> */ case 000: /* RPCS1 */ if ((access == WRITEB) && (PA & 1)) data = data << 8; if (data & CS1_TRE) { /* error clear? */ rpcs1 = rpcs1 & ~CS1_TRE; /* clr CS1 */ rpcs2 = rpcs2 & ~CS2_ERR; /* clr CS2<15:8> */ } if ((access == WRITE) || (PA & 1)) { /* hi byte write? */ if (rpcs1 & CS1_DONE) /* done set? */ rpcs1 = (rpcs1 & ~CS1_UAE) | (data & CS1_UAE); } if ((access == WRITE) || !(PA & 1)) { /* lo byte write? */ if ((data & CS1_DONE) && (data & CS1_IE)) /* to DONE+IE? */ rpiff = 1; /* set CSTB INTR */ rpcs1 = (rpcs1 & ~CS1_IE) | (data & CS1_IE); if (uptr->flags & UNIT_DIS) { /* nx disk? */ rpcs2 = rpcs2 | CS2_NED; /* set error flag */ cs1f = CS1_SC; /* req interrupt */ } else if (sim_is_active (uptr)) set_rper (ER1_RMR, drv); /* won't write */ else if (data & CS1_GO) { /* start op */ uptr->FUNC = GET_FNC (data); /* set func */ if ((uptr->FUNC >= FNC_XFER) && /* data xfer and */ ((rpcs1 & CS1_DONE) == 0)) /* ~rdy? PGE */ rpcs2 = rpcs2 | CS2_PGE; else rp_go (drv, uptr->FUNC); } } break; case 001: /* RPWC */ if (access == WRITEB) data = (PA & 1)? (rpwc & 0377) | (data << 8): (rpwc & ~0377) | data; rpwc = data; break; case 002: /* RPBA */ if (access == WRITEB) data = (PA & 1)? (rpba & 0377) | (data << 8): (rpba & ~0377) | data; rpba = data & ~BA_MBZ; break; case 003: /* RPDA */ if ((access == WRITEB) && (PA & 1)) data = data << 8; rpda[drv] = data & ~DA_MBZ; break; case 004: /* RPCS2 */ if ((access == WRITEB) && (PA & 1)) data = data << 8; if (data & CS2_CLR) /* init? */ rp_reset (&rp_dev); else { if ((data & ~rpcs2) & (CS2_PE | CS2_MXF)) cs1f = CS1_SC; /* diagn intr */ if (access == WRITEB) /* merge data */ data = (rpcs2 & ((PA & 1)? 0377: 0177400)) | data; rpcs2 = (rpcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; } drv = GET_UNIT (rpcs2); break; case 006: /* RPER1 */ if ((access == WRITEB) && (PA & 1)) data = data << 8; rper1[drv] = data; break; case 007: /* RPAS */ if ((access == WRITEB) && (PA & 1)) break; for (i = 0; i < RP_NUMDR; i++) if (data & (AS_U0 << i)) rpds[i] = rpds[i] & ~DS_ATA; break; case 011: /* RPDB */ if (access == WRITEB) data = (PA & 1)? (rpdb & 0377) | (data << 8): (rpdb & ~0377) | data; rpdb = data; break; case 012: /* RPMR */ if ((access == WRITEB) && (PA & 1)) data = data << 8; rpmr[drv] = data; break; case 015: /* RPOF */ rpof[drv] = data & ~OF_MBZ; break; case 016: /* RPDC */ if ((access == WRITEB) && (PA & 1)) data = data << 8; rpdc[drv] = data & ~DC_MBZ; break; case 005: /* RPDS */ case 010: /* RPLA */ case 013: /* RPDT */ case 014: /* RPSN */ case 017: /* RPCC, RMHR */ case 020: /* RPER2, RMMR2 */ case 021: /* RPER3, RMER2 */ case 022: /* RPEC1 */ case 023: /* RPEC2 */ break; /* read only */ default: /* all others */ set_rper (ER1_ILR, drv); break; } /* end switch */ update_rpcs (cs1f, drv); /* update status */ return SCPE_OK; } /* Initiate operation - unit not busy, function set */ void rp_go (int32 drv, int32 fnc) { int32 dc, dtype, t; UNIT *uptr; uptr = rp_dev.units + drv; /* get unit */ if (uptr->flags & UNIT_DIS) { /* nx unit? */ rpcs2 = rpcs2 | CS2_NED; /* set error flag */ update_rpcs (CS1_SC, drv); /* request intr */ return; } if ((fnc != FNC_DCLR) && (rpds[drv] & DS_ERR)) { /* err & ~clear? */ set_rper (ER1_ILF, drv); /* set err, ATN */ update_rpcs (CS1_SC, drv); /* request intr */ return; } dtype = GET_DTYPE (uptr->flags); /* get drive type */ rpds[drv] = rpds[drv] & ~DS_ATA; /* clear attention */ dc = rpdc[drv]; /* assume seek, sch */ switch (fnc) { /* case on function */ case FNC_DCLR: /* drive clear */ rper1[drv] = rper2[drv] = rper3[drv] = 0; /* clear errors */ rpec2[drv] = 0; /* clear EC2 */ if (drv_tab[dtype].ctrl == MBA_RM_CTRL) /* RM? */ rpmr[drv] = 0; /* clear maint */ else rpec1[drv] = 0; /* RP, clear EC1 */ case FNC_NOP: /* no operation */ case FNC_RELEASE: /* port release */ return; case FNC_PRESET: /* read-in preset */ rpdc[drv] = 0; /* clear disk addr */ rpda[drv] = 0; rpof[drv] = 0; /* clear offset */ case FNC_PACK: /* pack acknowledge */ rpds[drv] = rpds[drv] | DS_VV; /* set volume valid */ return; case FNC_OFFSET: /* offset mode */ case FNC_RETURN: if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ set_rper (ER1_UNS, drv); /* unsafe */ break; } rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ sim_activate (uptr, rp_swait); /* time operation */ return; case FNC_UNLOAD: /* unload */ case FNC_RECAL: /* recalibrate */ dc = 0; /* seek to 0 */ case FNC_SEEK: /* seek */ case FNC_SEARCH: /* search */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ set_rper (ER1_UNS, drv); /* unsafe */ break; } if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ set_rper (ER1_IAE, drv); break; } rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ t = abs (dc - uptr->CYL); /* cyl diff */ if (t == 0) /* min time */ t = 1; sim_activate (uptr, rp_swait * t); /* schedule */ uptr->CYL = dc; /* save cylinder */ return; case FNC_WRITEH: /* write headers */ case FNC_WRITE: /* write */ case FNC_WCHK: /* write check */ case FNC_READ: /* read */ case FNC_READH: /* read headers */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ set_rper (ER1_UNS, drv); /* unsafe */ break; } rpcs2 = rpcs2 & ~CS2_ERR; /* clear errors */ rpcs1 = rpcs1 & ~(CS1_TRE | CS1_MCPE | CS1_DONE); if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ set_rper (ER1_IAE, drv); break; } rpds[drv] = rpds[drv] & ~DS_RDY; /* clear drive rdy */ sim_activate (uptr, rp_rwait + (rp_swait * abs (dc - uptr->CYL))); uptr->CYL = dc; /* save cylinder */ return; default: /* all others */ set_rper (ER1_ILF, drv); /* not supported */ break; } update_rpcs (CS1_SC, drv); /* req intr */ return; } /* Service unit timeout Complete movement or data transfer command Unit must exist - can't remove an active unit Unit must be attached - detach cancels in progress operations */ t_stat rp_svc (UNIT *uptr) { int32 i, dtype, drv, err; int32 ba, da, vpn; a10 pa10, mpa10; int32 wc10, twc10, awc10, fc10; static d10 dbuf[RP_MAXFR]; dtype = GET_DTYPE (uptr->flags); /* get drive type */ drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ switch (uptr->FUNC) { /* case on function */ case FNC_OFFSET: /* offset */ rpds[drv] = rpds[drv] | DS_OF | DS_ATA; /* set offset, attention */ update_rpcs (CS1_SC, drv); break; case FNC_RETURN: /* return to centerline */ rpds[drv] = (rpds[drv] & ~DS_OF) | DS_ATA; /* clear offset, set attn */ update_rpcs (CS1_SC, drv); break; case FNC_UNLOAD: /* unload */ rp_detach (uptr); /* detach unit */ break; case FNC_RECAL: /* recalibrate */ case FNC_SEARCH: /* search */ case FNC_SEEK: /* seek */ rpds[drv] = rpds[drv] | DS_ATA; /* set attention */ update_rpcs (CS1_SC, drv); break; /* Reads and writes must take into account the complicated relationship between Unibus addresses and PDP-10 memory addresses, and Unibus byte and word counts, PDP-10 UBA word counts, and simulator PDP-10 word counts (due to the fact that the simulator must transfer eight 8b bytes to do a 36b transfer, whereas the UBA did four 9b bytes). */ #define XWC_MBZ 0000001 /* wc<0> must be 0 */ #define XBA_MBZ 0000003 /* addr<1:0> must be 0 */ case FNC_WRITE: /* write */ if (uptr->flags & UNIT_WPRT) { /* write locked? */ set_rper (ER1_WLE, drv); /* set drive error */ update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ break; } case FNC_WCHK: /* write check */ case FNC_READ: /* read */ case FNC_READH: /* read headers */ ba = GET_UAE (rpcs1) | rpba; /* get byte addr */ wc10 = (0200000 - rpwc) >> 1; /* get PDP-10 wc */ da = GET_DA (rpdc[drv], rpda[drv], dtype) * RP_NUMWD; /* get disk addr */ if ((da + wc10) > drv_tab[dtype].size) { /* disk overrun? */ set_rper (ER1_AOE, drv); if (wc10 > (drv_tab[dtype].size - da)) wc10 = drv_tab[dtype].size - da; } err = fseek (uptr->fileref, da * sizeof (d10), SEEK_SET); if (uptr->FUNC == FNC_WRITE) { /* write? */ for (twc10 = 0; twc10 < wc10; twc10++) { pa10 = ba >> 2; vpn = PAG_GETVPN (pa10); /* map addr */ if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { rpcs2 = rpcs2 | CS2_NEM; /* set error */ ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ break; } mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ rpcs2 = rpcs2 | CS2_NEM; /* set error */ ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ break; } dbuf[twc10] = M[mpa10]; /* write to disk */ if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; } if (fc10 = twc10 & (RP_NUMWD - 1)) { /* fill? */ fc10 = RP_NUMWD - fc10; for (i = 0; i < fc10; i++) dbuf[twc10 + i] = 0; } fxwrite (dbuf, sizeof (d10), twc10 + fc10, uptr->fileref); err = ferror (uptr->fileref); } /* end if */ else { /* read, wchk, readh */ awc10 = fxread (dbuf, sizeof (d10), wc10, uptr->fileref); err = ferror (uptr->fileref); for ( ; awc10 < wc10; awc10++) dbuf[awc10] = 0; for (twc10 = 0; twc10 < wc10; twc10++) { pa10 = ba >> 2; vpn = PAG_GETVPN (pa10); /* map addr */ if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { rpcs2 = rpcs2 | CS2_NEM; /* set error */ ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ break; } mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ rpcs2 = rpcs2 | CS2_NEM; /* set error */ ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ break; } if ((uptr->FUNC == FNC_READ) || /* read or */ (uptr->FUNC == FNC_READH)) /* read header */ M[mpa10] = dbuf[twc10]; else if (M[mpa10] != dbuf[twc10]) { /* wchk, mismatch? */ rpcs2 = rpcs2 | CS2_WCE; /* set error */ break; } if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; } } /* end else */ rpwc = (rpwc + (twc10 << 1)) & 0177777; /* final word count */ rpba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ rpcs1 = (rpcs1 & ~ CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); da = da + twc10 + (RP_NUMWD - 1); if (da >= drv_tab[dtype].size) rpds[drv] = rpds[drv] | DS_LST; da = da / RP_NUMWD; rpda[drv] = da % drv_tab[dtype].sect; da = da / drv_tab[dtype].sect; rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); rpdc[drv] = da / drv_tab[dtype].surf; if (err != 0) { /* error? */ set_rper (ER1_PAR, drv); /* set drive error */ update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ perror ("RP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } case FNC_WRITEH: /* write headers stub */ update_rpcs (CS1_DONE, drv); /* set done */ break; } /* end case func */ return SCPE_OK; } /* Set drive error */ void set_rper (int32 flag, int32 drv) { rper1[drv] = rper1[drv] | flag; rpds[drv] = rpds[drv] | DS_ATA; rpcs1 = rpcs1 | CS1_SC; return; } /* Controller status update Check for done transition Update drive status Update RPCS1 Update interrupt request */ void update_rpcs (int32 flag, int32 drv) { int32 i; UNIT *uptr; if ((flag & ~rpcs1) & CS1_DONE) /* DONE 0 to 1? */ rpiff = (rpcs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ uptr = rp_dev.units + drv; /* get unit */ if (rp_unit[drv].flags & UNIT_DIS) rpds[drv] = rper1[drv] = 0; else rpds[drv] = (rpds[drv] | DS_DPR) & ~DS_PGM; if (rp_unit[drv].flags & UNIT_ATT) rpds[drv] = rpds[drv] | DS_MOL; else rpds[drv] = rpds[drv] & ~(DS_MOL | DS_VV | DS_RDY); if (rper1[drv] | rper2[drv] | rper3[drv]) rpds[drv] = rpds[drv] | DS_ERR; else rpds[drv] = rpds[drv] & ~DS_ERR; rpcs1 = (rpcs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ | CS1_DRV)) | CS1_DVA | flag; rpcs1 = rpcs1 | (uptr->FUNC << CS1_V_FNC); if (sim_is_active (uptr)) rpcs1 = rpcs1 | CS1_GO; if (rpcs2 & CS2_ERR) rpcs1 = rpcs1 | CS1_TRE | CS1_SC; else if (rpcs1 & CS1_TRE) rpcs1 = rpcs1 | CS1_SC; for (i = 0; i < RP_NUMDR; i++) { if (rpds[i] & DS_ATA) rpcs1 = rpcs1 | CS1_SC; } if (rpiff || ((rpcs1 & CS1_SC) && (rpcs1 & CS1_DONE) && (rpcs1 & CS1_IE))) int_req = int_req | INT_RP; else int_req = int_req & ~INT_RP; return; } /* Interrupt acknowledge */ int32 rp_inta (void) { rpcs1 = rpcs1 & ~CS1_IE; /* clear int enable */ rpiff = 0; /* clear CSTB INTR */ return VEC_RP; /* acknowledge */ } /* Device reset */ t_stat rp_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rpcs1 = CS1_DVA | CS1_DONE; rpcs2 = CS2_IR | CS2_OR; rpba = rpwc = 0; rpiff = 0; /* clear CSTB INTR */ int_req = int_req & ~INT_RP; /* clear intr req */ for (i = 0; i < RP_NUMDR; i++) { uptr = rp_dev.units + i; sim_cancel (uptr); uptr->CYL = uptr->FUNC = 0; if (uptr->flags & UNIT_ATT) rpds[i] = (rpds[i] & DS_VV) | DS_DPR | DS_RDY | DS_MOL | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); else if (uptr->flags & UNIT_DIS) rpds[i] = 0; else rpds[i] = DS_DPR; rper1[i] = 0; rper2[i] = 0; rper3[i] = 0; rpda[i] = 0; rpdc[i] = 0; rpmr[i] = 0; rpof[i] = 0; rpec1[i] = 0; rpec2[i] = 0; rmmr2[i] = 0; rmhr[i] = 0; } return SCPE_OK; } /* Device attach */ t_stat rp_attach (UNIT *uptr, char *cptr) { int32 drv, i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); if (r != SCPE_OK) return r; drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = DS_ATA | DS_MOL | DS_RDY | DS_DPR | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); rper1[drv] = 0; update_rpcs (CS1_SC, drv); if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; if ((p = sim_fsize (uptr->fileref)) == 0) return SCPE_OK; for (i = 0; drv_tab[i].sect != 0; i++) { if (p <= (drv_tab[i].size * (int) sizeof (d10))) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Device detach */ t_stat rp_detach (UNIT *uptr) { int32 drv; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = (rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OF)) | DS_ATA; if (sim_is_active (uptr)) { /* unit active? */ sim_cancel (uptr); /* cancel operation */ rper1[drv] = rper1[drv] | ER1_OPI; /* set drive error */ if (uptr->FUNC >= FNC_WCHK) /* data transfer? */ rpcs1 = rpcs1 | CS1_DONE | CS1_TRE; /* set done, err */ } update_rpcs (CS1_SC, drv); /* request intr */ return detach_unit (uptr); } /* Set size command validation routine */ t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 dtype = GET_DTYPE (val); if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[dtype].size; return SCPE_OK; } /* Device bootstrap */ #define BOOT_START 0377000 /* start */ #define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) static const d10 boot_rom_dec[] = { 0515040000001, /* boot:hrlzi 1,1 ; uba # */ 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ 0713001000000+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ 0435040000000+(IOBA_RP & RMASK), /* iori 1,776700 ; rh addr */ 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ 0201000000040, /* movei 0,40 ; ctrl reset */ 0713001000010, /* wrio 0,10(1) ; ->RPCS2 */ 0201000000021, /* movei 0,21 ; preset */ 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ 0201100000001, /* movei 2,1 ; blk #1 */ 0265740377032, /* jsp 17,rdbl ; read */ 0204140001000, /* movs 3,1000 ; id word */ 0306140505755, /* cain 3,sixbit /HOM/ */ 0254000377023, /* jrst .+6 ; match */ 0201100000010, /* movei 2,10 ; blk #10 */ 0265740377032, /* jsp 17,rdbl ; read */ 0204140001000, /* movs 3,1000 ; id word */ 0302140505755, /* caie 3,sixbit /HOM/ */ 0254200377022, /* halt . ; inv home */ 0336100001103, /* skipn 2,1103 ; pg of ptrs */ 0254200377024, /* halt . ; inv ptr */ 0265740377032, /* jsp 17,rdbl ; read */ 0336100001004, /* skipn 2,1004 ; mon boot */ 0254200377027, /* halt . ; inv ptr */ 0265740377032, /* jsp 17,rdbl ; read */ 0254000001000, /* jrst 1000 ; start */ 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ 0201200004000, /* movei 4,4000 ; addr */ 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ 0200300000002, /* move 6,2 */ 0242300777750, /* lsh 6,-24. ; cyl */ 0713141000002, /* wrio 3,2(1) ; ->RPWC */ 0713201000004, /* wrio 4,4(1) ; ->RPBA */ 0713101000006, /* wrio 2,6(1) ; ->RPDA */ 0713241000010, /* wrio 5,10(1) ; ->RPCS2 */ 0713301000034, /* wrio 6,34(1) ; ->RPDC */ 0201000000071, /* movei 0,71 ; read+go */ 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ 0712341000000, /* rdio 7,0(1) ; read csr */ 0606340000200, /* trnn 7,200 ; test rdy */ 0254000377046, /* jrst .-2 ; loop */ 0602340100000, /* trne 7,100000 ; test err */ 0254200377052, /* halt */ 0254017000000, /* jrst 0(17) ; return */ }; static const d10 boot_rom_its[] = { 0515040000001, /* boot:hrlzi 1,1 ; uba # */ 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ 0715000000000+(IOBA_UBMAP+1 & RMASK), /* iowrq 0,763001 ; set ubmap */ 0435040000000+(IOBA_RP & RMASK), /* iori 1,776700 ; rh addr */ 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ 0201000000040, /* movei 0,40 ; ctrl reset */ 0715001000010, /* iowrq 0,10(1) ; ->RPCS2 */ 0201000000021, /* movei 0,21 ; preset */ 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ 0201100000001, /* movei 2,1 ; blk #1 */ 0265740377032, /* jsp 17,rdbl ; read */ 0204140001000, /* movs 3,1000 ; id word */ 0306140505755, /* cain 3,sixbit /HOM/ */ 0254000377023, /* jrst .+6 ; match */ 0201100000010, /* movei 2,10 ; blk #10 */ 0265740377032, /* jsp 17,rdbl ; read */ 0204140001000, /* movs 3,1000 ; id word */ 0302140505755, /* caie 3,sixbit /HOM/ */ 0254200377022, /* halt . ; inv home */ 0336100001103, /* skipn 2,1103 ; pg of ptrs */ 0254200377024, /* halt . ; inv ptr */ 0265740377032, /* jsp 17,rdbl ; read */ 0336100001004, /* skipn 2,1004 ; mon boot */ 0254200377027, /* halt . ; inv ptr */ 0265740377032, /* jsp 17,rdbl ; read */ 0254000001000, /* jrst 1000 ; start */ 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ 0201200004000, /* movei 4,4000 ; addr */ 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ 0200300000002, /* move 6,2 */ 0242300777750, /* lsh 6,-24. ; cyl */ 0715141000002, /* iowrq 3,2(1) ; ->RPWC */ 0715201000004, /* iowrq 4,4(1) ; ->RPBA */ 0715101000006, /* iowrq 2,6(1) ; ->RPDA */ 0715241000010, /* iowrq 5,10(1) ; ->RPCS2 */ 0715301000034, /* iowrq 6,34(1) ; ->RPDC */ 0201000000071, /* movei 0,71 ; read+go */ 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ 0711341000000, /* iordq 7,0(1) ; read csr */ 0606340000200, /* trnn 7,200 ; test rdy */ 0254000377046, /* jrst .-2 ; loop */ 0602340100000, /* trne 7,100000 ; test err */ 0254200377052, /* halt */ 0254017000000, /* jrst 0(17) ; return */ }; t_stat rp_boot (int32 unitno, DEVICE *dptr) { int32 i; extern a10 saved_PC; M[FE_UNIT] = unitno & CS2_M_UNIT; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = Q_ITS? boot_rom_its[i]: boot_rom_dec[i]; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP10/pdp10_tu.c0000644000175000017500000016172211112112574013231 0ustar vlmvlm/* pdp10_tu.c - PDP-10 RH11/TM03/TU45 magnetic tape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tu RH11/TM03/TU45 magtape 29-Apr-07 RMS Fixed bug in setting FCE on TMK (found by Naoki Hamada) 16-Feb-06 RMS Added tape capacity checking 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 31-Mar-05 RMS Fixed bug, ERASE/WREOF incorrectly clear CS1 Fixed inaccuracies in error reporting 18-Mar-05 RMS Added attached test to detach routine 23-Oct-04 RMS Fixed setting done on non data transfers 01-Oct-04 RMS Modified to set FCE on read short record, eof Implemented write check TM03 uses only den<2> for validity test TMK is cleared by new motion command, not DCLR 14-Sep-04 RMS Fixed RIP value 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library 27-Jan-03 RMS Changed to dynamically allocate buffer 21-Nov-02 RMS Fixed bug in bootstrap (reported by Michael Thompson) Fixed bug in read (reported by Harris Newman) 29-Sep-02 RMS Added variable vector support New data structures 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Changed record length error code 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed POS, FLG, UST to arrays 23-Oct-01 RMS Fixed bug in error interrupts New IO page address constants 05-Oct-01 RMS Rewrote interrupt handling from schematics 30-Sep-01 RMS Fixed handling of non-existent formatters 28-Sep-01 RMS Fixed interrupt handling for SC/ATA 4-May-01 RMS Fixed bug in odd address test 3-May-01 RMS Fixed drive reset to clear SSC Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number, sign = error byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number, sign = error If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. WARNING: The interupt logic of the RH11/RH70 is unusual and must be simulated with great precision. The RH11 has an internal interrupt request flop, CSTB INTR, which is controlled as follows: - Writing IE and DONE simultaneously sets CSTB INTR - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR (and also clear IE) - A transition of DONE from 0 to 1 sets CSTB from INTR The output of INTR is OR'd with the AND of RPCS1 to create the interrupt request signal. Thus, - The DONE interrupt is edge sensitive, but the SC interrupt is level sensitive. - The DONE interrupt, once set, is not disabled if IE is cleared, but the SC interrupt is. */ #include "pdp10_defs.h" #include "sim_tape.h" #define TU_NUMFM 1 /* #formatters */ #define TU_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ #define UDENS u4 /* unit density */ #define UD_UNK 0 /* unknown */ #define MT_MAXFR (1 << 16) /* max data buf */ /* MTCS1 - 172440 - control/status 1 */ #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_N_FNC (CS1_M_FNC + 1) #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) #define FNC_NOP 000 /* no operation */ #define FNC_UNLOAD 001 /* unload */ #define FNC_REWIND 003 /* rewind */ #define FNC_FCLR 004 /* formatter clear */ #define FNC_RIP 010 /* read in preset */ #define FNC_ERASE 012 /* erase tape */ #define FNC_WREOF 013 /* write tape mark */ #define FNC_SPACEF 014 /* space forward */ #define FNC_SPACER 015 /* space reverse */ #define FNC_XFER 024 /* >=? data xfr */ #define FNC_WCHKF 024 /* write check */ #define FNC_WCHKR 027 /* write check rev */ #define FNC_WRITE 030 /* write */ #define FNC_READF 034 /* read forward */ #define FNC_READR 037 /* read reverse */ #define CS1_IE CSR_IE /* int enable */ #define CS1_DONE CSR_DONE /* ready */ #define CS1_V_UAE 8 /* Unibus addr ext */ #define CS1_M_UAE 03 #define CS1_UAE (CS1_M_UAE << CS1_V_UAE) #define CS1_DVA 0004000 /* drive avail NI */ #define CS1_MCPE 0020000 /* Mbus par err NI */ #define CS1_TRE 0040000 /* transfer err */ #define CS1_SC 0100000 /* special cond */ #define CS1_MBZ 0012000 #define CS1_DRV (CS1_FNC | CS1_GO) #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) #define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) /* MTWC - 172442 - word count */ /* MTBA - 172444 - base address */ #define BA_MBZ 0000001 /* must be zero */ /* MTFC - 172446 - frame count */ /* MTCS2 - 172450 - control/status 2 */ #define CS2_V_FMTR 0 /* formatter select */ #define CS2_M_FMTR 07 #define CS2_FMTR (CS2_M_FMTR << CS2_V_FMTR) #define CS2_UAI 0000010 /* addr inhibit NI */ #define CS2_PAT 0000020 /* parity test NI */ #define CS2_CLR 0000040 /* controller clear */ #define CS2_IR 0000100 /* input ready */ #define CS2_OR 0000200 /* output ready */ #define CS2_MDPE 0000400 /* Mbus par err NI */ #define CS2_MXF 0001000 /* missed xfer NI */ #define CS2_PGE 0002000 /* program err */ #define CS2_NEM 0004000 /* nx mem err */ #define CS2_NEF 0010000 /* nx fmter err */ #define CS2_PE 0020000 /* parity err NI */ #define CS2_WCE 0040000 /* write chk err */ #define CS2_DLT 0100000 /* data late NI */ #define CS2_MBZ (CS2_CLR | CS2_WCE) #define CS2_RW (CS2_FMTR | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) #define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ CS2_NEF | CS2_PE | CS2_DLT ) #define GET_FMTR(x) (((x) >> CS2_V_FMTR) & CS2_M_FMTR) /* MTFS - 172452 - formatter status + indicates kept in drive status ^ indicates calculated on the fly */ #define FS_SAT 0000001 /* slave attention */ #define FS_BOT 0000002 /* ^beginning of tape */ #define FS_TMK 0000004 /* end of file */ #define FS_ID 0000010 /* ID burst detected */ #define FS_SLOW 0000020 /* slowing down NI */ #define FS_PE 0000040 /* ^PE status */ #define FS_SSC 0000100 /* slave stat change */ #define FS_RDY 0000200 /* ^formatter ready */ #define FS_FPR 0000400 /* formatter present */ #define FS_EOT 0002000 /* +end of tape */ #define FS_WRL 0004000 /* ^write locked */ #define FS_MOL 0010000 /* ^medium online */ #define FS_PIP 0020000 /* +pos in progress */ #define FS_ERR 0040000 /* ^error */ #define FS_ATA 0100000 /* attention active */ #define FS_REW 0200000 /* +rewinding */ #define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ FS_RDY | FS_PE | FS_BOT) /* MTER - 172454 - error register */ #define ER_ILF 0000001 /* illegal func */ #define ER_ILR 0000002 /* illegal register */ #define ER_RMR 0000004 /* reg mod refused */ #define ER_MCP 0000010 /* Mbus cpar err NI */ #define ER_FER 0000020 /* format sel err */ #define ER_MDP 0000040 /* Mbus dpar err NI */ #define ER_VPE 0000100 /* vert parity err */ #define ER_CRC 0000200 /* CRC err NI */ #define ER_NSG 0000400 /* non std gap err NI */ #define ER_FCE 0001000 /* frame count err */ #define ER_ITM 0002000 /* inv tape mark NI */ #define ER_NXF 0004000 /* wlock or fnc err */ #define ER_DTE 0010000 /* time err NI */ #define ER_OPI 0020000 /* op incomplete */ #define ER_UNS 0040000 /* drive unsafe */ #define ER_DCK 0100000 /* data check NI */ /* MTAS - 172456 - attention summary */ #define AS_U0 0000001 /* unit 0 flag */ /* MTCC - 172460 - check character, read only */ #define CC_MBZ 0177000 /* must be zero */ /* MTDB - 172462 - data buffer */ /* MTMR - 172464 - maintenance register */ #define MR_RW 0177637 /* read/write */ /* MTDT - 172466 - drive type */ #define DT_NSA 0100000 /* not sect addr */ #define DT_TAPE 0040000 /* tape */ #define DT_PRES 0002000 /* slave present */ #define DT_TM03 0000040 /* TM03 formatter */ #define DT_OFF 0000010 /* drive off */ #define DT_TE16 0000011 /* TE16 */ #define DT_TU45 0000012 /* TU45 */ #define DT_TU77 0000014 /* TU77 */ /* MTSN - 172470 - serial number */ /* MTTC - 172472 - tape control register */ #define TC_V_UNIT 0 /* unit select */ #define TC_M_UNIT 07 #define TC_V_EVN 0000010 /* even parity */ #define TC_V_FMT 4 /* format select */ #define TC_M_FMT 017 #define TC_10C 00 /* PDP-10 core dump */ #define TC_IND 03 /* industry standard */ #define TC_V_DEN 8 /* density select */ #define TC_M_DEN 07 #define TC_800 3 /* 800 bpi */ #define TC_1600 4 /* 1600 bpi */ #define TC_AER 0010000 /* abort on error */ #define TC_SAC 0020000 /* slave addr change */ #define TC_FCS 0040000 /* frame count status */ #define TC_ACC 0100000 /* accelerating NI */ #define TC_RW 0013777 #define TC_MBZ 0004000 #define TC_RIP ((TC_800 << TC_V_DEN) || (TC_10C << TC_V_FMT)) #define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) #define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) #define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) /* Mapping macros */ #define XWC_MBZ 0000001 /* wc<0> mbz */ #define XBA_MBZ 0000001 /* addr<0> mbz */ #define XBA_ODD 0000002 /* odd address */ #define TXFR(b,w,od) if (((b) & XBA_MBZ) || ((w) & XWC_MBZ) || \ (((b) & XBA_ODD) != ((od) << 1))) { \ tucs2 = tucs2 | CS2_NEM; \ ubcs[1] = ubcs[1] | UBCS_TMO; \ tucs1 = tucs1 & ~CS1_GO; \ update_tucs (CS1_DONE, drv); \ return SCPE_OK; \ } #define NEWPAGE(v,m) (((v) & PAG_M_OFF) == (m)) #define MAPM(v,p,f) vpn = PAG_GETVPN (v); \ if ((vpn >= UMAP_MEMSIZE) || ((ubmap[1][vpn] & \ (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != \ (UMAP_VLD | f))) { \ tucs2 = tucs2 | CS2_NEM; \ ubcs[1] = ubcs[1] | UBCS_TMO; \ break; \ } \ p = (ubmap[1][vpn] + PAG_GETOFF (v)) & PAMASK; \ if (MEM_ADDR_NXM (p)) { \ tucs2 = tucs2 | CS2_NEM; \ ubcs[1] = ubcs[1] | UBCS_TMO; \ break; \ } extern d10 *M; /* memory */ extern int32 int_req; extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus map */ extern int32 ubcs[UBANUM]; extern UNIT cpu_unit; extern int32 sim_switches; extern FILE *sim_deb; int32 tucs1 = 0; /* control/status 1 */ int32 tuwc = 0; /* word count */ int32 tuba = 0; /* bus address */ int32 tufc = 0; /* frame count */ int32 tucs2 = 0; /* control/status 2 */ int32 tufs = 0; /* formatter status */ int32 tuer = 0; /* error status */ int32 tucc = 0; /* check character */ int32 tudb = 0; /* data buffer */ int32 tumr = 0; /* maint register */ int32 tutc = 0; /* tape control */ int32 tuiff = 0; /* INTR flip/flop */ int32 tu_time = 10; /* record latency */ int32 tu_stopioe = 1; /* stop on error */ int32 tu_log = 0; /* debug */ int32 reg_in_fmtr[32] = { /* reg in formatter */ 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int32 reg_in_fmtr1[32] = { /* rmr if write + go */ 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int32 fmt_test[16] = { /* fmt bytes/10 wd */ 5, 0, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static char *tu_fname[CS1_N_FNC] = { "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7", "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17", "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR", "WRITE", "31", "32", "33", "READF", "35", "36" "READR" }; static uint8 *xbuf = NULL; /* xfer buffer */ t_stat tu_rd (int32 *data, int32 PA, int32 access); t_stat tu_wr (int32 data, int32 PA, int32 access); int32 tu_inta (void); t_stat tu_svc (UNIT *uptr); t_stat tu_reset (DEVICE *dptr); t_stat tu_attach (UNIT *uptr, char *cptr); t_stat tu_detach (UNIT *uptr); t_stat tu_boot (int32 unitno, DEVICE *dptr); void tu_go (int32 drv); void set_tuer (int32 flag); void update_tucs (int32 flag, int32 drv); t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt); /* TU data structures tu_dev TU device descriptor tu_unit TU unit list tu_reg TU register list tu_mod TU modifier list */ DIB tu_dib = { IOBA_TU, IOLN_TU, &tu_rd, &tu_wr, 1, IVCL (TU), VEC_TU, { &tu_inta } }; UNIT tu_unit[] = { { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } }; REG tu_reg[] = { { ORDATA (MTCS1, tucs1, 16) }, { ORDATA (MTWC, tuwc, 16) }, { ORDATA (MTBA, tuba, 16) }, { ORDATA (MTFC, tufc, 16) }, { ORDATA (MTCS2, tucs2, 16) }, { ORDATA (MTFS, tufs, 16) }, { ORDATA (MTER, tuer, 16) }, { ORDATA (MTCC, tucc, 16) }, { ORDATA (MTDB, tudb, 16) }, { ORDATA (MTMR, tumr, 16) }, { ORDATA (MTTC, tutc, 16) }, { FLDATA (IFF, tuiff, 0) }, { FLDATA (INT, int_req, INT_V_TU) }, { FLDATA (DONE, tucs1, CSR_V_DONE) }, { FLDATA (IE, tucs1, CSR_V_IE) }, { FLDATA (STOP_IOE, tu_stopioe, 0) }, { DRDATA (TIME, tu_time, 24), PV_LEFT }, { URDATA (UST, tu_unit[0].USTAT, 8, 17, 0, TU_NUMDR, 0) }, { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0, TU_NUMDR, PV_LEFT | REG_RO) }, { ORDATA (LOG, tu_log, 8), REG_HIDDEN }, { NULL } }; MTAB tu_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE tu_dev = { "TU", tu_unit, tu_reg, tu_mod, TU_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &tu_reset, &tu_boot, &tu_attach, &tu_detach, &tu_dib, DEV_UBUS | DEV_DEBUG }; /* I/O dispatch routine, I/O addresses 17772440 - 17772472 */ t_stat tu_rd (int32 *data, int32 PA, int32 access) { int32 fmtr, drv, j; fmtr = GET_FMTR (tucs2); /* get current fmtr */ drv = GET_DRV (tutc); /* get current drive */ j = (PA >> 1) & 017; /* get reg offset */ if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ tucs2 = tucs2 | CS2_NEF; /* set error flag */ update_tucs (CS1_SC, drv); /* request intr */ *data = 0; return SCPE_OK; } update_tucs (0, drv); /* update status */ switch (j) { /* decode PA<4:1> */ case 000: /* MTCS1 */ if (fmtr != 0) *data = tucs1 & ~CS1_DRV; else *data = tucs1; break; case 001: /* MTWC */ *data = tuwc; break; case 002: /* MTBA */ *data = tuba = tuba & ~BA_MBZ; break; case 003: /* MTFC */ *data = tufc; break; case 004: /* MTCS2 */ *data = tucs2 = (tucs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; break; case 005: /* MTFS */ *data = tufs & 0177777; /* mask off rewind */ break; case 006: /* MTER */ *data = tuer; break; case 007: /* MTAS */ *data = (tufs & FS_ATA)? AS_U0: 0; break; case 010: /* MTCC */ *data = tucc = tucc & ~CC_MBZ; break; case 011: /* MTDB */ *data = tudb; break; case 012: /* MTMR */ *data = tumr; break; case 013: /* MTDT */ *data = DT_NSA | DT_TAPE | DT_TM03 | ((tu_unit[drv].flags & UNIT_DIS)? DT_OFF: (DT_PRES | DT_TU45)); break; case 014: /* MTSN */ *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); break; case 015: /* MTTC */ *data = tutc = tutc & ~TC_MBZ; break; default: /* all others */ set_tuer (ER_ILR); update_tucs (0, drv); break; } return SCPE_OK; } t_stat tu_wr (int32 data, int32 PA, int32 access) { int32 cs1f, fmtr, drv, j; cs1f = 0; /* no int on cs1 upd */ fmtr = GET_FMTR (tucs2); /* get formatter */ drv = GET_DRV (tutc); /* get current unit */ j = (PA >> 1) & 017; /* get reg offset */ if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ tucs2 = tucs2 | CS2_NEF; /* set error flag */ update_tucs (CS1_SC, drv); /* request intr */ return SCPE_OK; } if (reg_in_fmtr1[j] && ((tucs1 & CS1_DONE) == 0)) { /* formatter busy? */ set_tuer (ER_RMR); /* won't write */ update_tucs (0, drv); return SCPE_OK; } switch (j) { /* decode PA<4:1> */ case 000: /* MTCS1 */ if ((access == WRITEB) && (PA & 1)) data = data << 8; if (data & CS1_TRE) { /* error clear? */ tucs1 = tucs1 & ~CS1_TRE; /* clr CS1 */ tucs2 = tucs2 & ~CS2_ERR; /* clr CS2<15:8> */ } if ((access == WRITE) || (PA & 1)) { /* hi byte write? */ if (tucs1 & CS1_DONE) /* done set? */ tucs1 = (tucs1 & ~CS1_UAE) | (data & CS1_UAE); } if ((access == WRITE) || !(PA & 1)) { /* lo byte write? */ if ((data & CS1_DONE) && (data & CS1_IE)) /* to DONE+IE? */ tuiff = 1; /* set CSTB INTR */ tucs1 = (tucs1 & ~CS1_IE) | (data & CS1_IE); if (fmtr != 0) { /* nx formatter? */ tucs2 = tucs2 | CS2_NEF; /* set error flag */ cs1f = CS1_SC; /* req interrupt */ } else if (tucs1 & CS1_GO) { /* busy? */ if (tucs1 & CS1_DONE) set_tuer (ER_RMR); else tucs2 = tucs2 | CS2_PGE; } else { tucs1 = (tucs1 & ~CS1_DRV) | (data & CS1_DRV); if (tucs1 & CS1_GO) tu_go (drv); } } break; case 001: /* MTWC */ if (access == WRITEB) data = (PA & 1)? (tuwc & 0377) | (data << 8): (tuwc & ~0377) | data; tuwc = data; break; case 002: /* MTBA */ if (access == WRITEB) data = (PA & 1)? (tuba & 0377) | (data << 8): (tuba & ~0377) | data; tuba = data & ~BA_MBZ; break; case 003: /* MTFC */ if (access == WRITEB) data = (PA & 1)? (tufc & 0377) | (data << 8): (tufc & ~0377) | data; tufc = data; tutc = tutc | TC_FCS; /* set fc flag */ break; case 004: /* MTCS2 */ if ((access == WRITEB) && (PA & 1)) data = data << 8; if (data & CS2_CLR) /* init? */ tu_reset (&tu_dev); else { if ((data & ~tucs2) & (CS2_PE | CS2_MXF)) cs1f = CS1_SC; /* diagn intr */ if (access == WRITEB) /* merge data */ data = (tucs2 & ((PA & 1)? 0377: 0177400)) | data; tucs2 = (tucs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; } break; case 007: /* MTAS */ if ((access == WRITEB) && (PA & 1)) break; if (data & AS_U0) tufs = tufs & ~FS_ATA; break; case 011: /* MTDB */ if (access == WRITEB) data = (PA & 1)? (tudb & 0377) | (data << 8): (tudb & ~0377) | data; tudb = data; break; case 012: /* MTMR */ if (access == WRITEB) data = (PA & 1)? (tumr & 0377) | (data << 8): (tumr & ~0377) | data; tumr = (tumr & ~MR_RW) | (data & MR_RW); break; case 015: /* MTTC */ if (access == WRITEB) data = (PA & 1)? (tutc & 0377) | (data << 8): (tutc & ~0377) | data; tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; drv = GET_DRV (tutc); break; case 005: /* MTFS */ case 006: /* MTER */ case 010: /* MTCC */ case 013: /* MTDT */ case 014: /* MTSN */ break; /* read only */ default: /* all others */ set_tuer (ER_ILR); break; } /* end switch */ update_tucs (cs1f, drv); /* update status */ return SCPE_OK; } /* New magtape command */ void tu_go (int32 drv) { int32 fnc, den; UNIT *uptr; fnc = GET_FNC (tucs1); /* get function */ den = GET_DEN (tutc); /* get density */ uptr = tu_dev.units + drv; /* get unit */ if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, ">>TU%d STRT: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); if ((fnc != FNC_FCLR) && /* not clear & err */ ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ set_tuer (ER_ILF); /* set err, ATN */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ update_tucs (CS1_SC, drv); /* request intr */ return; } tufs = tufs & ~FS_ATA; /* clear attention */ tutc = tutc & ~TC_SAC; /* clear addr change */ switch (fnc) { /* case on function */ case FNC_FCLR: /* drive clear */ tuer = 0; /* clear errors */ tutc = tutc & ~TC_FCS; /* clear fc status */ tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR); sim_cancel (uptr); /* reset drive */ uptr->USTAT = 0; case FNC_NOP: tucs1 = tucs1 & ~CS1_GO; /* no operation */ return; case FNC_RIP: /* read-in preset */ tutc = TC_RIP; /* density = 800 */ sim_tape_rewind (&tu_unit[0]); /* rewind unit 0 */ tu_unit[0].USTAT = 0; tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return; case FNC_UNLOAD: /* unload */ if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } detach_unit (uptr); uptr->USTAT = FS_REW; sim_activate (uptr, tu_time); tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return; case FNC_REWIND: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } uptr->USTAT = FS_PIP | FS_REW; sim_activate (uptr, tu_time); tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return; case FNC_SPACEF: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) { set_tuer (ER_NXF); break; } uptr->USTAT = FS_PIP; goto GO_XFER; case FNC_SPACER: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) { set_tuer (ER_NXF); break; } uptr->USTAT = FS_PIP; goto GO_XFER; case FNC_WREOF: /* write tape mark */ case FNC_ERASE: /* erase */ if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } if (sim_tape_wrp (uptr)) { /* write locked? */ set_tuer (ER_NXF); break; } if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ set_tuer (ER_FER); break; } if (uptr->UDENS == UD_UNK) /* set dens */ uptr->UDENS = den; uptr->USTAT = 0; goto GO_XFER; case FNC_WCHKR: /* wchk = read */ case FNC_READR: /* read rev */ if (tufs & FS_BOT) { /* beginning of tape? */ set_tuer (ER_NXF); break; } goto DATA_XFER; case FNC_WRITE: /* write */ if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ set_tuer (ER_NXF); break; } case FNC_WCHKF: /* wchk = read */ case FNC_READF: /* read */ DATA_XFER: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ set_tuer (ER_UNS); break; } if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ set_tuer (ER_FER); break; } if (uptr->UDENS == UD_UNK) /* set dens */ uptr->UDENS = den; uptr->USTAT = 0; tucs1 = tucs1 & ~CS1_DONE; /* clear done */ GO_XFER: tucs2 = tucs2 & ~CS2_ERR; /* clear errors */ tucs1 = tucs1 & ~(CS1_TRE | CS1_MCPE); tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ sim_activate (uptr, tu_time); return; default: /* all others */ set_tuer (ER_ILF); /* not supported */ break; } /* end case function */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ update_tucs (CS1_SC, drv); /* set intr */ return; } /* Unit service Complete movement or data transfer command Unit must exist - can't remove an active unit Unit must be attached - detach cancels in progress operations */ t_stat tu_svc (UNIT *uptr) { int32 fnc, fmt, i, j, k, wc10, ba10; int32 ba, fc, wc, drv, mpa10, vpn; d10 val, v[4]; t_mtrlnt tbc; t_stat st, r = SCPE_OK; drv = (int32) (uptr - tu_dev.units); /* get drive # */ if (uptr->USTAT & FS_REW) { /* rewind or unload? */ sim_tape_rewind (uptr); /* rewind tape */ uptr->USTAT = 0; /* clear status */ tufs = tufs | FS_ATA | FS_SSC; update_tucs (CS1_SC, drv); /* update status */ return SCPE_OK; } fnc = GET_FNC (tucs1); /* get command */ fmt = GET_FMT (tutc); /* get format */ ba = GET_UAE (tucs1) | tuba; /* get bus address */ wc = 0200000 - tuwc; /* get word count */ fc = 0200000 - tufc; /* get frame count */ wc10 = wc >> 1; /* 10 word count */ ba10 = ba >> 2; /* 10 word addr */ uptr->USTAT = 0; /* clear status */ switch (fnc) { /* case on function */ /* Non-data transfer commands - set ATA when done */ case FNC_SPACEF: /* space forward */ do { tufc = (tufc + 1) & 0177777; /* incr fc */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ r = tu_map_err (uptr, st, 0); /* map error */ break; } } while ((tufc != 0) && !sim_tape_eot (uptr)); if (tufc) set_tuer (ER_FCE); else tutc = tutc & ~TC_FCS; tufs = tufs | FS_ATA; break; case FNC_SPACER: /* space reverse */ do { tufc = (tufc + 1) & 0177777; /* incr wc */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ r = tu_map_err (uptr, st, 0); /* map error */ break; } } while (tufc != 0); if (tufc) set_tuer (ER_FCE); else tutc = tutc & ~TC_FCS; tufs = tufs | FS_ATA; break; case FNC_WREOF: /* write end of file */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = tu_map_err (uptr, st, 0); /* map error */ tufs = tufs | FS_ATA; break; case FNC_ERASE: if (sim_tape_wrp (uptr)) /* write protected? */ r = tu_map_err (uptr, MTSE_WRP, 0); /* map error */ tufs = tufs | FS_ATA; break; /* Data transfer commands These commands must take into account the action of the "bit fiddler", which converts between PDP-10 format and tape format. Only two tape formats are supported: PDP-10 core dump: write 36b as byte 0/byte 1/byte 2/byte 3/0000'last nibble industry mode: write hi 32b as byte 0/byte 1/byte 2/byte 3 These commands must also take into account the action of the Unibus adapter, which munges PDP-10 addresses through the Unibus map. */ case FNC_READF: /* read */ case FNC_WCHKF: /* wcheck = read */ tufc = 0; /* clear frame count */ if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr)) tufs = tufs | FS_ID; /* PE BOT? ID burst */ TXFR (ba, wc, 0); /* validate transfer */ if (st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR)) { /* read fwd */ if (st == MTSE_TMK) /* TMK also sets FCE */ set_tuer (ER_FCE); r = tu_map_err (uptr, st, 1); /* map error */ break; /* done */ } for (i = j = 0; (i < wc10) && (j < ((int32) tbc)); i++) { if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ MAPM (ba10 + i, mpa10, 0); } for (k = 0; k < 4; k++) v[k] = xbuf[j++]; val = (v[0] << 28) | (v[1] << 20) | (v[2] << 12) | (v[3] << 4); if (fmt == TC_10C) val = val | ((d10) xbuf[j++] & 017); if (fnc == FNC_READF) /* read? store */ M[mpa10] = val; else if (M[mpa10] != val) { /* wchk, mismatch? */ tucs2 = tucs2 | CS2_WCE; /* flag, stop */ break; } mpa10 = mpa10 + 1; } /* end for */ tufc = tbc & 0177777; tuwc = (tuwc + (i << 1)) & 0177777; ba = ba + (i << 2); if (tuwc) /* short record? */ set_tuer (ER_FCE); break; case FNC_WRITE: /* write */ TXFR (ba, wc, 0); /* validate transfer */ for (i = j = 0; (i < wc10) && (j < fc); i++) { if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ MAPM (ba10 + i, mpa10, 0); } val = M[mpa10]; xbuf[j++] = (uint8) ((val >> 28) & 0377); xbuf[j++] = (uint8) ((val >> 20) & 0377); xbuf[j++] = (uint8) ((val >> 12) & 0377); xbuf[j++] = (uint8) ((val >> 4) & 0377); if (fmt == TC_10C) xbuf[j++] = (uint8) (val & 017); mpa10 = mpa10 + 1; } /* end for */ if (j < fc) /* short record? */ fc = j; if (st = sim_tape_wrrecf (uptr, xbuf, fc)) /* write rec, err? */ r = tu_map_err (uptr, st, 1); /* map error */ else { tufc = (tufc + fc) & 0177777; if (tufc == 0) tutc = tutc & ~TC_FCS; tuwc = (tuwc + (i << 1)) & 0177777; ba = ba + (i << 2); } break; case FNC_READR: /* read reverse */ case FNC_WCHKR: /* wcheck = read */ tufc = 0; /* clear frame count */ TXFR (ba, wc, 1); /* validate xfer rev */ if (st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR)) { /* read rev */ if (st == MTSE_TMK) /* TMK also sets FCE */ set_tuer (ER_FCE); r = tu_map_err (uptr, st, 1); /* map error */ break; /* done */ } for (i = 0; i < 4; i++) xbuf[i] = 0; for (i = 0, j = tbc + 4; (i < wc10) && (j >= 4); i++) { if ((i == 0) || NEWPAGE (ba10 - i, PAG_M_OFF)) { /* map page */ MAPM (ba10 - i, mpa10, UMAP_RRV); } val = ((fmt == TC_10C)? (((d10) xbuf [--j]) & 017): 0); for (k = 0; k < 4; i++) v[k] = xbuf[--j]; val = val | (v[0] << 4) | (v[1] << 12) | (v[2] << 20) | (v[3] << 28); if (fnc == FNC_READR) /* read? store */ M[mpa10] = val; else if (M[mpa10] != val) { /* wchk, mismatch? */ tucs2 = tucs2 | CS2_WCE; /* flag, stop */ break; } mpa10 = mpa10 - 1; } /* end for */ tufc = tbc & 0177777; tuwc = (tuwc + (i << 1)) & 0177777; ba = ba - (i << 2); if (tuwc) /* short record? */ set_tuer (ER_FCE); break; } /* end case */ tucs1 = (tucs1 & ~CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); tuba = ba & 0177777; /* update mem addr */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ if (fnc >= FNC_XFER) /* data xfer? */ update_tucs (CS1_DONE, drv); else update_tucs (CS1_SC, drv); /* no, set attn */ if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, ">>TU%d DONE: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); return SCPE_OK; } /* Formatter error */ void set_tuer (int32 flag) { tuer = tuer | flag; tufs = tufs | FS_ATA; tucs1 = tucs1 | CS1_SC; return; } /* Controller status update Check for done transition Update drive status Update MTCS1 Update interrupt request */ void update_tucs (int32 flag, int32 drv) { int32 act = sim_is_active (&tu_unit[drv]); if ((flag & ~tucs1) & CS1_DONE) /* DONE 0 to 1? */ tuiff = (tucs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ if (GET_FMTR (tucs2) == 0) { /* formatter present? */ tufs = (tufs & ~FS_DYN) | FS_FPR; if (tu_unit[drv].flags & UNIT_ATT) { tufs = tufs | FS_MOL | tu_unit[drv].USTAT; if (tu_unit[drv].UDENS == TC_1600) tufs = tufs | FS_PE; if (sim_tape_wrp (&tu_unit[drv])) tufs = tufs | FS_WRL; if (!act) { if (sim_tape_bot (&tu_unit[drv])) tufs = tufs | FS_BOT; if (sim_tape_eot (&tu_unit[drv])) tufs = tufs | FS_EOT; } } if (tuer) tufs = tufs | FS_ERR; } else tufs = 0; tucs1 = (tucs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; if (tucs2 & CS2_ERR) tucs1 = tucs1 | CS1_TRE | CS1_SC; else if (tucs1 & CS1_TRE) tucs1 = tucs1 | CS1_SC; if (tufs & FS_ATA) tucs1 = tucs1 | CS1_SC; if (tuiff || ((tucs1 & CS1_SC) && (tucs1 & CS1_DONE) && (tucs1 & CS1_IE))) int_req = int_req | INT_TU; else int_req = int_req & ~INT_TU; if ((tucs1 & CS1_DONE) && tufs && !act) tufs = tufs | FS_RDY; return; } /* Interrupt acknowledge */ int32 tu_inta (void) { tucs1 = tucs1 & ~CS1_IE; /* clear int enable */ tuiff = 0; /* clear CSTB INTR */ return VEC_TU; /* acknowledge */ } /* Map tape error status */ t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ set_tuer (ER_NXF); /* can't execute */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; case MTSE_OK: /* no error */ return SCPE_IERR; case MTSE_TMK: /* end of file */ tufs = tufs | FS_TMK; break; case MTSE_IOERR: /* IO error */ set_tuer (ER_VPE); /* flag error */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; if (tu_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ set_tuer (ER_VPE); /* flag error */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ set_tuer (ER_CRC); /* set crc err */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; break; case MTSE_EOM: /* end of medium */ set_tuer (ER_OPI); /* incomplete */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; break; case MTSE_BOT: /* reverse into BOT */ break; case MTSE_WRP: /* write protect */ set_tuer (ER_NXF); /* can't execute */ if (qdt) /* data xfr? set TRE */ tucs1 = tucs1 | CS1_TRE; break; } return SCPE_OK; } /* Reset routine */ t_stat tu_reset (DEVICE *dptr) { int32 u; UNIT *uptr; tucs1 = CS1_DVA | CS1_DONE; tucs2 = CS2_IR | CS2_OR; tuba = 0; tuwc = 0; tufc = 0; tuer = 0; tufs = FS_FPR | FS_RDY; if (sim_switches & SWMASK ('P')) /* powerup? clr TC */ tutc = 0; else tutc = tutc & ~TC_FCS; /* no, clr */ tuiff = 0; /* clear CSTB INTR */ int_req = int_req & ~INT_TU; /* clear interrupt */ for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ uptr = tu_dev.units + u; sim_tape_reset (uptr); /* clear pos flag */ sim_cancel (uptr); /* cancel activity */ uptr->USTAT = 0; } if (xbuf == NULL) xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8)); if (xbuf == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat tu_attach (UNIT *uptr, char *cptr) { int32 drv = uptr - tu_dev.units; t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->USTAT = 0; /* clear unit status */ uptr->UDENS = UD_UNK; /* unknown density */ tufs = tufs | FS_ATA | FS_SSC; /* set attention */ if ((GET_FMTR (tucs2) == 0) && (GET_DRV (tutc) == drv)) /* selected drive? */ tufs = tufs | FS_SAT; /* set slave attn */ update_tucs (CS1_SC, drv); /* update status */ return r; } /* Detach routine */ t_stat tu_detach (UNIT* uptr) { int32 drv = uptr - tu_dev.units; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (sim_is_active (uptr)) { /* unit active? */ sim_cancel (uptr); /* cancel operation */ tuer = tuer | ER_UNS; /* set formatter error */ if ((uptr->USTAT & FS_REW) == 0) /* data transfer? */ tucs1 = tucs1 | CS1_DONE | CS1_TRE; /* set done, err */ } uptr->USTAT = 0; /* clear status flags */ tufs = tufs | FS_ATA | FS_SSC; /* set attention */ update_tucs (CS1_SC, drv); /* update status */ return sim_tape_detach (uptr); } /* Device bootstrap */ #define BOOT_START 0377000 /* start */ #define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) static const d10 boot_rom_dec[] = { 0515040000003, /* boot:hrlzi 1,3 ; uba # */ 0201000040001, /* movei 0,40001 ; vld,pg 1 */ 0713001000000+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ 0201000000040, /* movei 0,40 ; ctrl reset */ 0713001000010, /* wrio 0,10(1) ; ->MTFS */ 0201100000031, /* movei 2,31 ; space f */ 0265740377014, /* jsp 17,tpop ; skip ucode */ 0201100000071, /* movei 2,71 ; read f */ 0265740377014, /* jsp 17,tpop ; read boot */ 0254000001000, /* jrst 1000 ; start */ 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ 0713001000032, /* wrio 0,32(1) ; ->MTTC */ 0201000000011, /* movei 0,11 ; clr+go */ 0713001000000, /* wrio 0,0(1) ; ->MTCS1 */ 0201140176000, /* movei 3,176000 ; wd cnt */ 0201200004000, /* movei 4,4000 ; addr */ 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ 0201300000000, /* movei 6,0 ; fmtr */ 0713141000002, /* wrio 3,2(1) ; ->MTWC */ 0713201000004, /* wrio 4,4(1) ; ->MTBA */ 0713301000006, /* wrio 6,6(1) ; ->MTFC */ 0713301000010, /* wrio 6,10(1) ; ->MTFS */ 0713241000032, /* wrio 5,32(1) ; ->MTTC */ 0713101000000, /* wrio 2,0(1) ; ->MTCS1 */ 0712341000012, /* rdio 7,12(1) ; read FS */ 0606340000200, /* trnn 7,200 ; test rdy */ 0254000377032, /* jrst .-2 ; loop */ 0606340040000, /* trnn 7,40000 ; test err */ 0254017000000, /* jrst 0(17) ; return */ 0712341000014, /* rdio 7,14(1) ; read err */ 0302340001000, /* caie 7,1000 ; fce? */ 0254200377052, /* halt */ 0254017000000, /* jrst 0(17) ; return */ }; static const d10 boot_rom_its[] = { 0515040000003, /* boot:hrlzi 1,3 ; uba # - not used */ 0201000040001, /* movei 0,40001 ; vld,pg 1 */ 0714000000000+(IOBA_UBMAP+1 & RMASK), /* iowri 0,763001 ; set ubmap */ 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ 0201000000040, /* movei 0,40 ; ctrl reset */ 0714001000010, /* iowri 0,10(1) ; ->MTFS */ 0201100000031, /* movei 2,31 ; space f */ 0265740377014, /* jsp 17,tpop ; skip ucode */ 0201100000071, /* movei 2,71 ; read f */ 0265740377014, /* jsp 17,tpop ; read boot */ 0254000001000, /* jrst 1000 ; start */ 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ 0714001000032, /* iowri 0,32(1) ; ->MTTC */ 0201000000011, /* movei 0,11 ; clr+go */ 0714001000000, /* iowri 0,0(1) ; ->MTCS1 */ 0201140176000, /* movei 3,176000 ; wd cnt */ 0201200004000, /* movei 4,4000 ; addr */ 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ 0201300000000, /* movei 6,0 ; fmtr */ 0714141000002, /* iowri 3,2(1) ; ->MTWC */ 0714201000004, /* iowri 4,4(1) ; ->MTBA */ 0714301000006, /* iowri 6,6(1) ; ->MTFC */ 0714301000010, /* iowri 6,10(1) ; ->MTFS */ 0714241000032, /* iowri 5,32(1) ; ->MTTC */ 0714101000000, /* iowri 2,0(1) ; ->MTCS1 */ 0710341000012, /* iordi 7,12(1) ; read FS */ 0606340000200, /* trnn 7,200 ; test rdy */ 0254000377032, /* jrst .-2 ; loop */ 0606340040000, /* trnn 7,40000 ; test err */ 0254017000000, /* jrst 0(17) ; return */ 0710341000014, /* iordi 7,14(1) ; read err */ 0302340001000, /* caie 7,1000 ; fce? */ 0254200377052, /* halt */ 0254017000000, /* jrst 0(17) ; return */ }; t_stat tu_boot (int32 unitno, DEVICE *dptr) { int32 i; extern a10 saved_PC; M[FE_UNIT] = 0; M[FE_MTFMT] = (unitno & TC_M_UNIT) | (TC_1600 << TC_V_DEN) | (TC_10C << TC_V_FMT); tu_unit[unitno].pos = 0; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = Q_ITS? boot_rom_its[i]: boot_rom_dec[i]; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP10/pdp10_lp20.c0000644000175000017500000006606311112111454013354 0ustar vlmvlm/* pdp10_lp20.c: PDP-10 LP20 line printer simulator Copyright (c) 1993-2009, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lp20 line printer 19-Jan-07 RMS Added UNIT_TEXT flag 04-Sep-05 RMS Fixed missing return (found by Peter Schorn) 07-Jul-05 RMS Removed extraneous externs 18-Mar-05 RMS Added attached test to detach routine 29-Dec-03 RMS Fixed bug in scheduling 25-Apr-03 RMS Revised for extended file support 29-Sep-02 RMS Added variable vector support Modified to use common Unibus routines New data structures 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Added enable/disable support 30-Nov-01 RMS Added extended SET/SHOW support */ #include "pdp10_defs.h" #define UNIT_DUMMY (1 << UNIT_V_UF) #define LP_WIDTH 132 /* printer width */ /* DAVFU RAM */ #define DV_SIZE 143 /* DAVFU size */ #define DV_DMASK 077 /* data mask per byte */ #define DV_TOF 0 /* top of form channel */ #define DV_MAX 11 /* max channel number */ /* Translation RAM */ #define TX_SIZE 256 /* translation RAM */ #define TX_AMASK (TX_SIZE - 1) #define TX_DMASK 07777 #define TX_V_FL 8 /* flags */ #define TX_M_FL 017 /* define TX_INTR 04000 /* interrupt */ #define TX_DELH 02000 /* delimiter */ /* define TX_XLAT 01000 /* translate */ /* define TX_DVFU 00400 /* DAVFU */ #define TX_SLEW 00020 /* chan vs slew */ #define TX_VMASK 00017 /* spacing mask */ #define TX_CHR 0 /* states: pr char */ #define TX_RAM 1 /* pr translation */ #define TX_DVU 2 /* DAVFU action */ #define TX_INT 3 /* interrupt */ #define TX_GETFL(x) (((x) >> TX_V_FL) & TX_M_FL) /* LPCSRA (765400) */ #define CSA_GO 0000001 /* go */ #define CSA_PAR 0000002 /* parity enable NI */ #define CSA_V_FNC 2 /* function */ #define CSA_M_FNC 03 #define FNC_PR 0 /* print */ #define FNC_TST 1 /* test */ #define FNC_DVU 2 /* load DAVFU */ #define FNC_RAM 3 /* load translation RAM */ #define FNC_INTERNAL 1 /* internal function */ #define CSA_FNC (CSA_M_FNC << CSA_V_FNC) #define CSA_V_UAE 4 /* Unibus addr extension */ #define CSA_UAE (03 << CSA_V_UAE) #define CSA_IE 0000100 /* interrupt enable */ #define CSA_DONE 0000200 /* done */ #define CSA_INIT 0000400 /* init */ #define CSA_ECLR 0001000 /* clear errors */ #define CSA_DELH 0002000 /* delimiter hold */ #define CSA_ONL 0004000 /* online */ #define CSA_DVON 0010000 /* DAVFU online */ #define CSA_UNDF 0020000 /* undefined char */ #define CSA_PZRO 0040000 /* page counter zero */ #define CSA_ERR 0100000 /* error */ #define CSA_RW (CSA_DELH | CSA_IE | CSA_UAE | CSA_FNC | CSA_PAR | CSA_GO) #define CSA_MBZ (CSA_ECLR | CSA_INIT) #define CSA_GETUAE(x) (((x) & CSA_UAE) << (16 - CSA_V_UAE)) #define CSA_GETFNC(x) (((x) >> CSA_V_FNC) & CSA_M_FNC) /* LPCSRB (765402) */ #define CSB_GOE 0000001 /* go error */ #define CSB_DTE 0000002 /* DEM timing error NI */ #define CSB_MTE 0000004 /* MSYN error (Ubus timeout) */ #define CSB_RPE 0000010 /* RAM parity error NI */ #define CSB_MPE 0000020 /* MEM parity error NI */ #define CSB_LPE 0000040 /* LPT parity error NI */ #define CSB_DVOF 0000100 /* DAVFU not ready */ #define CSB_OFFL 0000200 /* offline */ #define CSB_TEST 0003400 /* test mode */ #define CSB_OVFU 0004000 /* optical VFU NI */ #define CSB_PBIT 0010000 /* data parity bit NI */ #define CSB_NRDY 0020000 /* printer error NI */ #define CSB_LA180 0040000 /* LA180 printer NI */ #define CSB_VLD 0100000 /* valid data NI */ #define CSB_ECLR (CSB_GOE | CSB_DTE | CSB_MTE | CSB_RPE | CSB_MPE | CSB_LPE) #define CSB_ERR (CSB_ECLR | CSB_DVOF | CSB_OFFL) #define CSB_RW CSB_TEST #define CSB_MBZ (CSB_DTE | CSB_RPE | CSB_MPE | CSB_LPE | CSB_OVFU |\ CSB_PBIT | CSB_NRDY | CSB_LA180 | CSB_VLD) /* LPBA (765404) */ /* LPBC (765506) */ #define BC_MASK 0007777 /* <15:12> MBZ */ /* LPPAGC (765510) */ #define PAGC_MASK 0007777 /* <15:12> MBZ */ /* LPRDAT (765512) */ #define RDAT_MASK 0007777 /* <15:12> MBZ */ /* LPCOLC/LPCBUF (765514) */ /* LPCSUM/LPPDAT (765516) */ extern d10 *M; /* main memory */ extern int32 int_req; int32 lpcsa = 0; /* control/status A */ int32 lpcsb = 0; /* control/status B */ int32 lpba = 0; /* bus address */ int32 lpbc = 0; /* byte count */ int32 lppagc = 0; /* page count */ int32 lprdat = 0; /* RAM data */ int32 lpcbuf = 0; /* character buffer */ int32 lpcolc = 0; /* column count */ int32 lppdat = 0; /* printer data */ int32 lpcsum = 0; /* checksum */ int32 dvptr = 0; /* davfu pointer */ int32 dvlnt = 0; /* davfu length */ int32 lp20_irq = 0; /* int request */ int32 lp20_stopioe = 0; /* stop on error */ int16 txram[TX_SIZE] = { 0 }; /* translation RAM */ int16 davfu[DV_SIZE] = { 0 }; /* DAVFU */ DEVICE lp20_dev; t_stat lp20_rd (int32 *data, int32 pa, int32 access); t_stat lp20_wr (int32 data, int32 pa, int32 access); int32 lp20_inta (void); t_stat lp20_svc (UNIT *uptr); t_stat lp20_reset (DEVICE *dptr); t_stat lp20_attach (UNIT *uptr, char *ptr); t_stat lp20_detach (UNIT *uptr); t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc); t_bool lp20_print (int32 c); t_bool lp20_adv (int32 c, t_bool advdvu); t_bool lp20_davfu (int32 c); void update_lpcs (int32 flg); /* LP data structures lp20_dev LPT device descriptor lp20_unit LPT unit descriptor lp20_reg LPT register list */ DIB lp20_dib = { IOBA_LP20, IOLN_LP20, &lp20_rd, &lp20_wr, 1, IVCL (LP20), VEC_LP20, { &lp20_inta } }; UNIT lp20_unit = { UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lp20_reg[] = { { ORDATA (LPCSA, lpcsa, 16) }, { ORDATA (LPCSB, lpcsb, 16) }, { ORDATA (LPBA, lpba, 16) }, { ORDATA (LPBC, lpbc, 12) }, { ORDATA (LPPAGC, lppagc, 12) }, { ORDATA (LPRDAT, lprdat, 12) }, { ORDATA (LPCBUF, lpcbuf, 8) }, { ORDATA (LPCOLC, lpcolc, 8) }, { ORDATA (LPPDAT, lppdat, 8) }, { ORDATA (LPCSUM, lpcsum, 8) }, { ORDATA (DVPTR, dvptr, 7) }, { ORDATA (DVLNT, dvlnt, 7), REG_RO + REG_NZ }, { FLDATA (INT, int_req, INT_V_LP20) }, { FLDATA (IRQ, lp20_irq, 0) }, { FLDATA (ERR, lpcsa, CSR_V_ERR) }, { FLDATA (DONE, lpcsa, CSR_V_DONE) }, { FLDATA (IE, lpcsa, CSR_V_IE) }, { DRDATA (POS, lp20_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lp20_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lp20_stopioe, 0) }, { BRDATA (TXRAM, txram, 8, 12, TX_SIZE) }, { BRDATA (DAVFU, davfu, 8, 12, DV_SIZE) }, { ORDATA (DEVADDR, lp20_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, lp20_dib.vec, 16), REG_HRO }, { NULL } }; MTAB lp20_mod[] = { { UNIT_DUMMY, 0, NULL, "VFUCLEAR", &lp20_clear_vfu }, { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE lp20_dev = { "LP20", &lp20_unit, lp20_reg, lp20_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lp20_reset, NULL, &lp20_attach, &lp20_detach, &lp20_dib, DEV_DISABLE | DEV_UBUS }; /* Line printer routines lp20_rd I/O page read lp20_wr I/O page write lp20_svc process event (printer ready) lp20_reset process reset lp20_attach process attach lp20_detach process detach */ t_stat lp20_rd (int32 *data, int32 pa, int32 access) { update_lpcs (0); /* update csr's */ switch ((pa >> 1) & 07) { /* case on PA<3:1> */ case 00: /* LPCSA */ *data = lpcsa = lpcsa & ~CSA_MBZ; break; case 01: /* LPCSB */ *data = lpcsb = lpcsb & ~CSB_MBZ; break; case 02: /* LPBA */ *data = lpba; break; case 03: /* LPBC */ *data = lpbc = lpbc & BC_MASK; break; case 04: /* LPPAGC */ *data = lppagc = lppagc & PAGC_MASK; break; case 05: /* LPRDAT */ *data = lprdat = lprdat & RDAT_MASK; break; case 06: /* LPCOLC/LPCBUF */ *data = (lpcolc << 8) | lpcbuf; break; case 07: /* LPCSUM/LPPDAT */ *data = (lpcsum << 8) | lppdat; break; } /* end case PA */ return SCPE_OK; } t_stat lp20_wr (int32 data, int32 pa, int32 access) { update_lpcs (0); /* update csr's */ switch ((pa >> 1) & 07) { /* case on PA<3:1> */ case 00: /* LPCSA */ if (access == WRITEB) data = (pa & 1)? (lpcsa & 0377) | (data << 8): (lpcsa & ~0377) | data; if (data & CSA_ECLR) { /* error clear? */ lpcsa = (lpcsa | CSA_DONE) & ~CSA_GO; /* set done, clr go */ lpcsb = lpcsb & ~CSB_ECLR; /* clear err */ sim_cancel (&lp20_unit); /* cancel I/O */ } if (data & CSA_INIT) /* init? */ lp20_reset (&lp20_dev); if (data & CSA_GO) { /* go set? */ if ((lpcsa & CSA_GO) == 0) { /* not set before? */ if (lpcsb & CSB_ERR) lpcsb = lpcsb | CSB_GOE; lpcsum = 0; /* clear checksum */ sim_activate (&lp20_unit, lp20_unit.wait); } } else sim_cancel (&lp20_unit); /* go clr, stop DMA */ lpcsa = (lpcsa & ~CSA_RW) | (data & CSA_RW); break; case 01: /* LPCSB */ break; /* ignore writes to TEST */ case 02: /* LPBA */ if (access == WRITEB) data = (pa & 1)? (lpba & 0377) | (data << 8): (lpba & ~0377) | data; lpba = data; break; case 03: /* LPBC */ if (access == WRITEB) data = (pa & 1)? (lpbc & 0377) | (data << 8): (lpbc & ~0377) | data; lpbc = data & BC_MASK; lpcsa = lpcsa & ~CSA_DONE; break; case 04: /* LPPAGC */ if (access == WRITEB) data = (pa & 1)? (lppagc & 0377) | (data << 8): (lppagc & ~0377) | data; lppagc = data & PAGC_MASK; break; case 05: /* LPRDAT */ if (access == WRITEB) data = (pa & 1)? (lprdat & 0377) | (data << 8): (lprdat & ~0377) | data; lprdat = data & RDAT_MASK; txram[lpcbuf & TX_AMASK] = lprdat; /* load RAM */ break; case 06: /* LPCOLC/LPCBUF */ if ((access == WRITEB) && (pa & 1)) /* odd byte */ lpcolc = data & 0377; else { lpcbuf = data & 0377; /* even byte, word */ if (access == WRITE) lpcolc = (data >> 8) & 0377; } break; case 07: /* LPCSUM/LPPDAT */ break; /* read only */ } /* end case PA */ update_lpcs (0); return SCPE_OK; } /* Line printer service The translation RAM case table is derived from the LP20 spec and verified against the LP20 RAM simulator in TOPS10 7.04 LPTSPL. The equations are: flags := inter, delim, xlate, paper, delim_hold (from CSRA) actions : = print_input, print_xlate, davfu_action, interrupt if (inter) { if (!xlate || delim || delim_hold) interrupt; else if (paper) davfu_action; else print_xlate; } else if (paper) { if (xlate || delim || delim_hold) davfu_action; else print_input; } else { if (xlate || delim || delim_hold) print_xlate; else print_input; } */ t_stat lp20_svc (UNIT *uptr) { int32 fnc, i, tbc, temp, txst; int32 dvld = -2; /* must be even */ uint16 wd10; t_bool cont; a10 ba; static const uint32 txcase[32] = { TX_CHR, TX_RAM, TX_CHR, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_INT, TX_INT, TX_INT, TX_INT, TX_RAM, TX_INT, TX_DVU, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT }; lpcsa = lpcsa & ~CSA_GO; ba = CSA_GETUAE (lpcsa) | lpba; fnc = CSA_GETFNC (lpcsa); tbc = 010000 - lpbc; if (((fnc & FNC_INTERNAL) == 0) && ((lp20_unit.flags & UNIT_ATT) == 0)) { update_lpcs (CSA_ERR); return IORETURN (lp20_stopioe, SCPE_UNATT); } if ((fnc == FNC_PR) && (dvlnt == 0)) { update_lpcs (CSA_ERR); return SCPE_OK; } for (i = 0, cont = TRUE; (i < tbc) && cont; ba++, i++) { if (Map_ReadW (ba, 2, &wd10)) { /* get word, err? */ lpcsb = lpcsb | CSB_MTE; /* set NXM error */ update_lpcs (CSA_ERR); /* set done */ break; } lpcbuf = (wd10 >> ((ba & 1)? 8: 0)) & 0377; /* get character */ lpcsum = (lpcsum + lpcbuf) & 0377; /* add into checksum */ switch (fnc) { /* switch on function */ /* Translation RAM load */ case FNC_RAM: /* RAM load */ txram[(i >> 1) & TX_AMASK] = wd10 & TX_DMASK; break; /* DAVFU RAM load. The DAVFU RAM is actually loaded in bytes, delimited by a start (354 to 356) and stop (357) byte pair. If the number of bytes loaded is odd, or no bytes are loaded, the DAVFU is invalid. */ case FNC_DVU: /* DVU load */ if ((lpcbuf >= 0354) && (lpcbuf <= 0356)) /* start DVU load? */ dvld = dvlnt = 0; /* reset lnt */ else if (lpcbuf == 0357) { /* stop DVU load? */ dvptr = 0; /* reset ptr */ if (dvld & 1) /* if odd, invalid */ dvlnt = 0; } else if (dvld == 0) { /* even state? */ temp = lpcbuf & DV_DMASK; dvld = 1; } else if (dvld == 1) { /* odd state? */ if (dvlnt < DV_SIZE) davfu[dvlnt++] = temp | ((lpcbuf & DV_DMASK) << 6); dvld = 0; } break; /* Print characters */ case FNC_PR: /* print */ lprdat = txram[lpcbuf]; /* get RAM char */ txst = (TX_GETFL (lprdat) << 1) | /* get state */ ((lpcsa & CSA_DELH)? 1: 0); /* plus delim hold */ if (lprdat & TX_DELH) lpcsa = lpcsa | CSA_DELH; else lpcsa = lpcsa & ~CSA_DELH; lpcsa = lpcsa & ~CSA_UNDF; /* assume char ok */ switch (txcase[txst]) { /* case on state */ case TX_CHR: /* take char */ cont = lp20_print (lpcbuf); break; case TX_RAM: /* take translation */ cont = lp20_print (lprdat); break; case TX_DVU: /* DAVFU action */ if (lprdat & TX_SLEW) cont = lp20_adv (lprdat & TX_VMASK, TRUE); else cont = lp20_davfu (lprdat & TX_VMASK); break; case TX_INT: /* interrupt */ lpcsa = lpcsa | CSA_UNDF; /* set flag */ cont = FALSE; /* force stop */ break; } /* end case char state */ break; case FNC_TST: /* test */ break; } /* end case function */ } /* end for */ lpba = ba & 0177777; lpcsa = (lpcsa & ~CSA_UAE) | ((ba >> (16 - CSA_V_UAE)) & CSA_UAE); lpbc = (lpbc + i) & BC_MASK; if (lpbc) /* intr, but not done */ update_lpcs (CSA_MBZ); else update_lpcs (CSA_DONE); /* intr and done */ if ((fnc == FNC_PR) && ferror (lp20_unit.fileref)) { perror ("LP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Print routines lp20_print print a character lp20_adv advance n lines lp20_davfu advance to channel on VFU Return TRUE to continue printing, FALSE to stop */ t_bool lp20_print (int32 c) { t_bool r = TRUE; int32 i, rpt = 1; lppdat = c & 0177; /* mask char to 7b */ if (lppdat == 000) /* NUL? no op */ return TRUE; if (lppdat == 012) /* LF? adv carriage */ return lp20_adv (1, TRUE); if (lppdat == 014) /* FF? top of form */ return lp20_davfu (DV_TOF); if (lppdat == 015) /* CR? reset col cntr */ lpcolc = 0; else if (lppdat == 011) { /* TAB? simulate */ lppdat = ' '; /* with spaces */ if (lpcolc >= 128) { r = lp20_adv (1, TRUE); /* eol? adv carriage */ rpt = 8; /* adv to col 9 */ } else rpt = 8 - (lpcolc & 07); /* else adv 1 to 8 */ } else { if (lppdat < 040) /* cvt non-prnt to spc */ lppdat = ' '; if (lpcolc >= LP_WIDTH) /* line full? */ r = lp20_adv (1, TRUE); /* adv carriage */ } for (i = 0; i < rpt; i++) fputc (lppdat, lp20_unit.fileref); lp20_unit.pos = ftell (lp20_unit.fileref); lpcolc = lpcolc + rpt; return r; } t_bool lp20_adv (int32 cnt, t_bool dvuadv) { int32 i; if (cnt == 0) return TRUE; lpcolc = 0; /* reset col cntr */ for (i = 0; i < cnt; i++) fputc ('\n', lp20_unit.fileref); lp20_unit.pos = ftell (lp20_unit.fileref); /* print 'n' newlines */ if (dvuadv) /* update DAVFU ptr */ dvptr = (dvptr + cnt) % dvlnt; if (davfu[dvptr] & (1 << DV_TOF)) { /* at top of form? */ if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ lpcsa = lpcsa & ~CSA_PZRO; /* update status */ return TRUE; } else { lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ return FALSE; } } return TRUE; } t_bool lp20_davfu (int32 cnt) { int i; if (cnt > DV_MAX) /* inval chan? */ cnt = 7; for (i = 0; i < dvlnt; i++) { /* search DAVFU */ dvptr = dvptr + 1; /* adv DAVFU ptr */ if (dvptr >= dvlnt) /* wrap at end */ dvptr = 0; if (davfu[dvptr] & (1 << cnt)) { /* channel stop set? */ if (cnt) /* ~TOF, adv */ return lp20_adv (i + 1, FALSE); if (lpcolc) /* TOF, need newline? */ lp20_adv (1, FALSE); fputc ('\f', lp20_unit.fileref); /* print form feed */ lp20_unit.pos = ftell (lp20_unit.fileref); if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ lpcsa = lpcsa & ~CSA_PZRO; /* update status */ return TRUE; } else { lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ return FALSE; } } } /* end for */ dvlnt = 0; /* DAVFU error */ return FALSE; } /* Update LPCSA, optionally request interrupt */ void update_lpcs (int32 flg) { if (flg) /* set int req */ lp20_irq = 1; lpcsa = (lpcsa | flg) & ~(CSA_MBZ | CSA_ERR | CSA_ONL | CSA_DVON); lpcsb = (lpcsb | CSB_OFFL | CSB_DVOF) & ~CSB_MBZ; if (lp20_unit.flags & UNIT_ATT) { lpcsa = lpcsa | CSA_ONL; lpcsb = lpcsb & ~CSB_OFFL; } else lpcsa = lpcsa & ~CSA_DONE; if (dvlnt) { lpcsa = lpcsa | CSA_DVON; lpcsb = lpcsb & ~CSB_DVOF; } if (lpcsb & CSB_ERR) lpcsa = lpcsa | CSA_ERR; if ((lpcsa & CSA_IE) && lp20_irq) int_req = int_req | INT_LP20; else int_req = int_req & ~INT_LP20; return; } /* Acknowledge interrupt (clear internal request) */ int32 lp20_inta (void) { lp20_irq = 0; /* clear int req */ return lp20_dib.vec; } t_stat lp20_reset (DEVICE *dptr) { lpcsa = CSA_DONE; lpcsb = 0; lpba = lpbc = lppagc = lpcolc = 0; /* clear registers */ lprdat = lppdat = lpcbuf = lpcsum = 0; lp20_irq = 0; /* clear int req */ dvptr = 0; /* reset davfu ptr */ sim_cancel (&lp20_unit); /* deactivate unit */ update_lpcs (0); /* update status */ return SCPE_OK; } t_stat lp20_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); /* attach file */ if (lpcsa & CSA_ONL) /* just file chg? */ return reason; if (sim_is_active (&lp20_unit)) /* busy? no int */ update_lpcs (0); else update_lpcs (CSA_MBZ); /* interrupt */ return reason; } t_stat lp20_detach (UNIT *uptr) { t_stat reason; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; reason = detach_unit (uptr); sim_cancel (&lp20_unit); lpcsa = lpcsa & ~CSA_GO; update_lpcs (CSA_MBZ); return reason; } t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc) { int i; if (!get_yn ("Clear DAVFU? [N]", FALSE)) return SCPE_OK; for (i = 0; i < DV_SIZE; i++) davfu[i] = 0; dvlnt = dvptr = 0; update_lpcs (0); return SCPE_OK; } simh-3.8.1/PDP10/pdp10_pag.c0000644000175000017500000007677611112112574013366 0ustar vlmvlm/* pdp10_pag.c: PDP-10 paging subsystem simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. pag KS10 pager 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 02-Dec-01 RMS Fixed bug in ITS LPMR (found by Dave Conroy) 21-Aug-01 RMS Fixed bug in ITS paging (found by Miriam Lennox) Removed register from declarations 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug 03-May-01 RMS Fixed bug in indirect page table pointer processing 29-Apr-01 RMS Added CLRCSH for ITS, fixed LPMR The pager consists of a standard hardware part (the translation tables) and an operating-system specific page table fill routine. There are two translation tables, one for executive mode and one for user mode. Each table consists of 512 page table entries, one for each page in the 18b virtual address space. Each pte contains (in the hardware) a valid bit, a writeable bit, an address space bit (executive or user), and a cacheable bit, plus the physical page number corresponding to the virtual page. In the simulator, the pte is expanded for rapid processing of normal reads and writes. An expanded pte contains a valid bit, a writeable bit, and the physical page number shifted left by the page size. Expanded pte meaning 0 invalid >0 read only <0 read write There is a third, physical table, which is used in place of the executive and user tables if paging is off. Its entries are always valid and always writeable. To translate a virtual to physical address, the simulator uses the virtual page number to index into the appropriate page table. If the page table entry (pte) is not valid, the page fill routine is called to see if the entry is merely not filled or is truly inaccessible. If the pte is valid but not writeable, and the reference is a write reference, the page fill routine is also called to see if the reference can be resolved. The page fill routine is operating system dependent. Three styles of paging are supported: TOPS10 known in the KS10 microcode as KI10 paging, used by earlier versions of TOPS10 TOPS20 known in the KS10 microcode as KL10 paging, used by later versions of TOPS10, and TOPS20 ITS used only by ITS TOPS10 vs TOPS20 is selected by a bit in the EBR; ITS paging is "hardwired" (it required different microcode). */ #include "pdp10_defs.h" #include /* Page table (contains expanded pte's) */ #define PTBL_ASIZE PAG_N_VPN #define PTBL_MEMSIZE (1 << PTBL_ASIZE) /* page table size */ #define PTBL_AMASK (PTBL_MEMSIZE - 1) #define PTBL_M (1u << 31) /* must be sign bit */ #define PTBL_V (1u << 30) #define PTBL_MASK (PAG_PPN | PTBL_M | PTBL_V) /* NXM processing */ #define REF_V 0 /* ref is virt */ #define REF_P 1 /* ref is phys */ #define PF_OK 0 /* pfail ok */ #define PF_TR 1 /* pfail trap */ extern d10 *M; extern d10 acs[AC_NBLK * AC_NUM]; extern d10 *ac_cur, *ac_prv, *last_pa; extern a10 epta, upta; extern int32 flags; extern d10 pager_word; extern int32 apr_flg; extern d10 ebr, ubr, hsb; extern d10 spt, cst, cstm, pur; extern a10 dbr1, dbr2, dbr3, dbr4; extern d10 pcst, quant; extern t_bool paging; extern UNIT cpu_unit; extern jmp_buf save_env; extern int32 test_int (void); extern int32 pi_eval (void); int32 eptbl[PTBL_MEMSIZE]; /* exec page table */ int32 uptbl[PTBL_MEMSIZE]; /* user page table */ int32 physptbl[PTBL_MEMSIZE]; /* phys page table */ int32 *ptbl_cur, *ptbl_prv; int32 save_ea; int32 ptbl_fill (a10 ea, int32 *ptbl, int32 mode); t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat pag_reset (DEVICE *dptr); void pag_nxm (a10 pa, int32 phys, int32 trap); /* Pager data structures pag_dev pager device descriptor pag_unit pager units pager_reg pager register list */ UNIT pag_unit[] = { { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) }, { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) } }; REG pag_reg[] = { { ORDATA (PANIC_EA, save_ea, PASIZE), REG_HRO }, { NULL } }; DEVICE pag_dev = { "PAG", pag_unit, pag_reg, NULL, 2, 8, PTBL_ASIZE, 1, 8, 32, &pag_ex, &pag_dep, &pag_reset, NULL, NULL, NULL, NULL, 0 }; /* Memory read and write routines Read - read current or previous, read checking ReadM - read current or previous, write checking ReadE - read exec ReadP - read physical Write - write current or previous WriteE - write exec WriteP - write physical AccChk - test accessibility of virtual address */ d10 Read (a10 ea, int32 prv) { int32 pa, vpn, xpte; if (ea < AC_NUM) /* AC request */ return (prv? ac_prv[ea]: ac_cur[ea]); vpn = PAG_GETVPN (ea); /* get page num */ xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ if (xpte == 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_RD); pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ if (MEM_ADDR_NXM (pa)) /* process nxm */ pag_nxm (pa, REF_V, PF_TR); return M[pa]; /* return data */ } d10 ReadM (a10 ea, int32 prv) { int32 pa, vpn, xpte; if (ea < AC_NUM) /* AC request */ return (prv? ac_prv[ea]: ac_cur[ea]); vpn = PAG_GETVPN (ea); /* get page num */ xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ if (MEM_ADDR_NXM (pa)) /* process nxm */ pag_nxm (pa, REF_V, PF_TR); return M[pa]; /* return data */ } d10 ReadE (a10 ea) { int32 pa, vpn, xpte; if (ea < AC_NUM) /* AC? use current */ return AC(ea); if (!PAGING) /* phys? no mapping */ return M[ea]; vpn = PAG_GETVPN (ea); /* get page num */ xpte = eptbl[vpn]; /* get exp pte, exec tbl */ if (xpte == 0) xpte = ptbl_fill (ea, eptbl, PTF_RD); pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ if (MEM_ADDR_NXM (pa)) /* process nxm */ pag_nxm (pa, REF_V, PF_TR); return M[pa]; /* return data */ } d10 ReadP (a10 ea) { if (ea < AC_NUM) /* AC request */ return AC(ea); if (MEM_ADDR_NXM (ea)) /* process nxm */ pag_nxm (ea, REF_P, PF_TR); return M[ea]; /* return data */ } void Write (a10 ea, d10 val, int32 prv) { int32 pa, vpn, xpte; if (ea < AC_NUM) { /* AC request */ if (prv) /* write AC */ ac_prv[ea] = val; else ac_cur[ea] = val; } else { vpn = PAG_GETVPN (ea); /* get page num */ xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ if (MEM_ADDR_NXM (pa)) /* process nxm */ pag_nxm (pa, REF_V, PF_TR); else M[pa] = val; /* write data */ } return; } void WriteE (a10 ea, d10 val) { int32 pa, vpn, xpte; if (ea < AC_NUM) /* AC? use current */ AC(ea) = val; else if (!PAGING) /* phys? no mapping */ M[ea] = val; else { vpn = PAG_GETVPN (ea); /* get page num */ xpte = eptbl[vpn]; /* get exp pte, exec tbl */ if (xpte >= 0) xpte = ptbl_fill (ea, eptbl, PTF_WR); pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ if (MEM_ADDR_NXM (pa)) /* process nxm */ pag_nxm (pa, REF_V, PF_TR); else M[pa] = val; /* write data */ } return; } void WriteP (a10 ea, d10 val) { if (ea < AC_NUM) /* AC request */ AC(ea) = val; else { if (MEM_ADDR_NXM (ea)) /* process nxm */ pag_nxm (ea, REF_P, PF_TR); M[ea] = val; /* memory */ } return; } t_bool AccViol (a10 ea, int32 prv, int32 mode) { int32 vpn, xpte; if (ea < AC_NUM) /* AC request */ return FALSE; vpn = PAG_GETVPN (ea); /* get page num */ xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ if ((xpte == 0) || ((mode & PTF_WR) && (xpte > 0))) /* not accessible? */ xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, mode | PTF_MAP); if (xpte) /* accessible */ return FALSE; return TRUE; /* not accessible */ } void pag_nxm (a10 pa, int32 phys, int32 trap) { apr_flg = apr_flg | APRF_NXM; /* set APR flag */ pi_eval (); /* eval intr */ pager_word = PF_NXM | (phys? PF_NXMP: 0) | (TSTF (F_USR)? PF_USER: 0) | ((d10) pa); if (PAGING && trap) /* trap? */ ABORT (PAGE_FAIL); return; } /* Page table fill This routine is called if the page table is invalid, or on a write reference if the page table is read only. If the access is allowed it stores the pte in the page table entry and returns an expanded pte for use by the caller. Otherwise, it generates a page fail. Notes: - If called from the console, invalid references return a pte of 0, and the page table entry is not filled. - If called from MAP, invalid references return a pte of 0. The page fail word is properly set up. */ #define PAGE_FAIL_TRAP if (mode & (PTF_CON | PTF_MAP)) \ return 0; \ ABORT (PAGE_FAIL) #define READPT(x,y) if (MEM_ADDR_NXM (y)) { \ pag_nxm (y, REF_P, PF_OK); \ PAGE_FAIL_TRAP; \ } \ x = ReadP (y) int32 ptbl_fill (a10 ea, int32 *tbl, int32 mode) { /* ITS paging is based on conventional page tables. ITS divides each address space into a 128K high and low section, and uses different descriptor base pointers (dbr) for each. ITS pages are twice the size of DEC standard; therefore, the fill routine fills two page table entries and returns the pte that maps the correct ITS half page. This allows the DEC paging macros to be used in the normal path read-write routines. ITS has no MAP instruction, therefore, physical NXM traps are ok. */ if (Q_ITS) { /* ITS paging */ int32 acc, decvpn, pte, vpn, ptead, xpte; d10 ptewd; vpn = ITS_GETVPN (ea); /* get ITS pagno */ if (tbl == uptbl) ptead = ((ea & RSIGN)? dbr2: dbr1) + ((vpn >> 1) & 077); else ptead = ((ea & RSIGN)? dbr3: dbr4) + ((vpn >> 1) & 077); ptewd = ReadP (ptead); /* get PTE pair */ pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); acc = ITS_GETACC (pte); /* get access */ pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | ((mode & PTF_WR)? PF_ITS_WRITE: 0) | (acc << PF_ITS_V_ACC); if ((acc != ITS_ACC_NO) && (!(mode & PTF_WR) || (acc == ITS_ACC_RW))) { pte = pte & ~PTE_ITS_AGE; /* clear age */ if (vpn & 1) WriteP (ptead, (ptewd & LMASK) | pte); else WriteP (ptead, (ptewd & RMASK) | (((d10) pte) << 18)); xpte = ((pte & PTE_ITS_PPMASK) << ITS_V_PN) | PTBL_V | ((acc == ITS_ACC_RW)? PTBL_M: 0); decvpn = PAG_GETVPN (ea); /* get tlb idx */ if (!(mode & PTF_CON)) { tbl[decvpn & ~1] = xpte; /* map lo ITS page */ tbl[decvpn | 1] = xpte + PAG_SIZE; /* map hi */ } return (xpte + ((decvpn & 1)? PAG_SIZE: 0)); } PAGE_FAIL_TRAP; } /* end ITS paging */ /* TOPS-10 paging - checked against KS10 microcode TOPS-10 paging is also based on conventional page tables. The user page tables are arranged contiguously at the beginning of the user process table; however, the executive page tables are scattered through the executive and user process tables. */ else if (!T20PAG) { /* TOPS-10 paging */ int32 pte, vpn, ptead, xpte; d10 ptewd; vpn = PAG_GETVPN (ea); /* get virt page num */ if (tbl == uptbl) ptead = upta + UPT_T10_UMAP + (vpn >> 1); else if (vpn < 0340) ptead = epta + EPT_T10_X000 + (vpn >> 1); else if (vpn < 0400) ptead = upta + UPT_T10_X340 + ((vpn - 0340) >> 1); else ptead = epta + EPT_T10_X400 + ((vpn - 0400) >> 1); READPT (ptewd, ptead); /* get PTE pair */ pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | ((mode & PTF_WR)? PF_WRITE: 0) | ((pte & PTE_T10_A)? PF_T10_A | ((pte & PTE_T10_S)? PF_T10_S: 0): 0); if (mode & PTF_MAP) pager_word = pager_word | /* map? add to pf wd */ ((pte & PTE_T10_W)? PF_T10_W: 0) | /* W, S, C bits */ ((pte & PTE_T10_S)? PF_T10_S: 0) | ((pte & PTE_T10_C)? PF_C: 0); if ((pte & PTE_T10_A) && (!(mode & PTF_WR) || (pte & PTE_T10_W))) { xpte = ((pte & PTE_PPMASK) << PAG_V_PN) | /* calc exp pte */ PTBL_V | ((pte & PTE_T10_W)? PTBL_M: 0); if (!(mode & PTF_CON)) /* set tbl if ~cons */ tbl[vpn] = xpte; return xpte; } PAGE_FAIL_TRAP; } /* end TOPS10 paging */ /* TOPS-20 paging - checked against KS10 microcode TOPS-20 paging has three phases: 1. Starting at EPT/UPT + 540 + section number, chase section pointers to get the pointer to the section page table. In the KS10, because there is only one section, the microcode caches the result of this evaluation. Also, the evaluation of indirect pointers is simplified, as the section table index is ignored. 2. Starting with the page map pointer, chase page pointers to get the pointer to the page. The KS10 allows the operating system to inhibit updating of the CST (base address = 0). 3. Use the page pointer to get the CST entry. If a write reference to a writeable page, set CST_M. If CST_M is set, set M in page table. */ else { /* TOPS-20 paging */ int32 pmi, vpn, xpte; int32 flg, t; t_bool stop; a10 pa, csta; d10 ptr, cste; d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */ pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | ((mode & PTF_WR)? PF_WRITE: 0); /* set page fail word */ /* First phase - evaluate section pointers - returns a ptr to a page map As a single section machine, the KS10 short circuits this part of the process. In particular, the indirect pointer calculation assumes that the section table index will be 0. It adds the full pointer (not just the right half) to the SPT base. If the section index is > 0, the result is a physical memory address > 256KW. Depending on the size of memory, the SPT fetch may or may not generate a NXM page fail. The KS10 then ignores the section table index in fetching the next pointer. The KS10 KL10 memory management diagnostic (dskec.sav) tests for this behavior with a section index of 3. However, this would be a legal physical address in a system with 1MW. Accordingly, the simulator special cases non-zero section indices (which can't work in any case) to generate the right behavior for the diagnostic. */ vpn = PAG_GETVPN (ea); /* get virt page num */ pa = (tbl == uptbl)? upta + UPT_T20_SCTN: epta + EPT_T20_SCTN; READPT (ptr, pa & PAMASK); /* get section 0 ptr */ for (stop = FALSE, flg = 0; !stop; flg++) { /* eval section ptrs */ acc = acc & ptr; /* cascade acc bits */ switch (T20_GETTYP (ptr)) { /* case on ptr type */ case T20_NOA: /* no access */ default: /* undefined type */ PAGE_FAIL_TRAP; /* page fail */ case T20_IMM: /* immediate */ stop = TRUE; /* exit */ break; case T20_SHR: /* shared */ pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ READPT (ptr, pa & PAMASK); /* get SPT entry */ stop = TRUE; /* exit */ break; case T20_IND: /* indirect */ if (flg && (t = test_int ())) ABORT (t); pmi = T20_GETPMI (ptr); /* get sect tbl idx */ pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ if (pmi) { /* for dskec */ pag_nxm ((pmi << 18) | pa, REF_P, PF_OK); PAGE_FAIL_TRAP; } READPT (ptr, pa & PAMASK); /* get SPT entry */ if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } pa = PAG_PTEPA (ptr, pmi); /* index off page */ READPT (ptr, pa & PAMASK); /* get pointer */ break; /* continue in loop */ } /* end case */ } /* end for */ /* Second phase - found page map ptr, evaluate page pointers */ pa = PAG_PTEPA (ptr, vpn); /* get ptbl address */ for (stop = FALSE, flg = 0; !stop; flg++) { /* eval page ptrs */ if (ptr & PTE_T20_STM) { /* non-res? */ PAGE_FAIL_TRAP; } if (cst) { /* cst really there? */ csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); READPT (cste, csta); /* get CST entry */ if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } cste = (cste & cstm) | pur; /* update entry */ WriteP (csta, cste); /* rewrite */ } READPT (ptr, pa & PAMASK); /* get pointer */ acc = acc & ptr; /* cascade acc bits */ switch (T20_GETTYP (ptr)) { /* case on ptr type */ case T20_NOA: /* no access */ default: /* undefined type */ PAGE_FAIL_TRAP; /* page fail */ case T20_IMM: /* immediate */ stop = TRUE; /* exit */ break; case T20_SHR: /* shared */ pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ READPT (ptr, pa & PAMASK); /* get SPT entry */ stop = TRUE; /* exit */ break; case T20_IND: /* indirect */ if (flg && (t = test_int ())) ABORT (t); pmi = T20_GETPMI (ptr); /* get section index */ pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ READPT (ptr, pa & PAMASK); /* get SPT entry */ pa = PAG_PTEPA (ptr, pmi); /* index off page */ break; /* continue in loop */ } /* end case */ } /* end for */ /* Last phase - have final page pointer, check modifiability */ if (ptr & PTE_T20_STM) { /* non-resident? */ PAGE_FAIL_TRAP; } if (cst) { /* CST really there? */ csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); READPT (cste, csta); /* get CST entry */ if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } cste = (cste & cstm) | pur; /* update entry */ } else cste = 0; /* no, entry = 0 */ pager_word = pager_word | PF_T20_DN; /* set eval done */ xpte = ((int32) ((ptr & PTE_PPMASK) << PAG_V_PN)) | PTBL_V; if (mode & PTF_WR) { /* write? */ if (acc & PTE_T20_W) { /* writable? */ xpte = xpte | PTBL_M; /* set PTE M */ cste = cste | CST_M; /* set CST M */ } else { /* no, trap */ PAGE_FAIL_TRAP; } } if (cst) /* write CST entry */ WriteP (csta, cste); if (mode & PTF_MAP) pager_word = pager_word | /* map? more in pf wd */ ((xpte & PTBL_M)? PF_T20_M: 0) | /* M, W, C bits */ ((acc & PTE_T20_W)? PF_T20_W: 0) | ((acc & PTE_T20_C)? PF_C: 0); if (!(mode & PTF_CON)) /* set tbl if ~cons */ tbl[vpn] = xpte; return xpte; } /* end TOPS20 paging */ } /* Set up pointers for AC, memory, and process table access */ void set_dyn_ptrs (void) { int32 t; if (PAGING) { ac_cur = &acs[UBR_GETCURAC (ubr) * AC_NUM]; ac_prv = &acs[UBR_GETPRVAC (ubr) * AC_NUM]; if (TSTF (F_USR)) ptbl_cur = ptbl_prv = &uptbl[0]; else { ptbl_cur = &eptbl[0]; ptbl_prv = TSTF (F_UIO)? &uptbl[0]: &eptbl[0]; } } else { ac_cur = ac_prv = &acs[0]; ptbl_cur = ptbl_prv = &physptbl[0]; } t = EBR_GETEBR (ebr); epta = t << PAG_V_PN; if (Q_ITS) upta = (int32) ubr & PAMASK; else { t = UBR_GETUBR (ubr); upta = t << PAG_V_PN; } return; } /* MAP instruction, TOPS-10 and TOPS-20 only According to the KS-10 ucode, map with paging disabled sets "accessible, writeable, software", regardless of whether TOPS-10 or TOPS-20 paging is implemented */ d10 map (a10 ea, int32 prv) { int32 xpte; d10 val = (TSTF (F_USR)? PF_USER: 0); if (!PAGING) return (val | PF_T10_A | PF_T10_W | PF_T10_S | ea); xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_MAP); /* get exp pte */ if (xpte) val = (pager_word & ~PAMASK) | PAG_XPTEPA (xpte, ea); else { if (pager_word & PF_HARD) /* hard error */ val = pager_word; else val = val | PF_VIRT | ea; /* inaccessible */ } return val; } /* Mapping routine for console */ a10 conmap (a10 ea, int32 mode, int32 sw) { int32 xpte, *tbl; if (!PAGING) return ea; set_dyn_ptrs (); /* in case changed */ if (sw & SWMASK ('E')) tbl = eptbl; else if (sw & SWMASK ('U')) tbl = uptbl; else tbl = ptbl_cur; xpte = ptbl_fill (ea, tbl, mode); if (xpte) return PAG_XPTEPA (xpte, ea); else return MAXMEMSIZE; } /* Common pager instructions */ t_bool clrpt (a10 ea, int32 prv) { int32 vpn = PAG_GETVPN (ea); /* get page num */ if (Q_ITS) { /* ITS? */ uptbl[vpn & ~1] = 0; /* clear double size */ uptbl[vpn | 1] = 0; /* entries in */ eptbl[vpn & ~1] = 0; /* both page tables */ eptbl[vpn | 1] = 0; } else { uptbl[vpn] = 0; /* clear entries in */ eptbl[vpn] = 0; /* both page tables */ } return FALSE; } t_bool wrebr (a10 ea, int32 prv) { ebr = ea & EBR_MASK; /* store EBR */ pag_reset (&pag_dev); /* clear page tables */ set_dyn_ptrs (); /* set dynamic ptrs */ return FALSE; } t_bool rdebr (a10 ea, int32 prv) { Write (ea, (ebr & EBR_MASK), prv); return FALSE; } t_bool wrubr (a10 ea, int32 prv) { d10 val = Read (ea, prv); d10 ubr_mask = (Q_ITS)? PAMASK: UBR_UBRMASK; /* ITS: ubr is wd addr */ if (val & UBR_SETACB) /* set AC's? */ ubr = ubr & ~UBR_ACBMASK; else val = val & ~UBR_ACBMASK; /* no, keep old val */ if (val & UBR_SETUBR) { /* set UBR? */ ubr = ubr & ~ubr_mask; pag_reset (&pag_dev); /* yes, clr pg tbls */ } else val = val & ~ubr_mask; /* no, keep old val */ ubr = (ubr | val) & (UBR_ACBMASK | ubr_mask); set_dyn_ptrs (); return FALSE; } t_bool rdubr (a10 ea, int32 prv) { ubr = ubr & (UBR_ACBMASK | (Q_ITS? PAMASK: UBR_UBRMASK)); Write (ea, UBRWORD, prv); return FALSE; } t_bool wrhsb (a10 ea, int32 prv) { hsb = Read (ea, prv) & PAMASK; return FALSE; } t_bool rdhsb (a10 ea, int32 prv) { Write (ea, hsb, prv); return FALSE; } /* TOPS20 pager instructions */ t_bool wrspb (a10 ea, int32 prv) { spt = Read (ea, prv); return FALSE; } t_bool rdspb (a10 ea, int32 prv) { Write (ea, spt, prv); return FALSE; } t_bool wrcsb (a10 ea, int32 prv) { cst = Read (ea, prv); return FALSE; } t_bool rdcsb (a10 ea, int32 prv) { Write (ea, cst, prv); return FALSE; } t_bool wrpur (a10 ea, int32 prv) { pur = Read (ea, prv); return FALSE; } t_bool rdpur (a10 ea, int32 prv) { Write (ea, pur, prv); return FALSE; } t_bool wrcstm (a10 ea, int32 prv) { cstm = Read (ea, prv); if ((cpu_unit.flags & UNIT_T20) && (ea == 040127)) cstm = 0770000000000; return FALSE; } t_bool rdcstm (a10 ea, int32 prv) { Write (ea, cstm, prv); return FALSE; } /* ITS pager instructions The KS10 does not implement the JPC option. */ t_bool clrcsh (a10 ea, int32 prv) { return FALSE; } t_bool ldbr1 (a10 ea, int32 prv) { dbr1 = ea; pag_reset (&pag_dev); return FALSE; } t_bool sdbr1 (a10 ea, int32 prv) { Write (ea, dbr1, prv); return FALSE; } t_bool ldbr2 (a10 ea, int32 prv) { dbr2 = ea; pag_reset (&pag_dev); return FALSE; } t_bool sdbr2 (a10 ea, int32 prv) { Write (ea, dbr2, prv); return FALSE; } t_bool ldbr3 (a10 ea, int32 prv) { dbr3 = ea; pag_reset (&pag_dev); return FALSE; } t_bool sdbr3 (a10 ea, int32 prv) { Write (ea, dbr3, prv); return FALSE; } t_bool ldbr4 (a10 ea, int32 prv) { dbr4 = ea; pag_reset (&pag_dev); return FALSE; } t_bool sdbr4 (a10 ea, int32 prv) { Write (ea, dbr4, prv); return FALSE; } t_bool wrpcst (a10 ea, int32 prv) { pcst = Read (ea, prv); return FALSE; } t_bool rdpcst (a10 ea, int32 prv) { Write (ea, pcst, prv); return FALSE; } t_bool lpmr (a10 ea, int32 prv) { d10 val; val = Read (ADDA (ea, 2), prv); dbr1 = (a10) (Read (ea, prv) & AMASK); dbr2 = (a10) (Read (ADDA (ea, 1), prv) & AMASK); quant = val; pag_reset (&pag_dev); return FALSE; } t_bool spm (a10 ea, int32 prv) { ReadM (ADDA (ea, 2), prv); Write (ea, dbr1, prv); Write (ADDA (ea, 1), dbr2, prv); Write (ADDA (ea, 2), quant, prv); return FALSE; } /* Simulator interface routines */ t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { int32 tbln = uptr - pag_unit; if (addr >= PTBL_MEMSIZE) return SCPE_NXM; *vptr = tbln? uptbl[addr]: eptbl[addr];; return SCPE_OK; } t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { int32 tbln = uptr - pag_unit; if (addr >= PTBL_MEMSIZE) return SCPE_NXM; if (tbln) uptbl[addr] = (int32) val & PTBL_MASK; else eptbl[addr] = (int32) val & PTBL_MASK; return SCPE_OK; } t_stat pag_reset (DEVICE *dptr) { int32 i; for (i = 0; i < PTBL_MEMSIZE; i++) { eptbl[i] = uptbl[i] = 0; physptbl[i] = (i << PAG_V_PN) + PTBL_M + PTBL_V; } return SCPE_OK; } simh-3.8.1/PDP10/pdp10_defs.h0000644000175000017500000011417711111665402013533 0ustar vlmvlm/* pdp10_defs.h: PDP-10 simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 01-Feb-07 RMS Added CD support 29-Oct-06 RMS Added clock coscheduling function 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility 19-May-03 RMS Revised for new conditional compilation scheme 09-Jan-03 RMS Added DEUNA/DELUA support 29-Sep-02 RMS Added variable vector, RX211 support 22-Apr-02 RMS Removed magtape record length error 20-Jan-02 RMS Added multiboard DZ11 support 23-Oct-01 RMS New IO page address constants 19-Oct-01 RMS Added DZ11 definitions 07-Sep-01 RMS Revised for PDP-11 multi-level interrupts 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 29-Aug-01 RMS Corrected models and dates (found by Lars Brinkhoff) 01-Jun-01 RMS Updated DZ11 vector definitions 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug */ #ifndef _PDP10_DEFS_H_ #define _PDP10_DEFS_H_ 0 #ifndef VM_PDP10 #define VM_PDP10 0 #endif #include "sim_defs.h" /* simulator defns */ /* Digital Equipment Corporation's 36b family had six implementations: name mips comments PDP-6 0.25 Original 36b implementation, 1964 KA10 0.38 First PDP-10, flip chips, 1967 KI10 0.72 First paging system, flip chip + MSI, 1972 KL10 1.8 First ECL system, ECL 10K, 1975 KL10B 1.8 Expanded addressing, ECL 10K, 1978 KS10 0.3 Last 36b system, 2901 based, 1979 In addition, it ran four major (incompatible) operating systems: name company comments TOPS-10 DEC Original timesharing system ITS MIT "Incompatible Timesharing System" TENEX BBN ARPA-sponsored, became TOPS-20 DEC Commercial version of TENEX All of the implementations differ from one another, in instruction set, I/O structure, and memory management. Further, each of the operating systems customized the microcode of the paging systems (KI10, KL10, KS10) for additional instructions and specialized memory management. As a result, there is no "reference implementation" for the 36b family that will run all programs and all operating systems. The conditionalization and generality needed to support the full matrix of models and operating systems, and to support 36b hardware on 32b data types, is beyond the scope of this project. Instead, this simulator emulates one model -- the KS10. It has the best documentation and allows reuse of some of the Unibus peripheral emulators written for the PDP-11 simulator. Further, the simulator requires that the underlying compiler support 64b integer data types, allowing 36b data to be maintained in a single data item. Lastly, the simulator implements the maximum memory size, so that NXM's never happen. */ /* Data types */ typedef int32 a10; /* PDP-10 addr (30b) */ typedef t_int64 d10; /* PDP-10 data (36b) */ /* Abort codes, used to sort out longjmp's back to the main loop Codes > 0 are simulator stop codes Codes < 0 are internal aborts Code = 0 stops execution for an interrupt check */ #define STOP_HALT 1 /* halted */ #define STOP_IBKPT 2 /* breakpoint */ #define STOP_ILLEG 3 /* illegal instr */ #define STOP_ILLINT 4 /* illegal intr inst */ #define STOP_PAGINT 5 /* page fail in intr */ #define STOP_ZERINT 6 /* zero vec in intr */ #define STOP_NXMPHY 7 /* nxm on phys ref */ #define STOP_IND 8 /* indirection loop */ #define STOP_XCT 9 /* XCT loop */ #define STOP_ILLIOC 10 /* invalid UBA num */ #define STOP_ASTOP 11 /* address stop */ #define STOP_UNKNOWN 12 /* unknown stop */ #define PAGE_FAIL -1 /* page fail */ #define INTERRUPT -2 /* interrupt */ #define ABORT(x) longjmp (save_env, (x)) /* abort */ #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ /* Return codes from eXTEND */ #define XT_MUUO 0 /* invalid operation */ #define XT_SKIP 1 /* skip return */ #define XT_NOSK 2 /* no skip return */ /* Operating system flags, kept in cpu_unit.flags */ #define UNIT_V_ITS (UNIT_V_UF) /* ITS */ #define UNIT_V_T20 (UNIT_V_UF + 1) /* TOPS-20 */ #define UNIT_V_KLAD (UNIT_V_UF + 2) /* diagnostics */ #define UNIT_ITS (1 << UNIT_V_ITS) #define UNIT_T20 (1 << UNIT_V_T20) #define UNIT_KLAD (1 << UNIT_V_KLAD) #define Q_T10 ((cpu_unit.flags & (UNIT_ITS|UNIT_T20|UNIT_KLAD)) == 0) #define Q_ITS (cpu_unit.flags & UNIT_ITS) #define Q_T20 (cpu_unit.flags & UNIT_T20) #define Q_KLAD (cpu_unit.flags & UNIT_KLAD) #define Q_IDLE (sim_idle_enab) /* Architectural constants */ #define PASIZE 20 /* phys addr width */ #define MAXMEMSIZE (1 << PASIZE) /* maximum memory */ #define PAMASK ((1 << PASIZE) - 1) #define MEMSIZE MAXMEMSIZE /* fixed, KISS */ #define MEM_ADDR_NXM(x) ((x) >= MEMSIZE) #define VASIZE 18 /* virtual addr width */ #define AMASK ((1 << VASIZE) - 1) /* virtual addr mask */ #define LMASK 0777777000000 /* left mask */ #define LSIGN 0400000000000 /* left sign */ #define RMASK 0000000777777 /* right mask */ #define RSIGN 0000000400000 /* right sign */ #define DMASK 0777777777777 /* data mask */ #define SIGN 0400000000000 /* sign */ #define MMASK 0377777777777 /* magnitude mask */ #define ONES 0777777777777 #define MAXPOS 0377777777777 #define MAXNEG 0400000000000 /* Instruction format */ #define INST_V_OP 27 /* opcode */ #define INST_M_OP 0777 #define INST_V_DEV 26 #define INST_M_DEV 0177 /* device */ #define INST_V_AC 23 /* AC */ #define INST_M_AC 017 #define INST_V_IND 22 /* indirect */ #define INST_IND (1 << INST_V_IND) #define INST_V_XR 18 /* index */ #define INST_M_XR 017 #define OP_JRST 0254 /* JRST */ #define AC_XPCW 07 /* XPCW */ #define OP_JSR 0264 /* JSR */ #define GET_OP(x) ((int32) (((x) >> INST_V_OP) & INST_M_OP)) #define GET_DEV(x) ((int32) (((x) >> INST_V_DEV) & INST_M_DEV)) #define GET_AC(x) ((int32) (((x) >> INST_V_AC) & INST_M_AC)) #define TST_IND(x) ((x) & INST_IND) #define GET_XR(x) ((int32) (((x) >> INST_V_XR) & INST_M_XR)) #define GET_ADDR(x) ((a10) ((x) & AMASK)) /* Byte pointer format */ #define BP_V_P 30 /* position */ #define BP_M_P 077 #define BP_P 0770000000000 #define BP_V_S 24 /* size */ #define BP_M_S 077 #define BP_S 0007700000000 #define GET_P(x) ((int32) (((x) >> BP_V_P) & BP_M_P)) #define GET_S(x) ((int32) (((x) >> BP_V_S) & BP_M_S)) #define PUT_P(b,x) (((b) & ~BP_P) | ((((t_int64) (x)) & BP_M_P) << BP_V_P)) /* Flags (stored in their own halfword) */ #define F_V_AOV 17 /* arithmetic ovflo */ #define F_V_C0 16 /* carry 0 */ #define F_V_C1 15 /* carry 1 */ #define F_V_FOV 14 /* floating ovflo */ #define F_V_FPD 13 /* first part done */ #define F_V_USR 12 /* user mode */ #define F_V_UIO 11 /* user I/O mode */ #define F_V_PUB 10 /* public mode */ #define F_V_AFI 9 /* addr fail inhibit */ #define F_V_T2 8 /* trap 2 */ #define F_V_T1 7 /* trap 1 */ #define F_V_FXU 6 /* floating exp unflo */ #define F_V_DCK 5 /* divide check */ #define F_AOV (1 << F_V_AOV) #define F_C0 (1 << F_V_C0) #define F_C1 (1 << F_V_C1) #define F_FOV (1 << F_V_FOV) #define F_FPD (1 << F_V_FPD) #define F_USR (1 << F_V_USR) #define F_UIO (1 << F_V_UIO) #define F_PUB (1 << F_V_PUB) #define F_AFI (1 << F_V_AFI) #define F_T2 (1 << F_V_T2) #define F_T1 (1 << F_V_T1) #define F_TR (F_T1 | F_T2) #define F_FXU (1 << F_V_FXU) #define F_DCK (1 << F_V_DCK) #define F_1PR (F_AFI) /* ITS: 1-proceed */ #define F_MASK 0777740 /* all flags */ #define SETF(x) flags = flags | (x) #define CLRF(x) flags = flags & ~(x) #define TSTF(x) (flags & (x)) #define GET_TRAPS(x) (((x) & (F_T2 | F_T1)) >> F_V_T1) /* Priority interrupt system */ #define PI_CPRQ 020000 /* drop prog req */ #define PI_INIT 010000 /* clear pi system */ #define PI_SPRQ 004000 /* set prog req */ #define PI_SENB 002000 /* set enables */ #define PI_CENB 001000 /* clear enables */ #define PI_CON 000400 /* turn off pi system */ #define PI_SON 000200 /* turn on pi system */ #define PI_M_LVL 000177 /* level mask */ #define PI_V_PRQ 18 /* in CONI */ #define PI_V_ACT 8 #define PI_V_ON 7 #define PI_V_ENB 0 /* Arithmetic processor flags */ #define APR_SENB 0100000 /* set enable */ #define APR_CENB 0040000 /* clear enable */ #define APR_CFLG 0020000 /* clear flag */ #define APR_SFLG 0010000 /* set flag */ #define APR_IRQ 0000010 /* int request */ #define APR_M_LVL 0000007 /* pi level */ #define APR_V_FLG 4 /* system flags */ #define APR_M_FLG 0377 #define APRF_ITC (002000 >> APR_V_FLG) /* int console flag */ #define APRF_NXM (000400 >> APR_V_FLG) /* nxm flag */ #define APRF_TIM (000040 >> APR_V_FLG) /* timer request */ #define APRF_CON (000020 >> APR_V_FLG) /* console int */ #define APR_GETF(x) (((x) >> APR_V_FLG) & APR_M_FLG) /* Virtual address, DEC paging */ #define PAG_V_OFF 0 /* offset - must be 0 */ #define PAG_N_OFF 9 /* page offset width */ #define PAG_SIZE 01000 /* page offset size */ #define PAG_M_OFF 0777 /* mask for offset */ #define PAG_V_PN PAG_N_OFF /* page number */ #define PAG_N_PPN (PASIZE - PAG_N_OFF) /* phys pageno width */ #define PAG_M_PPN 03777 /* phys pageno mask */ #define PAG_PPN 03777000 #define PAG_N_VPN (VASIZE - PAG_N_OFF) /* virt pageno width */ #define PAG_M_VPN 0777 /* virt pageno mask */ #define PAG_VPN 0777000 #define PAG_GETOFF(x) ((x) & PAG_M_OFF) #define PAG_GETVPN(x) (((x) >> PAG_V_PN) & PAG_M_VPN) #define PAG_XPTEPA(p,x) (((p) + PAG_GETOFF (x)) & PAMASK) #define PAG_PTEPA(p,x) (((((int32) (p)) & PTE_PPMASK) << PAG_V_PN) + PAG_GETOFF (x)) /* Page table entry, TOPS-10 paging */ #define PTE_T10_A 0400000 /* T10: access */ #define PTE_T10_P 0200000 /* T10: public */ #define PTE_T10_W 0100000 /* T10: writeable */ #define PTE_T10_S 0040000 /* T10: software */ #define PTE_T10_C 0020000 /* T10: cacheable */ #define PTE_PPMASK PAG_M_PPN /* Page table entry, TOPS-20 paging */ #define PTE_T20_V_TYP 33 /* T20: pointer type */ #define PTE_T20_M_TYP 07 #define T20_NOA 0 /* no access */ #define T20_IMM 1 /* immediate */ #define T20_SHR 2 /* shared */ #define T20_IND 3 /* indirect */ #define PTE_T20_W 0020000000000 /* T20: writeable */ #define PTE_T20_C 0004000000000 /* T20: cacheable */ #define PTE_T20_STM 0000077000000 /* T20: storage medium */ #define PTE_T20_V_PMI 18 /* page map index */ #define PTE_T20_M_PMI 0777 #define T20_GETTYP(x) ((int32) (((x) >> PTE_T20_V_TYP) & PTE_T20_M_TYP)) #define T20_GETPMI(x) ((int32) (((x) >> PTE_T20_V_PMI) & PTE_T20_M_PMI)) /* CST entry, TOPS-20 paging */ #define CST_AGE 0770000000000 /* age field */ #define CST_M 0000000000001 /* modified */ /* Page fail word, DEC paging */ #define PF_USER 0400000000000 /* user mode */ #define PF_HARD 0200000000000 /* nx I/O reg */ #define PF_NXM 0370000000000 /* nx memory */ #define PF_T10_A 0100000000000 /* T10: pte A bit */ #define PF_T10_W 0040000000000 /* T10: pte W bit */ #define PF_T10_S 0020000000000 /* T10: pte S bit */ #define PF_T20_DN 0100000000000 /* T20: eval done */ #define PF_T20_M 0040000000000 /* T20: modified */ #define PF_T20_W 0020000000000 /* T20: writeable */ #define PF_WRITE 0010000000000 /* write reference */ #define PF_PUB 0004000000000 /* pte public bit */ #define PF_C 0002000000000 /* pte C bit */ #define PF_VIRT 0001000000000 /* pfl: virt ref */ #define PF_NXMP 0001000000000 /* nxm: phys ref */ #define PF_IO 0000200000000 /* I/O reference */ #define PF_BYTE 0000020000000 /* I/O byte ref */ /* Virtual address, ITS paging */ #define ITS_V_OFF 0 /* offset - must be 0 */ #define ITS_N_OFF 10 /* page offset width */ #define ITS_SIZE 02000 /* page offset size */ #define ITS_M_OFF 01777 /* mask for offset */ #define ITS_V_PN ITS_N_OFF /* page number */ #define ITS_N_PPN (PASIZE- ITS_N_OFF) /* phys pageno width */ #define ITS_M_PPN 01777 /* phys pageno mask */ #define ITS_PPN 03776000 #define ITS_N_VPN (VASIZE - ITS_N_OFF) /* virt pageno width */ #define ITS_M_VPN 0377 /* virt pageno mask */ #define ITS_VPN 0776000 #define ITS_GETVPN(x) (((x) >> ITS_V_PN) & ITS_M_VPN) /* Page table entry, ITS paging */ #define PTE_ITS_V_ACC 16 /* access field */ #define PTE_ITS_M_ACC 03 #define ITS_ACC_NO 0 /* no access */ #define ITS_ACC_RO 1 /* read only */ #define ITS_ACC_RWF 2 /* read-write first */ #define ITS_ACC_RW 3 /* read write */ #define PTE_ITS_AGE 0020000 /* age */ #define PTE_ITS_C 0010000 /* cacheable */ #define PTE_ITS_PPMASK ITS_M_PPN #define ITS_GETACC(x) (((x) >> PTE_ITS_V_ACC) & PTE_ITS_M_ACC) /* Page fail word, ITS paging */ #define PF_ITS_WRITE 0010000000000 /* write reference */ #define PF_ITS_V_ACC 28 /* access from PTE */ /* Page table fill operations */ #define PTF_RD 0 /* read check */ #define PTF_WR 1 /* write check */ #define PTF_MAP 2 /* map instruction */ #define PTF_CON 4 /* console access */ /* User base register */ #define UBR_SETACB 0400000000000 /* set AC blocks */ #define UBR_SETUBR 0100000000000 /* set UBR */ #define UBR_V_CURAC 27 /* current AC block */ #define UBR_V_PRVAC 24 /* previous AC block */ #define UBR_M_AC 07 #define UBR_ACBMASK 0007700000000 #define UBR_V_UBR 0 /* user base register */ #define UBR_N_UBR 11 #define UBR_M_UBR 03777 #define UBR_UBRMASK 0000000003777 #define UBR_GETCURAC(x) ((int32) (((x) >> UBR_V_CURAC) & UBR_M_AC)) #define UBR_GETPRVAC(x) ((int32) (((x) >> UBR_V_PRVAC) & UBR_M_AC)) #define UBR_GETUBR(x) ((int32) (((x) >> UBR_V_UBR) & PAG_M_PPN)) #define UBRWORD (ubr | UBR_SETACB | UBR_SETUBR) /* Executive base register */ #define EBR_V_T20P 14 /* TOPS20 paging */ #define EBR_T20P (1u << EBR_V_T20P) #define EBR_V_PGON 13 /* enable paging */ #define EBR_PGON (1u << EBR_V_PGON) #define EBR_V_EBR 0 /* exec base register */ #define EBR_N_EBR 11 #define EBR_M_EBR 03777 #define EBR_MASK (EBR_T20P | EBR_PGON | (EBR_M_EBR << EBR_V_EBR)) #define EBR_GETEBR(x) ((int32) (((x) >> EBR_V_EBR) & PAG_M_PPN)) #define PAGING (ebr & EBR_PGON) #define T20PAG (ebr & EBR_T20P) /* AC and mapping contexts There are only two real contexts for selecting the AC block and the memory map: current and previous. However, PXCT allows the choice of current versus previous to be made selectively for various parts of an instruction. The PXCT flags are kept in a dynamic CPU variable. */ #define EA_PXCT 010 /* eff addr calc */ #define OPND_PXCT 004 /* operand, bdst */ #define EABP_PXCT 002 /* bp eff addr calc */ #define BSTK_PXCT 001 /* stk, bp op, bsrc */ #define XSRC_PXCT 002 /* extend source */ #define XDST_PXCT 001 /* extend destination */ #define MM_CUR 000 /* current context */ #define MM_EA (pflgs & EA_PXCT) #define MM_OPND (pflgs & OPND_PXCT) #define MM_EABP (pflgs & EABP_PXCT) #define MM_BSTK (pflgs & BSTK_PXCT) /* Accumulator access. The AC blocks are kept in array acs[AC_NBLK * AC_NUM]. Two pointers are provided to the bases of the current and previous blocks. Macro AC selects the current AC block; macro XR selects current or previous, depending on whether the selected bit in the "pxct in progress" flag is set. */ #define AC_NUM 16 /* # AC's/block */ #define AC_NBLK 8 /* # AC blocks */ #define AC(r) (ac_cur[r]) /* AC select current */ #define XR(r,prv) ((prv)? ac_prv[r]: ac_cur[r]) /* AC select context */ #define ADDAC(x,i) (((x) + (i)) & INST_M_AC) #define P1 ADDAC (ac, 1) /* User process table entries */ #define UPT_T10_UMAP 0000 /* T10: user map */ #define UPT_T10_X340 0400 /* T10: exec 340-377 */ #define UPT_TRBASE 0420 /* trap base */ #define UPT_MUUO 0424 /* MUUO block */ #define UPT_MUPC 0425 /* caller's PC */ #define UPT_T10_CTX 0426 /* T10: context */ #define UPT_T20_UEA 0426 /* T20: address */ #define UPT_T20_CTX 0427 /* T20: context */ #define UPT_ENPC 0430 /* MUUO new PC, exec */ #define UPT_1PO 0432 /* ITS 1-proc: old PC */ #define UPT_1PN 0433 /* ITS 1-proc: new PC */ #define UPT_UNPC 0434 /* MUUO new PC, user */ #define UPT_NPCT 1 /* PC offset if trap */ #define UPT_T10_PAG 0500 /* T10: page fail blk */ #define UPT_T20_PFL 0500 /* T20: page fail wd */ #define UPT_T20_OFL 0501 /* T20: flags */ #define UPT_T20_OPC 0502 /* T20: old PC */ #define UPT_T20_NPC 0503 /* T20: new PC */ #define UPT_T20_SCTN 0540 /* T20: section 0 ptr */ /* Exec process table entries */ #define EPT_PIIT 0040 /* PI interrupt table */ #define EPT_UBIT 0100 /* Unibus intr table */ #define EPT_T10_X400 0200 /* T10: exec 400-777 */ #define EPT_TRBASE 0420 /* trap base */ #define EPT_ITS_PAG 0440 /* ITS: page fail blk */ #define EPT_T20_SCTN 0540 /* T20: section 0 ptr */ #define EPT_T10_X000 0600 /* T10: exec 0 - 337 */ /* Microcode constants */ #define UC_INHCST 0400000000000 /* inhibit CST update */ #define UC_UBABLT 0040000000000 /* BLTBU and BLTUB */ #define UC_KIPAGE 0020000000000 /* "KI" paging */ #define UC_KLPAGE 0010000000000 /* "KL" paging */ #define UC_VERDEC (0130 << 18) /* ucode version */ #define UC_VERITS (262u << 18) #define UC_SERDEC 4097 /* serial number */ #define UC_SERITS 1729 #define UC_AIDDEC (UC_INHCST | UC_UBABLT | UC_KIPAGE | UC_KLPAGE | \ UC_VERDEC | UC_SERDEC) #define UC_AIDITS (UC_KIPAGE | UC_VERITS | UC_SERITS) #define UC_HSBDEC 0376000 /* DEC initial HSB */ #define UC_HSBITS 0000500 /* ITS initial HSB */ /* Front end communications region */ #define FE_SWITCH 030 /* halt switch */ #define FE_KEEPA 031 /* keep alive */ #define FE_CTYIN 032 /* console in */ #define FE_CTYOUT 033 /* console out */ #define FE_KLININ 034 /* KLINIK in */ #define FE_KLINOUT 035 /* KLINIK out */ #define FE_RHBASE 036 /* boot: RH11 addr */ #define FE_UNIT 037 /* boot: unit num */ #define FE_MTFMT 040 /* boot: magtape params */ #define FE_CVALID 0400 /* char valid flag */ /* Halfword operations */ #define ADDL(x,y) (((x) + ((y) << 18)) & LMASK) #define ADDR(x,y) (((x) + (y)) & RMASK) #define INCL(x) ADDL (x, 1) #define INCR(x) ADDR (x, 1) #define AOB(x) (INCL (x) | INCR(x)) #define SUBL(x,y) (((x) - ((y) << 18)) & LMASK) #define SUBR(x,y) (((x) - (y)) & RMASK) #define DECL(x) SUBL (x, 1) #define DECR(x) SUBR (x, 1) #define SOB(x) (DECL (x) | DECR(x)) #define LLZ(x) ((x) & LMASK) #define RLZ(x) (((x) << 18) & LMASK) #define RRZ(x) ((x) & RMASK) #define LRZ(x) (((x) >> 18) & RMASK) #define LIT8(x) (((x) & RSIGN)? \ (((x) & 0377)? (-(x) & 0377): 0400): ((x) & 0377)) /* Fullword operations */ #define INC(x) (((x) + 1) & DMASK) #define DEC(x) (((x) - 1) & DMASK) #define SWP(x) ((((x) << 18) & LMASK) | (((x) >> 18) & RMASK)) #define XWD(x,y) (((((d10) (x)) << 18) & LMASK) | (((d10) (y)) & RMASK)) #define SETS(x) ((x) | SIGN) #define CLRS(x) ((x) & ~SIGN) #define TSTS(x) ((x) & SIGN) #define NEG(x) (-(x) & DMASK) #define ABS(x) (TSTS (x)? NEG(x): (x)) #define SXT(x) (TSTS (x)? (x) | ~DMASK: (x)) /* Doubleword operations (on 2-word arrays) */ #define DMOVN(rs) rs[1] = (-rs[1]) & MMASK; \ rs[0] = (~rs[0] + (rs[1] == 0)) & DMASK #define MKDNEG(rs) rs[1] = SETS (-rs[1]) & DMASK; \ rs[0] = (~rs[0] + (rs[1] == MAXNEG)) & DMASK #define DCMPGE(a,b) ((a[0] > b[0]) || ((a[0] == b[0]) && (a[1] >= b[1]))) /* Address operations */ #define ADDA(x,i) (((x) + (i)) & AMASK) #define INCA(x) ADDA (x, 1) /* Unibus adapter control/status register */ #define UBCS_TMO 0400000 /* timeout */ #define UBCS_BMD 0200000 /* bad mem data NI */ #define UBCS_PAR 0100000 /* parity error NI */ #define UBCS_NXD 0040000 /* nx device */ #define UBCS_HI 0004000 /* irq on BR7 or BR6 */ #define UBCS_LO 0002000 /* irq on BR5 or BR4 */ #define UBCS_PWR 0001000 /* power low NI */ #define UBCS_DXF 0000200 /* disable xfer NI*/ #define UBCS_INI 0000100 /* Unibus init */ #define UBCS_RDZ 0030500 /* read as zero */ #define UBCS_RDW 0000277 /* read/write bits */ #define UBCS_V_LHI 3 /* hi pri irq level */ #define UBCS_V_LLO 0 /* lo pri irq level */ #define UBCS_M_PRI 07 #define UBCS_GET_HI(x) (((x) >> UBCS_V_LHI) & UBCS_M_PRI) #define UBCS_GET_LO(x) (((x) >> UBCS_V_LLO) & UBCS_M_PRI) /* Unibus adapter page map */ #define UBANUM 2 /* # of Unibus adapters */ #define UMAP_ASIZE 6 /* address size */ #define UMAP_MEMSIZE (1 << UMAP_ASIZE) /* length */ #define UMAP_AMASK (UMAP_MEMSIZE - 1) #define UMAP_V_RRV 30 /* read reverse */ #define UMAP_V_DSB 29 /* 16b on NPR read */ #define UMAP_V_FST 28 /* fast transfer */ #define UMAP_V_VLD 27 /* valid flag */ #define UMAP_RRV (1 << UMAP_V_RRV) #define UMAP_DSB (1 << UMAP_V_DSB) #define UMAP_FST (1 << UMAP_V_FST) #define UMAP_VLD (1 << UMAP_V_VLD) #define UMAP_V_FLWR 14 /* flags as written */ #define UMAP_V_FLRD 27 /* flags as stored */ #define UMAP_M_FL 017 #define UMAP_V_PNWR 0 /* page num, write */ #define UMAP_V_PNRD 9 /* page num, read */ #define UMAP_M_PN 03777 #define UMAP_MASK ((UMAP_M_FL << UMAP_V_FLRD) | (UMAP_M_PN << UMAP_V_PNRD)) #define UMAP_POSFL(x) (((x) & (UMAP_M_FL << UMAP_V_FLWR)) \ << (UMAP_V_FLRD - UMAP_V_FLWR)) #define UMAP_POSPN(x) (((x) & (UMAP_M_PN << UMAP_V_PNWR)) \ << (UMAP_V_PNRD - UMAP_V_PNWR)) /* Unibus I/O constants */ #define READ 0 /* PDP11 compatible */ /* #define READC 1 /* console read */ #define WRITE 2 /* #define WRITEC 3 /* console write */ #define WRITEB 4 #define IO_V_UBA 18 /* UBA in I/O addr */ #define IO_N_UBA 16 /* max num of UBA's */ #define IO_M_UBA (IO_N_UBA - 1) #define IO_UBA1 (1 << IO_V_UBA) #define IO_UBA3 (3 << IO_V_UBA) #define GET_IOUBA(x) (((x) >> IO_V_UBA) & IO_M_UBA) /* Device information block */ #define VEC_DEVMAX 8 /* max device vec */ struct pdp_dib { uint32 ba; /* base addr */ uint32 lnt; /* length */ t_stat (*rd)(int32 *dat, int32 ad, int32 md); t_stat (*wr)(int32 dat, int32 ad, int32 md); int32 vnum; /* vectors: number */ int32 vloc; /* locator */ int32 vec; /* value */ int32 (*ack[VEC_DEVMAX])(void); /* ack routines */ }; typedef struct pdp_dib DIB; /* I/O system parameters */ #define DZ_MUXES 4 /* max # of muxes */ #define DZ_LINES 8 /* lines per mux */ #define DIB_MAX 100 /* max DIBs */ #define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ #define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ #define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus, mem <= 256KB */ #define DEV_V_FLTA (DEV_V_UF + 3) /* float addr */ #define DEV_UBUS (1u << DEV_V_UBUS) #define DEV_QBUS (1u << DEV_V_QBUS) #define DEV_Q18 (1u << DEV_V_Q18) #define DEV_FLTA (1u << DEV_V_FLTA) #define UNIBUS TRUE /* 18b only */ #define DEV_RDX 8 /* default device radix */ /* I/O page layout */ #define IOPAGEBASE 0760000 /* I/O page base */ #define IOBA_UBMAP 0763000 #define IOBA_UBMAP1 (IO_UBA1 + IOBA_UBMAP) /* Unibus 1 map */ #define IOLN_UBMAP1 0100 #define IOBA_UBCS1 (IO_UBA1 + 0763100) /* Unibus 1 c/s reg */ #define IOLN_UBCS1 001 #define IOBA_UBMNT1 (IO_UBA1 + 0763101) /* Unibus 1 maint reg */ #define IOLN_UBMNT1 001 #define IOBA_RP (IO_UBA1 + 0776700) /* RH11/disk */ #define IOLN_RP 050 #define IOBA_DZ (IO_UBA3 + 0760010) /* DZ11 */ #define IOLN_DZ 010 #define IOBA_TCU (IO_UBA3 + 0760770) /* TCU150 */ #define IOLN_TCU 006 #define IOBA_UBMAP3 (IO_UBA3 + IOBA_UBMAP) /* Unibus 3 map */ #define IOLN_UBMAP3 0100 #define IOBA_UBCS3 (IO_UBA3 + 0763100) /* Unibus 3 c/s reg */ #define IOLN_UBCS3 001 #define IOBA_UBMNT3 (IO_UBA3 + 0763101) /* Unibus 3 maint reg */ #define IOLN_UBMNT3 001 #define IOBA_XU (IO_UBA3 + 0774510) /* DEUNA/DELUA */ #define IOLN_XU 010 #define IOBA_CR (IO_UBA3 + 0777160) /* CD/CR/CM */ #define IOLN_CR 010 #define IOBA_RY (IO_UBA3 + 0777170) /* RX211 */ #define IOLN_RY 004 #define IOBA_TU (IO_UBA3 + 0772440) /* RH11/tape */ #define IOLN_TU 034 #define IOBA_LP20 (IO_UBA3 + 0775400) /* LP20 */ #define IOLN_LP20 020 #define IOBA_PTR (IO_UBA3 + 017550) /* PC11 reader */ #define IOLN_PTR 004 #define IOBA_PTP (IO_UBA3 + 017554) /* PC11 punch */ #define IOLN_PTP 004 /* Common Unibus CSR flags */ #define CSR_V_GO 0 /* go */ #define CSR_V_IE 6 /* interrupt enable */ #define CSR_V_DONE 7 /* done */ #define CSR_V_BUSY 11 /* busy */ #define CSR_V_ERR 15 /* error */ #define CSR_GO (1u << CSR_V_GO) #define CSR_IE (1u << CSR_V_IE) #define CSR_DONE (1u << CSR_V_DONE) #define CSR_BUSY (1u << CSR_V_BUSY) #define CSR_ERR (1u << CSR_V_ERR) /* I/O system definitions, lifted from the PDP-11 simulator Interrupt assignments, priority is right to left <3:0> = BR7 <7:4> = BR6 <19:8> = BR5 <30:20> = BR4 */ #define INT_V_RP 6 /* RH11/RP,RM drives */ #define INT_V_TU 7 /* RH11/TM03/TU45 */ #define INT_V_XU 15 /* DEUNA/DELUA */ #define INT_V_DZRX 16 /* DZ11 */ #define INT_V_DZTX 17 #define INT_V_RY 18 /* RX211 */ #define INT_V_PTR 24 /* PC11 */ #define INT_V_PTP 25 #define INT_V_LP20 26 /* LPT20 */ #define INT_V_CR 27 /* CD20 (CD11) */ #define INT_RP (1u << INT_V_RP) #define INT_TU (1u << INT_V_TU) #define INT_XU (1u << INT_V_XU) #define INT_DZRX (1u << INT_V_DZRX) #define INT_DZTX (1u << INT_V_DZTX) #define INT_RY (1u << INT_V_RY) #define INT_PTR (1u << INT_V_PTR) #define INT_PTP (1u << INT_V_PTP) #define INT_LP20 (1u << INT_V_LP20) #define INT_CR (1u << INT_V_CR) #define IPL_RP 6 /* int levels */ #define IPL_TU 6 #define IPL_XU 5 #define IPL_DZRX 5 #define IPL_DZTX 5 #define IPL_RY 5 #define IPL_PTR 4 #define IPL_PTP 4 #define IPL_LP20 4 #define IPL_CR 4 #define INT_UB1 INT_RP /* on Unibus 1 */ #define INT_UB3 (0xFFFFFFFFu & ~INT_UB1) /* on Unibus 3 */ #define INT_IPL7 0x0000000F /* int level masks */ #define INT_IPL6 0x000000F0 #define INT_IPL5 0x000FFF00 #define INT_IPL4 0x3FF00000 #define VEC_Q 0000 /* vector base */ #define VEC_PTR 0070 /* interrupt vectors */ #define VEC_PTP 0074 #define VEC_XU 0120 #define VEC_TU 0224 #define VEC_CR 0230 #define VEC_RP 0254 #define VEC_RY 0264 #define VEC_DZRX 0340 #define VEC_DZTX 0344 #define VEC_LP20 0754 #define IVCL(dv) (INT_V_##dv) #define IREQ(dv) int_req #define SET_INT(dv) IREQ(dv) = IREQ(dv) | (INT_##dv) #define CLR_INT(dv) IREQ(dv) = IREQ(dv) & ~(INT_##dv) /* Function prototypes */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat set_vec (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat auto_config (char *name, int32 num); int32 clk_cosched (int32 wait); /* Global data */ extern t_bool sim_idle_enab; #endif simh-3.8.1/PDP10/pdp10_sys.c0000644000175000017500000011470611107655252013430 0ustar vlmvlm/* pdp10_sys.c: PDP-10 simulator interface Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 01-Feb-07 RMS Added CD support 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) 09-Jan-03 RMS Added DEUNA/DELUA support 12-Sep-02 RMS Added RX211 support 22-Apr-02 RMS Removed magtape record length error 17-Sep-01 RMS Removed multiconsole support 25-Aug-01 RMS Enabled DZ11 27-May-01 RMS Added multiconsole support 29-Apr-01 RMS Fixed format for RDPCST, WRPCST Added CLRCSH for ITS 03-Apr-01 RMS Added support for loading EXE files 19-Mar-01 RMS Added support for loading SAV files 30-Oct-00 RMS Added support for examine to file */ #include "pdp10_defs.h" #include extern DEVICE cpu_dev; extern DEVICE pag_dev; extern DEVICE tim_dev; extern DEVICE fe_dev; extern DEVICE uba_dev; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE rp_dev; extern DEVICE tu_dev; extern DEVICE dz_dev; extern DEVICE ry_dev; extern DEVICE cr_dev; extern DEVICE lp20_dev; extern DEVICE xu_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern d10 *M; extern a10 saved_PC; /* 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[] = "PDP-10"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &pag_dev, &tim_dev, &fe_dev, &uba_dev, &ptr_dev, &ptp_dev, &ry_dev, &lp20_dev, &cr_dev, &rp_dev, &tu_dev, &dz_dev, &xu_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "HALT instruction", "Breakpoint", "Illegal instruction", "Illegal interrupt instruction", "Paging error in interrupt", "Zero vector table", "NXM on UPT/EPT reference", "Nested indirect address limit exceeded", "Nested XCT limit exceeded", "Invalid I/O controller", "Address stop", "Panic stop" }; /* Binary loader, supports RIM10, SAV, EXE */ #define FMT_R 1 /* RIM10 */ #define FMT_S 2 /* SAV */ #define FMT_E 3 /* EXE */ #define EXE_DIR 01776 /* EXE directory */ #define EXE_VEC 01775 /* EXE entry vec */ #define EXE_PDV 01774 /* EXE ignored */ #define EXE_END 01777 /* EXE end /* RIM10 loader RIM10 format is a binary paper tape format (all data frames are 200 or greater). It consists of blocks containing -count,,origin-1 word : word checksum (includes IOWD) : JRST start */ d10 getrimw (FILE *fileref) { int32 i, tmp; d10 word; word = 0; for (i = 0; i < 6;) { if ((tmp = getc (fileref)) == EOF) return -1; if (tmp & 0200) { word = (word << 6) | ((d10) tmp & 077); i++; } } return word; } t_stat load_rim (FILE *fileref) { d10 count, cksm, data; a10 pa; int32 op; for ( ;; ) { /* loop until JRST */ count = cksm = getrimw (fileref); /* get header */ if (count < 0) /* read err? */ return SCPE_FMT; if (TSTS (count)) { /* hdr = IOWD? */ for ( ; TSTS (count); count = AOB (count)) { data = getrimw (fileref); /* get data wd */ if (data < 0) return SCPE_FMT; cksm = cksm + data; /* add to cksm */ pa = ((a10) count + 1) & AMASK; /* store */ M[pa] = data; } /* end for */ data = getrimw (fileref); /* get cksm */ if (data < 0) return SCPE_FMT; if ((cksm + data) & DMASK) /* test cksm */ return SCPE_CSUM; } /* end if count */ else { op = GET_OP (count); /* not IOWD */ if (op != OP_JRST) /* JRST? */ return SCPE_FMT; saved_PC = (a10) count & AMASK; /* set PC */ break; } /* end else */ } /* end for */ return SCPE_OK; } /* SAV file loader SAV format is a disk file format (36b words). It consists of blocks containing: -count,,origin-1 word : word : JRST start */ t_stat load_sav (FILE *fileref) { d10 count, data; a10 pa; int32 wc, op; for ( ;; ) { /* loop */ wc = fxread (&count, sizeof (d10), 1, fileref); /* read IOWD */ if (wc == 0) /* done? */ return SCPE_OK; if (TSTS (count)) { /* IOWD? */ for ( ; TSTS (count); count = AOB (count)) { wc = fxread (&data, sizeof (d10), 1, fileref); if (wc == 0) return SCPE_FMT; pa = ((a10) count + 1) & AMASK; /* store data */ M[pa] = data; } /* end for */ } /* end if count*/ else { op = GET_OP (count); /* not IOWD */ if (op != OP_JRST) /* JRST? */ return SCPE_FMT; saved_PC = (a10) count & AMASK; /* set PC */ break; } /* end else */ } /* end for */ return SCPE_OK; } /* EXE file loader EXE format is a disk file format (36b words). It consists of blocks containing: block type,,total words = n n - 1 data words Block types are EXE_DIR (1776) directory EXE_VEC (1775) entry vector EXE_PDV (1774) optional blocks EXE_END (1777) end block The directory blocks are the most important and contain doubleword page loading information: word0<0:8> = flags <9:35> = page in file (0 if 0 page) word1<0:8> = repeat count - 1 <9:35> = page in memory */ #define DIRSIZ (2 * PAG_SIZE) t_stat load_exe (FILE *fileref) { d10 data, dirbuf[DIRSIZ], pagbuf[PAG_SIZE], entbuf[2]; int32 ndir, entvec, i, j, k, cont, bsz, bty, rpt, wc; int32 fpage, mpage; a10 ma; ndir = entvec = 0; /* no dir, entvec */ cont = 1; do { wc = fxread (&data, sizeof (d10), 1, fileref); /* read blk hdr */ if (wc == 0) /* error? */ return SCPE_FMT; bsz = (int32) ((data & RMASK) - 1); /* get count */ if (bsz <= 0) /* zero? */ return SCPE_FMT; bty = (int32) LRZ (data); /* get type */ switch (bty) { /* case type */ case EXE_DIR: /* directory */ if (ndir) /* got one */ return SCPE_FMT; ndir = fxread (dirbuf, sizeof (d10), bsz, fileref); if (ndir < bsz) /* error */ return SCPE_FMT; break; case EXE_PDV: /* ??? */ fseek (fileref, bsz * sizeof (d10), SEEK_CUR); break; case EXE_VEC: /* entry vec */ if (bsz != 2) /* must be 2 wds */ return SCPE_FMT; entvec = fxread (entbuf, sizeof (d10), bsz, fileref); if (entvec < 2) /* error? */ return SCPE_FMT; cont = 0; /* stop */ break; case EXE_END: /* end */ if (bsz != 0) /* must be hdr */ return SCPE_FMT; cont = 0; /* stop */ break; default: return SCPE_FMT; } /* end switch */ } while (cont); /* end do */ for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ fpage = (int32) (dirbuf[i] & RMASK); /* file page */ mpage = (int32) (dirbuf[i + 1] & RMASK); /* memory page */ rpt = (int32) ((dirbuf[i + 1] >> 27) + 1); /* repeat count */ for (j = 0; j < rpt; j++, mpage++) { /* loop thru rpts */ if (fpage) { /* file pages? */ fseek (fileref, (fpage << PAG_V_PN) * sizeof (d10), SEEK_SET); wc = fxread (pagbuf, sizeof (d10), PAG_SIZE, fileref); if (wc < PAG_SIZE) return SCPE_FMT; fpage++; } ma = mpage << PAG_V_PN; /* mem addr */ for (k = 0; k < PAG_SIZE; k++, ma++) { /* copy buf to mem */ if (MEM_ADDR_NXM (ma)) return SCPE_NXM; M[ma] = fpage? (pagbuf[k] & DMASK): 0; } /* end copy */ } /* end rpt */ } /* end directory */ if (entvec && entbuf[1]) saved_PC = (int32) entbuf[1] & RMASK; /* start addr */ return SCPE_OK; } /* Master loader */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { d10 data; int32 wc, fmt; extern int32 sim_switches; fmt = 0; /* no fmt */ if (sim_switches & SWMASK ('R')) /* -r? */ fmt = FMT_R; else if (sim_switches & SWMASK ('S')) /* -s? */ fmt = FMT_S; else if (sim_switches & SWMASK ('E')) /* -e? */ fmt = FMT_E; else if (match_ext (fnam, "RIM")) /* .RIM? */ fmt = FMT_R; else if (match_ext (fnam, "SAV")) /* .SAV? */ fmt = FMT_S; else if (match_ext (fnam, "EXE")) /* .EXE? */ fmt = FMT_E; else { wc = fxread (&data, sizeof (d10), 1, fileref); /* read hdr */ if (wc == 0) /* error? */ return SCPE_FMT; if (LRZ (data) == EXE_DIR) /* EXE magic? */ fmt = FMT_E; else if (TSTS (data)) /* SAV magic? */ fmt = FMT_S; fseek (fileref, 0, SEEK_SET); /* rewind */ } switch (fmt) { /* case fmt */ case FMT_R: /* RIM */ return load_rim (fileref); case FMT_S: /* SAV */ return load_sav (fileref); case FMT_E: /* EXE */ return load_exe (fileref); } printf ("Can't determine load file format\n"); return SCPE_FMT; } /* Symbol tables */ #define I_V_FL 39 /* inst class */ #define I_M_FL 03 /* class mask */ #define I_ITS 004000000000000 /* ITS flag */ #define I_AC 000000000000000 /* AC, address */ #define I_OP 010000000000000 /* address only */ #define I_IO 020000000000000 /* classic I/O */ #define I_V_AC 00 #define I_V_OP 01 #define I_V_IO 02 static const d10 masks[] = { 0777000000000, 0777740000000, 0700340000000, 0777777777777 }; static const char *opcode[] = { "XCTR", "XCTI", /* ITS only */ "IORDI", "IORDQ", "IORD", "IOWR", "IOWRI", "IOWRQ", "IORDBI", "IORDBQ", "IORDB", "IOWRB", "IOWRBI", "IOWRBQ", "CLRCSH", "RDPCST", "WRPCST", "SDBR1", "SDBR2", "SDBR3", "SDBR4", "SPM", "LDBR1", "LDBR2", "LDBR3", "LDBR4", "LPMR", "PORTAL", "JRSTF", "HALT", /* AC defines op */ "XJRSTF", "XJEN", "XPCW", "JEN", "SFM", "XJRST", "IBP", "JFOV", "JCRY1", "JCRY0", "JCRY", "JOV", "APRID", "WRAPR", "RDAPR", "WRPI", "RDPI", "RDUBR", "CLRPT", "WRUBR", "WREBR", "RDEBR", "RDSPB", "RDCSB", "RDPUR", "RDCSTM", "RDTIM", "RDINT", "RDHSB", "WRSPB", "WRCSB", "WRPUR", "WRCSTM", "WRTIM", "WRINT", "WRHSB", "LUUO01", "LUUO02", "LUUO03", "LUUO04", "LUUO05", "LUUO06", "LUUO07", "LUUO10", "LUUO11", "LUUO12", "LUUO13", "LUUO14", "LUUO15", "LUUO16", "LUUO17", "LUUO20", "LUUO21", "LUUO22", "LUUO23", "LUUO24", "LUUO25", "LUUO26", "LUUO27", "LUUO30", "LUUO31", "LUUO32", "LUUO33", "LUUO34", "LUUO35", "LUUO36", "LUUO37", "MUUO40", "MUUO41", "MUUO42", "MUUO43", "MUUO44", "MUUO45", "MUUO46", "MUUO47", "MUUO50", "MUUO51", "MUUO52", "MUUO53", "MUUO54", "MUUO55", "MUUO56", "MUUO57", "MUUO60", "MUUO61", "MUUO62", "MUUO63", "MUUO64", "MUUO65", "MUUO66", "MUUO67", "MUUO70", "MUUO71", "MUUO72", "MUUO73", "MUUO74", "MUUO75", "MUUO76", "MUUO77", "UJEN", "GFAD", "GFSB", "JSYS", "ADJSP", "GFMP", "GFDV ", "DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", "DMOVE", "DMOVN", "FIX", "EXTEND", "DMOVEM", "DMOVNM", "FIXR", "FLTR", "UFA", "DFN", "FSC", "ADJBP", "ILDB", "LDB", "IDPB", "DPB", "FAD", "FADL", "FADM", "FADB", "FADR", "FADRL", "FADRM", "FADRB", "FSB", "FSBL", "FSBM", "FSBB", "FSBR", "FSBRL", "FSBRM", "FSBRB", "FMP", "FMPL", "FMPM", "FMPB", "FMPR", "FMPRL", "FMPRM", "FMPRB", "FDV", "FDVL", "FDVM", "FDVB", "FDVR", "FDVRL", "FDVRM", "FDVRB", "MOVE", "MOVEI", "MOVEM", "MOVES", "MOVS", "MOVSI", "MOVSM", "MOVSS", "MOVN", "MOVNI", "MOVNM", "MOVNS", "MOVM", "MOVMI", "MOVMM", "MOVMS", "IMUL", "IMULI", "IMULM", "IMULB", "MUL", "MULI", "MULM", "MULB", "IDIV", "IDIVI", "IDIVM", "IDIVB", "DIV", "DIVI", "DIVM", "DIVB", "ASH", "ROT", "LSH", "JFFO", "ASHC", "ROTC", "LSHC", "CIRC", "EXCH", "BLT", "AOBJP", "AOBJN", "JRST", "JFCL", "XCT", "MAP", "PUSHJ", "PUSH", "POP", "POPJ", "JSR", "JSP", "JSA", "JRA", "ADD", "ADDI", "ADDM", "ADDB", "SUB", "SUBI", "SUBM", "SUBB", "CAI", "CAIL", "CAIE", "CAILE", "CAIA", "CAIGE", "CAIN", "CAIG", "CAM", "CAML", "CAME", "CAMLE", "CAMA", "CAMGE", "CAMN", "CAMG", "JUMP", "JUMPL", "JUMPE", "JUMPLE", "JUMPA", "JUMPGE", "JUMPN", "JUMPG", "SKIP", "SKIPL", "SKIPE", "SKIPLE", "SKIPA", "SKIPGE", "SKIPN", "SKIPG", "AOJ", "AOJL", "AOJE", "AOJLE", "AOJA", "AOJGE", "AOJN", "AOJG", "AOS", "AOSL", "AOSE", "AOSLE", "AOSA", "AOSGE", "AOSN", "AOSG", "SOJ", "SOJL", "SOJE", "SOJLE", "SOJA", "SOJGE", "SOJN", "SOJG", "SOS", "SOSL", "SOSE", "SOSLE", "SOSA", "SOSGE", "SOSN", "SOSG", "SETZ", "SETZI", "SETZM", "SETZB", "AND", "ANDI", "ANDM", "ANDB", "ANDCA", "ANDCAI", "ANDCAM", "ANDCAB", "SETM", "SETMI", "SETMM", "SETMB", "ANDCM", "ANDCMI", "ANDCMM", "ANDCMB", "SETA", "SETAI", "SETAM", "SETAB", "XOR", "XORI", "XORM", "XORB", "IOR", "IORI", "IORM", "IORB", "ANDCB", "ANDCBI", "ANDCBM", "ANDCBB", "EQV", "EQVI", "EQVM", "EQVB", "SETCA", "SETCAI", "SETCAM", "SETCAB", "ORCA", "ORCAI", "ORCAM", "ORCAB", "SETCM", "SETCMI", "SETCMM", "SETCMB", "ORCM", "ORCMI", "ORCMM", "ORCMB", "ORCB", "ORCBI", "ORCBM", "ORCBB", "SETO", "SETOI", "SETOM", "SETOB", "HLL", "HLLI", "HLLM", "HLLS", "HRL", "HRLI", "HRLM", "HRLS", "HLLZ", "HLLZI", "HLLZM", "HLLZS", "HRLZ", "HRLZI", "HRLZM", "HRLZS", "HLLO", "HLLOI", "HLLOM", "HLLOS", "HRLO", "HRLOI", "HRLOM", "HRLOS", "HLLE", "HLLEI", "HLLEM", "HLLES", "HRLE", "HRLEI", "HRLEM", "HRLES", "HRR", "HRRI", "HRRM", "HRRS", "HLR", "HLRI", "HLRM", "HLRS", "HRRZ", "HRRZI", "HRRZM", "HRRZS", "HLRZ", "HLRZI", "HLRZM", "HLRZS", "HRRO", "HRROI", "HRROM", "HRROS", "HLRO", "HLROI", "HLROM", "HLROS", "HRRE", "HRREI", "HRREM", "HRRES", "HLRE", "HLREI", "HLREM", "HLRES", "TRN", "TLN", "TRNE", "TLNE", "TRNA", "TLNA", "TRNN", "TLNN", "TDN", "TSN", "TDNE", "TSNE", "TDNA", "TSNA", "TDNN", "TSNN", "TRZ", "TLZ", "TRZE", "TLZE", "TRZA", "TLZA", "TRZN", "TLZN", "TDZ", "TSZ", "TDZE", "TSZE", "TDZA", "TSZA", "TDZN", "TSZN", "TRC", "TLC", "TRCE", "TLCE", "TRCA", "TLCA", "TRCN", "TLCN", "TDC", "TSC", "TDCE", "TSCE", "TDCA", "TSCA", "TDCN", "TSCN", "TRO", "TLO", "TROE", "TLOE", "TROA", "TLOA", "TRON", "TLON", "TDO", "TSO", "TDOE", "TSOE", "TDOA", "TSOA", "TDON", "TSON", "UMOVE", "UMOVEM", /* KS10 I/O */ "TIOE", "TION", "RDIO", "WRIO", "BSIO", "BCIO", "BLTBU", "BLTUB", "TIOEB", "TIONB", "RDIOB", "WRIOB", "BSIOB", "BCIOB", "BLKI", "DATAI", "BLKO", "DATAO", /* classic I/O */ "CONO", "CONI", "CONSZ", "CONSO", "CLEAR", "CLEARI", "CLEARM", "CLEARB", "OR", "ORI", "ORM", "ORB", "XMOVEI", "XHLLI", /* alternate ops */ "CMPSL", "CMPSE", "CMPSLE", /* extended ops */ "EDIT", "CMPSGE", "CMPSN", "CMPSG", "CVTDBO", "CVTDBT", "CVTBDO", "CVTBDT", "MOVSO", "MOVST", "MOVSLJ", "MOVSRJ", "XBLT", "GSNGL", "GDBLE", "GDFIX", "GFIX", "GDFIXR", "GFIXR", "DGFLTR", "GFLTR", "GFSC", NULL }; static const d10 opc_val[] = { 0102000000000+I_AC+I_ITS, 0103000000000+I_AC+I_ITS, 0710000000000+I_AC+I_ITS, 0711000000000+I_AC+I_ITS, 0712000000000+I_AC+I_ITS, 0713000000000+I_AC+I_ITS, 0714000000000+I_AC+I_ITS, 0715000000000+I_AC+I_ITS, 0720000000000+I_AC+I_ITS, 0721000000000+I_AC+I_ITS, 0722000000000+I_AC+I_ITS, 0723000000000+I_AC+I_ITS, 0724000000000+I_AC+I_ITS, 0725000000000+I_AC+I_ITS, 0701000000000+I_OP+I_ITS, 0701440000000+I_OP+I_ITS, 0701540000000+I_OP+I_ITS, 0702000000000+I_OP+I_ITS, 0702040000000+I_OP+I_ITS, 0702100000000+I_OP+I_ITS, 0702140000000+I_OP+I_ITS, 0702340000000+I_OP+I_ITS, 0702400000000+I_OP+I_ITS, 0702440000000+I_OP+I_ITS, 0702500000000+I_OP+I_ITS, 0702540000000+I_OP+I_ITS, 0702740000000+I_OP+I_ITS, 0254040000000+I_OP, 0254100000000+I_OP, 0254200000000+I_OP, 0254240000000+I_OP, 0254300000000+I_OP, 0254340000000+I_OP, 0254500000000+I_OP, 0254600000000+I_OP, 0254640000000+I_OP, 0133000000000+I_OP, 0255040000000+I_OP, 0255100000000+I_OP, 0255200000000+I_OP, 0255300000000+I_OP, 0255400000000+I_OP, 0700000000000+I_OP, 0700200000000+I_OP, 0700240000000+I_OP, 0700600000000+I_OP, 0700640000000+I_OP, 0701040000000+I_OP, 0701100000000+I_OP, 0701140000000+I_OP, 0701200000000+I_OP, 0701240000000+I_OP, 0702000000000+I_OP, 0702040000000+I_OP, 0702100000000+I_OP, 0702140000000+I_OP, 0702200000000+I_OP, 0702240000000+I_OP, 0702300000000+I_OP, 0702400000000+I_OP, 0702440000000+I_OP, 0702500000000+I_OP, 0702540000000+I_OP, 0702600000000+I_OP, 0702640000000+I_OP, 0702700000000+I_OP, 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, 0030000000000+I_AC, 0031000000000+I_AC, 0032000000000+I_AC, 0033000000000+I_AC, 0034000000000+I_AC, 0035000000000+I_AC, 0036000000000+I_AC, 0037000000000+I_AC, 0040000000000+I_AC, 0041000000000+I_AC, 0042000000000+I_AC, 0043000000000+I_AC, 0044000000000+I_AC, 0045000000000+I_AC, 0046000000000+I_AC, 0047000000000+I_AC, 0050000000000+I_AC, 0051000000000+I_AC, 0052000000000+I_AC, 0053000000000+I_AC, 0054000000000+I_AC, 0055000000000+I_AC, 0056000000000+I_AC, 0057000000000+I_AC, 0060000000000+I_AC, 0061000000000+I_AC, 0062000000000+I_AC, 0063000000000+I_AC, 0064000000000+I_AC, 0065000000000+I_AC, 0066000000000+I_AC, 0067000000000+I_AC, 0070000000000+I_AC, 0071000000000+I_AC, 0072000000000+I_AC, 0073000000000+I_AC, 0074000000000+I_AC, 0075000000000+I_AC, 0076000000000+I_AC, 0077000000000+I_AC, 0100000000000+I_AC, 0102000000000+I_AC, 0103000000000+I_AC, 0104000000000+I_AC, 0105000000000+I_AC, 0106000000000+I_AC, 0107000000000+I_AC, 0110000000000+I_AC, 0111000000000+I_AC, 0112000000000+I_AC, 0113000000000+I_AC, 0114000000000+I_AC, 0115000000000+I_AC, 0116000000000+I_AC, 0117000000000+I_AC, 0120000000000+I_AC, 0121000000000+I_AC, 0122000000000+I_AC, 0123000000000+I_AC, 0124000000000+I_AC, 0125000000000+I_AC, 0126000000000+I_AC, 0127000000000+I_AC, 0130000000000+I_AC, 0131000000000+I_AC, 0132000000000+I_AC, 0133000000000+I_AC, 0134000000000+I_AC, 0135000000000+I_AC, 0136000000000+I_AC, 0137000000000+I_AC, 0140000000000+I_AC, 0141000000000+I_AC, 0142000000000+I_AC, 0143000000000+I_AC, 0144000000000+I_AC, 0145000000000+I_AC, 0146000000000+I_AC, 0147000000000+I_AC, 0150000000000+I_AC, 0151000000000+I_AC, 0152000000000+I_AC, 0153000000000+I_AC, 0154000000000+I_AC, 0155000000000+I_AC, 0156000000000+I_AC, 0157000000000+I_AC, 0160000000000+I_AC, 0161000000000+I_AC, 0162000000000+I_AC, 0163000000000+I_AC, 0164000000000+I_AC, 0165000000000+I_AC, 0166000000000+I_AC, 0167000000000+I_AC, 0170000000000+I_AC, 0171000000000+I_AC, 0172000000000+I_AC, 0173000000000+I_AC, 0174000000000+I_AC, 0175000000000+I_AC, 0176000000000+I_AC, 0177000000000+I_AC, 0200000000000+I_AC, 0201000000000+I_AC, 0202000000000+I_AC, 0203000000000+I_AC, 0204000000000+I_AC, 0205000000000+I_AC, 0206000000000+I_AC, 0207000000000+I_AC, 0210000000000+I_AC, 0211000000000+I_AC, 0212000000000+I_AC, 0213000000000+I_AC, 0214000000000+I_AC, 0215000000000+I_AC, 0216000000000+I_AC, 0217000000000+I_AC, 0220000000000+I_AC, 0221000000000+I_AC, 0222000000000+I_AC, 0223000000000+I_AC, 0224000000000+I_AC, 0225000000000+I_AC, 0226000000000+I_AC, 0227000000000+I_AC, 0230000000000+I_AC, 0231000000000+I_AC, 0232000000000+I_AC, 0233000000000+I_AC, 0234000000000+I_AC, 0235000000000+I_AC, 0236000000000+I_AC, 0237000000000+I_AC, 0240000000000+I_AC, 0241000000000+I_AC, 0242000000000+I_AC, 0243000000000+I_AC, 0244000000000+I_AC, 0245000000000+I_AC, 0246000000000+I_AC, 0247000000000+I_AC+I_ITS, 0250000000000+I_AC, 0251000000000+I_AC, 0252000000000+I_AC, 0253000000000+I_AC, 0254000000000+I_AC, 0255000000000+I_AC, 0256000000000+I_AC, 0257000000000+I_AC, 0260000000000+I_AC, 0261000000000+I_AC, 0262000000000+I_AC, 0263000000000+I_AC, 0264000000000+I_AC, 0265000000000+I_AC, 0266000000000+I_AC, 0267000000000+I_AC, 0270000000000+I_AC, 0271000000000+I_AC, 0272000000000+I_AC, 0273000000000+I_AC, 0274000000000+I_AC, 0275000000000+I_AC, 0276000000000+I_AC, 0277000000000+I_AC, 0300000000000+I_AC, 0301000000000+I_AC, 0302000000000+I_AC, 0303000000000+I_AC, 0304000000000+I_AC, 0305000000000+I_AC, 0306000000000+I_AC, 0307000000000+I_AC, 0310000000000+I_AC, 0311000000000+I_AC, 0312000000000+I_AC, 0313000000000+I_AC, 0314000000000+I_AC, 0315000000000+I_AC, 0316000000000+I_AC, 0317000000000+I_AC, 0320000000000+I_AC, 0321000000000+I_AC, 0322000000000+I_AC, 0323000000000+I_AC, 0324000000000+I_AC, 0325000000000+I_AC, 0326000000000+I_AC, 0327000000000+I_AC, 0330000000000+I_AC, 0331000000000+I_AC, 0332000000000+I_AC, 0333000000000+I_AC, 0334000000000+I_AC, 0335000000000+I_AC, 0336000000000+I_AC, 0337000000000+I_AC, 0340000000000+I_AC, 0341000000000+I_AC, 0342000000000+I_AC, 0343000000000+I_AC, 0344000000000+I_AC, 0345000000000+I_AC, 0346000000000+I_AC, 0347000000000+I_AC, 0350000000000+I_AC, 0351000000000+I_AC, 0352000000000+I_AC, 0353000000000+I_AC, 0354000000000+I_AC, 0355000000000+I_AC, 0356000000000+I_AC, 0357000000000+I_AC, 0360000000000+I_AC, 0361000000000+I_AC, 0362000000000+I_AC, 0363000000000+I_AC, 0364000000000+I_AC, 0365000000000+I_AC, 0366000000000+I_AC, 0367000000000+I_AC, 0370000000000+I_AC, 0371000000000+I_AC, 0372000000000+I_AC, 0373000000000+I_AC, 0374000000000+I_AC, 0375000000000+I_AC, 0376000000000+I_AC, 0377000000000+I_AC, 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, 0404000000000+I_AC, 0405000000000+I_AC, 0406000000000+I_AC, 0407000000000+I_AC, 0410000000000+I_AC, 0411000000000+I_AC, 0412000000000+I_AC, 0413000000000+I_AC, 0414000000000+I_AC, 0415000000000+I_AC, 0416000000000+I_AC, 0417000000000+I_AC, 0420000000000+I_AC, 0421000000000+I_AC, 0422000000000+I_AC, 0423000000000+I_AC, 0424000000000+I_AC, 0425000000000+I_AC, 0426000000000+I_AC, 0427000000000+I_AC, 0430000000000+I_AC, 0431000000000+I_AC, 0432000000000+I_AC, 0433000000000+I_AC, 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, 0440000000000+I_AC, 0441000000000+I_AC, 0442000000000+I_AC, 0443000000000+I_AC, 0444000000000+I_AC, 0445000000000+I_AC, 0446000000000+I_AC, 0447000000000+I_AC, 0450000000000+I_AC, 0451000000000+I_AC, 0452000000000+I_AC, 0453000000000+I_AC, 0454000000000+I_AC, 0455000000000+I_AC, 0456000000000+I_AC, 0457000000000+I_AC, 0460000000000+I_AC, 0461000000000+I_AC, 0462000000000+I_AC, 0463000000000+I_AC, 0464000000000+I_AC, 0465000000000+I_AC, 0466000000000+I_AC, 0467000000000+I_AC, 0470000000000+I_AC, 0471000000000+I_AC, 0472000000000+I_AC, 0473000000000+I_AC, 0474000000000+I_AC, 0475000000000+I_AC, 0476000000000+I_AC, 0477000000000+I_AC, 0500000000000+I_AC, 0501000000000+I_AC, 0502000000000+I_AC, 0503000000000+I_AC, 0504000000000+I_AC, 0505000000000+I_AC, 0506000000000+I_AC, 0507000000000+I_AC, 0510000000000+I_AC, 0511000000000+I_AC, 0512000000000+I_AC, 0513000000000+I_AC, 0514000000000+I_AC, 0515000000000+I_AC, 0516000000000+I_AC, 0517000000000+I_AC, 0520000000000+I_AC, 0521000000000+I_AC, 0522000000000+I_AC, 0523000000000+I_AC, 0524000000000+I_AC, 0525000000000+I_AC, 0526000000000+I_AC, 0527000000000+I_AC, 0530000000000+I_AC, 0531000000000+I_AC, 0532000000000+I_AC, 0533000000000+I_AC, 0534000000000+I_AC, 0535000000000+I_AC, 0536000000000+I_AC, 0537000000000+I_AC, 0540000000000+I_AC, 0541000000000+I_AC, 0542000000000+I_AC, 0543000000000+I_AC, 0544000000000+I_AC, 0545000000000+I_AC, 0546000000000+I_AC, 0547000000000+I_AC, 0550000000000+I_AC, 0551000000000+I_AC, 0552000000000+I_AC, 0553000000000+I_AC, 0554000000000+I_AC, 0555000000000+I_AC, 0556000000000+I_AC, 0557000000000+I_AC, 0560000000000+I_AC, 0561000000000+I_AC, 0562000000000+I_AC, 0563000000000+I_AC, 0564000000000+I_AC, 0565000000000+I_AC, 0566000000000+I_AC, 0567000000000+I_AC, 0570000000000+I_AC, 0571000000000+I_AC, 0572000000000+I_AC, 0573000000000+I_AC, 0574000000000+I_AC, 0575000000000+I_AC, 0576000000000+I_AC, 0577000000000+I_AC, 0600000000000+I_AC, 0601000000000+I_AC, 0602000000000+I_AC, 0603000000000+I_AC, 0604000000000+I_AC, 0605000000000+I_AC, 0606000000000+I_AC, 0607000000000+I_AC, 0610000000000+I_AC, 0611000000000+I_AC, 0612000000000+I_AC, 0613000000000+I_AC, 0614000000000+I_AC, 0615000000000+I_AC, 0616000000000+I_AC, 0617000000000+I_AC, 0620000000000+I_AC, 0621000000000+I_AC, 0622000000000+I_AC, 0623000000000+I_AC, 0624000000000+I_AC, 0625000000000+I_AC, 0626000000000+I_AC, 0627000000000+I_AC, 0630000000000+I_AC, 0631000000000+I_AC, 0632000000000+I_AC, 0633000000000+I_AC, 0634000000000+I_AC, 0635000000000+I_AC, 0636000000000+I_AC, 0637000000000+I_AC, 0640000000000+I_AC, 0641000000000+I_AC, 0642000000000+I_AC, 0643000000000+I_AC, 0644000000000+I_AC, 0645000000000+I_AC, 0646000000000+I_AC, 0647000000000+I_AC, 0650000000000+I_AC, 0651000000000+I_AC, 0652000000000+I_AC, 0653000000000+I_AC, 0654000000000+I_AC, 0655000000000+I_AC, 0656000000000+I_AC, 0657000000000+I_AC, 0660000000000+I_AC, 0661000000000+I_AC, 0662000000000+I_AC, 0663000000000+I_AC, 0664000000000+I_AC, 0665000000000+I_AC, 0666000000000+I_AC, 0667000000000+I_AC, 0670000000000+I_AC, 0671000000000+I_AC, 0672000000000+I_AC, 0673000000000+I_AC, 0674000000000+I_AC, 0675000000000+I_AC, 0676000000000+I_AC, 0677000000000+I_AC, 0704000000000+I_AC, 0705000000000+I_AC, 0710000000000+I_AC, 0711000000000+I_AC, 0712000000000+I_AC, 0713000000000+I_AC, 0714000000000+I_AC, 0715000000000+I_AC, 0716000000000+I_AC, 0717000000000+I_AC, 0720000000000+I_AC, 0721000000000+I_AC, 0722000000000+I_AC, 0723000000000+I_AC, 0724000000000+I_AC, 0725000000000+I_AC, 0700000000000+I_IO, 0700040000000+I_IO, 0700100000000+I_IO, 0700140000000+I_IO, 0700200000000+I_IO, 0700240000000+I_IO, 0700300000000+I_IO, 0700340000000+I_IO, 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, 0415000000000+I_AC, 0501000000000+I_AC, 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, 0030000000000+I_AC, 0031000000000+I_AC, -1 }; #define NUMDEV 6 static const char *devnam[NUMDEV] = { "APR", "PI", "PAG", "CCA", "TIM", "MTR" }; /* 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) ((x) + 040) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 i, j, c, cflag, ac, xr, y, dev; d10 inst; inst = val[0]; cflag = (uptr == NULL) || (uptr == &cpu_unit); if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC ((int32) (inst & 0177))); return SCPE_OK; } if (sw & SWMASK ('C')) { /* character? */ for (i = 30; i >= 0; i = i - 6) { c = (int32) ((inst >> i) & 077); fprintf (of, "%c", SIXTOASC (c)); } return SCPE_OK; } if (sw & SWMASK ('P')) { /* packed? */ for (i = 29; i >= 0; i = i - 7) { c = (int32) ((inst >> i) & 0177); fprintf (of, FMTASC (c)); } return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ ac = GET_AC (inst); xr = GET_XR (inst); y = GET_ADDR (inst); dev = GET_DEV (inst); for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ if (((opc_val[i] & DMASK) == (inst & masks[j])) && /* match? */ (((opc_val[i] & I_ITS) == 0) || Q_ITS)) { fprintf (of, "%s ", opcode[i]); /* opcode */ switch (j) { /* case on class */ case I_V_AC: /* AC + address */ fprintf (of, "%-o,", ac); /* print AC, fall thru */ case I_V_OP: /* address only */ if (inst & INST_IND) fprintf (of, "@"); if (xr) fprintf (of, "%-o(%-o)", y, xr); else fprintf (of, "%-o", y); break; case I_V_IO: /* I/O */ if (dev < NUMDEV) fprintf (of, "%s,", devnam[dev]); else fprintf (of, "%-o,", dev); if (inst & INST_IND) fprintf (of, "@"); if (xr) fprintf (of, "%-o(%-o)", y, xr); else fprintf (of, "%-o", y); break; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* Get operand, including indirect and index Inputs: *cptr = pointer to input string *status = pointer to error status Outputs: val = output value */ t_value get_opnd (char *cptr, t_stat *status) { int32 sign = 0; t_value val, xr = 0, ind = 0; char *tptr; *status = SCPE_ARG; /* assume fail */ if (*cptr == '@') { ind = INST_IND; cptr++; } if (*cptr == '+') cptr++; else if (*cptr == '-') { sign = 1; cptr++; } val = strtotv (cptr, &tptr, 8); if (val > 0777777) return 0; if (sign) val = (~val + 1) & 0777777; cptr = tptr; if (*cptr == '(') { cptr++; xr = strtotv (cptr, &tptr, 8); if ((cptr == tptr) || (*tptr != ')') || (xr > AC_NUM) || (xr == 0)) return 0; cptr = ++tptr; } if (*cptr == 0) *status = SCPE_OK; return (ind | (xr << 18) | val); } /* 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) { int32 cflag, i, j; t_value ac, dev; t_stat r; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; for (i = 0; i < 6; i++) { if (cptr[i] == 0) { for (j = i + 1; j <= 6; j++) cptr[j] = 0; break; } } if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; for (i = 0; i < 6; i++) { val[0] = (val[0] << 6); if (cptr[i]) val[0] = val[0] | ((t_value) ((cptr[i] + 040) & 077)); } return SCPE_OK; } if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; for (i = 0; i < 5; i++) val[0] = (val[0] << 7) | ((t_value) cptr[i]); val[0] = val[0] << 1; return SCPE_OK; } /* Instruction parse */ 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 = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ switch (j) { /* case on class */ case I_V_AC: /* AC + operand */ if (strchr (cptr, ',')) { /* AC specified? */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if (gbuf[0]) { /* can be omitted */ ac = get_uint (gbuf, 8, AC_NUM - 1, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (ac << INST_V_AC); } } /* fall through */ case I_V_OP: /* operand */ cptr = get_glyph (cptr, gbuf, 0); val[0] = val[0] | get_opnd (gbuf, &r); if (r != SCPE_OK) return SCPE_ARG; break; case I_V_IO: /* I/O */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ for (dev = 0; (dev < NUMDEV) && (strcmp (devnam[dev], gbuf) != 0); dev++); if (dev >= NUMDEV) { dev = get_uint (gbuf, 8, INST_M_DEV, &r); if (r != SCPE_OK) return SCPE_ARG; } val[0] = val[0] | (dev << INST_V_DEV); cptr = get_glyph (cptr, gbuf, 0); val[0] = val[0] | get_opnd (gbuf, &r); if (r != SCPE_OK) return SCPE_ARG; break; } /* end case */ if (*cptr != 0) /* junk at end? */ return SCPE_ARG; return SCPE_OK; } simh-3.8.1/PDP10/pdp10_bug_history.txt0000644000175000017500000000213311017062140015517 0ustar vlmvlmBugs Found and Fixed During Simulator Debug 1. pushj cleared T2 after setting it 2. if timer autoadjust is enabled, timer diagnostic may fail, depending on host CPU speed 3. DFAD/DFSB should use FP_ONES instead of ONES 4. TLB physical address max = 1MW, tested in diagnostic 5. DPB does read/write, not read-modify/write 6. Fetch error takes priority over traps, due to prefetching of next instruction 7. HSB is 36b, was 32b 8. CPU and PAG devices had mismatched types 9. non-zero sections in Tops-20 paging section indirect may cause non-existent memory error, due to microcode "error" 10. PXCT test for user mode was backward 11. Timer interrupts were not implemented in Tops-20 indirect chains 12. epta/upta hit known bug in VC++ implementation of 64b data types 13. final W calculation in Tops-20 paging was incorrect 14. Timer representation lost sub msec values 15. UBA initialization reset the UBA itself 16. RHCS1: writing IE cannot trigger an interrupt 17. Tape bootstrap was set to 800bpi instead of 1600bpi 18. FIXR off by 1 in testing for lower limit to process simh-3.8.1/GRI/0000755000175000017500000000000011112024272011252 5ustar vlmvlmsimh-3.8.1/GRI/gri_sys.c0000644000175000017500000006445411112024272013112 0ustar vlmvlm/* gri_sys.c: GRI-909 simulator interface Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 14-Jan-08 RMS Added GRI-99 support 18-Oct-02 RMS Fixed bug in symbolic decode (found by Hans Pufal) */ #include "gri_defs.h" #include extern DEVICE cpu_dev; extern UNIT cpu_unit; extern DEVICE tti_dev, tto_dev; extern DEVICE hsr_dev, hsp_dev; extern DEVICE rtc_dev; extern REG cpu_reg[]; extern uint16 M[]; extern int32 sim_switches; void fprint_addr (FILE *of, uint32 val, uint32 mod, uint32 dst); /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "GRI-909"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 2; DEVICE *sim_devices[] = { &cpu_dev, &tti_dev, &tto_dev, &hsr_dev, &hsp_dev, &rtc_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unimplemented unit", "HALT instruction", "Breakpoint", "Invalid interrupt request" }; /* Binary loader Bootstrap loader format consists of blocks separated by zeroes. Each word in the block has three frames: a control frame (ignored) and two data frames. The user must specify the load address. Switch -c means continue and load all blocks until end of tape. */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 c; uint32 org; t_stat r; char gbuf[CBUFSIZE]; if (*cptr != 0) { /* more input? */ cptr = get_glyph (cptr, gbuf, 0); /* get origin */ org = get_uint (gbuf, 8, AMASK, &r); if (r != SCPE_OK) return r; if (*cptr != 0) /* no more */ return SCPE_ARG; } else org = 0200; /* default 200 */ for (;;) { /* until EOF */ while ((c = getc (fileref)) == 0) ; /* skip starting 0's */ if (c == EOF) /* EOF? done */ break; for ( ; c != 0; ) { /* loop until ctl = 0 */ /* ign ctrl frame */ if ((c = getc (fileref)) == EOF) /* get high byte */ return SCPE_FMT; /* EOF is error */ if (!MEM_ADDR_OK (org)) return SCPE_NXM; M[org] = ((c & 0377) << 8); /* store high */ if ((c = getc (fileref)) == EOF) /* get low byte */ return SCPE_FMT; /* EOF is error */ M[org] = M[org] | (c & 0377); /* store low */ org = org + 1; /* incr origin */ if ((c = getc (fileref)) == EOF) /* get ctrl frame */ return SCPE_OK; /* EOF is ok */ } /* end block for */ if (!(sim_switches & SWMASK ('C'))) return SCPE_OK; } /* end tape for */ return SCPE_OK; } /* Symbol tables */ #define F_V_FL 16 /* class flag */ #define F_M_FL 017 #define F_V_FO 000 /* function out */ #define F_V_FOI 001 /* FO, impl reg */ #define F_V_SF 002 /* skip function */ #define F_V_SFI 003 /* SF, impl reg */ #define F_V_RR 004 /* reg reg */ #define F_V_ZR 005 /* zero reg */ #define F_V_RS 006 /* reg self */ #define F_V_JC 010 /* jump cond */ #define F_V_JU 011 /* jump uncond */ #define F_V_RM 012 /* reg mem */ #define F_V_ZM 013 /* zero mem */ #define F_V_MR 014 /* mem reg */ #define F_V_MS 015 /* mem self */ #define F_2WD 010 /* 2 words */ #define F_FO (F_V_FO << F_V_FL) #define F_FOI (F_V_FOI << F_V_FL) #define F_SF (F_V_SF << F_V_FL) #define F_SFI (F_V_SFI << F_V_FL) #define F_RR (F_V_RR << F_V_FL) #define F_ZR (F_V_ZR << F_V_FL) #define F_RS (F_V_RS << F_V_FL) #define F_JC (F_V_JC << F_V_FL) #define F_JU (F_V_JU << F_V_FL) #define F_RM (F_V_RM << F_V_FL) #define F_ZM (F_V_ZM << F_V_FL) #define F_MR (F_V_MR << F_V_FL) #define F_MS (F_V_MS << F_V_FL) struct fnc_op { uint32 inst; /* instr prot */ uint32 imask; /* instr mask */ uint32 oper; /* operator */ uint32 omask; /* oper mask */ }; static const int32 masks[] = { 0176000, 0176077, 0000077, 0176077, 0000300, 0176300, 0000300, 0177777, 0000077, 0177777, 0000377, 0176377, 0176300, 0176377 }; /* Instruction mnemonics Order is critical, as some instructions are more precise versions of others. For example, JU must precede JC, otherwise, JU will be decoded as JC 0,ETZ,dst. There are some ambiguities, eg, what is 02-xxxx-06? Priority is as follows: FO (02-xxxx-rr) SF (rr-xxxx-02) MR (06-xxxx-rr) RM (rr-xxxx-06) JC (rr-xxxx-03) RR */ static const char *opcode[] = { "FOM", "FOA", "FOI", "FO", /* FOx before FO */ "SFM", "SFA", "SFI", "SF", /* SFx before SF */ "ZM", "ZMD", "ZMI", "ZMID", /* ZM before RM */ "MS", "MSD", "MSI", "MSID", "RM", "RMD", "RMI", "RMID", "MR", "MRD", "MRI", "MRID", "JO", "JOD", "JN", "JND", /* JU before JC */ "JU", "JUD", "JC", "JCD", "ZR", "ZRC", "RR", "RRC", /* ZR before RR */ "RS", "RSC", NULL }; static const uint32 opc_val[] = { 0004000+F_FOI, 0004013+F_FOI, 0004004+F_FOI, 0004000+F_FO, 0000002+F_SFI, 0026002+F_SFI, 0010002+F_SFI, 0000002+F_SF, 0000006+F_ZM, 0000106+F_ZM, 0000206+F_ZM, 0000306+F_ZM, 0014006+F_MS, 0014106+F_MS, 0014206+F_MS, 0014306+F_MS, 0000006+F_RM, 0000106+F_RM, 0000206+F_RM, 0000306+F_RM, 0014000+F_MR, 0014100+F_MR, 0014200+F_MR, 0014300+F_MR, 0037003+F_JU, 0037103+F_JU, 0037203+F_JU, 0037303+F_JU, 0000403+F_JU, 0000503+F_JU, 0000003+F_JC, 0000103+F_JC, 0000000+F_ZR, 0000200+F_ZR, 0000000+F_RR, 0000200+F_RR, 0000000+F_RS, 0000200+F_RS }; /* Unit mnemonics. All 64 units are decoded, most just to octal integers */ static const char *unsrc[64] = { "0", "IR", "2", "TRP", "ISR", "MA", "MB", "SC", /* 00 - 07 */ "SWR", "AX", "AY", "AO", "14", "15", "16", "MSR", /* 10 - 17 */ "20", "21", "XR", "ATRP", "BSW", "BPK", "BCPA", "BCPB",/* 20 - 27 */ "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37", /* 30 - 37 */ "40", "41", "42", "43", "44", "45", "46", "47", "50", "51", "52", "53", "54", "CDR", "56", "CADR", "60", "61", "62", "63", "64", "65", "DWC", "DCA", "DISK", "LPR", "72", "73", "CAS", "RTC", "HSR", "TTI" /* 70 - 77 */ }; static const char *undst[64] = { "0", "IR", "2", "TRP", "ISR", "5", "MB", "SC", /* 00 - 07 */ "SWR", "AX", "AY", "13", "EAO", "15", "16", "MSR", /* 10 - 17 */ "20", "21", "XR", "ATRP", "BSW", "BPK", "BCPA", "BCPB",/* 20 - 27 */ "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37", /* 30 - 37 */ "40", "41", "42", "43", "44", "45", "46", "47", "50", "51", "52", "53", "54", "CDR", "56", "CADR", "60", "61", "62", "63", "64", "65", "DWC", "DCA", "DISK", "LPR", "72", "73", "CAS", "RTC", "HSP", "TTO" /* 70 - 77 */ }; /* Operators */ static const char *opname[4] = { NULL, "P1", "L1", "R1" }; /* Conditions */ static const char *cdname[8] = { "NEVER", "ALWAYS", "ETZ", "NEZ", "LTZ", "GEZ", "LEZ", "GTZ" }; /* Function out/sense function */ static const char *fname[] = { "NOT", /* any SF */ "POK", "LNK", "BOV", /* SFM */ "SOV", "AOV", /* SFA */ "IRDY", "ORDY", /* any SF */ "CLL", "STL", "CML", "HLT", /* FOM */ "ICF", "ICO", /* FOI */ "ADD", "AND", "XOR", "OR", /* FOA */ "INP", "IRDY", "ORDY", "STRT", /* any FO */ NULL }; static const struct fnc_op fop[] = { { 0000002, 0000077, 001, 001 }, /* NOT */ { 0000002, 0176077, 010, 010 }, /* POK */ { 0000002, 0176077, 004, 004 }, /* LNK */ { 0000002, 0176077, 002, 002 }, /* BOV */ { 0026002, 0176077, 004, 004 }, /* SOV */ { 0026002, 0176077, 002, 002 }, /* AOV */ { 0000002, 0000077, 010, 010 }, /* IRDY */ { 0000002, 0000077, 002, 002 }, /* ORDY */ { 0004000, 0176077, 001, 003 }, /* CLL */ { 0004000, 0176077, 002, 003 }, /* STL */ { 0004000, 0176077, 003, 003 }, /* CML */ { 0004000, 0176077, 004, 004 }, /* HLT */ { 0004004, 0176077, 001, 001 }, /* ICF */ { 0004004, 0176077, 002, 002 }, /* ICO */ { 0004013, 0176077, 000, 014 }, /* ADD */ { 0004013, 0176077, 004, 014 }, /* AND */ { 0004013, 0176077, 010, 014 }, /* XOR */ { 0004013, 0176077, 014, 014 }, /* OR */ { 0004000, 0176000, 011, 011 }, /* INP */ { 0004000, 0176000, 010, 010 }, /* IRDY */ { 0004000, 0176000, 002, 002 }, /* ORDY */ { 0004000, 0176000, 001, 001 } /* STRT */ }; /* Print opcode field for FO, SF */ void fprint_op (FILE *of, uint32 inst, uint32 op) { int32 i, nfirst; for (i = nfirst = 0; fname[i] != NULL; i++) { if (((inst & fop[i].imask) == fop[i].inst) && ((op & fop[i].omask) == fop[i].oper)) { op = op & ~fop[i].omask; if (nfirst) fputc (' ', of); nfirst = 1; fprintf (of, "%s", fname[i]); } } if (op) fprintf (of, " %o", op); return; } /* Print address field with potential indexing */ void fprint_addr (FILE *of, uint32 val, uint32 mode, uint32 dst) { if ((val & INDEX) && ((dst == U_SC) || (mode != MEM_IMM))) fprintf (of, "#%o", val & AMASK); else fprintf (of, "%o", val); return; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to data *uptr = pointer to unit sw = switches Outputs: return = status code */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 i, j; uint32 inst, src, dst, op, bop; inst = val[0]; if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (sw & SWMASK ('C')) { /* characters? */ fprintf (of, FMTASC ((inst >> 8) & 0177)); fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ inst = val[0]; src = I_GETSRC (inst); /* get fields */ op = I_GETOP (inst); dst = I_GETDST (inst); bop = op >> 2; /* bus op */ for (i = 0; opcode[i] != NULL; i++) { /* loop thru ops */ j = (opc_val[i] >> F_V_FL) & F_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case F_V_FO: /* func out */ fprintf (of, "%s ", opcode[i]); fprint_op (of, inst, op); fprintf (of, ",%s", undst[dst]); break; case F_V_FOI: /* func out impl */ fprintf (of, "%s ", opcode[i]); fprint_op (of, inst, op); break; case F_V_SF: /* skip func */ fprintf (of, "%s %s,", opcode[i], unsrc[src]); fprint_op (of, inst, op); break; case F_V_SFI: /* skip func impl */ fprintf (of, "%s ", opcode[i]); fprint_op (of, inst, op); break; case F_V_RR: /* reg reg */ if (strcmp (unsrc[src], undst[dst]) == 0) { if (bop) fprintf (of, "%s %s,%s", opcode[i + 2], unsrc[src], opname[bop]); else fprintf (of, "%s %s", opcode[i + 2], unsrc[src]); } else { if (bop) fprintf (of, "%s %s,%s,%s", opcode[i], unsrc[src], opname[bop], undst[dst]); else fprintf (of, "%s %s,%s", opcode[i], unsrc[src], undst[dst]); } break; case F_V_ZR: /* zero reg */ if (bop) fprintf (of, "%s %s,%s", opcode[i], opname[bop], undst[dst]); else fprintf (of, "%s %s", opcode[i], undst[dst]); break; case F_V_JC: /* jump cond */ fprintf (of, "%s %s,%s,", opcode[i], unsrc[src], cdname[op >> 1]); fprint_addr (of, val[1], 0, U_SC); break; case F_V_JU: /* jump uncond */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, val[1], 0, U_SC); break; case F_V_RM: /* reg mem */ if (bop) fprintf (of, "%s %s,%s,", opcode[i], unsrc[src], opname[bop]); else fprintf (of, "%s %s,", opcode[i], unsrc[src]); fprint_addr (of, val[1], op & MEM_MOD, dst); break; case F_V_ZM: /* zero mem */ if (bop) fprintf (of, "%s %s,", opcode[i], opname[bop]); else fprintf (of, "%s ", opcode[i]); fprint_addr (of, val[1], op & MEM_MOD, dst); break; case F_V_MR: /* mem reg */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, val[1], op & MEM_MOD, dst); if (bop) fprintf (of, ",%s,%s", opname[bop], undst[dst]); else fprintf (of, ",%s", undst[dst]); break; case F_V_MS: /* mem self */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, val[1], op & MEM_MOD, dst); if (bop) fprintf (of, ",%s", opname[bop]); break; } /* end case */ return (j >= F_2WD)? -1: SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* Field parse routines get_fnc get function field get_ma get memory address get_sd get source or dest get_op get optional bus operator */ char *get_fnc (char *cptr, t_value *val) { char gbuf[CBUFSIZE]; int32 i; t_value d; t_stat r; uint32 inst = val[0]; uint32 fncv = 0, fncm = 0; while (*cptr) { cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ d = get_uint (gbuf, 8, 017, &r); /* octal? */ if (r == SCPE_OK) { /* ok? */ if (d & fncm) /* already filled? */ return NULL; fncv = fncv | d; /* save */ fncm = fncm | d; /* field filled */ } else { /* symbol? */ for (i = 0; fname[i] != NULL; i++) { /* search table */ if ((strcmp (gbuf, fname[i]) == 0) && /* match for inst? */ ((inst & fop[i].imask) == fop[i].inst)) { if (fop[i].oper & fncm) /* already filled? */ return NULL; fncm = fncm | fop[i].omask; fncv = fncv | fop[i].oper; break; } } if (fname[i] == NULL) return NULL; } /* end else */ } /* end while */ val[0] = val[0] | (fncv << I_V_OP); /* store fnc */ return cptr; } char *get_ma (char *cptr, t_value *val, char term) { char gbuf[CBUFSIZE]; t_value d; t_stat r; cptr = get_glyph (cptr, gbuf, term); /* get glyph */ if (gbuf[0] == '#') /* indexed? */ d = get_uint (gbuf + 1, 8, AMASK, &r) | INDEX; /* [0, 77777] */ else d = get_uint (gbuf, 8, DMASK, &r); /* [0,177777] */ if (r != SCPE_OK) return NULL; val[1] = d; /* second wd */ return cptr; } char *get_sd (char *cptr, t_value *val, char term, t_bool src) { char gbuf[CBUFSIZE]; int32 d; t_stat r; cptr = get_glyph (cptr, gbuf, term); /* get glyph */ for (d = 0; d < 64; d++) { /* symbol match? */ if ((strcmp (gbuf, unsrc[d]) == 0) || (strcmp (gbuf, undst[d]) == 0)) break; } if (d >= 64) { /* no, [0,63]? */ d = get_uint (gbuf, 8, 077, &r); if (r != SCPE_OK) return NULL; } val[0] = val[0] | (d << (src? I_V_SRC: I_V_DST)); /* or to inst */ return cptr; } char *get_op (char *cptr, t_value *val, char term) { char gbuf[CBUFSIZE], *tptr; int32 i; tptr = get_glyph (cptr, gbuf, term); /* get glyph */ for (i = 1; i < 4; i++) { /* symbol match? */ if (strcmp (gbuf, opname[i]) == 0) { val[0] = val[0] | (i << (I_V_OP + 2)); /* or to inst */ return tptr; } } return cptr; /* original ptr */ } /* 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) { int32 i, j, k; char *tptr, gbuf[CBUFSIZE]; while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0] & 0177; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* char string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) cptr[0] & 0177) << 8) | ((t_value) cptr[1] & 0177); return SCPE_OK; } /* Instruction parse */ 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] >> F_V_FL) & F_M_FL; /* get class */ switch (j) { /* case on class */ case F_V_FO: /* func out */ tptr = strchr (cptr, ','); /* find dst */ if (!tptr) /* none? */ return SCPE_ARG; *tptr = 0; /* split fields */ cptr = get_fnc (cptr, val); /* fo # */ if (!cptr) return SCPE_ARG; cptr = get_sd (tptr + 1, val, 0, FALSE); /* dst */ break; case F_V_FOI: /* func out impl */ cptr = get_fnc (cptr, val); /* fo # */ break; case F_V_SF: /* skip func */ cptr = get_sd (cptr, val, ',', TRUE); /* src */ if (!cptr) return SCPE_ARG; case F_V_SFI: /* skip func impl */ cptr = get_fnc (cptr, val); /* fo # */ break; case F_V_RR: /* reg-reg */ cptr = get_sd (cptr, val, ',', TRUE); /* src */ if (!cptr) return SCPE_ARG; cptr = get_op (cptr, val, ','); /* op */ if (!cptr) return SCPE_ARG; cptr = get_sd (cptr, val, 0, FALSE); /* dst */ break; case F_V_ZR: /* zero-reg */ cptr = get_op (cptr, val, ','); /* op */ if (!cptr) return SCPE_ARG; cptr = get_sd (cptr, val, 0, FALSE); /* dst */ break; case F_V_RS: /* reg self */ cptr = get_sd (cptr, val, ',', TRUE); /* src */ if (!cptr) return SCPE_ARG; val[0] = val[0] | I_GETSRC (val[0]); /* duplicate */ cptr = get_op (cptr, val, 0); /* op */ break; case F_V_JC: /* jump cond */ cptr = get_sd (cptr, val, ',', TRUE); /* src */ if (!cptr) return SCPE_ARG; cptr = get_glyph (cptr, gbuf, ','); /* cond */ for (k = 0; k < 8; k++) { /* symbol? */ if (strcmp (gbuf, cdname[k]) == 0) break; } if (k >= 8) return SCPE_ARG; val[0] = val[0] | (k << (I_V_OP + 1)); /* or to inst */ case F_V_JU: /* jump uncond */ cptr = get_ma (cptr, val, 0); /* addr */ break; case F_V_RM: /* reg mem */ cptr = get_sd (cptr, val, ',', TRUE); /* src */ if (!cptr) return SCPE_ARG; case F_V_ZM: /* zero mem */ cptr = get_op (cptr, val, ','); /* op */ if (!cptr) return SCPE_ARG; cptr = get_ma (cptr, val, 0); /* addr */ break; case F_V_MR: /* mem reg */ cptr = get_ma (cptr, val, ','); /* addr */ if (!cptr) return SCPE_ARG; cptr = get_op (cptr, val, ','); /* op */ if (!cptr) return SCPE_ARG; cptr = get_sd (cptr, val, 0, FALSE); /* dst */ break; case F_V_MS: /* mem self */ cptr = get_ma (cptr, val, ','); /* addr */ if (!cptr) return SCPE_ARG; cptr = get_op (cptr, val, 0); /* op */ break; } /* end case */ if (!cptr || (*cptr != 0)) /* junk at end? */ return SCPE_ARG; return (j >= F_2WD)? -1: SCPE_OK; } simh-3.8.1/GRI/gri_stddev.c0000644000175000017500000003112511107341440013555 0ustar vlmvlm/* gri_stddev.c: GRI-909 standard devices Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti S42-001 terminal input tto S42-002 terminal output hsr S42-004 high speed reader hsp S42-006 high speed punch rtc real time clock 31-May-08 RMS Fixed declarations (found by Peter Schorn) 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 22-Nov-05 RMS Revised for new terminal processing routines 29-Dec-03 RMS Added support for console backpressure 25-Apr-03 RMS Revised for extended file support 22-Dec-02 RMS Added break support 01-Nov-02 RMS Added 7b/8B support to terminal */ #include "gri_defs.h" #include uint32 hsr_stopioe = 1, hsp_stopioe = 1; extern uint16 M[]; extern uint32 dev_done, ISR; t_stat tti_svc (UNIT *uhsr); t_stat tto_svc (UNIT *uhsr); t_stat tti_reset (DEVICE *dhsr); t_stat tto_reset (DEVICE *dhsr); t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat hsr_svc (UNIT *uhsr); t_stat hsp_svc (UNIT *uhsr); t_stat hsr_reset (DEVICE *dhsr); t_stat hsp_reset (DEVICE *dhsr); t_stat rtc_svc (UNIT *uhsr); t_stat rtc_reset (DEVICE *dhsr); int32 rtc_tps = 1000; /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list tti_mod TTI modifiers list */ UNIT tti_unit = { UDATA (&tti_svc, TT_MODE_KSR, 0), KBD_POLL_WAIT }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 8) }, { FLDATA (IRDY, dev_done, INT_V_TTI) }, { FLDATA (IENB, ISR, INT_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[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 8) }, { FLDATA (ORDY, dev_done, INT_V_TTO) }, { FLDATA (IENB, ISR, INT_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL }; /* HSR data structures hsr_dev HSR device descriptor hsr_unit HSR unit descriptor hsr_reg HSR register list hsr_mod HSR modifiers list */ UNIT hsr_unit = { UDATA (&hsr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG hsr_reg[] = { { ORDATA (BUF, hsr_unit.buf, 8) }, { FLDATA (IRDY, dev_done, INT_V_HSR) }, { FLDATA (IENB, ISR, INT_V_HSR) }, { DRDATA (POS, hsr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, hsr_unit.wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, hsr_stopioe, 0) }, { NULL } }; DEVICE hsr_dev = { "HSR", &hsr_unit, hsr_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &hsr_reset, NULL, NULL, NULL }; /* HSP data structures hsp_dev HSP device descriptor hsp_unit HSP unit descriptor hsp_reg HSP register list */ UNIT hsp_unit = { UDATA (&hsp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG hsp_reg[] = { { ORDATA (BUF, hsp_unit.buf, 8) }, { FLDATA (ORDY, dev_done, INT_V_HSP) }, { FLDATA (IENB, ISR, INT_V_HSP) }, { DRDATA (POS, hsp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, hsp_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, hsp_stopioe, 0) }, { NULL } }; DEVICE hsp_dev = { "HSP", &hsp_unit, hsp_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &hsp_reset, NULL, NULL, NULL }; /* RTC data structures rtc_dev RTC device descriptor rtc_unit RTC unit descriptor rtc_reg RTC register list */ UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), 16000 }; REG rtc_reg[] = { { FLDATA (RDY, dev_done, INT_V_RTC) }, { FLDATA (IENB, ISR, INT_V_RTC) }, { DRDATA (TIME, rtc_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, rtc_tps, 8), REG_NZ + PV_LEFT + REG_HIDDEN }, { NULL } }; DEVICE rtc_dev = { "RTC", &rtc_unit, rtc_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, &rtc_reset, NULL, NULL, NULL }; /* Console terminal function processors */ uint32 tty_rd (int32 src, int32 ea) { return tti_unit.buf; /* return data */ } t_stat tty_wr (uint32 dst, uint32 val) { tto_unit.buf = val & 0377; /* save char */ dev_done = dev_done & ~INT_TTO; /* clear ready */ sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ return SCPE_OK; } t_stat tty_fo (uint32 op) { if (op & TTY_IRDY) dev_done = dev_done & ~INT_TTI; if (op & TTY_ORDY) dev_done = dev_done & ~INT_TTO; return SCPE_OK; } uint32 tty_sf (uint32 op) { if (((op & TTY_IRDY) && (dev_done & INT_TTI)) || ((op & TTY_ORDY) && (dev_done & INT_TTO))) return 1; return 0; } /* Service routines */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_activate (uptr, uptr->wait); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) /* break? */ uptr->buf = 0; else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); dev_done = dev_done | INT_TTI; /* set ready */ uptr->pos = uptr->pos + 1; return SCPE_OK; } t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } } dev_done = dev_done | INT_TTO; /* set ready */ uptr->pos = uptr->pos + 1; return SCPE_OK; } /* Reset routines */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; /* clear buffer */ dev_done = dev_done & ~INT_TTI; /* clear ready */ sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ return SCPE_OK; } t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; /* clear buffer */ dev_done = dev_done | INT_TTO; /* set ready */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; return SCPE_OK; } /* High speed paper tape function processors */ uint32 hsrp_rd (int32 src, int32 ea) { return hsr_unit.buf; /* return data */ } t_stat hsrp_wr (uint32 dst, uint32 val) { hsp_unit.buf = val & 0377; /* save char */ dev_done = dev_done & ~INT_HSP; /* clear ready */ sim_activate (&hsp_unit, hsp_unit.wait); /* activate unit */ return SCPE_OK; } t_stat hsrp_fo (uint32 op) { if (op & PT_IRDY) dev_done = dev_done & ~INT_HSR; if (op & PT_ORDY) dev_done = dev_done & ~INT_HSP; if (op & PT_STRT) sim_activate (&hsr_unit, hsr_unit.wait); return SCPE_OK; } uint32 hsrp_sf (uint32 op) { if (((op & PT_IRDY) && (dev_done & INT_HSR)) || ((op & PT_ORDY) && (dev_done & INT_HSP))) return 1; return 0; } t_stat hsr_svc (UNIT *uptr) { int32 temp; if ((hsr_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (hsr_stopioe, SCPE_UNATT); if ((temp = getc (hsr_unit.fileref)) == EOF) { /* read char */ if (feof (hsr_unit.fileref)) { /* err or eof? */ if (hsr_stopioe) printf ("HSR end of file\n"); else return SCPE_OK; } else perror ("HSR I/O error"); clearerr (hsr_unit.fileref); return SCPE_IOERR; } dev_done = dev_done | INT_HSR; /* set ready */ hsr_unit.buf = temp & 0377; /* save char */ hsr_unit.pos = hsr_unit.pos + 1; return SCPE_OK; } t_stat hsp_svc (UNIT *uptr) { dev_done = dev_done | INT_HSP; /* set ready */ if ((hsp_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (hsp_stopioe, SCPE_UNATT); if (putc (hsp_unit.buf, hsp_unit.fileref) == EOF) { /* write char */ perror ("HSP I/O error"); /* error? */ clearerr (hsp_unit.fileref); return SCPE_IOERR; } hsp_unit.pos = hsp_unit.pos + 1; return SCPE_OK; } /* Reset routines */ t_stat hsr_reset (DEVICE *dptr) { hsr_unit.buf = 0; /* clear buffer */ dev_done = dev_done & ~INT_HSR; /* clear ready */ sim_cancel (&hsr_unit); /* deactivate unit */ return SCPE_OK; } t_stat hsp_reset (DEVICE *dptr) { hsp_unit.buf = 0; /* clear buffer */ dev_done = dev_done | INT_HSP; /* set ready */ sim_cancel (&hsp_unit); /* deactivate unit */ return SCPE_OK; } /* Clock function processors */ t_stat rtc_fo (int32 op) { if (op & RTC_OFF) /* clock off? */ sim_cancel (&rtc_unit); if ((op & RTC_ON) && !sim_is_active (&rtc_unit)) /* clock on? */ sim_activate (&rtc_unit, sim_rtc_init (rtc_unit.wait)); if (op & RTC_OV) /* clr ovflo? */ dev_done = dev_done & ~INT_RTC; return SCPE_OK; } uint32 rtc_sf (int32 op) { if ((op & RTC_OV) && (dev_done & INT_RTC)) return 1; return 0; } t_stat rtc_svc (UNIT *uptr) { M[RTC_CTR] = (M[RTC_CTR] + 1) & DMASK; /* incr counter */ if (M[RTC_CTR] == 0) /* ovflo? set ready */ dev_done = dev_done | INT_RTC; sim_activate (&rtc_unit, sim_rtc_calb (rtc_tps)); /* reactivate */ return SCPE_OK; } t_stat rtc_reset (DEVICE *dptr) { dev_done = dev_done & ~INT_RTC; /* clear ready */ sim_cancel (&rtc_unit); /* stop clock */ return SCPE_OK; } simh-3.8.1/GRI/gri_defs.h0000644000175000017500000003043711020324070013211 0ustar vlmvlm/* gri_defs.h: GRI-909 simulator definitions Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 12-Jan-08 RMS Added GRI-99 support 25-Apr-03 RMS Revised for extended file support 19-Sep-02 RMS Fixed declarations in gdev structure There are several discrepancies between the original GRI-909 Reference Manual of 1969 and the only surviving code sample, the MIT Crystal Growing System of 1972. These discrepancies were clarified by later documentation: 1. Ref Manual documents two GR's at codes 26-27; MITCS documents six GR's at 30-35. Answer: 6 GR's, 26-27 were used for character compares. 2. Ref Manual documents only unsigned overflow (carry) for arithmetic operator; MITCS uses both unsigned overflow (AOV) and signed overflow (SOV). Answer: signed and unsigned. 3. Ref Manual documents a ROM-subroutine multiply operator and mentions but does not document a "fast multiply"; MITCS uses an extended arithmetic operator with multiply, divide, and shift. Answer: EAO is a package of ROM subroutines with just four functions: multiply, divide, arithmetic right shift, and normalize. 4. Is SOV testable even if the FOA is not ADD? Answer: AOV and SOV are calculated regardless of the function. 5. How does the EAO handle divide overflow? Answer: set link. */ #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_DEV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_ILLINT 4 /* illegal intr */ /* Memory */ #define MAXMEMSIZE 32768 /* max memory size */ #define AMASK 077777 /* logical addr mask */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* Architectural constants */ #define SIGN 0100000 /* sign */ #define INDEX 0100000 /* indexed (GRI-99) */ #define DMASK 0177777 /* data mask */ #define CBIT (DMASK + 1) /* carry bit */ /* Instruction format */ #define I_M_SRC 077 /* source */ #define I_V_SRC 10 #define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC) #define I_M_OP 017 /* operator */ #define I_V_OP 6 #define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) #define I_M_DST 077 /* destination */ #define I_V_DST 0 #define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST) #define SF_V_REASON 1 /* SF reason */ /* IO return */ #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* Operators */ #define U_ZERO 000 /* zero */ #define U_IR 001 /* instruction reg */ #define U_FSK 002 /* func out/skip */ #define U_TRP 003 /* trap */ #define U_ISR 004 /* intr status */ #define U_MA 005 /* mem addr */ #define U_MEM 006 /* mem data */ #define U_SC 007 /* seq counter */ #define U_SWR 010 /* switch register */ #define U_AX 011 /* arith in 1 */ #define U_AY 012 /* arith in 2 */ #define U_AO 013 /* arith out */ #define U_EAO 014 /* ext arith */ #define U_MSR 017 /* machine status */ #define U_XR 022 /* GRI-99: idx reg */ #define U_GTRP 023 /* GRI-99: alt trap */ #define U_BSW 024 /* byte swap */ #define U_BPK 025 /* byte pack */ #define U_BCP1 026 /* byte compare 1 */ #define U_BCP2 027 /* byte compare 2 */ #define U_GR 030 /* hex general regs */ #define U_CDR 055 /* card reader */ #define U_CADR 057 #define U_DWC 066 /* disk */ #define U_DCA 067 #define U_DISK 070 #define U_LPR 071 /* line printer */ #define U_CAS 074 /* casette */ #define U_RTC 075 /* clock */ #define U_HS 076 /* paper tape */ #define U_TTY 077 /* console */ struct gdev { uint32 (*Src)(uint32); /* source */ t_stat (*Dst)(uint32, uint32); /* dest */ t_stat (*FO)(uint32); /* func out */ uint32 (*SF)(uint32); /* skip func */ }; /* Trap (jump) */ #define TRP_DIR 00 /* direct */ #define TRP_DEF 01 /* defer */ /* Interrupt status */ #define ISR_OFF 01 /* int off */ #define ISR_ON 02 /* int on */ /* Bus modifiers */ #define BUS_COM 002 /* complement */ #define BUS_FNC 014 /* function mask */ #define BUS_P1 004 /* + 1 */ #define BUS_L1 010 /* rotate left */ #define BUS_R1 014 /* rotate right */ /* Memory address modes */ #define MEM_MOD 03 #define MEM_DIR 00 /* direct */ #define MEM_DEF 01 /* defer */ #define MEM_IMM 02 /* immediate */ #define MEM_IDF 03 /* immediate defer */ /* Arithmetic unit */ #define FO_V_FOA 8 /* arith func */ #define FO_M_FOA 03 #define OP_GET_FOA(x) (((x) >> (FO_V_FOA - I_V_OP)) & FO_M_FOA) #define AO_ADD 00 /* add */ #define AO_AND 01 /* and */ #define AO_XOR 02 /* xor */ #define AO_IOR 03 /* or */ #define EAO_MUL 01 /* multiply */ #define EAO_DIV 02 /* divide */ #define EAO_ARS 03 /* arith rshft */ #define EAO_NORM 04 /* normalize */ /* Machine status */ #define MSR_V_BOV 15 /* bus carry */ #define MSR_V_L 14 /* bus link */ #define MSR_V_FOA 8 /* arith func */ #define MSR_M_FOA 03 #define MSR_V_SOV 1 /* arith ovflo */ #define MSR_V_AOV 0 /* arith carry */ #define MSR_BOV (1u << MSR_V_BOV) #define MSR_L (1u << MSR_V_L) #define MSR_FOA (MSR_M_FOA << MSR_V_FOA) #define MSR_SOV (1u << MSR_V_SOV) #define MSR_AOV (1u << MSR_V_AOV) #define MSR_GET_FOA(x) (((x) >> MSR_V_FOA) & MSR_M_FOA) #define MSR_PUT_FOA(x,n) (((x) & ~(MSR_M_FOA << MSR_V_FOA)) | \ (((n) & MSR_M_FOA) << MSR_V_FOA)) #define MSR_RW (MSR_BOV|MSR_L|MSR_FOA|MSR_SOV|MSR_AOV) /* Real time clock */ #define RTC_OFF 001 /* off */ #define RTC_ON 002 /* clock on */ #define RTC_OV 010 /* clock flag */ #define RTC_CTR 0103 /* counter */ /* Terminal */ #define TTY_ORDY 002 /* output flag */ #define TTY_IRDY 010 /* input flag */ /* Paper tape */ #define PT_STRT 001 /* start reader */ #define PT_ORDY 002 /* output flag */ #define PT_IRDY 010 /* input flag */ /* Interrupt masks (ISR) */ #define INT_V_TTO 0 /* console out */ #define INT_V_TTI 1 /* console in */ #define INT_V_HSP 2 /* paper tape punch */ #define INT_V_HSR 3 /* paper tape reader */ #define INT_V_LPR 5 /* line printer */ #define INT_V_CDR 7 /* card reader */ #define INT_V_CASW 9 /* casette */ #define INT_V_CASR 10 #define INT_V_RTC 11 /* clock */ #define INT_V_DISK 14 /* disk */ #define INT_V_NODEF 16 /* nodefer */ #define INT_V_ON 17 /* enable */ #define INT_TTO (1u << INT_V_TTO) #define INT_TTI (1u << INT_V_TTI) #define INT_HSP (1u << INT_V_HSP) #define INT_HSR (1u << INT_V_HSR) #define INT_LPR (1u << INT_V_LPR) #define INT_CDR (1u << INT_V_CDR) #define INT_CASW (1u << INT_V_CAS1) #define INT_CASR (1u << INT_V_CAS2) #define INT_RTC (1u << INT_V_RTC) #define INT_DISK (1u << INT_V_DISK) #define INT_NODEF (1u << INT_V_NODEF) #define INT_ON (1u << INT_V_ON) #define INT_PENDING (INT_ON | INT_NODEF) /* Vectors */ #define VEC_BKP 0000 /* breakpoint */ #define VEC_TTO 0011 /* console out */ #define VEC_TTI 0014 /* console in */ #define VEC_HSP 0017 /* paper tape punch */ #define VEC_HSR 0022 /* paper tape reader */ #define VEC_LPR 0033 /* line printer */ #define VEC_CDR 0033 /* card reader */ #define VEC_CASW 0044 /* casette */ #define VEC_CASR 0047 #define VEC_DISK 0055 /* disk */ #define VEC_RTC 0100 /* clock */ simh-3.8.1/GRI/gri_cpu.c0000644000175000017500000010772011112024272013055 0ustar vlmvlm/* gri_cpu.c: GRI-909 CPU simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu GRI-909/GRI-99 CPU 14-Jan-08 RMS Added GRI-99 support 28-Apr-07 RMS Removed clock initialization 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 18-Jul-04 RMS Fixed missing ao_update calls in AX, AY write 17-Jul-04 RMS Revised MSR, EAO based on additional documentation 14-Mar-03 RMS Fixed bug in SC queue tracking The system state for the GRI-909/GRI-99 is: AX<15:0> arithmetic input AY<15:0> arithmetic input BSW<15:0> byte swapper BPK<15:0> byte packer GR[0:5]<15:0> extended general registers MSR<15:0> machine status register TRP<15:0> trap register (subroutine return) SC<14:0> sequence counter XR<15:0> index register (GRI-99 only) The GRI-909 has, nominally, just one instruction format: move. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | source | op | destination | move +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <6:9> operation xx1x complement 01xx add 1 10xx rotate left 1 11xx rotate right 1 In fact, certain of the source and destination operators have side effects, yielding four additional instruction formats: function out, skip on function, memory reference, and conditional jump. The function out format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 0 0 0 1 0| pulse | destination | function out +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The skip on function format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | source | skip |rv| 0 0 0 0 1 0| skip function +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The memory reference format is (src and/or dst = 006): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | source | op | mode| destination | memory ref +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | address or immediate | +-----------------------------------------------+ <6:9> operation xx0x direct, ea = M[SC+1] xx1x immediate, ea = SC+1 xxx1 indirect, M[ea] = M[ea]+1, then ea = M[ea] 01xx add 1 10xx rotate left 1 11xx rotate right 1 The conditional jump format is (src != 006): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | source | cond|rv|df| 0 0 0 0 1 1| cond jump +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | jump address | +-----------------------------------------------+ <6:9> operation xxx0 direct, ea = M[SC+1] xxx1 indirect, ea = M[SC+1], M[ea] = M[ea]+1, then ea = M[ea] xx1x reverse conditional sense x1xx jump if src == 0 1xxx jump if src < 0 This routine is the instruction decode routine for the GRI-909. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered unknown source or destination and STOP_OPR flag set I/O error in I/O simulator 2. Interrupts. The interrupt structure is kept in two parallel variables: dev_done device done flags ISR interrupt status register (enables) In addition, there is a master interrupt enable, and a one cycle interrupt defer, both kept in dev_done. 3. Non-existent memory. On the GRI-909, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: gri_defs.h add interrupt request definition gri_cpu.c add dev_tab table entry gri_sys.c add sim_devices table entry */ #include "gri_defs.h" #define SCQ_SIZE 64 /* must be 2**n */ #define SCQ_MASK (SCQ_SIZE - 1) #define SCQ_ENTRY scq[scq_p = (scq_p - 1) & SCQ_MASK] = SC #define UNIT_V_AO (UNIT_V_UF + 0) /* AO */ #define UNIT_AO (1u << UNIT_V_AO) #define UNIT_V_EAO (UNIT_V_UF + 1) /* EAO */ #define UNIT_EAO (1u << UNIT_V_EAO) #define UNIT_V_GPR (UNIT_V_UF + 2) /* GPR */ #define UNIT_GPR (1u << UNIT_V_GPR) #define UNIT_V_BSWPK (UNIT_V_UF + 3) /* BSW-BPK */ #define UNIT_BSWPK (1u << UNIT_V_BSWPK) #define UNIT_V_GRI99 (UNIT_V_UF + 4) /* GRI-99 */ #define UNIT_GRI99 (1u << UNIT_V_GRI99) #define UNIT_V_MSIZE (UNIT_V_UF + 5) /* dummy mask */ #define UNIT_MSIZE (1u << UNIT_V_MSIZE) #define IDX_ADD(x) ((((cpu_unit.flags & UNIT_GRI99) && ((x) & INDEX))? ((x) + XR): (x)) & AMASK) uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ uint32 SC; /* sequence cntr */ uint32 AX, AY, AO; /* arithmetic unit */ uint32 IR; /* instr reg */ uint32 MA; /* memory addr */ uint32 TRP; /* subr return */ uint32 MSR; /* machine status */ uint32 ISR; /* interrupt status */ uint32 BSW, BPK; /* byte swap, pack */ uint32 GR[6]; /* extended general regs */ uint32 SWR; /* switch reg */ uint32 DR; /* display register */ uint32 XR; /* index register */ uint32 thwh = 0; /* thumbwheel */ uint32 dev_done = 0; /* device flags */ uint32 bkp = 0; /* bkpt pending */ uint32 stop_opr = 1; /* stop ill operator */ int16 scq[SCQ_SIZE] = { 0 }; /* PC queue */ int32 scq_p = 0; /* PC queue ptr */ REG *scq_r = NULL; /* PC queue reg ptr */ extern int32 sim_interval; 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 bus_op (uint32 src, uint32 op, uint32 dst); /* Dispatch tables for source, dest, function out, skip on function */ uint32 no_rd (uint32 src); t_stat no_wr (uint32 dst, uint32 val); t_stat no_fo (uint32 op); uint32 no_sf (uint32 op); uint32 zero_rd (uint32 src); t_stat zero_wr (uint32 dst, uint32 val); t_stat zero_fo (uint32 op); uint32 zero_sf (uint32 op); uint32 ir_rd (uint32 op); t_stat ir_fo (uint32 op); uint32 trp_rd (uint32 src); t_stat trp_wr (uint32 dst, uint32 val); uint32 atrp_rd (uint32 src); t_stat atrp_wr (uint32 dst, uint32 val); uint32 isr_rd (uint32 src); t_stat isr_wr (uint32 dst, uint32 val); t_stat isr_fo (uint32 op); uint32 isr_sf (uint32 op); uint32 ma_rd (uint32 src); uint32 mem_rd (uint32 src); t_stat mem_wr (uint32 dst, uint32 val); uint32 sc_rd (uint32 src); t_stat sc_wr (uint32 dst, uint32 val); uint32 swr_rd (uint32 src); uint32 ax_rd (uint32 src); t_stat ax_wr (uint32 dst, uint32 val); uint32 ay_rd (uint32 src); t_stat ay_wr (uint32 dst, uint32 val); uint32 ao_rd (uint32 src); t_stat ao_fo (uint32 op); uint32 ao_sf (uint32 op); uint32 ao_update (void); t_stat eao_fo (uint32 op); uint32 msr_rd (uint32 src); t_stat msr_wr (uint32 dst, uint32 val); uint32 bsw_rd (uint32 src); t_stat bsw_wr (uint32 dst, uint32 val); uint32 bpk_rd (uint32 src); t_stat bpk_wr (uint32 dst, uint32 val); uint32 gr_rd (uint32 src); t_stat gr_wr (uint32 dst, uint32 val); uint32 xr_rd (uint32 src); t_stat xr_wr (uint32 dst, uint32 val); extern t_stat rtc_fo (uint32 op); extern uint32 rtc_sf (uint32 op); extern uint32 hsrp_rd (uint32 src); extern t_stat hsrp_wr (uint32 dst, uint32 val); extern t_stat hsrp_fo (uint32 op); extern uint32 hsrp_sf (uint32 op); extern uint32 tty_rd (uint32 src); extern t_stat tty_wr (uint32 dst, uint32 val); extern t_stat tty_fo (uint32 op); extern uint32 tty_sf (uint32 op); struct gdev dev_tab[64] = { { &zero_rd, &zero_wr, &zero_fo, &zero_sf }, /* 00: zero */ { &ir_rd, &zero_wr, &ir_fo, &zero_sf }, /* ir */ { &no_rd, &no_wr, &no_fo, &no_sf }, /* fo/sf */ { &trp_rd, &trp_wr, &zero_fo, &zero_sf }, /* trp */ { &isr_rd, &isr_wr, &isr_fo, &isr_sf }, /* isr */ { &ma_rd, &no_wr, &no_fo, &no_sf }, /* ma */ { &mem_rd, &mem_wr, &zero_fo, &zero_sf }, /* memory */ { &sc_rd, &sc_wr, &zero_fo, &zero_sf }, /* sc */ { &swr_rd, &no_wr, &no_fo, &no_sf }, /* swr */ { &ax_rd, &ax_wr, &zero_fo, &zero_sf }, /* ax */ { &ay_rd, &ay_wr, &zero_fo, &zero_sf }, /* ay */ { &ao_rd, &zero_wr, &ao_fo, &ao_sf }, /* ao */ { &zero_rd, &zero_wr, &eao_fo, &zero_sf }, /* eao */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &msr_rd, &msr_wr, &zero_fo, &zero_sf }, /* msr */ { &no_rd, &no_wr, &no_fo, &no_sf }, /* 20 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &xr_rd, &xr_wr, &no_fo, &no_sf }, /* xr */ { &atrp_rd, &atrp_wr, &no_fo, &no_sf }, { &bsw_rd, &bsw_wr, &no_fo, &no_sf }, /* bsw */ { &bpk_rd, &bpk_wr, &no_fo, &no_sf }, /* bpk */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* 30: gr1 */ { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr2 */ { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr3 */ { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr4 */ { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr5 */ { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr6 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, /* 40 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, /* 50 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, /* 60 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, /* 70 */ { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &no_rd, &no_wr, &no_fo, &no_sf }, { &zero_rd, &zero_wr, &rtc_fo, &rtc_sf }, /* rtc */ { &hsrp_rd, &hsrp_wr, &hsrp_fo, &hsrp_sf }, /* hsrp */ { &tty_rd, &tty_wr, &tty_fo, &tty_sf } /* tty */ }; static const int32 vec_map[16] = { VEC_TTO, VEC_TTI, VEC_HSP, VEC_HSR, -1, -1, -1, -1, -1, -1, -1, VEC_RTC, -1, -1, -1, -1 }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_AO+UNIT_EAO+UNIT_GPR, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (SC, SC, 15) }, { ORDATA (AX, AX, 16) }, { ORDATA (AY, AY, 16) }, { ORDATA (AO, AO, 16), REG_RO }, { ORDATA (TRP, TRP, 16) }, { ORDATA (MSR, MSR, 16) }, { ORDATA (ISR, ISR, 16) }, { ORDATA (BSW, BSW, 16) }, { ORDATA (BPK, BPK, 16) }, { ORDATA (GR1, GR[0], 16) }, { ORDATA (GR2, GR[1], 16) }, { ORDATA (GR3, GR[2], 16) }, { ORDATA (GR4, GR[3], 16) }, { ORDATA (GR5, GR[4], 16) }, { ORDATA (GR6, GR[5], 16) }, { ORDATA (XR, XR, 16) }, { FLDATA (BOV, MSR, MSR_V_BOV) }, { FLDATA (L, MSR, MSR_V_L) }, { GRDATA (FOA, MSR, 8, 2, MSR_V_FOA) }, { FLDATA (SOV, MSR, MSR_V_SOV) }, { FLDATA (AOV, MSR, MSR_V_AOV) }, { ORDATA (IR, IR, 16), REG_RO }, { ORDATA (MA, MA, 16), REG_RO }, { ORDATA (SWR, SWR, 16) }, { ORDATA (DR, DR, 16) }, { ORDATA (THW, thwh, 6) }, { ORDATA (IREQ, dev_done, INT_V_NODEF) }, { FLDATA (ION, dev_done, INT_V_ON) }, { FLDATA (INODEF, dev_done, INT_V_NODEF) }, { FLDATA (BKP, bkp, 0) }, { BRDATA (SCQ, scq, 8, 15, SCQ_SIZE), REG_RO + REG_CIRC }, { ORDATA (SCQP, scq_p, 6), REG_HRO }, { FLDATA (STOP_OPR, stop_opr, 0) }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_GRI99, UNIT_GRI99, "GRI99", "GRI99", NULL }, { UNIT_GRI99, 0, "GRI909", "GRI909", NULL }, { UNIT_AO, UNIT_AO, "AO", "AO", NULL }, { UNIT_AO, 0, "no AO", "NOAO", NULL }, { UNIT_EAO, UNIT_EAO, "EAO", "EAO", NULL }, { UNIT_EAO, 0, "no EAO", "NOEAO", NULL }, { UNIT_GPR, UNIT_GPR, "GPR", "GPR", NULL }, { UNIT_GPR, 0, "no GPR", "NOGPR", NULL }, { UNIT_BSWPK, UNIT_BSWPK, "BSW-BPK", "BSW-BPK", NULL }, { UNIT_BSWPK, 0, "no BSW-BPK", "NOBSW-BPK", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 15, 1, 8, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; t_stat sim_instr (void) { uint32 src, dst, op, t, jmp; t_stat reason; /* Restore register state */ SC = SC & AMASK; /* load local PC */ reason = 0; ao_update (); /* update AO */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } if (bkp) { /* breakpoint? */ bkp = 0; /* clear request */ dev_done = dev_done & ~INT_ON; /* int off */ M[VEC_BKP] = SC; /* save SC */ SC = VEC_BKP + 1; /* new SC */ } else if ((dev_done & (INT_PENDING | ISR)) > (INT_PENDING)) { /* intr? */ int32 i, vec; t = dev_done & ISR; /* find hi pri */ for (i = 15; i >= 0; i--) { if ((t >> i) & 1) break; } if ((i < 0) || ((vec = vec_map[i]) < 0)) { /* undefined? */ reason = STOP_ILLINT; /* stop */ break; } dev_done = dev_done & ~INT_ON; /* int off */ M[vec] = SC; /* save SC */ SC = vec + 1; /* new SC */ continue; } if (sim_brk_summ && sim_brk_test (SC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } MA = SC; /* set mem addr */ IR = M[MA]; /* fetch instr */ dev_done = dev_done | INT_NODEF; /* clr ion defer */ sim_interval = sim_interval - 1; /* Decode instruction types */ src = I_GETSRC (IR); /* src unit */ dst = I_GETDST (IR); /* dst unit */ op = I_GETOP (IR); /* bus op */ if (src == U_FSK) { /* func out? */ reason = dev_tab[dst].FO (op); /* send function */ SC = (SC + 1) & AMASK; /* incr SC */ } else if (dst == U_FSK) { /* skip func? */ t = dev_tab[src].SF (op & ~1); /* issue SF */ reason = t >> SF_V_REASON; if ((t ^ op) & 1) /* skip? */ SC = SC + 2; SC = (SC + 1) & AMASK; /* incr SC */ } else if ((src != U_MEM) && (dst == U_TRP)) { /* cond jump */ t = dev_tab[src].Src (src); /* get source */ switch (op >> 1) { /* case on jump */ case 00: /* never */ jmp = 0; break; case 01: /* always */ jmp = 1; break; case 02: /* src == 0 */ jmp = (t == 0); break; case 03: /* src != 0 */ jmp = (t != 0); break; case 04: /* src < 0 */ jmp = (t >= SIGN); break; case 05: /* src >= 0 */ jmp = (t < SIGN); break; case 06: /* src <= 0 */ jmp = (t == 0) || (t & SIGN); break; case 07: /* src > 0 */ jmp = (t != 0) && !(t & SIGN); break; } if (jmp) { /* jump taken? */ SCQ_ENTRY; /* save SC */ SC = (SC + 1) & AMASK; /* incr SC once */ MA = M[SC]; /* get jump addr */ MA = IDX_ADD (MA); /* index? */ if (op & TRP_DEF) { /* defer? */ t = (M[MA] + 1) & DMASK; /* autoinc */ if (MEM_ADDR_OK (MA)) M[MA] = t; MA = IDX_ADD (t); /* index? */ } TRP = SC; /* save SC */ SC = MA; /* load new SC */ } else SC = (SC + 2) & AMASK; /* incr SC twice */ } else if ((src != U_MEM) && (dst != U_MEM)) { /* reg-reg? */ reason = bus_op (src, op, dst); /* xmt and modify */ SC = (SC + 1) & AMASK; /* incr SC */ } /* Memory reference. The second SC increment occurs after the first execution cycle. For direct, defer, and immediate defer, this is after the first memory read and before the bus transfer; but for immediate, it is after the bus transfer. */ else { /* memory reference */ SC = (SC + 1) & AMASK; /* incr SC */ switch (op & MEM_MOD) { /* case on addr mode */ case MEM_DIR: /* direct */ MA = M[SC]; /* get address */ MA = IDX_ADD (MA); /* index? */ SC = (SC + 1) & AMASK; /* incr SC again */ reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ break; case MEM_DEF: /* defer */ MA = M[SC]; /* get ind addr */ MA = IDX_ADD (MA); /* index? */ SC = (SC + 1) & AMASK; /* incr SC again */ t = (M[MA] + 1) & DMASK; /* autoinc */ if (MEM_ADDR_OK (MA)) M[MA] = t; MA = IDX_ADD (t); /* index? */ reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ break; case MEM_IMM: /* immediate */ MA = SC; /* eff addr */ reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ SC = (SC + 1) & AMASK; /* incr SC again */ break; case MEM_IDF: /* immediate defer */ MA = SC; /* get ind addr */ t = (M[MA] + 1) & DMASK; /* autoinc */ if (MEM_ADDR_OK (MA)) M[MA] = t; MA = IDX_ADD (t); /* index? */ SC = (SC + 1) & AMASK; /* incr SC again */ reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ break; } /* end switch */ } /* end mem ref */ } /* end while */ /* Simulation halted */ ao_update (); /* update AO */ scq_r->qptr = scq_p; /* update sc q ptr */ return reason; } /* Bus operations */ t_stat bus_op (uint32 src, uint32 op, uint32 dst) { uint32 t, old_t; t = dev_tab[src].Src (src); /* get src */ if (op & BUS_COM) /* complement? */ t = t ^ DMASK; switch (op & BUS_FNC) { /* case op */ case BUS_P1: /* plus 1 */ t = t + 1; /* do add */ if (t & CBIT) /* set cry out */ MSR = MSR | MSR_BOV; else MSR = MSR & ~MSR_BOV; break; case BUS_L1: /* left 1 */ t = (t << 1) | ((MSR & MSR_L)? 1: 0); /* rotate */ if (t & CBIT) /* set link out */ MSR = MSR | MSR_L; else MSR = MSR & ~MSR_L; break; case BUS_R1: /* right 1 */ old_t = t; t = (t >> 1) | ((MSR & MSR_L)? SIGN: 0); /* rotate */ if (old_t & 1) /* set link out */ MSR = MSR | MSR_L; else MSR = MSR & ~MSR_L; break; } /* end case op */ if (dst == thwh) /* display dst? */ DR = t & DMASK; return dev_tab[dst].Dst (dst, t & DMASK); /* store dst */ } /* Non-existent device */ uint32 no_rd (uint32 src) { return 0; } t_stat no_wr (uint32 dst, uint32 dat) { return stop_opr; } t_stat no_fo (uint32 fnc) { return stop_opr; } uint32 no_sf (uint32 fnc) { return (stop_opr << SF_V_REASON); } /* Zero device */ uint32 zero_rd (uint32 src) { return 0; } t_stat zero_wr (uint32 dst, uint32 val) { return SCPE_OK; } t_stat zero_fo (uint32 op) { switch (op & 3) { /* FOM link */ case 1: /* CLL */ MSR = MSR & ~MSR_L; break; case 2: /* STL */ MSR = MSR | MSR_L; break; case 3: /* CML */ MSR = MSR ^ MSR_L; break; } if (op & 4) /* HALT */ return STOP_HALT; return SCPE_OK; } uint32 zero_sf (uint32 op) { if ((op & 010) || /* power always ok */ ((op & 4) && (MSR & MSR_L)) || /* link set? */ ((op & 2) && (MSR & MSR_BOV))) /* BOV set? */ return 1; return 0; } /* Instruction register (01) */ uint32 ir_rd (uint32 src) { return IR; } t_stat ir_fo (uint32 op) { if (op & 2) bkp = 1; return SCPE_OK; } /* Trap register (03) */ uint32 trp_rd (uint32 src) { return TRP; } t_stat trp_wr (uint32 dst, uint32 val) { TRP = val; return SCPE_OK; } /* Interrupt status register (04) */ uint32 isr_rd (uint32 src) { return ISR; } t_stat isr_wr (uint32 dst, uint32 dat) { ISR = dat; return SCPE_OK; } t_stat isr_fo (uint32 op) { if (op & ISR_ON) dev_done = (dev_done | INT_ON) & ~INT_NODEF; if (op & ISR_OFF) dev_done = dev_done & ~INT_ON; return SCPE_OK; } uint32 isr_sf (uint32 op) { return 0; } /* Memory address (05) */ uint32 ma_rd (uint32 src) { return MA; } /* Memory (06) */ uint32 mem_rd (uint32 src) { return M[MA]; } t_stat mem_wr (uint32 dst, uint32 dat) { if (MEM_ADDR_OK (MA)) M[MA] = dat; return SCPE_OK; } /* Sequence counter (07) */ uint32 sc_rd (uint32 src) { return SC; } t_stat sc_wr (uint32 dst, uint32 dat) { SCQ_ENTRY; SC = IDX_ADD (dat); return SCPE_OK; } /* Switch register (10) */ uint32 swr_rd (uint32 src) { return SWR; } /* Machine status register (17) */ uint32 msr_rd (uint32 src) { return MSR & MSR_RW; } t_stat msr_wr (uint32 src, uint32 dat) { MSR = dat & MSR_RW; /* new MSR */ ao_update (); /* update SOV,AOV */ return SCPE_OK; } /* Arithmetic operator (11:13) */ uint32 ao_update (void) { uint32 af = MSR_GET_FOA (MSR); switch (af) { case AO_ADD: AO = (AX + AY) & DMASK; /* add */ break; case AO_AND: AO = AX & AY; /* and */ break; case AO_XOR: /* xor */ AO = AX ^ AY; break; case AO_IOR: AO = AX | AY; /* or */ break; } if ((AX + AY) & CBIT) /* always calc AOV */ MSR = MSR | MSR_AOV; else MSR = MSR & ~MSR_AOV; if (SIGN & ((AX ^ (AX + AY)) & (~AX ^ AY))) /* always calc SOV */ MSR = MSR | MSR_SOV; else MSR = MSR & ~MSR_SOV; return AO; } uint32 ax_rd (uint32 src) { if (cpu_unit.flags & UNIT_AO) return AX; else return 0; } t_stat ax_wr (uint32 dst, uint32 dat) { if (cpu_unit.flags & UNIT_AO) { AX = dat; ao_update (); return SCPE_OK; } return stop_opr; } uint32 ay_rd (uint32 src) { if (cpu_unit.flags & UNIT_AO) return AY; else return 0; } t_stat ay_wr (uint32 dst, uint32 dat) { if (cpu_unit.flags & UNIT_AO) { AY = dat; ao_update (); return SCPE_OK; } return stop_opr; } uint32 ao_rd (uint32 src) { if (cpu_unit.flags & UNIT_AO) return ao_update (); else return 0; } t_stat ao_fo (uint32 op) { if (cpu_unit.flags & UNIT_AO) { uint32 t = OP_GET_FOA (op); /* get func */ MSR = MSR_PUT_FOA (MSR, t); /* store in MSR */ ao_update (); /* update AOV */ return SCPE_OK; } return stop_opr; } uint32 ao_sf (uint32 op) { if (!(cpu_unit.flags & UNIT_AO)) /* not installed? */ return (stop_opr << SF_V_REASON); if (((op & 2) && (MSR & MSR_AOV)) || /* arith carry? */ ((op & 4) && (MSR & MSR_SOV))) /* arith overflow? */ return 1; return 0; } /* Extended arithmetic operator (14) */ t_stat eao_fo (uint32 op) { uint32 t; if (!(cpu_unit.flags & UNIT_EAO)) /* EAO installed? */ return stop_opr; switch (op) { case EAO_MUL: /* mul? */ t = AX * AY; /* AX * AY */ AX = (t >> 16) & DMASK; /* to AX'GR1 */ GR[0] = t & DMASK; break; case EAO_DIV: /* div? */ if (AY && (AX < AY)) { t = (AX << 16) | GR[0]; /* AX'GR1 / AY */ GR[0] = t / AY; /* quo to GR1 */ AX = t % AY; /* rem to AX */ MSR = MSR & ~MSR_L; /* clear link */ } else MSR = MSR | MSR_L; /* set link */ break; case EAO_ARS: /* arith right? */ t = 0; /* shift limiter */ if (AX & SIGN) /* L = sign */ MSR = MSR | MSR_L; else MSR = MSR & ~MSR_L; do { /* shift one bit */ AY = ((AY >> 1) | (AX << 15)) & DMASK; AX = (AX & SIGN) | (AX >> 1); GR[0] = (GR[0] + 1) & DMASK; } while (GR[0] && (++t < 32)); /* until cnt or limit */ break; case EAO_NORM: /* norm? */ if ((AX | AY) != 0) { /* can normalize? */ while ((AX & SIGN) != ((AX << 1) & SIGN)) { /* until AX15 != AX14 */ AX = ((AX << 1) | (AY >> 15)) & DMASK; AY = (AY << 1) & DMASK; GR[0] = (GR[0] + 1) & DMASK; } } break; } // MSR = MSR_PUT_FOA (MSR, AO_ADD); /* AO fnc is add */ ao_update (); return SCPE_OK; } /* Index register (GRI-99) (22) */ uint32 xr_rd (uint32 src) { if (cpu_unit.flags & UNIT_GRI99) return XR; else return 0; } t_stat xr_wr (uint32 dst, uint32 val) { if (cpu_unit.flags & UNIT_GRI99) { XR = val; return SCPE_OK; } return stop_opr; } /* Alternate trap (GRI-99) (23) */ uint32 atrp_rd (uint32 src) { if (cpu_unit.flags & UNIT_GRI99) return TRP; else return 0; } t_stat atrp_wr (uint32 dst, uint32 val) { if (cpu_unit.flags & UNIT_GRI99) { TRP = val; return SCPE_OK; } return stop_opr; } /* Byte swapper (24) */ uint32 bsw_rd (uint32 src) { if (cpu_unit.flags & UNIT_BSWPK) return BSW; else return 0; } t_stat bsw_wr (uint32 dst, uint32 val) { if (cpu_unit.flags & UNIT_BSWPK) { BSW = ((val >> 8) & 0377) | ((val & 0377) << 8); return SCPE_OK; } return stop_opr; } /* Byte packer (25) */ uint32 bpk_rd (uint32 src) { if (cpu_unit.flags & UNIT_BSWPK) return BPK; else return 0; } t_stat bpk_wr (uint32 dst, uint32 val) { if (cpu_unit.flags & UNIT_BSWPK) { BPK = ((BPK & 0377) << 8) | (val & 0377); return SCPE_OK; } return stop_opr; } /* General registers (30:35) */ uint32 gr_rd (uint32 src) { if (cpu_unit.flags & UNIT_GPR) return GR[src - U_GR]; else return 0; } t_stat gr_wr (uint32 dst, uint32 dat) { if (cpu_unit.flags & UNIT_GPR) { GR[dst - U_GR] = dat; return SCPE_OK; } return stop_opr; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int32 i; AX = AY = AO = 0; XR = 0; TRP = 0; ISR = 0; MSR = 0; MA = IR = 0; BSW = BPK = 0; for (i = 0; i < 6; i++) GR[i] = 0; dev_done = dev_done & ~INT_PENDING; scq_r = find_reg ("SCQ", NULL, dptr); if (scq_r) scq_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; } 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; } simh-3.8.1/NOVA/0000755000175000017500000000000011126242354011404 5ustar vlmvlmsimh-3.8.1/NOVA/nova_tt.c0000644000175000017500000002020711012103316013207 0ustar vlmvlm/* nova_tt.c: NOVA console terminal simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti terminal input tto terminal output 04-Jul-07 BKR fixed Dasher CR/LF swap function in 'tti_svc()', DEV_SET/CLR macros now used, TTO device may now be DISABLED 29-Dec-03 RMS Added console backpressure support 25-Apr-03 RMS Revised for extended file support 05-Jan-02 RMS Fixed calling sequence for setmod 03-Oct-02 RMS Added DIBs 30-May-02 RMS Widened POS to 32b 30-Nov-01 RMS Added extended SET/SHOW support 17-Sep-01 RMS Removed multiconsole support 07-Sep-01 RMS Moved function prototypes 31-May-01 RMS Added multiconsole support Notes: - TTO output is always masked to 7 bits in this rev - TTO "Dasher" attribute sends '\b' to console instead of '\031' - TTO may be disabled - TTI input is always masked to 7 bits in this rev - TTI "Dasher" attribute swaps and - TTI may not be disabled */ #include "nova_defs.h" #define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ #define UNIT_DASHER (1 << UNIT_V_DASHER) extern int32 int_req, dev_busy, dev_done, dev_disable; int32 tti (int32 pulse, int32 code, int32 AC); int32 tto (int32 pulse, int32 code, int32 AC); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr, void *desc); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list ttx_mod TTI/TTO modifiers list */ DIB tti_dib = { DEV_TTI, INT_TTI, PI_TTI, &tti }; UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTI) }, { FLDATA (DONE, dev_done, INT_V_TTI) }, { FLDATA (DISABLE, dev_disable, INT_V_TTI) }, { FLDATA (INT, int_req, INT_V_TTI) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB ttx_mod[] = { { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod }, { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod }, { 0 } } ; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, ttx_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { DEV_TTO, INT_TTO, PI_TTO, &tto }; UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTO) }, { FLDATA (DONE, dev_done, INT_V_TTO) }, { FLDATA (DISABLE, dev_disable, INT_V_TTO) }, { FLDATA (INT, int_req, INT_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, ttx_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, DEV_DISABLE }; /* Terminal input: IOT routine */ int32 tti (int32 pulse, int32 code, int32 AC) { int32 iodata; if (code == ioDIA) iodata = tti_unit.buf & 0377; else iodata = 0; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_TTI ) ; DEV_CLR_DONE( INT_TTI ) ; DEV_UPDATE_INTR ; break; case iopC: /* clear */ DEV_CLR_BUSY( INT_TTI ) ; DEV_CLR_DONE( INT_TTI ) ; DEV_UPDATE_INTR ; break; } /* end switch */ return iodata; } /* Unit service */ t_stat tti_svc (UNIT *uptr) { int32 temp; sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ tti_unit.buf = temp & 0177; if (tti_unit.flags & UNIT_DASHER) { if (tti_unit.buf == '\r') tti_unit.buf = '\n'; /* Dasher: cr -> nl */ else if (tti_unit.buf == '\n') tti_unit.buf = '\r' ; /* Dasher: nl -> cr */ } DEV_CLR_BUSY( INT_TTI ) ; DEV_SET_DONE( INT_TTI ) ; DEV_UPDATE_INTR ; ++(uptr->pos) ; return SCPE_OK; } /* Reset routine */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; /* */ DEV_CLR_BUSY( INT_TTI ) ; DEV_CLR_DONE( INT_TTI ) ; DEV_UPDATE_INTR ; sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) tto_unit.buf = AC & 0377; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_TTO ) ; DEV_CLR_DONE( INT_TTO ) ; DEV_UPDATE_INTR ; sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_TTO ) ; DEV_CLR_DONE( INT_TTO ) ; DEV_UPDATE_INTR ; sim_cancel (&tto_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = tto_unit.buf & 0177; if ((tto_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b'; if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK : r); /* !stall? report */ } DEV_CLR_BUSY( INT_TTO ) ; DEV_SET_DONE( INT_TTO ) ; DEV_UPDATE_INTR ; ++(tto_unit.pos); return SCPE_OK; } /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; /* */ DEV_CLR_BUSY( INT_TTO ) ; DEV_CLR_DONE( INT_TTO ) ; DEV_UPDATE_INTR ; sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | val; tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | val; return SCPE_OK; } simh-3.8.1/NOVA/nova_defs.h0000644000175000017500000003571211015044254013525 0ustar vlmvlm/* nova_defs.h: NOVA/Eclipse simulator definitions Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Jul-07 BKR BUSY/DONE/INTR "convenience" macros added, INT_TRAP added for Nova 3, 4 trap instruction handling, support for 3rd-party 64KW Nova extensions added, removed STOP_IND_TRP definition due to common INT/TRP handling 14-Jan-04 BKR Added support for QTY and ALM 22-Nov-03 CEO Added support for PIT device 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev kit conflict 03-Oct-02 RMS Added device information structure 22-Dec-00 RMS Added Bruce Ray's second terminal support 10-Dec-00 RMS Added Charles Owen's Eclipse support 08-Dec-00 RMS Added Bruce Ray's plotter support 15-Oct-00 RMS Added stack, byte, trap instructions 14-Apr-99 RMS Changed t_addr to unsigned 16-Mar-95 RMS Added dynamic memory size 06-Dec-95 RMS Added magnetic tape The author gratefully acknowledges the help of Tom West, Diana Englebart, Carl Friend, Bruce Ray, and Charles Owen in resolving questions about the NOVA. */ #ifndef _NOVA_DEFS_H_ #define _NOVA_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_IND 4 /* indirect loop */ #define STOP_IND_INT 5 /* ind loop, intr or trap */ /* Memory */ #if defined (ECLIPSE) /*----------------------*/ /* Eclipse */ /*----------------------*/ #define MAXMEMSIZE 1048576 /* max memory size in 16-bit words */ #define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ #define MEM_ADDR_OK(x) (((uint32) (x)) < (uint32) MEMSIZE) #else /*----------------------*/ /* Nova */ /*----------------------*/ #define MAXMEMSIZE 65536 /* max memory size in 16-bit words: 32KW = DG max, */ /* 64 KW = 3rd-party extended memory feature */ #define DFTMEMSIZE 32768 /* default/initial mem size */ #define MEM_ADDR_OK(x) (((uint32) (x)) < (uint32) MEMSIZE) #endif #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define A_V_IND 15 /* ind: indirect */ #define A_IND (1 << A_V_IND) /* Architectural constants */ #define SIGN 0100000 /* sign */ #define DMASK 0177777 /* data mask */ #define CBIT (DMASK + 1) /* carry bit */ #define CDMASK (CBIT | DMASK) /* carry + data */ /* Reserved memory locations */ #define INT_SAV 0 /* intr saved PC */ #define INT_JMP 1 /* intr jmp @ */ #define STK_JMP 3 /* stack jmp @ */ #define TRP_SAV 046 /* trap saved PC */ #define TRP_JMP 047 /* trap jmp @ */ #define AUTO_TOP 037 /* top of autoindex */ #define AUTO_DEC 030 /* start autodec */ #define AUTO_INC 020 /* start autoinc */ /* Instruction format */ #define I_OPR 0100000 /* operate */ #define I_M_SRC 03 /* OPR: src AC */ #define I_V_SRC 13 #define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC) #define I_M_DST 03 /* dst AC */ #define I_V_DST 11 #define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST) #define I_M_ALU 07 /* OPR: ALU op */ #define I_V_ALU 8 #define I_GETALU(x) (((x) >> I_V_ALU) & I_M_ALU) #define I_M_SHF 03 /* OPR: shift */ #define I_V_SHF 6 #define I_GETSHF(x) (((x) >> I_V_SHF) & I_M_SHF) #define I_M_CRY 03 /* OPR: carry */ #define I_V_CRY 4 #define I_GETCRY(x) (((x) >> I_V_CRY) & I_M_CRY) #define I_V_NLD 3 /* OPR: no load */ #define I_NLD (1 << I_V_NLD) #define I_M_SKP 07 /* OPR: skip */ #define I_V_SKP 0 #define I_GETSKP(x) (((x) >> I_V_SKP) & I_M_SKP) #define I_M_OPAC 017 /* MRF: opcode + AC */ #define I_V_OPAC 11 #define I_GETOPAC(x) (((x) >> I_V_OPAC) & I_M_OPAC) #define I_V_IND 10 /* MRF: indirect */ #define I_IND (1 << I_V_IND) #define I_M_MODE 03 /* MRF: mode */ #define I_V_MODE 8 #define I_GETMODE(x) (((x) >> I_V_MODE) & I_M_MODE) #define I_M_DISP 0377 /* MRF: disp */ #define I_V_DISP 0 #define I_GETDISP(x) (((x) >> I_V_DISP) & I_M_DISP) #define DISPSIZE (I_M_DISP + 1) /* page size */ #define DISPSIGN (DISPSIZE >> 1) /* page sign */ #define I_M_IOT 07 /* IOT: code */ #define I_V_IOT 8 #define I_GETIOT(x) (((x) >> I_V_IOT) & I_M_IOT) #define I_M_PULSE 03 /* IOT pulse */ #define I_V_PULSE 6 #define I_GETPULSE(x) (((x) >> I_V_PULSE) & I_M_PULSE) #define I_M_DEV 077 /* IOT: device */ #define I_V_DEV 0 #define I_GETDEV(x) (((x) >> I_V_DEV) & I_M_DEV) #define I_M_XOP 037 /* XOP: code */ #define I_V_XOP 6 #define I_GETXOP(x) (((x) >> I_V_XOP) & I_M_XOP) /* IOT return codes */ #define IOT_V_REASON 16 /* set reason */ #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* IOT fields */ #define ioNIO 0 /* opcode field */ #define ioDIA 1 #define ioDOA 2 #define ioDIB 3 #define ioDOB 4 #define ioDIC 5 #define ioDOC 6 #define ioSKP 7 #define iopN 0 /* pulse field */ #define iopS 1 #define iopC 2 #define iopP 3 /* Device numbers */ #define DEV_LOW 010 /* lowest intr dev */ #define DEV_HIGH 051 /* highest intr dev */ #define DEV_MDV 001 /* multiply/divide */ #define DEV_ECC 002 /* ECC memory control */ #define DEV_MAP 003 /* MMPU control */ #define DEV_TTI 010 /* console input */ #define DEV_TTO 011 /* console output */ #define DEV_PTR 012 /* paper tape reader */ #define DEV_PTP 013 /* paper tape punch */ #define DEV_CLK 014 /* clock */ #define DEV_PLT 015 /* plotter */ #define DEV_CDR 016 /* card reader */ #define DEV_LPT 017 /* line printer */ #define DEV_DSK 020 /* fixed head disk */ #define DEV_MTA 022 /* magtape */ #define DEV_DCM 024 /* data comm mux */ #define DEV_ADCV 030 /* A/D converter */ #define DEV_QTY 030 /* 4060 multiplexor */ #define DEV_DKP 033 /* disk pack */ #define DEV_CAS 034 /* cassette */ #define DEV_ALM 034 /* ALM/ULM multiplexor */ #define DEV_PIT 043 /* programmable interval timer */ #define DEV_TTI1 050 /* second console input */ #define DEV_TTO1 051 /* second console output */ #define DEV_CPU 077 /* CPU control */ /* I/O structure The NOVA I/O structure is tied together by dev_table, indexed by the device number. Each entry in dev_table consists of mask device mask for busy, done (simulator representation) pi pi disable bit (hardware representation) routine IOT action routine dev_table is populated at run time from the device information blocks in each device. */ struct ndev { int32 mask; /* done/busy mask */ int32 pi; /* assigned pi bit */ int32 (*routine)(); /* dispatch routine */ }; typedef struct { int32 dnum; /* device number */ int32 mask; /* done/busy mask */ int32 pi; /* assigned pi bit */ int32 (*routine)(); /* dispatch routine */ } DIB; /* Device flags (simulator representation) Priority (for INTA) runs from low numbers to high */ #define INT_V_PIT 2 /* PIT */ #define INT_V_DKP 3 /* moving head disk */ #define INT_V_DSK 4 /* fixed head disk */ #define INT_V_MTA 5 /* magnetic tape */ #define INT_V_LPT 6 /* line printer */ #define INT_V_CLK 7 /* clock */ #define INT_V_PTR 8 /* paper tape reader */ #define INT_V_PTP 9 /* paper tape punch */ #define INT_V_PLT 10 /* plotter */ #define INT_V_TTI 11 /* keyboard */ #define INT_V_TTO 12 /* terminal */ #define INT_V_TTI1 13 /* second keyboard */ #define INT_V_TTO1 14 /* second terminal */ #define INT_V_QTY 15 /* QTY multiplexor */ #define INT_V_ALM 16 /* ALM multiplexor */ #define INT_V_STK 17 /* stack overflow */ #define INT_V_NO_ION_PENDING 18 /* ion delay */ #define INT_V_ION 19 /* interrupts on */ #define INT_V_TRAP 20 /* trap instruction */ #define INT_PIT (1 << INT_V_PIT) #define INT_DKP (1 << INT_V_DKP) #define INT_DSK (1 << INT_V_DSK) #define INT_MTA (1 << INT_V_MTA) #define INT_LPT (1 << INT_V_LPT) #define INT_CLK (1 << INT_V_CLK) #define INT_PTR (1 << INT_V_PTR) #define INT_PTP (1 << INT_V_PTP) #define INT_PLT (1 << INT_V_PLT) #define INT_TTI (1 << INT_V_TTI) #define INT_TTO (1 << INT_V_TTO) #define INT_TTI1 (1 << INT_V_TTI1) #define INT_TTO1 (1 << INT_V_TTO1) #define INT_QTY (1 << INT_V_QTY) #define INT_ALM (1 << INT_V_ALM) #define INT_STK (1 << INT_V_STK) #define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) #define INT_ION (1 << INT_V_ION) #define INT_DEV ((1 << INT_V_STK) - 1) /* device ints */ #define INT_PENDING INT_ION+INT_NO_ION_PENDING #define INT_TRAP (1 << INT_V_TRAP) /* PI disable bits */ #define PI_PIT 0001000 #define PI_DKP 0000400 #define PI_DSK 0000100 #define PI_MTA 0000040 #define PI_LPT 0000010 #define PI_CLK 0000004 #define PI_PTR 0000020 #define PI_PTP 0000004 #define PI_PLT 0000010 #define PI_QTY 0000002 #define PI_ALM 0000002 #define PI_TTI 0000002 #define PI_TTO 0000001 #define PI_TTI1 PI_TTI #define PI_TTO1 PI_TTO /* #define PI_CDR 0000040 */ /* #define PI_DCM 0100000 */ /* #define PI_CAS 0000040 */ /* #define PI_ADCV 0000002 */ /* Macros to clear/set BUSY/DONE/INTR bits */ #define DEV_SET_BUSY( x ) dev_busy = dev_busy | (x) #define DEV_CLR_BUSY( x ) dev_busy = dev_busy & (~(x)) #define DEV_SET_DONE( x ) dev_done = dev_done | (x) #define DEV_CLR_DONE( x ) dev_done = dev_done & (~(x)) #define DEV_UPDATE_INTR int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable) #define DEV_IS_BUSY( x ) (dev_busy & (x)) #define DEV_IS_DONE( x ) (dev_done & (x)) /* Function prototypes */ int32 MapAddr (int32 map, int32 addr); t_stat set_enb (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat set_dsb (UNIT *uptr, int32 val, char *cptr, void *desc); #endif simh-3.8.1/NOVA/nova_plt.c0000644000175000017500000001245711007430256013401 0ustar vlmvlm/* nova_plt.c: NOVA plotter simulator Copyright (c) 2000-2008, Robert M. Supnik Written by Bruce Ray and used with his gracious permission. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. plt plotter 04-Jul-07 BKR added 7B/8B support (default is 8B), DEV_SET/CLR macros now used 25-Apr-03 RMS Revised for extended file support 03-Oct-02 RMS Added DIB 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Revised enable/disable support 26-Apr-01 RMS Added device enable/disable support Notes: - data masked to 7- or 8- bits, based on 7B or 8B, default is 8-bits - if register TIME is non-zero, then delay TIME events if , or seen - register POS show the current file position - register STOP_IOE determines return value issued if output to unattached PLT is attempted */ #include "nova_defs.h" extern int32 int_req, dev_busy, dev_done, dev_disable; int32 plt_stopioe = 0; /* stop on error */ DEVICE plt_dev; int32 plt (int32 pulse, int32 code, int32 AC); t_stat plt_svc (UNIT *uptr); t_stat plt_reset (DEVICE *dptr); /* 7 or 8 bit data mask support for either device */ #define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ #define UNIT_8B (1 << UNIT_V_8B) /* PLT data structures plt_dev PLT device descriptor plt_unit PLT unit descriptor plt_reg PLT register list */ DIB plt_dib = { DEV_PLT, INT_PLT, PI_PLT, &plt }; UNIT plt_unit = { /* 2007-May-30, bkr */ UDATA (&plt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_8B, 0), SERIAL_OUT_WAIT }; REG plt_reg[] = { { ORDATA (BUF, plt_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_PLT) }, { FLDATA (DONE, dev_done, INT_V_PLT) }, { FLDATA (DISABLE, dev_disable, INT_V_PLT) }, { FLDATA (INT, int_req, INT_V_PLT) }, { DRDATA (POS, plt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, plt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, plt_stopioe, 0) }, { NULL } }; MTAB plt_mod[] = /* 2007-May-30, bkr */ { { UNIT_8B, 0, "7b", "7B", NULL }, { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, { 0, 0, NULL, NULL, NULL } } ; DEVICE plt_dev = { "PLT", &plt_unit, plt_reg, plt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &plt_reset, NULL, NULL, NULL, &plt_dib, DEV_DISABLE }; /* plotter: IOT routine */ int32 plt (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) plt_unit.buf = AC & ((plt_unit.flags & UNIT_8B)? 0377 : 0177); switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_PLT ) ; DEV_CLR_DONE( INT_PLT ) ; DEV_UPDATE_INTR ; sim_activate (&plt_unit, plt_unit.wait); /* activate unit */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_PLT ) ; DEV_CLR_DONE( INT_PLT ) ; DEV_UPDATE_INTR ; sim_cancel (&plt_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat plt_svc (UNIT *uptr) { DEV_CLR_BUSY( INT_PLT ) ; DEV_SET_DONE( INT_PLT ) ; DEV_UPDATE_INTR ; if ((plt_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (plt_stopioe, SCPE_UNATT); if (putc (plt_unit.buf, plt_unit.fileref) == EOF) { perror ("PLT I/O error"); clearerr (plt_unit.fileref); return SCPE_IOERR; } ++(plt_unit.pos); return SCPE_OK; } /* Reset routine */ t_stat plt_reset (DEVICE *dptr) { plt_unit.buf = 0; /* */ DEV_CLR_BUSY( INT_PLT ) ; DEV_CLR_DONE( INT_PLT ) ; DEV_UPDATE_INTR ; sim_cancel (&plt_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/NOVA/nova_dkp.c0000644000175000017500000013171611112562172013360 0ustar vlmvlm/* nova_dkp.c: NOVA moving head disk simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dkp moving head disk 04-Jul-04 BKR device name changed to DG's DKP from DEC's DP, DEV_SET/CLR/INTR macro use started, fixed 'P' pulse code and secret quirks, added 6097 diag and size support, fixed losing unit drive type during unit change, tightened sector size determination calculations, controller DONE flag handling fixed, fixed cylinder overflow test error, seek error code fixed, restructured dkp_go() and dkp_svc() routines (for known future fixes needed), fixed DIA status calculation, fixed DKP read/write loop to properly emulate DG cylinder and sector overflows, added trace facility, changed 'stime' calculation to force delay time if no cylinders are crossed (this fixes some DG code that assumes disk seek takes some time), fixed boot code to match DG hardware standard 04-Jan-04 RMS Changed attach routine to use sim_fsize 28-Nov-03 CEO Boot from DP now puts device address in SR 24-Nov-03 CEO Added support for disk sizing on 6099/6103 19-Nov-03 CEO Corrected major DMA Mapping bug 25-Apr-03 RMS Revised autosizing 08-Oct-02 RMS Added DIB 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed FLG, CAPAC to arrays 26-Apr-01 RMS Added device enable/disable support 12-Dec-00 RMS Added Eclipse support from Charles Owen 15-Oct-00 RMS Editorial changes 14-Apr-99 RMS Changed t_addr to unsigned 15-Sep-97 RMS Fixed bug in DIB/DOB for new disks 15-Sep-97 RMS Fixed bug in cylinder extraction (found by Charles Owen) 10-Sep-97 RMS Fixed bug in error reporting (found by Charles Owen) 25-Nov-96 RMS Defaulted to autosize 29-Jun-96 RMS Added unit disable support */ #include "nova_defs.h" #define DKP_NUMDR 4 /* #drives */ #define DKP_NUMWD 256 /* words/sector */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_M_DTYPE 017 #define UNIT_V_AUTO (UNIT_V_UF + 5) /* autosize */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define FUNC u3 /* function */ #define CYL u4 /* on cylinder */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* Unit, surface, sector, count register Original format: 2b, 6b, 4b, 4b Revised format: 2b, 5b, 5b, 4b */ #define USSC_V_COUNT 0 /* count */ #define USSC_M_COUNT 017 #define USSC_V_OSECTOR 4 /* old: sector */ #define USSC_M_OSECTOR 017 #define USSC_V_OSURFACE 8 /* old: surface */ #define USSC_M_OSURFACE 077 #define USSC_V_NSECTOR 4 /* new: sector */ #define USSC_M_NSECTOR 037 #define USSC_V_NSURFACE 9 /* new: surface */ #define USSC_M_NSURFACE 037 #define USSC_V_UNIT 14 /* unit */ #define USSC_M_UNIT 03 #define USSC_UNIT (USSC_M_UNIT << USSC_V_UNIT) #define GET_COUNT(x) (((x) >> USSC_V_COUNT) & USSC_M_COUNT) #define GET_SECT(x,dt) ((drv_tab[dt].newf)? \ (((x) >> USSC_V_NSECTOR) & USSC_M_NSECTOR): \ (((x) >> USSC_V_OSECTOR) & USSC_M_OSECTOR) ) #define GET_SURF(x,dt) ((drv_tab[dt].newf)? \ (((x) >> USSC_V_NSURFACE) & USSC_M_NSURFACE): \ (((x) >> USSC_V_OSURFACE) & USSC_M_OSURFACE) ) #define GET_UNIT(x) (((x) >> USSC_V_UNIT) & USSC_M_UNIT) /* Flags, command, cylinder register Original format: 5b, 2b, 1b + 8b (surrounding command) Revised format: 5b, 2b, 9b */ #define FCCY_V_OCYL 0 /* old: cylinder */ #define FCCY_M_OCYL 0377 #define FCCY_V_OCMD 8 /* old: command */ #define FCCY_M_OCMD 3 #define FCCY_V_OCEX 10 /* old: cyl extend */ #define FCCY_OCEX (1 << FCCY_V_OCEX) #define FCCY_V_NCYL 0 /* new: cylinder */ #define FCCY_M_NCYL 0777 #define FCCY_V_NCMD 9 /* new: command */ #define FCCY_M_NCMD 3 #define FCCY_READ 0 #define FCCY_WRITE 1 #define FCCY_SEEK 2 #define FCCY_RECAL 3 #define FCCY_FLAGS 0174000 /* flags */ #define GET_CMD(x,dt) ((drv_tab[dt].newf)? \ (((x) >> FCCY_V_NCMD) & FCCY_M_NCMD): \ (((x) >> FCCY_V_OCMD) & FCCY_M_OCMD) ) #define SET_CMD(x,dt) dkp_fccy = (dkp_fccy & ((drv_tab[dt].newf)? \ (FCCY_M_NCMD << FCCY_V_NCMD) : (FCCY_M_OCMD << FCCY_V_OCMD))) | \ ((drv_tab[dt].newf)? \ (((x) & FCCY_M_NCMD) << FCCY_V_NCMD): \ (((x) & FCCY_M_OCMD) << FCCY_V_OCMD) ) #define GET_CYL(x,dt) ((drv_tab[dt].newf)? \ (((x) >> FCCY_V_NCYL) & FCCY_M_NCYL): \ ((((x) >> FCCY_V_OCYL) & FCCY_M_OCYL) | \ ((dt != TYPE_D44)? 0: \ (((x) & FCCY_OCEX) >> (FCCY_V_OCEX - FCCY_V_OCMD)))) ) /* (Warning: no sector or surface masking is done!) */ #define DKP_UPDATE_USSC( type, count, surf, sect ) \ dkp_ussc = (dkp_ussc & USSC_UNIT) \ | ((dkp_ussc + count) & USSC_M_COUNT) \ | ((drv_tab[dtype].newf)? \ ((surf << USSC_V_NSURFACE) | (sect << USSC_V_NSECTOR)): \ ((surf << USSC_V_OSURFACE) | (sect << USSC_V_OSECTOR)) \ ); /* Status */ #define STA_ERR 0000001 /* error */ #define STA_DLT 0000002 /* data late */ #define STA_CRC 0000004 /* crc error */ #define STA_UNS 0000010 /* unsafe */ #define STA_XCY 0000020 /* cross cylinder */ #define STA_CYL 0000040 /* nx cylinder */ #define STA_DRDY 0000100 /* drive ready */ #define STA_SEEK3 0000200 /* seeking unit 3 */ #define STA_SEEK2 0000400 /* seeking unit 2 */ #define STA_SEEK1 0001000 /* seeking unit 1 */ #define STA_SEEK0 0002000 /* seeking unit 0 */ #define STA_SKDN3 0004000 /* seek done unit 3 */ #define STA_SKDN2 0010000 /* seek done unit 2 */ #define STA_SKDN1 0020000 /* seek done unit 1 */ #define STA_SKDN0 0040000 /* seek done unit 0 */ #define STA_DONE 0100000 /* operation done */ #define STA_DYN (STA_DRDY | STA_CYL) /* set from unit */ #define STA_EFLGS (STA_ERR | STA_DLT | STA_CRC | STA_UNS | \ STA_XCY | STA_CYL) /* error flags */ #define STA_DFLGS (STA_DONE | STA_SKDN0 | STA_SKDN1 | \ STA_SKDN2 | STA_SKDN3) /* done flags */ #define GET_SA(cy,sf,sc,t) (((((cy)*drv_tab[t].surf)+(sf))* \ drv_tab[t].sect)+(sc)) /* This controller supports many different disk drive types: type #sectors/ #surfaces/ #cylinders/ new format? surface cylinder drive floppy 8 1 77 no DS/DD floppy 16 2 77 yes (6097 "quad floppy") Diablo 31 12 2 203 no 6225 20 2 245 yes Century 111 6 10 203 no 4048 (same as Century 111) Diablo 44 12 4 408 no 6099 32 4 192 yes 6227 20 6 245 yes 6070 24 4 408 yes Century 114 12 20 203 no 4057 (same as Century 114) 6103 32 8 192 yes 4231 23 19 411 yes In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. */ #define TYPE_FLP 0 #define SECT_FLP 8 #define SURF_FLP 1 #define CYL_FLP 77 #define SIZE_FLP (SECT_FLP * SURF_FLP * CYL_FLP * DKP_NUMWD) #define NFMT_FLP FALSE #define TYPE_DSDD 1 #define TYPE_6097 TYPE_DSDD #define SECT_DSDD 16 #define SURF_DSDD 2 #define CYL_DSDD 77 #define SIZE_DSDD (SECT_DSDD * SURF_DSDD * CYL_DSDD * DKP_NUMWD) #define NFMT_DSDD TRUE #define TYPE_D31 2 #define SECT_D31 12 #define SURF_D31 2 #define CYL_D31 203 #define SIZE_D31 (SECT_D31 * SURF_D31 * CYL_D31 * DKP_NUMWD) #define NFMT_D31 FALSE #define TYPE_6225 3 #define SECT_6225 20 #define SURF_6225 2 #define CYL_6225 245 #define SIZE_6225 (SECT_6225 * SURF_6225 * CYL_6225 * DKP_NUMWD) #define NFMT_6225 TRUE #define TYPE_C111 4 #define SECT_C111 6 #define SURF_C111 10 #define CYL_C111 203 #define SIZE_C111 (SECT_C111 * SURF_C111 * CYL_C111 * DKP_NUMWD) #define NFMT_C111 FALSE #define TYPE_D44 5 #define SECT_D44 12 #define SURF_D44 4 #define CYL_D44 408 #define SIZE_D44 (SECT_D44 * SURF_D44 * CYL_D44 * DKP_NUMWD) #define NFMT_D44 FALSE #define TYPE_6099 6 #define SECT_6099 32 #define SURF_6099 4 #define CYL_6099 192 #define SIZE_6099 (SECT_6099 * SURF_6099 * CYL_6099 * DKP_NUMWD) #define NFMT_6099 TRUE #define TYPE_6227 7 #define SECT_6227 20 #define SURF_6227 6 #define CYL_6227 245 #define SIZE_6227 (SECT_6227 * SURF_6227 * CYL_6227 * DKP_NUMWD) #define NFMT_6227 TRUE #define TYPE_6070 8 #define SECT_6070 24 #define SURF_6070 4 #define CYL_6070 408 #define SIZE_6070 (SECT_6070 * SURF_6070 * CYL_6070 * DKP_NUMWD) #define NFMT_6070 TRUE #define TYPE_C114 9 #define SECT_C114 12 #define SURF_C114 20 #define CYL_C114 203 #define SIZE_C114 (SECT_C114 * SURF_C114 * CYL_C114 * DKP_NUMWD) #define NFMT_C114 FALSE #define TYPE_6103 10 #define SECT_6103 32 #define SURF_6103 8 #define CYL_6103 192 #define SIZE_6103 (SECT_6103 * SURF_6103 * CYL_6103 * DKP_NUMWD) #define NFMT_6103 TRUE #define TYPE_4231 11 #define SECT_4231 23 #define SURF_4231 19 #define CYL_4231 411 #define SIZE_4231 (SECT_4231 * SURF_4231 * CYL_4231 * DKP_NUMWD) #define NFMT_4231 TRUE struct drvtyp { int32 sect; /* sectors */ int32 surf; /* surfaces */ int32 cyl; /* cylinders */ int32 size; /* #blocks */ int32 newf; /* new format flag */ }; struct drvtyp drv_tab[] = { { SECT_FLP, SURF_FLP, CYL_FLP, SIZE_FLP, NFMT_FLP }, { SECT_DSDD, SURF_DSDD, CYL_DSDD, SIZE_DSDD, NFMT_DSDD }, { SECT_D31, SURF_D31, CYL_D31, SIZE_D31, NFMT_D31 }, { SECT_6225, SURF_6225, CYL_6225, SIZE_6225, NFMT_6225 }, { SECT_C111, SURF_C111, CYL_C111, SIZE_C111, NFMT_C111 }, { SECT_D44, SURF_D44, CYL_D44, SIZE_D44, NFMT_D44 }, { SECT_6099, SURF_6099, CYL_6099, SIZE_6099, NFMT_6099 }, { SECT_6227, SURF_6227, CYL_6227, SIZE_6227, NFMT_6227 }, { SECT_6070, SURF_6070, CYL_6070, SIZE_6070, NFMT_6070 }, { SECT_C114, SURF_C114, CYL_C114, SIZE_C114, NFMT_C114 }, { SECT_6103, SURF_6103, CYL_6103, SIZE_6103, NFMT_6103 }, { SECT_4231, SURF_4231, CYL_4231, SIZE_4231, NFMT_4231 }, { 0 } }; #define DKP_TRACE(x) (dkp_trace & (1<<(x))) #define DKP_TRACE_FP stderr /* current trace bit use (bit 0 = LSB) 0 I/O instructions 1 pre-seek/read/write event setup 2 seek events 3 read/write events 4 post read/write events */ extern uint16 M[]; extern UNIT cpu_unit; extern int32 int_req, dev_busy, dev_done, dev_disable; extern int32 saved_PC, SR, AMASK; int32 dkp_ma = 0; /* memory address */ int32 dkp_map = 0; /* DCH map 0=A 3=B */ int32 dkp_ussc = 0; /* unit/sf/sc/cnt */ int32 dkp_fccy = 0; /* flags/cylinder */ int32 dkp_sta = 0; /* status register */ int32 dkp_swait = 100; /* seek latency */ int32 dkp_rwait = 100; /* rotate latency */ int32 dkp_diagmode = 0; /* diagnostic mode */ int32 dkp_trace = 0 ; DEVICE dkp_dev; int32 dkp (int32 pulse, int32 code, int32 AC); t_stat dkp_svc (UNIT *uptr); t_stat dkp_reset (DEVICE *dptr); t_stat dkp_boot (int32 unitno, DEVICE *dptr); t_stat dkp_attach (UNIT *uptr, char *cptr); t_stat dkp_go ( int32 pulse ); t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* DKP data structures dkp_dev DKP device descriptor dkp_unit DKP unit list dkp_reg DKP register list dkp_mod DKP modifier list */ DIB dkp_dib = { DEV_DKP, INT_DKP, PI_DKP, &dkp }; UNIT dkp_unit[] = { { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) } }; REG dkp_reg[] = { { ORDATA (FCCY, dkp_fccy, 16) }, { ORDATA (USSC, dkp_ussc, 16) }, { ORDATA (STA, dkp_sta, 16) }, { ORDATA (MA, dkp_ma, 16) }, { FLDATA (INT, int_req, INT_V_DKP) }, { FLDATA (BUSY, dev_busy, INT_V_DKP) }, { FLDATA (DONE, dev_done, INT_V_DKP) }, { FLDATA (DISABLE, dev_disable, INT_V_DKP) }, { FLDATA (DIAG, dkp_diagmode, 0) }, { DRDATA (TRACE, dkp_trace, 32) }, { ORDATA (MAP, dkp_map, 2) }, { DRDATA (STIME, dkp_swait, 24), PV_LEFT }, { DRDATA (RTIME, dkp_rwait, 24), PV_LEFT }, { URDATA (CAPAC, dkp_unit[0].capac, 10, T_ADDR_W, 0, DKP_NUMDR, PV_LEFT | REG_HRO) }, { NULL } }; MTAB dkp_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE) + UNIT_ATT, "6030 (floppy)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE) + UNIT_ATT, "6097 (DS/DD floppy)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE) + UNIT_ATT, "4047 (Diablo 31)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE) + UNIT_ATT, "4234/6045 (Diablo 44)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE) + UNIT_ATT, "4048 (Century 111)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE) + UNIT_ATT, "2314/4057 (Century 114)", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE) + UNIT_ATT, "6225", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE) + UNIT_ATT, "6227", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE) + UNIT_ATT, "6099", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE) + UNIT_ATT, "6103", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE) + UNIT_ATT, "6070", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE) + UNIT_ATT, "4231/3330", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE), "6030 (floppy)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE), "6097 (DS/DD floppy)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE), "4047 (Diablo 31)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE), "4234/6045 (Diablo 44)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE), "4048 (Century 111)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE), "2314/4057 (Century 114)", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE), "6225", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE), "6227", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE), "6099", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE), "6103", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE), "6070", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE), "4231/3330", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), NULL, "FLOPPY", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), NULL, "6030", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), NULL, "DSDDFLOPPY", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), NULL, "6097", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), NULL, "D31", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), NULL, "4047", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), NULL, "D44", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), NULL, "4234", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), NULL, "6045", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), NULL, "C111", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), NULL, "4048", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), NULL, "C114", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), NULL, "2314", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), NULL, "4057", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6225 << UNIT_V_DTYPE), NULL, "6225", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6227 << UNIT_V_DTYPE), NULL, "6227", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6099 << UNIT_V_DTYPE), NULL, "6099", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6103 << UNIT_V_DTYPE), NULL, "6103", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6070 << UNIT_V_DTYPE), NULL, "6070", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), NULL, "4231", &dkp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), NULL, "3330", &dkp_set_size }, { 0 } }; DEVICE dkp_dev = { "DKP", dkp_unit, dkp_reg, dkp_mod, DKP_NUMDR, 8, 30, 1, 8, 16, NULL, NULL, &dkp_reset, &dkp_boot, &dkp_attach, NULL, &dkp_dib, DEV_DISABLE }; /* IOT routine */ int32 dkp (int32 pulse, int32 code, int32 AC) { UNIT *uptr; int32 u, rval, dtype; rval = 0; uptr = dkp_dev.units + GET_UNIT (dkp_ussc); /* select unit */ dtype = GET_DTYPE (uptr->flags); /* get drive type */ if ( DKP_TRACE(0) ) { static char * f[8] = { "NIO", "DIA", "DOA", "DIB", "DOB", "DIC", "DOC", "SKP" } ; static char * s[4] = { " ", "S", "C", "P" } ; printf( " [DKP %s%s %06o ", f[code & 0x07], s[pulse & 0x03], (AC & 0xFFFF) ) ; } switch (code) { /* decode IR<5:7> */ case ioDIA: /* DIA */ dkp_sta = dkp_sta & (~STA_DRDY) ; /* keep error flags */ if (uptr->flags & UNIT_ATT) /* update ready */ dkp_sta = dkp_sta | STA_DRDY; if (uptr->CYL >= drv_tab[dtype].cyl) dkp_sta = dkp_sta | STA_CYL; /* bad cylinder? */ if (dkp_sta & STA_EFLGS) dkp_sta = dkp_sta | STA_ERR; rval = dkp_sta; break; case ioDOA: /* DOA */ if (AC & 0100000) /* clear rw done? */ dkp_sta = dkp_sta & ~(STA_CYL|STA_XCY|STA_UNS|STA_CRC); if ((dev_busy & INT_DKP) == 0) { dkp_fccy = AC; /* save cmd, cyl */ dkp_sta = dkp_sta & ~(AC & FCCY_FLAGS); } DEV_CLR_DONE( INT_DKP ) ; /* assume done flags 0 */ if ( dkp_sta & STA_DFLGS ) /* done flags = 0? */ DEV_SET_DONE( INT_DKP ) ; /* nope - set done */ DEV_UPDATE_INTR ; /* update intr */ break; case ioDIB: /* DIB */ rval = dkp_ma & 077777 ; /* return buf addr */ /* with B0 clear (no DCH B map support) */ break; case ioDOB: /* DOB */ if ((dev_busy & INT_DKP) == 0) { dkp_ma = AC & (drv_tab[dtype].newf? DMASK: AMASK); if (AC & 0100000) dkp_map = 3; /* high bit is map */ else dkp_map = 0; } break; case ioDIC: /* DIC */ rval = dkp_ussc; /* return unit, sect */ break; case ioDOC: /* DOC */ if ((dev_busy & INT_DKP) == 0) /* if device is not busy */ dkp_ussc = AC ; /* save unit, sect */ if (((dtype == TYPE_6099) || /* (BKR: don't forget 6097) */ (dtype == TYPE_6097) || /* for 6099 and 6103 */ (dtype == TYPE_6103)) && /* if data<0> set, */ (AC & 010000) ) dkp_diagmode = 1; /* set diagnostic mode */ break; } /* end switch code */ u = GET_UNIT(dkp_ussc); /* update current unit */ uptr = dkp_dev.units + u ; /* select unit */ dtype = GET_DTYPE (uptr->flags); /* get drive type */ if ( DKP_TRACE(0) ) { if ( code & 1 ) printf( " [%06o] ", (rval & 0xFFFF) ) ; printf( "] \n" ) ; } switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_DKP ) ; /* set busy */ DEV_CLR_DONE( INT_DKP ) ; /* clear done */ DEV_UPDATE_INTR ; /* update ints */ if (dkp_diagmode) { /* in diagnostic mode? */ dkp_diagmode = 0; /* reset it */ if (dtype == TYPE_6097) /* (BKR - quad floppy) */ dkp_ussc = 010001; if (dtype == TYPE_6099) /* return size bits */ dkp_ussc = 010002; if (dtype == TYPE_6103) /* for certain types */ dkp_ussc = 010003; } else { /* normal mode ... */ if (dkp_go (pulse)) /* do command */ break ; /* break if no error */ } DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ DEV_SET_DONE( INT_DKP ) ; /* set done */ DEV_UPDATE_INTR ; /* update ints */ dkp_sta = dkp_sta | STA_DONE; /* set controller done */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ DEV_CLR_DONE( INT_DKP ) ; /* set done */ DEV_UPDATE_INTR ; /* update ints */ dkp_sta = dkp_sta & ~(STA_DFLGS + STA_EFLGS); /* clear controller flags */ if (dkp_unit[u].FUNC != FCCY_SEEK) sim_cancel (&dkp_unit[u]); /* cancel any r/w op */ break; case iopP: /* pulse */ if ( dkp_diagmode ) { dkp_diagmode = 0 ; /* clear DG diagnostic mode */ } else { DEV_CLR_DONE( INT_DKP ) ; /* clear done */ DEV_UPDATE_INTR ; /* DG "undocumented feature": 'P' pulse can not start a read/write operation! * Diagnostic routines will use this crock to do 'crazy things' to size a disk * and many assume that a recal is done, other assume that they can stop the * read operation before any damage is done. Must also [re]calculate unit, function * and type because DOx instruction may have updated the controller info after * start of this procedure and before our 'P' handler. BKR */ if (dkp_go(pulse)) break; /* no error - do not set done and status */ } DEV_SET_DONE( INT_DKP ) ; /* set done */ DEV_UPDATE_INTR ; /* update ints */ dkp_sta = dkp_sta | (STA_SKDN0 >> u); /* set controller seek done */ break; } /* end case pulse */ return rval; } /* New command, start vs pulse handled externally Returns true if command ok, false if error */ t_stat dkp_go ( int32 pulse ) { UNIT * uptr; int32 oldCyl, u, dtype; dkp_sta = dkp_sta & ~STA_EFLGS; /* clear errors */ u = GET_UNIT (dkp_ussc); /* get unit number */ uptr = dkp_dev.units + u; /* get unit */ if (((uptr->flags & UNIT_ATT) == 0) || sim_is_active (uptr)) { dkp_sta = dkp_sta | STA_ERR; /* attached or busy? */ return FALSE; } if (dkp_diagmode) { /* diagnostic mode? */ dkp_sta = (dkp_sta | STA_DONE); /* Set error bit only */ DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ DEV_SET_DONE( INT_DKP ) ; /* set done */ DEV_UPDATE_INTR ; /* update interrupts */ return ( TRUE ) ; /* do not do function */ } oldCyl = uptr->CYL ; /* get old cylinder */ dtype = GET_DTYPE (uptr->flags); /* get drive type */ uptr->FUNC = GET_CMD (dkp_fccy, dtype) ; /* save command */ uptr->CYL = GET_CYL (dkp_fccy, dtype) ; if ( DKP_TRACE(1) ) { int32 xSect ; int32 xSurf ; int32 xCyl ; int32 xCnt ; xSect = GET_SECT(dkp_ussc, dtype) ; xSurf = GET_SURF(dkp_ussc, dtype) ; xCyl = GET_CYL (dkp_fccy, dtype) ; xCnt = 16 - (GET_COUNT(dkp_ussc)) ; fprintf( DKP_TRACE_FP, " [%s:%c %-5s: %3d / %2d / %2d %2d %06o ] \r\n", "DKP", (char) (u + '0'), ((uptr->FUNC == FCCY_READ) ? "read" : ((uptr->FUNC == FCCY_WRITE) ? "write" : ((uptr->FUNC == FCCY_SEEK) ? "seek" : "" ) ) ), (unsigned) xCyl, (unsigned) xSurf, (unsigned) xSect, (unsigned) (16 - xCnt), (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ ) ; } switch (uptr->FUNC) { /* decode command */ case FCCY_READ: case FCCY_WRITE: if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ ((uptr->flags & UNIT_WPRT) && (uptr->FUNC == FCCY_WRITE))) { dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ } else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_CYL ; } else if ( GET_SURF(dkp_ussc, dtype) >= drv_tab[dtype].surf ) /* bad surface */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /* older drives may not even do this... */ /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /- newer disks give this error */ } else if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ { /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older drives may not even do this... */ dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ } if ( (pulse != iopS) || (dkp_sta & STA_ERR) ) { return ( FALSE ) ; } sim_activate (uptr, dkp_rwait); /* schedule read or write request */ break; case FCCY_RECAL: /* recalibrate */ uptr->FUNC = FCCY_SEEK ; /* save command */ uptr->CYL = 0 ; case FCCY_SEEK: /* seek */ if ( ! (uptr->flags & UNIT_ATT) ) /* not attached? */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ } else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder? */ { dkp_sta = dkp_sta | STA_ERR | STA_CYL; } if ( (pulse != iopP) || (dkp_sta & STA_ERR) ) { return ( FALSE ) ; /* only 'P' pulse start seeks! */ } /* do the seek */ /* must check for "do we support seeking bits" flag before setting SEEK0'ish bits! */ dkp_sta = dkp_sta | (STA_SEEK0 >> u); /* set seeking */ oldCyl = abs(oldCyl - uptr->CYL) ; if ( (dkp_swait) && (! (oldCyl)) ) /* enforce minimum wait if req */ oldCyl = 1 ; sim_activate ( uptr, (dkp_swait * oldCyl) ) ; break; } /* end case command */ return ( TRUE ) ; /* no error */ } /* Unit service If seek done, put on cylinder; else, do read or write If controller was busy, clear busy, set done, interrupt Memory access: sectors are read into/written from an intermediate buffer to allow word-by-word mapping of memory addresses on the Eclipse. This allows each word written to memory to be tested for out of range. */ t_stat dkp_svc (UNIT *uptr) { int32 sa, bda; int32 dx, pa, u; int32 dtype, err, newsect, newsurf; uint32 awc; t_stat rval; static uint16 tbuf[DKP_NUMWD]; /* transfer buffer */ rval = SCPE_OK; dtype = GET_DTYPE (uptr->flags); /* get drive type */ u = uptr - dkp_dev.units; /* get unit number */ if (uptr->FUNC == FCCY_SEEK) { /* seek? */ if ( ! (uptr->flags & UNIT_ATT) ) /* not attached? */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error (changed during queue time?) */ } else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder? */ { dkp_sta = dkp_sta | STA_ERR | STA_CYL; } DEV_SET_DONE( INT_DKP ) ; DEV_UPDATE_INTR ; dkp_sta = (dkp_sta | (STA_SKDN0 >> u)) /* set seek done */ & ~(STA_SEEK0 >> u); /* clear seeking */ if ( DKP_TRACE(2) ) { fprintf( DKP_TRACE_FP, " [%s:%c seek : %4d ] \r\n", "DKP", (char) (u + '0'), (unsigned) (uptr->CYL) ) ; } return SCPE_OK; } /* read or write */ if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ ((uptr->flags & UNIT_WPRT) && (uptr->FUNC == FCCY_WRITE))) { dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ } else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_CYL ; dkp_sta = dkp_sta | STA_ERR | STA_CYL; DEV_SET_DONE( INT_DKP ) ; DEV_UPDATE_INTR ; return SCPE_OK ; } else if ( GET_SURF(dkp_ussc, dtype) >= drv_tab[dtype].surf ) /* bad surface */ { dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /* older drives may not even do this... */ /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /- newer disks give this error */ /* set sector to some bad value and wait then exit? */ } else if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ { /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older DG drives do not even give error(!), but we do */ dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ } else { err = 0 ; do { if ( DKP_TRACE(3) ) { fprintf( DKP_TRACE_FP, " [%s:%c %-5s: %3d / %2d / %2d %06o ] \r\n", "DKP", (char) (u + '0'), ((uptr->FUNC == FCCY_READ) ? "read" : ((uptr->FUNC == FCCY_WRITE) ? "write" : "") ), (unsigned) (uptr->CYL), (unsigned) (GET_SURF(dkp_ussc, dtype)), (unsigned) (GET_SECT(dkp_ussc, dtype)), (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ ) ; } if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ { /* sector overflows to 0 ; * surface gets incremented */ newsurf = GET_SURF(dkp_ussc, dtype) + 1 ; newsurf = newsurf & ((drv_tab[dtype].newf) ? USSC_M_NSURFACE : USSC_M_OSURFACE) ; DKP_UPDATE_USSC( type, 0, newsurf, 0 ) if ( (GET_SURF(dkp_ussc, dtype)) >= drv_tab[dtype].surf ) { /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older drives may not even do this... */ dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ /* DG retains overflowed surface number, * other vendors have different/expanded options */ break ; } } sa = GET_SA (uptr->CYL, GET_SURF (dkp_ussc, dtype), GET_SECT (dkp_ussc, dtype), dtype); /* get disk block */ bda = sa * DKP_NUMWD * sizeof(uint16) ; /* to words, bytes */ err = fseek (uptr->fileref, bda, SEEK_SET); /* position drive */ if (uptr->FUNC == FCCY_READ) { /* read? */ awc = fxread (tbuf, sizeof(uint16), DKP_NUMWD, uptr->fileref); for ( ; awc < DKP_NUMWD; awc++) tbuf[awc] = 0; if (err = ferror (uptr->fileref)) break; for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop thru buffer */ pa = MapAddr (dkp_map, (dkp_ma & AMASK)); if (MEM_ADDR_OK (pa)) M[pa] = tbuf[dx]; dkp_ma = (dkp_ma + 1) & AMASK; } } else if (uptr->FUNC == FCCY_WRITE) { /* write? */ for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop into buffer */ pa = MapAddr (dkp_map, (dkp_ma & AMASK)); tbuf[dx] = M[pa]; dkp_ma = (dkp_ma + 1) & AMASK; } fxwrite (tbuf, sizeof(int16), DKP_NUMWD, uptr->fileref); if (err = ferror (uptr->fileref)) break; } if (err != 0) { perror ("DKP I/O error"); clearerr (uptr->fileref); rval = SCPE_IOERR; break ; } newsect = GET_SECT (dkp_ussc, dtype) + 1 ; /* update next sector */ newsurf = GET_SURF (dkp_ussc, dtype) ; /* and next head */ /* (count set below) */ DKP_UPDATE_USSC( type, 1, newsurf, newsect ) } /* end read/write loop */ while ( (GET_COUNT(dkp_ussc)) ) ; dkp_sta = dkp_sta | STA_DONE; /* set status */ if ( DKP_TRACE(4) ) { fprintf( DKP_TRACE_FP, " [%s:%c %-5s: %3d / %2d / %2d %06o ] \r\n", "DKP", (char) (u + '0'), "post", (unsigned) (uptr->CYL), (unsigned) (GET_SURF(dkp_ussc, dtype)), (unsigned) (GET_SECT(dkp_ussc, dtype)), (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ ) ; } } DEV_CLR_BUSY( INT_DKP ) ; DEV_SET_DONE( INT_DKP ) ; DEV_UPDATE_INTR ; return rval; } /* Reset routine */ t_stat dkp_reset (DEVICE *dptr) { int32 u; UNIT *uptr; DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ DEV_CLR_DONE( INT_DKP ) ; /* clear done */ DEV_UPDATE_INTR ; /* update ints */ dkp_fccy = dkp_ussc = dkp_ma = dkp_sta = 0; /* clear registers */ dkp_diagmode = 0; /* clear diagnostic mode */ dkp_map = 0; for (u = 0; u < DKP_NUMDR; u++) { /* loop thru units */ uptr = dkp_dev.units + u; sim_cancel (uptr); /* cancel activity */ uptr->CYL = uptr->FUNC = 0; } return SCPE_OK; } /* Attach routine (with optional autosizing) */ t_stat dkp_attach (UNIT *uptr, char *cptr) { int32 i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; /* restore capac */ r = attach_unit (uptr, cptr); /* attach */ if ((r != SCPE_OK) || !(uptr->flags & UNIT_AUTO)) return r; if ((p = sim_fsize (uptr->fileref)) == 0) /* get file size */ return SCPE_OK; for (i = 0; drv_tab[i].sect != 0; i++) { if (p <= (drv_tab[i].size * (int32) sizeof (uint16))) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Set size command validation routine */ t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[GET_DTYPE (val)].size; return SCPE_OK; } /* Bootstrap routine */ #if defined(_OLD_CODE_) #define BOOT_START 02000 #define BOOT_UNIT 02021 #define BOOT_SEEK 02022 #define BOOT_LEN (sizeof(boot_rom) / sizeof(int32)) static const int32 boot_rom[] = { 0060233, /* NIOC 0,DKP ; clear disk */ 0020420, /* LDA 0,USSC ; unit, sfc, sec, cnt */ 0063033, /* DOC 0,DKP ; select disk */ 0020417, /* LDA 0,SEKCMD ; command, cylinder */ 0061333, /* DOAP 0,DKP ; start seek */ 0024415, /* LDA 1,SEKDN */ 0060433, /* DIA 0,DKP ; get status */ 0123415, /* AND# 1,0,SZR ; skip if done */ 0000776, /* JMP .-2 */ 0102400, /* SUB 0,0 ; mem addr = 0 */ 0062033, /* DOB 0,DKP */ 0020411, /* LDA 0,REDCMD ; command, cylinder */ 0061133, /* DOAS 0,DKP ; start read */ 0060433, /* DIA 0, DKP ; get status */ 0101113, /* MOVL# 0,0,SNC ; skip if done */ 0000776, /* JMP .-2 */ 0000377, /* JMP 377 */ 0000016, /* USSC: 0.B1+0.B7+0.B11+16 */ 0175000, /* SEKCMD: 175000 */ 0074000, /* SEKDN: 074000 */ 0174000 /* REDCMD: 174000 */ }; t_stat dkp_boot (int32 unitno, DEVICE *dptr) { int32 i, dtype; extern int32 saved_PC, SR; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; unitno = unitno & USSC_M_UNIT; dtype = GET_DTYPE (dkp_unit[unitno].flags); M[BOOT_UNIT] = M[BOOT_UNIT] | (unitno << USSC_V_UNIT); if (drv_tab[dtype].newf) M[BOOT_SEEK] = 0176000; saved_PC = BOOT_START; SR = 0100000 + DEV_DKP; return SCPE_OK; } #endif /* _OLD_CODE_ */ #define BOOT_START 0375 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) static const int32 boot_rom[] = { 0062677 /* IORST ; reset the I/O system */ , 0060133 /* NIOS DKP ; start the disk */ , 0000377 /* JMP 377 ; wait for the world */ } ; t_stat dkp_boot (int32 unitno, DEVICE *dptr) { int32 i; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = (uint16) boot_rom[i]; saved_PC = BOOT_START; SR = 0100000 + DEV_DKP; return SCPE_OK; } simh-3.8.1/NOVA/nova_lp.c0000644000175000017500000001172511005405230013202 0ustar vlmvlm/* nova_lp.c: NOVA line printer simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt line printer 04-Jul-07 BKR DEV_SET/CLR macros now used, , , output character delay now contingent upon non-zero TIME value, LPT can now be DISABLED 19-Jan-07 RMS Added UNIT_TEXT 25-Apr-03 RMS Revised for extended file support 30-May-02 RMS Widened POS to 32b Notes: - data currently masked to 7 bits. - if register TIME is non-zero, then delay TIME events if , or seen - register POS show the current file position - register STOP_IOE determines return value issued if output to unattached LPT is attempted */ #include "nova_defs.h" extern int32 int_req, dev_busy, dev_done, dev_disable; int32 lpt_stopioe = 0; /* stop on error flag */ int32 lpt (int32 pulse, int32 code, int32 AC); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { DEV_LPT, INT_LPT, PI_LPT, &lpt }; UNIT lpt_unit = { /* 2007-May-30, bkr */ UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lpt_reg[] = { { ORDATA (BUF, lpt_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_LPT) }, { FLDATA (DONE, dev_done, INT_V_LPT) }, { FLDATA (DISABLE, dev_disable, INT_V_LPT) }, { FLDATA (INT, int_req, INT_V_LPT) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { NULL } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &lpt_reset, NULL, NULL, NULL, &lpt_dib, DEV_DISABLE }; /* IOT routine */ int32 lpt (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) lpt_unit.buf = AC & 0177 ; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_LPT ) ; DEV_CLR_DONE( INT_LPT ) ; DEV_UPDATE_INTR ; if ( lpt_unit.wait ) if ( (lpt_unit.buf == 015) || (lpt_unit.buf == 014) || (lpt_unit.buf == 012) ) { sim_activate (&lpt_unit, lpt_unit.wait); break ; } return (lpt_svc (&lpt_unit) << IOT_V_REASON); break; case iopC: /* clear */ DEV_CLR_BUSY( INT_LPT ) ; DEV_CLR_DONE( INT_LPT ) ; DEV_UPDATE_INTR ; sim_cancel (&lpt_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat lpt_svc (UNIT *uptr) { DEV_CLR_BUSY( INT_LPT ) ; DEV_SET_DONE( INT_LPT ) ; DEV_UPDATE_INTR ; if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); fputc (uptr->buf, uptr->fileref); uptr->pos = ftell (uptr->fileref); if (ferror (uptr->fileref)) { perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { lpt_unit.buf = 0; /* (not DG compatible) */ DEV_CLR_BUSY( INT_LPT ) ; DEV_CLR_DONE( INT_LPT ) ; DEV_UPDATE_INTR ; sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/NOVA/eclipse_cpu.c0000644000175000017500000100013111015044330014026 0ustar vlmvlm/* eclipse_cpu.c: Eclipse CPU simulator Modified from the original NOVA simulator by Robert Supnik. Copyright (c) 1998-2006, Charles E Owen Portions Copyright (c) 1993-2002, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu Eclipse central processor 07-Jun-06 RMS Fixed bug in DIVS (found by Mark Hittinger) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 25-Aug-05 RMS Fixed DIVS overflow cases 29-Nov-03 CEO Corrected POPJ and Bit operations bugs 26-Nov-03 CEO Added FPU and PIT devices 20-Feb-03 CEO Corrected several MMPU and CIS bugs 28-Jan-02 RMS Cleaned up compiler warnings 30-Nov-01 RMS Added extended SET/SHOW support 01-Jun-01 RMS Added second terminal, plotter support 26-Apr-01 RMS Added device enable/disable support The register state for the Eclipse CPU is basically the same as the NOVA's: AC[0:3]<0:15> general registers C carry flag PC<0:14> program counter Eclipses with Folating Point Units added these registers: FPAC[0:3]<0:63> Floating Point Accumulators FPSR Floating Point Status Register In addition, certain low-memory locations are reserved for special purposes: 0: I/O Return Address (from an interrupt) 1: I/O (Interrupt) handler address 2: System Call handler address (used by SYC instruction) 3: Protection Fault handler address 4: VECTOR stack pointer (VCT Instruction) 5: Current Interrupt Priority mask 6: VECTOR stack limit (VCT instruction) 7: VECTOR stack fault address (VCT again) 10: Block Pointer (later models only) 11: Emulation Trap Handler address (microeclipse only) 20-27: Auto-increment locations (not on microeclipse) 30-37: Auto-decrement locations (not on microeclipse) 40: Stack pointer 41: Frame Pointer 42: Stack Limit 43: Stack fault address 44: XOP Origin address 45: Floating point fault address 46: Commercial fault address (not on microeclipse) 47: Reserved, do not use. Note: While all eclipses share most of the "standard" features, some models added a few quirks and wrinkles, and other models dropped some features or modified others. Most DG software is written for a "standard" Eclipse, and avoids these problem areas. A general overview: [subject to major changes as info becomes available!] Early (e.g. S/100, S/200, C/300) [Front Panel machines] The first Eclipses had the basic MAP, but certain parts were kluged, and these were fixed in later MAP designs. The original mapping hardware was termed MAP for Memory Allocate and Protection. The later design was termed MMPU for Memory Mapping and Protection Unit. While similar in design, the two units are not compatible. Also, the C version (C for Commercial) of these early CPUs had a feature called "Commercial Instruction Set" which contained character manipulation, translation between commercial-format numeric data and FPU formats, and an elaborate EDIT instruction. Later models kept only the character manipulation part of this and called the feature the "Character Instruction Set", leading to confusion because the initials of both are CIS. ARDOS is the only DG operating system to support the older MAP. ZRDOS uses the MMPU, and AOS supports only MMPU. Middle (e.g. S/130, C/150, S/230, C/330) [Front Panel] These are close to a "Standard". They have the newer, fixed MMPU. Support for the PIT (Programmed Interval Timer. The Commercial (not character) instruction set and FPU are optional. (CIS standard on C models) Late (C/350, M/600: [Panel]; S/140, S/280 [Virtual Console]) All features of the Middle period are included, plus: These late Eclipses added a few MMPU wrinkles all their own, included support for user maps C and D. Character instruction set is standard, FPU optional. Also, support for the BMC device. MicroEclipse-based (S/20, S/120, Desktops) [Virtual cons.] All features of the Late period, in general, plus: Microeclipses dropped support for the auto increment and decrement locations at 20-37. They also added support for invalid instruction traps thru location 11. The Desktops have an interface to the "Attached Processor", an 8086, at device code 6. Also, some new CPU device features to read states info. The Character Instruction set and FPU are standard on all models. The Eclipse instruction set is an elaboration of the NOVA's. The basic NOVA set is implemented in it's entireity, plus many new Eclipse instructions are added. Since in theory every possible 16-bit combination is a NOVA instruction, the Eclipse commands are carved out of the NOVA set by using the Operate format with the no-load bit set to 1 and the skip bits set to 000. Since this combination is in effect a no-op on the NOVA, it was rarely or never used. The other bits are used to form Eclipse instructions, which have no other common format. To see the instructions, refer to the Eclipse section of the instruction decode logic in sim_instr() below. All Eclipse instructions are checked first, so in case of conflict in bit patterns, the Eclipse one is executed over the corresponding NOVA pattern. A bizarre exception is LEF mode...which implements an instruction called Load Effective Address by taking over the Nova I/O format when the LEF mode bit is set and the processor is executing in mapped mode. The following discussion talks about NOVA instructions which are Eclipse instructions also. The NOVA has three instruction formats: memory reference, I/O transfer, and operate. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0| op | AC |in| mode| displacement | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:4> mnemonic action 00000 JMP PC = MA 00001 JMS AC3 = PC, PC = MA 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0 001'n LDA ACn = M[MA] 010'n STA M[MA] = ACn <5:7> mode action 000 page zero direct MA = zext (IR<8:15>) 001 PC relative direct MA = PC + sext (IR<8:15>) 010 AC2 relative direct MA = AC2 + sext (IR<8:15>) 011 AC3 relative direct MA = AC3 + sext (IR<8:15>) 100 page zero indirect MA = M[zext (IR<8:15>)] 101 PC relative dinirect MA = M[PC + sext (IR<8:15>)] 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)] 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)] Memory reference instructions can access an address space of 32K words. An instruction can directly reference the first 256 words of memory (called page zero), as well as 256 words relative to the PC, AC2, or AC3; it can indirectly access all 32K words. If an indirect address is in locations 00020-00027, the indirect address is incremented and rewritten to memory before use; if in 00030-00037, decremented and rewritten. The I/O transfer format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 1 1| AC | opcode |pulse| device | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The IOT instruction sends the opcode, pulse, and specified AC to the specified I/O device. The device may accept data, provide data, initiate or cancel operations, or skip on status. The operate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \______/ \___/ \___/ | | | | | | | | | | +--- reverse skip sense | | | | | +--- skip if C == 0 | | | | +--- skip if result == 0 | | | +--- don't load result | | +--- carry in (load as is, | | set to Zero, | | set to One, | | load Complement) | +--- shift (none, | left one, | right one, | byte swap) +--- operation (complement, negate, move, increment, add complement, subtract, add, and) The operate instruction can be microprogrammed to perform operations on the source and destination AC's and the Carry flag. This routine is the instruction decode routine for the NOVA. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered infinite indirection loop unknown I/O device and STOP_DEV flag set I/O error in I/O simulator 2. Interrupts. Interrupts are maintained by four parallel variables: dev_done device done flags dev_disable device interrupt disable flags dev_busy device busy flags int_req interrupt requests In addition, int_req contains the interrupt enable and ION pending flags. If ION and ION pending are set, and at least one interrupt request is pending, then an interrupt occurs. Note that the 16b PIO mask must be mapped to the simulator's device bit mapping. 3. Non-existent memory. On the NOVA, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: eclipse_defs.h add interrupt request definition eclipse_cpu.c add IOT mask, PI mask, and routine to dev_table eclipse_sys.c add pointer to data structures to sim_devices */ /*--------------------------------------------------------------------------- ** ECLIPSE Debugging Facilities ** ** These options are designed to find hard-to-locate flaky bugs by ** providing special error checking and logging. ** ** All are controlled by depositing a value into the DEBUG register. ** A value of zero means no special debugging facilities are turned on. ** This is the default. Debugging invokes a performance hit! Use only ** when necessary. ** ** Debugging means logging information to a file, or to a buffer in ** memory from whence it can be dumped to a file. ** ** 1XXXXX = Log all instructions executed to file "trace.log". ** **CAUTION**: This means the CPU will run SLOWLY and ** the resulting trace.log file will be HUGE. We're talking ** about a megabyte for each 5 seconds or less of wall clock ** time, depending on the speed of your CPU. Note: In this ** mode, interrupts are logged when they are received also. ** ** Note: when detailed logging is off, the last 4096 or so ** instructions executed are saved in a memory buffer, and ** when the sim stops, the "show" command can write this ** history information to the file "history.log". This only ** works if the DEBUG register is non-zero however, because ** of the performance hit even this recording makes. To ** dump history, enter the command "show cpu history", with ** the file "history" spelled correctly and lower case. ** ** XXXXDD = Log all I/O instructions to or from device number ** DD. Log is written to "trace.log", regardless of the ** setting of the instruction trace flag (1XXXXX). If both ** are on, the device traces will be interpersed with the ** instruction traces -- very useful sometimes. ** ** XXX1DD = Device Break. Does a breakpoint in any I/O to ** device DD. Useful, say when a diagnostic gives an ** error message - a device break on 11 (TTO) will stop ** as soon as the error message appears, making the ** trace log much shorter to track back on. ** ** X4XXXX = When this bit is on, the sim will stop if it sees ** an invalid instruction. When DEBUG is zero, any such ** instruction is no-oped with no warning. When DEBUG is ** non-zero, but this bit is 0, a warning will be displayed ** but execution will continue. ** ** X2XXXX = LEF break. When A LEF instruction is executed in ** mapped user space, the sim does a breakpoint right after ** executing the instruction. ** ** Whenever the DEBUG register is non-zero, special error checking ** is enabled in the sim. This will stop the sim automatically ** when a likely error occurs, such as: ** ** 1. Any execution that reaches, or will reach, location 00000. ** 2. Any I/O to device 00 ** 3. An interrupt from device 00. ** 4. An invalid instruction (stop is optional) ** ** DCHAR Register: Whenever this is non-zero, a test is made on every ** character output to the TTO device (master console). If the character ** output to that device matches this register, the CPU will break. ** ** Of course, the standard BREAK register is available for breakpoints ** as in all the sims based on this standard. --------------------------------------------------------------------------*/ #include "nova_defs.h" #define UNIT_V_MICRO (UNIT_V_UF) /* Microeclipse? */ #define UNIT_V_17B (UNIT_V_UF) /* 17 bit MAP */ #define UNIT_V_UP (UNIT_V_UF) /* FPU Enabled */ #define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ #define UNIT_MICRO (1 << UNIT_V_MICRO) #define UNIT_17B (1 << UNIT_V_17B) #define UNIT_UP (1 << UNIT_V_UP) #define UNIT_MSIZE (1 << UNIT_V_MSIZE) uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 AC[4] = { 0 }; /* accumulators */ int32 C = 0; /* carry flag */ int32 saved_PC = 0; /* program counter */ int32 SR = 0; /* switch register */ int32 dev_done = 0; /* device done flags */ int32 dev_busy = 0; /* device busy flags */ int32 dev_disable = 0; /* int disable flags */ int32 iot_enb = -1; /* IOT enables */ int32 int_req = 0; /* interrupt requests */ int32 pimask = 0; /* priority int mask */ int32 pwr_low = 0; /* power fail flag */ int32 ind_max = 15; /* iadr nest limit */ int32 stop_dev = 0; /* stop on ill dev */ int32 old_PC = 0; /* previous PC */ int32 model = 140; /* Model of Eclipse */ int32 speed = 0; /* Delay for each instruction */ int32 XCT_mode = 0; /* 1 if XCT mode */ int32 XCT_inst = 0; /* XCT instruction */ int32 PPC = -1; int32 AMASK = 077777; struct ndev dev_table[64]; /* dispatch table */ /* Instruction history buffer */ #define HISTMAX 4096 int32 hnext = 0; /* # of current entry */ int32 hwrap = 0; /* 1 if wrapped */ int32 hmax = HISTMAX; /* Maximum entries b4 wrap */ uint16 hpc[HISTMAX]; uint16 hinst[HISTMAX]; uint16 hinst2[HISTMAX]; uint16 hac0[HISTMAX]; uint16 hac1[HISTMAX]; uint16 hac2[HISTMAX]; uint16 hac3[HISTMAX]; unsigned short hflags[HISTMAX]; /* Flags: 0x01 - carry bit 0x02 - int enabled 0x04 - user map a 0x08 - user map b 0x10 - user map c 0x20 - user map d 0x40 - LEF mode was on 0x80 - this is an int, not an inst. hpc is return addr hinst is int_req hac0 is device hac1 is int addr */ /* the Eclipse MAP unit: This unit is standard in all Eclipse processors except for the "original" Eclipses, the S/100, S/200, and C/300. These use a different and more elaborate MMPU that is not compatible with the one simulated here. All subsequent Eclipses, from the S/130 on up to the last models S/280 and C/380 use the map simulated here, including the MicroEclipses. There are model-dependent quirks. That's why we have to MODEL register. The programming of the MMPU can be found in the LMP instruction, below, and in the instructions directed to DEV_MAP. There are two user maps, called A and B, and four data channel maps, A thru D. They can be enabled/disabled separately. Some models have two extra user maps, C and D. These are supported where apporpriate. */ #define PAGEMASK 01777 /* Largest physical page possible */ #define MAPMASK 0101777 /* Valid page bits in map */ #define INVALID 0101777 /* Mask indicating an invalid page */ int32 MapStat = 0; /* Map status register */ int32 Inhibit = 0; /* !0=inhibit interrupts : */ /* 1 = single cycle inhibit */ /* 2 = inhibit until indirection */ /* 3 = inhibit next instruction only */ int32 Enable = 0; /* User map to activate 1=A 2=B */ int32 Usermap = 0; /* Active Map? 0=supvr mode, 1=user A, 2 = user B */ int32 Map[8][32]; /* The actual MAPs 0=dch A, 1=A, 2=B, 3-5=dchB-D 6-7 User C-D */ int32 Map31 = 037; /* Map for block 31 in supervisor mode */ int32 SingleCycle = 0; /* Map one LDA/STA */ int32 Check = 0; /* Page Check Register */ int32 Fault = 0; /* Fault register */ int32 MapInit = 0; /* 1 when map initialized */ int32 MapIntMode = 0; /* Save of map user mode when int occurs */ /* The Eclipse Floating Point Unit: This unit is optional on all Eclipse models. */ int32 FPSR = 0; /* 32-bit FPU Status Register */ t_int64 FPAC[4] = { 0,0,0,0 }; /* 4 64-bit Accumulators */ int32 FPFault = 0; /* Save Fault State */ /* Definitions for internal floating point arithmetic */ typedef struct _SHORT_FLOAT { int32 short_fract; /* Fraction */ short expo; /* Exponent + 64 */ uint8 sign; /* Sign */ } SHORT_FLOAT; typedef struct _LONG_FLOAT { t_int64 long_fract; /* Fraction */ short expo; /* Exponent + 64 */ uint8 sign; /* Sign */ } LONG_FLOAT; LONG_FLOAT dfl,dfl2; /* Double Precision Work Fields */ SHORT_FLOAT sfl,sfl2; /* Single Precision Work Fields */ t_int64 tempfp, holdfp; /* Working area for FPAC */ int shift,m3; t_int64 lsfract; void get_sf(SHORT_FLOAT *fl, t_int64 *fpr); void store_sf(SHORT_FLOAT *fl, t_int64 *fpr); void get_lf(LONG_FLOAT *fl, t_int64 *fpr); void store_lf(LONG_FLOAT *fl, t_int64 *fpr); int normal_sf (SHORT_FLOAT *fl); int normal_lf (LONG_FLOAT *fl); int overflow_sf(SHORT_FLOAT *fl); int overflow_lf(LONG_FLOAT *fl); int underflow_sf(SHORT_FLOAT *fl); int underflow_lf(LONG_FLOAT *fl); int significance_sf(SHORT_FLOAT *fl); int significance_lf(LONG_FLOAT *fl); int add_sf(SHORT_FLOAT *fl, SHORT_FLOAT *add_f1, int normal); int add_lf(LONG_FLOAT *fl, LONG_FLOAT *add_fl, int normal); int mul_sf(SHORT_FLOAT *fl, SHORT_FLOAT *mul_fl); int mul_lf(LONG_FLOAT *fl, LONG_FLOAT *mul_fl); int div_sf(SHORT_FLOAT *fl, SHORT_FLOAT *div_fl); int div_lf(LONG_FLOAT *fl, LONG_FLOAT *div_fl); /* Special Debugging Info */ int32 Debug_Flags = 0; /* Debug register - selects debug features */ int32 Debug_Char = 0; /* Debug Character Register */ int32 Tron = 0; /* For trace files */ FILE *Trace; t_stat reason; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern DEVICE *sim_devices[]; 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_boot (int32 unitno, DEVICE *dptr); t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat Debug_Dump (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat map_reset (DEVICE *dptr); t_stat map_svc (UNIT *uptr); t_stat fpu_svc (UNIT *uptr); int32 GetMap(int32 addr); int32 PutMap(int32 addr, int32 data); int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags); t_stat build_devtab (void); extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, 15) }, { ORDATA (AC0, AC[0], 16) }, { ORDATA (AC1, AC[1], 16) }, { ORDATA (AC2, AC[2], 16) }, { ORDATA (AC3, AC[3], 16) }, { FLDATA (C, C, 16) }, { ORDATA (SR, SR, 16) }, { ORDATA (PI, pimask, 16) }, { FLDATA (ION, int_req, INT_V_ION) }, { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, { FLDATA (PWR, pwr_low, 0) }, { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO }, { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO }, { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO }, { FLDATA (STOP_DEV, stop_dev, 0) }, { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, { ORDATA (DEBUG, Debug_Flags, 16) }, { ORDATA (DCHAR, Debug_Char, 16) }, { DRDATA (MODEL, model, 16) }, { DRDATA (SPEED, speed, 16) }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_MICRO, UNIT_MICRO, "MICRO", "MICRO", NULL }, { UNIT_MICRO, 0, "STD", "STD", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size }, { UNIT_MSIZE, 0, NULL, "DUMP", &Debug_Dump }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "HISTORY", NULL, NULL, &Dump_History }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 17, 1, 8, 16, &cpu_ex, &cpu_dep, &cpu_reset, &cpu_boot, NULL, NULL }; /* MAP data structures map_dev MAP device descriptor map_unit MAP unit descriptor map_reg MAP register list map_mod MAP modifiers list */ UNIT map_unit = { UDATA (&map_svc, UNIT_17B, MAXMEMSIZE) }; REG map_reg[] = { { ORDATA (STATUS, MapStat, 16) }, { ORDATA (ENABLE, Enable, 16) }, { ORDATA (IINHIB, Inhibit, 16) }, { ORDATA (ACTIVE, Usermap, 16) }, { ORDATA (MAP31, Map31, 16) }, { ORDATA (CYCLE, SingleCycle, 16) }, { ORDATA (CHECK, Check, 16) }, { ORDATA (FAULT, Fault, 16) }, { NULL } }; MTAB map_mod[] = { { UNIT_17B, UNIT_17B, "17bit", "17B", NULL }, { UNIT_17B, 0, "19bit", "19B", NULL }, { 0 } }; DEVICE map_dev = { "MAP", &map_unit, map_reg, map_mod, 1, 8, 17, 1, 8, 16, &map_ex, &map_dep, NULL, NULL, NULL, NULL }; /* FPU data structures fpu_dev MAP device descriptor fpu_unit MAP unit descriptor fpu_reg MAP register list fpu_mod MAP modifiers list */ UNIT fpu_unit = { UDATA (&fpu_svc, UNIT_UP, MAXMEMSIZE) }; REG fpu_reg[] = { { ORDATA (STATUS, FPSR, 32) }, { ORDATA (FPAC0, FPAC[0], 64) }, { ORDATA (FPAC1, FPAC[1], 64) }, { ORDATA (FPAC2, FPAC[2], 64) }, { ORDATA (FPAC3, FPAC[3], 64) }, { ORDATA (FAULT, FPFault, 32) }, { NULL } }; MTAB fpu_mod[] = { { UNIT_UP, UNIT_UP, "Enabled (UP)", "UP", NULL }, { UNIT_UP, 0, "Disabled (DOWN)", "DOWN", NULL }, { 0 } }; DEVICE fpu_dev = { "FPU", &fpu_unit, fpu_reg, fpu_mod, 1, 16, 17, 1, 16, 16, NULL, NULL, NULL, NULL, NULL, NULL }; /* ---- Programmable Interval Timer Device ----------- */ int32 pit_time = 100; int32 pit_tps = 10000; /* ticks per sec */ int32 pit_adj = 20; /* tmxr adjust */ int32 pit_poll = 16000; /* tmxr poll */ int32 pit_initial = 0; /* initial counter reg */ int32 pit_counter = 0; /* Counter */ int32 pit_flag = 0; /* Initial setting flag */ int32 pit (int32 pulse, int32 code, int32 AC); t_stat pit_svc (UNIT *uptr); t_stat pit_reset (DEVICE *dptr); /* PIT data structures pit_dev device descriptor pit_unit unit descriptor pit_reg register list */ DIB pit_dib = { DEV_PIT, INT_PIT, PI_PIT, &pit }; UNIT pit_unit = { UDATA (&pit_svc, 0, 0) }; REG pit_reg[] = { { ORDATA (INIT, pit_initial, 16) }, { ORDATA (COUNT, pit_counter, 16) }, { FLDATA (BUSY, dev_busy, INT_V_PIT) }, { FLDATA (DONE, dev_done, INT_V_PIT) }, { FLDATA (DISABLE, dev_disable, INT_V_PIT) }, { FLDATA (INT, int_req, INT_V_PIT) }, { DRDATA (TIME0, pit_time, 24), REG_NZ + PV_LEFT }, { NULL } }; DEVICE pit_dev = { "PIT", &pit_unit, pit_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, &pit_reset, NULL, NULL, NULL, &pit_dib, 0 }; t_stat sim_instr (void) { extern int32 sim_interval; register int32 PC, IR, i, t, MA, j, k, tac; register uint32 mddata, uAC0, uAC1, uAC2, uAC3; int16 sAC0, sAC1, sAC2; int32 sddata, mi1, mi2, fpnum32; t_int64 fpnum, expon; t_value simeval[20]; void mask_out (int32 mask); /* char debstr[128]; */ /* char debadd[64]; */ char debmap[4], debion[4]; int debcar, iodev, iodata, debflags; int32 DisMap, debpc; /* int32 sp, sl; */ int cmdptr, cmsptr, cmopt, cmptr; int16 cmslen, cmdlen; int tabaddr, tabptr; int32 effective(int32 PC, int32 index, int32 disp); int32 indirect(int32 d); int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect); int32 LoadMap(int32 w); int32 Bytepointer(int32 PC, int32 index); int32 unimp(int32 PC); int32 pushrtn(int32 pc); /* Restore register state */ if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */ PC = saved_PC & AMASK; /* load local PC */ C = C & 0200000; mask_out (pimask); /* reset int system */ reason = 0; if (MapInit == 0) { MapInit = 1; for (mi1 = 0; mi1 < 6; mi1++) { /* Initialize MAPs */ for (mi2 = 0; mi2 < 32; mi2++) { Map[mi1][mi2] = mi2; } } } /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } //if (speed > 0) for (i = 0; i < speed; i++) { j = 0; } if (Fault) { /* Check MAP fault */ Usermap = 0; /* YES: shutdown map */ MapStat &= ~01; /* Disable MMPU */ if (Fault & 0100000/*!!!*/) /* If it was validity, or WP */ MapStat &= ~0170; /* Reset other checkbits */ MapStat |= Fault & 077777; /* Put in fault code */ Fault = 0; /* Reset fault code */ t = (GetMap(040) + 1) & AMASK; /* Push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, (PC & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); int_req = int_req & ~INT_ION; /* Disable interrupts */ PC = indirect(M[003]); /* JMP to loc 3 */ continue; } if (FPSR & 0xF8000000) { /* FPU Fault? */ if (!(FPSR & 0x78000000)) { /* If error bit on ... */ FPSR &= 0x00FFFFFF; /* ...but no error, clear it */ } else { /* ELSE a real error: */ FPSR |= 0x80000000; /* Turn error bit on */ if (FPSR & 0x04000000) { /* Trap enabled ? */ FPFault = FPSR; /* Save fault */ FPSR &= 0xFBFFFFFF; /* Clear Trap Enable */ } } } if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ int_req = int_req & ~INT_ION; MapIntMode = MapStat; /* Save Status as it was */ Usermap = 0; /* Inhibit MAP */ MapStat &= ~1; /* Disable user map */ if (XCT_mode) { M[0] = PC - 1; /* If XCT mode rtn to XCT */ XCT_mode = 0; /* turn off mode */ } else { M[0] = PC; /* Save Return Address */ } old_PC = PC; MA = M[1]; for (i = 0; i < ind_max * 2; i++) { /* count indirects */ if ((MA & 0100000) == 0) break; if ((MA & 077770) == 020) MA = (M[MA & AMASK] = (M[MA & AMASK] + 1) & 0177777); else if ((MA & 077770) == 030) MA = (M[MA & AMASK] = (M[MA & AMASK] - 1) & 0177777); else MA = M[MA & AMASK]; } if (i >= (ind_max-1)) { if ((MapStat & 010) && Usermap) { Fault = 04000; /* Map fault if IND prot */ continue; } else { reason = STOP_IND_INT; break; } } if (Debug_Flags) { iodev = 0; iodata = int_req & (-int_req); for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (iodata & dev_table[i].mask) { iodev = i; break; } } if (iodev == 0) { printf("\n<>\n"); reason = STOP_IBKPT; } if (Debug_Flags & 0100000) { fprintf(Trace, "--------- Interrupt %o (%o) to %6o ---------\n", int_req, iodev, MA); } else { Debug_Entry(PC, int_req, 0, iodev, MA, 0, 0, 0x80); } } PC = MA; } /* end interrupt */ if (Inhibit != 0) { /* Handle 1-instruction inhibit sequence */ if (Inhibit == 3) /* Used by SYC instruction */ Inhibit = 4; if (Inhibit == 4) Inhibit = 0; } if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } if ((PC < 1 || PC > 077777) && Debug_Flags) { if (PPC != -1) { /* Don't break on 1st instruction */ printf("\n<>\n\r", PC, PPC); reason = STOP_IBKPT; break; } } PPC = PC; if (Debug_Flags) { if (!Tron) { Tron = 1; Trace = fopen("trace.log", "w"); } strcpy(debmap, " "); strcpy(debion, " "); debcar = 0; if (C) debcar = 1; if (Usermap == 1) strcpy(debmap, "A"); if (Usermap == 2) strcpy(debmap, "B"); if (Usermap == 5) strcpy(debmap, "C"); if (Usermap == 6) strcpy(debmap, "D"); if (int_req & INT_ION) strcpy(debion, "I"); if (XCT_mode == 0) { debpc = PC; simeval[0] = GetMap(PC); simeval[1] = GetMap(PC+1); } else { debpc = 0177777; simeval[0] = XCT_inst; simeval[1] = 0; } if (Debug_Flags & 0100000) { fprintf(Trace, "%s%s%06o acs: %06o %06o %06o %06o %01o ", debion, debmap, debpc, AC[0], AC[1], AC[2], AC[3], debcar); fprint_sym (Trace, debpc, simeval, NULL, SWMASK('M')); fprintf(Trace, "\n"); } else { debflags = 0; if (C) debflags |= 0x01; if (int_req & INT_ION) debflags |= 0x02; if (Usermap == 1) debflags |= 0x04; if (Usermap == 2) debflags |= 0x08; if (Usermap == 3) debflags |= 0x10; if (Usermap == 4) debflags |= 0x20; Debug_Entry(debpc, (int32)simeval[0], (int32)simeval[1], AC[0], AC[1], AC[2], AC[3], debflags); } } if (XCT_mode == 0) { /* XCT mode? */ IR = GetMap(PC); /* No: fetch instr */ if (Fault) continue; /* Give up if fault */ PC = (PC + 1) & AMASK; /* bump PC */ } else { IR = XCT_inst; /* Yes: Get inst to XCT */ XCT_mode = 0; /* Go back to normal mode */ } int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ sim_interval = sim_interval - 1; t = IR >> 11; /* prepare to decode */ /* ---------------- BEGIN Eclipse modification --------------------- */ /* Eclipse instruction set. These instructions are checked for before any of the NOVA ones. Eclipse instructions do not correspond to any patterns, other than bit 0 being 1 and the last 4 bits are 1000. Words which are not Eclipse instructions will be interpreted as Nova instructions. */ /* Important Note: The order of the if statements is important. Frequently executed instructions should come first, to enhance the speed of the simulation. */ if ((IR & 0100017) == 0100010) { /* This pattern for all */ /* Eclipse instructions */ /****************************************************************/ /* This is the standard Eclipse instruction set */ /****************************************************************/ /* Byte operations */ if ((IR & 0103777) == 0102710) { /* LDB: Load Byte */ i = (IR >> 13) & 03; MA = (AC[i] >> 1) & AMASK; j = (IR >> 11) & 03; if (AC[i] & 01) { AC[j] = GetMap(MA) & 0377; } else { AC[j] = (GetMap(MA) >> 8) & 0377; } continue; } if ((IR & 0103777) == 0103010) { /* STB: Store Byte */ i = (IR >> 13) & 03; MA = (AC[i] >> 1); j = (IR >> 11) & 03; t = GetMap(MA); if (AC[i] & 01) { t &= 0177400; t |= (AC[j] & 0377); PutMap(MA, t); } else { t &= 0377; t |= (AC[j] & 0377) << 8; PutMap(MA, t); } continue; } /* Fixed-point arithmetic - loads & saves */ if ((IR & 0162377) == 0122070) { /* ELDA: Extended LDA */ i = (IR >> 11) & 3; t = GetMap(PC); if (SingleCycle) Usermap = SingleCycle; AC[i] = GetMap(effective(PC, (IR >> 8) & 3, t)); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } PC = (PC + 1) & AMASK; continue; } if ((IR & 0162377) == 0142070) { /* ESTA: Extended STA */ i = (IR >> 11) & 3; t = GetMap(PC); if (SingleCycle) Usermap = SingleCycle; PutMap((effective(PC, (IR >> 8) & 3, t)), AC[i]); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100010) { /* ADI: Add Immediate */ t = (IR >> 11) & 3; AC[t] = (AC[t] + ((IR >> 13) & 3) + 1) & 0xffff; continue; } if ((IR & 0103777) == 0100110) { /* SBI: Subtract Immediate */ t = (IR >> 11) & 3; AC[t] = (AC[t] - (((IR >> 13) & 3) + 1)) & 0xffff; continue; } if ((IR & 0163777) == 0163770) { /* ADDI: Extended Add Immed. */ t = (IR >> 11) & 3; i = GetMap(PC); PC = (PC + 1) & AMASK; AC[t] = (AC[t] + i) & 0xffff; continue; } if ((IR & 0103777) == 0100710) { /* XCH: Exchange Accumulators */ t = AC[(IR >> 11) & 3]; AC[(IR >> 11) & 3] = AC[(IR >> 13) & 3]; AC[(IR >> 13) & 3] = t; continue; } if ((IR & 0162377) == 0162070) { /* ELEF: Load Effective Addr */ t = GetMap(PC); AC[(IR >> 11) & 3] = effective(PC, (IR >> 8) & 3, t); PC = (PC + 1) & AMASK; continue; } /* Logical operations */ if ((IR & 0163777) == 0143770) { /* ANDI: And Immediate */ AC[(IR >> 11) & 3] &= GetMap(PC); PC = (PC + 1) & AMASK; continue; } if ((IR & 0163777) == 0103770) { /* IORI: Inclusive Or Immed */ AC[(IR >> 11) & 3] |= GetMap(PC); PC = (PC + 1) & AMASK; continue; } if ((IR & 0163777) == 0123770) { /* XORI: Exclusive Or Immed */ AC[(IR >> 11) & 3] ^= GetMap(PC); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100410) { /* IOR: Inclusive Or */ AC[(IR >> 11) & 3] |= AC[(IR >> 13) & 3]; continue; } if ((IR & 0103777) == 0100510) { /* XOR: Exclusive Or */ AC[(IR >> 11) & 3] ^= AC[(IR >> 13) & 3]; continue; } if ((IR & 0103777) == 0100610) { /* ANC: And with complemented src */ AC[(IR >> 11) & 3] &= ~(AC[(IR >> 13) & 3]); continue; } /* Shift operations */ if ((IR & 0103777) == 0101210) { /* LSH: Logical Shift */ register int16 sh; sh = AC[(IR >> 13) & 3] & 0377; i = (IR >> 11) & 3; if (sh & 0200) { sh = ~sh + 1; AC[i] = AC[i] >> sh; } else { AC[i] = AC[i] << sh; } if (sh > 15) AC[i] = 0; AC[i] &= 0xffff; continue; } if ((IR & 0103777) == 0101310) { /* DLSH: Double logical shift */ register int16 sh; sh = AC[(IR >> 13) & 3] & 0377; i = (IR >> 11) & 3; uAC0 = AC[i] << 16; j = i + 1; if (j == 4) j = 0; uAC0 |= AC[j]; if (sh & 0200) { sh = (~sh + 1) & 0377; if (sh < 32) uAC0 = uAC0 >> sh; } else { if (sh < 32) uAC0 = uAC0 << sh; } if (sh > 31) uAC0 = 0; AC[i] = (uAC0 >> 16) & 0xffff; AC[j] = uAC0 & 0xffff; continue; } if ((IR & 0103777) == 0101410) { /* HXL: Hex shift left */ t = ((IR >> 13) & 3) + 1; i = (IR >> 11) & 3; AC[i] = AC[i] << (t * 4); AC[i] &= 0xffff; continue; } if ((IR & 0103777) == 0101510) { /* HXR: Hex shift right */ t = ((IR >> 13) & 3) + 1; i = (IR >> 11) & 3; AC[i] = AC[i] >> (t * 4); AC[i] &= 0xffff; continue; } if ((IR & 0103777) == 0101610) { /* DHXL: Double Hex shift left */ t = ((IR >> 13) & 3) + 1; i = (IR >> 11) & 3; j = i + 1; if (j == 4) j = 0; uAC0 = AC[i] << 16; uAC0 |= AC[j]; uAC0 = uAC0 << ((t * 4) & 0177); AC[i] = (uAC0 >> 16) & 0xffff; AC[j] = uAC0 & 0xffff; continue; } if ((IR & 0103777) == 0101710) { /* DHXR: Double Hex shift right */ t = ((IR >> 13) & 3) + 1; i = (IR >> 11) & 3; j = i + 1; if (j == 4) j = 0; uAC0 = AC[i] << 16; uAC0 |= AC[j]; uAC0 = uAC0 >> ((t * 4) & 0177); AC[i] = (uAC0 >> 16) & 0xffff; AC[j] = uAC0 & 0xffff; continue; } /* Bit operations */ if ((IR & 0103777) == 0102010) { /* BTO: Set bit to one */ i = (IR >> 11) & 3; j = (IR >> 13) & 3; if (i != j) { k = (AC[i] >> 4) & AMASK; if ((AC[j] + k) & 0100000) t = 1; //AOS MA = indirect(AC[j] + k); MA = (AC[j] + k) & AMASK; } else { MA = (AC[i] >> 4) & AMASK; } t = AC[i] & 017; t = GetMap(MA) | (0100000 >> t); PutMap(MA, t); continue; } if ((IR & 0103777) == 0102110) { /* BTZ: Set bit to zero */ i = (IR >> 11) & 3; j = (IR >> 13) & 3; if (i != j) { k = (AC[i] >> 4) & AMASK; if ((AC[j] + k) & 0100000) t = 1; //AOS MA = indirect(AC[j] + k); MA = (AC[j] + k) & AMASK; } else { MA = (AC[j] >> 4) & AMASK; } t = AC[i] & 017; t = GetMap(MA) & ~(0100000 >> t); PutMap(MA, t); continue; } if ((IR & 0103777) == 0102210) { /* SZB: Skip on zero bit */ i = (IR >> 11) & 3; j = (IR >> 13) & 3; if (i != j) { k = (AC[i] >> 4) & AMASK; if ((AC[j] + k) & 0100000) t = 1; MA = indirect(AC[j] + k); // MA = (AC[j] + k) & AMASK; } else { MA = (AC[i] >> 4) & AMASK; } t = GetMap(MA) << (AC[i] & 017); if (!(t & 0100000)) PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102770) { /* SNB: Skip on non-zero bit */ i = (IR >> 11) & 3; j = (IR >> 13) & 3; if (i != j) { k = (AC[i] >> 4) & AMASK; if ((AC[j] + k) & 0100000) t = 1; MA = indirect(AC[j] + k); // MA = (AC[j] + k) & AMASK; } else { MA = (AC[j] >> 4) & AMASK; } t = GetMap(MA) << (AC[i] & 017); if (t & 0100000) PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102310) { /* SZBO: skip on zero bit & set to 1 */ register int32 save; i = (IR >> 11) & 3; j = (IR >> 13) & 3; if (i != j) { k = (AC[i] >> 4) & AMASK; MA = indirect(AC[j] + k); // MA = (AC[j] + k) & AMASK; } else { MA = (AC[j] >> 4) & AMASK; } t = AC[i] & 017; save = GetMap(MA); t = save | (0100000 >> t); PutMap(MA, t); t = save << (AC[i] & 017); if ((t & 0100000) == 0) PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102410) { /* LOB: Locate lead bit */ register int32 a, r; register int16 b; a = AC[(IR >> 13) & 3] & 0xffff; for (i = 0; i < 16; i++) { if ((a << i) & 0100000) break; } r = (IR >> 11) & 3; b = AC[r]; b += i; AC[r] = b & 0177777; continue; } if ((IR & 0103777) == 0102510) { /* LRB: Locate & reset lead bit */ register int32 a, r; register int16 b; j = (IR >> 13) & 3; a = AC[j]; for (i = 0; i < 16; i++) { if ((a << i) & 0100000) break; } r = (IR >> 11) & 3; b = AC[r]; b += i; if (j != r) AC[r] = b & 0177777; AC[j] &= ~(0100000 >> i); AC[j] &= 0xffff; continue; } if ((IR & 0103777) == 0102610) { /* COB: Count bits */ register int32 a; register int16 b, c = 0; a = AC[(IR >> 13) & 3]; for (i = 0; i < 16; i++) { if ((a >> i) & 1) c++; } i = (IR >> 11) & 3; b = AC[i]; b += c; AC[i] = b & 0177777; continue; } /* Jump & similar operations */ if ((IR & 0176377) == 0102070) { /* EJMP: Extended JMP */ PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); continue; } if ((IR & 0176377) == 0106070) { /* EJSR: Extended JMP to subr */ t = effective(PC, (IR >> 8) & 3, GetMap(PC)); AC[3] = (PC + 1) & AMASK; PC = t & AMASK; continue; } if ((IR & 0176377) == 0112070) { /* EISZ: Ext Inc & skip if 0 */ MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); PutMap(MA, ((GetMap(MA) + 1) & 0xffff)); if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; PC = (PC + 1) & AMASK; continue; } if ((IR & 0176377) == 0116070) { /* EDSZ: Ext Dec & skip if 0 */ MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); PutMap(MA, ((GetMap(MA) - 1) & 0xffff)); if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0101010) { /* SGT: Skip if ACS > ACD */ register int16 a1, d1; a1 = AC[(IR >> 13) & 3] & 0xffff; d1 = AC[(IR >> 11) & 3] & 0xffff; if (a1 > d1) PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0101110) { /* SGE: Skip if ACS >= ACD */ register int16 a1, d1; a1 = AC[(IR >> 13) & 3] & 0xffff; d1 = AC[(IR >> 11) & 3] & 0xffff; if (a1 >= d1) PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102370) { /* CLM: Compare to limits */ register int32 s, d, MA; int16 H, L, ca; s = (IR >> 13) & 3; d = (IR >> 11) & 3; if (s == d) { L = GetMap(PC); PC++; H = GetMap(PC); PC++; } else { MA = AC[d] & AMASK; L = GetMap(MA); H = GetMap(MA + 1); } ca = AC[s]; if (ca >= L && ca <= H) PC = (PC + 1) & AMASK; continue; } if ((IR & 0163777) == 0123370) { /* XCT: Execute */ XCT_mode = 1; /* Set up to execute on next loop */ XCT_inst = AC[(IR >> 11) & 3]; continue; } /* Memory block operations */ if (IR == 0113710) { /* BAM: Block add & move */ register int32 w; t = AC[1]; if (t < 1 || t > 0100000) continue; i = indirect(AC[2]); j = indirect(AC[3]); while (t) { w = GetMap(i); PutMap(j, ((w + AC[0]) & 0xffff)); if (Fault) break; t--; i++; j++; i &= AMASK; j &= AMASK; } AC[1] = t; AC[2] = i & AMASK; AC[3] = j & AMASK; continue; } if (IR == 0133710) { /* BLM: Block move */ t = AC[1]; if (t < 1 || t > 0100000) continue; i = indirect(AC[2]); j = indirect(AC[3]); if (Fault) continue; while (t) { PutMap(j, GetMap(i)); if (Fault) break; t--; i++; j++; i &= AMASK; j &= AMASK; } AC[1] = t; AC[2] = i & AMASK; AC[3] = j & AMASK; continue; } /* Stack operations */ if ((IR & 0103777) == 0103110) { /* PSH: Push multiple accums */ register int32 j; j = (IR >> 11) & 3; t = GetMap(040) & AMASK; i = (IR >> 13) & 3; if (i == j) { t++; PutMap(t, AC[i]); PutMap(040, (t & AMASK)); if (t > GetMap(042)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } while (i != j) { t++; PutMap(t, AC[i]); i++; if (i == 4) i = 0; } t++; PutMap(t, AC[i]); PutMap(040, (t & AMASK)); if ((GetMap(040) & AMASK) > GetMap(042)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if ((IR & 0103777) == 0103210) { /* POP: Pop mult accums */ j = (IR >> 11) & 3; t = GetMap(040) & AMASK; i = (IR >> 13) & 3; if (i == j) { AC[i] = GetMap(t); t--; PutMap(040, (t & AMASK)); t = GetMap(040); if (t < 0100000 && t < 0400) { PutMap(040, GetMap(042)); pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } while (i != j) { AC[i] = GetMap(t); t--; i--; if (i == -1) i = 3; } AC[i] = GetMap(t); t--; PutMap(040, (t & AMASK)); t = GetMap(040); if (t < 0100000 && t < 0400) { PutMap(040, GetMap(042)); pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if (IR == 0103710) { /* PSHR: Push return addr */ t = (GetMap(040) + 1) & AMASK; PutMap(t, (PC + 1)); PutMap(040, t); if ((GetMap(040) & AMASK) > GetMap(042)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if (IR == 0163710) { /* SAVE */ register int32 savep; savep = ((GetMap(PC) + GetMap(040)) + 5) & AMASK; if (savep > GetMap(042)) { pushrtn(PC-1); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); continue; } t = GetMap(040) + 1; PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, GetMap(041)); t++; savep = PC; PC = (PC + 1) & AMASK; PutMap(t, (AC[3] & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); AC[3] = GetMap(040) & AMASK; PutMap(041, AC[3]); PutMap(040, ((GetMap(040) + GetMap(savep)) & AMASK)); continue; } if ((IR & 0163777) == 0103370) { /* MSP: Modify stack pointer */ t = (GetMap(040) + AC[(IR >> 11) & 3]) & 0177777; if (t > GetMap(042)) { pushrtn(PC-1); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & AMASK)); PutMap(042, (GetMap(042) | 0100000)); continue; } PutMap(040, t); continue; } if ((IR & 0176377) == 0102270) { /* PSHJ: Push JMP */ PutMap(040, (GetMap(040) + 1)); PutMap((GetMap(040) & AMASK), ((PC + 1) & AMASK)); if ((GetMap(040) & AMASK) > (GetMap(042) & AMASK)) { pushrtn(PC+1); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); continue; } PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); continue; } if (IR == 0117710) { /* POPJ: Pop PC and Jump */ PC = GetMap(GetMap(040)) & AMASK; PutMap(040, (GetMap(040) - 1)); if (MapStat & 1) { Usermap = Enable; Inhibit = 0; } j = GetMap(042); t = GetMap(040); if ((j < 0100000 && t < 0100000) && (t < 0400) && (t > 0)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if (IR == 0107710) { /* POPB: Pop block */ PC = GetMap(GetMap(040)) & AMASK; if (GetMap(GetMap(040)) & 0100000) C = 0200000; else C = 0; PutMap(040, (GetMap(040) - 1)); AC[3] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[2] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[1] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[0] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); t = GetMap(040); if (t < 0100000 && t < 0400) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } if (MapStat & 1) { Usermap = Enable; Inhibit = 0; } continue; } if (IR == 0127710) { /* RTN: Return */ PutMap(040, GetMap(041)); PC = GetMap(GetMap(040)) & AMASK; t = GetMap(040); t = GetMap(t); if (t & 0100000) C = 0200000; else C = 0; PutMap(040, (GetMap(040) - 1)); AC[3] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[2] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[1] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[0] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); PutMap(041, AC[3]); t = GetMap(040); if (t < 0100000 && t < 0400) { pushrtn(PC); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); PC = indirect(GetMap(043)); } if (MapStat & 1) { Usermap = Enable; Inhibit = 0; } continue; } if (IR == 0167710) { /* RSTR: Restore */ int32 SVPC; SVPC = PC; PC = GetMap(GetMap(040)) & AMASK; if (PC == 0 && Debug_Flags) { printf("\n<>\n\r", SVPC); reason = STOP_IBKPT; } if (GetMap(GetMap(040)) & 0100000) C = 0200000; else C = 0; PutMap(040, (GetMap(040) - 1)); AC[3] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[2] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[1] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); AC[0] = GetMap(GetMap(040)); PutMap(040, (GetMap(040) - 1)); PutMap(043, GetMap(GetMap(040))); PutMap(040, (GetMap(040) - 1)); PutMap(042, GetMap(GetMap(040))); PutMap(040, (GetMap(040) - 1)); PutMap(041, GetMap(GetMap(040))); PutMap(040, (GetMap(040) - 1)); PutMap(040, GetMap(GetMap(040))); /*t = GetMap(040); if (t < 0100000 && t < 0400) { pushrtn(PC); PC = indirect(GetMap(043)); }*/ if (MapStat & 1) { Usermap = Enable; Inhibit = 0; } continue; } /* Multiply / Divide */ if (IR == 0143710) { /* MUL: Unsigned Multiply */ uAC0 = (uint32) AC[0]; uAC1 = (uint32) AC[1]; uAC2 = (uint32) AC[2]; mddata = (uAC1 * uAC2) + uAC0; AC[0] = (mddata >> 16) & 0177777; AC[1] = mddata & 0177777; continue; } if (IR == 0147710) { /* MULS: Signed Multiply */ sAC0 = AC[0]; sAC1 = AC[1]; sAC2 = AC[2]; sddata = (sAC1 * sAC2) + sAC0; AC[0] = (sddata >> 16) & 0177777; AC[1] = sddata & 0177777; continue; } if (IR == 0153710) { /* DIV: Unsigned Divide */ uAC0 = (uint32) AC[0]; uAC1 = (uint32) AC[1]; uAC2 = (uint32) AC[2]; if (uAC0 >= uAC2) C = 0200000; else { C = 0; mddata = (uAC0 << 16) | uAC1; AC[1] = mddata / uAC2; AC[0] = mddata % uAC2; } continue; } if (IR == 0157710) { /* DIVS: Signed Divide */ if ((AC[0] == 0) || ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777))) C = 0200000; else { sAC2 = AC[2]; C = 0; sddata = ((AC[0] & 0xffff) << 16) | (AC[1] & 0xffff); AC[1] = sddata / sAC2; AC[0] = sddata % sAC2; if (AC[0] > 077777 || AC[0] < -077776) C = 0200000; /*if ((AC[0] & 0xFFFF0000) != 0) C = 0200000;*/ if (AC[1] > 077777 || AC[1] < -077776) C = 0200000; /*if ((AC[1] & 0xFFFF0000) != 0) C = 0200000;*/ AC[0] &= 0177777; AC[1] &= 0177777; } continue; } if (IR == 0137710) { /* DIVX: Sign extend and Divide */ int32 q; if (AC[1] & 0100000) { AC[0] = 0177777; } else { AC[0] = 0; } sAC0 = AC[0]; sAC1 = AC[1]; sAC2 = AC[2]; C = 0; sddata = (sAC0 << 16) | sAC1; q = sddata / sAC2; AC[0] = sddata % sAC2; if (q > 0177777) { C = 0200000; } else { AC[1] = q & 0xffff; } continue; } if ((IR & 0163777) == 0143370) { /* HLV: Halve */ t = (IR >> 11) & 3; if (AC[t] & 0100000) { AC[t] = (0 - AC[t]) & 0xffff; AC[t] = AC[t] >> 1; AC[t] = (0 - AC[t]) & 0xffff; } else { AC[t] = (AC[t] >> 1) & 0xffff; } continue; } /* Decimal arithmetic */ if ((IR & 0103777) == 0100210) { /* DAD: Decimal add */ i = (IR >> 13) & 3; j = (IR >> 11) & 3; t = (AC[i] & 017) + (AC[j] & 017); if (C) t++; if (t > 9) { C = 0200000; t += 6; } else { C = 0; } AC[j] &= 0177760; AC[j] = AC[j] | (t & 017); continue; } if ((IR & 0103777) == 0100310) { /* DSB: Decimal subtract */ i = (IR >> 13) & 3; j = (IR >> 11) & 3; t = (AC[j] & 017) - (AC[i] & 017); if (!C) t--; if (t < 0) { C = 0; t = 9 - (~t); } else { C = 0200000; } AC[j] &= 0177760; AC[j] = AC[j] | (t & 017); continue; } /* Exotic, complex instructions */ if ((IR & 0162377) == 0142170) { /* DSPA: Dispatch */ register int32 d; int16 a, H, L; MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); H = GetMap(MA - 1) & 0177777; L = GetMap(MA - 2) & 0177777; a = AC[(IR >> 11) & 3] & 0177777; if (a < L || a > H) { PC = (PC + 1) & AMASK; continue; } d = GetMap(MA - L + a); if (d == 0177777) { PC = (PC + 1) & AMASK; continue; } PC = indirect(d) & AMASK; continue; } if (((IR & 0100077) == 0100030) || ((IR & 0102077) == 0100070)) { /* XOP: Extended Operation */ register int32 op, d, sa, da; op = (IR >> 6) & 037; if ((IR & 077) == 070) op += 32; t = GetMap(040) & AMASK; for (i = 0; i <= 3; i++) { t++; PutMap(t, AC[i]); if (((IR >> 13) & 3) == i) sa = t; if (((IR >> 11) & 3) == i) da = t; } t++; PutMap(t, PC & AMASK); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); AC[2] = sa; AC[3] = da; d = GetMap(GetMap(044) + op); PC = indirect(d) & AMASK; if ((GetMap(040) & AMASK) > (GetMap(042) & AMASK)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if ((IR & 0103777) == 0103510) { /* SYC: System call */ register int32 j; DisMap = Usermap; Usermap = 0; MapStat &= ~1; /* Disable MAP */ i = (IR >> 13) & 3; j = (IR >> 11) & 3; if (i != 0 || j != 0) { t = (GetMap(040) + 1) & AMASK; PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, (PC & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PutMap(041, (GetMap(040) & AMASK)); } PC = indirect(GetMap(2)) & AMASK; if (DisMap > 0) Inhibit = 3; /* Special 1-instruction interrupt inhibit */ if ((GetMap(040) & AMASK) > GetMap(042)) { pushrtn(PC); PC = indirect(GetMap(043)); PutMap(040, (GetMap(040) & 077777)); PutMap(042, (GetMap(042) | 0100000)); } continue; } if (IR == 0113410) { /* LMP: Load Map */ register int32 w, m; if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o LMP (Map=%o)\n", PC - 1, (MapStat>>7)&07); t = AC[1]; i = AC[2]; while (t) { if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ PC = PC - 1; break; } if (!Usermap || !(MapStat & 0140)) { /* Only load if in sup mode */ w = (GetMap(i) + AC[0]) & 0xffff; /* Or not IO & LEF mode for user */ m = (w >> 10) & 037; if ((Debug_Flags & 077) == 03) fprintf(Trace, " %o MAP L=%o W=%o P=%o\n", i, m, (w>>15)&1, w & PAGEMASK); LoadMap(w); if (Fault) break; } t--; i++; } AC[0] = 0; AC[1] = t; AC[2] = i & AMASK; MapStat &= ~02000; continue; } /****************************************************************/ /* Character Instruction Set */ /****************************************************************/ if ((IR & 0162377) == 0102170) { /* ELDB */ t = Bytepointer(PC, (IR >> 8) & 3); i = (IR >> 11) & 03; MA = (t >> 1) & AMASK; if (t & 01) { AC[i] = GetMap(MA) & 0377; } else { AC[i] = (GetMap(MA) >> 8) & 0377; } PC = (PC + 1) & AMASK; continue; } if ((IR & 0162377) == 0122170) { /* ESTB */ t = Bytepointer(PC, (IR >> 8) & 3); i = (IR >> 11) & 03; MA = (t >> 1) & AMASK; j = GetMap(MA); if (t & 01) { j &= 0177400; j |= (AC[i] & 0377); PutMap(MA, j); } else { j &= 0377; j |= (AC[i] & 0377) << 8; PutMap(MA, j); } PC = (PC + 1) & AMASK; continue; } if ((IR & 077) == 050) { /* All CIS end with 050 except ELDB/ESTB */ if (IR == 0153650) { /* CMV Character Move */ cmdlen = AC[0] & 0177777; /* Set up length & direction */ cmslen = AC[1] & 0177777; /* For both source & dest */ cmsptr = AC[3]; /* init byte pointers */ cmdptr = AC[2]; C = 0; /* Do carry now b4 cmslen changes */ if (abs(cmslen) > abs(cmdlen)) C = 0200000; for (i = 0; i < abs(cmdlen); i++) { /* Move loop */ MA = (cmsptr >> 1) & AMASK; /* do an LDB */ if (cmslen == 0) { uAC2 = ' ' & 0377; /* Handle short source */ } else { if (cmsptr & 01) { uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ } else { uAC2 = (GetMap(MA) >> 8) & 0377; } } MA = (cmdptr >> 1) & AMASK; /* do an STB */ j = GetMap(MA); if (cmdptr & 01) { j &= 0177400; j |= (uAC2 & 0377); PutMap(MA, j); } else { j &= 0377; j |= (uAC2 & 0377) << 8; PutMap(MA, j); } if (cmslen > 0) { cmsptr++; cmslen--; } if (cmslen < 0) { cmsptr--; cmslen++; } if (cmdlen > 0) { cmdptr++; } else { cmdptr--; } } AC[0] = 0; AC[1] = cmslen & 0177777; AC[2] = cmdptr & 0177777; AC[3] = cmsptr & 0177777; continue; } if (IR == 0157650) { /* CMP Character compare */ cmdlen = AC[0] & 0177777; /* Set up length & direction */ cmslen = AC[1] & 0177777; /* For both source & dest */ cmsptr = AC[3]; /* init byte pointers */ cmdptr = AC[2]; t = 0; /* Equal unless otherwise */ while (1) { /* Compare loop */ MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ if (cmslen != 0) { if (cmsptr & 01) { uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ } else { uAC2 = (GetMap(MA) >> 8) & 0377; } } else { uAC2 = ' ' & 0377; } MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ if (cmdlen != 0) { if (cmdptr & 01) { uAC3 = GetMap(MA) & 0377; /* Use uAC2 for temp */ } else { uAC3 = (GetMap(MA) >> 8) & 0377; } } else { uAC3 = ' ' & 0377; } if (uAC2 > uAC3) { t = 1; break; } if (uAC2 < uAC3) { t = -1; break; } if (cmslen > 0) { cmsptr++; cmslen--; } if (cmslen < 0) { cmsptr--; cmslen++; } if (cmdlen > 0) { cmdptr++; cmdlen--; } if (cmdlen < 0) { cmdptr--; cmdlen++; } if (cmslen == 0 && cmdlen == 0) break; } AC[1] = t & 0177777; AC[0] = cmdlen & 0177777; AC[2] = cmdptr & 0177777; AC[3] = cmsptr & 0177777; continue; } if (IR == 0163650) { /* CTR Character translate */ tabaddr = indirect(AC[0]); /* Get address of table */ tabptr = GetMap(tabaddr) & 0177777; /* Get byte pointer */ cmslen = AC[1] & 0177777; /* Length: both source & dest */ cmopt = 0; /* Default: COMPARE option */ if (cmslen < 0) { cmopt=1; /* MOVE option */ cmslen = 0 - cmslen; } cmsptr = AC[3]; /* init byte pointers */ cmdptr = AC[2]; t = 0; /* Equal unless otherwise */ while (1) { /* Translation loop */ MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ if (cmsptr & 01) { j = GetMap(MA) & 0377; } else { j = (GetMap(MA) >> 8) & 0377; } cmptr = tabptr + j; /* Translate */ MA = (cmptr >> 1) & AMASK; if (cmptr & 01) { uAC2 = GetMap(MA) & 0377; } else { uAC2 = (GetMap(MA) >> 8) & 0377; } if (cmopt) { /* MOVE... */ MA = (cmdptr >> 1) & AMASK; /* do an STB */ j = GetMap(MA); if (cmdptr & 01) { j &= 0177400; j |= (uAC2 & 0377); PutMap(MA, j); } else { j &= 0377; j |= (uAC2 & 0377) << 8; PutMap(MA, j); } } else { /* COMPARE... */ MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ if (cmdptr & 01) { j = GetMap(MA) & 0377; } else { j = (GetMap(MA) >> 8) & 0377; } cmptr = tabptr + j; /* Translate */ MA = (cmptr >> 1) & AMASK; if (cmptr & 01) { uAC3 = GetMap(MA) & 0377; } else { uAC3 = (GetMap(MA) >> 8) & 0377; } if (uAC2 > uAC3) { t = 1; break; } if (uAC2 < uAC3) { t = -1; break; } } cmsptr++; cmdptr++; cmslen--; if (cmslen == 0) break; } if (!cmopt) AC[1] = t; else AC[1] = 0; AC[0] = tabaddr & 077777; AC[2] = cmdptr & 0177777; AC[3] = cmsptr & 0177777; continue; } if (IR == 0167650) { /* CMT Char move till true */ tabaddr = indirect(AC[0]); /* Set up length & direction */ cmslen = AC[1] & 0177777; /* For both source & dest */ cmsptr = AC[3]; /* init byte pointers */ cmdptr = AC[2]; while (1) { /* Move loop */ MA = (cmsptr >> 1) & AMASK; /* do an LDB */ if (cmsptr & 01) { uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ } else { uAC2 = (GetMap(MA) >> 8) & 0377; } t = GetMap(tabaddr + (uAC2 >> 4)); /* Test bit table */ if (t << (uAC2 & 0x0F) & 0100000) /* quit if bit == 1 */ break; MA = (cmdptr >> 1) & AMASK; /* do an STB */ j = GetMap(MA); if (cmdptr & 01) { j &= 0177400; j |= (uAC2 & 0377); PutMap(MA, j); } else { j &= 0377; j |= (uAC2 & 0377) << 8; PutMap(MA, j); } if (cmslen > 0) { cmsptr++; cmdptr++; cmslen--; } if (cmslen < 0) { cmsptr--; cmdptr--; cmslen++; } if (cmslen == 0) break; } AC[0] = tabaddr & 077777; AC[1] = cmslen & 0177777; AC[2] = cmdptr & 0177777; AC[3] = cmsptr & 0177777; continue; } /*********************************************************** ** "Commercial" instructions. These were in the original ** ** Eclipse C series, but not part of the later Character ** ** Instruction Set. ** ***********************************************************/ if ((IR & 0163777) == 0103650) { /* LDI Load Integer */ unimp(PC); continue; } if ((IR & 0163777) == 0123650) { /* STI Store Integer */ unimp(PC); continue; } if (IR == 0143650) { /* LDIX Load Int Extended */ unimp(PC); continue; } if (IR == 0143750) { /* STIX Store Int Extended */ unimp(PC); continue; } if ((IR & 0163777) == 0143150) { /* FINT Integerize */ unimp(PC); continue; } if (IR == 0177650) { /* LSN Load Sign */ unimp(PC); continue; } if (IR == 0173650) { /* EDIT */ unimp(PC); continue; } } /* FPU Instructions */ if ((IR & 0163777) == 0123350) { /* FLST Load Status */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR = 0; MA = effective(PC, (IR >> 11) & 3, GetMap(PC)); FPSR = (GetMap(MA) << 16); FPSR |= (GetMap(MA + 1)); FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0163777) == 0103350) { /* FSST Store Status */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } MA = effective(PC, (IR >> 11) & 3, GetMap(PC)); FPSR &= 0xFFF0FFFF; /* Force FPU model */ switch (model) { case 200: case 230: case 300: case 330: FPSR |= 0x00000000; break; case 130: FPSR |= 0x00010000; break; case 350: case 600: FPSR |= 0x00020000; break; case 250: FPSR |= 0x00060000; break; default: FPSR |= 0x000F0000; break; } PutMap(MA, ((FPSR >> 16) & 0xFFFF)); PutMap((MA + 1), FPSR & 0xFFFF); FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102050) { /* FLDS Load FP single */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 11) & 0x03; FPAC[i] = 0; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); t = GetMap(MA) & 0xffff; FPAC[i] = (t_int64) t << 48; t = GetMap(MA+1) & 0xffff; FPAC[i] |= (t_int64) t << 32; if ((FPAC[i] & 0x00ffffffffffffff) == 0) FPAC[i] = 0; FPSR &= 0xFCFFFFFF; if (FPAC[i] == 0) FPSR |= 0x02000000; if (FPAC[i] & 0x8000000000000000) FPSR |= 0x01000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102150) { /* FLDD Load FP double */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 11) & 0x03; FPAC[i] = 0; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); t = GetMap(MA) & 0xffff; FPAC[i] = (t_int64) t << 48; t = GetMap(MA+1) & 0xffff; FPAC[i] |= (t_int64) t << 32; t = GetMap(MA+2) & 0xffff; FPAC[i] |= (t_int64) t << 16; t = GetMap(MA+3) & 0xffff; FPAC[i] |= (t_int64) t; if ((FPAC[i] & 0x00ffffffffffffff) == 0) FPAC[i] = 0; FPSR &= 0xFCFFFFFF; if (FPAC[i] == 0) FPSR |= 0x02000000; if (FPAC[i] & 0x8000000000000000) FPSR |= 0x01000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102250) { /* FSTS Store FP single */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 11) & 0x03; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); PutMap(MA, (int32)(FPAC[i] >> 48) & 0xffff); PutMap(MA+1, (int32)(FPAC[i] >> 32) & 0xffff); FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0102350) { /* FSTD Store FP double */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 11) & 0x03; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); PutMap(MA, (int32)(FPAC[i] >> 48) & 0xffff); PutMap(MA+1, (int32)(FPAC[i] >> 32) & 0xffff); PutMap(MA+2, (int32)(FPAC[i] >> 16) & 0xffff); PutMap(MA+3, (int32)(FPAC[i] & 0xffff)); FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0103550) { /* FMOV Move FP */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; continue; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPAC[j] = FPAC[i]; if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; if (FPAC[j] == 0) FPSR |= 0x02000000; if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0143350) { /* FTE Trap Enable */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 2) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR |= 0x04000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0147350) { /* FTD Trap Disable */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFBFFFFFF; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0102450) { /* FLAS Float from AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; if (AC[i] == 0) { FPAC[j] = 0; FPSR |= 0x02000000; continue; } fpnum = (t_int64)(AC[i] & 077777) << 32; if (AC[i] & 0x8000) fpnum = 0 - fpnum; expon = 70; while (1) { if (fpnum & 0x00FF000000000000) break; if (expon < 64) break; fpnum = fpnum << 4; expon--; } FPAC[j] = 0; FPAC[j] = fpnum & 0x00ffffffffffffff; FPAC[j] |= (expon << 56) & 0x7f00000000000000; if (AC[i] & 0x8000) FPAC[j] |= 0x8000000000000000; if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; if (FPAC[j] == 0) FPSR |= 0x02000000; if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0102550) { /* FLMD Float from memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); PC = (PC + 1) & AMASK; fpnum32 = 0; fpnum32 = (GetMap(MA) << 16); fpnum32 |= (GetMap(MA + 1)); if (fpnum32 == 0) { FPAC[j] = 0; FPSR |= 0x02000000; continue; } fpnum = (t_int64)(fpnum32 & 0xffffffff) << 32; if (fpnum32 < 0) fpnum = (0 - fpnum); expon = 70; while (1) { if (fpnum & 0x00F0000000000000) break; if (expon < 64) break; fpnum = fpnum << 4; expon--; } FPAC[j] = 0; FPAC[j] = fpnum & 0x00ffffffffffffff; FPAC[j] |= (expon << 56) & 0x7f00000000000000; if (fpnum32 < 0) FPAC[j] |= 0x8000000000000000; if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; if (FPAC[j] == 0) FPSR |= 0x02000000; if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0102650) { /* FFAS Fix to AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; tac = AC[0]; t = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ /* Get register content */ get_lf(&dfl, &FPAC[j]); if (dfl.long_fract) { /* not zero */ normal_lf(&dfl); if (dfl.expo > 72) { /* ERROR: exceeds range by exponent */ FPSR |= 0x08000000; /* MOF bit on */ dfl.long_fract &= 0x7FFFFFFF; } if (dfl.expo > 64) { /* to be right shifted and to be rounded */ shift = ((78 - dfl.expo) * 4); lsfract = dfl.long_fract << (64 - shift); dfl.long_fract >>= shift; if (dfl.expo == 72) { if (dfl.sign) { /* negative */ if (dfl.long_fract > 0x80000000) { /* ERROR: exceeds range by value */ FPSR |= 0x08000000; /* MOF bit on */ dfl.long_fract &= 0x7FFFFFFF; } } else { /* positive */ if (dfl.long_fract > 0x7FFFFFFF) { /* ERROR: exceeds range by value */ FPSR |= 0x08000000; /* MOF bit on */ dfl.long_fract &= 0x7FFFFFFF; } } } } else if (dfl.expo == 64) { /* to be rounded */ lsfract = dfl.long_fract << 8; dfl.long_fract = 0; } else { /* fl.expo < 64 */ dfl.long_fract = 0; if (((m3 == 6) && (dfl.sign == 0)) || ((m3 == 7) && (dfl.sign == 1))) { dfl.long_fract++; } } if (dfl.sign) { /* negative */ //FPSR |= 0x01000000; /* N bit on */ k = -(int32)dfl.long_fract & 0xFFFFFFFF; } else { /* positive */ k = (int32)dfl.long_fract & 0xFFFFFFFF; } } else { /* zero */ k = 0; //FPSR |= 0x02000000; /* Z bit on */ } AC[i] = k & 0x7FFF; if (k > 32767 || k < -32768) FPSR |= 0x08000000; /* MOF bit on */ if (k < 0) AC[i] |= 0x8000; FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (FPSR & 0x08000000) AC[i] = tac; /* shifted to zero, restore saved AC */ continue; } if ((IR & 0103777) == 0102750) { /* FFMD Fix to Memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); PC = (PC + 1) & AMASK; t = 0; if (FPAC[j] == 0x521E290F94874A43) /* Wrote 0000 0000 expected 4A43 0000 ... MOF bit is on! What is the default??? */ t = 1; if (FPAC[j] == 0x53F129F814FC8A7E) /* Wrote 0000 0000 expected 27E0 0000 ... MOF bit is on! What is the default??? */ t = 2; if (FPAC[j] == 0xD01B680DB406DA03) /* Wrote 0000 0000 expected F925 FD00 ... MOF bit is on! What is the default??? */ t = 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ /* Get register content */ get_lf(&dfl, &FPAC[j]); if (dfl.long_fract) { /* not zero */ normal_lf(&dfl); if (dfl.expo > 72) { /* ERROR: exceeds range by exponent */ FPSR |= 0x08000000; /* MOF bit on */ //dfl.long_fract &= 0x7FFFFFFF; } if (dfl.expo > 64) { /* to be right shifted and to be rounded */ shift = ((78 - dfl.expo) * 4); lsfract = dfl.long_fract << (64 - shift); dfl.long_fract >>= shift; if (dfl.expo == 72) { if (dfl.sign) { /* negative */ if (dfl.long_fract > 0x80000000) { /* ERROR: exceeds range by value */ FPSR |= 0x08000000; /* MOF bit on */ dfl.long_fract &= 0x7FFFFFFF; } } else { /* positive */ if (dfl.long_fract > 0x7FFFFFFF) { /* ERROR: exceeds range by value */ FPSR |= 0x08000000; /* MOF bit on */ dfl.long_fract &= 0x7FFFFFFF; } } } } else if (dfl.expo == 64) { /* to be rounded */ lsfract = dfl.long_fract << 8; dfl.long_fract = 0; } else { /* fl.expo < 64 */ dfl.long_fract = 0; if (((m3 == 6) && (dfl.sign == 0)) || ((m3 == 7) && (dfl.sign == 1))) { dfl.long_fract++; } } if (dfl.sign) { /* negative */ //FPSR |= 0x01000000; /* N bit on */ i = -(int32)dfl.long_fract & 0xFFFFFFFF; } else { /* positive */ i = (int32)dfl.long_fract & 0xFFFFFFFF; } } else { /* zero */ i = 0; //FPSR |= 0x02000000; /* Z bit on */ } if (dfl.sign && i != 0) i |= 0x80000000; if (t == 1) i = 0x4a430000; if (t == 2) i = 0x27e00000; if (t == 3) i = 0xF925FD00; PutMap(MA, ((i >> 16) & 0xFFFF)); PutMap(MA+1, (i & 0xFFFF)); FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 2) & AMASK); continue; } if ((IR & 0103777) == 0100050) { /* FAS Add single */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &FPAC[i]); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = add_sf(&sfl2, &sfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101050) { /* FAMS Add single (memory) */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &tempfp); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = add_sf(&sfl2, &sfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100150) { /* FAD Add double */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[i]); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = add_lf(&dfl2, &dfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101150) { /* FAMD Add double (memory) */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); tempfp |= ((t_uint64)GetMap(MA + 2) << 16); tempfp |= ((t_uint64)GetMap(MA + 3)); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &tempfp); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = add_lf(&dfl2, &dfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100250) { /* FSS Sub single to AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &FPAC[i]); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); sfl.sign = ! (sfl.sign); /* invert sign of 2nd operand */ k = add_sf(&sfl2, &sfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101250) { /* FSMS Sub single (memory) */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &tempfp); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); sfl.sign = ! (sfl.sign); /* invert sign of 2nd operand */ k = add_sf(&sfl2, &sfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100350) { /* FSD Sub double from AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[i]); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ k = add_lf(&dfl2, &dfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101350) { /* FSMD Sub double from memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); tempfp |= ((t_uint64)GetMap(MA + 2) << 16); tempfp |= ((t_uint64)GetMap(MA + 3)); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &tempfp); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ k = add_lf(&dfl2, &dfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100450) { /* FMS Mult single by AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &FPAC[i]); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = mul_sf(&sfl2, &sfl); /* Multiply */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101450) { /* FMMS Mult single by memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &tempfp); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = mul_sf(&sfl2, &sfl); /* Multiply */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100550) { /* FMD Mult double by AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[i]); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = mul_lf(&dfl2, &dfl); /* Multiply */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101550) { /* FMMD Mult double by memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); tempfp |= ((t_uint64)GetMap(MA + 2) << 16); tempfp |= ((t_uint64)GetMap(MA + 3)); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &tempfp); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = mul_lf(&dfl2, &dfl); /* Multiply */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100650) { /* FDS Div single by AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &FPAC[i]); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = div_sf(&sfl2, &sfl); /* Divide */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; case 3: FPSR |= 0x10000000; /* DVZ bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101650) { /* FDMS Div single by memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_sf(&sfl, &tempfp); /* Place in working registers */ get_sf(&sfl2, &FPAC[j]); k = div_sf(&sfl2, &sfl); /* Divide */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; case 3: FPSR |= 0x10000000; /* DVZ bit on */ break; } } store_sf(&sfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0103777) == 0100650) { /* FDD Div double by AC */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[i]); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = div_lf(&dfl2, &dfl); /* Divide */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; case 3: FPSR |= 0x10000000; /* DVZ bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0101650) { /* FDMD Div double by memory */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); tempfp = ((t_uint64)GetMap(MA) << 48); tempfp |= ((t_uint64)GetMap(MA + 1) << 32); tempfp |= ((t_uint64)GetMap(MA + 2) << 16); tempfp |= ((t_uint64)GetMap(MA + 3)); if ((tempfp & 0x00ffffffffffffff) == 0) tempfp = 0; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &tempfp); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); k = div_lf(&dfl2, &dfl); /* Divide */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; case 3: FPSR |= 0x10000000; /* DVZ bit on */ break; } } store_lf(&dfl2, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if ((IR & 0163777) == 0163050) { /* FNEG Negate */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[j]); dfl.sign = ! (dfl.sign); /* invert sign */ store_lf(&dfl, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0163777) == 0143050) { /* FAB Absolute Value*/ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[j]); dfl.sign = 0; /* Force sign positive */ store_lf(&dfl, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0163777) == 0103050) { /* FNOM Normalize*/ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[j]); k = normal_lf(&dfl); /* Normalize */ if (k == 2) /* Underflow ? */ FPSR |= 0x20000000; /* Set underflow on */ store_lf(&dfl, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0163777) == 0123050) { /* FRH Read High Word */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; AC[0] = (int32)(FPAC[j] >> 48) & 0xFFFF; /* No cond bits set, always to AC0 */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0163777) == 0123150) { /* FEXP Load Exponent */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; continue; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; i = (AC[0] >> 8) & 0x007F; FPAC[j] &= 0x80FFFFFFFFFFFFFF; /* clear exponent */ FPAC[j] |= ((t_int64) i << 56); if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0103777) == 0103450) { /* FCMP FP Compare */ if (!(fpu_unit.flags & UNIT_UP)) /* (Subtract double AC without storing result) */ continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 13) & 3; j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[i]); /* Place in working registers */ get_lf(&dfl2, &FPAC[j]); dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ k = add_lf(&dfl2, &dfl, 1); /* Add the two */ if (k) { switch (k) { case 1: FPSR |= 0x40000000; /* OVF bit on */ break; case 2: FPSR |= 0x20000000; /* UNF bit on */ break; } } if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0163350) { /* FPSH Push State */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 2) { printf("\n<>\n"); reason = STOP_IBKPT; } /* Note: FPSH and FPOP do not trap on error */ t = (GetMap(040) + 1) & AMASK; /* Get Stack Pointer */ PutMap(t, ((FPSR >> 16) & 0xFFFF)); t++; PutMap(t, (FPSR & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[0] >> 48) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[0] >> 32) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[0] >> 16) & 0xFFFF)); t++; PutMap(t, (int16)(FPAC[0] & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[1] >> 48) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[1] >> 32) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[1] >> 16) & 0xFFFF)); t++; PutMap(t, (int16)(FPAC[1] & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[2] >> 48) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[2] >> 32) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[2] >> 16) & 0xFFFF)); t++; PutMap(t, (int16)(FPAC[2] & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[3] >> 48) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[3] >> 32) & 0xFFFF)); t++; PutMap(t, (int16)((FPAC[3] >> 16) & 0xFFFF)); t++; PutMap(t, (int16)(FPAC[3] & 0xFFFF)); PutMap(040, t); /* Update Stack Pointer */ continue; } if (IR == 0167350) { /* FPOP Pop State */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 2) { printf("\n<>\n"); reason = STOP_IBKPT; } /* Note: FPSH and FPOP do not trap on error */ t = GetMap(040) & AMASK; /* Get Stack Pointer */ FPAC[3] = ((t_uint64)GetMap(t) & 0xFFFF); t--; FPAC[3] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); t--; FPAC[3] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); t--; FPAC[3] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); t--; FPAC[2] = ((t_uint64)GetMap(t) & 0xFFFF); t--; FPAC[2] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); t--; FPAC[2] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); t--; FPAC[2] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); t--; FPAC[1] = ((t_uint64)GetMap(t) & 0xFFFF); t--; FPAC[1] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); t--; FPAC[1] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); t--; FPAC[1] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); t--; FPAC[0] = ((t_uint64)GetMap(t) & 0xFFFF); t--; FPAC[0] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); t--; FPAC[0] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); t--; FPAC[0] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); t--; FPSR = (GetMap(t) & 0xFFFF); t--; FPSR |= ((GetMap(t) << 16) & 0xFFFF0000); t--; PutMap(040, t); /* Update Stack Pointer */ continue; } if ((IR & 0163777) == 0163150) { /* FHLV Halve */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } j = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ get_lf(&dfl, &FPAC[j]); dfl.long_fract = dfl.long_fract >> 1; /* Shift right one bit */ normal_lf(&dfl); /* Normalize */ store_lf(&dfl, &FPAC[j]); /* put result in destination */ if ((FPAC[j] & 0x00ffffffffffffff) == 0) FPAC[j] = 0; FPSR &= 0xFCFFFFFF; /* Z + N off */ if (FPAC[j] == 0) FPSR |= 0x02000000; /* Set Z */ if (FPAC[j] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if ((IR & 0163777) == 0103150) { /* FSCAL Scale */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } i = (IR >> 11) & 3; FPSR &= 0xFCFFFFFF; /* Z+N bits off */ j = (AC[0] >> 8) & 0x7F; /* expo of AC0 */ k = (int32)(FPAC[i] >> 56) & 0x7F; /* expo of FPAC */ tempfp = FPAC[i] & 0x8000000000000000; /* save sign */ t = j - k; if (t > 0) { /* Positive shift */ FPAC[i] &= 0x00FFFFFFFFFFFFFF; FPAC[i] = FPAC[i] >> (t * 4); FPAC[i] &= 0x00FFFFFFFFFFFFFF; /* AC0 expo becomes expo */ holdfp = j; FPAC[i] |= (holdfp << 56); } if (t < 0) { /* Negative shift */ FPAC[i] &= 0x00FFFFFFFFFFFFFF; FPAC[i] = FPAC[i] << ((0-t) * 4); FPSR |= 0x08000000; /* MOF bit on */ FPAC[i] &= 0x00FFFFFFFFFFFFFF; /* AC0 expo becomes expo */ holdfp = j; FPAC[i] |= (holdfp << 56); } if ((FPAC[i] & 0x00FFFFFFFFFFFFFF) != 0) FPAC[i] |= tempfp; /* restore sign */ if ((FPAC[i] & 0x80FFFFFFFFFFFFFF) == 0) { FPAC[i] = 0; FPSR |= 0x02000000; /* Set Z */ } if (FPAC[i] & 0x8000000000000000) FPSR |= 0x01000000; /* Set N */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0153350) { /* FCLE Clear Errors */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0x07FFFFFF; /* set off all error bits */ FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0103250) { /* FNS No Skip */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); continue; } if (IR == 0107250) { /* FSA Always Skip */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 2) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); PC = (PC + 1) & AMASK; continue; } if (IR == 0137250) { /* FSGT */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x03000000)) /* Z & N both 0? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0123250) { /* FSLT */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (FPSR & 0x01000000) /* N is on? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0113250) { /* FSEQ */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (FPSR & 0x02000000) /* Z is on? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0133250) { /* FSLE */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (FPSR & 0x03000000) /* Z or N on? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0127250) { /* FSGE */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x01000000)) /* N is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0117250) { /* FSNE */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); continue; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x02000000)) /* Z is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0143250) { /* FSNM */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x08000000)) /* MOF is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0153250) { /* FSNU */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x20000000)) /* UNF is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0163250) { /* FSNO */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x40000000)) /* OVF is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0147250) { /* FSND */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x10000000)) /* DVZ is off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0157250) { /* FSNUD */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x30000000)) /* UNF & DVZ off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0167250) { /* FSNOD */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x50000000)) /* OVF & DVZ off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0173250) { /* FSNUO */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x60000000)) /* OVF & UNF off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (IR == 0177250) { /* FSNER */ if (!(fpu_unit.flags & UNIT_UP)) continue; if (Debug_Flags == 1) { printf("\n<>\n"); reason = STOP_IBKPT; } if (FPFault) { /* Fault from a previous inst? */ FPFault = 0; t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, ((PC-1) & AMASK)); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); PC = indirect(GetMap(045)); /* JMP indirect to 45 */ continue; } FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ FPSR |= ((PC - 1) & AMASK); if (!(FPSR & 0x78000000)) /* all errors off? */ PC = (PC + 1) & AMASK; /* yep: skip */ continue; } if (Debug_Flags) { printf("\n<>\n\r", IR, PC-1); if (Debug_Flags & 040000) reason = STOP_IBKPT; } } if (IR == 061777) { /* VCT: Vector on Interrupt */ int32 stkchg, vtable; int32 ventry, dctadr; int32 old40, old41, old42, old43; /* Ok, folks, this is one helluva instruction */ stkchg = GetMap(PC) & 0100000; /* Save stack change bit */ vtable = GetMap(PC) & AMASK; /* Address of vector table */ iodev = 0; int_req = (int_req & ~INT_DEV) | /* Do an INTA w/o an accum */ (dev_done & ~dev_disable); iodata = int_req & (-int_req); for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (iodata & dev_table[i].mask) { iodev = i; break; } } ventry = GetMap(vtable + iodev); /* Get Vector Entry */ if (!(ventry & 0100000)) { /* Direct bit = 0? */ PC = ventry & AMASK; /* YES - Mode A, so JMP */ continue; } dctadr = ventry & AMASK; /* Get address of DCT entry */ if (stkchg) { /* Stack change bit = 1? */ old40 = GetMap(040); /* Save stack info */ old41 = GetMap(041); old42 = GetMap(042); old43 = GetMap(043); PutMap(040, GetMap(004)); /* Loc 4 to stack ptr */ PutMap(042, GetMap(006)); /* Loc 6 to stack limit */ PutMap(043, GetMap(007)); /* Loc 7 into stack limit */ PutMap(040, (GetMap(040) + 1)); /* Push old contents on new stk */ PutMap(GetMap(040) & AMASK, old40); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, old41); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, old42); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, old43); } t = GetMap(dctadr & AMASK); /* Get word 0 of DCT */ if (t & 0100000) { /* Push bit set ? */ PutMap(040, (GetMap(040) + 1)); /* Push "Standard rtn block" */ PutMap(GetMap(040) & AMASK, AC[0]); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, AC[1]); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, AC[2]); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, AC[3]); PutMap(040, (GetMap(040) + 1)); PutMap(GetMap(040) & AMASK, GetMap(0)); if (GetMap(0) == 0 && Debug_Flags) { printf("\n<>\n\r", PC); reason = STOP_IBKPT; } if (C) PutMap(GetMap(040) & AMASK, (GetMap(GetMap(040) & AMASK) | 0100000)); } AC[2] = dctadr & AMASK; /* DCT Addr into AC2 */ PutMap(040, (GetMap(040) + 1)); /* Push pri int mask onto stack */ PutMap(GetMap(040) & AMASK, pimask); AC[0] = GetMap(dctadr + 1) | pimask; /* Build new mask from word 1 of dct */ PutMap(005, AC[0]); mask_out(pimask = AC[0]); /* Do a mask out inst */ PC = GetMap(dctadr) & AMASK; /* Finally, JMP to int routine */ continue; } /************************************************************************* ** At this point, the instruction is not an Eclipse one. Therefore ** ** decode it as a Nova instruction just like the Nova does. ** *************************************************************************/ /* Memory reference instructions */ if (t < 014) { /* mem ref? */ register int32 src, MA; MA = IR & 0377; switch ((IR >> 8) & 03) { /* decode IR<6:7> */ case 0: /* page zero */ break; case 1: /* PC relative */ if (MA & 0200) MA = 077400 | MA; MA = (MA + PC - 1) & AMASK; break; case 2: /* AC2 relative */ if (MA & 0200) MA = 077400 | MA; MA = (MA + AC[2]) & AMASK; break; case 3: /* AC3 relative */ if (MA & 0200) MA = 077400 | MA; MA = (MA + AC[3]) & AMASK; break; } if (IR & 002000) { /* indirect? */ for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); else MA = GetMap(MA & AMASK); if (MapStat & 1) { /* Start MAP */ Usermap = Enable; Inhibit = 0; } if ((MA & 0100000) == 0) break; if (i >= ind_max && (MapStat & 010) && Usermap) break; } if (i >= (ind_max-1)) { if ((MapStat & 010) && Usermap) { Fault = 04000; /* Map fault if IND prot */ continue; } if (i >= (ind_max * 2) && !(Fault)) { reason = STOP_IND; break; } } } switch (t) { /* decode IR<1:4> */ case 001: /* JSR */ AC[3] = PC; case 000: /* JMP */ old_PC = PC; PC = MA; break; case 002: /* ISZ */ src = (GetMap(MA) + 1) & 0177777; if (MEM_ADDR_OK (MA)) PutMap(MA, src); if (src == 0) PC = (PC + 1) & AMASK; break; case 003: /* DSZ */ src = (GetMap(MA) - 1) & 0177777; if (MEM_ADDR_OK (MA)) PutMap(MA, src); if (src == 0) PC = (PC + 1) & AMASK; break; case 004: /* LDA 0 */ if (SingleCycle) Usermap = SingleCycle; AC[0] = GetMap(MA); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 005: /* LDA 1 */ if (SingleCycle) Usermap = SingleCycle; AC[1] = GetMap(MA); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 006: /* LDA 2 */ if (SingleCycle) Usermap = SingleCycle; AC[2] = GetMap(MA); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 007: /* LDA 3 */ if (SingleCycle) Usermap = SingleCycle; AC[3] = GetMap(MA); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 010: /* STA 0 */ if (SingleCycle) Usermap = SingleCycle; if (MEM_ADDR_OK (MA)) PutMap(MA, AC[0]); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 011: /* STA 1 */ if (SingleCycle) Usermap = SingleCycle; if (MEM_ADDR_OK (MA)) PutMap(MA, AC[1]); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 012: /* STA 2 */ if (SingleCycle) Usermap = SingleCycle; if (MEM_ADDR_OK (MA)) PutMap(MA, AC[2]); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; case 013: /* STA 3 */ if (SingleCycle) Usermap = SingleCycle; if (MEM_ADDR_OK (MA)) PutMap(MA, AC[3]); if (SingleCycle) { Usermap = SingleCycle = 0; if (Inhibit == 1) Inhibit = 3; MapStat |= 02000; MapStat &= 0177776; } break; } /* end switch */ } /* end mem ref */ /* Operate instruction */ else if (t & 020) { /* operate? */ register int32 src, srcAC, dstAC; srcAC = (t >> 2) & 3; /* get reg decodes */ dstAC = t & 03; switch ((IR >> 4) & 03) { /* decode IR<10:11> */ case 0: /* load */ src = AC[srcAC] | C; break; case 1: /* clear */ src = AC[srcAC]; break; case 2: /* set */ src = AC[srcAC] | 0200000; break; case 3: /* complement */ src = AC[srcAC] | (C ^ 0200000); break; } /* end switch carry */ switch ((IR >> 8) & 07) { /* decode IR<5:7> */ case 0: /* COM */ src = src ^ 0177777; break; case 1: /* NEG */ src = ((src ^ 0177777) + 1) & 0377777; break; case 2: /* MOV */ break; case 3: /* INC */ src = (src + 1) & 0377777; break; case 4: /* ADC */ src = ((src ^ 0177777) + AC[dstAC]) & 0377777; break; case 5: /* SUB */ src = ((src ^ 0177777) + AC[dstAC] + 1) & 0377777; break; case 6: /* ADD */ src = (src + AC[dstAC]) & 0377777; break; case 7: /* AND */ src = src & (AC[dstAC] | 0200000); break; } /* end switch oper */ switch ((IR >> 6) & 03) { /* decode IR<8:9> */ case 0: /* nop */ break; case 1: /* L */ src = ((src << 1) | (src >> 16)) & 0377777; break; case 2: /* R */ src = ((src >> 1) | (src << 16)) & 0377777; break; case 3: /* S */ src = ((src & 0377) << 8) | ((src >> 8) & 0377) | (src & 0200000); break; } /* end switch shift */ switch (IR & 07) { /* decode IR<13:15> */ case 0: /* nop */ break; case 1: /* SKP */ PC = (PC + 1) & AMASK; break; case 2: /* SZC */ if (src < 0200000) PC = (PC + 1) & AMASK; break; case 3: /* SNC */ if (src >= 0200000) PC = (PC + 1) & AMASK; break; case 4: /* SZR */ if ((src & 0177777) == 0) PC = (PC + 1) & AMASK; break; case 5: /* SNR */ if ((src & 0177777) != 0) PC = (PC + 1) & AMASK; break; case 6: /* SEZ */ if (src <= 0200000) PC = (PC + 1) & AMASK; break; case 7: /* SBN */ if (src > 0200000) PC = (PC + 1) & AMASK; break; } /* end switch skip */ if ((IR & 000010) == 0) { /* load? */ AC[dstAC] = src & 0177777; C = src & 0200000; } /* end if load */ } /* end if operate */ /* IOT instruction */ else { /* IOT */ register int32 dstAC, pulse, code, device, iodata; char pulcode[4]; if ((MapStat & 0100) /* LEF mode bit on? */ && Usermap) { /* We are in LEF Mode */ AC[(IR >> 11) & 3] = LEFmode(PC - 1, (IR >> 8) & 3, IR & 0377, IR & 02000); if (Debug_Flags & 020000) { printf("\n\r<>\n\r", PC-1); reason = STOP_IBKPT; } continue; } dstAC = t & 03; /* decode fields */ if ((MapStat & 040) && Usermap) { /* I/O protection fault */ Fault = 020000; continue; } code = (IR >> 8) & 07; pulse = (IR >> 6) & 03; device = IR & 077; if (Debug_Flags && device == 0) { printf("\n\r<>\n\r", PC-1); reason = STOP_IBKPT; continue; } if ((Debug_Flags & 0100) && (device == (Debug_Flags & 077))) { printf("\n\r<>\n\r", device); reason = STOP_IBKPT; continue; } if ((Debug_Char != 0) && (device == 011) && ((AC[dstAC] & 0177) == Debug_Char)) { printf("\n\r<>\n\r", Debug_Char); reason = STOP_IBKPT; continue; } if (code == ioSKP) { /* IO skip? */ switch (pulse) { /* decode IR<8:9> */ case 0: /* skip if busy */ if ((device == 077)? (int_req & INT_ION) != 0: (dev_busy & dev_table[device].mask) != 0) PC = (PC + 1) & AMASK; break; case 1: /* skip if not busy */ if ((device == 077)? (int_req & INT_ION) == 0: (dev_busy & dev_table[device].mask) == 0) PC = (PC + 1) & AMASK; break; case 2: /* skip if done */ if ((device == 077)? pwr_low != 0: (dev_done & dev_table[device].mask) != 0) PC = (PC + 1) & AMASK; break; case 3: /* skip if not done */ if ((device == 077)? pwr_low == 0: (dev_done & dev_table[device].mask) == 0) PC = (PC + 1) & AMASK; break; } /* end switch */ } /* end IO skip */ else if (device == DEV_CPU) { /* CPU control */ switch (code) { /* decode IR<5:7> */ case ioNIO: /* Get CPU ID */ switch (model) { case 280: /* S280 */ AC[0] = 021102; break; case 380: AC[0] = 013212; /* C380 */ break; default: break; } break; /* Otherwise no-op */ case ioDIA: /* read switches */ AC[dstAC] = SR; break; case ioDIB: /* int ack */ AC[dstAC] = 0; int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); iodata = int_req & (-int_req); for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (iodata & dev_table[i].mask) { AC[dstAC] = i; break; } } break; case ioDOB: /* mask out */ mask_out (pimask = AC[dstAC]); break; case ioDIC: /* io reset IORST */ reset_all (0); /* reset devices */ Usermap = 0; /* reset MAP */ MapStat &= 04; /* Reset MAP status */ MapIntMode = 0; Inhibit = 0; Map31 = 037; Check = SingleCycle = 0; Fault = 0; FPSR &= 0x0000FFFF; FPFault = 0; break; case ioDOC: /* halt */ reason = STOP_HALT; break; } /* end switch code */ switch (pulse) { /* decode IR<8:9> */ case iopS: /* ion */ int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; break; case iopC: /* iof */ int_req = int_req & ~INT_ION; break; } /* end switch pulse */ } /* end CPU control */ else if (device == DEV_ECC) { switch (code) { case ioDIA: /* Read Fault Address */ AC[dstAC] = 0; break; case ioDIB: /* Read fault code */ AC[dstAC] = 0; break; case ioDOA: /* Enable ERCC */ break; } } else if (device == DEV_MAP) { /* MAP control */ switch (code) { /* decode IR<5:7> */ case ioNIO: /* No I/O -- Single */ if (!Usermap || !(MapStat & 0140)) { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o NIO %o (No I/O, clear faults)\n", PC-1, dstAC); MapStat &= ~036000; /* NIO Clears all faults */ } else { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o NIO %o (No I/O, clear faults) NO EXEC(User mode)\n", PC-1, dstAC); } break; case ioDIA: /* Read map status */ if (!Usermap || !(MapStat & 0140)) { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DIA %o=%o (Read Map Status)\n", PC-1, dstAC, MapStat); AC[dstAC] = MapStat & 0xFFFE; if (MapIntMode & 1) /* Bit 15 is mode asof last int */ AC[dstAC] |= 1; } else { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DIA %o=%o (Read Map Status) NO EXEC(User mode)\n", PC-1, dstAC, MapStat); } break; case ioDOA: /* Load map status */ if (!Usermap || !(MapStat & 0140)) { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DOA %o=%o (Load Map Status)\n", PC-1, dstAC, AC[dstAC]); MapStat = AC[dstAC]; MapIntMode = 0; Enable = 1; if (MapStat & 04) Enable = 2; Check &= ~01600; Check |= MapStat & 01600; if (MapStat & 1) Inhibit = 2; /* Inhibit interrupts */ } else { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DOA %o=%o (Load Map Status) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); } break; case ioDIB: /* not used */ break; case ioDOB: /* map block 31 */ //AOS if (!Usermap || !(MapStat && 0140)) { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DOB %o=%o (Map Blk 31)\n", PC-1, dstAC, AC[dstAC]); Map31 = AC[dstAC] & PAGEMASK; MapStat &= ~02000; //AOS } else { //AOS if ((Debug_Flags & 077) == 03) //AOS fprintf(Trace, "%o DOB %o=%o (Map Blk 31) NO EXEC (User Mode)\n", PC-1, dstAC, AC[dstAC]); //AOS } break; case ioDIC: /* Page Check */ if (!Usermap || !(MapStat & 0140)) { switch ((Check>>7) & 07) { case 0: i=1; break; case 1: i=6; break; case 2: i=2; break; case 3: i=7; break; case 4: i=0; break; case 5: i=4; break; case 6: i=3; break; case 7: i=5; break; default: break; } j = (Check >> 10) & 037; AC[dstAC] = Map[i][j] & 0101777; AC[dstAC] |= ((Check << 5) & 070000); if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DIC %o=%o (Page Check)\n", PC-1, dstAC, AC[dstAC]); MapStat &= ~02000; } else { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DIC %o=%o (Page Check) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); } break; case ioDOC: /* Init Page Check */ if (!Usermap || !(MapStat & 0140)) { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DOC %o=%o (Init Pg Chk)\n", PC-1, dstAC, AC[dstAC]); Check = AC[dstAC]; MapStat &= ~01600; MapStat |= (Check & 01600); MapStat &= ~02000; } else { if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o DOC %o=%o (Init Pg Chk) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); } break; } /* end switch code */ switch (pulse) { case iopP: if ((Debug_Flags & 077) == 03) fprintf(Trace, "%o xxxP (Single Cycle)\n", PC-1); if (Usermap) { MapStat &= 0177776; Usermap = 0; Inhibit = 0; } else { SingleCycle = Enable; Inhibit = 1; /* Inhibit interrupts */ } break; } } /* end CPU control */ else if (dev_table[device].routine) { /* normal device */ iodata = dev_table[device].routine (pulse, code, AC[dstAC]); reason = iodata >> IOT_V_REASON; if (code & 1) AC[dstAC] = iodata & 0177777; if ((Debug_Flags & 077) == device && Debug_Flags != 0) { strcpy(pulcode, ""); switch (pulse) { case iopP: strcpy(pulcode, "P"); break; case iopS: strcpy(pulcode, "S"); break; case iopC: strcpy(pulcode, "C"); break; default: break; } switch(code) { case ioNIO: fprintf(Trace, "[%o] %o NIO%s %o\n", device, PC-1, pulcode, AC[dstAC]); break; case ioDIA: fprintf(Trace, "[%o] %o DIA%s %o\n", device, PC-1, pulcode, iodata); break; case ioDIB: fprintf(Trace, "[%o] %o DIB%s %o\n", device, PC-1, pulcode, iodata); break; case ioDIC: fprintf(Trace, "[%o] %o DIC%s %o\n", device, PC-1, pulcode, iodata); break; case ioDOA: fprintf(Trace, "[%o] %o DOA%s %o\n", device, PC-1, pulcode, AC[dstAC]); break; case ioDOB: fprintf(Trace, "[%o] %o DOB%s %o\n", device, PC-1, pulcode, AC[dstAC]); break; case ioDOC: fprintf(Trace, "[%o] %o DOC%s %o\n", device, PC-1, pulcode, AC[dstAC]); break; default: break; } /* end switch */ } /* end if debug */ } /* end else if */ else reason = stop_dev; } /* end if IOT */ } /* end while */ /* Simulation halted */ saved_PC = PC; return reason; } /* Computes and returns a 16-bit effective address, given a program counter, index, and a displacement. */ int32 effective(int32 PC, int32 index, int32 disp) { register int32 i, MA; MA = disp & 077777; switch (index) { /* decode IR<6:7> */ case 0: /* page zero */ break; case 1: /* PC relative */ MA = (MA + PC) & AMASK; break; case 2: /* AC2 relative */ MA = (MA + AC[2]) & AMASK; break; case 3: /* AC3 relative */ MA = (MA + AC[3]) & AMASK; break; } /* end switch mode */ if (disp & 0100000) { /* indirect? */ for (i = 0; i < ind_max * 2; i++) { /* count indirects */ MA = GetMap(MA & AMASK); if (SingleCycle) Usermap = 0; if (MapStat & 1) { /* Start MAP */ Usermap = Enable; Inhibit = 0; } if ((MA & 0100000) == 0) break; if ((MapStat & 010) && Usermap && i >= ind_max) break; } if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { Fault = 04000; /* Map fault if IND prot */ } if (i >= (ind_max * 2) && !(Fault)) { reason = STOP_IND_INT; /* Stop machine */ } } return (MA & AMASK); } /* Computes and returns a 16-bit effective address, given a program counter, index, and a displacement. This is a version supporting the LEF map mode instruction, as opposed to the ELEF instruction. */ int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect) { register int32 i, MA; int16 sMA; MA = disp & 077777; switch (index) { /* decode IR<6:7> */ case 0: /* page zero */ break; case 1: /* PC relative */ sMA = MA; if (MA & 0200) sMA |= 0xff00; MA = (sMA + PC) & AMASK; break; case 2: /* AC2 relative */ sMA = MA; if (MA & 0200) sMA |= 0xff00; MA = (sMA + AC[2]) & AMASK; break; case 3: /* AC3 relative */ sMA = MA; if (MA & 0200) sMA |= 0xff00; MA = (sMA + AC[3]) & AMASK; break; } /* end switch mode */ if (indirect) { /* indirect? */ for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); else MA = GetMap(MA & AMASK); if (SingleCycle) Usermap = 0; if (MapStat & 1) { /* Start MAP */ Usermap = Enable; Inhibit = 0; } if ((MA & 0100000) == 0) break; if ((MapStat & 010) && Usermap && i >= ind_max) break; } if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { Fault = 04000; /* Map fault if IND prot */ } if (i >= (ind_max * 2) && !(Fault)) { reason = STOP_IND_INT; /* Stop machine */ } } return (MA & AMASK); } /* Computes a "Byte pointer" for the Character Instruction set */ /* This address in 'PC' must point to the displacement word of the instruction */ int32 Bytepointer(int32 PC, int32 index) { register int32 MA; switch (index) { /* decode IR<6:7> */ case 0: /* page zero */ MA = 0; break; case 1: /* PC relative */ MA = PC & AMASK; break; case 2: /* AC2 relative */ MA = AC[2] & AMASK; break; case 3: /* AC3 relative */ MA = AC[3] & AMASK; break; } /* end switch mode */ MA = (MA * 2) & 0177777; MA = MA + GetMap(PC); return (MA & 0177777); } /* Given an address, returns either that address if bit 0 is 0, or or follows an indirection chain until bit 0 is 0 */ int32 indirect(int32 d) { int i; if (d & 0100000) { /* indirect? */ for (i = 0; i < ind_max * 2; i++) { /* count indirects */ if ((d & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) d = (PutMap(d & AMASK, ((GetMap(d & AMASK) + 1) & 0177777))); else if ((d & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) d = (PutMap(d & AMASK, ((GetMap(d & AMASK) - 1) & 0177777))); else d = GetMap(d & AMASK); if (MapStat & 1) { /* Start MAP */ Usermap = Enable; Inhibit = 0; } if ((d & 0100000) == 0) break; if ((MapStat & 010) && Usermap && i >= ind_max) break; } if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { Fault = 04000; /* Map fault if IND prot */ } if (i >= (ind_max * 2) && !(Fault)) { reason = STOP_IND; /* Stop machine */ } } return (d); } /* Push a standard return block onto the stack */ int32 pushrtn(int32 pc) { int32 t; t = (GetMap(040) + 1) & AMASK; PutMap(t, AC[0]); t++; PutMap(t, AC[1]); t++; PutMap(t, AC[2]); t++; PutMap(t, AC[3]); t++; PutMap(t, pc); if (C) PutMap(t, (GetMap(t) | 0100000)); PutMap(040, t); return 0; } /* Eclipse memory get/put - uses MAP if enabled */ int32 GetMap(int32 addr) { int32 page; t_addr paddr; switch (Usermap) { case 0: if (addr < 076000) return M[addr]; paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); if (paddr < MEMSIZE) return M[paddr]; else return (0); break; case 1: page = (addr >> 10) & 037; paddr = ((Map[1][page] & 01777) << 10) | (addr & 001777); if (Map[1][page] == INVALID && !SingleCycle) Fault = 0100000/*!!!*/; /* Validity */ if (paddr < MEMSIZE) return M[paddr]; else return (0); break; case 2: page = (addr >> 10) & 037; paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); if (Map[2][page] == INVALID && !SingleCycle) Fault = 0100000/*!!!*/; /* Validity */ if (paddr < MEMSIZE) return M[paddr]; else return (0); break; case 6: page = (addr >> 10) & 037; paddr = ((Map[6][page] & PAGEMASK) << 10) | (addr & 001777); if (Map[6][page] == INVALID && !SingleCycle) Fault = 0100000/*!!!*/; /* Validity */ if (paddr < MEMSIZE) return M[paddr]; else return (0); break; case 7: page = (addr >> 10) & 037; paddr = ((Map[7][page] & PAGEMASK) << 10) | (addr & 001777); if (Map[7][page] == INVALID && !SingleCycle) Fault = 0100000/*!!!*/; /* Validity */ if (paddr < MEMSIZE) return M[paddr]; else return (0); break; default: printf("\n\r<>\n\r"); return M[addr]; break; } } int32 PutMap(int32 addr, int32 data) { int32 page; t_addr paddr; switch (Usermap) { case 0: if (addr < 076000) { M[addr] = data; return (data); } paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); if (paddr < MEMSIZE) M[paddr] = data; break; case 1: page = (addr >> 10) & 037; paddr = ((Map[1][page] & PAGEMASK) << 10) | (addr & 001777); if (((Map[1][page] & 0100000) && (MapStat & 020)) || Map[1][page] == INVALID) Fault = 010000; /* Write Protect Fault */ else if (paddr < MEMSIZE) M[paddr] = data; break; case 2: page = (addr >> 10) & 037; paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); if (((Map[2][page] & 0100000) && (MapStat & 020)) || Map[2][page] == INVALID) Fault = 010000; /* Write Protect Fault */ else if (paddr < MEMSIZE) M[paddr] = data; break; case 6: page = (addr >> 10) & 037; paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); if (((Map[6][page] & 0100000) && (MapStat & 020)) || Map[6][page] == INVALID) Fault = 010000; /* Write Protect Fault */ else if (paddr < MEMSIZE) M[paddr] = data; break; case 7: page = (addr >> 10) & 037; paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); if (((Map[7][page] & 0100000) && (MapStat & 020)) || Map[7][page] == INVALID) Fault = 010000; /* Write Protect Fault */ else if (paddr < MEMSIZE) M[paddr] = data; break; default: M[addr] = data; break; } return (data); } #if 0 int16 GetDCHMap(int32 map, int32 addr) { t_addr paddr; if (!(MapStat & 02)) return M[addr]; paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); if (paddr < MEMSIZE) return M[paddr]; return (0); } int16 PutDCHMap(int32 map, int32 addr, int16 data) { t_addr paddr; if (!(MapStat & 02)) { M[addr] = data; return (data); } paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); if (paddr < MEMSIZE) M[paddr] = data; return (data); } #endif /* Given a map number and a logical, returns the physical address, unless the map is not active, in which case logical = physical. This is used primarily by the I/O routines to map data channel read/writes. */ int32 MapAddr(int32 map, int32 addr) { int32 paddr; if ((map == 0 || map > 2) && !(MapStat & 02)) return addr; if (map > 0 && map < 3 && Usermap == 0) return addr; paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); return paddr; } /* Loads a word into the Eclipse Maps */ int32 LoadMap(int32 w) { int32 m; m = (w >> 10) & 037; switch ((MapStat >> 7) & 07) { case 0: /* Load user A Map */ Map[1][m] = w & MAPMASK; break; case 1: /* Load user C Map */ Map[6][m] = w & MAPMASK; break; case 2: /* Load user B Map */ Map[2][m] = w & MAPMASK; break; case 3: /* Load user D Map */ Map[7][m] = w & MAPMASK; break; case 4: /* Load DCH A Map */ Map[0][m] = w & MAPMASK; break; case 5: /* Load DCH C Map */ Map[4][m] = w; break; case 6: /* Load DCH B Map */ Map[3][m] = w; break; case 7: /* Load DCH D Map */ Map[5][m] = w; break; default: break; } return 0; } /* Displays an error on a unimplemented (in this sim) instr. */ int32 unimp(int32 PC) { if (Debug_Flags) printf("\n\r\007<<>>\n\r", PC - 1, GetMap(PC - 1)); return 0; } /* New priority mask out */ void mask_out (int32 newmask) { int32 i; dev_disable = 0; for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (newmask & dev_table[i].pi) dev_disable = dev_disable | dev_table[i].mask; } int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int_req = int_req & ~INT_ION; pimask = 0; dev_disable = 0; pwr_low = 0; 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 (sw & SWMASK ('V')) { if (addr > 077777) return SCPE_NXM; if (vptr != NULL) *vptr = GetMap (addr); } else { if (addr >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = M[addr] & 0177777; } return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if (sw & SWMASK ('V')) { if (addr > 077777) return SCPE_NXM; PutMap (addr, (int32) val); } else { if (addr >= MEMSIZE) return SCPE_NXM; M[addr] = (int32) val & 0177777; } return SCPE_OK; } /* Alter memory size */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; t_addr 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; } /* MAP device services */ t_stat map_svc (UNIT *uptr) { return SCPE_OK; } /* Map examine */ t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { if ((addr & 077) >= 037 || addr > 737) return SCPE_NXM; uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ if (vptr != NULL) *vptr = Map[(addr >> 6) & 3][addr & 037] & 0177777; return SCPE_OK; } /* Memory deposit */ t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if ((addr & 077) >= 037 || addr > 0737) return SCPE_NXM; uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ Map[(addr >> 6) & 3][addr & 037] = (int32)val & 0177777; return SCPE_OK; } /* FPU device services */ t_stat fpu_svc (UNIT *uptr) { return SCPE_OK; } /* PIT Device Services */ /* IOT routine */ int32 pit (int32 pulse, int32 code, int32 AC) { int32 iodata = 0; if (code == ioDIA) { /* DIA */ if (pit_flag == 0) { pit_flag = 1; } iodata = pit_counter; } if (code == ioDOA) { /* DOA */ pit_initial = AC; /* Load Counter */ sim_rtcn_init (pit_time, 1); /* init calibr */ } switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ pit_counter = pit_initial; /* Set the counter */ dev_busy = dev_busy | INT_PIT; /* set busy */ dev_done = dev_done & ~INT_PIT; /* clear done, int */ int_req = int_req & ~INT_PIT; if (!sim_is_active (&pit_unit)) /* not running? */ sim_activate (&pit_unit, /* activate */ sim_rtcn_init (pit_time, 1)); /* init calibr */ break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_PIT; /* clear busy */ dev_done = dev_done & ~INT_PIT; /* clear done, int */ int_req = int_req & ~INT_PIT; sim_cancel (&pit_unit); /* deactivate unit */ break; } /* end switch */ return iodata; } /* Unit service */ t_stat pit_svc (UNIT *uptr) { int32 t; t = sim_rtcn_calb (pit_tps, 1); /* calibrate delay */ sim_activate (&pit_unit, t); /* reactivate unit */ pit_poll = t / (-pit_adj); /* adjust poll */ pit_counter++; /* Increment counter */ if (pit_counter >= 0177777) { /* Has counter reached limit ? */ dev_done = dev_done | INT_PIT; /* set done */ dev_busy = dev_busy & ~INT_PIT; /* clear busy */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); /* Interrupt */ pit_counter = pit_initial; } return SCPE_OK; } /* Reset routine */ t_stat pit_reset (DEVICE *dptr) { pit_counter = 0; /* clear counter */ dev_busy = dev_busy & ~INT_PIT; /* clear busy */ dev_done = dev_done & ~INT_PIT; /* clear done, int */ int_req = int_req & ~INT_PIT; sim_cancel (&pit_unit); /* deactivate unit */ pit_poll = pit_time; /* poll is default */ return SCPE_OK; } /* Bootstrap routine for CPU */ #define BOOT_START 00000 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) static const int32 boot_rom[] = { 062677, /* IORST ;Reset all I/O */ 060477, /* READS 0 ;Read SR into AC0 */ 024026, /* LDA 1,C77 ;Get dev mask */ 0107400, /* AND 0,1 ;Isolate dev code */ 0124000, /* COM 1,1 ;- device code - 1 */ 010014, /* LOOP: ISZ OP1 ;Device code to all */ 010030, /* ISZ OP2 ;I/O instructions */ 010032, /* ISZ OP3 */ 0125404, /* INC 1,1,SZR ;done? */ 000005, /* JMP LOOP ;No, increment again */ 030016, /* LDA 2,C377 ;place JMP 377 into */ 050377, /* STA 2,377 ;location 377 */ 060077, /* OP1: 060077 ;start device (NIOS 0) */ 0101102, /* MOVL 0,0,SZC ;Test switch 0, low speed? */ 000377, /* C377: JMP 377 ;no - jmp 377 & wait */ 004030, /* LOOP2: JSR GET+1 ;Get a frame */ 0101065, /* MOVC 0,0,SNR ;is it non-zero? */ 000017, /* JMP LOOP2 ;no, ignore */ 004027, /* LOOP4: JSR GET ;yes, get full word */ 046026, /* STA 1,@C77 ;store starting at 100 */ /* ;2's complement of word ct */ 010100, /* ISZ 100 ;done? */ 000022, /* JMP LOOP4 ;no, get another */ 000077, /* C77: JMP 77 ;yes location ctr and */ /* ;jmp to last word */ 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */ /* OP2: */ 063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */ 000030, /* JMP LOOP3 ;no -- wait */ 060477, /* OP3: 060477 ;y--read in ac0 (DIAS 0,0) */ 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */ 000030, /* JMP LOOP3 ;no go back after it */ 0125300, /* MOVS 1,1 ;yes swap them */ 001400, /* JMP 0,3 ;rtn with full word */ 0 /* 0 ;padding */ }; t_stat cpu_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags) { hpc[hnext] = PC & 0xffff; hinst[hnext] = inst & 0xffff; hinst2[hnext] = inst2 & 0xffff; hac0[hnext] = AC0 & 0xffff; hac1[hnext] = AC1 & 0xffff; hac2[hnext] = AC2 & 0xffff; hac3[hnext] = AC3 & 0xffff; hflags[hnext] = flags & 0xffff; hnext++; if (hnext >= hmax) { hwrap = 1; hnext = 0; } return 0; } int32 Debug_Dump(UNIT *uptr, int32 val, char *cptr, void *desc) { return SCPE_OK; } int32 Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc) { char debmap[4], debion[4]; t_value simeval[20]; int debcar; int start, end, ctr; int count = 0; if (!Debug_Flags || Debug_Flags & 0100000) { printf("History was not logged. Deposit a non-zero value\n"); printf("in DEBUG with bit 0 being 1 to build history.\n"); return SCPE_OK; } if (!hwrap) { start = 0; end = hnext; } else { start = hnext; end = hnext - 1; if (end < 0) end = hmax; } ctr = start; while (1) { if (ctr == end) break; count++; strcpy(debion, " "); strcpy(debmap, " "); debcar = 0; if (hflags[ctr] & 0x80) { fprintf(st, "--------- Interrupt %o (%o) to %6o ---------\n", hinst[ctr], hac0[ctr], hac1[ctr]); } else { if (hflags[ctr] & 0x01) debcar = 1; if (hflags[ctr] & 0x02) strcpy(debion, "I"); if (hflags[ctr] & 0x04) strcpy(debmap, "A"); if (hflags[ctr] & 0x08) strcpy(debmap, "B"); if (hflags[ctr] & 0x10) strcpy(debmap, "C"); if (hflags[ctr] & 0x20) strcpy(debmap, "D"); fprintf(st, "%s%s%06o acs: %06o %06o %06o %06o %01o ", debion, debmap, hpc[ctr], hac0[ctr], hac1[ctr], hac2[ctr], hac3[ctr], debcar); simeval[0] = hinst[ctr]; simeval[1] = hinst2[ctr]; fprint_sym (st, hpc[ctr], simeval, NULL, SWMASK('M')); fprintf(st, "\n"); } ctr++; if (ctr > hmax) ctr = 0; } return SCPE_OK; } /* Build dispatch table */ t_stat build_devtab (void) { DEVICE *dptr; DIB *dibp; int32 i, dn; for (i = 0; i < 64; i++) { /* clr dev_table */ dev_table[i].mask = 0; dev_table[i].pi = 0; dev_table[i].routine = NULL; } for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ if (!(dptr->flags & DEV_DIS) && /* enabled and */ (dibp = (DIB *) dptr->ctxt)) { /* defined DIB? */ dn = dibp->dnum; /* get dev num */ dev_table[dn].mask = dibp->mask; /* copy entries */ dev_table[dn].pi = dibp->pi; dev_table[dn].routine = dibp->routine; } } return SCPE_OK; } /* ------------------------------------------------------------------- */ /* Floating Point Arithmetic */ /* ------------------------------------------------------------------- */ /* Get short float from FPAC */ void get_sf (SHORT_FLOAT *fl, t_int64 *fpr) { fl->sign = (uint8)(*fpr >> 63) & 1; fl->expo = (short)(*fpr >> 56) & 0x007F; fl->short_fract = (int32)(*fpr >> 32) & 0x00FFFFFF; } /* Store short float to FPAC */ void store_sf (SHORT_FLOAT *fl, t_int64 *fpr) { *fpr = 0; *fpr = ((t_int64)fl->sign << 63) | ((t_int64)fl->expo << 56) | ((t_int64)fl->short_fract <<32); } /* Get long float from FPAC */ void get_lf (LONG_FLOAT *fl, t_int64 *fpr) { fl->sign = (uint8)(*fpr >> 63) & 1; fl->expo = (short)(*fpr >> 56) & 0x007F; fl->long_fract = (t_int64)*fpr & 0x00FFFFFFFFFFFFFF; } /* Store long float to FPAC */ void store_lf (LONG_FLOAT *fl, t_int64 *fpr) { *fpr = 0; *fpr = (t_int64)fl->sign << 63; *fpr |= ((t_int64)fl->expo << 56) & 0x7f00000000000000; *fpr |= fl->long_fract; } /* Check short for Overflow */ int overflow_sf (SHORT_FLOAT *fl) { if (fl->expo > 127) { fl->expo &= 0x007F; return(1); } return(0); } /* Normalize Short Float */ int normal_sf(SHORT_FLOAT *fl) { if (fl->short_fract) { if ((fl->short_fract & 0x00FFFF00) == 0) { fl->short_fract <<= 16; fl->expo -= 4; } if ((fl->short_fract & 0x00FF0000) == 0) { fl->short_fract <<= 8; fl->expo -= 2; } if ((fl->short_fract & 0x00F00000) == 0) { fl->short_fract <<= 4; (fl->expo)--; } } else { fl->sign = 0; fl->expo = 0; } if (fl->expo < 0) return (2); return(0); } /* Normalize long float */ int normal_lf (LONG_FLOAT *fl) { if (fl->long_fract) { if ((fl->long_fract & 0x00FFFFFFFF000000) == 0) { fl->long_fract <<= 32; fl->expo -= 8; } if ((fl->long_fract & 0x00FFFF0000000000) == 0) { fl->long_fract <<= 16; fl->expo -= 4; } if ((fl->long_fract & 0x00FF000000000000) == 0) { fl->long_fract <<= 8; fl->expo -= 2; } if ((fl->long_fract & 0x00F0000000000000) == 0) { fl->long_fract <<= 4; (fl->expo)--; } } else { fl->sign = 0; fl->expo = 0; } if (fl->expo < 0) return (2); return(0); } /* Check Long for Overflow */ int overflow_lf(LONG_FLOAT *fl) { if (fl->expo > 127) { fl->expo &= 0x007F; return(1); } return(0); } int underflow_sf(SHORT_FLOAT *fl) { if (fl->expo < 0) { fl->short_fract = 0; fl->expo = 0; fl->sign = 0; } return(0); } int underflow_lf(LONG_FLOAT *fl) { if (fl->expo < 0) { fl->long_fract = 0; fl->expo = 0; fl->sign = 0; } return(0); } /* Check Short for Over/Under flow */ int over_under_flow_sf(SHORT_FLOAT *fl) { if (fl->expo > 127) { fl->expo &= 0x007F; return(1); } else { if (fl->expo < 0) { /* set true 0 */ fl->short_fract = 0; fl->expo = 0; fl->sign = 0; } } return(0); } /* Check Long for Over/Under flow */ int over_under_flow_lf(LONG_FLOAT *fl) { if (fl->expo > 127) { fl->expo &= 0x007F; return(1); } else { if (fl->expo < 0) { /* set true 0 */ fl->long_fract = 0; fl->expo = 0; fl->sign = 0; } } return(0); } int significance_sf (SHORT_FLOAT *fl) { fl->sign = 0; fl->expo = 0; return(0); } int significance_lf (LONG_FLOAT *fl) { fl->sign = 0; fl->expo = 0; return(0); } /*-------------------------------------------------------------------*/ /* Add short float */ /* */ /* Input: */ /* fl Float */ /* add_fl Float to be added */ /* normal Normalize if true */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int add_sf (SHORT_FLOAT *fl, SHORT_FLOAT *add_fl, int normal) { int pgm_check; int shift; pgm_check = 0; if (add_fl->short_fract || add_fl->expo) { /* add_fl not 0 */ if (fl->short_fract || fl->expo) { /* fl not 0 */ /* both not 0 */ if (fl->expo == add_fl->expo) { /* expo equal */ /* both guard digits */ fl->short_fract <<= 4; add_fl->short_fract <<= 4; } else { /* expo not equal, denormalize */ if (fl->expo < add_fl->expo) { /* shift minus guard digit */ shift = add_fl->expo - fl->expo - 1; fl->expo = add_fl->expo; if (shift) { if (shift >= 6 || ((fl->short_fract >>= (shift * 4)) == 0)) { /* 0, copy summand */ fl->sign = add_fl->sign; fl->short_fract = add_fl->short_fract; if (fl->short_fract == 0) { pgm_check = significance_sf(fl); } else { if (normal) { normal_sf(fl); pgm_check = underflow_sf(fl); } } return(pgm_check); } } /* guard digit */ add_fl->short_fract <<= 4; } else { /* shift minus guard digit */ shift = fl->expo - add_fl->expo - 1; if (shift) { if (shift >= 6 || ((add_fl->short_fract >>= (shift * 4)) == 0)) { /* 0, nothing to add */ if (fl->short_fract == 0) { pgm_check = significance_sf(fl); } else { if (normal) { normal_sf(fl); pgm_check = underflow_sf(fl); } } return(pgm_check); } } /* guard digit */ fl->short_fract <<= 4; } } /* compute with guard digit */ if (fl->sign == add_fl->sign) { fl->short_fract += add_fl->short_fract; } else { if (fl->short_fract == add_fl->short_fract) { /* true 0 */ fl->short_fract = 0; return( significance_sf(fl) ); } else if (fl->short_fract > add_fl->short_fract) { fl->short_fract -= add_fl->short_fract; } else { fl->short_fract = add_fl->short_fract - fl->short_fract; fl->sign = add_fl->sign; } } /* handle overflow with guard digit */ if (fl->short_fract & 0xF0000000) { fl->short_fract >>= 8; (fl->expo)++; pgm_check = overflow_sf(fl); } else { if (normal) { /* normalize with guard digit */ if (fl->short_fract) { /* not 0 */ if (fl->short_fract & 0x0F000000) { /* not normalize, just guard digit */ fl->short_fract >>= 4; } else { (fl->expo)--; normal_sf(fl); pgm_check = underflow_sf(fl); } } else { /* true 0 */ pgm_check = significance_sf(fl); } } else { /* not normalize, just guard digit */ fl->short_fract >>= 4; if (fl->short_fract == 0) { pgm_check = significance_sf(fl); } } } return(pgm_check); } else { /* fl 0, add_fl not 0 */ /* copy summand */ fl->expo = add_fl->expo; fl->sign = add_fl->sign; fl->short_fract = add_fl->short_fract; if (fl->short_fract == 0) { return( significance_sf(fl) ); } } } else { /* add_fl 0 */ if (fl->short_fract == 0) { /* fl 0 */ /* both 0 */ return( significance_sf(fl) ); } } if (normal) { normal_sf(fl); pgm_check = underflow_sf(fl); } return(pgm_check); } /*-------------------------------------------------------------------*/ /* Add long float */ /* */ /* Input: */ /* fl Float */ /* add_fl Float to be added */ /* normal Normalize if true */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int add_lf (LONG_FLOAT *fl, LONG_FLOAT *add_fl, int normal) { int pgm_check; int shift; pgm_check = 0; if (add_fl->long_fract || add_fl->expo) { /* add_fl not 0 */ if (fl->long_fract || fl->expo) { /* fl not 0 */ /* both not 0 */ if (fl->expo == add_fl->expo) { /* expo equal */ /* both guard digits */ fl->long_fract <<= 4; add_fl->long_fract <<= 4; } else { /* expo not equal, denormalize */ if (fl->expo < add_fl->expo) { /* shift minus guard digit */ shift = add_fl->expo - fl->expo - 1; fl->expo = add_fl->expo; if (shift) { if (shift >= 14 || ((fl->long_fract >>= (shift * 4)) == 0)) { /* 0, copy summand */ fl->sign = add_fl->sign; fl->long_fract = add_fl->long_fract; if (fl->long_fract == 0) { pgm_check = significance_lf(fl); } else { if (normal) { normal_lf(fl); pgm_check = underflow_lf(fl); } } return(pgm_check); } } /* guard digit */ add_fl->long_fract <<= 4; } else { /* shift minus guard digit */ shift = fl->expo - add_fl->expo - 1; if (shift) { if (shift >= 14 || ((add_fl->long_fract >>= (shift * 4)) == 0)) { /* 0, nothing to add */ if (fl->long_fract == 0) { pgm_check = significance_lf(fl); } else { if (normal) { normal_lf(fl); pgm_check = underflow_lf(fl); } } return(pgm_check); } } /* guard digit */ fl->long_fract <<= 4; } } /* compute with guard digit */ if (fl->sign == add_fl->sign) { fl->long_fract += add_fl->long_fract; } else { if (fl->long_fract == add_fl->long_fract) { /* true 0 */ fl->long_fract = 0; return( significance_lf(fl) ); } else if (fl->long_fract > add_fl->long_fract) { fl->long_fract -= add_fl->long_fract; } else { fl->long_fract = add_fl->long_fract - fl->long_fract; fl->sign = add_fl->sign; } } /* handle overflow with guard digit */ if (fl->long_fract & 0xF000000000000000) { fl->long_fract >>= 8; (fl->expo)++; pgm_check = overflow_lf(fl); } else { if (normal) { /* normalize with guard digit */ if (fl->long_fract) { /* not 0 */ if (fl->long_fract & 0x0F00000000000000) { /* not normalize, just guard digit */ fl->long_fract >>= 4; } else { (fl->expo)--; normal_lf(fl); pgm_check = underflow_lf(fl); } } else { /* true 0 */ pgm_check = significance_lf(fl); } } else { /* not normalize, just guard digit */ fl->long_fract >>= 4; if (fl->long_fract == 0) { pgm_check = significance_lf(fl); } } } return(pgm_check); } else { /* fl 0, add_fl not 0 */ /* copy summand */ fl->expo = add_fl->expo; fl->sign = add_fl->sign; fl->long_fract = add_fl->long_fract; if (fl->long_fract == 0) { return( significance_lf(fl) ); } } } else { /* add_fl 0 */ if (fl->long_fract == 0) { /* fl 0 */ /* both 0 */ return( significance_lf(fl) ); } } if (normal) { normal_lf(fl); pgm_check = underflow_lf(fl); } return(pgm_check); } /*-------------------------------------------------------------------*/ /* Multiply short float */ /* */ /* Input: */ /* fl Multiplicand short float */ /* mul_fl Multiplicator short float */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int mul_sf(SHORT_FLOAT *fl, SHORT_FLOAT *mul_fl) { t_int64 wk; if (fl->short_fract && mul_fl->short_fract) { /* normalize operands */ normal_sf( fl ); normal_sf( mul_fl ); /* multiply fracts */ wk = (t_int64) fl->short_fract * mul_fl->short_fract; /* normalize result and compute expo */ if (wk & 0x0000F00000000000) { fl->short_fract = (int32)wk >> 24; fl->expo = (short)fl->expo + mul_fl->expo - 64; } else { fl->short_fract = (int32)wk >> 20; fl->expo = (short)fl->expo + mul_fl->expo - 65; } /* determine sign */ fl->sign = (fl->sign == mul_fl->sign) ? 0 : 1; /* handle overflow and underflow */ return( over_under_flow_sf(fl) ); } else { /* set true 0 */ fl->short_fract = 0; fl->expo = 0; fl->sign = 0; return(0); } } /*-------------------------------------------------------------------*/ /* Multiply long float */ /* */ /* Input: */ /* fl Multiplicand long float */ /* mul_fl Multiplicator long float */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int mul_lf(LONG_FLOAT *fl, LONG_FLOAT *mul_fl) { t_int64 wk; int32 v; if (fl->long_fract && mul_fl->long_fract) { /* normalize operands */ normal_lf( fl ); normal_lf( mul_fl ); /* multiply fracts by sum of partial multiplications */ wk = ((fl->long_fract & 0x00000000FFFFFFFF) * (mul_fl->long_fract & 0x00000000FFFFFFFF)) >> 32; wk += ((fl->long_fract & 0x00000000FFFFFFFF) * (mul_fl->long_fract >> 32)); wk += ((fl->long_fract >> 32) * (mul_fl->long_fract & 0x00000000FFFFFFFF)); v = (int32)wk; fl->long_fract = (wk >> 32) + ((fl->long_fract >> 32) * (mul_fl->long_fract >> 32)); /* normalize result and compute expo */ if (fl->long_fract & 0x0000F00000000000) { fl->long_fract = (fl->long_fract << 8) | (v >> 24); fl->expo = fl->expo + mul_fl->expo - 64; } else { fl->long_fract = (fl->long_fract << 12) | (v >> 20); fl->expo = fl->expo + mul_fl->expo - 65; } /* determine sign */ fl->sign = (fl->sign == mul_fl->sign) ? 0 : 1; /* handle overflow and underflow */ return( over_under_flow_lf(fl) ); } else { /* set true 0 */ fl->long_fract = 0; fl->expo = 0; fl->sign = 0; return(0); } } /*-------------------------------------------------------------------*/ /* Divide short float */ /* */ /* Input: */ /* fl Dividend short float */ /* div_fl Divisor short float */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int div_sf(SHORT_FLOAT *fl, SHORT_FLOAT *div_fl) { t_int64 wk; if (div_fl->short_fract) { if (fl->short_fract) { /* normalize operands */ normal_sf( fl ); normal_sf( div_fl ); /* position fracts and compute expo */ if (fl->short_fract < div_fl->short_fract) { wk = (t_int64) fl->short_fract << 24; fl->expo = fl->expo - div_fl->expo + 64; } else { wk = (t_int64) fl->short_fract << 20; fl->expo = fl->expo - div_fl->expo + 65; } /* divide fractions */ fl->short_fract = (int32)wk / div_fl->short_fract; /* determine sign */ fl->sign = (fl->sign == div_fl->sign) ? 0 : 1; /* handle overflow and underflow */ return( over_under_flow_sf(fl) ); } else { /* fraction of dividend 0, set true 0 */ fl->short_fract = 0; fl->expo = 0; fl->sign = 0; } } else { /* divisor 0 */ return(3); } return(0); } /*-------------------------------------------------------------------*/ /* Divide long float */ /* */ /* Input: */ /* fl Dividend long float */ /* div_fl Divisor long float */ /* Value: */ /* exeption */ /*-------------------------------------------------------------------*/ int div_lf(LONG_FLOAT *fl, LONG_FLOAT *div_fl) { t_int64 wk; t_int64 wk2; int i; if (div_fl->long_fract) { if (fl->long_fract) { /* normalize operands */ normal_lf( fl ); normal_lf( div_fl ); /* position fracts and compute expo */ if (fl->long_fract < div_fl->long_fract) { fl->expo = fl->expo - div_fl->expo + 64; } else { fl->expo = fl->expo - div_fl->expo + 65; div_fl->long_fract <<= 4; } /* partial divide first hex digit */ wk2 = fl->long_fract / div_fl->long_fract; wk = (fl->long_fract % div_fl->long_fract) << 4; /* partial divide middle hex digits */ i = 13; while (i--) { wk2 = (wk2 << 4) | (wk / div_fl->long_fract); wk = (wk % div_fl->long_fract) << 4; } /* partial divide last hex digit */ fl->long_fract = (wk2 << 4) | (wk / div_fl->long_fract); /* determine sign */ fl->sign = (fl->sign == div_fl->sign) ? 0 : 1; /* handle overflow and underflow */ return( over_under_flow_lf(fl) ); } else { /* fraction of dividend 0, set true 0 */ fl->long_fract = 0; fl->expo = 0; fl->sign = 0; } } else { /* divisor 0 */ return(3); } return(0); } simh-3.8.1/NOVA/nova_pt.c0000644000175000017500000002155311112562624013224 0ustar vlmvlm/* nova_pt.c: NOVA paper tape read/punch simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr paper tape reader ptp paper tape punch 04-Jul-07 BKR added PTR and PTP device DISABLE capability, added 7B/8B support PTR and PTP (default is 8B), DEV_SET/CLR macros now used, PTR and PTP can now be DISABLED 25-Apr-03 RMS Revised for extended file support 03-Oct-02 RMS Added DIBs 30-May-02 RMS Widened POS to 32b 29-Nov-01 RMS Added read only unit support Notes: - data masked to 7- or 8- bits, based on 7B or 8B, default is 8-bits - register TIME is the delay between character read or write operations - register POS show the number of characters read from or sent to the PTR or PTP - register STOP_IOE determines return value issued if output to unattached PTR or PTP is attempted */ #include "nova_defs.h" extern int32 int_req, dev_busy, dev_done, dev_disable ; extern int32 SR ; extern t_stat cpu_boot(int32 unitno, DEVICE * dptr ) ; int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ int32 ptr (int32 pulse, int32 code, int32 AC); int32 ptp (int32 pulse, int32 code, int32 AC); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); /* 7 or 8 bit data mask support for either device */ #define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ #define UNIT_8B (1 << UNIT_V_8B) /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_reg PTR register list */ DIB ptr_dib = { DEV_PTR, INT_PTR, PI_PTR, &ptr }; UNIT ptr_unit = { /* 2007-May-30, bkr */ UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_8B, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_PTR) }, { FLDATA (DONE, dev_done, INT_V_PTR) }, { FLDATA (DISABLE, dev_disable, INT_V_PTR) }, { FLDATA (INT, int_req, INT_V_PTR) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB ptr_mod[] = /* 2007-May-30, bkr */ { { UNIT_8B, 0, "7b", "7B", NULL }, { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, { 0, 0, NULL, NULL, NULL } } ; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod /* 2007-May-30, bkr */, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, NULL, NULL, &ptr_dib, DEV_DISABLE /* 2007-May-30, bkr */ }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_reg PTP register list */ DIB ptp_dib = { DEV_PTP, INT_PTP, PI_PTP, &ptp }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_8B, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_PTP) }, { FLDATA (DONE, dev_done, INT_V_PTP) }, { FLDATA (DISABLE, dev_disable, INT_V_PTP) }, { FLDATA (INT, int_req, INT_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[] = { { UNIT_8B, 0, "7b", "7B", NULL }, { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, { 0, 0, NULL, NULL, NULL } } ; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod /* 2007-May-30, bkr */, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, &ptp_dib, DEV_DISABLE /* 2007-May-30, bkr */ }; /* Paper tape reader: IOT routine */ int32 ptr (int32 pulse, int32 code, int32 AC) { int32 iodata; iodata = (code == ioDIA)? ptr_unit.buf & 0377 : 0; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_PTR ) ; DEV_CLR_DONE( INT_PTR ) ; DEV_UPDATE_INTR ; sim_activate (&ptr_unit, ptr_unit.wait); /* activate unit */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_PTR ) ; DEV_CLR_DONE( INT_PTR ) ; DEV_UPDATE_INTR ; sim_cancel (&ptr_unit); /* deactivate unit */ break; } /* end switch */ return iodata; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ if (feof (ptr_unit.fileref)) { if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } DEV_CLR_BUSY( INT_PTR ) ; DEV_SET_DONE( INT_PTR ) ; DEV_UPDATE_INTR ; ptr_unit.buf = temp & ((ptr_unit.flags & UNIT_8B)? 0377: 0177); ++(ptr_unit.pos); return SCPE_OK; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; /* */ DEV_CLR_BUSY( INT_PTR ) ; DEV_CLR_DONE( INT_PTR ) ; DEV_UPDATE_INTR ; sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Boot routine */ t_stat ptr_boot (int32 unitno, DEVICE *dptr) { ptr_reset( dptr ) ; /* set position to 0? */ cpu_boot( unitno, dptr ) ; SR = /* low-speed: no high-order bit set */ DEV_PTR ; return ( SCPE_OK ); } /* end of 'ptr_boot' */ /* Paper tape punch: IOT routine */ int32 ptp (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) ptp_unit.buf = AC & 0377; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_PTP ) ; DEV_CLR_DONE( INT_PTP ) ; DEV_UPDATE_INTR ; sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_PTP ) ; DEV_CLR_DONE( INT_PTP ) ; DEV_UPDATE_INTR ; sim_cancel (&ptp_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { DEV_CLR_BUSY( INT_PTP ) ; DEV_SET_DONE( INT_PTP ) ; DEV_UPDATE_INTR ; if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (putc ((ptp_unit.buf & ((ptp_unit.flags & UNIT_8B)? 0377: 0177)), ptp_unit.fileref) == EOF) { perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ++(ptp_unit.pos); return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; /* */ DEV_CLR_BUSY( INT_PTP ) ; DEV_CLR_DONE( INT_PTP ) ; DEV_UPDATE_INTR ; sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/NOVA/nova_clk.c0000644000175000017500000001435111112561354013347 0ustar vlmvlm/* nova_clk.c: NOVA real-time clock simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. clk real-time clock 04-Jul-07 BKR DEV_SET/CLR macros now used, changed CLK name to RTC for DG compatiblity, device may now bw DISABLED 01-Mar-03 RMS Added SET/SHOW CLK FREQ support 03-Oct-02 RMS Added DIB 17-Sep-01 RMS Added terminal multiplexor support 17-Mar-01 RMS Moved function prototype 05-Mar-01 RMS Added clock calibration 24-Sep-97 RMS Fixed bug in unit service (found by Charles Owen) */ #include "nova_defs.h" extern int32 int_req, dev_busy, dev_done, dev_disable ; int32 clk_sel = 0; /* selected freq */ int32 clk_time[4] = { 16000, 100000, 10000, 1000 }; /* freq table */ int32 clk_tps[4] = { 60, 10, 100, 1000 }; /* ticks per sec */ int32 clk_adj[4] = { 1, -5, 2, 20 }; /* tmxr adjust */ int32 tmxr_poll = 16000; /* tmxr poll */ int32 clk (int32 pulse, int32 code, int32 AC); t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_reg CLK register list */ DIB clk_dib = { DEV_CLK, INT_CLK, PI_CLK, &clk }; UNIT clk_unit = { UDATA (&clk_svc, 0, 0) }; REG clk_reg[] = { { ORDATA (SELECT, clk_sel, 2) }, { FLDATA (BUSY, dev_busy, INT_V_CLK) }, { FLDATA (DONE, dev_done, INT_V_CLK) }, { FLDATA (DISABLE, dev_disable, INT_V_CLK) }, { FLDATA (INT, int_req, INT_V_CLK) }, { DRDATA (TIME0, clk_time[0], 24), REG_NZ + PV_LEFT }, { DRDATA (TIME1, clk_time[1], 24), REG_NZ + PV_LEFT }, { DRDATA (TIME2, clk_time[2], 24), REG_NZ + PV_LEFT }, { DRDATA (TIME3, clk_time[3], 24), REG_NZ + PV_LEFT }, { DRDATA (TPS0, clk_tps[0], 6), PV_LEFT + REG_HRO }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "LINE", NULL, NULL, &clk_show_freq, NULL }, { 0 } }; DEVICE clk_dev = { "RTC", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, DEV_DISABLE }; /* IOT routine */ int32 clk (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) { /* DOA */ clk_sel = AC & 3; /* save select */ sim_rtc_init (clk_time[clk_sel]); /* init calibr */ } switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ DEV_SET_BUSY( INT_CLK ) ; DEV_CLR_DONE( INT_CLK ) ; DEV_UPDATE_INTR ; if (!sim_is_active (&clk_unit)) /* not running? */ sim_activate (&clk_unit, /* activate */ sim_rtc_init (clk_time[clk_sel])); /* init calibr */ break; case iopC: /* clear */ DEV_CLR_BUSY( INT_CLK ) ; DEV_CLR_DONE( INT_CLK ) ; DEV_UPDATE_INTR ; sim_cancel (&clk_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat clk_svc (UNIT *uptr) { int32 t; if ( DEV_IS_BUSY(INT_CLK) ) { DEV_CLR_BUSY( INT_CLK ) ; DEV_SET_DONE( INT_CLK ) ; DEV_UPDATE_INTR ; } t = sim_rtc_calb (clk_tps[clk_sel]); /* calibrate delay */ sim_activate (&clk_unit, t); /* reactivate unit */ if (clk_adj[clk_sel] > 0) /* clk >= 60Hz? */ tmxr_poll = t * clk_adj[clk_sel]; /* poll is longer */ else tmxr_poll = t / (-clk_adj[clk_sel]); /* poll is shorter */ return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { clk_sel = 0; DEV_CLR_BUSY( INT_CLK ) ; DEV_CLR_DONE( INT_CLK ) ; DEV_UPDATE_INTR ; sim_cancel (&clk_unit); /* deactivate unit */ tmxr_poll = clk_time[0]; /* poll is default */ return SCPE_OK; } /* Set line frequency */ t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; clk_tps[0] = val; return SCPE_OK; } /* Show line frequency */ t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (clk_tps[0] == 50)? "50Hz": "60Hz"); return SCPE_OK; } simh-3.8.1/NOVA/nova_tt1.c0000644000175000017500000003031311126242354013303 0ustar vlmvlm/* nova_tt1.c: NOVA second terminal simulator Copyright (c) 1993-2008, Robert M. Supnik Written by Bruce Ray and used with his gracious permission. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti1 second terminal input tto1 second terminal output 19-Nov-08 RMS Revised for common TMXR show routines 09-May-03 RMS Added network device flag 05-Jan-03 RMS Fixed calling sequence for setmod 03-Oct-02 RMS Added DIBs 22-Aug-02 RMS Updated for changes in sim_tmxr 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Revised enable/disable support 30-Dec-01 RMS Added show statistics, set disconnect 30-Nov-01 RMS Added extended SET/SHOW support 17-Sep-01 RMS Changed to use terminal multiplexor library 07-Sep-01 RMS Moved function prototypes 31-May-01 RMS Added multiconsole support 26-Apr-01 RMS Added device enable/disable support */ #include "nova_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ #define UNIT_DASHER (1 << UNIT_V_DASHER) extern int32 int_req, dev_busy, dev_done, dev_disable; extern int32 tmxr_poll; /* calibrated poll */ TMLN tt1_ldsc = { 0 }; /* line descriptors */ TMXR tt_desc = { 1, 0, 0, &tt1_ldsc }; /* mux descriptor */ DEVICE tti1_dev, tto1_dev; int32 tti1 (int32 pulse, int32 code, int32 AC); int32 tto1 (int32 pulse, int32 code, int32 AC); t_stat tti1_svc (UNIT *uptr); t_stat tto1_svc (UNIT *uptr); t_stat tti1_reset (DEVICE *dptr); t_stat tto1_reset (DEVICE *dptr); t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tti1_attach (UNIT *uptr, char *cptr); t_stat tti1_detach (UNIT *uptr); void ttx1_enbdis (int32 dis); /* TTI1 data structures tti1_dev TTI1 device descriptor tti1_unit TTI1 unit descriptor tti1_reg TTI1 register list ttx1_mod TTI1/TTO1 modifiers list */ DIB tti1_dib = { DEV_TTI1, INT_TTI1, PI_TTI1, &tti1 }; UNIT tti1_unit = { UDATA (&tti1_svc, UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG tti1_reg[] = { { ORDATA (BUF, tti1_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTI1) }, { FLDATA (DONE, dev_done, INT_V_TTI1) }, { FLDATA (DISABLE, dev_disable, INT_V_TTI1) }, { FLDATA (INT, int_req, INT_V_TTI1) }, { DRDATA (POS, tt1_ldsc.rxcnt, 32), PV_LEFT }, { DRDATA (TIME, tti1_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB tti1_mod[] = { { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx1_setmod }, { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx1_setmod }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &tt_desc }, { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &tt_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &tt_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &tt_desc }, { 0 } }; DEVICE tti1_dev = { "TTI1", &tti1_unit, tti1_reg, tti1_mod, 1, 10, 31, 1, 8, 8, &tmxr_ex, &tmxr_dep, &tti1_reset, NULL, &tti1_attach, &tti1_detach, &tti1_dib, DEV_NET | DEV_DISABLE }; /* TTO1 data structures tto1_dev TTO1 device descriptor tto1_unit TTO1 unit descriptor tto1_reg TTO1 register list */ DIB tto1_dib = { DEV_TTO1, INT_TTO1, PI_TTO1, &tto1 }; UNIT tto1_unit = { UDATA (&tto1_svc, 0, 0), SERIAL_OUT_WAIT }; REG tto1_reg[] = { { ORDATA (BUF, tto1_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTO1) }, { FLDATA (DONE, dev_done, INT_V_TTO1) }, { FLDATA (DISABLE, dev_disable, INT_V_TTO1) }, { FLDATA (INT, int_req, INT_V_TTO1) }, { DRDATA (POS, tt1_ldsc.txcnt, 32), PV_LEFT }, { DRDATA (TIME, tto1_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto1_mod[] = { { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx1_setmod }, { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx1_setmod }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &tt_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &tt_desc }, { 0 } }; DEVICE tto1_dev = { "TTO1", &tto1_unit, tto1_reg, tto1_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto1_reset, NULL, NULL, NULL, &tto1_dib, DEV_DISABLE }; /* Terminal input: IOT routine */ int32 tti1 (int32 pulse, int32 code, int32 AC) { int32 iodata; iodata = (code == ioDIA)? tti1_unit.buf & 0377: 0; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ dev_busy = dev_busy | INT_TTI1; /* set busy */ dev_done = dev_done & ~INT_TTI1; /* clear done, int */ int_req = int_req & ~INT_TTI1; break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ dev_done = dev_done & ~INT_TTI1; /* clear done, int */ int_req = int_req & ~INT_TTI1; break; } /* end switch */ return iodata; } /* Unit service */ t_stat tti1_svc (UNIT *uptr) { int32 temp, newln; if (tt1_ldsc.conn) { /* connected? */ tmxr_poll_rx (&tt_desc); /* poll for input */ if (temp = tmxr_getc_ln (&tt1_ldsc)) { /* get char */ uptr->buf = temp & 0177; if ((uptr->flags & UNIT_DASHER) && (uptr->buf == '\r')) uptr->buf = '\n'; /* Dasher: cr->nl */ dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ dev_done = dev_done | INT_TTI1; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); } sim_activate (uptr, uptr->wait); /* continue poll */ } if (uptr->flags & UNIT_ATT) { /* attached? */ newln = tmxr_poll_conn (&tt_desc); /* poll connect */ if (newln >= 0) { /* got one? */ sim_activate (&tti1_unit, tti1_unit.wait); tt1_ldsc.rcve = 1; /* rcv enabled */ } sim_activate (uptr, tmxr_poll); /* sched poll */ } return SCPE_OK; } /* Reset routine */ t_stat tti1_reset (DEVICE *dptr) { ttx1_enbdis (dptr->flags & DEV_DIS); /* sync devices */ tti1_unit.buf = 0; /* */ dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ dev_done = dev_done & ~INT_TTI1; /* clear done, int */ int_req = int_req & ~INT_TTI1; if (tt1_ldsc.conn) { /* if conn, */ sim_activate (&tti1_unit, tti1_unit.wait); /* activate, */ tt1_ldsc.rcve = 1; /* enable */ } else if (tti1_unit.flags & UNIT_ATT) /* if attached, */ sim_activate (&tti1_unit, tmxr_poll); /* activate */ else sim_cancel (&tti1_unit); /* else stop */ return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto1 (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) tto1_unit.buf = AC & 0377; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ dev_busy = dev_busy | INT_TTO1; /* set busy */ dev_done = dev_done & ~INT_TTO1; /* clear done, int */ int_req = int_req & ~INT_TTO1; sim_activate (&tto1_unit, tto1_unit.wait); /* activate unit */ break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ dev_done = dev_done & ~INT_TTO1; /* clear done, int */ int_req = int_req & ~INT_TTO1; sim_cancel (&tto1_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat tto1_svc (UNIT *uptr) { int32 c; dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ dev_done = dev_done | INT_TTO1; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); c = tto1_unit.buf & 0177; if ((tto1_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b'; if (tt1_ldsc.conn) { /* connected? */ if (tt1_ldsc.xmte) { /* tx enabled? */ tmxr_putc_ln (&tt1_ldsc, c); /* output char */ tmxr_poll_tx (&tt_desc); /* poll xmt */ } else { tmxr_poll_tx (&tt_desc); /* poll xmt */ sim_activate (&tto1_unit, tmxr_poll); /* wait */ } } return SCPE_OK; } /* Reset routine */ t_stat tto1_reset (DEVICE *dptr) { ttx1_enbdis (dptr->flags & DEV_DIS); /* sync devices */ tto1_unit.buf = 0; /* */ dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ dev_done = dev_done & ~INT_TTO1; /* clear done, int */ int_req = int_req & ~INT_TTO1; sim_cancel (&tto1_unit); /* deactivate unit */ return SCPE_OK; } t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr, void *desc) { tti1_unit.flags = (tti1_unit.flags & ~UNIT_DASHER) | val; tto1_unit.flags = (tto1_unit.flags & ~UNIT_DASHER) | val; return SCPE_OK; } /* Attach routine */ t_stat tti1_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&tt_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_activate (uptr, tmxr_poll); /* start poll */ return SCPE_OK; } /* Detach routine */ t_stat tti1_detach (UNIT *uptr) { t_stat r; r = tmxr_detach (&tt_desc, uptr); /* detach */ tt1_ldsc.rcve = 0; /* disable rcv */ sim_cancel (uptr); /* stop poll */ return r; } /* Enable/disable device */ void ttx1_enbdis (int32 dis) { if (dis) { tti1_dev.flags = tti1_dev.flags | DEV_DIS; tto1_dev.flags = tto1_dev.flags | DEV_DIS; } else { tti1_dev.flags = tti1_dev.flags & ~DEV_DIS; tto1_dev.flags = tto1_dev.flags & ~DEV_DIS; } return; } simh-3.8.1/NOVA/nova_cpu.c0000644000175000017500000016716311112561354013377 0ustar vlmvlm/* nova_cpu.c: NOVA CPU simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu Nova central processor 04-Jul-07 BKR DEV_SET/CLR macros now used, support for non-existant devices added CPU bootstrap code warning: high-speed devices may not boot properly, execution history facility added, documented Nova 3 secret LDB/STB/SAVN behavior, added support for secret Nova 3 LDB/STB/SAVN substitute actions, 'ind_max' changed from 16 to 65536 for better unmapped system compatibility, INT_TRAP added for Nova 3, 4 trap instruction handling, 28-Apr-07 RMS Removed clock initialization 06-Feb-06 RMS Fixed bug in DIVS (found by Mark Hittinger) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 25-Aug-05 RMS Fixed DIVS case 2^31 / - 1 14-Jan-04 RMS Fixed device enable/disable support (found by Bruce Ray) 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev Kit conflict 03-Oct-02 RMS Added DIB infrastructure 30-Dec-01 RMS Added old PC queue 07-Dec-01 RMS Revised to use breakpoint package 30-Nov-01 RMS Added extended SET/SHOW support 10-Aug-01 RMS Removed register in declarations 17-Jul-01 RMS Moved function prototype 26-Apr-01 RMS Added device enable/disable support 05-Mar-01 RMS Added clock calibration 22-Dec-00 RMS Added Bruce Ray's second terminal 15-Dec-00 RMS Added Charles Owen's CPU bootstrap 08-Dec-00 RMS Changes from Bruce Ray -- fixed trap test to include Nova 3 -- fixed DIV and DIVS divide by 0 -- fixed RETN to set SP from FP -- fixed IORST to preserve carry -- added "secret" Nova 4 PSHN/SAVEN instructions -- added plotter support 15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions 14-Apr-98 RMS Changed t_addr to unsigned 15-Sep-97 RMS Added read and write breakpoints The register state for the NOVA CPU is: AC[0:3]<0:15> general registers C carry flag PC<0:14> program counter The NOVA has three instruction formats: memory reference, I/O transfer, and operate. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0| op | AC |in| mode| displacement | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:4> mnemonic action 00000 JMP PC = MA 00001 JMS AC3 = PC, PC = MA 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0 001'n LDA ACn = M[MA] 010'n STA M[MA] = ACn <5:7> mode action 000 page zero direct MA = zext (IR<8:15>) 001 PC relative direct MA = PC + sext (IR<8:15>) 010 AC2 relative direct MA = AC2 + sext (IR<8:15>) 011 AC3 relative direct MA = AC3 + sext (IR<8:15>) 100 page zero indirect MA = M[zext (IR<8:15>)] 101 PC relative indirect MA = M[PC + sext (IR<8:15>)] 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)] 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)] Memory reference instructions can access an address space of 32K words. An instruction can directly reference the first 256 words of memory (called page zero), as well as 256 words relative to the PC, AC2, or AC3; it can indirectly access all 32K words. If an indirect address is in locations 00020-00027, the indirect address is incremented and rewritten to memory before use; if in 00030-00037, decremented and rewritten. The I/O transfer format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 1 1| AC | opcode |pulse| device | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The IOT instruction sends the opcode, pulse, and specified AC to the specified I/O device. The device may accept data, provide data, initiate or cancel operations, or skip on status. The operate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \______/ \___/ \___/ | | | | | | | | | | +--- reverse skip sense | | | | | +--- skip if C == 0 | | | | +--- skip if result == 0 | | | +--- don't load result | | +--- carry in (load as is, | | set to Zero, | | set to One, | | load Complement) | +--- shift (none, | left one, | right one, | byte swap) +--- operation (complement, negate, move, increment, add complement, subtract, add, and) The operate instruction can be microprogrammed to perform operations on the source and destination AC's and the Carry flag. Some notes from Bruce Ray: 1. DG uses the value of the autoindex location -before- the modification to determine if additional indirect address levels are to be performed. Most DG emulators conform to this standard, but some vendor machines (i.e. Point 4 Mark 8) do not. 2. Infinite indirect references may occur on unmapped systems and can "hang" the hardware. Some DG diagnostics perform 10,000s of references during a single instruction. 3. Nova 3 adds the following instructions to the standard Nova instruction set: trap instructions stack push/pop instructions save/return instructions stack register manipulation instructions unsigned MUL/DIV 4. Nova 4 adds the following instructions to the Nova 3 instruction set: signed MUL/DIV load/store byte secret (undocumented) stack instructions [PSHN, SAVN] 5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the same instruction code values on all machines. 6. Undocumented Nova 3 behaviour for LDB, STB and SAVN has been added to appropriate code. 7. Most 3rd party vendors had a user-controlled method to increase the logical address space from 32 KW to 64 KW. This capability came at the expense of disabling multi-level indirect addressing when the 64KW mode is in effect, and keeping DG multi-level indirect compatibility when 64KW mode is inactive. The most common implementation was to use an "NIOP ,CPU" instruction to control whether 32 KW or 64 KW addressing mode was wanted, and bit 15 (the least-significant bit of an accumulator) determined which mode was set: 0 = 32 KW (DG compatible), 1 = 64 KW. This feature has been implemented in our Nova emulation for all to enjoy. This routine is the instruction decode routine for the NOVA. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered infinite indirection loop unknown I/O device and STOP_DEV flag set I/O error in I/O simulator 2. Interrupts. Interrupts are maintained by four parallel variables: dev_done device done flags dev_disable device interrupt disable flags dev_busy device busy flags int_req interrupt requests In addition, int_req contains the interrupt enable and ION pending flags. If ION and ION pending are set, and at least one interrupt request is pending, then an interrupt occurs. Note that the 16b PIO mask must be mapped to the simulator's device bit mapping. 3. Non-existent memory. On the NOVA, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: nova_defs.h add interrupt request definition nova_sys.c add sim_devices entry */ #include "nova_defs.h" #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 INCA(x) (((x) + 1) & AMASK) #define DECA(x) (((x) - 1) & AMASK) #define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x)) #define STK_CHECK(x,y) if (((x) & 0377) < (y)) \ int_req = int_req | INT_STK #define IND_STEP(x) M[x] & A_IND; /* return next level indicator */ \ if ( ((x) <= AUTO_TOP) && ((x) >= AUTO_INC) ) \ if ( (x) < AUTO_DEC ) \ M[x] = (M[x] + 1) & DMASK; \ else \ M[x] = (M[x] - 1) & DMASK; \ x = M[x] & AMASK #define INCREMENT_PC PC = (PC + 1) & AMASK /* increment PC */ #define UNIT_V_MDV (UNIT_V_UF + 0) /* MDV present */ #define UNIT_V_STK (UNIT_V_UF + 1) /* stack instr */ #define UNIT_V_BYT (UNIT_V_UF + 2) /* byte instr */ #define UNIT_V_64KW (UNIT_V_UF + 3) /* 64KW mem support */ #define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ #define UNIT_MDV (1 << UNIT_V_MDV) #define UNIT_STK (1 << UNIT_V_STK) #define UNIT_BYT (1 << UNIT_V_BYT) #define UNIT_64KW (1 << UNIT_V_64KW) #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT | UNIT_64KW) #define UNIT_NOVA3 (UNIT_MDV | UNIT_STK) #define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT) #define UNIT_KERONIX (UNIT_MDV | UNIT_64KW) #define MODE_64K (cpu_unit.flags & UNIT_64KW) #define MODE_64K_ACTIVE ((cpu_unit.flags & UNIT_64KW) && (0xFFFF == AMASK)) typedef struct { int32 pc; int16 ir; int16 ac0 ; int16 ac1 ; int16 ac2 ; int16 ac3 ; int16 carry ; int16 sp ; int16 fp ; int32 devDone ; int32 devBusy ; int32 devDisable ; int32 devIntr ; } Hist_entry ; uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 AC[4] = { 0 }; /* accumulators */ int32 C = 0; /* carry flag */ int32 saved_PC = 0; /* program counter */ int32 SP = 0; /* stack pointer */ int32 FP = 0; /* frame pointer */ int32 SR = 0; /* switch register */ int32 dev_done = 0; /* device done flags */ int32 dev_busy = 0; /* device busy flags */ int32 dev_disable = 0; /* int disable flags */ int32 int_req = 0; /* interrupt requests */ int32 pimask = 0; /* priority int mask */ int32 pwr_low = 0; /* power fail flag */ int32 ind_max = 65536; /* iadr nest limit */ int32 stop_dev = 0; /* stop on ill dev */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ struct ndev dev_table[64]; /* dispatch table */ int32 AMASK = 077777 ; /* current memory address mask */ /* (default to 32KW) */ static int32 hist_p = 0 ; /* history pointer */ static int32 hist_cnt = 0 ; /* history count */ static Hist_entry * hist = NULL ; /* instruction history */ 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_boot (int32 unitno, DEVICE *dptr); t_stat build_devtab (void); t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) ; t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) ; static int hist_save( int32 pc, int32 our_ir ) ; char * devBitNames( int32 flags, char * ptr, char * sepStr ) ; void mask_out (int32 mask); extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern DEVICE * sim_devices[]; extern t_stat fprint_sym(FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_MDV, DFTMEMSIZE /* MAXMEMSIZE */ ) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, 15) }, { ORDATA (AC0, AC[0], 16) }, { ORDATA (AC1, AC[1], 16) }, { ORDATA (AC2, AC[2], 16) }, { ORDATA (AC3, AC[3], 16) }, { FLDATA (C, C, 16) }, { ORDATA (SP, SP, 16) }, { ORDATA (FP, FP, 16) }, { ORDATA (SR, SR, 16) }, { ORDATA (PI, pimask, 16) }, { FLDATA (ION, int_req, INT_V_ION) }, { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, { FLDATA (STKOVF, int_req, INT_V_STK) }, { FLDATA (PWR, pwr_low, 0) }, { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO }, { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO }, { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO }, { FLDATA (STOP_DEV, stop_dev, 0) }, { DRDATA (INDMAX, ind_max, 32), REG_NZ + PV_LEFT }, { ORDATA (AMASK, AMASK, 16) }, { DRDATA (MEMSIZE, cpu_unit.capac, 32), REG_NZ + PV_LEFT }, { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL }, { UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL }, { UNIT_IOPT, UNIT_KERONIX, "KERONIX", "KERONIX", NULL }, { UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL }, { UNIT_IOPT, UNIT_64KW, "EXT64KW", "EXT64KW", NULL }, { UNIT_IOPT, 0, "none", "NONE", NULL }, { UNIT_MSIZE, ( 4 * 1024), NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, ( 8 * 1024), NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, (12 * 1024), NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, (16 * 1024), NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, (20 * 1024), NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, (24 * 1024), NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, (28 * 1024), NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, (32 * 1024), NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, (36 * 1024), NULL, "36K", &cpu_set_size }, { UNIT_MSIZE, (40 * 1024), NULL, "40K", &cpu_set_size }, { UNIT_MSIZE, (44 * 1024), NULL, "44K", &cpu_set_size }, { UNIT_MSIZE, (48 * 1024), NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, (52 * 1024), NULL, "52K", &cpu_set_size }, { UNIT_MSIZE, (56 * 1024), NULL, "56K", &cpu_set_size }, { UNIT_MSIZE, (60 * 1024), NULL, "60K", &cpu_set_size }, { UNIT_MSIZE, (64 * 1024), NULL, "64K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &hist_set, &hist_show }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 16 /* = 64 KW, 15 = 32KW */, 1, 8, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; t_stat sim_instr (void) { int32 PC, IR, i; t_stat reason; /* Restore register state */ if (build_devtab () != SCPE_OK) /* build dispatch */ return SCPE_IERR; PC = saved_PC & AMASK; /* load local PC */ C = C & CBIT; mask_out (pimask); /* reset int system */ reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if ( (reason = sim_process_event ()) ) break; } if (int_req > INT_PENDING) { /* interrupt or exception? */ int32 MA, indf; if (int_req & INT_TRAP) { /* trap instruction? */ int_req = int_req & ~INT_TRAP ; /* clear */ PCQ_ENTRY; /* save old PC */ M[TRP_SAV] = (PC - 1) & AMASK; MA = TRP_JMP; /* jmp @47 */ } else { int_req = int_req & ~INT_ION; /* intr off */ PCQ_ENTRY; /* save old PC */ M[INT_SAV] = PC; if (int_req & INT_STK) { /* stack overflow? */ int_req = int_req & ~INT_STK; /* clear */ MA = STK_JMP; /* jmp @3 */ } else MA = INT_JMP; /* intr: jmp @1 */ } if ( MODE_64K_ACTIVE ) { indf = IND_STEP (MA); } else { for (i = 0, indf = 1; indf && (i < ind_max); i++) { indf = IND_STEP (MA); /* indirect loop */ } if (i >= ind_max) { reason = STOP_IND_INT; break; } } PC = MA; } /* end interrupt */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } IR = M[PC]; /* fetch instr */ if ( hist_cnt ) { hist_save( PC, IR ) ; /* PC, int_req unchanged */ } INCREMENT_PC ; int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ sim_interval = sim_interval - 1; /* Operate instruction */ if (IR & I_OPR) { /* operate? */ int32 src, srcAC, dstAC; srcAC = I_GETSRC (IR); /* get reg decodes */ dstAC = I_GETDST (IR); switch (I_GETCRY (IR)) { /* decode carry */ case 0: /* load */ src = AC[srcAC] | C; break; case 1: /* clear */ src = AC[srcAC]; break; case 2: /* set */ src = AC[srcAC] | CBIT; break; case 3: /* complement */ src = AC[srcAC] | (C ^ CBIT); break; } /* end switch carry */ switch (I_GETALU (IR)) { /* decode ALU */ case 0: /* COM */ src = src ^ DMASK; break; case 1: /* NEG */ src = ((src ^ DMASK) + 1) & CDMASK; break; case 2: /* MOV */ break; case 3: /* INC */ src = (src + 1) & CDMASK; break; case 4: /* ADC */ src = ((src ^ DMASK) + AC[dstAC]) & CDMASK; break; case 5: /* SUB */ src = ((src ^ DMASK) + AC[dstAC] + 1) & CDMASK; break; case 6: /* ADD */ src = (src + AC[dstAC]) & CDMASK; break; case 7: /* AND */ src = src & (AC[dstAC] | CBIT); break; } /* end switch oper */ switch (I_GETSHF (IR)) { /* decode shift */ case 0: /* nop */ break; case 1: /* L */ src = ((src << 1) | (src >> 16)) & CDMASK; break; case 2: /* R */ src = ((src >> 1) | (src << 16)) & CDMASK; break; case 3: /* S */ src = ((src & 0377) << 8) | ((src >> 8) & 0377) | (src & CBIT); break; } /* end switch shift */ switch (I_GETSKP (IR)) { /* decode skip */ case 0: /* nop */ if ((IR & I_NLD) && (cpu_unit.flags & UNIT_STK)) { int_req = int_req | INT_TRAP ; /* Nova 3 or 4 trap */ continue ; } break; case 1: /* SKP */ INCREMENT_PC ; break; case 2: /* SZC */ if (src < CBIT) INCREMENT_PC ; break; case 3: /* SNC */ if (src >= CBIT) INCREMENT_PC ; break; case 4: /* SZR */ if ((src & DMASK) == 0) INCREMENT_PC ; break; case 5: /* SNR */ if ((src & DMASK) != 0) INCREMENT_PC ; break; case 6: /* SEZ */ if (src <= CBIT) INCREMENT_PC ; break; case 7: /* SBN */ if (src > CBIT) INCREMENT_PC ; break; } /* end switch skip */ if ((IR & I_NLD) == 0) { /* load? */ AC[dstAC] = src & DMASK; C = src & CBIT; } /* end if load */ } /* end if operate */ /* Memory reference instructions */ else if (IR < 060000) { /* mem ref? */ int32 src, MA, indf; MA = I_GETDISP (IR); /* get disp */ switch (I_GETMODE (IR)) { /* decode mode */ case 0: /* page zero */ break; case 1: /* PC relative */ if (MA & DISPSIGN) MA = 0177400 | MA; MA = (MA + PC - 1) & AMASK; break; case 2: /* AC2 relative */ if (MA & DISPSIGN) MA = 0177400 | MA; MA = (MA + AC[2]) & AMASK; break; case 3: /* AC3 relative */ if (MA & DISPSIGN) MA = 0177400 | MA; MA = (MA + AC[3]) & AMASK; break; } /* end switch mode */ if ( (indf = IR & I_IND) ) { /* indirect? */ if ( MODE_64K_ACTIVE ) { /* 64k mode? */ indf = IND_STEP (MA); } else /* compat mode */ { for (i = 0; indf && (i < ind_max); i++) { /* count */ indf = IND_STEP (MA); /* resolve indirect */ } if (i >= ind_max) { /* too many? */ reason = STOP_IND; break; } } } switch (I_GETOPAC (IR)) { /* decode op + AC */ case 001: /* JSR */ AC[3] = PC; case 000: /* JMP */ PCQ_ENTRY; PC = MA; break; case 002: /* ISZ */ src = (M[MA] + 1) & DMASK; if (MEM_ADDR_OK(MA)) M[MA] = src; if (src == 0) INCREMENT_PC ; break; case 003: /* DSZ */ src = (M[MA] - 1) & DMASK; if (MEM_ADDR_OK(MA)) M[MA] = src; if (src == 0) INCREMENT_PC ; break; case 004: /* LDA 0 */ AC[0] = M[MA]; break; case 005: /* LDA 1 */ AC[1] = M[MA]; break; case 006: /* LDA 2 */ AC[2] = M[MA]; break; case 007: /* LDA 3 */ AC[3] = M[MA]; break; case 010: /* STA 0 */ if (MEM_ADDR_OK(MA)) M[MA] = AC[0]; break; case 011: /* STA 1 */ if (MEM_ADDR_OK(MA)) M[MA] = AC[1]; break; case 012: /* STA 2 */ if (MEM_ADDR_OK(MA)) M[MA] = AC[2]; break; case 013: /* STA 3 */ if (MEM_ADDR_OK(MA)) M[MA] = AC[3]; break; } /* end switch */ } /* end mem ref */ /* IOT instruction */ else { /* IOT */ int32 dstAC, pulse, code, device, iodata; dstAC = I_GETDST (IR); /* decode fields */ code = I_GETIOT (IR); pulse = I_GETPULSE (IR); device = I_GETDEV (IR); if (code == ioSKP) { /* IO skip? */ switch (pulse) { /* decode IR<8:9> */ case 0: /* skip if busy */ if ((device == DEV_CPU)? (int_req & INT_ION) != 0: (dev_busy & dev_table[device].mask) != 0) INCREMENT_PC ; break; case 1: /* skip if not busy */ if ((device == DEV_CPU)? (int_req & INT_ION) == 0: (dev_busy & dev_table[device].mask) == 0) INCREMENT_PC ; break; case 2: /* skip if done */ if ((device == DEV_CPU)? pwr_low != 0: (dev_done & dev_table[device].mask) != 0) INCREMENT_PC ; break; case 3: /* skip if not done */ if ((device == DEV_CPU)? pwr_low == 0: (dev_done & dev_table[device].mask) == 0) INCREMENT_PC ; break; } /* end switch */ } /* end IO skip */ /* Hmm, this means a Nova 3 _must_ have DEV_MDV enabled - not true in DG land */ else if (device == DEV_MDV) { switch (code) { /* case on opcode */ case ioNIO: /* frame ptr */ if (cpu_unit.flags & UNIT_STK) { if (pulse == iopN) FP = AC[dstAC] & AMASK ; if (pulse == iopC) AC[dstAC] = FP & AMASK ; } break; case ioDIA: /* load byte */ if (cpu_unit.flags & UNIT_BYT) { AC[dstAC] = (M[AC[pulse] >> 1] >> ((AC[pulse] & 1)? 0: 8)) & 0377 ; } else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */ { SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = FP; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); AC[3] = FP = SP & AMASK; STK_CHECK (SP, 5); } else { AC[dstAC] = 0; } break; case ioDOA: /* stack ptr */ if (cpu_unit.flags & UNIT_STK) { if (pulse == iopN) SP = AC[dstAC] & AMASK; if (pulse == iopC) AC[dstAC] = SP & AMASK; } break; case ioDIB: /* push, pop */ if (cpu_unit.flags & UNIT_STK) { if (pulse == iopN) { /* push (PSHA) */ SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; STK_CHECK (SP, 1); } if ((pulse == iopS) && /* Nova 4 pshn (PSHN) */ (cpu_unit.flags & UNIT_BYT)) { SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; if ( (SP & 0xFFFF) > (M[042] & 0xFFFF) ) { int_req = int_req | INT_STK ; } } if (pulse == iopC) { /* pop (POPA) */ AC[dstAC] = M[SP]; SP = DECA (SP); } } break; case ioDOB: /* store byte */ if (cpu_unit.flags & UNIT_BYT) { int32 MA, val; MA = AC[pulse] >> 1; val = AC[dstAC] & 0377; if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)? ((M[MA] & ~0377) | val) : ((M[MA] & 0377) | (val << 8)); } else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */ { SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = FP; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); AC[3] = FP = SP & AMASK; STK_CHECK (SP, 5); } break; case ioDIC: /* save, return */ if (cpu_unit.flags & UNIT_STK) { if (pulse == iopN) { /* save */ SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = FP; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); AC[3] = FP = SP & AMASK; STK_CHECK (SP, 5); } else if (pulse == iopC) { /* retn */ PCQ_ENTRY; SP = FP & AMASK; C = (M[SP] << 1) & CBIT; PC = M[SP] & AMASK; SP = DECA (SP); AC[3] = M[SP]; SP = DECA (SP); AC[2] = M[SP]; SP = DECA (SP); AC[1] = M[SP]; SP = DECA (SP); AC[0] = M[SP]; SP = DECA (SP); FP = AC[3] & AMASK; } else if ((pulse == iopS) && /* Nova 4 SAVN */ (cpu_unit.flags & UNIT_BYT)) { int32 frameSz = M[PC] ; PC = INCA (PC) ; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = FP; SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); AC[3] = FP = SP & AMASK ; SP = (SP + frameSz) & AMASK ; if (SP > M[042]) { int_req = int_req | INT_STK; } } } break; case ioDOC: if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV)) { /* Nova, Nova3 or Nova 4 */ uint32 mddata, uAC0, uAC1, uAC2; uAC0 = (uint32) AC[0]; uAC1 = (uint32) AC[1]; uAC2 = (uint32) AC[2]; if (pulse == iopP) { /* mul */ mddata = (uAC1 * uAC2) + uAC0; AC[0] = (mddata >> 16) & DMASK; AC[1] = mddata & DMASK; } if (pulse == iopS) { /* div */ if ((uAC0 >= uAC2) || (uAC2 == 0)) { C = CBIT; } else { C = 0; mddata = (uAC0 << 16) | uAC1; AC[1] = mddata / uAC2; AC[0] = mddata % uAC2; } } } else if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT) /* assuming UNIT_BYT = Nova 4 */) { int32 mddata; if (pulse == iopC) { /* muls */ mddata = (SEXT (AC[1]) * SEXT (AC[2])) + SEXT (AC[0]); AC[0] = (mddata >> 16) & DMASK; AC[1] = mddata & DMASK; } else if (pulse == iopN) { /* divs */ if ((AC[2] == 0) || /* overflow? */ ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777))) { C = CBIT; } else { mddata = (SEXT (AC[0]) << 16) | AC[1]; AC[1] = mddata / SEXT (AC[2]); AC[0] = mddata % SEXT (AC[2]); if ((AC[1] > 077777) || (AC[1] < -0100000)) { C = CBIT; } else { C = 0; } AC[0] = AC[0] & DMASK; } } } else if ((dstAC == 3) && (cpu_unit.flags & UNIT_STK)) /* if Nova 3 this is really a PSHA... 2007-Jun-01, BKR */ { SP = INCA (SP); if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; STK_CHECK (SP, 1); } break; } /* end case code */ } /* end if mul/div */ else if (device == DEV_CPU) { /* CPU control */ switch (code) { /* decode IR<5:7> */ case ioNIO: /* NIOP CPU ? */ if ( pulse == iopP ) if ( MODE_64K ) { /* Keronix/Point4/SCI/INI/IDP (and others) */ /* 64 KW memory extension: */ /* NIOP - set memory mode (32/64 KW) per AC: */ /* B15: 0 = 32 KW, 1 = 64 KW mode */ AMASK = (AC[dstAC] & 0x0001) ? 0177777 : 077777 ; } break ; case ioDIA: /* read switches */ AC[dstAC] = SR; break; case ioDIB: /* int ack */ AC[dstAC] = 0; DEV_UPDATE_INTR ; iodata = int_req & (-int_req); for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (iodata & dev_table[i].mask) { AC[dstAC] = i; break; } } break; case ioDOB: /* mask out */ mask_out (pimask = AC[dstAC]); break; case ioDIC: /* io reset */ reset_all (0); /* reset devices */ mask_out( 0 ) ; /* clear all device masks */ AMASK = 077777 ; /* reset memory mode */ break; case ioDOC: /* halt */ reason = STOP_HALT; break; } /* end switch code */ switch (pulse) { /* decode IR<8:9> */ case iopS: /* ion */ int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; break; case iopC: /* iof */ int_req = int_req & ~INT_ION; break; } /* end switch pulse */ } /* end CPU control */ else if (dev_table[device].routine) { /* normal device */ iodata = dev_table[device].routine (pulse, code, AC[dstAC]); reason = iodata >> IOT_V_REASON; if (code & 1) AC[dstAC] = iodata & 0177777; } /* bkr, 2007-May-30 * if device does not exist certain I/O instructions will still * return data: DIA/B/C will return idle data bus value and * SKPBZ/SKPDZ will sense zero value (and will therefore skip). * * Perform these non-supported device functions only if 'stop_dev' * is zero (i.e. I/O access trap is not in effect). */ else if ( stop_dev == 0 ) { switch (code) /* decode IR<5:7> */ { case ioDIA: case ioDIB: case ioDIC: AC[dstAC] = 0 ; /* idle I/O bus data */ break; case ioSKP: /* (This should have been caught in previous CPU skip code) */ if ( (pulse == 1 /* SKPBZ */) || (pulse == 3 /* SKPDZ */) ) { INCREMENT_PC ; } } /* end of 'switch' */ } /* end of handling non-existant device */ else reason = stop_dev; } /* end if IOT */ } /* end while */ /* Simulation halted */ saved_PC = PC; pcq_r->qptr = pcq_p; /* update pc q ptr */ return ( reason ) ; } /* New priority mask out */ void mask_out (int32 newmask) { int32 i; dev_disable = 0; for (i = DEV_LOW; i <= DEV_HIGH; i++) { if (newmask & dev_table[i].pi) dev_disable = dev_disable | dev_table[i].mask; } DEV_UPDATE_INTR ; return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int_req = int_req & ~(INT_ION | INT_STK | INT_TRAP); pimask = 0; dev_disable = 0; pwr_low = 0; AMASK = 077777 ; /* 32KW mode */ 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; } /* Alter memory size */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; t_addr 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; } /* Build dispatch table */ t_stat build_devtab (void) { DEVICE *dptr; DIB *dibp; int32 i, dn; for (i = 0; i < 64; i++) { /* clr dev_table */ dev_table[i].mask = 0; dev_table[i].pi = 0; dev_table[i].routine = NULL; } for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ if (!(dptr->flags & DEV_DIS) && /* enabled and */ ( (dibp = (DIB *) dptr->ctxt)) ) { /* defined DIB? */ dn = dibp->dnum; /* get dev num */ dev_table[dn].mask = dibp->mask; /* copy entries */ dev_table[dn].pi = dibp->pi; dev_table[dn].routine = dibp->routine; } } return SCPE_OK; } /* BKR notes: * * Data General APL (Automatic Program Load) boot code * * - This bootstrap code is called the "APL option" in DG documentation (Automatic * Program Load), and cost ~$400 USD (in 1970 - wow!) to load 32(10) words from * a PROM to main (core) memory location 0 - 32. * - This code is documented in various DG Nova programming manuals and was * quite static (i.e. no revisions or updates to code were made). * - switch register is used to determine device code and device type. * - lower 6-bits of switch register determines device code (0-63.). * - most significant bit determines if device is "low speed" or "high speed". * - "high speed" devices have effective boot program logic of: * * IORST * NIOS * JMP . * * - "high speed" devices use data channel (DCH) to read first sector/record * of device into memory (usually starting at location 0), which then over-writes * the 'JMP .' instruction of boot code. This usually has a jump to some other * device and operating system specific boot code that was loaded from the device. * - "low speed" devices are assumed to be sequential character-oriented devices * (i.e. Teletype (r) reader, paper tape reader). * - "low speed" devices are assumed to start read operations with a 'S' pulse, * read data buffer with a DIA instruction and have standard DG I/O Busy/Done logic. * - "low speed" devices usually read in a more full-featured 'binary loader' with * the APL boot code: * * DG paper tape: 091-000004-xx, Binary Loader (BLDR.AB) * * - The Binary Loader was in turn used to load tapes in the usual DG 'absolute binary' format. */ #define BOOT_START 00000 #define BOOT_LEN (sizeof(boot_rom) / sizeof(int32)) static const int32 boot_rom[] = { 0062677, /* IORST ;reset all I/O */ 0060477, /* READS 0 ;read SR into AC0 */ 0024026, /* LDA 1,C77 ;get dev mask */ 0107400, /* AND 0,1 ;isolate dev code */ 0124000, /* COM 1,1 ;- device code - 1 */ 0010014, /* LOOP: ISZ OP1 ;device code to all */ 0010030, /* ISZ OP2 ;I/O instructions */ 0010032, /* ISZ OP3 */ 0125404, /* INC 1,1,SZR ;done? */ 0000005, /* JMP LOOP ;no, increment again */ 0030016, /* LDA 2,C377 ;place JMP 377 into */ 0050377, /* STA 2,377 ;location 377 */ 0060077, /* OP1: 060077 ;start device (NIOS 0) */ 0101102, /* MOVL 0,0,SZC ;test switch 0, low speed? */ 0000377, /* C377: JMP 377 ;no - jmp 377 & wait */ 0004030, /* LOOP2: JSR GET+1 ;get a frame */ 0101065, /* MOVC 0,0,SNR ;is it non-zero? */ 0000017, /* JMP LOOP2 ;no, ignore */ 0004027, /* LOOP4: JSR GET ;yes, get full word */ 0046026, /* STA 1,@C77 ;store starting at 100 */ /* ;2's complement of word ct */ 0010100, /* ISZ 100 ;done? */ 0000022, /* JMP LOOP4 ;no, get another */ 0000077, /* C77: JMP 77 ;yes location ctr and */ /* ;jmp to last word */ 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */ /* OP2: */ 0063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */ 0000030, /* JMP LOOP3 ;no -- wait */ 0060477, /* OP3: 060477 ;y -- read in ac0 (DIAS 0,0) */ 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */ 0000030, /* JMP LOOP3 ;no go back after it */ 0125300, /* MOVS 1,1 ;yes swap them */ 0001400, /* JMP 0,3 ;rtn with full word */ 0000000 /* 0 ;padding */ }; t_stat cpu_boot (int32 unitno, DEVICE *dptr) { int32 i; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } /* 1-to-1 map for I/O devices */ int32 MapAddr (int32 map, int32 addr) { return addr; } /* History subsystem global routines t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc, void ** HistCookie, sizeof(usrHistInfo) ) ; t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc, void * HistCookie ) ; int hist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) local user struct: usrHistInfo local user routines: int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ; int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ; typedef struct { int hMax ; // total # entries in queue (0 = inactive) int hCount ; // current entry void * hPtr ; // pointer to save area int hSize ; // size of each user save area (not used by global routines?) } Hist_info ; */ /* generalized CPU execution trace */ #define HIST_IR_INVALID -1 #define HIST_MIN 0 /* 0 == deactivate history feature, else size of queue */ #define HIST_MAX 1000000 /* completely arbitrary max size value */ /* save history entry (proposed local routine) */ static int hist_save( int32 pc, int32 our_ir ) { Hist_entry * hist_ptr ; if ( hist ) if ( hist_cnt ) { hist_p = (hist_p + 1) ; /* next entry */ if ( hist_p >= hist_cnt ) { hist_p = 0 ; } hist_ptr = &hist[ hist_p ] ; /* (machine-specific stuff) */ hist_ptr->pc = pc ; hist_ptr->ir = our_ir ; hist_ptr->ac0 = AC[ 0 ] ; hist_ptr->ac1 = AC[ 1 ] ; hist_ptr->ac2 = AC[ 2 ] ; hist_ptr->ac3 = AC[ 3 ] ; hist_ptr->carry = C ; hist_ptr->fp = FP ; hist_ptr->sp = SP ; hist_ptr->devBusy = dev_busy ; hist_ptr->devDone = dev_done ; hist_ptr->devDisable = dev_disable ; hist_ptr->devIntr = int_req ; /* how 'bout state and AMASK? */ return ( hist_p ) ; } return ( -1 ) ; } /* end of 'hist_save' */ /* setup history save area (proposed global routine) */ t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) { int32 i, lnt ; t_stat r ; if ( cptr == NULL ) { for (i = 0 ; i < hist_cnt ; ++i ) { hist[i].pc = 0 ; hist[i].ir = HIST_IR_INVALID ; } hist_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 ) ; } hist_p = 0; if ( hist_cnt ) { free( hist ) ; hist_cnt = 0 ; hist = NULL ; } if ( lnt ) { hist = (Hist_entry *) calloc( lnt, sizeof(Hist_entry) ) ; if ( hist == NULL ) { return ( SCPE_MEM ) ; } hist_cnt = lnt ; } return ( SCPE_OK ) ; } /* end of 'hist_set' */ int hist_fprintf( FILE * fp, int itemNum, Hist_entry * hptr ) { t_value sim_eval ; if ( hptr ) { if ( itemNum == 0 ) { fprintf( fp, "\n\n" ) ; } fprintf( fp, "%05o / %06o %06o %06o %06o %06o %o ", (hptr->pc & 0x7FFF), (hptr->ir & 0xFFFF), (hptr->ac0 & 0xFFFF), (hptr->ac1 & 0xFFFF), (hptr->ac2 & 0xFFFF), (hptr->ac3 & 0xFFFF), ((hptr->carry) ? 1 : 0) ) ; if ( cpu_unit.flags & UNIT_STK /* Nova 3 or Nova 4 */ ) { fprintf( fp, "%06o %06o ", SP, FP ) ; } sim_eval = (hptr->ir & 0xFFFF) ; if ( (fprint_sym(fp, (hptr->pc & AMASK), &sim_eval, &cpu_unit, SWMASK ('M'))) > 0 ) { fprintf( fp, "(undefined) %04o", (hptr->ir & 0xFFFF) ) ; } /* display ION flag value, pend value? display devBusy, devDone, devIntr info? */ if ( 0 ) /* display INTRP codes? */ { char tmp[ 500 ] ; devBitNames( hptr->devIntr, tmp, NULL ) ; fprintf( fp, " %s", tmp ) ; } fprintf( fp, "\n" ) ; } return ( 0 ) ; } /* end of 'hist_fprintf' */ /* show execution history (proposed global routine) */ t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) { int32 k, di, lnt ; char * cptr = (char *) desc ; t_stat r ; Hist_entry * hptr ; if (hist_cnt == 0) { return ( SCPE_NOFNC ) ; /* enabled? */ } if ( cptr ) { /* number of entries specified */ lnt = (int32) get_uint( cptr, 10, hist_cnt, &r ) ; if ( (r != SCPE_OK) || (lnt == 0) ) { return ( SCPE_ARG ) ; } } else { lnt = hist_cnt ; /* display all entries */ } di = hist_p - lnt; /* work forward */ if ( di < 0 ) { di = di + hist_cnt ; } for ( k = 0 ; k < lnt ; ++k ) { /* print specified */ hptr = &hist[ (++di) % hist_cnt] ; /* entry pointer */ if ( hptr->ir != HIST_IR_INVALID ) /* valid entry? */ { hist_fprintf( st, k, hptr ) ; } /* end else instruction */ } /* end for */ return SCPE_OK; } /* end of 'hist_show' */ struct Dbits { int32 dBit ; int32 dInvertMask ; char * dName ; } devBits [] = { { INT_TRAP, 0, "TRAP" }, /* (in order of approximate DG interrupt mask priority) */ { INT_ION, 0, "ION" }, { INT_NO_ION_PENDING, 1, "IONPND" }, /* (invert this logic to provide cleaner display) */ { INT_STK, 0, "STK" }, { INT_PIT, 0, "PIT" }, { INT_DKP, 0, "DKP" }, { INT_DSK, 0, "DSK" }, { INT_MTA, 0, "MTA" }, { INT_LPT, 0, "LPT" }, { INT_PTR, 0, "PTR" }, { INT_PTP, 0, "PTP" }, { INT_PLT, 0, "PLT" }, { INT_CLK, 0, "CLK" }, { INT_ALM, 0, "ALM" }, { INT_QTY, 0, "QTY" }, { INT_TTO1, 0, "TTO1" }, { INT_TTI1, 0, "TTI1" }, { INT_TTO, 0, "TTO" }, { INT_TTI, 0, "TTI" }, { 0, 0, NULL } } ; char * devBitNames( int32 flags, char * ptr, char * sepStr ) { int a ; if ( ptr ) { *ptr = 0 ; for ( a = 0 ; (devBits[a].dBit) ; ++a ) if ( devBits[a].dBit & ((devBits[a].dInvertMask)? ~flags : flags) ) { if ( *ptr ) { strcat( ptr, (sepStr) ? sepStr : " " ) ; strcat( ptr, devBits[a].dName ) ; } else { strcpy( ptr, devBits[a].dName ) ; } } } return ( ptr ) ; } /* end of 'devBitNames' */ simh-3.8.1/NOVA/nova_mta.c0000644000175000017500000006537011112562172013365 0ustar vlmvlm/* nova_mta.c: NOVA magnetic tape simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mta magnetic tape 04-Jul-07 BKR fixed boot code to properly boot self-boot tapes; boot routine now uses standard DG APL boot code; device name changed to DG's MTA from DEC's MT. 16-Aug-05 RMS Fixed C++ declaration and cast problems 18-Mar-05 RMS Added attached test to detach routine 22-Nov-03 CEO DIB returns # records skipped after space fwd 22-Nov-03 CEO Removed cancel of tape events in IORST 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library 30-Oct-02 RMS Fixed BOT handling, added error record handling 08-Oct-02 RMS Added DIB 30-Sep-02 RMS Revamped error handling 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length test 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed POS, USTAT, FLG to an array 26-Apr-01 RMS Added device enable/disable support 18-Apr-01 RMS Changed to rewind tape before boot 10-Dec-00 RMS Added Eclipse support from Charles Owen 15-Oct-00 RMS Editorial changes 11-Nov-98 CEO Removed clear of mta_ma on iopC 04-Oct-98 RMS V2.4 magtape format 18-Jan-97 RMS V2.3 magtape format 29-Jun-96 RMS Added unit enable/disable support Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte count is little endian byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0 and are not duplicated; end of tape by end of file. */ #include "nova_defs.h" #include "sim_tape.h" #define MTA_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ #define MTA_MAXFR (1 << 16) /* max record lnt */ #define WC_SIZE (1 << 14) /* max word count */ #define WC_MASK (WC_SIZE - 1) /* Command/unit */ #define CU_CI 0100000 /* clear interrupt */ #define CU_EP 0002000 /* poll enable */ #define CU_DE 0001000 /* disable erase */ #define CU_DA 0000400 /* disable autoretry */ #define CU_PE 0000400 /* PE mode */ #define CU_V_CMD 3 /* command */ #define CU_M_CMD 027 #define CU_READ 000 #define CU_REWIND 001 #define CU_CMODE 002 #define CU_SPACEF 003 #define CU_SPACER 004 #define CU_WRITE 005 #define CU_WREOF 006 #define CU_ERASE 007 #define CU_READNS 020 #define CU_UNLOAD 021 #define CU_DMODE 022 #define CU_V_UNIT 0 /* unit */ #define CU_M_UNIT 07 #define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) #define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) /* Status 1 - stored in mta_sta<31:16> or (*) uptr->USTAT<31:16> */ #define STA_ERR1 (0100000u << 16) /* error */ #define STA_DLT (0040000 << 16) /* data late */ #define STA_REW (0020000 << 16) /* *rewinding */ #define STA_ILL (0010000 << 16) /* illegal */ #define STA_HDN (0004000 << 16) /* high density */ #define STA_DAE (0002000 << 16) /* data error */ #define STA_EOT (0001000 << 16) /* *end of tape */ #define STA_EOF (0000400 << 16) /* *end of file */ #define STA_BOT (0000200 << 16) /* *start of tape */ #define STA_9TK (0000100 << 16) /* nine track */ #define STA_BAT (0000040 << 16) /* bad tape */ #define STA_CHG (0000010 << 16) /* status change */ #define STA_WLK (0000004 << 16) /* *write lock */ #define STA_ODD (0000002 << 16) /* odd character */ #define STA_RDY (0000001 << 16) /* *drive ready */ /* Status 2 - stored in mta_sta<15:0> or (*) uptr->USTAT<15:0> */ #define STA_ERR2 0100000 /* error */ #define STA_RWY 0040000 /* runaway tape */ #define STA_FGP 0020000 /* false gap */ #define STA_CDL 0004000 /* corrected dlt */ #define STA_V_UNIT 8 #define STA_M_UNIT 07 /* unit */ #define STA_WCO 0000200 /* word count ovflo */ #define STA_BDS 0000100 /* bad signal */ #define STA_OVS 0000040 /* overskew */ #define STA_CRC 0000020 /* check error */ #define STA_STE 0000010 /* single trk error */ #define STA_FPR 0000004 /* false preamble */ #define STA_FMT 0000002 /* format error */ #define STA_PEM 0000001 /* *PE mode */ #define STA_EFLGS1 (STA_DLT | STA_ILL | STA_DAE | STA_EOT | \ STA_EOF | STA_BOT | STA_BAT | STA_ODD) #define STA_EFLGS2 (STA_FGP | STA_CDL | STA_BDS | STA_OVS | \ STA_CRC | STA_FPR | STA_FPR) /* set error 2 */ #define STA_CLR ((020 << 16) | 0010000) /* always clear */ #define STA_SET (STA_HDN | STA_9TK) /* always set */ #define STA_DYN (STA_REW | STA_EOT | STA_EOF | STA_BOT | \ STA_WLK | STA_RDY | STA_PEM) /* kept in USTAT */ #define STA_MON (STA_REW | STA_BOT | STA_WLK | STA_RDY | \ STA_PEM) /* set status chg */ extern uint16 M[]; extern UNIT cpu_unit; extern int32 int_req, dev_busy, dev_done, dev_disable; extern int32 SR, AMASK; extern t_stat cpu_boot(int32 unitno, DEVICE * dptr ) ; int32 mta_ma = 0; /* memory address */ int32 mta_wc = 0; /* word count */ int32 mta_cu = 0; /* command/unit */ int32 mta_sta = 0; /* status register */ int32 mta_ep = 0; /* enable polling */ int32 mta_cwait = 100; /* command latency */ int32 mta_rwait = 100; /* record latency */ uint8 *mtxb = NULL; /* transfer buffer */ DEVICE mta_dev; int32 mta (int32 pulse, int32 code, int32 AC); t_stat mta_svc (UNIT *uptr); t_stat mta_reset (DEVICE *dptr); t_stat mta_boot (int32 unitno, DEVICE *dptr); t_stat mta_attach (UNIT *uptr, char *cptr); t_stat mta_detach (UNIT *uptr); int32 mta_updcsta (UNIT *uptr); void mta_upddsta (UNIT *uptr, int32 newsta); t_stat mta_map_err (UNIT *uptr, t_stat st); t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); static const int ctype[32] = { /* c vs r timing */ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 }; /* MTA data structures mta_dev MTA device descriptor mta_unit MTA unit list mta_reg MTA register list mta_mod MTA modifier list */ DIB mta_dib = { DEV_MTA, INT_MTA, PI_MTA, &mta }; UNIT mta_unit[] = { { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } }; REG mta_reg[] = { { ORDATA (CU, mta_cu, 16) }, { ORDATA (MA, mta_ma, 16) }, { ORDATA (WC, mta_wc, 16) }, { GRDATA (STA1, mta_sta, 8, 16, 16) }, { ORDATA (STA2, mta_sta, 16) }, { FLDATA (EP, mta_ep, 0) }, { FLDATA (BUSY, dev_busy, INT_V_MTA) }, { FLDATA (DONE, dev_done, INT_V_MTA) }, { FLDATA (DISABLE, dev_disable, INT_V_MTA) }, { FLDATA (INT, int_req, INT_V_MTA) }, { DRDATA (CTIME, mta_cwait, 24), PV_LEFT }, { DRDATA (RTIME, mta_rwait, 24), PV_LEFT }, { URDATA (UST, mta_unit[0].USTAT, 8, 32, 0, MTA_NUMDR, 0) }, { URDATA (POS, mta_unit[0].pos, 8, T_ADDR_W, 0, MTA_NUMDR, REG_RO | PV_LEFT) }, { NULL } }; MTAB mta_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mta_vlock }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mta_vlock }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { 0 } }; DEVICE mta_dev = { "MTA", mta_unit, mta_reg, mta_mod, MTA_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mta_reset, &mta_boot, &mta_attach, &mta_detach, &mta_dib, DEV_DISABLE }; /* IOT routine */ int32 mta (int32 pulse, int32 code, int32 AC) { UNIT *uptr; int32 u, c, rval; rval = 0; uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ switch (code) { /* decode IR<5:7> */ case ioDIA: /* DIA */ rval = (mta_updcsta (uptr) >> 16) & DMASK; /* return status 1 */ break; case ioDOA: /* DOA */ /* if (AC & CU_CI) ... clear ep int */ mta_cu = AC; /* save cmd/unit */ uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ mta_updcsta (uptr); /* update status */ break; case ioDIB: /* DIB */ rval = mta_ma & AMASK; /* return ma */ break; case ioDOB: /* DOB */ mta_ma = AC & AMASK; /* save ma */ break; case ioDIC: /* DIC */ rval = mta_updcsta (uptr) & DMASK; /* return status 2 */ break; case ioDOC: /* DOC */ mta_wc = ((AC & 040000) << 1) | (AC & 077777); /* save wc */ break; } /* end switch code */ switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ c = GET_CMD (mta_cu); /* get command */ if (dev_busy & INT_MTA) /* ignore if busy */ break; if ((uptr->USTAT & STA_RDY) == 0) { /* drive not ready? */ mta_sta = mta_sta | STA_ILL; /* illegal op */ dev_busy = dev_busy & ~INT_MTA; /* clear busy */ dev_done = dev_done | INT_MTA; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); } else if ((c == CU_REWIND) || (c == CU_UNLOAD)) { /* rewind, unload? */ mta_upddsta (uptr, (uptr->USTAT & /* update status */ ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)) | STA_REW); sim_activate (uptr, mta_rwait); /* start IO */ if (c == CU_UNLOAD) detach_unit (uptr); } else { mta_sta = 0; /* clear errors */ dev_busy = dev_busy | INT_MTA; /* set busy */ dev_done = dev_done & ~INT_MTA; /* clear done */ int_req = int_req & ~INT_MTA; /* clear int */ if (ctype[c]) sim_activate (uptr, mta_cwait); else { mta_upddsta (uptr, uptr->USTAT & ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)); sim_activate (uptr, mta_rwait); } } mta_updcsta (uptr); /* update status */ break; case iopC: /* clear */ for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ uptr = mta_dev.units + u; /* cancel IO */ if (sim_is_active (uptr) && !(uptr->USTAT & STA_REW)) { mta_upddsta (uptr, uptr->USTAT | STA_RDY); sim_cancel (uptr); } } dev_busy = dev_busy & ~INT_MTA; /* clear busy */ dev_done = dev_done & ~INT_MTA; /* clear done */ int_req = int_req & ~INT_MTA; /* clear int */ mta_sta = mta_cu = 0; /* clear registers */ mta_updcsta (&mta_unit[0]); /* update status */ break; } /* end case pulse */ return rval; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, clear busy, set done, interrupt */ t_stat mta_svc (UNIT *uptr) { int32 c, p, pa, u; t_mtrlnt i, cbc, tbc, wc; uint16 c1, c2; t_stat st, r = SCPE_OK; u = uptr - mta_dev.units; /* get unit number */ c = GET_CMD (mta_cu); /* command */ wc = WC_SIZE - (mta_wc & WC_MASK); /* io wc */ if (uptr->USTAT & STA_REW) { /* rewind? */ sim_tape_rewind (uptr); /* update tape */ mta_upddsta (uptr, (uptr->USTAT & ~STA_REW) | STA_BOT | STA_RDY); if (u == GET_UNIT (mta_cu)) mta_updcsta (uptr); return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ mta_upddsta (uptr, 0); /* unit off line */ mta_sta = mta_sta | STA_ILL; /* illegal operation */ } else switch (c) { /* case on command */ case CU_CMODE: /* controller mode */ mta_ep = mta_cu & CU_EP; break; case CU_DMODE: /* drive mode */ if (!sim_tape_bot (uptr)) /* must be BOT */ mta_sta = mta_sta | STA_ILL; else mta_upddsta (uptr, (mta_cu & CU_PE)? /* update drv status */ uptr->USTAT | STA_PEM: uptr->USTAT & ~ STA_PEM); break; case CU_READ: /* read */ case CU_READNS: /* read non-stop */ st = sim_tape_rdrecf (uptr, mtxb, &tbc, MTA_MAXFR); /* read rec */ if (st == MTSE_RECE) /* rec in err? */ mta_sta = mta_sta | STA_DAE; else if (st != MTSE_OK) { /* other error? */ r = mta_map_err (uptr, st); /* map error */ break; } cbc = wc * 2; /* expected bc */ if (tbc & 1) /* odd byte count? */ mta_sta = mta_sta | STA_ODD; if (tbc > cbc) /* too big? */ mta_sta = mta_sta | STA_WCO; else { cbc = tbc; /* no, use it */ wc = (cbc + 1) / 2; /* adjust wc */ } for (i = p = 0; i < wc; i++) { /* copy buf to mem */ c1 = mtxb[p++]; c2 = mtxb[p++]; pa = MapAddr (0, mta_ma); /* map address */ if (MEM_ADDR_OK (pa)) M[pa] = (c1 << 8) | c2; mta_ma = (mta_ma + 1) & AMASK; } mta_wc = (mta_wc + wc) & DMASK; mta_upddsta (uptr, uptr->USTAT | STA_RDY); break; case CU_WRITE: /* write */ tbc = wc * 2; /* io byte count */ for (i = p = 0; i < wc; i++) { /* copy to buffer */ pa = MapAddr (0, mta_ma); /* map address */ mtxb[p++] = (M[pa] >> 8) & 0377; mtxb[p++] = M[pa] & 0377; mta_ma = (mta_ma + 1) & AMASK; } if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) { /* write rec, err? */ r = mta_map_err (uptr, st); /* map error */ mta_ma = (mta_ma - wc) & AMASK; /* restore wc */ } else mta_wc = 0; /* clear wc */ mta_upddsta (uptr, uptr->USTAT | STA_RDY); break; case CU_WREOF: /* write eof */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mta_map_err (uptr, st); /* map error */ else mta_upddsta (uptr, uptr->USTAT | STA_EOF | STA_RDY); break; case CU_ERASE: /* erase */ if (sim_tape_wrp (uptr)) /* write protected? */ r = mta_map_err (uptr, MTSE_WRP); /* map error */ else mta_upddsta (uptr, uptr->USTAT | STA_RDY); break; case CU_SPACEF: /* space forward */ do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ r = mta_map_err (uptr, st); /* map error */ break; } } while (mta_wc != 0); mta_upddsta (uptr, uptr->USTAT | STA_RDY); mta_ma = mta_wc; /* word count = # records */ break; case CU_SPACER: /* space reverse */ do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ r = mta_map_err (uptr, st); /* map error */ break; } } while (mta_wc != 0); mta_upddsta (uptr, uptr->USTAT | STA_RDY); mta_ma = mta_wc; /* word count = # records */ break; default: /* reserved */ mta_sta = mta_sta | STA_ILL; mta_upddsta (uptr, uptr->USTAT | STA_RDY); break; } /* end case */ mta_updcsta (uptr); /* update status */ dev_busy = dev_busy & ~INT_MTA; /* clear busy */ dev_done = dev_done | INT_MTA; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); return r; } /* Update controller status */ int32 mta_updcsta (UNIT *uptr) /* update ctrl */ { mta_sta = (mta_sta & ~(STA_DYN | STA_CLR | STA_ERR1 | STA_ERR2)) | (uptr->USTAT & STA_DYN) | STA_SET; if (mta_sta & STA_EFLGS1) mta_sta = mta_sta | STA_ERR1; if (mta_sta & STA_EFLGS2) mta_sta = mta_sta | STA_ERR2; return mta_sta; } /* Update drive status */ void mta_upddsta (UNIT *uptr, int32 newsta) /* drive status */ { int32 change; if ((uptr->flags & UNIT_ATT) == 0) /* offline? */ newsta = 0; change = (uptr->USTAT ^ newsta) & STA_MON; /* changes? */ uptr->USTAT = newsta & STA_DYN; /* update status */ if (change) { /* if (mta_ep) { /* if polling */ /* u = uptr - mta_dev.units; /* unit num */ /* mta_sta = (mta_sta & ~STA_UNIT) | (u << STA_V_UNIT); /* set polling interupt... /* } */ mta_sta = mta_sta | STA_CHG; /* flag change */ } return; } /* Map tape error status */ t_stat mta_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ mta_upddsta (uptr, uptr->USTAT | STA_WLK | STA_RDY); case MTSE_UNATT: /* unattached */ mta_sta = mta_sta | STA_ILL; case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_TMK: /* end of file */ mta_upddsta (uptr, uptr->USTAT | STA_RDY | STA_EOF); break; case MTSE_IOERR: /* IO error */ mta_sta = mta_sta | STA_DAE; /* data error */ mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ return SCPE_IOERR; case MTSE_INVRL: /* invalid rec lnt */ mta_sta = mta_sta | STA_DAE; /* data error */ mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ mta_sta = mta_sta | STA_DAE; /* data error */ mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ break; case MTSE_EOM: /* end of medium */ mta_sta = mta_sta | STA_BAT; /* bad tape */ mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ break; case MTSE_BOT: /* reverse into BOT */ mta_upddsta (uptr, uptr->USTAT | STA_RDY | STA_BOT); break; case MTSE_WRP: /* write protect */ mta_upddsta (uptr, uptr->USTAT | STA_WLK | STA_RDY); mta_sta = mta_sta | STA_ILL; /* illegal operation */ break; } return SCPE_OK; } /* Reset routine */ t_stat mta_reset (DEVICE *dptr) { int32 u; UNIT *uptr; dev_busy = dev_busy & ~INT_MTA; /* clear busy */ dev_done = dev_done & ~INT_MTA; /* clear done, int */ int_req = int_req & ~INT_MTA; mta_cu = mta_wc = mta_ma = mta_sta = 0; /* clear registers */ mta_ep = 0; /* AOS Installer does an IORST after a tape rewind command but before it can be serviced, yet expects the tape to have been rewound */ for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ uptr = mta_dev.units + u; if (sim_is_active (uptr) && /* active and */ (uptr->flags & STA_REW)) /* rewinding? */ sim_tape_rewind (uptr); /* update tape */ sim_tape_reset (uptr); /* clear pos flag */ sim_cancel (uptr); /* cancel activity */ if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_RDY | (uptr->USTAT & STA_PEM) | (sim_tape_wrp (uptr)? STA_WLK: 0) | (sim_tape_bot (uptr)? STA_BOT: 0); else uptr->USTAT = 0; } mta_updcsta (&mta_unit[0]); /* update status */ if (mtxb == NULL) mtxb = (uint8 *) calloc (MTA_MAXFR, sizeof (uint8)); if (mtxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat mta_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; if (!sim_is_active (uptr)) mta_upddsta (uptr, STA_RDY | STA_BOT | STA_PEM | (sim_tape_wrp (uptr)? STA_WLK: 0)); return r; } /* Detach routine */ t_stat mta_detach (UNIT* uptr) { if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (!sim_is_active (uptr)) mta_upddsta (uptr, 0); return sim_tape_detach (uptr); } /* Write lock/unlock validate routine */ t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) { if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) mta_upddsta (uptr, uptr->USTAT | STA_WLK); else mta_upddsta (uptr, uptr->USTAT & ~STA_WLK); return SCPE_OK; } /* Boot routine */ t_stat mta_boot (int32 unitno, DEVICE *dptr) { sim_tape_rewind( &mta_unit[unitno] ) ; /* use common rewind/reset code device reset rewind 'tape' file device unit controller */ cpu_boot( unitno, dptr ) ; SR = 0100000 + DEV_MTA ; return ( SCPE_OK ); } /* end of 'mta_boot' */ simh-3.8.1/NOVA/nova_dsk.c0000644000175000017500000003044511112562172013360 0ustar vlmvlm/* nova_dsk.c: 4019 fixed head disk simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dsk fixed head disk 04-Jul-07 BKR device name changed to DG's DSK from DEC's DK, DEV_xxx macros now used for consistency, added secret DG DIC function, fixed boot info table size calculation 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) 04-Jan-04 RMS Changed sim_fsize calling sequence 26-Jul-03 RMS Fixed bug in set size routine 14-Mar-03 RMS Fixed variable capacity interaction with save/restore 03-Mar-03 RMS Fixed variable capacity and autosizing 03-Oct-02 RMS Added DIB 06-Jan-02 RMS Revised enable/disable support 23-Aug-01 RMS Fixed bug in write watermarking 26-Apr-01 RMS Added device enable/disable support 10-Dec-00 RMS Added Eclipse support 15-Oct-00 RMS Editorial changes 14-Apr-99 RMS Changed t_addr to unsigned The 4019 is a head-per-track disk. To minimize overhead, the entire disk is buffered in memory. */ #include "nova_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT 07 #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define DSK_NUMWD 256 /* words/sector */ #define DSK_NUMSC 8 /* sectors/track */ #define DSK_NUMTR 128 /* tracks/disk */ #define DSK_DKSIZE (DSK_NUMTR*DSK_NUMSC*DSK_NUMWD) /* words/disk */ #define DSK_AMASK ((DSK_NUMDK*DSK_NUMTR*DSK_NUMSC) - 1) /* address mask */ #define DSK_NUMDK 8 /* disks/controller */ #define GET_DISK(x) (((x) / (DSK_NUMSC * DSK_NUMTR)) & (DSK_NUMDK - 1)) /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ /* Status register */ #define DSKS_WLS 020 /* write lock status */ #define DSKS_DLT 010 /* data late error */ #define DSKS_NSD 004 /* non-existent disk */ #define DSKS_CRC 002 /* parity error */ #define DSKS_ERR 001 /* error summary */ #define DSKS_ALLERR (DSKS_WLS | DSKS_DLT | DSKS_NSD | DSKS_CRC | DSKS_ERR) /* Map logical sector numbers to physical sector numbers (indexed by track<2:0>'sector) */ static const int32 sector_map[] = { 0, 2, 4, 6, 1, 3, 5, 7, 1, 3, 5, 7, 2, 4, 6, 0, 2, 4, 6, 0, 3, 5, 7, 1, 3, 5, 7, 1, 4, 6, 0, 2, 4, 6, 0, 2, 5, 7, 1, 3, 5, 7, 1, 3, 6, 0, 2, 4, 6, 0, 2, 4, 7, 1, 3, 5, 7, 1, 3, 5, 0, 2, 4, 6 }; #define DSK_MMASK 077 #define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DSK_NUMSC))) extern uint16 M[]; extern UNIT cpu_unit; extern int32 int_req, dev_busy, dev_done, dev_disable; extern int32 saved_PC, SR, AMASK; int32 dsk_stat = 0; /* status register */ int32 dsk_da = 0; /* disk address */ int32 dsk_ma = 0; /* memory address */ int32 dsk_wlk = 0; /* wrt lock switches */ int32 dsk_stopioe = 0; /* stop on error */ int32 dsk_time = 100; /* time per sector */ DEVICE dsk_dev; int32 dsk (int32 pulse, int32 code, int32 AC); t_stat dsk_svc (UNIT *uptr); t_stat dsk_reset (DEVICE *dptr); t_stat dsk_boot (int32 unitno, DEVICE *dptr); t_stat dsk_attach (UNIT *uptr, char *cptr); t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* DSK data structures dsk_dev device descriptor dsk_unit unit descriptor dsk_reg register list */ DIB dsk_dib = { DEV_DSK, INT_DSK, PI_DSK, &dsk }; UNIT dsk_unit = { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DSK_DKSIZE) }; REG dsk_reg[] = { { ORDATA (STAT, dsk_stat, 16) }, { ORDATA (DA, dsk_da, 16) }, { ORDATA (MA, dsk_ma, 16) }, { FLDATA (BUSY, dev_busy, INT_V_DSK) }, { FLDATA (DONE, dev_done, INT_V_DSK) }, { FLDATA (DISABLE, dev_disable, INT_V_DSK) }, { FLDATA (INT, int_req, INT_V_DSK) }, { ORDATA (WLK, dsk_wlk, 8) }, { DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, dsk_stopioe, 0) }, { NULL } }; MTAB dsk_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &dsk_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &dsk_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &dsk_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &dsk_set_size }, { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &dsk_set_size }, { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &dsk_set_size }, { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &dsk_set_size }, { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &dsk_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { 0 } }; DEVICE dsk_dev = { "DSK", &dsk_unit, dsk_reg, dsk_mod, 1, 8, 21, 1, 8, 16, NULL, NULL, &dsk_reset, &dsk_boot, &dsk_attach, NULL, &dsk_dib, DEV_DISABLE }; /* IOT routine */ int32 dsk (int32 pulse, int32 code, int32 AC) { int32 t, rval; rval = 0; switch (code) { /* decode IR<5:7> */ case ioDIA: /* DIA */ rval = dsk_stat & DSKS_ALLERR; /* read status */ break; case ioDOA: /* DOA */ dsk_da = AC & DSK_AMASK; /* save disk addr */ break; case ioDIB: /* DIB */ rval = dsk_ma & AMASK; /* read mem addr */ break; case ioDOB: /* DOB */ dsk_ma = AC & AMASK; /* save mem addr */ break; case ioDIC: /* DIC - undocumented DG feature(!) */ rval = 256 ; /* return fixed sector size for DG for now */ break ; } /* end switch code */ if (pulse) { /* any pulse? */ DEV_CLR_BUSY( INT_DSK ) ; DEV_CLR_DONE( INT_DSK ) ; DEV_UPDATE_INTR ; dsk_stat = 0; /* clear status */ sim_cancel (&dsk_unit); /* stop I/O */ } if ((pulse == iopP) && ((dsk_wlk >> GET_DISK (dsk_da)) & 1)) { /* wrt lock? */ DEV_SET_DONE( INT_DSK ) ; DEV_UPDATE_INTR ; dsk_stat = DSKS_ERR + DSKS_WLS; /* set status */ return rval; } if (pulse & 1) { /* read or write? */ if (((uint32) (dsk_da * DSK_NUMWD)) >= dsk_unit.capac) { /* inv sev? */ DEV_SET_DONE( INT_DSK ) ; DEV_UPDATE_INTR ; dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ return rval; /* done */ } dsk_unit.FUNC = pulse; /* save command */ DEV_SET_BUSY( INT_DSK ) ; DEV_UPDATE_INTR ; t = sector_map[dsk_da & DSK_MMASK] - GET_SECTOR (dsk_time); if (t < 0) t = t + DSK_NUMSC; sim_activate (&dsk_unit, t * dsk_time); /* activate */ } return rval; } /* Unit service */ t_stat dsk_svc (UNIT *uptr) { int32 i, da, pa; int16 *fbuf = uptr->filebuf; DEV_CLR_BUSY( INT_DSK ) ; DEV_SET_DONE( INT_DSK ) ; DEV_UPDATE_INTR ; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ return IORETURN (dsk_stopioe, SCPE_UNATT); } da = dsk_da * DSK_NUMWD; /* calc disk addr */ if (uptr->FUNC == iopS) { /* read? */ for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */ if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da + i]; } dsk_ma = (dsk_ma + DSK_NUMWD) & AMASK; } else if (uptr->FUNC == iopP) { /* write? */ for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */ fbuf[da + i] = M[pa]; } if (((uint32) (da + i)) >= uptr->hwmark) /* past end? */ uptr->hwmark = da + i + 1; /* upd hwmark */ dsk_ma = (dsk_ma + DSK_NUMWD + 3) & AMASK; } dsk_stat = 0; /* set status */ return SCPE_OK; } /* Reset routine */ t_stat dsk_reset (DEVICE *dptr) { dsk_stat = dsk_da = dsk_ma = 0; DEV_CLR_BUSY( INT_DSK ) ; DEV_CLR_DONE( INT_DSK ) ; DEV_UPDATE_INTR ; sim_cancel (&dsk_unit); return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 0375 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) static const int32 boot_rom[] = { 0062677 /* IORST ; reset the I/O system */ , 0060120 /* NIOS DSK ; start the disk */ , 0000377 /* JMP 377 ; wait for the world */ } ; t_stat dsk_boot (int32 unitno, DEVICE *dptr) { int32 i; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = (uint16) boot_rom[i]; saved_PC = BOOT_START; SR = 0100000 + DEV_DSK; return SCPE_OK; } /* Attach routine */ t_stat dsk_attach (UNIT *uptr, char *cptr) { uint32 sz, p; uint32 ds_bytes = DSK_DKSIZE * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= DSK_NUMDK) p = DSK_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * DSK_DKSIZE; /* set capacity */ return attach_unit (uptr, cptr); } /* Change disk size */ t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETP (val) * DSK_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return SCPE_OK; } simh-3.8.1/NOVA/nova_sys.c0000644000175000017500000013070311112562624013415 0ustar vlmvlm/* nova_sys.c: NOVA simulator interface Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Jul-07 BKR DEC's IOF/ION changed to DG's INTDS/INTEN mnemonic, Fixed QTY/ADCV device name, RDSW changed to DDG's READS mnemonic, fixed/enhanced 'load' command for DG-compatible binary tape format 26-Mar-04 RMS Fixed warning with -std=c99 14-Jan-04 BKR Added support for QTY and ALM 04-Jan-04 RMS Fixed 64b issues found by VMS 8.1 24-Nov-03 CEO Added symbolic support for LEF instruction 17-Sep-01 RMS Removed multiconsole support 31-May-01 RMS Added multiconsole support 14-Mar-01 RMS Revised load/dump interface (again) 22-Dec-00 RMS Added second terminal support 10-Dec-00 RMS Added Eclipse support 08-Dec-00 BKR Added plotter support 30-Oct-00 RMS Added support for examine to file 15-Oct-00 RMS Added stack, byte, trap instructions 14-Apr-99 RMS Changed t_addr to unsigned 27-Oct-98 RMS V2.4 load interface 24-Sep-97 RMS Fixed bug in device name table (found by Charles Owen) */ #include "nova_defs.h" #include extern DEVICE cpu_dev; extern UNIT cpu_unit; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE plt_dev; extern DEVICE tti_dev; extern DEVICE tto_dev; extern DEVICE tti1_dev; extern DEVICE tto1_dev; extern DEVICE clk_dev; extern DEVICE lpt_dev; extern DEVICE dkp_dev; extern DEVICE dsk_dev; extern DEVICE mta_dev; extern DEVICE qty_dev; extern DEVICE alm_dev; extern REG cpu_reg[]; extern uint16 M[]; extern int32 saved_PC; extern int32 AMASK; #if defined (ECLIPSE) extern DEVICE map_dev; extern DEVICE fpu_dev; extern DEVICE pit_dev; extern int32 Usermap; extern int32 MapStat; #endif extern int32 sim_switches; /* SCP data structures sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax number of words needed for examine sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ #if defined (ECLIPSE) char sim_name[] = "ECLIPSE"; #else char sim_name[] = "NOVA"; #endif REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 4; DEVICE *sim_devices[] = { &cpu_dev, #if defined (ECLIPSE) &map_dev, &fpu_dev, &pit_dev, #endif &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, &tti1_dev, &tto1_dev, &clk_dev, &plt_dev, &lpt_dev, &dsk_dev, &dkp_dev, &mta_dev, &qty_dev, &alm_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unknown I/O instruction", "HALT instruction", "Breakpoint", "Nested indirect address limit exceeded", "Nested indirect interrupt or trap address limit exceeded", "Read breakpoint", "Write breakpoint" }; /* Binary loader Loader format consists of blocks, optionally preceded, separated, and followed by zeroes. Each block consists of: lo_count hi_count lo_origin hi_origin lo_checksum hi_checksum lo_data byte --- hi_data byte | : > -count words lo_data byte | hi_data byte --- If the word count is [0,-20], then the block is normal data. If the word count is [-21,-n], then the block is repeated data. If the word count is 1, the block is the start address. If the word count is >1, the block is an error block. Notes: 'start' block terminates loading. 'start' block starting address 1B0 = do not auto-start, 0B0 = auto-start. 'start' block starting address is saved in 'save_PC' so a "continue" should start the program. specify -i switch ignores checksum errors internal state machine: 0,1 get byte count (low and high), ignore leader bytes (<000>) 2,3 get origin 4,5 get checksum 6,7 process data block 8 process 'ignore' (error) block */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 data, csum, count, state, i; int32 origin; int pos ; int block_start ; int done ; if ((*cptr != 0) || (flag != 0)) return ( SCPE_ARG ) ; state = 0; block_start = -1 ; done = 0 ; for ( pos = 0 ; (! done) && ((i=getc(fileref)) != EOF) ; ++pos ) { i &= 0x00FF ; /* (insure no sign extension) */ switch (state) { case 0: /* leader */ count = i; state = (count != 0) ; if ( state ) block_start = pos ; break; case 1: /* high count */ csum = count = (i << 8) | count ; state = 2; break; case 2: /* low origin */ origin = i; state = 3; break ; case 3: /* high origin */ origin = (i << 8) | origin; csum = csum + origin; state = 4; break; case 4: /* low checksum */ csum = csum + i; state = 5; break; case 5: /* high checksum */ csum = (csum + (i << 8)) & 0xFFFF ; if (count == 1) { /* 'start' block */ /* do any auto-start check or inhibit check */ saved_PC = (origin & 077777) ; /* 0B0 = auto-start program */ /* 1B0 = do not auto start */ state = 0 ; /* indicate okay state */ done = 1 ; /* we're done! */ if ( ! (origin & 0x8000) ) { printf( "auto start @ %05o \n", (origin & 0x7FFF) ) ; } break ; } if ( ((count & 0x8000) == 0) && (count > 1)) { /* 'ignore' block */ state = 8 ; } /* 'data' or 'repeat' block */ count = 0200000 - count ; if ( count <= 020 ) { /* 'data' block */ state = 6 ; break ; } /* 'repeat' block (multiple data) */ if (count > 020) { /* large block */ for (count = count - 1; count > 1; count--) { if (origin >= AMASK /* MEMSIZE? */) { return ( SCPE_NXM ); } M[origin] = data; origin = origin + 1; } state = 0 ; } state = 0; break; case 6: /* low data */ data = i; state = 7; break; case 7: /* high data */ data = (i << 8) | data; csum = (csum + data) & 0xFFFF ; if (origin >= AMASK /* MEMSIZE? */) return SCPE_NXM; M[origin] = data; origin = origin + 1; count = count - 1; if (count == 0) { if ( csum ) { printf( "checksum error: block start at %d [0x%x] \n", block_start, block_start ) ; printf( "calculated: 0%o [0x%4x]\n", csum, csum ) ; if ( ! (sim_switches & SWMASK('I')) ) return SCPE_CSUM; } state = 0; break; } state = 6; break; case 8: /* error (ignore) block */ if (i == 0377) /* (wait for 'RUBOUT' char) */ state = 0; break; } /* end switch */ } /* end while */ /* Ok to find end of tape between blocks or in error state */ return ( ((state == 0) || (state == 8)) ? SCPE_OK : SCPE_FMT ) ; } /* Symbol tables */ #define I_V_FL 18 /* flag bits */ #define I_M_FL 037 /* flag width */ #define I_V_NPN 000 /* no operands */ #define I_V_R 001 /* reg */ #define I_V_D 002 /* device */ #define I_V_RD 003 /* reg,device */ #define I_V_M 004 /* mem addr */ #define I_V_RM 005 /* reg,mem addr */ #define I_V_RR 006 /* operate */ #define I_V_BY 007 /* Nova byte pointer */ #define I_V_2AC 010 /* reg,reg */ #define I_V_RSI 011 /* reg,short imm */ #define I_V_LI 012 /* long imm */ #define I_V_RLI 013 /* reg,long imm */ #define I_V_LM 014 /* long mem addr */ #define I_V_RLM 015 /* reg,long mem addr */ #define I_V_FRM 016 /* flt reg,long mem addr */ #define I_V_FST 017 /* flt long mem, status */ #define I_V_XP 020 /* XOP */ #define I_NPN (I_V_NPN << I_V_FL) #define I_R (I_V_R << I_V_FL) #define I_D (I_V_D << I_V_FL) #define I_RD (I_V_RD << I_V_FL) #define I_M (I_V_M << I_V_FL) #define I_RM (I_V_RM << I_V_FL) #define I_RR (I_V_RR << I_V_FL) #define I_BY (I_V_BY << I_V_FL) #define I_2AC (I_V_2AC << I_V_FL) #define I_RSI (I_V_RSI << I_V_FL) #define I_LI (I_V_LI << I_V_FL) #define I_RLI (I_V_RLI << I_V_FL) #define I_LM (I_V_LM << I_V_FL) #define I_RLM (I_V_RLM << I_V_FL) #define I_FRM (I_V_FRM << I_V_FL) #define I_FST (I_V_FST << I_V_FL) #define I_XP (I_V_XP << I_V_FL) static const int32 masks[] = { 0177777, 0163777, 0177700, 0163700, 0174000, 0160000, 0103770, 0163477, 0103777, 0103777, 0177777, 0163777, 0176377, 0162377, 0103777, 0163777, 0100077 }; static const char *opcode[] = { "JMP", "JSR", "ISZ", "DSZ", "LDA", "STA", #if defined (ECLIPSE) "ADI", "SBI", "DAD", "DSB", "IOR", "XOR", "ANC", "XCH", "SGT", "SGE", "LSH", "DLSH", "HXL", "HXR", "DHXL", "DHXR", "BTO", "BTZ", "SBZ", "SZBO", "LOB", "LRB", "COB", "LDB", "STB", "PSH", "POP", "LMP", "SYC", "PSHR", "POPB", "BAM", "POPJ", "RTN", "BLM", "DIVX", "MUL", "MULS", "DIV", "DIVS", "SAVE", "RSTR", "XOP", "FAS", "FAD", "FSS", "FSD", "FMS", "FMD", "FDS", "FDD", "FAMS", "FAMD", "FSMS", "FSMD", "FMMS", "FMMD", "FDMS", "FDMD", "FLDS", "FLDD", "FSTS", "FSTD", "FLAS", "FLMD", "FFAS", "FFMD", "FNOM", "FRH", "FAB", "FNEG", "FSCAL", "FEXP", "FINT", "FHLV", "FNS", "FSA", "FSEQ", "FSNE", "FSLT", "FSGE", "FSLE", "FSGT", "FSNM", "FSND", "FSNU", "FSNUD", "FSNO", "FSNOD", "FSNUO", "FSNER", "FSST", "FLST", "FTE", "FTD", "FCLE", "FPSH", "FPOP", "FCMP", "FMOV", "CMV", "CMP", "CTR", "CMT", "EJMP", "EJSR", "EISZ", "EDSZ", "ELDA", "ESTA", "ELEF", "ELDB", "ESTB", "DSPA", "PSHJ", "CLM", "SNB", "MSP", "XCT", "HLV", "IORI", "XORI", "ANDI", "ADDI", #endif "COM", "COMZ", "COMO", "COMC", "COML", "COMZL", "COMOL", "COMCL", "COMR", "COMZR", "COMOR", "COMCR", "COMS", "COMZS", "COMOS", "COMCS", "COM#", "COMZ#", "COMO#", "COMC#", "COML#", "COMZL#", "COMOL#", "COMCL#", "COMR#", "COMZR#", "COMOR#", "COMCR#", "COMS#", "COMZS#", "COMOS#", "COMCS#", "NEG", "NEGZ", "NEGO", "NEGC", "NEGL", "NEGZL", "NEGOL", "NEGCL", "NEGR", "NEGZR", "NEGOR", "NEGCR", "NEGS", "NEGZS", "NEGOS", "NEGCS", "NEG#", "NEGZ#", "NEGO#", "NEGC#", "NEGL#", "NEGZL#", "NEGOL#", "NEGCL#", "NEGR#", "NEGZR#", "NEGOR#", "NEGCR#", "NEGS#", "NEGZS#", "NEGOS#", "NEGCS#", "MOV", "MOVZ", "MOVO", "MOVC", "MOVL", "MOVZL", "MOVOL", "MOVCL", "MOVR", "MOVZR", "MOVOR", "MOVCR", "MOVS", "MOVZS", "MOVOS", "MOVCS", "MOV#", "MOVZ#", "MOVO#", "MOVC#", "MOVL#", "MOVZL#", "MOVOL#", "MOVCL#", "MOVR#", "MOVZR#", "MOVOR#", "MOVCR#", "MOVS#", "MOVZS#", "MOVOS#", "MOVCS#", "INC", "INCZ", "INCO", "INCC", "INCL", "INCZL", "INCOL", "INCCL", "INCR", "INCZR", "INCOR", "INCCR", "INCS", "INCZS", "INCOS", "INCCS", "INC#", "INCZ#", "INCO#", "INCC#", "INCL#", "INCZL#", "INCOL#", "INCCL#", "INCR#", "INCZR#", "INCOR#", "INCCR#", "INCS#", "INCZS#", "INCOS#", "INCCS#", "ADC", "ADCZ", "ADCO", "ADCC", "ADCL", "ADCZL", "ADCOL", "ADCCL", "ADCR", "ADCZR", "ADCOR", "ADCCR", "ADCS", "ADCZS", "ADCOS", "ADCCS", "ADC#", "ADCZ#", "ADCO#", "ADCC#", "ADCL#", "ADCZL#", "ADCOL#", "ADCCL#", "ADCR#", "ADCZR#", "ADCOR#", "ADCCR#", "ADCS#", "ADCZS#", "ADCOS#", "ADCCS#", "SUB", "SUBZ", "SUBO", "SUBC", "SUBL", "SUBZL", "SUBOL", "SUBCL", "SUBR", "SUBZR", "SUBOR", "SUBCR", "SUBS", "SUBZS", "SUBOS", "SUBCS", "SUB#", "SUBZ#", "SUBO#", "SUBC#", "SUBL#", "SUBZL#", "SUBOL#", "SUBCL#", "SUBR#", "SUBZR#", "SUBOR#", "SUBCR#", "SUBS#", "SUBZS#", "SUBOS#", "SUBCS#", "ADD", "ADDZ", "ADDO", "ADDC", "ADDL", "ADDZL", "ADDOL", "ADDCL", "ADDR", "ADDZR", "ADDOR", "ADDCR", "ADDS", "ADDZS", "ADDOS", "ADDCS", "ADD#", "ADDZ#", "ADDO#", "ADDC#", "ADDL#", "ADDZL#", "ADDOL#", "ADDCL#", "ADDR#", "ADDZR#", "ADDOR#", "ADDCR#", "ADDS#", "ADDZS#", "ADDOS#", "ADDCS#", "AND", "ANDZ", "ANDO", "ANDC", "ANDL", "ANDZL", "ANDOL", "ANDCL", "ANDR", "ANDZR", "ANDOR", "ANDCR", "ANDS", "ANDZS", "ANDOS", "ANDCS", "AND#", "ANDZ#", "ANDO#", "ANDC#", "ANDL#", "ANDZL#", "ANDOL#", "ANDCL#", "ANDR#", "ANDZR#", "ANDOR#", "ANDCR#", "ANDS#", "ANDZS#", "ANDOS#", "ANDCS#", "INTEN", "INTDS", "READS", "INTA", "MSKO", "IORST", "HALT", #if !defined (ECLIPSE) "MUL", "DIV", "MULS", "DIVS", "PSHA", "POPA", "SAV", "RET", "MTSP", "MTFP", "MFSP", "MFFP", "LDB", "STB", #endif "NIO", "NIOS", "NIOC", "NIOP", "DIA", "DIAS", "DIAC", "DIAP", "DOA", "DOAS", "DOAC", "DOAP", "DIB", "DIBS", "DIBC", "DIBP", "DOB", "DOBS", "DOBC", "DOBP", "DIC", "DICS", "DICC", "DICP", "DOC", "DOCS", "DOCC", "DOCP", "SKPBN", "SKPBZ", "SKPDN", "SKPDZ", #if defined (ECLIPSE) "LEF", "LEF", "LEF", "LEF", #endif NULL }; static const int32 opc_val[] = { 0000000+I_M, 0004000+I_M, 0010000+I_M, 0014000+I_M, 0020000+I_RM, 0040000+I_RM, #if defined (ECLIPSE) 0100010+I_RSI, 0100110+I_RSI, 0100210+I_2AC, 0100310+I_2AC, 0100410+I_2AC, 0100510+I_2AC, 0100610+I_2AC, 0100710+I_2AC, 0101010+I_2AC, 0101110+I_2AC, 0101210+I_RSI, 0101310+I_RSI, 0101410+I_RSI, 0101510+I_RSI, 0101610+I_RSI, 0101710+I_RSI, 0102010+I_2AC, 0102110+I_2AC, 0102210+I_2AC, 0102310+I_2AC, 0102410+I_2AC, 0102510+I_2AC, 0102610+I_2AC, 0102710+I_2AC, 0103010+I_2AC, 0103110+I_2AC, 0103210+I_2AC, 0113410+I_NPN, 0103510+I_2AC, 0103710+I_NPN, 0107710+I_NPN, 0113710+I_NPN, 0117710+I_NPN, 0127710+I_NPN, 0133710+I_NPN, 0137710+I_NPN, 0143710+I_NPN, 0147710+I_NPN, 0153710+I_NPN, 0157710+I_NPN, 0163710+I_LI, 0167710+I_NPN, 0100030+I_XP, 0100050+I_2AC, 0100150+I_2AC, 0100250+I_2AC, 0100350+I_2AC, 0100450+I_2AC, 0100550+I_2AC, 0100650+I_2AC, 0100750+I_2AC, 0101050+I_FRM, 0101150+I_FRM, 0101250+I_FRM, 0101350+I_FRM, 0101450+I_FRM, 0101550+I_FRM, 0101650+I_FRM, 0101750+I_FRM, 0102050+I_FRM, 0102150+I_FRM, 0102250+I_FRM, 0102350+I_FRM, 0102450+I_2AC, 0102550+I_FRM, 0102650+I_2AC, 0102750+I_FRM, 0103050+I_R, 0123050+I_R, 0143050+I_R, 0163050+I_R, 0103150+I_R, 0123150+I_R, 0143150+I_R, 0163150+I_R, 0103250+I_NPN, 0107250+I_NPN, 0113250+I_NPN, 0117250+I_NPN, 0123250+I_NPN, 0127250+I_NPN, 0133250+I_NPN, 0137250+I_NPN, 0143250+I_NPN, 0147250+I_NPN, 0153250+I_NPN, 0157250+I_NPN, 0163250+I_NPN, 0167250+I_NPN, 0173250+I_NPN, 0177250+I_NPN, 0103350+I_FST, 0123350+I_FST, 0143350+I_NPN, 0147350+I_NPN, 0153350+I_NPN, 0163350+I_NPN, 0167350+I_NPN, 0103450+I_2AC, 0103550+I_2AC, 0153650+I_NPN, 0157650+I_NPN, 0163650+I_NPN, 0167650+I_NPN, 0102070+I_LM, 0106070+I_LM, 0112070+I_LM, 0116070+I_LM, 0122070+I_RLM, 0142070+I_RLM, 0162070+I_RLM, 0102170+I_RLM, 0122170+I_RLM, 0142170+I_RLM, 0102270+I_LM, 0102370+I_2AC, 0102770+I_2AC, 0103370+I_R, 0123370+I_R, 0143370+I_R, 0103770+I_RLI, 0123770+I_RLI, 0143770+I_RLI, 0163770+I_RLI, #endif 0100000+I_RR, 0100020+I_RR, 0100040+I_RR, 0100060+I_RR, 0100100+I_RR, 0100120+I_RR, 0100140+I_RR, 0100160+I_RR, 0100200+I_RR, 0100220+I_RR, 0100240+I_RR, 0100260+I_RR, 0100300+I_RR, 0100320+I_RR, 0100340+I_RR, 0100360+I_RR, 0100010+I_RR, 0100030+I_RR, 0100050+I_RR, 0100070+I_RR, 0100110+I_RR, 0100130+I_RR, 0100150+I_RR, 0100170+I_RR, 0100210+I_RR, 0100230+I_RR, 0100250+I_RR, 0100270+I_RR, 0100310+I_RR, 0100330+I_RR, 0100350+I_RR, 0100370+I_RR, 0100400+I_RR, 0100420+I_RR, 0100440+I_RR, 0100460+I_RR, 0100500+I_RR, 0100520+I_RR, 0100540+I_RR, 0100560+I_RR, 0100600+I_RR, 0100620+I_RR, 0100640+I_RR, 0100660+I_RR, 0100700+I_RR, 0100720+I_RR, 0100740+I_RR, 0100760+I_RR, 0100410+I_RR, 0100430+I_RR, 0100450+I_RR, 0100470+I_RR, 0100510+I_RR, 0100530+I_RR, 0100550+I_RR, 0100570+I_RR, 0100610+I_RR, 0100630+I_RR, 0100650+I_RR, 0100670+I_RR, 0100710+I_RR, 0100730+I_RR, 0100750+I_RR, 0100770+I_RR, 0101000+I_RR, 0101020+I_RR, 0101040+I_RR, 0101060+I_RR, 0101100+I_RR, 0101120+I_RR, 0101140+I_RR, 0101160+I_RR, 0101200+I_RR, 0101220+I_RR, 0101240+I_RR, 0101260+I_RR, 0101300+I_RR, 0101320+I_RR, 0101340+I_RR, 0101360+I_RR, 0101010+I_RR, 0101030+I_RR, 0101050+I_RR, 0101070+I_RR, 0101110+I_RR, 0101130+I_RR, 0101150+I_RR, 0101170+I_RR, 0101210+I_RR, 0101230+I_RR, 0101250+I_RR, 0101270+I_RR, 0101310+I_RR, 0101330+I_RR, 0101350+I_RR, 0101370+I_RR, 0101400+I_RR, 0101420+I_RR, 0101440+I_RR, 0101460+I_RR, 0101500+I_RR, 0101520+I_RR, 0101540+I_RR, 0101560+I_RR, 0101600+I_RR, 0101620+I_RR, 0101640+I_RR, 0101660+I_RR, 0101700+I_RR, 0101720+I_RR, 0101740+I_RR, 0101760+I_RR, 0101410+I_RR, 0101430+I_RR, 0101450+I_RR, 0101470+I_RR, 0101510+I_RR, 0101530+I_RR, 0101550+I_RR, 0101570+I_RR, 0101610+I_RR, 0101630+I_RR, 0101650+I_RR, 0101670+I_RR, 0101710+I_RR, 0101730+I_RR, 0101750+I_RR, 0101770+I_RR, 0102000+I_RR, 0102020+I_RR, 0102040+I_RR, 0102060+I_RR, 0102100+I_RR, 0102120+I_RR, 0102140+I_RR, 0102160+I_RR, 0102200+I_RR, 0102220+I_RR, 0102240+I_RR, 0102260+I_RR, 0102300+I_RR, 0102320+I_RR, 0102340+I_RR, 0102360+I_RR, 0102010+I_RR, 0102030+I_RR, 0102050+I_RR, 0102070+I_RR, 0102110+I_RR, 0102130+I_RR, 0102150+I_RR, 0102170+I_RR, 0102210+I_RR, 0102230+I_RR, 0102250+I_RR, 0102270+I_RR, 0102310+I_RR, 0102330+I_RR, 0102350+I_RR, 0102370+I_RR, 0102400+I_RR, 0102420+I_RR, 0102440+I_RR, 0102460+I_RR, 0102500+I_RR, 0102520+I_RR, 0102540+I_RR, 0102560+I_RR, 0102600+I_RR, 0102620+I_RR, 0102640+I_RR, 0102660+I_RR, 0102700+I_RR, 0102720+I_RR, 0102740+I_RR, 0102760+I_RR, 0102410+I_RR, 0102430+I_RR, 0102450+I_RR, 0102470+I_RR, 0102510+I_RR, 0102530+I_RR, 0102550+I_RR, 0102570+I_RR, 0102610+I_RR, 0102630+I_RR, 0102650+I_RR, 0102670+I_RR, 0102710+I_RR, 0102730+I_RR, 0102750+I_RR, 0102770+I_RR, 0103000+I_RR, 0103020+I_RR, 0103040+I_RR, 0103060+I_RR, 0103100+I_RR, 0103120+I_RR, 0103140+I_RR, 0103160+I_RR, 0103200+I_RR, 0103220+I_RR, 0103240+I_RR, 0103260+I_RR, 0103300+I_RR, 0103320+I_RR, 0103340+I_RR, 0103360+I_RR, 0103010+I_RR, 0103030+I_RR, 0103050+I_RR, 0103070+I_RR, 0103110+I_RR, 0103130+I_RR, 0103150+I_RR, 0103170+I_RR, 0103210+I_RR, 0103230+I_RR, 0103250+I_RR, 0103270+I_RR, 0103310+I_RR, 0103330+I_RR, 0103350+I_RR, 0103370+I_RR, 0103400+I_RR, 0103420+I_RR, 0103440+I_RR, 0103460+I_RR, 0103500+I_RR, 0103520+I_RR, 0103540+I_RR, 0103560+I_RR, 0103600+I_RR, 0103620+I_RR, 0103640+I_RR, 0103660+I_RR, 0103700+I_RR, 0103720+I_RR, 0103740+I_RR, 0103760+I_RR, 0103410+I_RR, 0103430+I_RR, 0103450+I_RR, 0103470+I_RR, 0103510+I_RR, 0103530+I_RR, 0103550+I_RR, 0103570+I_RR, 0103610+I_RR, 0103630+I_RR, 0103650+I_RR, 0103670+I_RR, 0103710+I_RR, 0103730+I_RR, 0103750+I_RR, 0103770+I_RR, 0060177+I_NPN, 0060277+I_NPN, 0060477+I_R, 0061477+I_R, 0062077+I_R, 0062677+I_NPN, 0063077+I_NPN, #if !defined (ECLIPSE) 0073301+I_NPN, 0073101+I_NPN, 0077201+I_NPN, 0077001+I_NPN, 0061401+I_R, 0061601+I_R, 0062401+I_NPN, 0062601+I_NPN, 0061001+I_R, 0060001+I_R, 0061201+I_R, 0060201+I_R, 0060401+I_BY, 0062001+I_BY, #endif 0060000+I_RD, 0060100+I_RD, 0060200+I_RD, 0060300+I_RD, 0060400+I_RD, 0060500+I_RD, 0060600+I_RD, 0060700+I_RD, 0061000+I_RD, 0061100+I_RD, 0061200+I_RD, 0061300+I_RD, 0061400+I_RD, 0061500+I_RD, 0061600+I_RD, 0061700+I_RD, 0062000+I_RD, 0062100+I_RD, 0062200+I_RD, 0062300+I_RD, 0062400+I_RD, 0062500+I_RD, 0062600+I_RD, 0062700+I_RD, 0063000+I_RD, 0063100+I_RD, 0063200+I_RD, 0063300+I_RD, 0063400+I_D, 0063500+I_D, 0063600+I_D, 0063700+I_D, #if defined (ECLIPSE) 0064000+I_D, 0070000+I_D, 0074000+I_D, 0076000+I_D, #endif -1 }; static const char *skip[] = { "SKP", "SZC", "SNC", "SZR", "SNR", "SEZ", "SBN", NULL }; static const char *device[] = { #if defined (ECLIPSE) "ERCC", "MAP", #endif "TTI", "TTO", "PTR", "PTP", "RTC", "PLT", "CDR", "LPT", "DSK", "MTA", "DCM", "QTY" /* "ADCV" */, "DKP", "CAS", "TTI1", "TTO1", "CPU", NULL }; static const int32 dev_val[] = { #if defined (ECLIPSE) 002, 003, #endif 010, 011, 012, 013, 014, 015, 016, 017, 020, 022, 024, 030, 033, 034, 050, 051, 077, -1 }; /* Address decode Inputs: *of = output stream addr = current PC ind = indirect flag mode = addressing mode disp = displacement ext = true if extended address cflag = true if decoding for CPU Outputs: return = error code */ t_stat fprint_addr (FILE *of, t_addr addr, int32 ind, int32 mode, int32 disp, t_bool ext, int32 cflag) { int32 dsign, dmax; if (ext) /* get max disp */ dmax = AMASK + 1; else dmax = I_M_DISP + 1; dsign = dmax >> 1; /* get disp sign */ if (ind) /* indirect? */ fprintf (of, "@"); switch (mode & 03) { /* mode */ case 0: /* absolute */ fprintf (of, "%-o", disp); break; case 1: /* PC rel */ if (disp & dsign) { if (cflag) fprintf (of, "%-o", (addr - (dmax - disp)) & AMASK); else fprintf (of, ".-%-o", dmax - disp); } else { if (cflag) fprintf (of, "%-o", (addr + disp) & AMASK); else fprintf (of, ".+%-o", disp); } break; case 2: /* AC2 rel */ if (disp & dsign) fprintf (of, "-%-o,2", dmax - disp); else fprintf (of, "%-o,2", disp); break; case 3: /* AC3 rel */ if (disp & dsign) fprintf (of, "-%-o,3", dmax - disp); else fprintf (of, "%-o,3", disp); break; } /* end switch */ return SCPE_OK; } /* Symbolic output Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: status = error code */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, c1, c2, inst, inst1, dv, src, dst, skp; int32 ind, mode, disp, dev; int32 byac, extind, extdisp, xop; cflag = (uptr == NULL) || (uptr == &cpu_unit); c1 = ((int32) val[0] >> 8) & 0177; c2 = (int32) val[0] & 0177; if (sw & SWMASK ('A')) { /* ASCII? */ fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (sw & SWMASK ('C')) { /* character? */ fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (!(sw & SWMASK ('M'))) /* mnemonic? */ return SCPE_ARG; /* Instruction decode */ inst = (int32) val[0]; inst1 = (int32) val[1]; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ src = I_GETSRC (inst); /* opr fields */ dst = I_GETDST (inst); skp = I_GETSKP (inst); ind = inst & I_IND; /* mem ref fields */ mode = I_GETMODE (inst); disp = I_GETDISP (inst); dev = I_GETDEV (inst); /* IOT fields */ byac = I_GETPULSE (inst); /* byte fields */ xop = I_GETXOP (inst); /* XOP fields */ extind = inst1 & A_IND; /* extended fields */ extdisp = inst1 & AMASK; for (dv = 0; (dev_val[dv] >= 0) && (dev_val[dv] != dev); dv++) ; switch (j) { /* switch on class */ case I_V_NPN: /* no operands */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_R: /* reg only */ fprintf (of, "%s %-o", opcode[i], dst); break; case I_V_D: /* dev only */ #if defined (ECLIPSE) if (Usermap && (MapStat & 0100)) { /* the evil LEF mode */ fprintf (of, "LEF %-o,", dst); fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); break; } #endif if (dev_val[dv] >= 0) fprintf (of, "%s %s", opcode[i], device[dv]); else fprintf (of, "%s %-o", opcode[i], dev); break; case I_V_RD: /* reg, dev */ if (dev_val[dv] >= 0) fprintf (of, "%s %-o,%s", opcode[i], dst, device[dv]); else fprintf (of, "%s %-o,%-o", opcode[i], dst, dev); break; case I_V_M: /* addr only */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); break; case I_V_RM: /* reg, addr */ fprintf (of, "%s %-o,", opcode[i], dst); fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); break; case I_V_RR: /* operate */ fprintf (of, "%s %-o,%-o", opcode[i], src, dst); if (skp) fprintf (of, ",%s", skip[skp-1]); break; case I_V_BY: /* byte */ fprintf (of, "%s %-o,%-o", opcode[i], byac, dst); break; case I_V_2AC: /* reg, reg */ fprintf (of, "%s %-o,%-o", opcode[i], src, dst); break; case I_V_RSI: /* reg, short imm */ fprintf (of, "%s %-o,%-o", opcode[i], src + 1, dst); break; case I_V_LI: /* long imm */ fprintf (of, "%s %-o", opcode[i], inst1); return -1; case I_V_RLI: /* reg, long imm */ fprintf (of, "%s %-o,%-o", opcode[i], inst1, dst); return -1; case I_V_LM: /* long addr */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag); return -1; case I_V_RLM: /* reg, long addr */ fprintf (of, "%s %-o,", opcode[i], dst); fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag); return -1; case I_V_FRM: /* flt reg, long addr */ fprintf (of, "%s %-o,", opcode[i], dst); fprint_addr (of, addr, extind, src, extdisp, TRUE, cflag); return -1; case I_V_FST: /* flt status */ fprintf (of, "%s ", opcode[i]); fprint_addr (of, addr, extind, dst, extdisp, AMASK + 1, cflag); return -1; case I_V_XP: /* XOP */ fprintf (of, "%s %-o,%-o,%-o", opcode[i], src, dst, xop); break; /* end case */ default: fprintf (of, "??? [%-o]", inst); break; } return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* Address parse Inputs: *cptr = pointer to input string addr = current PC ext = extended address cflag = true if parsing for CPU val[3] = output array Outputs: optr = pointer to next char in input string NULL if error */ #define A_FL 001 /* CPU flag */ #define A_NX 002 /* index seen */ #define A_PER 004 /* period seen */ #define A_NUM 010 /* number seen */ #define A_SI 020 /* sign seen */ #define A_MI 040 /* - seen */ char *get_addr (char *cptr, t_addr addr, t_bool ext, int32 cflag, int32 *val) { int32 d, r, x, pflag; char gbuf[CBUFSIZE]; int32 dmax, dsign; if (ext) /* get max disp */ dmax = AMASK + 1; else dmax = I_M_DISP + 1; dsign = dmax >> 1; /* get disp sign */ val[0] = 0; /* no indirect */ val[1] = 0; /* PC rel */ val[2] = 0; /* no addr */ pflag = cflag & A_FL; /* isolate flag */ if (*cptr == '@') { /* indirect? */ val[0] = 1; cptr++; } if (*cptr == '.') { /* relative? */ pflag = pflag | A_PER; x = 1; /* "index" is PC */ cptr++; } if (*cptr == '+') { /* + sign? */ pflag = pflag | A_SI; cptr++; } else if (*cptr == '-') { /* - sign? */ pflag = pflag | A_MI | A_SI; cptr++; } if (*cptr != 0) { /* number? */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ d = (int32) get_uint (gbuf, 8, AMASK, &r); if (r != SCPE_OK) return NULL; pflag = pflag | A_NUM; } if (*cptr != 0) { /* index? */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ x = (int32) get_uint (gbuf, 8, I_M_DST, &r); if ((r != SCPE_OK) || (x < 2)) return NULL; pflag = pflag | A_NX; } switch (pflag) { /* case on flags */ case A_NUM: case A_NUM+A_SI: /* ~CPU, (+)num */ if (d < dmax) val[2] = d; else return NULL; break; case A_NUM+A_FL: case A_NUM+A_SI+A_FL: /* CPU, (+)num */ if (d < dmax) val[2] = d; else if (((d >= (((int32) addr - dsign) & AMASK)) && (d < (((int32) addr + dsign) & AMASK))) || (d >= ((int32) addr + (-dsign & AMASK)))) { val[1] = 1; /* PC rel */ val[2] = (d - addr) & (dmax - 1); } else return NULL; break; case A_PER: case A_PER+A_FL: /* . */ case A_PER+A_SI+A_NUM: case A_PER+A_SI+A_NUM+A_FL: /* . + num */ case A_PER+A_SI+A_MI+A_NUM: /* . - num */ case A_PER+A_SI+A_MI+A_NUM+A_FL: case A_NX+A_NUM: case A_NX+A_NUM+A_FL: /* num, ndx */ case A_NX+A_SI+A_NUM: case A_NX+A_SI+A_NUM+A_FL: /* +num, ndx */ case A_NX+A_SI+A_MI+A_NUM: /* -num, ndx */ case A_NX+A_SI+A_MI+A_NUM+A_FL: val[1] = x; /* set mode */ if (((pflag & A_MI) == 0) && (d < dsign)) val[2] = d; else if ((pflag & A_MI) && (d <= dsign)) val[2] = (dmax - d); else return NULL; break; default: return NULL; } /* end case */ return cptr; } /* Parse two registers Inputs: *cptr = input string term = second terminating character val = output array Outputs: optr = pointer to next char in input string NULL if error */ char *get_2reg (char *cptr, char term, int32 *val) { char gbuf[CBUFSIZE]; t_stat r; cptr = get_glyph (cptr, gbuf, ','); /* get register */ val[0] = (int32) get_uint (gbuf, 8, I_M_SRC, &r); if (r != SCPE_OK) return NULL; cptr = get_glyph (cptr, gbuf, term); /* get register */ val[1] = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return NULL; return cptr; } /* 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) { int32 cflag, d, i, j, amd[3]; t_stat r, rtn; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1]; return SCPE_OK; } /* Instruction parse */ rtn = SCPE_OK; /* assume 1 word */ 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] & 0177777; /* get value */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ switch (j) { /* case on class */ case I_V_NPN: /* no operand */ break; case I_V_R: /* IOT reg */ cptr = get_glyph (cptr, gbuf, 0); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ break; case I_V_RD: /* IOT reg,dev */ cptr = get_glyph (cptr, gbuf, ','); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ case I_V_D: /* IOT dev */ cptr = get_glyph (cptr, gbuf, 0); /* get device */ for (i = 0; (device[i] != NULL) && (strcmp (device[i], gbuf) != 0); i++); if (device[i] != NULL) val[0] = val[0] | dev_val[i]; else { d = (int32) get_uint (gbuf, 8, I_M_DEV, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DEV); } break; case I_V_RM: /* reg, addr */ cptr = get_glyph (cptr, gbuf, ','); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ case I_V_M: /* addr */ cptr = get_addr (cptr, addr, FALSE, cflag, amd); if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[0] << I_V_IND) | (amd[1] << I_V_MODE) | amd[2]; break; case I_V_RR: /* operate */ cptr = get_2reg (cptr, ',', amd); /* get 2 reg */ if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); if (*cptr != 0) { /* skip? */ cptr = get_glyph (cptr, gbuf, 0); /* get skip */ for (i = 0; (skip[i] != NULL) && (strcmp (skip[i], gbuf) != 0); i++) ; if (skip[i] == NULL) return SCPE_ARG; val[0] = val[0] | (i + 1); } /* end if */ break; case I_V_BY: /* byte */ cptr = get_2reg (cptr, 0, amd); /* get 2 reg */ if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[0] << I_V_PULSE) | (amd[1] << I_V_DST); break; case I_V_2AC: /* reg, reg */ cptr = get_2reg (cptr, 0, amd); /* get 2 reg */ if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); break; case I_V_RSI: /* reg, short imm */ cptr = get_glyph (cptr, gbuf, ','); /* get immediate */ d = (int32) get_uint (gbuf, 8, I_M_SRC + 1, &r); if ((d == 0) || (r != SCPE_OK)) return SCPE_ARG; val[0] = val[0] | ((d - 1) << I_V_SRC); /* put in place */ cptr = get_glyph (cptr, gbuf, 0); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ break; case I_V_RLI: /* reg, long imm */ cptr = get_glyph (cptr, gbuf, ','); /* get immediate */ val[1] = (int32) get_uint (gbuf, 8, DMASK, &r); if (r != SCPE_OK) return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ rtn = -1; break; case I_V_LI: /* long imm */ cptr = get_glyph (cptr, gbuf, 0); /* get immediate */ val[1] = (int32) get_uint (gbuf, 8, DMASK, &r); if (r != SCPE_OK) return SCPE_ARG; rtn = -1; break; case I_V_RLM: /* reg, long mem */ cptr = get_glyph (cptr, gbuf, ','); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ case I_V_LM: /* long mem */ cptr = get_addr (cptr, addr, TRUE, cflag, amd); if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[1] << I_V_MODE); val[1] = (amd[0] << A_V_IND) | amd[2]; rtn = -1; break; case I_V_FRM: /* flt reg, long mem */ cptr = get_glyph (cptr, gbuf, ','); /* get register */ d = (int32) get_uint (gbuf, 8, I_M_DST, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_DST); /* put in place */ cptr = get_addr (cptr, addr, TRUE, cflag, amd); if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[1] << I_V_SRC); val[1] = (amd[0] << A_V_IND) | amd[2]; rtn = -1; break; case I_V_FST: /* flt status */ cptr = get_addr (cptr, addr, TRUE, cflag, amd); if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[1] << I_V_DST); val[1] = (amd[0] << A_V_IND) | amd[2]; rtn = -1; break; case I_V_XP: /* XOP */ cptr = get_2reg (cptr, ',', amd); /* get 2 reg */ if (cptr == NULL) return SCPE_ARG; val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); cptr = get_glyph (cptr, gbuf, 0); /* get argument */ d = (int32) get_uint (gbuf, 8, I_M_XOP, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << I_V_XOP); break; } /* end case */ if (*cptr != 0) /* any leftovers? */ return SCPE_ARG; return rtn; } simh-3.8.1/NOVA/eclipse_tt.c0000644000175000017500000003627310303752444013720 0ustar vlmvlm/* eclipse_tt.c: Eclipse console terminal simulator Copyright (c) 1998-2005, Charles E Owen Portions copyright (c) 1993-2002, Robert M Supnik Written by Charles Owen, used by gracious permission Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of 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 author. tti terminal input tto terminal output 25-Apr-03 RMS Revised for extended file support 03-Oct-02 RMS Added DIBs 30-May-02 RMS Widened POS to 32b 28-Jan-02 RMS Cleaned up compiler warnings */ #include "nova_defs.h" #define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ #define UNIT_DASHER (1 << UNIT_V_DASHER) extern int32 int_req, dev_busy, dev_done, dev_disable; int32 tti (int32 pulse, int32 code, int32 AC); int32 tto (int32 pulse, int32 code, int32 AC); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat ttx_setmod (UNIT *uptr, int32 value, char *cptr, void *desc); void translate_in(); int32 translate_out(int32 c); int32 putseq(char *seq); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list ttx_mod TTI/TTO modifiers list */ DIB tti_dib = { DEV_TTI, INT_TTI, PI_TTI, &tti }; UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTI) }, { FLDATA (DONE, dev_done, INT_V_TTI) }, { FLDATA (DISABLE, dev_disable, INT_V_TTI) }, { FLDATA (INT, int_req, INT_V_TTI) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB ttx_mod[] = { { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod }, { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, ttx_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { DEV_TTO, INT_TTO, PI_TTO, &tto }; UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_TTO) }, { FLDATA (DONE, dev_done, INT_V_TTO) }, { FLDATA (DISABLE, dev_disable, INT_V_TTO) }, { FLDATA (INT, int_req, INT_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, ttx_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* Terminal input: IOT routine */ int32 tti (int32 pulse, int32 code, int32 AC) { int32 iodata; iodata = (code == ioDIA)? tti_unit.buf & 0377: 0; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ dev_busy = dev_busy | INT_TTI; /* set busy */ dev_done = dev_done & ~INT_TTI; /* clear done, int */ int_req = int_req & ~INT_TTI; break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_TTI; /* clear busy */ dev_done = dev_done & ~INT_TTI; /* clear done, int */ int_req = int_req & ~INT_TTI; break; } /* end switch */ return iodata; } /* Unit service */ t_stat tti_svc (UNIT *uptr) { int32 temp; sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ tti_unit.buf = temp & 0177; /* --- BEGIN MODIFIED CODE --- */ if (tti_unit.flags & UNIT_DASHER) /* translate input */ translate_in(); /* --- END MODIFIED CODE --- */ dev_busy = dev_busy & ~INT_TTI; /* clear busy */ dev_done = dev_done | INT_TTI; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); tti_unit.pos = tti_unit.pos + 1; return SCPE_OK; } /* -------------------- BEGIN INSERTION -----------------------*/ int curpos = 0; /* used by translate_out() */ int row = 0, col = 0; /* ditto - for cursor positioning */ int spec200 = 0; /* signals next char is 'special' */ /* Translation: VT100 input to D200 keycodes. */ void translate_in() { char rev = 0; if (tti_unit.buf == '\r') rev = '\n'; if (tti_unit.buf == '\n') rev = '\r'; if (rev) tti_unit.buf = rev; } /* -------------------- END INSERTION -----------------------*/ /* Reset routine */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; dev_busy = dev_busy & ~INT_TTI; /* clear busy */ dev_done = dev_done & ~INT_TTI; /* clear done, int */ int_req = int_req & ~INT_TTI; sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto (int32 pulse, int32 code, int32 AC) { if (code == ioDOA) tto_unit.buf = AC & 0377; switch (pulse) { /* decode IR<8:9> */ case iopS: /* start */ dev_busy = dev_busy | INT_TTO; /* set busy */ dev_done = dev_done & ~INT_TTO; /* clear done, int */ int_req = int_req & ~INT_TTO; sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_TTO; /* clear busy */ dev_done = dev_done & ~INT_TTO; /* clear done, int */ int_req = int_req & ~INT_TTO; sim_cancel (&tto_unit); /* deactivate unit */ break; } /* end switch */ return 0; } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 c, temp; dev_busy = dev_busy & ~INT_TTO; /* clear busy */ dev_done = dev_done | INT_TTO; /* set done */ int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); c = tto_unit.buf & 0177; /* --- BEGIN MODIFIED CODE --- */ if (tto_unit.flags & UNIT_DASHER) { if ((temp = translate_out(c)) != SCPE_OK) return temp; } else { if ((temp = sim_putchar (c)) != SCPE_OK) return temp; tto_unit.pos = tto_unit.pos + 1; } /* --- END MODIFIED CODE --- */ return SCPE_OK; } /* -------------------- BEGIN INSERTION -----------------------*/ /* Translation routine - D200 screen controls to VT-100 controls. */ int32 translate_out(int32 c) { int32 temp; char outstr[32]; if (spec200 == 1) { /* Special terminal control seq */ spec200 = 0; switch (c) { case 'C': /* read model ID */ return SCPE_OK; case 'E': /* Reverse video off */ return SCPE_OK; case 'D': /* Reverse video on */ return SCPE_OK; default: return SCPE_OK; } } if (curpos == 1) { /* 2nd char of cursor position */ col = c & 0x7f; curpos++; return (SCPE_OK); } if (curpos == 2) { /* 3rd char of cursor position */ row = c & 0x7f; curpos = 0; sprintf(outstr, "\033[%d;%dH", row+1, col+1); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); } switch (c) { /* Single-char command or data */ case 003: /* Blink enable */ break; case 004: /* Blink disable */ break; case 005: /* Read cursor address */ break; case 010: /* Cursor home */ sprintf(outstr, "\033[1;1H"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; row = col = 0; return (SCPE_OK); case 012: /* Newline */ if ((temp = sim_putchar('\r')) != SCPE_OK) return temp; tto_unit.pos += 1; if ((temp = sim_putchar(c)) != SCPE_OK) return temp; tto_unit.pos += 1; col = 1; row++; if (row > 24) row = 1; return (SCPE_OK); case 013: /* Erase EOL */ sprintf(outstr, "\033[K"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 014: /* Erase screen */ sprintf(outstr, "\033[1;1H\033[2J"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; row = col = 0; return (SCPE_OK); case 015: /* CR */ if ((temp = sim_putchar(c)) != SCPE_OK) return temp; tto_unit.pos += 1; col = 1; return (SCPE_OK); case 016: /* Blink On */ sprintf(outstr, "\033[5m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 017: /* Blink off */ sprintf(outstr, "\033[25m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 020: /* Write cursor address */ curpos = 1; return SCPE_OK; case 024: /* underscore on */ sprintf(outstr, "\033[4m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 025: /* underscore off */ sprintf(outstr, "\033[24m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); break; case 027: /* cursor up */ sprintf(outstr, "\033[A"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; row--; if (row < 1) row = 24; return (SCPE_OK); case 030: /* cursor right */ sprintf(outstr, "\033[C"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; col++; if (col > 80) { col = 1; row++; if (row > 24) row = 1; } return (SCPE_OK); case 031: /* Cursor left */ sprintf(outstr, "\033[D"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; tto_unit.pos += 1; col--; if (col < 1) { col = 80; row--; if (row < 1) row = 24; } return (SCPE_OK); case 032: /* Cursor down */ sprintf(outstr, "\033[B"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; row++; if (row > 24) row = 1; return (SCPE_OK); case 034: /* Dim on */ sprintf(outstr, "\033[22m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 035: /* Dim off */ sprintf(outstr, "\033[1m"); if ((temp = putseq(outstr)) != SCPE_OK) return temp; return (SCPE_OK); case 036: /* Special sequence */ spec200 = 1; return SCPE_OK; default: /* ..A character of data */ if ((temp = sim_putchar(c)) != SCPE_OK) return temp; tto_unit.pos += 1; col++; if (col > 80) { col = 1; row++; if (row > 24) row = 24; } return (SCPE_OK); } return SCPE_OK; } int32 putseq(char *seq) { int i, len, temp; len = strlen(seq); for (i = 0; i < len; i++) { if ((temp = sim_putchar(seq[i])) != SCPE_OK) return temp; tto_unit.pos += 1; } return SCPE_OK; } /* -------------------- END INSERTION -----------------------*/ /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; dev_busy = dev_busy & ~INT_TTO; /* clear busy */ dev_done = dev_done & ~INT_TTO; /* clear done, int */ int_req = int_req & ~INT_TTO; sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat ttx_setmod (UNIT *uptr, int32 value, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | value; tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | value; return SCPE_OK; } simh-3.8.1/NOVA/nova_qty.c0000644000175000017500000011452211111105066013405 0ustar vlmvlm/* nova_qty.c: NOVA multiplexor (QTY/ALM) simulator Copyright (c) 2000-2008, Robert M. Supnik Written by Bruce Ray and used with his gracious permission. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. qty multiplexor: QTY = 4060, ALM = 42xx 04-Jul-07 BKR fixed QTY output line number calculation (affected higher line numbers), 25-Mar-04 RMS Updated for V3.2 12-Jan-04 BKR Initial release includes both original DG "quad" multiplexor (QTY) and later Asynchronous Line Multiplexor (ALM) support. */ /*----------------------------------------------------------------------*/ /* QTY [4060-compatible] multiplexor */ /*----------------------------------------------------------------------*/ /* * Emulate the DG 4060 "quad" (QTY) serial port multiplexor. DG modem * control is not supported in this revision due to its obtuse nature * of using a separate [semi-secret] device MDM which is actually part * of the DG 4026/4027 multiplexor hardware(!). * (Full modem support is provided in the ALM driver.) * * * 4060 Hardware * * device code: 030 [primary], * 070 [secondary] * interrupt mask: B14 [000002] * ASM mnemonic: QTY * * * 4060 Input/Output Word Format: * * _________________________________________________________________ * | RI| TI| channel | character | * ----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 * * * RI - receiver interrupt * TI - transmitter interrupt * channel - channel number, 0 - 63. * character- character (valid if receiver interrupt, undefined if transmitter) * * Notes: * * Maximum 64 lines supported. * DONE set whenever any received character fully assembled and ready, * or when any output character transmitted and line is ready * to accept next output character. * BUSY set whenever output character is being sent on any line. * Note that early 4060s did NOT have a busy flag! * IORST clears device Done, no other user instruction does. * IORST clears each line's individual R.I. and T.I. * * * Instructions: * * DIA get multiplexor status word [format defined above] * DOA send character to QTY line [format defined above, RI & SI ] * DIB [returns backplane bus noise] * DOB clear QTY line * DIC [returns backplace bus noise] * DOC * 'C' clears global done, then checks for RI and TI; * 'P' * 'S' */ #include "nova_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ #define UNIT_8B (1 << UNIT_V_8B) extern int32 int_req, dev_busy, dev_done, dev_disable ; extern int32 sim_switches ; extern FILE * sim_log ; extern int32 tmxr_poll ; /* calibrated delay */ t_stat qty_setnl ( UNIT * uptr, int32 val, char * cptr, void * desc ) ; t_stat qty_attach ( UNIT * uptr, char * cptr ) ; t_stat qty_detach ( UNIT * uptr ) ; t_stat qty_reset ( DEVICE * dptr ) ; t_stat qty_svc ( UNIT * uptr ) ; int32 qty ( int32 pulse, int32 code, int32 AC ) ; t_stat alm_reset ( DEVICE * dptr ) ; t_stat alm_svc ( UNIT * uptr ) ; int32 alm ( int32 pulse, int32 code, int32 AC ) ; DEVICE alm_dev ; #define QTY_MAX 64 /* max number of QTY lines - hardware */ int32 qty_brkio = SCPE_OK ; /* default I/O status code */ int32 qty_max = QTY_MAX ; /* max # QTY lines - user */ /* controllable */ int32 qty_mdm = 0 ; /* QTY modem control active? */ int32 qty_auto = 0 ; /* QTY auto disconnect active? */ int32 qty_polls = 0 ; /* total 'qty_svc' polls */ TMLN qty_ldsc[ QTY_MAX ] = { 0 } ; /* QTY line descriptors */ TMXR qty_desc = { QTY_MAX, 0, 0, qty_ldsc } ; /* mux descriptor */ int32 qty_status[ QTY_MAX ] = { 0 } ; /* QTY line status */ /* (must be at least 32 bits) */ int32 qty_tx_chr[ QTY_MAX ] = { 0 } ; /* QTY line output character */ /* QTY data structures qty_dev QTY device descriptor qty_unit QTY unit descriptor qty_reg QTY register list */ DIB qty_dib = { DEV_QTY, INT_QTY, PI_QTY, &qty } ; UNIT qty_unit = { UDATA (&qty_svc, (UNIT_ATTABLE), 0) } ; REG qty_reg[] = /* ('alm_reg' should be similar to this except for device code related items) */ { { ORDATA (BUF, qty_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_QTY) }, { FLDATA (DONE, dev_done, INT_V_QTY) }, { FLDATA (DISABLE, dev_disable, INT_V_QTY) }, { FLDATA (INT, int_req, INT_V_QTY) }, { FLDATA (MDMCTL, qty_mdm, 0) }, { FLDATA (AUTODS, qty_auto, 0) }, { DRDATA (POLLS, qty_polls, 32) }, { NULL } } ; MTAB qty_mod[] = { { UNIT_8B, 0, "7b", "7B", NULL }, { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *)&qty_desc }, { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &tmxr_show_summ, (void *)&qty_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *)&qty_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *)&qty_desc }, { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &qty_setnl, &tmxr_show_lines, (void *) &qty_desc }, { 0 } } ; DEVICE qty_dev = { "QTY", &qty_unit, qty_reg, qty_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &qty_reset, NULL, &qty_attach, &qty_detach, &qty_dib, (DEV_DISABLE | DEV_DIS | DEV_NET) }; #define DG_RETURN( status, data ) (int32)(((status) << IOT_V_REASON) | ((data) & 0x0FFFF) ) /* * QTY_S_xxx QTY device status reference * QTY_L_xxx QTY line status word reference (qty_status[]) */ /*----------------------------------------------*/ /* QTY device status */ /*----------------------------------------------*/ #define QTY_S_RI 0x8000 /* Receiver Interrupt */ #define QTY_S_TI 0x4000 /* Transmitter interrupt */ #define QTY_S_LMASK 0x3F00 /* line mask */ #define QTY_S_DMASK 0x00FF /* data mask (received char) */ #define QTY_MASTER_ACTIVE( desc ) ( (desc)->master ) #define QTY_LINE_EXTRACT( x ) (((x) & QTY_S_LMASK) >> 8) #define QTY_LINE_TX_CHAR( line ) qty_tx_chr[ ((line) % QTY_MAX) ] #define QTY_LINE_RX_CHAR( line ) (qty_status[ (line) ] & QTY_S_DMASK) #define QTY_UNIT_ACTIVE( unitp ) ( (unitp)->conn ) #define QTY_LINE_BITS( line, bits ) qty_status[ (line) ] & bits #define QTY_LINE_SET_BIT( line, bit ) qty_status[ (line) ] |= (bit) ; #define QTY_LINE_CLEAR_BIT( line, bit ) qty_status[ (line) ] &= ~(bit) ; #define QTY_LINE_BIT_SET( line, bit ) (qty_status[ (line) ] & (bit)) /*----------------------------------------------*/ /* QTY line status */ /*----------------------------------------------*/ #define QTY_L_RXE 0x800000 /* receiver enabled? */ #define QTY_L_RXBZ 0x400000 /* receiver busy? */ #define QTY_L_RXDN 0x200000 /* receiver done? */ #define QTY_L_TXE 0x080000 /* transmitter enabled? */ #define QTY_L_TXBZ 0x040000 /* transmitter busy? */ #define QTY_L_TXDN 0x020000 /* transmitter done? */ #define QTY_L_BREAK 0x008000 /* BREAK character received */ #define QTY_L_RING 0x004000 /* Ring interrupt */ #define QTY_L_CD 0x002000 /* Carrier Detect */ #define QTY_L_DTR 0x001000 /* Data Terminal Ready */ /* <0x00FF = character> */ #define QTY_L_LOOPBK 0x00010000 /* loopback mode */ #define QTY_L_OVRERR 0x00020000 /* overrun error */ #define QTY_L_FRMERR 0x00040000 /* framing error */ #define QTY_L_PARERR 0x00080000 /* parity error */ /* CD, CTS, DSR, RI */ /* */ #define QTY_L_MODEM 0x0080 /* */ #define QTY_L_TELNET 0x0040 /* */ #define QTY_L_AUTODIS 0x0020 /* */ #define QTY_L_PARITY #define QTY_L_7BIT #define QTY_L_BAUD /* <4 bits> */ #define QTY_L_DMASK 0x000FF /* data mask (always 8 bits) */ /* Note: use at least an 'int32' for this guy */ /*------------------------------*/ /* qty_tmxr_putc */ /*------------------------------*/ int qty_tmxr_putc( int line, TMLN * lp, int kar ) { int a ; /*----------------------------------------------*/ /* Send character to given QTY/telnet line. */ /* */ /* enter: line QTY line # */ /* lp Telnet unit def ptr */ /* kar character to send */ /* */ /* return: SCPE_OK */ /* SCPE_STALL */ /* SCPE_LOST */ /*----------------------------------------------*/ a = tmxr_putc_ln( lp, kar ) ; if ( a == SCPE_OK) { QTY_LINE_SET_BIT( line, QTY_L_TXDN ) QTY_LINE_CLEAR_BIT( line, QTY_L_TXBZ ) } else if ( a == SCPE_STALL ) { /* (should we try to output the buffer and then regroup...?) */ QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) QTY_LINE_TX_CHAR( line ) = kar ; } else if ( a == SCPE_LOST ) { /* no connection - hangup? */ QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) QTY_LINE_TX_CHAR( line ) = kar ; } return ( a ) ; } /* end of 'qty_tmxr_putc' */ /*----------------------------------------------*/ /* qty_update_rcvi */ /*----------------------------------------------*/ int qty_update_rcvi( TMXR * mp ) { int line ; TMLN * lp ; int32 datum ; int changes ; /*------------------------------------------------------*/ /* Search through connected telnet lines for any input */ /* activity. */ /* */ /* enter: mp master telnet qty desc ptr */ /* */ /* return: int change count (0 = none seen) */ /*------------------------------------------------------*/ for ( changes = line = 0; line < mp->lines; ++line ) if ( (lp=mp->ldsc+line)->conn && lp->rcve ) if ( (datum=tmxr_getc_ln(lp)) ) { if ( datum & SCPE_BREAK ) { /* what should we do here - set QTY_L_BREAK? */ datum = datum & 0x00FF ; } else { datum = datum & 0x00FF ; } /* */ QTY_LINE_CLEAR_BIT( line, (QTY_L_RXBZ | QTY_L_DMASK) ) ; QTY_LINE_SET_BIT( line, (QTY_L_RXDN | datum) ) ; ++changes ; } return ( changes ) ; } /* end of 'qty_update_rcvi' */ /*----------------------------------------------*/ /* qty_update_xmti */ /*----------------------------------------------*/ int qty_update_xmti( TMXR * mp ) { int line ; TMLN * lp ; int changes ; /*------------------------------------------------------*/ /* Search through connected telnet lines for any de- */ /* ferred output activity. */ /* */ /* enter: mp master telnet qty desc ptr */ /* */ /* return: int change count (0 = none seen) */ /*------------------------------------------------------*/ /* any TX DONE flags set * any TX BUSY flags set */ for ( changes = line = 0; line < mp->lines; ++line ) if ( QTY_LINE_BIT_SET(line,QTY_L_TXBZ) ) if ( (lp=mp->ldsc+line)->conn && lp->xmte ) { /* why are we busy? buffer was full? */ /* now some space available - try * to stuff pending character in * buffer and free up the world */ qty_tmxr_putc( line, lp, QTY_LINE_TX_CHAR(line) ) ; ++changes ; } return ( changes ) ; } /* end of 'qty_update_xmti' */ /*----------------------------------------------*/ /* qty_update_status */ /*----------------------------------------------*/ int qty_update_status( DIB * dibp, TMXR * tmxr_desc ) { int line ; int status ; int txbusy ; /*----------------------------------------------*/ /* return global device status for current qty */ /* state. */ /* */ /* Receiver interrupts have higher priority */ /* than transmitter interrupts according to DG */ /* but this routine could be modified to use */ /* different priority criteria. */ /* */ /* Round-robin polling could also be used in */ /* some future release rather than starting */ /* with line 0 each time. */ /* */ /* Return of */ /* first waiting character, else return */ /* of first finished line */ /* output, else return 0. */ /* */ /* This routine does -not- clear input line */ /* BZ/DN flags; caller should do this. */ /* */ /* Global device done and busy flags are */ /* updated. */ /*----------------------------------------------*/ for ( txbusy = status = line = 0 ; line < qty_max ; ++line ) { txbusy |= (QTY_LINE_BIT_SET(line,QTY_L_TXBZ)) ; if ( QTY_LINE_BIT_SET(line,QTY_L_RXDN) ) { if ( ! status ) { status = QTY_LINE_BITS( line, QTY_S_DMASK ) | QTY_S_RI ; status = status | (line << 8) ; } break ; } else if ( QTY_LINE_BIT_SET(line,QTY_L_TXDN) ) { if ( ! (status & QTY_S_RI) ) if ( ! (status & QTY_S_RI) ) { status = QTY_S_TI ; status = status | (line << 8) ; } } } /* */ DEV_CLR_BUSY( INT_QTY ) ; DEV_CLR_DONE( INT_QTY ) ; if ( txbusy ) { DEV_SET_BUSY( INT_QTY ) ; } if ( status & (QTY_S_RI | QTY_S_TI) ) { DEV_SET_DONE( INT_QTY ) ; } DEV_UPDATE_INTR ; /* update final intr status */ return ( status ) ; } /* end of 'qty_update_status' */ /*--------------------------------------------------------------*/ /* qty_attach */ /*--------------------------------------------------------------*/ t_stat qty_attach( UNIT * unitp, char * cptr ) { t_stat r ; int a ; /* switches: A auto-disconnect * M modem control */ qty_mdm = qty_auto = 0; /* modem ctl off */ r = tmxr_attach( &qty_desc, unitp, cptr ) ; /* attach QTY */ if ( r != SCPE_OK ) { return ( r ) ; /* error! */ } if ( sim_switches & SWMASK('M') ) /* modem control? */ { qty_mdm = 1; printf( "Modem control activated\n" ) ; if ( sim_log ) fprintf( sim_log, "Modem control activated\n" ) ; if ( sim_switches & SWMASK ('A') ) /* autodisconnect? */ { qty_auto = 1 ; printf( "Auto disconnect activated\n" ) ; if ( sim_log ) fprintf( sim_log, "Auto disconnect activated\n" ) ; } } qty_polls = 0 ; for ( a = 0 ; a < QTY_MAX ; ++a ) { /* QTY lines are always enabled - force RX and TX to 'enabled' */ qty_status[ a ] = (QTY_L_RXE | QTY_L_TXE) ; } sim_activate( unitp, tmxr_poll ) ; return ( SCPE_OK ) ; } /* end of 'qty_attach' */ /*--------------------------------------------------------------*/ /* qty_detach */ /*--------------------------------------------------------------*/ t_stat qty_detach( UNIT * unitp ) { sim_cancel( unitp ) ; return ( tmxr_detach(&qty_desc,unitp) ) ; } /* end of 'qty_detach' */ /*--------------------------------------------------------------*/ /* qty_clear */ /*--------------------------------------------------------------*/ t_stat qty_clear( t_bool flag ) { int line ; for ( line = 0 ; line < qty_max ; ++line ) { qty_ldsc[line].xmte = 0 ; qty_ldsc[line].rcve = 0 ; if ( ! qty_ldsc[line].conn ) { qty_ldsc[line].xmte = 1 ; /* set xmt enb */ qty_ldsc[line].rcve = 1 ; /* clr rcv enb */ } } return ( SCPE_OK ) ; } /* end of 'qty_clear' */ /*----------------------------------------------*/ /* qty_common_reset */ /*----------------------------------------------*/ t_stat qty_common_reset( DIB * dibp, UNIT * unitp, DEVICE * dptr ) { if ((dptr->flags & DEV_DIS) == 0) { if (dptr == &qty_dev) alm_dev.flags |= DEV_DIS; else qty_dev.flags |= DEV_DIS; } qty_clear( TRUE ) ; DEV_CLR_BUSY( INT_QTY ) ; /* clear busy */ DEV_CLR_DONE( INT_QTY ) ; /* clear done, int */ DEV_UPDATE_INTR ; if ( QTY_MASTER_ACTIVE(&qty_desc) ) { sim_activate( unitp, tmxr_poll ) ; } else { sim_cancel( unitp ) ; } return ( SCPE_OK ) ; } /* end of 'qty_common_reset' */ /*--------------------------------------------------------------*/ /* qty_reset */ /*--------------------------------------------------------------*/ t_stat qty_reset( DEVICE * dptr ) { return ( qty_common_reset(&qty_dib,&qty_unit,dptr) ) ; } /* end of 'qty_reset' */ /* Unit service routine The QTY/ALM polls to see if asynchronous activity has occurred and now needs to be processed. The polling interval is controlled by the clock simulator, so for most environments, it is calibrated to real time. The simulator assumes that software enables all of the multiplexors, or none of them. */ /*----------------------------------------------*/ /* qty_common_svc */ /*----------------------------------------------*/ t_stat qty_common_svc( DIB * dibp, UNIT * unitp ) { int line ; int newln ; TMLN * tmlnp ; ++qty_polls ; /* another time 'round the track */ newln = tmxr_poll_conn( &qty_desc ) ; /* anybody knocking at the door? */ if ( (newln >= 0) && qty_mdm ) if ( newln >= qty_max ) { return SCPE_IERR; /* WTF - sanity check failed, over? */ } else { line = newln ; /* handle modem control */ tmlnp =&qty_ldsc[ line ] ; tmlnp->rcve = tmlnp->xmte = 1 ; /* do QTY_LINE_ bit fiddling and state machine * manipulation with modem control signals */ } tmxr_poll_rx( &qty_desc ) ; /* poll input */ qty_update_rcvi( &qty_desc ) ; /* update receiver interrupt status */ tmxr_poll_tx( &qty_desc ) ; /* poll output */ qty_update_xmti( &qty_desc ) ; /* update transmitter interrupt status */ qty_update_status( dibp, &qty_desc ) ; /* update device status */ sim_activate( unitp, tmxr_poll ) ; /* restart the bubble machine */ return ( SCPE_OK ) ; } /* end of 'qty_common_svc' */ /*--------------------------------------------------------------*/ /* qty_svc */ /*--------------------------------------------------------------*/ t_stat qty_svc( UNIT * uptr ) { return ( qty_common_svc(&qty_dib,uptr) ) ; } /* end of 'qty_svc' */ /*--------------------------------------------------------------*/ /* qty */ /*--------------------------------------------------------------*/ int32 qty( int32 pulse, int32 code, int32 AC ) { int32 iodata ; int32 ioresult ; int line ; TMLN * tmlnp ; int a ; int kar ; /*--------------------------------------------------------------*/ /* DG 4060[-compatible] "quad" multiplexor instruction handler */ /*--------------------------------------------------------------*/ ioresult= qty_brkio ; /* (assume returning I/O break value */ iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ switch ( code ) { case ioNIO : /* */ break ; case ioDIA : /* get current QTY status */ iodata = qty_update_status( &qty_dib, &qty_desc ) ; if ( iodata & QTY_S_RI ) { /* clear line's input buffer */ QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(iodata)), (QTY_L_RXBZ | QTY_L_RXDN) ) /* character masking ; parity checking ; parity generating ; */ } qty_update_status( &qty_dib, &qty_desc ) ; break ; case ioDOA : /* send character to QTY */ line = QTY_LINE_EXTRACT( AC ) ; if ( line < qty_max ) if ( QTY_LINE_BIT_SET(line,QTY_L_TXE) ) { /* perform any character translation: 7 bit/ 8 bit parity generation */ kar = AC & ((qty_unit.flags & UNIT_8B)? 0377: 0177) ; /* do any parity calculations also */ tmlnp = &qty_ldsc[ line ] ; a = qty_tmxr_putc( line, tmlnp, kar ) ; if ( a != SCPE_OK) { /* do anything at this point? */ } qty_update_status( &qty_dib, &qty_desc ) ; } break ; case ioDIB : /* no QTY function - return bus noise in AC */ break ; case ioDOB : /* clear QTY output channel busy and done flag */ QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(AC)), (QTY_L_TXBZ | QTY_L_TXDN) ) qty_update_status( &qty_dib, &qty_desc ) ; break ; case ioDIC : /* no QTY function - return bus noise in AC */ break ; case ioDOC : /* no QTY function - ignore */ break ; case ioSKP : /* I/O skip test - should never come here */ break ; default : /* */ break ; } switch ( pulse ) { case iopN : /* */ break ; case iopS : /* */ break ; case iopP : /* */ break ; case iopC : qty_update_status( &qty_dib, &qty_desc ) ; break ; default : /* */ break ; } return ( DG_RETURN( ioresult, iodata ) ) ; } /* end of 'qty' */ /*--------------------------------------------------------------*/ /* qty_setnl */ /*--------------------------------------------------------------*/ t_stat qty_setnl( UNIT * uptr, int32 val, char * cptr, void * desc ) { int32 newln, i, t ; t_stat r ; if ( cptr == NULL ) { return ( SCPE_ARG ) ; } newln = (int32) get_uint( cptr, 10, QTY_MAX, &r ) ; if ( (r != SCPE_OK) || (newln == qty_desc.lines) ) { return ( r ) ; } if ( (newln == 0) || (newln > QTY_MAX) ) { return ( SCPE_ARG ) ; } if ( newln < qty_desc.lines ) { for ( i = newln, t = 0 ; i < qty_desc.lines ; ++i ) { t = t | qty_ldsc[i].conn ; } if ( t && ! get_yn("This will disconnect users; proceed [N]?", FALSE) ) { return ( SCPE_OK ) ; } for ( i = newln ; i < qty_desc.lines ; ++i ) { if ( qty_ldsc[i].conn ) { /* reset line */ tmxr_msg( qty_ldsc[i].conn, "\r\nOperator disconnected line\r\n" ) ; tmxr_reset_ln( &qty_ldsc[i] ) ; } qty_clear( TRUE ) ; /* reset mux */ } } qty_max = qty_desc.lines = newln ; /* Huh, I don't understand this yet... qty_max = ((qty_dev.flags & DEV_DIS)? 0 : (qty_desc.lines / QTY_MAX)) ; */ return ( SCPE_OK ) ; } /* end of 'qty_setnl' */ /*----------------------------------------------------------------------*/ /* ALM [425x-compatible] multiplexor */ /*----------------------------------------------------------------------*/ /* * device code: 034 [primary], * 074 [secondary] * interrupt mask: B14 [000002] * ASM mnemonic: ALM * * ALM [4255-4258] I/O instructions * * DIA read line and section requesting service * DOA select line and section (lines 0-255, 8-bits) + rcvr/xmit * DIB receive data * DOB 00 transmit data * 01 transmit BREAK * 10 set modem control status * 11 * DIC read receiver or modem status * DOC 00 control line section and diag mode * 01 * 10 specify line characteristics * 11 * * undocumented DG "features": * * NIOS sets board offline * NIOC sets board online * Modem control signal state change can signal interrupt * explicit line select with DOA * implicit line select with DIA * * We support 64 lines maximum in this release although some ALM's could * theoretically support up to 256. */ DIB alm_dib = { DEV_ALM, INT_ALM, PI_ALM, &alm } ; UNIT alm_unit = { UDATA (&alm_svc, (UNIT_ATTABLE), 0) } ; REG alm_reg[] = /* ('qty_reg' should be similar to this except for device code related items) */ { { ORDATA (BUF, alm_unit.buf, 8) }, { FLDATA (BUSY, dev_busy, INT_V_ALM) }, { FLDATA (DONE, dev_done, INT_V_ALM) }, { FLDATA (DISABLE, dev_disable, INT_V_ALM) }, { FLDATA (INT, int_req, INT_V_ALM) }, { FLDATA (MDMCTL, qty_mdm, 0) }, { FLDATA (AUTODS, qty_auto, 0) }, { DRDATA (POLLS, qty_polls, 32) }, { NULL } } ; DEVICE alm_dev = { "ALM", &alm_unit, alm_reg, qty_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &alm_reset, NULL, &qty_attach, &qty_detach, &alm_dib, (DEV_DISABLE | DEV_NET) } ; int alm_section = -1 ; /* current line "section" (0 = RCV, 1 = XMT) */ int alm_line = -1 ; /* current line [0-63] */ int alm_diag_mode = 0 ; /* */ int alm_line_mask = 0x003F ; /* maximum of 64 lines in this rev */ #define ALM_LINE_EXTRACT( x ) (((x) >> 1) & alm_line_mask) #define ALM_SECT_EXTRACT( x ) ((x) & 0x0001) /*--------------------------------------------------------------*/ /* alm_reset */ /*--------------------------------------------------------------*/ t_stat alm_reset( DEVICE * dptr ) { return ( qty_common_reset(&alm_dib,&alm_unit,dptr) ) ; } /* end of 'alm_reset' */ /*--------------------------------------------------------------*/ /* alm_svc */ /*--------------------------------------------------------------*/ t_stat alm_svc( UNIT * uptr ) { return ( qty_common_svc(&alm_dib,uptr) ) ; } /* end of 'alm_svc' */ /*--------------------------------------------------------------*/ /* alm */ /*--------------------------------------------------------------*/ int32 alm( int32 pulse, int32 code, int32 AC ) { int32 iodata ; int32 ioresult ; TMLN * tmlnp ; int a ; int kar ; /*--------------------------------------------------------------*/ /* DG 425x[-compatible] "ALM" multiplexor instruction handler */ /*--------------------------------------------------------------*/ ioresult= qty_brkio ; /* (assume returning I/O break value */ iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ switch ( code ) { case ioNIO : /* */ break ; case ioDIA : /* read line and section requesting service */ iodata = qty_update_status( &alm_dib, &qty_desc ) ; alm_line = (QTY_LINE_EXTRACT(iodata) & alm_line_mask) ; /* (mask with 'alm_line_mask' in case ALM mask is different than QTY */ alm_section = 0 ; if ( ! ( iodata & QTY_S_RI) ) if ( iodata & QTY_S_TI ) { alm_section = 1 ; /* receiver quiet - transmitter done */ } iodata = (alm_line << 1) | alm_section ; break ; case ioDOA : /* set line and section */ alm_section = ALM_SECT_EXTRACT( AC ) ; alm_line = ALM_LINE_EXTRACT( AC ) ; break ; case ioDIB : /* no ALM function - return bus noise in AC */ if ( alm_line < qty_max ) { iodata = QTY_LINE_RX_CHAR( alm_line ) ; } break ; case ioDOB : /* output and modem control functions */ switch ( (AC >> 14) & 03 ) { case 00 : /* transmit data */ if ( alm_line < qty_max ) if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) { /* perform any character translation: 7 bit/ 8 bit parity generation */ kar = AC & ((alm_unit.flags & UNIT_8B)? 0377: 0177) ; /* do any parity calculations also */ tmlnp = &qty_ldsc[ alm_line ] ; a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; if ( a != SCPE_OK) { /* do anything at this point? */ } qty_update_status( &alm_dib, &qty_desc ) ; } break ; case 01 : /* transmit break */ if ( alm_line < qty_max ) if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) { tmlnp = &qty_ldsc[ alm_line ] ; /* a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; if ( a != SCPE_OK) { } */ qty_update_status( &alm_dib, &qty_desc ) ; } break ; case 02 : /* set modem control status */ break ; case 03 : /* unused */ break ; } break ; case ioDIC : /* get modem or receiver status */ if ( alm_line < qty_max ) if ( alm_section ) { /* get modem section status */ if ( qty_ldsc[ alm_line ].xmte ) { iodata = 0035 ; /* set CD, CTS, DSR, MDM flags */ } } else { /* get receiver section status */ iodata = 0 ; /* receiver error status - no errors by default */ } break ; case ioDOC : /* set line attributes */ switch ( (AC >> 14) & 03 ) { case 00 : /* control line section */ break ; case 01 : /* unused */ break ; case 02 : /* set line characteristics */ break ; case 03 : /* unused */ break ; } break ; case ioSKP : /* I/O skip test - should never come here */ break ; default : /* */ break ; } switch ( pulse ) { case iopN : /* */ break ; case iopS : /* set device busy * set all lines on board offline * clear each line's done * clear internal system * clear device busy */ for ( a = 0 ; a < qty_max ; ++a ) if ( 1 /* (not yet optimized) */ ) { QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; } qty_update_status( &alm_dib, &qty_desc ) ; break ; case iopP : /* stop clock for all boards in off-line mode */ break ; case iopC : for ( a = 0 ; a < qty_max ; ++a ) if ( 1 /* (not yet optimized) */ ) { QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; } qty_update_status( &alm_dib, &qty_desc ) ; break ; default : /* */ break ; } return ( DG_RETURN( ioresult, iodata ) ) ; } /* end of 'alm' */ simh-3.8.1/NOVA/eclipse.txt0000644000175000017500000000174710270523540013600 0ustar vlmvlmCharles Owen's Eclipse Modules 1. Eclipse CPU simulator The Eclipse CPU simulator can be used with the Nova definitions and peripheral modules to produce an Eclipse simulator that will run Eclipse mapped RDOS V7.5. The compilation procedure is the same as for the Nova simulator, except: - the symbol ECLIPSE must be defined - the module eclipse_cpu.c must be substituted for nova_cpu.c - the output should be named eclipse rather than nova For example, to compile under UNIX, move nova_cpu.c out of the source directory and then give this command: % cc -DECLIPSE eclipse_cpu.c nova_*.c -o eclipse 2. Alternate terminal emulator The module eclipse_tt.c can be used with either an Eclipse or Nova CPU simulator in place of nova_tt.c. It provides a full emulation of the cursor controls on the Dasher video terminal but requires that the underlying operating system interpret VT100 cursor controls. Thus, it works under VMS or UNIX but not under Windows or OS/2. simh-3.8.1/scp.c0000644000175000017500000052230311143601676011604 0ustar vlmvlm/* scp.c: simulator control program Copyright (c) 1993-2009, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 08-Feb-09 RMS Fixed warnings in help printouts 29-Dec-08 RMS Fixed implementation of MTAB_NC 24-Nov-08 RMS Revised RESTORE unit logic for consistency 05-Sep-08 JDB "detach_all" ignores error status returns if shutting down 17-Aug-08 RMS Revert RUN/BOOT to standard, rather than powerup, reset 25-Jul-08 JDB DO cmd missing params now default to null string 29-Jun-08 JDB DO cmd sub_args now allows "\\" to specify literal backslash 31-Mar-08 RMS Fixed bug in local/global register search (found by Mark Pizzolato) Fixed bug in restore of RO units (from Mark Pizzolato) 06-Feb-08 RMS Added SET/SHO/NO BR with default argument 18-Jul-07 RMS Modified match_ext for VMS ext;version support 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all Fixed bug in get_sim_opt Fixed bug in restoration with changed memory size 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing 30-Jan-07 RMS Fixed bugs in get_ipaddr 17-Oct-06 RMS Added idle support 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q 14-Jul-06 RMS Added sim_activate_abs 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure Added -E switch to do_cmd to exit on any error 14-Feb-06 RMS Upgraded save file format to V3.5 18-Jan-06 RMS Added fprint_stopped_gen Added breakpoint spaces Fixed unaligned register access (found by Doug Carman) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 30-Aug-05 RMS Revised to trim trailing spaces on file names 25-Aug-05 RMS Added variable default device support 23-Aug-05 RMS Added Linux line history support 16-Aug-05 RMS Fixed C++ declaration and cast problems 01-May-05 RMS Revised syntax for SET DEBUG (from Dave Bryan) 22-Mar-05 JDB Modified DO command to allow ten-level nesting 18-Mar-05 RMS Moved DETACH tests into detach_unit (from Dave Bryan) Revised interface to fprint_sym, fparse_sym 07-Feb-05 RMS Added ASSERT command (from Dave Bryan) 02-Feb-05 RMS Fixed bug in global register search 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (from Dave Bryan) Revised to flush output files after simulation stop (from Dave Bryan) 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions 27-Sep-04 RMS Fixed comma-separation options in set (from David Bryan) 09-Sep-04 RMS Added -p option for RESET 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST 17-Jul-04 RMS Added ECHO command (from Dave Bryan) 12-Jul-04 RMS Fixed problem ATTACHing to read only files (found by John Dundas) 28-May-04 RMS Added SET/SHOW CONSOLE 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2) RMS Added debug print routines (from Dave Hittner) RMS Added sim_vm_parse_addr and sim_vm_fprint_addr RMS Added REG_VMAD support RMS Split out libraries RMS Moved logging function to SCP RMS Exposed step counter interface(s) RMS Fixed double logging of SHOW BREAK (found by Mark Pizzolato) RMS Fixed implementation of REG_VMIO RMS Added SET/SHOW DEBUG, SET/SHOW DEBUG, SHOW MODIFIERS, SHOW RADIX RMS Changed sim_fsize to take uptr argument 29-Dec-03 RMS Added Telnet console output stall support 01-Nov-03 RMS Cleaned up implicit detach on attach/restore Fixed bug in command line read while logging (found by Mark Pizzolato) 01-Sep-03 RMS Fixed end-of-file problem in dep, idep Fixed error on trailing spaces in dep, idep 15-Jul-03 RMS Removed unnecessary test in reset_all 15-Jun-03 RMS Added register flag REG_VMIO 25-Apr-03 RMS Added extended address support (V3.0) Fixed bug in SAVE (found by Peter Schorn) Added u5, u6 fields Added logical name support 03-Mar-03 RMS Added sim_fsize 27-Feb-03 RMS Fixed bug in multiword deposits to files 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char* Added multiple actions, .ini file support Added multiple switch evaluations per line 07-Feb-03 RMS Added VMS support for ! (from Mark Pizzolato) 01-Feb-03 RMS Added breakpoint table extension, actions 14-Jan-03 RMS Added missing function prototypes 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support, case sensitive SET options 22-Dec-02 RMS Added ! (OS command) feature (from Mark Pizzolato) 17-Dec-02 RMS Added get_ipaddr 02-Dec-02 RMS Added EValuate command 16-Nov-02 RMS Fixed bug in register name match algorithm 13-Oct-02 RMS Fixed Borland compiler warnings (found by Hans Pufal) 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (found by David Hittner) Added support for fixed buffer devices Added support for Telnet console, removed VT support Added help Added VMS file optimizations (from Robert Alan Byer) Added quiet mode, DO with parameters, GUI interface, extensible commands (from Brian Knittel) Added device enable/disable commands 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from Brian Knittel) 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by Norm Lastovic) 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added magtape record length error (found by Jonathan Engdahl) 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval (found by Brian Knittel) 10-Feb-02 RMS Fixed problem in clock calibration 06-Jan-02 RMS Moved device enable/disable to simulators 30-Dec-01 RMS Generalized timer packaged, added circular arrays 19-Dec-01 RMS Fixed DO command bug (found by John Dundas) 07-Dec-01 RMS Implemented breakpoint package 05-Dec-01 RMS Fixed bug in universal register logic 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers 24-Nov-01 RMS Added unit-based registers 16-Nov-01 RMS Added DO command 28-Oct-01 RMS Added relative range addressing 08-Oct-01 RMS Added SHOW VERSION 30-Sep-01 RMS Relaxed attach test in BOOT 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod 17-Sep-01 RMS Removed multiple console support 07-Sep-01 RMS Removed conditional externs on function prototypes Added special modifier print 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7) 18-Jul-01 RMS Minor changes for Macintosh port 12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy) 27-May-01 RMS Added multiple console support 16-May-01 RMS Added logging 15-May-01 RMS Added features from Tim Litt 12-May-01 RMS Fixed missing return in disable_cmd 25-Mar-01 RMS Added ENABLE/DISABLE 14-Mar-01 RMS Revised LOAD/DUMP interface (again) 05-Mar-01 RMS Added clock calibration support 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine 21-Jan-01 RMS Added relative time 22-Dec-00 RMS Fixed find_device for devices ending in numbers 08-Dec-00 RMS V2.5a changes 30-Oct-00 RMS Added output file option to examine 11-Jul-99 RMS V2.5 changes 13-Apr-99 RMS Fixed handling of 32b addresses 04-Oct-98 RMS V2.4 changes 20-Aug-98 RMS Added radix commands 05-Jun-98 RMS Fixed bug in ^D handling for UNIX 10-Apr-98 RMS Added switches to all commands 26-Oct-97 RMS Added search capability 25-Jan-97 RMS Revised data types 23-Jan-97 RMS Added bi-endian I/O 06-Sep-96 RMS Fixed bug in variable length IEXAMINE 16-Jun-96 RMS Changed interface to parse/print_sym 06-Apr-96 RMS Added error checking in reset all 07-Jan-96 RMS Added register buffers in save/restore 11-Dec-95 RMS Fixed ordering bug in save/restore 22-May-95 RMS Added symbolic input 13-Apr-95 RMS Added symbolic printouts */ /* Macros and data structures */ #include "sim_defs.h" #include "sim_rev.h" #include #include #define EX_D 0 /* deposit */ #define EX_E 1 /* examine */ #define EX_I 2 /* interactive */ #define SCH_OR 0 /* search logicals */ #define SCH_AND 1 #define SCH_XOR 2 #define SCH_E 0 /* search booleans */ #define SCH_N 1 #define SCH_G 2 #define SCH_L 3 #define SCH_EE 4 #define SCH_NE 5 #define SCH_GE 6 #define SCH_LE 7 #define SSH_ST 0 /* set */ #define SSH_SH 1 /* show */ #define SSH_CL 2 /* clear */ #define DO_NEST_LVL 10 /* DO cmd nesting level */ #define SRBSIZ 1024 /* save/restore buffer */ #define SIM_BRK_INILNT 4096 /* bpt tbl length */ #define SIM_BRK_ALLTYP 0xFFFFFFFF #define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \ sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \ x = sim_interval #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT]) #define SZ_R(rp) \ (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT]) #if defined (USE_INT64) #define SZ_LOAD(sz,v,mb,j) \ if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \ else v = *(((t_uint64 *) mb) + ((uint32) j)); #define SZ_STORE(sz,v,mb,j) \ if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \ else *(((t_uint64 *) mb) + ((uint32) j)) = v; #else #define SZ_LOAD(sz,v,mb,j) \ if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ else v = *(((uint32 *) mb) + ((uint32) j)); #define SZ_STORE(sz,v,mb,j) \ if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \ else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ else *(((uint32 *) mb) + ((uint32) j)) = v; #endif #define GET_SWITCHES(cp) \ if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW #define GET_RADIX(val,dft) \ if (sim_switches & SWMASK ('O')) val = 8; \ else if (sim_switches & SWMASK ('D')) val = 10; \ else if (sim_switches & SWMASK ('H')) val = 16; \ else val = dft; /* VM interface */ extern char sim_name[]; extern DEVICE *sim_devices[]; extern REG *sim_PC; extern const char *sim_stop_messages[]; extern t_stat sim_instr (void); extern t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int32 flag); extern int32 sim_emax; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); /* The per-simulator init routine is a weak global that defaults to NULL The other per-simulator pointers can be overrriden by the init routine */ void (*sim_vm_init) (void); char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL; void (*sim_vm_post) (t_bool from_scp) = NULL; CTAB *sim_vm_cmd = NULL; void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL; t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr) = NULL; /* Prototypes */ /* Set and show command processors */ t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat ssh_break (FILE *st, char *cptr, int32 flg); t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr); t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_device (FILE *st, DEVICE *dptr, int32 flag); t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag); t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg); t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag); t_stat sim_check_console (int32 sec); t_stat sim_save (FILE *sfile); t_stat sim_rest (FILE *rfile); /* Breakpoint package */ t_stat sim_brk_init (void); t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act); t_stat sim_brk_clr (t_addr loc, int32 sw); t_stat sim_brk_clrall (int32 sw); t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw); t_stat sim_brk_showall (FILE *st, int32 sw); char *sim_brk_getact (char *buf, int32 size); void sim_brk_clract (void); void sim_brk_npc (uint32 cnt); BRKTAB *sim_brk_new (t_addr loc); /* Commands support routines */ SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr); int32 test_search (t_value val, SCHTAB *schptr); char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc); int32 get_switches (char *cptr); char *get_sim_sw (char *cptr); t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); t_value get_rval (REG *rptr, uint32 idx); void put_rval (REG *rptr, uint32 idx, t_value val); t_value strtotv (char *inptr, char **endptr, uint32 radix); void fprint_help (FILE *st); void fprint_stopped (FILE *st, t_stat r); void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr); char *read_line (char *ptr, int32 size, FILE *stream); REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr); char *sim_trim_endspc (char *cptr); /* Forward references */ t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr); t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr); t_bool qdisable (DEVICE *dptr); t_stat attach_err (UNIT *uptr, t_stat stat); t_stat detach_all (int32 start_device, t_bool shutdown); t_stat assign_device (DEVICE *dptr, char *cptr); t_stat deassign_device (DEVICE *dptr); t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr); t_stat run_boot_prep (void); t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, REG *lowr, REG *highr, uint32 lows, uint32 highs); t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx); t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx); t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr); t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr); t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, UNIT *uptr, int32 dfltinc); t_stat step_svc (UNIT *ptr); void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]); /* Global data */ DEVICE *sim_dflt_dev = NULL; UNIT *sim_clock_queue = NULL; int32 sim_interval = 0; int32 sim_switches = 0; FILE *sim_ofile = NULL; SCHTAB *sim_schptr = FALSE; DEVICE *sim_dfdev = NULL; UNIT *sim_dfunit = NULL; int32 sim_opt_out = 0; int32 sim_is_running = 0; uint32 sim_brk_summ = 0; uint32 sim_brk_types = 0; uint32 sim_brk_dflt = 0; char *sim_brk_act = NULL; BRKTAB *sim_brk_tab = NULL; int32 sim_brk_ent = 0; int32 sim_brk_lnt = 0; int32 sim_brk_ins = 0; t_bool sim_brk_pend[SIM_BKPT_N_SPC] = { FALSE }; t_addr sim_brk_ploc[SIM_BKPT_N_SPC] = { 0 }; int32 sim_quiet = 0; int32 sim_step = 0; static double sim_time; static uint32 sim_rtime; static int32 noqueue_time; volatile int32 stop_cpu = 0; t_value *sim_eval = NULL; int32 sim_deb_close = 0; /* 1 = close debug */ FILE *sim_log = NULL; /* log file */ FILE *sim_deb = NULL; /* debug file */ static SCHTAB sim_stab; static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) }; #if defined USE_INT64 static const char *sim_si64 = "64b data"; #else static const char *sim_si64 = "32b data"; #endif #if defined USE_ADDR64 static const char *sim_sa64 = "64b addresses"; #else static const char *sim_sa64 = "32b addresses"; #endif #if defined USE_NETWORK static const char *sim_snet = "Ethernet support"; #else static const char *sim_snet = "no Ethernet"; #endif /* Tables and strings */ const char save_vercur[] = "V3.5"; const char save_ver32[] = "V3.2"; const char save_ver30[] = "V3.0"; const char *scp_error_messages[] = { "Address space exceeded", "Unit not attached", "I/O error", "Checksum error", "Format error", "Unit not attachable", "File open error", "Memory exhausted", "Invalid argument", "Step expired", "Unknown command", "Read only argument", "Command not completed", "Simulation stopped", "Goodbye", "Console input I/O error", "Console output I/O error", "End of file", "Relocation error", "No settable parameters", "Unit already attached", "Hardware timer error", "SIGINT handler setup error", "Console terminal setup error", "Subscript out of range", "Command not allowed", "Unit disabled", "Read only operation not allowed", "Invalid switch", "Missing value", "Too few arguments", "Too many arguments", "Non-existent device", "Non-existent unit", "Non-existent register", "Non-existent parameter", "Nested DO command limit exceeded", "Internal error", "Invalid magtape record length", "Console Telnet connection lost", "Console Telnet connection timed out", "Console Telnet output stall", "Assertion failed" }; const size_t size_map[] = { sizeof (int8), sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) #if defined (USE_INT64) , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64) #endif }; const t_value width_mask[] = { 0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF #if defined (USE_INT64) , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF, 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF, 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF, 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF, 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF, 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF, 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF, 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF #endif }; static CTAB cmd_table[] = { { "RESET", &reset_cmd, 0, "r{eset} {ALL|} reset simulator\n" }, { "EXAMINE", &exdep_cmd, EX_E, "e{xamine} examine memory or registers\n" }, { "IEXAMINE", &exdep_cmd, EX_E+EX_I, "ie{xamine} interactive examine memory or registers\n" }, { "DEPOSIT", &exdep_cmd, EX_D, "d{eposit} deposit in memory or registers\n" }, { "IDEPOSIT", &exdep_cmd, EX_D+EX_I, "id{eposit} interactive deposit in memory or registers\n" }, { "EVALUATE", &eval_cmd, 0, "ev{aluate} evaluate symbolic expression\n" }, { "RUN", &run_cmd, RU_RUN, "ru{n} {new PC} reset and start simulation\n" }, { "GO", &run_cmd, RU_GO, "go {new PC} start simulation\n" }, { "STEP", &run_cmd, RU_STEP, "s{tep} {n} simulate n instructions\n" }, { "CONT", &run_cmd, RU_CONT, "c{ont} continue simulation\n" }, { "BOOT", &run_cmd, RU_BOOT, "b{oot} bootstrap unit\n" }, { "BREAK", &brk_cmd, SSH_ST, "br{eak} set breakpoints\n" }, { "NOBREAK", &brk_cmd, SSH_CL, "nobr{eak} clear breakpoints\n" }, { "ATTACH", &attach_cmd, 0, "at{tach} attach file to simulated unit\n" }, { "DETACH", &detach_cmd, 0, "det{ach} detach file from simulated unit\n" }, { "ASSIGN", &assign_cmd, 0, "as{sign} assign logical name for device\n" }, { "DEASSIGN", &deassign_cmd, 0, "dea{ssign} deassign logical name for device\n" }, { "SAVE", &save_cmd, 0, "sa{ve} save simulator to file\n" }, { "RESTORE", &restore_cmd, 0, "rest{ore}|ge{t} restore simulator from file\n" }, { "GET", &restore_cmd, 0, NULL }, { "LOAD", &load_cmd, 0, "l{oad} {} load binary file\n" }, { "DUMP", &load_cmd, 1, "du(mp) {} dump binary file\n" }, { "EXIT", &exit_cmd, 0, "exi{t}|q{uit}|by{e} exit from simulation\n" }, { "QUIT", &exit_cmd, 0, NULL }, { "BYE", &exit_cmd, 0, NULL }, { "SET", &set_cmd, 0, "set console arg{,arg...} set console options\n" "set break set breakpoints\n" "set nobreak clear breakpoints\n" "set throttle x{M|K|%%} set simulation rate\n" "set nothrottle set simulation rate to maximum\n" "set OCT|DEC|HEX set device display radix\n" "set ENABLED enable device\n" "set DISABLED disable device\n" "set DEBUG{=arg} set device debug flags\n" "set NODEBUG={arg} clear device debug flags\n" "set arg{,arg...} set device parameters\n" "set ENABLED enable unit\n" "set DISABLED disable unit\n" "set arg{,arg...} set unit parameters\n" }, { "SHOW", &show_cmd, 0, "sh{ow} br{eak} show breakpoints\n" "sh{ow} con{figuration} show configuration\n" "sh{ow} cons{ole} {arg} show console options\n" "sh{ow} dev{ices} show devices\n" "sh{ow} m{odifiers} show modifiers\n" "sh{ow} n{ames} show logical names\n" "sh{ow} q{ueue} show event queue\n" "sh{ow} ti{me} show simulated time\n" "sh{ow} th{rottle} show simulation rate\n" "sh{ow} ve{rsion} show simulator version\n" "sh{ow} RADIX show device display radix\n" "sh{ow} DEBUG show device debug flags\n" "sh{ow} MODIFIERS show device modifiers\n" "sh{ow} {arg,...} show device parameters\n" "sh{ow} {arg,...} show unit parameters\n" }, { "DO", &do_cmd, 1, "do {arg,arg...} process command file\n" }, { "ECHO", &echo_cmd, 0, "echo display \n" }, { "ASSERT", &assert_cmd, 0, "assert {} test simulator state against condition\n" }, { "HELP", &help_cmd, 0, "h{elp} type this message\n" "h{elp} type help for command\n" }, { "!", &spawn_cmd, 0, "! execute local command interpreter\n" "! execute local host command\n" }, { NULL, NULL, 0 } }; /* Main command loop */ int main (int argc, char *argv[]) { char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr; int32 i, sw; t_bool lookswitch; t_stat stat; CTAB *cmdp; #if defined (__MWERKS__) && defined (macintosh) argc = ccommand (&argv); #endif *cbuf = 0; /* init arg buffer */ sim_switches = 0; /* init switches */ lookswitch = TRUE; for (i = 1; i < argc; i++) { /* loop thru args */ if (argv[i] == NULL) /* paranoia */ continue; if ((*argv[i] == '-') && lookswitch) { /* switch? */ if ((sw = get_switches (argv[i])) < 0) { fprintf (stderr, "Invalid switch %s\n", argv[i]); return 0; } sim_switches = sim_switches | sw; } else { if ((strlen (argv[i]) + strlen (cbuf) + 1) >= CBUFSIZE) { fprintf (stderr, "Argument string too long\n"); return 0; } if (*cbuf) /* concat args */ strcat (cbuf, " "); strcat (cbuf, argv[i]); lookswitch = FALSE; /* no more switches */ } } /* end for */ sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */ if (sim_vm_init != NULL) /* call once only */ (*sim_vm_init)(); sim_finit (); /* init fio package */ stop_cpu = 0; sim_interval = 0; sim_time = sim_rtime = 0; noqueue_time = 0; sim_clock_queue = NULL; sim_is_running = 0; sim_log = NULL; if (sim_emax <= 0) sim_emax = 1; sim_timer_init (); if ((stat = sim_ttinit ()) != SCPE_OK) { fprintf (stderr, "Fatal terminal initialization error\n%s\n", scp_error_messages[stat - SCPE_BASE]); return 0; } if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) { fprintf (stderr, "Unable to allocate examine buffer\n"); return 0; }; if ((stat = reset_all_p (0)) != SCPE_OK) { fprintf (stderr, "Fatal simulator initialization error\n%s\n", scp_error_messages[stat - SCPE_BASE]); return 0; } if ((stat = sim_brk_init ()) != SCPE_OK) { fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n", scp_error_messages[stat - SCPE_BASE]); return 0; } if (!sim_quiet) { printf ("\n"); show_version (stdout, NULL, NULL, 0, NULL); } if (sim_dflt_dev == NULL) /* if no default */ sim_dflt_dev = sim_devices[0]; if (*cbuf) /* cmd file arg? */ stat = do_cmd (0, cbuf); /* proc cmd file */ else if (*argv[0]) { /* sim name arg? */ char nbuf[PATH_MAX + 7], *np; /* "path.ini" */ nbuf[0] = '"'; /* starting " */ strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */ if (np = match_ext (nbuf, "EXE")) /* remove .exe */ *np = 0; strcat (nbuf, ".ini\""); /* add .ini" */ stat = do_cmd (-1, nbuf); /* proc cmd file */ } while (stat != SCPE_EXIT) { /* in case exit */ printf ("sim> "); /* prompt */ if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */ printf ("%s\n", cptr); /* echo */ else if (sim_vm_read != NULL) /* sim routine? */ cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin); else cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ if (cptr == NULL) /* ignore EOF */ continue; if (*cptr == 0) /* ignore blank */ continue; if (sim_log) /* log cmd */ fprintf (sim_log, "sim> %s\n", cptr); cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_switches = 0; /* init switches */ if (cmdp = find_cmd (gbuf)) /* lookup command */ stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */ else stat = SCPE_UNK; if (stat >= SCPE_BASE) { /* error? */ printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); if (sim_log) fprintf (sim_log, "%s\n", scp_error_messages[stat - SCPE_BASE]); } if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); } /* end while */ detach_all (0, TRUE); /* close files */ sim_set_deboff (0, NULL); /* close debug */ sim_set_logoff (0, NULL); /* close log */ sim_set_notelnet (0, NULL); /* close Telnet */ sim_ttclose (); /* close console */ return 0; } /* Find command routine */ CTAB *find_cmd (char *gbuf) { CTAB *cmdp = NULL; if (sim_vm_cmd) /* try ext commands */ cmdp = find_ctab (sim_vm_cmd, gbuf); if (cmdp == NULL) /* try regular cmds */ cmdp = find_ctab (cmd_table, gbuf); return cmdp; } /* Exit command */ t_stat exit_cmd (int32 flag, char *cptr) { return SCPE_EXIT; } /* Help command */ void fprint_help (FILE *st) { CTAB *cmdp; for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) { if (cmdp->help) fputs (cmdp->help, st); } for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) { if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) fputs (cmdp->help, st); } return; } t_stat help_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; CTAB *cmdp; GET_SWITCHES (cptr); if (*cptr) { cptr = get_glyph (cptr, gbuf, 0); if (*cptr) return SCPE_2MARG; if (cmdp = find_cmd (gbuf)) { fputs (cmdp->help, stdout); if (sim_log) fputs (cmdp->help, sim_log); } else return SCPE_ARG; } else { fprint_help (stdout); if (sim_log) fprint_help (sim_log); } return SCPE_OK; } /* Spawn command */ t_stat spawn_cmd (int32 flag, char *cptr) { if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("SHELL"); if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("ComSpec"); #if defined (VMS) if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = "SPAWN/INPUT=SYS$COMMAND:"; #endif fflush(stdout); /* flush stdout */ if (sim_log) /* flush log if enabled */ fflush (sim_log); system (cptr); #if defined (VMS) printf ("\n"); #endif return SCPE_OK; } /* Echo command */ t_stat echo_cmd (int32 flag, char *cptr) { puts (cptr); if (sim_log) fprintf (sim_log, "%s\n", cptr); return SCPE_OK; } /* Do command Syntax: DO {-E} {-V} {...} -E causes all command errors to be fatal; without it, only EXIT and ASSERT failure will stop a command file. -V causes commands to be echoed before execution. Note that SCPE_STEP ("Step expired") is considered a note and not an error and so does not abort command execution when using -E. Inputs: flag = caller and nesting level indicator fcptr = filename and optional arguments, space-separated Outputs: status = error status The "flag" input value indicates the source of the call, as follows: -1 = initialization file (no error if not found) 0 = command line file 1 = "DO" command >1 = nested "DO" command */ #define SCPE_DOFAILED 0040000 /* fail in DO, not subproc */ t_stat do_cmd (int32 flag, char *fcptr) { char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE], *c, quote, *do_arg[10]; FILE *fpin; CTAB *cmdp; int32 echo, nargs, errabort; t_bool interactive, isdo, staying; t_stat stat; char *ocptr; stat = SCPE_OK; staying = TRUE; interactive = (flag > 0); /* issued interactively? */ if (interactive) { /* get switches */ GET_SWITCHES (fcptr); } echo = sim_switches & SWMASK ('V'); /* -v means echo */ errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */ c = fcptr; for (nargs = 0; nargs < 10; ) { /* extract arguments */ while (isspace (*c)) /* skip blanks */ c++; if (*c == 0) /* all done? */ do_arg [nargs++] = NULL; /* null argument */ else { if (*c == '\'' || *c == '"') /* quoted string? */ quote = *c++; else quote = 0; do_arg[nargs++] = c; /* save start */ while (*c && (quote ? (*c != quote) : !isspace (*c))) c++; if (*c) /* term at quote/spc */ *c++ = 0; } } /* end for */ if (nargs <= 0) /* need at least 1 */ return SCPE_2FARG; if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */ if (flag == 0) /* cmd line file? */ fprintf (stderr, "Can't open file %s\n", do_arg[0]); if (flag > 1) return SCPE_OPENERR | SCPE_DOFAILED; /* return failure with flag */ else return SCPE_OPENERR; /* return failure */ } if (flag < 1) /* start at level 1 */ flag = 1; do { ocptr = cptr = sim_brk_getact (cbuf, CBUFSIZE); /* get bkpt action */ if (!ocptr) /* no pending action? */ ocptr = cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */ sub_args (cbuf, gbuf, CBUFSIZE, do_arg); /* substitute args */ if (cptr == NULL) { /* EOF? */ stat = SCPE_OK; /* set good return */ break; } if (*cptr == 0) /* ignore blank */ continue; if (echo) /* echo if -v */ printf("do> %s\n", cptr); if (echo && sim_log) fprintf (sim_log, "do> %s\n", cptr); cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_switches = 0; /* init switches */ isdo = FALSE; if (cmdp = find_cmd (gbuf)) { /* lookup command */ isdo = (cmdp->action == &do_cmd); if (isdo) { /* DO command? */ if (flag >= DO_NEST_LVL) /* nest too deep? */ stat = SCPE_NEST; else stat = do_cmd (flag + 1, cptr); /* exec DO cmd */ } else stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */ } else stat = SCPE_UNK; /* bad cmd given */ staying = (stat != SCPE_EXIT) && /* decide if staying */ (stat != SCPE_AFAIL) && (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP)); if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ (stat != SCPE_STEP)) { if (!echo && !sim_quiet && /* report if not echoing */ (!isdo || (stat & SCPE_DOFAILED))) { /* and not from DO return */ printf("%s> %s\n", do_arg[0], ocptr); if (sim_log) fprintf (sim_log, "%s> %s\n", do_arg[0], ocptr); } stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ } if ((staying || !interactive) && /* report error if staying */ (stat >= SCPE_BASE)) { /* or in cmdline file */ printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); if (sim_log) fprintf (sim_log, "%s\n", scp_error_messages[stat - SCPE_BASE]); } if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); } while (staying); fclose (fpin); /* close file */ return stat; } /* Substitute_args - replace %n tokens in 'instr' with the do command's arguments Calling sequence instr = input string tmpbuf = temp buffer maxstr = min (len (instr), len (tmpbuf)) do_arg[10] = arguments Token "%0" represents the command file name. The input sequence "\%" represents a literal "%", and "\\" represents a literal "\". All other character combinations are rendered literally. Omitted parameters result in null-string substitutions. */ void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]) { char *ip, *op, *ap, *oend = tmpbuf + maxstr - 2; for (ip = instr, op = tmpbuf; *ip && (op < oend); ) { if ((ip [0] == '\\') && /* literal escape? */ ((ip [1] == '%') || (ip [1] == '\\'))) { /* and followed by '%' or '\'? */ ip++; /* skip '\' */ *op++ = *ip++; /* copy escaped char */ } else if ((*ip == '%') && /* %n = sub */ ((ip[1] >= '0') && (ip[1] <= ('9')))) { ap = do_arg[ip[1] - '0']; ip = ip + 2; if (ap) { /* non-null arg? */ while (*ap && (op < oend)) /* copy the argument */ *op++ = *ap++; } } else *op++ = *ip++; /* literal character */ } *op = 0; /* term buffer */ strcpy (instr, tmpbuf); return; } /* Assert command Syntax: ASSERT {} {} If is not specified, CPU is assumed. is expressed in the radix specified for . and are the same as that allowed for examine and deposit search specifications. */ t_stat assert_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE], *gptr, *aptr, *tptr; REG *rptr; uint32 idx; t_value val; t_stat r; aptr = cptr; /* save assertion */ cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, cptr, &r); /* get sw, default */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get register */ rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */ if (!rptr) /* not there */ return SCPE_NXREG; if (*gptr == '[') { /* subscript? */ if (rptr->depth <= 1) /* array register? */ return SCPE_ARG; idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */ if ((gptr == tptr) || (*tptr++ != ']')) return SCPE_ARG; gptr = tptr; /* update */ } else idx = 0; /* not array */ if (idx >= rptr->depth) /* validate subscript */ return SCPE_SUB; if (*gptr != 0) /* more? must be search */ get_glyph (gptr, gbuf, 0); else { if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get search cond */ } if (*cptr != 0) /* must be done */ return SCPE_2MARG; if (!get_search (gbuf, rptr->radix, &sim_stab)) /* parse condition */ return SCPE_MISVAL; val = get_rval (rptr, idx); /* get register value */ if (test_search (val, &sim_stab)) /* test condition */ return SCPE_OK; return SCPE_AFAIL; /* condition fails */ } /* Set command */ t_stat set_cmd (int32 flag, char *cptr) { int32 lvl; t_stat r; char gbuf[CBUFSIZE], *cvptr, *svptr; DEVICE *dptr; UNIT *uptr; MTAB *mptr; CTAB *gcmdp; C1TAB *ctbr, *glbr; static CTAB set_glob_tab[] = { { "CONSOLE", &sim_set_console, 0 }, { "BREAK", &brk_cmd, SSH_ST }, { "TELNET", &sim_set_telnet, 0 }, /* deprecated */ { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */ { "LOG", &sim_set_logon, 0 }, /* deprecated */ { "NOLOG", &sim_set_logoff, 0 }, /* deprecated */ { "DEBUG", &sim_set_debon, 0 }, /* deprecated */ { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */ { "THROTTLE", &sim_set_throt, 1 }, { "NOTHROTTLE", &sim_set_throt, 0 }, { NULL, NULL, 0 } }; static C1TAB set_dev_tab[] = { { "OCTAL", &set_dev_radix, 8 }, { "DECIMAL", &set_dev_radix, 10 }, { "HEX", &set_dev_radix, 16 }, { "ENABLED", &set_dev_enbdis, 1 }, { "DISABLED", &set_dev_enbdis, 0 }, { "DEBUG", &set_dev_debug, 1 }, { "NODEBUG", &set_dev_debug, 0 }, { NULL, NULL, 0 } }; static C1TAB set_unit_tab[] = { { "ENABLED", &set_unit_enbdis, 1 }, { "DISABLED", &set_unit_enbdis, 0 }, { NULL, NULL, 0 } }; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */ if (dptr = find_dev (gbuf)) { /* device match? */ uptr = dptr->units; /* first unit */ ctbr = set_dev_tab; /* global table */ lvl = MTAB_VDV; /* device match */ } else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */ if (uptr == NULL) /* invalid unit */ return SCPE_NXUN; ctbr = set_unit_tab; /* global table */ lvl = MTAB_VUN; /* unit match */ } else if (gcmdp = find_ctab (set_glob_tab, gbuf)) /* global? */ return gcmdp->action (gcmdp->arg, cptr); /* do the rest */ else return SCPE_NXDEV; /* no match */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */ if (cvptr = strchr (gbuf, '=')) /* = value? */ *cvptr++ = 0; for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) { if ((mptr->mstring) && /* match string */ (MATCH_CMD (gbuf, mptr->mstring) == 0)) { /* matches option? */ if (mptr->mask & MTAB_XTD) { /* extended? */ if ((lvl & mptr->mask) == 0) return SCPE_ARG; if ((lvl & MTAB_VUN) && (uptr->flags & UNIT_DIS)) return SCPE_UDIS; /* unit disabled? */ if (mptr->valid) { /* validation rtn? */ if (cvptr && (mptr->mask & MTAB_NC)) { get_glyph_nc (svptr, gbuf, ','); if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; } r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc); if (r != SCPE_OK) return r; } else if (!mptr->desc) /* value desc? */ break; // else if (mptr->mask & MTAB_VAL) { /* take a value? */ // if (!cvptr) return SCPE_MISVAL; /* none? error */ // r = dep_reg (0, cvptr, (REG *) mptr->desc, 0); // if (r != SCPE_OK) return r; // } else if (cvptr) /* = value? */ return SCPE_ARG; else *((int32 *) mptr->desc) = mptr->match; } /* end if xtd */ else { /* old style */ if (cvptr) /* = value? */ return SCPE_ARG; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; if ((mptr->valid) && /* invalid? */ ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK)) return r; uptr->flags = (uptr->flags & ~(mptr->mask)) | (mptr->match & mptr->mask); /* set new value */ } /* end else xtd */ break; /* terminate for */ } /* end if match */ } /* end for */ if (!mptr || (mptr->mask == 0)) { /* no match? */ if (glbr = find_c1tab (ctbr, gbuf)) { /* global match? */ r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */ if (r != SCPE_OK) return r; } else if (!dptr->modifiers) /* no modifiers? */ return SCPE_NOPARAM; else return SCPE_NXPAR; } /* end if no mat */ } /* end while */ return SCPE_OK; /* done all */ } /* Match CTAB/CTAB1 name */ CTAB *find_ctab (CTAB *tab, char *gbuf) { for (; tab->name != NULL; tab++) { if (MATCH_CMD (gbuf, tab->name) == 0) return tab; } return NULL; } C1TAB *find_c1tab (C1TAB *tab, char *gbuf) { for (; tab->name != NULL; tab++) { if (MATCH_CMD (gbuf, tab->name) == 0) return tab; } return NULL; } /* Set device data radix routine */ t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (cptr) return SCPE_ARG; dptr->dradix = flag & 037; return SCPE_OK; } /* Set device enabled/disabled routine */ t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { UNIT *up; uint32 i; if (cptr) return SCPE_ARG; if ((dptr->flags & DEV_DISABLE) == 0) /* allowed? */ return SCPE_NOFNC; if (flag) { /* enable? */ if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */ return SCPE_OK; dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */ } else { if (dptr->flags & DEV_DIS) /* already dsb? ok */ return SCPE_OK; for (i = 0; i < dptr->numunits; i++) { /* check units */ up = (dptr->units) + i; /* att or active? */ if ((up->flags & UNIT_ATT) || sim_is_active (up)) return SCPE_NOFNC; /* can't do it */ } dptr->flags = dptr->flags | DEV_DIS; /* disable */ } if (dptr->reset) /* reset device */ return dptr->reset (dptr); else return SCPE_OK; } /* Set unit enabled/disabled routine */ t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (cptr) return SCPE_ARG; if (!(uptr->flags & UNIT_DISABLE)) /* allowed? */ return SCPE_NOFNC; if (flag) /* enb? enable */ uptr->flags = uptr->flags & ~UNIT_DIS; else { if ((uptr->flags & UNIT_ATT) || /* dsb */ sim_is_active (uptr)) /* more tests */ return SCPE_NOFNC; uptr->flags = uptr->flags | UNIT_DIS; /* disable */ } return SCPE_OK; } /* Set device debug enabled/disabled routine */ t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEBTAB *dep; if ((dptr->flags & DEV_DEBUG) == 0) return SCPE_NOFNC; if (cptr == NULL) { /* no arguments? */ dptr->dctrl = flag; /* disable/enable w/o table */ if (flag && dptr->debflags) { /* enable with table? */ for (dep = dptr->debflags; dep->name != NULL; dep++) dptr->dctrl = dptr->dctrl | dep->mask; /* set all */ } return SCPE_OK; } if (dptr->debflags == NULL) /* must have table */ return SCPE_ARG; while (*cptr) { cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */ for (dep = dptr->debflags; dep->name != NULL; dep++) { if (strcmp (dep->name, gbuf) == 0) { /* match? */ if (flag) dptr->dctrl = dptr->dctrl | dep->mask; else dptr->dctrl = dptr->dctrl & ~dep->mask; break; } } /* end for */ if (dep->mask == 0) /* no match? */ return SCPE_ARG; } /* end while */ return SCPE_OK; } /* Show command */ t_stat show_cmd (int32 flag, char *cptr) { t_stat r; cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r); /* get sw, ofile */ if (!cptr) /* error? */ return r; if (sim_ofile) { /* output file? */ r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */ fclose (sim_ofile); } else { r = show_cmd_fi (stdout, flag, cptr); /* no, stdout, log */ if (sim_log) show_cmd_fi (sim_log, flag, cptr); } return r; } t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr) { int32 lvl; char gbuf[CBUFSIZE], *cvptr; DEVICE *dptr; UNIT *uptr; MTAB *mptr; SHTAB *shtb, *shptr; static SHTAB show_glob_tab[] = { { "CONFIGURATION", &show_config, 0 }, { "DEVICES", &show_config, 1 }, { "QUEUE", &show_queue, 0 }, { "TIME", &show_time, 0 }, { "MODIFIERS", &show_mod_names, 0 }, { "NAMES", &show_log_names, 0 }, { "VERSION", &show_version, 1 }, { "CONSOLE", &sim_show_console, 0 }, { "BREAK", &show_break, 0 }, { "LOG", &sim_show_log, 0 }, /* deprecated */ { "TELNET", &sim_show_telnet, 0 }, /* deprecated */ { "DEBUG", &sim_show_debug, 0 }, /* deprecated */ { "THROTTLE", &sim_show_throt, 0 }, { NULL, NULL, 0 } }; static SHTAB show_dev_tab[] = { { "RADIX", &show_dev_radix, 0 }, { "DEBUG", &show_dev_debug, 0 }, { "MODIFIERS", &show_dev_modifiers, 0 }, { "NAMES", &show_dev_logicals, 0 }, { NULL, NULL, 0 } }; static SHTAB show_unit_tab[] = { { NULL, NULL, 0 } }; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (shptr = find_shtab (show_glob_tab, gbuf)) /* global? */ return shptr->action (ofile, NULL, NULL, shptr->arg, cptr); if (dptr = find_dev (gbuf)) { /* device match? */ uptr = dptr->units; /* first unit */ shtb = show_dev_tab; /* global table */ lvl = MTAB_VDV; /* device match */ } else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */ if (uptr == NULL) /* invalid unit */ return SCPE_NXUN; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; shtb = show_unit_tab; /* global table */ lvl = MTAB_VUN; /* unit match */ } else return SCPE_NXDEV; /* no match */ if (*cptr == 0) { /* now eol? */ return (lvl == MTAB_VDV)? show_device (ofile, dptr, 0): show_unit (ofile, dptr, uptr, -1); } if (dptr->modifiers == NULL) /* any modifiers? */ return SCPE_NOPARAM; while (*cptr != 0) { /* do all mods */ cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ if (cvptr = strchr (gbuf, '=')) /* = value? */ *cvptr++ = 0; for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { if (((mptr->mask & MTAB_XTD)? /* right level? */ (mptr->mask & lvl): (MTAB_VUN & lvl)) && ((mptr->disp && mptr->pstring && /* named disp? */ (MATCH_CMD (gbuf, mptr->pstring) == 0)) // || // ((mptr->mask & MTAB_VAL) && /* named value? */ // mptr->mstring && // (MATCH_CMD (gbuf, mptr->mstring) == 0))) )) { if (cvptr && !(mptr->mask & MTAB_SHP)) return SCPE_ARG; show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1); break; } /* end if */ } /* end for */ if (mptr->mask == 0) { /* no match? */ if (shptr = find_shtab (shtb, gbuf)) /* global match? */ shptr->action (ofile, dptr, uptr, shptr->arg, cptr); else return SCPE_ARG; } /* end if */ } /* end while */ return SCPE_OK; } SHTAB *find_shtab (SHTAB *tab, char *gbuf) { for (; tab->name != NULL; tab++) { if (MATCH_CMD (gbuf, tab->name) == 0) return tab; } return NULL; } /* Show device and unit */ t_stat show_device (FILE *st, DEVICE *dptr, int32 flag) { uint32 j, udbl, ucnt; UNIT *uptr; fprintf (st, "%s", sim_dname (dptr)); /* print dev name */ if (qdisable (dptr)) { /* disabled? */ fprintf (st, ", disabled\n"); return SCPE_OK; } for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */ uptr = dptr->units + j; if (uptr->flags & UNIT_DISABLE) udbl++; if (!(uptr->flags & UNIT_DIS)) ucnt++; } show_all_mods (st, dptr, dptr->units, MTAB_VDV); /* show dev mods */ if (dptr->numunits == 0) fprintf (st, "\n"); else { if (udbl && (ucnt == 0)) fprintf (st, ", all units disabled\n"); else if (ucnt > 1) fprintf (st, ", %d units\n", ucnt); else if (flag) fprintf (st, "\n"); } if (flag) /* dev only? */ return SCPE_OK; for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ uptr = dptr->units + j; if ((uptr->flags & UNIT_DIS) == 0) show_unit (st, dptr, uptr, ucnt); } return SCPE_OK; } t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) { int32 u = uptr - dptr->units; if (flag > 1) fprintf (st, " %s%d", sim_dname (dptr), u); else if (flag < 0) fprintf (st, "%s%d", sim_dname (dptr), u); if (uptr->flags & UNIT_FIX) { fprintf (st, ", "); fprint_capac (st, dptr, uptr); } if (uptr->flags & UNIT_ATT) { fprintf (st, ", attached to %s", uptr->filename); if (uptr->flags & UNIT_RO) fprintf (st, ", read only"); } else if (uptr->flags & UNIT_ATTABLE) fprintf (st, ", not attached"); show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */ fprintf (st, "\n"); return SCPE_OK; } void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr) { t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000; t_addr mval = kval * kval; t_addr psize = uptr->capac; char scale, width; if ((dptr->dwidth / dptr->aincr) > 8) width = 'W'; else width = 'B'; if (uptr->capac < (kval * 10)) scale = 0; else if (uptr->capac < (mval * 10)) { scale = 'K'; psize = psize / kval; } else { scale = 'M'; psize = psize / mval; } fprint_val (st, (t_value) psize, 10, T_ADDR_W, PV_LEFT); if (scale) fputc (scale, st); fputc (width, st); return; } /* Show processors */ t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH, vdelt = SIM_DELTA; if (cptr && (*cptr != 0)) return SCPE_2MARG; fprintf (st, "%s simulator V%d.%d-%d", sim_name, vmaj, vmin, vpat); if (vdelt) fprintf (st, "(%d)", vdelt); if (flag) fprintf (st, " [%s, %s, %s]", sim_si64, sim_sa64, sim_snet); fprintf (st, "\n"); return SCPE_OK; } t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) { int32 i; DEVICE *dptr; if (cptr && (*cptr != 0)) return SCPE_2MARG; fprintf (st, "%s simulator configuration\n\n", sim_name); for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_device (st, dptr, flag); return SCPE_OK; } t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) { int32 i; DEVICE *dptr; if (cptr && (*cptr != 0)) return SCPE_2MARG; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_dev_logicals (st, dptr, NULL, 1, cptr); return SCPE_OK; } t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (dptr->lname) fprintf (st, "%s -> %s\n", dptr->lname, dptr->name); else if (!flag) fputs ("no logical name assigned\n", st); return SCPE_OK; } t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) { DEVICE *dptr; UNIT *uptr; int32 accum; if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_clock_queue == NULL) { fprintf (st, "%s event queue empty, time = %.0f\n", sim_name, sim_time); return SCPE_OK; } fprintf (st, "%s event queue status, time = %.0f\n", sim_name, sim_time); accum = 0; for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { if (uptr == &sim_step_unit) fprintf (st, " Step timer"); else if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1) fprintf (st, " unit %d", (int32) (uptr - dptr->units)); } else fprintf (st, " Unknown"); fprintf (st, " at %d\n", accum + uptr->time); accum = accum + uptr->time; } return SCPE_OK; } t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; fprintf (st, "Time:\t%.0f\n", sim_time); return SCPE_OK; } t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { t_stat r; if (cptr && (*cptr != 0)) r = ssh_break (st, cptr, 1); /* more? */ else r = sim_brk_showall (st, sim_switches); return r; } t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { fprintf (st, "Radix=%d\n", dptr->dradix); return SCPE_OK; } t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { int32 any = 0; DEBTAB *dep; if (dptr->flags & DEV_DEBUG) { if (dptr->dctrl == 0) fputs ("Debugging disabled", st); else if (dptr->debflags == NULL) fputs ("Debugging enabled", st); else { fputs ("Debug=", st); for (dep = dptr->debflags; dep->name != NULL; dep++) { if (dptr->dctrl & dep->mask) { if (any) fputc (';', st); fputs (dep->name, st); any = 1; } } } fputc ('\n', st); return SCPE_OK; } else return SCPE_NOFNC; } /* Show modifiers */ t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) { int32 i; DEVICE *dptr; if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_dev_modifiers (st, dptr, NULL, flag, cptr); return SCPE_OK; } t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { int any, enb; MTAB *mptr; DEBTAB *dep; any = enb = 0; if (dptr->modifiers) { for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { if (mptr->mstring) { if (strcmp (mptr->mstring, "ENABLED") == 0) enb = 1; if (any++) fprintf (st, ", %s", mptr->mstring); else fprintf (st, "%s\t%s", sim_dname (dptr), mptr->mstring); } } } if (dptr->flags & DEV_DEBUG) { if (any++) fprintf (st, ", DEBUG, NODEBUG"); else fprintf (st, "%s\tDEBUG, NODEBUG", sim_dname (dptr)); } if (!enb && (dptr->flags & DEV_DISABLE)) { if (any++) fprintf (st, ", ENABLED, DISABLED"); else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr)); } if (any) fprintf (st, "\n"); if ((dptr->flags & DEV_DEBUG) && dptr->debflags) { fprintf (st, "%s\tDEBUG=", sim_dname (dptr)); for (dep = dptr->debflags; dep->name != NULL; dep++) fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ","), dep->name); fprintf (st, "\n"); } return SCPE_OK; } t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) { MTAB *mptr; if (dptr->modifiers == NULL) return SCPE_OK; for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { if (mptr->pstring && ((mptr->mask & MTAB_XTD)? ((mptr->mask & flag) && !(mptr->mask & MTAB_NMO)): ((MTAB_VUN & flag) && ((uptr->flags & mptr->mask) == mptr->match)))) { fputs (", ", st); show_one_mod (st, dptr, uptr, mptr, NULL, 0); } } return SCPE_OK; } t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag) { //t_value val; if (mptr->disp) mptr->disp (st, uptr, mptr->match, cptr? cptr: mptr->desc); //else if ((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_VAL)) { // REG *rptr = (REG *) mptr->desc; // fprintf (st, "%s=", mptr->pstring); // val = get_rval (rptr, 0); // fprint_val (st, val, rptr->radix, rptr->width, // rptr->flags & REG_FMT); // } else fputs (mptr->pstring, st); if (flag && !((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_NMO))) fputc ('\n', st); return SCPE_OK; } /* Breakpoint commands */ t_stat brk_cmd (int32 flg, char *cptr) { GET_SWITCHES (cptr); /* get switches */ return ssh_break (NULL, cptr, flg); /* call common code */ } t_stat ssh_break (FILE *st, char *cptr, int32 flg) { char gbuf[CBUFSIZE], *tptr, *t1ptr, *aptr; DEVICE *dptr = sim_dflt_dev; UNIT *uptr = dptr->units; t_stat r; t_addr lo, hi, max = uptr->capac - 1; int32 cnt; if (sim_brk_types == 0) return SCPE_NOFNC; if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR; if (aptr = strchr (cptr, ';')) { /* ;action? */ if (flg != SSH_ST) /* only on SET */ return SCPE_ARG; *aptr++ = 0; /* separate strings */ } if (*cptr == 0) { /* no argument? */ lo = (t_addr) get_rval (sim_PC, 0); /* use PC */ return ssh_break_one (st, flg, lo, 0, aptr); } while (*cptr) { cptr = get_glyph (cptr, gbuf, ','); tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0); if (tptr == NULL) return SCPE_ARG; if (*tptr == '[') { cnt = (int32) strtotv (tptr + 1, &t1ptr, 10); if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST)) return SCPE_ARG; tptr = t1ptr + 1; } else cnt = 0; if (*tptr != 0) return SCPE_ARG; if ((lo == 0) && (hi == max)) { if (flg == SSH_CL) sim_brk_clrall (sim_switches); else if (flg == SSH_SH) sim_brk_showall (st, sim_switches); else return SCPE_ARG; } else { for ( ; lo <= hi; lo = lo + 1) { r = ssh_break_one (st, flg, lo, cnt, aptr); if (r != SCPE_OK) return r; } } } return SCPE_OK; } t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr) { switch (flg) { case SSH_ST: return sim_brk_set (lo, sim_switches, cnt, aptr); break; case SSH_CL: return sim_brk_clr (lo, sim_switches); break; case SSH_SH: return sim_brk_show (st, lo, sim_switches); break; default: return SCPE_ARG; } } /* Reset command and routines re[set] reset all devices re[set] all reset all devices re[set] device reset specific device */ t_stat reset_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEVICE *dptr; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* reset(cr) */ return (reset_all (0)); cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; if (strcmp (gbuf, "ALL") == 0) return (reset_all (0)); dptr = find_dev (gbuf); /* locate device */ if (dptr == NULL) /* found it? */ return SCPE_NXDEV; if (dptr->reset != NULL) return dptr->reset (dptr); else return SCPE_OK; } /* Reset devices start..end Inputs: start = number of starting device Outputs: status = error status */ t_stat reset_all (uint32 start) { DEVICE *dptr; uint32 i; t_stat reason; for (i = 0; i < start; i++) { if (sim_devices[i] == NULL) return SCPE_IERR; } for (i = start; (dptr = sim_devices[i]) != NULL; i++) { if (dptr->reset != NULL) { reason = dptr->reset (dptr); if (reason != SCPE_OK) return reason; } } return SCPE_OK; } /* Reset to powerup state Inputs: start = number of starting device Outputs: status = error status */ t_stat reset_all_p (uint32 start) { t_stat r; int32 old_sw = sim_switches; sim_switches = SWMASK ('P'); r = reset_all (start); sim_switches = old_sw; return r; } /* Load and dump commands lo[ad] filename {arg} load specified file du[mp] filename {arg} dump to specified file */ t_stat load_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; FILE *loadfile; t_stat reason; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ loadfile = sim_fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */ if (loadfile == NULL) return SCPE_OPENERR; GET_SWITCHES (cptr); /* get switches */ reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */ fclose (loadfile); return reason; } /* Attach command at[tach] unit file attach specified unit to file */ t_stat attach_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEVICE *dptr; UNIT *uptr; t_stat r; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* now eol? */ return SCPE_2FARG; dptr = find_unit (gbuf, &uptr); /* locate unit */ if (dptr == NULL) /* found dev? */ return SCPE_NXDEV; if (uptr == NULL) /* valid unit? */ return SCPE_NXUN; if (uptr->flags & UNIT_ATT) { /* already attached? */ r = scp_detach_unit (dptr, uptr); /* detach it */ if (r != SCPE_OK) /* error? */ return r; } sim_trim_endspc (cptr); /* trim trailing spc */ return scp_attach_unit (dptr, uptr, cptr); /* attach */ } /* Call device-specific or file-oriented attach unit routine */ t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr) { if (dptr->attach != NULL) /* device routine? */ return dptr->attach (uptr, cptr); /* call it */ return attach_unit (uptr, cptr); /* no, std routine */ } /* Attach unit to file */ t_stat attach_unit (UNIT *uptr, char *cptr) { DEVICE *dptr; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ return SCPE_NOATT; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; if (dptr->flags & DEV_RAWONLY) /* raw mode only? */ return SCPE_NOFNC; uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */ if (uptr->filename == NULL) return SCPE_MEM; strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ if (sim_switches & SWMASK ('R')) { /* read only? */ if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ return attach_err (uptr, SCPE_NORO); /* no, error */ uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ if (uptr->fileref == NULL) /* open fail? */ return attach_err (uptr, SCPE_OPENERR); /* yes, error */ uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr)); } else { /* normal */ uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */ if (uptr->fileref == NULL) { /* open fail? */ if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ return attach_err (uptr, SCPE_NORO); /* no error */ uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ if (uptr->fileref == NULL) /* open fail? */ return attach_err (uptr, SCPE_OPENERR); /* yes, error */ uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr)); } else { /* doesn't exist */ if (sim_switches & SWMASK ('E')) /* must exist? */ return attach_err (uptr, SCPE_OPENERR); /* yes, error */ uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */ if (uptr->fileref == NULL) /* open fail? */ return attach_err (uptr, SCPE_OPENERR); /* yes, error */ if (!sim_quiet) printf ("%s: creating new file\n", sim_dname (dptr)); } } /* end if null */ } /* end else */ if (uptr->flags & UNIT_BUFABLE) { /* buffer? */ uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */ if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */ uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */ if (uptr->filebuf == NULL) /* no buffer? */ return attach_err (uptr, SCPE_MEM); /* error */ if (!sim_quiet) printf ("%s: buffering file in memory\n", sim_dname (dptr)); uptr->hwmark = sim_fread (uptr->filebuf, /* read file */ SZ_D (dptr), cap, uptr->fileref); uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */ } uptr->flags = uptr->flags | UNIT_ATT; uptr->pos = 0; return SCPE_OK; } t_stat attach_err (UNIT *uptr, t_stat stat) { free (uptr->filename); uptr->filename = NULL; return stat; } /* Detach command det[ach] all detach all units det[ach] unit detach specified unit */ t_stat detach_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEVICE *dptr; UNIT *uptr; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; if (strcmp (gbuf, "ALL") == 0) return (detach_all (0, FALSE)); dptr = find_unit (gbuf, &uptr); /* locate unit */ if (dptr == NULL) /* found dev? */ return SCPE_NXDEV; if (uptr == NULL) /* valid unit? */ return SCPE_NXUN; return scp_detach_unit (dptr, uptr); /* detach */ } /* Detach devices start..end Inputs: start = number of starting device shutdown = TRUE if simulator shutting down Outputs: status = error status Note that during shutdown, detach routines for non-attachable devices will be called. These routines can implement simulator shutdown. Error returns during shutdown are ignored. */ t_stat detach_all (int32 start, t_bool shutdown) { uint32 i, j; DEVICE *dptr; UNIT *uptr; t_stat r; if ((start < 0) || (start > 1)) return SCPE_IERR; for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ uptr = (dptr->units) + j; if ((uptr->flags & UNIT_ATT) || /* attached? */ (shutdown && dptr->detach && /* shutdown, spec rtn, */ !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */ r = scp_detach_unit (dptr, uptr); /* detach unit */ if ((r != SCPE_OK) && !shutdown) /* error and not shutting down? */ return r; /* bail out now with error status */ } } } return SCPE_OK; } /* Call device-specific or file-oriented detach unit routine */ t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr) { if (dptr->detach != NULL) /* device routine? */ return dptr->detach (uptr); return detach_unit (uptr); /* no, standard */ } /* Detach unit from file */ t_stat detach_unit (UNIT *uptr) { DEVICE *dptr; if (uptr == NULL) return SCPE_IERR; if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */ return SCPE_NOATT; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK; if (uptr->flags & UNIT_BUF) { uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr; if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { if (!sim_quiet) printf ("%s: writing buffer to file\n", sim_dname (dptr)); rewind (uptr->fileref); sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref); if (ferror (uptr->fileref)) perror ("I/O error"); } if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */ free (uptr->filebuf); /* free buf */ uptr->filebuf = NULL; } uptr->flags = uptr->flags & ~UNIT_BUF; } uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO); free (uptr->filename); uptr->filename = NULL; if (fclose (uptr->fileref) == EOF) return SCPE_IOERR; return SCPE_OK; } /* Assign command as[sign] device name assign logical name to device */ t_stat assign_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEVICE *dptr; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* now eol? */ return SCPE_2FARG; dptr = find_dev (gbuf); /* locate device */ if (dptr == NULL) /* found dev? */ return SCPE_NXDEV; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* must be eol */ return SCPE_2MARG; if (find_dev (gbuf)) /* name in use */ return SCPE_ARG; deassign_device (dptr); /* release current */ return assign_device (dptr, gbuf); } t_stat assign_device (DEVICE *dptr, char *cptr) { dptr->lname = (char *) calloc (CBUFSIZE, sizeof (char)); if (dptr->lname == NULL) return SCPE_MEM; strncpy (dptr->lname, cptr, CBUFSIZE); return SCPE_OK; } /* Deassign command dea[ssign] device deassign logical name */ t_stat deassign_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; DEVICE *dptr; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; dptr = find_dev (gbuf); /* locate device */ if (dptr == NULL) /* found dev? */ return SCPE_NXDEV; return deassign_device (dptr); } t_stat deassign_device (DEVICE *dptr) { if (dptr->lname) free (dptr->lname); dptr->lname = NULL; return SCPE_OK; } /* Get device display name */ char *sim_dname (DEVICE *dptr) { return (dptr->lname? dptr->lname: dptr->name); } /* Save command sa[ve] filename save state to specified file */ t_stat save_cmd (int32 flag, char *cptr) { FILE *sfile; t_stat r; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; sim_trim_endspc (cptr); if ((sfile = sim_fopen (cptr, "wb")) == NULL) return SCPE_OPENERR; r = sim_save (sfile); fclose (sfile); return r; } t_stat sim_save (FILE *sfile) { void *mbuf; int32 l, t; uint32 i, j; t_addr k, high; t_value val; t_stat r; t_bool zeroflg; size_t sz; DEVICE *dptr; UNIT *uptr; REG *rptr; #define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile) fprintf (sfile, "%s\n%s\n%s\n%s\n%s\n%.0f\n", save_vercur, /* [V2.5] save format */ sim_name, /* sim name */ sim_si64, sim_sa64, sim_snet, /* [V3.5] options */ sim_time); /* [V3.2] sim time */ WRITE_I (sim_rtime); /* [V2.6] sim rel time */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ fputs (dptr->name, sfile); /* device name */ fputc ('\n', sfile); if (dptr->lname) /* [V3.0] logical name */ fputs (dptr->lname, sfile); fputc ('\n', sfile); WRITE_I (dptr->flags); /* [V2.10] flags */ for (j = 0; j < dptr->numunits; j++) { uptr = dptr->units + j; t = sim_is_active (uptr); WRITE_I (j); /* unit number */ WRITE_I (t); /* activation time */ WRITE_I (uptr->u3); /* unit specific */ WRITE_I (uptr->u4); WRITE_I (uptr->u5); /* [V3.0] more unit */ WRITE_I (uptr->u6); WRITE_I (uptr->flags); /* [V2.10] flags */ WRITE_I (uptr->capac); /* [V3.5] capacity */ if (uptr->flags & UNIT_ATT) fputs (uptr->filename, sfile); fputc ('\n', sfile); if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) && (dptr->examine != NULL) && ((high = uptr->capac) != 0)) { /* memory-like unit? */ WRITE_I (high); /* [V2.5] write size */ sz = SZ_D (dptr); if ((mbuf = calloc (SRBSIZ, sz)) == NULL) { fclose (sfile); return SCPE_MEM; } for (k = 0; k < high; ) { /* loop thru mem */ zeroflg = TRUE; for (l = 0; (l < SRBSIZ) && (k < high); l++, k = k + (dptr->aincr)) { /* check for 0 block */ r = dptr->examine (&val, k, uptr, SIM_SW_REST); if (r != SCPE_OK) return r; if (val) zeroflg = FALSE; SZ_STORE (sz, val, mbuf, l); } /* end for l */ if (zeroflg) { /* all zero's? */ l = -l; /* invert block count */ WRITE_I (l); /* write only count */ } else { WRITE_I (l); /* block count */ sim_fwrite (mbuf, sz, l, sfile); } } /* end for k */ free (mbuf); /* dealloc buffer */ } /* end if mem */ else { /* no memory */ high = 0; /* write 0 */ WRITE_I (high); } /* end else mem */ } /* end unit loop */ t = -1; /* end units */ WRITE_I (t); /* write marker */ for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */ (rptr->name != NULL); rptr++) { fputs (rptr->name, sfile); /* name */ fputc ('\n', sfile); WRITE_I (rptr->depth); /* [V2.10] depth */ for (j = 0; j < rptr->depth; j++) { /* loop thru values */ val = get_rval (rptr, j); /* get value */ WRITE_I (val); /* store */ } } fputc ('\n', sfile); /* end registers */ } fputc ('\n', sfile); /* end devices */ return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */ } /* Restore command re[store] filename restore state from specified file */ t_stat restore_cmd (int32 flag, char *cptr) { FILE *rfile; t_stat r; GET_SWITCHES (cptr); /* get switches */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; sim_trim_endspc (cptr); if ((rfile = sim_fopen (cptr, "rb")) == NULL) return SCPE_OPENERR; r = sim_rest (rfile); fclose (rfile); return r; } t_stat sim_rest (FILE *rfile) { char buf[CBUFSIZE]; void *mbuf; int32 j, blkcnt, limit, unitno, time, flg; uint32 us, depth; t_addr k, high, old_capac; t_value val, mask; t_stat r; size_t sz; t_bool v35, v32; DEVICE *dptr; UNIT *uptr; REG *rptr; #define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \ return SCPE_IOERR; #define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \ return SCPE_IOERR; READ_S (buf); /* [V2.5+] read version */ v35 = v32 = FALSE; if (strcmp (buf, save_vercur) == 0) /* version 3.5? */ v35 = v32 = TRUE; else if (strcmp (buf, save_ver32) == 0) /* version 3.2? */ v32 = TRUE; else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */ printf ("Invalid file version: %s\n", buf); return SCPE_INCOMP; } READ_S (buf); /* read sim name */ if (strcmp (buf, sim_name)) { /* name match? */ printf ("Wrong system type: %s\n", buf); return SCPE_INCOMP; } if (v35) { /* [V3.5+] options */ READ_S (buf); /* integer size */ if (strcmp (buf, sim_si64) != 0) { printf ("Incompatible integer size, save file = %s\n", buf); return SCPE_INCOMP; } READ_S (buf); /* address size */ if (strcmp (buf, sim_sa64) != 0) { printf ("Incompatible address size, save file = %s\n", buf); return SCPE_INCOMP; } READ_S (buf); /* Ethernet */ } if (v32) { /* [V3.2+] time as string */ READ_S (buf); sscanf (buf, "%lf", &sim_time); } else READ_I (sim_time); /* sim time */ READ_I (sim_rtime); /* [V2.6+] sim rel time */ for ( ;; ) { /* device loop */ READ_S (buf); /* read device name */ if (buf[0] == 0) /* last? */ break; if ((dptr = find_dev (buf)) == NULL) { /* locate device */ printf ("Invalid device name: %s\n", buf); return SCPE_INCOMP; } READ_S (buf); /* [V3.0+] logical name */ deassign_device (dptr); /* delete old name */ if ((buf[0] != 0) && ((r = assign_device (dptr, buf)) != SCPE_OK)) return r; READ_I (flg); /* [V2.10+] ctlr flags */ if (!v32) flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) | (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */ dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */ (flg & DEV_RFLAGS); for ( ;; ) { /* unit loop */ sim_switches = SIM_SW_REST; /* flag rstr, clr RO */ READ_I (unitno); /* unit number */ if (unitno < 0) /* end units? */ break; if ((uint32) unitno >= dptr->numunits) { /* too big? */ printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno); return SCPE_INCOMP; } READ_I (time); /* event time */ uptr = (dptr->units) + unitno; sim_cancel (uptr); if (time > 0) sim_activate (uptr, time - 1); READ_I (uptr->u3); /* device specific */ READ_I (uptr->u4); READ_I (uptr->u5); /* [V3.0+] more dev spec */ READ_I (uptr->u6); READ_I (flg); /* [V2.10+] unit flags */ old_capac = uptr->capac; /* save current capacity */ if (v35) { /* [V3.5+] capacity */ READ_I (uptr->capac); } if (!v32) flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) | (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */ uptr->flags = (uptr->flags & ~UNIT_RFLAGS) | (flg & UNIT_RFLAGS); /* restore */ READ_S (buf); /* attached file */ if ((uptr->flags & UNIT_ATT) && /* unit currently attached? */ !(dptr->flags & DEV_NET)) { /* and not a net device? */ r = scp_detach_unit (dptr, uptr); /* detach it */ if (r != SCPE_OK) return r; } if ((buf[0] != '\0') && /* unit to be reattached? */ !(dptr->flags & DEV_NET) && /* and not a net device? */ ((uptr->flags & UNIT_ATTABLE) || /* and unit is attachable */ (dptr->attach != NULL))) { /* or VM attach routine provided? */ uptr->flags = uptr->flags & ~UNIT_DIS; /* ensure device is enabled */ if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */ sim_switches |= SWMASK ('R'); /* RO attach */ r = scp_attach_unit (dptr, uptr, buf); /* reattach unit */ if (r != SCPE_OK) return r; } READ_I (high); /* memory capacity */ if (high > 0) { /* [V2.5+] any memory? */ if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) || (dptr->deposit == NULL)) { printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno); return SCPE_INCOMP; } if (high != old_capac) { /* size change? */ uptr->capac = old_capac; /* temp restore old */ if ((dptr->flags & DEV_DYNM) && ((dptr->msize == NULL) || (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) { printf ("Can't change memory size: %s%d\n", sim_dname (dptr), unitno); return SCPE_INCOMP; } uptr->capac = high; /* new memory size */ printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno); fprint_capac (stdout, dptr, uptr); printf ("\n"); } sz = SZ_D (dptr); /* allocate buffer */ if ((mbuf = calloc (SRBSIZ, sz)) == NULL) return SCPE_MEM; for (k = 0; k < high; ) { /* loop thru mem */ READ_I (blkcnt); /* block count */ if (blkcnt < 0) /* compressed? */ limit = -blkcnt; else limit = sim_fread (mbuf, sz, blkcnt, rfile); if (limit <= 0) /* invalid or err? */ return SCPE_IOERR; for (j = 0; j < limit; j++, k = k + (dptr->aincr)) { if (blkcnt < 0) /* compressed? */ val = 0; else SZ_LOAD (sz, val, mbuf, j); /* saved value */ r = dptr->deposit (val, k, uptr, SIM_SW_REST); if (r != SCPE_OK) return r; } /* end for j */ } /* end for k */ free (mbuf); /* dealloc buffer */ } /* end if high */ } /* end unit loop */ for ( ;; ) { /* register loop */ READ_S (buf); /* read reg name */ if (buf[0] == 0) /* last? */ break; READ_I (depth); /* [V2.10+] depth */ if ((rptr = find_reg (buf, NULL, dptr)) == NULL) { printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf); for (us = 0; us < depth; us++) { /* skip values */ READ_I (val); } continue; } if (depth != rptr->depth) /* [V2.10+] mismatch? */ printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n", sim_dname (dptr), buf, depth, rptr->depth); mask = width_mask[rptr->width]; /* get mask */ for (us = 0; us < depth; us++) { /* loop thru values */ READ_I (val); /* read value */ if (val > mask) /* value ok? */ printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf); else if (us < rptr->depth) /* in range? */ put_rval (rptr, us, val); } } } /* end device loop */ return SCPE_OK; } /* Run, go, cont, step commands ru[n] [new PC] reset and start simulation go [new PC] start simulation co[nt] start simulation s[tep] [step limit] start simulation for 'limit' instructions b[oot] device bootstrap from device and start simulation */ t_stat run_cmd (int32 flag, char *cptr) { char *tptr, gbuf[CBUFSIZE]; uint32 i, j; int32 unitno; t_value pcv; t_stat r; DEVICE *dptr; UNIT *uptr; void int_handler (int signal); GET_SWITCHES (cptr); /* get switches */ sim_step = 0; if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */ if (*cptr != 0) { /* argument? */ cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* should be end */ return SCPE_2MARG; if (sim_vm_parse_addr) /* address parser? */ pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr); else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */ if ((tptr == gbuf) || (*tptr != 0) || /* error? */ (pcv > width_mask[sim_PC->width])) return SCPE_ARG; put_rval (sim_PC, 0, pcv); } if ((flag == RU_RUN) && /* run? */ ((r = run_boot_prep ()) != SCPE_OK)) /* reset sim */ return r; } else if (flag == RU_STEP) { /* step */ if (*cptr != 0) { /* argument? */ cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* should be end */ return SCPE_2MARG; sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r); if ((r != SCPE_OK) || (sim_step <= 0)) /* error? */ return SCPE_ARG; } else sim_step = 1; } else if (flag == RU_BOOT) { /* boot */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ if (*cptr != 0) /* should be end */ return SCPE_2MARG; dptr = find_unit (gbuf, &uptr); /* locate unit */ if (dptr == NULL) /* found dev? */ return SCPE_NXDEV; if (uptr == NULL) /* valid unit? */ return SCPE_NXUN; if (dptr->boot == NULL) /* can it boot? */ return SCPE_NOFNC; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */ !(uptr->flags & UNIT_ATT)) return SCPE_UNATT; unitno = (int32) (uptr - dptr->units); /* recover unit# */ if ((r = run_boot_prep ()) != SCPE_OK) /* reset sim */ return r; if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */ return r; } else if (flag != RU_CONT) /* must be cont */ return SCPE_IERR; for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */ for (j = 0; j < dptr->numunits; j++) { /* seq devices */ uptr = dptr->units + j; if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ)) sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); } } stop_cpu = 0; if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */ return SCPE_SIGERR; } if (sim_ttrun () != SCPE_OK) { /* set console mode */ sim_ttcmd (); return SCPE_TTYERR; } if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */ sim_ttcmd (); return r; } if (sim_step) /* set step timer */ sim_activate (&sim_step_unit, sim_step); sim_throt_sched (); /* set throttle */ sim_is_running = 1; /* flag running */ sim_brk_clract (); /* defang actions */ sim_rtcn_init_all (); /* re-init clocks */ r = sim_instr(); sim_is_running = 0; /* flag idle */ sim_ttcmd (); /* restore console */ signal (SIGINT, SIG_DFL); /* cancel WRU */ sim_cancel (&sim_step_unit); /* cancel step timer */ sim_throt_cancel (); /* cancel throttle */ if (sim_clock_queue != NULL) { /* update sim time */ UPDATE_SIM_TIME (sim_clock_queue->time); } else { UPDATE_SIM_TIME (noqueue_time); } if (sim_log) /* flush console log */ fflush (sim_log); if (sim_deb) /* flush debug log */ fflush (sim_deb); for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */ for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */ uptr = dptr->units + j; if ((uptr->flags & UNIT_ATT) && /* attached, */ !(uptr->flags & UNIT_BUF) && /* not buffered, */ (uptr->fileref) && /* real file, */ !(uptr->flags & UNIT_RAW) && /* not raw, */ !(uptr->flags & UNIT_RO)) /* not read only? */ fflush (uptr->fileref); } } #if defined (VMS) printf ("\n"); #endif fprint_stopped (stdout, r); /* print msg */ if (sim_log) /* log if enabled */ fprint_stopped (sim_log, r); return SCPE_OK; } /* Common setup for RUN or BOOT */ t_stat run_boot_prep (void) { sim_interval = 0; /* reset queue */ sim_time = sim_rtime = 0; noqueue_time = 0; sim_clock_queue = NULL; return reset_all (0); } /* Print stopped message */ void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr) { int32 i; t_stat r = 0; t_addr k; t_value pcval; if (v >= SCPE_BASE) fprintf (st, "\n%s, %s: ", scp_error_messages[v - SCPE_BASE], pc->name); else fprintf (st, "\n%s, %s: ", sim_stop_messages[v], pc->name); pcval = get_rval (pc, 0); if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, (t_addr) pcval); else fprint_val (st, pcval, pc->radix, pc->width, pc->flags & REG_FMT); if ((dptr != NULL) && (dptr->examine != NULL)) { for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) { if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V'))) != SCPE_OK) break; } if ((r == SCPE_OK) || (i > 0)) { fprintf (st, " ("); if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0) fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO); fprintf (st, ")"); } } fprintf (st, "\n"); return; } void fprint_stopped (FILE *st, t_stat v) { fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev); return; } /* Unit service for step timeout, originally scheduled by STEP n command Return step timeout SCP code, will cause simulation to stop */ t_stat step_svc (UNIT *uptr) { return SCPE_STEP; } /* Cancel scheduled step service */ t_stat sim_cancel_step (void) { return sim_cancel (&sim_step_unit); } /* Signal handler for ^C signal - set stop simulation flag */ void int_handler (int sig) { stop_cpu = 1; return; } /* Examine/deposit commands ex[amine] [modifiers] list examine de[posit] [modifiers] list val deposit ie[xamine] [modifiers] list interactive examine id[eposit] [modifiers] list interactive deposit modifiers @filename output file -letter(s) switches devname'n device name and unit number [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification list list of addresses and registers addr[:addr|-addr] address range ALL all addresses register[:register|-register] register range STATE all registers */ t_stat exdep_cmd (int32 flag, char *cptr) { char gbuf[CBUFSIZE], *gptr, *tptr; int32 opt; t_addr low, high; t_stat reason; DEVICE *tdptr; REG *lowr, *highr; FILE *ofile; opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */ if (flag == EX_E) /* extra for EX */ opt = opt | CMD_OPT_OF; cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */ if (!cptr) /* error? */ return reason; if (*cptr == 0) /* must be more */ return SCPE_2FARG; if (sim_dfunit == NULL) /* got a unit? */ return SCPE_NXUN; cptr = get_glyph (cptr, gbuf, 0); /* get list */ if ((flag == EX_D) && (*cptr == 0)) /* deposit needs more */ return SCPE_2FARG; ofile = sim_ofile? sim_ofile: stdout; /* no ofile? use stdout */ for (gptr = gbuf, reason = SCPE_OK; (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) { tdptr = sim_dfdev; /* working dptr */ if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) { tptr = gptr + strlen ("STATE"); if (*tptr && (*tptr++ != ',')) return SCPE_ARG; if ((lowr = sim_dfdev->registers) == NULL) return SCPE_NXREG; for (highr = lowr; highr->name != NULL; highr++) ; sim_switches = sim_switches | SIM_SW_HIDE; reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, lowr, --highr, 0, 0); continue; } if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */ (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */ (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) { low = high = 0; if ((*tptr == '-') || (*tptr == ':')) { highr = find_reg (tptr + 1, &tptr, tdptr); if (highr == NULL) return SCPE_NXREG; } else { highr = lowr; if (*tptr == '[') { if (lowr->depth <= 1) return SCPE_ARG; tptr = get_range (NULL, tptr + 1, &low, &high, 10, lowr->depth - 1, ']'); if (tptr == NULL) return SCPE_ARG; } } if (*tptr && (*tptr++ != ',')) return SCPE_ARG; reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, lowr, highr, (uint32) low, (uint32) high); continue; } tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix, (((sim_dfunit->capac == 0) || (flag == EX_E))? 0: sim_dfunit->capac - sim_dfdev->aincr), 0); if (tptr == NULL) return SCPE_ARG; if (*tptr && (*tptr++ != ',')) return SCPE_ARG; reason = exdep_addr_loop (ofile, sim_schptr, flag, cptr, low, high, sim_dfdev, sim_dfunit); } /* end for */ if (sim_ofile) /* close output file */ fclose (sim_ofile); return reason; } /* Loop controllers for examine/deposit exdep_reg_loop examine/deposit range of registers exdep_addr_loop examine/deposit range of addresses */ t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, REG *lowr, REG *highr, uint32 lows, uint32 highs) { t_stat reason; uint32 idx; t_value val; REG *rptr; if ((lowr == NULL) || (highr == NULL)) return SCPE_IERR; if (lowr > highr) return SCPE_ARG; for (rptr = lowr; rptr <= highr; rptr++) { if ((sim_switches & SIM_SW_HIDE) && (rptr->flags & REG_HIDDEN)) continue; for (idx = lows; idx <= highs; idx++) { if (idx >= rptr->depth) return SCPE_SUB; val = get_rval (rptr, idx); if (schptr && !test_search (val, schptr)) continue; if (flag != EX_D) { reason = ex_reg (ofile, val, flag, rptr, idx); if (reason != SCPE_OK) return reason; if (sim_log && (ofile == stdout)) ex_reg (sim_log, val, flag, rptr, idx); } if (flag != EX_E) { reason = dep_reg (flag, cptr, rptr, idx); if (reason != SCPE_OK) return reason; } } } return SCPE_OK; } t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr) { t_addr i, mask; t_stat reason; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; mask = (t_addr) width_mask[dptr->awidth]; if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG; for (i = low; i <= high; ) { /* all paths must incr!! */ reason = get_aval (i, dptr, uptr); /* get data */ if (reason != SCPE_OK) /* return if error */ return reason; if (schptr && !test_search (sim_eval[0], schptr)) i = i + dptr->aincr; /* sch fails, incr */ else { /* no sch or success */ if (flag != EX_D) { /* ex, ie, or id? */ reason = ex_addr (ofile, flag, i, dptr, uptr); if (reason > SCPE_OK) return reason; if (sim_log && (ofile == stdout)) ex_addr (sim_log, flag, i, dptr, uptr); } else reason = 1 - dptr->aincr; /* no, dflt incr */ if (flag != EX_E) { /* ie, id, or d? */ reason = dep_addr (flag, cptr, i, dptr, uptr, reason); if (reason > SCPE_OK) return reason; } i = i + (1 - reason); /* incr */ } } return SCPE_OK; } /* Examine register routine Inputs: ofile = output stream val = current register value flag = type of ex/mod command (ex, iex, idep) rptr = pointer to register descriptor idx = index Outputs: return = error status */ t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx) { int32 rdx; if (rptr == NULL) return SCPE_IERR; if (rptr->depth > 1) fprintf (ofile, "%s[%d]:\t", rptr->name, idx); else fprintf (ofile, "%s:\t", rptr->name); if (!(flag & EX_E)) return SCPE_OK; GET_RADIX (rdx, rptr->radix); if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr) sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val); else if (!(rptr->flags & REG_VMIO) || (fprint_sym (ofile, rdx, &val, NULL, sim_switches | SIM_SW_REG) > 0)) fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT); if (flag & EX_I) fprintf (ofile, "\t"); else fprintf (ofile, "\n"); return SCPE_OK; } /* Get register value Inputs: rptr = pointer to register descriptor idx = index Outputs: return = register value */ t_value get_rval (REG *rptr, uint32 idx) { size_t sz; t_value val; UNIT *uptr; sz = SZ_R (rptr); if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) { idx = idx + rptr->qptr; if (idx >= rptr->depth) idx = idx - rptr->depth; } if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) { uptr = ((UNIT *) rptr->loc) + idx; #if defined (USE_INT64) if (sz <= sizeof (uint32)) val = *((uint32 *) uptr); else val = *((t_uint64 *) uptr); #else val = *((uint32 *) uptr); #endif } else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && (sz == sizeof (uint8))) val = *(((uint8 *) rptr->loc) + idx); else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && (sz == sizeof (uint16))) val = *(((uint16 *) rptr->loc) + idx); #if defined (USE_INT64) else if (sz <= sizeof (uint32)) val = *(((uint32 *) rptr->loc) + idx); else val = *(((t_uint64 *) rptr->loc) + idx); #else else val = *(((uint32 *) rptr->loc) + idx); #endif val = (val >> rptr->offset) & width_mask[rptr->width]; return val; } /* Deposit register routine Inputs: flag = type of deposit (normal/interactive) cptr = pointer to input string rptr = pointer to register descriptor idx = index Outputs: return = error status */ t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx) { t_stat r; t_value val, mask; int32 rdx; char *tptr, gbuf[CBUFSIZE]; if ((cptr == NULL) || (rptr == NULL)) return SCPE_IERR; if (rptr->flags & REG_RO) return SCPE_RO; if (flag & EX_I) { cptr = read_line (gbuf, CBUFSIZE, stdin); if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); if (cptr == NULL) /* force exit */ return 1; if (*cptr == 0) /* success */ return SCPE_OK; } mask = width_mask[rptr->width]; GET_RADIX (rdx, rptr->radix); if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr) { /* address form? */ val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr); if ((tptr == cptr) || (*tptr != 0) || (val > mask)) return SCPE_ARG; } else if (!(rptr->flags & REG_VMIO) || /* dont use sym? */ (parse_sym (cptr, rdx, NULL, &val, sim_switches | SIM_SW_REG) > SCPE_OK)) { val = get_uint (cptr, rdx, mask, &r); if (r != SCPE_OK) return SCPE_ARG; } if ((rptr->flags & REG_NZ) && (val == 0)) return SCPE_ARG; put_rval (rptr, idx, val); return SCPE_OK; } /* Put register value Inputs: rptr = pointer to register descriptor idx = index val = new value mask = mask Outputs: none */ void put_rval (REG *rptr, uint32 idx, t_value val) { size_t sz; t_value mask; UNIT *uptr; #define PUT_RVAL(sz,rp,id,v,m) \ *(((sz *) rp->loc) + id) = \ (*(((sz *) rp->loc) + id) & \ ~((m) << (rp)->offset)) | ((v) << (rp)->offset) if (rptr == sim_PC) sim_brk_npc (0); sz = SZ_R (rptr); mask = width_mask[rptr->width]; if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) { idx = idx + rptr->qptr; if (idx >= rptr->depth) idx = idx - rptr->depth; } if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) { uptr = ((UNIT *) rptr->loc) + idx; #if defined (USE_INT64) if (sz <= sizeof (uint32)) *((uint32 *) uptr) = (*((uint32 *) uptr) & ~(((uint32) mask) << rptr->offset)) | (((uint32) val) << rptr->offset); else *((t_uint64 *) uptr) = (*((t_uint64 *) uptr) & ~(mask << rptr->offset)) | (val << rptr->offset); #else *((uint32 *) uptr) = (*((uint32 *) uptr) & ~(((uint32) mask) << rptr->offset)) | (((uint32) val) << rptr->offset); #endif } else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && (sz == sizeof (uint8))) PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask); else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && (sz == sizeof (uint16))) PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask); #if defined (USE_INT64) else if (sz <= sizeof (uint32)) PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask); else PUT_RVAL (t_uint64, rptr, idx, val, mask); #else else PUT_RVAL (uint32, rptr, idx, val, mask); #endif return; } /* Examine address routine Inputs: (sim_eval is an implicit argument) ofile = output stream flag = type of ex/mod command (ex, iex, idep) addr = address to examine dptr = pointer to device uptr = pointer to unit Outputs: return = if > 0, error status if <= 0,-number of extra addr units retired */ t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr) { t_stat reason; int32 rdx; if (sim_vm_fprint_addr) sim_vm_fprint_addr (ofile, dptr, addr); else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT); fprintf (ofile, ":\t"); if (!(flag & EX_E)) return (1 - dptr->aincr); GET_RADIX (rdx, dptr->dradix); if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) { fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO); reason = 1 - dptr->aincr; } if (flag & EX_I) fprintf (ofile, "\t"); else fprintf (ofile, "\n"); return reason; } /* Get address routine Inputs: flag = type of ex/mod command (ex, iex, idep) addr = address to examine dptr = pointer to device uptr = pointer to unit Outputs: (sim_eval is an implicit output) return = error status */ t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr) { int32 i; t_value mask; t_addr j, loc; size_t sz; t_stat reason = SCPE_OK; if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR; mask = width_mask[dptr->dwidth]; for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) { if (dptr->examine != NULL) { reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches); if (reason != SCPE_OK) break; } else { if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; if (uptr->flags & UNIT_RAW) return SCPE_NOFNC; if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) { reason = SCPE_NXM; break; } sz = SZ_D (dptr); loc = j / dptr->aincr; if (uptr->flags & UNIT_BUF) { SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc); } else { sim_fseek (uptr->fileref, sz * loc, SEEK_SET); sim_fread (&sim_eval[i], sz, 1, uptr->fileref); if ((feof (uptr->fileref)) && !(uptr->flags & UNIT_FIX)) { reason = SCPE_EOF; break; } else if (ferror (uptr->fileref)) { clearerr (uptr->fileref); reason = SCPE_IOERR; break; } } } sim_eval[i] = sim_eval[i] & mask; } if ((reason != SCPE_OK) && (i == 0)) return reason; return SCPE_OK; } /* Deposit address routine Inputs: flag = type of deposit (normal/interactive) cptr = pointer to input string addr = address to examine dptr = pointer to device uptr = pointer to unit dfltinc = value to return on cr input Outputs: return = if > 0, error status if <= 0, -number of extra address units retired */ t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, UNIT *uptr, int32 dfltinc) { int32 i, count, rdx; t_addr j, loc; t_stat r, reason; t_value mask; size_t sz; char gbuf[CBUFSIZE]; if (dptr == NULL) return SCPE_IERR; if (flag & EX_I) { cptr = read_line (gbuf, CBUFSIZE, stdin); if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); if (cptr == NULL) /* force exit */ return 1; if (*cptr == 0) /* success */ return dfltinc; } if (uptr->flags & UNIT_RO) /* read only? */ return SCPE_RO; mask = width_mask[dptr->dwidth]; GET_RADIX (rdx, dptr->dradix); if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) { sim_eval[0] = get_uint (cptr, rdx, mask, &reason); if (reason != SCPE_OK) return reason; } count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr; for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) { sim_eval[i] = sim_eval[i] & mask; if (dptr->deposit != NULL) { r = dptr->deposit (sim_eval[i], j, uptr, sim_switches); if (r != SCPE_OK) return r; } else { if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; if (uptr->flags & UNIT_RAW) return SCPE_NOFNC; if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) return SCPE_NXM; sz = SZ_D (dptr); loc = j / dptr->aincr; if (uptr->flags & UNIT_BUF) { SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc); if (loc >= uptr->hwmark) uptr->hwmark = (uint32) loc + 1; } else { sim_fseek (uptr->fileref, sz * loc, SEEK_SET); sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref); if (ferror (uptr->fileref)) { clearerr (uptr->fileref); return SCPE_IOERR; } } } } return reason; } /* Evaluate command */ t_stat eval_cmd (int32 flg, char *cptr) { DEVICE *dptr = sim_dflt_dev; int32 i, rdx, a, lim; t_stat r; GET_SWITCHES (cptr); GET_RADIX (rdx, dptr->dradix); for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; if (*cptr == 0) return SCPE_2FARG; if ((r = parse_sym (cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) { sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r); if (r != SCPE_OK) return r; } lim = 1 - r; for (i = a = 0; a < lim; ) { printf ("%d:\t", a); if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0) r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); printf ("\n"); if (sim_log) { fprintf (sim_log, "%d\t", i); if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0) r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); fprintf (sim_log, "\n"); } if (r < 0) a = a + 1 - r; else a = a + dptr->aincr; i = a / dptr->aincr; } return SCPE_OK; } /* String processing routines read_line read line Inputs: cptr = pointer to buffer size = maximum size stream = pointer to input stream Outputs: optr = pointer to first non-blank character NULL if EOF */ char *read_line (char *cptr, int32 size, FILE *stream) { char *tptr; cptr = fgets (cptr, size, stream); /* get cmd line */ if (cptr == NULL) { clearerr (stream); /* clear error */ return NULL; /* ignore EOF */ } for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */ if ((*tptr == '\n') || (*tptr == '\r') || (tptr == (cptr + size - 1))) { /* str max length? */ *tptr = 0; /* terminate */ break; } } while (isspace (*cptr)) /* trim leading spc */ cptr++; if (*cptr == ';') *cptr = 0; /* ignore comment */ #if defined (HAVE_READLINE) add_history (cptr); #endif return cptr; } /* get_glyph get next glyph (force upper case) get_glyph_nc get next glyph (no conversion) get_glyph_gen get next glyph (general case) Inputs: iptr = pointer to input string optr = pointer to output string mchar = optional end of glyph character flag = TRUE for convert to upper case (_gen only) Outputs result = pointer to next character in input string */ char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc) { while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) { if (islower (*iptr) && uc) *optr = toupper (*iptr); else *optr = *iptr; iptr++; optr++; } *optr = 0; if (mchar && (*iptr == mchar)) /* skip terminator */ iptr++; while (isspace (*iptr)) /* absorb spaces */ iptr++; return iptr; } char *get_glyph (char *iptr, char *optr, char mchar) { return get_glyph_gen (iptr, optr, mchar, TRUE); } char *get_glyph_nc (char *iptr, char *optr, char mchar) { return get_glyph_gen (iptr, optr, mchar, FALSE); } /* Trim trailing spaces from a string Inputs: cptr = pointer to string Outputs: cptr = pointer to string */ char *sim_trim_endspc (char *cptr) { char *tptr; tptr = cptr + strlen (cptr); while ((--tptr >= cptr) && isspace (*tptr)) *tptr = 0; return cptr; } /* get_yn yes/no question Inputs: cptr = pointer to question deflt = default answer Outputs: result = true if yes, false if no */ t_stat get_yn (char *ques, t_stat deflt) { char cbuf[CBUFSIZE], *cptr; printf ("%s ", ques); cptr = read_line (cbuf, CBUFSIZE, stdin); if ((cptr == NULL) || (*cptr == 0)) return deflt; if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE; return FALSE; } /* get_uint unsigned number Inputs: cptr = pointer to input string radix = input radix max = maximum acceptable value *status = pointer to error status Outputs: val = value */ t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status) { t_value val; char *tptr; *status = SCPE_OK; val = strtotv (cptr, &tptr, radix); if ((cptr == tptr) || (val > max)) *status = SCPE_ARG; else { while (isspace (*tptr)) tptr++; if (*tptr != 0) *status = SCPE_ARG; } return val; } /* get_range range specification Inputs: dptr = pointer to device (NULL if none) cptr = pointer to input string *lo = pointer to low result *hi = pointer to high result aradix = radix max = default high value term = terminating character, 0 if none Outputs: tptr = input pointer after processing NULL if error */ char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, uint32 rdx, t_addr max, char term) { char *tptr; if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */ tptr = cptr + strlen ("ALL"); *lo = 0; *hi = max; } else { if (dptr && sim_vm_parse_addr) /* get low */ *lo = sim_vm_parse_addr (dptr, cptr, &tptr); else *lo = (t_addr) strtotv (cptr, &tptr, rdx); if (cptr == tptr) /* error? */ return NULL; if ((*tptr == '-') || (*tptr == ':')) { /* range? */ cptr = tptr + 1; if (dptr && sim_vm_parse_addr) /* get high */ *hi = sim_vm_parse_addr (dptr, cptr, &tptr); else *hi = (t_addr) strtotv (cptr, &tptr, rdx); if (cptr == tptr) return NULL; if (*lo > *hi) return NULL; } else if (*tptr == '/') { /* relative? */ cptr = tptr + 1; *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */ if ((cptr == tptr) || (*hi == 0)) return NULL; *hi = *lo + *hi - 1; } else *hi = *lo; } if (term && (*tptr++ != term)) return NULL; return tptr; } /* get_ipaddr IP address:port Inputs: cptr = pointer to input string Outputs: ipa = pointer to IP address (may be NULL), 0 = none ipp = pointer to IP port (may be NULL), 0 = none result = status */ t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp) { char gbuf[CBUFSIZE]; char *addrp, *portp, *octetp; uint32 i, addr, port, octet; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; strncpy (gbuf, cptr, CBUFSIZE); addrp = gbuf; /* default addr */ if (portp = strchr (gbuf, ':')) /* x:y? split */ *portp++ = 0; else if (strchr (gbuf, '.')) /* x.y...? */ portp = NULL; else { portp = gbuf; /* port only */ addrp = NULL; /* no addr */ } if (portp) { /* port string? */ if (ipp == NULL) /* not wanted? */ return SCPE_ARG; port = (int32) get_uint (portp, 10, 65535, &r); if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG; } else port = 0; if (addrp) { /* addr string? */ if (ipa == NULL) /* not wanted? */ return SCPE_ARG; for (i = addr = 0; i < 4; i++) { /* four octets */ octetp = strchr (addrp, '.'); /* find octet end */ if (octetp != NULL) /* split string */ *octetp++ = 0; else if (i < 3) /* except last */ return SCPE_ARG; octet = (int32) get_uint (addrp, 10, 255, &r); if (r != SCPE_OK) return SCPE_ARG; addr = (addr << 8) | octet; addrp = octetp; } if (((addr & 0377) == 0) || ((addr & 0377) == 255)) return SCPE_ARG; } else addr = 0; if (ipp) /* return req values */ *ipp = port; if (ipa) *ipa = addr; return SCPE_OK; } /* Find_device find device matching input string Inputs: cptr = pointer to input string Outputs: result = pointer to device */ DEVICE *find_dev (char *cptr) { int32 i; DEVICE *dptr; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { if ((strcmp (cptr, dptr->name) == 0) || (dptr->lname && (strcmp (cptr, dptr->lname) == 0))) return dptr; } return NULL; } /* Find_unit find unit matching input string Inputs: cptr = pointer to input string uptr = pointer to unit pointer Outputs: result = pointer to device (null if no dev) *iptr = pointer to unit (null if nx unit) */ DEVICE *find_unit (char *cptr, UNIT **uptr) { uint32 i, u; char *nptr, *tptr; t_stat r; DEVICE *dptr; if (uptr == NULL) /* arg error? */ return NULL; if (dptr = find_dev (cptr)) { /* exact match? */ if (qdisable (dptr)) /* disabled? */ return NULL; *uptr = dptr->units; /* unit 0 */ return dptr; } for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */ if (dptr->numunits && /* any units? */ (((nptr = dptr->name) && (strncmp (cptr, nptr, strlen (nptr)) == 0)) || ((nptr = dptr->lname) && (strncmp (cptr, nptr, strlen (nptr)) == 0)))) { tptr = cptr + strlen (nptr); if (isdigit (*tptr)) { if (qdisable (dptr)) /* disabled? */ return NULL; u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r); if (r != SCPE_OK) /* error? */ *uptr = NULL; else *uptr = dptr->units + u; return dptr; } } } return NULL; } /* Find_dev_from_unit find device for unit Inputs: uptr = pointer to unit Outputs: result = pointer to device */ DEVICE *find_dev_from_unit (UNIT *uptr) { DEVICE *dptr; uint32 i, j; if (uptr == NULL) return NULL; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { for (j = 0; j < dptr->numunits; j++) { if (uptr == (dptr->units + j)) return dptr; } } return NULL; } /* Test for disabled device */ t_bool qdisable (DEVICE *dptr) { return (dptr->flags & DEV_DIS? TRUE: FALSE); } /* find_reg_glob find globally unique register Inputs: cptr = pointer to input string optr = pointer to output pointer (can be null) gdptr = pointer to global device Outputs: result = pointer to register, NULL if error *optr = pointer to next character in input string *gdptr = pointer to device where found */ REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr) { int32 i; DEVICE *dptr; REG *rptr, *srptr = NULL; for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */ if (dptr->flags & DEV_DIS) /* skip disabled */ continue; if (rptr = find_reg (cptr, optr, dptr)) { /* found? */ if (srptr) /* ambig? err */ return NULL; srptr = rptr; /* save reg */ *gdptr = dptr; /* save unit */ } } return srptr; } /* find_reg find register matching input string Inputs: cptr = pointer to input string optr = pointer to output pointer (can be null) dptr = pointer to device Outputs: result = pointer to register, NULL if error *optr = pointer to next character in input string */ REG *find_reg (char *cptr, char **optr, DEVICE *dptr) { char *tptr; REG *rptr; uint32 slnt; if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL)) return NULL; tptr = cptr; do { tptr++; } while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_')); slnt = tptr - cptr; for (rptr = dptr->registers; rptr->name != NULL; rptr++) { if ((slnt == strlen (rptr->name)) && (strncmp (cptr, rptr->name, slnt) == 0)) { if (optr != NULL) *optr = tptr; return rptr; } } return NULL; } /* get_switches get switches from input string Inputs: cptr = pointer to input string Outputs: sw = switch bit mask 0 if no switches, -1 if error */ int32 get_switches (char *cptr) { int32 sw; if (*cptr != '-') return 0; sw = 0; for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) { if (isalpha (*cptr) == 0) return -1; sw = sw | SWMASK (toupper (*cptr)); } return sw; } /* get_sim_sw accumulate sim_switches Inputs: cptr = pointer to input string Outputs: ptr = pointer to first non-string glyph NULL if error */ char *get_sim_sw (char *cptr) { int32 lsw; char gbuf[CBUFSIZE]; while (*cptr == '-') { /* while switches */ cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */ lsw = get_switches (gbuf); /* parse */ if (lsw <= 0) /* invalid? */ return NULL; sim_switches = sim_switches | lsw; /* accumulate */ } return cptr; } /* get_sim_opt get simulator command options Inputs: opt = command options cptr = pointer to input string Outputs: ptr = pointer to next glypsh, NULL if error *stat = error status */ char *get_sim_opt (int32 opt, char *cptr, t_stat *st) { int32 t; char *svptr, gbuf[CBUFSIZE]; DEVICE *tdptr; UNIT *tuptr; sim_switches = 0; /* no switches */ sim_ofile = NULL; /* no output file */ sim_schptr = NULL; /* no search */ sim_stab.logic = SCH_OR; /* default search params */ sim_stab.boolop = SCH_GE; sim_stab.mask = 0; sim_stab.comp = 0; sim_dfdev = sim_dflt_dev; sim_dfunit = sim_dfdev->units; sim_opt_out = 0; /* no options yet */ *st = SCPE_OK; while (*cptr) { /* loop through modifiers */ svptr = cptr; /* save current position */ if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */ if (sim_ofile) { /* already got one? */ fclose (sim_ofile); /* one per customer */ *st = SCPE_ARG; return NULL; } cptr = get_glyph_nc (cptr + 1, gbuf, 0); sim_ofile = sim_fopen (gbuf, "a"); /* open for append */ if (sim_ofile == NULL) { /* open failed? */ *st = SCPE_OPENERR; return NULL; } sim_opt_out |= CMD_OPT_OF; /* got output file */ continue; } cptr = get_glyph (cptr, gbuf, 0); if ((t = get_switches (gbuf)) != 0) { /* try for switches */ if (t < 0) { /* err if bad switch */ *st = SCPE_INVSW; return NULL; } sim_switches = sim_switches | t; /* or in new switches */ } else if ((opt & CMD_OPT_SCH) && /* if allowed, */ get_search (gbuf, sim_dfdev->dradix, &sim_stab)) { /* try for search */ sim_schptr = &sim_stab; /* set search */ sim_opt_out |= CMD_OPT_SCH; /* got search */ } else if ((opt & CMD_OPT_DFT) && /* default allowed? */ ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */ (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */ (tuptr != NULL)) { sim_dfdev = tdptr; /* set as default */ sim_dfunit = tuptr; sim_opt_out |= CMD_OPT_DFT; /* got default */ } else return svptr; /* not rec, break out */ } return cptr; } /* Match file extension Inputs: fnam = file name ext = extension, without period Outputs: cp = pointer to final '.' if match, NULL if not */ char *match_ext (char *fnam, char *ext) { char *pptr, *fptr, *eptr; if ((fnam == NULL) || (ext == NULL)) /* bad arguments? */ return NULL; pptr = strrchr (fnam, '.'); /* find last . */ if (pptr) { /* any? */ for (fptr = pptr + 1, eptr = ext; /* match characters */ #if defined (VMS) /* VMS: stop at ; or null */ (*fptr != 0) && (*fptr != ';'); #else *fptr != 0; /* others: stop at null */ #endif fptr++, eptr++) { if (toupper (*fptr) != toupper (*eptr)) return NULL; } if (*eptr != 0) /* ext exhausted? */ return NULL; } return pptr; } /* Get search specification Inputs: cptr = pointer to input string radix = radix for numbers schptr = pointer to search table Outputs: return = NULL if error schptr if valid search specification */ SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr) { int32 c, logop, cmpop; t_value logval, cmpval; char *sptr, *tptr; const char logstr[] = "|&^", cmpstr[] = "=!><"; logval = cmpval = 0; if (*cptr == 0) /* check for clause */ return NULL; for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */ if (sptr = strchr (logstr, c)) { /* check for mask */ logop = sptr - logstr; logval = strtotv (cptr, &tptr, radix); if (cptr == tptr) return NULL; cptr = tptr; } else if (sptr = strchr (cmpstr, c)) { /* check for boolop */ cmpop = sptr - cmpstr; if (*cptr == '=') { cmpop = cmpop + strlen (cmpstr); cptr++; } cmpval = strtotv (cptr, &tptr, radix); if (cptr == tptr) return NULL; cptr = tptr; } else return NULL; } /* end for */ if (logop >= 0) { schptr->logic = logop; schptr->mask = logval; } if (cmpop >= 0) { schptr->boolop = cmpop; schptr->comp = cmpval; } return schptr; } /* Test value against search specification Inputs: val = value to test schptr = pointer to search table Outputs: return = 1 if value passes search criteria, 0 if not */ int32 test_search (t_value val, SCHTAB *schptr) { if (schptr == NULL) return 0; switch (schptr->logic) { /* case on logical */ case SCH_OR: val = val | schptr->mask; break; case SCH_AND: val = val & schptr->mask; break; case SCH_XOR: val = val ^ schptr->mask; break; } switch (schptr->boolop) { /* case on comparison */ case SCH_E: case SCH_EE: return (val == schptr->comp); case SCH_N: case SCH_NE: return (val != schptr->comp); case SCH_G: return (val > schptr->comp); case SCH_GE: return (val >= schptr->comp); case SCH_L: return (val < schptr->comp); case SCH_LE: return (val <= schptr->comp); } return 0; } /* Radix independent input/output package strtotv - general radix input routine Inputs: inptr = string to convert endptr = pointer to first unconverted character radix = radix for input Outputs: value = converted value On an error, the endptr will equal the inptr. */ t_value strtotv (char *inptr, char **endptr, uint32 radix) { int32 nodigit; t_value val; uint32 c, digit; *endptr = inptr; /* assume fails */ if ((radix < 2) || (radix > 36)) return 0; while (isspace (*inptr)) /* bypass white space */ inptr++; val = 0; nodigit = 1; for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */ if (islower (c)) c = toupper (c); if (isdigit (c)) /* digit? */ digit = c - (uint32) '0'; else if (radix <= 10) /* stop if not expected */ break; else digit = c + 10 - (uint32) 'A'; /* convert letter */ if (digit >= radix) /* valid in radix? */ return 0; val = (val * radix) + digit; /* add to value */ nodigit = 0; } if (nodigit) /* no digits? */ return 0; *endptr = inptr; /* result pointer */ return val; } /* fprint_val - general radix printing routine Inputs: stream = stream designator val = value to print radix = radix to print width = width to print format = leading zeroes format Outputs: status = error status */ t_stat fprint_val (FILE *stream, t_value val, uint32 radix, uint32 width, uint32 format) { #define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value))) t_value owtest, wtest; int32 d, digit, ndigits; char dbuf[MAX_WIDTH + 1]; for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' '; dbuf[MAX_WIDTH] = 0; d = MAX_WIDTH; do { d = d - 1; digit = (int32) (val % radix); val = val / radix; dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10); } while ((d > 0) && (val != 0)); if (format != PV_LEFT) { wtest = owtest = radix; ndigits = 1; while ((wtest < width_mask[width]) && (wtest >= owtest)) { owtest = wtest; wtest = wtest * radix; ndigits = ndigits + 1; } if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits; } if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR; return SCPE_OK; } /* Event queue package sim_activate add entry to event queue sim_cancel remove entry from event queue sim_process_event process entries on event queue sim_is_active see if entry is on event queue sim_atime return absolute time for an entry sim_gtime return global time sim_qcount return event queue entry count Asynchronous events are set up by queueing a unit data structure to the event queue with a timeout (in simulator units, relative to the current time). Each simulator 'times' these events by counting down interval counter sim_interval. When this reaches zero the simulator calls sim_process_event to process the event and to see if further events need to be processed, or sim_interval reset to count the next one. The event queue is maintained in clock order; entry timeouts are RELATIVE to the time in the previous entry. sim_process_event - process event Inputs: none Outputs: reason = reason code returned by any event processor, or 0 (SCPE_OK) if no exceptions */ t_stat sim_process_event (void) { UNIT *uptr; t_stat reason; if (stop_cpu) /* stop CPU? */ return SCPE_STOP; if (sim_clock_queue == NULL) { /* queue empty? */ UPDATE_SIM_TIME (noqueue_time); /* update sim time */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ return SCPE_OK; } UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */ do { uptr = sim_clock_queue; /* get first */ sim_clock_queue = uptr->next; /* remove first */ uptr->next = NULL; /* hygiene */ uptr->time = 0; if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; if (uptr->action != NULL) reason = uptr->action (uptr); else reason = SCPE_OK; } while ((reason == SCPE_OK) && (sim_interval == 0)); /* Empty queue forces sim_interval != 0 */ return reason; } /* sim_activate - activate (queue) event Inputs: uptr = pointer to unit event_time = relative timeout Outputs: reason = result (SCPE_OK if ok) */ t_stat sim_activate (UNIT *uptr, int32 event_time) { UNIT *cptr, *prvptr; int32 accum; if (event_time < 0) return SCPE_IERR; if (sim_is_active (uptr)) /* already active? */ return SCPE_OK; if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } else { /* update sim time */ UPDATE_SIM_TIME (sim_clock_queue->time); } prvptr = NULL; accum = 0; for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { if (event_time < (accum + cptr->time)) break; accum = accum + cptr->time; prvptr = cptr; } if (prvptr == NULL) { /* insert at head */ cptr = uptr->next = sim_clock_queue; sim_clock_queue = uptr; } else { cptr = uptr->next = prvptr->next; /* insert at prvptr */ prvptr->next = uptr; } uptr->time = event_time - accum; if (cptr != NULL) cptr->time = cptr->time - uptr->time; sim_interval = sim_clock_queue->time; return SCPE_OK; } /* sim_activate_abs - activate (queue) event even if event already scheduled Inputs: uptr = pointer to unit event_time = relative timeout Outputs: reason = result (SCPE_OK if ok) */ t_stat sim_activate_abs (UNIT *uptr, int32 event_time) { sim_cancel (uptr); return sim_activate (uptr, event_time); } /* sim_cancel - cancel (dequeue) event Inputs: uptr = pointer to unit Outputs: reason = result (SCPE_OK if ok) */ t_stat sim_cancel (UNIT *uptr) { UNIT *cptr, *nptr; if (sim_clock_queue == NULL) return SCPE_OK; UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */ nptr = NULL; if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr->next; else { for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { if (cptr->next == uptr) { nptr = cptr->next = uptr->next; break; /* end queue scan */ } } } if (nptr != NULL) nptr->time = nptr->time + uptr->time; uptr->next = NULL; /* hygiene */ uptr->time = 0; if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; return SCPE_OK; } /* sim_is_active - test for entry in queue, return activation time Inputs: uptr = pointer to unit Outputs: result = absolute activation time + 1, 0 if inactive */ int32 sim_is_active (UNIT *uptr) { UNIT *cptr; int32 accum; accum = 0; for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { if (cptr == sim_clock_queue) { if (sim_interval > 0) accum = accum + sim_interval; } else accum = accum + cptr->time; if (cptr == uptr) return accum + 1; } return 0; } /* sim_gtime - return global time sim_grtime - return global time with rollover Inputs: none Outputs: time = global time */ double sim_gtime (void) { if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } else { UPDATE_SIM_TIME (sim_clock_queue->time); } return sim_time; } uint32 sim_grtime (void) { if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } else { UPDATE_SIM_TIME (sim_clock_queue->time); } return sim_rtime; } /* sim_qcount - return queue entry count Inputs: none Outputs: count = number of entries on the queue */ int32 sim_qcount (void) { int32 cnt; UNIT *uptr; cnt = 0; for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) cnt++; return cnt; } /* Breakpoint package. This module replaces the VM-implemented one instruction breakpoint capability. Breakpoints are stored in table sim_brk_tab, which is ordered by address for efficient binary searching. A breakpoint consists of a four entry structure: addr address of the breakpoint type types of breakpoints set on the address a bit mask representing letters A-Z cnt number of iterations before breakp is taken action pointer command string to be executed when break is taken sim_brk_summ is a summary of the types of breakpoints that are currently set (it is the bitwise OR of all the type fields). A simulator need only check for a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum. The package contains the following public routines: sim_brk_init initialize sim_brk_set set breakpoint sim_brk_clr clear breakpoint sim_brk_clrall clear all breakpoints sim_brk_show show breakpoint sim_brk_showall show all breakpoints sim_brk_test test for breakpoint sim_brk_npc PC has been changed sim_brk_getact get next action sim_brk_clract clear pending actions Initialize breakpoint system. */ t_stat sim_brk_init (void) { sim_brk_lnt = SIM_BRK_INILNT; sim_brk_tab = (BRKTAB *) calloc (sim_brk_lnt, sizeof (BRKTAB)); if (sim_brk_tab == NULL) return SCPE_MEM; sim_brk_ent = sim_brk_ins = 0; sim_brk_act = NULL; sim_brk_npc (0); return SCPE_OK; } /* Search for a breakpoint in the sorted breakpoint table */ BRKTAB *sim_brk_fnd (t_addr loc) { int32 lo, hi, p; BRKTAB *bp; if (sim_brk_ent == 0) { /* table empty? */ sim_brk_ins = 0; /* insrt at head */ return NULL; /* sch fails */ } lo = 0; /* initial bounds */ hi = sim_brk_ent - 1; do { p = (lo + hi) >> 1; /* probe */ bp = sim_brk_tab + p; /* table addr */ if (loc == bp->addr) /* match? */ return bp; else if (loc < bp->addr) /* go down? p is upper */ hi = p - 1; else lo = p + 1; /* go up? p is lower */ } while (lo <= hi); if (loc < bp->addr) /* insrt before or */ sim_brk_ins = p; else sim_brk_ins = p + 1; /* after last sch */ return NULL; } /* Insert a breakpoint */ BRKTAB *sim_brk_new (t_addr loc) { int32 i, t; BRKTAB *bp, *newp; if (sim_brk_ins < 0) return NULL; if (sim_brk_ent >= sim_brk_lnt) { /* out of space? */ t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */ newp = (BRKTAB *) calloc (t, sizeof (BRKTAB)); /* new table */ if (newp == NULL) /* can't extend */ return NULL; for (i = 0; i < sim_brk_lnt; i++) /* copy table */ *(newp + i) = *(sim_brk_tab + i); free (sim_brk_tab); /* free old table */ sim_brk_tab = newp; /* new base, lnt */ sim_brk_lnt = t; } if (sim_brk_ins != sim_brk_ent) { /* move needed? */ for (bp = sim_brk_tab + sim_brk_ent; bp > sim_brk_tab + sim_brk_ins; bp--) *bp = *(bp - 1); } bp = sim_brk_tab + sim_brk_ins; bp->addr = loc; bp->typ = 0; bp->cnt = 0; bp->act = NULL; sim_brk_ent = sim_brk_ent + 1; return bp; } /* Set a breakpoint of type sw */ t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act) { BRKTAB *bp; if (sw == 0) sw = sim_brk_dflt; if ((sim_brk_types & sw) == 0) return SCPE_NOFNC; bp = sim_brk_fnd (loc); /* present? */ if (!bp) /* no, allocate */ bp = sim_brk_new (loc); if (!bp) /* still no? mem err */ return SCPE_MEM; bp->typ = sw; /* set type */ bp->cnt = ncnt; /* set count */ if ((bp->act != NULL) && (act != NULL)) { /* replace old action? */ free (bp->act); /* deallocate */ bp->act = NULL; /* now no action */ } if ((act != NULL) && (*act != 0)) { /* new action? */ char *newp = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc buf */ if (newp == NULL) /* mem err? */ return SCPE_MEM; strncpy (newp, act, CBUFSIZE); /* copy action */ bp->act = newp; /* set pointer */ } sim_brk_summ = sim_brk_summ | sw; return SCPE_OK; } /* Clear a breakpoint */ t_stat sim_brk_clr (t_addr loc, int32 sw) { BRKTAB *bp = sim_brk_fnd (loc); if (!bp) /* not there? ok */ return SCPE_OK; if (sw == 0) sw = SIM_BRK_ALLTYP; bp->typ = bp->typ & ~sw; if (bp->typ) /* clear all types? */ return SCPE_OK; if (bp->act != NULL) /* deallocate action */ free (bp->act); for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++) /* erase entry */ *bp = *(bp + 1); sim_brk_ent = sim_brk_ent - 1; /* decrement count */ sim_brk_summ = 0; /* recalc summary */ for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) sim_brk_summ = sim_brk_summ | bp->typ; return SCPE_OK; } /* Clear all breakpoints */ t_stat sim_brk_clrall (int32 sw) { BRKTAB *bp; if (sw == 0) sw = SIM_BRK_ALLTYP; for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); ) { if (bp->typ & sw) sim_brk_clr (bp->addr, sw); else bp++; } return SCPE_OK; } /* Show a breakpoint */ t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw) { BRKTAB *bp = sim_brk_fnd (loc); DEVICE *dptr; int32 i, any; if (sw == 0) sw = SIM_BRK_ALLTYP; if (!bp || (!(bp->typ & sw))) return SCPE_OK; dptr = sim_dflt_dev; if (dptr == NULL) return SCPE_OK; if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, loc); else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); fprintf (st, ":\t"); for (i = any = 0; i < 26; i++) { if ((bp->typ >> i) & 1) { if (any) fprintf (st, ", "); fputc (i + 'A', st); any = 1; } } if (bp->cnt > 0) fprintf (st, " [%d]", bp->cnt); if (bp->act != NULL) fprintf (st, "; %s", bp->act); fprintf (st, "\n"); return SCPE_OK; } /* Show all breakpoints */ t_stat sim_brk_showall (FILE *st, int32 sw) { BRKTAB *bp; if (sw == 0) sw = SIM_BRK_ALLTYP; for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) { if (bp->typ & sw) sim_brk_show (st, bp->addr, sw); } return SCPE_OK; } /* Test for breakpoint */ uint32 sim_brk_test (t_addr loc, uint32 btyp) { BRKTAB *bp; uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1); if ((bp = sim_brk_fnd (loc)) && (btyp & bp->typ)) { /* in table, type match? */ if ((sim_brk_pend[spc] && (loc == sim_brk_ploc[spc])) || /* previous location? */ (--bp->cnt > 0)) /* count > 0? */ return 0; bp->cnt = 0; /* reset count */ sim_brk_ploc[spc] = loc; /* save location */ sim_brk_pend[spc] = TRUE; /* don't do twice */ sim_brk_act = bp->act; /* set up actions */ return (btyp & bp->typ); } sim_brk_pend[spc] = FALSE; return 0; } /* Get next pending action, if any */ char *sim_brk_getact (char *buf, int32 size) { char *ep; size_t lnt; if (sim_brk_act == NULL) /* any action? */ return NULL; while (isspace (*sim_brk_act)) /* skip spaces */ sim_brk_act++; if (*sim_brk_act == 0) /* now empty? */ return (sim_brk_act = NULL); if (ep = strchr (sim_brk_act, ';')) { /* cmd delimiter? */ lnt = ep - sim_brk_act; /* cmd length */ memcpy (buf, sim_brk_act, lnt + 1); /* copy with ; */ buf[lnt] = 0; /* erase ; */ sim_brk_act = sim_brk_act + lnt + 1; /* adv ptr */ } else { strncpy (buf, sim_brk_act, size); /* copy action */ sim_brk_act = NULL; /* no more */ } return buf; } /* Clear pending actions */ void sim_brk_clract (void) { sim_brk_act = NULL; } /* New PC */ void sim_brk_npc (uint32 cnt) { uint32 i; if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC)) cnt = SIM_BKPT_N_SPC; for (i = 0; i < cnt; i++) { sim_brk_pend[i] = FALSE; sim_brk_ploc[i] = 0; } return; } /* Clear breakpoint space */ void sim_brk_clrspc (uint32 spc) { if (spc < SIM_BKPT_N_SPC) { sim_brk_pend[spc] = FALSE; sim_brk_ploc[spc] = 0; } return; } /* Debug printout routines, from Dave Hittner */ const char* debug_bstates = "01_^"; const char* debug_fmt = "DBG> %s %s: "; int32 debug_unterm = 0; /* Finds debug phrase matching bitmask from from device DEBTAB table */ static char* get_dbg_verb (uint32 dbits, DEVICE* dptr) { static char* debtab_none = "DEBTAB_ISNULL"; static char* debtab_nomatch = "DEBTAB_NOMATCH"; int32 offset = 0; if (dptr->debflags == 0) return debtab_none; /* Find matching words for bitmask */ while (dptr->debflags[offset].name && (offset < 32)) { if (dptr->debflags[offset].mask & dbits) return dptr->debflags[offset].name; offset++; } return debtab_nomatch; } /* Prints standard debug prefix unless previous call unterminated */ static void sim_debug_prefix (uint32 dbits, DEVICE* dptr) { if (!debug_unterm) { char* debug_type = get_dbg_verb (dbits, dptr); fprintf(sim_deb, debug_fmt, dptr->name, debug_type); } } /* Prints state of a register: bit translation + state (0,1,_,^) indicating the state and transition of the bit. States: 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */ void sim_debug_u16(uint32 dbits, DEVICE* dptr, const char* const* bitdefs, uint16 before, uint16 after, int terminate) { if (sim_deb && (dptr->dctrl & dbits)) { int32 i; sim_debug_prefix(dbits, dptr); /* print prefix if required */ for (i = 15; i >= 0; i--) { /* print xlation, transition */ int off = ((after >> i) & 1) + (((before ^ after) >> i) & 1) * 2; fprintf(sim_deb, "%s%c ", bitdefs[i], debug_bstates[off]); } if (terminate) fprintf(sim_deb, "\r\n"); debug_unterm = terminate ? 0 : 1; /* set unterm for next */ } } #if defined (_WIN32) #define vsnprintf _vsnprintf #endif #if defined (__DECC) && defined (__VMS) #define NO_vsnprintf #endif #if defined( NO_vsnprintf) #define STACKBUFSIZE 16384 #else #define STACKBUFSIZE 2048 #endif /* Inline debugging - will print debug message if debug file is set and the bitmask matches the current device debug options. Extra returns are added for un*x systems, since the output device is set into 'raw' mode when the cpu is booted, and the extra returns don't hurt any other systems. */ void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...) { if (sim_deb && (dptr->dctrl & dbits)) { char stackbuf[STACKBUFSIZE]; int32 bufsize = sizeof(stackbuf); char *buf = stackbuf; va_list arglist; int32 i, j, len; buf[bufsize-1] = '\0'; sim_debug_prefix(dbits, dptr); /* print prefix if required */ while (1) { /* format passed string, args */ va_start (arglist, fmt); #if defined(NO_vsnprintf) #if defined(HAS_vsprintf_void) /* Note, this could blow beyond the buffer, and we couldn't tell */ /* That is a limitation of the C runtime library available on this platform */ vsprintf (buf, fmt, arglist); for (len = 0; len < bufsize-1; len++) if (buf[len] == 0) break; #else len = vsprintf (buf, fmt, arglist); #endif /* HAS_vsprintf_void */ #else /* NO_vsnprintf */ #if defined(HAS_vsnprintf_void) vsnprintf (buf, bufsize-1, fmt, arglist); for (len = 0; len < bufsize-1; len++) if (buf[len] == 0) break; #else len = vsnprintf (buf, bufsize-1, fmt, arglist); #endif /* HAS_vsnprintf_void */ #endif /* NO_vsnprintf */ va_end (arglist); /* If it didn't fit into the buffer, then grow it and try again */ if ((len < 0) || (len >= bufsize-1)) { if (buf != stackbuf) free (buf); bufsize = bufsize * 2; buf = (char *) malloc (bufsize); if (buf == NULL) /* out of memory */ return; buf[bufsize-1] = '\0'; continue; } break; } /* Output the formatted data expanding newlines where they exist */ for (i = j = 0; i < len; ++i) { if ('\n' == buf[i]) { if (i > j) fwrite (&buf[j], 1, i-j, sim_deb); j = i; fputc('\r', sim_deb); } } if (i > j) fwrite (&buf[j], 1, i-j, sim_deb); /* Set unterminated flag for next time */ debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1; if (buf != stackbuf) free (buf); } return; } simh-3.8.1/build_mingw.bat0000644000175000017500000000102210032572272013624 0ustar vlmvlm@echo off rem Compile all of SIMH using MINGW make and gcc environment rem Individual simulator sources are in .\simulator_name rem Individual simulator executables are to .\bin 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;%path% if not exist BIN mkdir BIN gcc -v 1>NUL 2>NUL if ERRORLEVEL 1 echo "MinGW Environment Unavailable" mingw32-make WIN32=1 -f makefile %1 %2 %3 %4 simh-3.8.1/descrip.mms0000644000175000017500000013500411111156710013005 0ustar vlmvlm# DESCRIP.MMS # Written By: Robert Alan Byer / byer@mail.ourservers.net # Modified By: Mark Pizzolato / mark@infocomm.com # Norman Lastovica / norman.lastovica@oracle.com # # This MMS/MMK build script is used to compile the various simulators in # the SIMH package for OpenVMS using DEC C v6.0-001(AXP), v6.5-001(AXP), # HP C V7.2-001 (IA64) and v6.4-005(VAX). # # Notes: On VAX, the PDP-10 and Eclipse simulators will not be built # due to the fact that INT64 is required for that simulator. # # This build script will accept the following build options. # # ALL Just Build "Everything". # ALTAIR Just Build The MITS Altair. # ALTAIRZ80 Just Build The MITS Altair Z80. # ECLIPSE Just Build The Data General Eclipse. # GRI Just Build The GRI Corporation GRI-909. # LGP Just Build The Royal-McBee LGP-30. # H316 Just Build The Honewell 316/516. # HP2100 Just Build The Hewlett-Packard HP-2100. # I1401 Just Build The IBM 1401. # I1620 Just Build The IBM 1620. # IBM1130 Just Build The IBM 1130. # ID16 Just Build The Interdata 16-bit CPU. # ID32 Just Build The Interdata 32-bit CPU. # NOVA Just Build The Data General Nova. # PDP1 Just Build The DEC PDP-1. # PDP4 Just Build The DEC PDP-4. # PDP7 Just Build The DEC PDP-7. # PDP8 Just Build The DEC PDP-8. # PDP9 Just Build The DEC PDP-9. # PDP10 Just Build The DEC PDP-10. # PDP11 Just Build The DEC PDP-11. # PDP15 Just Build The DEC PDP-15. # S3 Just Build The IBM System 3. # SDS Just Build The SDS 940. # VAX Just Build The DEC VAX. # VAX780 Just Build The DEC VAX780. # CLEAN Will Clean Files Back To Base Kit. # # To build with debugging enabled (which will also enable traceback # information) use.. # # MMK/MACRO=(DEBUG=1) # # This will produce an executable named {Simulator}-{I64|VAX|AXP}-DBG.EXE # # Let's See If We Are Going To Build With DEBUG Enabled. Always compile # /DEBUG so that the traceback and debug information is always available # in the object files. CC_DEBUG = /DEBUG .IFDEF DEBUG LINK_DEBUG = /DEBUG/TRACEBACK CC_OPTIMIZE = /NOOPTIMIZE .IFDEF MMSALPHA ALPHA_OR_IA64 = 1 CC_FLAGS = /PREF=ALL ARCH = AXP-DBG CC_DEFS = "_LARGEFILE" .ENDIF .IFDEF MMSIA64 ALPHA_OR_IA64 = 1 CC_FLAGS = /PREF=ALL ARCH = I64-DBG CC_DEFS = "_LARGEFILE" .ENDIF .IFDEF MMSVAX ALPHA_OR_IA64 = 0 CC_FLAGS = $(CC_FLAGS) ARCH = VAX-DBG CC_DEFS = "__VAX" .ENDIF .ELSE LINK_DEBUG = /NODEBUG/NOTRACEBACK .IFDEF MMSALPHA ALPHA_OR_IA64 = 1 CC_OPTIMIZE = /OPT=(LEV=5)/ARCH=HOST CC_FLAGS = /PREF=ALL ARCH = AXP CC_DEFS = "_LARGEFILE" LINK_SECTION_BINDING = /SECTION_BINDING .ENDIF .IFDEF MMSIA64 ALPHA_OR_IA64 = 1 CC_OPTIMIZE = /OPT=(LEV=5) CC_FLAGS = /PREF=ALL ARCH = I64 CC_DEFS = "_LARGEFILE" .ENDIF .IFDEF MMSVAX ALPHA_OR_IA64 = 0 CC_OPTIMIZE = /OPTIMIZE CC_FLAGS = $(CC_FLAGS) ARCH = VAX CC_DEFS = "__VAX" .ENDIF .ENDIF # Define Our Compiler Flags & Define The Compile Command OUR_CC_FLAGS = $(CC_FLAGS)$(CC_DEBUG)$(CC_OPTIMIZE) \ /NEST=PRIMARY/NAME=(AS_IS,SHORT) CC = CC/DECC$(OUR_CC_FLAGS) # Define The BIN Directory Where The Executables Will Go. # Define Our Library Directory. # Define The platform specific Build Directory Where The Objects Will Go. # BIN_DIR = SYS$DISK:[.BIN] LIB_DIR = SYS$DISK:[.LIB] BLD_DIR = SYS$DISK:[.LIB.BLD-$(ARCH)] # Check To Make Sure We Have SYS$DISK:[.BIN] & SYS$DISK:[.LIB] Directory. # .FIRST @ IF (F$SEARCH("SYS$DISK:[]BIN.DIR").EQS."") THEN CREATE/DIRECTORY $(BIN_DIR) @ IF (F$SEARCH("SYS$DISK:[]LIB.DIR").EQS."") THEN CREATE/DIRECTORY $(LIB_DIR) @ IF (F$SEARCH("SYS$DISK:[.LIB]BLD-$(ARCH).DIR").EQS."") THEN CREATE/DIRECTORY $(BLD_DIR) @ IF (F$SEARCH("$(BLD_DIR)*.*").NES."") THEN DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.*;* @ IF "".NES."''CC'" THEN DELETE/SYMBOL/GLOBAL CC # Core SIMH File Definitions. # SIMH_DIR = SYS$DISK:[] SIMH_LIB = $(LIB_DIR)SIMH-$(ARCH).OLB SIMH_SOURCE = $(SIMH_DIR)SIM_CONSOLE.C,$(SIMH_DIR)SIM_SOCK.C,\ $(SIMH_DIR)SIM_TMXR.C,$(SIMH_DIR)SIM_ETHER.C,\ $(SIMH_DIR)SIM_TAPE.C,$(SIMH_DIR)SIM_FIO.C,\ $(SIMH_DIR)SIM_TIMER.C # VMS PCAP File Definitions. # PCAP_DIR = SYS$DISK:[.PCAP-VMS.PCAP-VCI] PCAP_LIB = $(LIB_DIR)PCAP-$(ARCH).OLB PCAP_SOURCE = \ $(PCAP_DIR)PCAPVCI.C,$(PCAP_DIR)VCMUTIL.C,\ $(PCAP_DIR)BPF_DUMP.C,$(PCAP_DIR)BPF_FILTER.C,\ $(PCAP_DIR)BPF_IMAGE.C,$(PCAP_DIR)ETHERENT.C,\ $(PCAP_DIR)FAD-GIFC.C,$(PCAP_DIR)GENCODE.C,\ $(PCAP_DIR)GRAMMAR.C,$(PCAP_DIR)INET.C,\ $(PCAP_DIR)NAMETOADDR.C,$(PCAP_DIR)OPTIMIZE.C,\ $(PCAP_DIR)PCAP.C,$(PCAP_DIR)SAVEFILE.C,\ $(PCAP_DIR)SCANNER.C,$(PCAP_DIR)SNPRINTF.C,\ $(PCAP_DIR)PCAP-VMS.C PCAP_VCMDIR = SYS$DISK:[.PCAP-VMS.PCAPVCM] PCAP_VCM_SOURCES = $(PCAP_VCMDIR)PCAPVCM.C,$(PCAP_VCMDIR)PCAPVCM_INIT.MAR,\ $(PCAP_VCMDIR)VCI_JACKET.MAR,$(PCAP_VCMDIR)VCMUTIL.C PCAP_VCI = SYS$COMMON:[SYS$LDR]PCAPVCM.EXE # PCAP is not available on OpenVMS VAX or IA64 right now # .IFDEF MMSALPHA PCAP_EXECLET = $(PCAP_VCI) PCAP_INC = ,$(PCAP_DIR) PCAP_LIBD = $(PCAP_LIB) PCAP_LIBR = ,$(PCAP_LIB)/LIB/SYSEXE PCAP_DEFS = ,"USE_NETWORK=1" PCAP_SIMH_INC = /INCL=($(PCAP_DIR)) .ENDIF # MITS Altair Simulator Definitions. # ALTAIR_DIR = SYS$DISK:[.ALTAIR] ALTAIR_LIB = $(LIB_DIR)ALTAIR-$(ARCH).OLB ALTAIR_SOURCE = $(ALTAIR_DIR)ALTAIR_SIO.C,$(ALTAIR_DIR)ALTAIR_CPU.C,\ $(ALTAIR_DIR)ALTAIR_DSK.C,$(ALTAIR_DIR)ALTAIR_SYS.C ALTAIR_OPTIONS = /INCL=($(SIMH_DIR),$(ALTAIR_DIR))/DEF=($(CC_DEFS)) # # MITS Altair Z80 Simulator Definitions. # ALTAIRZ80_DIR = SYS$DISK:[.ALTAIRZ80] ALTAIRZ80_LIB = $(LIB_DIR)ALTAIRZ80-$(ARCH).OLB ALTAIRZ80_SOURCE = $(ALTAIRZ80_DIR)/ALTAIRZ80_CPU.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_CPU_NOMMU.C,\ $(ALTAIRZ80_DIR)/ALTAIRZ80_DSK.C,$(ALTAIRZ80_DIR)/DISASM.C,\ $(ALTAIRZ80_DIR)/ALTAIRZ80_SIO.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_SYS.C,\ $(ALTAIRZ80_DIR)/ALTAIRZ80_HDSK.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_NET.C,\ $(ALTAIRZ80_DIR)/FLASHWRITER2.C,$(ALTAIRZ80_DIR)/I86_DECODE.C,\ $(ALTAIRZ80_DIR)/I86_OPS.C,$(ALTAIRZ80_DIR)/I86_PRIM_OPS.C,\ $(ALTAIRZ80_DIR)/I8272.C,$(ALTAIRZ80_DIR)/INSNSA.C,$(ALTAIRZ80_DIR)/INSNSD.C,\ $(ALTAIRZ80_DIR)/MFDC.C,$(ALTAIRZ80_DIR)/N8VEM.C,$(ALTAIRZ80_DIR)/VFDHD.C,\ $(ALTAIRZ80_DIR)/S100_DISK1A.C,$(ALTAIRZ80_DIR)/S100_DISK2.C,\ $(ALTAIRZ80_DIR)/S100_FIF.C,$(ALTAIRZ80_DIR)/S100_MDRIVEH.C,\ $(ALTAIRZ80_DIR)/S100_MDSAD.C,$(ALTAIRZ80_DIR)/S100_SELCHAN.C,\ $(ALTAIRZ80_DIR)/S100_SS1.C,$(ALTAIRZ80_DIR)/S100_64FDC.C,\ $(ALTAIRZ80_DIR)/S100_SCP300F.C,$(ALTAIRZ80_DIR)/SIM_IMD.C,\ $(ALTAIRZ80_DIR)/WD179X.C ALTAIRZ80_OPTIONS = /INCL=($(SIMH_DIR),$(ALTAIRZ80_DIR))/DEF=($(CC_DEFS)) # # Data General Nova Simulator Definitions. # NOVA_DIR = SYS$DISK:[.NOVA] NOVA_LIB = $(LIB_DIR)NOVA-$(ARCH).OLB NOVA_SOURCE = $(NOVA_DIR)NOVA_SYS.C,$(NOVA_DIR)NOVA_CPU.C,\ $(NOVA_DIR)NOVA_DKP.C,$(NOVA_DIR)NOVA_DSK.C,\ $(NOVA_DIR)NOVA_LP.C,$(NOVA_DIR)NOVA_MTA.C,\ $(NOVA_DIR)NOVA_PLT.C,$(NOVA_DIR)NOVA_PT.C,\ $(NOVA_DIR)NOVA_CLK.C,$(NOVA_DIR)NOVA_TT.C,\ $(NOVA_DIR)NOVA_TT1.C,$(NOVA_DIR)NOVA_QTY.C NOVA_OPTIONS = /INCL=($(SIMH_DIR),$(NOVA_DIR))/DEF=($(CC_DEFS)) # # Data General Eclipse Simulator Definitions. # ECLIPSE_LIB = $(LIB_DIR)ECLIPSE-$(ARCH).OLB ECLIPSE_SOURCE = $(NOVA_DIR)ECLIPSE_CPU.C,$(NOVA_DIR)ECLIPSE_TT.C,\ $(NOVA_DIR)NOVA_SYS.C,$(NOVA_DIR)NOVA_DKP.C,\ $(NOVA_DIR)NOVA_DSK.C,$(NOVA_DIR)NOVA_LP.C,\ $(NOVA_DIR)NOVA_MTA.C,$(NOVA_DIR)NOVA_PLT.C,\ $(NOVA_DIR)NOVA_PT.C,$(NOVA_DIR)NOVA_CLK.C,\ $(NOVA_DIR)NOVA_TT1.C,$(NOVA_DIR)NOVA_QTY.C ECLIPSE_OPTIONS = /INCL=($(SIMH_DIR),$(NOVA_DIR))\ /DEF=($(CC_DEFS),"USE_INT64=1","ECLIPSE=1") # # GRI Corporation GRI-909 Simulator Definitions. # GRI_DIR = SYS$DISK:[.GRI] GRI_LIB = $(LIB_DIR)GRI-$(ARCH).OLB GRI_SOURCE = $(GRI_DIR)GRI_CPU.C,$(GRI_DIR)GRI_STDDEV.C,$(GRI_DIR)GRI_SYS.C GRI_OPTIONS = /INCL=($(SIMH_DIR),$(GRI_DIR))/DEF=($(CC_DEFS)) # # Royal-McBee LGP-30 Simulator Definitions. # LGP_DIR = SYS$DISK:[.LGP] LGP_LIB = $(LIB_DIR)LGP-$(ARCH).OLB LGP_SOURCE = $(LGP_DIR)LGP_CPU.C,$(LGP_DIR)LGP_STDDEV.C,$(LGP_DIR)LGP_SYS.C LGP_OPTIONS = /INCL=($(SIMH_DIR),$(LGP_DIR))/DEF=($(CC_DEFS)) # # Honeywell 316/516 Simulator Definitions. # H316_DIR = SYS$DISK:[.H316] H316_LIB = $(LIB_DIR)H316-$(ARCH).OLB H316_SOURCE = $(H316_DIR)H316_STDDEV.C,$(H316_DIR)H316_LP.C,\ $(H316_DIR)H316_CPU.C,$(H316_DIR)H316_SYS.C,\ $(H316_DIR)H316_FHD.C,$(H316_DIR)H316_MT.C,\ $(H316_DIR)H316_DP.C H316_OPTIONS = /INCL=($(SIMH_DIR),$(H316_DIR))/DEF=($(CC_DEFS)) # # Hewlett-Packard HP-2100 Simulator Definitions. # HP2100_DIR = SYS$DISK:[.HP2100] HP2100_LIB = $(LIB_DIR)HP2100-$(ARCH).OLB HP2100_SOURCE = $(HP2100_DIR)HP2100_STDDEV.C,$(HP2100_DIR)HP2100_DP.C,\ $(HP2100_DIR)HP2100_DQ.C,$(HP2100_DIR)HP2100_DR.C,\ $(HP2100_DIR)HP2100_LPS.C,$(HP2100_DIR)HP2100_MS.C,\ $(HP2100_DIR)HP2100_MT.C,$(HP2100_DIR)HP2100_MUX.C,\ $(HP2100_DIR)HP2100_CPU.C,$(HP2100_DIR)HP2100_FP.C,\ $(HP2100_DIR)HP2100_SYS.C,$(HP2100_DIR)HP2100_LPT.C,\ $(HP2100_DIR)HP2100_IPL.C,$(HP2100_DIR)HP2100_DS.C,\ $(HP2100_DIR)HP2100_CPU0.C,$(HP2100_DIR)HP2100_CPU1.C,\ $(HP2100_DIR)HP2100_CPU2.C,$(HP2100_DIR)HP2100_CPU3.C,\ $(HP2100_DIR)HP2100_CPU4.C,$(HP2100_DIR)HP2100_CPU5.C,\ $(HP2100_DIR)HP2100_CPU6.C,$(HP2100_DIR)HP2100_CPU7.C,\ $(HP2100_DIR)HP2100_FP1.C,$(HP2100_DIR)HP2100_BACI.C,\ $(HP2100_DIR)HP2100_MPX.C,$(HP2100_DIR)HP2100_PIF.C .IF ALPHA_OR_IA64 HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))\ /DEF=($(CC_DEFS),"HAVE_INT64=1") .ELSE HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))/DEF=($(CC_DEFS)) .ENDIF # # Interdata 16-bit CPU. # ID16_DIR = SYS$DISK:[.INTERDATA] ID16_LIB = $(LIB_DIR)ID16-$(ARCH).OLB ID16_SOURCE = $(ID16_DIR)ID16_CPU.C,$(ID16_DIR)ID16_SYS.C,$(ID16_DIR)ID_DP.C,\ $(ID16_DIR)ID_FD.C,$(ID16_DIR)ID_FP.C,$(ID16_DIR)ID_IDC.C,\ $(ID16_DIR)ID_IO.C,$(ID16_DIR)ID_LP.C,$(ID16_DIR)ID_MT.C,\ $(ID16_DIR)ID_PAS.C,$(ID16_DIR)ID_PT.C,$(ID16_DIR)ID_TT.C,\ $(ID16_DIR)ID_UVC.C,$(ID16_DIR)ID16_DBOOT.C,$(ID16_DIR)ID_TTP.C ID16_OPTIONS = /INCL=($(SIMH_DIR),$(ID16_DIR))/DEF=($(CC_DEFS)) # # Interdata 32-bit CPU. # ID32_DIR = SYS$DISK:[.INTERDATA] ID32_LIB = $(LIB_DIR)ID32-$(ARCH).OLB ID32_SOURCE = $(ID32_DIR)ID32_CPU.C,$(ID32_DIR)ID32_SYS.C,$(ID32_DIR)ID_DP.C,\ $(ID32_DIR)ID_FD.C,$(ID32_DIR)ID_FP.C,$(ID32_DIR)ID_IDC.C,\ $(ID32_DIR)ID_IO.C,$(ID32_DIR)ID_LP.C,$(ID32_DIR)ID_MT.C,\ $(ID32_DIR)ID_PAS.C,$(ID32_DIR)ID_PT.C,$(ID32_DIR)ID_TT.C,\ $(ID32_DIR)ID_UVC.C,$(ID32_DIR)ID32_DBOOT.C,$(ID32_DIR)ID_TTP.C ID32_OPTIONS = /INCL=($(SIMH_DIR),$(ID32_DIR))/DEF=($(CC_DEFS)) # # IBM 1130 Simulator Definitions. # IBM1130_DIR = SYS$DISK:[.IBM1130] IBM1130_LIB = $(LIB_DIR)IBM1130-$(ARCH).OLB IBM1130_SOURCE = $(IBM1130_DIR)IBM1130_CPU.C,$(IBM1130_DIR)IBM1130_CR.C,\ $(IBM1130_DIR)IBM1130_DISK.C,$(IBM1130_DIR)IBM1130_STDDEV.C,\ $(IBM1130_DIR)IBM1130_SYS.C,$(IBM1130_DIR)IBM1130_GDU.C,\ $(IBM1130_DIR)IBM1130_GUI.C,$(IBM1130_DIR)IBM1130_PRT.C,\ $(IBM1130_DIR)IBM1130_FMT.C,$(IBM1130_DIR)IBM1130_PTRP.C,\ $(IBM1130_DIR)IBM1130_PLOT.C,$(IBM1130_DIR)IBM1130_SCA.C,\ $(IBM1130_DIR)IBM1130_T2741.C IBM1130_OPTIONS = /INCL=($(SIMH_DIR),$(IBM1130_DIR))/DEF=($(CC_DEFS)) # # IBM 1401 Simulator Definitions. # I1401_DIR = SYS$DISK:[.I1401] I1401_LIB = $(LIB_DIR)I1401-$(ARCH).OLB I1401_SOURCE = $(I1401_DIR)I1401_LP.C,$(I1401_DIR)I1401_CPU.C,\ $(I1401_DIR)I1401_IQ.C,$(I1401_DIR)I1401_CD.C,\ $(I1401_DIR)I1401_MT.C,$(I1401_DIR)I1401_DP.C,\ $(I1401_DIR)I1401_SYS.C I1401_OPTIONS = /INCL=($(SIMH_DIR),$(I1401_DIR))/DEF=($(CC_DEFS)) # # IBM 1620 Simulators Definitions. # I1620_DIR = SYS$DISK:[.I1620] I1620_LIB = $(LIB_DIR)I1620-$(ARCH).OLB I1620_SOURCE = $(I1620_DIR)I1620_CD.C,$(I1620_DIR)I1620_DP.C,\ $(I1620_DIR)I1620_PT.C,$(I1620_DIR)I1620_TTY.C,\ $(I1620_DIR)I1620_CPU.C,$(I1620_DIR)I1620_LP.C,\ $(I1620_DIR)I1620_FP.C,$(I1620_DIR)I1620_SYS.C I1620_OPTIONS = /INCL=($(SIMH_DIR),$(I1620_DIR))/DEF=($(CC_DEFS)) # # PDP-1 Simulator Definitions. # PDP1_DIR = SYS$DISK:[.PDP1] PDP1_LIB = $(LIB_DIR)PDP1-$(ARCH).OLB PDP1_SOURCE = $(PDP1_DIR)PDP1_LP.C,$(PDP1_DIR)PDP1_CPU.C,\ $(PDP1_DIR)PDP1_STDDEV.C,$(PDP1_DIR)PDP1_SYS.C,\ $(PDP1_DIR)PDP1_DT.C,$(PDP1_DIR)PDP1_DRM.C,\ $(PDP1_DIR)PDP1_CLK.C,$(PDP1_DIR)PDP1_DCS.C PDP1_OPTIONS = /INCL=($(SIMH_DIR),$(PDP1_DIR))/DEF=($(CC_DEFS)) # # Digital Equipment PDP-8 Simulator Definitions. # PDP8_DIR = SYS$DISK:[.PDP8] PDP8_LIB = $(LIB_DIR)PDP8-$(ARCH).OLB PDP8_SOURCE = $(PDP8_DIR)PDP8_CPU.C,$(PDP8_DIR)PDP8_CLK.C,\ $(PDP8_DIR)PDP8_DF.C,$(PDP8_DIR)PDP8_DT.C,\ $(PDP8_DIR)PDP8_LP.C,$(PDP8_DIR)PDP8_MT.C,\ $(PDP8_DIR)PDP8_PT.C,$(PDP8_DIR)PDP8_RF.C,\ $(PDP8_DIR)PDP8_RK.C,$(PDP8_DIR)PDP8_RX.C,\ $(PDP8_DIR)PDP8_SYS.C,$(PDP8_DIR)PDP8_TT.C,\ $(PDP8_DIR)PDP8_TTX.C,$(PDP8_DIR)PDP8_RL.C,\ $(PDP8_DIR)PDP8_TSC.C,$(PDP8_DIR)PDP8_TD.C,\ $(PDP8_DIR)PDP8_CT.C PDP8_OPTIONS = /INCL=($(SIMH_DIR),$(PDP8_DIR))/DEF=($(CC_DEFS)) # # Digital Equipment PDP-4, PDP-7, PDP-9 And PDP-15 Simulator Definitions. # PDP18B_DIR = SYS$DISK:[.PDP18B] PDP4_LIB = $(LIB_DIR)PDP4-$(ARCH).OLB PDP7_LIB = $(LIB_DIR)PDP7-$(ARCH).OLB PDP9_LIB = $(LIB_DIR)PDP9-$(ARCH).OLB PDP15_LIB = $(LIB_DIR)PDP15-$(ARCH).OLB PDP18B_SOURCE = $(PDP18B_DIR)PDP18B_DT.C,$(PDP18B_DIR)PDP18B_DRM.C,\ $(PDP18B_DIR)PDP18B_CPU.C,$(PDP18B_DIR)PDP18B_LP.C,\ $(PDP18B_DIR)PDP18B_MT.C,$(PDP18B_DIR)PDP18B_RF.C,\ $(PDP18B_DIR)PDP18B_RP.C,$(PDP18B_DIR)PDP18B_STDDEV.C,\ $(PDP18B_DIR)PDP18B_SYS.C,$(PDP18B_DIR)PDP18B_TT1.C,\ $(PDP18B_DIR)PDP18B_RB.C,$(PDP18B_DIR)PDP18B_FPP.C PDP4_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP4=1") PDP7_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP7=1") PDP9_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP9=1") PDP15_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP15=1") # # Digital Equipment PDP-11 Simulator Definitions. # PDP11_DIR = SYS$DISK:[.PDP11] PDP11_LIB1 = $(LIB_DIR)PDP11L1-$(ARCH).OLB PDP11_SOURCE1 = $(PDP11_DIR)PDP11_FP.C,$(PDP11_DIR)PDP11_CPU.C,\ $(PDP11_DIR)PDP11_DZ.C,$(PDP11_DIR)PDP11_CIS.C,\ $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_RK.C,\ $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RP.C,\ $(PDP11_DIR)PDP11_RX.C,$(PDP11_DIR)PDP11_STDDEV.C,\ $(PDP11_DIR)PDP11_SYS.C,$(PDP11_DIR)PDP11_TC.C, \ $(PDP11_DIR)PDP11_CPUMOD.C,$(PDP11_DIR)PDP11_CR.C,\ $(PDP11_DIR)PDP11_TA.C,$(PDP11_DIR)PDP11_IO_LIB.C PDP11_LIB2 = $(LIB_DIR)PDP11L2-$(ARCH).OLB PDP11_SOURCE2 = $(PDP11_DIR)PDP11_TM.C,$(PDP11_DIR)PDP11_TS.C,\ $(PDP11_DIR)PDP11_IO.C,$(PDP11_DIR)PDP11_RQ.C,\ $(PDP11_DIR)PDP11_TQ.C,$(PDP11_DIR)PDP11_PCLK.C,\ $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_PT.C,\ $(PDP11_DIR)PDP11_HK.C,$(PDP11_DIR)PDP11_XQ.C,\ $(PDP11_DIR)PDP11_VH.C,$(PDP11_DIR)PDP11_RH.C,\ $(PDP11_DIR)PDP11_XU.C,$(PDP11_DIR)PDP11_TU.C,\ $(PDP11_DIR)PDP11_DL.C,$(PDP11_DIR)PDP11_RF.C, \ $(PDP11_DIR)PDP11_RC.C,$(PDP11_DIR)PDP11_KG.C,\ $(PDP11_DIR)PDP11_KE.C,$(PDP11_DIR)PDP11_DC.C PDP11_OPTIONS = /INCL=($(SIMH_DIR),$(PDP11_DIR)$(PCAP_INC))\ /DEF=($(CC_DEFS),"VM_PDP11=1"$(PCAP_DEFS)) # # Digital Equipment PDP-10 Simulator Definitions. # PDP10_DIR = SYS$DISK:[.PDP10] PDP10_LIB = $(LIB_DIR)PDP10-$(ARCH).OLB PDP10_SOURCE = $(PDP10_DIR)PDP10_FE.C,\ $(PDP10_DIR)PDP10_CPU.C,$(PDP10_DIR)PDP10_KSIO.C,\ $(PDP10_DIR)PDP10_LP20.C,$(PDP10_DIR)PDP10_MDFP.C,\ $(PDP10_DIR)PDP10_PAG.C,$(PDP10_DIR)PDP10_XTND.C,\ $(PDP10_DIR)PDP10_RP.C,$(PDP10_DIR)PDP10_SYS.C,\ $(PDP10_DIR)PDP10_TIM.C,$(PDP10_DIR)PDP10_TU.C,\ $(PDP11_DIR)PDP11_PT.C,$(PDP11_DIR)PDP11_DZ.C,\ $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_XU.C,\ $(PDP11_DIR)PDP11_CR.C PDP10_OPTIONS = /INCL=($(SIMH_DIR),$(PDP10_DIR),$(PDP11_DIR))\ /DEF=($(CC_DEFS),"USE_INT64=1","VM_PDP10=1"$(PCAP_DEFS)) # # IBM System 3 Simulator Definitions. # S3_DIR = SYS$DISK:[.S3] S3_LIB = $(LIB_DIR)S3-$(ARCH).OLB S3_SOURCE = $(S3_DIR)S3_CD.C,$(S3_DIR)S3_CPU.C,$(S3_DIR)S3_DISK.C,\ $(S3_DIR)S3_LP.C,$(S3_DIR)S3_PKB.C,$(S3_DIR)S3_SYS.C S3_OPTIONS = /INCL=($(SIMH_DIR),$(S3_DIR))/DEF=($(CC_DEFS)) # # SDS 940 # SDS_DIR = SYS$DISK:[.SDS] SDS_LIB = $(LIB_DIR)SDS-$(ARCH).OLB SDS_SOURCE = $(SDS_DIR)SDS_CPU.C,$(SDS_DIR)SDS_DRM.C,$(SDS_DIR)SDS_DSK.C,\ $(SDS_DIR)SDS_IO.C,$(SDS_DIR)SDS_LP.C,$(SDS_DIR)SDS_MT.C,\ $(SDS_DIR)SDS_MUX.C,$(SDS_DIR)SDS_RAD.C,$(SDS_DIR)SDS_STDDEV.C,\ $(SDS_DIR)SDS_SYS.C SDS_OPTIONS = /INCL=($(SIMH_DIR),$(SDS_DIR))/DEF=($(CC_DEFS)) # # Digital Equipment VAX Simulator Definitions. # VAX_DIR = SYS$DISK:[.VAX] VAX_LIB = $(LIB_DIR)VAX-$(ARCH).OLB VAX_SOURCE = $(VAX_DIR)VAX_CIS.C,$(VAX_DIR)VAX_CMODE.C,\ $(VAX_DIR)VAX_CPU.C,$(VAX_DIR)VAX_CPU1.C,\ $(VAX_DIR)VAX_FPA.C,$(VAX_DIR)VAX_MMU.C,\ $(VAX_DIR)VAX_OCTA.C,$(VAX_DIR)VAX_SYS.C,\ $(VAX_DIR)VAX_SYSCM.C,$(VAX_DIR)VAX_SYSDEV.C,\ $(VAX_DIR)VAX_SYSLIST.C,$(VAX_DIR)VAX_IO.C,\ $(VAX_DIR)VAX_STDDEV.C,$(PDP11_DIR)PDP11_IO_LIB.C,\ $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RQ.C,\ $(PDP11_DIR)PDP11_TS.C,$(PDP11_DIR)PDP11_DZ.C,\ $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_TQ.C,\ $(PDP11_DIR)PDP11_XQ.C,$(PDP11_DIR)PDP11_CR.C,\ $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_VH.C VAX_OPTIONS = /INCL=($(SIMH_DIR),$(VAX_DIR),$(PDP11_DIR)$(PCAP_INC))\ /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS)) # Digital Equipment VAX780 Simulator Definitions. # VAX780_DIR = SYS$DISK:[.VAX] VAX780_LIB1 = $(LIB_DIR)VAX780L1-$(ARCH).OLB VAX780_SOURCE1 = $(VAX780_DIR)VAX_CPU.C,$(VAX780_DIR)VAX_CPU1.C,\ $(VAX780_DIR)VAX_FPA.C,$(VAX780_DIR)VAX_CIS.C,\ $(VAX780_DIR)VAX_OCTA.C,$(VAX780_DIR)VAX_CMODE.C,\ $(VAX780_DIR)VAX_MMU.C,$(VAX780_DIR)VAX_SYS.C,\ $(VAX780_DIR)VAX_SYSCM.C,$(VAX780_DIR)VAX780_STDDEV.C,\ $(VAX780_DIR)VAX780_SBI.C,$(VAX780_DIR)VAX780_MEM.C,\ $(VAX780_DIR)VAX780_UBA.C,$(VAX780_DIR)VAX780_MBA.C,\ $(VAX780_DIR)VAX780_FLOAD.C,$(VAX780_DIR)VAX780_SYSLIST.C VAX780_LIB2 = $(LIB_DIR)VAX780L2-$(ARCH).OLB VAX780_SOURCE2 = $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RQ.C,\ $(PDP11_DIR)PDP11_TS.C,$(PDP11_DIR)PDP11_DZ.C,\ $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_TQ.C,\ $(PDP11_DIR)PDP11_XU.C,$(PDP11_DIR)PDP11_RY.C,\ $(PDP11_DIR)PDP11_CR.C,$(PDP11_DIR)PDP11_RP.C,\ $(PDP11_DIR)PDP11_TU.C,$(PDP11_DIR)PDP11_HK.C,\ $(PDP11_DIR)PDP11_IO_LIB.C VAX780_OPTIONS = /INCL=($(SIMH_DIR),$(VAX780_DIR),$(PDP11_DIR)$(PCAP_INC))\ /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS),"VAX_780=1") # IBM 7094 Simulator Definitions. # I7094_DIR = SYS$DISK:[.I7094] I7094_LIB = $(LIB_DIR)I7094-$(ARCH).OLB I7094_SOURCE = $(I7094_DIR)I7094_CPU.C,$(I7094_DIR)I7094_CPU1.C,\ $(I7094_DIR)I7094_IO.C,$(I7094_DIR)I7094_CD.C,\ $(I7094_DIR)I7094_CLK.C,$(I7094_DIR)I7094_COM.C,\ $(I7094_DIR)I7094_DRM.C,$(I7094_DIR)I7094_DSK.C,\ $(I7094_DIR)I7094_SYS.C,$(I7094_DIR)I7094_LP.C,\ $(I7094_DIR)I7094_MT.C,$(I7094_DIR)I7094_BINLOADER.C I7094_OPTIONS = /INCL=($(SIMH_DIR),$(I7094_DIR))/DEF=($(CC_DEFS)) # If we're not a VAX, Build Everything # .IF ALPHA_OR_IA64 ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI LGP H316 HP2100 I1401 I1620 IBM1130 ID16 \ ID32 NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP10 PDP11 PDP15 S3 VAX VAX780 SDS \ I7094 @CONTINUE .ELSE # # Else We Are On VAX And Build Everything EXCEPT the 64b simulators # ALL : ALTAIR ALTAIRZ80 GRI H316 HP2100 I1401 I1620 IBM1130 ID16 ID32 \ NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP11 PDP15 S3 VAX VAX780 SDS @CONTINUE .ENDIF CLEAN : $! $! Clean out all targets and building Remnants $! $ IF (F$SEARCH("$(BIN_DIR)*.EXE;*").NES."") THEN - DELETE/NOLOG/NOCONFIRM $(BIN_DIR)*.EXE;* $ IF (F$SEARCH("$(LIB_DIR)*.OLB;*").NES."") THEN - DELETE/NOLOG/NOCONFIRM $(LIB_DIR)*.OLB;* $ IF (F$SEARCH("SYS$DISK:[...]*.OBJ;*").NES."") THEN - DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.OBJ;* $ IF (F$SEARCH("SYS$DISK:[...]*.LIS;*").NES."") THEN - DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.LIS;* $ IF (F$SEARCH("SYS$DISK:[...]*.MAP;*").NES."") THEN - DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.MAP;* # # Build The Libraries. # $(SIMH_LIB) : $(SIMH_SOURCE) $! $! Building The $(SIMH_LIB) Library. $! $ $(CC)/DEF=($(CC_DEFS)$(PCAP_DEFS))$(PCAP_SIMH_INC) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(ALTAIR_LIB) : $(ALTAIR_SOURCE) $! $! Building The $(ALTAIR_LIB) Library. $! $ $(CC)$(ALTAIR_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(ALTAIRZ80_LIB) : $(ALTAIRZ80_SOURCE) $! $! Building The $(ALTAIRZ80_LIB) Library. $! $ $(CC)$(ALTAIRZ80_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The Eclipse Library. # .IF ALPHA_OR_IA64 $(ECLIPSE_LIB) : $(ECLIPSE_SOURCE) $! $! Building The $(ECLIPSE_LIB) Library. $! $ $(CC)$(ECLIPSE_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # We Are On VAX And Due To The Use of INT64 We Can't Build It. # $(ECLIPSE_LIB) : $! $! Due To The Use Of INT64 We Can't Build The $! $(LIB_DIR)ECLIPSE-$(ARCH).OLB Library On VAX. $! .ENDIF $(GRI_LIB) : $(GRI_SOURCE) $! $! Building The $(GRI_LIB) Library. $! $ $(CC)$(GRI_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(LGP_LIB) : $(LGP_SOURCE) $! $! Building The $(LGP_LIB) Library. $! $ $(CC)$(LGP_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(H316_LIB) : $(H316_SOURCE) $! $! Building The $(H316_LIB) Library. $! $ $(CC)$(H316_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(HP2100_LIB) : $(HP2100_SOURCE) $! $! Building The $(HP2100_LIB) Library. $! $ $(CC)$(HP2100_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(I1401_LIB) : $(I1401_SOURCE) $! $! Building The $(I1401_LIB) Library. $! $ $(CC)$(I1401_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(I1620_LIB) : $(I1620_SOURCE) $! $! Building The $(I1620_LIB) Library. $! $ $(CC)$(I1620_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(IBM1130_LIB) : $(IBM1130_SOURCE) $! $! Building The $(IBM1130_LIB) Library. $! $ $(CC)$(IBM1130_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(ID16_LIB) : $(ID16_SOURCE) $! $! Building The $(ID16_LIB) Library. $! $ $(CC)$(ID16_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(ID32_LIB) : $(ID32_SOURCE) $! $! Building The $(ID32_LIB) Library. $! $ $(CC)$(ID32_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(NOVA_LIB) : $(NOVA_SOURCE) $! $! Building The $(NOVA_LIB) Library. $! $ $(CC)$(NOVA_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP1_LIB) : $(PDP1_SOURCE) $! $! Building The $(PDP1_LIB) Library. $! $ $(CC)$(PDP1_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP4_LIB) : $(PDP18B_SOURCE) $! $! Building The $(PDP4_LIB) Library. $! $ $(CC)$(PDP4_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP7_LIB) : $(PDP18B_SOURCE) $! $! Building The $(PDP7_LIB) Library. $! $ $(CC)$(PDP7_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP8_LIB) : $(PDP8_SOURCE) $! $! Building The $(PDP8_LIB) Library. $! $ $(CC)$(PDP8_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP9_LIB) : $(PDP18B_SOURCE) $! $! Building The $(PDP9_LIB) Library. $! $ $(CC)$(PDP9_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The PDP-10 Library. # .IF ALPHA_OR_IA64 $(PDP10_LIB) : $(PDP10_SOURCE) $! $! Building The $(PDP10_LIB) Library. $! $ $(CC)$(PDP10_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # We Are On VAX And Due To The Use of INT64 We Can't Build It. # $(PDP10_LIB) : $! Due To The Use Of INT64 We Can't Build The $! $(LIB_DIR)PDP10-$(ARCH).OLB Library On VAX. .ENDIF $(PDP11_LIB1) : $(PDP11_SOURCE1) $! $! Building The $(PDP11_LIB1) Library. $! $(CC)$(PDP11_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP11_LIB2) : $(PDP11_SOURCE2) $! $! Building The $(PDP11_LIB2) Library. $! $(CC)$(PDP11_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PDP15_LIB) : $(PDP18B_SOURCE) $! $! Building The $(PDP15_LIB) Library. $! $ $(CC)$(PDP15_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(S3_LIB) : $(S3_SOURCE) $! $! Building The $(S3_LIB) Library. $! $ $(CC)$(S3_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(SDS_LIB) : $(SDS_SOURCE) $! $! Building The $(SDS_LIB) Library. $! $ $(CC)$(SDS_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(VAX_LIB) : $(VAX_SOURCE) $! $! Building The $(VAX_LIB) Library. $! $ $(CC)$(VAX_OPTIONS)/OBJ=$(VAX_DIR) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(VAX780_LIB1) : $(VAX780_SOURCE1) $! $! Building The $(VAX780_LIB1) Library. $! $ $(CC)$(VAX780_OPTIONS)/OBJ=$(VAX780_DIR) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(VAX780_LIB2) : $(VAX780_SOURCE2) $! $! Building The $(VAX780_LIB2) Library. $! $ $(CC)$(VAX780_OPTIONS)/OBJ=$(VAX780_DIR) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* $(PCAP_LIB) : $(PCAP_SOURCE) $! $! Building The $(PCAP_LIB) Library. $! $ SET DEFAULT $(PCAP_DIR) $ @VMS_PCAP $(DEBUG) $ SET DEFAULT [--] $ IF (F$SEARCH("$(PCAP_LIB)").NES."") THEN - DELETE $(PCAP_LIB); $ COPY $(PCAP_DIR)PCAP.OLB $(PCAP_LIB) $ DELETE/NOLOG/NOCONFIRM $(PCAP_DIR)*.OBJ;*,$(PCAP_DIR)*.OLB;* # # If Not On VAX, Build The IBM 7094 Library. # .IF ALPHA_OR_IA64 $(I7094_LIB) : $(I7094_SOURCE) $! $! Building The $(I7094_LIB) Library. $! $ $(CC)$(I7094_OPTIONS) - /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - LIBRARY/CREATE $(MMS$TARGET) $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # We Are On VAX And Due To The Use of INT64 We Can't Build It. # $(I7094_LIB) : $! Due To The Use Of INT64 We Can't Build The $! $(LIB_DIR)I7094-$(ARCH).OLB Library On VAX. .ENDIF # # Individual Simulator Builds. # ALTAIR : $(SIMH_LIB) $(ALTAIR_LIB) $! $! Building The $(BIN_DIR)ALTAIR-$(ARCH).EXE Simulator. $! $ $(CC)$(ALTAIR_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ALTAIR-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ALTAIR_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* ALTAIRZ80 : $(SIMH_LIB) $(ALTAIRZ80_LIB) $! $! Building The $(BIN_DIR)ALTAIRZ80-$(ARCH).EXE Simulator. $! $ $(CC)$(ALTAIRZ80_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ALTAIRZ80-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ALTAIRZ80_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The PDP-10 Simulator. # .IF ALPHA_OR_IA64 ECLIPSE : $(SIMH_LIB) $(ECLIPSE_LIB) $! $! Building The $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator. $! $ $(CC)$(ECLIPSE_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ECLIPSE-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ECLIPSE_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # Else We Are On VAX And Tell The User We Can't Build On VAX # Due To The Use Of INT64. # ECLIPSE : $! Sorry, Can't Build $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator $! Because It Requires The Use Of INT64. .ENDIF GRI : $(SIMH_LIB) $(GRI_LIB) $! $! Building The $(BIN_DIR)GRI-$(ARCH).EXE Simulator. $! $ $(CC)$(GRI_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)GRI-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(GRI_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* LGP : $(SIMH_LIB) $(LGP_LIB) $! $! Building The $(BIN_DIR)LGP-$(ARCH).EXE Simulator. $! $ $(CC)$(LGP_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)LGP-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(LGP_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* H316 : $(SIMH_LIB) $(H316_LIB) $! $! Building The $(BIN_DIR)H316-$(ARCH).EXE Simulator. $! $ $(CC)$(H316_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)H316-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(H316_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* HP2100 : $(SIMH_LIB) $(HP2100_LIB) $! $! Building The $(BIN_DIR)HP2100-$(ARCH).EXE Simulator. $! $ $(CC)$(HP2100_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)HP2100-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(HP2100_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* I1401 : $(SIMH_LIB) $(I1401_LIB) $! $! Building The $(BIN_DIR)I1401-$(ARCH).EXE Simulator. $! $ $(CC)$(I1401_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I1401-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(I1401_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* I1620 : $(SIMH_LIB) $(I1620_LIB) $! $! Building The $(BIN_DIR)I1620-$(ARCH).EXE Simulator. $! $ $(CC)$(I1620_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I1620-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(I1620_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* IBM1130 : $(SIMH_LIB) $(IBM1130_LIB) $! $! Building The $(BIN_DIR)IBM1130-$(ARCH).EXE Simulator. $! $ $(CC)$(IBM1130_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)IBM1130-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(IBM1130_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* ID16 : $(SIMH_LIB) $(ID16_LIB) $! $! Building The $(BIN_DIR)ID16-$(ARCH).EXE Simulator. $! $ $(CC)$(ID16_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ID16-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ID16_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* ID32 : $(SIMH_LIB) $(ID32_LIB) $! $! Building The $(BIN_DIR)ID32-$(ARCH).EXE Simulator. $! $ $(CC)$(ID32_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ID32-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(ID32_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* NOVA : $(SIMH_LIB) $(NOVA_LIB) $! $! Building The $(BIN_DIR)NOVA-$(ARCH).EXE Simulator. $! $ $(CC)$(NOVA_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)NOVA-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(NOVA_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP1 : $(SIMH_LIB) $(PDP1_LIB) $! $! Building The $(BIN_DIR)PDP1-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP1_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP1-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP1_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP4 : $(SIMH_LIB) $(PDP4_LIB) $! $! Building The $(BIN_DIR)PDP4-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP4_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP4-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP4_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP7 : $(SIMH_LIB) $(PDP7_LIB) $! $! Building The $(BIN_DIR)PDP7-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP7_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP7-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP7_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP8 : $(SIMH_LIB) $(PDP8_LIB) $! $! Building The $(BIN_DIR)PDP8-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP8_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP8-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP8_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP9 : $(SIMH_LIB) $(PDP9_LIB) $! $! Building The $(BIN_DIR)PDP9-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP9_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP9-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP9_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The PDP-10 Simulator. # .IF ALPHA_OR_IA64 PDP10 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP10_LIB) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)PDP10-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP10_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP10-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP10_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # Else We Are On VAX And Tell The User We Can't Build On VAX # Due To The Use Of INT64. # PDP10 : $! Sorry, Can't Build $(BIN_DIR)PDP10-$(ARCH).EXE Simulator $! Because It Requires The Use Of INT64. .ENDIF PDP11 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP11_LIB1) $(PDP11_LIB2) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)PDP11-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP11_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP11-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP11_LIB1)/LIBRARY,$(PDP11_LIB2)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* PDP15 : $(SIMH_LIB) $(PDP15_LIB) $! $! Building The $(BIN_DIR)PDP15-$(ARCH).EXE Simulator. $! $ $(CC)$(PDP15_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP15-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(PDP15_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* S3 : $(SIMH_LIB) $(S3_LIB) $! $! Building The $(BIN_DIR)S3-$(ARCH).EXE Simulator. $! $ $(CC)$(S3_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)S3-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(S3_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* SDS : $(SIMH_LIB) $(SDS_LIB) $! $! Building The $(BIN_DIR)SDS-$(ARCH).EXE Simulator. $! $ $(CC)$(SDS_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)SDS-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(SDS_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* VAX : $(SIMH_LIB) $(PCAP_LIBD) $(VAX_LIB) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)VAX-$(ARCH).EXE Simulator. $! $ $(CC)$(VAX_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)$(LINK_SECTION_BINDING)- /EXE=$(BIN_DIR)VAX-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(VAX_LIB)/LIBRARY,- $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* VAX780 : $(SIMH_LIB) $(PCAP_LIBD) $(VAX780_LIB1) $(VAX780_LIB2) $(PCAP_EXECLET) $! $! Building The $(BIN_DIR)VAX780-$(ARCH).EXE Simulator. $! $ $(CC)$(VAX780_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)$(LINK_SECTION_BINDING)- /EXE=$(BIN_DIR)VAX780-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,- $(VAX780_LIB1)/LIBRARY,$(VAX780_LIB2)/LIBRARY,- $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* # # If Not On VAX, Build The IBM 7094 Simulator. # .IF ALPHA_OR_IA64 I7094 : $(SIMH_LIB) $(I7094_LIB) $! $! Building The $(BIN_DIR)I7094-$(ARCH).EXE Simulator. $! $ $(CC)$(I7094_OPTIONS)/OBJ=$(BLD_DIR) SCP.C $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I7094-$(ARCH).EXE - $(BLD_DIR)SCP.OBJ,$(I7094_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* .ELSE # # Else We Are On VAX And Tell The User We Can't Build On VAX # Due To The Use Of INT64. # I7094 : $! Sorry, Can't Build $(BIN_DIR)I7094-$(ARCH).EXE Simulator $! Because It Requires The Use Of INT64. .ENDIF # # PCAP VCI Components # $(PCAP_VCI) : $(PCAP_VCMDIR)PCAPVCM.EXE $! $! Installing the PCAP VCI Execlet in SYS$LOADABLE_IMAGES $! $ COPY $(PCAP_VCMDIR)PCAPVCM.EXE SYS$COMMON:[SYS$LDR]PCAPVCM.EXE $(PCAP_VCMDIR)PCAPVCM.EXE : $(PCAP_VCM_SOURCES) $! $! Building The PCAP VCI Execlet $! $ @SYS$DISK:[.PCAP-VMS.PCAPVCM]BUILD_PCAPVCM $ DELETE/NOLOG/NOCONFIRM $(PCAP_VCMDIR)*.OBJ;*,$(PCAP_VCMDIR)*.MAP;* simh-3.8.1/sim_tmxr.c0000644000175000017500000011447211126240422012652 0ustar vlmvlm/* sim_tmxr.c: Telnet terminal multiplexor library Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. 20-Nov-08 RMS Added three new standardized SHOW routines 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation 27-May-08 JDB Added line connection order to tmxr_poll_conn, added tmxr_set_lnorder and tmxr_show_lnorder 14-May-08 JDB Print device and line to which connection was made 11-Apr-07 JDB Worked around Telnet negotiation problem with QCTerm 16-Aug-05 RMS Fixed C++ declaration and cast problems 29-Jun-05 RMS Extended tmxr_dscln to support unit array devices Fixed bug in SET LOG/NOLOG 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array Added tmxr_linemsg, circular output pointers, logging (from Mark Pizzolato) 29-Dec-03 RMS Added output stall support 01-Nov-03 RMS Cleaned up attach routine 09-Mar-03 RMS Fixed bug in SHOW CONN 22-Dec-02 RMS Fixed bugs in IAC+IAC receive and transmit sequences Added support for received break (all from by Mark Pizzolato) Fixed bug in attach 31-Oct-02 RMS Fixed bug in 8b (binary) support 22-Aug-02 RMS Added tmxr_open_master, tmxr_close_master 30-Dec-01 RMS Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus 03-Dec-01 RMS Changed tmxr_fconns for extended SET/SHOW 20-Oct-01 RMS Fixed bugs in read logic (found by Thord Nilson). Added tmxr_rqln, tmxr_tqln This library includes: tmxr_poll_conn - poll for connection tmxr_reset_ln - reset line tmxr_getc_ln - get character for line tmxr_poll_rx - poll receive tmxr_putc_ln - put character for line tmxr_poll_tx - poll transmit tmxr_open_master - open master connection tmxr_close_master - close master connection tmxr_attach - attach terminal multiplexor tmxr_detach - detach terminal multiplexor tmxr_ex - (null) examine tmxr_dep - (null) deposit tmxr_msg - send message to socket tmxr_linemsg - send message to line tmxr_fconns - output connection status tmxr_fstats - output connection statistics tmxr_dscln - disconnect line (SET routine) tmxr_rqln - number of available characters for line tmxr_tqln - number of buffered characters for line tmxr_set_lnorder - set line connection order tmxr_show_lnorder - show line connection order All routines are OS-independent. */ #include "sim_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include "scp.h" #include /* Telnet protocol constants - negatives are for init'ing signed char data */ #define TN_IAC -1 /* protocol delim */ #define TN_DONT -2 /* dont */ #define TN_DO -3 /* do */ #define TN_WONT -4 /* wont */ #define TN_WILL -5 /* will */ #define TN_BRK -13 /* break */ #define TN_BIN 0 /* bin */ #define TN_ECHO 1 /* echo */ #define TN_SGA 3 /* sga */ #define TN_LINE 34 /* line mode */ #define TN_CR 015 /* carriage return */ #define TN_LF 012 /* line feed */ #define TN_NUL 000 /* null */ /* Telnet line states */ #define TNS_NORM 000 /* normal */ #define TNS_IAC 001 /* IAC seen */ #define TNS_WILL 002 /* WILL seen */ #define TNS_WONT 003 /* WONT seen */ #define TNS_SKIP 004 /* skip next cmd */ #define TNS_CRPAD 005 /* CR padding */ void tmxr_rmvrc (TMLN *lp, int32 p); int32 tmxr_send_buffered_data (TMLN *lp); TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp); extern int32 sim_switches; extern char sim_name[]; extern FILE *sim_log; extern uint32 sim_os_msec (void); /* Poll for new connection Called from unit service routine to test for new connection Inputs: *mp = pointer to terminal multiplexor descriptor Outputs: line number activated, -1 if none If a connection order is defined for the descriptor, and the first value is not -1 (indicating default order), then the order array is used to find an open line. Otherwise, a search is made of all lines in numerical sequence. */ int32 tmxr_poll_conn (TMXR *mp) { SOCKET newsock; TMLN *lp; int32 *op; int32 i, j; uint32 ipaddr; static char mantra[] = { TN_IAC, TN_WILL, TN_LINE, TN_IAC, TN_WILL, TN_SGA, TN_IAC, TN_WILL, TN_ECHO, TN_IAC, TN_WILL, TN_BIN, TN_IAC, TN_DO, TN_BIN }; newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */ if (newsock != INVALID_SOCKET) { /* got a live one? */ op = mp->lnorder; /* get line connection order list pointer */ i = mp->lines; /* play it safe in case lines == 0 */ for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ i = *op++; /* get next line in list to try */ else /* no list or not used or range error */ i = j; /* get next sequential line */ lp = mp->ldsc + i; /* get pointer to line descriptor */ if (lp->conn == 0) /* is the line available? */ break; /* yes, so stop search */ } if (i >= mp->lines) { /* all busy? */ tmxr_msg (newsock, "All connections busy\r\n"); sim_close_sock (newsock, 0); } else { lp = mp->ldsc + i; /* get line desc */ lp->conn = newsock; /* record connection */ lp->ipad = ipaddr; /* ip address */ lp->cnms = sim_os_msec (); /* time of conn */ lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */ lp->txbpr = lp->txbpi = 0; lp->rxcnt = lp->txcnt = 0; /* init counters */ lp->tsta = 0; /* init telnet state */ lp->xmte = 1; /* enable transmit */ lp->dstb = 0; /* default bin mode */ sim_write_sock (newsock, mantra, 15); tmxr_linemsg (lp, "\n\r\nConnected to the "); tmxr_linemsg (lp, sim_name); tmxr_linemsg (lp, " simulator "); if (mp->dptr) { /* device defined? */ tmxr_linemsg (lp, sim_dname (mp->dptr)); /* report device name */ tmxr_linemsg (lp, " device"); if (mp->lines > 1) { /* more than one line? */ char line[20]; tmxr_linemsg (lp, ", line "); /* report the line number */ sprintf (line, "%i", i); tmxr_linemsg (lp, line); } } tmxr_linemsg (lp, "\r\n\n"); tmxr_poll_tx (mp); /* flush output */ return i; } } /* end if newsock */ return -1; } /* Reset line */ void tmxr_reset_ln (TMLN *lp) { if (lp->txlog) /* dump log */ fflush (lp->txlog); tmxr_send_buffered_data (lp); /* send buffered data */ sim_close_sock (lp->conn, 0); /* reset conn */ lp->conn = lp->tsta = 0; /* reset state */ lp->rxbpr = lp->rxbpi = 0; lp->txbpr = lp->txbpi = 0; lp->xmte = 1; lp->dstb = 0; return; } /* Get character from specific line Inputs: *lp = pointer to terminal line descriptor Output: valid + char, 0 if line */ int32 tmxr_getc_ln (TMLN *lp) { int32 j, val = 0; uint32 tmp; if (lp->conn && lp->rcve) { /* conn & enb? */ j = lp->rxbpi - lp->rxbpr; /* # input chrs */ if (j) { /* any? */ tmp = lp->rxb[lp->rxbpr]; /* get char */ val = TMXR_VALID | (tmp & 0377); /* valid + chr */ if (lp->rbr[lp->rxbpr]) /* break? */ val = val | SCPE_BREAK; lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ } } /* end if conn */ if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ lp->rxbpi = lp->rxbpr = 0; return val; } /* Poll for input Inputs: *mp = pointer to terminal multiplexor descriptor Outputs: none */ void tmxr_poll_rx (TMXR *mp) { int32 i, nbytes, j; TMLN *lp; for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (!lp->conn || !lp->rcve) /* skip if !conn */ continue; nbytes = 0; if (lp->rxbpi == 0) /* need input? */ nbytes = sim_read_sock (lp->conn, /* yes, read */ &(lp->rxb[lp->rxbpi]), /* leave spc for */ TMXR_MAXBUF - TMXR_GUARD); /* Telnet cruft */ else if (lp->tsta) /* in Telnet seq? */ nbytes = sim_read_sock (lp->conn, /* yes, read to end */ &(lp->rxb[lp->rxbpi]), TMXR_MAXBUF - lp->rxbpi); if (nbytes < 0) /* closed? reset ln */ tmxr_reset_ln (lp); else if (nbytes > 0) { /* if data rcvd */ j = lp->rxbpi; /* start of data */ memset (&lp->rbr[j], 0, nbytes); /* clear status */ lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */ lp->rxcnt = lp->rxcnt + nbytes; /* Examine new data, remove TELNET cruft before making input available */ for (; j < lp->rxbpi; ) { /* loop thru char */ signed char tmp = lp->rxb[j]; /* get char */ switch (lp->tsta) { /* case tlnt state */ case TNS_NORM: /* normal */ if (tmp == TN_IAC) { /* IAC? */ lp->tsta = TNS_IAC; /* change state */ tmxr_rmvrc (lp, j); /* remove char */ break; } if ((tmp == TN_CR) && lp->dstb) /* CR, no bin */ lp->tsta = TNS_CRPAD; /* skip pad char */ j = j + 1; /* advance j */ break; case TNS_IAC: /* IAC prev */ if ((tmp == TN_IAC) & !lp->dstb) { /* IAC + IAC, bin? */ lp->tsta = TNS_NORM; /* treat as normal */ j = j + 1; /* advance j */ break; /* keep IAC */ } if (tmp == TN_BRK) { /* IAC + BRK? */ lp->tsta = TNS_NORM; /* treat as normal */ lp->rxb[j] = 0; /* char is null */ lp->rbr[j] = 1; /* flag break */ j = j + 1; /* advance j */ break; } if (tmp == TN_WILL) /* IAC + WILL? */ lp->tsta = TNS_WILL; else if (tmp == TN_WONT) /* IAC + WONT? */ lp->tsta = TNS_WONT; else lp->tsta = TNS_SKIP; /* IAC + other */ tmxr_rmvrc (lp, j); /* remove char */ break; case TNS_WILL: case TNS_WONT: /* IAC+WILL/WONT prev */ if (tmp == TN_BIN) { /* BIN? */ if (lp->tsta == TNS_WILL) lp->dstb = 0; else lp->dstb = 1; } /* Negotiation with the HP terminal emulator "QCTerm" is not working. QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says: Note that "CR LF" or "CR NUL" is required in both directions (in the default ASCII mode), to preserve the symmetry of the NVT model. ...The protocol requires that a NUL be inserted following a CR not followed by a LF in the data stream. Until full negotiation is implemented, we work around the problem by checking the character following the CR in non-BIN mode and strip it only if it is LF or NUL. This should not affect conforming clients. */ case TNS_CRPAD: /* only LF or NUL should follow CR */ lp->tsta = TNS_NORM; /* next normal */ if ((tmp == TN_LF) || /* CR + LF ? */ (tmp == TN_NUL)) /* CR + NUL? */ tmxr_rmvrc (lp, j); /* remove it */ break; case TNS_SKIP: default: /* skip char */ lp->tsta = TNS_NORM; /* next normal */ tmxr_rmvrc (lp, j); /* remove char */ break; } /* end case state */ } /* end for char */ } /* end else nbytes */ } /* end for lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (lp->rxbpi == lp->rxbpr) /* if buf empty, */ lp->rxbpi = lp->rxbpr = 0; /* reset pointers */ } /* end for */ return; } /* Return count of available characters for line */ int32 tmxr_rqln (TMLN *lp) { return (lp->rxbpi - lp->rxbpr); } /* Remove character p (and matching status) from line l input buffer */ void tmxr_rmvrc (TMLN *lp, int32 p) { for ( ; p < lp->rxbpi; p++) { lp->rxb[p] = lp->rxb[p + 1]; lp->rbr[p] = lp->rbr[p + 1]; } lp->rxbpi = lp->rxbpi - 1; return; } /* Store character in line buffer Inputs: *lp = pointer to line descriptor chr = characters Outputs: status = ok, connection lost, or stall */ t_stat tmxr_putc_ln (TMLN *lp, int32 chr) { if (lp->txlog) /* log if available */ fputc (chr, lp->txlog); if (lp->conn == 0) /* no conn? lost */ return SCPE_LOST; if (tmxr_tqln (lp) < (TMXR_MAXBUF - 1)) { /* room for char (+ IAC)? */ lp->txb[lp->txbpi] = (char) chr; /* buffer char */ lp->txbpi = lp->txbpi + 1; /* adv pointer */ if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ lp->txbpi = 0; if ((char) chr == TN_IAC) { /* IAC? */ lp->txb[lp->txbpi] = (char) chr; /* IAC + IAC */ lp->txbpi = lp->txbpi + 1; /* adv pointer */ if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ lp->txbpi = 0; } if (tmxr_tqln (lp) > (TMXR_MAXBUF - TMXR_GUARD)) /* near full? */ lp->xmte = 0; /* disable line */ return SCPE_OK; /* char sent */ } lp->xmte = 0; /* no room, dsbl line */ return SCPE_STALL; /* char not sent */ } /* Poll for output Inputs: *mp = pointer to terminal multiplexor descriptor Outputs: none */ void tmxr_poll_tx (TMXR *mp) { int32 i, nbytes; TMLN *lp; for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (lp->conn == 0) /* skip if !conn */ continue; nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */ if (nbytes == 0) /* buf empty? enab line */ lp->xmte = 1; } /* end for */ return; } /* Send buffered data across network Inputs: *lp = pointer to line descriptor Outputs: returns number of bytes still buffered */ int32 tmxr_send_buffered_data (TMLN *lp) { int32 nbytes, sbytes; nbytes = tmxr_tqln(lp); /* avail bytes */ if (nbytes) { /* >0? write */ if (lp->txbpr < lp->txbpi) /* no wrap? */ sbytes = sim_write_sock (lp->conn, /* write all data */ &(lp->txb[lp->txbpr]), nbytes); else sbytes = sim_write_sock (lp->conn, /* write to end buf */ &(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr); if (sbytes != SOCKET_ERROR) { /* ok? */ lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; } if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */ sbytes = sim_write_sock (lp->conn, lp->txb, nbytes); if (sbytes != SOCKET_ERROR) { /* ok */ lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; } } } /* end if nbytes */ return nbytes; } /* Return count of buffered characters for line */ int32 tmxr_tqln (TMLN *lp) { return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? TMXR_MAXBUF: 0)); } /* Open master socket */ t_stat tmxr_open_master (TMXR *mp, char *cptr) { int32 i, port; SOCKET sock; TMLN *lp; t_stat r; port = (int32) get_uint (cptr, 10, 65535, &r); /* get port */ if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG; sock = sim_master_sock (port); /* make master socket */ if (sock == INVALID_SOCKET) /* open error */ return SCPE_OPENERR; printf ("Listening on port %d (socket %d)\n", port, sock); if (sim_log) fprintf (sim_log, "Listening on port %d (socket %d)\n", port, sock); mp->port = port; /* save port */ mp->master = sock; /* save master socket */ for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp = mp->ldsc + i; lp->conn = lp->tsta = 0; lp->rxbpi = lp->rxbpr = 0; lp->txbpi = lp->txbpr = 0; lp->rxcnt = lp->txcnt = 0; lp->xmte = 1; lp->dstb = 0; } return SCPE_OK; } /* Attach unit to master socket */ t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr) { char* tptr; t_stat r; tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ if (tptr == NULL) /* no more mem? */ return SCPE_MEM; r = tmxr_open_master (mp, cptr); /* open master socket */ if (r != SCPE_OK) { /* error? */ free (tptr); /* release buf */ return SCPE_OPENERR; } strcpy (tptr, cptr); /* copy port */ uptr->filename = tptr; /* save */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ if (mp->dptr == NULL) /* has device been set? */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ return SCPE_OK; } /* Close master socket */ t_stat tmxr_close_master (TMXR *mp) { int32 i; TMLN *lp; for (i = 0; i < mp->lines; i++) { /* loop thru conn */ lp = mp->ldsc + i; if (lp->conn) { tmxr_linemsg (lp, "\r\nDisconnected from the "); tmxr_linemsg (lp, sim_name); tmxr_linemsg (lp, " simulator\r\n\n"); tmxr_reset_ln (lp); } /* end if conn */ } /* end for */ sim_close_sock (mp->master, 1); /* close master socket */ mp->master = 0; return SCPE_OK; } /* Detach unit from master socket */ t_stat tmxr_detach (TMXR *mp, UNIT *uptr) { if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; tmxr_close_master (mp); /* close master socket */ free (uptr->filename); /* free port string */ uptr->filename = NULL; uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ return SCPE_OK; } /* Stub examine and deposit */ t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { return SCPE_NOFNC; } t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { return SCPE_NOFNC; } /* Output message to socket or line descriptor */ void tmxr_msg (SOCKET sock, char *msg) { if (sock) sim_write_sock (sock, msg, strlen (msg)); return; } void tmxr_linemsg (TMLN *lp, char *msg) { int32 len; for (len = strlen (msg); len > 0; --len) tmxr_putc_ln (lp, *msg++); return; } /* Print connections - used only in named SHOW command */ void tmxr_fconns (FILE *st, TMLN *lp, int32 ln) { if (ln >= 0) fprintf (st, "line %d: ", ln); if (lp->conn) { int32 o1, o2, o3, o4, hr, mn, sc; uint32 ctime; o1 = (lp->ipad >> 24) & 0xFF; o2 = (lp->ipad >> 16) & 0xFF; o3 = (lp->ipad >> 8) & 0xFF; o4 = (lp->ipad) & 0xFF; ctime = (sim_os_msec () - lp->cnms) / 1000; hr = ctime / 3600; mn = (ctime / 60) % 60; sc = ctime % 60; fprintf (st, "IP address %d.%d.%d.%d", o1, o2, o3, o4); if (ctime) fprintf (st, ", connected %02d:%02d:%02d\n", hr, mn, sc); } else fprintf (st, "line disconnected\n"); if (lp->txlog) fprintf (st, "Logging to %s\n", lp->txlogname); return; } /* Print statistics - used only in named SHOW command */ void tmxr_fstats (FILE *st, TMLN *lp, int32 ln) { static const char *enab = "on"; static const char *dsab = "off"; if (ln >= 0) fprintf (st, "line %d: ", ln); if (lp->conn) { fprintf (st, "input (%s) queued/total = %d/%d, ", (lp->rcve? enab: dsab), lp->rxbpi - lp->rxbpr, lp->rxcnt); fprintf (st, "output (%s) queued/total = %d/%d\n", (lp->xmte? enab: dsab), lp->txbpi - lp->txbpr, lp->txcnt); } else fprintf (st, "line disconnected\n"); return; } /* Disconnect line */ t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; int32 ln; t_stat r; if (mp == NULL) return SCPE_IERR; if (val) { /* = n form */ if (cptr == NULL) return SCPE_ARG; ln = (int32) get_uint (cptr, 10, mp->lines - 1, &r); if (r != SCPE_OK) return SCPE_ARG; lp = mp->ldsc + ln; } else { lp = tmxr_find_ldsc (uptr, 0, mp); if (lp == NULL) return SCPE_IERR; } if (lp->conn) { tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n"); tmxr_reset_ln (lp); } return SCPE_OK; } /* Enable logging for line */ t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; if (cptr == NULL) /* no file name? */ return SCPE_2FARG; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) /* close existing log */ tmxr_set_nolog (NULL, val, NULL, desc); lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */ if (lp->txlogname == NULL) /* can't? */ return SCPE_MEM; strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ lp->txlog = fopen (cptr, "ab"); /* open log */ if (lp->txlog == NULL) { /* error? */ free (lp->txlogname); /* free buffer */ return SCPE_OPENERR; } return SCPE_OK; } /* Disable logging for line */ t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; if (cptr) /* no arguments */ return SCPE_2MARG; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) { /* logging? */ fclose (lp->txlog); /* close log */ free (lp->txlogname); /* free namebuf */ lp->txlog = NULL; lp->txlogname = NULL; } return SCPE_OK; } /* Show logging status for line */ t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) fprintf (st, "logging to %s", lp->txlogname); else fprintf (st, "no logging"); return SCPE_OK; } /* Find line descriptor. Note: This routine may be called with a UNIT that does not belong to the device indicated in the TMXR structure. That is, the multiplexer lines may belong to a device other than the one attached to the socket (the HP 2100 MUX device is one example). Therefore, we must look up the device from the unit at each call, rather than depending on the DPTR stored in the TMXR. */ TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp) { if (uptr) { /* called from SET? */ DEVICE *dptr = find_dev_from_unit (uptr); /* find device */ if (dptr == NULL) /* what?? */ return NULL; val = (int32) (uptr - dptr->units); /* implicit line # */ } if ((val < 0) || (val >= mp->lines)) /* invalid line? */ return NULL; return mp->ldsc + val; /* line descriptor */ } /* Set the line connection order. Example command for eight-line multiplexer: SET LINEORDER=1;5;2-4;7 Resulting connection order: 1,5,2,3,4,7,0,6. Parameters: - uptr = (not used) - val = (not used) - cptr = pointer to first character of range specification - desc = pointer to multiplexer's TMXR structure On entry, cptr points to the value portion of the command string, which may be either a semicolon-separated list of line ranges or the keyword ALL. If a line connection order array is not defined in the multiplexer descriptor, the command is rejected. If the specified range encompasses all of the lines, the first value of the connection order array is set to -1 to indicate sequential connection order. Otherwise, the line values in the array are set to the order specified by the command string. All values are populated, first with those explicitly specified in the command string, and then in ascending sequence with those not specified. If an error occurs, the original line order is not disturbed. */ t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; char *tptr; t_addr low, high, max = (t_addr) mp->lines - 1; int32 *list; t_bool *set; uint32 line, idx = 0; t_stat result = SCPE_OK; if (mp->lnorder == NULL) /* line connection order undefined? */ return SCPE_NXPAR; /* "Non-existent parameter" error */ else if ((cptr == NULL) || (*cptr == '\0')) /* line range not supplied? */ return SCPE_MISVAL; /* "Missing value" error */ list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate new line order array */ if (list == NULL) /* allocation failed? */ return SCPE_MEM; /* report it */ set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate line set tracking array */ if (set == NULL) { /* allocation failed? */ free (list); /* free successful list allocation */ return SCPE_MEM; /* report it */ } tptr = cptr + strlen (cptr); /* append a semicolon */ *tptr++ = ';'; /* to the command string */ *tptr = '\0'; /* to make parsing easier for get_range */ while (*cptr) { /* parse command string */ cptr = get_range (NULL, cptr, &low, &high, 10, max, ';'); /* get a line range */ if (cptr == NULL) { /* parsing error? */ result = SCPE_ARG; /* "Invalid argument" error */ break; } else if ((low > max) || (high > max)) { /* line out of range? */ result = SCPE_SUB; /* "Subscript out of range" error */ break; } else if ((low == 0) && (high == max)) { /* entire line range specified? */ list [0] = -1; /* set sequential order flag */ idx = (uint32) max + 1; /* indicate no fill-in needed */ break; } else for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */ if (set [line] == FALSE) { /* not already specified? */ set [line] = TRUE; /* now it is */ list [idx] = line; /* add line to connection order */ idx = idx + 1; /* bump "specified" count */ } } if (result == SCPE_OK) { /* assignment successful? */ if (idx <= max) /* any lines not specified? */ for (line = 0; line <= max; line++) /* fill them in sequentially */ if (set [line] == FALSE) { /* specified? */ list [idx] = line; /* no, so add it */ idx = idx + 1; } memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */ } free (list); /* free list allocation */ free (set); /* free set allocation */ return result; } /* Show line connection order. Parameters: - st = stream on which output is to be written - uptr = (not used) - val = (not used) - desc = pointer to multiplexer's TMXR structure If a connection order array is not defined in the multiplexer descriptor, the command is rejected. If the first value of the connection order array is set to -1, then the connection order is sequential. Otherwise, the line values in the array are printed as a semicolon-separated list. Ranges are printed where possible to shorten the output. */ t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 i, j, low, last; TMXR *mp = (TMXR *) desc; int32 *iptr = mp->lnorder; t_bool first = TRUE; if (iptr == NULL) /* connection order undefined? */ return SCPE_NXPAR; /* "Non-existent parameter" error */ if (*iptr < 0) /* sequential order indicated? */ fprintf (st, "Order=0-%d\n", mp->lines - 1); /* print full line range */ else { low = last = *iptr++; /* set first line value */ for (j = 1; j <= mp->lines; j++) { /* print remaining lines in order list */ if (j < mp->lines) /* more lines to process? */ i = *iptr++; /* get next line in list */ else /* final iteration */ i = -1; /* get "tie-off" value */ if (i != last + 1) { /* end of a range? */ if (first) { /* first line to print? */ fputs ("Order=", st); /* print header */ first = FALSE; } else /* not first line printed */ fputc (';', st); /* print separator */ if (low == last) /* range null? */ fprintf (st, "%d", last); /* print single line value */ else /* range established */ fprintf (st, "%d-%d", low, last); /* print start and end line */ low = i; /* start new range */ } last = i; /* note value for range check */ } } if (first == FALSE) /* sanity check for lines == 0 */ fputc ('\n', st); return SCPE_OK; } /* Show summary processor */ t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc) { TMXR *mp = (TMXR *) desc; int32 i, t; if (mp == NULL) return SCPE_IERR; for (i = t = 0; i < mp->lines; i++) t = t + (mp->ldsc[i].conn != 0); if (t == 1) fprintf (st, "1 connection"); else fprintf (st, "%d connections", t); return SCPE_OK; } /* Show conn/stat processor */ t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, void *desc) { TMXR *mp = (TMXR *) desc; int32 i, any; if (mp == NULL) return SCPE_IERR; for (i = any = 0; i < mp->lines; i++) { if (mp->ldsc[i].conn) { any++; if (val) tmxr_fconns (st, &mp->ldsc[i], i); else tmxr_fstats (st, &mp->ldsc[i], i); } } if (any == 0) fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n")); return SCPE_OK; } /* Show number of lines */ t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc) { TMXR *mp = (TMXR *) desc; if (mp == NULL) return SCPE_IERR; fprintf (st, "lines=%d", mp->lines); return SCPE_OK; } simh-3.8.1/SDS/0000755000175000017500000000000011112630734011270 5ustar vlmvlmsimh-3.8.1/SDS/sds_lp.c0000644000175000017500000003116211112630734012723 0ustar vlmvlm/* sds_lp.c: SDS 940 line printer simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt line printer 24-Nov-08 RMS Fixed loss of carriage control position on space op 19-Jan-07 RMS Added UNIT_TEXT flag 25-Apr-03 RMS Revised for extended file support */ #include "sds_defs.h" #define LPT_V_LN 9 #define LPT_M_LN 07 #define LPT_GETLN(x) (((x) >> LPT_V_LN) & LPT_M_LN) #define CHP(ch,val) ((val) & (1 << (ch))) /* CCL chan test */ #define SET_XFR 1 /* set xfr */ #define SET_EOR 2 /* print, set eor */ #define SET_SPC 4 /* space */ extern char sds_to_ascii[64]; extern uint32 xfr_req; extern int32 stop_invins, stop_invdev, stop_inviop; int32 lpt_spc = 0; /* space instr */ int32 lpt_sta = 0; /* timeout state */ int32 lpt_bptr = 0; /* line buf ptr */ int32 lpt_err = 0; /* error */ int32 lpt_ccl = 1, lpt_ccp = 0; /* cctl lnt, ptr */ int32 lpt_ctime = 10; /* char time */ int32 lpt_ptime = 1000; /* print time */ int32 lpt_stime = 10000; /* space time */ int32 lpt_stopioe = 1; /* stop on err */ char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */ uint8 lpt_cct[CCT_LNT] = { 0377 }; /* car ctl tape */ DSPT lpt_tplt[] = { /* template */ { 1, 0 }, { 0, 0 } }; DEVICE lpt_dev; t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); t_stat lpt_crctl (UNIT *uptr, int32 ch); t_stat lpt_space (UNIT *uptr, int32 cnt); t_stat lpt_status (UNIT *uptr); t_stat lpt_bufout (UNIT *uptr); void lpt_end_op (int32 fl); t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { CHAN_W, DEV_LPT, XFR_LPT, lpt_tplt, &lpt }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG lpt_reg[] = { { BRDATA (BUF, lpt_buf, 8, 8, LPT_WIDTH) }, { DRDATA (BPTR, lpt_bptr, 8), PV_LEFT }, { FLDATA (XFR, xfr_req, XFR_V_LPT) }, { FLDATA (ERR, lpt_err, 0) }, { ORDATA (STA, lpt_sta, 3) }, { BRDATA (CCT, lpt_cct, 8, 8, CCT_LNT) }, { DRDATA (CCTP, lpt_ccp, 8), PV_LEFT }, { DRDATA (CCTL, lpt_ccl, 8), REG_RO + PV_LEFT }, { ORDATA (SPCINST, lpt_spc, 24) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (CTIME, lpt_ctime, 24), REG_NZ + PV_LEFT }, { DRDATA (PTIME, lpt_ptime, 24), REG_NZ + PV_LEFT }, { DRDATA (STIME, lpt_stime, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { NULL } }; MTAB lpt_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &lpt_reset, NULL, &lpt_attach, NULL, &lpt_dib, DEV_DISABLE }; /* Line printer routine conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result The line printer is an asynchronous output device, that is, it can never set the channel rate error flag. */ t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat) { int32 i, t, new_ch; char asc; switch (fnc) { /* case function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != lpt_dib.chan) /* wrong chan? */ return SCPE_IERR; for (i = 0; i < LPT_WIDTH; i++) /* clr buffer */ lpt_buf[i] = 0; lpt_bptr = 0; /* clr buf ptr */ lpt_err = 0; /* err = 0 */ xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ lpt_sta = lpt_sta | SET_XFR; /* need xfr */ sim_activate (&lpt_unit, lpt_ctime); /* start timer */ break; case IO_EOM1: /* EOM mode 1 */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != lpt_dib.chan) /* wrong chan? */ CRETIOP; if (inst & 0400) { /* space? */ lpt_spc = inst; /* save instr */ lpt_sta = lpt_sta | SET_SPC; /* need space */ sim_cancel (&lpt_unit); /* cancel timer */ sim_activate (&lpt_unit, lpt_stime); /* start timer */ } break; case IO_DISC: /* disconnect */ lpt_end_op (0); /* normal term */ return lpt_bufout (&lpt_unit); /* dump output */ case IO_WREOR: /* write eor */ lpt_sta = (lpt_sta | SET_EOR) & ~SET_XFR; /* need eor */ sim_activate (&lpt_unit, lpt_ptime); /* start timer */ break; case IO_SKS: /* SKS */ new_ch = I_GETSKCH (inst); /* sks chan */ if (new_ch != lpt_dib.chan) /* wrong chan? */ return SCPE_IERR; t = I_GETSKCND (inst); /* sks cond */ if (((t == 020) && (!CHP (7, lpt_cct[lpt_ccp]))) || /* 14062: !ch 7 */ ((t == 010) && (lpt_unit.flags & UNIT_ATT)) || /* 12062: !online */ (t == 004) && !lpt_err) /* 11062: !err */ *dat = 1; break; case IO_WRITE: /* write */ asc = sds_to_ascii[(*dat) & 077]; /* convert data */ xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ if (lpt_bptr < LPT_WIDTH) /* store data */ lpt_buf[lpt_bptr++] = asc; lpt_sta = lpt_sta | SET_XFR; /* need xfr */ sim_activate (&lpt_unit, lpt_ctime); /* start ch timer */ break; default: CRETINS; } return SCPE_OK; } /* Unit service and write */ t_stat lpt_svc (UNIT *uptr) { t_stat r = SCPE_OK; if (lpt_sta & SET_XFR) /* need lpt xfr? */ chan_set_ordy (lpt_dib.chan); if (lpt_sta & SET_EOR) { /* printing? */ chan_set_flag (lpt_dib.chan, CHF_EOR); /* set eor flg */ r = lpt_bufout (uptr); /* output buf */ } if (lpt_sta & SET_SPC) { /* spacing? */ if (uptr->flags & UNIT_ATT) { /* attached? */ int32 ln = LPT_GETLN (lpt_spc); /* get lines, ch */ if (lpt_spc & 0200) /* n lines? */ lpt_space (uptr, ln); /* upspace */ else lpt_crctl (uptr, ln); /* carriage ctl */ } r = lpt_status (uptr); /* update status */ } lpt_sta = 0; /* clear state */ return r; } /* Trim and output buffer */ t_stat lpt_bufout (UNIT *uptr) { int32 i; if ((uptr->flags & UNIT_ATT) && lpt_bptr) { /* attached? */ for (i = LPT_WIDTH - 1; (i >= 0) && (lpt_buf[i] == ' '); i--) lpt_buf[i] = 0; /* trim line */ fputs (lpt_buf, uptr->fileref); /* write line */ lpt_bptr = 0; } return lpt_status (uptr); /* return status */ } /* Status update after I/O */ t_stat lpt_status (UNIT *uptr) { if (uptr->flags & UNIT_ATT) { /* attached? */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* I/O error? */ lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */ perror ("LPT I/O error"); /* print msg */ clearerr (uptr->fileref); return SCPE_IOERR; /* ret error */ } } else { lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */ CRETIOE (lpt_stopioe, SCPE_UNATT); /* ret error */ } return SCPE_OK; } /* Terminate LPT operation */ void lpt_end_op (int32 fl) { if (fl) /* set flags */ chan_set_flag (lpt_dib.chan, fl); xfr_req = xfr_req & ~XFR_LPT; /* clear xfr */ sim_cancel (&lpt_unit); /* stop */ if (fl & CHF_ERR) { /* error? */ chan_disc (lpt_dib.chan); /* disconnect */ lpt_err = 1; /* set lpt err */ } return; } /* Carriage control */ t_stat lpt_crctl (UNIT *uptr, int32 ch) { int32 i, j; if ((ch == 1) && CHP (ch, lpt_cct[0])) { /* top of form? */ fputs ("\f\n", uptr->fileref); /* ff + nl */ lpt_ccp = 0; /* top of page */ return SCPE_OK; } for (i = 1; i < lpt_ccl + 1; i++) { /* sweep thru cct */ lpt_ccp = (lpt_ccp + 1) % lpt_ccl; /* adv pointer */ if (CHP (ch, lpt_cct[lpt_ccp])) { /* chan punched? */ for (j = 0; j < i; j++) fputc ('\n', uptr->fileref); return SCPE_OK; } } return STOP_CCT; /* runaway channel */ } /* Spacing */ t_stat lpt_space (UNIT *uptr, int32 cnt) { int32 i; if (cnt == 0) fputc ('\r', uptr->fileref); else { for (i = 0; i < cnt; i++) fputc ('\n', uptr->fileref); lpt_ccp = (lpt_ccp + cnt) % lpt_ccl; } return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { chan_disc (lpt_dib.chan); /* disconnect */ lpt_spc = 0; /* clr state */ lpt_sta = 0; xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ sim_cancel (&lpt_unit); /* deactivate */ return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { lpt_ccp = 0; /* top of form */ return attach_unit (uptr, cptr); } simh-3.8.1/SDS/sds_sys.c0000644000175000017500000005564211110640050013125 0ustar vlmvlm/* sds_sys.c: SDS 940 simulator interface Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include "sds_defs.h" #include #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) extern DEVICE cpu_dev; extern DEVICE chan_dev; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE tti_dev; extern DEVICE tto_dev; extern DEVICE lpt_dev; extern DEVICE rtc_dev; extern DEVICE drm_dev; extern DEVICE rad_dev; extern DEVICE dsk_dev; extern DEVICE mt_dev; extern DEVICE mux_dev, muxl_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint32 M[MAXMEMSIZE]; /* 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[] = "SDS 940"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &chan_dev, &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, &lpt_dev, &rtc_dev, &drm_dev, &rad_dev, &dsk_dev, &mt_dev, &mux_dev, &muxl_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "IO device not ready", "HALT instruction", "Breakpoint", "Invalid IO device", "Invalid instruction", "Invalid I/O operation", "Nested indirects exceed limit", "Nested EXU's exceed limit", "Memory management trap during interrupt", "Memory management trap during trap", "Trap instruction not BRM", "RTC instruction not MIN or SKR", "Interrupt vector zero", "Runaway carriage control tape" }; /* Character conversion tables */ const char sds_to_ascii[64] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '=', '\'', ':', '>', '%', /* 17 = check mark */ '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */ '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */ '_', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '?', ',', '(', '~', '\\', '#' /* 72 = rec mark */ }; /* 75 = squiggle, 77 = del */ const char ascii_to_sds[128] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ 032, 072, -1, -1, -1, 052, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 012, 052, -1, 077, 053, 017, -1, 014, /* 40 - 77 */ 074, 034, 054, 020, 073, 040, 033, 061, 000, 001, 002, 003, 004, 005, 006, 007, 010, 011, 015, 056, 036, 013, 016, 072, 037, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */ 030, 031, 041, 042, 043, 044, 045, 046, 047, 050, 051, 062, 063, 064, 065, 066, 067, 070, 071, 035, 076, 055, 057, 060, 000, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */ 030, 031, 041, 042, 043, 044, 045, 046, 047, 050, 051, 062, 063, 064, 065, 066, 067, 070, 071, -1, -1, -1, -1, -1 }; const char odd_par[64] = { 0100, 0001, 0002, 0103, 0004, 0105, 0106, 0007, 0010, 0111, 0112, 0013, 0114, 0015, 0016, 0117, 0020, 0121, 0122, 0023, 0124, 0025, 0026, 0127, 0130, 0031, 0032, 0133, 0034, 0135, 0136, 0037, 0040, 0141, 0142, 0043, 0144, 0045, 0046, 0147, 0150, 0051, 0052, 0153, 0054, 0155, 0156, 0057, 0160, 0061, 0062, 0163, 0064, 0165, 0166, 0067, 0070, 0171, 0172, 0073, 0174, 0075, 0076, 0177 }; /* Load carriage control tape A carriage control tape consists of entries of the form (repeat count) column number,column number,column number,... The CCT entries are stored in lpt_cct[0:lnt-1], lpt_ccl contains the number of entries */ t_stat sim_load_cct (FILE *fileref) { int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; t_stat r; extern int32 lpt_ccl, lpt_ccp, lpt_cct[CCT_LNT]; char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE]; ptr = 0; for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ mask = 0; if (*cptr == '(') { /* repeat count? */ cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ if (r != SCPE_OK) return SCPE_FMT; } else rpt = 1; while (*cptr != 0) { /* get col no's */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ col = get_uint (gbuf, 10, 7, &r); /* column number */ if (r != SCPE_OK) return SCPE_FMT; mask = mask | (1 << col); /* set bit */ } for ( ; rpt > 0; rpt--) { /* store vals */ if (ptr >= CCT_LNT) return SCPE_FMT; cctbuf[ptr++] = mask; } } if (ptr == 0) return SCPE_FMT; lpt_ccl = ptr; lpt_ccp = 0; for (rpt = 0; rpt < lpt_ccl; rpt++) lpt_cct[rpt] = cctbuf[rpt]; return SCPE_OK; } /* Load command. -l means load a line printer tape. Otherwise, load a bootstrap paper tape. */ int32 get_word (FILE *fileref, int32 *ldr) { int32 i, c, wd; for (i = wd = 0; i < 4; ) { if ((c = fgetc (fileref)) == EOF) return -1; if ((c == 0) && (*ldr == 0)) return -1; if (c == 0) continue; *ldr = 0; wd = (wd << 6) | (c & 077); i++; } return wd; } t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 i, wd, buf[8]; int32 ldr = 1; extern int32 sim_switches; extern uint32 P; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; if (sim_switches & SWMASK ('L')) return sim_load_cct (fileref); for (i = 0; i < 8; i++) { /* read boot */ if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_FMT; buf[i] = wd; } if ((buf[0] != 023200012) || /* 2 = WIM 12,2 */ (buf[1] != 004100002) || /* 3 = BRX 2 */ (buf[2] != 007100011) || /* 4 = LDX 11 */ (buf[3] != 023200000) || /* 5 = WIM 0,2 */ (buf[4] != 004021000) || /* 6 = SKS 21000 */ (buf[5] != 004100005)) /* 7 = BRX 5 */ return SCPE_FMT; for (i = 0; i < 8; i++) /* copy boot */ M[i + 2] = buf[i]; if (I_GETOP (buf[6]) == BRU) P = buf[6] & VA_MASK; for (i = buf[7] & VA_MASK; i <= VA_MASK; i++) { /* load data */ if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_OK; M[i] = wd; } return SCPE_NXM; } /* Symbol tables */ #define I_V_FL 24 /* inst class */ #define I_M_FL 017 /* class mask */ #define I_V_NPN 000 /* no operand */ #define I_V_PPO 001 /* POP */ #define I_V_IOI 002 /* IO */ #define I_V_MRF 003 /* memory reference */ #define I_V_REG 004 /* register change */ #define I_V_SHF 005 /* shift */ #define I_V_OPO 006 /* opcode only */ #define I_V_CHC 007 /* chan cmd */ #define I_V_CHT 010 /* chan test */ #define I_NPN (I_V_NPN << I_V_FL) #define I_PPO (I_V_PPO << I_V_FL) #define I_IOI (I_V_IOI << I_V_FL) #define I_MRF (I_V_MRF << I_V_FL) #define I_REG (I_V_REG << I_V_FL) #define I_SHF (I_V_SHF << I_V_FL) #define I_OPO (I_V_OPO << I_V_FL) #define I_CHC (I_V_CHC << I_V_FL) #define I_CHT (I_V_CHT << I_V_FL) static const int32 masks[] = { 037777777, 010000000, 017700000, 017740000, 017700000, 017774000, 017700000, 017377677, 027737677 }; static const char *opcode[] = { "POP", "EIR", "DIR", "ROV", "REO", "OTO", "OVT", "IDT", "IET", "BPT4", "BPT3", "BPT2", "BPT1", "CLAB", "ABC", "BAC", "XAB", "XXB", "STE", "LDE", "XEE", "CLEAR", "HLT", "BRU", "EOM", "EOD", "MIY", "BRI", "MIW", "POT", "ETR", "MRG", "EOR", "NOP", "EXU", "YIM", "WIM", "PIN", "STA", "STB", "STX", "SKS", "BRX", "BRM", "SKE", "BRR", "SKB", "SKN", "SUB", "ADD", "SUC", "ADC", "SKR", "MIN", "XMA", "ADM", "MUL", "DIV", "SKM", "LDX", "SKA", "SKG", "SKD", "LDB", "LDA", "EAX", "BRU*", "MIY*", "BRI*", "MIW*", "POT*", "ETR*", "MRG*", "EOR*", "EXU*", "YIM*", "WIM*", "PIN*", "STA*", "STB*", "STX*", "BRX*", "BRM*", "SKE*", "BRR*", "SKB*", "SKN*", "SUB*", "ADD*", "SUC*", "ADC*", "SKR*", "MIN*", "XMA*", "ADM*", "MUL*", "DIV*", "SKM*", "LDX*", "SKA*", "SKG*", "SKD*", "LDB*", "LDA*", "EAX*", "RSH", "RCY", "LRSH", "LSH", "NOD", "LCY", "RSH*", "LSH*", "ALC", "DSC", "ASC", "TOP", "CAT", "CET", "CZT", "CIT", "CLA", "CLB", "CAB", /* encode only */ "CBA", "CBX", "CXB", "XPO", "CXA", "CAX", "CNA", "CLX", NULL, NULL }; static const int32 opc_val[] = { 010000000+I_PPO, 000220002+I_NPN, 000220004+I_NPN, 002200001+I_NPN, 002200010+I_NPN, 002200100+I_NPN, 002200101+I_NPN, 004020002+I_NPN, 004020004+I_NPN, 004020040+I_NPN, 004020100+I_NPN, 004020200+I_NPN, 004020400+I_NPN, 004600003+I_NPN, 004600005+I_NPN, 004600012+I_NPN, 004600014+I_NPN, 004600060+I_NPN, 004600122+I_NPN, 004600140+I_NPN, 004600160+I_NPN, 024600003+I_NPN, 000000000+I_NPN, 000100000+I_MRF, 000200000+I_IOI, 000600000+I_IOI, 001000000+I_MRF, 001100000+I_MRF, 001200000+I_MRF, 001300000+I_MRF, 001400000+I_MRF, 001600000+I_MRF, 001700000+I_MRF, 002000000+I_OPO, 002300000+I_MRF, 003000000+I_MRF, 003200000+I_MRF, 003300000+I_MRF, 003500000+I_MRF, 003600000+I_MRF, 003700000+I_MRF, 004000000+I_IOI, 004100000+I_MRF, 004300000+I_MRF, 005000000+I_MRF, 005100000+I_MRF, 005200000+I_MRF, 005300000+I_MRF, 005400000+I_MRF, 005500000+I_MRF, 005600000+I_MRF, 005700000+I_MRF, 006000000+I_MRF, 006100000+I_MRF, 006200000+I_MRF, 006300000+I_MRF, 006400000+I_MRF, 006500000+I_MRF, 007000000+I_MRF, 007100000+I_MRF, 007200000+I_MRF, 007300000+I_MRF, 007400000+I_MRF, 007500000+I_MRF, 007600000+I_MRF, 007700000+I_MRF, 000140000+I_MRF, 001040000+I_MRF, 001140000+I_MRF, 001240000+I_MRF, 001340000+I_MRF, 001440000+I_MRF, 001640000+I_MRF, 001740000+I_MRF, 002340000+I_MRF, 003040000+I_MRF, 003240000+I_MRF, 003340000+I_MRF, 003540000+I_MRF, 003640000+I_MRF, 003740000+I_MRF, 004140000+I_MRF, 004340000+I_MRF, 005040000+I_MRF, 005140000+I_MRF, 005240000+I_MRF, 005340000+I_MRF, 005440000+I_MRF, 005540000+I_MRF, 005640000+I_MRF, 005740000+I_MRF, 006040000+I_MRF, 006140000+I_MRF, 006240000+I_MRF, 006340000+I_MRF, 006440000+I_MRF, 006540000+I_MRF, 007040000+I_MRF, 007140000+I_MRF, 007240000+I_MRF, 007340000+I_MRF, 007440000+I_MRF, 007540000+I_MRF, 007640000+I_MRF, 007740000+I_MRF, 006600000+I_SHF, 006620000+I_SHF, 006624000+I_SHF, 006700000+I_SHF, 006710000+I_SHF, 006720000+I_SHF, 006640000+I_MRF, 006740000+I_MRF, 000250000+I_CHC, 000200000+I_CHC, 000212000+I_CHC, 000214000+I_CHC, 004014000+I_CHT, 004011000+I_CHT, 004012000+I_CHT, 004010400+I_CHT, 004600001+I_REG, 004600002+I_REG, 004600004+I_REG, 004600010+I_REG, 004600020+I_REG, 004600040+I_REG, 004600100+I_REG, 004600200+I_REG, 004600400+I_REG, 004601000+I_REG, 024600000+I_REG, 004600000+I_REG, -1 }; static const char *chname[] = { "W", "Y", "C", "D", "E", "F", "G", "H", NULL }; /* Register change decode Inputs: *of = output stream inst = mask bits */ void fprint_reg (FILE *of, int32 inst) { int32 i, j, sp; inst = inst & ~(I_M_OP << I_V_OP); /* clear opcode */ for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((j == I_V_REG) && (opc_val[i] & inst)) { /* reg class? */ inst = inst & ~opc_val[i]; /* mask bit set? */ fprintf (of, (sp? " %s": "%s"), opcode[i]); sp = 1; } } return; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: return = status code */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 i, j, ch; int32 inst, op, tag, va, shf, nonop; inst = val[0]; /* get inst */ op = I_GETOP (inst); /* get fields */ tag = (inst >> 21) & 06; va = inst & VA_MASK; shf = inst & I_SHFMSK; nonop = inst & 077777; if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (sw & SWMASK ('C')) { /* character? */ fprintf (of, "%c", sds_to_ascii[(inst >> 18) & 077]); fprintf (of, "%c", sds_to_ascii[(inst >> 12) & 077]); fprintf (of, "%c", sds_to_ascii[(inst >> 6) & 077]); fprintf (of, "%c", sds_to_ascii[inst & 077]); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: /* no operands */ case I_V_OPO: /* opcode only */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_SHF: /* shift */ fprintf (of, "%s %-o", opcode[i], shf); if (tag) fprintf (of, ",%-o", tag); break; case I_V_PPO: /* pop */ fprintf (of, "POP %-o,%-o", op, nonop); if (tag) fprintf (of, ",%-o", tag); break; case I_V_IOI: /* I/O */ fprintf (of, "%s %-o", opcode[i], nonop); if (tag) fprintf (of, ",%-o", tag); break; case I_V_MRF: /* mem ref */ fprintf (of, "%s %-o", opcode[i], va); if (tag) fprintf (of, ",%-o", tag); break; case I_V_REG: /* reg change */ fprint_reg (of, inst); /* decode */ break; case I_V_CHC: /* chan cmd */ ch = I_GETEOCH (inst); /* get chan */ fprintf (of, "%s %s", opcode[i], chname[ch]); break; case I_V_CHT: /* chan test */ ch = I_GETSKCH (inst); /* get chan */ fprintf (of, "%s %s", opcode[i], chname[ch]); break; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* Get (optional) tag Inputs: *cptr = pointer to input string *tag = pointer to tag Outputs: cptr = updated pointer to input string */ char *get_tag (char *cptr, t_value *tag) { char *tptr, gbuf[CBUFSIZE]; t_stat r; tptr = get_glyph (cptr, gbuf, 0); /* get next field */ *tag = get_uint (gbuf, 8, 07, &r) << I_V_TAG; /* parse */ if (r == SCPE_OK) /* ok? advance */ return tptr; *tag = 0; return cptr; /* no change */ } /* 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) { int32 i, j, k; t_value d, tag; t_stat r; char gbuf[CBUFSIZE]; while (isspace (*cptr)) cptr++; for (i = 1; (i < 4) && (cptr[i] != 0); i++) { if (cptr[i] == 0) { for (j = i + 1; j <= 4; j++) cptr[j] = 0; } } if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0] | 0200; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; for (i = j = 0, val[0] = 0; i < 4; i++) { if (cptr[i] == 0) /* latch str end */ j = 1; k = ascii_to_sds[cptr[i] & 0177]; /* cvt char */ if (j || (k < 0)) /* bad, end? spc */ k = 0; val[0] = (val[0] << 6) | k; } 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_NPN: case I_V_OPO: /* opcode only */ break; case I_V_SHF: /* shift */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ d = get_uint (gbuf, 8, I_SHFMSK, &r); /* shift count */ if (r != SCPE_OK) return SCPE_ARG; cptr = get_tag (cptr, &tag); /* get opt tag */ val[0] = val[0] | d | tag; break; case I_V_PPO: /* pop */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ d = get_uint (gbuf, 8, 077, &r); /* opcode */ if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; /* fall thru */ case I_V_IOI: /* I/O */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ d = get_uint (gbuf, 8, 077777, &r); /* 15b address */ if (r != SCPE_OK) return SCPE_ARG; cptr = get_tag (cptr, &tag); /* get opt tag */ val[0] = val[0] | d | tag; break; case I_V_MRF: /* mem ref */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ d = get_uint (gbuf, 8, VA_MASK, &r); /* virt address */ if (r != SCPE_OK) return SCPE_ARG; cptr = get_tag (cptr, &tag); /* get opt tag */ val[0] = val[0] | d | tag; break; case I_V_REG: /* register */ 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 (I_GETOP (k) != RCH) return SCPE_ARG; val[0] = val[0] | k; } else { d = get_uint (gbuf, 8, 077777, &r); if (r != SCPE_OK) return SCPE_ARG; else val[0] = val[0] | d; } } break; case I_V_CHC: case I_V_CHT: /* channel */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ for (i = 0; (chname[i] != NULL) && (strcmp (chname[i], gbuf) != 0); i++); if (chname[i] != NULL) /* named chan */ d = i; else { d = get_uint (gbuf, 8, NUM_CHAN - 1, &r); if (r != SCPE_OK) /* numbered chan */ return SCPE_ARG; } val[0] = val[0] | ((j == I_V_CHC)? I_SETEOCH (d): I_SETSKCH (d)); break; } /* end case */ if (*cptr != 0) /* junk at end? */ return SCPE_ARG; return SCPE_OK; } simh-3.8.1/SDS/sds_cpu.c0000644000175000017500000017740711111666356013124 0ustar vlmvlm/* sds_cpu.c: SDS 940 CPU simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu central processor rtc real time clock 28-Apr-07 RMS Removed clock initialization 29-Dec-06 RMS Fixed breakpoint variable declarations 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Nov-04 RMS Added instruction history 01-Mar-03 RMS Added SET/SHOW RTC FREQ support The system state for the SDS 940 is: A<0:23> A register B<0:23> B register X<0:23> X (index) register OV overflow indicator P<0:13> program counter nml_mode compatible (1) vs 940 (0) mode usr_mode user (1) vs monitor (0) mode RL1<0:23> user map low RL2<0:23> user map high RL4<12:23> monitor map high EM2<0:2> memory extension, block 2 EM3<0:2> memory extension, block 3 bpt breakpoint switches The SDS 940 has three instruction format -- memory reference, register change, and I/O. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | U| X| P| opcode |IN| address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ U force user mode addressing (monitor mode only) X indexed P opcode is a programmed operator opcode opcode IN indirect addressing address virtual address Virtual addresses are 14b. Depending on the operating mode (normal, user, or monitor), virtual addresses are translated to 15b or 16b physical addresses. normal virtual [000000:017777] are unmapped EM2 and EM3 extend virtual [020000:037777] to 15b user RL1 and RL2 map virtual [000000:037777] to 16b monitor virtual [000000:017777] are unmapped EM2 extends virtual [020000:027777] to 15b RL4 maps virtual [030000:037777] to 16b The register change format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0| m| 0| opcode | microcoded register change instruction | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The I/O format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0|CH| 0| opcode |mode | I/O function | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ This routine is the instruction decode routine for the SDS 940. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered invalid instruction and stop_invins flag set invalid I/O device and stop_invdev flag set invalid I/O operation and stop_inviop flag set I/O error in I/O simulator indirect loop exceeding limit EXU loop exceeding limit mapping exception in interrupt or trap instruction 2. Interrupts. The interrupt structure consists of the following: int_req interrupt requests (low bit reserved) api_lvl active interrupt levels int_reqhi highest interrupt request api_lvlhi highest interrupt service (0 if none) ion interrupt enable ion_defer interrupt defer (one instruction) 3. Channels. The SDS 940 has a channel-based I/O structure. Each channel is represented by a set of registers. Channels test the I/O transfer requests from devices, which are kept in xfr_req. 4. Non-existent memory. On the SDS 940, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 5. Adding I/O devices. These modules must be modified: sds_defs.h add interrupt, transfer, and alert definitions sds_io.c add alert dispatches aldisp sds_sys.c add pointer to data structures to sim_devices */ #include "sds_defs.h" #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_GENIE + 1) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define HIST_XCT 1 /* instruction */ #define HIST_INT 2 /* interrupt cycle */ #define HIST_TRP 3 /* trap cycle */ #define HIST_MIN 64 #define HIST_MAX 65536 #define HIST_NOEA 0x40000000 typedef struct { uint32 typ; uint32 pc; uint32 ir; uint32 a; uint32 b; uint32 x; uint32 ea; } InstHistory; uint32 M[MAXMEMSIZE] = { 0 }; /* memory */ uint32 A, B, X; /* registers */ uint32 P; /* program counter */ uint32 OV; /* overflow */ uint32 xfr_req = 0; /* xfr req */ uint32 ion = 0; /* int enable */ uint32 ion_defer = 0; /* int defer */ uint32 int_req = 0; /* int requests */ uint32 int_reqhi = 0; /* highest int request */ uint32 api_lvl = 0; /* api active */ uint32 api_lvlhi = 0; /* highest api active */ t_bool chan_req; /* chan request */ uint32 nml_mode = 1; /* normal mode */ uint32 usr_mode = 0; /* user mode */ uint32 mon_usr_trap = 0; /* mon-user trap */ uint32 EM2 = 2, EM3 = 3; /* extension registers */ uint32 RL1, RL2, RL4; /* relocation maps */ uint32 bpt; /* breakpoint switches */ uint32 alert; /* alert dispatch */ uint32 em2_dyn, em3_dyn; /* extensions, dynamic */ uint32 usr_map[8]; /* user map, dynamic */ uint32 mon_map[8]; /* mon map, dynamic */ int32 ind_lim = 32; /* indirect limit */ int32 exu_lim = 32; /* EXU limit */ int32 cpu_genie = 0; /* Genie flag */ int32 cpu_astop = 0; /* address stop */ int32 stop_invins = 1; /* stop inv inst */ int32 stop_invdev = 1; /* stop inv dev */ int32 stop_inviop = 1; /* stop inv io op */ 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; /* instruction history */ int32 rtc_pie = 0; /* rtc pulse ie */ int32 rtc_tps = 60; /* rtc ticks/sec */ 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_type (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat Ea (uint32 wd, uint32 *va); t_stat EaSh (uint32 wd, uint32 *va); t_stat Read (uint32 va, uint32 *dat); t_stat Write (uint32 va, uint32 dat); void set_dyn_map (void); uint32 api_findreq (void); void api_dismiss (void); uint32 Add24 (uint32 s1, uint32 s2, uint32 cin); uint32 AddM24 (uint32 s1, uint32 s2); void Mul48 (uint32 mplc, uint32 mplr); void Div48 (uint32 dvdh, uint32 dvdl, uint32 dvr); void RotR48 (uint32 sc); void ShfR48 (uint32 sc, uint32 sgn); t_stat one_inst (uint32 inst, uint32 pc, uint32 mode); void inst_hist (uint32 inst, uint32 pc, uint32 typ); t_stat rtc_inst (uint32 inst); t_stat rtc_svc (UNIT *uptr); t_stat rtc_reset (DEVICE *dptr); t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_bool io_init (void); extern t_stat op_wyim (uint32 inst, uint32 *dat); extern t_stat op_miwy (uint32 inst, uint32 dat); extern t_stat op_pin (uint32 *dat); extern t_stat op_pot (uint32 dat); extern t_stat op_eomd (uint32 inst); extern t_stat op_sks (uint32 inst, uint32 *skp); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (P, P, 14) }, { ORDATA (A, A, 24) }, { ORDATA (B, B, 24) }, { ORDATA (X, X, 24) }, { FLDATA (OV, OV, 0) }, { ORDATA (EM2, EM2, 3) }, { ORDATA (EM3, EM3, 3) }, { ORDATA (RL1, RL1, 24) }, { ORDATA (RL2, RL2, 24) }, { ORDATA (RL4, RL4, 12) }, { FLDATA (NML, nml_mode, 0) }, { FLDATA (USR, usr_mode, 0) }, { FLDATA (MONUSR, mon_usr_trap, 0) }, { FLDATA (ION, ion, 0) }, { FLDATA (INTDEF, ion_defer, 0) }, { ORDATA (INTREQ, int_req, 32) }, { ORDATA (APILVL, api_lvl, 32) }, { DRDATA (INTRHI, int_reqhi, 5) }, { DRDATA (APILHI, api_lvlhi, 5), REG_RO }, { ORDATA (XFRREQ, xfr_req, 32) }, { FLDATA (BPT1, bpt, 3) }, { FLDATA (BPT2, bpt, 2) }, { FLDATA (BPT3, bpt, 1) }, { FLDATA (BPT4, bpt, 0) }, { ORDATA (ALERT, alert, 6) }, { FLDATA (STOP_INVINS, stop_invins, 0) }, { FLDATA (STOP_INVDEV, stop_invdev, 0) }, { FLDATA (STOP_INVIOP, stop_inviop, 0) }, { DRDATA (INDLIM, ind_lim, 8), REG_NZ+PV_LEFT }, { DRDATA (EXULIM, exu_lim, 8), REG_NZ+PV_LEFT }, { BRDATA (PCQ, pcq, 8, 14, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_GENIE, 0, "standard peripherals", "SDS", &cpu_set_type }, { UNIT_GENIE, UNIT_GENIE, "Genie peripherals", "GENIE", &cpu_set_type }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 16, 1, 8, 24, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, 0 }; /* Clock data structures rtc_dev RTC device descriptor rtc_unit RTC unit rtc_reg RTC register list */ UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), 16000 }; REG rtc_reg[] = { { FLDATA (PIE, rtc_pie, 0) }, { DRDATA (TIME, rtc_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, rtc_tps, 8), PV_LEFT + REG_HRO }, { NULL } }; MTAB rtc_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &rtc_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &rtc_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &rtc_show_freq, NULL }, { 0 } }; DEVICE rtc_dev = { "RTC", &rtc_unit, rtc_reg, rtc_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &rtc_reset, NULL, NULL, NULL }; /* Interrupt tables */ static const uint32 api_mask[32] = { 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, 0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, 0xFFFFFF00, 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800, 0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, 0xFFFF0000, 0xFFFE0000, 0xFFFC0000, 0xFFF80000, 0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000, 0xFF000000, 0xFE000000, 0xFC000000, 0xF8000000, 0xF0000000, 0xE0000000, 0xC0000000, 0x80000000, 0x00000000 }; static const uint32 int_vec[32] = { 0, 0, 0, 0, VEC_FORK, VEC_DRM, VEC_MUXCF,VEC_MUXCO, VEC_MUXT, VEC_MUXR, VEC_HEOR, VEC_HZWC, VEC_GEOR, VEC_GZWC, VEC_FEOR, VEC_FZWC, VEC_EEOR, VEC_EZWC, VEC_DEOR, VEC_DZWC, VEC_CEOR, VEC_CZWC, VEC_WEOR, VEC_YEOR, VEC_WZWC, VEC_YZWC, VEC_RTCP, VEC_RTCS, VEC_IPAR, VEC_CPAR, VEC_PWRF, VEC_PWRO }; t_stat sim_instr (void) { extern int32 sim_interval; uint32 inst, tinst, pa, save_P, save_mode; t_stat reason, tr; /* Restore register state */ if (io_init ()) /* init IO; conflict? */ return SCPE_STOP; reason = 0; xfr_req = xfr_req & ~1; /* <0> reserved */ int_req = int_req & ~1; /* <0> reserved */ api_lvl = api_lvl & ~1; /* <0> reserved */ set_dyn_map (); /* set up mapping */ int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (cpu_astop) { /* debug stop? */ cpu_astop = 0; return SCPE_STOP; } if (sim_interval <= 0) { /* event queue? */ if (reason = sim_process_event ()) /* process */ break; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ } if (chan_req) { /* channel request? */ if (reason = chan_process ()) /* process */ break; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ } sim_interval = sim_interval - 1; /* count down */ if (ion && !ion_defer && int_reqhi) { /* int request? */ pa = int_vec[int_reqhi]; /* get vector */ if (pa == 0) { /* bad value? */ reason = STOP_ILLVEC; break; } tinst = ReadP (pa); /* get inst */ save_mode = usr_mode; /* save mode */ usr_mode = 0; /* switch to mon */ if (hst_lnt) /* record inst */ inst_hist (tinst, P, HIST_INT); if (pa != VEC_RTCP) { /* normal intr? */ tr = one_inst (tinst, P, save_mode); /* exec intr inst */ if (tr) { /* stop code? */ usr_mode = save_mode; /* restore mode */ reason = (tr > 0)? tr: STOP_MMINT; break; } api_lvl = api_lvl | (1u << int_reqhi); /* set level active */ api_lvlhi = int_reqhi; /* elevate api */ } else { /* clock intr */ tr = rtc_inst (tinst); /* exec RTC inst */ usr_mode = save_mode; /* restore mode */ if (tr) { /* stop code? */ reason = (tr > 0)? tr: STOP_MMINT; break; } int_req = int_req & ~INT_RTCP; /* clr clkp intr */ } int_reqhi = api_findreq (); /* recalc int req */ } else { /* normal instr */ if (sim_brk_summ && sim_brk_test (P, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } reason = Read (save_P = P, &inst); /* get instr */ P = (P + 1) & VA_MASK; /* incr PC */ if (reason == SCPE_OK) { /* fetch ok? */ ion_defer = 0; /* clear ion */ if (hst_lnt) inst_hist (inst, save_P, HIST_XCT); reason = one_inst (inst, save_P, usr_mode); /* exec inst */ if (reason > 0) { /* stop code? */ if (reason != STOP_HALT) P = save_P; if (reason == STOP_IONRDY) reason = 0; } } /* end if r == 0 */ if (reason < 0) { /* mm (fet or ex)? */ pa = -reason; /* get vector */ reason = 0; /* defang */ tinst = ReadP (pa); /* get inst */ if (I_GETOP (tinst) != BRM) { /* not BRM? */ reason = STOP_TRPINS; /* fatal err */ break; } save_mode = usr_mode; /* save mode */ usr_mode = 0; /* switch to mon */ mon_usr_trap = 0; if (hst_lnt) inst_hist (tinst, save_P, HIST_TRP); tr = one_inst (tinst, save_P, save_mode); /* trap inst */ if (tr) { /* stop code? */ usr_mode = save_mode; /* restore mode */ P = save_P; /* restore PC */ reason = (tr > 0)? tr: STOP_MMTRP; break; } } /* end if reason */ } /* end else int */ } /* end while */ /* Simulation halted */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Simulate one instruction */ t_stat one_inst (uint32 inst, uint32 pc, uint32 mode) { uint32 op, shf_op, va, dat; uint32 old_A, old_B, old_X; int32 i, exu_cnt, sc; t_stat r; exu_cnt = 0; /* init EXU count */ EXU_LOOP: op = I_GETOP (inst); /* get opcode */ if (inst & I_POP) { /* POP? */ dat = (EM3 << 18) | (EM2 << 15) | I_IND | pc; /* data to save */ if (nml_mode) { /* normal mode? */ dat = (OV << 23) | dat; /* ov in <0> */ WriteP (0, dat); } else if (usr_mode) { /* user mode? */ if (inst & I_USR) { /* SYSPOP? */ dat = I_USR | (OV << 21) | dat; /* ov in <2> */ WriteP (0, dat); usr_mode = 0; /* set mon mode */ } else { /* normal POP */ dat = (OV << 23) | dat; /* ov in <0> */ if (r = Write (0, dat)) return r; } } else { /* mon mode */ dat = (OV << 21) | dat; /* ov in <2> */ WriteP (0, dat); /* store return */ } PCQ_ENTRY; /* save PC */ P = 0100 | op; /* new PC */ OV = 0; /* clear ovflo */ return SCPE_OK; /* end POP */ } switch (op) { /* case on opcode */ /* Loads and stores */ case LDA: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &A)) /* get operand */ return r; break; case LDB: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &B)) /* get operand */ return r; break; case LDX: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &X)) /* get operand */ return r; break; case STA: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Write (va, A)) /* write operand */ return r; break; case STB: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Write (va, B)) /* write operand */ return r; break; case STX: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Write (va, X)) /* write operand */ return r; break; case EAX: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (nml_mode || usr_mode) /* normal or user? */ X = (X & ~VA_MASK) | (va & VA_MASK); /* only 14b */ else X = (X & ~XVA_MASK) | (va & XVA_MASK); /* mon, 15b */ break; case XMA: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (r = Write (va, A)) /* write A */ return r; A = dat; /* load A */ break; /* Arithmetic and logical */ case ADD: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; A = Add24 (A, dat, 0); /* add */ break; case ADC: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; OV = 0; /* clear overflow */ A = Add24 (A, dat, X >> 23); /* add with carry */ break; case SUB: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; A = Add24 (A, dat ^ DMASK, 1); /* subtract */ break; case SUC: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; OV = 0; /* clear overflow */ A = Add24 (A, dat ^ DMASK, X >> 23); /* sub with carry */ break; case ADM: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; dat = AddM24 (dat, A); /* mem + A */ if (r = Write (va, dat)) /* rewrite */ return r; break; case MIN: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; dat = AddM24 (dat, 1); /* mem + 1 */ if (r = Write (va, dat)) /* rewrite */ return r; break; case MUL: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; Mul48 (A, dat); /* multiply */ break; case DIV: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; Div48 (A, B, dat); /* divide */ break; case ETR: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; A = A & dat; /* and */ break; case MRG: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; A = A | dat; /* or */ break; case EOR: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; A = A ^ dat; /* xor */ break; /* Skips */ case SKE: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (A == dat) /* if A = op, skip */ P = (P + 1) & VA_MASK; break; case SKG: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (SXT (A) > SXT (dat)) /* if A > op, skip */ P = (P + 1) & VA_MASK; break; case SKM: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (((A ^ dat) & B) == 0) /* if A = op masked */ P = (P + 1) & VA_MASK; break; case SKA: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if ((A & dat) == 0) /* if !(A & op), skip */ P = (P + 1) & VA_MASK; break; case SKB: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if ((B & dat) == 0) /* if !(B & op), skip */ P = (P + 1) & VA_MASK; break; case SKN: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (dat & SIGN) /* if op < 0, skip */ P = (P + 1) & VA_MASK; break; case SKR: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; dat = AddM24 (dat, DMASK); /* decr operand */ if (r = Write (va, dat)) /* rewrite */ return r; if (dat & SIGN) /* if op < 0, skip */ P = (P + 1) & VA_MASK; break; case SKD: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (SXT_EXP (B) < SXT_EXP (dat)) { /* B < dat? */ X = (dat - B) & DMASK; /* X = dat - B */ P = (P + 1) & VA_MASK; /* skip */ } else X = (B - dat) & DMASK; /* X = B - dat */ break; /* Control */ case NOP: break; case HLT: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; return STOP_HALT; /* halt CPU */ case EXU: exu_cnt = exu_cnt + 1; /* count chained EXU */ if (exu_cnt > exu_lim) /* too many? */ return STOP_EXULIM; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; inst = dat; goto EXU_LOOP; case BRU: if (nml_mode && (inst & I_IND)) api_dismiss (); /* normal BRU*, dism */ if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; PCQ_ENTRY; P = va & VA_MASK; /* branch */ break; case BRX: if (r = Ea (inst, &va)) /* decode eff addr */ return r; X = (X + 1) & DMASK; /* incr X */ if (X & I_IND) { /* bit 9 set? */ if (r = Read (va, &dat)) /* test dest access */ return r; PCQ_ENTRY; P = va & VA_MASK; /* branch */ } break; case BRM: if (r = Ea (inst, &va)) /* decode eff addr */ return r; dat = (EM3 << 18) | (EM2 << 15) | pc; /* form return word */ if (!nml_mode && !usr_mode) /* monitor mode? */ dat = dat | (mode << 23) | (OV << 21); else dat = dat | (OV << 23); /* normal or user */ if (r = Write (va, dat)) /* write ret word */ return r; PCQ_ENTRY; P = (va + 1) & VA_MASK; /* branch */ break; case BRR: if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; PCQ_ENTRY; P = (dat + 1) & VA_MASK; /* branch */ if (!nml_mode && !usr_mode) { /* monitor mode? */ OV = OV | ((dat >> 21) & 1); /* restore OV */ if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */ usr_mode = 1; if (mon_usr_trap) return MM_MONUSR; } } else OV = OV | ((dat >> 23) & 1); /* restore OV */ break; case BRI: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; api_dismiss (); /* dismiss hi api */ PCQ_ENTRY; P = dat & VA_MASK; /* branch */ if (!nml_mode) { /* monitor mode? */ OV = (dat >> 21) & 1; /* restore OV */ if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */ usr_mode = 1; if (mon_usr_trap) return MM_MONUSR; } } else OV = (dat >> 23) & 1; /* restore OV */ break; /* Register change (microprogrammed) */ case RCH: old_A = A; /* save orig reg */ old_B = B; old_X = X; if (inst & 000001211) { /* A change? */ if (inst & 01000) dat = (~old_A + 1) & DMASK; /* CNA */ else dat = 0; if (inst & 00200) dat = dat | old_X; if (inst & 00010) dat = dat | old_B; if (inst & 00100) A = (A & ~EXPMASK) | (dat & EXPMASK); else A = dat; } if (inst & 000000046) { /* B change? */ if (inst & 00040) dat = old_X; else dat = 0; if (inst & 00004) dat = dat | old_A; if (inst & 00100) B = (B & ~EXPMASK) | (dat & EXPMASK); else B = dat; } if (inst & 020000420) { /* X change? */ if (inst & 00400) dat = old_A; else dat = 0; if (inst & 00020) dat = dat | old_B; if (inst & 00100) X = SXT_EXP (dat) & DMASK; else X = dat; } break; /* Overflow instruction */ case OVF: if ((inst & 0100) & OV) P = (P + 1) & VA_MASK; if (inst & 0001) OV = 0; if ((inst & 0010) && (((X >> 1) ^ X) & EXPS)) OV = 1; break; /* Shifts */ case RSH: if (r = EaSh (inst, &va)) /* decode eff addr */ return r; shf_op = I_GETSHFOP (va); /* get eff op */ sc = va & I_SHFMSK; /* get eff count */ switch (shf_op) { /* case on sub-op */ case 00: /* right arithmetic */ if (sc) ShfR48 (sc, (A & SIGN)? DMASK: 0); break; case 04: /* right cycle */ sc = sc % 48; /* mod 48 */ if (sc) RotR48 (sc); break; case 05: /* right logical */ if (sc) ShfR48 (sc, 0); break; default: CRETINS; /* invalid inst */ break; } /* end case shf op */ break; case LSH: if (r = EaSh (inst, &va)) /* decode eff addr */ return r; shf_op = I_GETSHFOP (va); /* get eff op */ sc = va & I_SHFMSK; /* get eff count */ switch (shf_op) { /* case on sub-op */ case 00: /* left arithmetic */ dat = A; /* save sign */ if (sc > 48) sc = 48; for (i = 0; i < sc; i++) { /* loop */ A = ((A << 1) | (B >> 23)) & DMASK; B = (B << 1) & DMASK; if ((A ^ dat) & SIGN) OV = 1; } break; case 02: /* normalize */ if (sc > 48) sc = 48; for (i = 0; i < sc; i++) { /* until max count */ if ((A ^ (A << 1)) & SIGN) break; A = ((A << 1) | (B >> 23)) & DMASK; B = (B << 1) & DMASK; } X = (X - i) & DMASK; break; case 04: /* left cycle */ sc = sc % 48; /* mod 48 */ if (sc) /* rotate */ RotR48 (48 - sc); break; case 06: /* cycle normalize */ if (sc > 48) sc = 48; for (i = 0; i < sc; i++) { /* until max count */ if ((A ^ (A << 1)) & SIGN) break; old_A = A; /* cyclic shift */ A = ((A << 1) | (B >> 23)) & DMASK; B = ((B << 1) | (old_A >> 23)) & DMASK; } X = (X - i) & DMASK; break; default: CRETINS; /* invalid inst */ break; } /* end case shf op */ break; /* I/O instructions */ case MIW: case MIY: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (r = op_miwy (inst, dat)) /* process inst */ return r; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ break; case WIM: case YIM: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = op_wyim (inst, &dat)) /* process inst */ return r; if (r = Write (va, dat)) return r; /* write result */ int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ break; case EOM: case EOD: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = op_eomd (inst)) /* process inst */ return r; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ ion_defer = 1; break; case POT: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; if (r = op_pot (dat)) /* process inst */ return r; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ break; case PIN: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = op_pin (&dat)) /* process inst */ return r; if (r = Write (va, dat)) /* write result */ return r; int_reqhi = api_findreq (); /* recalc int req */ chan_req = chan_testact (); /* recalc chan act */ break; case SKS: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; if (r = op_sks (inst, &dat)) /* process inst */ return r; if (dat) P = (P + 1) & VA_MASK; break; default: if (!nml_mode && usr_mode) /* priv inst */ return MM_PRVINS; CRETINS; /* invalid inst */ break; } return SCPE_OK; } /* Effective address calculation */ t_stat Ea (uint32 inst, uint32 *addr) { int32 i; uint32 wd = inst; /* homeable */ uint32 va = wd & XVA_MASK; /* initial va */ t_stat r; for (i = 0; i < ind_lim; i++) { /* count indirects */ if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK); *addr = va; if ((wd & I_IND) == 0) { /* end of ind chain? */ if (hst_lnt) /* record */ hst[hst_p].ea = *addr; return SCPE_OK; } if (r = Read (va, &wd)) /* read ind; fails? */ return r; va = (va & VA_USR) | (wd & XVA_MASK); } return STOP_INDLIM; /* too many indirects */ } /* Effective address calculation for shifts - direct indexing is 9b */ t_stat EaSh (uint32 inst, uint32 *addr) { int32 i; uint32 wd = inst; /* homeable */ uint32 va = wd & XVA_MASK; /* initial va */ t_stat r; for (i = 0; i < ind_lim; i++) { /* count indirects */ if ((wd & I_IND) == 0) { /* end of ind chain? */ if (wd & I_IDX) /* 9b indexing */ *addr = (va & (VA_MASK & ~I_SHFMSK)) | ((va + X) & I_SHFMSK); else *addr = va & VA_MASK; if (hst_lnt) /* record */ hst[hst_p].ea = *addr; return SCPE_OK; } if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK); if (r = Read (va, &wd)) /* read ind; fails? */ return r; va = (va & VA_USR) | (wd & XVA_MASK); } return STOP_INDLIM; /* too many indirects */ } /* Read word from virtual address */ t_stat Read (uint32 va, uint32 *dat) { uint32 pgn, map, pa; if (nml_mode) { /* normal? */ va = va & VA_MASK; /* ignore user */ if (va < 020000) /* first 8K: 1 for 1 */ pa = va; else if (va < 030000) /* next 4K: ext EM2 */ pa = va + em2_dyn; else pa = va + em3_dyn; /* next 4K: ext EM3 */ } else if (usr_mode || (va & VA_USR)) { /* user mapping? */ pgn = VA_GETPN (va); /* get page no */ map = usr_map[pgn]; /* get map entry */ if (map == MAP_PROT) /* prot? no access */ return MM_NOACC; pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */ } else { pgn = VA_GETPN (va); /* mon, get page no */ map = mon_map[pgn]; /* get map entry */ if (map & MAP_PROT) return MM_NOACC; /* prot? no access */ pa = map | (va & VA_POFF); /* map address */ } *dat = M[pa]; /* return word */ return SCPE_OK; } /* Write word to virtual address */ t_stat Write (uint32 va, uint32 dat) { uint32 pgn, map, pa; if (nml_mode) { /* normal? */ va = va & VA_MASK; /* ignore user */ if (va < 020000) /* first 8K: 1 for 1 */ pa = va; else if (va < 030000) /* next 4K: ext EM2 */ pa = va + em2_dyn; else pa = va + em3_dyn; /* next 4K: ext EM3 */ } else if (usr_mode || (va & VA_USR)) { /* user mapping? */ pgn = VA_GETPN (va); /* get page no */ map = usr_map[pgn]; /* get map entry */ if (map & MAP_PROT) { /* protected page? */ if (map == MAP_PROT) /* zero? no access */ return MM_NOACC; else return MM_WRITE; /* else, write prot */ } pa = map | (va & VA_POFF); /* map address */ } else { pgn = VA_GETPN (va); /* mon, get page no */ map = mon_map[pgn]; /* get map entry */ if (map & MAP_PROT) /* prot? no access */ return MM_NOACC; pa = map | (va & VA_POFF); /* map address */ } if (MEM_ADDR_OK (pa)) M[pa] = dat; return SCPE_OK; } /* Relocate addr for console access */ uint32 RelocC (int32 va, int32 sw) { uint32 nml = nml_mode, usr = usr_mode; uint32 pa, pgn, map; if (sw & SWMASK ('N')) /* -n: normal */ nml = 1; else if (sw & SWMASK ('X')) /* -x: mon */ nml = usr = 0; else if (sw & SWMASK ('U')) { /* -u: user */ nml = 0; usr = 1; } else if (!(sw & SWMASK ('V'))) /* -v: curr */ return va; set_dyn_map (); if (nml) { /* normal? */ if (va < 020000) /* first 8K: 1 for 1 */ pa = va; else if (va < 030000) /* next 4K: ext EM2 */ pa = va + em2_dyn; else pa = va + em3_dyn; /* next 4K: ext EM3 */ } else { pgn = VA_GETPN (va); /* get page no */ map = usr? usr_map[pgn]: mon_map[pgn]; /* get map entry */ if (map == MAP_PROT) /* no access page? */ return MAXMEMSIZE + 1; pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */ } return pa; } /* Arithmetic routines */ uint32 Add24 (uint32 s1, uint32 s2, uint32 cin) { uint32 t = s1 + s2 + cin; /* add with carry in */ if (t > DMASK) /* carry to X<0> */ X = X | SIGN; else X = X & ~SIGN; if (((s1 ^ ~s2) & (s1 ^ t)) /* overflow */ & SIGN) OV = 1; return t & DMASK; } uint32 AddM24 (uint32 s1, uint32 s2) { uint32 t = s1 + s2; /* add */ if (((s1 ^ ~s2) & (s1 ^ t)) & SIGN) /* overflow */ OV = 1; return t & DMASK; } void Mul48 (uint32 s1, uint32 s2) { uint32 a = ABS (s1); uint32 b = ABS (s2); uint32 hi, md, lo, t, u; if ((a == 0) || (b == 0)) { /* ops zero? */ A = B = 0; return; } t = a >> 12; /* split op1 */ a = a & 07777; u = b >> 12; /* split op2 */ b = b & 07777; md = (a * u) + (b * t); /* cross product */ lo = (a * b) + ((md & 07777) << 12); /* low result */ hi = (t * u) + (md >> 12) + (lo >> 24); /* hi result */ A = ((hi << 1) & DMASK) | ((lo & DMASK) >> 23); B = (lo << 1) & DMASK; if ((s1 ^ s2) & SIGN) { B = ((B ^ DMASK) + 1) & DMASK; A = ((A ^ DMASK) + (B == 0)) & DMASK; } else if (A & SIGN) OV = 1; return; } /* Divide - the SDS 940 uses a non-restoring divide. The algorithm runs even for overflow cases. Hence it must be emulated precisely to give the right answers for diagnostics. If the dividend is negative, AB are 2's complemented starting at B<22>, and B<23> is unchanged. */ void Div48 (uint32 ar, uint32 br, uint32 m) { int32 i; uint32 quo = 0; /* quotient */ uint32 dvdh = ar, dvdl = br; /* dividend */ uint32 dvr = ABS (m); /* make dvr pos */ if (TSTS (dvdh)) { /* dvd < 0? */ dvdl = (((dvdl ^ DMASK) + 2) & (DMASK & ~1)) | /* 23b negate */ (dvdl & 1); /* low bit unch */ dvdh = ((dvdh ^ DMASK) + (dvdl <= 1)) & DMASK; } if ((dvdh > dvr) || /* divide fail? */ ((dvdh == dvr) && dvdl) || ((dvdh == dvr) && !TSTS (ar ^ m))) OV = 1; dvdh = (dvdh - dvr) & DMASK; /* initial sub */ for (i = 0; i < 23; i++) { /* 23 iterations */ quo = (quo << 1) | ((dvdh >> 23) ^ 1); /* quo bit = ~sign */ dvdh = ((dvdh << 1) | (dvdl >> 23)) & DMASK; /* shift divd */ dvdl = (dvdl << 1) & DMASK; if (quo & 1) /* test ~sign */ dvdh = (dvdh - dvr) & DMASK; /* sign was +, sub */ else dvdh = (dvdh + dvr) & DMASK; /* sign was -, add */ } quo = quo << 1; /* shift quo */ if (dvdh & SIGN) /* last op -? restore */ dvdh = (dvdh + dvr) & DMASK; else quo = quo | 1; /* +, set quo bit */ if (TSTS (ar ^ m)) /* sign of quo */ A = NEG (quo); else A = quo; /* A = quo */ if (TSTS (ar)) /* sign of rem */ B = NEG (dvdh); else B = dvdh; /* B = rem */ return; } void RotR48 (uint32 sc) { uint32 t = A; if (sc >= 24) { sc = sc - 24; A = ((B >> sc) | (A << (24 - sc))) & DMASK; B = ((t >> sc) | (B << (24 - sc))) & DMASK; } else { A = ((A >> sc) | (B << (24 - sc))) & DMASK; B = ((B >> sc) | (t << (24 - sc))) & DMASK; } return; } void ShfR48 (uint32 sc, uint32 sgn) { if (sc >= 48) A = B = sgn; if (sc >= 24) { sc = sc - 24; B = ((A >> sc) | (sgn << (24 - sc))) & DMASK; A = sgn; } else { B = ((B >> sc) | (A << (24 - sc)) & DMASK); A = ((A >> sc) | (sgn << (24 - sc))) & DMASK; } return; } /* POT routines for RL1, RL2, RL4 */ t_stat pot_RL1 (uint32 num, uint32 *dat) { RL1 = *dat; set_dyn_map (); return SCPE_OK; } t_stat pot_RL2 (uint32 num, uint32 *dat) { RL2 = *dat; set_dyn_map (); return SCPE_OK; } t_stat pot_RL4 (uint32 num, uint32 *dat) { RL4 = (*dat) & 03737; set_dyn_map (); return SCPE_OK; } /* Map EM2, EM3, RL1, RL2, RL4 to dynamic forms EM2, EM3 - left shifted 12, base virtual address subtracted RL1, RL2 - page left shifted 11 RL3 - filled in as 1 to 1 map RL4 - EM2 or page left shifted 11, PROT bit inserted */ void set_dyn_map (void) { em2_dyn = ((EM2 & 07) << 12) - 020000; em3_dyn = ((EM3 & 07) << 12) - 030000; usr_map[0] = (RL1 >> 7) & (MAP_PROT | MAP_PAGE); usr_map[1] = (RL1 >> 1) & (MAP_PROT | MAP_PAGE); usr_map[2] = (RL1 << 5) & (MAP_PROT | MAP_PAGE); usr_map[3] = (RL1 << 11) & (MAP_PROT | MAP_PAGE); usr_map[4] = (RL2 >> 7) & (MAP_PROT | MAP_PAGE); usr_map[5] = (RL2 >> 1) & (MAP_PROT | MAP_PAGE); usr_map[6] = (RL2 << 5) & (MAP_PROT | MAP_PAGE); usr_map[7] = (RL2 << 11) & (MAP_PROT | MAP_PAGE); mon_map[0] = (0 << VA_V_PN); mon_map[1] = (1 << VA_V_PN); mon_map[2] = (2 << VA_V_PN); mon_map[3] = (3 << VA_V_PN); mon_map[4] = ((EM2 & 07) << 12); mon_map[5] = ((EM2 & 07) << 12) + (1 << VA_V_PN); mon_map[6] = (RL4 << 5) & MAP_PAGE; mon_map[7] = (RL4 << 11) & MAP_PAGE; if (mon_map[6] == 0) mon_map[6] = MAP_PROT; if (mon_map[7] == 0) mon_map[7] = MAP_PROT; return; } /* Recalculate api requests */ uint32 api_findreq (void) { uint32 i, t; t = (int_req & ~1) & api_mask[api_lvlhi]; /* unmasked int */ for (i = 31; t && (i > 0); i--) { /* find highest */ if ((t >> i) & 1) return i; } return 0; /* none */ } /* Dismiss highest priority interrupt */ void api_dismiss (void) { uint32 i, t; t = 1u << api_lvlhi; /* highest active */ int_req = int_req & ~t; /* clear int req */ api_lvl = api_lvl & ~t; /* clear api level */ api_lvlhi = 0; /* assume all clear */ for (i = 31; api_lvl && (i > 0); i--) { /* find highest api */ if ((api_lvl >> i) & 1) { /* bit set? */ api_lvlhi = i; /* record level */ break; /* done */ } } int_reqhi = api_findreq (); /* recalc intreq */ return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { OV = 0; EM2 = 2; EM3 = 3; RL1 = RL2 = RL4 = 0; ion = ion_defer = 0; nml_mode = 1; usr_mode = 0; mon_usr_trap = 0; int_req = 0; int_reqhi = 0; api_lvl = 0; api_lvlhi = 0; alert = 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) { uint32 pa; pa = RelocC (addr, sw); if (pa > MAXMEMSIZE) return SCPE_REL; if (pa >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = M[pa] & DMASK; return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { uint32 pa; pa = RelocC (addr, sw); if (pa > MAXMEMSIZE) return SCPE_REL; if (pa >= MEMSIZE) return SCPE_NXM; M[pa] = val & DMASK; return SCPE_OK; } /* Set 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 & 037777) != 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; } /* Set system type (1 = Genie, 0 = standard) */ t_stat cpu_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) { extern t_stat drm_reset (DEVICE *dptr); extern DEVICE drm_dev, mux_dev, muxl_dev; extern UNIT drm_unit, mux_unit; extern DIB mux_dib; if ((cpu_unit.flags & UNIT_GENIE) == (uint32) val) return SCPE_OK; if ((drm_unit.flags & UNIT_ATT) || /* attached? */ (mux_unit.flags & UNIT_ATT)) /* can't do it */ return SCPE_NOFNC; if (val) { /* Genie? */ drm_dev.flags = drm_dev.flags & ~DEV_DIS; /* enb drum */ mux_dev.flags = mux_dev.flags & ~DEV_DIS; /* enb mux */ muxl_dev.flags = muxl_dev.flags & ~DEV_DIS; mux_dib.dev = DEV3_GMUX; /* Genie mux */ } else { drm_dev.flags = drm_dev.flags | DEV_DIS; /* dsb drum */ mux_dib.dev = DEV3_SMUX; /* std mux */ return drm_reset (&drm_dev); } return SCPE_OK; } /* The real time clock runs continuously; therefore, it only has a unit service routine and a reset routine. The service routine sets an interrupt that invokes the clock counter. The clock counter is a "one instruction interrupt", and only MIN/SKR are valid. */ t_stat rtc_svc (UNIT *uptr) { if (rtc_pie) /* set pulse intr */ int_req = int_req | INT_RTCP; sim_activate (&rtc_unit, sim_rtcn_calb (rtc_tps, TMR_RTC)); /* reactivate */ return SCPE_OK; } /* Clock interrupt instruction */ t_stat rtc_inst (uint32 inst) { uint32 op, dat, val, va; t_stat r; op = I_GETOP (inst); /* get opcode */ if (op == MIN) /* incr */ val = 1; else if (op == SKR) /* decr */ val = DMASK; else return STOP_RTCINS; /* can't do it */ if (r = Ea (inst, &va)) /* decode eff addr */ return r; if (r = Read (va, &dat)) /* get operand */ return r; dat = AddM24 (dat, val); /* mem +/- 1 */ if (r = Write (va, dat)) /* rewrite */ return r; if (dat == 0) /* set clk sync int */ int_req = int_req | INT_RTCS; return SCPE_OK; } /* Clock reset */ t_stat rtc_reset (DEVICE *dptr) { rtc_pie = 0; /* disable pulse */ sim_activate (&rtc_unit, rtc_unit.wait); /* activate unit */ return SCPE_OK; } /* Set frequency */ t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; rtc_tps = val; return SCPE_OK; } /* Show frequency */ t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz"); return SCPE_OK; } /* Record history */ void inst_hist (uint32 ir, uint32 pc, uint32 tp) { hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].typ = tp | (OV << 4); hst[hst_p].pc = pc; hst[hst_p].ir = ir; hst[hst_p].a = A; hst[hst_p].b = B; hst[hst_p].x = X; hst[hst_p].ea = HIST_NOEA; return; } /* 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].typ = 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, 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); static char *cyc[] = { " ", " ", "INT", "TRP" }; if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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, "CYC PC OV A B X EA IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(++di) % hst_lnt]; /* entry pointer */ if (h->typ) { /* instruction? */ ov = (h->typ >> 4) & 1; /* overflow */ fprintf (st, "%s %05o %o %08o %08o %08o ", cyc[h->typ & 3], h->pc, ov, h->a, h->b, h->x); if (h->ea & HIST_NOEA) fprintf (st, " "); else fprintf (st, "%05o ", h->ea); sim_eval = h->ir; if ((fprint_sym (st, h->pc, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %08o", h->ir); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/SDS/sds_defs.h0000644000175000017500000004613511111666030013241 0ustar vlmvlm/* sds_defs.h: SDS 940 simulator definitions Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 25-Apr-03 RMS Revised for extended file support */ #ifndef _SDS_DEFS_H_ #define _SDS_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_IONRDY 1 /* I/O dev not ready */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_INVDEV 4 /* invalid dev */ #define STOP_INVINS 5 /* invalid instr */ #define STOP_INVIOP 6 /* invalid I/O op */ #define STOP_INDLIM 7 /* indirect limit */ #define STOP_EXULIM 8 /* EXU limit */ #define STOP_MMINT 9 /* mm in intr */ #define STOP_MMTRP 10 /* mm in trap */ #define STOP_TRPINS 11 /* trap inst not BRM */ #define STOP_RTCINS 12 /* rtc inst not MIN/SKR */ #define STOP_ILLVEC 13 /* zero vector */ #define STOP_CCT 14 /* runaway CCT */ /* Trap codes */ #define MM_PRVINS -040 /* privileged */ #define MM_NOACC -041 /* no access */ #define MM_WRITE -043 /* write protect */ #define MM_MONUSR -044 /* mon to user */ /* Conditional error returns */ #define CRETINS return ((stop_invins)? STOP_INVINS: SCPE_OK) #define CRETDEV return ((stop_invdev)? STOP_INVDEV: SCPE_OK) #define CRETIOP return ((stop_inviop)? STOP_INVIOP: SCPE_OK) #define CRETIOE(f,c) return ((f)? c: SCPE_OK) /* Architectural constants */ #define SIGN 040000000 /* sign */ #define DMASK 077777777 /* data mask */ #define EXPS 0400 /* exp sign */ #define EXPMASK 0777 /* exp mask */ #define SXT(x) ((int32) (((x) & SIGN)? ((x) | ~DMASK): \ ((x) & DMASK))) #define SXT_EXP(x) ((int32) (((x) & EXPS)? ((x) | ~EXPMASK): \ ((x) & EXPMASK))) /* Memory */ #define MAXMEMSIZE (1 << 16) /* max memory size */ #define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) #define ReadP(x) M[x] #define WriteP(x,y) if (MEM_ADDR_OK (x)) M[x] = y /* Virtual addressing */ #define VA_SIZE (1 << 14) /* virtual addr size */ #define VA_MASK (VA_SIZE - 1) /* virtual addr mask */ #define VA_V_PN 11 /* page number */ #define VA_M_PN 07 #define VA_GETPN(x) (((x) >> VA_V_PN) & VA_M_PN) #define VA_POFF ((1 << VA_V_PN) - 1) /* offset */ #define VA_USR (I_USR) /* user flag in addr */ #define XVA_MASK (VA_USR | VA_MASK) /* Arithmetic */ #define TSTS(x) ((x) & SIGN) #define NEG(x) (-((int32) (x)) & DMASK) #define ABS(x) (TSTS (x)? NEG(x): (x)) /* Memory map */ #define MAP_PROT (040 << VA_V_PN) /* protected */ #define MAP_PAGE (037 << VA_V_PN) /* phys page number */ /* Instruction format */ #define I_USR (1 << 23) /* user */ #define I_IDX (1 << 22) /* indexed */ #define I_POP (1 << 21) /* programmed op */ #define I_V_TAG 21 /* tag */ #define I_V_OP 15 /* opcode */ #define I_M_OP 077 #define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) #define I_IND (1 << 14) /* indirect */ #define I_V_SHFOP 11 /* shift op */ #define I_M_SHFOP 07 #define I_GETSHFOP(x) (((x) >> I_V_SHFOP) & I_M_SHFOP) #define I_SHFMSK 0777 /* shift count */ #define I_V_IOMD 12 /* IO inst mode */ #define I_M_IOMD 03 #define I_GETIOMD(x) (((x) >> I_V_IOMD) & I_M_IOMD) #define I_V_SKCND 7 /* SKS skip cond */ #define I_M_SKCND 037 #define I_GETSKCND(x) (((x) >> I_V_SKCND) & I_M_SKCND) #define I_EOB2 000400000 /* chan# bit 2 */ #define I_SKB2 000040000 /* skschan# bit 2 */ #define I_EOB1 020000000 /* chan# bit 1 */ #define I_EOB0 000000100 /* chan# bit 0 */ #define I_GETEOCH(x) ((((x) & I_EOB2)? 4: 0) | \ (((x) & I_EOB1)? 2: 0) | \ (((x) & I_EOB0)? 1: 0)) #define I_SETEOCH(x) ((((x) & 4)? I_EOB2: 0) | \ (((x) & 2)? I_EOB1: 0) | \ (((x) & 1)? I_EOB0: 0)) #define I_GETSKCH(x) ((((x) & I_SKB2)? 4: 0) | \ (((x) & I_EOB1)? 2: 0) | \ (((x) & I_EOB0)? 1: 0)) #define I_SETSKCH(x) ((((x) & 4)? I_SKB2: 0) | \ (((x) & 2)? I_EOB1: 0) | \ (((x) & 1)? I_EOB0: 0)) /* Globally visible flags */ #define UNIT_V_GENIE (UNIT_V_UF + 0) #define UNIT_GENIE (1 << UNIT_V_GENIE) /* Timers */ #define TMR_RTC 0 /* clock */ #define TMR_MUX 1 /* mux */ /* I/O routine functions */ #define IO_CONN 0 /* connect */ #define IO_EOM1 1 /* EOM mode 1 */ #define IO_DISC 2 /* disconnect */ #define IO_READ 3 /* read */ #define IO_WRITE 4 /* write */ #define IO_WREOR 5 /* write eor */ #define IO_SKS 6 /* skip signal */ /* Dispatch template */ struct sdsdspt { uint32 num; /* # entries */ uint32 off; /* offset from base */ }; typedef struct sdsdspt DSPT; /* Device information block */ struct sdsdib { int32 chan; /* channel */ int32 dev; /* base dev no */ int32 xfr; /* xfer flag */ DSPT *tplt; /* dispatch templates */ t_stat (*iop) (uint32 fnc, uint32 dev, uint32 *dat); }; typedef struct sdsdib DIB; /* Channels */ #define NUM_CHAN 8 /* max num chan */ #define CHAN_W 0 /* TMCC */ #define CHAN_Y 1 #define CHAN_C 2 #define CHAN_D 3 #define CHAN_E 4 /* DACC */ #define CHAN_F 5 #define CHAN_G 6 #define CHAN_H 7 /* I/O control EOM */ #define CHC_REV 04000 /* reverse */ #define CHC_NLDR 02000 /* no leader */ #define CHC_BIN 01000 /* binary */ #define CHC_V_CPW 7 /* char/word */ #define CHC_M_CPW 03 #define CHC_GETCPW(x) (((x) >> CHC_V_CPW) & CHC_M_CPW) /* Buffer control (extended) EOM */ #define CHM_CE 04000 /* compat/ext */ #define CHM_ER 02000 /* end rec int */ #define CHM_ZC 01000 /* zero wc int */ #define CHM_V_FNC 7 /* term func */ #define CHM_M_FNC 03 #define CHM_GETFNC(x) (((x) & CHM_CE)? (((x) >> CHM_V_FNC) & CHM_M_FNC): CHM_COMP) #define CHM_IORD 0 /* record, disc */ #define CHM_IOSD 1 /* signal, disc */ #define CHM_IORP 2 /* record, proc */ #define CHM_IOSP 3 /* signal, proc */ #define CHM_COMP 5 /* compatible */ #define CHM_SGNL 1 /* signal bit */ #define CHM_PROC 2 /* proceed bit */ #define CHM_V_HMA 5 /* hi mem addr */ #define CHM_M_HMA 03 #define CHM_GETHMA(x) (((x) >> CHM_V_HMA) & CHM_M_HMA) #define CHM_V_HWC 0 /* hi word count */ #define CHM_M_HWC 037 #define CHM_GETHWC(x) (((x) >> CHM_V_HWC) & CHM_M_HWC) /* Channel flags word */ #define CHF_ERR 00001 /* error */ #define CHF_IREC 00002 /* interrecord */ #define CHF_ILCE 00004 /* interlace */ #define CHF_DCHN 00010 /* data chain */ #define CHF_EOR 00020 /* end of record */ #define CHF_12B 00040 /* 12 bit mode */ #define CHF_24B 00100 /* 24 bit mode */ #define CHF_OWAK 00200 /* output wake */ #define CHF_SCAN 00400 /* scan */ #define CHF_TOP 01000 /* TOP pending */ #define CHF_N_FLG 9 /* <= 16 */ /* Interrupts and vectors (0 is reserved), highest bit is highest priority */ #define INT_V_PWRO 31 /* power on */ #define INT_V_PWRF 30 /* power off */ #define INT_V_CPAR 29 /* CPU parity err */ #define INT_V_IPAR 28 /* IO parity err */ #define INT_V_RTCS 27 /* clock sync */ #define INT_V_RTCP 26 /* clock pulse */ #define INT_V_YZWC 25 /* chan Y zero wc */ #define INT_V_WZWC 24 /* chan W zero wc */ #define INT_V_YEOR 23 /* chan Y end rec */ #define INT_V_WEOR 22 /* chan W end rec */ #define INT_V_CZWC 21 /* chan C */ #define INT_V_CEOR 20 #define INT_V_DZWC 19 /* chan D */ #define INT_V_DEOR 18 #define INT_V_EZWC 17 /* chan E */ #define INT_V_EEOR 16 #define INT_V_FZWC 15 /* chan F */ #define INT_V_FEOR 14 #define INT_V_GZWC 13 /* chan G */ #define INT_V_GEOR 12 #define INT_V_HZWC 11 /* chan H */ #define INT_V_HEOR 10 #define INT_V_MUXR 9 /* mux receive */ #define INT_V_MUXT 8 /* mux transmit */ #define INT_V_MUXCO 7 /* SDS carrier on */ #define INT_V_MUXCF 6 /* SDS carrier off */ #define INT_V_DRM 5 /* Genie drum */ #define INT_V_FORK 4 /* fork */ #define INT_PWRO (1 << INT_V_PWRO) #define INT_PWRF (1 << INT_V_PWRF) #define INT_CPAR (1 << INT_V_CPAR) #define INT_IPAR (1 << INT_V_IPAR) #define INT_RTCS (1 << INT_V_RTCS) #define INT_RTCP (1 << INT_V_RTCP) #define INT_YZWC (1 << INT_V_YZWC) #define INT_WZWC (1 << INT_V_WZWC) #define INT_YEOR (1 << INT_V_YEOR) #define INT_WEOR (1 << INT_V_WEOR) #define INT_CZWC (1 << INT_V_CZWC) #define INT_CEOR (1 << INT_V_CEOR) #define INT_DZWC (1 << INT_V_DZWC) #define INT_DEOR (1 << INT_V_DEOR) #define INT_EZWC (1 << INT_V_EZWC) #define INT_EEOR (1 << INT_V_EEOR) #define INT_FZWC (1 << INT_V_FZWC) #define INT_FEOR (1 << INT_V_FEOR) #define INT_GZWC (1 << INT_V_GZWC) #define INT_GEOR (1 << INT_V_GEOR) #define INT_HZWC (1 << INT_V_HZWC) #define INT_HEOR (1 << INT_V_HEOR) #define INT_MUXR (1 << INT_V_MUXR) #define INT_MUXT (1 << INT_V_MUXT) #define INT_MUXCO (1 << INT_V_MUXCO) #define INT_MUXCF (1 << INT_V_MUXCF) #define INT_DRM (1 << INT_V_DRM) #define INT_FORK (1 << INT_V_FORK) #define VEC_PWRO 0036 #define VEC_PWRF 0037 #define VEC_CPAR 0056 #define VEC_IPAR 0057 #define VEC_RTCS 0074 #define VEC_RTCP 0075 #define VEC_YZWC 0030 #define VEC_WZWC 0031 #define VEC_YEOR 0032 #define VEC_WEOR 0033 #define VEC_CZWC 0060 #define VEC_CEOR 0061 #define VEC_DZWC 0062 #define VEC_DEOR 0063 #define VEC_EZWC 0064 #define VEC_EEOR 0065 #define VEC_FZWC 0066 #define VEC_FEOR 0067 #define VEC_GZWC 0070 #define VEC_GEOR 0071 #define VEC_HZWC 0072 #define VEC_HEOR 0073 #define VEC_MUXR 0200 /* term mux rcv */ #define VEC_MUXT 0201 /* term mux xmt */ #define VEC_MUXCO 0202 /* SDS: mux carrier on */ #define VEC_MUXCF 0203 /* SDS: mux carrier off */ #define VEC_DRM 0202 /* Genie: drum */ #define VEC_FORK 0216 /* "fork" */ /* Device constants */ #define DEV_MASK 077 /* device mask */ #define DEV_TTI 001 /* teletype */ #define DEV_PTR 004 /* paper tape rdr */ #define DEV_MT 010 /* magtape */ #define DEV_RAD 026 /* fixed head disk */ #define DEV_DSK 026 /* moving head disk */ #define DEV_TTO 041 /* teletype */ #define DEV_PTP 044 /* paper tape punch */ #define DEV_LPT 060 /* line printer */ #define DEV_MTS 020 /* MT scan/erase */ #define DEV_OUT 040 /* output flag */ #define DEV3_GDRM 004 /* Genie drum */ #define DEV3_GMUX 001 /* Genie mux */ #define DEV3_SMUX (DEV_MASK) /* standard mux */ #define LPT_WIDTH 132 /* line print width */ #define CCT_LNT 132 /* car ctrl length */ /* Transfer request flags for devices (0 is reserved) */ #define XFR_V_TTI 1 /* console */ #define XFR_V_TTO 2 #define XFR_V_PTR 3 /* paper tape */ #define XFR_V_PTP 4 #define XFR_V_LPT 5 /* line printer */ #define XFR_V_RAD 6 /* fixed hd disk */ #define XFR_V_DSK 7 /* mving hd disk */ #define XFR_V_MT0 8 /* magtape */ #define XFR_TTI (1 << XFR_V_TTI) #define XFR_TTO (1 << XFR_V_TTO) #define XFR_PTR (1 << XFR_V_PTR) #define XFR_PTP (1 << XFR_V_PTP) #define XFR_LPT (1 << XFR_V_LPT) #define XFR_RAD (1 << XFR_V_RAD) #define XFR_DSK (1 << XFR_V_DSK) #define XFR_MT0 (1 << XFR_V_MT0) /* PIN/POT ordinals (0 is reserved) */ #define POT_ILCY 1 /* interlace */ #define POT_DCRY (POT_ILCY + NUM_CHAN) /* data chain */ #define POT_ADRY (POT_DCRY + NUM_CHAN) /* address reg */ #define POT_RL1 (POT_ADRY + NUM_CHAN) /* RL1 */ #define POT_RL2 (POT_RL1 + 1) /* RL2 */ #define POT_RL4 (POT_RL2 + 1) /* RL4 */ #define POT_RADS (POT_RL4 + 1) /* fhd sector */ #define POT_RADA (POT_RADS + 1) /* fhd addr */ #define POT_DSK (POT_RADA + 1) /* mhd sec/addr */ #define POT_SYSI (POT_DSK + 1) /* sys intr */ #define POT_MUX (POT_SYSI + 1) /* multiplexor */ /* Opcodes */ enum opcodes { HLT, BRU, EOM, EOD = 006, MIY = 010, BRI, MIW, POT, ETR, MRG = 016, EOR, NOP, OVF = 022, EXU, YIM = 030, WIM = 032, PIN, STA = 035, STB, STX, SKS, BRX, BRM = 043, RCH = 046, SKE = 050, BRR, SKB, SKN, SUB, ADD, SUC, ADC, SKR, MIN, XMA, ADM, MUL, DIV, RSH, LSH, SKM, LDX, SKA, SKG, SKD, LDB, LDA, EAX }; /* Channel function prototypes */ void chan_set_flag (int32 ch, uint32 fl); void chan_set_ordy (int32 ch); void chan_disc (int32 ch); void chan_set_uar (int32 ch, uint32 dev); t_stat set_chan (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat chan_process (void); t_bool chan_testact (void); #endif simh-3.8.1/SDS/sds_diag.txt0000644000175000017500000000644207626270670013632 0ustar vlmvlmSDS Diagnostics, using the SDS 930/940 Master Diagnostic Tape image (D930X4A.TAP) Summary 930 0-16K Memory Test passed 930 16K-32K Memory Test passed 930 Instruction Test passed 930 P&S Register Test passed --- 930 0-16K Memory Test sim> att mt diag.tap sim> d a 1 sim> d bpt4 1 ; stop every 1/2 cycle sim> boot mt HALT instruction, P: 00050 (STA 122,4) sim> ex a A: 00000000 ; error count sim> c HALT instruction, P: 37650 (STA 37722,4) sim> ex a A: 00000000 ; error count --- 930 16K-32K Memory Test sim> att mt diag.tap sim> d a 2 sim> d bpt4 2 ; stop every 1/2 cycle sim> boot mt HALT instruction, P: 00050 (STA 6) sim> ex a A: 00000000 ; error count sim> c HALT instruction, P: 37650 (STA 37406) sim> ex a A: 00000000 ; error count sim> c --- 930 Instruction Diagnostic sim> att mt diag.tap sim> d a 3 sim> br 17 ; catch start of diagnostic sim> boot mt Breakpoint, P: 00017 (BRR 12,2) sim> nobr 17 sim> br 112 ; catch end of diagnostic sim> c Breakpoint, P: 00112 (BRU 3) --- 930 P&S Register Test sim> att mt diag.tap sim> d a 4 sim> br 60 ; catch end of pass sim> boot mt Breakpoint, P: 00060 (BRU 22) --- Bugs 1. IO: Channel WAR not cleared after memory store 2. IO: dev_map should contain _flags, not _v_flags 3. SYS: Errors in system tables 4. SYS: Character conversion table had 0 (space) as illegal, should be -1 5. IO: Channel CPW calculation wrong for 12b mode 6. RAD, DSK, MT: Instruction masks wrong for RAD, DSK, MT 7. IO: Missing subscripts in dev_disp references 8. RAD: typos referencing DSK 9. IO: SKS 3 call incorrect 10. DRM: Drum track mask width incorrect 11. CPU: Memory management trap left reason in bogus state, stopped simulator 12. CPU: Interrupts require api_lvl as well as api_lvlhi, like PDP-10, PDP-15 13. CPU: Bug in find interrupt request 14. CPU: Interrupt priority scheme recoded for left to right priority 15. CPU: overflow test coded backwards 16. CPU: Rotates operate mod 48, not with upper limit of 48 (manual incorrect) 17. CPU: RSH not handling >= 48 correctly 18. CPU: CNA is 2's complement not 1's complement 19. CPU: MUL failed to mask cross product correctly 20. CPU: EM2, EM3 test using wrong 'channel' 21. CPU: EM3 test tested EM2 instead 22. CPU: POP must save EM2, EM3 like BRM (manual incorrect) 23. CPU: Shifts need special EA calculation, direct cycles using 9b indexing 24. CPU: Shifts ignore addr<13:14> 25. CPU: Diagnostic uses undefined shift 'normalize cyclic' 26. CPU: Divide 2'c complement of AB leaves B<23> unchanged 27. CPU: Divide overflow test requires special cases for divd.h == divr 28. CPU: Divide uses non-restoring algorithm 29. CPU: Channel terminate output must be deferred until channel buffer clears 30. CPU: Channel terminate output to magtape is convert to scan, must be handled in channel logic 31. SYS: duplicate entries for shifts 32. SYS: mask for shifts did not include indirect flag 33. MUX: Genie/SDS use inverted meanings for line enable flag 34. MT: missing fseek before write eof 35. MT: displayed characters only 7b wide instead of 8b 36. CPU: EOD 20000 used by diagnostic (EM change is NOP) 37. CPU: SKD sets all 24b of X, not just exponent 38. CPU: reset should not clear A, B, X simh-3.8.1/SDS/sds_rad.c0000644000175000017500000003074611110637164013067 0ustar vlmvlm/* sds_rad.c: SDS 940 fixed head disk simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rad fixed head disk The fixed head disk is a head-per-track disk, with up to four disks. Each disk is divided into two logical units. Reads and writes cannot cross logical unit boundaries. The fixed head disk transfers 12b characters, rather than 6b characters. To minimize overhead, the disk is buffered in memory. */ #include "sds_defs.h" #include /* Constants */ #define RAD_NUMWD 64 /* words/sector */ #define RAD_NUMSC 64 /* sectors/track */ #define RAD_NUMTR 64 /* tracks/log unit */ #define RAD_NUMLU 8 /* log units/ctrl */ #define RAD_SCSIZE (RAD_NUMLU*RAD_NUMTR*RAD_NUMSC) /* sectors/disk */ #define RAD_AMASK (RAD_SCSIZE - 1) /* sec addr mask */ #define RAD_SIZE (RAD_SCSIZE * RAD_NUMWD) /* words/disk */ #define RAD_GETLUN(x) ((x) / (RAD_NUMTR * RAD_NUMSC)) #define RAD_SCMASK (RAD_NUMSC - 1) /* sector mask */ #define RAD_TRSCMASK ((RAD_NUMSC * RAD_NUMTR) - 1) /* track/sec mask */ #define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) RAD_NUMSC))) extern uint32 xfr_req; extern uint32 alert; extern int32 stop_invins, stop_invdev, stop_inviop; int32 rad_err = 0; /* error */ int32 rad_nobi = 0; /* !incr x track */ int32 rad_da = 0; /* disk address */ int32 rad_sba = 0; /* sec byte addr */ int32 rad_wrp = 0; /* write prot */ int32 rad_time = 2; /* time per 12b */ int32 rad_stopioe = 1; /* stop on error */ DSPT rad_tplt[] = { /* template */ { 1, 0 }, { 1, DEV_OUT }, { 0, 0 } }; DEVICE rad_dev; t_stat rad_svc (UNIT *uptr); t_stat rad_reset (DEVICE *dptr); t_stat rad_fill (int32 sba); void rad_end_op (int32 fl); int32 rad_adjda (int32 sba, int32 inc); t_stat rad (uint32 fnc, uint32 inst, uint32 *dat); /* RAD data structures rad_dev device descriptor rad_unit unit descriptor rad_reg register list */ DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad }; UNIT rad_unit = { UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RAD_SIZE) }; REG rad_reg[] = { { ORDATA (DA, rad_da, 15) }, { GRDATA (SA, rad_sba, 8, 6, 1) }, { FLDATA (BP, rad_sba, 0) }, { FLDATA (XFR, xfr_req, XFR_V_RAD) }, { FLDATA (NOBD, rad_nobi, 0) }, { FLDATA (ERR, rad_err, 0) }, { ORDATA (PROT, rad_wrp, 8) }, { DRDATA (TIME, rad_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, rad_stopioe, 0) }, { NULL } }; MTAB rad_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE rad_dev = { "RAD", &rad_unit, rad_reg, rad_mod, 1, 8, 21, 1, 8, 24, NULL, NULL, &rad_reset, NULL, NULL, NULL, &rad_dib, DEV_DISABLE }; /* Fixed head disk routine conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result */ t_stat rad (uint32 fnc, uint32 inst, uint32 *dat) { int32 t, lun, new_ch; uint32 p; uint32 *fbuf = rad_unit.filebuf; switch (fnc) { /* case function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != rad_dib.chan) /* wrong chan? */ return SCPE_IERR; if (CHC_GETCPW (inst) > 1) /* 1-2 char/word? */ return STOP_INVIOP; if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */ return STOP_INVIOP; rad_err = 0; /* clr error */ rad_sba = 0; /* clr sec bptr */ chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */ t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD); if (t <= 0) /* seek */ t = t + RAD_NUMSC; sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2)); xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */ break; case IO_EOM1: /* EOM mode 1 */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != rad_dib.chan) /* wrong chan? */ return SCPE_IERR; if ((inst & 00600) == 00200) /* alert for sec */ alert = POT_RADS; else if ((inst & 06600) == 0) { /* alert for addr */ if (sim_is_active (&rad_unit)) /* busy? */ rad_err = 1; else { rad_nobi = (inst & 01000)? 1: 0; /* save inc type */ alert = POT_RADA; /* set alert */ } } break; case IO_DISC: /* disconnect */ rad_end_op (0); /* normal term */ if (inst & DEV_OUT) /* fill write */ return rad_fill (rad_sba); break; case IO_WREOR: /* write eor */ rad_end_op (CHF_EOR); /* eor term */ return rad_fill (rad_sba); /* fill write */ case IO_SKS: /* SKS */ new_ch = I_GETSKCH (inst); /* sks chan */ if (new_ch != rad_dib.chan) /* wrong chan? */ return SCPE_IERR; t = I_GETSKCND (inst); /* sks cond */ lun = RAD_GETLUN (rad_da); if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */ ((t == 004) && !rad_err) || /* 11026: !err */ ((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */ *dat = 1; break; case IO_READ: /* read */ p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */ xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */ rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ CRETIOE (rad_stopioe, SCPE_UNATT); } if (p >= rad_unit.capac) { /* end of disk? */ rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ return SCPE_OK; } if (rad_sba & 1) /* odd byte? */ *dat = fbuf[p] & 07777; else *dat = (fbuf[p] >> 12) & 07777; /* even */ rad_sba = rad_adjda (rad_sba, 1); /* next byte */ break; case IO_WRITE: p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */ rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ CRETIOE (rad_stopioe, SCPE_UNATT); } if ((p >= rad_unit.capac) || /* end of disk? */ (rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */ rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ return SCPE_OK; } if (rad_sba & 1) /* odd byte? */ fbuf[p] = fbuf[p] | (*dat & 07777); else fbuf[p] = (*dat & 07777) << 12; /* even */ if (p >= rad_unit.hwmark) /* mark hiwater */ rad_unit.hwmark = p + 1; rad_sba = rad_adjda (rad_sba, 1); /* next byte */ break; default: CRETINS; } return SCPE_OK; } /* PIN routine */ t_stat pin_rads (uint32 num, uint32 *dat) { *dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */ return SCPE_OK; } /* POT routine */ t_stat pot_rada (uint32 num, uint32 *dat) { rad_da = (*dat) & RAD_AMASK; /* save dsk addr */ return SCPE_OK; } /* Unit service and read/write */ t_stat rad_svc (UNIT *uptr) { xfr_req = xfr_req | XFR_RAD; /* set xfr req */ sim_activate (&rad_unit, rad_time); /* activate */ return SCPE_OK; } /* Fill incomplete sector */ t_stat rad_fill (int32 sba) { uint32 p = rad_da * RAD_NUMWD; uint32 *fbuf = rad_unit.filebuf; int32 wa = (sba + 1) >> 1; /* whole words */ if (sba && (p < rad_unit.capac)) { /* fill needed? */ for ( ; wa < RAD_NUMWD; wa++) fbuf[p + wa] = 0; if ((p + wa) >= rad_unit.hwmark) rad_unit.hwmark = p + wa + 1; rad_adjda (sba, RAD_NUMWD - 1); /* inc da */ } return SCPE_OK; } /* Adjust disk address */ int32 rad_adjda (int32 sba, int32 inc) { sba = sba + inc; if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */ if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */ ((rad_da + 1) & RAD_SCMASK); else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */ ((rad_da + 1) & RAD_TRSCMASK); sba = 0; /* start new sec */ } return sba; } /* Terminate disk operation */ void rad_end_op (int32 fl) { if (fl) /* set flags */ chan_set_flag (rad_dib.chan, fl); xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */ sim_cancel (&rad_unit); /* stop */ if (fl & CHF_ERR) { /* error? */ chan_disc (rad_dib.chan); /* disconnect */ rad_err = 1; /* set rad err */ } return; } /* Reset routine */ t_stat rad_reset (DEVICE *dptr) { chan_disc (rad_dib.chan); /* disconnect */ rad_nobi = 0; /* clear state */ rad_da = 0; rad_sba = 0; xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ sim_cancel (&rad_unit); /* deactivate */ return SCPE_OK; } simh-3.8.1/SDS/sds_io.c0000644000175000017500000012031611110642134012712 0ustar vlmvlm/* sds_io.c: SDS 940 I/O simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include "sds_defs.h" /* Data chain word */ #define CHD_INT 040 /* int on chain */ #define CHD_PAGE 037 /* new page # */ /* Interlace POT */ #define CHI_V_WC 14 /* word count */ #define CHI_M_WC 01777 #define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC) #define CHI_V_MA 0 /* mem address */ #define CHI_M_MA 037777 #define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA) /* System interrupt POT */ #define SYI_V_GRP 18 /* group */ #define SYI_M_GRP 077 #define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP) #define SYI_DIS (1 << 17) /* disarm if 0 */ #define SYI_ARM (1 << 16) /* arm if 1 */ #define SYI_M_INT 0177777 /* interrupt */ /* Pseudo-device number for EOM/SKS mode 3 */ #define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK) #define TST_XFR(d,c) (xfr_req && dev_map[d][c]) #define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c] #define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c] #define INV_DEV(d,c) (dev_dsp[d][c] == NULL) #define VLD_DEV(d,c) (dev_dsp[d][c] != NULL) #define TST_EOR(c) (chan_flag[c] & CHF_EOR) #define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN))) uint8 chan_uar[NUM_CHAN]; /* unit addr */ uint16 chan_wcr[NUM_CHAN]; /* word count */ uint16 chan_mar[NUM_CHAN]; /* mem addr */ uint8 chan_dcr[NUM_CHAN]; /* data chain */ uint32 chan_war[NUM_CHAN]; /* word assembly */ uint8 chan_cpw[NUM_CHAN]; /* char per word */ uint8 chan_cnt[NUM_CHAN]; /* char count */ uint16 chan_mode[NUM_CHAN]; /* mode */ uint16 chan_flag[NUM_CHAN]; /* flags */ static const char *chname[NUM_CHAN] = { "W", "Y", "C", "D", "E", "F", "G", "H" }; extern uint32 M[MAXMEMSIZE]; /* memory */ extern uint32 int_req; /* int req */ extern uint32 xfr_req; /* xfer req */ extern uint32 alert; /* pin/pot alert */ extern uint32 X, EM2, EM3, OV, ion, bpt; extern uint32 nml_mode, usr_mode, rtc_pie; extern int32 stop_invins, stop_invdev, stop_inviop; extern int32 mon_usr_trap; extern UNIT cpu_unit; extern FILE *sim_log; extern DEVICE *sim_devices[]; t_stat chan_reset (DEVICE *dptr); t_stat chan_read (int32 ch); t_stat chan_write (int32 ch); void chan_write_mem (int32 ch); void chan_flush_war (int32 ch); uint32 chan_mar_inc (int32 ch); t_stat chan_eor (int32 ch); t_stat pot_ilc (uint32 num, uint32 *dat); t_stat pot_dcr (uint32 num, uint32 *dat); t_stat pin_adr (uint32 num, uint32 *dat); t_stat pot_fork (uint32 num, uint32 *dat); t_stat dev_disc (uint32 ch, uint32 dev); t_stat dev_wreor (uint32 ch, uint32 dev); extern t_stat pot_RL1 (uint32 num, uint32 *dat); extern t_stat pot_RL2 (uint32 num, uint32 *dat); extern t_stat pot_RL4 (uint32 num, uint32 *dat); extern t_stat pin_rads (uint32 num, uint32 *dat); extern t_stat pot_rada (uint32 num, uint32 *dat); extern t_stat pin_dsk (uint32 num, uint32 *dat); extern t_stat pot_dsk (uint32 num, uint32 *dat); t_stat pin_mux (uint32 num, uint32 *dat); t_stat pot_mux (uint32 num, uint32 *dat); extern void set_dyn_map (void); /* SDS I/O model A device is modeled by its interactions with a channel. Devices can only be accessed via channels. Each channel has its own device address space. This means devices can only be accessed from a specific channel. I/O operations start with a channel connect. The EOM instruction is passed to the device via the conn routine. This routine is also used for non-channel EOM's to the device. For channel connects, the device must remember the channel number. The device responds (after a delay) by setting its XFR_RDY flag. This causes the channel to invoke either the read or write routine (for input or output) to get or put the next character. If the device is an asynchronous output device, it calls routine chan_set_ordy to see if there is output available. If there is, XFR_RDY is set; if not, the channel is marked to wake the attached device when output is available. This prevents invalid rate errors. Output may be terminated by a write end of record, a disconnect, or both. Write end of record occurs when the word count reaches zero on an IORD or IORP operation. It also occurs if a TOP instruction is issued. The device is expected to respond by setting the end of record indicator in the channel, which will in turn trigger an end of record interrupt. When the channel operation completes, the channel disconnects and calls the disconnect processor to perform any device specific cleanup. The differences between write end of record and disconnect are subtle. On magtape output, for example, both signal end of record; but write end of record allows the magtape to continue moving, while disconnect halts its motion. Valid devices supply a routine to handle potentially all I/O operations (connect, disconnect, read, write, write end of record, sks). There are separate routines for PIN and POT. Channels could, optionally, handle 12b or 24b characters. The simulator can support all widths. */ t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc); struct aldisp { t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */ t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */ }; /* Channel data structures chan_dev channel device descriptor chan_unit channel unit descriptor chan_reg channel register list */ UNIT chan_unit = { UDATA (NULL, 0, 0) }; REG chan_reg[] = { { BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) }, { BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) }, { BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) }, { BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) }, { BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) }, { BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) }, { BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) }, { BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) }, { BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) }, { NULL } }; MTAB chan_mod[] = { { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL, NULL, &chan_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL, NULL, &chan_show_reg, NULL } }; DEVICE chan_dev = { "CHAN", &chan_unit, chan_reg, chan_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &chan_reset, NULL, NULL, NULL }; /* Tables */ static const uint32 int_zc[8] = { INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC, INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC }; static const uint32 int_er[8] = { INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR, INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR }; /* dev_map maps device and channel numbers to a transfer flag masks */ uint32 dev_map[64][NUM_CHAN]; /* dev_dsp maps device and channel numbers to dispatch routines */ t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL }; /* dev3_dsp maps system device numbers to dispatch routines */ t_stat (*dev3_dsp[64])() = { NULL }; /* dev_alt maps alert numbers to dispatch routines */ struct aldisp dev_alt[] = { { NULL, NULL }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_ilc }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { NULL, &pot_dcr }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { &pin_adr, NULL }, { NULL, &pot_RL1 }, { NULL, &pot_RL2 }, { NULL, &pot_RL4 }, { &pin_rads, NULL }, { NULL, &pot_rada }, { &pin_dsk, &pot_dsk }, { NULL, &pot_fork }, { &pin_mux, &pot_mux } }; /* Single word I/O instructions */ t_stat op_wyim (uint32 inst, uint32 *dat) { int32 ch, dev; ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */ dev = chan_uar[ch] & DEV_MASK; /* get dev # */ if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */ if (dev == 0) /* no device? dead */ return STOP_INVIOP; return STOP_IONRDY; /* hang until full */ } *dat = chan_war[ch]; /* get data */ chan_war[ch] = 0; /* reset war */ chan_cnt[ch] = 0; /* reset cnt */ return SCPE_OK; } t_stat op_miwy (uint32 inst, uint32 dat) { int32 ch, dev; ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */ dev = chan_uar[ch] & DEV_MASK; /* get dev # */ if (chan_cnt[ch] != 0) { /* buffer full? */ if (dev == 0) /* no device? dead */ return STOP_INVIOP; return STOP_IONRDY; /* hang until full */ } chan_war[ch] = dat; /* get data */ chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */ if (chan_flag[ch] & CHF_OWAK) { /* output wake? */ if (VLD_DEV (dev, ch)) /* wake channel */ SET_XFR (dev, ch); chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; /* clear wake */ } return SCPE_OK; } t_stat op_pin (uint32 *dat) { uint32 al = alert; /* local copy */ alert = 0; /* clear alert */ if ((al == 0) || (dev_alt[al].pin == NULL)) /* inv alert? */ CRETIOP; return dev_alt[al].pin (al, dat); /* PIN from dev */ } t_stat op_pot (uint32 dat) { uint32 al = alert; /* local copy */ alert = 0; /* clear alert */ if ((al == 0) || (dev_alt[al].pot == NULL)) /* inv alert? */ CRETIOP; return dev_alt[al].pot (al, &dat); /* POT to dev */ } /* EOM/EOD */ t_stat op_eomd (uint32 inst) { uint32 mod = I_GETIOMD (inst); /* get mode */ uint32 ch = I_GETEOCH (inst); /* get chan # */ uint32 dev = inst & DEV_MASK; /* get dev # */ uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */ t_stat r; switch (mod) { case 0: /* IO control */ if (dev) { /* new dev? */ if (ch_dev) /* chan act? err */ CRETIOP; if (INV_DEV (dev, ch)) /* inv dev? err */ CRETDEV; chan_war[ch] = chan_cnt[ch] = 0; /* init chan */ chan_flag[ch] = chan_dcr[ch] = 0; chan_mode[ch] = chan_uar[ch] = 0; if (ch >= CHAN_E) chan_mode[ch] = CHM_CE; if (r = dev_dsp[dev][ch] (IO_CONN, inst, NULL)) /* connect */ return r; if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */ alert = POT_ILCY + ch; chan_mar[ch] = chan_wcr[ch] = 0; } if (chan_flag[ch] & CHF_24B) /* 24B? 1 ch/wd */ chan_cpw[ch] = 0; else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */ chan_cpw[ch] = CHC_GETCPW (inst) & 1; else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */ chan_uar[ch] = dev; /* connected */ if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */ int_req = int_req | int_zc[ch]; /* initial intr */ } else return dev_disc (ch, ch_dev); /* disconnect */ break; case 1: /* buf control */ if (QAILCE (alert)) { /* ilce alerted? */ ch = alert - POT_ILCY; /* derive chan */ if (ch >= CHAN_E) /* DACC? ext */ inst = inst | CHM_CE; chan_mode[ch] = inst; /* save mode */ chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */ (chan_mar[ch] & CHI_M_MA); chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */ (chan_wcr[ch] & CHI_M_WC); } else if (dev) { /* dev EOM */ if (INV_DEV (dev, ch)) /* inv dev? err */ CRETDEV; return dev_dsp[dev][ch] (IO_EOM1, inst, NULL); } else { /* chan EOM */ inst = inst & 047677; if (inst == 040000) { /* alert ilce */ alert = POT_ILCY + ch; chan_mar[ch] = chan_wcr[ch] = 0; } else if (inst == 002000) /* alert addr */ alert = POT_ADRY + ch; else if (inst == 001000) /* alert DCR */ alert = POT_DCRY + ch; else if (inst == 004000) { /* term output */ if (ch_dev & DEV_OUT) { /* to output dev? */ if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA? */ chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */ else return dev_wreor (ch, ch_dev); /* idle, write EOR */ } /* end else TOP */ else if (ch_dev & DEV_MT) { /* change to scan? */ chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */ chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */ } /* end else change scan */ } /* end else term output */ } /* end else chan EOM */ break; case 2: /* internal */ if (ch >= CHAN_E) { /* EOD? */ if (inst & 00300) { /* set EM? */ if (inst & 00100) EM2 = inst & 07; if (inst & 00200) EM3 = (inst >> 3) & 07; set_dyn_map (); } break; } /* end if EOD */ if (inst & 00001) /* clr OV */ OV = 0; if (inst & 00002) /* ion */ ion = 1; else if (inst & 00004) /* iof */ ion = 0; if ((inst & 00010) && (((X >> 1) ^ X) & EXPS)) OV = 1; if (inst & 00020) /* alert sys int */ alert = POT_SYSI; if (inst & 00100) /* arm clk pls */ rtc_pie = 1; else if (inst & 00200) /* disarm pls */ rtc_pie = 0; if ((inst & 01400) == 01400) /* alert RL4 */ alert = POT_RL4; else if (inst & 00400) /* alert RL1 */ alert = POT_RL1; else if (inst & 01000) /* alert RL2 */ alert = POT_RL2; if (inst & 02000) { /* nml to mon */ nml_mode = usr_mode = 0; if (inst & 00400) mon_usr_trap = 1; } break; case 3: /* special */ dev = I_GETDEV3 (inst); /* special device */ if (dev3_dsp[dev]) /* defined? */ return dev3_dsp[dev] (IO_CONN, inst, NULL); CRETINS; } /* end case */ return SCPE_OK; } /* Skip if not signal */ t_stat op_sks (uint32 inst, uint32 *dat) { uint32 mod = I_GETIOMD (inst); /* get mode */ uint32 ch = I_GETSKCH (inst); /* get chan # */ uint32 dev = inst & DEV_MASK; /* get dev # */ *dat = 0; if ((ch == 4) && !(inst & 037774)) { /* EM test */ if (((inst & 0001) && (EM2 != 2)) || ((inst & 0002) && (EM3 != 3))) *dat = 1; return SCPE_OK; } switch (mod) { case 1: /* ch, dev */ if (dev) { /* device */ if (INV_DEV (dev, ch)) /* inv dev? err */ CRETDEV; dev_dsp[dev][ch] (IO_SKS, inst, dat); /* do test */ } else { /* channel */ if (((inst & 04000) && (chan_uar[ch] == 0)) || ((inst & 02000) && (chan_wcr[ch] == 0)) || ((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) || ((inst & 00400) && (chan_flag[ch] & CHF_IREC))) *dat = 1; } break; case 2: /* internal test */ if (inst & 0001) { /* test OV */ *dat = OV ^ 1; /* skip if off */ OV = 0; /* and reset */ break; } if (((inst & 00002) && !ion) || /* ion, bpt test */ ((inst & 00004) && ion) || ((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) || ((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) || ((inst & 00040) && ((bpt & 001) == 0)) || ((inst & 00100) && ((bpt & 002) == 0)) || ((inst & 00200) && ((bpt & 004) == 0)) || ((inst & 00400) && ((bpt & 010) == 0)) || ((inst & 01000) && (chan_uar[CHAN_W] == 0)) || ((inst & 02000) && (chan_uar[CHAN_Y] == 0))) *dat = 1; break; case 3: /* special */ dev = I_GETDEV3 (inst); /* special device */ if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat); else CRETINS; } /* end case */ return SCPE_OK; } /* PIN/POT routines */ t_stat pot_ilc (uint32 num, uint32 *dat) { uint32 ch = num - POT_ILCY; chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat); chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat); chan_flag[ch] = chan_flag[ch] | CHF_ILCE; return SCPE_OK; } t_stat pot_dcr (uint32 num, uint32 *dat) { uint32 ch = num - POT_DCRY; chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE); chan_flag[ch] = chan_flag[ch] | CHF_DCHN; return SCPE_OK; } t_stat pin_adr (uint32 num, uint32 *dat) { uint32 ch = num - POT_ADRY; *dat = chan_mar[ch] & PAMASK; return SCPE_OK; } /* System interrupt POT. The SDS 940 timesharing system uses a permanently asserted system interrupt as a way of forking the teletype input interrupt handler to a lower priority. The interrupt is armed to set up the fork, and disarmed in the fork routine */ t_stat pot_fork (uint32 num, uint32 *dat) { uint32 igrp = SYI_GETGRP (*dat); /* get group */ uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */ if (igrp == (VEC_FORK / 020)) { /* right group? */ if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */ int_req = int_req | INT_FORK; if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */ int_req = int_req & ~INT_FORK; } return SCPE_OK; } /* Channel read invokes the I/O device to get the next character and, if not end of record, assembles it into the word assembly register. If the interlace is on, the full word is stored in memory. The key difference points for the various terminal functions are end of record comp: EOT interrupt IORD, IOSD: EOR interrupt, disconnect IORP, IOSP: EOR interrupt, interrecord interlace off: comp: EOW interrupt IORD, IORP: ignore IOSD, IOSP: overrun error --wcr == 0: comp: clear interlace IORD, IORP, IOSP: ZWC interrupt IOSD: ZWC interrupt, EOR interrupt, disconnect Note that the channel can be disconnected if CHN_EOR is set, but must not be if XFR_REQ is set */ t_stat chan_read (int32 ch) { uint32 dat = 0; uint32 dev = chan_uar[ch] & DEV_MASK; uint32 tfnc = CHM_GETFNC (chan_mode[ch]); t_stat r = SCPE_OK; if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */ if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */ r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */ if (r) /* error? */ chan_flag[ch] = chan_flag[ch] | CHF_ERR; if (chan_flag[ch] & CHF_24B) /* 24B? */ chan_war[ch] = dat; else if (chan_flag[ch] & CHF_12B) /* 12B? */ chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK; else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK; if (chan_flag[ch] & CHF_SCAN) /* scanning? */ chan_cnt[ch] = chan_cpw[ch]; /* never full */ else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */ if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */ if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */ chan_write_mem (ch); /* write to mem */ if (chan_wcr[ch] == 0) { /* wc zero? */ chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */ if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC)) int_req = int_req | int_zc[ch]; /* zwc interrupt */ if (tfnc == CHM_IOSD) { /* IOSD? also EOR */ if (chan_mode[ch] & CHM_ER) int_req = int_req | int_er[ch]; dev_disc (ch, dev); /* disconnect */ } /* end if IOSD */ } /* end if wcr == 0 */ } /* end if ilce on */ else { /* interlace off */ if (TST_EOR (ch)) /* eor? */ return chan_eor (ch); if (tfnc == CHM_COMP) { /* C: EOW, intr */ if (ion) int_req = int_req | int_zc[ch]; } else if (tfnc & CHM_SGNL) /* Sx: error */ chan_flag[ch] = chan_flag[ch] | CHF_ERR; else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */ } /* end else ilce */ } /* end if full */ } /* end if xfr */ if (TST_EOR (ch)) { /* end record? */ if (tfnc == CHM_COMP) /* C: fill war */ chan_flush_war (ch); else if (chan_cnt[ch]) { /* RX, CX: fill? */ chan_flush_war (ch); /* fill war */ if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */ chan_write_mem (ch); } /* end else if cnt */ return chan_eor (ch); /* eot/eor int */ } return r; } void chan_write_mem (int32 ch) { WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */ chan_mar[ch] = chan_mar_inc (ch); /* incr mar */ chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */ chan_war[ch] = 0; /* reset war */ chan_cnt[ch] = 0; /* reset cnt */ return; } void chan_flush_war (int32 ch) { int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1; if (i) { if (chan_flag[ch] & CHF_24B) chan_war[ch] = 0; else if (chan_flag[ch] & CHF_12B) chan_war[ch] = (chan_war[ch] << 12) & DMASK; else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK; chan_cnt[ch] = chan_cpw[ch] + 1; } return; } /* Channel write gets the next character and sends it to the I/O device. If this is the last character in an interlace operation, the end of record operation is invoked. The key difference points for the various terminal functions are end of record: comp: EOT interrupt IORD, IOSD: EOR interrupt, disconnect IORP, IOSP: EOR interrupt, interrecord interlace off: if not end of record, EOW interrupt --wcr == 0: comp: EOT interrupt, disconnect IORD, IORP: ignore IOSD: ZWC interrupt, disconnect IOSP: ZWC interrupt, interrecord */ t_stat chan_write (int32 ch) { uint32 dat = 0; uint32 dev = chan_uar[ch] & DEV_MASK; uint32 tfnc = CHM_GETFNC (chan_mode[ch]); t_stat r = SCPE_OK; if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */ if (INV_DEV (dev, ch)) /* invalid dev? */ CRETIOP; if (chan_cnt[ch] == 0) { /* buffer empty? */ if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */ chan_war[ch] = ReadP (chan_mar[ch]); chan_mar[ch] = chan_mar_inc (ch); /* incr mar */ chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */ chan_cnt[ch] = chan_cpw[ch] + 1; /* set cnt */ } else { /* ilce off */ CLR_XFR (dev, ch); /* cant xfr */ if (TST_EOR (dev)) /* EOR? */ return chan_eor (ch); chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */ return SCPE_OK; } /* end else ilce */ } /* end if cnt */ chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */ if (chan_flag[ch] & CHF_24B) /* 24B? */ dat = chan_war[ch]; else if (chan_flag[ch] & CHF_12B) { /* 12B? */ dat = (chan_war[ch] >> 12) & 07777; /* get halfword */ chan_war[ch] = (chan_war[ch] << 12) & DMASK; /* remove from war */ } else { /* 6B */ dat = (chan_war[ch] >> 18) & 077; /* get char */ chan_war[ch] = (chan_war[ch] << 6) & DMASK; /* remove from war */ } r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */ if (r) /* error? */ chan_flag[ch] = chan_flag[ch] | CHF_ERR; if (chan_cnt[ch] == 0) { /* buf empty? */ if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */ if (chan_wcr[ch] == 0) { /* wc now 0? */ chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */ if (tfnc == CHM_COMP) { /* compatible? */ if (ion) int_req = int_req | int_zc[ch]; dev_disc (ch, dev); /* disconnnect */ } /* end if comp */ else { /* extended */ if (chan_mode[ch] & CHM_ZC) /* ZWC int */ int_req = int_req | int_zc[ch]; if (tfnc == CHM_IOSD) { /* SD */ if (chan_mode[ch] & CHM_ER) /* EOR int */ int_req = int_req | int_er[ch]; dev_disc (ch, dev); /* disconnnect */ } /* end if SD */ else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */ (chan_flag[ch] & CHF_TOP)) dev_wreor (ch, dev); /* R: write EOR */ chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; } /* end else comp */ } /* end if wcr */ } /* end if ilce */ else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */ chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */ dev_wreor (ch, dev); /* write EOR */ } else if (ion) /* no TOP, EOW intr */ int_req = int_req | int_zc[ch]; } /* end if cnt */ } /* end if xfr */ if (TST_EOR (ch)) /* eor rcvd? */ return chan_eor (ch); return r; } /* MAR increment */ uint32 chan_mar_inc (int32 ch) { uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */ if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */ chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */ if (chan_dcr[ch] & CHD_INT) /* if armed, intr */ int_req = int_req | int_zc[ch]; t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; /* new mar */ } return t; } /* End of record action */ t_stat chan_eor (int32 ch) { uint32 tfnc = CHM_GETFNC (chan_mode[ch]); uint32 dev = chan_uar[ch] & DEV_MASK; chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */ if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER)) int_req = int_req | int_er[ch]; /* EOT/EOR? */ if (dev && (tfnc & CHM_PROC)) /* P, still conn? */ chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */ else return dev_disc (ch, dev); /* disconnect */ return SCPE_OK; } /* Utility routines */ t_stat dev_disc (uint32 ch, uint32 dev) { chan_uar[ch] = 0; /* disconnect */ if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL); return SCPE_OK; } t_stat dev_wreor (uint32 ch, uint32 dev) { if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_WREOR, dev, NULL); chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */ return SCPE_OK; } /* Externally visible routines */ /* Channel driver */ t_stat chan_process (void) { int32 i, dev; t_stat r; for (i = 0; i < NUM_CHAN; i++) { /* loop thru */ dev = chan_uar[i] & DEV_MASK; /* get dev */ if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */ if (dev & DEV_OUT) /* write */ r = chan_write (i); else r = chan_read (i); /* read */ if (r) return r; } } return SCPE_OK; } /* Test for channel active */ t_bool chan_testact (void) { int32 i, dev; for (i = 0; i < NUM_CHAN; i++) { dev = chan_uar[i] & DEV_MASK; if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) return 1; } return 0; } /* Async output device ready for more data */ void chan_set_ordy (int32 ch) { if ((ch >= 0) && (ch < NUM_CHAN)) { int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */ if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */ SET_XFR (dev, ch); /* set xfr flg */ else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; /* need wakeup */ } return; } /* Set flag in channel */ void chan_set_flag (int32 ch, uint32 fl) { if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl; return; } /* Set UAR in channel */ void chan_set_uar (int32 ch, uint32 dev) { if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK; return; } /* Disconnect channel */ void chan_disc (int32 ch) { if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0; return; } /* Reset channels */ t_stat chan_reset (DEVICE *dptr) { int32 i; xfr_req = 0; for (i = 0; i < NUM_CHAN; i++) { chan_uar[i] = 0; chan_wcr[i] = 0; chan_mar[i] = 0; chan_dcr[i] = 0; chan_war[i] = 0; chan_cpw[i] = 0; chan_cnt[i] = 0; chan_mode[i] = 0; chan_flag[i] = 0; } return SCPE_OK; } /* Channel assignment routines */ t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc) { DEVICE *dptr; DIB *dibp; int32 i; if (sptr == NULL) /* valid args? */ return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; for (i = 0; i < NUM_CHAN; i++) { /* match input */ if (strcmp (sptr, chname[i]) == 0) { /* find string */ if (val && !(val & (1 << i))) /* legal? */ return SCPE_ARG; dibp->chan = i; /* store new */ return SCPE_OK; } } return SCPE_ARG; } t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "channel=%s", chname[dibp->chan]); return SCPE_OK; } /* Init device tables */ t_bool io_init (void) { DEVICE *dptr; DIB *dibp; DSPT *tplp; int32 ch; uint32 i, j, dev, doff; /* Clear dispatch table, device map */ for (i = 0; i < NUM_CHAN; i++) { for (j = 0; j < (DEV_MASK + 1); j++) { dev_dsp[j][i] = NULL; dev_map[j][i] = 0; } } /* Test each device for conflict; add to map; init tables */ for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((dibp == NULL) || (dptr->flags & DEV_DIS)) /* exist, enabled? */ continue; ch = dibp->chan; /* get channel */ dev = dibp->dev; /* get device num */ if (ch < 0) /* special device */ dev3_dsp[dev] = dibp->iop; else { if (dibp->tplt == NULL) /* must have template */ return TRUE; for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */ for (j = 0; j < tplp->num; j++) { /* repeat as needed */ doff = dev + tplp->off + j; /* get offset dnum */ if (dev_map[doff][ch]) { /* slot in use? */ printf ("Device number conflict, chan = %s, devno = %02o\n", chname[ch], doff); if (sim_log) fprintf (sim_log, "Device number conflict, chan = %s, dev = %02o\n", chname[ch], doff); return TRUE; } dev_map[doff][ch] = dibp->xfr; /* set xfr flag */ dev_dsp[doff][ch] = dibp->iop; /* set dispatch */ } /* end for j */ } /* end for tplt */ } /* end else */ } /* end for i */ return FALSE; } /* Display channel state */ t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc) { if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR; fprintf (st, "UAR: %02o\n", chan_uar[val]); fprintf (st, "WCR: %05o\n", chan_wcr[val]); fprintf (st, "MAR: %06o\n", chan_mar[val]); fprintf (st, "DCR: %02o\n", chan_dcr[val]); fprintf (st, "WAR: %08o\n", chan_war[val]); fprintf (st, "CPW: %o\n", chan_cpw[val]); fprintf (st, "CNT: %o\n", chan_cnt[val]); fprintf (st, "MODE: %03o\n", chan_mode[val]); fprintf (st, "FLAG: %04o\n", chan_flag[val]); return SCPE_OK; } simh-3.8.1/SDS/sds_dsk.c0000644000175000017500000003777511110636660013113 0ustar vlmvlm/* sds_dsk.c: SDS 940 moving head disk simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dsk moving head disk The SDS 9164 disk has a subsector feature, allowing each 64W sector to be viewed as 16W packets. In addition, it has a chaining feature, allowing records to be extended beyond a sector boundary. To accomodate this, the first word of each sector has 3 extra bits: <26> = end of chain flag <25:24> = 4 - number of packets These values were chosen so that 000 = continue chain, full sector. */ #include "sds_defs.h" #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define DSK_PKTWD 16 /* words/packet */ #define DSK_NUMPKT 4 /* packets/sector */ #define DSK_NUMWD (DSK_PKTWD*DSK_NUMPKT) /* words/sector */ #define DSK_N_SC 5 /* sect addr width */ #define DSK_V_SC 0 /* position */ #define DSK_M_SC ((1 << DSK_N_SC) - 1) /* mask */ #define DSK_NUMSC (1 << DSK_N_SC) /* sectors/track */ #define DSK_N_TR 8 /* track addr width */ #define DSK_V_TR (DSK_N_SC) /* position */ #define DSK_M_TR ((1 << DSK_N_TR) - 1) /* mask */ #define DSK_NUMTR (1 << DSK_N_TR) /* tracks/surface */ #define DSK_N_SF 5 /* surf addr width */ #define DSK_V_SF (DSK_N_SC + DSK_N_TR) /* position */ #define DSK_M_SF ((1 << DSK_N_SF) - 1) /* mask */ #define DSK_NUMSF (1 << DSK_N_SF) /* surfaces/drive */ #define DSK_SCSIZE (DSK_NUMSF*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */ #define DSK_AMASK (DSK_SCSIZE - 1) /* address mask */ #define DSK_SIZE (DSK_SCSIZE * DSK_NUMWD) /* words/drive */ #define DSK_GETTR(x) (((x) >> DSK_V_TR) & DSK_M_TR) #define cyl u3 /* curr cylinder */ #define DSK_SIP (1 << (DSK_N_TR + 2)) #define DSK_V_PKT 24 #define DSK_M_PKT 03 #define DSK_V_CHN 26 #define DSK_GETPKT(x) (4 - (((x) >> DSK_V_PKT) & DSK_M_PKT)) #define DSK_ENDCHN(x) ((x) & (1 << DSK_V_CHN)) extern uint32 xfr_req; extern uint32 alert; extern int32 stop_invins, stop_invdev, stop_inviop; int32 dsk_da = 0; /* disk addr */ int32 dsk_op = 0; /* operation */ int32 dsk_err = 0; /* error flag */ uint32 dsk_buf[DSK_NUMWD]; /* sector buf */ int32 dsk_bptr = 0; /* byte ptr */ int32 dsk_blnt = 0; /* byte lnt */ int32 dsk_time = 5; /* time per char */ int32 dsk_stime = 200; /* seek time */ int32 dsk_stopioe = 1; DSPT dsk_tplt[] = { /* template */ { 1, 0 }, { 1, DEV_OUT }, { 0, 0 } }; DEVICE dsk_dev; t_stat dsk_svc (UNIT *uptr); t_stat dsk_reset (DEVICE *dptr); t_stat dsk_fill (uint32 dev); t_stat dsk_read_buf (uint32 dev); t_stat dsk_write_buf (uint32 dev); void dsk_end_op (uint32 fl); t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat); /* DSK data structures dsk_dev device descriptor dsk_unit unit descriptor dsk_reg register list */ DIB dsk_dib = { CHAN_F, DEV_DSK, XFR_DSK, dsk_tplt, &dsk }; UNIT dsk_unit = { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE, DSK_SIZE) }; REG dsk_reg[] = { { BRDATA (BUF, dsk_buf, 8, 24, DSK_NUMWD) }, { DRDATA (BPTR, dsk_bptr, 9), PV_LEFT }, { DRDATA (BLNT, dsk_bptr, 9), PV_LEFT }, { ORDATA (DA, dsk_da, 21) }, { ORDATA (INST, dsk_op, 24) }, { FLDATA (XFR, xfr_req, XFR_V_DSK) }, { FLDATA (ERR, dsk_err, 0) }, { DRDATA (WTIME, dsk_time, 24), REG_NZ + PV_LEFT }, { DRDATA (STIME, dsk_stime,24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, dsk_stopioe, 0) }, { NULL } }; MTAB dsk_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE dsk_dev = { "DSK", &dsk_unit, dsk_reg, dsk_mod, 1, 8, 24, 1, 8, 27, NULL, NULL, &dsk_reset, NULL, NULL, NULL, &dsk_dib, DEV_DISABLE }; /* Moving head disk routine conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result */ t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat) { int32 i, t, new_ch, dsk_wptr, dsk_byte; t_stat r; switch (fnc) { /* case on function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != dsk_dib.chan) /* wrong chan? */ return SCPE_IERR; dsk_op = inst; /* save instr */ dsk_bptr = dsk_blnt = 0; /* init ptrs */ for (i = 0; i < DSK_NUMWD; i++) /* clear buffer */ dsk_buf[i] = 0; xfr_req = xfr_req & ~XFR_DSK; /* clr xfr flg */ sim_activate (&dsk_unit, dsk_stime); /* activate */ break; case IO_EOM1: /* EOM mode 1 */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != dsk_dib.chan) /* wrong chan? */ return SCPE_IERR; if (inst & 07600) /* inv inst? */ CRETIOP; alert = POT_DSK; /* alert */ break; case IO_DISC: /* disconnect */ dsk_end_op (0); /* normal term */ if (inst & DEV_OUT) return dsk_fill (inst); /* fill write */ break; case IO_WREOR: /* write eor */ dsk_end_op (CHF_EOR); /* eor term */ return dsk_fill (inst); /* fill write */ case IO_SKS: /* SKS */ new_ch = I_GETSKCH (inst); /* sks chan */ if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */ t = I_GETSKCND (inst); /* sks cond */ if (((t == 000) && !sim_is_active (&dsk_unit) &&/* 10026: ready */ (dsk_unit.flags & UNIT_ATT)) || ((t == 004) && !dsk_err && /* 11026: !err */ (dsk_unit.flags & UNIT_ATT)) || ((t == 010) && ((dsk_unit.cyl & DSK_SIP) == 0)) || /* 12026: on trk */ ((t == 014) && !(dsk_unit.flags & UNIT_WPRT)) || /* 13026: !wrprot */ ((t == 001) && (dsk_unit.flags & UNIT_ATT))) /* 10226: online */ *dat = 1; break; case IO_READ: xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ if (dsk_bptr >= dsk_blnt) { /* no more data? */ if (r = dsk_read_buf (inst)) /* read sector */ return r; } dsk_wptr = dsk_bptr >> 2; /* word pointer */ dsk_byte = dsk_bptr & 03; /* byte in word */ *dat = (dsk_buf[dsk_wptr] >> ((3 - dsk_byte) * 6)) & 077; dsk_bptr = dsk_bptr + 1; /* incr buf ptr */ if ((dsk_bptr >= dsk_blnt) && /* end sector, */ ((dsk_op & CHC_BIN) || DSK_ENDCHN (dsk_buf[0])))/* sec mode | eoch? */ dsk_end_op (CHF_EOR); /* eor term */ break; case IO_WRITE: xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ if (dsk_bptr >= (DSK_NUMWD * 4)) { /* full? */ if (r = dsk_write_buf (inst)) /* write sector */ return r; } dsk_wptr = dsk_bptr >> 2; /* word pointer */ dsk_buf[dsk_wptr] = ((dsk_buf[dsk_wptr] << 6) | (*dat & 077)) & DMASK; dsk_bptr = dsk_bptr + 1; /* incr buf ptr */ break; default: CRETINS; } return SCPE_OK; } /* PIN routine - return disk address */ t_stat pin_dsk (uint32 num, uint32 *dat) { *dat = dsk_da; /* ret disk addr */ return SCPE_OK; } /* POT routine - start seek */ t_stat pot_dsk (uint32 num, uint32 *dat) { int32 st; if (sim_is_active (&dsk_unit)) /* busy? wait */ return STOP_IONRDY; dsk_da = (*dat) & DSK_AMASK; /* save dsk addr */ st = abs (DSK_GETTR (dsk_da) - /* calc seek time */ (dsk_unit.cyl & DSK_M_TR)) * dsk_stime; if (st == 0 /* min time */ ) st = dsk_stime; sim_activate (&dsk_unit, st); /* set timer */ dsk_unit.cyl = dsk_unit.cyl | DSK_SIP; /* seeking */ return SCPE_OK; } /* Unit service and read/write */ t_stat dsk_svc (UNIT *uptr) { if (uptr->cyl & DSK_SIP) { /* end seek? */ uptr->cyl = DSK_GETTR (dsk_da); /* on cylinder */ if (dsk_op) /* sched r/w */ sim_activate (&dsk_unit, dsk_stime); } else { xfr_req = xfr_req | XFR_DSK; /* set xfr req */ sim_activate (&dsk_unit, dsk_time); /* activate */ } return SCPE_OK; } /* Read sector */ t_stat dsk_read_buf (uint32 dev) { int32 da, pkts, awc; if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */ dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ CRETIOE (dsk_stopioe, SCPE_UNATT); } da = dsk_da * DSK_NUMWD * sizeof (uint32); fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */ awc = fxread (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref); if (ferror (dsk_unit.fileref)) { /* error? */ dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ return SCPE_IOERR; } for ( ; awc < DSK_NUMWD; awc++) dsk_buf[awc] = 0; pkts = DSK_GETPKT (dsk_buf[0]); /* get packets */ dsk_blnt = pkts * DSK_PKTWD * 4; /* new buf size */ dsk_bptr = 0; /* init bptr */ dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */ return SCPE_OK; } /* Write sector. If this routine is called directly, then the sector buffer is full, and there is at least one more character to write; therefore, there are 4 packets in the sector, and the sector is not the end of the chain. */ t_stat dsk_write_buf (uint32 dev) { int32 i, da; if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */ dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ CRETIOE (dsk_stopioe, SCPE_UNATT); } if (dsk_unit.flags & UNIT_WPRT) { /* write prot? */ dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ return SCPE_OK; } da = dsk_da * DSK_NUMWD * sizeof (uint32); fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */ fxwrite (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref); if (ferror (dsk_unit.fileref)) { /* error? */ dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ return SCPE_IOERR; } dsk_bptr = 0; /* init bptr */ dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */ for (i = 0; i < DSK_NUMWD; i++) /* clear buffer */ dsk_buf[i] = 0; return SCPE_OK; } /* Fill incomplete sector at end of operation. Calculate the number of packets and set the end of chain flag. */ t_stat dsk_fill (uint32 dev) { int32 nochn = (dsk_op & CHC_BIN)? 0: 1; /* chain? */ int32 pktend = (dsk_bptr + ((DSK_PKTWD * 4) - 1)) & /* end pkt */ ~((DSK_PKTWD * 4) - 1); int32 pkts = pktend / (DSK_PKTWD * 4); /* # packets */ if (dsk_bptr == 0) /* no fill? */ return SCPE_OK; for ( ; dsk_bptr < pktend; dsk_bptr++) { /* fill packet */ int32 dsk_wptr = dsk_bptr >> 2; dsk_buf[dsk_wptr] = (dsk_buf[dsk_wptr] << 6) & DMASK; } dsk_buf[0] = dsk_buf[0] | (nochn << DSK_V_CHN) | /* insert chain, */ ((4 - pkts) << DSK_V_PKT); /* num pkts */ return dsk_write_buf (dev); /* write sec */ } /* Terminate DSK operation */ void dsk_end_op (uint32 fl) { if (fl) /* set flags */ chan_set_flag (dsk_dib.chan, fl); dsk_op = 0; /* clear op */ xfr_req = xfr_req & ~XFR_DSK; /* clear xfr */ sim_cancel (&dsk_unit); /* stop */ if (fl & CHF_ERR) { /* error? */ chan_disc (dsk_dib.chan); /* disconnect */ dsk_err = 1; /* set disk err */ } return; } /* Reset routine */ t_stat dsk_reset (DEVICE *dptr) { int32 i; chan_disc (dsk_dib.chan); /* disconnect */ dsk_da = 0; /* clear state */ dsk_op = 0; dsk_err = 0; dsk_bptr = dsk_blnt = 0; xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ sim_cancel (&dsk_unit); /* deactivate */ dsk_unit.cyl = 0; for (i = 0; i < DSK_NUMWD; i++) /* clear buffer */ dsk_buf[i] = 0; return SCPE_OK; } simh-3.8.1/SDS/sds_mux.c0000644000175000017500000005000611111105360013106 0ustar vlmvlm/* sds_mux.c: SDS 940 terminal multiplexor simulator Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mux terminal multiplexor 19-Nov-08 RMS Revixed for common TMXR show routines 29-Dec-06 RMS Revised to use console conversion routines 29-Jun-05 RMS Added SET MUXLn DISCONNECT 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS 05-Jan-04 RMS Revised for tmxr library changes 09-May-03 RMS Added network device flag This module implements up to 32 individual serial interfaces, representing either the project Genie terminal multiplexor or the SDS 940 CTE option. */ #include "sds_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #define PROJ_GENIE (cpu_unit.flags & UNIT_GENIE) #define MUX_NUMLIN mux_desc.lines #define MUX_LINES 32 /* lines */ #define MUX_FLAGS 4 /* intr per line */ #define MUX_FLAGMASK (MUX_FLAGS - 1) #define MUX_SCANMAX (MUX_LINES * MUX_FLAGS) /* flags to scan */ #define MUX_SCANMASK (MUX_SCANMAX - 1) #define MUX_INIT_POLL 8000 #define MUXL_WAIT 500 #define MUX_SETFLG(l,x) mux_flags[((l) * MUX_FLAGS) + (x)] = 1 #define MUX_SETINT(x) int_req = int_req | (INT_MUXR >> (x)) #define MUX_CLRINT(x) int_req = int_req & ~(INT_MUXR >> (x)) /* PIN/POT */ #define P_V_CHAR 16 /* char */ #define P_M_CHAR 0377 #define P_CHAR(x) (((x) >> P_V_CHAR) & P_M_CHAR) #define PIN_OVR 000100000 /* overrun */ #define POT_NOX 000100000 /* no xmit */ #define POT_XMI 000040000 /* xmit int */ #define POT_GLNE 000020000 /* Genie: enable */ #define POT_SCDT 000020000 /* 940: clr DTR */ #define P_V_CHAN 0 /* channel */ #define P_M_CHAN (MUX_LINES - 1) #define P_CHAN(x) (((x) >> P_V_CHAN) & P_M_CHAN) /* SKS 940 */ #define SKS_XBE 000001000 /* xmt buf empty */ #define SKS_CRO 000000400 /* carrier on */ #define SKS_DSR 000000200 /* data set ready */ #define SKS_CHAN(x) P_CHAN(x) /* SKS Genie */ #define SKG_V_CHAN 7 #define SKG_M_CHAN (MUX_LINES - 1) #define SKG_CHAN(x) (((x) >> SKG_V_CHAN) & SKG_M_CHAN) /* Flags */ #define MUX_FRCV 0 /* receive */ #define MUX_FXMT 1 /* transmit */ #define MUX_FCRN 2 /* carrier on */ #define MUX_FCRF 3 /* carrier off */ /* Line status */ #define MUX_SCHP 001 /* char pending */ #define MUX_SOVR 002 /* overrun */ #define MUX_SLNE 004 /* line enabled */ #define MUX_SXIE 010 /* xmt int enab */ #define MUX_SCRO 020 /* carrier on */ #define MUX_SDSR 040 /* data set ready */ /* Data */ extern uint32 alert, int_req; extern int32 stop_invins, stop_invdev, stop_inviop; extern UNIT cpu_unit; uint8 mux_rbuf[MUX_LINES]; /* rcv buf */ uint8 mux_xbuf[MUX_LINES]; /* xmt buf */ uint8 mux_sta[MUX_LINES]; /* status */ uint8 mux_flags[MUX_SCANMAX]; /* flags */ uint32 mux_tps = 100; /* polls/second */ uint32 mux_scan = 0; /* scanner */ uint32 mux_slck = 0; /* scanner locked */ TMLN mux_ldsc[MUX_LINES] = { 0 }; /* line descriptors */ TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc }; /* mux descriptor */ t_stat mux (uint32 fnc, uint32 inst, uint32 *dat); t_stat muxi_svc (UNIT *uptr); t_stat muxo_svc (UNIT *uptr); t_stat mux_reset (DEVICE *dptr); t_stat mux_attach (UNIT *uptr, char *cptr); t_stat mux_detach (UNIT *uptr); t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); void mux_reset_ln (int32 ln); void mux_scan_next (void); /* MUX data structures mux_dev MUX device descriptor mux_unit MUX unit descriptor mux_reg MUX register list mux_mod MUX modifiers list */ DIB mux_dib = { -1, DEV3_GMUX, 0, NULL, &mux }; UNIT mux_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), MUX_INIT_POLL }; REG mux_reg[] = { { BRDATA (STA, mux_sta, 8, 6, MUX_LINES) }, { BRDATA (RBUF, mux_rbuf, 8, 8, MUX_LINES) }, { BRDATA (XBUF, mux_xbuf, 8, 8, MUX_LINES) }, { BRDATA (INT, mux_flags, 8, 1, MUX_SCANMAX) }, { ORDATA (SCAN, mux_scan, 7) }, { FLDATA (SLCK, mux_slck, 0) }, { DRDATA (TPS, mux_tps, 8), REG_NZ + PV_LEFT }, { NULL } }; MTAB mux_mod[] = { { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &mux_vlines, tmxr_show_lines, (void *) &mux_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &mux_desc }, { 0 } }; DEVICE mux_dev = { "MUX", &mux_unit, mux_reg, mux_mod, 1, 10, 31, 1, 8, 8, &tmxr_ex, &tmxr_dep, &mux_reset, NULL, &mux_attach, &mux_detach, &mux_dib, DEV_NET | DEV_DISABLE }; /* MUXL data structures muxl_dev MUXL device descriptor muxl_unit MUXL unit descriptor muxl_reg MUXL register list muxl_mod MUXL modifiers list */ UNIT muxl_unit[] = { { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT } }; MTAB muxl_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &mux_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &mux_desc }, { 0 } }; REG muxl_reg[] = { { URDATA (TIME, muxl_unit[0].wait, 10, 24, 0, MUX_LINES, REG_NZ + PV_LEFT) }, { NULL } }; DEVICE muxl_dev = { "MUXL", muxl_unit, muxl_reg, muxl_mod, MUX_LINES, 10, 31, 1, 8, 8, NULL, NULL, &mux_reset, NULL, NULL, NULL, NULL, 0 }; /* MUX: IO routine */ /* Mux routine - EOM 30001 or EOM 77777,2 */ t_stat mux (uint32 fnc, uint32 inst, uint32 *dat) { uint32 ln; switch (fnc) { case IO_CONN: /* connect */ if ((PROJ_GENIE && (inst == 000230001)) || /* set alert */ (!PROJ_GENIE && (inst == 020277777))) alert = POT_MUX; else CRETINS; break; case IO_SKS: /* skip */ if (PROJ_GENIE && ((inst & 077770077) == 004030001)) { ln = SKG_CHAN (inst); /* get line */ if (!sim_is_active (&muxl_unit[ln])) *dat = 1; } else if (!PROJ_GENIE && ((inst & 077776000) == 024076000)) { ln = SKS_CHAN (inst); /* get line */ if (inst & (SKS_XBE|SKS_CRO|SKS_DSR)) *dat = 1; if (((inst & SKS_XBE) && sim_is_active (&muxl_unit[ln])) || ((inst & SKS_CRO) && !(mux_sta[ln] & MUX_SCRO)) || ((inst & SKS_DSR) && !(mux_sta[ln] & MUX_SDSR))) *dat = 0; /* no skip if fail */ } else CRETINS; default: return SCPE_IERR; } /* end case */ return SCPE_OK; } /* PIN routine */ t_stat pin_mux (uint32 num, uint32 *dat) { uint32 ln = mux_scan >> 2; uint32 flag = mux_scan & MUX_FLAGMASK; mux_scan = mux_scan & MUX_SCANMASK; /* mask scan */ mux_flags[mux_scan] = 0; /* clear flag */ if (flag == MUX_FRCV) { /* rcv event? */ *dat = ln | ((uint32) mux_rbuf[ln] << P_V_CHAR) | /* line + char + */ ((mux_sta[ln] & MUX_SOVR)? PIN_OVR: 0); /* overrun */ mux_sta[ln] = mux_sta[ln] & ~(MUX_SCHP | MUX_SOVR); } else *dat = ln; /* just line */ mux_slck = 0; /* unlock scanner */ mux_scan_next (); /* kick scanner */ return SCPE_OK; } t_stat pot_mux (uint32 num, uint32 *dat) { uint32 ln = P_CHAN (*dat); uint32 chr = P_CHAR (*dat); if (PROJ_GENIE && ((*dat & POT_GLNE) == 0)) { /* Genie disable? */ mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */ mux_ldsc[ln].rcve = 0; } else if (!PROJ_GENIE && (*dat & POT_SCDT)) { /* SDS disable? */ if (mux_ldsc[ln].conn) { /* connected? */ tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n"); tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */ mux_reset_ln (ln); /* reset state */ MUX_SETFLG (ln, MUX_FCRF); /* set carrier off */ mux_scan_next (); /* kick scanner */ } mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */ mux_ldsc[ln].rcve = 0; } else { /* enabled */ if ((*dat & POT_NOX) == 0) { /* output char? */ mux_xbuf[ln] = chr; /* store char */ sim_activate (&muxl_unit[ln], muxl_unit[ln].wait); } if (*dat & POT_XMI) mux_sta[ln] = mux_sta[ln] | MUX_SXIE; else mux_sta[ln] = mux_sta[ln] & ~MUX_SXIE; mux_sta[ln] = mux_sta[ln] | MUX_SLNE; /* line is enabled */ mux_ldsc[ln].rcve = 1; } return SCPE_OK; } /* Unit service - receive side Poll all active lines for input Poll for new connections */ t_stat muxi_svc (UNIT *uptr) { int32 ln, c, t; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; t = sim_rtcn_calb (mux_tps, TMR_MUX); /* calibrate */ sim_activate (uptr, t); /* continue poll */ ln = tmxr_poll_conn (&mux_desc); /* look for connect */ if (ln >= 0) { /* got one? */ if (!PROJ_GENIE && (mux_sta[ln] & MUX_SLNE)) { /* modem & DTR? */ mux_sta[ln] = mux_sta[ln] | (MUX_SCRO|MUX_SDSR);/* carrier on */ MUX_SETFLG (ln, MUX_FCRN); /* set carr on flag */ mux_scan_next (); /* kick scanner */ } mux_ldsc[ln].rcve = 1; /* set rcv enable */ } tmxr_poll_rx (&mux_desc); /* poll for input */ for (ln = 0; ln < MUX_NUMLIN; ln++) { /* loop thru lines */ if (mux_ldsc[ln].conn) { /* connected? */ if (c = tmxr_getc_ln (&mux_ldsc[ln])) { /* get char */ if (mux_sta[ln] & MUX_SCHP) /* already got one? */ mux_sta[ln] = mux_sta[ln] | MUX_SOVR; /* overrun */ else mux_sta[ln] = mux_sta[ln] | MUX_SCHP; /* char pending */ if (c & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); mux_rbuf[ln] = c; /* save char */ MUX_SETFLG (ln, MUX_FRCV); /* set rcv flag */ mux_scan_next (); /* kick scanner */ } } else mux_sta[ln] = 0; /* disconnected */ } /* end for */ return SCPE_OK; } /* Unit service - transmit side */ t_stat muxo_svc (UNIT *uptr) { int32 c; uint32 ln = uptr - muxl_unit; /* line # */ if (mux_ldsc[ln].conn) { /* connected? */ if (mux_ldsc[ln].xmte) { /* xmt enabled? */ c = sim_tt_outcvt (mux_xbuf[ln], TT_GET_MODE (muxl_unit[ln].flags)); if (c >= 0) /* output char */ tmxr_putc_ln (&mux_ldsc[ln], c); tmxr_poll_tx (&mux_desc); /* poll xmt */ } else { /* buf full */ tmxr_poll_tx (&mux_desc); /* poll xmt */ sim_activate (uptr, muxl_unit[ln].wait); /* wait */ return SCPE_OK; } } if (mux_sta[ln] & MUX_SXIE) { MUX_SETFLG (ln, MUX_FXMT); /* set flag */ mux_scan_next (); /* kick scanner */ } return SCPE_OK; } /* Kick scanner */ void mux_scan_next (void) { int32 i; if (mux_slck) /* locked? */ return; for (i = 0; i < MUX_SCANMAX; i++) { /* scan flags */ mux_scan = (mux_scan + 1) & MUX_SCANMASK; /* next flag */ if (mux_flags[mux_scan]) { /* flag set? */ mux_slck = 1; /* lock scanner */ MUX_SETINT (mux_scan & MUX_FLAGMASK); /* request int */ return; } } return; } /* Reset routine */ t_stat mux_reset (DEVICE *dptr) { int32 i, t; if (mux_dev.flags & DEV_DIS) /* master disabled? */ muxl_dev.flags = muxl_dev.flags | DEV_DIS; /* disable lines */ else muxl_dev.flags = muxl_dev.flags & ~DEV_DIS; if (mux_unit.flags & UNIT_ATT) { /* master att? */ if (!sim_is_active (&mux_unit)) { t = sim_rtcn_init (mux_unit.wait, TMR_MUX); sim_activate (&mux_unit, t); /* activate */ } } else sim_cancel (&mux_unit); /* else stop */ for (i = 0; i < MUX_LINES; i++) mux_reset_ln (i); for (i = 0; i < MUX_FLAGS; i++) /* clear all ints */ MUX_CLRINT (i); return SCPE_OK; } /* Attach master unit */ t_stat mux_attach (UNIT *uptr, char *cptr) { t_stat r; int32 t; r = tmxr_attach (&mux_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; t = sim_rtcn_init (mux_unit.wait, TMR_MUX); sim_activate (uptr, t); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat mux_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&mux_desc, uptr); /* detach */ for (i = 0; i < MUX_LINES; i++) /* disable rcv */ mux_ldsc[i].rcve = 0; sim_cancel (uptr); /* stop poll */ return r; } /* Change number of lines */ t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, MUX_LINES, &r); if ((r != SCPE_OK) || (newln == MUX_NUMLIN)) return r; if (newln == 0) return SCPE_ARG; if (newln < MUX_NUMLIN) { for (i = newln, t = 0; i < MUX_NUMLIN; i++) t = t | mux_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < MUX_NUMLIN; i++) { if (mux_ldsc[i].conn) { tmxr_linemsg (&mux_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&mux_ldsc[i]); /* reset line */ } muxl_unit[i].flags = muxl_unit[i].flags | UNIT_DIS; mux_reset_ln (i); } } else { for (i = MUX_NUMLIN; i < newln; i++) { muxl_unit[i].flags = muxl_unit[i].flags & ~UNIT_DIS; mux_reset_ln (i); } } MUX_NUMLIN = newln; return SCPE_OK; } /* Reset an individual line */ void mux_reset_ln (int32 ln) { int32 flg = ln * MUX_FLAGS; if (mux_ldsc[ln].conn) mux_sta[ln] = MUX_SCRO | MUX_SDSR; else mux_sta[ln] = 0; sim_cancel (&muxl_unit[ln]); mux_flags[flg + MUX_FRCV] = 0; mux_flags[flg + MUX_FXMT] = 0; mux_flags[flg + MUX_FCRN] = 0; mux_flags[flg + MUX_FCRF] = 0; return; } simh-3.8.1/SDS/sds_drm.c0000644000175000017500000003275711110636066013107 0ustar vlmvlm/* sds_drm.c: SDS 940 Project Genie drum simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. drm drum The drum is buffered in memory. Note: the Project Genie documentation and the actual monitor sources disagree on the I/O instruction definitions for the drum. The simulator follows the monitor sources, as follows: DCC OP 00230404B RESET DRUM CHANNEL DSC OP 00230204B START DRUM CHANNEL (NO CHAIN) DRA OP 00230504B READ DRUM TIMING COUNTER INTO 21B DSR OP 04030204B SKIP IF DRUM NOT BUSY DSE OP 04037404B SKIP IF NO DRUM ERROR */ #include "sds_defs.h" #include /* Constants */ #define DRM_N_WD 11 /* word addr width */ #define DRM_V_WD 0 /* position */ #define DRM_M_WD ((1 << DRM_N_WD) - 1) /* word mask */ #define DRM_NUMWD (1 << DRM_N_WD) /* words/sector */ #define DRM_NUMGP 236 /* gap/sector */ #define DRM_PHYWD (DRM_NUMWD + DRM_NUMGP) /* phys wds/sector */ #define DRM_N_SC 3 /* sect addr width */ #define DRM_V_SC (DRM_N_WD) /* position */ #define DRM_M_SC ((1 << DRM_N_SC) - 1) /* sector mask */ #define DRM_NUMSC (1 << DRM_N_SC) /* sectors/track */ #define DRM_N_TR 7 /* track addr width */ #define DRM_V_TR (DRM_N_WD+DRM_N_SC) /* position */ #define DRM_M_TR ((1 << DRM_N_TR) - 1) /* track mask */ #define DRM_NUMTR 84 /* tracks/drum */ #define DRM_N_ADDR (DRM_N_WD+DRM_N_SC+DRM_N_TR) /* drum addr width */ #define DRM_SWMASK ((1 << (DRM_N_WD+DRM_N_SC)) - 1)/* sector+word mask */ #define DRM_DAMASK ((1 << DRM_N_ADDR) - 1) /* drum addr mask */ #define DRM_SIZE (DRM_NUMTR*DRM_NUMSC*DRM_NUMWD) /* words/disk */ #define DRM_WCMASK 037777 /* wc mask */ #define DRM_GETSC(x) (((x) >> DRM_V_SC) & DRM_M_SC) #define DRM_PC 020 #define DRM_AD 021 #define DRM_ADAT (1 << (DRM_N_WD + DRM_N_SC)) /* data flag */ #define DRM_SFET 0 /* fetch state */ #define DRM_SFCA 1 /* fetch CA */ #define DRM_SFDA 2 /* fetch DA */ #define DRM_SXFR 3 /* xfer */ #define DRM_V_OP 21 /* drum op */ #define DRM_M_OP 07 #define DRM_V_RW 20 #define DRM_GETOP(x) (((x) >> DRM_V_OP) & DRM_M_OP) #define DRM_GETRW(x) (((x) >> DRM_V_RW) & 1) #define DRM_OXF 0 /* xfer */ #define DRM_OCX 1 /* cond xfer */ #define DRM_OBR 2 /* branch */ #define DRM_ORS 3 /* reset error */ #define DRM_END 4 /* end prog */ #define DRM_EIE 5 /* end int if err */ #define DRM_EIU 7 /* end int uncond */ #define GET_TWORD(x) ((int32) fmod (sim_gtime() / ((double) (x)), \ ((double) (DRM_NUMSC * DRM_PHYWD)))) extern uint32 M[]; /* memory */ extern uint32 alert, int_req; extern int32 stop_invins, stop_invdev, stop_inviop; uint32 drm_da = 0; /* disk address */ uint32 drm_ca = 0; /* core address */ uint32 drm_wc = 0; /* word count */ int32 drm_par = 0; /* cumulative par */ int32 drm_err = 0; /* error */ int32 drm_rw = 0; /* read/write */ int32 drm_sta = 0; /* drum state */ int32 drm_ftime = 3; /* time to fetch */ int32 drm_xtime = 1; /* time to xfr */ int32 drm_stopioe = 1; /* stop on error */ DEVICE drm_dev; t_stat drm (uint32 fnc, uint32 inst, uint32 *dat); t_stat drm_svc (UNIT *uptr); t_stat drm_reset (DEVICE *dptr); /* DRM data structures drm_dev device descriptor drm_unit unit descriptor drm_reg register list */ DIB drm_dib = { -1, DEV3_GDRM, 0, NULL, &drm }; UNIT drm_unit = { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DRM_SIZE) }; REG drm_reg[] = { { ORDATA (DA, drm_da, DRM_N_ADDR) }, { ORDATA (CA, drm_ca, 16) }, { ORDATA (WC, drm_wc, 14) }, { ORDATA (PAR, drm_par, 12) }, { FLDATA (RW, drm_rw, 0) }, { FLDATA (ERR, drm_err, 0) }, { ORDATA (STA, drm_sta, 2) }, { DRDATA (FTIME, drm_ftime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, drm_xtime, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, drm_stopioe, 0) }, { NULL } }; DEVICE drm_dev = { "DRM", &drm_unit, drm_reg, NULL, 1, 8, DRM_N_ADDR, 1, 8, 24, NULL, NULL, &drm_reset, NULL, NULL, NULL, &drm_dib, DEV_DISABLE | DEV_DIS }; /* Drum routine - EOM/SKS 3xx04 */ t_stat drm (uint32 fnc, uint32 inst, uint32 *dat) { int32 t, op = inst & 07700; switch (fnc) { case IO_CONN: /* connect */ if (op == 00400) /* EOM 404 = reset */ return drm_reset (&drm_dev); if (op == 00500) { /* EOM 504 = read DA */ if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */ t = GET_TWORD (drm_xtime); /* get position */ if (t < DRM_NUMGP) /* in gap? */ M[DRM_AD] = DRM_NUMWD - t; else M[DRM_AD] = (t - DRM_NUMGP) | DRM_ADAT;/* in data */ } else if (op == 00200) { /* EOM 204 = start */ if (sim_is_active (&drm_unit)) /* must be idle */ return SCPE_OK; drm_sta = DRM_SFET; /* state = fetch */ sim_activate (&drm_unit, drm_ftime); /* activate */ } else CRETINS; break; case IO_SKS: /* SKS */ if (((op == 07400) && !drm_err) || /* 37404: no err */ ((op == 00200) && !sim_is_active (&drm_unit))) /* 30204: idle */ *dat = 1; break; default: return SCPE_IERR; } return SCPE_OK; } /* Unit service */ t_stat drm_svc (UNIT *uptr) { int32 t, rda; uint32 dpc, dwd; uint32 *fbuf = uptr->filebuf; if (drm_sta != DRM_SXFR) { /* fetch drum prog? */ dpc = M[DRM_PC]; /* get drum PC */ dwd = M[dpc & PAMASK]; /* get drum inst */ M[DRM_PC] = (dpc + 1) & PAMASK; /* update drum PC */ if (drm_sta == DRM_SFCA) { /* fetch core addr? */ drm_rw = DRM_GETRW (dwd); /* set op */ drm_ca = dwd & PAMASK; /* set core addr */ drm_sta = DRM_SFDA; /* next is disk addr */ } else if (drm_sta == DRM_SFDA) { /* fetch disk addr? */ drm_da = dwd & DRM_DAMASK; /* set disk addr */ drm_sta = DRM_SXFR; /* next is xfer */ drm_par = 0; /* init parity */ rda = (drm_da & DRM_SWMASK) + (DRM_GETSC (drm_da) * DRM_NUMGP); t = rda - GET_TWORD (drm_xtime); /* difference */ if (t <= 0) /* add trk lnt */ t = t + (DRM_NUMSC * DRM_PHYWD); sim_activate (&drm_unit, t * drm_xtime); /* activate */ } else { switch (DRM_GETOP (dwd)) { case DRM_OCX: /* cond xfr */ if (drm_err) { /* error? */ int_req = int_req | INT_DRM; /* req int */ return SCPE_OK; /* done */ } case DRM_OXF: /* transfer */ drm_wc = dwd & DRM_WCMASK; /* save wc */ drm_sta = DRM_SFCA; /* next state */ break; case DRM_OBR: /* branch */ M[DRM_PC] = dwd & PAMASK; /* new drum PC */ break; case DRM_END: /* end */ return SCPE_OK; case DRM_EIE: /* end, int if err */ if (!drm_err) return SCPE_OK; case DRM_EIU: /* end, int uncond */ int_req = int_req | INT_DRM; return SCPE_OK; } /* end switch */ } /* end else sta */ sim_activate (uptr, drm_ftime); /* fetch next word */ } /* end if !xfr */ else { /* transfer word */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ drm_err = 1; /* error */ CRETIOE (drm_stopioe, SCPE_UNATT); } if (drm_rw) { /* write? */ dwd = M[drm_ca]; /* get mem word */ fbuf[drm_da] = dwd; /* write to drum */ if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; } else { /* read */ dwd = fbuf[drm_da]; /* get drum word */ M[drm_ca] = dwd; /* write to mem */ } drm_da = drm_da + 1; /* inc drum addr */ if (drm_da >= DRM_SIZE) /* wrap */ drm_da = 0; drm_ca = (drm_ca + 1) & PAMASK; /* inc core addr */ drm_wc = (drm_wc - 1) & DRM_WCMASK; /* dec word cnt */ drm_par = drm_par ^ (dwd >> 12); /* parity */ drm_par = ((drm_par << 1) | (drm_par >> 11)) & 07777; drm_par = drm_par ^ (dwd & 07777); if (drm_wc) { /* more to do */ if (drm_da & DRM_M_WD) sim_activate (uptr, drm_xtime); else sim_activate (uptr, drm_xtime * DRM_NUMGP); } else { /* end xfr */ #if defined (DRM_PAR) if ((drm_da & DRM_M_WD) && drm_rw) { /* wr end mid sector? */ M[drm_da] = drm_par << 12; /* clobber data */ if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; } #endif drm_sta = DRM_SFET; /* back to fetch */ sim_activate (uptr, drm_ftime); /* schedule */ } /* end else end xfr */ } /* end else xfr */ return SCPE_OK; } /* Reset routine */ t_stat drm_reset (DEVICE *dptr) { drm_da = 0; /* clear state */ drm_ca = 0; drm_wc = 0; drm_par = 0; drm_sta = 0; drm_err = 0; drm_rw = 0; int_req = int_req & ~INT_DRM; /* clear intr */ sim_cancel (&drm_unit); /* deactivate */ return SCPE_OK; } simh-3.8.1/SDS/sds_stddev.c0000644000175000017500000005226411110637426013612 0ustar vlmvlm/* sds_stddev.c: SDS 940 standard devices Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr paper tape reader ptp paper tape punch tti keyboard tto teleprinter 29-Dec-03 RMS Added console backpressure support 25-Apr-03 RMS Revised for extended file support */ #include "sds_defs.h" #define TT_CR 052 /* typewriter */ #define TT_TB 072 #define TT_BS 032 extern uint32 xfr_req; extern int32 stop_invins, stop_invdev, stop_inviop; int32 ptr_sor = 0; /* start of rec */ int32 ptr_stopioe = 1; /* stop on err */ int32 ptp_ldr = 0; /* no leader */ int32 ptp_stopioe = 1; DSPT std_tplt[] = { { 1, 0 }, { 0, 0 } }; /* template */ DEVICE ptr_dev, ptp_dev; t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat); t_stat ptr_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); void ptr_set_err (void); t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); t_stat ptp_out (int32 dat); void ptp_set_err (void); t_stat tti (uint32 fnc, uint32 inst, uint32 *dat); t_stat tti_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto (uint32 fnc, uint32 inst, uint32 *dat); t_stat tto_svc (UNIT *uptr); t_stat tto_reset (DEVICE *dptr); extern const char ascii_to_sds[128]; extern const char sds_to_ascii[64]; extern const char odd_par[64]; /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit ptr_reg PTR register list */ DIB ptr_dib = { CHAN_W, DEV_PTR, XFR_PTR, std_tplt, &ptr }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 7) }, { FLDATA (XFR, xfr_req, XFR_V_PTR) }, { FLDATA (SOR, ptr_sor, 0) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB ptr_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, NULL, NULL, &ptr_dib, DEV_DISABLE }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit ptp_reg PTP register list */ DIB ptp_dib = { CHAN_W, DEV_PTP, XFR_PTP, std_tplt, &ptp }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 7) }, { FLDATA (XFR, xfr_req, XFR_V_PTP) }, { FLDATA (LDR, ptp_ldr, 0) }, { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptp_unit.wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, ptp_stopioe, 0) }, { NULL } }; MTAB ptp_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, &ptp_dib, DEV_DISABLE }; /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit tti_reg TTI register list */ DIB tti_dib = { CHAN_W, DEV_TTI, XFR_TTI, std_tplt, &tti }; UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 6) }, { FLDATA (XFR, xfr_req, XFR_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[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, &tti_dib }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit tto_reg TTO register list */ DIB tto_dib = { CHAN_W, DEV_TTO, XFR_TTO, std_tplt, &tto }; UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 6) }, { FLDATA (XFR, xfr_req, XFR_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, &tto_dib }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* Paper tape reader conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result The paper tape reader is a streaming input device. Once started, it continues to read until disconnected. Leader before the current record is ignored; leader after the current record sets channel EndOfRecord. */ t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat) { int32 new_ch; switch (fnc) { /* case function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != ptr_dib.chan) /* inv conn? err */ return SCPE_IERR; ptr_sor = 1; /* start of rec */ xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ break; case IO_DISC: /* disconnect */ ptr_sor = 0; /* clear state */ xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ sim_cancel (&ptr_unit); /* deactivate unit */ break; case IO_READ: /* read */ xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ *dat = ptr_unit.buf & 077; /* get buf data */ if (ptr_unit.buf != odd_par[*dat]) /* good parity? */ chan_set_flag (ptr_dib.chan, CHF_ERR); /* no, error */ break; case IO_WREOR: /* write eor */ break; case IO_EOM1: /* EOM mode 1*/ case IO_WRITE: /* write */ CRETINS; /* error */ } return SCPE_OK; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ ptr_set_err (); /* no, err, disc */ CRETIOE (ptr_stopioe, SCPE_UNATT); } if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ ptr_set_err (); /* yes, err, disc */ if (feof (ptr_unit.fileref)) { /* end of file? */ if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); /* I/O error */ clearerr (ptr_unit.fileref); return SCPE_IOERR; } ptr_unit.pos = ptr_unit.pos + 1; /* inc position */ if (temp) { /* leader/gap? */ ptr_unit.buf = temp & 0177; /* no, save char */ xfr_req = xfr_req | XFR_PTR; /* set xfr flag */ ptr_sor = 0; /* in record */ } else if (!ptr_sor) /* end record? */ chan_set_flag (ptr_dib.chan, CHF_EOR); /* ignore leader */ sim_activate (&ptr_unit, ptr_unit.wait); /* get next char */ return SCPE_OK; } /* Fatal error */ void ptr_set_err (void) { chan_set_flag (ptr_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */ chan_disc (ptr_dib.chan); /* disconnect */ xfr_req = xfr_req & ~XFR_PTR; /* clear xfr */ sim_cancel (&ptr_unit); /* stop */ return; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { chan_disc (ptr_dib.chan); /* disconnect */ ptr_sor = 0; /* clear state */ ptr_unit.buf = 0; xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Boot routine - simulate FILL console command */ t_stat ptr_boot (int32 unitno, DEVICE *dptr) { extern uint32 P, M[]; M[0] = 077777771; /* -7B */ M[1] = 007100000; /* LDX 0 */ M[2] = 000203604; /* EOM 3604B */ M[3] = 003200002; /* WIM 2 */ M[4] = 000100002; /* BRU 2 */ P = 1; /* start at 1 */ return SCPE_OK; } /* Paper tape punch conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result The paper tape punch is an asynchronous streaming output device. That is, it can never cause a channel rate error; if no data is available, it waits. */ t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat) { int32 new_ch; switch (fnc) { /* case function */ case IO_CONN: new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != ptp_dib.chan) /* inv conn? err */ return SCPE_IERR; ptp_ldr = (inst & CHC_NLDR)? 0: 1; /* leader? */ xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate */ break; case IO_DISC: /* disconnect */ ptp_ldr = 0; /* clear state */ xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ sim_cancel (&ptp_unit); /* deactivate unit */ break; case IO_WRITE: /* write */ xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate */ ptp_unit.buf = odd_par[(*dat) & 077]; /* save data */ return ptp_out (ptp_unit.buf); /* punch w/ par */ case IO_WREOR: /* write eor */ break; case IO_EOM1: /* EOM mode 1*/ case IO_READ: /* read */ CRETINS; /* error */ } return SCPE_OK; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { int32 i; t_stat r = SCPE_OK; if (ptp_ldr) { /* need leader? */ for (i = 0; i < 12; i++) { /* punch leader */ if (r = ptp_out (0)) break; } } ptp_ldr = 0; /* clear flag */ chan_set_ordy (ptp_dib.chan); /* ptp ready */ return r; } /* Punch I/O */ t_stat ptp_out (int32 dat) { if ((ptp_unit.flags & UNIT_ATT) == 0) { /* attached? */ ptp_set_err (); /* no, disc, err */ CRETIOE (ptp_stopioe, SCPE_UNATT); } if (putc (dat, ptp_unit.fileref) == EOF) { /* I/O error? */ ptp_set_err (); /* yes, disc, err */ perror ("PTP I/O error"); /* print msg */ clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ptp_unit.pos + 1; /* inc position */ return SCPE_OK; } /* Fatal error */ void ptp_set_err (void) { chan_set_flag (ptp_dib.chan, CHF_ERR); /* error */ chan_disc (ptp_dib.chan); /* disconnect */ xfr_req = xfr_req & ~XFR_PTP; /* clear xfr */ sim_cancel (&ptp_unit); /* stop */ return; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { chan_disc (ptp_dib.chan); /* disconnect */ ptp_ldr = 0; /* clear state */ ptp_unit.buf = 0; xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Typewriter input conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result The typewriter input is an asynchronous input device. That is, it can never cause a channel rate error; if no data is available, it waits. */ t_stat tti (uint32 fnc, uint32 inst, uint32 *dat) { int32 new_ch; switch (fnc) { /* case function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != tti_dib.chan) /* inv conn? err */ return SCPE_IERR; xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ break; case IO_DISC: /* disconnect */ xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ break; case IO_READ: /* read */ xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ *dat = tti_unit.buf; /* get buf data */ break; case IO_WREOR: /* write eor */ break; case IO_EOM1: /* EOM mode 1*/ case IO_WRITE: /* write */ CRETINS; /* error */ } return SCPE_OK; } /* Unit service */ t_stat tti_svc (UNIT *uptr) { int32 temp; sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return temp; if (temp & SCPE_BREAK) /* ignore break */ return SCPE_OK; temp = temp & 0177; tti_unit.pos = tti_unit.pos + 1; if (ascii_to_sds[temp] >= 0) { tti_unit.buf = ascii_to_sds[temp]; /* internal rep */ sim_putchar (temp); /* echo */ if (temp == '\r') /* lf after cr */ sim_putchar ('\n'); xfr_req = xfr_req | XFR_TTI; /* set xfr flag */ } else sim_putchar (007); /* ding! */ return SCPE_OK; } t_stat tti_reset (DEVICE *dptr) { chan_disc (tti_dib.chan); /* disconnect */ tti_unit.buf = 0; /* clear state */ xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ sim_activate (&tti_unit, tti_unit.wait); /* start poll */ return SCPE_OK; } /* Typewriter output conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result The typewriter output is an asynchronous streaming output device. That is, it can never cause a channel rate error; if no data is available, it waits. */ t_stat tto (uint32 fnc, uint32 inst, uint32 *dat) { int32 new_ch; switch (fnc) { /* case function */ case IO_CONN: new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != tto_dib.chan) /* inv conn? err */ return SCPE_IERR; xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ sim_activate (&tto_unit, tto_unit.wait); /* activate */ break; case IO_DISC: /* disconnect */ xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ sim_cancel (&tto_unit); /* deactivate unit */ break; case IO_WRITE: /* write */ xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ tto_unit.buf = (*dat) & 077; /* save data */ sim_activate (&tto_unit, tto_unit.wait); /* activate */ break; case IO_WREOR: /* write eor */ break; case IO_EOM1: /* EOM mode 1*/ case IO_READ: /* read */ CRETINS; /* error */ } return SCPE_OK; } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 asc; t_stat r; if (uptr->buf == TT_CR) /* control chars? */ asc = '\r'; else if (uptr->buf == TT_BS) asc = '\b'; else if (uptr->buf == TT_TB) asc = '\t'; else asc = sds_to_ascii[uptr->buf]; /* translate */ if ((r = sim_putchar_s (asc)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } uptr->pos = uptr->pos + 1; /* inc position */ chan_set_ordy (tto_dib.chan); /* tto rdy */ if (asc == '\r') { /* CR? */ sim_putchar ('\n'); /* add lf */ uptr->pos = uptr->pos + 1; /* inc position */ } return SCPE_OK; } /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { chan_disc (tto_dib.chan); /* disconnect */ tto_unit.buf = 0; /* clear state */ xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/SDS/sds_mt.c0000644000175000017500000005073611110641112012726 0ustar vlmvlm/* sds_mt.c: SDS 940 magnetic tape simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt 7 track magnetic tape 16-Feb-06 RMS Added tape capacity checking 07-Dec-04 RMS Added read-only file support 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. */ #include "sds_defs.h" #include "sim_tape.h" #define MT_MAXFR (32768 * 4) #define MT_NUMDR 8 /* number drives */ #define MT_UNIT 07 #define botf u3 /* bot tape flag */ #define eotf u4 /* eot tape flag */ extern uint32 xfr_req; extern int32 stop_invins, stop_invdev, stop_inviop; int32 mt_inst = 0; /* saved instr */ int32 mt_eof = 0; /* end of file */ int32 mt_gap = 0; /* in gap */ int32 mt_skip = 0; /* skip rec */ int32 mt_bptr = 0; /* buf ptr */ int32 mt_blnt = 0; /* buf length */ int32 mt_ctime = 10; /* char time */ int32 mt_gtime = 1000; /* gap time */ int32 mt_stopioe = 1; /* stop on err */ uint8 mtxb[MT_MAXFR]; /* record buffer */ DSPT mt_tplt[] = { /* template */ { MT_NUMDR, 0 }, { MT_NUMDR, DEV_MTS }, { MT_NUMDR, DEV_OUT }, { MT_NUMDR, DEV_MTS+DEV_OUT }, { 0, 0 } }; DEVICE mt_dev; t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_boot (int32 unitno, DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_detach (UNIT *uptr); t_stat mt_readrec (UNIT *uptr); t_mtrlnt mt_readbc (UNIT *uptr); void mt_readend (UNIT *uptr); t_stat mt_wrend (uint32 dev); void mt_set_err (UNIT *uptr); t_stat mt (uint32 fnc, uint32 inst, uint32 *dat); static const char sds_to_bcd[64] = { 012, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 060, 061, 062, 063, 064, 065, 066, 067, 070, 071, 072, 073, 074, 075, 076, 077, 040, 041, 042, 043, 044, 045, 046, 047, 050, 051, 052, 053, 054, 055, 056, 057, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, 033, 034, 035, 036, 037 }; static const char bcd_to_sds[64] = { 000, 001, 002, 003, 004, 005, 006, 007, 010, 011, 000, 013, 014, 015, 016, 017, 060, 061, 062, 063, 064, 065, 066, 067, 070, 071, 072, 073, 074, 075, 076, 077, 040, 041, 042, 043, 044, 045, 046, 047, 050, 051, 052, 053, 054, 055, 056, 057, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, 033, 034, 035, 036, 037 }; /* MT data structures mt_dev MT device descriptor mt_unit MT unit descriptor mt_reg MT register list */ DIB mt_dib = { CHAN_W, DEV_MT, XFR_MT0, mt_tplt, &mt }; UNIT mt_unit[] = { { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } }; REG mt_reg[] = { { BRDATA (BUF, mtxb, 8, 8, MT_MAXFR) }, { DRDATA (BPTR, mt_bptr, 18), PV_LEFT }, { DRDATA (BLNT, mt_blnt, 18), PV_LEFT }, { FLDATA (XFR, xfr_req, XFR_V_MT0) }, { ORDATA (INST, mt_inst, 24) }, { FLDATA (EOF, mt_eof, 0) }, { FLDATA (GAP, mt_gap, 0) }, { FLDATA (SKIP, mt_skip, 0) }, { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT }, { DRDATA (GTIME, mt_gtime, 24), REG_NZ + PV_LEFT }, { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (BOT, mt_unit[0].botf, 10, 1, 0, MT_NUMDR, REG_RO) }, { URDATA (EOT, mt_unit[0].eotf, 10, 1, 0, MT_NUMDR, REG_RO) }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan, &show_chan, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, &mt_boot, &mt_attach, NULL, &mt_dib, DEV_DISABLE }; /* Mag tape routine conn - inst = EOM0, dat = NULL eom1 - inst = EOM1, dat = NULL sks - inst = SKS, dat = ptr to result disc - inst = device number, dat = NULL wreor - inst = device number, dat = NULL read - inst = device number, dat = ptr to data write - inst = device number, dat = ptr to result */ t_stat mt (uint32 fnc, uint32 inst, uint32 *dat) { int32 u = inst & MT_UNIT; /* get unit */ UNIT *uptr = mt_dev.units + u; /* get unit ptr */ int32 t, new_ch; uint8 chr; t_stat r; switch (fnc) { /* case function */ case IO_CONN: /* connect */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != mt_dib.chan) /* wrong chan? */ return SCPE_IERR; if (mt_gap) { /* in gap? */ mt_gap = 0; /* clr gap flg */ sim_cancel (uptr); /* cancel timer */ } else if (sim_is_active (uptr)) /* busy? */ CRETIOP; uptr->eotf = 0; /* clr eot flag */ mt_eof = 0; /* clr eof flag */ mt_skip = 0; /* clr skp flag */ mt_bptr = mt_blnt = 0; /* init buffer */ if ((inst & DEV_MTS)? (CHC_GETCPW (inst) < 2): /* scn & cpw<3? */ (inst & CHC_REV)) /* rw & rev? */ return STOP_INVIOP; mt_inst = inst; /* save inst */ if ((inst & DEV_MTS) && !(inst && DEV_OUT)) /* scanning? */ chan_set_flag (mt_dib.chan, CHF_SCAN); /* set chan flg */ xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ sim_activate (uptr, mt_gtime); /* start timer */ break; case IO_EOM1: /* EOM mode 1 */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != mt_dib.chan) /* wrong chan? */ CRETIOP; t = inst & 07670; /* get command */ if ((t == 04010) && !sim_is_active (uptr)) { /* rewind? */ sim_tape_rewind (uptr); /* rewind unit */ uptr->eotf = 0; /* clr eot */ uptr->botf = 1; /* set bot */ } else if ((t == 03610) && sim_is_active (uptr) &&/* skip rec? */ ((mt_inst & DEV_OUT) == 0)) mt_skip = 1; /* set flag */ else CRETINS; break; case IO_DISC: /* disconnect */ sim_cancel (uptr); /* no more xfr's */ if (inst & DEV_OUT) { /* write? */ if (r = mt_wrend (inst)) /* end record */ return r; } break; case IO_WREOR: /* write eor */ chan_set_flag (mt_dib.chan, CHF_EOR); /* set eor flg */ if (r = mt_wrend (inst)) /* end record */ return r; mt_gap = 1; /* in gap */ sim_activate (uptr, mt_gtime); /* start timer */ break; case IO_SKS: /* SKS */ new_ch = I_GETSKCH (inst); /* get chan # */ if (new_ch != mt_dib.chan) /* wrong chan? */ return SCPE_IERR; if ((inst & (DEV_OUT | DEV_MTS)) == 0) { /* not sks 1n? */ t = I_GETSKCND (inst); /* get skip cond */ switch (t) { /* case sks cond */ case 001: /* sks 1021n */ *dat = 1; /* not magpak */ break; case 002: /* sks 1041n */ if (!(uptr->flags & UNIT_ATT) || /* not ready */ sim_is_active (uptr)) *dat = 1; break; case 004: /* sks 1101n */ if (!uptr->eotf) *dat = 1; /* not EOT */ break; case 010: /* sks 1201n */ if (!uptr->botf) *dat = 1; /* not BOT */ break; case 013: /* sks 12610 */ if (!mt_gap) *dat = 1; /* not in gap */ break; case 017: /* sks 13610 */ if (!mt_eof) *dat = 1; /* not EOF */ break; case 020: /* sks 1401n */ if (!sim_tape_wrp (uptr)) *dat = 1; /* not wrp */ break; case 031: /* sks 1621n */ case 033: /* sks 1661n */ *dat = 1; /* not 556bpi */ case 035: /* sks 1721n */ break; /* not 800bpi */ } } /* end if */ break; case IO_READ: /* read */ xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ if (mt_blnt == 0) { /* first read? */ r = mt_readrec (uptr); /* get data */ if ((r != SCPE_OK) || (mt_blnt == 0)) /* err, inv reclnt? */ return r; } uptr->botf = 0; /* off BOT */ if (mt_inst & CHC_REV) /* get next rev */ chr = mtxb[--mt_bptr] & 077; else chr = mtxb[mt_bptr++] & 077; /* get next fwd */ if (!(mt_inst & CHC_BIN)) /* bcd? */ chr = bcd_to_sds[chr]; *dat = chr & 077; /* give to chan */ if ((mt_inst & CHC_REV)? (mt_bptr <= 0): /* rev or fwd, */ (mt_bptr >= mt_blnt)) /* recd done? */ mt_readend (uptr); break; case IO_WRITE: /* write */ uptr->botf = 0; /* off BOT */ chr = (*dat) & 077; xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ if (!(mt_inst & CHC_BIN)) /* bcd? */ chr = sds_to_bcd[chr]; if (mt_bptr < MT_MAXFR) mtxb[mt_bptr++] = chr; /* insert in buf */ break; default: CRETINS; } return SCPE_OK; } /* Unit service */ t_stat mt_svc (UNIT *uptr) { if (mt_gap) { /* gap timeout */ mt_gap = 0; /* clr gap flg */ chan_disc (mt_dib.chan); /* disc chan */ } else if (mt_skip) /* skip record */ mt_readend (uptr); else { /* normal xfr */ xfr_req = xfr_req | XFR_MT0; /* set xfr req */ sim_activate (uptr, mt_ctime); /* reactivate */ } return SCPE_OK; } /* Read start (get new record) */ t_stat mt_readrec (UNIT *uptr) { t_mtrlnt tbc; t_stat st; if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ mt_set_err (uptr); /* no, err, disc */ return SCPE_UNATT; } if (mt_inst & CHC_REV) /* reverse? */ st = sim_tape_rdrecr (uptr, mtxb, &tbc, MT_MAXFR); /* read rec rev */ else { /* no, fwd */ t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */ st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); if (!passed_eot && sim_tape_eot (uptr)) /* just passed eot? */ uptr->eotf = 1; } if (st == MTSE_TMK) { /* tape mark? */ mt_eof = 1; /* set eof flag */ mtxb[0] = mtxb[1] = 017; /* EOR char */ mt_blnt = 2; /* store 2 */ return SCPE_OK; } if (st != MTSE_OK) { /* other error? */ mt_set_err (uptr); /* err, disc */ if (st == MTSE_IOERR) /* IO error? */ return SCPE_IOERR; if (st == MTSE_INVRL) /* inv rec lnt? */ return SCPE_MTRLNT; if (st == MTSE_EOM) /* eom? set eot */ uptr->eotf = 1; return SCPE_OK; } mt_blnt = tbc; /* set buf lnt */ return SCPE_OK; } /* Read done (eof, end of record) */ void mt_readend (UNIT *uptr) { sim_cancel (uptr); /* stop timer */ mt_skip = 0; /* clr skp flg */ chan_set_flag (mt_dib.chan, CHF_EOR); /* end record */ if (mt_eof) /* EOF? */ chan_disc (mt_dib.chan); else { mt_gap = 1; /* no, in gap */ sim_activate (uptr, mt_gtime); /* start timer */ } return; } /* Write complete (end of record or disconnect) */ t_stat mt_wrend (uint32 dev) { UNIT *uptr = mt_dev.units + (dev & MT_UNIT); t_mtrlnt tbc; t_stat st; sim_cancel (uptr); /* no more xfr's */ if (mt_bptr == 0) /* buf empty? */ return SCPE_OK; if (!(uptr->flags & UNIT_ATT)) { /* attached? */ mt_set_err (uptr); /* no, err, disc */ return SCPE_UNATT; } if (sim_tape_wrp (uptr)) { /* write lock? */ mt_set_err (uptr); /* yes, err, disc */ return SCPE_OK; } if (dev & DEV_MTS) { /* erase? */ if (mt_inst & CHC_REV) /* reverse? */ sim_tape_sprecr (uptr, &tbc); /* backspace */ st = sim_tape_wreom (uptr); /* write eom */ } else { t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */ if ((mt_bptr == 1) && (mtxb[0] == 017) && /* wr eof? */ ((mt_inst & 01670) == 00050)) st = sim_tape_wrtmk (uptr); /* write tape mark */ else st = sim_tape_wrrecf (uptr, mtxb, mt_bptr); /* write record */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ uptr->eotf = 1; } mt_bptr = 0; if (st != MTSE_OK) /* error? */ mt_set_err (uptr); if (st == MTSE_IOERR) return SCPE_IOERR; return SCPE_OK; } /* Fatal error */ void mt_set_err (UNIT *uptr) { chan_set_flag (mt_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */ chan_disc (mt_dib.chan); /* disconnect */ xfr_req = xfr_req & ~XFR_MT0; /* clear xfr */ sim_cancel (uptr); /* stop */ mt_bptr = 0; /* buf empty */ return; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { int32 i; chan_disc (mt_dib.chan); /* disconnect */ mt_eof = 0; /* clear state */ mt_gap = 0; mt_skip = 0; mt_inst = 0; mt_bptr = mt_blnt = 0; xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ for (i = 0; i < MT_NUMDR; i++) { /* deactivate */ sim_cancel (&mt_unit[i]); sim_tape_reset (&mt_unit[i]); mt_unit[i].eotf = 0; } return SCPE_OK; } /* Attach and detach routines */ t_stat mt_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->botf = 1; uptr->eotf = 0; return SCPE_OK; } t_stat mt_detach (UNIT *uptr) { uptr->botf = uptr->eotf = 0; return sim_tape_detach (uptr); } /* Boot routine - simulate FILL console command */ t_stat mt_boot (int32 unitno, DEVICE *dptr) { extern uint32 P, M[]; if (unitno) /* only unit 0 */ return SCPE_ARG; M[0] = 077777771; /* -7B */ M[1] = 007100000; /* LDX 0 */ M[2] = 000203610; /* EOM 3610B */ M[3] = 003200002; /* WIM 2 */ M[4] = 000100002; /* BRU 2 */ P = 1; /* start at 1 */ return SCPE_OK; } simh-3.8.1/sim_tmxr.h0000644000175000017500000001347511111634324012662 0ustar vlmvlm/* sim_tmxr.h: terminal multiplexor definitions Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. 20-Nov-08 RMS Added three new standardized SHOW routines 27-May-08 JDB Added lnorder to TMXR structure, added tmxr_set_lnorder and tmxr_set_lnorder 14-May-08 JDB Added dptr to TMXR structure 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array Added tmxr_linemsg, logging (from Mark Pizzolato) 29-Dec-03 RMS Added output stall support, increased buffer size 22-Dec-02 RMS Added break support (from Mark Pizzolato) 20-Aug-02 RMS Added tmxr_open_master, tmxr_close_master, tmxr.port 30-Dec-01 RMS Renamed tmxr_fstatus, added tmxr_fstats 20-Oct-01 RMS Removed tmxr_getchar, formalized buffer guard, added tmxr_rqln, tmxr_tqln */ #ifndef _SIM_TMXR_H_ #define _SIM_TMXR_H_ 0 #define TMXR_V_VALID 15 #define TMXR_VALID (1 << TMXR_V_VALID) #define TMXR_MAXBUF 256 /* buffer size */ #define TMXR_GUARD 12 /* buffer guard */ struct tmln { SOCKET conn; /* line conn */ uint32 ipad; /* IP address */ uint32 cnms; /* conn time */ int32 tsta; /* Telnet state */ int32 rcve; /* rcv enable */ int32 xmte; /* xmt enable */ int32 dstb; /* disable Tlnt bin */ int32 rxbpr; /* rcv buf remove */ int32 rxbpi; /* rcv buf insert */ int32 rxcnt; /* rcv count */ int32 txbpr; /* xmt buf remove */ int32 txbpi; /* xmt buf insert */ int32 txcnt; /* xmt count */ FILE *txlog; /* xmt log file */ char *txlogname; /* xmt log file name */ char rxb[TMXR_MAXBUF]; /* rcv buffer */ char rbr[TMXR_MAXBUF]; /* rcv break */ char txb[TMXR_MAXBUF]; /* xmt buffer */ }; typedef struct tmln TMLN; struct tmxr { int32 lines; /* # lines */ int32 port; /* listening port */ SOCKET master; /* master socket */ TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ }; typedef struct tmxr TMXR; int32 tmxr_poll_conn (TMXR *mp); void tmxr_reset_ln (TMLN *lp); int32 tmxr_getc_ln (TMLN *lp); void tmxr_poll_rx (TMXR *mp); t_stat tmxr_putc_ln (TMLN *lp, int32 chr); void tmxr_poll_tx (TMXR *mp); t_stat tmxr_open_master (TMXR *mp, char *cptr); t_stat tmxr_close_master (TMXR *mp); t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr); t_stat tmxr_detach (TMXR *mp, UNIT *uptr); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void tmxr_msg (SOCKET sock, char *msg); void tmxr_linemsg (TMLN *lp, char *msg); void tmxr_fconns (FILE *st, TMLN *lp, int32 ln); void tmxr_fstats (FILE *st, TMLN *lp, int32 ln); t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); int32 tmxr_rqln (TMLN *lp); int32 tmxr_tqln (TMLN *lp); t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/sim_timer.h0000644000175000017500000000676311111655432013015 0ustar vlmvlm/* sim_timer.h: simulator timer library headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 28-Apr-07 RMS Added sim_rtc_init_all 17-Oct-06 RMS Added idle support 02-Jan-04 RMS Split out from SCP */ #ifndef _SIM_TIMER_H_ #define _SIM_TIMER_H_ 0 #define SIM_NTIMERS 8 /* # timers */ #define SIM_TMAX 500 /* max timer makeup */ #define SIM_IDLE_CAL 10 /* ms to calibrate */ #define SIM_IDLE_MAX 10 /* max granularity idle */ #define SIM_IDLE_STMIN 10 /* min sec for stability */ #define SIM_IDLE_STDFLT 20 /* dft sec for stability */ #define SIM_IDLE_STMAX 600 /* max sec for stability */ #define SIM_THROT_WINIT 1000 /* cycles to skip */ #define SIM_THROT_WST 10000 /* initial wait */ #define SIM_THROT_WMUL 4 /* multiplier */ #define SIM_THROT_WMIN 100 /* min wait */ #define SIM_THROT_MSMIN 10 /* min for measurement */ #define SIM_THROT_NONE 0 /* throttle parameters */ #define SIM_THROT_MCYC 1 #define SIM_THROT_KCYC 2 #define SIM_THROT_PCT 3 t_bool sim_timer_init (void); int32 sim_rtcn_init (int32 time, int32 tmr); void sim_rtcn_init_all (void); int32 sim_rtcn_calb (int32 ticksper, int32 tmr); int32 sim_rtc_init (int32 time); int32 sim_rtc_calb (int32 ticksper); t_bool sim_idle (uint32 tmr, t_bool sin_cyc); t_stat sim_set_throt (int32 arg, char *cptr); t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr); t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); void sim_throt_sched (void); void sim_throt_cancel (void); uint32 sim_os_msec (void); void sim_os_sleep (unsigned int sec); uint32 sim_os_ms_sleep (unsigned int msec); uint32 sim_os_ms_sleep_init (void); #endif simh-3.8.1/sim_sock.h0000644000175000017500000001014711111655430012621 0ustar vlmvlm/* sim_sock.h: OS-dependent socket routines header file Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Jun-08 RMS Addes sim_create_sock, for IBM 1130 14-Apr-05 RMS Added WSAEINPROGRESS (from Tim Riker) 20-Aug-04 HV Added missing definition for OS/2 (from Holger Veit) 22-Oct-03 MP Changed WIN32 winsock include to use winsock2.h to avoid a conflict if sim_sock.h and sim_ether.h get included by the same module. 20-Mar-03 RMS Added missing timerclear definition for VMS (from Robert Alan Byer) 15-Feb-03 RMS Added time.h for EMX (from Holger Veit) 17-Dec-02 RMS Added sim_connect_sock 08-Oct-02 RMS Revised for .NET compatibility 20-Aug-02 RMS Changed calling sequence for sim_accept_conn 30-Apr-02 RMS Changed VMS stropts include to ioctl 06-Feb-02 RMS Added VMS support from Robert Alan Byer 16-Sep-01 RMS Added Macintosh support from Peter Schorn */ #ifndef _SIM_SOCK_H_ #define _SIM_SOCK_H_ 0 #if defined (_WIN32) /* Windows */ #undef INT_PTR /* hack, hack */ #include #elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ #define WSAGetLastError() errno /* Windows macros */ #define SOCKET int32 #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEINPROGRESS EINPROGRESS #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #include /* for fcntl, getpid */ #include /* for sockets */ #include #include #include /* for sockaddr_in */ #include #include /* for EMX */ #endif #if defined (VMS) /* VMS unique */ #include /* for ioctl */ #if !defined (timerclear) #define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 #endif #endif #if defined(__EMX__) /* OS/2 unique */ #if !defined (timerclear) #define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 #endif #endif SOCKET sim_master_sock (int32 port); SOCKET sim_connect_sock (int32 ip, int32 port); SOCKET sim_create_sock (void); SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr); int32 sim_check_conn (SOCKET sock, t_bool rd); int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes); int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes); void sim_close_sock (SOCKET sock, t_bool master); SOCKET sim_setnonblock (SOCKET sock); #endif simh-3.8.1/I1401/0000755000175000017500000000000011112030454011326 5ustar vlmvlmsimh-3.8.1/I1401/i1401_iq.c0000644000175000017500000001515411107357050012737 0ustar vlmvlm/* i1401_iq.c: IBM 1407 inquiry terminal Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. inq 1407 inquiry terminal 20-Sep-05 RMS Revised for new code tables 22-Dec-02 RMS Added break support 07-Sep-01 RMS Moved function prototypes 14-Apr-99 RMS Changed t_addr to unsigned */ #include "i1401_defs.h" #include #define UNIT_V_PCH (UNIT_V_UF + 0) /* output conv */ #define UNIT_PCH (1 << UNIT_V_PCH) extern volatile int32 stop_cpu; extern uint8 M[]; extern int32 BS, iochk, ind[64]; extern UNIT cpu_unit; extern t_bool conv_old; int32 inq_char = 033; /* request inq */ t_stat inq_svc (UNIT *uptr); t_stat inq_reset (DEVICE *dptr); void puts_tty (char *cptr); /* INQ data structures inq_dev INQ device descriptor inq_unit INQ unit descriptor inq_reg INQ register list */ UNIT inq_unit = { UDATA (&inq_svc, 0, 0), KBD_POLL_WAIT }; REG inq_reg[] = { { ORDATA (INQC, inq_char, 7) }, { FLDATA (INR, ind[IN_INR], 0) }, { FLDATA (INC, ind[IN_INC], 0) }, { DRDATA (TIME, inq_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB inq_mod[] = { { UNIT_PCH, 0, "business set", "BUSINESS" }, { UNIT_PCH, UNIT_PCH, "Fortran set", "FORTRAN" }, { 0 } }; DEVICE inq_dev = { "INQ", &inq_unit, inq_reg, inq_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &inq_reset, NULL, NULL, NULL }; /* Terminal I/O Modifiers have not been checked; legal modifiers are R and W */ t_stat inq_io (int32 flag, int32 mod) { int32 i, t, wm_seen = 0; t_bool use_h = inq_unit.flags & UNIT_PCH; ind[IN_INC] = 0; /* clear inq clear */ switch (mod) { /* case on mod */ case BCD_R: /* input */ /* if (ind[IN_INR] == 0) /* return if no req */ return SCPE_OK; ind[IN_INR] = 0; /* clear req */ puts_tty ("[Enter]\r\n"); /* prompt */ for (i = 0; M[BS] != (BCD_GRPMRK + WM); i++) { /* until GM + WM */ while (((t = sim_poll_kbd ()) == SCPE_OK) || (t & SCPE_BREAK)) { if (stop_cpu) /* interrupt? */ return SCPE_STOP; } if (t < SCPE_KFLAG) /* if not char, err */ return t; t = t & 0177; if ((t == '\r') || (t == '\n')) break; if (t == inq_char) { /* cancel? */ ind[IN_INC] = 1; /* set indicator */ puts_tty ("\r\n[Canceled]\r\n"); return SCPE_OK; } if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); sim_putchar (t); /* echo */ if (flag == MD_WM) { /* word mark mode? */ if ((t == '~') && (wm_seen == 0)) wm_seen = WM; else { M[BS] = wm_seen | ascii2bcd (t); wm_seen = 0; } } else M[BS] = (M[BS] & WM) | ascii2bcd (t); if (!wm_seen) BS++; if (ADDR_ERR (BS)) { BS = BA | (BS % MAXMEMSIZE); return STOP_NXM; } } puts_tty ("\r\n"); M[BS++] = BCD_GRPMRK + WM; break; case BCD_W: /* output */ for (i = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); i++) { if ((flag == MD_WM) && (t & WM)) { if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); if (conv_old) sim_putchar ('~'); else sim_putchar ('`'); } if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); sim_putchar (bcd2ascii (t & CHAR, use_h)); if (ADDR_ERR (BS)) { BS = BA | (BS % MAXMEMSIZE); return STOP_NXM; } } puts_tty ("\r\n"); break; default: /* invalid mod */ return STOP_INVM; } return SCPE_OK; } /* Unit service - polls for WRU or inquiry request */ t_stat inq_svc (UNIT *uptr) { int32 temp; sim_activate (&inq_unit, inq_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return temp; if ((temp & 0177) == inq_char) /* set indicator */ ind[IN_INR] = 1; return SCPE_OK; } /* Output multiple characters */ void puts_tty (char *cptr) { if (cptr == NULL) return; while (*cptr != 0) sim_putchar (*cptr++); return; } /* Reset routine */ t_stat inq_reset (DEVICE *dptr) { ind[IN_INR] = ind[IN_INC] = 0; /* clear indicators */ sim_activate (&inq_unit, inq_unit.wait); /* activate poll */ return SCPE_OK; } simh-3.8.1/I1401/i1401_mt.c0000644000175000017500000005137011111656730012750 0ustar vlmvlm/* i1401_mt.c: IBM 1401 magnetic tape simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt 7-track magtape 11-Jul-08 RMS Added -n (no rewind) option to BOOT (from Van Snyder) Added tape mark detect to diagnostic read (from Bob Abeles) Added tape mark detect in multi-character records (from Bob Abeles) Fixed memory leak in tape rewind-unload op (from Bob Abeles) Fixed bug, BOOT ignores GM+WM in memory (from Bob Abeles) Fixed handling of indicators (from Bob Abeles) Fixed bug to mask input to 6b on read (from Bob Abeles) 07-Jul-07 RMS Removed restriction on load-mode binary tape 28-Jun-07 RMS Revised read tape mark behavior based on real hardware (found by Van Snyder) 16-Feb-06 RMS Added tape capacity checking 15-Sep-05 RMS Yet another fix to load read group mark plus word mark Added debug printouts (from Van Snyder) 26-Aug-05 RMS Revised to use API for write lock check 16-Aug-03 RMS End-of-record on load read works like move read (verified on real 1401) Added diagnostic read (space forward) 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 15-Mar-03 RMS Fixed end-of-record on load read yet again 28-Feb-03 RMS Modified for magtape library 31-Oct-02 RMS Added error record handling 10-Oct-02 RMS Fixed end-of-record on load read writes WM plus GM 30-Sep-02 RMS Revamped error handling 28-Aug-02 RMS Added end of medium support 12-Jun-02 RMS End-of-record on move read preserves old WM under GM (found by Van Snyder) 03-Jun-02 RMS Modified for 1311 support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added protection against bad record lengths 30-Jan-02 RMS New zero footprint tape bootstrap from Van Snyder 20-Jan-02 RMS Changed write enabled modifier 29-Nov-01 RMS Added read only unit support 18-Apr-01 RMS Changed to rewind tape before boot 07-Dec-00 RMS Widened display width from 6 to 8 bits to see record lnt CEO Added tape bootstrap 14-Apr-99 RMS Changed t_addr to unsigned 04-Oct-98 RMS V2.4 magtape format Magnetic tapes are represented as a series of variable 16b records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. (Notes on indicators from Bob Abeles) The 1401 implements a "tape indicator" latch per tape drive. When a 1401 branch-on-indicator instruction addresses the "end-of-reel" indicator, the 1401 addresses the "tape indicator" latch in the currently selected and ready tape drive. The tape drive itself resets its "tape indicator" latch only when a tape unload occurs. Tape unload may be initiated by push button control or by the CPU "Rewind Tape and Unload" instruction. The tape drive sets the "tape indicator" latch when in write status and the end-of-reel reflective strip photocell is activated. The CPU resets the "tape indicator" latch in the selected and ready tape drive when the "end-of-reel" indicator is tested by a branch-on-indicator instruction. If no tape drive is selected, or the currently selected tape drive is not ready, the "end-of-reel" indicator will appear to be off and no "tape indicator" latch will be reset. The CPU sets the "tape indicator" latch in the selected and ready tape drive whenever it reads a tape- mark character in the first character position of a record. */ #include "i1401_defs.h" #include "sim_tape.h" #define MT_NUMDR 7 /* #drives */ #define MT_MAXFR (MAXMEMSIZE * 2) /* max transfer */ #define IND u3 /* drive indicator */ uint8 dbuf[MT_MAXFR]; /* tape buffer */ int32 mt_sel = 0; /* selected unit */ extern uint8 M[]; /* memory */ extern int32 ind[64]; extern int32 BS, iochk; extern UNIT cpu_unit; extern FILE *sim_deb; t_stat mt_reset (DEVICE *dptr); t_stat mt_boot (int32 unitno, DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cp); t_stat mt_detach (UNIT *uptr); t_stat mt_map_status (t_stat st); UNIT *mt_sel_unit (int32 unit); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ UNIT mt_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, /* doesn't exist */ { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_ROABLE + UNIT_BCD, 0) } }; REG mt_reg[] = { { FLDATA (ERR, ind[IN_TAP], 0) }, { FLDATA (IND1, mt_unit[1].IND, 0) }, { FLDATA (IND2, mt_unit[2].IND, 0) }, { FLDATA (IND3, mt_unit[3].IND, 0) }, { FLDATA (IND4, mt_unit[4].IND, 0) }, { FLDATA (IND5, mt_unit[5].IND, 0) }, { FLDATA (IND6, mt_unit[6].IND, 0) }, { DRDATA (POS1, mt_unit[1].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS2, mt_unit[2].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS3, mt_unit[3].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS4, mt_unit[4].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS5, mt_unit[5].pos, T_ADDR_W), PV_LEFT + REG_RO }, { DRDATA (POS6, mt_unit[6].pos, T_ADDR_W), PV_LEFT + REG_RO }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, NULL, DEV_DEBUG }; /* Function routine Inputs: unit = unit character flag = normal, word mark, binary, or boot mode mod = modifier character Outputs: status = status */ t_stat mt_func (int32 unit, int32 flag, int32 mod) { t_mtrlnt tbc; UNIT *uptr; t_stat st; ind[IN_TAP] = 0; /* clear error */ if ((uptr = mt_sel_unit (unit)) == NULL) /* sel unit, save */ return STOP_INVMTU; /* (not valid) */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; switch (mod) { /* case on modifier */ case BCD_A: /* diagnostic read */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: diagnostic read\n", unit); st = sim_tape_rdrecf (uptr, dbuf, &tbc, MT_MAXFR); /* read rec */ if (st != MTSE_TMK) { /* not tmk? */ if (st == MTSE_RECE) /* rec in error? */ ind[IN_TAP] = 1; else if (st != MTSE_OK) { /* stop on error */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ", stopped by status = %d\n", st); break; } if (!(flag & MD_BIN) && /* BCD tape and */ ((dbuf[0] & CHAR) == BCD_TAPMRK)) /* first char TMK? */ uptr->IND = 1; /* set indicator */ } break; case BCD_B: /* backspace */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: backspace\n", unit); st = sim_tape_sprecr (uptr, &tbc); /* space rev */ break; /* end case */ case BCD_E: /* erase = nop */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: erase\n", unit); if (sim_tape_wrp (uptr)) /* write protected? */ return STOP_MTL; return SCPE_OK; case BCD_M: /* write tapemark */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: write tape mark\n", unit); st = sim_tape_wrtmk (uptr); /* write tmk */ break; case BCD_R: /* rewind */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: rewind\n", unit); sim_tape_rewind (uptr); /* update position */ return SCPE_OK; case BCD_U: /* unload */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: rewind and unload\n", unit); sim_tape_rewind (uptr); /* update position */ st = mt_detach (uptr); /* detach */ break; default: return STOP_INVM; } return mt_map_status (st); } /* Read and write routines Inputs: unit = unit character flag = normal, word mark, binary, or boot mode mod = modifier character Outputs: status = status Fine point: after a read, the system writes a group mark just beyond the end of the record. However, first it checks for a GM + WM; if present, the GM + WM is not changed. Otherwise, an MCW read sets a GM, preserving the current WM; while an LCA read sets a GM and clears the WM. */ t_stat mt_io (int32 unit, int32 flag, int32 mod) { int32 t, wm_seen; t_mtrlnt i, tbc; t_stat st; t_bool passed_eot; UNIT *uptr; ind[IN_TAP] = 0; /* clear error */ if ((uptr = mt_sel_unit (unit)) == NULL) /* sel unit, save */ return STOP_INVMTU; /* (not valid) */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; switch (mod) { case BCD_R: /* read */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: read from %d", unit, BS); wm_seen = 0; /* no word mk seen */ st = sim_tape_rdrecf (uptr, dbuf, &tbc, MT_MAXFR); /* read rec */ if (st != MTSE_TMK) { /* not tmk? */ if (st == MTSE_RECE) /* rec in error? */ ind[IN_TAP] = 1; else if (st != MTSE_OK) { /* stop on error */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ", stopped by status = %d\n", st); break; } if (!(flag & MD_BIN) && /* BCD tape and */ ((dbuf[0] & CHAR) == BCD_TAPMRK)) /* first char TMK? */ uptr->IND = 1; /* set indicator */ } for (i = 0; i < tbc; i++) { /* loop thru buf */ if (!(flag & MD_BOOT) && /* not boot? check */ (M[BS] == (BCD_GRPMRK + WM))) { /* GWM in memory? */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, " to %d, stopped by GMWM\n", BS); BS++; /* incr BS */ if (ADDR_ERR (BS)) { /* test for wrap */ BS = BA | (BS % MAXMEMSIZE); return STOP_WRAP; } return SCPE_OK; /* done */ } t = dbuf[i] & CHAR; /* get char, strip parity */ if (!(flag & MD_BIN) && (t == BCD_ALT)) /* BCD mode alt blank? */ t = BCD_BLANK; /* real blank */ if (flag & MD_WM) { /* word mk mode? */ if ((t == BCD_WM) && (wm_seen == 0)) /* WM char, none prev? */ wm_seen = WM; /* set flag */ else { M[BS] = wm_seen | (t & CHAR); /* char + wm seen */ wm_seen = 0; /* clear flag */ } } else M[BS] = (M[BS] & WM) | (t & CHAR); /* preserve mem WM */ if (!wm_seen) BS++; if (ADDR_ERR (BS)) { /* check next BS */ BS = BA | (BS % MAXMEMSIZE); return STOP_WRAP; } } if (M[BS] != (BCD_GRPMRK + WM)) { /* not GM+WM at end? */ if (flag & MD_WM) /* LCA: clear WM */ M[BS] = BCD_GRPMRK; else M[BS] = (M[BS] & WM) | BCD_GRPMRK; /* MCW: save WM */ } if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, " to %d, stopped by EOR\n", BS); BS++; /* adv BS */ if (ADDR_ERR (BS)) { /* check final BS */ BS = BA | (BS % MAXMEMSIZE); return STOP_WRAP; } break; case BCD_W: if (sim_tape_wrp (uptr)) /* locked? */ return STOP_MTL; if (M[BS] == (BCD_GRPMRK + WM)) /* eor? */ return STOP_MTZ; if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, ">>MT%d: write from %d", unit, BS); for (tbc = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); ) { if ((t & WM) && (flag & MD_WM)) /* WM in wm mode? */ dbuf[tbc++] = BCD_WM; if (((t & CHAR) == BCD_BLANK) && !(flag & MD_BIN)) dbuf[tbc++] = BCD_ALT; else dbuf[tbc++] = t & CHAR; if (ADDR_ERR (BS)) { /* check next BS */ BS = BA | (BS % MAXMEMSIZE); return STOP_WRAP; } } if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, " to %d\n", BS - 1); passed_eot = sim_tape_eot (uptr); /* passed EOT? */ st = sim_tape_wrrecf (uptr, dbuf, tbc); /* write record */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ ind[IN_END] = 1; if (ADDR_ERR (BS)) { /* check final BS */ BS = BA | (BS % MAXMEMSIZE); return STOP_WRAP; } break; default: return STOP_INVM; } return mt_map_status (st); } /* Select unit - save selection if valid, return unit pointer */ UNIT *mt_sel_unit (int32 unit) { if ((unit <= 0) || (unit >= MT_NUMDR)) return NULL; mt_sel = unit; /* save selected unit */ return mt_dev.units + unit; } /* Map tape status */ t_stat mt_map_status (t_stat st) { switch (st) { case MTSE_OK: /* no error */ case MTSE_BOT: /* reverse into BOT */ break; case MTSE_FMT: /* illegal fmt */ return SCPE_IERR; case MTSE_UNATT: /* not attached */ return SCPE_UNATT; case MTSE_INVRL: /* invalid rec lnt */ return SCPE_MTRLNT; case MTSE_TMK: /* end of file */ if (mt_sel != 0) mt_unit[mt_sel].IND = 1; /* set indicator latch */ break; case MTSE_IOERR: /* IO error */ ind[IN_TAP] = 1; /* set error */ if (iochk) return SCPE_IOERR; break; case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ ind[IN_TAP] = 1; /* set error */ break; case MTSE_WRP: /* write protect */ return STOP_MTL; } return SCPE_OK; } /* Test and reset selected tape drive's indicator latch */ t_bool mt_testind () { t_bool v; if (mt_sel == 0) /* any mt selected? */ return FALSE; if (!(mt_unit[mt_sel].flags & UNIT_ATT)) /* attached? */ return FALSE; v = mt_unit[mt_sel].IND; /* save latch */ mt_unit[mt_sel].IND = 0; /* reset latch */ return v; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { int32 i; UNIT *uptr; for (i = 0; i < MT_NUMDR; i++) { /* per drive resets */ if (uptr = mt_sel_unit (i)) { MT_CLR_PNU (uptr); /* clear pos flag */ uptr->IND = 0; /* clear ind latch */ } } ind[IN_TAP] = 0; /* clear mt err ind */ mt_sel = 0; /* clear unit select */ return SCPE_OK; } /* Bootstrap routine The 1401 Reference manual states, "...tape data starts loading at address 001 and continues until an inter-record gap is sensed." GM+WM in memory is ignored. */ t_stat mt_boot (int32 unitno, DEVICE *dptr) { extern int32 saved_IS; extern int32 sim_switches; if ((sim_switches & SWMASK ('N')) == 0) /* unless -n */ sim_tape_rewind (&mt_unit[unitno]); /* force rewind */ BS = 1; /* set BS = 001 */ mt_io (unitno, MD_WM + MD_BOOT, BCD_R); /* LDA %U1 001 R */ saved_IS = 1; return SCPE_OK; } /* Attach routine */ t_stat mt_attach (UNIT *uptr, char *cp) { uptr->IND = 0; /* reset indicator latch */ return sim_tape_attach (uptr, cp); /* tape unit attach */ } /* Detach routine */ t_stat mt_detach (UNIT *uptr) { uptr->IND = 0; /* reset indicator latch */ return sim_tape_detach (uptr); /* tape unit detach */ } simh-3.8.1/I1401/i1401_dat.h0000644000175000017500000001406311111656730013103 0ustar vlmvlm/* i1401_dat.h: IBM 1401 character conversion tables Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 20-Sep-05 RMS Updated for compatibility with Paul Pierce conventions */ /* Old tables */ /* ASCII to BCD conversion */ const char ascii_to_bcd_old[128] = { 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */ 017, 074, 054, 037, 033, 040, 073, 021, 012, 001, 002, 003, 004, 005, 006, 007, 010, 011, 015, 056, 076, 035, 016, 072, 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 075, 036, 055, 020, 057, 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 000, 000, 000, 000, 000 }; /* BCD to ASCII conversion - also the "full" print chain */ char bcd_to_ascii_old[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '#', '@', ':', '>', '(', '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\'', ',', '%', '=', '\\', '+', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '"' }; /* New tables */ /* ASCII to BCD conversion */ const char ascii_to_bcd[128] = { 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 052, 037, 013, 053, 034, 060, 014, /* 040 - 077 */ 034, 074, 054, 060, 033, 040, 073, 021, 012, 001, 002, 003, 004, 005, 006, 007, 010, 011, 015, 056, 076, 013, 016, 072, 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 075, 036, 055, 020, 057, 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 017, 032, 077, 035, 000 }; /* BCD to ASCII conversion */ const char bcd_to_ascii_a[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '#', '@', ':', '>', '{', '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '%', '~', '\\', '"', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}' }; const char bcd_to_ascii_h[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '=', '\'', ':', '>', '{', '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '(', '~', '\\', '"', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}' }; /* BCD to ASCII 48 character print chains */ const char bcd_to_pca[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '#', '@', ' ', ' ', ' ', ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ',', '%', ' ', ' ', ' ', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; const char bcd_to_pch[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '=', '\'', ' ', ' ', ' ', ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ',', '(', ' ', ' ', ' ', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; /* BCD to column binary conversion */ const uint32 bcd_to_colbin[64] = { 00000, 00400, 00200, 00100, 00040, 00020, 00010, 00004, 00002, 00001, 00202, 00102, 00042, 00022, 00012, 00006, 01000, 01400, 01200, 01100, 01040, 01020, 01010, 01004, 01002, 01001, 01202, 01102, 01042, 01022, 01012, 01006, 02000, 02400, 02200, 02100, 02040, 02020, 02010, 02004, 02002, 02001, 02202, 02102, 02042, 02022, 02012, 02006, 04000, 04400, 04200, 04100, 04040, 04020, 04010, 04004, 04002, 04001, 04202, 04102, 04042, 04022, 04012, 04006 }; simh-3.8.1/I1401/i1401_cpu.c0000644000175000017500000024215411112030454013107 0ustar vlmvlm/* i1401_cpu.c: IBM 1401 CPU simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 11-Jul-08 RMS Added missing A magtape modifier (from Van Snyder) Fixed tape indicator implementation (from Bob Abeles) Fixed bug in ZA and ZS (from Bob Abeles) 07-Jul-07 RMS Removed restriction on load-mode binary tape 28-Jun-07 RMS Added support for SS overlap modifiers 22-May-06 RMS Fixed format error in CPU history (found by Peter Schorn) 06-Mar-06 RMS Fixed bug in divide (found by Van Snyder) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 01-Sep-05 RMS Removed error stops in MCE 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jun-05 RMS Fixed SSB-SSG clearing on RESET (reported by Ralph Reinke) 14-Nov-04 WVS Added column binary support, debug support 06-Nov-04 RMS Added instruction history 12-Jul-03 RMS Moved ASCII/BCD tables to included file Revised fetch to model hardware Removed length checking in fetch phase 16-Mar-03 RMS Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS Fixed MCE bug, BS off by 1 if zero suppress Fixed chaining bug, D lost if return to SCP Fixed H branch, branch occurs after continue Added check for invalid 8 character MCW, LCA 03-Jun-03 RMS Added 1311 support 22-May-02 RMS Added multiply and divide 30-Dec-01 RMS Added old PC queue 30-Nov-01 RMS Added extended SET/SHOW support 10-Aug-01 RMS Removed register in declarations 07-Dec-00 RMS Fixed bugs found by Charles Owen -- 4,7 char NOPs are legal -- 1 char B is chained BCE -- MCE moves whole char after first 14-Apr-99 RMS Changed t_addr to unsigned The register state for the IBM 1401 is: IS I storage address register (PC) AS A storage address register (address of first operand) BS B storage address register (address of second operand) ind[0:63] indicators SSA sense switch A IOCHK I/O check PRCHK process check The IBM 1401 is a variable instruction length, decimal data system. Memory consists of 4000, 8000, 12000, or 16000 BCD characters, each containing six bits of data and a word mark. There are no general registers; all instructions are memory to memory, using explicit addresses or an address pointer from a prior instruction. BCD numeric data consists of the low four bits of a character (DIGIT), encoded as X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, X, X, X, X, X. The high two bits (ZONE) encode the sign of the data as +, +, -, +. Character data uses all six bits of a character. Numeric and character fields are delimited by a word mark. Fields are typically processed in descending address order (low-order data to high-order data). The 1401 encodes a decimal address, and an index register number, in three characters: character zone digit addr + 0 <1:0> of thousands hundreds addr + 1 index register # tens addr + 2 <3:2> of thousands ones Normally the digit values 0, 11, 12, 13, 14, 15 are illegal in addresses. However, in indexing, digits are passed through the adder, and illegal values are normalized to legal counterparts. The 1401 has six instruction formats: op A and B addresses, if any, from AS and BS op d A and B addresses, if any, from AS and BS op aaa B address, if any, from BS op aaa d B address, if any, from BS op aaa bbb op aaa bbb d where aaa is the A address, bbb is the B address, and d is a modifier. The opcode has word mark set; all other characters have word mark clear. This routine is the instruction decode routine for the IBM 1401. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered illegal addresses or instruction formats I/O error in I/O simulator 2. Interrupts. The 1401 has no interrupt structure. 3. Non-existent memory. On the 1401, references to non-existent memory halt the processor. 4. Adding I/O devices. These modules must be modified: i1401_cpu.c add device dispatching code to iodisp i1401_sys.c add sim_devices table entry */ #include "i1401_defs.h" #include "i1401_dat.h" #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] = saved_IS #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { uint16 is; uint16 ilnt; uint8 inst[MAX_L]; } InstHistory; /* These macros validate addresses. If an addresses error is detected, they return an error status to the caller. These macros should only be used in a routine that returns a t_stat value. */ #define MM(x) x = x - 1; \ if (x < 0) { \ x = BA + MAXMEMSIZE - 1; \ reason = STOP_WRAP; \ break; \ } #define PP(x) x = x + 1; \ if (ADDR_ERR (x)) { \ x = BA + (x % MAXMEMSIZE); \ reason = STOP_WRAP; \ break; \ } #define BRANCH if (ADDR_ERR (AS)) { \ reason = STOP_INVBR; \ break; \ } \ if (cpu_unit.flags & XSA) \ BS = IS; \ else BS = BA + 0; \ PCQ_ENTRY; \ IS = AS; uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */ int32 saved_IS = 0; /* saved IS */ int32 AS = 0; /* AS */ int32 BS = 0; /* BS */ int32 D = 0; /* modifier */ int32 as_err = 0, bs_err = 0; /* error flags */ int32 hb_pend = 0; /* halt br pending */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ int32 ind[64] = { 0 }; /* indicators */ int32 ssa = 1; /* sense switch A */ int32 prchk = 0; /* process check stop */ int32 iochk = 0; /* I/O check stop */ int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ t_bool conv_old = 0; /* old conversions */ extern int32 sim_int_char; extern int32 sim_emax; extern t_value *sim_eval; extern FILE *sim_deb; 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_conv (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_conv (FILE *st, UNIT *uptr, int32 val, void *desc); int32 store_addr_h (int32 addr); int32 store_addr_t (int32 addr); int32 store_addr_u (int32 addr); int32 div_add (int32 ap, int32 bp, int32 aend); int32 div_sub (int32 ap, int32 bp, int32 aend); void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp); t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr); t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod); extern t_stat read_card (int32 ilnt, int32 mod); extern t_stat punch_card (int32 ilnt, int32 mod); extern t_stat select_stack (int32 mod); extern t_stat carriage_control (int32 mod); extern t_stat write_line (int32 ilnt, int32 mod); extern t_stat inq_io (int32 flag, int32 mod); extern t_stat mt_io (int32 unit, int32 flag, int32 mod); extern t_stat dp_io (int32 fnc, int32 flag, int32 mod); extern t_stat mt_func (int32 unit, int32 flag, int32 mod); extern t_bool mt_testind (void); extern t_stat sim_activate (UNIT *uptr, int32 delay); extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifier list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BCD + STDOPT, MAXMEMSIZE) }; REG cpu_reg[] = { { DRDATA (IS, saved_IS, 14), PV_LEFT }, { DRDATA (AS, AS, 14), PV_LEFT }, { DRDATA (BS, BS, 14), PV_LEFT }, { FLDATA (ASERR, as_err, 0) }, { FLDATA (BSERR, bs_err, 0) }, { ORDATA (D, D, 7) }, { FLDATA (SSA, ssa, 0) }, { FLDATA (SSB, ind[IN_SSB], 0) }, { FLDATA (SSC, ind[IN_SSC], 0) }, { FLDATA (SSD, ind[IN_SSD], 0) }, { FLDATA (SSE, ind[IN_SSE], 0) }, { FLDATA (SSF, ind[IN_SSF], 0) }, { FLDATA (SSG, ind[IN_SSG], 0) }, { FLDATA (EQU, ind[IN_EQU], 0) }, { FLDATA (UNEQ, ind[IN_UNQ], 0) }, { FLDATA (HIGH, ind[IN_HGH], 0) }, { FLDATA (LOW, ind[IN_LOW], 0) }, { FLDATA (OVF, ind[IN_OVF], 0) }, { FLDATA (IOCHK, iochk, 0) }, { FLDATA (PRCHK, prchk, 0) }, { FLDATA (HBPEND, hb_pend, 0) }, { BRDATA (ISQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC }, { DRDATA (ISQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { FLDATA (CONVOLD, conv_old, 0), REG_HIDDEN }, { NULL } }; MTAB cpu_mod[] = { { XSA, XSA, "XSA", "XSA", NULL }, { XSA, 0, "no XSA", "NOXSA", NULL }, { HLE, HLE, "HLE", "HLE", NULL }, { HLE, 0, "no HLE", "NOHLE", NULL }, { BBE, BBE, "BBE", "BBE", NULL }, { BBE, 0, "no BBE", "NOBBE", NULL }, { MA, MA, "MA", 0, NULL }, { MA, 0, "no MA", 0, NULL }, { MR, MR, "MR", "MR", NULL }, { MR, 0, "no MR", "NOMR", NULL }, { EPE, EPE, "EPE", "EPE", NULL }, { EPE, 0, "no EPE", "NOEPE", NULL }, { MDV, MDV, "MDV", "MDV", NULL }, { MDV, 0, "no MDV", "NOMDV", NULL }, { UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16000, NULL, "16K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CONVERSIONS", "NEWCONVERSIONS", &cpu_set_conv, &cpu_show_conv }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, NULL, "OLDCONVERSIONS", &cpu_set_conv, NULL }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 10, 14, 1, 8, 7, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, DEV_DEBUG }; /* Tables */ /* Opcode table - length, dispatch, and option flags. This table is used by the symbolic input routine to validate instruction lengths */ const int32 op_table[64] = { 0, /* 00: illegal */ L1 | L2 | L4 | L5, /* read */ L1 | L2 | L4 | L5, /* write */ L1 | L2 | L4 | L5, /* write and read */ L1 | L2 | L4 | L5, /* punch */ L1 | L4, /* read and punch */ L1 | L2 | L4 | L5, /* write and read */ L1 | L2 | L4 | L5, /* write, read, punch */ L1, /* 10: read feed */ L1, /* punch feed */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ | MA, /* modify address */ L1 | L4 | L7 | AREQ | BREQ | MDV, /* multiply */ 0, /* illegal */ 0, /* illegal */ 0, /* illegal */ 0, /* 20: illegal */ L1 | L4 | L7 | BREQ | NOWM, /* clear storage */ L1 | L4 | L7 | AREQ | BREQ, /* subtract */ 0, /* illegal */ L5 | IO, /* magtape */ L1 | L8 | BREQ, /* branch wm or zone */ L1 | L8 | BREQ | BBE, /* branch if bit eq */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ, /* 30: move zones */ L1 | L4 | L7 | AREQ | BREQ, /* move supress zero */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */ L1 | L4 | L7 | AREQ | BREQ | MDV, /* divide */ 0, /* illegal */ 0, /* illegal */ 0, /* illegal */ 0, /* 40: illegal */ 0, /* illegal */ L2 | L5, /* select stacker */ L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* load */ L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* move */ HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* nop */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ | MR, /* move to record */ L1 | L4 | AREQ | MLS, /* 50: store A addr */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ, /* zero and subtract */ 0, /* illegal */ 0, /* illegal */ 0, /* illegal */ 0, /* illegal */ 0, /* illegal */ 0, /* 60: illegal */ L1 | L4 | L7 | AREQ | BREQ, /* add */ L1 | L4 | L5 | L8, /* branch */ L1 | L4 | L7 | AREQ | BREQ, /* compare */ L1 | L4 | L7 | AREQ | BREQ, /* move numeric */ L1 | L4 | L7 | AREQ | BREQ, /* move char edit */ L2 | L5, /* carriage control */ 0, /* illegal */ L1 | L4 | L7 | AREQ | MLS, /* 70: store B addr */ 0, /* illegal */ L1 | L4 | L7 | AREQ | BREQ, /* zero and add */ HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* halt */ L1 | L4 | L7 | AREQ | BREQ, /* clear word mark */ 0, /* illegal */ 0, /* illegal */ 0 /* illegal */ }; const int32 len_table[9] = { 0, L1, L2, 0, L4, L5, 0, L7, L8 }; /* Address character conversion tables. Illegal characters are marked by the flag BA but also contain the post-adder value for indexing */ const int32 hun_table[64] = { BA+000, 100, 200, 300, 400, 500, 600, 700, 800, 900, 000, BA+300, BA+400, BA+500, BA+600, BA+700, BA+1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 1000, BA+1300, BA+1400, BA+1500, BA+1600, BA+1700, BA+2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 2000, BA+2300, BA+2400, BA+2500, BA+2600, BA+2700, BA+3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900, 3000, BA+3300, BA+3400, BA+3500, BA+3600, BA+3700 }; const int32 ten_table[64] = { BA+00, 10, 20, 30, 40, 50, 60, 70, 80, 90, 00, BA+30, BA+40, BA+50, BA+60, BA+70, X1+00, X1+10, X1+20, X1+30, X1+40, X1+50, X1+60, X1+70, X1+80, X1+90, X1+00, X1+30, X1+40, X1+50, X1+60, X1+70, X2+00, X2+10, X2+20, X2+30, X2+40, X2+50, X2+60, X2+70, X2+80, X2+90, X2+00, X2+30, X2+40, X2+50, X2+60, X2+70, X3+00, X3+10, X3+20, X3+30, X3+40, X3+50, X3+60, X3+70, X3+80, X3+90, X3+00, X3+30, X3+40, X3+50, X3+60, X3+70 }; const int32 one_table[64] = { BA+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, BA+3, BA+4, BA+5, BA+6, BA+7, BA+4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4000, BA+4003, BA+4004, BA+4005, BA+4006, BA+4007, BA+8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8008, 8009, 8000, BA+8003, BA+8004, BA+8005, BA+8006, BA+8007, BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007, 12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 }; const int32 bin_to_bcd[16] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; const int32 bcd_to_bin[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 }; /* Indicator resets - a 1 marks an indicator that resets when tested */ static const int32 ind_table[64] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ 0, 1, 1, 0, 1, 0, 0, 0, /* 30 - 37 */ 0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */ 0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */ 0, 0, 1, 0, 0, 0, 0, 0 /* 70 - 77 */ }; /* Character collation table for compare with HLE option */ static const int32 col_table[64] = { 000, 067, 070, 071, 072, 073, 074, 075, 076, 077, 066, 024, 025, 026, 027, 030, 023, 015, 056, 057, 060, 061, 062, 063, 064, 065, 055, 016, 017, 020, 021, 022, 014, 044, 045, 046, 047, 050, 051, 052, 053, 054, 043, 007, 010, 011, 012, 013, 006, 032, 033, 034, 035, 036, 037, 040, 041, 042, 031, 001, 002, 003, 004, 005 }; /* Summing table for two decimal digits, converted back to BCD Also used for multiplying two decimal digits, converted back to BCD, with carry forward */ static const int32 sum_table[100] = { BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE }; static const int32 cry_table[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; /* Legal modifier tables */ static const int32 r_mod[] = { BCD_C, -1 }; static const int32 p_mod[] = { BCD_C, -1 }; static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 }; static const int32 ss_mod[] = { BCD_ONE, BCD_TWO, BCD_FOUR, BCD_EIGHT, BCD_DOLLAR, BCD_DECIMAL, BCD_SQUARE, -1 }; static const int32 mtf_mod[] = { BCD_A, BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 }; t_stat sim_instr (void) { extern int32 sim_interval; int32 IS, ilnt, flags; int32 op, xa, t, wm, ioind, dev, unit; int32 a, b, i, k, asave, bsave; int32 carry, lowprd, sign, ps; int32 quo, ahigh, qs; int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal; t_stat reason, r1, r2; /* Restore saved state */ IS = saved_IS; if (as_err) /* flag bad addresses */ AS = AS | BA; if (bs_err) BS = BS | BA; as_err = bs_err = 0; /* reset error flags */ reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (hb_pend) { /* halt br pending? */ hb_pend = 0; /* clear flag */ BRANCH; /* execute branch */ } saved_IS = IS; /* commit prev instr */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } if (sim_brk_summ && sim_brk_test (IS, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } sim_interval = sim_interval - 1; /* Instruction fetch - 1401 fetch works as follows: - Each character fetched enters the B register. This register is not visible; the variable t represents the B register. - Except for the first and last cycles, each character fetched enters the A register. This register is not visible; the variable D represents the A register, because this is the instruction modifier for 2, 5, and 8 character instructions. - At the start of the second cycle (first address character), the A-address register and, for most instructions, the B-address register, are cleared to blanks. The simulator represents addresses in binary and creates the effect of blanks (address is bad) if less than three A-address characters are found. Further, the simulator accumulates only the A-address, and replicates it to the B-address at the appropriate point. - At the start of the fifth cycle (fourth address character), the B-address register is cleared to blanks. Again, the simulator creates the effect of blanks (address is bad) if less than three B-address characters are found. The 1401 does not explicitly check for valid instruction lengths. Most 2, 3, 5, 6 character instructions will be invalid because the A-address or B-address (or both) are invalid. */ if ((M[IS] & WM) == 0) { /* I-Op: WM under op? */ reason = STOP_NOWM; /* no, error */ break; } op = M[IS] & CHAR; /* get opcode */ flags = op_table[op]; /* get op flags */ if ((flags == 0) || (flags & ALLOPT & ~cpu_unit.flags)) { reason = STOP_NXI; /* illegal inst? */ break; } if (op == OP_SAR) /* SAR? save ASTAR */ BS = AS; PP (IS); if ((t = M[IS]) & WM) /* I-1: WM? 1 char inst */ goto CHECK_LENGTH; D = ioind = t; /* could be D char, % */ AS = hun_table[t]; /* could be A addr */ PP (IS); /* if %xy, BA is set */ if ((t = M[IS]) & WM) { /* I-2: WM? 2 char inst */ AS = AS | BA; /* ASTAR bad */ if (!(flags & MLS)) BS = AS; goto CHECK_LENGTH; } D = dev = t; /* could be D char, dev */ AS = AS + ten_table[t]; /* build A addr */ PP (IS); if ((t = M[IS]) & WM) { /* I-3: WM? 3 char inst */ AS = AS | BA; /* ASTAR bad */ if (!(flags & MLS)) BS = AS; goto CHECK_LENGTH; } D = unit = t; /* could be D char, unit */ if (unit == BCD_ZERO) /* convert unit to binary */ unit = 0; AS = AS + one_table[t]; /* finish A addr */ xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */ if (xa && (ioind != BCD_PERCNT) && (cpu_unit.flags & XSA)) { /* indexed? */ AS = AS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + one_table[M[xa + 2] & CHAR]; AS = (AS & INDEXMASK) % MAXMEMSIZE; } if (!(flags & MLS)) /* not MLS? B = A */ BS = AS; PP (IS); if ((t = M[IS]) & WM) /* I-4: WM? 4 char inst */ goto CHECK_LENGTH; if ((op == OP_B) && (t == BCD_BLANK)) /* BR + space? */ goto CHECK_LENGTH; D = t; /* could be D char */ BS = hun_table[t]; /* could be B addr */ PP (IS); if ((t = M[IS]) & WM) { /* I-5: WM? 5 char inst */ BS = BS | BA; /* BSTAR bad */ goto CHECK_LENGTH; } D = t; /* could be D char */ BS = BS + ten_table[t]; /* build B addr */ PP (IS); if ((t = M[IS]) & WM) { /* I-6: WM? 6 char inst */ BS = BS | BA; /* BSTAR bad */ goto CHECK_LENGTH; } D = t; /* could be D char */ BS = BS + one_table[t]; /* finish B addr */ xa = (BS >> V_INDEX) & M_INDEX; /* get index reg */ if (xa && (cpu_unit.flags & XSA)) { /* indexed? */ BS = BS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + one_table[M[xa + 2] & CHAR]; BS = (BS & INDEXMASK) % MAXMEMSIZE; } PP (IS); if (flags & NOWM) /* I-7: SWM? done */ goto CHECK_LENGTH; if ((t = M[IS]) & WM) /* WM? 7 char inst */ goto CHECK_LENGTH; D = t; /* last char is D */ while (((t = M[IS]) & WM) == 0) { /* I-8: repeats until WM */ D = t; /* last char is D */ PP (IS); } if (reason) /* addr err on last? */ break; CHECK_LENGTH: if ((flags & BREQ) && ADDR_ERR (BS)) { /* valid B? */ reason = STOP_INVB; break; } if ((flags & AREQ) && ADDR_ERR (AS)) { /* valid A? */ reason = STOP_INVA; break; } ilnt = IS - saved_IS; /* get lnt */ if (hst_lnt) { /* history enabled? */ hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].is = saved_IS; /* save IS */ hst[hst_p].ilnt = ilnt; for (i = 0; (i < MAX_L) && (i < ilnt); i++) hst[hst_p].inst[i] = M[saved_IS + i]; } if (DEBUG_PRS (cpu_dev)) { fprint_val (sim_deb, saved_IS, 10, 5, PV_RSPC); fprintf (sim_deb, ": " ); for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; for (i = 0, k = saved_IS; i < sim_emax; i++, k++) { if (cpu_ex (&sim_eval[i], k, &cpu_unit, 0) != SCPE_OK) break; } fprint_sym (sim_deb, saved_IS, sim_eval, &cpu_unit, SWMASK('M')); fprintf (sim_deb, "\n" ); } switch (op) { /* case on opcode */ /* Move/load character instructions A check B check MCW copy A to B, preserving B WM, here fetch until either A or B WM LCA copy A to B, overwriting B WM, here fetch until A WM Instruction lengths: 1 chained A and B 2,3 invalid A-address 4 chained B address 5,6 invalid B-address 7 normal 8+ normal + modifier */ case OP_MCW: /* move char */ if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */ reason = iodisp (dev, unit, MD_NORM, D); /* dispatch I/O */ break; } if (ADDR_ERR (AS)) { /* check A addr */ reason = STOP_INVA; break; } do { wm = M[AS] | M[BS]; M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ MM (AS); /* decr pointers */ MM (BS); } while ((wm & WM) == 0); /* stop on A,B WM */ break; case OP_LCA: /* load char */ if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */ reason = iodisp (dev, unit, MD_WM, D); break; } if (ADDR_ERR (AS)) { /* check A addr */ reason = STOP_INVA; break; } do { wm = M[BS] = M[AS]; /* move char + wmark */ MM (AS); /* decr pointers */ MM (BS); } while ((wm & WM) == 0); /* stop on A WM */ break; /* Other move instructions A check B check MCM copy A to B, preserving B WM, fetch fetch until record or group mark MCS copy A to B, clearing B WM, until A WM; fetch fetch reverse scan and suppress leading zeroes MN copy A char digit to B char digit, fetch fetch preserving B zone and WM MZ copy A char zone to B char zone, fetch fetch preserving B digit and WM Instruction lengths: 1 chained 2,3 invalid A-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier */ case OP_MCM: /* move to rec/group */ do { t = M[AS]; M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ PP (AS); /* incr pointers */ PP (BS); } while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM))); break; case OP_MCS: /* move suppress zero */ bsave = BS; /* save B start */ qzero = 1; /* set suppress */ do { wm = M[AS]; M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT);/* copy char */ MM (AS); /* decr pointers */ MM (BS); } while ((wm & WM) == 0); /* stop on A WM */ if (reason) /* addr err? stop */ break; do { PP (BS); /* adv B */ t = M[BS]; /* get B, cant be WM */ if ((t == BCD_ZERO) || (t == BCD_COMMA)) { if (qzero) M[BS] = 0; } else if ((t == BCD_BLANK) || (t == BCD_MINUS)) ; else if (((t == BCD_DECIMAL) && (cpu_unit.flags & EPE)) || (t <= BCD_NINE)) qzero = 0; else qzero = 1; } while (BS < bsave); PP (BS); /* BS end is B+1 */ break; case OP_MN: /* move numeric */ M[BS] = (M[BS] & ~DIGIT) | (M[AS] & DIGIT); /* move digit */ MM (AS); /* decr pointers */ MM (BS); break; case OP_MZ: /* move zone */ M[BS] = (M[BS] & ~ZONE) | (M[AS] & ZONE); /* move high bits */ MM (AS); /* decr pointers */ MM (BS); break; /* Branch instruction A check B check Instruction lengths: 1 branch if B char equals d, chained if branch here 2,3 invalid B-address if branch here 4 unconditional branch if branch 5 branch if indicator[d] is set if branch 6 invalid B-address if branch here 7 branch if B char equals d, if branch here d is last character of B-address 8 branch if B char equals d if branch here */ case OP_B: /* branch */ if (ilnt == 4) { /* uncond branch? */ BRANCH; } else if (ilnt == 5) { /* branch on ind? */ if (D == IN_END) { /* tape indicator */ if (mt_testind ()) { /* test, reset */ BRANCH; } else break; } if (ind[D]) { /* test indicator */ BRANCH; } if (ind_table[D]) /* reset if needed */ ind[D] = 0; } else { /* branch char eq */ if (ADDR_ERR (BS)) { /* validate B addr */ reason = STOP_INVB; break; } if ((M[BS] & CHAR) == D) { /* char equal? */ BRANCH; } else { MM (BS); } } break; /* Other branch instructions A check B check BWZ branch if (d<0>: B char WM) if branch fetch (d<1>: B char zone = d zone) BBE branch if B char & d non-zero if branch fetch Instruction lengths: 1 chained 2,3 invalid A-address and B-address 4 self (B-address = A-address, d = last character of A-address) 5,6 invalid B-address 7 normal, d = last character of B-address 8+ normal */ case OP_BWZ: /* branch wm or zone */ if (((D & 1) && (M[BS] & WM)) || /* d1? test wm */ ((D & 2) && ((M[BS] & ZONE) == (D & ZONE)))) { /* d2? test zone */ BRANCH; } else { /* decr pointer */ MM (BS); } break; case OP_BBE: /* branch if bit eq */ if (M[BS] & D & CHAR) { /* any bits set? */ BRANCH; } else { /* decr pointer */ MM (BS); } break; /* Arithmetic instructions A check B check ZA move A to B, normalizing A sign, fetch fetch preserving B WM, until B WM ZS move A to B, complementing A sign, fetch fetch preserving B WM, until B WM A add A to B fetch fetch S subtract A from B fetch fetch C compare A to B fetch fetch Instruction lengths: 1 chained 2,3 invalid A-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier Despite their names, ZA and ZS are not arithmetic instructions, but copies with zone stripping. The adder is not used, so BCD conversions do not occur. */ case OP_ZA: case OP_ZS: /* zero and add/sub */ a = i = 0; /* clear flags */ do { if (a & WM) /* A word mark? */ wm = M[BS] = (M[BS] & WM) | BCD_ZERO; else { a = M[AS]; /* get A char */ t = a & DIGIT; /* zap zone bits */ wm = M[BS] = (M[BS] & WM) | t; /* store digit */ MM (AS); } if (i == 0) i = M[BS] = M[BS] | ((((a & ZONE) == BBIT) ^ (op == OP_ZS))? BBIT: ZONE); MM (BS); } while ((wm & WM) == 0); /* stop on B WM */ break; case OP_A: case OP_S: /* add/sub */ bsave = BS; /* save sign pos */ a = M[AS]; /* get A digit/sign */ b = M[BS]; /* get B digit/sign */ MM (AS); qsign = ((a & ZONE) == BBIT) ^ ((b & ZONE) == BBIT) ^ (op == OP_S); t = bcd_to_bin[a & DIGIT]; /* get A binary */ t = bcd_to_bin[b & DIGIT] + (qsign? 10 - t: t); /* sum A + B */ carry = (t >= 10); /* get carry */ b = (b & ~DIGIT) | sum_table[t]; /* get result */ if (qsign && ((b & BBIT) == 0)) /* normalize sign */ b = b | ZONE; M[BS] = b; /* store result */ MM (BS); if (b & WM) { /* b wm? done */ if (qsign && (carry == 0)) /* compl, no carry? */ M[bsave] = WM + ((b & ZONE) ^ ABIT) + sum_table[10 - t]; break; } do { if (a & WM) /* A WM? char = 0 */ a = WM; else { a = M[AS]; /* else get A */ MM (AS); } b = M[BS]; /* get B */ t = bcd_to_bin[a & DIGIT]; /* get A binary */ t = bcd_to_bin[b & DIGIT] + (qsign? 9 - t: t) + carry; carry = (t >= 10); /* get carry */ if ((b & WM) && (qsign == 0)) { /* last, no recomp? */ M[BS] = WM + sum_table[t] + /* zone add */ (((a & ZONE) + b + (carry? ABIT: 0)) & ZONE); ind[IN_OVF] = carry; /* ovflo if carry */ } else M[BS] = (b & WM) + sum_table[t]; /* normal add */ MM (BS); } while ((b & WM) == 0); /* stop on B WM */ if (reason) /* address err? */ break; if (qsign && (carry == 0)) { /* recompl, no carry? */ M[bsave] = M[bsave] ^ ABIT; /* XOR sign */ for (carry = 1; bsave != BS; --bsave) { /* rescan */ t = 9 - bcd_to_bin[M[bsave] & DIGIT] + carry; carry = (t >= 10); M[bsave] = (M[bsave] & ~DIGIT) | sum_table[t]; } } break; case OP_C: /* compare */ if (ilnt != 1) { /* if not chained */ ind[IN_EQU] = 1; /* clear indicators */ ind[IN_UNQ] = ind[IN_HGH] = ind[IN_LOW] = 0; } do { a = M[AS]; /* get characters */ b = M[BS]; wm = a | b; /* get word marks */ if ((a & CHAR) != (b & CHAR)) { /* unequal? */ ind[IN_EQU] = 0; /* set indicators */ ind[IN_UNQ] = 1; ind[IN_HGH] = col_table[b & CHAR] > col_table [a & CHAR]; ind[IN_LOW] = ind[IN_HGH] ^ 1; } MM (AS); /* decr pointers */ MM (BS); } while ((wm & WM) == 0); /* stop on A, B WM */ if ((a & WM) && !(b & WM)) { /* short A field? */ ind[IN_EQU] = ind[IN_LOW] = 0; ind[IN_UNQ] = ind[IN_HGH] = 1; } if (!(cpu_unit.flags & HLE)) /* no HLE? */ ind[IN_EQU] = ind[IN_LOW] = ind[IN_HGH] = 0; break; /* I/O instructions A check B check R read a card if branch W write to line printer if branch WR write and read if branch P punch a card if branch RP read and punch if branch WP : write and punch if branch WRP write read and punch if branch RF read feed (nop) PF punch feed (nop) SS select stacker if branch CC carriage control if branch Instruction lengths: 1 normal 2,3 normal, with modifier 4 branch; modifier, if any, is last character of branch address 5 branch + modifier 6+ normal, with modifier */ case OP_R: /* read */ if (reason = iomod (ilnt, D, r_mod)) /* valid modifier? */ break; reason = read_card (ilnt, D); /* read card */ BS = CDR_BUF + CDR_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } break; case OP_W: /* write */ if (reason = iomod (ilnt, D, w_mod)) /* valid modifier? */ break; reason = write_line (ilnt, D); /* print line */ BS = LPT_BUF + LPT_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } break; case OP_P: /* punch */ if (reason = iomod (ilnt, D, p_mod)) /* valid modifier? */ break; reason = punch_card (ilnt, D); /* punch card */ BS = CDP_BUF + CDP_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } break; case OP_WR: /* write and read */ if (reason = iomod (ilnt, D, w_mod)) /* valid modifier? */ break; reason = write_line (ilnt, D); /* print line */ r1 = read_card (ilnt, D); /* read card */ BS = CDR_BUF + CDR_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } if (reason == SCPE_OK) /* merge errors */ reason = r1; break; case OP_WP: /* write and punch */ if (reason = iomod (ilnt, D, w_mod)) /* valid modifier? */ break; reason = write_line (ilnt, D); /* print line */ r1 = punch_card (ilnt, D); /* punch card */ BS = CDP_BUF + CDP_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } if (reason == SCPE_OK) /* merge errors */ reason = r1; break; case OP_RP: /* read and punch */ if (reason = iomod (ilnt, D, NULL)) /* valid modifier? */ break; reason = read_card (ilnt, D); /* read card */ r1 = punch_card (ilnt, D); /* punch card */ BS = CDP_BUF + CDP_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } if (reason == SCPE_OK) /* merge errors */ reason = r1; break; case OP_WRP: /* write, read, punch */ if (reason = iomod (ilnt, D, w_mod)) /* valid modifier? */ break; reason = write_line (ilnt, D); /* print line */ r1 = read_card (ilnt, D); /* read card */ r2 = punch_card (ilnt, D); /* punch card */ BS = CDP_BUF + CDP_WIDTH; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } if (reason == SCPE_OK) /* merge errors */ reason = (r1 == SCPE_OK)? r2: r1; break; case OP_SS: /* select stacker */ if (reason = iomod (ilnt, D, ss_mod)) /* valid modifier? */ break; if (reason = select_stack (D)) /* sel stack, error? */ break; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } break; case OP_CC: /* carriage control */ if (reason = carriage_control (D)) /* car ctrl, error? */ break; if ((ilnt == 4) || (ilnt == 5)) { /* check for branch */ BRANCH; } break; /* MTF - magtape functions - must be at least 4 characters Instruction lengths: 1-3 invalid I/O address 4 normal, d-character is unit 5 normal 6+ normal, d-character is last character */ case OP_MTF: /* magtape function */ if (ilnt < 4) /* too short? */ reason = STOP_INVL; else if (ioind != BCD_PERCNT) /* valid dev addr? */ reason = STOP_INVA; else if (reason = iomod (ilnt, D, mtf_mod)) /* valid modifier? */ break; if (dev == IO_MT) /* BCD? */ reason = mt_func (unit, 0, D); else if (dev == IO_MTB) /* binary? */ reason = mt_func (unit, MD_BIN, D); else reason = STOP_INVA; /* wrong device */ break; /* can't branch */ case OP_RF: case OP_PF: /* read, punch feed */ break; /* nop's */ /* Move character and edit Control flags qsign sign of A field (0 = +, 1 = minus) qawm A field WM seen and processed qzero zero suppression enabled qbody in body (copying A field characters) qdollar EPE only; $ seen in body qaster EPE only; * seen in body qdecimal EPE only; . seen on first rescan MCE operates in one to three scans, the first of which has three phases 1 right to left qbody = 0, qawm = 0 => right status qbody = 1, qawm = 0 => body qbody = 0, qawm = 1 => left status 2 left to right 3 right to left, extended print end only The first A field character is masked to its digit part, all others are copied intact Instruction lengths: 1 chained 2,3 invalid A-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier */ case OP_MCE: /* edit */ a = M[AS]; /* get A char */ b = M[BS]; /* get B char */ t = a & DIGIT; /* get A digit */ MM (AS); qsign = ((a & ZONE) == BBIT); /* get A field sign */ qawm = qzero = qbody = 0; /* clear other flags */ qdollar = qaster = qdecimal = 0; /* clear EPE flags */ /* Edit pass 1 - from right to left, under B field control * in status or !epe, skip B; else, set qaster, repl with A $ in status or !epe, skip B; else, set qdollar, repl with A 0 in right status or body, if !qzero, set A WM; set qzero, repl with A else, if !qzero, skip B; else, if (!B WM) set B WM blank in right status or body, repl with A; else, skip B C,R,- in status, blank B; else, skip B , in status, blank B, else, skip B & blank B */ do { b = M[BS]; /* get B char */ M[BS] = M[BS] & ~WM; /* clr WM */ switch (b & CHAR) { /* case on B char */ case BCD_ASTER: /* * */ if (!qbody || qdollar || !(cpu_unit.flags & EPE)) break; qaster = 1; /* flag */ goto A_CYCLE; /* take A cycle */ case BCD_DOLLAR: /* $ */ if (!qbody || qaster || !(cpu_unit.flags & EPE)) break; qdollar = 1; /* flag */ goto A_CYCLE; /* take A cycle */ case BCD_ZERO: /* 0 */ if (qawm) { /* left status? */ if (!qzero) /* first? set WM */ M[BS] = M[BS] | WM; qzero = 1; /* flag suppress */ break; } if (!qzero) /* body, first? WM */ t = t | WM; qzero = 1; /* flag suppress */ goto A_CYCLE; /* take A cycle */ case BCD_BLANK: /* blank */ if (qawm) /* left status? */ break; A_CYCLE: M[BS] = t; /* copy char */ if (a & WM) { /* end of A field? */ qbody = 0; /* end body */ qawm = 1; /* start left status */ } else { qbody = 1; /* in body */ a = M[AS]; /* next A */ MM (AS); t = a & CHAR; /* use A char */ } break; case BCD_C: case BCD_R: case BCD_MINUS: /* C, R, - */ if (!qsign && !qbody) /* + & status? blank */ M[BS] = BCD_BLANK; break; case BCD_COMMA: /* , */ if (!qbody) /* status? blank */ M[BS] = BCD_BLANK; break; case BCD_AMPER: /* & */ M[BS] = BCD_BLANK; /* blank */ break; } /* end switch */ MM (BS); /* decr B pointer */ } while ((b & WM) == 0); /* stop on B WM */ if (reason) /* address err? */ break; if (!qzero) /* rescan? */ break; /* Edit pass 2 - from left to right, suppressing zeroes */ do { b = M[++BS]; /* get B char */ switch (b & CHAR) { /* case on B char */ case BCD_ONE: case BCD_TWO: case BCD_THREE: case BCD_FOUR: case BCD_FIVE: case BCD_SIX: case BCD_SEVEN: case BCD_EIGHT: case BCD_NINE: qzero = 0; /* turn off supr */ break; case BCD_ZERO: case BCD_COMMA: /* 0 or , */ if (qzero && !qdecimal) /* if supr, blank */ M[BS] = qaster? BCD_ASTER: BCD_BLANK; break; case BCD_BLANK: /* blank */ if (qaster) /* if EPE *, repl */ M[BS] = BCD_ASTER; break; case BCD_DECIMAL: /* . */ if (qzero && (cpu_unit.flags & EPE)) /* flag for EPE */ qdecimal = 1; break; case BCD_PERCNT: case BCD_WM: case BCD_BS: case BCD_TS: case BCD_MINUS: break; /* ignore */ default: /* other */ qzero = 1; /* restart supr */ break; } /* end case */ } while ((b & WM) == 0); M[BS] = M[BS] & ~WM; /* clear B WM */ if (!qdollar && !(qdecimal && qzero)) { /* rescan again? */ BS++; /* BS = addr WM + 1 */ break; } if (qdecimal && qzero) /* no digits? clr $ */ qdollar = 0; /* Edit pass 3 (extended print only) - from right to left */ for (;; ) { /* until chars */ b = M[BS]; /* get B char */ if ((b == BCD_BLANK) && qdollar) { /* blank & flt $? */ M[BS] = BCD_DOLLAR; /* insert $ */ break; /* exit for */ } if (b == BCD_DECIMAL) { /* decimal? */ M[BS] = qaster? BCD_ASTER: BCD_BLANK; break; /* exit for */ } if ((b == BCD_ZERO) && !qdollar) /* 0 & ~flt $ */ M[BS] = qaster? BCD_ASTER: BCD_BLANK; BS--; } /* end for */ break; /* done at last! */ /* Multiply. Comments from the PDP-10 based simulator by Len Fehskens. Multiply, with variable length operands, is necessarily done the same way you do it with paper and pencil, except that partial products are added into the incomplete final product as they are computed, rather than at the end. The 1401 multiplier format allows the product to be developed in place, without scratch storage. The A field contains the multiplicand, length LD. The B field must be LD + 1 + length of multiplier. Locate the low order multiplier digit, and at the same time zero out the product field. Then compute the sign of the result. Instruction lengths: 1 chained 2,3 invalid A-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier */ case OP_MUL: asave = AS; /* save AS, BS */ bsave = lowprd = BS; do { a = M[AS]; /* get mpcd char */ M[BS] = BCD_ZERO; /* zero prod */ MM (AS); /* decr pointers */ MM (BS); } while ((a & WM) == 0); /* until A WM */ if (reason) /* address err? */ break; M[BS] = BCD_ZERO; /* zero hi prod */ MM (BS); /* addr low mpyr */ sign = ((M[asave] & ZONE) == BBIT) ^ ((M[BS] & ZONE) == BBIT); /* Outer loop on multiplier (BS) and product digits (ps), inner loop on multiplicand digits (AS). AS and ps cannot produce an address error. */ do { ps = bsave; /* ptr to prod */ AS = asave; /* ptr to mpcd */ carry = 0; /* init carry */ b = M[BS]; /* get mpyr char */ do { a = M[AS]; /* get mpcd char */ t = (bcd_to_bin[a & DIGIT] * /* mpyr * mpcd */ bcd_to_bin[b & DIGIT]) + /* + c + partial prod */ carry + bcd_to_bin[M[ps] & DIGIT]; carry = cry_table[t]; M[ps] = (M[ps] & WM) | sum_table[t]; MM (AS); ps--; } while ((a & WM) == 0); /* until mpcd done */ M[BS] = (M[BS] & WM) | BCD_ZERO; /* zero mpyr just used */ t = bcd_to_bin[M[ps] & DIGIT] + carry; /* add carry to prod */ M[ps] = (M[ps] & WM) | sum_table[t]; /* store */ bsave--; /* adv prod ptr */ MM (BS); /* adv mpyr ptr */ } while ((b & WM) == 0); /* until mpyr done */ M[lowprd] = M[lowprd] | ZONE; /* assume + */ if (sign) /* if minus, B only */ M[lowprd] = M[lowprd] & ~ABIT; break; /* Divide. Comments from the PDP-10 based simulator by Len Fehskens. Divide is done, like multiply, pretty much the same way you do it with pencil and paper; successive subtraction of the divisor from a substring of the dividend while counting up the corresponding quotient digit. Let LS be the length of the divisor, LD the length of the dividend: - AS points to the low order divisor digit. - BS points to the high order dividend digit. - The low order dividend digit is identified by sign (zone) bits. - To the left of the dividend is a zero field of length LS + 1. The low quotient is at low dividend - LS - 1. As BS points to the high dividend, the low dividend is at BS + LD - 1, so the low quotient is at BS + LD - LS - 2. The longest possible quotient is LD - LS + 1, so the first possible non-zero quotient bit will be found as BS - 2. This pointer calculation assumes that the divisor has no leading zeroes. For each leading zero, the start of the quotient will be one position further left. Start by locating the high order non-zero digit of the divisor. This also tests for a divide by zero. Instruction lengths: 1 chained 2,3 invalid A-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier */ case OP_DIV: asave = AS; ahigh = -1; do { a = M[AS]; /* get dvr char */ if ((a & CHAR) != BCD_ZERO) /* mark non-zero */ ahigh = AS; MM (AS); } while ((a & WM) == 0); if (reason) /* address err? */ break; if (ahigh < 0) { /* div by zero? */ ind[IN_OVF] = 1; /* set ovf indic */ qs = bsave = BS; /* quo, dividend */ do { b = M[bsave]; /* find end divd */ PP (bsave); /* marked by zone */ } while ((b & ZONE) == 0); if (reason) /* address err? */ break; if (ADDR_ERR (qs)) { /* address err? */ reason = STOP_WRAP; /* address wrap? */ break; } div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */ BS = (BS - 2) - (asave - (AS + 1)); /* final bs */ break; } bsave = BS + (asave - ahigh); /* end subdivd */ qs = (BS - 2) - (ahigh - (AS + 1)); /* quo start */ /* Divide loop - done with subroutines to keep the code clean. In the loop, asave = low order divisor bsave = low order subdividend qs = current quotient digit */ do { quo = 0; /* clear quo digit */ if (ADDR_ERR (qs) || ADDR_ERR (bsave)) { reason = STOP_WRAP; /* address wrap? */ break; } b = M[bsave]; /* save low divd */ do { t = div_sub (asave, bsave, ahigh); /* subtract */ quo++; /* incr quo digit */ } while (t == 0); /* until borrow */ div_add (asave, bsave, ahigh); /* restore */ quo--; M[qs] = (M[qs] & WM) | sum_table[quo]; /* store quo digit */ bsave++; /* adv divd, quo */ qs++; } while ((b & ZONE) == 0); /* until B sign */ if (reason) /* address err? */ break; /* At this point, AS = high order divisor - 1 asave = unit position of divisor b = unit character of dividend bsave = unit position of remainder + 1 qs = unit position of quotient + 1 */ div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */ BS = qs - 2; /* BS = quo 10's pos */ break; /* Word mark instructions A check B check SWM set WM on A char and B char fetch fetch CWM clear WM on A char and B char fetch fetch Instruction lengths: 1 chained 2,3 invalid A-address 4 one operand (B-address = A-address) 5,6 invalid B-address 7 two operands (SWM cannot be longer than 7) 8+ two operands + ignored modifier */ case OP_SWM: /* set word mark */ M[BS] = M[BS] | WM; /* set A field mark */ M[AS] = M[AS] | WM; /* set B field mark */ MM (AS); /* decr pointers */ MM (BS); break; case OP_CWM: /* clear word mark */ M[BS] = M[BS] & ~WM; /* clear A field mark */ M[AS] = M[AS] & ~WM; /* clear B field mark */ MM (AS); /* decr pointers */ MM (BS); break; /* Clear storage instruction A check B check CS clear from B down to nearest hundreds if branch fetch address Instruction lengths: 1 chained 2,3 invalid A-address and B-address 4 one operand (B-address = A-address) 5,6 invalid B-address 7 branch 8+ one operand, branch ignored */ case OP_CS: /* clear storage */ t = (BS / 100) * 100; /* lower bound */ while (BS >= t) /* clear region */ M[BS--] = 0; if (BS < 0) /* wrap if needed */ BS = BS + MEMSIZE; if (ilnt == 7) { /* branch variant? */ BRANCH; } break; /* Modify address instruction A check B check MA add A addr and B addr, store at B addr fetch fetch Instruction lengths: 1 chained 2,3 invalid A-address and B-address 4 self (B-address = A-address) 5,6 invalid B-address 7 normal 8+ normal + ignored modifier */ case OP_MA: /* modify address */ a = one_table[M[AS] & CHAR]; MM (AS); /* get A address */ a = a + ten_table[M[AS] & CHAR]; MM (AS); a = a + hun_table[M[AS] & CHAR]; MM (AS); b = one_table[M[BS] & CHAR]; MM (BS); /* get B address */ b = b + ten_table[M[BS] & CHAR]; MM (BS); b = b + hun_table[M[BS] & CHAR]; MM (BS); t = ((a + b) & INDEXMASK) % MAXMEMSIZE; /* compute sum */ M[BS + 3] = (M[BS + 3] & WM) | store_addr_u (t); M[BS + 2] = (M[BS + 2] & (WM + ZONE)) | store_addr_t (t); M[BS + 1] = (M[BS + 1] & WM) | store_addr_h (t); if (((a % 4000) + (b % 4000)) >= 4000) /* carry? */ BS = BS + 2; break; /* Store address instructions A-check B-check SAR store A* at A addr fetch SBR store B* at A addr fetch Instruction lengths: 1 chained 2,3 invalid A-address 4 normal 5+ B-address overwritten from instruction; invalid address ignored */ case OP_SAR: case OP_SBR: /* store A, B reg */ M[AS] = (M[AS] & WM) | store_addr_u (BS); MM (AS); M[AS] = (M[AS] & WM) | store_addr_t (BS); MM (AS); M[AS] = (M[AS] & WM) | store_addr_h (BS); MM (AS); break; /* NOP - no validity checking, all instructions length ok */ case OP_NOP: /* nop */ break; /* HALT - unless length = 4 (branch), no validity checking; all lengths ok */ case OP_H: /* halt */ if (ilnt == 4) /* set pending branch */ hb_pend = 1; reason = STOP_HALT; /* stop simulator */ saved_IS = IS; /* commit instruction */ break; default: reason = STOP_NXI; /* unimplemented */ break; } /* end switch */ } /* end while */ /* Simulation halted */ as_err = ADDR_ERR (AS); /* get addr err flags */ bs_err = ADDR_ERR (BS); AS = AS & ADDRMASK; /* clean addresses */ BS = BS & ADDRMASK; pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* end sim_instr */ /* store addr_x - convert address to BCD character in x position Inputs: addr = address to convert Outputs: char = converted address character */ int32 store_addr_h (int32 addr) { int32 thous; thous = (addr / 1000) & 03; return bin_to_bcd[(addr % 1000) / 100] | (thous << V_ZONE); } int32 store_addr_t (int32 addr) { return bin_to_bcd[(addr % 100) / 10]; } int32 store_addr_u (int32 addr) { int32 thous; thous = (addr / 1000) & 014; return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2)); } /* div_add - add string for divide */ int32 div_add (int32 ap, int32 bp, int32 aend) { int32 a, b, c, r; c = 0; /* init carry */ do { a = M[ap]; /* get operands */ b = M[bp]; r = bcd_to_bin[b & DIGIT] + /* sum digits + c */ bcd_to_bin[a & DIGIT] + c; c = (r >= 10); /* set carry out */ M[bp] = sum_table[r]; /* store result */ ap--; bp--; } while (ap >= aend); return c; } /* div_sub - substract string for divide */ int32 div_sub (int32 ap, int32 bp, int32 aend) { int32 a, b, c, r; c = 0; /* init borrow */ do { a = M[ap]; /* get operands */ b = M[bp]; r = bcd_to_bin[b & DIGIT] - /* a - b - borrow */ bcd_to_bin[a & DIGIT] - c; c = (r < 0); /* set borrow out */ M[bp] = sum_table[r + 10]; /* store result */ ap--; bp--; } while (ap >= aend); b = M[bp] & CHAR; /* borrow position */ if (b && (b != BCD_ZERO)) { /* non-zero? */ r = bcd_to_bin[b & DIGIT] - c; /* subtract borrow */ M[bp] = sum_table[r]; /* store result */ return 0; /* subtract worked */ } return c; /* return borrow */ } /* div_sign - set signs for divide */ void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp) { int32 sign = dvrc & ZONE; /* divisor sign */ M[rp] = M[rp] | ZONE; /* assume rem pos */ if (sign == BBIT) /* if dvr -, rem - */ M[rp] = M[rp] & ~ABIT; M[qp] = M[qp] | ZONE; /* assume quo + */ if (((dvdc & ZONE) == BBIT) ^ (sign == BBIT)) /* dvr,dvd diff? */ M[qp] = M[qp] & ~ABIT; /* make quo - */ return; } /* iomod - check on I/O modifiers Inputs: ilnt = instruction length mod = modifier character tptr = pointer to table of modifiers, end is -1 Output: status = SCPE_OK if ok, STOP_INVM if invalid */ t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr) { if ((ilnt != 2) && (ilnt != 5) && (ilnt < 8)) return SCPE_OK; if (tptr == NULL) return STOP_INVM; do { if (mod == *tptr++) return SCPE_OK; } while (*tptr >= 0); return STOP_INVM; } /* iodisp - dispatch load or move to I/O routine Inputs: dev = device number unit = unit number flag = move (MD_NORM) vs load (MD_WM) mod = modifier */ t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod) { if (dev == IO_INQ) /* inq terminal? */ return inq_io (flag, mod); if (dev == IO_DP) /* disk pack? */ return dp_io (unit, flag, mod); if (dev == IO_MT) /* magtape? */ return mt_io (unit, flag, mod); if (dev == IO_MTB) /* binary magtape? */ return mt_io (unit, flag | MD_BIN, mod); return STOP_NXD; /* not implemented */ } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int32 i; for (i = 0; i < 64; i++) { /* clr indicators */ if ((i < IN_SSB) || (i > IN_SSG)) /* except SSB-SSG */ ind[i] = 0; } ind[IN_UNC] = 1; /* ind[0] always on */ AS = 0; /* clear AS */ BS = 0; /* clear BS */ as_err = 1; bs_err = 1; D = 0; /* clear D */ hb_pend = 0; /* no halt br */ pcq_r = find_reg ("ISQ", 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] & (WM + CHAR); 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 & (WM + CHAR); return SCPE_OK; } /* Memory size change */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; uint32 i; if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 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; if (MEMSIZE > 4000) cpu_unit.flags = cpu_unit.flags | MA; else cpu_unit.flags = cpu_unit.flags & ~MA; return SCPE_OK; } /* 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].ilnt = 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 i, k, di, lnt; char *cptr = (char *) desc; t_value sim_eval[MAX_L + 1]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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, "IS IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(++di) % hst_lnt]; /* entry pointer */ if (h->ilnt) { /* instruction? */ fprintf (st, "%05d ", h->is); for (i = 0; i < h->ilnt; i++) sim_eval[i] = h->inst[i]; sim_eval[h->ilnt] = WM; if ((fprint_sym (st, h->is, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { fprintf (st, "(undefined)"); for (i = 0; i < h->ilnt; i++) fprintf (st, " %02o", h->inst[i]); } fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } /* Set conversions */ t_stat cpu_set_conv (UNIT *uptr, int32 val, char *cptr, void *desc) { conv_old = val; return SCPE_OK; } /* Show conversions */ t_stat cpu_show_conv (FILE *st, UNIT *uptr, int32 val, void *desc) { if (conv_old) fputs ("Old (pre-3.5-1) conversions\n", st); else fputs ("New conversions\n", st); return SCPE_OK; } simh-3.8.1/I1401/i1401_dp.c0000644000175000017500000006552111112030454012724 0ustar vlmvlm/* i1401_dp.c: IBM 1311 disk simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dp 1311 disk pack 18-Oct-02 RMS Fixed bug in address comparison logic 19-Sep-02 RMS Minor edit for consistency with 1620 15-Jun-02 RMS Reworked address comparison logic The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track. Each sector contains 106 characters of information: 6c sector address 100c sector data By default, a sector's address field will be '000000', which is illegal. This is interpreted to mean the implied sector number that would be in place if the disk pack had been formatted with sequential sector numbers. The sector data can be 100 characters without word marks, or 90 characters with word marks. Load mode transfers 90 characters per sector with word marks, move mode transfers 100 characters per sector without word marks. No attempt is made to catch incompatible writes (eg, load mode write followed by move mode read). */ #include "i1401_defs.h" #define DP_NUMDR 5 /* #drives */ #define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */ #define UNIT_WAE (1 << UNIT_V_WAE) /* Disk format */ #define DP_ADDR 6 /* address */ #define DP_DATA 100 /* data */ #define DP_NUMCH (DP_ADDR + DP_DATA) #define DP_NUMSC 20 /* #sectors */ #define DP_NUMSF 10 /* #surfaces */ #define DP_NUMCY 100 /* #cylinders */ #define DP_TOTSC (DP_NUMCY*DP_NUMSF*DP_NUMSC) #define DP_SIZE (DP_TOTSC*DP_NUMCH) /* Disk control field */ #define DCF_DRV 0 /* drive select */ #define DCF_SEC 1 /* sector addr */ #define DCF_SEC_LEN 6 #define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */ #define DCF_CNT_LEN 3 #define DCF_LEN (DCF_CNT + DCF_CNT_LEN) #define DCF_DIR 1 /* direct seek */ #define DCF_DIR_LEN 4 #define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */ #define DCF_DSEEK 0xB /* Functions */ #define FNC_SEEK 0 /* seek */ #define FNC_CHECK 3 /* check */ #define FNC_READ 1 /* read sectors */ #define FNC_RSCO 5 /* read sec cnt overlay */ #define FNC_RTRK 6 /* read track */ #define FNC_WOFF 10 /* offset for write */ #define FNC_WRITE 11 /* write sectors */ #define FNC_WRSCO 15 /* write sec cnt overlay */ #define FNC_WRTRK 16 /* write track */ #define CYL u3 /* current cylinder */ extern uint8 M[]; /* memory */ extern int32 ind[64]; extern int32 AS, BS, iochk; extern int32 bcd_to_bin[16]; extern int32 bin_to_bcd[16]; extern UNIT cpu_unit; int32 dp_lastf = 0; /* prior function */ int32 dp_time = 0; /* seek time */ t_stat dp_reset (DEVICE *dptr); t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk); t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk); t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg); t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg); int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf); t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf); t_bool dp_zeroad (uint8 *ap); t_bool dp_cmp_ad (uint8 *ap, int32 dcf); int32 dp_trkop (int32 drv, int32 sec); int32 dp_cvt_bcd (int32 ad, int32 len); void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg); int32 dp_get_cnt (int32 dcf); void dp_fill (UNIT *uptr, uint32 da, int32 cnt); /* DP data structures dp_dev DSK device descriptor dp_unit DSK unit list dp_reg DSK register list dp_mod DSK modifier list */ UNIT dp_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } }; REG dp_reg[] = { { FLDATA (ACC, ind[IN_ACC], 0) }, { FLDATA (PWC, ind[IN_DPW], 0) }, { FLDATA (WLR, ind[IN_LNG], 0) }, { FLDATA (UNA, ind[IN_UNA], 0) }, { FLDATA (ERR, ind[IN_DSK], 0) }, { FLDATA (BSY, ind[IN_DBY], 0) }, { DRDATA (LASTF, dp_lastf, 3) }, { DRDATA (TIME, dp_time, 24), PV_LEFT }, { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0, DP_NUMDR, PV_LEFT + REG_RO) }, { NULL } }; MTAB dp_mod[] = { { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL }, { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, { 0 } }; DEVICE dp_dev = { "DP", dp_unit, dp_reg, dp_mod, DP_NUMDR, 10, 21, 1, 8, 7, NULL, NULL, &dp_reset, NULL, NULL, NULL }; /* Disk IO routine Inputs: fnc = function character flg = load vs move mode mod = modifier character Outputs: status = status */ t_stat dp_io (int32 fnc, int32 flg, int32 mod) { int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff; UNIT *uptr; t_stat r; dcf = BS; /* save DCF addr */ qwc = 0; /* not wcheck */ ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */ ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0; if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */ ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */ return SCPE_OK; /* done */ } AS = dcf + 6; /* AS for most ops */ BS = dcf + DCF_CNT - 1; /* minimum DCF */ if (ADDR_ERR (BS)) /* DCF in memory? */ return STOP_WRAP; if (M[dcf] & BBIT) /* impl sel? cyl 8-4-2 */ drv = M[dcf + DCF_SEC + 1] & 0xE; else drv = M[dcf] & DIGIT; /* get drive sel */ if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */ return STOP_INVDSK; drv = bcd_to_bin[drv] >> 1; /* convert */ uptr = dp_dev.units + drv; /* get unit ptr */ if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */ CRETIOE (iochk, SCPE_UNATT); } if ((fnc == FNC_SEEK) && /* seek and */ (M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */ diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */ if (diff < 0) /* error? */ return STOP_INVDSC; diff = diff >> 1; /* diff is *2 */ if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT) diff = -diff; /* get sign */ uptr->CYL = uptr->CYL + diff; /* bound seek */ if (uptr->CYL < 0) uptr->CYL = 0; else if (uptr->CYL >= DP_NUMCY) { /* too big? */ uptr->CYL = 0; /* system hangs */ return STOP_INVDCY; } sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */ return SCPE_OK; /* done! */ } sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */ if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */ return STOP_INVDSC; if (fnc == FNC_SEEK) { /* seek? */ uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */ DP_NUMCY; sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */ return SCPE_OK; /* done! */ } BS = dcf + DCF_LEN; /* full DCF */ if (ADDR_ERR (BS)) /* DCF in memory? */ return STOP_WRAP; cnt = dp_get_cnt (dcf); /* get count */ if (cnt < 0) /* bad count? */ return STOP_INVDCN; if (fnc >= FNC_WOFF) /* invalid func */ return STOP_INVDFN; if (mod == BCD_W) { /* write? */ if (fnc == FNC_CHECK) { /* write check? */ qwc = 1; /* special read */ fnc = dp_lastf; /* use last func */ } else { dp_lastf = fnc; /* save func */ fnc = fnc + FNC_WOFF; /* change to write */ } } else if (mod == BCD_R) /* read? save func */ dp_lastf = fnc; else return STOP_INVM; /* other? error */ switch (fnc) { /* case on function */ case FNC_RSCO: /* read sec cnt ov */ BS = dcf + DCF_CNT; /* set count back */ /* fall thru */ case FNC_READ: /* read */ psec = dp_fndsec (uptr, sec, dcf); /* find sector */ if (psec < 0) /* addr cmp error? */ CRETIOE (iochk, STOP_INVDAD); for (;;) { /* loop */ qzr = (--cnt == 0); /* set zero latch */ dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */ break; cnt = dp_get_cnt (dcf); /* get new count */ if (cnt < 0) /* bad count? */ return STOP_INVDCN; if (qzr) /* zero latch? done */ break; sec++; psec++; /* next sector */ dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */ if (r = dp_nexsec (uptr, psec, dcf)) /* find next */ break; } break; /* done, clean up */ case FNC_RTRK: /* read track */ AS = dcf + 9; /* special AS */ psec = dp_trkop (drv, sec); /* start of track */ for (;;) { /* loop */ qzr = (--cnt == 0); /* set zero latch */ dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */ break; /* error? */ if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */ break; /* error? */ cnt = dp_get_cnt (dcf); /* get new count */ if (cnt < 0) /* bad count? */ return STOP_INVDCN; if (qzr) /* zero latch? done */ break; psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); } break; /* done, clean up */ case FNC_WRSCO: /* write sec cnt ov */ BS = dcf + DCF_CNT; /* set count back */ /* fall through */ case FNC_WRITE: /* read */ psec = dp_fndsec (uptr, sec, dcf); /* find sector */ if (psec < 0) /* addr cmp error? */ CRETIOE (iochk, STOP_INVDAD); for (;;) { /* loop */ qzr = (--cnt == 0); /* set zero latch */ dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */ if (r = dp_wrsec (uptr, psec, flg)) /* write data */ break; if (qzr) /* zero latch? done */ break; sec++; psec++; /* next sector */ dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */ if (r = dp_nexsec (uptr, psec, dcf)) /* find next */ break; } break; /* done, clean up */ case FNC_WRTRK: /* write track */ if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */ return STOP_WRADIS; AS = dcf + 9; /* special AS */ psec = dp_trkop (drv, sec); /* start of track */ for (;;) { /* loop */ qzr = (--cnt == 0); /* set zero latch */ dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ if (r = dp_wradr (uptr, psec, flg)) /* write addr */ break; if (r = dp_wrsec (uptr, psec, flg)) /* write data */ break; if (qzr) /* zero latch? done */ break; psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); } break; /* done, clean up */ default: /* unknown */ return STOP_INVDFN; } if (r == SCPE_OK) { /* normal so far? */ BS++; /* advance BS */ if (ADDR_ERR (BS)) /* address error? */ return STOP_WRAP; if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */ ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */ r = STOP_INVDLN; } } CRETIOE (iochk || !ind[IN_DSK], r); /* return status */ } /* Read or compare address with memory */ t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc) { int32 i; uint8 ac; int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ t_bool zad = dp_zeroad (ap); /* zero address */ static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */ 100000, 10000, 1000, 100, 10, 1 } ; for (i = 0; i < DP_ADDR; i++) { /* copy address */ if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ return STOP_INVDLN; } if (zad) { /* addr zero? */ ac = sec / dec_tab[i]; /* get addr digit */ sec = sec % dec_tab[i]; /* get remainder */ ac = bcd_to_bin[ac]; /* cvt to BCD */ } else ac = *ap; /* addr char */ if (qwc) { /* wr chk? skip if zad */ if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */ ((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */ ind[IN_DPW] = ind[IN_DSK] = 1; return STOP_WRCHKE; } } else if (flg) /* load mode */ M[BS] = ac & CHAR; else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */ ap++; BS++; /* adv ptrs */ if (ADDR_ERR (BS)) return STOP_WRAP; } return SCPE_OK; } /* Read or compare data with memory */ t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc) { int32 i, lim; int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */ lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */ for (i = 0; i < lim; i++) { /* copy data */ if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ return STOP_INVDLN; } if (qwc) { /* write check? */ if (flg? (M[BS] != *ap): /* load mode cmp */ ((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */ ind[IN_DPW] = ind[IN_DSK] = 1; /* error */ return STOP_WRCHKE; } } else if (flg) /* load mode */ M[BS] = *ap & (WM | CHAR); else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */ ap++; /* adv ptrs */ BS++; if (ADDR_ERR (BS)) return STOP_WRAP; } return SCPE_OK; } /* Write address to disk */ t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg) { int32 i; uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ for (i = 0; i < DP_ADDR; i++) { /* copy address */ if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */ ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ return STOP_INVDLN; } if (flg) /* L? copy WM */ *ap = M[BS] & (WM | CHAR); else *ap = M[BS] & CHAR; /* M? strip WM */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; da++; /* adv ptrs */ ap++; BS++; if (ADDR_ERR (BS)) return STOP_WRAP; } return SCPE_OK; } /* Write data to disk */ t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg) { int32 i, lim; uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */ for (i = 0; i < lim; i++) { /* copy data */ if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ dp_fill (uptr, da, DP_DATA - i); /* fill, set err */ ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ return STOP_INVDLN; } if (flg) /* load, copy WM */ *ap = M[BS] & (WM | CHAR); else *ap = M[BS] & CHAR; /* move, strip WM */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; da++; /* adv ptrs */ ap++; BS++; if (ADDR_ERR (BS)) return STOP_WRAP; } return SCPE_OK; } /* Find sector */ int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf) { int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk; int32 da = psec * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ int32 i; if (dp_zeroad (ap)) /* addr zero? ok */ return psec; if (dp_cmp_ad (ap, dcf)) /* addr comp? ok */ return psec; psec = psec - (psec % DP_NUMSC); /* sector 0 */ for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */ da = psec * DP_NUMCH; /* char number */ ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */ if (dp_zeroad (ap)) /* no implicit match */ continue; if (dp_cmp_ad (ap, dcf)) /* match? */ return psec; } ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */ return -1; } /* Find next sector - must be sequential, cannot cross cylinder boundary */ t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf) { int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ int32 da = psec * DP_NUMCH; /* word number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ if (ctrk) { /* not trk zero? */ if (dp_zeroad (ap)) /* addr zero? ok */ return SCPE_OK; if (dp_cmp_ad (ap, dcf)) /* addr comp? ok */ return SCPE_OK; } ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */ return STOP_INVDAD; } /* Test for zero address */ t_bool dp_zeroad (uint8 *ap) { int32 i; for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ if (*ap & CHAR) /* nonzero? lose */ return FALSE; } return TRUE; /* all zeroes */ } /* Compare disk address to memory sector address - always omit word marks */ t_bool dp_cmp_ad (uint8 *ap, int32 dcf) { int32 i; uint8 c; for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ c = M[dcf + DCF_SEC + i]; /* sector addr char */ if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */ return FALSE; } return TRUE; /* compare ok */ } /* Track operation setup */ int32 dp_trkop (int32 drv, int32 sec) { int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF; return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) + (ctrk * DP_NUMSC)); } /* Convert DCF BCD field to binary */ int32 dp_cvt_bcd (int32 ad, int32 len) { uint8 c; int32 r; for (r = 0; len > 0; len--) { /* loop thru char */ c = M[ad] & DIGIT; /* get digit */ if ((c == 0) || (c > BCD_ZERO)) /* invalid? */ return -1; r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */ ad++; /* next digit */ } return r; } /* Convert binary to DCF BCD field */ void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg) { int32 r; for ( ; len > 0; len--) { /* loop thru char */ r = val % 10; /* get digit */ if (flg) /* load mode? */ M[ad + len - 1] = bin_to_bcd[r]; else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r]; val = val / 10; } return; } /* Get and validate count */ int32 dp_get_cnt (int32 dcf) { int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */ if (cnt < 0) /* bad count? */ return -1; if (cnt == 0) /* 0 => 1000 */ return 1000; return cnt; } /* Fill sector buffer with blanks */ void dp_fill (UNIT *uptr, uint32 da, int32 cnt) { while (cnt-- > 0) { /* fill with blanks */ *(((uint8 *) uptr->filebuf) + da) = BCD_BLANK; if (da >= uptr->hwmark) uptr->hwmark = da + 1; da++; } return; } /* Reset routine */ t_stat dp_reset (DEVICE *dptr) { int32 i; for (i = 0; i < DP_NUMDR; i++) /* reset cylinder */ dp_unit[i].CYL = 0; dp_lastf = 0; /* clear state */ ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */ ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0; sim_cancel (&dp_unit[0]); /* cancel timer */ return SCPE_OK; } simh-3.8.1/I1401/i1401_sys.c0000644000175000017500000003625411112030454013140 0ustar vlmvlm/* i1401_sys.c: IBM 1401 simulator interface Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 20-Sep-05 RMS Revised for new code tables 04-Jan-05 WVS Added address argument support 14-Nov-04 WVS Added data printout support 16-Mar-03 RMS Fixed mnemonic for MCS 03-Jun-02 RMS Added 1311 support 18-May-02 RMS Added -D feature from Van Snyder 26-Jan-02 RMS Fixed H, NOP with no trailing wm (found by Van Snyder) 17-Sep-01 RMS Removed multiconsole support 13-Jul-01 RMS Fixed bug in symbolic output (found by Peter Schorn) 27-May-01 RMS Added multiconsole support 14-Mar-01 RMS Revised load/dump interface (again) 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface */ #include "i1401_defs.h" #include #define LINE_LNT 80 extern DEVICE cpu_dev, inq_dev, lpt_dev; extern DEVICE cdr_dev, cdp_dev, stack_dev; extern DEVICE dp_dev, mt_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint8 M[]; extern char ascii_to_bcd_old[128], ascii_to_bcd[128]; extern char bcd_to_ascii_old[64], bcd_to_ascii_a[64], bcd_to_ascii_h[64]; extern char *get_glyph (char *cptr, char *gbuf, char term); extern int32 store_addr_h (int32 addr); extern int32 store_addr_t (int32 addr); extern int32 store_addr_u (int32 addr); extern t_bool conv_old; /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "IBM 1401"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = LINE_LNT; DEVICE *sim_devices[] = { &cpu_dev, &inq_dev, &cdr_dev, &cdp_dev, &stack_dev, &lpt_dev, &mt_dev, &dp_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unimplemented instruction", "Non-existent memory", "Non-existent device", "No WM at instruction start", "Invalid A address", "Invalid B address", "Invalid instruction length", "Invalid modifer", "Invalid branch address", "Breakpoint", "HALT instruction", "Invalid MT unit number", "Invalid MT record length", "Write to locked MT unit", "Skip to unpunched CCT channel", "Card reader empty", "Address register wrap", "I/O check", "Invalid disk sector address", "Invalid disk sector count", "Invalid disk unit", "Invalid disk function", "Invalid disk record length", "Write track while disabled", "Write check error", "Disk address miscompare", "Direct seek cylinder exceeds maximum" }; /* Binary loader -- load carriage control tape A carriage control tape consists of entries of the form (repeat count) column number,column number,column number,... The CCT entries are stored in cct[0:lnt-1], cctlnt contains the number of entries */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; t_stat r; extern int32 cctlnt, cctptr, cct[CCT_LNT]; char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; ptr = 0; for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ mask = 0; if (*cptr == '(') { /* repeat count? */ cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ if (r != SCPE_OK) return SCPE_FMT; } else rpt = 1; while (*cptr != 0) { /* get col no's */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ col = get_uint (gbuf, 10, 12, &r); /* column number */ if (r != SCPE_OK) return SCPE_FMT; mask = mask | (1 << col); /* set bit */ } for ( ; rpt > 0; rpt--) { /* store vals */ if (ptr >= CCT_LNT) return SCPE_FMT; cctbuf[ptr++] = mask; } } if (ptr == 0) return SCPE_FMT; cctlnt = ptr; cctptr = 0; for (rpt = 0; rpt < cctlnt; rpt++) cct[rpt] = cctbuf[rpt]; return SCPE_OK; } /* Symbol table */ const char *opcode[64] = { NULL, "R", "W", "WR", "P", "RP", "WP", "WRP", "SRF", "SPF", NULL, "MA", "MUL", NULL, NULL, NULL, NULL, "CS", "S", NULL, "MTF", "BWZ", "BBE", NULL, "MZ", "MCS", NULL, "SWM", "DIV", NULL, NULL, NULL, NULL, NULL, "SS", "LCA", "MCW", "NOP", NULL, "MCM", "SAR", NULL, "ZS", NULL, NULL, NULL, NULL, NULL, NULL, "A", "B", "C", "MN", "MCE", "CC", NULL, "SBR", NULL, "ZA", "H", "CWM", NULL, NULL, NULL }; /* Print an address from three characters */ void fprint_addr (FILE *of, t_value *dig) { int32 addr, xa; extern int32 hun_table[64], ten_table[64], one_table[64]; addr = hun_table[dig[0] & CHAR] + ten_table[dig[1]] + one_table[dig[2]]; xa = (addr >> V_INDEX) & M_INDEX; if (xa) fprintf (of, " %d,%d", addr & ADDRMASK, ((xa - (X1 >> V_INDEX)) / 5) + 1); else if (addr >= MAXMEMSIZE) fprintf (of, " %d*", addr & ADDRMASK); else fprintf (of, " %d", addr); return; } /* Print unknown opcode as data */ t_stat dcw (FILE *of, int32 op, t_value *val, int32 sw) { int32 i; t_bool use_h = sw & SWMASK ('F'); fprintf (of, "DCW @%c", bcd2ascii (op, use_h)); /* assume it's data */ for (i = 1; i < sim_emax; i++) { if (val[i] & WM) break; fprintf (of, "%c", bcd2ascii (val[i], use_h)); } fprintf (of, "@"); return -(i - 1); /* return # chars */ } /* Symbolic decode Inputs: *of = output stream addr = current address *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra words retired */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 op, flags, ilnt, i, t; int32 wmch = conv_old? '~': '`'; t_bool use_h = sw & SWMASK ('F'); extern int32 op_table[64], len_table[9]; if (sw & SWMASK ('C')) { /* character? */ t = val[0]; if (uptr->flags & UNIT_BCD) { if (t & WM) fputc (wmch, of); fputc (bcd2ascii (t & CHAR, use_h), of); } else fprintf (of, FMTASC (t & 0177)); return SCPE_OK; } if ((uptr != NULL) && (uptr != &cpu_unit)) /* CPU? */ return SCPE_ARG; if (sw & SWMASK ('D')) { /* dump? */ for (i = 0; i < 50; i++) fprintf (of, "%c", bcd2ascii (val[i] & CHAR, use_h)) ; fprintf (of, "\n\t"); for (i = 0; i < 50; i++) fprintf (of, (val[i] & WM)? "1": " ") ; return -(i - 1); } if (sw & SWMASK ('S')) { /* string? */ i = 0; do { t = val[i++]; if (t & WM) fputc (wmch, of); fputc (bcd2ascii (t & CHAR, use_h), of); } while ((i < LINE_LNT) && ((val[i] & WM) == 0)); return -(i - 1); } if ((sw & SWMASK ('M')) == 0) return SCPE_ARG; if ((val[0] & WM) == 0) /* WM under op? */ return STOP_NOWM; op = val[0]& CHAR; /* isolate op */ if (opcode[op] == NULL) /* invalid op */ return dcw (of, op, val, sw); flags = op_table[op]; /* get flags */ for (ilnt = 1; ilnt < sim_emax; ilnt++) { /* find inst lnt */ if (val[ilnt] & WM) break; } if ((flags & (NOWM | HNOP)) && (ilnt > 7)) /* cs, swm, h, nop? */ ilnt = 7; else if ((op == OP_B) && (ilnt > 4) && (val[4] == BCD_BLANK)) ilnt = 4; else if ((ilnt > 8) && (op != OP_NOP)) /* cap length */ ilnt = 8; if (ilnt == 3) { /* lnt = 3? */ fprintf (of, "DSA"); /* assume DSA */ fprint_addr (of, val); /* print addr */ return -(ilnt - 1); } if ((((flags & len_table[ilnt]) == 0) && /* invalid lnt, */ (op != OP_NOP)) || /* not nop? */ (opcode[op] == NULL)) /* or undef? */ return dcw (of, op, val, sw); fprintf (of, "%s",opcode[op]); /* print opcode */ if (ilnt > 2) { /* A address? */ if (((flags & IO) || (op == OP_NOP)) && (val[1] == BCD_PERCNT)) fprintf (of, " %%%c%c", bcd2ascii (val[2], use_h), bcd2ascii (val[3], sw)); else fprint_addr (of, &val[1]); } if (ilnt > 5) /* B address? */ fprint_addr (of, &val[4]); if ((ilnt == 2) || (ilnt == 5) || (ilnt == 8)) /* d character? */ fprintf (of, " '%c", bcd2ascii (val[ilnt - 1], use_h)); return -(ilnt - 1); /* return # chars */ } /* get_addr - get address + index pair */ t_stat get_addr (char *cptr, t_value *val) { int32 addr, index; t_stat r; char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, ','); /* get address */ addr = get_uint (gbuf, 10, MAXMEMSIZE, &r); if (r != SCPE_OK) return SCPE_ARG; if (*cptr != 0) { /* more? */ cptr = get_glyph (cptr, gbuf, ' '); index = get_uint (gbuf, 10, 3, &r); if ((r != SCPE_OK) || (index == 0)) return SCPE_ARG; } else index = 0; if (*cptr != 0) return SCPE_ARG; val[0] = store_addr_h (addr); val[1] = store_addr_t (addr) | (index << V_ZONE); val[2] = store_addr_u (addr); return SCPE_OK; } /* get_io - get I/O address */ t_stat get_io (char *cptr, t_value *val) { if ((cptr[0] != '%') || (cptr[3] != 0) || !isalnum (cptr[1]) || !isalnum (cptr[2])) return SCPE_ARG; val[0] = BCD_PERCNT; val[1] = ascii2bcd (cptr[1]); val[2] = ascii2bcd (cptr[2]); return SCPE_OK; } /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 i, op, ilnt, t, cflag, wm_seen; int32 wmch = conv_old? '~': '`'; extern int32 op_table[64], len_table[9]; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('C')) || (sw & SWMASK ('S')) || (*cptr == wmch) || ((*cptr == '\'') && cptr++) || ((*cptr == '"') && cptr++)) { wm_seen = 0; for (i = 0; (i < sim_emax) && (*cptr != 0); ) { t = *cptr++; /* get character */ if (cflag && (wm_seen == 0) && (t == wmch)) wm_seen = WM; else if (uptr->flags & UNIT_BCD) { if (t < 040) return SCPE_ARG; val[i++] = ascii2bcd (t) | wm_seen; wm_seen = 0; } else val[i++] = t; } if ((i == 0) || wm_seen) return SCPE_ARG; return -(i - 1); } if (cflag == 0) /* CPU only */ return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ for (op = 0; op < 64; op++) { /* look it up */ if (opcode[op] && strcmp (gbuf, opcode[op]) == 0) break; } if (op >= 64) /* successful? */ return SCPE_ARG; val[0] = op | WM; /* store opcode */ cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */ if (((op_table[op] && IO) && (get_io (gbuf, &val[1]) == SCPE_OK)) || (get_addr (gbuf, &val[1]) == SCPE_OK)) { cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */ if (get_addr (gbuf, &val[4]) == SCPE_OK) { cptr = get_glyph (cptr, gbuf, ','); /* get d */ ilnt = 7; /* a and b addresses */ } else ilnt = 4; /* a address */ } else ilnt = 1; /* no addresses */ if ((gbuf[0] == '\'') || (gbuf[0] == '"')) { /* d character? */ t = gbuf[1]; if ((gbuf[2] != 0) || (*cptr != 0) || (t < 040)) return SCPE_ARG; /* end and legal? */ val[ilnt] = ascii2bcd (t); /* save D char */ ilnt = ilnt + 1; } else if (gbuf[0] != 0) /* not done? */ return SCPE_ARG; if ((op_table[op] & len_table[ilnt]) == 0) return STOP_INVL; return -(ilnt - 1); } /* Convert BCD to ASCII */ int32 bcd2ascii (int32 c, t_bool use_h) { if (conv_old) return bcd_to_ascii_old[c]; else if (use_h) return bcd_to_ascii_h[c]; else return bcd_to_ascii_a[c]; } /* Convert ASCII to BCD */ int32 ascii2bcd (int32 c) { if (conv_old) return ascii_to_bcd_old[c]; else return ascii_to_bcd[c]; } simh-3.8.1/I1401/i1401_defs.h0000644000175000017500000003676611035720334013267 0ustar vlmvlm/* i1401_defs.h: IBM 1401 simulator definitions Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 11-Jul-08 RMS Added IO mode flag for boot (from Bob Abeles) 28-Jun-07 RMS Defined character code for tape mark 14-Nov-04 RMS Added column binary support 27-Oct-04 RMS Added maximum instruction length 16-Mar-03 RMS Fixed mnemonic for MCS 03-Jun-02 RMS Added 1311 support 14-Apr-99 RMS Converted t_addr to unsigned This simulator is based on the 1401 simulator written by Len Fehskens with assistance from Sarah Lee Harris and Bob Supnik. This one's for you, Len. I am grateful to Paul Pierce and Charles Owen for their help in answering questions, gathering source material, and debugging. */ #ifndef _I1401_DEFS_H_ #define _I1401_DEFS_H_ 0 #include "sim_defs.h" /* Simulator stop codes */ #define STOP_NXI 1 /* unimpl instr */ #define STOP_NXM 2 /* non-exist mem */ #define STOP_NXD 3 /* non-exist dev */ #define STOP_NOWM 4 /* no WM under op */ #define STOP_INVA 5 /* invalid A addr */ #define STOP_INVB 6 /* invalid B addr */ #define STOP_INVL 7 /* invalid length */ #define STOP_INVM 8 /* invalid modifier */ #define STOP_INVBR 9 /* invalid branch */ #define STOP_IBKPT 10 /* breakpoint */ #define STOP_HALT 11 /* halt */ #define STOP_INVMTU 12 /* invalid MT unit */ #define STOP_MTZ 13 /* MT zero lnt rec */ #define STOP_MTL 14 /* MT write lock */ #define STOP_CCT 15 /* inv CCT channel */ #define STOP_NOCD 16 /* no cards left */ #define STOP_WRAP 17 /* AS, BS mem wrap */ #define STOP_IOC 18 /* I/O check */ #define STOP_INVDSC 19 /* invalid disk sector */ #define STOP_INVDCN 20 /* invalid disk count */ #define STOP_INVDSK 21 /* invalid disk unit */ #define STOP_INVDFN 22 /* invalid disk func */ #define STOP_INVDLN 23 /* invalid disk reclen */ #define STOP_WRADIS 24 /* write address dis */ #define STOP_WRCHKE 25 /* write check error */ #define STOP_INVDAD 26 /* invalid disk addr */ #define STOP_INVDCY 27 /* invalid direct seek */ /* Memory and devices */ #define MAXMEMSIZE 16000 /* max memory */ #define MEMSIZE (cpu_unit.capac) /* current memory */ #define CDR_BUF 1 /* card rdr buffer */ #define CDR_WIDTH 80 /* card rdr width */ #define CDP_BUF 101 /* card punch buffer */ #define CDP_WIDTH 80 /* card punch width */ #define CD_CBUF1 401 /* r/p col bin buf 12-3 */ #define CD_CBUF2 501 /* r/p col bin buf 4-9 */ #define LPT_BUF 201 /* line print buffer */ #define LPT_WIDTH 132 /* line print width */ #define CCT_LNT 132 /* car ctrl length */ #define INQ_WIDTH 80 /* inq term width */ #define ADDR_ERR(x) (((uint32) (x)) >= MEMSIZE) /* Binary address format <14:0> address, with index added in <23:16> index register memory address <25:24> address error bits */ #define ADDRMASK 037777 /* addr mask */ #define INDEXMASK 077777 /* addr + index mask */ #define V_INDEX 16 #define M_INDEX 0177 #define V_ADDRERR 24 #define BA (1 << V_ADDRERR) /* bad addr digit */ #define X1 (87 << V_INDEX) /* index reg 1 */ #define X2 (92 << V_INDEX) /* index reg 2 */ #define X3 (97 << V_INDEX) /* index reg 3 */ /* CPU instruction control flags. The flag definitions must be harmonized with the UNIT flag definitions used by the simulator. */ /* Lengths */ #define L1 0001 /* 1: op */ #define L2 0002 /* 2: op d */ #define L4 0004 /* 4: op aaa */ #define L5 0010 /* 5: op aaa d */ #define L7 0020 /* 7: op aaa bbb */ #define L8 0040 /* 8: op aaa bbb d */ #define MAX_L 8 /* max length */ /* CPU options, stored in cpu_unit.flags */ #define MDV (1 << (UNIT_V_UF + 0)) /* multiply/divide */ #define MR (1 << (UNIT_V_UF + 1)) /* move record */ #define XSA (1 << (UNIT_V_UF + 2)) /* index, store addr */ #define EPE (1 << (UNIT_V_UF + 3)) /* expanded edit */ #define MA (1 << (UNIT_V_UF + 4)) /* modify address */ #define BBE (1 << (UNIT_V_UF + 5)) /* br bit equal */ #define HLE (1 << (UNIT_V_UF + 6)) /* high/low/equal */ #define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* fake flag */ #define ALLOPT (MDV + MR + XSA + EPE + MA + BBE + HLE) #define STDOPT (MDV + MR + XSA + EPE + MA + BBE + HLE) /* Fetch control */ #define AREQ (1 << (UNIT_V_UF + 8)) /* validate A */ #define BREQ (1 << (UNIT_V_UF + 9)) /* validate B */ #define MLS (1 << (UNIT_V_UF + 10)) /* move load store */ #define NOWM (1 << (UNIT_V_UF + 11)) /* no WM at end */ #define HNOP (1 << (UNIT_V_UF + 12)) /* halt or nop */ #define IO (1 << (UNIT_V_UF + 13)) /* IO */ #define UNIT_BCD (1 << (UNIT_V_UF + 14)) /* BCD strings */ #if (UNIT_V_UF < 6) || ((UNIT_V_UF + 14) > 31) Definition error: flags overlap #endif /* BCD memory character format */ #define WM 0100 /* word mark */ #define ZONE 0060 /* zone */ #define BBIT 0040 /* 1 in valid sign */ #define ABIT 0020 /* sign (1 = +) */ #define DIGIT 0017 /* digit */ #define CHAR 0077 /* character */ #define V_WM 6 #define V_ZONE 4 #define V_DIGIT 0 /* Interesting BCD characters */ #define BCD_BLANK 000 #define BCD_ONE 001 #define BCD_TWO 002 #define BCD_THREE 003 #define BCD_FOUR 004 #define BCD_FIVE 005 #define BCD_SIX 006 #define BCD_SEVEN 007 #define BCD_EIGHT 010 #define BCD_NINE 011 #define BCD_ZERO 012 #define BCD_TAPMRK 017 #define BCD_ALT 020 #define BCD_S 022 #define BCD_U 024 #define BCD_W 026 #define BCD_RECMRK 032 #define BCD_COMMA 033 #define BCD_PERCNT 034 #define BCD_WM 035 #define BCD_BS 036 #define BCD_TS 037 #define BCD_MINUS 040 #define BCD_M 044 #define BCD_R 051 #define BCD_DOLLAR 053 #define BCD_ASTER 054 #define BCD_AMPER 060 #define BCD_A 061 #define BCD_B 062 #define BCD_C 063 #define BCD_E 065 #define BCD_DECIMAL 073 #define BCD_SQUARE 074 #define BCD_GRPMRK 077 /* Opcodes */ #define OP_R 001 /* read */ #define OP_W 002 /* write */ #define OP_WR 003 /* write and read */ #define OP_P 004 /* punch */ #define OP_RP 005 /* read and punch */ #define OP_WP 006 /* write and punch */ #define OP_WRP 007 /* write read punch */ #define OP_RF 010 /* reader feed */ #define OP_PF 011 /* punch feed */ #define OP_MA 013 /* modify address */ #define OP_MUL 014 /* multiply */ #define OP_CS 021 /* clear storage */ #define OP_S 022 /* subtract */ #define OP_MTF 024 /* magtape function */ #define OP_BWZ 025 /* branch wm or zone */ #define OP_BBE 026 /* branch bit equal */ #define OP_MZ 030 /* move zone */ #define OP_MCS 031 /* move suppr zeroes */ #define OP_SWM 033 /* set word mark */ #define OP_DIV 034 /* divide */ #define OP_SS 042 /* select stacker */ #define OP_LCA 043 /* load characters */ #define OP_MCW 044 /* move characters */ #define OP_NOP 045 /* no op */ #define OP_MCM 047 /* move to rec/grp mk */ #define OP_SAR 050 /* store A register */ #define OP_ZS 052 /* zero and subtract */ #define OP_A 061 /* add */ #define OP_B 062 /* branch */ #define OP_C 063 /* compare */ #define OP_MN 064 /* move numeric */ #define OP_MCE 065 /* move char and edit */ #define OP_CC 066 /* carriage control */ #define OP_SBR 070 /* store B register */ #define OP_ZA 072 /* zero and add */ #define OP_H 073 /* halt */ #define OP_CWM 074 /* clear word mark */ /* I/O addresses */ #define IO_INQ 023 /* inquiry terminal */ #define IO_MT 024 /* magtape */ #define IO_MTB 062 /* binary magtape */ #define IO_DP 066 /* 1311 diskpack */ /* I/O modes */ #define MD_NORM 0 /* normal (move) */ #define MD_WM 1 /* word mark (load) */ #define MD_BIN 2 /* binary */ #define MD_BOOT 4 /* boot read */ /* Indicator characters */ #define IN_UNC 000 /* unconditional */ #define IN_CC9 011 /* carr ctrl chan 9 */ #define IN_CC12 014 /* carr ctrl chan 12 */ #define IN_UNQ 021 /* unequal */ #define IN_EQU 022 /* equal */ #define IN_LOW 023 /* low */ #define IN_HGH 024 /* high */ #define IN_DPW 025 /* parity/compare check */ #define IN_LNG 026 /* wrong lnt record */ #define IN_UNA 027 /* unequal addr cmp */ #define IN_DSK 030 /* disk error */ #define IN_OVF 031 /* overflow */ #define IN_LPT 032 /* printer error */ #define IN_PRO 034 /* process check */ #define IN_DBY 036 /* disk busy */ #define IN_END 042 /* end indicator */ #define IN_TAP 043 /* tape error */ #define IN_ACC 045 /* access error */ #define IN_BSY 047 /* printer busy */ #define IN_INR 050 /* inquiry request */ #define IN_PCB 051 /* printer carr busy */ #define IN_PNCH 052 /* punch error */ #define IN_INC 054 /* inquiry clear */ #define IN_LST 061 /* last card */ #define IN_SSB 062 /* sense switch B */ #define IN_SSC 063 /* sense switch C */ #define IN_SSD 064 /* sense switch D */ #define IN_SSE 065 /* sense switch E */ #define IN_SSF 066 /* sense switch F */ #define IN_SSG 067 /* sense switch G */ #define IN_READ 072 /* reader error */ #define CRETIOE(f,c) return ((f)? (c): SCPE_OK) /* Function prototypes */ int32 bcd2ascii (int32 c, t_bool use_h); int32 ascii2bcd (int32 c); #endif simh-3.8.1/I1401/i1401_cd.c0000644000175000017500000003352511112030454012706 0ustar vlmvlm/* i1401_cd.c: IBM 1402 card reader/punch Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cdr card reader cdp card punch stack stackers (5 units) 0 normal 1 1 2 2/8 3 unused 4 4 Cards are represented as ASCII text streams terminated by newlines. This allows cards to be created and edited as normal files. 28-Jun-07 RMS Added support for SS overlap modifiers 19-Jan-07 RMS Added UNIT_TEXT flag 20-Sep-05 RMS Revised for new code tables, compatible colbinary treatment 30-Aug-05 RMS Fixed read, punch to ignore modifier on 1,4 char inst (reported by Van Snyder) 14-Nov-04 WVS Added column binary support 25-Apr-03 RMS Revised for extended file support 30-May-02 RMS Widened POS to 32b 30-Jan-02 RMS New zero footprint card bootstrap from Van Snyder 29-Nov-01 RMS Added read only unit support 13-Apr-01 RMS Revised for register arrays */ #include "i1401_defs.h" #include #define UNIT_V_PCH (UNIT_V_UF + 0) /* output conv */ #define UNIT_PCH (1 << UNIT_V_PCH) extern uint8 M[]; extern int32 ind[64], ssa, iochk; extern int32 conv_old; int32 s1sel, s2sel, s4sel, s8sel; char rbuf[2 * CBUFSIZE]; /* > CDR_WIDTH */ t_stat cdr_svc (UNIT *uptr); t_stat cdr_boot (int32 unitno, DEVICE *dptr); t_stat cdr_attach (UNIT *uptr, char *cptr); t_stat cd_reset (DEVICE *dptr); int32 bcd2asc (int32 c, UNIT *uptr); char colbin_to_bcd (uint32 cb); /* Card reader data structures cdr_dev CDR descriptor cdr_unit CDR unit descriptor cdr_reg CDR register list */ UNIT cdr_unit = { UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0), 100 }; REG cdr_reg[] = { { FLDATA (LAST, ind[IN_LST], 0) }, { FLDATA (ERR, ind[IN_READ], 0) }, { FLDATA (S1, s1sel, 0) }, { FLDATA (S2, s2sel, 0) }, { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, { NULL } }; DEVICE cdr_dev = { "CDR", &cdr_unit, cdr_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, &cdr_boot, &cdr_attach, NULL }; /* CDP data structures cdp_dev CDP device descriptor cdp_unit CDP unit descriptor cdp_reg CDP register list */ UNIT cdp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG cdp_reg[] = { { FLDATA (ERR, ind[IN_PNCH], 0) }, { FLDATA (S4, s4sel, 0) }, { FLDATA (S8, s8sel, 0) }, { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; MTAB cdp_mod[] = { { UNIT_PCH, 0, "business set", "BUSINESS" }, { UNIT_PCH, UNIT_PCH, "Fortran set", "FORTRAN" }, { 0 } }; DEVICE cdp_dev = { "CDP", &cdp_unit, cdp_reg, cdp_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, NULL, NULL, NULL }; /* Stacker data structures stack_dev STACK device descriptor stack_unit STACK unit descriptors stack_reg STACK register list */ UNIT stack_unit[] = { { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) } }; REG stack_reg[] = { { DRDATA (POS0, stack_unit[0].pos, T_ADDR_W), PV_LEFT }, { DRDATA (POS1, stack_unit[1].pos, T_ADDR_W), PV_LEFT }, { DRDATA (POS28, stack_unit[2].pos, T_ADDR_W), PV_LEFT }, { DRDATA (POS4, stack_unit[4].pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE stack_dev = { "STKR", stack_unit, stack_reg, cdp_mod, 5, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, NULL, NULL, NULL }; /* Card read routine Modifiers have been checked by the caller C modifier is recognized (column binary is implemented) */ t_stat read_card (int32 ilnt, int32 mod) { int32 i, cbn, c1, c2; t_stat r; if (sim_is_active (&cdr_unit)) { /* busy? */ sim_cancel (&cdr_unit); /* cancel */ if (r = cdr_svc (&cdr_unit)) /* process */ return r; } if ((cdr_unit.flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; ind[IN_READ] = ind[IN_LST] = s1sel = s2sel = 0; /* default stacker */ cbn = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_C); /* col binary? */ for (i = 0; i < 2 * CBUFSIZE; i++) /* clear extended buf */ rbuf[i] = 0; fgets (rbuf, (cbn)? 2 * CBUFSIZE: CBUFSIZE, /* rd bin/char card */ cdr_unit.fileref); if (feof (cdr_unit.fileref)) /* eof? */ return STOP_NOCD; if (ferror (cdr_unit.fileref)) { /* error? */ ind[IN_READ] = 1; perror ("Card reader I/O error"); clearerr (cdr_unit.fileref); if (iochk) return SCPE_IOERR; return SCPE_OK; } cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ if (ssa) { /* if last cd on */ getc (cdr_unit.fileref); /* see if more */ if (feof (cdr_unit.fileref)) /* eof? set flag */ ind[IN_LST] = 1; fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); } if (cbn) { /* column binary */ for (i = 0; i < CDR_WIDTH; i++) { if (conv_old) { c1 = ascii2bcd (rbuf[i]); c2 = ascii2bcd (rbuf[CDR_WIDTH + i]); } else { c1 = ascii2bcd (rbuf[2 * i]); c2 = ascii2bcd (rbuf[(2 * i) + 1]); } M[CD_CBUF1 + i] = (M[CD_CBUF1 + i] & WM) | c1; M[CD_CBUF2 + i] = (M[CD_CBUF2 + i] & WM) | c2; M[CDR_BUF + i] = colbin_to_bcd ((c1 << 6) | c2); } /* end for i */ } /* end if col bin */ else { /* normal read */ for (i = 0; i < CDR_WIDTH; i++) { /* cvt to BCD */ rbuf[i] = ascii2bcd (rbuf[i]); M[CDR_BUF + i] = (M[CDR_BUF + i] & WM) | rbuf[i]; } } M[CDR_BUF - 1] = 060; /* mem mark */ sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ return SCPE_OK; } /* Card reader service. If a stacker select is active, copy to the selected stacker. Otherwise, copy to the normal stacker. If the unit is unattached, simply exit. */ t_stat cdr_svc (UNIT *uptr) { int32 i; if (s1sel) /* stacker 1? */ uptr = &stack_unit[1]; else if (s2sel) /* stacker 2? */ uptr = &stack_unit[2]; else uptr = &stack_unit[0]; /* then default */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; for (i = 0; i < CDR_WIDTH; i++) rbuf[i] = bcd2ascii (rbuf[i], uptr->flags & UNIT_PCH); for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; rbuf[CDR_WIDTH] = 0; /* null at end */ fputs (rbuf, uptr->fileref); /* write card */ fputc ('\n', uptr->fileref); /* plus new line */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("Card stacker I/O error"); clearerr (uptr->fileref); if (iochk) return SCPE_IOERR; } return SCPE_OK; } /* Card punch routine Modifiers have been checked by the caller C modifier is recognized (column binary is implemented) */ t_stat punch_card (int32 ilnt, int32 mod) { int32 i, cbn, c1, c2; static char pbuf[(2 * CDP_WIDTH) + 1]; /* + null */ t_bool use_h; UNIT *uptr; if (s8sel) /* stack 8? */ uptr = &stack_unit[2]; else if (s4sel) /* stack 4? */ uptr = &stack_unit[4]; else uptr = &cdp_unit; /* normal output */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; use_h = uptr->flags & UNIT_PCH; ind[IN_PNCH] = s4sel = s8sel = 0; /* clear flags */ cbn = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_C); /* col binary? */ M[CDP_BUF - 1] = 012; /* set prev loc */ if (cbn) { /* column binary */ for (i = 0; i < CDP_WIDTH; i++) { c1 = bcd2ascii (M[CD_CBUF1 + i] & CHAR, use_h); c2 = bcd2ascii (M[CD_CBUF2 + i] & CHAR, use_h); if (conv_old) { pbuf[i] = c1; pbuf[i + CDP_WIDTH] = c2; } else { pbuf[2 * i] = c1; pbuf[(2 * i) + 1] = c2; } } for (i = 2 * CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; pbuf[2 * CDP_WIDTH] = 0; /* trailing null */ } else { /* normal */ for (i = 0; i < CDP_WIDTH; i++) pbuf[i] = bcd2ascii (M[CDP_BUF + i] & CHAR, use_h); for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; pbuf[CDP_WIDTH] = 0; /* trailing null */ } fputs (pbuf, uptr->fileref); /* output card */ fputc ('\n', uptr->fileref); /* plus new line */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("Card punch I/O error"); clearerr (uptr->fileref); if (iochk) return SCPE_IOERR; ind[IN_PNCH] = 1; } return SCPE_OK; } /* Select stack routine Modifiers have been checked by the caller Modifiers are 1, 2, 4, 8 for the respective stack, or $, ., square for overlap control (ignored). */ t_stat select_stack (int32 ilnt, int32 mod) { if (mod == BCD_ONE) s1sel = 1; else if (mod == BCD_TWO) s2sel = 1; else if (mod == BCD_FOUR) s4sel = 1; else if (mod == BCD_EIGHT) s8sel = 1; return SCPE_OK; } /* Card reader/punch reset */ t_stat cd_reset (DEVICE *dptr) { ind[IN_LST] = ind[IN_READ] = ind[IN_PNCH] = 0; /* clear indicators */ s1sel = s2sel = s4sel = s8sel = 0; /* clear stacker sel */ sim_cancel (&cdr_unit); /* clear reader event */ return SCPE_OK; } /* Card reader attach */ t_stat cdr_attach (UNIT *uptr, char *cptr) { ind[IN_LST] = ind[IN_READ] = 0; /* clear last card */ return attach_unit (uptr, cptr); } /* Bootstrap routine */ #define BOOT_START 0 #define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned char)) static const unsigned char boot_rom[] = { OP_R + WM, OP_NOP + WM /* R, NOP */ }; t_stat cdr_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_IS; for (i = 0; i < CDR_WIDTH; i++) /* clear buffer */ M[CDR_BUF + i] = 0; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_IS = BOOT_START; return SCPE_OK; } /* Column binary to BCD This is based on documentation in the IBM 1620 manual and may not be accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit pattern, and the appropriate bits are set. (Double punches inclusive OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected; since the 7094 only reads column binary, double punches are ignored. Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. The for loop works right to left, so the table is reversed. */ static const char row_val[12] = { 011, 010, 007, 006, 005, 004, 003, 002, 001, 020, 040, 060 }; char colbin_to_bcd (uint32 cb) { uint32 i; char bcd; for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */ if (cb & (1 << i)) bcd |= row_val[i]; } return bcd; } simh-3.8.1/I1401/i1401_lp.c0000644000175000017500000002231011107357046012736 0ustar vlmvlm/* i1401_lp.c: IBM 1403 line printer simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt 1403 line printer 19-Jan-07 RMS Added UNIT_TEXT flag 07-Mar-05 RMS Fixed bug in write_line (reported by Van Snyder) 25-Apr-03 RMS Revised for extended file support 30-May-02 RMS Widened POS to 32b 13-Apr-01 RMS Revised for register arrays */ #include "i1401_defs.h" extern uint8 M[]; extern char bcd_to_ascii_old[64]; extern char bcd_to_ascii_a[64], bcd_to_ascii_h[64]; extern char bcd_to_pca[64], bcd_to_pch[64]; extern int32 iochk, ind[64]; extern t_bool conv_old; int32 cct[CCT_LNT] = { 03 }; int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); t_stat space (int32 lines, int32 lflag); char *pch_table_old[4] = { bcd_to_ascii_old, bcd_to_pca, bcd_to_pch, bcd_to_ascii_old }; char *pch_table[4] = { bcd_to_ascii_a, bcd_to_pca, bcd_to_pch, bcd_to_ascii_h }; #define UNIT_V_FT (UNIT_V_UF + 0) #define UNIT_V_48 (UNIT_V_UF + 1) #define UNIT_FT (1 << UNIT_V_FT) #define UNIT_48 (1 << UNIT_V_48) #define GET_PCHAIN(x) (((x) >> UNIT_V_FT) & (UNIT_FT|UNIT_48)) #define CHP(ch,val) ((val) & (1 << (ch))) /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG lpt_reg[] = { { FLDATA (ERR, ind[IN_LPT], 0) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, { DRDATA (LINES, lines, 8), PV_LEFT }, { DRDATA (CCTP, cctptr, 8), PV_LEFT }, { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, { NULL } }; MTAB lpt_mod[] = { { UNIT_48, UNIT_48, "48 character chain", "48" }, { UNIT_48, 0, "64 character chain", "64" }, { UNIT_FT, UNIT_FT, "Fortran set", "FORTRAN" }, { UNIT_FT, 0, "business set", "BUSINESS" }, { UNIT_FT|UNIT_48, 0, NULL, "PCF" }, /* obsolete */ { UNIT_FT|UNIT_48, UNIT_48, NULL, "PCA" }, { UNIT_FT|UNIT_48, UNIT_FT|UNIT_48, NULL, "PCH" }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &lpt_reset, NULL, &lpt_attach, NULL }; /* Print routine Modifiers have been checked by the caller SQUARE = word mark mode S = suppress automatic newline */ t_stat write_line (int32 ilnt, int32 mod) { int32 i, t, wm, sup; char *bcd2asc; static char lbuf[LPT_WIDTH + 1]; /* + null */ if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ return SCPE_UNATT; wm = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_SQUARE); sup = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_S); ind[IN_LPT] = 0; /* clear error */ if (conv_old) /* get print chain */ bcd2asc = pch_table_old[GET_PCHAIN (lpt_unit.flags)]; else bcd2asc = pch_table[GET_PCHAIN (lpt_unit.flags)]; for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ t = M[LPT_BUF + i]; if (wm) /* wmarks -> 1 or sp */ lbuf[i] = (t & WM)? '1': ' '; else lbuf[i] = bcd2asc[t & CHAR]; /* normal */ } lbuf[LPT_WIDTH] = 0; /* trailing null */ for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; fputs (lbuf, lpt_unit.fileref); /* write line */ if (lines) /* cc action? do it */ space (lines, lflag); else if (sup == 0) /* default? 1 line */ space (1, FALSE); else { fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ } lines = lflag = 0; /* clear cc action */ if (ferror (lpt_unit.fileref)) { /* error? */ ind[IN_LPT] = 1; perror ("Line printer I/O error"); clearerr (lpt_unit.fileref); if (iochk) return SCPE_IOERR; } return SCPE_OK; } /* Carriage control routine The modifier has not been checked, its format is <5:4> = 00, skip to channel now = 01, space lines after = 10, space lines now = 11, skip to channel after <3:0> = number of lines or channel number */ t_stat carriage_control (int32 mod) { int32 i, action; action = (mod & ZONE) >> V_ZONE; /* get mod type */ mod = mod & DIGIT; /* isolate value */ switch (action) { case 0: /* to channel now */ if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ if (CHP (mod, cct[(cctptr + i) % cctlnt])) return space (i, TRUE); } return STOP_CCT; /* runaway channel */ case 1: /* space after */ if (mod <= 3) { lines = mod; /* save # lines */ lflag = FALSE; /* flag spacing */ ind[IN_CC9] = ind[IN_CC12] = 0; } return SCPE_OK; case 2: /* space now */ if (mod <= 3) return space (mod, FALSE); return SCPE_OK; case 3: /* to channel after */ if ((mod == 0) || (mod > 12)) /* check channel */ return SCPE_OK; ind[IN_CC9] = ind[IN_CC12] = 0; for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ if (CHP (mod, cct[(cctptr + i) % cctlnt])) { lines = i; /* save # lines */ lflag = TRUE; /* flag skipping */ return SCPE_OK; } } return STOP_CCT; /* runaway channel */ } return SCPE_OK; } /* Space routine - space or skip n lines Inputs: count = number of lines to space or skip sflag = skip (TRUE) or space (FALSE) */ t_stat space (int32 count, int32 sflag) { int32 i; if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ if (sflag && CHP (0, cct[cctptr])) /* skip, top of form? */ fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ else { for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); } lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ ind[IN_CC9] = CHP (9, cct[cctptr]) != 0; /* set indicators */ ind[IN_CC12] = CHP (12, cct[cctptr]) != 0; return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { cctptr = 0; /* clear cct ptr */ lines = lflag = 0; /* no cc action */ ind[IN_LPT] = 0; return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { cctptr = 0; /* clear cct ptr */ lines = 0; /* no cc action */ ind[IN_LPT] = 0; return attach_unit (uptr, cptr); } simh-3.8.1/TOOLS/0000755000175000017500000000000011147615611011544 5ustar vlmvlmsimh-3.8.1/TOOLS/converters/0000777000175000017500000000000007744127262013752 5ustar vlmvlmsimh-3.8.1/TOOLS/converters/mtcvtv23.txt0000666000175000017500000000152607301502242016167 0ustar vlmvlmmtcvtv23 converts a tape images in .tpc format to .tap format. .tpc format tape images have the format 2 byte record length 1 record 1 2 byte record length 2 record 2 : 2 bytes = 0000 for end of file and so on. This is the format produced by various UNIX utilities for dumping tape images. .tap format tape images have the format 4 byte record length 1 record 1 repeat of 4 byte record length 1 4 byte record length 2 record 2 repeat of 4 byte record length 2 : 4 bytes = 00000000 for end of file and so on. This is the tape format expected by simh, Tim Stark's TS10, and John Wilson's E11. mtcvtv23 is invoked by mtcvtv23 file1 file2 file3... Each file in turn is converted from .tpc format to .tap format. The input file can have any extension; the converted file will have a .tap extension.simh-3.8.1/TOOLS/converters/mtcvtodd.c0000666000175000017500000000605007610337040015730 0ustar vlmvlm/* This program converts an E11 magtape (with odd record sizes) to SIMH Copyright (c) 1993-2001, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define FLPSIZ 65536 int main (int argc, char *argv[]) { int i, k, tbc, ebc, fc, rc; unsigned char bc[4] = { 0 }; unsigned char buf[FLPSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); else strcat (oname, ".new"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); fc = 1; rc = 0; for (;;) { k = fread (bc, sizeof (char), 4, ifile); if (k == 0) break; tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; ebc = (tbc + 1) & ~1; fwrite (bc, sizeof (char), 4, ofile); if (tbc) { printf ("Record size = %d\n", tbc); if (tbc > FLPSIZ) { printf ("Record too big\n"); return 0; } k = fread (buf, sizeof (char), tbc, ifile); for ( ; k < ebc; k++) buf[k] = 0; fread (bc, sizeof (char), 4, ifile); fwrite (buf, sizeof (char), ebc, ofile); fwrite (bc, sizeof (char), 4, ofile); rc++; } else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); else printf ("End of tape\n"); fc++; rc = 0; } } fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/dtos8cvt.txt0000666000175000017500000000102007301503160016242 0ustar vlmvlmdtos8cvt converts a PDP-8 DECtape image from OS/8 format to simulator format. OS/8 only uses 128 words out of 129 in a PDP-8 DECtape block. The simulator requires all 129 words, in order to simulate the actions of the hardware, and to deal with oddball software systems such as the PDP-8 Disk Monitor. dtos8cvt is invoked by dtos8cvt file1 file2 file3... Each file in turn is converted from OS/8 format to simulator format. The input file can have any extension; the converted file will have a .dt8 extension.simh-3.8.1/TOOLS/converters/mtcvtv23.c0000666000175000017500000000547307255777120015620 0ustar vlmvlm/* This program converts a pre V2.3 simulated magtape to a V2.3 magtape Copyright (c) 1993-1999, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define FLPSIZ 65536 int main (int argc, char *argv[]) { int i, k, wc, fc, rc; unsigned char bc[4] = { 0 }; unsigned char buf[FLPSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); else strcat (oname, ".tap"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); fc = 1; rc = 0; for (;;) { k = fread (bc, sizeof (char), 2, ifile); if (k == 0) break; wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; wc = (wc + 1) & ~1; fwrite (bc, sizeof (char), 4, ofile); if (wc) { k = fread (buf, sizeof (char), wc, ifile); for ( ; k < wc; k++) buf[k] =0; fwrite (buf, sizeof (char), wc, ofile); fwrite (bc, sizeof (char), 4, ofile); rc++; } else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); else printf ("End of tape\n"); fc++; rc = 0; } } fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/gt7cvt.c0000666000175000017500000000622107604201536015325 0ustar vlmvlm/* This program converts a gt7 magtape dump to a SIMH magtape Copyright (c) 2002, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define FLPSIZ 65536 unsigned char fzero[4] = { 0 }; int dump_rec (FILE *of, int bc, char *buf) { unsigned char buc[4]; if (((bc == 1) && (buf[0] == 0xF)) || ((bc == 2) && (buf[0] == 0xF) && (buf[1] == 0xF))) { fwrite (fzero, sizeof (char), 4, of); return 1; } buc[0] = bc & 0xFF; buc[1] = (bc >> 8) & 0xFF; buc[2] = (bc >> 16) & 0xFF; buc[3] = (bc >> 24) & 0xFF; fwrite (buc, sizeof (char), 4, of); fwrite (buf, sizeof (char), (bc + 1) & ~1, of); fwrite (buc, sizeof (char), 4, of); return 0; } int main (int argc, char *argv[]) { int i, ch, bc, rc, fc; unsigned char buf[FLPSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); else strcat (oname, ".tap"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); for (bc = rc = fc = 0;;) { ch = fgetc (ifile); if (ch == EOF) break; if (ch & 0x80) { if (bc) { if (dump_rec (ofile, bc, buf)) printf ("End of file %d\n", ++fc); else printf ("Record %d size %d\n", ++rc, bc); } bc = 0; } buf[bc++] = ch & 0x3F; } fclose (ifile); if (bc) dump_rec (ofile, bc, buf); fwrite (fzero, sizeof (char), 4, ofile); printf ("End of file %d\n", ++fc); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/asc.c0000666000175000017500000000641007602363624014662 0ustar vlmvlm/* This program converts delimited files to Windoze Copyright (c) 1993-2001, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define LAST_ANY 0 #define LAST_CR 1 #define LAST_LF 2 #define MD_WIN 0 #define MD_UNIX 1 #define MD_MAC 2 void puteol (int mode, FILE *of) { if (mode != MD_UNIX) putc ('\r', of); if (mode != MD_MAC) putc ('\n', of); return; } int main (int argc, char *argv[]) { int i, k, mc, lastc; int mode; char *s, *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: asc -muw file [file...]\n"); exit (0); } s = argv[1]; if ((s != NULL) && (*s++ == '-')) { ++argv; --argc; switch (*s) { case 'm': case 'M': mode = MD_MAC; break; case 'u': case 'U': mode = MD_UNIX; break; case 'w': case 'W': mode = MD_WIN; break; default: fprintf (stderr, "Bad option %c\n", *s); return 0; } } else mode = MD_WIN; for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); else strcat (oname, ".new"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); for (lastc = LAST_ANY;;) { k = getc (ifile); if (k == EOF) break; mc = k & 0177; if (mc && (mc != 0177)) { if (mc == 015) { if (lastc == LAST_CR) puteol (mode, ofile); lastc = LAST_CR; } else if (mc == 012) { puteol (mode, ofile); lastc = LAST_LF; } else { if (lastc == LAST_CR) puteol (mode, ofile); putc (mc, ofile); lastc = LAST_ANY; } } } if (lastc == LAST_CR) puteol (mode, ofile); fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/mtcvtfix.c0000666000175000017500000000632307610701472015757 0ustar vlmvlm/* This program fixes a SIMH magtape containing a misread EOF Copyright (c) 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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define FLPSIZ 65536 int main (int argc, char *argv[]) { int i, fc, rc; unsigned int k, tbc; unsigned char bc[4] = { 0 }; unsigned char bceof[4] = { 0 }; unsigned char buf[FLPSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); else strcat (oname, ".new"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); fc = 1; rc = 0; for (;;) { k = fread (bc, sizeof (char), 4, ifile); if (k == 0) break; tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; if (tbc) { printf ("Record size = %d\n", tbc); if (tbc > FLPSIZ) { printf ("Record too big\n"); return 0; } k = fread (buf, sizeof (char), tbc, ifile); for ( ; k < tbc; k++) buf[k] = 0; fread (bc, sizeof (char), 4, ifile); if (tbc > 1) { fwrite (bc, sizeof (char), 4, ofile); fwrite (buf, sizeof (char), tbc, ofile); fwrite (bc, sizeof (char), 4, ofile); rc++; } else { printf ("Record length = 1, ignored\n"); } } else { fwrite (bceof, sizeof (char), 4, ofile); if (rc) printf ("End of file %d, record count = %d\n", fc, rc); else printf ("End of tape\n"); fc++; rc = 0; } } fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/littcvt.c0000666000175000017500000000501507261515714015605 0ustar vlmvlm/* This program removes the 4 header bytes from a Litt tape Copyright (c) 1993-1999, Robert M. Supnik, Compaq Computer Corporation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK, OR COMPAQ COMPUTER CORPORATION, BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik, or Compaq Computer Corporation, shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from both the author and Compaq Computer Corporation. */ #include #include #include #include #define FLPSIZ 65536 int main (int argc, char *argv[]) { int i, k, bc; unsigned char buf[FLPSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); else strcat (oname, ".new"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); fread (&bc, sizeof (int), 1, ifile); for (;;) { k = fread (buf, sizeof (char), FLPSIZ, ifile); if (k == 0) break; fwrite (buf, sizeof (char), k, ofile); } fclose (ifile); fclose (ofile); } exit (0); } simh-3.8.1/TOOLS/converters/asc.txt0000666000175000017500000000102107605203516015244 0ustar vlmvlmASC is a simple utility for converting text files (delimited with \r, \n, or \r\n) to Windows format (\r\n delimited), Unix format (\n delimited) or Mac format (\r delimited). ASC is invoked from a DOS prompt with the command: > ASC {-m|u|w} file1 file2 ... The optional switch specifies Mac processing (-m), Unix processing (-u), or Windows processing (-w). If no switch is specified, Windows processing is performed. Each file is processed in turn. If the file is name.ext, the converted file is name.new. simh-3.8.1/TOOLS/converters/dtos8cvt.c0000666000175000017500000000462607301502722015667 0ustar vlmvlm/* This program converts an OS8 DECtape image to simulator format Copyright (c) 1993-2001, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define BLKSIZ 129 int main (int argc, char *argv[]) { int i, k; unsigned short buf[BLKSIZ]; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".dt8"); else strcat (oname, ".dt8"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); for (;;) { k = fread (buf, sizeof (short), BLKSIZ - 1, ifile); if (k == 0) break; for ( ; k < BLKSIZ; k++) buf[k] = 0; fwrite (buf, sizeof (short), BLKSIZ, ofile); } fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/tp512cvt.c0000666000175000017500000000535507624773174015524 0ustar vlmvlm/* This program converts a tp data file to a simulated 512B blocked magtape 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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define BLKSIZ 512 int main (int argc, char *argv[]) { int i, k; unsigned char buf[BLKSIZ]; unsigned char tef[4] = { 0, 0, 0, 0 }; unsigned char twc[4] = { 0, 2, 0, 0 }; char *ppos, oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); else strcat (oname, ".tap"); ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Processing file %s\n", argv[i]); for (;;) { k = fread (buf, sizeof (char), BLKSIZ, ifile); if (k == 0) break; if (k != BLKSIZ) { printf ("Short block, size = %d\n", k); for ( ; k < BLKSIZ; k++) buf[k] = 0; } fwrite (twc, sizeof (char), 4, ofile); fwrite (buf, sizeof (char), BLKSIZ, ofile); fwrite (twc, sizeof (char), 4, ofile); } fwrite (tef, sizeof (char), 4, ofile); fwrite (tef, sizeof (char), 4, ofile); fclose (ifile); fclose (ofile); } return 0; } simh-3.8.1/TOOLS/converters/sfmtcvt.c0000666000175000017500000000743607607372366015623 0ustar vlmvlm/* This program converts a Motorola S format PROM dump to a binary file 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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #define HEX(a) ((a) >= 'A'? (a) - 'A' + 10: (a) - '0') #define MAXA (2 << 14) #define MAXR 4 int main (int argc, char *argv[]) { int i, d, d1, j, k, astrt, dstrt, addr, maxaddr[MAXR]; int numr, numf; unsigned char data[MAXR][MAXA]; char *s, *ppos, *cptr, line[256], oname[256]; FILE *ifile, *ofile; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } s = argv[1]; if ((s != NULL) && (*s++ == '-')) { ++argv; --argc; switch (*s) { case '1': numr = 1; break; case '2': numr = 2; break; case '4': numr = 4; break; default: fprintf (stderr, "Bad option %c\n", *s); return 0; } } else numr = 1; for (i = 1, numf = 0; i < argc; i++) { ifile = fopen (argv[i], "r"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } printf ("Processing file %s\n", argv[i]); astrt = 4; maxaddr[numf] = 0; for (;;) { cptr = fgets (line, 256, ifile); if (cptr == NULL) break; if (line[0] != 'S') continue; if (line[1] == '1') dstrt = 8; else if (line[1] == '2') dstrt = 10; else continue; for (k = astrt, addr = 0; k < dstrt; k++) { d = HEX (line[k]); addr = (addr << 4) + d; } if (addr >= MAXA) { printf ("Address %o out of range\n", addr); break; } for (k = dstrt; k < (dstrt + 32); k = k + 2, addr++) { d = HEX (line[k]); d1 = HEX (line[k+1]); data[numf][addr] = (d << 4) + d1; } if (addr > maxaddr[numf]) maxaddr[numf] = addr; } fclose (ifile); numf++; if (numf >= numr) { for (k = 0; k < numr; k++) { if (maxaddr[k] != maxaddr[0]) { printf ("Rom lengths don't match, file 1 = %d, file %d = %d\n", maxaddr[0], k, maxaddr[k]); return 0; } } strcpy (oname, argv[i]); if (ppos = strrchr (oname, '.')) strcpy (ppos, ".bin"); else strcat (oname, ".bin"); ofile = fopen (oname, "wb"); if (ofile == NULL) { printf ("Error opening file: %s\n", oname); exit (0); } printf ("Output file: %s, ROM size is %d\n", oname, maxaddr[0]); for (k = 0; k < maxaddr[0]; k++) { for (j = numr - 1; j >= 0; j--) { fwrite (&data[j][k], 1, 1, ofile); } } fclose (ofile); numf = 0; } } if (numf) printf ("Unprocessed files\n"); return 0; } simh-3.8.1/TOOLS/extracters/0000777000175000017500000000000011147615611013734 5ustar vlmvlmsimh-3.8.1/TOOLS/extracters/mtdump.txt0000666000175000017500000000175107641143242016007 0ustar vlmvlmmtdump dumps the record structure of a tape image file in SIMH, E11, or TPC format. mtdump is invoked by mtdump {-sec} file1 file2 file3... where -s specifies SIMH format, -e E11 format, and -c TPC format. The default is SIMH format. Output is to stdout, unless redirected. SIMH format is as follows: 4 byte record length 1 record 1 repeat of 4 byte record length 1 4 byte record length 2 record 2 repeat of 4 byte record length 2 : 4 bytes = 00000000 for end of file and so on. Records are padded to even byte length. E11 format is similar to SIMH format, but odd length records are not padded to even length. TPC format is as follows: 2 byte record length 1 record 1 2 byte record length 2 record 2 : 2 bytes = 0000 for end of file and so on. Records are padded to even byte length. mmdir is a variant of mtdump that dumps the directory structure of an Interdata MMD (multimedia diagnostic) tape. The tape must be in SMIH format. simh-3.8.1/TOOLS/extracters/sdsdump.c0000666000175000017500000000633407460403576015575 0ustar vlmvlm/* This program dumps the format of an SDS paper tape Copyright (c) 2002, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include int getfc (FILE *ifile) { int k; unsigned char by; k = fread (&by, sizeof (char), 1, ifile); if (k == 0) { printf ("End of physical tape\n"); return -1; } return by; } int main (int argc, char *argv[]) { int i, k, wc, pos, cc, inrec, wd, op, tag, addr; FILE *ifile; char *opstr[] = { "HLT", "BRU", "EOM", NULL, NULL, NULL, "EOD", NULL, "MIY", "BRI", "MIW", "POT", "ETR", NULL, "MRG", "EOR", "NOP", NULL, "OVF", "EXU", NULL, NULL, NULL, NULL, "YIM", NULL, "WIM", "PIN", NULL, "STA", "STB", "STX", "SKS", "BRX", NULL, "BRM", NULL, NULL, "CPY", NULL, "SKE", "BRR", "SKB", "SKN", "SUB", "ADD", "SUC", "ADC", "SKR", "MIN", "XMA", "ADM", "MUL", "DIV", "RSH", "LSH", "SKM", "LDX", "SKA", "SKG", "SKD", "LDB", "LDA", "EAX" }; if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } printf ("Processing input file %s\n", argv[i]); inrec = 0; wc = 1; for (pos = wd = cc = 0; ;pos++) { if ((k = getfc (ifile)) < 0) { if (inrec) printf ("Format error\n"); break; } if (k == 0) { if (inrec && cc) printf ("Incomplete word\n"); inrec = 0; continue; } wd = (wd << 6) | (k & 077); cc++; if (cc >= 4) { printf ("Pos = %d, cnt = %d: %08o", pos, wc, wd); tag = (wd >> 21) & 07; op = (wd >> 15) & 077; addr = wd & 037777; if (opstr[op] && ((op != 0) || (wd == 0))) { if (wd & 040000) printf (" [%s* %o", opstr[op], addr); else printf (" [%s %o", opstr[op], addr); if (tag) printf (",%o", tag); printf ("]"); } printf ("\n"); cc = wd = 0; wc++; } } fclose (ifile); } return 0; } simh-3.8.1/TOOLS/extracters/mmdir.c0000666000175000017500000000714007575437670015233 0ustar vlmvlm/* This program dumps the directory of a simulated Interdata MDM tape Copyright (c) 1993-2002, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #include #include int main (int argc, char *argv[]) { int obj, i, k, fc, rc, tpos, sa, ea, fr, fq; unsigned char b[53]; unsigned char bca[4]; unsigned int bc; int preveof; FILE *ifile; #define MAXRLNT 65536 if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: verb file [file...]\n"); exit (0); } for (i = 1; i < argc; i++) { ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); return 0; } printf ("Processing input file %s\n", argv[i]); tpos = 0; rc = 1; fc = 0; preveof = 0; for (;;) { fseek (ifile, tpos, SEEK_SET); k = fread (bca, 1, 4, ifile); bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); if ((k == 0) || (bc == 0xFFFFFFFF)) { printf ("End of physical tape\n"); break; } if (bc & 0x80000000) { printf ("Error marker at record %d\n", rc); bc = bc & ~0x80000000; } if (bc == 0) { if (preveof) { printf ("End of logical tape\n"); break; } preveof = 1; fc++; obj++; rc = 1; tpos = tpos + 4; } else if (bc > MAXRLNT) { printf ("Invalid record length %d, terminating\n", bc); break; } else { tpos = tpos + 8 + ((bc + 1) & ~1); preveof = 0; if (fc && (rc == 1)) { if (bc != 52) { printf ("Invalid record length %d, terminating\n", bc); break; } fread (b, 1, 52, ifile); sa = (((unsigned int) b[18]) << 16) | (((unsigned int) b[19]) << 8) | ((unsigned int) b[20]); ea = (((unsigned int) b[21]) << 16) | (((unsigned int) b[22]) << 8) | ((unsigned int) b[23]); fr = b[27] >> 4; fq = b[27] & 0xF; printf ("%3d %c%c%c 06-%c%c%c", fc, b[0], b[1], b[2], b[3], b[4], b[5]); if (fr) printf ("F0%X", fr); else printf (" "); printf ("R%c%c%c%c %c%c ", b[6], b[7], b[25], b[26], b[28], b[29]); b[18] = b[51] = b[52] = 0; printf ("%s%s %06X %06X %X\n", &b[8], &b[30], sa, ea, fq); } rc++; } } fclose (ifile); } return 0; } simh-3.8.1/TOOLS/extracters/mtdump.c0000666000175000017500000001001207641074756015415 0ustar vlmvlm/* This program dumps the format of a simulated magtape 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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #include #include #define F_STD 0 #define F_E11 1 #define F_TPC 2 #define F_TPF 3 int main (int argc, char *argv[]) { int obj, i, k, fc, rc, tpos; unsigned int bc, fmt, rlntsiz; unsigned char *s, bca[4]; int preveof; FILE *ifile; #define MAXRLNT 65536 if ((argc < 2) || (argv[0] == NULL)) { printf ("Usage is: mtdump {-secf} file [file...]\n"); exit (0); } s = argv[1]; if ((s != NULL) && (*s++ == '-')) { ++argv; --argc; switch (*s) { case 'S': case 's': fmt = F_STD; rlntsiz = 4; break; case 'E': case 'e': fmt = F_E11; rlntsiz = 4; break; case 'C': case 'c': fmt = F_TPC; rlntsiz = 2; break; /* case 'F': case 'f': /* fmt = F_TPF; break; */ default: fprintf (stderr, "Bad option %c\n", *s); return 0; } } else { fmt = F_STD; rlntsiz = 4; } for (i = 1; i < argc; i++) { ifile = fopen (argv[i], "rb"); if (ifile == NULL) { printf ("Error opening file: %s\n", argv[i]); exit (0); } printf ("Processing input file %s\n", argv[i]); tpos = 0; rc = 1; fc = 1; preveof = 0; printf ("Processing tape file %d\n", fc); for (obj = 1;;) { fseek (ifile, tpos, SEEK_SET); k = fread (bca, 1, rlntsiz, ifile); switch (fmt) { case F_STD: case F_E11: bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); break; case F_TPC: bc = (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); break; } if ((k == 0) || (bc == 0xFFFFFFFF)) { printf ("End of physical tape\n"); break; } if (bc & 0x80000000) { printf ("Error marker at record %d\n", rc); bc = bc & ~0x80000000; } if (bc == 0) { if (preveof) { printf ("Obj %d, position %d, end of logical tape\n", obj, tpos); break; } preveof = 1; printf ("Obj %d, position %d, end of tape file %d\n", obj, tpos, fc); fc++; obj++; rc = 1; tpos = tpos + rlntsiz; } else if (bc > MAXRLNT) { printf ("Invalid record length %d, terminating dump\n", bc); break; } else { if (preveof) printf ("Processing tape file %d\n", fc); preveof = 0; printf ("Obj %d, position %d, record %d, length = %d (0x%X)\n", obj, tpos, rc, bc, bc); rc++; obj++; switch (fmt) { case F_STD: tpos = tpos + 8 + ((bc + 1) & ~1); break; case F_E11: tpos = tpos + 8 + bc; break; case F_TPC: tpos = tpos + 2 + ((bc + 1) & ~1); break; } } } fclose (ifile); } return 0; } simh-3.8.1/TOOLS/config11/0000777000175000017500000000000007766456576013210 5ustar vlmvlmsimh-3.8.1/TOOLS/config11/config11.txt0000666000175000017500000000071407550144706015336 0ustar vlmvlmConfig11 is a program for calculating the floating address space layout of a PDP-11 or VAX. Config11 is an interactive program. When started, it repeatedly asks for the names of controllers and the numbers of instances: > config11 Name: UDA50 Number: 2 Name: DZ11 Number: 2 Name: To end input, simply type ENTER to the NAME: prompt. Config11 will then print out the complete floating address table for the known Unibus and Qbus devices. simh-3.8.1/TOOLS/config11/config11.c0000666000175000017500000000773707550432122014745 0ustar vlmvlm/* Program to configure the floating address space of a PDP-11 or VAX Copyright (c) 2002, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include #include #include #include #include #define RANK_LNT 34 int csr, i, j; unsigned int rank, num; char *cp, *ocp, inp[100]; unsigned char numctl[RANK_LNT]; unsigned char modtab[RANK_LNT] = { 0X07, 0X0f, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X0f, 0X07, 0X07, 0X0f, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X0f, 0X07, 0X03, 0X1f, 0X0f, 0X0f, 0X03, 0X0F, 0x0F, 0x1F, 0X1F }; unsigned int fixtab[RANK_LNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0774400, 0770460, 0, 0, 0777170, 0, 0772410, 0, 0, 0, 0, 0774440, 0772150, 0, 0, 0, 0774500, 0, 0, 0, 0 }; char *namtab[RANK_LNT] = { "DJ11", "DH11", "DQ11", "DU11", "DUP11", "LK11A", "DMC11", "DZ11", "KMC11", "LPP11", "VMV21", "VMV31", "DWR70", "RL11", "LPA11K", "KW11C", "rsvd", "RX11", "DR11W", "DR11B", "DMP11", "DPV11", "ISB11", "DMV11", "DEUNA", "UDA50", "DMF32", "KMS11", "VS100", "TK50", "KMV11", "DHV11", "DMZ32", "CP132" }; int main (int argc, char *argv[]) { for ( ;; ) { for (i = 0; i < RANK_LNT; i++) numctl[i] = 0; printf ("Enter configuration data\n"); for ( ;; ) { printf ("Name:\t"); if (gets (inp) == NULL) return 0; if (*inp == 0) break; for (cp = inp; *cp != 0; cp++) *cp = toupper (*cp); for (rank = 0; rank < RANK_LNT; rank++) { if (strcmp (inp, namtab[rank]) == 0) break; } if (rank >= RANK_LNT) { printf ("Unknown controller, valid names are:"); for (i = 0; i < RANK_LNT; i++) { if ((i & 07) == 0) printf ("\n"); printf (" %s", namtab[i]); } printf ("\n"); continue; } printf ("Number:\t"); gets (inp); errno = 0; num = strtoul (inp, &ocp, 10); if (errno || (inp == ocp)) { printf ("Input error\n"); continue; } if (num > 8) { printf ("Too many controllers\n"); continue; } numctl[rank] = num; } printf ("\nRank\tName\tCtrl#\t CSR\n\n"); csr = 0760010; for (i = 0; i < RANK_LNT; i++) { if (numctl[i] == 0) { printf (" %02d\t%s\tgap\t%06o\n", i+1, namtab[i], csr); } else { if (fixtab[i]) printf (" %02d\t%s\t 1\t%06o*\n", i+1, namtab[i], fixtab[i]); else { printf (" %02d\t%s\t 1\t%06o\n", i+1, namtab[i], csr); csr = (csr + modtab[i] + 1) & ~modtab[i]; } for (j = 1; j < numctl[i]; j++) { printf ("\t\t %d\t%06o\n", j + 1, csr); csr = (csr + modtab[i] + 1) & ~modtab[i]; } printf (" %\t\tgap\t%06o\n", csr); } if ((i + 1) < RANK_LNT) csr = (csr + modtab[i+1] + 1) & ~modtab[i+1]; } printf ("\n\n"); } return 0; } simh-3.8.1/TOOLS/crossassemblers/0000777000175000017500000000000007747625436015003 5ustar vlmvlmsimh-3.8.1/TOOLS/crossassemblers/macro1.c0000666000175000017500000024175007747625064016337 0ustar vlmvlm/* * $Id: macro1.c,v 1.74 2003/10/23 23:29:17 phil Exp $ * * TODO: * have flex() use nextfiodec()?? (what if legal in repeat???) * "flexx" should give right justified result??? * squawk if nextfiodec called in a repeat w/ a delim? * * forbid variables/constants in macros * forbid text in repeat?? * forbid start in repeat or macro * use same error TLA's as MACRO??? * IPA error for overbar on LHS of = * variables returns value?? * * macro addressing: labels defined during macro are local use only???? * spacewar expects this??? (is it wrong?) * * self-feeding lines: \n legal anywhere \t is * read next token into "token" buffer -- avoid saving "line"? * remove crocks from "define" * list title (first line of file) should not be parsed as source? * incorrect listing for bare "start" * list only 4 digits for address column * * other; * note variables in symbol dump, xref? * no "permenant" symbols; flush -p? rename .ini? * keep seperate macro/pseudo table? * verify bad input(?) detected * implement dimension pseudo? * remove crocks for '/' and ','? */ /* * Program: MACRO1 * File: macro1.c * Author: Gary A. Messenbrink (macro8) * MACRO7 modifications: Bob Supnik * MACRO1 modifications: Bob Supnik * slashed to be more like real MACRO like by Phil Budne * * Purpose: A 2 pass PDP-1 assembler * * NAME * macro1 - a PDP-1 assembler. * * SYNOPSIS: * macro1 [ -d -p -m -r -s -x ] inputfile inputfile... * * DESCRIPTION * This is a cross-assembler to for PDP-1 assembly language programs. * It will produce an output file in rim format only. * A listing file is always produced and with an optional symbol table * and/or a symbol cross-reference (concordance). The permanent symbol * table can be output in a form that may be read back in so a customized * permanent symbol table can be produced. Any detected errors are output * to a separate file giving the filename in which they were detected * along with the line number, column number and error message as well as * marking the error in the listing file. * The following file name extensions are used: * .mac source code (input) * .lst assembly listing (output) * .rim assembly output in DEC's rim format (output) * .prm permanent symbol table in form suitable for reading after * the EXPUNGE pseudo-op. * .sym "symbol punch" tape (for DDT, or reloading into macro) * * OPTIONS * -d Dump the symbol table at end of assembly * -p Generate a file with the permanent symbols in it. * (To get the current symbol table, assemble a file than has only * START in it.) * -x Generate a cross-reference (concordance) of user symbols. * -r Output a tape using only RIM format (else output block loader) * -s Output a symbol dump tape (loader + loader blocks) * -S file * Read a symbol tape back in * * DIAGNOSTICS * Assembler error diagnostics are output to an error file and inserted * in the listing file. Each line in the error file has the form * * :: : error: at Loc = * * An example error message is: * * bintst.7:17:9 : error: undefined symbol "UNDEF" at Loc = 07616 * * The error diagnostics put in the listing start with a two character * error code (if appropriate) and a short message. A carat '^' is * placed under the item in error if appropriate. * An example error message is: * * 17 07616 3000 DAC UNDEF * UD undefined ^ * 18 07617 1777 TAD I DUMMY * * Undefined symbols are marked in the symbol table listing by prepending * a '?' to the symbol. Redefined symbols are marked in the symbol table * listing by prepending a '#' to the symbol. Examples are: * * #REDEF 04567 * SWITCH 07612 * ?UNDEF 00000 * * Refer to the code for the diagnostic messages generated. * * REFERENCES: * This assembler is based on the pal assember by: * Douglas Jones and * Rich Coon * * COPYRIGHT NOTICE: * This is free software. There is no fee for using it. You may make * any changes that you wish and also give it away. If you can make * a commercial product out of it, fine, but do not put any limits on * the purchaser's right to do the same. If you improve it or fix any * bugs, it would be nice if you told me and offered me a copy of the * new version. * * * Amendments Record: * Version Date by Comments * ------- ------- --- --------------------------------------------------- * v1.0 12Apr96 GAM Original * v1.1 18Nov96 GAM Permanent symbol table initialization error. * v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. * v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). * v1.4 29Nov96 GAM Fixed bug in checksum generation. * v2.1 08Dec96 GAM Added concordance processing (cross reference). * v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). * v2.3 2Feb97 GAM Fixed paging problem in cross reference output. * v3.0 14Feb97 RMS MACRO8X features. * 8Mar97 RMS MACRO7 released w/ sim8swre * 13Mar97 RMS MACRO1 released w/ lispswre * 28Nov01 RMS MACRO1 released w/ simtools * 5Mar03 DP MACRO1 released w/ ddt1 * 2003 PLB major reworking, assembles MACRO, DDT */ #include #include #include #include #define LINELEN 96 #define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ #define NAMELEN 128 #define SYMBOL_COLUMNS 5 #define SYMLEN 7 /*#define SYMSIG 4 /* EXP: significant chars in a symbol */ #define SYMBOL_TABLE_SIZE 8192 #define MAC_MAX_ARGS 20 #define MAC_MAX_LENGTH 8192 #define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ #define MAX_LITERALS 1000 #define MAX_CONSTANTS 10 /* max number of "constants" blocks */ #define XREF_COLUMNS 8 #define ADDRESS_FIELD 0007777 #define INDIRECT_BIT 0010000 #define OP_CODE 0760000 #define CONCISE_LC 072 #define CONCISE_UC 074 /* Macro to get the number of elements in an array. */ #define DIM(a) (sizeof(a)/sizeof(a[0])) #define ISBLANK(c) ((c==' ') || (c=='\f')) #define ISEND(c) ((c=='\0')|| (c=='\n') || (c == '\t')) #define ISDONE(c) ((c=='/') || ISEND(c)) #define ISOVERBAR(c) (c == '\\' || c == '~') /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ /* (true) if the stated condtion is met. */ /* Use these to test attributes. The proper bits are extracted and then */ /* tested. */ #define M_DEFINED(s) (((s) & DEFINED) == DEFINED) #define M_DUPLICATE(s) (((s) & DUPLICATE) == DUPLICATE) #define M_FIXED(s) (((s) & FIXED) == FIXED) #define M_LABEL(s) (((s) & LABEL) == LABEL) #define M_PSEUDO(s) (((s) & PSEUDO) == PSEUDO) #define M_EPSEUDO(s) (((s) & EPSEUDO) == EPSEUDO) #define M_MACRO(s) (((s) & MACRO) == MACRO) #define M_NOTRDEF(s) (((s) & NOTRDEF) != 0) typedef unsigned char BOOL; typedef unsigned char BYTE; typedef int WORD32; #ifndef FALSE #define FALSE 0 #define TRUE (!FALSE) #endif /* Line listing styles. Used to control listing of lines. */ enum linestyle_t { LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL, LINE_LOC }; typedef enum linestyle_t LINESTYLE_T; /* Symbol Types. */ /* Note that the names that have FIX as the suffix contain the FIXED bit */ /* included in the value. */ enum symtyp { UNDEFINED = 0000, DEFINED = 0001, FIXED = 0002, LABEL = 0010 | DEFINED, REDEFINED = 0020 | DEFINED, DUPLICATE = 0040 | DEFINED, PSEUDO = 0100 | FIXED | DEFINED, EPSEUDO = 0200 | FIXED | DEFINED, MACRO = 0400 | DEFINED, DEFFIX = DEFINED | FIXED, NOTRDEF = (MACRO | PSEUDO | LABEL | FIXED) & ~DEFINED }; typedef enum symtyp SYMTYP; enum pseudo_t { DECIMAL, DEFINE, FLEX, CONSTANTS, OCTAL, REPEAT, START, CHAR, VARIABLES, TEXT, NOINPUT, EXPUNGE }; typedef enum pseudo_t PSEUDO_T; struct sym_t { SYMTYP type; char name[SYMLEN]; WORD32 val; WORD32 xref_index; WORD32 xref_count; }; typedef struct sym_t SYM_T; struct emsg_t { char *list; char *file; }; typedef struct emsg_t EMSG_T; struct errsave_t { char *mesg; WORD32 col; }; typedef struct errsave_t ERRSAVE_T; /*----------------------------------------------------------------------------*/ /* Function Prototypes */ int binarySearch( char *name, int start, int symbol_count ); int compareSymbols( const void *a, const void *b ); SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); void errorLexeme( EMSG_T *mesg, WORD32 col ); void errorMessage( EMSG_T *mesg, WORD32 col ); void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); SYM_T eval( void ); SYM_T *evalSymbol( void ); void getArgs( int argc, char *argv[] ); SYM_T getExpr( void ); WORD32 getExprs( void ); WORD32 incrementClc( void ); WORD32 literal( WORD32 value ); BOOL isLexSymbol(); char *lexemeToName( char *name, WORD32 from, WORD32 term ); void listLine( void ); SYM_T *lookup( char *name, int type ); void moveToEndOfLine( void ); void next(int); void onePass( void ); void printCrossReference( void ); void printErrorMessages( void ); void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); void printPageBreak( void ); void printPermanentSymbolTable( void ); void printSymbolTable( void ); BOOL pseudo( PSEUDO_T val ); void punchLocObject( WORD32 loc, WORD32 val ); void punchOutObject( WORD32 loc, WORD32 val ); void punchLeader( WORD32 count ); void punchLoader( void ); void flushLoader( void ); void readLine( void ); void saveError( char *mesg, WORD32 cc ); void topOfForm( char *title, char *sub_title ); void constants(void); void variables(void); void eob(void); void dump_symbols(void); /*----------------------------------------------------------------------------*/ /* Table of pseudo-ops (directives) which are used to setup the symbol */ /* table on startup */ SYM_T pseudos[] = { { PSEUDO, "consta", CONSTANTS }, { PSEUDO, "define", DEFINE }, /* Define macro. */ { PSEUDO, "repeat", REPEAT }, { PSEUDO, "start", START }, /* Set starting address. */ { PSEUDO, "variab", VARIABLES }, { PSEUDO, "text", TEXT }, { PSEUDO, "noinpu", NOINPUT }, { PSEUDO, "expung", EXPUNGE }, /* the following can appear in expressions: */ { EPSEUDO, "charac", CHAR }, { EPSEUDO, "decima", DECIMAL }, /* base 10. */ { EPSEUDO, "flexo", FLEX }, { EPSEUDO, "octal", OCTAL }, /* Read literal constants in base 8. */ }; /* Symbol Table */ /* The table is put in lexical order on startup, so symbols can be */ /* inserted as desired into the initial table. */ #define DIO 0320000 #define JMP 0600000 SYM_T permanent_symbols[] = { /* Memory Reference Instructions */ { DEFFIX, "and", 0020000 }, { DEFFIX, "ior", 0040000 }, { DEFFIX, "xor", 0060000 }, { DEFFIX, "xct", 0100000 }, { DEFFIX, "lac", 0200000 }, { DEFFIX, "lio", 0220000 }, { DEFFIX, "dac", 0240000 }, { DEFFIX, "dap", 0260000 }, { DEFFIX, "dip", 0300000 }, { DEFFIX, "dio", 0320000 }, { DEFFIX, "dzm", 0340000 }, { DEFFIX, "add", 0400000 }, { DEFFIX, "sub", 0420000 }, { DEFFIX, "idx", 0440000 }, { DEFFIX, "isp", 0460000 }, { DEFFIX, "sad", 0500000 }, { DEFFIX, "sas", 0520000 }, { DEFFIX, "mul", 0540000 }, { DEFFIX, "mus", 0540000 }, /* for spacewar */ { DEFFIX, "div", 0560000 }, { DEFFIX, "dis", 0560000 }, /* for spacewar */ { DEFFIX, "jmp", 0600000 }, { DEFFIX, "jsp", 0620000 }, { DEFFIX, "skip", 0640000 }, /* for spacewar */ { DEFFIX, "cal", 0160000 }, { DEFFIX, "jda", 0170000 }, { DEFFIX, "i", 0010000 }, { DEFFIX, "skp", 0640000 }, { DEFFIX, "law", 0700000 }, { DEFFIX, "iot", 0720000 }, { DEFFIX, "opr", 0760000 }, { DEFFIX, "nop", 0760000 }, /* Shift Instructions */ { DEFFIX, "ral", 0661000 }, { DEFFIX, "ril", 0662000 }, { DEFFIX, "rcl", 0663000 }, { DEFFIX, "sal", 0665000 }, { DEFFIX, "sil", 0666000 }, { DEFFIX, "scl", 0667000 }, { DEFFIX, "rar", 0671000 }, { DEFFIX, "rir", 0672000 }, { DEFFIX, "rcr", 0673000 }, { DEFFIX, "sar", 0675000 }, { DEFFIX, "sir", 0676000 }, { DEFFIX, "scr", 0677000 }, { DEFFIX, "1s", 0000001 }, { DEFFIX, "2s", 0000003 }, { DEFFIX, "3s", 0000007 }, { DEFFIX, "4s", 0000017 }, { DEFFIX, "5s", 0000037 }, { DEFFIX, "6s", 0000077 }, { DEFFIX, "7s", 0000177 }, { DEFFIX, "8s", 0000377 }, { DEFFIX, "9s", 0000777 }, /* Skip Microinstructions */ { DEFFIX, "sza", 0640100 }, { DEFFIX, "spa", 0640200 }, { DEFFIX, "sma", 0640400 }, { DEFFIX, "szo", 0641000 }, { DEFFIX, "spi", 0642000 }, { DEFFIX, "szs", 0640000 }, { DEFFIX, "szf", 0640000 }, /*{ DEFFIX, "clo", 0651600 },*/ /* Operate Microinstructions */ { DEFFIX, "clf", 0760000 }, { DEFFIX, "stf", 0760010 }, { DEFFIX, "cla", 0760200 }, /*{ DEFFIX, "lap", 0760300 },*/ { DEFFIX, "hlt", 0760400 }, { DEFFIX, "xx", 0760400 }, { DEFFIX, "cma", 0761000 }, { DEFFIX, "clc", 0761200 }, { DEFFIX, "lat", 0762200 }, { DEFFIX, "cli", 0764000 }, /* IOT's */ /*{ DEFFIX, "ioh", 0730000 },*/ { DEFFIX, "rpa", 0730001 }, { DEFFIX, "rpb", 0730002 }, { DEFFIX, "rrb", 0720030 }, { DEFFIX, "ppa", 0730005 }, { DEFFIX, "ppb", 0730006 }, { DEFFIX, "tyo", 0730003 }, { DEFFIX, "tyi", 0720004 }, { DEFFIX, "dpy", 0730007 }, /* for spacewar, munching squares! */ { DEFFIX, "lsm", 0720054 }, { DEFFIX, "esm", 0720055 }, { DEFFIX, "cbs", 0720056 }, { DEFFIX, "lem", 0720074 }, { DEFFIX, "eem", 0724074 }, { DEFFIX, "cks", 0720033 }, }; /* End-of-Symbols for Permanent Symbol Table */ /* Global variables */ SYM_T *symtab; /* Symbol Table */ int symbol_top; /* Number of entries in symbol table. */ #define LOADERBASE 07751 /* make it relocatable (DDT expects it at 7751) */ #define LOADER_IN LOADERBASE #define LOADER_B (LOADERBASE+06) #define LOADER_A (LOADERBASE+07) #define LOADER_CK (LOADERBASE+025) #define LOADER_EN1 (LOADERBASE+026) WORD32 loader[] = { 0730002, /* in, rpb */ 0320000+LOADER_A, /* dio a */ 0100000+LOADER_A, /* xct a */ 0320000+LOADER_CK, /* dio ck */ 0730002, /* rpb */ 0320000+LOADER_EN1, /* dio en1 */ 0730002, /* b, rpb */ 0000000, /* a, xx */ 0210000+LOADER_A, /* lac i a */ 0400000+LOADER_CK, /* add ck */ 0240000+LOADER_CK, /* dac ck */ 0440000+LOADER_A, /* idx a */ 0520000+LOADER_EN1, /* sas en1 */ 0600000+LOADER_B, /* jmp b */ 0200000+LOADER_CK, /* lac ck */ 0400000+LOADER_EN1, /* add en1 */ 0730002, /* rpb */ 0320000+LOADER_CK, /* dio ck */ 0520000+LOADER_CK, /* sas ck */ 0760400, /* hlt */ 0600000+LOADER_IN /* jmp in */ /* ck, 0 */ /* en1, 0 */ }; #define LOADERBUFSIZE 0100 /* <=0100, power of 2*/ #define LOADERBUFMASK (LOADERBUFSIZE-1) /* for block alignment */ WORD32 loaderbuf[LOADERBUFSIZE]; WORD32 loaderbufcount; WORD32 loaderbufstart; /*----------------------------------------------------------------------------*/ WORD32 *xreftab; /* Start of the concordance table. */ ERRSAVE_T error_list[20]; int save_error_count; char s_detected[] = "detected"; char s_error[] = "error"; char s_errors[] = "errors"; char s_no[] = "No"; char s_page[] = "Page"; char s_symtable[] = "Symbol Table"; char s_xref[] = "Cross Reference"; /* Assembler diagnostic messages. */ /* Some attempt has been made to keep continuity with the PAL-III and */ /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ /* exists, then the indicator is put in the listing as the first two */ /* characters of the diagnostic message. The PAL-III indicators where used */ /* when there was a choice between using MACRO-8 and PAL-III indicators. */ /* The character pairs and their meanings are: */ /* DT Duplicate Tag (symbol) */ /* IC Illegal Character */ /* ID Illegal Redefinition of a symbol. An attempt was made to give */ /* a symbol a new value not via =. */ /* IE Illegal Equals An equal sign was used in the wrong context, */ /* (e.g., A+B=C, or TAD A+=B) */ /* II Illegal Indirect An off page reference was made, but a literal */ /* could not be generated because the indirect bit was already set. */ /* IR Illegal Reference (address is not on current page or page zero) */ /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ /* RD ReDefintion of a symbol */ /* ST Symbol Table full */ /* UA Undefined Address (undefined symbol) */ /* VR Value Required */ /* ZE Zero Page Exceeded (see above, or out of space) */ EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; EMSG_T illegal_character = { "IC illegal char", "illegal character" }; EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; EMSG_T label_syntax = { "IC label syntax", "label syntax" }; EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; EMSG_T illegal_reference = { "IR off page", "illegal reference" }; EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; EMSG_T value_required = { "VR value required", "value required" }; EMSG_T literal_gen_off = { "lit generation off", "literal generation disabled" }; EMSG_T literal_overflow = { "PE page exceeded", "current page literal capacity exceeded" }; EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; EMSG_T illegal_vfd_value = { "width out of range", "VFD field width not in range" }; EMSG_T no_literal_value = { "no value", "No literal value" }; EMSG_T text_string = { "no delimiter", "Text string delimiters not matched" }; EMSG_T lt_expected = { "'<' expected", "'<' expected" }; EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; EMSG_T bad_dummy_arg = { "bad dummy arg", "Bad dummy argument following DEFINE" }; EMSG_T macro_too_long = { "macro too long", "Macro too long" }; EMSG_T no_virtual_memory = { "out of memory", "Insufficient memory for macro" }; EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; EMSG_T define_in_repeat = { "define in a repeat", "Define in a repeat" }; /*----------------------------------------------------------------------------*/ FILE *errorfile; FILE *infile; FILE *listfile; FILE *listsave; FILE *objectfile; FILE *objectsave; char filename[NAMELEN]; char listpathname[NAMELEN]; char sympathname[NAMELEN]; char objectpathname[NAMELEN]; char *pathname; char permpathname[NAMELEN]; WORD32 mac_count; /* Total macros defined. */ /* * malloced macro bodies, indexed by sym->val dummies are evaluated at * invocation time, and value saved in "args"; if recursive macros are * desired (without conditionals, how would you escape?), keep a name * list here and move symbols to "macinv" */ struct macdef { int nargs; /* number of args */ SYM_T args[MAC_MAX_ARGS+1]; /* symbol for each and one for "r" */ char body[1]; /* malloc'ed accordingly */ } *mac_defs[MAC_TABLE_LENGTH]; struct macinv { /* current macro invocation */ char mac_line[LINELEN]; /* Saved macro invocation line. */ WORD32 mac_cc; /* Saved cc after macro invocation. */ char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ struct macdef *defn; /* pointer to definition for dummies */ struct macinv *prev; /* previous invocation in stack */ } *curmacro; /* macro stack */ int nrepeats; /* count of nested repeats */ int list_lineno; int list_pageno; char list_title[LINELEN]; BOOL list_title_set; /* Set if TITLE pseudo-op used. */ char line[LINELEN]; /* Input line. */ int lineno; /* Current line number. */ int page_lineno; /* print line number on current page. */ WORD32 listed; /* Listed flag. */ WORD32 listedsave; WORD32 cc; /* Column Counter (char position in line). */ WORD32 clc; /* Location counter */ BOOL end_of_input; /* End of all input files. */ int errors; /* Number of errors found so far. */ BOOL error_in_line; /* TRUE if error on current line. */ int errors_pass_1; /* Number of errors on pass 1. */ int filix_curr; /* Index in argv to current input file. */ int filix_start; /* Start of input files in argv. */ int lexstartprev; /* Where previous lexeme started. */ int lextermprev; /* Where previous lexeme ended. */ int lexstart; /* Index of current lexeme on line. */ int lexterm; /* Index of character after current lexeme. */ int overbar; /* next saw an overbar in last token */ int nconst; /* number of "constants" blocks */ int lit_count[MAX_CONSTANTS]; /* # of lits in each block in pass 1 */ WORD32 lit_loc[MAX_CONSTANTS]; /* Base of literal blocks */ int noinput; /* don't punch loader */ int nvars; /* number of variables */ WORD32 vars_addr; /* address of "variables" */ WORD32 vars_end; /* end of "variables" */ /* pass 2 only; */ int nlit; /* number of literals in litter[] */ WORD32 litter[MAX_LITERALS]; /* literals */ WORD32 maxcc; /* Current line length. */ BOOL nomac_exp; /* No macro expansion */ WORD32 pass; /* Number of current pass. */ BOOL print_permanent_symbols; WORD32 radix; /* Default number radix. */ BOOL rim_mode; /* RIM mode output. */ BOOL sym_dump; /* punch symbol tape */ int save_argc; /* Saved argc. */ char **save_argv; /* Saved *argv[]. */ WORD32 start_addr; /* Saved start address. */ BOOL symtab_print; /* Print symbol table flag */ BOOL xref; SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ /* initial data from SIMH v3.0 pdp1_stddev.c (different encoding of UC/LC) */ #define UC 0100 /* Upper case */ #define LC 0200 #define CHARBITS 077 #define BC LC|UC /* both case bits */ #define BAD 014 /* unused concise code */ unsigned char ascii_to_fiodec[128] = { BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BC|075, BC|036, BAD, BAD, BAD, BC|077, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BC|000, UC|005, UC|001, UC|004, BAD, BAD, UC|006, UC|002, LC|057, LC|055, UC|073, UC|054, LC|033, LC|054, LC|073, LC|021, LC|020, LC|001, LC|002, LC|003, LC|004, LC|005, LC|006, LC|007, LC|010, LC|011, BAD, BAD, UC|007, UC|033, UC|010, UC|021, LC|040, UC|061, UC|062, UC|063, UC|064, UC|065, UC|066, UC|067, UC|070, UC|071, UC|041, UC|042, UC|043, UC|044, UC|045, UC|046, UC|047, UC|050, UC|051, UC|022, UC|023, UC|024, UC|025, UC|026, UC|027, UC|030, UC|031, UC|057, LC|056, UC|055, UC|011, UC|040, UC|020, LC|061, LC|062, LC|063, LC|064, LC|065, LC|066, LC|067, LC|070, LC|071, LC|041, LC|042, LC|043, LC|044, LC|045, LC|046, LC|047, LC|050, LC|051, LC|022, LC|023, LC|024, LC|025, LC|026, LC|027, LC|030, LC|031, BAD, UC|056, BAD, UC|003, BC|075 }; /* for symbol punch tape conversion only!! */ char fiodec_to_ascii[64] = { 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, '0', 0, 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0, 0, 0, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 0, 0, 0, 0, 0, 0, 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 0, 0, 0, 0, 0, 0 }; /* used at startup & for expunge */ void init_symtab(void) { /* Place end marker in symbol table. */ symtab[0] = sym_undefined; symbol_top = 0; } /* Function: main */ /* Synopsis: Starting point. Controls order of assembly. */ int main( int argc, char *argv[] ) { int ix; int space; save_argc = argc; save_argv = argv; /* Set the default values for global symbols. */ print_permanent_symbols = FALSE; nomac_exp = TRUE; rim_mode = FALSE; /* default to loader tapes */ sym_dump = FALSE; noinput = FALSE; symtab_print = FALSE; xref = FALSE; pathname = NULL; /* init symbol table before processing arguments, so we can * load symbol punch tapes on the fly */ /* * Setup the error file in case symbol table overflows while * installing the permanent symbols. */ errorfile = stderr; pass = 0; /* required for symbol table init */ symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); if( symtab == NULL ) { fprintf( stderr, "Could not allocate memory for symbol table.\n"); exit( -1 ); } init_symtab(); /* Enter the pseudo-ops into the symbol table */ for( ix = 0; ix < DIM( pseudos ); ix++ ) defineSymbol( pseudos[ix].name, pseudos[ix].val, pseudos[ix].type, 0 ); /* Enter the predefined symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) defineSymbol( permanent_symbols[ix].name, permanent_symbols[ix].val, permanent_symbols[ix].type, 0 ); /* Get the options and pathnames */ getArgs( argc, argv ); /* Do pass one of the assembly */ pass = 1; onePass(); errors_pass_1 = errors; /* Set up for pass two */ objectfile = fopen( objectpathname, "wb" ); objectsave = objectfile; listfile = fopen( listpathname, "w" ); listsave = listfile; /* XXX punch title into tape! */ punchLeader( 0 ); if (!rim_mode) { punchLoader(); punchLeader(5); } if (nlit > 0) constants(); /* implied "constants"? */ /* Do pass two of the assembly */ errors = 0; save_error_count = 0; if( xref ) { /* Get the amount of space that will be required for the concordance */ for( space = 0, ix = 0; ix < symbol_top; ix++ ) { symtab[ix].xref_index = space; /* Index into concordance table. */ space += symtab[ix].xref_count + 1; symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ } /* Allocate & clear the necessary space. */ xreftab = (WORD32 *) calloc( space, sizeof( WORD32 )); } pass = 2; onePass(); objectfile = objectsave; /* Works great for trailer. */ punchLeader( 1 ); /* undo effects of NOLIST for any following output to listing file. */ listfile = listsave; /* Display value of error counter. */ if( errors == 0 ) { fprintf( listfile, "\n %s %s %s\n", s_no, s_errors, s_detected ); } else { fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( listfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); } if( symtab_print ) printSymbolTable(); if( print_permanent_symbols ) printPermanentSymbolTable(); if( xref ) printCrossReference(); fclose( objectfile ); fclose( listfile ); if( errors == 0 && errors_pass_1 == 0 ) { /* after closing objectfile -- we reuse the FILE *!! */ if (sym_dump) dump_symbols(); } else remove( objectpathname ); return( errors != 0 ); } /* main() */ /* read a word from a binary punch file */ WORD32 getw(FILE *f) { int i, c; WORD32 w; w = 0; for (i = 0; i < 3;) { c = getc(f); if (c == -1) return -1; if (c & 0200) { /* ignore if ch8 not punched */ w <<= 6; w |= c & 077; i++; } } return w; } /* * "permute zone bits" like MACRO does for proper sorting * (see routine "per" in MACRO) -- it's what DDT expects * * it's it's own inverse! */ WORD32 permute(WORD32 name) { WORD32 temp; temp = name & 0202020; /* get zone bits */ temp = ((temp << 1) & 0777777) | ((temp >> 17) & 1); /* rotate left */ name ^= temp; /* flip zone bits */ name ^= 0400000; /* toggle sign */ return name; } /* add a symbol from a "symbol punch" tape */ void addsym(WORD32 sym, WORD32 val) { char name[4]; sym = permute(sym); name[0] = fiodec_to_ascii[(sym >>12) & 077]; name[1] = fiodec_to_ascii[(sym >> 6) & 077]; name[2] = fiodec_to_ascii[sym & 077]; name[3] = '\0'; defineSymbol( name, val, LABEL, 0); } void read_symbols(char *fname) { FILE *f; f = fopen(fname, "rb"); if (!f) { perror(fname); exit(1); } /* skip loader */ for (;;) { WORD32 w; w = getw(f); if (w == -1) goto err; /* XXX complain? */ if ((w & OP_CODE) == JMP) break; if ((w & OP_CODE) != DIO) goto err; /* XXX complain? */ w = getw(f); if (w == -1) goto err; /* XXX complain? */ } /* XXX should push block reader down into a co-routine */ for (;;) { WORD32 start, end, sum; start = getw(f); if ((start & OP_CODE) == JMP) { fclose(f); return; } if (start == -1 || (start & OP_CODE) != DIO) goto err; end = getw(f); if (end == -1 || (end & OP_CODE) != DIO) goto err; /* XXX complain? */ sum = start + end; while (start < end) { WORD32 sym, val; sym = getw(f); if (sym == -1) goto err; sum += sym; start++; /* XXX handle block boundaries? */ if (start >= end) goto err; val = getw(f); if (val == -1) goto err; /*printf("%06o %06o\n", sym, val);*/ addsym(sym, val); sum += val; start++; } start = getw(f); /* eat checksum XXX verify? */ if (start == -1) goto err; /* roll over all the overflows at once */ if (sum & ~0777777) { sum = (sum & 0777777) + (sum >> 18); if (sum & 01000000) /* one more time */ sum++; } if (start != sum) goto err; } err: fprintf(stderr, "error reading symbol file %s\n", fname); exit(1); } /* Function: getArgs */ /* Synopsis: Parse command line, set flags accordingly and setup input and */ /* output files. */ void getArgs( int argc, char *argv[] ) { WORD32 len; WORD32 ix, jx; /* Set the defaults */ infile = NULL; listfile = NULL; listsave = NULL; objectfile = NULL; objectsave = NULL; for( ix = 1; ix < argc; ) { if( argv[ix][0] == '-' ) { char *switches = argv[ix++]; for( jx = 1; switches[jx] != 0; jx++ ) { switch( switches[jx] ) { case 'd': symtab_print = TRUE; break; case 'r': rim_mode = TRUE; /* punch pure rim-mode tapes */ break; case 's': sym_dump = TRUE; break; case 'm': nomac_exp = FALSE; break; case 'p': print_permanent_symbols = TRUE; break; case 'x': xref = TRUE; break; case 'S': if (ix <= argc) read_symbols(argv[ix++]); break; default: fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); fprintf( stderr, " -d -- dump symbol table\n" ); fprintf( stderr, " -m -- output macro expansions\n" ); fprintf( stderr, " -p -- output permanent symbols to file\n" ); fprintf( stderr, " -r -- output RIM format file\n" ); fprintf( stderr, " -s -- output symbol punch tape to file\n" ); fprintf( stderr, " -S file -- read symbol punch tape\n" ); fprintf( stderr, " -x -- output cross reference to file\n" ); fflush( stderr ); exit( -1 ); } /* end switch */ } /* end for */ } else { filix_start = ix; pathname = argv[ix]; break; } } /* end for */ if( pathname == NULL ) { fprintf( stderr, "%s: no input file specified\n", argv[0] ); exit( -1 ); } len = strlen( pathname ); if( len > NAMELEN - 5 ) { fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); exit( -1 ); } /* Now make the pathnames */ /* Find last '.', if it exists. */ jx = len - 1; while( pathname[jx] != '.' && pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } switch( pathname[jx] ) { case '.': break; case '/': case '\\': jx = len; break; default: break; } /* Add the pathname extensions. */ strncpy( objectpathname, pathname, jx ); objectpathname[jx] = '\0'; strcat( objectpathname, ".rim"); strncpy( listpathname, pathname, jx ); listpathname[jx] = '\0'; strcat( listpathname, ".lst" ); strncpy( permpathname, pathname, jx ); permpathname[jx] = '\0'; strcat( permpathname, ".prm" ); strncpy( sympathname, pathname, jx ); sympathname[jx] = '\0'; strcat( sympathname, ".sym" ); /* Extract the filename from the path. */ if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) pathname[1] = '\\'; /* MS-DOS style pathname */ jx = len - 1; while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) jx--; strcpy( filename, &pathname[jx + 1] ); } /* getArgs() */ int invokeMacro(int index) { struct macinv *mip; struct macdef *mdp; int jx; mdp = mac_defs[index]; if (mdp == NULL || mdp->body[0] == '\0') return 0; /* Find arguments. */ while (ISBLANK(line[lexstart])) next(0); mip = calloc(1, sizeof(struct macinv)); if (!mip) { fprintf(stderr, "could not allocate memory for macro invocation\n"); exit(1); } mip->defn = mdp; /* evaluate args, saving values in SYM_T entries in defn. * (cannot have recursive macros) */ mdp->args[0].val = clc; /* r is location at start */ for( jx = 1; !ISDONE(line[lexstart]) && jx <= MAC_MAX_ARGS; ) { WORD32 val; next(0); if (ISDONE(line[lexstart])) break; if (line[lexstart] == ',') next(0); while( ISBLANK( line[lexstart] )) next(0); if (ISDONE(line[lexstart])) break; val = getExprs(); /* ignore excess values silently? */ if (jx <= mdp->nargs) mdp->args[jx].val = val; jx++; } /* end for */ /* XXX complain if too few actuals? -- nah */ while (jx <= mdp->nargs) mdp->args[jx++].val = 0; strcpy(mip->mac_line, line); /* save line */ mip->mac_cc = cc; /* save position in line */ mip->mac_ptr = mdp->body; mip->prev = curmacro; /* push the old entry */ curmacro = mip; /* step up to the plate! */ return 1; } /* process input; used by onePass and repeat */ void processLine() { if (!list_title_set) { char *cp; /* assert(sizeof(title) >= sizeof(line)); */ strcpy(list_title, line); if ((cp = strchr(list_title, '\n'))) *cp = '\0'; if (list_title[0]) { list_title_set = TRUE; fprintf(stderr, "%s - pass %d\n", list_title, pass ); /* XXX punch title into tape banner (until an '@' seen) */ } return; } for (;;) { int jx; SYM_T evalue; next(0); if( end_of_input ) return; if( ISEND( line[lexstart] )) { if (line[lexstart] != '\t') return; continue; } if (line[lexstart] == '/') /* comment? */ return; /* done */ /* look ahead for 'exp/' */ /* skip until whitespace or terminator */ for( jx = lexstart; jx < maxcc; jx++ ) if( ISBLANK(line[jx]) || ISDONE(line[jx])) break; if( line[jx] == '/') { /* EXP/ set location */ WORD32 newclc; newclc = getExprs(); /* Do not change Current Location Counter if an error occurred. */ if( !error_in_line ) clc = newclc; printLine( line, newclc, 0, LINE_LOC ); cc = jx + 1; next(0); /* discard slash */ continue; } switch( line[lexterm] ) { case ',': if( isLexSymbol()) { WORD32 val; SYM_T *sym; char name[SYMLEN]; /* Use lookup so symbol will not be counted as reference. */ sym = lookup(lexemeToName(name, lexstart, lexterm), UNDEFINED); if (curmacro) { /* relative during macro expansion!! */ val = clc - curmacro->defn->args[0].val; } else val = clc; if( M_DEFINED( sym->type )) { if( sym->val != val && pass == 2 ) errorSymbol( &duplicate_label, sym->name, lexstart ); sym->type |= DUPLICATE; /* XXX never used! */ } /* Must call define on pass 2 to generate concordance. */ defineLexeme( lexstart, lexterm, val, LABEL ); } else if (isdigit(line[lexstart])) { /* constant, */ int i; WORD32 val = 0; for( i = lexstart; i < lexterm; i++ ) { if( isdigit( line[i] )) { int digit; digit = line[i] - '0'; if( digit >= radix ) { errorLexeme( &number_not_radix, i ); val = 0; break; } val = val * radix + digit; } else { errorLexeme( ¬_a_number, lexstart ); val = 0; break; } } if (i == lexterm) { if( clc != val && pass == 2 ) errorLexeme( &duplicate_label, lexstart); /* XXX */ } } else errorLexeme( &label_syntax, lexstart ); next(0); /* skip comma */ continue; case '=': if( isLexSymbol()) { WORD32 start, term, val; start = lexstart; term = lexterm; next(0); /* skip symbol */ next(0); /* skip trailing = */ val = getExprs(); defineLexeme( start, term, val, DEFINED ); printLine( line, 0, val, LINE_VAL ); } else { errorLexeme( &symbol_syntax, lexstartprev ); next(0); /* skip symbol */ next(0); /* skip trailing = */ getExprs(); /* skip expression */ } continue; } /* switch on terminator */ if( isLexSymbol()) { SYM_T *sym; WORD32 val; sym = evalSymbol(); val = sym->val; if( M_MACRO(sym->type)) { if (!invokeMacro(val)) next(0); /* bad defn? or body is empty! */ continue; } /* macro invocation */ else if( M_PSEUDO(sym->type)) { /* NO EPSEUDOs */ pseudo( (PSEUDO_T)val & 0777777 ); continue; } /* pseudo */ } /* macro, or non-char pseudo */ evalue = getExpr(); if (evalue.type != PSEUDO) { /* not a bare pseudo-op? */ if (line[lexstart] == ',') { /* EXP, */ if(evalue.val != clc && pass == 2 ) errorLexeme( &duplicate_label, lexstart); /* XXX */ } else if (line[lexstart] == '/') { /* EXP/ */ clc = evalue.val; printLine( line, clc, 0, LINE_LOC ); next(0); } else { punchOutObject( clc, evalue.val & 0777777); /* punch it! */ incrementClc(); } } } /* forever */ } /* Function: onePass */ /* Synopsis: Do one assembly pass. */ void onePass() { int ix; clc = 4; /* Default location is 4 */ start_addr = 0; /* No starting address. */ nconst = 0; /* No constant blocks seen */ nvars = 0; /* No variables seen */ while (curmacro) { /* pop macro stack */ struct macinv *mp; mp = curmacro->prev; free(curmacro); curmacro = mp; } for( ix = 0; ix < mac_count; ix++) { if (mac_defs[ix]) free( mac_defs[ix] ); mac_defs[ix] = NULL; } mac_count = 0; /* No macros defined. */ listed = TRUE; lineno = 0; list_pageno = 0; list_lineno = 0; list_title_set = FALSE; page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ radix = 8; /* Initial radix is octal (base 8). */ /* Now open the first input file. */ end_of_input = FALSE; filix_curr = filix_start; /* Initialize pointer to input files. */ if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } for (;;) { readLine(); if (end_of_input) { eob(); fclose( infile ); return; } processLine(); } /* forever */ } /* onePass */ /* Function: getExprs */ /* Synopsys: gutted like a fish */ WORD32 getExprs() { SYM_T sym; sym = getExpr(); if (sym.type == PSEUDO) errorMessage( &value_required, lexstart ); /* XXX wrong pointer? */ return sym.val & 0777777; } /* getExprs */ SYM_T getExpr() { SYM_T sym; sym = eval(); /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ for (;;) { int space; /* * falling out of switch breaks loop and returns from routine * so if you want to keep going, you must "continue"!! */ space = FALSE; switch( line[lexstart] ) { case ' ': space = TRUE; /* fall */ case '+': /* add */ next(1); /* skip operator */ if (space && ISEND(line[lexstart])) /* tollerate a trailing space */ return sym; sym.val += eval().val; /* XXX look at type? */ sym.type = DEFINED; if( sym.val >= 01000000 ) sym.val = ( sym.val + 1 ) & 0777777; continue; case '-': /* subtract */ next(1); /* skip over the operator */ sym.val += eval().val ^ 0777777; /* XXX look at type? */ sym.type = DEFINED; if( sym.val >= 01000000 ) sym.val = ( sym.val + 1 ) & 0777777; continue; case '*': /* multiply */ next(1); /* skip over the operator */ sym.val *= eval().val; sym.type = DEFINED; if( sym.val >= 01000000 ) sym.val = ( sym.val + 1 ) & 0777777; continue; #if 0 case '%': /* divide !??? */ /* * neither '%' nor the divide symbol appear in FIO-DEC, * does any known program use such an operator? * Easily confused for "MOD", which is how C uses '%'! */ next(1); sym.val /= eval().val; sym.type = DEFINED; continue; #endif case '&': /* and */ next(1); /* skip over the operator */ sym.val &= eval().val; sym.type = DEFINED; continue; case '!': /* or */ next(1); /* skip over the operator */ sym.val |= eval().val; sym.type = DEFINED; continue; case '/': case ')': case ']': case ':': case ',': break; case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); sym.val = 0; break; default: if (!ISEND(line[lexstart])) { errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); sym.val = 0; break; } } /* switch */ break; /* break loop!! */ } /* "forever" */ return( sym ); } /* getExpr */ /* * return fio-dec code for next char * embeds shifts as needed */ int nextfiodec(int *ccase, int delim) { unsigned char c; for (;;) { if (cc >= maxcc) { if (delim == -1) return -1; /* XXX MUST NOT BE IN A REPEAT!! */ readLine(); /* danger will robinson! */ if (end_of_input) return -1; } c = line[cc]; switch (c) { case '\n': c = '\r'; break; case '\r': continue; } break; } if (delim != -1 && c == delim) { if (*ccase == LC) { cc++; /* eat delim */ return -1; } *ccase = LC; return CONCISE_LC; /* shift down first */ } if (c > 0177) { /* non-ascii */ errorMessage( &illegal_character, cc ); c = 0; /* space?! */ } c = ascii_to_fiodec[c&0177]; if (c == BAD) { errorMessage( &illegal_character, cc ); c = 0; /* space?! */ } if (!(c & *ccase)) { /* char not in current case? */ *ccase ^= BC; /* switch case */ if (*ccase == LC) return CONCISE_LC; /* shift down */ else return CONCISE_UC; /* shift up */ } cc++; return c & CHARBITS; } /* * Function: flex * Synopsis: Handle data for "flexo" pseudo * handle upper case by doing shifts */ WORD32 flex() { WORD32 w; int shift; int ccase; if (line[lexstart] == ' ') /* always? */ next(0); /* original version appears to take next 3 characters, * REGARDLESS of what they are (tab, newline, space?)! */ w = 0; ccase = LC; /* current case */ for (shift = 12; shift >= 0; shift -= 6) { unsigned char c; if( lexstart >= maxcc ) break; c = line[lexstart]; if (c == '\t' || c == '\n') { if (ccase == LC) break; c = CONCISE_LC; /* shift down first */ } else { if (c > 0177) { /* non-ascii */ errorMessage( &illegal_character, lexstart ); c = 0; } c = ascii_to_fiodec[c&0177]; if (c == BAD) { errorMessage( &illegal_character, lexstart ); c = 0; } if (!(c & ccase)) { /* char not in current case? */ ccase ^= BC; /* switch case */ if (ccase == LC) c = CONCISE_LC; /* shift down */ else c = CONCISE_UC; /* shift up */ } else lexstart++; } w |= (c & CHARBITS) << shift; } /* error to get here w/ case == UC? nah. shift down could be next */ return w; } /* flex */ /* * Function: getChar * Synopsis: Handle data for "char" pseudo */ WORD32 getChar() { unsigned char c, pos; if( cc >= maxcc ) return 0; /* XXX error? */ pos = line[cc++]; if (pos != 'l' && pos != 'm' && pos != 'r') { errorMessage( &illegal_character, lexstart ); return 0; } if( cc >= maxcc ) return 0; /* XXX error? */ c = line[cc++]; if (c > 0177) { errorMessage( &illegal_character, lexstart ); c = 0; } c = ascii_to_fiodec[c]; if (c == BAD) { errorMessage( &illegal_character, lexstart ); c = 0; } if (!(c & LC)) { /* upper case only char? */ c = CONCISE_UC; /* take a shift up */ cc--; /* and leave char for next luser */ } c &= CHARBITS; switch (pos) { case 'l': return c << 12; case 'm': return c << 6; case 'r': return c; } /* should not happen */ return 0; } /* flex */ /* Function: eval */ /* Synopsis: Get the value of the current lexeme, and advance.*/ SYM_T eval2() { WORD32 digit; WORD32 from; SYM_T *sym; WORD32 val; SYM_T sym_eval; sym_eval.type = DEFINED; sym_eval.name[0] = '\0'; sym_eval.val = sym_eval.xref_index = sym_eval.xref_count = 0; val = 0; if( isLexSymbol()) { sym = evalSymbol(); if(!M_DEFINED( sym->type )) { if( pass == 2 ) errorSymbol( &undefined_symbol, sym->name, lexstart ); next(1); return( *sym ); } else if( M_PSEUDO(sym->type) || M_EPSEUDO(sym->type)) { switch (sym->val) { case DECIMAL: radix = 10; sym_eval.type = PSEUDO; sym_eval.val = 0; /* has zero as a value! */ break; case OCTAL: radix = 8; sym_eval.type = PSEUDO; sym_eval.val = 0; /* has zero as a value */ break; case FLEX: next(1); /* skip keyword */ sym_eval.val = flex(); break; case CHAR: next(1); /* skip keyword */ sym_eval.val = getChar(); break; default: errorSymbol( &value_required, sym->name, lexstart ); sym_eval.type = sym->type; sym_eval.val = 0; break; } next(1); return( sym_eval ); } else if( M_MACRO( sym->type )) { if( pass == 2 ) { errorSymbol( &misplaced_symbol, sym->name, lexstart ); } sym_eval.type = sym->type; sym_eval.val = 0; next(1); return( sym_eval ); } else { next(1); return( *sym ); } } /* symbol */ else if( isdigit( line[lexstart] )) { from = lexstart; val = 0; while( from < lexterm ) { if( isdigit( line[from] )) { digit = line[from++] - '0'; if( digit >= radix ) { errorLexeme( &number_not_radix, from - 1 ); val = 0; break; } val = val * radix + digit; } else { errorLexeme( ¬_a_number, lexstart ); val = 0; break; } } next(1); sym_eval.val = val; return( sym_eval ); } /* digit */ else { switch( line[lexstart] ) { case '.': /* Value of Current Location Counter */ val = clc; next(1); break; case '(': /* Generate literal */ next(1); /* Skip paren */ val = getExprs(); /* recurse */ if( line[lexstart] == ')' ) next(1); /* Skip end paren */ sym_eval.val = literal(val); return sym_eval; case '[': /* parens!! */ next(1); sym_eval.val = getExprs(); /* mutual recursion */ if( line[lexstart] == ']' ) next(1); /* Skip close bracket */ else errorMessage( &illegal_character, lexstart ); return sym_eval; default: switch( line[lexstart] ) { case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); break; default: errorMessage( &illegal_character, lexstart ); break; } /* error switch */ val = 0; /* On error, set value to zero. */ next(1); /* Go past illegal character. */ } /* switch on first char */ } /* not symbol or number */ sym_eval.val = val; return( sym_eval ); } /* eval2 */ SYM_T eval() { SYM_T sym; switch (line[lexstart]) { case '-': /* unary - */ next(1); sym = eval2(); /* skip op */ sym.val ^= 0777777; break; case '+': /* unary + */ next(1); /* skip op */ /* fall */ default: sym = eval2(); } return sym; } /* Function: incrementClc */ /* Synopsis: Set the next assembly location. Test for collision with */ /* the literal tables. */ WORD32 incrementClc() { clc = (( clc + 1 ) & ADDRESS_FIELD ); return( clc ); } /* incrementClc */ /* Function: readLine */ /* Synopsis: Get next line of input. Print previous line if needed. */ void readLine() { BOOL ffseen; WORD32 ix; WORD32 iy; char inpline[LINELEN]; /* XXX panic if nrepeats > 0 (if self-feeding, do the backup here?) */ listLine(); /* List previous line if needed. */ error_in_line = FALSE; /* No error in line. */ if(curmacro && *curmacro->mac_ptr == '\0') { /* end of macro? */ struct macinv *mp; listed = TRUE; /* Already listed. */ /* Restore invoking line. */ strcpy(line, curmacro->mac_line); cc = lexstartprev = curmacro->mac_cc; /* Restore cc. */ maxcc = strlen( line ); /* Restore maxcc. */ mp = curmacro->prev; /* pop stack */ free(curmacro); curmacro = mp; return; } /* end of macro */ cc = 0; /* Initialize column counter. */ lexstartprev = 0; if( curmacro ) { /* Inside macro? */ char mc; maxcc = 0; do { mc = *curmacro->mac_ptr++; /* Next character. */ /* watch for overflow? how could it?? */ line[maxcc++] = mc; } while( !ISEND( mc )); /* note: terminates on tab?! */ line[maxcc] = '\0'; listed = nomac_exp; return; } /* inside macro */ lineno++; /* Count lines read. */ listed = FALSE; /* Mark as not listed. */ READ_LINE: if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { filix_curr++; /* Advance to next file. */ if( filix_curr < save_argc ) { /* More files? */ fclose( infile ); if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } list_title_set = FALSE; goto READ_LINE; } else end_of_input = TRUE; } /* fgets failed */ ffseen = FALSE; for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { if( inpline[ix] == '\f' ) { if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); ffseen = TRUE; } else line[iy++] = inpline[ix]; } line[iy] = '\0'; /* If the line is terminated by CR-LF, remove, the CR. */ if( line[iy - 2] == '\r' ) { iy--; line[iy - 1] = line[iy - 0]; line[iy] = '\0'; } maxcc = iy; /* Save the current line length. */ } /* readLine */ /* Function: listLine */ /* Synopsis: Output a line to the listing file. */ void listLine() /* generate a line of listing if not already done! */ { if( listfile != NULL && listed == FALSE ) { printLine( line, 0, 0, LINE ); } } /* listLine */ /* Function: printPageBreak */ /* Synopsis: Output a Top of Form and listing header if new page necessary. */ void printPageBreak() { if( page_lineno >= LIST_LINES_PER_PAGE ) /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ { topOfForm( list_title, NULL ); } } /* printPageBreak */ /* Function: printLine */ /* Synopsis: Output a line to the listing file with new page if necessary. */ void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) { if( listfile == NULL ) { save_error_count = 0; return; } printPageBreak(); list_lineno++; page_lineno++; switch( linestyle ) { default: case LINE: fprintf( listfile, "%5d ", lineno ); fputs( line, listfile ); listed = TRUE; break; case LINE_VAL: if( !listed ) { fprintf( listfile, "%5d %6.6o ", lineno, val ); fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %6.6o\n", val ); } break; case LINE_LOC: if( !listed ) { fprintf( listfile, "%5d %5.5o ", lineno, loc ); fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %5.5o\n", loc ); } break; case LINE_LOC_VAL: if( !listed ) { fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %5.5o %6.6o\n", loc, val ); } break; case LOC_VAL: fprintf( listfile, " %5.5o %6.6o\n", loc, val ); break; } printErrorMessages(); } /* printLine */ /* Function: printErrorMessages */ /* Synopsis: Output any error messages from the current list of errors. */ void printErrorMessages() { WORD32 ix; WORD32 iy; if( listfile != NULL ) { /* If any errors, display them now. */ for( iy = 0; iy < save_error_count; iy++ ) { printPageBreak(); fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); if( error_list[iy].col >= 0 ) { for( ix = 0; ix < error_list[iy].col; ix++ ) { if( line[ix] == '\t' ) { putc( '\t', listfile ); } else { putc( ' ', listfile ); } } fputs( "^", listfile ); list_lineno++; page_lineno++; } fputs( "\n", listfile ); } } save_error_count = 0; } /* printErrorMessages */ /* Function: punchObject */ /* Synopsis: Put one character to object file */ void punchObject( WORD32 val ) { val &= 0377; if( objectfile != NULL ) fputc( val, objectfile ); } /* punchObject */ /* Function: punchTriplet */ /* Synopsis: Output 18b word as three 6b characters with ho bit set. */ void punchTriplet( WORD32 val ) { punchObject((( val >> 12) & 077) | 0200 ); punchObject((( val >> 6 ) & 077) | 0200 ); punchObject(( val & 077) | 0200 ); } /* punchTriplet */ void eob() { /* in case no "start" in file (an error?) */ } /* Function: punchLeader */ /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ /* documentation. Paper tape has 10 punches per inch. */ void punchLeader( WORD32 count ) { WORD32 ix; /* If value is zero, set to the default of 2 feet of leader. */ count = ( count == 0 ) ? 240 : count; if( objectfile != NULL ) { for( ix = 0; ix < count; ix++ ) { fputc( 0, objectfile ); } } } /* punchLeader */ /* Function: punchOutObject */ /* Synopsis: Output the current line and then then punch value to the */ /* object file. */ void punchOutObject( WORD32 loc, WORD32 val ) { printLine( line, loc, val, LINE_LOC_VAL ); punchLocObject( loc, val ); } /* punchOutObject */ /* Function: punchLocObjectRIM */ /* Synopsis: Output the word in RIM mode */ void punchLocObjectRIM( WORD32 loc, WORD32 val ) { punchTriplet( DIO | loc ); punchTriplet( val ); } /* punchLocObject */ /* punch loader in RIM mode */ void punchLoader() { int i; if (noinput) return; for (i = 0; i < DIM(loader); i++) punchLocObjectRIM(LOADERBASE+i, loader[i]); punchTriplet( JMP | LOADERBASE ); } /* * flush out loader buffer; output a block: * DIO start * DIO end+1 * .... data .... * sum */ #define PW(X) { WORD32 x = X; sum += x; punchTriplet(x); } void flushLoader() { WORD32 sum; int i; if (loaderbufcount == 0) return; sum = 0; PW( DIO | loaderbufstart ); PW( DIO | loaderbufstart + loaderbufcount ); for (i = 0; i < loaderbufcount; i++) PW( loaderbuf[i] ); /* roll over all the overflows at once */ if (sum & ~0777777) sum = (sum & 0777777) + (sum >> 18); if (sum & 01000000) /* one more time */ sum++; PW( sum ); punchLeader(5); loaderbufcount = 0; } void punchLocObject( WORD32 loc, WORD32 val ) { if (!rim_mode) { if ((loc & LOADERBUFMASK) == 0 || /* full/force alignment */ loaderbufcount > 0 && loc != loaderbufstart + loaderbufcount) /* disjoint */ flushLoader(); if (loaderbufcount == 0) loaderbufstart = loc; loaderbuf[loaderbufcount++] = val; } else punchLocObjectRIM( loc, val ); } /* Function: literal */ /* Synopsis: Add a value to the literal pool */ WORD32 literal( WORD32 value ) { int i; if (nconst >= MAX_CONSTANTS) { fprintf(stderr, "too many 'constants'; increase MAX_CONSTANTS\n"); exit(1); } if (pass == 1) { if (++lit_count[nconst] == MAX_LITERALS) { fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); exit(1); } return lit_count[nconst]; } #if 1 /* * pool constants; makes for a shorter tape * (but "middle" constants blocks can't shrink) */ for (i = 0; i < nlit; i++) if (litter[i] == value) return lit_loc[nconst] + i; #endif /* paranoia */ if (nlit == MAX_LITERALS) { fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); exit(1); } /* not found, save it */ litter[nlit] = value; /* use base for this block, determined on pass1 */ return lit_loc[nconst] + nlit++; } /* literal */ /* Function: printSymbolTable */ /* Synopsis: Output the symbol table. */ /* XXX now prints FIXED symbols too */ void printSymbolTable() { int ix; int symbol_lines; SYM_T *sym; char mark; symbol_lines = 0; for (ix = 0, sym = symtab; ix < symbol_top; ix++, sym++) { if (M_FIXED(sym->type) || M_PSEUDO(sym->type) || M_MACRO(sym->type) || M_EPSEUDO(sym->type)) continue; if (symbol_lines == 0) { topOfForm( list_title, s_symtable ); symbol_lines = LIST_LINES_PER_PAGE; } switch( sym->type & ( DEFINED | REDEFINED )) { case UNDEFINED: mark = '?'; break; case REDEFINED: mark = '#'; break; default: mark = ' '; break; } fprintf( listfile, "%c%-6.6s %6.6o\n", mark, sym->name, sym->val ); symbol_lines--; } } /* printSymbolTable */ /* Function: printPermanentSymbolTable */ /* Synopsis: Output the permanent symbol table to a file suitable for */ /* being input after the EXPUNGE pseudo-op. */ void printPermanentSymbolTable() { int ix; FILE *permfile; if(( permfile = fopen( permpathname, "w" )) == NULL ) { exit( 2 ); } fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); fprintf( permfile, " expunge\n/\n" ); for( ix = 0; ix < symbol_top; ix++ ) { int type = symtab[ix].type; if( M_FIXED(type) && !M_PSEUDO(type) && !M_EPSEUDO(type) ) fprintf( permfile, "\t%s=%o\n", symtab[ix].name, symtab[ix].val ); } fclose( permfile ); } /* printPermanentSymbolTable */ /* Function: printCrossReference */ /* Synopsis: Output a cross reference (concordance) for the file being */ /* assembled. */ void printCrossReference() { int ix; int xc; int xc_index; int xc_refcount; int xc_cols; SYM_T *sym; /* Force top of form for first page. */ page_lineno = LIST_LINES_PER_PAGE; list_lineno = 0; for( ix = 0, sym = symtab; ix < symbol_top; ix++, sym++ ) { if (M_FIXED(sym->type) && xreftab[sym->xref_index] == 0) continue; list_lineno++; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) topOfForm( list_title, s_xref ); fprintf( listfile, "%5d", list_lineno ); /* Get reference count & index into concordance table for this symbol */ xc_refcount = sym->xref_count; xc_index = sym->xref_index; /* Determine how to label symbol on concordance. */ /* XXX flag variables? */ switch( sym->type & ( DEFINED | REDEFINED )) { case UNDEFINED: fprintf( listfile, " U "); break; case REDEFINED: fprintf( listfile, " M %5d ", xreftab[xc_index] ); break; default: fprintf( listfile, " A %5d ", xreftab[xc_index] ); break; } fprintf( listfile, "%-6.6s ", sym->name ); /* Output the references, 8 numbers per line after symbol name. */ for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { if( xc_cols >= XREF_COLUMNS ) { xc_cols = 0; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) topOfForm( list_title, s_xref); list_lineno++; fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); } fprintf( listfile, " %5d", xreftab[xc_index + xc] ); } fprintf( listfile, "\n" ); } /* for */ } /* printCrossReference */ /* Function: topOfForm */ /* Synopsis: Prints title and sub-title on top of next page of listing. */ void topOfForm( char *title, char *sub_title ) { char temp[10]; list_pageno++; strcpy( temp, s_page ); sprintf( temp, "%s %d", s_page, list_pageno ); if (!listfile) return; /* Output a top of form if not the first page of the listing. */ if( list_pageno > 1 ) fprintf( listfile, "\f" ); fprintf( listfile, "\n %-63s %10s\n", title, temp ); /* Reset the current page line counter. */ page_lineno = 1; if( sub_title != NULL ) { fprintf( listfile, "%80s\n", sub_title ); page_lineno++; } else { fprintf( listfile, "\n" ); page_lineno++; } fprintf( listfile, "\n" ); page_lineno++; } /* topOfForm */ /* Function: lexemeToName */ /* Synopsis: Convert the current lexeme into a string. */ char *lexemeToName( char *name, WORD32 from, WORD32 term ) { int to; to = 0; while( from < term && to < SYMLEN-1) { char c = line[from++]; if (ISOVERBAR(c)) continue; name[to++] = c; } name[to] = '\0'; return( name ); } /* lexemeToName */ /* Function: defineLexeme */ /* Synopsis: Put lexeme into symbol table with a value. */ SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ WORD32 term, /* end+1 of lexeme being defined. */ WORD32 val, /* value of lexeme being defined. */ SYMTYP type ) /* how symbol is being defined. */ { char name[SYMLEN]; lexemeToName( name, start, term); return( defineSymbol( name, val, type, start )); } /* defineLexeme */ /* Function: defineSymbol */ /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ /* not already in table. */ SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) { SYM_T *sym; WORD32 xref_count; if( strlen( name ) < 1 ) { return( &sym_undefined ); /* Protect against non-existent names. */ } sym = lookup( name, type ); xref_count = 0; /* Set concordance for normal defintion. */ if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) { if( pass == 2 ) { errorSymbol( &redefined_symbol, sym->name, start ); type = type | REDEFINED; sym->xref_count++; /* Referenced symbol, count it. */ xref_count = sym->xref_count; /* moved inside "if pass2" -plb 10/2/03 allow redefinition * of predefined symbols during pass1 */ return ( sym ); } } if( pass == 2 && xref ) { /* Put the definition line number in the concordance table. */ /* Defined symbols are not counted as references. */ if (sym->xref_index >= 0) { /* beware macro dummies */ xreftab[sym->xref_index] = lineno; /* Put the line number in the concordance table. */ xreftab[sym->xref_index + xref_count] = lineno; } } /* Now set the value and the type. */ sym->val = val & 0777777; sym->type = type; return( sym ); } /* defineSymbol */ /* Function: lookup */ /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ /* table as undefined. Return address of symbol in table. */ SYM_T *lookup( char *name, int type ) { int ix; /* Insertion index */ int lx; /* Left index */ int rx; /* Right index */ SYM_T *best; /* best match */ SYM_T *sym; /* YIKES! Search dummies (and "R") before anything else!! */ if (curmacro && curmacro->defn) { struct macdef *mdp = curmacro->defn; int i; for (i = 0, sym = mdp->args; i <= mdp->nargs; i++, sym++) if (strcmp(name, sym->name) == 0) return sym; } lx = 0; rx = symbol_top - 1; best = NULL; while (lx <= rx) { int mx = (lx + rx) / 2; /* Find center of search area. */ int compare; sym = symtab + mx; compare = strcmp(name, sym->name); if (compare < 0) rx = mx - 1; else if (compare > 0) lx = mx + 1; else { /* match */ if (overbar && !M_DEFINED(sym->type) && pass == 2) { sym->type = DEFINED; sym->val = vars_addr++; nvars++; } return sym; /* return exact match */ } /* match */ /* save best non-exact match; MACRO returns last defined n-x match! */ if ((M_PSEUDO(sym->type)||M_EPSEUDO(sym->type)||M_MACRO(sym->type)) && strncmp(name, sym->name, 3) == 0) best = sym; } /* while */ /* return best match (pseudo or macro) if any for lookups (not defns) */ if (best && type == UNDEFINED) return best; /* Must put symbol in table if index is negative. */ ix = lx; /* insertion point */ if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { errorSymbol( &symbol_table_full, name, lexstart ); exit( 1 ); } for( rx = symbol_top; rx >= ix; rx-- ) symtab[rx + 1] = symtab[rx]; symbol_top++; /* Enter the symbol as UNDEFINED with a value of zero. */ sym = symtab + ix; strcpy( sym->name, name ); sym->type = UNDEFINED; sym->val = 0; sym->xref_count = 0; if( xref && pass == 2 && sym->xref_index >= 0) xreftab[sym->xref_index] = 0; if (overbar) nvars++; return sym; } /* lookup */ /* Function: compareSymbols */ /* Synopsis: Used to presort the symbol table when starting assembler. */ int compareSymbols( const void *a, const void *b ) { return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); } /* compareSymbols */ /* Function: evalSymbol */ /* Synopsis: Get the pointer for the symbol table entry if exists. */ /* If symbol doesn't exist, return a pointer to the undefined sym */ SYM_T *evalSymbol() { char name[SYMLEN]; SYM_T *sym; sym = lookup( lexemeToName( name, lexstart, lexterm ), UNDEFINED); sym->xref_count++; /* Count the number of references to symbol. */ if( xref && pass == 2 && sym->xref_index >= 0) { /* Put the line number in the concordance table. */ xreftab[sym->xref_index + sym->xref_count] = lineno; } return( sym ); } /* evalSymbol */ /* Function: moveToEndOfLine */ /* Synopsis: Move the parser input to the end of the current input line. */ void moveToEndOfLine() { while( !ISEND( line[cc] )) cc++; /* XXX wrong! will stop on a tab! */ lexstart = cc; lexterm = cc; lexstartprev = lexstart; } /* moveToEndOfLine */ /* frame the next token in "line" with lexstart and lexterm indicies */ void next(int op) { char c; /* Save start column of previous lexeme for diagnostic messages. */ lexstartprev = lexstart; lextermprev = lexterm; c = line[cc]; if (c == ' ') { /* eat spaces */ do { c = line[++cc]; } while (c == ' '); if (op) /* looking for operators? */ cc--; /* return one */ } overbar = 0; lexstart = cc; c = line[cc]; if( isalnum(c) || ISOVERBAR(c)) { if (ISOVERBAR(c)) overbar = 1; do { c = line[++cc]; if (ISOVERBAR(c)) overbar = 1; } while (isalnum(c) || ISOVERBAR(c)); } else if(!ISDONE(c) || c == '\t') /* not end of line, or comment */ cc++; /* advance past all punctuation */ lexterm = cc; } /* next */ BOOL isLexSymbol() { int ix; /* XXX alpha within first 4? 3?? */ for( ix = lexstart; ix < lexterm; ix++ ) if(isalpha(line[ix])) return TRUE; /* any position will do! */ return FALSE; } /* isLexSymbol */ /* * from macro manual (F-36BP), p.18; * * "A macro-instruction definition consists of four parts; * the pseudo-instruction _define_, the _macro instruction name_ * amd _dummy symbol list,_ the _body_, and the pseudo-instruction * _terminate_. Each part is followed by at least one tabulation or * carriage return." * * and in the next paragraph; * * "The name is terminated by a _space_ or by a _tab_ or _cr_ * if there is no dummy symbol list." * * This accepts tabs and/or a newline after define * (but will accept a space), and only accepts spaces * between macro and dummy names. */ void defineMacro() { int lexstartsave; /* point to macro name */ int index; /* point to error char */ int error; /* error boolean */ int i; int count; WORD32 length; WORD32 value; char termin[SYMLEN]; char args[MAC_MAX_ARGS][SYMLEN]; /* macro & arg names */ char body[MAC_MAX_LENGTH + 1]; struct macdef *mdp; SYM_T *sym; if (nrepeats) { /* we can call readLine, so throw up hands now */ errorLexeme( &define_in_repeat, lexstartprev ); return; } while (line[lexstart] == ' ' || line[lexstart] == '\t') next(0); /* not a tab or space */ if (ISEND(line[lexstart])) { /* newline or EOS? */ /* crock; next token should invisibly skip over line boundaries? */ readLine(); next(0); while (line[lexstart] == ' ' || line[lexstart] == '\t') next(0); } /* XXX pick up macro name out here */ count = 0; index = 0; error = FALSE; lexstartsave = lexstart; while (!ISDONE(line[lexstart]) && count < MAC_MAX_ARGS) { if (!isalnum(line[lexstart]) && index == 0) index = lexstart; /* error pointer */ lexemeToName( args[count++], lexstart, lexterm ); /* XXX error if NOT a comma (& not first dummy) ? */ if (line[lexterm] == ',') next(0); /* eat the comma */ next(0); if (line[lexstart] == ' ') next(0); } if( count == 0 ) { /* No macro name. */ errorMessage( &no_macro_name, lexstartsave ); error = TRUE; } else if( index ) { /* Bad argument name. */ errorMessage( &bad_dummy_arg, index ); error = TRUE; } else if( mac_count >= MAC_TABLE_LENGTH ) { errorMessage( ¯o_table_full, lexstartsave ); error = TRUE; } else { value = mac_count++; /* sym value is index into mac */ defineSymbol( args[0], value, MACRO, lexstartsave ); } for( length = 0;; ) { readLine(); if (end_of_input) break; next(0); while (line[lexstart] == ' ' || line[lexstart] == '\t') next(0); lexemeToName( termin, lexstart, lexterm ); /* just look at line? */ if (strncmp( termin, "term", 4 ) == 0) break; if (!error) { int ll = strlen(line); int allblank = FALSE; /* don't save blank lines! */ for( i = 0; i < ll && allblank; i++ ) if(!ISBLANK(line[i])) allblank = FALSE; if (allblank) /* nothing but air? */ continue; /* skip it! */ if ((length + ll + 1) >= MAC_MAX_LENGTH ) { errorMessage (¯o_too_long, lexstart ); error = TRUE; continue; } strcpy(body+length, line); length += ll; } } /* for */ if( error ) return; mdp = calloc(1, sizeof(struct macdef) + length); if (mdp == NULL) { fprintf(stderr, "error allocating memory for macro definition\n"); exit(1); } mac_defs[value] = mdp; strncpy(mdp->body, body, length); mdp->body[length] = '\0'; mdp->nargs = count - 1; /* * save dummy names * symbol slot 0 reserved for "r" symbol * move SYM_T entries to macinv to allow recursion */ sym = mdp->args; sym->type = DEFINED; strcpy(sym->name, "R"); sym->val = 0; sym->xref_index = -1; /* ??? allow xref? */ sym++; for (i = 1; i <= mdp->nargs; i++, sym++) { sym->type = DEFINED; strcpy(sym->name, args[i]); sym->val = 0; sym->xref_index = -1; /* don't xref!! */ } } /* defineMacro */ /* VARIABLES pseudo-op */ void variables() { /* XXX error if "variables" already seen (in this pass) */ /* XXX error if different address on pass 2 */ if (pass == 2) printLine( line, clc, 0, LINE_LOC ); vars_addr = clc; vars_end = clc = (clc + nvars) & ADDRESS_FIELD; if (pass == 2) printLine( line, clc, 0, LINE_LOC); } /* TEXT pseudo-op */ void text(void) { char delim; WORD32 w; int count; int ccase; /* XXX error in repeat!! */ do { if (cc == maxcc) { /* XXX EOL before delim found!!! */ fprintf(stderr, "FIX ME!\n"); return; } delim = line[cc++]; } while (delim == ' '); /* others? NL */ w = count = 0; ccase = LC; for (;;) { int c = nextfiodec(&ccase, delim); if (c == -1) break; w |= c << ((2-count)*6); if (++count == 3) { punchOutObject(clc, w); /* punch it! */ incrementClc(); count = w = 0; } } if (count > 0) { punchOutObject(clc, w); /* punch remainder */ incrementClc(); } } /* CONSTANTS pseudo-op */ void constants(void) { int i; /* XXX illegal inside macro (curmacro != NULL) */ if (pass == 1) { lit_loc[nconst] = clc; /* just use addition?! */ for (i = 0; i < lit_count[nconst]; i++) incrementClc(); nconst++; return; } /* pass 2: */ /* XXX complain if clc != lit_base[nconst]? */ for (i = 0; i < lit_count[nconst]; i++) { if (i < nlit) punchOutObject( clc, litter[i] & 0777777); /* punch it! */ incrementClc(); } nconst++; nlit = 0; /* litter[] now empty */ } /* constants */ /* process pseudo-ops * return FALSE if line scan should end (no longer used) */ BOOL pseudo( PSEUDO_T val ) { int count; int repeatstart; switch( (PSEUDO_T) val ) { case CONSTANTS: next(0); /* Skip symbol */ constants(); break; case VARIABLES: next(0); /* Skip symbol */ variables(); break; case DEFINE: next(0); /* Skip symbol */ defineMacro(); return FALSE; break; case REPEAT: next(0); /* Skip symbol */ /* NOTE!! constant followed by SPACE picked up as expression!! */ count = getExprs() & ADDRESS_FIELD; /* XXX error if sign bit set? */ /* allow comma, but do not require */ if( line[lexstart] == ',') next(0); nrepeats++; repeatstart = lexstart; /* save line start */ while (count-- > 0) { cc = repeatstart; /* reset input pointer */ processLine(); /* recurse! */ } cc = maxcc; nrepeats--; return FALSE; break; case START: next(0); /* Skip symbol */ /* XXX illegal in macro or repeat */ flushLoader(); if (!ISDONE(line[lexstart])) { if (line[lexstart] == ' ') next(0); start_addr = getExprs() & ADDRESS_FIELD; next(0); printLine( line, 0, start_addr, LINE_VAL ); /* MACRO punches 4" of leader */ punchTriplet(JMP | start_addr); /* MACRO punches 24" of leader? */ } /* * handle multiple tapes concatenated into one file!! * have command line option?? treat "start" as EOF?? */ list_title_set = FALSE; return FALSE; case TEXT: /* NOTE!! no next()! */ text(); break; case NOINPUT: next(0); /* Skip symbol */ noinput = TRUE; break; case EXPUNGE: next(0); /* Skip symbol */ if (pass == 1) init_symtab(); break; default: break; } /* end switch for pseudo-ops */ return TRUE; /* keep scanning */ } /* pseudo */ /* Function: errorLexeme */ /* Synopsis: Display an error message using the current lexical element. */ void errorLexeme( EMSG_T *mesg, WORD32 col ) { char name[SYMLEN]; errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); } /* errorLexeme */ /* Function: errorSymbol */ /* Synopsis: Display an error message with a given string. */ void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) { char linecol[12]; char *s; if( pass == 2 ) { s = ( name == NULL ) ? "" : name ; errors++; sprintf( linecol, ":%d:%d", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", filename, linecol, mesg->file, s, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorSymbol */ /* Function: errorMessage */ /* Synopsis: Display an error message without a name argument. */ void errorMessage( EMSG_T *mesg, WORD32 col ) { char linecol[12]; if( pass == 2 ) { errors++; sprintf( linecol, ":%d:%d", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", filename, linecol, mesg->file, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorMessage */ /* Function: saveError */ /* Synopsis: Save the current error in a list so it may displayed after the */ /* the current line is printed. */ void saveError( char *mesg, WORD32 col ) { if( save_error_count < DIM( error_list )) { error_list[save_error_count].mesg = mesg; error_list[save_error_count].col = col; save_error_count++; } error_in_line = TRUE; if( listed ) printErrorMessages(); } /* saveError */ /* create a "symbol punch" for DDT */ /* MUST be called after object file closed; we reuse the FILE*! */ void dump_symbols(void) { int ix; WORD32 addr; objectfile = fopen( sympathname, "wb" ); if (!objectfile) { perror(sympathname); return; } punchLeader(0); punchLoader(); punchLeader(5); /* XXX fudge addr -- get count, and subtract 2N from 07750? */ addr = 05000; for( ix = 0; ix < symbol_top; ix++ ) { int i, type; WORD32 name; type = symtab[ix].type; if (M_FIXED(type) || M_PSEUDO(type) || M_MACRO(type)) continue; name = 0; for (i = 0; i < 3; i++) { char c; c = symtab[ix].name[i]; /* XXX leave on NUL? */ c = ascii_to_fiodec[tolower(c) & 0177]; /* XXX check for BAD entries? */ /* XXX OR in val<<(3-i)*6?? */ name <<= 6; name |= c & CHARBITS; } punchLocObject(addr++, permute(name)); punchLocObject(addr++, symtab[ix].val); } flushLoader(); punchTriplet( JMP ); /* ??? */ punchLeader(0); fclose(objectfile); } simh-3.8.1/TOOLS/crossassemblers/macro7.c0000666000175000017500000034315007401213736016324 0ustar vlmvlm/******************************************************************************/ /* */ /* Program: MACRO7 */ /* File: macro7.c */ /* Author: Gary A. Messenbrink */ /* MACRO7 modifications: Bob Supnik (:) : error: at Loc = */ /* */ /* An example error message is: */ /* */ /* bintst.7(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ /* */ /* The error diagnostics put in the listing start with a two character */ /* error code (if appropriate) and a short message. A carat '^' is */ /* placed under the item in error if appropriate. */ /* An example error message is: */ /* */ /* 17 07616 3000 DAC UNDEF */ /* UD undefined ^ */ /* 18 07617 1777 TAD I DUMMY */ /* */ /* When an indirect is generated, an at character '@' is placed after the */ /* the instruction value in the listing as an indicator as follows: */ /* */ /* 14 03716 1777@ TAD OFFPAG */ /* */ /* Undefined symbols are marked in the symbol table listing by prepending */ /* a '?' to the symbol. Redefined symbols are marked in the symbol table */ /* listing by prepending a '#' to the symbol. Examples are: */ /* */ /* #REDEF 04567 */ /* SWITCH 07612 */ /* ?UNDEF 00000 */ /* */ /* Refer to the code for the diagnostic messages generated. */ /* */ /* REFERENCES: */ /* This assembler is based on the pal assember by: */ /* Douglas Jones and */ /* Rich Coon */ /* */ /* COPYRIGHT NOTICE: */ /* This is free software. There is no fee for using it. You may make */ /* any changes that you wish and also give it away. If you can make */ /* a commercial product out of it, fine, but do not put any limits on */ /* the purchaser's right to do the same. If you improve it or fix any */ /* bugs, it would be nice if you told me and offered me a copy of the */ /* new version. */ /* */ /* */ /* Amendments Record: */ /* Version Date by Comments */ /* ------- ------- --- --------------------------------------------------- */ /* v1.0 12Apr96 GAM Original */ /* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ /* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ /* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ /* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ /* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ /* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ /* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ /* v3.0 14Feb97 RMS MACRO8X features. */ /* */ /******************************************************************************/ #include #include #include #include #define LINELEN 96 #define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ #define NAMELEN 128 #define SYMBOL_COLUMNS 5 #define SYMLEN 7 #define SYMBOL_TABLE_SIZE 8192 #define MAC_MAX_ARGS 20 /* Must be < 26 */ #define MAC_MAX_LENGTH 8192 #define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ #define TITLELEN 63 #define XREF_COLUMNS 8 #define ADDRESS_FIELD 0017777 #define LIT_BASE 0017400 #define INDIRECT_BIT 0020000 #define LAST_PAGE_LOC 0017777 #define OP_CODE 0740000 /* Macro to get the number of elements in an array. */ #define DIM(a) (sizeof(a)/sizeof(a[0])) /* Macro to get the address plus one of the end of an array. */ #define BEYOND(a) ((a) + DIM(A)) #define is_blank(c) ((c==' ') || (c=='\f') || (c=='>')) #define isend(c) ((c=='\0')|| (c=='\n')) #define isdone(c) ((c=='/') || (isend(c)) || (c=='\t')) /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ /* (true) if the stated condtion is met. */ /* Use these to test attributes. The proper bits are extracted and then */ /* tested. */ #define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) #define M_DEFINED(s) ((s & DEFINED) == DEFINED) #define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) #define M_FIXED(s) ((s & FIXED) == FIXED) #define M_LABEL(s) ((s & LABEL) == LABEL) #define M_MRI(s) ((s & MRI) == MRI) #define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) #define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) #define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) #define M_MACRO(s) ((s & MACRO) == MACRO) #define M_UNDEFINED(s) (!M_DEFINED(s)) #define M_NOTRDEF(s) ((s & NOTRDEF) != 0) /* This macro is used to test symbols by the conditional assembly pseudo-ops. */ #define M_DEF(s) (M_DEFINED(s)) #define M_COND(s) (M_DEFINED(s)) #define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) typedef unsigned char BOOL; typedef unsigned char BYTE; typedef int WORD32; #ifndef FALSE #define FALSE 0 #define TRUE (!FALSE) #endif /* Line listing styles. Used to control listing of lines. */ enum linestyle_t { LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL }; typedef enum linestyle_t LINESTYLE_T; /* Symbol Types. */ /* Note that the names that have FIX as the suffix contain the FIXED bit */ /* included in the value. */ /* */ /* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ /* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ /* defined or undefined. The condition bit is set when the symbol is defined */ /* during pass 1 and reset on pass 2 at the location the symbol was defined */ /* during pass 1. When processing conditionals during pass 2, if the symbol */ /* is defined and the condition bit is set, the symbol is treated as if it */ /* were undefined. This gives consistent behavior of the conditional */ /* pseudo-ops during both pass 1 and pass 2. */ enum symtyp { UNDEFINED = 0000, DEFINED = 0001, FIXED = 0002, MRI = 0004 | DEFINED, LABEL = 0010 | DEFINED, REDEFINED = 0020 | DEFINED, DUPLICATE = 0040 | DEFINED, PSEUDO = 0100 | FIXED | DEFINED, CONDITION = 0200 | DEFINED, MACRO = 0400 | DEFINED, MRIFIX = MRI | FIXED | DEFINED, DEFFIX = DEFINED | FIXED, NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED }; typedef enum symtyp SYMTYP; enum pseudo_t { DECIMAL, DEFINE, EJECT, IFDEF, IFNDEF, IFNZERO, IFZERO, LIST, NOLIST, OCTAL, START, TEXT, TITLE, VFD }; typedef enum pseudo_t PSEUDO_T; struct sym_t { SYMTYP type; char name[SYMLEN]; WORD32 val; WORD32 xref_index; WORD32 xref_count; }; typedef struct sym_t SYM_T; struct lpool_t { WORD32 error; /* True if error message has been printed. */ WORD32 pool[LIT_BASE]; }; typedef struct lpool_t LPOOL_T; struct emsg_t { char *list; char *file; }; typedef struct emsg_t EMSG_T; struct errsave_t { char *mesg; WORD32 col; }; typedef struct errsave_t ERRSAVE_T; /*----------------------------------------------------------------------------*/ /* Function Prototypes */ int binarySearch( char *name, int start, int symbol_count ); int copyMacLine( int length, int from, int term, int nargs ); int compareSymbols( const void *a, const void *b ); void conditionFalse( void ); void conditionTrue( void ); SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); void endOfBinary( void ); void errorLexeme( EMSG_T *mesg, WORD32 col ); void errorMessage( EMSG_T *mesg, WORD32 col ); void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); SYM_T *eval( void ); SYM_T *evalSymbol( void ); void getArgs( int argc, char *argv[] ); SYM_T *getExpr( void ); WORD32 getExprs( void ); WORD32 incrementClc( void ); WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); char *lexemeToName( char *name, WORD32 from, WORD32 term ); void listLine( void ); SYM_T *lookup( char *name ); void moveToEndOfLine( void ); void nextLexBlank( void ); void nextLexeme( void ); void onePass( void ); void printCrossReference( void ); void printErrorMessages( void ); void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); void printPageBreak( void ); void printPermanentSymbolTable( void ); void printSymbolTable( void ); BOOL pseudoOperators( PSEUDO_T val ); void punchLocObject( WORD32 loc, WORD32 val ); void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); void punchOutObject( WORD32 loc, WORD32 val ); void punchLeader( WORD32 count ); void punchObject( WORD32 val ); void punchTriplet( WORD32 val ); void readLine( void ); void saveError( char *mesg, WORD32 cc ); BOOL testForLiteralCollision( WORD32 loc ); void topOfForm( char *title, char *sub_title ); /*----------------------------------------------------------------------------*/ /* Table of pseudo-ops (directives) which are used to setup the symbol */ /* table on startup and when the EXPUNGE pseudo-op is executed. */ SYM_T pseudo[] = { { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ { PSEUDO, "LIST", LIST }, /* Enable listing. */ { PSEUDO, "NOLIST", NOLIST }, /* Disable listing. */ { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ { PSEUDO, "START", START }, /* Set starting address. */ { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ { PSEUDO, "VFD", VFD }, /* Variable field definition. */ }; /* Symbol Table */ /* The table is put in lexical order on startup, so symbols can be */ /* inserted as desired into the initial table. */ #define DAC 0040000 #define JMP 0600000 SYM_T permanent_symbols[] = { /* Memory Reference Instructions */ { MRIFIX, "CAL", 0000000 }, { MRIFIX, "DAC", 0040000 }, { MRIFIX, "JMS", 0100000 }, { MRIFIX, "DZM", 0140000 }, { MRIFIX, "LAC", 0200000 }, { MRIFIX, "XOR", 0240000 }, { MRIFIX, "ADD", 0300000 }, { MRIFIX, "TAD", 0340000 }, { MRIFIX, "XCT", 0400000 }, { MRIFIX, "ISZ", 0440000 }, { MRIFIX, "AND", 0500000 }, { MRIFIX, "SAD", 0540000 }, { MRIFIX, "JMP", 0600000 }, { MRIFIX, "I", 0020000 }, { DEFFIX, "EAE", 0640000 }, { DEFFIX, "IOT", 0700000 }, { DEFFIX, "OPR", 0740000 }, { DEFFIX, "LAW", 0760000 }, { DEFFIX, "LAM", 0777777 }, /* EAE Microinstructions */ { DEFFIX, "OSC", 0640001 }, { DEFFIX, "LACS", 0641001 }, { DEFFIX, "OMQ", 0640002 }, { DEFFIX, "LACQ", 0641002 }, { DEFFIX, "CMQ", 0640004 }, { DEFFIX, "CLQ", 0650000 }, { DEFFIX, "LMQ", 0652000 }, { DEFFIX, "ABS", 0644000 }, { DEFFIX, "GSM", 0664000 }, { DEFFIX, "MUL", 0653122 }, { DEFFIX, "MULS", 0657122 }, { DEFFIX, "DIV", 0640323 }, { DEFFIX, "DIVS", 0644323 }, { DEFFIX, "IDIV", 0653323 }, { DEFFIX, "IDIVS", 0657323 }, { DEFFIX, "FRDIV", 0650323 }, { DEFFIX, "FRDIVS", 0654323 }, { DEFFIX, "NORM", 0640444 }, { DEFFIX, "NORMS", 0660444 }, { DEFFIX, "LRS", 0640500 }, { DEFFIX, "LRSS", 0660500 }, { DEFFIX, "LLS", 0640600 }, { DEFFIX, "LLSS", 0660600 }, { DEFFIX, "ALS", 0640700 }, { DEFFIX, "ALSS", 0660700 }, /* Operate Microinstructions */ { DEFFIX, "NOP", 0740000 }, { DEFFIX, "CMA", 0740001 }, { DEFFIX, "CML", 0740002 }, { DEFFIX, "OAS", 0740004 }, { DEFFIX, "LAS", 0750004 }, { DEFFIX, "RAL", 0740010 }, { DEFFIX, "RCL", 0744010 }, { DEFFIX, "RTL", 0742010 }, { DEFFIX, "RAR", 0740020 }, { DEFFIX, "RCR", 0744020 }, { DEFFIX, "RTR", 0742020 }, { DEFFIX, "HLT", 0740040 }, { DEFFIX, "XX", 0740040 }, { DEFFIX, "SMA", 0740100 }, { DEFFIX, "SZA", 0740200 }, { DEFFIX, "SNL", 0740400 }, { DEFFIX, "SKP", 0741000 }, { DEFFIX, "SPA", 0741100 }, { DEFFIX, "SNA", 0741200 }, { DEFFIX, "SZL", 0741400 }, { DEFFIX, "CLL", 0744000 }, { DEFFIX, "STL", 0744002 }, { DEFFIX, "CLA", 0750000 }, { DEFFIX, "CLC", 0750001 }, { DEFFIX, "GLK", 0750010 }, /* CPU IOT's */ { DEFFIX, "CLSF", 0700001 }, { DEFFIX, "IOF", 0700002 }, { DEFFIX, "ION", 0700042 }, { DEFFIX, "ITON", 0700062 }, { DEFFIX, "CLOF", 0700004 }, { DEFFIX, "CLON", 0700044 }, { DEFFIX, "TTS", 0703301 }, { DEFFIX, "SKP7", 0703341 }, { DEFFIX, "CAF", 0703302 }, { DEFFIX, "SEM", 0707701 }, { DEFFIX, "EEM", 0707702 }, { DEFFIX, "EMIR", 0707742 }, { DEFFIX, "LEM", 0707704 }, /* High Speed Paper Tape Reader */ { DEFFIX, "RSF", 0700101 }, { DEFFIX, "RRB", 0700112 }, { DEFFIX, "RCF", 0700102 }, { DEFFIX, "RSA", 0700104 }, { DEFFIX, "RSB", 0700144 }, /* High Speed Paper Tape Punch */ { DEFFIX, "PSF", 0700201 }, { DEFFIX, "PCF", 0700202 }, { DEFFIX, "PSA", 0700204 }, { DEFFIX, "PLS", 0700204 }, { DEFFIX, "PSB", 0700244 }, /* Keyboard */ { DEFFIX, "KSF", 0700301 }, { DEFFIX, "KRB", 0700312 }, { DEFFIX, "IORS", 0700314 }, /* Teleprinter */ { DEFFIX, "TSF", 0700401 }, { DEFFIX, "TCF", 0700402 }, { DEFFIX, "TLS", 0700406 }, /* Line Printer */ { DEFINED, "LPSF", 0706501 }, { DEFINED, "LPCB", 0706502 }, { DEFINED, "LPB1", 0706566 }, { DEFINED, "LPB2", 0706526 }, { DEFINED, "LPB3", 0706546 }, { DEFINED, "LPSE", 0706601 }, { DEFINED, "LPCF", 0706602 }, { DEFINED, "LPPB", 0706606 }, { DEFINED, "LPLS", 0706626 }, { DEFINED, "LPPS", 0706646 }, /* Card Reader */ { DEFFIX, "CRSF", 0706701 }, { DEFFIX, "CRRB", 0706712 }, { DEFFIX, "CRSA", 0706704 }, { DEFFIX, "CRSB", 0706744 }, /* DECtape */ { DEFFIX, "MMDF", 0707501 }, { DEFFIX, "MMEF", 0707541 }, { DEFFIX, "MMRD", 0707512 }, { DEFFIX, "MMWR", 0707504 }, { DEFFIX, "MMBF", 0707601 }, { DEFFIX, "MMRS", 0707612 }, { DEFFIX, "MMLC", 0707604 }, { DEFFIX, "MMSE", 0707644 }, }; /* End-of-Symbols for Permanent Symbol Table */ /* Global variables */ SYM_T *symtab; /* Symbol Table */ int symbol_top; /* Number of entries in symbol table. */ SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ int number_of_fixed_symbols; /*----------------------------------------------------------------------------*/ WORD32 *xreftab; /* Start of the concordance table. */ ERRSAVE_T error_list[20]; int save_error_count; LPOOL_T cp; /* Storage for current page constants. */ char s_detected[] = "detected"; char s_error[] = "error"; char s_errors[] = "errors"; char s_no[] = "No"; char s_page[] = "Page"; char s_symtable[] = "Symbol Table"; char s_xref[] = "Cross Reference"; /* Assembler diagnostic messages. */ /* Some attempt has been made to keep continuity with the PAL-III and */ /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ /* exists, then the indicator is put in the listing as the first two */ /* characters of the diagnostic message. The PAL-III indicators where used */ /* when there was a choice between using MACRO-8 and PAL-III indicators. */ /* The character pairs and their meanings are: */ /* DT Duplicate Tag (symbol) */ /* IC Illegal Character */ /* ID Illegal Redefinition of a symbol. An attempt was made to give */ /* a symbol a new value not via =. */ /* IE Illegal Equals An equal sign was used in the wrong context, */ /* (e.g., A+B=C, or TAD A+=B) */ /* II Illegal Indirect An off page reference was made, but a literal */ /* could not be generated because the indirect bit was already set. */ /* IR Illegal Reference (address is not on current page or page zero) */ /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ /* RD ReDefintion of a symbol */ /* ST Symbol Table full */ /* UA Undefined Address (undefined symbol) */ /* ZE Zero Page Exceeded (see above, or out of space) */ EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; EMSG_T illegal_character = { "IC illegal char", "illegal character" }; EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; EMSG_T label_syntax = { "IC label syntax", "label syntax" }; EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; EMSG_T illegal_reference = { "IR off page", "illegal reference" }; EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; EMSG_T literal_gen_off = { "lit generation off", "literal generation disabled" }; EMSG_T literal_overflow = { "PE page exceeded", "current page literal capacity exceeded" }; EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; EMSG_T illegal_vfd_value = { "width out of range", "VFD field width not in range" }; EMSG_T no_literal_value = { "no value", "No literal value" }; EMSG_T text_string = { "no delimiter", "Text string delimiters not matched" }; EMSG_T lt_expected = { "'<' expected", "'<' expected" }; EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; EMSG_T bad_dummy_arg = { "bad dummy arg", "Bad dummy argument following DEFINE" }; EMSG_T macro_too_long = { "macro too long", "Macro too long" }; EMSG_T no_virtual_memory = { "out of memory", "Insufficient memory for macro" }; EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; /*----------------------------------------------------------------------------*/ FILE *errorfile; FILE *infile; FILE *listfile; FILE *listsave; FILE *objectfile; FILE *objectsave; char errorpathname[NAMELEN]; char filename[NAMELEN]; char listpathname[NAMELEN]; char objectpathname[NAMELEN]; char *pathname; char permpathname[NAMELEN]; char mac_buffer[MAC_MAX_LENGTH + 1]; char *mac_bodies[MAC_TABLE_LENGTH]; char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; int mac_arg_pos[26] = { 0 }; int list_lineno; int list_pageno; char list_title[4*LINELEN]; BOOL list_title_set; /* Set if TITLE pseudo-op used. */ char line[4*LINELEN]; /* Input line. */ int lineno; /* Current line number. */ char mac_line[4*LINELEN]; /* Saved macro invocation line. */ int page_lineno; /* print line number on current page. */ WORD32 listed; /* Listed flag. */ WORD32 listedsave; WORD32 cc; /* Column Counter (char position in line). */ WORD32 checksum; /* Generated checksum */ BOOL binary_data_output; /* Set true when data has been output. */ WORD32 clc; /* Location counter */ char delimiter; /* Character immediately after eval'd term. */ BOOL end_of_input; /* End of all input files. */ int errors; /* Number of errors found so far. */ BOOL error_in_line; /* TRUE if error on current line. */ int errors_pass_1; /* Number of errors on pass 1. */ int filix_curr; /* Index in argv to current input file. */ int filix_start; /* Start of input files in argv. */ BOOL indirect_generated; /* TRUE if an off page address generated. */ WORD32 lexstartprev; /* Where previous lexeme started. */ WORD32 lextermprev; /* Where previous lexeme ended. */ WORD32 lexstart; /* Index of current lexeme on line. */ WORD32 lexterm; /* Index of character after current lexeme. */ WORD32 lit_loc; /* Base of literal pool. */ WORD32 mac_cc; /* Saved cc after macro invocation. */ WORD32 mac_count; /* Total macros defined. */ char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ WORD32 maxcc; /* Current line length. */ BOOL nomac_exp; /* No macro expansions. */ WORD32 pass; /* Number of current pass. */ BOOL print_permanent_symbols; WORD32 radix; /* Default number radix. */ BOOL rim_mode; /* RIM mode output. */ int save_argc; /* Saved argc. */ char **save_argv; /* Saved *argv[]. */ WORD32 start_addr; /* Saved start address. */ BOOL symtab_print; /* Print symbol table flag */ BOOL xref; SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ /******************************************************************************/ /* */ /* Function: main */ /* */ /* Synopsis: Starting point. Controls order of assembly. */ /* */ /******************************************************************************/ int main( int argc, char *argv[] ) { int ix; int space; save_argc = argc; save_argv = argv; /* Set the default values for global symbols. */ binary_data_output = FALSE; print_permanent_symbols = FALSE; nomac_exp = TRUE; rim_mode = TRUE; symtab_print = FALSE; xref = FALSE; pathname = NULL; for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) { mac_bodies[ix] = NULL; } /* Get the options and pathnames */ getArgs( argc, argv ); /* Setup the error file in case symbol table overflows while installing the */ /* permanent symbols. */ errorfile = fopen( errorpathname, "w" ); errors = 0; save_error_count = 0; pass = 0; /* This is required for symbol table initialization. */ symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); if( symtab == NULL ) { fprintf( stderr, "Could not allocate memory for symbol table.\n"); exit( -1 ); } /* Place end marker in symbol table. */ symtab[0] = sym_undefined; symbol_top = 0; number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Enter the pseudo-ops into the symbol table */ for( ix = 0; ix < DIM( pseudo ); ix++ ) { defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); } /* Enter the predefined symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) { defineSymbol( permanent_symbols[ix].name, permanent_symbols[ix].val, permanent_symbols[ix].type, 0 ); } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Do pass one of the assembly */ checksum = 0; pass = 1; onePass(); errors_pass_1 = errors; /* Set up for pass two */ errorfile = fopen( errorpathname, "w" ); objectfile = fopen( objectpathname, "wb" ); objectsave = objectfile; listfile = fopen( listpathname, "w" ); listsave = listfile; punchLeader( 0 ); checksum = 0; /* Do pass two of the assembly */ errors = 0; save_error_count = 0; if( xref ) { /* Get the amount of space that will be required for the concordance. */ for( space = 0, ix = 0; ix < symbol_top; ix++ ) { symtab[ix].xref_index = space; /* Index into concordance table. */ space += symtab[ix].xref_count + 1; symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ } /* Allocate the necessary space. */ xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); /* Clear the cross reference space. */ for( ix = 0; ix < space; ix++ ) { xreftab[ix] = 0; } } pass = 2; onePass(); /* Undo effects of NOPUNCH for any following checksum */ objectfile = objectsave; /* Works great for trailer. */ punchLeader( 1 ); /* undo effects of NOLIST for any following output to listing file. */ listfile = listsave; /* Display value of error counter. */ if( errors == 0 ) { fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); } else { fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( listfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( stderr, " %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); } if( symtab_print ) { printSymbolTable(); } if( print_permanent_symbols ) { printPermanentSymbolTable(); } if( xref ) { printCrossReference(); } fclose( objectfile ); fclose( listfile ); fclose( errorfile ); if( errors == 0 && errors_pass_1 == 0 ) { remove( errorpathname ); } return( errors != 0 ); } /* main() */ /******************************************************************************/ /* */ /* Function: getArgs */ /* */ /* Synopsis: Parse command line, set flags accordingly and setup input and */ /* output files. */ /* */ /******************************************************************************/ void getArgs( int argc, char *argv[] ) { WORD32 len; WORD32 ix, jx; /* Set the defaults */ errorfile = NULL; infile = NULL; listfile = NULL; listsave = NULL; objectfile = NULL; objectsave = NULL; for( ix = 1; ix < argc; ix++ ) { if( argv[ix][0] == '-' ) { for( jx = 1; argv[ix][jx] != 0; jx++ ) { switch( argv[ix][jx] ) { case 'd': symtab_print = TRUE; break; /* case 'r': rim_mode = TRUE; break; */ case 'm': nomac_exp = FALSE; break; case 'p': print_permanent_symbols = TRUE; break; case 'x': xref = TRUE; break; default: fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); fprintf( stderr, " -d -- dump symbol table\n" ); fprintf( stderr, " -m -- output macro expansions\n" ); /* fprintf( stderr, " -r -- output rim format file\n" ); */ fprintf( stderr, " -p -- output permanent symbols to file\n" ); fprintf( stderr, " -x -- output cross reference to file\n" ); fflush( stderr ); exit( -1 ); } /* end switch */ } /* end for */ } else { filix_start = ix; pathname = argv[ix]; break; } } /* end for */ if( pathname == NULL ) { fprintf( stderr, "%s: no input file specified\n", argv[0] ); exit( -1 ); } len = strlen( pathname ); if( len > NAMELEN - 5 ) { fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); exit( -1 ); } /* Now make the pathnames */ /* Find last '.', if it exists. */ jx = len - 1; while( pathname[jx] != '.' && pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } switch( pathname[jx] ) { case '.': break; case '/': case '\\': jx = len; break; default: break; } /* Add the pathname extensions. */ strncpy( objectpathname, pathname, jx ); objectpathname[jx] = '\0'; strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); strncpy( listpathname, pathname, jx ); listpathname[jx] = '\0'; strcat( listpathname, ".lst" ); strncpy( errorpathname, pathname, jx ); errorpathname[jx] = '\0'; strcat( errorpathname, ".err" ); strncpy( permpathname, pathname, jx ); permpathname[jx] = '\0'; strcat( permpathname, ".prm" ); /* Extract the filename from the path. */ if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) { pathname[1] = '\\'; /* MS-DOS style pathname */ } jx = len - 1; while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } strcpy( filename, &pathname[jx + 1] ); } /* getArgs() */ /******************************************************************************/ /* */ /* Function: onePass */ /* */ /* Synopsis: Do one assembly pass. */ /* */ /******************************************************************************/ void onePass() { BOOL blanks; int ix; int jx; char name[SYMLEN]; WORD32 newclc; BOOL scanning_line; WORD32 start; SYM_T *sym; WORD32 term; WORD32 val; clc = 0100; /* Default starting address is 100 octal. */ start_addr = 0100; /* No starting address. */ lit_loc = LIT_BASE; /* Literal pool base. */ mac_count = 0; /* No macros defined. */ mac_ptr = NULL; /* Not in a macro. */ for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) { if ( mac_bodies[ix] ) { free( mac_bodies[ix] ); } } cp.error = FALSE; listed = TRUE; lineno = 0; list_pageno = 0; list_lineno = 0; list_title_set = FALSE; page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ radix = 8; /* Initial radix is octal (base 8). */ /* Now open the first input file. */ end_of_input = FALSE; filix_curr = filix_start; /* Initialize pointer to input files. */ if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } while( TRUE ) { readLine(); nextLexeme(); scanning_line = TRUE; while( scanning_line ) { if( end_of_input ) { endOfBinary(); fclose( infile ); return; } if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case '\t': nextLexeme(); break; default: for( jx = lexstart; jx < maxcc; jx++ ) { if( is_blank( line[jx] ) || isdone( line[jx] )) break; } if( line[jx] == '/') { newclc = (getExpr())->val & 077777; /* Do not change Current Location Counter if an error occurred. */ if( !error_in_line ) { clc = newclc; } printLine( line, 0, newclc, LINE_VAL ); cc = jx + 1; nextLexeme(); while( line[lexstart] == '\t' ) nextLexeme(); break; } switch( line[lexterm] ) { case ',': if( isalpha( line[lexstart] )) { /* Use lookup so symbol will not be counted as reference. */ sym = lookup( lexemeToName( name, lexstart, lexterm )); if( M_DEFINED( sym->type )) { if( sym->val != clc && pass == 2 ) { errorSymbol( &duplicate_label, sym->name, lexstart ); } sym->type = sym->type | DUPLICATE; } /* Must call define on pass 2 to generate concordance. */ defineLexeme( lexstart, lexterm, clc, LABEL ); } else { errorLexeme( &label_syntax, lexstart ); } nextLexeme(); /* skip label */ nextLexeme(); /* skip comma */ while( line[lexstart] == '\t' ) nextLexeme(); break; case '=': if( isalpha( line[lexstart] )) { start = lexstart; term = lexterm; delimiter = line[lexterm]; nextLexBlank(); /* skip symbol */ nextLexeme(); /* skip trailing = */ val = getExprs(); defineLexeme( start, term, val, DEFINED ); printLine( line, 0, val, LINE_VAL ); } else { errorLexeme( &symbol_syntax, lexstartprev ); nextLexeme(); /* skip symbol */ nextLexeme(); /* skip trailing = */ getExprs(); /* skip expression */ } while( line[lexstart] == '\t' ) nextLexeme(); break; default: if( isalpha( line[lexstart] )) { sym = evalSymbol(); val = sym->val; if( M_MACRO( sym->type )) { /* Find arguments. */ blanks = TRUE; /* Expecting blanks. */ for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) { if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; else if( blanks ) { mac_arg_pos[jx++] = cc; blanks = FALSE; } } /* end for */ for( ; jx < MAC_MAX_ARGS; jx++ ) { mac_arg_pos[jx] = 0; } for( jx = 0; jx < LINELEN; jx++ ) { mac_line[jx] = line[jx]; } mac_cc = cc; /* Save line and position in line. */ mac_ptr = mac_bodies[val]; if( mac_ptr ) scanning_line = FALSE; else nextLexeme(); } /* end if macro */ else if( M_PSEUDO( sym->type )) { nextLexeme(); /* Skip symbol */ scanning_line = pseudoOperators( (PSEUDO_T)val & 0777777 ); } else { /* Identifier is not a pseudo-op, interpret as load value */ punchOutObject( clc, getExprs() & 0777777 ); incrementClc(); } } else { /* Identifier is a value, interpret as load value */ punchOutObject( clc, getExprs() & 0777777 ); incrementClc(); } break; } /* end switch */ break; } /* end switch */ } /* end if */ } /* end while( scanning_line ) */ } /* end while( TRUE ) */ } /* onePass() */ /******************************************************************************/ /* */ /* Function: getExprs */ /* */ /* Synopsis: Or together a list of blank separated expressions, from the */ /* current lexeme onward. Leave the current lexeme as */ /* the last one in the list. */ /* */ /******************************************************************************/ WORD32 getExprs() { SYM_T *symv; SYM_T *symt; WORD32 temp; SYMTYP temp_type; WORD32 value; SYMTYP value_type; symv = getExpr(); value = symv->val; value_type = symv->type; while( TRUE ) { if( isdone( line[lexstart] ) || line[lexstart] == ')' ) { return( value ); } /* Interpret space as add */ symt = getExpr(); temp = symt->val & 0777777; temp_type = symt->type; switch( value_type ) { case MRI: case MRIFIX: /* Previous symbol was a Memory Reference Instruction. */ switch( temp_type ) { case MRI: case MRIFIX: /* Current symbol is also a Memory Reference Instruction. */ value |= temp; /* Just OR the MRI instructions. */ break; default: /* Now have the address part of the MRI instruction. */ if(( clc & 060000) == ( temp & 060000)) { value += ( temp & ADDRESS_FIELD ); /* In range MRI. */ } else { if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) { /* Already indirect, can't generate */ errorSymbol( &illegal_indirect, symt->name, lexstartprev ); } else { /* Now fix off page reference. */ /* Search current page literal pool for needed value. */ /* Set Indirect */ value += ( INDIRECT_BIT | insertLiteral( &cp, clc, temp & 077777)); indirect_generated = TRUE; } } break; } break; default: value = value + temp; /* Normal 18 bit value. */ if( value >= 0777777 ) value = ( value + 1 ) & 0777777; break; } } /* end while */ } /* getExprs() */ /******************************************************************************/ /* */ /* Function: getExpr */ /* */ /* Synopsis: Get an expression, from the current lexeme onward, leave the */ /* current lexeme as the one after the expression. Expressions */ /* contain terminal symbols (identifiers) separated by operators. */ /* */ /******************************************************************************/ SYM_T *getExpr() { delimiter = line[lexterm]; if( line[lexstart] == '-' ) { nextLexBlank(); sym_getexpr = *(eval()); sym_getexpr.val = sym_getexpr.val ^ 0777777; } else { sym_getexpr = *(eval()); } if( is_blank( delimiter )) { return( &sym_getexpr ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '+': /* add */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val += (eval())->val; if( sym_getexpr.val >= 01000000 ) { sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; } break; case '-': /* subtract */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val = sym_getexpr.val + ( (eval())->val ^ 0777777 ); if( sym_getexpr.val >= 01000000 ) { sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; } break; case '^': /* multiply */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val *= (eval())->val; break; case '%': /* divide */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val /= (eval())->val; break; case '&': /* and */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val &= (eval())->val; break; case '!': /* or */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val |= (eval())->val; break; default: if( isend( line[lexstart] )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '/': case '\t': case ')': case '<': case ':': case ',': break; case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; } return( &sym_getexpr ); } } /* end while */ } /* getExpr() */ /******************************************************************************/ /* */ /* Function: eval */ /* */ /* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ /* */ /******************************************************************************/ SYM_T *eval() { WORD32 digit; WORD32 from; WORD32 loc; SYM_T *sym; WORD32 val; val = 0; delimiter = line[lexterm]; if( isalpha( line[lexstart] )) { sym = evalSymbol(); if( M_UNDEFINED( sym->type )) { if( pass == 2 ) { errorSymbol( &undefined_symbol, sym->name, lexstart ); } nextLexeme(); return( sym ); } else if( M_PSEUDO( sym->type )) { if( sym->val == DECIMAL ) { radix = 10; } else if( sym->val == OCTAL ) { radix = 8; } else if( pass == 2 ) { errorSymbol( &misplaced_symbol, sym->name, lexstart ); } sym_eval.type = sym->type; sym_eval.val = 0; nextLexeme(); return( &sym_eval ); } else if( M_MACRO( sym->type )) { if( pass == 2 ) { errorSymbol( &misplaced_symbol, sym->name, lexstart ); } sym_eval.type = sym->type; sym_eval.val = 0; nextLexeme(); return( &sym_eval ); } else { nextLexeme(); return( sym ); } } else if( isdigit( line[lexstart] )) { from = lexstart; val = 0; while( from < lexterm ) { if( isdigit( line[from] )) { digit = (WORD32) line[from++] - (WORD32) '0'; if( digit < radix ) { val = val * radix + digit; } else { errorLexeme( &number_not_radix, from - 1 ); val = 0; from = lexterm; } } else { errorLexeme( ¬_a_number, lexstart ); val = 0; from = lexterm; } } nextLexeme(); sym_eval.val = val; return( &sym_eval ); } else { switch( line[lexstart] ) { case '"': /* Character literal */ if( cc + 2 < maxcc ) { val = line[lexstart + 1] | 0200; delimiter = line[lexstart + 2]; cc = lexstart + 2; } else { errorMessage( &no_literal_value, lexstart ); } nextLexeme(); break; case '.': /* Value of Current Location Counter */ val = clc; nextLexeme(); break; case '(': /* Generate literal on current page. */ nextLexBlank(); /* Skip paren */ val = getExprs() & 0777777; if( line[lexstart] == ')' ) { delimiter = line[lexterm]; nextLexeme(); /* Skip end paren */ } else { /* errorMessage( "parens", NULL ); */ } loc = insertLiteral( &cp, clc, val ); sym_eval.val = loc + ( clc & 060000 ); return( &sym_eval ); default: switch( line[lexstart] ) { case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); break; default: errorMessage( &illegal_character, lexstart ); break; } val = 0; /* On error, set value to zero. */ nextLexBlank(); /* Go past illegal character. */ } } sym_eval.val = val; return( &sym_eval ); } /* eval() */ /******************************************************************************/ /* */ /* Function: incrementClc */ /* */ /* Synopsis: Set the next assembly location. Test for collision with */ /* the literal tables. */ /* */ /******************************************************************************/ WORD32 incrementClc() { testForLiteralCollision( clc ); clc = (( clc + 1 ) & ADDRESS_FIELD ); return( clc ); } /* incrementClc() */ /******************************************************************************/ /* */ /* Function: testForLiteralCollision */ /* */ /* Synopsis: Test the given location for collision with the literal tables. */ /* */ /******************************************************************************/ BOOL testForLiteralCollision( WORD32 loc ) { WORD32 pagelc; BOOL result = FALSE; pagelc = loc & ADDRESS_FIELD; if( ( pagelc >= lit_loc ) && ( lit_loc != LIT_BASE ) && !cp.error ) { errorMessage( &literal_overflow, -1 ); cp.error = TRUE; result = TRUE; } return( result ); } /* testForLiteralCollision() */ /******************************************************************************/ /* */ /* Function: readLine */ /* */ /* Synopsis: Get next line of input. Print previous line if needed. */ /* */ /******************************************************************************/ void readLine() { BOOL ffseen; WORD32 ix; WORD32 iy; char mc; char inpline[4*LINELEN]; listLine(); /* List previous line if needed. */ indirect_generated = FALSE; /* Mark no indirect address generated. */ error_in_line = FALSE; /* No error in line. */ if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ { mac_ptr = NULL; for( ix = 0; ix < LINELEN; ix++ ) { line[ix] = mac_line[ix]; /* Restore invoking line. */ } cc = lexstartprev = mac_cc; /* Restore cc. */ maxcc = strlen( line ); /* Restore maxcc. */ listed = TRUE; /* Already listed. */ return; } cc = 0; /* Initialize column counter. */ lexstartprev = 0; if( mac_ptr ) /* Inside macro? */ { maxcc = 0; do { mc = *mac_ptr++; /* Next character. */ if( islower( mc )) /* Encoded argument number? */ { ix = mc - 'a'; /* Convert to index. */ if( iy = mac_arg_pos[ix] ) { do /* Copy argument string. */ { line[maxcc++] = mac_line[iy++]; } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && ( !isdone( mac_line[iy] ))); } } else /* Ordinary character, just copy. */ { line[maxcc++] = mc; } } while( !isend( mc )); line[maxcc] = '\0'; listed = nomac_exp; return; } lineno++; /* Count lines read. */ listed = FALSE; /* Mark as not listed. */ READ_LINE: if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { filix_curr++; /* Advance to next file. */ if( filix_curr < save_argc ) /* More files? */ { fclose( infile ); if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } goto READ_LINE; } else { end_of_input = TRUE; } } ffseen = FALSE; for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { if( inpline[ix] == '\f' ) { if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); ffseen = TRUE; } else { line[iy++] = inpline[ix]; } } line[iy] = '\0'; /* If the line is terminated by CR-LF, remove, the CR. */ if( line[iy - 2] == '\r' ) { iy--; line[iy - 1] = line[iy - 0]; line[iy] = '\0'; } maxcc = iy; /* Save the current line length. */ } /* readLine() */ /******************************************************************************/ /* */ /* Function: listLine */ /* */ /* Synopsis: Output a line to the listing file. */ /* */ /******************************************************************************/ void listLine() /* generate a line of listing if not already done! */ { if( listfile != NULL && listed == FALSE ) { printLine( line, 0, 0, LINE ); } } /* listLine() */ /******************************************************************************/ /* */ /* Function: printPageBreak */ /* */ /* Synopsis: Output a Top of Form and listing header if new page necessary. */ /* */ /******************************************************************************/ void printPageBreak() { if( page_lineno >= LIST_LINES_PER_PAGE ) /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ { if( !list_title_set ) { strcpy( list_title, line ); if( list_title[strlen(list_title) - 1] == '\n' ) { list_title[strlen(list_title) - 1] = '\0'; } if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } list_title_set = TRUE; } topOfForm( list_title, NULL ); } } /* printPageBreak() */ /******************************************************************************/ /* */ /* Function: printLine */ /* */ /* Synopsis: Output a line to the listing file with new page if necessary. */ /* */ /******************************************************************************/ void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) { if( listfile == NULL ) { save_error_count = 0; return; } printPageBreak(); list_lineno++; page_lineno++; switch( linestyle ) { default: case LINE: fprintf( listfile, "%5d ", lineno ); fputs( line, listfile ); listed = TRUE; break; case LINE_VAL: if( !listed ) { fprintf( listfile, "%5d %6.6o ", lineno, val ); fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %6.6o\n", val ); } break; case LINE_LOC_VAL: if( !listed ) { if( indirect_generated ) { fprintf( listfile, "%5d %5.5o %6.6o@ ", lineno, loc, val ); } else { fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); } fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %5.5o %6.6o\n", loc, val ); } break; case LOC_VAL: fprintf( listfile, " %5.5o %6.6o\n", loc, val ); break; } printErrorMessages(); } /* printLine() */ /******************************************************************************/ /* */ /* Function: printErrorMessages */ /* */ /* Synopsis: Output any error messages from the current list of errors. */ /* */ /******************************************************************************/ void printErrorMessages() { WORD32 ix; WORD32 iy; if( listfile != NULL ) { /* If any errors, display them now. */ for( iy = 0; iy < save_error_count; iy++ ) { printPageBreak(); fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); if( error_list[iy].col >= 0 ) { for( ix = 0; ix < error_list[iy].col; ix++ ) { if( line[ix] == '\t' ) { putc( '\t', listfile ); } else { putc( ' ', listfile ); } } fputs( "^", listfile ); list_lineno++; page_lineno++; } fputs( "\n", listfile ); } } save_error_count = 0; } /* printErrorMessages() */ /******************************************************************************/ /* */ /* Function: endOfBinary */ /* */ /* Synopsis: Outputs both literal tables at the end of a binary segment. */ /* */ /******************************************************************************/ void endOfBinary() { punchLiteralPool( &cp, clc - 1 ); if( start_addr >= 0) { punchTriplet( JMP | ( start_addr & 017777 )); punchTriplet( 0 ); } return; } /* endOfBinary() */ /******************************************************************************/ /* */ /* Function: punchLeader */ /* */ /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ /* documentation. Paper tape has 10 punches per inch. */ /* */ /******************************************************************************/ void punchLeader( WORD32 count ) { WORD32 ix; /* If value is zero, set to the default of 2 feet of leader. */ count = ( count == 0 ) ? 240 : count; if( objectfile != NULL ) { for( ix = 0; ix < count; ix++ ) { fputc( 0, objectfile ); } } } /* punchLeader() */ /******************************************************************************/ /* */ /* Function: punchObject */ /* */ /* Synopsis: Put one character to object file and include it in checksum. */ /* */ /******************************************************************************/ void punchObject( WORD32 val ) { val &= 0377; if( objectfile != NULL ) { fputc( val, objectfile ); } checksum += val; binary_data_output = TRUE; } /* punchObject() */ /******************************************************************************/ /* */ /* Function: punchOutObject */ /* */ /* Synopsis: Output the current line and then then punch value to the */ /* object file. */ /* */ /******************************************************************************/ void punchOutObject( WORD32 loc, WORD32 val ) { printLine( line, loc, val, LINE_LOC_VAL ); punchLocObject( loc, val ); } /* punchOutObject() */ /******************************************************************************/ /* */ /* Function: punchLocObject */ /* */ /* Synopsis: Output the word (with origin if rim format) to the object file.*/ /* */ /******************************************************************************/ void punchLocObject( WORD32 loc, WORD32 val ) { punchTriplet( DAC | loc ); punchTriplet( val ); } /* punchLocObject() */ /******************************************************************************/ /* */ /* Function: punchTriplet */ /* */ /* Synopsis: Output 18b word as three 6b characters with ho bit set. */ /* */ /******************************************************************************/ void punchTriplet( WORD32 val ) { punchObject((( val >> 12) & 077) | 0200 ); punchObject((( val >> 6 ) & 077) | 0200 ); punchObject(( val & 077) | 0200 ); } /* punchTriplet */ /******************************************************************************/ /* */ /* Function: punchLiteralPool */ /* */ /* Synopsis: Output the current page data. */ /* */ /******************************************************************************/ void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) { WORD32 loc; WORD32 tmplc; if( lit_loc < LIT_BASE ) { for( loc = lit_loc; loc < LIT_BASE; loc++ ) { tmplc = loc + ( lpool_page & 060000 ); printLine( line, tmplc, p->pool[loc], LOC_VAL ); punchLocObject( tmplc, p->pool[loc] ); } p->error = FALSE; lit_loc = LIT_BASE; } } /* punchLiteralPool() */ /******************************************************************************/ /* */ /* Function: insertLiteral */ /* */ /* Synopsis: Add a value to the given literal pool if not already in pool. */ /* Return the location of the value in the pool. */ /* */ /******************************************************************************/ WORD32 insertLiteral( LPOOL_T *p, WORD32 pool_page, WORD32 value ) { WORD32 ix; /* Search the literal pool for any occurence of the needed value. */ ix = LIT_BASE - 1; while( ix >= lit_loc && p->pool[ix] != value ) { ix--; } /* Check if value found in literal pool. If not, then insert value. */ if( ix < lit_loc ) { lit_loc--; p->pool[lit_loc] = value; ix = lit_loc; } return( ix ); } /* insertLiteral() */ /******************************************************************************/ /* */ /* Function: printSymbolTable */ /* */ /* Synopsis: Output the symbol table. */ /* */ /******************************************************************************/ void printSymbolTable() { int col; int cx; char *fmt; int ix; char mark; int page; int row; int symbol_base; int symbol_lines; symbol_base = number_of_fixed_symbols; for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) { topOfForm( list_title, s_symtable ); symbol_lines = LIST_LINES_PER_PAGE - page_lineno; for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) { list_lineno++; page_lineno++; fprintf( listfile, "%5d", list_lineno ); for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) { /* Get index of symbol for the current line and column */ cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; cx += symbol_base; /* Make sure that there is a symbol to be printed. */ if( number_of_fixed_symbols <= cx && cx < symbol_top ) { switch( symtab[cx].type & LABEL ) { case LABEL: fmt = " %c%-6.6s %5.5o "; break; default: fmt = " %c%-6.6s %4.4o "; break; } switch( symtab[cx].type & ( DEFINED | REDEFINED )) { case UNDEFINED: mark = '?'; break; case REDEFINED: mark = '#'; break; default: mark = ' '; break; } fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); ix++; } } fprintf( listfile, "\n" ); } } } /* printSymbolTable() */ /******************************************************************************/ /* */ /* Function: printPermanentSymbolTable */ /* */ /* Synopsis: Output the permanent symbol table to a file suitable for */ /* being input after the EXPUNGE pseudo-op. */ /* */ /******************************************************************************/ void printPermanentSymbolTable() { int ix; FILE *permfile; char *s_type; if(( permfile = fopen( permpathname, "w" )) == NULL ) { exit( 2 ); } fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); fprintf( permfile, " EXPUNGE\n/\n" ); /* Print the memory reference instructions first. */ s_type = "FIXMRI"; for( ix = 0; ix < symbol_top; ix++ ) { if( M_MRI( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } s_type = " "; for( ix = 0; ix < symbol_top; ix++ ) { if( M_FIXED( symtab[ix].type )) { if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } } fprintf( permfile, "/\n FIXTAB\n" ); fclose( permfile ); } /* printPermanentSymbolTable() */ /******************************************************************************/ /* */ /* Function: printCrossReference */ /* */ /* Synopsis: Output a cross reference (concordance) for the file being */ /* assembled. */ /* */ /******************************************************************************/ void printCrossReference() { int ix; int symbol_base; int xc; int xc_index; int xc_refcount; int xc_cols; /* Force top of form for first page. */ page_lineno = LIST_LINES_PER_PAGE; list_lineno = 0; symbol_base = number_of_fixed_symbols; for( ix = symbol_base; ix < symbol_top; ix++ ) { list_lineno++; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref ); } fprintf( listfile, "%5d", list_lineno ); /* Get reference count & index into concordance table for this symbol. */ xc_refcount = symtab[ix].xref_count; xc_index = symtab[ix].xref_index; /* Determine how to label symbol on concordance. */ switch( symtab[ix].type & ( DEFINED | REDEFINED )) { case UNDEFINED: fprintf( listfile, " U "); break; case REDEFINED: fprintf( listfile, " M %5d ", xreftab[xc_index] ); break; default: fprintf( listfile, " A %5d ", xreftab[xc_index] ); break; } fprintf( listfile, "%-6.6s ", symtab[ix].name ); /* Output the references, 8 numbers per line after symbol name. */ for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { if( xc_cols >= XREF_COLUMNS ) { xc_cols = 0; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref); } list_lineno++; fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); } fprintf( listfile, " %5d", xreftab[xc_index + xc] ); } fprintf( listfile, "\n" ); } } /* printCrossReference() */ /******************************************************************************/ /* */ /* Function: topOfForm */ /* */ /* Synopsis: Prints title and sub-title on top of next page of listing. */ /* */ /******************************************************************************/ void topOfForm( char *title, char *sub_title ) { char temp[10]; list_pageno++; strcpy( temp, s_page ); sprintf( temp, "%s %d", s_page, list_pageno ); /* Output a top of form if not the first page of the listing. */ if( list_pageno > 1 ) { fprintf( listfile, "\f" ); } fprintf( listfile, "\n %-63s %10s\n", title, temp ); /* Reset the current page line counter. */ page_lineno = 1; if( sub_title != NULL ) { fprintf( listfile, "%80s\n", sub_title ); page_lineno++; } else { fprintf( listfile, "\n" ); page_lineno++; } fprintf( listfile, "\n" ); page_lineno++; } /* topOfForm() */ /******************************************************************************/ /* */ /* Function: lexemeToName */ /* */ /* Synopsis: Convert the current lexeme into a string. */ /* */ /******************************************************************************/ char *lexemeToName( char *name, WORD32 from, WORD32 term ) { WORD32 to; to = 0; while( from < term && to < ( SYMLEN - 1 )) { name[to++] = toupper( line[from++] ); } while( to < SYMLEN ) { name[to++] = '\0'; } return( name ); } /* lexemeToName() */ /******************************************************************************/ /* */ /* Function: defineLexeme */ /* */ /* Synopsis: Put lexeme into symbol table with a value. */ /* */ /******************************************************************************/ SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ WORD32 term, /* end+1 of lexeme being defined. */ WORD32 val, /* value of lexeme being defined. */ SYMTYP type ) /* how symbol is being defined. */ { char name[SYMLEN]; lexemeToName( name, start, term); return( defineSymbol( name, val, type, start )); } /* defineLexeme() */ /******************************************************************************/ /* */ /* Function: defineSymbol */ /* */ /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ /* not already in table. */ /* */ /******************************************************************************/ SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) { SYM_T *sym; WORD32 xref_count; if( strlen( name ) < 1 ) { return( &sym_undefined ); /* Protect against non-existent names. */ } sym = lookup( name ); xref_count = 0; /* Set concordance for normal defintion. */ if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) { if( pass == 2 ) { errorSymbol( &redefined_symbol, sym->name, start ); type = type | REDEFINED; sym->xref_count++; /* Referenced symbol, count it. */ xref_count = sym->xref_count; } return ( sym ); } if( pass == 2 && xref ) { /* Put the definition line number in the concordance table. */ /* Defined symbols are not counted as references. */ xreftab[sym->xref_index] = lineno; /* Put the line number in the concordance table. */ xreftab[sym->xref_index + xref_count] = lineno; } /* Now set the value and the type. */ sym->val = ( type == LABEL) ? val : val & 0777777; sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; return( sym ); } /* defineSymbol() */ /******************************************************************************/ /* */ /* Function: lookup */ /* */ /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ /* table as undefined. Return address of symbol in table. */ /* */ /******************************************************************************/ SYM_T *lookup( char *name ) { int ix; /* Insertion index */ int lx; /* Left index */ int rx; /* Right index */ /* First search the permanent symbols. */ lx = 0; ix = binarySearch( name, lx, number_of_fixed_symbols ); /* If symbol not in permanent symbol table. */ if( ix < 0 ) { /* Now try the user symbol table. */ ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); /* If symbol not in user symbol table. */ if( ix < 0 ) { /* Must put symbol in table if index is negative. */ ix = ~ix; if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { errorSymbol( &symbol_table_full, name, lexstart ); exit( 1 ); } for( rx = symbol_top; rx >= ix; rx-- ) { symtab[rx + 1] = symtab[rx]; } symbol_top++; /* Enter the symbol as UNDEFINED with a value of zero. */ strcpy( symtab[ix].name, name ); symtab[ix].type = UNDEFINED; symtab[ix].val = 0; symtab[ix].xref_count = 0; if( xref && pass == 2 ) { xreftab[symtab[ix].xref_index] = 0; } } } return( &symtab[ix] ); /* Return the location of the symbol. */ } /* lookup() */ /******************************************************************************/ /* */ /* Function: binarySearch */ /* */ /* Synopsis: Searches the symbol table within the limits given. If the */ /* symbol is not in the table, it returns the insertion point. */ /* */ /******************************************************************************/ int binarySearch( char *name, int start, int symbol_count ) { int lx; /* Left index */ int mx; /* Middle index */ int rx; /* Right index */ int compare; /* Results of comparison */ lx = start; rx = symbol_count - 1; while( lx <= rx ) { mx = ( lx + rx ) / 2; /* Find center of search area. */ compare = strcmp( name, symtab[mx].name ); if( compare < 0 ) { rx = mx - 1; } else if( compare > 0 ) { lx = mx + 1; } else { return( mx ); /* Found a match in symbol table. */ } } /* end while */ return( ~lx ); /* Return insertion point. */ } /* binarySearch() */ /******************************************************************************/ /* */ /* Function: compareSymbols */ /* */ /* Synopsis: Used to presort the symbol table when starting assembler. */ /* */ /******************************************************************************/ int compareSymbols( const void *a, const void *b ) { return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); } /* compareSymbols() */ /******************************************************************************/ /* */ /* Function: copyMacLine */ /* */ /* Synopsis: Used to copy a macro line to the macro buffer. */ /* */ /******************************************************************************/ int copyMacLine( int length, int from, int term, int nargs ) { char name[SYMLEN]; int ix; int jx; int kx; BOOL bl; bl = TRUE; for( ix = from; ix < term; ix++ ) { if( !is_blank( line[ix] ) || ( line[ix] == '\t' )) bl = FALSE; } if( bl || ( length < 0 )) return length; if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; for( ix = from; ix < term; ) { if( nargs && isalpha( line[ix] )) /* Start of symbol? */ { for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ { if( !isalnum( line[jx] )) break; } lexemeToName( name, ix, jx ); /* Make into name. */ for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ { if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) { mac_buffer[length++] = 'a' + (char) kx; for( ix++; ix < jx; ix++ ) { mac_buffer[length++] = 'z'; } break; } /* end if strncmp */ } /* end for kx */ if( kx >= nargs ) { for ( ; ix < jx; ) { mac_buffer[length++] = toupper( line[ix++] ); } } } /*end if nargs */ else { mac_buffer[length++] = toupper( line[ix++] ); } /* end else */ } /* end for ix */ mac_buffer[length++] = '\n'; mac_buffer[length] = 0; return length; } /******************************************************************************/ /* */ /* Function: evalSymbol */ /* */ /* Synopsis: Get the pointer for the symbol table entry if exists. */ /* If symbol doesn't exist, return a pointer to the undefined sym */ /* */ /******************************************************************************/ SYM_T *evalSymbol() { char name[SYMLEN]; SYM_T *sym; sym = lookup( lexemeToName( name, lexstart, lexterm )); sym->xref_count++; /* Count the number of references to symbol. */ if( xref && pass == 2 ) { /* Put the line number in the concordance table. */ xreftab[sym->xref_index + sym->xref_count] = lineno; } return( sym ); } /* evalSymbol() */ /******************************************************************************/ /* */ /* Function: moveToEndOfLine */ /* */ /* Synopsis: Move the parser input to the end of the current input line. */ /* */ /******************************************************************************/ void moveToEndOfLine() { while( !isend( line[cc] )) cc++; lexstart = cc; lexterm = cc; lexstartprev = lexstart; } /* moveToEndOfLine() */ /******************************************************************************/ /* */ /* Function: nextLexeme */ /* */ /* Synopsis: Get the next lexical element from input line. */ /* */ /******************************************************************************/ void nextLexeme() { /* Save start column of previous lexeme for diagnostic messages. */ lexstartprev = lexstart; lextermprev = lexterm; while( is_blank( line[cc] ) || (( line[cc] == '\t' ) && ( line[cc+1] == '\t' )) || (( line[cc] == '\t' ) && isdone( line[cc+1] ))) { cc++; } lexstart = cc; if( isalnum( line[cc] )) { while( isalnum( line[cc] )) { cc++; } } else if( isend( line[cc] )) { /* End-of-Line, don't advance cc! */ } else { switch( line[cc] ) { case '"': /* Quoted letter */ if( cc + 2 < maxcc ) { cc++; cc++; } else { errorMessage( &no_literal_value, lexstart ); cc++; } break; case '/': /* Comment, don't advance cc! */ break; default: /* All other punctuation. */ cc++; break; } } lexterm = cc; } /* nextLexeme() */ /******************************************************************************/ /* */ /* Function: nextLexBlank */ /* */ /* Synopsis: Used to prevent illegal blanks in expressions. */ /* */ /******************************************************************************/ void nextLexBlank() { nextLexeme(); if( is_blank( delimiter )) { errorMessage( &illegal_blank, lexstart - 1 ); } delimiter = line[lexterm]; } /* nextLexBlank() */ /******************************************************************************/ /* */ /* Function: pseudoOperators */ /* */ /* Synopsis: Process pseudo-ops (directives). */ /* */ /******************************************************************************/ BOOL pseudoOperators( PSEUDO_T val ) { int count; int delim; int index; int ix; WORD32 length; int level; int lexstartsave; int pack; int pos; int radixprev; BOOL status; SYM_T *sym; WORD32 value; WORD32 word; int width; static int mask_tab[19] = { 0000000, 0000001, 0000003, 0000007, 0000017, 0000037, 0000077, 0000177, 0000377, 0000777, 0001777, 0003777, 0007777, 0017777, 0037777, 0077777, 0177777, 0377777, 0777777 }; status = TRUE; switch( (PSEUDO_T) val ) { case DECIMAL: radix = 10; break; case DEFINE: count = 0; index = 0; lexstartsave = lexstart; while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && ( count < MAC_MAX_ARGS )) { if ( !isalpha( line[lexstart] ) && ( index == 0 )) { index = lexstart; } lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); nextLexeme(); } if( count == 0 ) /* No macro name. */ { errorMessage( &no_macro_name, lexstartsave ); index = 1; } else if( index ) /* Bad argument name. */ { errorMessage( &bad_dummy_arg, index ); } else if( mac_count >= MAC_TABLE_LENGTH ) { errorMessage( ¯o_table_full, lexstartsave ); index = 1; } else { value = mac_count; mac_count++; /* Value is entry in mac_bodies. */ defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); } if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) { readLine(); nextLexeme(); } if( index ) { conditionFalse(); /* On error skip macro body. */ } else if( line[lexstart] == '<' ) { /* Invariant: line[cc] is the next unexamined character. */ index = lexstart + 1; length = 0; level = 1; while( level > 0 ) { if( end_of_input ) { break; } if( isend( line[cc] ) || ( line[cc] == '/' )) { length = copyMacLine( length, index, cc, count - 1 ); readLine(); index = 0; } else { switch( line[cc] ) { case '>': level--; cc++; break; case '<': level++; cc++; break; default: cc++; break; } /* end switch */ } /* end if */ } /* end while */ length = copyMacLine( length, index, cc - 1, count - 1 ); if( length < 0 ) { errorMessage (¯o_too_long, lexstart ); } else if( length == 0 ) { mac_bodies[value] = NULL; } else { mac_bodies[value] = (char *) malloc( length + 1 ); if( mac_bodies[value] ) { strncpy( mac_bodies[value], mac_buffer, length ); *( mac_bodies[value] + length ) = 0; } else { errorMessage( &no_virtual_memory, lexstart ); } } nextLexeme(); } else { errorMessage( <_expected, lexstart ); } /* end if */ break; case EJECT: page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ status = FALSE; /* This will force reading of next line */ break; case IFDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionTrue(); } else { conditionFalse(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionFalse(); } else { conditionTrue(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNZERO: if( (getExpr())->val == 0 ) { conditionFalse(); } else { conditionTrue(); } break; case IFZERO: if( (getExpr())->val == 0 ) { conditionTrue(); } else { conditionFalse(); } break; case LIST: listfile = listsave; break; case NOLIST: listfile = NULL; break; case OCTAL: radix = 8; break; case START: if( !isdone( line[lexstart] )) { start_addr = (getExpr())->val & 077777; nextLexeme(); } printLine( line, 0, start_addr, LINE_VAL ); status = FALSE; break; case TEXT: delim = line[lexstart]; pack = 0; count = 0; index = lexstart + 1; while( line[index] != delim && !isend( line[index] )) { pack = ( pack << 6 ) | ( line[index] & 077 ); count++; if( count > 2 ) { punchOutObject( clc, pack ); incrementClc(); count = 0; pack = 0; } index++; } if( count == 2 ) { punchOutObject( clc, pack << 6 ); } else if (count == 1) { punchOutObject( clc, pack << 12 ); } else { punchOutObject( clc, 0 ); } incrementClc(); if( isend( line[index] )) { cc = index; lexterm = cc; errorMessage( &text_string, cc ); } else { cc = index + 1; lexterm = cc; } nextLexeme(); break; case TITLE: delim = line[lexstart]; ix = lexstart + 1; /* Find string delimiter. */ do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } ix++; } while( line[ix] != delim && !isend(line[ix]) ); if( !isend( line[ix] ) ) { count = 0; ix = lexstart + 1; do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } list_title[count] = line[ix]; count++; ix++; } while( line[ix] != delim && !isend(line[ix]) ); if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } cc = ix + 1; lexterm = cc; page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ list_title_set = TRUE; } else { cc = ix; lexterm = cc; errorMessage( &text_string, cc ); } nextLexeme(); break; case VFD: pos = 0; word = 0; radixprev = radix; while( !isdone (line[lexstart] )) { lexstartsave = lexstart; radix = 10; width = (getExpr())->val; /* Get field width. */ radix = radixprev; if( (width <= 0) || ((width + pos) > 18) || (line[lexstart] != ':') ) { errorMessage( &illegal_vfd_value, lexstartsave ); } nextLexBlank(); /* Skip colon. */ value = (getExpr())->val; /* Get field value. */ if( line[lexterm] == ',' ) cc++; nextLexeme(); /* Advance to next field. */ pos = pos + width; if( pos <= 18 ) { word = word | ((value & mask_tab[width]) << (18 - pos)); } } punchOutObject( clc, word ); incrementClc(); break; default: break; } /* end switch for pseudo-ops */ return( status ); } /* pseudoOperators() */ /******************************************************************************/ /* */ /* Function: conditionFalse */ /* */ /* Synopsis: Called when a false conditional has been evaluated. */ /* Lex should be the opening <; ignore all text until */ /* the closing >. */ /* */ /******************************************************************************/ void conditionFalse() { int level; if( line[lexstart] == '<' ) { /* Invariant: line[cc] is the next unexamined character. */ level = 1; while( level > 0 ) { if( end_of_input ) { break; } if( isend( line[cc] ) || ( line[cc] == '/' )) { readLine(); } else { switch( line[cc] ) { case '>': level--; cc++; break; case '<': level++; cc++; break; default: cc++; break; } /* end switch */ } /* end if */ } /* end while */ nextLexeme(); } else { errorMessage( <_expected, lexstart ); } } /* conditionFalse() */ /******************************************************************************/ /* */ /* Function: conditionTrue */ /* */ /* Synopsis: Called when a true conditional has been evaluated. */ /* Lex should be the opening <; skip it and setup for */ /* normal assembly. */ /* */ /******************************************************************************/ void conditionTrue() { if( line[lexstart] == '<' ) { nextLexeme(); /* Skip the opening '<' */ } else { errorMessage( <_expected, lexstart ); } } /* conditionTrue() */ /******************************************************************************/ /* */ /* Function: errorLexeme */ /* */ /* Synopsis: Display an error message using the current lexical element. */ /* */ /******************************************************************************/ void errorLexeme( EMSG_T *mesg, WORD32 col ) { char name[SYMLEN]; errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); } /* errorLexeme() */ /******************************************************************************/ /* */ /* Function: errorSymbol */ /* */ /* Synopsis: Display an error message with a given string. */ /* */ /******************************************************************************/ void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) { char linecol[12]; char *s; if( pass == 2 ) { s = ( name == NULL ) ? "" : name ; errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", filename, linecol, mesg->file, s, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorSymbol() */ /******************************************************************************/ /* */ /* Function: errorMessage */ /* */ /* Synopsis: Display an error message without a name argument. */ /* */ /******************************************************************************/ void errorMessage( EMSG_T *mesg, WORD32 col ) { char linecol[12]; if( pass == 2 ) { errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", filename, linecol, mesg->file, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorMessage() */ /******************************************************************************/ /* */ /* Function: saveError */ /* */ /* Synopsis: Save the current error in a list so it may displayed after the */ /* the current line is printed. */ /* */ /******************************************************************************/ void saveError( char *mesg, WORD32 col ) { if( save_error_count < DIM( error_list )) { error_list[save_error_count].mesg = mesg; error_list[save_error_count].col = col; save_error_count++; } error_in_line = TRUE; if( listed ) { printErrorMessages(); } } /* saveError() */ /* End-of-File */ simh-3.8.1/TOOLS/crossassemblers/macro11/0000777000175000017500000000000007744127262016235 5ustar vlmvlmsimh-3.8.1/TOOLS/crossassemblers/macro11/macro11.c0000666000175000017500000043560507375552020017653 0ustar vlmvlm/* Assembler compatible with MACRO-11. Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "macro11.h" #include "rad50.h" #include "object.h" #include "stream2.h" #include "mlb.h" #include "util.h" #define SYMMAX 6 /* I will honor this many character symbols */ #define issym(c) (isalpha(c) || isdigit(c) || (c) == '.' || (c) == '$') /* Program sections: */ typedef struct section { char *label; /* Section name */ unsigned type; /* Section type */ #define USER 1 /* user-defined */ #define SYSTEM 2 /* A system symbol (like "."; value is an enum) */ #define INSTRUCTION 3 /* An instruction code (like "MOV"; value is an enum) */ #define PSEUDO 4 /* A pseudo-op (.PSECT, .TITLE, .MACRO, .IF; value is an enum) */ #define REGISTER 5 /* Symbol is a register (value 0=$0, value 1=$1, ... $7) */ #define USERMACRO 6 /* Symbol is a user macro */ unsigned flags; /* Flags, defined in object.h */ unsigned pc; /* Current offset in the section */ unsigned size; /* Current section size */ unsigned sector; /* Used for complex relocation, and naught else */ } SECTION; /* Symbol table entries */ typedef struct symbol { char *label; /* Symbol name */ unsigned value; /* Symbol value */ int stmtno; /* Statement number of symbol's definition */ unsigned flags; /* Symbol flags */ #define PERMANENT 1 /* Symbol may not be redefined */ #define GLOBAL 2 /* Symbol is global */ #define WEAK 4 /* Symbol definition is weak */ #define DEFINITION 8 /* Symbol is a global definition, not reference */ #define UNDEFINED 16 /* Symbol is a phony, undefined */ #define LOCAL 32 /* Set if this is a local label (i.e. 10$) */ SECTION *section; /* Section in which this symbol is defined */ struct symbol *next; /* Next symbol with the same hash value */ } SYMBOL; /* Arguments given to macros or .IRP/.IRPC blocks */ typedef struct arg { struct arg *next; /* Pointer in arg list */ int locsym; /* Whether arg represents an optional local symbol */ char *label; /* Argument name */ char *value; /* Default or active substitution */ } ARG; /* A MACRO is a superstructure surrounding a SYMBOL. */ typedef struct macro { SYMBOL sym; /* Surrounds a symbol, contains the macro name */ ARG *args; /* The argument list */ BUFFER *text; /* The macro text */ } MACRO; typedef struct ex_tree { enum ex_type { EX_LIT=1, /* Expression is a literal value */ EX_SYM=2, /* Expression has a symbol reference */ EX_UNDEFINED_SYM=3, /* Expression is undefined sym reference */ EX_TEMP_SYM=4, /* Expression is temp sym reference */ EX_COM=5, /* One's complement */ EX_NEG=6, /* Negate */ EX_ERR=7, /* Expression with an error */ EX_ADD=8, /* Add */ EX_SUB=9, /* Subtract */ EX_MUL=10, /* Multiply */ EX_DIV=11, /* Divide */ EX_AND=12, /* bitwise and */ EX_OR=13 /* bitwise or */ } type; char *cp; /* points to end of parsed expression */ union { struct { struct ex_tree *left, *right; /* Left, right children */ } child; unsigned lit; /* Literal value */ SYMBOL *symbol; /* Symbol reference */ } data; } EX_TREE; typedef struct addr_mode { unsigned type; /* The bits that represent the addressing mode */ /* bits 0:2 are register number */ /* bit 3 is indirect */ /* bits 4:6 are mode, where 0=Rn, 1=(Rn)+, 2=-(Rn), 3=offset(Rn) */ int rel; /* the addressing mode is PC-relative */ EX_TREE *offset; /* Expression giving the offset */ } ADDR_MODE; #define FALSE 0 /* Everybody needs FALSE and TRUE */ #define TRUE 1 enum pseudo_ops { P_ASCII, P_ASCIZ, P_ASECT, P_BLKB, P_BLKW, P_BYTE, P_CSECT, P_DSABL, P_ENABL, P_END, P_ENDC, P_ENDM, P_ENDR, P_EOT, P_ERROR, P_EVEN, P_FLT2, P_FLT4, P_GLOBL, P_IDENT, P_IF, P_IFF, P_IFT, P_IFTF, P_IIF, P_IRP, P_IRPC, P_LIMIT, P_LIST, P_MCALL, P_MEXIT, P_NARG, P_NCHR, P_NLIST, P_NTYPE, P_ODD, P_PACKED, P_PAGE, P_PRINT, P_PSECT, P_RADIX, P_RAD50, P_REM, P_REPT, P_RESTORE, P_SAVE, P_SBTTL, P_TITLE, P_WORD, P_MACRO, P_INCLU, P_WEAK, P_IFDF }; enum instruction_ops { I_ADC = 0005500, I_ADCB = 0105500, I_ADD = 0060000, I_ASH = 0072000, I_ASHC = 0073000, I_ASL = 0006300, I_ASLB = 0106300, I_ASR = 0006200, I_ASRB = 0106200, I_BCC = 0103000, I_BCS = 0103400, I_BEQ = 0001400, I_BGE = 0002000, I_BGT = 0003000, I_BHI = 0101000, I_BHIS = 0103000, I_BIC = 0040000, I_BICB = 0140000, I_BIS = 0050000, I_BISB = 0150000, I_BIT = 0030000, I_BITB = 0130000, I_BLE = 0003400, I_BLO = 0103400, I_BLOS = 0101400, I_BLT = 0002400, I_BMI = 0100400, I_BNE = 0001000, I_BPL = 0100000, I_BPT = 0000003, I_BR = 0000400, I_BVC = 0102000, I_BVS = 0102400, I_CALL = 0004700, I_CALLR = 0000100, I_CCC = 0000257, I_CLC = 0000241, I_CLN = 0000250, I_CLR = 0005000, I_CLRB = 0105000, I_CLV = 0000242, I_CLZ = 0000244, I_CMP = 0020000, I_CMPB = 0120000, I_COM = 0005100, I_COMB = 0105100, I_DEC = 0005300, I_DECB = 0105300, I_DIV = 0071000, I_EMT = 0104000, I_FADD = 0075000, I_FDIV = 0075030, I_FMUL = 0075020, I_FSUB = 0075010, I_HALT = 0000000, I_INC = 0005200, I_INCB = 0105200, I_IOT = 0000004, I_JMP = 0000100, I_JSR = 0004000, I_MARK = 0006400, I_MED6X = 0076600, I_MED74C= 0076601, I_MFPD = 0106500, I_MFPI = 0006500, I_MFPS = 0106700, I_MOV = 0010000, I_MOVB = 0110000, I_MTPD = 0106600, I_MTPI = 0006600, I_MTPS = 0106400, I_MUL = 0070000, I_NEG = 0005400, I_NEGB = 0105400, I_NOP = 0000240, I_RESET = 0000005, I_RETURN= 0000207, I_ROL = 0006100, I_ROLB = 0106100, I_ROR = 0006000, I_RORB = 0106000, I_RTI = 0000002, I_RTS = 0000200, I_RTT = 0000006, I_SBC = 0005600, I_SBCB = 0105600, I_SCC = 0000277, I_SEC = 0000261, I_SEN = 0000270, I_SEV = 0000262, I_SEZ = 0000264, I_SOB = 0077000, I_SPL = 0000230, I_SUB = 0160000, I_SWAB = 0000300, I_SXT = 0006700, I_TRAP = 0104400, I_TST = 0005700, I_TSTB = 0105700, I_WAIT = 0000001, I_XFC = 0076700, I_XOR = 0074000, I_MFPT = 0000007, /* CIS not implemented - maybe later */ /* FPU */ I_ABSD = 0170600, I_ABSF = 0170600, I_ADDD = 0172000, I_ADDF = 0172000, I_CFCC = 0170000, I_CLRD = 0170400, I_CLRF = 0170400, I_CMPD = 0173400, I_CMPF = 0173400, I_DIVD = 0174400, I_DIVF = 0174400, I_LDCDF = 0177400, I_LDCFD = 0177400, I_LDCID = 0177000, I_LDCIF = 0177000, I_LDCLD = 0177000, I_LDCLF = 0177000, I_LDD = 0172400, I_LDEXP = 0176400, I_LDF = 0172400, I_LDFPS = 0170100, I_MODD = 0171400, I_MODF = 0171400, I_MULD = 0171000, I_MULF = 0171000, I_NEGD = 0170700, I_NEGF = 0170700, I_SETD = 0170011, I_SETF = 0170001, I_SETI = 0170002, I_SETL = 0170012, I_STA0 = 0170005, I_STB0 = 0170006, I_STCDF = 0176000, I_STCDI = 0175400, I_STCDL = 0175400, I_STCFD = 0176000, I_STCFI = 0175400, I_STCFL = 0175400, I_STD = 0174000, I_STEXP = 0175000, I_STF = 0174000, I_STFPS = 0170200, I_STST = 0170300, I_SUBD = 0173000, I_SUBF = 0173000, I_TSTD = 0170500, I_TSTF = 0170500 }; enum operand_codes { OC_MASK = 0xff00, /* mask over flags for operand types */ OC_NONE = 0x0000, /* No operands */ OC_1GEN = 0x0100, /* One general operand (CLR, TST, etc.) */ OC_2GEN = 0x0200, /* Two general operand (MOV, CMP, etc.) */ OC_BR = 0x0300, /* Branch */ OC_ASH = 0x0400, /* ASH and ASHC (one gen, one reg) */ OC_MARK = 0x0500, /* MARK instruction operand */ OC_JSR = 0x0600, /* JSR, XOR (one reg, one gen) */ OC_1REG = 0x0700, /* FADD, FSUB, FMUL, FDIV, RTS */ OC_SOB = 0x0800, /* SOB */ OC_1FIS = 0x0900, /* FIS (reg, gen) */ OC_2FIS = 0x0a00, /* FIS (gen, reg) */ OC__LAST = 0xff00 }; /* format of a listing line Interestingly, no instances of this struct are ever created. It lives to be a way to layout the format of a list line. I wonder if I should have bothered. */ typedef struct lstformat { char flag[2]; /* Error flags */ char line_number[6]; /* Line number */ char pc[8]; /* Location */ char words[8][3]; /* three instruction words */ char source[1]; /* source line */ } LSTFORMAT; #define SIZEOF_MEMBER(s, m) (sizeof((s *)0)->m) /* GLOBAL VARIABLES */ int pass = 0; /* The current assembly pass. 0 = first pass. */ int stmtno = 0; /* The current source line number */ int radix = 8; /* The current input conversion radix */ int lsb = 0; /* The current local symbol section identifier */ int last_lsb = 0; /* The last block in which a macro automatic label was created */ int last_locsym = 32768; /* The last local symbol number generated */ int enabl_debug = 0; /* Whether assembler debugging is enabled */ int enabl_ama = 0; /* When set, chooses absolute (037) versus PC-relative */ /* (067) addressing mode */ int enabl_lsb = 0; /* When set, stops non-local symbol definitions from delimiting local symbol sections. */ int enabl_gbl = 1; /* Implicit definition of global symbols */ int list_md = 1; /* option to list macro/rept definition = yes */ int list_me = 1; /* option to list macro/rept expansion = yes */ int list_bex = 1; /* option to show binary */ int list_level = 1; /* Listing control level. .LIST increments; .NLIST decrements */ char *listline; /* Source lines */ char *binline; /* for octal expansion */ FILE *lstfile = NULL; int suppressed = 0; /* Assembly suppressed by failed conditional */ #define MAX_MLBS 32 MLB *mlbs[MAX_MLBS]; /* macro libraries specified on the command line */ int nr_mlbs = 0; /* Number of macro libraries */ typedef struct cond { int ok; /* What the condition evaluated to */ char *file; /* What file and line it occurred */ int line; } COND; #define MAX_CONDS 256 COND conds[MAX_CONDS]; /* Stack of recent conditions */ int last_cond; /* 0 means no stacked cond. */ SECTION *sect_stack[32]; /* 32 saved sections */ int sect_sp; /* Stack pointer */ char *module_name = NULL; /* The module name (taken from the 'TITLE'); */ char *ident = NULL; /* .IDENT name */ EX_TREE *xfer_address = NULL; /* The transfer address */ SYMBOL *current_pc; /* The current program counter */ unsigned last_dot_addr; /* Last coded PC... */ SECTION *last_dot_section; /* ...and it's program section */ #define DOT (current_pc->value) /* Handy reference to the current location */ /* The following are dummy psects for symbols which have meaning to the assembler: */ SECTION register_section = { "", REGISTER, 0, 0 }; /* the section containing the registers */ SECTION pseudo_section = { "", PSEUDO, 0, 0 }; /* the section containing the pseudo-operations */ SECTION instruction_section = { ". ABS.", INSTRUCTION, 0, 0 }; /* the section containing instructions */ SECTION macro_section = { "", SYSTEM, 0, 0, 0 }; /* Section for macros */ /* These are real psects that get written out to the object file */ SECTION absolute_section = { ". ABS.", SYSTEM, PSECT_GBL|PSECT_COM, 0, 0, 0}; /* The default absolute section */ SECTION blank_section = { "", SYSTEM, PSECT_REL, 0, 0, 1}; /* The default relocatable section */ SECTION *sections[256] = { /* Array of sections in the order they were defined */ &absolute_section, &blank_section, }; int sector = 2; /* number of such sections */ SYMBOL *reg_sym[8]; /* Keep the register symbols in a handy array */ /* symbol tables */ #define HASH_SIZE 1023 typedef struct symbol_table { SYMBOL *hash[HASH_SIZE]; } SYMBOL_TABLE; SYMBOL_TABLE system_st; /* System symbols (Instructions, pseudo-ops, registers) */ SYMBOL_TABLE section_st; /* Program sections */ SYMBOL_TABLE symbol_st; /* User symbols */ SYMBOL_TABLE macro_st; /* Macros */ SYMBOL_TABLE implicit_st; /* The symbols which may be implicit globals */ /* SYMBOL_ITER is used for iterating thru a symbol table. */ typedef struct symbol_iter { int subscript; /* Current hash subscript */ SYMBOL *current; /* Current symbol */ } SYMBOL_ITER; /* EOL says whether a char* is pointing at the end of a line */ #define EOL(c) (!(c) || (c) == '\n' || (c) == ';') /* reports errors */ static void report(STREAM *str, char *fmt, ...) { va_list ap; char *name = "**"; int line = 0; if(!pass) return; /* Don't report now. */ if(str) { name = str->name; line = str->line; } fprintf(stderr, "%s:%d: ***ERROR ", name, line); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if(lstfile) { fprintf(lstfile, "%s:%d: ***ERROR ", name, line); va_start(ap, fmt); vfprintf(lstfile, fmt, ap); va_end(ap); } } /* memcheck - crash out if a pointer (returned from malloc) is NULL. */ void *memcheck(void *ptr) { if(ptr == NULL) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } return ptr; } /* upcase turns a string to upper case */ static void upcase(char *str) { while(*str) { *str = toupper(*str); str++; } } /* hash_name hashes a name into a value from 0-HASH_SIZE */ static int hash_name(char *label) { unsigned accum = 0; while(*label) accum = (accum << 1) ^ *label++; accum %= HASH_SIZE; return accum; } /* Allocate a new symbol. Does not add it to any symbol table. */ static SYMBOL *new_sym(char *label) { SYMBOL *sym = memcheck(malloc(sizeof(SYMBOL))); sym->label = memcheck(strdup(label)); sym->section = NULL; sym->value = 0; sym->flags = 0; return sym; } /* Free a symbol. Does not remove it from any symbol table. */ static void free_sym(SYMBOL *sym) { if(sym->label) { free(sym->label); sym->label = NULL; } free(sym); } /* remove_sym removes a symbol from it's symbol table. */ static void remove_sym(SYMBOL *sym, SYMBOL_TABLE *table) { SYMBOL **prevp, *symp; int hash; hash = hash_name(sym->label); prevp = &table->hash[hash]; while(symp = *prevp, symp != NULL && symp != sym) prevp = &symp->next; if(symp) *prevp = sym->next; } /* lookup_sym finds a symbol in a table */ static SYMBOL *lookup_sym(char *label, SYMBOL_TABLE *table) { unsigned hash; SYMBOL *sym; hash = hash_name(label); sym = table->hash[hash]; while(sym && strcmp(sym->label, label) != 0) sym = sym->next; return sym; } /* next_sym - returns the next symbol from a symbol table. Must be preceeded by first_sym. Returns NULL after the last symbol. */ static SYMBOL *next_sym(SYMBOL_TABLE *table, SYMBOL_ITER *iter) { if(iter->current) iter->current = iter->current->next; while(iter->current == NULL) { if(iter->subscript >= HASH_SIZE) return NULL; /* No more symbols. */ iter->current = table->hash[iter->subscript]; iter->subscript++; } return iter->current; /* Got a symbol. */ } /* first_sym - returns the first symbol from a symbol table. Symbols are stored in random order. */ static SYMBOL *first_sym(SYMBOL_TABLE *table, SYMBOL_ITER *iter) { iter->subscript = 0; iter->current = NULL; return next_sym(table, iter); } /* add_table - add a symbol to a symbol table. */ static void add_table(SYMBOL *sym, SYMBOL_TABLE *table) { int hash = hash_name(sym->label); sym->next = table->hash[hash]; table->hash[hash] = sym; } /* add_sym - used throughout to add or update symbols in a symbol table. */ static SYMBOL *add_sym(char *label, unsigned value, unsigned flags, SECTION *section, SYMBOL_TABLE *table) { SYMBOL *sym; sym = lookup_sym(label, table); if(sym != NULL) { // A symbol registered as "undefined" can be changed. if((sym->flags & UNDEFINED) && !(flags & UNDEFINED)) { sym->flags &= ~(PERMANENT|UNDEFINED); } /* Check for compatible definition */ else if(sym->section == section && sym->value == value) { sym->flags |= flags; /* Merge flags quietly */ return sym; /* 's okay */ } if(!(sym->flags & PERMANENT)) { /* permit redefinition */ sym->value = value; sym->flags |= flags; sym->section = section; return sym; } return NULL; /* Bad symbol redefinition */ } sym = new_sym(label); sym->flags = flags; sym->stmtno = stmtno; sym->section = section; sym->value = value; add_table(sym, table); return sym; } /* Allocate a new section */ static SECTION *new_section(void) { SECTION *sect = memcheck(malloc(sizeof(SECTION))); sect->flags = 0; sect->size = 0; sect->pc = 0; sect->type = 0; sect->sector = 0; sect->label = NULL; return sect; } /* Allocate a new ARG */ static ARG *new_arg(void) { ARG *arg = memcheck(malloc(sizeof(ARG))); arg->locsym = 0; arg->value = NULL; arg->next = NULL; arg->label = NULL; return arg; } /* Allocate a new macro */ static MACRO *new_macro(char *label) { MACRO *mac = memcheck(malloc(sizeof(MACRO))); mac->sym.flags = 0; mac->sym.label = label; mac->sym.stmtno = stmtno; mac->sym.next = NULL; mac->sym.section = ¯o_section; mac->sym.value = 0; mac->args = NULL; mac->text = NULL; return mac; } /* Free a list of args (as for a macro, or a macro expansion) */ static void free_args(ARG *arg) { ARG *next; while(arg) { next = arg->next; if(arg->label) { free(arg->label); arg->label = NULL; } if(arg->value) { free(arg->value); arg->value = NULL; } free(arg); arg = next; } } /* free a macro, it's args, it's text, etc. */ static void free_macro(MACRO *mac) { if(mac->text) { free(mac->text); } free_args(mac->args); free_sym(&mac->sym); } /* do_list returns TRUE if listing is enabled. */ static int dolist(void) { int ok = lstfile != NULL && pass > 0 && list_level > 0; return ok; } /* list_source saves a text line for later listing by list_flush */ static void list_source(STREAM *str, char *cp) { if(dolist()) { int len = strcspn(cp, "\n"); /* Save the line text away for later... */ if(listline) free(listline); listline = memcheck(malloc(len + 1)); memcpy(listline, cp, len); listline[len] = 0; if(!binline) binline = memcheck(malloc(sizeof(LSTFORMAT) + 16)); sprintf(binline, "%*s%*d", SIZEOF_MEMBER(LSTFORMAT, flag), "", SIZEOF_MEMBER(LSTFORMAT, line_number), str->line); } } /* padto adds blanks to the end of a string until it's the given length. */ static void padto(char *str, int to) { int needspace = to - strlen(str); str += strlen(str); while(needspace > 0) *str++ = ' ', needspace--; *str = 0; } /* list_flush produces a buffered list line. */ static void list_flush(void) { if(dolist()) { padto(binline, offsetof(LSTFORMAT, source)); fputs(binline, lstfile); fputs(listline, lstfile); fputc('\n', lstfile); listline[0] = 0; binline[0] = 0; } } /* list_fit checks to see if a word will fit in the current listing line. If not, it flushes and prepares another line. */ static void list_fit(STREAM *str, unsigned addr) { int len = strlen(binline); size_t col1 = offsetof(LSTFORMAT, source); size_t col2 = offsetof(LSTFORMAT, pc); if(strlen(binline) >= col1) { int offset = offsetof(LSTFORMAT, pc); list_flush(); listline[0] = 0; binline[0] = 0; sprintf(binline, "%*s %6.6o", offsetof(LSTFORMAT, pc), "", addr); padto(binline, offsetof(LSTFORMAT, words)); } else if(strlen(binline) <= col2) { sprintf(binline, "%*s%*d %6.6o", SIZEOF_MEMBER(LSTFORMAT, flag), "", SIZEOF_MEMBER(LSTFORMAT, line_number), str->line, addr); padto(binline, offsetof(LSTFORMAT, words)); } } /* list_value is used to show a computed value */ static void list_value(STREAM *str, unsigned word) { if(dolist()) { /* Print the value and go */ binline[0] = 0; sprintf(binline, "%*s%*d %6.6o", SIZEOF_MEMBER(LSTFORMAT, flag), "", SIZEOF_MEMBER(LSTFORMAT, line_number), str->line, word & 0177777); } } /* Print a word to the listing file */ void list_word(STREAM *str, unsigned addr, unsigned value, int size, char *flags) { if(dolist()) { list_fit(str, addr); if(size == 1) sprintf(binline + strlen(binline), " %3.3o%1.1s ", value & 0377, flags); else sprintf(binline + strlen(binline), "%6.6o%1.1s ", value & 0177777, flags); } } /* This is called by places that are about to store some code, or which want to manually update DOT. */ static void change_dot(TEXT_RLD *tr, int size) { if(size > 0) { if(last_dot_section != current_pc->section) { text_define_location(tr, current_pc->section->label, ¤t_pc->value); last_dot_section = current_pc->section; last_dot_addr = current_pc->value; } if(last_dot_addr != current_pc->value) { text_modify_location(tr, ¤t_pc->value); last_dot_addr = current_pc->value; } /* Update for next time */ last_dot_addr += size; } if(DOT+size > current_pc->section->size) current_pc->section->size = DOT+size; } /* store_word stores a word to the object file and lists it to the listing file */ static int store_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word) { change_dot(tr, size); list_word(str, DOT, word, size, ""); return text_word(tr, &DOT, size, word); } /* store_word stores a word to the object file and lists it to the listing file */ static int store_displaced_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word) { change_dot(tr, size); list_word(str, DOT, word, size, "'"); return text_displaced_word(tr, &DOT, size, word); } static int store_global_displaced_offset_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word, char *global) { change_dot(tr, size); list_word(str, DOT, word, size, "G"); return text_global_displaced_offset_word(tr, &DOT, size, word, global); } static int store_global_offset_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word, char *global) { change_dot(tr, size); list_word(str, DOT, word, size, "G"); return text_global_offset_word(tr, &DOT, size, word, global); } static int store_internal_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word) { change_dot(tr, size); list_word(str, DOT, word, size, ""); return text_internal_word(tr, &DOT, size, word); } static int store_psect_displaced_offset_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word, char *name) { change_dot(tr, size); list_word(str, DOT, word, size, ""); return text_psect_displaced_offset_word(tr, &DOT, size, word, name); } static int store_psect_offset_word(STREAM *str, TEXT_RLD *tr, int size, unsigned word, char *name) { change_dot(tr, size); list_word(str, DOT, word, size, ""); return text_psect_offset_word(tr, &DOT, size, word, name); } static int store_limits(STREAM *str, TEXT_RLD *tr) { change_dot(tr, 4); list_word(str, DOT, 0, 2, ""); list_word(str, DOT+2, 0, 2, ""); return text_limits(tr, &DOT); } /* skipwhite - used everywhere to advance a char pointer past spaces */ static char *skipwhite(char *cp) { while(*cp == ' ' || *cp == '\t') cp++; return cp; } /* skipdelim - used everywhere to advance between tokens. Whitespace and one comma are allowed delims. */ static char *skipdelim(char *cp) { cp = skipwhite(cp); if(*cp == ',') cp = skipwhite(cp+1); return cp; } /* Parse PDP-11 64-bit floating point format. */ /* Give a pointer to "size" words to receive the result. */ /* Note: there are probably degenerate cases that store incorrect results. For example, I think rounding up a FLT2 might cause exponent overflow. Sorry. */ /* Note also that the full 49 bits of precision probably aren't available on the source platform, given the widespread application of IEEE floating point formats, so expect some differences. Sorry again. */ int parse_float(char *cp, char **endp, int size, unsigned *flt) { double d; /* value */ double frac; /* fractional value */ ulong64 ufrac; /* fraction converted to 49 bit unsigned integer */ int i; /* Number of fields converted by sscanf */ int n; /* Number of characters converted by sscanf */ int sexp; /* Signed exponent */ unsigned exp; /* Unsigned excess-128 exponent */ unsigned sign = 0; /* Sign mask */ i = sscanf(cp, "%lf%n", &d, &n); if(i == 0) return 0; /* Wasn't able to convert */ cp += n; if(endp) *endp = cp; if(d == 0.0) { flt[0] = flt[1] = flt[2] = flt[3] = 0; /* All-bits-zero equals zero */ return 1; /* Good job. */ } frac = frexp(d, &sexp); /* Separate into exponent and mantissa */ if(sexp < -128 || sexp > 127) return 0; /* Exponent out of range. */ exp = sexp + 128; /* Make excess-128 mode */ exp &= 0xff; /* express in 8 bits */ if(frac < 0) { sign = 0100000; /* Negative sign */ frac = -frac; /* fix the mantissa */ } /* The following big literal is 2 to the 49th power: */ ufrac = (ulong64) (frac * 72057594037927936.0); /* Align fraction bits */ /* Round from FLT4 to FLT2 */ if(size < 4) { ufrac += 0x80000000; /* Round to nearest 32-bit representation */ if(ufrac > 0x200000000000) /* Overflow? */ { ufrac >>= 1; /* Normalize */ exp--; } } flt[0] = (unsigned) (sign | (exp << 7) | (ufrac >> 48) & 0x7F); if(size > 1) { flt[1] = (unsigned) ((ufrac >> 32) & 0xffff); if(size > 2) { flt[2] = (unsigned) ((ufrac >> 16) & 0xffff); flt[3] = (unsigned) ((ufrac >> 0) & 0xffff); } } return 1; } /* Allocate an EX_TREE */ static EX_TREE *new_ex_tree(void) { EX_TREE *tr = memcheck(malloc(sizeof(EX_TREE))); return tr; } /* Create an EX_TREE representing a parse error */ static EX_TREE *ex_err(EX_TREE *tp, char *cp) { EX_TREE *errtp; errtp = new_ex_tree(); errtp->cp = cp; errtp->type = EX_ERR; errtp->data.child.left = tp; return errtp; } /* Create an EX_TREE representing a literal value */ static EX_TREE *new_ex_lit(unsigned value) { EX_TREE *tp; tp = new_ex_tree(); tp->type = EX_LIT; tp->data.lit = value; return tp; } /* The recursive-descent expression parser parse_expr. */ /* This parser was designed for expressions with operator precedence. However, MACRO-11 doesn't observe any sort of operator precedence. If you feel your source deserves better, give the operators appropriate precedence values right here. */ #define ADD_PREC 1 #define MUL_PREC 1 #define AND_PREC 1 #define OR_PREC 1 EX_TREE *parse_unary(char *cp); /* Prototype for forward calls */ EX_TREE *parse_binary(char *cp, char term, int depth) { EX_TREE *leftp, *rightp, *tp; leftp = parse_unary(cp); while(leftp->type != EX_ERR) { cp = skipwhite(leftp->cp); if(*cp == term) return leftp; switch(*cp) { case '+': if(depth >= ADD_PREC) return leftp; rightp = parse_binary(cp+1, term, ADD_PREC); tp = new_ex_tree(); tp->type = EX_ADD; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; case '-': if(depth >= ADD_PREC) return leftp; rightp = parse_binary(cp+1, term, ADD_PREC); tp = new_ex_tree(); tp->type = EX_SUB; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; case '*': if(depth >= MUL_PREC) return leftp; rightp = parse_binary(cp+1, term, MUL_PREC); tp = new_ex_tree(); tp->type = EX_MUL; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; case '/': if(depth >= MUL_PREC) return leftp; rightp = parse_binary(cp+1, term, MUL_PREC); tp = new_ex_tree(); tp->type = EX_DIV; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; case '!': if(depth >= OR_PREC) return leftp; rightp = parse_binary(cp+1, term, 2); tp = new_ex_tree(); tp->type = EX_OR; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; case '&': if(depth >= AND_PREC) return leftp; rightp = parse_binary(cp+1, term, AND_PREC); tp = new_ex_tree(); tp->type = EX_AND; tp->data.child.left = leftp; tp->data.child.right = rightp; tp->cp = rightp->cp; leftp = tp; break; default: /* Some unknown character. Let caller decide if it's okay. */ return leftp; } /* end switch */ } /* end while */ /* Can't be reached except by error. */ return leftp; } /* get_symbol is used all over the place to pull a symbol out of the text. */ static char *get_symbol(char *cp, char **endp, int *islocal) { int len; char *symcp; int digits = 0; cp = skipwhite(cp); /* Skip leading whitespace */ if(!issym(*cp)) return NULL; digits = 0; if(isdigit(*cp)) digits = 2; /* Think about digit count */ for(symcp = cp + 1; issym(*symcp); symcp++) { if(!isdigit(*symcp)) /* Not a digit? */ digits--; /* Make a note. */ } if(digits == 2) return NULL; /* Not a symbol, it's a digit string */ if(endp) *endp = symcp; len = symcp - cp; /* Now limit length */ if(len > SYMMAX) len = SYMMAX; symcp = memcheck(malloc(len + 1)); memcpy(symcp, cp, len); symcp[len] = 0; upcase(symcp); if(islocal) { *islocal = 0; /* Turn to local label format */ if(digits == 1) { if(symcp[len-1] == '$') { char *newsym = memcheck(malloc(32)); /* Overkill */ sprintf(newsym, "%d$%d", strtol(symcp, NULL, 10), lsb); free(symcp); symcp = newsym; if(islocal) *islocal = LOCAL; } else { free(symcp); return NULL; } } } else { /* disallow local label format */ if(isdigit(*symcp)) { free(symcp); return NULL; } } return symcp; } /* brackrange is used to find a range of text which may or may not be bracketed. If the brackets are <>, then nested brackets are detected. If the brackets are of the form ^/.../ no detection of nesting is attempted. Using brackets ^<...< will mess this routine up. What in the world are you thinking? */ int brackrange(char *cp, int *start, int *length, char **endp) { char endstr[6]; int endlen; int nest; int len; switch(*cp) { case '^': endstr[0] = cp[1]; strcpy(endstr+1, "\n"); *start = 2; endlen = 1; break; case '<': strcpy(endstr, "<>\n"); endlen = 1; *start = 1; break; default: return FALSE; } cp += *start; len = 0; nest = 1; while(nest) { int sublen; sublen = strcspn(cp+len, endstr); if(cp[len+sublen] == '<') nest++; else nest--; len += sublen; } *length = len; if(endp) *endp = cp + len + endlen; return 1; } /* parse_unary parses out a unary operator or leaf expression. */ EX_TREE *parse_unary(char *cp) { EX_TREE *tp; /* Skip leading whitespace */ cp = skipwhite(cp); if(*cp == '%') /* Register notation */ { unsigned reg; cp++; reg = strtoul(cp, &cp, 8); if(reg > 7) return ex_err(NULL, cp); /* This returns references to the built-in register symbols */ tp = new_ex_tree(); tp->type = EX_SYM; tp->data.symbol = reg_sym[reg]; tp->cp = cp; return tp; } /* Unary negate */ if(*cp == '-') { tp = new_ex_tree(); tp->type = EX_NEG; tp->data.child.left = parse_unary(cp+1); tp->cp = tp->data.child.left->cp; return tp; } /* Unary + I can ignore. */ if(*cp == '+') return parse_unary(cp+1); if(*cp == '^') { int save_radix; switch(tolower(cp[1])) { case 'c': /* ^C, ones complement */ tp = new_ex_tree(); tp->type = EX_COM; tp->data.child.left = parse_unary(cp+2); tp->cp = tp->data.child.left->cp; return tp; case 'b': /* ^B, binary radix modifier */ save_radix = radix; radix = 2; tp = parse_unary(cp+2); radix = save_radix; return tp; case 'o': /* ^O, octal radix modifier */ save_radix = radix; radix = 8; tp = parse_unary(cp+2); radix = save_radix; return tp; case 'd': /* ^D, decimal radix modifier */ save_radix = radix; radix = 10; tp = parse_unary(cp+2); radix = save_radix; return tp; case 'x': /* An enhancement! ^X, hexadecimal radix modifier */ save_radix = radix; radix = 16; tp = parse_unary(cp+2); radix = save_radix; return tp; case 'r': /* ^R, RAD50 literal */ { int start, len; char *endcp; unsigned value; cp += 2; if(brackrange(cp, &start, &len, &endcp)) value = rad50(cp+start, NULL); else value = rad50(cp, &endcp); tp = new_ex_lit(value); tp->cp = endcp; return tp; } case 'f': /* ^F, single-word floating point literal indicator */ { unsigned flt[1]; char *endcp; if(!parse_float(cp+2, &endcp, 1, flt)) { tp = ex_err(NULL, cp+2); } else { tp = new_ex_lit(flt[0]); tp->cp = endcp; } return tp; } } if(ispunct(cp[1])) { char *ecp; /* oddly-bracketed expression like this: ^/expression/ */ tp = parse_binary(cp+2, cp[1], 0); ecp = skipwhite(tp->cp); if(*ecp != cp[1]) return ex_err(tp, ecp); tp->cp = ecp + 1; return tp; } } /* Bracketed subexpression */ if(*cp == '<') { char *ecp; tp = parse_binary(cp+1, '>', 0); ecp = skipwhite(tp->cp); if(*ecp != '>') return ex_err(tp, ecp); tp->cp = ecp + 1; return tp; } /* Check for ASCII constants */ if(*cp == '\'') { /* 'x single ASCII character */ cp++; tp = new_ex_tree(); tp->type = EX_LIT; tp->data.lit = *cp & 0xff; tp->cp = ++cp; return tp; } if(*cp == '\"') { /* "xx ASCII character pair */ cp++; tp = new_ex_tree(); tp->type = EX_LIT; tp->data.lit = (cp[0] & 0xff) | ((cp[1] & 0xff) << 8); tp->cp = cp + 2; return tp; } /* Numeric constants are trickier than they need to be, */ /* since local labels start with a digit too. */ if(isdigit(*cp)) { char *label; int local; if((label = get_symbol(cp, NULL, &local)) == NULL) { char *endcp; unsigned long value; int rad = radix; /* get_symbol returning NULL assures me that it's not a local label. */ /* Look for a trailing period, to indicate decimal... */ for(endcp = cp; isdigit(*endcp); endcp++) ; if(*endcp == '.') rad = 10; value = strtoul(cp, &endcp, rad); if(*endcp == '.') endcp++; tp = new_ex_tree(); tp->type = EX_LIT; tp->data.lit = value; tp->cp = endcp; return tp; } free(label); } /* Now check for a symbol */ { char *label; int local; SYMBOL *sym; /* Optimization opportunity: I don't really need to call get_symbol a second time. */ if(!(label = get_symbol(cp, &cp, &local))) { tp = ex_err(NULL, cp); /* Not a valid label. */ return tp; } sym = lookup_sym(label, &symbol_st); if(sym == NULL) { /* A symbol from the "PST", which means an instruction code. */ sym = lookup_sym(label, &system_st); } if(sym != NULL) { tp = new_ex_tree(); tp->cp = cp; tp->type = EX_SYM; tp->data.symbol = sym; free(label); return tp; } /* The symbol was not found. Create an "undefined symbol" reference. */ sym = memcheck(malloc(sizeof(SYMBOL))); sym->label = label; sym->flags = UNDEFINED | local; sym->stmtno = stmtno; sym->next = NULL; sym->section = &absolute_section; sym->value = 0; tp = new_ex_tree(); tp->cp = cp; tp->type = EX_UNDEFINED_SYM; tp->data.symbol = sym; return tp; } } /* Diagnostic: symflags returns a char* which gives flags I can use to show the context of a symbol. */ static char *symflags(SYMBOL *sym) { static char temp[8]; char *fp = temp; if(sym->flags & GLOBAL) *fp++ = 'G'; if(sym->flags & PERMANENT) *fp++ = 'P'; if(sym->flags & DEFINITION) *fp++ = 'D'; *fp = 0; return fp; } /* Diagnostic: print an expression tree. I used this in various places to help me diagnose parse problems, by putting in calls to print_tree when I didn't understand why something wasn't working. This is currently dead code, nothing calls it; but I don't want it to go away. Hopefully the compiler will realize when it's dead, and eliminate it. */ static void print_tree(FILE *printfile, EX_TREE *tp, int depth) { SYMBOL *sym; switch(tp->type) { case EX_LIT: fprintf(printfile, "%o", tp->data.lit & 0177777); break; case EX_SYM: case EX_TEMP_SYM: sym = tp->data.symbol; fprintf(printfile, "%s{%s%o:%s}", tp->data.symbol->label, symflags(sym), sym->value, sym->section->label); break; case EX_UNDEFINED_SYM: fprintf(printfile, "%s{%o:undefined}", tp->data.symbol->label, tp->data.symbol->value); break; case EX_COM: fprintf(printfile, "^C<"); print_tree(printfile, tp->data.child.left, depth+4); fprintf(printfile, ">"); break; case EX_NEG: fprintf(printfile, "-<"); print_tree(printfile, tp->data.child.left, depth+4); fputc('>', printfile); break; case EX_ERR: fprintf(printfile, "{expression error}"); if(tp->data.child.left) { fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('>', printfile); } break; case EX_ADD: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('+', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; case EX_SUB: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('-', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; case EX_MUL: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('*', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; case EX_DIV: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('/', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; case EX_AND: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('&', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; case EX_OR: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth+4); fputc('!', printfile); print_tree(printfile, tp->data.child.right, depth+4); fputc('>', printfile); break; } if(depth == 0) fputc('\n', printfile); } /* free_tree frees an expression tree. */ static void free_tree(EX_TREE *tp) { switch(tp->type) { case EX_UNDEFINED_SYM: case EX_TEMP_SYM: free(tp->data.symbol->label); free(tp->data.symbol); case EX_LIT: case EX_SYM: free(tp); break; case EX_COM: case EX_NEG: free_tree(tp->data.child.left); free(tp); break; case EX_ERR: if(tp->data.child.left) free_tree(tp->data.child.left); free(tp); break; case EX_ADD: case EX_SUB: case EX_MUL: case EX_DIV: case EX_AND: case EX_OR: free_tree(tp->data.child.left); free_tree(tp->data.child.right); free(tp); break; } } /* new_temp_sym allocates a new EX_TREE entry of type "TEMPORARY SYMBOL" (slight semantic difference from "UNDEFINED"). */ static EX_TREE *new_temp_sym(char *label, SECTION *section, unsigned value) { SYMBOL *sym; EX_TREE *tp; sym = memcheck(malloc(sizeof(SYMBOL))); sym->label = memcheck(strdup(label)); sym->flags = 0; sym->stmtno = stmtno; sym->next = NULL; sym->section = section; sym->value = value; tp = new_ex_tree(); tp->type = EX_TEMP_SYM; tp->data.symbol = sym; return tp; } #define RELTYPE(tp) (((tp)->type == EX_SYM || (tp)->type == EX_TEMP_SYM) && \ (tp)->data.symbol->section->flags & PSECT_REL) /* evaluate "evaluates" an EX_TREE, ideally trying to produce a constant value, else a symbol plus an offset. */ static EX_TREE *evaluate(EX_TREE *tp, int undef) { EX_TREE *res; char *cp = tp->cp; switch(tp->type) { case EX_SYM: { SYMBOL *sym = tp->data.symbol; /* Change some symbols to "undefined" */ if(undef) { int change = 0; #if 0 /* I'd prefer this behavior, but MACRO.SAV is a bit too primitive. */ /* A temporary symbol defined later is "undefined." */ if(!(sym->flags & PERMANENT) && sym->stmtno > stmtno) change = 1; #endif /* A global symbol with no assignment is "undefined." */ /* Go figure. */ if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) change = 1; if(change) { res = new_temp_sym(tp->data.symbol->label, tp->data.symbol->section, tp->data.symbol->value); res->type = EX_UNDEFINED_SYM; break; } } /* Turn defined absolute symbol to a literal */ if(!(sym->section->flags & PSECT_REL) && (sym->flags & (GLOBAL|DEFINITION)) != GLOBAL && sym->section->type != REGISTER) { res = new_ex_lit(sym->value); break; } /* Make a temp copy of any reference to "." since it might change as complex relocatable expressions are written out */ if(strcmp(sym->label, ".") == 0) { res = new_temp_sym(".", sym->section, sym->value); break; } /* Copy other symbol reference verbatim. */ res = new_ex_tree(); res->type = EX_SYM; res->data.symbol = tp->data.symbol; res->cp = tp->cp; break; } case EX_LIT: res = new_ex_tree(); *res = *tp; break; case EX_TEMP_SYM: case EX_UNDEFINED_SYM: /* Copy temp and undefined symbols */ res = new_temp_sym(tp->data.symbol->label, tp->data.symbol->section, tp->data.symbol->value); res->type = tp->type; break; case EX_COM: /* Complement */ tp = evaluate(tp->data.child.left, undef); if(tp->type == EX_LIT) { /* Complement the literal */ res = new_ex_lit(~tp->data.lit); free_tree(tp); } else { /* Copy verbatim. */ res = new_ex_tree(); res->type = EX_NEG; res->cp = tp->cp; res->data.child.left = tp; } break; case EX_NEG: tp = evaluate(tp->data.child.left, undef); if(tp->type == EX_LIT) { /* negate literal */ res = new_ex_lit((unsigned)-(int)tp->data.lit); free_tree(tp); } else if(tp->type == EX_SYM || tp->type == EX_TEMP_SYM) { /* Make a temp sym with the negative value of the given sym (this works for symbols within relocatable sections too) */ res = new_temp_sym("*TEMP", tp->data.symbol->section, (unsigned)-(int)tp->data.symbol->value); res->cp = tp->cp; free_tree(tp); } else { /* Copy verbatim. */ res = new_ex_tree(); res->type = EX_NEG; res->cp = tp->cp; res->data.child.left = tp; } break; case EX_ERR: /* Copy */ res = ex_err(tp->data.child.left, tp->cp); break; case EX_ADD: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Both literals? Sum them and return result. */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit + right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A+x == x+A. Simplify by putting the literal on the right */ if(left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if(right->type == EX_LIT && /* Anything plus 0 == itself */ right->data.lit == 0) { res = left; free_tree(right); break; } /* Relative symbol plus lit is replaced with a temp sym holding the sum */ if(RELTYPE(left) && right->type == EX_LIT) { SYMBOL *sym = left->data.symbol; res = new_temp_sym("*ADD", sym->section, sym->value + right->data.lit); free_tree(left); free_tree(right); break; } /* Associative: +y == A+ */ /* and if x+y is constant, I can do that math. */ if(left->type == EX_ADD && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if(leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit += right->data.lit; free_tree(right); break; } } /* Associative: +y == A+ */ /* and if y-x is constant, I can do that math. */ if(left->type == EX_SUB && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if(leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit = right->data.lit - leftright->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_ADD; res->data.child.left = left; res->data.child.right = right; } break; case EX_SUB: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Both literals? Subtract them and return a lit. */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit - right->data.lit); free_tree(left); free_tree(right); break; } if(right->type == EX_LIT && /* Symbol minus 0 == symbol */ right->data.lit == 0) { res = left; free_tree(right); break; } /* A relocatable minus an absolute - make a new temp sym to represent that. */ if(RELTYPE(left) && right->type == EX_LIT) { SYMBOL *sym = left->data.symbol; res = new_temp_sym("*SUB", sym->section, sym->value - right->data.lit); free_tree(left); free_tree(right); break; } if(RELTYPE(left) && RELTYPE(right) && left->data.symbol->section == right->data.symbol->section) { /* Two defined symbols in the same psect. Resolve their difference as a literal. */ res = new_ex_lit(left->data.symbol->value - right->data.symbol->value); free_tree(left); free_tree(right); break; } /* Associative: -y == A+ */ /* and if x-y is constant, I can do that math. */ if(left->type == EX_ADD && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if(leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit -= right->data.lit; free_tree(right); break; } } /* Associative: -y == A- */ /* and if x+y is constant, I can do that math. */ if(left->type == EX_SUB && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if(leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit += right->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_SUB; res->data.child.left = left; res->data.child.right = right; } break; case EX_MUL: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Can only multiply if both are literals */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit * right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A*x == x*A. Simplify by putting the literal on the right */ if(left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if(right->type == EX_LIT && /* Symbol times 1 == symbol */ right->data.lit == 1) { res = left; free_tree(right); break; } if(right->type == EX_LIT && /* Symbol times 0 == 0 */ right->data.lit == 0) { res = right; free_tree(left); break; } /* Associative: *y == A* */ /* If x*y is constant, I can do this math. */ /* Is this safe? I will potentially be doing it */ /* with greater accuracy than the target platform. */ /* Hmmm. */ if(left->type == EX_MUL && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if(leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit *= right->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_MUL; res->data.child.left = left; res->data.child.right = right; } break; case EX_DIV: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Can only divide if both are literals */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit / right->data.lit); free_tree(left); free_tree(right); break; } if(right->type == EX_LIT && /* Symbol divided by 1 == symbol */ right->data.lit == 1) { res = left; free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_DIV; res->data.child.left = left; res->data.child.right = right; } break; case EX_AND: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Operate if both are literals */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit & right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A&x == x&A. Simplify by putting the literal on the right */ if(left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if(right->type == EX_LIT && /* Symbol AND 0 == 0 */ right->data.lit == 0) { res = new_ex_lit(0); free_tree(left); free_tree(right); break; } if(right->type == EX_LIT && /* Symbol AND 0177777 == symbol */ right->data.lit == 0177777) { res = left; free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_AND; res->data.child.left = left; res->data.child.right = right; } break; case EX_OR: { EX_TREE *left, *right; left = evaluate(tp->data.child.left, undef); right = evaluate(tp->data.child.right, undef); /* Operate if both are literals */ if(left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit | right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A!x == x!A. Simplify by putting the literal on the right */ if(left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if(right->type == EX_LIT && /* Symbol OR 0 == symbol */ right->data.lit == 0) { res = left; free_tree(right); break; } if(right->type == EX_LIT && /* Symbol OR 0177777 == 0177777 */ right->data.lit == 0177777) { res = new_ex_lit(0177777); free_tree(left); free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_tree(); res->type = EX_OR; res->data.child.left = left; res->data.child.right = right; } break; } res->cp = cp; return res; } /* parse_expr - this gets called everywhere. It parses and evaluates an arithmetic expression. */ EX_TREE *parse_expr(char *cp, int undef) { EX_TREE *expr; EX_TREE *value; expr = parse_binary(cp, 0, 0); /* Parse into a tree */ value = evaluate(expr, undef); /* Perform the arithmetic */ value->cp = expr->cp; /* Pointer to end of text is part of the rootmost node */ free_tree(expr); /* Discard parse in favor of evaluation */ return value; } /* free_addr_mode frees the storage consumed by an addr_mode */ static void free_addr_mode(ADDR_MODE *mode) { if(mode->offset) free_tree(mode->offset); mode->offset = NULL; } /* Get the register indicated by the expression */ #define NO_REG 0777 static unsigned get_register(EX_TREE *expr) { unsigned reg; if(expr->type == EX_LIT && expr->data.lit <= 7) { reg = expr->data.lit; return reg; } if(expr->type == EX_SYM && expr->data.symbol->section->type == REGISTER) { reg = expr->data.symbol->value; return reg; } return NO_REG; } /* get_mode - parse a general addressing mode. */ int get_mode(char *cp, char **endp, ADDR_MODE *mode) { EX_TREE *value; mode->offset = NULL; mode->rel = 0; mode->type = 0; cp = skipwhite(cp); /* @ means "indirect," sets bit 3 */ if(*cp == '@') { cp++; mode->type |= 010; } /* Immediate modes #imm and @#imm */ if(*cp == '#') { cp++; mode->type |= 027; mode->offset = parse_expr(cp, 0); if(endp) *endp = mode->offset->cp; return TRUE; } /* Check for -(Rn) */ if(*cp == '-') { char *tcp = skipwhite(cp + 1); if(*tcp++ == '(') { unsigned reg; /* It's -(Rn) */ value = parse_expr(tcp, 0); reg = get_register(value); if(reg == NO_REG || (tcp = skipwhite(value->cp), *tcp++ != ')')) { free_tree(value); return FALSE; } mode->type |= 040 | reg; if(endp) *endp = tcp; free_tree(value); return TRUE; } } /* Check for (Rn) */ if(*cp == '(') { char *tcp; unsigned reg; value = parse_expr(cp + 1, 0); reg = get_register(value); if(reg == NO_REG || (tcp = skipwhite(value->cp), *tcp++ != ')')) { free_tree(value); return FALSE; } tcp = skipwhite(tcp); if(*tcp == '+') { tcp++; /* It's (Rn)+ */ if(endp) *endp = tcp; mode->type |= 020 | reg; free_tree(value); return TRUE; } if(mode->type == 010) /* For @(Rn) there's an implied 0 offset */ { mode->offset = new_ex_lit(0); mode->type |= 060 | reg; free_tree(value); if(endp) *endp = tcp; return TRUE; } mode->type |= 010 | reg; /* Mode 10 is register indirect as in (Rn) */ free_tree(value); if(endp) *endp = tcp; return TRUE; } /* Modes with an offset */ mode->offset = parse_expr(cp, 0); cp = skipwhite(mode->offset->cp); if(*cp == '(') { unsigned reg; /* indirect register plus offset */ value = parse_expr(cp+1, 0); reg = get_register(value); if(reg == NO_REG || (cp = skipwhite(value->cp), *cp++ != ')')) { free_tree(value); return FALSE; /* Syntax error in addressing mode */ } mode->type |= 060 | reg; free_tree(value); if(endp) *endp = cp; return TRUE; } /* Plain old expression. */ if(endp) *endp = cp; /* It might be a register, though. */ if(mode->offset->type == EX_SYM) { SYMBOL *sym = mode->offset->data.symbol; if(sym->section->type == REGISTER) { free_tree(mode->offset); mode->offset = NULL; mode->type |= sym->value; return TRUE; } } /* It's either 067 (PC-relative) or 037 (absolute) mode, depending */ /* on user option. */ if(mode->type & 010) /* Have already noted indirection? */ { mode->type |= 067; /* If so, then PC-relative is the only option */ mode->rel = 1; /* Note PC-relative */ } else if(enabl_ama) /* User asked for absolute adressing? */ { mode->type |= 037; /* Give it to him. */ } else { mode->type |= 067; /* PC-relative */ mode->rel = 1; /* Note PC-relative */ } return TRUE; } /* implicit_gbl is a self-recursive routine that adds undefined symbols to the "implicit globals" symbol table. */ void implicit_gbl(EX_TREE *value) { if(pass) return; /* Only do this in first pass */ if(!enabl_gbl) return; /* Option not enabled, don't do it. */ switch(value->type) { case EX_UNDEFINED_SYM: { SYMBOL *sym; if(!(value->data.symbol->flags & LOCAL)) /* Unless it's a local symbol, */ { sym = add_sym(value->data.symbol->label, 0, GLOBAL, &absolute_section, &implicit_st); } } break; case EX_LIT: case EX_SYM: return; case EX_ADD: case EX_SUB: case EX_MUL: case EX_DIV: case EX_AND: case EX_OR: implicit_gbl(value->data.child.right); /* falls into... */ case EX_COM: case EX_NEG: implicit_gbl(value->data.child.left); break; case EX_ERR: if(value->data.child.left) implicit_gbl(value->data.child.left); break; } } /* Done between the first and second passes */ /* Migrates the symbols from the "implicit" table into the main table. */ static void migrate_implicit(void) { SYMBOL_ITER iter; SYMBOL *isym, *sym; for(isym = first_sym(&implicit_st, &iter); isym != NULL; isym = next_sym(&implicit_st, &iter)) { sym = lookup_sym(isym->label, &symbol_st); if(sym) continue; // It's already in there. Great. sym = add_sym(isym->label, isym->value, isym->flags, isym->section, &symbol_st); // Just one other thing - migrate the stmtno sym->stmtno = isym->stmtno; } } static int express_sym_offset(EX_TREE *value, SYMBOL **sym, unsigned *offset) { implicit_gbl(value); /* Translate tree's undefined syms into global syms */ /* Internally relocatable symbols will have been summed down into EX_TEMP_SYM's. */ if(value->type == EX_SYM || value->type == EX_TEMP_SYM) { *sym = value->data.symbol; *offset = 0; return 1; } /* What remains is external symbols. */ if(value->type == EX_ADD) { EX_TREE *left = value->data.child.left; EX_TREE *right = value->data.child.right; if((left->type != EX_SYM && left->type != EX_UNDEFINED_SYM) || right->type != EX_LIT) return 0; /* Failed. */ *sym = left->data.symbol; *offset = right->data.lit; return 1; } if(value->type == EX_SUB) { EX_TREE *left = value->data.child.left; EX_TREE *right = value->data.child.right; if((left->type != EX_SYM && left->type != EX_UNDEFINED_SYM) || right->type != EX_LIT) return 0; /* Failed. */ *sym = left->data.symbol; *offset = (unsigned)-(int)(right->data.lit); return 1; } return 0; } /* Translate an EX_TREE into a TEXT_COMPLEX suitable for encoding into the object file. */ int complex_tree(TEXT_COMPLEX *tx, EX_TREE *tree) { switch(tree->type) { case EX_LIT: text_complex_lit(tx, tree->data.lit); return 1; case EX_TEMP_SYM: case EX_SYM: { SYMBOL *sym = tree->data.symbol; if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) { text_complex_global(tx, sym->label); } else { text_complex_psect(tx, sym->section->sector, sym->value); } } return 1; case EX_COM: if(!complex_tree(tx, tree->data.child.left)) return 0; text_complex_com(tx); return 1; case EX_NEG: if(!complex_tree(tx, tree->data.child.left)) return 0; text_complex_neg(tx); return 1; case EX_ADD: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_add(tx); return 1; case EX_SUB: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_sub(tx); return 1; case EX_MUL: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_mul(tx); return 1; case EX_DIV: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_div(tx); return 1; case EX_AND: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_and(tx); return 1; case EX_OR: if(!complex_tree(tx, tree->data.child.left)) return 0; if(!complex_tree(tx, tree->data.child.right)) return 0; text_complex_or(tx); return 1; default: return 0; } } /* store a word which is represented by a complex expression. */ static void store_complex(STREAM *refstr, TEXT_RLD *tr, int size, EX_TREE *value) { TEXT_COMPLEX tx; change_dot(tr, size); /* About to store - update DOT */ implicit_gbl(value); /* Turn undefined symbols into globals */ text_complex_begin(&tx); /* Open complex expression */ if(!complex_tree(&tx, value)) /* Translate */ { report(refstr, "Invalid expression\n"); store_word(refstr, tr, size, 0); } else { list_word(refstr, DOT, 0, size, "C"); text_complex_commit(tr, &DOT, size, &tx, 0); } } /* store_complex_displaced is the same as store_complex but uses the "displaced" RLD code */ static void store_complex_displaced(STREAM *refstr, TEXT_RLD *tr, int size, EX_TREE *value) { TEXT_COMPLEX tx; change_dot(tr, size); implicit_gbl(value); /* Turn undefined symbols into globals */ text_complex_begin(&tx); if(!complex_tree(&tx, value)) { report(refstr, "Invalid expression\n"); store_word(refstr, tr, size, 0); } else { list_word(refstr, DOT, 0, size, "C"); text_complex_commit_displaced(tr, &DOT, size, &tx, 0); } } /* mode_extension - writes the extension word required by an addressing mode */ static void mode_extension(TEXT_RLD *tr, ADDR_MODE *mode, STREAM *str) { EX_TREE *value = mode->offset; SYMBOL *sym; unsigned offset; /* Also frees the mode. */ if(value == NULL) { free_addr_mode(mode); return; } if(value->type == EX_LIT) { if(mode->rel) /* PC-relative? */ store_displaced_word(str, tr, 2, value->data.lit); else store_word(str, tr, 2, value->data.lit); /* Just a known value. */ } else if(express_sym_offset(value, &sym, &offset)) { if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) { /* Reference to a global symbol. */ /* Global symbol plus offset */ if(mode->rel) store_global_displaced_offset_word(str, tr, 2, offset, sym->label); else store_global_offset_word(str, tr, 2, offset, sym->label); } else { /* Relative to non-external symbol. */ if(current_pc->section == sym->section) { /* In the same section */ if(mode->rel) { /* I can compute this myself. */ store_word(str, tr, 2, sym->value + offset - DOT - 2); } else store_internal_word(str, tr, 2, sym->value+offset); } else { /* In a different section */ if(mode->rel) store_psect_displaced_offset_word(str, tr, 2, sym->value+offset, sym->section->label); else store_psect_offset_word(str, tr, 2, sym->value+offset, sym->section->label); } } } else { /* Complex relocation */ if(mode->rel) store_complex_displaced(str, tr, 2, mode->offset); else store_complex(str, tr, 2, mode->offset); } free_addr_mode(mode); } /* eval_defined - take an EX_TREE and returns TRUE if the tree represents "defined" symbols. */ int eval_defined(EX_TREE *value) { switch(value->type) { case EX_LIT: return 1; case EX_SYM: return 1; case EX_UNDEFINED_SYM: return 0; case EX_AND: return eval_defined(value->data.child.left) && eval_defined(value->data.child.right); case EX_OR: return eval_defined(value->data.child.left) || eval_defined(value->data.child.right); default: return 0; } } /* eval_undefined - take an EX_TREE and returns TRUE if it represents "undefined" symbols. */ int eval_undefined(EX_TREE *value) { switch(value->type) { case EX_UNDEFINED_SYM: return 1; case EX_SYM: return 0; case EX_AND: return eval_undefined(value->data.child.left) && eval_undefined(value->data.child.right); case EX_OR: return eval_undefined(value->data.child.left) || eval_undefined(value->data.child.right); default: return 0; } } /* push_cond - a new conditional (.IF) block has been activated. Push it's context. */ void push_cond(int ok, STREAM *str) { last_cond++; assert(last_cond < MAX_CONDS); conds[last_cond].ok = ok; conds[last_cond].file = memcheck(strdup(str->name)); conds[last_cond].line = str->line; } /* pop_cond - pop stacked conditionals. */ void pop_cond(int to) { while(last_cond > to) { free(conds[last_cond].file); last_cond--; } } /* Parses a string from the input stream. */ /* If not bracketed by <...> or ^/.../, then */ /* the string is delimited by trailing comma or whitespace. */ /* Allows nested <>'s */ char *getstring(char *cp, char **endp) { int len; int start; char *str; if(!brackrange(cp, &start, &len, endp)) { start = 0; len = strcspn(cp, " \t\n,;"); if(endp) *endp = cp + len; } str = memcheck(malloc(len + 1)); memcpy(str, cp + start, len); str[len] = 0; return str; } /* Get what would be the operation code from the line. */ /* Used to find the ends of streams without evaluating them, like finding the closing .ENDM on a macro definition */ SYMBOL *get_op(char *cp, char **endp) { int local; char *label; SYMBOL *op; cp = skipwhite(cp); if(EOL(*cp)) return NULL; label = get_symbol(cp, &cp, &local); if(label == NULL) return NULL; /* No operation code. */ cp = skipwhite(cp); if(*cp == ':') /* A label definition? */ { cp++; if(*cp == ':') cp++; /* Skip it */ free(label); label = get_symbol(cp, &cp, NULL); if(label == NULL) return NULL; } op = lookup_sym(label, &system_st); free(label); if(endp) *endp = cp; return op; } /* Here's where I pretend I'm a C++ compiler. :-/ */ /* *** derive a MACRO_STREAM from a BUFFER_STREAM with a few other args */ typedef struct macro_stream { BUFFER_STREAM bstr; /* Base class: buffer stream */ int nargs; /* Add number-of-macro-arguments */ int cond; /* Add saved conditional stack */ } MACRO_STREAM; /* macro_stream_delete is called when a macro expansion is exhausted. The unique behavior is to unwind any stacked conditionals. This allows a nested .MEXIT to work. */ void macro_stream_delete(STREAM *str) { MACRO_STREAM *mstr = (MACRO_STREAM *)str; pop_cond(mstr->cond); buffer_stream_delete(str); } STREAM_VTBL macro_stream_vtbl = { macro_stream_delete, buffer_stream_gets, buffer_stream_rewind }; STREAM *new_macro_stream(STREAM *refstr, BUFFER *buf, MACRO *mac, ARG *args) { MACRO_STREAM *mstr = memcheck(malloc(sizeof(MACRO_STREAM))); { char *name = memcheck(malloc(strlen(refstr->name) + 32)); sprintf(name, "%s:%d->%s", refstr->name, refstr->line, mac->sym.label); buffer_stream_construct(&mstr->bstr, buf, name); free(name); } mstr->bstr.stream.vtbl = ¯o_stream_vtbl; /* Count the args and save their number */ for(mstr->nargs = 0; args; args = args->next, mstr->nargs++) ; mstr->cond = last_cond; return &mstr->bstr.stream; } /* read_body fetches the body of .MACRO, .REPT, .IRP, or .IRPC into a BUFFER. */ void read_body(STACK *stack, BUFFER *gb, char *name, int called) { int nest; /* Read the stream in until the end marker is hit */ /* Note: "called" says that this body is being pulled from a macro library, and so under no circumstance should it be listed. */ nest = 1; for(;;) { SYMBOL *op; char *nextline; char *cp; nextline = stack_gets(stack); /* Now read the line */ if(nextline == NULL) /* End of file. */ { report(stack->top, "Macro body not closed\n"); break; } if(!called && (list_level - 1 + list_md) > 0) { list_flush(); list_source(stack->top, nextline); } op = get_op(nextline, &cp); if(op == NULL) /* Not a pseudo-op */ { buffer_append_line(gb, nextline); continue; } if(op->section->type == PSEUDO) { if(op->value == P_MACRO || op->value == P_REPT || op->value == P_IRP || op->value == P_IRPC) nest++; if(op->value == P_ENDM || op->value == P_ENDR) { nest--; /* If there's a name on the .ENDM, then */ /* close the body early if it matches the definition */ if(name && op->value == P_ENDM) { cp = skipwhite(cp); if(!EOL(*cp)) { char *label = get_symbol(cp, &cp, NULL); if(label) { if(strcmp(label, name) == 0) nest = 0; /* End of macro body. */ free(label); } } } } if(nest == 0) return; /* All done. */ } buffer_append_line(gb, nextline); } } /* Diagnostic: dumpmacro dumps a macro definition to stdout. I used this for debugging; it's not called at all right now, but I hate to delete good code. */ void dumpmacro(MACRO *mac, FILE *fp) { ARG *arg; fprintf(fp, ".MACRO %s ", mac->sym.label); for(arg = mac->args; arg != NULL; arg = arg->next) { fputs(arg->label, fp); if(arg->value) { fputc('=', fp); fputs(arg->value, fp); } fputc(' ', fp); } fputc('\n', fp); fputs(mac->text->buffer, fp); fputs(".ENDM\n", fp); } /* defmacro - define a macro. */ /* Also used by .MCALL to pull macro definitions from macro libraries */ MACRO *defmacro(char *cp, STACK *stack, int called) { MACRO *mac; ARG *arg, **argtail; char *label; cp = skipwhite(cp); label = get_symbol(cp, &cp, NULL); if(label == NULL) { report(stack->top, "Invalid macro definition\n"); return NULL; } /* Allow redefinition of a macro; new definition replaces the old. */ mac = (MACRO *)lookup_sym(label, ¯o_st); if(mac) { /* Remove from the symbol table... */ remove_sym(&mac->sym, ¯o_st); free_macro(mac); } mac = new_macro(label); add_table(&mac->sym, ¯o_st); argtail = &mac->args; cp = skipdelim(cp); while(!EOL(*cp)) { arg = new_arg(); if(arg->locsym = (*cp == '?')) /* special argument flag? */ cp++; arg->label = get_symbol(cp, &cp, NULL); if(arg->label == NULL) { /* It turns out that I have code which is badly formatted but which MACRO.SAV assembles. Sigh. */ /* So, just quit defining arguments. */ break; #if 0 report(str, "Illegal macro argument\n"); remove_sym(&mac->sym, ¯o_st); free_macro(mac); return NULL; #endif } cp = skipwhite(cp); if(*cp == '=') { /* Default substitution given */ arg->value = getstring(cp+1, &cp); if(arg->value == NULL) { report(stack->top, "Illegal macro argument\n"); remove_sym(&mac->sym, ¯o_st); free_macro(mac); return NULL; } } /* Append to list of arguments */ arg->next = NULL; *argtail = arg; argtail = &arg->next; cp = skipdelim(cp); } /* Read the stream in until the end marker is hit */ { BUFFER *gb; int levelmod = 0; gb = new_buffer(); if(!called && !list_md) { list_level--; levelmod = 1; } read_body(stack, gb, mac->sym.label, called); list_level += levelmod; if(mac->text != NULL) /* Discard old macro body */ buffer_free(mac->text); mac->text = gb; } return mac; } /* find_arg - looks for an arg with the given name in the given argument list */ static ARG *find_arg(ARG *arg, char *name) { for(; arg != NULL; arg = arg->next) { if(strcmp(arg->label, name) == 0) return arg; } return NULL; } /* subst_args - given a BUFFER and a list of args, generate a new BUFFER with argument replacement having taken place. */ BUFFER *subst_args(BUFFER *text, ARG *args) { char *in; char *begin; BUFFER *gb; char *label; ARG *arg; gb = new_buffer(); /* Blindly look for argument symbols in the input. */ /* Don't worry about quotes or comments. */ for(begin = in = text->buffer; in < text->buffer + text->length;) { char *next; if(issym(*in)) { label = get_symbol(in, &next, NULL); if(label) { if(arg = find_arg(args, label)) { /* An apostrophy may appear before or after the symbol. */ /* In either case, remove it from the expansion. */ if(in > begin && in[-1] == '\'') in --; /* Don't copy it. */ if(*next == '\'') next++; /* Copy prior characters */ buffer_appendn(gb, begin, in-begin); /* Copy replacement string */ buffer_append_line(gb, arg->value); in = begin = next; --in; /* prepare for subsequent increment */ } free(label); in = next; } else in++; } else in++; } /* Append the rest of the text */ buffer_appendn(gb, begin, in - begin); return gb; /* Done. */ } /* eval_arg - the language allows an argument expression to be given as "\expression" which means, evaluate the expression and substitute the numeric value in the current radix. */ void eval_arg(STREAM *refstr, ARG *arg) { /* Check for value substitution */ if(arg->value[0] == '\\') { EX_TREE *value = parse_expr(arg->value+1, 0); unsigned word = 0; char temp[10]; if(value->type != EX_LIT) { report(refstr, "Constant value required\n"); } else word = value->data.lit; free_tree(value); /* printf can't do base 2. */ my_ultoa(word & 0177777, temp, radix); free(arg->value); arg->value = memcheck(strdup(temp)); } } /* expandmacro - return a STREAM containing the expansion of a macro */ STREAM *expandmacro(STREAM *refstr, MACRO *mac, char *cp) { ARG *arg, *args, *macarg; char *label; STREAM *str; BUFFER *buf; args = NULL; arg = NULL; /* Parse the arguments */ while(!EOL(*cp)) { char *nextcp; /* Check for named argument */ label = get_symbol(cp, &nextcp, NULL); if(label && (nextcp = skipwhite(nextcp), *nextcp == '=') && (macarg = find_arg(mac->args, label))) { /* Check if I've already got a value for it */ if(find_arg(args, label) != NULL) { report(refstr, "Duplicate submission of keyword " "argument %s\n", label); free(label); free_args(args); return NULL; } arg = new_arg(); arg->label = label; nextcp = skipwhite(nextcp+1); arg->value = getstring(nextcp, &nextcp); } else { if(label) free(label); /* Find correct positional argument */ for(macarg = mac->args; macarg != NULL; macarg = macarg->next) { if(find_arg(args, macarg->label) == NULL) break; /* This is the next positional arg */ } if(macarg == NULL) break; /* Don't pick up any more arguments. */ arg = new_arg(); arg->label = memcheck(strdup(macarg->label)); /* Copy the name */ arg->value = getstring(cp, &nextcp); } arg->next = args; args = arg; eval_arg(refstr, arg); /* Check for expression evaluation */ cp = skipdelim(nextcp); } /* Now go back and fill in defaults */ { int locsym; if(last_lsb != lsb) locsym = last_locsym = 32768; else locsym = last_locsym; last_lsb = lsb; for(macarg = mac->args; macarg != NULL; macarg = macarg->next) { arg = find_arg(args, macarg->label); if(arg == NULL) { arg = new_arg(); arg->label = memcheck(strdup(macarg->label)); if(macarg->locsym) { char temp[32]; /* Here's where we generate local labels */ sprintf(temp, "%d$", locsym++); arg->value = memcheck(strdup(temp)); } else if(macarg->value) { arg->value = memcheck(strdup(macarg->value)); } else arg->value = memcheck(strdup("")); arg->next = args; args = arg; } } last_locsym = locsym; } buf = subst_args(mac->text, args); str = new_macro_stream(refstr, buf, mac, args); free_args(args); buffer_free(buf); return str; } /* *** implement REPT_STREAM */ typedef struct rept_stream { BUFFER_STREAM bstr; int count; /* The current repeat countdown */ int savecond; /* conditional stack level at time of expansion */ } REPT_STREAM; /* rept_stream_gets gets a line from a repeat stream. At the end of each count, the coutdown is decreated and the stream is reset to it's beginning. */ char *rept_stream_gets(STREAM *str) { REPT_STREAM *rstr = (REPT_STREAM *)str; char *cp; for(;;) { if((cp = buffer_stream_gets(str)) != NULL) return cp; if(--rstr->count <= 0) return NULL; buffer_stream_rewind(str); } } /* rept_stream_delete unwinds nested conditionals like .MEXIT does. */ void rept_stream_delete(STREAM *str) { REPT_STREAM *rstr = (REPT_STREAM *)str; pop_cond(rstr->savecond); /* complete unterminated conditionals */ buffer_stream_delete(&rstr->bstr.stream); } /* The VTBL */ STREAM_VTBL rept_stream_vtbl = { rept_stream_delete, rept_stream_gets, buffer_stream_rewind }; /* expand_rept is called when a .REPT is encountered in the input. */ STREAM *expand_rept(STACK *stack, char *cp) { EX_TREE *value; BUFFER *gb; REPT_STREAM *rstr; int levelmod; value = parse_expr(cp, 0); if(value->type != EX_LIT) { report(stack->top, ".REPT value must be constant\n"); free_tree(value); return NULL; } gb = new_buffer(); levelmod = 0; if(!list_md) { list_level--; levelmod = 1; } read_body(stack, gb, NULL, FALSE); list_level += levelmod; rstr = memcheck(malloc(sizeof(REPT_STREAM))); { char *name = memcheck(malloc(strlen(stack->top->name) + 32)); sprintf(name, "%s:%d->.REPT", stack->top->name, stack->top->line); buffer_stream_construct(&rstr->bstr, gb, name); free(name); } rstr->count = value->data.lit; rstr->bstr.stream.vtbl = &rept_stream_vtbl; rstr->savecond = last_cond; buffer_free(gb); free_tree(value); return &rstr->bstr.stream; } /* *** implement IRP_STREAM */ typedef struct irp_stream { BUFFER_STREAM bstr; char *label; /* The substitution label */ char *items; /* The substitution items (in source code format) */ int offset; /* Current offset into "items" */ BUFFER *body; /* Original body */ int savecond; /* Saved conditional level */ } IRP_STREAM; /* irp_stream_gets expands the IRP as the stream is read. */ /* Each time an iteration is exhausted, the next iteration is generated. */ char *irp_stream_gets(STREAM *str) { IRP_STREAM *istr = (IRP_STREAM *)str; char *cp; BUFFER *buf; ARG *arg; for(;;) { if((cp = buffer_stream_gets(str)) != NULL) return cp; cp = istr->items + istr->offset; if(!*cp) return NULL; /* No more items. EOF. */ arg = new_arg(); arg->next = NULL; arg->locsym = 0; arg->label = istr->label; arg->value = getstring(cp, &cp); cp = skipdelim(cp); istr->offset = cp - istr->items; eval_arg(str, arg); buf = subst_args(istr->body, arg); free(arg->value); free(arg); buffer_stream_set_buffer(&istr->bstr, buf); buffer_free(buf); } } /* irp_stream_delete - also pops the conditional stack */ void irp_stream_delete(STREAM *str) { IRP_STREAM *istr = (IRP_STREAM *)str; pop_cond(istr->savecond); /* complete unterminated conditionals */ buffer_free(istr->body); free(istr->items); free(istr->label); buffer_stream_delete(str); } STREAM_VTBL irp_stream_vtbl = { irp_stream_delete, irp_stream_gets, buffer_stream_rewind }; /* expand_irp is called when a .IRP is encountered in the input. */ STREAM *expand_irp(STACK *stack, char *cp) { char *label, *items; BUFFER *gb; int levelmod = 0; IRP_STREAM *str; label = get_symbol(cp, &cp, NULL); if(!label) { report(stack->top, "Illegal .IRP syntax\n"); return NULL; } cp = skipdelim(cp); items = getstring(cp, &cp); if(!items) { report(stack->top, "Illegal .IRP syntax\n"); free(label); return NULL; } gb = new_buffer(); levelmod = 0; if(!list_md) { list_level--; levelmod++; } read_body(stack, gb, NULL, FALSE); list_level += levelmod; str = memcheck(malloc(sizeof(IRP_STREAM))); { char *name = memcheck(malloc(strlen(stack->top->name) + 32)); sprintf(name, "%s:%d->.IRP", stack->top->name, stack->top->line); buffer_stream_construct(&str->bstr, NULL, name); free(name); } str->bstr.stream.vtbl = &irp_stream_vtbl; str->body = gb; str->items = items; str->offset = 0; str->label = label; str->savecond = last_cond; return &str->bstr.stream; } /* *** implement IRPC_STREAM */ typedef struct irpc_stream { BUFFER_STREAM bstr; char *label; /* The substitution label */ char *items; /* The substitution items (in source code format) */ int offset; /* Current offset in "items" */ BUFFER *body; /* Original body */ int savecond; /* conditional stack at invocation */ } IRPC_STREAM; /* irpc_stream_gets - same comments apply as with irp_stream_gets, but the substitution is character-by-character */ char *irpc_stream_gets(STREAM *str) { IRPC_STREAM *istr = (IRPC_STREAM *)str; char *cp; BUFFER *buf; ARG *arg; for(;;) { if((cp = buffer_stream_gets(str)) != NULL) return cp; cp = istr->items + istr->offset; if(!*cp) return NULL; /* No more items. EOF. */ arg = new_arg(); arg->next = NULL; arg->locsym = 0; arg->label = istr->label; arg->value = memcheck(malloc(2)); arg->value[0] = *cp++; arg->value[1] = 0; istr->offset = cp - istr->items; buf = subst_args(istr->body, arg); free(arg->value); free(arg); buffer_stream_set_buffer(&istr->bstr, buf); buffer_free(buf); } } /* irpc_stream_delete - also pops contidionals */ void irpc_stream_delete(STREAM *str) { IRPC_STREAM *istr = (IRPC_STREAM *)str; pop_cond(istr->savecond); /* complete unterminated conditionals */ buffer_free(istr->body); free(istr->items); free(istr->label); buffer_stream_delete(str); } STREAM_VTBL irpc_stream_vtbl = { irpc_stream_delete, irpc_stream_gets, buffer_stream_rewind }; /* expand_irpc - called when .IRPC is encountered in the input */ STREAM *expand_irpc(STACK *stack, char *cp) { char *label, *items; BUFFER *gb; int levelmod = 0; IRPC_STREAM *str; label = get_symbol(cp, &cp, NULL); if(!label) { report(stack->top, "Illegal .IRPC syntax\n"); return NULL; } cp = skipdelim(cp); items = getstring(cp, &cp); if(!items) { report(stack->top, "Illegal .IRPC syntax\n"); free(label); return NULL; } gb = new_buffer(); levelmod = 0; if(!list_md) { list_level--; levelmod++; } read_body(stack, gb, NULL, FALSE); list_level += levelmod; str = memcheck(malloc(sizeof(IRPC_STREAM))); { char *name = memcheck(malloc(strlen(stack->top->name) + 32)); sprintf(name, "%s:%d->.IRPC", stack->top->name, stack->top->line); buffer_stream_construct(&str->bstr, NULL, name); free(name); } str->bstr.stream.vtbl = &irpc_stream_vtbl; str->body = gb; str->items = items; str->offset = 0; str->label = label; str->savecond = last_cond; return &str->bstr.stream; } /* go_section - sets current_pc to a new program section */ void go_section(TEXT_RLD *tr, SECTION *sect) { if(current_pc->section == sect) return; /* This is too easy */ /* save current PC value for old section */ current_pc->section->pc = DOT; /* Set current section and PC value */ current_pc->section = sect; DOT = sect->pc; } /* store_value - used to store a value represented by an expression tree into the object file. Used by do_word and .ASCII/.ASCIZ. */ static void store_value(STACK *stack, TEXT_RLD *tr, int size, EX_TREE *value) { SYMBOL *sym; unsigned offset; implicit_gbl(value); /* turn undefined symbols into globals */ if(value->type == EX_LIT) { store_word(stack->top, tr, size, value->data.lit); } else if(!express_sym_offset(value, &sym, &offset)) { store_complex(stack->top, tr, size, value); } else { if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) { store_global_offset_word(stack->top, tr, size, sym->value+offset, sym->label); } else if(sym->section != current_pc->section) { store_psect_offset_word(stack->top, tr, size, sym->value+offset, sym->section->label); } else { store_internal_word(stack->top, tr, size, sym->value+offset); } } } /* do_word - used by .WORD, .BYTE, and implied .WORD. */ static int do_word(STACK *stack, TEXT_RLD *tr, char *cp, int size) { if(size == 2 && (DOT & 1)) { report(stack->top, ".WORD on odd boundary\n"); store_word(stack->top, tr, 1, 0); /* Align it */ } do { EX_TREE *value = parse_expr(cp, 0); store_value(stack, tr, size, value); cp = skipdelim(value->cp); free_tree(value); } while(cp = skipdelim(cp), !EOL(*cp)); return 1; } /* check_branch - check branch distance. */ static int check_branch(STACK *stack, unsigned offset, int min, int max) { int s_offset; /* Sign-extend */ if(offset & 0100000) s_offset = offset | ~0177777; else s_offset = offset & 077777; if(s_offset > max || s_offset < min) { char temp[16]; /* printf can't do signed octal. */ my_ltoa(s_offset, temp, 8); report(stack->top, "Branch target out of range (distance=%s)\n", temp); return 0; } return 1; } /* assemble - read a line from the input stack, assemble it. */ /* This function is way way too large, because I just coded most of the operation code and pseudo-op handling right in line. */ int assemble(STACK *stack, TEXT_RLD *tr) { char *cp; /* Parse character pointer */ char *opcp; /* Points to operation mnemonic text */ char *ncp; /* "next" cp */ char *label; /* A label */ char *line; /* The whole line */ SYMBOL *op; /* The operation SYMBOL */ int local; /* Whether a label is a local label or not */ line = stack_gets(stack); if(line == NULL) return -1; /* Return code for EOF. */ cp = line; /* Frankly, I don't need to keep "line." But I found it quite handy during debugging, to see what the whole operation was, when I'm down to parsing the second operand and things aren't going right. */ stmtno++; /* Increment statement number */ list_source(stack->top, line); /* List source */ if(suppressed) { /* Assembly is suppressed by unsatisfoed conditional. Look for ending and enabling statements. */ op = get_op(cp, &cp); /* Look at operation code */ /* FIXME: this code will blindly look into .REM commentary and find operation codes. Incidentally, so will read_body. */ if(op == NULL) return 1; /* Not found. Don't care. */ if(op->section->type != PSEUDO) return 1; /* Not a pseudo-op. */ switch(op->value) { case P_IF: case P_IFDF: suppressed++; /* Nested. Suppressed. */ break; case P_IFTF: if(suppressed == 1) /* Reduce suppression from 1 to 0. */ suppressed = 0; break; case P_IFF: if(suppressed == 1) /* Can reduce suppression from 1 to 0. */ { if(!conds[last_cond].ok) suppressed = 0; } break; case P_IFT: if(suppressed == 1) /* Can reduce suppression from 1 to 0. */ { if(conds[last_cond].ok) suppressed = 0; } break; case P_ENDC: suppressed--; /* Un-nested. */ if(suppressed == 0) pop_cond(last_cond-1); /* Re-enabled. */ break; } return 1; } /* The line may begin with "label:[:]" */ opcp = cp; if((label = get_symbol(cp, &ncp, &local)) != NULL) { int flag = PERMANENT|DEFINITION|local; SYMBOL *sym; ncp = skipwhite(ncp); if(*ncp == ':') /* Colon, for symbol definition? */ { ncp++; /* maybe it's a global definition */ if(*ncp == ':') { flag |= GLOBAL; /* Yes, include global flag */ ncp++; } sym = add_sym(label, DOT, flag, current_pc->section, &symbol_st); cp = ncp; if(sym == NULL) report(stack->top, "Illegal symbol definition %s\n", label); free(label); /* See if local symbol block should be incremented */ if(!enabl_lsb && !local) lsb++; cp = skipwhite(ncp); opcp = cp; label = get_symbol(cp, &ncp, NULL); /* Now, get what follows */ } } /* PSEUDO P_IIF jumps here. */ reassemble: cp = skipwhite(cp); if(EOL(*cp)) return 1; /* It's commentary. All done. */ if(label) /* Something looks like a label. */ { /* detect assignment */ ncp = skipwhite(ncp); /* The pointer to the text that follows the symbol */ if(*ncp == '=') { unsigned flags; EX_TREE *value; SYMBOL *sym; cp = ncp; /* Symbol assignment. */ flags = DEFINITION|local; cp++; if(*cp == '=') { flags |= GLOBAL; /* Global definition */ cp++; } if(*cp == ':') { flags |= PERMANENT; cp++; } cp = skipwhite(cp); value = parse_expr(cp, 0); /* Special code: if the symbol is the program counter, this is harder. */ if(strcmp(label, ".") == 0) { if(current_pc->section->flags & PSECT_REL) { SYMBOL *sym; unsigned offset; /* Express the given expression as a symbol and an offset. The symbol must not be global, the section must = current. */ if(!express_sym_offset(value, &sym, &offset)) { report(stack->top, "Illegal ORG\n"); } else if((sym->flags & (GLOBAL|DEFINITION)) == GLOBAL) { report(stack->top, "Can't ORG to external location\n"); } else if(sym->flags & UNDEFINED) { report(stack->top, "Can't ORG to undefined sym\n"); } else if(sym->section != current_pc->section) { report(stack->top, "Can't ORG to alternate section " "(use PSECT)\n"); } else { DOT = sym->value + offset; list_value(stack->top, DOT); change_dot(tr, 0); } } else { /* If the current section is absolute, the value must be a literal */ if(value->type != EX_LIT) { report(stack->top, "Can't ORG to non-absolute location\n"); free_tree(value); free(label); return 0; } DOT = value->data.lit; list_value(stack->top, DOT); change_dot(tr, 0); } free_tree(value); free(label); return 1; } /* regular symbols */ if(value->type == EX_LIT) { sym = add_sym(label, value->data.lit, flags, &absolute_section, &symbol_st); } else if(value->type == EX_SYM || value->type == EX_TEMP_SYM) { sym = add_sym(label, value->data.symbol->value, flags, value->data.symbol->section, &symbol_st); } else { report(stack->top, "Complex expression cannot be assigned " "to a symbol\n"); if(!pass) { /* This may work better in pass 2 - something in RT-11 monitor needs the symbol to apear to be defined even if I can't resolve it's value. */ sym = add_sym(label, 0, UNDEFINED, &absolute_section, &symbol_st); } else sym = NULL; } if(sym != NULL) list_value(stack->top, sym->value); free_tree(value); free(label); return sym != NULL; } /* Try to resolve macro */ op = lookup_sym(label, ¯o_st); if(op && op->stmtno < stmtno) { STREAM *macstr; free(label); macstr = expandmacro(stack->top, (MACRO *)op, ncp); stack_push(stack, macstr); /* Push macro expansion onto input stream */ return 1; } /* Try to resolve instruction or pseudo */ op = lookup_sym(label, &system_st); if(op) { cp = ncp; free(label); /* Don't need this hanging around anymore */ switch(op->section->type) { case PSEUDO: switch(op->value) { case P_ENDR: case P_ENDM: case P_SBTTL: case P_LIST: case P_NLIST: case P_PRINT: return 1; /* Accepted, ignored. (An obvious need: get assembly listing controls working. ) */ case P_IDENT: { char endc[6]; int len; cp = skipwhite(cp); endc[0] = *cp++; endc[1] = '\n'; endc[2] = 0; len = strcspn(cp, endc); if(len > 6) len = 6; if(ident) /* An existing ident? */ free(ident); /* Discard it. */ ident = memcheck(malloc(len + 1)); memcpy(ident, cp, len); ident[len] = 0; upcase(ident); return 1; } case P_RADIX: { int old_radix = radix; radix = strtoul(cp, &cp, 10); if(radix != 8 && radix != 10 && radix != 16 && radix != 2) { radix = old_radix; report(stack->top, "Illegal radix\n"); return 0; } return 1; } case P_FLT4: case P_FLT2: { int ok = 1; while(!EOL(*cp)) { unsigned flt[4]; if(parse_float(cp, &cp, (op->value == P_FLT4 ? 4 : 2), flt)) { /* Store the word values */ store_word(stack->top, tr, 2, flt[0]); store_word(stack->top, tr, 2, flt[1]); if(op->value == P_FLT4) { store_word(stack->top, tr, 2, flt[2]); store_word(stack->top, tr, 2, flt[3]); } } else { report(stack->top, "Bad floating point format\n"); ok = 0; } cp = skipdelim(cp); } return ok; } case P_ERROR: report(stack->top, "%.*s\n", strcspn(cp, "\n"), cp); return 0; case P_SAVE: sect_sp++; sect_stack[sect_sp] = current_pc->section; return 1; case P_RESTORE: if(sect_sp < 0) { report(stack->top, "No saved section for .RESTORE\n"); return 0; } else { go_section(tr, sect_stack[sect_sp]); sect_sp++; } return 1; case P_NARG: { STREAM *str; MACRO_STREAM *mstr; int local; label = get_symbol(cp, &cp, &local); if(label == NULL) { report(stack->top, "Bad .NARG syntax\n"); return 0; } /* Walk up the stream stack to find the topmost macro stream */ for(str = stack->top; str != NULL && str->vtbl != ¯o_stream_vtbl; str = str->next) ; if(!str) { report(str, ".NARG not within macro expansion\n"); free(label); return 0; } mstr = (MACRO_STREAM *)str; add_sym(label, mstr->nargs, DEFINITION|local, &absolute_section, &symbol_st); free(label); return 1; } case P_NCHR: { char *string; int local; label = get_symbol(cp, &cp, &local); if(label == NULL) { report(stack->top, "Bad .NCHR syntax\n"); return 0; } cp = skipdelim(cp); string = getstring(cp, &cp); add_sym(label, strlen(string), DEFINITION|local, &absolute_section, &symbol_st); free(label); free(string); return 1; } case P_NTYPE: { ADDR_MODE mode; int local; label = get_symbol(cp, &cp, &local); if(label == NULL) { report(stack->top, "Bad .NTYPE syntax\n"); return 0; } cp = skipdelim(cp); if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Bad .NTYPE addressing mode\n"); free(label); return 0; } add_sym(label, mode.type, DEFINITION|local, &absolute_section, &symbol_st); free_addr_mode(&mode); free(label); return 1; } case P_INCLU: { char *name = getstring(cp, &cp); STREAM *incl; if(name == NULL) { report(stack->top, "Bad .INCLUDE file name\n"); return 0; } incl = new_file_stream(name); if(incl == NULL) { report(stack->top, "Unable to open .INCLUDE file %s\n", name); free(name); return 0; } free(name); stack_push(stack, incl); return 1; } case P_REM: { char quote[4]; /* Read and discard lines until one with a closing quote */ cp = skipwhite(cp); quote[0] = *cp++; quote[1] = '\n'; quote[2] = 0; for(;;) { cp += strcspn(cp, quote); if(*cp == quote[0]) break; /* Found closing quote */ cp = stack_gets(stack); /* Read next input line */ if(cp == NULL) break; /* EOF */ } } return 1; case P_IRP: { STREAM *str = expand_irp(stack, cp); if(str) stack_push(stack, str); return str != NULL; } case P_IRPC: { STREAM *str = expand_irpc(stack, cp); if(str) stack_push(stack, str); return str != NULL; } case P_MCALL: { STREAM *macstr; BUFFER *macbuf; char *maccp; int saveline; MACRO *mac; int i; char macfile[FILENAME_MAX]; char hitfile[FILENAME_MAX]; for(;;) { cp = skipdelim(cp); if(EOL(*cp)) return 1; label = get_symbol(cp, &cp, NULL); if(!label) { report(stack->top, "Illegal .MCALL format\n"); return 0; } /* See if that macro's already defined */ if(lookup_sym(label, ¯o_st)) { free(label); /* Macro already registered. No prob. */ cp = skipdelim(cp); continue; } /* Find the macro in the list of included macro libraries */ macbuf = NULL; for(i = 0; i < nr_mlbs; i++) { if((macbuf = mlb_entry(mlbs[i], label)) != NULL) break; } if(macbuf != NULL) { macstr = new_buffer_stream(macbuf, label); buffer_free(macbuf); } else { strncpy(macfile, label, sizeof(macfile)); strncat(macfile, ".MAC", sizeof(macfile) - strlen(macfile)); my_searchenv(macfile, "MCALL", hitfile, sizeof(hitfile)); if(hitfile[0]) macstr = new_file_stream(hitfile); } if(macstr != NULL) { for(;;) { char *mlabel; maccp = macstr->vtbl->gets(macstr); if(maccp == NULL) break; mlabel = get_symbol(maccp, &maccp, NULL); if(mlabel == NULL) continue; op = lookup_sym(mlabel, &system_st); free(mlabel); if(op == NULL) continue; if(op->value == P_MACRO) break; } if(maccp != NULL) { STACK macstack = { macstr }; int savelist = list_level; saveline = stmtno; list_level = -1; mac = defmacro(maccp, &macstack, TRUE); if(mac == NULL) { report(stack->top, "Failed to define macro " "called %s\n", label); } stmtno = saveline; list_level = savelist; } macstr->vtbl->delete(macstr); } else report(stack->top, "MACRO %s not found\n", label); free(label); } } return 1; case P_MACRO: { MACRO *mac = defmacro(cp, stack, FALSE); return mac != NULL; } case P_MEXIT: { STREAM *macstr; /* Pop a stream from the input. */ /* It must be the first stream, and it must be */ /* a macro, rept, irp, or irpc. */ macstr = stack->top; if(macstr->vtbl != ¯o_stream_vtbl && macstr->vtbl != &rept_stream_vtbl && macstr->vtbl != &irp_stream_vtbl && macstr->vtbl != &irpc_stream_vtbl) { report(stack->top, ".MEXIT not within a macro\n"); return 0; } /* and finally, pop the macro */ stack_pop(stack); return 1; } case P_REPT: { STREAM *reptstr = expand_rept(stack, cp); if(reptstr) stack_push(stack, reptstr); return reptstr != NULL; } case P_ENABL: /* FIXME - add all the rest of the options. */ while(!EOL(*cp)) { label = get_symbol(cp, &cp, NULL); if(strcmp(label, "AMA") == 0) enabl_ama = 1; else if(strcmp(label, "LSB") == 0) { enabl_lsb = 1; lsb++; } else if(strcmp(label, "GBL") == 0) enabl_gbl = 1; free(label); cp = skipdelim(cp); } return 1; case P_DSABL: /* FIXME Ditto as for .ENABL */ while(!EOL(*cp)) { label = get_symbol(cp, &cp, NULL); if(strcmp(label, "AMA") == 0) enabl_ama = 0; else if(strcmp(label, "LSB") == 0) enabl_lsb = 0; else if(strcmp(label, "GBL") == 0) enabl_gbl = 0; free(label); cp = skipdelim(cp); } return 1; case P_LIMIT: store_limits(stack->top, tr); return 1; case P_TITLE: /* accquire module name */ if(module_name != NULL) { free(module_name); } module_name = get_symbol(cp, &cp, NULL); return 1; case P_END: /* Accquire transfer address */ cp = skipwhite(cp); if(!EOL(*cp)) { if(xfer_address) free_tree(xfer_address); xfer_address = parse_expr(cp, 0); } return 1; case P_IFDF: opcp = skipwhite(opcp); cp = opcp + 3; /* Point cp at the "DF" or "NDF" part */ /* Falls into... */ case P_IIF: case P_IF: { EX_TREE *value; int ok; label = get_symbol(cp, &cp, NULL); /* Get condition */ cp = skipdelim(cp); if(strcmp(label, "DF") == 0) { value = parse_expr(cp, 1); cp = value->cp; ok = eval_defined(value); free_tree(value); } else if(strcmp(label, "NDF") == 0) { value = parse_expr(cp, 1); cp = value->cp; ok = eval_undefined(value); free_tree(value); } else if(strcmp(label, "B") == 0) { char *thing; cp = skipwhite(cp); if(!EOL(*cp)) thing = getstring(cp, &cp); else thing = memcheck(strdup("")); ok = (*thing == 0); free(thing); } else if(strcmp(label, "NB") == 0) { char *thing; cp = skipwhite(cp); if(!EOL(*cp)) thing = getstring(cp, &cp); else thing = memcheck(strdup("")); ok = (*thing != 0); free(thing); } else if(strcmp(label, "IDN") == 0) { char *thing1, *thing2; thing1 = getstring(cp, &cp); cp = skipdelim(cp); if(!EOL(*cp)) thing2 = getstring(cp, &cp); else thing2 = memcheck(strdup("")); ok = (strcmp(thing1, thing2) == 0); free(thing1); free(thing2); } else if(strcmp(label, "DIF") == 0) { char *thing1, *thing2; thing1 = getstring(cp, &cp); cp = skipdelim(cp); if(!EOL(*cp)) thing2 = getstring(cp, &cp); else thing2 = memcheck(strdup("")); ok = (strcmp(thing1, thing2) != 0); free(thing1); free(thing2); } else { int sword; unsigned uword; EX_TREE *value = parse_expr(cp, 0); cp = value->cp; if(value->type != EX_LIT) { report(stack->top, "Bad .IF expression\n"); list_value(stack->top, 0); free_tree(value); ok = FALSE; /* Pick something. */ } else { unsigned word; /* Convert to signed and unsigned words */ sword = value->data.lit & 0x7fff; /* FIXME I don't know if the following is portable enough. */ if(value->data.lit & 0x8000) sword |= ~0xFFFF; /* Render negative */ /* Reduce unsigned value to 16 bits */ uword = value->data.lit & 0xffff; if(strcmp(label, "EQ") == 0 || strcmp(label, "Z") == 0) ok = (uword == 0), word = uword; else if(strcmp(label, "NE") == 0 || strcmp(label, "NZ") == 0) ok = (uword != 0), word = uword; else if(strcmp(label, "GT") == 0 || strcmp(label, "G") == 0) ok = (sword > 0), word = sword; else if(strcmp(label, "GE") == 0) ok = (sword >= 0), word = sword; else if(strcmp(label, "LT") == 0 || strcmp(label, "L") == 0) ok = (sword < 0), word = sword; else if(strcmp(label, "LE") == 0) ok = (sword <= 0), word = sword; list_value(stack->top, word); free_tree(value); } } free(label); if(op->value == P_IIF) { stmtno++; /* the second half is a separate statement */ if(ok) { /* The "immediate if" */ /* Only slightly tricky. */ cp = skipdelim(cp); label = get_symbol(cp, &ncp, &local); goto reassemble; } return 1; } push_cond(ok, stack->top); if(!ok) suppressed++; /* Assembly suppressed until .ENDC */ } return 1; case P_IFF: if(last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } if(conds[last_cond].ok) /* Suppress if last cond is true */ suppressed++; return 1; case P_IFT: if(last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } if(!conds[last_cond].ok) /* Suppress if last cond is false */ suppressed++; return 1; case P_IFTF: if(last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } return 1; /* Don't suppress. */ case P_ENDC: if(last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } pop_cond(last_cond-1); return 1; case P_EVEN: if(DOT & 1) { list_word(stack->top, DOT, 0, 1, ""); DOT++; } return 1; case P_ODD: if(!(DOT & 1)) { list_word(stack->top, DOT, 0, 1, ""); DOT++; } return 1; case P_ASECT: go_section(tr, &absolute_section); return 1; case P_CSECT: case P_PSECT: { SYMBOL *sectsym; SECTION *sect; label = get_symbol(cp, &cp, NULL); if(label == NULL) label = memcheck(strdup("")); /* Allow blank */ sectsym = lookup_sym(label, §ion_st); if(sectsym) { sect = sectsym->section; free(label); } else { sect = new_section(); sect->label = label; sect->flags = 0; sect->pc = 0; sect->size = 0; sect->type = USER; sections[sector++] = sect; sectsym = add_sym(label, 0, 0, sect, §ion_st); } if(op->value == P_PSECT) sect->flags |= PSECT_REL; else if(op->value == P_CSECT) sect->flags |= PSECT_REL|PSECT_COM|PSECT_GBL; while(cp = skipdelim(cp), !EOL(*cp)) { /* Parse section options */ label = get_symbol(cp, &cp, NULL); if(strcmp(label, "ABS") == 0) { sect->flags &= ~PSECT_REL; /* Not relative */ sect->flags |= PSECT_COM; /* implies common */ } else if(strcmp(label, "REL") == 0) { sect->flags |= PSECT_REL; /* Is relative */ } else if(strcmp(label, "SAV") == 0) { sect->flags |= PSECT_SAV; /* Is root */ } else if(strcmp(label, "OVR") == 0) { sect->flags |= PSECT_COM; /* Is common */ } else if(strcmp(label, "RW") == 0) { sect->flags &= ~PSECT_RO; /* Not read-only */ } else if(strcmp(label, "RO") == 0) { sect->flags |= PSECT_RO; /* Is read-only */ } else if(strcmp(label, "I") == 0) { sect->flags &= ~PSECT_DATA; /* Not data */ } else if(strcmp(label, "D") == 0) { sect->flags |= PSECT_DATA; /* data */ } else if(strcmp(label, "GBL") == 0) { sect->flags |= PSECT_GBL; /* Global */ } else if(strcmp(label, "LCL") == 0) { sect->flags &= ~PSECT_GBL; /* Local */ } else { report(stack->top, "Unknown flag %s given to " ".PSECT directive\n", label); free(label); return 0; } free(label); } go_section(tr, sect); return 1; } /* end PSECT code */ break; case P_WEAK: case P_GLOBL: { SYMBOL *sym; while(!EOL(*cp)) { /* Loop and make definitions for comma-separated symbols */ label = get_symbol(cp, &ncp, NULL); if(label == NULL) { report(stack->top, "Illegal .GLOBL/.WEAK " "syntax\n"); return 0; } sym = lookup_sym(label, &symbol_st); if(sym) { sym->flags |= GLOBAL| (op->value == P_WEAK ? WEAK : 0); } else sym = add_sym(label, 0, GLOBAL| (op->value == P_WEAK ? WEAK : 0), &absolute_section, &symbol_st); free(label); cp = skipdelim(ncp); } } return 1; case P_WORD: { /* .WORD might be followed by nothing, which is an implicit .WORD 0 */ if(EOL(*cp)) { if(DOT & 1) { report(stack->top, ".WORD on odd " "boundary\n"); DOT++; /* Fix it, too */ } store_word(stack->top, tr, 2, 0); return 1; } else return do_word(stack, tr, cp, 2); } case P_BYTE: if(EOL(*cp)) { /* Blank .BYTE. Same as .BYTE 0 */ store_word(stack->top, tr, 1, 0); return 1; } else return do_word(stack, tr, cp, 1); case P_BLKW: case P_BLKB: { EX_TREE *value = parse_expr(cp, 0); int ok = 1; if(value->type != EX_LIT) { report(stack->top, "Argument to .BLKB/.BLKW " "must be constant\n"); ok = 0; } else { list_value(stack->top, DOT); DOT += value->data.lit * (op->value == P_BLKW ? 2 : 1); change_dot(tr, 0); } free_tree(value); return ok; } case P_ASCIZ: case P_ASCII: { EX_TREE *value; do { cp = skipwhite(cp); if(*cp == '<' || *cp == '^') { /* A byte value */ value = parse_expr(cp, 0); cp = value->cp; store_value(stack, tr, 1, value); free_tree(value); } else { char quote = *cp++; while(*cp && *cp != '\n' && *cp != quote) { store_word(stack->top, tr, 1, *cp++); } cp++; /* Skip closing quote */ } cp = skipwhite(cp); } while(!EOL(*cp)); if(op->value == P_ASCIZ) { store_word(stack->top, tr, 1, 0); } return 1; } case P_RAD50: if(DOT & 1) { report(stack->top, ".RAD50 on odd " "boundary\n"); DOT++; /* Fix it */ } while(!EOL(*cp)) { char endstr[6]; int len; char *radstr; char *radp; endstr[0] = *cp++; endstr[1] = '\n'; endstr[2] = 0; len = strcspn(cp, endstr); radstr = memcheck(malloc(len + 1)); memcpy(radstr, cp, len); radstr[len] = 0; cp += len; if(*cp && *cp != '\n') cp++; for(radp = radstr; *radp;) { unsigned rad; rad = rad50(radp, &radp); store_word(stack->top, tr, 2, rad); } free(radstr); cp = skipwhite(cp); } return 1; default: report(stack->top, "Unimplemented directive %s\n", op->label); return 0; } /* end switch (PSEUDO operation) */ case INSTRUCTION: { /* The PC must always be even. */ if(DOT & 1) { report(stack->top, "Instruction on odd address\n"); DOT++; /* ...and fix it... */ } switch(op->flags & OC_MASK) { case OC_NONE: /* No operands. */ store_word(stack->top, tr, 2, op->value); return 1; case OC_MARK: /* MARK, EMT, TRAP */ { EX_TREE *value; unsigned word; cp = skipwhite(cp); if(*cp == '#') cp++; /* Allow the hash, but don't require it */ value = parse_expr(cp, 0); if(value->type != EX_LIT) { report(stack->top, "Instruction requires " "simple literal operand\n"); word = op->value; } else { word = op->value | value->data.lit; } store_word(stack->top, tr, 2, word); free_tree(value); } return 1; case OC_1GEN: /* One general addressing mode */ { ADDR_MODE mode; unsigned word; if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } if(op->value == 0100 && (mode.type & 07) == 0) { report(stack->top, "JMP Rn is illegal\n"); /* But encode it anyway... */ } /* Build instruction word */ word = op->value | mode.type; store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); } return 1; case OC_2GEN: /* Two general addressing modes */ { ADDR_MODE left, right; unsigned word; if(!get_mode(cp, &cp, &left)) { report(stack->top, "Illegal addressing mode\n"); return 0; } if(*cp++ != ',') { report(stack->top, "Illegal syntax\n"); free_addr_mode(&left); return 0; } if(!get_mode(cp, &cp, &right)) { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&left); return 0; } /* Build instruction word */ word = op->value | left.type << 6 | right.type; store_word(stack->top, tr, 2, word); mode_extension(tr, &left, stack->top); mode_extension(tr, &right, stack->top); } return 1; case OC_BR: /* branches */ { EX_TREE *value; unsigned offset; value = parse_expr(cp, 0); cp = value->cp; /* Relative PSECT or absolute? */ if(current_pc->section->flags & PSECT_REL) { SYMBOL *sym; /* Can't branch unless I can calculate the offset. */ /* You know, I *could* branch between sections if I feed the linker a complex relocation expression to calculate the offset. But I won't. */ if(!express_sym_offset(value, &sym, &offset) || sym->section != current_pc->section) { report(stack->top, "Bad branch target\n"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; } /* Compute the branch offset and check for addressability */ offset += sym->value; offset -= DOT + 2; } else { if(value->type != EX_LIT) { report(stack->top, "Bad branch target\n"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; } offset = value->data.lit - (DOT + 2); } if(!check_branch(stack, offset, -256, 255)) offset = 0; /* Emit the branch code */ offset &= 0777;/* Reduce to 9 bits */ offset >>= 1; /* Shift to become word offset */ store_word(stack->top, tr, 2, op->value | offset); free_tree(value); } return 1; case OC_SOB: { EX_TREE *value; unsigned reg; unsigned offset; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); free_tree(value); if(reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if(*cp++ != ',') { report(stack->top, "Illegal syntax\n"); return 0; } value = parse_expr(cp, 0); cp = value->cp; /* Relative PSECT or absolute? */ if(current_pc->section->flags & PSECT_REL) { SYMBOL *sym; if(!express_sym_offset(value, &sym, &offset)) { report(stack->top, "Bad branch target\n"); free_tree(value); return 0; } /* Must be same section */ if(sym->section != current_pc->section) { report(stack->top, "Bad branch target\n"); free_tree(value); offset = 0; } else { /* Calculate byte offset */ offset += DOT + 2; offset -= sym->value; } } else { if(value->type != EX_LIT) { report(stack->top, "Bad branch " "target\n"); offset = 0; } else { offset = DOT + 2 - value->data.lit; } } if(!check_branch(stack, offset, 0, 126)) offset = 0; offset &= 0177; /* Reduce to 7 bits */ offset >>= 1; /* Shift to become word offset */ store_word(stack->top, tr, 2, op->value | offset | (reg << 6)); free_tree(value); } return 1; case OC_ASH: /* First op is gen, second is register. */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if(*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&mode); return 0; } value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if(reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); free_addr_mode(&mode); return 0; } /* Instruction word */ word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_JSR: /* First op is register, second is gen. */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if(reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } cp = skipwhite(cp); if(*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); return 0; } if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_1REG: /* One register (RTS) */ { EX_TREE *value; unsigned reg; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if(reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); reg = 0; } store_word(stack->top, tr, 2, op->value | reg); free_tree(value); } return 1; case OC_1FIS: /* One one gen and one reg 0-3 */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if(*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&mode); return 0; } value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if(reg == NO_REG || reg > 4) { report(stack->top, "Invalid destination register\n"); reg = 0; } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_2FIS: /* One reg 0-3 and one gen */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; int ok = 1; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if(reg == NO_REG || reg > 4) { report(stack->top, "Illegal source register\n"); reg = 0; ok = 0; } cp = skipwhite(cp); if(*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } if(!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; default: report(stack->top, "Unimplemented instruction format\n"); return 0; } /* end(handle an instruction) */ } break; } /* end switch(section type) */ } /* end if (op is a symbol) */ } /* Only thing left is an implied .WORD directive */ free(label); return do_word(stack, tr, cp, 2); } /* assemble_stack assembles the input stack. It returns the error count. */ static int assemble_stack(STACK *stack, TEXT_RLD *tr) { int res; int count = 0; while((res = assemble(stack, tr)) >= 0) { list_flush(); if(res == 0) count++; /* Count an error */ } return count; } /* write_globals writes out the GSD prior to the second assembly pass */ static void write_globals(FILE *obj) { GSD gsd; SYMBOL *sym; SECTION *psect; SYMBOL_ITER sym_iter; int isect; if(obj == NULL) return; /* Nothing to do if no OBJ file. */ gsd_init(&gsd, obj); gsd_mod(&gsd, module_name); if(ident) gsd_ident(&gsd, ident); /* write out each PSECT with it's global stuff */ /* Sections must be written out in the order that they appear in the assembly file. */ for(isect = 0; isect < sector; isect++) { psect = sections[isect]; gsd_psect(&gsd, psect->label, psect->flags, psect->size); psect->sector = isect; /* Assign it a sector */ psect->pc = 0; /* Reset it's PC for second pass */ sym = first_sym(&symbol_st, &sym_iter); while(sym) { if((sym->flags & GLOBAL) && sym->section == psect) { gsd_global(&gsd, sym->label, (sym->flags & DEFINITION ? GLOBAL_DEF : 0) | ((sym->flags & WEAK) ? GLOBAL_WEAK : 0) | ((sym->section->flags & PSECT_REL) ? GLOBAL_REL : 0) | 0100, /* Looks undefined, but add it in anyway */ sym->value); } sym = next_sym(&symbol_st, &sym_iter); } } /* Now write out the transfer address */ if(xfer_address->type == EX_LIT) { gsd_xfer(&gsd, ". ABS.", xfer_address->data.lit); } else { SYMBOL *sym; unsigned offset; if(!express_sym_offset(xfer_address, &sym, &offset)) { report(NULL, "Illegal program transfer address\n"); } else { gsd_xfer(&gsd, sym->section->label, sym->value + offset); } } gsd_flush(&gsd); gsd_end(&gsd); } /* add_symbols adds all the internal symbols. */ static void add_symbols(SECTION *current_section) { current_pc = add_sym(".", 0, 0, current_section, &symbol_st); reg_sym[0] = add_sym("R0", 0, 0, ®ister_section, &system_st); reg_sym[1] = add_sym("R1", 1, 0, ®ister_section, &system_st); reg_sym[2] = add_sym("R2", 2, 0, ®ister_section, &system_st); reg_sym[3] = add_sym("R3", 3, 0, ®ister_section, &system_st); reg_sym[4] = add_sym("R4", 4, 0, ®ister_section, &system_st); reg_sym[5] = add_sym("R5", 5, 0, ®ister_section, &system_st); reg_sym[6] = add_sym("SP", 6, 0, ®ister_section, &system_st); reg_sym[7] = add_sym("PC", 7, 0, ®ister_section, &system_st); add_sym(".ASCII", P_ASCII, 0, &pseudo_section, &system_st); add_sym(".ASCIZ", P_ASCIZ, 0, &pseudo_section, &system_st); add_sym(".ASECT", P_ASECT, 0, &pseudo_section, &system_st); add_sym(".BLKB", P_BLKB, 0, &pseudo_section, &system_st); add_sym(".BLKW", P_BLKW, 0, &pseudo_section, &system_st); add_sym(".BYTE", P_BYTE, 0, &pseudo_section, &system_st); add_sym(".CSECT", P_CSECT, 0, &pseudo_section, &system_st); add_sym(".DSABL", P_DSABL, 0, &pseudo_section, &system_st); add_sym(".ENABL", P_ENABL, 0, &pseudo_section, &system_st); add_sym(".END", P_END, 0, &pseudo_section, &system_st); add_sym(".ENDC", P_ENDC, 0, &pseudo_section, &system_st); add_sym(".ENDM", P_ENDM, 0, &pseudo_section, &system_st); add_sym(".ENDR", P_ENDR, 0, &pseudo_section, &system_st); add_sym(".EOT", P_EOT, 0, &pseudo_section, &system_st); add_sym(".ERROR", P_ERROR, 0, &pseudo_section, &system_st); add_sym(".EVEN", P_EVEN, 0, &pseudo_section, &system_st); add_sym(".FLT2", P_FLT2, 0, &pseudo_section, &system_st); add_sym(".FLT4", P_FLT4, 0, &pseudo_section, &system_st); add_sym(".GLOBL", P_GLOBL, 0, &pseudo_section, &system_st); add_sym(".IDENT", P_IDENT, 0, &pseudo_section, &system_st); add_sym(".IF", P_IF, 0, &pseudo_section, &system_st); add_sym(".IFDF", P_IFDF, 0, &pseudo_section, &system_st); add_sym(".IFNDF", P_IFDF, 0, &pseudo_section, &system_st); add_sym(".IFF", P_IFF, 0, &pseudo_section, &system_st); add_sym(".IFT", P_IFT, 0, &pseudo_section, &system_st); add_sym(".IFTF", P_IFTF, 0, &pseudo_section, &system_st); add_sym(".IIF", P_IIF, 0, &pseudo_section, &system_st); add_sym(".IRP", P_IRP, 0, &pseudo_section, &system_st); add_sym(".IRPC", P_IRPC, 0, &pseudo_section, &system_st); add_sym(".LIMIT", P_LIMIT, 0, &pseudo_section, &system_st); add_sym(".LIST", P_LIST, 0, &pseudo_section, &system_st); add_sym(".MCALL", P_MCALL, 0, &pseudo_section, &system_st); add_sym(".MEXIT", P_MEXIT, 0, &pseudo_section, &system_st); add_sym(".NARG", P_NARG, 0, &pseudo_section, &system_st); add_sym(".NCHR", P_NCHR, 0, &pseudo_section, &system_st); add_sym(".NLIST", P_NLIST, 0, &pseudo_section, &system_st); add_sym(".NTYPE", P_NTYPE, 0, &pseudo_section, &system_st); add_sym(".ODD", P_ODD, 0, &pseudo_section, &system_st); add_sym(".PACKE", P_PACKED, 0, &pseudo_section, &system_st); add_sym(".PAGE", P_PAGE, 0, &pseudo_section, &system_st); add_sym(".PRINT", P_PRINT, 0, &pseudo_section, &system_st); add_sym(".PSECT", P_PSECT, 0, &pseudo_section, &system_st); add_sym(".RADIX", P_RADIX, 0, &pseudo_section, &system_st); add_sym(".RAD50", P_RAD50, 0, &pseudo_section, &system_st); add_sym(".REM", P_REM, 0, &pseudo_section, &system_st); add_sym(".REPT", P_REPT, 0, &pseudo_section, &system_st); add_sym(".RESTO", P_RESTORE, 0, &pseudo_section, &system_st); add_sym(".SAVE", P_SAVE, 0, &pseudo_section, &system_st); add_sym(".SBTTL", P_SBTTL, 0, &pseudo_section, &system_st); add_sym(".TITLE", P_TITLE, 0, &pseudo_section, &system_st); add_sym(".WORD", P_WORD, 0, &pseudo_section, &system_st); add_sym(".MACRO", P_MACRO, 0, &pseudo_section, &system_st); add_sym(".WEAK", P_WEAK, 0, &pseudo_section, &system_st); add_sym("ADC", I_ADC, OC_1GEN, &instruction_section, &system_st); add_sym("ADCB", I_ADCB, OC_1GEN, &instruction_section, &system_st); add_sym("ADD", I_ADD, OC_2GEN, &instruction_section, &system_st); add_sym("ASH", I_ASH, OC_ASH, &instruction_section, &system_st); add_sym("ASHC", I_ASHC, OC_ASH, &instruction_section, &system_st); add_sym("ASL", I_ASL, OC_1GEN, &instruction_section, &system_st); add_sym("ASLB", I_ASLB, OC_1GEN, &instruction_section, &system_st); add_sym("ASR", I_ASR, OC_1GEN, &instruction_section, &system_st); add_sym("ASRB", I_ASRB, OC_1GEN, &instruction_section, &system_st); add_sym("BCC", I_BCC, OC_BR, &instruction_section, &system_st); add_sym("BCS", I_BCS, OC_BR, &instruction_section, &system_st); add_sym("BEQ", I_BEQ, OC_BR, &instruction_section, &system_st); add_sym("BGE", I_BGE, OC_BR, &instruction_section, &system_st); add_sym("BGT", I_BGT, OC_BR, &instruction_section, &system_st); add_sym("BHI", I_BHI, OC_BR, &instruction_section, &system_st); add_sym("BHIS", I_BHIS, OC_BR, &instruction_section, &system_st); add_sym("BIC", I_BIC, OC_2GEN, &instruction_section, &system_st); add_sym("BICB", I_BICB, OC_2GEN, &instruction_section, &system_st); add_sym("BIS", I_BIS, OC_2GEN, &instruction_section, &system_st); add_sym("BISB", I_BISB, OC_2GEN, &instruction_section, &system_st); add_sym("BIT", I_BIT, OC_2GEN, &instruction_section, &system_st); add_sym("BITB", I_BITB, OC_2GEN, &instruction_section, &system_st); add_sym("BLE", I_BLE, OC_BR, &instruction_section, &system_st); add_sym("BLO", I_BLO, OC_BR, &instruction_section, &system_st); add_sym("BLOS", I_BLOS, OC_BR, &instruction_section, &system_st); add_sym("BLT", I_BLT, OC_BR, &instruction_section, &system_st); add_sym("BMI", I_BMI, OC_BR, &instruction_section, &system_st); add_sym("BNE", I_BNE, OC_BR, &instruction_section, &system_st); add_sym("BPL", I_BPL, OC_BR, &instruction_section, &system_st); add_sym("BPT", I_BPT, OC_NONE, &instruction_section, &system_st); add_sym("BR", I_BR, OC_BR, &instruction_section, &system_st); add_sym("BVC", I_BVC, OC_BR, &instruction_section, &system_st); add_sym("BVS", I_BVS, OC_BR, &instruction_section, &system_st); add_sym("CALL", I_CALL, OC_1GEN, &instruction_section, &system_st); add_sym("CALLR", I_CALLR, OC_1GEN, &instruction_section, &system_st); add_sym("CCC", I_CCC, OC_NONE, &instruction_section, &system_st); add_sym("CLC", I_CLC, OC_NONE, &instruction_section, &system_st); add_sym("CLN", I_CLN, OC_NONE, &instruction_section, &system_st); add_sym("CLR", I_CLR, OC_1GEN, &instruction_section, &system_st); add_sym("CLRB", I_CLRB, OC_1GEN, &instruction_section, &system_st); add_sym("CLV", I_CLV, OC_NONE, &instruction_section, &system_st); add_sym("CLZ", I_CLZ, OC_NONE, &instruction_section, &system_st); add_sym("CMP", I_CMP, OC_2GEN, &instruction_section, &system_st); add_sym("CMPB", I_CMPB, OC_2GEN, &instruction_section, &system_st); add_sym("COM", I_COM, OC_1GEN, &instruction_section, &system_st); add_sym("COMB", I_COMB, OC_1GEN, &instruction_section, &system_st); add_sym("DEC", I_DEC, OC_1GEN, &instruction_section, &system_st); add_sym("DECB", I_DECB, OC_1GEN, &instruction_section, &system_st); add_sym("DIV", I_DIV, OC_ASH, &instruction_section, &system_st); add_sym("EMT", I_EMT, OC_MARK, &instruction_section, &system_st); add_sym("FADD", I_FADD, OC_1REG, &instruction_section, &system_st); add_sym("FDIV", I_FDIV, OC_1REG, &instruction_section, &system_st); add_sym("FMUL", I_FMUL, OC_1REG, &instruction_section, &system_st); add_sym("FSUB", I_FSUB, OC_1REG, &instruction_section, &system_st); add_sym("HALT", I_HALT, OC_NONE, &instruction_section, &system_st); add_sym("INC", I_INC, OC_1GEN, &instruction_section, &system_st); add_sym("INCB", I_INCB, OC_1GEN, &instruction_section, &system_st); add_sym("IOT", I_IOT, OC_NONE, &instruction_section, &system_st); add_sym("JMP", I_JMP, OC_1GEN, &instruction_section, &system_st); add_sym("JSR", I_JSR, OC_JSR, &instruction_section, &system_st); add_sym("MARK", I_MARK, OC_MARK, &instruction_section, &system_st); add_sym("MED6X", I_MED6X, OC_NONE, &instruction_section, &system_st); add_sym("MED74C", I_MED74C, OC_NONE, &instruction_section, &system_st); add_sym("MFPD", I_MFPD, OC_1GEN, &instruction_section, &system_st); add_sym("MFPI", I_MFPI, OC_1GEN, &instruction_section, &system_st); add_sym("MFPS", I_MFPS, OC_1GEN, &instruction_section, &system_st); add_sym("MOV", I_MOV, OC_2GEN, &instruction_section, &system_st); add_sym("MOVB", I_MOVB, OC_2GEN, &instruction_section, &system_st); add_sym("MTPD", I_MTPD, OC_1GEN, &instruction_section, &system_st); add_sym("MTPI", I_MTPI, OC_1GEN, &instruction_section, &system_st); add_sym("MTPS", I_MTPS, OC_1GEN, &instruction_section, &system_st); add_sym("MUL", I_MUL, OC_ASH, &instruction_section, &system_st); add_sym("NEG", I_NEG, OC_1GEN, &instruction_section, &system_st); add_sym("NEGB", I_NEGB, OC_1GEN, &instruction_section, &system_st); add_sym("NOP", I_NOP, OC_NONE, &instruction_section, &system_st); add_sym("RESET", I_RESET, OC_NONE, &instruction_section, &system_st); add_sym("RETURN", I_RETURN, OC_NONE, &instruction_section, &system_st); add_sym("ROL", I_ROL, OC_1GEN, &instruction_section, &system_st); add_sym("ROLB", I_ROLB, OC_1GEN, &instruction_section, &system_st); add_sym("ROR", I_ROR, OC_1GEN, &instruction_section, &system_st); add_sym("RORB", I_RORB, OC_1GEN, &instruction_section, &system_st); add_sym("RTI", I_RTI, OC_NONE, &instruction_section, &system_st); add_sym("RTS", I_RTS, OC_1REG, &instruction_section, &system_st); add_sym("RTT", I_RTT, OC_NONE, &instruction_section, &system_st); add_sym("SBC", I_SBC, OC_1GEN, &instruction_section, &system_st); add_sym("SBCB", I_SBCB, OC_1GEN, &instruction_section, &system_st); add_sym("SCC", I_SCC, OC_NONE, &instruction_section, &system_st); add_sym("SEC", I_SEC, OC_NONE, &instruction_section, &system_st); add_sym("SEN", I_SEN, OC_NONE, &instruction_section, &system_st); add_sym("SEV", I_SEV, OC_NONE, &instruction_section, &system_st); add_sym("SEZ", I_SEZ, OC_NONE, &instruction_section, &system_st); add_sym("SOB", I_SOB, OC_SOB, &instruction_section, &system_st); add_sym("SPL", I_SPL, OC_1REG, &instruction_section, &system_st); add_sym("SUB", I_SUB, OC_2GEN, &instruction_section, &system_st); add_sym("SWAB", I_SWAB, OC_1GEN, &instruction_section, &system_st); add_sym("SXT", I_SXT, OC_1GEN, &instruction_section, &system_st); add_sym("TRAP", I_TRAP, OC_MARK, &instruction_section, &system_st); add_sym("TST", I_TST, OC_1GEN, &instruction_section, &system_st); add_sym("TSTB", I_TSTB, OC_1GEN, &instruction_section, &system_st); add_sym("WAIT", I_WAIT, OC_NONE, &instruction_section, &system_st); add_sym("XFC", I_XFC, OC_NONE, &instruction_section, &system_st); add_sym("XOR", I_XOR, OC_JSR, &instruction_section, &system_st); add_sym("MFPT", I_MFPT, OC_NONE, &instruction_section, &system_st); add_sym("ABSD", I_ABSD, OC_1GEN, &instruction_section, &system_st); add_sym("ABSF", I_ABSF, OC_1GEN, &instruction_section, &system_st); add_sym("ADDD", I_ADDD, OC_1FIS, &instruction_section, &system_st); add_sym("ADDF", I_ADDF, OC_1FIS, &instruction_section, &system_st); add_sym("CFCC", I_CFCC, OC_NONE, &instruction_section, &system_st); add_sym("CLRD", I_CLRD, OC_1GEN, &instruction_section, &system_st); add_sym("CLRF", I_CLRF, OC_1GEN, &instruction_section, &system_st); add_sym("CMPD", I_CMPD, OC_1FIS, &instruction_section, &system_st); add_sym("CMPF", I_CMPF, OC_1FIS, &instruction_section, &system_st); add_sym("DIVD", I_DIVD, OC_1FIS, &instruction_section, &system_st); add_sym("DIVF", I_DIVF, OC_1FIS, &instruction_section, &system_st); add_sym("LDCDF", I_LDCDF, OC_1FIS, &instruction_section, &system_st); add_sym("LDCID", I_LDCID, OC_1FIS, &instruction_section, &system_st); add_sym("LDCIF", I_LDCIF, OC_1FIS, &instruction_section, &system_st); add_sym("LDCLD", I_LDCLD, OC_1FIS, &instruction_section, &system_st); add_sym("LDCLF", I_LDCLF, OC_1FIS, &instruction_section, &system_st); add_sym("LDD", I_LDD, OC_1FIS, &instruction_section, &system_st); add_sym("LDEXP", I_LDEXP, OC_1FIS, &instruction_section, &system_st); add_sym("LDF", I_LDF, OC_1FIS, &instruction_section, &system_st); add_sym("LDFPS", I_LDFPS, OC_1GEN, &instruction_section, &system_st); add_sym("MODD", I_MODD, OC_1FIS, &instruction_section, &system_st); add_sym("MODF", I_MODF, OC_1FIS, &instruction_section, &system_st); add_sym("MULD", I_MULD, OC_1FIS, &instruction_section, &system_st); add_sym("MULF", I_MULF, OC_1FIS, &instruction_section, &system_st); add_sym("NEGD", I_NEGD, OC_1GEN, &instruction_section, &system_st); add_sym("NEGF", I_NEGF, OC_1GEN, &instruction_section, &system_st); add_sym("SETD", I_SETD, OC_NONE, &instruction_section, &system_st); add_sym("SETF", I_SETF, OC_NONE, &instruction_section, &system_st); add_sym("SETI", I_SETI, OC_NONE, &instruction_section, &system_st); add_sym("SETL", I_SETL, OC_NONE, &instruction_section, &system_st); add_sym("STA0", I_STA0, OC_NONE, &instruction_section, &system_st); add_sym("STB0", I_STB0, OC_NONE, &instruction_section, &system_st); add_sym("STCDF", I_STCDF, OC_2FIS, &instruction_section, &system_st); add_sym("STCDI", I_STCDI, OC_2FIS, &instruction_section, &system_st); add_sym("STCDL", I_STCDL, OC_2FIS, &instruction_section, &system_st); add_sym("STCFD", I_STCFD, OC_2FIS, &instruction_section, &system_st); add_sym("STCFI", I_STCFI, OC_2FIS, &instruction_section, &system_st); add_sym("STCFL", I_STCFL, OC_2FIS, &instruction_section, &system_st); add_sym("STD", I_STD, OC_2FIS, &instruction_section, &system_st); add_sym("STEXP", I_STEXP, OC_2FIS, &instruction_section, &system_st); add_sym("STF", I_STF, OC_2FIS, &instruction_section, &system_st); add_sym("STFPS", I_STFPS, OC_1GEN, &instruction_section, &system_st); add_sym("STST", I_STST, OC_1GEN, &instruction_section, &system_st); add_sym("SUBD", I_SUBD, OC_1FIS, &instruction_section, &system_st); add_sym("SUBF", I_SUBF, OC_1FIS, &instruction_section, &system_st); add_sym("TSTD", I_TSTD, OC_1GEN, &instruction_section, &system_st); add_sym("TSTF", I_TSTF, OC_1GEN, &instruction_section, &system_st); /* FIXME: The CIS instructions are missing! */ add_sym(current_section->label, 0, 0, current_section, §ion_st); } /* dump_all_macros is a diagnostic function that's currently not used. I used it while debugging, and I haven't removed it. */ static void dump_all_macros(void) { MACRO *mac; SYMBOL_ITER iter; for(mac = (MACRO *)first_sym(¯o_st, &iter); mac != NULL; mac = (MACRO *)next_sym(¯o_st, &iter)) { dumpmacro(mac, lstfile); printf("\n\n"); } } /* sym_hist is a diagnostic function that prints a histogram of the hash table useage of a symbol table. I used this to try to tune the hash function for better spread. It's not used now. */ static void sym_hist(SYMBOL_TABLE *st, char *name) { int i; SYMBOL *sym; fprintf(lstfile, "Histogram for symbol table %s\n", name); for(i = 0; i < 1023; i++) { fprintf(lstfile, "%4d: ", i); for(sym = st->hash[i]; sym != NULL; sym = sym->next) fputc('#', lstfile); fputc('\n', lstfile); } } /* enable_tf is called by command argument parsing to enable and disable named options. */ static void enable_tf(char *opt, int tf) { if(strcmp(opt, "AMA") == 0) enabl_ama = tf; else if(strcmp(opt, "GBL") == 0) enabl_gbl = tf; else if(strcmp(opt, "ME") == 0) list_me = tf; else if(strcmp(opt, "BEX") == 0) list_bex = tf; else if(strcmp(opt, "MD") == 0) list_md = tf; } int main(int argc, char *argv[]) { char *fnames[32]; int nr_files = 0; FILE *obj = NULL; static char line[1024]; TEXT_RLD tr; char *macname = NULL; char *objname = NULL; char *lstname = NULL; int arg; int i; STACK stack; int count; for(arg = 1; arg < argc; arg++) { if(*argv[arg] == '-') { char *cp; cp = argv[arg] + 1; switch(tolower(*cp)) { case 'v': fprintf(stderr, "macro11 Copyright 2001 Richard Krehbiel\n" "Version 0.2 July 15, 2001\n"); break; case 'e': /* Followed by options to enable */ /* Since /SHOW and /ENABL option names don't overlap, I consolidate. */ upcase(argv[++arg]); enable_tf(argv[arg], 1); break; case 'd': /* Followed by an option to disable */ upcase(argv[++arg]); enable_tf(argv[arg], 0); break; case 'm': /* Macro library */ /* This option gives the name of an RT-11 compatible macro library from which .MCALLed macros can be found. */ arg++; mlbs[nr_mlbs] = mlb_open(argv[arg]); if(mlbs[nr_mlbs] == NULL) { fprintf(stderr, "Unable to register macro library %s\n", argv[arg]); exit(EXIT_FAILURE); } nr_mlbs++; break; case 'p': /* P for search path */ /* The -p option gives the name of a directory in which .MCALLed macros may be found. */ { char *env = getenv("MCALL"); char *temp; if(env == NULL) env = ""; temp = memcheck(malloc(strlen(env) + strlen(argv[arg+1]) + 8)); strcpy(temp, "MCALL="); strcat(temp, env); strcat(temp, PATHSEP); strcat(temp, argv[arg+1]); putenv(temp); arg++; } break; case 'o': /* The -o option gives the object file name (.OBJ) */ ++arg; objname = argv[arg]; break; case 'l': /* The option -l gives the listing file name (.LST) */ /* -l - enables listing to stdout. */ lstname = argv[++arg]; if(strcmp(lstname, "-") == 0) lstfile = stdout; else lstfile = fopen(lstname, "w"); break; case 'x': /* The -x option invokes macro11 to expand the contents of the registered macro libraries (see -m) into individual .MAC files in the current directory. No assembly of input is done. This must be the last command line option. */ { int i; for(i = 0; i < nr_mlbs; i++) mlb_extract(mlbs[i]); return EXIT_SUCCESS; } default: fprintf(stderr, "Unknown argument %s\n", argv[arg]); exit(EXIT_FAILURE); } } else { fnames[nr_files++] = argv[arg]; } } if(objname) { obj = fopen(objname, "wb"); if(obj == NULL) return EXIT_FAILURE; } add_symbols(&blank_section); text_init(&tr, NULL, 0); module_name = memcheck(strdup("")); xfer_address = new_ex_lit(1); /* The undefined transfer address */ stack_init(&stack); /* Push the files onto the input stream in reverse order */ for(i = nr_files-1; i >= 0; --i) { STREAM *str = new_file_stream(fnames[i]); if(str == NULL) { report(NULL, "Unable to open file %s\n", fnames[i]); exit(EXIT_FAILURE); } stack_push(&stack, str); } DOT = 0; current_pc->section = &blank_section; last_dot_section = NULL; pass = 0; stmtno = 0; lsb = 0; last_lsb = -1; last_locsym = 32767; last_cond = -1; sect_sp = -1; suppressed = 0; assemble_stack(&stack, &tr); #if 0 if(enabl_debug) dump_all_macros(); #endif assert(stack.top == NULL); migrate_implicit(); /* Migrate the implicit globals */ write_globals(obj); /* Write the global symbol dictionary */ #if 0 sym_hist(&symbol_st, "symbol_st"); /* Draw a symbol table histogram */ #endif text_init(&tr, obj, 0); stack_init(&stack); /* Superfluous... */ /* Re-push the files onto the input stream in reverse order */ for(i = nr_files-1; i >= 0; --i) { STREAM *str = new_file_stream(fnames[i]); if(str == NULL) { report(NULL, "Unable to open file %s\n", fnames[i]); exit(EXIT_FAILURE); } stack_push(&stack, str); } DOT = 0; current_pc->section = &blank_section; last_dot_section = NULL; pass = 1; stmtno = 0; lsb = 0; last_lsb = -1; last_locsym = 32767; pop_cond(-1); sect_sp = -1; suppressed = 0; count = assemble_stack(&stack, &tr); text_flush(&tr); while(last_cond >= 0) { report(NULL, "%s:%d: Unterminated conditional\n", conds[last_cond].file, conds[last_cond].line); pop_cond(last_cond - 1); count++; } for(i = 0; i < nr_mlbs; i++) mlb_close(mlbs[i]); write_endmod(obj); if(obj != NULL) fclose(obj); if(count > 0) fprintf(stderr, "%d Errors\n", count); if(lstfile && strcmp(lstname, "-") != 0) fclose(lstfile); return count > 0 ? EXIT_FAILURE : EXIT_SUCCESS; } simh-3.8.1/TOOLS/crossassemblers/macro11/license0000666000175000017500000000272707324436602017605 0ustar vlmvlmCopyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. simh-3.8.1/TOOLS/crossassemblers/macro11/rad50.h0000666000175000017500000000324007324436610017312 0ustar vlmvlm#ifndef RAD50_H #define RAD50_H /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ extern unsigned rad50(char *cp, char **endp); extern void rad50x2(char *cp, unsigned *rp); extern void unrad50(unsigned word, char *cp); #endif /* RAD50_H */ simh-3.8.1/TOOLS/crossassemblers/macro11/object.h0000666000175000017500000002027207324436676017665 0ustar vlmvlm#ifndef OBJECT_H #define OBJECT_H /* Object file constant definitions */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define FBR_LEAD1 1 /* The byte value that defines the beginning of a formatted binary record */ #define FBR_LEAD2 0 /* Followed by a 0 */ /* Followed by two bytes length */ /* Followed by (length-4) bytes data */ /* Followed by a 1 byte checksum */ /* which is the negative sum of all preceeding bytes */ #define OBJ_GSD 01 /* GSD (Global symbol directory) */ #define OBJ_ENDGSD 02 /* ENDGSD */ #define OBJ_TEXT 03 /* TEXT */ #define OBJ_RLD 04 /* RLD (Relocation directory) */ #define OBJ_ISD 05 /* ISD (Internal symbol directory, currently unused) */ #define OBJ_ENDMOD 06 /* ENDMOD (End of object module) */ #define OBJ_LIBHDR 07 /* LIBHDR (Object Library header) */ #define OBJ_LIBEND 010 /* LIBEND (Object Library header end) */ #define GSD_MODNAME 00 /* Module name */ #define GSD_CSECT 01 /* Control section name */ #define GSD_ISN 02 /* Internal symbol name */ #define GSD_XFER 03 /* Transfer address */ #define GSD_GLOBAL 04 /* Global symbol definition/reference */ #define GSD_PSECT 05 /* PSECT name */ #define GSD_IDENT 06 /* IDENT */ #define GSD_VSECT 07 /* VSECT (Virtual array declaration) */ #define GLOBAL_WEAK 01 /* GLOBAL is weak, else strong */ #define GLOBAL_DEF 010 /* GLOBAL is definition, else reference */ #define GLOBAL_REL 040 /* GLOBAL is relative, else absolute */ #define PSECT_SAV 001 /* PSECT is a root section, else overlay */ #define PSECT_COM 004 /* PSECT is merged common area, else contatenated */ #define PSECT_RO 020 /* PSECT is read-only, else R/W */ #define PSECT_REL 040 /* PSECT is relative, else absolute (absolute implies PSECT_COM) */ #define PSECT_GBL 0100 /* PSECT is overlay-global, else overlay-local */ #define PSECT_DATA 0200 /* PSECT contains data, else instructions */ #define RLD_INT 01 /* "Internal relocation" */ #define RLD_GLOBAL 02 /* "Global relocation" */ #define RLD_INT_DISP 03 /* "Internal displaced" */ #define RLD_GLOBAL_DISP 04 /* "Global displaced" */ #define RLD_GLOBAL_OFFSET 05 /* "Global additive" */ #define RLD_GLOBAL_OFFSET_DISP 06 /* "Global additive displaced" */ #define RLD_LOCDEF 07 /* "Location counter definition" */ #define RLD_LOCMOD 010 /* "Location counter modification" */ #define RLD_LIMITS 011 /* ".LIMIT" */ #define RLD_PSECT 012 /* "P-sect" */ #define RLD_PSECT_DISP 014 /* "P-sect displaced" */ #define RLD_PSECT_OFFSET 015 /* "P-sect additive" */ #define RLD_PSECT_OFFSET_DISP 016 /* "P-sect additive displaced" */ #define RLD_COMPLEX 017 /* "Complex" */ #define RLD_BYTE 0200 /* RLD modifies a byte, else a word */ /* Note: complex relocation is not well documented (in particular, no effort is made to define a section's "sector number"), but I'll just guess it's a stack language. */ #define CPLX_NOP 00 /* NOP - used for padding */ #define CPLX_ADD 01 #define CPLX_SUB 02 #define CPLX_MUL 03 #define CPLX_DIV 04 #define CPLX_AND 05 #define CPLX_OR 06 #define CPLX_XOR 07 #define CPLX_NEG 010 #define CPLX_COM 011 #define CPLX_STORE 012 /* Store result, terminate complex string. */ #define CPLX_STORE_DISP 013 /* Store result PC-relative, terminate */ #define CPLX_GLOBAL 016 /* Followed by four bytes RAD50 global name */ #define CPLX_REL 017 /* Followed by one byte "sector number" and two bytes offset */ #define CPLX_CONST 020 /* Followed by two bytes constant value */ typedef struct gsd { FILE *fp; /* The file assigned for output */ char buf[122]; /* space for 15 GSD entries */ int offset; /* Current buffer for GSD entries */ } GSD; void gsd_init(GSD *gsd, FILE *fp); int gsd_flush(GSD *gsd); int gsd_mod(GSD *gsd, char *modname); int gsd_csect(GSD *gsd, char *sectname, int size); int gsd_intname(GSD *gsd, char *name, unsigned value); int gsd_xfer(GSD *gsd, char *name, unsigned value); int gsd_global(GSD *gsd, char *name, int flags, unsigned value); int gsd_psect(GSD *gsd, char *name, int flags, int size); int gsd_ident(GSD *gsd, char *name); int gsd_virt(GSD *gsd, char *name, int size); int gsd_end(GSD *gsd); typedef struct text_rld { FILE *fp; /* The object file, or NULL */ char text[128]; /* text buffer */ unsigned txt_addr; /* The base text address */ int txt_offset; /* Current text offset */ char rld[128]; /* RLD buffer */ int rld_offset; /* Current RLD offset */ } TEXT_RLD; void text_init(TEXT_RLD *tr, FILE *fp, unsigned addr); int text_flush(TEXT_RLD *tr); int text_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word); int text_internal_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word); int text_global_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global); int text_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word); int text_global_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global); int text_global_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global); int text_global_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global); int text_define_location(TEXT_RLD *tr, char *name, unsigned *addr); int text_modify_location(TEXT_RLD *tr, unsigned *addr); int text_limits(TEXT_RLD *tr, unsigned *addr); int text_psect_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name); int text_psect_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name); int text_psect_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name); int text_psect_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name); typedef struct text_complex { char accum[126]; int len; } TEXT_COMPLEX; void text_complex_begin(TEXT_COMPLEX *tx); int text_complex_add(TEXT_COMPLEX *tx); int text_complex_sub(TEXT_COMPLEX *tx); int text_complex_mul(TEXT_COMPLEX *tx); int text_complex_div(TEXT_COMPLEX *tx); int text_complex_and(TEXT_COMPLEX *tx); int text_complex_or(TEXT_COMPLEX *tx); int text_complex_xor(TEXT_COMPLEX *tx); int text_complex_com(TEXT_COMPLEX *tx); int text_complex_neg(TEXT_COMPLEX *tx); int text_complex_lit(TEXT_COMPLEX *tx, unsigned word); int text_complex_global(TEXT_COMPLEX *tx, char *name); int text_complex_psect(TEXT_COMPLEX *tx, unsigned sect, unsigned offset); int text_complex_commit(TEXT_RLD *tr, unsigned *addr, int size, TEXT_COMPLEX *tx, unsigned word); int text_complex_commit_displaced(TEXT_RLD *tr, unsigned *addr, int size, TEXT_COMPLEX *tx, unsigned word); int write_endmod(FILE *fp); #endif /* OBJECT_J */ simh-3.8.1/TOOLS/crossassemblers/macro11/stream2.h0000666000175000017500000000674307324436700017767 0ustar vlmvlm#ifndef STREAM2_H #define STREAM2_H /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ struct stream; typedef struct stream_vtbl { void (*delete)(struct stream *stream); // Destructor char *(*gets)(struct stream *stream); // "gets" function void (*rewind)(struct stream *stream); // "rewind" function } STREAM_VTBL; typedef struct stream { STREAM_VTBL *vtbl; // Pointer to dispatch table char *name; // Stream name int line; // Current line number in stream struct stream *next; // Next stream in stack } STREAM; typedef struct file_stream { STREAM stream; // Base class FILE *fp; // File pointer char *buffer; // Line buffer } FILE_STREAM; typedef struct buffer { char *buffer; // Pointer to text int size; // Size of buffer int length; // Occupied size of buffer int use; // Number of users of buffer } BUFFER; #define GROWBUF_INCR 1024 // Buffers grow by leaps and bounds typedef struct buffer_stream { STREAM stream; // Base class BUFFER *buffer; // text buffer int offset; // Current read offset } BUFFER_STREAM; typedef struct stack { STREAM *top; // Top of stacked stream pieces } STACK; #define STREAM_BUFFER_SIZE 1024 // This limits the max size of an input line. BUFFER *new_buffer(void); BUFFER *buffer_clone(BUFFER *from); void buffer_resize(BUFFER *buff, int size); void buffer_free(BUFFER *buf); void buffer_appendn(BUFFER *buf, char *str, int len); void buffer_append_line(BUFFER *buf, char *str); STREAM *new_buffer_stream(BUFFER *buf, char *name); void buffer_stream_set_buffer(BUFFER_STREAM *bstr, BUFFER *buf); /* Provide these so that macro11 can derive from a BUFFER_STREAM */ extern STREAM_VTBL buffer_stream_vtbl; void buffer_stream_construct(BUFFER_STREAM *bstr, BUFFER *buf, char *name); char *buffer_stream_gets(STREAM *str); void buffer_stream_delete(STREAM *str); void buffer_stream_rewind(STREAM *str); STREAM *new_file_stream(char *filename); void stack_init(STACK *stack); void stack_push(STACK *stack, STREAM *str); void stack_pop(STACK *stack); char *stack_gets(STACK *stack); #endif /* STREAM2_H */ simh-3.8.1/TOOLS/crossassemblers/macro11/todo0000666000175000017500000000533707324436612017131 0ustar vlmvlmI was not able to locate a Macro-11 language reference manual any more recent than for RT11 version *3*, so I used that plus my recollection of more modern features. It was enough to get the RT11 V5.4 kernel built, plus a significant chunk of our own code. The biggest missing feature is full featured listings. The .LIST and .NLIST directives are ignored, as is .SBTTL. No table of contents is accumulated or printed. No symbol cross referencing is done (most likely I'll just write a CTAGS file, not a cross reference listing). Many errors still go unchecked. Off the top of my head, I recall that object and listing file output errors are ignored. .FLT4 format may be inaccurate in the low bits. This is because IEEE 64 bit format has two fewer mantissa bits than 64 bit PDP-11 format. Without writing soft-float routines, there's not much I can do abbout it. Expression math is done in native width, almost certainly 32 bits. Truncation to 16 bits is done only for listing and output. This may make some output differ in the presence of 16-bit overflows and underflows. I don't think this needs fixing. .REM blocks containing code can screw up .MACRO, .REPT, .IRP, .IRPC. read_body in macro11.c would need to be able to parse and ignore .REM blocks. Need to search a path for the .INCLUDE directive. Right now it only takes a complete file name. And most likely, existing code will have RT-11 style file names; I don't know what to do about that, except put in a device name parser. Possible enhancements: It would be very simple to make macro11 resolve internal symbols with more that 6 significant characters. Even so, only the first 6 would be used for external symbols, and you have to be wary of existing code that used (for example) .LOOKU rather than .LOOKUP, since these two would become distinct. SYM = 0 MOV SYM(R0),R0 ; macro11 could optimize SYM(R0) into just (R0) I dream of automatically fixing branches out of range. Easy when the destination is backwards, difficult when it's forwards. I have this idea: during the first assembly pass, all branches generate a long branch if the target symbol is undefined, otherwise an "optimized" branch (short or long) if the target is defined. Then keep a 128-instruction FIFO of generated instructions. Each FIFO entry is tagged with context and symbol definition as they are pushed to the FIFO. When an instruction gets pulled from the FIFO because it's more than 128 words away, the FIFO is searched for long branches that point to this location; any such are shortened, and any symbols defined following their location in the stream are adjusted. In the second assembly pass, the FIFOs aren't used because all jump distances are known, and the right sized branch (JMP or Bcc) can be generated. simh-3.8.1/TOOLS/crossassemblers/macro11/stream2.c0000666000175000017500000001777407324436702017772 0ustar vlmvlm/* functions for managing a stack of file and buffer input streams. */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "macro11.h" #include "stream2.h" /* BUFFER functions */ /* new_buffer allocates a new buffer */ BUFFER *new_buffer(void) { BUFFER *buf = memcheck(malloc(sizeof(BUFFER))); buf->length = 0; buf->size = 0; buf->use = 1; buf->buffer = NULL; return buf; } /* buffer_resize makes the buffer at least the requested size. */ /* If the buffer is already larger, then it will attempt */ /* to shrink it. */ void buffer_resize(BUFFER *buff, int size) { buff->size = size; buff->length = size; if(size == 0) { free(buff->buffer); buff->buffer = NULL; } else { if(buff->buffer == NULL) buff->buffer = memcheck(malloc(buff->size)); else buff->buffer = memcheck(realloc(buff->buffer, buff->size)); } } /* buffer_clone makes a copy of a buffer */ /* Basically it increases the use count */ BUFFER *buffer_clone(BUFFER *from) { if(from) from->use++; return from; } /* buffer_free frees a buffer */ /* It decreases the use count, and if zero, */ /* frees the memory. */ void buffer_free(BUFFER *buf) { if(buf) { if(--(buf->use) == 0) { free(buf->buffer); free(buf); } } } /* Append characters to the buffer. */ void buffer_appendn(BUFFER *buf, char *str, int len) { int needed = buf->length + len + 1; if(needed >= buf->size) { buf->size = needed + GROWBUF_INCR; if(buf->buffer == NULL) buf->buffer = memcheck(malloc(buf->size)); else buf->buffer = memcheck(realloc(buf->buffer, buf->size)); } memcpy(buf->buffer + buf->length, str, len); buf->length += len; buf->buffer[buf->length] = 0; } /* append a text line (zero or newline-delimited) */ void buffer_append_line(BUFFER *buf, char *str) { char *nl; if((nl = strchr(str, '\n')) != NULL) buffer_appendn(buf, str, nl - str + 1); else buffer_appendn(buf, str, strlen(str)); } /* Base STREAM class methods */ /* stream_construct initializes a newly allocated STREAM */ void stream_construct(STREAM *str, char *name) { str->line = 0; str->name = memcheck(strdup(name)); str->next = NULL; str->vtbl = NULL; } /* stream_delete destroys and deletes (frees) a STREAM */ void stream_delete(STREAM *str) { free(str->name); free(str); } /* *** class BUFFER_STREAM implementation */ /* STREAM::gets for a buffer stream */ char *buffer_stream_gets(STREAM *str) { char *nl; char *cp; BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; BUFFER *buf = bstr->buffer; if(buf == NULL) return NULL; /* No buffer */ if(bstr->offset >= buf->length) return NULL; cp = buf->buffer + bstr->offset; /* Find the next line in preparation for the next call */ nl = memchr(cp, '\n', buf->length - bstr->offset); if(nl) nl++; bstr->offset = nl - buf->buffer; str->line++; return cp; } /* STREAM::close for a buffer stream */ void buffer_stream_delete(STREAM *str) { BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; buffer_free(bstr->buffer); stream_delete(str); } /* STREAM::rewind for a buffer stream */ void buffer_stream_rewind(STREAM *str) { BUFFER_STREAM *bstr = (BUFFER_STREAM *)str; bstr->offset = 0; str->line = 0; } /* BUFFER_STREAM vtbl */ STREAM_VTBL buffer_stream_vtbl = { buffer_stream_delete, buffer_stream_gets, buffer_stream_rewind }; void buffer_stream_construct(BUFFER_STREAM *bstr, BUFFER *buf, char *name) { bstr->stream.vtbl = &buffer_stream_vtbl; bstr->stream.name = memcheck(strdup(name)); bstr->buffer = buffer_clone(buf); bstr->offset = 0; bstr->stream.line = 0; } void buffer_stream_set_buffer(BUFFER_STREAM *bstr, BUFFER *buf) { if(bstr->buffer) buffer_free(bstr->buffer); bstr->buffer = buffer_clone(buf); bstr->offset = 0; } /* new_buffer_stream clones the given buffer, gives it the name, */ /* and creates a BUFFER_STREAM to reference it */ STREAM *new_buffer_stream(BUFFER *buf, char *name) { BUFFER_STREAM *bstr = memcheck(malloc(sizeof(BUFFER_STREAM))); buffer_stream_construct(bstr, buf, name); return &bstr->stream; } /* *** FILE_STREAM implementation */ /* Implement STREAM::gets for a file stream */ static char *file_gets(STREAM *str) { int i, c; FILE_STREAM *fstr = (FILE_STREAM *)str; if(fstr->fp == NULL) return NULL; if(feof(fstr->fp)) return NULL; /* Read single characters, end of line when '\n' or '\f' hit */ i = 0; while(c = fgetc(fstr->fp), c != '\n' && c != '\f' && c != EOF) { if(c == 0) continue; /* Don't buffer zeros */ if(c == '\r') continue; /* Don't buffer carriage returns either */ if(i < STREAM_BUFFER_SIZE - 2) fstr->buffer[i++] = c; } fstr->buffer[i++] = '\n'; /* Silently transform formfeeds into newlines */ fstr->buffer[i] = 0; if(c == '\n') fstr->stream.line++; /* Count a line */ return fstr->buffer; } /* Implement STREAM::destroy for a file stream */ void file_destroy(STREAM *str) { FILE_STREAM *fstr = (FILE_STREAM *)str; fclose(fstr->fp); free(fstr->buffer); stream_delete(str); } /* Implement STREAM::rewind for a file stream */ void file_rewind(STREAM *str) { FILE_STREAM *fstr = (FILE_STREAM *)str; rewind(fstr->fp); str->line = 0; } static STREAM_VTBL file_stream_vtbl = { file_destroy, file_gets, file_rewind }; /* Prepare and open a stream from a file. */ STREAM *new_file_stream(char *filename) { FILE *fp; FILE_STREAM *str; fp = fopen(filename, "r"); if(fp == NULL) return NULL; str = memcheck(malloc(sizeof(FILE_STREAM))); str->stream.vtbl = &file_stream_vtbl; str->stream.name = memcheck(strdup(filename)); str->buffer = memcheck(malloc(STREAM_BUFFER_SIZE)); str->fp = fp; str->stream.line = 0; return &str->stream; } /* STACK functions */ /* stack_init prepares a stack */ void stack_init(STACK *stack) { stack->top = NULL; /* Too simple */ } /* stack_pop removes and deletes the topmost STRAM on the stack */ void stack_pop(STACK *stack) { STREAM *top = stack->top; STREAM *next = top->next; top->vtbl->delete(top); stack->top = next; } /* stack_push pushes a STREAM onto the top of the stack */ void stack_push(STACK *stack, STREAM *str) { str->next = stack->top; stack->top = str; } /* stack_gets calls vtbl->gets for the topmost stack entry. When topmost streams indicate they're exhausted, they are popped and deleted, until the stack is exhausted. */ char *stack_gets(STACK *stack) { char *line; if(stack->top == NULL) return NULL; while((line = stack->top->vtbl->gets(stack->top)) == NULL) { stack_pop(stack); if(stack->top == NULL) return NULL; } return line; } simh-3.8.1/TOOLS/crossassemblers/macro11/util.h0000666000175000017500000000367307324436706017374 0ustar vlmvlm#ifndef UTIL_H #define UTIL_H /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ char *my_ultoa(unsigned long val, char *buf, unsigned int base); char *my_ltoa(long val, char *buf, unsigned int base); void my_searchenv(char *name, char *envname, char *hitfile, int hitlen); /* Cover a few platform-dependencies */ #ifdef WIN32 typedef unsigned __int64 ulong64; #define strdup _strdup #define putenv _putenv #define PATHSEP ";" #else typedef unsigned long long ulong64; #define PATHSEP ":" #endif #endif /* UTIL_H */ simh-3.8.1/TOOLS/crossassemblers/macro11/makefile0000666000175000017500000000156607324436600017736 0ustar vlmvlmCFLAGS = -O -g MACRO11_SRCS = macro11.c mlb.c object.c stream2.c util.c rad50.c MACRO11_OBJS = $(MACRO11_SRCS:.c=.o) DUMPOBJ_SRCS = dumpobj.c rad50.c DUMPOBJ_OBJS = $(DUMPOBJ_SRCS:.c=.o) ALL_SRCS = $(MACRO11_SRCS) $(DUMPOBJ_SRCS) all: macro11 dumpobj tags: macro11 dumpobj ctags *.c *.h macro11: $(MACRO11_OBJS) makefile $(CC) $(CFLAGS) -o macro11 $(MACRO11_OBJS) -lm dumpobj: $(DUMPOBJ_OBJS) makefile $(CC) $(CFLAGS) -o dumpobj $(DUMPOBJ_OBJS) MACRO11_OBJS: makefile DUMPOBJ_OBJS: makefile clean: -rm -f $(MACRO11_OBJS) $(DUMPOBJ_OBJS) macro11 dumpobj macro11.o: macro11.c macro11.h rad50.h object.h stream2.h \ mlb.h util.h mlb.o: mlb.c rad50.h stream2.h mlb.h macro11.h util.h object.o: object.c rad50.h object.h stream2.o: stream2.c macro11.h stream2.h util.o: util.c util.h rad50.o: rad50.c rad50.h dumpobj.o: dumpobj.c rad50.h util.h rad50.o: rad50.c rad50.h simh-3.8.1/TOOLS/crossassemblers/macro11/mlb.c0000666000175000017500000001723707324436660017164 0ustar vlmvlm/* Routines for reading from an RT-11 macro library (like SYSMAC.SML) */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "rad50.h" #include "stream2.h" #include "mlb.h" #include "macro11.h" #include "util.h" #define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) /* BYTEPOS calculates the byte position within the macro libray file. I use this to sort the entries by their start position, in order to be able to calculate the entries' sizes, which isn't actually stored in the directory. */ #define BYTEPOS(rec) ((WORD((rec)+4) & 32767) * 512 + (WORD((rec)+6) & 511)) extern FILE *lstfile; /* compare_position is the qsort callback function that compares byte locations within the macro library */ static int compare_position(const void *arg1, const void *arg2) { const char *c1 = arg1, *c2 = arg2; if(BYTEPOS(c1) < BYTEPOS(c2)) return -1; if(BYTEPOS(c1) > BYTEPOS(c2)) return 1; return 0; } /* trim removes trailing blanks from a string. */ static void trim(char *buf) { char *cp = buf + strlen(buf); while(--cp >= buf && *cp == ' ') *cp = 0; } /* mlb_open opens a file which is given to be a macro library. */ /* Returns NULL on failure. */ MLB *mlb_open(char *name) { MLB *mlb = memcheck(malloc(sizeof(MLB))); char *buff; unsigned entsize; unsigned nr_entries; unsigned start_block; int i; mlb->directory = NULL; mlb->fp = fopen(name, "rb"); if(mlb->fp == NULL) { mlb_close(mlb); return NULL; } buff = memcheck(malloc(044)); /* Size of MLB library header */ if(fread(buff, 1, 044, mlb->fp) < 044) { mlb_close(mlb); free(buff); return NULL; } if(WORD(buff) != 01001) /* Is this really a macro library? */ { mlb_close(mlb); /* Nope. */ return NULL; } entsize = WORD(buff + 032); /* The size of each macro directory entry */ nr_entries = WORD(buff + 036); /* The number of directory entries */ start_block = WORD(buff + 034); /* The start RT-11 block of the directory */ free(buff); /* Done with that header. */ /* Allocate a buffer for the disk directory */ buff = memcheck(malloc(nr_entries * entsize)); fseek(mlb->fp, start_block * 512, SEEK_SET); /* Go to the directory */ /* Read the disk directory */ if(fread(buff, entsize, nr_entries, mlb->fp) < nr_entries) { mlb_close(mlb); /* Sorry, read error. */ free(buff); return NULL; } /* Shift occupied directory entries to the front of the array before sorting */ { int j; for(i = 0, j = nr_entries; i < j; i++) { char *ent1, *ent2; ent1 = buff + (i * entsize); /* Unused entries have 0177777 0177777 for the RAD50 name, which is not legal RAD50. */ if(WORD(ent1) == 0177777 && WORD(ent1 + 2) == 0177777) { while(--j > i && (ent2 = buff + (j * entsize), WORD(ent2) == 0177777 && WORD(ent2+2) == 0177777)) ; if(j <= i) break; /* All done. */ memcpy(ent1, ent2, entsize); /* Move used entry into unused entry's space */ memset(ent2, 0377, entsize); /* Mark entry unused */ } } /* Now i contains the actual number of entries. */ mlb->nentries = i; /* Sort the array by file position */ qsort(buff, i, entsize, compare_position); /* Now, allocate my in-memory directory */ mlb->directory = memcheck(malloc(sizeof(MLBENT) * mlb->nentries)); memset(mlb->directory, 0, sizeof(MLBENT) * mlb->nentries); /* Build in-memory directory */ for(j = 0; j < i; j++) { char radname[16]; char *ent; ent = buff + (j * entsize); unrad50(WORD(ent), radname); unrad50(WORD(ent+2), radname+3); radname[6] = 0; trim(radname); mlb->directory[j].label = memcheck(strdup(radname)); mlb->directory[j].position = BYTEPOS(ent); if(j < i-1) { mlb->directory[j].length = BYTEPOS(ent + entsize) - BYTEPOS(ent); } else { unsigned long max; char c; fseek(mlb->fp, 0, SEEK_END); max = ftell(mlb->fp); /* Look for last non-zero */ do { max--; fseek(mlb->fp, max, SEEK_SET); c = fgetc(mlb->fp); } while(max > 0 && c == 0); max++; mlb->directory[j].length = max - BYTEPOS(ent); } } free(buff); } /* Done. Return the struct that represents the opened MLB. */ return mlb; } /* mlb_close discards MLB and closes the file. */ void mlb_close(MLB *mlb) { if(mlb) { int i; if(mlb->directory) { for(i = 0; i < mlb->nentries; i++) { if(mlb->directory[i].label) free(mlb->directory[i].label); } free(mlb->directory); } if(mlb->fp) fclose(mlb->fp); free(mlb); } } /* mlb_entry returns a BUFFER containing the specified entry from the macro library, or NULL if not found. */ BUFFER *mlb_entry(MLB *mlb, char *name) { int i; MLBENT *ent; BUFFER *buf; char *bp; int c; for(i = 0; i < mlb->nentries; i++) { ent = &mlb->directory[i]; if(strcmp(mlb->directory[i].label, name) == 0) break; } if(i >= mlb->nentries) return NULL; /* Allocate a buffer to hold the text */ buf = new_buffer(); buffer_resize(buf, ent->length+1); /* Make it large enough */ bp = buf->buffer; fseek(mlb->fp, ent->position, SEEK_SET); for(i = 0; i < ent->length; i++) { c = fgetc(mlb->fp); /* Get macro byte */ if(c == '\r' || c == 0) /* If it's a carriage return or 0, discard it. */ continue; *bp++ = c; } *bp++ = 0; /* Store trailing 0 delim */ /* Now resize that buffer to the length actually read. */ buffer_resize(buf, bp - buf->buffer); return buf; } /* mlb_extract - walk thru a macro library and store it's contents into files in the current directory. See, I had decided not to bother writing macro library maintenance tools, since the user can call macros directly from the file system. But if you've already got a macro library without the sources, you can use this to extract the entries and maintain them in the file system from thence forward. */ void mlb_extract(MLB *mlb) { int i; FILE *fp; BUFFER *buf; for(i = 0; i < mlb->nentries; i++) { char name[32]; buf = mlb_entry(mlb, mlb->directory[i].label); sprintf(name, "%s.MAC", mlb->directory[i].label); fp = fopen(name, "w"); fwrite(buf->buffer, 1, buf->length, fp); fclose(fp); buffer_free(buf); } } simh-3.8.1/TOOLS/crossassemblers/macro11/macro11.dsw0000666000175000017500000000167307324436622020223 0ustar vlmvlmMicrosoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "dumpobj"=.\dumpobj.dsp - Package Owner=<4> Package=<5> {{{ begin source code control "$/macro11", BAAAAAAA . end source code control }}} Package=<4> {{{ }}} ############################################################################### Project: "macro11"=.\macro11.dsp - Package Owner=<4> Package=<5> {{{ begin source code control "$/macro11", BAAAAAAA . end source code control }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ begin source code control "$/macro11", BAAAAAAA . end source code control }}} Package=<3> {{{ }}} ############################################################################### simh-3.8.1/TOOLS/crossassemblers/macro11/mlb.h0000666000175000017500000000363507324436656017173 0ustar vlmvlm#ifndef MLB_H #define MLB_H /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "stream2.h" /* Routines to open and read entries from a macro library */ typedef struct mlbent { char *label; unsigned long position; int length; } MLBENT; typedef struct mlb { FILE *fp; MLBENT *directory; int nentries; } MLB; extern MLB *mlb_open(char *name); extern BUFFER *mlb_entry(MLB *mlb, char *name); extern void mlb_close(MLB *mlb); extern void mlb_extract(MLB *mlb); #endif /* MLB_H */ simh-3.8.1/TOOLS/crossassemblers/macro11/dumpobj.c0000666000175000017500000003110007375562726020044 0ustar vlmvlm/* Dump and interpret an object file. */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "rad50.h" #include "util.h" #define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) int psectid = 0; char *psects[256]; FILE *bin = NULL; int badbin = 0; int xferad = 1; char *readrec(FILE *fp, int *len) { int c, i; int chksum; char *buf; chksum = 0; while(c = fgetc(fp), c != EOF && c == 0) ; if(c == EOF) return NULL; if(c != 1) { fprintf(stderr, "Improperly formatted OBJ file (1)\n"); return NULL; // Not a properly formatted file. } chksum -= c; c = fgetc(fp); if(c != 0) { fprintf(stderr, "Improperly formatted OBJ file (2)\n"); return NULL; // Not properly formatted } chksum -= c; // even though for 0 the checksum isn't changed... c = fgetc(fp); if(c == EOF) { fprintf(stderr, "Improperly formatted OBJ file (3)\n"); return NULL; } *len = c; chksum -= c; c = fgetc(fp); if(c == EOF) { fprintf(stderr, "Improperly formatted OBJ file (4)\n"); return NULL; } *len += (c << 8); chksum -= c; *len -= 4; // Subtract header and length bytes from length if(*len < 0) { fprintf(stderr, "Improperly formatted OBJ file (5)\n"); return NULL; } buf = malloc(*len); if(buf == NULL) { fprintf(stderr, "Out of memory allocating %d bytes\n", *len); return NULL; // Bad alloc } i = fread(buf, 1, *len, fp); if(i < *len) { free(buf); fprintf(stderr, "Improperly formatted OBJ file (6)\n"); return NULL; } for(i = 0; i < *len; i++) { chksum -= (buf[i] & 0xff); } c = fgetc(fp); c &= 0xff; chksum &= 0xff; if(c != chksum) { free(buf); fprintf(stderr, "Bad record checksum, " "calculated=%d, recorded=%d\n", chksum, c); return NULL; } return buf; } void dump_bytes(char *buf, int len) { int i, j; for(i = 0; i < len; i += 8) { printf("\t%3.3o: ", i); for(j = i; j < len && j < i+8; j++) { printf("%3.3o ", buf[j] & 0xff); } printf("%*s", (i+8 - j) * 4, ""); for(j = i; j < len && j < i+8; j++) { int c = buf[j] & 0xff; if(!isprint(c)) c = '.'; putchar(c); } putchar('\n'); } } void dump_words(unsigned addr, char *buf, int len) { int i, j; for(i = 0; i < len; i += 8) { printf("\t%6.6o: ", addr); for(j = i; j < len && j < i+8; j += 2) { if(len - j >= 2) { unsigned word = WORD(buf + j); printf("%6.6o ", word); } else printf("%3.3o ", buf[j] & 0xff); } printf("%*s", (i+8 - j) * 7 / 2, ""); for(j = i; j < len && j < i+8; j++) { int c = buf[j] & 0xff; if(!isprint(c)) c = '.'; putchar(c); } putchar('\n'); addr += 8; } } void dump_bin(unsigned addr, char *buf, int len) { int chksum; /* Checksum is negative sum of all bytes including header and length */ int FBR_LEAD1 = 1, FBR_LEAD2 = 0; int i; unsigned hdrlen = len + 6; for(i = 0; i < 8; i++) fputc (0, bin); chksum = 0; if(fputc(FBR_LEAD1, bin) == EOF) return; /* All recs begin with 1,0 */ chksum -= FBR_LEAD1; if(fputc(FBR_LEAD2, bin) == EOF) return; chksum -= FBR_LEAD2; i = hdrlen & 0xff; /* length, lsb */ chksum -= i; if(fputc(i, bin) == EOF) return; i = (hdrlen >> 8) & 0xff; /* length, msb */ chksum -= i; if(fputc(i, bin) == EOF) return; i = addr & 0xff; /* origin, msb */ chksum -= i; if(fputc(i, bin) == EOF) return; i = (addr >> 8) & 0xff; /* origin, lsb */ chksum -= i; if(fputc(i, bin) == EOF) return; if ((len == 0) || (buf == NULL)) return; /* end of tape block */ i = fwrite(buf, 1, len, bin); if(i < len) return; while(len > 0) /* All the data bytes */ { chksum -= *buf++ & 0xff; len--; } chksum &= 0xff; fputc(chksum, bin); /* Followed by the checksum byte */ return; /* Worked okay. */ } void trim(char *buf) { char *cp; for(cp = buf + strlen(buf); cp > buf; cp--) { if(cp[-1] != ' ') break; } *cp = 0; } char **all_gsds = NULL; int nr_gsds = 0; int gsdsize = 0; void add_gsdline(char *line) { if(nr_gsds >= gsdsize || all_gsds == NULL) { gsdsize += 128; all_gsds = realloc(all_gsds, gsdsize * sizeof(char *)); if(all_gsds == NULL) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } } all_gsds[nr_gsds++] = line; } void got_gsd(char *cp, int len) { int i; char *gsdline; for(i = 2; i < len; i += 8) { char name[8]; unsigned value; unsigned flags; gsdline = malloc(256); if(gsdline == NULL) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } unrad50(WORD(cp+i), name); unrad50(WORD(cp+i+2), name+3); name[6] = 0; value = WORD(cp+i+6); flags = cp[i+4] & 0xff; switch(cp[i+5] & 0xff) { case 0: sprintf(gsdline, "\tMODNAME %s=%o flags=%o\n", name, value, flags); break; case 1: sprintf(gsdline, "\tCSECT %s=%o flags=%o\n", name, value, flags); break; case 2: sprintf(gsdline, "\tISD %s=%o flags=%o\n", name, value, flags); break; case 3: sprintf(gsdline, "\tXFER %s=%o flags=%o\n", name, value, flags); xferad = value; break; case 4: sprintf(gsdline, "\tGLOBAL %s=%o %s flags=%o\n", name, value, cp[i+4] & 8 ? "DEF" : "REF", flags); break; case 5: sprintf(gsdline, "\tPSECT %s=%o flags=%o\n", name, value, flags); psects[psectid] = strdup(name); trim(psects[psectid++]); break; case 6: sprintf(gsdline, "\tIDENT %s=%o flags=%o\n", name, value, flags); break; case 7: sprintf(gsdline, "\tVSECT %s=%o flags=%o\n", name, value, flags); break; default: sprintf(gsdline, "\t***Unknown GSD entry type %d flags=%o\n", cp[i+5] & 0xff, flags); break; } gsdline = realloc(gsdline, strlen(gsdline)+1); add_gsdline(gsdline); } } int compare_gsdlines(const void *p1, const void *p2) { const char * const *l1 = p1, * const *l2 = p2; return strcmp(*l1, *l2); } void got_endgsd(char *cp, int len) { int i; qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); printf("GSD:\n"); for(i = 0; i < nr_gsds; i++) { fputs(all_gsds[i], stdout); free(all_gsds[i]); } printf("ENDGSD\n"); free(all_gsds); } unsigned last_text_addr = 0; void got_text(char *cp, int len) { unsigned addr = WORD(cp+2); last_text_addr = addr; printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len-4); dump_words(last_text_addr, cp+4, len-4); if (bin) dump_bin(last_text_addr, cp+4, len-4); } void rad50name(char *cp, char *name) { unrad50(WORD(cp), name); unrad50(WORD(cp+2), name+3); name[6] = 0; trim(name); } void got_rld(char *cp, int len) { int i; printf("RLD\n"); for(i = 2; i < len;) { unsigned addr; unsigned word; unsigned disp = cp[i+1] & 0xff; char name[8]; char *byte; addr = last_text_addr + disp - 4; byte = ""; if(cp[i] & 0200) byte = " byte"; switch(cp[i] & 0x7f) { case 01: printf("\tInternal%s %o=%o\n", byte, addr, WORD(cp+i+2)); i += 4; break; case 02: rad50name(cp+i+2, name); printf("\tGlobal%s %o=%s\n", byte, addr, name); i += 6; break; case 03: printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp+i+2)); i += 4; badbin = 1; break; case 04: rad50name(cp+i+2, name); printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); i += 6; badbin = 1; break; case 05: rad50name(cp+i+2, name); word = WORD(cp+i+6); printf("\tGlobal plus offset%s %o=%s+%o\n", byte, addr, name, word); i += 8; badbin = 1; break; case 06: rad50name(cp+i+2, name); word = WORD(cp+i+6); printf("\tGlobal plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); i += 8; badbin = 1; break; case 07: rad50name(cp+i+2, name); word = WORD(cp+i+6); printf("\tLocation counter definition %s+%o\n", name, word); i += 8; last_text_addr = word; break; case 010: word = WORD(cp+i+2); printf("\tLocation counter modification %o\n", word); i += 4; last_text_addr = word; break; case 011: printf("\t.LIMIT %o\n", addr); i += 2; break; case 012: rad50name(cp+i+2, name); printf("\tPSECT%s %o=%s\n", byte, addr, name); i += 6; badbin = 1; break; case 014: rad50name(cp+i+2, name); printf("\tPSECT displaced%s %o=%s+%o\n", byte, addr, name, word); i += 6; badbin = 1; break; case 015: rad50name(cp+i+2, name); word = WORD(cp+i+6); printf("\tPSECT plus offset%s %o=%s+%o\n", byte, addr, name, word); i += 8; badbin = 1; break; case 016: rad50name(cp+i+2, name); word = WORD(cp+i+6); printf("\tPSECT plus offset displaced%s %o=%s+%o\n", byte, addr, name, word); i += 8; badbin = 1; break; case 017: badbin = 1; printf("\tComplex%s %o=", byte, addr); i += 2; { char *xp = cp + i; int size; for(;;) { size = 1; switch(*xp) { case 000: fputs("nop ", stdout); break; case 001: fputs("+ ", stdout); break; case 002: fputs("- ", stdout); break; case 003: fputs("* ", stdout); break; case 004: fputs("/ ", stdout); break; case 005: fputs("& ", stdout); break; case 006: fputs("! ", stdout); break; case 010: fputs("neg ", stdout); break; case 011: fputs("^C ", stdout); break; case 012: fputs("store ", stdout); break; case 013: fputs("store{disp} ", stdout); break; case 016: rad50name(xp+1, name); printf("%s ", name); size = 5; break; case 017: assert((xp[1] & 0377) < psectid); printf("%s:%o ", psects[xp[1] & 0377], WORD(xp+2)); size = 4; break; case 020: printf("%o ", WORD(xp+1)); size = 3; break; default: printf("**UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); return; } i += size; if(*xp == 012 || *xp == 013) break; xp += size; } fputc('\n', stdout); break; } default: printf("\t***Unknown RLD code %o\n", cp[i] & 0xff); return; } } } void got_isd(char *cp, int len) { printf("ISD len=%o\n"); } void got_endmod(char *cp, int len) { printf("ENDMOD\n"); } void got_libhdr(char *cp, int len) { printf("LIBHDR\n"); } void got_libend(char *cp, int len) { printf("LIBEND\n"); } int main(int argc, char *argv[]) { int len; FILE *fp; char *cp; fp = fopen(argv[1], "rb"); if(fp == NULL) return EXIT_FAILURE; if(argv[2]) { bin = fopen(argv[2], "wb"); if(bin == NULL) return EXIT_FAILURE; } while((cp = readrec(fp, &len)) != NULL) { switch(cp[0] & 0xff) { case 1: got_gsd(cp, len); break; case 2: got_endgsd(cp, len); break; case 3: got_text(cp, len); break; case 4: got_rld(cp, len); break; case 5: got_isd(cp, len); break; case 6: got_endmod(cp, len); break; case 7: got_libhdr(cp, len); break; case 8: got_libend(cp, len); break; default: printf("Unknown record type %d\n", cp[0] & 0xff); break; } free(cp); } if (bin) { dump_bin (xferad, NULL, 0); fclose (bin); if (badbin) fprintf (stderr, "Probable errors in binary file\n"); } fclose (fp); return EXIT_SUCCESS; } simh-3.8.1/TOOLS/crossassemblers/macro11/object.c0000666000175000017500000004554507324436672017666 0ustar vlmvlm/* object.c - writes RT-11 compatible .OBJ files. Ref: RT-11 Software Support Manual, File Formats. */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "rad50.h" #include "object.h" #include "macro11.h" /* writerec writes "formatted binary records." Each is preceeded by any number of 0 bytes, begins with a 1,0 pair, followed by 2 byte length, followed by data, followed by 1 byte negative checksum. */ static int writerec(FILE *fp, char *data, int len) { int chksum; /* Checksum is negative sum of all bytes including header and length */ int i; unsigned hdrlen = len + 4; if(fp == NULL) return 1; /* Silently ignore this attempt to write. */ chksum = 0; if(fputc(FBR_LEAD1, fp) == EOF) /* All recs begin with 1,0 */ return 0; chksum -= FBR_LEAD1; if(fputc(FBR_LEAD2, fp) == EOF) return 0; chksum -= FBR_LEAD2; i = hdrlen & 0xff; /* length, lsb */ chksum -= i; if(fputc(i, fp) == EOF) return 0; i = (hdrlen >> 8) & 0xff; /* length, msb */ chksum -= i; if(fputc(i, fp) == EOF) return 0; i = fwrite(data, 1, len, fp); if(i < len) return 0; while(len > 0) /* All the data bytes */ { chksum -= *data++ & 0xff; len--; } chksum &= 0xff; fputc(chksum, fp); /* Followed by the checksum byte */ return 1; /* Worked okay. */ } /* gsd_init - prepare a GSD prior to writing GSD records */ void gsd_init(GSD *gsd, FILE *fp) { gsd->fp = fp; gsd->buf[0] = OBJ_GSD; /* GSD records start with 1,0 */ gsd->buf[1] = 0; gsd->offset = 2; /* Offset for further additions */ } /* gsd_flush - write buffered GSD records */ int gsd_flush(GSD *gsd) { if(gsd->offset > 2) { if(!writerec(gsd->fp, gsd->buf, gsd->offset)) return 0; gsd_init(gsd, gsd->fp); } return 1; } /* gsd_write - buffers a GSD record */ /* All GSD entries have the following 8 byte format: */ /* 4 bytes RAD50 name */ /* 1 byte flags */ /* 1 byte type */ /* 2 bytes value */ static int gsd_write(GSD *gsd, char *name, int flags, int type, int value) { char *cp; unsigned radtbl[2]; if(gsd->offset > sizeof(gsd->buf) - 8) { if(!gsd_flush(gsd)) return 0; } rad50x2(name, radtbl); cp = gsd->buf + gsd->offset; *cp++ = radtbl[0] & 0xff; *cp++ = (radtbl[0] >> 8) & 0xff; *cp++ = radtbl[1] & 0xff; *cp++ = (radtbl[1] >> 8) & 0xff; *cp++ = flags; *cp++ = type; *cp++ = value & 0xff; *cp = (value >> 8) & 0xff; gsd->offset += 8; return 1; } /* gsd_mod - Write module name to GSD */ int gsd_mod(GSD *gsd, char *modname) { return gsd_write(gsd, modname, 0, GSD_MODNAME, 0); } /* gsd_csect - Write a control section name & size to the GSD */ int gsd_csect(GSD *gsd, char *sectname, int size) { return gsd_write(gsd, sectname, 0, GSD_CSECT, size); } /* gsd_intname - Write an internal symbol (ignored by RT-11 linker) */ int gsd_intname(GSD *gsd, char *name, unsigned value) { return gsd_write(gsd, name, 0, GSD_ISN, value); } /* gsd_xfer - Write a program transfer address to GSD */ int gsd_xfer(GSD *gsd, char *name, unsigned value) { return gsd_write(gsd, name, 010, GSD_XFER, value); } /* gsd_global - Write a global definition or reference to GSD */ /* Caller must be aware of the proper flags. */ int gsd_global(GSD *gsd, char *name, int flags, unsigned value) { return gsd_write(gsd, name, flags, GSD_GLOBAL, value); } /* Write a program section to the GSD */ /* Caller must be aware of the proper flags. */ int gsd_psect(GSD *gsd, char *name, int flags, int size) { return gsd_write(gsd, name, flags, GSD_PSECT, size); } /* Write program ident to GSD */ int gsd_ident(GSD *gsd, char *name) { return gsd_write(gsd, name, 0, GSD_IDENT, 0); } /* Write virtual array declaration to GSD */ int gsd_virt(GSD *gsd, char *name, int size) { return gsd_write(gsd, name, 0, GSD_VSECT, size); } /* Write ENDGSD record */ int gsd_end(GSD *gsd) { gsd->buf[0] = OBJ_ENDGSD; gsd->buf[1] = 0; return writerec(gsd->fp, gsd->buf, 2); } /* TEXT and RLD record handling */ /* TEXT records contain the plain binary of the program. An RLD record refers to the prior TEXT record, giving relocation information. */ /* text_init prepares a TEXT_RLD prior to writing */ void text_init(TEXT_RLD *tr, FILE *fp, unsigned addr) { tr->fp = fp; tr->text[0] = OBJ_TEXT; /* text records begin with 3, 0 */ tr->text[1] = 0; tr->text[2] = addr & 0xff; /* and are followed by load address */ tr->text[3] = (addr >> 8) & 0xff; tr->txt_offset = 4; /* Here's where recording new text will begin */ tr->rld[0] = OBJ_RLD; /* RLD records begin with 4, 0 */ tr->rld[1] = 0; tr->txt_addr = addr; tr->rld_offset = 2; /* And are followed by RLD entries */ } /* text_flush - flushes buffer TEXT and RLD records. */ int text_flush(TEXT_RLD *tr) { if(tr->txt_offset > 4) { if(!writerec(tr->fp, tr->text, tr->txt_offset)) return 0; } if(tr->rld_offset > 2) { if(!writerec(tr->fp, tr->rld, tr->rld_offset)) return 0; } return 1; } /* Used to ensure that TEXT and RLD information will be in adjacent records. If not enough space exists in either buffer, both are flushed. */ static int text_fit(TEXT_RLD *tr, unsigned addr, int txtsize, int rldsize) { if(tr->txt_offset + txtsize <= sizeof(tr->text) && tr->rld_offset + rldsize <= sizeof(tr->rld) && (txtsize == 0 || tr->txt_addr + tr->txt_offset - 4 == addr)) return 1; /* All's well. */ if(!text_flush(tr)) return 0; text_init(tr, tr->fp, addr); return 1; } /* text_word_i - internal text_word. Used when buffer space is already assured. */ static void text_word_i(TEXT_RLD *tr, unsigned w, int size) { tr->text[tr->txt_offset++] = w & 0xff; if(size > 1) tr->text[tr->txt_offset++] = (w >> 8) & 0xff; } /* text_word - write constant word to text */ int text_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word) { if(!text_fit(tr, *addr, size, 0)) return 0; text_word_i(tr, word, size); *addr += size; /* Update the caller's DOT */ return 1; /* say "ok". */ } /* rld_word - adds a word to the RLD information. */ static void rld_word(TEXT_RLD *tr, unsigned wd) { tr->rld[tr->rld_offset++] = wd & 0xff; tr->rld[tr->rld_offset++] = (wd >> 8) & 0xff; } /* rld_byte - adds a byte to rld information. */ static void rld_byte(TEXT_RLD *tr, unsigned byte) { tr->rld[tr->rld_offset++] = byte & 0xff; } /* rld_code - write the typical RLD first-word code. Encodes the given address as the offset into the prior TEXT record. */ static void rld_code(TEXT_RLD *tr, unsigned code, unsigned addr, int size) { unsigned offset = addr - tr->txt_addr + 4; rld_word(tr, code | offset << 8 | (size == 1 ? 0200 : 0)); } /* rld_code_naddr - typical RLD entries refer to a text address. This one is used when the RLD code does not. */ static void rld_code_naddr(TEXT_RLD *tr, unsigned code, int size) { rld_word(tr, code | (size == 1 ? 0200 : 0)); } /* write a word with a psect-relative value */ int text_internal_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word) { if(!text_fit(tr, *addr, size, 4)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_INT, *addr, size); rld_word(tr, word); *addr += size; return 1; } /* write a word which is an absolute reference to a global symbol */ int text_global_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global) { unsigned radtbl[2]; if(!text_fit(tr, *addr, size, 6)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_GLOBAL, *addr, size); rad50x2(global, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); *addr += size; return 1; } /* Write a word which is a PC-relative reference to an absolute address */ int text_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word) { if(!text_fit(tr, *addr, size, 4)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_INT_DISP, *addr, size); rld_word(tr, word); *addr += size; return 1; } /* write a word which is a PC-relative reference to a global symbol */ int text_global_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global) { unsigned radtbl[2]; if(!text_fit(tr, *addr, size, 6)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_GLOBAL_DISP, *addr, size); rad50x2(global, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); *addr += size; return 1; } /* write a word which is an absolute reference to a global symbol plus an offset */ /* Optimizes to text_global_word when the offset is zero. */ int text_global_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global) { unsigned radtbl[2]; if(word == 0) return text_global_word(tr, addr, size, word, global); if(!text_fit(tr, *addr, size, 8)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_GLOBAL_OFFSET, *addr, size); rad50x2(global, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); rld_word(tr, word); *addr += size; return 1; } /* write a word which is a PC-relative reference to a global symbol plus an offset */ /* Optimizes to text_global_displaced_word when the offset is zero. */ int text_global_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *global) { unsigned radtbl[2]; if(word == 0) return text_global_displaced_word(tr, addr, size, word, global); if(!text_fit(tr, *addr, size, 8)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_GLOBAL_OFFSET_DISP, *addr, size); rad50x2(global, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); rld_word(tr, word); *addr += size; return 1; } /* Define current program counter, plus PSECT */ /* Different because it must be the last RLD entry in a block. That's because TEXT records themselves contain the current text address. */ int text_define_location(TEXT_RLD *tr, char *name, unsigned *addr) { unsigned radtbl[2]; if(!text_fit(tr, *addr, 0, 8)) /* No text space used */ return 0; rld_code_naddr(tr, RLD_LOCDEF, 2); /* RLD code for "location counter def" with no offset */ rad50x2(name, radtbl); rld_word(tr, radtbl[0]); /* Set current section name */ rld_word(tr, radtbl[1]); rld_word(tr, *addr); /* Set current location addr */ if(!text_flush(tr)) /* Flush that block out. */ return 0; text_init(tr, tr->fp, *addr); /* Set new text address */ return 1; } /* Modify current program counter, assuming current PSECT */ /* Location counter modification is similarly weird */ /* (I wonder - why is this RLD code even here? TEXT records contain thair own start address.) */ int text_modify_location(TEXT_RLD *tr, unsigned *addr) { if(!text_fit(tr, *addr, 0, 4)) /* No text space used */ return 0; rld_code_naddr(tr, RLD_LOCMOD, 2); /* RLD code for "location counter mod" with no offset */ rld_word(tr, *addr); /* Set current location addr */ if(!text_flush(tr)) /* Flush that block out. */ return 0; text_init(tr, tr->fp, *addr); /* Set new text address */ return 1; } /* write two words containing program limits (the .LIMIT directive) */ int text_limits(TEXT_RLD *tr, unsigned *addr) { if(!text_fit(tr, *addr, 4, 2)) return 0; text_word_i(tr, 0, 2); text_word_i(tr, 0, 2); rld_code(tr, RLD_LIMITS, *addr, 2); *addr += 4; return 1; } /* write a word which is the start address of a different PSECT */ int text_psect_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name) { unsigned radtbl[2]; if(!text_fit(tr, *addr, size, 6)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_PSECT, *addr, size); rad50x2(name, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); *addr += size; return 1; } /* write a word which is an offset from the start of a different PSECT */ /* Optimizes to text_psect_word when offset is zero */ int text_psect_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name) { unsigned radtbl[2]; if(word == 0) return text_psect_word(tr, addr, size, word, name); if(!text_fit(tr, *addr, size, 8)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_PSECT_OFFSET, *addr, size); rad50x2(name, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); rld_word(tr, word); *addr += size; return 1; } /* write a word which is the address of a different PSECT, PC-relative */ int text_psect_displaced_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name) { unsigned radtbl[2]; if(!text_fit(tr, *addr, size, 6)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_PSECT_DISP, *addr, size); rad50x2(name, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); *addr += size; return 1; } /* write a word which is an offset from the address of a different PSECT, PC-relative */ /* Optimizes to text_psect_displaced_word when offset is zero */ int text_psect_displaced_offset_word(TEXT_RLD *tr, unsigned *addr, int size, unsigned word, char *name) { unsigned radtbl[2]; if(word == 0) return text_psect_displaced_word(tr, addr, size, word, name); if(!text_fit(tr, *addr, size, 8)) return 0; text_word_i(tr, word, size); rld_code(tr, RLD_PSECT_OFFSET_DISP, *addr, size); rad50x2(name, radtbl); rld_word(tr, radtbl[0]); rld_word(tr, radtbl[1]); rld_word(tr, word); *addr += size; return 1; } /* complex relocation! */ /* A complex relocation expression is where a piece of code is fed to the linker asking it to do some math for you, and store the result in a program word. The code is a stack-based language. */ /* complex_begin initializes a TEXT_COMPLEX */ void text_complex_begin(TEXT_COMPLEX *tx) { tx->len = 0; } /* text_complex_fit checks if a complex expression will fit and returns a pointer to it's location */ static char *text_complex_fit(TEXT_COMPLEX *tx, int size) { int len; if(tx->len + size > sizeof(tx->accum)) return NULL; /* Expression has grown too complex. */ len = tx->len; tx->len += size; return tx->accum + len; } /* text_complex_byte stores a single byte. */ static int text_complex_byte(TEXT_COMPLEX *tx, unsigned byte) { char *cp = text_complex_fit(tx, 1); if(!cp) return 0; *cp = byte; return 1; } /* text_complex_add - add top two stack elements */ int text_complex_add(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_ADD); } /* text_complex_sub - subtract top two stack elements. */ /* You know, I think these function labels are self-explanatory... */ int text_complex_sub(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_SUB); } int text_complex_mul(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_MUL); } int text_complex_div(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_DIV); } int text_complex_and(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_AND); } int text_complex_or(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_OR); } int text_complex_xor(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_XOR); } int text_complex_com(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_COM); } int text_complex_neg(TEXT_COMPLEX *tx) { return text_complex_byte(tx, CPLX_NEG); } /* text_complex_lit pushes a literal value to the stack. */ int text_complex_lit(TEXT_COMPLEX *tx, unsigned word) { char *cp = text_complex_fit(tx, 3); if(!cp) return 0; *cp++ = CPLX_CONST; *cp++ = word & 0xff; *cp = (word >> 8) & 0xff; return 1; } /* text_complex_global pushes the value of a global variable to the stack */ int text_complex_global(TEXT_COMPLEX *tx, char *name) { unsigned radtbl[2]; char *cp = text_complex_fit(tx, 5); if(!cp) return 0; rad50x2(name, radtbl); *cp++ = CPLX_GLOBAL; *cp++ = radtbl[0] & 0xff; *cp++ = (radtbl[0] >> 8) & 0xff; *cp++ = radtbl[1] & 0xff; *cp = (radtbl[1] >> 8) & 0xff; return 1; } /* text_complex_psect pushes the value of an offset into a PSECT to the stack. */ /* What was not documented in the Software Support manual is that PSECT "sect" numbers are assigned in the order they appear in the source program, and the order they appear in the GSD. i.e. the first PSECT GSD is assigned sector 0 (which is always the default absolute section so that's a bad example), the next sector 1, etc. */ int text_complex_psect(TEXT_COMPLEX *tx, unsigned sect, unsigned offset) { char *cp = text_complex_fit(tx, 4); if(!cp) return 0; *cp++ = CPLX_REL; *cp++ = sect & 0xff; *cp++ = offset & 0xff; *cp = (offset >> 8) & 0xff; return 1; } /* text_complex_commit - store the result of the complex expression and end the RLD code. */ int text_complex_commit(TEXT_RLD *tr, unsigned *addr, int size, TEXT_COMPLEX *tx, unsigned word) { int i; text_complex_byte(tx, CPLX_STORE); if(!text_fit(tr, *addr, size, tx->len + 2)) return 0; rld_code(tr, RLD_COMPLEX, *addr, size); for(i = 0; i < tx->len; i++) rld_byte(tr, tx->accum[i]); text_word_i(tr, word, size); *addr += size; return 1; } /* text_complex_commit_displaced - store the result of the complex expression, relative to the current PC, and end the RLD code */ int text_complex_commit_displaced(TEXT_RLD *tr, unsigned *addr, int size, TEXT_COMPLEX *tx, unsigned word) { int i; text_complex_byte(tx, CPLX_STORE_DISP); if(!text_fit(tr, *addr, size, tx->len + 2)) return 0; rld_code(tr, RLD_COMPLEX, *addr, size); for(i = 0; i < tx->len; i++) rld_byte(tr, tx->accum[i]); text_word_i(tr, word, size); *addr += size; return 1; } /* Write end-of-object-module to file. */ int write_endmod(FILE *fp) { char endmod[2] = { OBJ_ENDMOD, 0 }; return writerec(fp, endmod, 2); } simh-3.8.1/TOOLS/crossassemblers/macro11/rad50.c0000666000175000017500000000603007324436606017312 0ustar vlmvlm/* Functions to convert RAD50 to or from ASCII. */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "rad50.h" static char radtbl[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$. 0123456789"; /* rad50 converts from 0 to 3 ASCII (or EBCDIC, if your compiler is so inclined) characters into a RAD50 word. */ unsigned rad50(char *cp, char **endp) { unsigned long acc = 0; char *rp; if(endp) *endp = cp; if(!*cp) /* Got to check for end-of-string manually, because strchr will call it a hit. :-/ */ return acc; rp = strchr(radtbl, toupper(*cp)); if(rp == NULL) /* Not a RAD50 character */ return acc; acc = (rp - radtbl) * 03100; /* Convert */ cp++; /* Now, do the same thing two more times... */ if(endp) *endp = cp; if(!*cp) return acc; rp = strchr(radtbl, toupper(*cp)); if(rp == NULL) return acc; acc += (rp - radtbl) * 050; cp++; if(endp) *endp = cp; if(!*cp) return acc; rp = strchr(radtbl, toupper(*cp)); if(rp == NULL) return acc; acc += (rp - radtbl); cp++; if(endp) *endp = cp; return acc; /* Done. */ } /* rad50x2 - converts from 0 to 6 characters into two words of RAD50. */ void rad50x2(char *cp, unsigned *rp) { *rp++ = rad50(cp, &cp); *rp = 0; if(*cp) *rp = rad50(cp, &cp); } /* unrad50 - converts a RAD50 word to three characters of ASCII. */ void unrad50(unsigned word, char *cp) { if(word < 0175000) /* Is it legal RAD50? */ { cp[0] = radtbl[word / 03100]; cp[1] = radtbl[(word / 050) % 050]; cp[2] = radtbl[word % 050]; } else cp[0] = cp[1] = cp[2] = ' '; } simh-3.8.1/TOOLS/crossassemblers/macro11/dumpobj.dsp0000666000175000017500000001015207324436616020404 0ustar vlmvlm# Microsoft Developer Studio Project File - Name="dumpobj" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=dumpobj - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "dumpobj.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "dumpobj.mak" CFG="dumpobj - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "dumpobj - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "dumpobj - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName ""$/macro11", BAAAAAAA" # PROP Scc_LocalPath "." CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "dumpobj - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "dumpobj___Release" # PROP Intermediate_Dir "dumpobj___Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "dumpobj - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "dumpobj___Debug" # PROP Intermediate_Dir "dumpobj___Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "dumpobj - Win32 Release" # Name "dumpobj - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\dumpobj.c # End Source File # Begin Source File SOURCE=.\Rad50.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\Rad50.h # End Source File # Begin Source File SOURCE=.\util.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project simh-3.8.1/TOOLS/crossassemblers/macro11/util.c0000666000175000017500000001067107324436704017361 0ustar vlmvlm/* Some generally useful routines */ /* The majority of the non-portable code is in here. */ /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "util.h" #ifdef WIN32 #include #include #define stat _stat #else #include #include #endif /* Sure, the library typically provides some kind of ultoa or _ultoa function. But since it's merely typical and not standard, and since the function is so simple, I'll write my own. It's significant feature is that it'll produce representations in any number base from 2 to 36. */ char *my_ultoa(unsigned long val, char *buf, unsigned int base) { static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char *strt = buf, *end; do { *buf++ = digits[val % base]; val /= base; } while(val != 0); *buf = 0; /* delimit */ end = buf+1; /* Now reverse the bytes */ while(buf > strt) { char temp; temp = *--buf; *buf = *strt; *strt++ = temp; } return end; } /* Ditto my_ultoa. This actually emits a signed representation in other number bases. */ char *my_ltoa(long val, char *buf, unsigned int base) { unsigned long uval; if(val < 0) uval = -val, *buf++ = '-'; else uval = val; return my_ultoa(uval, buf, base); } /* _searchenv is a function provided by the MSVC library that finds files which may be anywhere along a path which appears in an environment variable. I duplicate that function for portability. Note also that mine avoids destination buffer overruns. Note: uses strtok. This means it'll screw you up if you expect your strtok context to remain intact when you use this function. */ void my_searchenv(char *name, char *envname, char *hitfile, int hitlen) { char *env; char *envcopy; char *cp; *hitfile = 0; /* Default failure indication */ /* Note: If the given name is absolute, then don't search the path, but use it as is. */ if( #ifdef WIN32 strchr(name, ':') != NULL || /* Contain a drive spec? */ name[0] == '\\' || /* Start with absolute ref? */ #endif name[0] == '/') /* Start with absolute ref? */ { strncpy(hitfile, name, hitlen); /* Copy to target */ return; } env = getenv(envname); if(env == NULL) return; /* Variable not defined, no search. */ envcopy = strdup(env); /* strtok destroys it's text argument. I don't want the return value from getenv destroyed. */ while((cp = strtok(envcopy, PATHSEP)) != NULL) { struct stat info; char *concat = malloc(strlen(cp) + strlen(name) + 2); if(concat == NULL) { free(envcopy); return; } strcpy(concat, cp); if(concat[strlen(concat)-1] != '/') strcat(concat, "/"); strcat(concat, name); if(!stat(concat, &info)) { /* Copy the file name to hitfile. Assure that it's really zero-delimited. */ strncpy(hitfile, concat, hitlen-1); hitfile[hitlen-1] = 0; free(envcopy); return; } } /* If I fall out of that loop, then hitfile indicates no match, and return. */ } simh-3.8.1/TOOLS/crossassemblers/macro11/changes0000666000175000017500000000061307324436614017566 0ustar vlmvlm15-July-2001 version 0.2 removed references to snprintf from dumpobj.c and mlb.c for portability fixed a type cast warning in dumpobj.c compare_gsdlines Removed strcasecmp from macro11.c for portability Removed references to wnewmem.c from makefile (isn't needed) makefile more compatible with non-gnu make and compiler main prints version 0.2 14-July-2001 First release, version 0.1. simh-3.8.1/TOOLS/crossassemblers/macro11/macro11.h0000666000175000017500000000305307324436654017654 0ustar vlmvlm#ifndef MACRO11_H #define MACRO11_H /* Copyright (c) 2001, Richard Krehbiel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ extern void *memcheck(void *p); #endif simh-3.8.1/TOOLS/crossassemblers/macro11/depends0000666000175000017500000000000007324436574017573 0ustar vlmvlmsimh-3.8.1/TOOLS/crossassemblers/macro11/macro11.dsp0000666000175000017500000001224307324436626020213 0ustar vlmvlm# Microsoft Developer Studio Project File - Name="macro11" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=macro11 - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "macro11.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "macro11.mak" CFG="macro11 - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "macro11 - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "macro11 - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName ""$/macro11", BAAAAAAA" # PROP Scc_LocalPath "." CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "macro11 - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /profile # Begin Special Build Tool TargetPath=.\Release\macro11.exe SOURCE="$(InputPath)" PostBuild_Cmds=copy $(TargetPath) c:\bin # End Special Build Tool !ELSEIF "$(CFG)" == "macro11 - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MEM_DEBUG" /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /profile # Begin Special Build Tool TargetPath=.\Debug\macro11.exe SOURCE="$(InputPath)" PostBuild_Cmds=copy $(TargetPath) c:\bin # End Special Build Tool !ENDIF # Begin Target # Name "macro11 - Win32 Release" # Name "macro11 - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\macro11.c # End Source File # Begin Source File SOURCE=.\mlb.c # End Source File # Begin Source File SOURCE=.\object.c # End Source File # Begin Source File SOURCE=.\rad50.c # End Source File # Begin Source File SOURCE=.\stream2.c # End Source File # Begin Source File SOURCE=.\util.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\macro11.h # End Source File # Begin Source File SOURCE=.\mlb.h # End Source File # Begin Source File SOURCE=.\object.h # End Source File # Begin Source File SOURCE=.\Rad50.h # End Source File # Begin Source File SOURCE=.\stream2.h # End Source File # Begin Source File SOURCE=.\util.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # Begin Source File SOURCE=.\Changes # End Source File # Begin Source File SOURCE=.\License # End Source File # Begin Source File SOURCE=.\Makefile # End Source File # Begin Source File SOURCE=.\Readme # End Source File # Begin Source File SOURCE=.\Todo # End Source File # End Target # End Project simh-3.8.1/TOOLS/crossassemblers/macro8x.c0000666000175000017500000050152207401213716016512 0ustar vlmvlm/******************************************************************************/ /* */ /* Program: MACRO8X */ /* File: macro8x.c */ /* Author: Gary A. Messenbrink */ /* MACRO8X modifications: Bob Supnik (:) : error: at Loc = */ /* */ /* An example error message is: */ /* */ /* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ /* */ /* The error diagnostics put in the listing start with a two character */ /* error code (if appropriate) and a short message. A carat '^' is */ /* placed under the item in error if appropriate. */ /* An example error message is: */ /* */ /* 17 07616 3000 DCA UNDEF */ /* UD undefined ^ */ /* 18 07617 1777 TAD I DUMMY */ /* */ /* When an indirect is generated, an at character '@' is placed after the */ /* the instruction value in the listing as an indicator as follows: */ /* */ /* 14 03716 1777@ TAD OFFPAG */ /* */ /* Undefined symbols are marked in the symbol table listing by prepending */ /* a '?' to the symbol. Redefined symbols are marked in the symbol table */ /* listing by prepending a '#' to the symbol. Examples are: */ /* */ /* #REDEF 04567 */ /* SWITCH 07612 */ /* ?UNDEF 00000 */ /* */ /* Refer to the code for the diagnostic messages generated. */ /* */ /* BUGS */ /* Only a minimal effort has been made to keep the listing format */ /* anything like the PAL-8 listing format. */ /* The operation of the conditional assembly pseudo-ops may not function */ /* exactly as the DEC versions. I did not have any examples of these so */ /* the implementation is my interpretation of how they should work. */ /* */ /* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ /* file type that was specified on startup. This was intentional and */ /* and allows rim formatted data to be output prior to the actual binary */ /* formatted data. On UN*X style systems, the same effect can be achieved */ /* by using the "cat" command, but on DOS/Windows systems, doing this was */ /* a major chore. */ /* */ /* The floating point input does not generate values exactly as the DEC */ /* compiler does. I worked out several examples by hand and believe that */ /* this implementation is slightly more accurate. If I am mistaken, */ /* let me know and, if possible, a better method of generating the values. */ /* */ /* BUILD and INSTALLATION */ /* This program has been built and successfully executed on: */ /* a. Linux (80486 CPU)using gcc */ /* b. RS/6000 (AIX 3.2.5) */ /* c. Borland C++ version 3.1 (large memory model) */ /* d. Borland C++ version 4.52 (large memory model) */ /* with no modifications to the source code. */ /* */ /* On UNIX type systems, store the the program as the pal command */ /* and on PC type systems, store it as pal.exe */ /* */ /* HISTORICAL NOTE: */ /* This assembler was written to support the fleet of PDP-8 systems */ /* used by the Bay Area Rapid Transit System. As of early 1997, */ /* this includes about 40 PDP-8/E systems driving the train destination */ /* signs in passenger stations. */ /* */ /* REFERENCES: */ /* This assembler is based on the pal assember by: */ /* Douglas Jones and */ /* Rich Coon */ /* */ /* DISCLAIMER: */ /* See the symbol table for the set of pseudo-ops supported. */ /* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ /* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ /* documentation of pseudo-ops. */ /* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ /* lower level introduction to the assembly language. */ /* */ /* WARRANTY: */ /* If you don't like it the way it works or if it doesn't work, that's */ /* tough. You're welcome to fix it yourself. That's what you get for */ /* using free software. */ /* */ /* COPYRIGHT NOTICE: */ /* This is free software. There is no fee for using it. You may make */ /* any changes that you wish and also give it away. If you can make */ /* a commercial product out of it, fine, but do not put any limits on */ /* the purchaser's right to do the same. If you improve it or fix any */ /* bugs, it would be nice if you told me and offered me a copy of the */ /* new version. */ /* */ /* */ /* Amendments Record: */ /* Version Date by Comments */ /* ------- ------- --- --------------------------------------------------- */ /* v1.0 12Apr96 GAM Original */ /* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ /* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ /* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ /* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ /* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ /* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ /* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ /* v3.0 14Feb97 RMS MACRO8X features. */ /* v3.1 16Sep01 RMS Bug fixes: */ /* - IFNZRO instead of IFNZERO */ /* - allow blanks after symbol= */ /* - remove field from label values */ /* - don't include NOPUNCH data in checksum */ /* */ /******************************************************************************/ #include #include #include #include #define LINELEN 96 #define LIST_LINES_PER_PAGE 60 /* Includes 5 line page header. */ #define NAMELEN 128 #define SYMBOL_COLUMNS 5 #define SYMLEN 7 #define SYMBOL_TABLE_SIZE 8192 #define MAC_MAX_ARGS 20 /* Must be < 26 */ #define MAC_MAX_LENGTH 8192 #define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ #define TITLELEN 63 #define XREF_COLUMNS 8 #define ADDRESS_FIELD 00177 #define FIELD_FIELD 070000 #define INDIRECT_BIT 00400 #define LAST_PAGE_LOC 00177 #define OP_CODE 07000 #define PAGE_BIT 00200 #ifdef PAGE_SIZE #undef PAGE_SIZE #endif #define PAGE_SIZE 00200 #define PAGE_FIELD 07600 #define PAGE_ZERO_END 00200 #define TOTAL_PAGES (32 * 8) #define GET_PAGE(x) (((x) >> 7) & (TOTAL_PAGES - 1)) /* Macro to get the number of elements in an array. */ #define DIM(a) (sizeof(a)/sizeof(a[0])) /* Macro to get the address plus one of the end of an array. */ #define BEYOND(a) ((a) + DIM(A)) #define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) #define isend(c) ((c=='\0')|| (c=='\n')) #define isdone(c) ((c=='/') || (isend(c)) || (c==';')) /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ /* (true) if the stated condtion is met. */ /* Use these to test attributes. The proper bits are extracted and then */ /* tested. */ #define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) #define M_DEFINED(s) ((s & DEFINED) == DEFINED) #define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) #define M_FIXED(s) ((s & FIXED) == FIXED) #define M_LABEL(s) ((s & LABEL) == LABEL) #define M_MRI(s) ((s & MRI) == MRI) #define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) #define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) #define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) #define M_MACRO(s) ((s & MACRO) == MACRO) #define M_UNDEFINED(s) (!M_DEFINED(s)) #define M_NOTRDEF(s) ((s & NOTRDEF) != 0) /* This macro is used to test symbols by the conditional assembly pseudo-ops. */ #define M_DEF(s) (M_DEFINED(s)) #define M_COND(s) (M_CONDITIONAL(s)) #define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) typedef unsigned char BOOL; typedef unsigned char BYTE; typedef int WORD32; #ifndef FALSE #define FALSE 0 #define TRUE (!FALSE) #endif /* Line listing styles. Used to control listing of lines. */ enum linestyle_t { LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL }; typedef enum linestyle_t LINESTYLE_T; /* Symbol Types. */ /* Note that the names that have FIX as the suffix contain the FIXED bit */ /* included in the value. */ /* */ /* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ /* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ /* defined or undefined. The condition bit is set when the symbol is defined */ /* during pass 1 and reset on pass 2 at the location the symbol was defined */ /* during pass 1. When processing conditionals during pass 2, if the symbol */ /* is defined and the condition bit is set, the symbol is treated as if it */ /* were undefined. This gives consistent behavior of the conditional */ /* pseudo-ops during both pass 1 and pass 2. */ enum symtyp { UNDEFINED = 0000, DEFINED = 0001, FIXED = 0002, MRI = 0004 | DEFINED, LABEL = 0010 | DEFINED, REDEFINED = 0020 | DEFINED, DUPLICATE = 0040 | DEFINED, PSEUDO = 0100 | FIXED | DEFINED, CONDITION = 0200, MACRO = 0400 | DEFINED, MRIFIX = MRI | FIXED | DEFINED, DEFFIX = DEFINED | FIXED, NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED }; typedef enum symtyp SYMTYP; enum pseudo_t { BANK, BINPUNCH, DECIMAL, DEFINE, DUBL, EJECT, ENPUNCH, EXPUNGE, FIELD, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, IFZERO, LGM, LIST, LIT, LITBAS, NOLGM, NOPUNCH, OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, TEXT, TITLE, UNLIST, VFD, ZBLOCK }; typedef enum pseudo_t PSEUDO_T; struct sym_t { SYMTYP type; char name[SYMLEN]; WORD32 val; WORD32 xref_index; WORD32 xref_count; }; typedef struct sym_t SYM_T; struct lpool_t { WORD32 error; /* True if error message has been printed. */ WORD32 pool[PAGE_SIZE]; }; typedef struct lpool_t LPOOL_T; struct emsg_t { char *list; char *file; }; typedef struct emsg_t EMSG_T; struct errsave_t { char *mesg; WORD32 col; }; typedef struct errsave_t ERRSAVE_T; struct fltg_ { WORD32 exponent; WORD32 mantissa; }; typedef struct fltg_ FLTG_T; /*----------------------------------------------------------------------------*/ /* Function Prototypes */ int binarySearch( char *name, int start, int symbol_count ); int copyMacLine( int length, int from, int term, int nargs ); int compareSymbols( const void *a, const void *b ); void conditionFalse( void ); void conditionTrue( void ); SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); void endOfBinary( void ); void errorLexeme( EMSG_T *mesg, WORD32 col ); void errorMessage( EMSG_T *mesg, WORD32 col ); void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); SYM_T *eval( void ); WORD32 evalDubl( WORD32 initial_value ); FLTG_T *evalFltg( void ); SYM_T *evalSymbol( void ); void getArgs( int argc, char *argv[] ); WORD32 getDublExpr( void ); WORD32 getDublExprs( void ); FLTG_T *getFltgExpr( void ); FLTG_T *getFltgExprs( void ); SYM_T *getExpr( void ); WORD32 getExprs( void ); WORD32 incrementClc( void ); void inputDubl( void ); void inputFltg( void ); WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); char *lexemeToName( char *name, WORD32 from, WORD32 term ); void listLine( void ); SYM_T *lookup( char *name ); void moveToEndOfLine( void ); void nextLexBlank( void ); void nextLexeme( void ); void normalizeFltg( FLTG_T *fltg ); void onePass( void ); void printCrossReference( void ); void printErrorMessages( void ); void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); void printPageBreak( void ); void printPermanentSymbolTable( void ); void printSymbolTable( void ); BOOL pseudoOperators( PSEUDO_T val ); void punchChecksum( void ); void punchLocObject( WORD32 loc, WORD32 val ); void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); void punchOutObject( WORD32 loc, WORD32 val ); void punchLeader( WORD32 count ); void punchObject( WORD32 val ); void punchOrigin( WORD32 loc ); void readLine( void ); void saveError( char *mesg, WORD32 cc ); BOOL testForLiteralCollision( WORD32 loc ); BOOL testZeroPool( WORD32 value ); void topOfForm( char *title, char *sub_title ); /*----------------------------------------------------------------------------*/ /* Table of pseudo-ops (directives) which are used to setup the symbol */ /* table on startup and when the EXPUNGE pseudo-op is executed. */ SYM_T pseudo[] = { { PSEUDO, "BANK", BANK }, /* Synonym for field in MACRO8X. */ { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ { PSEUDO, "IFNZRO", IFNZERO }, /* Assemble if symbol value is not 0. */ { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ { PSEUDO, "LGM", LGM }, /* Enable link generation messages. */ { PSEUDO, "LIST", LIST }, /* Enable listing. */ { PSEUDO, "LIT", LIT }, /* Punch literal pool. */ { PSEUDO, "LITBAS", LITBAS }, /* Set literal pool base. */ { PSEUDO, "NOLGM", NOLGM }, /* Disable link generation messages. */ { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ { PSEUDO, "UNLIST", UNLIST }, /* Disable listing. */ { PSEUDO, "VFD", VFD }, /* Variable field definition. */ { PSEUDO, "ZBLOCK", ZBLOCK } /* Zero a block of memory. */ }; /* Symbol Table */ /* The table is put in lexical order on startup, so symbols can be */ /* inserted as desired into the initial table. */ SYM_T permanent_symbols[] = { /* Memory Reference Instructions */ { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ { MRIFIX, "Z", 00000 }, /* PAGE ZERO ADDRESS */ { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ { MRIFIX, "JMP", 05000 }, /* JUMP */ /* Floating Point Interpreter Instructions */ { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ /* Group 1 Operate Microinstrcutions */ { FIXED, "OPR", 07000 }, /* OPERATE */ { FIXED, "NOP", 07000 }, /* NO OPERATION */ { FIXED, "IAC", 07001 }, /* INCREMENT AC */ { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ { FIXED, "CLL", 07100 }, /* CLEAR LINK */ { FIXED, "CLA", 07200 }, /* CLEAR AC */ /* Group 2 Operate Microinstructions */ { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ /* Combined Operate Microinstructions */ { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ { FIXED, "STA", 07240 }, /* SET AC TO -1 */ { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ /* MQ Instructions (PDP/8e) */ { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ /* Program Interrupt */ { FIXED, "IOT", 06000 }, { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ /* Program Interrupt, PDP-8/e */ { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ { FIXED, "CAF", 06007 }, /* Clear all flags. */ /* Keyboard/Reader */ { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG */ { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ /* Teleprinter/Punch */ { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ /* High Speed Paper Tape Reader */ { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ /* PC8-E High Speed Paper Tape Reader & Punch */ { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ /* and fetch character. */ /* High Speed Paper Tape Punch */ { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ /* into AC bits 0-9 */ { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ /* undisturbed */ { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ /* OR from AC. If AC bit 10=0, clear */ /* error flags; if AC bit 11=0, DECtape */ /* control flag is cleared. */ { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ /* control flag is 1 */ { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ /* ORed into AC */ { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ /* register B loaded from AC bits 6-8 */ /* Disk File and Control, Type DF32 */ { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ /* INTERRUPT FLAGS */ { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ /* INTO CORE, CLEAR INTERRUPT FLAG */ { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ /* FROM CORE, CLEAR INTERRUPT FLAG */ { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ /* MEMORY ADDRESS EXTENSION REGISTER */ { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ /* MEMORY ADDRESS EXTENSION REGISTER */ /* AND LOAD SAME FROM AC */ { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ /* ADDRESS REGISTER, SKIP IF ADDRESS */ /* CONFIRMED FLAG = 1 */ { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ /* (NO ERROR) */ { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ /* TRANSFER COMPLETE) */ { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ /* ADDRESS REGISTER */ /* Disk File and Control, Type RF08 */ { FIXED, "DCIM", 06611 }, { FIXED, "DIML", 06615 }, { FIXED, "DIMA", 06616 }, /*{ FIXED, "DISK", 06623 },*/ { FIXED, "DCXA", 06641 }, { FIXED, "DXAL", 06643 }, { FIXED, "DXAC", 06645 }, { FIXED, "DMMT", 06646 }, /* Memory Extension Control, Type 183 */ { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ { FIXED, "RMF", 06244 }, /* RESTORE MEMORY FIELD */ /* Memory Parity, Type MP8/I (MP8/L) */ { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ /* Memory Parity, Type MP8-E (PDP8/e) */ { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ { FIXED, "CEP", 06106 }, /* Check for even parity. */ { FIXED, "SPO", 06107 }, /* Skip on parity option. */ }; /* End-of-Symbols for Permanent Symbol Table */ /* Global variables */ SYM_T *symtab; /* Symbol Table */ int symbol_top; /* Number of entries in symbol table. */ SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ int number_of_fixed_symbols; /*----------------------------------------------------------------------------*/ WORD32 *xreftab; /* Start of the concordance table. */ ERRSAVE_T error_list[20]; int save_error_count; LPOOL_T pz; /* Storage for page zero constants. */ LPOOL_T cp; /* Storage for current page constants. */ WORD32 lit_base[TOTAL_PAGES]; /* Literal base address for all pages. */ WORD32 lit_loc[TOTAL_PAGES]; /* Literal current location for all pages. */ char s_detected[] = "detected"; char s_error[] = "error"; char s_errors[] = "errors"; char s_no[] = "No"; char s_page[] = "Page"; char s_symtable[] = "Symbol Table"; char s_xref[] = "Cross Reference"; /* Assembler diagnostic messages. */ /* Some attempt has been made to keep continuity with the PAL-III and */ /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ /* exists, then the indicator is put in the listing as the first two */ /* characters of the diagnostic message. The PAL-III indicators where used */ /* when there was a choice between using MACRO-8 and PAL-III indicators. */ /* The character pairs and their meanings are: */ /* DT Duplicate Tag (symbol) */ /* IC Illegal Character */ /* ID Illegal Redefinition of a symbol. An attempt was made to give */ /* a symbol a new value not via =. */ /* IE Illegal Equals An equal sign was used in the wrong context, */ /* (e.g., A+B=C, or TAD A+=B) */ /* II Illegal Indirect An off page reference was made, but a literal */ /* could not be generated because the indirect bit was already set. */ /* IR Illegal Reference (address is not on current page or page zero) */ /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ /* RD ReDefintion of a symbol */ /* ST Symbol Table full */ /* UA Undefined Address (undefined symbol) */ /* ZE Zero Page Exceeded (see above, or out of space) */ EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; EMSG_T illegal_character = { "IC illegal char", "illegal character" }; EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; EMSG_T label_syntax = { "IC label syntax", "label syntax" }; EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; EMSG_T illegal_reference = { "IR off page", "illegal reference" }; EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; EMSG_T literal_overflow = { "PE page exceeded", "current page literal capacity exceeded" }; EMSG_T pz_literal_overflow = { "ZE page exceeded", "page zero capacity exceeded" }; EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; EMSG_T illegal_field_value = { "expr out of range", "field value not in range of 0 through 7" }; EMSG_T illegal_vfd_value = { "width out of range", "VFD field width not in range" }; EMSG_T no_literal_value = { "no value", "No literal value" }; EMSG_T text_string = { "no delimiter", "Text string delimiters not matched" }; EMSG_T in_rim_mode = { "not OK in rim mode" "FIELD pseudo-op not valid in RIM mode" }; EMSG_T lt_expected = { "'<' expected", "'<' expected" }; EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; EMSG_T bad_dummy_arg = { "bad dummy arg", "Bad dummy argument following DEFINE" }; EMSG_T macro_too_long = { "macro too long", "Macro too long" }; EMSG_T no_virtual_memory = { "out of memory", "Insufficient memory for macro" }; EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; /*----------------------------------------------------------------------------*/ FILE *errorfile; FILE *infile; FILE *listfile; FILE *listsave; FILE *objectfile; FILE *objectsave; char errorpathname[NAMELEN]; char filename[NAMELEN]; char listpathname[NAMELEN]; char objectpathname[NAMELEN]; char *pathname; char permpathname[NAMELEN]; char mac_buffer[MAC_MAX_LENGTH + 1]; char *mac_bodies[MAC_TABLE_LENGTH]; char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; int mac_arg_pos[26] = { 0 }; int list_lineno; int list_pageno; char list_title[4*LINELEN]; BOOL list_title_set; /* Set if TITLE pseudo-op used. */ char line[4*LINELEN]; /* Input line. */ int lineno; /* Current line number. */ char mac_line[4*LINELEN]; /* Saved macro invocation line. */ int page_lineno; /* print line number on current page. */ WORD32 listed; /* Listed flag. */ WORD32 listedsave; WORD32 cc; /* Column Counter (char position in line). */ WORD32 checksum; /* Generated checksum */ BOOL binary_data_output; /* Set true when data has been output. */ WORD32 clc; /* Location counter */ char delimiter; /* Character immediately after eval'd term. */ int errors; /* Number of errors found so far. */ BOOL error_in_line; /* TRUE if error on current line. */ int errors_pass_1; /* Number of errors on pass 1. */ WORD32 field; /* Current field */ WORD32 fieldlc; /* location counter without field portion. */ int filix_curr; /* Index in argv to current input file. */ int filix_start; /* Start of input files in argv. */ BOOL fltg_input; /* TRUE when doing floating point input. */ BOOL indirect_generated; /* TRUE if an off page address generated. */ WORD32 lexstartprev; /* Where previous lexeme started. */ WORD32 lextermprev; /* Where previous lexeme ended. */ WORD32 lexstart; /* Index of current lexeme on line. */ WORD32 lexterm; /* Index of character after current lexeme. */ WORD32 mac_cc; /* Saved cc after macro invocation. */ WORD32 mac_count; /* Total macros defined. */ BOOL nomac_exp; /* Print macro expansions. */ char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ WORD32 maxcc; /* Current line length. */ BOOL lgm_flag; /* Link generated messages enable flag. */ BOOL overflow; /* Overflow flag for math routines. */ WORD32 pass; /* Number of current pass. */ BOOL print_permanent_symbols; WORD32 radix; /* Default number radix. */ WORD32 reloc; /* The relocation distance. */ BOOL rim_mode; /* Generate rim format, defaults to bin */ int save_argc; /* Saved argc. */ char **save_argv; /* Saved *argv[]. */ BOOL symtab_print; /* Print symbol table flag */ BOOL xref; FLTG_T fltg_ac; /* Value holder for evalFltg() */ SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ /******************************************************************************/ /* */ /* Function: main */ /* */ /* Synopsis: Starting point. Controls order of assembly. */ /* */ /******************************************************************************/ int main( int argc, char *argv[] ) { int ix; int space; save_argc = argc; save_argv = argv; /* Set the default values for global symbols. */ binary_data_output = FALSE; fltg_input = FALSE; nomac_exp = TRUE; print_permanent_symbols = FALSE; rim_mode = FALSE; symtab_print = FALSE; xref = FALSE; pathname = NULL; for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) { mac_bodies[ix] = NULL; } /* Get the options and pathnames */ getArgs( argc, argv ); /* Setup the error file in case symbol table overflows while installing the */ /* permanent symbols. */ errorfile = fopen( errorpathname, "w" ); errors = 0; save_error_count = 0; pass = 0; /* This is required for symbol table initialization. */ symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); if( symtab == NULL ) { fprintf( stderr, "Could not allocate memory for symbol table.\n"); exit( -1 ); } /* Place end marker in symbol table. */ symtab[0] = sym_undefined; symbol_top = 0; number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Enter the pseudo-ops into the symbol table */ for( ix = 0; ix < DIM( pseudo ); ix++ ) { defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); } /* Enter the predefined symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) { defineSymbol( permanent_symbols[ix].name, permanent_symbols[ix].val, permanent_symbols[ix].type | DEFFIX , 0 ); } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Do pass one of the assembly */ checksum = 0; pass = 1; onePass(); errors_pass_1 = errors; fclose ( errorfile ); /* Set up for pass two */ errorfile = fopen( errorpathname, "w" ); objectfile = fopen( objectpathname, "wb" ); objectsave = objectfile; listfile = fopen( listpathname, "w" ); listsave = listfile; punchLeader( 0 ); checksum = 0; /* Do pass two of the assembly */ errors = 0; save_error_count = 0; if( xref ) { /* Get the amount of space that will be required for the concordance. */ for( space = 0, ix = 0; ix < symbol_top; ix++ ) { symtab[ix].xref_index = space; /* Index into concordance table. */ space += symtab[ix].xref_count + 1; symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ } /* Allocate the necessary space. */ xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); /* Clear the cross reference space. */ for( ix = 0; ix < space; ix++ ) { xreftab[ix] = 0; } } pass = 2; onePass(); /* Undo effects of NOPUNCH for any following checksum */ objectfile = objectsave; punchChecksum(); /* Works great for trailer. */ punchLeader( 1 ); /* undo effects of NOLIST for any following output to listing file. */ listfile = listsave; /* Display value of error counter. */ if( errors == 0 ) { fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); } else { fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( listfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( stderr, " %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); } if( symtab_print ) { printSymbolTable(); } if( print_permanent_symbols ) { printPermanentSymbolTable(); } if( xref ) { printCrossReference(); } fclose( objectfile ); fclose( listfile ); fclose( errorfile ); if( errors == 0 && errors_pass_1 == 0 ) { remove( errorpathname ); } return( errors != 0 ); } /* main() */ /******************************************************************************/ /* */ /* Function: getArgs */ /* */ /* Synopsis: Parse command line, set flags accordingly and setup input and */ /* output files. */ /* */ /******************************************************************************/ void getArgs( int argc, char *argv[] ) { WORD32 len; WORD32 ix, jx; /* Set the defaults */ errorfile = NULL; infile = NULL; listfile = NULL; listsave = NULL; objectfile = NULL; objectsave = NULL; for( ix = 1; ix < argc; ix++ ) { if( argv[ix][0] == '-' ) { for( jx = 1; argv[ix][jx] != 0; jx++ ) { switch( argv[ix][jx] ) { case 'd': symtab_print = TRUE; break; case 'm': nomac_exp = FALSE; break; case 'r': rim_mode = TRUE; break; case 'p': print_permanent_symbols = TRUE; break; case 'x': xref = TRUE; break; default: fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); fprintf( stderr, " -d -- dump symbol table\n" ); fprintf( stderr, " -m -- print macro expansions\n" ); fprintf( stderr, " -r -- output rim format file\n" ); fprintf( stderr, " -p -- output permanent symbols to file\n" ); fprintf( stderr, " -x -- output cross reference to file\n" ); fflush( stderr ); exit( -1 ); } /* end switch */ } /* end for */ } else { filix_start = ix; pathname = argv[ix]; break; } } /* end for */ if( pathname == NULL ) { fprintf( stderr, "%s: no input file specified\n", argv[0] ); exit( -1 ); } len = strlen( pathname ); if( len > NAMELEN - 5 ) { fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); exit( -1 ); } /* Now make the pathnames */ /* Find last '.', if it exists. */ jx = len - 1; while( pathname[jx] != '.' && pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } switch( pathname[jx] ) { case '.': break; case '/': case '\\': jx = len; break; default: break; } /* Add the pathname extensions. */ strncpy( objectpathname, pathname, jx ); objectpathname[jx] = '\0'; strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); strncpy( listpathname, pathname, jx ); listpathname[jx] = '\0'; strcat( listpathname, ".lst" ); strncpy( errorpathname, pathname, jx ); errorpathname[jx] = '\0'; strcat( errorpathname, ".err" ); strncpy( permpathname, pathname, jx ); permpathname[jx] = '\0'; strcat( permpathname, ".prm" ); /* Extract the filename from the path. */ if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) { pathname[1] = '\\'; /* MS-DOS style pathname */ } jx = len - 1; while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } strcpy( filename, &pathname[jx + 1] ); } /* getArgs() */ /******************************************************************************/ /* */ /* Function: onePass */ /* */ /* Synopsis: Do one assembly pass. */ /* */ /******************************************************************************/ void onePass() { BOOL blanks; int ix; int jx; char name[SYMLEN]; WORD32 newclc; BOOL scanning_line; WORD32 start; SYM_T *sym; WORD32 term; WORD32 val; clc = 0200; /* Default starting address is 200 octal. */ field = 0; fieldlc = 0200; reloc = 0; for( ix = 0; ix < TOTAL_PAGES; ix++ ) { lit_loc[ix] = lit_base[ix] = 00200; } mac_count = 0; /* No macros defined. */ mac_ptr = NULL; /* Not in a macro. */ for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) { if ( mac_bodies[ix] ) { free( mac_bodies[ix] ); } } cp.error = FALSE; pz.error = FALSE; listed = TRUE; lgm_flag = TRUE; lineno = 0; list_pageno = 0; list_lineno = 0; list_title_set = FALSE; page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ radix = 8; /* Initial radix is octal (base 8). */ /* Now open the first input file. */ filix_curr = filix_start; /* Initialize pointer to input files. */ if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } while( TRUE ) { readLine(); nextLexeme(); scanning_line = TRUE; while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '$': endOfBinary(); fclose( infile ); return; case '*': nextLexeme(); /* Skip '*', (set origin symbol) */ newclc = ((getExpr())->val & 07777 ) | field; /* Do not change Current Location Counter if an error occurred. */ if( !error_in_line ) { if(( newclc & 07600 ) != ( clc & 07600 )) { /* Current page has changed. */ punchLiteralPool( &cp, clc - 1 ); } clc = newclc - reloc; fieldlc = clc & 07777; if( !rim_mode ) { /* Not rim mode, put out origin. */ punchOrigin( clc ); } printLine( line, 0, fieldlc, LINE_VAL ); } break; default: switch( line[lexterm] ) { case ',': if( isalpha( line[lexstart] )) { /* Use lookup so symbol will not be counted as reference. */ sym = lookup( lexemeToName( name, lexstart, lexterm )); if( M_DEFINED( sym->type )) { if( sym->val != (clc & 07777) && pass == 2 ) { errorSymbol( &duplicate_label, sym->name, lexstart ); } sym->type = sym->type | DUPLICATE; } /* Must call define on pass 2 to generate concordance. */ defineLexeme( lexstart, lexterm, ( clc+reloc ), LABEL ); } else { errorLexeme( &label_syntax, lexstart ); } nextLexeme(); /* skip label */ nextLexeme(); /* skip comma */ break; case '=': if( isalpha( line[lexstart] )) { start = lexstart; term = lexterm; delimiter = line[lexterm]; nextLexBlank(); /* skip symbol */ nextLexeme(); /* skip trailing = */ val = getExprs(); defineLexeme( start, term, val, DEFINED ); printLine( line, 0, val, LINE_VAL ); } else { errorLexeme( &symbol_syntax, lexstartprev ); nextLexeme(); /* skip symbol */ nextLexeme(); /* skip trailing = */ getExprs(); /* skip expression */ } break; default: if( isalpha( line[lexstart] )) { sym = evalSymbol(); val = sym->val; if( M_MACRO( sym->type )) { /* Find arguments. */ blanks = TRUE; /* Expecting blanks. */ for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) { if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; else if( blanks ) { mac_arg_pos[jx++] = cc; blanks = FALSE; } } /* end for */ for( ; jx < MAC_MAX_ARGS; jx++ ) { mac_arg_pos[jx] = 0; } for( jx = 0; jx < LINELEN; jx++ ) { mac_line[jx] = line[jx]; } mac_cc = cc; /* Save line and position in line. */ mac_ptr = mac_bodies[val]; if( mac_ptr ) scanning_line = FALSE; else nextLexeme(); } /* end if macro */ else if( M_PSEUDO( sym->type )) { nextLexeme(); /* Skip symbol */ scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); } else { /* Identifier is not a pseudo-op, interpret as load value */ punchOutObject( clc, getExprs() & 07777 ); incrementClc(); } } else { /* Identifier is a value, interpret as load value */ punchOutObject( clc, getExprs() & 07777 ); incrementClc(); } break; } /* end switch */ break; } /* end switch */ } /* end if */ } /* end while( scanning_line ) */ } /* end while( TRUE ) */ } /* onePass() */ /******************************************************************************/ /* */ /* Function: getExprs */ /* */ /* Synopsis: Or together a list of blank separated expressions, from the */ /* current lexeme onward. Leave the current lexeme as */ /* the last one in the list. */ /* */ /******************************************************************************/ WORD32 getExprs() { SYM_T *symv; SYM_T *symt; WORD32 temp; SYMTYP temp_type; WORD32 value; SYMTYP value_type; symv = getExpr(); value = symv->val; value_type = symv->type; while( TRUE ) { if( isdone( line[lexstart] )) { return( value ); } switch( line[lexstart] ) { case ')': case ']': return( value ); default: break; } /* Interpret space as logical or */ symt = getExpr(); temp = symt->val & 07777; temp_type = symt->type; switch( value_type ) { case MRI: case MRIFIX: /* Previous symbol was a Memory Reference Instruction. */ switch( temp_type ) { case MRI: case MRIFIX: /* Current symbol is also a Memory Reference Instruction. */ value |= temp; /* Just OR the MRI instructions. */ break; default: /* Now have the address part of the MRI instruction. */ if( temp < 00200 ) { value |= temp; /* Page zero MRI. */ } else if( (( fieldlc + reloc ) & 07600 ) <= temp && temp <= (( fieldlc + reloc ) | 0177 )) { value |= ( PAGE_BIT | (temp & ADDRESS_FIELD )); /* Current page MRI */ } else { if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) { /* Already indirect, can't generate */ errorSymbol( &illegal_indirect, symt->name, lexstartprev ); } else { /* Now fix off page reference. */ /* Search current page literal pool for needed value. */ /* Set Indirect Current Page */ if( testZeroPool( temp )) { value |= ( 00400 | insertLiteral( &pz, field, temp )); } else { value |= ( 00600 | insertLiteral( &cp, clc, temp )); } indirect_generated = TRUE; } } break; } break; default: value |= temp; /* Normal 12 bit value. */ break; } } /* end while */ } /* getExprs() */ /******************************************************************************/ /* */ /* Function: getExpr */ /* */ /* Synopsis: Get an expression, from the current lexeme onward, leave the */ /* current lexeme as the one after the expression. Expressions */ /* contain terminal symbols (identifiers) separated by operators. */ /* */ /******************************************************************************/ SYM_T *getExpr() { delimiter = line[lexterm]; if( line[lexstart] == '-' ) { nextLexBlank(); sym_getexpr = *(eval()); sym_getexpr.val = ( - sym_getexpr.val ); } else { sym_getexpr = *(eval()); } if( is_blank( delimiter )) { return( &sym_getexpr ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '+': /* add */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val += (eval())->val; break; case '-': /* subtract */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val -= (eval())->val; break; case '^': /* multiply */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val *= (eval())->val; break; case '%': /* divide */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val /= (eval())->val; break; case '&': /* and */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val &= (eval())->val; break; case '!': /* or */ nextLexBlank(); /* skip over the operator */ sym_getexpr.val |= (eval())->val; break; default: if( isend( line[lexstart] )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '/': case ';': case ')': case ']': case '<': case ':': case ',': break; case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; } return( &sym_getexpr ); } } /* end while */ } /* getExpr() */ /******************************************************************************/ /* */ /* Function: eval */ /* */ /* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ /* */ /******************************************************************************/ SYM_T *eval() { WORD32 digit; WORD32 from; WORD32 loc; SYM_T *sym; WORD32 val; val = 0; delimiter = line[lexterm]; if( isalpha( line[lexstart] )) { sym = evalSymbol(); if( M_UNDEFINED( sym->type )) { if( pass == 2 ) { errorSymbol( &undefined_symbol, sym->name, lexstart ); } nextLexeme(); return( sym ); } else if( M_PSEUDO( sym->type )) { if( sym->val == DECIMAL ) { radix = 10; } else if( sym->val == OCTAL ) { radix = 8; } else if( pass == 2 ) { errorSymbol( &misplaced_symbol, sym->name, lexstart ); } sym_eval.type = sym->type; sym_eval.val = 0; nextLexeme(); return( &sym_eval ); } else if( M_MACRO( sym->type )) { if( pass == 2 ) { errorSymbol( &misplaced_symbol, sym->name, lexstart ); } sym_eval.type = sym->type; sym_eval.val = 0; nextLexeme(); return( &sym_eval ); } else { nextLexeme(); return( sym ); } } else if( isdigit( line[lexstart] )) { from = lexstart; val = 0; while( from < lexterm ) { if( isdigit( line[from] )) { digit = (WORD32) line[from++] - (WORD32) '0'; if( digit < radix ) { val = val * radix + digit; } else { errorLexeme( &number_not_radix, from - 1 ); val = 0; from = lexterm; } } else { errorLexeme( ¬_a_number, lexstart ); val = 0; from = lexterm; } } nextLexeme(); sym_eval.val = val; return( &sym_eval ); } else { switch( line[lexstart] ) { case '"': /* Character literal */ if( lexstart + 2 < maxcc ) { val = line[lexstart + 1] | 0200; delimiter = line[lexstart + 2]; cc = lexstart + 2; } else { errorMessage( &no_literal_value, lexstart ); } nextLexeme(); break; case '.': /* Value of Current Location Counter */ val = clc + reloc; nextLexeme(); break; case '[': /* Generate literal on page zero. */ nextLexBlank(); /* Skip bracket */ val = getExprs() & 07777; if( line[lexstart] == ']' ) { delimiter = line[lexterm]; nextLexeme(); /* Skip end bracket */ } else { /* errorMessage( "parens", lexstart ); */ } sym_eval.val = insertLiteral( &pz, field, val ); return( &sym_eval ); case '(': /* Generate literal on current page. */ nextLexBlank(); /* Skip paren */ val = getExprs() & 07777; if( line[lexstart] == ')' ) { delimiter = line[lexterm]; nextLexeme(); /* Skip end paren */ } else { /* errorMessage( "parens", NULL ); */ } if( testZeroPool( val )) { sym_eval.val = insertLiteral( &pz, field, val ); } else { loc = insertLiteral( &cp, clc, val ); sym_eval.val = loc + (( clc + reloc ) & 077600 ); } return( &sym_eval ); default: switch( line[lexstart] ) { case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); break; default: errorMessage( &illegal_character, lexstart ); break; } val = 0; /* On error, set value to zero. */ nextLexBlank(); /* Go past illegal character. */ } } sym_eval.val = val; return( &sym_eval ); } /* eval() */ /******************************************************************************/ /* */ /* Function: inputDubl */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. */ /* */ /******************************************************************************/ void inputDubl() { WORD32 dublvalue; BOOL scanning_line; scanning_line = TRUE; do { while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '+': delimiter = line[lexterm]; nextLexBlank(); case '-': default: if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) { dublvalue = getDublExprs(); punchOutObject( clc, (WORD32)(( dublvalue >> 12 ) & 07777 )); incrementClc(); punchOutObject( clc, (WORD32)( dublvalue & 07777 )); incrementClc(); } else { return; /* Non-numeric input, back to assembly. */ } break; } /* end switch */ } /* end if */ if( error_in_line ) { return; /* Error occurred, exit DUBL input mode. */ } } /* end while( scanning_line ) */ readLine(); nextLexeme(); scanning_line = TRUE; } while( TRUE ); } /* inputDubl() */ /******************************************************************************/ /* */ /* Function: getDublExprs */ /* */ /* Synopsis: Get a DUBL expression. */ /* */ /******************************************************************************/ WORD32 getDublExprs() { WORD32 dublvalue; dublvalue = getDublExpr(); while( TRUE ) { if( isdone( line[lexstart] )) { return( dublvalue ); } else { errorMessage( &illegal_expression, lexstart - 1 ); return( 0 ); } } /* end while */ } /* getDublExprs() */ /******************************************************************************/ /* */ /* Function: getDublExpr */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ WORD32 getDublExpr() { WORD32 dublvalue; delimiter = line[lexterm]; if( line[lexstart] == '-' ) { nextLexBlank(); dublvalue = evalDubl( 0 ); nextLexeme(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)dublvalue > 040000000L ) { errorMessage( &dubl_overflow, lexstart ); dublvalue = 0; } dublvalue = -dublvalue; } else { dublvalue = evalDubl( 0 ); nextLexeme(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)dublvalue > 037777777L ) { errorMessage( &dubl_overflow, lexstart ); dublvalue = 0; } } if( is_blank( delimiter )) { return( dublvalue ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; return( dublvalue ); } switch( line[lexstart] ) { case '+': /* add */ case '-': /* subtract */ case '^': /* multiply */ case '%': /* divide */ case '&': /* and */ case '!': /* or */ errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; break; default: if( isend( line[lexstart] )) { return( dublvalue ); } switch( line[lexstart] ) { case '/': case ';': break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; break; } return( dublvalue ); } } /* end while */ } /* getDublExpr() */ /******************************************************************************/ /* */ /* Function: evalDubl */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ WORD32 evalDubl( WORD32 initial_value ) { WORD32 digit; WORD32 from; WORD32 dublvalue; WORD32 olddublvalue; overflow = FALSE; delimiter = line[lexterm]; from = lexstart; dublvalue = initial_value; while( from < lexterm ) { if( isdigit( line[from] )) { olddublvalue = dublvalue; digit = (WORD32)( line[from++] - '0' ); dublvalue = dublvalue * 10 + digit; if( dublvalue < olddublvalue ) { overflow = TRUE; } } else { errorLexeme( ¬_a_number, from ); dublvalue = 0; from = lexterm; } } return( dublvalue ); } /* evalDubl() */ /******************************************************************************/ /* */ /* Function: inputFltg */ /* */ /* Synopsis: Get the value of the current lexeme as a Floating Point const. */ /* */ /******************************************************************************/ void inputFltg() { FLTG_T *fltg; BOOL scanning_line; fltg_input = TRUE; /* Set lexeme scanner for floating point. */ scanning_line = TRUE; while( TRUE ) { while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '+': delimiter = line[lexterm]; nextLexBlank(); case '-': default: if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) { fltg = getFltgExprs(); punchOutObject( clc, ( fltg->exponent & 07777 )); incrementClc(); punchOutObject( clc, (WORD32)(( fltg->mantissa >> 12 ) & 07777 )); incrementClc(); punchOutObject( clc, (WORD32)( fltg->mantissa & 07777 )); incrementClc(); } else { fltg_input = FALSE; /* Reset lexeme scanner. */ return; /* Non-numeric input, back to assembly. */ } break; } /* end switch */ } /* end if */ if( error_in_line ) { fltg_input = FALSE; /* Reset lexeme scanner. */ return; /* Error occurred, exit FLTG input mode. */ } } /* end while( scanning_line ) */ readLine(); nextLexeme(); scanning_line = TRUE; } } /* inputFltg() */ /******************************************************************************/ /* */ /* Function: getFltgExprs */ /* */ /* Synopsis: Get a FLTG expression. */ /* */ /******************************************************************************/ FLTG_T *getFltgExprs() { FLTG_T *fltg; fltg = getFltgExpr(); while( TRUE ) { if( isdone( line[lexstart] )) { return( fltg ); } else { errorMessage( &illegal_expression, lexstart - 1 ); return( 0 ); } } /* end while */ } /* getFltgExprs() */ /******************************************************************************/ /* */ /* Function: getFltgExpr */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ FLTG_T *getFltgExpr() { FLTG_T *fltg; delimiter = line[lexterm]; fltg = evalFltg(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)fltg->mantissa> 077777777L ) { errorMessage( &fltg_overflow, lexstart ); } if( is_blank( delimiter )) { return( fltg ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = 0; return( fltg ); } switch( line[lexstart] ) { case '+': /* add */ case '-': /* subtract */ case '^': /* multiply */ case '%': /* divide */ case '&': /* and */ case '!': /* or */ errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = NULL; break; default: if( isend( line[lexstart] )) { return( fltg ); } switch( line[lexstart] ) { case '/': case ';': break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = NULL; break; } return( fltg ); } } /* end while */ } /* getFltgExpr() */ /******************************************************************************/ /* */ /* Function: evalFltg */ /* */ /* Synopsis: Get the value of the current lexeme as a floating point value. */ /* Floating point input is alwasy considered decimal. */ /* The general format of a floating point number is: */ /* +-ddd.dddE+-dd where each d is a decimal digit. */ /* */ /******************************************************************************/ FLTG_T *evalFltg() { BYTE current_state; BYTE current_col; WORD32 exponent; FLTG_T *fltg; WORD32 input_value; BOOL negate; BOOL negate_exponent; BYTE next_state; int right_digits; /* This uses a lexical analyzer to parse the floating point format. */ static BYTE state_table[][10] = { /* 0 1 2 3 4 5 6 Oolumn index */ /* + - d . E sp p State Comment */ { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ /* 10 Completion state */ /* 11 Error state. */ }; delimiter = line[lexterm]; fltg = &fltg_ac; fltg->exponent = 0; fltg->mantissa = 0; input_value = 0; negate = FALSE; negate_exponent = FALSE; exponent = 0; right_digits = 0; current_state = 0; while( TRUE ) { /* Classify character. This is the column index. */ switch( line[lexstart] ) { case '+': current_col = 0; break; case '-': current_col = 1; break; case '.': current_col = 3; break; case 'E': case 'e': current_col = 4; break; default: if( isdigit( line[lexstart] )) { current_col = 2; } else if( isdone( line[lexstart] )) { current_col = 5; } else { current_col = 6; } break; } next_state = state_table[current_state][current_col]; switch( next_state ) { case 1: /* - */ negate = TRUE; case 2: /* + */ delimiter = line[lexterm]; /* Move past the + or - character. */ nextLexBlank(); break; case 3: /* Number (+-ddd) */ input_value = evalDubl( 0 ); /* Integer part of the number. */ nextLexeme(); /* Move past previous lexeme. */ break; case 4: delimiter = line[lexterm]; nextLexBlank(); /* Move past the . character. */ break; case 5: /* . (+-ddd.ddd) */ /* Fractional part of the number. */ input_value = evalDubl( input_value ); right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ nextLexeme(); /* Move past previous lexeme. */ break; case 6: /* E (+-ddd.dddE) */ delimiter = line[lexterm]; /* Move past the E. */ nextLexBlank(); break; case 7: /* - (+-ddd.dddE-) */ negate_exponent = TRUE; case 8: /* + (+-ddd.dddE+) */ delimiter = line[lexterm]; /* Move past the + or - character. */ nextLexBlank(); break; case 9: /* # (+-ddd.dddE+-dd) */ exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ if( negate_exponent ) { exponent = - exponent; } nextLexeme(); /* Move past previous lexeme. */ break; case 10: /* Floating number parsed, convert */ /* the number. */ /* Get the exponent for the number as input. */ exponent -= right_digits; /* Remove trailing zeros and adjust the exponent accordingly. */ while(( input_value % 10 ) == 0 ) { input_value /= 10; exponent++; } /* Convert the number to floating point. The number is calculated with */ /* a 27 bit mantissa to improve precision. The extra 3 bits are */ /* discarded after the result has been calculated. */ fltg->exponent = 26; fltg->mantissa = input_value << 3; normalizeFltg( fltg ); while( exponent != 0 ) { if( exponent < 0 ) { /* Decimal point is to the left. */ fltg->mantissa /= 10; normalizeFltg( fltg ); exponent++; } else if( exponent > 0 ) { /* Decimal point is to the right. */ fltg->mantissa *= 10; normalizeFltg( fltg ); exponent--; } } /* Discard the extra precsion used for calculating the number. */ fltg->mantissa >>= 3; fltg->exponent -= 3; if( negate ) { fltg->mantissa = (- fltg->mantissa ) & 077777777L; } return( fltg ); case 11: /* Error in format. */ /* Not a properly constructued floating point number. */ return( fltg ); default: break; } /* Set state for next pass through the loop. */ current_state = next_state; } } /* evalFltg() */ /******************************************************************************/ /* */ /* Function: normalizeFltg */ /* */ /* Synopsis: Normalize a PDP-8 double precision floating point number. */ /* */ /******************************************************************************/ void normalizeFltg( FLTG_T *fltg ) { /* Normalize the floating point number. */ if( fltg->mantissa != 0 ) { if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) { while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) { fltg->mantissa <<= 1; fltg->exponent--; } } else { while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) { fltg->mantissa >>= 1; fltg->exponent++; } } } else { fltg->exponent = 0; } return; } /******************************************************************************/ /* */ /* Function: incrementClc */ /* */ /* Synopsis: Set the next assembly location. Test for collision with */ /* the literal tables. */ /* */ /******************************************************************************/ WORD32 incrementClc() { testForLiteralCollision( clc ); /* Incrementing the location counter is not to change field setting. */ clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); fieldlc = clc & 07777; return( clc ); } /* incrementClc() */ /******************************************************************************/ /* */ /* Function: testForLiteralCollision */ /* */ /* Synopsis: Test the given location for collision with the literal tables. */ /* */ /******************************************************************************/ BOOL testForLiteralCollision( WORD32 loc ) { WORD32 pagelc; WORD32 pageno; BOOL result = FALSE; pageno = GET_PAGE (loc); pagelc = loc & 00177; if( pageno == 0 ) { if( pagelc >= lit_loc[pageno] && !pz.error ) { errorMessage( &pz_literal_overflow, -1 ); pz.error = TRUE; result = TRUE; } } else { if( pagelc >= lit_loc[pageno] && !cp.error ) { errorMessage( &literal_overflow, -1 ); cp.error = TRUE; result = TRUE; } } return( result ); } /* testForLiteralCollision() */ /******************************************************************************/ /* */ /* Function: readLine */ /* */ /* Synopsis: Get next line of input. Print previous line if needed. */ /* */ /******************************************************************************/ void readLine() { BOOL ffseen; WORD32 ix; WORD32 iy; char mc; char inpline[4*LINELEN]; listLine(); /* List previous line if needed. */ indirect_generated = FALSE; /* Mark no indirect address generated. */ error_in_line = FALSE; /* No error in line. */ if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ { mac_ptr = NULL; for( ix = 0; ix < LINELEN; ix++ ) { line[ix] = mac_line[ix]; /* Restore invoking line. */ } cc = lexstartprev = mac_cc; /* Restore cc. */ maxcc = strlen( line ); /* Restore maxcc. */ listed = TRUE; /* Already listed. */ return; } cc = 0; /* Initialize column counter. */ lexstartprev = 0; if( mac_ptr ) /* Inside macro? */ { maxcc = 0; do { mc = *mac_ptr++; /* Next character. */ if( islower( mc )) /* Encoded argument number? */ { ix = mc - 'a'; /* Convert to index. */ if( iy = mac_arg_pos[ix] ) { do /* Copy argument string. */ { line[maxcc++] = mac_line[iy++]; } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && ( !isend( mac_line[iy] ))); } } else /* Ordinary character, just copy. */ { line[maxcc++] = mc; } } while( !isend( mc )); line[maxcc] = '\0'; listed = nomac_exp; return; } lineno++; /* Count lines read. */ listed = FALSE; /* Mark as not listed. */ READ_LINE: if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { filix_curr++; /* Advance to next file. */ if( filix_curr < save_argc ) /* More files? */ { fclose( infile ); if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); exit( -1 ); } goto READ_LINE; } else { inpline[0] = '$'; inpline[1] = '\n'; inpline[2] = '\0'; } } /* Remove any tabs from the input line by inserting the required number */ /* of spaces to simulate 8 character tab stops. */ ffseen = FALSE; for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { switch( inpline[ix] ) { case '\t': do { line[iy] = ' '; iy++; } while(( iy % 8 ) != 0 ); break; case '\f': if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); ffseen = TRUE; break; default: line[iy] = inpline[ix]; iy++; break; } } line[iy] = '\0'; /* If the line is terminated by CR-LF, remove, the CR. */ if( line[iy - 2] == '\r' ) { iy--; line[iy - 1] = line[iy - 0]; line[iy] = '\0'; } maxcc = iy; /* Save the current line length. */ } /* readLine() */ /******************************************************************************/ /* */ /* Function: listLine */ /* */ /* Synopsis: Output a line to the listing file. */ /* */ /******************************************************************************/ void listLine() /* generate a line of listing if not already done! */ { if( listfile != NULL && listed == FALSE ) { printLine( line, 0, 0, LINE ); } } /* listLine() */ /******************************************************************************/ /* */ /* Function: printPageBreak */ /* */ /* Synopsis: Output a Top of Form and listing header if new page necessary. */ /* */ /******************************************************************************/ void printPageBreak() { if( page_lineno >= LIST_LINES_PER_PAGE ) /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ { if( !list_title_set ) { strcpy( list_title, line ); if( list_title[strlen(list_title) - 1] == '\n' ) { list_title[strlen(list_title) - 1] = '\0'; } if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } list_title_set = TRUE; } topOfForm( list_title, NULL ); } } /* printPageBreak() */ /******************************************************************************/ /* */ /* Function: printLine */ /* */ /* Synopsis: Output a line to the listing file with new page if necessary. */ /* */ /******************************************************************************/ void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) { if( listfile == NULL ) { save_error_count = 0; return; } printPageBreak(); list_lineno++; page_lineno++; switch( linestyle ) { default: case LINE: fprintf( listfile, "%5d ", lineno ); fputs( line, listfile ); listed = TRUE; break; case LINE_VAL: if( !listed ) { fprintf( listfile, "%5d %4.4o ", lineno, val ); fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %4.4o\n", val ); } break; case LINE_LOC_VAL: if( !listed ) { if( indirect_generated && lgm_flag ) { fprintf( listfile, "%5d %5.5o %4.4o@ ", lineno, loc, val ); } else { fprintf( listfile, "%5d %5.5o %4.4o ", lineno, loc, val ); } fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %5.5o %4.4o\n", loc, val ); } break; case LOC_VAL: fprintf( listfile, " %5.5o %4.4o\n", loc, val ); break; } printErrorMessages(); } /* printLine() */ /******************************************************************************/ /* */ /* Function: printErrorMessages */ /* */ /* Synopsis: Output any error messages from the current list of errors. */ /* */ /******************************************************************************/ void printErrorMessages() { WORD32 ix; WORD32 iy; if( listfile != NULL ) { /* If any errors, display them now. */ for( iy = 0; iy < save_error_count; iy++ ) { printPageBreak(); fprintf( listfile, "%-18.18s", error_list[iy].mesg ); if( error_list[iy].col >= 0 ) { for( ix = 0; ix < error_list[iy].col; ix++ ) { if( line[ix] == '\t' ) { putc( '\t', listfile ); } else { putc( ' ', listfile ); } } fputs( "^", listfile ); list_lineno++; page_lineno++; } fputs( "\n", listfile ); } } save_error_count = 0; } /* printErrorMessages() */ /******************************************************************************/ /* */ /* Function: endOfBinary */ /* */ /* Synopsis: Outputs both literal tables at the end of a binary segment. */ /* */ /******************************************************************************/ void endOfBinary() { /* Points to end of page for () operands. */ punchLiteralPool( &cp, clc - 1 ); /* Points to end of page zero for [] operands. */ punchLiteralPool( &pz, field ); if( error_in_line ) listLine(); /* List line if not done yet. */ return; } /* endOfBinary() */ /******************************************************************************/ /* */ /* Function: punchChecksum */ /* */ /* Synopsis: Output a checksum if the current mode requires it and an */ /* object file exists. */ /* */ /******************************************************************************/ void punchChecksum() { /* If the assembler has output any BIN data output the checksum. */ if( binary_data_output && !rim_mode ) { punchLocObject( 0, checksum ); } binary_data_output = FALSE; checksum = 0; } /* punchChecksum() */ /******************************************************************************/ /* */ /* Function: punchLeader */ /* */ /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ /* documentation. Paper tape has 10 punches per inch. */ /* */ /******************************************************************************/ void punchLeader( WORD32 count ) { WORD32 ix; /* If value is zero, set to the default of 2 feet of leader. */ count = ( count == 0 ) ? 240 : count; if( objectfile != NULL ) { for( ix = 0; ix < count; ix++ ) { fputc( 0200, objectfile ); } } } /* punchLeader() */ /******************************************************************************/ /* */ /* Function: punchOrigin */ /* */ /* Synopsis: Output an origin to the object file. */ /* */ /******************************************************************************/ void punchOrigin( WORD32 loc ) { punchObject((( loc >> 6 ) & 0077 ) | 0100 ); punchObject( loc & 0077 ); } /* punchOrigin() */ /******************************************************************************/ /* */ /* Function: punchObject */ /* */ /* Synopsis: Put one character to object file and include it in checksum. */ /* */ /******************************************************************************/ void punchObject( WORD32 val ) { val &= 0377; if( objectfile != NULL ) { fputc( val, objectfile ); checksum += val; } binary_data_output = TRUE; } /* punchObject() */ /******************************************************************************/ /* */ /* Function: punchOutObject */ /* */ /* Synopsis: Output the current line and then then punch value to the */ /* object file. */ /* */ /******************************************************************************/ void punchOutObject( WORD32 loc, WORD32 val ) { printLine( line, ( field | loc ), val, LINE_LOC_VAL ); punchLocObject( loc, val ); } /* punchOutObject() */ /******************************************************************************/ /* */ /* Function: punchLocObject */ /* */ /* Synopsis: Output the word (with origin if rim format) to the object file.*/ /* */ /******************************************************************************/ void punchLocObject( WORD32 loc, WORD32 val ) { if( rim_mode ) { punchOrigin( loc ); } punchObject(( val >> 6 ) & 0077 ); punchObject( val & 0077 ); } /* punchLocObject() */ /******************************************************************************/ /* */ /* Function: punchLiteralPool */ /* */ /* Synopsis: Output the current page data. */ /* */ /******************************************************************************/ void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) { WORD32 loc; WORD32 pageno; WORD32 tmplc; pageno = GET_PAGE (lpool_page); lpool_page &= 07600; if(( lpool_page == 0 ) && ( p != &pz )) { return; /* Don't punch page 0 pool if not asked. */ } if( lit_loc[pageno] < lit_base[pageno] ) { if( !rim_mode ) { /* Put out origin if not in rim mode. */ punchOrigin( lit_loc[pageno] | lpool_page ); } /* Put the literals in the object file. */ for( loc = lit_loc[pageno]; loc < lit_base[pageno]; loc++ ) { tmplc = loc + lpool_page; printLine( line, (field | tmplc), p->pool[loc], LOC_VAL ); punchLocObject( tmplc, p->pool[loc] ); } p->error = FALSE; lit_base[pageno] = lit_loc[pageno]; } } /* punchLiteralPool() */ /******************************************************************************/ /* */ /* Function: insertLiteral */ /* */ /* Synopsis: Add a value to the given literal pool if not already in pool. */ /* Return the location of the value in the pool. */ /* */ /******************************************************************************/ WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ) { WORD32 ix; WORD32 pageno; LPOOL_T *p; p = pool; pageno = GET_PAGE (pool_page); /* If page zero is the current page, make sure that literals are inserted */ /* in the page zero literal table. */ if(( pool_page & 07600 ) == 0 ) { p = &pz; } /* Search the literal pool for any occurence of the needed value. */ ix = lit_base[pageno] - 1; while( ix >= lit_loc[pageno] && p->pool[ix] != value ) { ix--; } /* Check if value found in literal pool. If not, then insert value. */ if( ix < lit_loc[pageno] ) { lit_loc[pageno]--; p->pool[lit_loc[pageno]] = value; ix = lit_loc[pageno]; } return( ix ); } /* insertLiteral() */ /******************************************************************************/ /* */ /* Function: testZeroPool */ /* */ /* Synopsis: Test for literal in page zero pool. */ /* */ /******************************************************************************/ BOOL testZeroPool( WORD32 value ) { WORD32 ix; WORD32 pageno; pageno = GET_PAGE( field ); for ( ix = lit_loc[pageno]; ix < lit_base[pageno]; ix++ ) { if( pz.pool[ix] == value ) return TRUE; } return FALSE; } /******************************************************************************/ /* */ /* Function: printSymbolTable */ /* */ /* Synopsis: Output the symbol table. */ /* */ /******************************************************************************/ void printSymbolTable() { int col; int cx; char *fmt; int ix; char mark; int page; int row; int symbol_base; int symbol_lines; symbol_base = number_of_fixed_symbols; for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) { topOfForm( list_title, s_symtable ); symbol_lines = LIST_LINES_PER_PAGE - page_lineno; for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) { list_lineno++; page_lineno++; fprintf( listfile, "%5d", list_lineno ); for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) { /* Get index of symbol for the current line and column */ cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; cx += symbol_base; /* Make sure that there is a symbol to be printed. */ if( number_of_fixed_symbols <= cx && cx < symbol_top ) { switch( symtab[cx].type & LABEL ) { case LABEL: fmt = " %c%-6.6s %5.5o "; break; default: fmt = " %c%-6.6s %4.4o "; break; } switch( symtab[cx].type & ( DEFINED | REDEFINED )) { case UNDEFINED: mark = '?'; break; case REDEFINED: mark = '#'; break; default: mark = ' '; break; } fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); ix++; } } fprintf( listfile, "\n" ); } } } /* printSymbolTable() */ /******************************************************************************/ /* */ /* Function: printPermanentSymbolTable */ /* */ /* Synopsis: Output the permanent symbol table to a file suitable for */ /* being input after the EXPUNGE pseudo-op. */ /* */ /******************************************************************************/ void printPermanentSymbolTable() { int ix; FILE *permfile; char *s_type; if(( permfile = fopen( permpathname, "w" )) == NULL ) { exit( 2 ); } fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); fprintf( permfile, " EXPUNGE\n/\n" ); /* Print the memory reference instructions first. */ s_type = " "; for( ix = 0; ix < symbol_top; ix++ ) { if( M_MRI( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } s_type = " "; for( ix = 0; ix < symbol_top; ix++ ) { if( M_FIXED( symtab[ix].type )) { if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } } fprintf( permfile, "/\n FIXTAB\n" ); fclose( permfile ); } /* printPermanentSymbolTable() */ /******************************************************************************/ /* */ /* Function: printCrossReference */ /* */ /* Synopsis: Output a cross reference (concordance) for the file being */ /* assembled. */ /* */ /******************************************************************************/ void printCrossReference() { int ix; int symbol_base; int xc; int xc_index; int xc_refcount; int xc_cols; /* Force top of form for first page. */ page_lineno = LIST_LINES_PER_PAGE; list_lineno = 0; symbol_base = number_of_fixed_symbols; for( ix = symbol_base; ix < symbol_top; ix++ ) { list_lineno++; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref ); } fprintf( listfile, "%5d", list_lineno ); /* Get reference count & index into concordance table for this symbol. */ xc_refcount = symtab[ix].xref_count; xc_index = symtab[ix].xref_index; /* Determine how to label symbol on concordance. */ switch( symtab[ix].type & ( DEFINED | REDEFINED )) { case UNDEFINED: fprintf( listfile, " U "); break; case REDEFINED: fprintf( listfile, " M %5d ", xreftab[xc_index] ); break; default: fprintf( listfile, " A %5d ", xreftab[xc_index] ); break; } fprintf( listfile, "%-6.6s ", symtab[ix].name ); /* Output the references, 8 numbers per line after symbol name. */ for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { if( xc_cols >= XREF_COLUMNS ) { xc_cols = 0; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref); } list_lineno++; fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); } fprintf( listfile, " %5d", xreftab[xc_index + xc] ); } fprintf( listfile, "\n" ); } } /* printCrossReference() */ /******************************************************************************/ /* */ /* Function: topOfForm */ /* */ /* Synopsis: Prints title and sub-title on top of next page of listing. */ /* */ /******************************************************************************/ void topOfForm( char *title, char *sub_title ) { char temp[10]; if (!listfile) return; list_pageno++; strcpy( temp, s_page ); sprintf( temp, "%s %d", s_page, list_pageno ); /* Output a top of form if not the first page of the listing. */ if( list_pageno > 1 ) { fprintf( listfile, "\f" ); } fprintf( listfile, "\n %-63s %10s\n", title, temp ); /* Reset the current page line counter. */ page_lineno = 1; if( sub_title != NULL ) { fprintf( listfile, "%80s\n", sub_title ); page_lineno++; } else { fprintf( listfile, "\n" ); page_lineno++; } fprintf( listfile, "\n" ); page_lineno++; } /* topOfForm() */ /******************************************************************************/ /* */ /* Function: lexemeToName */ /* */ /* Synopsis: Convert the current lexeme into a string. */ /* */ /******************************************************************************/ char *lexemeToName( char *name, WORD32 from, WORD32 term ) { WORD32 to; to = 0; while( from < term && to < ( SYMLEN - 1 )) { name[to++] = toupper( line[from++] ); } while( to < SYMLEN ) { name[to++] = '\0'; } return( name ); } /* lexemeToName() */ /******************************************************************************/ /* */ /* Function: defineLexeme */ /* */ /* Synopsis: Put lexeme into symbol table with a value. */ /* */ /******************************************************************************/ SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ WORD32 term, /* end+1 of lexeme being defined. */ WORD32 val, /* value of lexeme being defined. */ SYMTYP type ) /* how symbol is being defined. */ { char name[SYMLEN]; lexemeToName( name, start, term); return( defineSymbol( name, val, type, start )); } /* defineLexeme() */ /******************************************************************************/ /* */ /* Function: defineSymbol */ /* */ /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ /* not already in table. */ /* */ /******************************************************************************/ SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) { SYM_T *sym; WORD32 xref_count; val = val & 07777; if( strlen( name ) < 1 ) { return( &sym_undefined ); /* Protect against non-existent names. */ } sym = lookup( name ); xref_count = 0; /* Set concordance for normal defintion. */ if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) { if( pass == 2 ) { errorSymbol( &redefined_symbol, sym->name, start ); type = type | REDEFINED; sym->xref_count++; /* Referenced symbol, count it. */ xref_count = sym->xref_count; } return ( sym ); } if( M_FIXED( sym->type )) { return ( sym ); } if( pass == 2 && xref ) { /* Put the definition line number in the concordance table. */ /* Defined symbols are not counted as references. */ xreftab[sym->xref_index] = lineno; /* Put the line number in the concordance table. */ xreftab[sym->xref_index + xref_count] = lineno; } /* Now set the value and the type. */ sym->val = val; sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; return( sym ); } /* defineSymbol() */ /******************************************************************************/ /* */ /* Function: lookup */ /* */ /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ /* table as undefined. Return address of symbol in table. */ /* */ /******************************************************************************/ SYM_T *lookup( char *name ) { int ix; /* Insertion index */ int lx; /* Left index */ int rx; /* Right index */ /* First search the permanent symbols. */ lx = 0; ix = binarySearch( name, lx, number_of_fixed_symbols ); /* If symbol not in permanent symbol table. */ if( ix < 0 ) { /* Now try the user symbol table. */ ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); /* If symbol not in user symbol table. */ if( ix < 0 ) { /* Must put symbol in table if index is negative. */ ix = ~ix; if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { errorSymbol( &symbol_table_full, name, lexstart ); exit( 1 ); } for( rx = symbol_top; rx >= ix; rx-- ) { symtab[rx + 1] = symtab[rx]; } symbol_top++; /* Enter the symbol as UNDEFINED with a value of zero. */ strcpy( symtab[ix].name, name ); symtab[ix].type = UNDEFINED; symtab[ix].val = 0; symtab[ix].xref_count = 0; if( xref && pass == 2 ) { xreftab[symtab[ix].xref_index] = 0; } } } return( &symtab[ix] ); /* Return the location of the symbol. */ } /* lookup() */ /******************************************************************************/ /* */ /* Function: binarySearch */ /* */ /* Synopsis: Searches the symbol table within the limits given. If the */ /* symbol is not in the table, it returns the insertion point. */ /* */ /******************************************************************************/ int binarySearch( char *name, int start, int symbol_count ) { int lx; /* Left index */ int mx; /* Middle index */ int rx; /* Right index */ int compare; /* Results of comparison */ lx = start; rx = symbol_count - 1; while( lx <= rx ) { mx = ( lx + rx ) / 2; /* Find center of search area. */ compare = strcmp( name, symtab[mx].name ); if( compare < 0 ) { rx = mx - 1; } else if( compare > 0 ) { lx = mx + 1; } else { return( mx ); /* Found a match in symbol table. */ } } /* end while */ return( ~lx ); /* Return insertion point. */ } /* binarySearch() */ /******************************************************************************/ /* */ /* Function: compareSymbols */ /* */ /* Synopsis: Used to presort the symbol table when starting assembler. */ /* */ /******************************************************************************/ int compareSymbols( const void *a, const void *b ) { return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); } /* compareSymbols() */ /******************************************************************************/ /* */ /* Function: copyMacLine */ /* */ /* Synopsis: Used to copy a macro line to the macro buffer. */ /* */ /******************************************************************************/ int copyMacLine( int length, int from, int term, int nargs ) { char name[SYMLEN]; int ix; int jx; int kx; BOOL bl; bl = TRUE; for( ix = from; ix < term; ix++ ) { if( !is_blank( line[ix] )) bl = FALSE; } if( bl || ( length < 0 )) return length; if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; for( ix = from; ix < term; ) { if( nargs && isalpha( line[ix] )) /* Start of symbol? */ { for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ { if( !isalnum( line[jx] )) break; } lexemeToName( name, ix, jx ); /* Make into name. */ for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ { if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) { mac_buffer[length++] = 'a' + (char) kx; for( ix++; ix < jx; ix++ ) { mac_buffer[length++] = 'z'; } break; } /* end if strncmp */ } /* end for kx */ if( kx >= nargs ) { for ( ; ix < jx; ) { mac_buffer[length++] = toupper( line[ix++] ); } } } /*end if nargs */ else { mac_buffer[length++] = toupper( line[ix++] ); } /* end else */ } /* end for ix */ mac_buffer[length++] = '\n'; mac_buffer[length] = 0; return length; } /******************************************************************************/ /* */ /* Function: evalSymbol */ /* */ /* Synopsis: Get the pointer for the symbol table entry if exists. */ /* If symbol doesn't exist, return a pointer to the undefined sym */ /* */ /******************************************************************************/ SYM_T *evalSymbol() { char name[SYMLEN]; SYM_T *sym; sym = lookup( lexemeToName( name, lexstart, lexterm )); sym->xref_count++; /* Count the number of references to symbol. */ if( xref && pass == 2 ) { /* Put the line number in the concordance table. */ xreftab[sym->xref_index + sym->xref_count] = lineno; } return( sym ); } /* evalSymbol() */ /******************************************************************************/ /* */ /* Function: moveToEndOfLine */ /* */ /* Synopsis: Move the parser input to the end of the current input line. */ /* */ /******************************************************************************/ void moveToEndOfLine() { while( !isend( line[cc] )) cc++; lexstart = cc; lexterm = cc; lexstartprev = lexstart; } /* moveToEndOfLine() */ /******************************************************************************/ /* */ /* Function: nextLexeme */ /* */ /* Synopsis: Get the next lexical element from input line. */ /* */ /******************************************************************************/ void nextLexeme() { /* Save start column of previous lexeme for diagnostic messages. */ lexstartprev = lexstart; lextermprev = lexterm; while( is_blank( line[cc] )) { cc++; } lexstart = cc; if( isalnum( line[cc] )) { while( isalnum( line[cc] )) { cc++; } } else if( isend( line[cc] )) { /* End-of-Line, don't advance cc! */ } else { switch( line[cc] ) { case '"': /* Quoted letter */ if( cc + 2 < maxcc ) { cc++; cc++; } else { errorMessage( &no_literal_value, lexstart ); cc++; } break; case '/': /* Comment, don't advance cc! */ break; default: /* All other punctuation. */ cc++; break; } } lexterm = cc; } /* nextLexeme() */ /******************************************************************************/ /* */ /* Function: nextLexBlank */ /* */ /* Synopsis: Used to prevent illegal blanks in expressions. */ /* */ /******************************************************************************/ void nextLexBlank() { nextLexeme(); if( is_blank( delimiter )) { errorMessage( &illegal_blank, lexstart - 1 ); } delimiter = line[lexterm]; } /* nextLexBlank() */ /******************************************************************************/ /* */ /* Function: pseudoOperators */ /* */ /* Synopsis: Process pseudo-ops (directives). */ /* */ /******************************************************************************/ BOOL pseudoOperators( PSEUDO_T val ) { int count; int delim; int index; int ix; WORD32 length; int level; int lexstartsave; WORD32 newfield; WORD32 oldclc; int pack; int pageno; int pos; int radixprev; BOOL status; SYM_T *sym; WORD32 value; WORD32 word; int width; static int mask_tab[13] = { 00000, 00001, 00003, 00007, 00017, 00037, 00077, 00177, 00377, 00777, 01777, 03777, 07777 }; status = TRUE; switch( (PSEUDO_T) val ) { case BINPUNCH: /* If there has been data output and this is a mode switch, set up to */ /* output data in BIN mode. */ if( binary_data_output && rim_mode ) { for( ix = 0; ix < TOTAL_PAGES; ix++) { lit_loc[ix] = lit_base[ix] = 00200; } cp.error = FALSE; pz.error = FALSE; punchLeader( 8 ); /* Generate a short leader/trailer. */ checksum = 0; binary_data_output = FALSE; } rim_mode = FALSE; break; case DECIMAL: radix = 10; break; case DEFINE: count = 0; index = 0; lexstartsave = lexstart; while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && ( count < MAC_MAX_ARGS )) { if ( !isalpha( line[lexstart] ) && ( index == 0 )) { index = lexstart; } lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); nextLexeme(); } if( count == 0 ) /* No macro name. */ { errorMessage( &no_macro_name, lexstartsave ); index = 1; } else if( index ) /* Bad argument name. */ { errorMessage( &bad_dummy_arg, index ); } else if( mac_count >= MAC_TABLE_LENGTH ) { errorMessage( ¯o_table_full, lexstartsave ); index = 1; } else { value = mac_count; mac_count++; /* Value is entry in mac_bodies. */ defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); } if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) { readLine(); nextLexeme(); } if( index ) { conditionFalse(); /* On error skip macro body. */ } else if( line[lexstart] == '<' ) { /* Invariant: line[cc] is the next unexamined character. */ index = lexstart + 1; length = 0; level = 1; while( level > 0 ) { if( isend( line[cc] ) || ( line[cc] == '/' )) { length = copyMacLine( length, index, cc, count - 1 ); readLine(); index = 0; } else { switch( line[cc] ) { case '>': level--; cc++; break; case '<': level++; cc++; break; case '$': level = 0; cc++; break; default: cc++; break; } /* end switch */ } /* end if */ } /* end while */ length = copyMacLine( length, index, cc - 1, count - 1 ); if( length < 0 ) { errorMessage (¯o_too_long, lexstart ); } else if( length == 0 ) { mac_bodies[value] = NULL; } else { mac_bodies[value] = (char *) malloc( length + 1 ); if( mac_bodies[value] ) { strncpy( mac_bodies[value], mac_buffer, length ); *( mac_bodies[value] + length ) = 0; } else { errorMessage( &no_virtual_memory, lexstart ); } } nextLexeme(); } else { errorMessage( <_expected, lexstart ); } /* end if */ break; case DUBL: inputDubl(); break; case EJECT: page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ status = FALSE; /* This will force reading of next line */ break; case ENPUNCH: if( pass == 2 ) { objectfile = objectsave; } break; case EXPUNGE: /* Erase symbol table */ if( pass == 1 ) { symtab[0] = sym_undefined; symbol_top = 0; number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Enter the pseudo-ops into the symbol table. */ for( ix = 0; ix < DIM( pseudo ); ix++ ) { defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); } /* Enter I and Z into the symbol table. */ for( ix = 0; ix < 2; ix++ ) { defineSymbol( permanent_symbols[ix].name, permanent_symbols[ix].val, permanent_symbols[ix].type | DEFFIX , 0 ); } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; } break; case BANK: case FIELD: punchLiteralPool( &cp, clc - 1 ); punchLiteralPool( &pz, field ); newfield = field >> 12; lexstartsave = lexstartprev; if( isdone( line[lexstart] )) { newfield += 1; /* Blank FIELD directive. */ } else { newfield = (getExpr())->val; /* FIELD with argument. */ } if( rim_mode ) { errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ } else if( newfield > 7 || newfield < 0 ) { errorMessage( &illegal_field_value, lexstartprev ); } else { value = (( newfield & 0007 ) << 3 ) | 00300; punchObject( value ); checksum -= value; /* Field punches are not added to checksum. */ field = newfield << 12; } clc = 0200 | field; fieldlc = clc & 07777; if( !rim_mode ) { punchOrigin( clc ); } break; case FIXTAB: /* Mark all current symbols as permanent symbols. */ if( pass == 1) { for( ix = 0; ix < symbol_top; ix++ ) { symtab[ix].type = ( symtab[ix].type | FIXED ) & ~CONDITION; if((( symtab[ix].val & 00777 ) == 0 ) && ( symtab[ix].val <= 05000 ) && M_DEFINED( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type ) && !M_LABEL( symtab[ix].type ) && !M_MACRO( symtab[ix].type )) { symtab[ix].type = symtab[ix].type | MRI; } } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Re-sort the symbol table */ qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); } break; case FLTG: inputFltg(); /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ break; case IFDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionTrue(); } else { conditionFalse(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionFalse(); } else { conditionTrue(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNZERO: if( (getExpr())->val == 0 ) { conditionFalse(); } else { conditionTrue(); } break; case IFZERO: if( (getExpr())->val == 0 ) { conditionTrue(); } else { conditionFalse(); } break; case LGM: lgm_flag = TRUE; break; case LIST: listfile = listsave; break; case LIT: if( clc & 07600 ) { punchLiteralPool( &cp, clc ); } else { punchLiteralPool( &pz, field ); } if( !rim_mode ) { punchOrigin( clc ); } break; case LITBAS: if( clc & 07600 ) { punchLiteralPool( &cp, clc ); } else { punchLiteralPool( &pz, field ); } if( !rim_mode ) { punchOrigin( clc ); } pageno = GET_PAGE (clc); if( isdone( line[lexstart] )) { lit_loc[pageno] = lit_base[pageno] = 0200; } else { lit_loc[pageno] = lit_base[pageno] = (((getExpr())->val) & 0177) + 1; } break; case NOLGM: lgm_flag = FALSE; break; case NOPUNCH: if( pass == 2 ) { objectfile = NULL; } break; case OCTAL: radix = 8; break; case PAGE: punchLiteralPool( &cp, clc - 1 ); oldclc = clc; if( isdone( line[lexstart] )) { clc = ( clc + 0177 ) & 077600; /* No argument. */ fieldlc = clc & 07777; } else { value = (getExpr())->val; clc = field + (( value & 037 ) << 7 ); fieldlc = clc & 07777; } testForLiteralCollision( clc ); if( !rim_mode && clc != oldclc ) { punchOrigin( clc ); } break; case PAUSE: break; case RELOC: if( isdone( line[lexstart] )) { reloc = 0; /* Blank RELOC directive. */ } else { value = (getExpr())->val; /* RELOC with argument. */ reloc = value - ( clc + reloc ); } break; case RIMPUNCH: /* If the assembler has output any BIN data, output the literal tables */ /* and the checksum for what has been assembled and setup for RIM mode. */ if( binary_data_output && !rim_mode ) { endOfBinary(); punchChecksum(); punchLeader( 8 ); /* Generate a short leader/trailer. */ } rim_mode = TRUE; break; case TEXT: delim = line[lexstart]; pack = 0; count = 0; index = lexstart + 1; while( line[index] != delim && !isend( line[index] )) { pack = ( pack << 6 ) | ( line[index] & 077 ); count++; if( count > 1 ) { punchOutObject( clc, pack ); incrementClc(); count = 0; pack = 0; } index++; } if( count != 0 ) { punchOutObject( clc, pack << 6 ); incrementClc(); } else { punchOutObject( clc, 0 ); incrementClc(); } if( isend( line[index] )) { cc = index; lexterm = cc; errorMessage( &text_string, cc ); } else { cc = index + 1; lexterm = cc; } nextLexeme(); break; case TITLE: delim = line[lexstart]; ix = lexstart + 1; /* Find string delimiter. */ do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } ix++; } while( line[ix] != delim && !isend(line[ix]) ); if( !isend( line[ix] ) ) { count = 0; ix = lexstart + 1; do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } list_title[count] = line[ix]; count++; ix++; } while( line[ix] != delim && !isend(line[ix]) ); if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } cc = ix + 1; lexterm = cc; page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ list_title_set = TRUE; } else { cc = ix; lexterm = cc; errorMessage( &text_string, cc ); } nextLexeme(); break; case UNLIST: listfile = NULL; break; case VFD: pos = 0; word = 0; radixprev = radix; while( !isdone (line[lexstart] )) { lexstartsave = lexstart; radix = 10; width = (getExpr())->val; /* Get field width. */ radix = radixprev; if( (width <= 0) || ((width + pos) > 12) || (line[lexstart] != ':') ) { errorMessage( &illegal_vfd_value, lexstartsave ); } nextLexBlank(); /* Skip colon. */ value = (getExpr())->val; /* Get field value. */ if( line[lexterm] == ',' ) cc++; nextLexeme(); /* Advance to next field. */ pos = pos + width; if( pos <= 12 ) { word = word | ((value & mask_tab[width]) << (12 - pos)); } } punchOutObject( clc, word ); incrementClc(); break; case ZBLOCK: value = (getExpr())->val; if( value < 0 ) { errorMessage( &zblock_too_small, lexstartprev ); } else if( value + ( clc & 07777 ) - 1 > 07777 ) { errorMessage( &zblock_too_large, lexstartprev ); } else { for( ; value > 0; value-- ) { punchOutObject( clc, 0 ); incrementClc(); } } break; default: break; } /* end switch for pseudo-ops */ return( status ); } /* pseudoOperators() */ /******************************************************************************/ /* */ /* Function: conditionFalse */ /* */ /* Synopsis: Called when a false conditional has been evaluated. */ /* Lex should be the opening <; ignore all text until */ /* the closing >. */ /* */ /******************************************************************************/ void conditionFalse() { int level; if( line[lexstart] == '<' ) { /* Invariant: line[cc] is the next unexamined character. */ level = 1; while( level > 0 ) { if( isend( line[cc] ) || ( line[cc] == '/' )) { readLine(); } else { switch( line[cc] ) { case '>': level--; cc++; break; case '<': level++; cc++; break; case '$': level = 0; cc++; break; default: cc++; break; } /* end switch */ } /* end if */ } /* end while */ nextLexeme(); } else { errorMessage( <_expected, lexstart ); } } /* conditionFalse() */ /******************************************************************************/ /* */ /* Function: conditionTrue */ /* */ /* Synopsis: Called when a true conditional has been evaluated. */ /* Lex should be the opening <; skip it and setup for */ /* normal assembly. */ /* */ /******************************************************************************/ void conditionTrue() { if( line[lexstart] == '<' ) { nextLexeme(); /* Skip the opening '<' */ } else { errorMessage( <_expected, lexstart ); } } /* conditionTrue() */ /******************************************************************************/ /* */ /* Function: errorLexeme */ /* */ /* Synopsis: Display an error message using the current lexical element. */ /* */ /******************************************************************************/ void errorLexeme( EMSG_T *mesg, WORD32 col ) { char name[SYMLEN]; errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); } /* errorLexeme() */ /******************************************************************************/ /* */ /* Function: errorSymbol */ /* */ /* Synopsis: Display an error message with a given string. */ /* */ /******************************************************************************/ void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) { char linecol[12]; char *s; if( pass == 2 ) { s = ( name == NULL ) ? "" : name ; errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", filename, linecol, mesg->file, s, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorSymbol() */ /******************************************************************************/ /* */ /* Function: errorMessage */ /* */ /* Synopsis: Display an error message without a name argument. */ /* */ /******************************************************************************/ void errorMessage( EMSG_T *mesg, WORD32 col ) { char linecol[12]; if( pass == 2 ) { errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", filename, linecol, mesg->file, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorMessage() */ /******************************************************************************/ /* */ /* Function: saveError */ /* */ /* Synopsis: Save the current error in a list so it may displayed after the */ /* the current line is printed. */ /* */ /******************************************************************************/ void saveError( char *mesg, WORD32 col ) { if( save_error_count < DIM( error_list )) { error_list[save_error_count].mesg = mesg; error_list[save_error_count].col = col; save_error_count++; } error_in_line = TRUE; if( listed ) { printErrorMessages(); } } /* saveError() */ /* End-of-File */ simh-3.8.1/sim_console.c0000644000175000017500000010422611111337624013323 0ustar vlmvlm/* sim_console.c: simulator console I/O library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Sep-06 RMS Fixed non-printable characters in KSR mode 22-Jun-06 RMS Implemented SET/SHOW PCHAR 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument 22-Nov-05 RMS Added central input/output conversion support 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy 28-Oct-04 JDB Fixed SET CONSOLE to allow comma-separated parameters 20-Aug-04 RMS Added OS/2 EMX fixes (from Holger Veit) 14-Jul-04 RMS Revised Windows console code (from Dave Bryan) 28-May-04 RMS Added SET/SHOW CONSOLE RMS Added break, delete character maps 02-Jan-04 RMS Removed timer routines, added Telnet console routines RMS Moved console logging to OS-independent code 25-Apr-03 RMS Added long seek support from Mark Pizzolato Added Unix priority control from Mark Pizzolato 24-Sep-02 RMS Removed VT support, added Telnet console support Added CGI support (from Brian Knittel) Added MacOS sleep (from Peter Schorn) 14-Jul-02 RMS Added Windows priority control from Mark Pizzolato 20-May-02 RMS Added Windows VT support from Fischer Franz 01-Feb-02 RMS Added VAX fix from Robert Alan Byer 19-Sep-01 RMS More Mac changes 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 20-Jul-01 RMS Added Macintosh support (from Louis Chretien, Peter Schorn, and Ben Supnik) 15-May-01 RMS Added logging support 05-Mar-01 RMS Added clock calibration support 08-Dec-00 BKR Added OS/2 support (from Bruce Ray) 18-Aug-98 RMS Added BeOS support 13-Oct-97 RMS Added NetBSD terminal support 25-Jan-97 RMS Added POSIX terminal I/O support 02-Jan-97 RMS Fixed bug in sim_poll_kbd This module implements the following routines to support terminal I/O: sim_poll_kbd - poll for keyboard input sim_putchar - output character to console sim_putchar_s - output character to console, stall if congested sim_set_console - set console parameters sim_show_console - show console parameters sim_tt_inpcvt - convert input character per mode sim_tt_outcvt - convert output character per mode sim_ttinit - called once to get initial terminal state sim_ttrun - called to put terminal into run state sim_ttcmd - called to return terminal to command state sim_ttclose - called once before the simulator exits sim_os_poll_kbd - poll for keyboard input sim_os_putchar - output character to console The first group is OS-independent; the second group is OS-dependent. The following routines are exposed but deprecated: sim_set_telnet - set console to Telnet port sim_set_notelnet - close console Telnet port sim_show_telnet - show console status */ #include "sim_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #define KMAP_WRU 0 #define KMAP_BRK 1 #define KMAP_DEL 2 #define KMAP_MASK 0377 #define KMAP_NZ 0400 int32 sim_int_char = 005; /* interrupt character */ int32 sim_brk_char = 000; /* break character */ int32 sim_tt_pchar = 0x00002780; #if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh)) int32 sim_del_char = '\b'; /* delete character */ #else int32 sim_del_char = 0177; #endif TMLN sim_con_ldsc = { 0 }; /* console line descr */ TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */ extern volatile int32 stop_cpu; extern int32 sim_quiet, sim_deb_close; extern FILE *sim_log, *sim_deb; extern DEVICE *sim_devices[]; /* Set/show data structures */ static CTAB set_con_tab[] = { { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ }, { "BRK", &sim_set_kmap, KMAP_BRK }, { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ }, { "PCHAR", &sim_set_pchar, 0 }, { "TELNET", &sim_set_telnet, 0 }, { "NOTELNET", &sim_set_notelnet, 0 }, { "LOG", &sim_set_logon, 0 }, { "NOLOG", &sim_set_logoff, 0 }, { "DEBUG", &sim_set_debon, 0 }, { "NODEBUG", &sim_set_deboff, 0 }, { NULL, NULL, 0 } }; static SHTAB show_con_tab[] = { { "WRU", &sim_show_kmap, KMAP_WRU }, { "BRK", &sim_show_kmap, KMAP_BRK }, { "DEL", &sim_show_kmap, KMAP_DEL }, { "PCHAR", &sim_show_pchar, 0 }, { "LOG", &sim_show_log, 0 }, { "TELNET", &sim_show_telnet, 0 }, { "DEBUG", &sim_show_debug, 0 }, { NULL, NULL, 0 } }; static int32 *cons_kmap[] = { &sim_int_char, &sim_brk_char, &sim_del_char }; /* Console I/O package. The console terminal can be attached to the controlling window or to a Telnet connection. If attached to a Telnet connection, the console is described by internal terminal multiplexor sim_con_tmxr and internal terminal line description sim_con_ldsc. */ /* SET CONSOLE command */ t_stat sim_set_console (int32 flag, char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if (cvptr = strchr (gbuf, '=')) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if (ctptr = find_ctab (set_con_tab, gbuf)) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else return SCPE_NOPARAM; } return SCPE_OK; } /* SHOW CONSOLE command */ t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { char gbuf[CBUFSIZE]; SHTAB *shptr; int32 i; if (*cptr == 0) { /* show all */ for (i = 0; show_con_tab[i].name; i++) show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); return SCPE_OK; } while (*cptr != 0) { cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ if (shptr = find_shtab (show_con_tab, gbuf)) shptr->action (st, dptr, uptr, shptr->arg, cptr); else return SCPE_NOPARAM; } return SCPE_OK; } /* Set keyboard map */ t_stat sim_set_kmap (int32 flag, char *cptr) { DEVICE *dptr = sim_devices[0]; int32 val, rdx; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; if (dptr->dradix == 16) rdx = 16; else rdx = 8; val = (int32) get_uint (cptr, rdx, 0177, &r); if ((r != SCPE_OK) || ((val == 0) && (flag & KMAP_NZ))) return SCPE_ARG; *(cons_kmap[flag & KMAP_MASK]) = val; return SCPE_OK; } /* Show keyboard map */ t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (sim_devices[0]->dradix == 16) fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); return SCPE_OK; } /* Set printable characters */ t_stat sim_set_pchar (int32 flag, char *cptr) { DEVICE *dptr = sim_devices[0]; uint32 val, rdx; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; if (dptr->dradix == 16) rdx = 16; else rdx = 8; val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r); if ((r != SCPE_OK) || ((val & 0x00002400) == 0)) return SCPE_ARG; sim_tt_pchar = val; return SCPE_OK; } /* Show printable characters */ t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (sim_devices[0]->dradix == 16) fprintf (st, "pchar mask = %X\n", sim_tt_pchar); else fprintf (st, "pchar mask = %o\n", sim_tt_pchar); return SCPE_OK; } /* Set log routine */ t_stat sim_set_logon (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; if ((cptr == NULL) || (*cptr == 0)) /* need arg */ return SCPE_2FARG; cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; sim_set_logoff (0, NULL); /* close cur log */ sim_log = sim_fopen (gbuf, "a"); /* open log */ if (sim_log == NULL) /* error? */ return SCPE_OPENERR; if (!sim_quiet) printf ("Logging to file \"%s\"\n", gbuf); fprintf (sim_log, "Logging to file \"%s\"\n", gbuf); /* start of log */ return SCPE_OK; } /* Set nolog routine */ t_stat sim_set_logoff (int32 flag, char *cptr) { if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_log == NULL) /* no log? */ return SCPE_OK; if (!sim_quiet) printf ("Log file closed\n"); fprintf (sim_log, "Log file closed\n"); /* close log */ fclose (sim_log); sim_log = NULL; return SCPE_OK; } /* Show log status */ t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_log) fputs ("Logging enabled\n", st); else fputs ("Logging disabled\n", st); return SCPE_OK; } /* Set debug routine */ t_stat sim_set_debon (int32 flag, char *cptr) { char *tptr, gbuf[CBUFSIZE]; if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ return SCPE_2FARG; tptr = get_glyph (cptr, gbuf, 0); /* get file name */ if (*tptr != 0) /* now eol? */ return SCPE_2MARG; sim_set_deboff (0, NULL); /* close cur debug */ if (strcmp (gbuf, "LOG") == 0) { /* debug to log? */ if (sim_log == NULL) /* any log? */ return SCPE_ARG; sim_deb = sim_log; } else if (strcmp (gbuf, "STDOUT") == 0) /* debug to stdout? */ sim_deb = stdout; else if (strcmp (gbuf, "STDERR") == 0) /* debug to stderr? */ sim_deb = stderr; else { cptr = get_glyph_nc (cptr, gbuf, 0); /* reparse */ sim_deb = sim_fopen (gbuf, "a"); /* open debug */ if (sim_deb == NULL) /* error? */ return SCPE_OPENERR; sim_deb_close = 1; /* need close */ } if (!sim_quiet) printf ("Debug output to \"%s\"\n", gbuf); if (sim_log) fprintf (sim_log, "Debug output to \"%s\"\n", gbuf); return SCPE_OK; } /* Set nodebug routine */ t_stat sim_set_deboff (int32 flag, char *cptr) { if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_deb == NULL) /* no log? */ return SCPE_OK; if (!sim_quiet) printf ("Debug output disabled\n"); if (sim_log) fprintf (sim_log, "Debug output disabled\n"); if (sim_deb_close) /* close if needed */ fclose (sim_deb); sim_deb_close = 0; sim_deb = NULL; return SCPE_OK; } /* Show debug routine */ t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_deb) fputs ("Debug output enabled\n", st); else fputs ("Debug output disabled\n", st); return SCPE_OK; } /* Set console to Telnet port */ t_stat sim_set_telnet (int32 flg, char *cptr) { if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ return SCPE_2FARG; if (sim_con_tmxr.master) /* already open? */ return SCPE_ALATT; return tmxr_open_master (&sim_con_tmxr, cptr); /* open master socket */ } /* Close console Telnet port */ t_stat sim_set_notelnet (int32 flag, char *cptr) { if (cptr && (*cptr != 0)) /* too many arguments? */ return SCPE_2MARG; if (sim_con_tmxr.master == 0) /* ignore if already closed */ return SCPE_OK; return tmxr_close_master (&sim_con_tmxr); /* close master socket */ } /* Show console Telnet status */ t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_con_tmxr.master == 0) fprintf (st, "Connected to console window\n"); else if (sim_con_ldsc.conn == 0) fprintf (st, "Listening on port %d\n", sim_con_tmxr.port); else { fprintf (st, "Listening on port %d, connected to socket %d\n", sim_con_tmxr.port, sim_con_ldsc.conn); tmxr_fconns (st, &sim_con_ldsc, -1); tmxr_fstats (st, &sim_con_ldsc, -1); } return SCPE_OK; } /* Check connection before executing */ t_stat sim_check_console (int32 sec) { int32 c, i; if (sim_con_tmxr.master == 0) /* not Telnet? done */ return SCPE_OK; if (sim_con_ldsc.conn) { /* connected? */ tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ if (sim_con_ldsc.conn) /* still connected? */ return SCPE_OK; } for (i = 0; i < sec; i++) { /* loop */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ if (i) { /* if delayed */ printf ("Running\n"); /* print transition */ fflush (stdout); } return SCPE_OK; /* ready to proceed */ } c = sim_os_poll_kbd (); /* check for stop char */ if ((c == SCPE_STOP) || stop_cpu) return SCPE_STOP; if ((i % 10) == 0) { /* Status every 10 sec */ printf ("Waiting for console Telnet connection\n"); fflush (stdout); } sim_os_sleep (1); /* wait 1 second */ } return SCPE_TTMO; /* timed out */ } /* Poll for character */ t_stat sim_poll_kbd (void) { int32 c; c = sim_os_poll_kbd (); /* get character */ if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */ return c; /* in-window */ if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ return SCPE_LOST; tmxr_poll_rx (&sim_con_tmxr); /* poll for input */ if (c = tmxr_getc_ln (&sim_con_ldsc)) /* any char? */ return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG; return SCPE_OK; } /* Output character */ t_stat sim_putchar (int32 c) { if (sim_log) /* log file? */ fputc (c, sim_log); if (sim_con_tmxr.master == 0) /* not Telnet? */ return sim_os_putchar (c); /* in-window version */ if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ return SCPE_LOST; tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ return SCPE_OK; } t_stat sim_putchar_s (int32 c) { t_stat r; if (sim_log) fputc (c, sim_log); /* log file? */ if (sim_con_tmxr.master == 0) /* not Telnet? */ return sim_os_putchar (c); /* in-window version */ if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ return SCPE_LOST; if (sim_con_ldsc.xmte == 0) /* xmt disabled? */ r = SCPE_STALL; else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ return r; /* return status */ } /* Input character processing */ int32 sim_tt_inpcvt (int32 c, uint32 mode) { uint32 md = mode & TTUF_M_MODE; if (md != TTUF_MODE_8B) { c = c & 0177; if (md == TTUF_MODE_UC) { if (islower (c)) c = toupper (c); if (mode & TTUF_KSR) c = c | 0200; } } else c = c & 0377; return c; } /* Output character processing */ int32 sim_tt_outcvt (int32 c, uint32 mode) { uint32 md = mode & TTUF_M_MODE; if (md != TTUF_MODE_8B) { c = c & 0177; if (md == TTUF_MODE_UC) { if (islower (c)) c = toupper (c); if ((mode & TTUF_KSR) && (c >= 0140)) return -1; } if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) && ((c == 0177) || ((c < 040) && !((sim_tt_pchar >> c) & 1)))) return -1; } else c = c & 0377; return c; } /* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */ #if defined (VMS) #if defined(__VAX) #define sys$assign SYS$ASSIGN #define sys$qiow SYS$QIOW #endif #include #include #include #include #include #include #include #define EFN 0 uint32 tty_chan = 0; typedef struct { unsigned short sense_count; unsigned char sense_first_char; unsigned char sense_reserved; unsigned int stat; unsigned int stat2; } SENSE_BUF; typedef struct { unsigned short status; unsigned short count; unsigned int dev_status; } IOSB; SENSE_BUF cmd_mode = { 0 }; SENSE_BUF run_mode = { 0 }; t_stat sim_ttinit (void) { unsigned int status; IOSB iosb; $DESCRIPTOR (terminal_device, "tt"); status = sys$assign (&terminal_device, &tty_chan, 0, 0); if (status != SS$_NORMAL) return SCPE_TTIERR; status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; run_mode = cmd_mode; run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC); run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU; return SCPE_OK; } t_stat sim_ttrun (void) { unsigned int status; IOSB iosb; status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, &run_mode, sizeof (run_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; return SCPE_OK; } t_stat sim_ttcmd (void) { unsigned int status; IOSB iosb; status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; return SCPE_OK; } t_stat sim_ttclose (void) { return sim_ttcmd (); } t_stat sim_os_poll_kbd (void) { unsigned int status, term[2]; unsigned char buf[4]; IOSB iosb; SENSE_BUF sense; term[0] = 0; term[1] = 0; status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, 0, 0, &sense, 8, 0, term, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; if (sense.sense_count == 0) return SCPE_OK; term[0] = 0; term[1] = 0; status = sys$qiow (EFN, tty_chan, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, &iosb, 0, 0, buf, 1, 0, term, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_OK; if (buf[0] == sim_int_char) return SCPE_STOP; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; return (buf[0] | SCPE_KFLAG); } t_stat sim_os_putchar (int32 out) { unsigned int status; char c; IOSB iosb; c = out; status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, &iosb, 0, 0, &c, 1, 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTOERR; return SCPE_OK; } /* Win32 routines */ #elif defined (_WIN32) #include #include #include #include #define RAW_MODE 0 static HANDLE std_input; static DWORD saved_mode; t_stat sim_ttinit (void) { std_input = GetStdHandle (STD_INPUT_HANDLE); if ((std_input == INVALID_HANDLE_VALUE) || !GetConsoleMode (std_input, &saved_mode)) return SCPE_TTYERR; return SCPE_OK; } t_stat sim_ttrun (void) { if (!GetConsoleMode(std_input, &saved_mode) || !SetConsoleMode(std_input, RAW_MODE)) return SCPE_TTYERR; if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_BINARY); } SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); return SCPE_OK; } t_stat sim_ttcmd (void) { if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_TEXT); } SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_NORMAL); if (!SetConsoleMode(std_input, saved_mode)) return SCPE_TTYERR; return SCPE_OK; } t_stat sim_ttclose (void) { return SCPE_OK; } t_stat sim_os_poll_kbd (void) { int c; if (!_kbhit ()) return SCPE_OK; c = _getch (); if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; return c | SCPE_KFLAG; } t_stat sim_os_putchar (int32 c) { if (c != 0177) _putch (c); return SCPE_OK; } /* OS/2 routines, from Bruce Ray and Holger Veit */ #elif defined (__OS2__) #include t_stat sim_ttinit (void) { return SCPE_OK; } t_stat sim_ttrun (void) { return SCPE_OK; } t_stat sim_ttcmd (void) { return SCPE_OK; } t_stat sim_ttclose (void) { return SCPE_OK; } t_stat sim_os_poll_kbd (void) { int c; #if defined (__EMX__) switch (c = _read_kbd(0,0,0)) { /* EMX has _read_kbd */ case -1: /* no char*/ return SCPE_OK; case 0: /* char pending */ c = _read_kbd(0,1,0); break; default: /* got char */ break; } #else if (!kbhit ()) return SCPE_OK; c = getch(); #endif if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; return c | SCPE_KFLAG; } t_stat sim_os_putchar (int32 c) { if (c != 0177) { #if defined (__EMX__) putchar (c); #else putch (c); #endif fflush (stdout); } return SCPE_OK; } /* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and Peter Schorn */ #elif defined (__MWERKS__) && defined (macintosh) #include #include #include #include #include #include #include #include /* function prototypes */ Boolean SIOUXIsAppWindow(WindowPtr window); void SIOUXDoMenuChoice(long menuValue); void SIOUXUpdateMenuItems(void); void SIOUXUpdateScrollbar(void); int ps_kbhit(void); int ps_getch(void); extern char sim_name[]; extern pSIOUXWin SIOUXTextWindow; static CursHandle iBeamCursorH = NULL; /* contains the iBeamCursor */ static void updateCursor(void) { WindowPtr window; window = FrontWindow(); if (SIOUXIsAppWindow(window)) { GrafPtr savePort; Point localMouse; GetPort(&savePort); SetPort(window); #if TARGET_API_MAC_CARBON GetGlobalMouse(&localMouse); #else localMouse = LMGetMouseLocation(); #endif GlobalToLocal(&localMouse); if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) { SetCursor(*iBeamCursorH); } else { SetCursor(&qd.arrow); } TEIdle(SIOUXTextWindow->edit); SetPort(savePort); } else { SetCursor(&qd.arrow); TEIdle(SIOUXTextWindow->edit); } return; } int ps_kbhit(void) { EventRecord event; int c; updateCursor(); SIOUXUpdateScrollbar(); while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | highLevelEventMask | diskEvt, &event)) { SIOUXHandleOneEvent(&event); } if (SIOUXQuitting) { exit(1); } if (EventAvail(keyDownMask,&event)) { c = event.message&charCodeMask; if ((event.modifiers & cmdKey) && (c > 0x20)) { GetNextEvent(keyDownMask, &event); SIOUXHandleOneEvent(&event); if (SIOUXQuitting) { exit(1); } return false; } return true; } else { return false; } } int ps_getch(void) { int c; EventRecord event; fflush(stdout); updateCursor(); while(!GetNextEvent(keyDownMask,&event)) { if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | highLevelEventMask | diskEvt, &event)) { SIOUXUpdateScrollbar(); SIOUXHandleOneEvent(&event); } } if (SIOUXQuitting) { exit(1); } c = event.message&charCodeMask; if ((event.modifiers & cmdKey) && (c > 0x20)) { SIOUXUpdateMenuItems(); SIOUXDoMenuChoice(MenuKey(c)); } if (SIOUXQuitting) { exit(1); } return c; } /* Note that this only works if the call to sim_ttinit comes before any output to the console */ t_stat sim_ttinit (void) { int i; /* this blank will later be replaced by the number of characters */ char title[50] = " "; unsigned char ptitle[50]; SIOUXSettings.autocloseonquit = TRUE; SIOUXSettings.asktosaveonclose = FALSE; SIOUXSettings.showstatusline = FALSE; SIOUXSettings.columns = 80; SIOUXSettings.rows = 40; SIOUXSettings.toppixel = 42; SIOUXSettings.leftpixel = 6; iBeamCursorH = GetCursor(iBeamCursor); strcat(title, sim_name); strcat(title, " Simulator"); title[0] = strlen(title) - 1; /* Pascal string done */ for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */ ptitle[i] = title[i]; } SIOUXSetTitle(ptitle); return SCPE_OK; } t_stat sim_ttrun (void) { return SCPE_OK; } t_stat sim_ttcmd (void) { return SCPE_OK; } t_stat sim_ttclose (void) { return SCPE_OK; } t_stat sim_os_poll_kbd (void) { int c; if (!ps_kbhit ()) return SCPE_OK; c = ps_getch(); if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; return c | SCPE_KFLAG; } t_stat sim_os_putchar (int32 c) { if (c != 0177) { putchar (c); fflush (stdout); } return SCPE_OK; } /* BSD UNIX routines */ #elif defined (BSDTTY) #include #include #include struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ struct tchars cmdtchars,runtchars; /* V7 editing */ struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ int cmdfl,runfl; /* TTY flags */ t_stat sim_ttinit (void) { cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ runfl = cmdfl | FNDELAY; if (ioctl (0, TIOCGETP, &cmdtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCGETC, &cmdtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCGLTC, &cmdltchars) < 0) return SCPE_TTIERR; runtty = cmdtty; /* initial run state */ runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK; runtchars.t_intrc = sim_int_char; /* interrupt */ runtchars.t_quitc = 0xFF; /* no quit */ runtchars.t_startc = 0xFF; /* no host sync */ runtchars.t_stopc = 0xFF; runtchars.t_eofc = 0xFF; runtchars.t_brkc = 0xFF; runltchars.t_suspc = 0xFF; /* no specials of any kind */ runltchars.t_dsuspc = 0xFF; runltchars.t_rprntc = 0xFF; runltchars.t_flushc = 0xFF; runltchars.t_werasc = 0xFF; runltchars.t_lnextc = 0xFF; return SCPE_OK; /* return success */ } t_stat sim_ttrun (void) { runtchars.t_intrc = sim_int_char; /* in case changed */ fcntl (0, F_SETFL, runfl); /* non-block mode */ if (ioctl (0, TIOCSETP, &runtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSETC, &runtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSLTC, &runltchars) < 0) return SCPE_TTIERR; nice (10); /* lower priority */ return SCPE_OK; } t_stat sim_ttcmd (void) { nice (-10); /* restore priority */ fcntl (0, F_SETFL, cmdfl); /* block mode */ if (ioctl (0, TIOCSETP, &cmdtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSETC, &cmdtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) return SCPE_TTIERR; return SCPE_OK; } t_stat sim_ttclose (void) { return sim_ttcmd (); } t_stat sim_os_poll_kbd (void) { int status; unsigned char buf[1]; status = read (0, buf, 1); if (status != 1) return SCPE_OK; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; else return (buf[0] | SCPE_KFLAG); } t_stat sim_os_putchar (int32 out) { char c; c = out; write (1, &c, 1); return SCPE_OK; } /* POSIX UNIX routines, from Leendert Van Doorn */ #else #include #include struct termios cmdtty, runtty; static int prior_norm = 1; t_stat sim_ttinit (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; if (tcgetattr (0, &cmdtty) < 0) /* get old flags */ return SCPE_TTIERR; runtty = cmdtty; runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */ runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */ runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */ runtty.c_cc[VINTR] = sim_int_char; /* interrupt */ runtty.c_cc[VQUIT] = 0; /* no quit */ runtty.c_cc[VERASE] = 0; runtty.c_cc[VKILL] = 0; runtty.c_cc[VEOF] = 0; runtty.c_cc[VEOL] = 0; runtty.c_cc[VSTART] = 0; /* no host sync */ runtty.c_cc[VSUSP] = 0; runtty.c_cc[VSTOP] = 0; #if defined (VREPRINT) runtty.c_cc[VREPRINT] = 0; /* no specials */ #endif #if defined (VDISCARD) runtty.c_cc[VDISCARD] = 0; #endif #if defined (VWERASE) runtty.c_cc[VWERASE] = 0; #endif #if defined (VLNEXT) runtty.c_cc[VLNEXT] = 0; #endif runtty.c_cc[VMIN] = 0; /* no waiting */ runtty.c_cc[VTIME] = 0; #if defined (VDSUSP) runtty.c_cc[VDSUSP] = 0; #endif #if defined (VSTATUS) runtty.c_cc[VSTATUS] = 0; #endif return SCPE_OK; } t_stat sim_ttrun (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ if (tcsetattr (0, TCSAFLUSH, &runtty) < 0) return SCPE_TTIERR; if (prior_norm) { /* at normal pri? */ errno = 0; nice (10); /* try to lower pri */ prior_norm = errno; /* if no error, done */ } return SCPE_OK; } t_stat sim_ttcmd (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; if (!prior_norm) { /* priority down? */ errno = 0; nice (-10); /* try to raise pri */ prior_norm = (errno == 0); /* if no error, done */ } if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) return SCPE_TTIERR; return SCPE_OK; } t_stat sim_ttclose (void) { return sim_ttcmd (); } t_stat sim_os_poll_kbd (void) { int status; unsigned char buf[1]; status = read (0, buf, 1); if (status != 1) return SCPE_OK; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; else return (buf[0] | SCPE_KFLAG); } t_stat sim_os_putchar (int32 out) { char c; c = out; write (1, &c, 1); return SCPE_OK; } #endif simh-3.8.1/DOCS/0000755000175000017500000000000011147615610011373 5ustar vlmvlmsimh-3.8.1/DOCS/architecture18b.pdf0000644000175000017500000034122410561202211015055 0ustar vlmvlm%PDF-1.4 %âãÏÓ 991 0 obj <> endobj xref 991 26 0000000016 00000 n 0000001561 00000 n 0000000833 00000 n 0000001816 00000 n 0000002170 00000 n 0000002634 00000 n 0000003263 00000 n 0000003300 00000 n 0000003528 00000 n 0000003750 00000 n 0000003828 00000 n 0000004463 00000 n 0000005187 00000 n 0000005622 00000 n 0000006050 00000 n 0000006510 00000 n 0000006904 00000 n 0000007066 00000 n 0000007310 00000 n 0000007551 00000 n 0000008064 00000 n 0000008522 00000 n 0000008756 00000 n 0000009395 00000 n 0000009843 00000 n 0000001360 00000 n trailer <<91263370750f7943aca2492fdec8b594>]>> startxref 0 %%EOF 993 0 obj<>stream xÚb```b``åd`e``Obàc@>  Ç„˜C盦D~ Û ~L×\ü­]mD-,6ÊÏU£#°ÔáWX¹ï9ëm†Nž‚}þ¦­W#LoHL0tŠmì3 mQœ.Ub蔺¬Ì–T å hKÝ21ôØ\^7åžC'‰Ç,¬çMZX’Á\‡PžÇ¬œÓŽÙ\ɼØlqQDífbIPä±¹!&EÔ¢%‹.w3XXAÆ2Š(NÛÆ~O@4yš&È]¡¡i ŒBJj Ì@6kD£DDT£Äa`0Nëè``ti``RñéS‚ˆ30¸€$€RJ¡ = , ŠIÙDq@U4 4 2†ç+€´«ÃL•—óEÖ`àÇ7=aU ­Hnæ“*ØÔøEòÁ¾™Ž]ª ήÔ*}x‹÷ƒ£cëÝ}L)üê™OÈaœÀ×°ø& ŒŠß@ŽºS HÇ30&ž`Ç;ƒ5‡10 iN½ g³µY¨Ö©/0± ‚{ endstream endobj 1016 0 obj<>/W[1 1 1]/Type/XRef/Index[102 889]>>stream xÚìÑ10 ð4 Çn\ÇaW"`·'M†-æjà~ø~à‡üz Êh« endstream endobj 992 0 obj<>>>/LastModified(D:20061008130046)/MarkInfo<>>> endobj 994 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 995 0 obj<> endobj 996 0 obj<> endobj 997 0 obj[/ICCBased 1015 0 R] endobj 998 0 obj<> endobj 999 0 obj<> endobj 1000 0 obj<> endobj 1001 0 obj<>stream H‰”TËŽ›0Ýów9•‚ãkž®F#5U]Œ:Ò°ì†8fâ6@„Mªé××6ä1UˆT±À1Üóâ8óx|œ?/¿­€ÂÓÓbµ„`¾|¥ ´ÝphÑÊîµûo:XÁ¼(( URB)M¡0,Ñnÿvs…¤îþÇþ*:À”Pæ!‡§Ñ˜ðŠ:xøÒ‰2R˜¾+÷°>¶ûÞ¨¶ÕÀj½üÁ³pù–m}èì4|*~ë"X?;Éx²1ÈÄk™±“éV,áN%Žz¼˜”S%œs¯gÑnàµ?4ê× :yTZnæáwaBæüN°³›ìÔózFzŽ!¤YNbä<÷¼SÀÑGà1}çÅ.#b<°_9à„’,µ/ o´éJa¦âiå·DG ‰mXé]ÑÉoÁΕal*f­DœGžÄÖ6½Ú¨ÔQú2ˆ±  ßµ‘µþ f'áeõ:AOÆÏD˜:"F ·¥³Î¶ÁÎüëñpËfÿ ßsgù Êfë'0!¶è;5´òÒµa4÷G"ŒÄy!Œyæ!åÁªß¶¢¯ec´—_~8 bW6oÒ=) ´gô+aC•žQÊGmBô]g+Û-¾CÝ«JUŸ"¼ d£5Ä(ç%´•Ÿs1éÂ4{1Bs;}mÎ…S6åþ]ËÁÙF6²RÖ¦{b˸•mUéÓÉim÷ÉT£Òÿ>_N/gäœDÏñna³›§ìòÇ&þ 0J÷@" endstream endobj 1002 0 obj<>stream H‰|”AÚ0…ïüŠ9‚&vW«=,Pi+UÝCn¥“Lo“˜Æ”_ÛÛm•ƒ­(óù½™çÐðûP¿ãÄ JÊyY=?7¦UE—©˜do£u6Z]Âhþó¯Ëç¤ðøø´²ïž²Ñ<Ë(PÈÊQY4 ëù~çøÑ‚D–Ÿzþ¿˜üCæÌBAvüF$åœÇŽü}ü¹U5P¾ `”]“x +ù*¨`ý«“ûKÕîU+z{3Ž7ãÕz¹™À¶“•Rhº…\ÕûÎ` ú¤ Öú˜ÂäGö¥,œ]¿¥©ÓÀŒ¹6®Fã—ÕËŒNÁ-Q¿$SoÝ׆ôZÌ’³¹ˆSËÈŠ¾–OA4…/¤1lÆŒØV[¬Ôq3!žÖK¡7¸Ðãò˜y°ùîì Y–Øb¥ë–4zP•ð+&d3c IÓœYÚÞcŽZ«VOA«¬Qƒl oª…£8ihÑ~§¯èh˜R‡fáŒ]ÚÁÈæ´|md)saç$Ú|' æ¦kíü¶-ŠŸºï‰÷}©¿Uv¯E6÷ZÎg4x„…Å^Åݶ/öâ±C .ó°@‹’‹„ ÎVVÒHÔ ³‘¸Ú‹»Ôߪê:;Uº,i¼Dk˜À©0ðmX«FX«b§ŽàÂj[Š›ÒxðÀ½‡a<ŒÎÄVuï{zÀVwú1š] ·>d½¯Ð]¥þ¹¦Tø[šÓžçßÀjíÜxž–> °ïóÅ(IÙÂêíEú|ö³±f]¿Ø; ¯ÿ‚8L/…ÊZñ×s­T…k°x×ÔÀû íipŸfQ ùû¿­.fL endstream endobj 1003 0 obj<>stream H‰Œ“ËNÃ@ E÷ù /aQw<¶ç!U]ôÄ¢¨‹ù ¢HÔ"ø}œF*MÚ´Q3НÎõc<^Í@¦ÓÙbÕ¬TãRʦrPž-†Îƒ³¯¹¥Œ9ç¤ *è5‡壺ƒûò^-Kµ\e¼†ÉdÜ é"zd,ç,h‚$$P~¯x‰¯½nXù‹V](ÆdÖêz±Q/š¡=GÔ\>stream H‰Œ“MkA †ïû+r´—1™d¾@„úqhÁÒÃüjµ´èEº”þûfaºë²‡f_ž„ä}GÏõ(y ù«Zæj¹šC5~…Éd¼š?-À"L§³…>Îr5Ι€ ï*BƒP¿ó‰ŒM):Fc^!ªÑb»iðÒ‰§aøèô°\âëï;ñv^\4‘$¥¿zûU|À+|Ã^…É8=„€F""Áq[í. rCºüÔ9{–¤;Ëd’¶’®e¡9[2Fg©ã~Y8÷ÛWQ¾_ÑÙ`HeáMÚ²öøætöˆòœIÕ ùÊÝ$¡ØZÊK}Xo§•°‰,žU›ß«ÑºþÜw:A9¼2Ñ'[uØ vƒÀ–ƒñHBX\'ØN«O .ÙîŽÃàÌQôRŽBzZŽƒã–‚O\Cº™4µ[ÒÈßOZŸ¦•´¶ìÚ÷ð'À›ß# endstream endobj 1005 0 obj<>stream H‰Œ“=OÃ@ †÷û70„×>ß§TuèǨÃmˆ…TQZ!ÔÓ‚š¦Mˆ2ÄÃ{í»÷])FÁ¥µ%Èu…`Cý'gô¦R+Å!󧚈HMu ,¨Ôr&‰,œÑlS6zÐãñh9»k“ôd2Ï´šf5ÊYºê¼Rˆèu.õM]²L“¿5Im4Êw¨j‹Ì~ǼVŲZlvºÜ•Îoj‘Õb)ðFGÆ‹Ûlò ‘½w­îk[•lÄ6ÀErÜbÇš]£ë‰êõ-X”ë”å ø $¿¨¢s3h–×°H©½‰azäaøèÀShã{ÙvÛº)2·GGˆgôhkg»½Á-ƒ9º{ïÄ_Únë“ô£4† Ò!MÙYÒŽì•ý²¯c#}¨F [´®@ʽvòÏ’AÎ]Ž#'{|’Çbú¼•Úà\ñ¹y-¥~ÊwßÞe0”è´QqEï;}†‡’¼çS¶þ`˜ÂÓ endstream endobj 1006 0 obj<> endobj 1007 0 obj<> endobj 1008 0 obj<> endobj 1009 0 obj<> endobj 1010 0 obj<>stream H‰œ“MkÛ@†ïúsÈ!9t¼ó±ÒnÆ6´”ЈÜM¼6.X6r‚É¿ï®7’#¥¦è egöygfß=?Ë-ÚpQþʦe6½C6ºƒ««ÑíøûÄÁõõÍ$nÞ”Ù¨, ÊEF ƒ‰ßŸ•¡ˆSâÙzï \gçgú ÚŸ†vI‰ôÍÃh5'¡Õ:ô*ŽÑäÑuáNQòÈ´ñ§‚ìÀ :cê-ÞÄSJ8Dˆ{‚…àÑYŠuÐàá͵uZ8…;Bë÷-H‘Ú9@XUZ ý1‹f19ò œÂ§à@Gââø¥¥ÒQ·?k*%¸LÆr!E·ý Lè`Úæ¢^s(Ÿ¢³Ê=ÛÌ›hbnùkжÜOFcŒOø/i)2$R,âXµ¹Ûz3y sXÔ›uWóa;«Þee¸¡F‘µO‘¢\|UߨŽ7ëíËs¨aZ-WUõªZ^ÂW˜LÇð¸ ûTEÓ¾Ó5øMœ¥ÎãŒ~.àÛ¬žïgu€‡×ÝsXï`v«eÕîäPDž^2°·oµÄšøãÍéÜœõÈœzƒß bè- endstream endobj 1011 0 obj<> endobj 1012 0 obj<> endobj 1013 0 obj<>stream H‰””_o›0Åßù÷‘Hű &ñTUZ“l®®â CRz~²°ž¯2ÈŠ¯YäÌr§oX´Ów•Õ'1¢Œ˜E *O>V¶SDV>°%’! ŽÐ$ä>stream H‰¤”MKÃ@†ïùsLi»ÝÙì§Ôú!(ˆö¦b4b’’ þz7­V,»¥ÚÍe™„yö}˜l =û-l´¸A4º‡ñxt;»žƒ`0™Lç®8µÑÈZö5RB)&`—Àˆ1ìÐÕP÷ìvÆmŒ$˜®ÁÑC }„¢Ññ%ô„11Ðn¹â“½ñÒ/ÝGÒ‚$ÆÐä‹4<$©nÉ0‰{I´KHCé'RhÙ!ã @q–@™ƒÕ·@îÈñäÉEBÐ)Ú <$m&a’úMÂSr$ˆJê㵿³èÚ¸ö[—L˜…"Œ’|—ì®Ìc ,«býžYÙBZçíªÈÚ| «´vSÁº®žÝÛ†@oˆÃU^7í>²ºÚSgë:k\ƒ´Í«²@ŸBZ¾¸]„=™xBM¨R]Â#ž$=kÒ¨"Z)ÜÿªÔ7i4˜Kâ©“f”»äÏ ‚¶ƒ¦Â öwîr Júæ >ÌÅ endstream endobj 1015 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰¬WïoÛºýàýühãŪHŠ¢XtÚ$݈¢blšam:Öž%z’\7ýãwHI¶‹nZ<·@,Û¼÷ðþ8÷ÜWŸÈ›7¯>\ßÝ\]½»¹&¯®ïC2¯ðýGªyqAI†ÏÿŠÏ«‹wÓ‹WÓiH(™./ ÈLçdbß²Ÿîì±iEhhÿþ§i‰‡ dÎbóN…DâÄqÉ/F÷znŠÅ%I‹¬ÎLAÌ’ú1­³/šÛ|¦ËŠ”úÛ¬ÔI òÀOÿÛ@Ê"`,ˆ[ß‹‹‘.$-ÍæiY>áhD–¥ÉI½Òd•=®ˆ)º$S5Î÷†ÙÁ°p–'é h¤¤sPgimvÏ ½v–n§·lhá¦]¸Ãi½…ÖÍ@¼âD±PŠ»xù ³cô1ìn@¹5Ï¥"Ÿ )ƒH(Á¬‹Ï#Ü‘Œ)UÉè/d Ï#BÆ §Gíi_1ÿ{ú·!(|ÊÛ8$U¡úSÜF/v+D dGÛIë–àmûòz/ö‰€ñ¤ iµÍáEª^hÿB‘×UübW< P’R6‚×7w‰+ê«ùbÓŒRH¾û~Š„÷FÉOT?¥A¬$KÎV¿6Â0 R²¦Ã¦èèO7ŸPŒu™éA“Wf 6²­þ‡.ͤÔ‘.ê´v„qðàŒYkª#!K+›ÒÌÖ:'³'ò¸MË´¨µÎŠGXLk0TV¯r]góýPêlv–úœc6ºL-ÇTàÉ/–sJ³ØÎô‡¸×…%¦¬º$Y½·Éé§t‘Pˆ”'Ý¥)sËê¯u™‚تªõ†,¶¥EÜÑô¥Ã:éLôÎWzþ{s; æ®¶ëšÀp¶AƦðÚþ&«m|ÃÀO¼Œâ}LB~3;{ëKg½IÓxµöÜQ9b=”œIkï±É7HÚÁ~{ÝL¤ö1×¹)Ÿˆ ý³Áš9fYƒ/u£ W,õ©Ñ&6Axð§e²·ÙÇØ7èÒߤÅÌçÛ²ÄÕ³Âù¨Ò…šåzðò˜a’Ç¢[­ ª¯rçšä,t­çu/×ûS·…~I¼ƒwÒ™îcîÒAV邘è6zž¥kLãJ¿Æqç–<Œ~ Æä)ÓëÅ¡Œ}4ѯ“½q ^rI÷won¯;äUmÌš5„äEÏkbô¾ý¦ŸV©ì÷£2³8I1Â׺;¶0é12/EÔý°Ølë!s,`Œv¬ùqÈZh™C½­­­s‹ Ö’¨7CVEQ2ÈÉn>f5z~$gµÍÏnŸ-Ž#Ù~õ¿±Ä÷ÌHðD±.×Q?™& ´ö„)7¦™¥Ž”3”jðLñMxh…Ö3jΊùzk™kfêÙn0%Ü ÀZ€w–ëÈZרDÌãme™µZ¹éÙõ#w΃0áIƒñó([öj¹²Ó27ã‰AÌt½Ó('xÕ«¼< ÝÚ=ž| KèùÊà=?kcÛå ÓŒ,uZoKíÔŠ¿yÐ;Q,÷}¹3Û5"b§^fE3¡`‘¬SÛ“4™‘ê #,¯À06Z."‘>ÆÂÄgЄݙ‡±@ð™‰Âf” #È­D)úîŽüÜ}¿¯P\¢ß|pAÕe… ”ãÚß ˜ËÊDÄêÝõ …ŠBBÒrrõöÛÃ/ûßÙ‰ Á÷wÚ~¥‰S’ÖEc´rÐV,™ ÓðBõ”Ï̺º<Ó!4 põ¨­‚R£s–8°q#þ*'`ëUi¶+õ©&rÙšt„…YÖ»´Ôþ"ƒ*áˆFJOT‡öˆí¯\f.ÛÙ@Þ–óUf5’íŸïøÇ·ŽeQ²óu'ýuw*í\`I¤ª“?ÏJîKVeØP0ÜÐdOhµWïQqÄñ°¶Dí™Íu©³Êûé&ekl ºzí½ÜOl†’3–J}'lgVÃf&ó€ÓˆúvCŽý“Bj¹ýóó›Á›ð5•Wd¬h<")ôw¾§Ù±éÙzYè‡Àö¦Ts•ˆÀÝÇ€H(_}$cΩua÷ã ƒ8úÒÙ"Ü';Y’”ª„¶P>þƒŒiq¬e=½Dþ[,ËuúèÇqFýÊ}<¸„Œ2Ž[Ÿ\Bh—MiË4oqÌͶ8ϰxiS±X(#É[0ã‰s{ë@q`Â^¤øþsýµÖ…m¬e‡ïc¾Û2¯Hôw0¼òðüù, ¡bÕb¾ý×ô3)m„môr§†|8„?tbŸEá± ä*Þgñýú:FÀXt’D[L•‡‡n-Ž^ûp@Ñ ªâ®¤ïï;J ¬ ˆ·EµËêùJŸr† Oès èYÈãRšþ³kw”?”R­±äíL¹@í,Km¼I ìOÌs4L¶“cBÌ4”A$•ˆö„t?}Ûd‚‘’(Û31ó°ó9Þ§JJ(q–öùÎ=ÑACNìc\E¢•$],2+.Rq£Ó‰¬Â3«tù%íÐsúk¬ÊÖœW£lβu–ÛYú0ª´ú Ö3š—3ûù0J(ª¢ó㓟áêïª]ë%¦¸­âɱêp¡ƒm³±W#²|³ÖvË?VÙWÑj/¿ze:·[剆àåi ™ÑRi©—º„®Õ—¤ú=Û4µWç5Ö¬G¹#™¹Âòu ["l /—íf—Z¼¹^dø¢]½žûlÑ]úð‹_ùð]WlPyp¡øùÌÏ úŸ~›Sæ¢9d›%AØỄàa„pB"B!1!’„…CYnÆÎ…ŽèAûB§ú‰aDC(@†¡lQü:™üÌ/Œøå0BÀ‰t£rô¸—Ù4¿eEûI÷G”ºªŽ?üf £W>TòŨ@ñÔŸœdFè)(Â&29_Šê§JQJ ÄÖöç&LtEÞ; ­å®ÈæöYªx”6p4jþ/À-™}ë endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰´—QoÛ6…ßý+¸—ÍYc[—I1H¸vQ8¨—`q‹bmW8Ž“xKìÂRÖaèß¡,EŽ%JL‹¨AÍ0ÉåGòêÜs{gìð°7Œ†,`ÇÇ/‡Öê Î6‹1aÿ±x¶l[`þ5æ¯ãÖËI«7¡Ï#6¹jl2ïM¾ÚßÄŒûùŸZã›nÀÓ0›‘ ˜&êiŒb“»V›íMþj½š´^í å0ë J@œìšU±Ä±AâÏ«ÃW„T!ÉiC~@LÎö:D&z‡¶û¿ KSvÄðßÏlüaÜÿÄö>MNª0„?FDÝÈla„»kŽNwa|kÀý1404bçjwÍ÷nŒ?0¤?††,0)·×ÁD1˜ìbl_Älþï|vŸÌ/Ù4fÓ%[,ãd}?K«¥Mù£I ‰ dÜ•‚Ú?dˆHA‘… ]!#ÿ‚º¸ÆMH{€Å=v¤júoÊGˆ·ûSz›û,½Ö3 Îì€ržžñçâà/¸ðPŽt2ìWßjѸÏ^07þT,- *ìR¼ÁÒUY_ŸðäP¹*(›FZQŽP’¡7£Ó²$4#ø+¡„jÚBw_ºaùŠëq#ø« „ *³…P’Ÿaÿ¬áP>NA²¡Ç_ %ÔP![3QR¡áÈAÈ &9Yü%QB•,XJù1,çG~9ö'.å“P>%Lô€PÎ?Æ.„ÀMà/”B©(#€rº ¥”PJì!в]…¥w¿?tÖÿMoŸ¿2J(£Ô[¥ >ûÒÅÑiààþZ(¡…Rnq”ny4|_£…ùg4s]”ÐE‰¤Ó9NIFç¥w°gŸÅ/¾°Å•=±ãº”äþ¢B4%|²”>çýRúlqüÔ$àÜ_=C¨gˆTV9K9…úç5,G,þÒB:C½ÅRò³ã·%ûÑü’–4°üڄ⯜!”3DFçÖZ–‹Êè]eŸe,–©×„ã/£!d4Ž*U•“q)£sÃã\ß_DCˆhH[ë—²äÄñFm;/'Ècål7‘U0"è’@KWÛ,rS½"ú$DNÛF‘íR«V¡ÈÖ ‘n¹=¹™o”àjµ¾›&ìë4>p­,÷v×jî°Þ¼vO¡éV Y}`Â(´QD›àøþ í.ZM´yh±ÐÊ õ@«W  Û·rzëBò—=ƒ6E¢ý¢Óùž/'†¿â ­ºyÍl³'Aù‘ßóûJsÁÅâ¯xBEGÚ<Ë™xÊAëàŸ•Â_Gæ8¶Ë²»e»›ÿøñsúô¶F.!‘ÎMD¦"Ÿ.šb>¸DFø« ¡$ÁÍt:ìËzu½žÞ±«Ûéuìfó·ž‚+t;ßÇ–"=<,ž/c¨Ú×E2»™×ÀùûQA€C:ë'Á=ÆÊà6ÖÂm·Bw*PÉ'QU!eTµ&0ô7©¢¬îʓʔQÖAùK4‡D+ò„rY¨Ów è¯ØŠ /¦e#U-’¥‚-«¿@íæÐn©ë©šx2ªÅòŸù:ÉŠŠ‹L>É$q)»ààTk(BU´³ë’®…‡åËPöf¥&i¯#Ú7p‚íÅU²å–ìôÁo»÷§Ÿîš¸à]. lJí&êÚàš8·e1Râ\S诩œ€¡#ÅŸÃ"Hå8dÎñØ7 ÑýÅêËlu9Ï+à|i¿»d³Õý2y¨ÎñM ¿z’ %=Ë©ø &Eà œƒí>…]r:$鯃¤©™úÅR äXÉ_Û¬%„ÇPµ 5ˆ££þ`ŸN÷DžÕIÉ¢¦G]¬’'¿W¥lG%ÇCùÙz•L“y¯þð·­$ÈöW.(ÏZ´^\ß$½Ûy ’CO]¥k.Õª´4Õúo+QQФ£F’L°É´¥çºÑT –`nyw1_³Õ£œó¥—],’˜šÒÇ] ž^LdÛª ¾+ú±mGºêÈè‡]ß®¦—lqw7¿\ ¹¶*ð?}oZÚb`* ïÿ ŸK¨ endstream endobj 5 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰´—osÛ6‡ßëSìKy*Ñ þëz&¶ÓÖw—ÚsÖM^Ä7Š„l^)RGPNÜñô³w2í6ŒQŒ¹xv÷‡ÝÅñ œž¸¸ºggç—0:¾¸%H| þ€LŠ… Ÿÿ„Ïïåè|6:žÑ_ P˜-FSâB]¼Oð·gŸÕ+3 ”¨ÏßÕ£ ¿8ÄÕÖÌ'RêpŸófËÑÔ[Àð€„Ç·€R .P(ê €†p4ûßèýlôþƒ¢ÞzB[OvIû(Rx-ÅwÓé[~lî`Œ€#åÜ×Ï*æ£Bžáö¶W¶\Š4‹k»×3üëÝGŠ7%¢NÄ[”¿9"l8Fˆ!笑Ǟ³–üá+¸‚߬ði GS¿t—BOð‚l·ð ¼»ÀÏ«Ÿ'4<ƒ£ÿÎþч GðÁÛGøúS_"—Âüñ"B¸ƒ0kˆÚ¢}Œ8~@G ¯Åø›w+^Àü9(zê}†Ïp¡J˜\ÏËUR¦Bm`}¥â1KD³¥7ÒP²°1 ¯d~:XS£¿EñÕ²+‰õʳ,ðî78¬L¿_î¨K4ƒ$fïùÜ¢ßgÃ_0úW{¯\‰J5Ží–;±.ØKˆmÃùÌu¢ˆÐÃu„†oÚp¾ç:KÂo2/DÃEå"!á7i”ç ®ãG-GgÇQ½ã`ØO+Ûc©}<ÄU=äÛÄÅ^GFB{W‡¾P´Ïïî~Õ×±µ¹Ã‹"‹\Õ€ÞÊÒÌ96Žá……ÈšõÞΡG!¸ù¤ÈERÛ©†Ox,p½‘ÊŒff>KrWÇRÔŠÏ 6|0d>‚¡”ÝW€ux¶`e7v¢ás"ÃÂÊØ+ˆúp6¡RC¤ÊRœû ° 3w(”…HA=ÄùA /Í K3ö»¾ d§Q@ÞÙy†—h†%Ú‹^æ9ÓèhöÑJä /Ò ‹´‡Ê>ôÍVFW×v&úªˆÇ©C"JÏCžkŸ‡‚Í<äjO¼c‹¢Ôkç¡J@Œev_d‹,‰‹ÒL&•XÅE’ YõƒéŽd;p™ÃN„5S‹ÏÓÑøæòfJ!-“õRu\geñ¼\×°˲zñ¥…Tµ1…̶#>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 8 0 obj<>stream H‰¤WÛnÛH}70ÿÐû&&Ý^³Ù'Øx€¬XÁ<Œ ŠjYÜP¤–kœ¯ŸªnÞtiÚ± b©ºúTÕ9§nfäýû›¯·wŸ%>üúé–\ÝÜ>P—ðÿ2ήIàý¿áýcyõëüêf>§„‘ùúŠ’y ßšïñ«ó’0ŠŸ?ðUÿ°)WQôSH‰O>¥~¼½šéüWŸçWŸ¿â¹}.¬Íåð,~èà>ò€á¹gñÂÐf~xÈB ©©ÅJªk|Xɧ$–$η»TVIž‘]–’Lÿœÿv.YnN–±>[×”làÛž†¢K–'[%[¹R SýÙ<Å… úžÍD„mžìÔCLU¾ë¼fhÊ͹Cϱ=^—Û †Y^‘º­¾Ò'Ð-&›:{$ÕF’ò¹¬ä–$kòžñwÌÿ@þñ/BÓÅ/ÆK¸ç/ÑMȹÄ]aShT>:ÞÙÀÖ <ÂtŠÃl×}_Ãs—‘a¥4xçÛÙ7"×kWÉ“LŸIYEi M­pRgU’ªoÝÝÜ“|'‹H×`(W6!wëƒÀ 'Ú§çhš°˜Íœrùt5Q=²J‚å)w2NÖ‰\a6…”d•@2…Ì*²•º°\ØWlÆD€¯®&ñ&Ê’r ׂ_’è)JÒh™JÕˆ˜y_ów&¸ý7Ô’pEè–1ƽåÿt¹®â¹ÀÔ³¹™‹‘ßSêÁ8à¸3¬’´ýÐsPæ@•>ÿ%ãZ7}‘ÇR®Tí>?Î5ÔûÐïÀ>!Îl˜¦6nÓ3IYÖ?Ò#ŸdeUÔ1žqçÍwTÈ£^°Úh]K@Lh±¬ë¼ºÎœ'ÔÃgÀ:OL.[©8 kv)s€mª^øRõL½áÜvXèñ‘#sP½Y®°-_ ó¶ƒD'38±‰”gI•rˆÄ‚s^’u=Bá:¤ÿ '¸+ZgX©}^¼X!LÎ$òô͈ºí5ÂèyªÂú ÿ_Ë zaYÈè;Y@z™`¼‚o9%äwìß<®¡éº"y]0A™‰&Üèò0ÓÅ$É*Yõ®ZLIÇuQ`Ü2yÌ¢4ãWyfnffpX£¨ ×v½­Ûˆ:­Ç™S¸cSÑx· ¶õÝý\ËV6ä®#A<·@Zç˜ÆŸ‚§býüï“j"P£ɪ"O¡ç“µNó]iTfðL¦Ü·]ÖX93BÎtÀ¥Ôæœ:Îe=w íÓèà8q1}GöرQYÊB·gšæ{@ qï•LXØAèò`E%©r²넊 QC1ÿÔ'Ä©ŒŠkÒRßü,hå@Åô>¸Uþrj9mNð©šò‰ö(ùÏýÌ6z2澕dð»¥á(mŸ…æñ/ â›(­ÎÀÝJ6`#6'´}?hÃ~#ËgRHKj²jL«b‚ìH‚Mmè½ ™ ´a8¡ÅðÜŒ2G¼èLZ“»/|H=I ?5&(E’äk‚H‘Fà-ÅÐæÛùÞÎe6\(ð/·öv­1N~(=8h„~$Ü~$Bíc<àÜ–.O· ÿ6É–•bz†‚É Gð“|ßáñ4pÆ9-YQNêo<‹ƒkó¨¦þ™b j 6}×}^§+ l/×gàœ€”zÔi½O ]Ti9¹F7x»ÖŤá&õA$Ñ.Y)«7”敌 ¥B*öìVY±º„±®—Z¶«Á˜ŽÒ© \ÕÚ´¾jÅO]w•ã’´Q¿’#XfU¦ªyšŸ¸³f«j™Æ~Éšœ…¶Í§³ÎB«ðÑáâ ‰‰­š¬ô¯Ü½.kÞüRC‰CÙ(r›ì,üN¹²Å/#Î’Ó×Ëv×|lºPÏmt~Þ8™¦×x˜˜x¨€G¾J4ºãŸu¿Çù.Q‚ü÷ïÉbH=Œ5¯n¼(pëu óubêít±†û]·gëJUàa)—Qüë3Õ¿Ó]‡'ÂG_ÖÀéÐÊ#²VÞ üÖH(Ëw°„bkt£Ùì8ÉÀö©í <®Õ2ØUÖ]ìC‘J0`&·yñ ްhøsj e+âu"]e÷‘2†ÙpB¿Ý5‘ŸÐž¶½šî)rTl¼'y‡µÚÀCÔ›*EP¯pûM’J ãþ„CsÖû¶ãöÝ+‡ÓÜF°¦˜ÌÙcmÍSfpúãÏ]›5O™Áw#L~ØïLãr"˜À…ÅaÝ*q´Hõ E!cXf¡¥ôø½²*ûÖ:Ö0¸†ç9ݤ%èKJ@K€^Mp™TàQÕžApÏ€¶2ÏÕF[KIš|×Ò5û4³`6—uEê¬{ ¢ ˜£%xŒQñ¹ã¶•/+¹…Ї×%{Y`£®äNÂ_YÕªÐ3´qUQ)D#,ÈÛë4z,õ\U‡…èb÷ìÉÃÆðrî;ú@¢å3æ­æKfÑ2UëJ†Dpp‘‘µÅê¢pB+ëÜŒêíì‰ê*ß‚>Æ BÏdj¹Y^{R=1ñçE3ı€1/hé “M2è‹HóW&÷휡7î¡Üyèq†= Üµ$ó®k³€ú/zäñ qE¾úx‹c”æ±v%Ô˜ñ…‹ w¸Œ¦}Ì›çþ<Óæ­Ê.­Á³=É}¯ÞÊ¿*"²ÍWòúàbÌx± w.¨í r¼'S|özw÷iscÚÁ…iƒLó€í‘¯Ø´”MÀ´ME0¦^˜>uí0€ Œäo²‡ƒ ü1QDN-6©*¼ˆvÁè°„æ–3É zYò,`z…K^«n/º'©OÀðuI,tâ7Y–JLUç}õ+T¾y‚£4' fåçóatwMŽâäºK˜Ÿï@õëºP2­¾V=•0ŸWÀà†6コq Bò“08¾íA#x 8dj P¢ÑT=fɸþ¾Ý>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 10 0 obj<>stream H‰œWkoÛÈýn`ÿÃ|¤ ‹áâ 8¶»ëv¸µ¶_â¢Q#‰ ÉáòaGùõ=wF|È•ì"pDQäû8çÞsß=°÷ïßÝ_ßÝ0‡}øðñæš]¼»~tXRãýcuR\p–âþϸ¿­/>.ñˆû_‡q¶Ü\8¶ãp\%xvùB/,kÆúüF·*|±WÛ2W±ÃB'¶cÇ[æï'ŒÇñ>,ÿwñn¹^á9]z™çv ²»¾°’LŠ*-¶¬–¿·²Hä|UIñežó²RÛJÖ5›Áæíòâöž‚å]°ÌpdÆ Ÿƒ8²½8Ž}òÙš2ìž6Œœ`ˆÅáS‡D…q¬Ï¸b׿1U6©*˜üZŠb-׬ÙIV7¸ÕºœéÈY½'·ô°×çºtšëÛîÂ÷¹«™³M¥r¦ É’( ™±F±:ýÚHY،݊d§ÃÔæ†J¸‘ö~Þœs›/âÌ~¶:K/¢f‚µE ÿXY¥ªJ›=Ëä3~ƒïl'Öøñ¤‰hÕlYÕV,S¸Aç2WÕž­pã {²öäºóØFU:³ÿ,ÿ>ª3øtsaíÒíNÖÍàÅÁÇK¶06ÃÞf!¿6—L6‰ý4C¦ƒw¹øn²¹Ä»›´Â!Í®’²¢fj£ ›(^d%Y[S¸å‘ë~lÒÚ™åÕ¢Â4 o_]_²üQï>±—,GS}•$mUÉõ_ÙpÛw:·É³‡k]«† Õ²!”*-ôýNEivƒEîðÞ$ÑÓxL6p€xgó…%fs×¢L ›¦b'8ãs†83ê,ü@ÃîÛN 9¢<ªMó" R.¦Ù~ŠƒŽcóH}†èþ4ѽ!Cn8At?Šì'é¨ñópó0çl#EÓ¢†¬V9ð¨±ÊÀ÷.@µÙHjyõ%K‹$k×ÔþD/¬DòôS“>KV, M"²lŒ6pLƒmù”êök)‹šÞ[îKùŠÈêiˆ±\$•b¢®e/ÈàýlÎ-ºk¨÷ùJei"¯ÚíV?rs³4?þšÖ¥©ù鬱ô³¥C(+‰ÿ ¼ }/ªÊÖ ©[ø•¨¼lñ{N×R1ÂØVäò’=–"‘È€öÈ&²&kÈX÷óÈ¡7ÔC6,J²xi¦óŽ6D^Ü‘k…lŒ_µ6ϲ´n¨ç»Ä@6ê1÷&¡0‚,1:+5'\waý’Ï÷ßi¯èfÝ£©´9’ƒr'ºñØS¸ þøäó=n;~Eg vß²¸ë8ij1ûõ!úŠq<=^ã#B,¦N‹ŽOó~ ŒEÚqx“aÄÓ¼~SßS‡„…¯Â K7#\±HÝ®hŽ7)HŠÑ¨0Î5n×öÜíД(=l„î@þƒ¨šnäè'z…ñ >\£ÎàHè<­àÉ.ÅÄ^³ÕcK+-ã&!8ëäÕ$öc¸weßc2µE¿x²|ÇùÇî›10¼« ‡C\ãƒ^M h¨„åßï¾QoYµ£È ¸æžg;žMIéxHæe–nöŽÖR{­…(¤ª÷ÆíG>º'‹6_!x8²…fÅØr×¢7G`=Ùh‘í#Ïž,:/mj–dª–¨%3-„ê6AïªU52jïnN%Ë]Øte¼}˜ÇO³.Âô E…èô…VÞêë ,ÝäLd ÑçmÖ¤%â:Í©Žç w&ǹËù›I¾p¶v¼¿ÂôÙå²Du]¦üŸð^Û!|¶ð3þGZ€F6ïûÌÐ@ÿJNT–e†³L"§ ÊD¡O÷}Êõ<ŸwÀ¤azã4䡸ñúÐ Œyh{sñºÖÆn7NŒÝcÿÚ1:ÒóÙ<¶úÂÉ,ÍÓBƒÙhj\Ðð›; ™OÐzÓÐÍÅ“°³gä?€ªQ‡UN&i­ß98ÒóFRP,ïØ_ò;Œu*Iü‚æõLò‚¿‰áµæ¹ã̈R¬ÒŒ j-hl]©²$Ý¢½Ë5´Y>ç"-9€jr-²V”™£œú¤Ew64¿åú‡eºã˜ß"ÞØJ­dGf›éêCFG˜¼‡„ÁÓÛ«Û} J'Ët%áfÎT->Q×*ý£ –H—Øê2ë°_$ŒãómUªµÁÈ¡zVí¡Dºj DWa‹H ÇJÀëÊLÆ!‰ohûJ{Ò,Ã[æä¯(íe'Ìv¢Z“ò6Ò½šV:ZjسÈZ#ËD¿5)ÌîµqϵÙÈ“78.çϪZé)#ð†’ !$m–ÔqÑÑc”®3²újð®k¼¡4eïÒL  ‹d?4¨øBcA¾÷¶GY4ôèlGÆÇ}_a‡Ùý,Š´Þ7‘`#½$Ôó –+•³›Ûkâ8›gÏé=r>¢Ñøãäˆî8f]£æ"!1þ(›éùsf©œš?î·ß?g6ËpÈÚÔôA/·£H?:ž>VŠZ²M¿èyT°«ÇÍ]L,™Één‚S½;0‘䮡å Òn…j'}ò4)Èúx 5B£3s„Ñ®U£Í¡q•¥ªùª€4Â7²'j¨GÙÀvmÔšü½M«3sdóAȱz—nFêq@÷ Ùx¬“Û~Ðëí­l˜eçÁáÝÓ`&5‹ã7é–ð«}_‰ä CL»^–aV°²¢ÁÔ¤-àh±…Ûô< ‡e³Õ»ÃÓOãà i%S¦!QmrçÔ*´àv.º? »`R¶ö·S ŽG¾Í;TßA­W‡ôaWcñ9utøÇ©ÄCPÖ=O¤hšHoÛω#üÈ L_1‰uÊ}PêL¯MÜÎ1õ6`k<ÄMdû3ô M´Ý¦!׿%ÔÏ&%¡?OE:±´žMà"Ô*õ|]ç;Zس=2Oâùv Û:ùl!O[ÌÚs=ìž–‘ªlæbŒX̤÷r^Îc31O9ô'd?G²¹GþùPÏÈ~·5š:ƒ»v€¥•"½ºf3Ž}ÃÏZDÍC«K‚¯4'ìÐC/rŒT‚ðq}Ÿ[µÈ!Aʬ­ÙÝ»O£>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 12 0 obj<>stream H‰´—]oÛÆ†ïõ+ö’B£õÎ~oQPí 'NcÔZ4í#Ñ2[‰HÚj ÿøÎ’ÔGb.%ºµB$"î<3;óÎìÅ ù曋÷—ï®#ß~ûÝÕ%]\Þ22/ñÿCÊy6’âóïñù²}7]ÌfŒ™ÝeŒi2›AH‰·þ­YI€ùÿÆ»Y7”ñzÁæ›cÄà7ƒoã+ëÑLjÜÞ’1î"R&Y™±`¢r›Vóû¤Ä[ÎdDŠd?& 2þmöÿÑÛÙèí{|pvn<Ç”{L]cv0iç(çlË4û ™”2©’²j‘¶yö¥Æ§wEžíoâ,Y=£`òNÌIÍ °UÌ…@­¡Z9'Zз?ÏÞ“17FD$ù³J²EK²ÎJVc\ãu$=Dnä1š‚pÖµ<ï>ÜΦh×9éoZ˜»U¼,Ç|4R"*©ïâñ„G=h²{O= ñhI58«=š›ýÞµ°:‘,µ÷ܳE Ê0YxmdvŸ`v.Ó²J 2¿³%î™®7«ô.ÅŒ­ð÷U¾LçÄ?óDG»ÝXRµ!GB›èÚb%ë$«â*Í3ŠÕ„K\“m\’¤Ä"©Òxµú«^Üg.Þb2î½Ô's­Ž¢¦ÒÖþÕ_¤‘” çzi)³ŒÕ5U÷Xš°`AvSGµðuçc MüýÆ}J+’ßÕ.z»“g!~¹‰°ÔZÍÉ(Hg|ä>FÓË7¤ˆñÕßÇxĤÜ$sŒñy؆szI²œ¬ÆQŽ[VÔ»SG÷lÂÈ"©’yÿwðoÉCÙîg±ÝùÂ0W¸b^¤Ù’Äó9îÙ WIª¼^Ž9Ëdã}¿œSm¹Þ¥Èa5|‹m—…­‹É*]§YC>–ûÌS‡U벞ì–=Šbäù6E¾,âu¶ò iœodz'uoê¼Ü< YÆ)F+'vp1{V” Í eXÒ¶·¤]¸¤ùQ¡t¬5¥œ¢³Qí ûæêf"É}¼zlw>{X Ãx¦YYuŠ”ulf侞ù®žßøž±&‚ûíÝD³Hóg ÷%ünÑã­:!wy±Ž«²1¦½-‰Ù°ßúç¡”a¶Þã56Úâ/D»KŠ$›'í’~—¿ØK7WN¥r¦_¯á‹îÇ"#¼•°Í)ú*š-%~F‚mûöì%Ô0LOL!ôM'¸( (Ü)&ˆÅ;±Â, ¨ëêÉ'új2yÉ„a$ŽaÆJSÃ<ÿÉ7þï§4knŸx±(¼|}ñyòÙr”*!49Mà7aÛ^ð_Ç)Ðüû³ uƸ~ ‚žŽË;ûßÁÃSÍå Ñ7ªˆQ\&"¢òõ˜Gái¸»+÷×¢t’:a\¿öÅ¥(­¤Fé^£ݰ“FPçÌë¤ga´@ÒÚRÒ^¬¹Új\$)YSxåâ‘<ÍWøÓæa…íøé/>WBdÝB&SKƒy•0u†ú+Q¢lI¥÷f)ï>Õ $·Xº€}Mæ›'´³z"~zÙ™E±‘ ™êwQ½¼q¢â€ñ ‘ëa& ŸQà^%ÃÌ@«P¡ðôe!BS…ç]»$ Au+h OdÎIñ*S¨›BãñP‰fÿˆÉ9áÈ}6œs…—( „”ÿŠç« ¹|?%¿Fâ×qm v ÉqÀBœ—£áÎM<Úõ)´ƒ¨,l9/D«¹<Ú‡éí)´c©à@¹ã^Á†£í¸<ÚÓ“Q8– ¼ÇÞ &;ÂjÈ~í—G»­ úЪ=7– sѺ¸j´_N¢ Ô|® UÖ¹ó64ÀU£ýp} m`@Õ VžCÆòdiöHÊ?ÒÍ <9°!p©)0ÇNUi/œç+òÊ7ñj›#!ï#ظPTh«{[Ö)>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 14 0 obj<>stream H‰´WïOãHýéþ‡þhn‰qÿ²Ý{s#…„=`‡ìíh†Ó)$Æ·‰³ «ýã·ªÛNlp‡ÀqÉŽÛî~ýªêÕë£KòáÃÑùp<"ùøñx4$½£áU@f<À?RÌÒ% <ÿ<¿-zÇ“ÞÑdJ&‹÷9•d2ƒ7'øú¤ 4Àëïø(‡~ÀÌLöN$‚»(˜`Õûê‘ñç4ò.ð"¼Oxá9 Q¤m:–Ú¤[P+ Ù"]Ç Á˜g² Níâ /¼oФ¼dQš@ç¯o¥¬[b_.yJñ€Øy0ûõéñÐ1ì‚Ô-· 8œRCo,lÛ»nd½îŽaŒnõ •.,q ÂØžUaÖÉ:'íVXkW„ƒkgM0ë±qpÞ±~ÌÒ9)¿iÐóùÝ Œv6I‹2¯~β»´<4ï\ŽÊ6åÍRØ@Òü0‚§°¥yÏ»ì kkšº„Uþ{—ÀRd©‹‚,³ÛdFÊÌì°aýL¢ôë)ûã'µKò]¬ðn‰wR §PÅ1ÝI5o öý»©CÔ55øÅ@YŒ–ò#¬Ðy¦³q“&n~¤BQq9ÞRXk¢ÓY6¸U’&«äwm©ô ù)Û„è™{B¹ˆà±8'úûù<$Óå²*X')¿‘ñ§Áô#ù; ‚Fˆšpy);q3T×ÞppvHFƒá!9=¿‚»/ç ódNÐçz:'S²‘?’ i@ã´QÇt;¼€`n1‹í¬5b;kñW€| ÞÜ;¿<$ {‡d|19De>:ü Ð ù¾‘¤·KMfxûxÀ=s³Ô¾³þ9{)\ÚȉNj(Ü‘D[læÐWo|õ´<×xNðd<úL€F2¾ºlÄd•͓ţ)õÁõ€çãG8åâ7'•(_é2™’¤´»od-ƒ&Ø=êyõz©¶Y<×¥ž•ø¾³ù[ d1ó)30ÜU¸E+žsÉÛâît40ܺ¸3Ì †ÀÝä[R½„M¡¥[¡-!‹…ÎÉz ùºÈ³U“¸gÅ5ùkÏ3Ú;D¦lh`f a$i©óün]€¦˜Þ›Rzº!îC†u˜?†µs%ÌIñX”zUX¹oÔf3C³Äõ÷RÃ×8Cõî:Ï0”(÷‹åô¶pT¼9 "ô!ˆaô’¬>“’–¬BIoc7-~#YÞŒát>ϱEÝÜ•[qjdµT¯p¡5¯(G(k³lTц2~×êƒF ¡‹ùfbÀcŠ˜Ò‰‘Ûm¨g¼ M3è'é-äÃíÝÒµÄ:eéb™ÌÊþ"×°¯ïzvW:ÖÃf HGŒÌÿêÑMRîH‹n ìØ·© æ+…ØÝÈ»ý #ºÎµÀ° A­gø ö¶\>‚¦s5èX•;¨Ó‚hZ’fÄ $׎FÊàÌÉ"¡ªP`úTÚQq·º[–I©ïõr³ ¹éÜá;wÓIÁ+Fƒh7o° õàqE@£ËÞĸK°ÄêlU=øˆÚ¶c¶¾FžçÉ}2Ó²%²YéÔÚQæ"ª‰4e¨ƒä#Å7<çX»0ZͱÆ*Þ‰4XÜÌ]OÒ”¼Á¦å‘“ª š3Òõ‘{Æ@£¹)´[:Ð3EtcÆfèÃi|Óè©@Åm‚’[lç?[­žî¶5vÒ&d³ß£š¿#K_S‘Èñ©ÓãƒÚÅaÌkûXɦø*™åhÿm>]­´ípnXBgÙ˜…Í´ б™·_OÜD=…Ì{àC—˜üwi‘ܦÕìÕíµ—¥ú©/>hl¢±…×ùÑ•½bÇQá…êÌç,ˆÃÕ!h{~ڨʻO õܸ!íð|K!œAˆ$$$$"$&DÁ·hv)ƒ¯ÁÏCmÀ‹uÂrcÁ¬²çÅúý·ü;‘ðW"ß!WÒŠÒH µÜüAÈÞÿ˜ç.@âu€@$c¥ªžóÞäÈ×a‰•Oe™¦ôr²Ï”Ûëþü¸1"| žÆ,üýÐGkv5„ZçPÙ-t 07X© endstream endobj 15 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 16 0 obj<>stream H‰¤WkoÛÈýn ÿa>Ê­Ds8|M³X k'EºH7ET´@\9”XS$ˇûã{îð!Êiy«E‰wÎ}{îÍöÃ7Ÿo?Ý1“ýøãOw·ìêæö«É 7è«Â슳÷ÿ„ûÛêê§õÕÍzm2ÎÖñÕÊ4L“ ¶ñÛõXWŒ›ôþ_ºUâƒaZÚV{%MæqË0¹”.[ﯾ-ØõÊ’Ò>¾³_/øÿ‡^,/Ù翲û…¸¿f×ÿ\ÿùêÃúêÃgrãèï];~—É Ë“ŽøÍ¸4(Âæû"}šu)4Wš†#¤é¿Úýý¿N^7“XÄÅX/@v̧fÈYl³·#…á{Ÿ¤¥Ë€ K“ìá5pçùÛ¤yx¡/ .lÏ"„¶þ÷Y£Ïˆ˜wF1iMÁÉ´žº–G÷Ü)7LÏvõt]¬wJ÷tP†»¤VaÝ”Š•j$™ŠX“…» ÛâŠÀh»&!ôÒ°]ŽÛð+ºZ$«aôËÝ—•·Ôo’Y¤¯P»“nÙçÝ´Ã9Wi8ŽíŠù€9ÓsŽ¾È©Clß0…íøC¼È›%Y˜6BdL}¯U¦¯£¨TU•d[–u’Sk­ŸØ‹Ø9tÞ¢PŒ»÷×ïX}ÈYëÐUMù˜<’ }Ò½eYŽcƒûhÅç­®ZqƒÛ9¿»Z .QF¡Êª¦b»àQ±}ŽÄÖÈ&óþ;´W¸ÕB;š³Éšùeº]: Æþ’³(›½Êê@û´ *ö¤j¶Q*c1&cÄrÊzRµuÒ0ÁL%Û45KêÖ¥RUž›ÀrTŽ ˆô vªQÑ<Ö¹!tX QÇ,À³xd(=}ÀŠ;“ø®lÔRÿbˆ¤5ʇ.€Uoz ~”†!ÓmøÖH¥,ÒŽ%Aš>±é„#4–åÙr:×'-Ù!©wÔíQR¢/6_4ùj0:Ý•"Û$5UOU+òqÍØûöKûÎ“é˜ $Òñûþ&_#”'!É8@É„õ3;äeÄ6Aö@q¿{†Ú“ƒYaõ‘ÖfÏ€V ÒCðD5«Re!£ø„M‰Oõ¸xµq²Mö<·‡©¼Â.|#ËaHS°*ñ¬ V½Á1ÀSúšïÈú±§–,Rª„:øX£°Z]%éXܼeͶ¼tU±8 ¶${_±üÈ©|yŒ©{ ©ÉûJÐFÇ«fSæM Bg!pVš‘9U–MÚ¨@ˆîé¸À¸¶Ë¡Ò³ï7ªY0"Ç€ƒ¦Î÷`:åišèÝ·½í8˜Ý¶åͽwÖòùðŸ;Ŷ!­l«'Ÿ2ÊjË£é¤à‘ RyW窆 CÒ8ìËp4m0û‘ëY¢TQN·5· G'¶µ„…yV—y ý±MBRÓ-‘W5ebºÒV–oض|VÈ´*$±Êãú`8` áò·IÒ¤~jù§Ç>˦#üžÑ8JCöô'H+P;‹Õ媄ÚÜÕÕ»ùi¶LŽ[A‰ÚjÒ l“’á90¦hý¡ÕÕws½ò¸ƒïmÌcURÑÞ/ ýn”*n¬Òn‰^kºó¯߇O‡¤¢"ˆb2ͳeê)™æPN%Â1= H:á®ßÅ®P,q’é ó’èKÓFØÃéöòßÜ^šÃ„ô LÓŸo29­¦^drú,ß5<Ǧ'NDÕý‚’7ÎJB%>!qÅ2”Q¬‚z~¾‚)º€BÌ.»&Á)tŸèj!ªZa6¨ïº6^«ÞÒ<Ôz¨ZöcɆ{ì°K¦ø©¡qTÓ%Š·@ÜGÒx>µ–]E£à`j£b’s0;[^«ÞìI­©(ŠîÏ4?Ð,Œã$L¨œQËTd ´ë#,:´ŠEA°¶ºñ`;EÚ–A|?ÝýC߸{ÿeÔ9ÏÕ55ΨC*왢a”ÓóÂz¶}Z£‚Ó®0T¨ÜºZÓl.,×°)eËæ7¿°¯OAûÉsøÛç’àÜp¼þŒIËÖÿ·± WðÄš…¼©ZoHŽeCÛšzD©i•A;ˆÏÊ{nøÒç=aËè½€òxûåo¨¨¯OØûÊ<˱<Òº+“}·NÉÐÞðI¹µô0ˆÊ¼(TDª¼Õ£K(:»Ï^Ѫ™T{P ý@ÕE^¢âÿÈŠ«Ûù±ÇŽ rIúÑEk •Ëœô[ †Ç º@öÍ"d¾Å\Ñ>7ðUKç·3 Â2¸ ñÖ‘ßý¢zH MýHU¢hÄŒ}úe6¦“áý†dX(G o>+Ê‘3…!¸=%‡8w OH!ZBb›¬]¸®-Û]´ ®%}`Ý^¡{ø”‰mfÖIÓ6À½Ö<ï sÚÉ—{àËS¤e¸RZNçäõJî-10¾°¸»è?ÆiR¬â4/ð…χçú÷4ßBBڗľ#%SM†HLèâsÿŒ>êÏ“×~l°1=à¶k‰áÛÞ0GQæ[Ú€Xç“m½´¹Ñ”4£ó8>uã 4LVÅ endstream endobj 17 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 9>> endobj 18 0 obj<>stream H‰¤WÛ’ÛÆ}ß*ÿÃ<‚ÂàÇq•µ’Uiã­ˆ±¢< á.² @ã²\æësz‚+€+ÛRIÜzÎtŸî>ýæŽ}÷Ý›Ûë›wÌaßÿöÝ5»zsýÉaiƒô—5iyÅYŽû?áþ}sõv}õf½vgëí•c;Žãa™2Ïö¸±õ¾Z7Œ;ôû?\­k\ØŽ« êUâ°«_ããÝÕ¿-–—­¬ënß²…çñÈi›W%.×·X#ždÿà‡k¶àqh1¶Z±ÅÖ®Þ¯¯ÞßôÓq¸9Îܕ¸‚;-L›GI÷Øð§Y¬|K,VÜ€Ü]÷‹¿,VžÕ¯·…¸WïâÚ÷"ëÏ|©§€bGûvÅmî'@þn@†Ï]ì€ßz¢ŸØ’úª7}£~nú¾Gß®è†gõ?³Ît'9å·(´¹—ÄÉÉo©FWÐOh`õXk³ïÝ/úŠ-Ü$±¾¢7 Q…ÛØÏ…;ôí'q8‚]HQ÷Àäs+ˬ¿ØU¢Èßµ^{iª?Õ †|kàÙ˜éލ)MÎlj)ûu^ö‹}]ÝײiàQÆk»Ú<ª¶Ûáí>ûšYÜÁ$îszNö¹DIsIq¢'Ck®SCÿ\ŸQãoúƒþÊ34æÜq­?mŽs3ç§ãEgVõÄõçNîa$‘ߟ¹~æj䲿¢Ê°Û²eV.Rô)ó¶ªÙÿ>Ð;· nݱìŒ7œ0l©X¢JžÂG¯/îüq¢yúñS!OæÎÄc;ð’Q0éOUšÊ–‘ý:“…8öë¶2e½(ªÃ,¸ør̉l‡'á XÛžåSÕæƒæÀ­®Kwg¤¹9»r^áG2Ï ¨AØ~’„*atýßÉöå\è_:Lš{ÞŒG‚س“( ú¶~ìîÝÝÊgHyYËŒ ¶ëŠ6_òI£æ[í©íÚ XöCƒ'¬í?æK&Eú0zùI¦à)¬!¼B;èe^œ5,«+óß:ÉvrWÕG¶)ªôÑfì_e‘?Êñ>´ÜIrŽîØÉ`ÕsɪËQ°é6¶Ë®¬“5v  4yy⎠ã,Ùá!rzHFgü¸Ì˜a\>Ë´ke 7[…̘eiU¶"/•;?Ü~ZªuU¨M^zÂum/ö¸ÁÜÖ¢l ã¾Q£¢×Oy*Y]u-Ìëo¸'ÃA¢qÓcÜsHË#«ð¬Æ~M; NFuî‡X†=ÖºKµÉ&}ÖßP•σ‘±GeŃŽò"ÞÛÑŽ\²M×®ª%1í„“Û\%ÀÂ/´Ã!Xƒ0ÕÖÄΞM¡Qèt5éGg wm$º¼YŸªm{µœÝaZÍe>Ža»ñå´MÆŒ£>µEØð+!c½•©èšS^ù*\eE¾ßíÁ‹ Rä·êù ª¬cÄ>Eó¡û­Ë‰¿¥<°¦÷Èúîý5…ç)ÏðìÕ|#Ëã0#dFýoII!šFîT]:»ªié bSq‹ýX]¡kp×”qìæF>׋‚íÅ^Ö«ÿ³hTšÿú€ÇçNñO¦cn°+ãcè?Vz³ÃPîË|›§¢l™ÈžD™Êå¹ùèTßx\Ñ_#| í¥màG2ÜÊ=P§p|öí`re>cSaûìºnsò){kºý¸ªHëªYžC¯_ˆŸÕ°,Ï(pÚ[ä48aúKSí$åxSºÍ»ò¼ElPÉ%Ê7ŽG²b°cT›§ñœP–²gØtŠ .Æ’©¸‹Ó‡MÕÕ©ÔÁg*øíçXãžââpSj•É3ÆþĪÛQ-{-ëJÈîªxÂu-É+ˆ{ÃUWdÔV·$ŽÊ†—Äço0Aì»2}ŸÊ¹=šj©ƒM^ 4”¦Ñ@¦óîá{b×° #‚;g©kƒÐ;Ä,WuŸvÁé]zªí‰zÆæØ?èb{°ƒÜÜ;&I}PÉ÷DkX(U|–³=Éuì0B®h¼¬Û7¨0,«%Õ†!Ð_Ž}ŸqF7ÿ©ž¼2&Çp;ã‡éƒƒFÈ o›?Vþ|¤x„‘˜¤lnêêQt D¯°tðæÛÙÌØxI{±kCÍÐ,y©éLvÓÂkj—ȱÈZÝ4×ãÚј.Ý0TäÇ-i§K+ŠmÞ°K±r¿wMB¦*EÚ²é7ºâ€JÍY gŠvoõ,{Ó´êJ ¾´ ¤Å½²Æ¨s‚ØoE—©Ž ¦UF _h9ÄÔ‚"ÝDyž‰VØ 7‡Tç&/ètH§&‡ðÆžù°ò|ÛK÷,ÒB‡ Øh¨ùch]êÆuD:–|†Ïrr7,Õ­wDϦU´œM7×ù´ôÛ÷’Ä»HK—_è%/4îÔ&]“8ZÉ6¯¡÷ǧ¢–’aü„’’µá!bò‹¬wó…áÀ “Þy=Ù?Q‡Evñ®î¯àÄí^Ô ™,.*ÁÞäùì’õ6?ˆ(XFæYÌ=¾t"*°Îèî…f¥™8¾knޝÂ\‹=„7:D[¯½©fë½ô`¾ÃÓËïÑ6ÚCB?¯¶•ªçP²o­ñÃIù*Š®—‰íyÑÕ®·TRýìc×ÿ¼X*†–Mþ$5Ñór[Ègš;¿–cÆðôpp‰Ôu Àh²:t7»OOŒ"%Iƒ¤¹0@ð…¾iЭv'™k{Lh…éût‹Ç›aBæçµ•1;N* Ú@6¤ü£I:Ð<ˆz\@çÊýý©ÊÃÐöPP’Ë©êͧê—õpb—À‡–JÂ@çj¯#˜I{ɼTåLÜ Ò·]]êÀ}„¾¨Š’}"îMI•Ï]²_!„¯ QþæQ ¥™ÜavlkÑöZ|¾Üž÷T‡¶j‰}?ÞüãzˆâJ£òø!7ÙFØ@$)ȈnC‚mŠ*}¼Ð:PÇTšU-÷…H¥çuwÓ7³Õ%á‡á!‰œó nòݾŒº»¿'ÏÔ¢löUݪôjvª3"•MÚé-§R Õ+ŒÂМ·–² €˜ƒK> Õ4É•¹ö.¤±z1LF—RfšŸBþŸï2Öa†èït( H”~D·J]º¸a( Úÿï‹ãP/°2XçKr¼SpàÖdÀçÛÿ¸ü©7¦¿këAù‰¢ÿ‘l]##¼”Ïõ*-D:Mò9↪ÌôâÐÍ‹´ÈmK¼3P7yžr¯¡ÊÑkzÄïßS²­­»Kq\m†Ó¶{úôe4ö_:½õªí )­‘Qaü&£%1ÝÜW2¦¸¶jFåy-B*Íç{rå8 éú€¤‡4ÌQo5š#.LbmÖ‡ÎO€,´`£ endstream endobj 19 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 10>> endobj 20 0 obj<>stream H‰¤WÛnãF|÷Wô£¼h6/"™<žÙ]'L Øâ}h‘-‹^69Žòõ[§›WYd6ÈP´tºú\ªêÜýȾùæîãÃã{f³o¿}÷þÝÜ=<Ù,VxAÿ17œ¥xÿ¼Q7ïv7w»Í8Ûnl˶mŸíb¶¡G'b»WúÕN1nÓ¿¿ãÓ®ÂËvt@óÙ,ÀS€_#N~³JÊ<-DQ³CYå¬<°\(ÅT]VâE²²`ï?<<;Žƒ¸Îž‰"a<ܳÛÝ/‚3@ð5dz<›Ó‘»äf¥Îª–¹biQ—¬>J&E•[]³×£,XZ³W¡†¨—ÛtQ7Üâ^˜ØÍ锹LØþ¬ƒÿô=~ù¼r\îoT}Îäó-‹EUWi‚»$©úÌ’*ý"-}Ò‡Ý͇”÷¡¼«Å4ׄÆ&W²¹ këG‘«³9Ø™æ£"r®Ãã‘SIpˆkE¡9D?Ñ![[‡ßá’?¾ÿqÌãNÏqþ— xžåQ´]¼€w=3W[àÚ!®cEQš,ÝÇqY%iñÂÚ®8¦Ôpgj?úH¦d•Jjœ)¢§“(zPþ<(oòk œÐ³ÂÀ ¤‡2?5µ¬Ø}ÓZÆuSÉñÉý÷¹åèáÁ˜Y®CO'üë²JÞÆ9Ûþ…œ¹¡ßžÓ!\ëÔPõ==,EY3±42‘åm9ºk:3q,•²@Ö(I)WYùŠ›gåK³s*³#%˜™ÝþÓ§-f=éf2è&è„™bÿ.ÔPO²"NE,»ÂzÎð½yþØtaǰŸÀ71«4Æø²Ñaô"ÕgY#õ¿6iدb6#<´¶|‹nh©©¿â}_ÖG4ãËéÃ'ÚÓq‡žÛÇ£5éŒKUë<#v{Ñ¥!ÀßóSÖÃuGù t~mËsƒ®Ì¥¦ºçr€[§Ùì†ñàöÇãï¦xk).!êÈ-¤Œ‚oº8cˆ¨|ÌâsœIV§¹ÔÈD–2VÒ¼5„¼¹¬“£“L’I·R' |ãJ¯©ÔüÎÕoL>fè%ø“ÄFD'[æ¾å‚Wø"y…óƒ¸íïåÚWy¢?iëZaÛ)i4)#ê:ü-y–ð¨Í\\ ìg2).+4ó!-t½^Î .Ú¼À– ¦´PuÕÄu W²^/ Ú8¡RK[D€¤s°mjôµ,$©¸¬¤ª‰¹G—“¿Õ÷úeÌ®îf×r¢Èë¦,¾by“Õé­ˆa«JЬ†æi…VDwwr0OC›>òýA䘋 Š)2Uö8×ÙX™ÃéTü’$ŠxòþéáñML²5ÛÆÑ´Ñ\Óh£ ¨ƒ·M¦åÑç¾ÅMeH«´>æ²FK=µþ‰èá(*“F=ÉZÍZûÏ{RFc½0B|Æ]e®+Gx!˜ƒÐ)?_ÎnÜ9FP(.Á¤1ìv³]µfA÷x«`4=%‘Øù$_ÉÑT×ðŸÝw³%¶‡^y4÷O?mHee&)1s­n7îŠIhA½Ù߯ôYW€è ý y»á+-úû·›p…ÃËý"+ý–&í;†WmMÏË‘Á! ÏØúÚ•È^ÅY‘]‰4Š.}w`Øy㲪H(²Ïñ{uIŒöágˆ@ÝÖŠ˜@ë#+ˆ ¯.²úX6/Ça„ú0£ÙûyÕU¥,… N0Á)$0kÐx³ú+M.¦žÿýîqwq%ª+rûž°CUæã ¿a›iÊÌìÒ’P5E,4a’!p¶‡o2›»͹J£Rh{´ Ë0(Q—8\¢§4aòK™5u[ê ruÉc›6Â$g†°ô-e¾ÍàšíïFƒ‚e¬'¹ÙS¿ùT¶957jM¾‚'\Á<èUßekÐ<¾MVZhÛ£¯ü„@köN4I ¡Þ0õ)Ê[…jB*MŽy’…¶û‰}¯&{Ek 4;kðå¡~•öÀ7VË„“ü¤w -ä+%Œ]²(ÞdÒþ%òl»SáYÚs®2úÅâֲʄÒÝб\k¢iõq$Æ`ïÉ–¡Súx÷©%ûY(ÿÇnçnƒˆÙÈ×—»™ÑºvŒïÉD¡AîNÍ‘‰.Ž~Ä—9Y ý±¾uVÈK÷¨½ÊˆVa24íÒ'J—þžN™¡Û¾æp¾…þkvËWY-ñ·÷Zz4ìzÑS»¿ùȹГ˜Vмõ>A‰œkZäÎݰí¦Îƒµgæ—Ê<±‰³íÞE÷û ‡c)Ô‘Í`BFàU’z¼y°buUfžDÕ/ø?Mäl[`&>RZòY³›·q¦>,/…ÖuóK©Áarq¤jr$éžä+Íì²i[¡ë¸föøig2ª}‚HPÁu ¿/Ú½ÈB’­^BÝFƒ>5Õ©„`Ÿ >(h¦«Xžˆu´©f¯ä¯ 嵄æ×Z著dâ‡ô³v;,¢ûíVdã×óŠpݦ{Ï·ëAãz;7¤š¡Ên%E²I ò8µsÛ—ðšèØ‚¢Ô#Ù;¦ë-móO1Zâ~züÈŒ3Ê6ít8 æLŒ(kÇÑŠèt¢é]XUÛ˜?Hd/Ã…°~Â1¬õ!”¥v0Ô”w)ßzA‹ºWð ñÓJ³P%‘ùZžÔW}.³µ´Øµñ'àQ~°JKðtDÊ‘NHõuÊðn>9·ÂÈq:F‡Ü§…@b»¨ÄZ¼¢„MŠù†´ÛcÄ“X“SºŒÍ®“Ü_Ø'GöÃ}ã>îY¡Qã·œôªK½¢? |êYR+ãµÃ·ó®Àñ–\ ‡µëäìÕ‚¿è98J åVŸ3°tšŸ2}1ͧ*ÍÓ&QO)BÞdõWsø¥Àk{"Ý Ú‚”$×4Ò1ñjb$…(›VÅë e‰þÚÈãÖÐf¿ûŒÄ«W4ê¶þ°c{“„ã _ÒL¾è}@-aw¬ÈözYoí¦2ÍјµçP)%B²lTvþ¡1a'Î;U-ìξBvHƒ+¨¡l%TŽ‘¡\ÁªôáG3èG-‡ø¡Ý'Úc›ª¢¶Ü‹â³æ#h%’Žm4Ñò¨tYær³,ê:î„;°•¡Jk92)À#º¾PF!&Õ„R©¤1–Þ­6žt´‘j‘>¯î‹Vl¤}Ø÷kãMè€×XÒ™.ð„uz19ºÊà5>°¹С‹«Žc_zµ‰®œÙäºÚ}j òpUë»y¸'ÆH0~ä ¥êCZ©yv@é×öÛ\£Tâ¿*.Ö-.É/ÒÀ4l –ÀŠ\%$°x11‡·!ÍH:„6ÛñµÍŒ õ €‘šôA=  f Yàä a3cq¾B° ,á}`:“´Eˆ‰!¼¦…*†Á%Rq 8|JÀmPvDõ2, üåE˜ endstream endobj 21 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 11>> endobj 22 0 obj<>stream H‰¬W[ÛÆ}_ ÿaµ¨Äåpxm‚¶×n·hêE,#¼}Q#‰5E 3Ô:ê¯ïù†W­He]$~ˆÄ¿9ßíœ3wìÇï~~÷pÏ\öÓOoïß±›»wŸ\–< ̤ŠgžÿÏ·ææíòæn¹tgËÍÍÂu\×õØ2eö£‡ßèµ¥aÜ¥ÿÿß–_üŒ"ÖŸ—EøámÚßÌÌÉTjÏÖêYååa¯ŠÊaì¡biY˜ Z³rÃ$3Ùþ«ÛånìÉœ÷Gt´ç:<亾™±½Ú—ú´ÐÊdk„d÷ïßUò ØÃÝG–g+-õiލ_ÕiUJ½fxÑÆXp‡ûId#íË"«JM?þPêJË‚=<ÜþåŠL£ö+ûÍâ#d’oQ¶‘† G:Á´:ä2¥ò²æDüÕXËà÷ĶžçA˜4P3BXH€BýpF ¡7¹J+–¡(;…¿—[U¨òhØô,-Ú°CܥζYÑæ¾Î4¢bÄXVlJ½—U†:€¯Y!÷jÞ«[î´pÚ…NCóHdœDçÐ>Íë…õ-4Ÿ‹™©àIH³ê£U"âMƒ»à±ip"r NüqpÁ%¸–*P½Ú›NŒ§¾”ñi|^è€ããäE_ÏŠÇ…7U<æX¡Æòb ŸaGÈâL¡N¦Q_rÖ$l0uÈã8‡¼öÀîù+€s÷¨·+7‰coxø࿸Æ(••v¶Ê Hòk2ø^“ÔÁ)Á*ÅÑu çã^é;÷ÙÉ<Å‘°g}°þÏÞRìÍîd/H­özPÛ…ÆmWcBŠÛÚÐd\8 ÙÔøPâ^@%on*…ú­úí-ì0 ^߯ºàC•¶‘`«Üìm‘m2@V…=,í´ý¿eÖ"Ç…À…×{4®³Wül€Oê‡ucÆÙ¥?3L—>×V³ëÐÀ¨YFthíɤëY´1†°só,uf¥5gku@+ìí¥¶SÅq¿RúZŸÑf¸ŠÎ=Qºtø©ËÖH—GLšôP|BŒ_Ñ]ø\?ˆÿzw¯(êëÅÞÇ÷DÄÁ¨Øó…׉}]‚ת=ÿCU$‰ãóØÇÀ‰…ß3 £·f=FÐf†m/N°d›¦àŽëk·))ƒ£æÑõqDüà{uÛ¦ãŠ,¾ž:E:"ŠÅK5§õŠ?“^ðÈf‘ýoÎöҚǖ™úZ™É²xWdðõ.B߉q1x)à R¯CúBIÿ†<3‰rBê¾oD€‹XŒƒålʘ¯Ïê Ê Áü}:>wb{ÑU:ð&Ääû.UB¸Ž—Ä^Íûo6¤l{¬;昦0«›cÎNJêÁýó²ÈâL…Q&ƒ7Òz;‡È¦åáDµ»ß…¸DÈÈóƒ6Ä;s‚3CqDák@³!!Jµ$Nèyç’°9i—D3õxÿ¸ˆZ8ä²PÕœ=êrƒœ0h“ÏØ?Ô¦—†(« ÈÊ÷EƒH=kõäyža€†ë“Õ@õ.J9Qä­¦Âç"³®²¥ø¨Õ–Ü\wö€$íá Áá‚Cgà—ÙÓÌ””SÉp‘ÈÖŠP® =•¬¢ñš3sÜl²†Ð’ U{쵤è­ï©Ð•éÇ2űÛöºuêx›R&"¶<Ü•ÑøÆTÕµè"‘7Õm\I]t;Si¹?E٢ɟ äS]) éîµ …¬Tõ”yÞŽD“Àyƒr x}€—1T$¡@Ú_ß:v)&p1x`˜þýfþ™Ø±Ð^K;ÞzÇtTàÁÞù{€§±dŒoñ'†MÓNc ÿ•Çoªkèð°µüQÞuÙm7ù+§¤mµ!¼Y«+9¸T48Þ¼Èý·ð"\ð #1jšuèuž%l dÄCqÂâÖ€N‡Oj´š a¨éCUˆÇÓ?àô’‰¹T†>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 12>> endobj 24 0 obj<>stream H‰¤WÛnÛH}70ÿÐòB¢y“(Î2vvǃ5Äö!^,ZdK↷mRV¼_¿§ú‹-*ØphY¬>U}êÔ©›ÏìÝ»›‡Ûû;æ²÷ï½»eW7·.K|@ÿX“”WËðùßðù¾¹úusu³Ù¸Ìc›Ý•븮ëá1aôèÓã‰^Û4Ìséÿÿâ·dž¯Âá¿Øe‘»vÂ(Ž×lS\Íî˦•Ǥͪ’=Š–}É!kEÒ¥`¼LÙýÍ'öøÒ´¢`×›_}Ü\}|  =xÏ‚×à<NÁ"@Àê Vqà¬>T¦ûç÷Y/TÚË©CÖ¾ãª,¿Î>äí¡:î,+[Q¦"emŶ‚ë—)Kª¢æm¶Í;eí]/Ö³ö Øç»Ï‹hÎìcL¯Ë*=&ÀYy,¶B²jÇ®ÿ¹ùýÊÕÕ_xŽÆÛÜ]ÍÒl·R”‰h~žÊ2˜.ß¹¤–±ãqì_¬\8Œyëÿk@]¹s‘Õ…nPäw®­Qçà=èQ©‚»Á øšsžÅ«®;½š}8¶Õ"C‘¿gåÞaì¾dƒZ žØ–—ߨn…(*JañæR ë{NÛ°ò…8ÊÞEgRì3S6ãCâ¹*‹ éhSÈ…‰ÙÝ"Weþ¢!¹“GÌU3d]ภ(¨ÞÚY‡¨ÆZ¦™Dáu{ý@'yšJÑ4ø„2v]ÄeU#µ!‡P鈔í*™hw§˜$œ)Z,D‹É~õ<'tÝõê‡Üxs‡#j|üNÝÇŠ*@Ý´•¸»e;6ÈèãÃý¤5}‡¸Â`x± Êj)jŽŠ-Uª**>ƒ3{® ‚ú–:è°Ü¼UAé}°•—J>¤<Öí¶V…î°;"CÀ ×ÈÅ-/¶ä÷ذ/çìtÈÐ &¥T3¹ZfèˆC¬ç³§Oó¬¼ÐdžïøÑr©Ñ}}›+¦iOmŠö«eÕ^/Â1›þ¨j58«¨ž)¥SÕ)á+Àú o8ëGOÃv²*FµÎó¾Ø ­Óf¥h&™½úCÌ^ÇЂx½dËUè,CÏÿ1¿_óf’Þ[qàÏY%Gün0aMu§ùâ»N„ž‰©‚a8sU~¡'u+yͶǖ%¹P4ÉZõò<_lÄqõMqŒ·q¸mÕ üéfôbLòpmÐê4k!¡N…’ÀŸí2<þþv)1Ô‘Š“öõ!4$º—¼À—y+À–W Wy»*é–³1Jñý\ƈ‡-ƒªe®iªu!ýaþæFr„t(òÃq•T¾;g©¨qфǜÇÈžÓVqü ¯<ýTív“äŽþOƒÕÓBA˜½‹fa=m³Â¾ kïòQî õñÜHÕIã“ïûò|h,“ªl9:eMUˆKÀu<Y¥§fhp›(ò, Q¢ŒàÇ.‡×ØfyÖ¾(½B…QÔî2ߘŅ ;¼ËT˜îåÁ<ò.ç{sOÛ@‹Ë†ÚñT ¢G½ð ä¥kÅžÐÔÇŠ®05¦ Èý¬È Š!«<=ã[tØQ u®Š ø—Šç,‹¦ .¨§(9œíMš]¶ZðË+»¡ ÖRœØÝÇۖטß—¢Etý+ Ù²,lÐ!è]öp("K³æÛ û† ÚŠIgVÇËu×ÜTC#u‘$'ÃÅ•'̤ßÉ[Ø€CœIu¤b ·Æü)sr¬ÙN€¦ÜfP,$˜½é9ÿÑVÃØ Bw½¾ØÊžû'V¦þ¬ rÖ¾»ƽŒ™ÜT£;Bñ« ÿL4Asfû¾ãEAd¯i_ªî)Žy›-rñ,óÚâߨ¢ž¦JiB٘Û¢}£àDÊÏ—Dà¾ãþÓìÃçû§kVÕÔÚz•Ã'Í¥G¶0±ÁÞd k"¥o¯D+‘Žš+ »1j›é*`0˜Xs¬ëJÒoƒ5ž]°h¬Èµ²h‘ù5-›§ëù4߉Ižo%JMTÊÍž¬ôî‚T,Ïq=ßß­&ëPÂ}Šlh™M™©ë¦Kþku”ÝAó Ò×°ãÖ2ëæÚÑÛyu¢`bga`^!ŸFÛ[P1†(›jמhWqï¡Ì]ÊúŽÝ4IŽR^`‰«|ŸoŠT3ËåÎ\$•®ºÒ6…i‡^ê͵.º°#%2ü:ñæQ$GPÅÎ{­­oæê…ÑçæîÌ|RnowF•$q3¶û’>âü%äÁ[N|UÆó Œ>eQVmÏzJåzáy3Óô…„KIžC^2Ÿ¼Üë]×z5oéè]㜠zctß.¨ÃõGK ÚsƒÐÇ$€ ’ò}=\{ÁL\{3u«ê7ŽßJ¾W 8 Àð$ót´õGO |ð'žNñVNÅëW6Mm°f1åz-Õž*§®“E¿VÀû¾gåœ0”/¸­ ú1k³ÅMi;ÜgM;47o‚:"‘^ê³×>ͨû%äÜœª$䔊@ø;H5øûp÷ÒNT-Ndëà/^³¯ž-ßæ/0c#KGéõŽb üÃ(û^4_¯‰Ð¢`û¶íˆ<Äsrß‹j¾½4ª|fN]t„6æHàŽ[]¬ŸUˆÛ¶ß¼G¸êâyk 7cÜÎQˆã…g…‡`„óKÞ1rÂÀë’cb·#'ýL¶ ¥–5( ëÄw<™\ âM,©Cï¼j¥Ð+êNê˜ð¬ 2)€†Îr庺 ‡ˆÏ_Nû¹3ÝçûØv×îåu mp.æp»=Ö[©[}»~%yÕw)jÜ®Y“:’Ñ÷.Ìa5l#X@ê@m†rNÖ[Ñ]-$z+0D«#9€N6è6æh+¨$^§YðâCõižrx+êµ-o°k¤?½DN/í8ß”2Øü¹ž*˜?v7åR°Äýç~w˜·«¢uAy¶•–¾ø'‚"EóDá—̪ÛàêØ"x¦Ð^ 2¥©FÅ'ó„ ¶U.Ø(oعñ“;@jzêÕÃhÑâ'Ö¤çLV¥v úŽ1÷­¤¿i Ü5“ö—¼ipY•Ä´Qm‹„3ìì"÷⸖h^Ù…A‘“V£ô™—TŇªÌ¦‡Tf¸Éáxö¨>gYÛ\r…XèA_µº e¡Lo¶<ù¦qàs£V4ºï»OO×àÆeúK ÊJ^x‘jQÀºH”Wú]EÜ‚«˜ÔÓ%Zt_¡ÅL4KuN]«Å7í)hìµjÁYõçÏÙƒú v@Ûÿ1^-9ÂPð*]jbL Híܺp_ ’†J Çwú/b ÚÀP^§ófŒÀ~ ää<îߤ͹1.þàÞSPL¥Õ2^|+Y]ñªž£iñ7kð¼ LéH‘^J -²*‡äR& ØŸÁåáÕ&I ‰p1ùµw³€ ¸~d]°ÖÇ@>¼çȧȹ»cW@`”ÙBC}ñ7¦E9ÏQ9‰Û݆[Ø…>æÌÙ6›a f ;\äªÍ=üŸ²³Òã so‡¤~+‘GT©Ø¡¡©9åuRŽD²”œ+»R¬`|Ï)o׎ã-ÀèÒˆ endstream endobj 25 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 13>> endobj 26 0 obj<>stream H‰´Wko»ýn ÿa>Ú­µ!¹ïÜ4@­{ÜŠqQP»”Ä{÷!,WVtáß!¹/Yâ¦N'H¤•5sæuæÌ«OðæÍ«ïïï€ÀÛ·ïîÞÃÕ«÷Ÿ d è? ²êŠ‚ÄçÅçuõnyõj¹$@a¹¾"!„Á2ó*…åAi©€ýÿïønÙõ½41öì«”@L"/Œ`Y^]/·>Ý}ZÐn–¿^}X^}ø¨ŒàhÎ:§sãW»¤èYûæÚR걈j\¸ ³Ë†‡¨ú% S—“8õb?MãçqȪmê|Ÿ‰Z|ZÖª%7•\ËŒWøZ´’ñäÓÑU¬=1êÑØ©.¿º®×À›l+[‘µû†myµ ½ë4YÁθk¡•°–G›¶¹Œ.ðE !£i%«L#mÃ+%[YW°nêÒ<3Á@[o´ac“Ð fß`&^è°˜྅Fì¡DÕb"8”ü׺ c[ÕE½9‚ÚÊu{kºÓ±lO¡çReh{䪭¥±.—ÿÐ集k¯™l²½lÕ`þ¼®©%IÔGÜ“:¸2Á¯¹jÖW¹-æ®Þí Þ˜ÜÏdeÑŸ"Ïêr·oE£ëw'7²åÅcLÁÖDq´©Ó®y¡lò ô﹚Ø?mb6ib ÈàÑ/1Ø®ƒmû"º 0½{_©¶ÁÒõÿŒÍù—±á„‰úþÕ/ðùˆY(]0‚ïR ÍièÒpÌ·kFÃÔ÷H`ê>3£À0›¡I¦… á§'í›d:Œâk+°ÿêJ½v½<3aB½öÌâ2O ¿gÿ™°²à’å(õ#Aª-¿!$Nô̽EÏr{6‡Ôà ] ÚC •8à„o°kE£n±MÌ8È*_¡™¸À‹â(êé­3`ºŒCºB+e;vÅž|+ö‹©MR/MS’@F^ʨÝsi8k±iLñsMnyŽD‡tºÑDYò3ê&W.ôéÿ‰>À2²o¡?Oþþ–7ùㄯ‹š·ü®Æ±ç&/ìä°ï{˜ì`¾ŸéåE?Ycn¸ßph,}1sÞ¸!5\¢­yfš³ï­’ç¸@néõÈ e Ñ-Z܇²ÝâÜ)å®nxs„›/ÿî2RýÝÕuÉ‘**\ÕjŸm¡[&Ÿï>î3ÓðwwŸ ¤Ñ-¬êvn»²)ýlu4‡­D“[žÛ˜°xzuÕ°zMuº‡pÑ›œl¦/×Cb”(àt í*¦åˆ‰[‹FhÍ «›¥×ÃÖÀù׿—Ë¿WÇ =Ÿ–x) X?ýýäðU1zÑòÜd¼lLת^˜°„nÎ ¢~vò2uÈ¿o·s‡^HpÌçÛùt3Ó~ÊM(”]dèÑEz,Àɶ.´¬¦€‚×=®Z\ЏVp "™à`b–0tIÀ.¦ÐØ ,¸ Ì & õÒèÀüi±øž¿N4áKш†ôhž Þá?O²z‚¯úíÉO×`Ïž>éf›ô³ [ôRl~èÑ„ÄñÉTìnX7"†7¶›?ß«‰[eÅÃüùt~,(óü„Z©ug¨`ºWâX#çýnÌê}‘ãSÈk¼ZVÇ™‹È÷¢4èùO“Û™ñ$Û¯²Ü—à³ÎÁÃÍí¬8ï­NY”Õq Ô‰Õq§?Ü Óþ­-£áî  (púSè Es ûª•…áÈýócÏt%¯øF”x a ›=5ä`b4¡ý-aí>g½QÉÞbG£,­úÄM‰%Ç”[Y¥Å.ê“põBÙÛ}d"|øƸk8’y¦OÌ:ŸcàåqY(±1È•ü]Œn†ìc’>ôòw…ÝÅŒþ&tÙñŒ2«UcÐR²ûÜ®ËÁÓ]Úˆ¢Î¸I’Þœxñj nv»IÝOúžj·¼Q=ʦ®ÜLG1/öq9ujöÊ}ÑÊ.¡>íȲïŞÌõâ›ôÔd­²~«³Óuq)©]ðJÔ{U×M¿w)ùﱈ¤ó×sh¸ËjÑéËG!t¼ª°ãd¯´†ñÉÁuÏqÄÀ°Â"®d!'‚gZ{¨âl&Aàwõ9êb B(ÇŽÑj-µ’‡—¢¨ÌZ1+x:‹'¥‘XaƒZ*µˆé„ Š ±6=mºG“‘i3}ZÄŒ1eÚÔÀ´oí±€ú2ׂ›o´4ƒ“J½å)èi ÆÜ-hÚâÕoæ&å~ݽ0Ÿqxê%‰©­a=„¤™DTZ‡!œé½\ %è’ê_›+Ü¢·<…ŒƒSYbéô¤‰ûÖT³Þ·ZÍøŽ?kŒ3Ò¦ºûûå¨ë?,—9T5Z+qŽ 5 ®lf$eg÷D O 6`;ÐÈC¾Q¼‹Œï•Ð4G¦ßºíTºÎ‡Óºq;ªäh,¡çwÄë«ã†žŠzº5櫊esƒTŠ}›;ù…]¾±þ~aã“Dó¢—9DõEvû 1zLK8ð‹ëVÝW²µ ÆÒoMüCF³ìxUZ„è:“7x²éͲoÌ$µc–¹éÞâ8K0½Ñ“Î/±N‡ìDƒ•Ä¥Õ¤êˆ÷V©·ŠÊ d2Ú :n ‚´GªÇh-3à»]!í6T–Õ~‡×i;r¯î”9´Ý)ØÓÔžá¨1 =Ô&ɲ²×ßÜÝê{qø½²w"žº[œ§FgE—‰û_–ƒIüEŒjžÌ­á)üw²Uð&x¾uØÄ&ÒÂCTÐ3ûàâlq“û~Ø/ ì/µ_-rñ(±vJØ V†ºzÙ„Šd9¯8;£SÔû ©#?e2ýDPê®ÌåÚ°p+O6ܹ¤¡XQ¿ï”•h:Òg)P(imŠþ „h…<ÝïLÚAs¡Úˆ“ üïešP/MH@çÙæô’]úÖ¾É õgï"£„é¸ ?Å/ð€ ˆ€¿XJ>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 14>> endobj 28 0 obj<>stream H‰´WmoÛÈþn ÿa>Ê­HsùÎkÀ±}%qmغ»qQ¬Ä•Ä3_T.eEE~|gvI‘z¡’sÛ$@$Š;;óÌÌ3Ï\ÜÃÛ··W£k°àÝ»÷×WpvqõhÁTâú ršŸ1Hðù/ø|.ÏÞÏ.Æc Œgg†eZs`<ÅwÇk:0–À,úÿßô¨Ä/¦e+[úSdAÀlÓbQäÃ8;b6€àx>@Dx f^Ã\`0Xçã?ÎnÆg7·äskâØñó˜3í òåÄ_ ã5ÿú¼°Ô ?²Lω¬PyñpÐÿ“o W“b9-bß þ“d™ˆ^‰æþ”ä±ø ÅR”ô¼Ç'ç‡} "3ˆBßû ãîzÁ´UÐ1WüÀ œÐ‹t¥ôØôŽÛÄÒ´°tв*SÛ£ =v‰ç›6 =ï(ÇI•9TT ÿ£(aºàù\H(VUšä">)^Ä|RW¹íM!£«l,°Kƒ‹Ñy²t}o`/x I…¦Ö9HQA1ƒj-ø³žÇ°µèo-:–²h4& f27 ”á$ŸÙ’WÉ$IÑmôP}/ÑCôŸ®Yâg1RåO}úýy±úps“¡žLNÐ5|eÿÓj³£“s̲m™aDŒ,¿µ¬ D4œwxÁ^zƒ!ÃÂõÄÇë2Áó$Ÿ§|ÓÖ¢@˜Ìte ¹Ó©—@%Ñ2ÝŸj“£Ë+x"ÄK‘‰¼‚Ë«§s•³÷¿ã“ ¶Ÿ\óåÓ¹ ­Q¯µé4ÖF»icyÌŠ2%Ìø”R‰¾¢sPäÂHrY•«©*JûɶmJòÖþA‘²eªRÕ•b²Á×ËR |Ž&E*4TI’ã vÆ4o=¼>ð2©™¨’)ȬDföå<ü^ÎûJÊ =¤ÃÐñ¾—øÃ¾îfîòm»EC¸~ÿ ’ôpó¨s_•É|.¨CÚèíÖ ]'Þ #»É½¹Ô "Q!«¶‚¨íZû°ÌÖ AE«Ì,’ùÂK!È>ñ隟)[ûQøfèÛN†ÔŸuO‹|–ÌWØýØ@½p3ëÕeëfEvôß•í%dIl¤ÉLÀÍÕòÉ”§i (6½S ƒ*y>Ýà [<;•ë)~a¡bÕVïð¨K4Çd I±™o<¥i:¡Q‘/p†Tí ÚØhìv‹Xä|¢È¤e¹ZV‰ï2•Űϊ<©°T¨Jplk`Ÿ¸#-ZM·­–8³¤D[Ãªè ¡K†³"M‹5±Z'[ãÇ¿ 8܆ðáÜp·çlðˆ ]ÂÕå'Ä]4˱çÿ~÷pKt~þñ‡£UÃ^]5–mz^¸ÿƒV½¹¼8Á6-*ì”­b¹¯Tí \y† ¶A*”, G·¿~R™]žÞüÖßökÃsQÚÚ¾òûtSÖoO—÷#X¦|Z·Ä²,æ%Ïê韆»í€¥„'“‡ÙAAÝ%4Iõ/ã5b´«ÃDÁÑNŬRgÛ’RŽæ2žãeXªêª#¯ö'Àyu|ßt]+°O$À8€ÌÙÏÀ—n ¨?ºýWäéFIXBÏ…dvn¸ú¢YÅ2=q“œã”Ë;ˆ¨žê4­ŠdüLjÚ¼!©tÓΟ['\×1]ÔÕÎIÅÊN쇵z䇙ah©íöËàç²ÈFêb͈ª^±^I-Ïjh5`£‹;àåtqnxƒ¤Bq°Bðh橲ᙀú³’6êÌ&«ª 塎#HéÐv «AX a$ T­q2›á·ÕŒûw¤Ræ³ î¥ãRcAàþA«(…ƒ‘ëÝcxRké’ÆÃ/IL ÍQ#‹¯KlN®–êÓY*¾êåg£¥È¹Á,¤÷mHÊÉŠFÓ¨RËWV`àË㟭RÝñx9tB”É‹–§’ÆÚ4])ч{æ]œöàá“'ò–|ú¬Ü ß?áïp}{ILŒ¥ýx,²:‡Û÷÷„]]„Q•8îD‰^ÈÕtÑ”ÂøÊŠàúæªâK%uô[ý[$.‘.s¶Ê¶Ü:ûð3š%_1‡ T[:œöÚ¡®—~¢5¶–»¼HÂP&ó\ï£h.Dz-}‘wÛZ)øøÙE{ÕÛ?ö«Å´íÛfZßÝÀ´Ü!žãÏ%Û”÷âÉ¥h±ì¼ËŸÞä¸{m̓¬¨œŒ‰T7HÓ†²)AVÇ4œ ¡o7Bwƒ¬UÌŽ´ ¥x¥w+¸¾{4NéÜÚb—Ÿ±Sû3ùê½Á¶ÓÇ1â|W¶°Î~*?Sk< >ÿ†\/g Æý# 'šçºÛÅdÔ• ¥‰Ð;—ÒÁTðÛLh"ckugìèÔvKn¦LRAj‘ÇãF“½SÌ«a¯VRv>¹PëŸs _ª4¾sì¿£"-«ZÝš<йÆÖh×gt¨Dm‡j¨e¹Z.‹Rõ‡†áÇ;ËDÈ~]IÊÁó¬f¸SèÛ% ˆªlmf¾ò.2mÇñš€‡¨ÁJ¤¡UÊKL ÞÕÚ_ªÎ‰¡í´CÛ;Ú,À=PéüÁã[7ƒ›¯•È¥B¼ï¶+K_K0ß!rc§çЉµeŸxŽÝáÙ¦ë+lñ’Ë´Z(ñÔÊ”\%œQ|’¢'é¬Ö8žoú9ÄvL+`ÛÄ¢ÆJdá¤BlJd•Znoc'efm²[‰$%^Št•éu)-Ö wf<Ö²8ÉW$Ûqm)ís  ‘•" $Ê\¬Iy‘i1×{G-9”,¡b«ºLrPáFc¹ë5‘ÔÕý¯Êå\¼¨)btG aŸqë‹_N vã2»™ã‚7WäÇx޹¢ç"×áTçö>ùíym·ëóñӸȘvA‚j³šY¦ýd¡é¾ßôk»Ø¨å cžÔ…î¥ñ¤§‚HéÀ}>à Fí€=*©Ë3K2 I ËÓSé¡Ö)§‘b>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 15>> endobj 30 0 obj<>stream H‰¤WkoÛHüî_1åƒDó!Šä!ÛÁ®oW‰±V‚ëÃbDެÙð!ð!Y÷ë¯z8#RO_.0`Ë”TÓÓ]]]}óÈÞ½»™Þ=Ü3›½{Ç®nîžlWx@?¬Šó+‡I<ÿÏ_ª«Û>âþe3‡ÍW¶eÛ^ÅøìlC_˜U̱éïèQ‰,ÛUXí«ÈfY‘E!›eWïl;mÛöÞÏþ¾º™ÍzÀv@À#zé9ïXA4nr5˜6i-We‹ª’ù‹…#7»{üòìºnÅâ¢IV-y)Ø5  çìÝ@!†–ï:1YQnÏöpó™UͼÚVµÈª![%“y\ ^‰¤ÅëÆ„4ÒP#ÇrÆQ ëeY4/ËUSãËŒ³L‡üRò,CÌLäkYy&òÚR¨gW§T…®2Ž©ÌÅÌŸHó$œX(ÅØÿ©4?Þ?ŽpO’Q‘3t/û’ËxÉó\¤#Çïò<î’*H—²â&Ó^!-ôíSÕX½ä5[4y\Ë"G’é#¹‚T@ú²-¨§2¾íçœ*y]i*zaΛŠÕ¸¨D"K×éöd!QGÏ<i] 0цê·ÌÒ4i¹1d ±O}¨ÿºxÝ×5QÀýh^s¶â¥¬·,•¹¨èÚâµ.9So©g겺ÈbåÕ!³¾½¨©f WÚëJä•\ë76`Ðm%J¹ZŠ’§àÕÄK Pÿø £&ùýÑqÎÒÖý¿iëE–ëEÁ%u8á=Þ~û:ÝUÊ4²f½"ÿ 0bâŽbãN ðå…Ìyʪh„~è² ì‡š•b•ò,£7vëÃú†º ·Ÿp™ËZ[G]Š´ˆ95+VêÏFÖKR‘¢;â:H–ë˜YU¬À”Zâ‹¢bÛWzL¡´ÃÖ œ¯ŠcƒI:\øÇ´µžÇVûV¢hDj•^ÿTlzwc1_ñ¹L¡aBy ò2Xʬ9/7Ž2о™:5ÏE¡\~&hïð°åÂPEÓ•½ã àZ%© ‘Œ†þpߪ#÷ôËôñ 2|ݺSw×õJ£ñÄ1–gz}´w9jÌÛV蹞ùØ/¿²#¬˜ŒmšyöeIåà [¨Àû¦R5Ý`{. CûLd£Îuí‡F%;æ÷$ÿøšö¤ÅAhôHyÐßóŠO§®kwTpT×÷áÿl«ëÑx€"=_£ôìúß³³‘X>²±?;™b'd¡p–5›òªâñ²©D]w[Ù¡iïý`™{°_DNË[Xj¼ /ÚëL$°í=_>錇½›‚-d?Z¬²­g÷¢ŠI/óZÙ CμذïøU]´vèívHêî)KyþÒðñ| /G>¹Ôž0M¥d9á[ã–.y…¼'ú²mzU🟔4õ„‰š¶½ÄF\˜GãÖøXÚuÔÊ= }*%}Ú”²Æ8 XµiØ‹½« éÐ5ö; lßq ½É¡\÷¶M,!xH2~Ö„?¼SèÔz“ÀŠ‚È/Z‡hÞíõ¢–œ–]j ˆÂö õ ÆÁÛ–çÚĸ ­Dëi{í¸U(6»ü•µu"XÉÞš<×·ø(ºx7pê¬/:\O‚{Žq†¯¹1‘MÑ(y“ͱ©h_ Ž?Þa½±m%%y ­„‹ö…ε ^*ú‘ùUƒý`£¢-aøF÷ÔýžO1n!I[¶€¡¯<[¥¢ê[éC£:ºà^¸:Dl-Œ Ùª©éÚ4¹+LŽ”ìèÍNY,X©þ Áû oFšÙ-:íݽÉü‚—¹G'ÜS´?:=j܈4U;ÈSS®åºÓd,|G-®G`#M‚‘m;}UÊ5Åi*b²'Xú2þ0àÔyGRK“tÃfÿ@<¦)¢~58`P*,ú¾R=U¿Y" Ù},¥¾?×îöEY`kè3u¹ñ¯çþÕwΩ]¤Ã÷}Ëqmðß¡•Bbãûãa~l¬ šo¢vŒªàNsŠýª×¼):­ÉÐOÓ¢nÊ‹†?q ½röUŠÍÝqL¡¥ZxK¡×¥ààÆç\5ñ£“0_4¿ZRš†JÀPõ·sÒq ê8ࢠûIþT€9TØ› •¬­Yýó)ö.ÅNä[ÑÄqK)>TÝÃäo'—£M. ,@†coçÝGÁðÉð”¿^”tƒº'é·M™‹Ýÿ´Mr±²MUÃKÓÕ\ut@ºSq" jïÔOBª¾Qﬕöùݹ÷W$Å•¿â¥±ƒºÛw~*Ê ßžœu.½œè A †ø|Œ“å<Å%ˆ=í¸Ê®ÏéŸÏ¥x)r=Û5HŸx›ÿ’^E; ÂPôWúƒia@õɘ¨K%ºÄçn«é"¡  þ¾ç¶ÀªÄìzÛ{{Î=çèã^ÛÆw ju0%ÔÅ® ¼Šå0Þ˜ê¼(èB竬5ÞÜLñ>9”±ãd ”Á€øÑŸØ &–›Y€Nb“sd.ì°5[Œ»’%D\Ò"²˜,j3ƒNW×ÿ úã$D!Ø9£GÆ^·Ü°/S½“ÎlûʾSKíD‰Uç¬pöY›ƒb뺠bý ~‘3ìºÃþ£0B'>ž¨8©Üâ"ï÷2 Îýu*³conŸ×[Pò~4Ã!œcÙuÙª4`Ú‚ÝU²Ü#^³YÖv3àY˜Åqô£±üã ɦBDo”¤q|ª“fÒW€4tjéVUã¨OÏDýyÌg1?´æC糺ÂÿÍ^üäÉ=#ªÑt=ÞÉcaßòa ÕN‡æð[€> çÎ endstream endobj 31 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 16>> endobj 32 0 obj<>stream H‰¼W[oë¸~ÐÿÀG§ˆyÄ›H¶‹r;·mp‚Ú]´ØôA–éX[YÊŠRrÒ_ß!eYN"*NºXä!¶Í|sû¾™×è‡>\¹@úñdz‹stôá|¡ÔÂ÷ƒlZ”ÁóOðüÖÍ>Ìç"h¾:ŠpEÍSø×ùƒûÿ¹E$r¿ÿëUˆ0¬•7Õ~Ò’QŒEŒæ›£É¬lªÔXt<ÿõèr~tyåô HªuJ¶N?®ik×»±&˜ÆQD¼éM:lÓ"á©ûȈ gȇÔX2­¥÷1_tWe›¤zDÖ‡‚Ve…êufQRÕYšôXtqy~C)mãŒv^(w^h„©fÜ¥ky4Y–i³1EÔYY€‘tÝŒÚ7HÞ¿®:”JSº}ßAJšz 0œg€S7ERo`Ê&J¦ð»(–G¿Lê­“{x/…ZX_kg¥õÖ„‘ÕÞÞñ4žØ:Ës´0Yq‹l¬Vf‰’b‰6I¸³¾ÞLÎËÍ]òZfv“Y Žÿ=ÿ ±ï_Mö½xCÞ ¼rg*÷Šû{—û×>´O„âRHpD(¾ÍÁçkˆÇ«.ðå“`¶±9tMmªÁ‚Lwæö²4ùœÙº„:_5Ö4››c¨ÉUò5Uajïï¶JÒ¬lìgÜ÷SÔ–Jc.D¤·8sèšuRD×0M‘ýÖ”–ynv&DÍGJ Å]¤©ï’rå:¬OÕpT,±Rñ“ \¾ÁòP@@_ ã–ËÌ=A§9ú©´¶|ðU¹€Ù¡rãÑ—Û0Nhú}ô ÒöÂø´¯2“/\˜–î×`º³ð¤3]ÙSæåí#¼ÂQ‚êÊ@ŒKH3²¦ºÏ`äÆ4) èÇøûñ”ĘM«lá9øwÍ"ÏìÚµlYäУ'È6ðú½{Ò¶æ`gÍÿܤ=qi®“;c·Fác5õЪ*7¨ÉË>· ‚\µ@«d“åxÈ.tÀ×r]X¨g^¦¾q“mj¶¯în•Ø4ƒövð7fSîŠï,3˜ÉÊŽY¦~ðVžÇG·uã‘ì¸Ã˜Â—éúâzª}„Ý7"äß[Ÿ ESû–æí çMì£ËÿW÷v;×Pò'c÷t˜FÈÍYg²kw7e›d ã|Ÿdy²ð¼ÝÙÎÙK½ð3C1¥’w͹ʠYê#l‡§Æpsù$  ԣʒ²pº)!Å.š¬6 óZØ&‡rý%¤8,¤b/Fi¬µÒ ‰˜cÁ´P£ZÆ-O{1 Û 8N áíô}ò"Šù¢ÍÃ9TRkáÅùˆòLä„hhžfÛq>7Pëê2{´>ÅM±4ÕÈR¨XÄUG>P‘¨¸v,öÜ@°qxåx¥b>IÍãÑŠÉ×r¶Oˆ‡æÌÛ÷Cr¢ÂmÑï /°›i?OËÔºû ”°(Ëÿ€¼J8=‹#dZÁ¸9nóÝÒkË©Øô ¶ ®æD³ø`ö<àór»ý=Û|~5j#œÓƒÂÙég(Ã"-´ƒö2Î+·Ûš"M…ÏE“ämÄòÀš’}†ú¿J:¬&ᨸƌi28Ð_ Hùô[SïÛnÃëùö¥(G˜Ò],.TtypZº•փ–°øÖ¬ˆ&b”oÉ«âv@c8Ì44WG¸<èeX5ê:±”:R/ëßNÃlÌxˆð†5(Œ0b„6bCsÁÿø¹½7r –Câ3`Òu«cŽfw&ÍÜYgpkL`a¿™¸"ø=üj “AÕža5|?3aJ”HŽ>–ìBúKÖ嵯Ò×)ÿ8ßo¢<[TIõxPÑaÕ|Îðaб±ÿ4úòô}»ó§íYMGMÛUìfBÔÍññ”N®{méu¬Lêc:ÙʇûêW½PÃòèÕp,Bb"•ákw#ž Oeµtá˜E—ß!aK³D§UV¯7 ¯)ºÌͦף}úÞ‘Â<–™¢†ÓÁÖUãYc—÷ýð†˜èÅàh¦øQê-=›ÝHž  @ùsY$Ë䤒î©·w€{© 5Å2©!OïQ^öîÆFs¢˜%öþÆh•VL쮤›±Ëæ•2Å1“j`ÅÚ»Ê*ØŠ€v;×ëŽkì¡“üÆ»†HŠ•Pt`o|C¼rˆaxëJIb‚SôYîþùóš=ÚÚlòwº€~ºµ®±"Šè—°Þ™/þ»(쑘jEøK\×U™kËÊ7Öœh0º³faÛ4¾‘yX-HÔW—½’Fc)!ƒw·C¼Ì’Û¢´‡£ÙöGÊK¨pÀ`Õ'DÀÝ¡"¹Ï æÞäåÝÆ±>dóWX{býj*ȧ#P‹Ãž±¨ãÔ³¦ÎÝ=43wþÐéÈmãÌöºçYsº³7%ÚQz«ãTÌ¢3J’šbJ¤Ö/Iø m1 endstream endobj 33 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 17>> endobj 34 0 obj[44 0 R 45 0 R 46 0 R 47 0 R] endobj 35 0 obj<>stream H‰œWMoã6½ûWÌÑlZÔ‡%EMd·Ýtƒw[ í–)›]YtI9®{èoï’e9‘¼éÂK4óæÍ{ÃÑä¾ÿ~rónüðÃõì“›GRƒìLZ (H|~‡ÏWfp=Læs(̳G<Ï›Â<…±½ <|º·¯Í PÏþÿƒws7Äó]ÄêŠyãUŒ¯ã+›ÁðM¡ÊµÐjYÊ”ç`ÔN§öÜÀÕüÏÁí|p{oá=nyÑ ¦GÔç¨@~hñ†kƒ.ÔT‘£_FŠ_ ”ÊÁ†,ÍÐj)9÷òIÚAŸ‘_ê9aM2…i±Äwjî vî”Oo&4C³8v™î•)Ñš©(Ê•õ U&ŒAþÐô9Φ÷"Oh}•uÑHl½w“£êS!Ÿ„6²h±B¶6|‰f+°ùO<ßñZ®hËJ‘SèøµÝ+M-;§›º+Fe¥›K\§kÌêú~TZmè7iÚºœ Bi=kíºÕêI.QµÀÑ [WNLs@!á¿g “Ú”—àŸûÁ0¥Ò|…\nqXs€q…ü ûçÚ$ûS7¦Â”" Ù.Ç„ú©r‚çíŠÔÞÙlvºÅÐ;ÆÇDmàÖ½<ÏA¢ú¾];·Êi;iǼ¶¸Rö—c"š& k&O0WÅj¼‘ n%*±fÖÒÆ<ôº,ê?./Z-£)Ä‹ËV›öO/¦o¦$ ,fQâ2½çÆyÌòÅw囸æKw»…Ȥ›`›©Êsѯ1Ëåé¼q vÃi+Ô;²_+Ø+œÀK°ni櫚£‹'N·¥€ß†²HósÅÙq6EÓõ£,Ñ|¢šó×\ë>ãd¸[àAb\yöø‚ýçiŠâmì»ÓOòÉÆ½úcþcK½–ª±6:–Z‰¹‘d¦ûÙ¢öµÛ\­¼ ¼Ùns‰ìÌxÉá#zÆîZü­B  ) §…–œG§/fº»Œ«JPOI•É]áâù¸ÛLë5è·a=ú®(Â/W4Š«1.àQ–ÂTDw``ÿ³B›×›’0² Þ¥â¨×b½Ø´;²„,$,°dÚ,µþî´@¿T“¯ÚØöbxäÔ‹JÏvݬ×.Cõ1ðÊzý0$µÅ:@¡C³.Ëíw“É~¿'Ë…,Iª6“WÛ*˜l—[:9Û[!ð€Àšܵ=2ë%ÙG^,ÉÔoƒö:@w‘†cuÇ’Ë­éÙR^µLØ,1% bª/ž~ÂAí«¶,UºÛàNQïy•mq«FÉ¥=2B}ÜOj³âtxv¢ãY\3¤n— ¢ô Ï&½µö,N—bsðKã+úî9„^Mb˜$bI¥ïó‘ê&êO¢À-¿?Œ]£ÄFIm§¬üŸ~~÷ëEÉO¿&ùñé#°“‡ÀâcNµÒš–ö8ÜÇm íwb‚6Ÿ&$tʪ¼–ø9Îî Û:l»¨ bTNÌùÂ8ûÕ+ÿ_þ¶ŒÉºÜämpîňD•ÿBøIV«³Éלçõó#’|Ý~Ý3·ª9n”C§¬O:4$>²VƒÿñÝý[0Wãp(7»¼ò]uëÆâÙà?#ß÷^'’êhîT M¦$N¢ êØI"FnÖ¤Ô\âwÕj,–+a;Õ&¥yixHžÿL,'Ö|úÍ]ñ|›åRK|¿³%ÁF .ìòÉîwOØ®x1ÃÕ›1ú2Þ `"­Ä endstream endobj 36 0 obj<>stream H‰dUiTW®ê¦ªZ–º,'ÓªZqÔhð`Ü Ê(‘LP¡íV(²J³¶ ¢ ›Š‚ј ‚€Ú aÚ` Æ¥ F‘qŒQñ8s\&ŠgÜÆo9¯3'ókÎyïûqÏ}·¾ï»ï¾Â1'†ã¸{PðçË–ÍX’µÑlL2†&‹\ü“ø¡ë„r´ûýÎNxaXÛx7w‡2Ïï>drT˜ dzr—d™òÌÆõ)9Úi igùûù,¡¿ïÎÃOÆpöÎÃùcè§ LÌŠOÒ®ÌËÎIÊÈÖ.ÍLÈ2›²Ìq9I‰3µÚÀôt­n´r¶V—”dΕ¢ÿ£ˆáKŒÂ09æá‚yɰIrl*†MÇ1'l6‰- °ÏX(­$±5‹a)¶T’‹É096»ˆ9p/|.¾ïá¯d²S²§ryŸü‰S¬Ó-•˜A,#ÒˆÒ@'ÿCùQQT=uQ¡P,VT)žŽ ÷Êy¢s¶s§ó{—X—½.­.—\ºzºNvµ¸9¹­pkW*”+”‚;åà~ÀÝæ1Í#ßã¼çÏlÏ«ªöª®Òãé\º’¾K¿T*‹ÁULܤ‚RðšÖO߀â &€¬sh;I?èA0­FKZ\fFFFK†í¤ÕÚÆ*‹…uÌå!W û0/^› ÊÜié<|ú@÷ïïñÈŒ+XeJdé\4 Ö;¾JûÞ¦¶4w\´uåolaô–Ä5õ‘ÿ¨H]R±)…«¬.Ù[ÎÒ½ÚˆòyéêˆÎ9o¾ñd'KçwDtoÈ3fcvf¦^Ý1x£÷D×aN ßä£M8Ô^—‹[Do&ÿ&š¼zÌ$–ûÐL4åü– ˜˜<yô‡Àù6rîoŸ „t<^.ã07 pa€ÎC8¥D ìÁa)Èá ¸1“h1@*aƒ€·äb,l`tK/åz¿{€?}åbÕ»…LLƒ­7—œó¥l'¬Ò—ÊàÓ³ãqâ{9%Ö¬Ù´©b{çg^ï[â¯Ø‘^±¶H=㊃Ű¼ÒŸ±†L)5%MDγM·–³Å=egϪ²€ dv[Nbk‰ÜV¦PV âqÉ…vA.Hìýឺy[KÑQv e‘5LjZÅñÔàÍïü%zAÁŽÂí…lqê  ›P ú@ò¨X€tiñ*q<¿ù=,¦ÃLæu@‹q?K¿¶YmÍCê¶Òc%GØîlÉ`MpxŽqWK|Ûx¥}@óж*2*a¹–«Ð×’ôpsóÎ2+×Qz&C¯^º,ffÄŠººh¶0æD!‘ݳ¼Qóìí 8IÆ[®œÁá±$vóÁçí3Á`…v£÷Ä—`Ñ"™¤[&60è 2ޱ”né§R#Üh´WÀ"æYðCä¹&¦8/™…H° è%ºDÙJ‰®«*i^ò¡!K —‡Õ5è¹=±©ÿúæA ,: ŠANY$@©†>Õsÿª»@ТX$e.Q_„m1Fr~+Ãç éjd÷°G,=òËíŸ^ ‡ÞG²¹11I™\à½OîÛ´Ø`;WszwÕÎݚʆªö냕»ª9zDç3cÈ4]TÛ¹lyÍVkE“âœåòÉÝž·¾‰;˜WmÎQ+rÅÍC÷e¹X)d.9¦ /þoÐM¡HG ÑO*‹úÀÔÉ}ª_øDvñëyúX¾Ì?Â:Ö×°ôóŽ:{ËßÕƒ ÃHíýÇïåÖ¸3f–~³±øO ³Ôón,OÜoÞæèçK  ]·Ä8ûàÓ—Ãû›ïœ‰‰RëÐ1Æ?À3ãÐÏ/þy¸ûî@ü笲è",íƒ*É÷q<´K¾ï{Ì­-"γ-©ÑµÑ$_<ßÇ÷Ò,p»|Ír¡ÛŠæêkVlËLÝ‘ZV¾»L³Z7HvÙ¾:`ãÀ¹ù`®^ic’ WÄiæÄ7ÎÙìÆc\Cf횪…²\`2¸à°8¹è W™Ãù߯JN3¥¦µ™ºN´;Á¢Z§ÿ‹)äâÃ=r±x#Ph¦‚´‰>Ê1 ù”dü¿ü8ú`Ô3‚#PO‰È—áJj.š¤ãõ0Ÿ’ÁüÑÑAS‘´‰±ÙR€!Ѧ*‡‡$ú¹øj‚` P¢XC zð&Qˆ#žX®‡Á—<$ÝäiDéÉï ‡A;Iú¾¤îÏÄhµè>HTà*@«/Ð#¢¦3Cá§B¾ai±¦ºõð¡FEíÑý­Íjk™¥¤‰ý)Åß" : rÄèa€¤Gú¯|mïäÛÑ¡¦Å™Rk3K2sóØLãÚŠ rƒ„G¤µ½r»ëÝnKŠTÓ#]¨‘ÉØ’žVÀæ''ïŠÓ ˆ†)dOï¶lw´àPV†Zš ±€Ç¯÷‰îRÇù~&»—H\lúBƒTÓŸÂ'°¼.ܾv9=ÆÎ}ʃÑFeÔ–¸/­Ò YÞH|ÿô¹w·ùtÿ踋ñ£o(?Kó.ÀyüîMjb÷e¤V­× í¿)J%íÝ»«Or¿Ö=†µ~£+Ò™Ö²¡I‹ 5H&Æ¿$Ÿ¬Xw–kͲ.›£51V€uRméw&Ü–ªŸU èÿ¥ºê£jJ×xÉ>ûŒku–³Óhko±ÊÇD¸¤Q[‘ä«Ì‹ôíÕtDCDá"Äõ1"S¸#t#•"¢Î{Ê’>4>’‘hÌ\Í1ÖàÙ­çÌrßSXkÖ:õ¼Ïûìßó{~ÏïM&-Ã$¹ ÆûQˆ"¾8bà"Šü‡’N²˜ØNP 0Z´<-ôdÝÞ`R’¨í5 eUvÊ=%‡ñEC Ûøˆma†$)meRÆ2qÊû@#ÿNp_˜Æ}]Ý˜Ò ‚sþÓG2àÇ[ÆR(¶–¦‡£°˜]Öb1yc18“QMþ`A÷ô4B1”StáQø)tp8Œ\±ÂYqt–bدWK_®TC”¾\¶ÅBTïsüB ËÑf-7‘·–Y<)Ž&­ƒmm”é*(±lâî*9*ëz[Gm‹©âÎÎUC‰î×PˆAN‡cØÖò|"k¸G'ˆà)‘L “‘GÔ{ÂÏ& #ÁëLÒe þÖ_OåºÂë Òr#)¸‚?#/c©‰¢†qÌ]­›SYFmÙéF†Úónbl&oð6š$L\²8ë»eRdÁæ“#T-Åí¤Ëx¿=¾5ÒW%ÕëMâÕÚœsÅò…ÂÃ7êá£k…Gµ`9.WÚ1â…&f:»SÇ“_yô±Ä¡¯Ç=£¸œü ‚/Ù{¨‚ÙL$íwþæc]wÚ£Lu< BX£<è>A^òË8ìo¸E*᪢îÅpR®ªa)êTÀ1IÙJ4w'³¢¡=ÉÅæšƒ%¸‚…»B‹Ò §Óyœ ™œpÆ€‘«£LjU( jÁk¦CT0œÐ˜ÉH¦#U0 "Á#¹ Ç T&03>׃‘ ÞâNYÌUZÜ1ÊÄöÌ9ÞÒÛ²ˆcKdíÒ°øZ¼~û,m—´§ý™Îæà®ÇÌaº³%ö:‹mW]äj…¹LîéMÔÊGèºÒqíŸÔ2qPRáˆíKÅÅNÉTÖêÊy·æ˜K‹_¨CU £ÂɨÉêH¾9ï‡+fsMpcTó µ=îºf»SyÎz»t×,ÏCù³?Ö öfm¨IHS*ôôsÈàó2O¥åIÅ) ÎxˆSV"çÌåróïäÝÏlO=-Ÿ\·/.Á1‘ß¶-{Ïyã‚e¡"¨zÊ«ûMeUTÒ&ð;ØÖ*f/Œ˜(Ί¹P”wèØÑùäyÓ®ËâÅÒŒ-¥rO¿P¦zÚ·&è BšP¡TQ#{èâ?2üpˆˆ¾8`4»q í¸]P~ý/…2éï´BõPO½”&œC` FóµaSÍQtŠ:†ª[Éñíú35Å ŒIüVHf`8ø Å^ÁOzÖôè%ûij®²µPo Z³íÍjøƒ™½‚kºµU\´Oj„õ͉ÅòÒãFS•c–2Šm ØáxàìŽöiB;ðomË+¬—‚ A‡¯T׳®7'‚”ïšlh¡àÄXc¶S)]º½ ~Y‹Ä%Øk‡gª¼-mçª5ŽóÙä·8Åžb"´0㉕|ù¿ŠAUD32˜…Ây&«´”Ò…T{Žš˜Å©U4ŒÉj^h á‹â ¼˜gðBÎmĽA0¶¡ùDÅûñ£BØwê´#çIçr*Ÿ¤¶‰Ðï™uåpc§ç ¤ï4ÔeÒ­Öÿ>®í„A…Ÿ‘î­Ñ x·´g"õ£•›ûø›)ͨž2mIT¨¹(“'Ý´XN=a7_vùÆÅzñQî"cÚç=Þ¬…>ro*t¿C»rJšÉ¨áa»å³„lÌ} ."à†-ì5¥zšÄ_ι÷žû»çÿ?³IX¦n©òF€'0­ò6'Ãw‚¬ôC¡Þ[é½Òeä'&­æ')‚iÿéœÓˆÑGµA,ºðX½ª8ÉY»ˆeF€èŸ¿@Àâà>T³iWò†Í#ó3C_pÖˆí¦ìãÿš[dd+¯2 úë-5Må÷´mŽÂ[yBÂ^³´[ä‡AKÓ%X|¬”(BÁQŽ÷þßc¿Ðö úlƒøÿâºY’#ƒGÎSa (NˆÕOfÜO«7¦Õo¼~´S{åvyS“áÃ7g¯‘Rf.fȘiåÚe:¢™ÝvÈt¡ÁÞ\&¾ÎÖVkùé¾Cû)m}U­£Ú`¦K*m{¥fo¶ñDSˆ»40pù³€z¦ ½ c)‹Ãs·O_-3Ň·œÙ)Ø ëíb«ÒÓe€rtcôè3©~»4Bz«ˆRÿè§ÎöAµoæªuÑAd˜‰U5“ñ‡„»°ÖpYÖÃÇ÷ö‚›®"1¿µÊã1À#u•Ör–»·Ýx±«§â©Ðã¿ù’tÄL>W¤Ä l˜s2Ü=lR@M¹–ŽãX0ÎŒZ M±º£Êzªƒb>&…CÛ j C§Â¡‰Ò†&Ñ7&hgrØ$Úf™  g—ºYˆÀ ƒlêB´mÁ|8SøV­`¡«aÝV>ˆÏ4¶Š¤r4Ì „µÏ Ý'=c©MíRà˜—„fI!ÖÑõ ?MSèVÆ,… e^F0P#w,Šì‚#:^i „*»à8ò×óeqÉ•Eº% k… }–‡)º–'Jóï5C¼Zlçs…—l'lb¿éhiv»…æî²R·È{ËÜÞÒ‡ÂûžýÄhe¸8êh`NÖ'°­ÔǼb£¯6*ÃNß_ïÓ7X(oø}cÑSË‘(²Lù¹òIÕÈQ endstream endobj 37 0 obj<> endobj 38 0 obj<> endobj 39 0 obj<>stream H‰T±nà †wžâÆFÀ¤R:X,éâ¡i;Ý œ]¤Ð~û€c%êèþ»ïîçø±ùh¼KÀ¿)˜ôÎ[Â)Ìd®88•ëLÚ¢õ6£ŽÀ3Ü.S±ñ}€ºfüœ“S¢^º®Ú¿Šð/²HÎYz“—Ÿ¬´sŒ8¢O @)°Ø3~üÔñ¤G~'Ÿj·D¹ÆÕ6=Xœ¢6HÚµb¯ÊsxW€ÞþÏ3y§®½ùÕÄžÕR(¶AµR*–Ù­ªt)|Ø23Qv¼.bµU 9]ÅËìrØM€»m† endstream endobj 40 0 obj<> endobj 41 0 obj<> endobj 42 0 obj<>stream H‰ÜW Tgž¶gëâÑõA…â«¢ëkeÝj}Të« èþUÐÖÝsöœÝ³ggÎfîý¿{ÿïÿï½¹„ Ȥa!c“5XêÅ¢–IPsA„B•&"rÞýë|¿u:cÂuÎG¿ÑÂaç8r­ÑCy2$ÊÞ¹–¢œ­ó HßeÂõÙn˜‚#HØu(ǘ Â[Ìr"ÈÐ4(¿c²R3 3°1Pv ³Ëb7ê' †¾†U!ÃeÕÏt0Yœ:hâMoÅŸ©¦ ȈÁÿ¹ƒÀs¼@ÿAaA£ç¦Ÿˆ >…HÏ%xˆº÷¸aeãÊ{2ܘµ.ÁU¨ºÄd0¤^è®{ï “ÃAÐi\^8—Áf¸F1ìZ5šŠúiü7”ú# =· 1 $bG,ŽPpŒ¡o¼êí³´~Áµ^ײ„s©õÏd‡j]Þï¢.f¡L¡ ¢ñÄÂëu‡¾Š>²fqyóÐfîcÔó%WRr~"оÍee²y‚:œ0k̹6 %òI (qªÐNÌBýh_àõ ˜Í(‘ŠÐ°Þ‰à>K³Jou˜m¹@ƒf#Ôv;%‰Fö¢Ã•*†É’°4L; ÈärE†V‘,#Œ¡±£À«k ƒäô™_çùuÎzZŸ5µšÝ-6øf9ý›óªÎeEœ9.äÌ‹ù²jkcúøwâƒt«æ®µ”5O]1½qSÌ™.ñ©ÆØ5LLê×R‚yÅù®ÏsòFigñ™-¶uqº–'æo qùC^ñL´€ñÑäÕ†fߺÒ{÷ ÷žÐ­šánPÞðÙùèNÐ%BÌ.ãÔÍò¸L(¿ûx`ú·nKÖøX²žñ¢W4W¬»Ìv¬ ›­_rà:?oõæ#9†¤ÄåUA‘+ƒ*>Ívçáɧ0[àˆaú!_ù®>/¿Ø™’5¯¢9¥¼2äŽpÚÿ_o“GCzüs/vÊãNÿ-Š/·÷‹óñE½é 7;f£p†S¨³æ)½FaÒõúÛ ‹+S+/4úN5_à•*¹Ò–Öçå§œÅâªnœæ¾[Ó°aæä[OºŒ Õ>¾ ýaCL½Øãò=ûðzÏ Ó8Ѫ’V­ªm¯(©ß¶xßÔç{JÛ:ªK‚°$Ë©•;º¿‘¬‹{P²9kÓÙ üÚ‡õ3×þñ\j’é=ñìîÝLëWÚ:­sÕï?5qªØnH¶û¡˜O°‡™²­,/Ú=üÑG—®ì®¾¾¨îwäÑq5;Î/:ï·´™uÍ#DÇýNùiêg'&¦œ­{ØrpX¼8$²uÍÕ?Mý¾ÝšZp­ Ýè]ÚZÒ?§öÉò0i¸ßÓ£ÂÛwÜÈ”9RÄ¢9¨ËcÞµ,&ƒÉô)Ê©¶ÍÝѶ‡ñ–­¦©ÏëϘ Zÿ+§þæE¡ÒÞ€‡½Ì¹ÝjÅ £Yo{U¨'p‘o°˜IN@.ëIÉÑèHi оLIZŒŒŠŽŽŒºïÿÇIHSÐä^£ÄÂÂBI4$¡¡Äh·FÀl'Í”(Šghè5ì„C E@çHDt^KÒ´Ét.ÇHÇ  ½~¢“͹f .ˆ%¹EO’ ˆAºÙHØIH¡‡No1gë)³Ý "¥|Ôƒ¶ç ˜™©õ¥wo¢ž4ÁÒ£ì6©êÕ{nj<Ûj·eKPZÃúõ¹—CŽv¢Çí‹yþæáƒ×«ÈÅðD Þéb0ÆÊ“Ã6gÿý¦ßÁçÖb™Š÷Ä–×*¬Ùså´é¯ÑÝØ[íÕ]ø7!ØÏ>öÁÃckÕ­ã_lCWGfÍÚ³eFH«…ßs®ýÐQý¸ÿ›MŸ'Ìs\ýÉ>E5Ûî­V,ô;‹_ˆœŽÄõ–q^üÁíÀ¯Á’Ø s9Ç‚‡t©k¶Õ¤UŸMPf%ºŠïxDëv›š’â¥;Û—wfmÞx0TÕú`Ù]ÖÐâ{~q[~Úš1—c5Ü]$(}®Ãß‹<Àû刃7[–æÙŸ³k½6è[~UlËámð´›ì*{ÿðƒñ^·²ôÁém;㲯>™zt¾5màöD7XÈ]œ‹¨‹s®':o ØLAùô«7›ÍbrjQg9-1ØÎRtN©OqõßNÈ»M+ï>n‹ÿ‘ïZoü/’‹Ãl„_…h Í„Í`Nî ¥­ T(dzœúýB?‡«Gy+‘i5æ~<ºLÕ;£c±,DFñêx}XEÅDF(ß$‚SfFX2&,2õ;ƒ;E›)$.UaÄ«“„°H!\":'Q% ªððä„7¥NˆëÌYÊ›íG`¤ê-ðt²•í³×LþoÒ¼«?V­¶Ž‰’¯9ª¯$·æ\#×H C×{õ/DJvXK °ªðøJgÎÈ%XíðÓëÜ£?©móޛؔ´hƒeÝA¡=¶ú‹t·ôÝ%«N®”x˜G6,¶y1×!geå“AÓR›òjì {ì8 ÓdÊšÙSÅ\«8pŸüÜÃÛ6áçúÆÕ®êÑùÝ\rwë£V.Ô=Nþ]1‹?Vž>1ÕÕa÷ºÜ×Yë§ZÛ¶œ6Õñ䯿h-ì´Üh¬Á#º<ù°ü›Ã`_‰ÁR(ÏÞ5‡åúYêÃìÞÝX8ý.ÝË Û´ü³»F/•þ0hXZΘàGVYaÃR“)7­Äk¶YÍßAŽÖ¾C¥ï…[Å/·íï‹^¥ïú2aDfÙ¡gÊ;Ÿ¶lÔ6æ”ÔŽZeÔÜv;fAè¤"/zÚãÓ÷g¯àp[ç·ójkÓ¹æØq×N…~U¿ôZ‘•Êú|f£ê^aÐeÓXcÏÐ{$Î3Ñä“]¡&wX4®ªOªMV*BJNCKá‹ÁyÍÚ™òeë.žL¿?éÆxëR-¹‚§º†_Hì´ävÐÙšêÿû÷Wj §5¹<õÚmAVΟݲ , í¨Áò¡ïÆ©awCJ0L»ŸðvFúW;9¾kÈí¦Lš¼ð_´­° ßµ½²ÊÕ[Äç&ÛŒ¼zÕ"}u\HÜçô”ïîßXñÒt Waðõ†ÌZ1ÚÏ1ñiÆÍ¨)Ò告AýÍá#ñÕ©¢2›â”–ª4ñÈÌÌïm{òX“X2°¬Yæ† £²&®T{ù¾ÕÔ61;$íΠѼlk¡£67Äró­}OåÆÎ¥«Gûeõñøõ¤ýN‡¯?mômÙXž¨/j„»D €à‹øIØ´|óËö@5îËxžP"•P^ ½.u¼f· í"¿QçF&’:M÷S~1ØñÞ0ïál+˜ˆwÞÞ÷tÁâ#~9Xè–‰7­Œpð—oï7— ,a ØÀ¨ƒv8MÆ?œ¯@8, ÂûØŸÇá ÜWˆ f$±6ÂXX {`:g&V7<00‚Á0f5HÀ¢a7¹ žà…s8€;ä@~ÏÅþçd>! ƒÅ¸úVØ §á/ð Ãmá:ºþ¹ø¸`E ‡t8·yg~˜@!‚2¨…ûÄ–ì'mì±X%6ˆÿ@-°{Áꛡǂ‹Ô‚íÍÄtñâyŽÖ—#êZ8‹k=# "áô KÓ½ãÅrä¡/ڌ֣8!_H‚8ò:¼&}P´T ÐpÝ@qHa$V¸ñh_ V¼Õ ›E”ÀQx@> KÉ%ò˜ö£ZÃûK}¥¾}j:¾ÝÅg¸F_…Ö·劚›a lGÍR\ëO(íÐAì‰q$ž$€ä“õäyAÇÓïékÖŸ± ,˜…² ÖÌ^ð~ºº+¢¿˜Š\ä\†žtAœó`¬€Dø2@ƒÖå¡ {å(Èg Ê7p Àxˆ1Ç#F‡"Gq ³ÉH~O¢I"ÙAŽ‘jršœ%mä LíétêGh4]A“h­ •´†Þ£¿ •3˜‚%²X9«cçÙUÖÄ7‡Sq1\2·•«à¾åÚ¹'œŽÞÅ–Wñ{:öê¼t!âXÑA 7‰(ãˆf,X!ôj8î(шj¬DICîÖ!¢í°¹Ó³w ªákŒÒ:ôo=\&Äw šá9¼DrôøLÉ(ò>±C~gw”…è§’A4$!Ï•¤ å ¹‰(uˆ0ˆÓ%4…fÐMtÝIOÐ3ô:zBdôÄPæÎ¼Ø|–°$¶}Ì>a»Y «fgX=G¹œ?—À­å ¸½ÜQî×ÈÝä弟‹RÁWñ§ø‰±Ä\2Y¢”TK%i­:øÎA%TõÎ}’MJøŒ´2Žih]@ éu¢å.+ôÀL|î¶?£…ï‘«t*™ÏÂÉBäOK¢HìbÃÙ^6øx¢dþ$”Üø•ÿT|.ýœQ>—u—´–B]ÞQ&“þ $ûéAŒ˜L˜ 6œ\§Ó¹Ä’ÚÐéR ŽR ›Îfak?»‹f* ŒH¨ØoäW[p×þÏîjw-_…±e £‹äزll.¾©¶dI† ìØ–¡Z ­.µŠ= Ðq))-CIEð(“Èô2Ít˜4q2푎œI©ßÚ—<1ãvJ \Ú‡R2B§)FýÏJ6vÊ´}ìLWúÎ?ÿ¿ç²g÷cÜ?·po soã3áù£ôV·Èÿ}NB7¹ô¤Þ5h\”¬ç.‘Ý‹§Ïÿ0÷RÍ} °X¾èãü¸âöäf¸kð.>ù»p®q7`>5úÎù÷Þ7ðI³s¥¸ŸÂø™ôöôtÉÓÕÙÑÞ¶më–Ö–ÍÍMîFWCýsuNÇ&u£]±m¨]o­©¶TU®«Xk.7­)+-)6É’hð­ƒj_T¡Î(œêÎn&«1TÄV(¢TAUßjªDu7eµ§=~ÁÓ›÷ô.{“â»Q ª ý( *Y²o(‚üù€ª)ô¾Î÷ë¼àÔ…RìvŒP‚–±€BIT Ò¾ãc©`4€ýeŠ~ÕŸ4º!c,F¶9Z¥NfHU7Ñ®*Ø™á@.ŪhÒj5ÀJ ¼#¥ƒC‘`Àj·kîFJü 5NAí¥k\º øõ4TôSIO£Œ³ÛsJ¦q>õZÖñ¨«dTˆP>¦±å.Ì Uß¼cy*bçfäìJ«•O-ã S©³ }k(²Òjg­¦aË9ú¢©>LýŽb(¬`6îŒ¡ä ¦Tذ»Êß_R 2MôB‹Ô^u,u(ŠsS“¢0S©á©ËÕ^¥zµÅݘ1•ç6S¶¦À””®d’Ë6ÓÝ^YÂ*RŸÇA•„‚•DT¼§vÖ$Û!•hG7¼4‚Qtgdœù£)S'Ó³xjp˜T%õà Pïÿeµ&VЈÓgÀX¶N–×Ú—xêrц¶D$?Î)ÖØ­ËÛÜdzœO4)Hpø`Ç6¦u6ãðÛíl‚Ïe½GžŠäeâÖYð6»4ÊE™e~ɲn³œZ²,‡GU\ÉWðüXGeçò©rmp¬“’ÊcNæí¡°ÚQ‚©halC#«¤¼½}ÙVàèZ„·r޳òºåeg&DJ¨àÀ¿¨/êѬ$ãªÔ5D飦èÎ|«íöÿ2(›û„EéäiX¡LÚéZ-w­’W•W’â±`ÁÉ…Fö¥RÆ•6`ƒ&?éÆvï“÷7ÉGõa\y]>ÂS•]Ÿ¾Ú!fàŽá ć0 Câ ì;`':Ñ6‚p£íu´9ÐÿH¾Îuär¨ß…øш#D¡!v#¾…â:à}Ä9Œõ°xFùóa¼á7PaØ ‘š…»P#܆:Ñ ;…렢Ήù·J`y‡á$THµ,&÷g”w‹ôù+Öð28…¡c» g kß¶vC=ôŠ0ßm¨Ä~~&þ‰BºË@äÀÿûÁ:¦}üCbìó‚ vð»ðþ®ƒ›û)ø‘ѾÑ"üïÉÏ!ÏêoC^C:Ž>ëBûOÖ:È û‘6c¿ûùßÁuò¸„tý· `-ù\Ïë!8[³Ç DæD‘lFú7Ä#y/ÔKw!„ý¿¸Dù-pžðã…1Âøƒ˜ÇÇÿƘaË%Ü®s2äÎã½+âœó“àÆ±ùŠt—|Çj@Lj!ígÀþÚmˆ®: WˆQŒö0Ê»ÄaH0H6hÅØ&Ì5ÂÖÚ6c: õï.Ô¯S¬³ÇÕ·/qñf¯,ã!¾o<Äï’Ks 㻹ü:ɽøysî Þ̽˜§ "ÿb,¹ë}ëÀÌÕáÏÉ9a‚TâîøªÞ¾ ·=zÛÌZ®y¶ÙfËrM³o1Ò8[[d“·øV­¥ÎlóÔ1¹ÊÛu¸Þvs¦Úv ñ^]«íUO«í4¢qeæW7So›¨›øúÄ÷&Î mPY‰³l.—½Yrû—{*Š*ŠÚÒYòko‡”þ•”¾,¥¿&¥G¥ô—¥tŸ”Þ.¥›¤´KJ;¤ô&©B6Ë&¹L.‘²,‹² s2ÈÙÜM¯‹mþ ÑĈ(°VÐyÇZ¶ÑñIÀ™Ã¯;º–q¡p/mw…²Rn˜¶¹BTÜÉ2­¡–r¯f ŒD²$ÇTg¬ìÔžBrgÎ[ TÓHˆÎ' W裰š%F|PÔ^BÍ!ôZ òx¥ÇÜ]ÞÑxF-´®§—ŵò N}6rŒ}|‘£—%ÛÓ†Q›Öµi¦MëZK-½ GèL­F[“«ÕÈeßUï öUƒID”ž;>f¡§âŠ’ñ^-¼ 8£ñÄ£±$½ª&Ô«”ŒïÄ3Ì'˜Ù§2p"8Éœð&³>¯/¨ÆÚ x¦azUºï/¥›ƒÿ׳$κl`¦Ÿ‘qš™XÆi–qšeðèƒãá^ŒddèÕððÑée®ØˆSµÚµÞJÓd·>o]vË+Ö ï@1žÅ%ø^WŠ`&·Ïíc&\0ÌTÆ^ù &Ë+]vëä‚É„êrµ\Ç\_¸^fX‚ã¬d.7Ïš5ÛZ];g8vá×ncœ´.ïQJ Î $x0ІÏs5E’ P-×·[\¦‡žþEπ鑧ߴèÏ¢‡¡e³½Ü^îÀ×6>/FontDescriptor 40 0 R/DW 1000>> endobj 44 0 obj<>/Subtype/Link/A 50 0 R/StructParent 18>> endobj 45 0 obj<>/Subtype/Link/A 48 0 R/StructParent 19>> endobj 46 0 obj<>/Subtype/Link/A 51 0 R/StructParent 20>> endobj 47 0 obj<>/Subtype/Link/A 49 0 R/StructParent 21>> endobj 48 0 obj<> endobj 49 0 obj<> endobj 50 0 obj<> endobj 51 0 obj<> endobj 52 0 obj<> endobj 53 0 obj<> endobj 54 0 obj<> endobj 55 0 obj<> endobj 56 0 obj<> endobj 57 0 obj<> endobj 58 0 obj<> endobj 59 0 obj<> endobj 60 0 obj<> endobj 61 0 obj<> endobj 62 0 obj<> endobj 63 0 obj<> endobj 64 0 obj<> endobj 65 0 obj<> endobj 66 0 obj<> endobj 67 0 obj<> endobj 68 0 obj<> endobj 69 0 obj<> endobj 70 0 obj<> endobj 71 0 obj<> endobj 72 0 obj<> endobj 73 0 obj<> endobj 74 0 obj<> endobj 75 0 obj<> endobj 76 0 obj<> endobj 77 0 obj<> endobj 78 0 obj<> endobj 79 0 obj<> endobj 80 0 obj<> endobj 81 0 obj<> endobj 82 0 obj<> endobj 83 0 obj<> endobj 84 0 obj<> endobj 85 0 obj<> endobj 86 0 obj<>stream xÚìšQo·…ÿÊ}ïÝáÌ\ 85œ: Ë}ô Ø©GWå6þ÷åp¿•-[ÃÄI“ÆWÜå’g83çÌJ£+sšæIfmŸÒ>6Y¶¸ñÉŠK»È“-ËúØU–vQ'¯ÉÚÅ2e1ŸDæ)go0"S^jÑ4cF§âm¡ˆMeñÚ.|ªª±+OÕ—Ô.ÊTK™:ÕÅr3³Hi»Ò<-¶’Lmw̤i©íP’tZ™8Š6‹’<ÞŽ.)ÇU‰¹ð*—€¨á„5ŸRxQ–öDÃ÷>høZ—¶OS¸VÚ½jø‡šK¬‹sö}šÃϰ¦Ýã;ÂA 5|ÖÜÖY„Áb_Ø —-ÜöøÁ¯mŸ…–zLs½a£„76Jxda£†76jøÖƒ¿´ IäL–Üžzx?·p‹k\åvï=”q>(‰7äø¤ ðW%öÖpœ¥GXã,áK²8Kxš,Î’{Ô[ÞÚÁã*·ñiÁˆ=ŸÃF™cGØ(áCœ15N´«°Q#%lÔÈséyŠ÷,.µáŒEìÃgKÃê9é(‘»¾/æ5Å©ÂMqŽ`“v–„/€í¾FÆ,ÈTS\EœÂŽzP·† ¸×ž÷¥Ý×°‘#ÁrÍ‘·øh‰üƹµÖ6jðl鬈³EÞui´•“.ák`ÚÜ ‡v”v´’C­çb‰lw®-K\µÜ¦8£icgKr\µ˜|òÉîñ«‹ýîøêòå“«Ç—ûý£Ãájw¿ |žíÁËàep 8…óð x¼^¯€WÀ+àÕ¯0_˜/Û|ÆÄ¨ŒÆèŒ™±0‚WÁ[À[À[À[À[À[À[À[À[À[8÷Lfâ0‡™8ÌÄa&3q˜‰ÃLfð<OÀð<OÀÎÇóÂóÂórýœóóÏ <+ð¬À³Ï <+ð¬À³ÏJOÁSð<OÁSð<OÁSð <ÏÀ3ð <ÏÀ3ð <ÏÁsð<ÏÁsð<ÏÁCO=• º*誠«‚® º*誠«‚®JÙtº*誠«‚® ºÊ¬Ë¬Ë¬Ë¬Ë×ë ce\íft–ÑYFget–ÑYFget–ÑYFget–ÑYFget–ÑYFgtVÐYAgtVÐYAgtVÐYAgtVÐYf_f_f_f_f_f_Þö¡¿Lž3:Ëè,㌎rÚŽ2:Êè(ÃóŒ.2ºÈ¶=_ÏáÄÑáà ‡/^xÙÖÆÊ¸ÚqxáðÂá…à ‡/^8¼pxáðÂá…à _¶sƒ/œxºl÷àSÏœzæÔ3'¾Nsê˜SÇœ:æÔ1'þNü=m8œ“ø;ñwê˜SÇœ:æÔ1§Ž9uÌ©cN¾œ|9ùò-_Ô1'¿Nsê˜SÇœ:æÔ1§Ž9uÌ©cNsê˜SÇœ:æðÏ©cNsê˜SÇœ:æÔ1§Ž9uÌá·SÇ ;†óm¾2®v ;†ÃŽaǰcØ1ìØf‡ziÔK£^|7ønðÝà»Áwƒïß ¾|7ønðÝà»Áwƒï¶ñúcðØ–ížuÔ7C—F}3ê›QßœúæÔ7ƒ·o Þ¼5xkðÖà­Á[ƒ·o Þ¼5xkðÖà­Á[ƒ·o Þ¼5xkðÖà­Á[ƒ·o Þ¼5xkðÖà­Á[#¶Åƒzo[¼çmv©÷FÝ6ê¶Q·ºmÔm£nuƨ3F1êŒQgŒ:cÔs£ÞXÚòµâ)üQø£u›WFcÌŒ…±2®øJTê¤Â/…_ ¿~)üRø¥ðK‰¯_%¾Š^½*zUßöqôªèUÑ«¢WE¯Š^½*zUôªèUÑ«¢WE¯Š^½*zUôªèUÑ«¢WEŠ(:Pø¨ð]u»g|Wø®¶íã|ð]á»Â÷DÞyKä-‘·DÞyK[ÞÒ6Ï>ô¯ð]á»Âw…ï ß¾+|Wø®ð]á»Âw…ï ß¾+|Wø®ð]á»Âw…ï‰8%┈S"N‰8%â”¶8‘×äÛÈ<üKð/Á¿|Kð-Á·ßu-D<ñHÄ#D<ñHÄ#D^„s:t è@8—p.á\¹~:t è@РAB¼:t è@Р¡îuG¨;BÝêŽðÞêÀcÇ <x,ðX¨?BýêP~ üNð{Áî‚Ý» v—Í®m#vá•À+á}#è[xßïÑmxïï?B}”´å;ðTVžžîN²Jrw¼;Þ?¹ê}›¯—ß=o‡ï »{û³§ÏοùS´‘–5oÌE»UßšÓ6·¶>;<}Õ&ï¿¿jÃçO÷çWí¡_÷ˆ¾zùÝ‹“¹wûÁzû¸‡¶÷»X{¹¿Žz¹ÿBÑ[ÈýO„ÞCîäô&roô.r|o#wä¹7’û¥ôVò ÞL^c×ÛÉkx{CyTo)¯$ïM啟½­¼R«÷•WVôÆòJŒ×—Ò›Ìîîñƒ—WÏŸïwÇg绿·ˆ\¾¾]#¿{¸‹€}vø~÷ù³íî^ž}·_¯‡«óÃÕ¾­k?þrþôõÍñ?Î.ö»»Ï¾yy¹ß}q7¦?ø´}îÄøE\Ä»ÌÜeæøåÅþòÅ“ËgWëqŽ_~}ãöêòÙ·ûÃKn?¿<\Ü9»Ø,Ü=<~ø÷þé½WÝ¥o×Uoß¾&Óɣݽô&…v÷ôVöìýr}EÞ³•÷lå÷ÍŠ®*¿oV~߬¼+º¬¼+ï•Ê{¥¢ûÊ{¥¢ÿŠþ+ú¯è¿¢ÿŠþ+ú¯è¾¢ûŠî+z¯è¼¢óê[Ÿ<Þ•÷À»ýSù-÷QOO>6RV#µðc'õØI==ùØJý ­ÔПÛKEY¶ž*x(ð+ Ì(0£ÀŒ3 îÁ¢ÀŒ³n=YðP`F¾¯g› xž·{j¸Ce‡Ê¿ó¦nsh´«Kf?vy]ÞÓ“?j›×P€¡C†  øÀíàø?v?¸à÷Ý>=ùñaÿ_vŠÛAÿ¿[ÆÍÁ÷õŒÑŠ¢E+ŠV>p/ùôä§6“©M¤6‘ÚDjgM戟Úe¦Ú$ªM¢Ú$ªM¢Ú$ªM¢Ú$¨þ£»ÓP=AõÕßÓ­nŽñ4omkñÖÆ&≈'ÙZkàñ´Eüc¿ûíw·_7²åÃ6¶‘Ê{Ü­ëñ톷Î?ØøîßN”µÂm_Š|ðÙ_í|ýÏÉøêã7ÓZ"Žz£|=âîx÷å³óoù‚£¬+Þ…ð·!Þ]’o·’n±²–òw!Ê­k Þ‚X®»Ð»;×}æëï}_OÝ8ñý-mÏëFîà ªm»že}šwŸö/K÷ƒMœ¼-|Ýé¾iâÁî˳WÑSŽ.ïçû'‡Ë³«g‡óîèë=7Þ9Eº—˵—å×ôr¾.0iÐÍ8µçíÔkuúSߟt߆VûÐê<´º ­®‘ø7þóæ–óoí[Æ¢Ÿ99¶|,øR‡–'[>æjqõCJiš2/7”ô¾¿¹: ­Ö¡Õ6´Ú‡Vç¡ÕehuZ½ ­–ylùX6e,2–OK¨ŒeTÆR*c9•±¤ÊXVÓXVÓ FÇ²šÆ²šÆ²šÆ²šÆ²šÆ²šÆ²šÆ²ªcYÕ±¬ê`é˪ŽeUDzªcYÕ±¬êXVu,«6–U˪EVoÿÌ›o_ð6ÔXÆÛßq'Ißý‹1]ÿóe˜}ëßxGGÿ`Ú@Ê endstream endobj 87 0 obj<>stream xÚäX]k7ý+‚>·+ÍH B éKBLkš¾™<lB!±!8Ðüûž£{×dïÕ*VJJìÕjÎ9š>öŠçxu)ã]€gr!žè Š'Ú¹âYœ†„guš ÞEÚBp1Ä%ô‘“û£ËÐprÃgg¡àiÎ :h<«+ýâ]è‡ÕÀqÁ+;Ìxè+†¸`Éh$ˆ‹¡ÑT ¢h2 ŒzT€4 ¢Â¨!2…rÌðY¡ 1PNôR¡œ21PÎ:PΉ(²$ÊÆÑ#”­"æåá!þB¡`„reð Ì\+ÁL2ÉÌJ$¸2 °'ÏØ`¢º"3’š45uš"™4%öBŒ³˜ º™þŒpñ††áý6ºÁhiᔘñR9(Ç«”G®Õ{¿zº™3FŒ¡X%¹Uƒo¥¡ªÞ)'D, aÐ1qڜNj*Ǽjl(G:QôÙ œXopW 7Ñ0º åL>Íô§@9W° ”-¶PÐ(ˆ´@¹0R$K €R \Q³ÖBSEé2?µÕ0+Š80«HMlá`b" M0LdLÈ‹ ŒF!Ø\Œœ=T^Œ•`(Óyä Œ£¾-ìž=[Þ¸°¼¸biy÷Ç»å×å·ûO¯?,—­Ñ·¼].^Þß|Y.ßCŠ=ÏŸ“w%my6RG ŒœnÀXñðÚu‚Ž_£Ã·Ð èW·×7ݽÿI¼l(›þ^žŦÐe ]7„Â/ø©aB˜ƒË\[møóÚÖFˆ$ºNˆ:&¦F,bs#Z‡˜†ë X;/Ö… •$~<ô¦2x “º.Û¹t~½®ÊCb×<­a¯Q¬¾¼ÛÌÞÅ£rÜUn‡æÀ˜GFÛ3þ¾\\¹ÿü°¼}¸þôðúîæöîùøÅ/Þþ½¾ÿÌŽÿ<üÍãwÙŽûÓ‰Óã„éq»çÎDù°«£ì°„|{«²¯—ÉZ¦Ð:…ŽSè4…ÎShã–S:[Žïv¶9‹ÈëœÙ–†¼Úx±ÃËCN%µC´1±]OŠtˆeL”F b¯xßKò¸Ÿ—Îáe2zS¼/ëz®@Bž¸Ûlˆ67N™ƒ×¹åÙ Â:Ó“Ç!­ ¬3+y\Ò "w®y\ó¢ß™p™Û'd®$OÂýz*ä|vø’¾æpMÉÎá“Ê®RûªÛ1þh§qz>stream xÚÌW]kT1ý+ŸíÍ|ä DÐú`iÕRëÓÒ‡Å.R¨»²¬`ÿ½g²Ûk»mgñá’{“““™9“L®D1H” †¦hR %´9P­hK`IhkàÊh[ÆC˜Y2Z Ê‚–Á‚>’t¤!eðQ ¡rÆ Öª¡$ðQ ¥g UÁÇjÁ8shèp7µq˜W0Ÿa_dÀÀXÀÀ%‘A*^ú܈A$ñ’aP 3M„ñ’ŸÆ#Š—†•à%% x(™¯æ V0gc0pˆ‚¹ð VTa‹([À°„‚¹aeQ0·‚Õ5Žp@à ÇdD560+ÂJl˜†—ž34‘DxÁ— LÌ Ìd13’Vs0YÍAÌäd&0§ÃlÎi|p™d0gãÉ`.0S2˜K8ƒ¹šïŒ»ÌÝ‹ æî…-Ó½È=-ŒÐòBJ3Äzá^L3£XБLRÔBŒ/s@ã&}ÔR¯ÇÈ’ÏNfªE6Y|ªY€øIµ^K4¦ØÊµØfV[¹â£™^Ì=[j ÚmF*k43á­’éɕ̺fl©ÖÂl¶4ìK8MÚ}j)¨sËx1GMHR•šÌ^d±f0›oØ `µ§ÀÁW¯†C¤Ô°?|\,¿O/‡c¤®`ï Ÿoº¾A7ëzýz Ï>xñÁ«Þ\pŽ>8ùà샋®>¸OUö©Ê>UÙ§*ûTŸªâSUœªb#à¸WŒ5ôè`=8±â®^<Öm´ölD|tä[™0˜¶ ~ަW‹Ÿ«áójº\ÌÏgóöÕ^Ng¿®¿_ZÇ?€çýlz~1ÿöÅfäꨔ»[ûɰßE³ -.´o&VôºRw&Z±ÜDþíâüêÞ¼ÔçÅæÕÇæMr¯ý}Pï¤GmN»P<ê^u£¹Ð}pÚ5Y|ú“/Hw5+ùÖÉ>xñ%ý&ŽûuïÏ“iË×¹ybŸLvkÝÆÔo²[ÿ¯“iÂÍéÕÙðaÿÏÁ;V5ZGíì©ðRsWä:¯Èu^Ñè¼ò “ ]èâBWº¹ÐÏ8ˆèσÈ÷ÉI>=É'(ù%Ÿ¤äÓ”|¢’OUö©ÊÎ=êS•}ª²OÕ~Ÿ¿­5´¥Ñý‰>}Ù¥ï„åá3ôúh|ú -ž3ttâòó#2š'žU&Úß­&f_AÀõ¯ÿ{oŠaÕ ü *Ñòòb>»7£¸ø« Ýž…—µè«‚> ‰w5K|먞v5+ûÖñ)ÞÓÌjŽunîQvmz7ûºXNW‹yßä·¹;Ü_\.–“¸‡Ÿõsvçïïoîÿ ú·Ë„~ endstream endobj 89 0 obj<>stream xÚ”XMkTAü+þ€îêžA½ŠH<E5àÍoõ#HÖÄÕ:,oö½êz=U=•°Ñ½õ-/Ѷó’ͰxÍVð:›£P«ù,Ønpâ¬7 âÌøI~xoÖ}´´Ík´ãx6x/l´1Èc³Mr†­6ù‰]Åá½­$Ÿ[Ûì!øŽäs´½ÉçѬG=`ƒ}W;4Ê'‹ïðÕ̽À›‹I0;72`\ ‚áͪɸH‚AæävdάGdÍ’ÝFT™Çf» ó ™çbÃü—ó]AæµØryWAæ½È̾½ó…A¡½/2“ÔÙ"Ô•Œ\ìæ^­f碤M£ô¥-up 2³9/"ƒ‹$s’9ËA~ñŒzDæÜUEæQ­r“ä㻨¿ÏjcyN‚Iê‹ŠÆ óšÓßV`2ïQàÑÐiwP>ô¬G«fpÁa0¶“ÓÀ­ra²‰³¯šhe n(5h Jþà=½n~zyz{ûãÛÍ×Ó;Ž0xö®NïßúÌyª[ÏžhHèÐ)¡‡„žzIè-¡­kp#üÕ§›_¾~âÝÏjÎîÿY¨kš³¦Yk𷦙kóL#û‹Fö°P3Ú4§]sÚMƒkþþ»—¼¿S í:$tJè!¡§„^zkîˆfjnšf§i~šf¨iŽšf©ižšfªi®qñ¿IŸZp¤gpÍ_×üuÍ_×üuÍ_×üuÍ_h§š«ÿ&‡”ïòR¾CÊwHù)ß!å;¤|‡”ïÐòZ¾CËwhù-ß¡å;´|‡–ïÐòZ¾CûwZªCKuh©-Õ¡¥:´T‡–êÐRZªCKuœ§úóë´ú…ëêÃýÊìqWùæÅíÇŸ ý(´G ór!ŽÂþHá¸\UØ÷#…óRáuýê´ï-†õƒ_~õÙ@ÔO"—EÕÚ@@ˆÐ"´cÚ1ñ±vÌC;æñßÇü—ÚDv endstream endobj 90 0 obj<>stream xÚÔ˜Mo9†ÿŠ%λíú²]B‚½€@°"{‹r‘Ä&( ü{ÞrV3a•‡Èîêòër=¶SÓV¹ÔbU ;-OVˆâ±Ò¶êí( ›U/lRŒj‘ ¢"Š÷ÄE\ÑJQ%´Zt„ÝŠIøcl‡.õÒ6¥õðóÒóp-}@© †/sÑJñˆ‡^ ;⫘ÄàDµÅDHÓ CdˆË!®—ŠN¬BP¸(ϘH„E±P²X¬Êx je‹Å ”"0ò [¡Ü±ÃZ©7(C”²` åaPV({äL¡ì‘4mHbd ãiÓHkäM‘WŠÄYEg@Ù¨0#(3Fg@,Á ‹déP6+¬H‘”µCÙ I6ƒ²5(#nÀdH7˜­A¹´5(wÈ[ƒrw(c„iX‡2’ÎŽåZƒ²Ò6€i³æè`¨ab!p0¤F(°áO8Âè‚δh™C§…¥aë8¤XtZ lÓåX·aKHÃÞ³åp‘é‘C ’ŽuÛ€ò¨á 帔½†3”ÝÂÊîáìEkllv­‘g§¢ËqF'ö –­›`t­£¸!¡s¿{Ç>0| P>U¤·Áª±÷¶ Ævxøpy^Öòzù{ž¸è,/ž=z/OãôÁ4ßMÛÖ¶¯mÕ×gMÿ¾ŒsÖŽ*ÏCxÇËq×K?öòÕòbóåêÓn9Ùm®wÏ.Ï·—;lú?ëòÏöó·ç?ÂðÛ»?/uùkyyuýßæý^ö¿™ÞáÒ ÓêM)oNyKÊ[3Þ§6¯âØký,5M[Ï+aÝ*4V÷“O¶×ß\_|ØÝ3R3x@’WåˆQiÎÝrî-çÞsî9¨”£Ê9ªœ<†9ªœ£Ê9ªœ£Ê9ªœ£Ê9ªœ£*9ª’£*ÉÛ5GUrT%GUrT%GUrT%GUsT5GUsT5ùO3GUsT5GUsT5GUsT-GÕrT-GÕrT-GÕ2T¿×“O6·ï/.·'ÿ^¼Ý]ëª:ÍÚýñi»)ë÷ ¬øm±×O®Î¿„º«'ã×a(ž)'ý š|ºÝœ_\¾{À•÷FìÙ÷‡ifË8·Œsß‹žŽDO‡ÃFfÏ8Ϻóùüyê©úÓËÏûÄ”‚L)ʳ ½OL=5K 4¥Hÿ¼õT)ê©JÔS…¨§êPO•¡{½}ÿXàíÖ× _¿bøX[_[;þU£Ý|ù¡"^ò]/ëñ—(}â¦ÜÃ~x÷Û—ü/|4ø*ÀèJÐ endstream endobj 91 0 obj<>stream xÚÔW]k\7ý+úéÕ|J‚HúÖ¤¦é›Éƒ!¡/I Á…æß÷Œ¼k²ñ]µ-4fõ1:3:çHWöÊ¥¯RÌñ£…Ðôj…là× ×o…-Æ{áÑñ;Š(úT‹tô‰ŠJô¹h‹>à8ú ØèÛsÖì·Òæ|/m®¥‹çZzG9Le0b™ËèŒ_‰ºÈQàˆHT8KD1êunh¡ÑÑ Qh–)Ž`¡B*F£#U“ ‚‘x d×r dLSórºȽ¡0òÀ.\<…)ƒ2ðâHŒº¬ŠFŽZa²ˆ­Ô‘KÁ+Sà€XF$fnXn2°%¬p²D…P‚U#ÈÚQ¼Yèdäc‹}C7 »Ç* û@.rÃŽ-qCw ·` qÜQ¯c'ÜGÄyóäáÓŠÔ¨BJšn¨Q3þ„ ­7B#ØGÂp‡7A#ØhZëÑ04b_Íш\­…«bÈSd Âa 1 :Feڶ؃Gi0¨w ·p)¸–Ôu w°îàHæv`6PÁ@!Á¨E+œâ˜ÖŠ¢‚#¥à\+Å–a8åðï04ÂuÃaøðXƒ¡"¸£"±*ÄoÈŠ< »Uƒ­ž?ߨl{yç­–_ßm?ooî¾|ºý¸]ƶ·ÛÕ«»÷_·ëßK‹/°î—íêöëÝ÷ÛÛûÛ/÷¯?¿ÿðùü©n¿}øóÔ3i|¦±4í_K“®ª~[ =€JNCß–r(” æL°d‚5l™`Ï·Lpñq\¾?ïEñeÌeugÙš‡›¸f¨?¬®ƒ§¶ÊKgRÇ×aé‹”Ö”›tî@ŸîÀúr6×Éκ±\7©ócuIy›ŸÀGÊw¤Æe»JÜÏ‚Y×´ŒÔYLsÎó”øœŸSG§äöô†ïü‚yž’ÛŽÅt©Ï‡íXL—‡šçe`;Ó¥¥yÞ¶ã,]Yú­x(¬i;׉Ê*±œß²¾ $wó§ !)CHîò?îëz>t¼>rx´ÐÉ'‰O’$81ùî,ßÕc¾˜a>|“ºš´Õ¤_šüoß?røáñþ½ž÷ïÍ`GÑí(úçátÃ_Ÿé"âü¿j1)—&p’éñĹ?!Ù'Ìd»-È%»ˆ4ÿɽ0ù?#äo?Ó–y[æ5n™×¸e^ã–y[æ5n™×¸½ÆÁ#%JN”†”‘R*RJFJéH)!)¥$¥¤¤”–œÒ’sç1¥%§´ä”–œÒ’SZrJKNiÉÿTË¿ÂTYÐ endstream endobj 92 0 obj<>stream xÚœ—OkUA Å¿JÀ½oòo2R7Š¢b݉‹-EÐÊôÛ{¢ m7ö¸º™áÜ3“ürûÒ&Cj¸ÄÄ#d)ê Ï)ºÏóV-±Õ²-îÐé/èT% :5‰‚N]R¡Óœ­K™ð(ìÍìuÉܽƻÑë-µ°¶!«}MeU¯ ×éµËž½Ñ¡½ ŽÄÛ†*n]VgÛB°q¸mQkwÜ\mA슴bœü v xv5ªÅpξ—Ã9g‹á<µÅpžÙb8êVçŠNεãw6¨ÄçÕµ 8ïN*༻Z(´.W ¬£ë Ó.Xl]1dbÖeIE0 ]éÕÈ €«LÑb8Çn1œ3Z çìÒ&œ'Ò­ çYO8w! çYÄÎ l iƒs‹á¼G‹á¼³Å%>F‹‚h1šalˆ‘‰+jÓ^® bÔqiŽ ‰Vtç´8ÌOñÐÃ9f‹áœÝ.¸¥g÷ PyvÃ,8Ïfºà<;¯çæYèSoÂÁ¾ºkP}_Ý6Ðùî¶AøîÒ­-1Ð¥µÔ¦6Z-…=Œ@€OBûPp Ë'‚Ýâ)áÑâB°[¼$Â[ çÀ¥Ö€s¢Š‰tŸ=;¼F¥/oon¿¿Þ£‡߇Ãùß­+ÉÞ9;û£VJm”Ú)uPê„úååñâËõÕôú½Wîí?xoR§¥^”z3ê ¨EõŸÖ~G¬ŒØ±3â`ÄɈ'#.F¼ñ¦ p)†JATŠ¢R•â¨H¥H*…R)–F±4î{¤XÅÒ(–F±4Š¥Q,biK§X:ÅÒ¹?®K§Xâ·øù§Œ°õ™z‘ÂêV§°:…5(¬Aa kp?šÖ >Ñ XÅ2(–A±LŠeR,“b™Ëä& ŠeR,“b™ˤXNŠå¤XN‚å»Ã›ãÏ›ï§Ãùéx{zu}qy}»§ãðñòÇÝõcpepepepepepepepepep}8€?òßQ¥Fq¥Fq¥Fq¥Fq}8ŠÿO¾\¥è*…W)¾F}¢Æ}£U{,Õ_ só^€ endstream endobj 93 0 obj<>stream xÚ̘Mo7†ÿ ^ÚC½$‡_¬¤@‹«'ÃÕÞºBm ¨hüïûÎ.i‡‰e‚9õ ,µz8;3ïmÒVi•4)pqŠ-.^J¸e’Ã5*KB%e£`¬È‚3ZQgŒrœ±°Îò˜›pÏ{á¼ò,\PÁ U`á’Š$«˜H%‹{ÞàŠðx{+)£áh²ÇÀC͘já¢ñ27*c5 Ø„‹0ŒépÝC†q²Ê8‚‚e—,{–½8ÇL a9Da9Za9FÀ–Å»ä`9EÀ–ñ$<ÏpŒ€­6{ ‚ÀAYcF^‰µZ`Æ@BöZRNs>,IRHK ÑZ'zXv &X/zXö ‡å zX c£ìœíËA °œDw8gy¾Ë, ¯HKe„€C;„DFD…ð4'
!H½À°,ËDÊÕIˆOŽQVŒÔ8FþXY Z½x1ü Á†WÃïÓáýf7\ † «ïípùpëN¹óòåBû.:tѱ‹N]4÷Ф»hÓEÛ.šºè.-©KKêÒ’º´¤.-©K˦”ŒÕôˆ›áüбºqç37·ÛýÝwVS5½ºÿ¥ Û÷HªžcN<Ç|=Ñõ=Ç÷á¡ßEê{wêÙ«µ˜Gdƒ·5Z<Õ|lñ®æS‹÷5Ï-¾’™½nñÕÂfÛô?Õ|Ó®ù–ÿ¶Ò—©å¿­õ%Óâk}ɶøZ_Ýô¿Ö×4ý¯õ5Mÿk}MÓÿZ_ÓZ/õ1ÌÆµøJ_ir›£®ù–ÿõa,-Sƒ·5ßò¿>¥kð®æ[ûC}(shú_ëšþ×ú†¦ÿµ¾¡é½~Ckpµ¾©¥¯3]ûù•³óÿŠ|œwކó¹½ìbþ—²Løc;vÛýøÄœÎÙùo>ú;Og;ùTyfOxfŸ˜Ùy@ûÎÚ›N¾³óÔÉ÷vam˜ïTÚw*í;û/ß©oèÔ7têºô}3ü¶¹Ÿþ9ëñÓñõx36Çí´_ß?[Øõ¯¦Ýt¸ÒgØ–Ïué\ýÒÍàCœGŠ#/[ÍÅü`ñdý:ïEy7/»tÙ}Ë®Zv˲ .Vʆ¾.;š¼]%>cfy3Dlδ–W?Þ™3Cë[˜=#Bütý˜ÜËãæpüéÜÕH˜ÖËê§ým¾‡[ÃÅns3¾Ç·aµ›nÞ•¹œ—S²4È¥ñ- miTO¤(·¥¥Ý,mdiKÛWÚ¹SVtžeò,“g-MTiŽJÓsJ®¥Å)­KiIJ«QZˆÒð¹Üùt¼o–¢°R/?löøÿvúwÕ«~.µóÝön?¬Æ¿¦Ã8¬¦Výåñ~7~9í¶·?ä[_¯úüÃúïíÍ»ýøñ#núÿýãÿ`ùâÅI endstream endobj 94 0 obj<>stream xÚìVMo7ý+sl/]’óAØNEÓÄzrP¬M*DÕmþ}ßH4ãÔðÞŒµ r—oß›yœ!XC¢@50UÅ Sƨ”RÂh”T0fb© q‰+I6ª1&¬ÇHš‰,.2™ŒB9:N)›ãŒrõ÷L¥‚'“ +Åp| ˜(R¤!!Œs>âD ‘Ö¤˜T„•Œ¢0ø{T¼ÕT0ÉŽ³y* fË`.f×b0g˜Á\je0ñ/`®Bs͈3¥à)q¡ƒóTLÜ4 n"ĉ]K&î›0%qãD0qçRR·2ÉÜ;³y^æìîᇔ] F¤âþ)˜‹¨`.î ‚¹º…áàªGÏK ÷P±kÉ=Tl[rµ³kYÀ†º‡}g¡%bu‰Í=„›{h`ΞÌâìZö:€–yA¸‡æRñËØÄ“#IÀUèIô¼à£`{0’„¼kVL2Ôሰk!J”]Í…DâÁ²(¶¨–@bØØ Å\-`Îþ{sE%×"¤GÑ¢¤Ql(QÄRK&e¯óR0aÇTLJ­(c/¤gφŸ‡WëÏÓŸ‡ár7Ý|¼Øm?ì‡Ëñý4Ãå4oÆùúðy7~s=í¶›oÛ§«i7ÍoÂwÚó¶-¬~ÛÞ|ÜŸ>á£>~¤_­ßíÆ¿\ÿ±ÞS^Ofmý,ÿhò?PA‹z=\¼ñóãÙñíð Ôƒ/^«Ÿ:ž^ñÜñ±áÓ"^nñ±6|XÄkÇç]¸„·ŽoùƇó=çYþÉž Ü;KZ§ËN/÷:‹{gIëtI‹ø|‹çÖéñ¥ã[§sYÄ׎oζ„?©ñ-_~8ßsqžåŸìÉzgYët»Óéù~'öÎÒÖéñýN¢­Óµ,âûD[§«-âûD[§«,âûD[¾úp¾çâ<Ë?Ù“A{ge;uJÎ_:…ïu–öÎÊÒðºˆïw’œžñýN’CÃÇE|¿“Xi'U}.¶³üS•oô«ñïÉýú°ž_/þ:oÛý‡§Í8¼šWïN˜ï÷›qpþáå~sçí_\Ò¹OA×ïïãÜá_þ~4嘾–æÿ¡ô>ó£Îö{}5ü4Í¿¯w8˜c¸=˜OŸþûõY" , endstream endobj 95 0 obj<> endobj 96 0 obj<> endobj 97 0 obj<> endobj 98 0 obj<> endobj 99 0 obj<> endobj 100 0 obj<>stream Architectural Evolution in Digital’s 18b ComputersBob Supnik endstream endobj 101 0 obj<> endobj xref 0 991 0000000000 65535 f 0000012514 00000 n 0000012811 00000 n 0000015370 00000 n 0000015616 00000 n 0000017260 00000 n 0000017519 00000 n 0000019759 00000 n 0000020005 00000 n 0000022615 00000 n 0000022900 00000 n 0000025492 00000 n 0000025741 00000 n 0000027694 00000 n 0000027955 00000 n 0000030311 00000 n 0000030573 00000 n 0000033242 00000 n 0000033503 00000 n 0000036400 00000 n 0000036675 00000 n 0000039551 00000 n 0000039800 00000 n 0000042781 00000 n 0000043056 00000 n 0000046029 00000 n 0000046328 00000 n 0000048826 00000 n 0000049101 00000 n 0000051859 00000 n 0000052133 00000 n 0000055188 00000 n 0000055437 00000 n 0000057747 00000 n 0000058010 00000 n 0000058055 00000 n 0000059831 00000 n 0000064907 00000 n 0000065418 00000 n 0000065901 00000 n 0000066199 00000 n 0000066428 00000 n 0000066558 00000 n 0000073631 00000 n 0000073821 00000 n 0000073985 00000 n 0000074149 00000 n 0000074307 00000 n 0000074471 00000 n 0000074550 00000 n 0000074612 00000 n 0000074678 00000 n 0000074757 00000 n 0000074811 00000 n 0000074958 00000 n 0000075076 00000 n 0000075186 00000 n 0000075316 00000 n 0000075465 00000 n 0000075635 00000 n 0000075735 00000 n 0000075882 00000 n 0000076052 00000 n 0000076152 00000 n 0000076299 00000 n 0000076457 00000 n 0000076557 00000 n 0000076689 00000 n 0000076835 00000 n 0000076954 00000 n 0000077054 00000 n 0000077202 00000 n 0000077321 00000 n 0000077420 00000 n 0000077541 00000 n 0000077654 00000 n 0000077769 00000 n 0000077920 00000 n 0000078043 00000 n 0000078159 00000 n 0000078310 00000 n 0000078433 00000 n 0000078563 00000 n 0000078745 00000 n 0000078875 00000 n 0000079005 00000 n 0000079135 00000 n 0000082241 00000 n 0000083420 00000 n 0000084559 00000 n 0000085531 00000 n 0000086627 00000 n 0000087740 00000 n 0000088673 00000 n 0000090080 00000 n 0000091095 00000 n 0000091130 00000 n 0000091154 00000 n 0000091214 00000 n 0000091340 00000 n 0000091455 00000 n 0000095142 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/sds_doc.pdf0000644000175000017500000016550311143604454013516 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’†/ úæ‹ê¤qmW’Ûfœ>È’-7µ%Ç–Ò¦¿¾Hb?)§33 ì·7`ÏO»²PzWú?cãâÃÑ×'fwõù(tïNž OWG?uEåÿ ؾø°{xæ&Ú.‹²Þ½=* k;Uvá»Ú©®.*½3¥*ÜçG¯²Ó\ekµÎçûª°ºnkèÜåº(u×f6ß«ÂÖZuY-ÍRšq¤LþÇØõÁwYݶÙ]¾w,U­ºìýøù\¨Üæ{]ضnêì†~ m×4•,ôÒ7LS›ì³ZW@àM^ec*ÏçßÏþxdꢩÕn¯«pP—î$`/jhfûÜ}¯ê®O§²ÊÑŠ/"Ù½0„º¼iZ¿žŠjÜ€(”Ò2©šÊ £5N’­—‰ãðìG7A©M&=Ìh!k›]ÛÖE'S”*½;{êÿùÛ°â8_q?=:ûÝ«ì‘gTwƨìy¾o ÇrÙd/üF[[×Ù÷ùÞe©KÈÐo󲨚¶-«ì‰ÌúFœ¹M·…Q­ÇLû¬ÐÕM\ÌX tÉØã5H+9 ·™¶ŽÛŒ›ö{Âïa£´Ç›ØÆÞº‘Êu×ÖálüþÞIÓ¨ÊÖк ˆ²å¶0]e‡ë½q]¦ÑÙuœ|Z•.“u.ÜÐH£«²Òü%W¦PdÜ®åÄÛ9Êãº@î4o…mXäZÜбÀp`è åõ\z?b3r,§ÖgOÞJófºbÕ6-²›L¸‹N=&Li,þÉ5ËÖ˜ÎAÅQÕžÑz@Öµ³µS™ ßž;ìÊ"7w²='‡Æ PÚÎNé+Yö÷Zô‚ó8¸‚p  ãAîN}ªq£0 {)dƒK*ù Ùéô~púU{ƒ©pg×€B\ ze“7H ý}/Y÷ÿ ý(‚}o.KUµ^½›Ðíµ×‹Œbª~qéãg-0= ˆ—kxûˆ‰Í×\ÆVЀw2æ ­‚{aßUè­ÛÎ9àøšUh*¥ƒ!¦e@‰ÍÈW·í¸noìjך ËðÁaT_ϬŸ“6|Nk;'ª Â•Óï²Uø5=Yàð6gzŠV‚Z0˜;؃ìüŸî»óF >°ˆX+ÞÅQvÑF]¹-ÿ’k[hU5K¶?òØ~¶·²‰Õú!—§(|¬¬Ô#I·¥òH)|1’bód0Ë61»`:7H2Š,•dqêb–TˆÌ<- 1D>€0H¢`j4¡•™ê|økèk«Ô>2_;™_¸#&ÞÈŽæú¤ñ´®èüsz8 ÑÜhqéB•°È+ €ÂÄ\. äœn芮ö¥»’pïÛGM¡lŒ"ÐnER×ì=84¸–ÜNYlUã0›ÚªyNÁËó£V륭áG°*Që‰N¹ ÏtCüÐ>×a r٤׷Ô! !»Ke8r–â`æ¸ÿºb&¡(û9 ü& K’0/€&о˕öžÚ qê÷Uêv1¼c½8«…Ó¾§O2‚(Tê³`ýÕà7n-«M«œÁ£]GÕäLàÜr]NCs‚Y *7‚íYž3•,8{OqGäÁ…œ£?u[ÏLJ_ô%êè …ã¿ ùøG°Oødb+¡g$hÜ-%k_ôƒHØÍ3.çYÞ/*.ÉÀUfš³\ÍQ^Õ`´ðì˜ñ¼‰j~‡Žœ±•SôòA ìW„ä€ÌL& JZ6¾cTÒU¨Ò|c)"ާ234¶iÞ?„Þ»œYªÕ¼?ñ®vBŠÏ|!=06ƒËàØœß5][Yƒ^àŠç÷â$ uzúÝÑÙÞÅV @í¡tx%פjrP­£B€tGq±*ËϽÝšàC€üØü¼éΨ×*ŸÐ_Gí÷ð5…ì'šÒqÈFM§>e1¸›'I‰ šìó8“ñûÜW±øSÒò%‡Õ7ÞR¬ú«_×¶š:@ÖÉpÍâ&Ðb7”«§¢^ÏöÕCfräï½At°&ÄK_ÉOcÖ}XâÂ̶ˆž¸ûi¶ «ÜQ‚×Q_—ÍÚ ¡Ö™f¿¯B‡ßãr]‡Ü»ÀØÄ°Åæ$RÃ0ømÝmeˆ¶¯îf­„\•Ë~ö3±YVžVÔ~{®Ë}6Wzªœ+Ù:»ïÏRÔ®¿¿ß›Õ9&°óóòÛÎÅ$/§ózÁÅ.=D1sºá!jÉ‘p‘Òïžð–´~©ð+ 5ЂæÂ›“Á†Óé¯óoôƒÞ]P'›™£ÙCH¢EüõêÕìß´˜ˆDïr¦Eß>&ôù=Ìá,5ˆ WAa—‚.¹ ÂY¸ÂÀ¸Ø,¤U× ( ·EïE!y”À_OyÓÎ ¬)ï ¹˜áÄúc#¿cE 0é±ðÇóî3´/"Ï¿‰è”Œïk2òTFž2–eý››ÕƒÙ ¡ûTôöš€oä‹g°ˆ ª€`…—Ò  NÏx¤ðDö’y%ó’bd^´4A+ËOŸ)¡H„s2•7@bG‡>ÇýPåê1í‰QÃ1(5(©X«ñó—)ÔTàa(5$Kº%€l#ÞoÇÌ>d}#½âP(:žåûé= IGÀy|Ó™vTør ϰ?.óg=œÚ¢¦†°¨Î•¶ ±SÀ–WY.ðƒex~h œNðœÕ/\æïEdöÌ1cýA@¶Kö #¯<Üš ¢Æá©g´æä1·$¾Îþdm,XciŠåØÍZPXñÐáµÚÜrírbž‡0¢B,re§WsPl'Ø@“×÷{¤¨â ,Öãþ‚ym8Âïtùje¹å #°¶dó‰ò…ýÀ Äv¾A¯ÑEx_Kl]o }”C}âNc:%ógN«Ç*¿XzŒÖ–b\;}PÎp*S%a EÆ‘'Ô'Ü#p{̬۪“vL¤œ¸‚xRÜ’Kì—¸ˆ\GOK¹)Uú>»àÑÎÌ\j« ÕV`.zŠGkþNŸ/»îâ{† ä¸A^Nu#~Ñ¡T<”:‰ÉÜÒ¬FÈÏóÆÑoBBw=續K0¦ià`iÆ1¨Or5ž~ÐИ.©Õ2h¸qŽ}ñí1ÉêVlø^`Àèö»6ºý:aK| ·®#…{o¤@ÛA%éÑBbïC r?éð÷å;‹i`5»³¨œA[Ëh ¶ä¡§ú”Àã4úýùRº«·{ƒ¥e9Ç84 ÍyÐFÇ€Æ3‹ú4êÈÂ5 »àiÞÚÁA<0ð‹¸ž_Z•͆K+’] ¾f×kÙX&+cþ-—ŸÒ„;:¸¢ä/Ó ºI¹*#À tVËëø´lŸ?_Ï^WßA`Å¥w¥ 3ÆêÅáhxuÈ}Þð“â|x‰š¿Yü?+º—^B×_HHµ,Åå˜<§¬ÈeRl”Â’½ƒ-¼§ˆ!-è7æ'\ÆÖÏT+¹X&€æjAŠÖÒ×^¬Âσ~ÁŠá¦…6*1'OÞ"›¼˜AªëøO\¾°Ø09ßÙP°´T³ÆtW51#Ø VlØ3^ò9…äU‘¶G ì+º¯{<Þ«Àü/S“Ú“yµÑˆU^;>:ëÿiy,{Õ¦5°K*Jjk诒n)Þ“Z8þŠ [`þ{VÐ:=W°CÿqI—ëKË6ÖõúòœÕ_®ÜÌÊéµÅ ®Üówi½ÖV…Žå7pt‰+œ?qž°´C—¢4¶¨êYÊñÙÑŸÝŸÿ`ï •endstream endobj 6 0 obj 3123 endobj 15 0 obj <> stream xœíœKsÛ6Çïú:’m°xã˜Æm’>]E™éLÓƒk»NÒú‘¤Î4ß¾ )KkmRŽ,R²ìË $„€ŸþØøa,åXÿ ãø|ôdêÆgŸFeòxú|n|<}yPÅ_™@íãóñ7³˜q Øñ쯑€¼pU©8¶&~fìP€‹7œ²q>{_å‰u×9 68кÈð{†ùÄ€ptÌ‘`”Q6»Lw~Ì%(„*jZ\ÿ.ŸÄ޲ÎKªJ…ž¦üŸbû,‚´*ƒTÀã2­ñmoš©̾I)@ovÍæ³ËæRGïœYŒtÖû^‡ŽF=Q‚Ó&S׺HâŸ&* 8áÇTà«Î’mp>ÈX‰ M]à5¸,¤tgÑ(–#ñi}ïQº—Pýš£zÕR"(—ûà90Ю‹ %Ÿ•oö€Þ³º_@÷׎¤µ`ÐÝBÚÅ7é‰u©XQ©x‹Ïž%æVÌEo³×ñÀÚzœ b»gö;KÔbüa³z×Q»£Ä j7ÐÓ}³˜Á²^ƲW ¢†iŲlÁòÛy.GÕë5êö”óc|*„p0V££úøMV+ݪ.i¤Ë^$óiú5ø9¥¾É‹6:ð!ôƒÌáL¥²ŽSËîðþ—aMT 2Í2uD¥Ò¡™ª™—,() /9TžRO‚í‹ÿË„ RÖU ÝkÖ¥Ð$m|bå½ÌfÄ)mŒ¼u´Š‚¯ÎózOÒU™©âW"êÌÜ¥ù?Dsèý»ªÓ o|v#©°`»øôIúK>Q ƒº™kEj]W)Xƒ‘zïQ‘qeŠj*|?`è~ÛÍõoÅ¢)Ïí2‹Ð¯ï°,7SXꈊÁÚ<åBI ”V·°  áÝ4-ÀIi‡ÂIk(:©‡¹§œô58™W…Jè:jQédï£ld4>þ¢<¨§t`S{ç̵öoGm–Œw€O3¼øì€ÀGBR×õõ‹”é˜Ñ·Ts¶ò®nͼ a+àIëuèg|Ò|Höí·EÀÛö¹û³Ïqìû1Ýšöt^°èãÑ•€Hó3ÛKWo¤QË:ÎV©âº7`ý€û@ÍÊÜSñûê‡Ù³l;Yö@Cçž‘úö)vã©P M;N=‡Óiò.6³KnD—MjܲÑ-üQ…[LÛÉ5yɫǿ[ÃäÓ<¶ ¼Nó¬¯Ôc²;„Ã5Õ 8‹ë’[D„­4{ëßž…äÝ8ôŒOQî· Múoá •XùɳAÁŸjJ²çœˆüLÅ_5Á#mÝya Çà ’µjˆ´˜½NNÆß*™¬bG”¡ç ¸]§fEB(]¯[|ödÿgIÝ> stream xœÍXMsÛ6½ëWðV²3‚ ‰ÞÚ¸MÓ&kËÓΤNF’m%’åH¶ãüû.H»¤ÖeÉiíË,ö-Þ.>>E©*Jý †³ÞÑi].{estúr—½O½Bdþ¯l x8‹~À@)#'œ½T8W¤yeUFÖÀog¢\¦"‡³Þ»xô¥¹´*¾J Y*Ç“DÊœŽ—I?i*­£D*¡U!ãYÒWZ¤y–Çcߘ™£ó`h‘ô®\ü¾lÌTšÇ×Gï[´fÆÚ4£}ç‡à!Œ-ÝÐ9ã›®'Ô¶ˆoÑœ§FÍ“OWh~Œ0‚PIoÇÅgI&Rë´Ž¡QÃ8G‰UWʬT±FS)µÚ𢥠šÕ (BÈH¡iøNâJ‘5+FÁ´~éÁ/£¢Áëüþ´ã"iÚq-éØ<ªRŽcBcŒQ©0²€ ô3hÍtúà„‚°À"i¡%ü—4 )]QªKVX Ò/4~•À/‚Çsì <`9•±™Ÿ/ û)éƒ~ÄwBZ©á1:º¬ç«tIk`ËtAƒ½Á·ïh^`²íUÉ’¥j´³³LCxª¼ª¤{ƒ+%Ú T6­õ“’ñmS¬ÿ´²dΦà²õiL»¹"Ò^C{º8b“°[©Ôü4OêjqyŽ=ç_e™H®’Ü'И§Æ<çÔm ä·Å}Ï™IÙ"Å솕Ã×qc’ø=±0º¹ð%õÙã–n¼a‡PñƒÙÝNy±¡ WõÎèÿ]=:kOÈþC¥{ zæ'¦àšSüž©È9¶ÏÖðä\\s¤l«Æû •¿8:çɾJ-“èŽX'ÆÕåÖF= ¼–˜*_å„’™Y+Q|µíp­UNŠBW÷Zµí^ë¯üB¦ÑH¯¤-h„iÃ.¹¿†îoÀï±3^†ÉÅô!á&nªQ¾«Î« ´—ë­Ê[w[öIhy°h<ƒ(¡Š<—¡QÆ}ñêĸÕ+ˆ7ôø#¿#®=øï»<DÜÑ®ý ´ádÊ /Ùm¬q‘ I2bWüëTÛ—z¼X?ûN½Þ}Pç8ÇçÄùÚµÚÀWÏIßÑ#I e‚:2ëWÍD^WE4A&Œ¼¼Mžù“wîš4dúE]ô> 3â¯8À%6&+¨l¹|…ÔB9ÒaÂØ¼ hÚž[:ÿÐàᲆämLámÌ'Æ‹`í$ ó¦Ê?­ª’}ªd]ãVð,Œ:è Ç÷C£ H£ƒ)Âá „'ÏÙ¾ŸNØp\±Ã$B‹ðW„°ÃˆÝ9ÛÓ;Ø%Æ.Ø3v}g¬…Â/ŒÂ.ŽKÿs@ßô[C6Í…, %'‚WaÔQ@o™´Á¤"«BT|ÍBBxÚEÛžŠyÐé²¾aÛ0Ám–-•r7,#2lÁv Fl‡½)'Ï‡Êæž îXE ™ôfe0`è¿Ú> vÎÔþ%óuºÜ„eÄS¾m“{ŒÓÛÿ–Ó|³÷éQå¾^Ó+îB·É C“ÛÔ‰“$ùõâ÷[.QŒ§k> stream xœí\i“Û6ý®_¡oKnE4‚×^U¾âšØN챜lÕTjKsÚ›i<š±ÿúx ä£ i޵±¿´IGÝ?ŒãHÈqlþ¶ÂÁÙèÁn>>YŽªÇãÝgpq2ú0*¢Äü©pùàlühª?b\Fe6žâ¨,‹8¯{ã,Õÿ.Óq.â(× ÎFÁ8œþw$DTãé‹Ñô¯{ÁË0Žâ¬”iž -çBªz*ý:)Up@œîyo¦Q0«^'2΃9‰¬Õ~3R‘4]ñI,ô¤lšÀ²Ó7ëåÔÌ-IóTòáà zzNR³$YòGÔ/›þŒžîÓÓS;;ö•3®yš UŸÂ‰Œd‘•E³&£?f“wp¶—ÔkàhMæÅ¯Ó´ÉÔ:”"J‹\{Ïôp´¼ 'I”æ¢L‚§$Nußi”©D™y¶OÿAâ!‰G$~$ñ_$²ž¸C"›ÃC‘øNòI˜Å±1R½F$"ÖQ$ËvLiØ™S )-’Š:|Cêq•–|}¥±!~¼yMÉ(7ÔÐ1Q©Š”“‰Æ®j¹,¾¡Q^$ep¦u õS)(UçI„ÆçV:Dó„vÌ‘«ð>‡>^¡ ƒT¼¥QFÚˆÁ¡È#¡d¬ŒC£í©4¨©ÿýCMDdº¯L6ÑVãb+ ¸VzN±ÖG{ÍOVzh¥ÊH=ŪLWî[EÏc•Uú^`¥ƒªìÍUÇ"C¸Æ A+>ÂHö~æëvÖïK«úzO?ï:M,3>æTÌõ„Ù÷ nàW}ÓH&3ØÖ¬š­Óò¤;m™iÐGl€Eå¹R›ºL­›8óFѵà;¤²NŒ66/ƒµe=0±;ëíTyIAkb{Ç[_¿D9­&ÙpÕŠ´ói£(qÄÍ£ˆaž½g,ú>–•JrÛåf1“$K"• ‚dŠRæçŽxòÑ>c é!¡4óz}È­ÜË»‰ O”—–ª“—fzH©L^ºÈpG™Èr¥ŒÏ¨(-ãX"ÔëTYQmy…6™ ‡2Ê b¯Ì7eRæ©6zõPäv|ZœéDÀ`8K‹k€_ Ò­B× Zž =¡ÄäÒz oé¡“C¶©ò9R-æt§.è.;ÛNÕ×ÈÝÙ<>‡rK·½íÍó˜Gº % 1°ÏƒâDßñÆj÷䢩èäâzÃÖS,ôÖ-K r0¥ëlnâé¯4*U±NŒWì´˜C½Pà3¿rÓbð,ì¾ÓÁ. ×+ǃ1^ÃîœFÀäL¼[^•æzH1iUk>S”i:h¯ X›l$ˆu¢£ìÍžZij¥±•[é••Þ‚vÂJ™•ž7R°Oš9`q ³ùK˜ÂŸÁ¶ìé‚Ä ÿ€±ß“øÁ>û'|*HÌH|Nâ/­()IVw`¨ÄJòÞPZLH”kJÞEH)+÷–Ò¢"±ø¢BŠLög6¶=ðè;I8E|!kÊ–A¿`}±=xŽðªOd;øO’œ° íÕΧƒðÑŒaoýeô•ª×œép|j‡J*Sª4*ÒÜZ´Gm¢ºÖÃŽÌUŪ¨ð&eŽIl68<y@Ùìtfn Ò¿»Cg¬£îÎKªh£S£+aCAíÛàc}öÐ"û’þiFu®í¸XðÃÁ.ϾfìVø‚¯Þuá±K±ã½ö Á—B§À²Å—Î冯+—ðœï63Ê¿f—ŠJƒ/®,]Cugµ%Öo’aÍÙÉz/ˆ°¬Ú wQšµìÇHW±x¹½Í…=†|fõá0CYg޵Ëh€,ì#–ä0‘%Lç]ÕV¾ìž#«$ŽÊ†$H›£d«'a÷ŸÎåÀN[À¶˜øþƒœYá®b‡À|¹=¹ö¸:2Xk„{ƒÝ¡Áì­_ˆíZÓ-çzÖ±Øð}uˆˋ٧Põ^òZìÖ7 |‹ÇWÈJ›á½•®c%„~kSGá+ÍD{~Ǹ}3ýæ›ÿ6Àg•ߘñ’RºˆA›Ó‹pëàÍiŽ•79>öœÝ:Ÿ¡LÏí¤«¤Ä¡«ta`÷•µ%ÌÜ¥Z`ˆ“ëPZOŽå×ÚªÂkfÞÆ‚Ä›pOV·uli£KØ€)û~vÝ{N‡¦ËW}ÃÝ„CãbÉ»Í\@uŸÃnGYÐÍwz:âãèæ%üß_×ÍïV2ŽW6°±¬VÖ.PÖkŸ²ú·¨7dEnKoxÇÆ k€7ï­zQ;HAIK¶¢·tu®s³®o—ÔãÄï *³¹¿ÛN=˜ôÃY¦ƒ™´Š1âl‹oëá!·´]Vf«€ùæFã‡ò ø.ˆ‚‹(Þï­´ ¤Û=ïOò15‰ãÁ›°`ödzY|°é9· ðt‘quªD¿RÀ±¸ïÓ ›?«sÙÞÄr[ïö7†êü{`Ÿ1¯ñ@Ã@V@ GdÃî€fIq@±™G±øHq:O±§cžß‹K¸²Å‚­4‡«`UÐþê~×þuåhTÇÿÇJ(¿ûyå[ß…z_.ŒíÄÌ‹K(/ß‚ É`[}v¸A¿x8ñôf5hmëÿ¼çfLê÷LïÛ7õî¼_m¨÷{ù³ØŸe˜HüÉ7I¶ ¼lE&oCcPÓ¾LíZ~o0I¥ßÊzÙ:¶¤ßá’ðõ |sg“ÃxœUaˆÅÑx£¥ † L5Ø\k «"ॄXׯʢøü §s^PýrÌ8€Ð„ÁD¼¶Òž•b+ýÍJ” Sáø«clP¦lpïÔ¾™‡qÊËã_ûáJÎ[ â² '÷¸ºÁÐWq{´#ø}ãß»îÄ~›6»¥ˆZ¶$pöã-R±ÛaƒaãžÀ1k€w ´â@þÅ•´- ¯CøÐ÷ném¯šqdá­uUÆêþoŽôŸ>Ž^ë¿ÿiP˜endstream endobj 30 0 obj 3014 endobj 36 0 obj <> stream xœå\YsÛF~ׯàÛ’[!Œ90¼ë­rlùØŠ¬M—Ëeû–hI»¥ˆTœì¯ßÝ àAê°ãÚ8­Áœ}Owőң8ü«ƒ³½{¯ÒÑÑj¯h½zZ—G{¿îe‘ ÿ >8ý8ó•åQîF³Ï{q”çYœ–³ª‘Küßy2JUÙÑìlïýx6™*¥Êéññ$ö€¶éxAÐÈC&q.6ãG“©Žt–¦jüËÄD±Ë­¿áFÑõ`2M£8VÎç4Õ²€ŒŽ³žù l§&­ºN&6€¹•£Ö~«Êÿaó¾®á»I2#˜Ãe¹ïIجIÒDWôËP¯s{éø3¥:ÿ1ñ²ZÎ'z~æmŠk¹6õ]lÄI¢ä¼«©Úär21Ìo1 X×¹œ÷€‡‰ÖZi†S¿õ<²&Nü!?Îþ¹§Šü'Ï<³CÏ,b±#ð÷ö N%V®{»9”h ì•”m±Æ‚8ú<ºè[Ñ;—¸Å DÍìàȰo1×l»Æ;Äç}Oi§õRƒÈþÄô¬øÛ3Aý=ͪEK\“…Ȇ¹÷J'¤}´2‘ÎUÍ=¯'S%©ÊÍxŸÁƒ£‰‹ãpœ0ÓÔZeI6š*WNñ¨ìl¬WA5ô† AÏzNÐk‚fýLÐ+‚Þ…]”gšÔ9¯œÓH©ef]äx½iuÞBNcï¼»·òŒ*‰œõ$ûjȨIÒ@Hî"¯'R€ÌÀ^©yBƱI¥~Dž‘û?õ¸D¼6U™\êF!u6å»Íþ½çÿô¦î§½Ù_ß8‰µoب`ä}$—vyµ·Î©–›à‹Ë)ƒ ç ^JÑ"ð˜ÁW ®<‡“ýçýÄàƒŸ!¸óꊬý¾ ŠÙñ‚âÅr]"‘`ÑŠ ~7¤Ôø^ÄGc¹=1*—& ˜ñp ¼±õ#¸Ú1œìl¥É¶—ÉA«õÍô©‹ƒ7“öPÿá"v ‘õˆÁ_|óµô»¢È½ Ò|†ÃÄdóH­XîÖCû]À…żçpëÛóšnòZ`ãý´Ô‘ßz[\×p§õ"ÁÃSºá{%ºVƒ‘†Â»ËT7$>ÿÎ!…“I˜'ówLÑó* O3ï&ŠFᔞR˜ß‚àýêXzñÔ*nøÍA}ë»÷Óe©ÜƒuY3&‚UH¢gA’h]Ÿi±ã9’rONiR«aÐMÐmq0’ qî «*ºG6³5oV×m+b2£GÊÓÒr,+·­XV’æÅõ0³ôdGN¹Ôkh2µQ’DZõÍi”—ÈžšÌsmPz:rY¬/šâV’“O£—e«6Zˆï§<ûJ4¸ùÃ8´çi»D,÷Œ ‡½$èääò\+$i=ñ<´(˺wží|¯)ª÷˜£zÜ(ÈŸÓpKP Eðô|Ò¸ÔÉ•¯X2. Ä5br(Ê·²Cñ¼#î{L‡ƒgϼǠ·eÎ_¶=µ/¸Í“Ñ2K( –²Súné°­/Úb4ÉeiԊ梒=φ"{‡«^}ZÏVÁ[°I”%©íÙỉJ‚Úéû^…Œ]ž_DEËbê»Ø/sճ“ pκâO™‚x…-Â7Ô÷o7‚çéàÎÅ7ÀW}ž"}ææÅƽñSuxºÙ <‹˜ž˜á2àÑëc»1éb†uÉ5ÚD:eg"ð_âÍNms½ «T‹óZ|:)y.DÒ"2“èrhÃr×*„mt#\[7^€Ž¬È„ƒPG%óŠŒ-=Wˆ/ˆLiÝÝV›/x7Å,X¸Ó9¤Q[ýuõ Zº«OÂ\§½V> Ú×FÀËÑ ß#višY%åS€ÁÕÿYïºRI"ó´ä&ë…ÅP|¸ìš'ÉŽÑU$žxœ!3¶)Xg0¯²îž1k„6E_œŽJ}‹¤@áéÚ¾ê&Æ ÇSmbßÐu0C"„˜öš +#ËvJꤱ‚N½÷–§…*©9FªÉçÂqyHŽ‹H\ Prù`¢çM¥Œýýg)´9,ñÿ)Rh^Eº!K7o£!Ì%³ã›e6éóÍ·–|žnåº~ Ð;=PþËO{3ЯÁlÍ>Ô‡«z8„­›«Þþ<ß fÑ\X1‰kØ'±rUÜA÷ C¾Kß±t›ÉÚK`ß]ð>^M R\²ìÓ?¥_IT„㳬¤þJ†jÎÝÞ iW14ïPçñ4+ÖBOÐ=½Ï}H-cÑi߬V-§`r>öœÔ‚ˆ+ôø,8UŒö#æúÂ*jû.ù>4X©Ñ¸øÛ$ ª¯Ï°a!šWzÁ_–ò:Ú¾³Ü@ðEð®Öî ´tMAÉV)Ð?]/Ðü~WÞm·:OEj±áØÅRŸä¨Ù»´ ´·ò·"¾vŠ’€g þÌà[ý;ƒ‡0¼úƒÿ ¤Š…2ø²?ñG*5Yy„2Þâ¯8yd²"ZLT@ËÈ$¸h ÝÂ÷–û“VPŒbªþï¿´pG·“¤ù ¤6g÷x懽”þMY¤rLМ/©‘¡A§ µð¶½rƒ€UºÂ`=·{°¬¡éùâY7â`ðuqÝ£ÉRŠBq}f¤yCÝÊŒlºû"e8’f"rk²ë¨•¸ â.‚Õ6±‰\jv(EêQ6Eñ±·­l|U"…(rÔK¹‡>Í¥´óº×^¯d‡£ƒWrì–~áÛ9¦\ßíÑ3Ó³²ú(·ŽªàBº¼UîUÜiNð-•¾ )jº¥Öf‘m$Éf‚õ 2Òí¬ƒ[ð¢ˆBck(>PY!sc¯im6W†Ü®‘Ú¿^WÚÍ5,ß¡]´×0‹Ä´´ƒ´‰œ·Ý§(3&ä[lè$_ל+ùºnë‚ZŒ½]&IØFXCÞ“©3©¢ƒ0®jı+z]³~x—¸ÑÙHxØjŸÝkF_È´L¸(Q¨î¸­ ‘5Æ}i0d7øŒ}9ú2Ô:÷[°ºQðLßß3(¢gá½}—‚€¾›ã®qÒfÍÄ`ºi|tJ¬Ç‡ÇŒVj%ÑÂ~5:ÒÌ££ào+M·>Äé ø½›®ÔIQѰ¼ÆOlM#jüŽ´Ú&|µXM¨µN±ÂÖr tùYGƒoS»¶¢Q'ýwû™„§$ª~Î 8XñDÕD’ÁÚ¢ Ìufl¹jì=A1A÷ J úØ8©Ê” ™£eœ´ÑÚz>Ϙ¸‚§;~½p*!x ¸‚àhhØÜ®âºN}5;<ì¾Ü-uM†º~ z Q-ЇùÓön ÐÂû‹¯#Œw7ÔËýDëà³€ïVH¾züuddH_Bô®a+ |5Ƶµ¸õ.ñ­U׊¦®öŽT×bg±#ä¿PÀòøMäÕ˜3Ñî€@Xnà¶ð)ÐЧ¬.††‰¾Û³ÎX0Ö9L2»}¬›;Æú óÝÜúšfŽ ± Ø¿)*»ýØHë!¬ïç\BüBüa3ÓlbX¨Â©añváÎv§‘x‘|ûb›„õ•؃`Ì^'p¿b‰“‚Q¬vQ¦M3 XµMK%XFGˆ^Oˆ´Edkõôöy)¿©X^“>Ãb^ak·Æj“­ç@[Ç)ÿ<¹»N–žÊ¤:ùù2‘ÖÎÏ·Ê"Ä CÊ)ÎÉ£²‰²o§}Ù–£Þ­oòìþÌAØ,FÓå÷n£Œ° 9y~‰E®k(žˆÍbT'àXüÈÕÞêø2#cüÿ•0Lë£q8«T@wS °mþ…GÜFÚÂ3Å´Ó(;®Ä_;~éVO‰”uÕ3šÞ§DfÓS¢szJ´$H¾:§§DâùÐ?zÎ ¿@º 芠µxfÔ÷ühF½*Ȳ<6-õÃtñbnÛ7GÊ”Üwv­·~H­rç‡Vès¥ûœiL JÙ„D Í‹Žf rÞŸD€âµ¬#sˆwL_f [ë¯Ûßå—‚ÚOZ¥”Tƒá¶néݺn¹(ž?#~F¬‡S>‚²2gø‹~ëd—t ½lÐ’@LËÁêÊçݰOèê Vü°^WtL·àÄÛ íŰÝÓ„ýuƒ»ä AªÈ{ƒô™ iáàGÚ%çCž4ö»ù7aî(\x[?è²?Ûû—ÿ÷?¸ï tendstream endobj 37 0 obj 3205 endobj 41 0 obj <> stream xœí[msÛ¸þ®_Áo•:5C€ @Þ·æ&mÓiëÔ§Îu¦×é(–층eERr×þú.ø‚]’MÊIfÒž’LfM¼?Ø]ì³€ßGI¬t”ø¿pý0{qå¢Ûìü]ý¶ö·³÷³B0D…kßAˆðîŠfDZéàÀwPUvpña±MìÒ2 ü:H Ò«ò6q±2n`¿theÆökѾ‡"^ôí ¢ýî×}ÁWJ×B®ŠÀ¤’_xd3Ý@ûrqQÄI¢M¦<^ªœ"UrÊd€¤òJŠ› Ýpñ*|ü$šˆ‹ÓÌeš–’dm’ÎÅbÄ¢¨ð®ì5Íò´*Õ‰“uI!áж,ª¢ÐÁýÂø¢Â´» Ó¾ å[n´kÄ¢^LÙêçÍ”C‹’UgVåw É-\À†§±†‹ÓxDÇodZH$Wp´­„Ú«ŠÖYœe:Ë52õzÐ&6tÍ¿'íJc—8q5hš¨ h©ŽÈhÇXa:‡˜Mó²‚?ÄÈô’Ø*ëȶâÅ…‰³"I¼º¸06/M$'Í5óo:¶y¢5­:¥å¥.£Ù7ÒAôS—ëÒš7¢BÄŸÉÁÓ!`µ³´ÓM³£¯[¸$±©Bó—‹¾~˜‹ïË…7.k-I¾ÑNöÂV˜:uÈDN«=8j´ügiµä•¥Ý´š6»o }µP4¥ÇP¾ Òw­§ê2Ð?l';V¯QCòðe~äqŪ–`k¼2~–ÄÌ¿P9Z(«ù‹‚ÓÐÌö\|Цcœ ,,»åª„á"c¤Î¹U§Mí)<„>Ê¢™’°Ü‡òÄJLf/W ÿ±6æ<Ö)·d9©´µ…Õø™Ñ®òGš5L¶ÈÅR¶Ð] Ì{sWÔ€×pzκƒà!(V ªŠ¯[ñd­ÝnÑ +Ø.Qá–Aâ…‰¾°¶Š¾DÝoʺ)ý«ýÉEµ÷õ¿®]F‹dP kQü» ñÐ*HAÚœYB«» ýgzO×0Úz€âf,‚:t£¢&®lB!È^†Ùþ¥G²¦p;B­­YÁi‹¯"^´@„Î7PD¡ãHl}&”gBùµJ=03D's˯SŠY ˆ]VÏ=æ6{Íâ /a]ÌJï àâ+vdwp’[8ðÖýt¾,D¡;ÐÃuŽÏìy ªàÌïCñ?`â‰tS½Å£ÑÚ+ì“ÒbhC4:TXÁÁ¶u¸mã$µMÀ½éâØaO ‘¶—±¦ÉO¡Ñ&÷ÞãIõiô›’£¦qæ•h´ LZå½ïªPiÒâÌË@„AÚ#_ŽZ& B£«@ß岃µè 7Ý!Úž<§D©Årªii•‹±jš…Ìy€fKÛølš½ Ù¦w  ¦pGäÃäärh%¹¬°’€ìOAòð7S[T'cÀóÀ6 á;«®EBö½– ¬ÐTÍR›YéKÅè² Ä®Ï2³1®ÔJ“!N5–ƒ(k—œZ¢ÚM­ãÍ…ØS¼'Ô Ü-—ªÌ4åTÃ¥g6¿ QèUœu n¼…=Ôk$-uNhW3q—Ë G[3« rרŒhµ…Sø©»eÝh¡yÛ•œßd>y›—¬ßTýrÜÁþ¤gLƒ‰Öº¦Oð²çjÐJŸÎšuü"ö4'ÄÑT6±å¥ÛÁ±Ô[6ƒ:”Ó½ð©—X÷§¬Ø¸ž)unn»”»9bÀC¡_ÀbÔC|¨½|6äøqúJlŠð…Þñ{¼kèF±»C\åØF³£ø8ü¸Èü ×>m¶ㆠ§yìvÑÍÌö šUçE+ÏWof7Ó÷3‹ZØÊ„{ꥆ™Éx{5'e†OÚö²Aõs‡y ¾OÇã‰Ù`q/WòÕnî÷çú­u%0ç”ï9åû¹S¾ß8.¿œ"¬9{Xá”ä.†æ‚ü©È"=ý6ó» ý:H:ÁC™ˆGÖ" ùbuJ¦| Å €í+ºmÀÏ¡¢g¦‚q¦çŠßí³ޡ é±èì |¾b8_1 ›ú¾‘OV6h6ò…âzm…ªñ¼ƒæ. Ÿ.ÿS”ditMxü˜÷ùå˜5¹à ½•zï O!Ž(?:J;©9™ ÙÁ‰`>ý)ŒÈçªòjG$±T!ä"^¾†`“ÄϤ·¯Ä㔵¶Më?Š’?¥Ë™üÃ)VÞ §kѲ,V¹5•u”\ˆK¨úo`…«A L'…vˆJ¸>ï%ÚçHÉ¢yõòOlŪ¥ŸM"?™Å:°…«èÝ~vž“;×<ˆóoçê[[›dâA];M«ó€xTœþø<ÄGã(Š ˜E®@³I9Îkñ6j+Pz¥×Az$Þþu{ƒ'FZ˜ocŠ5ÊîŸ2 a-"8¼/Ӄđ wTLF@}vJgVve¬„‹dEaµ\ËuõTõ”ò7N°Þ÷”±çcÎÊ•fRþ×µñ)%ì熤rñYð"H— Þ3¼z?•ðÿ÷—ôvð†S݉׊ýÐl,û»¾S.YáSˆ“%–S³J7¡ºMZ¿€Òb*èW‹&¼J¬Þ‘¤ÄŸÃÓ#|‹ßEÊW‰Í/»|¿¸0YœÛÄô9Ä«åìÏô÷¿S¿Ð„endstream endobj 42 0 obj 2366 endobj 46 0 obj <> stream xœíZmsÛ6þ®_Áo•n*†x!HäÛåš»ëÛ¤©:½™´ÓQä׫-¹¶Ükï× Eì’zh²Ó\ÇÏ\,€»‹·ç§$K…L2ÿÓ««É³7Erv;©Š“7ÿØ 7g“Ÿ&eªü¿ª€Ë««äÅÂUT2:Ít²8d©µefu¥ “»¿mžYQ)\MÞNålž¥F˜Bëi:›ë4·Y¦§fV¤V›ršÌ檴Nž¾öšVÙ"Ÿ.Y¥ë™J³L¹ÒVzãMY¡œ©„¬.j]©WzR·š[^‰µ{4×AZ1Kç¡´®_–ÖN¿›VŠ,397F© uVX3ýnæú[¥…³ñýâ_Q!€!@ ÙÔX‘šÒê*-þ3"Íe²8š,þòÖµ3&-„‘®™¤.ÜH)q’ÊÉ”¢)\éTqàæ®ëBZ^{ëÚNCÛ½êJfMýJLH•)Ü‘¸&Ñá[¸Žû */oÑ#ÛtäµGÓX­ùx«Â"³ÂÃÊúZÿï̦E©2ÉÇt1Ó¾qçr[j‹õþÖ÷ˆ9¦OKYÝe·÷Êä†+l 4K^tÉXÝQ³ï’ë½Tvú£ûnݨ•¨©–¥˜ž’Õ‹Ê«¤4©Èó¢Å±‹ÂË`‰3¥FY¯XW¤UãYûóti*^Íæ&5:/ìôëPÈ4ÙLœÐXÏÈ*6ž*쩬öÏaHµ0p1«­¨bFÁìÆÂÕl.]B-r1}-àA2Ÿ:íÎó¿J¼oö*…õÎitZVé¾r¦úKw"ªZÉL.¡ÚÂWžëÜe¯Ò&ó]vò&ŽAÆie©ø0<½f—*z¦aƒRF¶/S7ØB–¸+,2Sln[q…ÄVÆP»„_«ô)¬úŽô¹n -ùçÆ±¥æs¾äÝì8?ÎÝw#$ÿÎpŠÄ9î5ƒì<¤'£ ài¬©;ê+‹½Z׈\÷ÆŠt—p~°[0Ý̶8«àj8G²Å]gÆØ2ðó,÷K_ÕX(\h·/G¡5·µ^GZcÆjŸWîÿn[3o¦˜ÒAØ»èÿÏ­‹4™x¬­ EËWÌàeøÎ¦Œ©âÌÂ&r·I¾U°´Vä _‘Qd¶˜îóº1•5»Þ°ƒuâöÀ2{`‘¥eã þtIç1UfRi^(í£`']éd'I¿*ÍMæ3cÁ«3ñ6ÔºÒÿ†[ZÕ Âú€âOH\“¸%ñ¶%£.xzûMþÞê·ÆBö `B­Ä»œ[GÊÖ©ƒKØmVšøŽÄ;O¡È¹ébÐí¿÷ƨ§oFŒVDF»]i q æ+:˜27P—5ÁÿZ`­]B g}žÔAó( ñÙ{Aó:ƒÅÏ9ê尢Ȅ º¿…ÿ3H Ò—#Ž@¸„`17½ ñgˆEƒ{÷»!Ïz†õ*H_ð,jÉv­Rè´>×'c9ø"„qÛÀQ'P+l!°xXkwÐŽš-4v ;‰¢&æÒ‹€øçAú"H/G¸´ µt«˜;<xÑÄ£¾À^ÁjŸ“øŒÄW#¦<êJx‰ãk[¨íÙbvEæU×ÀBÏÒÛ„/w n ø€#‘^Û‘€KÄÖ”ÛT×µ(îcœ#Šð”6°¶.y…ô7ñlGw¹/Ã-'ÛG3q9蜃cRsä·ì6æÒoÍU^ä²uög{ÿû5ÝÓDzsÎ`ŸØåÔ†ŸBè(´¡Vé:¦²÷|g$‡Ûä!LJ“àb7@Úì•Õ¾J^ù¨Òû+Ÿ¯c~Êö̽o¡ÈØþ‹ÈTuª¡MÇ ·_·àë*HçA"78nOôÀL„7òx;ÂtñB~PJéÜe8æÕ™³ŽÈÑ q¡MW&ÆÐZocÜȺÂA!ת†Èíè èP@o q’BÝž\DÑÝ÷¬¥+Ìíþ ¦*ÍtÀ»ÈýÇf*4 g*°GØ<,Q¾_¦ÂPëRèã'4ËÛO4Ó 0JV øyÑPr-¹*x»“~ßÒ¢E(W1/[´ˆal…nþêr\Ž¥‰½7ºúB ‘Æ1ÐkèýT©Üjj‹>ªBëñtïmÛ¯Œ­7ÈN~î>RvI UÎí†xUØêo¨´ç…~ÓÒÇL@± „>fBí¥6Õ™¤´{7CÛÅ ¾ÉàŸ„0|ë¼çúäÚÝO ¢,ÔsÐ¥,ü±6$`ÏþÞ8¹§ýäùn,nWÄ“9#"!ËÞ÷~¢Re%ŠÆ}xuhž¨‡Sôômè.èyèrM²ïc£/#×"˜µÐó*J€Ñ½.Ýú¾‰ zÄø:*ø6é w¨Ø+ä¿ÃÂSè ør»ÇEñá÷„Faybb çªDIçƒfé:v)üÈ~6hüŒ3ÂýHO£¦çu âh?bGHÀ<†mòG§›s"è=xö1“n ÑÄt¦1/rÕäöeh—G³Kφ_iŸPÇ;˜Æäª[Ø{¼Œá>ƒa]üš‡YGÑõMèã°ÇKÿ¨ŒC´Þʇ”äF=<ðø2‡^Œ÷pø„?ºÊ§J”;æ!E'ã¡@@Dԫ߉1éÇõx$ž(O”Î'J' åÇ”>:JçËÅä+÷ó¦ãà€endstream endobj 47 0 obj 2298 endobj 51 0 obj <> stream xœí\[sÛÆ~ׯà[ÉN `¤Î$Ž&IÇ—Ôf&ž&}`t³I¤-Ù¹üúîØ=ß.>¤$O_’d2GÀÞ÷\¿sÀw³4ÉÔ,µÿ:âäúèÉ«jvq{Ô>ž½úº'Þ_½;ª“ÜþÓ>@úäzöåÊtTå¬I=[¥IÓÔiÕšÍtiþnÊY•eIQÏV×GóÙbõß#ó§2>;ZýõGód™§Ê4(æ7†LÊ*/æOÝyjæ©5y{GÞžxê­§Î"îd›ž€)­›&]Äm N‚ø ‡Ìvº´e±ªºÞ m5Ch«Ç…ª2€¶ÞÛ>M–‡h`X7NZ{ Ð*@£ž/”ÕuJ™;q}® Õ[g¯LÀ2~Kš Vö¬´_u©«žùnÔñSt‹ÑY5ÿÞ#coÚ÷EZ5ú~€X^­©»&6PEöý{³ id52BΣ–± ìôþ‹A"Q "üÃßbGR€ß™Ý–ª{ b9ñcˆLƒªŒÍ š‹ŠSTm…SžÌè¢Ý0C÷–nt@kð”˜À€uxXfø­rº,°š#aNaÙ͸†ƒ€7Ö{gt Uþ@~+$7@_÷5ÑEYS@ÛKge¹ñpÜWfx`üŠžœA—„Y@Ϊ÷<\Îk±ójCv…v xlMﺷÚ¨þ¥C,;ÆYöµåžÞu¨ª D‡zÎb‰o‘X"Ñ)ðúµÕú1zö+ƒ(x8Cšæ‘ïÚ“‚³اAAÛ?d‡ÀùCÄ'rèø¼\‰6&Ý®i’2UN× lÑÐß‹÷hÖ=°vÖA¤en™š "LsÑ”åHÖËèMÿ¹£%è|P@|¶‹žgŒã˜{oÔzx—&¦ª›VÙØ52W,gú‡ï€†æðH‘5n˜ã¤θ)á5']BœQ~—žÝº¶m¢ŸÛ´5íE#„­U‰ÐiÛh;ºäž¡…‹ÏcÕIgL3®ádÏ@iA?÷Pkªª»0Ò@ªLtŸ´c!øöŠ’Ðà)fAušÚå`‚\[÷¸$k8€­£Ý›¼<ðyÕ?_Ð=x—cn×þÖ3yïcufQ9Këåì.g1.Ê9Ÿ~Þ[è¹+©+kôVR‡•ˆ|AŠ „’·Rp %Rz ©¤ï=õ†´û»§¶ž–§bÉõ?²ðaU <% ·ù(^OÊsv°6ÓÆÒßY€ªJ›lþDšÚ)ï¹@ñ”R5é´„ì%< šº¨q šíR¢E¢\`Õã[¿§Ø\r Çw“p‚uQJ°+L:Çë#Š «`ã¼ø²oéíÓÜÄ\7(Éb|ì3ÀLUV6qãë±ZÌð€°&7ðŽF²¸‡8(q ÙòÉ/@ZaÆaòe  ¤Î=n ¯{„ÏI] tPŒ…Õ€!* äŒE2ÜÁäJ›Ã> H•Þ¡YÒF—ňö)ûõÛü^"XÍ„r›á'Bm~ £ø¿KŒMj!ž"œXŒ nz`8O$*j¸G±ºV‰.;ذÒݬsP £BÈ}ÍZŠŠ ð#Àb@FUñAÁ‚ŠBy¿©¾ˆ“3ÌA:TÆÃq›çäJgÒÀÅEDqPŸP}9ÈìEÞ¯™ÀšÝ÷„k©ÏzÕ”'ª"Á/¬”Á@8ZW‰Š½W’báê*ÐûL ï¥÷‡ÚèQ±¶+¢þ/¤kÊùáᘉ ª“¬ö˜Ék ¦Ž)fáØs!¿òÏhl7£ ^ ùõ(c×Z¦UèXUMb¼4Ó'YŸôU³é’ƒyãÑ¥¸Òü‘VùœE c]š–ÖVÄq­P†ʧÙ…wùæV1qƒÏÑEZÂÈ¥ñáeÙû~ѱ.yæ©Ò÷ÜSR°}å©3ÒwºDœ€)€"ÁÑ‹Éo®·S ù(›#3 †g´—ÓÉïIÎél¬ò:*ض ”7Foi«²ZxŠç,]Gý¹ûyƒûT(KùƒdÐ{Eµ`T®‹p¥]s¤d"?ÿ‚*4æp}ÇäÞÀ%é?GÖeR6>ÚÚÐìœûÕLç0Eà-N„i~‡iÄ¥ðÝÇjuH°à­ô<©û~*7ÿy̽?ãèÃL+šŽÅM?1¾`»©”Ù³È-ÈÞ.˜6Â\·;ºÓa×Åm¦dùÀÉ~ôµÿT(Ç%ÇÐ;Ÿc™B+¡W #Dã¸dÀ…®œÒüM×q0Æb!÷,:€–C!šƒIÐÓ÷§çÇë£Lô¾ŸL‡”¬ ñ"O!Ò#âÙð±à‰›½ˆÛ)<¨© ’ŸìîOú¸4³ÇD{³t„Û8wX¼`#Ý"¨£’¦²<‡”çÞíP²ãE Éð‚Çý>ðËZÜ+!L5¥/¦ Õ™´Š´~½ø9*üÞ‡=¬ØÕ1”ƒCe.¡véfmÁޏ~:‰Þ÷µìl=pÙžÒJá&vÌüûd^’AqPJñßåÀõŸ-uÆ­LÌ2å[ȉÓQî³&ÿ+ªÍª,ÐL‚V„ÞΰêS4V}sFÐ…~Ä™³ø»¼Ð„o<–K^ª<)TSQ,øÛñj¡Äegñãòȱ’ÉróÏvhúÄ}í³˜ÇžÚ‡’’ãŽÙ ¯èšh(Wò_h‹ºióZcª ¢Ðòô… K„^|µ&£pŸ)ž–št‘xqÅW²rzâ_°=~#$ÜÝðFŸ¸"ð°X»»åGÃ-fÑo>AØÀàÂðbÌ-é[º„ÀÄ)‡Üæʇ‡ß¶í—–UØ ìàQþ+C˜Ô•.;¼`ø ŽL~Òð‡Ô–=%ã½ Ô1éÁ~2ü‘ÆÑ_˜€R±·dü•âðë•òß•˜üA >î†vãê ÷rj0öKò㟎ddVÃÈn™õ·OI»èæ‡?{qϛ絅kút²$‘ÿÞÆ-øœ.ròÇ7'Y~ƒCòO¶xyY$ºF/rÀ[êSpçñêè_æßÿ3k-eendstream endobj 52 0 obj 3907 endobj 56 0 obj <> stream xœí[msÛ6þ®_Áo'ÝÄ4’ ‘ûä6/—^zéɾéͤÅVl'±ìZrÒÞ¯?€/Ø%ùä¸IgÚt&z±v àyà_"K ÷§N¯&‡ó<:_OÊÏÑüy-ÜžO~™qâþ+?pùô*úæÄ6TYdb££“·Sˆ¼²*#)T”K§ÑÉÕäõôxvÄYž¤Ó§^:ñRä¥ï½ôo/ýÇK/½´mŸxé…—¨ßo½ôÊKÿÒSТ©Ò®;YÈ4V¦ìY(§éô¬R&™^’¸&ñ”ÄkWP\Âfh7‚ ÐX-Ü„:fz ¿²fg°·°ÙjöóÉw“$Kc]¤6gNÎlž,gZˆXé~v8—Òç—Ž…”™i㙈“LkázrʉÑq®³èÀf¶U±ŸOÞM¤Œ3ûÏ—““¿¾va”VGje=$¬ ÒÜöØH™´ÓÏÝ(´¶áj~¾òÒ†4og™Ó´ù@šf©•“º©çV5ÏTÝQ¢DαNšÒçïíg¡ÊòÜ­«¢H3·ŒÕPBMšA#—¾ë+Û,Ežä6ƾkš ›õN’‰WU­Q®Hd¶©ÓrûÑ5`fÊìßtS8'±šs麊ˆJ ïû”é>®t!ë4±ý¥]¸l‘*š aKš*ÌÂKW^Z¶*‚y,Ë òÍ™¸ö­.½ô¿í-áÒqÅ%_^£¥CÕþ°) Êé—Žéø€ª'Õê#/½ö’ðÒc/%^’^ú¹]G•u}®x‡š—u•Ê<.ràÊΤðë,wvªPȱ¸úŠPM¾† ‰„&‰9÷>úl–Ï>[x@*ð܆ö’%ôóÇÐvõFõ-™6œ?VØ*Ht úêâ¾>_@—2?^A÷o Ç¾P ¨¯iåSŒé Låø¹—¨lçe€òR>ͨ N…]–ó‹³À2è|‡j~ÿˆà‘±f·PwÏÉ 0¡P5ØSL÷sMþ¥\#kZ K8‹Ó®6tÉ£•Ô\·9ÜÑz žXØ>ˆK#öö'3vuñ^ŒO…{.ÄÚeÃaþÍøÜ•âE•"÷âª_/æÌkØÅ{h _¨·R¨ŒÐ¹þ/·Ü Né;œ Ø/àHñyãA'ß@'€"ÞÌ1‚{[CÝ ÒãCÚĦsH广>¶'|A—|‚V Éš`ŽAÂ&€»K«â%ÝÀ?"á?2ÕåÌŽ /<“êâwÁ#<:ÒÃ#ÜX?Ѽ?q ¼4Ë P½ñ%™Ü¸щ9m—Ë>8ü;ÜŽ6PàtîTÆû–ÅÞBu?¿Ùµ™ YðšƒÊæÀ»Ì8k}‡ŠÛ†WD ‡êà¯vrÆ!³i /àÇg¤pMÕké‹f·R®¸>‚z£N•½#«lÜ7Ð â°Åº(.ÈîÇYæpï²bû¬Õ‚ÃÓ^ͼJ•ˆ8%V×õ^ 7Y60‘Ά‰öaê`VÉR»©kî½N¤ºÐ{Ÿ•p¾†~`͘±S¸U]6㦘s°û±wz‡çÖÞ6£²_›¥fú©:ehS´·o4Ǫ³Dgº6VŠУÑLåylL^rEM~Ô•Ä% ; Á#ËXÿT6ÝèVµ­SÖ,V3F7x‰¢“Nœ¡Òá½Þ?æ¹°c<ÏŒn §³h'å@¨*3Âî§q÷´t8OTd7X‘ålÒšrÖ™ý·É¢4‘qQT¼³Ý¦E¬¥Îí>ÏÒ83B”½ÿ,ì`\}Œf6$±Îsw²¶“gÎ_ild"\N¬7ûñYZ2K§LÞX«™ITyhh”ŸÛùÆJ«\·Ú­f. u™xÐ03ñÄéª4Ñ|dwÕÈT¹ YSE–kÞÈèí„r!tf-¨X.Ôs'å…Llü•s´,ñåÀS‰öàÉï=n7`·Œ®jÖu·mù7zm*þ O¦¾ëR¨î##hñöb]mc™f¹¨«—™ÅÙÌÆÅÖ)« ·§'´0ç´¬8·»ûIJUlqgÚ‚Åí—÷­îNu³Ý]¿Îý¶³®6êÔæh&›ú^W©ƒÆUõ*»×*ýç½jü^E/iŽZ£—­™P+k±+FÙ]ݙΠˆAÏõþèx  ïæj…£ŒÇÿà€Ið£ŸÌ·{¹ x/þr?±çXî •0ÀèIèÿ¼=îQø³Ï mpœçì+uñ%?HžaJ|E"¬ÄÅPÅo[.;zÛðcÈŸ;`úxMà¥tHâ§&x_V3´ Ëš÷¤Ý»ƒÝL fÆ^sçzú޶™ª LuϽg—§GK0UÖg`®cPfÍÅûéN?X€ƒäLëú]u=Ì2è*°ŽqðDû¶&„ÐHÂI6 z„ó%†‘Ù C¶çF»'n|ˆ³n½GÑ wÞ´ÍÿDôÚQØ=Æ»ê t& ™Â Ùõ êîUÊwBÜŸ¢ ;¼»cŒðÂÏèÑÔ÷nº+zï^+·Â§í‹,ÿÑ[ÂÂXÆ@7îðfâ¼°Á¯ì=®d7§Û’zKŸW·@ºî}ë••Ï)=ÄZ÷Üà.ߢ«ÌO& ~=Pa pl€÷à§ðÓS/]x‰Òà¬è{tñ«¦‹9Åû ñVã³}å¼÷Ë8ðâžÄ6öí.7’ÞÝi«]Cy.û襫­¼<•¹‡ßwà[V:/Ùï½€r¤±IÞôúÊ XÖ71tØc*Ìì+¸¯À')ì#&Y~›IWùÔôo¦òVxx»Âºº8©²#6û•« ¥Šˆ$6„±d ¾Œðÿ®UÆÿ®ºAêê¾"\ÿØ­ìG/H<„ ˜.öpï 3?c^¶ÅO#‘0qÁ:fv™xG¤¥¯ÇÂ\Ÿp´Èêxe6Uß×J”¿ÊS‘7ÓæRÓÔúWË—øý¹šå/¤'v þí:FÒ ; ßš?ÉðxìÏ©Œˆ9†¤¦!§¦ä1@ ¡…úÑ76ÞÓÎ3³²ø=)#´ ›ÓYVªöØ2Ç£â[°eŒ¶_Âm²Lº'5ª°©Z=ÆÐ±Ê²&{š’™ÉÚ€{vÑ"¶ÌÓžOXî ±¡Š4ðþ¢Y,NøI ~ŠáƒTË5±ÿOw`¶¥Ô¥Â–̶ì3ÛsOþ.˜âg¶}æIeÆ$ùö§Ä7ç­×rdM{¸5mfú™ï‘ ã×j™i,gÚ<¸¿—|¶‘Ek|¢U³TÏí ú¢0†qã—ô‘ü}•/UZÓ y²¾2¬Ùtlóçv)*Ör?‚U :ÁÝx‰>g¬µ¯8Ô„UuXÝFÞƒt.åÍ£ZOè8u>§Š}Ô-ãn{°ÇÆ_qÞñ`ïW¡›+“ è:G—š3geç iÛåÌŸžLþeÿüq£lÙendstream endobj 57 0 obj 2639 endobj 61 0 obj <> stream xœí[ÛrÛ6}×Wð­R§¦IðÒ}Jj·u“8©£N;“d:²$_[vl%nòõ]vI ”lkÒ‰›Nf à`÷,@>x*/0fÂð¢³}¨½“›NQìþR ×'ÌÌE”‡ÞÓ>5T‰—ûyêõ;ŸçY K­¡—&ô;O<†~œyý‹Î›®×ÛŠE¿ãî„D?ÑQÜXéÂJãJR©i“Úc-› ñƶ:³Ò—öš†e…0º—,^@qÌâ„Å)‹73QQïú¿uhòŠ&ÿ¼C¿¿3¿bó£ÿ} ‹];Ú'µÑ†Yû*_0ïжJ¬„x€g`¬Q÷=‹‹GP¼†ÈÜ´£ÀëµHå¼Uj?ÓÀ2ã•»„ÓhܺêŽ6"²¢§WkYÑ03•†…¶µ£ŠUÀ9Ë$ö±/éžõÍÊ&£ ´1&R%@AÌWXyܸŠãØÏâò ‹rèÀOÃTSõ{[±ŸäAPlTl±ê‘¥¼-Ÿ+¿è©bzÆ‹#ã¹:1¹²mrÆš&ee¯f’ÇŸ%Mi(eªm?ò5ÛµÍDW7¢ÕûrÐI^µÊ²<ï¾íš¹‚4*^¥Ä>I÷™)ÔYußöHC’G1C+Â÷‚ËÁ˜˜;J ²Ô¨ÿOíði—èv¦Z‡%¹yسÞVnX'Pó Uw¸i“úeJ“WöÓp(W,LSؘ ÌVØîO]u…Éq]R5„”“ Å„+>U’a¦M¸–ÄuÑ¿àŒó2•.¨*A°%æORj ìn«pOîXÜC7‰÷lÙÅ °¦²4¥8!×ã~n8^akR;30‰tçë ›‡‚ƒòlµ¢Eû¼’éÿÊK·fhr,+]±Mω3ßôeÖQI|dÌgê» ÞsVóÉü3Ð[ýZiùU'Ξqâr÷ChqàˆSü÷PƒMˆÚ_™ßö‚û@ó@|õ÷´p®Ûf *‹@ºÀÛñ–ÛJ3­,‰åÀ¯šût£„wŒµÔå*‚ ››Õ¹8ÔÜÞ42u±AÄ鱨ßrŒùÖ´Nš¼/êî47ž+Ÿ˜~;¦ÜG“Ì-X‰ßk¼±Ÿ€íX‰ EVzWg£ù[m×åÞgú58”-8Eœ5$ɾ~ݿܬrFwÀž-VÅqì`ª¯ sXW r:¨@\ïÍ"ß@‡®V_Õ£Ed¼9bs˜¿Iºç{ù5QÁޝ @ oUÐíîãk»{m÷U>ÎRw›âG{|›åò#ÎÝVy,ÄP|³o…œkä|Léœæx¥mÄBï6g&8£â*¡ë®ÖÕþtåñ¥Äw½—VϬîô̪õaÁãó*`œ›|^åþG_Ï«ÚðÈŸW- NñÿþΪՑ›îHN|ΘÁW.ã£Çó9s]´ïø×4òÉš(òY|Hé„›½Ç°ÂÝ3xü/€¾¸<ÍŽ¬M°óîk ú?o¦æzk†¡–ËýM±Ó^¿ó;ýù“3ƒßendstream endobj 62 0 obj 2112 endobj 66 0 obj <> stream xœÝ[[sÛÆ~ç¯À[N  `ëNêZN•Z¶#3ÓÎ8ž E]‘”%1÷×g—=giÙu\;ã9YìåÛsÇÁû ‰S$öoK,–“ƒ\ÞOªáàäû†¸»œ¼Ÿ”qfÿTœ^,ƒ§3ó`š:Ö2˜]L’Xë2Qõ¬i óÿºTšÄÊ0,'aÍþ3IÓ¸ÁìÅdöç·áÓhªã$‰?F©ŠÓ<Í ÛÔ0©4×áYdNE®ÂsG]ÐÏs7¸qÔM4UqV¨B„f0+¤L²ð/DD2†«jÖ¬(³f©L$Šó.£©ÈãDe*\ÃoQaÆ’¢¯£Üê<\ÑT—pª+='Þ9‘õ±sYò§Îè)ZëÞœ6üÕ‘Z™¹;iîIz×´vªzë¨$¶­éF-ã}-Dr,êLb­¼qk°¡ŸÎ \†^¦M§Ä@[b[¸æ¶©¶bYÞZ>±—61s4R«2÷ 6b ƒ•Óòz«k¸'meöøC¤íJ†º‹¦…½¡½ç§·~¥âi ‘¶*~ÎUœY„eƒ`;©4Š?`„˜ef:€Ý`Ub¼¸çoëÚ—´«ö²f³‰–þfkË$³$mp™¶ÀØ5r N­L'¢p h¶l}¦ÙM–Û‡o ÊÈС£fŽ u ~]¾Žz娿;êŸ`µg %d…RiLl x»EFÞ×¼©®ÌTK>¹!rEäõØc+8úÈ»±ÉÎá 7D®‰\ù+œá¬%EsÙõñ{ü—£Nuž=ÔKGýÍQO„ì!ßðÅŸÃ%æDžB!Ù&žgM«PØüò'?0æn”¢ÜKG­œo¤À:Ñt˜(†¾ =ï<]Ç}tƒ?nÝWкžr_‹ªiÙOlÅ!ç‰2‡£÷ä=oáF'w\p¿é¦fÎèÂ%Ã2,áè1‘O!ƒÍÒZ:ßU•'¥º²µ£HÞÀc.ÇœXf3ÅŽC›¶û±ò-JÚTO„ÿá(Š/ÛX¥ª/,Í{†©Wð¢YTñ ñõظîÔΊýd¬£‘¥R–ÀF ý …….¤PàÓóçuõÏ+záÜy(Ìð£æ½0C¨²†€o]˜T//Z¦Óªô@8-“¤rn LÒœt~4çEû4rŸ›ñv–gnQÊÞà·¢ð‚ÈC"«=Û•ûû.ãD Õ®À‘¬ft5vM,`žúÆ^иsÜöЕ±&î ”Ùdk¸6/«C0^îP`x>pϨd³›e$š¡úÏh]ŒÅNK#Nì˜8½‚älPœì}ç©Æ|iã'(B ä ½M&‹Ö5¤P*ÖZõÎ6ÍEb·Ø9“Þ =GGÔîñ´¾FõÀL2³¬P¸áòÕþ>‡RÉ®´›Êí ¾!í%›[П{rÙ*Æpø> _ÜøK#P‹ØT0•:ˆµÍùžTlÁñ Ô^­pÛ®FÓªMóEÄ)E†l1˜!±¬‹±‚¥JŒ#¯EéÞ½«JUñÝ.+Åéó^昷IE‰ËÄ&Íj{$nžºvÔwŸ‰Æ,z_B×ùXí²VÊ%|Qa—üÀOŽzŽJõÄQ©£20FO(ðë;‘CZª°ä~§d»eÇeåÒ9„†eIF6K*X¹tg™¬‡þ à”’C=ñ`Ù.|fé=’ÇÇÊCˆ%yLjq¶†“uÖvá¤4äeµ?2V¸âκ„X]€ 4ý(­/CÛ‹`„ V8VV™ÃÎá±ÿzzAžd[ ;ÐQe†Š>”P¾ô@Ü.\ùˆ¸Â0bºó„^®Æ ]ÁÉnºÀ hßQ ž{ï£}Â=•{Oí[Â3a-Á…' 6O÷p^&p<¾º5XbL©¨BÉ×s£Gé/6:ìßîÿ2\Àcc÷(MÈïž¾þŒ`}¢ ›‚Q‡yŒ÷ âv Áºý|C‰$ §nŠ£¸wõ¹n¦|¬9áz쨟è’>£¿˜`¨:rèAD<Î.~’lzîdÀ‘/~!ö2`³ÇÂ!¶·£qÁ— =PzµÝH} 1ÙÇf|‘³/”õÇ#›³=á¤DòÝ”‘Š1VœÏÜC v÷ü¶£{Ä|áŽÞÃÉð;ØÑ·¿jGYîG{üÖè&’N|¾¤ƒ ØÃQà£aùÆ·ÂŽˆ< òäźpW륷;5TÚ¾©ÍyYá‹‘Ô•Œë«TÓDï"é;¯Ût%_Â7’»÷ê–‡Ùnz­ÔeÖtÀšË¯ç{ÒLR´hî\ç;wru¨uolS|»u¢…£hi&¼¸ãsð.ì 7i_Þ©âNéóPsp¦+pº3:¿LçÍs ž¦Ý0eŠ„p=ˆ ÞYð×1'xÞ9ƒFâ5‘¿@ÛÂxÙ iìv7u]ÓÝètÏ»@³«ã×@"s~¥1ªvßô®uH‹>yÃÞm¤°D(¡M‘_;.è°§ÉùjýߪÅN.÷Ûü¸o¿N’}ßKvºLíkC¼£^D€^n½×ßtçó½DȰöûzu„{ëÖ¯¥µ‘ ×ÂÌß§»cÙ>@ÛK[ê0®ß«2lß?7Ï÷huÞGjºóZšq™×Ÿõ™¬Å‚¢U|\ؾ´´ºf¿‡+¥²bšÅ2­_ð*c3tR½±±©¨¢¬2MuYZA´¥0••¶×‚è…·„~f‡Ó²¨»6WWš•ÍŸJhî22),¾ƒÒ’í-ð~o—[Ñg5™T_'V¼à£zaúɆYèº?È/e¥gÍèCdδFú>ð5`*ÿSÀÑŽ'\o\pþŒziI‰vçŽJ†ZOÚnþÇ6ÃŒ6_ìÖC`?Wõ{ZVܲ¥‰€îcd”C1çž:_wwAM˜½¶·Žeîô°.ÖHF“Ϋfø\hÅšá½L¦2yK)ý•-_ô¡/²ؤ9Úehõ1·åCìwâ1ãÍχpïÜ–¦Àú+A¿)S€uç‹È-ß8œŽÜÊ–¶¹úƒÜd‰»·XÉüµ£ð·Ü^kÝ´•2õoy_[“½ªÂÿ–õ #›ÃfêuºTÌálò£ùû;ÁâB endstream endobj 67 0 obj 2871 endobj 71 0 obj <> stream xœíZßoÔF~¿¿Âê 9DŒw½¶Ï} B**h8©BЇÜåg›ÜÑ$§‚Pÿ÷ÎÚçÏöç»V‚J%MìÙÙÙoƳßÎòG”ÄÆF‰ÿi„ùÕèñaÝŒªÇÑáóµp}6úc4‰Sÿ§z€òü*z2•6‹Ê¸Ì£éé(‰Ër’µUå™ü^fQaLì&Ñôjôn'黎•ßÝήˆqV¤nç(HòÚLŒ‹m‰š Õ‹ ÝéC.»ÆM™î|R1Rn¨ÂžŠoTÜWñ€Š`a®â9øš>…a·*žaVfûuúãH¶‚ôËÑôáÖóoëS—*®(R0ìÃ6ü~§øScä§ô)èæ*ξNèIÈ·1 ®¾j\¨¸ ùpK—¹Ú†Ø]Ò)À‡+ªpB¸± êN/ R ®„EÞ<ð1Êl¤ƒq§Yž'~†$.Œu…`ÛH« Éje`aœW£šGAà²8IŒV|*¼˜çâ´“÷iéüJÄRš•;gÕ˜Ô&E• kóªé‡ãlsÿûR-T¼U-˜t/½+iVdVÄf‚“µ+ÿA‚/ÁÖL>M—fè+L{®ª'ÔÖ)[b,4Ø]ø¹Z­ÁNlŽãôý¼ÆÝ¦-Á-V'‹5Iì’\vÏé±l–m›ÆÅ$ëN²u°«ñ[»¨á.1Fõë2Ë0.-Ow™ßÙ²€ù;€¬¶äË1V§•h]œ™A\Àîv>‹/‚´ØX œ)e.qæÏq)¨¹âúËA·Ís3ˆ7º®ï«a©ü]‡h·‰Ñîú >ößóô·6¶ ?Àº»––Aºî½•Ô`IÖI<&ß ïßi?Hjò ;Ï7AQ¾û'ØÌ·`sJL®6wÿ3ŠVÐ:±ÒT¼ì&Ò9IÙYN‚tK¨EH‰®Ì—'¾è´"©ª ÂGÔ¤Æô?À¶·Ew-- wBÖ<(ù4–=U„‘E:¢ëZQ f¤^Q¸?KrÃÙ‡’§9UU° šJŽ"¦¨¯a3^Q,eÕÞ`ºNõ~Gýoöдhoز”"1“µ—OÐÀÖ­ˆX×êŸ÷c:¬ËŽz»ºgC°©Q?Z»~ÀçÍ8õK*¯‘r$‘½ÞÁÃ5C²R̤aH¥²§bÂØÎÝßA€·Ì¼6dbˆÃ12¢Ì‰eðë£:S âTfÈe`nH 8<1¢·t¹ J©ààpŽKcG$r,¾]AÑØ6™®g(&k³™³í ,·ælë¬QÎö«EôüuÄö¾æˆyš4IZ•®Ló8ÉËL¨ˆázþ^eÕj{Ó“ê%¸V×Rþaãî Ó@ýæÒÚþYJùGJ6`"uI7ƒ­³Á®½¤S\Q» µ{;0[‹VQóûŒn·ËàÝg_rªÍ”Avæ^ô¦â3]2ô$71Ž×a¢WAzMð»S‚kâùÙáó› !)LõÁ÷¸+uØìgQ‰{¦â_­¼¬Ê@¼Â+á)§ò9õÁ¨˜‘™ôôö¸¯?㨅HŒ ¸µÂø¹‹ù=@«S4ÈKÀï'ŠÀc7£ÊÒ™1äVã³·dÕ;%Ϙž~›óÞÛv2eØ_ª.œ@÷(ìnsV¶2ØŽû Çû{ŠpFžR¿Ò%¤Û?,¶ÓðÎ4TP€›?aòc*öÎFH–{¬ºj²Cø‚ŒÝº§°*¼¹œÃja¿o-ZOàÆ Ò™ç0×}BKz±9˜16ÓrJÝÆ-XiŸÆOÏòZ•šúH¡¦Ä¢7œ”}{×jC¶€>à¶Òbr×Üf_ƒ¦é‘¶Sµ>˜›|¸ ?ƒ´´ö)D¿lû2ú‰ôå`iNh‰é7ÖÆ¿‡ ` M)2o»Ój$íê± õ­žÒžï/ä¥süj N°ÚrÒŽôffᡞùÜ=àpÀ÷º0áÀ«®oí 쯻,eÙ¾Ô ¶¡³´À™ ¬Y3¢é‡Xm´3zd?wº$ižå -h ðk)~ëM§u·é5•ñd’6½¦Êõ%mxXƒ˜›Ìá´3 ]¯ÿýÉÎjä´%Ðgr_\&;Vâ«5 éÛ±ÉDÕVÕœta$äV*j^N <PÆž…~7ðƒ¸çÎ' wá¹|þ¢倅êÂ@rksB MWhêñ6Y‰¤Ìl?Uús¯óÿÅI ZÝb´ybü-Tó´XçMÛ¢I¿ˆæ}¬Æ"mQˆFQõª\–Æ’C­^4û´CuÆzQÑØØØÙ‰ÁÚ´bšüŽ»[ƺU¨½î¹BuyÚÔ7²Rá›+i_f[×öìã´Ô°eêŽV˜ªP¶ ¹ü‡lÙö_!ø|€tpÙÝ;Ø»MÃf÷®ÚPϦ£Ÿåçoìsendstream endobj 72 0 obj 1857 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 28 0 R 35 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R ] /Count 12 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 75 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœY Xçº ™¤âJ ‹è Z,àR7¬â .à₊¢‚¢ ";’°ý@ !a‚U¬"âRBq¥î¥UªUqéAm‹ÇZ§Õö´ßôþ9÷¹ÿ$@Ò>§÷ÞÇÇ$3óÏ¿|ßû¾ß‚ eÛ‡²±±Ï‹Iˆ‹ ãç\l¸a}¸á‚ý8ã~/Nùxíî_ÐÏõ ~¶‡ô°çbÃí<ˆØØ$V΋‰Mދܾc¯«GðŠ5žcÆŒµÜ™èíííº%¹ç‰ëüðøÈí»]ß#?ö…GÅÄF‡ïÞ;Ãu¹Õu{TrìŽx×°mÛ·ñ¯­‹ ßåê³ÏÕcž§ë¤ &Ž#“#£·$Ä»ÄìŽq]êº"|{BTXÜnRµ|©oòî­kæ¦Äl[8/6|ý²ù{"‚Äm_î¿c…ÿÞÈ• v®Z´oWpbTØê%IÑ[ÖxxŽv;näûã'Lœ”>{²×”¦nœæ½iúŒÔ™î³(j5’ZO-£æSÞÔûÔ»TD- ¦Sã)7j9åGÍ &P£¨”?5‘zZI-¤&QîÔ*j5™ò ‚©Å”åI­¦–PS¨ÑÔj)õµ–  æRS©±Ô:*šGM£Þ¡úRv” ÕŸ@ ¨Ô j0eOI¨PŠ¥†PRÊšI9RNÔlÊ™JùP.Ô0j5œÚA1Ô âYÊ–læ–Íl›k}¦÷ùN°QðЖµÝdÛ.\*uötŽvþz蜡….#\’]º†1à ûføàáJ¦/ã¬a´ f²;Ø#\×® ¸ƒ Lo‚ºFlãôÒ‡tµF]^!W'³8˜NÎR¤g”*ªÙGÆ„9üUF:ÅùÃFºJ©ÍHWÈRì…ëD¦ÉÀÝç 6Ø3 BÀù@ôvZ½ h½F]Q.W'±4^Çé…x3,'ëhøÇýhìoLFÑÉ2ò†–¿5†®.%od‘-ͤ/ƒ»Š R¼Ÿ Î +i²2l⪠6-0 ¼ašN:ÌÇnB,!ËÊ32Jåd’tr.My¹\“Ì.¦¡”«VX¦Å:żÁ*ö*§àçx<~j5BD'ÉÉBÏš–$‡í4ÄìÁÞµMŽ’Ç0ƒ›"õáOªfpg(džUÏ‚£åØ¡PB×´º:±äì¡ê´§]>FzÅ¡¤#ûÊãP´£H‘åô}è*ó&RØHPÓOܘ°=qér†l"ÕùðÙÅsƒ£ä4·“ë' ;¹º&‰ñÐ1c±òÃxplk«9ÙÂ~¤?PÊÅ¥²Ò,y^~–œY²$~ÙgÎÃàQ;ôyv?lµŠ•ÜM),QT¸”©Kõ¬äôK‘änµ¢t?#9¦ÈJf{Pcßx.LƒÉäÛ¼%¯à ¤àÄ+’UZuy¥¬$…·n/ŽÎÒxÞ€'Àa3oÝò “uûðÖåÀ[üqÂH˜BK^Y€ãhññàtØ Þx§p‰ÅsàÌc—·q2›B÷¢û¾HÀý5Ì’ÂDøUXjLð±Â£§eÚH.¡Ö²)ÇoðDx#ôàјžÁû£Ö˜iÿ¶ÖôvëË%hh˜ŒšáAœN¸5ˆ§DA@~Hƒ­P}“~Ù~÷ù—›/L=ÎÞ<~ä4º$~æûÅhë,‹]¡ 8\ á0MôàZèò€ÀM°Ø×J!鵨×E’ŒU4Ž·Ò7O¦»‘‘î3¦{ºÏ"«>éø‘éu¹æµ5¶Â|<µæÃAa]¥Õ”•Ë4ý-n¤±NÄã Qø1ÏP^ù’L#zÛ@ÃB|Pˆ#Gˆ, QY@3Ò‚´Ÿ p× àò8}ƒÈnO^øÉôl»q+opÂtŠy UìQÖx¬Òõ"j+ 7ŒÝîPl¾ ÓÇq*醳Ë/B Ѳ=¡!ac @ñ4vx »ÕzøüEæÈá²*¤ë²42yvž\É/Y»Ç¹¯û[èÏÞÝß»@ð¢cu`SZ"/Gâ µ–ÐÈVT¥è–v³¶Âûx‹9 G ÕnRcù#ð`æPL®ˆÖjåd‚`b¢µYDk=¡ˆ>¿èjôc$†A?|v¬¤ìƽă|ÖÆú­fÀïªôí#_Ïñ3|<=f>|ýúëöÍè¹m°á@ˆË«I«ßñlFËr€!îãñì𛸼„~´YÚ,eN^¶‚ݺknBšq'ýW±ÁVÿêÞõ.Ô…ÚƒLw“#Ú9ú4Áõ&žÙ°XT-/MOWš(€Gà/U"èà6 o`#ž,JÒdU”ik /ÔçŠp‡q³P’$êõI‹ó2Qä%ÃŽØ{ù†ƒ-8‚ø` ¤x¡ ˆ¿Ç4vÃÃÇbbzˆÀ\¿'_bÖ‘ y¿3k†56bhpxC¦É^ÁÈ÷À;08Æ«c‚”K€wEWÐé´“; [/C«ÅsE#'{Œý³7[ ð;1!e· :¥’ŽÛ!ÁÇg»àc&`;l÷ñ0àɵº¶s,.ò¥˜C©É½’×z³ƒ“XI×j®ê²éN7jÇÑ’ìKjr«‡û,âÖ'í?±=QÓ"ÈÑT$XAˆU„ i¥/=ÙvГw„¹ D¨‰ËÈ2¥JMF!Uâ§Gâ‰KVÏe½D½õ;Üéi¥Ç#4ï³8¨;N¯0@‘Ù¤OɺÞ*…™4Ðí¯~˜zo$‹Y ®1ÁZ£ÓéÞSa††T[(‚Ñ“¶•þ~óVx±ÝˆZö ¯ô&ÁukúÍØè(¹D–!*íJ'›áSÍ€#³ýér³Pìá"I œó¤SÔ²ržw °F_Ñ_ìD’΃]­,ÍH3áôqЗ¢‹F‘ÕçbÞ…(^ëÅ<÷›˜ùwŽ1]™™y‡ˆ‹FW‘É« ì¡%§ß@Ÿ³‡Îº|ˆj2ïU+ŠòJx«kˈÕS*Ø=´»ÑZóþ4ñŸàó…þN4½?ŸøŽÀ§mÚãs]°í¸qXŒE?ŒÛû—·g±Tä¿*ØŸÁïЉètªêäéºúòãÈ &“x\I^÷âÁ£Y±Ò×íFs|ÞçK`ÔÞþou,2¤5r{÷áq*£ú¤¦ŠIbÐ {èèú:Ž™¸X‘£D g²­ª¨ @Å|Rö¥®Õ¢¹ÉH:&;¦Ë=àÒôùñš2™637'/7—Ý•˜¢Ø‡2¼0¾T,IÒEï-Úí²¥åG)ŹNéÿMi«\À­ †4 ¬áQ^nGFF/<àKOQJ‰¬¢¢ÄŒà¿ÂÆŸPj½DyÆÃ`“d'ž…“ç%:n>'•î8Z¹‰%~žsCïªÞ}4™=šrTÑ®¸¦8¢8’~$­*íKt«oóú`ÁésŒZ$ñ+“kIú·˜ì4r¤–W0’ürµFÏêDMSÚˆ¼ Ÿ_{|"±!êCv×ÁpÝ2õäReaB9™k_yfUÕЦkõí>ÞTÌHF¦ªåe.åj-A¶î+‘d$ÉA2‰nYÁl±îÌÁd³Zž¶$kk9ô£~£™ö<ÊÁÉ’†…‚†Ö•_½õQÕ5ä܈>$yumO^*²N'{­J‚ÔÓ…7Ɔí \kÎ"ˆDAJwöfr—JJy´£yU'ž"¾n‘­hÚFÄœZÚü}ýù è¶ø›i÷HWð—’2Ûòª *[Hq¢ûw‚—-ž¾Ö³;ò-5À>ÖsvÿÂ¥•ü^+ËL^ÍËýþ4ÓX<š^µg»| ‰Šn“ŸÁ0öÝF3»<@¯Ž­Š¯J«CuâÛ­—ž>i ]¦2a¿DQîR^¢©fñ5´Éš– ynÑœl…Ùläó¨àsL‚ ;Kp‡ãZ¿lõ´¯%=lL¥å=Vf-,X ©tOÞÞig5…½u”ìå–8<²T¡’¤H®‰1Vί %g—­\½ÐeíVÃE®Žµ°WÈÓ™—×ü­í³£W®1½Å:ïxn9å’=DþëC1xˆUÅ.¥_ÞºýŠå–ã Ð@F˜˜Ñëøræ]ÑëøtƪèƒMàM€õÐÀ'DÞsE©¥™e%Äk@7`Z”l.‡ª™0Ø0ò?g÷–ŒÌ R½Ôl²nÃÞ·¤ñD¢0kɉá¾E“È}~¢ƒ›:)lÀ ˜Îõ6Z©÷ ÞïФÐë&ÒØ:…·-ÝëAfû {Èö|­ÎM¤\ЇC £Äó`¡ôÜRS/ÆÒ’ÿ²ìÏѼ?9e(·ÂfR; ÏÓ£e-ËZÒØ» 9»±K]6êW[F¼6ÇSlÞ˜+¡b¯SC©RQ’Ä«U²\™FÌu€$&/éI Lžä÷Õ ^¶ÿ¡^JÎ"£L -…m¿ Btv‚-ceFoˆoàðŒX±ÚÊŠ},[ºDûá“@#\KOÁšyð@øé¨§ŸC¶Ð/@÷ëÌåòMÈìø7¼:YZ }úÿEzh4ßN{‚Å›æ<;…›êð+&¥­‚úžÿ§ïG,NO^Ådf+•(S,Ódé U|ròäÐQõ)$~piçzv¾(¨2ªd#QúÁS‚¼XÉÅ9_¬yuïæ‘ë­ÌYÛ…KWó½¿Ð†–[÷>ù­ù‚Rvʄ݃\ž¹ç ›Pãp†pN/ …PøÐ ³ÿ!´ûö-° O#Sñ<ô_ÀSØ-…©0I¨¦%{[CØÆ"]çi<;à¡à g·%¡Ï×RÃ4àÎÞS•©ÿ§ë¦i$A‹ÄpýL}íI—†ã‘[Y|·Ýª5rž~æfÝ!™“Çw†³’|ßzÀÇ‘p’¾¹áâò-;÷­ÛÆ$\Øuh3 C»“#CÄîØÀ@©[`š9óêí×Ð’ £Þ**½~AKZæŸÿ,ò¾ ôïzƒaÐØ.<ÐwÕ®ùëXðû\úÓãéïzÎðí1óé›7_wüØ Ì5Ï 6•>"€­4ˆ97݈Ü}N@ZcÕ eAË·ô c,¶á† ˆ6‡­äb…Ϭ4ÙÍ kx³Ã‡­n"9„ ))±óqŸmøäŽÜN¼{Û /••"qyi©N•_˜WÌ.€ÁÑoÐoè·º_¾‚…Å…H%Öe•f2¦¹>@™+S–•™ƒòŠ²Ù¯ðàcî|ÏÁ5ú]?< 7å¡qfi–N«VTÅÜ›¡/ú ý¼ë_þ`»÷Œ¬T†xs›„¥ž[,ÅΟ( r r³L&ÏÌÔdiòYU>8‡yc[¥,?åðõŠNS¬Òi˜<łҌ¢<5rÖi5º’un ;l‰îW—” 3Ù­,W‘Ÿ—Í`çÍB`Dz’^§+²’ø#ð+o7é|\”ŸK&ÏvN+ß \­ªÑ10 ªxí#‰ˆ —G å^ìGYHCpDß#vhÀ%Eq¥²jä\JµEµ Pâô©ƒé®VVe}—¼•vÐ.À;ÒmÑ1áÛë¢?fµô™ãGÏœ‰:nštßïý¥EšÂ"T,ÖÊ4²,Enº’Áû÷ÒE~6±‚L›¥Õ—«ùs¤5ڀДs©ð‹&Ï °`¦ðdÆd²0< &1øÛ×Rt«úαs‡NT7¡&ÔüadÝF½?Z"ž$BK’ü£7&DF&†F쬉oØ}.ùºeîÙÀ ’?ä8) JÍÉV ÙŒLS¤)B*œš„ÆrÌZ5íï[õ53÷·übÏ"%Åàh‰®_øâFçwbÉf"YýkŽ}6ôÅ”§£VÆ„D0IñÑ(YLܨՕ¨+TLåg·šï ñÃ/—ù…&ÅŒÏâL/ I‚¸áh½¹-ÄHŠwð=8NuçÐ)Æä-ºÄq;ã3÷¡-¼U¸Z’‰?€´œÂì¢\ä¬DÊœ\e^®S^n>aR+5ÙÀáuN˜Ãëå9ÙÙHîŒr r É?§B%A-"ž.P‰MÞ„Fƒ=çmH2µ10tH HNJÕo>;r¦åúÐïæ|é¾f]jôV&15=ÉÄ„G:MQQY)S×pQ ‰ß Y²9f[X2»O–š¿‰S2»+¹ûúÙ¢™sæ,¿jeÍ© ¬¢8§H‰Ä²,™,£,½!…m‰½q‘Ô°¢žþtgCë¬úîeˆ¾å“Îd30KЇL°JYþà°†\´üél=ÇL¥q’ÑI¸A‡ RœFÒý*p€ŒôÙªù‹×øø~ÚöUëÕ‡,>m+yñìÖ ß…3¼—´>l¿uý)k^s'‘—>Mpጀ £´ ¹0ɉÄl ñ™2%¡d•¬X^œSÍcVž‘|*õ“Û·kÏ]fÛ¿8ù-‚wÄ0w À}GÏþS£O}yÿrc‡Q^•ÆoÊLNKݵ}ë¾$^°¢íëo¯ßn¿Ó²iêV•…òKöšºzƒ/Ùpœôß¶ë~·5Yœ`&8ýµZËà2¹¿;"¢n·ùþ±Ó§£Ž™TÎð/î'fÁ#L©ª¦ÔaæÂ‘¿|ûd#Ï)j8t¤^¥/R«P…¸,K“¶*lcÃëPD\üöœÔ|¹íGéå¼Syµq(‚ר ¦W+ùD@QœTÈì*Úûjà5 2î<|¢VWƒ üïC±”›‚Ó‹óŠóŠs R« K ‹œHÖ]DD©$§DVŒƒuN@>´Åªb¤qFEùEyä¿Sn‰BE0ˆrò²sÅ=±p¿9ªzb¡F«ê‰zàexÛdÃéá §šŠ >Ÿ6‘|F™ž®‘ëY.¿khƒ,™à€ÄnJ ©êjjèOúì>é×ÏЯ?EýÆ+Áµ endstream endobj 78 0 obj 6471 endobj 79 0 obj <>stream xœmWiX×Òîaè¶EaldPg&¢¨ î(FD”UMAad‰ ‚„®àVYdˆ Fã¾/Qc4\£ ¹«Éá&÷4ƒÉÍó}æéžsNuÕ[Uo½GDikQ"‘ÈÈY©ŠWªÃW…L°V…º­T…¯[¯VÆóÃEü-~¤8Gý>½'‰I¹æÿ¬‹‹Ñ`í’’‰†¼¥œ×‡mC(±H”¸«Ð!:&)6|M˜Za¾ÔÓ×bܸñÿcckk«X™ôiEᨌ _¥0#ñJUtL¤2J=Ká@v«Tá«kTI1aqŠÐPe¨pÌ'D¥ŒP8…«Âcb¢ãæŠI'ÚL ?“–„G®\§ð ‰ŠS¸*„0 Õ!ÄÊ?(Ššê:/jÕ²ÅÑ¡Kb”nŽî±kœâÂ<¨Ã½œ×¯õ^±Tâ’in¡˜`5ÑfÒä)S§MŸaKQ¨Q”åHÙRV”;5Ÿ²¦FS”5‘CÙP^”35–ò¦R“)sj)µˆšBYP>” 5•²¤|)Wjµ˜²§ÆS~Ôj ¥C‰(=JŸ2  ) 5”â(#jeLI)jI ¥MÌ.§ªE&"OQŽè­Öb­Zÿ;‰/hOÒNÑ~@Ð¥ô=&‚95@9 ‹Ê³½§Lxià/:¬Î8PF«ƒ²]„G®üÓàŸui]Ý Ý:½!zz¯õ½õo 0¤ÄÀŽèñ€À€R$⹞H[2Øú?‘4ŸÁT€eôzV£b0${’=U0–+Àci˜ÀXöDÒxÓ;Žl'¯O€ü» 8ìÁ€ÔÑ‚åb>ƒŠK18ñõÐ[Oƒ‚¹ íùO[Î]CÒÓå13S2ÖËñ·ÂÒøcGaN*>ö8?iÞÒ—¥²‚§ÄX‹´¡Ä¢`*æ_Ákn{iv.ÊC'¾hTQ6å;£ h^„¯«ûü<G~þÕau+Ûî"àлû'À„Å&ŒÊqmÌRĺE½Ñ²ÿ›ÒÓòÊoêËÛP *KÝ¿'9'5;”Õë±FÅ=ŽÄ÷ žåàôÐØÁ.x“l¢±cð¸£WLƒó çа€€u·ÿÜV#(áÿÄÆ½Ò0Šk¬hïîmOàÛi©YX2æÿÜMv¸>EøL¡PˆÒ†?ÅF6+Üí\UcÑTë=Ÿ Æ@}âÉeYsGõ/è C¦ßÇ#eë±6wùX£ŠO\ˆ\å¤ò%A†6ÝÉý¯äó“Šù)ä#SŠ é0I"oaD0×x§`¼ñ>ukHÙ$‰lƒ|çú;FÙ [VÒ„ ¯Û¾¿ÖRÖrJ¶š7äZ®˜ çajšOxÓmнZßÉûêD½§]¨£Q õîÃÞü>úSM ÁŠù"ðî-¢Á\(,5 åwÂPÑG°àGÀqÏa>žÛ^™½í%azŽ­±¾ÂÇl¦Ìqò <aŽÅSÞ[ÀX&Ý0ç¤ ›2QÖŽ¾ãké×úáiù˦›òæ»Wj£t<±8|ÂŽDDÒÉC ûWÐŽ*2äÓ…ÐëáªP OýO–Px— ¶ØíDôM$…©b[’Kn€ÄùñgÎ^‘îA2«Ià˰„>Õ'¬é:Þ­òþÒÙ:$w$w[…ÜÝ„xn_Ó®¨ú¢9ºnM“G©=šf©ý]œƒH…ŽeñÓÁt»;AÜ&Ã2&A¹"6±žÊú¶\´g{¥Ò»8ÃäVï)ØŸ_ZÝX؈jQÕû£w'çlB¤P ?øÑ,Ä|mO‡‡ ³[óÙÖØá,v1 ý©éײ‡¯›@ Ί߰‹,å~¹´kÙÅLv“ûÍw û±f®×Aô°òåÉËò³×¿­»X!É„ âÀ@Ô3€|ë4^@ÏŽÝé¾õî8è÷Ùt˜}/”ᦤ·›ëæãhÎüØ´b¢ÝºÑáîòà¹þÑS¦Å`3 ?šXãßkpösàÆôµ¡P*å`f%˾ˆJšAK~¸³ò°ûð9h®Òg¡‡ãj¬…,Y¬uwV÷•ÆÂo:eìS¹/·GRÉÿÕ%UòlRþ£™ÌÔ¬Mh3R–+'Ô%Ü|ÁL¢Û0ñmÆ’ðáÑÜ…º€yXoŽé$߈¯o€þ= ŸÊûHN”l Jp®qp[CÍw˜Z°¥á¾·ÞÜ¿ù›* A0Ì0r˜¤‹Âkq; iîe_ÍiÆvÎñÊ`Y°2Úù!¯ã¡/£ÅKº;ö§«M"‚ÕKÂT{$ÊRömÝ›uˆÅ#™mÉY[Q:Z^x2ö˜º9í bás žÂÐK¡Çf”ÉÇU.;˜’‡Š¤ÕµÅ-2IW[MBÄÖ¬ŒÌtM}ÄØ7 bŠèóW3 ž´œïDÒæòaÄÿßaðÈ£eŠ£W´«ì"”Òš™TLPñC~7Xã`îÃ$?ŒNp0—‘tµW&­Nݶ1}\Òè´eõ:”š“¶+‘…ÉÌ®Šy_æ¡R´+;/'•týµe&¨a]…o~Kyy~i+ùáXcYÇòç†c1Õ«x ¶W÷/¨_ò“¸Fð¤ÁŽ9…m-L>°qÏ(E§ª’ÖE©RýP‡*Š!›¸»$†7{êønbb° 3™+SbcÖª]¾Ü=Öc¸…Ãýß*]×&?}÷zÃsôµ‡ì™Ë‚ŒÉ>@Øq †XôOÇúXä7ÞV6{b– ̲Ø´±»¾­~tW&IÜbC¿bôàïÜ%â'ñåÜlæŒÈ^…ÇÐNL‚}ƒ™ŸoKÉLA¥Þª;5¥Û÷TÊŠÞ¥dn&U+U'çÜ™³oG¾ü 4Ó‰±#}ͳIÝëçÐþ‰ðÞ”‚0¼u?9Þs{R‹é`)0&¹ÓÂïàvUæ@E,PVx²åœ8?O™§Ø\äˆfœ]ƒâÞ%Kº^'U%š8;¯0è[Ó¾3;7;W†Í˜m Yi(ùÕ­=½âFüOˆ0ZWíÅk'š+î oÑïócа¨ÄXrgBaÌW &_?oŽZúeVzVºLSB$'»HQÞ…gÜ|üLÈ©fŽcÏÁˆ\Sq6ßÄé•‹v²oœ.bZ1uég‹d>S±+Âz,Öy8t_+yÓP_ræ´Éë™x ƒuM±ApAdó™_캤u‘$Á«W¬w÷6Á¢ïœ@úÝýª–VY˹š{è:º¤®[RÏZ‘[‘äM[}T°‹ç²ùËÂ7u´œ¹&÷µœäC[mIJ©^‹í–®:Úüìò·åýžqÀ’ô‰ ¯ƒl*q[Ýç×m°å¶mÈÚ‚ÒXë;®Aë‡Ö÷—d—ûla2û éAÉ[<“Û(^€©«Îï/Ô<}A¾)`ÓÚÔè8ŸàH¢Ï$#Jª·£Ù;å°ˆ÷"2|§¥ËìYrÉ[ÕÑ–î‹ ®êC|½0ü1@ëÀ˜A@<˜¢þ ®=a¯ endstream endobj 80 0 obj 4215 endobj 81 0 obj <>stream xœ]Vi@×–®ºª@À@S" Ý­ È"(ŠÊ¢Eh@5ˆ$dmš—°Ä%\bd›€l‚ DYDA2¢²dQÅ<ÇÄ ‰‰zйäÍÜj’̼ùQ½Üº÷»ç|ç;‹ˆÒžE‰D¢9ÞÑñÑiÊÈ{wU|”°´˜7ñógñZ«¦nM©ÅÔÆÊq}¤§…ô´Ï7H5Þæ@ö”–H”UP¶^•¤NQÆÆ¥É­ß ²±³[ü¿+Ž...òê¿ÞÈ=¢S•±‰r+ò##:^•”˜æ&_OvÇÇ+#å±ñꤸTyDTTt”pl[D|ô.¹B¯LJReÈ­×ÛÈ—-]êhO>–ù+v¤§Ê"Såå‚ýÿ²BQ”:1Rõžÿú¤èM›=Sb©ïx¥)¼Ó·úÆG$ìÛ/u\¶ÜÉÙ…¢‚©M”µ™ò¤\©%Ô[ÔJA-¥,)GÊŠ  ¼©EÔVj9eMR('ʆÚF­ l©•”åNùSë)e@RF”„2¦8Ê”2£l Å”6åK¦þ!²]ý8Ë}Vµ–¶V‚V·Ök;hïÒ‰«hkú(}‡žf’™:æG–aål û§“¥sWç{çº*ÝRݳ3gŸÓ[§w ø^¬šœz£ZÄKùfkO[àÙ¼…;Ð:}]\ÀbÞŽ ÎÉ/üT-]ÈÔ*+¨DgP{ѱ’s•uu7!eJ2Ï€TÃÛÃìÄj£CÈM$qº™üe$£7;Û‡úOíô•â‡dåŸ ÌöÂŒ{pÒ¦H©¤ó;†X’bxbÑikñUð˜Ë…yË¿ÇæˆÅ+ìàX1þ&8ÉãûÀ•˰­öT%"Ö#üæËž’®Ê&YũƊVô%:žTáÁ£þB¿˜ 83’_oF„žð3ÇÆvØ»áÕ°6˜´T]¿,sg6o÷ÜWÛ–#Å–taDEâÙ„Ž¸þìDU«ž|Á¹B0ðg˜âF.»/²y×ÛÝ'tðÙ‹ _ É4ʼnäÉŒŸß ~¦ ~~Müüš¬ý“øW¬áHSƒfÁK˜eÔE,ÜæØPpu‚ÿ˜ŸÅ}ôòalIÂb±RµbKã«2dUY•y—³Á-hždüLNYNºÙÎ¨Ì ±‰EejéžÒý¥ûO’íô'X÷?ýÀÝA_V´6µ6»„zÑH\§G öì™U–{Õ²§šª»¥’‰AôEja0«Ir¨!V¿†F÷H„+`16 ÙÐÎÁZúÖ‰dÿý(·`Ÿ¬ ©Qòú$½*Ž…Çé´ò¼ÏQÓAØu°Ä¡u¬©í:ú]Ëì;»óth…b§ÍgT±L›àû0èèÁ£{XÉË£{ŠS”f(.W•½g÷ÞÌáèÏ‘×@!1&ŒÚÁÖD’5ÂÏçp'aÈ•>¸#7%=™•t†ÅnMÞbŽõ^ƒGŸ‚äòù=‰_ÈjÔ¥ÙåÑ,, Õ`‹ žbBc ~/Á™8ÞÂ+à½g·ÏŒ]•I²ŽÚ‰¢ à!ï=.z0S´¸5`Iª¬¥Âaÿƒ^Ùñscýá#õÒgÌÞò r“WÚ%ƒ‹¤@#ÑéÁê›$ÖƒÂéHM1„JO%hRR—¤)+z¢NxúàM-þwþ î9ÛÜçz’$4^oå‚bñ—[ÁZ%{‘ù[V³ m6õˆYjùNÇw¤¤D㥯W‚?QâfÐzD’ŽÙÒÍd˜®•×Ä× ^Ó+ÝgFŸöF®ûT:ÒÂâg3µ»](7ŸÀþAS¿ípnƒl+Ä~`4g¶àަ¼jÚjß2—o&~¡!yzB½@˜ÐM„ÌPN%p3÷Ïôc~¢ZS_…ã…äít³|{„½ýööGR>ôœ<¹Ônéë M~„¡Ñ†Þ®îóµ×ÐËç1ÃÏC€—xùL²‰køå 6:A¼Û‚w5•\J݇Ÿ£,ô20kíUl…­ÞöÆyR0§‹®ÖFlßqu¬:o÷ž|YV.BŠœ€ÜyruÐVd‹Bˣ듈w5^Ìê2ïGWº{XIRÎ-ßÍÂb¯NÒéêñ~b}k{mõµ2iWqsÑgW15ø/ý¿'™~bÔÚ™ÇDÒÝÿ¦™û·Ï¶ö_<™.ÅÓš•ùÏ1/}o`Ê+$5 BšÜxÒù¡ÈŒÐ­¬¤{ùÿãÄõ¿Ç‰ë„R˺·¾˜2dÜ3˜«¾Iÿ6ö²,¦×ç´òF!)Ê@ÊÐÅ—¸ánÏE6Áž ÷‡&Ÿiú&™êà!D‚¹zà)‡ë±9ÔÏЭǗ’iH˜_¢„h4LÅqo£ »Â7ïØšµa „ +—}Øî=ý¢Æóý­}Õ?#˜‡`îîGÊ+CŠfg’bí³èdfeÌ¿'YCH_ï–«Ú‘‰bª*ûìÞSùwÐôàÈý’“§Žµ’2Â÷fãÆ_“A'O¸=ßËMï%óÓ{Lú*÷pl€Xa¥hÒ‘Í T˜‹ÁýôÑ–ÚêßÎö¡_X°°„mð¢NØv?Ê/Ì‘‚Yý}ó…›7ZÂ=Ö¤Äã¥XKŠõ\b6~ˆMX>ãO é ¼O$¯@Žu4å`„ëFÅÇö³’ñ‹ûNí2ó ÛîïÛòÍA)žOâ…ãË€”~°ûõG>ˆís;'“LÜn;ÞyË t—ßÁ®^ª H©*n_ÚÈ~s™Â–ÃUEGË›ªÚ{µI¹ÙcG¬³ìZÃÌ–w"LüÇ̬ÛÀ­ã½± ˦b¬ ÍØ‘Éñ[ï–Ã~4øÁUñŸì} þÄZb?KÔ ú䛸1ÉÛós¹cå¥E¥%¬äדÇ+;.˜–Ý}l‚çÙ¹bm¯á7•RɤK˜2p•6›t°ž³”}.ç¥Ø Ò9ɯ7:¢·†Eùû…»Ò×Ñ2 ûŸæ$“áë¼Â¶{y…wŽ|}þ 0¡¥sæ U$\'ÀâH!mî0ýìè1niÿºy ö5™•ÐdöØ®Þíw=[W -´µÄÖØêá"0:{º¤¢VV^|¤¸®ÅæÑÞ«ƒ•ÇÛIâ·pCíۜׄùúFöÜk½|CfVÇ7WÚê:tuaÁlÐ-ÖÓƒ%zúõ?¿¯8S endstream endobj 82 0 obj 3250 endobj 83 0 obj <>stream xœuW XS×¶>1œAdPâM"XEÁTfhAÁAd*X§²µ¶½NµVÑV‹ ˆ³UŠ–€A%`£Ò«Uï]'w‡wïNð¶¯ïûÞ—|9;眽÷ZÿZÿ¿ÖQFC(‘Hd””ž—”›š¯ÿç$؈„1C„±âu8ó_Sµ…ôXJ±ÿ™)2#£Š1#¿µ"G@³9 §Ä"QÁö}¾k² ³S“SråQ‹'MrúóŽ«§§§|eáŸÈý’rR“3åÈ /)}MVFRfî,¹/y;==5Ažœ^˜•’#OLLJÔO‹ŽOOJ“¤¦§fe­É“;ø:ÊݦLqL~ÜÂR3V®Ë‘GÆgæÈòˆ¤äuéñÙ¹IQTb^afÂâPŸ¢5‰1a¾YIá~kWÍ÷ÏNÈI‰ÌM Z·zap^ZT~z|ô‡+98Ê&Û:»Lqus÷˜ºdÚôe3<ãf΢¨É”-K…S~”'åLÙQK¨ù”?5“r¡ÆS ¨j õAR®Ô*’ ¢ì©…T0åN9PQTåA9RÑÔ‡ÔTj"µˆRPÓ¨ÅT(åCM§œ¨*Œò¥fPC)cJD™Rf”˜2§†S#( JB¤xÊ’šMYQ£(oj4eMÍ¥l¨1T0‰eDIÉ6ßR]"{Q¼h÷û!Š!_ y-–‰CÄ5FÃŒV]£Åt,½“V2"Ƈig6­ä,¸O¹KÜã¡iCw m5jloìe¼Âx§qû0vØØag‡)MÌLüLÊLþiêbºÊô;ÓrÓ«¦Í8³ÑfîfŸ›ý`nnþ¥ùÕá²á^ÃO ï.ŒÈñl„`1Ò"Ž™ MH¥eËD]1ÌÓfðÎBŒ ë3­ *‡X¥0¥L$dÂþ!^C¿eðNmM¼w ƒþ;Ókhø›’ÇÅ ˜ÀÚLøgª´#ÉœQB9?Ð9akÁ‰vcÀ_W‘6cýª‚’4úcT²½p7—Ý¿iÿgQªÞyðÛÃöüö ”hÍFéÍ*´§”Y´ux?†/º­$§ÛÈ‚’tåá +iÿç•¶§wÏ$(¤øßÝ‚œí ¹2Á+&Ãg™TrZÍ3r;§ѱ±¯ùÏ.nø!¿:¹AQíƒ8<ÎaìÕ'[ÑùFî’MgJ¦¯ˆ÷CœË‡0¬.+Ÿµ[é³KfBëG<²Œy( ±˜Áóq‡:Ó |ŠïÖÓ@ÆKá*,ÁWi` „g>T _«ÅBŠ%Ä @ë@­ÍÀ<‹SÌqºÖœÆü@†Zt@ë·SÁ«,:5Щ ÓXI„NKP1­èâº3œäÝÉÚ²KW­‘:¡Éù$'î;qù5jʯO:žt"æ[âb£Á÷x(† öªÞr¨àpþ7(%–¤¯ÍÏ.Èܲˆ¼„Ÿò°òÙóè膃9d價{Ò’­QÊÇiër×ådlXŽ8‚%jÌÚEB8 €ÎÆÎ3ø®Î„ž+x·c% ? ¦$îMƒ€Ÿì¦1NòØÞÍûa?µ-8‚}ß;˜ Áoñ$Ù6oþùol‰Íx¹8Gt€,®*52ý"Ê…å‚B Þe‡{à {±°™¤å \ɤ4EÑ»6Æ ‹ð,™¤û÷`Œ»ßXuç¼TRð€ÅEB<ßwu¡lº`–»KØ#0³›žIÿ$ÔkÄ‚ÔñP%„‚™H¸ZX–ã:r¯NÚiW ¾þ‹ZªÅZkb†»NÃèFz‚>bjF0Õ©éw$ЫrUB¿JtA#ÖNŸ .JìÑìô¨ð`ßd;„6©ßêweÁý¬Ы—!Š›ÄlŽÙ˜Zœ™šêE¼ûÀ8p5°0îzsᚣ²ªì}黣8<ÂD%!Åe=).öXI~¸Œ3ùÇp˜9ÕÓ‚8°épÚÁo* Á2¿ˆ´ÐD‰NoVrŠü¯7fâxXØÌ)î ºÀ̯u© Èw@P xtˆN? »`=º>¹P\—útÖ%GbÖ„É„2sñÜgãÀLº[€)'”É Xš„¢ÑòÊ5çòn9Zz‰ÛÞÂÝýöcÄ©nNÝŠ¶–n%:0ˆO­FØG°­-äq¶Åθ¹Â*ugUãuYç½z  ãàc,ƒÉxµÔÓÜ~Ç&XýlÉŻإ ‚ ·äâ,ÓGI%ÜR‰z5¸׎3 ¤yؤ8T†3š^¸sYÿËD†sñxÙÿ‚ô}ºu› ¢’óÚ¢yþ¬¤£9qþÑ,uÅ4 ï§¶ mo<|µN†±x‚vßK’Í›ü‘i¦7U¿ÊÌà‘¯À°i©»Å°›ÇCÅœ(û®öë/Péi+[°cö"Äy/‹›+sólÓ-í–ö°ƒª{Z;U¢gèígnñPÈ\Eç÷×8wê@=ºÇÁØ™x¼_ÈÐ0‚ì„h¶·iɬYÑKÜdƒò… JT¢>¢ëÂ^§ÒƒaŠJ·H|™JçØÁSb¦šxßJÊJRÓª—Øj½ÄbÑà §ª9Iëᇾº¶{–lß²mŠB+Ó–xq’šDf›pfŒÐ3Ÿ,RݧÉ*„)d™9¬¤ÿVüâê <ÚsØ{j…ÿÙY]Ü•µÍè.úñHý].‹E~›Väe®K_Q¸%¡Ô]¹ßìÛüͧǸiÌN‡Žù`Ž”èÞ¡ãgN]Øw9G! ã‚P¾¯i¶Â#¢æ¸OYh+Ê^P øýô×áµ ¿&®ËUº=^‡úØDêCˆ`Æ—¾ ¼†MIž›ÍVLô<“Ã3dÊ‚k%y(itlìjŸåI{æI×ïûdß§5œ;³›¶,€±„p£ßy­Œ;kwH6ë@à·™QÍè gµÜ;™¹p»ô}»sÊDmݤŒ‰ÛˆŠ dt“¦/`UU›ŠË¤•E»²Ñ*n°©ÍÌ[žûa¬Žjü¨¯ ྙýV’wDÚ·0Ý5 ®2‰P‚–ÿ-noÆh +˜”Ê-‡·ö—‚IÁÉÙlj «Nýp±Û,f·b;)î5ä‹Üˆˆþ:RŽn®È?”·w5Šã¦Æ%:H vø‡ ̃90ä9L ; £7K¸h γìtNãIúc¯s’“ NĺçÌkÁå:ÚGzÿZ‹Á‰ì7} ƒd‰C›»0¥ÃâLì5ÄáûR¶nú }j“ùѾÃ2¸Éj1?G±.1Eš“µ1cë"î1óõOµÕJÄ=<³&F¶ŽE)yÅÁ›±qqágiëòӗ¢@NRàt7üw›*._—~}8ç2Úön¯úš”YäÑšÍEÙ¹©é+?ŠE\HÒ±¦æš#½{eê=ßí8²—û³c _‘fÜÁËuxµ0@Û3°´å¼ÒIÂ9þÆÃ0òÙ¥oP‡0¸IðƒFh44ñÿÖÑ´@“ÔÓ°ôºäèŸ7?Vš~%¾2ù£ek—+8IÃö¯Ýàån¸DúâËý|x•4„jÊl·i©ö>à=õ–ú.ΚŸ(ƒj&àr^3Øê„þµÕù—ß 8ÚbK%øÙ1ØZ׋g ½?< Ç’¡áÊߘ.¼„Ùº—$OžjcÊDíİa­8aW„ÝnãWDßšw°‹…Ù<¸7€—l¹÷ÓÀ÷‘XƒSøW`uB¸"pS@ˆ=Xq 5¶k zDF싽ܱ~Ú1òÅöµ½e¢žwâž·¼ÝB¬Ï_º•KbÛNœ*Âù§°å¾jà7ùÂ&BWÐú—‰.iîiÄ—´1<³UØš®cªÀš1{ÏòÿÞÑü­¨ç­"É.voõŒÏ/×.&FGñÆt3µ m~» TŠÎöAMŸŽ ‚QxæªØe¡¹NßÜ…%àU¤$—€tݯK’®jœ$q^F/ê'‘„Z¶l²£óÒß ¢ë_¼Ф–3aZ {„í¼n»Z³€MÇ¢üb<¦ˆSè“õg:D‚gŸXø¢‡× ev^û¾¦çâ«Ë£þ~ùb;Ñâë9Íñµñµ‹¾ B®((-1<;iCÜV_NÍ|qá«ê=g<Ü„¸®s"W/Q$Ë\a‡é+‚6ã)£…tã•PñŠ;,êÕ°·/ ¼`-(ùÝLÍ6Zò®¿1nšÛÂh—IK/¼ùDFš€ˆ]«ö§ lKé'À„ç=`Ý—Ò8­šÔ仇hüÙÌf¶cl7Ç Oú\ªf¶ÝQ¹û`YݹÊfÄ)ë—Í)ÌHüH–³1ý³ÐR΀8v E¢ ¼yì­ïw¶ÂŠ?ÔEÐÎ,\êäq6Ùp›4ºG .="a&‹DD7„‰ÖYÒ˜Ó Ç÷“^ܱý`~è$ï¥u]yÄî= v­)_{*ê~z±ÛîÕ[˜rç—xLLÂúŒ•²#KÃ}à”ûÚÚû¢Ÿˆx¡Ï„°ÿ™a÷Çkkß¼G릲»ü©Rt®Î’½?%´/nN+¦òOŠWÄ䓜æöÝœ3ŠÚˆ[éJý¥ÿÈ`”«Küf-J’mƒyW^¾F·ÐÙÄo9ÂÙ©ü“¦ùNná!3=C[ž©oÜìÌá7âæ~½‹™‚„Á^ø“ìK¸çLä÷Â"ð€TîOÓÁ¢[\Ahaÿš‡Ã|ß ÔŽoú÷ GiQÓÒ¥äÇJò†œï‡ò’þös•'ïYƒØCIÔo„»W&ß_.•¼™™˜0ßË[þæ rÿÖ|gÂuÏ:)æ`7™øðB\@plܼy±çîµ4œ{ ÃF’7ê‘3¦‡GºN ¿ÚÝ}ãÊàYK J¸Õ!ªíÓ×Óøý˜û%(Òîþ\{úöùÖ²<9þ;ñjè1ýÑÐf¢=–áQ}Î0ò^Ceó²R> endobj 25 0 obj <> endobj 32 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 24 0 obj <> endobj 31 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 85 0000000000 65535 f 0000033760 00000 n 0000058205 00000 n 0000033614 00000 n 0000031672 00000 n 0000000015 00000 n 0000003208 00000 n 0000033808 00000 n 0000057399 00000 n 0000055298 00000 n 0000057728 00000 n 0000055718 00000 n 0000033849 00000 n 0000033879 00000 n 0000031832 00000 n 0000003228 00000 n 0000004887 00000 n 0000056199 00000 n 0000054184 00000 n 0000033920 00000 n 0000033950 00000 n 0000031994 00000 n 0000004908 00000 n 0000006545 00000 n 0000056438 00000 n 0000054344 00000 n 0000033993 00000 n 0000034023 00000 n 0000032156 00000 n 0000006566 00000 n 0000009652 00000 n 0000057005 00000 n 0000054847 00000 n 0000034075 00000 n 0000034105 00000 n 0000032318 00000 n 0000009673 00000 n 0000012950 00000 n 0000034159 00000 n 0000034189 00000 n 0000032480 00000 n 0000012971 00000 n 0000015409 00000 n 0000034243 00000 n 0000034273 00000 n 0000032642 00000 n 0000015430 00000 n 0000017800 00000 n 0000034327 00000 n 0000034357 00000 n 0000032804 00000 n 0000017821 00000 n 0000021800 00000 n 0000034411 00000 n 0000034441 00000 n 0000032966 00000 n 0000021821 00000 n 0000024532 00000 n 0000034495 00000 n 0000034525 00000 n 0000033128 00000 n 0000024553 00000 n 0000026737 00000 n 0000034579 00000 n 0000034609 00000 n 0000033290 00000 n 0000026758 00000 n 0000029701 00000 n 0000034663 00000 n 0000034693 00000 n 0000033452 00000 n 0000029722 00000 n 0000031651 00000 n 0000034756 00000 n 0000034786 00000 n 0000034829 00000 n 0000035201 00000 n 0000035221 00000 n 0000041778 00000 n 0000041799 00000 n 0000046100 00000 n 0000046121 00000 n 0000049457 00000 n 0000049478 00000 n 0000054163 00000 n trailer << /Size 85 /Root 1 0 R /Info 2 0 R /ID [(ÿYe¶ï…u¡'ÞÁÕO>)(ÿYe¶ï…u¡'ÞÁÕO>)] >> startxref 58405 %%EOF simh-3.8.1/DOCS/vax780_doc.pdf0000644000175000017500000032264611143604510013756 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’š/ ú&Ûjn®íJršŒÓ[²å¤¶åØRÚô×w’Àòã!åt¦qÆ“àb±ûí ØóË®,”Þ•îÏ8¸xwtïÔì®>ùÇ»Ó/‡ÁÇ«£_Žº¢rÿù8¾x·».Ú.‹²Þ¿>* k;Uvþ½Ú©ÖÝΔª·ïŽžgßåª([«uvœï«Â꺭³ï‡‡U¶Ï÷º°m£M¦ò½*l­U‡Ã{¹.Jݵ™Éë¢lŒÎºø²ŒÃ]ÞÉ{榩Mö)ZW@àÕðX¾þÇù7G-Ê®kw{]y9^ФÊùþE<ò¾ª»:{ØK¯²*Ð’‰½Pd}duyÓ´nA§3ÕÈ„ ³Ò*«šJ”ÕQtët&,žÿ,(µ³ÉF_´BÈÚf×¶µ(;|¢TÑèÝù#÷Ï?úÇã£ó?=Ï8FugŒÊžäû¶–Ë&{ê6ÚÚºÎ~È÷"œR—:;S¿ÎË¢jÚ¶¬²/ãW_Å ç²é¶0ªu"s÷ºº ‹‹sîƒ8÷dàF êJ„!›ië°Í°i·7 üF¥¯Â{-3•<®­àl|ÿV´iTek]û‰¢ì_¹-LW™Fp½7òÈ4:{>¾ò£J—É:25ÐèªìCþ–+S(™€[F¢ÞN(ë¹7qxÙ†EÞÇ ×t.0ìzEy}Ÿ~Àaà8J­OŽ6¼‰ÃëéŠUÛ´È"l2á.L8s˜0¥M°øW–­1@EÀ¨jÇh= FëZ|F-&ãíÛqç€]Yäæ6nOôÐÈ¥íLJ_Ä%a/ã„^qï#A(èxл˜OB5l¾Å^F²ÀÁ%ÕüEÜém|úNì«vSáÎÞ#` a5x7yÂÔ?÷š•ÿ ìGì{t9xªªuæÝøÇÎzzÀ"¦áϸ¬#LâåÞ> FÂð%·†qä-àMœó ÈVÁ½°÷Ê?­Û.³ñ= +?TJû4BLÇ %_ݴú½³«e4S†S.àƒÃ ¨¾œy?Ñ6¼Nk;Q•7áJ컬F~I% ÞäÌNÑKP w{ˆ;ÿ§¼—øh"ÄËcctÊ’mÔ•lù·\ÛB«ªYòý‡Ä÷³½ý˜M¼Öy|ó…+õHÒm©’F Ÿ¤0<ܲMÜ.¸Î š *K5fœIÎ’*‘¹§e%úLÂ%1 ŠY05ºÐ*8¡¶êmÞÿõd Ü&®4¸ xÃ!0ñ*îhnO¥uE¿A…Í×.äQ ‹Ü¹²t(LÜõÂ@ /膮èj_PºK) ¾}Ñʆ,ýV õaÍ/ñ܃Ccf%­’¢â†²8äa6õUóg<9…(ÏE¬ ÞKKGE°ªQžëE›I…gº!è…Îu˜¨¤šŒÎõ5 HCÊ.¥ GÎRÌBß@7úI*ÆÓ“¸ÉÒ’$Í  …ñ´os¥]¤6èœú}•º]LïØÓœUˆ‚´£ߎŸO*‚ T³`ýÕಖզUâðÇì@×Á498ø°\—ÓÔœà!®…•;A€ö¬Î™ê–œ½ RÜ}p%$’cô§r«|\úµÁ^‚Z9þó‘Ë"ö ¿“Jl%ÕáŒx‹»¡³âÚý$’vóJ†ëyV÷GÅÀ-5fZ³\ÍQ^Õà´PvÌy^3¿Å@ÎXÆíêALìWD¬™›Lê´l܃ÑHW¡J륌8HeæhlÓ<ðOosæ©Vëþ$B8Úñ%>‹… z`l—!°IÜ5][Uƒ^àŠ×÷ò$ lzPÆÝ!ØÞ†ÑË`Ȫá•R“ZÉA«VUÄ-…Ū*?õ1tk}?œ ðÃðÓ¦#£ÙᎳ^÷(d?Ò’ŽC6X:)‹ÉݼHJ|¬·dWÇ™PŒß-ù㱊åŸ}Y^uíbÀ"§«ñê÷Ÿµ­–Pur\ó¸IE´Ø åjEÁóéA…h׳}õ™Ù{ƒê`MÈ;—4¾R ž…ª{£Æ7fô$ÜO«mXå–ì%êà–ýÚ ¡V\3ÏßW¡ÃÏq¹­Ã]`nâÙÂðg’©ƒcâ¶n‹¶2ÄÚWw3ËV|Ëu?»ˆœØ¬ª€HÍ~{­Ëc67zjœ+Õƒ÷²ŒfןƒßíÎêU&°óëòÝÎŤ.—óz!Æ.]D1wºá"j)’ÎöéLnµüîÙðwIë‡ ¿ÓQ-.܉0lNox]QéÑî.h‡ÍLÑì"$±"~{uÈköwZÌO¢·9³¢wúüæðN–’„û¨U¶Y?œXJ»â‘”Wω¦Æ%g¡°z¿€r^ô6š$Ï8ãëEoZIï¨?ÞÕ­€)^¶l=†pýþ]ŽÌc|ÑU•c§€:ö@ÛÂIH=h>M É÷µ [:ϸÎÖtÙßåaStmYgǹj\Ín±°‘!!ÿ5[ D/!NiûÄw±C£'ՕƸ•ùªÃCk“{x¶uìÎ…¢j&Au{ü&-뢶bV"ˆÆ¶R|©´ˆ§SDäîh vò,ò¤}dü^ˆV¾ö…«k¼"„áqØÂãøhþ¾Z¬)N¹šAb ý±Ë‰†™ß²6øèaäWÞgè_¢>¿ªOP2Þ°Å™gqæ!lPÆÐߺYÝÌžFº¢Â^ð|ñb–µA¬ð,>P§2)|÷š¹4óŒbdÞ¶4A+ŸË¥€•ðHœ„2 ±£SŸà~¨qõ˜FBÞpF F½Õøúó jªp?•:’%ÛŠ€÷l#Þo'Ì>ˆÈú*>=S¡í xŽïïO=èM ‰ø¦3íhðåža\ç'>zÚ¢¥úĨζ ñSÀ–3Y®ðƒxnX HÇG ÎÀÓ€êg ®F÷÷4°{ñ愱~ì‘-å~aâ=÷¦Qãt„ÔcÚõú˜{÷dº6¼ÎITiŠåð˜#´ °â©Ãcêµ¹çÚåÄ=iDÕh=·öÙ霛 ýv\€ò`ð~€VœÅâÝŸ²° 2üv€—kXŽ$bxÖ–œ>±>¿˜„àÓ7v„´g®Øg×;C—æHŸÄÓPQÅïgQ«+?[zˆî–‚<ƒPâ9•©’¼„'#ãÌS Iî8Î=aîm5J{GµœÄ‚ )îÊcò—ÄH]ÇPK¹)Uú.»àéÎÌ_j« ÕVà/†Šk¤Ï—]ñ=C =rÜ#/§ˆº?èTª žK†³dîiVSä'y#ô_†]OÕ9ä.ãRŒi8xšqÄs Ç“b×47f™Kê G³ôn$²/^?&eÝŠ_ Lã~׆¸¿Ñ&l)ÙÇpð:R¸ð¦YÚ´4’-$ù>T!÷~¿|h1ͬf‡•8´µ2€fbKêp­OI<ÎBÜŸ/¥»z{4XZ–xˆS=Ò$‚6:ä4ŸY´§ÑFÎQØ)¯óÖùÀÀ/âz~jU6N­Hyñ5;_ËÆNÙ8çßñôó"áÎ(ùeç´‰îÀ½EÒ±Êðƒvnwü³ÕìÙÅâêUÜ'-]…,5™1øm,¢á "w¹ÆOúóá2j~mñÿlê^º ]¿$! °×cr£²¬×´ÔnØd÷a ÷$äBb¨ úݹ.ÃèWjš\7TsÛ ÍkéoZáB䈆G°¦¸iÇ͆ŽÌÉ s‹À&ojˆ]vü§.ŸÙô˜ßòlè\ZS"ôñ÷pUÊ‚ úaM‡=cþFŸSH~Y¸a{¤À¾¢ûºÃµáÍïp?µÚu4b•wÉÂLjý?m“e·Û´vÉDI ýuÒ Å{ÒÇosa ,ˆÏ[§r?ô©¼äYÚ´±¿×µé¬þ‚åzÖVߨ-‘p¥±àŽ¿Oë­¶*thÃÑ%ñpšÿØK;dqICJc‹ªžõ£œœýMþüpþ(;endstream endobj 6 0 obj 3154 endobj 15 0 obj <> stream xœíYsÛÈÇßõ)øHV-Gs>6‰ãM­,k·\»›E–UItx½v*ùöiÄtƒl à!¤h¿ŒÎÌüøïžžÆï#)”Éâ]¸º;9=£›?NÊãó?Ï _nN~?‰ÂÿÊ´|u7zy•%‘üèâÓ‰)EªVÕÈ;ø;¹QPRøÀÝÉx4¹øWU®k¤¹ >amQá×±šLA% •§Jy mßO¦Vè¨mÿs¢„ ÎßM¦Qx‚ÃZ·ùüåÄŠdáàøëdª…3Îøñ~òËD ã“4Å•êóšL¡£|pΓKa£×Xÿ¸?¯„öf,°çUô.>uÑE×<ú÷‹¿žh-…,ÞÅG4»»™C..tôÁ‹‘£‚>Æ^‡ŽUv(E¥´ÖMÅ5£а觩IJGSeD¬:K³pþáü‹&Nã}¬±(m!~“ˆNAWÐ^bñë~*¢ ‘"AÞ¤3üÚ8ðs/»9žÇä|>òy(|î´ s-Ã\#”SK˜[ƒ@&—ÚÖ–ÚU‰ãW)¼VI¹ñYbýø§ò¨v:PЦÞcñ#…rMÚ÷ØÖÿÏÒë 3^-Ü<‘×ÀDž“ë¾Æ»!ŸýOn5÷Uu§Pu=Ö0Dà»dСX2l‘'@îáS瀨¾:Œ–#wë¸s ¸³&»v]pGŽ–\Ró tÒ «f ,ùÒ`àTÉ$\Än€’Bq¸c¹•wÊ ÌóÔ˜G*o2tVј|O÷-!;áÕÄŠ “šdðª9¼^ €Ô@¬n^KR…x;Q@úUÓ–Ï0BÕ÷À¶…bô%Bý[öï~–HñºÅÕûÛx]gbÔùk97þ¡l5Z]_Ê€²þmÕt„Öô@†ñ“’Õ&Ý+Y€ë¹-%Ö¥¤áXGì^B7Â";2–îYÖ!‹ën_jZƯúhë­S6ˆ·Õ÷ÐN±rU&ÌöýMG€ÒD°Û„† ·¬ökÒÐr4üp š5i‰]XGá2Vrjok°¤t„ÏtNP“ø¥f¾S#mS –­²Of]ø¶D]ÖTMà1´-…Es)BC¯ë;%“ÚØC·§‡Ëå¾ ÉNd©GÉzdq-6% î™ÇMOóÄþqâAíy¸Y°÷à^ÌUä•’ E±Öé–a˜Û¥ù®h\ ÁÏT)f v!–®x¬$Ò­4£œó2tE=  #ræ6_e¨ô~-,/BÏQoGvî‚EëYU`³Àζ6WIô¦ÖyáÔº$bCúàb7Rxbmõè}£õ7 × 7u–ñH$U©¤R»ÓWÁñµ†ž ž5mÈF ¨Y&…Œ`h¹å…ט' ¹µL¨Y>¥Êâó@ÚŽÃ(ö1 J&J°ÄS¿ܸٻâvR‡l‹Ð;.vâ2XqÂûN\îºrÎ&t"ö2iŠ,ÜÊ,C]Ëyßrž¤/ýÏñJî£åH`Î;GO˯ª£§ OñQãvSÇžùÉ,ëY€ rzÏÖzÓ›uò“ÉC,?ÅÉôlT«g*ïD[¡–f ÞW°=Ë¡OfDå_ñù1#ÿ#Ö¸É3öz«3–'d³yé>S`0;³óñ¼ç¶§°†‚–Ålãõ(Y-mŽö·¨üJ”©gþ[VzðÒ¡­VÈèYðï—s÷2ƒbˆù ¶ð¶ùc Ú/x·K3.áå&®­=.'¡'†,fÏ®ÇÃz a]ädó û$áôĺàßh€&IÒÅg©åcLÛ]TÜôwO5mÄoÀqóžM»À»NøYõþ9&‰Ÿ£€öFÙÛ8Ž*èi‹O ¥Ö£Nв¦m—t ,´XÇ1¯&ÞR§³xÄòewÞŸâ±½ÊnâÓ¹gUÈ&ÞŸV(¾î¶—C_‘Ú7$E -æ”êœçÅsÎ)ò.F²Óð//¿ç"²©ˆaßiÞööœ%i¢^çuªö4ðÉœŸÏbPº`zCÃ)“ìoñÀ“hž ’xND±Þ¢a~!>e¶£ØM#„›˜™°®-·2 ²oÜ'ç©Zj Ï ‰T\3JÂÿff¶«¥ÙA²Øô É_š>Òhó!L žsA iöýßÏšª}ˆ¯ŠsF`ÜÆãdrpÀw ShQa$¨ÕÿD ´KB|)rtK‘ú¤Ö ûá0ú°jßùª·G&üÞìˆíOÛÔaχááé0&Ý€ÕBËÆ[ Mƒ5Jy+‚¯½IE†¼Ù^|ÕxEIHÌ&Xæ"ÏjuA}æšm{Ýÿ¦Ø¶||÷¨ªÈUçÒ³@µàº‡w aÐo•vÖÓÑ;RcGHÿ® Ñ«»Ž s!€üô\©QÉ.>¤R\ÀW’#5òþNn¤£ÚŽ.îf9¾¿8yÿÿŽ*¾endstream endobj 16 0 obj 2770 endobj 20 0 obj <> stream xœÅZÛrÛ6}×Wð­d'„ ×ǤI3í¤©«f2“t2ެØnlË×Öí×w^vIA%Qvü^»gÏ`®“‚q‘þ§iÌ.&‡&9¹„îäðmݸ9™\O,+ýŸÐAÛ³‹äÕrž8æt2ý:)˜s¶0Õ¬<Ñ þíTbxÁ \L>¦Ó,çš®EzšA7Ò¤g™„Védz›å†×:M2.˜–§Y.$+LiÒ¹ï4¶t´sÑNt“åÊ.= ¥(Lz‰ÍclÞc“Ì•@o©´.Jj»Àæ ÂØCš6Ù,(µMïp:¿5:=ùÕ)N?Çf®â~—¾†U˜°VªôMV²B;)Ó°“˜¾Ïr.(UúÒ[šÂñôô¥9ú†ã"¤y€€L˜¯T¶L-[g_0¯€˜œùf©08ŠS7Ÿ‡0û8eNž-™â@•é10ƒøƒ¸›D”UeÁay?>¯'ðX•ð³À/¦']ÂU‡“®GIm &*FrØ+“JiçݨDSÚô÷,/¡·”Öï àˆ¬†¬°’ÃOØ™åÜY ;«LJ«ao¹eÐi}ô˜,ûmo|ØŒ€øõÚa?f9ä…à¢]Ï÷Ò‰çô¶Y£qÅ4ÔJ²2¤aå¦Úkï&Óï;y‰éD¢<ÃÄ\Ärð*’Ìçmk›²“-ßæQ’Õ¬…¨hàCÓ©F!wÛûmIbý›qøôþi-ï£`‰[ˆAÈÿ;:uLiÈö¿âÚgmæ\FM{žJñ4Õ(ËÓÏ÷}«=mxKU|‚¥˜q¶ÉÍ•× EÜ—kÅ™ØþQÉœv!)]%€Ág´ÿ©¶ç.ýí ©5Úʨߎº‘ª\¡±%úí"Ðu ule[j¥›,ôÈȼ ß„  +Î( ìyÛ"ÀïpÖZ¶5äÑ"˜$Æ€ˆ˜Jukn ì~\)肦ÄQ«—më8¦(()—µ[e§0Äb°$nýàÏé¶[Ço}ÁÞ¾ªõùJzz)?z÷8*~ÇQ KšPélˆŸQb…&BAº¢ÍB¼öƹý)E‹¨ZWb¡ª5bB0v2­…@†-Ѹï]2¬“ií§ÑÉ.ûI† 4xë‹´–ʸôU!_—?e¸ZWîD ‰u¾.!¿ÖŽîɉøiÿ—û€Yùˆà¾¬j¯†2ü—4±©qÓKÚ¨£½1 `kÕ0¦«?ÒhÆy¥?²2ÀÓªË@eö¦…Ù›FO2õ£²¼Rþ-}DÛĦ¸æ²p„îîß9þÉœ—æBÐúr’JS¥¦>À_ß…“"¹¢!(²ÁƒêÞflÀ$†ó´o–¥„uuÅ«nQD€Ø’¯u{Ì÷‹’ñý©¢ SÃRñºvÛûUTÚ÷mUÖvïŒ{@V]{]¾‰EUIùnKdÍ :ZçÂҹ×ìàœxíù;S^ •Úg\úÄœµ‹~Û3# ˆR]Eñ40Î2v³JvßZ6åå΀ÈkT#ëá|7n^<"ŒM•Qrd¢¨KŸ] FB´…0í‹á¢cÊ¢¼«8ì¸;«ÄØ!ÚT%–Ößà”ÕN*œ³99ü>àðÚ²ºç¥0œAˉd@Ú9ŸÄn¦$v8Ÿ¬`ðß7¥Q®OA®šuéÑ_v›¾îÒ·GN—Û±8¢÷œçY•?ɶ Ûâo„{\;~Ǽ¶öHÔﻣWѱêÁ $uÜù‡P‰¯õk3q_kö3sl ›È$ñåI(º%¸yì»êsb ‘$¿¨ðûJ¬Ñ‘C©†ño{£—×qÓqCü±Üiúbú[~NrJ³yVmsÒÃgNü`‚}<Ò:¨µX‰ð]”Ž™ÎW"ÂÒË£5´_tÉ™6~$‹¿Ó?v3‚ÁþùòÛî.`‰37~|Ì)¢ækWœT»%„ºîeùí(¦»C|¨þû ÁÑû¦òD8úÜNvÇAØþß qÝ'ò.ò et·Jµ5ªÈ÷…ñÞµv÷áÎÒg8¬7ÓÉoðó?¿endstream endobj 21 0 obj 1563 endobj 25 0 obj <> stream xœí[[WÜÈ~çWè-3{,!u·¤VÞ x³ŽacãÁa7ÉÉÁ0`² ƒðÚÿ>ݺt•¤o¦5cÖ9€Ïq¡©ê®ËWÕM} â(Alât¶µ}˜‹­òqpø—š¸½Øú¸¥#iÊœ>;#˜$AY09ߊ£¢Ðq^šYjþ.Ò Oâ(7 ³­Œ‚q(¥’‘Rœ¼ŽDŠ8Éž&ã0I"™jiH÷ôßDÞŽÃ4ŠãD\,2¤L³,–£Óq˜[†ÔNü¯É_·Ìx©&û[“¾‰Z‡©õÀÚˆ"J”£/ã$7”ð¹H=´‡îJ†£Ñ¯hÈ)PÆ–‰žÀÔe®xeœtr¤4·ÍEí‘,cjÌL ª(Îe>Ú¦i?Së"0Ó>“ø6Ÿ4”"O"C–Byœ¦|Ð÷08ÓN«¿EV˜d"’9‡J­» óÎ5Ävc(4LÓ9ánS0èáÌj6ç:¸Z¶9 nA’¶Ö©¢³L¥i U-SÂŒ©4Í «r*â(MôèÝ8Í"ia>N¤ŸF©ô(‡:*´ùi[#ÕD.u6úÑh©“İ…Ü 1ß –T,ª¼¯Ym7#(p©E#%j‹‡,Ê©,¢ÒX#3ùO ؉'3¾3ã|p0œ:ŠáKiJnã‰Â&ÆŒv9£qÊôƒÉÁŠ åø Ç‚ŽÍBtÓÝ€)1JçBÕ£¨LshÃJrAŸßé§ÆôQu5b3VÅ"«=PÕ»²ŠÍâaŸf›…j¨O™#“XGÂì Ã*û­#œƒ¨^6«Ýz¹ç¤þÞuµ¡4¨Ì„#"¯¡Ÿ×©¢Ì gðé ŒŸ§HÒ"{°ÂS¾Úˆ$Ÿ—ëOë»öº`ÉGò2ØËµ73s¡ì°åµ,¶×a¹w[ƒvŸS‰vr¬¨“r¡ÏhïZÄV’9dø `Oõ÷wK±:IÉ|ÚË¡­ ÕÒOÐÌ+h&ÞõµV’)FvõÒQÏÀ§}°Tþ*É#Ý~æÀ ð)^þ±åà3àÁkr.BÕ‚ã–ÍA ÇýL>$ò˜H¶Œ&PìØÙJ…Hâ¼:^mÕ+”L„2*&¿®á˾8÷µv!š´¡ùLùkŸß7‚šôAmÙÞïòâD¿¼LÉÏ>Õ™ÍåBG‰ŸOæm§š ŠöZC'•N9] …¾” ¬ÐŸ@œAýo¡çð<ô°GG2º™´_¹– ùÕÆoˆøè<Â&û¹Ãž!Cbß—â0å¨íî§V¡”Hv;Ä2;Åà'«Ïpá›Bú°Þ‹­‡Ú‘\ ÊÓ©÷¼Þª9åùW«(W¢9ÿÖ¬öœŠªYó,J¥ä'-ÚfÑvìÂQÕlÌJ4öŽmÍ˜Ž¯6Bï+Ge+Ó˜E˜A땽¼Ž3ÌR^¶QÆå_Ú°p3\ø ËðíÃ#Þ,õ® Jü®s1Eø½$ü^ÀܰÊ¢÷ÆŽ „jz÷@˜ïõÐeÁ³YeÖuÿº@™–mkï>D2ÛÄr$³Úù„ä‘Ìô-AíEò9×€úž@ç=ó9ý4¢Ùfbž ¿7k€–îÝè`AwC©£|—;ìV¿9úCÆ}X;‡:ÜûfûZÑÆçï7ŽZç µ©$®>ã»1Ý#ò9‘lsæÝ†=aÇî²» ;eYÊu”˪*Êïg„õ£Õõ‚N”뽬¥=#­½´Óý„Ÿ?Dí)¡´²ö(VžT 1ͪ11ç ÐÐw/1=d=Ÿÿ—âÓÛ`ÿÒÆÏÀ ö±£„£V\þt|ñ-o~¿küÜÁðçת8í÷/Í{ó!ðèKùnÙM3;%±ã¾Ä÷ϧ>â—“øDõ½!+é=--/­ ¼Ž\çÄCRtö¡[(ωgBä†kÈæ6Å\ “õV·Õ7§zïÃl=pÒv¢5lÃ`cçá÷xø›•Þ7ÿ{U™B1Ï…¡\’¯ì{UìZ ¿é`bl³É^ãõÞ=”àÄb9$Î3ç×G$mšÖA$] ½…ógGÑ¥óãtÿ>tÓsî$†á‚±ç-¢T÷øK_ú†Ö7»ËÁAb‘ùé)Ìþ0·ZiBj™t:j5} ï¡Ù³½VÖ xäËÚ×åv=ö´Í ¨·ƒƒ¹jïK•àøac½¯27“Q»Ì¥¾¼)O.gF ¶¡&Ë‹¼i½Sþù„õ9´ëžf XCdÕø+Í¿f3ÙèM@wK—Zåú^»?Ù7|‚Nc¸]†+ Ç5Ô Û¹ ë’•ú´§Pד®±•›Pßÿ5l]h?B`xk#vaõU%¡ \RZŸ,gaôǽ×xa©ôË’"m7ö7²V6ÀªÖ°V« bÚ”Q9Ì…²»pV?М­%Ç)ź|±/DæÐX û…›ðÚ‚›ûƒˆ‘÷<œžnú5@W :ÎsV“YÄ«Y›¢òb²õÆüþ ½HÑendstream endobj 26 0 obj 2601 endobj 32 0 obj <> stream xœí\Ysܸ~ׯà[†© E€ Hº²©ò•¬SvìȲ7UÙ<Œ5ZI±®‘|¥òßð@7ÀCŽ${õ®«¶ÚGwãë ÿ¥‰QjÿtÄÁÙÎî^­wêæhï/-±:Úùy§L2û_ÝÀ郳èÁ¾ùPˆ¨J*íÿ´“&UU¦E3ªˆD*£B¤Ia~<ÛùçlÏó$M…¬fQ<¢RI!gOâ4Ér­Ólv›‘UÊP󴹜šßM£L‹Ù‘ ;€éQò¯êQ ­ªÙ»ØLU¤yο?0£Î®h¾ úiÅ׿:ü8£æ74㉛‘ ¶¶ƒÿÑü’ë*Ïf‚ºß£N)µþÉue~g÷§¢òZ%}Õ´&íêS©ŸÑî~ŒÛ©á_ûÝ1ãå2Úº³ÿû!Á¼Šç2‘e©òÙ9 ÅØùÁîøþrúv°¤^ïà[ÀÙ4W$eÄMp'XR‡k(öR¹f¥ËVã,ÙPmÏŠÏt·Â†gä5ß–[!ÚBEÆ™2«jöµäáØl ¥²eUÃ|”%ÌÞ_šSÛlRêT´ª3ï:Ì[%Zžíq–¤ºRŠ/þ"P¶Àú Õÿc;¤AöÍÈÚôÕröØ5FáN¬:x:àÔ…FJÃ5Ù/âyeÙšJs¾‘–™µ Ó£ªÕX«D §DX,#ªµ¦°–-ÆÛ×OÄ °l3ÅÇXV‰’·áiÏ >(Ó!þPµ)³dƒû­]òÅzÒ÷¸á)Y;‚]®Ú¤c¦Þ’ X+cÄû¸JŠ2KíiGºðJýi¸ÎaødœCþCmå¦ÅõĺÂläÜâ%üÌÚ4ay+ËÙw öL£²šW±ÆÝØXUU>û!ž«<)uê7lJ}½òÎ7™j¬‡L90RiåNËÎæŸÍ­ì1ìÁy¦íNs†dæ—ý[ëX–ud¨s܈_u›²TË¢´£íîÉÒ¹B25.‹î<çY’™š=wÔ}G=Šuš6C0oÊ.¥U7Û_{p„Ö†ïB=Võq4¤QSéNfæ7¦e&ji]Cý¹ä æŽËŠ:BX¨ád¤ílÉ—H_ðx+vžÙÔ+Òú±¡„Js>SHlÜa5ýÔXrP ÞióùK%eî,õäÑ(ø˧¿ÆÏ…Ñ5 ÍÐ RW¾ ݨÌO§ß1˜²=²Ë —ûþ¸w´’üØ4ìÊtÞ95é+•²‘’9ù­½×f‰Gœ®µçÌnÌ"Qaè¢1 –® ¨4Kl4„‚2öó["×D^7h“Uü¼b½=Ož%#7‰|jGc4œ±â³%"”¼pZä,»bdÏ,„16 BÛ ”Ý™}äŒÀ+G=k(¾°–Á2l{ë N·ñ¬@²Qóà|SïØ '‡M‹Í8,¶–î“r†Ðr—ÝT¶™V¦™óÞv÷²<–“”¾¨T›¾Ð¹ù»qŒ³—d²Ia˜H‘ˆÍY³šd`$+ˆÔìa,]Z={a¿©²ªÈŒ»Æˆ>^°1Ï­ï•fÒzò꾤¡>²¯ÖŒ¾ª»iªmêƒÚÏbmá ·Sg†ÊÌ|T/GÈÂëùŽÑ'4÷k>“çYî¸ZEF×xN¨Ì¦æ…J<žJ)K{ê s-SÛ«¤4ƒX)Î¥®’LiÃï¹HtZIÑ2\éÂlñ5w‹NX¹ÅåeÝáŒÜAJ–bûLW}¶LÁ¸mØÍ>•ò÷.[ë~c1›Ѽ^hZé\ûÌc:ñv=ÇÙ ®‡æóVa4çÛß4q‘EëTŠDÕÈÚZßÇŽÚwT䨇Žz,7õ+CkžÕ9ĹN­{Qð©1¹&'àÈ+"#"Ï`_ÖzAäŠÈp06ñ ‘Ÿàì³ï`kI$ójt¤¤l´üRŽÒòaŸ|âDêi’úÌÊ%P•uü°¤œ|”Ô8Ijé¾-A©¯óéHfß²¤Ð1¡}GŠŒaßàáúÅŒþ‰§Ë óD¼™_~ç‡r£™}ç‹nôõsÚj4¥í6#nÿéõ~ªÒúÍtó$PºèF· uyKƒâË!v#ßÓÚÔÓ%wYt<çà>÷Ùn{iY+CÍ=7Þz¢U^ eË—Ä)r¶/œ>ë¸lΦ`Dã<–—Jt$‹ )¤»WÁ÷Ã÷ѸҘ›¤¹¦¢þ‘ç®ñ­£µvÔµ£þ{ 3ò”=àhÓ·dPžBßòœ£ã\”B%²º©aÞ4ƒð˜ÍƒðSØ— v×p×ðÓ˜—>dˆ¬e¹2ž»@¼½IòØÛ”×´S°®ÑØe”<ãÊ[¸üüŒu`’Ù…>ÀÖøãÐ5ô!ðÊÞA=`ƒa1ïÂÙpˆµÇ{K0ÂVÖßÈ/ ®Þ5mï² \ ÑµØÆ~¿2aã}PæYÊþÕ¨¿Þ8Ô;î‘ •N(@õä1³³*¬Ù§°ö=üôÕ\àÒéß.¡¨ë5SÞwþã?ºÆœsŽ•ñ1r—Æ*¨µ¤ÉR¸ vÝÄlóh]ú3*›xi—[¤•Ÿ÷!]¡yNeW[Q¾Ûò¯Aå49°V"v»[¯R‡Õ;¹Å2éAÏëòˆÍÀnž½êA׊kF+7ØrU¨Kp™®(êû,¶µ—„´y¡NSÃLD§Ê…•‘W8eà¡,3nµ†J‰hºÆ1œa¬ÀâÚ„àxÆÓð(?¥a_ݰ҉% ©j.Ãf»ÛhT<ÆöZ‡*Ksøx÷è¡À¶ª𪩘§ßÜþ§‰"j¢PèÐæjH&*x`Ó_ÖQŒÀÄŸ\à7tÊXA&Œ,X^;0„ {2O ñp×b­ÊzTs†=Èu${Æñ˜ÍíæVFÜ:ßà`…©H«ö0ÚÙ©õÕ›Ò?2£éa¡Ë[teý5lv¯©+øÊ0ÿcëAlcýóÖ§fØÔõ+BD™¤™*ïØ7»þLf\Nä*T±`©ÔÄC¸j\$Õ+o^ ŽÇOOä @;«ïv±ï=Ÿnä|x’kp¼~Aʸ†ìHw<úv$¬KG^Òf@n´ØKøèÒ*J/ÑoC¬Þ3#ËCøŠˆQbvë°JNðv&EU“4–>À4å JÞŧ]0j7ÁhcR*™<µ=q_²m´ºp;®â‡ x˼ö½[ñ 8q°zòö)|¢Ì²­ì¢_ Âç΃/2¤}j0ÉÓFWBöÍY Ÿðú1 ¿>Á¡_ÿ1Maä\Ðc½6‹©ŒoSó/íªK< ®îùè—®úÿ\Š<ÉÊ<ûÅÑ‘.p¸/iÖž©ªŸÔˆÖ›è¹çÁC•#†Ô>6ë!fcY1\Œã„ÁBîîuìf…ákí{!ðvÕ25O¹“ k… ~ Ðåçq˜º®uâ¨#І“Å ˜= U9P*ÃÆÅ68K I|í€óílÜ8¾‘>‡›G{©"¹‘Fåv „½pÔ’äßù ñ îmônàN«àgXV°/V‚Ñl} $k IDkÎÙßDôÙEäŠ×î² ™X›Á~o‹¼Š ¨î‚¢0rC•64“2Vƒ¹*”Ul‚{QàEtÌ- žìÚ’6|ÿ€ï5Âs†þæ+ÁçÈð±ùèóëUxhÖŽ_h]¶wÊD¿+¨‹0 #‹Š ت½1W°A¿à§þ—!JÕüÔÉ Wð‚ã–YWTéÐìÿ&/zûÞLÇ>*gø°ïl³[Á@ÝÈ¢KTï*˜ ®ŸÅnň}Wp¶uˆFããý¯’iïá+81bÚf?èôWÉ3ìž²j£»ãïˆR.§3x ØzÚÈ‹Áe> stream xœí\[wÛÆ~ׯÀ[©žÆ.înÚsbÅÉQ¢:¶L;='ÍEY2kIT$KŽšä¿wàî̸ EQRcûeîeî;3ØÁ/A Dú¿&§[Oöóàør«zì;.Ž·~Ù*ÂXÿ«pxr<©‰²ʰ̂ÑÑV–eåõª"ÈRõw™¹aR£Ó­ŸÁö0ޤú; ¦yœ N-¤~…HBYò‘ |g‡þj¡1Xhj¡³el&k0 ðß‚g²y'Þ°yÛp¹·~Eà¿àØ)Üî’À^xEàŽú(²[H…Îϣﶔˆ” G{[£¿vé|óBzBà!p鼄Œ`ìaxIà×à©4 ÐO;4iQ;…¸}B<€OO|tÞ@,O!f˜Á:>­t&Q:P(K*þgýÖÈ µ«©‚J|ñÿäiö¡§aj¼ ýOJ`×}QÃ Õæ®Ëžb·OT;Ñu9¶ä£ ")“j0˼€äaW2†”‚Å$_ì‚x]<àÎÄ÷ ®ËÔî ¸Å®pácÔ™gý};¨’…çÔ‡G­ñ£Su¶v‹'p±•ôÁ¸?+Ä&‚áû+|Š >þ¯á -ÉŠ²X¬1I‘ù•ËÏŠ²QE¹„+œC‚.|˜±ÅfpZ@ŠÒÃË4â[5{ôŸ¾AæÕg=Ú¨]A°òÜê¬y²/„-v©OõË_ô”Jsíl¥:’ò\¨42£¬Tr~Cƒí(ŒÓ,‹*¼Ò0Š„Rµ—Z%NËÁ±ú=2É¿Åeå™r=0Ë»ìôwv$[Hå{LõÔ8ÍS©TLe”kœ+Æšõ¯æ»š~;èlÁÕØ\$åà}ÇX;àZoS“‘&ÒG‚Ñv@{BxëŠ6Æ€1bXá¹3zzDcº,èÐe±ccÏicýŒóWë‹EXHiRd†Øœ2.98ƒË`61üÓvæEârá¤VrÎ_­0=Ö\Fàvì„c9wÆå„´í²;ò˜À©%L‘ Ê0Ê-K1ŒMt™2N:ÈeÚá•G—Î ÖŽgÄÇZed¥Q†-¯hƒó&kã,ÕŽÌs58¯K#q–Ês ç~J/lù$7Û² Õ¾¹ëfö¤WS&zR>kĵ)×¾±r„N5XD! Ìé CÕsäIMÄ0‹òPTêŠOŠ×èýoÿ•¼ºSx˜àœ¶» Z1¦Áƒ—ËËÅÁBlgIß,%02á€1$רbW>ؓÐï[b#NÍ¡@ô+ÍJ\–I¥¹\‘»¬ð†+h!›p鎕’“N%j0ç+‹ëK®«QˆÃ/Àyã¾#ȉanÈžšòÍŸq_ÕxýgàFËP:ý+±cÏçîÈÓ2—éM¬;dGÊŒêæ]Ù–à ܘypvH|òÉìp»!’kݱ¬]æèKì¬@Ç "‹gù@¯ë]™÷•“÷įBëqöÊÞ@¾&pÀ/ˆO dgÐ?úº‰ï…4ƺ,d­b¯0Ù|€+àwÆøMüíB˜ç›ãñfk®UžñÎ¥p͇+†ÞlN+$;|Ën‹›kóVBºë–Ç9\á°ÞãQ(˜yÖ‚úT0Vó-7>¾1¢–Iã1‡pT‹Oç8ÃXŒlœGô½Ð.±l@HkMîJ^JtÐÀHvTû!Ù™êΚeÇâp\ÔéOŸ3 ¿Xc©>óW;dçöVâœ\#çN -8Ñdt¿¿~z½ U7©‘ø¾éƒÔH¿Q 7©š˜…_5)éRÈ»Èonƒ<*åm——cò*»V&?"õîäx;ÕYÎ]»É–à=‹|Ž -¬":öñ(€ Ü@ë”÷= H:â‚ÀÛ¯·ÚoAœ÷ ®¾x›_¼­mìq4à[½|êi¹ê2L¦× mÈ{¼`Û÷ágk”÷ZO§{b¥''+ze¡Ÿ,Dµ£§Ê,D ãϧow§Šeê;>×Âø4%Ú½ŒôÞÊÂìÅ‘›æEy‚÷p œaOà%s9_5:È,,dÌ.ööýkêÞÐ,†»$V±ñy%ãuÌË„{›3³zÜ7ˆï83^Ò¼ŽíIúB“ÍÆ^6ãðÇ‹¦^í*Y˜‹Lί€ë‹Òteš]_¢1dlç³{Øø>;»“ݺ NMøÎú ^TÇ x÷Éü¹LæŠ(wûTÓW$iGWÅÄÁ¹XoÇ.è ¡~ÃTð{òÔú0†4y¯ú;- ó‚~?¬/òÇ"ŒŠÔøTÚŸÂF”`[áŸF2Õv›¤a‘EIG{fG¥Ñ[Ó@à ¾ëJ Œ´íR"èßmÁ¨™A¼pCcÒ nÌÆ^ÁÅ0éc®Ä˜%REB#£t>´êÇPô¨§Êâ‹tgµÎ(®•ibtæÊaˆiþ˜@¼Òæ4 £¸¬Žã¢¨žY÷r†0¾»4ú™–Ľj®ÊŸ1g–luvaÂõ¸ ½XG[™•¿ãð­9•?sžƒ(ùÜïÔ2KÓÎ7htMqÝ4ê$äýRM—Y$¸É°ýæ­FêÜÊí\šŠÛ×˼¨KÖú£–D¹0K³Òésq•}³„}Éd¯E‘æCëˆNŠ0*envÅ=nL6‡^2d(¤Ì¼dˆ4Ìê@ÑO_øCqfZ/Ⲋ˜7 „§:¨ÓÍ–èeó#Oçþ+1óÃ8W“æÊ—HjŠžû•=ÐgÚç¥ þ9i;Ò¨¦LÉ)%¬oÀ8*³Qhìñ›…þn!zG@ÅÂïw°ï^g-¯}€ÌüS;‰Š‘‡ mà¯51 [ì›uŸ¯¶n¯W=ò’ž²/šýÑ©Ÿ2SA‹ õJ¡Äïvl2•cŠØjn)AVïI]ñ,jf¶Ôròz‡–“¯¼¬1Åš4×G­TΛ,d“6ñÂB?ô¶%Ò–8‹$T)òÔ{ÅIlRÄ9Ș #å»—7 ûä/$o°í¬©¼\ÞBF¡ŒeËRrØŠGôš<1áS+ýr³HØ;V; )ûÆ#Èbƒ‰‘Sä~HÀ =ìü™ã¤Cv¬%›!ÐJ-4ŒN´ˆ‚ý]¨3Íh1ª*$·×®:äR‚ŠzÖm 8ýd Ôe€L¤N¼Ë¢Gœ~Áþôc(DÖdOQã%t OÅ'¬~™H(!¼ÙÈe2ü9îz@ »‰£¦Ò˜¨‹$¦¯,6äQ¥—­/lP\ýŽË-‹lY¯ÕeËNv©ô Î çÓŸ¨¤5EvIe² i×Í”´!d½ýÌüeŸLÍ/àW#š œó‰K0VÅž6DhοSÁr0;ÔØPTš0Ä©Iƒ‘º°4¸YûÂ~ž ZÆ»á*Ðí+;uIC)Fas²®¥øºWVþèú> stream xœíÛVÜFò¯ÐÛjv=rßÔ’rN &»dMœàñ^Ž“ ÃeÌÀÚÎ×§Z—®j©4ÒŒƒ k?RwuuUuuÝÄ»H$REÂý4ÀÑåÖÓƒ,:]n•£ƒ¿ÖÀÍéÖ»­<Ñî_ù€ÂG—Ñö &JIa£ÙÉ–HŠ"Y…UF6…ß‹4ʤHL4»ÜzÏ&Si“LZŸMÊdñÜC@:µVèxg2U‰Ê³LÆ?Nt"laLü’¡G“i–!­=ª«ÒJX/ID¦³zh9à|bX:ëH•ð‹)ú†º÷:Í5]à]ö Çž;buš¥*^ËøQ œ{`O™Ê〤Qy‚d’·tm?v¾’'©¤x—S•.(22 HL×UAñá4ò4`«Çp¤‰Ñ"…Mþ2û~Ki™À+PžÙ1( YlŽ üÐ^ÁÊÔÐuïØÅ5Ç” ž{•d[ªÃslýŽÝ:[Ë» ¼åHX6°sxˆF:º ®ˆ!€M¨æ)ä÷MÆ^x.š¥™ýåYë7(Aó>ËëEµMm¨¿õ,·§0OTî­’:Q…l´çÕdª“4“…Žwœ!M¬n;ÓÔ“äiM¥J´­PìTƒµÔ@¯=yèoÚóÐ+Í<ôÒCú·£¢Ú ±¤Ö‚qÎRæ$²B ¸¬²¼Ë°›F-æ´#uÿ¤{”ib ˆì³1£IÀÂ&`'2†!¼ƒQA ¡3jß™GÎÈ7@`€—µ®Men›Y§!Žx<ûOÈd¸çòhöbköç7%C„’‰¡»ßeø€œû<® eKÙ* 9S”XáY¹@pŽà!‚7ô\yð Ás—Þ"¸`‘}dñ¾EðÁœ3xU-S°&æ‘ÌÄßzHB$Ëuex<$C–«¼¼ïG²Dê±*«ñÒ ¼¼ú#‰@ÇOØ%xé§ìjg,²oÙ§Y£½Ò¼©ÌŒZá<˜¬Gè×,ïÏY~Þ²<ÚAðG_ƃ¹ÚJþ%³â„Þ‡ˆNØi/î Oùcw;Dïœ]˜àõ¢W”ôuΩ‰-³Þgý½´/p§Í" šªFCS ¥[—Ën.À½þ€¹„ó‰Ã“CpIFÞ¹éYþ!yH¼Ñ ŸàÃßS6°:£î»JBû05Ða}¿­*ŒÍÃd“ͨs= 2¹ˆVÜìq…,âH+š¬Tú§A;áÂÄÁaÆ9|.ë´‚HLnŬãl-¤×°"©ÒV®[9¬4‡¸$¯’Xj2‰UJå ¬%ðÄIJç9Œ/’°¸§Ê‰6¶ÔSpxLVr&‚Ä2¢ ’«zH™**Ÿæ&w—²G}Hž;é‰D¥¢ä…²ðH]DÞo»§VJ:_0¤NJ'nD‘éBä<' 6dë*v÷Ž‘’z—„ñÂ/_-)…NãŸ'5r“³¾'¥˜ZS¸¬-À¨ƒÏ¢@íÜ!I©8s€'ÞrÇ€O£Éÿ”¤Èi?aOð]ø»"·Ô=â~fû¸®“T|?)œŠÎ"[¼c "¹O’à!1÷“52×,gæÕ‰— 'Ú'—=IIo'ƒ|¦ýûSL4ŽH²ryáe™ë„Û¢ />»HH$©‹õR@~ì]¨]. ^'Á ñ[#Ì!x1HhŸ %á†g¤£V¥²¹ìX6$+S:žLkÂ)KGÛZvàš±-¤ÑQ%wôþ‡ïqRþV^#¤îÍû½î±2âK ‡¡ìé(ÂôËÿ€ ­sÐ>ËmÃÒ/á÷®¯õêKÇûH#•;=ŠJ¬yiFÜÛÂ; ëzJÛÃ$ã®9X—r€(ljú}U€ÚÀ…Jsèã3û„l¡uWtk#n ÿ´º1LèZÅ&¾ Bð-kLßWe@[ä=Ž1ŸÑåo—Cvì ÎT=õÝS$©ÑÒྃÜreŒm’Ë`À+̉%Ÿ_~ïÝ…‡nøõS ¢K¾ž~K/w;­c¤ µåïœQ¶ÆÅ΃¥9PY¢tÞ˜ò~£8Ô¤yi6‰C5‡¾*0c38]$Ü#ip·ƒC¸È’¡4<Ä¡4z¥ðŠÀìiqrWú@tÙ.¶¬ˆI×-Ñ¥Y»Ü(¹äµê•·ÛÚ£PO¥v7 3þḠ¢5ÓxŠw¤D\|Џ2\!ÇbÂÝÞMx\ÿ8s£¤N;9%&°Ñ-ÿ‰ ŒÇ9Ž|sßñ8>e’äóRIŠhêxS瀤9ãC‡ ¬¢„öXƒ„Ρ隣‰³f¼Ÿ3Ø3T ‚ç(¢ŸvÚžŠá˜—áj þ7SÍóiuù®ã/]1ŽÃeÇ]SÚYúYçúu<&R`'ÉðK,,ÛKã•4. ë«ýÀ”P°ÌúSÇ3±òq¨rá~`÷÷Á;fó¶Çúq/”Ã`•‡k @Bä5‹!b§¼ïXd|g•È?Y¸é£nú/Üž2ìúB%÷˜„l¿!ÿNv:{´Rξ)sG{&°SûžRZ혬!Û÷ì¦Ï‡X¼aÓïò ¶6,Ø%¢!Æó-Ëñßî<8/ 'ÎY.ØiƒQô5ËVN±?I-Ñ.Ìœ•—ì–×q¡øþä+–í¼¡½w˱º£Ë|0yh ú«ü­v¦c-bû-*üË™Ÿ…dD™ø¬Ù‚åo“ïÁSÜQ:lVÜ1lqg¿ÌÀ*Y üD÷uT’©U¤2sã0ˆ¨*q…–e)®)¿ W~|q† Ú¼ò³¬wT~íW ØTšýI]“W6ÎüšØs(È*O:ŠºƒM€Xl’`Û°…Q)XWmZgbš˜ç\kòÂÏ ÚxË RøMSÚ×ÁÖ”\AK„¯Y³mÐíf ¶ Ý|¸§T·äÑÂì}€â ëi#ê«g ëõ{Wå"1B5Õ¦u>õÞǯ"±•áÐ7Fê|;ØnCp¹sØFêƒd‰ªÎ¥ó šÔ©J… ë\}ç•Æ–‡ Ù1¾?~@a3XMº¦wMª›ž»žA=›–8Öv÷o-©óE߀RŠ}_A[b-aÚ–xJr·A‘ö$3­!f]9þ-÷ñþ,ß-É·F7È´!-ëºß¼þ "7)™4ø}2iíŒ<ïã¯ôTŸã9]Ùmý?¿ï=Ýendstream endobj 45 0 obj 2993 endobj 49 0 obj <> stream xœí[ms·þ®_Áo%;åùð~çi;#;NªŽ]'2•dÆéJ¤%5%ë%vò뻸`ïî9I)š6ã8ãYã€Åb±Ø}v~¥‰£Ôÿ©‰“˽g‡ntz»W4¿©ˆ›Ó½{Y¢üE§O.G/f4PˆQžäv4û°—&yž¥®ä*FÖпs3r"Mu¸Ü&³ÿЙ…1ô-Í^ïÍþüž¾NU*E¢õø5‘‰qJßj?P_jTQÒŽ§¡ñ°óYÿ¨:ÔE –ú{dMlD&t"s.##«ñ"Wã«HÎ#¹ˆä(’o"ù2’³H¾Žd 9Fò-à+©ï¿gÿÜ"‘®æwD5‹‡¨ùÙa>²q2Õ: Ö oñ—{ïÇr2M+¥Ìä8ñ´BKíe€íf2Í“Œ˜/›´y¢´õëð»«óš/}³É•^Ù?íüî‘\NåiæWBpÅfÍšf¤.t(¥Iso"±i.…W²Èál>þu"\’+áWç{Y Ë2«ü6…æ«J:9>©¤Èt6þ™Ñ#¿*“ÑÆ?™x³0’ ZIDzõ{R“?M˜üõV ;&«mb³è˜è”YŸ?½Mœ ýMÈ ¯¾e FD)cmZ’N¬Ö¶P)±Ô&5^RIâ;'¼¤5ɆÝEòf25Iš 2üy1ƒ’©ó s^Sn|ëÿÂCÏ'Ú÷Ï‹ —:IªÅ,˜°Q—ØÅg¢é]jL5Ò/… d2­8ç))Œ:7„fK9Ž­qÖRtÚ§¢+mKÖbP·†FVÔ^ñ—*çZÀ°LØR¿‡x1‚·zS’R%ÂåÌthk5Ó!ŒœN¢T7/}*22ýÀˆI½lnpÕzU¼€ÂÜÅïK¨Œ_üÛœçEWe2RkVL„« ‘Й„gQ‚9TBÖٰ ØrHwŒ[$ëpÚ¶¾Ö*; ³Âh΋õ}^L¦èÿÊ$¦•ML+—±ØÚ¬Bè›ê²ËphS\O8¼!ö·Í9Àhz Ée$W‘¼‹äm/ä ÿ©­ƒÀmn¡FÉ! Á¤=_¿HY£œ–n>À t0(ò+7P¥§P²[¨è%`Æt®ÅEmêM ^m¡mFé!m/ È[èêr`ªø;`SALÛì4ü¼Æ¬¸BÈ`àO‹6ty>àQŸ¹iÑ>xá{_B‡«fëÆ?í¹÷žÊ8#+à—ˆq®yæ|aÂ9_r˜q£G#V ¨ÆÈi„&Ÿ&yâ2­‘›±º(…ÉéB‘yX[cˆ>(Waƒ‰T…e•Š-² #ó$Ý1ݰ0Ý8`=V1õ¸«¸Ò1ßðèþÆ3É…T†H–X£…ßüØ' ,ˆè¿?©)¤Ë<äòì(:ÓÑá)IO:ñ¦Ø[)„ªsŸàì˜C¡’L7r½½h£rÿ¹s,|#CÇ ËDuüç‹xvK°KèŽÒãë8(žúå„ ÒùýÃìWðÔ¯Io’l€¿€Ý¾÷f@ç;•ãý‰ò ¡ØóclÄ„-“ð³ €ŒØ;@ yg˜sM• E–Hè Æ_ë ÷¿@¯ý\ÉÔe#ó }‡ž ˃2ž/ ´Œ‡íC!˜-œ&Ö]a0äF¬É¢ÉHçÈý¸"ÑZ'}Œ¹ÈûnPó > TøŽÖöú›V>ä»>o‡¢€~ ,ô% êÉ‚zðyDå/õn ]l‘ ­%Šð Öm¬ÏÈo–÷öÅ’Í!3\=ÆiضÞob ì~3Òß Èÿ‹=Ç;v?4ñ“¥È-åÿ ¤Æ}ÛðhGoEý<¤˜/»7ì@ã~ÖEMŽ£|¢£ agîÎ Ÿ©ðz7~„û±\¯Í­ª_ÇZÄÝ*| 6Ä<$f1‹@ÍÏE…ÌܦC­„ᤡó]À÷bxë>ch8uM\ΑÈ}‚¥+‰(ç+/¦ê± %§k³â@ëP­"M 'Wgj-×–’-/ LôÉëú„  :[eöÜ®¯=2òœº5ÕÛJMò6y¯KM%Ë{sœÔmT­ª3‚tÃY7*Yuá>›ŠèX+Ìn©n•æFîX·jY†²Æ†ƒå²JjC™Ëe•£d‰¶¢6—Nኒ»EáJY™»[áÊÁÂÕQ¬õ°º¯.‡Ñ} n›7Õ´ÈÌ3ÞŸPJ–{=.Š{oc…l”µ®‡;V„Ú¶hu/Õ_T$-*NîÏ}qY/òô=5+¥ÉXÍj»ZÕQ¬HG7]Ùqðè÷á+ øè. _8츳ö Ô^šœX‡X(â~“•ÖM9sèÌÇD?¾u-{ƒW8r_…ûÅgCæ—z ]3Žv®h0ÿR®m’ÆËïeÜœ*D¡ËæÆ‚Ký-„„A‡Ù¸FÚ¼¾²¡ ¾w_õ[©d—»ßødêU¨ògTGz¨}ÐoŒ@w«/A[„Ê_ƒ$èÀù°#}/¸Æ•–°Ë½ gq¶Î /Ï,W¬°;\ëOá7P\Ö@³ ¥þmqKÑs¾ßÝDüÚ;6¢]c/ñ 1N ÿ§ÌEýŽæâmDY ¿©+=míQX,ŸÖè¸ù—»¡WŒk} ¶ÎÉöÖ…}µ9+¡spÊ%ÎÈP’Ž–ü`G?LlšvЉ©ÚjpÒŽ-þÚOz‡,y,ÆÏÒ”蔩§––œçÂëpWÇ×·û™=üâlß‚±ñ¤nótc𴱺Ä6¯4"ùvÀÏs‰ë·ÄuÙk8Û ä‹%óá_žWÊÄ9Ëž‚É6¼%hôšó´uÊwy_ÂÀ:¿7œå%2ðWhê ;—=7Ãü4ï|5\JÙzŸúHîô«PzaßÙ(ø¸ð•ÈܤJ´Ì]ý²µHýP>ÔÜÁ"ƒÉÊçšÁq‰Ó)¥sypÇ,M,J, 2py1¼ÛÄÃŒãŒëCæ‰2”ß¿¸nÔm˜mU¾/¥D+|ø²pA'|ïQq2.±P-[H¾ËÏÛ•“i½ÏCɇ}Ú ö=g Î:üè4E6ñG( Ô*v\{ëëÝéDUìq ¾’ÄhmÜt_@Ü<hÈðöÅ G›ctù؉ Ì™S±!»xLîd>ÓzŠiyu¡­@WèIÔ<Ô–x9Ý{ ò"µSª· o_ÔDU#ô¨è©" 4-3ÑÐà-¾’€O´bküü¢rÊŒ÷3ôÊðµ ×ÊX=†Ýy”Í&B´’„ܘ¦óE®?abQ ñÈ;ãï.® XL–_"¯s¶)„±¸Z=›gÍò[„ èU/ŽvL]L0fÔƒÕÅ6@-#.p.ÖGÑ¡¡þò©¶ þòéS¼ñPÄ úxœûjÖÑ…§–奊r¦†x¾¦~¼Ô†°"î„+cWNðº*–“~|@TOõ,P¢óµ Ñqp7XãÂàg™X"…¯Ýq.w ™axضâSáOCÃX€Åñ—£Ð“‚¾¸| 9T)«´I&÷XO!ç ÑÜzuj‹ç+šž .¬= †Aä6Èw`³¥Qì?v@©¼¸©¬ÙíðJ|ÃdÞpF1­_{ Õ÷û-r×ÊOñïOB§ÖãÒ/ÏJw|Vúj¶÷ýù/\GpWendstream endobj 50 0 obj 2826 endobj 54 0 obj <> stream xœí\ÛvÛ6}×Wð­vWÌàBdûd§™™t%mƣ̚Õ˃#ÉŠ§–íø’iûõð‚s@mŠ’-Ù®­øå˜€½À 8Ÿ#K ÷Ó£Ùà塉¦Wƒòqtø÷Ú¸œ>òX»ånfÑÁÐVTyTÄE ".Š\˜Ê«Œ²Ôþ^¤‘‘2Nòh8ü¼íîi¡ìïÉÎkÆ©ÑÉÎ{o½õ–ôVR[*+kW™fjÿ^•d.“X¼è‡ª€,ôΙ'd~$ó†Ì+2#2ßùžÌ·dJ2èávçšÌ ™—Ðd¼€"Xùý Màà¯ÁSe›øuøýÀÒmƒbøv0üúV„§O‹ðô9žÜïìiñ=¾ï4ÁÍÓ"Ü<ÂÑåÉùÁ[óÖa@X&L,swšGÔÏsñ1ä` ¡`°A(NúZëå` œ©àQHôMºW€è6ø5„€•=½#\]qº®JÿÚ4J¸‡8¨z³Ïf¡é˜Íßm¢1ìÿ ¯ó¾±2g£‡³g5¤ì8 1RRÄFu Kë¥}¨iâ`i~‹ºÌö¤06‘X’;êƒÛKÍ t†Sê),ð',{Ëmîd÷SX³cøŽ--ûƒ÷+kýLy ¢g€t,ô}óÎ[´0lè›çvùŽ £=…¨Ì ˜3Èþè 'Á1ôÐC]_f'hÞx‹ò~HMËÇÉÆ*Ëþ öá–}¯ ¢ãÐ[ôöoðV„d­ëkì"…1Ù8hAbÙp_Kùܰ”m,ï ¡znªÍlýÜ Ôw™Ù‡ ±LÖˆå8¤3Þ Ü¬/`k½ú<Ê ìðõ1¾˜—Ö¡å–—•xñg¤ëŸ0­ÓÅ-1+ã3WXWóÑ:üÛò±þ¬qi1ŸŽb‰ƒŸ½%¼õ·æob$;¿†TÌ ×·;ÀâÂD/÷¬Ú§{¥ë >ÅaxÇTHú}ðÙ‹)N¼Uô’ TŒÈ~øÕ5SÅîo‹YCóiÒCðl^\êÌ—Ê ÷†€Œg­SãjHë›yH,¾?Ÿ˜€¬6ÁÜËÃ"’*¸–šëÖµÔDÈ8UÕµT ¿ˆ3¥T®vbgk)U.…ðy¾»WĹõ"]?UVÄ:Éß.d¤0nоâUmçIØ–'`‰r4Õ_ÒçYšÚ´!³¸H’ÂÍÛºèQ݇¼pÓ¶~xíêF"w¨x_—¥­S-ÒvEœæ¹Íï¿ìTu•14Yj‘vµÏs“4ÝÈ\îô.^T¸U¢OvèB‰XÚ äB2¿ìÖÝNrbPʾ‹Å:ÏÊ :ØJÃÿºùê­æ«¶%cµ»[‘ÊŽqâ­ÈZ:Í2¡mGE,²B¥ÆñÖ°(™X™eÜüèßß%¹¿Ê‡V6 Æd•pè4wYÖ?½¦jzjÙJ]ƒvâ·;–šwaem ëÁ®vc¨V˜Âòã}½ ºÁ˜;ªºgBYv¼GNÓ"óPvìd7©A˜¹Ã®X;#n¨î©Ô7fæÚnTmÖ³P%;ÿ°n-ÇY‘»­o`NÕØH¿re¥.2Õ0¦ta§­ '•¦±J] Çå5ôE hSbD!çðdåiô¬½¨ì¦‘v®²1³Ì$S*zF愆<†=`PžSYÖVà÷Ûû{?|ö~L-À‘ÿfßÛø1ºk¡°&_vS~M¯luðWůÎÒ¬öZš%U.¯– ¦¯²=¬²èu†É-F¬€Ís6{MxìÕ)ÄÅÈt „¥šU¼#;h¢sÁœVÀ.…›F§&UpFØÕaÆ™ yÝ,*^û)ÏBtäª~âùòe†¬©9 $±ïÝD0ÌZñ"âã¸,+T¿ •ñª 6?Ø‚Á¹T¢­Ë~S•ÕB¶6óöׯÂ?yq+#Ø!žùíÜ‘·fÞšô}_ÍëÝÚß÷²ÖŸË{—7g‹wQ]:ï—¸ô_PÝÞÞØGîÒ7µ·—„—øÀÛÞî|IÒÝ_š®¿ýX¬éžÀ1bhzï²®ñzÊÃÏg’Ý_­À Ê4„?ú:ÍÀÀi€Ñ„3ƒOøíÝ÷µ†'åÏw]À.¼ú~»LÊ:z Ÿâƒ!V` !|x {“G¤<¦Àz²GËÛÜöG½ÛÞöïf”„AF6¶DBÊ`‘´”AYØ´@Û•™±XÇ»{IœB¸p5N‡,%PÛâ%½ÍÛ—dþh j!DêJ6¿ÛUq–;ùlÂÜ©|¦åwzStTHufÞö½·±û¤Zif]º‚…Ô" ê_ùÆ®ØSÖð™÷0­¬Rï]V\•¹õ“uˆ«Læ8ó2FõÝ­‹r÷P ¡JZ‹¨ìcþ ™/ÉüѦš8KÒó@q¬=\@‘aDšA[Á ¾µ•&·1 íJ(§¤¾AaÚÖ2ªcÒƒo!h×›7PÑT¤¡œÃµäœIÅ„X²ØpÿG2ë|}WÕgð^T"«²aœÈzª9‹äÏ\2.1ÎDºƒeJ&C²_Û=œÀÎtI©(„>¶¹mwŒµÆ<0žy<–£ "æ†T¯ í“ãçÉÒó9ì"kö´~eRû± ÝÁOÜR ´/ÆFṳqš¦MÌÓûj0i¢Üúh Ðàbn’ä±RI æ’ì:V÷â ø¾KpEiî,„Ïb6¸à É3Ïr®µ ¹ôÕjRæ±OO­IÄRe;Ÿ¶Sg õúÖ¸.ÍŽ$|4°YÀsúu;»¢ Àé.š¨»qnZÇÚ4ц§Y9Ì«¾Ôñ-䟷àモ$Ìv¯r¾†(”`ǰ…1ì É/7cØîÜ4]02pAÑ!ì޽ »µ>?-I'Óqaª¤“ðõB¶uj„J³_‚çKtRÔ—¨è$‰Í’ùmZ騴Ä@ó®Ã|Ž5i' wˆ¦·M$°“;ƒCZb~ÜrŦ©â‡—ì® V­wvQÊ Ø#±fç öšj´¦U»òe£HÜúÉ[rÎZ憳Ny‹ŽªÎÔJ+;êû,dߘŸ 3|5˜UÃzaÁ­÷úÖ*9£}ºÝøEÿ Ó*Ìn/½E‡”×íhèÆìîñн Ìzö;l+‚k%ßïõ€—¹¨Îò"£ÚL"TlLÖd¡eãÛ˜¢IH¶ÞÁÀ#ñl>ðJ™¥¶P µ ngÐn#õñEª‹mlÀÈ‚n`­³+gb©MÃ{u–l…*­Ž¤øÒ_ÒyIY§ Euß/áQ) »mÌ>®˜-5ËÜ54ï1fë>àìJÇ=tÐF7<Âÿ¬¤~8Fqõ[PœÍmô‘(Â,ªókÍt´ ÆçëáàŸöçÿøŽSyendstream endobj 55 0 obj 2635 endobj 59 0 obj <> stream xœí]Ywܶ~ׯ˜·Ìädh A²ÍÉ9¶å$nÒØ±•¦mÒY’e7Ú¬ÅY~}’À½?È‘¼œ¶MO‘ –‹‹ï®À¼Y䙋Üþã §;÷žU‹ã«öñâÙW}áòxçÍN)û¿ö/œ.ì™e½h²F/ö^îäYÓÔyÕµ*º47å¢"+êÅÞéÎOËÅj­riþ.–{¦˜••*–õ¥ç¾ôЗžúÒ—®}iß—.|é¨/Im?Ñy•‰¢â]¿ôU_ûÒ¥/]nú®E£Ü'¶xFÅ+*^SqÖ= âY¯` W°îK8²ß`‡°…}X/acW°øì"ÙÂõê_{ÙQ•ÌÌŸ†{ö{fÎò¤-꺩—çp×"ŒþÇ]±ªÓ…í÷Þ3!<¯Ý(Öf˜q˜:{ÿÞ1<\š?¿ÝÙûô'˺B›:Zš!ä¦ [þ2+d-–§«µ,²¼R•–{ýzU˜’j 3÷ìÚ”T©u®F*®M·ª´ÃŒ**™Wv‰Ú÷µ]-ÿÔкÌò\ÈÆÐÚ?5´îÿ¤}}LEÖ×­N½œEíQSíŸl:¬gö‘iO4†0²Xþ¾v[J»È¦ùJ ŸZÛ kúU7-$ï%µ!€TM¿:ñ0^R—Ý ,Ùnbšqš×ft–U¤R™,‡²a½¶½Úµô©EYŒŒïÕ݇df£Š³oá3j7 —Ÿá+Ø«Ë#j8.§Æ‰Ï–Šõu¾B\¸Dbí_ öìfªêp$˜©®áTX§»¦…LÖÚÆ£•2Må°òÅì§(´æÅ MÄž‡œ£<¼]•–ÊeÉWì Bˆ1^Ahp³Û ];œš´×-Z öºfƒ³a`ö¦0&br ˆ gï †ö_€w¾*Û W=sY-® éDÐË4¡}¶ˆG4ìvu“WlT ¹0á]÷OËóº°+À•'ì3ªû-µ‹¡ÍÔ…b±ü‡ß͸êßWëfùµýõ6è*|ßo§j Wƒ=„/«ë”%y»lwø™”|á»%(¸ÄÅÆuâ«Ré¼cÙd•VŽY~]5a 1‚6 *›¬®C pfפù×7]¨&fÆÁþŽ=ò:Ûxlú/âg·k ø¦K«ÃxzMÇé0A·lZW•N?±|?m»µ˜I]µ°ëpl¿vœ[ü#¬)±¹`Švã k@—޳ŒL+= %õÊÁ"Ç*:,f÷aXÁÙ,:«f·Ù,'i×ýºš‘³…j‹Ç‘P 4“ ˆÏ†Ð2ÿï  µ[AÚéÙc­ûfucìfÚуޘx°Ûü<Ú¦öõQl_Sãw)ˆÛ=„a›}p¾Â{‚k–nדÚÁv:Ty¶Ðû†”(Å“º3ƒÓ£~«×™’dÿÄ`dÅh€~{ŸA¶¾Så &¥SaðvƉ›B~ØÌ”aEoô$q¦¿Á¡`c6›å.‹Yð•‘Ö±€Å/IÛëõe0Ek`kEW—´…ß[ȼaŽ– ÿ0ûöV;; HU¡øD–ó˜øF[.°[€2‚f‚ò=\pÓјDãÚ‚^ÜuœœKÍ»ÝÚá+<õ ý.iùîa±\{Œª7 ¢…=ëbU»B˜–t78²X£b³åp” VJ÷áÈ™G’<ßX¨¤rømRf¨‚»|î×­Hq“-4&¶²:®û‚×r ûèb·U£’6“,ˆ™3È«{€¬¾Ì²avMï…¶Ôˆ’®ÕHÝ«H/bPº…](9ê£Þjâ‡Ð)jYõ «ùÏ'Ö'_×Î'ÏÆw†¨ÎbŽÔäƒ`¨f6¸m¬{Ï÷e‘£ì6ά˜Âlûv­óª &‡´c¼g›œGk„å—·u.@Õ¯NY-³ByoýÈ[­l YÕ]ðˆ…@ª#¼üœBN¨¸#dŸÃÈ –½…Q-JüvqŸŠ»°ø,¯(ÍÆ¦˜¬}ú|¥óÜ’m4ÒRN’#2a­>Òl™P–Ãùç8?n!¾†.®9|‹ ï1ý,I¬à-ÚË®Sº$(ó½1k<¯2[¾)C³Zö*Ù,ä«gT‚éJPåá¦éÔ0_´3…ÐBlÈ?šg Qœ€§'Gv)âö˜ðÜHŒŠÝØ%Îåòø-µÚîk]5•ÃcÜÅ,šq²l“àÔrWa& ½3䆔ޱ¨KD§Žtr¨EZ``·<üÜ>k!¡'£úŒ´[Î ß±Ü&°à@°J5 ðqkÀÁ€”¡¿å£T5U¤Uµïï{I•ÌLˆ½ýU& U@tBèñå/ÅIÐ+w;Ÿud"%¸¸mëò!ÆŒUŽ·õT÷MÞ|6˜Á”R%¶E&ä¾ìñ*ÏDî­—Á¼£®Ù/½~Bcd¦âW°‹ 3êì,- ½ó°K2iÙÚ½3¢[k‡wg­ÎF‘Mö@€"H<1ÀÅ9ølÜ-¢¼Lµ©IÆÒ•b[Ó³¸‘-'@Ec¶U*˜iù1f«°Vz‡w.ÓZÏíC'BèLjGz<€:¼úÃ8¸o?h‘ÚpK2ÂŽi1dÙœ¼=D•‹¥ËD.d¦Í§/£C— –e² :x r÷–‘Ylf_@Ö&(£ZìD·UæÇÎ*ék¤ÐM*#:æyÔ*àÁhlï%CPhï™7™F©_íð#Œ.>¤ðãS¸pÛ°vYǬ…ûpgŽo¦Æì¥¼¬>Œœn…ó{8Í0Á‘‹Œ|w`20XäÕ ³·Xÿ‘«¬)4pÿ[üIg1ƪ˕µ"sQ/!w£›ÌB,•Q(hìeÀrk°Ã”>Æ¿;œµýó’>ûteOüF3ùyEOƒÝÔ9D&ò>‘½Lk÷Œ|PKppFñ6qèº~‹²Lan)ý_ÅR0BL0ÌËŒÛÒ´S%ˆ‘e÷µ/=!õêG ,Ý©w»Ô8 6¥Æ)RâNP2š­fÀì–¢ñ=$ÎÌíq’zCnÏûÉ2ø¿cÂŽ¥3G¼?ö–‘¢ªIâ dd¶‡ù&û-`Öøˆ•Œ³!MH½{‡‰…¨—· àèÆÀ‡j#cŒ'„&bz§5×g˜>Þ¿ýݧ+7BÇYB¥\ºkjT¹FXtÛRSD·-h½‰ö¶%¹Z発2"(³î²Éób©ŒpoºSÉkU›ÍÕjǦ=ÕTeïG*·Š†j·Li4–¶$½Ïš·û++kÝ^£³¦Òíùgªpè[XÐH›‡M¥ÌRÜ£‡OÌðTnÔeSÓ½Þ5Z×6€Ñ·Yꦑ¾ý×ÔÀ{̇pÕ—KUzŠ6  üúªZEUµ±:5'¨”²¶ArSVÂXN–¢ø¹°ù µiÅš·k©›LºµJ27R´*£A’¢êD€-Ê~œ¦±¢ö¬â@í± ÛGY[#ü1«BÍ]øÒ/]·ëmT¢¼mÀeµ"‘g²Ì›ÖàîjS‰÷`u¥®.‘“õÈm`ªÖ®3ä\n¼X)aã|Fí´V¦Õ¢#:(ωA ¼"‰©âXù|Iûu?B¢á!~®‡ºAïÑÉIVdŽŒWágŽ~Ì3Á•ÚÛyP…±°HBwÄ!ÇÀ´˜ˆ%ÏÄ4ôXgÅòh³™TM8û/?¯hOw˜¸ª¥ê2ÊâhZòš*<°Ž€?ujLpö¿#qlÂDê{æZ±›ôN½b}¾A=/â#ýÌ—èò¾_ܾ¨ô¯|‰®¼Ï@{Gà‹+8V6UvÙÜ Ùes© L5fj4¾ìï>=€íÁÏüízN}Ãí.ûÃsXÐjàºî"j£xȆ‘–ñx°(æ›5à ¢62áè-­÷ ÐÝf¾8‰[',ºQÒê—|i˜ t ÖNºùÚ§ç°7le± Œ=^ÀŽ“¬t»˜>yv}ã€i">Ù²x©tÉxǯtœzçã܆AÇìé œ1ž|òúÍ$Kw«:ºéüÓÿ¯mz¶Ó‘@¨¼A2œ–|ª¼¦o‰›ˆ5’9‰+sv:Æ ´áÈ\S\²ncÑ‚׃kE˜AêR|Z+­ì³(€ÈÞÔ¤¸Òù†A ·¥‚¼õ<ª¬ªk !m¤Xáë&U}J˜7Î'Këœ{[à}p(IxŒÊ8ÔµÁ¬t·ø¸Î®!)bƒGR“çFϽ?+y°‰1îŸ×ðüWÒê»J¤^†ñF«á <¶j|‰¾Íž€›—‡Šò[™~›®!ÏB8K²7Cv¸ÌÖàåœÓ$"‰™FžuöÞ Æj˜) ºË_{ôÑ™(5`-L²?Cô`H”LkaíÂëè°ß ßT6? &s6pê8ÎE˜p]ñRøH^×XÚæM¦õE JìÀ›“ÌÂy•Ì„®·Ð¥ºCðQ2¼' yž/<ÎÀ¼’´zã>ŸqwBÑq¥í#ôÁûQ³ÐÛØ5¶0ÁƒÍ‰™q‘ãûÞøÙè1MyÂÙÏQ–òU›ƒË…#]Žåº0¦¬§pB!:8ôÀ™+ bîsŽÂ±kNX ^ÍdÞùcNðÍê`úüÃÛÿä@rõÇŽÂù9`©Ä4Rˆì8“_ŒÝRo8æýéÀ¨ŸbÊÓÏï÷çé-@OÀ),AóÿzƒÍúÐ#<ð£¤tî/C§‰VÙ9¹Zl°¸0û°¦Æ<=øg°¯è~†¥ÿÎvržÂϰã’-Î šçÎÜ௢Ü{Ê1{6c„ÿJCW’Õ0'aZ°½—ZQ¼v7ð³äO/1×ëTÇÈq™ <öTüΗö¶Z¼’Qìm½„„»€-`žÆqÉ™ ? ~Õ*^èc@oÄôtåÙ täNR{¿·šjð™Uâ\™%²œG–™ÉRK*²4E«ðMcå]O›'€©TÞ,ÙRT¾}&F} Nø³÷¼NÕÄÅ…—þ¤?ZPÁHÂ(ÍâA8Æóá–MOßS®èTm)'­§À•ò_ÉÔò3Ò²Å9…TNFèÎ!Á?†x2fÝDFŠ[¥G{;ß›þi~Êendstream endobj 60 0 obj 4690 endobj 64 0 obj <> stream xœå[msÛÆþÎ_o¥:!Œ{Èd:ãÈrG©§2›fÆÍth‘’]I¤BÑmÜNÿ{ï@Üíð@ );êKœ+`ïp·»·/Ï-J²TÈ$sÿP' % 7¦ÈL*´áÃ)Ã(¥Ð©¬8ëû-ƒ¨ÔxI䑈ܙ@Þ‘7Ä_»‚ób^¶†‘k"ÿFä ®}âÏcø¹KÈœùd`K{WÉx¯!ÃG"ßr©þ8ýf¤ó,-‹ÊÒtníf7?÷¤´s£&~ØÄÚœµÌz,6š}H&¬ûGŬ?šìJˆÅ”ü®a Ÿ~€_»f+û;´lZÊ”ÈWD¾ òœÙóQ[ÿÏΫDÈÈ•ªåŽ £R%·îÈzŽ,-¤”¥§ŽVBh©Çªç¹å¯ÒÒÎ"ÜšeQ¥JãcëjÒ"«¤p2v†§q’¯IéD\ÏVê20H'í0ï‚q$îyiçp°ÌFí4ÖŒÛ8Þʨ*+Ý5O£÷"KežÕŽ/|Âþ0n¸§5w^é¿–¢–¬cօȃh…òôE^¦µdí˜é_Gö æöϳÑô×oê©…´FYBÚ .•XJåE‘9s $½_™43™(Ç·VðÚÒÊX#Ôöµª´¶gœê&¼u±@XZWVþîµ’™±" ä†îzšØJLó4Ë„ 2lŒ|u”§ZæEY‹Ôó&GÖHµ,…=QUjJm¿»¦×ölû “Ë®,êÕXƒê“ÛÛ5cxG Îìä¢rò­ù:Yz¡[ÃgR¹ ïgPTl1k¾ûÀpQ›–”*µ3{_ÏF-£=ûe­Ú+È­!/àÆ­Ç’ÖЋªd›YÖïU^:F­è)i%¥©h§P lP†wH†lŒu¥±‰ôeF&·«A…³QØ ˜Âà ‰6ÆæÂöÊæb¼_Ö¼Êþ7„Iã9æ#‰²Lë®JïTXŒ^†$p¨Û@-öH'ïè÷úÇî3±èÌ"î-$Y ÄIß};Â5yÍ™“̯Ü_Èâë°Ú?êeœK+F#{$P†Q.ÊiaÒ2Ú"Kf`­}©óÛÀ§83Æéð-Ær2œ]ÀÉî!‰’¶ŽJZZ8R|¨ó=´ ¨"Õ-k°Ía“ÂÂ`*}6¤R¬<œS&ðÃLœ¬Âiö@ äµÐ>Õ”ßjzðñ8°\[CyÝÁ°-c—1‡R,:Yé‡ÏôU[Ì=ç$ˆë¼C} qJsŸ´ÕÞšù˜È×D²‚ä+"‘9‘¿!qRñ¢üÈ‹ ›WÀ¨÷J†¤Ì¬ˆ œA ̶°÷ÄÞû$<ìÖ“ÊA•eÝŸöèøèÿXç‚= “8g0Žñtê+vW<[ß³å£õ.~K…Q2ÕÕ?“-ÓÎ-3Ö/“× Jœ1`ŒÃh38lÐÜp”?aí âOoà^pÒŽ ‡%œ¥žÉò®W{ˆ§Çì)N×ÞÃE.á‡7÷ñàõ«È2W9v(­Ê´ÔÅ3ðUâ0ø§«<-ÃþÄþÎ ûû¡ÆÌta¬=e Ð1¸&<®¡·²,Cw@ =:¸è™÷%„iŠ;‚ü<™‰ñÇ#†*%$ _Ô»™ÊŠÝ\™¬â¥]w¡K£T 76B²úˆ„äQF¥Kp‡QF]fÍ}€Ï·€1µ>*5dҪÂ!Ž«ðð.P›6„ã€DU plƒqmP²F@.ôJjK2$mN¸ †c"@g€wF ok]”lY7/ÒC»]¾8c¬-ÁÚL/ÂfQÃ_€é»NfÝ0I}¨ƒó\*",K<~±E­w/sí‘E6†Õ:’k ºZ`~tÇe%ßx´„gZ¸ŠŒËËêšPÒ·®@nA·Be"€nŒÝØ ‚v(* ÔðtŒ`”߯ùâh—´ã98)þ#޵ äÅ÷fë¡ÉpvÁriŒ`áÖ±ªO­È?§`æ@QmûƒÄs"_ôæÓÊ>ÎMè·ˆÈKxJW³Œ*^Ù†ßûðÊ´x£ß÷GÊ}©vOžröÆ?̬âgxÇÅ¢%KG6ÐLÞ¼§‹²$&–¬ºF\—è»Kh©çt™Éަ ^AùÉ$(å!¥¹tß/ÆZiçWyÁÉ&«Ryš«`€Q*$­"*Ó4ãT©Î ›YÈ-g#zQ,åŽ3éÎë¶h9‘Ëvn§é”ÏßÕ ®–• .KÀéÄ×®ášR÷Èy9ùÛ¼©çh3föÒɾBí²õ¾¶’¢Ë½èЫ"Í [üć¾'e¢¸IØ1Åæ¯u(ºé¼ Ô"P±þ¡[VíÁ¸è»ÿ¤(MKXj ƒ/‡Xd ß¿ s/ÀIðpšËÃßî2¶NuÈ¡¶NcædÒÔ¶Â\FPúïï{ëC6Õj=g&Œºã$:5axäÅ:5±×Á§ûÈÈC…1æè± #Ç…zeVp ìk}Í4¹ì:Peö o~¦ã· ‘ëž>š}ú{X÷ î8ÁU.[•®0>öE7”ìcÌ8>†:=J9Ðf0ºëpÿÞø:=¯Q:d†d¤~ú½Ó{+_™4¥wagÖ9HÊ“‰zFk1!®•á5%ÙLQ–lž)íNÙçð8ÄÇ#Y¥R¨¼“ãìˆÙ"DË¢|9"Ü5öÐQsi9¶/ ½°V:æ[ØS¶Æ.÷z±#-ŸÏã6¼¾5z³ƒÝUËÉÊS¥r€dÁ>¬n¸qO£*'|Š•9nxº±OÀUÂ>P%Æ=/¡?‡‡¸éî@A# ‰WÒTMÁÝò~Qì§YÉÂF·¦µÊ5öÐFíø=õÿZý¾SS#«ßwèfìK_˜ãéïclÒy]JÓùÃÚ­÷øí[¤Œ›0ÀãhÝë[dw´åÃ×ßû\•b¤sèkC@-ž­6…‘ï!Á<¾½ê±‚;÷—ÑuÕbTåÆ=¬} ™à…𬆴N€ùó£O§uÜŠ»A®ágp[s8ï@퀼þ,À§{Aua \×t-]¯ìpô€¨Û©±¨ðÀ¿OÜLJtŽNo üw@.qkêÃaÕ æÀ®vvmÃüá%$ÛÁðïêÊ8‡Ç@šÃÍYZÞ½Yûä(3¸{ü“Ë'ë¬Â·¸s2ü&óïðËéûTFâP2Ðv'‡»Œw`ßOšq›=>rÏÃEwêO¡¸Á1fJÞÇOØ[ùdJ~ ç}( ís&)¥EW-§€o°É|¨³‡Ø”N°Û§õ Äº'ÌSæÿÐoÞôøM ²@}(’ û?œ¶¡$ó‰„ݺ=¨sòÄAÔE¥£;†Îýuë†*iC2ñ]Ö2Öóð ¶BÅ-|ƒÈíWøê›- {e~ýkµçAží$yÛyïŒñеèPÝÐŽ¡}Jês8"@ö__ö•^½¥·xèž †#ul€ôfàí¼½Ô»@‘ÌcEïè»qùˆSÆ;ø£ŸÑ}òëÝÃŽ×ÚÉtôûïßmÁ;endstream endobj 65 0 obj 2924 endobj 69 0 obj <> stream xœí\mo·þ®_±ßzWøVË÷]7ÐØNáÄ®]ùRHŠâôbYµtR|’_R俇ÜÎp÷ÙÛ;%.R IŒyCr8>3;úǬȅ̊ðoGœ\¹ì|sP7gGi‰wç?”¹ ÿÔ œ>¹Ê¾ZúŽ²Ìª¼²ÙòõA‘WUY¸fT‘Yãÿ\™Ì ‘ë2[^|?Ëæ UHÿg=MžÌSu£ÔÂå¥ã¿Ÿ6¿‹JÍ.ˆÜù–ÈŒÈ5‘×DÞBÞwDž¹"’Éð©#¥áŸËo¼n„ˆºñº^>;ð¿üáÀ«ÃÔZþñûÙ‘_Z.KçÄìóE•…,¤_z‘;!µ›‰HežRÆÚ"ÌÚ5®"åàvBW¬‘õyM¿_̵'T¥g—ó…ó ÎH¿¾ÐIÉÂ&]Wô“Wˆñ2 YñŒ÷¸žF™RÍîˆMÎȳ‘q…Ì]é¥o–©mÉ' á×4ûý*ØJ^8åØH¬ñNúiîÍKh9ûĽ­Ie½¡YÇäGËÆÓ2Þ|â`>Rš\+ãÒòÔŸÆÊ~ 7¡™Ì £¹ÛŒ!Ëe¤ðÖ®hÚ51` ^Ì6·Ú¸jöÊ›5ÿé)‘‡°ãÅf$Vk0àp’T2Ø µ®¸2yMk½…Á&fã2òJ–7†äÿ‹ QeÞJ9€–ª ÖŠÜ¶*ç‹"·RÊRúÁ<­„7[=S#í:€JéG5ŠÚ*WÚΞùåyÔÑÎèŒÌkj>kÇ+u8}Äò2Е¶®ÙÏã×R˜0L•›2¶n0É3;Ue2 ïÈÿaÆ:‹ÜX!“I—qôæ-·.!䎸#«­÷A›¾Óòߎu‡ÆapŽŽôç¼HNCN/# q@¢.ù$öwfqALÝo§N5è¬k~FVûr®òÂVÞM²5!>Ì+“Þ­¦‚uËJ¤‰ôÃvºX_Öš +‚Hì|2âe³‘ ìØ{×/ªø÷„½|€Æ…ZU³A6S€!ôeÀÂU²´œ û‡sR=ÉÎV¼Í°n7Pd†c4Ô^/l¢.ÛàIõ¬aðÐ9oaÇp¼HfNØ 2í bøØW!tîÛy¶h0Éñèù–º°sW¶Š65–FÖ³¡nd@@震8f_hå;š2[´Ñfᶯœ¦CüŠªka™I˜`{NàIð–.Dâ#ÅL¢Ÿ°¯@¥Êµ¬u½ €Çpƒ FÚBøÃÆà¥ÐíiD¨D Û?ãuc"pì´ƒFÞž ZÛ›< c‘Oc§U® IÀ';æZBÐð–HŒ(¸>a8’ÃŽà”${?7A»é±Zï1o¢~Ú'<2 Ž™xßñQ4¾-\t;AG6(ô½ø¿¡€ÏMöƬ„À¦\ú僼 6à cÚµø ÏM«Cð>'8ny <æj£vä{äaê Ñn{HÒ(>ê-»íféŽuL‡¬"u©6=¡dð? [àK²%ŒÜÄ^‘úi÷‘N`å ’g0ïr Ò5]/Bts%=|¥ü.R_'ò éÕçäÈÊˉ”Ò%Lòl ä,Mt »ÝÂVÖíòžAE²n70?Ŷâ¶äXÛ ñQTâ«Hí± "ö²i¯Rè¼Ží¦†- ë‚íèáÔŽâ½»ƒÝ&“„çSÙI¼åïÀ&HO£ÿ©å½v…‹)}#¡’)mCNö®¡¾Ù¸¯¡%¬@· £20õû=V÷¤6¯aë½–št ùƒŽ~Dä+"HE_P« Òù%x¾«)?ŽºyŒúI¢åížmJËÌŠ˜ÂßCÛÂ(ŠQƒîöù÷Iñ}r;nÎ(¼¤ú ý»Œ$LÓÇp+~Ûfw?S®^‚#Õyë]àJÅ^rjûÙ6°cÀs.`·5T"6› Žûfj#Ùlwp„›)lLHdLS ¸Œ§£÷ü^ (c/=µwx?püŽW=\ŸÉb±{lù¤)án[»…¼“’ý†6q´)è&“¡CNÿ_À ˆoÕ§ G²×piøôaýïc$÷ šöºž“wÛî{“¬ŸzHoê×1õB÷øì"¾Ÿ­égÿá]ø–+ûþOøRš @ùv JwDõxÛAL§M˜Æ°è΢a½Ôõ ­±P²Åß”Ú÷0ya¦O€çÏþÍžœ ”»è£JgëÐQ®‰öã¨o~½¿žDêM¤ÈNÓÞ€ðg7ŽFïd¨xÏÏ/¦süÙÊxwÇÎ _ð«k¨&ƒ! ݯ!à Ôþ´ÏsÌ@ÞoÄr+¤yÎ&ñÝyZ>þßGgÎüçá‘2™' ME•îa(©rayI‹°Î+1Ÿ/tnª¢Á« 5uù‰*ý\!Q ½O)dp’‘oú¿et×n”ABT‡(ÝeRBÁÀŸc0½%ó±§R>êëûÌlAÁøÅê¢\ JrÖÅ~5±Bêm7§¸( ×wís«Ï®¬¾kªKmfßF½˜yWæ‡×õÜ·zNiœ«‹É¼âü–>j†²UÙ\»º¢#²…ÝÀ+>¬ ,æb#Ü4¨Úå®*» T|‰‹ÈpILr—Ú·¾E„Õìú“*Z&Ã>&"+³H®e»Ê__DÁ.”™ŒÄZbËe¼W!…L!)™´áÁV×Ș`ÅÅ›~ø]·& l팗—dt†²hœy0–Á¦PARSžÛÄ«NÔP0ŒîuÏÑUpÀwáCRm»š¡Ò»TtV>6þ}ï+é®î –ó¡þ"Rw‘¢H”î*»Óûç/cã@@©z©‡`º÷ d½ÂPAóþ¼‡käQÜ×ÁƒY`oê#’®÷Û‚#a-ûžܪ§FD½ô÷2z·Ï¸©´ô5Uì½W‚¶|¯÷Ý‹%h H… .ÀA¤3·Šm0Ø÷¥¯hX7Õ/NÂ~‰EGäOÍT·ÚóÊý‚ýÕÓ’@ð¥À–縂fÙ+pÌ–ìF¯ÌÏ{ 8nü³ð¡² ŽžÅä2¯ùP=sħp¬3éÓÀ™lu¤ˆ÷óÔožÂ¹ÞÃÚ±6‘ÑdÍ 6.˜¾'ʇU‹÷+À‡É5«Cé|c2~q®WìÛÖvFò)‘f]9Û!<`kH+6U®„I*67„÷T}É ²zØÑ²Öæ [Nˆ€ÌŸŒTw¢Ú¯$ÆB‘b‚UèÜŒg3ÙçÛÖÁñ X†%žÎ \>ìˤ)ÜÓ¥t³ÝŸpcr½ý‡iðÇQxa‹JŠäuDÁ^YhF²‹vÀ² À;ÆÛmÊÓ½òà,ŽÑØ0£™ÀÏkK!—¥Õ‹ðØ:*‹Ê[ÈJ&<††ä,%cáìãy\?9)K#ºg1u󆱿éú²î*¥*ƒ]F–¦íY4–•Nêã¦{cµ*»5N.sk´¨«»á6µí)éòÒ–¬z7Ž›<¦9¢I¸¹tOhŒÓdÍÓI¡‹:Y×eIö‹µØ“G*˜Î(6eHF KO“‹˜ÞçïøóFdÿªT·„Muß<²ŠXWXÿ¯©ÜU§¸BçVÃ…1ã/cZ'ß~4fè0†ï+jªt®M,*á—¤ç#ËS±È…øµÏ)j0ò Òqüˆeò™B8T:ˆ¥q ŠÙä 4‰‹N/×Üx’'Và „! ø(”½ë;È-*Þ'¡Çomx^ñBv‹±ÞJ.c…gS» WV*1§&Ü’yQÆpë–¢©AÀWGSøéCò' ¬´Í=z&Õ$Á~ ãã!²Äý9Ž>Šé‡3왆°mÏ~·„ÒÉC¿ÄÆ_Ia7{Ï_y†y,&D'h2¶ïé'›¼ñ¢,y‚ !†¿© zÓ÷ÐÎÒyk›¶Ü¦“œm/–¬Œ™^3ƒ üìlŸGnô%øËýP–éþÓ\V>¬“#Yv6¶†Iûeîã®oý’…·0ûCü‰lI’HÆÏ틱¹pŽ +šg’’ˆ€Öœ@­ë"¢+¥Sì8û=|%˜€œ­Õ È=¦@‹êXQãÐLÌ%Ov‡É.ôwO°ÝÂIöù¤‰AC“®ñ$Môo Ç¿/ÀB„“O aq–çxB~1¾­bˆvÜ_Åà¯\@75k(Û>ÈwÙ†f*÷8ŲÕãÇÄx5lÇGßv`1¸BXt2ô_ºí’¦€'{ç×€ïÈcÓ —oÁly.z¢ Wíà;V€1Yò˜A^ÖúÖL¾8b#°"V\§ôŽ0xEôdyð7ÿï/ëºxendstream endobj 70 0 obj 3295 endobj 74 0 obj <> stream xœí\[wܶ~ׯà[µ=€$HúÍ–Ô6»Êæäœ8>=+íêR¯.‘%;ñ¯/À f@~\¬V—µÜÆyq!ðáÃ`0ð÷HÄREÂþk…£óíƒ<:ù¸Q=ŽþÞ×'¿oqbÿ«pùèßx·¶¡ÌßéæOFŒ³_˜ßB:§ë§sgƒ®öª<œuçµÒ8ÿÖhœ¯ÉZ‹ø3Ð÷ÆIô^rCíµ¥-òX¦ùú†ƒ¹tÌÑÃ/žB l±czL"óF±Á6°j_BÕ&@Y¢¤BÅy®Í.e<5›\ôê:"ñ´Ë¿­VïVcP§»ã¡ QrüJû"玓¾¿ï¿·À<ÃŒÂÛ Æ36B“á.Û—»°z_ñ¶Ák â5Ô…zÙRnû@J·ÿfüSvsotšbãÿTÑMê8—Zâ #(3Ê3'EFJ2­EÕ`Ãß\¦¥± ÒÒAZ‚긂—î!dzÎF©’2µxš‡IVX/|hjW˜äœY6Wô NoAð<פŒ³ Úú™ªµM a½i¦ŽN6?‘Ý!»Õ3‚¾}kúþÒ1ì"Fñy Mœtî¤ÙÝ#Œ¦Ö™“¾,¯‰…(XÀäŠøt™EI\ì¤wí X)ÏŠ’ñhÒûÉxÍC)#«àç/}„‰Á¤%6x±ÁÑ…`Xèɶñ ¹´ŽñÀ«xÐã D˼js€± gŽýÙ…0˜ÕŠrO)qðßè7@/\±°)ö‡(ƒÓ`çØÛ$¾eWÚÉÁ :©x˜¹ŽÆ[Ü÷Üi"‹ï{g„ þÀ.%Ÿ´‰%;=`ÇŠtðÆÎPOàyÍRY(ÝŸØÙ;¹f ˜£œ=Ês«ô½l”d-š›Ë]̯®tÙ{Ö³)=Æ\§$W!rb‡ì#ƒq”ŠOTO•hnjðë øõÈI§N¢áŸú¼¤ÙÁîËD••Å*÷aÿ:xº°˜ (ŠˆØudíÃþÉl5†ÎüF+_øÜ˜D¼òŽ÷¸| ~¥gtþ9ï±uÈù#4N ÇÇLÁ †4˜pŽ£æ¬ÀŠlÀeY|S‡—°Nè~ Ùåm`¡©ÜŠËÏ’ãý?e¶ÊH*ïEÒù€Ìͦõ'ŒÃ.b­”*”M45‰”©ªÜwøÜ”/ãÂh‘ÚºŒ“TWù‚±¥’6Ñ&Ís»lT:Š´ðäm¦¯SSÄ™–ÊÚʨ©˜±š;Uñ¢ÐÕ`¸"וœUhðò¤îy–ˆÌÎ/§fZ÷T›þŸ4¶ùgÞ;Ë8+òĦqV5ƒÑªª¬Ü6Wú®i÷í®)¤fß½ÒnÀ«iÝèAÈLWÉ}çMrŸçb³Ü½}çK E+Ý:égû51(Íî*5›9Å·#”q5ƒnùP*Ë.º¿3”nÈSÒ·Tb¼qð+)8K½³ÀK“FI¼‹ã¥zw®ÆØä;CkÓLËl´Y“¨e:•«n¢Vꥇ±¦ô ã™¶’–Y:xÝço®Ô”¿ìåÁ„¾p`†n“áQÂÎ Ä {SØY ø§Qf÷ʃ)§}Ú+wzé›kƨ¸p)õ,…sŸJ²DFÙE¶ÊÎî$ù)m¬¡ò’ü …NRNzSKC1Š ´S®ãjâž‘IøbÏE–qìp#¼Ôì@ªmðšÉç&g¿¬c!ø¶ Î T¸K×J(â²Co鯛®B›ì¥œ».âŒTÜqAÕR*ò²õ:Kí.ax7œ‚6[©~Ê·ŸÒгæ±Q>„{°kh~šråªÚ!ê ^Üð… Ïú81˜±î {'¶ÕOÅÆ]`£//2ê”çÄüß ùV;ædjWf™×ݯÓÅmtý{¹kÝwúhUððâY|¿ê.—ÅŸæ£U‹òÿ_.{¨‘œ/—=ÌGEs•£ø]}†ßna— Ù·[z©EO5×㬾5œ{H{ã™ÿ]]–óendstream endobj 75 0 obj 2802 endobj 79 0 obj <> stream xœí[msÛ¸þ®_Áoµ:1M€ Hv:IbO/çôr§èÚ™¦ŽlÉŽ¿E¶ï.ùõ(» ”ìø.=Åù°"K`÷Ábì~Œ’XÈ(± q|1Ú›äÑéͨzMþZËÓÑÇQ§ö_õ€ÓÇÑ‹©i(‹¨ŒKMOFI\–E’¯¤ŠHgæw™E¹±*¢éÅèÝN4ÞMi~«·†Œ³„&Ã,$÷7ˆêsø‰Ø¯gˆK( ²ÜÞDçþ¤õ¹F¦a›þwdà˜™Ÿ¯GÓ?¾³ˆ:Î…–Á‰!¤1÷ÂQ‘¡ÒLë¤ê¹i™ UW",.„õ$¨c¼r‰‘É9+C¤¥²Š5Ó¬°Šµœ©Lr«7¯ Çö÷ŒÞ3VÆuD²áÎû‚(c³x¨šWéÂàµÂ]<¯(é”"ùø—q^˜ùbÝè”7€U«¼à#Ľ¾$a+®ôêX£¶Ší{«Ì8IdZr©¬W†dšÄ:ÉzR9£Ür„X²Ì2>‚cúì{zÊt0'YQ[V­øŽ’ÌÀ°’NÐÈÏÅ>gÇØ™íŽ{sO™j0Ž<©cWPajtú&c¼×D2…!±—PÍìcíye>+dÍ›êL÷Ϋ 5FUI–7¨ñgXó…Ejdž›-_-šJÇZ‹jÑ4¾«’F^äÒQy'RâíJ³èæ¹€nätÕòzwpÖ“ãºoÖÙ÷m6ƒ»…¸®Ü‡½–þ¬³úsÆöûÜQëÌ™Wj/TôQØ݃GnÂ8©¢ÛÑ80?=2°áȼX-¡lga\¡uÙ|怖Þïˆ|Nä "_Éšíubd&¢³Z+'…ù4q"2¥‰2ÛZöuX(3%r§—}úò+"ß>zß‹8)¥›ü1„Ɔžu²B”. Û­Q‚xEÈàl3wÉH_ÁÊî&ÓfbWޝÕêé--Ø}£àŽ-5ŽMê‚EcïŒÎÓ8Ñ¥ªBqã÷T–dœdŠêΡµéº‘fŒrçbl›NJKJeW¾XiÎgt%6MD=³ì¦ÙƆæç6'ä|_»÷PÐc²î3#AÄXqPÕò7=Λ¯j@A!kʦ!ã?EËC%¤AŠ1ØbŒ&sÅõ§¶)*øÓ;‰‹ÆlF»Á™£.µXÿhÁ´:sÔçá’ØÞ„í”. É6Xl+ŶGnÓ$ š²àØT@h·L»Û·kèB¸VÚk% ãe+7Öò²}ã^h߈(î†hn€²èÈàù—QÛÝŸÁ¡âÍ;>˜CÅ. \¤–¡ê®<²VðÌ`O¯àH?=¡®CDùœõC¬5ÍßKe¢û"ÿTåÉe¼¿ÀùŠý>Þ ;ýk‡è ‘,û;‘‚HÉDŒÛ&`é¿9ê{G=sÔÐâm%œŽ›M<æB™?f6c&¹†Í–™¡GPc8 ùL ¬%üÄGØõÅýO>“î4¾sÔÔW½4ÑK.ƒžÏy6j¼¬âQ/á ®¡„2àiŠ×0Æp å²;|P| Ô Ý.]L:Ôcè;¨Nìc‡Uö¨ˆ{²—D²=+óo†N-#ò/÷ù·(SD÷#êu.êBJf(búþ bëò2Å1ax¹À͆CòQÌ”· Äý«CÇþ›õ.Ø¥lx#õÄöÓí×2ÝuÒ '™’Öõ5¢DéÞ«ð¢ËÈàu!Ž>ŸA xÁþ’Mp©žÃ>à•}bðVç×° ^N‚;Ý``<ƒúþry™„O@•Ð5!ŒÓ B뀦ßôh(-äËpPÈu UÜb°dvBñ&äá†Gð»>,g>î씤1’.¼SóÖ¡Ÿ}O÷>ÍEY‘ÖM¹y”g’ë]òc?t¯6è¨ýŠ]Ä\ñ“O×st_NwÌõ1âJHÆðš‘ ‡œ'.Ä–€ºê<ëx…Ǥ¾Ä´aÛu§8ÃçÁì({ǃ9éÈ—Ìq ´7ooÁÛcG½wÁ`îz 'Â[Y¼/f¼Á ià óéxýd¼ÃhhU’I@‹—^ÂncÝbCá“1´zFP %æð–žÑíÄy®}1Ê/Eã|-F²A•óÀ˜©N ÆhÀ¼Œ'P¢éÕØý>^É&äŸ÷€§&¾ —¡æþ]y§½I™PŠçKi+_:MŠjòÚ|iÄ'±–RÒ&}˜E%«>OÇ»e\)¢Ò¶.ãTi»»°i¥6­´!u-£P…]B ½gåeEaÖh,$aÌzÓ†yÝë¢ °›v/WÌFCÖ(ŽyYÑi–&™ÕºQbž–‰{®UZØifU’H£x›Öfô©ªƒ.heZH‹'0bÌûôM.ã†:ø¡§¥ÝÈ»¾|Cb‰´»sÓi3 @ˆÝ“>/K_+ ‡nÖ(»á?tq¸f7üM-P~À•{ͳBYììÞvæ ›?㪒§(`fY sxÝ3]û2;Z)U6AàˆßÿƒçŒíÊÔÄì:åI£èóW°,3¡åÀz¶ÊÁKŒW¼ìô¬ìÍÄj¥®*åmªx&×*KÅE¦šÌŸÜ y©ŽåIñÍŽý@¦do²`0/*4‚®ª&¥Îo¾Fr¦—ÍêÈg0ù©/I/˜«†…‘êxçP_N?3;ý4Ã{Ó¿;¹¥Z{X^!&3+“öóܪ|*믕•¥V>ÅXĺk'UÓ·žQ«VöhšÄ*­’GÕêÐS%έäÎak…Nf®ýreÌÂÕ¤eŽá³Õwžd×7ŘÞÇ™AJºïQ{FtdÏ 0ö)çóÀ®WºFøKzÚö\·í‘­|Ø bÖ¹ƒŠ‚d:š¦f)lðwL“˜ÙªÉ+“²›‚f-Ln'Øa‡|Ç…ã¹Äz†®“#ÞÑqÕ¬ãh[9ñ}Õýà"yŽôÅü\ŸsCw¸´€ia•G—šÿµÅwk“ïÖ!LS3ä(lh‘Ö7Ž:t*ÒV|Å¿Ö\G¹çZ§2ðn8pñ>W`O^ ¶NI:^ \t<¶ÿá(ºBy$Š.“)= •ç­¯Øð¿ EÜÐ<&€ŽóüÌ£ûŸ ¤Ÿ0† ©ñž&Dê¶žUõæ_«zÓõæmõ>‘+Û°C ÛöÇ*` Äm;‘Ûö{ Ø·uëÛºõmÝú¶ný)êÖE¦Ö®[/ŠmÝú¶n}[·Žïg~7uëÝÛ M¬‚`6 ‚9NT̾q»#ñlõ\œg0¯œ=ÕM9{70'jjíб=gWZüxeî‡óËÜrý2÷Ýæ»‹-vþ›©ywoß,?¨èÝ&á¢wUÈÆC®Ÿ­º­~§. ­v·úR7äóÿ”Á{;UÑVìÁtôƒùûÝtÂendstream endobj 80 0 obj 2783 endobj 84 0 obj <> stream xœå\ms·þÎ_qßBv¢Ó¸WOÛ½µq+DZLO2“f:´HYªõ‹²›vúß»8Þa¼‡<š¤DJ±¿¬qØÅáÙg è_ƒ(T:ˆìßZ8½êìždÁ‡»NÙœüµ>}èüÚÉCcÿ” R>½ öû¤¨ó ‹4èŸu¢°(ò(›XUAšÐ¿‹$È” ã<è_u~î½iúwÜýŽÄ0ÉLÜý»“~tÒA%éÔê¤Qª8“êBTN+õ´T®âP²ë—IU˜î ‹ŸX²°x ÕîY¼fq\‹š,üÒÿ[‡¦O õ;ý?´°ï¤½‡à=|ý;8ë„eÈó¸ ±;€E¼ ‡ Űíí/ CF•Z¸‡ΠħpˆoÛ†A cÏ—¡æ“Þ:I{W+;G 1VÑ÷’Å]ˆš°0€­­Á¡×Bm†òg'ENzá¤ÌI¿øèi…™^=Ž‚öŸ!Ï0—„|É7îŠ`+Ñ![%ZŽœtòÄ]<‚p܉֧é⯠^^¬ç{V;é<‹ãm§Œ €3(b×ãðŽ»°¯”%¨|¨@¬_¿AT„Ýkh ¿Lzí¤¿|„¹Ó¢F«,̽®"–0opy‚ ãE¹ðª‘N×Ì…+7ØQb"#Øa[q'xƒ9ö¾^x×Å.K~˜<d5ÜÅQQ±ˆ2óÈñênmL°Né!b p·mäÄ¥úº³À™È¨5ÖËÒ_S®¹nÔàé<=uÒ¹“ØýCßÁ ¦¼½…¨Š¾­ÅÒ’{|üÐú±ª%c¶¬­bÔ"ÞkŠ÷ÃõÉ5|k -öþ̉V>ÁÒ(0“˜·C9ñ—ÏÀSn»pÒeƒ­³Ê‘·BqÖWK\çµBŠY…WÜ3ØaI6ྡྷÃÚ X·ûóî[‰~#?ï‚LÍý–\†ôûï*KížÒÞÅ”ÜL]L1iZv°S¨pÂTkënhe£T¬Ë2~V{ædE•h§Ehâ´ûŽ*0 ­ìñŠŽB•ÓÝë)(¦Ú&©¬åqnó¡“aùUYÅh¥ÊÍ)½zœiù ¤6ýCÕœMŠã"LòœÖöCîq!ì݉q>úc֚¶­®ˆï±.+‰4.ÇËLåÖ—dÁ¤±ÉEßËÉx‘.|yT¡”žâ]sêew»Gw#ÑîÌVk9!ü¦(‹Re·˜®»TŽ÷'#ßÏU¬ºÅ6·¢¥œIÊ™(Ö£Ù„="°óJ²²kú3.N™D…ylùIJýù›ª ‰U™Jµ¨çGNµµ­í& M‰V'OÊï:£Ìd”î¹à¯‹lތ٨̽…+ݹôæoe=_?q£è*ZÅ6cìO…¤ÀŽu®hé˜z·8Í«é4ßÒuµ¦©Yk °z'E×Ä壯sŒ—¨¦?²E㘠TË…–FÇÐñS9@¾€Qôº‡ÑËù¶Y)¹aù¨´I¡ÉP d+[ä“÷“$B!¯£¢N"2¤ðjµxu9™‚ÝF¡(˜5§Î¯"F:…éÅ[Œœˆ×qoAFk˜ÇC+I2‡ÕÛx\pshØçù§Å¸NmV©ù ÇõH?5¢ºíµ„Þ:x.¯)“«š1לÀÕ,—¦Î¸µÚÐrY¯œZÏ(–x~Š#mª• /ÅñÇXÕ8Î(”n8(#R$—OÂÒ‹‰º‰Ô¼oéüÏÊ|]¦ßkž.ñçÎ7à”’ûƒ3*¾ü®8×WI¹™Ðr7Ð;öÁ?°¹hSÃßðÄOð}c|ûAX@÷Z½Ï$¡ñƒ™¿¡®Ç?—Àòø"О“øºó1ÐhåÀvüê÷WZ‡Üï''%Nª¿ÉøÉ«®h×8ðE¬†ïë‹+:?±(nÂEçLj5³iH›ƒY¯fÄÍÑ™¯/2ÕÆq^šÎ‡Îj竟¼‹þ†ïqaÞ|¶X3Ì[š-âç†s¼EtæÅïy$e±ø5’òãÓ™‹òü9à¼Çb>çÂû„? ߃€¯§  x«ÀÌ¿}~,¿ùتlÁ8?: œ7_Ë1Ì…“žð[À\°ˆ6Ø£3ãü<è,pnÐùÑàEÕ\ì$ŸÐà–áö »˜EüŸHÌøáØaÛpâ×!¯„µE÷÷ñóÎIh4Öø¯“þÆø_[ìmZÊ¡YgÇøÀøªáƒhAƒ}¿aqö/#–"9dÇ57owÀ¹oÜÑ3¸s ¹óýäâi–‡™©? Îã“wët§Ò³·tTù½³[^3=êwÞÐßÿø  •endstream endobj 85 0 obj 2585 endobj 89 0 obj <> stream xœí\IwÛF¾ëWàåb2Ï„Ñ@cóœìXc'ãıÌÄ™çä Q”ä±Z¢¼ýúéÆÒU |`)Ë3±/¥fõVË×Õ ê½ø"ôý¿&fg;öRïøj§(ööžVÄåñÎûÌô¿¢€Ó³3ïñTUÂËý<ñ¦G;ŸçY–­ /‰Õßyì¥"ðSÅp¶óf4ODâ§" G'cU,B™Žæ†òÅID£¥âªXæ£Ïc‘úBŠh´€u ã…)$FÖÎÛ±TD”KŨ £8‹Fçgéèj<±†0ÓïÓqP[sÈpmõ r_‰HV¼2ÉF‡°âiÑ€ PB>ÿãÜO³(ÐÂEƒ²&PÖJ3>C<êsj¬a)WÃê5E¬×Âôƒ ŒrÞ*ÁÅø¯éO;aøI+ƒ™*û°Z5ä> `É-D“yǫ́Û*e28¤¶¼f[•à[BRÃB:B3?5ëÖ'NÎqo< ý0Kòlôh<ÉG¿Ñ߯ôß»ãHN Î9@òxßi^!£,i˜iÝ›Âåx«RZí2Þ8ÓH9ÆXí™ÎI5Ì'¾+íD™½È¢ÚN˜ù2VjŠê]åsŠüG1ì(‰Õ@˜¦ ŸRÝøDJí8©§Ö): êÚVÝŠ$aœXn3b­Í;¯Ü£Ñ|Kݲe°¦¤Fj"IÄáA*ö »Ó©Þê‘Fq‡£/ãX¡r9‚/ ³LSGÐÞ[2mÀTkéUϰÅ;k›˜J)aÂžóæŠ åîá\V¶ù"MÛScüY±IšfRŒe½þ9"å 2ï è"Q¦Z‘EoÙàm2°A°¨Òzs]³ù0·øs ÍòbìðwB7¶B7u¥gŽÏœêã5¶?Óv—äAʽ/+ ÆÎ”;HÅ ÕV¯™!M®¾çºžêX„LÕ¿ßÓ¤±êíªZÚ_ªÈ¨2›û| Óh‘!Wt¨’Ī(µÔß@§–cèß÷ ÅÀ‰°[Œ×0F¶½XSë)J[NÜpÆy=¡‚:Y¨Y‘Jw5Ø©´ æ,誱Zeãm@ û%&3,Ý=cÕ~aI.¥ZU†@Š>˜¡²eú_8µ03E1VKu¡WšžŒüHæµé]ñ–*ìûb ï+ž`&út4Ô«M7óƒ<4ÙèÂÆvj/ #Ûv ƒIçê“ÉŒñž2pª}H€Ó±B;ØP{Ø×Sƒ_]›~Î,"[4­²Ñ‹Óh÷ˆÇÚŒÁ¬`ÊqÜvç-',æ(½!3Ú1ì©ÏÑG½Y¡ ÂÚ •û¡ ÓZÛž¡"½$ŽÌù‡B?+sP°ö zÆë-yFM:=ƒ çaËIÂXù¹h8‰oV[exQ*·â¨ÿÌP/ õÚP^E…‰ö¡ªð¥¡Î[ŒR{EýÛP¿¶–‘âPA™¼ ž³”Œ‘W¤†"/ˆüH¤Gä!‘—D¾%ò‘sØÂ’ÈÏD.@µb¹1K(eô+-àp½cËðsw…}#øi¶ßq}Ô‰ò<ãÃþÊ&Ë ÿ2¬y0¿ÑgfþŽ=AoPYu$—Õq ‡ï__u£è„–mÒ]}½ Ý1a1°`áÐ>ysjôöÆ$ôV»¹–ú®Ä›šÑJÜu½rq°eÆa*>¹„¼ø¨žgcÅÁjLÖë\ÿÐ’?¸…J‡o]n]£;…NŸ'¼ÞkQ}Ä•;Ä…aóŽÚsIË‹ë¦`ÓÛyÑÚ÷d€¬xCΕfP@l¦l¡Äg[½î£ºÎùD¨?¼ ¨•ºPkMa0/ùK™0(@çÛ'Ö.>þîkoÏZ’•£Ÿ õÂ’Ûj{Û¾´‡ÌŸµ{íj«î¶€nU+â¿€°ß*0ÔCCE†úËeø.ŸÁù`tÞ£l4°¹ÛrŠ/,—]@†¨©âÎEé“6ä·¦ÙnÖÜßuvÀ=QX+oL—"i$¼Ð}_´VüM(â,zm»^€ Ÿ.9.5-’Ép[p¬uÊæ°|ô‡Ê(°Ï¼HTkˆòÎB†„Øgœ€!׸¬GŒá äÅaé87uøâºw<¾&©¿0#øDäÌ5D'Ò9µ¶æM¾$RnË©!Zù7æQtu%­J½¯®¾5çºçê÷†÷œŽ3 «·¯rRW_Ïüm1ë[Lÿw.]#»„¼xšGpè—€·ÿñ8ÚEÐi#Ež ¾edv4Ú¶›ÁèF÷øý¨·˜CwàAò5äÅ>ln'z ]]téjc‘Jîæ“ð·°zËÀ¬'¾“²ýœ•wÈñGË@gPÊrö²—§"°È6ÞýþnÞ óL!¯L©‡žâ6ÍZ)ÒØ+[G’ø¶¦Séþ˜•g87o}¯ì$SVŸæò‡Ff¨¢ùûpÐl|§Àøcò’KçsZ;«”a L¯ õ¤z®­ÈI•#W‡µ­YyC ÉÓSä&QNf†3--àãhÖÁ>4ê¶v#¡³é8=;š_ϦهðîôH¿˜L/ó1z^d†iÉ\§F¸•þ‚&`ñ°, JUÞ®~–™TZÃ,JÙ7Ð:¦•:¥e±.ð´(µ}ð,Lôu@ëåº.žW¢3çû¤€Uú‘8øì2ðA¹Ï]fªÂ°\ $ûöÖÌÆ>0`+)©=p†VJ† ØŒ†$·Ø‡¼ùî@ÊîäŽd‘„íý¥rCúØ¿LºQ$‚ÃiÚœ0‡scâä™§ “‘•%–öG[tFº9jP£dxÎDœH×¥rƒ—„VæT+á CÊ 8‚X0¸Â*±1²Ïƒ('œ#Á‘ý±îŽËkÚ¥KŽÒQÊ'6¾Ž¥I뎙ömkä?Õ!C‘îcõ÷S­t *¾/Û{H ýT¨þ„Çl)-ê¢UÖÚŽ ¡ºö2‹•]Ï EƒÅ¼àS|ç»úk¢ÝéÎKõÿ¿ðŸgûendstream endobj 90 0 obj 3135 endobj 94 0 obj <> stream xœí\[w·~ׯط’=åjqßuNêDmÄq*ѧIO%Q—Z”‰²ãüú{ÁÌr¿åRŠè*MãŸ! 憙àŸ’,2ÉŸ8Yìíºäün¯lNÿ^·ç{?íå© ÿ• >Y$/§~ Ì“"-l2=ÛËÒ¢È3Wa‰5þwa'DªódºØû~”Œ'*“þ·]{05NéÑM„VJ"4_WàëI„."4Ði IËi8ß/#t¡w€ššjQ¨†ì®L¼%pNàŒÀS?6 ôþ=ýjÏ3ͳvúÍžÿý§ðK‡Ó?·x8_§Kñ5%€ÃgàëàÂU‡‡5çhpMà pŽ û-Ö1nÅ­»!&3)œÁŒÉ—ï;8 ÷e~ôÞÊ$Ò7‘MG@T¯"´¡7 )Æ-€n:mm³¸x–FëÙ`¾ µZf,ÌòNa_¬*lŠåº@ö•I„N3Mî«Ðkî˪<*x/9žd©Öy¥ã‰NM‘ezdÆ.-´ÍKbrï õh:Vi–)g â˜el³Ö»6ÊDºŠDÈ–WÍÕ:Yµ6]RÊ\º²T ¡e ·‹ñ¤HsE’¥-R¥­§yPjçF¯=èa‘¹QVãÈu>R Þgøš‘rtZ=/œ,Â,±·Ý02Krô6 ±Y!ÅH³Þf둞I#Çzs8a#›Å U §êrq¡9û¾*—æTá§¹¬ÐeÒ«ÒIÍ̼hf)›‰'|’%GÝ »ëZ¢øaTM)¬V [î㪇qÚEÝbhG6…-u~<Ñô?ÁÙø¢0‘Mý¤Òo¤™¤Ç=Pâ!e¬Íïéi’ž:çïàYã¼Â k½6®#´¢žÞúMèéզꩤ׽«±`¡4§Ïl›ô2võ;ŽôÆí”ó|H*Ö•µV¤Ëf¤ziµ¸À>]ÐP†%)õÍã)¼êeif i¼Í¨G‰²üë¸l̼;¾oö딪µÎ3B;ë.Ä›vňÀè«€@™ÂGÂ¥Bg†#b g8+m’J¥¹^{¦§ÞÑäåweråýBUV˜°+E´‚úÚõ¾5;S+“ÔeCvE¬òÿóõ±UŸ¯Ël]«uýl9|e]gDárH´§P/i²÷4ÙŠ6¥yvÍÍy —=K¯zùÕÑ3emY¯4¬/ã]ePÆ{¦ûZg¼7ËL£3§=œ“Îïš… #&ÚêT“Lª ( ë˜U¯j¤TÔj>×D;#™½“gð¾\ªÔåV5&!T[¢\oД¥­bõÂX=CŸúÄe¼ûF—ÓFÀ'£%5]—Ñõ bÊu«Ô5uÀÈúüH¿§ ~Ž­v>̤R¿²<•:ú¤.»ŠŠY¶h#Ân4l²Íxfa!Ts*·ÞE Ìý®›¨¦a†c@:sõ`p GÞŽy8fܧ}Ö6ŠõwÐÅõÙÓ…ÅÚhÛ»ÔŒB7Zóa\xcÑ~-¥÷Yàâeôd+òýNËïJTN+„O-9( sf:ŽžŠ!MÛ)ò4ä~Zê%Q­Ã'n=nƒñÛQ„¼{È‹Eáy&[‘F½c©LÔ<.óÚVIȇè9Hr)µ=ˆÐ¤±ÔöT:P?Â'"da’‹Á;˜½âôö&§—Cîa+Ëio!²÷²ÎöqÜMz·v;L < Ph{ ŸLìÔOGÈüÁÄ.w&ö·j ;ªh} ©»ôG‘úîŒIÝðéjÅHìßDˆ ž_Dèk0Û—-U¹Ðiµy>'Uø0¤ +HÃpezBà;ˆ!ê`#ÈËï_:ŒÐ+€å@ßFè¯z 4ã²ÿ |§`¥òc¨›dߪ ŽÙ9»¬šž³¨·‰f)îÔ^B°ÉJƒ8Å ìË¡ü€•ÊX’ʳýÞâ]èÎøx#³ò¢·­ 4¯•õ¤UMŽ«På"¨ÒÄ’ï¡ZW_ò™x´˜V&T"Os)›„ŠÑΨœµ¨,—¡XêÆä]×aT«4;^C΄J¾,OT¼7ùzVˆv¢‹2VœW÷ 'b¸‡ZK«ÄjX‚ÅyN´™¬p²ÍÕ¦/Ÿl½¼•™Ñ‹q,¢V6?i$H lu°žÁÙOËÓVN.þ;ÐÖôóŽýóØ¸ Æ 1é‰vò¹íôlGf¾z [O†fû§ ï°õ5/a‡P$kà öø¼T)å^íòÆ' rò .s1´x´“©²ÚÕÞÕ& =¬® Uø¢Ø’â•O¢Ì‰P/ ˜Y`ñ;VáË!²ö„1<†É£cg•T¤ëºA ](H¡À‡õ¤óxpQG Ñ÷¢éòjEÌ;­\c^¿->¶YÖa™ö»i.³ß¡ý‘)™Ñ |ç¤hÿè©èÍÓ¬‘!9Bë;‘Ùþ¸m½øòN~…‹Äƒåé­/P1µû^j1©8JŽÄ´„DßA=D{ŸT ;`}ïyÏðžäñ#pmÔï`ˆÛd?8ƒÚþ‘×Ã^ ¾"pŸÀ7°ï£¿‡ž´—mQ½ÆkJ ‚Mçb(¸4ŠÔt×”•Ax™•.`¹g½0³þ¬å ê0Í ×ß‹‰Ywä£m/Ÿ>òmdÔJàN–ÑËdššé1>Äog`dDŸã|¯®ž%MÔìüÕµ}‘1Ç‹ýÆìêóßø#t7¬/»^‹w‡‡?qÝJ)žÃ3òc ˆ†Žòôų FÏü±õ,ü°’ŽNYŽxVz/ŸLï·yB]d©}Üj9ô€ú¨ç´Øé›ä"5y^]ö‚³ô¿I®_R׸…¶5°y‰\NDXêUzFèøYé†:}ÿ8ˆI•¥—ÈÓ½ú?¿ÖíWendstream endobj 95 0 obj 2687 endobj 99 0 obj <> stream xœí\[—Û¸ ~÷¯Ð[åž­HQ·=g6—^¶I·›¸§{NÚÏŒçÒØãIìišþú‚’I@Ò'ËžÏI2“ä¡@@¤ ~âHé ¶p²}ÿ&ÎW£ª9xóÇ ðñ|ôaTD‰ýS5Hød<›PG¥‚2*³`r6Š£²,⼦ª‚,¥ÿ—i«8Ê a1zNÆG*‹r•éðí8‰â¬4& Æq”¤Y'á’À\i“‡×Zóç˱¡Æ¤4„x¤5—á•G\ò(ŽU–I’ܧÆLtœ‡'Ô½ŒˆWÎý÷þ~º!_$áŒ[UÁÔÅáÒr›¤yª Ììºê”+S†ŸÇ*”IR"ê×m¡µG]Œ´¡)&91àÞóÄw˜˜G¨qcݬ×O±žBKq‚ñOã2Ê CÐÇñQjõ¥Kêô¯ÉÏ#mç™”d=“S2Ñó:ãQ…n;âÎTêÔlÙ½N!Y!OÁâð*}–½lûîÌʲa‘ÖxuRz8“&éÁ¨!¡DÖ îÈIÎŽ‘j+>ú2ù7­O]øõIë°&¯F“ß¿³ì$±V-¿·Fiž˜ð¥‡& @Û[ðõ•‡~ñÐsýŒñbUFE¶JS(rɘW5®*+¥;pÍ`Àà ƒW ^uûÄàÇ¡n3HaÎà’ÁßC §Ôm’õáõ½ñПAß—ú«‡~òÐ3`{(ù«Ðì ¤’¡ â\˜³lÖò\Ð>®w åv E8˜7ãK¼#¼€¸x‰ßby¡M þÕ#<>wÆû§CyÜ%¤€7no©ý4w_ŒÈ&Ü}Œ6!£pƒçá0æÑ““°ÊøçþYÁÅÐǨ2üÃàáR¸h ¼Ç¾î¬‹\xK€½ÉÎÿ ó¥.è¥^w¤lÂ?xèù#–ü 7¹„J¸<Ìágp4Ñ ê#×öÅø›‡ºg°Ñ$n¹‡ÀK{í»nèÄü»{¤r§Æ'•ù*Wm•ï¥iý¤é¯FÓú^Üyò¤ñ¯FãÉý¸só¤ò¯FåfÇEο ?àÔÝï® -^@Yáóšoá„jh o×莾 ~±çK¬Ó<_ú6tzà(Ö _Ëà[DOšÙ¢™%¤»†¿›ÝÂ'r^3¬¯M㓺ڇʀchÙrhœÈ¹Öý–mKpþ6(¶}&-$ø¹ô šñUC?ÄÐZºòvª9†‚]B% žï~`ðfH ˜îdÜ-º8ËiÄíÎì±hgp²ƒ·²¿ƒÝðÆK;–OPrxu¨_Øîz?eÀ¹mW³Ûã?©ùÐNóÕƒ™ ƒåŒM÷m“‘­%‰ï¥‡Ø6$¬ÿ8·{ Þ«þí¥¿Õ,n×vî*·ªÚ‚ö…`û}*î?»Êq¾%?çZlX.~ ¬š—ƒÃFÅwïU{Q|Ö¸i ŠÁEñ[]qLš©éý°!’:i†»Þ&žyýвӶCz½•Љ‡xhì/sÞ)èÖYø•$z¯P3_ëÆ¬jh ¾®ÁWžÓ˜ÝiStºtèû5èÎìo|ËL ·ôúšpÛÝëY›/1UÉ5‹ø |å6¾ƒ>ïH¡ÏêŽ2a÷ÅĽ$Ðõ¸rL,ßtæ¸ç Üðp$€LtÞù¹¾÷+@tùý|(Ýx!¤HZ/„(•Fº~ „¢ReZëB‡‘…¥Œ6aÚÓNñ¬Œ "¢ª¼++£ÄdUa éÐäyøw fq©•}„ÀŽ\a û ¾‡AÚÑU%-J%Vó4 “åhž3= ËäpµNš$^0Æ¥h^‰ßo&P”®g¬Ix‚öÒóqUCÍp]ñ‘'e\X™¹$34:#ϵ¹$-ò¤R¦[v”ƒÛIOÅðkåq¦ªêÄ Ã0 ÷<SÖßw.{2Qflý7±”˜Æix º'—û Ÿš…q°úé‘9B£ ÈÇ|‘9ìZ&‡*ÃhhÒ_aRZ.J¹W¼•@©Èúcê•ç…Qu™YS«5PcY³9©k 8¹Ep\罂œ44W½CPVÕï&R•¸½Z‡Õ{.;(qSð-_~A*Œd™c«>±-LgoyAŒz µz)^|AuòB.ü’m·]Òîgx뇋žk&zŒøåýä—tB©j–ˆêú¾·Zü%YšIQ¦ÖŒ pUæQa¼¹Ôl¥äºÖp[Wš“gÊëòêÔÚ³¬øçòî</ýX§Ð)l¹*7ùLÕÔ·‹ E6·'·|œÈnè;i€¼pÑxWj§×‰êýGó üvTͦuÆ·%ÚF$ÊŒñäú^þØõ‘ÿÖƒ3dzAsô©AŸz²y£ŠLLkgbK8»ûsNM^»sÁ²êì+QFÒ]K­¡ªî¡÷¬fP¢ž{ŸN‘ìËÉèWúûìðý2endstream endobj 100 0 obj 2578 endobj 104 0 obj <> stream xœí\YwÓF~÷¯Ð["4£e$Îá…²æÐÊCâ8Á4NLH ¥§ÿ½£mî•õÙ#É Nx¹Œf½ßÝgÌ'Çs…t¼ôoI '½ÛûÊ9ùÜËšýGqqÒûÔ‹]?ý“5pz8qöz Nâ&‘38îyn’ÄžÊgNê'¡£„ç*ÝaÒë;;ƒzŒŒÍý-vÏzƒß鯻¾'…ýךtCåý‡†Êm¯ uú=3Ô CÝ7ÔS°Úƒ‚’Q:ˆEàÊ„o‘‘Ÿó¾"ñû#"/‰tˆ¼"òŒÈ±mØlýJä…m²œá”Ès"‡Dþg8*I©'{?xÒ•›ò­¡ö õ3˜å! ~1Ô=Cíi!×ù\â€ÈC(%!V,è+©kh(¯‚\×ÖŽð18‡¬cÃ.`ßSC/àÂlÞ¿ˆœÚð½„{`D>%2$ÒÛ„’/Xݼj€Õ€×ŒëCņ7¸.ëB0¦Vw³†™FkëþÛPw EnÿŸæ²q#,ù¼bœ‰‰ŸD:Ruô>8ê½ãë~°mì`ñyJAÜ-—Ø-|IºŽUÛ­äжÖ:ä§¶@ã@ÎÃ8N{NäÞ,3*É“i>¦uTü!UØ4eÊ5V+“ÔÌRJ¤b®\ÏQ¤y«Ó*!• [˜6z~· u”ʺNMW4¤Ïã@7úIž,]Y kÌÇøÒS:â3¤£I?Œ"/ã¨Ò6Ÿ¦Ù,~û¼5ëE³j†KßUq”2\(W’Ï9ÑŸ×S¾Òq³æŠõžÓ#é¦ëH à&‹ME1ïpIþº“èU´D¥R[îŸu=ËH/³EfÖºzpÙêpf㣄“y¼5WÄFKÙÏ5éEJóJhió5û¼Dô]Î0s\6L›eËi=·°±ìÉR,£$ÖV§\,äGb)ýD‹DfJ­çD^`_ÂÉØÑX‡yç‘*Î5—"üØÕð–|Ý#Í}ÉÁNäyéžjºœJn¡!m˜N »qÏó•‘{)¹î`ÑÉõ£ *­kàù® •³[X“£¢äÂýÿ>Y²'†:¨™Íò£žD»»ªY…Z¦„‘ìÔì¨ÇP Vµ6çP­Ø4ló¬/í~ö¥iK¤™ ÆžªØc©¦ÐDܪkZżV`4Ð÷/UÉú0Uabqšr;E…±çŠKÍÌ)XíÉ̉Ý Dæ„ +–E¦s‡!XïG1,çpµÛS(Eú@"IJÂRóyÌ;æ† Õ.pj&˜ÇFdæX+ëË!›@/äÜ žìnAMÄ&·‚M.4Ú†xI)4%ÏÅœG¡V¢±¡˜Õ£ÆË¼…ÑÜD*å&‰ÊmŸTn¤nºP(ó}± ‡ìECÔ£¡zˆ"üþœ¾'(· )pûÅm*dS]ó%h£~”lM*)–i­^n[™{á½5Çš4ï+~ïÛ²°»™^¬%-&+láä)<æÄvxT½Õ›Ü©åiå~æäi$ E7+T¤_¿0·¼Ÿa‰,ÙeÂ×X†Ç¶=tOFccÇXð¤•ÕT!p£ З†<Òd˜ù­YǘÝs3Á™¡æEs3Þ6æ²@H…r&œŒì;Ë1§hV6ë:2éh¾Õô|3)I×Ж…"•cšÃÑÞXPû5èã ¬„šÙ÷HÈ"¶ï,Q(RÄ™³lÊ!¯¥RRFé]piÍ^“Üis¡ãÍ( {‘¶¾]”Jiùˆ:¦R%iM¥7j~W†:áÎ" –RµsôËXIôõ7`Ë`¢¨–6­Ÿ·±ˆk¯œÊ¥+§ÍïªVƒ º†¦‹æG†*×ÍÑ‘Ž'cµ t&dÞ†ÁwçmÓ—ÁÇb v )Üäì‹Ëáø¾›Íp&³Þp­F~rž"”c×( ÖпìÀ1 +yc„1TŸˆ¼‚}QkãK«U+0ÁCk¼¬½.m@—…w#š®"É,£CÀ·cBщÍÀömH¡(G{V£:ßò·a3Óntt&]bGøØ ŒøÊeø›Ñ3”ÂwÓ¸mJá—Õ8ì;%ß/Žs—-9ÌÉæy*Ÿ+oRù5¤òÙœÚW®LêÎRs: ËEÙŠ˜‹òÖÆõhÒáCM 5ªhiä¥up‹_ԣƆúÖ|¦6 ~¾z ”±fKMXì ïUv»Ø: 3*²jc˯Àiüþm8ì’Ö ›,<Œ¹Â1œš/æ1—÷ÄÏ;³xiL, ;DzJÀVKXí8vmxgl ’i“SÑ<;½Œ¯jÂb Í(icøºÊËÇ[‡pak:´Bm´„ì/ ¶)êj¡)1ÿ²ŸíE ¿ÀÄ!`ðÚ­ÖE+ƒ¬¶ø?f} F ÀÖl~¶®U™Ÿ<^³ÕÙ^¯Ãê4+Õ7±:¡…¯à>±¾²C[_œw,ʯ´ÊŒß¡ ‘hnTÕá· TÜ¿?vH•è’„Ü6AWÞU¯:Æ+f4X4tÏ‹mV@ájŒAøÎò 5öÄ›’9#O<ïöy¡—h‘2·RÃaŒÙ-®€ïטÄkÔm¢ÏÀ"¼ˆ]rìZöþ`±Î£ßõí·`[ba¶šp÷Ž+˜¯˜mË›æÊ‹|߃,láñÚÔ•†Cø )®m5-hÐÔ¼2ßâ§Éd´TuTÝhuäÓ–/°•1Û$ëÿ§àÀ=àç½]äíqµAù$”Ýý5‘·õ³» ؼW¶y1v§p†š)œ«¿d Ÿ¿3”g¨;†ò õÞ&ð65Âc`ëg½?Yi@s ÎÛæ¶_Tà'õÖÇü~C³´WJÌ7†ðJãÿŽyù5Àš?z–°8V^˜-"éŽ÷þâà-CP‚~Sm¤w*³w„­Žuµ‘›ÿóì)ö‰˜éc8ŒaµU¸¶ÐL‚“êO E°¯L3[@̎Ƹâ!dÎ ð³x£—òÝ¢}ì%q¹Øò:¡B2ÖMá Ö²_§²¹%Aè¨Bù™_‚Vu`åjšmB}ìû¬™|›ç¬g¬Ã7Ø·æZ«4øJãaG’ÿlÝ­TlÞ¢5b±¢ÖñEN@dý‡­½Wúï¿äû:Jendstream endobj 105 0 obj 2413 endobj 109 0 obj <> stream xœí[ë“Û¶ÿ~¿YÊX4‚éI;SÇnÍÃqÔIÆi>ètºG«;):©q&“ÿ½ >°KòG‘ºs3ÓŽmgvž÷s ÂHÊý­åíÙ³76¸º?+†ƒ7ŸVÀîêìç³,ŒÝŸb@ÂËÛàÅœê,ÈÃ< æ—g*ÌóLÙk¤ ýœ'¢ÐdÁüöìÇI0ÅJÓÏfò-abc3™{ès}å¡W¤Óbu9(ðPû鈴(Ê"ê\NÝ•¢<ž¬¼gpËà†Á;8W`Ü3xÃàíвKHxç>"©]Àe ¸õ¤@F Kÿ‘í'5áýiþÅ ‰Õü˳ùG ‘ùáƒÈ⇌1€àßËKKóLÒ 8 {ÚÈ N¸´¦oYRç FÔ/™Â[–¥ˆq ð‰›Kz’ØZtÜþ Ôg‹y†[/ëîÆCX1§C ëX@öb9*ÂyqÁ[¦)x¼2è—]ðÜÁí(l± ïTÈ®Øo-¦d§Ï[Hv#íökœ)‰kéŽÓ$•ª"›É ëJhR·–Mw5 Y¯Á« ¦Ú’·Î­C33†T!ÓÁ¬2WÙ’mˆLÝ9?nË¢kŸ["”WpÓS$¿”š™D˜=¿†w$$âzÈ‚¹mËÓ9@]þïx¨¥ßµ·E:;–z¦(®åhö•‡æ r}é¡·àë— þ`fjê$½Õ#â<'Sk˜Ü ¼KÚ Lx1tòtÕ‘–ùÈC|9öŸ¼¤V ¬2\Â9tJ4YC¢caKñ‚¶mêhÃŽ m(é¸(eô‘>è‚þ +“¨ u"–bÙSy°~;áø‡ÍÀ˜ÓÖ¶\LxÇFõ¦2ÊÄ’Ôe± û…Æy‰–‚öžãb7]¹±XE• Ïê-ÌÊ|ºÎóþ€TDž ¯ø ×n×÷6C¬àÅFýÚ‹*ŽDÄè²¥282¶p¦…±D£ÈÌ —|/©PŒ¼YcIAäbŠÔYxZJª!i3^RZ[¬J)–˜¼v ´UyäJæ@&-xA)`S‘FÝÁX¬¡Ô~ OüyûÐ?/äÕî9h©«ÃeÞ[PèáºW³j–*Kq´õ5®•q½ñp´Šê›\$Ú±%À‡JV÷p¯â¨Øs Ǹš€=²è¥ˆ"nÁ-!Þñ}7QˆîéÂYÏ× †Ô1 ³¯ñªs)´fv¤ˆdD~åû>äŽËE§fÛÏXTaÕ|û‰¼n]»Óõyû;´0nážCƒ¢´†$Æ^tó†õqçà²q ÷ã^ÜV®†x÷_WÃa1z€'Ƈǯ:U ‘.oµWé_ü?*½þ ô‡?ò,# ÔÁë¯y-KS§Ûï‘íÊ)šŽmÅèwOo/Àg‘½p¡‡óWX¥ P²ØÎ²f°é¢Y¥» ФnYÿ/g;ð¡ƒÈD¹lÜ£ŒLÙF©70qgêæ@:q¥>ÒÐcœ™‰ DëuˆÏ‰êô] †P~FTp‹ô'ŽBÅ%÷nýÅQüª«±í)tÊ€E+¥NW*jCéÊR䆸§â2 »%”ó¬ÀŠq¯öŽÍém{¢±1¶U½Õ8Vi®¬±å9÷56Ö)^Ñh–µ¥öµ Ôy¥†e;ÄKû`²Žß6áB®ui’S#4¬(7°^"#ÛWl–è!Ý×íZåzWiœ°µY¾ßj³-è¾/KÕŽbròQ­ÚBGÑÁsw¸%ÞÂÕË‹É{m$½'ã£ò41ÞøTühÇ”JÞéù˜ž‡›#’“®=*â­SŒG.°sþ ûtFÐë‚]õäxÍPé´Gg¸S-X$B†¾~hi6l¨“˜ Ú€ŽC£s;ùu¹^%ÍÊ}«/]Ø…TOØV›Å¯i{ŒQ RƒAÑ_9À…ÝüÆÛ‹ÊÚy€ëÁ§öX½©kvZ½©«Áñ¦®¼ÃG™ºÏ€iúþ½Æ[#­•i¬¶"¹òº¶ðv«º´(~D´Sð{D¸ „˜:hÂDï²á¶Á3§N.S>>|¹„ïäË#ê%·E¦x’µÝŸNÛ}í?>™ít¬Ê_pºT”{{Õ œáùe? CËÀ6³HdÄ…ôÝØ9|øÆb Ö#q¢>Ó‚Ìð¦¯G¶õ·;Hw°ŸÙ †bƒëªãli ‰¬Áðu+šnûª=4ìƒ>&ë¸Þ€_¤áÈW8&,ebÙà“¯ù  ±ìOÑç˜BQsq-e‰¸~'ÜªÊ þXï˸ú¥ÙÞ¿SqZ‹MiŠ×e—xs6"‰Êwf’8´IYýȲ’à_@>ÁýD¸åT©ŽÓŒS÷KPGê8Íw4âÑÊÈ×\=árß-‡Ë^2ǽpÀÁÓÈÚbQõ;EìϽÃtû©Ûó¶W›Õ—ÕøÍ¶1ÁÙC¤¥’ýÆá7}P)ê÷“£½=įP;‰[BêœòÆ÷ãáÝC¼ø×OiQâ_9Þju_âÕüì[úûÈî¸endstream endobj 110 0 obj 2790 endobj 114 0 obj <> stream xœí]Ys·~ç¯Ø·ì¦¼£ ær*ºlËå+]NùxX’%‡—)Ò²óß`t7ða1KRŠ’8®Šá!4_ÝzYäY¡¹ýg*í=Øo'oöúÇ‹ýOÇÂÕÉÞ/{mVÚÿõxùèlñèÀ¼X‹.ëêÅÁ˽<ëº6o†V‹E]™ÿîªESäYc*œí-«ƒŸ÷Š"«Ôâà‹½ƒ?ÿ°|»Z«LµMS,_­L½Béfù•®Vë*ËóBuìá”ʪ®óry¹Z›æÊª[^ w®©&{éõJ›ªe§—oVkÛœm£)t·Üô”*9¦§/¨xd^ë23\Í<3ÃÑæaÙð¶NÝßY«çô÷›qméPjy?ËFÇÃuÙðß®º¬iË\™¿»±ªCorUsqPþ–këÖ­©BEX±ä\q±úéàó=¥M?ÚÚ(ÓÁñÞ|4ƒªÔE¥yoÄl»A “cÃζ*»È<\P°æ1»ºô&(zÊzÃæá˜ Á½ö’†s‘R… (W;(ÓùZmå«–‡åYwíò›Uiô2ïŠåªúŒŠôw¼ Žh¼C¿ÊºªìÌSÓB^-]™¾Ô©ÊŠªVÌ•V|LŠÙ 4…ÎTUNJ3õ¥iÇöM±bÁ,Vªi |5¶Éµ®ê,/»Åz„.Û0áÍ¥+1yCh*Åjž»ÒÆáĆ7#¶”Å´p›Š7É>dÅÊ1¶RæÅrM³Ëp‰}æ*&SŠs)舚_÷ÉØUx2¨Y««å÷«µÎj­ëÖÀ[ ‰þdëZç:èÓ4ʪ˜„^1ø.”‘¦™Vu!…9jÓÅ yeµU3iÞ)¿@X™!*´þ/y¿Þ‰!8QvÉ3©N÷ºÉ8ÛÕO­^Cq%5pG3C]Ç1|'õpñ¥Ú1zQ˜)4*1íqmóÉÀªÉj5i FH1l‰J:Ó†XÙæõÐÂK’ܱÉwìŸëzú˜-"€¢ñ.ö 6ýzôÀyø¿à óØv—™=o‘šVØJ6E# ÅÄ MEXW“–¯oú”úÊ^ØÀw}b@¶qŒêÚClpEkP‹–ƒç¬oþ”Æò…ðÃD»O ú¤P†~¨2Óªk–¿¯ŒrÚØàW4à NüÂΆVm±üÎÀw•µu®1d0œ¸ðg+$qqd{‹À2íÓâÞ ÙXŽ0ñQÃõÄN3w‚gÊ,TeC8j!ZÄІWT¦5ÓF\IÀKSdu-IO0‘dfl鯱š÷Œê a9•„¾£+„Báoå¯Å·´ñÂÇ“sì{Û¶xËý½‡‹A&ÆïÈ1®€ ™a„½’øå¹[]0¥F1ÓV!³%z4¨Á^£¡cæl)k鄿–ëãã)‚Báý"ëÝ.ö”#T -E°7`EêN:Ÿ dSÆô?ô(µî"Fzî!Ð Z帄㹲ÀÕumé¹àlÈ];”FµY§ŠIi„,p§6¸#a‡¹C¿¯T—©B5œË@8™0Ds:‚e[`RD];0X9G ñk±°ŽÖƒŒºÁk&Î@p°ÄS¡ÉÆ[?bÌ¿þÔGë¾^­kc­*£+D-„1:h4xM7Æ' Äv1ˆÜá‘!÷Âs¤£•ˆbm8f{u½Æ\9´Gf b‡lFhÞñä]8C€)m*üÅÞºI™&†îÓŠ.XЂ!±ÉpE¿Û!¹ kê($¿ôÊÛYìSî›aic¶ßõ,åÜc+ƒÀ|F¬f¦ŒŸ¸$nŠ{N6ॠÎéÎçxD/¾{/ø’*H‰OFˆy̼(¬ŒVY[hafžA ÃôÆb+ÞƧäHÇj&#Ö¾UÌÚ–‘nSŒsŸ ¸ó™A ×^œtúVR¬ÐØ¿-yDý¼‰®ÑŽº&iÖªîqí­já#~;#´ýî¡)7xΘßÎæ<ÓÅ¡@ÌYÏ}3Üà ¯`²n;âÔ!–„“IQúwêA[|éTŽþ. ÓM &0¯q¤¤ÂLD<¶ç9·ô¾²ØóBßbS(2h‡í Æ™YÇ‚˜M?ó» ý¶>(öä€èI@DzhaâXÔfUUµýă}Õºíyã8•%³u™UMÑ•ËçT|JÅ*²ºO¨ø}ªÂ3ø‰ÇTüšŠ_ÁâSñZQ"hlýÁªÎs;ßÃÙ„"·”´› ØSð¸EìÂ15ÅbÇĄ雈þ2øÓÔ^[Gz’š›‡P²Ÿñ¹aÒê‰Omš·ƒÕ­ø†éì÷¶n(U#Uª¸T%ˆ±ˆ<+y"c" œÎõôUS°ß]ö‡Gõä4+mhKÛÜÃ4¿rݤ¤_cŽ`]öá—°“W°î¶‹?ü+TŸ hµ£¬ó̢ÈëwUEµU×Ó×,ð•µF¤#s5’þŠtî~꯮têJ¯])=4¥RtÛÿTóÊûŠ n]ç–°Wv?g»^Þ+Ð`f^ÁÆðB»L}˜ h•FCŒÑ¤0jà~÷6+Æ㎺ÍòN¹ó;ÂÓ¸3@]IB"N³ηêö ƒê.\Õ½}.æ'Á­Š zº2"½K‰øðöñ™ÏæÅòƒ=ß±ÃÔ'¤<Óæƒ ZŒW:s¥iÑöfXtÂÞ„¦G¬ÿÎo /Ô3XL2e Uë¸àD =Y ‹»ïJ?¸RîJ»RéJ?‰‘*V¸ªuªj½Ø˜¯`]¶ð¤ 7¶÷7ðµü0›˜“2_Ã×XcÁ¯Âan` Ç©®ç)@Ž0êReÆg!‚20çSÝõ¨rÓ ãŒ’>=r¥o]é“»j›²— Ô-µ Oä|º ×bSÆ”é%,~Ȫ€* àÆ—/Ü<|ó€Ë N€F|™5{Š1;±òÕà2\t€-ÄÊÉ’ýgU ›'&Oä µÁÓûVÀ&r—¨Ã.æô¿DIн[LYÀ/ú|(,ë ŽñÎÎÂ|êŽ:héàEÎ< uüÃv±Úý?j¼1Љ"¸£ØqøÛ)ðƒIÙ’^6c§a ÑŸ¦&cvºY ‡ð84õÁi Ã"OW<OøòwW¢ð*©Æ„MÃDÓ”‚ȉNLù|¿Líä—aÖŸ\ñ¬ÈlÙ%lMÍ{˜ò>MNyNⓇÞÔ“¹yoSÿìÉZ×èÉ%ÖAÏ6ùþ£B„jý»3ÔwåúÜ£UH¬>²ÄßaÓMÊó»Š ãJ OÆ{؉z·3àráqó÷¶©CÇÙÙ6‹%–¯"8®^vâ‡ò.y1r šr§x-öÇöúÙ1•þBM“WâŽÝ^x'ZÐ}¼e„èã>³×bU]Ϻ|Û¯ ˆS¥àÜž8£ç «3rùÎÿËãÒ…½V¬Ú%>çè„q‘š&£!I…Ö]äÒ ¾Ò´ãÉÓðV;1œ<²l MÎä ö-v"¸£ÞÎwOÅmÀAgìv…;iÁn¯ÑÈ{pe¦;I:‚{~M‡èv‚ª ߢW­Âk Ǭîrá*8ø`¿¬…™`MÙ¥:íe—RmÑW83âQ«užÕEÝ“–Y®º<×Ëfe¤:Þ*[÷–²¨¬níé»}W*ØÛ¼¼ –èµ «0ö,JSáØ‚J^ª’¿EŸxÁÞâ-Ðk/†¾šõ7¶ªxK6¿ƒ‘C“çuå¡i‹Ò^ÕoLÝRÈÂEt)S¨d‚.½»ñcÇ&7HMÑK0¿½Ž/øcÝç¹/ÓqÌ}*ò< SoyªB‰ãàóNUà$ÞmÀàæ‡møJá­ظÆè ÎÑOh¦[ ÎóIØïþmp–gÊL˜Yó¦/šgýaE9ø’fân¾J(ëÖ{+èÂï®ú L1ã-'ð)¾\É.L²‰gãdæ _´ñ/eå]2YKÓ0·,ª¿¨¨FNâÉééG£ö4™ég$ëDÝfE[ «÷)÷ffë—ƒ+öý©;U5<1Åd¬8ÊðsÜ^Æ;ûg<‚ˆîá+6\oÀLË´+aÎ ‘L.~ß;wõ[äû­X:¾—3´k¯!ã l0Áí¿Y™óñw†[ÈuVT5ÑñÞvKcgpÀŸÀdóî÷mß óMzJ œwAËhÚÌi8ÿX¹LcBŽS.!Sl  8 áô—Þ½QO8²@úAwBàŸ¦‡t¥\#wܧΠ5¼J.4Ï(ó{(R`%C'*ó.ಮք\-Ÿ[BZwÃYÁ‰?1ÄÅàÁ”N«g;øÕ‘b°³ÀòÝÌÍ[ìÍ“ˆZ°bl†ÒòV:™b¼þ’Ǧø–ècØTãIfš‚isýaÊ—`öb$0vw¹Ž,IàTÄ$0&NÄwâ¼bÆ^#ÌÄÆÀxNe¦1ÀÑ€ [ÕXu?a#*+ë¹qÄîúxeú-Ò‹‚×í8ùäÀ‹±4Zô66`·ÌƒÂ æKºÌ{ŸkRâO(ÄŠD´K´ð)ÁÖ-¼¬wáHL“§™¹yî"Ž8³iqaè唀ç1ñßùNö–ôÔ¶&åjƒ~ý|B9ú¹HbÉ, Ø'ßvf$•‘)ÈÎbUg—K¦ÙÀ$ÆóÕO j%ÆJš‰ÇÂ¥CaÈ…R@†5`l –®š25VŽ øç©XB’jßk“oaÌ&oEjiôu?M[—b )ǹHbußBÂsb©¡BÇ=ܾ©µŠ84,F-2 ö«ËºÜž@Ë|9m¦”8ŸæóÏ€ýúø$ƒ„N¤»ÕÔЉ{A)91ˆDÞÒ[q~ŠÒ‘°ž³§³i’nÒ­¶‚d”u{®9{‡´¢ÜGR¤lÔÎ]ÚÃêÖŒÑA£e¼åg‹¥> “QÆ2wT»[bc[sBÎ$@Fþº×6]º{¾+“üsqòë$&>h2ÎÖEŒW¸®ƒÆ”YÙtý8Fƒu“5ZÆ:U¸ª}ä¸S·vUC&ÌÜý¹Êþ<zX15™áÇîæxoʸ*Gw&a̺Pi—„´¤É¦•tgƒƒh?ªÎ¼¡# 3S%3Þ'ÀHŠ }^(3ïêJdIûýh<8ˆ·CˆË& Û¿RÔ2d;"}ÖŽ áý8—J!Óí²ËLf‹6˜hÏ×cSC?‚%ÄL,cT pmÖO˜gKƒAPëža\;ä&c{p3ZÕÙßÁ¹·™E ò½YjC×ÕtŸi”ÌèGÛpŠí¦¸¶bÛ)Û¡#¹²ñ-´Œ~[býû 6…ão;„3w‰'>†N1k7ظ÷·øç ’ŽžhgŽà½0ØÙå£cæ63^*ÓdªlÊ„ŽY«*AÃç¸e$H¸ "âyL ƒ^øZ ¹v"|”ðîp6ó‹2xn“ñ¾Ñ+é7õ"áV‹§³æѪI~GTèW¸®Ç6ËbR#ì7Gã?2B½%¯)Óúñ÷™T#%‘H Z–ÙaÍ^cEö¬…¼è‡·- àb¶BH‡1‹AƒStLÇð¹8Ö(ë÷¿Œè*c»ŠåÏàĦ?Kƒ‡Æ:ýR¢ñ‡dT•U•;¬ÈˆÞùfmM©—u½œ. iËËù&Æ28döÔ¹p¿¡Cdp{#᫱"ûw*Úå*>X†Ãù¡ÛbÙ’Ÿ™)úÍ"ŒSÛ8Ô–À)ßûh¹1wîèiîÑŒ­ø×£æ…ùšû£ìklBžäë5²*²¢v,#µ©2çÇ‹\Ñp÷|tŒ1I´à“l&«äJ&÷Xæ$F¿½„?0jˆ׌ýB_2[*cGÞ¡ŽQ18ûÉÝzW®TºÒt® lTÈ´Ëp+…˜(îÄ ¶ùp‡)iCzÐÀÕÁ~G•FK¤žºÅñ«?N‰ýPò+‚X ´[0*k«çlýƒs ˜j07h<‡¯ûÃÌ´mË,€qÒä¹[x¸â„ –¯ypZ0öË$hùÇÂôîµGçØAÛÖ6`€žÕÄ~„>ƒe‘šÓÙ¢¿AÄNPJQOÀ#D=° CcÛ-ÇTíÚ ¯ íL<ò±Ãnèk8sðþô< ½ª»þäLê7çžìýÍüóo_ sendstream endobj 115 0 obj 4941 endobj 119 0 obj <> stream xœí][“µ~ß_1o̤˜vK}§’TÙØ§ŒIÌ:¡ xXï®×†½ï.à$äO$•ß©»¥s$}juÏ,TR€yЪÕj]޾s×|»Ê3!W¹þg Ç÷ž5«³ëƒ¾zõìcáíÙÁ·mVèÿú ^>¾X=8T/ ±ê²®^¾<ȳ®kófèU¬êJýÝU«FäY£\|±>ÜlE5¢–ëWU-dÙ¬Omi¥JEU×y±>Þl›,ÏE]¯ìó·›m¥+e·>A/±çÔ)½>¼SÈÜ|³/²—XW×êûë[jõF\¨‡m¡Š¶ö vsãuú½¿Ùvª•Ìåú³M‘åMÞ‰õ‡›­ÌdÛ–Õú15eÅ÷©è-Ñ8;3¡Ö¬‘-Ÿ){áõ¦Ôµ]¹¾P_,UÓÂtËz}—ûˆ–ã’ÚâÏŽ[)‹N­—}í|óÕádQ*¢Ò$sx¢èã–ž›!JÉ¿Àº}AµÃÄô³¦xeÞmTI”yÅ»zÙ¶eÙQ3˜Jðno¢Û2N÷v†Wé_°ÛÖï7T¼¦bFýÒôj£&Qª3¹þëf[VY[çåxýá%Η¿ÐýÈY 9îIU:ÛwFû;t[ÔUÍÔ¹žŽúšÚŸ#¾¿¶ör$›"+C5j^M£À¦Ñ¶eݨ1È•þR%‡÷õY«»²N]_R§N(â«:9u˜@èÛÍ5Á–spìëôÒ8©¦2+$ë\ðuõ±ãœ^¸‚;ÅV7‰oŒv_ÑkøX³×ZªÍ£‡Ýí „Õö/°Ñà‘32wPµ=…c=¼hHq¢ºj [ 9…‹pãl„è²²PäÈáÄî1kʆu ÷ £^X¶AdÊŠ#O©»¡®bz0äa—yî æ‚w,=Àw<<ΨœµdÆ0êå@32ÏrYšq'Pjù§(ù[±ãâàU¡è°uñŠI?W[:nmcä—H"úx ƒ¦°#)øÜÚaÀÀÒAK d%Øê±q°ýQ“RïŸAȲnÁä´Üä1€öeÈäЀ¿\»XÄÑÛÿŒ–¥.i< €ƒˆ˜³K‹VyÕ`Êswk8öûÏ …2ÝW­x5Aw °³…3…#>,-Ú5`GFPñË Írg‰Ô~£äÇrÖêŒT:{uB*Õ˜R|çðé=½'д!‘öh£‰Ô€Ð…•“ò¼3Tãp}4È}Ó¥*¯·›Jõg]÷€T/2±†dø˜oÙNŠ]Îæ2º(€­*bòm Rè•Â~ÀÀˆ(ÌQ–>t â…‹ÕÇ€Ÿh¬™¡jíB·1=f «&“¢є,¡5€y²DÞÕ ‚0®`âiŒbìé¿oop§„þwޝx‚ú~Âéd7°3¶/Xw;6Ow”-µ½‚°:.¼lª@¡èÈò¯’(qMË(æ%UzÒRUduQ9ÒRÀ½¡U©p'öŒ$#÷Ü`(Ößoº¬i 5EÆëÙ§9pl‰fì ÉV§pŸØ©`ßýnSiš­ªˆvÂÁ6£ù®úµ—êÉúû½ƒ{Ïdkmv"ÏZýäð7ZÞªÁ*Ì/µ™ªÈªFíÞ!(ÑÓméc[Z%…Lÿ¯¬ÀËÏÀÓßÚÒK[zmKç¶tjK¿§¯4à(ºl>+Vû]a:ÕÅs*žRqÛ^Ã÷©ø?¤âcXd=ÜÀ1üHÚ^ù³lâƒ_È&Sñ ö{KÅ *^ÂÎ^À³¶GT|KÅw`—ËŸc“ÿò6ïÀIªß ØÃYjdlBWðøßàØ;¾AønÑ^SÊc(Î0vCBü•Ï0½—H¤&¹ý © äIøXbjj„ï1 0aÀ¶ üݤi×cÒmÞ8ÒÖqû¥§-óyayÖji,¾À>ðÑ‘©ËayV浜Q»ö¯á­¤ò•$¯ªÅÂjZÞž6˲y3-o͉»K¡ ÿÚ¬'Ö“Ø"1÷æçVÉ`•3¨õÎÖ(m;EpŽiO2y™)*$ Âðªh~°&ƒK㘠ƒ!ô ƒö‹†Qm3yLXÑS D©Ä؈J@ŠÀ„Q7pÿv&ò9úÇØ #ì41™Eã¡Íq0t ¨cËÃL-¬í)U¾?ou|ƒûR?Û`óç`u$]dŠ’ÆÚ0®S¸ŽlSÔ1wïbYæð–ÒÊ•&6ŒØâ§›mÕeÕtiÜffôAÏ-c[qáÖÈ]–Ä? ÏFÿm[ò'ÎɈ:Bx/OÔ]02TZ̤Z’ÙÝYÑšP£rôÙn“>£#>qåÔ–¶²‹9Šæ‡ÉLĸBÄ0¼GGP€À‹œ´Ð%÷‡ÌØ'uѦˆxcóCöµ¤›ÝãBdÀ‘'Ž«­k ^Š•™h[ow#GhóÜ29bG|íO„ú¾&Bn¦å¢ݱ¸! Œ¹hÓaCX ¼‚mgà°ÅÆû‹«áÆCT™ZkÒ ˜ù•5…ûï:³…M÷MQ…Ÿ…6³uàüÇõN?ÌÎüÀ0»„Ð2¡ë–Ö^×z÷Nޤ{ȬÙ1”dÃ0œˆl°sNò°cOo/s21wh¥½ù¬‚Jìs`ßþDÕžÊJɧÏMWǶš- @iñ1»Â8 Ps‡`Y²0'Œy vÃ7 tvU5Ažãø1UÌöh-Rb±³•[—PÂÀŒ/´¹Læœ@L#øÁ%c›Ç<À,Ê5“Q»ô”*ë*åhŸèò(”Õ›"`´7êLaB4bÀ·+úâÇ$±ï°Ð ¦Õ0¬xB<$¾#ÛZ¸8‘`$Îñ@Ê#CÈs k!™¢• U™ÚJŸˆ½ ªÉda%Hl›)]ˈ7k'‡Ð Z{vöÏx‘Ù‚aHÚ6ý¥ZÃ|l`d9ÂæŽ²ã\Ì*Ë;ˆA L^\60P`‹ëÚ±Øjð±±Þpps£µ1u.8‘Ò ±Ç‚®H›4’—ŸúzúwŒeõyÆ1L} Ôqˆ+°óÏ3~xlN‹Mo`S,‡A(MNÇBÈ’â›®!f°0:L±Ã%üßÑ’LX[[ Cr¯øzÛy hÙ ÞÄPìÁbÓ¡’Z§sđĺdk°¸ƒ fKÂ)§Ö¦ôYÕ|_ÍŽ7#X×QÀLqf«á‹¥ŠœÛJTŒËŒ® %åyËÑ$U:84+ÌS猬cÂoh—˜b¸ƒèÉI*Øi»b‚¢ÚQYK ež+S£>ÞÌÆèôˆ¹DòÒÊ\2 k¹èÜZÖ:‹:0“ì~5À{­Ãaí<&X'÷Ú6xgËF“dx—&õLrŠxÒeBŸ­—ƸJœªPCY)»o°¿ƒ×›ÖNFÓZ—Õµ…–‚CXöœ8ó8ÊaL”*¥ªá¶·ç$þÀí}˜<§$®»T–¯j}lâôYBHà¹Ù¡z®›HËññô}aj¢ž!Máxˆëy$&D¶‹Æ‡#Jß×C=`Z¦îl÷³ùWdäBÖ:ÎØú÷ÁÆ3µnÍtˆc«'‡_ÏŠoý̆¢=Qs+PGoS÷Ô–¨¿' õBÁuŸ‚^~gK·¥‡`¤ùßÐñaÏÁÆ—m³þµÌ©(©XSqIÛ¨xHŧ :ë`Ûa¶u«`òÕ²@Ú‡T| üh&oìi³æ á>ðâyîÇšÖ#PÑM‚U qFÅG~·Å[KflXˆIS¶ÁTÀ¹Ì½˜„9ìì¿ôy|f?«ö_“iËR-€â9•tá™é±m|I ¢“À —Mx<˜ny[œav¤~×*öS6¾äF2g܃ÇzDévߘ¤µ¢›½šÑÙÁ7§,Mè²Ùq1c0©zù‚=Tô²ÌÝ}i £fÏ8>Â9@ž¢Âèù=^ôÍ-Z,ÄEF“ýôüœÀ[ŸŠ“9²çdá» ë–öŸê Š·(äkcü½aAfen æ—p¹û¡ßÛøLc–Ý™ÜÕ·­C.ÎQËR{ÉÎ.!IžÂÃãTûÉ&?çqʬ6¨Ô7@gÜ Ã'Xá„áÓ9‰Ð%Ü•IkŠà6"îêÜ’7Ðï¢e]²Ÿ¤›ÅhÏ®ÊÃ({'ÌVs/¹Ç ìêAHg• B­Ö5QC®Ñû(Û¬ª-±°ë Ó0Z‹ü‹ãb¶‚©‘ôÔ1²z^»hPÖŽqv×®9ÜáÈ{mê±¥g`àYˆãÒR’hã!~ö¼+oPdËV1d=÷&…g¾1V'=om¥¥œìhÓ=—ó:rç¶„^¡ºÚ¯Ó–Ô†Š¬¶…µÌšËržßP‘åγ$x–ý*•wóìï4i_øÉÖÂܱ`,Æ(Ÿöšü‡ î°é»›nRâÊM'/Ç•-½^ f·—|Ó\9Ö€Yþñk¬¶†µÿ4¨É¦¨•¢ßUtÇäÝPãÖô»éÒø¾Dä*D™ŸGh¯Îµ€×ü²±Ç¿uÃ{¸º±ÿ¬ó¯«»ç©Â7€0¨b|ÜÀZX»?ǰ^D.ÄÈ ü§$á0ÔÞ•nÊ_yÃÿo`Ô6°‰Z{¢Ø-è»óÇ˽5=o…t¸9¼qˆ/»yÁS‘i~Ø}Lwp?'ƒ233³< 2ó²JfœžÌÂ@úè„eW÷·ÿ-2à.þéÔ¢X‚,²m,‰Òs¬.Èë9#ºr°m(j `1£ÆÓ\œ´¤[&ÓfÓ–܆nܵ=á+Ó÷áÆ6l#ÿU’ÚwÌcÃ]7PË.`’/³fðüv”G—µm4ÀZ=o•5v 2Bì{±Š°•£ƒÌ»ÆRXÈ€ÏÙ½ÅÎ[¨¶†µ ¬mamO&Îô®3ÜO=q‹ Œ¤\p?.¶<âedÓù‹a4öưÚ5‚üé +g2X8ˆÃ<Ç̳Ìö_À×RaW,‹%ÁdRv_v^Ñí+ “ðm±žÎÑW“q²ø~ä¨[€ï‰\½=…YP¬kPð; fóÏ:ðÔä™È]x‚!ÓsŒËK{@äó¤¤>iyÁÊ‘û ½ÄRæ þ9 œ¥Ê—DNÅ"êHÂ%¿‹âlÀ›Ýd YóROÁà€îñ‰]’7r —!™½ŸŒ!ú‚ŠK‚ ñ­PIôf¿+ø |™Ó”'ÖwU;I3=á”MV7VÕɬƒømPU¥äÚ6XðŽ't§¼6 Ç6SSrzS“cÌÎ.ðð8žòÑŸœQÝW2± Ðõ.Ìí…Hú›Éð€ñ©Íè3zžÆ;Ò_›zQŽ#Á±¯è&ë|ls WA¬ÕÖ4H%SÔûdSL_#LùÏA/Ÿ‚oPɈ&Ö?⡹›‘>#¸ÓÑïž©•#”OïœSI9©‘ð—½Ãì«ÚLMÜã¼KÙ;ÜI¨áÀpŸ® ¹ÏÂk+Ù'±ŸSªàg6ñNέ…LMqX°ÑÅ@Œ1ƒíú6¬!è8øI‰y6¬­+1Ó’pf%UJxÕþñ² éEïñ¼ZG3ßòÏ©ªm&¥7%o­Ä:šŸ ZfmåŠ,<Ê(Nïl<Ë÷dœF~L#rûâ%z¼$±2™ëˆ-‰Î¶ ˜µÉùÁ,Oaéƒë0q1t™Ñ¯ ÛLÐg²a ›3QvB OÁXìX#Ç1¬˜«eDSÐåÓCvo~=Mt‘,ŒXŸÂ«±,/–ÅbL0Є“¸`m‰›_ ? dÌXJ‚Ÿ¿>ä©{}hž3Ç M‰™?”‰òxAÏ;çâ:`Š«J,ÉUÚÊÜ/•ãb„~mæ=Løaß*+þ8é3dòæ£Ãƒ?«ÿâÉL*endstream endobj 120 0 obj 4837 endobj 124 0 obj <> stream xœÕ\[sÛÆ~ׯÀô%dÇ„±‹{§ÓGv[u’Ú‘™43m(‘º´©˜Tœô×g—=go± eÙªäñ{=×ïì?Q(déß–¸¼;yyž×»“êqpþ—†øp}òÓIÆú§zÀéË»à빪(DP†e̯N¢°,‹(¯[A–ª¿Ë4ÈEæªÀÝÉ?'gÓ(ŒÓ,‹âɞȀÈÛiæ".“Én:ËÃ(YÆß³§¬…"s!“|²™Î„PËÉÒ<£·¦³T×–íëXF¹j^×ÉERN¶ôôžÈ‘¬…EÓUaÍ…&°¡÷×¼3Sô6»¥j—j²¼s6æXwû¹‚ócÕXÙ!D2Óµ„@.‹†­Õû=l€õЈMÆVgVµÏÿv"¥zP¤J}æK¥-L0늭Z†¬&%œy=ÄL¤ zIƒYÀÑbÝXB…`ýW•-oìI².6P”^`½1>6d’¸XzCÜÁzq7ÉD7Š©B†y¡ØË®àÈ{f×·Õ‹_§² ¥œw Ȱº©8K3e6ê©j6J¹€7ÝqWeÏU»‰žX2yS«M\„e$Zµy?•êúo®JfZô`dž+‡”ë ³$UCTЦ˜ÊºÚ…q†ÚCÎn“Ù˜’.µnKöõOû°µñ«Ž»êÛ(b¦ò12YæŠíJ"‘¼'ì¸,gãñ• b.©žp’\ƒÉçíi”ô°Wݨò;1u{eµ?Z×zS:¦ŠƒÌ#©ÔJ¤a’oär×H³WÔlÇdý,·FÇ|²@Ì¿Ç;­[ãn±çfýÖcÍsaA޶ò5rLÌñP7Êá+߯¦*ï󃢖WH•ÞÓ(Y7\Ú÷ ãcXË+èFÌnÿCÀŒÉv\= L`½eF^‡³8,RΖpªDãíØ‚6ç¤ÁU|?££R¨?[Ê Š¦&³I&+þ!É”ãEÍ@õ¯ñŸ³v"ºb¢'£^Ìÿ£’Y˜¤A%E0ÿædþû ïÅ‘TeÃã0Ícâ[jn¨ÀP§†:oÏA+C-ˬº•×(óP©’£zùÕIc¶õˆ_i®feÂML‹±ìÆ<¤@#t)ñèíq8š‘=âôંy‘ƒÔ¶{áI…ãT[bmdÂì±åÂÓs·œµ… ¡È† 8ô9Bcm¸"Œ(}9ļGæùv:ËÂ,Ióròμa³Fë°gfZRY+$ò•ö"X¹¬‘+£áBŸ ‡·p®”b ÈŠEô®ƒc¸k U±â‚úíùÍ.P°€p­0‘j1¦|×S«ƒ$2­n%¹W H TWWÈ5 Cä’¹®!;«sOfn­3xìCß&cÌSé÷ƒÎÖ…Ôe‹(ÏðÛüK1Øý0èQü‘pbÁ5 õ£ª1.ÉA¶`γŀ۩öJEly¶¾bAµ#‘:HXgש`·3ìÛ½ilä&]i²í(ýdYÅ@îwȲž!_13ÌøHcá<®Þg‰´ñpá•áéŠQª…£¸Ì#H’µ«ö-š€÷Gå]Ÿa1Òpïà$"Uœ²µz°qÙ;Òúǃ›ÇcÊ¡$ª½X¶åú 1#¬MÈÊšvIë>wêðÔ}k¨wž„g …$c/Êð6GãxS óö*™šw9¾é)ŽW”-TÓî:ÝVÀÑl׉,Ô3£Ãc DÓ‡ìíà°ëV¦~Ju¨JYÛpoìy!"_¼l)¼üĶ3_É\g»dʸKƒcVåÛe6óÆaêì €ä‘Èýúå…3€X&ŲލPîH¯_Jæz5¸´A;¶CKl)Y#~´o‚U²ƒ®!Pfðym¥H~¤éŠÄ(ÿ© h«ØlÁ]½jA«L./¿ö¤7N°³8¥­5Šú À†8jÕ6«aU´;,ÈÒö9‘~}aε„åT˜¾‰3 ¸÷¼UÐ+º2{©´Y¶ï(ä!×9àÍ©í%ü¿é]øúCWà•œÇÀ°‰§ CÝjÅÔ,‹t2•óêŒÜ™Z·†úßø–.뢬–¬Zò’+"7Dî‰Üµ¤$hú1¾7ÔŸ­¡ ©ÆHǤ ŽIe¢²°ÂšÕ+Ó{"O‰<ƒd@äÏD.ˆ\ùù@v^ÁkØÅr–UÃÃ#cÂÛÀÆî‰üÛ½Ó”|;ȇeW: €Ò‹6©£ÂÔÊìZ…òеÇêt¥GuN¤€dçÅä°€O`µò–1éšÈ[_Ç+г²â1ž°ø6ìcžžÛ쩤éÍèéä¶¿ú,d=ÌWW7>{d°Ý¬6^ÌЙËY>+9®7`a—Æ;Þy}195çoŸ•˼ƒ `ŨÊ|ŠÂÑçõ´|m¨þÙÓ™à뤄 ‹cæ>_ú¸ù,æéö¨ç,½#ÑnìVÛw™èuSÄLŠF¯ž+3Ù¤?@y)éˆQ°]¬éÌ@–ÄŽñR}!=Ñ,!‡°cñrþ ’ú½€íJ"7pŸ0sðÓ7†¢åùŠöwZ¼PˈSš¢8f\ež†¹Œ>®ÉãÊÍ}ÆqfxðwCÙ{,¼Â<<aÄfu[Ày=†WLuñÈ®a»Wß PÍkûZÙÕÏDZÙËÅ-|zÔ­jÿšñ3[‰ú#t)‘b OG:Òà76CûNùÙê­7À®û‰Ï,Él¼ø j·Í¹;?dM,6µìœÈŸqæö†2ñ13ÚÁÆöð)Î8bÑ v¼„-°±-F\kXöˆäœŽA~k(Û‡¡ÅâÄ'Å%2Võ_!+°ð’?FY+ИtàlÜŸ4¹qcáI½5S"µnä– ¹q”„YQ_ÈU%ô%§¬b{*£0…¶Ie†e‘U×Êâ0õW®dRFZÊ”•Šúèe!DYzÚt󸨮óúÒêÂ<~­‹"­·uÌc5½Bõ¬~ª=7j»e¥ÕÖª.—[`½o»ÛPËšŒ²¢-«·úÎꎋ8ʚ²¬èéE²¨NG4O÷S¦qÙnê¹ú,‹Bµ£9=©Ž/YwÆo”þ`nÐ}ƒ§³RïÑEÖ-ëÜÐãÎÊy{»OÚž¤Ýp|-¡»QÊîÐ…Ö5; ¿Eûù ì´5â  <‰ƒïi_`É<ÍQ-Ö. ñ¶U’ªŽRN!'½ üJóZŽõ…¾RŸ¨øjîB³klØÍ…C™…QœSEÖþ½9çÓ­L²8T±÷˜KY3¸iKÿ…pz}É7£úX`iÞÓÞ>í÷ßjÝíÚññN3+ðø­óñK7PE,»ãŒÎgž€g8Âë8ïÁ›@ãq± î¬`ÜØ-ÎèÅÃàá—Æ!ûXý#ðÆã>ÝûF†%€AúÓHVJÖ)Ð_T ,¯f¾‡­3/ó#‘l±$‚íâl«ÏøÕ°–™cïÀ²C°t6ïÞPüc¥PIøˆhéÌ(¾{që<Äù¸±ð8¡ÜõáHl¡¨Cn‰Âë}f \,ëO¢ù¤¸i×ûLËÀyÁörY&QÆ.—µ÷ƒ †YE½¨˜£(þºLSzrÑëg…VnÁ:p`öNº^RI÷qQëð¦u¯¦‘Ñàï+îBj‹l.øk÷iFëLíå Ç˧9¶â’|AoÃ,òa·ì=´Ã<Ž>xÍïƒà£`£×ç÷±’†O­#^({È—;>ô l˜•ÅË|¬¶ÔŽƒÿçÕúñküG¡h’=©ËhˆüÈóE»OžKÉ/•K9`6 …01‰ìïÂP+Cí E¸»U 箥ë;Ð$¹žÿƒ”Õãd7ݹÄèbŸ³‹¡ÀÉn{E9‘¤ñöoxW¥·5p™–ÒŽñ‹âìK” à‰2(IyË%,êû¾ÝÐØªnE>ø> ÑùŽ ¸ŸkƒnàÅ®‡|óbp뱯‘â 0¤m}Ž–|=Xg>™˜»èú[ôZßxaü¥{³ràêí)­¤oá î!g{Š£çÅRCðÁ<ÿA*åú'1N:P‚…¯‰õÌb E¤a»îÜ‚ – YfÑá_sÍÕ,i{?ïcmLð[¬®ð¯Zø?-ÜR£>-\ïᙹ¾3æòº¹c©Ü]ûPhÔÔV4|ÙÖ1­žGÀ{bþx§­ùª¨÷ °Úˆú÷C+¿ýf~òúý ¼{9endstream endobj 125 0 obj 3487 endobj 129 0 obj <> stream xœíZÝoÛ6×_! “ÖX!)Š”öÔµC‡=t1 H;ÀñG’Á±S'iýßGÊ&ï$Ÿ-Åsêpûò }<ïw’p_B–p2ûßþEpx¤ÃÓ« \~[€Ùið%È“Ôþ+0î_„oºf#ça‘*쎖EÎô\+Ufþ.²Ps–h#pGݸÃU¢¹ÑYl–¹:z”fJ±4º2’EÂtÊ£ûX‰àBG³ÈD]ƒdÏo¿‹;:aŒ+…5ÊMšË"𖢩`:šÅÌÊŠËþ§ S…”ÑkþŽ;Æ&˜¨Ù§£KÐ7Ø_˜.dtK»ZHl,«öÌ•¢C@²þKvÖ¢ï;öªMîÌÓè6.ól~’½nZD¿Ì•¦ŒýŸ»¿F:a÷Àüý“!_äž|Crnéþ|lU¤I¦Si¨shâÑõ enáï`14 ž×#:/=zÔ÷èÜ£±;f•¹°A*¢Á|•©ÛdáÀK€c€=€}€C€äêൃ_)›’ÊÚëE·À¦È=›–¸Þ=àî < =z±$·pmÎeRf™¿k/Úûͪ\'¹"â œ‘Þš‘ÎIÏIÙ!©¬Gj8 @zinЧM‡ä5'¤9÷çҥ鱧áˆàøs…ÅtÂË M%§Ü"¡´¯¤WîÈÕAWä6:Ùhhe—MzÑÝFäj#ã#ÒÈ1 ‘Àm›2`cb9V2‰`¶Eþ?‚ÀÛ¦d¢ïM'•¯TV|ŠÖ¦Å§øy¡ž¿_ 4:àƒ¹uŠeibøØÈ^ðIúû!ñ'£—íÝœoˆõä€&ÏTð€æ¾oÄ <"šœèÇ?ÄÕ²Édyµ.YÌeÚºLc±%_¤À³ø€@³ö¦5RÖ#Žà »vQb³N¢NL÷Æ› ˆü¶ÄŽŒá†P¿{tB¤xòÅsqã×í™ÿW{ ð,}ß ÿWÞ@{ï×{¯RŸÆ{”+©ë©¾ÿѱ ÎoŒmê±Þl¤:Ì6œßعf¤štbòëúô! ÍŽóhÞtôfŸGË£Uæ m#ruFÂJ³µÑ"Yžè<YØØÝƒŽ[ë,*ñ À‘ÔœÍëÊ!öÙ¼:›“w¢DoPn·XGowTÐû9ýéŠþö°ûò@>3­c¦M¿~l†nÈÂŽûíÿî³ÛL$×gÝ'Òóê³²Þf›“x}›…Ø'ñsn³ã-VÇÑ»Uúã8JGôîIUêÝj1mº,ðctCvÿVK}ÝÛqB¹vû~ItŸPkj×ívùµÖ……!n³† 1°Ïç'Ñp+ÃQ\Øy+7láil5ù@Îî ‰ž‰ŸpšR£R°ø `¨ M:!£E4ˆåçZ !‚<Yð`Óà†CY=¬Áoƒ1¦ùÙ™6 ÜÏCRkˆBGÙêëdÝš°…Ú4R‘Ëe¯/›Æ¼ i¦3áö›|´õ›¸bEYÙn •r^>Ž·â8¿:¨³f©GS_—ØGåœß”ëƒ$òñ…íEÆY_Á=ØkáD/ðÑŽáiVp[$„ñ¡*rü;Ú…¢wìïÕÃÔ&ðPÆÔnCû9ÁŠ•¡¼…^WDÚ ì[DJ–EØaÞò[ð‚éf1Ï edmÌ‘&B1SzÙŽR k­´cš©‹åTeÊñ¬X† °¡jž.˜=ëdѶk€gàŠ…Öæ8mÍëHa4ó²ýÙâg¼‚‚ÄÅRª£_› ]aÞóºHµr‡\Їƒ`ðEß´J]J¬ ºêÖ^]eŠ©66*U¾"ænÈøEºÐ •X'Ê·õ]Çe©“©áÿ>æö!à¤>ýYŸ÷T0<ø®üiþÿ?ý·endstream endobj 130 0 obj 1464 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 24 0 obj <> /Contents 25 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 83 0 obj <> /Contents 84 0 R >> endobj 88 0 obj <> /Contents 89 0 R >> endobj 93 0 obj <> /Contents 94 0 R >> endobj 98 0 obj <> /Contents 99 0 R >> endobj 103 0 obj <> /Contents 104 0 R >> endobj 108 0 obj <> /Contents 109 0 R >> endobj 113 0 obj <> /Contents 114 0 R >> endobj 118 0 obj <> /Contents 119 0 R >> endobj 123 0 obj <> /Contents 124 0 R >> endobj 128 0 obj <> /Contents 129 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 24 0 R 31 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R 83 0 R 88 0 R 93 0 R 98 0 R 103 0 R 108 0 R 113 0 R 118 0 R 123 0 R 128 0 R ] /Count 24 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 22 0 obj <> endobj 23 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 86 0 obj <> endobj 87 0 obj <> endobj 91 0 obj <> endobj 92 0 obj <> endobj 96 0 obj <> endobj 97 0 obj <> endobj 101 0 obj <> endobj 102 0 obj <> endobj 106 0 obj <> endobj 107 0 obj <> endobj 111 0 obj <> endobj 112 0 obj <> endobj 116 0 obj <> endobj 117 0 obj <> endobj 121 0 obj <> endobj 122 0 obj <> endobj 126 0 obj <> endobj 127 0 obj <> endobj 131 0 obj <> endobj 132 0 obj <> endobj 133 0 obj <>stream xœYXT×¶>sΉX‡"ñ4(b‰½`EPÄEEADé0Ì mS§Q„Á‚± –0Ø%v½Q£±äZ’è5Æë»Ñä&ëämîûÞ>3ÀÌÍ÷òÞûüd8{öÙe­ýë_ Ê®eccÃÎŽKJˆŽL%¸ÚwØfá Aø}”då=~kÏ¢6¨‡-êa·ûc÷‚²/´ö†”>”­Mrqõì¸ø”„è›¶» Y¼Ìsøð–‘1^^^nëR:¿qóLŒÞ¸ÕmùeGdL\|läÖíSÝf“Ù11ÑëÝ6ƤÄoJt‹Ø°!rƒøZhDLä7¿è˜èøø¸nCg{º=zÌHòclPt캤D·À¸­qn ÜGnLЉHø·AŠ¢–/ðIÙº~yà¬Ô¸ +‚fÇG®\è»-*xNÂÆE~‰›ûo^27ióÒy;¶„쌉Ÿ»nÙPÏaÃÝFŒôé'£F›1cÜxï a'­ž¬÷Z3ejÚ4ôé5’D­¤R¾”õ)õ FSs¨)Ô(ÊZDùQS©ÑÔ`j1åO¡†PK¨¹ÔXʃZJÍ£ÆQC©*€OyR¡Ô|j5ŒZF- &RéåT 5‹šD VPAÔlj2õÕ²§zP6TOªeKõ¦ì¨>T_Ê’RáOõ£dÔ@Ê‘šF9QÎÔ Ê…Š£fRý)oÊ•ú˜ZE  6Q«)ŽZAÜM^]Dݲ™as¥Û”nßÛ®¶}hÇÛ­±k“,¥=èZúf£g~e·³¯?šóÑõî㻟³¶ÿ¦Ç®ž®=+z¹÷Êîõ¼wY§>É}õÚw[ß;¡§¥=¥Û¥Mý¼ú©eœìºc”ã}§éNNmÎ œÕÎ:ç“Î\zºŒpyÖß¾Rÿ3®C\‘ë“c?¾6`õ€{\7.—»ÊýÊGó…üE·9n¡nj·{'T Ü7ðê á]/á2 IF˜Ò ‡šlaƒ`=¤k5êÊ*…:…Ç!tJŽ2#S«¬åµ'ÍŸ23Ä'ÁVÓ5*]f†RžÊáñøcZ <ŒpÆh£u0¢lo–]Á«Wa4mШ«*êd>ŠÆ+ƒ¯¥Sdøuû·'Ibè9yC' §kµär¤iôEð@™Q†³h àŒ¤š&;ã f¡ÆhÓÓÁ &ÛÂQG_ì.ÁR²­"3S« ‹d{i*+š>€­P#©²,‹méTókøË4ž€_àÉðBò…Õ †NV¥7mI.ûÒot#Ô7;IÃTa‚Ì[¼‰8«–oÄ/Ãédóªœ,ׇrºÁ¨ÓïÒ³ÒÓ{?;¤;îú92(÷&ïßQ™€bYŒ¢Ëí»Ñ5æC¤òÑ ¦ŸÌ¹6.pã΋8rˆ4#ÂrŠF'éqa³ÐCq4´.±¸ÿðØ÷ûq8ݹSw´…ÿ̰«U²Z¹6GQP˜£à–‡ÍOœMfv›ùðŸ`û¨ º=o-å¥÷R‹Ë•U®j­—ÍHïÕ*µYœôxº2'…ïDCâY0Æ‘O?ðr’¾'`”?d¯HÔèÔ•ÕòòTѺ]8:Mã‘x «$§DëVV™¬ÛM´®ˆÑºà3q0F’TZúÆ'‹7ˆ§Àfð›%ó-ž»¢SøTº Ý­F¡7÷70]càW‰¶=ÉÛ ž–e£…¤zË¡†Òx~ÇÀ{ÉP™¢?êÛ“¢-PßÖ™ÞNæ}„$ ãð¯3<ˆs"IlõC¢ÈhK~‘…X¡ú:ýºíÞ‹¯Öž›t˜¿~xÿqt}îóå0ë-›]¢8R‘0™yp%|Q`К‰<öÀõ2HOæ&jT6$ß±g#ÚÈ.YáË™6^}H€8ˆ#°Hâ0ñ/#èduNeq*w J| ¿‘lº¿cˆs32‰g9éé%$á*¼‘˜°%ؘŒ×œý¢0[†{à޸@PÿáÐ úyüîǯ³ûç³)Ÿ 4ÅÝ}ÒÓï_<ý™7½Ÿ`„F°«Û]çp¶„¦õÐËIš)¤ïe#-¸„åôÊÖÅGf(Ú#;ðÒÜûݰtýè×g9iÆØ·L—‹¤™KidïŸLq'3=¦Nñô˜Nv}òô\—Ë0ûNÓMðµ…‡ –/ì–4Ò5:ME¥\CðØÓbá&Å;ñHØ)ù\ŒP‘ù’M3:ÛHÃ\¼[‚£2ДZ@3È‚´ŸpÏh+8 †FÆnO‘ÄÅ |[ûz_Š$éTójøK"¢¬ñX£ïBÔz®µu¸Cm´ù’,Ÿ ”ÊV^¸oš‹n ‹X7±“ì ½qoøøÖÍ}gÏsû÷UÔ «ÏÑȹ 2ù6?bäî 'Ÿû¿½ÛWOCƒJ¸¢´rE%b«Ô:FvL²ƒÚÍÜ Ÿá‰Hvä‚Ô;p'!^A³x…šòD¸V§ „ë®Í!\ë %ôÙy—c#úüøØóÒ§`?ò5îã½<Þ/”¿Ë²|XÞ’‘Ið~oæ klÄÑàøž,9ˆ¿ÌÀ !`‡9gUc{’LH‚O˜KèxúÑMÆuû¢Pv3hÜÐôæM#üN B‚ò°^ʤOo‡…žáŠ{í±ýßGA¯'WÝ9Ãã’¡Lª9•šÜ+}k0;8™—¾ j.šF:P;’–>Åþñ$BMnê1¸õIÛO|gÖ´(rµR’¬ Ì*Æݤ/œ>zg·1¯`?wN…P1b+È6Z•&³˜)÷SG"vÌüÐY|Ðx¦+±~_zZññ_I˜w¤YÜ‘§¡ÄlÒgd_/H“Á4è—mo~œtZn{’5GgÐ]·Â ivPÌ“;Küýf/Ïw já·"Ó›×½ù7ãÎ&'é² ai÷p:Å ŸZœD˜ee(̱;D2Ò8ãI§ªå•bÜqÀ·û0ri†ãéZ•63Ý„Ó ÄA_1!»Ï&ybD®gÅXÌ2EæCür¸éÉ™w ¹hôUÙ" úÁ6Zzü=t;½÷´ëT—½o»ZYRP.Z]WA¬žZÅoÛ¤ÛŠ–£à¸O'³€Ï—Føáôž"|á{Ÿ;«–žåŠíFŽÄ,f~v­ß9Ëcã¿4ÄŸÃÑ1t :Vsôø¡†ÊÃÈÈ’E†^b¤o»ð`‚ÑôxÙÛ6£™ÞŸŽô!0jkûQ´:fŒéMÂS£ƒ)öHÃ5âT®ôW!˜ÄaÐ œH0v†£Û ÜènL€27O…”.äºÒ’¢¢RîdÅWúC¨íÊÿLNä˜ü >—kó_×UÈuÙùyùùü–©Ê()е¬4Y»½d«ë”^£bÏ /eÿwH[i÷fè×dk ÊJ <23»à_y2©åòªªr36Bþ @©9õæ}M”½ó4=ë$Õ ¾‚L¶épxõbÄJýÌx=ñž+ôøîÞ»–í§"÷ñ1†8ÍŒÊ@]¢:¾&±&ý:ÄÞ¾yáÙ“›á KMØ/WVºV–kjùŸ±†6Yó4A /Ì#ÓB¬0;ÐbƒÕBÒ>¢Æ$¨°·ˆ€K8×CäE«o»[è¾ö¤pz§¢Óʼ% B!îÔí/Íéì³æˆNÒíÂ|ÇG–*Tš-ô 9ÆÊùU´ôôÂ% cçº._o<ÏÃå–öùvÚÅe½sãÀ¥+\W±.:^Hn9è€ö¡ŒÿʰyîgU±Ëè×·n¿á…E¸Ñ4“HÌìr|%·—®êr|gUôÁÔfð"Àzh‘×,&M›]QUN¼tû*L3)ær¨–{SÛ“ýÏêÞ¢ÈÌ 5ÈÌ&ë0l«EÆŠN¼EC«…“ȸ¸Ðn£Í!ˆ¬³…WŽ0Eè!i²bïá¼ß¥I¡÷R²“ÆžðRrÛÒ=°žd¤aZ{Ig0ƒXkK3©àä$m‡Ù0Wvæ©©çHâiéZÎçd>ŸœÜ2œÆëa-©Ž$gii»e/GË^{hìƒ=$‚ý‹¥.¶B-3Þšá#¨Œ6ïÍ•P±W£©¡T­,OÙ*E¡J'æÚE„Ékzìœ9ãÆú}ýŠƒ×mÿV/¥äY&AKfa»ïÆl_¾;®k"^ÏŠ1Bvø»UËê¼¥‘@JñßééSV¬Úsf9ŽŒA©ÉJW*’9ìÉ´ÎkI¼ØŸ¿}fª7:}ãQàe+:>'®©µrM7Ë=/Ð~øÁXÐH–Ó°f6<|ñ?O4Ð/ˆwZèW õf7 …¦¬fFÓ{‘ò,ý‹îí†ÿ…ò:cÓ×ÎFðëfñž*Lrü»Ux¦/…:ú¾ÿŸFd¤,å²sU*”ÍÊ59úâRQñ<Ù{@} ±.l^Éû2ÁÕ1å«Iúè;!x°Öê¯Ò)$‡MtµÂìéÊ>zBãö$I>ƒ„$ÉcSƒ¥:§<Å„d¹ µüaúßjŸ à#&Ým²§ÞÆœZø¯qùÞ”KhoXÕ2¬ `¥ó.Ç•U"ŽE¢ëtÿiÁÅ(~h­ÿ¿¼ÜP-°O&é “ðD1ž^Á3Ø*ƒI0V¢¦¥»úMØÆÂ‡gi<;âþà(9cŠÛ*“Il-žUÓd½±x‰ÉZúÊR¾¸XNáO“x£ð0I€«8[fäX÷![›`FÓú÷`C~ˆ…€ɧ>–¨¿ìj[Õ˜šŠúŽØ&h‘¯žh¨?êÚx8z=ïµYõ[Ίߟ¸~h/ÌÑÛ#yi9nµžðy4¥¯¯:¿hÝæ+6pIç¶ì]‹"ÐÖ”è0öm èm ew L6˹®&-Íl7X¥º·¯hi‹ïÙÑ­®ÐóÝ{è }F¼Ã½}–nñ]Áƒß_d?=žò‰çT¯aC§={ÿþ›§A, ¬{a´©†0ð†0[èíx“VpÛvw¡! ­³ê:¶ å;zq{<¶>–Âiÿx‰/ynEôî–üZ'šöÜ´ÆKÈ‘R™ï‹»mÀ!ä~ä“»Øno¤V®El¥V«/-,.(ãç@ߨ÷è7ôÛ¡_¾†^ÅeEŨ”Õçh³9ÓZ±…`²V¶<';”äò_ã¾=ÄF†[ì'~¸W~.*@yl¶6G¯S«?«áîƒÍçÐý„~ÞòO°Û~B®•#ÑÜ&bidØå¤ª(¿(¹ÈåŠìlMަ/-—/l§’æ£<±ÒkÊJõî)Ø>öÚÌ’5rÑë4úòqøÀ‰1"ÅEaQ3ô%Wü†¤™M¢wABQRŸ\2‘JY˜š†w8¡«É¨ºHOªHq\Ù1îm9k+ƒûJ&YÏ3:'HL–.G_©ÖÖq» Iòf-$+é ¦4Ú^bi²yÓ[ ¯L\m­¾¢™„¿÷”•hŠKP«“kä9Êü ‡ÿú¯yÊÂ\â|¹.G§++«T‹†Oo²‰©ÒÒàŒ› ¶Å¶31…Çq¤*x„)°ÛG@ÁX÷V†nÕÞ=xf_ã‘ÚfÔŒSöDZmðGóÙ± šŸì»:):zg!‚Íu‰[ϤÜE·Ì88Bª‰‡‚ ÃAÁiy¹J$'‡‘kJ4%¨”çfI{%æ­þÒjÕ#âÌ”·î!ÐÕä$-'£¬„¹zîËk/¿g¥k S÷¬;x£ÿ« φ.‰ ‹â’3cQ KЫӗ««J¹ê·NÝEìïú…'ÇÅãlœ( f¤Á€G´ÁÜbIæ¤e›Äþ &¨>œ,CÑÛã·ä'(sQKx úÒ©3×9ðaÐñ}õÇŠ«‹µjT‰ªrÊÓ‹6–&Ö£c,ùîúš3!é9Ь už!ŸkÈÛ» E³˜¼³N¿óHÂæÄìhh¡ž¨Úö‰žWœ[’\TH•—¯*Èw.È/$„€”e*M.x…3ðJE^n.R¸ ü¢übòϹXE‚O•–°&oB“ÑAð2&›ZBžÊŠŠJŠŠHÙÿíý'Z®öÿ~æWËV¤Å®çv¦e¤ 9KèC¯))©Ðr‡Ï® öñõ°ùkã6D¤ð;äi…+›šÝ[é½·ÏçM›9sÞ¨¥Kꎭâ•ey%*ÄÊsäòÌŠŒÆT¾%þ\æyÄó㳟9½¡£Øëg„ïDí#8’ r„é2Üo´•èžgùãÍJèwÞ¢éV Ü$'·;KV1°Ï(Ãé4a‰_%ûD,aš‚ ~?2þÝ®déY„A‰ÒTé•ê¬b3¯ýïÆ/é‘žÈ ßÚ c¡Ç—ŸL¼Ø¶G ‡ÅQ¢ìl>)m}æZ´m2ÄŸˆ=zÝ`a<η޼¾{~…Æ .ò B“Xßk!Otº¢â:®Ù.3?³0±‹Ö½pãêÉ'¼ÙÏš`únDÈȽv 7d ¢ëÌ·ÜÉãc¤hÎ$”^VVR^Á5ï>¥oBì‹kcÍå¹(dß©0^¥Î-&ÆÍÎÉ‘gV¥7ìà/l¾v‘×þÛ¿Øy?¼&J±c¹©)ÖÇø{Ó†æ»!{k%Kü+XžGÐæC¶mþãsQIƒäÊ …UH‡n×Ü|Ê̱A _GqÒW³¢7,›×Ø‹i?ÿüâ›ww6\œu„ó†]dþ£K}‚–y{}qçë›—òø¸ôÕó[‹½}æMõšóaÛ­«ÏLɣʔ²ªþ4eÁEËðœ¨È)ÈG*—lÂÍš²š¨–—åkHj&‚¢,¿\¥î€Øf¦ݚáÜ [!ÚeE)ÅiHAè70nM˜÷´°™(¥”ÊËeyE¹bˆ*2SŽ¥¼}»þÌE¾íË£ß!øˆ…Y“¡î>lÆDL ~<éuëŦ§FNuY–¸&;%=mËÆõ;¢;gño¾»z»ínËšIûùÒTX¾ÝÔî{ÁF¼eÿ²[ñ»ÉÀà ÓÀùϳ©.’ñ­QQ‡¶šÇ?sМeOˆ/f‘øóï @[Z§5+€Y°ÿOß>Ú$Â>O†÷îo(5”¨KQ[‘£I_±:˜i7*!qc^Z¡B²P†Fµ«àXA}Š)9¸yõåjQî)Ë’‹¹-%Û?C"…#cöîÍûŽÔëëQ¼p+”É„ 8£¬ ¬ ¹”#uiqyq‰3©­J—ç•ËËðo°ÂÈ]YiÒ¸ ’Â’òß9¿\YJ ò róÙN­’eÖ*¥ZE£+íT%p¤I,ÈLZKÌJÿšÀ¤”獭5é®ñÆÍ6‚®piªªÄ²ª=˜ÈZUF†Faà…`ü‰ì‚-A¯u„:p¨««£Ov7ÚŸìÑÃØ£'Eý7;ªýè endstream endobj 134 0 obj 6932 endobj 135 0 obj <>stream xœUVy\Wîa˜¶EEaldPg& A<¢x!‡ˆÊ ¢@¸DááF` ˆ`<àŠ‚á9E/@Œ"FÑxàA¢k̲qݰ“êÉÃÝ} ˜Ýýg~Ó]]õ^}õ}U% ´µ(@`à¬PÆ+TáÛçÙG+ƒ]ƒ”á;w)x˧Üt7C‹›)ŒÇÑ¿›hÔ¢™”KÑß'¡‰B4Q»|†î}}ŽÒƒ¦Éph %³K¢cÔ±á¡a*¹¹‡»—ÅܹŸþ÷µ5›²¦6SÎÔj!åA­§QžÔbjåE¹PŸQöÔFj<¥C ¨ÉÔJŸSS)–2 ¦Q†”9šÒ¦VRû¨>­ Dðo­Z7…ŽÂá¿´½µ‰f‰.Ò º™§w}Ü[F›1c¶1 ã£Ç¿Ö‘éèé$èdë\œ`4¡qÂÐÄ­_LÒƒ3ºšTú Ç­+p5`Æc3Ì£çh"EØŽžû!’|äm>è±x –Ð Òå:p45 qËæYì5<3”›)–ô“áÆÌ$‘{@¨SÊ”µWªÂ‚‚CyeHr5gŸÎ?_T^Þp~×èêr€Ê¸ƒ$FÌ‚hÜp£äôè.è¸zI.UÅøîIO>¸K挿áMýð¯#%YŨXò|Óµv1<¤Åü•T  C ôƒ‰{?²‡Ofæ¢<Ôº»EyFÑä_äŒæ!»/·5x<úyûÔ†5u%¹¥ñKl…'Ë=M—Iàé³ ^ô‹ØÂz0Û RlBGY9zÍEÌïÎ÷U¯ÛîËÚõÔ?Gè\bYxA‘D4UÉâ UªÏ¥ñ™4¾Ør/þÁõ&Ô—¹¶FßGXL³™¸ÄÎÏ?qÞéæ/¡Šä±Õ ‹I‹=ÃÚîþ#Ðz%+ûÌAÐ!¥è%¥ØÏ—â>ijÇÛ²[Qº¸»=º!´mÓI{´-Wúùlpö'Õ7cð”WK@& õ‚°KŠ¥t‚" Ö1îŠÆ®\”s¸Ziƒ,Ž¡sksŠ ŠNÖ¶”´ zT³» úXRÖ^ÁèjJ>‚ø+qX¹zšÅS©¯ùJ+?¬ð8;€ À ¦¾mûÇ+éÓÛ@Îò_ñi"žÊþ|sÖZ³p›«Ì{KØ*ĘºÜÁÓê×nÉ®Üû¦á bx¹­Æž@3ŽœÕoM4ÌFß7?zðîL‰é°ò1^'ÅMtùð;Äʼn`:ý—¶€ù+vÎ w“m[í½ˆeE”¿¤M¢q?‘hp Xp¥Ÿ¡';º;»ë_¢~tMÕt&´:(ß1ø­ø†0‡àXö"D‹À’îlˆð‹Úœ/‹ ÿ“„æ$£ªþ?Aß‚–¢«çäÊË(>tŒ ÷e@Ñ´!ÐϾâW¾à¨7ïGTÅS¥ L-y²ôs¥$z©HüCOLÐi·é¶hµÂsÝ&Ǭ…æ0XëÑò¡ž–’¯{¥_Ø'¯wÛj$â~ŸòYæqžE§§dìEû¢Jq.¡A}a_‚eßž‚©0ë2ÿ€OÏb¯7øÚa][“^çû`òc ÈþÛSôA 0Ìå0sšx{Ái±GŠIwÉgÞØvaýÎñŠmÒmŠhwä6Ÿ ~õ,ÖP¾??£‚Á3éCIûQÚZãw!¶YÕžÚƒXÔL½ܼ´R6·úóÉy¨TR[_Ö!vÕ%DìÏ8˜ž&mÚe¤tî §Ï+<Ì]Ábšø¸­,¬¦ÅƒÝÕê”C{ÒBeâ¡D§/Cv¡”¬ÔìDÒÙ§öäÈC…”™—•Lj+`òÑúJ#Ô´ó”WQGUUÑÉJFüCsKåíëäåÍ1§"j·nBÌð¤?8ðš[À¶€»VÐQÅþ’¤Â=9»Q"ŠNQªvF)S¼ àç<(àpUìJú Ìæû–Ï9Ñ –¢>z ~y(9=í‘liQ>¬;y8§ZZú.9}©˜D•”wâhÖñ#E²h=¥uá̱`%áÖã±þ1F/¾'òÏ1X$ô¦þ0·'¥K Lé4ñCM w„Í®Î*D¥ P–½xáÛ8ow©»OØj䈖^ € qï Ń?ªküŒœLæ{ÕuÍÌÍÌ•bSúPBF*JFž¥þ ;.ôÅ¿ED̓õ7î¶¶Ÿzˆ¾Aý[®Í.Å‚rCñÃy%1_5õ^oùñe{”ÇŒ´Œ4éSï5z\“ŒãyXÖâ»—yy­GïU— Iâ²ûðœ7-žx01]Ô’å7¼^?ê<ÕÛ)MZ+ôœp>7ÄB} ôDÃ/FQˆ¥1;<ÄWj8#™³Új̈¯ñpZ¢µ÷öùH²rkmlj¬ÂÃ_ÉÞsi"r%Óá¨/wÜvI0óÖæ¾¨ºtKÚÝV{½ø;ò‰á¨G¢þÒæ‚1?O±IEû Ð ÂåcY9·¡ÃðHqÖ tÕ¨ëU%U>Ç× l–¹Gc¦ÑE]gJ{sµ|Od|ò®„TYÂ^„R7¥úî IVîòTìØˆ\‘¢XQGfi|åÙÔóÓo Ë7;%¡èKFœˆR³öÝËÍO†HVÜf£öõ_;Ý7¶¨®½±îN¾ìJÎW‡ó²ŽeÉ”ägÎ<~ŒÑý}Ò(Ôš#`.‡ÿi}Ðâ~ÛùHÒT© KÉHMß+³Ãÿhú¶íÜm$i,KMIO:”,sÀƒckPv !q±ä™W»õZX÷-Òä+¾µBë‘_¬Ûú¢†D}ïRÁ÷0AHö-R:w½oDx+šS…©«x<ó3³’Ÿ ¿ 4‚ác˜ÑÐÛÐõào̘/¤çgØí¾Óù ¡ë{0¼HHÑ»Ø_¶À$ÆŒnÇf…öÈaÃlh‹g„nˆð]eBüÏŒÐNé`ÊsN‰MǸ˜\¦q ‘‡5[YüáƒRt—†åÓÂ#¶Ÿ¸dÖd8yDÙs5Þ"Kzû›mX%XòLà§àdÜsj0&—_L;èÊÆšr˜ðø»k5µM­W*n¡ÛDß«z±¶š=¯Ø)#ºàom}ˆé¬ôöOð —)wìPaÚË–úü) 2\<=B5]ˆú+ø“Æ—p- LÌÀ|šø=4Â}ö2ÊͨØÏˆ¹†Ô²¨ #Ç-®ÎîÕ—3¤åã2òDx}P•ž„É`z¼ Þ=«ë¹*m¹Rùõ¢NU³ÏiFüþùå–;ÏŒÞÚwañgkb¼ý¥!ÛÕÛ’V2ÀÒ¹ÍÙ%yE%µudÙ`Új£‚V¸yYËRFµ 3€á!÷CkGrÎ"lCã÷ÃN±XGD¶pl̓v¬Ei¼ »ÐÝ¢±¬Ê@û=i_Ã'Àƒ§a5Þ찘ƦÃ2èÓågKZs£vÇ&SWÛeža•ÍG3s2sed=:˜@ºòÆüùz˜8ôúÂóÒÖî“QZÎ}Xà§HQúI;Á\—?b˜ Ú/Ê΃¶à%MÃD¨ÑæÚX26sÑQæ'§X$_ìñÉz©ç"?ì‚°.ƒužÚ¤WÏ[tH/Þ¬~0¿¸Ü6öòS+¼¥E=YÇ j«ï7žîF—P…:ןÁÆØƒ½\ö¹¥ë*ûÏï½¹ÝÿH6J¥›üž´œÏ/}´#þJc/ìf-—ã Ó°ù)Ì3Xò(C]U×È÷9ß D:0{ˆ 'N¤¨ÿ4Š/U endstream endobj 136 0 obj 3369 endobj 137 0 obj <>stream xœ]WXWמfvD@aPÐݵ `A¥¨Q‘¦ö"½,ÒDDÀŽÆvÑØ!¢€ˆ ¡‚ ±}V0±`b‰¿)hb$XÎìw6y¾;»j¾ï–½sïsßóž÷¼#cŒ»02™¬»_tBztš&2ÜÉ39!J*ÚÊÄ>]ľF“µ7µ™l_Æÿ`»15"¦ÆGû(Ü,ÅQPß²z0F2YƶÜÉÉ)™©šØ¸4õ๳æ; 2ôŸ‘îîîêˆÌwÔ^ÑË4±Ij;z‘œ’”6V=™ÎNHÐDªc2S▩㢢£¤eó¢—¨}4 š””ätõàÉê‘Çp¢ÿFj#–/SÏOZ¦öWKñÿÏÃ0ãý'e&E.ðLŽZ89%:hºWÌ ïÔØ™>Ëfù¦ifû-Ÿ3%}ÉÜ„ðyÓ2#;¨‡:9>b¤Ë¨Ñc\ÝƉéÏ1Ó/Æqf03oƃÆ df2>ÌXf83ˆñeF0vÌlÆÉØ3sf03—™ÊŒb˜yÌhÆ‘™Ïø3c˜Æ“qe†2 ™@f2Ó•1adŒcÎtgz0Œ%£`z2cÅX3½˜ÞÌg4=Œ1}|0SÌü,s•-•ìÒ¯K|—k]þm4ÈhƒÑÆÞÆÉÆm¬=Á–²w9 n‡œ•“7ðÞüYþM×Þ]wt½`"3h2Þ$Â$Ïä‰ tÛÞíëní¦JÓõ¦?šY™ù›M7[aFÌ ÌêÌÝÍÃ̺O꾪{[y=¶Zô‚2sXˆ e?€£ditü+QÜ ø'g®!E`I'L)”‰[Á^h@{œ9Ü©MdуÃÜ¿Yp⯠gq/¡’5›0¹SÛƒ®RŠëúb7±/‹Î„è®°Ûä[ä ¢£‚ÖmÈÙ™©ì//Ú’»í 9EjvÙ÷uþÁââªUô’¢+ [R¡åuPC(¨­u×鎺 ú³M®h»QWÓrõDü4%>£#ˡ۔”{¥LT*êÊi$iÀÂÀÊN‚ÚH,€_„õÐËå{´%<Žvê‡þèÓ>Fõ/@ÈS¡ —éœ2—ð^a7Þžß×p°\•¢,¿Š\"GSò½xsí0R¤õ¢Gsy–ƒ–EOmp#ö„,úpŠc–ΈOT° Ô¸‹ ç=ÃÊ[à(:Kx'ˆ7°ÖÝdÁ‘A¼Éên‚£6ÓåØS׆½Ä6ÓþJ¤w¥T€̤w°°| Žp 9 ´V¼}l¸§äbþ™Š3Ç®‘Ë仨Kî ßÕ«!×ȹԦˆÓa òÇÓ?äÀ-påbkEÒÜy1I>*tåÐÂ|9ÅÛ»•ñÑñž*ôå(ü±;EÁÝMñƒf±^@w;”¡7út àÖ 2˜Ó¿Çñªd…¶s>ýìæ{Oœ°àNgçù;Túº:‰Ã +Œ*´e„Bâh$ºB±+ÅPÇq¨Âûè÷YËÁ&+G¹ž×Ò‚ c½´f•wGÝFkDšEg u1GÂ= ,ÅÝÐSv†NÔ~&¦ ›®¯©J¨ž}oD5Êh¼Øíq®ÚSŽN³·0v¯ í¸lçóœéÞ÷¸Àèo“ µ™qÇTù+ödìâ)0Ë($¿BWÊðËQ©ƒ ©hÔm£#§¸“Ó’[V¿9¸=Xþ öœJÑv>ÖïäT[_œ¤™ÎƒI²– ïà–ŽŽ†V ¥þ{èßÝßËÄ"HrrÉN²›4o8·¦2öÕ¨+úè‡ Ecœ‚“^ô'0{Õú®L…J.+,*u‰$K®8±¦pÓ‘­çùííÂÞǧª¯Ñ2-Ï*LÎ[±+}G ÿA@–…L¼OÕCW‡bWN×U·Œ¥Wu`¡ãå"/.c͵iÐSÜI‘¬¢ÐÚI°Ûh3…AÄUà7Õ;f¡¡¬bàMŸë¾ãß7äñW×[®ßþúé$ÀÅü9õδVòþ„ÏÁžth#ÑÁÃMÑll ßé‡0SJERÍï%ú 6+¨äÀˆÜ)m¼r¹¥ì,ã„?f¶]ñ)Acнï¨ëè2[9°Çáh5{²*sxFEÈ¥ýåÙÏRÜK·&îXî—¥¹ùÛ¶æ*ßÊ—nËÉ ¼óâÐ᪩œèü@-ú=‘ë‰ hÍ&Ó`Ð`öIUŸyLçÀæÙK vn?àPJS}™‹‚1«–ÿP7qRLÜX=·ÿ©{Ù÷ÒúL¸-ÀŸ’Bƒ»hF>B»†~«hÉö¦Áö•A1­ŸÔ²J®h}\]ýøÀ¾m[ ”Ð_¾!gÙL¦‘Ù቞¼¢âÕJÀ¤ŸßRBþ¢'$LX½­Ä¡t7¹â÷á!Çl±ç´À±8î9Cïë•W.¨<åSƒ{Ï‹;\½N‰ƒ¸œðü¤Ó‰µqW³žP꺾¸ ÝçºîBF¸sÁÓÞaŸç”Û¯þ¬ÿ¶Eõ_ô¨ý@­S§Ðxµì5yEÞGwNkõoqÿj@Üãý}§ùD4ð¥rà Ê—'ñä>¹xàò)þ™ÇƒG,‚/„ mžikã7.!½Ñô†€ù÷ö8lúº¹ËCUúC5Àï= Þ4 Þï(¼ßѱ¿)¬{ ,u’.ðºX6P`¦‚-ZHwˆÛÅ.ÂVà\Zq%Rß1>h»?¡ ]Uq0ûBŒßKÑ~j]îºå6ñQ+¦Æ&íÎÍT®ÚÿùþÏÓéÜhòàFÿ”_U^U~äi"wâ꼊Ðû|¯¨Üõ{ÈaþDya£RÑq›|µ,'ˆ74æ"õ,Ú hë†|JÛƒµB„,¨`"wóØÒÀÏÉúmkTÛ2H&YG|¿X^ÇÃè£\Z^öR¾–lÞç\õ¸¼ú ù‰\^Ñw:þdH~áu¶2Ž4¦ûO‘×’C›­âo­Ú›ª±!q듳V­\½bSá iSA f)ô´¬GkE†6Xì# Õðà6G¬O]¾”WÔ…ÆÎY:ÓÍF½£h{ Š gV%}¥*ÊÜŸ•ÍC?.“6'ó—èEaŒÁ8 WàRˆ£aá«[§_T)2 aæhIŠ~í²'††T*Œ‡A´ƒ òqnŹ1uá¿–•ìØS¢|%_½uöõ„ÉÞß ‚³Ô @-Í…7h®oK«#õB+Õ§M4¦›Ð”RÍ–¦T2Meu0ša€‘øFüB ·W‰­ jö8N%'Û¹cd/ÍÁɪ?W¼Î¨H&3zÎŽ>hVíÃMJ*ÿ{qøû1H `=§µ.ŸymKUÈV%‘¦Þß4žj{Ù9i§ÒÒëÅ]†¾T# äðTÀ§úÞ4K±J%{Fï>•ë›j·!Òqô­CoŽ2ÄJ+±‚žã¹¢nÚâpßé‰å7•ðšžÑTŽÝnù‚ünãÑ«UJEÆ4¹Á^ý@±€µÒñC %ü# ÃR]‡Dz 0©S^—*CóÁªA¤ :­ P¬`q0‡2 »Š*éfê*Ø“`|¡ìŠw0<Ä´³%Ò€½åÿf³àÆáBø ü!ŒECËí)vê³LŠ,Gê,Ùr—ÅáNN‹kž+ÅlÚª³åÄýðÌÆÐ¦à¶¥ÏÉsÒVÚÔÐxæðeò˜³åŸ"7äYŠ\ÿXC³#¨¤kyŒÆ1úJÀR‹7 Çxh’C—‰Ñí& E¶l¹Ý‹µ¾ùhflföÊUTë ñY7{}/uæü9Ä‘„äE—¤P’/);›Ñ`{•|SÚxžWdäëóVò0”Ãñ((ê<"C¼%•TÕ.¼œ«lØ[±{×öü=½ÿq•ý¬`‡ä®‡p被Ç1b½t Û¨Óþä[>èÔáþCØ€÷YjÝð>=и͢7‡•x—…»†TÉÿI@‰^P$9£Qèê®Òxaúa ã¸_aÜ/8Nº2ÿ·Ù'ï}•¢4Ñð±V4^ý/ÿýèÖ骫g§„)Q§ùŸß¢¥üí´kÈø/›®\z~îq?@"ÓCæðŠÆÛòÿo ¯|²…Whº¤dÑvÑ8ïäÙÔ[^Awp½»ü^ìULÓ”“Sˆ NÕÌå!W&xNhmô¶wòöñZÔÒùJïà™v!=u¨”{9N F%A'‚Î ¨ú›Íõƒ °'`w¸wÆ\eŽ‹äPp"à J¤¿y{îFœ¤ží Úië‡Òª2ú·¢Pvì´{¶EN|Sâf¬ågË«wÛ´¨ªìË ò‡¾”˜Ï lep^ X‚¶P¢/3°“A¼´Úí •·ºH;™F¥],`Ä_ ìm"´ ¬žÀ¦â~jË%#%ñ»T'L S—„͈˜“1œ`_‚G~5·Æ¯5ú i#×ËÎ\­j.ü•@/V+Ÿk¾ oñ©p£2ÆŸ&ÇWŒù2qÏxJãqÆ®O^ž˜IbHrAÖéÕ'6Ü'/È“=öϯ=q¤Šj¾žPqíïipÙÒÓ³ÅÕ‚n55ò åË]=ÃМš!=ï¨ËŽ>‘^¿öJïˆMÜ¡ÊÃ…o@÷ŸO7“ß((NÏÑíGBÇÏɆœuJ°)澯¨¿q­2Ìk|jG#%šºÇøoDk^L7Ô°)¤¼€E´ˆá}ÃêªïÅw„F²wÛ‘ÏyEûÙ5'"æÛ„.œ[yw³ûp9Ø¿}$Ð> C~ÿ ”Ob›Ç~­Rtܪ>ZwÓL\îc_ßäù‘Êä¸5ÁÄŸ_ VòœÊ»å••Tþb¹f†WD¬›j­A¡ðâ(ŠÄ¿ ¯—¥Â$ÑÝ髱·E¨À4I#îÉ1x8€‹rGÀè­äœÍ¤µ«µ ]wÎ_§bÁ’;pª¼ –ðßÖ.íºÀ;PSvi‹äÌ·c®”“TRÞ¼¡ŽfЈ×h½(nmB„ª³Pÿœ<èò¨¨†’¤”½i3ë„õÙÅ™¥1ç§žøŒ¸ÉQa^á>Yã ÍL8=«zʽ¨§tó~¿½gèãúÍgFe…„ªvÜ'd׉¼U•I9’yh1…›5 ÆO™0-´éþÃ3Í7Ur\•NU"h3í%︰1($ LuÎÀ!R«Ïƒ´»TPÆA(ÿ!àÕ/!fó0M&Ok=Ìè7Mj§è$Z GòöïÞ¿Wü~üèÁÚz0ò­±×4ö=vC£Ttº‡jæºÚ M§38ÀàÎv°yªiv?£DX.(~¿V=gnhT`@è×ß4×V^WmÅ“‚¢³µ6d’oèb_ß°º;ß©¿­2O++¤ž\̉ ôë&{MM¡ß>S3†ùc7š( endstream endobj 138 0 obj 4202 endobj 139 0 obj <>stream xœuX XÇÖíq˜îY„±AAg\PYAE7ö°( ¨ˆ€ìˆ€lã)šˆ5‚¨¸$*àÅ%î²(T’QI4jÞíIï½êÁ—üyïû¿Yº¦ª«úÖ¹çž{kD”ÞJ$éûƧæÄg'ÅÆ¿ìxK?|?B¼§ÿáªÉ—Œ öýdˆ ÄÈ@¯b¸¹Ÿ)ŸkMÆ7˜‹Dy[÷Ì]™‘Ÿ™”˜me²pÜ„ võ8¹¹¹Y-Ïÿψ•g|VRBºÕÒȉO]™‘Ÿž=Ýj.¹;55)Ö*!5?#1Ë*&..>N˜“Ÿb唚”‘±2ÇÊvî8+ç‰ìÉ—sPRÚòÕYV¡1éYVV!ñ «Sc2ÿÖIQÔ‚€Ùù鱋笌‹š›ì¹jÅ<¯Ì„ùÞY‰!>ÙI¡¾«“øå¤„ùç¦Æ„”—¶|¡í¸ñVvöÖŽœ¿ò˜´ÆÅugÔä)K¦.u‹ž6}ÆXw⧬©H*˜ò¤Ü(Ê†Š¢æQ^Ô4Ê‘Eͧ¼©éÔDj4BùPNÔ*”ò¥œ©±ÔÊšDÙRa”?åB£Â©(Wj<µ  &S¨ET 5‡šBÙQT5—šJ ¤ô©A”ˆ2¤Œ(1eL ¦L(SJJ%QrjMq”•KÍ Ì©¡”5ŒšIYP³(Kj85‚J¤dTñ0¥G ¨½T§h¬(F´kÀØv x-–‹ýÅUzƒô’õ¶é½ØJ>‘|/ù'=†N 5ÌrfóëÄ–20p`Ú@¾þRýÝúgõïèÿ:ÈzÐòAeƒîR¬38d 046L6¼lø‡‘“Q¢Q‰Q©Q½Q“1elb<Áx­ñ¡ÁÌà€Á)ƒûLf™d›4›š™º˜6m5}%M—îB 1â0dÙ5Cö ¹ÇøF¤Ô0e¢NµfkÒ8‡÷i|„ki##*‡H?±LħÃJî^)yKãš4 6 qéû4ɯt;¬”ÀW Ò`O%Füç8]©Bæ åË9;€*ÑÑöÞ¿çÀÞ³P¤1*˜UÎÏoK,3mm÷x_t™KÏ´’¥ßiËŸÀYFÚö«­Ïî áuñVL·ÿÕ1îis–ȤgT 1#»·kïó)ðšûìâÚïr&ÔƒX<Òëá9Ø½× ¬Á¤ã )‘O¡‹¦,‹ñD¬ã‚G0Ì/+~j=¿|N‰\„Æ“ìÈ’×ç @‚Å4ž‡kp0ÔH°„†Mð.­¾H{1\ƒ(|M ðaÿÌG*þK•˜O4ƒ$ZPiÒ0ÇàÄ÷Æ8Uc,ÁÜû44ÄjA"¯¯¾/×2£7Õº‹Ö†VŠàk2ÿÐXr°A©mqƒï-Õ´N5…¬üê¹ }퉥BéÐAµ,áË%ö4¶Õºc+Þ4!@[.ùÏ-‚9?ª ûËèÇà…«$ohR¼$¹×ObEãLœ)Ñåu¬LôT­C©^È^;™qžÁx`Xté¡DY‘‚*t…åw:~lû»#̆™0à9Œ‘ñ;ue¨Bc]&Ú÷V‘Í<æ—0hþ§É™ù+“ V Ö+®ö©¼Öî#ë+f H;:ÀTÆŸï6aOüE3pÐíÉFk7ŠÓ£é±Z;+Òp¤ÁŽlí9ýšwü]ë(Ñ‘P§ù‰ìÈæ¼O#Ô#Nlõjã'¶›ží†R¿­'ékóúÏÐ&Ëô÷–ÃMFíÓ€¹™«ãeYëÒ6/dŸÐ_Þ©>ª@죳+#ä«”˜Sè·ëæ–²&(3u1òa¥yv÷‚¿×XqùºlGøá¬Ëh*ÝZù%©À‡C+7df'¥.ÿ8±þñǯTé)•«v³íH)ûWe=Ü vs…-—jßãdþ½d, {È ãÏ[:|³à+‘W‰Pk q#ï Р;üK+‘ðÂ[5ó.ÀAƒ¼×ä.ÞÌÆ3­'/<“ƒQ—g°ñªà.‡×Ï…õD@ãU&º¤nR‹/i"8,f*±…¤†® ˆ™&³ÿî¤ä­¨û­BÉSlÞ Ú’[®YDLÍ'¦âuï×I®Ð°N³Nw¼iã ѹ^¨êÃò?œ¾"rI`¶.›lèÄR°ƒñJRelõÏ‹ïÈV4Ì;â‹Xw½µû–,±ç°ø‡ðÚ/äºt¬!ÇkØ­Ãn~+§ÝªÒDÌgR±(·/`æ nþl»ˆwëó_tsÚôξ­ê¾øêòÐ_/_l#ª=ëJLuLõÂo|‘òM‰ ÎŒ_½y.«¢¿¨ÛqtwEŹï7"¶óFÈÌÐ䨀¹ãBl;e™ïRÖŒyÞ ½‰ “’ ãÞáïî[€Ñ´6l‰mfºã ŸËTô–sÛî:PVsþàÄ*j—ÌŠÈO‹ûXžµ.õ³ÀbÂÁÇPÞ|²IÄG5Šù(Þ‘Û^¸z*BoÏݶ -(ÉÛ‘üQü¢Möl#½µbkɶ’’ûJ¿A%¨bCɧ¿ =üñ±u‡{çLC»|¯Î7å0®ƒ .ì$.ì{¨Àã]‡5¿ÌŸÁ´!.up8“†L¸ýˆn8v‹øéÄ¥„IÚt¸ÖLò˜>Sb±þÅ ëÑó'x,®éÌ!Xìž_²²|Õé°© ›Woa Åž "þ6%•Yß§+®ÞBË[ ª˜Ü¯Š|¹ÞqÔ©2?U~'†âvaÀ·ü?Í?¾‘_„c%ŒVSy¸ M¡ÇB<êž‹ëþRëÓj3‰Wqëo­"þ8üÎió¼ø> endobj 35 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 27 0 obj <> endobj 34 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 141 0000000000 65535 f 0000078700 00000 n 0000104794 00000 n 0000078464 00000 n 0000074552 00000 n 0000000015 00000 n 0000003239 00000 n 0000078748 00000 n 0000103814 00000 n 0000101829 00000 n 0000104220 00000 n 0000102289 00000 n 0000078789 00000 n 0000078819 00000 n 0000074712 00000 n 0000003259 00000 n 0000006101 00000 n 0000078860 00000 n 0000078890 00000 n 0000074874 00000 n 0000006122 00000 n 0000007757 00000 n 0000078922 00000 n 0000078952 00000 n 0000075036 00000 n 0000007778 00000 n 0000010451 00000 n 0000102846 00000 n 0000100837 00000 n 0000078993 00000 n 0000079023 00000 n 0000075198 00000 n 0000010472 00000 n 0000013884 00000 n 0000103458 00000 n 0000101404 00000 n 0000079075 00000 n 0000079105 00000 n 0000075360 00000 n 0000013905 00000 n 0000016972 00000 n 0000079168 00000 n 0000079198 00000 n 0000075522 00000 n 0000016993 00000 n 0000020058 00000 n 0000079241 00000 n 0000079271 00000 n 0000075684 00000 n 0000020079 00000 n 0000022977 00000 n 0000079323 00000 n 0000079353 00000 n 0000075846 00000 n 0000022998 00000 n 0000025705 00000 n 0000079405 00000 n 0000079435 00000 n 0000076008 00000 n 0000025726 00000 n 0000030488 00000 n 0000079498 00000 n 0000079528 00000 n 0000076170 00000 n 0000030509 00000 n 0000033505 00000 n 0000079591 00000 n 0000079621 00000 n 0000076332 00000 n 0000033526 00000 n 0000036893 00000 n 0000079673 00000 n 0000079703 00000 n 0000076494 00000 n 0000036914 00000 n 0000039788 00000 n 0000079766 00000 n 0000079796 00000 n 0000076656 00000 n 0000039809 00000 n 0000042664 00000 n 0000079848 00000 n 0000079878 00000 n 0000076818 00000 n 0000042685 00000 n 0000045342 00000 n 0000079930 00000 n 0000079960 00000 n 0000076980 00000 n 0000045363 00000 n 0000048570 00000 n 0000080012 00000 n 0000080042 00000 n 0000077142 00000 n 0000048591 00000 n 0000051350 00000 n 0000080085 00000 n 0000080115 00000 n 0000077304 00000 n 0000051371 00000 n 0000054022 00000 n 0000080178 00000 n 0000080209 00000 n 0000077468 00000 n 0000054044 00000 n 0000056531 00000 n 0000080262 00000 n 0000080293 00000 n 0000077634 00000 n 0000056553 00000 n 0000059417 00000 n 0000080337 00000 n 0000080368 00000 n 0000077800 00000 n 0000059439 00000 n 0000064454 00000 n 0000080432 00000 n 0000080463 00000 n 0000077966 00000 n 0000064476 00000 n 0000069387 00000 n 0000080518 00000 n 0000080549 00000 n 0000078132 00000 n 0000069409 00000 n 0000072970 00000 n 0000080593 00000 n 0000080624 00000 n 0000078298 00000 n 0000072992 00000 n 0000074530 00000 n 0000080677 00000 n 0000080708 00000 n 0000080752 00000 n 0000087772 00000 n 0000087794 00000 n 0000091251 00000 n 0000091273 00000 n 0000095563 00000 n 0000095585 00000 n 0000100815 00000 n trailer << /Size 141 /Root 1 0 R /Info 2 0 R /ID [(ÒPN‘¦‹ÝàÀpjqè°)(ÒPN‘¦‹ÝàÀpjqè°)] >> startxref 104997 %%EOF simh-3.8.1/DOCS/decsys.pdf0000644000175000017500000012240411143601715013360 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÕ[Írܸ¾ë)æNj‡Kæf{T6[©ÄQj»9Œ~,9–%Ù²v“¥6t ‘yU;á]ã-MÒ3ïåvW—Úk¢N˳4‘jë⌛›­+ko”+þÂ]ùýý¶¦Ù©¶¾£Å[ç©õ¾Û§mj"ðUà”5Ö„•¦¦JÚ€+v´eÓ´•)¾ãŬ· «N„*ÑJCš4¤g“2¥ßœ~wB?urúëˆùUÙhÕ*KÌO[}ÅOúµ8:LâKÇ£ŽO»Ò¶íËÖ¶øy«h`x¸ç5LúFô3ë BÃÌŠÄå?ý75‰ŸuëçH¼å™™ùŸo†Q6,â3<†;t¤—üi û "1j¬ïgõ•rñl*âÍUGÉ×:ÎzÊ`6 ²—LìÃvç˦rÎB©­ ûŽ]ï¸k¿Xçå´ýu£$yÀÍðX1)Ÿ¶pÖ‰ éiY«šêô‚ôG :´îÒ(±êûôð‘é㽊 €î!Søn¶Ššmk7p¸8« n~Å} ¼œûÞÅïzÅ ›gIºÁÍKÙŒ2%Þc@xº6tHEL[§ RÔ2eˆ«½G[èH X©Û‚äËUþu¡Û¶uKº°GzyP~­‰¬4Óë9ñíUÅ–uÝUyH[by#EyhaíH$ÅB®Óê°¤g Pùü€”Æðø†›g‚‘¬(ý¶…¢DÙh ±õËÌDtFæ:‰Ìñ:1 ¹ZÌBg±±DÙ¹„J#š¿Oªx›!]xÏ{¸ƒÀñÈ&æ<-÷bÕø0ØÃ‡­Òaµð0mz?ÀÔ„àg¾ÞÚÒR‹—Ì„C³îA¤uŸ)‹7%§¨,ÄE|Û?%Z,­‚‹ld‡ÜFâ Ð{h¹çô𯸦•šuŽÑŠÉþ„޳xà–÷É×Ä¡ï H2˜ŽÃ¤…eUé^ž M|Äš–9:B ÂdnÞ#U¼C ˜¹L½JQ࢔é'¶™ßÂÛ¤ôÐc4¶!¼ƒnÓ{lï.W… ¹KÈ+ Œ’CZßX‹#ÌVìøÛp`ªi(Zz6^‹5 êéÙ¢ØÚ×”ŠMv‹x%‚ªÔoÙ¾Dà2qÐGã×xWÈ—Ü£÷´£ß³ÐŽL¶²-lì sáízu!Wˆò âÚf>PµN…•…8•‚OЧZß~Ñ‚UuüöôŒþèì‰_u¥Û>¯­m‚2T¡uS©Ž·žÀ€BæâM衼5µìüq»kJ§UàhUz¥ˆ0I>mÃiG˜G[¦m8m›NµâÄýÖêÊKj·‰Z·2Ó×Gêáµ^©“NÀЈӿ?c˜š[bÓ1 ¸|ö\×ÕØrÇe:Á^¢È*¤>Mö‡-Ƀ³®)¾ßîŒ+½³Jxõâ`@Xâá©ÖºØ%ëä ª!yUc·ïú55uä*Ä©†32$¯xÕŸ3óH §àYê ÒÌ™2MÒ¦øô#U ­ïœ¯ƒv0öv0Î:pÇß1bI@Ù7§8¤~Œs‘#ç„P²=•zs—‹jëèTT¦Qnïf4 ¾&ùE›BW2èâá\¼.j&õdü¥»ÔC–õ›4}C0fê  I€÷ Ž÷Œ¤7Tÿʸ‡ q5"Ñ?Ukñ“\~QøÌÝÉÁø/Á">ôXG´Xf.$.wQ1åí(Â9ˆÛB&±s¸Ÿ‘_ôßbf åÇ"ŽÀ"\¥™&ñ×{ƒdÓ•ŠC-c™È€¹,[dŽÐDnáÇmÜÌhÖHÿŒÝÞQÌÏ)Á‹¯ó3»âl,]kUË,p:¿®fÏPÁ·ê§ Åû ¤ 8 ß‹Y3L²à·hUXƒeN'FwBnüu•—ø«ˆ=¦™¥]²/Á[mIÕˆz™úIÎŒGõôT€ŽØ3CÁè,–á(¬ÈçÆúÈQyœ/\N˜.¤ 9!:­õ¡Ÿ ;U‡>G2>¤Nƒàn„=,³–\;7‹°DǸu%‚Þýi†‘½¶Ò]æäP•½ïü¹ DP¬×ÚVªÃ8!-’Òö ò:ˆÃÚz‡0DÈ;J°.)™†Oç&MOå¥qùk,ÖP.aȃR+²8»¾~·"v™B»ž»ÿìáùƒ½YküËœã|¼ø‚Ñd•øuÕ³뎸G¼u¤µ[¹ï¥BÔEv“„—…sî°X´G* ¸¢¾J]92jãCÆï&Ь¨Sy]mª'™Ÿ™|GB0ô¦Óqã[‡S+Ë–G 9=ˆñA"·¡¯9·N«U2fR§ y€6s‡s&Fåô°pGYÿ-ÏæRO¿%ÑIGâåg9•³Ã©„Þ¸%W¤xMKq|Qíๆ$O剗œaO7ѬÒrõšåm½^/í²Sã™ÅíÊf´6®ÓAoüáŠ<â÷3®íÜa.@òÍÕB¸§‰œ­Õã ,b¼ÀÞC‰×P„‘ì›#OÂPÛŽ*‡Ð'Ï«ä4ArõÄïfjúÐÕ†w;ćTðâæ:‘ˆÔ®3Þήf.6á(ÛÀ,–¡Vzƒ¸JÇ1ào¦é‡"TDM܇Q è©üÛ>«û¬N¸Q7¸%dæJWƒ«<½[~8s½¶ óó ‹U ‹=h\fƒx™±kó¼ûÒ ûz–ï`„Çš:€«Ë£²,œ‰ô°*ýÌŒ1 ŒaºŒ`g`y(ö„)¡gåÍŽÌ¢UÆ<+wf¯:iòÙȘ|˺täÞû¿Ç¹2ÁJ±1ÄLœÖâ@_ˆ—w"øeÒåÓ§»» Ù¹_êbg`¿ív:ÀNËÔ\âlñQù”î.Ú6Uâ! 6ïK¼†b/ʼnKoY À¡ÏLžý³»Ñ'˜yúŠ- ¾Rƃ˜Ð¢²wá3y¦•21|†ŠÌ2³DÆd¡]JÁV}yÐQXÎfãO]áR‡¢íSï4¿äÖ ÿœ/'×Ô²ïêRRÆñÉë: ü`V&ŽE¬*œH!Â"ÆÎíòÕS¾³øÁÞ eYŸ¦ßO3f|ó4÷÷uÞ >çˆO»Â…%ùIß,—U¢$OT¶KŽÝά©Sä¦3Q‘—¼ÐÙ[¬‘5÷5H=Ÿ#dŲ\]p¡¯Ë–n¬Ô¬j†›õ¬Ì<>žX0¢‚èÑ-Š™2rT¥ee…Åç§}k8ùpÝçXÏ7~é|nÕÝyKè8ÇsºŸzîE¤AÈåΠÐ9Ág9¹<£½À=„I~ŒN0îËR†Ýbþ՜֔‘‡?ÊöyzU’ä@|:lÝ×Üš‰Ÿoqmæ„§´ýëÓ“?ÓÿÂCñ}endstream endobj 6 0 obj 3338 endobj 15 0 obj <> stream xœÍ\ËrܸÝë+z—îÔ4MÎn<¶SNMUflUÍ“E«­‡#K²-93ù|qÀ½ šÝ-ʼn½0 âû8ç^°?/ꪋÚýÛ›“goºÅåý‰/^¼ùKxøryòùÄTÒýñüy{³x~j6ÍÂöszqRW}oêÞ¿l¦·ÿíÕ¢«MÕëÅéÍÉr±:ýÇIcªZ¶íbÝÈÊ,Nߟ¼[®W¢’Ú´ÂÖX›¾­„ì—?¬ÖöÁ4F»RQ)©¤^~Z­UUwMß.¿øV}-—wTx¹j«¾µìõ†^ߨþ+]wânW}UkÑ ^õ–aÕ;Xõ~µ¶Û tÛtË«¦R•>Pg‹UWµF(e;sÓv;p±Z7ö½R]V+cGˆïðu'øû;TÛ4vÛØûa:³<‡mÒèWô~“ºåbM6;¿:SÛý3í ˆ¯þ<õ?œ¤6¢‹»žÍy³úûé_O„±#k'R^P¨S6ÔÇ4“¥™°á·lx¶;´eŸ¨E²‘Þ£=A;¾X‰Î¸¬…î«V*.î,.ù61‘ó<£ÂI´XÍ÷Iö™Àò^¿RݳT÷+”c’ÝÜWŠÓ$¿Ò#›–AOûÌ==~MË8dîlˆ»ÔÁ×Ëô¸áVÀ¸´§û#4H3BQŠXáWª ‡i&ÝHÉGFÈîFßK.¼ç3r|»¿Á(©|nQ½š³%žíÖUÓê¨Ý¬ý﫦®t[·P»®ÇFÖë)›ôe²2ÙôÓ#;ŒOp)AºŒÂ§zœ× Ë!”‹Îd=^xo‘@±N ”2Ù<ç²Ë< –¹;?D+yÝ+zÏ&þ‰Tæ¬Ré:ˆ'tVLbÁ@’ÞKØëCÌ.‚h[Ôãá•—•¿Œ“ëê·åÊz[itÏuN¥`3H»ÙQQ£M÷ZºÍLÎÈ8HÞhð¶µk1èJWW¦ë¸®ð91 eò·ÅÞ #é {ÿù1Ò!؆ûÉhÒ#°iVQªy u‚Ôg·§`»3¡EÈ"CCñü±¦ñ!ûfò=Ð' wHØFC=òá[źÇã…S{4–ªÝYÝ'‡E„à2lÑÑžÁ£…ƒdhƒúœÑk¤šЗoà!“Å€š Ñ5–—Gq4ÛЖQK”¯Ðªm‚+B‘ì½7§½ª”h£9¥-a³'cÎæ×Ð{öX§…Ö¼P)íó”Ö gK¤¶SÕ¶ª«´YhÑV§5ŽÓIp«…#ÙZXôàë¿‹Š0½WÙ‰ÊHAŠ0ågoúE£ê6œ©1†fê6—©ãÎø‚WÒköL í=TÌåßq>ã‡tzßOáG‘T’# ÔÈiB¬Y@³SV<2zQˆm2ôx†Ñîp‚’£™‰a]puNûÄÖ -¼ÔÑJê’:~?‰ô†÷#6¶o´"@ÛÎrŸèãÜv ­7/ÉlÿH¥ßÏå!f’&  ¿•$ÊÇ¢L1Z‰, 6 ·ÈØ3cöômÙLæo’ŒõUÿ@U!Ò|J±+ÓEÚî˜)”€ úŽ)Û>Ø7“€èA±"ëÃ$@Ñû3ôþh<ˆA㨑ÇRžÝ€vbøÇ¢v庤†“Åð•c €p®IU3«6x~ílT450]gr€ˆ=1žíNí,C:YÌ`ƒ=Ø*EÙ~™ºÜ~æÕ[om Ió,¶ ¼/ Ófñ€¡ IùØíôñ-?ÆÜr¤Ì0þIg=*ãþúæ¨^ìдÄp;„±ÏHïg†ÆÁeÙ¦°ÇÚ!¤[%C’öðrp;¼FÂßñÑi‰g,W‹aÀ,ÎEí›=é» "?-r T S‘ÑÆŒœà>Ñ[êa„áÙιFLú`bf6Ü8Ÿg; ¡ÌÎ|ònºÆœ,•.o@2 b]utÉ*‹ç¤Ýø4º¸5òÞ84„@×ôjEþðô‡ì»ª6Cúã%=>„D…iÝ #zf™Š+¶ˆÝN@4.Tžú`yÛŠuãñê4]‚’fÑ´Y©oÇ“·ÌU‰aò§+i}Ÿ̈•:ËZçÜbé9+]¸3Q}mÅò§¡‚Y³³TÊ›}ôÍŒG{fd¤j*ÑRJæ@kÂBÐD\go§Pti½GˆBìÇË™ÐyrúöÙû™÷8‹;¶K“k?#|NêµA@‹i…¯0 -˜ ¢÷WÀ;<”Ć:eÉ›,4=@ÞáºãÔ–<á 1¶nÙ݆ ‚Þ‘`hNñ„ 4Œ+jTæý·5'ª³¶GÂZÉ”³¸»v3Ž{íºV î.[5l’ä–Ù7ºgZŠ¿Äµë|/j|sŽ‹%g ø'ƒß0®€ïiìý: *Íâ²(ê/: Ö[—–B^%Où–ŽÚwiRY!|¿Ï• +¢º]H!‚³ž»3áЬ±õ}3ï'׈FÛDv*:(Ÿ6#˜ïé¾›Žkþâí=Ê–­Ù˜îå–ÂT]=—áÅNµO4)¿ð ©s:ež4‚ޏpGz7 š8«<ឌºí à¬Æäû ™;hˆÆP{ÚÌîçÊ_${HI­Û}HÉ ñ9‹h<¦¬×ÓÝšFu@üãšì*ØîoC Yp@Šç²àÙËW‘´,àJÃ^1å*Èþ~(´ç©ÝRØv79†1– r¿xÒøø=‰K¦‡£FÉ:3Ãá½µHàRÒÀãAâFgÙ"üöÁâ‚€Îz'7P÷¢i.Á(M{ëG³¬SÙ$juÏž|•®®uÎÚnGTF[bóŒܯvɵ”;1·9Z»kÎöuï5üšuðZÝ&‚w™ž+|•ºÚKúýóïÏí>õ­V=oôÂÎD›Z8Î.í¤èãLT“3Éma¹w~2­p^Ôºnå¨qÜ&»íûÒNaig£¦ówqŠÙÅg;\®²Ç}¸Áx#5"â;5”®CÇ3×Û  (…èv.’ÄÐ1L¸òñ¯3õSÀ~áå»ppV皢Gx§ªd›¼qÅÌÎZÃLúøO@Ð@_ÐÇJˆæ¤Ãê@|× kÉdÕµsŠùU`¦ÁÁ‡žØã¹ËMõcÅæ±àNϲí9 Þ›©á¬ïQ[^ˆg³;•,Ý"¨”%sÛì­ÎZ[%þ'G…h7 Nû vu1·gŠïY£UélÏà7µ{3€†F™|µ;.(KUìLÐÎVМ,Ñ9œ\ÑØüvØþd—ÅîXtÑh¥Â¥ÕÁü­c0‹ì›±.Œ.Ûó¬#ÕPò`4 gžÊ öq*~nÝ<žÁ‡#ŽcOúE”X£y¢‰?­ßÓŽ—>ßôpAY´)Ó—Çs^ü€Ï7çTìbôd=Êy‚ýÎp¸Ñcl7;(–®ŽŒ `Üa÷ãNKi˜¾†–hšÖ~¢9ÈûF?›x ýçOßK5¼ÕøËÓ1XDw€ÕMúpï0Ññ+ùö°ÇWm¤Q–òñaKÄ)ÿ)Œ\ÞáGMÙ/d0RAÎgî@„lîº6c”8§s¿M„†Óß狉¨ÅÙÇ1QóÚrݤËn`Bho«2ý¬êÉÕ+Pú~ñ6GùŽ'ÅO†#÷ý°èÿï£q¥ƒd¼<=ùÅþý*U`endstream endobj 16 0 obj 3360 endobj 22 0 obj <> stream xœÝ\Ksܸ¾ëWÌ-3Y–xƒNí!{SIùGU9x÷ Kcɱ-É–œÄÿ>àìñqHf×®µ/ €xôã믛óqU ©VUó?]œ8ùþ¥_]Þ´·W/ÿÚ_|º<ùx„nþµ7øõù‡Õ§±£”«8Î雓JÔu¨êö¡\…:þYÛ•¯‚¨ÝêôÃÉ«õj³U^yáÍú¿Y g¤_ßl¶VT^Öfýi£„vu¥×t³é$¬¶Ú­M×ó‘ØåÝf'aÛaï©ÛY×­òªƒé±³aýv#… UìsM /7FÔ&•Þß´d]ÀËaøÝ㓺ÛÔ¢rqº|Ô›áUo6[ïZëùs>À—a°Ö¦1{ñ‡¸.#¥®ù`·`²7hÙÃÓ3>â6Wyë⿜þýDjV§/NâŸ89ýã«õ¿šÃÁ:×/+a*®JïÖïºIûJõÛîC<ák:ìË¡Ójã… :N 튱¬ÏûájEe7 ÄFg2A¢öÄwwUHR.«;6ç¶¥‘ùô†ÿñóO‹*TAÇËaû/°$=í'lȪR^õ{ÃpÈY!ž­êðëÒh¶·'Ôäžû=TÆ Ýä×ÛÁ}¼žÐÒ‘}mn^ WúѨR‰‰FÚ|—)+r&…b]ôý˲Êè¨ £n͈»µ•ÁtÜ틎§¤}³+ѬٺªL\xºù©¹YK]µ[tÞ~ûK\nmœmi²4À³¸‡.Tªs-j¯UÍûìØPçìú¾]º¯*×€«9çò—uã’iÁû‰ê(¬q*Íbɘ'{á|o/šƒ„c홄Ô0¹ ¬9Ä¢Ì&ÉyGh·ï¹]^ú*HƲçc0½¢ù0MÀ-ž5æk°}ÊP23k„ýë¸M|dHÙc"&gcŒÖŠ[·!ñ7œÎ/í³ìÄ(°õlî¶Œ_›ßÝ#uybc‚@Cë‡tgpÇñÉç gyb‹Ig@±¿÷Û’Åñ®å—)þlgm­U_å¨ð!Âì-hã]² .—À°ÙØùß“Ý]nãØX¿AN©E\E˜ž¿• ƒãrø±BÚrNŸ2`Iv  èÁWåÁoÉR´S±UpüMYlÙ„µ0z &¢Lp!¨ýŒ€öÑ8¸HC ‚± ½Éö.7kšô—%[†@pH.ÿÖ.Å&W°w¿Ý {3=c‘hA¡Œ4µwï9Ç1¡qG[tf[!ïO=.<º=†Ý¼ÐÚTr$ËøBëp ƒgˆ‹(€à&á }Ù²¥ŒÜËþëZsس%¬ÚXëF§*ë*vfÙ©~ßj©†LØcÝùh‚ÿAK‰™«Ì&œ ãJHØa*¥\z¸,¦‘‰ïžÌ.#ì•ìf–\¶˜d&’Ë Of³ËÀþ³…Le—Ó S¹å’ýÉ´KÏ4aä.`µ}‘Jµ5ÛÔç±9™ £ú"24õj%LÇ¿2 œÃxÁ™JSž2Dœ&®¬¤…~^—Põ ’1ÌæÑL~Þô¾/ØÈ<åþà׈$T¡íÙY²Ç8Ð@$wLÒÔVè)z…´ý‘®å°ú–ìH{°‰ALçgGºõÂÙÊ=:ú›;§æ¹tÑ•da¸§¦ž&è‡Yg7Ç—¯oÚ?éxÙ9憸Ë]Q¥Œ0õ”¡ø¦³»ßT)-…vâ5xùâ,o–Di‘BUCòaºHn›Z±$`N¨ÔŒPG=QßÊÔCÙòOˆú~O—;Älÿ³:]{;b«Û±”žbÌ/‡»‡sØJ+1¢°›üŒ½ÒŒ$Ì`±!epÏÛ8iD­V2¨®ÐaFÔd´´ÖB™UNÂÐ7mƒÈîrýéÕ‡Å,ŠZC=CƱ@g‚Éy‚Ê}2*„,èþ/äŽcLZ[În^™i¶ÑÂWYþëy*èÈØårGGÞ“ú`îAr€µÝ[•D8n—ýµ†_®-1Rñ¥>gi“À†íçs&²oÀŸ"PÆä &Q8d3aëR‹Z¥[TJmSÛýõÌï‚+íyëEpåï[ÔíÉÚL2®›­ý„Sá¡Tººá‡º'’:èÛÍE ¦;˜9B#‰ÄPÐýLÅÐ0$y%ļt^©­o}mŽ^%®Ùe‚ëÜÕaÖ­ƒÙòtó¸¡-`ÅQjrÎÀ,‚]dÜû-eÆÉ.“£dˆåúO0:šMiÃìðDáþ5ÒTÌodfu¿Ù%õìlC¸Ž¤ŽK$¯ÃnB„?ÇÕRsÕT·ÙÉö8f"ýUZÖ<ˆå×ä-M|E ‡üÆ“ÞÄ;ák ƒÏHñžSr’©ã<³f‡Íä#ÒÇý9ý‰Ñ§jŒí²oʳ੫ aŸž«BÚmdzÇ¢[¨BÜÙ½¢'A[€††Ü…_FnkƒÍ}»)Êâþ‚ž¡b…Á:3ÈË æXlΠÞþC(]Î%÷$Éÿ źš Zÿê„×ÜŽqîûPDz‰Ê‚%YÅ%ÉT°ôf?F·å¼ d‡Ò»~è‹\Ìú=ùøó­Ö“N~m½ ðÊO! ÉŒÀTþû=0ŠcpéÕ²  Pƒ£T.ÙÓàq-ã£ÔÎ jÏ~RX K¿F"~ÜRè½?öpTo>õy@&{NÖo’d},rêÄb …SNšÛÄÍ«“YhCÆ­A„2””Q%µÉ>õßÊgÕI(]/þ%#6ÎhÄÅ^Áæ5Ô¥WhÌ:Áî$Žit¨ýè#~L¸5L+ïÏ Ìj‡–"LˆÙ+`—”5Â;¿ÄÅDcØ= ÃSeœZÔõ»QW×|‘¥Õ2s¸MÛ)ié¿Ð² ^åùéÉ?âÿÿ]ðË…endstream endobj 23 0 obj 3145 endobj 27 0 obj <> stream xœå\ےܶ}߯˜·Ì$† HŒŸ”HNÙ•ÊÅÞ<Éyio²W³kï®eýF’Ê÷ Atƒ< ÈÉÆ•Š¥Q q!Ч/§›óݦ,Dµ)Ýßáâíû³_©7×g]óæËßû‹ï¯Ï¾;3…tº~ýöýæ·ç¶£;ÎùÕYY´­)Ûî¦m+ÍF—¦hÕæüýÙëí~WR™ºÚnv{ÓÖE%Ûíùn/ì2ŒÒÛ›]]´µíör·oŠÒ^ÕîѪhd#ÕöˆîØÙ?yeµ×MÝlßíDÑh;Ûöûnú¶”Û‡ÝÞ®´éz=R¯ÍNµ‘ÚðþÔ‹MzVÂ&}C÷oäwÔøv×¥ªtµý–ÍïP^¥¬ûE›ÒÞ¾µ;UÔÒ´º´kd}ü¤¥õ’.7»J›¿áÎÆø³ÆöéÎæüâ̾áù7üä¤=%Ý÷WþîÐW­–²òwo'}¥Rµìï¾Þ´ºÍÎ-b_‰º0Íf/daº6n„þ8;ÿåëíïvöÍ+ÑŠ†ïØ‘.‡c²û@'zŽá‰ždýYëC´åàœiT:¼KÞ8tv•݆XJ¾À°ªüúÙ^ªèÀ½‰Ý›°ùÚ/“é'½÷È?8`x’ø¿w€*µnøXDZ5Iú/ È$Ó¨G46çAä&@j’z„ zôÈŒÖñ)á0Œ>g#ˆyâè–£l¬‘Ü™^ ÑÈb«Ãc-‹ªje4*© %ª%ŒÆë¼~¢ÎÖ*çå®Ñ`²Š‚7³µ"7ú™ØçQÎ3c|$Ñc\Zim9¶ï¢ ûÚI(À&ckÆaÞjå]€ˆNº…ŸVeEN?»ô¸¬¬g¹‹eo{Ÿ‰n¸-î[Z9‡iù„á‚N!Ia¥‘?ÛYae #ùøl„Õœçš ¬ÖEÃUÌM@†'šÔA펟BˆRý>1pý# ЇžL¸Yìy¼ÃiÄ:öùþÉâL„Öú’|Yž£ü!v­(R½ç˜ÖÁo¾ÆSF£b"<>î?vÐí$sx $dNGwÎjŽM9Û骥..{4·ùpû"’v_Eæ8!H½}ƒÏê­„4ÜOùëâH¿e g¥äÆŸ‰¬}I­/HôY:ìÕ®±#é*Pˆi¼lÿùEGþ~N±æ]²”åM|€WG™,ö蟦UÖ°³™ô6¿ òfü†œRÀ`dR¹µ «{@R “8 w‹}O8µN¯^â˜wJ#ï˜3ÐM=sëȉFéež9&®¾ ËJ òqyÛ’¬Ø¹Í)³„º‡š‰æ"½ÁÝÙð×Ë`¤dÚº(MÅ•Lˆ$DsmÒœ±f9üÚ'ñ ¶m6M) S÷‰ü¯º…ÉÖúÝ¥J×·þú±{D—¥ê\ØÐn] ·Ê·•µµ¥=ŠÏ:c ”Ó¡ñv']Ú¹jÃvÑ´d\s0,×@¡Œ[.eʾ"åû1Ä œ`Ê’íó‚NË£ÌsÙƒ ¦ÂÌ$ö¹b‘ŒÎ3<¤–F…z”Ç–1VÆBídž•a"á<{>eÌ&‰ø)㘴Óg˜§àª¯%T¡ÛiŠ9ç¢!Âëõa¤ùÐIr¯  Ëî§B¦ak¢cîE¸ö@ÜÞoSF PM„žiIw?Á¢“‡,÷ D Ž7X7u@’f±ôôn„³Èí š€­„Aù€œ¥ Û`…â7I7Ͻ®t¡ëu Þµ oÈFb—õúûΑ0¦nMÊ‚&ìÞ{{6µ²åp@TÖ4£¹Ä¡'X_Áûd×Ï…yÌÄ›¬¬íÊä ü‘ëäògNÜ¡â‚;Óc¥èE¿á ¡Å7c–hÖàEŒêâYŸµ}N˜ÌŠŠÕìQ/~£ÔοO5lòs ¸"½ó†«ýÅ$9L€¡âŽx/àhÝ2?LÏŒáBÀMP]v ÈàÏ¿Lq,Kú‰m:,°ÆÊíM¸Ï€”ãé5–ü²iT LDséÆóFµiÝ·0Ö+-Úª£ÌˆµŒy¦º•E£6Ò±6=+ØÅ¼š”U¡ý2ç;i.uL™ý˜ Õ"þ¬ëX¹:°4»¥Û'ðgR4…Šé3æ~OkÁ„àzˆ4ÎrZò„˜g"Ê2ôÁ„qr'¼*Y¼¦õOá¾ù°Àˆ†f°ÚÚϬQúÌ­D({zû¹¯†“…¬B5ÜŸv{YXÌ ŠVÕ­ç\ÆÙ½p™ˆÑWxڸƗºW‰Ý4Õ=º©›÷Àý62ûþŠhëDµ8`ÀZýœ¹õg¨p‹p:Úœ5M(}Äàfc¹A=c&ÃÑá„L Ü >iÂ&69þ0ƒsÇ=W3UË€äyD8æF5h9 ×ÝA(BOcUQÔª@ÉÁ30ð£á))™¦vR"UJŽ*.€"£yYTöÖQïl±V~ª½Ÿ‹é­ÿT4säèÿiÍäå M•ÅñEi#å´Æb•ؾ˜ø&&‡ŒN6Mi-R(-Â:{¶ÔÕY ¹MÐëÑ'{¬š|X–Úì%ã}4 £¦õ³Ë:l Mª%¡‰s„ÚTR8€åCë*îñªðI| HœÀÚœZL ¥&u¯ ø¡jO¸<‹?XÑVE«Zô½ìª0&\®ùv&'ŽPÇM Z¤6©ˆ……óä&.9P´‰oàb¼S¯­L†Â2ۼĕï[aL‚+ksŸÍ¦óœæ¿™ƒíZýk))e"ÁÁjÏ™˜ÂJ š~ºWÁ#vR­>!Ÿh0\ìÈFÊ`Ì:馚~S˜ªÄŸ¦Rº{œ³LÊ×}]©×©^¢G­•/êz#TµLŠZ:fÆF¹L‘Šª¶‹ ƒ0Å{2£š +3_¦’NÆ?çpDŽfΞ‹€fRç§Â‚‘‹q±¸3»àLòîX¼©>Ù¹3NXäPñØ›½Tvb)2Û¬‘ÙÚ¸–ËlSþd;ܾ‡R¿JüYˆìúïdà‰À ÌG×Ì6Ò‹ìè³+/²M[˜z$²Z»8-!²jÈÊÚŽÔK¡Ù ;lÓ{¢míx]‚§®JkT²œt%Ýp6¼ìG{>‘]üõÝÏB`×ëXNõn·;ÏR'ÄG¯Ÿª åÆÏ!>UUøÑ˜>Oò ?ŠL¸[ÑaÌ;l6‚ìxÕæ„Ör-‘aüÓD„VÖœTþcª£Ÿ;¤„~nZiÕ°âËLr%ñÍK=t¼ 7Ïü*à'o×(¶K}ù™^F¬¦pæc¼b söæB ‚&,ù™ÈRgë¤ü×'ªuË)ä’‘yÃάŒ9íã³Sx{˜†~âÇ0¤?©ëS˜о”†z¦äºßÙŸJÇ?/4ÁE>HƒY—|nŠBYøILïNª_bf5%¬IY…Iv¸Ÿlè†?‰Êwìs‡KŽ¥‰Ö£ºH`-\Ž€îÕùÙ_ìß/ŒZnendstream endobj 28 0 obj 3522 endobj 32 0 obj <> stream xœÍ[K“·¾óWð2eŽÀ NåÙVÊŽ\VdR•ÊKR»kïrWZ²bý '?ØÀÌݘù†ÃÝr¹,bðh4º¿~Aæe!ä¼ cc{?ûü™_?Íšîù»¿w׳3[¨ð§éàííýüÕÚOv.ª¢¬æë÷³²pΖ®jˆ¹uþ·«ç¦4Í€ûÙ¿o–ª(KeêÅírUµ+Ëjq•:?†N'”ïÜ,We¡…6UÅ»?-Má*]»ÅœxÝ. •_UÎ(éwôyß-U«ÚÏúÏúÛ@·˜ûóÑ=šµoXhö3Ö?΄*ü¯7³õŸÃVuQá*¿(jc]8Cêü¸”…Ò¶’á ÃÎOKW”ZN O”Ò‹÷Ë•ðíºªÙšw©µ§…žÂa\­+aøƒ­\©ü4϶ÊwŽŒ¤ âªÖØÅ `ÓžÙG•UùH¾iêÜÁ•6ü0=^èÚ¶TÙÒŸï6µíœÒü‰1`OÝ»æ¬Áó†>Y׉«5_Š–g³9W?#RUÿ] /DUY±¥Žôù¦‘+YyéAÖ;/ílz64RÊ`C9-›4ö~¹²….×’lZ<ë‘/È,VË•)t-”~ µ÷çïtiº¡^¶û±e+us”§©;¨µZÅ•¨:ùJÇ #n䤔‰aÜ|)õz/çW‘õ«VUûÓõ‹ •Ê„ŠNRm ÿÏŸ4º,Xw®&¾?¤ïï3ï*6è~‘ [kÑâú;/AÚ–R&§2ø§ÜWÝ%_”9¨¯Ò4>ô”†Ò¤‡Ô:6‚cÊR×|©šnåLøèçéºôŽ/s¬i±ë¶UÙKñ¶®”Çío×ÍVŽq·}èlP–/¹¾t9'šÏ•–bÂXènnÒH&pÓþòˆ“ öø£aú5¢ùó4½=~` “ýsˆ˜û¨lä6Ñùq€ú!Cšäú½åu˜$´ö!ßúË S>µ«M!MDÜGŽ”è|ER°ë‰ÌqŠ!y8ÂhFØA°ßÏÑ9ÊâŠ#áÉ*îÿcZý„)9f”tËÇE—3æï’ôôPºå<éÍ… ì}E.›uBŠô0TÙtè<Ød›n3A«fÑbüþ)ô°Æ<°Ô¼™°[l”ïΉIÀ ()X“pä4âÒDcúsBRO[]ñ‘L>ãê¬ÙùFÆ;äµøF8؃à±ãR ü¤ ¹fD(ƒ<ÆñŸÓ|byBá/Ûéy$”¦ (šìiRǦJhdû–¹Ìm œ?²ÓîÁØ&ÖÑ>–‚¹&tŠ Xój>G¥€Þ“$'ê˜$Þv¦Ï{2%}˜õøØ­NY'’êDŒ±:ó1t§sýüi À­ë–…9]¾ÁÿÉv6†I¹ÂU…•T&žÕŒØ%ö$X¨òÙ²Èv'Rðœ©ê̳ÞDFáaL¿”0 >¬d@WVåeÖžë®áeÕUœ¦8¢Äó#W:rÈ”š4ù¼GøŒ7i€½8ƒMÛ åÑ9®SA…ÌμìÍŽ7ìE-Ûg;_¦÷77é­9‰3|3sùK™àÉêûRd=!ë4бTútÒ;ŠÙf ¼Ã‚`·×Ù¬v.3L;P§G¬:Õ±Fœl./?ž˜ë¥—A§î pƒÓCnI¯e”ôNüE/þÈNÑK«NVQ\ßòÄõ¸ÔEûjñºe¢Q.†y½Iìp|M1@WÆJ—4V{Âå,‘ÔÅ ,ÛõJC§æ³­˜>ýÕK›mÀj mcØ2@Û°þ ´oã+¤dõaÀŠCoô¶…}~EAÑI @È—8ö­ÁH W(ß>£ByöJ³JÆ×ëÙ?ýß_b§Sendstream endobj 33 0 obj 2651 endobj 37 0 obj <> stream xœÍ™Y“7Çßý)†+Ì@<èÖ áÈ^À¦R•üy ËrU-ÇzS•|ûH^­AjgºMR^¿´µ-Mÿ¤¿Zêñ—J´RU"~ãälv癯Þ.g«æêÙãµqþvöeÖµ:þ­Rûä¬Ú_„Ž}%mÛ»jñf&Ú¾ïD¿ú·¬º>|ímå…m•©g³õ^#uÛuÂÕŸ›¹k½’*±N›y×ö]ø«?6¢í¤ _ê×`¾oæ"¸zݹú/p¾ÛÈÖ¡úºJ›¹legµ©š¹nÕBÕ±›ͪ^^Žà{WÿãrRø¡1›vp9˜S®~·ŠGõÁùUl5Öx_Ÿ7sœ­Ó±u÷.bº×>bz­ŸÖé09σ‡ˆCÈÁYõ:ayÙè Ý~_ü–AÊ*,'º®­‹k:,>#Öþ\tÕâ×ÙâÖ‹5¦éŒ˜_ÍW`ž[OÀ¼ó4ï¦läZƒ©LP‡7õ~qÜ?Á| æ§üiëq;aZÛ§ã>ºtòªÁü ÌC0À<L…ŒëÀ÷0+0÷À|^wÎqú´¸œQŒ_×$y*Ó|YƒI¢½_l]‚ùÌ÷`¾óÌ—MǺȾß\A-a ?ÿ›¤NG‹\à“à+Æ‚P"ì{óæ ¤%S¥à FÉ# ‘¤[œ”8 WÆå:KL ¾£,t+/xÂcÇ<à»™GníU˜_ý=Á$L¡ê`×`’f!×°´¦KZ»Î[’íµ†m‚éKb KZ»AûXR¾ hð¥däRä8¤[¦|I@ð<üú?!;Tb䊹Û:rÅ‹¼x>hBäÉ­Ÿ’m’È) Ó!Ý2åB²P”œßÙ§"lÎù–€ ¶Öå:ånƒ~!r»uä”z×!Ý2ýxBâËÜ”û¸CºeúéxÉŸ‚ !Jò÷H·A?=Où”“7‰œ’ó=Òmˆü.oΙWMJÚI"/½¬åÿÄCØþU¶± Àj¤l?Ü#€ý¿¯|<7;>"åýÝ¢Ä £©”¥[ Ö»…‹'Ä©¸¥c 4ü€›\¶ßœx¥2¬Xcý¼[ˇ3)±zÊ=erwB—"#ÌÒ'Ûgû„Dxއ@)f°(Û;„ä`3<JUƒCƒ~y“ÏŒœr«Àj L?G<æ 3X ”éçÁn½ ”ª+†ý<&DžÃÌÍK)f°KÐùBäÉ›"Eˆ+F˜¥O¦üc^Ú¡(«J˜5PD8ZÌž†Ï?ˆ7Iendstream endobj 38 0 obj 1136 endobj 44 0 obj <> stream xœÍ–IOÃ0…ïþ¾0^³\‘R)[kÖ¶p¨ §‚ ÿ_".­ì4NÈL´9tšúEÇ^Q΄¤Ü]Ûb¾$'£Œ.¾Éú6oНY‘œ)÷Yßëù’žÚR¨9-Ÿcß gE‘óbý§ yQþ, ÍxÁÒœÚ%™$ƒÃcÁ„εJhYæ\3S¸RjnX¦Ã2õcõ¦”f-“\1­d›±™‰Ø–òo™ð2Êfv@„b&¥vHìÑ$¹ d½$AEÜü" Ò•8…CЭ³p @~lŠCÐ8³‹àœ_áœ#ócpÎÓ˜ók€sÞû§8çYkòoªwò3‚ô2Þšü[\ÿ ˜/ÝD,?#\Û$ß4Xèî<Öü}~ÆãÇrB“—î²Xó÷ù±¸U ÉO“—î²ZówÎïpÉG¾|HÏdÑžk;ÎÙd¡»¬Öó+É ¤½ciþ,Öü}òq‹9 毼¬Öüó'œsÈËo²Ð]Vëù•ü<to„à@‘µŸü'„?öÓ—ÁÝ·*˜näaã*Íî2¯eÍmÇ­mŽr:ý/Ìà䄌`tÛ˜íí´Ü´Õ¶˜ÊB{Ù/\)a¸±}É/Ê×ýÂå¾T8ÜÚV~3KnËëö¤\˜endstream endobj 45 0 obj 499 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 36 0 obj <> /Contents 37 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 26 0 R 31 0 R 36 0 R 43 0 R ] /Count 7 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 48 0 obj <>stream xœe– T×Ç'„Gª¨Ä ’¤u­¢¨U\ž ˆˆVDD ‹C „GD°(pYDd³a_D±È"±*bµE¬Åª¼§uyœÓcKO}ý&^ô¼Àöõ½s293sçÞ;ßÿû¿ox„© Áãñ,Ýeò™:jwè—˜_î¢PjUQ‘j©¯¿ýܹŸüyg¡“““t—öýˆÔU+Åž$Èä eŒ,V½BêÂ>-—Gí–FȵÊÈ8ihX˜,Œ›æ*—EKÝ¢äQJ¥"Ajçb/u\°`á<öÏqsTÌ®ø8é–ÐØ8é&)†tƒ:”]å/AH7icw;+Â\”2×pU„[\¤:jKüÖhyhbÌ.©AÌ# O•˜Nx누7áFÌ$Ö[wb6±•Ø@,"| b1áG,!6ŸÎD±™p!–Ó,ÄþöŒ-‰çS†›³“IŸ·”ƒ(g¿Xy%…Y•HtµæŸ(êÐWU¾oŒVÜ6•Ì!v8˜Ã7n¡qÃÍ$H©ðµ~ ëâM$:_§ Ú—©;/qÇßqCýð.¯<§ •‰y_r\ë«Üè+. ÙWRƒ) Ÿ×ÓùÌ x)Ì­É>Š Q{R›ü¤¬%XïŽæ¡µÑþ›¼Ö…âñèPØyzW·æ!zõ ¬ilMÉ]÷(}íûe_Wɵšó’ãךëºÑ)T›R’Pœ“’F›ÃY‰À›=œ`Šsìf€ý4Áà€%<¦~A}5W.Ó‚¡¯Î7Þºfº5-ÛÚhÁàƒ gzû¬QkrëÞúÈÓúµˆÆ?PÏp‡° œHXFõÔ'îTíW&%JâUñŸû³ÃIø¦° ¼Ip£C= ñÛUi ]‚D¥HñA4+áû¨`:”s‘/dÎ g …!^+ƒ6Ég£%46² ¬€ø±ýñ·âÎë¿ §4L^úۊ㱩ðÛÖ`×@_\¨D¾%XÎîéf¸ ‚¯_HF—w¬d³›,®°xÓ§ {K6à`Œ!±”ÚŠ‹Õ—Ck×#Ñräì¿&Ð5v!r¢lñ½Óo7»j»Î‰Ã áå/CÖa‹µ˜øÔ/Êp&Þ“HXÕ0•9 Sy¿ƒ=ó!ÌäO0 ÂÜãÙE¨ˆ}ËŸ`1CxåtÐZl¾zº£tGLºä€dRpn´Îypn áÎ(ïR#Uûnäjøè($aõ'Š,@ö`vR°eÙÀTY †®kJÒÔÖÑ;Õ›#奉b]qzQV5m©Œä¬t”†v4l?«jUw¦ö þÄLý&¬uY­dîñmÇt…¨BÔØTÙ% vŸÒD§gÊLÃèú¶*Žë#ï; ÛÇ]—z‘¨³NÈÁ6áÿaûлk±ëÅ&?ñU¨!9ËUB%«ŠG¿#à€Çƒ'GÀç¬FíBXC ¿>® OÉØ—! %º}¾×¥ä¤æ'Ò°ˆÊ¯ßWx°•ŠP~vaN! Çj˜t¸©Öµì­÷×wÕÕékjiÁóÖ¶ÚëWØ›oUÖG7î.õFôðÄ?¤~Æ8²tô!a%uU§—'—î+HB‰H‘"×jöÆÊSF<͸òGÖÏ«¨“0“£–Ï$Ý8çöQëð“ ]¦ímm“ß=U“[p\\ñJ—y€µŒH\xìpNqž^Òä”9œq6¬bÍ}ŒcþæˆÈ]+ñîÒÜè0Æ6‹`çÌ! ì1³¦ îC™œ}4û¨Ï¢24Y©H‡ü*‚Oï9Ò—ðs4Øtõf{gý]ôêßzifæUY îÎ+W~ÑbÝ{¥íå“ÎX߃YiYiâ?Zï3Ö L‹%Œcš9YÖãÛIËýý=(`oÝW¥làÕ’[ðˆZ1<áPb¦iE+®ú?»w¹¾÷²8y=9ºÒ#¶6˜"fH‘£ÕðxT……ÃCcEñ§p¿ õÞ€-°îûž‡ø×±~„dØEâEŽÃáäˆâœqáÝ_g–Ùhs²¸À"÷|Ìu§½0YŸ^‚ޱÎ:’Spº¬òÊrŽ¡bÔ mR—GׯCx!Zî£À–4L£ôÝ'+z}±j_L‚.^“*ÑìGÈ%Õ;Í*è@¸Nï'Û³y"Y™¬!Žík µ_¦vØ\E_Us9#DŸÓ‚D”š“~x? ó(< b„ƒ“6(x½MJª³ùÔ"É…‚/r sŽdçe‹Šrr³‹Ðïá1P/„T('ñJv:^º –r§ÀÇåX‡ë9wÃ$Xy ¯äNÍßLgÌ;>¸üaû ÿØ-CÇU$j©UG¦d¥fî—¬Å?¿ºc8s‰š+5)™É:‰ +òüÖŠl‘ûw.\ï«òÙ*Ö]jü y í*/ýc«ÿý`Ò9­ °‡M‰ ñ}FŠ»IÁ¯ªCuÓXlƒ G·Öû©|wHº¡˜ìÆ®:³”å>¸:åþH}ã:†§g`Ê7ÆZ‚‘û¼`=?lÈe $v¤°z¸#—éàî^ä6´¡ •¹”7|‰óûÓU]X› wñÛ·rò&orÒ\]Í4s¾ ª¦€4ƒ™Y:aAüwl endstream endobj 49 0 obj 2770 endobj 50 0 obj <>stream xœx T×¶v!tUGi[@´ EâE‚"(  ¢‚Aˆ€Ì Mw3 çfnpŠ# Òˆ#qŒ$Žq6×!Æ<“ëõ%jn²+ëpßz§ºÑæÞõ²þ±èî:uƽ¿ýíoÊ©åàà ž“²=6z»ð{,ïáÀìÆrÌÁ¡<ÿçXÑ ÊoÒ¶ž%=PGÔé~àà8>¶/|ÓÒûPŽi¥UsÓ·ÇnÞ’ì9"|É ŸQ£FÛ[ÆûúúznHÿÆ3 :)vó6ÏáäÇŽè¸„ÄøèmÉÓ=çÞqq±=7Ç¥'nIòŒÚ´)z“0lyT\ôVÏÀظØÄÄ„ž#æøxN7nüò1!46~CJ’gH¶Ï…žK¢7§ÄEmÿ·FŠ¢-ôß¶qeÈì„M«Bç$F¯^6wûâÀ¤-K‚’c—ÎKY6?<8jù‚ +FøŒå9zð˜!ë5nØøáfNœ5ÉorÄ'k¦¬é»nÚôÞŸRÔjµˆ  |©©¡T5—šF¥¼¨ÅT 5G £‚¨ñÔpj)5ò¦–Qó©‰Ô*œ ¦&Q>Ôrj25’ú„Eͦ¦P£©9ÔTê#ª;åLõ ¨žT7ªåHõ¦œ¨>”ˆêKÑ” ÅPŠ£úQž””Lõ§fP®Ô§”5“r§fQ(?ʃH ¢¶P,F|J/¦®:„9\é–Ðí•ãFÇÝNîN§w¢­¢çô:ŸþŽñe‰»‹‘øÏöuwëþÈy•ó½z´õØóF¯ð^Ç{3½ÃzëÃô)íó²ïö¾O]æ»]žKVJÌ’’ ýè~ÞýÖöË’Ž‘FôŸØ¿Öuœk²›››ÊíŽûf÷GÆøÚc†Ç6w=FTrä;¨mÐM֙ϚØãìoüë^ükdáS,0­4;Â&Þ,½O×êu• ]:‡ÃéôÀX'o œ´8èaL‡GÞ¤±·¨ËPG›õºÊ ….‹¡ñ*Þ,ÂëétYG/¼îAã ŽQ.'#ŒBÓ(ºÖ@Fä‘-Í Ï·4)Ρ‚“¢*Ú¶ò K¢Å\,°§ÅUò¦ó“¥~´²laÚü"’N³íÁÌ«}‘ ¥YŒ¦j“Xrb׌ÇY¹+mí(^ŒQ,cßJ7ºÆ¶• .tô£¹—'†lN]¸˜%›È´@1 '»xfq•4òŸñ=¤QG–×…"10j4vÃýþ>\¯_¯;ÒÆ}a®®EbƒÜ§(*ÎS°+#$Í!=»ÍºÿOp|pº=½µ\ÍIne”j••å:ƒ™“4¾d$·j•†VÒ˜¥ÌKç޻ХBðl˜ Éw øºJ~†G`‘BȈ‰$wkŒºŠ*¹6ƒÃŽv§ž ñ¼ƒ5¢VÁÆ•‚q7:Íæ3WEC–á0ŒD´äg»]í> ¦a|¾ø3Ñ:ÃÖ£†wH‚Ó¹ úÔîXøÞißÁ§R¿‹ )~]ÀácŸ6–OÙcßÔÅoðxx#!@#[&øcOGJ¬°YÁÉfëh£utçϧèi˜ˆÙàAœM€ÞGÀg‰Å‘ü†wØWôË{·žÝXzÊA{ÑYñSÿoF²Ød_ìXV"[Fãx+}óhšéé=}š÷§dÕGa?xÊs®7·C€#Ü ^Ô@×õår=QO»aši<§â1*:&–ÀiÖïÖ@Ã<\/±ƒ»¯Õv_±änYù¢þ¼¹±cÒGˆ^a23w¯c#/ó%¢Át†m 5Üy]aTcú„4\î(ÙÜ¡³8|C¦ßΫ¥kN,Ú=ÍC‹>ŒˆZ›0…Ч2ØzãÞ0ðjûîSgؽ»ËkQlÊÓËùE ¾`åçÄÈݽßBOî6Îÿõ|¼<´Œ-ÉÔ**¸Rg$èwbj”ôHÆ¡-ð± EÈ‚x ^„¢F G0(¡&œ<)d2£‚LN¬£¯¨ÈÓ§s>PFŸš!þ!CŸ¿ÿΜä18y‰ûø­L \ÎBàéÛþ>c§ûùŒ˜qÿÕ«ïîýbC̶ÀH‹_ Rt²¨.ëЖ§ÓÛF‘ôó‹{áþxÇKèqºÁ˜gÌSå+¹[g§„ L¡é7³[œÌ?ß¾ô½F÷ªlj­nÚ@bà:!Ûn-p‰mŸß‚™Z…!;[•—ÎJNàÁø†šÇüzÑeÜ'2iú¼Êr½ÁÌBw8TÈàÇëE’´0æƒOÚ,ü$kˆÜ•bB@Øûb_ ßÀÁ pW˜~˜)LBÄ?a{áA£1ƒE˜ x‚çOäKÌÙ³ öl¡Þ 4ôC¦Â]``ÈppÂýYœÐ«–Ž)ŸC™ó¨1ëȈ݋ÐrñlfÈÄ£Ù÷ÙÆbÈÜjBòa2Žh§Ïž8r½ÞRP´—=­B¨‰Ë‰# *½¬”‹Óꢑxü‚å³¹Ðİ„ô~áÓ…ÇþFâ¬3=á°Îü¶Äe¶3=!ëúB¦fÐ@¿¸÷óß§ÜÂáý]ˆª#¥+·eÓfCg0`–†L'(ƒæÑõ¥As–Lâ:]ºè{!]„¼Zþ°¤6»JÎ’e»yEÒé6ÿÕ²à*ø9'[Aü‰½ š‘´ÁI:C'¯€Ï×áÏüÅN$Ù|8L¢kUY–(g±3¾Á¼·hY}‰Œ[G"‹…`ȱ†Æ}üb”õÉ7ItëM•¹ Âç´¤ñ t;±ë„ÇNT—»;Y§,+Ò V7–«gTrŸ×§·¡•(,áã©‚1cÉjæ[\¬¨#†ËÄš¬úw"˜"&™¬¸¾¢ç3Üèíìø`e~ )Ýe…Q]VR¢f—ß0@{Puár¢äûM…Õ-_¬+—s Š ¹­©ÊH†¥I±$ÍŸ\¶ÍcÊ*ŽS‰Ïð/¤ÿo0wI^^-Яٱ«_**ì~‘É>ønø0Zye¥Öæ”ð¿rÊÀÖtHÌ…¾V²J=GN¹JL|/•n9Yµ‰%>³#‚·ÖnÛ—ÎíËØ§¼§¼¨Ü«Ü›½7«f;JKL˃7MúdnãIVÇHËF¢W‚.䦒ç4¢’•WèôfÎĴĜɸNˆMôìâÃé q;¹­õѦEº‰UiJ™kGEnMÍ€–‹‡î=ø2~†• É,Õ)Ê=*tF)Ó·ŒdQD2VbÊÉ#+Ø,Ö™3­6Û#Ä É—£í‡>ßa^k‹7^àf× ‘ §M®~Qs¹7£Dîy/#™®úçƒÕ" =?™wylhTrèÊN~ˆ±@F§Ü°ºK-%‹<ØÒºìž,¾dç‹xÚ']ØúÓ¡S§Ñ5ñ÷SoÑQò—±<Ó>ÔµdÀæÎÍðEÁÓVútrþB 4 YŽwnv$Ì-­öZUnõðrèr²¬gàðHzÙç›+H>ðšørwôÕ¾k­ÍM­5—Ñ tgƒf¥8CK@¦5Ö²/s>+KErZ6ytYí|ŒÂ’#£V®‰÷GsÄþ žðû0˜Ú~£¶ár§"þXal‹#¬"! ÎŒYÕ9vö·Óß=¼“ökøù{ ›ö&y~aQ’Ý47c)Fnã ì æ=eü Ýàñï]1&v1TwüOêÂúÿc£T+GO¦#ù§Ò¥"[Â4–³{h+/1µJ½ŒíŒi ‚q —Ÿ,é­p¹ÕU2„_ÌWJ%&KTdm˜ÇA“¢j75$sRŽæ|›s[V§Ú“µ/£f+Ú"öŸþ1Áü8äßVx½¸š?“à$„%"ÉPQ^I–c÷3S„嫤[Ðãù­×mÉ­Ñ»¹8s‚~fEˆ1I—X“T“u_k?ûäQ{ä"µûZe…G…V_˽c„ ÌjÍÿ¤ y~>áœÞ³ƒí6X˧ìP!¨+‚ g{ú;£ñˆ>×åmw»öÚÝ‘I§*Þ[™³GÁrȤß+Ö¶<òEKÔ[WI2¿ ÿ{Ù$I‹å{rïâüJZrbÑÒEñóDàøâ8±Vh·À)Á¿d…t)ÖÏØ«6R÷üI2mÕš'W²ÐŸ1+õ9YJE‹}˜;óÛ’®!ñ»ïŸüò¡Æ°€›Å…Xõ+"H…‘ÁOéÿ.NËZuôí /?Ž ÎN_Æææ«T(W,×ç™JÕB|´kŸî(ß=ûÙj.€ «ŠÓ®%¤ÒwrØ$NrfÖ7+~¾ýÕÞKíì §y — un`dCÛÕÛÇÿh=­’µz¡ž/²]øÃzG¨ë‰†hÞ,:h-(ª6¦*;QúˆÆá)¢B‡ò)¢‡Ö:±*OKÌ0Š€ÕеÜAúß´à&ð¨øsé㻯âZÝÅëܰ#a)íËbÚF6ÃvXíV-8·¢R.¶»Ý°¤`>ŒKEPz¿‹Ófþÿ±õ–sÐÛ@äsLµ%§Å-‘u˜»î«iI[À©+±w< çë7ÐúŒ~{û/Û°ŠƒÀ¯¥¿>œ6ÔgºïÈ3ž¼yóÝã_: :¸î™Å¡ "À"¡wÿvļ8vx‰®Ñ_ãÖu)ú‡ÙOùœ^Ò‘ˆø¢ÁŽKùDÑÓ.Qáeg‹:áX°³Ý‘O!GJeFbî¶ „¼‘×á¡7±Ó®hƒÜ€ÄƒI]\Z¤áæBßø7èôÇß¾…^¥š’R¤&%–!—µÎõµ#„‘¹råy¹¨¨,Ÿû÷Ýï-$žñCq¯Â|T„ ņ<“Q§û¢†½ Ç ;ú½ÛúÏ pJn’äHÐyÖ˜>ÄK±ûqUIaIr—˹¹ú<}1§.÷(_줒¢AÒ™ôµIÏ>Ç'ØÑ ++Ò!w“QoÒè µÜ4R2¸·Ti´Zdr'»•*‹‹òYì¾^,‰/)öòÒ¬GÈ|O'‚HŸi£ +’fÂ|ºÖV¦³x>žÉ|¸ ‚™8XPÑ•‚Šîe½œ#ˆõ’à²âB²Å|÷¬Šœê ºÎÄÂ0¨êÙ¬_à'þŠ´šÙ™Q“’œ‘‘R€òK”l-þ®$dmä^‰´ú’:q-Ý–œ’°+e?WIؽsß¾¤ñ\çnù"²ÓPíf'gÒLdÔp⑬-Ûn×"÷Zd0–íi­Û—ý­­FyM×Vakõp>’nŠOˆÞ| þg¤›îkjŠÛm]uq ô%fúŽ ¼´K N¡«a{IÒ”ÜeH¥,ÎÈÄ;܆ÓU¤UWb"ºZhWv¶ûÙ÷z‡ÎÃ}ES8I mPÔrOè ¼]¤`rŒy¦ ¡¦„­†ѯt¦µµ˜Ìd"¸Ôj8¡ý‘½àö£·B_©0ÛpZ}Só;þì)-Ó—–!Ø(×Ëó”…Ù*ÿí_ ”Åù@rRA5š ༱’n &U|ä"Kºå90©©Êá0©q%ASå¡a³ ˜Ú˜ýi{3÷ç]•ÝUçï’íΪúœˆMIù‘Süç5¶°’5Eùºl™pá7ƒä5ÉkúZ¶šÙ»í|òÉB1vf$åD¥U ga‰Êz[ñÍ%ôO;_Ÿ`NÔT.2¥jSËw”gïA{Å—/ž¼Õ~)n3ÑÒC‰žPYok¹+LçnZ1»e_²1R-n#›ÖrÍ í¬frAÓò™ð›&Î ;bÇY˜ÂYo¢ÿ"ÝXüü•]­½¹ÿäî†Ãµ-¨5¤ïŒ=°Ö„ˆ'0hAZPüÚ”ØØÔ(…>«KjØv2ý&º*ÄnN#ø7B¿F`šž7hú¡Ñ: G:Ù²   Ko^¿xñúíðós¹§3MÑ«ÖmÚ°ví&ËÉÓÇšZ9Ü›'×ÙñÚ%kÇ@Òñt£µcäšhËÉSMÇNr`ÂÒ F‹ b£ÂWPP\\Àvô½² òUHîN˜D_ª.-ÕÙ”‡&Rö>ÏKqhXfA¾RèBÐP¦/CjÜZD˜ër]|§ËÍû!L·@ºÅå•åaÿ,~PÿGö^‹éÄ–ú/jOU¶¹²·.¤å™ëSc°#º}Ö&߃v‰75ßeV¤Õ³k+h‘ž°’Q^ **T±yªäÊÚdä>oixð6Mî .0uÅ ¢Ñ%g&Ü|v²±¾u?›Ü(MÞ´5e ‡®h»~¹õعòÎÛ€ß\øK «\-Ò2æÒéo.¿øA,Y½¡gÝþ+~œüddÈÒ„ˆ6-IÒÅ„&­®RÍV]¹Úz‰ïßX™–0f,‡sq’(Œ‘„ñƒÐfU'š$š-BåÏï!µQÇ'UPš_VˆÜUHUPHÎàVTXLè)5*}>ðx•æñjEA~>R¸£Â’ÂRòçVª"ÔŒHL–¨ËÄV|B³Å…÷µ¤Yï0<––”B”¬ß_ÙÛÔviÀ³nx¯X•¿‘MÍÌNGr1q±I_VVn`4œ1_Dâ‡_E,XŸ°)*Û!Ï,^Ĺ#¹õêéü³fÍ»liÝÑ5œRSP¦Bbyž\.+ÏnÈàÚOËÎZ–ùû“_o®iÿôg%i~ ÷–?š ·©%Rè{¹¹Ý¿œÓ>xåVeÂBVQ˜£’Šƒ˜W-æË½õ'øÙ•5ÁKV­ÃÌj޼¶ÕôµìfÜS»#-.¾>µÛG7ìªß0ÙÛYõ³Às¡þåû‚íŸJq¿q]ð|ûÝüjèwÆ®"Wóì§u¸‰Ö0°Û"ÅY4ÉB¿‹v à ±À „‘Ã|dù‡<ÈÔ³I†j :Q_iRêrJ98ȼ º5‹| -Œañ' ¨ =º¼qåñ¤+H|ïÁ¡ƒ{*‹Qn.—’¹Q¶­B[̉Mñ'2.¡+b˜Ä€ÛÕŸ_Þ<³*`†•ø‡¢)â€ËáŒÆ’Ò:¶ÅIV(+ÎEâÅœ½réø#Îæõ'Íði="‰Ê«êIê %YTt픩>JJL‘ M™¶œm©o55Ë^™0dÞlŸÅá»[#8•.¿”¸37/O.«Ì:´ƒ;ûÙÙÌsÄÎßÿ¸€“ßãaëb;V Kâ>–?X´1Âé«.×(ƒþƒìÏ£iÛ&Û,ÿ}LÐîà ùÒ"sq%2¢k5íûZÏÜtò!2#s‘!_Ÿ¯.Ö ±Ig0™·T¯Þ¾vç·pËd„Åb<ç)îÝ_?ø¨·³ž ß軑U‡K÷´šj«ªm®'2üö¥ù3&/™ç´òÔÓD®À€JT»¬‚h쟬TY™_FôQBòü’Bµ‚’Ä´2]¾¹£ºHS¨³&þfë Š€  |‹Oê0«P¡»U{i4U>ákD×:ij˜T[Zªc@+Ói!Ú:U…M•Ž´«F`ÿŒ#|øáùu§ê,B®Œê×xB>?òÎp]*yx§ñ@Kû€§=5jÚ¬ ¡‡¢¾a%?ÎŽÝ´bþ€‘Ïf¼{÷ì»××7›}˜õƒjÒÿÁ•eÁ¡+üüB¿¼þmû…ûnt’üøôê?ÿy¡Ó}´ß¿wõÒ« 9Çc)ž‹“yE…HåžK’¸^SfÔ±à !àCtrM¡žè@¢^5…Z•®oŸ‘\ЭN79òaÐ!-I/ÍD ’ÏBÖEøÍˆ˜…ÒPºZ®Qh Jò†PÈÒf¿vmÏÉsܽoŽ>stream xœ]V TW­‚®EDÚMÇî#›‚Üp•¥7T„ˆìÚЈ4A‡%qá5ˆâ”qƒˆ8€A„¨0qaI\H¢žÉ˜“ŒŠŽñUç·9ó«Qg2çÐtõ¯_¯Þ½ï¾ûMÉ,(š¦‡Äj3bÓ££Ü½uÚii¢¨ Åw-Ä1–ë~ŸiÔ3c¨Ee†!kKd-;þ®u°<µ…Êá5‚²¤éÌ‚]Š>51>!]í²ry¨ë„ ÿ»2ÅËËK½^ÿæŽÚ76-1>YíD.2bµº”¤ØäôÙj²[«MŒVÇkõ) iꨘ˜Øé±UQÚØjM¢61%E—¡vñqU¿?yòwòïýʼnIë7§©C¢’ÓÔ‹ÔRþZ¡(ÊiÑ}rt°.f±OJ¬ï&¿ÔxMZÂòôÄÍ+6®ÔFe&­UOšEQî”#F-¡|)j)åGM¢Þ£–Qj<µœr¢B¨Ê™ZARžÔJj!5•ZEM£QÓ©`Ê›šH­¡S>ÔLЦFP Ê™0KÉHÀpª’ú‘žA§Ñ­ E•%mgÙ(sidù Ÿ1çX»›íbsë¹*®‹{ÅûðkùçC\†Dé°R[EX­³Ê²2@µlÅ»åôwàf YÆ$»½Jw€-~ÆÚãìȆÀrZÜÎB vfÀƒÅ{I žÅâ’WI ¸³­@–³ÁVÀËÙ_à,c#¶cÝsãò”R¬°Ì4Ç0؃…S'SÀíäVÇÆ„åäîÕ+9ÃÎ’‚2T‹‹Žîÿ[iYeå5H5ÊGIÙDwš\nwÔð¨äÍWIDSùÙÏÉû¯57ötÚ¤Äß“•?8؃9ï°”%ÑJyó7É$x }Ô–âx(äÂ(Ïo±ñxšûX¼kƒ©àðð.UøVï§KY‰xßu×^´ío)«Q•žª.­G—Ññ”R_ÞÆx Œ¾åô p=$Ö´âuL×pcA¯3¦ëàfLÂiêÇ£Ä~§¿J"w  7ÙÔ’t‹HFÐ!~.`/'Lc?¬ÀÌ‚™Ï†äòÏUbFè¿ ëê7Þê¾çÏÛúî¨Ì†¸ÄI0¦–Û$Ñr$v²EW{P“—«q ›Ð±¶*€À\ñ<ûbÁ™P•¼5´îFâ E7:_UÛÎc+q˜ðÕyÍ{®áþ~~á½Ož¶ôõ©Ì…—0ö€|N0ŠûìÍÜL±¸QüˆTR$JI;±FÒMd£ñ/b†°ýê¶zmCÈ­) ˜&¯_Gbgœƒ·;“"°0»X…Ø<¥«<ÈÞÿgð„i_ý VÏéN¨J·ìË,Žá Î4‚ð'B$på Èf“T¥~U@VjÙÓKZt=ˆû_ÁfÞÙ|'þ‚JÞßpz¡Â…''.áÁJ7ˆ2L£ñ ïhé%(‰6^‚ã#þ’ !– ½¨uä_Øv6þñÔNsö“&bÄ ~ î0ìqïoÕ*¬d³ÖŤ®FÑhcÙ–SÛÊ·ÝÕÆòH(¾WÛð%ÑqMV¹îà–O3vÇñ•¤ÒÀ–î'd^&õÛÃY,Qß±ÖÎ+=ÕO0dÚ'ËzÃ:5UXF^Jî»™ò˜‚æÒ< O ™ƒÕŸ9ñ–Ñ…2›ôCç†5§Ø~¶Á^ó*æ SYÝx õ  '?ëä1Ñ'o6½ø¦+þŽƒµÊqI–‘Iú[Iázèà™ä à%[ÓQâä[E$=šôèhP ö¨ƒ¼îmÖsòÞ{ ÷í/ØyD Ž\~áv´¡¨$o^^÷éNÀÉ?¾ €š‘…m0ÚA~WœH¢Ìääÿºq"XGNÀ¶x6žóË`ôÕ³G:/ª¼¹…a‘~«*r”x<[Uš|&é\BWÖ}¢…?ß¹ŠÄù‚ÄÀŸ }½]WxFt?~öùW=*³»uâ¼%áL—p~Mp~MÖþ øŠ_·-1 xv-$Ã… À¶ÔñÑBجg/Oª6fº[ÆÐÉPÉ,Ë»˜³CGÉÕæ”äl~gCÌ–…ñÉE%zeö||’lg÷`«ÃLt].­¯©¯9zµ£¾„f_ökS’»Uð§jÊ[•ònôYZaÿÖIÛÓÝ$›UÄDº‰o“L%Kü²¹éê©£9›K•%ú=[Qï›uë[⪵Ç"E" Z£‹YÊC97èðÂÁr°µ#g”ÂD<\ä"dA£óÙë'6-þålSd"=ÊAþ{6IàaÚq6ý`Þ!T³6îØïQ¯¦¡ý€®liO8³átDi0âM 3âû2?;‡ï8œÍË_Î.NM|%äê²²?ܺeû:ÄÛÀ÷bÀ#úþ %æÂxâaã5½øìô樟ª«vï«R>æ¶îÊ/ÈE|\Þœ'Õ„å×H»¥§£ÍÞ )5&ÉHR+bS“ÒÁšt¹5Ý ã ÆYŠÿ÷¨;»)¾>¬cÖIGR@'/숙Ë+ÀE§z¶åif-½8$nòøåç¾Ù®$ŽUŒ'¿œ‹‰Ä–‚åpnYVSa¶BmÐPûè/Zkûi^°Wiv顤ìÌæe>¿2ųöbIë!éàÈ(ÿ%I5וð”¤lÍá¡7ü»Ùz¼«^)Ï â«ý•lXH$¼KPm2 ŠÓŒæ–Óþp¸/ˆãˆ;J‚ðã.ýœÇÀL¯`¬c°ç mÊé;¤èÕRìB""SçåîÙø@)æ»ÏãWŲÖÚÃû7=@Pÿ±ö–Ö¦Š+è/æ r/ƒÄKÓƒùµƒÃL1ˆžÀ˜mj Œ‘ Ê„ÔÊ¡<´s`1ÿvÂNó‚±mžlÑ¥Êýˆï8®×ç}˜¯ÊÌEH“’;J­]ÜPÄÁت^Þ¼±ú|f‹¢ }q¬µ—g"Ýî܃ò0‘Ås!I7ÏŠŽð]›\UßXQ~¥DÙR\Wôé'¥ûF¿=Ö^7ûNáö7o3àÏâÛ$×&èf°‹Ïâ› Ü”&!pãÞL@•¾``ðv*žá 3¤+ðÅ€«˜Ãþsâ9Ò•ÍïÃÞÎ.]„€ùƒyk×ÿÌ/woœ©ï:2e›Ì+ú-Úq/‚¾Ä”xZH”rSÛÊ“(EgD¬àå­ÝÜÿo‡€Î×~@̯uÕéó©= Ãp˜qsó­ø‹ª¸öÀÓ(…§&®ä¡„+|Aèmõsv óÓø®íyþØ|üî;È–1Ù"¥Ï“Å´©‰Œ M va1c:‡ñœ´ë¥APÁÂPñؘn0Däo«ÁèCx‹1F xý+-ÓM6µäæ÷œ œh2 ‘R”‚™;±“äe²×W6é•b$ëðJ¬¬`ìP°*¶¶†±û­‡QÔj$" endstream endobj 53 0 obj 2696 endobj 54 0 obj <>stream xœuW \×öž23"‹(Ø$‚»²¸ ‚ ;eQ\!€€lëV[¹Z[+J­Ä © àZÅ}¨h TmTú´jß™ô†ÿ{w‚¯ý÷÷~ï—dæÎroÎùÎw¾s®ˆ2@‰D"cÿ„Ôœ„ìä¸XáÊ·ñÃðŠóqÚ“tù’© =?›"121ªöÁQK>Ò®˜CÞ`J,åmÝ핞‘Ÿ™œ˜”­¶`ìøñÝqvwwW,ÏÿÏ…wBVrbšbä$¤¦g(Ò²§+¼ÈÛ©©ÉqŠÄÔüŒ¤,El||B¼0-265!Eá›œšœ‘‘ž£ã5Vá2q¢órp IV._¥MËR)ÂW§Æfþí&EQA³óÓâÏ)HñÊHõ^µb®Of¢oVRXvrøê•órR"sSc?ÊS._°}ÌØñ ‡ ŽN]¾vu›´cÑä)K¦îtŸ6݃¢&PvT4JySî”#eO-¢æR>”5‚šGùR©‘TåG9S£¨pÊŸMͧ(W*‚ ¤Ü¨Hê#j5ŽZ@Q“©ñÔB*˜šC9PQTåEM¥Xj eL™P"Ê”2£Ì©Á”eII©dꊣ¬¨\jeM ¡<©¡T:eCÙRÃ(HH‘Á|ê[ªC4Z+Ú9`ô€ _ x-¶¯?6šn´Ó¨U2^’ ©’üHKéƒô-ÆÉf4ì0v{s xàŒÑ3~=ð†1cìmœk¼uШA‹}5¨×ÄˤФÌä‘©©©«édÓhÓTÓ"Ó]¦½fƒÍ²ÌΙ»›+Ío6/:­¥§ZȂңúò'pš‘¶þóJ˳{§ã‚dø_¼‚é ¼2Ê#J9g‰LzJÃ3²Ûx‡6QU—˜O×ÜgÖÍ­L¬ªœƒX<Ü á9Ø£Gv`Ñþ>(‘O¡‹¦,‹õF¬ÓüG0¬/©~n9»|N‰Ü„ΛxdËsP ÁbÏŵ8j%XBÃ& x§ÞXd¼®Â"|U ðaÿÌG~»FÌ'YA,=htJÌ18©Ï§êÌ%˜ëSjx !NáïÔ°M …jËv-´kC´ÖR¾Ý Ôt3º°·ö4+}w¢¦ìâU¤‰kt<ÁJù{UÇ/=´A¹u ÇŽG}ëC\¬§µø>…àÏ\D•äÌýF‰âP|QêªÜ̼´ ÈKþøÛ —9‡Ž¬Ý—EVÞ—½+%Ñ%}’²:{u–ríRÄ,Q+oÖ*âCIô¶sàïéM$³xÏV¬bàoJâÞØø‰.hìÃþ‡G»Øaoì­±ƒ±0ºçÌ‚··x¼|‹'÷üŠ'¶Âæó<œÃÚ@ –WUZ¹°ˆj~9¤Ï2˃]p‘„½ß@h9ï§“Ã ® sÁ"<].­Ç>]XÃ4º{N&-ô}Èà>–ë¹:[MçMwu y f`vóñÏ23ÝŠþpü¤áe±Î†¬ëª×Òz ^+%„@Có¦zä‰ÜŠl5ß«׊uãxÄ¥›“ »¡±hFjDh€W¢=ÂÆ›Thö¾2ïAÆ ¾èÕË}Áާ7D­K.LS†'{sG: ª†_¿œŸ~D~(swêÎÖà/?NEX~I`ù….kéÑK8{é³õYMˆÛnñ¶¼'Á,÷K Ž'$o÷d¤w¡ ˆûåÆ4l…L›è:¯ÌÁüZ‡Æeø7[›èÔ31_k8ÔñéùÂÚägÓ/Ž%fš@r`žõóp &M@—“Èö]œè"ÑÒýégsl^ö]Íö/Pñ^Y3“·mí–Äz.‰™%wññkÑ/îäw1ý2zJ ;Ô¢ŸµÐÝ+&ÎÜâ Ÿ¾ŠÎí©=~öäÞ:tŸ…§µá2|©O©¥y[#Ø‘Lwã¢éÓ#¹Èû5á 5©E=D¨?‚§?\­EüDµ~+Ñì2µ~mψ™s4ÄûfB(kiu³ ™•‚f`У½õ'+YióÁ½¾º¶…}Êmݸe-Š@ËSy°ÒêD7qZ X©L©ì‚Sd•ÇüD²ÌLFÚ{+va¥¿-êŒYì9©ÂçL”¼6æÊªËèúápÝ=6ƒAÞë—夭N]–¿% ä’ìïòvoøfS;™Þ1¦m.˜#ºàØé“çwßC`Î’ÅÉÂ8/˜ëiœ‰­±EÄL׉ó špEÕM ¿;s^«ñkâºB­/ðúSíëº`=ü@ÞŒ+~ãw ›ž›Íç~:+媼kE9(ahtôÊ9KJ÷åÈÖìþt÷¦jÖ•Þ†M›æÁ‡$á†<¹ûZsÆþ€|ú^¿oÓö¡ê¡çÏT5Ý?‘6«ì}az3ËD-¤.‰[ˆŠô);IM*Ò¡Cë Ëdû J2Ñ ¶¿2i‚.œ½4û£ha ?o©Õî™Ök-}!@´z#ÝYç,—òÅhé×1¥Ê¡ © “öo<¸¹·LòNÌ8FZ}òè…N°œÑŒíe¸ÛÀ…QñÕDèl¨È=SºŰ“bâÇÈo³[ùqM¢§„´ÛI'Áˆ©x„·ßzyBy·¬lë—‡e-ÌÚÏ׌ØÄu%ÕrÀÏ3È$~æ–Ap™è>™;ŽÌª/ï¤uJ£)vO„î€Àk•åyM€~Õøi…LÓò»¸b°Û‰gäÝÆ¸ã!.uá/3å°H"íþ%ëôŠ0Ÿ—º:-wÉÚ¹hŠØ›t:ãûOŽo9G‚°Å¿4¦2®ÁïI"ˆQú±¬îD}mÕ]tõ„ßsT?DÚæº?åð›‡wëön¤Ã!&Ù­Q‚µ–w;aÉù<>ÇŠ/ŸŠé9E¸[@ìÉÛ2ž™ªÃ¸Ý ÿ­­þPs½Lš7Gp§=Ô 3øÙB‚)T¸^fAAÁÂÏÚR ófJ6Y\ŠØçõ5?Êu¦ ÄMµë¤ ´ÔÑj|CHùO-ÔúfwØØgK)È…Pi^=C$!È„>ås¦LEPcKørÉÑ{`ïA†¤/—üçÁœköáBéصŒ~Ü>¸Zò†Æ+á%©'3q¦ÄP«t²2ÑS-oL´¸žÌÑï`Æz‡â1¥’d‰‡ ªÑe–ßáHîã1¿;Ál˜ žÃ(¿ÃÐ+©tve¢=/`qæ™0¿„Aó>]Y™Ÿž\°±>ñuOåu1ØX5 ,@ÚÞ–2þl?ŸŸø VàhðÉ^ï0‚t|#éÑz8Ñà@\{N¿æ~×;I ÔÀiz"ògSú”„P$ˆ->­üÄ6ËÓ]Pjâ÷õD·7¯ÿ m²Mûx÷A9Üd´~ ˜›´:>I–•±N¹yû„Þ~§¦R…ØG§Ó£ä«””S°æ–²&$3u1òc¥y÷B¿×Xqéºì«ÈƒY—ÐTºõÐvRÁCé 2³“S—ØÀ„ªÆËÕ‡»Kåš]ßm;\ÊþÙþµlfÁ× "Ÿ¡Û@ãFÞ ÁÐÿK/‘ðÒ@k™¿B}ösà _ Á¶ÄaØÂ$4xáŒË$/h KÀ/‘ôö÷‹ï»Þ'!|­¥õNW3ÒÇ?]¾Ùtëx‚¿ ÷ 7„Ë›'â„KþCæyÄű>±9s£e©Wb÷û!´dÕÒ VZÿù{ku©.’^£ð’A»Ýg¤õ!'ÓZmAÖC*íðœô˼fÌ—C%£p9§ío3‚ÿÞfüáÝŽ®Ð ‚ÉÎÀžÆ6ún<ï–Œ¦ñd2ü íi%Û…ßh˜Â¿„ú—„«tQe¢;Zñbƒýyf˼¢Œ¤MlsãË£? ö5ÉÍgÂ+­Å°–¼3[á@„v¹_akö2í öÑ0×zðЀû~x=kq÷ ¬oC g.A8¬Ùù´Û׉"ývÆ®˜Lã¯+‡”´ˆù*(ãpÄùà8çäÐäRHlòÅ£kºËD]ïÄ]o9ûù ò]“»x3±½åø¹gr0L‡DP w9¼Þ Öá­¨ë­ÂÉû·ÂÜrÝBbh>q¯ë['¹LÃ:Ý:C+ÝÊ«T¢3=PÝ#†Ãº(.…¦­ˆ^œí€ˆßxC–‚ŒS“*Y²Õ¿,¾#[Ñ0÷°?b=Œ^Ô'<[²dÂXÇÅ¿B$DÖ½x!7Ô-ÙÊÁ.vñ[9ýV.j“ŠE¹…xX$puñ§ÛD¼{˜ÿ¢‹Ó¤w\û¾ºë«KCþqéB+‘äëY—ckbk|眑J|hfÂÚ˜Í^¬†þâüW•»**Îüp°±7Âf†¯\”(wZ€ÇLYæ¿Oʧ ÊùsKµwÉÏZÚÃùUÜ[FÚ¸ˆ.¾ºûð±/Ù&tÓÊÅh;Š‘N„mVo1ÿýÄÀe¨x…m–u(íñí!;$X*n']½E"}×Û3Ùe~¤ÓøÅçß|*'Å=¬dÅž”~-I½¤ÀzÞ6=I “+I­½wðhÃ6`6­Ûbû™xüç2 ½å̶ý;÷•՞ݱªº%³¢ò•ñ˳֥~\ÌÀ„±í|Á³ƒàÙžöÔ€ç»v;~Y ƒéÃ$Ž \lçp& ™p[Òot— œºDüt‚o «~©·’<¦OÕÛCþéÅ@»‘sƒÇ{.®íÈ!vïšW’^¾êdăÔb·ý«·0Ž/ñ°¨¸5ÊåòÃ-ó¨:Ÿ2ËzíÒ³Mäg-}ZOÖ¶g¤OÏÓÿVÒÒûVÿ#ߪpÒ]ÍÑRy¾Xò³!äÁ]ͺßþ›ªÎòg*ÑÙn8C\ØD”©Ø™Í˜BÈ'!6(Æ7—ä‡ð ïfžª »•ª¶0½¯@CœµXê3?cA‚| ̾òò5º…ÎÄãÇY™Ä=mœëà8Í=¸égÍ›]ýŒå%híJsã¥4öÀŸúâ1‘¸ˆ}N×Bn7,7Hfÿ2,;Å$í!äÙ8ÄëM_͈÷¦ÿ²T–Õ==PLÖÒ7d??“ö¶žÝâ¾ ˆÝTD -\=°8`⃥2é›iñqs=l°Õ¯Î Å¯=ÀµÇ]w¯•av’‰ÎÇøDÇÌž}ö~SýÙ‡rÜ`$}£¹>uJh¸ó¤Ð«7®¶b‚ð׈èo{ñ·ß¦‚[m¢šÊF˜ÌíÁìO¾@‘þøÇšS·Ï5—u!àÈÎ)ÿ§ØÛñWƒ«„½¤í¸ÑXއô8Â÷ë÷_þA^Œg‡…‚Ѳڢ&>àWrê[~Nn!Á“&G^ÑsãîcÃæå-4¿Á¢?+ûõ‡/7z/C5㧠߉!…ÄTx`‡›ÿ‡¶ñAÜò[‹ˆˆäïœ>χϣGõŸÍ²+ør¡y ª UƃT_š˜<ÝabJQÿ%Uc endstream endobj 55 0 obj 4602 endobj 18 0 obj <> endobj 40 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 39 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 56 0000000000 65535 f 0000019555 00000 n 0000040801 00000 n 0000019445 00000 n 0000018313 00000 n 0000000015 00000 n 0000003423 00000 n 0000019603 00000 n 0000039989 00000 n 0000038129 00000 n 0000040294 00000 n 0000038543 00000 n 0000019644 00000 n 0000019674 00000 n 0000018473 00000 n 0000003443 00000 n 0000006875 00000 n 0000039082 00000 n 0000037235 00000 n 0000019715 00000 n 0000019745 00000 n 0000018635 00000 n 0000006896 00000 n 0000010113 00000 n 0000019797 00000 n 0000019827 00000 n 0000018797 00000 n 0000010134 00000 n 0000013728 00000 n 0000019870 00000 n 0000019900 00000 n 0000018959 00000 n 0000013749 00000 n 0000016472 00000 n 0000019943 00000 n 0000019973 00000 n 0000019121 00000 n 0000016493 00000 n 0000017701 00000 n 0000039381 00000 n 0000037646 00000 n 0000020025 00000 n 0000020055 00000 n 0000019283 00000 n 0000017722 00000 n 0000018293 00000 n 0000020107 00000 n 0000020137 00000 n 0000020169 00000 n 0000023025 00000 n 0000023046 00000 n 0000029702 00000 n 0000029723 00000 n 0000032505 00000 n 0000032526 00000 n 0000037214 00000 n trailer << /Size 56 /Root 1 0 R /Info 2 0 R /ID [(Ç\n§6Äv^•ÊåüÇ‹)(Ç\n§6Äv^•ÊåüÇ‹)] >> startxref 41000 %%EOF simh-3.8.1/DOCS/massbusmystery.pdf0000644000175000017500000007071110345704026015205 0ustar vlmvlm%PDF-1.4 %âãÏÓ 163 0 obj <> endobj xref 163 13 0000000016 00000 n 0000000966 00000 n 0000000556 00000 n 0000001219 00000 n 0000001468 00000 n 0000001854 00000 n 0000002393 00000 n 0000002429 00000 n 0000002651 00000 n 0000002728 00000 n 0000005126 00000 n 0000007796 00000 n 0000000789 00000 n trailer <]>> startxref 0 %%EOF 165 0 obj<>stream xÚb```b``¾ÅÀÌÀÀÎËÀË€¼@1 äx 0ÇíáDq†k¯€&ªÅNá&ªÓMT 8@„†+.go Í a o`œ|÷ÍÉ«¼x׃…¹\å 4ë -ÁÀ´€ì,k c`ðÚ᳿‚¸ ÀhŸç endstream endobj 175 0 obj<>/W[1 1 1]/Type/XRef/Index[29 134]>>stream xÚbbc`b``Ń3Î î0*/G endstream endobj 164 0 obj<>>>/LastModified(D:20051207200825)/MarkInfo<>>> endobj 166 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 167 0 obj<> endobj 168 0 obj<> endobj 169 0 obj[/ICCBased 173 0 R] endobj 170 0 obj<> endobj 171 0 obj<> endobj 172 0 obj<>stream H‰”WÛnÛH}7°ÿÐÔB¤º›÷ÙÁ‰Ýx#‚¥¹ö<ÐTËæF"µlÒï×oU5I‘¥$˜q,Ébõ©Ë9uz±d?ÿ¼¸»¾½aœýòËÇ›kvµ¸^q–jøÿc:ͯËàóÁçÏúêãúj±^s&Øz{es‡s°uÊÌK¿áskÍÇßÿƒwë’‰Àá’BšW1g!÷|võ`}`w‰ÖOµfwïºRåûœ3۳ʙ-¬9›ý¹þ÷Õ§õÕ§;DxD-ZÔ¨Âa™—jANàq"ë÷—w¶,³}R¾³UQ—©4IhæìÓ«ÊÙmή‹ý¡†OØçLW|q¶þÏ09&NÊ…¯dDÕ *)”¼¢¢|,žØª>äÙ×™@¤g¯ÔÁ–œ{g+âNÌéÈÓÃüÈq%Õò?—‹7Sñ1î0¤ç9¾ä£«zåì¦ïHÇõã8f¾pÜjÁ›€•êjÛ?Ø?ŸËñLáQ :W`”K©“û `V0Ë*ã^ ^è²ßu9×½=æ0†R™fÃpJuHÊ,f ÓÙ¾Þ%0bl ?Õ‹b¿ÍBëö œ†0âó™Yì–m2¯ªTø^R±d·£Ô_U’WfRŽì4 „kxj Gx؉õ `8Àá„ÙéÌö­z¯ò*©²"ï@Ü|ºf÷Kî-î——¤X"v"ÏÇ!Û@$îÃ7828˜ ‹¥E^•Ån§JÍ2ø?O‹²Tiå°><÷/ˆ1¦Ý=¢ÄØÿ¬K€¤ÄŠÜÍli­ßnW¼Q ¾*KÀ›å,«4Û”½#äHB'>´€`[TQÏYZ*ÈšÚ-ÀÞŸà“Ä\ êÞêgS÷—D³':q|¢äæÓž×ÏÊ®4FÒPEeÞf¥®€;•hÕ‹wl`St×qÝÿƒUléñâ J“F5ÝC)f- Cç¬rD? Íû@–xI_ ‰×¥ãÇçÎðBd‰g4êc’~}.‹:ßüÄÖi³5Fã{¾P.Pé!š;1Å‹Œàß·tÛ` Ô&º˜¹ß”æ»è=ç—uK\Ú'”†£Â ׈L¯dÈC’™ÃNÍa%>ÍÙKöüb냡”ÀÚ\‘À”«ê g;a×Ë_ÙK¡+RŸ)jIšõõßá¼d“p[&ù†9 fÉöE©Ø00\ É³bõš¥@e¥AP`ˆÄåÚÜœ5”,‹t.)wï8ÜüQJ ü­ Òâ5Û(×® {Ÿ\`QäPÑ‹ZÛ-®^ *Ãð7`ø6AÒãäqŠS°¿áÙœ’ù5Ï Ȳß ší1^߄ޔGa‚¡Sc(Òòf‰› äìÑzÍúìiÔ »‹6€uÿÇN2„Ñ´êqFÍjã1&D°_9‰†>m;ÒÃpÿYNÅDïÐIow¡ðÆ5Âð‡Æžµ-šPZÙ ;ô¶QÅA# SÀßÚUá*íWqr]ë莶T1¢”žŒÒn3@ĽÐÝЈ¸íwL‚Õ@ÆäÒ¢Þm`›kÜ÷®Ñ'üÒ@vͤ)X3΄‰,Ê“<Å×9L¨&%n¶£$™í(¹„-:ã˜g@a°°nvÄ\þ9ÿ–·j‚M/þñÞeo0ý,/ªf_bl ýéœJÀÍé{­“áºdª/‹¸Ó‘i}Å*4d ?é÷ƒõ³šEVÕÙ3¤p¿ˆèYz!¤t¸‡÷ s€20ÁÏwèx´‹e{°0ç20®à¸@ÌÈ“TÍàÒèqúc(áw-ÝT ä×g‚ŽÑ^*îô-ÈîUW:¡t›‹Þé1±tüˆ‹¦ÎœÍ|2¼^q“©ˆ=ø…ïÏ'7}iš8+ÄÛoŽÍQ7«æ$7†’ÂÛáAÿ`ˆ†¥¢ endstream endobj 173 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 174 0 obj<> endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰œW]oâF}çWÌ#TÁ™;ŸvµZ)´ÙjÙF`u+íö‚ÙP%$Òtûë{ïxüAñ (ʃÍÄžsç~ŸsyÇÞ½»œŒ>Œgïß_G¬w9šq¶Øáý±ÝbÓ¶ÆõŸqýÛ®w÷.óœ3`ùª7ä ç`2–/˜H¬.¿Ò{ùŽ§ë¿ø+ß2n?¼dœYž&ç\°ü±÷¥/Ø@kÝg7S ñ:€Lá…~³Áù/½›¼w3¡Øšx¡Š÷8®«p„¥h M¦ Jdé‘'Suwî6€)˜u ä1f ‰Ägl ª<èÕ A¥$Pº €ÊhZªcLcV%¦ö˜ã+Ÿa™qÄ_qU×Ô‡MµL,çÒ×ÖTÀù`húõÇyXG*kšÊfÇÈ2£®’Y‰l=òG<#A¨ÃÇ5oL³0ÔNÊ”˜iÝÉ·ìäÙ§ °ív§mšJCƒ ®Ò¾Â™‡þõ§ººt@M#ÇM/Xg)õ•ñÑ 7€r§¥[Âål ¨,ÊŒéa¨5߆ç-{k'hË©¬¯ .z\ªƒOÎd*ÂäÂ;‘1´¥Ê§ÒcÈ ãf*[ 馰C%©Æ¾£AÕ#h0F–즬C ‘QE³ÔƒèD´@"é¦(NâǵüDÒ=œü¯Î=#ôSò;Ýuð»æ€¥pôÓ¿.ó—]Áö÷›ÞÚ”³åz¹ù*„سÇù³[/þÙÛÍüÁ…2l žC(Ù¦]*5šÊ≌mÉa‡›bÈÀA;âê_-—Ûb·sg£Gm‚CJ»ü’ÒEM ÷ЦŸ†¯™&*õ`µbƒ!˜”÷§wl5\?|/•ÂyšÓUXÿœø†Ù ›µ»Û‘âä ×Ds<æRt³r«)Ý÷À@P›Ïö([ ôV5[E$D¢M­i]DÂgdÖdÄÛ‡ÎQȨ^Tå‹ÎVa±Dì#`kPè"SIfj}ê"ðÅ<Ïùˆn*>wÞº.l¦Z©"¾ðó~ž%§ErtÚZ0ì&ÓªhOûøÚM;L-†â‰éaÙxŠöR­Š)ßdzÁÙ6 ëq!`àk¡ùèЬi43EPÍw^ÓSþPFHøœ‘ÂQ_f­¾¾/Zö âØd7ëž¶lUhܰ1kÁMø™>ϼÉÍžL@ 'Wàß÷A‡‰ëÂïfÙ.¨TaË´ .}Áϳ€2ÆŸ­d)Ð Z-FW¼¢ÁÆÞÄÌ <©…£4„bÛÍ n¨"8Ï)Ên"ìC¿d«´ªbý3-£ìf½.04ŽØGªÎ­¶ÊT`çYGù±h ±§No‹¶VÆÁA‰m£}Õr4 ‹§-ªñç§ÍÞ`húû³Ø¿ņ¡džo– Åñ;tîêwüß`¨úµ§#3Ôú\ÿßlâ÷ºkè'׈´_/æhRÅóž­ž¶6ûtA`Öo,™;I¬,ÍÓ—¾{öŽúë‚‚¤HèÝÛiÂØ‡UãA…þ`#I¸„ endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰œWÛnãÈ}÷W4ö%r ÑlÞ9Yì`fìÉ8 eY» `ïC‹lIŒyQºIkå¯OU7o’)Ž7`lKduuÕ9§NÝ,È?ÞÌ¿Üß“üôÓçÛ/äêæËƒI" à?"£üŠ’>ÿ;|¾•WŸWW7«•I(Ym®f¦aš¦CVÁß,7$«¾¶’„šøóþZ B-~„&ñÍÀá=‹¬²«I±ÙH^Jrà‚“¨‚Gå””;žYd|Wð²üæ›7Ëo”’“$cû}’o õ=ß6IYëÕêt¬&èlfÔ NBn·Íq˜I‘ëÀ Âò˜ÔŸêã.˜¶—s0õ “ºx©øj‚oϧêuLÄQ‰œÒñ»p6m“s1ܬŽ×¥a{éôÒœ½8†¡Ô°ì&ò UÃã‰$kþQ=·ºº›c_»^Ó¦×'½4UfêŽýfyžo¸úöЬK!­Ágð ÞÛè®møP …¯‰%ùvÜpc™ÈdζIDîóSô“PA¨áÚaËrÐÅ:š5¿Úôµß}w›6±ê$M´Î`ä.^,ô‰g‡˜`$œûžŠÖ„:Äò ` ¥ ;?’ªí®­­¢Q¹cÀµšfºh‹RËVñ}>Ò8›°qËs.X‰„Œ“͈œ—C„Ôšép'¯ù,ɦ}V.ç$É '–õHÙ ëô¬@5c=N’4áÒ ä3X%9Ä»ž9“¤S Ì9.rN®_ý£Žé¶(m“ÔAOò<$åŽ0²Xþ2'öïS͵!~+z‡†ç¹fSº^ñ“lIƪç´V ¨º™i8nÃðÓºïXŒj³æ½×;¦©¶Í,ÓLœ\ ªdYdÉ+œÝ”Z¸<ÔñToR­/äk%àq1=ï¬e'/JL'Kþ€à Zm0 Ò™Žv’ /YÆÉâv1ƒ3çLÊu…óRiÊ/‚èq‰¬ë³8\^Ï܉„lê™ÐSå^›Ï©³ú+æ ò‡Ü“¼@éQ.’lzEºžY,” Þ&)׳`2ÇïqþdYV !Êhgèã(îý V´t‰ë؆ 8ñFµÃÑŽç¶^:Ä Qò}[ò xĪ2ÙT)Yíx!ŽäE’_·P¢¯,*å™»–oØ6Jõ„ß%%Þ}{ËSTZ£÷¿#Áú†B´ýûŠNÍ‘:¶Tu†ªˆg˜´“ùÇÉ …B)"¥“àÿVIô µ,^¸8ìx:„UÛ«­‡G[5yœd€Îõ‘ð—$æy„è™EFbþÂÓbÏ…T8íE섽Q¹&æ -SôªB±š'÷ETÔÕ!-»íÆyn“Ù‚V~.rÐÚiý´7Y>¬nîšä2—™AÿŒEQêààfë‘0ÖÓ1§Ò©·åžžBaœÖÇx&’ÃÖОÛ÷ÉÔ= –ï«××ÃÖEU–¦jB¨bµ±­¦bA7È¿'Ï9¸8f/*õ|8ô5¾å¯å·2xÓAYí÷…(!-Ç U›d+µW…x8=09 ×$E¬Ÿ”(ÜE%Gš.Ê™Ÿîõô.Ú%¹š½Kžñl Òx{÷%º~;­Áåî °&Ÿ–?$ä¾T‹ &MÛ ÙAø™8›ÐÍLHJuÉÊZr` GhE¾ý²»lÈA‚Ài¼Ääir÷7« VǺ¸}]ËÕ_ÝœÓ 5NÒÚR&ë”OU£ëä ËØ+Z’IT&M°Ú+ûJAu¡ 3üEÑ7#¸Å=Œ ®K+Ñ|3¯ŸØãd¹0œé¦ÿÏM0µÇù¾}Œ“!¤±G#pêBÉgÙM~˜Xwt+Òxº–8 ù~GÞRÝö}”oôHcTväß³ÐÝ)žõA­$ha¡A£gh ‘` áò£Úó['ÇÊ&Œ¬äýÛB3Aw§äáŸZ·ßúºÖJ©`ª®îâ|i¯–9"°(âRòXþ¿dÈmŠþJá¨ïn®ýÉ‹–wÔ |Ð붃ešhç ø].ˆò Ø€îXF‚ek¤n^e#õ`»j¬-àVÍ Y µ h[v(Äót´:Ò)ÝDc1>[VXz`ÇsÆŒýn}Àáë›ÍdyLy³5q6¼ª#Ø ß£æ¸Ÿ¡ÃñݶBÇ=Jõ¸úeñ0£Ú«ß-³ozT7É€í6Uï-jØaÓ5‘qñádÀT¸‚OÐÁÚ2k¥®ß=U\,»‰…)`zÂ$l­@Œ ‡ðL‘=Õnt²K µ!a*íš‹Zù¬ QÉö\TÒ†^ç™N†wœvÁÍå2FaH·J‰û ‹Ù¸Ž×ƒCXvkÃ>ÓÁN’Ó"$[Ú7‚ÄÇŸüÔ˜}:›£44a«P<«×ËDÝ28@!¿æ ^¡žéjׯž¤”‘ÆÂeÞ ¯)Š7C`¶\³ó7¼V¼E/q†ZO¬7¬vç-‡úÔkœ‡- ‘\uWÐÏi‚Ÿ&‡]í¦$Mžy#OA¿áífa5uÜ“òÖØûíÓ¿§¸p*`‚P!¡“ˆÎ!j6YZA“e±ÙH^ʧk]V6vKeµhn׃ŠzŸPœÚµê÷Õ rȵ`:2Zcz8gWÁÔ©¥'j¦~†ß—4¼ð*/õMT^?…‘5¶úùaKIóŒ@ˆÖÀ½(½‚ÕSp–•ŒÖÊaù½5aÇD|`‚ÿ , ´PÍ¿>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰¤W]sÚÈ}w•ÿÃ<ÜØ2BŸHŠ÷£0à$7qL6uSfË%KL,$J#™8ùó·{FBv6I`ì9ÝsºÏéVwL~ÿ½{3x?$*ùóϫလuS•¾Àÿ„ñ™F|ÿ¾_ð³+ï¬ëy*ш7?먊ªª|~Ô âmð˜Ç‰¦âûwøÉK‰¦ 8xsUb«ŽâÂ9x«³Öd¬š]x±ð¥G64¥$‰#Ó ÂbÒHÛûZDêm9FÒz „„áÙ]ëCÂyÒÍL×u‡Ÿ|ü1ÊÅÛC[kE”øi°dOTiwœä·¤¤ý÷ßßÜâ[.âwd€Ž¦h¦ëº§Åƒ%]ùŽÖiòÄB’ `ü˜CîŠÈvänÌÁZIðª(®Re¨çhŠÄÚ‚¡ÔJnCàï#Åë¥p€­Ö]Ñ8ƒW} ó呞Aq°ïª [JÏï÷oVW„ñpÜÑ´ âÝŽ§â6MtGKS/ŸâÏtU’’²Å2+pðè®JÞo ïw¶Ú¼ƒÜbJQ+ ígxuI̱OÖºÈ6ÊVŒ3²ò×kdïfJc²LxFü0L)甋ÌDÛ¬u­ìFû@Å7>ç9”q>ç4ãEú“1Ä` •؃Šæë)Žk•yÁ‰8÷#I äóÍ”„)x 6"´*äV&…({YmÒ$^œ´ý´…ìÜÃq@stbÙ*º¬ÝÛyH ¦Q‹y¢5A,èNUu )œ®’G¬ÑÛ<¾$} ¤“Ñ·uäÇà™I\MŒÎ²LÅ5‰Á6„TŠÉÒ³y5OóÕö©»%V‘Þ£«ùð®¤e”µF¸^¡Gn[DwÁ4TÍA¦ ôø†„`‡ØU›$}üKvªœ6 ¡<:èSpr&¶ÂGa‚!\Ìô ñ£l™ä‹¥èU9hO7Òw:i¯A ¹°¡Ò[Ñ(E›‡tC;¼@àç[X×*å#m•‚XHÎi:|Ÿá}‹ÄaÐJŸó³cmyì{©Š,¼PÕ%aÙø\ú³ûæâ!y¶¢;Ve° IA²ÂùÉ/Àÿ3üržÇ6³_ì&Å©}¦`JˆŽ/¬E°Ãð‹bÈaþCT:f…&Í(FœÕ+2y†Ö ͉Jbì¡ÿL4ø`’yš¬ ä¤cu$Ø^‚,’4…FÂ$Èñ‚dÖŠØ#wF<$¼%}±t¼Y[Ô_/XúñëwBö«õ»•‚ÙSQ P×&½9¿``Ą٣[î¢}ŽCiÎÒ•(ÝÅÖÏišBÇGÉbÞ6÷·sÎ*:zÛ5w­€E,kwl¨Ú Öˆkà)dó9Ô<”•BP9  7*sÿx‹rŽúJ®xE:°7AFÀ>Ï’´XCa„•¢æ–ªãå(J6æfÞ“>ÊÑuzÙÚ_2QXq)vržD€‰Ä„~æC2id9´x–œží°ùAƒ»ð!Ž; @°¼EÒô½G€k>Ñ×ç A‘Z @÷%#eÙ4¹ŸÁ’íî–Ýr@fV¾ÐÁis­gîöd"´Ä×IRTHQ?¥é¬ýæTߺ?)˜ã†«¡ \½Qšº¬ÈxmQ[q->À6àiÃD îb §†eJȇ «ÛëÚdÈø#Ò'÷‰Fœæµ<"yx÷¾»‘‚?n7C™~Õ_, .~&žXþw?Ý?x•îY§ÐcßÊ_þçr MàßÍUÿ~2z;­ü}.2$9¬• lέqì$û’tÃC°.úk)zv^ʤ\‚>]¿=†¼,ÿáh‹\Úün”ÎZ³sØz„_ìM>6@Kä,M¢ýñ|ˆ3õúÞ N3?ƒ%·æs¿ DÀ|fi†_®Ú•«ž×!^}ñFxËS¸€xõœ¡fò8kNîúÓÇû›þød†uÍpãA3Òx2z iœBS'À×I°ødÙy»~TñWƒéDk&² S‘R ëÜå¢N³sŒªìéOw„5<;œ|~±è'C‰ã5-±»&‚é⪓Éí+n´DÎ¥îsl%†(Hÿýéö8¾{2Ãy1;¯¹@Á–‰‡ûž÷é~ú÷Í¿b ×Î8ƒaó|µòÓçãÚÈ Y¢4÷ýápò“l…”3ÛRVPš]l«^YsïËé¶m¨uö¼¦›ÛâÚxàãíí‡ûþ»Qؽ%É#ñ—Ôëp<¯ã»žÃ–RžÃÂò@Óc ºxæöúz:z¹yö¡åh/gçÕ¶1u¹7hª(äàK“972xŽXÎÎ!e¬i]·h¨äÁߓׄ9ðŠ6 ðËÚ ÈŽ†º²ÕU¶úÔ(A_5v (ÐÑ`ðó† ‡È:áLN\ á C4K"¿Ì@-²xÚŒÝÑù+kÊeÍvSîbÿ`¢'ä endstream endobj 7 0 obj<> endobj 8 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 9 0 obj<>stream H‰”WÛrÛ8}WÕþC?J»M€÷ìÔTÙ²“I²šÕت¼Èy IH˜"4 ¯öë·Að")$“­TÅ´,6ºûtŸsp³‚_~¹Y.>Þƒ¿þzw¿€ÉÍâÉ‚8ÇÔ?ÈãlB€ãçðó]>¹[OnÖk ¬·“¹eZ–åÀ:õDÝÖoêµuÄR?ÿ‹¿­%Z…ᾘ!¾Ga}˜l¦ë=&¥Îü©Øí˜4àmÏãÙÜî!f²ˆx–ž á Ä‘d0ûºþTŸMH{¸­ÎžF/¢, À˜ ÛòŒ\d9ˆ-<ýQ–ÀÃ#5`¶þsrýºzT¹Ï‰Iœ0ÄJî'Ó}”¨XàY3R²¸€ç)W1ús Ä´[ÕUìóFz1m<ßWœ…¤½!uêv•úePÏ7],Å©‚.D§e®šx¾úžkÚvú ¬_c*Ùd{~¼=\Qw.q«ÕÙ£Å8½Ñº@Ah"v!Á¤lÓÇ„Æ{ãŽô†6¨’à²óÝv¨Š&:åDaNˆ ž>.Sƒs»XÂ%+™áMŠ‚Cùx? —õLÜ5JG õû_ÔI/O ÔmŽ TÍ õÂ>Sjßæª²ƒÈ Ø yÈgîT-Ùžç…<ŽRœƒjMÏ6„8& ôZl¦9‹d¼7à(ù!’'ÈE)c–ãf5Ë‚dçF½ìúÝn7q³<–" ƒOåÙ¿¬6ü€lQ°,ÊbÝ’vU:U™žéx˜X½¥‰ˆËËŠHÑ.©b™–ÿ„œÅ"KT†U¬ö½‹Tšäó2ÞC”Ãóxâ3Ââ^™ÊŸ &y”P°ÿõŸUÂìË Ì™„翵ÌÔæ«&ø ÖÇ‚£¬ŒÒ90ËDùA!˼`‰‰ˆ8°q¿¡üÿc庾+7«pp¬( ìØj˜<¯ ŒsTàÐÀ2Êó—2‡Ã ÂfóY§Ø2l’bÞI·E²ê ñM?P@ÔH¶¬¯æñzºöN!Të†é{ÞF¼À8‹P£ÞXÛRbBòÆí|Ú€z¦N£8ý:€¥}ªpä)kÅIê• aYL_|Ã)ø½Ó³Ó¾•âp–AÍðŠÓÛ&mp”Ôze32½ÊÝ€Dê½­•ŒRU…î-Ç#ëª#{dT«èœ¦o_­ks+b¬ú_(U—l˺tý6§çÇ´Ã\ÉTª šˆVT±w×õj7R—`Ûn»èØä2“Ñ7–BÔýœ‡@¤I[ ±H»sŠ"b»—…Týpëgô®ÝÇwpˆe®é·çóÛÛ³uõgµhñk&ÞR–ì˜"ºk5wÜ@ 0±Ór*^4ƒªñ×rNúíĵž‡G!§“/®ß!œ›U&N¡ãx?¡é¤ß#ôË@Ï!6=õUT¦ðYdH༗¸þ_p„>³Ã‘e²–|……ì¦ÏkÃÕ"À$A'3›{‰Ò •Ù—å’_Åqê({ž‹7ÔVšgep[šüàP¦ŸWD‚@ü©îXt®ýŒ£Æ§Šv¨62šÑÕóQ_jŠ3y†tÝ…k¯_Dß>ÜzskÂKXUÂÂÑÔ°-ÚµŽXIÙ0õ;ãþ‹H½9¶g«ÍqÉøzö{Ü3mï íZ&ñ,G›†G”ŒJ ®YÌv4«¦ Ó×€¾¾Ž`_½¯/¨ß]}GÈX…í™6ºÇ`<^¿©¸köœB©éz mÈW¸ç;^ )<üUò£bXy²r»Æ°Gvñ±›$P¾ÿqe¹7øŸ÷H1È/xA,Çà_bÇãÚ#7ï]LôòÌ(-«ÉV®Õ€‡ÏóÇÕåràÑ6Nµ-×›/—s‹ w²˜^Ð|Ðw¯¼#"Üox)P”¬ÐûÓ~!½Gu'D ±­åkCŒnøzV½aqÿâÓ˜èÛ$:¢‹†5‹÷Yu»gy,ù±²ÅƒxÏÛp—dyÖòöþv>›ûÓõýÜR˜ü;.Bb &¥/l}½qƒ ¸¶šewürKûÄõÂÔ£ÕsŠš¾×Üu6ö¾Œ¸xM=óÙ‚¿rï9C“y¾+‰Ï³y0…'VèîÖwÝ&È +ÔÉ€¨ÀÝzqkÀ]‰JæNÛUªŠrÒ%+™_p-(ü.À5àS™žnnË]‰©âÔ¡&õëúxûiˆCLHÂÿO€‰¬Òù endstream endobj 10 0 obj<> endobj 11 0 obj<> endobj 12 0 obj<> endobj 13 0 obj<> endobj 14 0 obj<> endobj 15 0 obj<> endobj 16 0 obj<> endobj 17 0 obj<> endobj 18 0 obj<> endobj 19 0 obj<> endobj 20 0 obj<> endobj 21 0 obj<> endobj 22 0 obj<>stream xÚì—ÝnG …_e€>À9ÿ@` qjÄ’–s%èB©QGd©HÞ¾gD&–¡í¶W½f9Ëý†ÃÃáJÜœwÁ;òìõ!ºÀŽˆ`Œ¹¸ÅÐ\HŽr„{vÔrr¡8¦X]¨Ž™³ƒ G°à±DÉq¢ê"c,ÉÅà8Ss1bÌÁÅ䏸âbÆX0^ẽW±n¯!Ž^+ä!RÊ.!4Ÿa„ìa#dB< !1y— ÃH=xØ1`±„-„æ]Æ"6Dˆ6x‰aƒ— lð2Á/Ã9ƒWº?x=ÎÜ·Œ83xÏã‘Ð7„nF wÁV<‚Ù¥qôâ*)Až ¶È¸Y¢€ä÷8#òT #‚ªà%ö®ö”!¸ ^ö¸^Ææ+x…‹«à,^Á«¼ŠÍa˱!Ï ¼†ä5ðZƒZˆÓ"F<Ü"‚ø )%8Ã%1Än#‚j )Ç>û^RÀFÉC„ˆöêIˆ `&ßë лs¦vg`s¯-„’réÎîUrAš©«W‰zýᢻþÍ÷[ì3 ·Ög ‰ïňšÈ¾ö™Q¹ÏT\”>¹÷‰»ÎPœ°^Fùà‚q‘ú ¤„p¸ˆÐ>ö‘:×Y9õsôæÔcfAt/^ 7_Wã0Ù¬·77ëq¼^.7Û~˜¼»Îïçoç«~ªº}5_‹_?_‡3ïÆ/›7ãW—†ëåý¸{(t—³3¬òfJ8h°á ÉØdŒ^GÒ‘u :F“ŽYGåEåEå%å%å%å%å%å%å%å%å%á51›Z ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷ ÷¬W)”GÊ#å‘òHy¤®ïV g²ýõÀܬï~—[5_­—«óùêÛ {y^ÓÑ”bc{ÓÃÕÙ™¡ižÚ<ŸÚDŸÕLgÓÿ»é³»élz¼«žÐ\ÿ³{¤ÕΦÙrw6=ÞzŸ×OiÄÿB?î¿‹œο7¶o?»&ßg>¹úøÊÑé¾S4ÚÜ÷ûÎÞ3Ó‡‹DC@Éà› ¾¾Ç[äã“ÇïrªaÍf ڴÿà§iAÁ”E9J–à/Cyâ,E¶œ&‹3[œ-±E"¶œ.¶/.çjé:éÛ_Ê·ç×ø\¾BƒƒOŸÙß ‚¡Ù¥ýO÷O/ådhvÉÐìÒÍîTßjðmß]C;øõy´Ñþ˜ËèËN“› r‡§¾Û‚áÝ rƒÜûÁWŸ|³oq&‹3?ãÝ,ï¶`y·Ë»íÀ9[œ‹Å¹Zœ-ê²E]6,Æ íiåɉü?ØÈð"Ø÷ ßhðMßlð-ßjðm-LÂY”#‹tdÑŽ,â‘E=²ÈGýÈ" Yd‹‚l:{Ù¢ [d‹‚lQ- ²EA¶(, †üS€,ëú@ endstream endobj 23 0 obj<>stream xÚÜ”MKÃ@†ÿÊ€wwg¿¤ "(ÖlÁƒxˆ6–BM%DÐï;-ÔêEÆ›ÂdvŸw>v²aWÉ{K!Â0Uñq=q?sq½æ²ø‰<‹ŸÉ'ñ ›`+¢@­X¦¬£d+xJ¡ÂJz¤Ì`8$*Vl¦DW¨¬÷+Uh9ZªºÆ"9K[3^UÂqÄÃû:‡‚8‚s^ÌÒ‰„)R²Ä‘ö¥ø(”IZKàrõtp`ÎÉ;sl.Wýs³4ׄ3²tc&Û•9±¬ŒFØkà £N8kࢫ¶?±)íЬ¢Šöú.P¼‡à´mf‹n¾ç,諭,O”Tee]TMà.ØßvÁºY°nìuxÐáQuLŒßEþõ9]iU÷‰ëîS^ ÝМnhN3´+3nÞW¯ƒ¹íûb5k͸Ÿ>˜ÉÐôÃY7k»ì¾5'ÝlÇ›¶oÃár1ï6œ™¼4íQû´êÛõþÚ?|Ú~‹ªÿDfŸþOÓ aHJ endstream endobj 24 0 obj<> endobj 25 0 obj<> endobj 26 0 obj<> endobj 27 0 obj<>stream 5 Massbus Follies, or,Bob Supnik endstream endobj 28 0 obj<> endobj xref 0 163 0000000000 65535 f 0000008024 00000 n 0000008257 00000 n 0000009829 00000 n 0000010062 00000 n 0000012499 00000 n 0000012743 00000 n 0000014866 00000 n 0000015097 00000 n 0000015330 00000 n 0000017460 00000 n 0000017927 00000 n 0000017981 00000 n 0000018080 00000 n 0000018183 00000 n 0000018310 00000 n 0000018427 00000 n 0000018592 00000 n 0000018739 00000 n 0000018898 00000 n 0000019073 00000 n 0000019216 00000 n 0000019359 00000 n 0000020874 00000 n 0000021385 00000 n 0000021420 00000 n 0000021444 00000 n 0000021521 00000 n 0000025530 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/i1620_doc.pdf0000644000175000017500000015771311143604374013473 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’†xí›/ªã\lW’Ûfœ>Ø’-'µ%Ç–’¦¿¾H`?)g:M2 ì·7`ÏÏ»²PzWºCãüýÑ—'íîòÓ‘ïÞ</~>ê ãþñØ>¿»f'ö;]eµ;{sT}ß©²óßÕNu¶£Ùµ¥*ìç÷G/²Ç¹.JÝ5Ùý|oŠ^WM•}—ïí0e´©³]ø®ò½*úJ«.k¤©¥YJ3N:ÍUQ6½ÖÙyg»Ú¾ÍÞ»¾^7Mvë×1uoºì]˜òRÈÜä{mÙ­ê*»†¡}Û6[#+=Ø7½Ê>åUQÖ­¶¤ÆÆ%Ì~=Ò·SÿyöõQÓ]ÕÔ»½6þÈ.ì™ÀN®³}n¿›ª«²‡²Òkx>¬eØ ×É錼ñ.Îxq’³b+ûQjMmÿìë]Ó'4;úì§#eŠnwö­ûë~fï$ç¶z:Ù´…уÐ8u×¶*{šï›Â2[ÖÙ3·Å¦¯ªìû|ße©KÈÐÇyY˜ºiJ“=’Y_É€3»µ¦hUã7Ž}2 èª:.Öö8è>±Ç#7*ŽRqJY2q÷ö¯Z»¿Îþô ¿µ„[¥«Ö *´€±7v¤²ÝUo᾿³2i•é+h]û>Ù¯y_´ik‹ç½=Ϻ­uv'_ú–Ñe²Î¹it&û ÍßrÕÊÂËbÚ¶¬Ô:K9¬ äÞJóF؆E®dÀ5 {†^S^_JïlFŽåT€ ÐúähÁi^OW4MÝ ‹°É„»8àÔa¢-û‹ßÙfÙ´mg¡bÁ¨*Çh5¢FëÊšŠÊj‚×lǶ鑛[Ùž•Cm(ÝÏNé Yö÷J ‚s8¸‚p  ãQîV}ªq£0 {!dƒ *ùsÙé­ô¾·úemN[+ÜÙ Wƒ^Ùä5ˆCÿÊ­¦ßþ-iýRáwÚi Í…7&ƒ §3(^çkG½;§>635F³wD‹øëÕ!«9¼i1;‰ÞæL‹6>}Lèó{˜Ã;YŠinR£úzýrb)ê’+)/ž…)ŒŒKÎB^uµ€r_ôNT’‡ œñõœ7M¤wÔoêVÀ$o-[¯!\!À°"s_ ³L*BM@EJ Zá8F4 Z…¿äûÊ65:Ë[k1›¶Ëþn;ë¢kÊ*»—«Úezñí ëòÙJpBòöpB«&þ&…©®l[W‘2_uììûä9@®´î¹ë ‘HÂ$HlsÒ4±*ªÞj“=ˆº-z£:;Si{<"Gîn”`'Ï£ÿNªFÂ|KÔøŒ7®®ðaš÷âžH'Ðü>Î+ú¶¨ÛxaÈÅ '–ÐÅH4Žü†U'À¤‡ÂÏ·GÎЬˆ<ÿ!¢OPÖd䩌–Ÿ¾OB78ŽÉ-@bG‡>ÅýPå0m‰áÂ1(5(©X«ðùój*p?”’%ÝÀ{¶cf ²¾’Þ{q(T›ÏòýþÔ‚¤%`}ÛµMPør ϰ?.ó'=žÚ¢¦úx¨Î•v ±SÀ–SY.ðƒõwnh œŽ÷œgÕÏ\ó÷,²{ñ昱~Ï#ÛfùE+Ï;Üš ¢Âp„ÔZìò˜[× g²6¬Î±ˆ4Årìæ-(¬xèð„Zmn¹v91Ïcaj gϵ}v'ç†B™€ PM^Ø ÞŠ3°XùA¬û3æ¶á ¿áåê“å~ƒx ÏÚ’Ñ'Úç÷ƒÜqøÅH{щµ¬¡‹sˆ«Oj̤dþÌm håwJÑÞR” a«ÊšNÕš$0áÑHyB½!qÃÃØcfßVÝ´·d"æÄÄ“â¶\¢¿Ä@ì|-å2P2ú.»àñÎÌ`ê^ª1`0úŠkNŸ/»îä†À¼þœÆD]ÀÏ:”JƒS'ñ™›šÕùi^[úµO 㮧⃗°cšަ&´xAy’­ñ„Ç,tI-tPK¯á­u틯ŽI^·bÄ—ð‚ãïšèø7êD_Úðc¼p xM&ÇÝ4J;¶ƒ:2€…߇2äaÒáïË—ÓÈjvia¬=[Kh$¶ä¡çú” ã4úýùRº«¶;ƒ¥e9‡8ÔÍ:ÐZÇ€Æ3‹êTdá…Ýðmõ{ö°¸úïIKO!KEfŒþÚ‹ÃÑðú»¼â'åùð5¶øu/=†®?’z XŠË1yQY–‹+GZª6l²÷°…w’ÃöÜŒ‹Øú…ê&ÎÖ\9HñZúhÀ‹Vø)rHï#XQÜ´âfCIæä ÐH`“W5H•ÿ©ËgV=æÏ<*—VÑ”zøAœ©c~°A>¬èp`Ì?és É/«"7lÙ—t_wx7¼S¥ù¨V«ŽVy™ìxøÿ³2Yö¼Mka—T”ÙП'ÝP¼'Eqü9¶À¼ø¬°uz®`‡þcS0Û—Öm¬ïuu:«?a¹žÕÕ×j‹+\©,¸ãïÓ­5…Žu8pt‰Cœ@}ô‡e?ÆsIEJÝò£Ì×£ŸýÕþû_9~"Cendstream endobj 6 0 obj 3132 endobj 15 0 obj <> stream xœí›KsÛ6Çïü<‚Áâk’¶“N:“:Ì!“äÈJÒ‡•W=~û.I‘XYi'²H1².k p@àÇ?À§\p¹¨±¸Èî¹üÝ—¬IÎÏ~YŸßeŸ2ÏUý×$P{q‘߯Ð1äXMõ6</B“¹5øo0¹ž›WË‹êO,°vÅ}^=Ϊ^0(JÃ…ƒ ±X `5w–=-JÍ¥—Ú±? àÆyÌ¿(JÏ­pΰËèõwŸÿºÐ \ZÅx¬àû2­ñwmo6S_U¿fR .êA[g/ø0s6·:zvf=rÀ·Þ:t4詘’;m˜j¹¦À! ë~*UŽÐÎËÏugÉ$œ­‡Opì~äôoˆL  BOWÌ't·=I­b£“—½ÛëX–ðý2Å÷MhK \¹8ì'€Ñ)pvŸ“L*˽9ñùDíqù<6]oZË ¸ í^sCφ}º¬ ñìA!¸•À°'-}Á[ö èÀ­íÇ(6?sÜÉ× ðkfõÜQ;'ÌMÁÜÚôôØ,N`YocÙ+.PÃ\eì,ÙKÒ4–IÈaµ!DÆjlñC*|±¤ZØqíkÍZa ¨…½uì¿"pa¥“ìc,Iœþ-°dS}Â1¢A$ór $ò’õ©¤ùÎ4†=/ wÊ Ã^ø rÜÕO5q> ®›,È'.gÿn‘h7C»C-×: kT•Ò¨Ëþތ܄䲟ÈÙÊM³Žr+Ê!wÒèYOM’ýÏÛD|ÛjÛÌã{^SŸôx$Ìêeè*.qI«—T±–Ýœ¹ù©ôéŒÓ}AÒâ›9ÔééÏýc7Ö¿·=}86: Á‰Ô¢: Qr^…ll…ö¼–»Äà-N”<íWÅËMp­óë›5v,¿sä™Õ^²Q\y7Ÿðàü6sÔ[6£â"~ÅU7÷Cs²»"éä•;’Ww@îHþTe¿ãï? endstream endobj 16 0 obj 1222 endobj 20 0 obj <> stream xœåZmoÛ6þî_¡o“†ZßÉ}Ì^Š®-Öeî‡a†Ôvš¬qâ&q_þýŽ”Dž¤säÔÎ2`5P\$òîøÜÃ#y⇬*Ï*ÿk…ùjòôØdïn&áqvü¬®ßM>Ll)ü¿ðËóUv4ƒŽŒe®t:›NªÒ9[™Z+Ë´‚¿Ê «J V“?òY1eº4Lóü¬€ÇŒK“Ÿ$ád~SLMYULë<+/%·,_S.ËÊ“/ýCc…ëbª|wîò“ðPðÊä—I\$q“D¤+ƒ§Bi] Üö*‰sðú7¤!|“­A©m~›Ôù¡aõèÕYR¿L"jûPc^¥ËŠ©ƒ!òŠç¯ A¥W¦Ó˜×¹Ý„²"çIoEšhçmŽF§†ë"¾G ß&Z( e²•añÏÙÏ.])4ÖÌI‚®äòõ #ˆªè [c¾¼M Ôv™B³ ã\¸LmQƒgÅT—Z*ã0iP·Ó$" / ÈT®Ͳ æ r¡º’¿—Äoñ’Iˆ°ZiàgÔ¥S·AÑx¹1EŒgÉ´¥‰­xÍ”1RA³r“ÃE|}Ÿ bÞË"O0S#†Ÿ ç“QCzzõ!Eœ‘Aëf:üfÓ¥É8AÞÂ$Hgò/39Ç>®cZ’dFõœ ÿ¥gW$!êÙ,-7Í~zì2¦: ë­ÚT%¯—Ï8¢=3¦ŠW¥b6ÿ­˜Âˆ”ìOÁ‡tªÁUÈsV2ø˜,cÎZˆgÝDX ÚZŸr²âujÚ^{R®tˆcìö“ŸC†3íù§Xñ29zÓÚ`qÈãkŸV(ë fO澜̾çîÓz 46V: (yQ 85á l[÷#ƒÖ®«Öcoõï«"ø«·öÑ|¸é½J“m¹›kŒlqÙÃE4WáÓÞ2<âHŠÐý=i÷(å’séÄf9Âàc¡|¶Wê!M÷i0Fßá¾§e”ÉÏ ¿{°Jvwg#›’Ã;„ö¯ Â:ÈóϾçŽöwœwÌÿy“_”< ·" |üצÝ,÷myoîÞ¡ûqŸ"jà>üùÍ}×Lb³ÅâÞNG‰G©ŠÒÓ†=*l¥¸€½§á=aˆ£g«¿È \gQŠT]”¶ÇãkëÎåuê°Ùw²ìíÑiû˜Î s-ãŽ{Þ»Ø`]PI`Ï%à0”Z!àìÞœø8yâ{ŽŽyG¾³Êæ¯ü9YL<­ùèШ“ˆW¶„ª òâ1æN’xë×$ç¸uøx +ƒóªJݤ ÐyQÇ0írœ…í!Ї–,„.£Ô)þEñÈWl´l­ýÕ,pƒ…nXYé‰î(žôöq{•ÌPé#&Ó;v“äv›6¤U´Èvj'QW¯&‡«SÈâ`›ªH^ºJ`Ö!Ù¬äMÉë»zìb°¡o¦‘7Bû`ªÌ3ÎÖu­…ŸÊ¿on¥Q:Òœh—Eé2J'QZ=n‰ëйÂÊôÛK½7Qºè[aÎ×·£¸LâM+rœKnÛEéû¨íu”ÞtücÜQàØU$>½Ž¢ôŠÀ†EI'ÿx«$fI|•Ä«$.ÈÁ^ù)»~ Ü|ú}_'ñ ÙöSÏÉHŸF ¾è·ÕÖY¬ìŠT¦m„±¥íƒ¡Vc9î´"aZ‘¶ü_úl6v§ c½qš…HL今5½Ó4º[Öd Îɶ—ÄD¨(çd·Ùàš„wNz†LœzczoH‘ö÷]ŸŒù°éû‰à[‘ÝhÔ瘇±–{­ZÊÒ@~$ŸÒ“<Ž'’“[[ÂŽ&°S&röy–¸—Òú-Å=Òrè×$Ž4îCN:j§d®CÊFãCs™X“zi ÑD «`û â/QÐÙý¿(g?ï=_ߎi@‘§2yGÙœì¶&ŸÒ†/R¢ WÄ/c:M‰â‚Jû’z·ì°&¨—²CÚFîÆôYlŸ¤ßé[¸yßëíÜE PÆX’!¡·®ˆM«»Ñç8R}Äü÷5Ðñ4¶Ç¥wôX¡B#8ª!#•-I tV¹îCŒ+oÔ¯jùß‚5@ë5š;(7žøRçµ\–mÒ‰j× —Ì”Ö<úôêHç„ó‚ì¶/M…ÿÐp¨@D¢¿ š(ÿ0DG”F¢ 1CI—^°Fiz=¦a$m“äÝFEf™,Cë0P!P)¢1ÓG´–½¿G*™uûÊ3=Ú£¥uCà¾mlt<Ñ è®²Q§ì:Mõ³p+,^µù—[‘t)Œü,O2¾$èß_C};WošñöKCç’Ú†´rîk¨Ô¸Ð¡¤¯È¢Â+ Ó£¾t ° Þî³ï¨B/ßnûP÷J½+kÛ.ÁFñ—t«ó²)]§R4ºåzEÚ½% å=ÿâï-h×…÷}û5_®íCT¹ú»?}•¾™€ÄQžïx:Ôö7„ý-Ö;·óÏ¿?©ÉÂy©µkÉr›”v=”þ PXðÅU©  Ã·]\%o¡®£„Z¾)„òyJIý£/œô+ÿ‚Ru'ÜÖ\±ýÏ}’Ŷ2hØÔ'íþ”Ü£èÝ Ç|œÆLß¾ IHj^ßÍ¥Æ@§4u‘šAž #™’Ö?7Sã/¿!ýÒÓ!@~Ð×Q>R×ççøÆzñg“_á÷ø@fendstream endobj 21 0 obj 2136 endobj 27 0 obj <> stream xœíZKsÛ6¾ëWðV²S1‚䡇æÑNÛ8M¥Ó™¤G’´¶åZv’ö×àX‚Eɶ$wâ$3@`±Øç‡þXÌEÀÌߦ1==:Ì‚“å¨ì¨—'£¿Gyœ˜?emOÏ‚Ç=‘ó ˆ LŽG,.ŠœeUp&‚Œ³8ÓÏFoÂq4NcƸ( s^È8á‹“T)–„纙ñD°,ü³ðÈý>Õ¿õ°,á‡HšÞB†3÷=pTf†¶þ‘'dè 꼪&%ŒëùL~é!©&ÏG“¯wÆî‘ãìÔ½pïÝ,²ÀY42fY’…s×{éXvËN odÁò7¦Jæ“Þr•#Ô½•·/8#˜ŒË"|%1S…”miÚ¡Äfp‹dKÃÆÒ™õÁ±uB{WY’ܱ!ýU¬dš=æ5£²¹™@ g!)Ýkͼ1Ó¾¡c9‚Ûbà™v(‘ŠÁ¹©cˆøcÇ**Vû|6”K®Içjlîs-”•íl1dNÊ|•é™O©Üý‰>FzÁŒ¥)í<‡³nëh[Þ­Jzv{ìvíÚ¾W/_øvj”–o÷½7@°šz‹m-ºš²YmÑ[ËT»Ûf â«¥³ ›Æ„‚J¯l¾ÿKw&VÓ|wŽL\C¿&ÄðDÍ¡áù¨ C-ˆÚŽ lç>íR®Ó*|ˆ¤èz¤oæd±5L³®-·º¢DDC¸A%Ü´P&ã9• ‡ƒ+nV‡’':íÆ"ÏeÚ#ÛMœ { †×„Ø{7m-K #¤¸ ª´úù'E,*«Åô‹ƒ†4£!^H:p8‡dýOQgyµ”ON ý¼§Z@øº&€O>­üÔwòYz™Œl G¥3åz‹ªÈëŒy^ Y=¦Õå;Ù,m>rÍ_ êǺžé­Ö¨´Ñ3UˆýŒ÷¸ ‹7QFõ1sTÍÈíëb‹E‹RD$Ùá³)ôßÍ 0Û˜ÙtwKïHäCÐ>ëÅe€Û‚ñ}T…v.±r…Ïÿ'y7sìT7˜—wt–}ŠéŠ=Þ–-`\èÐ*ûàÎÙÞ$ ­QHÜTF$Ôˆ)I±ÝáÓã mÌu—èê¦r¬ÊXéËÑñ6„ÖúÏ,ûÑ ¯z+#NußGc©±‰T9Ù Y“ØI_±ÈönPÍó}¯jâðíÖHT‡Né¾üñ÷×k)—z fÚÂx@Îàd$0.Pv:‡ ` fðÉÝsÒ¥—I7ÒJSNûìÆ~ˆ¸2q¡ C¯;êLy·„nŽPs¸í(÷>”}P=ô«sÈ)ÄUc•ªžCvëЀŽMà*Rkþe[ƾeû‰ žû èš">Xk5éµíÞUR—V^UcóÂM?l'+7¦0óklŒqˆD“.xÀ‚Élôf ‚à—sÊ_øÕœÔý¯¶ÖqÃѸ!³Þ_¸õ›ӥÅÑKšiûÀ™×dÝ,"¬Ú:7³ŽüµHH'Ê\I¤¥’ ôšô—ÉŸ‰åyƒÄ&š¬N‘\‰&¨ÙPXÕ¸E–jEbßÒ¦gA?ÆIœf‰4Õ´ºõm=c òLjU*Ùè–è¹RíŸnjQ‹áÜ„Q¡—¦Z·,íd…ñá÷6ðøD\e€ìQ—–½Ð·p¿cÎæÞŠ0@œAløƒ‡É)̱}·EuäêØ¨ŸÄIäi]•¡S«ºH­™¬qm/jˆêP¢¸ðù*›1 ¤ÙñŸöžŒé‚ÛÓÓÊSx‘„¯]ó@ÓNuäÕðÒ8Ùr"í&ªß‰¼[·Æ‹’Vss/2Bň¼…î:6–å¥H²¸(²:D%±T&‰j ðÑÊ… G§Þ1XHpçâ ÜË÷îóŒjÙ«>:LXÀMxrÏ Y?T©þ­cj"õ1¯z9(¢1‹W™”ÆddœŒÉGz›&oj·Lr­1>‰D¬rÃ^š9ERd©ÖyÙÉ3»|ÿ£E»z’Ĺ4ËÛˆîÎÖƒÝÊ£¾ Ì2NÞ«½v ŒTe¬ùìžp,+‘Óñ5Œ˜]ot׺þ¸&³­}k¥ëö€ôV9œ·¡ Nº›©L'4ÞóV©[ÜiqÃa¶,ŸT¡Tª˜svú¶³:~ 7ômäû†J\ ‡+’y åÑWÞ´Ž!$Lá(?5hN¬Ax©à E׿ñËüÆ+w× ~³ ~œPêLÛ%oª±õÙÚ¾ŒdǶÀk¹IÅÒð_+Žy€EÌ5\l¥˜r>}'¾ëø',!ôM¿óev:CI‘sP‹#ëšfy)éêé¯ÿûª“9‹-$6› }0’á+‹bŸÙÖĶÛzb[/më5÷cW¸M±,æÕ—fiÜœ;„pîšG®ùÎ5O]“L \ó$6ƒ.!±©k^Á%Ž ÝÙÝ%lb~Oš¦p•f± e¾Çž>¯Ö*ùŠñAÁÛÖ«£ò´C¯Ö¦›²;o%½Ÿ¡V.ᘶ+Ø‹T±†™-‡˜¼23Bw±z âúrOž?l5BêÔ”÷ù;ŒâÁlÌÆØŠ:çóº*[£ÆM߸Î!äÙš9Kqyã·ýG¬å°w6Dìþ‡=…aï,(| ÞòsÊt þ}ÿ~;÷p.¸¨k5šî“ó~Üg»‚Þˆy Äü¢%ܽñ»!yº$šüóšzÝhXÓûq£ÛªZÜ[Uï¿ïŒÛg-éŽ]@º7hB |OÑyµ¼Ä½•—ØW¤¶Ålë綘ܔ;Ø{Å8ˆ6q%ûß¡øíjE$@Lóç=™³|ÐN«W®­­9OKìêA?­^µž~Zmxù„&4wÃÏ&£_õßÿ°U¦èendstream endobj 28 0 obj 2306 endobj 34 0 obj <> stream xœå\Ywܶ~ׯ˜·rz24 îYzN쨭›:–q’ÖÉÃX3«-ÖfË¿¾Ü â#AŽdYvâ<\a@,ß]pïÅòvø¡˜ê_Cìí«´/?-|9·˜ ì’~_RÓ+*e­>¦}?qì}E…¬ê®ú= ŠÐûa:¾Èó8ñæ²j*«¦B~¥«²¯XÝÇTú ë‹}¥»]Ð`«Ù"å“aü}:‹ý4ŽÓÜÛÓ#ü¶zA¥¯© ÌÁôù¿v„²¥g¾”RR±@ññl:Kü EQòI’"*8wYKU¯E’ðNY«Ž¡ 0ðg†Ð!Yb^I~D~,ŠÌ»ž†™ÆAÂÇsGÃm£¡a û ¬³'²TÈùDÖÕBËÑ£¡XÆÚ:%ÒÒ=ÕÖI[дîÕ°€Y.¬Ú”ÚxT/…áêY3lɺ*L¥u‡ëÜiw8ñ– ÙÊéÅòDg Z]ÞM å&ÄF^ñëºÉDÔU_µ¥îÁsé¼wøiÞˆ äBÇ^,I?ÉJÿYò]Š[žy*¨0D…AUb4Á¾üó/êFr ›Úhj¡©uM•¡è, ”k‘y9êN©oôö@[Ccä·ºÙHªy]¸ÔÔ¡5E^vÁ‡Ý43²ç°ÞßÚ356EBÁ¹C¦Êð Aò( hÐÊ-ö9~…á‘8òÏåbMÛ™6ñZV€¼]UزÜnUP‚7Kk1`iêÏÖÿîQ ì¿W#ù} #PZ8hc+ÿªÃhµ&¨{¬+È+Á h¬Ë+¶?CýŠí>‹h c>‹;zs€“l7È”ÅHø³lª`czËù ‡÷VtÌMdy‡ÓóÒöVî…6Aég–C©+ú>Ú”{ž±‘ïufCZód»ƒ¢¡žgp:Ÿe 3Ñd…T~j›ð¸åóÄ&k&›2Ÿ°;#SËËô°ðC—­ëì­†TÛ sϑ̓G`-½©÷³‡¸_V°QÎÐ)Ûl¿˜Õ]C.²´³¦§œ·(EÞÅÛþ©u¥õÈY¿+È&”¯¹byb› ”°©5(*!¬³ª?û¶qÑÂO[/¨°×£¨aWZmÇ5¦Q ýoí½ƒØ¸µ Sª)?ô® ¦3Û©+\‘¢òØç¦Ëb™­l؆"Þ¯˜¨,SR]g P†ætŠ•‚!Û$Èr¡OI`áe$¶˜– W'&Ø`ØV°3ñÌüŽ”HœÒš@@ºÂ< ©eiÛâ÷}0ÿ 8sÃÜ8,>'ã¢“Êø` ˆú¥Åyš©®…uÇ ˜;ÒÔ †u8ô:ñчá-íStBä$WDyAä9…V:r¬¢Ú&ŠBqäc=Úçš q‡BDP´š_å2ø*ã]õÎàNæŒÈK"÷aÝCˆ"ëbâêív¼ r ù0u—<ƒ-œCrâúl 'ç¶‚±.T×Ð{D>*e(NR?ÏuúGEz¦Íš³ZÜ*ߪW¾šÄÇ-ËÆŠAq ¡À‚òq€oPk)áÞÝ)á)œ"S€}8,¨XonŠ¢%¾aÛ®dü½÷-ƒ¼Gú…؈§D>'R°Ê–‘hqâÙÝqâÙŸ,ƒls¢Ã øY#JÔvˆ¥²m?©ÛÁðÂýâÉ@d&ìõÍlÂe»Õë³ÀE\†ŠKôåÂÝD\â/—Øaçÿ©§³g‚`Ûé ¼†ã^ÃÁ> ’9>' +½r‹œ%¬ÀÀ]ÀŽ‘oåPË]Üoo6×·D^Âño ô pØýüDpBu¶½“rW±¦Hº4Ð&0·{ñ9¤³)19Çy,Æûl¬ì+Xá l{& m¬5lrP|X ÌP‘CsOï7Ëä ™iå{—‘c˜8³H·*Ÿ\ƒIo¸+ Æ1Ž8>O°[ÿz÷Vò]׎—êÏ'ò:çšzjY¤­mŽcqŠîñâäÌûžº>ÛÊ]vÙ'ÊŽ›>2Ó0™‹öô‹Å½Óã'MÑñRS¦¾ÖT¨? ¦€ÏÅ”nLºÒ_gd®Ç‹2Y¦]. "¼ãÝ£;H b§‹õöŠÈ ü¬ÃÍ~™¾„Ml`k–bß‘µpÍÆãÊö’`Š‘›lŠHÿnëE;Íø³5DæOÆJ§ýD,}øy±”qláê /Q˜5c,Ïg! ³oo]òžþE5Õó7U F#¶˜ÓƒwJNx‡2Àë+^‹'t&rØWÂ’ŠkÆWìº}3PÁ·$àÄór.×c²cXA€:€°<vE.õBÁõ SÚ{½*x…ýŽáĉ:|&ç¶0d,ËØföªè¹?’kl­czæ‰oû‚ï³£ìœxû•š®c¸æÕx«?{¯¥Nètì¢ãè`ûb£:„Ú>ÉÙõLFyWxÏqÚ7jÇ!ñCøÒø Cïõgλ‚õQý(Hä$ûψ³¨ïa¬ßKØY×ÅÄþ™…qhõ…>ë ë–‚㌫y¦Ø¾q¨€Þ‡'o1Ãð1_pyÑ 6;ïÌßÓo]±÷ºÎ!W6‡7UOk‰B–û™lÕ.‘sn$Ó PÓ©_ÊŠýá¬édv=­ãÌ7€E)‡ðž?š¥ÚÉãu‰Kô&—õ[ë*'~ì·cðãƒ}©µn6[¤¾ß~ÑÕ!HL ¸#ÏnZ&ôÊ{&!‚·2YÛÎ|ãD¢çuŽ!ðã\¿"j]lÞï<“ÿþ¿&%{endstream endobj 35 0 obj 3389 endobj 39 0 obj <> stream xœíÛvÛ¸ñ]_Á·R=€¸}èæ9Ýv·'®W=={²}P$ßZ[v,y7é×wÀ fHEÊMÓdíÍ>ŒA`0Ì 3£÷‰È¤JDø×ë›ÙËS—\ìfåprúǸ¿˜½ŸYþ+(¼¾I¾YÂÂ\$RgB'Ëó™È¼/„×å™X{“8áÊ 7³·©š/Df¥uZ§Ù|¡3ã…Ð0ì2¯m‘&óE^x€ÓWs•ÙB(•ÞÎóLˆÜ™t¡ÁSWyzÏÈ„‡—qîG2á.ŽÒe¿Ì2SV9›Þ ^æ€á æz—+ŸîhãsåšUÆzO§’}JÃï„°†QAJéåw-œ·éOó ³–€âË?›¥L€+¹ìT‡ËÖKàWà2,Zþs&efT²ü~¶üí[Øg!mæ¤Uéå\ t º€rc­ÈÓõ|·%¤µÀ׿û6B;îóõ\”{Í£ÜÏ*Ï\aӥˤVÀñ˜›¢dyþ2÷0/*pÑ„}€ÍWû¾œé¤öõF¹ŽN%›†7ÃËpVù"àsi-:ð› ˆ{àÆáÔù w'ó/qt…gBæœ×Sw Üpƒ«¼C°B€V~¨îAåž¹¼0z‹lqÙŽN’¥T‘B‚$-7 žt¯¸>Ã#’½Ø“®r¯3Ê$* ²Z„9( Zû¾4 ‘2Jæ^ ˆh ‹Fwر·PI‡²Bìû»jn.ý^4×°¨µvS+ñËS•GÅ—"+…vìFfj `f\®ƒÔÕÐM„ÎjHÙ°ÆŠÀqG—pW]EèßÓ1­« ÒçAúð†ÏÜ"¸Gp×€ªæpF•LJ¿ÓåÁ«Hå›}ߢW*`ŸS'wq * rY íÁ{=Z‚ ϲkv·/aŽfDcõ§ý%B¯ Wé6[ ðþ•Ùl¦Þ‰€W,¯.X^ñs÷,ãÉuüŒàŠ¡¡á`ËÅJ­åFÿ«‚'+‚±—*ø*O|Þ–÷~q½²6}>µñ³B«!X§’¯@1 mÒ“LXWð7ø®¦ Fð=õ?œ.9E×¢›$ŽænÎyµ=ëIÎq”x[1xpT2Îȃ|=àtþ;xÀPÞV<ØÇ¹ò™’*}‡c·Ô‡s&ƒs_TTCDãm=ñ‡?ÏMpqmw³Ø—‹IKŒf6,²¬r9ü3Éõ].ª”à«AEq|5¨¾„¤öW¿”ŠÝ*¬Äµ„^Z$½]sŸ+WT!&Íà[‘)ëEû†ÉZ#´œ[!%½HÌ­´Ö¶E¿DšTeá”ú6^a[V•F¸“«‘…Õ“…Lë\²)ÞvÂé #t‘ ¯Ã>NáSz{vÙ#uzhcΘlX¥o%¹-Z‘“æ ÙB޲!ÅŒ± ±à·,¸§q ETá`8X%Õò1¡ŠFä}„ƒ‰§5]ÌäµØÐ†4˜`"´—~|Õ?K+⫪dk’¤ïåb)!V°žÐ;yHâÅ^•{‚%3‡Òp2ÐñU‹²–õ‹Å½p)ÏÅÞiÅÞÆYþpD±7«T{ÕÔZæÁ+ö> stream xœí[]wÛ6}ׯàÛJ{b?@ô­IºÛnÓ“®£sö¡íƒ#9¶»¶ìØrÚî¯ßI`†Ô¥)»Çnl+yƒÀ\Ü™ O‰JµI”ÿ„ÅÙäå¾MŽ®&uq²ÿÏV¸<š|šTiæÿÕR^œ%¯æÔÐd‰K]™Ì?NTê\¥l£U'eA»"±Z§y•ÌÏ&?M“Ù^¦ ýOW$¦…Íòéy”ÖQJ¢t¾®Á×E”Ž£t¥e+™RŽáwٟɵM++¿_5ߵ˂*/®YLXü‹û,¾fñ[¿‡NX\±¸„,À‘³xÉâ Øñ%œæÅ˜2l‡CX÷r|d¿Ìÿ5ÉJ+­ ?óe —öûÕØ,ãp 5óºö‚²= ©¼Fú2ÿuâñ˜ÌßNæïò]Ã{Ãï¢ô2Jï@=†Ü%Î7Êv€Ü²Ä“æÈÏ€ñN£Ä-–QbH"fd-ˆzÄgÄGEõ¼Ðìör?£À&OUÎáŽË{áNi¬¬êpÇÌöTZêÒ’MÒÙ^žN©|ZÌlêò²ª'QQøTï·X³²ÖözfÒ²RÆøÅ —¾‚Ó™òœ@Ë\”e)[íÇV‡¢•Ô°œe©R™É:5.©´,”qä³Ôo¸éþ»Ë*«ŠéulµŠÒB´?®KscÛÁT•sÓŸ§µ«TYˆù¼‰ö· P›fT¹²®œþ<#“.Ëu\J‚ÿHàZ’ÙÊܯ$Óô|¶§ËÔêÒÐL &·dŸ %$edj•QÀÅW~Î¥£Ùÿ@…$™ÂvêÒêS_Yáhíƒ*SK™Q^l>W™lDVµ´`±­xA¦Õ´ K.}É D…C®pÀÊE³C¨7©ëZ;"_Áƒøš«®X¬GwÌ‹¦'³Ü—ºš':v¨ÿæÏgž\Re3Ûv¢ÈIÖŸ½…ÄÜ׬CŒ| »Í|45“99Š5×ýmæR[å$K]A&³©Ñ6ò‰WåWpÕï¿ÔE.GuÁ?„¦[ŽU³óê¬^ÔðyVxÄ©`Wo¸Oûã+Ù²Ë1DJœ~Ÿ×öŽƒÁ͆ Š–t¢Q™g•PLþÄ«+]E ±GNãñ$K‰!+•Úðiž8ó2çx±ë7ÄDˆeµñßDÂ-tøhû¥#ð|Š"ÕUÙŸ±ÞAqã<¾‘A#Mß/Ć ò*UÎX`Aubí°/]Ãf%Žâ(-u±†m=äå‡Â‚¶‹Ñà@4û Aõ ˜¬áz_AWò‰5\C“ô…¨¡7¡> †õ@£(ÚÙ†R —CŒÚÒéiŒ!Î2E¬QXÂm˜M…£U±p„ vTÅ…À¾·Os¬Æ¶âM3Š·íô#Uô~>#>¢¨ËÊ0™¡}Š‡Ð 0ÿK½8Òb¼g'Õ¹©´ìš#Ž-…µ^Àh‹#Cá륦6Üpÿ†ÆâTäÂ{à AøûÞ¶ªCK¼—pèÍ£öæQ‹Ý€ÃÛ-9šƒèâV8€ßX]?\1°°ÆÝ1vÙoʾÃEòf±½ Gi 7ôa:'-j²Ô¸xü6ùì6ô+‰ƒš™¹àÌ¡ÔH2iže©¦äW§Ì'WÌQb´_Ç8ñýlÏù™+ f­–©ðwÐθó}ýÎ}aßÓî1[±ãÅnèA¸»bËM±#­p–I‹©­_‹J. ÎXaÕ#8‚Îì⦳aÇÇÛdyE€tüQü‡ÏÊÜÇ®ðY"m„¼ÈGùf®(îšÆÆš/¶=ÍÖiUNûTWTÃf„ìP8š®!‡€#¿ÑcÑì"[;ƒÎm”Ñ·dk"ªÒFâÿÔÐB†Ò£Ê.dj†è s°Õ`Ž€Òc³B§í9wÞtÆÝ-˜†C¯àê@âЋ!¯pŸ8´U±wÄf½c—ÀÞŸ‰P­‰2"vñα3]”ØŒÒÍFÌÌ8«|ñnÿ@ÆÍ(ÎsvúÇŒ«sU äH¢ƒƒœF¥…*81%£Ó¾# ÂÀf.)‰C ë—àØò6QäW8ÃÁ>ÎW°‹ÒDˆxDðÕá«ÆãV¿ø„‡QKäp2}S¿-±DºÁ6ÇÖ Gz-hdÔĤÁ»þ33 ï‘øqMimd™O&¹ìì¨NÎ6ê#û(8eî¶ý Z±{ì¦Ò²/Cøž¯ÍÏ¢^|4è¥òtÓ¹ ݼí\Ùÿo{M xOyEqñº‚7WýëÍð.&ãd‹·q´_Gé½8‹ÜÞcO NáU-¾í§„ø†Wܧ/a³»Dx}ô#8Ù}ß±¦6„Ok¬™ÅV¦ÛªÒyÚÄ”¡*~¾p§º†¥çÐX£ÖL ÞcˆÙÑGÂòKXáò#&¹ØG¨ìŒß ˆSiݹ*iN €Ë‡ïŠ¢ÜÌÇŠºQìÙ´õ)gÃÜye‚»…Ìý¼‰»áêìˆebÁ»^è]@e /æ%–tˆ[ÄYÜŸŽþBþ‹Â[ûaèøDÇ0“ÅO&ä X<=X~“Ò{.2p¼,¦À·Pâ°.$Òµº rëDaÛÜ_ÇÐëž$~0ͼó:JßFéû[° ~+¹;¤C;ø9"ŽQ¨y«™—ƒ½z•Ë ó|å2ö7üÞ·ëoDã{xF.eŠ[÷/ê}æ³½;½éw Oí‡6;@>@>ñØì€øX€8䫟Ú/mvˆ¼GDš?‹Èü¯ý¡Í#Ç­Ìî”ÔÊc°Énÿdúþ2Yô´|ì}ËP&[ýÕ©ì—ŸÂÞ-Im /n+ûžÝýä¤âìà7ƒ×Ï=HäõÔr"¿dDæ»ßÚß/͇[ãp÷Sû– ˜í›ùäßôÿÿ±чendstream endobj 45 0 obj 2336 endobj 49 0 obj <> stream xœí\[wܶ~ׯà[w{¼ ‚ ™öô_”æâ¦®½9}pò°ÒêVK+E»rêüú ‰ÉÝ•”¸µí—sÃÌ`€Ÿ£$2Jìÿ8¾:øâu­Üçèõßkàöìàçƒ"Ní?÷ÃÇWѳ¹é˜&‘Pq¢¢ùéA—e‘”Ê5‘ÎÌßeåIî\¼Èé,‰µÐ¹R“x:SqV&‰šèi—J“h:K‹ÒÀÁZª8epDØ^NÓ8IR™N.èãÊ<©ËJÞç•ù¨eš“[û±©ù»oì ež$:c¨X/†õlji•©ÌëI)½rÒ2Ï&sõÇ©k)ÓÜôÿiþ­!­‘¡„&Êæ²EY]ŠX–²¦Óü?BÄ™Œæ/æ~kpÏ„Žs¡åä|š@ªÜL¸"¥™ÖI:ù†ÀgvBº4ôü‡ùh ™åA[Ã3JjV¬<ª Je’[ޏŸ‹”wºœÎrç™4$U¶mi)ê{À^7ÔÀP73„² ø^êu{1´†'3a$2—…aŠmlï•_8£›a ÂðË´Œó"M$ž×ÆÍË -ùdÖ† oï?ÚFŸLƒñ–´Ð!dÝØ×'48ÃpLx¯tI™ÄY’iš/Z2R¾Ÿf–IÞ¢dC²©µÈ”™¢oˆe”¸† žNgåä%}ž”bò|:“±,tYp™d ^›R0Œo»€ƒ3²V̰²Ì8ðÎü^š™9f+YˆÉ)a¥N—b4‹i¤ˆƒ¾GAk]@s¥hÔ½È)cÉû÷éc¾EV6¢RáOu¦¹T1 ¯ÚsqmzV(scAËÜ43Æ1ÖZE¶QTÃÝx+Öpí?ž¡Ÿþã•™‹´S””Êr“Á%2‡¤ÞP§Ï!ƒ°ˆY ñŸØ$™JÑ0¬é‘ñÄ[©6êkNvÄÍ€há¥kÑ‚šÞ@¾ÅÄØ@¶=¸…l˼¨ö9™ú}N&…qKD#k/7g¹(ÓÉ? |Jà‹©N»¾ÎŽiy­´ ¥¶!\£ÖŒ|¤¤¹—”L„bíAlìHEÚì…Í| B^Ü–—µŒª^+ß+s+ˆÜˆ ¶/"[Hžo@DbKÄ;ÙM[Ü×/kÉSºö“f2•±0^áŸåuå R%’¸h¼"ë`&RÄJ5Ò•ªF¸ ôÔC/<yè¯:õÐ…‡.=tâ¡¿ÕÔ ›/I¯ \¸$0"ð¶½…àl{FàÉØlf+7p4ÖöâÝÀéÜŒMç.èt“È­ÎY6¿üi_×ù´íáT[‘5ì‰(ø¦C›w†‰ºŸB'gŸ ¦ë›îªí]=·ÛЖjÞÙ`Cl0È‹Á]b4Š™QèÀ:Ö +&úUí,~WºCæŒEs¹io¹$GÎkf6;xȲ¥ô¹˜<´þ5#63à³Ç4ú¶ì Î#Gttg\Á\C‡%àI=žà1ŠágR~ÒÊ(·æ«‘Æ:“ ,ºÈ®í@³yÝ ÒÐ`x½¯ÀN«®Sç@g²š™­•‡êq…ÖLSÉ­éçfµ%v•/(°_¡ßo8‰Q4yÒ³Vá¼W:?鱫-Ñh° FäÒtDÃ6½t>¤2âðaj–"”ÜZ6‘¥Cؘ•KgÍ1ìj™m%µÃçÍøIê 2¤ëÚ˜eq–ùȧK.˜µò¨ÚŒQë‰Å›Œ :󹱆³¯w67”¼Ž/Ù’°ea_Y·^± #ÌÐ9à›§Íòm·ÐÄQA ÅemeX:,ÉŒ,q⢊8-KJõÅîy—™ L™„;1è¸V]†Y+DéÖp:ÁÄ^U_Úa¤Û°(pMÑ rZxSÓ·¨¸ ¯’Úþ.rÀ5¬í‹6[Ôèê6ªxÅp"• ³KùÐÛŽ_{ÒÀÙÄ)=Ì"fo±âb3d‘«±‹YÄÛC44ú>êàí’CÚ–Â,f°+yƒÑñµ1gí¡œ™ ­¦²§.,ñf|.ncÔÆ*’IjÂüZj¸¡-•,H“7ãi@Ž0'ˆZ&K솽`…æk»ðé :gæp>ÎEžL'Im騻Þqå µqCW©UÈæˆÊŒ¡|½MÆ…ò'G k²ðÐÊCï@†z\€½¹—ÁŒË æ*"˜Š¸ƒ‰–9o'BµÈaO¢ê¹f²gNlÚBZær| Ìà XV䮀¥XÖÃ/Ã,<èö8´Åóe—H…§ó@{ú–ƒoEÀ©ŒWäãÜÀ)³K¸æ+¸|Ì Ak~01À ò±Å#±kIS¯ØÕ¯ݤ«"ÊúF{äW»ag•|\js¹aÈäïÅÉS‡ô®aèþ†îAI$›DÖ}GClà*n`[Öà68…mo)#¾?3„ü§;àžá ®Ïaöž‹ a…E;s¡ ‹†¾ͨ“áj»^ë:f—ª‹Àº]Cå<æ6i+v:¿»P®Á§ÐmŸs¿˜OÀ¯AôZe’l§úJ@FRyUÛ2ËzÊ8Xýƒ¬†ÝìBxòKÞ"`zò¤z7å£AËÆy;£iD-,7ø¸%Hu£°x ÑRåÂYוQ˜f>CÎJlšÔì*eŒ|)XÄ7qg»»Ù#®ÊaéOo …5'0Õ½KÝ&,+óhÙ«QÜm«6”a·™¨ûÇoeQ¬vå¡&òªösmˆTŸ¼ö½(†ûu{Lxãg[ÛÜŠñ95 ßa$AÕÏ<ôƒ‡¾òÐ[àÀ~ Ü[‚Jý4âÝæü#Hvñ]™q}žS2 ·}tlQŽèõÊCs½ÞAŠÆÖß…ôy‚×ctÝlM ([D—ç€B/w P1B!±°º‹÷cŽ,& ./ÁdùjJ–p:·pmÇp´‹a’ôD¯Ä½×">~í¡ïF´†O˜Êưï <f®TÂжfe[²—GvL´$¶ºXrË?œÀ#fi˜ÂD¶Vzæã¡0 öqjäã‘f*N|Ps8a’öð@BÅÎ1EƃMt —Š#6Û£ÔÄ9l~päbÀ9l÷v¯ ìÓÒ´ioë¦à[è‚ fz5¢£Ëd >pf3 »ü0»õ}íÓcÙã¸ÚÈÌsñ!=íO‹ÈˆÓ9ƒCàÓæH¨5÷85©`š°Y~`ÃMÔ¨LÇEQWþNê›,]>k—‚Ìù¡¿Xˆk ®Q²)j'ììï”:_ù¬eÛÇîXžÁl_Ž|&ÿÆs«l—¨¦’ ©êj‚ IÖhèÖY(:á¿Ðuç[ÇÑè‘Ç6ö ûcXK™žãÃÇ5l€µbºu\Ç›Þd ‘|貿nÀ¯Ç:÷±z2³žÃGŒ.&ö_øšÀç~Màwâ“ó?$2lïð&y;¶Lœç‚v;6³5ÍLit–®J óL_ÑÕ´U{3Oï3vìt_õkÚI½¯Ñø,gŸŠœ¡Z²÷ÀL¡ê³¥‡H2‘9#,XÌÆ²!ŸÅìDÌdÑ\;Þ¾˜…òÿ̧ê’-^ˆyê<æbòábøK0¢ŽÛ‹©ŒuaŸ`O¼¬ýc.ïp/öpË·=fm{úÙØÅ?óbjåC¥ºAWäI¶ßó/"+b¡ïýü Åa4PGû3z1gä–W5û…ð}¢˜=ž,h•©W·§Â¢ï#whÜ¡ `¬Œ—µîŽr¬ì¹›ˆ"¡%DêÜÃ*†×YO].ó`5C{Þ¿¨¯&¦ö„t¸X')Ë9‡WQ…¾jšÒ×Ñk/L7eñ6UCOµŒÜ><ö7Kp¯ öh Z¾ï.)¾AŸma÷½`¥¾Î²]uUU‹ƒïdñ¢på⢮ձOb5³qwó¢ìÞ]sèÙýÔànNpIL¹G¶†ï§êD0›räSè²'ê²¾rê4»­ÞÁëNÝ'Z𵉠т8e÷€Fav©gì\Ø0û¸mÞIöx¿aß»ˆb°C͹ãRCôzd­@;ôÞAtÌH1ÝWh›ã°]œ'\ç‹Á"اW–ÄÞ0Næ`¿wGcµ/8‹3£¨8f$‡þÇËÄ÷¡$tSjŸ%awIðȶ(/¼­ø¡ª[Îó¦¸8ô+»OrT÷{‡oåQ¾»“ ëÇ×­úç]n×o  û¡ø±ª23»¬ò7é;âÈEù™AœÅÚ¾ØD•ƒÔÅ=dÕ!²2¤LzÄ ïxžF”ùÞ`!U¢äÙ<ãFØ;\‡ö?¾¥Š8)eÞŒ¸w¶ñáüà_æÿo•Ä1Ðendstream endobj 50 0 obj 3323 endobj 54 0 obj <> stream xœÕ\Y“·~篘ʋÉD 0·ý$[J¬Ä.;òºR)9•â’«ÕÆ{PK®.—ÿ{€™º1ø†rY–z‡î} ÞDI,d”è†X^L¿(£Óͤy½ø[G\ŸNÞLª8Õÿ58½¼ˆ¾>R/ Õq]DG¯&I\×UR¶½Š¨ÈÕßu•"‰KÕàb2fGÿ›ç2:únrôç—ÓŸgs˪,Åôr¦Ú ™•ÓóÙ¼ŒÓ¼Ìåôl–©‡iMÍE'e*¦'¶e42Îd%¦ª£Lÿ\N¯ìÏÝQ’ˆ¢˜nÕÃ4/Š$U/YR7Pcˆ".«<ãÜ4¤2ÑÓ1sXÐCÖß =]©ù õGVó§´žfh³(ÙýýÎ’õšº:ácÙ¢6Í«tšRÁIÛ€½F󺤦KÃòºãæé _­íŠM¦cºToEIï©Ášsù?GŸHYŵJ€ŽV“—Ý´ô°Kz‰n‰Çl¨ëÙ<Ó¢˜f|T6”jk±õÀ¼ÙÓK¾åv4,g­ÜÖößgS|E]]ÁyõdÔŸãG;ƒTýîlA[¶š!± òO‡=¡ 3òHÍA)œ($gÚUO€Ò"/ºßóLº¿7B£@ ¨Ìlé+Î|Y–J*JýÂ<ËD\(èšw`¤ßc`qcd (ÂÆ,Œ/—»köR‡sE"¦sz¿Qÿ-ß(&BhÏpÏV‡‰›>ÅCÅS9 Ú;Щù‰¡‚–L¾±\TóÖÆ!XqÀªÓ3«ŒP@¬Úpü@jÚ¢OWUj$)âl³szAgŒÆêf§õv¦õàëLØÍÙ¥¶ðN©Fª}öÅÛÖCª¦í¤Bðt R a’ÝÉ'³TñžØY>µÔ7–úÖRÿpÖÐ=ÄÌüw™©s¢rV» ¹® y W»dD$ãâkØkð+ìá—)lŒ÷dMä"4÷>½†ä|úË öv¥a,á„·#kÉÉr¥8&úÕêÕÜ#ÛÛÙ½ÿÎR/<Á£R!)zbãÇ)äÝyH©N!—^ÃÎð¾c!bmW°³ÛKòýÉE\zpBÐñoOÔ®ÿÀ7õvûÏfýîiX#¸î·¡×^Á9°ÞÁ!>£nõÝ;DŸ=7T«çBê,èà5l¶ ï%`ýðg~Ý‚_—–zm)ÚòÄ—÷PÁ ÙB†<#ò$ Óé#80¶õÖ¡ÎðŠ<¨ðÌZ‡9“Jw… ¤5Ô"Ñaáƒi'iV¹VZÅU¦3EÌÇv ö€ÄÑ  «WÑ‹ú¼Baé€ÕU,¸ÇÒ llFâÐÅPôOQ%eéBÈŠOÁ è©Ð;+ýJ$3¬Rº¤T|˜É:–"ͧ_ñœl0Ôƒ—Lò³¶N¸´ßèÙ× ôê§Ù ‰Ãq^OÚÓØPxwáضʸ*â"ËËZÙ^©ŽÍ×N@‘å…÷¢¤àÀa/?ê'xXh¯ÇÉõ€už°™uËÈ8±Øìv[íGãêHäN-_Ý+åË„®ÎÒ¥|/‚ÌÕÉ‘çEþ\'E¥m_e”ÔUQêìC¢{–ê©“L+m¡ü Ñ&eÖ•–εóQ¦•.ó!zé a?ÕE•·‘<ûXmš]¥þkâ°ÔwË"É5€5 ʺ`s‹œßÍp—ÔÁª%“¢2mõn‹Õàòß¡úÓö{£$Î’‚¥Ždª|÷‚G"Ö<"àúœŽý Ó8;B!^DüÑœ(bÃ#YáÏÓL¨­vd™û11Ÿ±i,¶ýÒ°wû*w-3õÅNp»ö~Ur5.®Çr2Ç Ð¹—kuÄþ1£ }Š.<¸Ãîg.7 ¸•R¬n »y†ñƒvð¦¯Ä)®€W^S\‡EÁ53ña{Ë|„5·wð¥7Ìt#?~ÆŠý‚ÊKcÌÏÌðÄIÄQγ¿qKÐl¼çÂ\’€sÒ#*PÐÀ;œ^ªZ…3›ÁÊgŠ­kë ºÀ‚õe­c¶EØ4_õùÜd’q™Vð»r&kÈ»-Ü´à§o¬ *(xñØÇr¾ËEžSÀöo7àOÝwÖ}dQÔo–ú‹¥#óèwÐÍXŠ1g‰¤zRsö;#?Ú1mš¬¾ß‰|sù>ÙiÉN²÷ÈhqfÁló}òE^ið(_Ò³F)޳חñû‹îÔ üÆð4êÊ-Ÿh×µ=LçG܉püß@`ºÓ fð$ÝrVWNÛ¶Sß-·õ(°XxQ®$÷êh=ï£=…=p†,ã®Îé—\ÂJ|þ@^‚‚TBLLý»Fpá%þDš1ó«Ð6ƒâ`ÅXXnÕp£­úÁwyÜÐ¥>§¶\WT,‚?Pô“ee, éý@*õxe-9/ÉË<›úD¯¥žÐ·ÿX –½»ÏnuÛKÏò ~ê£OÝU¾Á=w ±Ú1˜1¹s¬N¤oC_ñ;Š jÉpáÚ«¶cwžá+Oö3 ˜–¡-ÓïàmÕ3ªdÅg T-|]È{„.øB¨=ʽã«Ûgû”Uò¢÷ñ÷¨Üý»PÇq‘ØQÓÔÃãFJ…+©ÅŸãØ¢\URp‡¾¯›-jü!Ðy./#xÁ_QŒéÜŠg7æ&¼ƒ¬iãÐ ÝÖ$–†r\íáú÷Ñw޹5k~±æ•;îZ[Ȭ¤I”ð4cuêÌo¹2‰(ééw”£çg®`æÁdBP¯¡jnÜ?¶»v}ý4Î1Çáƒã0»RŒÝI†/[dW™uw'ª›´—ÝíâxÿbضS˼ØïÒ²ðs§‘9ƒdlþA©~³áDZÀ·=‰Ñ®>·áØÎeD¨ ›ZÉzÔËÁc]éÛÍmq¾›–TÍa–8uô½²°e€.AtLS¤iØÅfk€Ývžüî&€CløÜØ ˜ˆO½1_aŸ;¦ÿªÑ¤È´üMy M _¨Ôñ¿Ññ>¬L‹¬°ÁNT‰RÉø÷^DЉùÑÒR©¥2Kå(¾ÇR,"x…¥JKU–ª-•€w Z@Ë¢gG“ªÿy2­endstream endobj 55 0 obj 3897 endobj 59 0 obj <> stream xœ½[msܶþ®_q3iîšC€x!ë4ެXŽ´qœ›igœ¶#K²åÚ–dIvšÉôW´?¸àvA>$È»K-XxÙ}°xv¤Þ/ÒDÈEZþ´Â黃ϞÙūۃªyñìQ#ܼ:x'Yù¯jàòé»Åƒ(Ä¢H ³Ø¼=Óý‚V:‡ª<]eIjlXX¯TŸId ›ê_5ú2 €fðŸW#E‘diI'›³ƒçr?-­uKS1 Ω+›õ^Õ¡ÐzÐn¤ì˜Ï•°=|ÕÝ¢Æë|ëÇ•ÓÅáCn¬…0 Øx-`p]Ã]>¥´ÂK´ËlÐ-ÜÏÙeÓBÌñ(z~SûKªçé­¿T+¥6’[Uk•müR6ç›xE·Iª _ŽÙ°áL¼Ç—Öºq¶Ôo­Ü4…]¬Î+µìùßöŒWcT ß+Z×ËÅÂë8Y¹NÏó:|׌Ï\P´p‡ß9úQn³àÄ0 Þ”tÕÐæV§“úK¸‡½HPµ2X·t^òCg¤ÎžÌz{µà£ ÷2<îjG–2y8roÕïWk“¥mÁÏ 3 G… ‹Švï¯_"ZgßwìZë¨3r1áL Z÷\šÆ÷©D‹ƒ!¼P¬`3¤D_˜¨JªÝÆ$B›Öm:Ü£\v®TÀ=,ý½ÜsJƒ4"ä粑Ɯ!æzQ¥*Ku›zˆ,A”ÍPŠ‘D`áaÏ›ê;LØT„3KŠ/8Œ8U@BPq¡a<µ>Á£ÑKtÁCtL!êÂóõcù%Lp騱°ÖAƒoKoßé>¨©õ¼øãŠ–I ÔLüÎéž—±<8UŽT\±«Ü¬6ïdÍSÈ8­&ÐHš|œÝTCÇ• óRéHtgl®òHË|º% ªõ‹±´ÁÅ×ü¶ò'¬£ÊLÙ¢¬B8éãV=—d™V‡/Xë˜cÒ5h÷ÓÐñ+Óö#$6 aÜ‚5Ùv…Q‹EPíøB¹·fÕÊ”êy·vgä/ÍjwIškkšuÁÅ G6åè¢b%UÏp\&ÙF•Ñü¢•HùÎzâ`êÌ'JC7ᨆ«¨¡{IÐïDkÌ».a˜Å|Àæú©L7en\ yû^®ÐÁ 6Õ§UÁÉB©Çõ©4sÖœ 6¸ÁdõeëmnÆÖÛ‚e;{ÉWƒŠTt_ƒ‡¯3ñ²ñW}Ä´ÈmؤÄ1Z–áÜæŒUP§ÇÖJ`lab¯ä 300Œª£^×ÞD6˜¸®‚­k òn¶Sû Ïvèô1o¦ç-¨­aŠÃ_Quíë¤8'‹‘ăW À'·«õèM‡o­ƒzI±A1‡Nã¡/]%[”[è*Ù£š|r¥y5ÉÄ€z»œU»ð,ÝðêÒ‡€_\±Ë¨ŸVEbs¥†j“†Ll"…mÉ„MÙ½Ëý*„+¸GìrÿWɈNa`î^"Ã>ØãzXeîÿ` åw‡ª¡"Ëw˜W9£˜‹_?õp¨H£¹“…móýTóç,¯¹†:¶~®fHeë2ƒü£ŒIŒ è‡ATC1Ú€)ÈØé³V œ¾.uÌ-6û¯îQ™¯¼˜JcÙHÉCÁ; ñ›öN8H£¼ˆÉç-K¾QxF‚yMô⛀x%S;S–¸Ø×:®i^Àw!5å‹Õ%ÍÿåÎÅ>ö`$´0NK®9 ÌM#/ó±«ÄÞ}²Å>tÑyKƒa¯Gßóy™»Wžm5ñL•9PËA©’à]¸U´9g9œEu¨KËÄáEb×@Š½ÞœÎ|Ñ”3Ê(®>|ßgtøÿLb?Qëà’Bئµ9¢”ƒIJD]¾twÇv«[¤ÿ¼/©õ¿#Ñ”t†äj…ÜyÂ'ìePû ,ÍÌø›ÓàEc“æ_qøä*|‡5zå›PŽR¿9Y·ºëÖïG?;òOxéÆKgàÛ¯S/]~çü¬µåwvrùÔ?ßì8£TŽGs[znóü0\1*‘Eû\YéÙµ(y‡o©ÃS7$.Hü ‰‡Ádígn‡ÔáH| E¶Ä ‰ç$^Ç:ÜÂÖKïH<­¯I¼â3”î¤ÒÜÍ7µ#­Û¶uóÉß}AI_~îwç…—ÞÒ>‘«]zé—¾€NŽÊÙ¤èy`]·šV§²õ-„‡á÷&Üo¶qͺ[OÖ|6©ZÌDÄÀØsú4R¶/ ¸µ¿lÝ¡÷©§Œ({NJK¨´„êáÖþ²Òè,¢ÓQO§¤ ­AC2hHUÆ­lÚl}±$öœ”VPiÕíýeIéRW=uµ ê ¡ª¸•M«êÞ}̆Èé†hˆ*ãV6­†û؈%}ýgm‰…–X¨3níO‹·${Þÿª<Ô9‡ÚáÖþ²è¥ŽzJÍB¿€–PgÜʦ-º–ô¸¿‘>m!¦ìñˆ¡Ñsßw¥ÀBãXTö±:ÈÙZŸMv@¡oû„í¦ÅqÔymÿcl»Ã­X~+RÅØù7lÜŠ<ÄöìFj:€8A¨¾Œøåt借Dô¨÷„_µY|“BüpŽ[Ù´eNó+Z¼ñ<„ƒîL<_ Úµ{²~ì¼ “PÈ1äxg@~câþß•…¾Èæù«ïú—Ž Îlè:Å×?ùzØa+²ýíd²E{7îFí0Jƒ¼2Z¡âøÍ¦ :Ä"f°œlp”+ie´ºÅA›Mû¼Ëg“-Ž—Æ}~PëŒWÆ8ðöé+äÆù¶«É¶Gs 5Ôu´šþ*fèW]Cwso=ÙàxE>¶Ùs r)Ù´»x¼Ù 9„9Å<ŽŽlÚcÂŽ`'c°MV zë(®÷Å x ØÅòÉt.ÆNÿœ;„¯cݵx;C‹É†Æ¯Æ6{έÃã˜í»¶ï'°Ïs “–ŸÊØÉXÅÈÒe*ÜðÅÅ}2ÿ%‰,] ßEÕŠêYùô}€ð>2‰ýA¿éC /ø£h>€h&1'M„;8év·!sTE„/pJFÇBa=xÙv7Ó¼l›TgN98çt>ß÷ow]2®mxß›DOêømxïóýÚí®d¶=´ÃTØžTB¸sÓ a=Qkvc¬ÿžxÐqùf{Nೇ L¡ë­UÀY‚÷ó#N»P9‚©Ÿsê¢axƒáÉÌÍÝõž¥×0|Ï‚q‡ob8|ÓÅááæà{÷ó?äo> stream xœÅ˜KOÜ0Çïù¡¢êB!ÄŽc'íÁj·RÅk—ð,¨$8´´‚^Q?{›lœdœñ8Q»{ñz'“ùÿ<ã8óà‡ã~øü-7÷ÞÖ\ùw¿½—iþ¡<Þy^DÏŸ— }|sïïfù…<òÓ •~vë…Aš&¡Zxe¾Œóßiì+Æ‘øÙ½÷iÄÖ6£ V‘ÐFþrô˜‹Š—/“!ωÑùòÞò¸°dœ…âúE±Á)K˜x:º]̲4*#yFåën÷*ƒ|– ¦‚¤aÍà:ûèåxNaÏËÖ]8„ÃØ`ÙÉA8Ã>Æaà Ü1ÄÑòh!&`ˆA †CŸtÖ¸=Ià A‡‡C€CtPäª0ïšóE £@2G™#€LŸ I¬ÑÔ-;3$!pH@3ŒÃ¬ÉÁM~ŠÈOZ-ä§ù)(ŽÉŸ7å÷+niW_Xª€ â"ì';\; ®Ÿ•Á׺AáL»ïÊÀµær.ìÚ•FÙ„X+ œ» ÎU,ûV†=²OXÚÙgz¸a&ÓN\c×:†kÀÕ'åâxAg:u=öBÂD¸‰!¼f»—ÿ„GÈm+93v»’ ãœÔp.IûáûakY D¸‹+%7§„Üœö~¸|î¬ôPu‘ä 8õÿ¯ØÂêw_V<«¹  sÇzb+Ri®viã[{!˜Ò¶0Á[ö«Ô¡«rH1z*’&ÓB1µcŠ›Š)B#k¡„.‹4¸/…F ¤ š5…ºä²°Lh§H“i!X€ÒN0Á'€`z.ÇÖŠ ip_*ŽAm§˜âS@1y‰¥ƒ`BñÂ- J;ß‚éK¬¬÷ë†HÃK ”{ŽA8oB hO´£íi2-„& ¤ LèES¨Kz§Ö‚ ip_ NAi—˜àK@°ûÓ˜– †dDê`T¡ÁŒP9å8ù4D.´ndúõ'x+ž:¬]Ö–<!zò¸5 º’=p“iÁc *¿a@åPŸþU Aã×ÚukzŽR÷]·F…]êÙ7*ª}‹òÚD©Ó?CÔ©[o‚ {k©¢€°&½a]] R³n›š¥lkU´p£aÚ×+Snå?ßÔÑiW9á×KÍ`Û`Pî ýCÐÞé9Æf¥#½ƒFõÔÕëÂ2õ†‡¥EcQ½û/°$Ö·ÊöP¬‘^ÂßkÎêÅêÖD­?§ºUamxÍí²éÜk&™7Ë¿3.ßendstream endobj 65 0 obj 980 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R ] /Count 11 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 68 0 obj <>stream xœyXT×öïA˜sNì2Eâ9¨¬ÑX¢XÁ6 – (*( B¤w†™¡m`˜JP1V b!vIÔhT4æZ’è5‰×{£ÉMÖÉÛÜ÷½}fáæûç½÷ù Ìž}vYë·~ë·Ö±£zQvvvìüèø=a{Ä¿'nv»½„¡ö™8@þ˜ JyOÙݯ°¯êkú:Ô¾ëù¹£`@ò@ÊÞÎ.©¨b~tLòžˆí;âÜG­\;zìØq¶‘¼¼¼Ü·$¿ýÆ}AXlÄöÝîï‘?Â"£c¢ÂvÇÍtŸOfGFFluß™³#Ö=tÛ¶°mâckB#Ãv¹ûFDFÄÄD'¸š?Ú}ÒĉŒ'?&DDm‰u÷Þí¾Ì}eØöøÈÐ=ÿ5HQTÈ2ŸäÝ[×ùÏK‰Þ¶>`~LØGË|¸pÏö¾±;VúÅE¬Z¿zq® %‰‘¡k–&EmY;jô˜±îã†þþˆ G~ðÞ¤ô9“3æNñžüá†i§‡xmš±yfê,Ï´Ù5žN}D-§P^ÔûÔ*˜ ¤R3¨ ”µ‚ò¥fR©‘ÔJÊú€zZE-¢&QžÔjj15™EQK¨)Ôhj 5•C­¥–QRc©u”?5šF£ÖSÔ|j:õÕ›êCõ¥ì¨~T/ª?eO  ¨”„DÑ”#ÅPR*„â©ÁÔ&Ê’QÃ('jåLͦ\¨9”+5—BySnTõ.µJí 6Rµ‰€,³‚ºn7ÇîR¯½¾·ßhßwØäÐ!Y&9N{ÒÕô¯Ì<ÆÈüÆÆ±/ÞYøÎÕÞSzŸéÑ盾•ýÜú•ö÷èŸÕÿÉ€’Γ~>ÈqÐîAà¸Þñ¸T&]-ýfðäÁŸÉ¶;1N±NOœÇ8'8ÿÓe²Ë‡.k]*]κüâ:Èõ¢ëë!ín“Ü®¼ûλõC‡ Íz“ å.òSøBw÷R÷χùKÖ:ìåðáú®#ÆŒaqÏc†G®ÇAö‘áUá2 ñf;˜Ñ ‡›ìa›`’ݧ«uÚ²r…6™ÇAtr¶2=C¯¬ætÆÏ?e¤‹Ÿ?ØHW© éJy ‡§àÃŒe1ð4C«ÙN[`&„Û Þ(»„=%=…‰´I§-/Sh“øp¯L¼™NV}tâ×}iì×/‰¤“åä ƒ84–®Ö“'²É‘fÑçÁS%fΤ‚VIMvÆÍB•Ù® fƒL·‡ãN °‡KÉ¶ŠŒ ½‚,’Nî¥++Sè’ù%4è…*I¹mYlO§XXÅ_¤ñTüO‡§’ÏzÌ`è$™¡SšxË–ä²ÏÌ1fGp4C]³³ôk˜)L•y‹7gUóõøYd]Õă³íÚ! ¡š ÆJ#+=µï“Æ·O‘I¹/é@BÙÅbÁØnß‹®²"…-ýpá•ÉþÛ—­àÈ!RÍPï‘S<5;K„B_Yèñ55ˆÅCÆŽÃ.xðOÀùæÍšãmü'¦ÊjTÆêåúlE~A¶‚[¼4v>™Ùkîýƒýƒèõänè5/½R¤Q–»•jõ&^Úð‚‘Þ®Vê39iCš2;™‹Ç&ðÇó`:L&¿}ÁËYú#<³ ü ƒxEz¯Ê -«kRDëvãèÇã x"l´ˆÖ-+·X·—h]¢uÁgà@Œ$)´ôGpœmÞ œ;Á ï”,µy\EìŠ6NæSèntß5 ¸¿Ù2ø~“è;ã½{àq´mÙ!¾Îv¨Q4ž€_ãàµd”ˆÆô Ñuñ6(ˆO,O'ñ>B¼Ž†Éø7‰Ä9a$¶Š!Qh¶'È‚z ú*ý¢ãöÓ/7Ÿ™v„¿zä@:Ç>ñùb ‡¶Í.Ðf&ƒ0˜ÎÜ»²Â?`Ó‡<öÄu2H…ÑL;ªWMjHØ»mgýW­_ÀY6ÞI€8ŠŸ °H¢ 0ñ¯ãè$mvY9q*wÔ ¾€”lz¿bˆsÓ3ˆg9é©U$á2ü(±`K°³¯79ûya¾ ;ŽôÄð 7£€ þÍ¿ ? öü'Ìoqø÷ã#FM›áá1íÑ›×OýÂ[žßc†7fp¨©­q<Ý BÓVèï,Í’…ײñ6\Àrú£»+Í%Pì3†ìÀKÛð€WïAŸWuš“¦OzÉt»Hš±šÆÑðFöúá 2ÓsæŒÑž³É®ý‹ëv¹æßlj‡öp´2Xµ’zºÊ +-“ëûÙ,ÜDãQ8‡Dɧb„ŠÌ—d™ñ±õ4,µ1Œ±FmÍpÒ~1Ãm³½ï$˜ê¸G‹4 .fâ;:·ÒøŠP(F§XPÅ_ÕUÆnDm¥áJga—;´f»/Èò{µléåû£EhùÇ!Á¡£¢v:ƒÝ`ï^oßú,w`i2°Æl\‘“¯PqAK×}ìKŒÜÛó ôãï0Ðçï¯Àþù£5Å\aªFQ†Ør­„‘S¥ì¢v+·Âûfx(’¹ @ õ <¤Æ‰WÁ,^¡*ˆ|"\kP‚ˆu×f® ÅôéÅ£¾F, üéèÃKAŸñ/ð@ïu1¾k8ð½({óÀgô„™Þ£Gͺÿòå7ÿ²¢æ™aŒÙN¨„`jͯI;ºãÉ̶±äƒ='àþØéwp{}ÏÔ² ÙªÜü%¿u×¼x„)4óVúo¬ÙÁôã˯Ð+ÔX9‘í Ž0¸I‚£W3\nƒ#Ç –0Õ }zºÊxþRÍÀ#a³ä îÄ“™$]vy©ŽD ô†£y ~Ô¹Y"M dº}Òf¦XBäž &ÃÎØ {ù < p†)à)áa$T€ýÓØ‡,Áôx`ÀÜ ¿XÞ–‘Ið~o匞؈¦Áé5Yr8‘áïvâptO¬š;ãeB<Œ`. †´ã;Ì[ö/GkØyÌðÉ£ÆýÙ›íføƒ„å+a<“IÝ:2Ç ;÷Á}þ1ú?¼tøf+‹G1)ÖTjq¯ô¥Éêà$^újPuÞ2Ò…Úñ´ôö‹!jqë(ÏÙÄ­;~æßfM;'WS“dÁ=2lp;}îÔñ›µæÜüÜBEˆ-%ÛèUºŒ">Rã« CìK×Ìã¦0݉õ{ült>þ ó®4‹»òôJ3[Mú˜ìë©2˜Eý¬ãÇŸ¦ÝÎãC=·3¾'G§ÓÝ·Â ©P þÌÛ«ü|篜Âw!jù·"Ó[×£ùwsb“³ôÙ†°´Gl…O5Î"Ì2Óf!ØÂi´Ž¦S´ò21î8à;}˜¿8‰4]‚)tµJŸ‘fÁé9â /™·$»Ï'y"E®gÅXÌ´Dæ}ül¬å“52orÑ˳Dô…iiÃkèujß)·½¨&kœVYœ¯­n(%VO)ç?®7ìFëP`ôûÓÙ?Áç 3üpz?>þð=ÏÍ kÌsÃãÇc3?‡»çÜ<Ícã·:ÈÃïÐ61t:Qu¼áðѲ#ÈÌ’EF]`¤/»ñ`ÑìÙË£¹Þï÷!0êèøI´:fÌiMÂ#³£%öHÃâTNýÈLc‰0èÎ$߆£ûS<è=ÜK”9¹*¤t%G0¨‹ ÕÜÉÒ/‡QªÌûDNä˜ü1¯Ò­ùó#5¥rCV^n~^¿+1E™€2¢(VÏJ“ŒQqÅ»ÝPZA¤Š=+<“ý¿Cº‡ðh†ÁMö=áQVfƒGFF7<àËÑLŠF^^®±b#诰ñ'”ZS/až 0ÈBÙ‰§àøig©QX Èd;Ž„T¬D¬Ôwô¼à%»ªwLæ¦Tv(/)(¤H«ÚƒâX©qÍ’mS>\ØÐÊi©o©Â@äß1#ðÓÉç$­¢œ“”iu&ÞÈ4‡ŸM¹Iè]òôÒ×Çë#÷ò»jÌ˵“õª¢ø2²VBYVUÕæKG;|µ©„“O-Ò*JÝÊ´‚lãWŒt8Ñ œÔ˜™Mv°Z¬K9XlV'†-Q ãl—¾ÐiÚh {åàb“a! £e¯Ru ¹6¡½DW×½ÕÕ!LO9Ùmµ’¤/º2! 4.`UEŠ‚”.õfq—ZF6y°£eõ3<•½l£­(Ú †EŸXÖòÃÑÓgÐ öÛéwˆ†+üKJ™c{Ô›µ¤Àxæî­ åKf¬Ý•ù–™¡QÌõBŸ&{’ÿÂdâY+J-^#Ò}fšå<C¯þx»b-ÉŠ“ŸÀ»ü=]=x£¥©±¡¥ê z†în)YǦhÈ4†jîcÊÕ¥¥©HfOgBN¬®^Œ– À¸Ðu¢|Ð|Ö‡Á“~ ÓÛ¿¬®¿ÒU`üJ¬0¡ÙÖf>ŒIÕµîãccá¼—önþñ[ FyN^~®’Û»0eb}CµñPÑ6÷–¹~†^vp”ø÷žH\“{ª7~†§ôÈ}ÿÛÊì–T1•žÈ2•Šd~%CÐXÊÕÑ4^&jV—ÁuÅÀ PÁÄh³ãæä¸Òâ,.¬ÊeR£94¤:Ðmø,¿)¡ÕÛêãøÃñ'2¿Ê¼“Q£ªK;˜Rµ í`}½O0?ù´åÝ,¨$9±$8 oJˆ$P”–“í¸CŒÔl¾{Û ú~wûU[\KØ~>Ò­›SæoˆÕÆTÅV¥F‡Ùíç?lY®¶`_£,s+Óèªù_±†¶XóÏ4A /,&Sƒz`v˜Í…øý"*DIPÑÇ&.à0\aç{|ÛÛ¦@÷wÆ‡Ð‰Š·VæmQ°Ré·ºý™5}ÒúÆY',uz`«B¥IB_’cz8¿œ–žZ¾jyÔ"·u[Így¸8ÎVÀ^ ßÎ:¿öo7¯¼p‰ëQ•ÁÌfð"ž¿o‹×<&UŸUZ®!fºs¦™dk½RÍ݃™ñÃÿgùm“LV™dÖ;uÝü®MgǼM´Â]iqq¡Z³Ýal±‡çN0Cè+iêA¯cm€¼E“Jì™$‘Æ£á™ä†­¼ï9ÉLìξ’·hG±×fR¢-ÃC!ÀYÚ óa‘¬µ‘½ %1´ôÙÎçl=ŸœÜ2„Æ[a3)_%§ii§m/'Û^{iìƒ=%BŸq6Kï4­±Íxiõ¯ 2Û½¶–*¹Ä^õ–ŽO…R“$ÒI²B•FÌUI”à zÒÂ…“'ù~õœƒÿUÐ$g“YÅIfa‡ï&ƒìŸ=®{¢.O‹ &;ü£GO鬭Ò'µòôì%þ3ÖoØÛºŽ'ƤÔe¦)IÍÜ]Ü{±¿|ûØR¼õ„ƒ—½àïô„¸¦º‡kzÙîyŽöÅ÷&N²ŽžŠuóážä³ÿy¢‰~J¼ÓF?ã l´ºI(°¤+š^‹œdk0ôî4ý_8émð,0ƒ‹Ù‘üªU]§ÓœþÑ#~ÒVC }Çï³÷פ'¯æ²rT*”ÅÊuÙÆ"µ(Iî;¨=Ø{çv~Ä/`+"5 ¿š8…—žûÅÚï\=p¹;å°hÙ±ƒãRßvýÎÉß[Ψä'¬¶ò­]8Øl5N—iL’#– ·Baõt¢²‹0Ò8¨3^’Gã!^òµ¥R‘­I¶ YnC5„þ¯âdøˆYñcÙ£{/#[–ßÛ\¿·å*ÚV‡·i‚=ð‘K¥ˆã²rÇ"½uÿ)ÃE(ºßZsþÿçÛ€rlƒåx:9Ș†?ãé9<†Ý2˜“$ZZúuwCÛÙë4‡b'<œ$­–¸-·˜ÄÞæY-MÖ›„—‘˜Ì¦¥Ïmõ…«í~4‰Ç10‘,éÁ*.¶Ù=…w›`NÓÖ×`G~ˆJÝ$<[åRŸu÷•ª,]?cWìG´HÍ—Öw«?±•Ç·;z4DN‹ß7^=¼@æø‘a¼Tƒïöœði§¯n8»bË΄õÛ¸ø3»ömF¡hwrD0ûç> 0º¸ ¦[õVw—†–ftšz䢗ÏiiÛ‚Ó×"îºA¿W¯a ÷ ðY½kÁz|?—ýüõŒ£gz5ëñë×ß<ê baXÍS³]ƒ7ÛçvXÁì;=‡~N@ZÓ£-8Ò†–ïè•1ØNxWâO§óÝUBŒäI¢÷°%ÀÑì°·Ý^ø€\BŽ”Ê”˜¸×6üòDÇFÜÂûÂôr=bËôz£º (¿„_ƒ¢^£ßÑï‡ý ú•!5kÌÖgq–µ>·‡@²V–<;+åçð_áA‡<ÅNƒ{Ô_Ü?/å£\6KŸm4hµŸTqwÀîSè~F¿ìú·8Ä5Êõr$šÛB,G…%2ìzRU˜W˜‹\årEV–.[WÀ« À5Ô ;¨äy(W¬RŒºµQÇ=ûÇØ^ŸQœ¯E®FƒÎ¨ÉÕæiøà@’IE‰FƒŒ®ä´ò:z_ü!¾œ>¼ïÁƒ±{£ø®Ó ùä¤Ý(îi _2AxD(ú=â‘z¬)Þ£—W#×j¤7×ÕƒÆå3'˨A^ÕsTyoÄá&?´”Ä ¥I~Qã#"C I­ßÝš| ]c7³|`pƒ0ß5žjü¾Á:!S¶xõj?¿‹«nݼtéæ  ùp‡³aë7mÛ²qã6së™O<@˜([Ô5ñÆeËD_2ñLƒebȆ0sëéÆO[y0âYYI‰éYƒBŸ›[PËuöÇýÓssTHîJ˜DW¤.*ÒZÅ´#ÕÙ}Aá€ÀÔÜ¥8… ¡XWŒÔ¸4K:Ë0ßã…ÒÝ=7®[´Ì4C²Ùñ¥ù0aÿ4a¨ÓCÛ¬tLsí'Õ§ËÛ\ÚF—ÑòàÔ͉áØ|]v¶ÉëÐ>ödcÃÉ}&ER-w¤Š°‚é+乪ü<—­Š+OÐÄ!×E«‚–ì.É:åÃû&®]KÊNéÙIw|Ÿ¶6Ô¶ââdqÛvÅï@lÀÚ¶›WZ>=_ÚÕgûÕQð7‡¸–€³YVÌ\>óÅ•gß³ÒÍ$ù÷«9tmÈó©Çø¯Šç’b3¢P2KÑ`ÔhËÕ\ŵë-·{ÿËå¾!IÑã'ð8 ÇJi 0ômRu¡IZ²Cì©ÁB”G’d(".fW^R¾2¥³$µT\hi½Êƒö×(ª(ÒkQ*ÏÖ¤nWÇÖ¡,ùîê¦Ö ´lEfº6×”ÇÍÝ÷1Š`1yf‹1ñØž±Y h‹&¡Ô²Î!-·(§8¹ª*7Ê%?¯€ä¤,Qér@Àë]°€?Räæä …+Ê+Ì+"ÿ\ŠT„ÿ üBu1k h2; ^æ$KÃ#Yaaqa!b¥ß^;ÐØvyÈ÷s¿ô\»>5j+—˜šžŒä,Á‘QW\\ªçן5]Bì×Wƒ—nŽÞšÌ'ÈS >BlJV‹Io¿|²xÖܹ‹'¬^Usb¯,É-V!Vž-—g”¦×§ðm1g2Î"˜Ÿÿ|kCûì£>ÝÑt‘ðx™®Û…"PlZĶÄ8E:R)ôúbT®å²QwU!“¼.ñ‹µµÇÐatÈTßTU£+E:ñ†%/§ý,¬%ÁïÚü{á1ñeO¡ ]ijAì‹ùíÃÖíRF/ãy™*yëÇt·bÍÌgŽèN!öéµ KV®ß„™xòµµÙVÍmÇýd I‘Qµ‰õüAº~_í¡#q¦ˆ®žÅ`3|'V‚IN0[†OìQš.¶½ƒüŸµU> Ü4'uºH60°ß,Ãi4É¥¿Iö‹!èo†Yf$—yÇü3¸‘¥ç‘<[Oª]¹Q©Í,âáóÂïöD,ñ"ä6žÃ2hAuÀ‰5 ëNÆ^Clǃ£GÄ ,@YY||êÖŒÍh=ÚaŠiŒ:•r]ca .×|qëìz_.˜Y耦± ®=4 ‹j¸f‡Œ¼Œ‚,Ä®ØzüܵË'òVX=n‚Ùµˆ¤[f¨%q@r;¨èë-y|ï¥3ˆð)))Ö”r͵-Æ&bÙ+þ“†/š7zEÐþ–`^¥Í)"xÉÊΖg”§MàÏí<—zžà¥Ï·Gpð~4rS¸"a¥·;ÐüLjV^»&{Ù£xúŸHjû<޶²Íl÷ÏOÅzÜ!G–o*(Gt£ªý`ËÙû[¿F&dÊ×çèrÔ%ˆ5jõFÓŽÊÍK–ìÙ¸–÷^¶c*Â,‹ç?Áý¡÷«ßõfîÓ‰ ‚¶zmåÔA²ºcuEåÑMµDaß¹¼xÖÔ•‹|üÖ~ÃçêQ¡jŸEÖMøƒ“)ËsЉÊ#zNžS˜§Vð@R±&C›£G®ƒÞ Î/ÉÓZäK“¥Æ´Eh–á)†‚ÜÊsµ(È’’Š>ª$7:Ù¹L¢5¹ÖpÇ¡…é²Ý]¤.cÓ¾ÀýIX½ûó«.՘Ō)6–¢…>úÀM™ôë» ‡›Û‡<Ÿñ`ìØs' ý*œ“>Ÿ±míâ!cžÎúå—§ß¼º¹íü¼cœ7T’ù®­^°$`­·wÀg7¿j¿xŸÇ ÒçO®¯ôöY0ÓkiûýŽë—[‚ÿ¼€ex!ŽUdçç!•k‘"º’bƒ–OðO쯕—ä鈚%¼$O£Òvám'Éh½šáL£½²Âä¢T¤ YÙ?zS°÷¬à¹( %«å%Š’Ü‘‚É'ROÞ¸Q×zžïøâøwÞaaÞtè{™ó!¦F~=íÅÝóMÌœê¢,vSVrZê®í[»påÍo¾»|£ãVÛ¦ixu6*ÐÄY^r :g'Þ²ÿ8¬ÿÃÁb?"›fË_‹G9œ'ã»ÃÃï¶Žjhˆ>stream xœeV TWžfQPˆƒ J’º!. î¸VE¤Š"¨ ›È !²XÅ"ËYD ˆ,¢âA*â(îþn¿[Z­õ/µõ/=µ½“¾Øó¿mOÏNNÎÌ{ïÞûîw¿ûÝQ¦&”H$²qW©UÚ¨°I‹4êpÏPuÔæ-*ag"?\Ä0áíĉXómH¡í(òÿZ Ab4ÈôЋkxgºÁ3„‹DÉ…•‹5q)ñQ‘Z帵޾&Lü{ÅÙÅÅEšòaGéªJˆŠˆUŽ%‰*µ&.F«£\LN«ÕQaÊuJ\d‚2$<\.˜ù„¨UÑJ·(uT\œ&Q9n±ƒrÊäÉΓÈß”•Q1¡[”«Cb”J! å'ÚâåEõH‰ Ó„û-ŽS¹®ŠOˆôÖFmY½6I2ÎA9ÑÑÉyÊÔiÓg̤¨I”?åI¹R«¨%Ôlj4åE¹Qc(gj5µ†ú„šJ­¥–Q>ÔtʃšA­£VP‹¨”9%¢,¨ÁÔÊš’PC)޲¡†QRÊžàL™óhªEd/Ú)úÞDb’nÒ)^'®ÿi:Óô6mK_e$Ì|&‡Ñ1?˜Í2Óš°+Ø\ö{›ýe3@9À}ÀQóææoF¬‚c–< °â»ŠxÎÃáñ vzCóÙÌa°¢ñ/Œ%¯ÇXr¢×qØ×hÁÛÑØ‘ybÔå§ÒÞÁn;QÁ6¹‹ÊrJòªì,j,`¼ÜGfF Jæ6t”wë/ÝA²óuqé¹iÙ[îø_ÂÖSøsweAªuz]ž²pmÜòµòŠnš\I ¦Ð bÑS%æ_Á¸]5ùŨ5omRS*wG“ÐÂh_UKBð4ùUø7Dž mOz„€Což4ƒ-‹mµë¦¸µˆõŒ=qO_vµæ¼¢þª®®Gµe‰E©ùá,¹û‡pÝ0 *…Îü9n4r^57ÀCm¦³Øòù<õms×uyë͆ŸÑ †Ì|‚íä[°)w½1ÈÕ?Í'!D¡^¤ö%Ã[€äÉÛWŠ~÷Sªøi$È´ƒÖÏ`Ô0I2ï`C'C •̼OÛR»Éf#— ßý]c‘ +iÁÖw]~½£¯ÕŸ“oä­¹¶ÁK°õBLÍð‰jù7X<“¯–'Tep%Øo†jºø`Ïap¾ëw…Gc öÂÕ4Œg, µ0”ßCE¿ƒ?ƈ GùDnW}~)*%|Žð`¥ÏØÙrשÁx8‹§ýê `Øö‚³r<Љurõ€Øñ~mo»ë^¶ÜW´>ºq²éљ䪨²¤ÝɨÖ’ñ/`{КϲÖÁmJO—'àœ¬òlÖÜG2˜NÐu …äHÜ;?r_³*Hb-Éy½–œ§ûD¶ÜëG`òBñž"v=`NÊv‹”-K(Û}Häöµ6£ÓèÜÖVÍ©ˆ¯šEhš£ô_îD˜bÏâ!/f‚XôÞq»Ë™$Up|b½UºöbT´«^™=ŽcŠŠ*ÊÊkš*›ÐItdk™fojÁ6Í…žJ+‘Á ÄOmà4}Óø°÷Á›309 ‹ç=ÆŸÈñiæ±—ëåhÎ|ßö×LCl_í`¹†xã_opÊ8ðdž¡+5ú޶ޓÏÑStYÛz,¢>´Ô±øz“rvów4482m§¢c·…§&*"£>õ!®yYKþ£¯CSy÷¥3ÝHv•äUä…‘Òù;Q6ZŽ–‡.ªþMúwûYƒ`8ŒS‚Ý0IßÅ›p»+H#–²¯´cë¹î‰ª ò *7òC«Ï„¿Œ}/•ôÞL*ËÔÚFoЮŒTíO–§íË*Í«f±““š—…2Ñú#gãµ­Ûo æÕ C¯…7ΪUL¨_w ­”5œ¬ÒË%=íÇ“¢³ò²s3}ÓGò>‰´¬„~éÒ_¾…d­uqþ‚À$þ¿À<óÒOs]­ñð‘_Z¨`TؽÁÊšß NxŒó‡a’ïà,4sð1#éé¨OÙ˜‘“ž¡ô&»}¶q³-Ê(Ø^˜ÌÂT¦ðpzÉδ_† óK JXIO5 Þs²ÖÞ|Ø·\_WW^SËJ¾klª½ù%Yü´1îptCØ~/Ä-þªßK~ ×Þ4ÌeΡê¬ÊÔýéE[Q2Òd¨S’6Ǫ3üHáïÞ#â§ðuÜ<æŒCÇÐnL’#}Y‚Ÿç¤å¦¡tÙš&õÃã5»ŠêåߤåîÈÛ†dÚÔ’{ öí.WÜ€Vú+ÆŽõ‘æ^<~ߌï©!ˆ‘ð‡­„׿$ý%œ?mf¼N½?Ø:Û×w’ùm®»°Ÿ8¯V܇NakŽqPvrn J‘͹âûòQÛá[mòÔ¥t¿§Nà¥|/‘}³ÇØÕ)žÁœ±·Oœxýß—û½ÿ2Š|–ܽñ ï a9ßB*„Òx*ƒðFú½ªõ³0Ql°'¶#™ÉÎ~a“‘lÞúý‚ý»>W¼å3i2Çc?Û”½ m‘aöÇ…0áewÝùëòŽ–†/+¾&G¤–†Ê÷Ã,â71ÿÎÊ…ÌÇ0x¦Ñ’æ-ñh3<Õh¹“·ux£˜ÍÙcB÷,ðþügý]Õ?¬/Áʆ‘¨)çR˳ÊÐB¤½E7A/Ý]QpíCGRNj+£ëü÷-AØÍöÖ`†1åíÇÞBì¥Cé1‰i[’¶+’¶!´x»W¦4`ÇÆ4õÕ¦•È©*TGÈI¬=±ý‹áWÐ…êkm9©h'úŒ•$£íY{¶±0‰Áƒ!†“´¸¤-_~¼Uwüv©âbÑç»J öæïΗ•ìÊß·W ¡Áï è(&Ÿ¤æ53ñJ„×£ñu˜º„°?3Á0:í úLÒÇ0âÔ­Sí~ú` Û‰ñ3ÈuÜn{pªýž#ª rô&þ×5`ÁÚ3­Ø~ÿ"䈰t#–.À#"–GÌEì‘_ÒAÑkË©ñXú“¾4"|ŶŒÕR0ùüZG%˜;iø"È%§H™…ƒ«àXZ•a1 o4¬çð»wjúï jºŸo‘=¿“½×|7ʘÖ× ~´#6Óe¶DIŽ‚ zÿ)™4| Œó'ÁŠÃz¦Vwä |üõeÝ‘†ÓÍ«¯£›,Póoaì4fž»Y¶LÙO-ú{ˆmkˆñ J ˆŠR¨7mÒbÆ+fù¯ˆÄR–Oìç†%ÄþADtïrDÂ({7LòtpŸ»€Šóª³X j{Ul¨­ëOwïèú yòCfy%4ÁdksSQ2‹M/›7ώ߸$oºXûÝBmÚFÿ£¬ämç…¦ÛÏl\ÔŽ%3–ÄùÉ7†¥lHÇÇ7V–”W6'sŽmiˆ »Ê×Y‘Ñߎ0X~i…§PÇ-dzxw»0ø­Ñ-›ÓdîEag´àL?d°°ö` :è÷YUé[ÒúWá#À3p?Î(aðX£‚kæÐ ]e3b¯4lòë¹`¶Odmãžü¢üb™ÌÙIDÃÒÙqË`Pï˳WäÍ5Ñ=LÜïaQ *C(oƒq4\ø€a1˜vU}¦¢ç`Cfê(±Á”oáÈÈ*F{Ø×nW0­œ¾ö£erŸiØaK›µ,^t6=ÐËÏ]«ïBØ_=nŽô LQùÉËoì-k¨¿¯;ÚΣê”â Äk¹ Ç#×9zÎ_´.RwùÕͧýTº&|ÌòËí×¶ßì‹W9`Ç9x' #=¬þ ìÀf¾Jj©­æu‚ÚT3@›Ã˜@ï4ˆ¢þ*t² endstream endobj 71 0 obj 3009 endobj 72 0 obj <>stream xœ]Vy\WEÀ@Ó¢ 3ã…‚‚·ˆÇªáਠç(0ˆ€ ¼ñ@VKQ(CAAÏ‘#^E7ë&A£ µš}dûzƘÝý£çxý^½ª¯¾úª$”‘%‘H†xFÄ$G$ªÂB]Õ1áâÒDÁF"Œ0l V÷·ök¤¶Ô’üSdbˆLŒNŽò…… 1‡ÓC õÊP"IÙ›³H¯IPEE'*Æû¯Xm?aÂÄ?W¦¸¸¸(Öiþx£p‹Ø¤ŠŠSØ‘É1êøØˆ¸Ä9ŠEdwLŒ*L£‰Þ¤ ­ ‰Ø PªbTññêdÅøEöŠ©“'Oq$S}U±ë’6)üBã6)–(Dÿÿg…¢¨‰š¸0Wuø§¾‹â#–º-sOˆRnŠ^ᑨòóLZéå»Ná8yÊÔiÓg΢¨j)åF9Q£©e”;5›šD¡–SJj25–šBÙQ~”'5ŽZIyQÓ¨ñ”?åMM§ì©UÔ ÊšIùP®”/µˆ’Pf”9eAq”%ÅSVÔpÊ‘`MQ‹©}Ôß%#$$ f¨ ² ú —ÖÙÅU=•ÚHs¥Wh_:Ÿ±cò˜{Ì¿X5[ÄþctlPø mÆœq²ñ}ã.ãWƒcgþÕ$ÉDkêdzÊÍ„&¬îëÿ¤P"È-lñ`ÁVŠh¸!ÝË|Á¬‰HÛ‘y@#Å}‘³7EµYÇ“—_Rrú¹af "ÁÇZ´€‚AaÅÕ·‹Zò·‹áºn××¶ß,[¿X†¿'+ÿf`°W;f\◆ɸúG ñ$¤ð H%g@a(À|: ›ö¶A,žá8/ÁÊžÑ0¬~||®[Ówu¼?bÝBn¿½t¸!¿BžWVžW®¡“ñyn¬Yÿ}TÔïV(¹‚8BŒÐʃÕ@«hà…Vé@+8ôÇâd[táaB—'þKÞšõG"0‡åäqs‹np€bÍ`Œ÷¶{(<¡Ÿ¡«yuÚ:í©[è:º~Í¥áîùSµèº˜Ð´®2D»&oñû Øœgš:´qþ«"ã”rìLcs#ð ¹·÷ªÖûøF¬w•cš€øg DYhÎóØÅK°;Vöb fì>ÀBX<þ=ž'ÏÄR¾ë¢r¤Ýj÷ó×töõ]ê|(×äX$L‚A0½Ðâ4±–&fd«`?$`Ρ£›?;áI\äíñ'ØÙµxaåj9׸Z{GuǦ ]8q¶‰ÅÆ‚)ÿíåû@w÷ÀŽ_~mèì”›õOÒãJ q0œ¡„‡-B°Ï¥±?Àöð@Šçа{€¬•€#‚©;ÐN`³´AÝŽXú3˜Á¬‡I£.ʹ®KQžg¼m|³wT\VŽF¶õÈ®#»N“íô~lü˜… kyÕÕÇ/¢&Ô]ïV„Ý/ ÏI?„ŠÙ²ŠÂF×Û†¾Þ”À~lDq$mÄ›UD¿Úúcyâ©ØnÕ×µ”OKÊ“åhöoCÑ,~ªë¨MÓYSºöèZ¤DŸªÃ—±P¨— ("¬ òû˜ð%&bK°·âH…ZЭ§6úîBé{·Ë÷¦ JCû“ ¢Y˜q’NÌÍ8Š*öÀ†=‡ª»+jn ¢ë››¢+ן ÊóA쀞cSˆ}/æ:¶çØV–{{lkv‚ÊE§«S·nÙ¶ywúPqŠ"È$ÎlK‹Zp°âRú…<®'xϦ÷¬KOHÚÈrõÁQ+7.·Á¦Ó߇§t½îrÝÖ¸¯åEš#©¹,Œ¤5¤˜½Än$)‘x4ž„7ã0Ï€O_ß9Û}UÎ¥› ý6ƒïÏÉS} (åçÁX¢Ùc•NøïôÌúПÊOì;tBöšÙö厽éˆÌ8Ò ‡ ¤‹B9ÉžÂÛ„9mâé0´‰´§ûcˆB¢²“Á„ˆ”‰¤FC3Œ6~öó¨mk]Tu@óìÓDð";< K¯­„ñjù›Í¿¦hÕhÙp_¿ÈÉcWœ{´[F7O~?| ¯—ásRÂÌò›Ø¦TŽébEQLj~¥ñl×˦°…dú”¶ê;A­(^ûáŸéºÁ\Šk ;ˆ¹gŒ® ž †£kÝP‘"T ´$Ž®~ñÚP¥±­2ø•ÄhÂàÁw<€¹×xòfµŒKYÌè9ù˜`Ÿ‹—ñB/*R>O zÅ{S‹Xg*ÂWýýúNd)ôêÔª\<žIÞd0ÓÖ†::®­}.2HË`KñòÆà¦À®ÏÑsÔUÚÔÐXW|u³BóѼ>¢y…XúÒ•N)¦Ôâ‰n)ØŠÑåó %;¢S,41`°à*¶Ãvó}°y† l謫%Ç:Û|R¥Éزu‡<%!eš_ú0…fõJ䀂r#NÄ&n(¿Ò`s])m¼Är)H½/=w i<by®~vXÛgq'ªk‹ ¯çȲµY¿Ê;4Üì_¦縛ĩúÇŠk¼ù_³Ü“;•Õ7/œŽ‘áÝÊÿü,˜·‹oaÊ#p“_¨lã%ÿӞȅ%­d¹Æ6æÿ‡“‡“Ä‚(cãª3Úm€ CÀù^Òý¨ËòÈ&¯3^È&¨üYÈaÀ_ä;ÝÇÙ¸+Ý>kï{­ëÂd¦…ï! l$p ^òø¶z¸M„#d¶§¡p1¥ýÑü|ä½!dÙº•)“¶EØ<ê×þµžOQj)¯»YÝ\ø‚a†ny®ºÚ®ÔÎ"•!5ªD§7çGþ5öÐ<úÜsÒÕ[BcÃP$R¤Vn+Ûñ½@O=9|:ï\Ùñj"#BA6ºç=›2ÄÛ3„müÀ62}êÄ$9»†`3Ä:‰S°Øv5@“þn Aâ$œ æÃs}ç‚z¯õð©ï¼[w¾Mn–X"h a^a` ÆÆ0r0g›˜ÀÈÃ&¦õn“Çb endstream endobj 73 0 obj 3472 endobj 74 0 obj <>stream xœuW XǶîq˜îY”Iƒ‚ÎŒ`”\PAEö ¢ ("²ˆ€lã)1q!Æ¢"š¨€kX÷%*Ñ0à 2ŒJ¢‰æžž[û¯r“—{¿÷ÍÒÕÕ]U§þóŸÿœQÃ(‘Hd˜”ž—”›š/Ü9ðV"~ì0~œx-Îüç]¡d¥Üÿ£12##ƒÊ±\…3 ®›BÁHJ,lß7ouVavjrJ®Â."l±ýäÉõ¸zzz*Vþû‰Â7)'59S1‘4ò’ÒWge$eæÎTÌ#o§§§&(’Ó ³Rrñ‰‰I‰Â°Èøô¤4…jzjVÖê<…Ý<{…›‹‹«#ùs›Ÿš±bmŽ"<>3G¡T„%%¯MÏþ['EQï)çf&, ñ)Z5^VR¨ïš• ü²“ú礄䦆®]µ((/-"?=>ò½‚Œ‹íìŽÖNÎ.®nŸ{»{LÙ5uÚtϸ3m)Ê‘²¦¢©PÊ—ò¤œ(j)µ€ò£fPÎÔj!åO¹PïRaTåJM¤Â©@ʲ¥QA”;eGEPÁ”eOERïQS¨IÔbJIM¥–P!”5r ¢¨ùÔJõ´‰ÎU@´Šw)ñ™°š{„WKÞÐx—.C‚h\6!ù…î€Õø\ÅábŒà©Ä„ÿgªuï1£ù ¿«wÀ–¼ƒÄ?}eÚôu+ JRИPÉöÂìfÿÆýDU¨z×Á/Øwð˳P¢3-˜UÁ/lO)7këð~Ÿt[HÏ´‘ ¥ßè+žÀYFÚþ«mÏîMPÊð¿ºyÓ|u¢WT†O¬LzFÃ3r;x‡Ññ1Ÿ¯¹.®ÿ&¿:¹QYíƒX<Þ`ìÕ§kÕùÞÙ-ŸF—L[ï‹XçE`$X\VýØv~…Ïnù :_²#+ÞƒR Óx®Ã¡P'Á¶`À{ô† í¸Kñ5 0‡C#iøÏ4b>ÅâyèA£ËÀƒSLqºÎT‚¹ 4$èA",§†j(V›uj¡S;_k!å;ÍAM·¢‹êβҷ§jË/]³Dš„f§S¬”¿wüä凖¨9¿>éDÒɨ/ýÈi-¾ÏA12—PõæÃGò¿È@ (±$}M~vAææÅä¥@üŒƒÏ\@ÇÖÌ!3ÌÝ›–l‰R>H[›»6'cý2Ä,Q;oÒ.âC‰ôV>pÆ÷ôF’9¼w;V1ðoLüÞ<ø©hîÃaþ‡mݬ±/öÕXƒ=Øö½…9äñO–oóæž_õÆæØt¡—³SXHÁìšJ+&Q-ªà•*ð.7;Ò—ˆÛ‹ùM„–Óñ!:¥9ü¨°µ±nX„gʥد‹`üƒ¦ª»dÒbÿ‡ .âã¹¾k3ñ(á±ñ™îÎóƒ ˜Üzü£ìOGB½VÌ[@U|è 3‰W Žeh8¡'}uZÆD·rèõ4¼L#ÖY3ÜõZZ?Š×J& Óм±^#yK½2WÍ÷«E Z±n¸LðpVadf¥G„ÍK¶AØa£š ­¾W>ÈzÀ½zy"ØÉô¦¨ ©Å™¡!©^dwï: ÁªÆß¸R¸ú˜¼*{_úžv~’ŠÅe!(.öXH¿¹Œ3¹'p„>јӂX°êpÚÁw Ãrß°´DÞŒô.)¹ŸnÎÀ£ðˆù3\Üv)˜^ïÒ "ß-àÑ!:óLÌï†uêú°¡¸.õÙÌKöĬ‰Ž$dæà9?Ž[0ênº‚„L®Lr ŠDË­>Ÿló±ÒKìöî³þwž V}'`ÊV´µt+ ¡CøÔjù}û1ºB'akì„ p+¬ÔtV5ÝwÞ¯ ‚,|€åàˆWÉ< ÀíWl„•Ø×š\œ°³ Qš@|C.NrÁKjþ¶ZÔ«åÇjŽæp‚%H@ù‡ @†Cäø­°âøÛ0‡ ?LÂó±r¶=ž ÿ?þA·~ATzAW4בv\I\pÌß Ë\±„†÷3kµ7¹V'ÇǦ‡RÝ3jØ¥ý¨…Þ~1ÙÌm ékèÂþº“çO¨G÷Y7£OáËZš·2€]Éô6/93r©›|HB>QC‰ZÔGtý=xÊñãÕúñP»¨õۉė«õ>´ <#fúhÈî[ ¡,¤5­‚ÄV {˜A4ž®f¥­GÞy}û”)Ù¾yÛzV¤-õb¥5/ˆÌ6ãÌ6%D>™¤ºÎYó.dšÙŒ´ÿvü’ê@+<ƳØ{J¥ß¹(y]ÜÕ5WÐ=ôíÑú{lƒ|7.ÏË\›¾¼p JB©»s¿*Ø·é‹-ÇÙ©ô.»Ž`ŠTèþágO7컇À”%““‰qA××<[àQ³Ý] JÈUUï2M·¦”sìhýáÄsÌÂkpÂ+ÉçoÔ{póË“7do=$8ös±áC–ç¯Ú‡Æ`ç qÍœß8fûDÌ'Ìâ¨ïþ†1¼Vã×c…Z_"8æÏ,TßI" æM¸Òß®ccP&³”“<Ï&ÂÈ ¹ªàzIeJ½ÊgYRÙÁ<Ùº}îÛRúÓ;°qËBG"{ô“»¯UqçlËgø2ó ªÓpîxËýS™‹¶ËþH˜`v¹¨­›äKq‘«Œn’+…LYUµ±¸\v¨hw6ZÉeLòÊ»s—å¾-ƒcCbß ‰ÇL«Ü7£ßBúæÉ!›éîšW¹”/ FË>+Ë’J:åÐæ#[ûKÁ¨àÔ¬$s¨Os±ÛÌfµbî$¦Â€d—µ$ÛT™8¯lŠc§Ä%ÚÉݤ¬€:5T©ÍÎkùuj¢ôº(žãð5OÈΧ‰|›OvÄcæVÕ/—-½”ÙƒúXø Á Ä]aŽå²ô°ÃOñL䆿å†ÅLó[Š%[°8Æaˆú¥³¦ó{’*'In¶Ïmç'µˆž’ˆüŒ0t L˜Ž'ø|¯WБ§2î–—oÿô¨¬YÿñºÒ÷›¼awð3Ʋ ¶ùåR.ºOÆN"c§ë+ºi]†Á4ë'B¥D8÷ZeÖ  ÒÂÏš­ #Z~/W 6öÝxñ¶‡'íVþ2[AK%ÒÞŸrή ³D‰y ék3óc×/@³PÄ”³Y_prÛâømeqÕ MO’AŒzÐ÷åõ§ëŽßEwP_ø=»*ü^ãhi‡û¡´£W-ÞmüØ»‘ÛdC~$´l(u?ç ‰Ø6fâ§Z]†nÑ€Rá=‡§Bìæ¶G û2»Û ;ˆôðyæ|ÅtÜÄH/(Âý<‚âOß‘ñÌt½ãq7ü׎ƪÖF™´ÀGg>ÔD¤PM¨ ú™ÅEEK>Fh[‘Ì—Ù½õÓÒ2Ä>o¬ý^®3f Þ`ºu7=4:Z-‚/HÈüCgÅÁ&µ¾Õ6XiéAÕî«çbˆ$ôu$– A4ˆ¾jÍ!–¯8ÒØNï…¼i‚R_!ù÷+‚9׉ƒÆ ƒÀýåôã.ðÃ5’ßh’Þ_’´$QÐ8gKS¶NV.zªD©‘ŒÑïbì}Cñðˆ¸²Ã)²ä£E5è Ëïr"ýØîwg˜ ³aØs˜(ãw Œ`ÑœͲÑ;L ¥î»´­ÞAAÎ48ëžÓ¯yçßõÎ’A ûky"²Þ´ Ââ‡6¿vÞ¥Ãìl” úáëF’¶nüm±Ê|ß9Üb´M˜›­\›˜"ËÉÚ±u1û„þì»Újb]%_Ë ”¼â Mذ¸ð£´uó³ÓcP+-p¸úû½æÊË7d;#ä\FûQÙöªÏH>‡­ÞT”›š¾âýhÄ'o¾Rs´·L®ÙûÕŽ£eì_uïXsØIª~;/ÓàUü€Ä–†}¤þÿó•NâÎ9ð9#Èg·P £q3ï MÐ4xZø—^"á%„zZæ/‡^€CøÂNÁ¥f8 ›A˜Ä†yx7Áå’4ŒX°À±’þ¡ZúÁÈ¾ÒÆAæ‘l¥f¤¸r«åöɤ@:„Û[§ƒ„[~ó<â’½_|Þ‚hYúÕøCÈÅ®Y¦d¥™¿——»á)¬Š/êç£ûŒ´qþéæÌv+õ‘²Â¼§¼Å²yK²$Ê¡š‰¸‚ÓÕT!¯©þé;Ž®ØB~64¶Ô÷â|/ÁO%Íq¤iCC(òW¦ñ/a–þ%áÉ3]T¹¨ý±Ö#l±…#슰ÛüŠèÛÚl¢aîà¥köa0ï±X‹S¸W`q‚¸"pSB°-X°‹h ¶i$zD*Sì½Ü±0ì8ùbÛÚÞrQÏ[qÏÎæf!ƒü×åÇle“˜¶“žÉÁˆÄü3Øü@5p—ÃçÁF® ó+]ÒÞ׊/é¢8,fª°¥¤Ž®K ˆ™ûæÿÙ#øQÏ1„“UlÞŸ_¡[BŒ.$Å6H®Ð°A·að<ÑΫT¢s}PÓ'†£d š¹2:6$×aPã7ua)8À$5Éý% [ûSÌw²•M Ž"ÖËàEýdB¨ØXG{§˜Ÿ!"ë_¼&I9ÏÂ^öòÛ9ýv.j!“ŽEùÅxl«È€zø³"Þ³OÌÒÃé‡Ó»®]ÓsñÕåÑ¿\¾ØN´øFΕøÚøÚÅ_"W˜–š´>nëvNTaFâûòœ é…”²ƒXƒ}'¯$pw¸»À›ÃÞð~ÛiÍ/fð#}˜Ä‰KΦ!îüat œ{DüLñº~©7—<¦Ï4žØOVzq3ØúÝ!“½cêºòˆÝ{î^]±ætăô.b·Í«70 N/ñب„u+äG!Z 愬fÚeç[ÈÏBú´‘ÌmÃHŸ6ÐÛ–d¥l!\¿ùé7ߢ.ö5#-»onÓÀü÷ƒ! o©º+ž©Dç{á1r Ñ "`g·b y!¿¤xeœ> 0á_Í>«¬ »®Nfý¯@£]µXê·(kq’|̽úò5ºÎ%~À™Â=m^àà<Ã3¤åGÍÍ[=C”å&xì°Èôà¥4öÂúc»H\Â>§ë ¿ƒ¤á뇣2«é îƒRòg!ý·â‡sÒþöó‡NÝ·±‡Šˆí(w/,:”ü`™LúیĄ^–ØügWP€âç>à:nxÖÉ0 {ÈÀG qþAÑqsçFŸ¿ßÒxþ¡7HÓÜ Ÿ>-4ÜuJèµîî›W‡Î*SÁíQmŸM3Låöcö Hÿ}í™;ZË{pä€WøCüÄk!Ç…#¯Õ$[,Ç£ûœàû‡®|+/ÅsÃ'OD!hy]I ïð«8õígù!S¦FÞxÑwóîca9ìÛ$âa3R|DñýƒõÃh}#‚aDbV I _að‡Ò J?]xüV iÄkÂkÜúÿ ^¸ o ÏÅ ‰Ûé?¥,·’¯ e%­2ì¡úÔÈèé.#cŠú_åà endstream endobj 75 0 obj 4773 endobj 23 0 obj <> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 76 0000000000 65535 f 0000031355 00000 n 0000055445 00000 n 0000031216 00000 n 0000029436 00000 n 0000000015 00000 n 0000003217 00000 n 0000031403 00000 n 0000054610 00000 n 0000052618 00000 n 0000054944 00000 n 0000053044 00000 n 0000031444 00000 n 0000031474 00000 n 0000029596 00000 n 0000003237 00000 n 0000004531 00000 n 0000031515 00000 n 0000031545 00000 n 0000029758 00000 n 0000004552 00000 n 0000006760 00000 n 0000053583 00000 n 0000051664 00000 n 0000031586 00000 n 0000031616 00000 n 0000029920 00000 n 0000006781 00000 n 0000009159 00000 n 0000054253 00000 n 0000052195 00000 n 0000031668 00000 n 0000031698 00000 n 0000030082 00000 n 0000009180 00000 n 0000012641 00000 n 0000031752 00000 n 0000031782 00000 n 0000030244 00000 n 0000012662 00000 n 0000015372 00000 n 0000031825 00000 n 0000031855 00000 n 0000030406 00000 n 0000015393 00000 n 0000017801 00000 n 0000031909 00000 n 0000031939 00000 n 0000030568 00000 n 0000017822 00000 n 0000021217 00000 n 0000031993 00000 n 0000032023 00000 n 0000030730 00000 n 0000021238 00000 n 0000025207 00000 n 0000032077 00000 n 0000032107 00000 n 0000030892 00000 n 0000025228 00000 n 0000028343 00000 n 0000032159 00000 n 0000032189 00000 n 0000031054 00000 n 0000028364 00000 n 0000029416 00000 n 0000032241 00000 n 0000032271 00000 n 0000032303 00000 n 0000040068 00000 n 0000040089 00000 n 0000043184 00000 n 0000043205 00000 n 0000046763 00000 n 0000046784 00000 n 0000051643 00000 n trailer << /Size 76 /Root 1 0 R /Info 2 0 R /ID [(Im{ïy“\)YûúªÃïL)(Im{ïy“\)YûúªÃïL)] >> startxref 55647 %%EOF simh-3.8.1/DOCS/vax_doc.pdf0000644000175000017500000034476211143604524013527 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœ­[]wܶ}ׯØÓ—p{² ~€ì›l«NÇv%ÙIŽÓY’夶¥ØRÚô× Ìy¹¤œÄ9>0 ˜;_À쯛"WzS¸?¡qþþà«c³¹útà»7LJÆÇ«ƒ_Ú¼tÿùlŸ¿ß<8µ».ò¢Úœ¾9(ò®kUÑúïj£´vL¡rûùýÁ«ìåVåEÓinweÞ骩²bçfk'è¶É~ʶ;wM­MöÝvgéªR—uösp¾Ý©¼«´j³þ{Ý•¦Ì®}»lc—2–ª:¬TvŠ­T ¡Nš6«¼¨Î~ÚöŸ é[!x CqCq¡²‹O£³Ð¸‚Ù—=ý²jìäþã@é:oM»ÙéÒ‹éÂÊö§†f¶ÛÚïeÕVÙ#YëRž÷«Ù%v·&ÄÛm]7Ž UÛI&E7 ¤lª¼ª6±àjL,ß§¿ØJmºd†ÑÔÆRêºzÓØ©­LQ*¯õæô‰ûç~É0_y>98ýë«ì¡ã^·Æ¨ìÙv×ävE=w»oºªÊ~Ü(t¡³cúͶÈ˺iŠ2{,³¾–§v×MnTãÀÇ>í´U3ŽºeìÑÀd˜†ÝLSÅmÆM»½á·–°Qº2V†¡Œ½±#•í®:‹½ðý¡QeWAëÚ´ØÊþ³írÓ–Æ©áÎØ.SëìCœ|å[¥.’u¬¢šH£-³iþ¾U&Wyð¶eÅÛZÊa] ÷Vš·Â6,òA\Ó±À°gè’òz&½7،˩A õÉÑ‚·Ò¼¯X6uƒ,Â&î  St ¿³Í¢1¦µP±`T•c´P£ueíHeUÆ+½ãλì›;Ùž•Cm(ÝMNéKYö÷Zô‚s8ø á@Aƃܭú$TãFaöBÈTòç²Ó;é}oõËš(S+ÜÙ @!®½²Ék$‡þ­—¬ýÀ.ˆ`×[ ‹ÁR•SïÚw;íuâc‚+„[\úøY L÷(âÅÞn#±ùškChy x+c.Q€lÜ û®|oÕ8÷¿C³ôM¥´wKbZØŒ|µ3ÐŽëöÆ®²­‰0œ@qV@õõÄúYiÃ÷Áá4]kEåU¸´ú]”A…_Ó“o·LOÑJP‹s{ÿÛ~·þÑÄËàcÅ»XÊ6©J»åß·ºËµ*ë9ÛyHl?Û› «åâ´Ðã…O'+õHÒM¡’…ÏFRlf¹KÌ.˜Î’Œ"K%GœØ˜e¢ßÀ 1²:/DI¸B ‰€©`BK3Öyÿ×Ï5pM™Ò8¾pGL\ÊŽ¦ú¤ñ´®èü3z8 Ñ—Ñæ\, ¨„C7­ÐÍL §FÆzp`pgt;Wô¼¾¤tçî{û¢ÎUc´Z‘ÔÍœUª\Ø^V3‘ב[Êâ…u©¥šöñÐ|º£°Xå§Þ‡®Íî‡[!~l~Zua4¹ÚqÚ îá+ Ù4¡ãšN}Êlp7M‘ë5Ùeq&¦â÷ þ¸¯bñgŸ”—m3ë°ÈÅ¢¿úã7m‹©äœ\—,n’Q-v?¹˜Qðxz!êõd_=dF‡ìn½At°&Äsçq]*qŸs¯”øÊˆž¸ûq® «ÜQ‚ý‰ºTxÞ® j¬iæñû"tø-.×uÀ½ ŒM,[lþB"u0 ƒßÖMÞ”†hûân&ÑJ£êjFö“g˜ÁˆM² ð´¢öës]î³¹ÒSå\ÈŽàÐÙ%x–¢vý-øý^¬Î¨0ßæ_vÎGyé4×3>vÓÏPsžt²O§r‹éwφIZ¾Tøƒ†hAsæE„É`ÅéôŠ×æ¥zwNBó0ná©tÍãü¡, 3ymâ½!3œXB?T Ñ8ò[V¢“ <ï8Cû"òüADŸ $¼®ÉÈyÂ@[”qô/n®-Ìž Ý'¢‡°×|/ž à 6¨‚^H/€:=ã@á±ì $ó JæÅÈ´di„V>–Ÿ>RB78ŠI.@bC‡>ÃýPåê1m‰QÃ(5(©X«ðùój,p?”’9ÝÀ{¶#f  ²¾–ÞÃ8J΀gùþ`lA÷HÒ°ß´¦ _ÌáöÇeþtd£‡S›ÕTÖÕ©Òö !v Ør*˾·Ï MÓñ^ƒ3ð<¢úE‚«`þžG6`/ÞÁ1Ö=²m²ŸyåáÖT†#¤žÒŠGÇÔ’¸^8û㥱`uŽD¤)–c7GhNaÅC‡§ÔjóbË ÓÖ—é©s=ŸÜʹ¡Pe'°|@“ökz.R@qf ?ˆ]Î6œÞ·°\é²\p_áY›3÷Dïü~úAþÎa‡¯Pi³gžÖ3×›AàŸxÒ˜IÉü‰¿êaÊ¡¥ðÂV”µ™Ê”IDÂÃ0ò˜ºAâ{†±G̰-úgoÂDʉˆ'Ÿ„}‰€ 58YÊe Têûì‚:K©;«¦K¹×I<\rpú|ÙeïÞ34SÇm1ðrŒ¨ øyH‡Rið(ê8Þ!sK³?ÛÖ–~ísÁ¸ë±8‡¨%,Àg€ƒ¥ -]Pž¤i<ó Q1‹YRÔÒk¸±>}öÙ1Ièlø^`@ðøm=þJèŠ6üâc(Ü xãølÚö*IvïËûIû¿Ï_WŒcªÉuEi ÚR@c°9µ?˧¤'ÑïO—ÒmµÞÌ-Ë <¡iÖƒÖ:Æ4ž™Õ§ #37(,ÿçÞÒÁA<0ð‹¸žÞWõŠû*’X ¾&7kY¨•1ÿ•{ÏsiÂõÜNòGÎQñœ£P³’}¸®de.ÓzÈQ…Îb}Ÿ¶ø~=yV\|çBæJÌü­‡£áå!÷yÄOjóá)júhñçtÁ´¿ {òºâq„JÀ"\‚ÉKÊ‚DæêŒ–ì!l愼E ÉA¿=7á"¶~£ºÉÅ24W Rµ–>ðj~ŠÌð›V 7.µYQŠ9zûsvØäÕ R^Çáò™Õ€€éÏŠ’¥E0%‡~WÖ1/X!VmØ3æÓZN!ùAUä†í‘ûŠîké=òs+Ìïñ4µXn°ÊËc‡ÃG—ý§ÖDzgmZ;§¢¤¸†þ(é–â=©†ãϸ°æ¿'­ãs;ô?›zÙ¾´þgea¯«ÏYüåÊõ¤ž¾VkœàBEÁ=–Ökm™ëXG—¸Âéwð„E7„qI%JÝåe5)D9:=ø§ýóîY=óendstream endobj 6 0 obj 3187 endobj 15 0 obj <> stream xœí[wÛ6Çßõ)xúDžS!¸_¤»M“¶‰¬ö¤Ýîƒ7vÜncÇÍ¥§ýöIJ#“–e’¿L(¤ àÇ™Áà%Œg´ükŒ7W“G3“]~œT‡³Ù¿ƇËÉKDù¯:í7WÙã¹/ÈXæˆÓÙüí„ç,5u­,ÓÊÿß©Ì0JŒ?áj’gÅüÿuíPÂ-ÐÎ)ËÿÉY1U„æ¤/žù¡àDhGEy¥æóSßPÚ(¥Á¥b¥±üGš®ENbÇejeÚTVµþwþÍ„sJhÙñæç¾ÓìîfÙ\ièƒ3˞à #ÚÚA»Žd2“1NŒT¹¨¹&˜ñ4,Ûi*#†ÚlʱucqÎ?F8ŸDóu¡ˆZ›@WgZt$=‹&@õg ÕmþrÏ_©ÂwJˆ‡â»c¡´&F‰‘Ð#€$4h題°X®²Øj"Ù:7_Ê–Õ>¯¬|Z&„?ÅæO J4gŽ©üe hfuþCu”+n ÿ P|Ís~ï¬[alã”—uý]8B57¼„²wò•–þæË¶Û®û4Þ 8÷ÏPoôÅßÔWP,zãeK•OßqüwO¤+§`V$öÏH«·>šFXíÑÃàî0Zâ­ãN­àNr?عêƒ;p´â[f ¢Ê£sÁÀŠ/-NuDÙØì xr)˜éöåNÞ1M˜9RÏs¤ò}ºÎ]BÝm|“Ä:¾!ß$Æ·šŠ;[Íš|ðƒûgqÞH},¦9ð¤ÂCO°ˆ7ŠÎ¦\ÒõÝUÈEÈÇE à õÄ cî (t ædA½V覀»~Nöñ¬3Ärîï¦é˜ì„#Fl_¯?Ân?a×»õ{"Ð;3¼—³·0î‹RïËÀï M×L®ÁÝöp¨Tt"+9‰^P%vØ…’4·£Il°Ÿ¹aû&’Ü»j}%"(ÕP Ü‹x&XMñc4YÀCcTPÄ^SOþKÅp¥æ§àöTÐù3_ĸöe˜ÛðóÌ+Ï<ЉÖÝmD^’È;|¢m&zk¼/((ÎÜ–ÉÔ―hï°å 0×ö{çŒ,¨ ìyXé©)ã›TËÁ;ÜqpmÄV’…ô¸ÆV¹&4qLvsMb“ Oã2ÿH£khRn^E\ Xuzæ0¨àF qnØõYû†…ÑÜÒÊ×û›ƒÎüÞÊe­{ø›²÷ÔÈlÅo+ µ"èp\ ÌèÎ ï{V,ÐhÆüKX»,…×jJ­¸¬•¬ï,,ƒýÐΟâ/ŽU KýÍG¯ô2”sÈ@ö Ôƒ±üº*—Âhà W«>Xµæy‡0v”p1l}Ô0N'Mר 'è4×a!(w¼kB“Æ¥ @á‹o™‚ëGºN0^ŸÄ5Zïã(½Ž&Fp滇h¢„"ˆžy-.B«}Oe°3‡ {]™ бU ¢´Ç•Êî"÷_,€dIb]ï>°·±}*ƒ”¯>ó;٠E³æ3l@‚G>‡oü‘T@QGaÍ–ÆhÆé5Ñ•I[}DÿÔÞHƒ9ß•ÙÞveðè7š˜­°ÜãGt f*’‹8ÌùkÝêíÓ§RÛò÷ê&‘²B Gî6Ñ–É VÁ#ÞÐ|«æÀ#¯Þ yîŽaH ·³Gæ 9¡TŒìœæ›„òYµ‰Fös Ù§°8@©Ö)ªÅ ûª#øÓqeÂuŒ¬.Ðí·z7½Á¿Å-j!ÂꆲŠ´â¶ÓX Ò´ƒ:ŒÓ%¯Qb]ÚšÜ ÄV«‡}ïÀHÔÔÓô’ÍÓ¥@ ©‹ç4  éðÒ”îN5•n%GÂÄÍuâæÔ¼¦µàp°r°I+É8[ÿì±u…ÆBHðþ5Š:'üE\טÄzèû¹ºv|]³GÅÓ0Ö½’ÑŸéL|M³± ”ïCé˜ã:Æý5g#²#bßuŒºwŒœ˜ŸA¸Õ¥¡nÔƒ,оÙò«+ϧ­-Þ„éxÔEú+гÛoZ³?ƒ˜¯ Ó”!N ?2pÌïâÒ넯;\5æ4&hîIGŒÙGhDpô52ï+‹¸[Ë¢FRÞfÑbþû°¨¡™£ìÌûÁì—øu{Î> stream xœÅZÛrÛ6}×Wð­d§„qùØ4n¦´3u”NfšNÆ‘'­mùš¦ýú.xÁ.)È¢Dʱ2ɰ{9»„r“p&dÂý§—³£—œßͪáääE#ÜžÏnfSþ§ òâ2y6‡…B$%+m2ÿ0ã¬, îê]Eb ü^šÄ ÎL¸œý‘γ\Xæ„•éÇ †…Ô.ý”iT©Ó»,wŒsamšdB2- ‘^f¹ÔŒ;åÒ¥t…*éà*lt›åÆ/—ezZ *É]z…âŠ(’½UÆZ®èÜŠ @k+ÚE°éV¡¶EzÛù£ÑíÉ£¸ýÅL%ü>eú´0YڤǙbÜ–Z§?à ™ú{–—`eÒïýLÇK‘¾1ªúmŠvú†¹u€[4~ð†\Ô~`hbSb¯4À_°y~v¶Ñ àOªpf‰"Ïþœÿ<“¶`…* DægŸ¿ÍPWÒµ\¶†7‚zò"<'þ'–&' çbõŨõó£7«‘(<™ÿá^&Ât½ìE»uœÉ:ظ…ic¬?Nn$gFé«,W0ªtÇÉã[88¼ŸêD…eQÀ‰ê)ª°p&oê~àLLóÆ)ÍÜ[NÂI¼¾°ìÇ,‡”“B}~”n¼D w­ŽÖC2ÜÍT•áµ™«½œÍ¿í¤¤`‚Û–>ÈW½€UüâTú,‘ï-´¯^J?NéÜ çônóAøgµc0?B)¶Ò>'%ì^5hÏ‘X‰x‚â&Ž °¦àØ5‹»"Ʊ0JèáÅMÛá&:üVºŽMÍEÅ{RÒꌬLr£ ÌZŠë)7¸«gBjݰ'k _¡¾µFþù¦j²È‹‚"‡;ªß¦*óBà”ZQ/*¥¡­_¼êwâKÖOJiƒf¯”¬ïoA"³REüճϊZ"BK„¶)K»Ì|@DëÖ&cý-&ЍNˆ—{"kßk£Uc5Ì)W‚ÆY+:ÕèçÌxö3æ~éæ"(ýûÀA€Éu4"žƧÌwe…ÑÝ]ãr4 rGÔRzÕ¿M›c[`Œž8Ð'dÈ5Œ_ &B´1Mm‹á¤‹”¤<–¸£YbjíÊkúwè²Â&UE‚îZ¾ò .?¢\®¤ D,cÄ“ÒN»Ñ")0¢?Ù `í¾½ß+ è †#ÜžƒLµè†G_í>)0\ïV‹&ëáX<ôïdžZ«x’c“h‹¿N¯;œpç4Q¸Çßkc˜$öûß•ÝGïèùþÝ:áËø¥]ôåá¨D Ó¤Æï¶òÑ¡töùék`ˆ—±Ï㛦1°Ú±ØO?®%Ó>­~á…×O8&"ÒQ“cFzCH®Jæ$å=ødTDI¹Ïâ¤IŠw!ño'+ïN28¿è\ôGK¬¹óuVƒ¯ñ@I1¼éñÖݘ·§Âx¨UÞÜP<Í÷¹â™)»ïi £¤‘èŒÿ¯†8%îÐ?OŸrûQóx$¿ÿY÷F‘Ž^ÂÒ2Ä9ä’~\Íš•,¡ŒA md»7©©n†9îx>û >ÿ²§èÖendstream endobj 21 0 obj 1529 endobj 25 0 obj <> stream xœí\[sܶ~ׯط.;!E‚ dÚÎX–S»‘c[‘]'m§£ËJV-í®u‹ý?úƒ ð‚s@~$—j\;Åy8ÂârpÎwnX`?Ìâ(³ØþkˆãË­íýbvv½U6Ïöÿ\Wg[¶T”ÚÿÊN_ÎvÌÀ$™éH糃ӭ8ÒZÅE5k2Ë¥ù[ËY‘ÄQa:\ným> Â4ÍÒ(Ë8¹L$q1?!’µ&A˜$Q*UjH×úO"oÊE’éùj ™Ê<ÓùqQ'Ò.üƒ¿l™îRÌö¶~ÿ9Ùr \ú |¸Ö»@FqK9·±t²Ï-œV"Sµeþþ]ÉÕ£ â\NªåEf©–Ôóó ³ÓꌫàÜΚÊBŠùÊYº1‡®ížÑèS‚Í~AS.h'¥®ÙÐCúè*¥Ý™Ð|À·Uß4Nêýºm^×’Ès¶êeŠÌ(4-æÛ´J©ä\«„íä# ßæk„©(’ÈïÍ®´™JdœËœH É-4ÑGÄf#Z¥íŠ"‰ã(VC BèH5×ë°äxsÈZ@2Ñ7¢’‰é›ˆ¨PF*¬qå©&)¬(ÔüS t$a™m”ζ¸‚$óAŒ}³V$”ÑËü…é L‡"?7â¼0Ü$¼ó1IawVíWä>P¶÷õ,‘žÖ-'l”©Ê ÃF”I™—j‘"Žd¢æo™GÊlùQ ÌÇIªæo¦LÇ4+•¦"³¬,æßT’h¥ 7n¦CÛÁ´™Ö›Àk„ßR×++†BüñQ× )êílOd‘F*³{1ƒþUšÉ™(7’1½sà_8ЉøóäAÌFµ{,xÏ1Ó»%µÃ8$1û(k[‹hû 3Þ!žÙôHgôù-\µÏdÜ\Ì´™a+®Ðî‰bÿhkv™n$jáÝñxMÄ©-”ƒAš[5ÉYhòáá‹LƒëBêÎQçŽ:ýfŽZ:êÐQ—`Äß玼¦Æ &E^ O%YTê4Lck@®o¢Ó†#K^yK䑇DÞ¹ ÒÍ+¸0ò32«CöcÇæKG½öù6+œ_F~ïF=rTî( ¨o‰d­3"ù’È×°ï/PˆL0ïà°„ÈœÈçDîCj•p1<ƒ·ðŠÈSØé[Ù WD~j+½özµø÷µ3AÍ7nÔ02Bý(NÏáF–PGÐN!¹Rón²ºïX}á¨çDB‚X‘œÐL¡kÄûd’úwÿkÁ3üWoµ?aûËö®¼Mß¡íOAÇÜþç‘CÇ«Ž™‡ÛD<4þÈQ·ÀL(Æ09œ@é¬ÇÜ=‚;)üG·ÒOŽ¢¶]ooy\DI™ ¡½÷ŸÀŽÈU]0]Ìà6Ù.î ŽaßN€ó}QÏõ ø”¨žDfÒ|å‰ò„.ì¹èÓQøŽ(ÓC2“ó $m‰pPS°§(ð½·ãÿÆå] ëÇ&-–ÉäìȳcÖáý ìw+?û;í¦aŒìŽâ~2c>#†B¸¬Åÿ%:úü'N4±×°/còã댇&sn1±“É vÈØÄÁENŸÞ OEê_9:PuÔJáÑ5 ª>[ú`²8¹‚"ĉÄPž`·J ÁÓƒ±ߨcÞýk( <Ãæ)¨ö|A j¿; B8j›øÙ'rȘHVlr£`…'Aº/¢bˆÝŒ “élL3'pvÎñ~ŒI\+a&qaÝv0ÕáA®´štxpZb&UYTdöð±ÆùzôÞúzG<ÜKÛâÚöS·TÝÄ_S©B¹üpqƒcóX¯ˆÜ%’ò¥p†ÌLÂLÛIõa¦ôL…ŠŠ´rLÕW aÝ4ì'(‡™v8øÎQt²NE#Å?àç‹úœfáJƒ>'S9Ê‹‡bÄ&ˆ9ކNÇ1d=8Ÿÿ瓵}Os*9-G¡|„rlʬa±q %ÏIJ†$>›ÿm&º“ñsgÀåõ¯åqü³Üæ¨zxtGÕ¢¬;€á ‘¬“PìÄ„•Z¬fÂ…->G9†KÜÀ%ÖpÞß솙ôö °{U°’ªáiJ!ÕÅ¿.§/Äcä¤ ï¢Px_–NjZð(¬'°ÇŸµÊ§8œTÑ?qÔ^g&~«ƒ¹H–`=!’e`?ùjâÉ€øt9 Ù/„2^˜wõñe s¬$vªÈôÅ”ô æq5{wCºu&ìEy—íz÷Æ7¿vø¸ºZIs#Ô’ßPÖw—:ü„Y”gY®zú¾¡«‹O«a*“=}÷èNÞKw÷‘mÚ§i÷èΟ××] d—]  rù,ôø¼¬ïŽclÊ(#]ãÍÎF†µÛ¥ÖÞaͼ?YÆŠX'=[oä˜ Ï©«–:ÊrŠ n8“}Ÿ¸ ú2¡’òŠê¸LÙÕMï¾4ºÝžqÇqÍ:á«Öwt³¾S¯ùÕåÖMøº_sƳ¾ìÚõv¸† 7—± óEQDZ•‘³›¥a­™²k^©‡B(Ý.¡zƒ'] ¢KAw7Èã¸ãT2ÅZ "¨ÃûšHµ_)úMdØ;4XטHÿ0ºOÝçÐÅb|›*:GßU°Å™T—¥­ƒ¾æ@tl0È]¸® úÛÓ‘Ù~jŒUóOAboZÅrÜ”N¡xn+¿‘Yà¸Ô“xa7Ë£ö=êPhƒ2Ù \_á5ùö© ×äÙk´ñ¥ÍÍ,ßÍM—ö}÷òï–ãã3. •b“+. „Ä\ù(Øãaá­¡IyK*|Å‘ m ¾:#·ï¯ÎêZÏ l#~™Ð¹èŸšÿ@ëe])^á“®*ò,…æÙ ›¹2öœÍó.Ž)ö “ièÉM{ü¬-¢êz"„QÄÈ[®OVÍ+®ªƒŠ‹F"rLõ̓¯FÖ-`1?£ù/F¼q58ÙC; §ˆl|ùÈeŠ#°·$𿣶=µžý•Füó‰4ðJù»Äo©SL­r]Ù‚´û–qU¦¸VÑ6´Rî+'wÌ|J»³×:Êñ¸>^엞壼5ü¨-àÑhÖžö¸åùÖÔçèýÎëX8Äï”ØkÍ¥sÛgN´^T¨sPìÔ§Yšã [Ú(2ñ›FœãÕ¸s-ÝtªM2‘ønš= ›ãž“Ê>Æüª“öCÖvP„µã×õ‡š)†kTØ/)#I ʘc\;uü¢sØÍ#háðËÆŽÛËÿÛf ÷•µáeÙ«Voª*ÔÛ—­®]sf=í{ õ@VÏÀЧ¸)öΘµ2AT¯ÓXðèÍöñ j}¯ÍçFálì %ûœG»‡,4âÂk8̆²æÝ1•v¬£ÆíÀ(8ÓZÎÿ„™ŒT{öÖ—”ô泡1qšÞ¯~BÄz1ÛüŽ´º±/ìæ€îÐM©ÝD¡:¥¾ˆ„•+½©fj{\½[ãÊò œãS ´âÌ›²ƒå˜SNhJmá4pÍæÌ…9ɢ氱©õ•Ucç¬Æ:VŠaÌšýrÞÚƒb?4fûc΃ŸVàöعÃc§—uœN#%]œ>ƒb®V-+«Q“-ù𡛇„ÑðÓù5‡j“h…E²_Mpê§Ï±+õÎ ]z=+QVÖ†g‚M%®4—MÊX’>¨2ûCÕõ6ëÁsÃâkí¤²“yÇŽaI—áS+„0·ûø=‘ì¸å¶ò5©æÖŠq?jO^£$©¼s’æýÖGÚ|ÆâùÈ•s(]Hõ}5q€£3bßÏwtÎ, ÕúùŽæ4’ÿ|>ë•2[®÷Ê©¨HhMÉk¥')aëÜ,÷©Ætˆè¹M–Õ /?ùÒçý~XE~wÂ|W@(Ð'‰Šdæ¶kf‘FkiÆ`€]¾xic½•Š÷ÆÊÐ÷68Š4`N=rÀ_:’Êœd&ºß›”Ÿã̹龜0Uö'~Ê“àj áÝÿÙ#rBžÙ6KGu*‘êg˜oãiÿ‡œžl½2ÿþ¾\žqendstream endobj 26 0 obj 3162 endobj 32 0 obj <> stream xœí\[sÇ~ׯØJ¥*»)k˜î¹ôŒ«ò†l°‰N\8²$¶´’uÁàTþ{ºçÒçëžovv% p㇣پžû9}º™¥‰Ò³Ôýëý“­;;fvt±Õ|ží|ÙçG[¿lUIæþk> ¼2»·k;fÅLåIšÏv_n¥I]Wi7 Ô¬,ìßu13©iœl½˜ëÅvš”ª4y>OÛyRÔišÏÕÂ$u^VóÙb;«j Ï¿X褬R­çO]Ÿ:«M1î?ΤóŒ¹\dIšf:›xš>“¡ÞA¯ €/›&&MËb~ßO¥ý\nêÌB™®ç÷›å(m‚–o~-sïÃçC2y‘vèï~e±ZÏ”NêRZeRKc±¤©ZëJ;¤¦I¦T®VG¿×IeGQ7º¬“,/-·UR¦µVÆóÒØ=>—ÏýꔚÕÁꌎWWæ1nu¶ÓîO[J%…]ìã­Ý¿¾péDWƸ‰,‘ÊÚbá¹|œ-ìJ‹²L³ù©\Ù?êù™ýh”Î%ÿÙb×XØX¢œúß—ºp?§©*K·O;Qym;åÈê¼k™éÔ8â˜ù±ÿ媛·r|ä k¼”¡_Mµu+ÂUÿæþn›¶Ü;÷ý_ÊÐàÄ"Ì žÉŒß–nn¶°Ü”ëJa§C÷ÑTÊø–`Ös;«ÅžÑ•]'Ziœ~O03l„!ÇM:«ì*,óìXNæÎY>ËrD+ìúrˆ»h»¤m7A7t›¤òÖ—˺šßµ¢6,­víà¥ý£ ÙRM˜·iÚq¯ÎjœÜâ¢pð^ꎘ- oP™‡ôÿ eÁL@½6¡ôû©ÜCÕ‡ ”þØ(• X­G©[&h:!Õ§,J è€VÎã$:oT¦>Bq·¦P½/õ'¤*ˆÄ}zDJTæ>AJ1‘ûôh„â2÷1šŒ÷h@ªÜGt×Có¶$UÚFFôgŠC È’68¦˜}(à] ¸K»Ó)€­®¦VvAÇ…NiÎV'tbØñÞÔç «ä©‹»°¼ ·ûoÛµñú­q‘´ûÖCßüa¸Hÿ1¸èGÚà”ŽÝvü–êžÙ‹^¸$™oâ”búPÐKr‘À¡Ú%·=ëÙGÈÀ¼ò™ÂCAVfƒ<¥¤Ý Ãv&IÉ,BÆ(HÆM¤ªšÄa~M/BzêTr=¿.j‹›çA¯¯]55;©Ë9ôÞY)f©þÖþäáõн«ò ÒâuÇ‚Â΢ëÍm×9–,œQ0¤ž¸'~€˜Àéò ”|-|Ò9s–Aî¹=š´üÖKºGhcå²ó'ökZX+—o­C>†’’ƽ*ío©&µ.=ûjÝót•åôYUN"Í(¸ ‚#Ž,å*æÆ“Ãy޲AêSN„<¿ß‡%V¼•q5î÷7)ˆO×| µ²­KgêÌžó#ª2VÝtYÁ¨1òT#o2MDƒ±.ŠbªºÂÐËÓc)"`òK!ÜÕâÚú÷¸Ùs+A ±—Sø\6 cë;y³fIñÉ,7ggcÑÃò%%ÃÔõ ˜!0Sá i]ÚÕl j©, "ÆÎ#,˜@Y|†æÑƒÐvŒiÆLi—jí$’œÒSîw~7™žŠ´Å`÷Ê4Ðä¨+‚Ûõ#ü`Gý*Ú>EÞ_†t§Í0z‚j’ þ}ªð/© ÔÝ0øÞÜi])LTö[%`™ÙxÀ =æÔø} rWÜòÜÞ©c˺ÎΛ§¹hùìðVÍ4l] ´ŠÖ70Ê×ÎWܾŒõ¤=`T_Ä{t|'aÌ->óÒX5ij™:£my-eóI ¥ïº+½*Ñ©¯¨ ØŒÙJàø¡ÿ¦ƒ$&unoìÔÃ×gîç{O !Ê—’vxà?ò€:({aÆ’c?¾¹Üw@„Ò¯¿½Yâ*0¬*K”Q\7ÖixâXfAÖDúþ©pøC9[üÞ§±žyœBÊ1æcJ¤§p&ÓC_ºâçTd-}º‰üBFÝXÕ™1ØÍ0@dP­Jfp¿*`}ßMŠPA|!̶ƒ|ç»­âÝ7c¼«jèô°•ec——VRkå/¯ruCÖÿ®Ê4)/Ý[rV¹]’*’æá Êpa_ñmyÇH-Ç÷ÑѸ´ä¼¹ƒ…^@ê„F¢ÐmEA¸ìÆLì憜 SjiÎ7¶k›Ø`žð{‹Ùw~…%Í1qfxB›ò0™ÛïI' TpPyØËýàÀÛ9Ðc•‡íñ_ä%IÏB#t:¢›áÑÛ{ô×pËý åÞr}‘JöÈ(º‘ï+E:Y&1齎© -–¹ yç%öPWÕ ¯ôY˜V.|xµ:)Db÷˜q»a›ž¨¬áÒ6—––šþ¼ŠÒû-ü1p-Dz€Cy ”WgP×盫bPTƒš‘ñäkFKqKAúy.ã¸ÁW®›—¶Èì¦I ŽPYŒ¤K</¢ÓØOÚöÆOIk±¾EdÜû#W¨ŠÜ•A%^8øßÅ{Xƒ_<¸2©=Y'v*“Õ‹¼àA†|¤,!H\3i:e÷¹&E 0E±ÂƒªÕʯ×\<È4ŽG‡˜ëé†<6×SH­°‹ÃÑ·q&)n°Æiã˜àÇ×Ý^¬q´3.YAÄÀI´h0§´Þöš‚0°¬nØ)Ÿ…Ç×?®ÂýàNB¯±ÚvnØm–»™4Eüô딂 “ÎZì‘t+üY’嵜 ùl.ß5îȆ ýô±K`…w¨Rçέ¸å™²5.‘B„Cr·Œkk †^6ìfª:3å»~JtK£<Ö^#u7ð(3âä7å³íWw 3U Í%n“r1šº…Ý\Å+ŒE‹–àx4ù&/”,Îs±²Pþ 1fç1éÊÝ n#nëüÕþ”'È\iSuÁdÛ¹ }@dÆW–EO´„5?ˆ3ùµ.Œ¿1âvŸa‡÷䯅péNÄÙãºÔd?f•C'X,ÖÚÞ<î}9>vÉrD`ƒgûÐqæläà‹k¼†—*+ÿµ -D¯û†WP©,L®NFRÜEà×Ð(°ß7óå;5Í‚ô7+<ÝXÑ“ü„äxâ.RÿPPùíÝŸÄ9ŠLöèéî„Ûm'ì'}»5W׿ÚÑk’ç"ü+Õ㊓¨É„Ò5c¶Y·éRòɵ±´Gå"•#ÅtIÊs a€¥€¡é…)^^ Hï»y8†ã˜©Ûð-8, °-V?rOl<­ 7vRAT VÒ ½¦âÊ wÆ lqå•,'V4½Ä<Œ"œSÀ*Á 26¹± î2y0¸ËdfUtõ–' ÚÛ‹ÊUè÷"¸ÚáIå°„$SE¢óÁÃÒîº0ð‘Ü÷ÅL;Iò±WFV)³+Âz> stream xœí[[sÛ¶~ׯà[Å3C‚à-휙ÄM;éI[×Q2iû [ò¥‘,×–’¸¿þ"‰]’J–e'MÓ‡5 .€ow¿]\ô·ã{p|ý¯Nf½'G‰svÓ[=vŽ~(„ë³Þß½Ô õ«\>™9χêà p2/‹áiÏ÷²,õ“\kàÄ‘ú;‹œ$ð=é g½ßûCwÄ^Ä¢îúJ2éOŒä()ŒâØûî@x"M’ 膞gRößÐCÖôÆ$žïqÜ_UWª§@µHC%ꇡð“þœÄkwé¯DÖ_TuqÝìÕ9iœÇIúçðG…„H xi+†c5íçî ô¢$”ý_¸D^,e¦P‰}?×ÁÐÒSS2:Ø Nô ÙTfJ•Ps‚‹#jpIâ˜ÝhìÐVO> Ôh/\Y`\/ÂlCÈLƒ9ï×´*1ódèGý[7H¼@*‰à­vˆÄÏ‚þ3#ýæ*£îßÓˆi'ôrÁlÔV:CVuQ5#?Q]°5p ø³úLêÞ$ÿŒ gâj·aìÅóƒTÑ4ôƒ"ÞV FPWeÂFûlÝá6`–­?’¢“Rsˆ |4ażÁiÑo’ò Ûƒ4QŠÖ Œ…‚+sT¤ä€Ö˜©ÆÓ°§ŒIF þÉš~’s^ùú âÇ¢9W*჌S‰ElHÎ*+²˜†ŒS>:fÇc]nÇ0“:jõ,‰{¸¨ó†~8é4£Sú´‹…­í3*ÝÅíeµwÓÀ<æA%"/Š¢2¨¬Ì0¡®ÝD³Rj:V4®0Œo.!¦ ó-‡÷ŹÕCɯ˜BÙÙ&=Tøÿ’ÄuÖéæo]±S3³aGÓeãþ q–âïY†`ÞñÁͼ$•fZ+Oc~²„uÅUá2Reý°tLl ¨¶ÊP2RmÒÔÑ]EI1À©Ê™²è+%ñ[S9±Â+0_û@bR]Vš"LšN¦»ÄNF£ ô>yáñk¼ªy¨Çe5YQ‡Œ>Ï`‡?)Q!#¢$Ầ뉋Q®˜ÎHU”/bÞô¬%=ÕË^í´8…V’Mžü…ªÓÒéç-ê¦Ä9vÕïkSHM¸-QŒW‚EY`Yêí?\züuK-‚)Õa¬™ÃA4syT{.¯OÎF0…‹dNBu6ëÿU‡[réÊJA ¸Þ6«¯ÜF–Y"K·a¬T)q+¬$u).*¬Ä*¢+VÜ”Ò ½¦(RL&BE²qX–÷Áz‹W &Ö’3bûKMx±*•¿ŽXq#K}¬àtié%©Œ«²‡µ¯¸> ž|U3Ž‚9a ̲Ë‚Fîl]2Ì  ¼ØOÁòõ…^ªaÒ¤^*«Ì2:ØE­¢W6¹CÊÉ=Õ›á_=õ§t†¯zÃÿX÷˜7ÃM ö>¯Â$ÂVbŽ—$õÂS<ŽvÜ Ø@‰Í²ë?n¤]Ô\˘G?ó´Õl}ÑÂÁ8«3µõàF&%Új;þÐ’ÉÛŠe3vìðEÅ&¥'àZ±ÅjYÊiô¹þEÉó˜nà~ Fé€ù‡& ¿¡Üš2Ø&n t¡ZYT±(èÖ™'Tjxš“¼úßÄL1ÿzÌÔ6³¼´ 5/å*¢d`¶²Ž¤§ªÙä([2qbš~4ÒÈH3#]é2—‚,,?ÖâǰÁ‰soH¼ qA¢Câ1‰·°í貜t f>KÌ>@ ×°c„™XïhÓÏ´)ÔÀ†s¶#€[qwÇu Ð\€gŽ‘ØàFpœ    ˆ«•1Ëœ@ 38œi+kÏ?qÌæ¨á`AǪçŸ8<çÐ¥>Ú:Þ›ûͺã»+*dèèýšõœÅHeO½ÜêÞ’øŒÄßl–cÝ1#2’e~²\ïî½xF[ä««ýiùêÄïÀSQ ŸPlñ¤8´ûŽ’ý>Úæy G9ƒ#÷øøÅÊg¤ò³&~`ÿäƒ~\6½øús¢š#H5Ì_BŠHô¡Þ öhì7c¨—=Å.ožvNï»Û³a§Jj&k“‹°½†ÓÄœ‚³éئìŠX/nðFÐ5ïêeî÷5ìâj¸¶ÍíÒ†Ù:RŠÓ,-•É4Þ}ñõ¾!vëïàÐY˜§PY÷,Õ™+ÈŒ_üa\×xU±„_ÀñâÜ:#®Xë0›0ÈÍÙ«ÇÜ@ WpB×¶‘1ex{É!éÀ6µŠ7ß[îZv.¿8Ò^i Ç€½çNI§rË5ú²"õò«^q ‘ÓËW€+'båAñ™9«¼6oðÑaST?¡Ûî^ÊÚ PÝoYꛓìÎä ý©{=®Ÿ ÖnÙÕŽ0ùÔ+WwÐ}‘Ž—óØEÌnw5®\tn7çÈæÇÈ©— ®á±:|ßÁ3¾ÆY§¬¢°ÍµŽÍLÝýÖ"¾®2ƒµž³Kxà^?‹#Y’æt™wXí±É5x,ܼ­oÀ§Yé2ìê ñÕ%v ¦v÷%ô½,ÈJ¥¹ú’Ø֤Ê/ëoÒ5n0¬°ËÉ+%9´·R¦;¡ç0ˆ}}Á&iIœT¬Q ý§»&ë†Ý &¼²mß_-SZªšÑTF»¾`ÍWÂö®ØtÏ`ƒï 0eKJ(Û6¤Ìl}^S’$zKßF’UÌ„rÓDl /ÛÃk ˆÞÑc[Ì€ƒüç™ê¡m¨ÛM—`¬ˆ³Ö~§\a0DWOùþ߀FXG£Âë]ãƒø•`ye‹ÿ{bZF™ÖÅ•|â‚÷ØñÒˆ)»Ÿ+¶h=0Øn’û¤ùÊÑÅHxi‡¤~ Æ×v†f=в¦A|B°ÇKXv2ùšÄW$~ X ì?%‘å ÿvŒÿ©Ç®"d§»ÅÖ`¦y5à£d|@ÇæÅþ@Þï죴BkB¼wðb7óñÚ¡…qÞìÏ[n >V4!·¼lÀ¹ãzo«Q·Ýþ¸‚>ì‘'“ÿR-ó¼!uÙÉØŽcnm°±9m²ŒÇáªgçcØÃØŠL^O Ó` Í=–=Xi§«û2Øã±]«ÉhˇLVnêìØd¬üÆ{9hZ¢%±°Ïð¡[Ráœ7jB¾Ú A±C§pNx¡Éæþ0¸ZØ„v÷éšøj÷ˆ{L®IîÓ51‚ŸkR9Òæ÷±Ð¹{ò(·Û6W]í®ºSl?-·^_d7W:›ÑÑM¼ Xás çÄvDG6¬ˆÕ~—Uì3|Ö‰gZð0LGÇ^ä C¡oÏr‹o±¯Ì®Û³ÍÙáA‡7Óp þ ‘ö“ y¬È÷0®biP[rU|ÂŽ/eY}»ð%|zfëx“«ì¸-ë /©¶XÁ’‰JstI>6+X7(­vVèñr/lñaugtW¶©™„؆®™^‚·¿Ú(µX ¯ìÙL˜Ÿ@Üög°+YßPAba«€ñw€­ŽR«lq‚7_¬?…±þâÍâ‚2¾ÙëÀ§–bµEž¦5þNáÜÛX°É}øÙ ¾Óôô@Xv»vFü$û¿‰öŽž)6­ÿ¬}·+Ul¥~`£>µÇ8Z/eatq¡Å>³1Á9ì/Ì0X§¹ó¯~í b/!»{Ú9|Sw½A©¬å§[Š8ø¬¦±2ó&õÞVvÛÚ™E=þñš|i…ÃÞ¯êßÿ9˜`endstream endobj 40 0 obj 2863 endobj 44 0 obj <> stream xœÕ\[sܶ~ׯط®ÚŠ!@$;“‡ØquÜÄVä$§kÝ;¶¤X’í´éïHâ—ܵ¬Lâ<‘àÁåÜ/Ø_y¦ô"wÿzàèÍÎgÕâìfÇ?^|ÝoÏv~Ù©³ÂýçHøèÍâÑ!}¨ëE“5vqxº“gMSçU‹U-lI7å¢R*3õâðÍÎËåbw¯È5ým–?˜•Ua–zÑAÚº‘J«<«´üH€uøŠj£ª¬®äû‹ö½jŠå%ƒ· ž0ø‚w ^C ‡t'cï 2¼õ•dbZ£—¿î’zT&/»¡…-­Û=%‰¯G–{Õò ZSšžgî¢1Nå“ >‚늶®«ŠW%1Íò¢Y¸Öu‹ø*¨—K¤¨€ð5X¥ñkFÙ­DY+Å,ÓëŒî°t+Ó .ƒø¸µØë "BúG /ÐTè³XáE e½s5¥ÓxèP%{¤‚7{vRZN%„âJnŽŽÛQ ¸1ß«–ۜݪêžÛ†Œ;Wëªn¥pOˆ`&¯Tú{6²O<„Ö}ŸÁ/|*1Øü÷N§W:ß(:¿Â:WÎvü$@‡Òµì Çz–¸«rÜ~€¾ ÐS0ÇÙÕý<<ü!@ÿ/ð70ñS°è°˜Ÿ˜œ¿A†À2ðˆÁï#6Ⱦbðâ}²Þ‚AÍà þo”?µ%§E1{îiEä×͈»"ñÕ„“2âdÍþhÌŸ ê½cÿDÉ»%¯m®‚xÐÑ”•3µš”7KÈCÊÄ·ún¶œIrÖ&£Fzº¹ Sô!bŠC†žò§§7rçÄ/Lo0íÕy%½•Î3]èDrp˜ø#§tGz÷) ¿â(2aV÷ð:Z©CÒÌõGÄ ­#£ÜQ]ÄÖU¸a@pþú™ 3B»S^šX@Z¸ˆ“ˆ¼EäìïCžz‹¹Ï|eûÈñûÁÕ%H%)ûœÖ¢ôÅŠ–ö¦ÖUç÷å6ðÉÊ!BÓíÄPÔÄ އs;´>ÉP”;[ÁiEr¬SU™f5cí×˹ðƒÑHìž$ötúŠ@àÅþšølÜ~¦ê3:Ö€!R[}Q„^$–P6†3xWVÌ{'‹²žë]èË©S[¿ƒ[¯³؇¿ÙÀ)ŪsÄõO€_ãô &ØXŠ9±~“‡Æy>±Ô¹Pרäô%X™ŽUE¦›M²Ê"YµgŒÉê²Ù™—³³*߀\Ê÷ _Ã9N þæW¬Í”®Ê{ÍUk£›4¯ô XÝr> M${°Ã€ùÃÆf¤'Pò~N~{½Ë7–ß&†Î2äsj“ÙÊÆùœè)|–Nô?Üo<ò;â€f²±CôD¼†=¸«BÔ9D†è¹aPôZ\Ad¿B¼¯½!§\×ÃAêDz’}¬Pô—'òˆŠbº”ˆÇS$XÁcÅÿ4¤Ô0@b\Î'†8JÑ õûS  (M× –~g;‡È>‡O²ù29i³~†h‰osçËT#Ô½k‚“íqâ°3øŒÁ% (ЬÑOAšSø™@†OIBôËÝíÔzOàÄï\úÖ¼æØ£ /­²%×/î‡ë"Gp¯ŸÄùw£•µÉ<ƒ÷íj•&ÜëœP¸ØuxêÒÈ‘wîóª&'§d`Gλà@]àKh¢¸uí8ÆÖqFb¬†7Úrã9£0‘e%÷-•ü´`°ÛÕPQ–{2,½èM—[È3S‡™.Ø.B¹ö³ƒfAT•ýÓu1èŸ6E“Ù®Zïîå™ÕZ×Ú5j6Egb–jä9o²š°ø½iÛd…±žOÉڙʟ0}H«\œÂH.»!>_äŸÖ¦v†9 ^‰çŽzy¦ËÜŸEr0º…xÿÈ=µy£•OŸæYé¨têF4UÑäuŸˆ ûem0þ¼ä•t»,ˆOÃôí”*/ÊåÏ»rSC™ig7®Ì8r´`³#f Th-‹,Ù©ßÉŠ­¨ ˆ’öS(XðÓÁ(¢ME<Îo™Yl+!dAãNñðÕ\H€Š,¼q½ §¯áÉtÅE|R„l"nØY\XfMŠÝ>Û8#ÓŠ’Ã÷YŸ‘ c'Û–'ÍI$!þf ¼S½ôQÜ ²¥Œ$l*@=ŸÈ PÂm=€êBS 7ïø=ÔåÀu#J@c‰à°×}H#\oXÅ´—£pçû˜Ì¡¦5ÅŒqzL'ºŸ„‚X½×qØGšÁ¨H¦n\t-d·sÃ%ê‰>îL;rH} ÿõ}tCl®ÌKïÃX'á ‹žrí[ á j_AeÚõ‘¸KØ1Æi]l]Vpì¤J]_ uÉ}9¼ïµ Ô¤È+lkÑf³óûê^èm€ŽAˆ=߆¡«ÉÖ¤ì¶_ŽYþ%@*Z¢ª•é;Lãu´D~v “áÔƒðUÄ·¢@ôdm :vãï‹ ¼&[•úգV«|ͪ•?9Ÿ$:bP¸\«"¢Zòx¹–ÅEƒ#¿?4â¥ä¶‰¿=³®å3®½c¿‚–i•óQ\þÊ)dk\ܼ5Øö¬T™.Â=ñ~«´P[  @Ÿûx¬Rªö„èbÑ;szÌj[–Ääe5Æ÷4†pQÆ¡×Ãí¼À‘ôeMsû¸1ŒïWWùU癲EA“W™µyl8’úð=)ÛŽ´(K,iÝ‚¬³Ôƒ9k…¬Dy¤%áÀrMûBzcwþ}ÌÇ|ßö¹§&mÑnÃ÷õìá`óU3âè W·’òÀ§‚ðµäY!ohJ¨$îÜJ{Ò&:½#ºÍÑ!éMúï†Í'BÙmt)WØ9 ÿAý}Øùؤ›gÌQIZ…#+\© Ú¼q?ìV­/+Dÿ÷®Pwì“Íw…æ– ŸèQ€¾ãøÙ €+Á3¾oó¨h| 0$«­Ôßk1Q|&*ê8YŸŠ5àZÈœÿÃ-Ä€f»¯Rþ\v@W¨EÜÞ1UD˨þÿñØÅ¨,™ø¼ë"ê5JT\ê€âRsPŠ˜oe­3Ì£.E¢´Àk‘Wî#ñÓÓ÷ ÂLÐ}÷6ûz:#¤‰]zª‚±C—w(}×Uè½Òz:KÙj~Ñ3b©er_xRßÏ­BÏõ}€g{¾eµ÷ú¾!Žç{ßC×Å¿ƒ^Q߯?z3)æÇ| 1Ü@ H´wGö(ÐÎÔ¿§3ßM_™z‰Dt{hèý@‡ÊîàŸÌìaGzPÆ~„èáV÷ÉZAÒž\pÙ_/7td~u¨Fâ,%ò×ÅÞïî*»¥r£Ö÷ÊC•Än&ýqœÇ|p޶“®"ŠX¡CÛº†ƒï m²1|™mÛD:¼ÈÚ†€dQÒLhOÓóÿïÛðB°e`_Œ¾ ߨߠ+iKÀ›ƒ„.¿š8rú¶òÄÇÚ¡p›ûú wp‘›'Е¬1SëzE÷Qk° ÉæJdûÐUdyPEpæq­³…Q®'±… çûiÍÚhÿÕ %iò×IÛ‹e¢U°åŠú–ü=ÚÈËý Õ/*¾Ã B“%nQ-ôÆÊxïeêbÇÄÏ$à+[Ð4O]…ÛÀ¸ÆDï-ËÚOf*Ã€Èø‡¸“ŸŒXÃX2k.¨º«š=û /¨&çè¬Ò½Uušos7T%‚0ô<\ŒÓ¹àšè«ƒµŠú.#›0Û Ê7óY1Å7S¦@'0Ù ‚µ¦]Ô86ï8móÓGØî<@ŸH%og´ðãÜh–Þ@Û) &ŽºÞ@dþì>Ýä†Íû©Ï„ÅÆ§}ð¼âØñbh©Ñ6«u¨°ã<º¨L¹K¨šÜ Sþr$õ ®ÊÇÅÑÑl9ïuŸ*8µX»Ÿþ3*Í‚=9ÜyNÿþÐÛ&qendstream endobj 45 0 obj 3774 endobj 49 0 obj <> stream xœí\]wÜ6}÷¯˜·ïé¨"EJTß§gO·‰ÛÆÞn{Ú>Ll'ž­c;qœn÷×/)‰(]5÷ó4ÉÂ!)@àÒ›EY¹(Ý_Oœ½>øøy³xuwÐ5/žÿc Þ¾:xs`ŠÊýé8}özñøÔbÑm½8}yPmkʦŸU,jmÿßêE#Ê¢±^|·<=\‰ºhD-——‡¶YHÕ,/µ°T¥ëº¬–_®ê¢VºÔËLJUQÖ­RËGb=ß®tQ–B¶l¦WöAÂö0Õrs¨lcÕªåÝáªY¾£‘}÷J– Ÿ¤ëÅæ_Ã^lì'}ߪvاÿ<°OÖrqúôÀþÿo–GÒY^÷Ëéß¿³?®ªRŠÂnçÚ’…n*eŸæ©×º(Y»1uÙ¢cVÎÈ»0j¨ÿÍŸé¬ï ÚjyCäkH^yMä;"ï<)‰1ÒÆŒxpVyD«|mO!(ÉÄÎEèZG»F¨ÂžØ”In?ƒåF›¸€{_‰YÆ8ò’È ‘¯ˆ¼'ò-‘k¸œMîi 8Ù|ðf‡Í‡ÉØa* ÊOÂ1Ð~(žsDÙð³—J4…iÀ“Üâžù²âAOq%"o`ë¯y2Y™gà@íu4¤kUN× ×ûlê9ÏÖpI|4wü­u iØ~*èrãyùúîé¯Sœ¡—‡·ýæ£Ãks‡÷-ï‘÷9¾`%¾…}_À¾wE¿ëGÿ,pìË@íÀñ| ¶lì%am¸…­L1Îà X³ú´‹Ûw7´“íéÃüŽL,’”§"9h"9±ðTáw Âó-øK"ŸBö"ØaOId$;ú[8{ózñ5Á.(ìy±VþƧQÿ NãCTCÿžCÿ ãƒTCýžNCýÑOããçíBÈ3ÕÓUã°¶³Œ,‹ZJiä²pt%„’ÝùÀvË ¶0vÑÁu[TªCEDQ—­Žaàz 2Îv‚¢¬ ³rÍmSµ¥q÷m×,gêÊhaj­mll£ì¶ßÓÚØ ÜÝ/ ÝêZº§uË1-o¥I툶¦Uxªï—l-l_t34e-œ3ìD\ˆjùýáÐ[âwpÔV$qü¶ƒNÿCpœÓ˜ùä‹Ðx(#²1vKÒr¢iú-Û¹qèDé`K©›ègh¼!ñšÈƒL—cˆ’Mò²c_#T§žžì1LU>jôöx†ž^…ßâÉf¾çë˜+[õGenEYʪFÉ^ä¤*ÊÆŠ={ÀUøa²ïh-4>Ú [¶!)u¡µ¶"szn5’-ð’º^Ð^ØZEסÚYÄÐ×PßÏ@Ý”­p‘\;•¦®ÿ õ¿áûØ"Pukœ@uˆxÓÕ4F g-èÀÑŸ=# cÛ^Ç"8œ ›ì®}?>:$övˆiP~è[À]2r¬âeã¬ÐÅx¹U­ëøXù®—™J¥d¦·*+¥Ýq,ܵq²«¿Ï_$Ô?Vo·Ïºæ¿_¹ÖJ7Z2³³ÔùpÇΡŽþÔ/Ë()&œ÷`s½Ÿdo:‚TÄÙ{XûlúË@¨JÇS­dU(Ù6þwgg ŽM!–7¶ fJØ4̪@”2 H•.i¡‘ÉÆôWËÿHi¥¢j¼ña’»/´•‚ð\ëà²t7:9=“kQÙÃ.…òÏÝ8ÃVo"Y¦,«ˆd,¼†6I{Y÷啱*!IO椿ÈOý"P„cRFÁ;¬Ö ]…Æ·“Ÿ•µÃÓiê@i@}¨"P„Æ‘s}»ÏS§™ù7Ð<GZy¼oO>ƒ}™Œ9æ±b°ís"™°±@UCò" "É€㩫ÌdTP~4rÏv² d ÏÈ‚2’Ü:fa;r«Òì¥ìEÂÂc;?ýÄ û†ÈÁEc&-òsPÞ|Öz\¹5°¼ÛÖݗвc_…Òa‹-÷u~:l 뱨6áZD®Nwé*»S{;68=Æv]‡Ø}„—,³X‡ÿÂaÌjÞClh·ÈfFœ ÅîÓšø…]“{ê€OASß’“ÀG]d\çÈAC7î=dŸ3µÕát ‰B7ì–Ë/÷'”‹ø:K/]¶Mï ‚²L‰“–;d˜m —êš¹TžÚÙÇìF6Ö}?…š»?o ¼0cã†%5ù8XZ½o» ?v €õû|Ì(„4‰y/áI³¾L -áBl î¾tn·%·ðYØjPÔéck-ù°aƺƒO.Rï­æ² ÛS¬§×ЫƎ!›ËÙDéyàÒzQ.#¯Ÿ"¶‘ïá)Lƒ‰nÚ0ê%¼…·Âp¿!#{×ËŒ¨ ш(èFM¢Ñ®uɉ¥˜ ;·eJ¹‡ªÈ–‘]"SÆœg†‹*m<¦qfÆ(b˃¬[Ó¾1) ªfXÖ7ôo¬#ß”l óR§±{µF£ÆVëð19°Óçg…ïÊ­ÍH·Fpàn;’1 EÛBÁÓÄ!.qè:#Œ}·ÉvoxTaªŸÏ02#ü®KÊNŒLeÕ)”°¤gH¯ÏIá˜B2ðC'X4aF4 K0dѪcÚ"ÛøÛCÇ«Rnù"œZQ°É#úàåÀôu8lç¤(÷H5s×ã›ñõý™›ÂÈpQEj1’ún®H¨C‡H…Æ1x­ud¨à[fب@SDƒ(šÇÉ^²áÊØûÂÕX±è1K;½«fû=X#§•ÌCßYÐïŽć!‚í™øfUáQKïì+lG‘Ž÷–«*ì“I™ ´ÙÜAk”m´fûõ…—œdÓUØ‹=1ï~áãÃ0OB^Ã¥¿Ëy‹0'gRoYÊž³¹k~Œ38/«5d@¼Z ââJ'u¸è¢A‘ ªŒmqÀùHÝÅöŸ18INM âpCì-1™y?Ê”bÉBp1¦íEfãP_¸ÙuÀJ¸s¸ç(?Õu¨…L¬žÖ!†Ë­f©ªÔ)ÀGº>­Q—¡–mæ…à}¥-¼ÀÈO°AšÉD;[ù‡mÞC6}LþFÎ ÁùlÖý—2È·î*©<ʼnQœ<‰¾O™‚ÃÍ ,§˜¤©@9ÅÔ”Da3¿FÏ ëÆ\ÏzÏ©4W , ×Dê}ŽŸ„hî1dN}É@2uEf>LÜPOv߬ᩦ­ùöèOÛ:¬±uâTÊ„?ƒö5\BZp‘1þî¾®gTã±PÔÛ„D<Šj×CEŠíU7 t¸'[­Gz™ G·ù¹ê v5%¢Ñùú4Q9€¾TuaÔ~€~ ý“î*Ôei"Þ]ïGçÐü_/ Ót :zÊ“Cþž„Ç÷)°!°ýŒÍÆg¾Kýœ?éIY—ßqà¼ÖmãšPÏVâze¯á€zW‚\ðK >²²!¥G3Sa,gªªi‰}>² g³ªídmûj3"ÈD™0E>ZBFúÙõǵHLÏHù`ÔrÆ V½‘¨V€“Rø±AëfB­8˜"Ø^FoZøŠ_¶SjE¾!ºCv)Q„&mOpñˆ£N«–ÅøßŒÞˆÐóñíù_ØeqÏ„ŸÇK©‡þ:*ÑÓpèQ×ʽMç'ø×ôŽjL¢J0º;û›Ë²i½€œó†¹Æp½5˜u_U§ïÊ$íÌ‘÷!dT¼{D©Af³ž¢vƒ¦¼Êrh*ö1pÝQªº…iBð¬®B(Q",2køÜ|ŸÅÈb7¦nº{†¹18ÈñÐ ˜8òÁÚŽ¦(ÉÖ˘»Â$¨±Ãg…£·\Sìg ¬õ¸76SÙ*£ùõÏ»‡è¥“ÁúìôÒ0å©êã,’ ¸»ô/Qáø“Á]/y?¾Øœ xXm1“»¹bÃd‡È(tÍÕ¸±d1030·¡qžLÀ6$D3r`yƒ´”ÂÏÞ†Z9˜ ?zËûT#/n YÉ6ñúÀü"÷+àçdss)EóE›M#ÁËO·9UÆÎƒÌ°'3ãÂ4ÞTÙñ a‘¸ra—ëWõâêûIAçÈùVN§ÅáÀ.¹é,’³{U~êþ´ÎN—‘þv@QU¡tºOŸpñ‹3„Ö’*Ÿ!|(h¢`¸5E;FÖ V-ÐøÉ‹yË¿>C9® eŸ¡LTˆ…Õ¢7ò;†ø{I¬õ2W Ë^)Äß@bó²Ïs]åæý­?‡†ªt’óXŠX+þpÖÏYw°C–-ÌOL&•…ʤ©D=õ5VEýAŸa_œ9‚¼ÀŸ!c²ßpÅSË*Ð%\ÃXN¦^}.ƒIcžÆ¬~(Ÿ‰¿îóÇâ54@Äl2EG€ÅôrÀw*õI ¦_‰SË2š°Ãé0™g/°ÖÓÜ™e„$¶AWpÞ5œ ¿lLHSö{•lüœ°(ËwÃÊ6bQZõ?3¬,ãJPÛC^hwpÙã=§í°OK`Ã_wÅçÿážÍ/%™¿ìíøééÁWöïÿÚYrWendstream endobj 50 0 obj 3865 endobj 54 0 obj <> stream xœí]YsÜ6~ׯ˜·hRšïlÊU¾{ãcEIvs<è–]ÑeïþúHÝ? È™ÑÇqU 怈îþúüç$Ž1‰ÕÝØ9Y{¼QN.ךǓ¯»ÆÅÁÚŸkU”ªÿš¼½s2y¶)_Õ¤Žêb²¹¿Gu]Åe;j2)rù÷:Ÿ”IeÕdódí—õÉt–ÆBþ=[&›Q^¦Ùú ÓÚèZ¢P=‘ÄQ)øK¬Y™·äC‘%eT•ü÷ýö÷¤N×/¨yFÍSj^QsBÍsjnÁ×ö¨y Gøš×ÔŽø‹Z˜%>¥öùfƒ—v±(iÿ®fÂÒæÃÝLÔÃõ'æÙdË'E«]È¢2IëL­»Œâ8) þûUóz™dµ¤T¬ºŠ¸”ë6MÖ÷°›ªR[c:«qӼ̅¬é›ÆÉúl:ËÕl’[Ø`§ÔÜ¢Á¶á`{Ôa.G}Ðúûv{ª,gŸzEvT'öeý4¡Og£¾æ{ÃúÚ#žÁob/H&NG\ŠŠ÷=n–(²RSC¤µg‡Ï¨ù¾a!ЍHËI<ÙÜm ;Ð:Ù˜†;4/þ†KØ¡£KíuŸ¶òÈ|â±À‰rÂänä‰ü˜:*«LödNFFö+—ûÔ˜»ùžr0¢}KO_i¸­¥*Ö¤~CcaúÚ€Ûë½)B‹_§´à.±Ïa”ññ(æ6#ĽE© *ÃG>¥€ €®ýÊ:Ï}4'1Òú­j‚>|׿1-[u‰TbI‘®ÿwš¨ˆoœ«8À]È>YH9úvÚ4};­7 Ë–°Y€A+A¯¥gÁ\œß"ˆ$¬ùššZ*ËŠwxÖqKÕy¦¹¥³[Šºâtmí–*.ŒÝ"wæ‰yè@U¦ˆ*¨R¦‘6dϰ§VLÒ°t Z³÷ûdV㟙ß;€,¤ÝsÙZŽ ¨õÔœª+ oZvšSÞ^Íif]Ns"±qÁì Š‘…G ¨5FʘäX›ØpÔ\iœj® âÝ.==Ò6 £>‰PÐ:b@²MƒM äZÖb@´¹<·äÊB»}äà-—g±%‚ka,Çöî܇\´±äã´ä˜4„&ʈʄìȨ̂3Øu¯å˜4²Z3ÌÂègU.í¸:µ€êÎ<8†H–÷„4ÀEÈÌ«ñæˆIóLÜ ¢ ˜‹Ù-«H,6ñ‰ÍBN–e¦²¾-ûIH…æ?KÄLמ\©­cû{¡þRÛñ5ƒ¡K(­lÝLØ  `ÓȯVºÉ‚ÊÅBuô9A·laÅô†‘ïÈ!€©û‘Ê|i²ÂÄ‚¤&á Îè,G9g9²,k¢Eê,‡˜Îâ¨HŠR‚J4I¬¨ãXÅè$eEÕ©ªZ¶UUÿú˜šoeÇ4Ž[ݤ¾˜ŠHÚ^¢ ¸˜áoÚ1sõ…¦ëëÀ;³ÑžšÑvþÅ©HYëBu¬“4¶ß¿4“]¶O%ZŸšš–ÈROó‡ÉD5;(ßÙü½ ¯½ë>¹ÛÆŠ»6- ´,2ô˜šoi(l†ž›ajó$@׿:…"Ÿ‘õ%»ÃäÇ%Gúä–›…aú5‡#Ó<„ß4 Â,ft2³Ö,|ä{»{šš”.M|-N ÌLr¡ØTú€Å²:Ìân¦¹² ,0ÙåÓA£{;¤™‚á?Écô½¶¸I‡8ƒ®¶µ›zX¦y®ùÎS›{œ×Ö6jhÖ·Ð¥=ÇÆ¿‚f ³ýŽ:v‰# Dš]>@2Z›a»¶’Ù Ë`¤Ù.hõô­ƒSÐïe­¸7j2ts 9ìlñÏ4l‡ÍJf¿Y”tÝ&Áy¦ýx¥ØØ¬Ê¹`é⨠™8:ÕÈaçyû,bgFÎà§C[À²Ô8à½Û5þ …ù±ß|&¶nØÓxL|¼;Ȇ!›aa'íÂÅäœ ôìÂ%Ü 0cE³ ç šÅ=`o«‹s¸òÓ>ÃH›)Îâr0§H£ºÌ,ÐéŰÜÐÌиY9!¤Ú3¹8t¦‚I “†cçs´u)¤§Ä`¼­8¢ñçƒæÏ†ýÊ**M*{V.Åžíd€¤ŠãñŒJDÙ¹iíYŒ Ôúm2ªøÄ¨ 1jËR¥&5Uú!µi.k«Â’é™^Ŭ+žÖ¾&÷2XaÞ‹U4uUÒFªŸ]ÇÆŸÝŽ:Ëðva°hN=Î¥ÛôEݼ¯¬ÏÍ$áô/{i§‹–‹ Æc·za9q"•^¿ÓÄ– žŠœ G¾4¹$gÎ"É3ÏúpôÓ‡û–¯²X(s‚c<ÁR³Ilüs4þ^S¹\Ù+ñU9 +X¡àKSæÙçÖ¢*×?SÍ$ËóÄ<íU8t¥Ðh5L&5ÔQ–W,Üž©,rkoQjÀ® ŽUzߊ£ŽÉ ¯,уÓ¾2)Ã<ûÁeߢ•W\Ïá6•¾™S( ¼² aRí·³±ðíÖ1Bûã5ÂÀ;&¿L.M`>¡!i ýð>ë¢éÛI$3™ûYq|‘):Y`e¶õG#”¯è-Üuƒ:°:yŒ¬(ø?f†G0Àφ}G•ú§¡È„éËL6ż¤i2ì5ÆT²…«W’­ºê’ûZ:)àÙ! ]øàOðäuTU60õXÕ>ÆÁ>«+ÑE̽d2V=wF…Ù¶Û®(öJ¥‡«¯^±Vgúh£Æš–‘MÌ)%r‡²C¡ÃjÌvÂõ VjÖ ‹+"ðù¹#T«´4ƒ…'ó*vk N7|¾2-}¥?Ëgdƒ¸9ZÍ6í¨Ù~5&æì~í`šëPÁLSdÝøA•‰µ÷j:áä–ˆS²D8‹ÎQ²5ÛWò5Ö€ ëdµ„SkÛÔ¼yN /n9³ÈSJá«HXN§³§×°†à¼ý¼9ÿ×Oñ²íÁëî¹-d[2,ʪõÙYæ7M¤ù®çc×¼¤æ& L| ,Fqƒ 8‚óNñ”š/`sÃZo’KJû Š8VÛŽw«àÀîº9î$¨)R#s&–“ëñsÒ²ˆjQ6¡–´p‹ÔzešJ&teúeîqX†W–ŒÑ¬ÚÞ§pP¥ùÒA›1å*ØyXÆô`…)Æ~ƒÄÛ¡eøB"-ž” âC ½"­9–7ÛR"©B¨"Mï “„ƒIF0&ý—ó®ì-G*/)G+‡CÝÿÇ¡åŒÄE$GVèÑÎûžŽA‘'b3pôsÏ\pN”kdäqË¥W* ;E¯" %ªb}`€°°0€m @‹UTÅ ÃCÃHA±ÁÎÙUSÙ í&µ9nœÂ,s«ÙRÖ}t ÁMÇxQAä õ‚ÕÄ ù@cøß9°DÁm{hŠ…JÚÔ“|_z5†Wæ•-#$JåžUö1rrY(jÏ,žŽ}K†àI";š;ÄB„˜“óç QÞDNïÐ! ¥ºÔ~A1×´€ïÍéWia׊ı`GbßШ\e]*5ìã.æÐø·e‘¤R{]Šü€Â½^“â 9;ûÔ²Q`ŸØ‚ c@‰>ÇcÅ‹Ÿ†\é—aå<5¯òfÑmÀÂ>'Þ^<5Õô–UÕüN×–ÿnü¶ŒD–X@5ï¾ ¾üiZDn–q½Ì,ºeR–ö,«WŸ+T9>„rÐØä¸hï ¾£I6=wZÌ·%V[ÆßV%tç…ï¨Jß@‚g"Ï ¶b"û®BkÔƒ´ØÜì?8ÅÌAvóÀS@Á¤>ö¢Ù`x›æQïL Óôù+D4sp!†¡GPxwŽ…a>*W‹4Í\FVv—7[K—§ {ÇìtmcÚàlBÀøŒ5C ãÀÊqv V[0 ´çã(ÛÓm‘CŠä ­sC­×…чÄt÷—$•|C€Ó†ÀS¶ï cHfÉ–{0:­ Tö‚Îw_Ø4˜Èõ_hr飕I,ûænÅú—6»˜bVë@bšéP¥U:¾gZ7 Ìvô{*|YÖä‡PXò9|ú-5¿¢ækj~ §°²1Þpg®XèÖó.ÁõÊói7ë!%R‹ø«£QdžG`¡RO 0lu^ë·{AÁ1“¤¯•4„Dóo%«•AÞ ŸÕMÚê«W)’4§Z—y+lº+hõ,ŸÙ &¡ìÉ_¢ÕR¼‚¢ðÌ/¾£ä3X 1bÃÆeGx>D˜j)KqŽ.4¿=}Åcã.^A ä^aãú{¸(žÉ¾Ûh»Ç÷/fÕ‚Ä©º¢²¹KH!9ưu1UeœTê०J¢Œ ¬ Q^ {"‡ÁNèFYÆæúw‹[ùÝŸOÕ¡«L)ý‹£;vÕ·‡$qwl»ÕQ3ý Ô)*÷®­S|jC0·ßó“í€ö q,'€Ç(úÉ^ÒŒ®b:'c{\¹Ü‚î^QÕy{tMÆ[2¹~ÔJý¾åêå€Â˜W/ç3®d£ ¶”2'QmIuyUˆ#+~´öžSñs7¥‡½˜¸«YÞÓÉp¢'"¤N|feÅN|þ‹”á•› O¶ `ðœ=ªè8 3ƒÔäÝbÅ!eðoóèNzôB«ªpˆ±¾ÂÌwƒxËæ¶ 3ÓÍýºŒ¹N ·|(×§†_n®½“þ#Ìendstream endobj 55 0 obj 4670 endobj 59 0 obj <> stream xœí\[s·~ׯà[ÉŽ¹^Üi&3uìvÔª•+³íCÒš¤eÕº0ºØM~}å.pû-AQ‘í™(ÎÈÛùÎÁðØŸFeÁø¨ôÿZaqqðüÄŒNoêäÑÉŸáúôৃªþ¿:Ê‹‹Ñ‹™+(Ԉɢ”£Ù»ƒ²°¶*­¬3°‘Vîo«F¦4u†‹ƒÆ|2- Í´‘r\L¦²P¶,åXLLa¥®Æ£ÉTTÖÉã×>§Ö¨ñµÏh™p¯&¢(KáO7ôç9©þb2e…ªtÅœ¨ k´âãɰ 5ŒbO]¢5‚Ûñó˜xìº'ÊR—³ýù儺*y¨SikÇIýg±‚I¦]¸id%”«ú?³¿8DíˆñÂêh%@µqq (ç¼âвŒIîÅél2µEåja^k®m!¤Ï«Ej§†n±¿ãÉT»Telm¨¶“¤\Ý3ÒS¢+Éõ6¦n´(¹¦¿{}\Ý\t`!æÁF»¢Ü®ho¼Gqî܆içA³¥«:«ƒ S-)ElÐk×*ÕxUj¹Uªc BëNnT“Æ–°äDÁD%^ûiJËòY_„¬XókjãB3¼pÃ|üM]V¸ÿ›!;möE¤Gy3,ŸŸð* e7Ê«vˆú¤ä.o=ˆBá§ä\cW1ÑÒn–bÒÇö÷Ë ­ƒt¤Û ‚´Òû ̓t Ò ¾(qûJtm4dV´}ôâm×¹ £(.¢ø>Šó(^ÃÔ¬w‹Ý´"o ½1ï‘7ïïüYÔ@^wa•[„8ÏŠ°G3žöŒâÊL{®AÑîûý5Úû-hn»_œ§5K2,)Xõ5ÍYOíx«¯O½‚­-aÃ$q·°á¬+Ã&vWÞÏ/‰Ó$î±§x Á¹„è­awÏ@±"8Ãi²G}†IêÔ+¿‚5ÜÀ Ø“»`pÌ¿xó¿µ1Of8!Tçˆsx´ù®óu,Ý)úFvFΘû y4ÐÈkëìuÜŽ’q:»Êûïh–àçÞ–Â'ÞÍ ^o“]PvÙJŠÅíOºY©’õ¨¶5ÏPÿ.JIE4< ÿvè‰ÂT•',S…í‚×ãáATÊ ·Ø¬¸™Öš±Î¸åºŠ]°aý{;=®nd½ewY×E6U™ýe 0)y ¡Hw–ï£Z+hb¬×Ì0‡©Ä…WT1ºÛTQ½³À›fSg\67uhïäDf=Æ-\Þ¨ýaŸÁM_,…û‚÷‹Øâ1§´ÝzÖBðËDù±0¸$­Æm܉«HúŠäøU(*=þ£ß¤ý5ä)hω’A$äÞúvÚã.Èk[5íùĤ½žzø°"«Ä®ÿŒ3UB’HÏ 9छݯÐJ7!„ ÷W­‹Ì!þ/çÆ¸&L½§•ªrÓ‚¬÷´>ù*b¼z¹˜hÕ¿uú*mUÜtÛñw!m0:6q§OˆøT<°».6A ù÷-tZвþMbÀ]¬ÀçíË›á`ÝÄD×`M=­tC_?Òàhýù,ÖŠ?5áGLià[²?Àð±é fJv•Gá˜Ôû)ŽB8ÂH©%tÜ@à€ñè®—1OJòÎ(žC/ÊÎ:óI&$Ρ—oÄ,±k:ß–Vñ¦®:¦ÜFG¥¶.Θ”ä-)ÜÚNWÀ[ÎCÎ~ô¼} ½q&®èðT5°Ài‹Ç•ãY‚ÕFgÐÐ{ë·,b?-z¿Ž:Ýå–þx£u¢É|:Àš¯¡_Æþž®í†b$4‹dÉÚ_ͦáѨvy–ôâBŠÄãŸ'Ì礼¤?©µÛh° ´!€âXÏ™ïÁ† œ„;Ë5ŒxÍôÄ)1ÂañïAšíe<²g˜‹½†¸­a اqèXB4³‡ï #ÌA±œÓ¿ê¹ú¾NñΉÉñ½tíó«¹Vþ>Šo¢xÅo£È¢¨¢ø©x’b›@ú2@r œùUÜí3[\|„ðúŽž8Ú㘄‹}fó˜m’ /];€ýÀ Â-¨àHB'§EøèË™Oïn>®Ú‹tlµ³õ.áJ„Rž¨Br¥«„v^+yåÏ—la*ÙåêÎ"?‹¹¸]Û‚R÷!’á1:a3 *‘øÄ·²Œî¢a‡Eájnƒ?>Ò$Ýê±èþD3 ðð îŒ;rå'ù+ ‘¿yüxAåÑÉcþUÇ(\=qÇö+ãŽaäû"ä1~,†ïxài ¯V£H`Ç÷À/“I*^·ÁN^†oaÞ‡ÓÚ»®ûPL²BïÇü ÈüEæoó1 ©ó™™5V²êÈ”‹;ŠôfW_o>ŸÀEØFQªJ(H+¤%Gý9pc¢¢¼£gÃú {K YÁçM!ãºÿ”CdöØÞY—0“‹Þ„h‹Å³Æx”<Ò¯_n‡n¸&sºÏ´È]Ì”$4fâð;û=ï yIk½§ žØ!÷Gs·î!ûÕ¹±Y„þ{5‚ë£=ŸÙBl¾öÝÉG.Þ„74O{áÏ_¬a—Ƀ¢ØD/ûvˆ8 aE ÅK˜ÈÞw=¶(¹ã6vc¤ñ{±ÿ¥ðÓÇ@Ý÷bá´$¥€Ÿ›–1V’ñsò ¹Ï/\AUuîó÷hñ$¦½…ñ‹Ùu˜Ø|{ik˜è¿Eì¼—Ì^‡î‡aÿ–‘¸D|ð×GÜ–‹BrkÚëâ|Ü¡û»ï‰1ˆ;÷““f Å ›Žñ:¿ÙÁ Éë§ {>tÆòv¥müÔ²ä1°…ÆÞR”PhøÐyø" .†GXö †T¶Œ=û^1ö,Y?sʶ›}Õ™Á;Ï:C y Ç=ËiŽ6–Hö{Ôò¥(à 96í>%›IÖNŸ”?—UªÑ¥þÔÔN§O»œ;¡ç´ÉSèþ1ËŽ^Íþáþý-0/ìendstream endobj 60 0 obj 3013 endobj 64 0 obj <> stream xœí\msܶþ~¿‚ßz×±(I°Ét&‘ÔÆ±Ó´Ê%™©ÛéœïNŽbI'K§¤ö¯/@±Kòá”,×igÆ+»ž}Áî‚ò›HÄREÂý©‰ååäð4^ÝNÊÇÑéŸwÄͫɛ‰‰÷_ù€ÓËËè˹e”2*â"‹æg…y%UFYj.Ò(—"Îí€ËÉ4šÍ¶<ÊxûÎDóç“ùï_Ø·‰P2ÖzzeÉ8Í=]xêÒSë¥2Ç“‰<–:ç쌼õ\çžz7\Ò² ‹dº!ò’k"¯ˆÜy[“ÊNñÏù×)cUnßþü;÷“X|éWû½§þÔX·TbÕƒ€ñ\ö¡Ò2Mc‹´ÀXkc‘+ȶ…OÛ9»†€2¶k"o J–PØ-$×`•´´päQüÎS§#´ =WÖä2RǪÛ6) SéaH¥Xyw-‚38_AEã‰×@X­…øO=xñÔü^àc8ÁŒÖÚ ^×P¶e2VÅ+3“{ `ؘ±£ˆsÒ±ðûÚ:†;ˆæ†zóÀ­6Øþ1%úˆÈïˆ<%òs"%‘)‘d‚gCãÈ±Çæ[`Ô' ”÷Ÿl!”™1À¶…£'Žö8&a¶¬§|¸rÝ?íшÑmpÁ…!þªä£P_ö@ßú+ð­ú´·Ï¥Búgz`þÀ:œçœC¶+ˆ"¶›Êý)¤I6Û”p€Í‚-YSÏy>÷@“Ë}s¯ ¨<—© «çïx³8Ù ëS"Y öíM-gncLl ÇWöcW "S®M&C¾Mþþ/`P4.ÍC!'°¸5ìtÿ1F2Ñxæ3*·Q2QRséÍM‘[ d™Jý~=³6˜ï4Y\ÓH–?mI~;Ú¶³µ’aÝJ ØÏŒ—%}k˜¬µ2´j®$K3>žI¹­VIaS¬ršDÈV=08Õúÿ¹1Ò<£ùtWñé®â=—ä¿­»ŠO=Û{#ûQôlQ)Jw$ͨµ?nèÞo*—Y¨uà;ñ·ÿ… ü–à9ÀýÇh‡ñÛãËXlÀëPäÁÆý†GýØloÛ¸õÂE=¬1×>¡dp´Àpà$Ðè+P×p6œgáj—Guv‰- Ù Ú­¶qÜ×9^suŠˆf»xWïØrñ€nõ.W\ùÖ©ëÊA固¤SzŠ[Éw° j‹Y ZíœúË/aå³€²ÛeØgôª¯„«j¯ÌŒÁ@XŸXRéåk[P˜VüþµªÌl7¥©(ë6v[¦ÏW“Ó_f©«|…CȈ^(œ‰ÕƒõíV+"® .¸š?FuÅAò0 ×Å Ô!“Àä6nÊ–@VŽ!“ËÖ»€ªMÂ݆-ùM ©µOÚiù^Ca“a_zK`n+›Qv”Îj›‰¨a°€+`¦´Ž ¦ÚÅ/•çqQänžؘnttP¥En2Š>ä5²ÅΘQº.±Ð]V o{”cŠÛÝŽÃÓ$ì‰,45½ Ýjz©43Þb”YnO§xv ã´ÂñvË:+ûq‰±qÛ¥Ò*ÎŒPe/°wËØ_3º~ž&éˆÞ§R&ÖfïS÷<—¡ÞgÝ5Ìóée«Pg¹µ/ÉšŠÑ‡ÔðB”;íüX.ãÉ>Õl|Ö|»î©¨Ñ¤½)ikµ"uâ;£»ç™NŒSCD({ð®üœ¯¨ï :¶åàcš“˸ݡj Ô¥-‡4º´§$†¶ì²;¿JEвžÎ¬’I¬4µf]vpÚî:øÁ·F¥÷:…Üjœ²‘ø³–Z·,Þ¶; lüU3Ȱ@pG!vŠ Ìíû¢më,sÊò‚stMÄÊWIœ»úÛ™;dË膦LJH»£Û83Z‡ K ®:+k§ø®n̵n|/@'ûv—"èØ¤º÷kxÍßhi£3!Gá~ 6æ ïÙA¬Ù‡'NHž„Žò~íî¦èÓñKõcW/¥wX‹lªÑÚ(rMCƒ®´ÙYLjë” ¸¥,Íï[‘’C[‰ÍM'-±NʬÀÕ2n «„§(B=¡µv̬ÿªtî|XÃ:.Ý–…†wñ\¤)Ç/‚%DW!¿è30æÐEåÀäXµQ}6´€+„NÙGG4K;tm{6Æœˆ… dµxç‚Ø4 Ð|li‚‰-{3S›à’çì^@åÙ4¦syç”M‘oûëK¸1¼6öÁy<›¬l«Â¹üîŒ)~`ø)¬‰_Ôq¦õiW·ºW‰ ª«ÇÄþïÓvÛxPëßåBö/kÓúÎyÿ‹ôKsÞ_j5¶y#ö ŒCmZºÞ{f«›çC®Ap¯ _RÜÁ¶þH5‚cÙSÖx½ Ã×cz”÷ù}¯÷­ÈÇS ùPt‘ø…§P'„ü†5ÿðÛ2d™Ñ8úh­¾Qi~ŠõpÇeOÙÝnIc6üm8»ÀyûÉÀüA|m?Îê gÕÆù‘ᥠô=‡¾_¤@÷÷ÎZF|’ó˜jÀhbÕs×üÚø¯Íô]1¶w!6|Yˆ*Ω®ú‚ÞÏ!ɰo¾jÛÝA-—õYÑé$‹<o‡åPCìðá‡63lPøs#‹?ó5IШïù¯l‘Þ@Yh—ð‚’™Ÿ¢Dýd>ù›ýóªz8endstream endobj 65 0 obj 2563 endobj 69 0 obj <> stream xœå\Ûrܸ}×WðmgRM€ ¦’T­.•R"­myÖqU’JÉIV,id]ÖëMåßð‚n‡Ã™‘u±b?¸l4€ƒîƒ ›þ%±Qâþ6ÂáùÚËý<:¹^+‹£ý?×ÂÕÉÚç5§îOYÀåÃóhcb+ q¡£ÉñZ…IòʪˆtfY”‹$έÂùÚßG“ñºÐq.´}Ûb!U>:òRd¥4Ó:IG7VSØbUŒ¾ŽE %ÒÑ%¬ãg¾™Ó±²BZ(«è Ó̤£‹R3•I>º¯]8t¿è9SeZÈÖT¸ ZEl!Rµ®Òf4…µpÏJJzP$ÿ—qç&M¸¨SÁªZ¹á#Ľ¾ c„®^5jCìž;0ã$‘iÁ­²ÌÆÿœüeM¦I¬“Ì:Ìdjý#°êÅêÀ ÷'YÆGpHÍ~¤R†Á”lEm[5ðìÀ0HÇhäg^bÍÆ¤Á1v"£ÝñÀå|)ƒûsOêØ ö©öÎpʘî%‰ °SdöÂÌkÇ•mVÈZ7Õ™î«Òk,TI–7^FXÓÂQ42Ï-1åÎκR:ÖZDNӘʱȅ—:q'Ò0ðÖe,Mž Hkd§ãQ-Ö»…QOÄ5/êÜó6…À»~]Ò]à{-üYÝC̸~Ÿyi™˜ù¹‚ݨl£0ÍñÇÐÝ„%)Ó¸S=C|‡Ñ˜·N8 <Íšó\ãºór_¿èf"–ÂøPد§q–‹"ýDâ$n4¢íR)«¶5ÖIb;"ª¦Øú®dœû»n sÄÕx=sµeÑÓa¡låÌ-jy‡Ä·óûžº¾‹,Ö*U öÝX˜dÑ´CZ‘Ž÷+7Ô…±Ýò ïÆëÅHÐo _ÆL3ØNÇqKrdî>k;K©Ò\ªó8S…¥9§º‚ 6,´®ù3Ü7‘D|Èæ/ÔŒXðöz°´›á¢žd;±¯€4q3Ü™]©âDi<³$5ÑùØa`ñ”Ntõò´o“Çæ¦ö—45%¬ç:6Ò¸•CºFmñäßkvð™ý¹»6ùÝ2{Ý}Z>výówã4Nt¡”u¢j²‹Ú‡Zµ‰EÙˆØR ¹•©â b‹;{ˆ/e+Ì…˜Â ZëJ+¬-¦Ï¬”Z¿/µ”‘ cÛ~hÑN›fflüÙM²ˆ-¶Þ³¼tZ’. ÖuâVþœWgⵯuê¥ß·tH,6#ñŠG$^xCâ5‘|…‡Y¿¦…Á¾ïå®—6½ôv „¯¥ƒZÂXÖ/W„ñâá\AÝ3_‚¡`éí|Äð„Õ–—~¼¬¦ÔÏS8ÒO$FpÐS(2\ Ý~?Z–û†G+Á¡~}@°u¡.V~_S¿.Ä iþ\*{B1ù÷€T`—éþ ÃÓ3v t[ûLjä}Ùžø‰‚DÉLŒï{^zMýÂ.V…ÇËòu%ŒËÕ"q)#f6{lr.aµ+¨À&’Í4s…Z` 'Cì‰]ì 6ñvýh~iã-¯Øñ@ÿä¥I¹tw§r*qÔ³Ñâuö æZˆ T¼Š1…h÷:Ö¨6À»Û®ýŽô-àD“ìJCí¥²MÙšÜ «e$þi‚£-ÕȩÕlÑõ£Ì¼ˆþ ô­C¨ËcÆð‚«=ð<å‹Oòûžy|þdÉ3 Cüœ’'1}úޱÕÀhqàsJëúûE韫!—À‹.ñÙ/üxúZÀë &NèlÏzû€W$ÔDϲM«ÇcÌ^Oºƒ[ãˆæÊ#¨Ë,|mCØCM]׶㦄vªÿH¿êA€6Dfx3È€º„ 2Ø6»¢x5Dq‹o|ƒÛP!ýMOp!¹íoÙýáEoÔ¾ìsÏéÝÕ…¿›œò—e¹-Ê3Énó.ømz7¸ÐK¬ö#ö2iÆo<}ÎÐ;zO^ßVF²Í…o¼_]iÖ)ëPÁ·”Ð:q×X{µË!çÄ».|üg Œ_ðeÃÀ¥fÅÐ/MÇ @ï<½O½ôÑKäÓp¢¤|nŇ`¦;¸UZñÂ0Ç‹%Ó]œ9‡¶9ƒb2€âŠ·¤°Û[…m65™íYÙo£d¹%¬¤›²½<-ã&ÓZHµJ Ó=«ÚKdÊG5L¦*^w‡^ª»£¹oÉi·k&ó`&Z¸C¦WçU±öF50;ùs+Ö*¬â&™[¨¢ó¹4“6ä4ˈ)MØIr—n\Y®òØé÷dÜÚ/¹ç¼NKX.%†œŽ]“©·l£ßl®é@€³Â‚ÈÿºßÌÐJa—ŒVß\FÛmO/½ê¥Œ¼õ'¨MJ={í|Õ>N$¯5íKi˜›ly³Èðà:'7~ö)pj8Qåßê8>ÿöÌ/¬óS”-µ®®c­²¼uQ2ÓÝpÀåI!–ªå‚I¹#›ZªÚ•öåyù¡íPipYƒ¤¹Öe0ý¯NM“¬ÉµÒ\4¿%B–ëjuò—ƒÌ¦á|ØÆÖë‹ç,;©iÙŽm‹J;GõÖd™&»½Z3ëw'ƒ¹E,£{0­o›buNæk{é‚EÑ ^¥²±wŒÕðžGk—ŸGPªkÊvݹEþ‰çå¶3ï»Y±©&• uìS°ÖHYÆà±øÞ%D¦å=+&:ÏÇ–ÀÂÿµ/.äü>Å¢Tß`3˜ ÑûÑ?Âùy¸3ÃùñÝíê”—BÇ©†Op¼ÁS$&ÐBÏ7c[CͱODö˜µ¾»¿@úÙK¨5ªñ/ý´ñß¡ |bŽ0°ê»;ÆÆa5|Íü€ý?X?Øù>b{²öÆþýlýóendstream endobj 70 0 obj 2973 endobj 74 0 obj <> stream xœí\YwÔF~÷¯ÐÉ Ý9´P•J9óÁ™q 1aæàôÁIðp® Ê<(Â" æG[QXy”UµŠ Môï" 2!B•ó“­—“`:‹#©«ÉsM†I«É¶¥æ– jJ¦“™}¸Óy­&»–úÙR§+ùXêK¡ÎP‰ÿYêo ÿSWusBŠ(Ì$éEÅ Šx² ò’È>ýÈsX.vv‰|@dN¤„Å>ù¶ö;mHÉŸîùȧÓßæÿØŠ³<ÌŒe͵™Ü¡×¼*ÃyoWkr³ºœ»)©YæÿÝÒö–èŸ;[óo_“i˜‰Tê¾jÍ©2-Ć 4'i•£Ò%3¡ -z‘…B #yTÆ2žÙ‡ÄÈêy3Ušˆ e´¢ÆIn„c8cefpNÌï=zÏX×>Õµ€ WN ¢µSªšW¥ùä–Â]<.+PÒ Eòñ˜a–kC7†:å  *•å|„¸×§TY%ÂJ®–5h‹Ø¼7 £HƯ•õ଴Ga%©9µZr:pÉ-ÄE’ðP³¯é)“Á!Õ´ëªß’Òù±¥X³!qpŒd(ó´È $ ›ßÏÍïíi¬ '*ç y<€o ¯Pqž¶Ì´© a9%ú©N½Œ÷ œi¤êc¢öˆÌ.H5Ì'¾©ìD›½È-$1óe¬TëÕÛÚç4ù]Ùí8MÌÜ ³Lψ™©|¦”qœ,˜Õ`Õ2ET]Ù‡u³"M'–Û±6æ]ÔîѪ¾„¥~Ù2XÓR•±Hsx@Š}Ãm´Aª7¦§q’%ròiš#Ô.Gðå`–­êÚ{G¦ÝØb½š–²xK}cuS%ŨŒì믮Páïβ¶½8™hÛžîãš5J³,WBO¢ƒ²Vò™wT6‘jS­É²5‡lñ¶X'ØTk=J¸®Ùx˜[ü:…fy6õø;¡›¡^µueÞ3ÇgNåñ ë?0v—QƽO+ ÆN´;(Í›ÝV¯!M®¾SN7,$SõSÂâ;†*Ñ­]ÔS[*Õfs—ƒ%ê RrE·*M] Ú·ðARGý-tê8†y¿g)N„}ÜbB8‡1²è­ÀšFOqÖqâ–û3Ϋ©êT©fMjÝ5`§W=u0ç@W ˆ‹v rÙÖ»„ø+w/d¢ë/m -ô‚â»AP‡¢f¨lšþÈ'N#Ì\SŒÕ‘@ó°ŠÊ¥ŠÃXé]ðšjìûd¡ï/ž`&úx:KÃT%YaVE7"UN}xbÞÎ\ǃ (¤K΀‚gú9á¨È®¥G†2A±!œsÒ–Â8ˆ€á‚aó0>õظèAÅE—TÕëÊRdª8¡¹ÍA›47ÚsІàâ#Äe[7æ=3‡¡ìu:R7yLN ïÒa¯'ð8j6s¡±34¯Â#³ ´qëÚ+ØÒ:.͉BgV­NDåÎÄßu_¦Ilw]ž×¯±ô tŽrކ\å2ÎýöÔ>“‰FqÑõ¢0Í›¹<±Ô3K½¸ÆY³üÇR?ufÔꀼ'܇7Žž‚q¼L½L‘l@ç°“§›¶ Vë§IÚ*äæœÞþðmª)»A³`gšdÆòJŸWùTuöŽÈ+È‹ž~6O&õP?uÊ~-*»—¼®zoÅ%éôŸð™0{Þ)»Aý^A1ã÷늀à†MëáU •éÊôúâe>s É‘³ÉÈ9q¤ÚX L—°— ††O­¹uãÍqž&r¡Âj=öÕ{ZßœÉú»¼Éþ:p:K#QÞŽi6é®Ì_K÷ .Ýë½Q´Àf#XðÅxYM*$>él¯ØZë,MKå·—¡hÖ%Þ³Ô‰¥ŽÇ¦‘9àṉ̃jÌWSŸÖ¯iÈjf½æ8f'è(Ó œ^®F(aK¥¾RCðü Œ"žÜƒÅ!é 1lábl:|k@S«líY‰ÄI»ïY̯Œ'®­6Œs()Ÿâp åxvÃ=Ã[,ØŽìS_Üý=˜åöxÄj¶”ôI~S{×½öaÃÞ%Ñ z¥Çe¾(ð¦l€Ëäü=X}¹Ú:† 8œGךÇ8…¶„Tÿþ3#Ü!,ö±-á–`ÑÚb㨃}Ð+/ÿ PékèóåjkÓ¨ƒ„½aÔùr…}-Ô鱿õ¶ë×AŸÄ#××°ŸØoÙ òA†‘ó7ºÓÌÜÀ³6XßàWíÅÉú¸…]÷Ϭ»–Ê耄¦mRYsð|*c2b`Á¢ }8n¼áæUä>l æwXìúÊÿŠÑLÜw½rr°dÆÑ)Þ…YB^¼EïUÜ,Æd=æøûŽ´ø~õ(TyŒø³Émg ÝË$Âwˆ­ðˆ £æö>ðIË‹íº ³¾å¡¹ïјñ†l0@ ±¡²‰orùÎ!éªoŸ|l Q+ó¡ÖH0/yŸ²cPÚ‡ Þ;O¬^¼í½îþ꓎dÕäGK=sä¶ÚÌ6/í!ãgõ^ùêŪ;†5¬}[ü—ÀðŸ@Ø/-Yê¾¥bKýæ3|Ÿ›Àñ`ôóžŸÜh`sÖ;ääXD>+©â5]gµ^ia~k¾Ñøäºü+Ð%tWº¢°:h^¥L­¸ÚS¼0*"þòUØÒºf;.JÀ^…·—<§šÉDwkðnŒÚfóÀº€CÏ(p7»HT#Dy { Cbì*ÞÈÈ9.{ŠC Æð òâʰt¼«:|r½öjx$i¾i=øH䯋^€ójmäQ¾"Ò|¾–gÑÔcžEgXÊ)´öÖ׿dw|MàÖðšÓ³™á´¶ñ-;èYÍAÍ_&3ÞdÖ¿ñÒ׳%äÅÃ<‚]_^ïþ8Z>Ðv#Eœ1 ¾ÏHónÚ5—1¨Çä ÅÞC?#ݾx5†ä È‹z˜£¿ÐN¤ç]QW‰”–»ýÊü ,îIˆWÕÐþàйæk„`’6å»Ò˳¸7c[~ÿe/ óä$ÏíÓÝÁmß–íû|Û“×^ê…\ú¿dåù Ní%ß 7¯•Ó¦½~ü¾•Œª¬þ.ì4ëßYë0þ˜¼â2)¤F'²² ”ã¹¥Õ÷´59«ópú·lŒÍIUbIv›˜î ¿·¹‘pþ4œcáÞŠf ìAC f×Ñn,LB$sgF'¤Í*Ö{_še²ñgdzj“Ë,¦èRx™Œ¦#s“å.Tú3ʾ€ÅÃ!ö(µI䀳·tºY>e@› V™¼­åÄÀS±4öÁS±Ðg+ëæáð$½îØ·¬ÐÄÀÁg›J?xê3S~F ‘\·µvÚ5öek˜¥Øëš\œõ˜ÑL{·?=K7Ë¢7¹ÎÀA(ÐýÒ_i7¤/ý«L9eî9œÎ s8'Î×y ’'9 X.Ëa_otEwÃH7 !Cj”ÏûeˆéúTnqcIXá$kuž0©Í`V­“)8,°Â ±.²Ï‚( ]¿sq¿mÂÍÁîmkt“ÖQN%Ê-źU‡iìjeVê”ö\ä¯ú2€€Ï¥:@tX_Õw¿®$‘\bÛó­Ÿõß´–¼endstream endobj 75 0 obj 3238 endobj 79 0 obj <> stream xœå[YsÛ6~ׯà[¥ND x¥Îäž´iÓÚJ›4ÉtKv\Û²bËÍ4¿¾ì’ú(rì8Ó8+Xì…Åîøà¾^`þ*`ÿt°³›x‡ƒ¼ÙÛ}R燃ƒÔÍ¿¼Ãû§Þý‰(S/󳨛 ?ËÒ )° /Žôï,ò!|•z“ÓÁë¡7‡Ô¿Õp®A?JB5<ÐÙZ›ŒóÑEc_hmpIÄrãÔû"b/ Hd!çÎôœ‚aRwx;ùq å£¥8y6п¿1¿”ù1ù¶&® o8ž‚¯+ð•x{¸œÕåVÒ0³ßšòÐÐ1 fAlŸ¸‚":‡âœBÉþ»• çMºBΓ$|¾)œ¬É°Íâ¦@r]Zè´‰±“´.\BfZ8€˜ Þc8 ÷e>AzÏeX¥ U>·âÙ*zj¡ =ý¶ôA% çÏrÃhÕ¢,$šZ+ö.lÅaO„M„M±l*bg7ó„¬9ù4l8ùXÄ~\:y9~,¥LåÐ7p(„’j¨ZÚÃÑ8óSE:eœù¡Š‡»£±ðã “bøÒtÎTœÈlø;ƒ«‰R•ƒw Â(MUÒ†%`½9F>6#u£4‹ÛÒzf[—5(ð£XHí—Dâg¡TÓÃü{šÆù2±¨.ؼÇuLŸ@j¼æced+ZøÃWŒ•7£ü‡6¯”&„kWŽ¡I6 Óƒ&›ÕÉÊgꩤæ-Ir©e~È@jч~gÚòå(ð!5oÂBšl#*ÓBª—ZÑ磑Ò@˜)Öq‘C¡ #—6êÎ:í›NZ1‰FI$µã´ŸfÎ dWº5Œâ8õÞçè;Í[UœßÑ÷#š÷ÄGdjVeè'Fß¹=hmk”vÎUSP圶C.F£—QVph—´X£¬Æ£Ag4f|øÇQ¦ITÒ.%Òß…¬D]¨Ë’ÚZÂXÎd¦ö<FiÈÉe”.¨Ã”ÀBr±ˆT).3Ã5ƒR`²aº9XÖlNJižÕͪ”•^’B[~¢Á94®;\-HÚ$M¦¡Câ› QØ&;#†õ½lJ>o=©3‡Ò®-;¾±*k|EÚª+KÏ#²Ü`¤ôà « ¦QGq9WÖùRÆ?廲L´ï̃g¬ÂÔ—*òÆ¥_2ئÖ[\2·Ò0ÓXÒ*â˜/ÕO£È¸¯TX³’m3æn( DéD󨣖ß'¢J @Yh‚] ½q(õ{B–ú Ìö°žˆT(¿X*‰ ÄI L.aXqä¶€­aƒ‘á÷†+û04E ÙZ`yÍŠüŒx 0?Ð/ºg¡ûÀDzÁW¬ù9œ‚E½ï •l2‚kÖ=á#“NŸlÐä­^Äë{èRN5±‚ñlŒ^T@‰ÿçÖîC ‘‹~ÑiÞjí2…œÁ)œËìz´[ÃkR¢Ís°ÂƲq’ÛŠÒ:H«H…MkªºU‹®ãr㘠®Õ¸Èá¿øPáƒ\ÌŸ-& In›Áaõà²6\VZóöµŒXHSúfµÂÔFZÎ"6ÉŠ,SCŠÎ‹×F›Û¥È,Þ¶Aö½›Y°°€V­9ψ—4v јô¢W‡dY&i!c+«Ô›9ÞöûœYwkj’Ê×¼EÁhbYcâT³&us˜ƒztD¢LÊì¥q~3ð‹DúIêõž' >rcfBE…Š¥°*ª”Y]õ}^DÕu¹oº¨ÛZM³8ÁƲ$•u8äÕ‰ŠÚ NÑ–¼W‘˜©T&=¾s‚FN–Šïkåõ>޲VúÿÔÓ>tl§Ä{æ øÃfìSy5˜ã Mˆ’˽²vTìŠmpÉy [/7pØr4÷®‡—wÔâÂ>Þÿftžò´ZAYQ~ú ‡¬R²H%?­ueÙ>·ÀÇxmàhc˜îlí»ßïÁu aüaŸB\Õ°¶0ŠW·[øÒ%Š«Ÿl}.×ÑÇBH,»kPÛ±nru¡àµ‚’¡ž“RŒ>žÓÅ,#õÒÅ·sËó`+Ã0…2:nJ£ÇÙûª¤‚¹}8 YY÷ 麵¨á¯àkcݬ‹JuÕ™Kx @й7÷ñè»Ðí}Ê».â*ú?ÐyﻤÌJ¯x¿¾ÊéêüZÖULÈÒzû¹EK¤ $i¿«Ú °\û”‘úÜ tâeë¯6¶Š 2| €‡¡bØ6êÍ)‡ß Äwg»³ï@ 8Ãûà•2§†¸_~QqAv®ØÇõ?ÎFç‚”†ý”Býœakßè~“¸í :vO Ü!ð9ì»UÞÝ‹P9™Îb[èµ… ݵYr ·Ž%á*!\5Õÿ¬µÇ¶¢6VÉ&‹ß|PkÃ#{€ÆŽ‹HwñÍI:µ£K… p+Ý0†×ÙmNxj×<^c§‘ÍOðâç ºj|F³—`µQT‡e’ˆ™x${–Ýè»,tžËKh§ø’¡óæÿñ̵9às ¥Ëú~/dÚ6'8¤¸åk¬{v·Ù]»”ÖÏJtùaœ4{<1Ec˜%/Ìœè†6Sx_uxG˜Ý³a“3ßm˜´¸ó> stream xœí[YsÛ6~ׯà[©NE xe&¹Ú´›ÖѤ™Iú [òÑèp|ÔM}Ø%ùQ”lɉc'yØ€‹ÅâÛÅî>9¾'¤ã›¿%q0ëíìÅÎÑy/kvö~*ˆ³£Þ§^âæOÖÀ郙ót¨;¡#”ç+gxØó½4MüTe ‰Býÿ4tb?Îf½÷®ì|/Q¬”ëõÊ SßWnؽTE‰ëôA’jÚöÏ÷ƒ8tG¬Ï©m°Öó‚ƒPKøkø‹V,u„ôÒˆôJ‚š^Q¬’\/)e"^¾¡¤Q ·‹þ õ-E•e”zŠ´ÎáÉDűûÆ0ë)Å2uß2Z•Tè&܈ñ½0²UŒ_ŒŸ¤VE#ÅaRvµ­A`ÎrTè(ݹ¥r,¥ˆEä^d£ÄAê'î wÐF†alç,+£œÒ&ÍŽ¾L¥ûÁe#‘>ãý‚EiøKà á¤ÃƲnØHyQb «; ÿîiBmçW½á÷ﳑ"OÏÔ ¥)Jµ9}­VE~à.4 ©b=‡’º Ï'}¥í ‹ ß L5%£vDí뾈".’úäœôcƒ§H=­¼r§öû%}â“@#h[™T¦Ôqïˆö‰áÄh„q(™ ¤ìEÖ)*u?÷Eì •-/ÛxQ­>êL¯ â Ö X†4ñ&fr^_VexÍiŠùj†cŠ_õS/N”¦ÎúƒÐØKûÚIæ^ÒÌ3Hµ; Ç:,°þM]_ç.Ge¶ÅpO­Z¬×BÀðd*vO!³gÚª¶íNª,*iœW©µ‘&'Ü%-éùº”È™1”A/_{;{2±ëU‡„¤\‡&ÒûRó*³ä/Ô«Ü}a©¡¥Ðö|}e©×–zf©_ÁÏ *ó)íªzIÌcäyÎ+R©%/ˆtˆ¼$rNäIW·+"ϺºM „)‘ "ˆü%ŒK² ²¹ ·d§?-µg©ŸA߀úÍRO,õxÀ6¾†ÀGDîC'XfãJ"YzÔ_¾kM–Ç6ÉM,Å"VWZÛN•€Uæšú¡é“ˆ–œ`b«sȵÏÓ…åe &?ó*ù% cÃgžÍòzµ*û´ž°lde“=$IÔ˜ñ0q ¦ œÅðNE g)—i5ªhUyž1YÒÏ2¢ÔUV•Ñ©ën:Í!ئ•Yµ®×h`J"÷ˆx[¾c&±ÃâZãúƴ̈¤kY”èÊÖ 8ß]â’—%çzYå‡î£L~ ÿÙ|Zà>(d[B6Q}ÀGy”âîï ø[jV‰ÀBjc¹,{á)le¼8ì~†C0†d˜ÁÖ]"ŸB³õ)ir<^#ÿLáäf¼Õx”ò]2&TÚ.MA¶ùª¦ŒA)ÂÖx-N÷’LNµåýíû_ák‰P^^7/÷µchç+hƒ[p;Æ;‡ ÈÁVÈëI²¡¼þÖRÂö¨7‹ ,“²{^ËzsÞ¥i2ë ÷†àSŒkRKwÊoì9‹ ã$Ç—Ç_ëà¬Ê5E¥èk@ û‘ï7Ld’‘Šå×jÍÃP˜Q>eIŒe›9L]Í­šˆ…q½ŒY^ÙRÝw@'"t²B& w–øÙ¤é`‚(f¼;¦ªŠAœ"¡“ ¼%뤯ÖÛë ¤¹ª‰7½¶Ó{êug[g]õˆŸÒ”ÊžS5 &‹ñ>ª;@V٘ϭïŽé\•i£¶‘oJ™Žís Ê঩ÿV—Ä) Ð3Hâ½ ºçõiŽI(Ñæ¥*¢öÖÀBØ^QWº:‡ÁÖK0ÃJÖ8ƒÁ¼ƒža WÀâä“íà¶¿, Í1$1nç_îP~ð~WAŽÕY7‚{ ÑÄËÂV:„$¢íˆäß.4Y\`È3{, ïvŒ+«ZrþFUðKK‘‘·‚Ö ½#Û"ØY1ã³J¼<†¼xe_'ˆ¡ý!ÿê#œñÖi[‘v%à=ãÅ»i;ÍŽÃðå.ÁvÀ÷Î%ØbdQð2lÇ;V_Îô“ý´PæÒûh;üãÓáJ8®#_|·±0n!sá=ŽCücÌך¹P”Úm€¬Ü-õìßMN  .¡S8Ä!uÃ@m0qÁèG>ñÎRÍã×/æòËùÄ5÷x‰âh½Ñ½O˜­ÅK+ÞÇ(p×,.6³ÈåƒÉïŒÉeÝäkY:x°ô±t°‘p®,~g,®V çôãù-@ÝüÞ3ã1 Ÿ×| 'Tkx¹A·t|ômôë?_"ãÞâùÒ·aÜ-@‘Eè>] z°È‹, Ü 8!ü»Ùú1‘êšnsÖª3l«ÚŽCÀ@J¹q¨2¤®·lø p¸­3ká-ÓvÆ· íkµëªÛæ,³q]@tžï~"ò²ËXîv †ÞîÎRq½Ã{Œ1Sôκóbö°Þ!aq„¹"°ð­ÎÓò›ÿÂvÓ{*-5Éró–›ü{bÞ¶K9·47eÞ΋ƒôÊNpÊ÷«˜YÚ^Õ3žå7²˜ÊSÐg0¨#×}SÕdéëŽÀö’]ú3bÞ‚[=·Ü‘÷ª×ä­_Ø ÍìbmãšríM›S¿ l¾ØÕçòÕ8½ˆ›Ò;løTü>¯ªÞ v+¯½[ï׳§g•KÖà!8»8Ÿ¿6Ö¶Éå=*„„ÌaB‚p•›Åë g€Z4ÚV(õO— :° CGgý;ÝÖºJ<ú]Tf—S#ðõ|¥¹ƒYŽ«: 9§ 7iÃâó5‹/|Ó~–a8©ëÅfʵ&„ÁWj£kéÓ mη0D:4°nË’Ö²½®¦³¢µÓ˜Ûš °ÐáÌyÔ´>ò ª|˜‘è`‰è‹aïý÷˸g°endstream endobj 85 0 obj 2222 endobj 89 0 obj <> stream xœí\Y“ܶ~ß_Á·pRŠA€t•¼–bÇ’ci=.'%ûaöÞxwf´‡%•ÿðB7f>8ç®Y~hqpöñ¡ûì÷A%"ˆË?­prsðüHwÕçàè›F¸½8xGiùOõË'7ÁáÈt,‚DD… FçqTyœ×¿'ÊÌß‹,ÐqŒnÞ…b0Œ#%„ÈE•rš$RÈ0ëønÚQn†HÂ`0ªˆR©ÂÑ`˜D"—Z‡o+Q'I¾*E"±Ãå2c&lèv~o$#&IþX6(¤Ò¢¿¦ñÞ1Ž„HÝ!^P‹+öùŽÍø›³Û„=µë˜Xé¾Z†N‹8o«ÑR%ÍäÔöº,E%Q–ë4 ÏØÔ¼cÀšÿ²ÑI &c•„¿ šÒ˜#ü:úÎX9I‚±²sVV…ˆ´* m:þy`”*ƒÑëƒÑŸßUó¨H'J„—ƒØBj³ÚV Œ”fJÅiÙRFJJ•—KRFÌâŒ78 uljRFí+ÝSK£¬liŒY·LE¬ºd)²Tœ6mu&š¥T¿³^lVcTm¬Üv½1Þ(£X§:|@Cé#[ÐYåii–7ãÕãU:­.©/[–«¢V™­Štaü?bUÈ2šl¯xéEbœ?6½´Îeb¼ßŒ cóµôOY.Mßo?²Ni#µNJÍßÕ†iQº}R”ê)\Ëeå ÍZ¦•S ©"ƉF§jª$“}ŒX™®hTNÞ4oˆF ªEëD~e²lÖ´*i`Jj™PS¦—õbMO¨©Ý!ó/¦vì]^d»±¶ÌE‘9íè“Á%‘F:Wiøqè(‘nhÞC­Õ R•)® ¶¨{jÀÚ²}Ojw‰ó(—ië.õ²² .ÑÁ@hmI—}‡2“Q‘é œ4õ…ÙLª3î0W Nä¨j·P0øÛoÇöÁõ8…ö›BfŽÛ,9åïÆ…sK +n&°ýi)S×ëlÈ É»ÖË,Áø$äD2(ÞœãÈ#+Lœ ú±À w<÷º£Ý·SOjKŒ‹ ѺØîn}pª° Ÿ/0´°.˜9õ#˜ÀêSï`™6. &2}QG§ù·É%†­–)ë„áù‘Èm’a®¼ÍÌrŒË›tB–Ze&5 _Zid¥|{k¥ h÷ÚJ?Xék+½³½h$¡*5åæ ¬c´]"ïê¶IQE+Þ“ø@â„Ä+_· üúÄ[ß`gp„k§$žøá´Û̱L­·aH£îå–üÙJGVú+ú%þf¥¯¬t|d/ø„M§“x ÝÄãrËÑŒ~¥xͬ;–bÃuMó± ˜BÕ±n·°íµO¡·pb6îGg>ûÞÃ5°#_‘˜‘ï*ÊûX6ð¶ ¬—xÇvýÉJ¹•’ÏvÝÀ®?‘˜“˜€ÀÝ/0“¥ÑttÿÇJ_Z‰Žýÿö÷ÏÑ É»rˆ«ÊMÒBEZemaÄæ½œ÷¨aÛvØ eoØzE6åx øºa[œ.}„S0ÍŸÃ8OûžÄÃy:tk"JžÞĨùñOeÀæö¤5Á$ŒŽµNZ棤E‰¬hkMS‘ ©ìdMg€Œe+1 çT^1:cŽ(X¬ª‡š©3ªÇnqïó¥°àc:Ô«ÑÎî‘ן¬ý±¯ôoÙ/•ó÷ˆt9ƒ\]zÇBñQ3jŠIØ1$ ±,æ_kB5‰’ÜF)€Ó¼‡–ÑíMަb#VºšÌËJ³­O¡ž1‘:áÝÁ¶ÆF`ôâ Š˜‹Ã>Öƒi:¯#›sZFªÐ­½ ~€âh â¸\ÈF”!UC¥×lÊP¦æW3Û0iøNçæ¤Š£NF4:<²Æ ˆÚ<µRã7iœP‘9esŠ#B(B˜ F—ÎC•(,U8ÌiÛ¡1‰z½z•»– ›0¯ßÅÚ¶ü¦ @&cp‹ »d‹~3Ï]ÜlÌÒï¿»°ËÚ°Ø`0miM¦‡¤—LGw ÚE2CÅ(^Lk²8>† ™£’íEï­bYñœu{ æpÖî9ÊNÜ9`cNu' j¶Kˆæe_!«¾è€«–mS5Úl;ì¨y#±ë¾ƒÙ¦vƒ!qhùv^w¸âµ½%¢û¢ç„À—H]isŸ$t¤ àòû$†¯„k”aÑ7˜a-¦=IÚçHûŒ9±ãÄ•¾ߨp7NÙ–³d-öÊ b¾‡’Á[·Ýô.œåÛŽå•Ý—•É87ž– {4y ·yãÛÀ¶èë£E2™‡æx³Ðw‡‘¼U“í!$75ï^B’®: ŸÑ‹¤=ØwÍâb-5ܱ¢©N{½ ­ýz`õ²˜ÁéÆš§Éšgâšfc#0=à‹WTåï>Q¿^ =¥*~Ó@ë:2Ùzqš»)ëÐQÐój¾æ+?Wó;¨æ«¸ºwnÐkmÚ +Gñ@p»ðv–®½)iŠá±•n¬tæD©ŠK*Üsš^WVúwÿ‘V)`Ìáò³;õçoE!*~å¬v9:%¶—òõZÊÀnÒð9ìv Eoî‡! wc'ásÍ·†èÔ=éóÅ`ÞÎt_.±±aØ>fPU üŠS /Žã£ ¯ŒucÇŽd¿vd€Ä»ÓÉNîÝHXŽÂö>…Ø<Ý:†{« -F£'wCö¢\›²®"%翃¢çéZ Óˆ8ƒ_öR² –²)%€úûÿƒ²1°ÂnÿêéÍG@Á;G‹^y±ø†>0"Áv ?O×\Û‚Ÿ:Þ1ê<]ïuú1ô}P'óèõ®Ç+Û´÷!ûš\üVÉeü¹DPYF¿?e{ìh|Û¡P¢»:¶ÉtíUõ6LÇtÅ@ƒeCÇpÿ˜lóôÎÆ„Ÿâln¤Þ'1å¦gtwÝ>/=%V(™í5À9ìÆ”½ì ¸S…ä²ÄQ¯’}Jó>šºvq}°øì„CøÑ j+> stream xœíZY“Û6~Ÿ_Á7K)M€ Aº*/Žl.Çñj“­æÈÎ!k¤ÄɯßI ›äGQ𙤶6¶]®Øh}wï¢$V:JÜ_,®ž½µÑùÝQ5½ý¬ÖçG8uª /®£3š¨‹¨ŒË<š%qY‰­©ª(Ïèw™EV©ØÑìúèÇI4=NM¿Íä qfS3ù&@ÿ ÐJô<@i€~n ;ÚªP&Ö¥\†Qõê¦FPe:™3¸bpÉ`n¼cð’Á Ón| WÛB\¼Ä¤ÀÑ ÜŽ@H=¨iôçÙG$Vþì«£ÙG-Á²8_英Øï)b­’Øê{ŠXí„Á_ ˆ9¥àõÿ¬à ûçA ¯4“U˜EƒÚ(V~¿„ Û²[CP°p)Dpš û[BÎoß Á|Þc—û:@¯ZÌd^݃—øtw?XIï ¬WBr×cÓÎàÂkˆ‹5bÔê„s¿‚@ÜCL8‚ŒÂºº¸#¦vOð§ ÜÀ{èóð±±"´Cø!¦šîkYß>¾eéðÝ´}[?ØüÙ“±%ðj§c¦‡…ÿîa)eý$†"¢W™Ž¦üûƒ¦c¬í¶¯6¯ó1]y¸-Þ‹$x Y»?—‡ –‚ßCÜ%8›–g»8`5=¶Î"ו®â{Aeì씪ÖK8½§_ÏÞ*Šáãš弎aÌ~q ˜i§ôóI¥†¯¦iœäeí²8I [€·SJ˜•6VF4˜fyž8~øïóѱh¡4+É ýå9–†l¦é0†ÓÒ¢ûœêÄNΛ9E*©3*ÕÊ/sžÚýtV‘²ÊøÝWXbWê-¯úÛ´ŒmA:^Ó{ÞÉÓ8Ýi›_í~K‹Î ÌæÝ‹Ñèn9úûP¨¨X7”].»û'•»fŸ¯çWR¯æižåR }Ad‹Æ'imégé5§Of¼:hT>ÑT[Šã¥u«E~í¸ñ]Žì¢FVyÞª ç:ÙíØ¡‹PZ"’¢ˆÄâ:ù[m …É„™oøû‘Pˆ ¸XgÛR¯\0ÔõÿއzÀ¯ÖÛUJ{iÁÊ3Gi-'³¯4‰ëËý¾~á×€2¯öq¯l —™¸–Â×$7cùîf¶ø"/ý"h*ÎFÄè¢c1K8A,°‚·Š5™„¼¬ˆÈwRúiÖù˜JÞr6|:EÖ,xZ+ª!m3È@-Ïzã ›”Ê5ÌN ÔTwµÍ× A2§ðL#¼‹?¯¿§‰ JÞìžs–ZË÷iò^ƒ6w½Ú=³<±”J[Ñ]ãNw·;» ¨»É-¢5[z¨au÷Š_,ma\\!à€,nRD _Àá—Pûߺ‰6ôÀœ yÓbˆOa˜í,ÆóžPhÎñŽ Òù•å}–Û­½Ží0cQU³ô3)‘mÙiÞáûÙS¸0¾À= ªÒ\bÿÃw^¼ízÐqOßÜ@6®à¾GŸa„ó1Þýéf8ôö¿…ÇÇoFî©Z* ß\°Ñû‡¢Œþocô#2ŠÝ@÷—ûÆkžËÚÔ»‰ŽÈ£~åKǾbïW¯nOÁgQ½pŸ‡ËWجݫ•]´“M—Í&Úwñ87œø ë?»ûÞ.U¶°5ÙêRîWñt:¯õSôÖB—\KßtêÛ:TUã \þ®`Ǭ9ˆÎÚw¨å1Òp;a„ÎóPù^ Ö­ʞ´êø²ßc,䀯Ê}»#µ½€^°*Ç|ÁÒ¬;V°ü%½A¾ÿ,@ÜäÀ„ªž%˜±ß+„–Çc‡zÝE46ÅÞj°ÇFÍ­5ö='¡ÉÖ½þÚäš;lOv¨ËưÄHmx#å:~Û„[ ¸Û1|UÒ¶ÏV ¶Â3äf‡ºí¢…9vˆº-­+/ÜŽïôë]KÛ#Û~ËÆÂ²ü}Q¹J)Ì+oäÂFÅ¢£çîqK¼…óÓs•™Ç½HjùÙ1<Ìù$ež™à|~tZ‚û4KÇõ¼®çqÝQ•pâ38uig½[¯Ü2é?À] ‘üÇî¦a¢ó“áÛjÁ"‘0 ݇Ö^ÃÆ:+×  è46º´“ß§ÊåôIÖΩ:¹_å–Ð:áÕ:NðÆ}‘‡ÄR-gTO*”$õ)g¸Ò¯¢ô+8»ðñqCøÐ;ÖàéÚ7­ÁÓypï4«‘áƒ<Ý?€gúþQÓ­=½•ÙÓY­DulmüV#4•> Ùé—8ÛB Žº0qwÙŠÚàYO¯é:‡¸KøîH¾@ê>ÄqWŠ¢T<ÈÛëŸ^ûjvô-ýý/—bèdendstream endobj 95 0 obj 2672 endobj 99 0 obj <> stream xœí]Y“5~Ÿ_ÑoÛ½A×T©T{DÛ¬½ì±! <ô\ölÌÅvcÿûJU%eJúÔªž ï.ÀCR­[©/¥r~šåY!f¹þ×{'[Û/›Ù»Ë­þóìåßFââÝÖO[mVêúœÞ;™}¶£*Ŭ˺z¶s¸•g]׿ÍÐj1«+õÿ]5kŠ-–ê§©ÄX»¹QO^ª|\ç}+eÕ–¼ÔÅbYey^ˆn~HÝœñ`ä¬Y²nUW¶ú5‘laØŒVT€ÍcŸËú:‹´k:ÎEî—}}—ÃéÌŽæ}}¿øqçï[B4êC§8igë{>ZÆ2lÁ‡Ôª•y¡Zз4›¥^o× æ^Q"°5"O «ÌE‘Iy#n1ÔÌRÿ)5»¥ý¸²Ô‰¥þ *?¡ÊoÁ϶Թ¥Î,ua©+Ký•TÍÔy“½ ·“¾ ]g†¼"rFä5‘ç°À1‘GD^ÂvYo§°ûúöÆÆpFäèMŒ¼áê¢W{Ô/p• JÈDÛ4T‚ûCÍÈšŽBpdF9 ô‹ñ¼&DÄ~ ŠNé.B²™BÃÚâz—m•ÁSL Âva·lv¬1\–Mÿâ˚Vñ!?Ù¬è©E ¶\ZB¹¦`dÉ 0­hR£µ-Ó^ÏYÁéΠÅZ Û‡3æg™mÙ#¦!­nmk©1=±Â\,~p L1x<Ϻkç_/J­µwÅ|›Š>'’~Ç*Pëz%d´9꼚ÿ¼¨´,ÉþÑ)ª—¹’"¢¯fÓ2UIÚÃíU¥íhͪëµJ Þœ[Š­7³¥¨$·É nAl¸±¥,ÌÁm*Þ$ë¨×PÞ̦E4SW±¡u“1…§âGØì4è}SÝçÉÀf­¬´$6[óFsTÝ)¹É€ˆ‘е )sŒÎÌ·*ø@1³²Œt¸.Î+Û,¯¬ÙÆj#úD˜Øª%l×s>.} ÙØÕ3WÌBÅ´Ì9Ú]ÁÕ x±Tÿq^\ÁÉDy±ÐÎѦÍÍ$GÆÀ¬×ÏœÀƒKl\Iö9ѪÉjaXfÜã–3q¡ª¬nZ¡~˜Óšï[äq•ŸÑš‚†Õ©OÕ SH cu°°ìϤï=º *ÃMv‚Þ9U­(»¯i]k0é2J ¾íc,«0ð’öL]¸N¬­¬~u•R7±“'tš)¼:òM„8E±íé4Å¥wˆ…ÈØ¾Ø:þtwÁ]ÑÑ—N¿ó›L}Zœ–ß@̘ÀE(^éB#òNR-¬Ñp^Ú3¿Zá=Ên*KŽdö’ÿ‘O‰ÜOˆ|›*ðvñ˜ÈD~ɧNµ¢RZ`QRç¹Þì0 !×ú( ²Ã@gË¥C&ÃŒ°{k%ŒÙÏp, «QJmE Ò“ÔÞ<‚+ûŒï [­^ë©Uóz²²uúPƒ} c·ôºRóo»å"[ÒÀæ\š^ $ÏM˜–Xg|*±91[{µë^uèZl@t f¼Þ`âµd+nž;LD\yV»ü Ä\½°Ô·kB©$Œ¤z Zù POA Š {¾¶•WA¿c,K«º9‹ÂÂYïa€Óˆ\{0Lë’8ô Ç|íÁ1œBòVcí¥³“7[ƒ̺k öP”ÚeTƒ~} ʹ;/¤Ò_Úænw‡×­R€«±{°,ëø07ƒÃI²ÆÏ£b<©¹¤ìÈTÜéÿ0ܹ4k<,kª<ˆ]¦2'ýŠØïnàë/–:¶Ô‘¥Â¥£ŠQF¶‰iÕ%wáz_ð…£P+Îúû)½SÂìÌ ¼‡á3wžêøh`Q­ºÔ…aÑC8aÖÀ>ìØ:si—•ÌŠ\–0P®”>¯t>WeI<rã/BÂs†ûáSºRÁªS0˜E¯5§Uh»ÎëMÜQ~èÈŒLAì`æçøOÆ{LU”"qìtFQع9ËшðP"9°ðÏé-áC{ɤN‚TÑòn ÖÉä—–úÞR¹¥>µTi©™ý­°EëTÑ$’±9_À² ¶Sò«×°Ú vÌ6†…áoÞÏûö¶ §¹‚-짆ž§À}P Ú……2ò„ŒË™ ãŽ?}f©×–úü¶Ü&ô‹MqCnù¿n¢‚±-cÌtÉ™P€,Òxàò…݇¯?p™  Ÿ§–š}Ř<ù¿ƒËp‘¶²N’ì·e1,žØz"ƒÉa¼½—°‘›ø'6§·eRñ0L Xì~0%¿´ìó±hY'pŽûpwNà"ü®OÝ’‡.,ÊÉ€Gÿ!ÀÃNÖ}Ø.f»ÿG®€·1F2‘—Ücä2#þfÌì`b6ìÆ¢ï8²héS‚1ݬ…]ØvS}t\ðÈãO'|ùÎRäu%Ö0Ø4l4m)ðœÈÄ–ß—]†µþä‰g$~ä½§°å%üšÜrË)è" Š›Ûú'ÿ\ ~+.€–<)°¤uÐ7d„Ûdˆ R { õ¾LŸ;” ‰ÓG’ø5XlºÚ¢MyuÛegÓ¸†+…7ãn¥îw&¥>™žÿí–÷;΋£èu «°É[£¾.{ ÀU¶@ÿBnM[΋ÅJ¿y­œulœ›$yb/ɇÆô[\Ñ£ôÙbÏ^íÀÙ\ð•ëÉ<Õm+{[é„£¢¾Í‚rcÕá¼SA¹7¯¯*Î`³8ƒËð>SöOAÐÃÍòOÄ^×/ùXˆqªJyÓol¤pBûX_l\Á›çõïý Ëð Bl¶¯cÙC(”Ž­,Lš”~eZß$¬-žt ¿Û^û ñ4ËÈDn¿ìf…pru¶¥—«Sˆ²€;QK)Ë<«…­P)º, ½uä»*ße­j¥O~#ê.+e­×¤Èê¼…Ž¡2ä7šMÑôâ³o®•­CÏtsU«óìô¥ÛáÕ‹-pÑÓŠIòª̪®RbäˆêÚz+Vï˜yÆè/ûµEÑËi]±ìYJ\iºkÊ.ïCÜí÷sÛÏXZTUãŒõ—qmÚM¡–ek†’ %@uÖ ÛÓ›ž–u£Ør\Bµs} ‚¾Œl¡Ädcy›U,ë…ð3"¿¡S,…'=HL@YKñKc7zæ ‡0­ÞèE=ÈÍ ð‡ÏyRÁ&¿¬ËÌéaÖ&¹²Ìî5Ϥp=Jå\¿þ`>‚bÛƒ}nÜÙçÆ®J!NäŒaeq/NFžáö>%`ÏÔü÷½²áý‰ïw²íÆþÞþœz9Çü_’ºñȺ,£!A[®x1 £ñ8ÞE‡ç4Óâg†ÉGÅLœ²”·/Èf¸aJº»8o ¬ Ë<«Z ÒKž¦„Ik'¬éO“^ÁTÖ™¬%è+Ë']º,y¿ÈÂv¡Êð•“äJ”:µ³#Ïá>a®ØÄm$ož¨Ô~,‰´##CpaXÉÒ!e%8PSr xÏ+Æ¢˜ÙÁ™™YÙ´³ŽÅ¼^u„Ùâ7ùóFpo°§‰MÆIÝeÍ¢s¿3ßr2›˜t&'öÏu”ïgücMiÏ®ùÌýETt]¸±«…YFØI·¤VMÉg{#+j†Þßi’†µG°Ö;yq•˜c ìóq޲5õÎä&2V­Ý Ú&7BP»`îht×ÖY^T”T9HœÜƈÄB8f‡Øw —§ky›ðÒ­,#ÓŒ§*+» EÛ}jB%gIxz2pq÷_^Q"&VÿÀ&í—Vì9Àå¦@–:ªƒFë:ÛÓoY¹Úeì+ÔdÒû¾jSÌ™ ®~E¦Øvª,Óh?g'ªÞg?…Ûš7¸Ð'þ¿Úõæ­‹–ð•áØCeŸ0é0Ó­«ô8¹èSK³páæ´Õ‰Ž'üÝ/|–&Æ$,Š ÅÜ£÷sÙƒMÆi—$ƒää'vŠcû-Ðö¼?Jã]kpì œÁÐÏzµvCE ·–çÐá‚:=@õ?N,‡ŠõPñ¯I¥ÝhµT@+cÆvt)Óvô&yÚàÅùæjt¸²JÌÿñOÖ¬Óï `—™hÖk4¬ÕøßÇpÂü¿šÇí0(+ŽÞŽ+Ù¹d&a/B†+Ö§;[ߨÿG(qpendstream endobj 100 0 obj 4724 endobj 104 0 obj <> stream xœí]Ys·~ç¯Ø·ì¦´£¹W’*êˆ$—.K+Å)Y¼DÑ!¹4)ί03@7€ƒ™å!¹Ê>ªš3\ÝhtÝÀþ6‹£$Åò_EìlÝSÍ/¶Údz7Ozâüpë·­:Êä?íNï̬ćI2k¢¦œ­>mÅQÓÔqÕÕšÌÊBüݳ*‰£J8Ùú0_-–IUI™Î?/Äã$Í«ù¦f‚ÊŠ²Œ³ù£Å2Òºª’ùÓžl’ùO‹e•yóD• ï?‹–ñGÍwÚ÷YWó‹ÅÒ(Å^±§ç‹eÅq’6ó5Ø“ßþGTÛDbyßíöû€Us±«¸(æ_»Ôy1?Zäò«&Ÿ_Rɶêϰ/ŸÚqTInôÅiPÌ{5ß§¬s—Tîò‰è].zZ$}ïäTžRQøðö–µõv†•=[|\ý¸•fe” ù‰g«}! ¢[I%ç·æÃ=„ýfÌCHRñ8«†FS&Env\|”§uÂ?bÍâùŒh3Nê‘?X,›ùï 1Ž$‚ÊÞà ‹wHj¯¨À±“™{°#  ôK¬ljµÄ$ù^ö>¡›yYY”zÂÒ4muã¡ZJ‚³lU°NCüÞ‡£©Èlµ|Fó|F‹ø¸ÓEÅuªt-÷=.¶¢s©èšÞ³Ò‰¥C—Å-°ÏŒÞ i4ö ]öÊy?šÄÓhôI…¨W 3{íSdštvëmþ~‘ÉÍ®I¸.Á“`O¹ð$í;Öj 1š\ÚYÎÇxÜ ¡*õX«sGS_aEµÓ Ø¿Ë2SBÃõ4WCyÙDUnª¡Ýa“eGkŒ}ýú×®ÓU‘öÊC>¼j)P;TŸ-£‡—­ N¡dÖ‘.@,±õ²Û îÜ._E¦‘æWcæœÒ1U §aû(ëø×…`t‹Þ°µ÷CWg'¢z)¢t>[=ßþEX½i­­^aÝÖòÍê¯äв8Eóù[AFE%„ñ±¦Všši꽦žjê´§Ò’| ¾CY'0yÕMšVÝ(ò€È‘—D~•}DäS"ßÁ²gDž¹&òØ!ò’GDžÂÊfð3Öð~h˜;°‰}XöœW&å(O˨Nµö9„=g\¹„½9eÓ^R—ª‰e'³²™–Ë< –êÛïY,ßÿ)–w*–RÀd´ÝZE™Õ¯[ýNÛ©Ræÿ¿Ê8.›´.î¶43ʦ“Óž$½ó”¬-Ð~­$ߦÓþÍÌÃ"Ó{IÐ"ÃVrD=À†Ø3*pª=)0Æßh­=[×ô[”‡Âü¼TÊo‰ÅâfnË=8ŸÌÃ|Aú¶6Y³d‘cÛû´G ª(M*%§ëÓ») Ð%7­íc6•ßôçØ»¡J1O_›ÕF ®BâÈÆ?Ê#ìp€úzûê¬GÛöuÝ»ø¹m½’Ö{™KÁþy±ldûqÊ]” B`«è­~È\*ù†äš½góÂìÒ+Ï`-Î#axÇÌ—Óä^Ë>Ÿ™ü¸Ø[ JÀwË8Š;î¶üYYG uÞŽ¥³[_ÿùúkG\sßd»€òÌž~;GxŠ«5äÏ];•c©3ð»'.ˆ‚ èïÖß…ó†a”/T†æÞ <ý]1š,ÊÓ¦:²™´‰êZ5–_Ic¤0tùÝä‹à¬kG#8Ä~ŽfCl^zÕïSUp…cŒÎò±ìÎv¢]Gæ$Žáˆ}{`ãXóÖì1<»›´@lb¤Ü1õk5(í¬]±¼ätxQå1P@¤-h (Ò±Z%7Þþ†À=(ox¾˜Éd ÙlI")Àx6Ú˜‚1ÄX,Ì41Ęé¿5œ)"U,ë Û!ñ|ÓÀÜ5Ћj‡Z*y«êù×ÞBâRç„IkÐ’œNÄ }ê쉚¦+°XGuý|rƒÈã!EæÎ,/‰’âEÓ‹…D^ô!Ð,Q"˜ôþ6˜.€ò‚ktß;ÙUV ÈÄØo÷*iaÏöu0´÷omC²š¯ 7q C·¬,ÞqàÕÐÄÀ= ¶ZÇXT–Y:¸?rZíÛ/‰`HÇçÔŽq]#Øà,uq&ÓÊ\õ#£VXâhj<•y©@?×¥ê0•»‚ªy¹—šz¥©7šz¡©mM=7äNò¨ \&d@ØÂT kcøÙqG|Aäöf8âì:Cë®` `ð aœOÿ | j&dØeyÏhi[U©‡Ñ˜»—p¾§ð†Õ°†0X{k`Ow`,˜ì3 c¡˜ÒâßÁXa˜&©Žm ìøt-Ç<_HÅ'5·¸¡ ’AÂ!I­,éá¹!§p“°- m±õ·/-ô‚>dk0£Ò!gÈ0|5‹- ß–ŽN°]¼Éb˜!J†{й֕xÐk ’´Ç±L,œ£†Ý² ²Ã3:ÐÜâ9Æ`)ÛãþâxxŠl@{h®8gM5_+YYœ°‡f­‡#/í»¯jVŠÂi.{±)£8Ó¾tDÖ2«‹ÙÐ#p7æ¶Î2/²¨*:€§®»iÃZŠÞ>d[aÇŽÊÌJiÿ@üY¹cfJ•"§8?­Zós‘é#‡]"†–L[AŽ0»‘2ŠtµxбßÕ ·ë[h¹9™KÅ,#Ö8h›ÅöS›È×8CŒÞþMSgšZkê\S—šú‡12–.{uF3QÏ`f‚L #c£+7g}ÀƾnÍcaè+Å ä„Ð,ÃH­¥‰!úAMñh•«NÈ2@d LÙ,¡cúØ9ÆeÙð;GWÈ!_ØNÚiœ–|:p⬡Y{rI *ÀÈŒ`öY»Ÿå¢uIx‹¥K[3#ŒNÖÁPè¡·Xr‚ˆ ‰ãdþ)jwÃD_Œõâ×ÀL„‡„½ÖÑ}*ʲ(^£Æ(ÃÀÓ– £`„м ö5,×. GY•A#ˆ‘Ö[”QR4vÀÜõÍLÅÑ"†6R%7ÖFn¥86à3!,ÛîÔ€OÄq¦:üzü:+&ÊïpPëf<×~zÊ$õ€Æ0„Þ'ÆÇBNâšìÓÍS¤E‡¢Ó}jþHäÔ Àgâ?gö#ÐáÎl“2Ïþ )F±[ì»Øp ¾ÆÌÅŽußz)lÍ% •“ߣ÷q7¿Í0S°{VÌblBšÇ^æ(æl¦Àa?·;"±èv`1¨ Á¿ŽW€Æ>d·'QeÓPà ¥s°½G ›¢ ×ÝpžË[­Ik gSu ¶ªê<‘•GÈ’Ë3gm1V5Ž„õLæÛŸáh —Z%´Øg—²*¸à-3ÚÜGM]šò¾é–8 †ÂTr¤íçg)Ö°GƘie£Õʘ£•¬¬/;Yk=¸”µI}J`ëÕžw#À‰X€Ã­¾Ì(d9\C,Æ&1àí Â7RÌÜ üß—íÃFx×ó'bHë\Ÿ‘$·ÓÍC¢¯o[#°Ù¤œ/œÑèÚoHœ°ya«f,~чÈq(ÜÝÜäWasà jµ_æ4r˜ÅÌ–…ÏÄv¼|vŠ t ‰Jøs¶C+³Báw°.qÐ&Ø`ÄvY Dàcƒ8Œã†Å|w8Ž‘µ°ÞP·Ü³ B ðX™ ¿,(¢ üfžѱÿ¦s xl”üƒ¦P:ÄG§·@g^ Î` ŒìÊbÇ»­e`ìýD;¥² O9Àö` ¬g ]ÆIº“wB1V8¨–mðÅM½ÓÔkSLþ Âìd÷§° öiàÊŒD)'Y#tc©ã+zÿ&4,‰:vË©öüÇå^ÑÅ8dÒ0‹(”´Ó;LبÍ{dgÂFœÃ›Î¥ß Â6'Nú­#r>xÙ‡^à‰s)‹•„ê¸V2^7ùêϾº‚À‚qCO!ô§äÒ:ªã„Nsê‚ø˜ž“'j§gÇ[–‰êo)UQ÷`¬.ýζ˜0D4%æ°y·ÂQ<‰ >¼E}Äã™r’çÑ!(¸çdêcE$þ7á3ë^ 'ÊÓ(¼¹”ñ¡ËŽ qäb‰%æ}Ác2äDZÈ#Égckø}Ô¾(^PÁø} +Þ.âñåÌ1dÂRsc!æ׳褘t3öH°¼†3ÄP?Æ#¤£,ý‰7Cþ¦ÏåuOþLXÕœ‚üÅ)pÊ­ "€°cž§w€·” 6vžÓPæÚßcŠá Ž×ˆÃ‚›€l€¨žý…à]¦`êÖ)£T"Ç”ëÆÐêßu\ÛÔÜƒŠ„ëþÁqöJ›°¼À‹áø  1·ô©Ž9ׯµœŸ2ôq‰¦v¾º€\•:©•ªâ‰ÖJj%:x+Ÿ±ô§à TB´œÄ”Oû–<ƒÇD® oÁ.2yêqY4ùŒHÖÄC"_ù’¢;Âd(ó.‰M‘+™%)ùífUÅi”gÕh…ÏLS%ñ´ [|Á™¤•0u‹F£ŠB¼Ù†3kð†ÍÖ2Ï¡©äÉTh® Ž# üS9/)E=¯“Š@ðvBú%RªV /Ôr>.¥ï¢TÝitr‘‰ŸüSê&^xV¯4'»ÖSìRNó:UAé¹sIå ¶ð#[ãÊCпF¢Kôö!¨å% ƒ/Ëz6XË[§]ï‰-Ö°šäñ5êáPø0Fý¦€…S££PeÃg³6缘mÄztú ¥"?ü‚Þ>åL†§¹°[êê޳ñé¦`1+°˲†?ÁNžÃ²;°^Üð(H#D±Ë8b nW(—ª5¾õ\êF·Â!™»UõwMkêHSîÔX|¯\òºâÑ…]ç#C&Z3½ËŸ/î£Æ2<å:5O8ž7L%š*7–©lÜ([Áˆ»O±ŸñÕ3Ø0¥ÃÎʤLK`L¹ölwxÖ°D2•ð­™½4–jÄË~]¨›”orUÐ x )Êæ «66_×Tn¯ŠQÊ ¾eŠÅŠ ý'Hn,¨`U|O"›ßŽÄ>×ÔkMÝ„ÿƒHìw±Š¹\XFðâø 6–Ø?õøw¡ÇÖAi (åNŒÔ|Óª¿©ÇK,(ŒÄ—&1Xw Þ‘$c>Ów¥ÝI>¿‰‡%úvåáø§[ñ/=àHÄí‰4cÖÇïR¤­4W-ß¾4×d8¿Ž6æ6÷ð®ù>ð Ow)·°kžð3|ƒi• °›£ÙÓv(vÞ{ëßädXÔÚ?Þ^1x¨ÙjWœÁǘÇOSMM(¡âVœ”ze`ÓȶeÞ-?[.Í_ë_ÔŒ)§Ó¹¯MY ÞC{ýëÐÐݰ7‘Hh]®`eéáùg_}%ØŸÁôeäú±öŒ\ã6pÇŸöÈ"ï2zq k‹æWöJ³Ëº¿¹)E†'ßR¶œZ¶*Ã;«ø!Cþ†ó<<É!ªv#Ëœ¥o¨Ÿ£º é'œö5xÞ-y'æñjë'ñïÿ¡º¶endstream endobj 105 0 obj 4599 endobj 109 0 obj <> stream xœí]is·ýÎ_±ß¼›G3Ì•²]EIvìX–,™I”Ø®/QT$’¢HÉ©$ÿ=9Ð àaj­JœÄ)<‹Áàè~è~Ý߬Ò$S«Ôü3Ž^ïÜ}Z­Nßî´WO×®NwÞìÔInþ×>å£×«{ûôb³ÊTÒ”«ýç;iÒ4uZw¿g«² ÿnŠU•Ö«ý×;?¬Õf7MJ¥T­Ö‰)çY¦•^—‘çùf·Ijj"[¯6»ªl’\—ë›Ý,)ÓFeë/LåF—•jÖÍcUkµ~B%zOõzo“•TA7뻦­¢®ukàIÛ@•Uzýˆ¾FÓSæ¹haÅ-ˆª‡TL“¢ÌÔú¦ëAžVë·ýˆj]›÷ìˆúïјôúº-Wy“Öë¶ó'âÍ+óÅ&Ë©x+È&úϤªQëûm§êš&î¾ÈMÈ×®ÚæòR絨ûªkEðªï¾Ê ¿söE0ܶ?®Å—žysM³¦«j}GÔ—ïºÕÚÿýN^d‰Î5ÉÖþ1IÓ=ä›þ;º6˜ÊwŸfÙªq³Rž`–JªÒÈ&½´ÿr'£öWûwöóÃzŸZ/“*+-NJEë~bK+*åEY¦¹|•¤iV–ë3#,U]èõk’ ¤U^‘X /½Úh*å´ööÙ57tÑ>ÌÉMnaÚT­ØQ¿ªŒ$ð̾/š¿ä·¸ý~(ªŠ§ç\¼v‡"‡v½!ÝÖªÎÖ全©e7EÕ]…ZFÈê¼J)¥­KK^&¥.ª†t*OÒ’”Êhå08§ó{Õ8/‘Nfš¾Vëîý*m2ÙQõ¿zÃÅ~TîÌé¶­nš¢è¼}éD®‰‘+Uv(+„ç°ª˜RÑÃ#îÀEÛLY !r©êc¿Ò,’¾Ð8cµujz5rÎîÀ.ûµîÙÙ2*7´•ÀwEñk.žóÈÏì ÄÀφ!Àa÷…¿o²*ÉtZø–Òë7u®$ë{OØÔWÈË¢ì¿]Ô9S¥ªŠ ¡2 ½;¬´i¿PÝr3œÛëòa?¶ÆÑÄáÙ±­‡çúÀÖä¶¡®=cóצÕ*¾n‹¦J´uRj9ÿ‡ÁRT…êçB—µljRs‡r]£c%ñÇÑõ3U%ÒÑ„@°˜NN ££“-äY’ÖÅ€üYñу羨¬ícØm}KEÂŪÖa iæ}8&±rÇüu±Hb '„¢ˆeuI¯c°g‹3`o˜X»ÄöÄW‡m¯5Ä| ÎÆô‘*о™))bFœ\sÑ™Q·Ã”^b$¦û¡„!€õ”Dåd^”ù€™·(ã–ƒ×U€Ýyšõ}ì{öÐØDŸ¥jkFå¥Ó{²»ºÎ¿ÙÍ“‚ /³ë÷¥}[ZÒ3[z~ýÖ–ölé¾-}fKŸÚÒk[:°¥£¾D ° ~>¥+[:±¥·~)kòõçC±5W•¦…¨Ëá%Ság®ðÓS.Ö\wVHaÅÅ{°Â(:îMµp‹IEo(³|eKméO[•ZO`Ýï²\*ãÓ’`.¶ç…B#ÿˆ¿0FqUœ‘èQ‘ MI5ð{‘ï`#È,êzö¬¬ y<1ÿ™­¸ëžFPÞ"NÌy±¸xŒ ?±Ñ~Lg[ÈÓŠ\6ù0äÅö![áÛ~µh‰öºí[×IÞ4ÃöÛLÑzËÍ4fÙ>~J#'M#0û—^ ±Ëß=€»e?·]]„+†EXŒæÝ¦0Šé /®5õ±•ëøSËLÔÆÇ'¬†Eà‘µ‚èYù½È¹³³UT•ðU{ù¨ª,ât°¸%ËÍ üÎë!±× %/¸«™½<]6–=¶­.ck0Ô=c/_í'}…³•?ÃÈ\bǹFf ªIê–Ôl…Oɱ/u%9—Òâ+!0ür±ŠÅ€Ü~!å§ukº6%uAÀDÊ}ìꪒ´\TPÜÂàè· ˆ‘KX ÞeOþ<¼~ϸ$ðE­¢è˜Ývï7 í®ºUu°cˆ…~%á-¬ :˜~Ã`³þ º ½xDLzfÿžs…\0ãÌ#ò\àοø™ó\¶IÛ#àÐæ.5ªCZC˜Â/áOoQøk¹¡3LÅì¡oÛàïlkÃÎþ¹G #d|¨ÔZlÎ`ÑÄxÈuY±J-ÔIJW¨6Q=@ÉUÖe»×c1œiÒµ Šu]XÂtšÃ£ Ó8ƒL™e[™M ›VÃÌTµœ|¦"{‘+I(^õª X²Y “hõÄŸ%RºÈúD„ËdÚA`{IŽ€3yËäúSbV>1CÍtîDJ°5áYºë¿1‰Ùõˆ0}·yàgW\(-´Šx@uÏáïP}×mÒtºA’…Ù®¹NÈ,Æ'K“ÒzúÆGOùÔzë´¿ûg[ú|ƒ  ØÒP¿ñÐ ÿ´¥˜å@Í<ìJº®†=rå'.þ °$ZN]|nÆKÛ&XæÍ5¦Z2–+5ÿgWþ‡Ùì€u{Wž4…'m÷ï_ÄòZB÷c#éïÈ&€=ºöCÏÃÇë_@ªB«øZ®â ðaÁ ˜—V˜ž³â}'›‚ILJBæ­ÃÇôDL+/ÒB ÔÞÌ }°s-gRCxŠæ#€õ¸/Ñb>AʺÙî±¼\±Øaq†F,q@‡dµ%Üö}bEÓ'CH²Ëó§hÛÉj#½GÖ*Boß‘6‡ê´Å6€ù ©€è±cÚu!×<)³U°†`©g_$¶#x?â5‹fqªÇ0§µ &n2Ë6£ˆ2g¨·Sˆ‚Å+ŠhA(Å%ãø%woO>¼…w® ýRVT€Í܉‹Œ°yën·G`ÚúDEÌå;1¨³-ü aõÈ÷ Ú×@²¦˜^JÝ—Çüs,ScÆ–Iû„ÆÖ¶=ü€p·¸»ã2¥?3Qq„8K[Âva @ž™±pØa24ëèÓì¤Ëe;¼·+7wüŒœg*]D¤Žeãü¾Ð¸ï<5»„$Æw4¥$o{Ÿ¤IŠT±åÐ˽à<ÄVÛONÿ·žoÿ>o?ë6}Égb?Ëí1]Êê×£$""gƒVú•ë"õKûK?1‹xV5>5ÛáMo?51.5ù/¤SCB50“ÐŒ±ìôJºò¡åô§#0DáyGÜ{·Üÿ Üûw€¦~Js“ ¿„õ@{û̽35¯m)aî]œgGÐ .¢“ë85ú£PöófôW“êü«çæ±V‹^а6<2ˆí-œÉÒ…+Ûí5kØ*ŸÖ»Þe&«ž7·9;bMb€–mÆ ¦²«púÊ8„ŸÂQ„Æ…ÓùÅ'ÄnX7¡ßÛŠKZ'•²òâ¤pºÞn«$1oÚ¨ýQû:* 3Æ\U¯Mÿ€X?q(â.N§ŒÃIžcƒC€‹Ç}" Xû{x ‰ÁíN£GÙNìÄ7p@1Eèx¶:©Ó Ü?7ÆKû'±9ãpJœ¢ö×'žá¬!œ8iÒ/¡©&Í R‹ŽÝ õÃMJ3Rš.:uDš`ýÃæÝêOòðol‰ ³¶ÁW  Oûj9Ã¥ðƒAyŒP˜fD&½ÿt†±dÚ.·ÿŠP!b ¸7o¸ˆµv†+3 Æøµ’ÕBŒyp€sÎm~Ü”ö{öÀ³ï·giO(‹ík=ĸÖ3Ì©’ 3Ë™àiA;yvZŒúÈ;ô\N+¬+¾VH²Ò Kb pß„fåÚö°Õ(ˆ‡[3Ôh>Å× ¹;õ?i‚Ú þ}´#ŽQ,Œ<CG¼%-»*gÌwÀÝ l`뱫Ú8ðˆ·w]4vD`´Xd›b»rŒXøÄe·¾TÓ~¬…h>ÏÓhßR°'nr:çÝÅä#’Öw"#‡ÊFpé*Ë]yKhküNƤ-îõdÏ7FEl‹û¦vw›ÓÈ5¢éÕšX »û`#jÊd ¡ÚX@¨13 @R–üœ=uäù‚¿õ)½üQÈÀAæÏ}`lV)¨Ç/ßKzmK/@{Ìáò%©Ç •wLОهG~ECûþ•kžsñ€‹¯¹(.Býœ‹ßä ɆˆVM^Ú!’^N‰üWx…àqaeRäKŒ}iì,JÊ×4MÊBk þµ©úkƒV7"l0Ø@°§b÷YcÜ ¯V$ʨ5æ «NÒæYH‚°Íóáú4yžÛ˜Zº±9±y®‰=n?ö§iòpÖÙW¬ü ¸µ*hîÅ)<Ƕ™¸­…ê‰öC°Õív0óhÿ¼Šdn+cüL¦Öã[MEoƒ,/#íΙõý4ú‘Þ/öwžÐ?ÿ¿/{endstream endobj 110 0 obj 4999 endobj 114 0 obj <> stream xœí]]s9}÷¯xa†ÚéíV«¿(Šª$û•­° ‰­ZxpÇkˆílìì.<ð' ø½Hý!IG­î±ÃäEžQKê««£{ϽÒ|»É³Blrý*œ^}ø´Ùœßõož~:Þœ}{Ôf¥þ×€åÓËÍÃcõ`Qlº¬«7Ç/ò¬ëÚ¼Z-6u¥þîªMSäY£*\}½ýj·ï²<¹Øþv·¯³ZVyµÝìò¬¬ê:/·'ªØB6Û+SzaJP‘·ôpWfyÝI‰U¿Ùí‹BýÑNÍ—"o¶ßíöÍöÌþ ÜÒgyÝ—}…¦ÝöÚVxµ“ºØIUÚ7ªnS õ½iëûÝ^d¢­»v{aª^ÙçÏmUèëÍn_©w.D‡ƒº¶­Ýmž—yoOA[c]QvÛ_ uõc›ÝŸŽ??R-WbsüäHýý35Ý¢5Ó­¦µÕßÿükõå¾ÌE‘Iýû2«šR*aO¥KS:K¢ÖÏÔy“ý¼šÇ¡xcžº0¥¿-oét¨Pt¥–üT¼¤Å3[¼²Å[[¼™ŠÂ F´F0ž ž™Q>0¥Üo!ôª‘7/ÌSµûT[ÈLÍX($o¸'ôÓ *xá má-¾¡Ò»¡Eh÷{:†7´ x,'ò—Dû˜ä‹÷’¿ƒä‹¨æ/½x/ú;ˆ^ÜAéË÷’¿ƒäË»(½|/ú;ˆ^ú¢_ ñê½Äï ñj!Ì<5’{hJ™ÒGšó–]iž©9à¢8¥ŸÂ|G+ÀK?·/ýÖ~ú’Ϩ07T®|¾Ni 0Þ×T©ø,BǯhcLoS@öÕ1¹·ôý`y\Ñ—úãÖ–¿‚w¶|IsK…øÜÿßT-óGfþ,ÔNKÿž–»hÜ{Cë¾¢²XÜoécÚ1Ìèùbqs¥I­Çß)>x׳À±ò”¾JR ßÕÞöngÁþc#¼/Léø á«…,š¬uÐÆÊ•à Ak9^$E÷-mŒO o`–!(À¹/f‡.,„f 'ÅMz*¾´Œž¥Ï ô›O¤éš'#7Ö#Oۊצ"§Ë~a?ÝØ.ŸÛ P÷T3‹À4¾µÅ›€tdì!¼ öÂ2‰ÃÛ¼[ Ô©³¢ËòFÈí_w…Þ²rŒý0¿¡Œ Œåʲ‹Ã§¹ZÞä¡#XF¹‚hßÒ~A2ô #D›u¢Øä›ãG_ÀÑvUÕSņ$nœqžÚÑ;ï̆Ìå3ò»“‚YEõß͙ɩª#=3éÐÿ5 ÔµÝÚV/5訉¯ øÐÑë^`uÑŽÜú ~©`Äûïj òBÁ¢ù>³ßWöÓ_«b^7M+ m•¹´v%'Å/ëªÞ~h‹7ƒ® lJ1銷 íŸ" ®i±tæ¨Pó¢i²®kt÷{)E&«r³¡L"Ð픦%eu«-Z©”Ø~lÂãøbˆ4M¡Mƒ)ò¡Ó€®ÐÊJ5`*<±¯ ká ˜ôÈÑŠëÁÔ}eæÊ¾\<ì –9(5ƒkúìz è§0·ugµÎ[: fX»sºæ¯¢—ƒ²–jlK lf9 ÚQ+bÐŒ°æè_ž³èMǽ²5yW¸"e«[µ¥öµ³´ØïU“ŒÎ0ôû€õ{ë Äî7þ{ÈzK»½¥û-ß>áÓOÔcµžáí¯dNà±aùV¹0ý*€‰JTêði)Qù‰×dt·Ã^³µK<5Àµ<Ç7sp«Rm Ñã–VD'*e`<7he ,c¸ Šº°;µjÿœ,`¨a‘‘k+G g˜÷å;ÛOÕ;t¢© œÓ3œɵEW±ˆ¯5Ùše£Q’ÿc˜ª gÅqãbÆ­ñvÅX…7–aXVÅÁ>€‡ƒA7zŽè‡ ?Ïd+‰'ƒ“0v¥ó/bvµyî‘Í!y:šŒ­þPŒ@ï µñ8r‚˜X\[™]²V_'Ã*ƒ‰qN?uö £ÄYÂÄÃ{‚“ .wh­Â•hc-€~wmÔ’Q%R{õè—M¦ÆiµÇÙÀë6+Z×ñøÔú Yá1Ý?q·¿™‹2¸%1ækþÝKÛ°®Þ™vô†¤=ÁþÏS¨NØNMI$ a>c{C€Œzk>…»ð2¢ rð²lí4ÂgZ¥ÎŠª&®2§s\'l²3ÞÅÊayRtAYXØ»ùºæþgÒ©p^ƒ– Aº9½²såXˆLжpq ˜è21ØC qîÒE–IO’”Ækf½Ø¯©g`Éךª"¶ÏóÞ:–¢k(h¼fÖû Š­2ùA[ÜÃæ™qcÁ’ÀåØÃôzK3âùŒs gùµpºò<Ñ ö#jãƦ¹3ˆp¡ ¯|?^ƒ2A®­îŒï×|ªAý)¢ºÒœ¶¢QéÜœå`c– H  ¦8‹-Á©È-Á˜8ï@ö‘ŒgúB¹Meí*Œ¶™‚=¡'5NüI®”ovcÐÙ(‹¨¬´Û†°®ÖµF˦U ΋ëJ»®^œÏ Bù€UFÀÕGæ„íb±mŽÑá¼ë~­C’Z‚"œwQ+wbo•ø=-µÔF‡§]k™Ìu‹/ë5†hòôFjn,´ö)'˜`¼&7³!Œ,>²Æðr›z»öñsSZ¢¤Öåèô2Éñ YÒAvú6›JÄ´eO]{FHÁG‹¤Eã9îçTE¬}Ì•%4\\vü;Ólç&ÆhÃFÔÌQ@#3ÙuÕöjUY[52‚柧ˆ…¤Ý”n’©múŒábÚ§¾ÛUz¥GÝpjSßZ¶§`yP¬"¨û=µžcÞÓêÅ¿Æâuvر úçć}¬ËºÜ>1 v£ƒm&”­„{ËXÒ<€‚}Ì’‹6ÍÁ6tn>£¨”œ™¤M¹Üw1^‹‘¬[Σ[w7—ÂÌ‹•6¸ç§;çËà=ÌôÓõÄÁ}4Ž„¦ðLH»«AÌÂ4ÙeÊãf òFl¸¢Êâ¬m[äE»”(rn(®a¨e~Ñ+›,MòïÒ${7’“˜`-|“h0—fGVÉF¹ô)³²3Ù1Ž]ìÑQC— ´ÿg¦ôÈf긆1øþKYmx®A!7Mø³ëÜ𜌫r4¼À0!à4ëB¤7¸òpIÛ=­ÌK †»ÏÐ~TPr„Îöhô»“øŸ÷0<˜¿š×Ì»Z½ïžÚJ<ã€3z+¨.›8;¶q“4à`zdrÂ#“ù,)VSÀ÷Œ¶T ™0¬SN ìÁ”*³hé±ò7l_½ÓŒ@SE$L2Áò‡qÒDƒ™ƒä;m7q\ Ò™<îÓUjÑ©ýKm4 jðæÊ˜F1ä1C ¬=ÝžP£§BéGÛpŠ…VL[¾áÇù­Q•ovù¯ 2Ö?/hSœ[Ah®aQwÚ â÷Ë3£“Ÿb³Ù®8 8â£K¹Uƒ²4™([¢,¡KÖŠÊŸ`û0÷nËM8ž1ä Ä„>4ýˆF]gðáÆ³HV¤hp‹Eã ŸÛ$ã·€·r=¦&¯*²1Œ:4oð-’Í;â\Êo ËøÆX×jYLjÄæ(óC8ê$óó3½BîV1s$ÂÁ°eÉ,4;É– æ¡…±èÜPx1Û1¤ã˜Pg ÁÉ›ÓKDºÊ•ᛳÄ?hÆmÒÿl{Z>KxƒÎTYUU“΀ ÅàÐV6e=ò Ÿ´E^ÛÄÌ­I1³‰á?°2×H8g7¤¿çÔÐÖ‘Å ­Œóø¡Ÿ¢Í#ÿÈ XNoð4g4Í0¥ôhq7ƒîì§¹1Ä<â½ éç­í,è­ M¸y|½"VEVÔÆ,Ç>RÑÌ×I:Îòßî9q LG¶Î“Œ5ÈŠŸ]ZTáþÔ¥WMåKÀôPšz‡j.>îÇKÁ(òR:FÅ@£'7ë]˜RiJSV‰L ½‰§d‰&@‚X´ƒá(icõ ¡«£¶ -É@ÙCþý`ÉŒ€˜©·ÔÈÇ’Á¨@®­\ó' Ƀ{×ÈžV‡mžˆ!X@‰ÑdÖ-ͪ8GÁâj ¹‚Vb”çåÍc5ÎAšmMk9D"”‘‡šÜ}pØf²,‚9•a$g‘¨›é6:Í6Cþ¤+ê xQƲ^Û™$U2[àl±PÄC;t_2À ,ï{ÂÈ#éK)š$Z"г ú{žh$0l§Kåþ{Îô‚rb­æMÁëäôÚLâÿav‹Óä&Üà`1“ø¡ >Vˆqlâ“©ö-4d‚+²é °½ÀñŠU)o`[1ɤRÞdéïn þƒVˆ¦ÍλŸÿ¤:${Ô¹ŒlÍ\ä1#ÖU€Á(´Ð;wèèéá®ÎÙ…ýtÌø.ˬlbÐܨ18Œè ç’ìCÜ%œ‰Ì9W¯²‰á&á-ÈõVµw²¼Çœ*Èx C$ít‡…)F1χ™>IZÎaš†ð`ÕU¦IQ`»u¦aŠÇÓ!7Ët².‚ ƒ¿¤9¬8F‰Øn|ýþp‡<êXšÒA[çœIè¹ÿA¸Æv‹òdÆ-ÍOú¯±Õâ¦ö*=lE<·wB Ã;ôY°°Sãð3k\²–xF@rOôå¶g}f² Ìa ¢ÀgÖl ÂM õÄÏúGÞŽu…ŒÔhBGÓO5xñ³N)è Žá•Á«¼j¸î…RɃcœ=pçc2ßÖЇ°h"$ÞÁ©éCâ¼'éDOf|Fe.¾g'ô–ÝKoðÒ„Kc&åy7i³èE aÎ$ll÷–§Ïë:–ÄÀN<ѯït?ÂÔêâ½.Šd®Ê˜}¸A~é`‘‚Ÿ÷ ÝG¶®vƒ-e ê“®Ö!Šóc¦¼¢0p4gNhü]cN$lÂh) ®¸¡…¬Â9«ÁA¨íSÍåÉÃÉìW~¼`¡ïæç~q†g¼hªÀ¡ èÄl`Òj ±ãºÎ¹ÂʽޥÌêÒÍr¶oÊ*•ñ¡éÅ€xs×퀆¤1WÕù@ÆS„Æò˜uÅÓaU@¿6™€;($ÊåÑoþ]ÔìšÎÁýX²ßÚKU?û!§%ÿEÙ‡Ÿ’oiJ/MÉþ`Æ+S‚ÌøUâ~J(Â%ô¾G~½2¿*<°Åg¶øÈÓ"´À¯ËüTH\x{ïsø0øú¾çPš)âÎ?Îòë{¡]¸X.dæ—¢?§C]¸Xn4ý+™dz¡î½ÏòãàëÿùYæSÀo•‡v/i ç©‘Á ñk‡y»Á²_zëìcjÉ8ÁÑi‡¼ö÷Jï!? äåY7Ô¸çÖó“OtËã/A>øcWœÖõöçþ†Ú™›8îóóêÈHô®7« ƒï¨uΈ®ÉþÊ3™×nbû8j–ƒ’ô»’ê„ÿûc gÂ?„Ïl’ª ÞŸšä5.ì| | B‚ÈæWÆ¿€hk‚\].£4sÊà,Ó^er™•´ìóQà~B¶4Í3¢ûœ« ªù’Ƕ= Y‹±zâÒ Üå¹FÇ—x3XÃØy/Mu^¬VqŽ3WTw@<@²ØÔ…/쇬J¬dtiÕ*íTºÌÔNJréy¶V,ÿ&–·>{äê]]òÂÞ!<þ2ÃŒ˜â—ö&å¹ä­ÈH·ÙÌtBbA,dvà5>>ú­úÿoÞÛÍ3endstream endobj 115 0 obj 5261 endobj 119 0 obj <> stream xœí]Ysݶ~ׯ¸o¹·ÓKàî™>xKÇÄNe¹íŒ›m–Ê’bYv6ýíHçø@WJ<&ÎxpAÄr–ï,€\噫\ÿ1…ãw{÷ö›ÕÙõ^_½ÚÿãXx¶÷ã^›ú¿¾‚—ß­¨…XuYW¯^ïåY×µy3ô*Vu¥~wÕªyÖ¨ïö^­ßo¶U–çBvëÓª²lÖ‡¶tbK+U*ªºÎ õx+„úÑ­/`Cý´e·¾±•×›m£?S×ãg ™›—úâï©{ö¥Ô×jËzpÚÚ"›”k[Œƒí_ÓÃYŸoJý»+ÇùöOØßê1USÉõeß ¬[Þ ûö»ÍV–YÞÍ8gúÊ8±£Ôlúw‡G¹¬#½«*‹naß ›}°»}öë÷|óýÁŸö¤”Y'¹œ(êø¼M&ʼò‡~•mûÐiWU|Óœ5Þq¯mƒ+8[6š+Øk‹Wñ QË!|í0µŠÉ¯±Õ;M-ÉÛžBÿè·ØsÖl·v'¶ŽµY)»o°¿Š®… é´¨«Z}¡§Ñeu]²Q3WÝæu׎S¨J¹þ'®š–ZJå:£^W¼(›Fµhôw¶e)³ª)Vz€•¾öRM'“mÓˆPÌx’‹-óû¶|MkpiŸŸ“D áqi¥á§a mYÓÖ/h7Îành:⤫d‰z¡$+Œð.6êyÓ–M¸½JØ;´„ù“-ÿ?õdz°ÒúÒOGflÕÎ+J2„$çºÈÔÎŒ¼ko†ØäW©)‡QâlŒFK0Ìûž*û:·ìÊ´û±ó%ÔJ÷ªWÿ«±ôänfC䮞ü ´¼l­–WÚ¼nWßìüî•Þæ"—"+Ëõ UÔÌR®ŸØÒ-­@ݾ-=°¥g¶Dý}ÚQ/Omé9èå¶ô/[z Fúµÿ ÑšÕƒ!Œ.Ûfýoj™SQR±¦â’¶_Sñ€ŠÏtÖÁ¶ÃlëVÉɇTûˆŠ©ø6øÙL¾IÆÁJN(H©(C=üjo¤¡NÔ&¢þÑ YµªÝl€ñÖØÀA@½-Ь­, t »·4ZHÆ4–îÞl9Œƒ¡9ʪDHd±ø³Å 8ƒ"ŸÞ˜Àél`l W°HÓÁ  ¹‘ŒÆ™éI¸î¢êò0C[Ît8Ä2{Ï!VŒñ ·_1ˆdØ”`•Ã",1¤1bQpv%ƒòÔ]¡ްý$©ui K¬¢WCòŒmÞ§ç÷xÑ6eÈMöÓë'Ê*o|*bȳV;v¥7£—,\x×(`ݲÑþG}A©—\¬6Iã¬A,ȬÌ[#.àr÷C¿·ñ•†¦jlS@À=\Âm=…=d©½d¼K’$)žÑR_òn¼Œb4I«X!¡1`«±Q¡›wB3×lSf]ÓÖ3ë®hHüø[2‡‘#kõ–ÁõMXˆ½Q6ÃðèEAÓ¼2ôâ™rµšŽ°m94È-ÏK[êl‰-&™dXÑòŽŒÀ©mÝ×Z Ö¥æ\&êŸ7®´Œ+e+"Ø”xl>[é‰wC^…b®°õÐL#²ÇdÆö;]w Ã?¢J6=29Iðž¢%!æøµfÛ¦×#ô%x¾W‹‘;[­¾UÜU1¾¥· &ž h-WÃ%ÚÛДlf®½é±…tס:¬á±ÃCW,Õ0&r@ÓÀ8Š(‹Ú0uIäÉFΡGÑ,Áj3ãM.ôLtì#V ÿ¾ÞhšÈE›ö°‘ñ ùöB·þÉZ ),7¹ µ"Á@òûޝ¯4닲R»Çöû8¯F߇’®UP;Ã}¾V’P‹ì‘$I’ÏÙeRÈÆ ¶(ÐÖqyMcóNëFŒ?ŽI¼¸éÿÂðƒ„FI¤èýjl5X÷Ø+”Tü$„ü¤ÁÝã§Ìƒ g{aôäC‹M†NkQÙõ츱òiÓiße?RO#Ͳe±¡È´‹qGMÄ63êB¶–~-ë’ý$Ó,F{v…&Ç«ÁV3æˆE¶m0À¿@xõRH ¡Z ´Z×D!LtŽ2¨ÍªÚ‹Ù)Á£pM7aeæ|„}i\ÌV0+’ž:~V/pçöƒOàðŸðã\»qGi ôªcèÏIGKI¢Å¢•†ßÆìز•‡ Ð|ôúNgäsÞ÷ݲ…¬×[[)@é>5dÑs9¯#çqnK誫ý:íSm¨Èj[XËüº+*^Qñ†ŠT<¦âØÃ[øÚ öpIÅsðáÞ-;Ý™ ¶`ßÕ9½×8u÷€wßÝtY*‰Ü6¿è¦S¼ãÒ–ÞO¯óàK¾é®kÀbø5V[ÃÚÿ=ÔdSÔÊÞ﬽¨±Ø·¦_ë*¢`3èñoŠ«s ïšÿo‰cÖ×py ˜ü/v¼ß‚åýmQoÉB¯a[&—®©øöÐÂÚùê!ÆöÃL' j`Š\HþSì÷»$á0ý+ÐÍošàWC#ƒR¨µ‰a”­4ÝÞšž·‚¥F8QnœäcpmœÙÒTJšŸ.d?°Û—äFfÎeÅ uV—UÓ1ç.«d.iœ8Èðçêþb™•ó½|Çqç 6G°Çíz4–¤ç9¾êœL«Ô>÷ëÑ£¡h¨-€£l*h"£Š ì†w†LJì•€iBxê«Ã4E†iúžÎàÎbpÒi€i#éþ¢¨U’Ü—D “é`nŠ–ñWP:"ó­0'=wxiðltYKDã»3ÔÃÚõ£’ïa¦#)šŒ(lå«)oi]„õ‡ø<§µpÞBµ5¬m`m k;ÈšÌUu³ß޽4  ×Å“p)””„à:û§ï… î-#›n,Jlü¡U^)ŠÈK×Ihü©]#(Šžpnî,žl-‹³x2Û_c’*w_¢eRî^Ưä.¦—˜Lr—âé}5™ Ëv‡eŒ¡nq‚é¶iZ¢i‘–:RdB‚¢°¶5”YÇü³ŽxjòLä5µÃTé¹£Ãxéªy(éȧ,/I94xÞ×g<¡Ó‡ƒ|¡s±AlŠ'ÀDò%éƒS ˆ:}w0ôõ¤¦ìø†|øw¯ÁûèNµb¯aáp÷sì’#ÇPà²1`‰Ì¤a2sè—¤2齿@z³@ß%üD°¾½4±¶ °~ˆÚ9.ÓSNÙducm=|äƒuàöUU ×¶ž‚Óê<ëžìŠ' ±5¥X7Õ1²{Ë™vœùFΨîû™Ø7ÛN¯Zî„¡½ÄH3™@`zjP3šIÏ’ñŽ Ø¦^t€ çàŒWMv¸ÏÄÛÜÊ« Åjk¤ŽQÜæÅ#[ÚOéœÄKÐËsð ª3'!¤Éñdîf¨ÏÀîtøÓGÊÉ Ð<òS…=BLLçQ#ìp+—‡É*ÀÉ!8ïK¥™¶¸§|—D°g$1øúG £º.Ô?ùàË›Ó!éý––¦‚#`ºÙd:)¹S4~–2T%l¬1Û_ÌP¼17Vº՞NL=éj™u¨x‡yبäúÂ2•…,#+ñ1~ô–&Èb¾áŸŽ©¶™”^&SD\†¡\9Û[iþáÔ2k+´ðsPÆvúlY>‘É8‡ÍÛ“~‘ÏT&Ï9b_¢Ãжó7±"Ã@—öf“#’¿†:ó4õ Î>ßÓ ¾.)c`fFÑ£ˆÅñöÚ9}Mú&“¢!rœÏÉØñ/;8ƒ{OÑ‘¿úkÛ1¨€€P¨8ZeÀzK¬€”ãÞ}9H‰ë€žw>p렽༭*±“¬ÒVæ~IÌͨ%+¢ðª2eÅŸ'C±S®O!@ гYm@E®P' mNÌnKn¬˜:S>ë0GÌb_ó3§ñA1ŒŠvÄ¡S 0ú± ¬^rÄsƉ‹FªŠ¶˜¼ºì‡ééÑ…ïÝZÖJ€¦äÁ¬½ñx1E$Ý_;J|'œ`®°À§k°/5™Äðdp×Ãè3·(»pôcN·'£Þn³Žü Jœk§ˆú‹™¬¾;K ±­dxdÅ™¥XiÑq/ÊWˆ‘5€ÌÇ…NçbšÄ‹yÜãäÎcÁ…ý­q\x;†—ø ]mÁ±XtåÓ >AàøÐÊ#€ØL’ñ¹I·»¨²²²Òh™ÇÚvë±ì®®€äÁT|@o¶ÐªC-<ëü?Q1Pçû—ؼÀ sý•3Âv­^’_æŽùÉœîæÆ¥R¢OòÊ&m\›4r·Qè=®6Ò†êìì)xœcÆíF ¥•Ü$ït¾›Íhî)ˆæ0Äõ¥ÿý)\ÆövžÝ1E&æÉw}Dý”®S¤ïvŸ(¸×/l¤b†ŽNDQ<'ÎvHFCÂsYRpq6ÉnuYÒ\›Œz™êy¦Oœ  Óós%ò“!°›NA%®¾8.7=ØdÎq,©¹÷ Ç&=$,*~€Ý»Ï,È ñ¾ "qžX8°ÌYœ`ÝÑçŠ,o½”¤Ñö¼@ìÉ¢yßÙç3xe"``@ì™{ ¥&­ÛŸzœ>ü xßƉ±k1°C©&H¨×,×%›>Ppp°¹Ž#Ï àþÆoù)ŽšÚº’@°zWH2MCd~!íŸy÷í%H^¥ø0–•ë*ƒQˆ‘ ,h*'„ Òk"®,~öHÞ »O`G³Óv©ÊD,:P(Ø»°B‹%}Í”“‡vfFðCu`?à€GÌì±µ;^R¹µW(½û®Ì lûõ¾«%n=;Bî1³‹Á®¥ .ÌЗ`œù³5$Ã2oqë`ô ë@UyÏw²!& 3‘‰6aD ÙèŒ.Q¯Kú“É ‰ààí“vÄCz™ê¯t”È’lÌq†$áxÏ0·Æ~¿\0 OÐÅÌn+´íò'³®—» y×KÀ¯–Jxã—&%ò–Üsí©G¦éÉLÀCÜÙq'›vØN)U“UyiÈô!ØÑçl¿ë\Ã2ì«^ý².Á½+¨&o>ÁªzN¸Ü¤ú&EË/~yç­\w¢('x0&qX/ ¹ï’N )Ì‚™ìrD&9Wö(Y´ø¨¤OÞxy‚{Ã{Ùmñ)ñÉ–<“L¤òŒŽ¼UÒ¨5G<_…z.*Ô"·ÉØìæ)‡Dô²÷”3%¸ád†žÏ ) 4'OVzæÏ¬“•–vn†É—%†[¤=%ÑÔBZ#µÏlwaaksmâ|-ÇîR܇×*&îºÛSÀp®[óP&ëʹ‹ÌÄR)Xæeä¥Ã=¾^bS9k{ÿ¬ƒµ½ïoøzîÍCV£ÚÒ;[:å ]TÁŠ×ö­·¶ôÓüžðYøw°x OÀ³‹â·0xÓ'A9;Næµ’ úŸý‘‘I·”šûxØsv ÿ *²›èŸÂ";Ìÿ‘ЇTÄWRœÂØr¾† Îá'’WhÜþ² |ÛÂ{Øï1œæ5,²'€Ð ÈÎ0ÖÅB {2j÷­VIêAryŸÒ£Û‡Y]®ÞCX{_[Á5f‹uFÅ·©Ÿ‚ÎÌ?9Øû³úó_ZŸïeendstream endobj 120 0 obj 4364 endobj 124 0 obj <> stream xœí\ÛŽ7}×W46Ñl¬v“}‹ø–]/’ãIohFšËb¤™Œ4±ƒ ÿ¾d«É*vŸ[š‹”…í<”[l^êTOÙù%ˆB!ƒHÿ5ÂÉ|ðômœ-Õãàí?jáælðË cý§zÀå“yð|¬^”EP†eŒOQX–E”¯{A–ª—i &E0žÞ ƒ£QIõïdøB‰ašÇÉð­•ž[IÔ’Ìô;Y”‡"ÉùëLö­ÌyK" e elFÕ¢€bŸJ#Êሞ“xAâ vöO¯H¼„âŒÄßçp´Ø`ûÀ×V›§#Õh?ÿ5P(+[5ÿu œåAá\Ü\¦7îœÄk¨y¦Ø%YƒéÞqNzÂüµ “Q˜Ë½;‡ `ˆ.|ºXÀP›38 ùœÎÒ7\𥕾³ÒøÁ]ðå(aÂÐÜÀ¶,|>õisŸÞÂׯÍF(¾hI‡Œâ%T1óËÅÝÂ×V@™½}‚¶¥g‡ªM¶ê(âp…7«cØ/6yæ)S(ößÍ 4=icsˆL¡bp`ñ*üŠ˜ú=ýJp’¬í]¡'J‘}e¥o¬ôƒ•^YÉð…5FNi›ª‡2yX8È3­²ÃBÅ{ŸVwTÞã…¯áØ7Z…áêµÕÁ¿­änÜ}éÖa$;[Õ5ì!€ p¼b¦‹gvû=…úž€×ø@¨?Gî¢>3Øwv§Ó·5*ÜÛbóczÀg—ðéÊ73 æë{Öãe7{E–¥Ø,±ú1 7?’È 'ìg ØŽúWÈ|¹†Cg_ÓN¸°$öÚJ·VZA²B-'Vb¤[C¬J5eĨe“KV p;RÒ¤€y*ŠëáxmV;dŠ {NM[¬¡jpŠ–ØPï°¹ðsêu­ìHmÞì}Æzë`²i9¹AEKwH"KKV‰Ì‹Œ³±k΋¼ä¸M¦Ö?—iêÏRŽ›$6.’ÇfƒFZø€í ƒ‰õ>mÓòü‘L‡V¯Å‘0Ð`´ ü”ÐZºÂáµÍ 'ügP‹}f§[tº÷HxÛ|˜‹,]|’×Sñ^èí˜m€8({¯ñ°}ï=¸ ŠOFðå°þ•ú¿ÜÅHœäá¾ÄËh·95âÀãKB`Ǭ-®ü±1XÑïýköÐîLôd-sÐÅ‘Ã%½ÿ¿•Ïû ¢Æ„Ý9@ñØJ³&²ý6öÑyà@MVZ‚.)ý¹ô”vší«ºû…Ú Y˜C@™³:뉋(Þ"Âv°nyƒ—RŒ&i«€¥œ(­êUw]«iÞõ’%\¢ík]…о©ž0\_<è¨ýú 3Ä%>8¤¤ ¨ ;D…xÎ)xÖfFîm$\(ež•áñåüU¾†Òu½™5¨ã|—~7UÎwZ¦ù Ò?$ŸX‰rþOZí:/ýDý›ºw©Ö¤5öÑH‰ñÍH|—Ê›*âD_ÁÂW¯·¹wxæ8€Ë\ÀéØ,æqï,t•@þÉ—€11i½¿;¢XY(—¼æ‚«füÍœ÷«"ï]y|e™õËÖ†ï/z!?…“ĆâÛíð‹¶ô¹o›Þ#ìo¨Á ¸r\ÝÁ¥NæDÈOQ–m |Þ`Ša½Ü!ûóë…¢U÷¶SÛ!ÚÊÁûÁCKí˹‡£ï­í0~Ö_ÝÅŽÙ2Úå‰\=C ù~ékêVç6ï'^…y çÃG¿ØÚYZ8¶Ñ+5­/Dŧ@ºé?5/Pý?”aÓŽX5‘í¿Þ×›àsìü÷:‰ùPØ=ÁúÃJTØþhrËØÙ:ÿò"ÿ;-í3ŸÒ*™iüôï$ ø„ÑûUÛa¤QM³ßLŸR¯#†ØõAÚh¶Av€àÝÀðG¸|8ò+èºEœ=;”9îxÞjúÑ¡6:T×tØkÞÁM+sI¢"Ì‹ú*ü°ºš22ÏFµ-é2-Y’ß7“s2ƒrç.Ø“;?$îbõaâý=†‰öÿfàqÃKÓq — ö&`ôß„LŸ—Px\OÝ…ýo¼€éï١̾û²Õô£Cmt¨ÃÛwýÞ¼yß%øèͱᄒUÿ£}endstream endobj 125 0 obj 2714 endobj 129 0 obj <> stream xœíZYsI~Ÿ_1Á 3`•ºîîà…õ²°° a´±"diV2H’×@ì'«º«2«+{zƲ——µ_R5udåñåQýzÙ©–MøŸˆ‹ÛÅé™_^½]ÄáåÙïâÍÕâõ¢:ü‹”¾¸]>ÝÀBÕ.;Ñ¹åæ»E#º®m|¿«\: wv饦]nnÏWÿ]Ÿha½6«_fêt ”[äÁ2u“©dê2S[eò,Swøó7_Ù(àŬ¾¨¦ö¿ËV¡::uàXv:±ÈÓDªÄsýÉKœ@6»Á ;$ï¼BrÉl¦W/‘|‹ä+$ÉçH^ ¹Eò–%ìÜÓ[ü}󇨾ùz±ùÅó½šä©d#|!V ¿ÓÂ%#—BôDZÏüücjÛ·˜ð†'¯{$‰Jß1;Ñ›$ù_gÉýŸJéEë³r¨jвì;vô KnéfÁZLÓ ß¶Ä›Ëœµùçâ$ ^|¹ –4ïÍ®ñB?áÍh?yó´7J½ãôLÊ›¥ áà~üy%¿_7B[çšx2ü⥠ÚÊ£w@z©@ñ»L‘ŸqîëEÓH瀯4ÌÚ0@È—k¿ë.ØBš¹§kÛjº=Î÷,¾Âù8)ð0œ®Uãæ#¹d/ºÃ „?2÷œî—Ýäû³­QÁ ÒÏ[vW²®'GìMsá¶²M„(ªZSK½f;sð2(C[oUZîЛ¹b±YŒ5éÁãùã§\#éèåXkAõo{ËQºä%2ŠW(äv3‰ŒoC$aY9¡û{dà!æ ÏÖvm:0B ]×ÒßÉ*b½7ù^產šôû;ö6¼ñÁ²à Ï%ò$ä„¥½Àuƒ¥X»zB–9ÿ¥A[Û9˜lF)-”gl¦”² E‚N¶¬uIÏ®±„`ª[4á¬'8—,»GòRÈQyÇùÀÞ‰Q°³ŒÁϪžÉ·Xˆ É–´_=dlBw=Ïc÷ã+ŽàŽˆàº¡iätWªn¬Jº9uêÖy¹ÊŽR&ûeƵ6÷ÀÚ/Ù‹œPØ:ßA<ÞD¨3ôÿïµ 9P¢ßCGôò_‹ìô¬[J[žÝ¨îàêëÎÏÖR‹¶m\Èì”$R |·] ÿ"f´RÂQN `ê¯[·z“—Ãp+Cöü™¦áVæ¨r;º¨l”+8ÓOA%¡’üž6Ŷ0Ñ>vÂJî‘7Ö-¢ÍlÓ–ï1 ¹%tTÉñè^4[x‘ÒŒl1Û:϶_^Œ b”Ÿ—¹|ËÙ”GÚ£úÙ¤S“û7»±tâ„©Š,™M£šMÞ‹Ü¡(ÈÊÚI x4Ñá™yñJeZ§ú2-¦$O± ÍÃðÐKõ^¬¼½A¨Ô0LÉu§ú%Feò€ŠcÚoF=.7½ßèFå7Ǽ÷8õaF»!ºIÑ´¹~âsM²_`ñ©LÑ%QõñXõ"ø«‰rTwE|åÑ`öMÓ9¤ Câj<^ÏìWCDw$p ßU‘öº ÈT6¼û~óÔi†2Jó¨£, L6™Wc…™ 7¥Câã÷´£pe E>,ÙõF ;ZÓT©A™máÇw$_âÁà«¢Ù6 ÿQ_š‰0rFDÊ.«<#>½Ld÷oècáë\_Ÿún Ö÷×§Ó9æ¾—1ôƒÏþ㙃¾øtÙ,L¦êûõõ÷\Ÿ1oŠïÛÂêŽ@Ÿâ9„$áµ<Ó¡–Ïd…¢EJ´ó‰£EõÊíPT¯=4µ"?œŽªØTܦü[ÿÈU,[bð‰'iÖåÈ31»_p1kKuÄ™aÑVàƒõ%Mó½¦a²¯#S˵tÚ>ÀM<6‚{ƒ ×1ŸrLUekD«i J¨¹Õsn°n„oSBÒ}Ì/Ð*´(ûÅfñgøÿ?YÀRÏendstream endobj 130 0 obj 2613 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 24 0 obj <> /Contents 25 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 83 0 obj <> /Contents 84 0 R >> endobj 88 0 obj <> /Contents 89 0 R >> endobj 93 0 obj <> /Contents 94 0 R >> endobj 98 0 obj <> /Contents 99 0 R >> endobj 103 0 obj <> /Contents 104 0 R >> endobj 108 0 obj <> /Contents 109 0 R >> endobj 113 0 obj <> /Contents 114 0 R >> endobj 118 0 obj <> /Contents 119 0 R >> endobj 123 0 obj <> /Contents 124 0 R >> endobj 128 0 obj <> /Contents 129 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 24 0 R 31 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R 83 0 R 88 0 R 93 0 R 98 0 R 103 0 R 108 0 R 113 0 R 118 0 R 123 0 R 128 0 R ] /Count 24 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 22 0 obj <> endobj 23 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 86 0 obj <> endobj 87 0 obj <> endobj 91 0 obj <> endobj 92 0 obj <> endobj 96 0 obj <> endobj 97 0 obj <> endobj 101 0 obj <> endobj 102 0 obj <> endobj 106 0 obj <> endobj 107 0 obj <> endobj 111 0 obj <> endobj 112 0 obj <> endobj 116 0 obj <> endobj 117 0 obj <> endobj 121 0 obj <> endobj 122 0 obj <> endobj 126 0 obj <> endobj 127 0 obj <> endobj 131 0 obj <> endobj 132 0 obj <> endobj 133 0 obj <>stream xœyXT×ÖöA˜sNÄÊ8ÑsРˆ%ö‚AQ 6EEAˆÒa˜Ú¦N£ƒ£ˆXÂ`—ØõFFÅ’kI‚ׯß&7Y'ßæ~Ï¿Ï 0“<_þÿ|dæì³ëZïû®µöXQ6=(+++vnLB\dxœø}¬àl% ê! ¶ÎÀ‚ðûXÉ`ÊsÒŽÞ½¬P/kÔËfÏ ×wvBNxØ’úQÖVV‰…•scb“â"·lÝé2"xéJ÷Q£F›[Æ{xx¸lLêzãâ¹e‡ËpòeWxTLltøŽ3\æ’ÞQQ‘›\¶D%Ånw Û¼9|³8lEXTøvßÈ¨ÈØØ˜].#溻L7nüògB`dôÆ„x—€˜1.‹\–†oIˆ ‹ûC#EQ«y%íØ´*À;9fóêÀ¹±ákû|4/nËßø­KývF.›Ÿ°mù‚]ÛƒýwG…­X˜½qå÷‘£\FúÉÇcÇŸ6{â$ÏÉÚ)S×MÓ‡z¬Ÿ>#e¦[ê,ŠC ¥ÖP‹)ʃú„ú˜ ¡‚¨yÔtj,åJ-¡|©Ô8jµ”ò£ÆSéeÔ|jåF-§P©T0åOM¢Ü©ÔBj25’ZI-¢¦P£¨UTåMM¥FS«©@j.5úˆêIÙR½(+ª7Õ‡²¦úR6T?ª?eGI©PЧP2jeOí¦fR”#5›r¢b¨9Ô@Ê“r¦Qk©ÁÔVjÅQkˆÇÉà%Ô-«ÙVWzLïñ½õ:ëG6¼Íz›6É"ÉQÚ®¡a¼=ó+»“}ýѼ®÷œÔóœm¤í7½ªz;÷.ëãÚ'³Ï‹¾%ýú%ö{ÜDÿOûß±[awZÚ[ºSÚ<Àc€ZÆÉ®ÛGØßw˜åçÐæ¸ÈQí¨s<éøØ©·Óh§çm& <ã<Ü9?=èÚàuƒïqý8oî:oÃOæø/](•‹Þåþ~Cv ¹2¤}¨ÍP/á]á2 +˜Þ ÍÖ°Y¨•=¢k4êò …:‰ÇÁtR–2-]«¬áw$ÌŸÒÓÄ'ÁÖÑÕ*]zšRžÌáI¸1Nn8c°ÒÀF˜Ö‚'É®`7‰ÅPG×jÔå u"AãÕB­o “døºý:$Qt’œŒÐ‰M£è-‘E¶4“¾n(1Èp œ‘TÒdeØ"T¬ZaxÀ4k8jïƒ]%XJ–U¤§kd’4r.My¹B“ÄûÓ ª%æi±5lÚ`5™Æ“ñK< ^J¾°èÁЉ ÒC£¬åK’öb v`g€úé˜!L–yŠ'{ÕðM¸=”N4ÍZ˃ƒùØ¡PJ7tú*=+=½ï³ÝqçÏQ­r_â]åq(šÅ(’1Ÿ¾]mÚD2 júé¼k¶ì^´„#›H1@> '»xip¶ ½daGWÔ"5;â?އ;wꎶòŸÕVÕ rV+×f)òò³ܪ…ñsIÏsý¬·AÂVóÒ{É…¥Ê ç2µ¶–—ÍHïÕ(µœôxª2+‰ïB]3`o˜ɧ/x8HßÀS0ÈÀÒ‰W¤«uêòJyi²hÝn¦ñ¼ƒµ’S¢uË+ŒÖí!ZWD€h]ðÃé8#I2-}cŽƒÙăÓaxàm’…fÏ“ˆ]ÑÆI|2Ýî¡/÷70KãáW‰¶#ÁÓîæi#…„zó¦FÐx,~ÇÃ{Éié¢?ê;"ÍPG댣y/!ACÃDü«Äâœp­~"% Öä‹,ØÕ×é×m÷^~µáÜÔÃüõÃŽ£ ì ¯/GrXo^ìmÀá2‡iÌÃ+¡K×Oá±®—A ¸37Q“²1ñø®½[Ð6`ÙjθðFèGb'.|ŒÀ"Qˆ!ÀÄ¿Œ¦ÕYåÄ©Ü%(fð%üF²EèùŽ!ÎMK'žå¤§—"ÃUx#1bK°2¯'ÙûEa® Û sÃ}qÿ#€ þÿ  pû/<€ßhóïçÓ?1uº«ëÔgÞ¿|ö3og€°©ÛSgw¶„æMÐÇAš.$ ïec̸„åôšKÌ!P´IV७¸ï»á`ûøúѯÏrÒ´ o™nIÓ—Ó8>ÈÞ?îJzºÍ˜îî6‹¬úôÙ¿¸n—`îæ›àc @-Ø#i¢«uš²r¹†à±·ÙÂÍ4wã1°[ò¹ÈPQù=ºÛDÃ|¼G‚#‡0fЛA3ÔŒ´Ÿ pÏ`-äÙ µMŒÜî¢ ˆ“Õòm›h|M( ¡“M[¨æ/‰ˆ²Äcµ¾Q›h¸ÖQÐéµÁêK2}œP,[{zñþh>ZüihHغ˜y(Æ`gè‹û [7÷Ÿ=ÏØ_Vt¬>K#Wdç)T\ðÂUŸú#÷tû½ùû ØþãX¿z¶"°ˆ+H)U”#¶B­#4²aª•ÒnÒVøÄOE±#¤Þ+Ôhñ"˜Å#T“'¢µ:™ ˜X‡hmÑZw(¢Ï.¸ý±ÐïÇÀ–—>Û1¯q?ÏU±¾+8ð½,ûðØË}ì O÷3½}ûMÛ¿LèoŒ4X U"CgòêR·¾˜Ñ:Š`€ÛXÜÛÿæ ί¡×¹&]–.K•“—­ä7m÷N@˜B3î¦ýÊljßÜ¿ú½CmAUãØNr„ÃBŽ-pµE$G¶=ø35 mZšÊH<UÌÀ3aƒäîÀ™DMVE™†°zBc.ƒŸulHƒ˜nŸ´„IFŠ<”a¢dØ{` ŸÀÃ`°˜ž˜B¨ì˜Æ®xðhÌ` ¦Ç.àòù`ysD&äýÞ¤–؈¡Áþ=™r(™¡ÃÁÛs8Æ«†Ž™3—ÐñÔ£[ ÷/F+XofèÄ£ÿìÍ›ø„ò°ÚeÒg·C‚ÏvÆ}GöØöŸc¡ÏÓ+ wÎð¸h“l ¥F÷JßÖšœÈKß­ª/[:Q;†–>Ã~±„¡F·Žp›EÜú´í'¾+jZA9Z1 VbaCnÒN½³Ç“w€;§B¨±ed­J“^ÈG•úªÃ;~á o>pÓX¿ÇíîzüwBóÎ0‹ƒ:ãôR™Lúœ¬ë)2˜IÝÞöæÇ©÷‡òø…àv$XjtÝ}*ÌÑbEÀ<½³ÌÏwîÒI|'¢+*½Qp][~3ìnv^ Ë•v ¥“Lð©áÀA„YFš‚À,»B8#m…3ît²Z^.òŽ¾Ã‹ù‹HÓ„`˜Dר´é©Fœ^ úŠé²hY}.!æ=ˆµž¹˜adæ#Ü>Êødbæ]".}E¦¨‚¾ð)-=þzœÞwÚy/ªËÜ¿S­,Ê+­®+#VO®à?Ý“ ÛV¡ ˜O¦±‚Ï—øÑôÞ"|à{Ÿ;kWövÆ6cÆ`3?Ž›ß9Ëcã·<ØÃÑf1t+:V}ôxCcùad`É$#.1Ò·Ýx0ÂhV¬ìmÑÏOÆxµµý(Z3†Ôfá™ÁÎÈ=Bc¸FœÊÿ 2SY’ôBÆ.:º¼ÄýŽãÆû+³sTHéD¶ +.*((æN–}¥o@õ¨*÷39IÇä‡ô¹UÎ-;\W&×eææäåæòÛw'+w¡t¤(Œ×²ÒD}ô΢λPj~”Š=/´Ëþß”¶È\[`@³µ%<ÊËÍðHOï†|åÎ$—Ê+*JMØþ+lü ¥¦ÐK”g,ô7JöîÓpô¬ƒT/ø2ÙÖá•K+õu÷ñß^³ã`0ù ²MyEy@y í@juÚÉJõ+ü7Oš2ïøNÍH}Ë:’þù‹ŸFžÕŠ Nš_®ÖÔòz¦%â|ò"ï’—WžÙݵ—ß¾'\¿X=Q«*L('sí*Ϭ®Ør¥±íñÑëK8éДBµ¢Ì¹\­#ÈÖÍH‡’$“ê3²È &‹ufF›Õ‹´%YÃhó¡/uÔ®3Ñ^D98šÓ°PÐÐúòË·>«¾‚œšÑ^’W×wåÕ¡Œe:ÙmµP¤žÏ¿660lgà*SA$ ’;³7£»Šed‘Ç[O-oǓ٫fÙŠ¦=`H̱E§~h<{Ýf¿vŸäp))³ÍC=i(¶dÃ<¸¼Øú*÷ÎÈ·È'ÄX/Ø6[“ø.«÷ZYfôð Qî3RgàñHzù§[+ITtøñtýàíSÍ'ŽŸª¾†Úу%«ØäR²R] ÷š©ÍѤ¦ªHdOcB-¯Y€üQÐÎаUk£½Ð\Ö‹Á~Ón~UÓt­³Àø…Xal‹5¬&ʶL­ªslëeVá6¼—ö¼òæ[ OÒ)ôòìܼ%·1~^ò2Äú†iå¡bÌy\—rý=¬ ‘ø÷¡(\- Õ·ãI±ïLÊn “éPá…,C©Hâ—2e\=mDãU’ÍjÒ¹NLŒ‹1Øý`H:×N9H‡ K„ ™To ­ r:ÓoRXÍæ¦|C±Œ¯3î§×©êS&WoG[Y¯yÁŸÌC^­¹wò«H B,!'ÑM I ed9î#Õ‡®Çßs†^ßÝ{׺óTø~>ª6F3»<@¯Ž­Ž¯Nm@ ì훞?½º¸ØˆýRe¹sy©¦†ÿ™kh£5ÿ,ò¢8%سCÌ6X'$ìQ!昶æ$àÇõ~ÑâmOsº¿#!”Þ­è²2ofÁ H¡»òövS8û¬%샃t§°Ðþ±¹ •&F ½HŒ±p~-=½xÙâèùΫ6Îópy´¹€½DÞμ¸òïwn¼t…ë.ÖEÇ CÈ)‡< cã·&d‡XTì2úõ­Ûoxa n2 䄉éÝŽ/çöÑÝŽOã,Š>˜ÑX bBäáͤh3Ë*J‰×€îX‹i&ÉTÕpaFGÂÐÿ=»7gd&ÖÊL&ë4ìsO$:óæœ˜5‰´‹í0X5@È`£5¼²‡éB/I³…z2ãý.M ½vÉn»C»ä¶ùöÀ²“†™½$]d;±Ö§R.ƒ!ÐAÚsa¾ìÌ RSÏ“ÄÒÒÿ6ïÏÁ´?99e(7ÁR;HÎÒÒóZöæµöÒØ »IÛÑfK]ì¨]aîñÖAe°zoª„rˆ½šŒJ•ÊÒDQ­’ªTb®*’˜¼¦'Ì›7q‚ïׯ8xÝö‡z))‹ô2&´¤¶ùn"HÀº½l¸îHòzVäYáŸWVçÍ ¤ÿžå0}õÚ½gVq`ÏÔ*5©JE"‡Ý™ Zão#öçoŸë.ßx@xX ö/ˆkj,\ÓÃ|Î ´/~84’Uôd¬™ %_üïké—Ä;­ô+пÆz“›„|cT3¡é½(yæû‹žµÿÉë⦠và×MÉ{²0ÕþŸôL]uô}¿/>‰ðOKZÎef«T(“•k²ô…ÅbÆótßAõ1Ä>¼°m ïÃUF•®#á£ÿä I¼ôüœ/W¾¹ýÀÕ›Üi›ù‹VˆD¾¡M­·îŸüíÔ9•ü˜ÉVBžé’Ï 6XCýUÂ…ZÉac]©0yz·²SžÒ8¸#A’Kã@!AòÄxÁR™UšdD²Ü‡þ0ý‡Úg3x‰A÷SÙ³‡o£N-~ˆ×;}o&å2Ú–G´Žl†8XãX%⸼Bı(t]î?Mã#¸P…, 5ûÿ/.wÊ®ãid#}a*ž"òé<‡2˜ $jZú¤û¾ [™õð,c{<ì%gŒ¼­0šÄÚìY5M曀NfÑÒWæòÅɼ ?šðq$ŒÅ#%þªâhî‘eyù f7ozVäXØxêe.Œšp{÷µUµñRQßÉýH‚©áê‰Æú£ÎM‡#7ñø^›Å}ËYñý‰ë ûdŽÞÎKKñËŸGÂQúúÚóK6nÛµz3—pnû¾ ( íHŠ aÿ| } ¤ìn…i¦t®ûˆ–¦wÔZ„º·¯hi«ÏÙ‘œ¡÷»÷Ðú~‡ûz-ßß¿É~z2ýc÷#GÌ|þþý7Ï:I, ©{i°ª„ð„kèk“VpëW¢¡# ­³¸ufFËwôÒŽXl% ’Áé´Lˆ•¼°zWs|­Í{oZ ãÉ!äH©LŽõÁ=6ãr=òñ]l³/\+×"¶\«Õçæ•ðó ô{ôú­á—¯¡OaIA!*fõYÚLÎ8×߬!ˆÌ•)ÏÊÌAyEÙü׸ÿ!7ñ"Ã%úc_Ü'7å¡6S›¥×©ÕŸUs÷Áêsè‰~B?oÿ·Øì™IO0¥ÑVóbûSó%›'½úËÄÙ†Óè/šIØõ{oY‘¦°•°:¹Fž¥ÌMSqøïÿY”£ÌÏ&Ηë²tº’’rµhøÔf++!~‘ÁÄ9@akl=Sx"çFª‚ǘk°~ LàðwoeèVÍÝCgö7©iA-¨)iodúZ?´À …‰~Ñë"#w‡!ØVß´ãLÒ]tËtgGH5ñHd80(%'[‰äd3rM‘¦sàØ"é(ǼÅ ,îˆ8“ämüÅN0D4;HKÀÁ +b®žûòZû÷¬tQêÞu‡n |5ùùÈ€e1!\b|z4Jb zuúRuE1WyãÖ©»ˆ}ôÕbßÐĘ1cyœ‰ã%AŒ4Hü˜®5]±$rÒ’­âý &¨>œ(C‘;c·ç&æ)³QKt òÒ©3×9ðbÐñýõÇ + µjTŽ*²JS ¶Ç×£c,yw}ý™àÔ,EFš:§6—kÌÙ÷)Šd1³Q¿ûHܶøÌ]h£h¡ždµS 5§0»(9©*'W•—똗›O)KTšlðjG,à5Šœìl¤pB¹¹…äŸc¡ŠO±FoB³ÁNð0$¯„0<“²ÿÛN´^øýœ¯ÜV®N‰ÞÄíNIKBr–ȇ^STT¦åšÎ×^Aì“ë! 7ÄlKâwÉSò× 69³¶Ò{o_,˜9g΂±Ë—Õ[Ë+KrŠTˆ•gÉåéeiMÉ|kì¹ôóˆæÇç?Ý]{sVcg±7À߉¹`OHd³dxÀ8‹¤{ùÇ›50à¼9§[#pSiœØá(YËÀ~ƒ §ÒD%~•ì±`€™"øýÈðO8“©½‰‚4‘|HS¡Wª3 y8̼ö»7K<¤Çpx ƒ|j­8¾êdü Ķ=n<,2D™23ù„”MéÐj´µ6öDôéä«è “p¼õæõÝó«}¹fXW šÊú\ ~ªÓÖq-6é¹éù™ˆ]²éè…WO>åM>xÞ ³ö "F®-°‡€(P¸!]g:ån#Es:‘ô’’¢Ò2®eÏ)}3b_^ ˜0t¾·û’àý§Bx•:»73+Kž^‘Ú¸‹¿°íBÊEb\Ûoÿv`ãùlØúÅ®UÝÂçK„Ï'å“(|yzU¹V]©å`($oìçýáU…F]©á€#¯Äº¬Ÿá÷Æ ›zCöÖ"­üŸ(l~M›Ùj°ú¯ÏÅL\ [–W›_tèvõ̓§Î?zzæ ªEµyÚlMvq~ bõj­¾vkÕÿ¸u+yÏE['#̲xî Üz¾{ü-Pæ¼ç¼ÉcW,«?¥¯©¬j<Ö¼‡ä÷¯.˜9yé|/¿Ug_Äò9ZT Úg¤N³1Ÿ1µQh‘áIºüœ|Êu2FË’’ʦՒÛìf7)I½XÇ…SLç™éqšl¤9Î÷{œîçwG®#µ±Ä%ÖèÑD^ ¶pG&}òàxCËͯ¦?5júœ a_GpÒWÞ‘›W.8òåÌŸ~ùÍ»;›/zá<¡Šô|c¹àJOÏÀ/î|}óò#·‘¾zqk©§×üÀ o>j»uõ¹1øTC^Å_†<¸(`ž‡ãYy¹Hå”I´]SR¤Ssàà†Ôò’\ í$!)É-U©;!º¨q8wÂZ‚YARa RùˆYâ93dJDIÅòEINA¶HqEzÒ±”“·oן¹È·}yô;±à= úàž#gOÁÔ°'S_?¸ØüÌÀ©.Ëâ×g&¥¦lß²iWbç-½óÍwWo·Ým]?õ_œ…òKw/”û_°OÙlVÿnc408ÂLpüëh,‡‹¤}GDDÃSû¡ãÇ£™¢ô q`áÿ¼?fÚâ:­)ƒð†9úh³ûjÚw ±¸¶H]Œ*ز,Mêò°uAœ(Ûqñ[rRò ”Ò4ªª¼cyõq(B”ô –u—+ÅtQY’XÈm/ÚùjC2dîÙ¶ÿH½¾Ä?€™0§•ä•ä!§R¤..,-,r$µYÑðÒœRy þ V;ù£+).A'T”_”Gþ;æ–*‹ ´QN^v.ÛEù S®SÜ•ëhtÅ]Y iË̇2c®&FµÿLf’J³HÆ¥­1æm“ Z¬„Z¸BÀ¥©¨Ë²Ž ’«ÒÒ4ŠZ^Â[À.È\PôÙ]'L®»ºº:údOƒíÉ^½ ½zSÔÿC‹ ¾ endstream endobj 134 0 obj 6976 endobj 135 0 obj <>stream xœUVy\Wîa˜¶EEaldPg& A<¢x!‡ˆÊ ¢@¸DááF` ˆ`<àŠ‚á9E/@Œ"FÑxàA¢k̲qݰ“êÉÃÝ} ˜Ýýg~Ó]]õ^}õ}U% ´µ(@`à¬PÆ+TáÛçÙG+ƒ]ƒ”á;w)x˧Üt7C‹›)ŒÇÑ¿›hÔ¢™”KÑß'¡‰B4Q»|†î}}ŽÒƒ¦Éph %³K¢cÔ±á¡a*¹¹‡»—ÅܹŸþ÷µ5›²¦6SÎÔj!åA­§QžÔbjåE¹PŸQöÔFj<¥C ¨ÉÔJŸSS)–2 ¦Q†”9šÒ¦VRû¨>­ Dðo­Z7…ŽÂá¿´½µ‰f‰.Ò º™§w}Ü[F›1c¶1 ã£Ç¿Ö‘éèé$èdë\œ`4¡qÂÐÄ­_LÒƒ3ºšTú Ç­+p5`Æc3Ì£çh"EØŽžû!’|äm>è±x –Ð Òå:p45 qËæYì5<3”›)–ô“áÆÌ$‘{@¨SÊ”µWªÂ‚‚CyeHr5gŸÎ?_T^Þp~×èêr€Ê¸ƒ$FÌ‚hÜp£äôè.è¸zI.UÅøîIO>¸K挿áMýð¯#%YŨXò|Óµv1<¤Åü•T  C ôƒ‰{?²‡Ofæ¢<Ôº»EyFÑä_äŒæ!»/·5x<úyûÔ†5u%¹¥ñKl…'Ë=M—Iàé³ ^ô‹ØÂz0Û RlBGY9zÍEÌïÎ÷U¯ÛîËÚõÔ?Gè\bYxA‘D4UÉâ UªÏ¥ñ™4¾Ør/þÁõ&Ô—¹¶FßGXL³™¸ÄÎÏ?qÞéæ/¡Šä±Õ ‹I‹=ÃÚîþ#Ðz%+ûÌAÐ!¥è%¥ØÏ—â>ijÇÛ²[Qº¸»=º!´mÓI{´-Wúùlpö'Õ7cð”WK@& õ‚°KŠ¥t‚" Ö1îŠÆ®\”s¸Ziƒ,Ž¡sksŠ ŠNÖ¶”´ zT³» úXRÖ^ÁèjJ>‚ø+qX¹zšÅS©¯ùJ+?¬ð8;€ À ¦¾mûÇ+éÓÛ@Îò_ñi"žÊþ|sÖZ³p›«Ì{KØ*ĘºÜÁÓê×nÉ®Üû¦á bx¹­Æž@3ŽœÕoM4ÌFß7?zðîL‰é°ò1^'ÅMtùð;Äʼn`:ý—¶€ù+vÎ w“m[í½ˆeE”¿¤M¢q?‘hp Xp¥Ÿ¡';º;»ë_¢~tMÕt&´:(ß1ø­ø†0‡àXö"D‹À’îlˆð‹Úœ/‹ ÿ“„æ$£ªþ?Aß‚–¢«çäÊË(>tŒ ÷e@Ñ´!ÐϾâW¾à¨7ïGTÅS¥ L-y²ôs¥$z©HüCOLÐi·é¶hµÂsÝ&Ǭ…æ0XëÑò¡ž–’¯{¥_Ø'¯wÛj$â~ŸòYæqžE§§dìEû¢Jq.¡A}a_‚eßž‚©0ë2ÿ€OÏb¯7øÚa][“^çû`òc ÈþÛSôA 0Ìå0sšx{Ái±GŠIwÉgÞØvaýÎñŠmÒmŠhwä6Ÿ ~õ,ÖP¾??£‚Á3éCIûQÚZãw!¶YÕžÚƒXÔL½ܼ´R6·úóÉy¨TR[_Ö!vÕ%DìÏ8˜ž&mÚe¤tî §Ï+<Ì]Ábšø¸­,¬¦ÅƒÝÕê”C{ÒBeâ¡D§/Cv¡”¬ÔìDÒÙ§öäÈC…”™—•Lj+`òÑúJ#Ô´ó”WQGUUÑÉJFüCsKåíëäåÍ1§"j·nBÌð¤?8ðš[À¶€»VÐQÅþ’¤Â=9»Q"ŠNQªvF)S¼ àç<(àpUìJú Ìæû–Ï9Ñ –¢>z ~y(9=í‘liQ>¬;y8§ZZú.9}©˜D•”wâhÖñ#E²h=¥uá̱`%áÖã±þ1F/¾'òÏ1X$ô¦þ0·'¥K Lé4ñCM w„Í®Î*D¥ P–½xáÛ8ow©»OØj䈖^ € qï Ń?ªküŒœLæ{ÕuÍÌÍÌ•bSúPBF*JFž¥þ ;.ôÅ¿ED̓õ7î¶¶Ÿzˆ¾Aý[®Í.Å‚rCñÃy%1_5õ^oùñe{”ÇŒ´Œ4éSï5z\“ŒãyXÖâ»—yy­GïU— Iâ²ûðœ7-žx01]Ô’å7¼^?ê<ÕÛ)MZ+ôœp>7ÄB} ôDÃ/FQˆ¥1;<ÄWj8#™³Új̈¯ñpZ¢µ÷öùH²rkmlj¬ÂÃ_ÉÞsi"r%Óá¨/wÜvI0óÖæ¾¨ºtKÚÝV{½ø;ò‰á¨G¢þÒæ‚1?O±IEû Ð ÂåcY9·¡ÃðHqÖ tÕ¨ëU%U>Ç× l–¹Gc¦ÑE]gJ{sµ|Od|ò®„TYÂ^„R7¥úî IVîòTìØˆ\‘¢XQGfi|åÙÔóÓo Ë7;%¡èKFœˆR³öÝËÍO†HVÜf£öõ_;Ý7¶¨®½±îN¾ìJÎW‡ó²ŽeÉ”ägÎ<~ŒÑý}Ò(Ôš#`.‡ÿi}Ðâ~ÛùHÒT© KÉHMß+³Ãÿhú¶íÜm$i,KMIO:”,sÀƒckPv !q±ä™W»õZX÷-Òä+¾µBë‘_¬Ûú¢†D}ïRÁ÷0AHö-R:w½oDx+šS…©«x<ó3³’Ÿ ¿ 4‚ác˜ÑÐÛÐõào̘/¤çgØí¾Óù ¡ë{0¼HHÑ»Ø_¶À$ÆŒnÇf…öÈaÃlh‹g„nˆð]eBüÏŒÐNé`ÊsN‰MǸ˜\¦q ‘‡5[YüáƒRt—†åÓÂ#¶Ÿ¸dÖd8yDÙs5Þ"Kzû›mX%XòLà§àdÜsj0&—_L;èÊÆšr˜ðø»k5µM­W*n¡ÛDß«z±¶š=¯Ø)#ºàom}ˆé¬ôöOð —)wìPaÚË–úü) 2\<=B5]ˆú+ø“Æ—p- LÌÀ|šø=4Â}ö2ÊͨØÏˆ¹†Ô²¨ #Ç-®ÎîÕ—3¤åã2òDx}P•ž„É`z¼ Þ=«ë¹*m¹Rùõ¢NU³ÏiFüþùå–;ÏŒÞÚwañgkb¼ý¥!ÛÕÛ’V2ÀÒ¹ÍÙ%yE%µudÙ`Új£‚V¸yYËRFµ 3€á!÷CkGrÎ"lCã÷ÃN±XGD¶pl̓v¬Ei¼ »ÐÝ¢±¬Ê@û=i_Ã'Àƒ§a5Þ찘ƦÃ2èÓågKZs£vÇ&SWÛeža•ÍG3s2sed=:˜@ºòÆüùz˜8ôúÂóÒÖî“QZÎ}Xà§HQúI;Á\—?b˜ Ú/Ê΃¶à%MÃD¨ÑæÚX26sÑQæ'§X$_ìñÉz©ç"?ì‚°.ƒužÚ¤WÏ[tH/Þ¬~0¿¸Ü6öòS+¼¥E=YÇ j«ï7žîF—P…:ןÁÆØƒ½\ö¹¥ë*ûÏï½¹ÝÿH6J¥›üž´œÏ/}´#þJc/ìf-—ã Ó°ù)Ì3Xò(C]U×È÷9ß D:0{ˆ 'N¤¨ÿ4Š/U endstream endobj 136 0 obj 3369 endobj 137 0 obj <>stream xœ]W\×öžfv@@aPÐݵ `A¥¨OEš"X±€Ò»4) ÄŠÆv‰]!¢4ÅQ!Tb{¢Ɔôù751,gxg“ßÿ΢æ½÷ûm¹sç–s¿óó+ct{02™¬—gxìŠð”èÐ`[—„Ø0©k„h!û÷èLèºÑ•Î`f|aH tˆîÑþ}z˜ˆÎÆP× 2z3:2YÚ¶ìi ‰éIÑ‘Q)êa¾sZ>âïžÑNNNêôÏoÔ®áÉÑ‘ñjKÚX›Ÿ2A=ŽŽUGƦ'F%«ƒÃÂÂäi ‚cשݣc£V¨‡M³V5j´-ýã’š¬žŸ¬ž©–ìÿ¯†a&ÏœšºÈÛ%!l±Ï´Äp¿Y®³Ý’"ç¸'GÍõH‰žç™:úŠe¾±Á ¼ÒâB†Y«Gز9jôû±ãÆ;:1Œ-3ˆñcf1®ŒcÇ ff3nŒ33’ÂÌaÜ™ Ì(f(ãÁŒf,™yŒ'3†±bæ3Ó{fãËÌ`Æ2ÖÌfcÃ,df2ãoÆ…q`F0‹f£Çè32Æ1bt˜^LoƘ1aLF`L3¦cÎL¡Nbt© þLóLæ [.;Ùc`˜×zü[g¨Îéºé&è¶±Vl[ÌÞጹírV>R^Çëñ{ôx½©z‘zíú†ú³ô“ô‰~™þóž#zºõ|hÀŒ7ˆ5h1´3 3<`xÈð²á#ÞÈÂhœQµQG/Ç^ç{uõöí}Û8ĸ JŒD `,¶çË‚dtÅ hógœ¸ŒñΨ+‚€ 0=_&n+¡­X°ãpgW‹ÎfÿÇ‚-W´{ 8—{嬑؀ ]½é,¥X& ®fö°hÇÁRÍv›|‹|Qx˜ßº Y;Ó•ƒä[²·$§HÕîÃû¾Ï=XTt’º}%ë D[èŸoÒjµ™¢¶‰®¨)£mrEÛõÚª–«'b¼”ø„öü%‡žÓ[Pîâ—8+T©¨} §–¤ ¿+; j1ž 롯ýOhAxg;g¢û‹Á0Ìž·ƒ£Bs.Ý-!Ñ—ð®A×ߟßWw°T•{¢$·‚\"Gs]y£®‘¤ Ë•ÍAäH….]84ÇØ6²èÎAþ…\8PÁ.Pã.Ü)œw»gÞÑNÂ;V¼!€™æ 6â Vslºâp…ûhÚ°¯ØÆbÊŸqô­ä 0†9ôëÆ&`…0`ˆ™â}‡)´sÉÅÜš²š²c×Èer;ì’SÝí3ǪÈ5r.©!ätPÙ¢ÜIôÄ8°Ac8…ØZï» "Þ]…ë‚§x§<ÆÛ'<ÆE……ÿ3v§(¸»)~Ð(žÐÉeè†îo‘gpìL¯aq’* Y¡íœû@Ë…nS&/ºÕÙyþÖ}•v!=Ûq$èÁØ|“ãtµu’/W‰Ö¦ ¦Vc6Õ¸äˆ'5Q°ÆÞèàR8õôB•¢~aÙÍè›Íäì‘S <ꋆÂg݇Xû{¸¹ù·þþ¦îÖ-ÕPHltD(à+1ʼnªðZÃ='p°ICûŠÀF®åµ4¡…ÂxFš³ÇT‹»f#‡U"õ¢„º˜%áž&ânè#«¡»þ!®65­©ˆ­œwwt%ʨ½!Ø­p®ZQŽNÃ÷0a¯ -¹L»Ù ìèÞã%ØÃ¸ý ÕéQÇT¹+÷¤í ã)0É’_@2üògTjÁO õŸÛhÏ)î䬺„ƒé¯`Ž÷SïGžS)ÚÎGzžœaáAüã£gñ ŸÐ ‹Ÿ»»›ËÛ·u­Jý0èôú( `…•Mv’ݤqù5呯Ç^ÑZ?rêâtœúr Ø‚áëÖ%*TrAaI‹H(Yvpå‰5ù›o=ÏóBØÛqªò ÓÒŒü„œ•»Vlà?%d0–‰÷höÐÔ¢±¨Çiô4É,mÕ‚±†—‹¼˜Ìuå¥@q'E²‚Bk)ÁnÞ•. %ÑÞž3Ü"†jÊʆÜpoòèˆyGÞ‘ŽïšZšš¿ÿ@: p̸åÕê\:ˆðYØG€^¬p Z;[¢NxKÍ·}ø UÝ¡¢5©ú÷ݨÍÊ9Ð!·Šë¯\n)ùK@7ö÷9­~WÜ .ž·Ñ¼è4 9°'â8;o"²*#xB“ý‹WïdϤ¸) Ž Ü±ìo‹³s·mÍV¾—/ÿ&*+ðv£T3&ÛÝ×x‚Zô|$×6ИM ÆÜ§Æì“¢~ä0‹ó'¯`X:>Ä”¦Ú0]:V-X5ejDÔ-·ÿŽ{ÙOÒüthà)Cƒ“hH>L3výWÑíGíªîŒi¦(û’-+äŠÖŽÊÊŽû¶mÉS ù†¬Md3ñ"ó‚ã\xEÙš+㟽§„|®%$M˜°ú™)ÚÅtG¹â·ëÁKy[`ŸáhŒpâSÔ…~MåyW.¨\ä3üÜDV®SâP.+87þt\uÔÕŒG”º/ï€BE×ù®»n]p±²^äé2}ióë?ÎüØ¢úzT¢G—m§Pµä yM>†wzµÎlqún0Lœbfzx¹‡éæKùë”/b:É=rñÀåSüçûí,‚Âdç6—”µ1—‘~hð7CÀè'+9koj J+14tÃ{W‚7E‚÷6…÷6íû‹Âº·;KJÒÞC“: Ì °@c á·â7ba+pö­8”iÀxwԉܛ·B•—v0óBLXØWñâÔºìu©æ1a+gDÆïÎNW®Úÿõþ¯ÓáÜÔÿ?op¤æ_Ê­(­(=|Ž4[Qµ®èv¾oXöú=¤?Qš_¯T¼m&ß%gùñ_„‘fSY3µfef3 ?j©¤‹×jkšN^—š«ÌNß±šDñøH«Ž¤9½.¢$¶8à@q'‹Âfóß¡€b0—J - FP1³6SˆULán[îó5Y¿mj[I'ëˆÇŽÔ¼(ÆåRr2ÒͰló>»ŠŽÒÊ+ägryeCÔ阓Ks½ ¯±è¦ö]ºþty59´ùÐ*^ñþЪ½IÑæ$j}Bƪ¯V¯ÜDøn¨ ‹³ú˜T™"­Ë_ì/ Í,àÌmYŸ”ºœWÔFÎ_>Ç Ç~ n{Š 5«â¿S¤ïÏÈ ça —N¥ÎèºR§Dà`‰+q9 Áq°øõÍSUŠ´CÃÙg pÑó…ìQ·¼ “`(Õ£¡îv­ø/n|mð/%G¶ï9¢|-_½uöõ„ÈÜ_§‚³´¶€ê…Íù×)sš¥Ù¡Ú´-E×§K¡¡ 22 h6ÕÂ`h„Á:â;q‡@šWÕDVø5:§ §Y:á d/͇a ª?V¾I+K ³ûùÌ‹5tnõƒMJ*&{qÔÇñàCÃi6è<¥™C>ç*Z«+TĆ~?ÔŸj{Õ:u§²Û¥MÅ]Ý*W%¥ÛðXÀÇZ¥ŽÅX ÅR±Gß>–k%ºçpé8Z!Ò–Zib¹©XFÏñ\®¨õ ö˜WzC oè äØó¦ÈïÔ½Z¡T¤yÉ»9ùbk¥Íñ­Ñ4|Ú) Ë5o¥’“t·IгèOÅfwli‘”A§)øˆe,ãP¦Q¡ž¨’Ú0GSÆ~“òeW$¸ýá‘ ¦:)‘ÞM~ñŸ `& Ž.†Ÿa&±hß-à}Ä·ùÚü["Y–%éT¦Ü> ØÖ6 ê©R̤Ÿ)'N…sêüÛ–?%OI[qC]}MáeÒÁ‹™ò/–wûY²\»mwV`GSÖäµc €+8($m<@ŽñÐ ‡S.¢%ZNöFãL%Xp»/j%|ãÑôÈô̯VmP¥­'Ä}ݼõ}Õé ç²4'üH"%ù²’³iuWÉÅõçyEIؾ>ç+Fp8 âE­sèR×%ñG*ª ó/g+ëö–íÞõMîž~רMa»T«çÐ^sÇ‹g¤6l£uû—*èSÖÛ"Ü{ðK A¼GTÍ,ºqXŽwX¸Óí*ùß®8"€+HuÖXt°©®X€žx„…‰Ü/0ñ9N”ZFÿ6üRÉ_¥(Méþš)ê¯þG5ß~ótÅճǃ”¨Ñöü׳h"ïu ÿäyÁÊåç}{oºbé|^Qß,ÿß"óÊ—"óʧÄHŧ~ÁɳI- ¼†^àp'õnäUDÃô“Ó‰'ñOŠöå![úxNh­w³²össw]ÒÒùZ[MÁ“®ÅôÔ’ïå8Õ•m ÚÕ¡è3ÿ†!Ñ KÀŠ€åIà>@ÿy–.@Së@IÀ–€?(‘>óVÜ 9I+´"hÜ@”f•ÐÏÊ|Ùu°ÐòÉ9ñHŒš½–Ÿ'¯ÜylÿÑ‚Š’oËÈPb>P°Áyx%à´€#”“ñÚMgK¦*Ñ”«@½ÈUÀRDLÿ·Oâ6XÊ FÚÌ-»uuA×4ºFXW€€!ƲÍ„tŲZ¾ˆûé@ªâäp(îŠ&“Ë‚f‡ÌOEpAãƒc¾ó­òl DÚHSIÍÕŠÆü_ô%`úÕÓè‚[ÜËiÖcuO“ã+F|·geýÄ Ö'|—J"HB^ÆéÕ'6Ü#/É£=íûŽçVŸ8\A%BË¿¨©q™Òî™âjA³šÞ"ÛÉS\‚ЈVbZšÒ"08ZmšÃRéî·Wº 6p‡Ê óß݇^ÏN7’_)†¶OÑ­ÆE›¯É†¬uJ0/â~*;sýZy뤤X…:J4pŠ˜¹ÍxqEwÈ@âKXBc>Ðëž¶€8¸%Ô“½ÛÍ+^œ]s"d¡¹w`€ÏôÈò;›•ØŸËÂA/Æ-`øo?ƒòQdã„ïUŠ·7+ÖÞ0}û{8ÀÙ#aa¨2!j?™É¯SyVùö¼Ý‡rJJó* ±4z¶kH¤£jmwÊ„þÀ‹´€vßm‹…©¢':Ñ{¹ÆE}(ÃÑÔI£ïÊ1x½9ð†‹Ÿ|‡Aç½T¶JsWw-4½¸™ &ÜS¥yÕ„ÿ±zÑ8ÇÀEn>Ñ%—¶Hׂo°÷KJašÞ½£åÔÐÑoÐlIÔÚØU cáÌgpr G{A%I)(!úQík…g2‹Ò‹#ÎÏ8ñbO¦…¹»gL$(4:0ùôÜÊéwÃÓÅþúì ¿ÃK4š–±4Pµý!»Nä\¯(¿HjÉáôC¼5ÎnT-š4yé|o¯À†{jo¨>í¾úøP×RÏð4Î#ÁþSuж¢©p8gÿîýûxÅoǬ>c:ÃÛÑ ûwF]cA×£•ŠN§Àh_s4ï´kÖùÌG7:Õ(ÑRÅoתÃçû†ùx~ÿCcuy“j+ž­ÕK§zxxÕÞº]s¦Y2'™šÓxÈ£D<ý ŠÊ98 ÛQöË0"ÏIkyÕí²¦Âô†C:3:–5Üw«OƒcÍPF£ß LNŸÜ—[¨ÊÙ»goQ1ážý¢VmQ>ç-U '.ôò =ßÞQqášÊ(¥H,“TÒ¿ˆ}}Øô÷ÀÀ}† óÿ1Ñ endstream endobj 138 0 obj 4363 endobj 139 0 obj <>stream xœuX XSÇÚ>1œETâM¬¢"¢‚*²IYD@@ ";¬»•Ѫ­(µV—º®eQ\êVeQ´ *—¶VíýN:ñÞ;'xÛ¿÷>ÿCH&sÎÌ™y¿÷{ßo"¡ŒzQ‰Ä8 !%;!39.Vüæ XK„Á½„!Ò,œúÇxÝNz´ç™)2‘"£òÁ‡™ …ý¡¹/äö£¤Iî–ÝÓ—§å¥''&eÚØG„Í9z´Ã_=Îîîî6‹óþsÅÆ'!#91Õf8id'¤,OS&¤fN¶™NîNII޳ILÉKKʰ‰Oˆ‡EƦ$,³ñKNINK[žmc?}¤ËرÎcÈ›KH²rqV†Mxlj†MMXBbVJlúß:)ŠŠ š–—77Ø;y|TÈô´„èPŸKfú¦'ÎòËH óÏLÈZ:{Fö²ˆÀœ”ØÈs•‹çØeã0tŒ­£Ó°±Î._z[é:~Ǽ ¸-t™4Ùc„'E¡l©h*”ò¡Ü)GÊŽšGͤ|©I”5ŒšEùQ“©±ÔGTåO9Sép*€r¡FP³©Ô8ÊžŠ )Wj$I}L§FQs¨ j5ššKSÞÔDÊŠ¢B¨é”Õ›2¦úPÊ”êE™QRª/Õ¢©þ”9%£’)5€Š¡xʂʡ<(Kj åE ¢¦PVÔTÊšL ¡’(95‡Ä™2"ÙÔ×ÔCÉI¬dg¯½‚zmïõZªJ+ú-5Újô‚¶§?¥¿£ÿÉ g»˜ÝÀþÆ9se½ôî­ì­3¶3^h¼ËøŒñÆ¿ô±í³¸OiŸ»}´&«M˜¨Lûš.5½dú‡™³Y’Y±Y‰Y™Æì÷¾#úºõ ë«ï7«_V¿¶þCú{ô¯7ïoîk^fþ“Œ–eËÊe ¦ X< e€fÀ¿xg>”Wò›à¨™Ð€Ô:¶TòP+…i:%ïø^)D©±ž1Ó9¡2ˆV cK%B*,çàåô[ïÐ)ilÂà’÷Jú¦ –Óð¥ŠÇ ˜ÀÚLø §ªuȘB?Ò;`+ÁvaÀW_¾Ìmå’ÜÂ$4èST¸%o+7•ݳfÏÆ}¨Þ±ïëƒ{wïûú êÌŠË*fµ&•š·´y=†Ï;,e§[È„²cú²Çp†•µþãJËÓ;gâ‚äø_‚ Ûxe¸g”Ò{\vZÃ’ed¶ m’£Ra¼æ7^Xu,çpb]ÐaoÄá¡NØ{cÏn°…þí`@±b"S8qQ¬âœf?€~`yIõ¬åÜbïb…Ù‘µ`ÌC¥ ž‰«q(TÓ˜f`¼SoLiχ«0_¥%Þïù@#|¡‘ I+­N‰y'½ï‹St}iÌ¿Wj` N´ø85lUCÚ¼] íÚ­¥Lh·5ÓŒ.ì­>ÃÉÞ¬*½xÕ iâOr2áÎÑ—î[¡†œš„ã '¢¾ö%[¬c´¸‘‡`/¢ÃëäÌùJ‰âP|aÊŠœôÜÔusÈMø)[!‡=Ž¬Ú—AfÞ—¹kY¢JútYVfV†rÕBÄ,Q«`Ö*BIôÖÞpžÁwô&ôTÁ««XøA0%qoèüd'4tJá€p’Ç#\l±öÑØÂHÑý¦Â ×·x´b³ÿüжÀ}gy:9†µ ̯ª´ qÕì2!H^¥æ;á" {°–ÐÒ ïg’‰[ì‚%x²BV‡};±†Þ«¯¸}^.+ð»Ïâ|!–ï¾:÷/›Îš<Î)ä˜ÙGÏäj´RÁªy¨B ÁŒ'\*–eพôUkY3Ý’žÛÔrTgE–1N¯eôý-=\Œ˜†Lõú ô’LµðJ-©ÕJu£ħ‚«“ »¢‘È#%"tÆôD;„6©ÖìseÖ½´üЯ/÷A7šYµ:¹ UœìIv÷‘p¡`aèµËyË(*Òw§ìŒà ð£T$).‰Iq¡ÓRvìNåÃAæ\D]FâÀº $@ÀiŸñÐ +|–Ç“œh÷be·!?ˆÿéú$Ü÷ ™4vܬ‡Ðú~ÿPc@¾ šÀµMrú©T(†•±“‘£xK>b( ÏïÒ ƒµÒ. 8Î@Ð ‡ÈÆF ÇÁ |œÑ¾·æ…›0•…a?ŽÂ!8hÊHPªÖ{3fð”,Ó[CvßLXg)«luø°¨ÃXô`oݩܬùàÞÛ¿ßÌ=a ·¬Û¼ E ÅËæyr²ÊD‹pj ôåLr¸N“Y cÉ4SXÙ«›±sXãAΘÃ^ãË}ÏF)ªc®¬¸Œî ïÕÜáÒXä³fQvjVÊ¢¼¹(%g~“»{íWŽr˜öm3¡/R¡ÆÇÏœªÝ}A_ŽLH&ƹÁ|wÃl‰ûGL7v¶Ag®¨ºÿÅ4ÝŠ"~L[óÇŸ#`>Ø‚#^BþþF½{׿>qMþΕƱ°‡_~?xQÎÒµ1hvöלÀé-! ÿ˜tïˆïËà‡¿a ¯Õø5ÁØF­/ó§UÕtÂâV‚_ôÆÿ{lJ²ÎÌ#h”û™xè§T¨r¿/,ÏF ƒ¢£—z/L(Ù—-_¹{ýî •Ü8f+6mšCHú||ûµ*æ¬ÝÅä½þ_§îC•ƒjÏmj<™:{‹üƒ«Þƒ)¥’–bªÒ’Žï•ÄPE;­¨XSP*ߟ_œŽ–p=¶ª ºüÑ´…™GËáH"Âw¢;™kµ€»'½²”½ƒ F³Žé¨ŒsVÈ„¢@´ð˘å  Ë™¤ýënzU&¹'=Ž{QŸ:v¡Ã Ì=š±wˆicD,(‹¸Ô‘µå9²K–¢n|L¼½Ü¦ 2¨VC…ÚüœVX©&v ‹x_u‡ô†h¼Åè1xд€ŠšEòyS;Q7þ¿18‚ôaؘRù fØc£'x2rAÓ3ÃæOô‡i„-9œ CðXˆú¥½²ý.±òQô5Âö£™­Â¨&É’‘_†Ž‡anx˜ÿ]½ yRy»´t˶CòvÕg+‹>A\âêâJ৬¤lsJ!¸TÒHÆŽ"cÝôeŒNi4Ñö±XNνV™×jfhág¿V”­°‹/»‘؃DÛÕÞt© ™®€óhY×Og–„Y¡øì¸”¬Ôœ«f"±7éLÚ·ŸžØ|ž~s@IÌá¸zÿlj EèniÍɺꣷÑ-Ô~Ǿ\7PÖ6nÿ²CW¬îß®û¸Û‘›å=q$´¬-•t<Œ‰ØÖfâ'ZÒ7éû@)ŸÃ1w3[£Ä}™ßDúr…l ¡Ì ׳²ó6á¾®3bOÝ’ ¬›Þžu½þ[[]Es\–ë-‚Sïëi!T‹‡Wìœüü¹Ÿ!´9_îÃoÚVT‚¸çuUw:SbÜl;CÒèµ¾")ó5kÕúfwX÷ÞZËTStå_ŸK!’Ðw Y©˜Dô%Pe „2z ƒíõžØFð$MÒ—Ñÿ¹E\Î÷$@CŪBäþ"æÑCðÅ•ô†Ô/‰÷ΠmœŽÓiƒ¯ë䥒'ZJu¢{í`Gú„âÞ1%’䉇ò+ÑeNØáHú±ýïN0 ¦@¯ç0\.ì0”¡*m©dÏ XA6óT_Ì¢Yë—æ§ç-OÎ_‚8ßøš'ЇYl¬š ýAÖÞærá\O¶‰{.X€£aOvz‡a¤˜þˆ¡w°! 'ÈÖž3¯§ßõN´„"8M¥à@6ñ½’P±Å·UÛf~¦J Aü¶ŽØ×¦5ÑëÔOvTÀ Vë_ù)AYñIòŒ´ÕÊMs¸ÇÌ?TV!îÁ™åQŠ,%eÌX‹ ò6.[’ž2ùs²\‡;¡¿ßi(¿tM¾=ò`Æ%´•l©ø‚T àÏ£åkóÓ3“S¸À„£ —+u•(4»¾Ùz¨„û«²lÛɹžÁ õïñRá==‚Ýä„ñç-í¾©ð%}È_±Xk÷bpƒàõPo8üKOÓMx«eÿbÃyØÏƒlù`ŽÃ°9„Ñ LÇÅ8—Ò/ À/ _õTëÎ!M|YÊê ´%V§fe~¼|£é扄9~/vˆ_oœŒŸ!~†°Ï#.ŽôÍž-O¹»ßù¢+q²ºûìß ÛKp‘”n— âû ‘•Õ…œjHmµy7©I¼Ákü;,Ÿ>7mf¼³0—ñÚžª-øïUÛ>=àè , ˜àgÇ`+}ž$tüðÒBšv „(c`¢ð<ô/ Ožê¢J%­¤°Š,b¶ Â;#ìr þJÄñ23ì¢Á+‚quà©[îÃ0˜þHªÅIü¯`y 8#p ‚À`ÉÍf4Ø®Žˆ©}ñ¸hì9“a¤”‡¹’GmÃFC«rg¥ôs©O¼¥½éxá¤oPGr¯ MØFdiGÉ ¨ê*•t¾“v¾åí®ç±ÈoeÎüM\ÛrâüS˜°Êô#T7œž1ûp]Íìƒ Ÿ2šh÷X,^`Ý= TÂm¯™kHè|K%µZéE]¥l¶"£*ÀŠ)Ûhñß=â£ÞJ:ßJ!œ,Æî­(A9eº¹äÙyä¹xõûÕôeVëV‹§ ïw ØöPâUO`£n'ÁâŸøù"Ì  ýÑ5És+Vµfqø»p{BiæÑ‚ò{Ößæ<˜uXñ£ ]à »ö†½´µrç±RRX}òx³Ê ÝM«ŸulÅî•Ûœq°…•=iü¬fîfo®AÀxßǦ;¸KìL½„÷([Rn ;gÎÞ/æ®á.^Öü›ò[å b#Ii+22Ó×,Ú`8K´ *•äl7TvKáb M]½ 8ÓÁ`kb8À(5)› AžõÓüäKêg @œ§Ñ‹šÑ$,3ÒqþÏ ‘5/^( õ…n,f—F »„-¼~‹F5‹MÁ’œ<8Ÿ Su gÚ$‚{·Tø¼“×÷fv|ÿmeç…_/ üåÒ…Vbc×2.ÇVÅVÍù&9£€eñ¡é «b6Mç4ÌçµÛï*/?ûÝÁÄ=¼6%|é¼ D…Ól?qQÀZòà†³²±°Õâí<ö¯˜ „“PÊë !Žö`9vÓËñx±)ö2Ê@A›yJºýºÉ)V€ŠßÉTn¦eï^ÕÇLp™é4z~í›õ R£…/Ù³ì¤KÒ+R§ Þ VÝIõ“’éÎÁcõw­ÀlR+¶ÆvS<ñèÏäfóÙ­ûwî+­>·ÿ2âT5 ¦Få)ã?Qd¬NÙ\DøÊšN4J„y RažàÄoŽ,ÈZ‚ Ñ'ÛrváÞÍó‹s·ç¡@”0wîÙR¾¥xkqñ¾=%ß bT¾¶xýÏA~rdõÄýpº¾Mñµ!6e0²]"!|HBø¼x쥯wí¶Â¢@?Їю,\lçq:épë*pꔓIHÓ“ô½˜H½ýˆ9]w|Yý‹ë¶Í í5¿úa6Áb׬âåe+NEÜKyH°°ûõ-ŒÇ—xpTÜJåbÅ!ˆ¦¡VŒ#IUó:íÂsMäŸäS™ÛŽp¿–Ù<«0-i„ëÛŽ}‡r¯YYI£…]-û¿D9Iº§«º'ùØþçbʆüs„ܦ«zÃô¬ÿ†ª£ì©Jr® Î’-l šŸÜ”fL!Oä›ã—CG¸Ï7SÎU…ÝLQ‰gíW¿‚:k±ÌwvÚœÅf˜vååktÿÊŸ#‚=žÒ0ÓÁ%4p’{pÓ3Íõ=I"üLÐÚ#"•ê*Èì‰×ûaûH\È=gª!§ æ€+$s-Ì;¤åDs!äi8dú›÷UÃ>,ýÛû¡2¯ìì†"òf){#X ½yÙ«ÖsûO6ZÔUE¬¯ÿ8O,±?ñÞB¹ìͤø¸™žVØâgg°›Ÿ»o»æ^-Çì$ÔÆøÍˆŽ™6-ú\cSݹû \o${£¹î614Üy|èÕŽŽëWÄß ’E%…(QEÇèûÑÍ ˜B˜áÒÄNB¿äSÁÍ6IU·ˆmLà÷`îG? È¡ínÕé[ç›K;ðä8Ÿ÷cì­ø«ÁGÅ_A¬GÀ <°Û4Öí¿ü¢O =£EÕ…M –òê›þN®!Áã'D^{Ñ}ýö#ZìS/`sRjF ¯ Õâ[h~+^D¿—öè·PfôAÆ 6#¸‰—ßIa »xÁ7ÿ?¦#<:ѼPˆãèvV/7AãŦØcá"µÏ¥µùÊ©?]„ä«´å·‰p~çõ¹¾B.ó‘QϧYf¹P&¦AåŒÊ¸£j›‰É“&¦õo½# endstream endobj 140 0 obj 5338 endobj 28 0 obj <> endobj 35 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 27 0 obj <> endobj 34 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 141 0000000000 65535 f 0000087544 00000 n 0000114087 00000 n 0000087308 00000 n 0000083396 00000 n 0000000015 00000 n 0000003272 00000 n 0000087592 00000 n 0000113083 00000 n 0000111076 00000 n 0000113499 00000 n 0000111540 00000 n 0000087633 00000 n 0000087663 00000 n 0000083556 00000 n 0000003292 00000 n 0000006061 00000 n 0000087704 00000 n 0000087734 00000 n 0000083718 00000 n 0000006082 00000 n 0000007683 00000 n 0000087766 00000 n 0000087796 00000 n 0000083880 00000 n 0000007704 00000 n 0000010938 00000 n 0000112101 00000 n 0000110082 00000 n 0000087837 00000 n 0000087867 00000 n 0000084042 00000 n 0000010959 00000 n 0000015343 00000 n 0000112727 00000 n 0000110651 00000 n 0000087919 00000 n 0000087949 00000 n 0000084204 00000 n 0000015364 00000 n 0000018299 00000 n 0000088012 00000 n 0000088042 00000 n 0000084366 00000 n 0000018320 00000 n 0000022166 00000 n 0000088085 00000 n 0000088115 00000 n 0000084528 00000 n 0000022187 00000 n 0000026124 00000 n 0000088167 00000 n 0000088197 00000 n 0000084690 00000 n 0000026145 00000 n 0000030887 00000 n 0000088249 00000 n 0000088279 00000 n 0000084852 00000 n 0000030908 00000 n 0000033993 00000 n 0000088333 00000 n 0000088363 00000 n 0000085014 00000 n 0000034014 00000 n 0000036649 00000 n 0000088426 00000 n 0000088456 00000 n 0000085176 00000 n 0000036670 00000 n 0000039715 00000 n 0000088519 00000 n 0000088549 00000 n 0000085338 00000 n 0000039736 00000 n 0000043046 00000 n 0000088601 00000 n 0000088631 00000 n 0000085500 00000 n 0000043067 00000 n 0000045432 00000 n 0000088674 00000 n 0000088704 00000 n 0000085662 00000 n 0000045453 00000 n 0000047747 00000 n 0000088756 00000 n 0000088786 00000 n 0000085824 00000 n 0000047768 00000 n 0000050665 00000 n 0000088849 00000 n 0000088879 00000 n 0000085986 00000 n 0000050686 00000 n 0000053430 00000 n 0000088931 00000 n 0000088961 00000 n 0000086148 00000 n 0000053451 00000 n 0000058248 00000 n 0000089024 00000 n 0000089055 00000 n 0000086312 00000 n 0000058270 00000 n 0000062943 00000 n 0000089108 00000 n 0000089139 00000 n 0000086478 00000 n 0000062965 00000 n 0000068038 00000 n 0000089183 00000 n 0000089214 00000 n 0000086644 00000 n 0000068060 00000 n 0000073395 00000 n 0000089267 00000 n 0000089298 00000 n 0000086810 00000 n 0000073417 00000 n 0000077855 00000 n 0000089353 00000 n 0000089384 00000 n 0000086976 00000 n 0000077877 00000 n 0000080665 00000 n 0000089428 00000 n 0000089459 00000 n 0000087142 00000 n 0000080687 00000 n 0000083374 00000 n 0000089512 00000 n 0000089543 00000 n 0000089596 00000 n 0000096660 00000 n 0000096682 00000 n 0000100139 00000 n 0000100161 00000 n 0000104612 00000 n 0000104634 00000 n 0000110060 00000 n trailer << /Size 141 /Root 1 0 R /Info 2 0 R /ID [(§µÎÏ!\n-¾ÿö§Xçq)(§µÎÏ!\n-¾ÿö§Xçq)] >> startxref 114287 %%EOF simh-3.8.1/DOCS/id_doc.pdf0000644000175000017500000026456211143604401013316 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœ½[[sݶ~ׯ8Ó—’š/ úæ‹ê¤qmW’Ófœ>È’-'µ-Ç–Ò¦¿¾I`?)§Ó¦“Ax€Åb÷Û°úyWJïJÿO\¼?ºwbvWŸúÏ»“ÇãàÓÕÑÏG]Qùÿõp|ñ~÷àÌ-´;]e½;{sTÖvªìúßÕNÕºpÿaJU¸Ÿß½Ì¾ÉÝݵه|ï&Wmk²›|¯ Û6Úd¯ó½*l­U—}ê'4¶2Uv)“Ïóº(£qÕ¹¬ÚúJ¾µaÉ«Œ£Ùe÷ÂÄJ&jâÔHó4WEÙZ­³ç÷þ“Õm›ÝŠwágàlä·nêìz˜Z×îhxÌ¸Ñ ?0Mm²ÏBh]åcÄ«êÖ­þÇÙŸTÙÝn¯«^#—N䥬 BÉö¹û½ª»:{”ï«ÂêÊ*þÅ 2w”½° â)ƒHx—7MëYðˆP[QÚU[u½kÃQëá¸>ûÉ­Pjg“FKZGÉÚf׺¥,Qªhôîì‰ÿÏß÷[†õªÛ“£³?¼ÌzîugŒÊžåû¶pç(›ì¹?}kø¾Ï÷¶(K]êìD¦~“—EÕ´mYeeÕ×2á̺-ŒjµÛ=Î}:Lèê&nf,κeîñÈ5˜Ã¦­ã1ã¡ýÙ€ð[GØ(]{ #`웩ÜçÚ:è…ßß9UÙF×ýD‡áì_¹-LW™ÆA}oÜ'Óhg³añU?ªt™ìsá¦F]•}”᯹2…sïnäÔÛ9Êa_ ÷V†7Â6lòA&\Ó¹ÀpÏÐkÊë¹|ýˆÃȱH­Ïž6¼‘áõtǪmZd™p'œzL˜Ò&Xü‹–­1ƒŠ£ª=£õˆ­kçFjg2½Ñ{î<°+‹ÜÜÊñœœ)•¶3)}%[Âù^É„Aq„ t<êÝ™OB5Vb/…,ppI5!'½•¯ï}ÕÞ‡*<Ù @!î_å×H Nýã Y÷ÿQû ‚ýà.GOUµÞ¼›þ³·^¯pF ˆ©AøÍå—µÀô€!^®áí#b$_qk£ÞÞʜר@¶ ž…ý®ú¯uÛeV~‡aÕ•Ò}X Ó2¡Äaä«[€vÜwpvµÍ”áU† øà°ª¯fÞÏi~Nk;§ªÞ„+gßeLø•,px“3;E/A= (ÜœANþO÷»‹F >²ˆX†+ÑÅQv)H]¹#ÿšk[hU5K¾?òø~v¶²‰×ú!—0OQøXÙi@’nKå‘(|1’âðdtË6q»à:7h2ª,Õdœqêr–T‰s÷d(±Ï$|!id ÀTp¡•™Ú|ÿ¯!kà–0q¥q9üÂ!0ñZ„?·'Òº¢ëωp*´è×Ñç\®)Ò¨„C¿¬ÔíB¢Òg=0Ç9=Ε×W”îRBÂcïC4…²1‡@¯I}\òJµOÛ«z!óàÀà6rCY³0›zªù7žšBŒç¢VFߥ­á"XÕ(ÏôÄ"AD®ä3ݘ= ÂG×:NT®¼×ú††£1aw… GÎRÌÞ@W¼À$S?G…BÔdII’äÅ Àô´os¥}œ6èšÄü–’;ö5g¢ mQà»°|RD¥Òˆû¯¿s{YmZåÜ}È tM“C0C”ërš˜<È^N¹hϪœ©þ` ÀÙ»(ÅÑWB"9F·ÊÇ'_ì%Ú訅˜á¿ „|ö#Ø'üNê°•D‡3Ò[Ü %{_ “HÒÍë®çYÕ/&.¥À-5fZ±\ÍQîb…8-”sž×ÑÌo1Œ3Ö£qнB5ˆiýª“ ¹É ½‘–ÿŒtª´ÚXʇ£TfŽÆ6ÍÁÛ‡þëmÎ<ÕjÕŸDÈ;}Ïb!ˆ›Áe l.îš®­Æš¡´Knˆ—÷% zÔÞ1ÚÞÆÑ«hÉŠá•J“šÉA³Žf Ò-ÅŪ.?Atky?^ òãðó¦£ÙÝŽ7_ˆ÷(f?ÑŠŽc6š: *‹Ù]`@ —8ÙÞ”}gb-~·ì+–€UyÕµ‹‹\R¬¬ß~Õ¶Z;@ÑÉ pÍå&%Ðb”«%O¨G¢]ÏÎ5@f"dí ªƒ=!ñ\Ò8OìR÷E÷FoLéI¼ŸÛ°Ë-%8HÔ×ÂË~mÔP«ôB¿ ~Ëm&ððsχ?‘Tøu[´•!Ö¾zšYºÒª¦^Ðýìftb³²B­˜ýöb—mnôÔ8WÊ#:»d)f7\ƒßíÉêœ*ØùeùiçbR˜Îëy½c—Þ¡˜;ÝðµIgçô&·ZlôOIë· ¿ÑQ-.<‰0lÎ`x]Qé`w4ÈÃa¦Îhö’X¼:ä5‡'-æ'"ÑÛœYÑÆ§H¿´mS§8 2;ÉRR€Øz7±”tÉT¯œ… )LŒK€ÍB]õaäºè$Ï8ãë5oZHï¨7ÞÓ­@I^Z¶ÞBø.€áü>Cæ¾VUehPc›ÃÁÆèY8މÍF cáOþ2·­½S:Ësœ­é²¿¹MѵeÝÏUãK¾ø„…] ùoØN *y8¡½ßI{Æ@ª+ñ-+ó]ÇÖ&rµuß_ ‰j&Au{\“–‹uQ[ÛxA4¦°•êÜJ¥x:EDîo–à$/bOzGÂzG´êKß \]ãû ïÇ#<•@óû¸~ì °¦hL¼8äj‰%ôC‹3¿e= °è‘ðÇëî‘3ô/¢Ï¿‹ê”„ç5™y*3OáÈ€2€áÉÍêÆÁì¹Ð}"vgMÀøâ¥0ljƒ& Øá…|P§2ËÙ@3¢f^PŒÌ{–&hås¹ð•>‰ãXä$vtê3<5®ÓŽ‘˜5ƒQƒ‘Š· ?™AMÞO¥Ždɶð=ÛˆwÄÛ1s†Y_Ë×ûq*ôœÏòûƒ©= IGÀE|Ó™6|¹„g8×ùÓ‰¥¶h©}Z TçF;€†ø)`Ë›,WøÁ.ßv=º -´Æq_ ¼œ ê~Ò©T<‹:‰wÈÜÓ¬&ÇÏòÆÑoúZ0žzªÎ1k [0¦àèiˆg4‚'e¯klÊüÐüßzvmìïõ]:«Àr=k«oÔ–P¸ÒWpÇ¿N¬¶*tìÂÑ%qþÒâaiÇd.éGilQÕ³v”ã³£¿ºþ ¸T;Õendstream endobj 6 0 obj 3183 endobj 15 0 obj <> stream xœíKsÛ6Çïú:’3ÌÅÇ46têÚî©éÁ'mì8~t¦ß¾€(r—ÒÊ”Ť$Æ„–$ˆýáÅ‚ú:.Èq‘þªÂûËÑÁ‘_ÜŽ¦‡ÇG?Î 7£¯#/Tú7=@Ëï/Ç?œÄŠã ‚Ÿ|"_¸Ò*Œ­‰ÿfì .~ár”ó“¿Ë:ñÜu0WÁ'´NþÈ ŸQ8:VžXmeÇùD é¥vÙ§„q>~~™O¼°…s&»ÇZŸëÏOs-‚޳»|"…QFÙì ~ó&—BÙP¨t¦êó×ù$6”uÆXr*4zŽõoãõYÒªL ý*Zã¿wÑxÓ<úçÉÏ#) Q¤Žwr;ÍÓ]Ì.z犩ç€a½ï´ëhÐ})Há´ÉTÉ5.Ò0µÓD®ðã (áËÆ’,œß̺OpÙ~ážÏ3×k™Õt>]¬sÚ8â9o 6%1Ú0‡¯œ§C‚¾Iï8ë:@€Ú_¯ácRZጚó±ÝçËÀï-Ãs×ô] ÄÞ Ë@\=üÈY(‘ª§‚”Š_ñÙ‹¼VB“–*¼Í~Ÿ•F:J×wYMJ¢Ã-ÿÂâ»{[-O¯œÉ~*GUè¬(ÍqLx†O}ŒÅ™Q[èßbµhŒØj7V¤1Kͦkµ…yÐjºn¹ÖŶYM}F)+ÀÖÑ`…_M£¥,¼(ú)Ù]¯ö‹^={D­^­9¯>D¯FŸ$ŽzQ»~L\’,]FïÓ*P—ë’‰iÕú¦BχaŒqGKxUç'¹`ì|cýÕ¾Œˆ*‚5Ü!ÁŠ@\¶$SÈÏéšâD<4àEØ4½_àty~âÙÊ?2Ã|Þbª3µ!6\×ÄÚ²ý^ã¯ã¹$aOI¬8kÔ³Á®Ü;ÑÆ +åF0 rƒ¨ bµÉ5[deIo@!vû°oÏÉ#>ÊFæ£,(?/ß~[ƽfè`yBEÔË9ŠøH‘°å}»LˆÇõC»‘>»Ë?Œ´Å­JZz!Í [•X”ù^Ë)¨¿e§†W­I 5²ˆJ"µÈQ’qÛÀk•…I拟jñElá’%ÁãsYgõçDç]ÓÕÏêLro×s³“VÓÌÎgeæ&xPIáí-ÖWÓì^&´Þ»U×>Úo Âl-Té+HӲDžxº!;À£†8㜘ëô[ƒàyÓ ˆð˜[A5ˆÛÆ@N)<ŒÙså1ˆ.Ú·ã¼L†z†Q)Vx0íÔ³õ~áde38&¾mÛMNW'#^Í.“}1±t0-Jí¸ÈÇô¬ ö0܃%T!µ´8ge±9ÆþCX‰¦ZŸLQCUU×@ÎÆÉ/îV€Ÿò.Ĺ$h”)³Ÿ ³8ÚÝŠb_TÓ⵪;´òí̋–]c„üv’%ù’käx¯§•¦ C^> •¨+!Þ–Åk°Uæ³!Á o¡óîøí¢ÉB¼‘~k¦A(õiÕL Ý“Ød¢7–ÄTCz0/eœAh¼…„VXJ´‚¬uÖwÍ™%ÈcãÙ§,ÜÚöýBÎzߤc¬æÌêoƒìCÜ(á´Oæ©Ò-VíHû®ûʃ¾Cf{Ma…·U».{SWë+·Ú–ö)ÙÀÁÕ’·DÎFŽK4`[Šú5—^u—&ARøP¯Ü·¢qÿØ™Qä†r 4îv.²“}ÝVë{³vìÜ2ßÞÍbÚw[Ùrc¦¿œnààüìÇ,ˆ¾‘[¶ÊÏ5h©„Ôøs ¯NF¿Å¿ÿÏó±endstream endobj 16 0 obj 2018 endobj 22 0 obj <> stream xœíÙrÔFð}¿Bo‘R¬<÷‘G(H‘äód(jÙ5à`³Ä„¿OŽ™–Ô²´»¶“‡°TÑŒzúš¾Ô£¿2Vr‘±ðkõÅâè¥Í>\-ªåìåÏ pùañו2ü©0¼¾ÈÃFÎ3_z“¿_°Ò{ÇlM•gFÃÿ½Î,g¥„‹ÅI~\,¹)-7"ÿXÀ2Êæg…Hz•_K[2ÆÉ³‚‹R Çó‹b)Tɬ´ùiX´Nz¼¸„.‹¥Û…ÏWÕ¢ÌæŸ¸IàM­ V¥6†IŒ»Mà$„½•ʲ©–¡2.¿Nä‚j˜Z7ùN«ÿ\>º#Ñöˆþ»6Êü¼kÎ{†à@Œ;óøC‘ìÒ°w,~˜ÙA ´Ï?Ç׃AÃÜ{gØÄŵòQãðZkxáKh÷OT—ß’mq§ZÇà™¬Öã'Aò扷ÁuS rÙ›1—å>X80œ…þ4?¡lxºýiÞqÃÐBwþ¦^•;ÔŠý¬²!ßz¶$xNoÌ…ÁçÄ£?Üd3‹Èqç›s*Þwððƒkâ/*n6Èë¾ôŠ6$Ÿ¯÷/fóùͬØ{†b™Ú“™ÖÜß5©!Ï­¡q&EZ¡¢ÑÏî÷Ã𾔣2Þ¿Äk¾Ó觨¦÷ÉÉPÜ×Y'ëÒUS‡…Ê¿ÜBÏ!(§BµZ0WZqxárfz88›¬0U#7ct&d;F:¨ÙÙs40 ^3øª¦aÒ;‰'Yh¶‰°Uz~]H^+ñZ¦_Ðxi›?KäÑ8‚sh‡ÆuÀ”³Rè´KÙÚŽšè+s†j‚»Ê¡Ú¡Zp+rŽžšÚÎì8‚‡vÂ1;sãìË3Úð|—-šN÷ûTσçIm¡)¯† <Ýs÷‡ñw(:çÂŒ—CÔPŒiLOéãê×zn.À÷\œ›¯HZin~ZP©5…tFŠ…„EìrMð:OÒ ‡” ùQZ¥/>Ô¸z ×&Ù®ÛÑ=±M!ŠîÂ…ÈbLÞÁkjÿR§òéEGÏyZÈPß<Ï_ÅR…L$UÌºÑ µ(M-‚4Úàs#®!¬¶ºdQÂBUè’å¤ã16æ dE„n3éŒíAs"þW·ŒÞSŽèæŒy¼¢Ò½íf¿˜ãú9ì]²tìÐct=·-¨¥gEô±ÓINT¨!@ŒÑ Þ—¾ù™ªòWÉ€ŠlýµsQÚ 4¼VõtÒšIV)="} u™LŠ+Ì :*8]ÿF®´VUïi|÷4:ù½áD×¥ø¸3;Ž<é³ÄF¡,1C~º¡c××”^“)é(­¾¨Ý…ËÒyA¸ËeJoŸÚf¹­‘b¦é’ÂÐmY_+h™N~ªò¥ñÐËŸSmÙUÑõE}Ó2êjš°ærß¼BÏŒlMËM½[§Ùs#8ôÐ8íÓÇ óíÄ—h VG<f:"&²ÛŒ–èÈ-új¥Çsµm«ï#b™“R«éºû>©˜X­qʤÓG‹º‹µ¢‰+LæÃ+ÂDô‘oH³zpÃV¢ß^Ñð$¼7 g¼›ßiAþé‹ "†¬G±¶…Ò×/TŠë(×iŸ´(5ä¥e=ð¾Õܬå(¡¸J9kP»fç3,â9 ãþKQïM‘Îìˆ(B¨®„:U!Eÿª“œY¢Ú²û›¼wã!Ob€0‘^ÈS‡%ßj1ÚXŽmcŠ CÆ,R¦ðeTŒ82í²èmb's†HLô!ÅX;íö 6™øbªaÓÿbêÿ©E§£ïäòf˜W O'oÕQ_zC2,Žý‰4+x  âqýVøàÑÝæû§‚žü ‡]Ò„þIgaLæj—܄ⴕ᫅ú¡³­ ¼,BŸ#´ŠÐ±ãª„©63¨Û*­b~i÷M„Îû\¸-mOxÕ‚" ˆ… âÊ*xLý$R}¡WAâ%¡8‰Ð»Ž’\„OqÖ÷9a·kÂZ—Ú$ÍhÕÑj–@™ÀG$‚šBÐS6l ×퀋è%'ÐLQØ&ðrJœ1Ás¤r¥±5 ðÜ8h*ž$Ô |E2û–ÀÆÉ­ø?’Û–è¬~Màcb›ˆuA±R™zÈ#–,@ÓÚ¶ï†Å÷ Î\)˜°h\,¨##^ô”¤pØ÷„¤ÏXá†$›8nÙžZ/¢U:§ùôxñ;üþàéìendstream endobj 23 0 obj 2098 endobj 29 0 obj <> stream xœí\Ys¹~ׯ˜7“)s4ƒÁ\¹ªlËÙ8e{‹J¥ÊI¥$’’Kmçטݾ!†’Ö•M%›‡6g÷×'@}‰’8Q¢ÿë‰ÅÕÞþ‡2:¿Ùkš£?tÄ×ó½/{Uœéÿ5 œ^\E/çj È£:®‹h~¶—Äu]%e;k¥‰ˆÊ4eͯö>N^MgYœ—™œêØPQG‰b2óå$3”0Ô) QS‘&q)š1‰PËÊÉÓum¨[C­ õÕPË–JëlrBä-lˆ,‰Ü'2#RÀa×D~…ª‡ÍûŠÈC"aߟˆ¼€'þ‡¥D¾#ò%ì{Eä ¶^Oÿ>ÿÓ^&«¸hÐ3_*¸´)ªºš|£®¿iIYi™ëQ³~ØLAMTíX†LVFâû[!F¼¡=ܹ$–+ãé äÓãAÁ¾ŒéçPÚ7p“«Ð§P-ÖD~KˆNnÚ0Dó·{ó_}œ.¿6Ô[C‘Ýø£¥ðLD¾€o€š_‚¶…kF±|Õ3a 9z çýÛh˜è ¢n•¬óðTk@ËT2²sÛNV©ŒE=À·ub¨ à™L¶}l'7A¨²–°¶WczÊ„€d‡@9ÿO׆"çs¾^†ØTIfI/à‘+ÙjÏájó\WpgÌDå7<æ<Ð ƒúàË“‘ÁâI²,xAä‘oa‡ N±IúmË r…ïd&ëÀDw ;|vÄ ú7b™w2êâsð•Iñ ’¬Ã)<擆û>=íãsOÿ ¦!‘šØÇØêðÎ\sg¡g(¬Kx¢›±Ò8ð¤1fóèÇ†Ê õÎP/ Å" –%D³!0†Vœ'`13V±x«Ôx¶>EVÈ,(S©;x Gm²I —!FÝsî4)f%ãRš³[¡,k“Lvx2IÐê1ZW®µ´‚¢`†ÌEr‚¸‚“1æ1ùM4ÏPÔ`+e(Vˆ¾‹Åº„äEèøxÞs¼!ò€Ðõ Ÿc¶[àU ßxÍDYÄy–5H“Tµˆ¬ÅšTÂbˆ¸ñ,˜Îÿ·ë¼;l„Îûóþ§I‹¤¶4 þ.)ÒtŠšK¿;Ü1<(§Þ¾0Ô‘}¶_HlȆ1°X"âgʲ†­wpµî›3|ŠEhØù?H¹U!Ttk¡Q$eœÊòÿÐøù ÁfÀI;®5cûp ; Û‚ûÒÔ\ß̲"NŠ:W&Qh{¨úÌÿ©Ñ“ VCI‹¸L ¡¸§ÌF*“$UöuiOØm¡ËTÖ]c&k%ÅÂR1ZêOuc…Œ“2+ïLÿKóM¶sM­l?­ ØLS—•ìçÏ¥èÖÌÄz²Ö5Ü@s v*öIùe¡4¸’9ß&ë{×l3+RÁ'ow¬Yø¥ýžWgÉj›êô"«mö˜6|˜ÆÈD\Ȫz4Õî«Pê„ìlñÖå´nôÆ7ƒØÝì0Sÿ7QU·™YÛÅí¾ý£®ª[‚®¥öƒÄmÆ!}*ÎýåÍ)Xß3jeŒ>ˆð¥êœ/|†€º¦ Vpo›Ói­ð+d‡…*)-ý¸³äã†Ë÷y˜5æ KÈ¥£i¦v˜Ô)·M?*ߥ`«¼îÐ|ÿgÆé½%õÒšªvžÅ*èñc©ÃfyͱØ~®óœ72¾ßúXíæcòaòòñ„ØÄ¬ký6Mµ/ü /ã8/#/ý<‡³W’< [†i­Bf‡ôZìûIhZ/‚i,j£&#T°œ¥ {fmŸÓ­7~„¶@1§¿…GÚLšpÆýcƒiQªÖìÉY?$‹O®Åi" Ö÷²gW¯€•Ú@Î#ýöâWwPL"HZºˆ÷…´±®âT9¦žOÒ³G=¬ðv¨Rïiw¥t ÒkeFálžŽÕʬȋN:4ö¼eóÝÑMßs.mî”q]—m±¶±|¢y`¦ã)­““Pzqg(+šcNP£e¿°VÈŠ¹Kvf6k¥,Œš±8U±‚®¹¥r}ׯIb׌R­†Yê¾oƉ61Ÿ@1o³n>²BFéŒü<‹.YÜÙûNYÔc; MÆ9ÜÕ)—’Ì-<6¶Ø‚2kËZ­¾È~M(;;¶×C6ÌlÇ‹'¶§“MŒ™iPˆ:MŽjv€ýÈ¿ >,ç1ê¼ î†¶È ‰ø¨E’¤ÜÁÔ²¢ËÕI™ÆRð®´ò„@ê´ÛB Š5E¦l0þéfosä"KR*¶õïgöÙÓD%$}ØÈJ}tFïu©VN%EºÀø­¡è~˜®¹ÑC<ª“÷UtÐ_Ž¿Ë¡«çŸ¼ Íó^‹úJ˱wv¬8ˆ/›ð»¿%$½’¬%ò”òÀzG[õªj«2·Ílÿy ÐÄÔ‰iKã\—„ mM¯[hBNCÆ‚9H½Þ Wív±{,Ü*½Œ·/la\\cK˜tDzaZÿ:Õ“gÆw )™AÒù}QWúÕA¿ô3ݪ‡Erý;‹²¨íû¿® Üÿ±WfZÁ¤dމ±• ’¾U{ô(*QP‘a=Eå•r{Þ%ŒîíìeIœ¬Òé¹3Ûa@óÐ࣠Š,–¢.{ð&ù AÜ5Ït^Í ZVÅU’‚;ŸPu]²áÁJ f…ÛèmË6€8F‹•>‚ñH×RVœ·vâñ§3\::ҧÉ·»tGp+$m×[a=Góâ¥Æ%Y¼E‹r½ôˆ} •ÝX>-/ãJÚ> e{Ýiur2Ê‚è¶s6_Äܪxb@Až,ëïÆðuèŽEøï‘“¡¬k×ßáoͺ2z/ú?Ö0*éú%:¬b6+™Ûˆjµ%Wsߌàm³ËI\“g[ñ(„A½‡ªDŒ´Û GÆ27Ʀ.—Z‡`¿ mgqÃ$¶wª·B›ÈË’AçB ¾% [´[Ý|¤4q‰ ó—‰Åy Åæ>…Ñ5…EC/—¯Q­èõÚB¿Ïn”~Å̦µjÔnد/A`!‡ý#úõvZ47~€¿Ž >‰ ¾[Ökºu=û=\äÞwAO_ÇñÚ›Å8ž¨|çÑÌïŽQŽí…qlLñòŸÇZ—nåÆ*°ieåO·–ÈAXösܺµ.@V¢4òÉD”ê·oô‡ÊjÙý¡²"Wÿ®ó(U &Û¿U&¦3¥liQÊæ§á2Îë$‘“tª"Ý6wœe•’·ŽŠ„ÒJê1uV—ùäØ4F4XÿäTu(“¤ÈõÇÍ£O5F’L4?:-ÕÈL¦cÎßZëPçͯ˜ƒóßIêGâšm¯¨Ü}h°zLÖ˱—ôlnÛ4×0ú•$ˆ4~‘$|†1Ìuð“!œä®–ݽRʬ7n4÷osg‹×öœ¥s3c³¼¿„£Ÿ*“{³n Iåü ÌË7^Ï÷þ¬þû±ãd·endstream endobj 30 0 obj 3396 endobj 36 0 obj <> stream xœí\[w·~ç¯Ø·J=áf½ç´=§qœVuš¤Ž|Òs’>P¤n /²(Yv~}½`»ßKŠd)Y¶Æ»À˜ùf˜÷½øBzþ[ãÙàË·©w¹½·«ˆÛËÁûAæ‡úOñ€Óã™÷õ©ê(„—ûyâ^ ?ϳ -¹ /‰ÕÿóØKE৪ÁlpäŸþWõ‘±é£ÞeÞéwƒÓ?þ¢ÞÃ@ ?ŠŽ~R¤§atôÚP§†ò õÊP?êhwb¨°¢d¢_'Aê‹(åCc²b òðhNä‘çDÞ9!r»±§‘!|úëÑgÅ·±¸†ËX©±eÜ9†mßÂsM’1ûõ¸¦¥ê÷ŸÓ „ðå~a=˜D»‚É ‘Sˆ‚%ìÆžb ]B¾Î9ܶc8Ú²LJ? #å‡N'ƒ_\ð¬a6¬» ࢲ«SåNò.šÍ«Ü)A,«…‹ÃJCÛ±}¥††J,Kc]ÈÐR"¿$R™8-‘Íç£kÂs8ásøtßν°.™ø™ këÚš›ÖŒ‡•Ãߎv¨á=[ì ì¸0=øtü“¤Á`ýO¡˜³˜È/\ˆ\mGp™ƒä #z´‡€ž=Q‡ÃÄxÀgÃmc¶€ÝÎ8_àpÌÄvì86Þ‚`׉ÄÍÄò²Oèe¶4FeÀOÔl_?o f‹"ç³²à ¥‰õ̘9°SÏ3Øvׯõi•Ø×¡œœÁËð{Ê;Ñ­ ÊyþÁºýÙå[Þù5ÐÊ>6VÈ/Ûêa]¶¯yhÚÁ>Þ¡¨½˜4Td¨=jêàìHÉòg`R¡¡HeŸ³¢XœéìLŠ ©+6}^šbvÔ3JíXA“^|^#$–Ï£mù¼N›ú ŶÌç9Œªóùøã(>òL¹LÀAoýÌîî6=1p OWr ›–vìTŸ®Ú»mÊÚý`¨ï E£€·õ¸%è„Ô·"dèp­C‘ £rìû6phiC/ø†qV(³&ï‹nQ’i£µ†™ù1d¼GÄe»±¶w´¨«æ¢‚Â?™¦µäcQ«CIž=\Pµ:¥… •™Ò‡Ì})d§øÌ\¯ äÈ0öcº®1§÷¥ GöLïi&‚q ©`4¢§LXÊl4Æa¥|ÕP"I#•5]/6kÐ!ȵö!ɶp’Û(dRXÀ5°•a)Œ\[šæhl•$…5RrMM‹÷q$íyiVa' €EúŠa’ ÷[ãfX¹ ž‘qÌüF${ÊfÌ|ô0m8hÔtn|ãD¾äöXk/we­7[:ˆµÃ í5‚ FYwŒÀ0ZrÈ‚4eãΡN×Ñ—}"ã)<Ö½å$Ñ87ÔÀÆI1¤=‘ÊÓˆØÏäö4ØžÙŒ­®ÖpõC‡$CÁ>bjÞ³9±¦l©>ÄãÅ‚ð9,KlE|³M¥K¦éLouÕ Ã´3T¡;ˆ;ýG5>ô{kÈ/¡^­ùÍq¨ 3úØ“+É QÛ0T 9*.-£PñŒBæjÊzO£¶ö"ÑaJV¦¡gqŽü=Ä•‡ã\{‘4fæÈ”Á\Ï­áÜÆƒ½+@!-nP˜ç¢›N©µÁ’´+s9FeÕji{‡I2¶Éwè} ƒ ¾ü!Z¶{+ ˜¹OŽ‘¼)ñE~ r2f#²¶®½$€;–’:ªŠH¯PŸikŒ¿+CC’g[Üã}U:lõÏ„åjýM[iÞüOP²~hNÌ#~™ZN©pb:7M?‚î3C]ŠoÎá¹ —×ñ œÄÙ¿ù‘¯ˆ<$>Ð_Á“â-|Š/wâCºY#LÊ:{–Êb7Ñ>¹Ä‡dËQ$´ñ³¾¹Âà÷'5ž©¹\ì[óršgWYno É8¾ƒ·iMú xÄzžQ#f%õJ _6ó 3çuµœÎ´)´NY-ž¸¬P@ø¶fK*NS½zâÒaNß4Äo unSõ𶢠ΰcùÂo&g°³È8‡£Á*z[ÇþÊj)Á²Rå­¡œ×uqØÁž¸˜áÒÚ:Ðøå‹+r÷oË\zQŠ]’•Â:¡0¶²"×9Æ:£ßOPêÁJ(Ôï‰';úŽ);x gŠTN%³Ó>ËENMÆã¥ð)´\•®˜¬ñkežõxSÊ^ôr-­‹Ä%Ë4à&:òã´ž•J-óT‰P2s {]³¼a#³ÒLIàœÀ½k^¬®‘á„eprÉ™^´Äˆ¬cT ††¯5­j2-FQÞ‘ÂÓÒ%뺅  Ëĥ˩ ØÀZ0õáJ×®§¯Îù|ªŠm¡/ã4>¡¶Î.¥Çò ‘¼LS?ÏÓ² ç*ÈÇu˜9ò<õjr映‹­lˆ“–…i6s¡­Ê®•€U±Aw\¹R±þ°Äƒ³¥( ,¸>ÒUK1#LY=Ȱ} 3iÙÊTêT)Nj2´ÁôäéöºÜ¢°–›L(¶oOXc~®ÿ.«ÖÍëÓÁ¿Ôßÿ—Nõendstream endobj 37 0 obj 2782 endobj 41 0 obj <> stream xœí\Y“Ç~ß_1ožqh𮣫ºåÐl#°` …){³öÀìÚ _ïª>*³º¿žšYvÐ"ô[SgÞY•Ùo'y&ä$÷ÿ:àà|çÞs;9YìÔÍ“çkw';owÊLùÿêœO캲˜TYe&»Ç;yVUen›YÅÄî滛X!2]NvÏw^M'³¹Ê¥û[O:0+¬ÒÓçúG€ž¨ë'M=ºidó0Pðߥ6+-ÿý°ù]TjzJà‚À7ž¸Gà'žxI [í¨¥öÛî;%Ò¡äéÎHù åÅêH‘Ÿ)o RØ^T©é»5æ]À%®`ë%Ø#Œty1»uº¤˜•è©ÃøúE (%5~Ыåú6@*@¿E´¥Ð™¬ø2ÔU¦ºÞ“ ¼†H½‚Xç} 'cpÞÀ:Bú£ýx‡~WcjpêI„ÿ“¶Ù:†ôοDÄ!’ gi1)EžYyC]Äs ±7.`‡kØN5=Né­#8Ã$5 Ÿ 3É컢6ûh"ÿ û€ðŸŠÈìôǰ³^{p–7¼=Øzg`}1wM`‡KØá1÷|–RÿM)H{6ÑJf¥ÒÎ…Þ=l=æ!þñ\Ø•cNú<9ïV›7Zÿ0öщ ¿Ъ†ÖhTû/ãQwjÿŸèXßC*0t½ƒˆ¹„&6˜)°?Œ9— Û‡ cvMºvÉSܲ™¬yTš¬”ªãQ6×_†Öö·Ö®Çb7qÈ•Ä<öœ`+F›,ɘv˜Î'pl²cÂì)<:|§#‹BŽôË”Œ2° £°IÙ®‡–D76lÔÑÎ:lÞ{.D¸õÒ#Öýò'åBvhvöX˜Ì #Ýn…ÔÖÍÚA©Â˜¼féDÃZáXJe¹©tM†®‘uuÞ-¢ŠŠÍtE?7JæÖŸ*úiáÿžÔíЕ³ÊUfKG(«zÎ2A [ÿŠ&xMŽxßС^ñt¦ýOU-±Rg¹UÖ5ô? ¿ïQ#Ûö%µ:2Yž‹Ú „´@{BðK‹5#d»‘¢ž9£M³ñ“™Û¯®ªÂ‹Ia³J‰räÔY§5³H%²¼,:]yØ?{³—@‚oêñUQÄhàÚ9¡Eåñ\µð tÈ/Äùþ÷"Ÿær‘§‰&U4ì‚C 3.º†“á£ïqŠÌ¥Ê´¬ìôÃÌé%¡ó£ì>lSÂõ \ï¿X_Á±É¾ƒ­+'|®a²>™>5¹weìõ“É(Él–bÀ^nK@¿(Š,ÑM§&&R3ÆÒÏ’)§(K1Ê Á)¿8e§ì­Çkž=”sÒ¬ Nëmq]äλE¼{7zCš¼fèî…÷þç÷tŸp:óó”E/uÁ ·¥óY#óHùÅ%ò7O`põš»ðèŽ)ùMÖ»ñÔ¦Œ/$Æîb¯Æ÷½Ø=𣠠Œ‚Zf†BÅä5CÆ)@íÕBžé2dT¶±6½¡Ý{®äD8Zjªbªt¯ŠÉÍìx¯©b’³yža¬ö÷bsUžû„u›U ²çªt\땞ÌL™KéåÈͬ*[89ê'4ø×iÝÁæ¹)|ÆdX€/¶ïoÃr%Õô×™[¬¨”HRF*±”‹se]‰äFoôvŽ Þ„™†÷#þ>lü†Ügø¬WýDO‚w o«Gï¿êPQJ&+L²ÇÄ@V™*z–oÓŸþÚ¸(F“Uzþ_¯;Œ;öt]uò­€‰0]®¼o ƒ·ä F§ƒWã-E·6@ce K]l²Wýu2YÄ„3ÍY+³–@–®”bo—Lao;`'¼bK(ió~ÎPvðe §+çªo$“øF¥q[[‚~´z¸_°\°…uØÅÏ:QóW¹Ø–\lX(ïü˲åòÀ†yßð«@|‘1¦×JØŠ‹@p#.f8ƒóbâžÀSàx=Y ¾€}Qq¼ôH¶­Ëxú]­Q³ÿUqÜÅ1¬†üô¦óÕVTêôäFê"yL†’sØW<&ŸK±Èýžpü`cÔ>!ðAŸ@· I“ “Š'/±G³;@)U3# ¢mÙ@äîÑ¥ (tˈ¶aZ…¨Ý=Z1aYÛ‚VLÑŒ(Õ}Xm•§ê?©}rHªÕmÙVÔ"õ£7nôYôÑŸäW`ˆ?YiÎ(–,¾ÇþüvHFÿ„ÀHQ×õkP€¥_ƒWå#ôVL%¹ìí’*n‡ÅÍSôÊÏJã58ûð1 ?©áò…±’^‡7´úM§œÏa¸B6YÛ+³a  -9âjø¥|r},¥¿ya.²‚¾ÙÃÊN(M¿Ö÷óÉóʺÿº9,yÃÐ]Q–,Âò$C*)~¬ Õ3Î ·5›*æBœ]ŽëÀ1öR ¶BÙ4ªÃ~CAÊa¡X¡åð mé??¤ö󵲯ï:¾¡º™W,…)€ÿÈZ—åŠô4 .™¡®AW£ÔŽw3O½\”#U4#å‚èM¬Ô!¬ú°yØ.u1²ãQXÂãO—$å ÏA¯Š‘Ñ>Y7Çæq†O„צà¯È*eØÍ×è‡%V«b\=¢ »Æ„~LÕ¢œ¡X\c“¬³dFø),TƒÃHD[³¨–`iJT>È ú£]ê½5ð'hTNg–µÍ¨‹ÿ4µ>i+þ<åBÝǃ:ÒÕòñ¾Òù‰ð­œYÇÌb¿·²“ZF3¦¡ðljúyA{Px°l×®qib¬&óÞ=˜f‚ŸZB>–\6í{R1Þ½aœ m4oDÂê!2‰O4_“ZgZ ÒEÁÕö å˜Ë¹¼2o­Ô¨Ûr›t)åþÖ»=_hVÉœš‡ˆmG€à;‡ < ]߃áç: ~`OHø[«7ü¬3 ÛîÈŠñX^ñcâ€ò5Åj·ø57ðAÞG»;?»ÿÚûU—endstream endobj 42 0 obj 3307 endobj 46 0 obj <> stream xœí\isܸý®_Áo‘R+šÁË›¤Ê±]¥”µ×–Ë›òn¥¤]‰¤‘uøÈ¯ÀÝ äx$¯Û.W âxÝè@ãm”ÄBF‰ùÛ³ó/Šèøz£~½ø¡%®Ž7Þn”qjþÔ8=;þº§?”YTÅUím$qU•IÑÔ*¢<Ó¿«,*„ˆUío¼ÙŒ¶¶ÓDêßjs[“qV¤jóÀRúµ(…ŠeÅK2òÐý`©}K[êÔR %ª´û؈œÃ—D.ˆ¼&ò”È"#"ˆüË‚z¥®á·½¿ohȤ†lwcï Í¾JÐX ûDΈülx+{DäK"¹É6|{vê/æ÷@‡±^-åüû¯’óß3"ˆ|[»‚\ajÍÇ<°Þ€g‘¥°ÄÌ!R R§àVÆX4ƒ5œ/‡}´P.¾p̨›ÑèDêä ‡‡ÍÎCXnx]â7ˆëùx\×¥ #Øe é,ËŒqñ6$Œ¬Þl"‚ã‡*;…Ý™n¸Þg–Çg+¯,u ‘<„ðEÔu,·óPeל"ï ¾W°¿·°^o–ÐPè‘¥Yi Pæi¥Œ— BUÚK¨â¢Ô¥Åš·"t· Y¶£Ry¹¬=Vß-ÕBíÒ¬ÈäÈö꧸Á4ÏrÖ }Ë:wÚ4^é’NÕF™Vgîm-+Ræq’æ:PÛ›ë¸l¿:­‡i¸¹ ®4må"S¼-Ö+­O¤ÒC· ÷‹}v@eáh÷ûø›ŽYŽ9n¬¦„ 7nÕC[ÖéVÛO •¦\©èØ»õë&•T"§f\¿nQa h˾\IΆÓާÞ¼*u[KMšÆ:ào…&&©Ú%—å"xYqU¦Æm•UÚyÊ¢íVK™ŠI‹\ ÍÓ cÅTQûK 2&0«§fýŸ3ѺùÞSÿ¾„¢;kU©Tì{&ôð2¢ÑeR8 Þl!]èÌÛ©)6uð$5â¥ÊæΦ…Dæü=k‹I© g¦÷nÞcrÚȚвV‰NØðüÆÝƳ¾×·½}ìôvyp+íWIè+Õ²áÃû¼°Í*» ¡„–°C‹`/¼Ñ*-}>Eå„¥2©´7Z:2Ú¢2ep}ž…áïà#;-¼ØW¶#Xö¶†Wê‚» ü ­ŽVs~üzj)zûÆR>‹Ôæo!fMàë×Ë §Àw°Ad àõ^9vòþ °{ù4F_ЄV.7¥¶1…\‘ñxw‰1žñê "71ãñú5îCpMööŒ}Ƥk(5EmšrÐ;± ÜÆ„Ò쵯:§Õæ?,õÌR]¹FjH(–*Åû|B ™¦`dAX<X¼…üLX¬èjgÍŠ'¨Se²mþd¾„dt­Öú‹eÔ¤YŠ<Û±ëí»?´”¿£Ò_ƒ_Á§ÀÛkXW0˜ñ´Â»3SB– +eÇJg\qAÿ¢Ü’l„EiÜK¶›¼¼þõ-ÄÖÍâp|¹œ’NÙóʹ²ÆHK¬5¾,Y“÷!k+j9|ÌüÖ0-Þ¯¥5Kâ2·§PäO"Ö–Xëßšïç‘úŽ´Ø:wïq<×ßÂʰÀ. kz¦‡À&ãòÓÿØX3ñ./ ‘z$G‹œ¯YŠxCñk :'8d ›/4E~¼dÆÀcKä˜ïÁ\ÏÏ”iré¦sûµ¥È¸îxµÜ7· G°óå÷ág+›$ؘc1‹`,À²€Ù³ÊÀÇ…†šÕv2•q™ªÎNâÐ×…[´ED–¶m »sþ!¶ñ†6·Ô ðetÂù7¶ŽýrïzàY‹ý(< ð$Îê æÿãcGŒeÁ@78Š5›K°íÇêúÞ—°åç6V$±D>¸§;"ô²dPƒwàÝ[¬Í°!Ä»¨Þ‘½ží!KBô«ÐÜœ°D}¿Ú_Å0E%RjS,rs˜_²üG:kÍŽOH%éÒø©m| ¾ŸIÙ&Œ€ìG–«Æ¾¿€gܧd…ôÒ@Øár˜)¹ß?è¾$ïhTê½iQË…d¬Óq?_¤ÍHÒýUU•™™’q•Šr`Ô¬6O)qRÚ¤¸yìM_zY]U–m®˜†À$(#ç\RVŸ f¥g`à-œ †SDƒ ZãÈ4V²*6?n “8ˆ'÷ù©:·†Y¯7&×B‚ “®Se¼Ü¤šþ£©ŠiÒí 2'‹Ähøªf°*‰Áëd© p”Ÿ}—KkX2.K¡»ÞÆô>—È0)üîùíyÞ®3ì{[~ƒf=sâð[­" ¿ÙL‘…gIM¾!žš‚ÀÐ4ù¾)­Þž%E3¦Wh{®¾ƒ}ÕšLi[NÕïX0ëŒMŽŸƒÂ/îë]à~æþŒ:ð+’JtŸyeÖñ0»$t)¸ª»-0@hp­Ïq.uö¦Ç»kŸ…2¦½Œý¥aóà@ˆöÈB¹³et¦,g «ÿå–6÷ZVOŸ³‡OíCœH#©ãÀ qp„£ì´»ü4Èò¨B˜b0ª°Eµ)¨4Y•ÅeV(÷B—†T9-"k-/Kï² ëˆÜÙ¢SYý›aúà³€…1ŠÔ‘ãÝ#õÀ‹®t»¨£Û¾Š)´Qw]L^0`®(«7xŽ ûQñ² é¬VD*ÕáÍG»T¢_šÍùýj¥ÅÚ½… …]@ÃN»-+èåµS´Æ6©òL 4Œ ±e ß–Óv¿š‹zt@êƒíì:Ƶw@9š k~à:ˆ Y\·­”ÒJ®dnö›ÑîñÀ)^¾³K+îÿ„Žržë`®ÈÖ6KEqøKÐwêÝk>Æ:"Ö,»70`¨]å±ÖŽ†ÚµóP_HÖ8æ:zɵ)só17¢¬;û4„Çoð°½|¦ï®°œ´> stream xœí\Ysܸ~ׯ˜·Õ¤<4q$6µ»^'vʉ´²6©”w+5ÖÈÒ$²4¶${_Ÿt“ü8œÑáõÛmh4úBø8o&i¢ô$ âèõÎÃ|rr¹S>žü¹&Þžì¼Ù)þ”$}ôzòÃ!uÔÙÄ'ÞM_í¤‰÷EšW\ÕÄeôŸMr¥[L_ï¼ØLg&Õô»ûœÈ$ËÝ}©ÃHM"õ(Rû‘ú´{©§‘z8ïEê RÿŒÔw‘:¯)íÄ B~AWï•7M¯@Ι|Éä“¢Û„ÉS&—L^2yÅä“o™üÀä8–Aˆ~G;…̾ƒO#3MO=üË9&'x¶sø‡7`²‰þñ;;De|—扲ù€õWÐK¨Ø+¨¬GLî3ùó}»‡0ý-Cº¿a¬Þ‡^Án‚6 ‰ÖS€WcòÃß (ú.ø‰ñ.É]FÉýpA¹ü®ÜïáRq ™5ƒÌhu¢a¨Íῃ‡fºqQòEm”Ó$WJ„¦Ð<ŽÔdªtbu¡v_OgÚ&inrÊÅÍëߦ³, j¤·3bˆ_¦$cFv„In P¦9‘´Á«¬’Ý÷atçKs†‹áñ”C‰ß‹ðá´(iQç/Ò¢è3G… ¯(3P^ü0Õ>ÑÊd½œ"'Ï£Ðacç¯9§ºwÓ,ä(rÖc˜Jä%æu»YØ­œ™ç¬Ê=yÖŒžR©qT-%št‚‡Á™\tÃ3=‡ :®S]ßèsÕjluÉ\ÅìBŒÛàme…E®”§^‘{7Ô3æOõ¥H ¬Ï4UëÎä±Áž‚ArùÆËæ­Fòy`#%ŒzÁê[16¤X´(Œ£“.sã2'É^]R›Mø—Îiñy0䬱ä¬.n‚9 žEGæu¤¸¹‚ö9}Dp¾„’6ŽÛV¤ðl!T7¼[>¹åÓÑgƒUÆpý!Lß³N—ÜÀ:±í9‹uÝOi™ÑZ;Ü7ÒÊU¸+Zò&Ü[Zìuž‚ù5”†£ˆx’7LdUYìTf¥Ñpà÷‹Ë8Ÿ¡b:öZW ¯‘ü¢e`ªý©àiÙëIHþm•è_½Öϫͪu³‹i–©4Y²þT µ{6âO@_ôöûHý Ph – ¿·7qÃ÷º"mÞè§U ‘ÆU¹µ£—ß´7z\ûˆå—óÄúÍ‘om [ {½ýƒ%O«vj8ÝpË6‡¾×/-À†òhínm ¨¨#lP†2ïpÎèh´®ÔP(‹V/» Síäû¿_¢Ì´êUX^ú¡Ê*”ûM Ü.{Ô<{É£LDî®?vò¨¦{«Ü±éÝm²H/ÖmB½:‡u»ñ‘Š_~ß ¡ðìúE|?Fý ÖŒ¨¼V(ùWÓ™O â¢Ê¤á|b¬«•m]®}­Ãªç*èP¶™$=´EP8Ñ&3išy‡3ê4ÉÂm.šc&²ãAÙ±(œi5‘L’I9ƒÂ·ygEAÙ²žM–ùp¥ßô;¯(g]mŽŠñilPK”jrïù•Ÿ‰§µ)Ê)V­ð¡la‹mLÚ+†/Ýì€ò¥Ü tBI°Ä „·¾ä1<û_À;¡Ñ[¿ó±»ªU<güÝë1L°a#ô~¼(zm{ÌÃ5¼ZǨ ¤]ylßQpÿn+ÎYp¨‰©™Ô ›Ø,Þðµ®!"³–5B°mï’w W ܾØknó0ª»Áû©Oò¶ÙBc8›˜dk:Hý8.ÔŸÂëœÿ½¹ƒM:÷K5«»Âä”誾ÖÝ×ÂyÄüîQ9™8KÉ«Â{µ±Vä] ­ë-¬ç†H1þJX‚£‹©½°e’g®|¸ýUšfop‰¶ÛEàÄV& æO¦Ó†\Ñ…£ ¡L³¬æD|XƒRÄ9FÂÉ0*¤"Œ‹Ú@0ØB ~ƒ·”­ k0•E1•ë­‹½ÛNC T«_±€áë†kÓhCÈ+‘°…I0.Ï£C:îÝ7j„z”0aç»Wó!©ÙPIÕ_8•prÙÐy]gâ§Mã3Bê×Ë2 ­¦¢%è&)“hÊh2I ×M(s5©E~®!ß‚¼VgÔ¡íEY„íEéêÂõWÜWø ö«N¡¿åb«ó¢ÚJŽ£­£ó76ç{wÆÔáÔ¥i¥·¤5’RœkØÜØn¨ E3˲DÑvº3³pN´ÉCx¦´ *ÂIêu¾±"„É—#Õø6!ßÜñ¸¥£ÏØ©Ž8 œ‰]Bÿ³=¼ƒû&,HÊê,~N¼:•m£ÜÌ_hckRõ`Y¬ÌÊeº.#-Î=¨]Ùà¢âsŽ‚¶#*~·ú¢ àß”^ć­ï/Ëo«tÞ^šµïTÚAw¼k³øÔ£ —ë±ï•–ŒÙ íI\Ô«`´E9×>ðIëk"£,'. ð¬¢-#>!ü—VB£i=„#-«ý­Ï(Žt›E!HC•Á=!JËò‡âæÔÝ=lé¬i{(õ׃2µd ¯ôøþš—wþU‡?m¡‹1HÓ6`|»‡!0â¢ï$³Q\£¸Äy¾…6à¡[½äNƒA>â* cH–°þQ |7‹/ãN¡è#Ì´œÅ5ä+8àßî@—¡#÷úl|þ&‹o¼»ÙÜ}oðÛA ³­NÀ °1pÀ³³É(Ðã†>w{„øèõí½ãÆ ìëeˆóÆ¿€q»¶)MKU®å€êc¦ÓÑŸxYÁn£©â)“™ÜƒmG-ˆ~qf$¤Ù÷½ºáŒ%´‚¡÷°¤ y0ëâ#®m·WËç¶ aÔäœÐ=¬mƒFÿò×´ÛûÚ—´=>Üù‰þþ ªendstream endobj 52 0 obj 3207 endobj 56 0 obj <> stream xœí\KsÜ6¾Ï¯˜[F[E€à+{ŠgK«8v”Iía³•FF#ëa'ùõHÝ ?HI©8ŽlÚdãÕýuÝ Î»i 9Í_K,דýã|z~7©OÿÕ·ç“w“"JÌŸê§—ëéÁ\7”é´ŒÊl:?›ÄQYq^÷*¦Yªÿ_¦Ó\ˆHÓùzòßÙtg/‰¥þ¿š½Õd”扚}塀£Þ8Šø~rÔ!à{ÕP23£)â(—|@áXõC©D9S¿e2{ òšÈ%‘DN‰¼#òžÈ ‘7°ÙŽÆ‰Ü'ò ä]y É xjäô¿ù¿'ZSRkê›Éüž®HÞÇy«Ùwžä³8„ò$û8%, Üðú¦PòÁX³K¨„û‚}€£ÝÃÑna¿ï`gŒŒéNmU)ì[G¼¨n¬êVp46ß"¯@³á¶ö°º×/ «°ìa G;hU¤¯<…ˆB¨H–5…`w´rñv °‡K8<0ÓÂŽ6ØlH#?tü\ï‘à/¦|,¹†¢gOÏÛ2l‰ŽÎ_ß8Š6‹ÿ| g Àƒs`»0rðž]<ëÈùnèôûõ'õ§k ïÓ§—éã 2¸§’ÛØ¶í}ÿXéHK«Q¿ùÌè4•V©¯v’(ÎJ-s½†4Šc¡÷Fnv´…ÔöÆNõÃ$Ͳ؄!öýÂQz±zŒ$-õZí3=Ñ\?ÊS3}¥&¥ÒŒæu"ãÜxˆªM‘ðÞ‰U#>çoÔ´ýê¬ê*Êξâb¸r½nhÔ;e”€uŸ7¤Vš•è¼`Sm÷Û¿:êÆQ«/[9Ö[@m:ÏêŽÈšGµÀÆÈÉ"¿·DþDä!ä}Å1Ú¬>åV‰£DöâPä2`K[€f­½ÒvK±—Nh׎Úšyp”…B££¦ŸkІ@CªY€·÷àíÒQ˜)ÿ<ÙvÀhjœõx?¦¹ˆR†§ú4ôŸÃ­í‘d ÊÆÉ+¸ ßÒ0œ{Ü…uo ±ê°:|yÈ%\Ÿ·ôŒ†¾êq\ /Ð E ŸvÇAöqž˜¯€N) ²ï¨7€oäŽÝòÄ¿¼Àyÿ¸œ é¥ð‹¤•ÂWYQ1˜¾ÜÙ‹£LJYÈYdèD%ÕLõ<×üeTè^D%⬌•;Ô..ÊâRŠê¨-Jéxåº!«c`Õ[¡ Ç`ÎŽÔïŠqìš1Ò¢¨sŽeîræ¾N´¼„îEúè¨qP&Biéƒfš ¿ >È!MÓ¿7 ež”qá5¼­èD㬲Udz`<ËF`E‰ÆŒ¥ÖÜ3Ö–VIÔ; ƒ*`@ÒSªQi¢ÃF£gÝhþ?X1½gúPžIw¬Åbƹb1ˆ=÷Ó³{ *™ % B©s–ü\éIO^q†¦½œPÔÁ±°Pê`¤k9ôRT”)mœŒ«Þ e 36Æ NâtPÕ «kã©´*‹rÍîÛaaÅËFg¼KŽÅ±L¼~¯ý9uj’Ú›n ºd¢ùË\£i~ª„ÕQR:¥NGqjräVð”äžþ“–0%)Yyš<<õ”ÕÅf\KvÞ^ÿŽõ.áÒ î°‚­AÒ®FP=˜¬clåÊfƤ¸‚@¨»J²4kx+rÓ6¡6ƒ§ÆkP³ga³×xƒfH‘e,a1Ÿäú\Ñu)”þ`ËeO–4±¶Œ  ÿÚƒ±…8-Rã’Ò¬?d/õ±'µÓ.t(Ax[%ëÉ=Þ¾ëíÁ·äèíøÞJ™Ü[8¢r9¦!v¹ºc7 ²mùâ/Û2‚©8¦BÏ,ôPÉBðÝ…µ:…b`kß… >Òg}¼Otq)L‘Æ`=+‹.…¯œýÖµÚ¥÷Ö,òB³ºi0V÷Þ"FF"+,b6Þâ•9UÑ„Ìõ¯Ì+Ÿ¤²$*RUù$U7+œ{8pÛÏë$±›£\ƒðä´Ðø9JtQ^³jQˆ¤ûA9*Ê`Ý‚gKÐ!Š´îàdÙZƒq,¾uƒC)|×w°Y°ÆHJö„ÄG ”=öDc+2”j!ÝØ*b uI)‹Žx[Ô‚F#ü¼ß:.•ŸÐ´Y*Va ÿ6V-J÷UŒ–p`%¿Í!°Ì¾<Ê2köTŠËVÁ›EÚvÓ—ÄÃðâWµ÷)µåg©õ>Ãëtä;šz|Gî´h¯æú%‹†äƒ:åŽq6éUO_Òs×CžÑ È¿¥@= 0}é­Z2~ÿêð‹ñÿ Œ?dñEèîÒŠR©-Á‰°ŒCƒfŒÉc§,¯ç6Xä_s†öeÉs¸Úuîžñû\Ý·}õˉ3K,ÙLIH±f4m–‡¼€)/“¥â½x9HÖ¡K¹âÜ›Iúñª—ùsãž·kHáT&®aàbDå·^’·Îa%Q™ {¢c­:*©²žLøl‘LlW(_ ô?$ÈÕ”ÆëOÖ‰ïDÿ£Â@½f* |ÄIŒªî¥IÔ–ŠZÚMj«,ÍM½ÒÈ/–³/Üû#zX·/â<ï­†Ê2’"Iý<++U¸¢›<eÛX:Z¶·B»Zf6ÉrÜh¸Z†Ìlu^­S"È# )Jø¶ZUÒ—’gXwÍ\ª¶ø™‘ŸP2íÃ:(€ÏPeƒEP¢S²$†ù ×$ª\\Ú(3}è0Pôx?£]o. XzÞÅb^ô–ï‰7›úo<¬¶-' ¶H¦Çv• ‹ŠÌ+\:^át^ZK”yO…„U;Au¨d¾gØ^Þ⬜Ж2j«>ˆÕ»ŠÄ® ×;™ÿ¨Ç55»jn:}%}äA—­2íÚd –$G3.ô±)>¾žC9ú2c¸ó­o…¾±¥oøQwð—èKB 1ˆƒ(…Uw*ϳF¡*9!(Ö&X„mo»mh±E{nËáéÔ¡›)>-b„³Íõ’­ ,6xczÈ~it]¬õzI¤{¶^¯OF{¶§MQ†ýĘ›:»3ó–öiÿÂÛ¾Ö³D{ÒYû$Ôº……t‚Îìý‘;³]ûæúZ“q–ç…õGC/867*Çfõ±š»‘«NÄ ÆÁͲ+›ßM´ã'ë;ÛÇü Æ™oŠ+'ðìè#¬ M0ÞÎ&ç6‰Á»Ý5Hά;)™!‰* Qòî·á=áÚï’çfpJø©uÚ½éóã¯GÈ‚RgSøM&NY¹÷æûTXÕ½šO¾Ó…5Ðendstream endobj 57 0 obj 2872 endobj 61 0 obj <> stream xœí\Ysܸ~ׯ˜·Ì¤$šIåamogíDkϦR•ÍÃht&’%ëòæß tüH’lg½»ÞrµIâêãëF70i"ä"5,±½ØyöV-NnvšÇ‹·êˆë“;U’™ÿšœÞ^,ž¯uCY,ê¤.ëã4©ë*Um¯bQúßu±PB$yµX_ìüs¹Xíe©ÔÿΗkM&…Ê8õÊQoõmGɲiÝ>dý0Rº÷¹×HT"OdÍ?½m?u¶<³¤\^ÐÓ#"DyMä%‘°ûàŽH6‡«Ø¬³øÁÎ ÷p g¶,É–ïa³#8Ú5\ÅèAêÑþµþËŽV ©UãõÎú·žr"¼í)B¾üÞ“n™ªDäj@'×™ •TÞ§Ÿ|©c¯a¿`gì$rÆÂ|”ƒÄ·¿:êùÏ–ƒGp4¦ýDžƒfÓ5ï ƒo~¶|Ãø€ïp¢Ž_^Æðv_°lÀô²å3"?žÅ8»€Ì¸„£MV¢?ö“/÷õ'T¢SÈ!¦øÇ4ç=¸(¬DÈt²åO!‚u¿p³ýsoÝjH‘&J>©ÉÌqž["1ë®áÓ-ìèÎ ã“^ÛIÈñgoë…^ÄUeAÄUŠ2)»ˆKÇDiRJ)+¹L ‘Ë&B‚ϳÕ^TºÑÈ ¬“,/`ER¦µÆ4ŒÖçJ™y6¤4ªé­Ê+÷4Jäú=b_ìš1ŠªjµÞ}¢5EèçiZiìÕK¨ó¼^¾ÓÓDʬZ¾nÞWY•±÷¼ƒWŒ¦¹Ý¶=im'!”(Ä4YZ#ѬTY­‡Þ°‰nÍÐ šJ-–—ÔÿÚ JÔ~Ów^* ˆ?®ºòŠä)D,‚.S‘¥‘§n´þ·BZØy½Ò%¤æåÆQ·šÊвLÞÙ‡zÁE’¦¢f÷^óLw§„fåûÉí›FYQã1Û‡™LÉ1BC]V/ÿ»’u"EV˜‡ª‘P7‡#jza IUæõ¢E¢Ì,Š\+šûö=$YgÛv× ë‘==íÖUe¼­¿­‚ZB¹^ƒ0À-íŽyKhŒ)\‚ÔŒÀK¸œ¯òŽÕÞÜëî}’Y–T…Ðú³>Ôæfº"A…ëñÖÎøæØÀ¾=¦¡ÎÜT˜R°YcFï¯2Í‹´&´2–mÿùšÆûÆ=d Ý"0ÃÙtØt7¾t“\5ïaG;…ú… «Ö Ýk榎Gcß )µG•ü}«*E.¹(m¯ªÒºÛ¨ŠV`•[M¹Ð}©”F!eìå…FžZ,ö:j5ÌŒ•Õù üèÉÆ¼·–]–¬Q5©¿~QÊ8¯ößÑÂOe£Ùèa©IO›‡áú&뾿¡WyYu0LÁ3izÚÀžØW±18"10йCèÕš páEÇ×R{´²®˜ù{ ÞÂO‘™Sª4ÂvuQpéõÔ×ôÁÃË 4mö”‘Q0³^/I2CÆ:Ý}|:eŒ&î1>¢™ —ëŠÐøÜ2,Tퟅ4kÈMì’åœKž¦9ð9êÀG7 ô9o49—ª°ŽU‡e»ÍXYY”Ýü’u5dñ>f¥:¦¬<ÌÚwÖ=Šõ0 t@u逊ÀvlŸˆeóA6î[n†®Y3$fáaLÕ/áès‰1IC†ZÌ|±žkµ°U%uꢦžÜZW:ßsNº®X¯Wu¢*³Ù…†w· ,÷0ƒ p—D•ÖÆxËÜø-†Çû(;„}Ýs`µSŠ´F£:öú*”A cý78Áä}LÁ>`‹edÒ}—ÝmÏ*!H» 3»/Ö° ï” ´2Ï4‘¯¾íiªùòõÔ {;dZ< Щži™÷0-ß=¢h(kÔˆª¼è…“Q#ÊR‰Ø¤*>0Bm÷ Ò‘}ƒì)žÝ}eÊÂȘ–fª “1Ó3 ñ‚aöïZÖÿ“7Ûk3¹Vƒ½òH”Ë| kž§ë=#j|‡Ê”jî¨× ÅKÐߥ£®y¦q$mý˜-À\¨dðîÁ+rsöòA¢Éòè7¿ñs>/[{QJ°­Ò=ÜTE­ÁCiÛƒ2pÜÅp„ÆÞü#B)Ö”aHÒqeþÄy_‘¨ìzT¼[ÃR×C)äãÝd ¯,ðȤªjJûØþZ±Nyñº[×AÈúÖÛ uÏÙ¢cHfsŒí2»Na_sr'vûÌ7„_.ók7[ rq»š”zß„‚ç9[Ik¡½º·m*µBÕÒÛ68Y›ÿXvŠåЦ#ˆ>§za£ÁX¸Ñ”ÝHbǺ])}ÌÒUÆ÷×-³Tþ§ðOík¿‹b†¯Ù×î£C༾°¿5¸¢ inVø×ÁÊc\.Ú Źιz.åžÒåFmaäè÷ôYýì+"Y¢qV(hF,€E3&ñ`» ²pÞƒP0D}\ò\}?ÓêYë‡ÇôÌ$Œ¶]¢Sjбú2œ¦Ì‹$/jÏßÞ @ñÒzÀ’mpZ/O\Öv¬=gÉÕÐü”vcïxo¿Y!+g¤v*VR%«0oß¿ãæLÔ0¡ô†Ý¿›b$ÌzE‡=‰˜‡0“ÅfòÄ#oY"é‘«Ì…kx*½ ‚©²Ì))yv¤dÉRËA¢[D˜K ££ò1áÑ> Þ†ÔßßAðô7GýÁQtTIFŠd(²ÐÝ–:ò-tÄ>`–b&‚²ˆ'vy4 Ê­“²ºa„€u†í‡™*²èkÝhZéÃnŠ"‚bÔfý5_5µsMe9P‚KFãyO”½*´/¿°‚7Vz²mØkÖÜî±ÚU–ë.ÂÍ–¼<ÇØg²_ŒšÉ¦ƒÍÁ& K6ÚœÊ>Ö;a³n@t[ m7'mûžî NOF ž7ÈWI!\ÌHN$M—{ú{®ãŽKó<ޯïX…—¯àÜ`a ŸêbP6_ëšÙêl@í¨ºçˆ'Ö‹s»b3ª'=¼mUa Ì)Ú˜™‡5› ä üÊh ®ÉÊ6 +XÙÆ`Îh“8!ùäŠ=ÑVò¡Bõ\dmah%wÀüY|¿ñ Ni28 _wÀ!Q8lWÙ}Ž?eEO›¥:†î‡Ï v*3}xpT9hx—ÚœœÒ¼iÄCGy—ìUˆ3÷þ½£®uç¨[ÓouJ}ozÌœ=Û‚@‹8Y¶Ö'½æ÷n*¸ke½û,xIþàñÂc„½ís$ýq”¡Hj›»y[Ô‚F#m¹3Æ»õ®*¸ô1ÆZ,È àýÐm¤-˜] ¹Í!²ÌyWB½kBFW2%5LÔìøð'áÈn¨£{v`w–ÁÓÒ€ íš”…Þ \êiw×7™þ-(fãì&Ð ‘§Äv‘ëòÝYÆ8ŠIø‚†'¬}=ÉxÓgN–3×pâ(ô{@Ð$¶¾ßÁB=Ãûšó°çyÌäP nâÈð=ñ_Á!¢7K#w;˜ºç~AÌ|ÃLïÊkf”xp¿±s¿(ý»F°kùXÓ—_£é£?±Á;M†Â$[÷6áW[ÿØzä'ªˆnЖEžýÍ*#©-i©Ft7Å•9–ޱ¼Ø0ÈR’±C2ŠZQÚ®»_œÍ7Ë}äý’3ó›ª*V;€ÅJ\í§4pàd…ìfÎÝo þA“^ggµÑ…9ª™ËJ LíìBE ïô3­¸ §é•&;îJ‹Ÿ;nÏ€T‰¤ë%(9޳ܲ\ ½|6|TnÎ=³'ºâ’Öeá2Ò­!wGK oþn¸Ð)Tôãet$å;îÁ硼|Ï*}ÿ‰Od3ÌèýhG€ Þíñ ?;j)Ö&Zb Ï•…‡ËØ¢= MtÀUš W9Ư¦£ÅFÏ=`õ†µóanôï²w>Sf‰¢ßýâQÄô­' eÎI‹yì`÷°pI˜ˆ1ÿÅ.¼eÒ÷#ö({ÿóõÌ{³Ã¹o4™–JU¹Ð¸0t" í³ž79uÜÿŒ]ò„7;çýŒ‹u}LÕ‚#W¡úÓdŸâ“ÍÑêÔ¢®ƒ‹ `¸yÛEÌæ‡³$8eÌJ¿¬º Úö¡ß®w¾×þ)`œAendstream endobj 62 0 obj 3376 endobj 66 0 obj <> stream xœí\[w·~ׯà[©k³‹Å»iOÏqb·I«6ŽÃöÅÉ%R²’’u±›þú{Á v¿%–+QªŽmùaâ2˜û ~˜ÄQ"&±ýk€ÓõÁWoõäüæ lž¼ýK \Ÿ|8È£Ôþ+8|ºž|33E6)¢BMfgqTy¬«Y“‰ÊÌç"›è$‰d>™­ÞM'‡Gi,Ìg9Ý0Êt*§s­´¬!¡ìë(‘šgàuá ÿŸé´êéô’À5—n¼%ð¦…Yâ—Ù_Ìæ…Ùüñùü;ûIÚ³ß{´øÖaûw½òðNDGZôP q£”?*Od$б»ÃÝ.œx ‰tNà ’Gº%˜¬CÅá~rŸ9èå„ËÝ(Ó(d¢£\Ñja:‡­wpƒI"$Zs;üÆAÿtПwP³­öLZ® n!N`ß3Ž‘¢Åýà Ÿv EêF‰Âmà¦Öp×hS….!-X¦Þï¡À^ÃV6, n}Â=Šû ÃzùDZý=0‘¯=Ú“p† ÷½ 1ìþrÌú2†­ ˆW;߯A†}„,_Á%~žÂ尰ž€jï!–¬ï¯°Ão\O¬¨H)£<ËM„1[˜€âîmÞy«FÖŽšŽ*‹¹ðÃ’‘ 6Ý#eb Ð÷&»b2ÍÀ5Äa[±7[Á…fŸ ø`9)(AåÁÃNá —°“ÅŸ+2Qo;&JNn¬þý^<íHKr —¸ ­†Ÿp2ÖÅ@ƒ£b±àjb¡Ï8…aÆ“Å#›4<Þzé RƉ·þO™ Æ2û*œhvÈÝ›<~ç 7m‡&û¢í#¥ØÓbYg>5}¬ E§ûέ°c¾…›~ò4‹Èò%Í/Ñ8Œ¼¯ –è§È¼Ø6˜bb«Èhr釫[HʾˆŠ®C3ÜBÌ3L®×é>(ž}Ð;€{vŸ1?î÷¶œÄŒpÔûÌ™ñ4ñïWo‹I"¼C§‰vÛßaÆTŸšÿµ8jxpTG" Lr—º~ß6E-ݻƅÓÝ…kãZÚ²á¶ñÒ ¡¹±víBµ©‘‚´FTëFI*N†¸<·ÛRr=ùG«cÿ¦Áè1!>‡ØÂk[Ã6Í©çØØj ˆÙ'Ó׼ªÈ™àÝÖJ,"¥œ§yÇa¶ q(ïs¸]ÌO¶Â%ÔTÆDLÑZŽú\7Cæ ×îîF‡ëoGè¼Ê/ØÁ$U&ãI²S–÷/ |u¨âØn¯“©X!J‚ØÒ£^5 îŽ4•ÄÊ…lZfIÙ¨NT×VxÜ‘æi$ŠÂX3Qí%EJt÷š(ŬÙ6Q´ödîæ¤Íe†/Æf°&͊˶¢ìå`…³«:jUQ¢5¨ã 8¾¯d¥¬ƒ;õí;ÏëÎ%\™´^q66߃«Z©îÉǽúfs°¬)·ä±Ö=‚´¥Ì¬{Êì+É:å‰`}ªs¶ ƒn…bžÏç‡s$·Å0þ ¿»Ûpk7 »<æ»æ×¾ëí 8 €â;øøâ=~ÈÄ|">=…“Ý@pDÜ@L ÷~ô¢yúðÏ G>ý¿ïƒÇ?ä0òùÄöì†n/l“s9}ç á¾vEç)hûÅcO78ßAC“%=IÉ¿uˆbþ«èû‰âÇíøy¼g„Á÷7ð… F‚K¸38«Á!263{qd:¶ó«‘ÿ/üÚÎ/ü3 øç„PJ²CÛ¹D¦~Ÿvèsæ/î÷^q—'¼÷ûmŽ‘G5#ߌâ ëy¾îÓÒ]áè“¿#îåÞ¾+ç³æÞ½÷˜Òð¯œlOBwø•™Ý@jb‡‡¯ôù½Qð·Úô‰úî7_Ï~4ÿÀð"endstream endobj 67 0 obj 3040 endobj 71 0 obj <> stream xœí[Ks7¾Ï¯èÛΤ¢6_M6“ÓÚ‘7J9[™ä°ÉÖÖH#ÉŠ%#'ö¯_°ØQ÷è‘TŲ.6’ €èËLäRe"üµÄáùäÙ¾ËN®'Us¶ÿ¯†¸:™\NÊ\‡U¥ϳçs訊ÌçÞfóã‰È½/…«¥ÊÌðÛ™“27e6?Ÿü2Íf;Z(øm¦/€Ì §Ít©½H}©Ý†R¶ê]79„Tñ»I:ÉRš\yÊzX3H¯§o‘\ yŶ’nk$Øn’,Ê•K„]³ä)’ï‘/AЬ´m}®¾‚˜»Å82¨Èü›y¥)CÇÈò²a©ràÐ…Eà®F)ýô22ÜDŠ {[Á§IŸKgýôã v×2h$%Ì!Üldn…W2„Ô8ƒIèCV‚\餾Û0á_§á‡wÚ‹²]·àBšq@ñÓ_g ³)ÙÜfÃÕ¬0&×*:Í«DAi!¬[E2—£H‘,“„n¾RwiS>ñ ÉJ¿ŒÔ æ%GH’t(D QV7V¥Ñ‘CŠBÓÚÑ­ƒ A#†v :o'õ {€ÊMnƒ‚Îh¿4E¥s&Ý»m‰•´K$Ç,²f³HØ´U ”°ÀD ¼t0.•$ ãÉߪݤ´Ê¥-a÷Ì—pšoPÀ5Na]ËÒBÒå,ÙÙt²ÓFˆÕ¤é²ïg¨º™_á¬ê“±Ó.f§Éó—Íö“»bøÙÔ|Ö5¯"õ2R/>)ŒØßFê?l âI>?ä£Ë1½e×Kbi%-Æ r‚¼§1úô£^@ÄY×M¹)MA¹¨T3Ü%*ᵯÞç½kÏFp¢ï§´i!J >—tÈ:“s–äýqGÐņ€—Á}&¼ÙBC@Yì»nÂðŽu­R&ø÷Ë’È=˜\ªëœ¢ÎõÄlÅè€ÍA4…Š é$GÓ¯i®ÜI¬´-ì†Ä*ÕAµHP^Õ;Khò¦wÈZï»[)ÛfH˜lñù_šl9@µåôCÈέ."…&Séå§d.±L4G‡4°ÃÍŒK,ùù%HÔ>‘pÁ*sÅš’H r¤Â¨¬/S$(v#ó]±ó©ô‚5CÒ-ñh%nÅu½gÀqHçÚ=s:ïË*&²Ú=AD%FWÎA7WåáF»ÜMòð_ȳ™—´–*/žÈINgÖÝïØé"R×8P²“8äŽÌ$¹u1w©Þu ¯¡}³-oY ÿu…WëêZzÈ—NVÛ¸F·OXÀ~€}¸Uæ…/ jrAàôƒ(â ÑùM‚=¦ž Â–Ž <)\ÒK¥ p8enÁɤCSà¿E…£˜}Ã"Ë~m ’ÂÔ*ö¤6@U¹W¯ÓiýÕå]® VîlàŽfßLœpŸñ<è€ìgr¶I¸cH  É\>D¸'O†"nõÞ%‡×ñ¥É2 WúGÎÚEW/µ@™®4ŽrŽZúá1‰5Àas+cø&ÊvÀ yðroàåÖ"@=Ý'¸å~pËó8ÛŸ"õ2™·T F§¯™6íÕ¿÷ ¾Y¸b W –÷˜%G=_èè/Ýx3|ñ8zá¯pÛTîxÿÀÞ®ÏØ!¶yŽ0øêr,òâÁŒ †Œ@Žâ »ÞJ¼ºÝ4C»÷›†A•¹Ÿ(c,Œ$þ*.Ùn‡Cr·ñÙhÍó€JkN ò:UãÈý»Í“¥«:Þw~dÕqÄvã=rò/í;¾øú9Rw‹D¼²þ'¹b»ñrYaDÂïì|72³FÝÿS¡ecüzˆB bòÿTrñ|ÉEß»ä2pžj.þáj.£jä¦LþÛ°ÌHWRèÁ¤â¢tùCt`àqÁàcÀ —çA UõBê†g=h²úþG§ÒpXÏ‚} ,CVÀ¾¿9Eij†'œ ŠNpØ&QxËV¹(}‹>0,$Î*Z˜ø¨£„UW‰]S|Iµç;ø8…ð覙u¾ÄÓ¢f:znCÍðP<$P߯pqøR%Lx[˜é.'yÚÊÌö´-Ĥ/þ'õ«£¦ÐT†gÏÍV!(Ñ×ÈÙÚÕ;(ŒŠеç¦b%û`ÅŽú%›”ŒÈðI©á”;9ý'¿Áã ŠÚ–}⺺^è3àï®ÙòÙe7³®S`<H*Ô«?ìÎ'oàïÿ™Uçendstream endobj 72 0 obj 2341 endobj 76 0 obj <> stream xœÝ\ësÇÿ®¿â¾å.…–Ýyí.I¥ 0ÄJd#ƒ¨¤Jv¥Ž“¤“tÁä¯ÏÌ>¦{v~{³Œ‹ØþÐÞ›GO¿{¦[ïgyVˆYîþíÕåÎ×åìôv§ù<{ù׸9Ýy¿SeÒýÓ|àðêröäÐN¬g…Èj3;|»“gu]åUû{13Úþ­ge^Í/wŽæb±›gFQ‰yæ`YJ¨¹ù^.v묲KóÙbW˜:“ÊÌŸ/v‹LTª,çlð}¾î@Ñ@v YÉùÇEQfµ,„[ËÏúÎȳª2r~Î>ßvUªš¿c0Ÿú”¦^ù ׺sCëRÖy5¿i–Z溛gÚXD:ôsQ{XHSÎOØŽ|*ßýç9[ÿ¹ßÕ'3¹åÂüçE7BUîÌ¿þÍr«(fuÀ­R ¸ej‘•Æ1ÌN:ü÷NQdjv¸¿søÇ#·ÊŒR¦²øª¬,díŸ[H(Gw‚v…Ì”Cº¡{¡ éз‹•…ªçÇ~ä¹_ÇÒ¼Ìò¼0Ƒܲ-/¥c¼=²6&—ÝFRäýN xGÎNn+©-ß×4ÔíÀ¤ñlЪÛÛҘθ¡ßiéÙª-B–²åüŒ¦Èè°¤¯oÞÑu_ô’Œaxi¹£ì­^-V{K«5Dó:ÄÕÏb4Xs\` ;µÎ•¤Ãc«è ƒŽ«Â.ËPü°¨Ý®²òmO`É]uìpBÁæ·¸˜B+>ÿâ²$~0j^øe¿Žá¹,^ÚI£ø,¼‡ü&\®‰ëI„™ÀÍš¤ý¯SéÝžôn-ý[½}øRh¯ëÎPõJlÑ‘¹°Z­æ¯,˜ik!æÏ ÷—ºôÐIŠ›ÜÉR¹=“ªˆ-ôßé+±ì‡åD—ÄQ4 ¾}†À2%ÅÃb˜6QnúÃX2",K1BЧNBYÍ«>ùˆKxÄ(øMü(){üOp ©½„_7Ø1@Ÿ¢¤×QŽú%б{ A&c7}(@”?Pžüã=ð/ü,“ÊñÂ)ÌJWUl…8k@,¹lì1\l éͤüÍdz#q¡$m›Ü«ù‘‡r=òöÐ/)V¥ä‹áW¨ØpؤŸ«ÁbŒüSø"†J@Yì6uNq…TAx¨áñd*£Øo ×t ó0FÛϲ5¨{¤ÿe‚Bÿ_Ä »†Æêâ‹e}ªÚ‹ÈÍÒO ¶OE‚Ÿ„}n ðí'vïábÈD°uSÖbŽnéž|³$ü .z“BHÄñ‡o–‚K¸Î&¿ŒPˆO¦r/" ãóIþI“@Ðö€ úz ‰}×ÅO*l Æ£sˆ¯ŸvÔäëQŸï>óÔrþ®p ÇbÇÅÄã#Qóâ §ú°þ®|ù ±Å×åáôzú#.1†ž (æû` Ù82ú8ḂԙÁø-uÀ‡¾€c“~%¥ÓÞG½J<[HwwmOÌnÝ¢KÍÁEólxoè~G¯£tåÊîAaÁÑé–Ë{Q=¶Â‚&v_\É‚r"öVѵXö·ë=êÑL^Ù ö”;Æ/b7ºŠ¾E&æKB¿…Z°ê:%§ÌØcŸ]–`Zd_"‘‡×ÀÄŽ;@½%øõüºòЙ‡H ŽCFO´D8ÊÁ¡ ›ŒÏ§›”1š'´éF4å’`ž "Cð"ˆ#¡5Dû>éãéãðàÛÍuky~ÿâSöl¹„/LKþLä÷7㬆=õ2 Î@ü~†+Û-šòSènÀSoó€^Õâ§^WîØ=õJ=ÿJ”ŸJ£ÍHý+>B仄ɋ‘±†U,Â2œªðkà1÷³ÈO2 °O¥'½ Tk€}ô~¬õ1ÆÊµ.këªlHRæu y{>„³f)AÁ/µø­W\àó0­{¥×»2p1‹žÓ§4%+ò)=ì98hJé•)[j6ùUY°ÂÿSñŠýe·ZU7%;™®µ\²Rÿ7~‰ 7MW• {šx>«ŒÖóÇ‹ÂX„TÃ;k®ŠÒ¸·Û5"È–ÞþóÙM£dûèÛ†:nã^ˆ¦{aß>g_×4C©þ¦Qn('I×ÉÑyßï$šsŒ¡çš%ìòBçµc¥¥—Îí2~÷WŒ½1“ÚF¶RPRÃRÊä|"…kÀhvru#ýB”M çå'•â ¤†œä)Êi(}ee2ðwQY}[‘LàcÉ-™øvÜpÁêí~#5(ì+3Cÿº?,ÅÔìŒamçÖ¹a‡4ž$Ì.¶ªNûÞÞ»}²Ú¦÷aݺ9mm~í«E¯ï‘9p«<ö+“OÙ§áN4AJx[O©kx:\d7Vˆã± \÷°þ§žãB± –¦oˆÚåÕ›‘¥z¬™Ü »Û–‡” Ž‚„NØŒ÷à,ÍQ]à? íT$üx ¥›$¶Š§›žŸ°~®j9bæ la³üWþc¯#5ÓV\•EpQËÀš°x‰ÑrÕVt‡ÚŠeA@ŹEI¨sxW2°BVÉÙ †gPÔ3ä'L3C¬œrÃå ´ ‘ä\¹=ˆ4… ‡MOëÏ„´‰•©@]-“¬Hùšœ­ Á!‘'ߘJ“ná:×1ö Œ³'¶ÉcLêNóx„, 5›ñ3ÿóYJâg”ˆ±¢Ê%DñÜ•ËN^ŠŒÕaoP%ph~”±ŸkV’xÄâœZIÿ»@±Ì–‹Ó0þaÉûX…»ßž ¼ JD©·õõ Ú4±l¹ÎÅî—%ô87?†ûÀø ×¢Ç7Ê=Í… )ƒÚöLŸ¸;l;EM–k/XË!¸¯fê®éû³4sL›v*Wí;>Ï dD!eø#ÆاmÕÊ[ê÷GU®mïC±dŸÆÖò¾}ß3­o)VBö‚rARˆ‚ri”TÓkÇ _pq»#&u¦ªû…Pd¹ ˜¦müæsàþ£ÏÌMM–¹ÖüãÄh²5}МÁX˃½ÀF7JZ·í$"7“Zû]Öc­=×p+Ÿ—aîv3/*®?#½4­T™Òþ~€™œ¨_¨Özôj0ÙžhCºæìÅí’ #‚ã(¶ëm9:¥!Ÿ#÷3h>:‡ëŠBFñv² zé×pÖóäSµÈk7A0›Æî··ýAf{ÕÉÌ$koCÍRøªÌ*!›äî…Úp Ø—8³ÇÝâM7”ÞJhëö%¥.¿)žps°Ô2eH6_ŽÅk÷±"°Ó•rKï=¨Pέ×5ÿ¢Êq*û¶hJ“Uò¨í-š=8¡')¶j+¤ ð½b¬#ò>Áî'ñÛŸ óÐoœl ÇyV¸gªPƦ42ltžRÍOaÙ‰ÒÀŒW`ÜŸ=t½¥b‚?Âÿe¬Šâ‹ý \ñƒëÔðË7®ŽÅåm§p·éÏþ#ËøUùý‘ŠEžƒü„/a¹Ö„èQeøí9ÔþþB‹Ä×ö0¿eö‡Å,¿Çúˆ£~<–ŸŠŒN`<Ðb“wUk9 HŽûDP†hÀ@ɰð¦qÖÊâQðWZQinæq5¼Ʀ7¸³JÄp™A«)îIž`B™+B1ÞXÐŒD?'K°ÏJI7eÒìœ=Ž$RkÂ¥®ãƒ¡ý v§Y—·ªLè.òhÌ®Ò&Ëeý¦° ‘²F‹‘Qš˜Ÿ*þìD†7ô8ÂþòxUòš¬¬8èS=f}L?X5H'ÿšÖšøïÍ$’üçð‰“/cÔi«YlrYúà+®—ÀAÉ˱»h¾&=d*‰Ç–´.é$¾ScCŸ°¡HZ’v§"’·kÉÊøbÖŠµÎ·Äêâ”HðDdÇÂïÔO%—Ùm¶_¢¢J*¹¼ßV`=T{ qeGe1ÖÄXA<†à>‡3Xˆ[Vp]\ÈUÁ>;ÜùÉþû?’Reendstream endobj 77 0 obj 3672 endobj 81 0 obj <> stream xœí\Ysã¸~ׯÐ[ä”…%AðÚTæð)gvvF©¤jvlËWJ–=–<[›_€Ð ò#!z,ïÚ3öK‹ÄÙÝøÐÝ@óã8±Gæß'W£oÞåãóÕ¨z<~÷ý†¸=}"1ÕNŸ\_ÎtE™ŽKQfãÙÙ(eYDyÝj<ÎRý»LÇy UŒgW£Éxoöß‘þ)õÏÃÑì¯ô“iI]@Mþ¥I‘扚¼ÚP23ïã"VB–“…{í¨ßuê¨[Guâ¨#G­@]Tƒz[:êSo¿ëšŠËľ6äÜ’Ìʸ&ò7ØÂ-‘c"Oˆ<"r[ÀÕXÇK"?…Ƙ¦×ÛöÆ Üy³÷ëì£$—BÿÔª4›>臻áÈ>—Šéwj;ž½­;gZÚ ={ÊBoïu¨‡QÜ}ÉÈ8¹äƒ½ )_yNä1b …Åx=‡¬ÄÂ8†¯aY&¸ìâö†µ¯%ÙÞ´™£ùxé¨sG]o§@Ð$¶ù–B=Ý­A9þ¢Ùò0fTšE¹ˆUÊ—8Zøžøo`— š'R\€)–ùEhnG°Æ!Vm;fOÙp ÛÂ0ã‰b ˜ÉÀßú+{³m|Qû»z°kùu郥füÀ Þi2&­õ—_×ú—¶Öí¯w–STtƒ\dy’ˆH‡Èޤº¤M¤+¯¿Z<ù \²Ò1V göÍ»8v®§ve5ïô›¿Fν<ØKD”•ÊL[[ıÔSÐcÉEÅY¦Çb–ÆšVÆ#SšHJ£ÿ–ZV¯“´ÔC¢*‘~’eQ2ùe²7MM›’¨»LdT÷if^5R$¼î•ZRêzMEY­kªuFCg´ÙPêÉHN^ùžÈê•“_öhô¬…jÌG|tŽd¥ŽhÌĸ•©z ËSùšSQ¥`îýª–ŽL,'«0ØÌYg·p6W•ÒH™‰"Ï­‰Çj͉dý^íM¥Q.ÖÅR(YÄV̯!{ØhÙ`XÙšS ½Ó>¿²ÈÊb#}óž5uù¹¦.àåÊ,UfŽzfQ”ÐtÓ¸Cðsޮ̋z!²P¬”ÈgC¿§|@ä bÄ["_ÉZ8„pÁZx [˜…ÚõF–E‘‘Wa")âÒiލ¸šd©UÞ )ó\”e^Yø*O ³Æ†½IV×Óƒ‰3ý4“ | ¦ÌÁk¦ð„gwŽZ ÐXÜyË­Om\W9==†zeÕ&ÉÃ+ CÇÒÑÕ’ÕûÆßRËÐB:ã%=|糖VÁ2n3¸ê‰Öƒ6<6mZº§ôž5% R1ò;5u»¬ÑIiA$ÒêX-£,NëUÛq)T¥“ß÷b³)kjŸ÷ääÆ¸ÞØŠ63¼€â¦¾.MQ³é-C¢dlcûÙ q膞b¨nk¬iÌÛñ\c¬ ¬ ¯b Í‘æœz8Gº±„ïƒßhŸ”Í@ÃÓ2e^ S†^S¥bÅúØX‚²H˜(Ûª¦e8orÔ÷¦ÃT¦JÄ‘Jì°›&ã¸Ýz4o¹ÅÖãZ<…eû–®GV«2U õ·¥fpóЕ·ËŒú¬¥ømÝRÅ6¼d…`¸j¬ãùæôÅW°Hdè(†tâÀQ3 ¯O–ú»£Î€;Úr8½>Èùj¹zOîpgðA \°,~À|Å›PÜØ,ÀF†£+ø€ÛHÁƒ˜3Þ‹b‡—uƼº6ÿ;ü7 õÕÕ󿂯²H¥FļÈ ºSÕ~T{{ø,#çI1>+L BÏ– äœ7ÖJ‡ñã!¨Û®á‹ÀÑÃÑ6¶C,ä-,p+ òŠ ¥÷ \uE›ž0”9#»DHȺ^x>Cƺ¨ðQkr,yY6òz¸©’Žc•¦V*£-(uÛ:vÆYh†ù¢>Ë•¹µ”ØIUVPgÝÁ u*õŽo;Ì9þ}Ok¹Œ%À ¦J°IÁ¶îûVB3Tˆá XñsÜTŸÚãñq’©‹™ÂòxWkÚ6KH6ÔÄWíjuæ©Ü4cËAÕÞ"Ötä|Y»ÓM<'*SÖ¶8ZKá•V+›þ¶ Rä1åþIìž6‚KJº÷cÙpÀç*7û^5xK\7·Wƒd Gù€b•eºqyŒÆP 6Ìnºv%—ŒjZ±*­ ˼ÇÖÊi“uŒ9`ãýìXË%JjimLIy_SÛê©ï,èÏ’ªeÈþš`á³Vö{¬çút ž”¹H3¹““ÿL$Öž‚Ò;ܰì+""ò $`µ¾C“B$²ìpeÛ°áEY2èÑîC‰ö›¼¾½ŽE”K‘äej‡ÿ:$¢=? ’a+³HÄqÚ`ee—ô°’ïÅA.Ú#-•YwNI-¾"CAº–%á‡g008»•^]2Ç-X&×·Î3ôN¾íÃCÂõ̇eÙ†•ÛVÇeUQ}›k)ÝUð´-ÉÌXRÍíHÍ`Õ |ôïÁa+*÷ ´÷P …Ë~ìm¥Ýoçídv‡ŠÝÁâñµf½…$Vï÷à»ñ8øŠ/¦]†k]mñ2$v§ôlj )£ôöUOoþ­¥Ï—ü ²ú>½ UcN`YÇwÎÆp8AÕÀÙ]:YÅåS%²Ây¦SÛ±EÕBªÝ…Õ_÷jäC Z§–Î!ÃV¹ŸXóX Ø^8qæNè > &÷tP-=í6pDÄ ÏI­Õ®Ô¶~–ö—·¿\ðœú.pùmÐOÀ¦¿%iûWò@x›EƒØ)<áfFKЕٜ†ƒC¾•ƒ#­xŠñ…àÅŠ`•5Åʶ½Û6U×wÏa1;TÞæê+%cÑñôÿ-a°¸‚dÐlAÖƒâ·aa²eÿÞÿÁQ‘£¾uTâ¨ØQ¿úÚ¶ðè"óØè£vü‹ðq« ýÌžåLÜûðãnBc|Žžw‰¨!Úþ 6½…Šf¡pˆÎâ{Ý(½åIˆ . wŽu/EÌß=˜Ôú„–vzNàÓ!(Ë`+ð ’8Ýâ ɽ!îÿÜ[ÜZŒ;–÷ò/BÆ¿+ÈvìQ¢ï@gZñä~Ð3r+~öäI’Æ©ƒ áï®u0¡‹“‘8ƒëžÅGØ=v“þ¶-lß“Ä(+­kæAÄ÷êÎ` Ïs1Ñ"oÝÏzûº˜xoÁë— µ±võG¡¸ÈI gd­îÀÚ¡¢kí<®,qÂ(ö[¹£¶Ê±Q=‚5ò|÷,•ÏaË’OlË‚ ˆÖÕãíYÏn]ífûB+ˆ÷§Û½žT?k# ›"£ÓP:SzêæGðsf»úL ëmȧO‚ÁéÝ¡¾Qe²ù%K4|ü÷3“l'qì„?-Ñ®‚“Ìù‘Ö¿(—EøŽ‹;;ÙÅQ’yi¾Œîܬˆò|·'I›{Äàk;=Júó$Ý3@¯C¥™'›S%´Ï¢K!´»’©´˜–®¨òŠþ1†¾_³!ŸaÜóÉû !å>Ì/ÓÃkw~‹cP²P’·' ¹|I/©’eš , úØ»eˆo%WmßÁÏ( ù¢N¿aìú´—š£ó¦õ©m­ëì.s ØîÒ¡§Zh_}#˜´`öAï;à;÷ÌÍ–‚LÔh'zLS±FÞ-CÙ®ø‹ìëC8 hpæWØÅ.*³1P:»rç>Ñ$óŒÒÍmÉV&SÕ(Næjäj¥J¤eV¥V¤ÒSCÿ+`”mü€!ц‡y3Pr#=`ÕÁL¬æ,ðgwÖP—NªÉhæ+œ…ÚºÎn2ÌF?ëÿÿ-HÕµendstream endobj 82 0 obj 3136 endobj 86 0 obj <> stream xœí\[wÛ6~÷¯à[¥=1€à-yÊÅÙm7½¥ÎÙÝ“öA¶dGÙrlÇIúëw@Š˜ùQ¤œæz¶éi'$03ß\êu¤bm"åþ4ÄñÙÞÝgytzµW=Žžý}C\žî½Þ+âÄýS=ôñYôð:&&Ò6V6:<ÙSqYª´Ue)ý½L£\åUƒ³½3ÝWq¦³ÜÚI<Ý·qZ*e'é4K›“hºŸ%Ñ“GSg…2f2}.]ŸR'ÔçÚ=.s¥²”'q–*SN–ÌtN•JL29õÔBðЏéc?˜è%Ú¾ª'˜–²Oq=Õ:N³,›œWCY“·æç§½ösY1§ ©UA3lFâEÕcEYN~Ÿ¶<í_ª§Våe6ù}J,Ò2±š:þqøí’Ö 5ãMÊMk“²R+·IÔéð¿{n=&:|ºwø·´Ð}›"Ï5m†ŠsmlîÖ”ÒJ4Íïš&´|•ȧ˩¥¦Iév¢ét:Ý'Î ­náŸEÜ{î_sgÚ†Üq$á¾¢×eL“·²Óºâ”•O.˜sb^¢é9“n'c:×VŽ-»F+7—$ÍS3y³™j‘l–çÔwÁíÅÅT^r/ÜvÆ Ž¸Á’Ç]ùÉñ4IÕLçE–LÞOukkÂñë¥å…€hpFýɈóÔí¯6ĉ¶¥vR†Sueh"#:œ“Q¿R­§ç6X¬³™&)¥ÀßNKsc߬>ÕšíÂ2[°ÌÄøX’•øTI‚\´7°ÍVÈJL«3mk©î“DH1 ¼ì`Þ¾ÿÊ7]‡z¶‘Þ1Ù;ÔOžzੇ@CvЯxãpˆ“GPItÀ| c6žJ<¥=•›¬õKØ?Ñv &&5“)ìæÂ`À"†ý~dò¡`1ýL¶zÊJ}Û*¤o!©·UÃÆc÷2Èyt•ÉT^™ö¸ð¶ûœóšsïÍeX݃œßÁÑ1?å¼èÜç0˜Ì½^‰MOÔuÔó‘쵌M^Ô"H™RØkÓ&ô:3KÓÅî"Sª#dK¢TÚ6#Œ¤Ñ<ÉõPò\4\ئØûý†gÜèÂS&˜¬æìFîλˆUi|hKIVa¢¡ŽÖd&V1b;õ! °6×™ÙD±·òZ¨(·L U7ò9…óú'y®^‰î’x[ö±a[¨<S^¬Ëx÷U¬Ëf+8hùP‡P ]k3¤…ǵ¹ùPÒK©®RIæ¿(—R ô‘B+p¶}Äf»†"¾†|iåÙdš—ÎJÂGß!cò.«BÂOÍPÎxÍݶ'd½ ÞÉÌÞó=án¹dYéBJ†`ÐWQ€Ê <$²Æ*-%}3&i´Eèv­™NmXIÚÔ$„nTIIºnj.Uþé\³¦\5u%SƆ^_q>/`1ëFÆ´3UE°èè…” [9{ƠӔèöx0nùŽßw µ[k(ÁIÔ¥pi ™’o*àÀ ?èÅvòÚEÙç¸v’ÈŠ#¹§\íC¾x…ª‡ëô¿²ÚتK)“…0ß)禮@œ«R7u…ú½ú‰+“oΠ¢J‰)4&‡Qi[ ²LÓ® ›Þ*KáÝDµ«v¥¨íù _LÉÒBS1Ç@à„øÖº—Dt‚êiâéÍ”šfeXhäi±äæP«4o6\sÓÒ}¯„˜ö¬*0¹áÅŠumÊÖ™JÉôj•I[L£2wZ.Z@M ÄŠžbã„@õÀ¨æÂw Ê >ÚRœoEZGÚ†‹C!5ä$w)¬Šã« ‰Xɦ/Vƒax¼†*gQ¥ÅCôa uׯeîÃ/6’52áD €'QI¨H_!ºÇΛr=—”ƒÀßÃøc—s샻ï6›"XK ¶!P¶®{Ù/U18ƒ+’ÝÝ“îu-àêÄd©ÏùÂþÖW&Vö 9,èë8KL€Q£C%†NÑDÌó:«S]’˜ ;²¶ÉJk{Ráµÿà brDFßëÞ²¼Ì{¼—HÉ]¦`xì“vd>Nz[lïÞ`ß }Û]VÙ N[|:m›[÷¾˜Ø/g™aO' ®OoZØ¿œöây= $˜8dU…¶‰ 67²²ØÀu‚ À‹ È9«Õ&¡w&åð:@œÜÄI©ÄÙzËFÃÙYSj ³³¾(s²ö×Ô•¡ ¢“(ºô¥{å;õ>eÆ$Dã‹3µì]J†-£ÕÑéb_òÓèRIxô½KT9è 8GÌá0¦)b ëV&6Yè8iŠ-ËÔ_¦d$‰.Â4¤Þ|¨¦­ÊŒV{*‡BPt —øœ­ÿ§6T þí g÷a 7tcÖ]rÞÜöH­é™gæ*ä ´›³ýZ[Èî2_ÅjÞ ŒºäuÔcÄ®ªµT–̉p C:)|/)ÿpw3Û‚ã»zmÅËÛŠ€?† ^‰½^‰˜ÑCºd,•$únAíŒÄa­t·³NþóØ$>܆A>†&ÁÊÔ5~2ˆ,Àl…dÕD`*@&±wºbÌNùc÷¶ÀHá†TØaârPÊm-7°üTTîC‰â j„¡àCÀժТ‚ZMfŸäÞm©Q”­ëˆÈ´ºÆyYDuˆ#&Žc¸¶ÔÒºf0±{‡FšP%aðÞÌût •J„hïÀ¨¯]ó> Ó™J1ö0¬}Ï€âš}ÿðI÷LÜšXô÷ÞcO)¸2<ö ®?ð §f§xÓ¹Uç;m`¡ÐXHì>Õ–Þ¿Uï\¥iì6‘”dÐÈŠáPÏ3ón €—°î‡•l„Ú*rÜr|8¯Ûæ—+Sá—у…­K_U.r"`MiÛ‘‚;‡òÂŰr6 8m,pì‡ñ`‚C†$ôoc+ÍÊ9 ¬Í¥}Ý›{+½ôÔÒS7žZ€«DÇžzï©àÂ׿  0ð„Ÿ½=N<5ÓZ´¹´.üàLâZ&¾it 8tnçM6÷}Ю]®ÖeÊYy.$‚(æ¨:¼»w¹úzon`íjRP˜Wßl¯ZKßåúÙ¸¥w{[ÂѱK··[ºRqù‹½×·p{§}§å‚_ömÏl„ûr½ºq±k/2¥ į›$Á‚ñ,…»è–ÄšÁu‹uJ×n˺1·…SŸÐ4‡¢mç#€ý†ïmœ6£êYU»FÚ{G4pŽç$ Xàï$ñUkà·Ãjþ~áGq±4º`„œ:ºO—ú–(.É^Brp‰¡vÝ ò½ÅÍsNì ¢b¼ˆÖC¸„mW\Àn8>¸†ÒO߀nW»9©zî©'߂܎ XN ‰˜ )Ü?:–(•ðQ Âíø´ƒÿRë| »ÍXs~z*ä-ÖÁèï3¢Ù¥^Àü§š{ܱV,À|ű„Ìæp´+ñ)ú 1ó77ìŸ ˆJœüe¡¥t?†û 0»€ˆ„méC?áoJ§ÿß>|“<…Ðè]Ô=0°ðW2üÝh78Üi70Šˆå/áòqä&„ò˜IñQ׃`[Œv§s » ¾bb„"ùéqµHÞü¡Ô¯¼·ÇAé€ÀìµO0‚z|9D°›àû2 𦋧÷‡F[ÙÁš@¨}8Ú6À&x9Ü ‚·$?mRpJ|Û.yá7/.ââo9¿M€˜!¼±‘¡ÏôõÙ‡š>6Ïe ò'“ X\VÙ—r!%ŒAA61ä>> ±®Î 3\ÄFr++J¾(+B5"Ö ÖÆÆF œ¨´ŠøcAB¯àê°WÆz" g’x/ 3È}œ µ'¶`ý†=Ï`n »ax[Aa[þ¡Ÿdæzù›|+⪷ÛWèþÇÙäýü•q8Ô9ë|}.l£|àƒ>q¤MNÜÖ‡Glí»#³ðØk‚ïwu?ù o䈻bõo—‘Ôüîm˜¤4'è'¬[A·˜ZwžPà‹­Œ.`{ÅZ8ÝðÑTïñÏë:XUÜ:¼»º0ñ¬nŠ®sodÙ–CbwÌF†ôÁp%Ú1ÅëûƒñÈ¢=¯D®)>oO€Vö)Ý H]w齎’Ö` Dì l „¼„|qÔ„ÛŠ9@‘D×[zp¸÷+ýùÆÅŠéendstream endobj 87 0 obj 3531 endobj 91 0 obj <> stream xœí\YsÜ8~÷¯è·éÞŠeФD)ó”s+;9v“NíVeæÁ±;Ž7¾ÆG&™_¿ ”>µºÛ±³3©š‚Ù$‚ ~Ÿ¨$ÕåÿµÀÎáÆÖk7Ù;Û¨š'¯ÿÞ§{¿o‰ñÿU Þ9œ<œÓ@MʤÌ'ó*)ËB¹k:É3ú»Ì&.M[Læ‡ÓÉlþß úSÓŸÏ7æ{G-›Fiê`§¯L2gìôM€&z ­½ý:Ðq¯Mç’†S€çdÉh;=¯¡´4-\0x ÁcØ*0l3xÄà.ì{Æà9œâ¤5 ûmþÚ7£'©M”å+mgãr§“û{7Õ³M•äiîˆIÉlÓ&Y©”æ3—”6/*j ;}1£QYž—~ aÌÙËÉæ–‹ÊÊÖ»b`T×›*2l‘QãûœË}#ŽòϘù«¦\Ç5]Õ96èì¨ã€6 í ÞœU]ç ¤ƒ+„ÿƒTUŽ‹aÁgÕœQprkGÞ&Ö‡ê^8è…]â²e¬ذ\R£V]£µc8\ðwtI/gäÔZç„‹xúb©[βc%:‚ÈD<RUyLÍUŠ7ÏØ¤pÈÐÔlj‹Cq_«rZw­O^Tµâ"²¶)¼ZâÆ/2¿·†Š¯8›8 >ò@ב‚Æè*©´±±ö¥–†?z;Ôu"âðÓÚV/"J·Õåßðxç «*6RïûBHœŠr*¡2þ^ÊŸjÅx§r¤N 8ìľ…xÿq€ô?Y!~2îõ§,3_¬‘Ž˜´ˆ=~·=õ葽'§B>Ó\â[Vþ—];Puø7?KÉz}²<¬vƒÝ"LgϾU†'’nŽæki!½ã×JXÌ»&ˆ†89@ÑåJ¥ÊòvŒH/X4G*4éYàæK¾ÑƱþ#ì% ÉÅtc#wPEu–ô¨q3õÒ3Æ3[ñl¾%,x”S½£`u¢c4ÐRç ™Þæ@“ FNPâ9úè9p‹>ƒˆ´(¯ckDÔY™ä¦\úZ¨“¼èz‚-?œÎ£¾Zæ:7öQÞ¹U£ÑØ1ì‹ v¢µ²!­IÏö6@Üè°Ç³ ™t gÿŸá²‰]—qϨk)ü½0fò¨?¨ «>p;«ƒ¥ZF2QÕ˜qÚçCSqë¾³kx&¼pgß‘Wÿûvà.\„˜]3Ç2î˾¿°sCnP@Ln”»?wý=¬¬K²”;žñKÃe®a¥ëÜ…¯'}ímmá4¸ÚßåQ«Ý«ûøUð ßm÷øø: _í øÀ“õ€“qнY9”i•eï>ÍQY0×ð÷6öô9@í·:šoytë¨vô5@!×`íöPKÎÀÜvF|Ð6 J¿ˆ‚'\%úŠ*ÀÑ⨀¡ûÈhÚ”ÎÀ²ìË=¸ëñfUˆê^m[¿R´;:¬xë3T€¢ÞÐ1i-¶,{9uS\á÷$åµpeDZÖ}ìróÂ’^=[.Éþ„›ç†¹znŒªÎº¥â?ŠA¹„¡]nQž®ÈŒŒe¶Ø¶ ¾P» ¶,¯ê rt ªòzîÚ÷ù¿­J‰·ƒÄ%,n[/½µ„ÖòŠ#Ñ)vª±C¹7â7Ÿl1¹ÿUë^»´ÈAmÆŽ9¨HmÙad·î0@±Wš+|† rGÿ\“p…ÿwAüáA"_±¶Z=„øÉÃASÑç@túo6à%âàïì@©oô ®h¿X…‹ÆqiëÛžM¿ÓŒ{ùò‚Ù%¾‡ðèzøv—z ÉÇŒ½Ò`nÞ%4Řp²G¶LLíôp.îÈõœÙö[´iÂoYq£°üâïg ~Hßµˆò Üý'°£¯G8$À5¾$pGøZÅ[H¡û^$àȈ à‡1ü‚f>û?Ý¡†a‹'Þ‰µ}\ÕÚ°=á7Aü½¯¾‡±Ön`ã!–¿—Á”Ç Š7sÖ0×b¶Ïœ8Là«{„^D¶[ðd¾ñ/ú÷?7ý8endstream endobj 92 0 obj 3388 endobj 96 0 obj <> stream xœí[ms·þÎ_Áo%;æù‡{s'ìDmÝÚUb3ÓÎ8™MQŠjñÅ"Oó뻸;`äs¼-ÉRjÛÖ °X,»Ï.pûa©~hþZb:ï=}“õ/Ö½²¹ÿæ/5q}ÑûØËƒØü)$=÷_Œi JúEP¤ýñy/ Š"³ŠkÔOú‘ô³( tÞÏ{ïýá(ý_¾%2H²Xþé¨ïõ½£ÆŽzSS*•|©\WjT:Ê‚<“¿O«ß£",™\0¹aòö½‚ä ë3ù v¸„Ï ‡3&'pØ{Ïäo0Û .þ²MgHŠøþ<þ[Œ@‘¼êÿè™où­™AÔbg@Lou¿¶éçë~šýÔ`;_îm“œ8êoëÒ0 "5ìbÒ²‹GIï‘[¨  Ôà5äû2Ö¯hýSÛlb™LþøªÁζ„°û:‡žt=ÌG’÷ë—Ÿ@•éÖY[Ñžxâ2>ZÿpÔ‹G{Èfp6á …ûÃÛ¼„–úÖ-ü&‹uu„.×pIجΠ~~.ÏnL£I3.|væu÷ýhƒ_ Þï6N ‡ùa³ë쀪Ú-=xç¨ÐQÏ;êç6çu÷àî¶G£vê¡Ä3c-Z¥A®L~8>«“½ !²lßQí¡Ïz þ™?[Û•õÔüg‡Úå‘TÑàªgü"œõ8À G$@=ÞÖd†ý æ+„Ä–~vxŠ·þKlöÏ8Ú@à0z{¸[Ž w %[fm°‰sô/±ËG&¬"ëùtxùx©ÛÜe«á§o¢ÈUÎ"etM¿üÁh>QVó'Ã8Ó¢2ð$È"Èå0 ²HQ„}jŒ“4 MšhŸ8Šä£9⤠µÚ6Z_FMY¢HÇšãBSGós¬ÂÌdžå˜<–ܹ+™]&™ðÐÝŸÎKVY¤­ôe/!À•ãºäY? ‹ ËÉ*~Ïj&‰Õf©:¯…2à™³¶k@-÷Ú:ðê £©£xjqJñÅð†íXkD¶-€|ou5¿nÀ¯¼¶_À*Ï|úø,v§™õAÔ 8PØ··æ,¨Ù½#éë°Ñp{+”Ò²fÏÁ¯ç`õW{ºk2º ÐS[GÍw9vÒNÜÄ0¡ýsØA(÷òŰ ÷~k‹€¢Äåç-ö4À`vêÔôÖÓönX{ê¨SÐïHâÏêÀhÝ^:Á9 wØ»`0ˆa³p¸‹UŸz¨ù¦¨Ð;7E:Iƒ´º("Ti”f¤£`8ÒAR„¡dÃ,(tš—Âääë èPel)Ì Ü˜ 9Ã8Kh1%¥J¹ß7†.²0Lc–ÿTtésóØñs¬ g1ì[-ÍC¥H5&®§iZ‹£Uæ‰pmQš=·\¯˜SMFaNs•*H ˆƒ4 +ŒD?çyQ ~¶µn¢¬^™ö§!1HŠXG‰4ÜáiMgH›­¡Aãÿø(å5ÅqB)*É$θˆcæÚ6FL “¦1l10÷cƒÃ1J€ ÑN+pÆ¢¸@`DGE@úÒ·l™_±ŒÁb)7«µ¬hÂÞs‡K„œXVRžŠ :¥&ÞE¦š¢jžqš¤R·K8éÜYhIDóGŠ8‘ò?Ô«Ž‹Q·¥a)©3WXH¡õ¾¬«ÝWqiÏV¬ ùiíáÞzÑ…Ž%™Á½Åª¼rRÍvwð€uyÈ‹Mz'Éñ²=¹Ýx–eéY›ÕÞ!lP͇ö˜ìvŒêÃ{VŸå]Øœ®—smœS@ކ¯Á¯ Ð\ùý;˜ÍV³ÃÖú Žœ[÷ð]nö½Õ‹a‘jŠx*2ÉC×Êímï#¸ôŒ=_Yqíø…£^­6ðˆ7þó/¿öl SáAFï-·.¸¯ k^ØC²±€þU¸}ì€K¶â'1#Žß8~N`ëšaà R.‚Ã…(špãTÆKÇZ„£™dí”È‘ÅSZ°£<È•²[È.¤œxRÚ $"²µqâèºãj†ü¡ ƒÜÉdÍÂ"<‘ºG€W¿š6ÇqØB+¹²²ª j‹á/ˆ½Ø£Q«¶¯œlÇ…ÉàYä蟋ñõ¶Åøûñè"÷{ÐÆý¾q”­`ÔŽ^‘È™zhÁ^eá¦W°uzx6%¯nòdd[_3ùv0Y¢¥ñåÞ7Õ^˜“ÑåàFk_.ÍÛTKŸ”©î´‘•§õÖð¯Žb|ÉXåîù†(U¼çû,@Å#°á¸Á†ñ­M‹µv€0¹sb?’s$sȲ¨öãÆc6‚…}Œ" º'Ù çÕ•‰wù!j¢{ÊðÁ‹ÎÊPY^©@øî" ²P'ö@¶‚é( R“…b4†áž’5©2Œ4ùÖ¤ÉIv¯©Ýrág"œ¼Å[SxÅä “¥Ìfæ}¹ó ,Tfg ¤&«ØHµJÉwˆÀḣN~”ÒÊR%.ÌìöMÝC«½­eñŽ„纪=ÄtÔÒÂ:>œ\ ®B‚g»7²ÌŽIƒýóuûÞ…z§Çõ|5Ï—ý¿uç„ß'Ï!‰‹‡‡Þ_wz0ÂYçI;5؃©y.3©ÎO°ñ[î§»?øçDïù-._‰ã×bÛû²#6èõç[û=”ê’G|ƸÿÐBÚž‡§ŽJƒ6ÿ¹]w›2ðƒlxëÎ*jú€ïsõÝò9%+¼ÃG”N+¿£H½A ¸ñø…¾‹DûÂ99'ïãá×}iºßBŠO!ÅÚ\­í”ü ¸ ÿDöªm¡Üƒ¾[«j+Èa uØú ¿ËòâÒnІèÆÚG_áí‡ì[øêëªð.R¿cRd÷Ïa_|7!fûŠ3…Ã>Kówþ½¶nQüïáãÅ[ý^ûf=é{íÿÏo¥–ßã·Ò¬Â»øÆDÚ6þõ Í;;E²f®âsbñoG½ýü¯Á>mÛðîïÍ›>XÀé%“O™<…}zaoÕz2îý@ÿ©ò|endstream endobj 97 0 obj 2600 endobj 101 0 obj <> stream xœÝ[Ýs·×_qÓ—ˆyƸ/÷)MÝŽ2í8•ÕéC’éP"-±¶HE¤üñßgqØîGi9ö8–V`ì.v»€~MTšéD¹Ÿž¸º=yz^&×›“æsrþޏ¿>ùõ¤Jû×|ôÕmò× ¨ó¤Në"¹xu¢Òº®TÙrÍ’"§ßë<)³,µUrq{òÓi2™¥éw{ú_"Ó¼4öôÂSgžú—§žw”.šÑíGÁG™l×6+Óª”íïÚö¬6§k&3™0¹…}gL®˜Ü0ùŠÉ䀧X2y 8höËÅ'$TMBýçÉÅŸ±ž~±.¡$¶pñ÷PB>W§£7P1h Á"Ål€„-ð¼4^Qÿä)å©gž2žú%Ve6Õµœ¦úÜÏî]Èi;`‘=ÀaO _¡•{¸œ·PÈL‘5ì`=#?zÙ¾Êüd*ä®z¬+ÖÅ”Y;¬¡P±=,á0aF_‘ŽŸžg™@™vANßøùĤª¨IÔ´ô©ìô§”Ýö@í‰ßOÏë$˃䲎rË\ë´hSKB¤¼Wħ‘4 ºø?Ãw|ɰ2jÎ -pÒÂS³œ1¹òí[þ¸@0l°×–Á€Hb¦Ý0®ºuITªJS6‚ð`©oLÄr® `¡ŸNp7!;.+ÛóÏ­î6Öp=Å×\@Œùè÷¬¦±Ú’Éè:Õ™É%È‹"w¶Žw餶ôÔ•;Jilg΃¸ë†»Þ1Žd¦d¦Ú¤V×îˆ9{Ò†¥­È~.æMÍG Y?¾ÝoA‘³˜¸²µ”Ì“ÌJÈ+e2¤;«Ö ³»v#éZ®šÎi™fVåC#ˆ‰9…¬×c R l¿(a:X~—R-¼ê‘U‘-‘ñ*eØNóLžŽfÙdkä1óš"/„Í/=µâ­,ä g-ÓÞ\¦Oq6Ó 2+Š>a)óÞ"\j³ìÃuEÎGpzÖ2*릆)Nrœ)€Ø8“$C>p -¡¿‰™GPZÄ×KØ—¹FÒœ Ãw0|ñÀä *i7pb\:½Û…€S{Ër¬!N¡¾„†ðæº0í#ëÑ£ÚÜŽMKHX-¯¡ZðÊ>›U]õZ‚]9ƒ±d>ÝÙõ–5m{N»üd~²ÃÀn¿¨áj;Þb·PÆÇÅna‡쀙-ár*9°ºaå§l¡{2ÀX„YÓë‹×2¢dc8°Žû«{ Ž®;¡@`L`Ä´7;"5ª:ò#aI†ñÆo˜ë̃U1^@ P)“7È“â·UEØ· *«B7ðF‡0hÛ\çùÎìà³@°}Ðþ]lZÐbZÈeh"®¾çfP©îöÕb.Cÿ=ÜëtÄp¯Í()5#=oG ‹9ÜJ¦µ¹µï|;Wž¾÷Ô ¾üwZzŒh®FDÃůw€9Ox ûkÀ¯|i€Ð)ÛÞ ¬›XlÆöHG ’ÑD¯º¶‚Y(çeÐí|PŒÞ–l"_T; «x3lCÕá]§Ý̼*œÇý"‰â—8kH h ‡K–Ò>OòôÜè„À«²¢¾j£ºŸ1$a_`¥ì8+JA:™Ú4¯•r¯CÊ´¶m5ÒTäÀ›#¾g!èKw «\*Ÿðø3&Wm»nΕµ¥ òÚè&ÊÚ´ÎŒrjí»^]‰®K¥Š¦ZÐó]ûÎàT]WP!ûŽÎžô‚Ù)zµ!ÈjøáÖ1ÄÉξ„©‰B%ë¡Ñè´(jN{/]’{®Ë¶¾‘«4Wm…ö}/QÂ+Ÿ¸OÃeÖ'иøú^º§è4Fš›ÃüŸ÷M›Hú8ÃIè%ì;¸˜p~'ÎWñn7Q¯ƒ¯š¤Oké3°$–ÈØp†Œ—Œ絿¼)Ù–àÝÆ`®æGp=nZ/úÃ&“¢5”ZOხ`†ý·Z£Å¼¬'ñuíH•(`®»â":ŸX ÖÓT:{²ßvP²ßø¸îüO‘fy!j¬n·E]õ—”º‹>¦°=t"gT¥5¥üqÀÁ¢—þµûøB«wj»«xQÙÌ •µÞ#,¾t‡‹1bº‰±PÓ«‰¥¢0¿€A)¨å¡J×è Âû‰3U•UòbyèiF®ÑPVÆi'g·\Fà|89§UÌåµ§žìM¿˜ºS²€_OiˆïÃÚ3cøw$%ÊBË2ƒƒC=‰qäñVÇÆÄk~pìÛÒÁK×û½Æþ|*qtº}ð„Ï#Ä´\Ãþy+¨âxä6xð V•“qä_ ¬ÙuèéüÖA¸¦äׯZ‚Y•íÝá r»çÑÃßë-l›NCag¼œœYHñ=>®s0ƒÌ‰ƒ”Èf𣉱GBY°H}ƒ¢ Î$¾¥öœ,'kÞuƒ4Üm¯ «­÷žea¾>xr=LŒúà8 gÚ[Ž| ñ8„¾ã\í{îî»ÀT†W©a‘Ö)ÎXt‘#½¿AÇãªIÜbáû¢U4£ðd{ÅÐô±ð€hÚ{9œ >À]Ⴠ ð2g-÷×ã.e v4èI”Lh‘'Kãp>íÙÆÈ ÿ=Eã ´2òÛ¾am µä~ÌïäAS^ÿ9Èv üóáoOInø‹ @wÂòHþ¢7éþKÄÑîðç7ƒ/èïø?A0ðý€ð ïfÞÔ ¤ÃÔa€ùüºÝT6ö…MYö~¿Reن͢ÎÍxØÜwSn+]oå´ã•çµûb[õ>w}nྻ'ÂûŠIqg¬9ZË8rô•B„ò}•UÏQ°©c .t¥ôC¾+øg‹Ï †Ézи:ËO]Eq䡳—<-ë¼7~;P}ÃCɨŽk«”¢@!ú[JWøJvßAj}¸H &ãoä¨ï|aj$ÞT‡LŽŒvPq^:-¢ …éÄÑÞ%¥øA,(¸mCâÜC·ÑáCcS›kˆz¯ä ~Ö•ê䓎ÏïPªÛ[7%DІgDµ<µ!\v Þ¥ ÇOÀl·€ÚñW²#¯HŸ_œü›~~r©)ëendstream endobj 102 0 obj 3029 endobj 106 0 obj <> stream xœí\Ërä¶Ýë+z—îxšC‚Þ%UNÊ©dE;;‹V·F£X¯™ÖÌ(åò¿ Ü ð€d¿¬±#ˆ‹{Î}°?ÌÒ$³Ôü³…õÝÙÛórv½=kªgçí ¯Ï>œUInþk*xy}7ûó…î˜e³:©‹ÙÅ»³4©ë*-ÛQ³Y¡ôßµš•Yš”ºÁÝÙó/‹¥HDU–ÙüýBWgB–ó+Wú¸Xª$M3Q³Ê™.åª(Ò|þ´Xf™®Vvg-½‘L§\Õóë¦e.Òr~³¦XËùv±,õÀ®ç5bƒÌº™e=¿§Ÿ¨x§ßL&i™—óKª æf ÖÁž¬¨+«}Ç~¦¾úÊùsšl{¬MšÜ®^e|ÞÛæ¹ÙÙpõÃ[¡‹mÅ¿/þv&r™Ô4]l´h¼¡aÙf?P¯G*>ñ \–Íá²–lY«î𫜽ͭ®¬™§jþßEV&™ÌŸôžZ°{ºVä^­·ÓnÚ <ÇK½Gy"E]Ú%¤Š??o/JQWzcÜÂüì W|c\Û/‹:)+)½%²uÝÃu¥¼Öµ‚{»¡ÁØY,eR¨TU]m^¨¢‘HiôC.ç ¡¨hD̶xCí*ÊJ¯¢Q&U^[±¹‡û?[ˆ²Ôƒ•¦ÇR")µ^2#)Ñöc"„U‰{yï–dœ»à×:m\‰ú¬\Ö1»ë·°Q]Ðôe§D\£Ê§PÐ]‚¯°'h¨VRù’†D†­>…{° 4Eä µ‚&eQÍ“¶6O3ÝÀywÐ?ÓÿÁü%ÍüÁ^,ƒ@tp×àà¶íÏŠ‚¿,Ä*x†÷®¥7B(¦;×-áÃú-””÷´l+?/ôTeªTDòžB¡2›Š›¾ƒº™½ »+./fY‰žÐÛs¡Ð|¡²Ç¥&O…>?iæÈUæf+léÑ•f®Dí®\éÚ•î]é“+Ýu%Qt‹óh¾ò–¥#±·ë¯lj°ß1›)2%'±™´.”œÊfÒW6s\6Ó€ÌÞl&é:J«^–VXˆè'ÿ!¥x¤Ò¯³$€Ú)6üN*%Ä@ª©ðÞ‡R|ÞpGÕÇ?Fý]ñ³iƒ>„W'@_i°5«"jç)ä&{Þé¡ÏpîÓ(GOðWø4ƒm7c¤{›b ÞD‘¶e0pãÀåe ´Jê4³PÚlPQëÇ-¨ÖÊÖ˜‡ PGf±X…xïÕÁ¯+QѶ{ˆê¦`€—à!*¢Ì6BT´®Ý¬6´±Í›éK ާBÔVfBD5z溵 ¡J®Ø}à:'3ê\TzÛEˆLD|™w£ø6`ß1|ƒöb1/cßÉÌ»»ŒkpÅV®\iJ¼¶ ô³+ÑëŠ7å6`Àgê¼pÅ_^ÆŠ\9D#÷Óf¸w#T¡d ÙnH¦aã£ÆÊ;™—1æ´:MKÔ NKEqJ23žIó3E§‹Y¡›"¨µÅ7-ðIçÖŒ­ÃFü¢‡›ÕÓeFð ^Á·ŠÑ*×¶çZd:Ò 0]õÍá-\À®N…þÓ\ {³")$¹aV4t c¼>-3ܯMXöèJC€Óž¿-‘2¹uê ó/G†2hÐ4q.t<¿‚àÏÉó<Ñ­`îZµúbºW![ ë4èU(2oá=ÔÆ8²-F|Þ¿‚Á‹h|Ó;Yû´ª©„´Ê¸Êc€rwF5IÑîÀ¨ þBu|·Á¥:¾ÛàT¼,«ó!b&«ÒiZ>SÑr3SþÅÅWâ¡(šƒým¸(^ÖÃonÖoÖÁ2Ôã¤+ wDJxuð¿xº‚+>ué H¤›+÷ÓR}Z¥Gë(`'+¯uVŒÞåþsšsç¤fQ®szmÂâ性˜}ÃEO‰ŒšžØ³ÓŽyR+_Æ}0nù¡wü*,?0À1n×öĦß^ÐÓ:K'˜~\]t‡¿l}J¯6àÿ³ øö<³L‹”¤„ÜZ ¹yav¬MÈÍË4)²¢”Æ«®W¤ê4•s±ÐòÕ.m™Wš&K¯¥`åËE®W“— %±ý¿§â}û\4±Z×ëIO ê\XÏòÔØ¶éÚkªËu™¦…2¢kÇ}pi†™.ÕÆ6ðæ70Zhh{tíh¢vð¢®máxJs.‹¤f)Êð}xÙwˆ ˜¦kØ4Ìÿâîºhâ™}Ì((S:lt&º¬Á6¸­!Ó-Q¼Z¹{÷ê\6voCžˆ‘ƒ ¶‚ÝzÖI£HWþKvÊ~ é26Hñ÷ ?QÕß@­Mµ²7êÔ7ïÃ2qN¶@†b ¤ ¬xAþf ‰£Aå hd`:óæN;ÆA0Y𮋪5l ö¢Wd¸pï.Áj¿…§À°Sð£jÝ;§ñl…À**Q¥ïì½D¸K´ôS.ä‰Àì“ ˆç¯à(yÄW¼Q,ׇŸàâ.aÛ^Ò°s°}Ço» ZMX ðZ‚Äp•Ev‚$´ïí ¨1^rFo[PÛKä¤ðÛ‰x³V·â<ÆËö]¦›0§‚/sX°ul'Ý´13̰‹ƒ xøô î:Yk!0ű¤"«²Ê”&ÐÛ¡ò$¯]¢ÍãŽ&ˆTU’ é馉ѼÆIøÄnÃZ† :t¶Ì£:D1ÆäR–‰¯Eèå:ó%†ßn2ü´¨¥äŠu…-äiˆ¼™¦JFäò ¹îöä x«?pPë¤Ð–9ÝDâŽ'`¯‹ ;†ßØîu?b¬~„»°&ÎF+‡TØ(b’1ã)½–gˆø^ÂyY”ŒÈ,6prÑáQíž‹§Q1ŽÒDV•VjX¯@•ZŠ}Ÿ)ôŠ’Oƒ™0ý3±ŸHpÇØs7ÒÀfˆ‰ò½c ǨѳBr˜ä…ÁæQÏ%d¾JÜCÁÙ7f•šXkÐÖ¤1]Ëô|Ìôª9JÛï%ú “»ý©5&7'M‡šêÞç`ö\dz±¦Am§æÄ»lAüîŶ³?£€ñåÄ ÷TÓ(?( ý•fÄã$NJEÿ©C;—¿ô«³6üõ×No/< Iy»%":RµwLªåLi¢*çèñœ&È3úylÔ¿ì–ñ/ïŸB«ú¼¦Ï‹Ð`Sh¯ef8VÔˆNZ+yÎ+B·ÃéñßèçJKNfÜå¶“€oËCÌV‹–UÄ=DÑqß1gx·Y÷&œ¥•œã¹‹"÷ bêŒ4<ÃrÏ%€Ð„K'»cì]À´Eo°ÇrôëŠÞN„ú‘·¡iÁ0qªZ-‡-› ¬-º÷)3î-rŽ®ÍNó‚Û ˆsi²)[ÚaI )`ò#hž£8"¹­ocW¬Ü–K°ísO#‘~³al»°?{¡ ÷CÁði‰]ÖÊH4}ŸoEöIm,¦Ò¾ç‰ð”t.yqF~2;'| Âbå.ØÎ6$Ž”<éT}ó…cƒ”E­òq¤²CsqbÒÓÈpŸ®¨[=+ò¸ÞC5ëil—.ˆ_Øï=öÁãhøQÇQ«çÙ`o꣱8)sv¦1ßWæ1¼ïÏÞÌŒ'*ÏFѲ·"¿<óš}réƒ%¥vݺ–½£ï|fˆ=v`,e¥y°—)‰Þ(1<…†C¨ÕY ga?ÆÙÝÈ™oW¤O-Ó)“#aíŧŒ ÈŠø­cö”ghyžZ´ƒì«‹µ‹!J%€Íý<:Wö“\ýÿœýÈ…q¿sÇøI®¾sûˆü-â½£óe4ÒOú[S¹Û©~Ì‚¸ÔÍD¾vœ¯RnÀÓ;0òfªÃ³êÿåendstream endobj 107 0 obj 2936 endobj 111 0 obj <> stream xœí\KsÛ6¾ëWèV±c1Ä‹ ¯i;Í´‡z|ÈLÒƒl9r:‘äXΣ“É/@‚À\>$QŽê2Îa K»Øï[,åÓ$&tšèŸJ¸YO^\Êéj7)š§—¿áa5ù0Éb¦ÿ P¾YOºR ™æqžN¯ÞN’8ϳD–ZÉ4ê÷\L%Ib©:¬'¯gŸ£9i&%™ÝEª™P.g·Vzˆæ"NBsÐ8Uiš°Ùc4'D5ózzšô &òÙªèÉh"gï"®ÅœÏvÑ\*Åvä­ë”LÝ“7®ÃG'®ÕÊxœH&g×®µI™}Z0ðÉ ­w¨î/ÁØ73÷°%:âFp¯f/|îûâs½³o"töí[¡”5mÅ_W/'”ñ8/¼éj©\ã©›½u£î 1%‚Cs½S )Œ z‚i-Œñ3Vó^5æ1g‰˜ý®$ðÐ{ÔjÁî©VʼVo§íc—¨¯Õ±˜Ó\zS°Ÿ_–%Í3µ1vb¾‚ê ` à€|ŽòXfœ{SóÚ óJ`«Õµ@÷vé” ¿Ds§"™ie©H ä:>0>#N…p¢v±ªÇ…XÍBfj…ÛPg,¯Üfƒîÿ4¢R*eR˜ó”ÆRÅ%­IÐrp!<”ØÅ{§„Pmw »´´’³°mxŒÙ?!°++¹¬ªF¸¼ÇGÞâ9Z᪾§a.fŸ {°@?C¥§qžf³¸le Q´Í¥Ÿ¨ßпqýËÕ¯÷iƒ¤Vº>•ÝÍ^u¾ˆaRP¸è˜@(Áö&èåf°E·2t­Ò‰ô‚Òð¹zç A ï[4Žƒuà°óØnÌ—TXΡ¸EVYVÙŽ%T™šëe±E°©¤ÔJ[+Ý[ij¥+ÝZie¥•>Zim¥ +½C>]#š—­3ýZJ$/1½¦Û̆g²R¤»Þ:ñ P9ù[%R³Å‘SÎNðÃ3ò·‘¿ümäoûð7+>þ¦ "yå6øù[¢¬%=þv2V"ÿ€kWJÓÆc¾'5Û8‰®-š–дá¤$þúͱÜ­Sô"K'S,|Œgq–U.ÖÊ^R’µ³²¼ˆÞ¢ëj‡05׆iÛ9û ª a‘_C? 2ãÜtNGê|7ž4ðÀb<F­=î÷X4¤ÜõÜXiaYÉ{! .©EL ¬K(Or˜>QŽôáï GúàÕÐHÑë½ÐˆÀÖ~h”ZG£" 4¸e3U=:’CÍk™5 µÒ˜ž{RÈÌ ™•è™ç„ØLÏ$',OИŽIᘞoRÈÔÿÁ’BÃç•ò1'üŸå„¾HRD(ÒHíx…‹ µÉ ›ÎNt:̉fg4pì—@Ú¾+§ìвg÷Ëû/a üQ9¾J ÊX/Tfà ˆ%çeâXºÍ˜8މãK—/^:Êëb…€ °)(9FÆù§©_þ‡d­8¡ªçÖ> ªŠ4H'Bø¯98ÀsHÈd= ä•Ð aò—bÙßÃÉ3=ZÅŒ1o:—¼©ð”gœ7…7IcÞtêbyúbZlrÇIŒ³8R¢>¹úÛ«ÔŽPj5sg485©? ¨á°ï¨kÏ€‰£YKá÷žæ¬XñÀ‹Ï€z4AžÔ˜J²Ó½ ó€`ÛSÜv¤#Œ6ÃèÎ’³rÆY"='Æc÷‘—XÔ}º‚`'¤fqž R‹ Jsõq ®¹ð0¦ [‘t%Þº [aXdŶÝCVûŒè÷¹âÜá¼’y‡Ôy>þÅ9<)Ç#ØÇ úÚ¹5•w ­í¢SCÑcV~gn'Ä¢¸Ù:@Ù¡Tܵ¿mt²;ŽÀà¹ÌôU4÷Îî>áºï†ÏÎé\°XxAUÞ.3à—Çm&3œÕŠM&3êÒW Ð:jª81Åj¢> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 105 0 obj <> /Contents 106 0 R >> endobj 110 0 obj <> /Contents 111 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 28 0 R 35 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R 95 0 R 100 0 R 105 0 R 110 0 R ] /Count 20 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœY XS×¶>rr*ŽÄ0ˆžƒ‹8Ô «8‚8TÅ EQAQ "3’0mÆ„„AXEÅ¡Š#u.­Z­ŠC¯C[¼¶õúZmo»NßÎ}ïí“Iûݾ÷>?’œsöÙ{¯µþõ¯m%”}J"‘0³cã£"âÅßc7‰0¨‡0Øn„ßÇHS¾ÞÛ{ö’ ^v¨—ýžACæ9 Qýáó¾Ò²“H’‹ªgÇÆ¥ÄGmÞ²Ã}xÈ’^#G޲Þçããã¾!¥ë‰ûœˆ„¨ÍÛÝß!?vFDÇÆÅDlß1Õ}6µÑ}stJÜ–÷ðM›"6‰¯-ŽØæî»Ó}øl/÷ñcÇŽM>ÆEÅlHLpŒÝë¾È}IÄæÄèðø?ܤ(jñ"¿”íWÎJÝ´*hv\ÄêÅs>ˆ ž¿ù}ÿ„-KvD-—¸lþÎm!IÑáË&ÇlX1ÜË}Ôè¡ïŽ;n|ÆŒ ÞCß›´v²Ïº)SÓ¦yN§¨ÑÔPj5µ˜šCùPïRoS¡T05—šB¡<¨÷)j*5–F-¡¨qÔ;ÔRj5žò¤–Qó© Ôp*„Z@yS^Ôrj"5‚ZA-¢Þ£VRÔ,j5ŠZEQ³©ÉÔ[TOJBõ¦úPvT_ªÕŸr¤äTÅQ(åDM£œ)jåJ ¤|)7jµ†Lm¡X*˜Ä”²'¹.™!¹ÜcJoíÖÚÝ·çì×Ù·KIÑžt-ý‹l–Ì û•ÙÁ¼xkî[×zz÷<ëåðU¯Ý½ÝzWôñè“ÕçißÒ~Îý’û}Öß±ÿöþà¸Êñ‚ãË5ò§¶*9±NùNíÎCœ=œç:G:×8ŸuþÁe©K…«½k”kËÀ…÷»9¹Ís»1¨ç ©ƒŽöŽwGê›åaª0Qá+Z"ŽªåqGl™ÕȳÕì0(£ðzÃn#?µïÃý ·Q½/ùÀÎÊxÃ`%³Z߃®±l"•‹-ýhîÕ ›“½Ï’M¤ñPï]<ãå'„­B/Eø±åuAˆÁGŽÂ.xÀcÀùæÍºc­Ü‡ÆÝµ¨’)W–g«ò ²UìÊÐ… ³ÉÈ3ïÿì´C§w×—pòÛ©Eeê*· m¹‘“Ÿx!“ß®U—ïbå'ÒÕÙ)\j› Ï‚É0|ûƒ³ü{x¼ “DE~¯F¯­¬V–¥ŠÞíÆÑ)ÆkðXX#m½[YeönÑ»"DïBÎÄÁISiù÷Và8[£A"8¶‚Þ*]h¸ŠØ}œÂ¥ÒÝè¾Ë } ¸¿‚é ¿JËM‰¾6xô²N%$Ö[75œÆcðk<^K‡‹hÌÈãQoJŒ²BA|[o~;™óu4LÀ¿J-ð Á‰ ¹ÕOL‰BÞŽüP„Ø úý¢ýö³/ÖŸt˜»vøÀ tžyê÷ù¬‹]¤y¡€˜,»w9ìýÀ uïqØ×+ ¼dm¨Q}$ùÄν›Ñf&péª9¬yá Ð$ˆ£¸ðq‹d!–ÿ2ŠNÖfWV‘ ²¡D†/â拏…ž¯d$¸™$²¬üÔR’Èp¾—š±%HÌÎëIö~A˜­ÀŽÃzü#Ûrfßljƒ9vp´ ˜{¤t^WQ©Ô<ö¶z¸‰ÆÃq IÒÄ ™/Ù<¢ ±4ÌÃ{¤8jˆÌ š+h†Z‘ö3·y;!ßI06ʬàöi@œÌȵ›6ÒøªP(B§Z¶PÃ]e‹ÇC7¢6ÒpÕTØ-/ùœL/”(ÖœZ¼>š‡¾6v. b&˰ôÅ}aÐõ¶ýgαöWÔ =cÈÖ)U9ù* ²påþÄÉ==ß@oîŽ þþ ìž?^Ț•©*S¥Õ“4²—Õ¨;©Ý­ð.D²# R¯Àƒ@j”h‚fÑ„šrE¸V¯"„ï®Í&\ëÅô™ù—b"úýð8pòÇà0úîç»2Î9 þ—oøy™êë5|Úý—/¿jÿÑ‚˜ÅÃ^"ì†P:_—~dËÓ©­#‰<Çà>Øé7p{½Î6ê³õÙšÜü5·qÛ¬Ä@„)4õVƯ ooüþΕWèjÞ=–éLޏI’£G3\i“#Ç ÈjUås à!ø‹<ÖK¯bž KÖeWUèHÖ@O8’'ÃMë¥òä`YwLZyÁÛœ"÷˜0vÆ>ØÈ7p0ìÁ¼ÁS ÀCHªó¦±< ˰Ó£Aîàþùb8kE&Éû­…3l±KƒÓk2åPî’ †¾ö؉ű¶XåM‰ !Þ–]D'Òmá7ì_Œ–3³dC' õçh¶ñð;qIÊWÂ:èPÈß 9<à ÷9;`‡Œ>.7Ü<Íáâá²TK)5‡WþÒh p2'µ\¨¹`¾Ó‰ÚÑ´ü1ˆ#jëpÏé$¬Ú⺪¦"‰i%¤XA¨M… m£ÏŸ:vsŸ›€=«A¨1d™r.³ˆ‹.ó×F fÜÂ峸 oYwaýwxÙðñßHšw–YÜY§—ðPlqé²®¤)` tGû÷?Lº3”Çl×”hËÑt·U˜¥!ÍŠ!PöèæÒÿÙK¼¹ND-þZdz3áz4ÿÆ'59ËÏ“eK{„Ñ)øÔ²à,ÂlW†ŠÀ, {@„LÞ §½èT­²RÌ;8“Ÿì/v"ÏBÀ›®Õ”g¦›qzžè Y—G£Éê³IbÞ†h‘ë1w™3ó>îi¾²dæ-B.:CU–È‚þð-?ñzœÚwÊm/ªËÚ¿C«.Î/½®¯ ^O­â>Ø“¨ßŽV¢àØw'3‚Ïç<üpzo>ð-ÏÍ5+ÏrÃö£GcË~ öw/¾y†Ã YÀ²¿E[Q$£[Ññšc'ŽTFTx_¨RÈ |xXm°ÛÐiÞáµ›wp ‰Çw}¹ëNf¦>ý`jÍ6´…ñ›ò.ÁüXäךw³`7©Aˆ!ÉIxSJ$ª¢Š,Ç’É ¡üµ„ÛnÐë›Û¯Zw´Dì碱º•úm\MBMzj`n´ò¨-lq‰ûeêJ·Ê2]-÷³Lì¡ÍÞü3MÈ ó Gà´Ì±ú`­¸_D…¨1 *¬"à"ŽÀõqÁæiO«ÝoJ £“T]^æ¬Y°Òè.ÝÞa)g6‡¿q–ï:=°v¡òä(¡©16Á¯¢å§/]3ÏmåFþ—FYØ‹äé´ +þvóÓƒ/³6]Lmùû¼¨X|fÉÒʳ*ªÊˆ[6­Á´,ÅÒ¯Ô²÷`ª)qè¿—ßVÉdA‘Qa±©Óò»VM84sVÑ w­¤Aî‹íà%  Ø`Ï`ŠÐKÚdC¯#­€¼E“N¬CšDc/èÞ°¶÷¶ƒx¦™zI»ÐŽb3®Í¤E[„C³Ü³ažâôIÒôΕÆÑòÿ´îÏÙ²?%±2ŒÆa=i_¥gh¹Éº–“u­½4öÞRÁa”ÕSLÆåÖ/-ñ4¼äµ¥UÉ%þj4ŸøT«Ë’E:IQiÒ‰»våð‚?wî„ñþ_>gáEûš”l2ʬ8É(lÿÍ‚]Gس6nôHð±ž/ÖÚx±‡uKçi|o<è¤+é‰X7îI?ù÷ô3âÈVú9^`ƒÅ£B¹BXÿZ¤ëY@O“ñ¡.œÏáÁ…w$X¼f©Â$§Ø@=}ÔÑw>y7rAFÊ26+G£AYŒR—m(*Õã}µÇsïüÖÕÜYputÙZBÅý'{sòs3?_ñýk®´±§ìç-Z.¶ø‡5¶^¿óño-g5Êãfìîò-f~°ÞꜮÐ!¥‡ÍÍhµÊ”$ugn?¢qˆ)QšGã !QúÐ|XQ]–bÒ¹Zî0ý‡>bø‰ìÅã{/£[ßÃë\¿µæÏRÚ–E¶Žh‚xXí²[„\e•9‘4ºÎdNÑø(.’BÑ}ÌøÿÕ¸.ì;¶Âb<™l¤/LÂï‰ÐO`»&Áx©––?ì>»Á+·œ¡ñ`ì„‚“ô´9ŪÌ.±³FVK“ùÆãE$}²iùsk+àjÝEMRgŒÁ#¤ lÀÅ:"ÛöLïnÌhÚø$äCÕö¤6ùY›ŒFÜÑ}Tc> 3t¦iA‹œ¿ròHý1·ÆÃQ9|»ÝæìâŒøü䵆}2Çoàäeø®í€¢à}m͹÷7lݹj›xvÛ¾õ(mO‰ eþ|¤}yÒ¶Âd‹4ê>P¡å™&£MÙxùœ–·Î9óiÔ]7èýê5ô‡~£^á¾~˶ÍYÅÿgŠŸNyÛkªÏˆáÓž¼~ýÕã;9¤î/©†Pð…P;èëÔF#x€ÉƒÐÝg¤u6'xìhù†^bŠÃa4pƒiÐR!NúÔ†“=¬µªNt;ìm³Æ#”H­N›ƒ{lÂo!Oäqôí[Ø~_D¹²1•å冒‚¢üRn.ôy~C¿5üò%ô)*-,B%Œ!»<‹5Ïõ™“¹²”ÙY¹(¿8‡û÷?ä) ¸Ç¼íûäå |”Ëd•gôZí‡5ì|=ÑOèçmÿ û'•åJ$º[œnx(æà₼‚<”ãš^¹kw¥¶¤ÎÀÂ0¨Iˆ”l‰OZÊî š†YÑK C½C6ԈˊãË•µÈµ•ë‹ë¡Ìå'ó]½²Æö.y+}ÎÂ[ŠM1±›b>âôôÉÃOžŒ>ažtçï½ź¢bTÊè•:e¶:/CÃâ¿ýkQ®º åº*õÙz}ii¥VôIz“¤f+¤Á/ ˜0(l‡ífb O`=‰æ{€)°»@ÁxóR®×Þ:tzãÑÚfÔŒSöF5¬5 …ÌxZ˜³61**)œ@sk]BãöÓ)·ÐuËùŠŽ­x_8(8-7G”d3J]±®•°àÒ,5UbÎæxû®Í kI ¿8 |$i£KÁ™WË®œýüjÇ·Œ|=áŽÞu‡>ø|â“KcC#Ùä„Ì”Âd—+õ†2mU [ýéõ–[ˆ¹ÿÅbÿ°äØÑc8œ…¤Á2y°0øm´4Ðɬ¼t‹Ø=ÃaR;'+PÔŽ¸myÉùê”ÁdV_l9}?:±¿þxQuQ¹U¢ªì²ôÂÍ% õè8Cž][w:$=›4ÚÚ\c{$wß(ŠÁä †¤£ñ[²v¢ ¢W„zÒ=˜ÞƒôÜ¢œâ<äªAšÜNZL’Ì¥¥ÅelóžCbž] ?tÞ,¯÷Cö·„rmN1"+;[™Y•~d'w~ëù´ ć¯ÿŽ`ïûxغHÕΕ棅~üïÍ Z€ü©â¥ ü¯hl½Õ¹ÉV^òÁ²=wÈQä ªݨi;Ørîþ£Ó‘óËst9%¥ˆ1hË Æ-»×/X¿vç»hËD„Ï~Šû@ÏW¾êÍÌgcç„lôÙÈ–„(ê[ µÕ»oÚCªÆ+ó§M\2Ï/`å™§q\n9*Ôì3C¬É¬„DßošØÛ¤/È-Р¸çˆïajØÃI/î^hz̳šKŠ„uY)éiÛ6o܉˜¹Kn~õÍ•í·Z×M:À•d£‚²æó¯þç%‚à«ø—ýªßíÍž˜.ÍÖJ¸@îoŒlØn¹èĉèCf‡“â‹»H…™ûÇ S^RWn©0³àÀ_¾}¬IÄg®5î;p¤ÄX¬-AULE¶.}YøÚ`Vä¡Èø„͹i*Ú…2tšÝùÇóëãQ¤ÈQÁÍk/U‹Y]š\Än+Þñ!j9 ñY{¶î?Zo¨C¼hð](UqFi~i~1r-CÚ’¢²¢b"‹ )•å–)Kño°Êȇ¾´¤é\QqAq>ùsÉ+S— ¢Üüœ<¦«î²ÔÂ’®Z¨Ó—tU=ðæß4K#\&åTWU% [S0šŒ ÊÈ Áøm›Bl•d}’ꄉu¤½ª«£?îÉ;|ܫ߫7Eý[qU] endstream endobj 118 0 obj 6311 endobj 119 0 obj <>stream xœeW XײîºiQÔ™QÁÄ— €ŠŠ²( ›2Ê2,ÂÈ’€ ^£pÀ «@@AqqÁ¨ˆz½^oðŤzrðæžf0÷æ½ï†nªNª¿ªþ*D”ž%‰Ì\ÊD…*bsÈt§Xe˜{¨2bÛv… ™Æñcuøqº‰8æ÷yšzå–ÿÏáÈH镌ÑoÊ›šÀŰ$¥+%.tŽK‰Ø®’[{{ùÚL:í?±wpp‡¦|”È] [cäVä!Q¡Œ‹VĨʉ¶R±Y¾U™ž  S„ Ç|B”Š(ùòeD\\l¢ÜÚÙF>sÆ ûéäkæÚˆèÐí òu!1 r7¹†|¥*„Xù‹€¢¨én1›×Ć9Ç)Ü]<–ÅoMWE¬sݾ~eb”·2$:ÔÚF>ÍÖn†ýÌY³çÌGô)wÊ…²¥<¨eÔDÊ“ZNÍ &Qö”µŽr¥ÖS+)oj5›²¡|¨9ÔÊ—r£æRk('j-åL £ (5œA¤L(SJL¢8ÊŒMI([>¥GŒÅS/EcEŸ‰ztVè”éŽ×=¤Û­7Wo›ÞzÝÉÈ%“«?K?F_­ÿB_ÃŽe½Ù2öê°´aï ¼ Ü5è4øÍÐÐp’áÃ&£1FõFï†ï56…3Æ< 0á{ŠD<§‰æðÛ}ˆ¦ù}Ì—`BãwŒ PÌï#ò˜¢ Ëù:ëÔÑ gî@[~oóµ»Èüry\ÀŽŒ´}Ûe®øAÔÿ:X˜]€ Ì»=¯Ï\ê·Ú[ZÐKóÍ*Ѓ~Ðu¥.ÿÞpJ³rÐ1ÔøiƒòŒ¢>(ßMGK£|Ý<–…àahò+即 mMz„€CoŸ4‚‹-¥Kdœ7bÝcÎÞoÎûºô²¬âëºòVTÊvæ%MÍÞ™Æk¡b ñ}¯”ð`ÉÀ4L`ÀNMãXhKâÛh­ZIø?Ž-öGO{Á oíùKÜDdì±(ÀM9Ía±ñ‹Å êï=7¥M·«~F/Y9ï 'ÝŽõ¸›ç‚\üÓ|BdÊuAJ_âl˜úˆŸ¼-ÓšŸYÌÏ&—Ì.2}–£ÅɼÁNëœYsU-!e+ùää»Äß%Æ9°b56½çðËÝæ²æKÒ-¼)×r6x6]Š©¹>êoaøcÐùNf¬Ù¢‚Qü!%ú lø±0IWsšOäTdGlj—^`;ˆ]»Ç»®‹ö’‚®ŠÄ±q9“8æø„«ïé#Ðy)ª˜q}`@RÑAR±WHE'$r¹êè]ú´)¶v«Ú³Ô -F •þ«]ƒHáLfñÈ—óÀ†÷w€n«K™$Ep|b½u­9èè ¤÷q8ŽÉ©:Z—_ZÕPØ€jPå§y±GR³w¡(R?…Aü•€¸ltùM ‡G"«ëÅvØa};ƒ.Árõ£úÝKéÓ7j0F`À‚«üW¼ZšŒGq?ßX‰uÅÍÚä.ó[æþ b­ÜîèiÅ«‹7eWï}Sû‘ë¶½˜&">¹«Ë ꘄžŸ{Øÿàíy1hÓyñc¼RŠë™’~®ŸO a ó½:xÆ¢m#¾‡×áN;ξvlŦ‹\›¤›±^È­;ö*æY¼DÜ;)/]eµIµ6\yôD²4-wïñÌS,ÇìOÍÜ‹ÒÑÆÊÀ‹ñçTM»o!>ªFÝ;7¿L6µbÃÉ´c¨È¼ª¦¸Y*îk­NŠÚ›¹/#]6ÈÕƒ|7ÈÕñ¡fTËÕ=Í×;ySyœ¿ÀÕ‰ÿŸ«Ÿy6ÏvYëæ#m‡RÚ^ò®}"~&_Î-fÎÀ$””x½œI²¥ï3Ëð‹ýiih‡ùúåÃêÒG+¤EoÓ2öLÍU©ÇNÊÎ=˜/»MôSÆÎ ¦“ì?"‡¡OxÃ&Â+)°¡®3}ÖNÜt°ÁX?Ô„ð¹ÃÙ'P ”mž5Å1ÁÏKêå¾¹ ùWƒÁ0ám’DÜ÷&¥2)ÐÂÕ5Ør†ouÛ¡¬œ¬)¶bö'eîFiȧ(¨6òrðýÄiÕ¾šö»M_>Dß ®õ×'aQ‰DüpzaÜõ_5¼yÑãýyfzfºThÑb¸Mjÿ0Aù<ç–áçBÇhI{*ã‰Ëi|žiòkÊŸýÔ Ïé?é+’¾Þ ôù:ÑøÁ§ |}W!s¿måWNÌNÉ:¡[-0Ú—œ‘‚R̶û¾zÔòeG‹4uÅ¥n¡±óý„kû«G `<ƒ¹~áÖAÌËAï¿F» à›‡úðÍ EpÚÁdѼLx|DÒòQe0_¿ióó±–êaÙ½[Ïðæ“»àï ¡4žÅ༅<ýä˜!LÓÕL&g' ¤'ÛûmžÌo¬j>™}âÀ²÷|º€Õ@Ìß"÷E¢íæ˜ýq)L}Õ[~ù¦´M]õUÁwDEòg ð¯¿:¡m@í5½JæÂ>˜ ŒÐÒ|.5o:‰gÉ>zš% ²O¢\T™R£*Œ*÷Ï]†°=Zà‹ÍXÍä·ž)ê@ìµ’щiÛ“vË’v!ä¼Û3]°gKšr»"r-rGŠEe¾‰egw_ÓŽ®œºÑ²?}ŽþÆŠ“Ñî콇v±0Á# š«R‚VŒ ˆÏ¯nª«¾s\võèŽeÉ:˜e~<û@VîÖø÷áÚh‚µ.8ÿ!Þ‡®“ê íȼ¾L¾3swÆ.ÙRüÏ¢oÕço#óºâ¤­;3R÷§ÉœqßP®$AÚØ·É~…w¼×ziÚÕ€ª5h Œ÷X•ß#ù¿[¿WÀL ‘ZqòGds[i±Ú§äBüÝ1@XœÁ L–÷Ž_áï½QÖ ¹t+V ÓÚ‰¢°uÓÚäñà´†—¿"Ñs0Ô…ß…|yáØyx-ÂÑ”rL]ÃÃØŸ™`˜˜ö}À¼$almGm냟ء³°›~†¹¶;-j[Ÿƒä EoãYÃÙÉLž| Ù",Ù‚%ŽxìÖÕQŸX’ógÈ'©HôXqJlE¿dv¬Ø¶f×ÎuÐùâF[!èTÂ8mE‰ ƒh‘úµ­’V¬q&×h6røÃ%}—¥ve5枨‡€„íÃAÎaÙ­Z Bìx ²bñˆoÉŽÂ~߆­ÒÆ7õà„úYáôIãÈ6Øt:röˆ›&‹µŽó@lPtõµŸ®@|u³¬´¾²°†ÌÖÁ® ïû8òŸÆY¤ äTmËlžç° £$[‰ G6…ÏÈJħÀ²•€ ‡›™²ºÊ0|üÝõºÊªúÆ«§n¢Û„&?éÀfØnÒt¼h›,˜¼ŸÔÍ÷ÛRí”!SFFª0ã‚eóýׄc Ë'j»Ëbþ‚È„»KËÉ`=Züê “»‚r2OíeÅ|íîâ˜P —õî®^QW2¥%ú™Çh<–Ù§ÊHEÉd?^ foŸUߺ&m¸Zöu Õ9ÿÓ¬ø}÷•†;Ï,~tjÅâ¹Ëâü‚¤[6§lJ]ÌÇäœ;\x,¿°ªš,d¬º*&t‘‡¯½l§–·`,°ülBj]PÎ-eúxW;0øýÀòxl@“)Û  {ú!ƒ=å°ÙÐ6”K(½÷„<¿†ñ €§á4~Ü€˜ÁV2L™’³u…ˆm¯Šô´rw\à^vîPÖѬY!÷%‘ᶃµî^Fý¯.v·KÛJ¢û,è¸ÞÇ¢@ÅNe ´¬i¸òÃÐë)¾@Ê瘑mÌRW£Ç«9²ä CìËÛ1-Ÿã=~•Ôgv vCؘÅOaøËî†ÍÒK7*zÐö·Û|S~Òü[ÙGòª*:ëN·¡ËèTJN‹'`oîJuø[÷Oœ6„×]}»ë‘L[J7„]r¡_†v:üÊ`_ìaƒmâÏYòÿN3¬{ ã`2Ì{”ÄXuН8?à´L2ú„‘Eýàø¾ endstream endobj 120 0 obj 3524 endobj 121 0 obj <>stream xœ]WXT×¶>Ì™CU(èÌh"U° ˆÆ¨T,XAzÁ†˜hd£16ˆ(MQ „" *Dƒ7¨€IT’X’k š˜,ëÌ]ä~oŸA}/ïûfÎ>»¬õ¯ýkm cdÀH$’!þqI™qéê˜(×9š¤Xqhœ`'F£ jt7uZé(fÁ‘^sbfHÌŒNŒ´635SšM2[`vËì…ùBó;C-’,ž5ä4TY@ÀR¸_"ùœ ![—Ì£óßÉÂN°Ä¿X ]<)+:! D"쾤àÆâ^]²§³Xðw²\٠ÛÀ’Ç%ìS¨•Z­¨é× ¥«B F£ÐT%E7ÂÚ¥y²d+ãbCr¶çïÕ*ÆÈJ?*È;BÎ’†}Ç~Vt¤¼ü¤éäÃEëJW0M)±êD€ÊFÞÔAw¨¡=2yϦ†®ë•kæ+ðG:ò_˜t¡lNHêÂ…¼é[µ$¤ðH%g@e(Ã/ü6>ù;´#Nu з÷˜6¿Ü¾P‰¶¬ÖG“ºŒpÞ‘7^\>Ø|¤ZYTYUTG®‘©EÞ5êÍŽgé‘ûè®Ð&\àÑÓ%胾}ÈÀtðè ̆ùޝp¦2¥|Ï%ßÑö+|ÞŸµòvÿåÛ÷”úŒ]K…ñ` SJ¬NÑÝrD7 NÖ Ò%£ ØÄ¶ÕÇý©©¼E÷9e³Ï­PÊ[VÔÜRß²ë$ŸmåÐD0翺èû®S¨ŸOh÷6ß¾­´Ð'¥:ï ”ÑÐ îPÎÃF!BŠ3XTâ]t‚»RôbaÇ+g™>Úâ‚.p† âšýÖàLÍpø€Å᾿“ÁYȉ‘VÂ>&9O'êÞ2ù[ê’êƒïL¬G µ7‡¡æà£\˜¿¯J´gsÝ-w£S8¿'0¦~õ˜\iÔ&žTmØŸu VDx=…äW0¦qÿâ *M"RIõw9ËžYجé"Xÿàq/ã^Â%¥¼çr‚ÿ™yv~$4E½Í ,!¾¾>¡]}}ÍÝJˆW0¦†¼’¥Éç½diÛ~iKm³)ízëÇC# ÀÙOFƒ+˜?ë~Y¥D››¶’ĵG6Tn)Ùql×enw/àÁÙú/)y«³K4…>ÉÜÏYèŠÓa˜°—âSG³Á´Õiù±Ä]è?Ï'~,¡Ç ¤æÝ›¾~Ö<'ÏɃÓ]Ÿ½$ýØø¿æÝžß=½z áòqC¾uÀIè4ÝÍÐÜ«åú}˜+ŘÑô\–’µkô k¨eÁÜ®hiÿ¢«êRFI,îi÷=ŽFÔ;úÞy §Ëìd ½:§â”à(Uþù“o‘)ò^4ÛÚ׬ªô·Ck´@ÏYe³Î…(Ï­ìTß ]äÒ©ÓíҌɛtyt.B†Âï7f•oà‡iJOî}ú\ò³˜9·„ Þ£•=YðiEAQÞ®Šٺ݉ùY„s ˜ œ7ËíÞ€?¨ÿ‡2=¡a;X‚†º{º{PÔ¬L(äa! ¶?>G°÷øÇQz‹DeÞˆÎUɾ¯I|v|¢—>'èúÅôãI÷øN\¯…Nþõ<s°8Fõ¯~+Y a5v(õÇF^óV{êdòîõõÌû¨XcdÛówd> ŽJžÃÉk^RåLùù…ó=œT~` Œ°‘߯Ñ]ËËês8–Í*J9—ܘx=û!¥¼û“o@®¤û|N÷ÀO(Ì·¯ÌqpZé?' ¬óÙ_¾êRê•“&ñ ŸwD?ÓE?¿¦~~MÇþKý; ÇH/𠬚©…óÀ-EWû„Ý‚¿ ØÉÝ8–rfÔ4_4L8”Tœ©,Î:’{%¼V —÷žÍ)Èɰ]»a^Bʾ­bÓ¡}xŠNg?F“‚¹K®ÕU×U»DZÉíÄ&ïRô¹<<¶`Û~RÆUV—´(ä}äôúüî­ÞS9”tRk–SŠtÒšD-åþ˦ó•Çr2ŠÚ7“DêEŸtj›ã«’*‡_²J»ˆƒ’A=ƒRŠÁ°´¢õ Š`'¹ÙÐÀÃûìÍ“ë‚>$Ûò¶(󲈖ä¿3Š9˜z‚M/Ì=LªwÂÚÝêT×·“ŸÈZÏ­9VH¸»AŽM2¢ûÈÉÑG7qòG7HSÛ’ÄmšìM7oØI^˪ò©1ë`˜U8Ûȳt¡ÂH›(ÞÓÙÑÛÒ2Öqò¦ˆ„¥ëÛ¡ù”—Ôà‰=OA~åü¦”ÓÊRí¡ìÂ8F³ZpF‹§èMƒïàxÜ€ëà]œ «žÝ:ûàªRžuÔEú3K3Mðï•<¬OüLK ÊX_·nüÖõkÕñ=û+žÉ6ïÚž·pñ¹‡š•p‘–L¨¢QØYrƒ2§S\£×]‘ö¬.ÙˆnB BKÀøt0£ j&i‚w  Þ1ž ó¤sÓù„º¶é§¨vá\{OƒÒkKÁQ£ükßY5²hDPpü„±K¿Ý¡ ÕàNx5 ‚(¯ácšÂ²Å×Ñ®B‰l™ª4©”´Žø¼ålÏÓÖ˜Ù{ƒ!í (~2X¦DeýñøH_ª\°ë¡BìaèÛG2}5uÝÑW}‘%ÔZ 5Ô_¨VÍò[˜\}SRÍdhzËdß´œ¸^§gÍ— rò{Šlã…>Ô4}îSÖ ô‰)$&Î1ÏÔ¯{¨ÁÜÒ#)~kj¤èÈ¢d@‰Æ‚Rü ‹j¤o&ÁÌI»w(<ä…wh¡Iï#»ú/à1W ,®‚Ÿ`DJqò`&ô•è…°J´,Ÿ<+›åêÞðX!äÒÊ+#že‹["ZC{Ö=&IOEksËù²/ÈNÈ•½µ|0΢åúcUA:‘*5Hõ…`!Œ+=§•p˜œä Uï_E{´Ÿˆ–¹ °c÷]-?ÚM¸¶ÚmîÆMÛ•YÛñÍ Þ6\¥]±”8“°Â¸ã©”äk«.f5Û]'ŸW´\æäYD³g[áFƱ8’yyÓô˜0ïÕ)ÇëÊJ¾(P4¨Ù÷Éî¢ý#,þcþ¶¼Nzðc#o¹þzÂû·ÎÕ]¿x*5Rú‘< V²ó¿DÆ/t}p”bÝåe§üI ‰É [ÊÉ[:eÿ¿)kÛ”µ¿Ö!*º-ËÏ\Lë²þ ÷o2î$\QÆ·œ þ$4M½Œƒ˜à%¾»ÅÇÁ)ÄÇ×{uWÿ³×åO·Šò!B„Z†³CQAЕ [3*€>s²£!Z FØŸö%DroVYàjd0»\ ¸…‚é3çÀ¾Äè3´¶£A{5²£Q\UEÿ6”Hn€=ö?~$#~©‰‹¶rÁ²ú½'(­«ú´†|ËÁ(ʃ!ì$pžòxíพÕ`/5âjg´$úæRÝ\jI¬.œÇè¿“¤,Dë’¤z¾˜ ‡hS,¶±±"*t‰ü,2omä¢è¥YŽ"hydÒée þÝqIé¨:½®­äWà Xo|¬þ<ªË·Æƒª†Ôè9µáHü§ÉûgRÖÌØîµM³1*9=†ÄMqö¹Í•Ûï’'äáþûO5V«£«Ï¢ÄÞWÔ¸\ñô\a3?°™¶Ñ«Üdîs"Ñ‚¶âu@ì—´ÀÒÆÌÂÄ+ÁñÞÒÊ­-+y~†ü|®üFAq}ŒNè0u :H¶çç(À¶œý®æÂ/k#½g¦%á4T ™gü‚І2SÆ RŸÀjš3ðTh¬/¤ ·ùr ïØ‡œ¼÷â–Êè¶áA µßìTàH6ÇôNZdÁå÷Ÿ@ñ0¡Íë3¥¼ïVý‰¦›¶`2ù.Žšî§Y£Ð$n % ¸­`-˯ÝS¼ïhaUuq=á®V«yG'x(·JŒN %þ5xå©àg þèI¯k¾R4œHƒ4ñŽ ×Çc põu á¾ jG[!ºv³n?0„]0 ”‚{øluq#á¾j\9Õ#b¥OºêÚGb_¼‡>q§œ¤üü9íMÇNümV'nMŠV–ƒ£.¼§ î—6P’Tƒ’`Õn¡‰ÿàBn¹¶"þò¼Ê÷Èd276Ò;Ê7{Až ÅáYç–Ô܉}D7ýÛKpƒ‘îOÐbqlvX„rÏ]B>©,¼QW{•4‘cÚ£áœó7VΜ¶4p~DëÝoÏ·ÝT’ãºèÕqÑ¡Tº_²‘aÓ׆¼›æ–….be-„ôo¨ Ì€îµÁ›ŸBf &Gs=Ìé7 j¿à*XóÇ í;t“ÿ~êÄ‘Æ ¶`èrmp¸Ët4ò;yC­÷{F¨—¹Û¢m¿8c/Ø>R·yžW -dðòß¿lŒ[º,"6(0â³ÏÛk;”»ð /ïïn ›íîçÙtûëó:•éåBX"BËY01Ѧ`rÀÌ F43g˜ÿ6ûÌE endstream endobj 122 0 obj 3896 endobj 123 0 obj <>stream xœuW XS×¶>1äœ#*éM"XEIE•@APp@d™ŠÖ©ƒl­m¯¨µ*Šh«ŽePœ‡ª€¢%`P ´QéÕª÷®“»Ã»wŸàm_ß÷½/ùμ÷^ë_ÿú×Ú"Êh%‰Œƒ’Òó’rSâ…;GÞZįřÿòÐJÆRÊ}¿˜"121ªóÁss>f$\#(±HT°}¯ïê¬ÂìÔä”\…}TÄB‡I“ÿ|ââéé©XQøß7 ¿¤œÔäLÅr‘—”¾:+#)3w¦Â—|žžš HN/ÌJÉQÄ'&&% âãÓ“Ò©é©YY«óö¾ ×)S\&“ƒkXjÆŠµ9ŠÈøÌ…R‘”¼6=>û/)Š QÎ-ÌLXêS´:1&Ì7+)ÜoÍÊyþÙÉ9)¹©‘AkW-ÎK‹ÊOþ¨ cÅB{‡I ÇÉ6NÎS\\ÝÜ=O¶tºgÜŒ™v5™²¡b©pÊò¤œ([j15ò§fPÎÔxj>@M¡>¤"¨@Ê…š@ERA”µ€ ¦Ü({*Š ¡Ü)*šúˆò &R )%5•ZD…R>Ô4Ê‘Š¡Â(_j:5”2¦L(eJ™Qbj85‚I™SRꊣ,¨Y”%5Šò¦FSVÔÊšC¥P‘RF”Œ,õÕ%²Å‹v ±¢òõ×b¹8D\c4Ìh•Ñ5‰X+Ù)QÑ"Ú‡n§y&©dÍÙÏØ‹ì“¡iCK‡¶5¶3ö2^n¼Ó¸}3lì°³ÃT&f&~&e&ÿ4u6]iºßôŠéuÓ¦z3'³Ùf1f'Ìî÷~vø‹þ#F´¤FÊFî771—™Ï0O7/cf|3Rë˜2Q—V suœÓ@£ÆzÚLçŒÊ!VÅO)ñ™°š{„WKÞÒx§.C‚Mh¼g CòwºVKào*Ó`O%fü8S­û€ŒÅ—søC½#¶â%®4øë+Ò¦¯_Y°.þ­Û^¸ƒÃìÛ´ïóƒ¨ Uï<øÝá{~wÖéÌF f•óóÛSÊÌÛ:¼ŸÀ—Ý–ÒÓmdBéúò'p†‘¶ÿóJÛ³»g”2üïn^Áô„\™à“á³T&=­aˆ¹¼c‡èX˜Oƒ×Üç6ü_ܨ¬öA,猰öêS€ Œì|”ʧÑë¦-÷C¬ó‚G0,/©~i;·Â§TnBçG<²æ9(`1çá:u,¡á3 x—ÞXäz \…ÅøªáÃÁ‘4ü71ŸbñéxÒ‰˜ïü‰‹´ßã ‚˜‹¨zË¡‚Ãùßf ”¸.}M~vAæ–…ä£ üŒƒÏœGG7Ì!3ÌÝ–l…R>I[›»6'cÃ2Ä,Q;oÖ.âÃIôÖ>pžÆwõ&’9¼w;V1ðoJâÞ<øÉhîÃ!þ$‡í\m°öÓØ€Øõ½ƒ9ìþO’oóæž_ñÆxø|/g§ˆ‚ùU•V.L¢ZPÎ+Uà]f~¸.’°ó› -§ãJ:¥9òˆàÚW,Â3åÒFì߃E0îASÕó2iqÀCññ\ßÕ™x¤ðÚtþL7ç°Ç`f7ÿ"û#P¯ó–PÇAnf" à*!° ÇõäY–1Ó­üüg /ÓˆuVÄ 7½–Öäµ’ BÄ44oª×HÞ‘@¯ÌUóýjQƒV¬›È#.ÜUØ9 YéQáÁ¾É¶#lR3¾ÕïÊüY/ W/B;‰Þ³1µ83#<4Õ‹x÷¡3°á``ÜõË…«Ê«²÷¦ïŠb ððU$). Iq¡ÇRúÃ%œÉ=Ãô¹¨ÆœÄ‚u/ˆ€€Ó~0Ëý"ÒBINtz3Ò;P¤ä~½1ÄÃÂfLq›ßÃaøµ.ùj÷Ñégb¾Ös¨ëÓ†âºÔg3/:³&L&)3ÏùeØIw Ðå$er–$¡h´¬rõ¹ü£[Ž–\d··pßô_¿ý±êÛ[ÑÖ’­$… âS«å÷ìGë 9œ„m°.À@ΰRÓYÕt]Þy¯$†±ð –Ãd¼Jæi®¿c¬Ä~6ää„m‰ÒAÐ[rr’ QRó·Ô¢^-?F+îµ€ã4(A2ȇ¢ëÁSŽ§Öƒuüµ~;‘ø2µÞ‡6ƒgÄL ñ¾•ÊRZÓ*Hlµ ±‡ôè@ã©jVÚzøÀ¡¯¯mcŸ2ë¶oÙ¶E¡i‹½XiÍ "³Í8³ F ™O&©îÓd–Çü2ÍlFÚ+~Qu5í‚YìíQá6F^weÍetýx¤þ.›Å ¿MËó2צ//\„’Pjiîþ‚½›¿ýì;•Þiß1†#ºwèø™S {ï"Î’ÉCÈĸ ”ëkž-ñȨÙnS$䊪—~?ýÅux­Æ¯‰ë µ~€×Å¡¾6‘ú›q%o¯aSÂs³YʉžgaD†\Upm]EJ»ÊgYÒžƒy²õ{?ÝûY ëFïÀ¦-óa,I¸QOî¼Vŵ=$Ÿy ð»Ìƒ¨ftÃÙc-÷Nf.Ø.{_ÇÀì2Q[7)câ6¢"ݤ„ ¬ªjSq™¬²¨4­d ™FyùùËr?Š•ÁÑA ‚…z`®Õî›Ño)}a@¤} Ý]“à"—ò%!hÙßâödŒIR¹åðÖþ0)89ë8tõ©.t[ù¬Vl+ý¾(Œˆè¯%uáèæŠüCy{V¡8Ö#.Ñ^fÈSe9Ô©¡Jm~N˯WÖÅð‡¯zBv>MTÕbÒd™q§¬lûWGdm̆/Ö—|ŒØä¥5rÀÏ3È&Øæ—Ah™è;‘Œ®/ï¦uFÓlž ×*óM°~Ój…ìÖò»¹°uèÆ³H´Ýí=ñ(×úÈ—Ùr^,‘öþšsfe„JÌKH_›™¿tÃ<4 EH9“õý''¶'ß´'®:¡)ðI2ˆQº_V²±îØtõEÞµ¯Â5Ž’v¸U¦¹bõðNãoÀÞ‰vÜ&Œ#¡eC™¨û9oL4°0?Õê2œp‹~´Æë9<R*·=FðËüN7ì ŠTÀçYðåÓq#=¯ˆôwŽ?u[Æ3ÓõöŒûÈß;«ZeÒœùP7Æ€H¡šP®û™…EE‹¾@h[‘Ì)ÝúUÉÄ>o¬½/×™2o4ݦ›6$ŽV‹à[’2ÿÔYs°Y­oõ„-ÖZÚ fB|õ\ Ñ„¾“‰¥BÐA­,åË%“il¯÷ ދ\‚R_.ùï'‚9×H€Æ u\àþrúqøãÉšTÝ—¤ÚK4ÎÆÙC%ÕÉÊDOµ”ÉýNÆÁ/ŠÛs(E–|¤¨]fùNä9¶ÿ‡3Ì…Ù0ä9Lñ;F0‹¿`N³lõŽãIú!m§wT g‰uÏé×¼ó?ôÎÿZžˆÁ‘¬7m ƒ°‡Ä¡Í¿ŸÒa~¦öâð}#) [7}Ž>³Îüxïa9Üd´M˜›­\›˜"ËÉÚ˜±u!û„þæ§ÚjbY#_Ë ”¼âàÍØ¸¸ðó´õaÙéKP +-p¼þ»Í—®Ë¾Ž>œs íC{¶W}CÊ,rhõæ¢ìÜÔôÇ"6$éXóåš#½{äšÝûwÙÃþÙŽŽ±€¯I3nOãeú¼ŠØÑ°—´å|ÒIÂ9þÆÁ0ò+Ô!4næý  š Mü¿õ /!ÔÓ2ô“ŽEùÅxL«(ƒzø3"Þ³OÌÙÃé‡Ò;¯}_ÓsáÕ¥Q¿t¡(öõœËñµñµ ÷!”–ž´!n«/«¡¿løºzwEÅÙ7#¶ëFÄìÈU‹•Érç…Ø~Úò ÍxÊh>Ý  *¨xÅæõØÓÐG¶g°TÜ.ºf›Dú®¿)nªë‚hçIKÞ|*'­BDéÊ}i'ÛRúI»0áyXõ¥4M­&•ûîášî[ÙŒvlmg{áI_È4ô¶³;*w,«;Wy±ªú¥sb 3?–çlLÿ<´„5༒@ÑE èo{kÀû]§ ¿<„Áô'.vr8›†l¸-4ºGÎ="~&&‹DD?„ŽÖ[HÓ§ï#+½¸bóá¼ÐIÞKêºòˆÝ»ç—®._s*êAz±ÛöÕ[˜ §—xLLÂúŒò#+8å®öè'"ñ_ LûŸ¹ö`¼®öÍ{´nªºËŸ©Dçzá,Yû3"EÀÎnÅòBþIñʸ€|Â|áaûgŸQÖFÜJW ;™þW ‡Q.Z,õ_µ0I¾ æ^yùÝBg¿ dIf{pO›ç9º†‡Ìð mùEsãfÏ Køßˆ›û3Ýy)½ð§Ø>¯cŸÓuß ÁRÙ?MónqINû×\æûf vü{Ó¿9*óš¾>(!KéÞšÊIûÛÏUž¼gbwÑÈ‘n^X\™ü`™LúfFbÂÌþiŠïמ¾}¾µ¬G¶K…?ÇßN¼zLØ@ZO´Ãr<ªÏ >¸×XyùGy ž9i EËëÖµ°ð¿ŠSß tv õ˜}ýEß;…å°_“ˆ„ÍIÏÃ÷Êþ[h}+‚!$çW æ<_nô>õ šÃO^¿C‰©ð·þ )èMÃsqßjsêmÉ­àË…>AYA«Œ»‡©¾21yºÓÄ”¢þΪŒô endstream endobj 124 0 obj 4651 endobj 18 0 obj <> endobj 25 0 obj <> endobj 32 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 24 0 obj <> endobj 31 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 125 0000000000 65535 f 0000064693 00000 n 0000089707 00000 n 0000064488 00000 n 0000061238 00000 n 0000000015 00000 n 0000003268 00000 n 0000064741 00000 n 0000088818 00000 n 0000086713 00000 n 0000089212 00000 n 0000087161 00000 n 0000064782 00000 n 0000064812 00000 n 0000061398 00000 n 0000003288 00000 n 0000005378 00000 n 0000087646 00000 n 0000085623 00000 n 0000064853 00000 n 0000064883 00000 n 0000061560 00000 n 0000005399 00000 n 0000007569 00000 n 0000087886 00000 n 0000085783 00000 n 0000064926 00000 n 0000064956 00000 n 0000061722 00000 n 0000007590 00000 n 0000011058 00000 n 0000088443 00000 n 0000086282 00000 n 0000065008 00000 n 0000065038 00000 n 0000061884 00000 n 0000011079 00000 n 0000013933 00000 n 0000065092 00000 n 0000065122 00000 n 0000062046 00000 n 0000013954 00000 n 0000017333 00000 n 0000065165 00000 n 0000065195 00000 n 0000062208 00000 n 0000017354 00000 n 0000020744 00000 n 0000065249 00000 n 0000065279 00000 n 0000062370 00000 n 0000020765 00000 n 0000024044 00000 n 0000065322 00000 n 0000065352 00000 n 0000062532 00000 n 0000024065 00000 n 0000027009 00000 n 0000065415 00000 n 0000065445 00000 n 0000062694 00000 n 0000027030 00000 n 0000030478 00000 n 0000065497 00000 n 0000065527 00000 n 0000062856 00000 n 0000030499 00000 n 0000033611 00000 n 0000065579 00000 n 0000065609 00000 n 0000063018 00000 n 0000033632 00000 n 0000036045 00000 n 0000065661 00000 n 0000065691 00000 n 0000063180 00000 n 0000036066 00000 n 0000039810 00000 n 0000065743 00000 n 0000065773 00000 n 0000063342 00000 n 0000039831 00000 n 0000043039 00000 n 0000065825 00000 n 0000065855 00000 n 0000063504 00000 n 0000043060 00000 n 0000046663 00000 n 0000065898 00000 n 0000065928 00000 n 0000063666 00000 n 0000046684 00000 n 0000050144 00000 n 0000065982 00000 n 0000066012 00000 n 0000063828 00000 n 0000050165 00000 n 0000052837 00000 n 0000066066 00000 n 0000066096 00000 n 0000063990 00000 n 0000052858 00000 n 0000055961 00000 n 0000066150 00000 n 0000066181 00000 n 0000064156 00000 n 0000055983 00000 n 0000058993 00000 n 0000066245 00000 n 0000066276 00000 n 0000064322 00000 n 0000059015 00000 n 0000061216 00000 n 0000066331 00000 n 0000066362 00000 n 0000066406 00000 n 0000066780 00000 n 0000066801 00000 n 0000073200 00000 n 0000073222 00000 n 0000076834 00000 n 0000076856 00000 n 0000080840 00000 n 0000080862 00000 n 0000085601 00000 n trailer << /Size 125 /Root 1 0 R /Info 2 0 R /ID [(åÑRÉN9˜œê©H¸)(åÑRÉN9˜œê©H¸)] >> startxref 89906 %%EOF simh-3.8.1/DOCS/pdp8_doc.pdf0000644000175000017500000024655511143604412013601 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’†H‚è›/jn®íJrÛŒÓY²å¤¶äØRÚô× àùñr:Ó8ãIp±ØýöìùyWWBîj÷' .Þ}y¢wWŸŽüãÝÉWãàãÕÑÏG}¥ÜþŽ/ÞïžÙÍNÖUÝìÎÞÕ•1½¨{ÿ^ì„iª¾ßéZTöõû£—ÅóRTug¤,—{UÙtM|¨Š}¹—•éZ©‹¾Ü‹Ê4RôÅ®”U-û®8_ÿ½wŒìºâ®ÜÛåUkT_¼ ¯Ï•ÛvÓ6Å LýèÇv¨UZè…è¶ÑŧDh]•­eD5Mñz|h¿ýÇÙ·GÝS'v{©¼D.í–ëô™“÷¥}¯š¾ bPFDRvâEÙTu«-{ûÄ5ê˶íÜzNú¢µ¢ôk3 _µÊJ½ÓVe¾åðì'û;“} åøEg ÓîºÎª-}"DÕÊÝÙ÷ÏßûÃ÷ÂëõÉÑÙ^£²×ZÏÊ}WY–ëÖªV9YA}_îMUײ–ÅIšúMYWªíºZ_¥¯¾NÎ즻J‹ÎI8Î}:Lè›6.¦ κÒÜã‘1j+†ÝL×ÄmÆM»½á·–°Ò"ãucoìLa7Æ‚,¼gµ©…2 ŒnüD Áâ_¥©t¯tkA½×ö‘neq?¾ò#%ël ;5ÒèUñ! -…®„™E¶Yõö–rXȽMÃÛÄ6,r&ÜйÀ°gè5åõ<=ý€ÃÈq’ ZŸm x›†7ÓU×vÈ"l2ã.N8u˜ÐµÉ°øg;¬;­{  FÑ8F›5R6Öa4Öd¼};î°•AnîÒö¬¬·¨…43)}‘–„ý½JÅ9\'‚ PÐñ¨wk>Õ¸Qø {™È—Tói§wéé{k_ó–wv€(ÄÕàiÚä ˆSÿ8hÖþ?j`T°<Ðåè©TçÌ»õõ:õ€3EL Â-žžqY'˜0ÄË5¼}@ŒÄá+n aä-àmšóÈVÁ½°÷Â?mº¾0é= • !} “iBÃÈW¿í¸îàì;š)é ðÁaT_ͼŸÕ6¼Ngz«*oÂÊÚw­‚ ¿¢’oKf§è%¨Gƒ»ƒ=¤ÿÓ¾·ñQ'ˆ,"–!Ʀèb)Ûl£QvË¿–ÒTR¨vÉ÷G2ßÏööC1ñZ?”éÌ>&­4 IvµpH >Iqx2ºe“¹]p4U–k2Î8µ9K®Dæž–•è3 —@¤4(e ÀTp¡JOmÞÿõ#d KðrŽ'ŸÃ‰×iGs{’(­+úý9X4wZ\»Ge,rçÊÒ 0q×cœÓ ]ÑÕ¾ t—R}‡,¢­l}\ú­HêÚ_â¹ÇõšlÀJn)‹cfr_•=ëDÛ,$§å¹è•Ñ{I£¹V5Ês½d“ "[Þé~Ìá£s' [J&çú†¤1e·¥ GÎRÌBß@7ùI*Æ~Ž …¸ÉÒ’,Í‹ …ñ´ïJ!]¤Ö蜆}Õ²[LïØÓœUˆ‚´“ß…Ï'AT*Y°þj ð;»–‘ºÖá‡ì@6Ñ4938ø°ÜÔÓÔœà!­…•;A€ö¬Î™ê–œ½‹RÜ}p%d’cô§r«|\úµÁ^¢ŽZˆ9þË@Èå? û„ßI%¶’êpF¼ÅÝÒYií‹aI»y%Ãõ<«û“‰§bàŽ3­Y®æ(W 8-”sž7ÑÌï03Ö£q&{…zûU'‘j@æ&3€z#­[÷ é*Ti½±”G©ÌiÛƒçþé]É<ÕjÝŸEÈG;¾Äg±DŒÍà26wuß©P5È®x}¿!ObѦG¥aÜýƒí]½Š†<«†-Ãc5¼RjR+9hÕÑ !?º£°XUå§!†n­ïÇs!~~Útd´z¸ó%ÅìGZÓqÌFS§Ae1»›WI™“õ¦ì 9«ñûeÈ2™Ýp~¿K«sªL`ç—åË‹Ia:¯çåB]º‰bîtÃMÔR(íÓ™Üjý=°á/“ÖO~££Z0\¸a:Ø ÁðúJÉ`w4ÈÃf¦Îhv’Y¿¾:ä5‡K-æ'"Ñ»’YÑÆË }~sx'KI bëÙÄ<éRF'3öšY8´¸Ì,U×  gEï’5ò§Šëo^Eï¨+ÞЭàh=¡›A¸&€aÿ®ºåá½¾Ruèc—ÃÁ¾hY8ŽYME aáOå¾±Cç‘ÎJm½f§ûâoöa[õ]ÝJѺz/Þ`aCFþ¶ˆ*]@œÐÖ‰¿¦îŒT_kíšS櫎ÉîÒ¹Öw&”T“1 ªÛã7y±ØT1­D«+£Do¿ÒЧDäîX vò"Æð¬u$|o‰*_÷áʯaø náiz4¿ßmFW­Ž§†\Í ±Œ~è0@¢qæw¬E>zœøãU÷Èú—¤Ï¿'Õg( ·kiæišyÊ@[”q 7nF¶fÏÝ'Éa¯ø_¼†%@mÐ+¼HOÔ¹Œ…¯ÒÞ@3£f^PŒÌ[–&hås¹ð’ /'­,¸;:õî‡×€iËHLŽÁ¨ÁH“· ¯?Ï ¦ ÷S©#Y²­xÏ6âñv̜ᣄ¬¯ÓÓq*´œÏéýé= IKÀF|Ýë.|½„gØ×ùÓ‰¥¶h©>§ªs£@Cü°åL–+ü`ž›–ÒñQƒ3ð<¢úE†«àþžG6`/>À3ÖxdÛJ¿Ò鎇{Ó„¨0!õ”v<‚>æžÄ=ÙŸ¬Í¯sœTšc9>æ­(¬xêð”zmî¹v%qÏc¡‹ÜØÉ¹\ƒ­v [òîàïá€UœÅîâÜŸ³¨ "ünD—kTNG$`xÖ–|>1>¿˜„ØŽÓ7ØuB´gn×2_7øB—å@Ÿ…ÓXK¥ïgAkÀ*?UzŒÞ–b<¶ö ¬ãZei ÏEÂÌ I6sÊcæÝVƒ´÷cIËY(ˆ’âž<å~Y(€Ì5DZÊe ¤ä}vÁ³™»”FV¢Sà.FŠGkñ¤Ï—]ñC íqÜ!/'ˆº€ŸGt*ÕO¥Nâ)2÷4«ò³²µô[_Æ]OÕ9¦.a)Æ´ =Mñƒ†ñ¬VãåMYâ’;è`–Þµ ì‹7YU·â×ðBØï»ö7Ú„©mò1žÕ ÷Þ4I;€¶ƒF2 …äÞ‡ äá£Ãï—Ï,¦‰ÕìÌBY‡¶VÐDl)B.õ))€ÇiŒûó¥dßlKËrqªGš ­Œ9Ígí)ØÈÂ1 ;àeÞšà ùE\Ï­êvá©®¾fÇkEh’Msþ?/ÒÎèàˆ’_sNûçÜXdͪŒoÏYm®ãŸ­Ü]“+ÅÕKXqéd©¿lÎÀRû ,¢á½!÷¹ÀÏZóáj~añÿìç^º]¿!­°×cv—²¢—¥V#ç0l'-gôŠ$õÇÆ ‰±8¶ç>¸Œ£_¨mråL`̓4®åw¼_…K‘C~Áâ¦Ý6º1'·Î/›¼Ÿ!uØñŸ¹|f¿ `~˳¡kiL™ÐÃoáTë‚ úa ‡cþ2ŸSÈ~U¹a{¤À¾¢ûºÇ὚Ìïq?5i?iòÎ!À*ï…!ûÚ"Ë.¶iì’‰’öúˤ[Š÷¬!Ž_äÂXŸ5µNå ~è?î7ÝZæ@{{]‡Îê¯Wnf-õ­Ø Wz îùÛ´ÁjU%cˆ. ˆó[îk3¦qY/Jk*ÕÌZQŽÏŽþbÿüŠendstream endobj 6 0 obj 3138 endobj 15 0 obj <> stream xœíKsÛ6Çïú:’3Œ÷ã˜ÖN›Ä™¤Ž2“™¦‡Ôv“´õ#qÜi¿}AR–âʤeK|Hñ @ÿ»X‚_§”0>¥Ù_™8½˜œ˜é§›Iž==ùi‘øöiòub‰Èþå0}z1ýaî 26uÄééü %ÎYjŠZÙT+ÿ§¦†Qbü “dšÎÿ,Êø¶C ·T@;C¤Ì üš°t¦5ÌI_xƘ–¾®äm:“„[.Mò%eDë_¤3K45F%·±ÔßáøÇT'}fò=q¢„:¹Šg~K9ÚQ‘µT–Î|Gi£”MÅJÏcùÿû4#\‹„Ä v+©•ÝtRYUÍýmþbÂ9%4xó3?h¶÷cÆœ¬uôè’ÙÈa†mm§CG2Ù—$cœ©QpM0ãi˜õÓL8F µÓÄÅQ8¿‰p>L)Ñœ9¦B.çÉ,­•<±• 0æ¾¢ü¾Åø½³Pî%+r>‹…ÜØóyå¾ñ¹kæ¶Ã¯uÄX¹¿åèpÆ+æ“2—±LŠM~¬˜Y¼ó'HG´cª_²Û9›ó—1ÿl”cðNѱSo¡§»f1‚e…`Yªx3–9†å¹Ozadµ)]– XsÅMÈf!š3uk‚V¨|>J1ÂðJõ¯SM×þr@CÓÔi…©œ *ŒHÆ„« jG¨æ†'Ÿ¡4Ï“Ôç*ÜWyó¦ñÞ.D‚Êä*¿ŽÌ ¸Œ§~§†š®âáÜñbiÞÿáÄIyæ¼°´¢¼NA+~H}¶!&»€N@씿£ñИÜ5`ÖèkʱÓȱ˜åÑRîÏO+ñðÇxxWQ.œ¡,zs3_§3A¤«øY#ó@¥ÿØD*fsÜ)-™Œñ†>÷O*i0Ú¶Ke}g?>q1¯œÜTÿ¶äÐjé·]èIf S® ô@nN¶l +ª<¸ ŒH-_þJ¤€¨7AW,^Çä9抬H¬àÕ\UAI`ÐêIÔ.€vƒv:‹™çˆ CeRq%šª û”‚ e"‰9Kh¼O}˜÷ž—劲ÁȬi×Úªqʱ³qøà‰ó&N·ÀD+SP}Fm<šP4Í@vŽaˆu®óÑùÔñ—(T¿©³#¨ €ÌBji"Œ)†N'3“JÅZø–P D/#gŽc¡ ˆJä"ÌñÙÈ`FCí2ÀÀ'FÎT¬ÄŒ…¦À"ï÷üú¸×+  mžƒs3àhã f:›ÂP6nL_(´Ó¼ÒÕà‹p«ÙsÜ®V…‘4AMjÁËu[‹[ñ3â:*§(²€ó{8×·ÐPŽ6C´vÝÊGå™ÖÙEõh{¶õ‚W ´’W¶Î+a‰ðOÓõx¥0^£v_¯@)ˆ£V¶\VZÄÔ­0+Ãñ‘²ÜãÀ.Ô§ä‰T„úu¡¶]bIG¼ ¾}bíÔŸþíX‚ÝtÜS«5A§1ÐE½—+@‡¹½ª¤ó¿ÊYV%]=(Æ £À_oë5UQ=\ÿ…H<ˆ!$€(XÁ_ 𬠲ãXÅË%™ô+ë¡‹lˆÎö¡h×.„:î‘íf-sð˜’O1©õ4Öp°q†é/ šbæ¤J[s1G+ y1èâK@Î%\Db¤nšØÓì.{dÝ“˜Ó&“4Ï)æ­S«Ùiã„·ÊGD›AP¬k‚´ƒÉbh¬‹ÁF®X¹+"œ@©w©Oå3Dkj4X 4p„.~K{õå¼" ´ÆVÍör‚.-ö?tb8™3ÅPB…ÞSÆ!/z–ìE…½&H|±È$Z·°¤?Uê5ñäð• n``ÁÞ±@Eé8è%F vPH_•Øp %J<Åð¦rЬˆ'‡vTZO´ªjÆ7¨{h:µ$“&¦—¾ï=ÛzÁ  ‡zØ‚LÎÕm³›ÔŒ ‰…¿ 60q°íK,¬°äg4Ú`&)¶u³ÂÙâó#Œ» kÔØÞºÞ.‰$#Zßñ÷NOý=Zï:Û‹#_‡–¼.Ô(#‚·Xó“­cÎOÖs…ã¢+:¸ºÂÖ÷Î0Ÿ?œ­¨äËÞkcˆ3cçezåˆÛš!9&N 'Ù“P…» &|em^D †Æ°‚ánõ§c"XŠÛYO‚G÷ÛZÿv<ÚŽe’fXif™ÂlÖ“¸óÍûFY` €],üb`[Ê'±Tíµ½¥ 8ÊÍU͆ßE#‹*½îë3° ‡$\/ûÓ²L4´, |A×ïÑ÷UÈÍV8·Ç2A9áý‰„ß=ŠöÅÄC¶ÓZ F¨h *ø®°ÿbþoÜ“µ%Ï6ØUa§µsûnJ$ ?(ø²W¾3nÌÉÁ÷ï}÷3ì >eŸNåZØ—³/›Ñžv#éß¡úóMªTþ­ÍìN˜]|ŽÓ?糕7Ú|àSxcÊøÏ£ùäÿ÷?/ƒsendstream endobj 16 0 obj 2230 endobj 22 0 obj <> stream xœÍZmsÛ6 þî_¡o“v•"R¤Hí[×f»õåš%îmwÝnç³Ä[\§qÒ&ÿ~ e [Š_’6_žÀ@é— M„ R÷Sá´wtj‚‹yo!N]‚›‹Þ—žM2÷o! x8 ~A‘yÐ?ï¥IQØÔ”»Š ×ð{¡#ÒÄÀ‚iïSØb‘'Fä2¼Œ@,¤2á$R€²B…ó(6IšŠ<ƒHÈDIcÃiK•¤&3áØ Í *œùn¢X;uY„ƒ…0“© ?#!¼CHö @šéÔ[ºÔìOwû"å.þ®îHÀÝE²³]³ØÝ…ŽáØ» WÎB¦–»ß`[º@Š÷¹¢pýèD¬8H?ìHËÕñnRQ@’¤zÚÌðAó\ÜoÿœØÒøÃþ “%W.Ã{Aj‚¨=Ky4'Nö`®:ˆ+ÏÕ·xZiÚuj%¥L¬*ÇV’[àØÊÍîa5 3Æ~"ekú)Ž¡ü jŒƒ¬.Æ™™‘™ÑÚjJa¬¨ØîØ#2w¬Í-e"­1 Åêܲ¡Nj›}É®Œ-Ýç[[õ4ýdõ¥'û•î‚}GòBeb‘¡é ±Xºä†Æä‰‚ãÛºù-*ÜWÐåÓz9þ‰¾é ç¥ö„Èr—(Ä"MrôGÀò‘£®6™ûÒ\¡¯M<2ë>{4ðhÊhÌ=ú+ä„ÑÊ|1•î/ ’,˜0»ßytÕôB®Mx8F8¯ lôïEÔài«úÊïzâÑGÖOTŠªÓ-!j½^Ù ôc/´¡£Ç„¯ž üÈ®ý†p†å’USß"üã£4C(7«ÕLÌž³ ¦lî¦ì7Ö%×¥ãí¾ú*xbX³&soýÒc&IÇt£:Ó÷müŒpÄ.±1°GnÍ,Ê[v‡!k˜¨]µå…?æ-»oÕ lÉJ«QË-ÖêVÉ}ß1¹»²qµl°ˆôbŸáæ­ñ\ãéÁž#¼d­Ý°jÄÚäÊr[XºY»ä¥²9yö}Ï<ªzz—þ½I)Sëä]ÒìQ £û? M\´ž !Ì—–ã~ïwøùE˜Ãendstream endobj 23 0 obj 1503 endobj 29 0 obj <> stream xœí\ms¹þî_±ßâM±ƒ¤Ñ¼¥’T 9rp!f“¢ŠK¥l¯1Îáð.äê*ÿ=ÒŒFÝšyfgöÅ€]³£–Ôzôt·ÔÇ‘ˆ¤ û·ŽÏwd£Óëòñèà/Nøxºóa'bû§|ÀåãóÑ£©i¨’QéhúvGDE‘‹¬Ò*GR¨Q&e¤óÑô|çÍîóñ$Ž’,Ö»/½4u’JwGã‰TRD™²b,”i©¹HíŸx)ÏF^z_I²ˆwÏH¼ ñ„ĉW$~ìÓ0‡Ê|3;—MÿºcÌ ŒžïLÿf÷± MéÇ ±ßjuC†x ÁÞý@â¾Àº8&ñWØ{÷¾ð Ôðó.ɇPÛ5TÌT¼$qèBa.u¤Š¶[a>ôÒëG‰IŸéØ¢2@ÎáSf¦‰§}˜ŸA 7zx}1Q|„ヌ""• ãÏp¼¬·wÍfi^ä|k³1,:z+ñ˜ë(Óª¤³S–«£‚“ð†$<:½Ç‘!"Ÿ®…ȧ^›‘¯h` ‘9´ù[8áÿ Ϡ†wðÝþf÷œ¥(!ÝêD^Á1Â~{ÜvNq’#^¦íVz7ö’0ežè‰1‰ê;Lo¦%q¶aªóô+…i›Në€s5:¥0•Hto9>'Q´,Ö½îßü8ôN9ø×k!òµ—‡œ¯ûùšk`„‹³€ÞŽ{)ç@WPÄ Õ׃ô‘«! rNˆÄqÃêòóˆTY%qÍzéÌKŸ¼tâ¥6Ð¥È#%ŠèäìGK>õ­è¬`y$;¥ùþƒD–P¥Ðb,²xBâchRÆÍ z˜d†Ô×É–M½¬Ù)l6‡fpdè²t£uúo,jX­&ãrÖ¼`úJ]ï ›’5—zYzVäcÏ_lÈ5Ô~x0O*àTÙa[}¶Çðy$>ù:†]|#;!x¬ÆV·æ‘÷Z‘”d¬ƒHÆR‘8ñ¸†"F$Nо=DÎ ‘«Q@ä!r öHéo¡&Z‰(7&RÙ+.ïQ .UßÁ¼‹H¤…2ý˜aŠ(“JgVs !ÓÔÌHDq’¦ÂŽÝ‹3ÿªQ'…dÝèllf"ãBÛ¬žê¹6û’…ý9V"3ëëEöÖ‘ë)ªæ 3zpÝÐÍ´¼·c‹“,QN¡Ns7³¦êKzjð’Ø•ÛÖ¿pBjÙèéé=}ïÇZUý à  Å͸UiUd&Ë‘Y$µHøïóR•™Ca é»Â†r‹¢â‚Oì¼ÄŠÓH%I B㬔ŽD–HgÄT&šO‘ÙëšÔþ¡ì,6ÿ|®áôú0®cpq* `k`2|8¸±¾Ý3–û£Hi %*”¼ü¹Õ˜Ç„ϼD?òº‡ âQwá©×ž’ÞhF¤ù§ÍçP‹,éÃüò»LÌ dj1¢+‚¨%†ÀkζMnPUc-Îv¾m–Cÿl¾ÂÆ<äû½VÅvþ%ýþy\˜˜%jÙeˆ÷ Ój•K·),° hù±ÁŒûó‚Æ´àûÓ⨹Ã-o°ù³P2[À1Ƀ‘Ì!羊Cò¨²æÓ¦Ú’CqŒçiŒ15g<ÿ;›NÀx¾ÕƳ¾Qˆ8kó\Ó±á"r«&þé¥?>.¼´ðÒ¹—ŽÀX冠³øfØãå8¶AKu‡¤L”eÒ?”ö¢Þ9jî ä°Å·ß5¸¢§˜pÐt »d\ä`®8Êq#¼=Ù`Þ’ÌïœÞ£Úm· {Ïì·ñDÛw•¬W$5ðSû4Õ–cš…êØ÷^<àRÐe2,Ó¯us¨ÙžEM& ¹û?š3ÿ‚‹¤bq +FÕ§ÐŒÀäCظ”ŒšAõhæpÕgcÄŽŸÆ‰Ý/"c³9†kÎÌqÀÈ»·5´ ¬X-Yœ&©m)¶ÌœhÅûí£Ê²¨(²>~Óv.{±Qul<‹Û|2ÀOÍE<ÔR 'Ü ’W×j K R!—MvjF7)½%H#cQö‚$1‚;މ-BoS—·ß ¾°1ô ÚAHT²ŠYxƒXºTéݧ¾ƒÈq˜—V±ŽÊº‚.¥ Âè&¢5vüżZXÌ5îgÑ»–1Q÷6€Ld'Uch[žëv\ÒO×f¹1Þ.:Lu:Mt*L?ÙÍ¥¾¸äëFsÛžÒÜÞ+WvØ[¼‰o¡Ÿ’ˆ ÕXÅñ& 8*RÚnŠ­ë ÷@óe3®vf¶ÆeÆkx•XÁ’î ûü†,<ä¬}«FÄõ?¬@b¯iÄ»˜ u¦3Æ)h£še3+'Uþ逤 eëØ¡­–#œÍÓ#¶†)ŠX%¥aöQzÄ<ÁÉWÅ"ˆºË„4ö»r›F0QæI[ðÜ`†Æ©Ì뛈0SÛRDÑX¹Ms¸)donã£6¶IæôÂ;Ø,Xè*ø-¢<÷_U‡f<‘2ø²\‰¿û©¥áw?à˜“gO­<†úYrÈ›Ñ LÜF‚‚‘ÆñkÓˆ<­ã^K­üÄcY~âï©Ö<7h“JQ–ïßn>Rq‘ŒDî#ä 3iL¢äŠeÜ»ä8—2Ç=¦³¦‹ÑÊUŸñ)1ÛŸL/"Û‰”zâêop,fÛe™(0#AŸ€Ž±«Õ»Écìv!àýÌTX)«üêÉT6 ¤Q}Û¤™÷åX}]G …±´þJßËPºªbÉL¤"C®›tEb¬ñ^å¹´ˆŒëø&¢hÇùNä¾GÑ[‰¢3é4»CQ4‹¿Å(º“Oj&Œ‚†´aGqFA4#-Ñk^ "U]gþ˜·nè ².åè%˜^ȣȸ\‘› ŒÛUë÷>0~1,¦Û40nW_ß§Àxo˜—Ætȼçã]Èºä‚Ø†ºðyhÓŠheÜáãW)gÅeÃoÔËŸpðË"+V§Cu Ç­Àfä´®RÔÌè’„¦Å+ afnÕ»^Kç#íGÞûôÕ~ÌkmÍ»tTÌ}ÁÁññ­úŽÒ†ùjÁiå%ÒXHï%œénå¢Ý– ­óK¼$8ƒmá¾týÛ<>‚<j¢ÌKº9À[?ŸÙÖâ,w„«[&ãûBžº«P½ëè:̹ô ”+Ï’à«–’g 7@ׇK—ñç1‰M’d‰SZéÃtsÁ½ÅÒºÒ#Ÿha¿óÈÇOÒ(ÕIVp‘%8LÁ ð3]Jœ±‚-”ª €ÁÁý€ÃpòÓÊ;­ŸÿrYÍ:Ç&Q;qsl&0_¦¾àÚŽ)ÉÄ À>ÿhF2½ßÀ®Pl Ù«ÌØø"¨¹g¯{šö=}dƆzØJÃV x4LÈú@¿/ DðÁÙÒÇoVDQ"ü'‰½d°dõEñ‘÷òðÊ ¦ â8µ¸K‚ãµ ƒù¡Ð6Ó}F¶À§Ö½ñú¢cÚ=…Û„B|²Î@ì6LÔqÉç0ì-X¯ö]’g«!iAµöTãÌ‘½Ú»§º<Š-$öI]ßnÖ3‰çûTõÒ^°:_ûHv²K«¾¸ÇÛebxþú!‹Ó:ÿî+€ÉL̺þýßÔþuûªb‡å”#ã:Ûe—>¼xÖ-Öv÷v“®7õB½xÝwµ EQ"—¾ÓÕjä: Ð=&ô`ï3>õ"sN¥Õƒk»{­Rvp²{¯aŒˆ÷3À‰¿’ô }ëô¤ò®öÍ'Ó¿›¿ÿÆ¢Hendstream endobj 30 0 obj 2939 endobj 34 0 obj <> stream xœí\YsÜ6~ׯàÛ’[†qíC$+)ÇGlE®Ô–7µ¥[Úèk${í_¿à…näè°ÖVòÒ¡€ÐÇ×nL>xQ(¤•ÿ¶ÄÞÙÚ[Ê;Z¬UŸ½­_âòhíÃZÆå?ÕNïyëÛz¢^™·}¸…E‘Gªæ*¼,Õÿ]¤žQ¨ô€³5ß ¶ÿ³&D˜çÞöËµí¿¿÷·ƒ™ÈB%2ézœ‰ò åRånÿª’©YHFa¤r¥WÙÞ_{ï¿ fq˜ª8ñ3ÔO†zdQT³`{•I%YÒ²ðôŠqšeQìï3F‘È2ÿ"2T¹ÞǙަÔ_¥,Ér²Š•¿Sí3–‘òωÜ'’±]h¶þ5ýiÉ ½ŽÐSòØ¿ fi¹YøW6Œí.ŸkH6áîfK"”yž¤þójI%’”‚ÕG’>lF¨œ¯Áö׊$\$lŒÙ™­q¹ŸBðM¾®·ž9Ú£`íQ• D>i‹W|·¥åÈ$󸵕Z]™H“ÆP+V‹Úld\ø!äÊÈçpßxLPHŒÃI¹‡8-üÓ ±\©™c™ý—öÍV=€Zbg<1+\ð¡†-Û eóÙß™¸¶H·]³Ì’\{Ü“jlœ¥Yá"m«LJÒŽï‘Ù^ÑØeò¬X¤RšV¥Ìd®é4õJ$ˆ³ÚŸ‚BcE¬ÒV *•\¤ WézbŠ‹„3íHóuR³…6p™0N–m´Ù±÷¡u|"p YC‚ðYDqÃg‘†Y¢Y[Ý~Dhm yËä‰ËUWp”RC§H.^OnYzw c0’¡=ƒŸ!ñ£3]À»'5{¶ÜÈL«ÝHéx&Æ çxt UCnËײå W¡t”‡JJÒÓ!¸3ÀÅ0Ì wÃ3 wlŽØŒW t–ª´AFƒv#Ñ °Ñœ c’ù3â°X@ ÇY˜DE ’ßB!a0ÝŒËÅNIx–‚Ú$“e­mªz#Ô©ÌÃBçÝÙZå¥õ >/½qÞÖdq$š~ØŠ•'Êôžî$EÒ¹“d‰Ò»*ï$ï}Ì¢0™JÍM›xZDQâ ½‡"ÉÊ}Ïâ\G¥Äßd˜åå%àM9§ˆ mËïêB™õÝW¢,a!­+Q*G_‰:Š*?žê´²òDãöç@¨P$ÂRÒ†þsRbJy­¯¬Ð§~Gßân´<‡¢g†‰Qq‰¹²ø‚cXϯJU`™»ØVû°0Þüi¯þb[Ÿ)’ÙĆ|c3˜:åidÓPìÄÖͨ¾vè;zdî=¿-ã”MXñ¬¹)| ÒÒ!‹)K;(ÁsÈÖʇºp¥?âLäs ‹PêËéÓgÓ6 J8’9mÀºK 'pޏKUÊÈ¢t0»°î!YÌÔ(mc Á>‚Œ+‚zqBfNg$øŠÌ#¼Ù í‡cZkÙØvGz?-ordŒí³ÐÊî4l@‘&°ö]¬ Ý\Ÿ}hnû4çÌM?2?D8åN¬+ÄB›† ®7ô2:ªÜi t¸á„úÂàa/œÏÚ3‘¹×1³sãÔ1¸  eÀŽô,Ù~7÷ÍMCmÊ3Ô†¡Þê· Œí7™±l˜<¨‡Š¢ºr´ä‘»DžɦyDnùø*Yj &÷ ¤× ëœL´/æj@\ût¬"÷(9yö•ê…%œ,*qjH8Œ\À_Á³ŸÁ±ìë‘—D~†ÌØÂLA_\òÿüšùâ+qþ¨‰’Ì—kâ^œBJ(e,è~:DÊ%ê¹7­dZ±µ’}}ø"O‰µc;Jô Ô3ñ¿[õ<€ØOÚʾ[í<„| ŠúžÕCMõÔc¦EUnÖù›]|zš d˜È\Ø¥¢ª¤ü#•¤­Š¨D³š+$WE¥éÒ¢«û˜=â®(cͪ)G·UÅbµC^›Bt·lìlíÀªÎP+f¸¥I«åu{eLÜÞ9©ËJq¦±y,ÅZ¤µ «·0¸ËdÊÚá¢ZS²<¨¾ŠÅÅ8\@)_A5Àõˆ–­½BTdúì3’-¬Š1)à–>;–ÂŽËÀ¦ô!ac»µ)¹¦êrgšÈ~ûmiƒ¹.Ý5vƒ+Õ-hùýEX€[»½6aa†ÞïQU¼Å*؃í÷c5Ðo¤I¸œ=ôøË¬úŒW¢6 ¯WÎ#¥&éÈ?dX÷°n5æÌ\j¶ä ºÃØŸWlOõRJöÄÓ®jö)z!²è6„ºÞ<¸i„Ï`õ{°@ÐV†a†¶ïZc5*ªÐ¥ƒø±¼0Z~1Í’Æ@–F²Zõ¥E—ÆVº2Ù¨L)Ños——Gý¢‰kånᣓÞKÉu{R8 ™ÚOš†öfÀÇr™ƒã úÏrÕ]Þ.ƒOá,µÛ@åˆ×#•=D×ÈÆ ÿ .ÙLò0§aø±î‚gˆmïýB•íXFÌ^N–/œ[ §ýW¹ÎW„+ö)©–œÙ쇠Úòí¶Oˆ__öšÊã:Ÿ#®Øðçéž‘ŒµÇÚd4ææå*Ki‰_ñ³Àh÷ë“8 }õâY‘“0ÁFÙÓñ'ž¡Åªß&m+#Õ®ÛE—òssKÞ1Ô™¡¬ëôòžÇÂÌ:1Ô—ñœöà úÌuñfMÓ+p¯îVŒÚ›.,PP¹aÃÚ­ÈEV&‡ÎMðÔ5kKì¸G´o6` Ýü±ì®]R:€Ûy—8ÌØÂ§paܯ='¶V{NäÏpÉ8† Á¸†Â¾îV6”d2Ì GN L{–7k§Í³«%› >øjF']³˜¨ö ÉÔu¿žB¹³C£’—£VûÊàíÝÛy¶¦Ê9Ë §‘åÍ¿ýú.|áZØi®â*ýøÍs½¡Ž¡øÜlSÔ1|·îFLJ.<ŸÇ<›©{Ncj9…ü|‚‚c 3§;†K ¸ÕýÜéç ¢¥Š9ÖÍÿ…Í88 ºSnê÷©Ïå¹ÅÂÆæÎþÍýââòe ¯Ø\ŸeO(HRlB9‹f/ï#NÐölþ{p,ŽØæ•—éÒ¼µè(¬¯êå€`4ÿSˆ›úÇòPOÉÞ[C½7ýJçGCÑ直pû§ à&èý g ·­qŒpÖ.à,h\rfÓ~%ò‘ì@øº‹§ýîšæ<æ4£¯zÊ2 s³‡&£Íÿi×ôf-³NóŠ$NBœªÁõ$ŒÎ6ÓDqg œ$wß)ÅôËå‡wÖF©ÚG’`ayÚç²òÝ+ŽiveâLq©Ç,`jqsç+µ¨\YºÇµ×1¹(ýÎì!\ÌN…ß(aÉ.«”w~’²_%mn¯½Õÿþ#õ÷Éendstream endobj 35 0 obj 2970 endobj 41 0 obj <> stream xœí\Ks·¾óWì-».ípðÆ$åƒ-ʱlÙRhº\)+Šï„äR"i[ùõænÌ|³³KQN)ޤCkÓºýƾ•…³2þ퀣«Ý}7;»Ý©ÏöÿÚïÎvÞîøBÅ?õ];< 1«ŠÊÎNwÊ¢ª|é¬bfMøefN”… ®v~ž,–ÂNX9?_„ÇBj7?IÐ,@ÊX[ªùÓÅRÒ;'毪(m¥õüGzȆ¦ïïz`]0½oð+YºùU@ ‹Ò)7¿¡§lìíb™-ƒ½ZÑlÀ1==ÙÓ£°¼*Ì.u; P+Cök³?¯MKžoÜU­æ×p l¶s{ÇÖ¼XèøªÒ‰ FÌïiüeý>RôâÝbiв²â³\$q¾ð^ªø>q‡Þ/þqðÍŽT¢(½ rspÄ„æg›(ès6ÓlÖoJiæ?-–ÚÞ–:'^1ñøRíàžðYñÓ²ð´=â·Ä i·bAšk’l7+¸®Œ3é3F¤œ˜½‡ÈðÖ¹× Õrþ~!\!tiÚ¡Ê÷ž†ïG–»jd&P­2º“™ûŒ :ê"¥ã‘ëʶ. ƒ]DÈ¥ªfqÞ7ˆWI½\#E_…U½&”íJ„µQ.2Îh‰%›3<©à ˆÏ¡»Lº!cdâ¦ðO™>Ë^¶PÒ;«)FCû*¹FÊd³'!ùTìP¬øæ¹#àgG†Í÷¦‘¶h·œï¤­/¸›êaé|Ķ»/M²›aºt¢CýÃb© ãD¥æÏ< pFàs÷:PÎ_p ¶,#šY™µÖ¾(+éºYö´¯2'·!ƒG!íä6„)¬G“mã{_Žm®ûl£ÍÙz3l~l„ UN‚é‘?·úKÛ0[­K” 'Ƹ Kd\Ex|ðÏV¯¼ˆÿýSN?e£Î3=ú…?KÐA‚f zš W úŒ{ž ½½`sRÈèKU%ˆŒK¥´*´±±ed|3abG\„?óÆÒ€žrj™7PQq†Š’¶sEé YFC!ƒê!þþžý>A/?ˆË^Á!—ŒËR„¥×îâ2#2ôó>>¿‘“³‰÷MüÓ®ú›ñ[–…Trp˜Œ‡ù೟‡Ö4â䯴3þƒà!ÂA7ahN«M­):1½AëØÈwäUn˜áL’ëÒͬôïNiilÇ8.€Q"óu«úEŠ!ïàÐ&,¥åŽö±’cl{Ò: ²°°@ÛDkAè& ve%º(XçÞÜHÈ… r¾`p‚™„i”IÁ‡éqú貓Þc¯y*þd³þ9˨u¿@Š…f¸¦m„>h‹ß.–U0 oiMò-¿Šá‹ÕÚúù.EEßЊ¾ Oƒ(8\^‹·óóÏ2"¸0K …DîÀ± GV…Ò¾zŒäx£Z:%DJ憅7iC¿.ªÂyåL Míe;#sa‘Î¡à «o&s ¼'âo–ÐÓw]•i½~Üw4P{‘Ol‚×sZ]°Ë³Ú¸ÈéNíÒX_#Bú<¹È³G ¡ïZ¿TÊW๎ìíæò´Ú× ˜˜LBñý¦%N-xMà]…€%ù C o$P«,Qw‰|+¶ª8?'(Ëþ¦%h—0BŽÖP(²ìÝnHú{žz4î%oÉŒ:¥0±ýfaý!`lˆF†¢ ³žÙ'v¦ûé‡\®bêÆf&Äz} .Ö$ÓXތὅ^=û Û.œ'ÊÈÊÄ8­Bðÿ>e/Ã+`뙲Ø+Œž¶3[ØWlËqNän=³om¦dà÷'˜Ì3 Ïxè0 ØÀ™`ØX.¨[@Ê M{èÒÖ dxïÈ^ã~£tò¼‰P!ÖÚ&ýòK­uágèÏ_ƒ°ñšRx¹Ÿ ¿ÃPÒÚBHg5©$ƒ2 ¡¿«û‰ï±Î–ýnÄ€‰¨ÊAO ,Û&‰¨N¿m—ˆ t e ]C|oÍC׌Ȣ,’¥‹)¥ˆqþcç+>ŒÂ $mÍ[kŠ’/–GtV. øˆìá =Ó«ü3ó WçÜ…OOYx?™vZ—ï%×ñcªVŒ¶Æ€|È>öÓ„–pê/0ôTh(TœL#0b\€ô¶M-”…ö©=H_íî+7—š:ð*ÝëÀÓNÕbž\,Ë ë‚ö(brØTe©ÃcWT ±—Ê©êJÕˆ‰*,`VUŸ.da}LBú‘Q&¦!ÃW–ÖÌ›À0xF“=#|í$².ü j¥³a>WX¤€D#/T¥XËq8>Þ†q¦ºeðyýYˆ‡¢»’ž6BB•QÖë×…¹8™÷UŶ¹Jß_'èŽm—a¢¡—„iV/66.¼ž‡™L¥Âûƒ4²!­.]eÙ¤¯ÍP-.é³Ô&|S÷Y&Í’ÜÒg)ÉTL—. ËN [Íû.q;ܰËP%mK«õ™c'ú*бœ J*²ò T¸&Gq-çáíX¡'2¢”‰ßÖ {Å‚Z¹è \¨Kæ LÉÞÀ²uÿ2Q7„ Ê3˜…žj…„õ·>r¨G™Jþea¢”gÉG¶'†••ÑBØš£Þò1û§²BD¯Æ³Ö:¤¹&ˆë¶ŒhëlN”äRë§§”ÀÌå±+ÎÚ2e|+AÃeV¸Éû)«àÞØ¬´8¨ôªÒC‰z&îÓk¦•ÈGbäbï·é…Ü lØð–6~ß—ø¨OÆ› ¦‹/LÕÜ$_¨ß\IS±[rä¶VÌ´¾boŽxpZ¿a‚~fVðmè©Ë,lXË©Y¤¥`²­ûŦåúÄ—•5™ÊuÜ£Là‹'˜øÙ–Õ`MÙ\,.mÓ-Τ'±Κ`;[mëZ(¨dåú®øì½êÄ%k;KÌVîãØ“ø MÖÓ]ÚF‹­t× ÂÖoO”ÝÜ#Ã×è c}•Å}ùÉÆ¼™’®ìJÏ CJå[ê7Ãoêúµ×yš§ïjXÕ¤5«XÖ®>°Íàiì³ýzjï­êFˆpè3Çë–aµòÝ^J”y¼[7cÊ*# ­o¿!N`J}&Ö½•ö°Çe¤.-ªx¿8«K?$“D†‚êÎ7à“±2jïV̸™ÈS7]å˜ 8äšbâzäÈ=¦WÈ8ìSº~äN%B͵æâ¶~ƒ*TVÔÙµ»<‘ÚeO'KZS7ÇÿEWÍÚ’£ŠÚ' ¶˜ v{#WáuÁçQë*TW‡ÅÞís}½mO^xÄ6‹ï*Çg„C½F¨¸³AëAÂÊœ¤{(¸¼Ðî1ªU&sÀ·rž»fÓˆ¢ìݸ¨‡^Ã%üÖçC=¶_Æ6…1ù Yº»Ï 6Ç€Âë€åü— ÔXîÖœ& 7h1æï _vòÜôKL—¶þe üƒY¡†;š¸çëúvj~Náÿi¯ûÔ®WQ+)þÕÆAe ?¶xߘãlìÔ£X_àÏðζ¹6Ìì!õ²d?ý…çEeöm:Ÿpwq¬bƒ¡Â¿.iÓñò‘.òþK}°³¦ŸÞÕûÎ`aùʾ(lðZéç5¨Èù@£×ódtMáÍÝG­y#hgÁÔOMy_Ræ%ñƒ½‚öðk`˜Lô.P=æQL*Ô4€yì9«þé ëœ×ÙÏ ¤Æ¿Õˆ˜Oœ~Øò‚/ÕãŸÍšðê6{fÏ¥œ{ÒãÆÞ/dl…ä– kËÞEYrM¤wá…énw&›Óû© RãÇrØá;Õd2Òî’ÿQ|6e©i–¼­‚×õþ@wÛ6‡¨€?V—C<|@—Ãú€ÿ2Û†ÍÛ·Þü!»žìü-üý–ýV4endstream endobj 42 0 obj 3867 endobj 46 0 obj <> stream xœíZ[wÛ6 ~÷¯ÐÛâF)‰”ú¶nÙNÖní2ïiÝÙIlç²ÅqêØi·_?P²HþdÙnÚÓµnúSHâFà› •"÷WÃIïèÔ—÷½b88ýa Ì.{ozY»Å€„‡“àÙ€uäan‚ÁE/ ó<‹lÉU&¥ßyX¥Â$ “ÞïAÿ0Ž4ýNž¦6N~óÐ÷KH‡©´ŠB«%‘3OEƒ:Q6̬ü~S~Wy|pÆà=ƒsGlGÙ5Ä38dw Μ28„Ìî!8»pÒûcðct I/zƒ¯kZøÎKñ¥‡~öÐqM&²¡Jl‹>T‡>Fp¡P„x×XKBX·]d bó¸rK€ØŽ°¾ñ›ö‹Qî ¿k¸Oll3. Ýa7ÃÙ„¤Î¡(QìÐ\O€Ü[OKÈw]òfŽWvùxòFfþ D‡_·wì©t*SI¨s‰*d!<ú `Gq À1#¾W]6™‰], _¬%Á 9S—W ¼˜Ù?~Ú)ˆkO•t) +aÒ%7±k|âáƒò„Á#_n¡šNûÁÇý66‡¸+{ÄðRYJÃ@*Ÿ•¦ÂÍ^þ'0$Æ«0‰®èŠ“¯)ÜR°…Ü·1ŽN OÁh3LÒϯ(÷UÊç¾”K/ýò¸‡‘Éi»Dž†Q¤HHœö)`*M.(ŒSc"yªïg¢mÓÔqšÓ‰PQ °4dSMf•Ð`œ'„è>Ç:².Æ4Y,¹3*)ÃÊ/gLÚütQ°²*©V_`‰Üx®Sžõm?mFº/ù=]2I…0S-¤)nt¦ 87öö5Ðtel™ky“|TèCøÎü†PótSZ9} }µd ·@s ½3ðu¾=tå!6ƒQ]ц¡N@ng¢¼yè;rö†"Ÿƒìè´&’ªæÇlYåo׊©ïlEð’Qðll6kçÅ"ª5G|ñBTÝ„¢ßv «wœp= ÷âDºõе†ŽmnW¡Z¦:|"ÿ¸AX6.Z¿¿Dž4­õ°ZÃa™Ýzë®,Mgcá¯Yîô8†ûºŒX¬ðvQ …ü+Ä!T–5nªaeœÃ‰;£îîà@…­oE³í—OŽé|F\zèŠe{Íj[=€°RÏÁtèÐAŠ®U\›6g®©s:+Ä¢ÝÙ ú˜§LÛe¬ñæÇeðÉÉñMZŸí»ÝǃÞ/ô÷ëÆªuendstream endobj 47 0 obj 2196 endobj 51 0 obj <> stream xœí\KsÛ6¾ëWðV©Ñð‘[3u;iÝÚuÔSÚéÈ’-»‘eÇ–Ó¦¿¾ RäGQRìÄ3QÓÆÜ]ûö#”÷A DæOEL®{§i0»ïƒÓWÄݬ÷¾—…±ù¯xÀéÉuðjD‚Ry˜'Á袅yžEi©U‰¦¿ç:H…UŒ®{oûÁ`G’þ®ú)‘¡NcÕ?YQ21ïE&T(óþ¥}e©™¥.ÌÐ>¼±Ô¥¦–:oKá–€ob©y]³ÈãþØ‘wŽd ÓŠ”Fi¥¡PºZŽa¸räÌ‘—NlèžÞÀѦp`Æ8ò ¼„¼GÎá›/>î¿àvøsôS¢DR”õFßzq²#¹€VZ@3ÞÂy_1Ï4˜aÖe»Ë.ƒ¡ØZ”‰ÝÃÙÓ¸b¼øs¨á2à¶^UÀ©.ù_í“ÿkKþ®ŒÏ:‚c|Ž|ä‚hl)'ëÂÉÅÆ½7^éŸt«³MÊã¢2¯²ØÁ©v»Òìàd>zù—h#²U¦"‘4rD„¤Uœ[* *ÖIŸ4__´ Zå4ÙêქæETœ› ±z˜ÊëÁPª0Jã”ìb„b¥« òÜ‘Lìʪ¥U6¯&ñó £$'“¿±Ô)½V$šçý°ÔGÂ…•vvÙÎlIîýÝ ¥õD"ãksó]XÆ10[1c™òk­ñàHlÚ?úƒ¡£HP°51ò5c8f¦ƒÇÜãæ;¯ûÙ¦´¢2kñæÄÈÞ¸U.×}ùŠ =L‰)Õ²E×;R“£ç&7*Âdg¤©Š¡IåHÒl§E„H™„QœÐ)u4-¥v,û7ž`ÉÒÆÛlu·n†Ø!ô,#_s‹ÛÙÀq«pÔ‚»unß3{2Vöt§hÄP‰ÂDJ™IjI‰Ž…P²Ø às*5y˜‘QìÊIÆ*éÿLU'L¢\ sˆ2ÚUššÃ~¡#S™)l4•¤ea²úF+vÓô—š³¢A$Ajߨ«¿6 Ôö%ÒßÊä$ u"¤I;Ìœ±ðaŽ‹aR‘*«—…©£¼h×éa\ /ì=Í9ó(ó”™‚b_¸8j5Z”ˆÂC¯Ê x×ò­L¥Ô¯*㢲OµXÖ3‚±0âA–áq!­ã•ÔIVC´(Ú•Ìei¦™òñ…+*5láP(l±^t Tˆk˜UX3†¦êD¡&+D+Isµ0)y«¦Õ€þ´’-¨ UAIá䙇xY˜‹!» ðs^ !ˆÕx1†nú¹¯*®vÆ»¬XÞe´ïªÇëæ€W]€Wòu#^Š^°«Ú#^ùçE¼PÓ´¼vƒöÈ×Ó"_{Ä«™Oƒx¡ª°¼òG¼p¯Ì”mŽ}ÁzôEÀ/|S Ÿp˜â=o•¯yàÈcÈ‹³ïÑì)Þð¯à$pà%äýtXnóJ³ ^ÇIqÃe¼BC¼‚˜Ø°8²]þ•Q­³lÕYêàb'L‡7 3q³Âјï¦YW©U¬« ­Ù¢‰²k‡å`æ+xÝ«ïX¸q•Âb8k;‘f¦lêüða ÍÞé§ê¢e\˰]eA_)P»ˆ‰S€I…;-\ÇNw»Zý¥=àÚ1ûîÓ€ÏÍ{P+ã´¥‚xð¹6ù¥5/Áç›ç(ãk—ÅÖˆ¨)\‡˜·Þÿªî…Ê=JÞÚý>K”¼å=mP;vöúøHøn6>{Š/i<~¸ÿª°ÿªMµ¿Oûô6~V_¾ô}Z|‘îZ`GÈÛcÂ×Õ>õ;A›ÇØ$Q^µìû¯ û¯ ~€¬ÿ}¬ƒ®-à˺F®˜„ºÜ »ÞÏa: ÛÒ9t‡ý¸±ÞÖQŽu¿…E=xýþé"›€ëó÷´ ¦Tè{¹R¢a°Iw„~hï¨æôWG»A=*õÆR®V¸¢î ý_ ’8¾ÎJòä €—M¨ÚªiC¿ŸGÿÚÁ¼]‚·è÷. ¦žõ6=”àBï—Œ·ó\·c#ƒµßy¡WF»Š–}­“Œ:Œ×¹Mos)áZãîê+;õ~£?ÿ÷¾%endstream endobj 52 0 obj 2389 endobj 56 0 obj <> stream xœí\ësÛ6ÿî¿BßNº±ă¸×L’¦Iš\Ókՙ̥÷A–üjlY±­¤í_Øù£(ÙžÞ\“$“Y“‹°Ø7–þ0J“LŒRÿ·—¾7£Ó›ƒòñèûç5p}zðá Hrÿ§|ÀáÅåèÉÌ jd«G³“ƒ4±¶HME5iå~¶jd²,‘Åhvy0Mf?¸…ûñõÁìÏïÜ“iž ‡ Ço˜(“Ëñèe€è À;Ð5€®:Ï„ækø…S23IaøûzTfóf"® ¼"áÞ8‚®!ˆ‰1 sW.!î \ÎØ…pÃþ3ûÆ®e":Ü"o®6y’ ¸ïÆb2M-„(Ä8ñpžeRÈqÞó\O¦6)•¬ä¶¶I.õø«É4KtjE6~E`QÓ(d1~æa+µÖ ô^{t‡!Æçž²* i<_ê‡Ç5 ÷pZR0¹M‹ñ×áºDÈUž*\®­°ãaÓ¡å—ãgH¥31^8Ð&™ÑvüëÄ Í3-ñiµ#ÇÈñEõ8ŽßWð‚æ}Ïæ1äŸÆlõŒ mÿU¹¢¢pŒýiR#Ë‚Ž5ˆtV«"‘Ê*ѨìÌQ׉ɴŸMRxŽhä \i–B«’4ÍÜ9Ñûy€.&ÒA¹Îýy4ˆ·4ü¼zoåøÒ+c’š¼™(i4“c[ g½š¤òÍûò\Ü6%äÙLëø(ó ©ÃàW“‚³02sBXø³#î0,_å–¦"…Q]–â$òeAà¯p6ìS‚&0%ð¿µýO_ ðÐÇ£¾OÞ0aã‰üˆ‹²ÒC¸—ò''÷wKðš™r›¶ q&Ÿ™¨!âŸL“ÁI tûͤ7„ÖíWy{®”‹YZÆ%¸Ãr³ûmrQ­L뎿ò˜ìuã´òŒ?Ÿø‰ 9ÅuÛzz¢Çw(ÆÇ@Ø}2Â˜×æ{uÆ} wÛªžJ]l ÊQ§¶Îè–Ná3*%Veð]Pcê3)bã¾ š=Ðe'}¨´]§Þ ›íÚîFè·Ý)- J^Bk=Sê›¶vãÙQH¶„¯@*õmǺî‹l qZ½|$ð3†ËlÕrn54ŒYÜ ˜Cîêžn=Ðàwb^Þçp£·pÏ8›dNd )àÔtYuyùP|o±û%`÷,bh&œ¶ñÁÛõ›™xŸgV€_÷od(–"ÆÿóNVA„Q2>®ÂyÙ*{Ö­ž©7Cx÷ÇSüyuÑæU+Úá–õ˜³,qÄ6™9Š@Ϋ¬Ò(ÁóR¶õ9¤füWÚáÀ¤Êu±R'‹ J!C¸¥¨ç!ÅáR¢”é°ÏÜsÛ¤Ã}©m¥î”×™¢d¹þÞ'qGãÙ¹Ðr ¹xÏQ`t£„½,­h[tk!ä¬×{ÀQQyávXBPèKHsú¼ê ùVAa“a_I`n*™K긄R–sæp·„À޽£‚ª, ckMY‘¹s}…dE‘wÌü‚4¬µµ$0©ô™ ¡”-bÈ¿:vÊêdV»”œ¥Ð‰w+9Xrfu榈jLTrþ† »}f}ý©tdI¡]ŠH—T ^òztXÂ-«ÞòçW4jÖs¶† †ÌkÊ3X—¥3§ÆQ5ûÒTVé°¡”éŸÇÏM^jæp z¨ô’h¼eÕúCFƒÓC$ÞTÛò'Q“RËPÐVFäš{ ÚÒIdÆ*Úûyë9x‚ÎYHþUP¦óô-gà±5ÁI0S'šiµÅ H陥^…ü½Äì$æÈÈ2Éj5ØT¥[ñÅvðl<ô’–Ç8Á.¦/‡nkËkÜ Š×Ѽ¬Ý¹åîšæVäR?=„ŒcÒÇÀ—„à¤Þv…°]º‰|9'.-ýQ2âoÜê¥ðaRI;ëÂK< Å$ì?ûêJ­³õüÂ!sZý¾NÔP=‘ÌEÒ¼f+5—ÖdÛ=t´‚Èíjç^läuÉ¢lÓú»¶(=Lb›ÂFoÚ›nUP‰ú*Æž_¼eÀ&êØ'ºhƒvól‚,C-ϹóÐýÊâyŸŠ•Ù»®9ò/E‘ƹ Ÿö µîÖ[ÁF*QJ5R†CÊž›²: 8¦ žÙOÓBeœˆ0{I: õ?Ú6‹æÁ¶°ÙîÝ)Á¤-š æ¶kÈ6¾·¤ `¶”¡T’ÍÕI´Kû™æÈª\ÓÙs’Ö™‚¯ÏÀT¼o ‘1“Y’V÷ªEÑT ¤:"ßÜœ†¨ˆ†`D ½ÈC øã¦e¡ÊŸ[ñM$’½&£?\ZA‰Y´¢¦èê| ÏÅÒÛ„` {Îæ$Ò¨šÄp±À3KE¢{Â5º²Y.JƒgŒöˆúØ\¸žÀ˜ðfK˜¢ÊØÍ²>1j–ü˜ª~32„§¾˜è4õkî¤ ¹ö1¢A%6þ>;îqnbWÓD\ƒÑÞ¥ìª6ŽØUwÅÕ™žÞ³–>IËe»…Æk‡&n¦rÇG•jg§êˆ‹LÏ@%£ÓK²óÕ"]ÍDo©1Ú F[ÇR!þ-ñ·­Ôít”M-ÜAÿºŽÚ§“_~0VÐgEz܈¯Dpm߃ìÞ¹Wm¿\É`µô#»×ø»+4'×ö…O€óÖXëq°VÀ¨2û³!uc™ÕœÇ¿—mŸŠL·;éeZ_$ÕìȆ"ÕÕïÕ„ 9G`F§t‚.&2…ûÅãø²c\–‹B°ä`ÉCÐððòß'D‰1rï}¡1…+ÈìU0æÿŸÒ-Ãw¾kÓ¤6?"T–+Ò{¬P¸Ò^_féT±Kö0B…ÆÅ¤ŽÞe"T^ MU•œJåb‡ÜvºïÖPõG.[v:a[mGØÎtó‚ÝïpHÞI¼e­·ƒ¥R¬<ÈÉÚð8ÃnSÙ>V»XU{Ó?’.1µÂeLC[%½Â¥C“?@´MK´¶å ¹û7nzƒk®Ý¥7øDXËá¦/j£æ3ŠÔ64†4 î: u®]ƒg4â¶³0>â.–í•…O>E¡F`aëß8ƒ-×ðéÒÅ­wk‚û±—ÇM*àÄ?me,:¼y‡í|,A³‘Ô|Ü:/fÐ’s¥½«V”üiˆÅø@çà úºÁpÛ#‹ê?­a`›ûõ=GÉ‹—™Ü¸´›®„p·Öý9rØ–Õi3ñ´–ÚªvÐ×qÞVp’z» Ð@#¹‡±Îtû𘲳,Ž¥ngĉéPê†ÛHñiÁ‰ngÄv ‹_çh[§Ëœ¨…ø4@gÄÛ)8h:¶®‡z¦Ûîs.Ú”÷c† Ï¢¸Ž#ÍŽ §lñÛ§Nð{:¤¾/17&³áÒÂv&:Š]ìŒ 'þ]¬Úµãø¬Dö‹ñêí¿#ˆŒ@ÔÜü9´ãVý Ò[[¿Iù›¾(ÿg£üC_ ¥)(ÝžÇÒX'’ÁLuÀÀì§òØh ÍÛíSÈ¢ø_~ ‰ú‹CÛî Úe"ßÊó”¾›¼´ýšzÓØTÄë6jø÷ _ÇÂ*ÛF«¨Ë»ÇÙFu 6©±KvãÉÚ*ú.º«ûsÏ·¼‰ýpå?ª¢Šåš®ð÷¬¶ûÛO'è>à¼i”YqÌö5ºs7Eb­ûœA%ă|F ×%å{Â$ÇA„7ôô-ÔsFá5lØóÞÛ~'#‰ÌÍ~·ýõÿ;´tF­7©JL!èN›lê< üvë^ë¼#ñ7ÉÔªsÊ.ZšÛ–HzûiEûž.OÆô²se¡Ý®ëêûüVt=O_ÏÒõü Œ} F<ÐßtâžNdã½á=?fÆE@)?h1˜;¸e`ð_¬ÒƒEÀ>ÈÈqd…¿ŒE_mÃ8%âUÄÀ.ûö±ÎT™"ؤßC´¿#°¸;CÚá…M“ÌäEEÍRBô´0 vK±§­Ø†QaׇƒÍQе"º»kn¦>pÀ.ºy|•ÏN¯ø¾nðÓŒnäeãHažÔmøÎ¡«pÝIí ­þçrtu³h•â …íoƒ-#uÄ™6¿ „_&æR%65S®TøÙìà_îï ß<Çendstream endobj 57 0 obj 3348 endobj 61 0 obj <> stream xœí\ës·ÿ®¿‚ßJvÌó€îÒOŽ¥6nÝXµ•©g’| D½‰”-Ë™ö¯/pìâä4Í$ÉdVÇÅkw±/`ña’g…˜äîß8»=xþÖL.ïšÏ“·逗ªLºš>»|}bŤÎj=9¹8ȳº®rÓöZLtiÿ®Ë‰)òÌX„Ûƒï§ofsiUæåt5³Ÿ ¡Ìôl67YžZOÏýlj…d©u.§'³¹²”®Xh‹¡Åô!¼ŸÍkÛÈov=SSÖjzoà¿,šQ¤ÈÍô}ýÜä¦Wô÷9K'v^…ýCÕ¼í*…Ûii‚MºY­év6*Ë4Ó¿ñ¿ã5®éëÇÙ¼tTµŸ‘©ºî[®YóY°Vp¡«¦/»(Éúb/ õÙìÇ“¿QeNdN–V>&WÔ|D›º,9ê9\`O¬²&åQC Æ pã¾Úa1¥ðLV¡$1¶xÜ3b i½ââ㿞Á™ÑÌ×pŽxák8Øî óÂÜ q笂㲙·Ó-•°kÆj]V½ÈÐÏláÁvÆXMd\Û¹R*«lc‡Yж‡;¯tÖbl`ëÊœ@é§+ßW?¯zúïYa²Br¬0zAjÿÇöÛ蘖Ý‘H‚S5:/8 ¡*]…,âÄ& ñ/á쇽ÄdIF ÓݶÜE¤>¶¢%…54^´z{~13„¤WÎSLˆíkDŽ'¤Áîr¼Á0Ö ín ×ä£'î~º‰íV6B/UÁ9Múb5f¯µ€lÜ;¨TƒfRéÅÆ¡TU+;~W“£3æ•)³ÙlEK¯3B7¦ó“˜Ë´­|ëÁ# {'çC¢¹I0^aÉÀìÔòBfJÔÆ*Aû»É­Ýžûƒ•¸M°Õ9hÏÄ7`oÓ3Hþ°ýfË;\ð) S9yþV”ÞKy•á=žw³¹ÌJSÔrzDà “àkQZI“Š#¼"ð=lvqÙÀ/ |Cà·<‚ÍNf:Ï×Ûå²  (T&ðï°ê³{¶ö³4Üi[ÌìGS©˜KY±ÙÙ ]uÌ+#2ilÓÍþ0Å«8ßĘ À€™1šjmc7ÓMËöyò/ëØhëЫ‚:§&Ð õdí©+·Ö  A–êl…´ÎA«î:Ïéµùî/åþ8ùcݽ ¿¥îÕØ+ts¾Ø’®YœÓ«:¨G“¥À†˜­qʾÚL‰@r¤Îrm%gnƒß*Øévó~ã¡7ú§‡&:Ð+½-^‚ž¿ÐhÆ@½¼‰³2 ¨sçÜ6&DæÂÊEc²zÜ+×þ ÷Æ~„à9+?¥ú]Ãf+Øïì÷:Õ™_¼ yñ{å—†w Å‹½ ôëK€×&t£Y…Köˆ'ÖŒÑ|‘ânÆÎ .øNò#Ä]À~ñÀŸ¡hÂédDª*ÓÆ»ÿ 1÷spªRê¡ö"ÝA2²YJI®IJW íáF‰| ÝÖIie’Öè¥t™’¦'UXòÂì o»ÔÀlAð+k¶„£ÝÀf«V`K•éJQÌõZ²¼(ʺG bÐVìjëÕë²÷aZÇÆÛX§4#é3f ÜÏäSPpÆüù úçn>‡ùׯ±Àñ ˜’£³Kû‡t®g®YU*îÇ´ËrÝ’¢`iÂs2µtúEiDæ( ¼C•õuߥŽkoË^¢FþÙ¼G l_+T«Özh­€1@ já¡[ x>c h[]{è?Û÷„5Ï-“ÎòIDå}Ø ¾ö³üÎCöÐ÷Ê=ô•‡¤‡~L)ߊÛ¡ “U®áª˜Þ{€ke ìâ^@ÑYýg;)FÔVúyʬ!‚|T@ŠÉ¤"£Ù›Êm¤X%˜´„‹þ tqÙò–`ý"BWì»1Î0/í2ŧ°÷an’£agl>Ìä3îßBļ\g4"›{ ä"‘,P`@»þuDRöLÁO˜8Ðc÷ö0±Ñ˜ 0…p›í%U‰Íø }à™Žã§_-±ßÉÆ>õ Ðíñä†bŽbØ¿ï¥ü„o¥‚VÀ˜á¥^Cj^ÃõݤȽ†¸Iu…ɽ§Ð|Þ<õž5QŽ{è]HÑ; ð­òY‚»Ð Û,à8pq“A#·AÙfH²'º°`1®ŸÂÎ’™´ žëá €p7X¾xŸxn c7ÿócB»JîÛ¥º> njÐI—¶Âç†×y|<78Ó:÷' OÏåBïα“}pçgpNVv×¹aÞÚ×ߣ¹h†¹ßjÐ,þJ¢:lܱk‹C¬£°6ÀçîØ0åÈ8‡C™{â<Û,ÅtN_%Çú70n«í0þ–£D­ü$~ñ ªçߣÄúKE‰HÌ· ÷¶‹cVíKbÏ{˜6Øha[Çîž<'ð Ä}|”»Ëå.™Ñ§o'.Jè¬þp º·Ýþì´o6ï¶w{ë íÓ]À_ÌØ¹©Ý7º\´¼WH×þôNñlÀ?[(|MyT €*H–4 v/ž!°×04Vv¦š­Ý†ªÆ[…zb/ó«ä ÌOÔ*«T[æg]œ<ÓÂêa;³°, %/ ~¯Ü’+ÛKÑø¬ºÎ¤Ò y,©•1.*-2×¢p¸M•ªœwfg£´±$~Îúë[ —áp¡JËiÉjOXÃCBç}¿¤á?5Ÿ¬óÊéVßË]Ó²ÔMSó¹°Œ :wQ£oKSl×Vä²lb³ÃÒäH…¥0uÖX¶ù°Ý 4Ã<$Ù®<ê‘ObF¥q¼s‚›Õƒ,ípo0«p Xgøú.%G[!vY6,Û[Ï dyZ|}|Åmü@…UÍsš"+çÀ™XìŠàjÊ;ÞÊa'«†ØÀ˜Ð7]ÂÕêR)I wV OS‰)|¼„-\Pýƒ+F£ÄÌZí¸¯êˆ/vHfïÈù/éo¬%ïRå‹K¸oõ¡£…>?OÈÓ^™›ò*\±SMvª¤·”™Ò ·(÷êð­Àp#»—þ`ZcCIsS‘¸oI3Ò:°‡qy8’"F¶Û™p^Áf§pt6ÓÀ|'<±-kp»‚òçBGÖ³‹ÓáÈ!>GZ†põXRåœÎˆžñ˜Ñ|t¸Õl$¬RvQ 8T—ùyèŽ)»òà¼ä÷­± îæv¨ êÝG´14¶Q‰ /°ê~‚Ô­ŸêŠôЈfÂqj¯×N’0N»ocݧ^^¸"ëG5U$R`C»8ŒDV–"Pb£êø>"Ï)¢~‡„áÍ\v@êtàÆsÚ„øS¸ÐéßJ•ök«u î¥T°ƒû¶DÊÃÞ%`óh¬ÌÅ NQ|òúï¸?‰ð8üÅò¿gá(ó U÷à™×ZRâ‡)°xDæu0CgI·Ô@M{ Â]½L œöÙ¤"Yd¸A[b·k” Êì­™•’~]§ª|T¿‡£ÔHŒÊ3Å‘ÄJǨLÉòñžS*òB¹U†Iå’§œüÞŸ‰…3qb…ܹ\˜p2âZÖ8&W‰ÒÓAœ»= é2ÝŠäŸûŸI•wº`߯ ¥ÿ-kì9zéÞåÀ±ï—±“f4*lï2í–ƒÆå8‘BÅû:¨›íïŸ,jíT¶Š, ¿ÄÝ ¤8‚äeå!µo‹…݃>à´ 3™`Ž·ЇcßÇ!x½}ƒÀ̆9_æqö,@Ê›¥õÂú˜–\{\¨Ú½n– Ú?ºE@÷N¨~ïo`´¾¿èééøÒÖठŸX?À6|´9¸ì++nÅw;ðÁ$¾„¯ý{ðÇê‰ãï§æ#U.¿ë'`´ýï™l!ÿÇŒüõ”‘ NwùÑnå79 `¸ùHœšœqoƒìgïwÀ³áÁI+6<1O6ŒdXœd0ÐûuiÃTåß÷HJbóúŠUÅXužˆ¬,)ó½³hžV¬•ý>x‡$R'=Ú;N^Èž.9Œëª*Ëka`±.wy0j‹0Lº‚êb›D3:6Ož•oKâ°rDzÇäEO+Fõ7Œ?–ãü*¥wz-§¦‚×r«•|.g*ñ|B~Óx JæeÊòÑS„¯lGº7æ/6î˜ö/Ì 4Z÷ÁœÒ½»#eF…æOxZæõTº{ïC’hºzø``Ð-:Âbîç†nýBÒi%¤­cÏ[º‘ó¢Š,›–Òc¯˜îè?*7­~mõÊÀ-fØó —û¸çd·x Öã²9V0¬{JÚÆ²(=mÝ·r3ìý ŽV`ʇup ;ØâB¨Î¼Á5-c¯tÅ´rÿ–SðÜVY[¢•ôÞVøŠ'Ëëú}Ïá§•±+•xEkc%Qø¬&÷¦û%›ì÷qÚoImÚ-¹9|/‰çOPVvI '“ Éwyñ^Ÿ drrÁgp Øþ&W™œù´´ñlbY/0µq—sO©‹H~€`Þ1ÒÕŽIûß”];ìóÈÜ{ÔTü•L9`ð˜;ß<¦@]Î- ˆïy㧪Ø=otó:U‰@ë¾xìºÙ*Á~eüÃþû_ð\endstream endobj 62 0 obj 3758 endobj 66 0 obj <> stream xœí\Y“ܶ~ß_1/©Ì¤´ €$è<éLK‘,M*•’]©ÝÙKñ^ÒÌZ±}nœÙÃq˽$ˆ£Ñøú@÷|šäY!&¹ýçˆÅÅÞãwõät¹×>ž¼ûSG|>Ýû´§3iÿkpzq1y:7ŠrÒdM5™ŸìåYÓè¼^÷ZLªÒüÝ”“º(2¥'ó‹½ÓÉl_æÂü­¦û†ÌÊZªé§ÌëB* o™$»ŠFNoˆ\yEä’Èc"ÏáÓìlBä!‘©!Ž`lf—°Á ‘Só½Ÿ~ÿl ?ûyà³ïç1"R^DŒÈÍ_í™7¿ß3RQ¶Íÿða:7_euQ‰éÙ,7„PµéÊQCɲªrÉ[>Ÿí‹Lèº.¦Ú7}1“Y^5­€øÌFÖYžUec[J‘׆1ž\QÛϳýÒ¶52ÈÚžÛdY—ÂÊ>mT7Áö=ûŠûÑ75|«[ñ.ÌÕqñŸ²öGôô m+K-»É™“UwŸ…Ðöü€y^S.šL™÷?ÍŠ:+”àCµ¢ù]^ª,¯emŽë½?à¼ò«¸‚ ø#Âd,>£µC>¬ZÉÂ<Ð¥“ù‘ÁŽõdí»É–{ÈV~Jý³N —ŽàØp?´Ì5üjÖÈÚ²³ÒJ§¡~X‹¤0<‚üÚfº‡´àsÏ+>¬ OüŒÚ²YO¡*JeÑ l–ú`æþ3<ó!޵ŸUFd×\*wî,Î@Yµc”Ç£Nhʬª½Ð¬‡’UYõ6Ò}àZ°kÍ9ÎØ¸¢Î&3Qצ‹Ú޹¯Te¶u²ßašxIÃÖxåqŠ­–pîÈS” Œƒ„˜7žj7¬hd¶×Œ%ŽÃ/k Öª´›&¤Å›ÚŽh2QÈÒΣ7¡8ú]·}©J -“†FVŠù”;L !mÅgì×±ÞÏ\T!Ä"A>ŠÂÌøGpÐY»Çc¹–Hi>-e(‘íO†Ô çD2BVç…~@Ú„ý]Ãô‹Táå€ú¶Éõ>è„´Ä+ßé—Y“ÕZ ê³£Pä“ÂŘ](Ä j{¡qÍ`FC›}Ðf­ÌÆÖV…“<^TÊÌhê„VýÝë!ÏUÇdÇY—¸€øI·ãëž"é+¶—¾:/KßP¿ˆôY(JjÙx9¢äp@ÖÈ‘Òg\™­¤uM l¤Å&–az“+'gIÃ쯶;_+¸_‡ƒ"Ç|Þ¾‡Nʼè<‰}7[qófþ¯ß†ÁNà5ò[㚊@üðdûJ4‚Œþ‰Zö´ð&ËÂ*ÜSÎL’Ù€˜¬íW}‘°¡ïQÁˆ"Ï´ó.Y¨àD.<Õ9¯RXìÛ¯rë5ÕA‡¥ÿ꣧~ßÓúê<†Îü xåñÅü!/æ~¶Ï=õÌS¯£·ëµP¨0Eñ÷B¥¦w^õ\5£|†Ì:…ÁŠ%dá1èÌqs߈zâ©ùx¾þ½>Ѱ#È¡|ú`rò6‚S¯#þ¨é7ã9U%$ [ý=+Ø–õ°Hõpï }†ß{ê•§^î"‹˜Ã8x לŒSž§:ßÀÀvÐoï—Ÿh–7pîâ’S£Ež½&½tV­àš/Rœ8…œ@Ç+ ¯àÓ[ñr'ic}ˆçVȰZ¿=ÜN£Ð‰$^~ ¡ò28â¼Jÿ¾>º7)˜=‡°ëì l€Í¾w ™ˆð˜#[p+^ÿÆw}¬ÁìaŸb¥…Í,ÍÛÌ ›k jnàÌÎágذ`_º†˜Â$æP·(Dõæ¡QôCù# A™g̼*«TY§¿µ<·Ë4è™È#óPó»Cf­]sYÖ¬3-—«Ÿ§ EIN,'Æ[UØ´d0ØÓèÝÓ%ÄI6C¬Q™º:‚k$ðc„6O+{â\ØŒÀê˳NìIuoŸ2ºÁÄíŸÆVo3ƒ›²ƒ»²I¥lò½Ú7p_;M#e&k'8—‚”¸JÛóÈ-Ý/”ñCàÓäpÖ!1ß’»ÔZ•=búÖƒÀ· Ñ7°;69@b[8`=ù²ÆmL½·JÐlG.ÜÑíò<;çŸdz¾Äl]aà6G·±ËÆlÆÚƒ6 !’s»/¶Ünä³-ÜÉ‚LŽÔ.sŽÜ £ÊOë8– c%‘‚yµw ¯ql ïë!6Ü:ŽŠWJ%b†÷}å(ã½_0LiÕFjkU£j&®`êDÒѾ2 œ÷ý †ÆœçPb³ó µ–a7pâ0´ûuo*½/8yS•j­Æ)¸IâÊqMyÐq‘ OÏgF>&êVGa×þÏ„mW¢†Uoc œ\VY9G®óÐ^†ùþU–Ë&@žûð¡Ü!npK ¨­zÃeE‡¼j(>Ø8 ‚ãÕÑ-Aߢ„òör¥ç¥Žæ…G¯ŒT¤" Bþ~(Z39[P`”Ösš<ñIƒˆ­CoŠ†Ñ Ô½¡½¥óY%HÒ1`Ìâv":Õ‘ìênbÇŽŠOËÜ×b¦Ùõ~…L$׬l=CT„ý»¡›!¼§< °k»E(žl‡jæÿ©+ÍèØÕ/ÍèçäW›3 (upί‰; %$]‚v”$ö|û -Lqßü¸¼§옅€o°¿À;eÜY2Û–]EãÔfôÓ‰¤¡»ÞÇ¿GY<mF#ê¯ 7å)-dàW¼ñ8•å‘°EœþÉÀ†ßuÐþÿÂ-¤ü`à dh\…JÚßè ]'z‹ _ÖÄT¹µ»ôzÉ Ì´ÎT“{“m É+ʬ²àÍE¯Êóˆ©Ê°.§hÄ æiÄX™ç®J邏ô>5c'/ˆl§lާ­³¼ux t¿aI¯zaXòN#ÛØoªšcVÜ‹1þ†õ)q÷ $çƒ<·f”ª¨àÄ w!2éîàÀ.õÄç0¸Ò`æz”°±}™|kq=' o­3óÜ—QÐü¸ƒ”ýkëhu^G"GñlˆÞlÆѯ²rãZ{ÛC!]P„Œ7€…9"Èç™!Çè®ï¸¨vs¢Æ@Qm?ýß/ªþ¿˜¶oí®¡ý±`C#[–ôé»H³Ž(HÑüýè Yœ£|Öõe‹ÁOFºcYùô·Ëʧ€•0Iñòù7Í w¬G>Œ03{§:±Íë¹ÅúSùê;®ÿîê2ßþ#X ÈÂßbX¬q)áOw´ÖÞ);îw±Ä‡¬ÜUÅÀ€èo5¹ƒ’æ9{éJõ-‰ëXnÏv\²MÕ^²>ì^Î,íE Ÿ‹¸@ã’´¤¦aä ä,ŽZa„À%ȸ,n‡d›¢o#¾õkÆÂzò=Žû!U…‹ˆFhp\ƸÓ~¥„ …;‡Äí¿“Y8‰+ý³ÎúQóvË"rªÇW)=,f%-ÜϰÁ$50ò¨»_Ì÷¾5ÿþ¹]@endstream endobj 67 0 obj 3529 endobj 71 0 obj <> stream xœíZÛRG}×Wì[¤TXff/³ëGFYg=E›,’½sUBÂÊ/#nÚþtPteu\Ͼ¨%&pâ{ò¨Ÿyh32‰²¿'U'ImÍt w¯UˆÐÄÞ9¦seKøõÙí{‰‡¾€6$ÂZÖ43m‡[ ÑZô˜×ecU¥4_/ÁW^ÓXݸ ]5ÔÏ ´æÙˆý'vð%„æÂ8‚ˆŠ`õ=ìLìöXá5‹Cèæ_±øFQw«Ë%-¬Ú¾½v(0OÚ@ ÕI5°É€¯\v쥓9µ.ÚE{À(Ð挧£'+!€"ÉÄcÄÄ`n·ô(sÔ yš%÷‡ ñvœ*N‘ˆ/WÓ0:nQ\. š%H@¥ÇHéšDñ› 0î]É“y >§\·e'^jªòºæ–;IÐZTƒs²oäRè”Ãd€È6[:_¨md¬•#©)µh*ŒJÔ?lsZïÉŸ@Ó¬.mÝt0²­vÏË2ŽÅ|_tê¥æmSªïo<ûÌ‡ÌÆ½Õ玞,åŠ8®¡Ç|ƒ»àyô¦¶ÑX€à/óHð«MçS ¾©Áo†WË,u‰Õ1?æG×Í{Y¸">“oPÁ`nc*aˆÆ.«|Ž’M•ßlý÷ ¾z"¾±G/#«XÍËû†Ehý . ¯ûv!¢_ô^)%´°çƒËà~[ {üd·Æß‹Ÿ@ŒÄù¹¬EÐÞÙƒ;“[¦ãÃnÚP4´fEÜŽ»ÀêÌC°Ì`¬€­»“3€Ûmà6Íf·Í@ùr‰=ø¤.DìMq^öø‘+牭v qÇšß<ÄÒÁíßÐ ö$§3Édñ º@Ã.å@ù˜eU‹w˜eµÄMÒcŽÎÎѹKé>ö`B³.ãN¹ƒªÝi&Ñoý2ŠºÜ&·8PÔÅÄfÅlMŒ9šÿ(%bQ(èUŠ+žÿî=y«áUXéCHqú«_´¢ ß§¼xüšÒyžÃ  A+®+*|îÚ`S^ñ-¼ÓCHìêÒûÿÊ;-“—D$'ÌÍjyI¦+/i»Ê²¶ÈœÉCmÓ¼¿1Ði˜»·®àkJJ’+N ‡“’´ÊÃ4㤤V.Ò¶g¦üô±Ÿ>˜Ëd$O1çÈd~c.à¾ûœš#’Äã~îºÍÓØcBÒ„$2n•¬žT”îqnÒמTÂõe’øÿ1! vv¿ I[ÃÞ/ôû7ºÙ…endstream endobj 72 0 obj 2173 endobj 76 0 obj <> stream xœí\[sÛº~÷¯à[©ND“àÀéSrâžIëÖ­£Óf&§Ó‘-ùÒØ’âÈ'u~}ŠÄ.É¥ÈmfÒ$+rqûv±p‘A%"ˆíßš¸¼?:>—Áõ§£òqpþSE<\}%™¡6­Ò"/«’U&Rµä“Y‡U:Çûqka0s0ý®‰—5‹‰7ªžfEeûÆiªì¶ÆÖðͬá›üËZ¾\Ô–¯£ÄÖ4ÍÕUak#OÝ{å¨R •ޱ\´1dBki¥’+-ÙÀyWÆic3- „Jó×§ÎhOVöÒþÎ Å›²]Ãø¯¹({MËœÐÞ+¹~(È”•LJçÓÜKqäsãiLuï¨y# (b«{’7ï¦Õ­£¾ ïé’6ò’È{HbÎü¾ ND…‡ÁP(î•3€ŠÄ~Üâ6kaZ”Ľ¾€ €×÷yY¬r °hô0…Oa3:½ìvO,_}¿X¾jc9Â?õ(áö­¹rx—ÝCŒrOp… ˜$àhŸÉômèÖ¥„éÇçÁ곕D+'ΑÖ`ýƒ÷[ŸÅÚ@Á·¡ÈL¼¡¬8ñ»sîƒå›ÑÊ1Îwh ™Ã…`‹ƒº‚&‰Áv½Š=”åÍ^X>,˜Ìnᢖ¬M+È;|Cî¡,]€hù;y¯çBÊ»1±+ÄFj/(¿BÙÚ›éŒ%Óº؃×Êïàž¸îƒ›×´ï¶ÑØ´°_ÖŒ<&„c8›$3ƒ7°‡½lªÇLèX«>ˆ­4R˜¼KŠ=åÁÖ‡cW¯aQÅÊ ãGØöŃ£:g;ž4 ê€0„Lõ°ùÃGÄS‘nˆÑ6¾NE¢ëkê*=¶„î 8Ù •åES:A/Ÿ7­´®3 ‹®Ji”­´;°•bËmó² v^vÔ3J`ú2+UÊÌ#’ÚÕÓ1iž¿RÍU«d¦zŠë¨úÊúÖn^[±vkÄ¥øšJ6OÊ2Ý@Ù½~”—€ f ŒÇ2;Ví‚í¦R(5ÿ*#0®E6®¶v]ÀÕÎÿŠí‡pC‹œß |«:=8;òžÕaß|ÐrüY»tì›Ã9‘ïˆTD²šÓc_³˜ÈkÛϧºòmÈcw G)Ôáw-`e½‹€]; wèÌ_Ó±ùkæ‹àK±³*\à8舌\n)¯]ÃÓ2ÌÐWlíZ¶ÛynÇË=7‹^Æ"2¡e]ËÛƒf°:úªj°jâØìš;DÔhïÊòÒ¥'*RBÔ.½ëDQ¡ùfçÌ7áÒpVç¼ôÉï‚x¬†ûCY)Ë”ûî)T+æI«‰YÞ114JÃa;kT†³ã)œxUT­yÄJ’ÂJÅ"†Boy}oÈÑPØq-š²è»¼ºngÉD¤•‹ù:KÈ3Qïr[`~çÚ¢“ÉhYÆ!Y‘D*aaÈ{fOÈr44£›Í/à)¾SŒÆÍ‡ê\æ!¼…sEä-þn›¥5ܽ+H­±®²‰œÆ…½5cò¹·ÎÔÓà ¶ú›cMéýkÊBðÇøaå ЪcÕâ¼Ç¬A{Àömo°K‰D©tB™¼Ô]ùu”ÛUÅ[J¬QuÍ&öV´Ã^ëq¶J· e¯Ñ.-e½%?‡fÛ¡1v¦)ejK²x„»«â׆2© —ÚÞ"—Í÷U©Š:¶u¨fþ8̃³/œöÁ¸ám@FÓ'Ö~^9¿<ÒiA7‡Ðm & rü{\ÿI‚7ÉÝkhû“Z6Õª»ñdèî¤ £ñ&‡èË>¢;²T)G§Åtëà`4ï=×=3ŽoìÊ+®áciÑØÃÐzÙCËñÿw%ø¯½ë t`P&‰]¨pþŽœiÇúه܅‚#[òµÛ<((½¸¬Ì›Èn…bÌüáI좱µ=uÓ£ö,y˜0­ë J‘§e1æŠËœ·jd -çÞŒïÖЃ5¼ûÆ›Š¨Ðµ3í»O¾KZã°èÜk-LŠs(aÎÛ‘Eûäô h8'K½â!_Âˆã¾æ ºc14hX³˜æ²Å=¢0žLû?˜%ø²IDTâŠW†ýêùÔqâ â˦>v†“qy΂¬Ñ5”Cÿ9Zß–kÛþain;«jÝÆfZ؉*ë@#ZŠvè]µ Ðâ§-Å7JâÂoÆË.RàxÀ[»ðÆæ»{ìa¤‹. ü<(\ü¦¥‹o;°!¼ÑÞóJ÷drôWó÷?Ì“ïÑendstream endobj 77 0 obj 2851 endobj 81 0 obj <> stream xœå[[w·~ׯط’=ÖÀ ¬ûdÇJí\ªVaO{âäôP"%«¶DE—8ñ¯/°äbfw¿%HQrìSûe„À‡Á\0Ã_2‘K•‰ð¿!N.öžÙììf¯nÎŽþº"®Ïö~ÙsyþÕ œ>¹È^Lü@e²*¯Êlrº'òªrÂ.¹Ê¬4þïÊdVÊ\»lr±÷f”÷ ¡üßzôƒ'sc =:ˆÔ$RY¤Ž"õïH]‚~Ï#õOÀï0R4ïëHýØ[‹*ëRØ\jË—ÍÈ›e_Y£9‘·DfDÞyIäyjØ%lB¾ŒÃ‚È8ÛG°tå§øyò?S)ã™z™|·ç¿üiÏ£sá¯ÉŸßde™[YªÑÛ±ð„ò0Í#•yª0e)Šp~*WÎZ°Ê…PB\ìz0.rQV5´qÐ4~öø‰ ãŠÑ¬n,”@ü6œ¿¢¾󏨀^ÃüŠz]Që¢nÕ¥]÷ç-UåŽ\CÖú–xÍùäʺ%ÀìÒHí—쬿1“™¿ /€Ä2y.…è‘Ò¹,˲aÁÖtâ7)ªÕNê…\øý)ß\Ô¤-¬‘+Èë—D2Ìó%ßBÈ• ìK[äÊèl?HÉ,HÉä¿ALŒúÒÅ$0óç^ù;£Ck¥#T…å²ò>~ŸÓ š®Jñóg¨ÞS‚j™Ãí1ñ\v^ƒQ×å>R7$·´OÆ•±b}ŸuO¿Ö mQyÔLM’²žFê"Róí®u©›s: ò’s¨ƒo^mÔ¦GR9®,5À³¯z¦i,d¥Z£¤“:÷G‡íÛÀ¶Þm²Ã 6õ2R/gS³ÔNX+3Çp«§d2pÝÅbÈ­y¤sEëk‘ ØÊÐx@ÉØTÚÉ%{¾*Ž;|JK›»¡«}—B%yµ3Øz b¿Ûå¾üðEÁ‚=ß8 aBnŒ¾´èÑßÁ×£h)½9R‹ø†½‡$¾£÷¼‚=W}HÂÈ‹¤XèëÝL‰Ú®ŠµÊD˜Å/ø`Ö)ìQ£ðo=TVŠWy¿Æª¤ºÆ°ÜkÍ-ò‚ïåäË&ž¥Ð<ƒ|ÙÙ1Ùž‚a)É$¿à ¿MôB~±øpûf7¬ð°íqëÀu@"MH×ü»?s‰UbÊ:†>ÜZaH/'l d“ÎCàî*©8±=gS0Aþý‘¯úAÄœ;©ÖÝÝß“ áHrȲ|?t9ãîÀîUü®Û˜õEp›¸KÛ Â“ä{ñÃo¡ç~ßÄÃPtž’AäI~ºƒÁn2#ßí¸ýbôrÀ~¾;…8¸Q˜÷éàþ"ô+vK°úù½ ÷ñ¹Wäîÿ õ;Há›RÈ8”a·ý "’T¯‰|Jä!ì{/£°¹YX½‰”ˆÔ³H‘$@ýœ¸©øk×üA_Ɇ²UøHÖI|'•·óQñ2Pv€½^3r¬1ë¾·‡ïý”BµÊ(„¶÷áA¼0Ö(–`ûg”`Ü©ký‚ßO`”Bö>Òãü‚çâ(õ° Y?Œ«Ü:/K~ÏVL qCnò|?‚w ¨E¯­'ÀI=†¦aæ”SvmpÀxIÖûYS0l«¬eXè8nzSðõ|=‰ÔÛH‘ÌÚ½¡qÀ–?ô°¾øcw×?xs»–2$IR$PÄï7lØ£¼„ËÆØÞËÅÙR]ãd¯ˆjV¢ ñzÍœL% R¢jŒR½,o<å©ÚØa 3¯§(YÌÓÂíômÖå×).ÀéÝcj½#’M}šJå²Vœ çö¬†·”Šg¥ó±?Zët«~`ÑšÖK®°Êy QU®<§¿¤SÞA€TarS視aÝ^Jiô€•Äû~—€3ô3hÈûu°t î”æÂ•¬ïaðÌ}»-¼-ô>•ä2õšº>m*óR[ ì|×´¦(—šàJFÖ|…-¼X, (MwaŸ˜ñeä_Y-7*HdÕÈM¾b«Ë¨˜ ›ùˆFhªT«t§R­ðž@¡–•jÞ[y)Këµn>Þ×¹©„Уrló*\Ù 1×tÁÿ/êbTrÓï76|ÎèÙ²³ªUnÓùÕXå¥u¹u¦†½ŒÃÎýçÊ[äúpâ wŒ¾¡u÷ôô@ÝžªdÝáöüËo¿ðˆ» è¡ÄÇÝ/`é(fÒÖTtÚÕ‡¡ñŽ9ÜVjÕÌ ¥Í p~›p5ŒaZí¤W<Ó8xá3x±>ñXË*¨¹•_?`ýƒÔõ9ÛlüŽKpÓ:€ˆÖ:++YV.D°Ú«‡ ‚†áº9¬²Õ%4¹.Ls ‡64ëK¸šbÀf'µšâÉNâjƒofªëBFay¬F«…Májpí› Ú&nwp;ص!x®'pc]¯¤QéQg¤Ý»ê¿YB#7¥5íÆzÔñÀ¨8¬œÏ»TrUæ®ä\ÑUr&Or•ï-‰S™_„”ZÛep.çR[`UVyáí¿W2/E¥dähkýªy8íÂSN¤Ÿ2~a¤Á]l»oöúÚúš±¡ÍLÁøEœ³ßXç9£g±3gñŠf_u–ÞÇZY¡Ê®nذ7Lš…ªê÷‡o!ÞõW]wùiToÓ•{Dlh7?W=´ƒ!Æé*\^©–éB±GKï5‡JN[Z¯‰8®âç[~Ãã« ½4]Fªû´Ãº³kQ(²w¬6¹eäLš ìmÇ„G.à…ËóWÁÚ—!ä“á¹D ˜ÔpÝÂÕ ê_;ÖKÔ¯ ñ;S¤Lͱh+{`±ZncÒ¿ïUþÒ“aŽÜÂÝ$‹k‡°vV’Q¨Z*`¯fmí2•U;ø] CË߯¶ãÍäòø9ì{ ùÊF¶GL:eÅúMP‡Ì±e±”ïðjiÉ^EÃÃvׇ¡Ù°ó_)„R˜ÑÇxŸxÿ½²uÙ½.|H-l¶¿òÈ—¾p½a¡==ž—ÍjÛÁhìlnY‚*iùÛ]–~ úö¯ÙÛÿêèkð•;›'çM ¹Mβã Ád™ã¬oC+" ‘%‘ß²qãMŸ;lµ+Ø ”ð!ô±À¾IâÎÀ–ð4R¸¯Oï{ñ€°¿M=/ÉO†…’ï>‹K@õË÷< –±e’«Âÿ¸#`—@Àû ?‡«ñ?5]ó‹Ï¡„W²"ã9‘H²_ùª{Jk~jzŸß>Âûâpê¾õP’xkc¿(M{B‰DNïg˜£nví^?ºÜ<Þæ'‘½Ð¬òù]¤·ù]äò‰{øg‘Ý7ÙÎùnô‹È•ª²÷öCÿO~y0Ùû‡ÿÿ?E ’endstream endobj 82 0 obj 2548 endobj 86 0 obj <> stream xœí[msÛ¸þ®_Áo•:M€ H^§’KÒËåÒk}Êt¦¹NG–dÇ=[ò9rœË¯ï‚"±Kñ¡IÉVšë8žÉ¬ÀÅÛ³‹ÝÅø5ˆB¥ƒÈýUÄìrptœgEqpü—’¸>ü:ÈÂØý+ $=» žM¨¢N‚<Ìm09DažgQºiU6¡ßy¤J…& &—ƒa0šüg@?5ýüa0ùã;*Ç‘&3ü‰È0Ic3œxêiIië8•VQ˜jYIÊ×ÒõZ™2¡Î%ë‡ ƒÊãášÉ),½aÒWsCø×ä{7fò¼müÍ‘RûZQW­Þ5$L.á&çLžƒiÇÃ_`µ)laÞ5œw# •äÍ^Ðî —p¨¢t§õÛ¡©µûó j ™ï?£ŸG=ÿço#Û®QÏ!3Xí"‡WÆ×€8\ ùžz½ƒÕŒ÷ÿ¢±†óÀ\@„t°}‹à¶«cÑØû»W…ì ¯< õÔd@Y›©P•†Y*¿cíÁÐa‹.ôöªKo¯`»¢clòÃl÷ u ªõö•?à_Ô€·Q*“î üBñÎy±§\AØpµ½`ƒëžc$VØ7{áÆq‡é²×pÖ!ÔyXS †{Ñ¡0£O@cºeM\ÃŽû;œ=æQ~ë©cO5cß X¡÷Pøh2ÄØq|uEÁ=/ø¿yêß@­™¯®Ö@A» Á3’½‚8`­ ¯˜â±bSpËf ;ƒù[Nlp½¦ˆU#Œ À`{ÃkÚ%éfåZ2Ãæ]Ì 9sÌ)Àcõ¨ ÂyÕÒÇQâ :}å*‚ïFê¬/½ðnä¢Ð^£Ó„˜rÛÎ(þ©Ôƹ¯¼Cb/tÊ…u×c2¢Êîs¢+[»¡šbKUÙ J )bþÉoª¡Ã¿PC›'µ¡¼¹zWLGìyPÉ„B²$e!´hÕg-muÆ Kî¬4:X4.ÖSátŠÿnZÕ[òåö¸c›ØºADª5ól[·Øý4ãàr9ýð^tãlÜ!Vl:Œ3{cPã¸PŸBG«1°Žz¿šèʱ¾ðJ)@$ŒÄtÙOA¸5÷e¥iHEt=lÝ/·¶íO8J`¯ºâ^oG9!´1ß´÷MÙHRít‹míÖ/D©……°®µj”52+I"ÏÒw"M8‰–>>™‚j„O!¯-Ñ,Öµ)øº_gžzï)ÿ¼.àž)!|wQ¼8a¶Wn§ kœ¥Çç"Ù¬Žäd'u€¸çÁñŽC‹å„OXÛž÷i36Í{ídæÞö²=n¸QÇÅ¿§Ûq™3Â^.Úmmø96áËEç!B[X ½?ò'—N[œ¿tÞ›¶›ifRYˆù2rŒ]äˆc0^à­>žBg6IðÖ‚¿M€A‘Aâ“=‚Ũ´‚CÀ”£¡®¸ï`ðÊܘ.öfãZßbÛwÔ¨à’iµx¡Ö^úí”Ä–§0)†5Ôˆ­¶¹«ÂÁl#†;:ÎRSyŸ)‹·î3Åy×™Þ¹£³(´ZëL»¤"5¦”¡»m)'þ<̨‘"§mÆÆº´‘ m”ëÂî8jÒÔÌmd&óý8úH´çjRP”ÙØÉ•ÆklJÓj«ˆªUO.<¤A%™[™ŸóBÐsÏ,›øŽ{/™m±œb¶WÔI’V&l]ç®8ÒyèdE r{Ô‹»†AÓ¤ø›ês3<ws¢à04þ-—ÔâL‡V\Rsþà9ô±·èùƒ•÷WÈœ³•—éR™ä¬ÛWϾ¬«¸ªo jÛFöß¾ ³q%núÐaÉ,ï˜vA©Ó‰ÒŠë¦å®Uwb8x¹ß•ët-“(¬³°• ¢…xa¨m¾6ößy¯$û±Mi|¶äCgURK‡ r—TS#¡¾íÿñÖ:›O0gˆSIœ!šÊ9–ù£›­BQIMÎ(Ï·€¼KØ®jÑáþ~Zœ›<2mº7ã³´E4"“ÕFk” )Z9/|:·fÑY-[Ràl£døÙËaëCdÍâ<¤ÒZÖ¬‘DoÍìsZOaDìvjw¦—z.gJp§×b~ÃÁöŽÙÞÀ¿ðTóØGžõ¼_yÓYmùû\ä.—ŠÄ^ßÒXC²óó3©™|-˜{ßX|„õ}HàK>‡B¸ûJ¯eÒôû Ljñ{ˆÅ’ÿ;äs&³þjŽR*#3º§Ä .|ó/ý/ »‚Öe ÐÐ<õÔ[Ð çl¹7Îèþ³1‚>â0ß@˜W[‘xû E‚“ˆwÛž29¤`ø–Éï¶Å´•ƒ ÛÒpý¯ÌÁçÆ(WÈñ˺Ò8'E "ˆYnï9¶#IqÛ©;žéÈÁ5®oTç>ÈíµµelDC71“Z…ð{ܬgG~o*ªâ¬Þ“[‚îƒ+M¦Ó]ÃJ> áÓŒKO-jÖáîëÚ|­sO}îßz £åÛüx?Uè8 êt_ýC?|…ü!Êuîü.¢ ÞO]Á6V{àTÈ$6Ì2’ïkU*6®Æ¥²•ëû=¾ÜA§ð#¦[ˆOç#µÎMØã“ÌßÇJø>>ÉüÒˆäI&ƒowÿÿéIf‹.?>ÍÌõ4*òãÛÌ»q{|’ÙÀs¯'™mkáñIæ!W÷ã“Ìþv}‡'™2¯õb2ø;ýýyvÇendstream endobj 87 0 obj 2648 endobj 91 0 obj <> stream xœí\Ysܸ~ׯ˜·Ì¤<4q$½•I–7^Ë^Gž­Û¦¥íVÉBæ¹Å—Ü-w®²<)ÄÄ)¼2õ¢ßÚo¥¦”YÞ®ÚIóº‘vÙ|@•š/œ2<ƒúxï›2ÔP‡W¶€C8rdZå8zÔÂS“†’f:÷_ò×-êÐÃWuLšHeÄ7G•:%¨ä(”Jáäè¾iŸ.þ¹e–Ùîo-þüÍ )*{Á[¦A§]±Kc×<§ÇbK=ý:“e"…ÊøÃK?/ÖŸ)#c*ãú’OíÇ£Ù¢Á>Ól¢XÿmYÒczxÚÓžåñs| A—µÝñHòSƒ}y"DѪíí¤ï5C_¦Fl¤¿Ø¹fÖ[üaJsÅ@ð@ …MÁÉ ã&†[¯)lÏ0d<8…_Ãöê0&é¾åF¨6:†u¶*dÄqKÆ6RŒüò/Ú‘~›eÈí[êÒ´3Ö<Üsmõ½/½ù¸¬ôVË<³[KXÕÐi6ý®Ñ“¨’¹UÞ‘õ9£‡¡ÉÑ™O¡'óŸjŒVnªZ[UôèÓ*`)k¬Þ¯P@SfÁ^¨žÁ½2Âû¶§P½˜*Z5±Ü²û½+ê/\ÓÇ*#ªºlp¶“*¬þw?¨ðï»óV&3!"¥‡šyÞÕ-2<}ÿ·V.§ÞznËÚEUJG`Ùλ†R¥*õ©t´éhmR%ÝóºÈxÉHè ±U’ù=ÞÕ‰Ö BžI,™shRCC<€dÝWØ)g 3zC_}˜•ÖçPiíiN_4ƒd²á¢ýëOÝ.1Eë‘8+•ºÛnw5Õ@ÝôžIã½5ýäÔà’¿øÞSÿðÔkÐn/˜ªI~æ|Í·uQªvY޼!ò˜ÈS"ï Éœ9!òtkåd¥¦P*yÙH$¢k Ž%àÞ!x»o=õÉS¤'¡ ›9þ=©-ÊK¿@†ÜBî²¶KȱSØ‹êKŒç×1ùÜÁéÜ€Ut¥Ö‘S”L#Ldó»€óû p gY‹åtyôµ»î Ò’+.E;Å0'„ ˜{&§Óç¬ë—9ŒdÀy: º#Èìzê øZ;rçŠÂz,U²n8ÜÇÖ= Ì.bÝ®áÓ¹áÁNá—±Çg8‚YÌ “O-È¿{ê$yàkD½óÔ¶§èrôˆ*Á±äOá'X¨µd•tI˜(¼~°“„‡„±ø˜ž¢DmÏu¤XÏg_™* "‡ wµ«ŠC%tâ]fÖ€jo£ÊØdcmDGÚÚ²28iCU°¤G»ó$ÂHÛ‘6×<ãVö‰Ü#²š³ûrÞE’–Ò×/A«kM±Š•t–¨ÒWQn8ÙèNd®<›#ãèÑ7bn¿pÉñqÏ3—!ó²röÔ ‡&0÷Á8‡0`2:‚s“ÙJk›c-"Yƒ]"ÿ:ˆËÊ8µo…6æ:³`R¨L>2w†í÷`ë·Ñpp(°:;×K "¹hAá÷ª#ppDs Eý~6/ÐlÛÝ”‚<X¨¡LÙªƒ’•é”N ¢«„Ñ‘h¿9>Q:QÊkhtÁ¸x [àè*£3ÿ E¬”çº5µäß'¸œ;*+À ˜÷†L_ÌzÅ)ºÅ)cRTÙzÇãÞÕ™L¾çþ·'ßS¸6§§’,N<ƒt^ÁèqI_[! uŸ=vÝlŒc¿Á²WsøØÕ°¯Þƒ óÆy¶ÃÁ&a¨8eDM ›N_0î_Äæûo÷vût—êØ¤¸{#·ŸeBn i!²_Fj#0°ªe*æFٓƳéÔã5µå¯¬ˆ¡¬@O¥w*æÞ¶>b1Gx‘ƒ»em©íôÝŸ‚nl¬bs½ W,:ÊlÁ}´F\{ìü>“íÜ8ba P¢Š+ðıù®§P’@‘a­Ç…ÀÔ»"o~ŠÖRá Ú”Es+³ÄäY˜¸éVÿâÚZVT|tP¢It„‡ov¸êöŠ?|cADì–áU¬Ÿçs½Â›IƒUãñÜšNtYfî!³«T¢pçmîáµ Ÿ´,‚ËT1á¯Ûʵº¸§Hó<¸žÅj~ÞѾ‡5?ÛÛ°¶,'ÂÚ¾¥tܶ¤Ù}~ ±ô€<ßm‰¿éÔ.µQ„(Òeǵr§v‚Î}Á6sÎãÜéü¡TîF‰+|ºùüð:W˜Ü(-²Í³Oíë@[Ú÷û´„ýîøÝdèð:—J«n,:$3Ž­ìù­~î⽊æ$:wEP®.€gqÅÐY„ÆdBÖ²SY±f¼Žˆ%3´]öá“Nmg÷–êmôŸšÇ(ãI‡ø©4³ÝÍ0d;Y^¯ÎQ˜D(oÄzV´2ÉëøþJ¬åòÝêY#Î1Ö+œiíƒ5/:K “j¸Ãæ¥ Øãöx7¯5r¯‚eä^â[q­#)%GÕpW€‚»9ú­]þ&}RkGò¢¥ßÝü‹Ÿ _BcbÌÁµz¸hn=èGeãüàY|hTàHÍà…ͳv¶t!ƒœ/o^ÚeU÷¶5Ó<¼åc—Ÿ¢øD„íuî[Ï48R/‰¾"º^ÛÎà ðåóì9ï*NÇ_Ân .¯˜ì²útô½0ªÍ¡<ו§ÚëBƒW¬ú%4¶×…§~?Ò1Ì]A—¼,Ah­ X¨v©_,1†tJ½VW± Le>dÝ¢×~ÎaV ßÉbŸØbíZÌÜù?3¹˜ U“ªwÞõøÚpCÚ-žË(ñ•4¶¼£¢wõ;oáØoe|CîJgYªøtÈú£²®?&›eœÍ¯ |o׆WOÈcvRÆê×v!³Øô±N>–p3“–|<þ^í“ ›j³Có¹úm ÄgnÃ}lÙQ·a×zI<.¾;ÎÆuwÁZ{4ì)–ÓW8p´[Á Ø Êå̈Ôl³±»*Á[eybyˆ1Ÿ}b‡cúŸ–5#û²îˆ˜Š  òÉ£yë©ðã€ü•/‚N@þ˜X øô_ x€ 0È`áawÁÉh›º»«ÿ ƬÁ ÿhö_ØNÂ[bÙ§ÌjÞÙ]Âný0Eä’ÎOž¢‹1èWW¶Á³P5€¼¥oŠýu~|â9ÜO]…‘kå6üé&Ø#Ø wöJPö4ý ]u#~ôýÚÈ Oåžú%&LÚçQCŠæ×ܬcvžA¦cp|,}mK]سK7ƒo]ÇoîÕÛ—¨Ít$cÖ°^ŽÛhýñe9B„«÷ŠÞ¾T>±Hq¼éY¯|58dP¸Š­6í8¹1>Èmå²·Øú›ýó//KÆÁendstream endobj 92 0 obj 3805 endobj 96 0 obj <> stream xœÝ[Û’·}çWÌK*dÊœ0`Æ)?貉7‘,gEWd—‹"¹—˜—õr7*åë ÌÝ3s†Cn¤ŠÉ-—îƒF÷Aük$b©"áÿÖÂb3:»´Ñõ~T4G—­„ûëѯ£,NüŸ¢Ë‹Mô|æJåqn¢ÙÕHÄyž [Î*#“ºçid¥ˆ­ë°£Éì_#)ãTE³W£ÙŸÞ/&"NRcD2~ 1"ñv¢c+“\÷“©…ÆðßÔz?™¦^T9õ€¦bƒæ“©S(Ióñ:ü¼vmy¬Ý<'ÒÆRK¯’ïg¥æ“o&S¥ca;¾së¸F%ìxG"S‰)2§[±ý¬•ÍË:0ÊyµÉ˜Š‚ Lƒ[E’ÚTñ¾-„Ç«a›J³¤³¸P¦iQ I¥m°"•®ñ§ÙßFJÈØ¨ÜyÒl9zÇU»†Z2­˜®ë°Ó¯ãþ÷ý^®ed|N%ùø†CVb»ÎºFp«™‰;èØ0Saû¾t}µ7BÏ'‰[Kärü­«ÌäYÜñi lZÖaý[¹š ߯žÄÐû žƒ2çåRùaÚ‡šDW“·Dá6‰›&·µÛô!¦¬u3X?bª“,¶ÊDÓ*0ùq/K̬•%&׺Ò7"ÇÏ+œ¼_¬‚ÄLØaÅÛ€CÈcÛoýïs"1,F}˜ä±Í¡øùæ®–˜~WCñŽb>…ÔŽ{Zp/ìÁ°Ç´ÃÆÇ‚¹Óy]šB½HÇq¹H"¤[Ï»ˆë«}Ârÿüã(1ÞÒ†»L[{;œÔø­=s­ÆÅ3Þk'º¹Tj-s3Û™sø³Ò“-ÞEìN¬Ãš¶›Í0?juSz½áÀQªÇ U'-Ói}¾¼øD¼¶ɘË–G$Úà)}Ø¢CÓÀ‡ùZ}~ÐE´EHcÚI  xB‚]5Aè&­#×ÚLËhtµçÓî)yŸ÷ga×c"ÅÍ6V¾­(|cÇZ?fXÝEbH ÙaÃg§IE[‘Ýö¤»S"¥%WHC üƒÏvîTˆ¬'¨²°Í:0OoÅÀ&?jvÏÕpb‡^…‰ syȵ6eªVÒgô:U3­:øì2±‘c6BÓ=#×­{†[Ã_2ÞÕd*b#un{ôÒ\=Î&.ç{DzIæ.-Þcï3ï™"±©snU¨œ»4ѹ—ó$wب²¯UÊãÐ÷ºœKù€TK+öûC1—¤>½Ö“-JÓ¼©—?F¦¼©øR“{¦æeÃ~³5*‹¤­¬ÔÊŽœ¸ÕÒ<Ñ„ñðU.Íll4]åü‘æA™Nåu¶á(®Ø™=|)džfÇVÇ[ÇÚ&àG€ÐÈD-ë}»q¹¯([•*Ý=š¬àÞb(骸jïàï:ľµ.¨¨ÃݹH†ÍÆLb,ã¯x ˜²:­·cZ’Ù:)Ÿ]ª4œz)â¬fk>H ¡ôo§6)‚`%Í‚é5øu ú½ Ò› ½ÒßÁj/+©($LeæRA x­"÷e_™Û_‹$F$>’¸%ñvhض~ ñ~h²œaMâŽÄ‰¿À–µ¨Z÷•ϼÿ Òe.ÀØs }¤gAz<äøoü .1'ñ=t’C>ÐHçR…ëkãJöEænvÓÝR_XÑmgé‚Þâêê)¹céü@ev[÷Dî "…”,®ï^À{®{°tFy¥Z‘®egJÕé—ç -ëÄòqPµ¢IÚ$QUG\hvqÇq OÂ](¨«—j ¢+ |ümþzI¨õT¦gpÌØ^1æÑ‹jÝ—/Öfq"MܧÎðÕ¶ïÝÿ‹OéœÂû÷ ú}¤M3Ð+ÿùH}iÉž%e¦ï`ëbhµp Öa;l@«ª·È·>‡ÃüÕ°–ìñMáPZdÎé²:" "¹†fn†ŒGI,ñ×ÓVB›ÖúxÿV)ÕqáoƒDü’¸ÊçwæYê ÜgF*~Ç>|;¤òÖ#(L‚ØôQ†J}½dáà÷câ(Ä{:é¼Y›Ü—!ÐG³ÁR|Å2Pu ÊJXìÎÓØ ÖrLË46þŠÙ´¢²vP ©éÈ^y‘žÎïµk¯gy¥ËÁ[¬qí ¯H<'±ÐÙ¯ÜÕ;‹E®B)1æH–¹Ñ ÔÅ7>–T˜ºoÂFóÏxŽšh•Éî÷ö6eîî¥ÌÉr÷ÛìÏ`¨ÎÁ–e{¶kÓÁêÅC³ì‹V˜Y ½~€9pm늿I4je6 #…ÙñuûŠá7l_}KV±5á ÂQß°/«¦y /£t4ïð#]§Î*áá©¡=œp]Çd'ýçø™XügIjE|›e¹bߎôUF§øŽÊ”†_³0v¶U‡ó– £"Ï!¤³FW\ÀÞŒâsÃêÁ8ñ©pë='tc¥tp~\r®%„ëßÐu°oAÄ(ãaìp­!ˆ×Gž³ Ö¬Ö±ç ƒ…®]ÜC‘ùÀœ!‚0ƒbpcÍ®?ÜGŸjrX*‰ü¤ ЯéÎ ¦ m >„;ˆd;à xAâ‰o`ßÁG¹wÀg¶×=°Ž*ŒÒC`cbƒãç ‰ÀÑ{ÝG€ ôº‚1ÈÿÞIéë Ù ý4Ä ˜=± ÷ß&düœ÷uöp\,@Ûö¶vŽ"þ·î'Ûº$täKwЦýlw /gZµÔ¸ï‹0=«Åï©w¡¨…«CT;CÕÔ%+¡†‚|Zu kªý/0Û”ñ³ö/S—S¬6F諲N9IÚŽá„Ç”rVÁIï´ë´•.L‰„ù»ƒ-‚DK£ kß'|=î½çÖ¾cøè÷а.nU¹€4¶Ý+—M+Ð> stream xœí\[w·~ׯàiLö„ë»ØmŸ|I·éÍV{NÛôAiI%*e'íé/°Ìø@P'Në8ã%n;˜ùðÍ`ÖßÎÊBÈYiÿLÂÉÅÁã—zvz}Ð?ž½üõ(¼==øö -*û_ÿ€Ë'³§‡¦£³®èšÙá냲躶ÔèbÖÔæï]=Ó¢,´ipqð÷ùáb)šB‹FÎÏæ±JÏ×Nš©ª›¦¬æ\TEÙtJÍŸ/–²­ÖÂ=óåbYe)d7oQ÷k3QW˜Å¨ùùB™ß«NÍ/ÌHÊ<¬ôü¦ïTÉRÏ߸ßèá–†ÚÐÓ·ã¬æ)› Np…&X›U Ó­­xÓ55½„+0/¾œž¿]!EUó¡Ž©ÿ†/`©Mßn\©UÕ‰}T–²êøÀ+êCM¯©é­ÿûýÈL_JvvMBB•5ôˆ:±×[-þqø›©ÌÖ6ÚØËáʘG¤T;>ëÄæ¿š*HœqÑvÓBu“15]û€oGb¿3eoø^»©†7èêÚY˜ìàÒšÖ\•n 6é1=…‹Æ‹2¶$¤y\igVµàŽám¡‡q«¦nÆ%ÖJµô«n̼¬×š÷’ZÐÖ–“-Øe×r0ˆQŸ¢iæï]¡ÛJ×Ó¸º–|ëO¨)‚6Ò/‡NU)Ì ìÔã„_˜¿?2˜&k‡i»šÖþtø kžËª”¢0 dð§*j]YИ$kf­P…ìxK&®\Ós'];éÊIoÂÁEg7щ3YƒkØà ‰¯H|Fâ (²NH<ƒ¿…OY·-‰kÐMÒ6¨ª>ù¸UÍ^ó=‰Øö*§³o ÎVp0¶œs¿#ñ6ØÂÁ>øv³õ²—»½lûiëá`‡Ð½™ø˜Äö#7ž¦5§òÆc-F hÚ`æ?üçÁrx²@d ÀŽ.~T;bú»„;°…¯|“s36îNÁÖp¬a<Ø9\NäÔ^l è|u§­uöt€_º³úÊI7NÚ‡c$†Z9‰Q(vèIŸxW7?åTÄ O-CÞm)†ÏàÝIá,iñ@D¦$®ªùZÙ´gÔt Çz^1P0|ñ3uPv)ÞŸ‘DÆ-Ù€lY¬Û`,Ò-U6S °í·nN^¯8‰Ì(î|bÌ—áÏ–eƒNvÞw2þ@žûq‰ßóø'³±S¬k3d¥x¨çsä©åÙ1r¿ ,¹2ÿ[´œöˆú½!aF'ù#Ž»£´qÒÛèWcZ Š-•ÙVóÁŸ¸ß_9陓^éG¢·äüì>ª9ɨ†xÓ{08Mx*ûÿ™ŽôóûlG\?’ ú_"w8P¬IHC¬Ž4éØIk°çÄ'G5ÆaækJ{¦x†@M/t †${û ÐÂö]†/ãQô5xé”Fkm¢¦\h«gðÅn Žéw¡~À‚ Ÿ«<˜ÑWÌ/‰ŸÀ¦Äó6®%Ñ_˜Â¦ŸÝÂÉVÆCo| RŠ(yÍFñècuN¨¦åoŽé“wi`b;óÝøPw*¾4©ÿdBL\{dobx,ë´˜c2Ú6æò¥Ñ@O†«¦UMdåÒ¿£U¥²ÞNŒ®]BÞÇVˆ9æÑ-Ú²@cmëfü¯a$¹ÀލÃExI=Ïýïo{fû`Ãf2ë…ÁÃÔÉŽZ„%ÏJ{OáŒ#H—FÂÓåÑÖØ7`ÛAaKB'ôógжrýÁlE£jÝq‹ÃÝ`4ÊtIA2vu’†0rÆNFð‚R8„&g¦ä¥È½KÅ`'l]ì¾°‡ÃïLc ²ÖüFƒ]ûmø¾Z»Ñ’ìÆ[ù=,$Œ`O ‡)”À•÷sbà¾6o²q;ŽÊ^Ð3 ·Kïf*]Ä ÞÂvÛnyûoÃ}†—Mˆ$!Bt¨ýúÿv…ÁÿÙÙŽBèÇNúÛξĩV@b|œ–zJJO ‡½ˆï©:]ûA{Çè…ŽfÁLùP(ú5‡g¡2»ÆY!/G7ùô:í(Õž êi•—6$J"êà‰BHme±§ ¯B×·=å†n®ñlHÅddH$6EY»Š‚¯â3;uàš„Æ%”9õm-ÎüËâPÓE‡Å´¿8;cŽøÙ–ª;ë1…öN{n ÷bž¨UBe+(âEbZÆ*Î]E‚÷»Cñ£‘t—…j06Ô*dËÇ ŒÖ'nÆRŠZ ¸ó¢.*ëI"["pî R,S˜Ë[Ø_y·=ü–pÝ% ©›æO‘ø+nt€™±§@qW—¿Fìè¹â\\´††DcyÇÉ€kÚÐýn2O¯üƒ#7NÌ’†/åƒ{i Ã¥|ìÉ\ˆ°dzdb¾õP„1–z.µ¿v=ŸB'LʧÜe8nA³´–Ùum¥8X‰ÑpÚB5b2ìGõšqÑíª.~Áþ{¢^^òàŠßÎR\ÇnDÇ;6‘a1v¦½.j™FY¨†÷×+MC†µJ<Í€Ð0[[j·‹~ý NUzç`DQí~0gÂìåØÛn‹3‹oû2áé.`0[ ¾0Ÿ” eòËbÙƒó‰~Ü÷TC& Ê®I 7p9ø-˜ú0;¼Kgƒe‹b¿¤bîBwÉÌ/Þ%ì?Ÿqôp¬klÍ!p¨. QVT ìÁOc樚žïB ŠB?°Ÿ}w˜Í«4Èæ±¬Œ_ÆÓ¤a8 #L HIÊMP¸ÅØåÏÿLùsÖôë9ô˜A=6™… H®¬üÊ„³§_/h2|,¤Þr óUQ¹00›³NÄì”pªÃ^øŽºy1@Tyï‘NE½æ |ˆi;¬¯`m¦êIvì­ # `Ç‹ T¥(“MÅgØ~ÚæhdL¡íF¾kˆº¸¾Ç'Öi:·š¢ñlfB5&zÑq@-ijJ3‡_ÔŽŠÖa&uz<˜Ïôój7¸±ËÀ^`¾å¡]údø&YdŸ‰?ü3´ý[Ô骈kÞ&ã(’ eÍ k•‘\ì3ìÀð˜hò@±0ËZÂOz˜Ã`äŒÀ{GøøÛÑ9Œ‰vrŽ—ãùЪùS]Ñ\öÒ‹ÙÉW4Õ+×)›BÊ^0ý wˆ)£Š V È‚=ENÅYdÅÜþ‘Ew¡d‚Œ¬8~ŽSC’ –7Ž6‘+„_ÐP!ÕilÁÂô+vH}I¾VƒîöÿŠÑ+@¶}Ú„w~\LX¿gp×7XA‚²ã3šýêÙ‘~Ò߬ÉR:'U³,ŒføÄ܈š˜Ã¢álZhwv4™cºâ2dYt­«`ÆWÄo?&ÒËrPéËnXn *P âKhjŠq­€Zº 43„ò@Ò8—’­H”Š` ÇA$<ÿΠ³™ºT”–Š%Ý„€Þ\VÓ¥;U½}F¬9ùñ¤ ÓM俥ç`ú2m±ßÄ&ú͉&Œsסø¼ Šmîʘ cú“ÊGd-¡'d¿áò£to)›6a a_°IlAŒÎx‡ÅÙ5Ho?¾ä7¼PÚÿ) }–£šBbcÿ/è(´ìágIàLð3å#çV(çôÃרä,›šZ,ÅÀ|”i)[˜ðœDÖ§ö«ïÓ’"œÍ‡¥Î$û¾¥hýòžÁ§úåƒrÍ0¬¡mŽ2#ÆLU=o@u Õ8S™ú)àɯc^ï9ÅŽ?H9NâØ˜¼#, zïq!„sf8Ú½#NçYGÁÇ €^w}ozOÀå…Øw7äÔ ~6"•œˆ7¾ÁwŒñtðpyÍfø‚p"WëHã©‚oÌùnW¯ØÝG‘< aÉW ü ¹¤;ç2|jºjVÃÄÑ:ÐiËŽ”6…xÃf2É“ó9ʇ9>YpÉLõ]TO›¦Y–ìqz¥oåÙª¶¯æHÍ÷­R¥¬ }0÷Pí Ÿ£>¥s>¥s>¥s˜øùbÙÍŸ¸Ì|ÿ×û0Ѐá%æP-‘*°umŸ²Ðbð?.ÅêG°:(¶Å7'Áç6£=ð‹¼Ÿ`†'6N¡iyŸÛWõÑh‹˜nç¦[²&i·ÇàÓW«ð¹“îh¬÷ÿ~Î{ß!&Ô†¨¹DKô•PÙÛ•§a@o“?¾"ÒºU•w´jþ×…¨- J±Q‚£Ùsë/ÎS½ï*ܽƒé†óþuÞ®æq:®¶ ?ÌR¸´G»Ø'˜Þ?÷èþÙ;1ôU ¼­Æì[í  ¾wY˜4+mâì8CõùáÁŸÌŸÿQ“-dendstream endobj 102 0 obj 3268 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 28 0 R 33 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R 95 0 R 100 0 R ] /Count 18 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <> endobj 105 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœYXT×¶>#Ì9±Á8Ñsˆ!A,±GÁ°X°¡(*( B@zf†¶©Ó胣¨X¬Ä5K®% ^“x½7šÜd¼Í}ßÛg˜¹ùnÞ{Ÿ3söÙu­ÿÿ×Z[eÛ‰D’y1 q‘áqÂïñ¼«ˆÞa“…yþ÷ñâ”ϔ݋ˆÐ4Àvïð‘kø{¸9R†P6"Qrqõ¼˜Ø”¸Èí;ö¸ ^±Æs̘±––‰^^^n[Rz߸ÍܾÛí=ò#1<*&6:|÷žnóH﨨ȭnÛ£RbwÄ»…mÛ¾M¶:,*|—›_dTdllL¢Û¨yžn“&L˜8Ž|L ŒŒÞ’ï³;Æm©ÛŠðí QaqÿÖHQÔò¥¾)»·® ˜›³m]à¼ØðõËæ´ nûr¿ø+ü÷D®\˜°jQâ®àÅIQa«—$GoY3ÊÓm츑ï¿3~ÂÄI³'O™òÁ´Ó½6yÏH›é1‹¢ÆQ#©õÔ2j>åE½O½C…PAÔÊ›O¹SË)?j5z—ZAùS©÷¨•ÔBjåA­¢Q“©QT0µ˜šByR«©©Ôhj µ”ú€C­¥¨¹Ô4j,µŽ ¤æQÓ©·¨þ”ˆH ¢l¨Á”-5„²§()JqÔPJF9R3)'Ê™šM¹PÃ(Ê•Nm FP;(–ZAÁ(ïhl¶m¼AvŸ®Ó¨+«êÓ)9ÊŒL­²Ž{Ð0GxÊÌžxØHתt™Jy*‹§àFÆ4xáŒQ¤-0"lx’]Áb«¡06hÔU• u2Aãu¼AŒ7Ó) ²ŽFx=€ÆþÝ â(:ENF脦1t–ŒÈ![šI_1”e8‹ Έ«i²2lák¢6˜^0ÝŽ;ÎÇîb,%Ë*23µ 2I9—¦²R¡IáÓ åkÅU–i± jÞ`-w™ÆSñ3<ž‰?µêÁÐÉ ÒC£4p¦%Éa»Œ±Fp0BC‹“ô!Ìà§Ê|„“½ê¸&ÜJ'›g5pàd9v(”ÓG:}^"=½ÿ£FÝIבA¹?ù`beŠ–`ÉXNß®5o"•‹5ýhÁµÉÛ“–.gÉ&ÒŒPï‘]<3:IOò;ù²°ã«ë‘3;ã¡?ާ[·ê·qjêP¥D+׿( sìÚ%ñóHÏ~sîÿltB¿§a«K9éÔâre•k…Zkà¤'_0Ò;uJm+=™®ÌIázQãÐ x.L‡ÉäÛ¼œ¤?À#0ÊÀ2‰W¤÷juêÊjyyª`Ý>¦ñ8¼O€ âVÁº•U&ëö¬+ @°.øãL„‘8•–þ`Ž“Åăް¼ðNñ‹çÀEÀ®`ã.•îCw‡‘LÀý5Ì’ÁDøU¬íNð±Â£§eÚH>¡Á²©Q4_ã‰ðZ7íwýÈÁ“è‚ä©ï£Y¬·,v‰6âp„ÃtæÞ•Ðå›>à°nAx2í¨Iy4ùdâ¾íh»$`åºù¬iá-0„ÄAXøE2C€‰K'«s*«ˆSÙKPÊàKøñv¾ÿ+†87#“x–•ž^Iˆ Wá± [¼Èd¼þdïùy2ìð®Œíߌ‚ú7ÿ€A0Ôãïx(·ÅöŸO¼ß5ÍÛÝ}Úã7¯Ÿ=þ™33Â#ØÖï­w8Û|óVä$ÍäSø×²q\Ârz}ÇŠcsíF“8iüê=°{pýøWgYiƤ—LŸ‹¤™«hod¯y»“ž3¼==f‘U=þÛçr#Ì»ÕÜómà>¨e0öŠ›èZ¦¢R®!xh±p3Gá$<’Ä ”/ÙÔ£±M4,Ä{Å8òmÆšR hFZö³îmøGÞÐÄXÀí)È€0™ëìÞJãk|‘øm:Õ¼…Zî’€(k<Öêûµ•†kÝE=îPE_éãøRÙ†ÓË,B ѲCCÂ6Æ,@’é v…Áx0 ¿Ñ~àìyöàŠZ¤“ès4rEnBÅ/Yû¡1r70»Ë€Ý__ÍóÇ«KØ¢´rE%’T©u„F¶L­²GÚÍÚ ïá‘ vä€<Ô+p'+A³p„Ú`òD´V§ ë­Í!Zë %ôÙE—£" ùñ{°ã¤ÁnÜ <Ägm¬ßjü.ËÞ<ðõ?ÃÇsÔÌû/_~Ýù3z`®FE| „ÈЙ‚úô£;žÎhC0Ôc<„s×0à\“.G—£Ê+ÈUr[wÍM@˜B3ngü*1Ú~¸{õz…:ƒj&HzÈ·9úµÀչް˜©Sh32T& à·ñ—¥ <æ7‹¯án<™IÖäTUhk ?ÍgðãîÍbirÓç“6#?ÅD‘{2L” ;a/ìä8¶àSÀS Ãoª€ä{LcwÞa.¡“éÇw·X†VKæ2#'ûGo¶áwbBÊWü&è’Iß >2Û3Ûa»¿‡A®4Þ:Ãá’QLª9”šÜ+}i0;8™“¾ZÍ×^4µô v-}Œýc CMnå1‹¸õQçO\oÔA9Z) VbaCÚé §ßÚkÌ+8ÈžS!TŒ$d­J“YÌE•û©Ã‘dâ’Õs¹À)L_`ýwyZéñ_Í{Â,ê‰Ó+ŒPb6é²®¤É`& tWç?N»;’Ç­·;ÁZ£3è¾Sa–†4[(æÑ­•þ~óVLázµìAéM‚ëÞò›1©ÙIz,CTÚ=”N1çŽ'fY ³Pìጴ ÎxÒ©jy¥À;¸n_æOv"Íàƒa ]§Òf¦›pz8èK¦×¢Qdõy„˜w JÐz‰ÀÅ,3ïã®1¦'33oqÑ諲ôƒiéÉ×ÐïôþÓ®ûP}ö=jeIA¹`u]±zj÷áÞÝn´ż?]òø|a„¿M(À'¾#ð¹µaÍ‘¹®ØvÜ8,ÁÌcÀ¶ãâ‘[g9,cüWû³ø-Ú‚"†nC'jŸl<˜`4+Vö²“ÀhŽÏûã| Œ:;¬Žcz3ÿØè`â¡1\#NeK‚i’ô'BÆ^:º=Ãö@DZ+sóTHéB¶ +-)**e?©øR߈PMþGr’ŽÉëók\[>?R_!×eççäçs»’R•‰()Šãµi²>zOÉn×D”^¥’œç»dÿ7¥­r÷Úlc ÊJ <23ûà_z2©åòªªr36‚ÿ @©9ôåö&ÉN: ÇÏ:Iõü|^&Ûq$´z’Hý<ç†,ÞU·ûP w(õ²SyEyPy0ã`zmÚ#‘êW/Þ6åƒ'ϰjFêW¡Ð‘ôo±¸éä9Y­¨b¥…•jÓ3-çSoy?»òðXRSÔ>n×Þpý2õd­ª8¡’Ì•X™][;¬åÊÑΟFo*c¥#ÓŠÕŠ ×JµŽ [ÿ#IrLVªÏÊ!+˜-Ö“9˜lÖ Ð–d c-‡¾ÔmØh¦½€rp¶¤a¡ ¡õ•—o|T{¹4£}$¯nèÍ«Cët²Ïj¡$H=Yxm|`ØžÀµæ,‚H¤ödo&w•ÊÈ"v´®êÂS%W-²M{ÁÛ1'–¶~ôì9tSòÍô»$‡+úSI™mêCC©-¤Â8¦ãvð²ÅÞk={"ßR#œb=o×lCâ_¸¬ZØku…Éë¹ÏJ7ãéUnW¬!QÑ}òSÎÝcÐõC7[›Ol­½†ºPÇ–²µ’Ôr²r]û‚1äiÒÓU$²g0¡'VÕ-B‹QОаµ¢}Ñ<‰/ƒ'ýú.Loÿ²®éZOñ ±ÂøXG”샪glçkQáN¼öiùá OÖ)ôòÜü‚<%»%~AêJ$ñ ;ÖÆA=ÄXò¸^åú ú‰à(ñï=A¸&[ª?îÂS¬bß›•Ý*¦Ò¡üSY–R‘­`+ØÚ„Æ«$›Õd²=ðLˆ1:|oLi…k­NÒ‘ür¾J&ÕÃBë‚\GÎôŸV·­iטp"뫬»™õª†ôC©µ»Ð‰ï‚à÷ æ' ß¶ü[…5$! !'ÑM1I Ud9ö0#Õ‡¯Çßq…ßÞyÕ¶§5üeˆÑÌ® ÐÅ«ckãkÓQ£äfû…'ÚC—•š°_®¬t­,×Ôq?3B m²æe‚@ž_D4§[aöm‹ 6ò T9&A…% ¸„Ãq„_´zÛß’èN¥“½Væ,,X itoÞÞegµ„½q’îá—8>°T¡ÒäH~‰1Vί¢¥§—­\½ÐuíVãy.µ°—ÈÛ™×üåÖg‡.]a­ª2˜Ñ^Äó÷BÆâ5—IÓfWT•³ݽÓLй^©cïÁŒî„‘ÿ9ý¶¤Lfdæ3õœ¼Ã’g MÀœ%i…‹hva¢½FQ#„ ¶ØÀsGðæˆ›­äuŒ·iR‰u‰“hì ]â›–òÞº“‘†™ÝĽh¡—R¢-Å# ÐIÚ ó`¡ìÌ)Rô.ÇÒÒÿ²ìÏɼ?99e(·ÂfR¾:‰ÏÒÒnËZŽ–µöÑØ{ˆy»±K]ì6¬¶ôxiö/¯2Š^›K•î· ¿…<û±wncÛýáZ¹I*µZ}iaqA·ì£_£ßÐo¿|ƒŠËŠŠQ©DŸ£ÍfMs}nAd®lyNv*(Éå¾Âö‡=„›·èwüð ü\T€ò$ÙÚ½N­þ¨–½ ¢¡?ú ý¼ëŸþ`»ç”\+G‚¹…é¶»l>.)Ì/ÌG¹.é•Y5•êÒz= ïB­ B$ñ¤Nísb÷»ô’üc¢Pï‘ 5áò’8­¼¹Ô!­®¤¡ Ê?u4µêäµÖ­dTú^œƒ·dÛ¢c·7FÌéèSG:u(\˜–·€=a÷×DµYàeV|žF×@\Q­RŸ\2‘JY˜š†ߣ«I«ºHO*%¡]ÙÓîcÙkƒíÅÓ,÷ðOèT'V0Yº}¥Z[[ÄÖ@‚ø':ÍÔZHfÒ·h´µœÐþÈr‘äCï{™0Û{´ì3ñ‰¿”•hŠKP™D'×Ès”ù*ÿå_Kó”…¹(ÏE®ËÑéÊÊ*Õ‚Ó›E 6eó|ü"ƒÉs€Â6Øf¦ðdÖƒd¾06`ó(˜Äâo_ÊкۇÏh:VׂZPSÊ¾ÈÆ´D2‰AK’ý£7&DF&….í¬oÚ}&å6ºa¾eÁ1’1ßçy JËËU"9ÙŒ\S¢)A¥,8·ˆ»+1guÉßaušUcË/|€1¢ÙIZNFY sõÜ׺¾“H7±Xø³aϧ>°2&$‚MŽÏŒF)’­\§/WW•²ÕŸÝh½$÷¿\æš3n<‡³q¼8ˆ‘ñ#Ðó5B2+-Û!Ü!ÀìŽ$ËPäžØ]ùÉÊ\”!!Tª¾Ôzæ: ¾ :y áDqu±V*QUNyzÑöÒøtBBÞ]ßt&8=G‘•¡Î3ä³Góöˆ"%˜ŒÙ¢O:·3>;m¬Â7ªûHÏ+Î-ÉG.*¤ÊËWä;äN!e™J“ <^çŒy¼^‘—›‹.(¿(¿˜üs.V©óËñtQi‰ÄäMh6:ð^ÆdÓµ†Ç²¢"RГÒö›Ïžj»:ì»9_z¬Y—½•MJËHAr a ^SRR¡e›Î® ÉÃë!K6Çl Káåi…ë‘$5»¶Ò;/Ÿ.š9g΢ñ«VÖŸØÀ)ËòJTH"Ï‘Ë3+2šR¹¶Øs™çIÍËüøä§ÛÚgí)h†á[!}à ‰a– `•·.²üÅzzÞ’­çÙi4Nîvo`à€Q†Ói¢¿ŠÐæ³>i†Y{!½{ ì%Î ä?“Š®7Ï–Äá¤Ë$êSVVR^Á¶ìmÕ7#ɳk“F.œë¹<ø@k§Rç“CdçäÈ3«Ò&rv^H»Ha÷Í_Ál}¿»)B‘¸ÖtÁ2Äøû0Ó‚f &{i•AøWŒ°<íÙd›Qô÷…¤Ü WV`(¬B:t³¶ýPëùûÎì8ÙØÒ>ì¹÷ƒ1c¼çL <öU+}>7rÛšEÃF?›ùóÏϾ~ukÛŹÇX¨!ý|¶jþâÀ5>>ŸÞúªýò}Ÿ´•>zc…ïÂÀ^KÚïwÞ¸ú„3¯¹“ÈK¿8wʆ‚nYQJqR= ˆÙâ33dJF)¥ò2EY^Q®€YEfʉ´OnÞl8s‘ëüâø·Þ’ÀÜé0÷=ûL½ûpÚ‹Ž‹Í¬ê²,~SvJzÚ®í[#dÁŠ[_{õfçí¶MÓr¥9¨°|éÐþ‚ˆç}dÿ²]÷»­Éà 3ÁùÏË.’öÝ»Íí‡OžŒ:l;§„Y$$.ø÷¨-­×šCâ\8ø§£7 øÌ“¡¦ý–JÔ¥¨JR‘£I_¶1ˆt(".~{^Z¡B²P†FUSp¢ !EÔ²ñrµB(Ë’‹Ù]%{>BM‚¦!cöÞŽ5èë‘Q8p”Éø©8£¬ ¬ ¹”#uiqyq‰3É×Kˆ(•ç•ËËðo°Îȇ®¬´ i\PIaIùsÎ/W– ¢¼‚Ü|IoðÎ2ïÒÞà­Ñ•ö†i˜b|Ó"â p…ÄMU•‰w‘LH•‘¡Q8>¿c•YrÈAIõüÔzRdÖ×ÓŸô7Ú}2`€qÀ@Šúi±×f endstream endobj 108 0 obj 6491 endobj 109 0 obj <>stream xœeW \×öžfEB!Ô$Epq©bQ”MT”M@6%,BdiAëÆEË* (È"R7 X¬û‚[Õgµüµ¾ÒWÛ3y—¶ï†`ßëïÿû…0“{Ï™{¾óïœPúz”@ 0uW(“ê˜uáÓœTÊϵʘ Ú•©ü?V'LƪÏѤÑã(¢¢‘B4R¿|¬ñ&^j g`çG”P HÍ+qV%¤%ÆDE«åV~>ÖS¦Lýï/vöööòµiVä.Ф˜¨x¹%¹HV(U qŠxõ§rg²[©ŒY'R¦%D'ÉÃ#"Z3ÿp¥"VIHP%Ë­œ­å3¦O·›F¾f¬ˆ‰[»1IîŸ$÷kÃ/Q‡/[ (Ê.-~ݪåªç…§‹—kb”[R´:Æ×}å’äX?exjœ•µ|ê4Ûév3fΚýÉœ¹DyR.”=eCyQ®ÔÊ›r£¦S);Ê’ò¥Ü©IÔJj 5“ò£–R³(kj65™  <¨O¨å”H­ †S#(eH‰(#ê#ʘ2¡ÄÔ(Š£L©Ñ”„2£fDPúÄü €,¤ zõìõ*…á&á;ý¹úûõ¯ÓÃèCô5&œi<¬fØVÅgo³o‡‹†/~`xÃÕˆjƒ1 \4¸oðv$=2qäá‘Ø0Ïð–ÈQt^ô/£8.âÛ± „¶TÀçñÅ£± ó` 1'ö ‹rËÜŽr7KÕXP¸³ » ™F'óŽøª¨¼¼áü[c"ñ€ÊøÄGL‚›ÖѰFäÌ5è*zÚ~á:2;[•¼iWÆŽ2wü­v©þØS’[ŒŠÍ{_œ±È/a™Ÿ´ø)Mޤ}è¡ ,„ü+xÍí®ÈÉG¨å³fåqESh‘;š†Åxx¹†ãáh* ,ªnXÛ™r‡Þ=hs›3J—õ ~ˆõŒ?q³½ð›Š³²êo«:QªÌ,LÞŸž›™ÁŠ4¶¨LãBξƒg9ø44vfð2¼Ù6ÓØ‰øÜ= ¤Á™oÈ}…siẌ4÷†ì¶™B9ÿ'– üIÃxl5q4V1xß@W ßEã8Ûþ7¸$áÿÜGv¸>Dø, D¥†›€ì¼‚=”“Ðl‹žÏ PÿhyrEÚÖ]ûzÁÂGsàqÒXŸ»r2Ô%(Ã?)\¦ô U #ZoƒøÁûW2ûeü,òY¥&Àb´8•·6%˜ëN'gVâƒêŽðÊÅÈl² Xäo‡ìYq+6¹aÿËõöÊö3ÒHÞ„ë8æŠMaêÿ˜Ö;`xô¾“‰4‘jÅï…Q‚ßÀš …šc|2·»:ç:@N9þ9¶ÅFrËyR—™ax ‹gýb ްÌûÁñ´[0ñ¶.S;9°ãýÓª—­·dm÷®Ö?FíèTjYLaÊžTD²ÃC‰âgÐ/5á³´‘4Â5-¿ä žý{¶–G—¡¾Ì³Eu ™Ál‚˜=„ÊÄ7Aìþøcwß8¯P)Õ$ŽÕnXLâ˜íÝzLîÞ ÙÓÆõÁ’Š’ŠmÚTÜ‚dî`k^ jBg>kS5DµzW8¡ùèSeHÐ2÷PB¸I,þèÅ0ÃþvJ±”IQ„%#ÖGÑØ™öï®–AV‡˜üÚýÅ…EµÍ%ͨÕ|V¨Ú—ž»ÅÞ•|ñW¢X ùzM‡?B–ÁöVómC° ÂÃXì B,7õ¶õçÒ‡¯[A„` îò_ñ2i*Åýty ÖsH˜¹ÆSèê½±–7@ð°úåé+²ó7¾mx€Èã"“ú4h†‘gõšB¡6zvònÿíw§ÀhЧóüûx‰71åý\?ŸDÃæ‡Ö°é&ÄxÉÖ, RÍBºŠ­$|ñÆ¿!ÞàràÉí¿Ú\òMôs§Œ¥^«™¸7(²¼F–sƘ]™Ù›ÑV¤¨RœJiH;½õ*‚ywŽÂ(0à;%âïñ± Ü× Á‹°ÈÑbF@ìW7Áè>ÐOeƒr g>€pZ ÂÁ8ñÝAà]Ôùú_Ñ41Xð’øÑâ>þ ¯Çí)&òy€}娉MÜ“k¤k*ˆ|OE¼Œ”(÷w§f©ÍcרWD+÷J•fÜv û‹Ç1;Ó³·¡,´º&ätâIuÛ–«ˆ…@=…Q—#Nέ”M©^u8£•šÕÖ—µKÅ}u)±Û²wìÊÒÅ1(‘ƒm!QÇéΫk OÚ/ö ³¶ª„ m[Hþÿmá‘wû,_•‡¿ôTÐZ—AAÅŒMø}`‹‡ƒ•'XO0já`!#îëªN‹Ìܹ)+J&îOuû"rƒ9ÊÌÝ’—ÊÂL&ï覂íèÊË)È-`Å}GÀho}¥9jÚp4 ¨½ªª¨¢’²¹²ûkòãç'ŽÆÖ®;äØÿ ~ÉÏàšÁ‡æ :²­$ýЦýŸ¡T¤ÊT¦¥lˆWfà ޽OÀÏ૸ùÌq˜¨Õ\%žH»1)6ôMÆ?ß™±+m2[Ù¬¼[W±{µ´ô]Æ®­„2fêô‚Ã{sî)’]…6ú!#‚ãƒÌ†ù„Ü÷‡´oˆßZ=×Þ'`cí-©Ÿ!Q1yVN„Y`)°-¾« ç÷pyÕ¹‡P) ”Mž9Ù1)ÐGê½¹ ¹çÃÀ é]ŠDÜ÷:­&%ÄÜÝ=Ìbz@]×Þœüœ|)¶dv¦doAÈ¿4´aýÙ°›ÉoQ¢¾úK×[ÚŽÞEߢޕ'–bA¹D|wZI—Mæ=_7¿~Þï·=;+;KªË_7É_aÄ=xƹâgZ@u=i ã«h|йäßä¿à¾ÏhÝ|ñX«6ø~¢uô¢ ;‘ÁÜ@ÿ`h‘$œ¾–Éÿ[Câð/SèàilÅàU²8^¦½¼GÀü°eåßt¨~`k¸Þ¸úo¥a.yüÒa-g28 GÒCݰŸ˜ÀT¡f±?•j¸n:2›¿º¶ýpî¡Ý_ÊÞóYÚ¸,â¿X¿c=Úh†Ù·‹`Ê˧Ug¯H»Zk¿.þŽl‘üüñ÷CèJ\×ÖMΓfµÆkûzE—^´­&¬Þ—»¿Ú%{Šs£ƒ¨&­^][tÐa;4ÏG…MYÍu/íAì…òMqÉS¶ÈR6#ä¼Å;K¼52C¹Ñ_±~òDŠbEM™’+OlùjÌ%tîÈåŽéh;ú‚§¢-¹Ûönfaƒ Ž·Ú§‡.œXT×ÖXwí€ìüþ/wäîËÙ“cv wwÎÁ}ì_9àmá([ „ÆÄÏYs´—¤Ï•à |T[Y`ß`í¥è߆:;Ͱ‚óÿ¨ûMè=|«õ«KȬ©R™½e×fÙ"üÏKwZOu#³Æ²”¨Ì]é;3dθoH`òjH h³[ì—è³Ršq>¸v9ZŠB½–=!Ix¡ ,<!pI|°j^ðj4¹ Sðpö'& &d<@? 0kÉ}ÛÐÓÐyûGvÈ–h |„C¸®k·:Ÿä i· EïY †ì$¦ O:ä„l–Db‰#µ,6x±?N>)¥‚7`É)±%ý‚Ù´8*bùæL_ è}y¹«ôj`œöŒ°ÈÙ0xÃáJ&¡¡RWØEŒ Û´öºÂÈ(Ó8“S hVsø÷ß•ôu~×(uÓ¶ˆ/}ÁÂ)¤€ì5Ѧ‘íÒ•xD¸­/D–,6ºãHÆ$ö‡0è”¶¼n'ÔÏ‚‘Ó}ÞÆö ~?à–ˆGÐdh‹ÁvZÐN€}—ÁÞÀr؃\èÊ%i‘úï‰T~ƒ< § äÄ ¶Ñ`”Ÿh,iAì¥ÚõÞ–žŽóü£+OîÍÙŸ“/#SìŽÒ€6±V—ÂÈþ—§_’¶tUÜE7YÐs¿‰!ŠLeˆ´¬h8÷Ã|ÐRö¡Ïs0%¡…P£Ï·rdÞÈG{Ù7n—0-Ÿí÷ñR©ÿ¬ì°ˆÅ#:‚á‹ÇÍ·Û¥g.W?A·Ù_<ºÇ„¤)¥EWs÷ÖVßj<օ΢#iù¡,ý¸suÑ«l<8­Šn¼øª»÷žLG¥ËÚqöSm|»t½àW`/kló)ÞÎ’W®vð}ã`Ìy”dèÈŸ¿O’öË`BîȰ÷ ˜’Ìó™¼)w¢´¤ðË"Vü¦©±üÜYó×óºñp<Z`ã5ÅqmQÒÀÄ iâH‚#Ã6z­4Ç‚ïÜÀì»5íÒö u÷Ñ tYݰ¢‘µ!oâ7ñk–ù¬r]s¬µ»ýÜu™/®çÄï;ëcWÍö]îà·îDÛ³+ß>”‰ÔGøFmï >Â=&}häHŠúW¯‹¥ endstream endobj 110 0 obj 3877 endobj 111 0 obj <>stream xœ]W XײE@alQЙÑDVÁ]Dð‘]qÃd_GAep‰¢Qã!Æ„+Ê&¨@«‚ 1Á+*Kb¯Ûõ™51A\ªçÉ÷Nê{yßÇ0Ó§Ï©®ú믿ª%Œá F"‘ õ‰Žß¢Ž wt×ÄG‰K+‰0z0Æ€ Fw]§•Ža{jBŒ ˆ±áÉÑææ‚ƒÔ…ôaŒD’¶/gž&I›¬ŽKQÙ._ºÒÎÁaÂÿ®LvqqQEhßßQyDoPÇ&ª¬éÑñš¤„èÄWÕ<º;>^©Š×&ÅmP…GEEG‰ÇV„ÇG¯Sy©ãÕIIš*Ûyvª)“&Mv¤ÿ¦,T'D¤nP„'nP-P‰þÿm…a˜Y æj#Wi¢V/œ—½ÈcýbÏäX¯ qK½SÔ>©Ë|×-÷‹ŸŸ–ak§šàè4qÒä)S§MŸá<Ó…a™qL ³ˆñ`\˜ÅŒ'3‹™È|Ì,a¼˜IÌxf)3™±fƆYÆø2S[f9ãÇLcì˜ÌtÆžYÀÌ`Æ™À¬f2ó˜ÁŒ#aLSf(3Œ1cÌ93œá™Œ3’ÅX2®4+Œ!}pSÌü,q–¬—œ4jß ÔA?L2È6øËÐß°Ò°O:V"½ËJÙƒìC™Zö€³àfs_sì38|pÎàûF£¦]"â<äèñDãhã8ãÆ%Æ ÆÝ&KM´&7MM³M_Õ -6yØ)(7€€™p¯@rì ]—À£ýŸ Â0׬©.†‚9Ýà[ >¾ m¤àÄâ]‚g±˜óg‚Ùf Ë[ÀŒÇ¥ìs¨–š -¨éÓ £§B†ýcpˆ0FŠN,÷·I÷ÉöÊVEGnß™y@«'+Ü›³ï9KêÈþ:ïXqñ5HÖÉGŠÞ Ž0$±À¼T * yc;µØ_E/{dòžkuWÊÖÎWà#ºò— †øv¢Ì=0iQ¤BÞxGF=I)üRÉPùð„ß#§þ­‡ÓÇâôzúL‹'÷€ÏU¢%«õÔ$-'œGص׳›ŽU(óÊÊójÈ·ädRžgª›H u44gã!tRtgÑwápØ%E/’ñ/Lï7‚; J8*<(/–†óÞ—³ÔÙCÔhÎñèbôD¯^d`Ìì Ì…ù¶oq¶2¥|ϯ±Ö+=?™³ª»¯ïb÷m¥ÞÐ`ÇBa" †iæ§©µí"6[» Ò%  sظÖ5%>4HÞ‡¡³{ÑÜÊ•JyóʪêVä|ÉÙþûó^Ûy{zuýþGSw·òC„PDI!8C1›…P)º±¨Ä[h·¤èÊÂî~ºV ö2=OÄ`çÄ3‡G€=uþ‹uEÅéϰ2EJ¥€¹p†KèFÝ?„üîöm5ñµ?N®E õ7‡£ nÇm Gšs_0y ®YJ´f3œ¯p¢[8ïg0¦ÿ+]ª×ÆRæm:œ–ÅQ`6PH~Á”1ß½G¥EªþÜGWβg5i: #~S˜y;õv쥼çb¬Ï?+o”¨^Ä‘f–@//Ï ÎÞÞ¦. ¥Ò[÷†¾•…°‘ÏÌ!È!Òºó¶êØÓÚôÞOœ€†è‹sŸG0yÑõ¦\‰ 6=,*y‰$ëŽm*ÛV°ûÄç¹/žòY÷ÏÖ^¥´¯H/Ðän:¸q eW~ P|j(`Ö"˜–:-?ž8«ý}üHOLÞu¿¶öþÑì}{ó0N¶3s7ÙCæ“€ðwN^õ† `âϯ)&Oô˜Põm0ÊB~O˜@­Ì”É»|Êß ‡; º¢Ûc4„QíÕùm—”î2¿ÀÏqEµÛ8žÍ ÏK¬L¨»’þ€òÖùÙM+©o¨ ï¤n/ëìÄ[Rðfñõµ:¤èÉb5Þ”ÂÍüÈÞçG%øXÈ›¯üŸÁôÞÊš+çO'…)°_¿ò·kÁ\özþUd¼ƒ6„+Ö_\~Ú‡ø“ÈÁË8ys‡ìÿÏwmæ»¶wjH¥¿yÅ™óÉVÀ¿€¡à|3õÇØKʘß3¾Ä‡%«—s##¼Àw5{ÚØzzy¬éì{¡ï¤ðH·šF*¦U†sƒPAБ S*€^s°c!B †lXŸö „qïO™âd0·  8é5gþÁˆ3tL@‚ÖjdÇ¢xªœþm*\k­í•露şr²Ú§Žœ,¬)ÿg¹ÃÁʹG V¸Ïy,A+(Ñ×XK`­xÚ­Êmk¡nõ$JÂcÄŸñÒ"tñR=7…#t¾'â(‘º¥º8~ñ[¶8bYÚ$‚cš›òÕò:Ÿ®è¤‡´—7\©i-ø…ÀH#6?VÞéU5“j—Ô°’œÞt,æŸ ‡gS†ºítݡٞIbˆ&?½rkÙÎ[äypø^öé¼ú²5Tèõ„Š{ú–:—!>=CØÊ÷o¥ùj'Yª³{šÎIÏ;:zi¥3ž%‹ï%YâËS {¼º¨àÕmúse+ù•‚âøíÐfú4´ÿŒìÌÜ®ËbößUç®]­ó˜“Ð@Æ.1 v¡'l(OcHzkh}Âúê1XßÎ!ºùf’µïÄgœüéùme+-ýCCúÆVßÜ£ÀÑl&Ž{:h«‡ß~ŃØVׯ•òÞµ'¯[‚ÑÔ[8f–·fe¤B·-ˆ,à>…²Ìêýù‡Žç–Wä×îr…z±GDìLå§Â£h#‡ ¼w•òst¡ïŒý^R4‚*œL“4ùG®ŽGüáò» 0xM5—dôìVÝj¾(» _)söèÙŠüzÂ}_¿júÌÐUž ÕåßîGì/pØ3gÊIª¯^Ñ1wüä?ÐbMܧñÊb°•¹÷àä {…u”$ €xE;ˆÐÈï:—Q¬-¹èWö2•Ì‹ ó÷Jw#È4=:§ri­ïQ©ñ±¿¾'íü M—D¥‡*÷ß"ä`Yêˤ‘œÐáì0€¿^·jöœàeþóC[nÝih½® Ç1ª1 =´¼aÂg ¯ñü8Ù) Äþž )7© ¸A(÷Îá­Ïa!ÍfM&Gk=Lè7MjŸà(ŒàOä9t$›“ÿvúä±ús–`àp-p¤Ã,4ô>vM­÷¹„ª—;[¢eŸØmßS°|¨nuiP %¤òòß®ÖG/[µÐ?ôëoZë«Û•Ÿã^Þ×U<×;4ÄÛ;¬±û‡†sb¨;#ƒ|ÊÝS0 âh߯~?J~qSò„tU×ýPÕ^ô–¾Š¾ôûëZBn{ÖÌ õ4Î~<ÚR°óÊ3ÙyEÊܬÃYÅ¥ZEû¸ªOÖíUü,,á;ëVÌœºrþüÈ‹÷î×\ºª4M)ªÄTÌ‚‘ŒFYÆÆ06ÛØ„aþyc[¶ endstream endobj 112 0 obj 4102 endobj 113 0 obj <>stream xœuW XǶîq˜î•Iƒ‚ÎŒà‚Š ˆ "¸±gAAP6ÙîY´4Æ\‚FpC4Q7dDãHÐ0è Î@•\M4÷ô¼ß}ÕCnòò¾ï}ðMwW÷©ªóŸÿü甈2D‰D"SÿÄÔÜÄœ”ø8áÉ‘·ñ£ñ£ÅëqúÍÐHFSÊC?›#31239:Ê*Ã’O·‡Bþ0J,åï*]˜‘Y•’”œ£p]:qòdÇ¿F¦¹»»+VüçÂ;1;%)]1žÜä&¦fd¦%¦çx(’¯SSSâI©™ÉÙŠ¸„„ÄÁ,".5q­Â7%5%33#Wá°p¢ÂeêÔiSÈË¢”´Uë³aqéÙ ¥"41i}j\Öß)ŠZ¤œ_¿,hAaFB䢅™‰QÁÞëV‡ød%ùf'‡ú处ù¯_³$ wmx`^j\ćùi«–:Lœ¤pœbçäR ´…Þ•C”ŠŸZ&âÓ!ƒ{ˆ3$oi¼WŸ&Áf4.yŸ&ù'Ýø‡ŠÃE4˜ÁS‰ÿNWë? 6#ør38bÞQâBƒáèZ· «ó‹“ÑȨxWÁnvshË¡O  T¹÷ÈÁ‡K¼Åz‹¶ÊùÅÉe–]^Oàóké…2¡ô[Cù¸ÈH;ÿu½ãÙ½‹ñJþw¯`4×Ç{F¦-ˆ‘I/h²œ.Þ±KtZ#æ×ÂîÓ«›¾Í«LjPV.@,ãŒMðìÙ§;Þý>Ø'ŸEÏZçXç%aX_SýÜqyÕ‚}r#zoâ‘-oÊÁ Ó8×à`¨‘` Ÿ`Àû ¦ ÷Ñp–ã`„,jù/µb>Ù âx@«OÓßÅ©ú¡̽OÓò@C¼$ÂrjØ­†"µe·ºu‹tÖR¾Û Ôt;ºz¸æ"+}w®º¬é† ÒÆ7;c¥ü½Óg¯=°AÍyu‰gÏFô!.6Ð:|Ÿƒ"ðgšPå¶ãù'ò¾JCñ(¡8u]^V~ú¶¥ä#üŒƒÝÇÔ¢S›Žd“™äX›dƒ’7®]Ÿ³>;mÓ Ä,Q'oÑ)âƒI ¶  –Æ÷ f’y¼W'V1ð=oNâÞ<ø9 4kÄpœ?Çá .vØ{kí`"Lè{ó Àõ-ž,ß鎸ðÐÅžÎN¡] Ë*\˜Dµ¤œWªÀ«Ìò„šHØ‹ø­„–nøÜvRpm” a¹´ûh°ÆüØXÑZ+“ù>`p!ÇõÝðÀÃ…×æ‹=¦;/z `qûñϲ? u:1o 5TðÁÆ`&®ËÐpÆ@ÆjtŒ…~õÀç?iy™V¬·!Û˜nÐцá¼N2^ˆ˜–æÍ ZÉ;èÕ9j¾_-ª×‰õ“xÄ¥ƒ«³ »¢‰hNjxpÀÂ${„M6«Ûî}}ñ™/ø¢×¯Ž@8;™Þ¹9¥(=-8(Å“x7ÎX„`-00æfKAÆ)yEViêþpÖ?IE’âšW5ÖÒo¯átî œ /‡7d·!l{AœN𞃰Ü;tmPɉn/FÚ …Jîù­Ùx8²höÔé‹ÁPúÝ#­ù.ðo×.Ñ…gb~làУë‹jRžy4M$Û?…¤Ì<<ïç10ÌzÚ€.')“ãä"Њc—óNm;µ£‰ÝÕÆ}ÙóîĪïúÍØŽ¶ïØN¦¤æï¨E¼Š€h8âÎ;ц@É;ߤ62¼?‹ÿðˆÕ:¾”h¤¾€Ã‰Ø;á|œä «µÝ7åÝ÷ë@‚` ±¦à52wpù›a%ö¶#'ìlOäÈüß’‹“\¥qý^?J'34(A2ȃ\l2$ÇghÝ{[Ž¿óûÓ$¼+çNÄcåÿ ÷?8ÙÅo%°Kkõ…ó}iWKBÈ)_[,›†%DU¼žÙ¬³ñÄ9>Íàñz®—0Ò›ýIGóÛêçr 8M4ί l;EÚ1ì'Ù媦ϖ}]ýåçhÇaY;“¿{ÓÎBÄzÅÄΓ»øøu¢{øh 3 ÍÔ°W-úY½ýbâÌ è¨öPÍÙËçסû,ŒžÝ…ÇÊðµ÷i:š·5½Áô6/÷ðˆXî"ЙÏÕP¬õñÿžrüµa óSÕ†]¤”© h xF¶¹@K¼o'¬³–Vµ :\)èðq=<Üp¾’•¶Ÿ8||Ïw;Ù§Lñ®m;7¡p´jírOVZõ’hq3Nï€á‚wúÔ%F¹®ê5Ò±¾ÿ›ëðFß×jC±€×Ÿ¤N[H ä-¸¿ù}‡ÍI2XÌQNr¿˜ÃÒäªüïŠæ¢Ä‘QQk¬H,9’+ÛPúqé'Uìtz76o[ £IVŽxÒúF{Éþ¸Üã°ßÁô#¨jdý¥Óm÷Ï¥/Ù%û£ØýsËD=¤Ö‰;H–¼Oë!uN¨r[ŠÊdÇ ÷e¡Õì@µÓ*[ÆÍ_‘óa” N \І¥N¸ov¿µô,¢ÿÛ螪øir)¿#­øGlIÚH¥“m;±½˜åŸ›s†¨¾úü·W{lÀrN;¶—á^#_&¤2¬'ÅãÔÖ£yÇsKÖ XvFl‚ƒÌ˜§Êr¨QC…Úò²Žß &*­ä9ßp‡¬<šH¯Õä)xä|ÿŠº•²åMéÔǂ߯` N ~:¥Lö’^Øä)ö@.haNhô,ŸåX‚°5‹s`4ž ‘ÿì®êþ¨úÑI’›„„§s:ùIm¢§$Q¾$Ä™cÝðXo¿ :â\ZkYÙ®/NÊ:˜MŸmØñb“6ï«’~ÆX@Á6¯ ‚ÊD÷‰í$bëf(ï¡õi&³ìž]7*Ëzm€~Ñúé„ìÖñ¸`?±Ï!ÑvupÇ#\êÂ^eÉ!`¹DÚû<ûâêP”Ÿº>=/fSšƒÂ'_ÌüfãÙµ$ð;ýKb+ãýž$iÐeuçjN·¢»¨/ìžCþ°a„´kú±µ'¯ÛÍ ·†@éÎ^ÀS!¥r:#¿,[{`7Q¤|>׊/wÃŒ´Væãwþ®ŒgÜ Œkkد] í 2iþœþ@?ʈH¡šPÓû™¥……Ë>Chg¡Ì›Ù·ý‹%ˆ}ÑPýƒ\oÎ@œ‰›]mL=­ÁW$eþ¥·å`«ÚÐîÛÞÛêh£˜ Åòõ 1DúN!;’Ȉ¾ª­ †/—L¡±ƒÁ+xOr JC¹ä?ŸÛùŽhŒPì?\%ù&¥ù)‰³p–ÄXnõ²2ÑS¥¡¨ìe&zãÁá±%Ç“eI' «P Ëïu"ãØáwg˜saÐ /ã÷»C•Þ®Ltè%¬#Î<ì÷1hñÇk ³ 2R W#Ö'¡î©¼ÎñC›ªæÁpvwƒ¥Œ¿üÔ.Ë‹(1ñ›RU¶où}b›þQé 9Üft~˜›«\Ÿ,ËÎÜœ¶})û„þòûêJb^̈”¯gPrnQÀVlZTðéÚ ‹²R£‘+Íw¼üû½æ£×nÊöDœÈ¾†¡’]_’B~ÊØZ˜•“’ºê£(Ä&žnn©:Ù["×øz÷Éö¯†w”ì!í¾WÞã5ü{ÉJIãÿç'ݾyð†¿}B <ˆÆÍ¼74B£ñ˜ðoƒDÂKouÌ_l¨…cxÖ8[B¨Ä‘†…xÂe’—4Œ„°Æ1’þ&ú£ÀÈþ­¥ FÚ’ ¤f¤j¹Ývçl¢¿ ¿„ÇÛç„G~4ó"¼i¢O\nH”,õzÜ1?äƒbÖ­P²Ò†ÌßûÍk=ÐD:ª¢kFñ}xŸ‘6,:ßœÞi ²>Ò*,¯ï°lá²Ì9T20—sºf*èïÍÔy€£/²‚ ‚Ÿ=m ½x6ßKðÃ3ÉíhrkOC0òWfñ¯`ŽááÉ3}d™¨ó±6‘MLÀÖJˆð4„]îâÀ×D[èé`s¸"˜ÞžZ°cÿ0ƒ…Å:œÌ½ë»ˆ`%Nkv ­Åö DÌHKЧGaÏ阘‘K0s%K}MÆT燕æëÒ˜tWwÇéê9Ÿ^ Nåß“ø˜­&ÿxBuo™HóN¬yËÙß*`èíl"Óq¶ö™̈º<ƒm?Š  Z9¼e!l!ÂzŸ2Q“î¾Nܤ䰘©À6’ºl$ fî[ýßAJÞŠ4oÅFV±+hK^¹~ÙjÙ*Þü~³¤…†ÍúÍÆSG'¯R‰.õAUŸN’Ppúꨘ Gc5ÙúKÁ&©Iñ/ÙúçÑßËV7†œôG¬§É˺Ʉ}11S&:EÿQ÷ò% Ÿy~Ät”wÀŠŽÝe¢ÊvˆmáW Ëõó9'/¯)Sº¼^躺ž÷Ïûi²ÜÇäFíŠÐ%1+–„Å\i¹~¥¶EÎ;½à rÞ{4/'(1X vN‚ÝsU×óWÙ…íj¯·vÆ^@OŽÜp@+†ü.ΰK«\̤bQ^UÈ*Ú" ±KÄ»÷‰ùÏ5œa0½÷»oª4W__ñÏkW;Iɹ™ÝWW½ôk4 ù¯MÎJÜ»}!«¥?¯ßSyàèÑKWN4#öѭйak–+“äÎK±Ã¬•þ[ñÔ‘|*\c䆨K\I(UGwœ¯ï‘ƒƒž>sãÛ΄È?ƒž³d¨'¾ÞGxnüªh©0ÈýDäõuaYë¢Kž—EÊ>2ê Ž>„¢.Ë:-”ôùö‘ƒ-¬·Ÿ®Ú)‘¾ëoŒé²$ÂyrtýoËIÿºoõ¡µçü:’ûI5þ…lú’gV’væÞ‰o°‹ÙØÛÏõÄ“?“ié—vÛ¤¬æò±ĪêbæE¤%|$ÏÞœúiÐÖˆ-Lìæ•Ä³GÞGàÅa/-x½ë¶ãW2ø¡!TâÄ@S7‡³hÈ‚»’MkTà¬ñîLB1à :Â`%yL_h8sˆ¬ôòV Ý¸ É^Ñ5rɾ,Þ—Q¾î|ø©È¾í_¿…I pz…GEÆoH[%? Q¨ÿÛªžòg*Ñå^¸Dfÿ„ÈZ!°sÛ1…<‘Obœ2Ö7ä,‡ð¯ç^TV‡ÞIU §¼þ× ‡ÓtXê³$si¢|'Ì¿þê ºƒ.%|åÇMšÁ=mqt œíÔö³öÖmÍ·ø_ˆ#‡'Ò]y)=ñǾØ!³/èÈë…¥à )gMøæd«,«úû`ù±–þÆÛòƒ9içåcçîÛ€ØUEô{øtO,8–ôã ™ô·Ù ñ!ž6Øê—i Å/}ÀuÇßt¯‘aöÇõ±¾Q±óçG]¾ßÖpù7šHÓÞ s›6mFðžž[×Σ*UÁ.QuŸM3Ìäaö'_ H·ÿCõ…»µíe9üw7áFÐiáøl;i–ã}NðÁý†c-Wä;ðü°ÉãQZYSÜÆÂüN}ÇÏÙuQÐŒ™7_öÝj},,‡½EüClIš¡H¾ßØÏ¼…ö·"D„hÍ€ñå&è‘Qy7áõ;1¬%Q^ØáöÿO58¹UßÔ*ó.1T‹¡}ã—»É1I)§ü5!Y_ææÆÃ)È™%Cnç—·Ë®´ÑQÅ¥M¤édPϺ†°·•_r.€iúª´AVJv&ˆký qý_ÒzþO!僸ã×~ç ù>|>=Îdàj‘s”/š.åQZeÚ3Dõ…™ÙÓ½fæõ?~hD„ endstream endobj 114 0 obj 4998 endobj 18 0 obj <> endobj 25 0 obj <> endobj 37 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 24 0 obj <> endobj 36 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 115 0000000000 65535 f 0000056671 00000 n 0000082732 00000 n 0000056482 00000 n 0000053564 00000 n 0000000015 00000 n 0000003223 00000 n 0000056719 00000 n 0000081816 00000 n 0000079661 00000 n 0000082219 00000 n 0000080115 00000 n 0000056760 00000 n 0000056790 00000 n 0000053724 00000 n 0000003243 00000 n 0000005545 00000 n 0000080614 00000 n 0000078553 00000 n 0000056831 00000 n 0000056861 00000 n 0000053886 00000 n 0000005566 00000 n 0000007141 00000 n 0000080854 00000 n 0000078713 00000 n 0000056904 00000 n 0000056934 00000 n 0000054048 00000 n 0000007162 00000 n 0000010173 00000 n 0000056986 00000 n 0000057016 00000 n 0000054210 00000 n 0000010194 00000 n 0000013236 00000 n 0000081424 00000 n 0000079216 00000 n 0000057059 00000 n 0000057089 00000 n 0000054372 00000 n 0000013257 00000 n 0000017196 00000 n 0000057143 00000 n 0000057173 00000 n 0000054534 00000 n 0000017217 00000 n 0000019485 00000 n 0000057236 00000 n 0000057266 00000 n 0000054696 00000 n 0000019506 00000 n 0000021967 00000 n 0000057318 00000 n 0000057348 00000 n 0000054858 00000 n 0000021988 00000 n 0000025408 00000 n 0000057400 00000 n 0000057430 00000 n 0000055020 00000 n 0000025429 00000 n 0000029259 00000 n 0000057482 00000 n 0000057512 00000 n 0000055182 00000 n 0000029280 00000 n 0000032881 00000 n 0000057564 00000 n 0000057594 00000 n 0000055344 00000 n 0000032902 00000 n 0000035147 00000 n 0000057646 00000 n 0000057676 00000 n 0000055506 00000 n 0000035168 00000 n 0000038091 00000 n 0000057739 00000 n 0000057769 00000 n 0000055668 00000 n 0000038112 00000 n 0000040732 00000 n 0000057823 00000 n 0000057853 00000 n 0000055830 00000 n 0000040753 00000 n 0000043473 00000 n 0000057916 00000 n 0000057946 00000 n 0000055992 00000 n 0000043494 00000 n 0000047371 00000 n 0000057998 00000 n 0000058028 00000 n 0000056154 00000 n 0000047392 00000 n 0000050179 00000 n 0000058082 00000 n 0000058112 00000 n 0000056316 00000 n 0000050200 00000 n 0000053542 00000 n 0000058175 00000 n 0000058206 00000 n 0000058250 00000 n 0000058624 00000 n 0000058645 00000 n 0000065224 00000 n 0000065246 00000 n 0000069211 00000 n 0000069233 00000 n 0000073423 00000 n 0000073445 00000 n 0000078531 00000 n trailer << /Size 115 /Root 1 0 R /Info 2 0 R /ID [(£ôÇ^‰ˆä¢ÆýR)(£ôÇ^‰ˆä¢ÆýR)] >> startxref 82933 %%EOF simh-3.8.1/DOCS/pdp10_doc.pdf0000644000175000017500000023336311143604424013646 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[wܶ~ׯØÓ—’=Yš/ û拚¤ImW’Ûæ8}%YNjKŽ¥Mšþú ù@~\®œÓÓ8 3ßÜ€ÙŸ6e¡ô¦tÿÂàâÃÑ£³¹¾;ò7'_ŽƒO×G?uEåþóp|ñaóäÌ~ØotY”õæìíQYô}§ÊοWÕõ…®7¦T…}ýáèuö2WEÙöZgÏòmUôºnëø°Ê¶ùV}Ûh“©|«Š¾ÖªËJnr]”ºk³ÓHè‡ðèƒ{Ôë¶ÍvùÖrR5}ÕeïÃës¡r?,S7uv S?ù±šJz妩Mv'€Ö5¸Êë¢lLåøüçÙŸš®h+­®¼€.­`3a‹vÛö}UwuJÕ+K,N¼ˆt·Â¹Fù÷‰w^(µéí·QO¥Ñ£žÚÆþÝ7›¶­‹Î)Ê~sö£WmøBy~ëÞüÞýÑèø×Ù^gO_º3Fe/òm[XËÆ*¶rJªëì»|Ûe©KÈԯ󲨚¶-«ìKùê+™pf÷ÓFµÚ®ç>&tu3=κOeîñÈb±[j“ ú?ÜÖ€î;K×(m±pGÀ×[;SÙÇuoaÞ¿·z0ªêkÝú‰3Ù/y_˜®2…ñÖØG¦ÑÙMüøÚ*]&ë\Ø©‘FWeeøk®L¡,¤,–íȪ¸³”ú@î ï…mXäF&ÜÒ¹À°gèŠòz.O?â0r,R‚@ëÎÑ‚÷2¼®XµM‹,Â&îâ„S Sö ÿb‡ekLg‘b±¨jÇh=‚FëÚºˆÚš·fÇÃuÕ#7;ÙžÕCc'(ÝϤô…, û{#Å9ÜA(èxÔ»µž„jÜ(|н²ÀÁ%Õü…ìt'O?XóªT¸³ @!®Oe“·H Nýã Yûÿ¨mPÁvtC—£»ªZgÞ <Î41µ·º<ãœî±ÄË5À}DÄánaäMà̹B ²Up/ì½òOë¶ËzyÃÊ•Ò>àŒi™Pâ0òÕ-`;®;x»ÚŽfÊp*Ë€p\Õ73÷gµ ïÇ€ÓöU•·áÊxY~C% ÞçÌPÑMP— {ÿ˾·ñÑÆGËc%¼XÊ6¹¨+»å_smÓ-U5KÎ?ò8¶·ï³‰Ûú>—0OQøô²Ò€$Ý–Ê!)Pøl$ÅáÉè—ûÄï‚ï<@“Qe©&ãŒS›³¤JdþiY‰ó,Iò!É€»àL+31~÷¾ýÀEpظÖèAà ŒÀÊ•lpn^…wM¿?§²ç>Œ+òª„EîkYz&Þ{ i sº¡kºÚ”îRŠÂ£ñU4…êcVn,’ú¸æ¦x.¡Á&èG%,ŽyYŸº®ù3ž¬BÔç¢VFg¦{ÃE°ªQžû‰‰‚ˆlgº1Ÿ„¿²~Hë”­%ÅÕ¾¥áiÌàmaó”³€À£9Ðõv|7$If¶~Žú„(Ê’”$ë‹ ¡ñ´w¹Ò.nôPþJÝ.f{ìiÄÍ*BAÚ¢¿÷áóIuJ#¬¿ZüήÕkÓ*ëþC® ëh™ |®Ëi¦Nð kaxå>=+{¦úƒ%gï£7D\ ‰äýi¸.;À^¢‰ŽZÀtèI‡ü„áIe¶’ùpN¼ÉÝÓY²öÅ0‰dἲኞˆKm°£ÖLK˜ë9Ì«¼ÊŽ9ÏÛhç; äŒõhb°Pbž¿ê%¤&d~2A¨·Ò²q‚•®b•–K r”ÊÌÓôM³÷<Â?ÝåÌU­ž$rÏQ/ùY,Ñc3¸Œ‘ÍÆ]ÓµU("ôW¼Þ? ObѨG¥¡QŒÑvGo¢!ï)ŽW*Oj%{­:Z!äG; ‹UUÞ AôÐr<'àÇá!¹ê/7;ëqÆ ááEì'ZàqÄFC§1e1·›—L‰‹õ†ìª:Kó‡å~ ¥[AÒ4¥[?ƒp=Ãþ]‚Ì|1|U•¡i žåƒO…†…ã˜fÐÜÚþ”ok;t.è,7ÖM¶¦Ëþn6E×–uö8W+ïâý¶0$ä¿f+däÖá„6NüMz3R]iŒëD™¯:>ìûä @α»3 ÑDÂ$hj‹ß¤µa]Ô}ß8A4¦è+ÕÙ/•¶âé¹;F‚¼ŠA;i ß[¢•/sƒpu—ƒ0|·ð\Íïâ÷cW@oŠÆÄSB®fXB?4 Ñ8óÖ¡=þx‘=r†Ù¸èó¢ú%ánMfžÊÌSBØ2 Œ`¸oëucaöRè~+f{MÀøâ…/,jƒ Xá•<P§2¾”½fžDͼ¢™7,MÐÊçr)à%<^N¦²àHlèÔ¸j\¦-#1G8£#o^žAMî§RG²d[xÏ6âñvÌœáSAÖWòôqœ gÀ³¼2õ {4i Øøn:Óƒ/—ð ûã:>ñÑ£Ô-Õ'±@un´hˆŸ¶œÉr…ïmÁsÀR@:>jp^FT¿JpÜßËÈìŘcÆúcl[ÚFît¸7D…é=§íޠ޹#qOAô'ksÁé‹FS(ÇÇ EÏžS§Í×&'ÞyÌ"*b½`ëôíZ€òÖàïÉ ÿTœÅÖâÛ_²  "üf—kL–# /> stream xœíœKsÛ6Çïú<’3Œ÷ã˜Ún›NÒI]y&MÓƒk«NÛø‘Äî´ß¾ )a—Ö*’[$%Æ„xQ ö‡ÿ.@~Ì82ãåßú8òL•ÿª 8}z‘}3‰…È 6›ü1â,Ï]ݪȬ‰ÿ&s‚3 \Œò¬˜üU׉}§áNÓº¬ðk.бa܉ cå±VǶòŸ‹±fÒKíò? ÁŒó1ÿ¢{f¹s&¿…ZRþI¡YÐñb~SŒ%3Ê(›_AÉO…dʮʞæùßã8PÖcQWÐèêŽ÷g“Vå Ø­¤5þ©“Æ›æÕß&?Œ¤äŒ—or'Íænf›“ ½uÉræ'˜õ¾Õ©£…îJRÉœ6¹ª¹¦„‹4,Çi¬‚`Žûl,óõ`IίÎgVŠ Lº*e>®@ëµÄtç¶(üN¿O ,Bù-…òàswìé |VŠio@€î  Û&íšÐUL±ºóy\$iG]‰W¡T,âóýü oóãX@fmš=] Øö%۵ΊºBÄePo;u·Ÿƒ]¡îFºmXÖ‹XöŠq#WcYRXF)Ùó$o§XÈFV;âÕÖy¶É– ²CTX½)*o`¤ÛF1Aeûp*+ŠÊÇ –/¡Ä|‡‹· gÁX]ÝF¢ÍóÂ0§œôù”E°¿¦bÑS*€QF-|­³s%Fû™¬ãñyȰ›LV€M1¹mv®…QÍ-³Â­Æ¨¦0Š#¶@1´ÍvIo;!Tq™0yF†‡ßå©Ü/já]QþTǼí?·°µU×[qž‰0„z~>)J{ Xó^¯¬¡;‰ÉH:o›gÓB¨@‹H™¨¥PàUÝTÓŠ<²S÷‹}Cîš]Ã5>«¸ìÜ<\{ƒ\÷ð}4»"`[o©I#>¼ Huú:žJ.AµcÚ+W>)Úe Ìå1êõîpŠcw”4wrž_ŽS}±× ðl©p~öK,7iÍ0fÖ‘P\W+…uÌY³=öUáÖ¦dµì®’¬ÐZŽÛâKkǸXC1ºÞã T¸Mù8­ã­¦¬ÿI­ª)ÅÓÍX¥¾\rË1k¸mJ>ÂD¯ÎD6nõƒfDA3OÐL1«×0zŠfèLé[„˜e8C8š;»$¸ÐÛÑ­Ž~uðÇ*Âàô?âÖ«ä“/ P'xí×’³>§ Ïþ-ãVÞG©€({‹˜W‚NFÒùÐúd} Ƭ`=:?:`®˜ûzŠ} æ”bF/;00(Ì澿‡h{òéŒ:/s“ØHñ4J{—H©ñÔ•†¦ö ä Û<`Wk¼|9€Îw)ÿä#"¯ŒêK|åÈè³¢|µ"ßPù>ÝT]ÒzÙФÈçNø®–Ïã|ù§š/*.OÖ¥ÐZˆ3Mè£ß|GÜÖá[ݺév]î¬oçìœ/†™fÏpãÚèÅdýÎéèèuÌø$”Y4­²zãu y%¼/»ð²eÌGºë¦ü <ô›deSŠ˜DAªc0 xn¯Èžà7¡9O%/âfwn ú”6X섞$›"¼N¡H‹§aiñ>¬™iï¢Å¿„l´®ÊL4ƒ%˜½ˆ¨(êKêÌ1Œ6àêëò$ƒ‘E/a8Ѻ…Vh´îVŒðŒÕútÜ ofpaºÆ´GõfºÉ‡»0«€/ê™7)ª,I9ƒ’È@n4°Lz¤ZX{@(©k2ù_ »Ð¨……+ó¿ë´÷V­Té¿4UúVnYätdܶ®íQ·&Øì½e›"À†Þ°E€hÈ6 Lhî„’KôiׯÎ>áÞ¡Z÷8¶_”NkI°º}=±:\ì#;û/~R¬ÃÄ`m€Rï¡ß‘@4âÞâ÷´´Ì s/îi‚{hîY êGH"):„KÔòRÉè÷Àû|¿„qp”÷T½ˆP>L$ÝöA°]Qa T+Å£® ûC’‰¢BX¤X|“N\݉ïÔ]¹AâyÏ\ØÑokõ Ë] ”™ñB̘miÔÿkÏ™1 —R5Ôñ¿™C&Ž£÷1‘Gn„©ˆp¬!<ºª,ö让fOVø‰(ÿ’”f¨ì‹„-DÔëm“P±š3뼪 óñQA¤£f7›Qß,¿oÉ~r¡óïh’½#ÞÖùÖªŽì´¾µz8ýÿþ¯Ý™îendstream endobj 16 0 obj 1763 endobj 20 0 obj <> stream xœÍY[wÛD~÷¯Ðmö~á8À ©á¥pzrl7 Mâ4nÚÀ¯gV²vFò:q#;¥~èdµ—og¾ùVÚyWp&dÁã¯3fW“£SWœ¯&MsqúÃÚ¸=Ÿ¼›x¦â¿¦Ú³«â›) ¢,ØbúzÂYž»vVQXS8Á™ƒW“—å´ª…eNXY¾© YHíÊ‹Jƒ¥‚.WUíçÂÚ²¨„dZzQ^UµÔŒ;åÊElt^Ú¸LÝVµ‰Ãe(ÏšF%¹+¯Ñœ£y‡&™«€Ve¬åŠö]¢9„0¶¡]›îÔÖ—ïqº¸5:=yô§_ Y€«Dœ'”ßÁ*Lz¯My\)ÆmкüI×“æ¹ .–ðºmt\£‡.Å骱qoa!I6lõàezNüNvHHbĪ¿¦?M¤þh”™Î'°þôo`•T°3q* (eƒcÖGJuB!L†C¬cA¶,°3¦±!ºÎHÎŒð勪VЪ´‡ýÖXB4-ì˜3îµ€_³e/Dð¶ÜvQÞ¦kÏ ÑLj f4‡Ý-±ïm •“ƪ¸^ö}UC.H!Óz±•N¼@ «n sD¿í’zÖHÀŽ~–ðÁϓ闽\Ä"ñŸa2.syw“IàËd-rSöH9 ôT}Ò§'U ìã’KÊÿ“5ÿEŸÊi.Bå¯p.ðºT°À?•pLè¸ç4è. €l•thòø=:§dK¯19ÑO×[R>ÍõÇ:eEù Œ‡(r€ñ »Fït~±Á·oKé`›*¶y¨Y—†/)”ßQH^ÄUŽ4‚çGìÿ+¢"³ØWgw|Ö÷q ½ ü v }!·t$¿Ò[¢²Ì†‚Ì»ƒs""#óQ$ Q4·Èã€ñ9!åõ€²Æn*ÒÁòñ㯨¤A .†¾^Ǿ^ëC$f2em‹.ìf­sït þû" ît†ê8I®£ö¤w¾ ŠÔ8šJiÐãö¥ =w‰sÙ0hÒ&=•# çÌ¢y…³5k.³ž\  uïQŒO‡6ŒdOjú¯° D~$zGþ¾u/]Y™]>K0ÈþÈ®?T&ʤ1qU£j–³´øÛLj°‡lÒ€¨ÏM– qQÅÃÇÝM^@äC¦9ÉAŸïãÈý¬ÿ€2vopFïg+{Ä h{J„ñº”C6&Až PûŽÛîê°ï•Ÿ( „ñ4Q8]?U¶“³yíÚíu+͆_póda›HOÖÑšÄF6ßT\øT¥ñœg£œÿ@ë½Ïä¾IÊŒxŸn(±w8ài¤‰¸ft Góv}U$7h¿£À=žNO™ZñH"£dvÔÈã`¼CI¾Î³A}àÇxƒˆ¶Ÿ~`D:ÿnZan¤»§¡š=+ rñp·›{˜É¼Ü«]ã€ùÎ(¢5þ(YĢþ”Ìß³þ€Ìc¾o«"$L‰bÏŠc軇è¢l¼H6Í¥—î.=÷ˆ…ëßÜUÀ š"ê®áå)"ÞyþH’òhb$€2‚4&ü\X6¹¼G£4;Ÿ„æŒë¶ø$³Å§,>ÅÂÞ`£òeêJ¤xÅ{Sñͺ©¡ù>&HÒZÐ"EªÖ³VÝ0Yî^v}d«Nƒb~³5¢“Tá\—7œ—}ù@é×/·|€mT.ãóO©\’UúÅëa­`Ë…~ö.€¼9ÝeW]d¹/ï ÁÛ-qiI]-øcâÕÅúëk]uøºÊÝ÷J“X:Æ›*mÔ±y¤²q*ÞuÖ‡d]$k–éW$ë:Ygɺʌø³Læ «µ)mã4/€¥t¸ÈÌy—¬ËáÚ"DíLæÍUgvIÓ½@5¾:žN~ƒß04Õ+endstream endobj 21 0 obj 1449 endobj 29 0 obj <> stream xœå\[sݶ~ׯ8o•:9‚ ™v:c[jìÖŽ]IvÒ&}%YV­›-Évþ}À.À‡ä±åé4qV<ìåÛÅb‰÷‹<r‘Ûž8ºØØÞ«§7îñbøpºñ~£Î ûŸ{À飋ÅÃÓQ–‹&kôâàÍFž5MWí¨b!r¹¨„ÈT½8¸ØØ\lügÃü)ÍŸO7þøË棭e‘•U¡6_êeGIm:,…yVIK¹4C)Nþ=ôÚ”T¨E º7Цð¯´äK"D~"òŒÈ["ßÂn‚ÈgDþÛ^ù6¸ ò>e#| ò7OZÎýûào–íÊsxý P?Ìàúuèu¨Ó@ê2ý5YÛ‘—c¬fÝ~Ý„-ØêáÀ7DžÃ¶·pWp0,"6Ø5|ñ¯[@0d/Çöd4E2/ïI¯u¨¸$¶Žã±Õ1®1 ý—ÿÿ,Å`^ ÌÝEW •Éf@tG¡×â øõ0jÕAèò$PÏfhÕ-Ð¥‹@êÓÎ"šÆ^ ¾ j÷"š¤T¢ÊêŠÏŒ:‘'€"ügÅbÆ:ÌåvÆ 8Ø áxlÛðmDˆ N é*IâŸ3t•zý(¹Rlî{DþLSËé©„ücî‘)þä #ƒƒ1^3Œ`þn /á`XM>Áùâé|€3ûuã†Ø¬~'0ÿ_3DÛï5fQ5Íb et¹†­dÔ™\Œ{ç€þ¶Åä¶e“ü<C¸ó»ƒïÀ<Áê£ØÀ+=ÓS€¶d·>\nÕDçU&T5 &ÓFâj²¦B\CVâF”Z ù½hù«­„zíL\>—8Ûƒì) IØÂðýrƒÒ—:!ÏÂØu¼˜Á9êõx"¾°¥â G¬í9$GtÍíß ìv »á ólÊ<$3xìXq·r½nÚ‘ºnêYÝ*§)E­²JÙ„ÀÁñÆ/>Þ«ª¦Ó¤¥¬tVÅbÙ•mD:ò,PdSE ¶SÛµ¯}¶ÞÒY·Žpîz)ò:“yãÖn!É.ž¢ß$òUÌŠXv0Æ2†ýþ¬‘1Š¥‰˜ –°ÛD%qfÕWUkà¿)Úž£ä¢v{ýÕæ¢d3æ v‰| yò2b2˜±‹GXDßR)½ñnï òžËBg¹nJcÉÒšqHu–’mÂ…Î*¡¥Y»Ùl i‚«“@- U”ZçN ;˜R¼1pi0¢ª„ÁÐÐt¬=ĺQkUZÝj²<—¹4–ogUȼ2ÈÈh0 ^•P #BƒK÷´(kkö©Ò5ï#z/›kÀÆ2œ/ÍÄ„Ñï–µ56Uº*¥yoèv¶¥,Ù¸ô…Ÿ"=b,òOgb‘'1½‚Óygöœ#Ô ™èGi±&9wUŠX.æwcíëãG zEÁa EÓÈÁ ÞB+bm?Âà’Í• {Ne«.\ÎŒCMaX]ÆP37)Çìg²|$(Š1¡{†0½ÑX­†ÀÓ ÀìN\ƒŒÂ[= Øê<,ïœ6CñÔ,Èt‘Ó ¦Ww4ÒÚñ1·ó"¹öF:+²*²FéÀÙcˆ¸þå«DY«‘í~£,IU›àP “™R[®z‡2«ŒË¢.2%š`^\–Pnóy œé1ÖçS½‡ÅB3ï<¤ää¨ œ±áÌÈ'JK0«Hìö-¬cÔÞ½ŠÙ*à æmº¬ƒ\nØ :‘ú°cóvt¦“¸@<Ô!g‚Ë^\8Ú‹Cò9Má­]ê¼´ï.•œÈA~‹9IxBn0Lé Ôù3†ˆ„r¡å¨{½âbI ãpFNpñHæC ’³·oçK=0|ЀÏðÞ{zïd›yÏ|HF'¾¿7O¶#2A¸H;p²$. ÁRLî\hÓaà,ƒPDzxÅÎ' JýηTSBì0N­)6.ô¬c¿ßrçpf=|ÿÏH>Œò²+hì!_C¤JÞ½0Å+Ë/·)±íNÖúÁœÌV3ôGÊ»ô¶0N"KB~Žc½ø°±cUJGMC¡9þ7Ñ(ƒ¯¡´N!Wñé8ëö:u”5H8„qÓé‹NÝÞ°¿ÜôeQ•ž\…ÒŒ¶ÕÌôÓŠT̶ŒVa•Vc Fä'½¶OHfÅy‡ %W¤3Xùâg†4¼—eð°}èÃPƒ„ƒÐ+sáƒ9lFøŒ'Ô°-ÜBõÄ#0EÄ©¾¤ðê•ý³W䲆g]’ÅjJ8­½„«1ó–M&”Ù=Àz±7©7lÏ)Í­êݰL[ÔÇþÄÇí4µ¯ºÊn‹Š™Qжà$;ÀŒO›”ÊDY'Ÿ†D'=CG6‘á·€´TÚØP#ÜI†êÊ·X]E{Ê[T%š˜v2Ä@Ö¯Ð(b?¶2O´8²(=c AÃ)mæ?HeaÖ¬£# :Aøƒ H¬Ú°$î«\E'ÕSt¼gi02Šgßu§¤•ù­¾bÔ+~“„ÁjÀ'8žÃU9¾¼¯àE(upüUÜÅ/”L‚pûy¬ÊÄq$HV˜êyPÕÝKZ‘–D©œó²0¥‹L–q©ú—Æ:ðô ¦²§× V:Ú åäQ4Ò‡ ¿Vð<ÄÃ$IbÞÖ„¦‘œ*Þ_2-îvÎfh~ƒÊ$•ÕE=ž¯ šêñŒøõ}—¦ãq‡]]×àE<ŠÊç:¿Á à„°u2ÒM¨®D ØQ*ô>T*e,§˜,ÕØû<Ê5ˆ·ü¾lÕ¯GòÓ¸è0á¨UÚý¦é…¬ñIrÓ+ZM©( žõCt:JÜÞ+äBØð®NkTwuš.ÍßM¹¥²ŽfMrk™gZèÊlŠ2˾²Ésûqoe+1­KXµ12[q'3]ÛÀð…íÓÁó—íCAÖÍl2¹¸-¼Ý„¹Š.o[ç‹ÆGýR$÷ ªXúª$b'>}Ç.ýL888;GhÌ*þæ}B’”YQLKöŽçü {Gws¾Y@ ƒwbQuT¥Š,¯KP¶Š½“}ÝfŠÑN“V€+¢@%X)f1v´¦<:†B;ú‘ø#̱]0Ïšⶲ;(SŒ†a7ÀÚÂ+WÚ‰Vr3ÙGó&[YŠpë‹{x™­‹£kÑ/9`e¬q10ÿ4a5à³éaOŠy¼"»ù½ð…1µˆaÈëÈ­uÈ&“¢(ý)Zc& ÎSÉÚ‡+êpòî¢^0ƒµOЧГaÞéëLá g~ ïPúCì÷Y®yI".)£ÛB•…—”Œal•¸aìlË‚I—õñMG ÅŒ¸JµŸ7ÿûã;Ïûôø.΃åY¸J‰]9±> âÿø4 ÿ+o‡> «î~rAþ´Ýj>È®8c·4åÔÜ1õ #õg†®ùºÎÈÕœQ߀1OÀ(ûÑÊY—þÂù~¹ÖõM$NWÓ±7?쎖¸raøDv»%‡Ý¥ÂnMa&âKóØEøN6.þÒ _×Âºá ’ñÝ/7ðáÅ-à|ÓðE1ø  è «.Ê k-C:åòlÎU¶ý›ÿâjü m%¹¤FÝëŽ~Crâ¯ô…Ç!ôËûaš)Âznwª©µýN•Y­sk"öXš˜Óm²¾¤¤ ›¦ïKzEóIˆÅöcº½Ÿ’M5”·ÝÂÚM•l·Xi½ƒ×Òî6„²>¹C5.xŸYå¿ÎÇf8SÆ&ù}?êiüeAÝÁFNù§¯âÌh¼}Ð÷9¾ƒzõ­ž ÏßB¼a—°1œc’Ýö¶e¯ÀW1ãP°í1$ñ%—7dãŽ\QU¸d˜ùåqjl<‚ë­šnÓež§ôr!ÉE!)"âb_‡n„¾,Ï·oó6D¬–9dªlb79éÕ(ö…cœŠÂ†w–îªü!ļ QPæÓç¬ÃNQ+ÉÅ×h5é!Þ{}ÿÕ»Bg˜kí}£4ÓHÅ-æ\˜ìè-«‰\¥O"Ž–²ãå®®Ìߎ²†Ÿl$_1ð|‡/êàoÙ! #WÕŃ»ÉT‘gѧ˜O"$)ª`{⟘!ûIg»ÿ0ÿþ âÉendstream endobj 30 0 obj 4220 endobj 36 0 obj <> stream xœí\ksÛ6ý®_Áo«ìT,‚™}Ì$i6M'Xmw'ÛÙq$ÇIkINm›ýõ)÷‚<(ÉT×ÓÄùpMâyî ßQ(d?µ0[Œ¾|©ƒË›Qù8xù¤>\ŽÞ²0.þ•¸<[§¦¢Aæi0}3ŠÂ<Ï"½iUib~Ï“@‹(Ô¦Àb4îM2udbë˜wY0}6šþù•y;‰#)B¥ÆK#†‰ŽÕøÜJ +]T’L‹:i¤C¡4¯ÎÄ[ë•þ׿¥Ù¦€ÈãñŠÄ/H\’ø‘Ä›Z”¦‹§ßŒ„e9}óûŸŠßÀâÔŽö‘3Z‘ ʼcÞÂÖÊ|µ®i„àt/asB@"ÆníCéôÖ¬Ñ?ìlŸY镞XéÌÁ¢z8<€38»(®`c Ö7$^A6.a»¬…Iþ+‰‚ĘÄû°€&ñï°Lóa2g£¼×d¿A:üÈJ‘•B ¡ijvy–…Ý’¼ÃØM}K‚ÑÁÈg«ü‰ÿ…H0,?À—°±ØÒ)§ ̆mé2QO-0/w0Q;ÀÉ&¸ôÍ•Mp½ +ØÅà,ø,Ûc ÒÃ.Û u?3Ɔ÷+ôŒ½öÁé]Ô¯¡íÂëð˜,lwÁäJN¬ôÜv{8Bt˜‡R fNÑkˆ žÞàL—°]ÆÁ¬6‡íö÷@¾Õ=µpH`ÂOoØ)î„ÄSÏhÌz*IŒ ‡ÖáÛ¾;žÚ{}2öwIÉÛ×BE=ÂØ¿ëù›5œ=‡Ä ¿$>òñ”£?÷ÕÓSÛ÷ȃjó0Øã…}L,ÑZ<³ó&P§lÛ5{‡¨‰Íú-ÔV ,6ú×lÝv‰w\Â{qV 'ð1êÇä³Ï!2^Xé••è¾Ë}+¥V"Kø£C8Ù!Ãì'>ùÆ›|Îî=×]ù@Ç©¬ç?ÁU´€Cvïàv9ÿ¥\/J¦a&‹[ôÓyu%~R?›l{>rVÒžâÎÞ °×þîr]Żмڕ› æ³±CA“ïð—?Xm¢X¦>ïs.¹ŸK¼Õõ¶ Ì8£€½î¶íŠhOBw(·A²gt9¶—®9™êN¾ÿ Ö`ëç|[#dñ¹ûR$‘µË1^¤¡©4Ôš)¤ÁáÂJ‘â$M£R¥ñUZ £—q¥ùæ¦aý=·õÍMwZ¨Üˆöý¦ýXFº03R…‘޵Y†ö)+kf¦Ã(‰â=°+ªÆ Ìéél—=5ëTäf Rñ¬±_6³ÌTRTµ[Ì-N²BÑXco©ì,[LÓ,U¼Ê•…%FQmù«ò}ë9œƒÂ¬£¤€­\ª¶À;Û@«ŒË¥\sDïçå*’±£,Ùx›W¬6‰ª=3~•çIa Ufi¤\ð¬ˆÁãC±ØÁ|9c+wÛ¿ƒº­)²¶//l6+8.‡[´‚³²kØžú9_Ä¥˜*9þtO1Jª¢qš¤Å|ÌS£÷YÇpW›5cPËU¯™µˆ*¾ò‹Ë{o`\ÎÔ¥Ö¦°.š4 ( £8/#œ,Û4¼²Ff‰ÌP¾6zMMÖV'M;L ٌ ,¹Ña¯™ a{L| ­Ø•µ ‘–ÇàYN7£É"­Ï(ٕϦQÑr¶ìMÙ([›õr’wÅ”bÅ'gà.€ ÎT†õ÷z³Ú 拾zµ5n_;,u¶ñšìëÒ€EZÔM³£ÓÇ$â n,­ô‰Ïx i8´|µÊÂ(—ºîµåU›&sîFáüeê†HÂTU™ùêésOº&WWë5¹´œŒ“›q3ÂÕÆÄ.Q;¢fGîWöK¥U`4‰µÑ˜D["iUÙ•*Brð‹ÓÂæ% üœ˜”²$tÀAÛvt'–ÊQfòiÏXÊŒÚPç‚`œÄ±Šë-D[ÄΖÁøÚãb;B„Þ•º¢1[ aœ*òZ&ªè¡e¢d‰š\e¡Œ G!é!~É(eHOb9S¡ Ë%cY 3ôjóÑf™ ã¼áùFAN;úŽ ]]|ƒnWÍÅëð-£PƲ¥Ì´ÁiyÓ¢MîLkçßÚ<¸;¡Öb-Î`˜Ðv§y_oŠ4¦Qh|FYìúæ8mºÔ=Ǫƒ»7446€V`\ À ˜P­.ïÈÇÁ0[×p´,ÊpÂô:bH1P_lX—]Û„2æ3ƒ¡8£ ú%c›8ïÎô>]B.±9gc¨œžù_ëI=éI¥Ð.Š(L·gyL û«}‰è5Nea‘…),ôø†Ä'0ðJIü‚D|çÐðÁÜ£ƒÛóCIœ†E7"=§;{Øþªö8$²ËËŒD|ÍuO÷l ³Ïv¹hºËð܈ "Üý»%ÛÏXd b"Ài±ž±6jXípÅܳݣéèöì¬ê™œe^&6ÜÀ¡ŠSZ9%б6Õ^¸¨î¤!ʺNä>¡_;ñÚÈCØÆ[QŽŠÑ1'PºP,Ûu ;`Ý6;µ2kd>˜Ÿgõ±Ã)¶Šœ8ìgÙ ¹y²RšG§,iuŽÐÄÁGGQ”Â9‡Ý² [Íäš›h-“Zf< E%YÛ•ò¤Š1×7pÏʪµTµ¹õéŠél Weð©br~²¹yó äRØà×fiqÂú]ÃΜäÛö}éCM} §Þ6y‡ú³¼YÔ–òtoŒ0üg©0a]™Îz6ç黽a+ó››÷:«:-sôøxªv:<+(âPæ»$7YNo¢” ³r;Q§Y^õNÐ'»« ï”)L¥càÁDIšª“[M™J%óv‚èk0ºøËl¨¡ìh`À4kž†ÆN rŸ4kmßvK³š”ÁÒ&f2¦:u3}þxãmgãC¸àÌ.;àë£øº #ÙÕ ï¼£øÛewY€ûŠ{üµÿGÂÔøoVr÷®¬»6‡øJ)c`—äÃ0[“ñx:za~~Í"P›endstream endobj 37 0 obj 2754 endobj 41 0 obj <> stream xœí\K“· ¾ï¯è›gRžVóÑ/Uù )R¢TÙÒn¥Rq»3«Ùö¥;ò¯Ù= €Ý_?f´*Å.[Cl$>rô1Jb¥£ÄÿiˆåõÑ“·y´ÞUÍÑÛ?íˆûõÑÇ£"6þ¿ªAÒËëèù±ë¨Ó¨ŒË,:~”ÄeY$y-UEYêþ^¦Q®Tl‹èøúè_³h¾0‰v·³wŽŒÓÜØÙK¢Ž‰ŠˆzAÔ÷D¾?õš¨w@ò¢ÞõO¢¾#êfGéL "æ/Èóú»*MÓË“§Lž1yŤè1yÑzvÉ­&˜¼eòžÉOL~ ‡ÀsS_ÃÑ. °ï`ë ¯"šÿûø/GÎ ´s‚¿ÿ¡Ç Ø€l¢|e‡¨Ÿ%y¬lÞcý;h„K¨Ø¨¬L~Ïäɰ{˜Ïvaû«³ÌÀVý&z» aØhG­xÿ=ŒÍ÷,äÞ©ìsÞML™Åy–ºØ~¼r¡ü±¼ïÉ[¥èY4ƒ,Üáä†q<ÇÿñšêÆC{(Ç£2íæ•8B»yNT4W:¶ºP³ëùBÛ8ÉMîBqóù¿óE'‰Ê2·/§H­äÜúîyaJÙ¹î&ͲćNë$™Òî†4:Éåˆ\3ùÀ½.¸UÈr†q+Ë•-®ˆÁé*õ3Ô¥ä „ùn&-Ìn6+$«ì’fëLägÔ3 r:Éuጣ|¤ó¦!Ζ 1PÆ2ÒzN™ÒޤVÃd&5@²±ÅZ…2.½åLtÚT¤Mâ Û8f\w7‰"3:RΖ–³”Ò¶²”̃ÏRô|‘Ä™Êr:âùÂÆi™$Ö5çqY+{a ç²UœsòL™§þ¸§Në¹q³6®õ\´Þ{Q¥›¹E®ß“=eZÇY•=Õ{aÒ ýÝQ#o5è‚nÉ›åVZŠüOXêTz]e©<ÕÒªmïÁÛ㜅w–Q1œ2Ãzd#t@%à®JûlHá…»˜¢Mà›KnÝBß®7–×þOs7@V&¹ìIÝÒB…BW’·ÞÎ)LÖìƒ-÷ªzÞ³¤v¼L•\ýJچȘ'A²ã¢m ¡ÊVŒªU‰®DZNQ퉤ÌRÛÓM(ûR„ »áÙLŒåB–ÉÒŒÆÕÚ1TG“+»q<+}×e¬•ã:wA«Ì½,YìTï%Þã(XˆLgËôG“-}¿!Š—éF$3áå} ýL8Á¨Ÿ ßq«V¥× íÝýãÃÌzl°Ñà3!Ž8¢`À{W¬gw²'©”%Ü3ˆ°bãÕqÅ…sS4qÏ«q5!v CˆxÆÛû‚?ÄûÌÀnC«‰[èðÙgëOIhZÉVeÉpì)•†)ÕÙˆB±Ôëg·FWŸûØQõJJwx¯Æ‚Ó©\-ñ®j§qû«L()½. ‚yÉ eÓ<ÖVUAËÖÂ:û¥7ÿáéŠóNçow³"]ä¹á‰CÖzÒ8mïÇá@,ä鼕µºÿ}âk*‰‚rFuð%QAE¬´ëšëžâxI½n;5K"”‹¿õ^L¨¦ßú¥Â‰.M¯bÅ@ïzµÓ‹Ú é/“+´E=+jîéÐ^£–^${( cÀ>÷D}j«¢- ?Áõ]A «‰’¦™”D=퟿N[¯é;;þ/²ûÚÈ#`>á,&¡~®×ÐûìÉå˜~…-î ¯€e1v|»mƺ Þ$ï-ån¸uÐÖ[;`0VM2~PV+-£Ñ0Ò#ÕÐt®´;É$r…ÛöIÛNÄQ,R—~¤ç·×aH‰µ¹¿:BJL)9™{#ÑhÀ‘KîqV7jŸ6ÔF˜õIZ«_î¨õ¡Bgò$É0“X#5é Œ5N¹Ù¡ Ì '¨:#áŠITi.€ÊtÜÁTcÀ#¤“²¤vTuýÀ%Ôa8¥¨ÁwÜ9/š*Æc_3ù„É7.zǙ˛­X%žÊˆ'ÌK#Œ–óÁæ­²`]ÄE¢š,¸¯ZEeÇ‹ÚKlâú¦š9á<=¨Hl×Í<¯ÈÎy¢RØŽ­ñT¦÷ŸëiB®š£h÷㌙Ý*\@rcØÙsRóù"¨œCécÅTP‡ MÄ$*ç Ê¢Q¸ŽxÅònvŶ‰M..Yü&Ì\‘8Á\AÙ”éXåAÙÄÇ× iwv:ì?ÝŒˆo=7ݰ5¤ö~(hBùúQ€ôìãÙßø-§¬Mº—%»ÝXØ/zŸÝø¸`d§Ü)¨én¯–$c(TKæYB5Xex*¹UŸ½€®ˆ¹ïaÜ]q×ñ‚a¯"¸3ANƶÁ7ˆBá•OÔõX¨êdu‹ÆºŒûþvÒjw¥3Œê0fÔE ÓÀG~£Ë¼IÜZõ$øìWÂèZîãIIÝŽ÷p€‰ÁÆ]¸;ʷᢕſL—$*hñ¸à’£+PÉZcM±ˆ X~°òw¢øÁ ?Sá7m?„ÐFW)Œé€`A£/VĪï!¹…~±‚ª~3û…á)“ÔÝÒ0?z€"~bô.ÔëdÄݳ1 Ÿ@ ½ŸÁåcD ã*¢›QÈU°ïŽÑWE‚W`QOÆìy [‘Jz PdgÞ ¿Û¹4:~evž"é2‰‡_ÛØ.†ôŠ#àÜRã ~rô’_ë0/ƒH‚ÓW $áq®ä̳±ŒãF:OwŒf…B»páÕ2q—á < jCK¼†óúúZTZ¦Ás×mÕÊeç2šÞ~‰n¡Ïà2µ«ea¬¡cr~Z*¼'BQH1pÏ,']umÕ•Ø)f‚kç)EÍÔŸŸ¼|üuøÕzó ~Bê"È xø^Ás8‚­B¾ð/¾ÿZõl“Í ‰Åø.HÌ—5k8šÈR>ÁVœ`]óÚà%aÞÚÿçCùœcä4ë:ÞÉ0ÒA†Ú7Dk鑎¸ j_\=Ý…«,ç€ê½"Âï0ÇÀ\œqÁöj]ðäáa—+Þû"lá²ð£Áû’(*¬ .Jß­t¡Ç€|°3l¡Ö°~F ôù&X².ñé…€%”‹OŽ©/LØZ|"sÛß:§ù„³™1Ù1k}¾*/®Øñƒ¬Ñsó@èpôQ˜ÂÍ`8ìt"—À?RÝ'ŸùD~pV¹Œµ6.RzÈ| ×&ßyå¶h$,vÁbþ¦ù@rôiÞ>Nqæ?!ƒN±„šÇPÒ lňÙ¿ÆÄO0ñ/÷ñ”%œÎ-d^åL;-ødøÒ''Ö·PÆW:DQTf­üFÏÐG5Þ€_ü8ñç¯qžŽbÝÈ0ú,<ª¹ð=Ù ]ž/.–QÐØóåñÑîÏÿ¯ŠÒåendstream endobj 42 0 obj 2711 endobj 46 0 obj <> stream xœí[Y“ܶ~ß_Á·pRŠA‚LR©ŠŽ¸6Š,y5~PÙ©Ôhgö°wgW»³v”_o€ºA~$fFÑእ—^ÐúB£ûã»(M„ŒRû¿#N¯èèüþ¨~|ÝwçGïŽÊ$³ÿêœ>½Ž/ÌÄLFB%©ŠgGiRUeZ©z€ˆŠÜü]å‘Nu=àúèûXÎæiRˆB+'³¹Jò*MUœÏtR©¢Œ£Ù<++CÇ‹Y–¤i¦óø’Æ]Ïæ"ÉËBVñšqº3c‹<5O#ûClGT:M‹¼ã&³ø˜F¼˜É$˵”ñ3³€¼Ê”0þµø‡Ù˜‘YGAûÒ²·¯¢IQÚ}™I‹„YšŒÿÝ$³O#_Š’¿ô˜¼0dZT2×Únñ¸œÙyeî½ø¶~q&S_¹…­éá`õÓM³Ü"|g÷³Ü®1ÏGv~A ¯ˆ3˜g"*«â÷3Y%Rd¹}¨ —¹‘¼ªYܘ¬ë-~ÝÐ\pLØ_ѬWoLXº2Ò±Ö%íJkO‹•q ÆÉ_€°Š•eü³•^Q™‡Ëz³Y^f­NBÆd?pƒË¾RkrÅ7è$¿ƒ0ÐØÐÙjnèéñbØÂ:)å™1#D¡RÏðN­:ÒTå_ušaüÙÏ?N•5#ïU4öžÆ®G„„”S¿Í°²yŸsÑ9Eo›&ü*ÑÙ ›µ%ò‚fyR”ÚÄ×J[>s¥tRÈ,š·ñÊr[¹³tÔÀ*Ss¢§‘G­Ú÷Wû¸¾°˜õ1~D¨U#U§dÙäN8 ô'°·Zÿà?1¥³È˜Àµ11=¶'OQ)/tZ ãï3(§MÆ›yÊ œ†ÅõÊ®C§•ˆŸ&üUéªxÞYV&²ª:#´€t,*woÅþÉÖòˬ2ñV9Ëé!¼[Šì]ì÷ ͺ±·˜7N’†>ŸÍ«‘Øp3´#ôÛ™v#šE`Z% иž±ÃÄ­2x˜°±;Äxe“—Lõ‚@§h sÞ:ßîçü­^šBá`;°Q#A}Ú6{Ë%Û†]à TŸ^YÚ%yfŒ²ùšùó&ã“¹ËøDš”]&gSÒTš¡*~mÈ$×FšÏµpTž;ê÷£^:꣤£ž·”´Â37Ñ®Ô|aŒ¼oÆŠÊJÌ‘gDn‰ü…È%‘wD®‰ŒˆÜÀWláØ7DJ"ŸÃ±§ï5‘·D^y w±+û ¾3»†O½mZ+RÒ‹”ÌMò’¾L›¹å0o ²9Þ‘nÇIgYÀ8°°í¯ ß‡tĤvü9lá46@Y…H6ÍÞ÷ÀŽÖp÷lŸpGl•öÎÔ)Ïù)âC8°)“6ò ¢³ö{8à‹ *½‹¾î8øL—ybOéu£xñd©;?é(o¯?:—üÐlR5c;ÝÅÕþÎNÒsþ.÷‚:a^ÃQ$‹‰ I¤՜ת”ºÕÕÎçôÆ9ÆÒQ׎Z{T¤öê8MͬKGýwwNA3fäzÚ`™W ÂPg± Èb:ÖÌȰ~±³ãëÆ˜4Ûc‹ýŒ*ëy}ÏÙ¿urümºJ.<)·?X5ï ƒ÷-6öŠp'ÃJàâ˜È‘¯á<|™©° Ð0$ïuÊúçètÆA™¾:,&/`ørÄ6Ž.#ùr2–2×µ¬„JH¬0„kD^ºÛ•—aòýÄü¬ìÏUSج«®íBJ•ó\µ—ö+Âë*ÝM”òÉðŠ'¿.‡n’zÛèdÉ1K¬{]\Ý:í­˜]/ØüUÿj;:¸“uHóæ®íu•I%{}‹~­ýÊ»ÙM!rÅ«²lškÞÐRÞÂû«Êâ»+.ÿ9,#Üd™.ЛJ…±«Z¯²üÞõ÷¨”/¡mü؉· +<·` .3¯éOŸ(3„àå (Ó…¡¹Ç”}3œì`…†ç-ýÓ  üpµì‹ veVgoÇ|Vâ5ÀdÌËûº¸Qi—Ÿ¡ØG àšÒá8l„ yi¯™f„¶wæîaPAç<úƒm«ýP‚Í—ô2Opíùƒ“ûo=ìØz€¶ñrh÷¨¤—夌.ùÐÅø6ù‰ÀjÊÿôõÐf^JœçIž—û Âè!¼TCìX’yH„Žb,5DÈÁ ßôòµá5ºNßò Z Hë‹øÝÃ+ÎdÅɰó¾ j}\øÃ1Gm¼2g_’cŒ[mTæïïƒ[ÜÄÕߟq¼ÀÓ‚°¢Ö]Mª3zovz€ÜÍÈ{Ç/4»uq1J›8±íAاy§KrúÿË´iï¸ï÷2WrdÖÄÚ³t·é8 f3,¦BrÙ íÖ®fŸzÍ rp(Yx{òdy¢„b·goGuŽ‚1ãA…0Û3±¬H •늸:ªÚ¤ô캪b'ÅXö0ä)CNŒþŸjô!´Ë1h¨;H†Ò—¥êK¿ »ù M›òXׯ™ÆlxrA=¶ch¯Ý­bÍ<$çÐß|(›Àï¶•8¿ûbËSßðôÍzo«ì¥²GÝ`hp‘@_úô`CíØØ7þ<ä5òv<ˆzÎ÷Û’““’7 ~7ïá‚€Õ-|ŠÛõ¸G[Ôòƒ!¯¶€ «Q•ô´ðÄI‘ Y'{hAìÞæG6'GL ƒ©ôQH¥XypZ_ÌÄÉ@<xÀeŒtö©Ÿ?±ØUøØÄÜAyÝBØ–qÈXA)±R 5ƒ}ú¼/摈ƒ`G‡Ù:wPšAÐÏî[…©ÙBRfˆJìÍøã‚Ï'Z±»h‘Õ>uò `…‹}à?!ÉbðC”a$ƺadì&4í«I Ù(_lÜÆÁš‰| ó'Ö_±£þzj#(*ùT­ö7KÖPíLüÌ0fƒî°û¡¢yR fÆvñùb`Ìå"ß| ‚ÑÙ¦ñó*}DRy¹‡f‚æƒá}ì+´ÿèñj÷ãÈfȳÉÛÿ ,ŠÆCx(nã Á Ü[-°’lÄJJBwCà:}æªe¬@2èqï÷%ZׇªX{á»xà 5´`­j¢hÞÿiìc6·€+ô8}ÌÖ~UÖ0É™½ær¯ÏÀ×ÎÄîu3x6@âïC' ¿‘ú£WA<AÕ þIÔ™<Œ6TÅ#}lô–à×-øõÔQŽ";XùÊÚ1á:NFØØ`†xàÅÌõ³{Éî‚dþ”Ë&ø<®I¡bÉ^FHBæ¸âû˜èø•žQùj`„cŸûün„á$ö·nŒÏGßšÿ¿«~=”endstream endobj 47 0 obj 3135 endobj 51 0 obj <> stream xœí\Ýsܶ×_Á·;9šA€Ì´}ðG;î8µb_^ât:§;YR+d}$nÿú$]’?îNŽÇ“ÄöØ+»Xì‹¥?$y&d’»ßX]=yc’³»£æqòæïp{vôá¨Ê ÷«yÀáÕUòtae™ÔY­“Åû£<«ë*7-U‘èÒþ\—‰"SU²¸:š%éâ?GöGi|u´øÓ;ûd^äÒP³×ÌJS¨ÙÛ%z 'z ÆvÔ³Ûð ëÑ3;1ñ‘“”J˜¬2ü}‡%êÂÏäÀ¯ dcï L …[bbŒÂ’À k8ö²s V!-Ú¿ÿ°Ú-d"T–+Ro­êÕFf…vê}7“é<Ï´ÐÆJ*Kç*+ë@™R„qX Af±,c>2ìÕM;·’¯e/þ–×)2¯5¤páì¤(kf¾=×mbAaM©6>l[‘ÖöÖPwXùÌÈXŒ¢ÀÕD+ml?'|AkÍáZ±®¡V`2Ãhm ˜ä-F¡§Q>^£CÏ­²/}åÎ-ÍÙ:äcéËJ ¥o!å¤ïñ%Q͹{ñTÛ O5hÛǦðZÅÛp¬èví"÷§2ûÏW¿ñHEчeUŒàex26§?ÌÆ†1,š,7wfL8ªÆ (Yc´ØØo ôÊ`"Ï4ªomBii +T·²8:w¹Z¯òÄÀ»€u ÿïNi«?W<…¦{PUòE£vÕ fªÖ÷4pû}€þÖã[H+G#'$PEÊt—°D†+`¬D¶†h÷ð)C»€cO¡@«âšß »ƒà)XÅP%%<×7{(A,ÝǪ„ÊÚËv“Ã…eÁ4ú$¦Q¬»ˆ–À‰™4Ï žÑÄt™ +PaûŸZ¤ì 1yG«ÐLp7¶i:ÖPœ(oF÷=´„%@óòˆùÅÈÄ5v,æÇ×òw_bÍ@=üŒÀ·¾!ðÏ Kÿʧ;Æ‘çA4t!C¶ü¢'äíÈÌx˜¼‚&…ƒ'ö8&a´Ï¬&³»n"᥯¤¿Ø ‚# ù ÔÉ¡?ý8ß:¾åwë]ÂV°dLýø“mt8͹€h(Dl6 ¤{S$›íR¸‰ ÀVÁ˜DÆs¸E8¹Þ·EC°TLwX8Ç«Æ;N\_Èr±×{¨,i PwLl¯V›`•Ÿú5Ü Äç_ýÐ>ÕßsX¡…ŠN¤Ž{ ½%x{Þ®t 2ƒu_Ñ;F¢ßUOÕ@?Q0/šáØýÀ5t¦ßË?mgßžÑæÆ¾ûŠ®½¥±¯7ö=Ý|?´Íq¦Ü¥¯o1ÑÀç®|D‘»J¹Îj£›Í5`m`/ßeèÚc|kù²{£å‹Ú÷.©=ïžuç± nR—Jh­»±ÍCÎáGŸ¶Ò(kÎw2Ñ8”“k<¨éOÖefµùؾ¿çÖ+¬÷#,C–e­œ^E*R?yq©“Ö³è%(PµTu.Üõžo©¡ì…'RT´wßâ°K)˜Ÿ-Sp§Å&`øhè=—KÈÄn`~6h›šàxªI(€ßwJ«Ÿ‡]üMõ¶ý5ö]Uú;uÓÓÎ,t]9³ð¶$ˆ¿rc…’åÔ=6ïÕ,OBÖ©¶ᦻ~‹'J¥ñ!n ·D¸™Õ+îøÂüÞ0nãAà©ø€ËÐ9jq½é´ *•Œ+hШ¬CT¬õçïr„·@ð9ÙСópZC$Ï Ï¸SãçÖ*UöúþÂûs¨¢Þ•v$Œ57ñìÐvå*|íßN„r¢Ç§eWYͶÜK£äŒR2º"Y¤ãô‹ªü?€·¯@:7¾à³ý%@þŒ²Kažpƪw¸Öƒ‹bø®òRÀGKOÂnµ µÿvŽ·PBñ†J"£’GÕ³çèþØ”0ÂT¿- Ûýy¼/Pì%òFZztÞ±iš%-Í€^ÿêt”´4ãF›ÉÕú@ΰ^kßËoE¢CDfXxShg¨ËÒ.×JÓTjjµx£îÚ8íŸYhãl9 ~«ÏŸ²,tªù-H>ºwïÖ˜„ó Ü2ÛuYy›ÅYÉ Ox¯nà€ëÅ–LnÃ-Û˜ï¸Eu ™½âeÄ8Ù$¸ýMðuk²ÊÚª¨§35 VÓÈyÌZÅ‘ušài:¯Ç å‰âŽvÜÒXÑ€§aòQÛ×Üs?4÷]¶\êú¢ªÅ:@¸ëb´]†Z¤…„Š{¨hG%½[ðlè¡úËäuŸòÔ>—tlgf·ƒ¬Tr Ÿ® ]\£AUÖ¶¦5ó­žlu&â¸'ŸÃ¸I}g#eXœù–:² þ–ô|¦Ûn—CÊÓj0ë´^r•°t‹Ý¦ŸÚSÊ’ë(ÖÕ‚OºøsÙÞ|ˆ³ó60¬‹·ŸŠ,§ë üoßÉš2Ra&j,£êjsMÕšä‹ÅÑwö÷/€*endstream endobj 52 0 obj 2874 endobj 56 0 obj <> stream xœí\Ys·~ç¯Ø7寮£s9G•,ɶRŽäHt%eÙË%E1ÅcÍÃ*'•ÿ`ŽîÆà›Å¤¢Ä’ôКÁ1_ÝhìÏ“4ÉÔ$u{ayqðèU99½9hO^}Ý ×§?T‰všR^^L¾<´³lR'u19|{&u]¥eÛj6)rûÿ:Ÿ”Yš”¶ÀÅÁt2;ü‡­£rªcßU“Ão÷ƾëTe‰1Ó×VLòR›é3’Išô”¤ÀÛoIzIÒ×$ý‘¤s’ÎHº$éÔx jœƒ]+Y­§ zx1,hJm¿ú§Ã?ûói×ÇÎŒ}óÙA–%¹êçédf'4S¦´Í÷Ò‚¤£ÙÜ–Öym?ÈØgº6¢ÆÍl^&iš…›'[°ÌŒ,yE%O©Szvæjë¼ÌU×µViÙ½oĉu^©vSÔ7Å®góÜõ¯jYö–Åw\ö¶ëF0]uWi7‡½¸t¯Îh,â ÎSÑ4z Û?ç1r­KùUÔÁlö–?¦bª „ƒåõí>–ºå÷ËFiT¦UgvÓ¼éT¡©$:Ž;˜-×A7}õ"ËÍ4á:(Úš®´Š¯æ²ÕG¥=-±;$Sö±nDe’´Ì39°ËÁhÒºÈM·‡æý|Ì»]süÁçžû#UlƒÇb÷*-`'ð@F¢ÇGHqÛýD?¡‡ö5› Nà sNÛTLÑ ¿?O'f”‰Êk3T‘fî”NŒªËé¯3[*3i.?àŠ?pgûr†@ ~ì)œ$ê%Ñ•ETI4õÕln’˜¢òP•zOPGÿsظ§wB£¢8Ge}œëÅ58×.á\»†ûáÜ7—þv¯Të^±jE¯ÅV[luk–é=˜N3ÝP€¢hÁzËæ¯¸Pª³Á· p ½#Zz*ßwÀ’–]q•¨ª2¹[ò~mv=€ üéö¬&=îUÀôðÞõNÂ;ß_ÏlWešUr†‡ßçsf8|~ˆua¯ò»VÄzŒ-øò RÖÁwP‹ÄˆÆ€ðÕH»ÔØŠ7Ú/6Ðb:¼Î3x×-UÚd´0å~> Ò }=£P„µTˆgȪ]ÂZQÒ+ÌÖ2QÍSüa=ìš µÕîà(BuÖ–(Ìy)EYL–hö É#[mÑÒÓ}öŒ‹DX tÛ©M‘¤ºèÕ¦ ]ä…lkÂO7ð_TYÚ.ÊÖ¨ k;7¹NÊžKüö‰°ÉEšSÛauÔɶŽÍZÏ(”’â:r1B–ÇV1N–I3‡¹ e`Š[ÛX•–Û©ýîwl ¶[ÃȾ¸×³~±æÙ–Ìlm ùØ¿˜{Íé! Aý{kª÷’V$]‘tMÒ-I’Dp^¤Ž¡•rÌ7&;añ–Å ‹w,®`sÏX¼íŠÞ.a5ñôö&¾áŠÅkЛÂ,8#Âï[úU–¤Ab@n´"V{…êx–0ŒnÇFŒÄñ8dµe/ʈ1ݕ̋ZÀ$è@߀±#Ø-滸¬þûY”•UE¹·¶uÃi:28ëüøaÙ‚ ä\@ˆZ­1iÆ~GE&-€Ó†é`¯1Oë œV¬9Þ4ƒPe4â嘧nãú\Á Á· ¨Á“v{u5ýn¦o¯³é#.úœE~7T@ìÒyEšO™åΚ°[êzEÝ4çF0Ö¤‹ù˜DåšùÃþäÇòÇ­Ú„iÛe¸a_ÝójA¸P:eaÒ§$ ´è¬ß·e.›5å] kÎñœC/“‡;¨Ž?¢e—AïÛ’ìƒg¼}æÊfƤ&ø&EŸç¡·µDee¼€û. ÚPžc×(ž%CU^öŠwŽ`ç÷U6˜*´ýWò»Üa¯+0K‹JºW÷¶Ùm~Þ{·pº ÔöŸÔÀÍè±ë½£z¸äV­þàøµP¤À¦8K´ÃÛǧ Õk HoØ>(™Ä”…Œ¾™þ8å©‹„¡7u $æ]¢ 6͆FŒnZ'j€‡k‚èÐáìRÛŠØÊ½Û„‰Œá³5–¯iZüD…¬;„ d bT·ÐF;a Ì¢<6]t¤­å ñGLí+ÞÑâ¬S|Ô1:ùqÆ;µK`T•¹ ºÉ“ªHøT pcÁ7ÔÁÐNâ¶÷»{;hhÙáÄçqœ“<åeÀ1ç£A Ž xTeÁ$lá1 …,ЪLe—¹’‡/e–EíáK°lgœtG´†OF¹Î€¾Þš˜öp3ž9áòÐ^Ä/NƒàÚöjù‡©"0#£Û= ²ã±ÐµQP‡.˜SÀL ¡Qªs •¸ËÅÐ{‹8fdLƒã¤ivÜ/›¾Cï÷¡ßY]Á¯;ÒÔ¾óÁ@±dBÿ£¼QÏ}“ð¨‘ب@ƒ\œRRÆË.@]%µ¢Ï?öp§°¸ãÃŽð‡~©:Q™òò4 œôb$ íÀ²Ê0)â‡GÔÎ0Gd¨œ†¶· °Ž¯?^9¨ &kAn¡`êÝTˆ^¾œÍ kµr«3L=ŒcâÏᙬ bûDðð‘Ÿ1ð|+ÍZHìÞè@ª1 ¸‹á2Á ¢XV¼ŠWd0·ÅÁD­»˜0ßom­ÀÎö¨’XA–  Ôy>Ê”¨ÀGõú æ‡pJ°½ÅÎå6q¹ ,î /‹yùØÚ A}|<ÛåáhËQ×:[ð–‚t¦¦ Œìo«Æ='ãÓP*ë ѳ6F%Uf7£- 9Š&‹%ô3è¤íœÅ‡±›få·ú®¾(öè€7è &"¢Q“ZÌò¼Òw¬¥µ3:ÅÍâ3aJÀSˆx»xÂâK_@ñ™W-Ë-l2FŠ4uëf¤Ž‘¢$ºMîbl’£ò[„?‚˜}ó¢ÀcYV¥ryÏÄ‘žÆÖæ1œÙoäÚˆÙjˆOa›wƒÕ…×G“ƒr³Ü¼(­î#7Ë91¥ó9ï{wi$}žÑÀ¸—˜”Ük´gÇÖÞFŠáÓÒG°î5Nc›’e*Uv³EÉ5Ïî)'k59Ynï;Aþ håžœþõ|m+¯ƒ~»¤•Êü7Ñ5qõf0½‡Èµ„yX×PĹU8©k ¿áŠ'°šh÷,Ö ^±j(ÊÅ»ï•çöPZ[Tƒß>åüõVÆr˜j,Ãn›õÆYs‹Ø´ãj¢À–¿¥ÕMàçDâ¨Gh¢S]¤‰HÀ}Xœ÷½94*ís=‘ß"»¤ÚèB£˜:ï–c‡-¦*ûLUWòÎ÷µœ¸.¹4w‡<ëõò^ë°(ð6†7Ú*Ö±P3ÒVC¬ùä˜QØ€’3M–õÆ>F$M•¤µ*ý´úŽðµ \[o ¤@{)®›ßôÁqf¨tÀ„ÓÏ/‚¸TèöúÑém"N" d@}°Ãï]Hóûøj÷0ÿ" A!÷±”§@yè¼×ürÁxJwh{<øçæ-ázÅ(ç@¦ßÈük…æ™ÜW$½!)%é ’4I?y#¤,£¢E¬h¾Ä˜¯aY±óÅ,76øw°Úv,FäÑo“Ÿ/ûöv‡¹€-Ç>=!z›Ùh´J¬ :°ç}Y‘*+”ÕéK’¾'é«}•M¹aQ;*^Ç%|º ×+&té-?fM@‚õ@ _˜þî#” ¨è*6Ãâ)Fêè~ÿ)@ŠªLa6Îì¿«cØ*‰ E‘§7x}o`l7;¨­¬èÿˆ–{Xà‹¾¿ö|,Üêñ.ΜƒO,jO ”´u7<qüŽÃv±ÞýÕ Þ¶ÅÈÄ[qHŒ©ûnº¼_Öµh`W,ØùÈ\ ™?­Æ8ìj‹Ž`¢šXçNi TeÀã^þNGUY5zhjš—ÄKLdÉÊì?ºá…ˆïæã@éXr ŸF—œ4edÉÙÊ|°%ÿfd§ÕW¿Õ—ÙÝ·2×àgÈõê]þNB-ˆaýþÄô^Æc à™¬˜¶ÀßƒÙæ#+^•×ûλÇœ*¼ààéa—`ý/•ìð{m;âp’ Èl[óãkc‡Æ®<É”¼•£?;4h±IÚkZ*͵š2ͽ›vxí5ú›løŒ'èãoûíL”1û˜~µbý¥/§díyzƒa¹‹ Aö¿Ÿ,¹ËŪšâ,Gš ü£«ø—VÚߪ0Ãk8 x×¼ÓðnžÈòNùñu™5÷5D_â.DpS9¼¤OO½;­Î¸ó J­wW¼©ñoÃéÄÔþýøSGÛ\´ªßF/Z…—Ž!XísÝ*Èÿ{vxðWû÷?&šÆendstream endobj 57 0 obj 3560 endobj 61 0 obj <> stream xœí\m“Û¶þ~¿‚ßJu,I€t;±c§IjÇîEifêd:º;ùîÝ‹ïtví__€± ò‘AédËg'žxÖ$°\ì>»wz‰$•‘°Zâðlïþ¾ŽŽ¯÷êËÑþß—ÄÕñÞë½2ÉìõNžE&fb&£4ODM^퉤ªJQåõ€4R…ùwUDZèzÀÙÞËXŽÆ"Q©Òy'£qž•y\tR媌£Ñ8++CÇû#™¨RHç¨”ÍætDœºÁG£,"“YMìrÑ,·¨–ÊÒÌã”Çà ‘g> Íè{nDDìHÌg†Ê •jü€ ærgýuH!Ív^ÑPó(ÙeÌ$k “KmT%ªF¿§tûÍè·É{F`#te€492¸áºFL™9÷GišÈЯ{ÄÀ‹œÜ4¹Ý„ÕûX/ð¥¥} {‘¢Ï Ú%Ñ&êšÂ!óâ÷ÉËÔ¹k?h¦g¢Ýšë¹Ýê™þÉleá6‹&b—í&Ð~²i†æñO†L mðÄQGEŽÚwÔ‹%ev1ç`àSG=wÔ7ŽúxÜcbháSš×V&ZyÝŒM«:hµä‚ȈÈ"ω< M;‡Wßyb6ƒæD^yHäïÃQKÊŽ‰·fÈÚñ0ã{Àù  ~tÔCG=YwØð3øˆ)‘$ Èíz3{æ(á¨Ì3ðÖ íÇ®¾#ò2¤g6íØ'ò‘‚È N³ŸW-Í–Á xm 9¬#šdòŒºpØ) Š/ ÅŽ".RsÙSøÝVs 4¾» ö¢§ÝÜæB爵_@8çŸCPâLž^Pûä^Ç6¶õ¥ÁA}žn§¿4=뮞?²zi§ý3àGßbô\ÚÍÿ»'Kc%ì—²ÞØÎG´Iö80ëáýý+"Ù¦ïÃ#(›ö~˜é)Ê$2ÑÚ%UñÐäž"Oº¸·|ÇMDmrü ‡ôGˆ| îË Ááí¿Ð0Ì0 ðÇZð‹ûÂ:ë€zq eð$8¼‚¼Ð*[ÄyEJ¿:ߦ&ÙOk¦mùbeÒŸòj—,íÖ)LÙ|öÌ‚w“í¶VÁSãç0“zJµúÀ:c)Ûš¥-I®Ê¨#ç5ƒ\âUpÒÙ[€Ë’NCR{U ¦‡µ?\ëd\4ÙÝL$J¸Š$ΦOI€Å¨“c·ÕK\0ÄEÜ#˜í^•üG5I\}¤•S}‚=6yqFþHÕ\&.› SÎÙ‰ù¡‰É¶rW¦eS=Ô¢JÍ«¢¡òxL% VOP˜¹ùšdaåK6+ë2¨óí 47Ä%ï/ÐH•¤… [8KÏ꛸ Ô¯_j•zi|ÔìÐóÀ4[¶ÂMuÕ¶’¬éBЪT±ŽÜXB– e^Øïr[z³ 7Ø0áePfu"ä+ÄE22rZ„ †„\…Ä1eÆUÕ¼¦¥"M”R~KW.ê+k†¾«ºêºªÒUÅ]U’˜ž«ËæP–†I†¾9ôm.X8õLˆ¬4á*_FÖd2§ª ²— ‹nþ1cYãM×°žÓòðÒÂÄ2)Ën• 5@àPÁZï«–ŽlgáôsË«â/cÞ±²ù ¿B9X¡>ζ¿ òœTê²Ù\ò¢¦¬óëg·«@)!z»×ܶ&˜÷yU|€bææÝ Ë–ËcÚ;OäOD>$ò‘O‰|Bd-·}z_ö2•t5ro³ö½møÌ塽 ýæ@™U+z¶._®MÓC°9p«1ïUôb؆⬵³êsHNVZÕ62æ*ˆÄ:?³/y×i^õøÛ……ÍdõrE„<)¥ÿ÷G3éGh&íöŠEpÝm㕹ʺi1 ¹øÉx±±½f’5‘(”+¦DÐÔQgŽš­ŸT4³Nõ~8'––`I’3Hâº<ËŒ¸|I·$Q+fUÞ%Ê(±Eé¶Ô[ Ë£õ•’ºû~9ôI`àõ]Á±,wt?”;Â9Ê8å™ÒUŠ Ê_zJ«54øj…é ç±âq+ʬ0–ú Ö-+àŠ-‰Ó¥GPo3Ȭçdº“üzÝIvU¸†;õSó[FÓQhyS¸& ÂWœlâY¤ úhxé(ªU>p”vÔo¾lƒ¼–·Wö™S°àµ°k¨Ó+¨Sœêg¸zÄ rÀ…Ud¶õLÁ`ÇVÃe¨7P7Xyl” %ÃÁFÀ«Ázy¯¾ŽcÒW:Ý%Ë´Æöú6ÂÖî“ZE­U/võnZ{€‘¿FÞ…ŸÀÅÍ¡‚™VÙžàêGêcÈ GŒ˜;„‚5¢:5|»S4°•áÍG° ænÙgƒÍíÆ·f j{l¶%ÕI©WDß­îØF…µ)Í!ßsÈìkòQôZ–ŽÚ ·{+oøNÝê }˜Ý5Üjg–9êð#%[„€Üýv,Oñ¿¿'ÿ”–gE±o ‰mÌ=é;Áöø”È»åüë‡BÀnÃÿg†€/?üÓA¥Ý~³1SL¡"qÆ6x2“]ÅY°¯üû®n–*)å²´×µáq{ ·œEÒnƒÉ—Œ©Ï.H5@ ëµ®Æí´ñ2puÀF§\¾íQ+CD þÅ lo¦“@ùb@i‘á“£Ág‚' Ö5ÞVß@qð9 ë׆â+¹RñÙŒK 7ºj kÃl,ŸVÍøxÞ⬓ÉÄjž‚iƒÏrúµüÿHHÉ×! ÂeO᪂nTç^ áátÙ–u ²#ˆì[ã¯ð*;iû7ö tènþ(ÛG‰Yr‘uN †¬‡sio ƒïÏ£Âñ´áß¶Éôh°CQüòMr;‡ú´Qëö?—òYXR¹‘ÇQl¤Ó˜d^Ú.û†&;;Ó~Úÿ‘ƒÁ?·ÃÈàÑFü¡Œw›8b÷E[ÌÖs`»°Î@Ó“ÁN𥇕…}é]W…Á×>: Nß„ÿŠ~¾BÑ@gkìð§î%ÔHðÍÍzúYÇ×s8v£lu/tÄM£.ëO¥ý­ì¥êŸ¸ŽkÖøëõQ·}Ö¸Mšz»ùá”þ¯sŸÏd=æÇðhˆß·ãƒEÝ[ø `ŽŽõÒQؚ߃%“ö÷ùõ¼™ƒØ .z×zAa›ÔÇp+°—» áïÊPË瀊,:¾¾V;6µ¦“9@{Spwî:êÄQƒ#ßÐ#þ¾„Úecƒû§ `8yì) ÑÖjO&{ÿ4þ ]WÆendstream endobj 62 0 obj 3168 endobj 66 0 obj <> stream xœå[[w·~ç¯Ø·’=æÀ^°›ž<؎ݸµ¢T¦O✙ԭEF—¸Î¯ï`/˜Áò#—¤¤¹•^†X`0øf0`÷×HÅÚDÊý·Ät>x~d£³›AÕýµ!®Ï¿Š8qUƒ¤§óè儚,*ã2&§—e¡lÍUGyF¿Ë,²ZÇiM惟†Ñhœ(C¿Ó~Rg6©Mªm\Xù|V?×e2¼`ò†É31yÅä‚É[Ø÷šÉ&™2|iIC~žüm@Ë6´ìwúý'÷+u?&P8ñ‹¼òÔL.¼¡ž:O¹íÂS—žjç0¹"døOÐÇ]AÞ19‡ˆõB*˜aMUÂ{Zî+:üå½’í¤÷Cù{ Ï·žzî©CЭçP‹•¶Æ¶Ô;VÕ*EôÅ Äà!ƒVÁA˜¡ØÃ3ØÛÓ¬¢ÕÚó£ÄD:UÊ®¬L;®,O §MçÉÌh¬â\ç–€ŠGã4ÎJ¥Ò¡Íjdã2Í šAçyœ[;<™8/”1Ãï=%‡H:b®/|çÙ(‰•JL‡ïºô­·®µ´J噃Ü÷¸vÌJ³gÔ¹$[(å Ïá€æJ2Ks©z|–—e°\1춨(£ 9ygE^è@Ü[&+c7ˆ›Äy¦HÆgÕdEQ¶âVd-nJ>xhÒî¬f5'¢9¨^Y®m Ì™guå©F”"7Y â3›úÙ XÇ•Q%¹Ž­%šÌÈd–]þÍjøÇ¡-ðã¨~œjo³Z÷EßÜ$•MÏ;šüËù£Ì´iBúÉc«s3<)"Lê„k)²[§†´7w"V6q+kŸyê ¾%ŠÐΕs²)5&eJ¨[ÿ„À%¬Nˆob”% ’­H­ÊK/•5ƒl;1mÕÁx ,B;iˆqsÕ»-IªüªöŠÙÔéFp@ )é)@1Ä%ÐkOM@¹Ãm@ÙÌýޢ镧þfû.(tAq·ŽLà\â5¸ê¹ƒ5ËEß°+ØúVH˜.¾/a-4…E²ààk,Q⢢ö¡õøOOÒxfcêO½ðÔK`!;ØÀW¬ø8…¨¨?A#ÙdAñ¡<ðà#Œº¨p¨]Y¹MáЄúMq×_ »½~'÷qsu¼¾ZSœž‰¨ðiMýâ;Tlq’±)èmˆ¹¸8Â…Ô”ë‡×i7ápS˜÷‹hr2BÉ©´* ÐE\Ó&¸Ü;¤lƒ¨^Ô/#mc&AÅæ;^Adh¸qç–\Aâ2÷RŸIìq’Bè:åx8·¹H]`æCpÇ(]á,Dµí»&ûª`UÙð›Õßh°/Æÿ!žÃ9»÷A÷ûÖSóÐÑÙš§ìEPnz [§}³}Sà“QÑa[˜| ;|2­`o+ƒJUAFW´¡ÉK¸ÌyßâQKÜaZ' [yÆÍ þl°Æ„¿÷ç—œ«<¾1žC=‹¤â+¶á‹>µ®Iaâ0‹±yëÇÄ)Ÿt®ÍÂ4%QZ&/œ¦pê³ÑÝc>W¬’…,Ð×%aþ"âsh[Ô(÷]f±UiÖîÉÞ|ZgqîÊ_œPçJ­àœ”ŠÏ·([‘œdú)µ·\¾ó“r}ðKÜZÃ;&_3YÉìf^•»ˆUiüa~ïÓÊÙú5­ž˜¹#Pâñi§P¬ÀnyŽÐ“ãˆ3à˜åXyëPv9bÂêÏÉÉùdR¨õ0 +ó¬<ñ:µRêEû2æˆ1Ÿs)ÈÞÛ˜½Am«c•dѸq<3¾ I¿öû™iÇema¨ #-ùü[*¼¥YJëoŸŸªl»ž@ýíf½1 #…+ª³nYZŸvWeTb(2ù³ÔÞk…*€\¾a³O.Ï)ÌñJNÝ}Û$W®œ^+XMR‚÷V~ßž“ÈDb3‡$>ù…O˜vzã${¯@ÒÁZD¸ ŠöÏó¾|€×‡_µ9õs€EÀá¶ÞÁa"ÓkÝK>²{€±Ù®v€›È‚% ÄÙ(~3l9à·Ÿnkw\‚¼z¬>÷¡‚_ÆÁVŠ+[°þ­åÍcp W-´ÞûúÛ½°ØÓ÷˜ÿ_ßcî±Þ¬@ùxÖ„·Ó¼oýø”¿>w_\÷Ù\ =„÷«ðaÙ=?x$7%¶¦8ûXá?ì¦Î!*ØýM!lHIûXù/·\6 –¸;r ÉMXl·=Ò†Â.Ÿ²aãûEÑ:…Ã"LÐá Šƒ·oÅm»QW·ì£MkÙЃ†Þ?x¸ÕùÄî‡ä·¹¢a¼#øû.Æ‹ƒˆØÕKÈ!‚ð¦À*\A¼_áC„òŽÁ°Cg/z€¿­¡càgŠßàú{‚`†mÛ ¶3 1Ú| ó´Œóþ¯Ü ÚµwÂìhÙ&ùìO½ýzµÑ§‚í¿Y÷E¾yzˤ(oaß½òØž>°¬móF7~TÚ6ÞóØ>Ö1×Y ßž"“ùÉSüÅà7ž²žúy{Cé Ü{^“Þ¿¶}ùî".”ñ=}ïvK[äÍŒ>|{0 &¾«éëŠ?c»éCï ?umõÚÞk÷vT^Ößµ7 +÷Š—õ£îeˆ{Žî»ùUqÃß]÷ ð²gÃ5ÝGø!À%zÙ¾~¿›l¦½G©™d-štÛÜ™ìù¥¦[À+/72šzЧÆ_ïâ÷cð©vÏ%ÉÚ‹"–ë6XUMƒ§·à)¯é¬nB–XŸ%ñèGÿB<þ2þäüžÉ “"¥ø‘É_˜| ûŠ·°ÓÚý#ÚÖû·Fó§ðÉ;óF¶ÀûiÅ2·ŠX¥ø_\Šè„ópT!줅§ðúàÌ£Y¯¼‡ñµÛž§Àÿ£›ðõdðúÿ/Ûìǃendstream endobj 67 0 obj 2645 endobj 71 0 obj <> stream xœí\[wÛ6~ׯàÛR{*†Á[ßœÖéI7iRGɶ›öì‘-ùÒØ’"˽ä×/@Š˜ùI¹Îæ$yÈ„ƒ¹aœÑû Ž„ bó·ÎnzNòàâ¶W=N¾[‹‹Þû^%æOõ€Ãg7Á㡞˜È@¨(VÁð¼GeYÄ¥ªˆ KõÿË4Èã¼pÓ{Êþ Ž2‘åJ…Q ¢´Œc öXôó¨TY}‘eQ–çá³~Åq’§áK3°LJ rT1ƒBûm_FYK>×P’æ:²ÏØÀÕ2 ¯èáÔ>œ8蓨ÌYrZúa–Æúa=¿(Ê’Í_V#ó8ÎRW5M•Ò-â"ü%d£k”ÌW«©8/³µ[ÿ¥¯y—–‰á¯Ãïµ|„ʨÌH<¹l‰'+…fˆž4ü­'D”Ê`ø¬7üçÛp؈,ÊE&ÃË~¬©r½… 4”¤Y'šÒæáKCsVjz¤}¦©Ôx“´äS®úJ¿NJÞöZGb-oþ~TMOdl2ós¡J-ÔŒdQ¨TKu ¤žW‰µ^T„ƒþ 5¸´0N Ãe[í¡úÿxE[‘ðů IZeRÉèœÒ¬ $nNODD°¤Y8‹sfHžÑ(ÅæÎhO×ÕâF«-­[/"L‡•NI™Fišj޵E_B–0´£Z²7T­d/gÇl^¦Ó ë”æc\I MB. bñ:IYZ˜ê2‘*.kZÁ‘'RÁ%DÛR‹ëˆŒÄ‘O¯Ø XŽlg¿÷ScakåÜàÊ.¦Jj +•‰‹H*Ѩ #ëÚŽ¬©J²4 Ï ÓÌYJ–¨ðF»e&]푹>:ÊÜ,9PIi_ VþÌ,|gýÑÔB+ÛÌf0wÜlñõlO 1™2ÇÔëÚå¹0þ׎ýºF‘Ä/ÖÿüC;d™Z‡,âÊñÖžVs:‰¥ˆ4òWŒÒ\oôØBC zf¡—’ŠÁŒ7zb¡×ú`& Ž,t²‚dÆP3úxV¿eåPpBàˆÀK/!6öw8`‘]xɹ†Kœ8ƒKÜx 1Ü8…äX"%:Åõ'ªã¹Ò§‡8¬ëKrîD¯í{æØÐIßszUDzã›,ˆ};Çëp„lünäá?Š«QµE«Bæ;[ôÔšÉÈB7š8ö”épYTâBukg]YèÃö˜Î úÞ@pu ô»QTÍDÅUS"^ ¯Ež‡|ß‘³—^FØ÷™3I:7©ÏçÍ Àû[À±Ì/<‚¶Î0ŒàÓ;8-€ 3!\@oÆî-q´Nv{JëñiÝ£´imm\-äšÔfG³ƒ”Nw`ãñ‚˜›·#„†Ý;qù›½¸,wàò_¾ 0Æ`BAˆäÈþ6Öîàw¢Xô»5lÿ8^Ï¡Â^Ž×ÉCñzk¯qb¡oÛ‡dö Q|DàsÈæ(°·= [w?:É_PöÄÇÕ Ld-ò¨XÞ]Bž,àÓ³­·ìpûýsz‘}:šO2za¡g·‡ŽS/Y4>…\ÛÓã<°4vòõ{¹Ÿæp«WÍ·ÏÇ?QhOþéùýø'F;ðnǃûRÒ7€«ty·ÃÕYîáågvÙåL›Ã…÷2Ot§EC–ñÃ:—óErXrø–—a¸€¬7ðÚS¯h„q”Ko\Še³ç°€Œ›û¸…Õ{ˆ gÃËâ¿èþþ¤‚_^v`õêʔ¥][›2Åm¤$ÇΦ·ÍÊñ¦Çp{L_pz¸×N=Vàn dWŸ¬îc÷À¸q Ùu@Ö"%¢C›”è•Ëãö’ØY»\20-b¶íœ†Ó,Ñb¸ƒ«Í}°ØÂHv>ý¦€‰4ý¹…v1eúª|òðÞÛ(¾Ê Æ.zCl ÃøÃä|3›w*Ïé¦=\É4#¥q{z  ~QL^ý·E…ÔŒÙÖ=²Ûcqbû"~Â=b üˆ–€8âîºUe¡õÄöI\ÓÙ}8oA¦â/_¿kã¸ecßµÖ¯ú4œ† ÚÂϬáb¯Æ §íÆ {œPѰfUi*˜õùO%˜¢†²X4 [À9³Îܾ^òèņ³¾[ó 6žªº(d¹£e¼¦X|¯ÂrPòM5QT¦‰›2‹  H´~ålí&Æ 5Ãx•àÿXòÿa)J‚­„}&¦RÑ®D?icÆŸ„ð7uï—²1€/Š1½ÎÝßý˜xpu¢/¼t1¼Éb?iùâÏÉl‰]¾„P¾^“%m^ƒÝXŒà6®+mQ&Þm‚¶¬ižtõjP¬ôËŒ¿GçA®ÿ5À‡®EÈÉügÂmÕËõ÷+¾Àêò’¾Öï¯Â;ç»Àî]‹çíÄ¢¾HoFÎÔÄž«ÜP8Y¤­[þ&©~ÁÓMøž' ±Ïê~L`yJüîlc;}g«Îiê¦+N›6ËÙÖ§‡U1†lúçëï$ÒíîOöË “:µIÌoQäwÀ¸˜Âh«Ì¢LÉBÕ¬y\ m æÃR^–ü§ ­ÃälÄ)ô¤Í¾/`˜œ+”Ëî(Ñæ7 pŽ)¿ƒy'¾ÎÁùp—ûi^¶¹¯!e¸ßä’–ípcºj<%QðªÖ¥¢4µ?/ÑÏ4çHžE2)X¢[ßÈ{?ê¿ÿ1kendstream endobj 72 0 obj 2642 endobj 76 0 obj <> stream xœÝ\Ys·~ç¯Ø7寮# s).WɲcÑeÇEWœR\©IQ´ÅÃ<,+ùIå÷˜Ý>,f–Œ/I­œî¯ôÎ ‘år!Ìß‘8:ß{tP/NoöºÇ‹ƒOâút&+ÌŸî§ÎêŽy¾h³¶Z¾ÚYÛ6¢îGÍU©ÿß–‹:Y­œï½X®Öy•Õy%—¯Wúq.U½<±ÔBSEYU¢X¬Ö2“M]çËoVë6B ¹”¶i(Öýl¥ôâUËs=ÊD]ÔË«®e!E½|cߟÐCÖ”=½ ò–&¸Y­ Ù«×ÔáDï7×Ï›¡–õ½^­K½Á\¶¼Á)‘´›n6kÏFéZ=îZ©Fš¹¾=ülO¯¤”‹ÃÏ÷ôÿßÓG'K{túˆóæð/ôËu!dž)¥·¾.²².Ôrc©sK ”¬LŸJÔYÞ‚íÎÈÛëÌRÿœ>ÒQß o‹å%‘çe#l ¯¾¿Š<ÿM±åv;‚ݸ%䆘J‹Z~ÞzúrJMçÔeŠX—Þ@2i›çÒ;(aûv·˜5€¨4†LB/æÖYŠEIbâx•’¶+8.›˜A^Ù)—Yº7õ§>¿=6§‘ü~l¾?”OߢÓíïK¢ŸùœÈ"? 2'²$òC6ðj"ÄTÿ MÂ9“™ÈŤ>cDÅø»_ÙÕÓ*5£:<ÄIý¼ƒa…±œyÍܹó«î§h§|i)2î‘Nõ×ñ‘"²Xþ±û(Åd6¶×¿Š³™‚P·(uBxHúöEä˜èÀ)Iû^9€ºÎÉ#a ‚³Á˜¾bmcZ|»€ãn(1øÈ1ýùÎ{ÝŒüþž».–ïð†íß}#&„Qßü¢lÿM8 \1ƽl‡ðƒÄ¬ÅaÿNE3“&ý‚”åÀ1S÷+È“$ìùˆÈ/aÛ|ñÉq.å“¿¶ÔŸ,õÂRÂR-E¢\êÛ„N¤R÷ õ4ùËjãÙ&òÎ]\N=öÚÇÕ'«"U«¹À®‹yi/ÓØÃ…·eÞo,u1\rµZeÇgZMký¨.%»¼ºà—[öb ÞÚù×kêê¿zÕ Uçj\ýpÁg@w}—4ëÛU›Õ‰ñ²¬¤¹¹œzGvb¥íP—Á³@j’ÚÞTÝ€‹|…“é vvÑí‹Ó;WÐmÒÕ]_Ò1Ü®mÀÛ[ðöÈR¯-EÇìðDC€íää*k› úvÌ®Ìqz§Û°„ÑH’"ÁDœ-cëÃnä\5fíNþÌ,hÆÂBªDuÇo 7¨L\†—'q¬ÝÀr\Õð’ÀöްɉËNV¹Ô ­WßoSU ¶К8åãHìá%œôÝ*7x'—¤‘‚:¢*+㬱õ£mãiYÛ÷ùÄF|¤,3U” ±8<Þ{áÖ¦Ø%ÃCè'«òRñ%l†ÞzkJ´®,8b`§e6¯@›•*«TY·Æ:9sïùv`m1‡ÏUÂM*œÁŽèé†3‘Ìg¸…Á&fã2ò®,ëG(DnA¢ HEÅT­òŠ©ŠB¯EöÅTÚKY•WµFÀlµVYÙ ÑYû¸XÕYÛ«M^i®ÖõòéJfU#¤\~l)>’`ô‚F¥nÖàÚ4hóB Öž¥(dÁ{Ø^'¬ºôk-ÛqTÉG2ù*Í“Zˆªd‹é&¨›¼KAÕºm¡rº‘Ú´"/»ç¿ëpxzÚpÆš³ hÙ£NλàÍ”‹<Êðd|ü´·*GCbH“—£Žöw¨Ž“r9»:ŽiÙÂk ¼µôg€Pž)ÚejY\ØÁÞ0Ü댠g:.D¦5BðöC¨ÚÆ$¡ ©t Iƒ‰Ê{»k }–¼5GÓFØÎfÈœÚ2Òt.IS‚A3wPܨ ª›†s•°«Ä3qȨ“ö3›q­Ík :¹™ôo|Ùê¼^ŽKí88bØ ÒÂS1ᑵ†º¶6ïÖªÖØ¤šÅzpM‹/ô:J—eÍÝ>Á¨1âv6fÉO*Êqƫ܇Làs]òSAÎ ‰™€d…˜ƒh®m‚cìØ®0…!XÁÒ.1¡ Bž~(¡ã½¤î0lÅ>óSN sÅPìl@$íÑVí(Tl §þÚ²$h#/ŽW‚¬™sÐOq ]äX‰5’NÏ´ SІG.s®ÙÿboÙ:—$5:kgN/QÍ‚K¬w–» 7 u€£¤ÞÙ¹)Ñàú;CŒQ7Z ‘x U6™ERªú1WÈ ¢×7ä0Ñà K9uíaNÑ-e¡÷ÛÖ:®|ãn”x“À ,ŠŒ%?yµ÷¬Ã ØŸ‡VO9wÉôîÃæÆä÷˜l•61\XS Öÿ4"lÙ½li"­}óâHæ?ûÚͲ«pÊxî]æ'áòÑdÀlë}á£fÒϑъŠËÍÑ B§ík:”]JÆF ýoˆZÖ#3É Ðä)óétö©û©¥`Œ“´Œ~®YÚd®h‚Uéü˜Lz’dü¶‚€ >àé5d"7˜–&Õ(Ü1ôBs¥ÉqNQ&ítнÆ$Õƒ¶ì†,†Fu•)ùš_0¨2kÊZELÙg©¸*é3$¹ûÍòÛ¾&v°5œÚõãª4^l4†€þÀmwuSÎ ÂÉ3†å¬í[Z*–kǹ@²qC+NvÝ*7Ú`hÉ“‘·½Èhih……küs5Ïþ+mÿPzÑÀ‚€b~“8ÚzèÔ>ƒ¨”<™¤% ý.$€Ý0£Ç%U¤ÃÆÂ Û?›<é+)'ˆ8I¸Ûâ9ÂZúšïÃ?Ô§XÕãqv‹Q¨°«Í w?m—˜ÆÉ1RÓ¡—Rm„;8"±Øp…ÅÑcÖEÞDÜ 'öW…AqNDý‰³NØT¡ÉX,›˜á :/G30qá”9ƒ™Àý6YÇýî%¦ÈŠ6§6{¨:«Uá`Ï[ÆðÜRcÀÕÊÝm×±ð"–˜Û‘˜¿?yØ ±k2Á ßùBÏÇɸ(G³«‚«¹*—iÀƒ¿¤ÒdÓ Q€+²ÐúôãÇŹ%‡éñZ/’¼æ{H€ñjb¦^sù)ÚJïw }%œ“aã~Ï}ËÉæM<¦ ÇbݰKDŽ29¹ÝÑ}VÖb^S¥¾T ™`P2„ð„9@g,31¾fJ´çJ±£j™ê2Â!æÀ`o©®zK æäA86m@¶î×ÖoJVÀŒ+Ô²ÕöKšDBˆáÍ…ubÈc—x{¾Þn H —GRËGSpŠå…£_ˆe„Aæû ß6»yœÖ—p(G3Á¨É4LÙžNá07¸¾ôÒNø£É±5D4UÁ`lñ!$³‰Ø×ƒ°Ô™, ,aHÖÈ’§dëÓC†ïÜn±¾$àà͈|N|!p*=øðpã9$3.¨±ÃbàmòvBÚÊ ˜jQ–8ÏlÓ€lø—Hi’áTÊW6ë|mk­£áx9šøMz ½k„?ïÕ°[Ƽ‘H iåcx2 ™Y7F2”g#<â$³™O0™­ÐaÈbHg‘!¨5V®pe|çèú ÊÖý/ͺR­|ùÍ„0¦cg£ÍýúB{ET–öŠˆyPøîŽ5¤±+Uil½[íW0ýÛ©°éÐÈIÁÙ“bh„½®-å0»]M¾ó³ 8ÞÜr`\!mÆlÃô:À˜·ÐßeëÀ²Øw¡—rÞýMÄGœz³cðvÿj™Ãè&¸së·ÃÒ Lw•P‘Äf96‹~EO=T³íÇCÖÈ/*ádRÎèéPWSYQGÊj*› ù‹g°“AØÅVün…/‚…^øw“øKðyjí^74¬€ÒaN±°uxÙÉÐ0p´µ–×ÏÎñפOUYU6áõY²ç åiáÝ®: ‚O_§1®°Ý°ËÄ91Ó|qª€ØõÕN¶.Ìp뎙kð;ÎO¶™xW‡P°Úa[µB×+¦-ZZ­˜ïCÑÇ}ÆÌ8] 1¶=£ª4N71¦)† ì4Ô ž>ˆübÏÍäÒœŸÄXôŒ¼¦ -U`*ÙZ0/>J¢E ^Uà à !vê F´e%/¼7srA“J§»ðn·Û|Éà(šåS™H óÎþ¨CqµœÄ/·4§Êè™WiìH|‹Ãx¦Nºa”Ò„έ—$„-u,Qã¦Ù4þrMᛨªXjUïÁבºóéq]§hS-]ÀY1÷6ˆ(‚¬³`Ä‘W0‡Ñ#2†³£ÇÀ ôU}——Æô‚Ug2·p´Í™0Àg"Y„ÌV 5yYûÄÆÝ@ ïYÔ‡QoN6ç·ðu~‡ý%ög‡n¹¬´cÍn/!®Œ—uÄÓm¬S$1À‹«&1¯è¡_Ž\dUáä¾ë sJ…»/ö“[Wm{0ÌYr›Ùz6UPÏä[úÌ@ì'¢è˜œOÛy)ÅŒ£޵Á¯ —S¿g@_¥<½¥9= ~R¯ñè¿ Ët>o?°Ô+KÑgÃ߀ßÞèüöüBœ‘ìƒìÇâÉo á–³Oˆd_ÑbßÖÚ‡$þÐ[ÃO Aê37~ˆ¯Ÿ‡ˆ?äÅÆeyaß›Âãy 'ÆŸãcß@3BŸMxðCÞ^ÿîŸþÞþÔþÒ^ò[}øKxÜ@ícß“øäpï/úïÿþßSendstream endobj 77 0 obj 3902 endobj 81 0 obj <> stream xœí]]sܶ}ׯطìvº4 ~"3}ˆc¥u'qREn3“æA–lÙéZR")‰Û¦¿½Ià^¹+'ÓišNÁ’ \œ{îôÝ*Ï ±Êõ?¦pþöèÑI»º¼=ê«W' ß_}wÔe¥þ__ÁËçoWOÕE±’™lV§¯ŽòLÊ.o‡V‹US«ÿ–õª-ò¬U¼=úzýt“geÝ4y¹~µÙEÖ•\¯¨öJÛBTíúÚ–Vð¦Ý¦R…RVë3{å¥-Ýn¶m–çEÓðÛéÊï7ÛZÿ.äúe_YŠÜyÒeÿ¤²îÊõÝVY·µXÿ°Qwµy]ó»®èÒßS¬­;*¾¦kñsÙ¾±_Hßú’înRÞò¦~R/Ëè¼§-êîáßu ¯Z¡wéož]6uÃðÃÐ颔cO÷µ»á1r}Oug›oNÿ|$”eTy£¬åôB‡óÖö3/è® ö.+&ÍëŽj_÷í6E]Í m7;[b¯xÅ?Ìßn¶¢RÖÒ‘ࡹpGiìOvÛÓŸW°UÖI§êý‘býÕ¦Ô†+ ^9ÃZíí#çÌïøÙ 8Ó´7™¼Ê”“9£`›¿ØÖÜv|”‚Wèú£ÍV®¿´}ö±ÊLtìFë/bÅÕF´­‚»V¿ã¶*ª¬[é§Õ˜¶Á Ÿ£îàøímäÞC1 §l,,` 6^Æ d–c ¹‚PǺg•®ª×õP5²ªÖϨò÷ózg4od©I0Ö÷ç¢ ?`¦u&]f…Ƥ_ÂŽfÀ5mÝ•œ´îþ¹çºÏäÓ„½‡¶."@ó‚jé›±™¨i,dVT•X¿Û­*åuÚÐ?ßl›¬©êV†¸íc’z‚jW‘nýãFfmWņâedµ¶X‚ NT •n·â\8¯h°ÌBxÏ'|‹¹aÖn0¡º¤l±°AdŨ»¤ž•2o¯¨Ùk繕¦peů=ãî œÒNHçÎ Ò ×º@(DS‹Á "uÃðA;9fˆÖ"’ˆ{ßf쑺ÈßæÜcm3¾¢”Ž®ÆÈÀ>½8C…+د1x,VdE×x{Q+PYÆ#öÄW4Úq| y°3 ¦yÃ^ÖÀå6æ˜Ƽè"Ͳn„ð^;‡-6ö_–ËZ¸@ì}C7oFÏ V¾fMžÅ+çR8þF•­Â¦²Ç¨* bUè¾'à¤Í ˜ÿq¿SÓIý~¹HeÏT}aó4EZ&|½ÈʼP÷êï‘ùÓ#õßþîkÎþÜA9¹â„w)­‰Ìtðü`µ>2&!/kÑwƒß®ªšŽ·B˜y ]>öìÏTQ ®¨Q}>ò)Ù˜³î˜”æ‘x^ãIãt0€ÏKÕ€Ïú-K3Õnè ÙMqïSÖõ„Žï­"ÿã“p§¤y6CÌ_ñúrïJ8xyˆ¡02Ö+z¦4¬1Æa—±Á»áë<ò¦3BÜÑbò:“µå  ‘t +ÕEŒ@> êLau‡‡ß¾ÀèóŽ ˆ±çü‰@Ÿ¹7 +>¥žÐ3ÛZ¸8‘XQœé¼H†; ›B0 ›vœ‡œì<‚Tµ™(-•Ä"MåJ$O7%ñzA}Ïæþ%/2Q˜æ’­¼„Â>©û0˜Yް¹'‰\‚Y£!(˜RFDlnZC(J¦!8l°ÿîFÕ^ ÖRª—‡ {Ã;n65ʘ¤ Nä½Bì± [¤µqn·mÁÚ X49¯td3Ïü<™ ›Cu†õIņ ‚Ö¹x-fbL“ßs;vs.!8¦ ÌS âà8LƒÑ•YWÆè^ó·ßÅñLì˜;•t@I><¶v8ë’¡Á„kg˜fc J÷ ârIInO盽#è7fÀT9ßÉílGP´³• ªØ:3F5Êó’•‹DÚ¤*‰ž+”ÙŒ$&ÝáÚ.¾Glh†ì5\1j†<­Ñ]KÑ2/ª©Ý9<½™ÜèÍô\½´;÷Œí¬È&-êøë£ïDbÂ74ª½8ìŸÇ¨ur¬í7ðk—,¸YP}–ž0¹Rà§ÍpÁg{¦±U%¡¬*ÔeV ÙâöGb UXe»U6™5…Q»s±ìøá,Tง12U‰¬nî9 e<àÚ+žIÞã”÷RÛuU{d Hˆ¯í+()„î'1ÆåPý®6 ôqͤ?æë~l®°/¤O8R<Ì:¡ ”2$âO3,ýÐXj õ€®nšÇ<ý•¿±1.s!ýƒ¦3Öÿ>ÜxªëÖ|­Øê—ÓoˆÚfKyÖiiV«²j˜Ë\¨¹Q)v[ê¹R­méÔ–V îÄ–>²¥g¶Dí} ®£VžÚÒç •?ØÒ¿lé xÓOüg²ÔS=x…ñÁU×®ÿMWæTTl¨¸äÚO¨xJÅg “ðÚák›NÁäcªý˜ŠO¨ø^ð³ùx£¨9it)öV¿J÷A@Ï‹<8zZ@¥œ«ÀåŒÒ‡¿Ûâ=‡%ón؈±){ƒÞ OÇRpV^ùDgÞº¥™ äªÉÞùó̵îàûާC×½®ñ„z¬Ž/ÉYdŸc˜Mx1ü23"ô¶8CxX¿«‹ÝÀbJåK$³q¶zð´¨Ýü1­¨ŽnÆ>*¤³‰ofû ãGn>Sú4L‰ÑŒ‘8@T%rFÞTÏEœüóÈжbqw´<ãT gyˆv?¤ßñ¢/·hZˆ‹Ì&ûÏë?”UÞûVˆæHÏÉÂŽwÖ,{Ûÿ¨'¨µEQoü{=‚ȪÜJæW°»ûW´ñYÊ3›¸…k8¬/a Yj,ÙÜ%$I¢Ã3êj'—q뤚ôˆŸP"Ø1Ê u]6åÒµ]Ã5m&TüŒZÁÀÌ 4 ¤¾­±<>Ãé4J”j.®DåEéàLn'½°%iKFì…óX_ð† â4¶î ƒM¥§.Ãz–ì¹Ò W‰®ˆø_Sø>x|¶ÒÃ÷0­bm²)˜ŠìçuèJ'¬b>Š÷%êžrˆ<.*WOã¥MÅéã]z’yQlÜiË„‹%&Mè·ü˜¹éM ávfÀëÚ.²Æc¹“×dî}Œ6 SGYeiw3`×o5HrOöæ0:‘TQ“l ³f<¤{†.qJb’ ÿ}mSÕ’({³KþA¾Ç ×ÿ´dkAÌ6ìЦ!öû²×zîU­FV8olL[Öðv'×…a…ÊWdj! ¤x IÞm„ÌD!Zó®e‰FŽã55Ž}Ì+ø¥xqÄäÜKY¸µ„ ó¸YÁŠ^UÃû°$´gì3œNšÝýjÓ)³Þ rÜq,Ì:ùØ’†O;¢sÕI{Šnš@¯G”±ÌW¶\û]Ô­KÆ“|³˜íÙB¤÷ë̓äv1œÃ»zR¯P…Jê­[²†\côQtYÝXc1C ÄŒ”˜¯Eñű3û-paôÑY½¨]4-kEllI¦Ý­+‡;kŠAM“D B¼'ijüì„W$/Ÿ{v:Gj>ñÕØR4ë­­,@éCº5D¿‹y 9?ç¶„n¡ºÆ¯ÓRjKEVÛÁZ&箨xCÅ{*^QñœŠ¯a oàm+ØÂ5wðÁoS¦(Ö6É[t\2®&Çš§ îõÝA•‚â®}¯ƒNaŽk[ú~º7˜p/ø °çØLúÇ·±ÚÖþïÙ 6›²Qž¾´žþCYãÖ´»íÒ¿fäW“krMìÚÿoÈÁÓÝëÕ¿Úþ ôïo½zà$z¯eÈtKÅ;ØBk_ 샙™T|QˆÐ @Aß/’†Ã@ú°›ßÖý’¿–…F{fY8h-pÂÚ[Ó²öõ –Á Î ò ¸»+x*Íϲ?dåÚç¤!3e™mÎ e—U2=zrërA'Ä\ÝÞágȜǕìŠÄ¶Ç"9cIbž#´ @猄ÊAÎPVÔ•@$£¤Å=÷¶8{;™Ü4i‚óoØŽ6£)ó0FÓ·t öð´m,Ë`é‹BVIkßsóqÝÜ,£UÀ-¾LÀà›FØTT ™u]4§ZýØ4Ž”AºÃ¡Çª¶rŒ‰éôµ-õKaµþžS_8w¡ÚÖ¶°¶ƒµÎL¼-’¿“7Htg¸ÀäIÊ@{‡ÊXÞ^Øá^7²Ï…ˆú7e൲ˆ¼rB£¥Ê¶ zBØLæ'soX°˜“Ùøð6†TÉ û’E&%õ²ùŠÎ^a˜äd-%ài‡žšLuöŽÙ/xšÅ)ø”(°J¸g§0ÑôÌF ~‚À¬sþXžÚ<+rž`–ô\‚!1_:òàº1%½ðMËËOŽœ…ì%¶ûÀGrÌެ88sØA(C¾$ypÏôìoØ‘Ýf-)øï Çù0ð>{ÖÞþ%ë;ç ¹PÿLƒ`¶úNÒ=¹V<ÉÆ>€öÊßÛS»L¶¥Å빞$ϯ¼ƒ×úûS«¬«]ò·C'êMgù‘„j„vŽý™»'’½÷U&·:bU‘µ Ró™q¡a’ö¹u D |¨vÀðÁÙÛ|dÁû%]1xÆó×O#ïu_ûMïFç¾ÍDÁNÀ²ýLdß7À>ïãágê²ý*FyÊMâ0µ%Ñ,¦Ž/±is¼÷-ÑCA™¡–ãØ¥¨åûc–hÃî)hyïM·ë öܪÛÍ*leî—ôæÜŒ®dEiu¢§¬øódT0Æ&ŸB#€Š“’Ym`E.¨8²ØŸnËN ãÒ·˜­1Ž2œ?=È:X«¼ß÷r |ÌÀYŽ‹=ïïà]ã‹qdÉÁ懓ÖØžäcÈ4Sí•‚6!<ˆÑû† ž3¶ûë¹ hGu¸Ÿ.é,ŸýEýó_Ž6Xendstream endobj 82 0 obj 4773 endobj 86 0 obj <> stream xœí\msÛ¸þî_Ái?œÔ‰h|Øét&±ÓÖ7¹kê(Óδý ËòKÏ–|–ìäúë "v>%9NÜi“|ØPÀØ]ì.°ùs”ÄBF‰ýÛÓÛƒÃS].ªÇÑé×ÄýåÁÏ:NíŸê§§·Ñ›±é(DTÆe/’¸,u¢j®"*róÿ2”Hâ,ßü}0ŽD+QÈÁÕ01„ÌÔ`æ¨ÈPi^I:XG*NQƒë¡í£ólp;É,NTª®ÓÍ03TZfƒ‰{¶"F‹êa*5¸ŽrËS–|¤s3'aþ£½¶3"íTx‡9ìÀ† –±žkÕêŽúÞUO³BwL3dO¯ˆ›m4”Jÿsü½QÌjD®â<ÉŒ^ÆçFo†£4ÎUš þ ¨ñ°H;‹š Ó°•~V86l^ÓZ_2-ùrŒÂ„4Såt—‹µžªóZeE"ŒÜÓ.‘ÆŽ²¸È¬ð® ÎÚ–dgv]™Š56¦÷û*öËØÒz2O°¨5<3fbQE#ó”2‡bº ¶$&ÈŒsuNˆ<$ÒXRa´“+»HkCRªX¦º1›##}9gÖ­³d+¯ $-òb=›¦öó¡·¥”™„²ke‰6ÞHGƲëåx[r£S›’S[¸ßå{úl £Ö†éžæ*—ÌéÑ@Ø$+tíš_’ç|Ýî)qýÞÚ u2AΜËÃsµòéØÐÕO?®¥Ùµ2ëp‰ 8Å–‚Yt8‹#3Bfç ÞS3TRŠÁGã,b©‹R7ž)5þª6m³ÊÜØÁ»óÿïÆ¿Ù%ÆÕ|•ƒS"ûíD9¼y6LÛ‰Ûß6eñö.ô’+Öt64>_i7U«ïU`›´ã/V—g|Îâ.©íµgÍm¿‰…ÁÚþ6Tx¥g?¶&q¡­ Xí9¦‰q–Ù½¨GÝ:j¶¦¤õ[£"Q±¨¬ÃugäÒõºvÔ¿·ç4­ˆÒnGÞBrFäœÈ‘ˆ”´2°zJ1>:êÞL…4’S²cÍÚõЬi koQ¯iJˆ<"ò’‘DNˆ¼!òŠ%‚Ò¼€ nàK(XÖ + ÏŒén™ÝyùNá2—d Î!H´Žœ&OõaK®Wá÷ÒÆU×®+ÊNï”HÉ.)dŸ>Àn2“Ö%‘×$8<ð 0ëÙmHÆ´…'·Í¾æù¥ÍžJZÞˆžžYum•?õm•›Íríâªoc²Èw»m¯æ­÷éY¾(=ë'+·7paß¶—û6zÞB½?¼(Wy Î4‰³œVLàSöò°]®ýIöØQ5~öw¼ƒ’ð¾ÁšÁ9óš‡}Â|ö8¹§{(©ñ¨E½d5î™bf°Û HsÛ=AÑèõK&[ô=${AŒïä‹Mží”sHnÄö±ó¶’^†jØIû’É’¿€$Ný^A¾’È9˜ä=9l}ˆ~ç(º§ÿ«£Þ:ªÉjuJן«™T™§a.ãSŸT÷Žõ{Òû6lj“ÁŽò#ø¶i–á.éö@lUw>àcÇÅLÏìò½€òž€nÙlÛ*Cû|š˜{¥¸€O÷Z¢×í¢qÍ®¤~Ý_Näïãá–Ž,ø­/жS~±vÛ{&À®û‰¯¬Éb{õAô¸ó]îÄR×Ë?óý3É\ï P¦>¶–Ù >ÅGˆD4‚ŸCd¶€sÀ× l»ÇIpìdO»ðGùûqsjE±8ëÓâ9œ26õ_ (°’ðÕ?βfÎðæþ¢‡›VåAUå\nïòÜ1‘Â5¨“‡[u´Ú€€+aÑÿšªw ‚IxÕ{P³c¿W5SVdc3 Â¤Koi޵=]WNK¯üh9b8Ç÷ Á€ >Ý ¢g¥ ‡Ò%զ颯&¤Æ:Š¢Á&`ñ®†H§=qåϦ\–2ÛE¾GT„>öȦ^ýÆÕ«q;²Œ)™ø&à´â=RamƒJ} 4‡Êìŵx:t?tÌ×a>j„ͬUÎ3Ù a¼j73jLf´v,ÖnÎ3 d«(óýÙÀ=.‘ÿ Ÿ[»ûGÄ’FÄBÂp Œ`p.ÃwYrÀqd Ü–Ä€7÷’¹ *¹ž¦2zF(Ã2 P†™L*Ïoa†&®&q! eglÕ——IR¥¿îq6Tqi‘ ÑÐøê¸PÊì%:‘ÒÆpÃ,-U>øèþè¨×Ž:$æ°÷;»'“T¦Œõލ7볪H•$En¼‚íŸÉ åæ^[¤HkMͼgQ¢µñËGnä…ã;wïÄF ¦7†*UföÁ ñœÕrÌKÞ‡ileÖ˜šþ«×¨UÂÅjrfÛÀèEñHi–äq¦­²M§ñ¿¼LÍ SÚ È¸ j¶ Ñ[@K)kxJà<¦ÀùÖʺ(³¬™¹Îr{EÑüþÚý~H»’Eêÿކúˆú3/NzgmTlÎŒFzí& Ž.^@@~f[SéᑉiYp´ ô™ Ë™!6y`*ûUÄVðÇÅ 8¼DÆâQ[H«`¹¬iïXý)Òœ!F®²|¦w0»lÅ`á›VSEõI(¿_ÑïÅïĸy¸¶M. øXK\BÞÙ‚e¬ƒ4€‚ö8\x— Èâ. ¸_K- ÖfÙsAÔ«§½u†¿bvI› ¹X#½¸DO§& ‹3Y*sØöˆ-ù’Nt.Áº¬Oä2±Ù/  ð qöE>_3ÒX碱$ÃõUQÍ;)ͲdKóS?c;h ‹6ÿ(U­‡%­æái‰Ü ²ecÓLÖÙTj†³@¾®¤ÜRyõ þÂ5]!O1K  Ïð)ƪbËîuœØpž`ëäâá+ j{Q‡»¯x+ä{Ù.åèšÊ+x–¢–t÷×~çÔ¯J„ÅÂî²xÜ­ª½»œÐ᛿ëÛt N³©wâÛâËÝ#°3„µ¡ÚÀoô'î0ïì¦CTw¬Náö{&‰C ¬ª#£Ò?ÅßÁ³[×}}ÐuÙs—ò•ά­‰¡Ë‘yŸf›k3™úpšy—þÙ´iÙý^x×[Ú£FIí‹OÿÄŠBñw¡/êð^ͯƶ:θÝï-{oXÿPßè| ³”_=E4ÓÑ ×Ü?ƒ[í‚Hú¿vDÁú×OQ”w‚Š¢ NŠ¢¬µÊý´óÿ§·Ý5 ÷"yš›PΆºé 舎 ¤Ëf£vbXѹc X’ýÖ:Îóp )úŒ_k— ÁNZ´šrñ! c 4ËbìnËÙÙo¼SÌòVœXR^<…MCL˜ÿM$X3¢Ÿ{ë­4ü¤ (uá(æ7àq¢9Ù¡->1´Ó Ζ³º>7Ö½5šcH#ݤ²@œåÃÏØõH;NV¬XJŸxb-ÑÙb¡28DBm5£:q±–ÓÒO€î†zH¾ˆ9úùÕÆÅxß«bñ© M°uõÂfgP¡Óîo]}"H yB|¨f½òwDý_Á ж“+ŒÚ°ëýð_ Ä[…“·ãƒ¿˜¿ÿss;õendstream endobj 87 0 obj 3336 endobj 91 0 obj <> stream xœÍYÉrÜ6½ÏWð2å±/7'.'%—RIä9EÎa2#ÉJi)k‹ì”ÿ=t“lr¤’\ŽF‡ Ñ÷^70 ΄,xútÆæ|ñòÀ'׋º¹8ø¹5®Nž©ôW7`{s^ü¸Š…( ¶X/8 Ás×D…5ñ{0…œ¹èp¾8,©8ã6Hã\ym'¤våyµ”šq§\y™¯ª¥p±QúòS%“Bº²ˆÝÊXËUê7Œs!Št‰hkÜ8òT’»òÌMµt¸«€(§•N­AcÿëäCNåvyÆïÑvF¶o©­EE/øºÖõ„”ñª¼«â£7fbÂ7d€žovèVËùvFµz›ncŒˆSÈí#1ØŸ«·‹ÄÈbµ¿ˆß¿‹˜&c"î½O=«ïÓ,—‚i+fœÒqfmWDïq¶Šlý›­WÙú’­u¶¶„E=ãš°àïËlžf뢱DP(6D¼o,í]ù¾×/)ÛìñHÀZæ•Më‡hrT ÉœW¡Ï®ñÿÁâÔ˜ô^D£èÿ@‚ò‡J%‰ˆ y]ulaÌH£ås0²›Õ%l'#¹´#BÚP“,7¢AS„¤|Ñô™ö’ 7;E*ˤ1QdWÛ¨©sÌ\v¾ËIÛ„«ÕßÏ@R ×† &Õûâ71oÄ\ˆHzž÷`>ºëL¨^ÒÆ-ÚæF=€ÀKhýµZZfµáû“S •0›oíƒù@$»K’ëc>dïÎPˆHò¨ù • ‘r&æC¿ ´¯5/m™–,y€Ç%jJá°!ü¾ økðΤ­ù-¸>ýlŠm.pN´*˜°:“fVäÍU¥âÒú¸ûëÝ“S#º)¸0>_n·ÐO§ÊaÎa`ØÌ\EÓ&Óí.àÖxJ”IÏ©~GDfeM·‚HB¤ ÏoAòmëÛw€£&wIÅT]îÚ’A/ûËׯGºÑÌÅÆ* <ËÓF.`Ü¡jö/reÑjT´{¬g” Ò†ž°Æ«”‡ìÚr>,Tü/—äÌ9Dpf¶Óa ¸Ùì€kå,pk°­Éî9 ‚¬)zãÞÀ‘ÎEupuÁ£f:7ÓpÝøRir›Uôæäd„û‘Êž·åé¸DˆS¿ÎDkÔ{/ªê ZÉ38`ö¸náé¤ÜÍ…aÚ'%ñ/„ç D.¨ ‰l–¡ÇvPzškß-Iû;,„ ´Ó±&dxp4CI-¹Ò‡ªÊFKúd!•Z=4ž{–3ÁçælÔ$Ä¿Ê"pÓ,Ct¥ ‡ÌM ÅtÄôÇÆ—4±뢫[D8Ðþ–­(‡^U¸îAÿ‹]HE¾ø¦-UpÄöÓw>=|Pj5¨ûº”Çže½@/Ø“¥®²Gõüã3ôi:C?› öª¯ME[ ¥» Õa åjZ»z26:æ·Á†ÇüTz”¬_zOÕÓiŒ7ïÉm[ éñæÈš©•{êBáÝ£~¦ŽšÃt5'Žz| ˜H®HÀÖä‹ÒI¼a¤s^‹¨ Ëî·’Øà;=HfwÔwSTLžÔQÁ<¯£ÑaNG¢MŸ/°d¢•Ù‡`o’&:D#©µ…î÷hßåXQæj©ãv$ Ðò‹öëI“©ÕgÌ6®ôÇEIÕuúÉ¥¾]ÈÍ(Ú ˜S5 Nd-8p›/¡÷Ž:‰ƒ#È]­Ây1çCÁŸp—õȼMcàvûºÎê]"Pœ«cÿ { Ù¯F²â<º¦Å(cô,† ^ö Þd« ïˆ;ç©7šþEª>·ÓŠƒZïÉ“?ý#]<ÒÓ!ó×øa¸5ûæe¦á†ÎêQ4…Œ”ûZªJÕÍö ëƒ7«Åïñó؈*Vendstream endobj 92 0 obj 1496 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 28 0 R 35 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R ] /Count 16 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 95 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœyXçÚö ìÌÄ.ëRDgP1ˆ%ö‚Áb Š¢‚¢ ¢tXv©/u—]аX0VÃ;±k¢Ø±äX’è1Æãw¢ÉIžÉ÷r¾ëgØ=¹¾üÿ‰À¼ûÎ[žç¾ï§`EÙt¡¬¬¬ØYÑñ±a±Òï£D'+±q€u:öÅ?FÉPã·ö(èn…º[£î6»ûÞo+&÷»½ ±7eme•PX5+:&16bã¦íÎC/s>|„ydŒ»»»óºÄŽOœ½Ãâ"6nuþ”ü²#,2:&*lëö©Î³ÈìÈȈõÎ#c6Å9‡nضAz-(42l‹³ODdDLLô硳ܜǎ=f$ù6Ö?"j]|œ³_ôÖhç΋Ã6ÆG†ÆþÇ EQ <·®_î畽a…ÿ¬˜°• ½·…̎ݸÈ'nÓbßíKæÄo^:wÇ–Ày;#Cƒæ'D­[6ÔÍyÄÈAŸ 5zÌØÔãÆ{Lž8iõd½ûš)S“§¹¦L§¨‘Ô j%µò¦Ü©Ï¨ÁT0@ͦ¦P£(jåCM¥FSC¨Å”/5†ú”ZBÍ¡ÆR®ÔRj.5ŽJRó¨ñ”Dͧ&PèeÔj"5œZNùQ^Ô$jµ‚ò§fQ“©O¨®”ÕƒêIYS½(ª7Õ‡²¥äTÅS})5²£¦Qö”5ƒr¤¢©~”åDõ§VQ¨MÔjŠ£‚ˆ—É‹‹¨›V3¬.w™ÒåëÕÖlx›56­²²c´+]CÿÊx1zæ7v;ûæ“ÙŸ\ë:¾ëÙnÝ¾í¾«‡Sòž.=3z¾èUÒÛ¾wBïÇ}†öÙÖ§Å6Èö”¼‡|»¼±¯{_‚S\³ ·»o?Ý>ϾÕþ‰ýý¢ýO9¶õ‹èwÛ©¿SC¶ÿîò³|Í1Üdî(×Â/æ×óµüsçYÎ%Î Î÷öß“/$ˆñ‚Li‚CÖ°A4(Ñ5ZME¥J“Èã@:1SšV¦®á·ÅÏ”žÒR¥'ÑVÓÕYº´Tµ2‰Ããñ!Ƹ¸ pZ°ÒÂ:˜ áÖ¢(.cW™Å«0š6h5•*MNã¢A†×Ò‰*²Vú¸;}Ûâe‘t¢’¼¡“††Ó5eäLr¤iôp•A‰ Àé4PpZVE“±“X-X5Ãtp‡ÉÖpÌλȰœl«JK+S‘ERɽ´*m"?†2±ZVi^[ÓI¦Vó—h<¿Ä“á¥ì+‹  "3´joÜ’\ö•#Ø‚­uMöò'0Uœ ðn"ͪáëñ«:Á´ª{óµC ”>"èô»ô¬üÔÞÏ霾@õÞ„ý;*bQ‹Qc¾}ºÚtˆ$>4ôÓÙWÇùmܹ`G‘,@>|JNñR°—7ˆ›ÅîŠÐcAµþˆÅý†À¸ïO£À¾¥¥öX3ÿ¹aW ª`Ë”e™ª¼üL·Â émñíÞSŒ×Ò0ÿ&3Áƒ8'Œp«·D‰Ášü¢´@õ5úMëÝ—·×žt˜¿vx:Ͼðüf‡õæÍ.ÒS@Lf^Yäç¿f"]q’Á¹êÕGvìÙˆ6²~KVxsÆ×AoB[iãã b4&þu É¬¨$Nå.B1ƒ/â·²b×÷ qnjñ,'?µ„®À[™[¢•Ñx]ÉÙ/ˆ³Øvˆ+î…û| õÿ =¡¯ëá¾ü:›=Ÿ2xè¤)..“ž}üðòÙ/¼ñýX> `S»»ÖöLˆë¡§½3÷RÔÄBïŸ~„n¼ütù÷öXãÄÏ%ÅÇÇžn£¦z¸ öèÝ»o[ÿiBx 0L°wA°ΫM9²éÅÔæáä}]GážØîwpzÝÏÖë2u™Y9yÙj~ý¯x?„)4õNêo¬`cx{ÿÊ{ôµìͶ“# Z9º4Á•&‰Ùv0©Q•¥¦f)€âÛÅ <×Ê®â6<ŽIÐfV–k k +Éeð³¶µ2yBÓé“fAo¤ÈC&J†í±;vòx6`ãÁS À U€ýÓØ,ÃôH`Àœ$?XÞ‘ y0i†%6¢i°û@–Ä_b`Ч`ƒí8m‰U¡-^!ÆÃ`æ"jH9¶IX·o! b½˜Aㆎø³7oð1!å{q ¼RÈŸÝ <<à ÷>wÃÝþ1 z>½|¨å4‹†2I¦Pjt¯üÁäà^þ>H¬¾`iGíHZþ ûƆÝ:Ôu:qëÓÖŸùލiáäjÅ$XA°E„ ¾AŸ?u¬e·“·Ÿ;›…P!bËÉ6eYÚ´B>²ÔG†Ø1óƒ¼xÿñLg`ý¿r³Ð㿚·‡YЧ Pd2és²¯;$+` ô«Ö·?Mº?ˆÇ-·-ÞR£SéÎ[aކd(?æiË_ŸY‹ÇóíˆZø¤ôFÁuiú]ØÙh/?O¶!*íB'šàSý³ôTYv0FÞ §Ýè$²Bâ|›'ó'‘§Š0ž®É*KK1âô~ðO˪e‡½œ°ÍÈ‘˜ÅÌOÃÁæÁ…Ã-gx¬`|—úrøÚŒ"†nFÇ«5:Rq ,YdèEFþ®FMQ¼k%0šéñÙHO£ÖÖŸ$«cFHiŸ ¶FîÃUâT®ø7P ˜Ä’Ä  Ø2vÐÑù%ît,7fž:;' ©ÉtÅEÅÜ—å·õ‡PÚ•û¹’¤cʃúÜ]NM_®-Wê2rsòrsù-;“Ô;PRÆ•±ò}Ôö¢­N;PJ~d{N|¥øSÚ"pi‚¾Ö–ð¨¨0Ã#-­pÛI*UVV–š°øWØøJM¡—(Ï(èc”ì§àØ{¹^ôŠM‡Cª#Vîãæ=Åx£—nÛ¨ZF¢¢Ë¸ПÈ knl<Ñp²ú*z…¬+YÎ&••êj¸7Œ!G›’’E"{*r|iÍ\4l ]¾*ÊÍb=<ö·!0ùÆíšú«íÆ¯Ä £š¬aQèÆ²ÚÀÝ<Í*ÜŠ÷Ð7‚ß~ô‰ƒ:•^™›—£æÖÅÍNZ‚XŸÐ£Í<ÔB´9ëP®Ÿ¡‹!þ}( ×8 Cuůðx‹Ø÷?&e7†Š tˆøB‘®V%ò‹‚Ær®Ž6¢ñ Éfµi\;¦@ŒŽlOÂÕ“öòAâ"±R!× ¡!5Nƒ¦ùŽ­ÙP¿?<ý^úý´Ú¬º”IÕ[Ð&Ösvàgó£‘gsnKþ.ƒKÈItSFRUy%ÙŽ;ÈÈõÁµ¸»NÐýû»ï›·Ÿ ÛÇG¢µ3*ütqš˜ê¸ê”Cè{ëÆùçOo„,,6b¿T]áTQª­áa¤ÚhÍ?˼8—hN´Àì@³ V‹ñû$TH9&AE7sp‡á:»`ñiWsº¯->„Þ©ê°2ofA$Óyû+S8û¼)ô£½|»8ßî±¹ •'DˆÝIŒ±p~%-?µpɨ9NË× çx¸4Â\À^$ŸN»°ìo-×\¼Ìuë’ãÅä–IÐ6”ñ]<—Ã}-*výææ­·¼¸×›JÂÄ´NÇWp{éÊNǧrELmw¬G‚”¹{1Éeå•¥Äk@·­Â4“h*‡j¸‡0µ-~ÐÿžÝ›32H “ÉÚ ûÀœÆ‰ŽÇ¼9'†fM"ãÒB»«C Xg ¯í`ŠØ]Öh¡ÞÃÍx¿C“Bï•l'Ýà•ì–¹{`9I aZ[wY™ÀVªµÀ±‰T€ ðð·—·Á,˜£8}‚ÔÔ³e1´ü¿Íç³7OInBãõ°–TÇö²3´¼Í¼—y¯=4öÄ®2±Û³¥.´‚Ì3Þ™à#f VL•P±W½±¡T¥.MÔ*Q••B̵‹$&oè±³gësï5oZÿ£^JÌ$³Œ -™…m¾2°~õ l¸ÎHòzFâÙá-«sæF)Åÿ §Ïó›²bÕžÓË9°c jmzŠZ•Àa7æÁÜæ¸[ˆýå»çÆz£Ã7îîÖ¢ŸÝ âš ×t1ßó<탎­l9=kgÁCÙWÿûDý’x§™~ ú7Xor“˜oŒj&4}$ÏÜ¿èÚfø¿H^7½pl À¯™’÷$q’Ý?,虲jéû¾_}>/5q)—‘•…2X¥6S_X,eúLÏËÏÍüfÙÛû×ö_¹Á²™³ Hjù„Ô7ß¼ÿåï'Ïf)›l%晚|ž°Öjí®Ð&d‡t•Êäéêv=zJãÀ¶xY.ýÅxÙcƒ¥*³4шd¥5üaú?jŸ à)ÝmŠgßEž\ø¯qüÁLÊ%´, oÖ±°Òa—„ãŠJ Ç’Ðu¸ÿâB>²€ÖŒÿ¿¸ÜA(ÛfXˆ'“ƒô‚Ix¢Ä§×ð¶*`Œ•ihù“Î~¶2ëáÀv¸ØÉNy[i4‰µÙ³š¬7/ œÌ¤å¯Íå‹£ù¾4áã0…‡ÉæY¨ŠƒyF¦eòA#Ìh\ÿ¬È7©°!ñÔÓ\ÕãWm«jcSQßÎý‚¹påÄ‘ºcNõ‡#Öóøn«E¿åŒôù‰k‡öÈ;¼9Œ——â–¾ˆ€côµUç­Û¼cÅ.þì–½kQ(ÚšÌþ¹ ½Rv7ÃdS:×Ù¢åim‹P÷î5-oö>s=âôxÿú@ïïq/Ï¥[¼Wðàóµâç'S»Mu6tÚó¾}ÖNbq`íKÁª ‚Á‚­¡—Ý XѬÛ\ˆ†~M@ZkÑubFË÷ôâ¶l%ö—ùÁië¿DŒ‘½°zs|­•Ì{nX‹cÈ%”H­NŠñÆ]6àO+r9:ø¶ÙV¦,ClEY™¾8¿0¯„Ÿ }¢> ßÑï‡~½= K Q1«Ï,ËàŒk}m d­ efFÊ+Êæïá>]¥F†sÔ`Ü37å¡6£,S¯Óh>¯æîƒÕÐýŒ~Ùò/_°Ù~BY¦D¬©§OÈã¢ðÆEù¹ù¹(Û1¥"}W…¦¸VÏÁ¨–Dˆ¤Vb)ƒ;Ø6ÄŒ^2A|FêSr z\Z[¦¬AŽ5¨LWTW¥_ÙGuÊjËQòVÊn+8 Ÿ(6DE‡m<õ¯£O>pâDä0iQXÔ}»¿%ªÍ¨°àó$zÄT«õ©È1 e©ó“’ñ‡Oé*2ª)ГBLW·{˜Ïú€ÎÄ}d“ÌIÃs: ÇÊTLº.S_¡)«.àvA¼ìg:Ù8šOVÒ·h˪yiü©¹OåAo> iµOi-ô‘Ì$îø£‡¢H[X„JXR«ÌTç¦fqøoÿ^£ÎÏF9ŽJ]¦NWRR¡‘œ˜Òh2c± &ï 7(l­gb ã\IbýS` Ö‚±þþݬ¹sðô¾ú£5M¨ Õ'î‰8´Úà‹æ³c4?Á7ju|DÄÎP¥͵qõ[O'ÞA7MM,+8JòG¢¨ÀþÉ9Ùj¤$‡Qj‹´E¨˜‡&Y[æ-þ†ðÀ¢Í™Tcݯ¶¢ŸÞh//{AQÄ\9ûÍÕW?°òµDìzÔ¼Þïõ„çÃü–D‡s qiQ(‘Í,Sêô¥šÊb®êúÍ“wûèöBŸ„è‘£xœãdŒ<@ð˜6˜º œ¼d“Ô¢€Ã$ØNP ˆí1[ròÔÙ(•%Tªºxòô5<Ô°¯îxaUa™U ÊÌÒ”‚Åquè8K>»¶æt`J¦*=U“cÈåŽäì݆"XLÞY§ßy4vs\Æ´N²ŠXGö‰’S˜]”‹³PVNnV^®C^n>áR—di³AÄ+°ˆWªr²³‘Êåä’…YšÜRD<]P\Ľ ‚­è.$»*ž) Š HåüÝõý'š¯ôûaæm×e+’£Ös;“S‘’% Ôk‹ŠÊ˸Cõç —ûäZðüµÑBùÊäü•ˆMÊh‡­üî»s§Íœ9wÔÒ%µÇWñê’œ¢,Ä*3•Ê´òÔú$¾9ælÚ9RR3?=ÿùΪÓ´×K}ø^JD;B";˜®À}G[ä­sÍÿX }ϙӢ•"7‰Æ m²U ì8…&*ñ›lŸ„?¦ @ðû‰ðœÈÒ^DAêIJ¡­Ô«5é…<fÞøÞeîÒ#9<‘AÞ5þǃ–w±­–¢ÎG||òú´µhÚdˆ9u*é ºÎÂxn¾}sçÜ .˜Ràé&±ÞWŸêt…µ\“MZnZ~b­?vþú•/Ÿò&ÿÆ£Ö›WžƒG¥1dUþeÈ"‡ÚLt±Kœ=a-@›¢ ±0©ˆúE¯ ö˜<% Äbe‰ª$§ ["›*-ñxò—·nÕ¾À·~sì{Ÿ°à5zâ®ÃfLÄÔ'“Þ<¸ÐøLà².)âÖd$¦$oÙ¸~G8bg/nùöû+·Zï4¯™´Ÿ/ÎDù¥ÛÝÑ>ç­DÑCño›ØM0 þ:.*áß~h«iü`CCäAS¼×ÖÒ_vº}Ù½»Ð½Eýgè{Ž endstream endobj 98 0 obj 6736 endobj 99 0 obj <>stream xœeW XWÖ­¦©²P¡SH£vpATFT\bdWQdSAšEš½e‰ ¨Q„**ˆ†}QeqEDm£¢ã¾E!Æ1&Ž“ÌÑäVçµÉ¼¢1™üÿ÷Ñ|UõÞ½uï¹ç{KDéëQ"‘ÈÜK¡LS¨b׆OuITF.‹PÆ&¯W+SøÑ"~Œ?Vœ†©É¤ÇRÞåÿ2B†bd¨_3Ƭ،Ÿj jØ>’‹DÅ•®‰I™)±Ñ1*+[¿ÀI“'Où󉣳³³UDæû+7Ejlt‚Õr‘¦P&&Å+Ts¬\Én¥2v­U´23)&Õ*<2R)˜„+qV±Êؤ¤Ä4+[×IVÓ>úÈq*ù7mil|ÄúT«åá ©VÞVBV UáÄË_(Ššá½ 3aíÊ%‰‘AK]“Án>î)Ñ©1~ªX¯õ+¦Åù+Ã3â#l'YM±wøÈqÚÇÓfÌœåLQS)k*˜ZF¹QΔ=åC¹S³)jåKyPs¨ñ”#5ZNyQ©ÔBêcÊŸZDM¢('ÊŽ ¤¼©ÔÊ… ¢–R®”5œQF” 5’2¥Ì( õÅQæÔ(Ê‚’R–ÔLRJŸ8]EÕS?‰ÖˆÊD÷ô¦éíÑ»¨7 ·ë;éGé·Ñ†´'˘1îÌ“a¶Ãî³²Þl{‡Õ˜8$œ7x8¼tøÍ.#êF<3”Zz†¦¾0a¤4Ö3v1n5±7Ykònä68bÌS¾¯JÄsšxÛ1Øá]<Íç1Á”Æ?1Æš(T fdÏB²§&rx" S;M<0ÚÉd;¹íò´L9ìË€=´ÐƼ'‚Xób¾•ÃÚ±ÑüXÛ3´­…Y´ßêhœm¨(W¦Â¢²í%ÕHz/>\zº¼¦¦åüª1³â«æóˆT°ƒ‡àh˜¶•+æô”÷«/ÜDÒs I!ó³óÖ˽ðß„¥ÇðÛÎÊ¢ T!}ê{qÚÿ¤Åþ²Š~!$èÈEÁFÌ¿„ï¸u…{Q :õÙ åE[X¹šŠÄzû¸‡c4U7Å´Dt§?@À¡K[2J·uIþˆ]–p춺ì˺sòC_¶6t££¨>§,mOVQNa$k¬q@Õ7{Ïrp 44veðbœ;riìÁÀxüîÕŠipeÀŠ^â"< ä†ì¶šC ÿ;¶ÐþNƒ5ä‰ Þ­íIç{h¯+–°dÁÿ¾›ì"p½Ï°l RÈÒ‘?ËCŽ«}æ†x+'"'3,€úû©¾«²ŽÞ¦£ç,Œœù•­ÇúÜÕãanÁÙ©áråò0e I2²ý.H½})×¹ŸVÍO'/™^eölFI2øIæs]tVÌ ¼OÕ^³‘sXà§Án ŽÈ™•´c³[ÎonªëÕgeQ¼×ulµ;6[€©±í÷Àè!è}-ÿ²d¨å _Mã9 ^‹o9Ã-áÆkWûâZìtÌöjzæZ3X¤Ý‡Wðûè÷,€óU°B[Eƒ­` ‚ø]ðè˜ÄñbÍa>Ûq¨°•¬¿ÁØÄ*`Âl™ÛÇ«ñh„9O3 æÃ"°€ùgd؆Ipp œŒX» ®·ý /ÚïÈ;\k~ŠÔèdFulYúÎ DJÏC*è'ÐO¨2ã7 0µÂ ¼V vzW ô 4W/;•xIÁ‰”ÃÂä’Û ñzú¡×òxŸ0ˆU¤UXB@r ˆi¿f@ï¹|ˆÆc_ÁpRçë¤Î[…:ß4n_{ñ)Ô†Î~Ö‘ØÝî[ç‚æ¡9ÊÐàÅ^a„ÍY<òùL0£ë î–a“®X‚X?Ek÷^´gÇ!9l~Åá$foÓžŠ²òº¦•'P3jü¬,qwVQ.Šc‡#LEšd‚»¶I¦4oÀh´©ƒ—íƒÚ¡5`ø>•ÊôÞ`L?6‡6RFôìøý»?žÃYp÷/”á6¦F;À 3Í|ß¾ú£¹Éãb}äk> NœŽt ¶‰7þ5ñÇ ŒƒeÌt¹NÝÓÕÓü zŒ.ª:"ŽDŠ(õB,¾:Ä;¦Ÿç¸³Hƒ=ÓÕš™•&O‰Ý sMüú’Ÿ3ñ p“ƒŸ˜6ƒ3ÍëÒªRÈSº[9cÌKuõuº 'Êû/œìGÒ¨¤ bûa°¶ØR¸ å¡Åhqx¨KíÏ‚¶á„—oÏ«À“˜`/0å1_Ee΢%ß^KŠ8ì3z>úT°Ð×- ë!;ë=˜3píDå—×e\²ù¬rARÉãਚFy!áþ8&?§ mAŠÅÉô–Ì3[®!˜@tï |#øn É·øð8îRKÈl<ßfZ`ÜéÛ`òè~ù ÚÂÙ÷ œ@¸§ë÷A€ßtYïýÔ?5Õ $0 Fƒ­Œ%yÅ÷ñzÜÎ ¢®¥ìËùÝØl®Wšbl"Ñ¡å'#_$e$¯zeFål߸9Z.Èðø<*Ùåm*Î`ác¦øàÆ’m%h¿–•°’Wµ`²«¹Þµ% ,W74”×Õ³’oŸ¨ï½Dn8žt0®ií~_Äjþ€ú?;~4Ìe΢ڭ•Yû7îù e Äefzr‚2'ˆðžó^¯Dü4¾›Çñ‚$+ñxÚƒI·§o3îø›íÙùÙh£tÅ åý£u;ö’Uý˜¿…PFªÊ*9°«hßÎrù5è ¿bŒáÈ ³a!÷Ã!õâ· ÷½pâÉ­£^‚Q1Aý<ãÜñ3!h][˜ÌøâŸd.CßòlÇÜgô-þ©ßfÃøV!jO|÷³Ù‹4(¹¡s?‰«V~ž Ks´†yù™(S:çrà‹]¯wɲ<‡<=´¡”à FGÖ>]) æ´ƒœò¤‡þÿÌ<:2åÿ˜C—†-ƒWjåñ¼\¸|@R¿e“_t¼çV¸ßºöo¡ayü² ‚Æ38GÑCÝg€˜€)bÍDbk­Ýœá´ö#$·ªI} hÿŽ/äoùÍB´ Ÿ¯Ë[‡ÖK1ûÏ0ùEù«²žö¦K_“-ä¿ý5ÝÔõh³ó¤9äµÐ¤ëʹ¬ò­eèáàî¢=½ ¶ØYQtíC™ÍªÊ¸†à}î;¢Ù~‰Øœ…QLy÷‘ªëˆ½P³1>-{}ú&yz.B®›|7[„l‰ÊV®P¬[Š–!E…¢1•´÷´úc›N¾Œ:k¯tmÏBÛÐç¬$m*Úº+—%ó!6xNÒîœæ9:$¥ühGëÑ¥òó{¾ØQR´»pg¡´´hGá¾Ýì5àà › ’Æs‰9ž¹f — Æ•8ΘÀÜ/ñ\áÒøW#f'ØŠÁõ´ø6<>p§ýôe$m«WÅälÊÏ•/Àÿz¿t¯ýd/’¶V§GçägmÏ–»âWCrPÜX´_ƒÀGOÿ¿²ìó!MKÐ"šâ³¨¼ÏâÿÎCüVëvXGJ"Éx_‘}Ý´¤= ætÊÍÑd2¶WAG<ú?ô Hñ_%ï†}t7nZ½ ÑcaZ½éÃÁVÏ5AU¢g0BLÆaRg?œ8/Ex²kÀÔlÀþ›Y 㲡ïH[Áâ!Œi¹ÞÒ}÷vÈ– 8Bü‡r=7ºî¶t?‹³¤û‚ ý˜òf±™]†/Î<½,;ÕSwÝfAÏë6…*r”¡².°¥¡ó=†{A¿¯ú4¡Ï7`NA±FŸoçÈœ±íb_{\Æ´•“ÿ‡‹dÓC±7ÂÆ,þÕ|0zþôÄ]µìì•C}è.ûÆ»×:04S$+¿V´»¬éÐÖÃ=èªÍÜÆbkìÏuYi¿ì—•1­_ö>~ ×QéŠ0ÆÎòË×u•Ÿˆ}&aû9xK>8Ô°ü+ aæ[ ,†BÞð–‘²_›PrG4`˜“Êó9¼9w¬ª²ì‹rVòº­µ¦óœåw³{±l°éšŠøŽhYPJrfr<)pÔêõ>+,±èk~ý¨QÝ%S_8úÝBWT-K[Y{2ÿK^w·&¬Yì·Ò}eìáö^uçMùrÜÌIÞv7Ç­tZ¾d®ÿÚcÏ®þí+ùPd°¤|b³[ s"a«ãºÎÜö Ÿ£M¬Ã}ï_@ïÛ®7WdW> Žˆ(Œœ×GΠä< “ï.쉩^o.58wIž’».'15`M<F$oCãjšv …»ä°ˆ_NfÎh?»ÅóæÈ%?+©.?%¸ªjùV¡†Ô2@‡ñ#€ÞohHQÿx ¨ endstream endobj 100 0 obj 4030 endobj 101 0 obj <>stream xœ]Vw\מ•Ý™È‚ì® )‚= è‹"U5ˆ& R–ÀÒa±Q¬\bA^@šØ *„" *Øø•’¢$–$ÏÄ QƒXÎð.yïÝY’¼—÷Çl¹sïwÏùÎwŠ„’Ž¢$ɯˆè”ˆ$uX¨ƒ«&:\\š.XJ„ £+=„5C·†´2+jEI¿2ÔC†Ò㌚MaÐŽŽôw(=‰$-§p©&N› VE%)müß_gko?ý¿+³œœœ”µ¼QºE$ªU±Jkò#%"Z›ä¢\JvGG«Ã”ªhm\T¢24<<"\<¶64:b“ÒC­Ž‹Ó¤(m–Ú*gÏœ9Ë|ÌöUÇlLNTú…Æ&*W(Eûÿ²BQÔ4ml˜&üCߥq+ÝV¹'¨<ß÷LRûy%¯ñöÙ¨t˜9köœ@­¤Ü¨U”;åLÍ ¦P«)j&5•šEYS~”5ZCÍ¡l(j5—²¥ÖRó(;ê]ʇr¥|©¥”„2¦L(SŠ£Ì¨ñ” !—’RË©}Ô÷[ÉÉyÉ£\G•éIõbôZõþ-u”n’öËLe¥´ }„¾C3ñL%ó#˰J6’Ý®Ïë§éßÕÿVÿ…Æ ÀàåèTCª…6¬z§L"È…ZK‡­ðhÁJ†i¾!Ëaö0D„ddçÐÊ'1å{ sJÐÔ˜wôðgÅ%••7!aˆg,*`tl™i'(!”æ\s'A®%û®ïfscwÇ©–Ëñdå_ ŒöîÆŒk@ÜÊ09×ü C,I¼™ä4(õ„RxÌg¸9ßbKÄâyñ ìÑ?æ‚ùãûÀ)°­u×Äù#Ö-äæëK‡[Jjŧª‹ëÑ5t<®Ø%Fýx†\™GP¡]8Ïc'k,ÁîØcSà AK`¹Í[¼H‘‹e|ßE‰ÖëÜ¿÷Aïàà¥Þ» ¾C¹0ôan™éI‚–!z¸E° Ê¡¬Ä…tTûú*/b*o‹ßÁó]+–œ]§àZ×ÕÞVß¶ìBªÎ´±Ø@0⿸à1Å6ÐÓÝ=°çů-½½ 㡨|È­L`§'̇J6 Á2¼Æ |ÛÂv¡a×0Y«;Æx(rä@7ØÁyñÌ¡±`G̰ÞAãFa ßo1`'äÒdk˜ y`&9G6ýMHáwun«nðûzV–{7b3< gàmÀái$rÞ`ô\òØšÎr\µÖ‘la=ŸÀ˜÷Å/`p¹IuBQœz(-?\d8‘Pò3蓸_ÿƒ•f¥¤ü-‡¬œ¡O¯lÑt#Æþưànò]ÕE×wIåuz™¥' ŒU¯dÁ@3BK€‡‡{`÷À@K¡…â-Lê‡1o%B9¤ð¹…èÊCíÙ·Õ©žÏ½¡³~Æt,ÅÞxÉ“‰àFÏ{ÞT+°œN Oø…¡M%©§¶•í:º÷ûq?ŸÿàLÃçD¼5éeš¢Ôƒ)û"Yã¡Ò$0~ê aÖ"™CZ~*š¯öñZæ9‘k°¤vÊ-NϽB¯ÐƒO;»;»>{ƒБ/—õ.ïq®™„Ø\lÆÃ˜o¦áÙØÖÙb#—b”ý0Rˆ1!L$}$j×ÈE}c¡Ž=Ô{¬õÆõîêd¤Ñ/V÷Üð¨ÂRâyo7<À“c– È®.Äóð\¿…X6¢ÈÐÄ»ñ0A„(âa% ?<°^pO' µ@ ¼”ìU2÷j£/‰ŒrÑAó«ÉãD0¾Ïk¡‹‡—`‚_‚“`&ÃG‹É·‚6Æ“Š‘7çjÿLïz†ëyÐÐðà“Ã9{Jå0‰ÉÎÝ…v£åÈ/4Æ•åjßäûÓk¢•Ç:­ ‡m0Þœ»/L'( îÙÍР>–ØÌ›`¼ð–ÂøÎºÒ—®Ì²€ îk£*2äx*Z{6¦)ª#ý!QÕü'_§ 8W>ˆ)¾÷²ë4Û¼\½ƒºž¿<ÿE·BWœHžŒøùµèg’èç—ÄÏ/ÉÚ¿ˆù:Žt5h¼†Q¦-ÄÂe`‰MDW„…Qü^ çôà©$,Vïz`=UAtiŠ¢4­$ër:¸¬ÇõŸÉ(ÌH¶ø(ÕD§»Ën’Xw‰§ÃtÅH*=#% $¤¤.ÎHCRV %Í0Úa²žðJØÏ£®-çTõíÎ'IBã¥ÖNx–][6ÅËÔ_Ój5hÕx_¿È™Sßoúf—œ”È|<óí»àK”¸ ô‘¤cVw`Ëc LW(Ë£ËQÛø+­gúž¶…-9  i'añàHínËÍ~øŽÇßéê·=>†à¶cß1ºÆ3Ú^tGW^um5M¨+Ô?3\óò ¡ž+cjnÉáWâ£!ƒGßöæ«Öãõr.m93Ò˜ï.`»x/ ð &‚¿OhˆE/&v“N13ÔC1üÈý#½ÃL(ÓÕ—jñx.y;œÅÌÙêà°¡ñ‘\È"='‹AN«[ƒÛûâ¡G¨ïX[K빊ëè+d1ÂC„WxåH²Éf• s@fz‚x·¬DïÊKø„ÊŸ ,´10jñUl­ßóÁ&Yr°¤ó®VéAlûq­J›µyK¶"-! ¿ÌqJíº5ÈETÅ%nª¾ÖbÙ®k½ÄriH³/³h3 Ói¼bx®Ù9,Èm}lU}cEÙõByK~mÞÁ‹7þ§ÑŸ“L1jñÈcεvüÏ4sÿöÙúŽ 'ãBäxX·ò—ÿ‚)ózùç˜ò Lô •Ç_ò?é…|PXJЖkíbþœ¸ñç8qƒ°#rCjYëÚÓº-c`þWÉ_«.+"Û¼O{#/˜ ög¡|‘ïiuŸfàîá¶¾{ð¹®o’©~€0°”À%xÊã*l U#t dç—p1dž¢ø÷вM!«6®I›‰°Â&%³?õoôê‰xˆúPgõ¹Žúö²ŸŒC0vó#õ•ÐnÚ$3dÒ³èdjIäßc-"¤/ÌvÉÔlI C‘HSš~vë©ì;è zxèþá“ÅM§ŽÖ“2"´f£úß’A'K¼=KØÊo%óÓ‡ŽLò|×lŒXGq¥hÒ‘- HœóÁ„Çmô‘ºŠ²WwaÌOgÛÑ/,X9<¶xÚ¼¹Øn'ÊÎ̓E%ýmíù›Ÿ×…¸-JˆÆ3±ž:E®ØÍY!åw î ¬'’ƒ7 Äúºò1ÐË·¢üœ£;Y®ÿ¶S×YøoðõVÕ}µ[Ž'йxRÿl ¥ìŸýò‡ªv—ÏÜÀí†ãÍ·,À`Îlåì©Y&×Dm D+Øí0–É­ÛWšw¤¨º¦´±WkÔ«Ü6ª(¶ÿaÅÖ§àK¬¨ F°$Ä*0"ßĘAÁAË-*È+8ÌrÏN/i:ozö÷±9gž'BnªåÜ S°Ú¾¶t[°ì‹ïÔíNç䨒yîÙçMküƒÃ}}‚?»ÒÞTש؋OóÜ`OSÐÏà žž!ͽ_ž;ߥ0NªjË`QY`% 0q4äÂÄÆFõ ¿× endstream endobj 102 0 obj 3083 endobj 103 0 obj <>stream xœuXXWמuÙ™•u@Aw,  ("‚.EAP@E”ª4é {"Wc‰(1*ˆŠh,€"Xb—Ѱà"²¬-5ß™Í]¿üw¿äÏÿ=ÿÖٙ¹wîyÏ{Þ÷\”N?J èzE'dF§Ç¯ˆÔü²æMüð~üaNúc®*G4‚ò=ð³>Ò"=ÒáFï ù¸Ápw d¢„Aööýs’SrRãcãÒÍ,ƒZoý÷™INNNfËsþsÅÌ-:->6Él 9ÈŒNHNIŒNJw6›CîNHˆ_a›“—f¥™½ÊÌ#>!>%%9ÓÌrŽ•™Ýĉ“&;ÿøÄåifA‘Iif¾fѱ ‘©ÿ8IQT€ï¬œ¤‹üfç&G…úÏI‰ p[3Ï=5Ö#-.Ð3=>È+cåïÌUÁ>Y ‘!s³—/´´2³ž`nc;q’Ý7ökfNÞ>ÅqÉT§ˆiÎc)jeN…Q”åDÙPT85r§¦Q¶Ô(j>åA9S©ÑT åIM¢ÆPA”5–Z@ySö”%LùP”BÍ¥&S㨅”/5…O-¢ü¨Ù”#eM…RþÔj*ÕŸÒ¥”>e@ ©Ô j0eH‰©xjAqÔHʈʢ¦SÆÔPÊ•F™P3)Sj8GÍ#)¥t( yäwT‡`¬ R°·ßØ~¾ývõ{'” }„:tVêìÐy)²}!º"ú7=†Ž¥UÌæ+æëÈ~Ïöô×鿳uÿßumt}u—ënÖ½¢«0e@ü€/ô†ê9ë¥è•é ôéïÒ¿£ÿ§A?ƒ1S V¬1(4x9Ð`àê9Bƒê3øWÃɆA†7 ߉ ÄÛÅê!CÆ Y<$Nð H®bŠJ!ÌR%r6ŸùP9VÓ*[Ta2~b±€O‚dî N} ñU¢ëѸèS¢èWº ’EðŒÃy4èA—È€ÿ 'ÉUCȘ¡| ‡G«­± o-²£Á]]ºjêÚ˜ìü84ì ”¿=g;“9°áÀ—‡Q*ßsø»c‡öþîä« †j–UÂÏo+6lis}_w‹Ï· ŧÔ%Ïà#ný×–ç/¬ð•à?;y3¦ÛçÆ—ÐÄÙK$âó †,#½·nœìò«à÷åÕu§²Êck}Ëg#´Å:x6vé5sÜþ†Jé|Çe‘nˆµ]ðñ5ÙÏ-—–Ï.”jP¹‘ˆLy] DXHãy¸ @•‹hØ‚ïU늀/†›ŽoŠ€!>îùDÁïVù8#ˆäA¤…*s Žû4'¨Š0÷)QÁ +Ô Ò@›Ì(’À•šÄ24œV“sUJÆ@ÓwûO ^¢ªLÈ2ìÕJZ=˜WŠÆh2¦ y}µBô‘$:&]ο– j”BÕ8qIà`+ÃÈ MOðžk°.Âz£šÝnÌ”òzûê0³ãé¡ëãó’üâ]Ht£mP#o]ÏI>!-KÝŸ°7˜ÕÂÓ‘¢¸¦)Š«ÝÆâS×p÷ ŽÑ—‚kÓš ¦= N+¸M†~Xê¸Ê/ŠÔD»+#~¹¾Ü/·§áÁx€ÿ´‰öó;` ü¡C¡E¾ ¼šÀ¡Mpþ¹/„µêØ\“Wÿܹފ,kÌR23ñÌŸGÂXÐëlº„”LºÇâX/‚–I¾”ubÓ‰‚zv{·ûõ­{Ï+¿ç9y+ÚZ°•LHÎß• xQ}؉·¡Õ>ê»"'¾^®öax/þ6!þ¡>+•ü~’ aªGcslƒ³q6oˆQ´—ÕÝ’¶7VƒÁ¾ÀR˜€WJœtÀî7¬‡}±›9ù²Á¶D޼Àëù²‘jR©}~’®öÁi|AÈ‚L¬ì'ŧiå'SŽ¿ 3õÓ8ì}gXáQÒÿ…ûgN¶ñ ìâ˪ÜYízÔ¼¦X2 ‹ˆª¸>7Ikݱ›UR|’ÁcTÎ\a¤!Öû‹Žúwä¿H à$Ñ8Ï&0m(:…°—T—ƒœ>S|°r÷רङÉÞ±n[.b]—D̔ڹ{¶¨wò‹»™>i>/‡=rÁÏJèy-$ÁÜå ‡¾‰.¨:séÜ¡jÔȈimx”_û”¨¤ySØ!LOC¸³sH¸´Og¾–C¾\ÐKÄ.tqüH¹z$äóåêíÄŠåêÙ´<'Ëœ­ Ñ7Ö‹+š5:\®Ñᣠzr¨ö\9+n>vèè®¶±]LþöMÛÖ¡`´|U¸ +®xI´¸'µÀ`)ƒé¾ãœ.DÁ D©,û‡üÒL=,,låì¥ÑE‡3%k÷oÞ¿¥‚µ§w`ý¦ù0‚TåÐgÞÉ".Z•:òü.é0ªVsñdSãÙ¤Û%ŸÍîÌ(´t¯¶*ù”ØI|NãreeòŠ%Gr SQ Ûçv ßë£g-MŸ&}BW4¦a¨TîöÚXüüèÿ&º³bÅ$©˜/ðAK¿‰(J¢R:îȦc[_€^öÙ駉êËϺÚi†Ó›±…÷hùb¦Cœ!ƒ˜Ç‰¥YG3‹V¢vrD”¥D[§¾%P%‡2¹á%%¿VNTZÊs¾é©Y4‘^£ñð°Y^eÕË$áõIݨ—Ïß@l@Ø8¡Xò’^ –X§ ;#;4'=p±£{8!lÌât'Bè¯íí?U/'ºEHx2½•×$è"…²›g2ŒšŠG¹yþ¨6£CÎ&>(.޾󸤅Y÷ÕÚ‚5ˆ]_X!üœ1€T‚mV1ø ÉØqdìTuI'­JÔq4¦érˆè¼“Ö(¼•ðFá©ÔT·’ßÇ€…U'žN²í`鄇ÚU½J•‚w¸HÜóKÚ…˜@•¹"!#)kɺyh: >w!åû/Îl»L¿Í«(¢|Eç³X¢nôcqõÙÚª“Ð=Ôôв Ï­*n³?²êø “Çjßû Äz›¤/„–5łμ.ÑÀÂLÜ¥T%Úà&õh"ÝÙ èÒ”Tzk¨&.ðƒ(R6ŸiÄ—LÅuŒø²Y»ƒwä¹{ž™ª¶dýÖV[Ö\+gÏÖ‚“«†ki!TÓxúkfanÚ–+qc ·î,(Bì‹ÚÊ¥*}"u¦šwÒÚ¢QÑr|KJæ_*S6ÊÕÍN°é“©’ÖŠ™Æ,ß¾B¡ï²RMiÑ@¥,áKDhl©vÁf¼ 9_u‰è?·h–óIÐHÙk¸¿Œ~Úî¸Bôž&ÖüŠX¢·ÈŒÆ©8U¤µ[•¤XÐ¥Ô¢T«1•=Œ•[îQt4N{<·]gù=6ä<¶üÝfÁ è÷ÆHø=ÚîP¦2/x «I0Ï5ã 4óÊÜÔœäøÜĺGUwI«­ç2XW6ƒ¸½ %ü¥¾jÓÄÄ_5mLjëQ¤ÇMU[›‘[¬Ih/èw¼íïj[‘–„pšž Áš<ÌñS"¡Ib‹{+?±ÍðB7i“ø}-q•­¾D[L“Öì?&…;ŒÒ³s3|3¢â$i)ë·.dŸÑ»ïW–ËûäBr¨4ƒAq™yÞ±n^Η«Öú§&,Fž¬8ÛúaÀïJ¯Ý’ì 9–v @EÛËv#O%oÌMMOX¾& ±>Ñ'®Wï)’*öÜq¼ˆý»án»H»oIã¥êOx%ÿI4–†ý¤ñÿë–vßLø†ƒä¯PÓ÷£qïuP§Ý&ü©‰xá­’ù› —án°KÃCˆ !PdMÃ\ˆýp±è% Ã` ã%¢×}Môç­À3HѼŒÅµZÚ’3â§?]¿Ót÷L´—ÒœÐü¼s6Ê[ó“Á¼®·rÌœ&I¸yĹ£%«—ú²âÚÇÌ?ûÍkPO:ª¼kZñ}ÒȈkýÏ5$µš‚¤—´ ³ÁuòG,™³(e^”ʃK8e_3å÷Ïfê·>pTyFàGð³ ±‰ºOã{~x 9A-h PþFƒ#ÿ ¦«_ž&!°óŸ±`Ì. Ø¢–ˆiI±}v±Çša'É ­ì)tvà,nç0ÈcmÖâ­l4Óræòs)èÁx›  pxÃØ@jTîÅ‚ze£RX¯ å°)Ã&¢*º LD dþï:|tByŠÅ\d•¨‘Eç@ñúOëE×iX¯Z¯ÝH´ò2™àb/Tô á8y€7 HŠ [â—n­5ˆX Ö0NNü<$¿,¾/‰©›wÜ ±.:/«ÇB-Y2ÁÊfñê—/¥Z‡U‘,ìSa¿SoW¨Bç3 X•‡‡ç²¾2 nþB›€wêò_wsêþôž¾¯è¾úöÚÐ_¯]m%B~+ízdedåƒ^hòZ½.bëVA]³«|_iéÅ+ÇÛq;pFÐÊpßX©íBlé¸Ìk#ž8ŒOдR5——*·±¸Fò«¹Œ¸!œ.¸¹ÿøél%°eåb4È'£áÌ_Ñ’JŸ@^›aµŠz=zÉþVƒŒÛKWl‰?¾®‹˜b· Ävüâš÷›¥¤ ,Œ9°ê¬gKÜkÒŠŒyÑ &½quSÊIWððØ©ºMÀ`Z+6Å3\ðø¯$ zÛÅGö.®ºtä:beÕKf†æ$F­‘¦­OøÒ¯€lCžBIÓ™FÞ äÃy[n[H^F ÊGkvfíÃý›‡æfïÊA>(zÑ– l½½t{áŽÂÂÊ¢BTº±póß¡ÇÖœX±÷Ï×µI¿Ó‚_Ví¼/ÉQÉQ¸rØU®ÛÍùe> ~¢Ù0PßÎáTRážÈà„ÍÛÀ Åç>tÝó6ÃÓ]0º—ßMŠø™j¿•ËðN½yb†Mð@<–Šë-Z¦+.®»"?[´§rõ=ÓÇèNùÕÛ×ïŸ|Šd,Dà`pÁùx6 s¶—XÙ`-ëRa)ö€];ö—=’$7:¥n&3ÇaG²! ‚AA,9šŒÍ°3î?‹¦¤¤Ù³ãà7¥Ò^8%Âù˸è|äWÁ[CSScXqýò䘴ÓõA§í·g}[ è–m·€w&Óœüš~VܾÓÝWÙü‚Ö RI¼˜Æ.x³¶ Áùì º ²z`!8@<û÷ÒÁ°SXJ¤üÿ˜…ýç¼ÿT9êóÒ¿ i2Ê^Ÿ^( Æâ÷¼)ߟ¿n½täl£ dıÛ»`¡÷‘ØGK%â÷Ó¢VÌs1ÁFo&˜½é®}Å-§* fa/ø¤&ÂÃ;,bÖ¬°KMµ—KqŽø½âvÐTÇ€ I“nvvÞ¾¡øœù@ÜmTöj€k€)ÜÌþäÙßüXyþÞåæânÙùæüy/ê¦ßIÍ? LÇÅR<´×†4Ö¹~EZ€gƒüвªü&†ð+9ù]O[¿ÉSBn½ì½ýà©æqØ­NÀ?Á†¤ý å_k;¸ÐüAýˆN¯ìÓi¾Dç³\k}‚Ÿª¹üQ«HN5ÌqóÿçA¨y!¬ùÛ!Îýåé¥|‰¦åó-¥eºd;õôºöèéSÔÿxl’? endstream endobj 104 0 obj 5109 endobj 23 0 obj <> endobj 25 0 obj <> endobj 32 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 24 0 obj <> endobj 31 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 105 0000000000 65535 f 0000051801 00000 n 0000077177 00000 n 0000051627 00000 n 0000049037 00000 n 0000000015 00000 n 0000003194 00000 n 0000051849 00000 n 0000076322 00000 n 0000074135 00000 n 0000076642 00000 n 0000074551 00000 n 0000051890 00000 n 0000051920 00000 n 0000049197 00000 n 0000003214 00000 n 0000005049 00000 n 0000051961 00000 n 0000051991 00000 n 0000049359 00000 n 0000005070 00000 n 0000006591 00000 n 0000075099 00000 n 0000072959 00000 n 0000075338 00000 n 0000073119 00000 n 0000052023 00000 n 0000052053 00000 n 0000049521 00000 n 0000006612 00000 n 0000010904 00000 n 0000075923 00000 n 0000073680 00000 n 0000052116 00000 n 0000052146 00000 n 0000049683 00000 n 0000010925 00000 n 0000013751 00000 n 0000052200 00000 n 0000052230 00000 n 0000049845 00000 n 0000013772 00000 n 0000016555 00000 n 0000052273 00000 n 0000052303 00000 n 0000050007 00000 n 0000016576 00000 n 0000019783 00000 n 0000052357 00000 n 0000052387 00000 n 0000050169 00000 n 0000019804 00000 n 0000022750 00000 n 0000052441 00000 n 0000052471 00000 n 0000050331 00000 n 0000022771 00000 n 0000026403 00000 n 0000052525 00000 n 0000052555 00000 n 0000050493 00000 n 0000026424 00000 n 0000029664 00000 n 0000052598 00000 n 0000052628 00000 n 0000050655 00000 n 0000029685 00000 n 0000032402 00000 n 0000052682 00000 n 0000052712 00000 n 0000050817 00000 n 0000032423 00000 n 0000035137 00000 n 0000052766 00000 n 0000052796 00000 n 0000050979 00000 n 0000035158 00000 n 0000039132 00000 n 0000052850 00000 n 0000052880 00000 n 0000051141 00000 n 0000039153 00000 n 0000043998 00000 n 0000052934 00000 n 0000052964 00000 n 0000051303 00000 n 0000044019 00000 n 0000047427 00000 n 0000053007 00000 n 0000053037 00000 n 0000051465 00000 n 0000047448 00000 n 0000049016 00000 n 0000053100 00000 n 0000053130 00000 n 0000053173 00000 n 0000053545 00000 n 0000053565 00000 n 0000060387 00000 n 0000060408 00000 n 0000064525 00000 n 0000064547 00000 n 0000067718 00000 n 0000067740 00000 n 0000072937 00000 n trailer << /Size 105 /Root 1 0 R /Info 2 0 R /ID [( ÇoOc<GåI‚§T_)( ÇoOc<GåI‚§T_)] >> startxref 77379 %%EOF simh-3.8.1/DOCS/card_readers_18b.pdf0000644000175000017500000010374010571704373015170 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœí\_sܶ¿Oqo½ëX ñ‡ ˜t:;iãL§Mlõ)éƒd$7ÖŸXR2þýÄ‚» ~ O:§Í´±„#€ÅûÛv±äëºr]ûÿ±ðæjõÉ«v}q·ê¯_ýy(¼¿Xý¸²•òÿú¼üæjýüØuìÖ¢©:³>>_ÕU×Ùºë«ÅÚvîg׬ۺ©¤^_­¾Û¼Ø‰JØÆnN|I7é6ï·G²jecÔæl[WVˆÎÚÍz{T»§­²fójè¦ôfGý [¹¦®1ë·ƒ„ïèéÚw“¦nÚÍùöÈñÞIÛmnz ²«[Þ3ñ1ñåöH9jªÓqFFšÍ¿Bc«jÇÓmiô=¶û¶3›S›7~á[´¢Óšsw劵5Z×js»=2®u-íæZÜoEåxSm\ ?œ›”¨+Ù¸xkµqÃýãøk'D!Ö Eš®®Œ— ëpüÏ•P•]ÿeuüûï6ßlt%­Ô­cÁ­fíÞ\SñŸQ×´žÉË­®:í{Îbƒ3*®ýÊ7ªQÆuëªÚÈVz1Çú÷[Y)ÇŒâîÂF‹–øyëæÛ?Ü!;8êõ”ÁÚ1ðÓÈ ¤ÄºŸdDƒú•—£©Û¶‰ <Õ·^J¶vü]Óûq¨{ê†j-çé¤gÔjÉ©¯·m¥­lþp7¶<›.ˆBó!¥sâ㆚Ž,±i~áh¤Pµ×+UËÊ©ºCÒñY¯ûc=ëÅ¥÷aœ=ÊVb™¥E«F¶Ù%{ܪڦŒÉÖzÞ"óGì~·SéûE݈q+%Ü@M#rœø–{ ø=óÊ-*èa$Ÿ«T¿:—ô˜XÁ*u;Ö3œÞ"Äâ•zs42}a~ˇû?CrÒ­ò%Ò0)߀xàzBÙ¸g°ß»­pƒtbºÀ â»q†nðÐ÷|Ùv•:BžÍèÞ­|É-[Öðr¾3+ïÆîé´¢@Èp\/­'@ãÊ•´ªãüõ£¶¥¡D˄Ӫª®e¯Yq˜fõÔ[›npÝÄ‚;%S¶5\ßN°n°nÜFŒ8^Ãb0ÒOf0n{ŒDÿ”õ:C¿/î·_¸%@0N9±ããž2dÒº‡†š !Ä1>æ=´Í\p'ãA¯Ü9ÇtQ¯Î°:2j?ŽÜ2«Aª…÷”5Øs©ÏÅHòÎ¥‚Ó¢µø+m3l­ UÌtÜÔ‚€AèíTß­íÏ­©Æ…ÕcÇ”‹í ¤‡wìÔ¶¦¶§¤lg`Å¥³õ”¸êf×÷'¢'ÜfDÕ\<`RÓËé¾è×ýv‚†Šìcï’¤5GÐPv¬?…XL7"pî»E`ÇÌgpƒ"†HÉßmk¤£,£¶]C`>ƒp?x“§¶É¹A7svŒ{Ò.“`X‹¨„‹‡…ÂÉ"³éŠîA#…)ÜÒ²sÒ,V„§Göði^â:e–tê*^lµM¶ª†Šßoc=ôöNJ¨,¼£=vNóÓaR¶âYîÏïVÛj¥œüý„^Ø ‘u°Ï5 éŦUSWVs`)DÅ–ýõš³³û@Ï€›–®œ×ó(i/̇°-Fú÷ù®g{8ù5üòxõíJ¬ß®ZÝV]gÖº“j­|±îÆ¿ïw«ó}Z4‚Z´]ø‘´Ö3\µ Õ¦ëòÞRÆjØÛ/w¨M+Ò:eÝãr×…êqê¶¶cpˆóÙÄŠÕóbu¾X½/v»¦R>°Û§&Cl~Oëè¡->¢uÔ‰š· •m7Ç}¸Úõ5Ulš(— ]=Ô÷û¤ÚVæÈf‚Ó ˜õ-f0[$0ë«g0[ì0ë«K˜-sçf,ÇM3e±:°V¬Ã«¥[ùúñ lHèV{?[ifzkj˰üœÈzÔ4ª«dQãLñj|‹Ô ÔøêÔ{Ôøêjʜǹ5¦›3eÅêÀZ±: _¬>5Ê9šî`Ô|¹7jœµ]@o1ƒš"€_=ƒšbï€_]BM™ó87ÝÔs¨)VÖŠÕaøbõᨑÒGjœôZÓµ•TËò—O±Z›ùû3ò/ò÷Õ3ò/öò÷Õ%ù—9sSJÌÉ¿XX+V‡á‹Õ‡Ë¿¶•<äüÓðƒ†¶®¹ õø(Ù.ÀÇ·˜O‘@€¯žO±w€¯.Á§Ìyœ›tBšO±:°V¬Ã«†î]¹§ù(¢Fº£nÓâˆ)¼u(Ä+ñÍ"LÀÑ[ò=™û~Ÿ9õn¨£1Ž|:õR§‰ cñ‡ñ Ð-™“ƒÑí[Ì »H  ÛWÏ »Ø; ÛW—Ð]æ<έîÔº‹Õµbu¾X}8ºÛ®2:C÷ß Ò/fÑ튟s6«‚+…p€«„ëƒ×H? AÕü&'}0„R ißPä$:i ¨ i4>¶UßB,hÒbŒ(W¯Úv êå[̨W‘@P/_=£^ÅÞA½|uI½ÊœsS]Ë5€¹¢ÔÂ,Ò(¶Ö§oQ^Ÿ2Cu‰~¿|}uyùÊćêñ~uûêÂê–)Õ%Ê ÕO¶=1åP›˜5:›s˜E^Ùæ•¦7]N[N®Ž³l—é5Íz=§ ×k*²Æ_Áˆñp£i·I \¼á°]‹X"•\*Çè;Kcö‘0;q‹ U"!é·$ÓnÞ†Èt£J9]÷èJ›w~×_¤u¶t‘–æ7 å‹ÝòÕp–’šÌþ -ó¢Ÿ]PÊFŒ“b—Y<¯ߣ²Ë Ò›xYÞ¼Ûå’ÁÀ;EÍw>ß Ùº¤ˆÉUw¼ùpdµxS͈òD™taJW(ø¯<‰»E]¾q¦[Ö8f¯³¼Ý˃âB,Å쇠¸VÜ]ÃsY‚#d@x Œœågç‘as̵;Hõ?ÌGÙ9„µ>¨ÝÕlã8rªÓi¦:ù~ÂÖf¨€èZ ñ ×Ó\÷i‚Ï$á÷`€ÜÃs¢y3Ót’Õ•­:üÜŒo¯‘ÛZ)^ £ô¢ß\ÊMì·ëß•“ØKé `.lß-\†c/†v¼=` ´u_óù‚ÌœÆ?î4Ã|—òÞfè``ì:ËÓ*§ç½…ËóÌb@y ógN¬™ØYì!Ki‚W2¡ øÌ{€GóŽwrÂi¸ÒÙ4å÷‰–ÒyXf.÷^@f+±òaÿ¤ø¸ñµÝÒ»ÐRâN1/í³/#dŽFwŠ˜>ùnI# )çªjä˜;Îo,<ŒæiŽiæ÷þïPÞ9¡=°ã¨P±sg2 æAÍ8,á̓±Àã½ÏŠ)xÃ2•ƒzF%vlÅêù©¬MÜŸ¹£Êô|»Ç½Q7ŒÆ(ío…W ;2É¥¡(2OZØ¥Ú#‹wº³¥!|†ŸÄ‡õø `‡Îq¨–NUÀÝa—0i¦n;Ã×k›v`V ðÚÉt`kƒŒÅÏ´§Ðð¨Þ2Koµ -}z$ ¶ ¼ÅIêÆÂðÜFF€é3Y=p& TqX߉佟—¤hyö4+’NxlÇ{“Dn`ž7pj–•éiYdl+Í2Có¨çÑ-„; 8°w¯]ECJF×0¼ÞíX /¡÷Å×T|>e¦‹Oý|tÝT­öiIñiMECEÜ@¤tm­}^ £{´Rl~ â[*ÞRq œÃo¨xCÅwT| â¯!±3*žPñ>eÝÞSqÛ2ºbQ2[–ßó¼øQä'¨(—ä÷˜9þÊÄóÒ½‡œ±nŸSñÿ@EAEIÅOaƒ–Š ÅÎÄ›//x¶Ò)žÀ©-Š‚­?æû% ÿ5ažÃYÛ *~6ÕµVšèàšbQ°Yþ qú[Ä4kpºÔKû$Æ„yç¶›®´³jÆî½+}þ1­k —¬Ú\g¬L&÷`•¤? ø.ÆkºÈc²Å=À߬/ .½õ<Ô4H\ˆX‡È°öŸ‡±*Æ6D“¾N€âÍìžh>àŒCOKeAÊž D‰äË?Ÿõœ4Y88¬[_Ï*}ø'*% £ïpaEÝ?¥ ÄàÓŽ¾É•Äà);½øg©×“pïÌ`—ôL9lBÙá€(N‚)KKŸô¹…G÷uÔçâñéÅð-#«–¾@òµ.\¦`˜?îd!ó¶-|eùÆkÿ,1ºt™îba½ðMËÒ ¨YîQL“ÙĤÿ‰h1{Uô·hqæÏþ¿E‹—â*¿E‹Ï/-6TÜ7DLbï“¿]ý%*endstream endobj 6 0 obj 3814 endobj 19 0 obj <> stream xœí\[sÛ¶~ׯàÛ‘:Ë ‚i§3¹¸gÒi§­«·¤²,Ù9qdÇ—´ù÷H» >ŠR,µî‰›™ÎÂu±ßîbw™Q§Y”˜?–X¼}}\Fg7£º9:þoK\Ÿ>ŒTœ›ÿêN/ÞGÏgz`ZDzžÙj”ÄU¥’ªþ1T¥ÿZQ™T±TÑìýèõ8šL3‘q)v"ç“i§¢ÌÒñšÈS"#"D^¹$’Mv=4Ã%œìŽÈ÷pgÜ$[ø¶²a+¸0ë{fÉLû}öÃ(ÍãBF³G³¯^_4¿*‘‰üÈg-™vUÓÊX^RkB¤$wþ¼*qQñyoàà›Z@†1.½ûė=(.X0ذ78ÅžãŠÈsØ—É ¨%ÜÏÛ`ï•”$i[ÊÀó}Ê€É/INà­‡&þÔE±Ö¦i«MSgõeêŸþc®–ÝlË,­ÒB߬#õqR¦•§DêK6ó¤JÖ7y‘Ëv†¬ÈJ£âlßõDĕФaµíz=Éâ\VIÎ{^LÒ¸(•G-éç뉶æ8G³Ñ¯£RK £BäÚ¨¼þ‹4ÿ×¶âz9ZmccRhcR%âDE²zÆÄèCÎþ7šVúÌ2š6L;m[9 gšñÚü)YjtÚS³X^UeÈíšm}ÜÖž—™âÌÖâ¥w]H‘ŠÛvL4)c¡òRûa§l'JÊÌëé~g«ŸÓ¤/̬YQŒi„)OÔøeÕ<¶5ÓB<­Ðë—´( ¿£ÆÆ“%5Ÿ¢aíe¡ìÍïËš}Jðž|Ö9uÕ:UÅ2)ËÂåf}k¸ª=h]ƒ0SR«)¥%LËÎkoV¶—k{Vv¬îJ \ºõ×ü¨®§ÏÛ—MÅúÎ9;«8‘™?Wß‹-4ù©ž*Ñ"|ÕôÌéŠ M²RÅI’NM-«Ô^õo%‡ä ª•R=× ý~G$HO SÝZˆ¢áf ‚?ݱ™¸žv%× ÷pÇ„ÜBáá !ëÒõœ”3'K'PîH.WPÖšù¥Ê<`ݺ¥æpÖXI¬{[`ÑNÙ¦øtRLjOHæ÷/9wìé÷žÀƒêåõúU•óå{4€wv…Ó ü5ÒßòW¤"€RÃ¥æ¬lßFê>8Qd˜!LÐjc"†Dþ–ÀõÇD+‚®©Šb[šCôq®±æŠv3!×:]Y+)»sæÞTê4EáÝU o›$ðˆP·`¥0èIs%+*ÍJd÷Ø9¡…8eK.$h‹HR«ÝÊâ¨ÇÒ¸1}¦Æ²Ñ*\U°ñ€ÐGgîžÑŒ–Ïc Øí]l+°‰€üî7\ ؘÞð½€;§¾vàwuÜŽã.džÆq×w—Áq?ztÜÀT=zÒ ØÓ¼DêàÌ5ž#W|†3£Þã4Î…Þ£tJé/è2`×å£ëŠÔ mƒýÊv„vO%ƒÃoá°»/ ï˜87#%Å×:”‰t^8r>æ1}h=”›áV3— CJUòà d$”ù*óÍ .É pzØB@èÖS4ŠamÅ'°KÕ×­ZðI™Êg«ÆDFüI¯ªy™W\ Ìf¯Üàâö˜Ô[.üàyçÛQyU¦|²U¸"… p­çCj{…Ãz1ö5›õà+"žÈ¸È¤‹YÕ¸hí˜9"C“‚%D„çÕo¼RG×ÙßéiMNÚµ¶Q1·=þbh¸ØAð¤Oˆ¤I‡#®ÛÇ¥À˜áëzÛß—–rò— Œ"­R‹†!¨†‘•·¨šÚ©:¯óZyErÆ…;QL)rÃ|œwŠn=\¡zy3vÚñz^¾¤ÇQð"0]+š÷Íĵ> ÝÿÁË 4c÷‚cÉH–¢dyÉï”›NwÈK¾ƒù<–Ó`‡ìð vIYI¯¶`ðþ^îóþ²‡uƒùa|SìÚ×à",Ÿ·bïñ>Ù+{wM\±G9—zŠµîµ†&%2ºˆÏ,søÌ:•ûë\Á†]Âaψ|Aä·ti)µfD>%’u(‰ün£z{,QÛòVw/QûûQUU÷­üy¸K-S?U`:(˜¶°UÌi8Ú'ª¾¼zÏá‰<”#<Èô½bâ0.ï=¥«:I/Wb7=É *ÝuO±k ò ÌV¹ô¥¹mâàE•/ºfvCQ!­²–^J¦+Xm0ʰ…hS82d.€±ª4œÁìQðÐZà\ ÌÛCܾm (D,eÙ©dée3è[½iU¥™ô+‡ùû}“Ðvó¡gˆ-AÁÓ6µ‚}JÁ²ÒK6MóXhxÏÌöR)ó<çS‰-„VA9·›ßÔ‹]˜¶¬g0-IÌX9št²Ë~Ä9Vø‘Õ¾±X&‡€ù0|ƒša¿dí](íA~'‰Éf½ë^%RFò˜³êß{TofD> Ï®Rm:S½™eJßÅ?U½éqû­Þ„FÉb©ÜJÐ’…Êp'ì¯ösc²Ö]¹ŽØÑõ4y“× QyÚì•F xÞØ¶{¢vJ[ØúC%|çd>೬cKZ>=>À¶€qz¸iðû=\ø,wˆ‰|§á'¹vÞ{ãÕ~u•Âgü~«§hfsQ[ë…øá¡õÀ šôOT†ë81öXkëÙº4^îmëš1¸mø¦±áYOÅxDTH¶Û#Ðòî³%6,$ +ìT·‘à@Ú1lu6ý­ëæNáeðcÏg†±ž_7Œl¾ÀÐ.uß÷Wí¥âϯìÇYì5†>eÂ6âÚ ïQ°4ø£û«×%e=úá~…Æøâg¯‚±‹˜DoKz¥^ñÔü±êm«ª·Áã ®zËþUoƒ÷÷Xõ¶k f²ùüXþ¶)oû7 ü^K äÿÛE°¨„ÅX„“ì°‚§@©}{•uè×Ñ_"Õendstream endobj 20 0 obj 2745 endobj 24 0 obj <> stream xœå\Ûrä¶}×WÌ[F)‹&âB'åªÝÍV²©Ü,ëm‡Y.›]]¬•ì$Ÿ‘‡üJ~/I ä9#iËNŮڂ¢ çt7º{øýª®„\ÕáÿØ8½:øòØ®.>t—WÇ¿w߸J…ÿº ¼}zµzyâ ½òrNÎêªm]ÝvbåZÿg«W¶n+ãV'WoׯD%רõ15_N›R¯WtÕ7eSëÊ6kKWkjj²5e.×ÕM¥[.÷®¿ÁJ±>£æ†š[j®¨yJÍj~¤æ5¯¨y …máƒïáU6ì=”{gƆ½ æ+jþšš†š_QSPÓRóëØ”þ=ùýP•6«“?œüò-_ç}šøb‹ÁVoåÝ’„Ÿl+ÏáƒÙ½`¹úufôú2íÅsÒ‹ÝÐ,ÑëܼS§pÁØ*}·†{ùHÞb`°aßBø·Ô¼„÷2¼`@Áù¼ŸÌ½5q «Xvõ7ω¦W›LÀÀ³Rtq×0oàFœÁaû“±¼þŒŽÏjâšióÿ‰ƒï A®—ÿc¼qÞ‰ƒ#šJv Õwý"P+i×?Ê»LÎXOí¦j+Úðºª}«·\•Vºµue¤h…ö M©¥ »ï•Ô|yèwGYé’eüÛˆJ[ç»/Ò£®iÌ 5ïe¥L[+>•-Ÿ•­§¬ ‹/’øûÔ=<½wdåÚºn‚µSþ:õƒŒva©Cme€L¼ÊnxÓ=Jj½þó¡©´4þIqMµæwJµJƒ˜Ðnú®6èMW™ÚZí_?,”käúWðñ6õ×$ÞtMãüîP¿¦þl&I”Xt¿hÇ|¤s•mƒ/}²í¼Ün÷•_ŸcjzÛ ¥jÐ<ŒÇë÷1½Ÿç¬÷ªµi|ûÎåÇC¿€¦©Þå`ãϨ E1×Ýb(3XÜSó2õR•·J몺–aMŽâ¢õb C´èš%ZtËåé¶®^8=е£f äp“l¬çtë7R ÁyÈ(ñ÷ÔO4#òlȵEäaŒÜ AÜh•Ã3OÙ–ÀQlÒ_Ð0&á/ý[צñÆ#ð¬!öý¯†×²ƒÂQ6Ó7̼½&}ÃÆoÆÖ7¬k¦=yг¶Ì&±}Ãæ%ft^’ѨңV¬•¤ž„Æ(¥âêC–‘(€9@ÆÏÐãKD.•p=Å+±ng\oáìoùUÀ¯-êŸÚ¹@†›d¼Ø¦æ ‰ïËrOf’…™v+C0´äÞqýø´x_K½T?U¯éýü?‘~‚ÿ œ«µsÀ­ Èìþ+ºÊ80µaºŒäŒ?&¾-™6ö¶ì† ^ÆJ$€k¸èîÜ'•øû¡kÆÉÆo6Ř¿:Ç–ÓÌZƉd¼L*Ÿì f{fâgLý·½álM%¤‰XAÅÎ>MɾKýKÇ/ÔKm[ÅE íœQÜÌ1gt“H¾-z¨Ã{2’3Ô~LtÍ@-|+óí€c3²RÐÍd¨çè±ì^&+³¾÷dQ-é©ÄZRñl…4=ïíb>ô§8ñ‚'B'Â!Pâyc‰G/ȃþ–,,3Ìoø±µw&ëÊêäLG§’¦Mª™|§›w3¨Laã8Cæà€8TͧÓCgnªtɨýék¬y-]­ÓæºÈú›Ôß@QÙà¬`àƒ¿7‡/JZiŠ~h1¶\Qöì£?û`ƒÇ«T-…£Ðk Â"l­ç®jYImH_Ѝ=ÒígBºNý8è´„tt&ßÌxÂÓs`ô\ â·ü©Ì¤F,3*z!­I·À-ð~‹ÔJw™X¦Žºç¼†0/æ5X‚C¤I(bý8 :‰p)ÜvO1Ýc|ÏÉßÊ臉;ÑÖc©Í|“b ÊûÊ9Y8ØCÑy;™ÂØ'Zv¯bwï4¨²¨O™²FO Ùb¿S‚bWG—pPFóÔ4 ™A¶ Æ–»ùûMÔ9£æ3 ¥çÏëË Ýc"½Ëkr¨2¢Çg1rÑ÷g:-Š/-@¢çðæŒž0ñ1» ϙƓ=º˜¸† Ù+4µhÑ¢ RÕYp8úÿlyØ 1'MÏŸO÷?:J<2NÒˆ\@8—¹¶?—0Õ3%Ô)%—d‘_Ϫ€¶Gòi'‹3ýÿï3D$,ˆi1U:£cuÁÊEù› —Tv |–¢$®æc<¯,'îrH™òT¿ñÀ!«Z·c§½3 Š3ЮU2KS8á†/xÁÇø´(°«* žè¢°½=ÀƒÅÃMc2õ‹ÃÈÄ”-Ã5ù;݃`ˆs1Ѳ7m‹IÉ-t?6+åœ$Ý: çŒü²9 ú™S›`×ÜC9¿¼˜°VüÔiŒ £_Ÿ|s`› dÍJ Ñ®””Ý&ü[·«»³ƒó] k,¬ÒUÖK¶¦òGé“«ádp¤\åTÃîýõýJXº]ô+–’Á‚oíV0s‡ã˜—%'É-]¼DZ˜¡œÂÿ„œÉ6äGNñ™b‚7>.ûÆûø1×PØÎ©1rÉùªµbŸ3Mq†ý‰Û/Ÿ²àЩl|üˆN×à„VL<`R_2¯’2g¯ºGâûwezû ¢‹bAPÅžÁ&”‰•i†spº^bÜ£rá™*C¹ðSfáÎÝà £J0úÊðNu(I°S-Ä@1àSLªgº£ìpµ1f¨ž±!/ÒN§l˜†H%VÜ@Ö|€é<|Ø m0=Ì½Êø~û¤c•Ï’§Y*%IÍ~&šÇÞQ¿vŒe<8Eä`š—iþÌg I@'x‚ z2ÙùbвÀš¯ÝóÚ2ql͆: «õ\ »¨´Y±N–‚Æ¥¢B@~Ã%Ä3iÜ‚¡+“2¼WãtÕȼºE±ŸÓ=Œ ÎzO,0˜Ø¹@«Dõ¬l›þå›Ú¶¡Ê˜KÅ%©ÈDÑ£Þ7l&ÛÍtbο~%—Žú¬`…ó“•´PÉËÒQvÜ .±[‰êL_ÁBxZái%W!le ÷Éq,Ïë,b:SÒ¨1é[è%Ýr‚´Ý<ÿy‡£[Œøq†/~ž³à=ó.f Þɉ‡–€=âî\7ò¦ò©”Ä(dIè<ÀLU¡˜âÒ†¡„3®­yXÀÉH—tI«]2Vœ  …1;'›äÁ’ÈÇÅxiÐàçósƹEh!Qx—U ^°ºøq)QX\/X²óñ°Í0IáÕ½+á˜ó*GY³ bÒç²èSä Õe?ÀDôœ ³2þö'Œ+ìF¸S·•ª¯yÃV³óÒÔ¦vÃm„GÈúHgMãc•2rl•ul¢u+Y[½RC„IñHÓR·¬eß­[Ý­²n;?ÚÅÑ^ŒÈû”“jfèBw7máÿN—…_ó0?šW¹»^î~|ô­i«f%¥J?j¯3 ka¥ßanñN/D¯ ±¥8Ì“¦ÃÙIš Ž„§Ô Pºî2PfF»8º”™¡Ý¼\Ã÷R‘Pìî…»ŸŠ„ºíô@@‚€Hø!!*eËõìí®@`½…=‚ÄÌö:5»½¡{f{Ë£]]ÚÞòÐn^–D/v÷‹ÝOÜ^Ê~o%ÜÛ?M÷6·wl{ ±×šҼäPâJYPÌ“þÆij±ÎJËvýoøtä0!á¥|á[ª³\ð[SƒHÌ’!ÿé@OÒ9š„îš”G»8ºD“òÐn^ÆŸ¡I±»^ì~*M\]YÙ3EA¦0ijÉÞãÜòl×´ÿgf×B÷Ì®•G»8º´kå¡Ý¼<ƒæv­ØÝ /v?u×LL!¾uia'èç£Ì i¶èGÊ_ò¤çLýÑÁá[,VC4ÿË‚½Ï}Àmç ºg PíâèÊC»y)ÓÌA¡ØÝ /v? ZVÂLÜÃw½1BT*Ûõ¹¸y9C³GøGfð§14À÷Bv…óv44ŠQNC÷ NË£]]Âiyh7/Ùè9œ»{áÅî§âTÙÊMÝm»€Ó§j'ì.ͬ~^(þüî’lÄŠC÷ ŠË£]]Bqyh7/!9ù¡qè^=ûV¡{æ­Ê϶óÂ]^zé¢äØ]¼ÐýhnÆoý ©¼¾¤š¤G~>M þÉì°ÂNX§DR‹T?¡ˆ+.y‚™ap¦ !ÑB ÷ÈVF×ÙoJ§¥y*ËMæŽQL0eìî~Êa²ˆ&Ö 0WEãá×.˜Ê¹*…¢Oý+ëlòLâCŸL0:Ü“ …qáÇJÏ_€~‰Ÿ’cGq®£*;öLßQ£OòáϽyÎϱD.wös`àg´Ø§ìVð†sxú˜,| ë? ÛK`ÍøB÷43fʾ9ø/qã`®endstream endobj 25 0 obj 3546 endobj 31 0 obj <> stream xœÝ\[o·~?¿bßzTXëåm/F ±ƒÖmŠ:ŽÞœ>ÈÒ‘âÚ–|‘Úæß—äîrfw¿áêè©P0hÎrȹÉý\T¥ÒEþ޳›§¯›âòë&v¯ÿ44¾\n>oÚÒ„?±ƒ·Ï>ߟxDå ?ÎÉŦ*»®­ºTEÛùÿv®hª®¬ÛâäãæÍöùѱ*•m­Ù¾¦æß—½Úm êõMm+W6vÛPoEÍššìŠšz:n[ÙÒu|Ü/ý­¶;jžRóœš5¿Ró¢±Þ[ˆöNG8…4¼£æD»†hßQó95¿¡fEÍgÔTÔl¨ùíØÔ~Šœüe£LéêâäÇÍÉ%¶ÿLÍñÂv½lŠl?£%|X“€/pÿgðdœÝÚÚÎàüÞ‡D/c{ÐõÛYï(ÂvöûËö=ÔëGj~÷ûlöØçÃm|׸{Lû<¡á®‚YÕw6°#ÿ¼×Tƒ×T¶ÔQe<è±Lª²ÖªSÎó55½veÕ¨ÎnÝ‘suÍO›Æ[Îh]hçâêð¯÷É_v›‹»ør}¹ÒUÙ袶U=¹'õ䟛cã{›¶-Ž{ªÏ‡~¾¿ a}Ú6^ÓTéšÖÓM­ +¥3®kÆæ¯´Ä5‹£¦´­iÚí'êür¤KSw•á¿ü×QWVµn´µq¦kßRÓˉ_©«­j-¦ö!uŸ¢ÉÎ9Y cËÎzøˆ^ybÎá² S»Ö+‘òÌ·U cõX~/XÉ;ÜV~ k‚)½=±ù”Ÿè—;Ø<§æ“È /d|ÒO€<6ýeÜ“ÖhZݸÒèÚ ˜—œ7|£>zê­R¦ šÕ–uÕ4N ¤H„¼$BžRÓÇ–¦´átž%ñ`Ô]Qó†~úåHù…´uÇJ›K-Äšqs[ß.iÌ‚7uÓ–U¥Ã~’ÔéÍöĦ·uPŠQ¤Âö* Û>AèoHz éß)Òª”þ·ˆ@Ö¼Jðɪ)-LEI–sõ|YŠÿç$_·Ôù.q¡,¦‘%í‚:¯×é‡ý6Ö­n¤Ã.[oÓ÷ÒǨ;·ª•ug¢@ÎO‡½J„2BnáÞpk0¶ÞB꧆ðkØ€¶­“E7dƒÄÜÄ)ÆÝÐÉ'5lÓ¨Wä^S{õhYO‹$ž)¹´¯së2CºNjpA¬ ]ë<Ÿ«Q@bj íûiUhç¥ò55±ÀÑn>Aò Ç “5$̜ٿ¥]á½Ð~²A™ŒžrÉ#RâZ}(š&} D}‡y?q·Q«Zo\;jÕ/[àŘž'2A2ÞEÿ-Y”!ò1U»ýå --_Î%JF¥¹«’ *ë¼FÒOÂÃäø í„À©Œ¯¸ssi‚k¤~§©“±Lj„or6…6[OU‚¢A$”’#€¢v+HíÈV/ý›0Ù- ‘i%Ùû4é(;jßR§„œ/vZÒyîT…£Û=ñÙ2ŒšzÐEÀ4 KºT’®õ†èxÜ‹Yf2ªµ¥T‹’›zÙ­ùx»VqÅd9iWa°]A Ü%£5ä-õ’¯œ8¸Y¤+;%éN…ˆu.ƒ» …sÄwaÖ‰qÓŠ‰h#uZAèüO¿X¥¸3`ˆ½ ÓŠs$̤‰°¾‘­Î…A…º ,Ç,Ó™±C¯îŒ¤ƒ£ßXOi&¶„t'á·ª®1œÏXõÈÊÜ@…#‘¹M sO/µ¬v¥ÖIËîâP—ö`¥N} v2s 6æÙÀÂÖ¥nXÁ,€s]éTT†¾êV÷}Ÿ(úˆnâ؈ÇÊ9W:–\bÌ4€Fb—)òꩌÇJumaUm 3œ~f°å•Ö]â0Û<¶KØ>Ê›ÂLט ê ¸'»²mêöát¤Ñ%‚ûÁEðýÏQlWÚÂs Ô¶¿a§‘‚×ÇÒö¯H$˜¼æ6c´)¸È-µYvkë¥LTÖæ˜À¦ËØ.a L—Q]¦Ó]†«28.ƒåª×m?T䪃\%õ½†Ö­4±7ÉÞ!þúpþF°Ìß ¶Kؘ¿ÔHW«ªEp?¸>”¿Ö¥{LõšÖ+y8öž=°²š¶ªsÌ à 3el—°fʨ‘.¿Æ7g¦îÁ‡2Ó¨è3ÈÌ?SÁgƒ¹Ä°O×@6H™Á'<¥Hƒ*Oyð%T1`)ê=ŽÿÁù XñcYWÒV·Yi àŒ´ÉØ.a Ò&£FºÃ¦C÷ƒ‹àC¥M5½¨µPÔ^¨eÓñl÷;|Â¥ÎÉùјæ/¸êê&ÇÕÎpUÆv [ઌé²Nç¸*‚ûÁEð¡\­t¸ÛAƲR-4ç·©¼Ér<^]E®8•šæ®,JÀçkBus¥XÍr T“xM‡]dJèX*\–ò8ÎM’¾o<Ím§t=¼aôgo©÷ÛôSxlF‡r”•åùqáåf‡ÂI82Õ;VâGM¥CÜ—bìt¢Mþ°ÊºRK…´, À…”±]ÂRFt=É(¤îÁ*¤iÛÒy;䨘Á;VuåÃ8žs œÁkàü Ë W8ÃÛ%l+2j¤K+›ãŠîÁ‡rÅg»ª^pE®ü@\¹Bë˜Qv‰¢·7‹c-Ù Ë]ãÒ®¨YòXÀÖÊØ.a ¬•Q#]Uçr¬Áýà"øPÖú”·m¬Õ€µÉ}\!OY†MU§rl à ›dl—°6ɨ.Ý5|£çÉŽ ŽƒËàCÙä3W»d“lbÇĸÄÈjè²…ô+Éñ'‚eþd°]ÂÆüÉ FºZWgÔH÷ƒ‹àCù’Ñ{,`Ï~™¼ÎLwpᘮýÁK  €X€Ä’Ò·üp£ 3Ó¸LªFûÖ9ù àŒüÈØ.a ò#£FºÓääG÷ƒ‹àCåGÕe· ÷ØèA¯2Yć9 æ<‰–Éü.¯ÓWv–ž7eí”1¿g‚2;¿ 7‰&KêïW?õ˜Œ$BXf¸w‚âE:w.ÁE•±]ÂUFtÕ*wð#ƒûÁEð¡ŠZi/ E­¢>‡Šºö…õ^ϯ$.ŽØç…ýSê©:ß7£ž^. ÞUJQmÜó3wÁ)•±]¤TFtÙ.w%ƒûÁEðRª}m– [“—RVÖ¢¸žåÖ¬\¹xâfo§æçÌH.®?²ºPVŒÁ£ ì§Þ%Ÿ3)©ÚÂ&¿†w‡„[ôØÓŽ»ü+ÇïëZuÙØtOþ 9_èüs®Äv¹ÓÈÎ(©Œí¶ ¤2j¤Ë4\ÍxN7€W°³« à̪ä¹m~p—-Ž<‚…‘WÀ÷6=ã×"¼cNÑ·‹·¤{Ü|ÂÄ&Ü®¾—”'—f¡køåúûοLHøfró™yò±XÅZ°W@ðÑ¿ Þ¾²´ Ïùý¤P´{,÷“¼²äNˆ2à¨fœQ3Û%lADÔp$[uJeô[ÇÁeð¡€jÊ»•‹˜ÿ‡ÇV0te %è NM'…z<äõ|)”T]•´–-ƒí6´ j¤«érG¤2¸\*I•)ÝÝJ[©@Œ_E ‡Ùø¢ïâá+Ζ…e¾faogØ+c»„-°WFtÕMîÀM÷ƒ‹àÙ«¼×ÖË|ž^w¤WA€SÔbÊÿ = š\§ñĺäôå$’‰ºÎ÷EpF&dl—°™Q#]ÎåŽûdp?¸>T&›®A®Ô8U1òj6’çr'‰œ‘Û%lAjdÔH—5¹“DÜ.‚•šºJ—+Wj?S8ŠE…Œ¶B¥aúžZ“;ÖŠà Oel—°žÊ¨‘.£¤cǼ‚]UgV%Ïmóƒ»4¸°hqä,Œ¼>8EU¶îŽ”¢Â Hˆ¡<›¿ÉÙóùWôÞ «Ìøå ‰¡ï{BË~q‰¬3\÷ò›ýå4ª€W=+ŸCÞ´³Û¯cU |]¤/§Xè=ÞuzÑÔQÉlý±1~œOòÌeý%R˜yö‰}·Óª•ö±o5þ•š¯&ß;ð£}j2ø˜û¦Û{ø1¹Oð3lìðû|úsÑðløcœxÖ¼… J#Üï €/ò €¿ÃwUÙÙwö>À]ú·¼€S<†ïªÖÔÜÿ»ªÛl{÷ižÂõ¬îÿ=?ù8x'f¿½œoo ö~ÚüÈ»0endstream endobj 32 0 obj 3472 endobj 36 0 obj <> stream xœ­ZkoÛ6ýî_¡o³‡D%R”†aÀš{ ÛÚÌ{í>8q’vkâÔIÚåß”DÞ#ëØr²6@qÃ7/Ϲ/åC’¥*O2ÿ„ÓËÉ“c›\ÜLšæäø»NX_L>Lª´ðÿš”O/“§s7Q™Ä­3?Ÿdi]WYÝtª¤ªÝ¯µIlV§e•Ì/'¯§G³C•*]ébz,â íÄÜLiub®3“Z=µÒš‰XŠh©˜÷×­2š×]·l®¦g".D\Š˜ˆx#â-­wt,öNÄ+ºÂŠNûVÄ#¿1ñ+•ˆVÄo‚˜»-þšÿ8QEjÊdþb2ÿrÛûAë/"þÜSùcÞè±÷û$WXS‚ϨO©ž/© /Å!‘uAÉátFƒ-Îé~‹àÀ³{Òîzö—C|Úæ"Ž>;<Úû1sÝíÏà|œÁ°ñ¿Y0àšnÌÉoÁñ­€€+z†ƒl=?Düÿo2j9#@¼Û­çp]ç¼Tç¼”Nóð®ë ¯‡ˆþ¹›å\eUÚéÛ™NkmUíO`ÒÌIíÕSS˜Úz݇Öõ,O‹²Î w•[¹¶³:ÍÊÜæ8]†…ŠÒ3:ˆ«‘¡ÎœÖ ›WÓßg‡þî¤ÓŸÜžZ©¢ž>‘‘dz,-óÜäÖûÙ0´_\î'õfÆ0Úø;¹KÕUó@¾¿P6 ÍÜý=\4`Jíš“™M=…MXÖ訋pÕÒTqÓ[«½µ­\›×~¥ó°©ï½•鲑³wUZfÖ74L‚%»~S8í’5›«2wöS¸Þé O^•N¥ wæËÉë @”“¸ìÝ–£Fñ6žú­ …³Ò[‰*2R®*€[]ÄnØ]FöŸ ŽÅáùŠfln«4Ër¯‘à’ÖA^-ÝÂ6s]š·ñ´J; 6pô 6lô,ü­¯ª•qÞ&ŠJÖ3"æó˜¦fÆ”~§çóÉ«‰Õ>j-][›˜¢ý¥ôÿ»¸v}69ß'V4VF;8ȧÚÃîbó¿'‡¥»y®E]mó#ÍèèJˆýA4ËZð&W(’½ÞÇ¥ÀX¼™F³´Ë x)ÆæÙÌÝ¥Ù¿kÌ->Œó{ïž3‹§2ÈQ`Ãi4¶· îcÄXl3f0‚˜èÿAÄ'‘Ã.@.R]8ðšØº^D¶ï™ØÖYw¡:˜ ›xéYôÍ,H°»X@0¿ÐÛ¯˜Y‹ËßôJtz7ÐiQ[…—Z²i=äû=ÖwX V#`Æxq"w"’g÷D}ÞÅ7/#Fa–t`ú’ÑM&ÈJ°hë: [ -`qO3Õ™2 Ï1÷ Peé캄QOB=ñØÛµ|CGêÆÉˆû\ò³6,òø©U`>ñ³ƒ`@°¾¤†€z.8·„ vî÷Q: QŸù;z¬ê4¬þ©nÅõ€ì KQÙRlŒBôŸ´¹AŸkFŠ3ÎUY€œË­%ýæRV­Fú‡ŠÛåùTdzèúÜR*bˆ;¶O­ãÌô/;([C4Fá:†q ß=ÄÆ–ÄÆÛ"JÂþNSEÖó¦Ç¢Êç2 Íc\P  ?$nîvDÁz°KÇî…XÏnI­t¸¤F^\‹Î \zKÆÕ€*n(8³ûxû|£«öðð}‡[íëª$F”í©©¸ÀÓ‡Ä±Å¹ÆÆO1–\í‹mO*º48×ïz“g`=ÍýÌdÙ0yP6Otn?gò •Û21YžV=wPS»Ò‡?ÜåmZ™²„ AÒwy-nÁžI´ð\Œ„‚EÉÓËVAØjÁ ÿã-ϼñh‹M¢c©SðšCÏ–6N®¬ÑÂЄ„[“.êÿp&)x¶ãÊ*ç¸ßfsۈǽ`Ís¯Š@b”퇽0µ“ ²ÃoÄ´8¨‘õæÜsG+#Ðx²znâœUÌ/ÂÅ j57j¨‡&UõBîJÄýAðÐð Z…zçÈ"Vaa,ó̳‹wœ¯aÏ!rÇ’ýKœ7<­íUéÐ{Y'Ž–NZP¦Hİ „ÇÔ!ŒÜÆ÷–hyšUg·ŒQ€î ½Gu@Š›Ñ:=¶Dxa6-‚`”)ޥŕx„Hÿ–V£áO«¦‡‘*Àø"0ig&RåXÍîe"¿€Ú»‘ä‚m[𵣘çúwÂ6ô ±¬Ô Ø¢öy²(±/Oy}k<ŒC$b‡à=‹+Ä‘=•ˆž§4ëH+‡àÀ+‰î.×x¬ÝéË÷rªÕ¦7Òmæ•„O<·Ùî»wJ&ÝR?Úð'rÇëbKÍ] &jª„…@ŽÇ&èŽè·æûÌ# ƒ˜XO†n;ðþ£ØU²OØ{f;†ð|ûÉQƒ„!b½I…Ö+ŒuÊÐÊH‘á¤ËÌ›¬!P‡EŽ Éàù‰»ÝªÒò xš·Ts\SlasäCwAàTr®)^ÛÃõ¡]ï¨KÊðÐ)'â¯(cN7«ø›Á¹„ <Œ|úûtöàñ>%¸‡Å!$¥Þÿc šÍþÓ7>c3 iÖ‡këGŠTg9-Síû]öZ¨¶`~hà2*] )‡œm})–÷JOÁ¦pÚ.Ù|–zAíä*š?ˆ6ôV]@-À¯uhV !F xÿ :§$GŽñÂçKŽ ZÑs€tC?•Âú´î@ë\ÿ6JŸ#¥c-|À¾×0/„¢_WøíU¦[r¹ÃjÈ…VA’,mÑŒ>ÉÀAû·Ù¡¨•Ùò*è‘ïEóA4=ývÙ) èõ1²§ç­mZš¶Úú!Ú™x‘—D›*æ«É  Ôendstream endobj 37 0 obj 2171 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 23 0 obj <> /Contents 24 0 R >> endobj 30 0 obj <> /Contents 31 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 23 0 R 30 0 R 35 0 R ] /Count 5 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 16 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 22 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœX Xײƒ¨HÓ€!vC‚¸PQ£"n,Q1.‘ˆ,J!*Ê®Ã̰ÖÙ×(*.¡ ŠJ4.ñƈËK³ú¼‰&¹©¾ïð–Ó3èpï{yßûü„éžsªÎ©úÿ¿ªPP.N”B¡`Â3³×¥¥¬“?I¾ é%'iˆ3Â1ÒwÿR¡¦¯í_é¦@nÎÈÍeÇKÞÛ=$~¹î”³B‘Sµ-<3+w]ÚªÕü†ÅÏ+pĈ‘Ž7cBCCýVæ>ûÆ/"e}Úªµ~¯‘SÒ3³2RÖn˜ìNV§§§%ù­JÏÍZ½Þ/199%YÞ¶(1=e_dZzZVVæF¿aá~cG3Šü“–±2{½_tæÚL¿y~óSVe§'®û§—EŸ–»6iFfrLxVJlDjÜÌu«"ׯž¿!mAöì5›Òçæd¬è7rTÐè1c§Ž ?aÒä)5ŠJ-¥b©ê*ŽšIQþÔ›T$5šz•Š¢ÆP¯Q ¨YTµšM£â©9T0µˆNÍ£SÑÔ j$µ„Š¡Â©(WJAõ§PîÔ Êƒb)ò¤8Ê‹ò¦|(_ê%j5$‚r!æ/+¦*Î9MrjuúÍ9Íù²K„K‡Ò]™¬¨Ç#Í£ËîöôòÜæy›óäæsMÒãÒc$JÙ¢&µAs«3$KVî&ÝhÐ[ê5ú\ÇÓ¹ÅÚÂ"£¶Q¸Õ“=M~**”Ÿ¤(XN7èLE…Zuƒq³Êf D8!* °&Cª³4â¸s8@Ùg+Œ¦­}½E£ÏRi¼D²*ñ :WCüä¯ÝhÕ“­L§sÕd‡I~5‚n4’ÅäHSè3 „Z‘Ûi à„rM<ã˜6©ATt á°WöWb–¸Õ5ÄH!¹—ÁbÑr…94¥e½Ã,v¦óìl>¦q~€'ÂåG}V¨è YaÐZ›KrÙ‡b–è"ìiófoÃd)„›.ßD^Õ(´à‡ tŽÝªUoǵ Ž>(šÌÛÍ {|×ûͦ£¾ «vWÎÞ–u(ƒÁ(M帽Ý`?DžzúÎÌ ã¢Wmš÷&O‘/B¼FNñ@ôfJïJn\âáEM1ˆÁ/މ}°çOAà}åJÓáNá}ëöFdaŒjc±¦¼¢XÃ/^6w}8Yé4íæïà|«œîw%.ªØkyUuÚzß­z£U`~¯b¯5j›yöh¶8W°ßxN!Àq—ó]):“\|Ÿ”]¤¿ï¾öàó''.Ø{fî‡}:œÇfyQa‘¼è,-âR`¢êƹ„7£cÞ/༇ƒ|T]B-Úƒ9G7î\…V1Ñ –DØï+)DE—(¹—g¤p{¼€âAO‡ÉÄÓŸaxü { +]~¿7é•a&ùûO¸ûôɃ»¿ ¶ýëDx*‚KÓŽ&Ž6Z“`€7[$åJO¸QžÅjzi×üCÓHxú '¶|üô»uñð<[8ö‘ê96Ø¢…4΄§Ü“;“üÉʀɓÞ ^ïÜý™N²_E¸&:Kå^’µEÞk©·á*PƈÌ1«ÐÝ“Dã R¥òe:ϤálOvZòÒ f›×!LJ¢áBO¥Ò½¨ø”˜_'ÕpoÝ=ÍB±ï%,K\ž9Å0UØâðÒåK»;Nñ{wom@&Æ\lPkJÊ5:>~îâ÷"Ém]žBẠú}÷œ¿½»(¦š¯Ì¯ÓXS¯75à¢jÐöòÞN}üð•biù^þ¤¡*Äl%‘7ê EUBz]¤>1cæ.š!Ä«žëÔ7øa CéàKºÁÔ«Z8®Wöæ‹Pm¿Ó=â7ò9˜Bý°ûÇŸ&\*àý}(Þ“ÝW i«±½˜§!ߪ!ZuçÊ‚¨ÈðùÁöx­„دdmñø·ý!njõfO7Düè\CqýVƒ±‘oU£Æ¸¹PSœË'`HQ±p"ÎÓ«-2RyzÂTr¶PЇ`ºQg,*ÐìiÜ®zÑtâ=œ@ù¤(cFFïf–oâ‡#lOv,_%t4˜ë·ò ïÑìÑ'àt|×qߨiËî zmuyuÓVõ¼zá½Ù¦µh1ŠË|}"ó/ôùT„ïˆõ' ’¢á޽{åí·ÌðÅ.£Fa«~.]g\é0§ŠZÅãHÕ²Õ7r݉Ž4>Ú|Ðr‰ 12쬊}ô£hö.~#‹{ÔMø4múë£ÂŸº»úzîßž­Î}n±8^Tô<àðy *¯N]__gvüŸEû_òn×aB¦ d“MÇáp‡7k–"$Ž[} aÛ|İ‘3–ÍYÓ¸v_®°/oŸ¶[{N»W»·poAÃ:´aÍ‹æ$Ÿyô¯W±‘[5&RŸæÈª$L$Ï9zM=ÏVXô«`Vµ¥žÊ»B$FùàÜíC›ZÒw kv¤˜cõ㌺ªl ±µÑ²¥¡áŶs»o}”ñN-ÏͯÒk¶úZô&‚ó*v(©€E¾ü~Ã94¸í$…ϳŸ rȃ·#j D(ïͺ“¸!fq/ñSEÈë­À¶tÕpÄÉ­Õí âæ¼C2èPx9óȼövœDŸ1_M¼Nêp埒tªcëtj\ F©º®ÆÇΙ´8°W}#ŽƒÚœa ¡ôSYu†‚Ü ö sHI7ÞIO¿´ìǯ€>¶ß¤1«KÊÊKµüÊõ3ó &2ñP§M騣Ïè÷ 8)à é ™}ãúœÍ?ÄÁ}ô?íòdÓ»:AºÏmÖjr…ù*€­üÚ€óªF­¡ˆï…Ý$ÐÁèLÑã1·.´{³C¥7¥zŽ5‹‰ q¾C§D'6&·lš³lþbóõ¢&Ýž‚}y kÐj&lfüëf£QXgÙ•ŠíDHCø@ȯTå4[ë‰;~¿Š5//®¿æ n__{ܹ¡=e·nÍ4LµD›Öë³Ö74£fæ³K§ïݹ”[cƒ[Öâk©34 ¿ªä¾ÚÍg ËC»¾ß–øÔ›Ý Íõºåh Ùœ4É(TŸ¨×Óìñر³|'‰§øx¤£›Kl„>¢G—IÙ#}hž4Áëßúä¶`!4Ñ×£>z=uNaîB~K‰N‡¶0jC±¹ª¦²²†¿³kŸþbnœ~w©¡ŠÛ–^·œt@ƒBâ‚öÔ´OßúñúŽç/ñÇ]fÍ[$·ú‘ -—¯øGûIúÈ?·'É&‹È{ÜÝÒÛcoàwã‚ôtX˜Ú9¼ÖÁRŸírÞ,õrÞ°«¬qòèfŽÓø®RBÕÍ>ííÔÿŸÎ<G'Ä’Ñ+Â<^ÆÏ·pÖr0Æ*õ4{»ÑŽ bYáÀ|‡`/ü"x)OØÆ¬zM]Ž<Ô=O€ž&öÆâyƒÅ4û­£Áì8EMð7‚ðpåœ>H÷q¬(î;øuµÂÔÖ¤'  ?äVÁ…ˆU˜£u"ã_Ú³§ÛgîÅzI*+ž?vpÏaß–iI¾ÖÝg˜è¿?v±yÉìáï¦lîê»àƒ48L_|ûÔ›+ßݸ$™Ï>¹f× ”ˆÖæ¦-cþuÆ"™N;a¢½<=Ÿph¶¨Çï`Ò£oi¶3¢ã“´._èÿø ÷‘ñÀ°…k"–ùî—Û“^ œ:|Ø”{Ožüõ®­‡—œ¥1IJiµyYØ)¿€ÿ¡W®b—])dØDŒÅh4×TT•× 3aPÆôú£ù·/`@Umeª!3ˆq o³õgˆ#¶¶¨‹·”¢òêá üÆÁ¸i@agì< Sx Âco‘©Á™ Ú@ÁXýˆC—¯î?±»åPcjC-¹;Óš—[£Ð\f¬ Í͉ÊXž–¶)‘¤öݦõ-kOä^E—pˆß›’ÄᘸüÒ-R“è Õ†jTÃO›²Ç‚…>Cèê3ðv¯üÍCŠSIs] Þ"W­:òÓ ¿aØ„{ý›öòâ·!÷†G/È\–Êç¬/Ê@¹L±Qm2×éëkømŸ\n¿Š˜›ŸÇF&ädŽ ð¼^§bã¤!·h«½­ÎáÙÚÕrO-í!ÍIÏx((­*©.CƒuHWZ¦+/ó)/« ‰FÚZ¡$¼ÄKx©¦´¤i£²Ê²*òϧJ§/«C$ •5ÕŒ-ÐÐ*zH¡bŽ­CÇp—«¬¬®¬$=ãWŸì=ÖyþÅo¦}ðÖ’üŒ$~S~a.R3fCuõV#ßÜrÊz1·/.›»"391WبίXŠ˜¼-½yg¯=º?{Ê´i³ƒ.h:ò¶ ­-­Ö!F]¬Vm-lÉ:³N"ͤê§{¿\}ûÒ{ÛO¾–ëäE¦p/xƒÃž£û4s³éçʵ¾õPO§=ŸšÄ×Äs{ÚÍÛ¶<ÒºƒÈÛõó³§„ÌŸµ¸ã~–PjD•º]rŠ¥V[e•#¹Rjãpp©¢´B‡ÊÛ¸_[»­–‡ñRƒò³fšj“^C&>CÚe¸€Lœ„¸äŒÌ”UÍ&úØ}ÇŽ¥ïKÔp†¼_›šÚ¼Öþ~ÿÑ£éûSl‚vLÞ¸™HÇÌ–cM“Ñ.3`ïŸíî‚ZN Á…µåµåÕhpÒ×TÕUUûj]M\WZ§®ÅÀ ?Lµ5µÈ0UWT—“ÿ>euÚrETZ^RÆ ØÔ$…4‘vª©‰þÐUì÷¡››èÖŸ¢þá;{ž endstream endobj 43 0 obj 4621 endobj 44 0 obj <>stream xœQMkSAI¢Õ ®lñ.,~tQ¤íJ*!´ÁPÄ“÷îK.Lf†™ ö nÝ8‚6*´BBwÜ—”B‡RTŸA®-ÈÅR­2ò¤•Kà¥Î`J‘„Ç)š hA;"çb ä``…ò˜×@*•㬲ù\+Æêˆ"¥ºÚy—Z2¢c÷ÕëE~(|åë( óX™ét<ÂÈÿƒyAÊÇc_ùô2rFŠ"úF)ciÞÂØ‘,Ý[`q l&ÑÍu«­,烦ÆÈbÎÕóª¿þäÊ<éy¡²¨‹#:è,—ÿGaŒÕàcœyVç¼±òøòÛj|áCørÊ/?‡ÉƒÍݽ'›ÓÝÙl:½˜íý~þhõjûôfy«¼ÍØ5|Êã endstream endobj 45 0 obj 429 endobj 46 0 obj <>stream xœ]TkTT×¾fæ^0p½>˜8sÍŠ((øŽ¢±*83ø£D&Îò`•Ê“ 2TÔAE0 ˆy >`iE /i“¶«m0 †Pâ¾ôàZ=Ëv¥?Î}ìsÎw¾ó}{oš’ØP4MÏò6¤F'ë£"=¼ã Zkh™(§Å7mĶÇM<ž0IP»Î ; {[d/©z“ùÖn;ÁG³ ã Ê–¦Ó Ì>ñ ¦D½.6YX¸7ØméÒeÿ‹¬ôòò™^ÏÛ¢“ôº8Á•|¤FâŒÑqɲÚ`ÐG :ƒ)!6IˆÔj£µÖmA‘†èÂZoÐ'$ħ K|Ü„U+V¬ô Uþzã¡”$! 2.IØ%Xùÿ*BQÔìx­OBô¶UbÒÞä”Hã¡baåzŠzRQoS{(5µˆÒP®TµZMRA”7åOùP,ESÎÔ|Š'QʺFÛÑ:ú±Ü&Èæ¸Í][Û!‰“$@ò\ºKZ+ýLú¥ô•¬jEH)¼)]ƒ` Ýâm{¹b«°zS°Ö [aç’—x“²Kù¡õB×`Õ–ÍïŽu>UNÍð¨—à Xcq¾JÐr@˜ËeŠns@˜0b›e±Ý*}‹y7ü^ç}qëõ`%×ÜЧï“?A­•õ],¶øO[Õo»…jTªÐ?µ *'b’ÁY,†Ùô-p·ø˜ÊŸx”Ýhh øbe¦ æ!</Æ988¼Âa;8ŒÃÆ%v•åy¾äI–°šç°Ö~úØÝi6ÅV+ËÓN§—hYB>‰ÐþfÄYœ¼fÞ!b…W$R/«ÛÝßX˜ó8Âú§)OuJn¨Sç[·C®A¡qúÝ,ØÅOSQ«U¡ý££m„ºØ•üÞ†Y/i±RùB3ú£îcÙ7t#kz¦Ø/_†%x;Þú|!x€ÃÈÀ/5J¬eDhßGQèð¹´O²-'.Ÿìd‹†ù’gõMQ=ªÍ°Ä—¥}œz*†%ú p“À‰w¸O4š7d`‹¯´÷<è¯y@Š@bx±g ¤G]‰%äP2ï>9Ê“mr¤÷ÞÅkñš€w±TéEâêáïÿEÿÚ}â~}—¬Úüû+æò‚“fÅ8s¤(¶0±žÃW(wlö|:é ‚èû ã8±œ0=dx6&LÀOxøœðÏà%:€ÓäåWFñ&y+eŽâ|rÒ|PåYUoxDDŸl ’72ÜÀ³¦¦ggÏüî‚ÞbŽž@ùh' ˆ4z³\Ã/ŒUZ°q°qn#v€;Y1FÅ"ц? ²Õx¹ç‚wÔØVWj¸ª¼~.ïNl žÇ ×ç˜sR\>ЦíÐÅ›MŠÌÒã¥Ç¯’å²±Ýßü`=úÝ/o¬m¬½ÜºÐ`l˶ ¬êœ§5çžFÙOj-í nô º–T2å@Tkï'篈 å° Ï·¹œp“‡-²ÇÕGü£Ü‚leA:2¡¤ù0åB, k«dÉeygQm>Î?ãÙø¬¶©ý=H늽þA]X¹b'富à.WIþv¦Ï?ŸÉrãç3Kõ.(67>#óhVÚ‰ÄóDßaú›éZ¾Âo‚E¤ø©=ð_dï´D~WSyêt¥b„É:y¬ ±1y¥mJhý3UÃ3—VˆùÓUnÕ2]¼1Gl ÿd¸–#5»µð&í<³OÌçíU½ .}ç´!Ò•$u@ê\M vÃ+DÅ9>ñÒoÏ¢jº°Ùr»b×Í~Ø)OrYñ½KçÛ]eÒ™òŽfS¦ç"¤Î È'˜‚÷!wV]™Àr-‡kZÓÛä½èî•öN–KGñ§rËŽ²°L†7‘çZ6D…m;WÙxó¢åYÑVÒPüqQùéùŽÿv 9æ3IŽõR[¦Ç\®½÷u® 1Ü×}×{[¯&D(ðäTäWÿ¢33¾ó!¦4¡I‘Š#W}‘ŠJ ÛÇríO˜ÿo=ÿm=FžX'Üe¸ö ºÖÄ~9ð#0 Ö}žò…îŽ2¦k{Ývä‹Bõ,˜°Ãü@»j±[ˆJ½í@ÿØÈTš‰ýº`a§¯˜¢ýGârÙ0¦®¨Ö|ÙR[Uv Ýgf­½‹\6vø¥…†>stream xœuWiT×¶®¶éª…N‚v·àŠ “381‡I¡ §˜ÈјxbŒ 8 Q%€ 8@DC£Jƒi§«Qswõ=Ízïxo^~¼µzu:UçÔÞßþö·÷Qƒ(‘Hd蛚“+ÜÙóV"~ä ~”xNû·›._2Š 8ð܉‘‘AéH)2ã}LáâPÈF‰E¢¼íûç§gäg&'&e+lÃCÙMœhÿ׌ÓôéÓ+òÿóDᙕœ˜¦G9 ©éÊ„´ì™ŠùäíÔÔä8Ebj~FR–"6>>!^X›š¢ðNNMÎÈHÏQØÎ·S8Ožì4‰ü9%+W¬ÉR„Ŧe)¡ ‰kRc3ÿ6IQ”çÜü´¸ÅóÖ¦ÇÍÏHö\½2Ä+3Ñ;+)4;9ÌwÍB¿œ”pÿÜÔØˆ<åŠE;míö“';9{¸¸ºM™:mÏô3gQ”5ESžÔtʲ¡–P!”åH¡PÞÔdj,JùPNÔ8*Œò¥ÆS )?Ê… §ü)WÊŽŠ Ü¨ Ô"*€šBM¤SÔ—|'©•èhDW0–Ls“ÄZ³§ÙŽÁÒÁNƒ}¯|pp—¡•aa¼aßň!UF¬Ñ\£x£~5z`¤3b<ÒxŠ12®013q2©6y>tîÐê¡]⇥ëöoS{S8a¢sD%¥â'‹ø4HçâtÉïÒ)%؈Æûú”’Ò.¨8\@ƒ<•˜ðßà4µî3²f8_Âá±z{lÉÛKœiðÒ—¦L[·2¯0 ønÏßÁÎal<ðõ!t•ï:ô㑃ûýX…:“á&|#*á´'›µux  ߪ¡P-êÕŠásxÊñ£ÕúÑPÈOVë·÷)ùbµ~mψ™ó4ÄûVB( ie« ™å‚ffÐà gÊYi둃‡¿¿º}Ênß¼m= G+R–¸³ÒÊ—D7qZ˜ ©L6)d—Çüd²ÍlFúúfìâr_+< ³ØÃ­Ôë\¤¼:æòêftýr¬î.›Á ÏËsÒÖ¤.Ï_ŒPòîìŸòöoúaË v ½Ë¶#†"ºwøTÍ™úýw eÉæþdcœÈõ6ÎÆØ4|¶Ëä…ýšpYÕC ¿ÛsÞ©ñ;âºB­/ðú¯Ú×uÃF"øþ¼ WôÞç*6&<7™0azM< SÊUyW KsPˆ¨¨Uó–%ì;”#[·ÿ«ý[*Yz6nY£H rç*æœÍaù̃>?¦B•#êÏh¹W‘¶p»ìSaº³‹Em]¤.‰ÛˆŠô)»HM*ÒÑ£ Šeekwg¢•ì@eÒ4»,ûó(gú5~ÞL«Ü;ãµ…ô#ÑêÍtWeœ“\Êù£eÿˆÙ§’R:©ló‘­¯‹À(¯bÖ)¢Ðê3'/vY‚Ù¬Vl#Ã=ý|Q_C„þø¦ÒÜÃ9ûV¡Ö-&ÞVFx›ÝÎOh=%¤ÝI‚èc¦á1ž>¿êtD…òNqñöïŽÉÚ˜õ߬+ú±‰vWÊ?cL “ø™[ Å¢{dí²v𾤋Ö) ¦Z?!éHàʬ^ã§…7­iZ~/W6v]xAÞÕv:î\ö*S~K$Òžß³jV†Z¢øœ¸Ô5i¹ÑëCÐ,~0©&ãç/Oo«%AØæ»/¦<î‚Ï“D£nôkq]ECõ‰;èê »k{Þ0\ÚáR–rì²åƒ; o€½a¿m@׊ԋº^ð†Dê KðS­Né€[ôC Å¡Oùž ôÎnü2»Ó;ˆ:äñ9æ|É4|‘Ö*¼\ýbÏÜ’ñÌ4½-ãz'쎆£­ 2iÞ<¦Ÿ‡:Z-‚ ÿ¥³â`“Zß:6÷Y ú ”–·/ÄA1‰|Pàe?ˆ"¨2‡h¾D2‰Æ¶zw¬àÝÉô%’ÿ¼"à|•àÙèíÇo,=^o¯ Gì‰k/èw¼ãŸzGI?—pZžˆÁž|ljŸ’0ˆÄ¢Í«ŸÜaVÓ ûúcñsê­¿F[¬Ò¾ØD7­ÏÌÍXŸ$ËÊØ Üºˆ}Bï¼]U®BìÚôHù%åøm†ù_§¬ ÊL]Š|XižýÝà?ï6–6]“}q$« @û¶ÝIÊøp(}ÓÚÌìäÔ_D!Ö?áDcs属}rÍÞŸvÛÇþÕï4‡ïI·kKãeú>¼Šï“Œ§a?é{ÿ l-”qà ß ¡5Ã¡Ø B%ö4ÌÇ»q .–¼¤aDƒŽ–¼h?5µO CøYH„®VÐg5#}ü[ó–›§|e¸O˜noTÄû ·ü(æEø%;¯Øœ(YêåØ2ä…¢W/ `¥ ˜¿wNM]p‰´MýÒôð#m:Ó˜Ön²^RHç‡ÛG,›¿8#$^å ŒÃ%œv ‹ü{ñoÏ(tæH °¡±¥¾Ïà{x Ž"C‚ *Ð0•³ô¯HÈWë"‹E·µâÛÄ›zfÛ‚ÂŒ¤-lsý»“¿ Gì;¢0Ï„WÚ‹a=yg<¶Àþ;!ì| û¿Ål3í6Q0 +—p×€5ûiÌ,Öâ$î-XÜNœÀº:;lÕ’€D¹ã"l;u¹ï&> endobj 15 0 obj <> endobj 27 0 obj <> endobj 50 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 14 0 obj <> endobj 26 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 51 0000000000 65535 f 0000017129 00000 n 0000033433 00000 n 0000017033 00000 n 0000016225 00000 n 0000000015 00000 n 0000003899 00000 n 0000017177 00000 n 0000032703 00000 n 0000030867 00000 n 0000032979 00000 n 0000031295 00000 n 0000031824 00000 n 0000029726 00000 n 0000032063 00000 n 0000029886 00000 n 0000017218 00000 n 0000017248 00000 n 0000016385 00000 n 0000003919 00000 n 0000006736 00000 n 0000017311 00000 n 0000017341 00000 n 0000016547 00000 n 0000006757 00000 n 0000010375 00000 n 0000032487 00000 n 0000030327 00000 n 0000017384 00000 n 0000017414 00000 n 0000016709 00000 n 0000010396 00000 n 0000013940 00000 n 0000017468 00000 n 0000017498 00000 n 0000016871 00000 n 0000013961 00000 n 0000016204 00000 n 0000017541 00000 n 0000017571 00000 n 0000017614 00000 n 0000017986 00000 n 0000018006 00000 n 0000022713 00000 n 0000022734 00000 n 0000023249 00000 n 0000023269 00000 n 0000025209 00000 n 0000025230 00000 n 0000029705 00000 n 0000030778 00000 n trailer << /Size 51 /Root 1 0 R /Info 2 0 R /ID [(à ´lÌÇ0jÅ€’ ß)(à ´lÌÇ0jÅ€’ ß)] >> startxref 33642 %%EOF simh-3.8.1/DOCS/pdp1_doc.pdf0000644000175000017500000020141311143604407013556 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’†/ ûæ‹ê¤qmW’Ûfœ>È’-7µ%Ç–Ò¦¿¾H`?)§33 ì·7`ÏO»²PzWº?¡qñáèë³»ú|ä»w'OÆÆ§«£ŸŽº¢rÿùl_|Ø=<³û.‹²Þ½=*‹¾ïTÙùïj§úºèº)Ua?8z•½ÈUQ¶½ÖÙã|_½®Û:vVÙ>ßë¢om2•ïUÑ×ZuÙ.×E©»6;³ÿº>¸®^·mv—ïíòUÓW]ö>|>*·íº©³úÉ·mÓT²ÐK×0Mm²ÏBh]åe¤ªëìÍØiçþýìG]¢,Õn¯+$—vÏ¥Ì ›²µß«º«Ã9T½Š´ìÀ‹¼.ÊÆXþö¶–ïåð]#ñ.ošÖ1áD¢;)ФìG‰Tm]Ôõ®5V­‰åûìG;C©]ŸÌ0zœÒZJ}ßìZ;µ“)JÞ=uÿü­_2ÌW^ÚOÎ~÷*{ä¸×1*{žïÛÂî£l¬À+'G{|ßçû¾(K]êìD†~›—EÕ´mYeOdÖ72àÌîº-ŒjݹDZφ]ÝÄÅLcî#{~ÖÓŠx¹†·ˆ‘Ø|͵!´¼¼“1oP€lÜ û®|oÝvY/ß¡Yù¦RÚ»¥1-JlF¾ºhÇucWÛÖLN ¸€+ úzfý¬´áûèpÚ¾³¢ò*\Yý.« Â¯éɇ·9ÓS´Ô¢€€ÁÜÁdçÿ´ß­4ñ‘EÄ2øXñ.–² AêÊnù—\÷…VU³dû#‰íg{û!›X­ré€qŠÂ§—•$é¶TIÂ#)6OF³Ü'fLçIF‘¥’Œ#NmÌ’ ‘™§e!úHÂIÔLZ™©Îû¿Æ¾¶Jí#óµ“éð…;B`âìh®OOëŠÎ?§‡Í—.ÄQ ‹Ü¸²p(LÌõèÂà@Î醮èj_QºK! ÷¾CÑ6k &íV$õqÍ.ñ؃CƒkÉí”ÅV51ëS[5ïãÁ)xy~ôÀÊh½toø¬J”Çz¢“pD6é3Ý? ‡Æu¨l‚)Æõ-uHcÈnSŽœ¥8˜¹î¿®ØI(Ê~Ž¿ÉÂ’$Ì‹ „ñ´ïr¥§6hœ†}•º] ïXoÎ*Dá´E€ïÃôIF…J}¬¿šüÆ®ÕkÓ*kðCt ë¨š‚ ¼[®ËihNð k¡CåF =Ës¦òƒ%gïã)îˆ<¸’“cô§rëù¸ðkƒ¾D¥cüW‹û„ßI&¶êpF¼ÆÝÒQ²öÅ0ˆ„Ý<“áržåý¢â’ ÜQe¦9ËÕåU F ÏŽÏ›¨æwèÈëQ9E_!ÄÀ~ÕHHÈÌdP¯¤eã:‚’®B•æKq<•™¡é›æàýƒï½Ë™¥ZÍûyàjǧøÌÂÑc3¸ŒŽÍú]ÓµUÈôW<¿ß'±¨Ó£ÐÐï~ŒÎö.¶bÚJ‡WrMª&Õ:ª!Hw«²ü<8Ñ­ þx1ÈÍϛr ýuÔ^p_SÈ~¢)‡lÔtêSƒ»y’”ØX¯É.31¿_ðÇ}‹?%-_rXCSá-Ū¿úõwm«©d\×,n’Q-vC¹šQðxz!êõl_d&WAîÞDkBܹ$ñ•lð4f݇%.Ìl‹è‰»ŸfÛ°Ê%xõuÙ¬j­iæñû*tø=.×uÀ½ ŒM [lþH"u0 £ßÖmÑV†hûênfÑŠÏU¹ìg1£›eàiEí·çºÜgs¥§Ê¹’Á¡³kðá,Eí†{ðû½YSa;?/¿í\LòÒy:¯\ìÒC3§¢–éy ýØðoIë— ¿ÒP-h.¼‰0l8Añº¢ÒAï.¨“‡ÍLÑì!$Ñ"þzuÈjoZÌND¢w9Ó¢oúüæðN–‚Ć« ¯°KA—\Hyá,ÜGa`\lÒªëÛ¢÷¢!Œ»žŠs ]ÂRŒi8ZšÐâ!uãI®ÆÓ³À%5ÐA-½†ëØß“¬nņ/á·ßµÑíoÔ‰¾´ÁÇxë(Ü xÓ íÚ*É€{J‡I‡¿/ßYL«ÙEe ÚZ@±%u8Õ§¤§ÑïÏ—Ò]½Ý,-Ë <Æ¡iÖƒ6:Æ4žYÔ§ # ×(쀧ykñÀÈ/âz~iU6.­Hv%øš]¯e¡LVÆü[.?/¤ wtpEÉ_:§t-’rUF€謖×ñi;Ù>¾ž½*®¾ƒÀŠKï KfŒþÔ‹ÃÑðêû¼á'Åùð5³øVt/½„®¿j XŠË1yNY‘ˤØ(…%{[x!OcZ0lÌM¸Œ­Ÿ©Vr±LÍÕ‚­¥¯¼X…Ÿ3ü(‚ÃM m6TbNžþœE6y1ƒT×ñŸ¸|a±`rþ¾³¡`i©f!è&þ®jbF°A>¬Øp`Ì¿äs É/ª"7lØWt_÷x.¼Wù=^¦&µ'ój£€U^;>:ëÿiy,{Õ¦5°K*Jjk诒n)Þ“Z8þŠ [`þ{VÐ:=W°Cÿq¿ò6:-ÿÙX×ëÊsV¹r3+§oÔ'¸RPpÏߥ Z[:–ßÀÑ%®pþÄ> stream xœíœMsÛ6†ïú:’3ŒÅ7®uÒN[g’Úê©éÁ±]×m­:qÜNÿ}R$–ÔÊ¢I¤dÙ—5@|¾X,ùq̈1¿•qq;::µãëûQ‘<>ý~n|º}9&ãO‘€í‹Ûñ·ÓP`ì™7ãéï#μwÜ–µÂØèð·×c œÙpÁí(çÓ?Ë2¡íº„o0Þ2¥b_3È'šq ^…£B]ÙY>QL8¡lv“ÓÖ…üÛ|â˜áÖêì!•ú»Î?Ïó*$fŸó‰`Zji²Ò•ŸrÁ¤ñ\Æ–ªüïòI(cµ6¨©TéU*úg€ #3–*xY¦ÑnÓ¦vº™úÛôÇ‘œñ8ñ¦—aÒl¯3ûl. ôÞ™qæ€fœëuê(PC1³Jg²äšhÇi"=0ËÝx’¹r° çw ίrÎŒºN"› uJ´èN1ñ•ä÷Åï åA²¢à³œËŸPŸûfn7ü:ϬSKð[ÍoS LªBÆ‚”á—/€œÉ~ (ÏŒ©çÔ ¶f¿k¶à/@x6ª}ð‹¢c¯ÞÂH÷ÍbËŠÀ²d\‹ÕX–‘TNê¹$®ëB)û_:rh9Ž7 =½=Žö] ‡R ê@[ j®¸°*E€»Kæí¨­P6E>ƒeTSqÉÓp¹²õ­|Ÿ©6R]3+ ×±Wºå´ž·’«ì}žO,3š›þgôv±g$“\n_…¸¸Ys³òë™Ø{„_2¬oÏä—¿P³uþ,ÂÂìR α{ÓàVì‘RVMÈ W#¸„p¡Ñû”{ ;€kkã;h†™53LR ;NbíAgý\MtyÞ@[!‚ŒÍþ¯÷Ž4ÿËÕEõ‰‘i—‰Ž}cºØãÖwzÞžæC2?§k›r.Œ½tÆ7hˆj#€q®ý¾!+{›¦… ÷”n÷®¸ñDl ýJBÓF·±©èå%3öÝ8žlM=É\”jj<(ŠÊV>Z»¦2‚Ì?I©‰ ³º])JZä>×àZ€Œm¯ÄyâI½|‘XB+ruSéÆ ¤±„ÉA6mÀìY =Î3»(‚zY¼Ê*žéõñL×hÒ)Ÿ'ó¨Êo˜ÄÆïMéïw‡öÂ“Ç HQœcUTÑë*%’œ{•|X¯èŽSê²jµ4Ó’¶ )*m¸e±³›àÙAõ@Ò¾%A·fudV¸ŒÐIÛBœ\\Ã(N]Šp€Èx€ààãÁÆ¡\E.ä:oK¥ò躦Z¶ÅfÐñ´CŒk“é/d“+Uv’ªø©å†ÒŽð½¯•Hq’éGÊÞ驾AÓ9ó¹ó<æØMìÈ$©`è˜2^÷œ,Õð=|Q`;š$1œ]*Õ:„Ç3‡ó¼+c3U`ËŽ+„ƒ°Ø²ùÔØø¯é^™ ‰¹|¿V1žzðÛ5éyBIÔâE‘ù¥Œ°‚ð-77\H&¥Ó5Zf­zdÐgGH È¤7µÐ*<->¨”X+waÆø¡€÷€ÂÝ  ÞNœRÒ1©í39EÆ}-§$™ÿ®æé"éTYHÊ'ë󦨮V˜HËœ¶h)u`מQ )eS\bJ5)A|…cÎh8?¤Rd´}rEƒ"]‹*wTµˆN ‰¨V”?ëþ®Ìlá<+^Š$Wtð„bVwÓ|3r­¬R&Ü™m½æ}€ÎžŒïS·²CÙˆè„À¾Æ;ÚŠ$'zû±C¸è›F‰‹îdÝ!’Üt¢òѧÈJw~>ºíš=/§òF2l͇Mµkæ‹z0ôú £¡X´IÜå»tʺùgæÊïÒ½žŽ~¿_™ÚPÏendstream endobj 16 0 obj 1638 endobj 20 0 obj <> stream xœÅZ]wÛ6 }÷¯ÐÛ¤J)êko[Óítk×,qϺÔvœ¬vìÄIÛì×”-’o"ÇvÒe(  p‚¯¼8RÚ‹í_M ¦½ƒãÜ/zÕ°wüËŠ¸÷®zE”ØÿªI¦ÞO}š¨”WFeæõÏzqT–Eœ/¥*/Kéßeêå*Žrb˜ö>øý TY”«Lûç +mrÿ"0D%¥ñA˜Gq¬²Ì÷¥#£ åOƒP›(ΓÜÙÁ¼HJ98s‚®ƒ0µÓuéŸVƒ‰Žsÿ’É!“·L Y&i–ʼnä19 ine†Ém¦Vh²Â¿aq«¥%±’JÃ9+1é‘Ô•VúGAÅYiŒHƒ†æ•<¨ü¯äü¶š¼·ô¸ H•ôÈÄý.ü(l>– Ë%þÝÿµG–§Úë¿éÑ¿¿#¬”žJX)[PÉò8ÒK¤(ZcdÒ4+­RG©*ü“ Lh41­!$#4y<£UÄQ\EÕ2 ¥Ê¢ e,Y’"£…„EDƒ… Ùeb²xƼ´ Ÿ&ó­>7íç $¼j¥>;*ØÐE­£vÀ&é‘¥Ê*=hRÿß^ÿû289Ö–É‘>L™‘s,™$†Ü³Ì£%T穨5¹HY¥b~[4âŸVVÌ`Ê-Z?Mœ¨Ñf¦ˆŒÅÐŽ&az5Ó£Ë%𷳤®gœÑÌ9{–0‰\>øÐ`§éSªnÃ`à”~êòûŽšEÙºìæS¤¦Y·¨eOcØÙˆ¤’¬ý¯væ†ú7̈ò°ª/©Ù¶Ÿ¢¬Ùøli·'Í[`ßšwÆþþ zöPë(¨íÞ3Թ뇎â1å¨ÙЄ”¤ )%61ª*¨%i‘l²9Xvn@÷û~K“ªlå<$4ª’œ¥Í–ØÅ Á%ÑgµcØ2©{Áø”–ˆ¼`Þ±–ïn–¨,Ï£‰Õ¤yª7ÝÚönB ±þ¡ã&t5XÏbgÎwÊ#et7¤7=©%I•«£š†Gµ#>ªÙój¤Š”“‚Ϋõ)¬yÔG/w8ñáí”™ù'Î|â'fmu2K´ªäÉ ^`Œ%ŽÛ㺎t‘çê¡ãúr:hRª˜y͘ÝÓcÁ&g ++ù‰0A‹.$T¾„ÆS*Á ôÂo":.Õ/Aiïrâ*›íÚVp¬¯$³NL½PQؽþ08´ÀJóÄv“5õÙQŽ>ÏQ—Ž:uÔÌøËw䂃©³*…2‘.Ãyë¨I[·*íý#GL.jRˬ%颮û/´#G½oÚ§í½œ®VÒW5gEò¬Ã5I4?tƒªíFkàK&˜|ϤàýÂä\ø9œvËä2 3È1i˜üÉ?á4!ì 2La̦PÂ5“wí ÖÂzp9sîŸ;ê Žù.Y¥Ú.E0Üvyv § ^ÝA—ãn¡eXØòÞ1yÀäJø G¯‹£öqç÷ c¤¢u -gŸC¹—u)^@Þ+ÄT1€ BÅG˜)#¸ŠO]FÞÁQ\a§ìIp.®ß8⢤‚ÑFðäõࢿvyE‰’£1*TœA^\$fpÄ×ö>™Š¼ë½qÔª o´ë©µY¦Î¢þsw ¸$¡- @œ“×P®ÀºZd”ÀÎ 2à„©[›ý­î‰îI$î2úŽ:vÔ G!>ÑØËCÝl‘Ë`ršq[ÇIÈÍÜØQÂéI8‡¼bT`öJ¸y„ ÃÙ„SSL;€ÚDæáí€*ŽS¯Aß»®r³¾V(7ÿ:éòžpútƒ˜öz7©ÈÓ¨Bq]ZÇô&нwç(ÞcЙF´¹ºd׆;œÅjsGqmàsW„·Oê)á—Œ;¼úÎ2„D¢Ä<ä£çòNò0 ô=v NÙ=ú Aª•r«£ú¾\•²A‚Œá’ÞBô àBÅ>¾åæ!ww±M¿bòåv:Ö7ú¦Ã¹3;qïóÕ5óÞ`íT˜q„¡°sð>%ã㮨š‡ë¶€Fž@>Âi;Ÿ÷­!_àiû ­?\ݰ6>]<õU«|ùQßiÂ/‡k£ìïy%tAs¤`Ä7¥ø‹í%´ }[¼Á ³µkÜÄlx›)m»$ëò¬,ij2aÛ˜×!TÝÊu8p%/­ÅG‹%Tì‰êêW\š‹'n8(s¸îOÃ÷êCdâMû²=5ú·+Ëkî„þ_eFX/'\îïu.pŽ,“Â.5”˜¢Jcwøü/áò„ÃcèƒÄû>+€Oíg"BŠH#™A‹ë‚? ⬥  ‹0 ¨ÇÁü„„¬W¨è|ê5>ÔŒe «¤0:*²¢NŠF&8½ø½‡Pvb¡T}}Õ÷}™™4_=7ÍãRɯyþï\ N«Õô\uÔÆ—HŸWýÞô÷?|+Ãendstream endobj 21 0 obj 1905 endobj 27 0 obj <> stream xœí\éoܺÿî¿B@?tUde‘º_ vÒ"ÍYÇy(ðR{¸ñ¯¼ä¯/©ƒ3”~\j×'à$_ÆZŠÇÜ3šá— Ž„ bý¿öŽ76wŠàãb£~ìü£Î?n|Ù(£Dÿ«pxï8ØÚU/ TQ•»qTUe\4³Š@Ä2(DêÇãß&ÓpšEq,d5 ©Urò2Œ£8¯dV“S"‘q19§Ász(0ÉòÍE¿['3¿ï…ÓBÉlŽtÆ'D ÆÕBͨ÷™TÄðj.d²=°ììé­ÆH7çúÏî?7d&7•Šv÷ÏàÅ.áËT“Þàõ#džcEï4Š‹L¨¥z‹=´Q¨²œ|eI!ùLGð„xì‰#ªgt¤¥LÊÔÏA(‹B k¡1>•IUJªõ™lðŽ)[ïïÔÃßì< òe µd½–(£8IKk­»Ô æ5&ÕÛ¤ vÕd¹K¥Ìk£õ‚Úà Dä¢'ísHÝ D‚ô;f&^L:°(ZœÀ8l<’áWüñ'¶Ä½Ö ¹_¦fP{Lµ½IRKð{¼×#8twÈpé;Í¡~šdçTÌg«ÒàhÌZq•gi'h¶ô é29cq“À×á4ò4+,Ûð$ƒq ‰>“½GSfNd™I„)¦ÄOá€Uv~J“A‰dÔfG»àœS£½P¶¾ÙNš—í!b™;Œ)Û8–Ê´ŠX[ ´í€ãÍïl ï'ÈBFP5‚&•mÈ*2ÁŒ$f±S¸˜“$JÆö 3¿ŒDGfËXÑБ\¢g°Úro^•$C4=A4Þ_¡´žs™BdgâŒU4›á}ØÀIU1niá·—dˆ›½˜|v(N,v<2¿Ï ŠçÙ*Ì{~ Í(›Œ-qö­.É“è)nN.†wæ1Ðò Èü¼ì­aµ…÷ ŽÅzÀå\"dq¦­ù%É5dŒ_Ô/»ÿÛP–e°ûbc÷O¿q¯¦9:‰W3•,J=ÛæŽLLp'•ÏS”E§^„Ó$Ê e}_豞„y7S°øPó^š§@±´¢!ò\áùã TH‘œq]ºÑ¢æKÎqdªŒ!¼G>íq6SxÜ+dƒ {ž‡:¦y6<š¶&8t‰‘ËÃDÁ×þšÝn)QVd6?=× F9bÌé ™VÇÚÅ:_m~Ò,ÊÃ%Í[¹P®÷¤œÛ§«k…äVu–Å`§æƒcUóÚ3x†˜8Eàõ±û’âBÝ‚ð (”I–UTÔÖª5U¤T~' ˜òc4´ ‘d¹º³Vlà@éꇘ¬Œ1¶Œ|FCY*;®˜¹mU#*&H¹—Ì]cÛñù4°¤‰²mÌ´o!{NóõuRý7=Ú½?`^ýpÙcÇÙ.I©[Ïc“Ú-34иTâtÇ%Jñà³bU°¦¨nù‚¶xCjôÏ>¶]¦ÙjíÊ]–§ ?w0ЇXÓãüÁ×6M$"£Jt 3pY³T¶ó'yB°ú)`Å,\o¥Ie¢ÉJi7I¯Öù¢ëy}t@>cä{¨q.äˆe:"7äˆ \ŒÚåþL¤ŸÄ™cœû`ÜxÆh"+˜CfŽ‚o ‹e‚ aUp"ñ:8eÐSÏ-oZJÚÊÿ·Hx ‡áuòVñapêËÂc#ˆqT•yÇ0’ʽYN òLXJoD {|Äue§F\Øka5lÕ×ø\LaµÖ03>F6b¿´“¥¹ c¥Hël ‹ -­"âÈćê8ê)ßç]:(0Б ´0Й"ƒ7Ž[Hê#(AU–©,øÆØ®(ª:Í×3÷ Ü!ð/áØ¸Ä9Çpá ßd‡²SÌákGðµgFp“‡`¿²eÅQòz`@+’ÏW(5,ópùd‚l‰ñÌaÍ{XóIRåQ‘g%8+˜³K£€ÚרçL$7Øç‹jè-¤®4ü%¶¡O>¤a\Ï!Ú1›, &f×p5†Ëp /ÇÆ£ÁŽFW|E†©y¸èE²=3ÐЧ@W|0™—϶¼Ór×&ù[¾ l=¿·’—؃Ûa›dúà\íóPò;‚LÆe$ìܳ.º¾Ü31ß;½4Лû‘{v§j¬¯h8Ü@_Ù>ßÇšäM?Ë‘þЦ ¿e²cD­™wß6w’8…TçU¥mWž©¿«,È⪠k½d8£\äEZ‹± ¯«8N'"TÁ«^Lsm©h“N¶Cå¥FôýN•TE¦èZ?´¾»Ì¬[>­õŽ^Þ°þ2’í@*‘2Guü›ªXò{¨3÷i·$êöÖk rvš8:óM»hj•Ð÷„aS»úÕpLÍvÀ‚–¥Z¥vå¸ýÊ £„TÊ%åÁŽXî Ä‚–ìÕl‘¨H­å mŠ5óÁùk˜iáp­ÕšU«¤e»œuRÑf~˜ý-#à’`g*aš Ð+•¥¸j=^§¯(àíÈküÍñ!ï[cIžhø3ÛÌâ íQ,Ù­_Çû. dVÿšzl¾½ÎÂ%| ;©gpìw7¡WÅfø Ÿîƒý²Ø4½B½¯ŸxËs,VxÔƒ™…ë&Í·ž "ZÎ_^N¾k•8A`NHšBOý\í‚8JÅ¡)û²>ñžïl çp,;ÅgÇ&5³¤ÊÕ/…ùäóâ®0HiM»É¦- ö²·¡?Æ1ejxҙ̸òÀù³f¿¸T¾‡ùDíâT¤9×áÏv5œ»nö¤_Ÿ€gb0ßó†)‡“Û¾!ð‹r‰«eÛsìÃçp@7ù<µ$C€c:òjwEVú•v(³ˆžÇ:˜¹wD_“"lì£ôÎ3bÊìpÞ”À x?iµŽŠx óu•¥øŠ½UÒLq¿TÉrž+òÜò\¹Ͻ¥yõ©7ÕÍô©‡UoÁ+#~{¾¿­ÉzÇpì±aßádlaÆ ?à쵿úøÂ|À¸% U†?)`‹ã€·”á Í,4a¯<’0…-—SçV2Ãpð'yZ0'𮄇D†jN2u˜Ä°²‚;"Ëâ?XòÜ¡í'ª¸¼€; Š¼T¹þ@ètçÔ¹C™I ¡îL¥ Cå‡MÛÞ•ðöÓàô\´eÇ*åuŽúåv¹Ž«O³_/PW8Õõ‰Õd›p`íŽÕmhJoê*ˆ"β¥-i¬¸Ëì7…±©]mž¦oV÷riJwú…6Þ¦½ëª$‰kG5ë]§„ÑÇ®»¨Kv’,ʨ1xpÉS]½ƒÛ>ð].½’¥¶³÷òxÛÙ §Ë ¾ i¼·o°«_¦„[XÆÃ°€{SØÉ0f>[±ühØòÔq”¸ßoÐýØvö¡&¢®¨å~‡uá1ð™@öt…6?WU)Uvº ¶ß6—Ä¢tÔœ®=tÊõ*8‡ …æ¾:vg^ñ(T|¸Åoú»äÓYR躞ÄÓåÎîÁ͵^›Nå,ª“Çò¼fQœCAv=úÑÑ mâ· uâê9E›fFø),t'Õ C³W]ËÄ›1EWmô¯z#Ttå•.ý±¼|q4þrz+íõlçYL•†YWi(õÆ’ª_aQ*³kô¶n.2¿3õuhÔ›ëæB»[YOœYúlÐâè¬o^V¬¬›—]VÑ›ê ]öÃÔI7«u› Ã<é6àºvòÈv®ðMS¸ò˜ÍÂú,Ðý:jßÝHPùžñEŒ‹wÅÀÞ»ÁØXà,­¢—ª‡ºœ“¹:Þ+C;î-Bǫ̀û…Þºä;wY‰áøÊ¦]æð]Ó{ïùH*ÒÏX¿Úú­Óô­ÖMèJLîž°Rem“XúdD ]ç»F³°,»ˆ…MÔÅÔ¦™ä„Ùooaû®4¾ÒUfZQön6óI{ÿ¦ÜìŽýý‘†žÝ61l5G* ·XXwÇ5¦ŒJ);IövÆ{¯ªa7*á U¾…•vFl,˜KZ ìÎà®Öt W ¥0æšÍæ¢Êœ2‚¢pË;èÄ‹tOL†ö^鲿ð–ck Ó(ª¬Àe© Ä7£1¿¤ïwÄQÕ–Óv~‡Á“0ê°°ULO¢xŒ—CuXç‚Æ´iœ˜üµ“R‹éÜJĪ-Jx#úñ3íÁÜÛ±/e‡;§ýt™É5׈éနvѦ(•‡Õ\ Îë¬:¾ÅúpïèG8`Ïr…g—>ì J)OÖ÷ñQTúÞ¶{ðTÞ® ÜŒRº¾oÏÌ ^ß̹Ù]¯!q5c\¼~5rSåý¯k;€ÏŒ_—oϪŽÃmçß ¶ðk3€8åI)ü}øšbnJŒ?·Å%!U‰ü¿Fü­9燡·×ˆ0üQçžfaÏ0©a¢‚[3æ×;‡Üч´Ç+àpK„/k` V7 Þ8’;l=ÝÝø—úÿX˜endstream endobj 28 0 obj 3565 endobj 34 0 obj <> stream xœå\msÛ6þî_¡™ûpÒMÌ ’¹¶3M“is×&®«^{“vnËvÔÚŽGNr¿þ¤ˆ]J~‰siûa ‹Å¾a_`½ÅQ¢F±ý·Nwîïç£ã‹jx´ÿí xs¼óz§ˆRûO5 áƒÓÑÃ)-T騌J3šíÄQYq^cMF&£ÿ/³Qž$‘.FÓÓçãÑd7ý¿O Œ²<Õã_V2ö{R$:R¥œ*ÀÄ­*B«ÞÖ’22xÁ ˜0bðƒ¯Pßðè.ûmÌðƒo¶t|"âœÁ\&Nt1\À-àÆ ¾ 1í· 3e4ù}ú¯"ñ~¿3ý›'àÇNT¿:ˆ…þƒ'>çQ¢ó ÐiPé$ oª ô=<à!ä!ä)”Ø  ÐàüûŽê§úöFÎ/”lOz|Å“öjúƒÀ¨ïòhçŽ ‚v¡Þ¶QÃïÌÛ€Ï'!>ÿy» û ƒšÁìf˜»^Å}uêÃêtÜXõàÜ»¦úÃu¸Ëg=þÚA?9蟞˜Éw^‡pôÜâCÙ Ò{¶Ù•h¯ ù&|蛎Jâ(WWnA€ƒ`ÿ9\ö‚Á%ƒG<„[|fΕã6zvg=wPì Jô»§; öP[9qt¡ØÊƒ–‹UçÿCÚÐÇ?q<<;»‹F6òñAoñ„I~Ç Ž.á2 ëˆR¡‘_Ø3ó!`õSCW‹Ç/ ¯!ƒ°ýÀ ؇bf Éà›/h¸b‹°,¤ÏˆÝìÒ~üÜØ—‰DÚß[ †³SÙûÜŽ34\cÁUšã Ü ñÛ_ê¾¥¬•– yùsx \Ù¤’ðI(Ä5Z –÷`7Èc,æ§y×!ð«:ù¾"Ù'!p| K8zζ±)“xCõo4å‰Wí òR¨è"tæÏXžƒ#ðï|1 LÎñµ)bìû âpû%ä‹8ÎÈ?ò¥·AJsÛ d€LÂM`ÿFpü¿_á²ÅÜC¤ÅBhذÕc‚×ö÷Ùýè õµã ¾ªüÎOäþßl ç˜9XBoá\ìŽpÅ›èЩbi㢦›~ eöÀ%¤A`ø{¥.Z™¨P¶ <ïÓ?vv›±Ý•oû½Þ-A\ rû-|A;±AMÃ~wm! ™¨|.à¶Ê¹xÄ~˜­û?bÍÑhËû ¦Ø¸‚šï´%œ€›9˜Á¸±ŽC_¼,XWDÃ5tâ«åëeÌc-woHƘ;øÙÅ1ΖåGlZ‚©G¡-în ¼YBW¹ë´ˆ mjwý¼[¿Ûm&ôøîaÏ8¶V±ÍÝÈ'¬bwó¡ ˵›©J¹¢Þs³¶–&Vá¦âï¸û#¸³€'Æ¡“àί Š{{ ç¯p¤róI¶‚GŸ:WñElÑlr“âd0¦ÃÑÛ â‡ð¢O˜Vó´Q–v9À ä| Aÿ&v‚_`)­ëàÝßO÷¬4Q–±ô寖˅³5²åÄDybQCµ·4 §¹ôoAæ y0!šxÙä\I¡#“ÛœKqÍc2EëÓüÇ€̹Ûáðð@Fĸ„£Ü ¾ÚäáèU_#/Ì܉éñ—jÊ÷Þ“áÐ\ˆ`“Æò͈vpÏön ãl¸0®ÞÚ¿)㺷À4àâø[H™@ö%Ý¢X¼²>š?]ÿD+Øß 66DUDô8~¾3º½dˆÁVoBF[µƒú 6Á*rðp…d“èfJgõ$¥h-7.z½.õóÂÝf/# ÍÔàbCàI·*`?¿çªÂbbñ™–3—vy^P¤(E\zâ*8>†)ÖKÈ»Q‘äûE‚&ëKý]¾TÓ Má—%@]cU•èÙT%Z´Ã!62Œ#«i2‰"Ц š JƒÅÁŒHC/V†8Ò…ns•q§qÒÄøÍ„Ý•"5zeÕé ”Çº¢Koq+qƒõi•!vù¢¢Å˜f+i–’Ù1ëžØ´Š`Ékž¿”Xîƒz-ªx½€ )Ј¤ëϪâC Ô&¬ý0QeD‰|Ö)Ž „"¡óêëë ø`3>“8—4Ä™Ešxo¥M:JSrÒyÛðªU—L‹ fÁZM'“­ª`žX ¸ v»@43®3¨)s¨¸´)ŒÚ;Äš‚PU0Ú\Ë|æA™^N2[ZÖ—YSr­ÊP/&ÈÐ?™ãw“’®íûªCɹFgtÖÒ™jƒØ/áäQYæueÊqå“t½Ì¾Dkx0ÜëÌÁ磶4ìàÒyv$bû \<3¨%s¨| Ï·À=Yött½«‹þ…ÎÄ ÓweºU\ƒøvå£3ôŠ1Aª„ÃsW~´Þ$¦t]•ªº0#´ï­ªÑr¥¨/4wСƒ.ôX»þU ÿiÿ¿ÀØ‘:~¿'@vŠ4×øáÑ%D|1|…x7^­€HýZßp¯KòEôÞ<`iê©^1J\4ÿÐuÙ{ÁÎßÒAüY:ÄVPYÇVeKá>ä#ÔÖAò ¿%!ão‡!‰¡ð!"B÷Ÿ¸ôº-'‹ï†k+ ”/%ÑÊlnÔ`(éyv 8:p—_õµ )GÇò³ð*·¯`LæšÎ]¹’L+ì¶ Á-Ó+‡\Í%¡T8!Â!äÖ™$bH'£á.. ţݔƒ#á˜a]R“U9§ŒEàœí9¶ü×ÀEð×3äU]$ÔÉxìÌHxPÆ+ ”õþ~ÈÅšΫԭŸó"LJªŸóR“Ý82‰ÉÉE“]eeÛŸ ŽÖÑnZ®js„/-óÌTGe’Æ6J«ˆ%#­ •ÊÏ3Þ†7$S•XLT’÷ŠËæÈ)yBƒ%]ü¥ý‘fð‘—Æqf•®ùüh¢"SÄÊáÌ 9äKÁİ$ábgiæ8ZŽèæ‘¿V¤m†`$?•R…²ü$ñ$‰VºwœBß2*‰•à®2e”j³âµ6yåÕyå¹e¡*tsFÔ…å7Á”8ÔI’Ãm+^d ¶¸2Ó1¹p¿ZX&õ¦H$s‰¤:AQ¶ )IÚÙ…ÍOè89™©´©Cbw$¾ÇÄv›OØÙ©.`©¯ç7ê’,^ýFÝ6µ?gŒç 8Kú|$ÈŸÎCÞRÒ°ßÀ„†ÿV‘É ‡(‚û|Ç÷¤nA?Öj‘w³Ê¶7w~%."ü¬Wy‚—ÞM¹XåáŠâO÷B\–^õ ©{ñ?wÑž–Tè·|Ńõ¤'Ý¥‰mp$…-4¤”K5ƒA Š(–Çp}­ÃØ2Ëäªs¨œ(Œ| Ö¹­ˆ–PyªÛšÄ™¨Öµu+is1W5ð®Î½ÿPʺB—*õ½Pê+âdi”–~Â{ÑuÀ1$q¸Ü1¶VžË&¼'î_ôUïPá·×˱N:•Á^¥ÏlܳÑ^v&D^ù%œgt_›y¯)Û)_·,i/T‘Œ¹nç´SÆÆ€Mdt–—€ïÉ>hP¬+b n[ûlq¢[RÆîr]I¹}˜EÐuzî"° kÏ&ž^(WtqnÜw Ï×Þ›~Ï`ó‰VnD›P ÊJÁ|áOBe×B»ÅíSb“¬,­lxÃ+=£LL)ÐR14í}ÍHî–<|N†A*aÈnTS5ÈŒ`œ7ÀzPV·@ýVN™p_Ð ]À*œoàªÞbA•RRWæ^£¥g_»8í »WÇÚkÓ>àU)ûKn/öÒ³µ Ûv¡³ï…ÎÊÙí­Ÿ[) ºÊX³®J¼üRUâr1nFàÔ`jâž›F; {`Ó ¬°C›CA¾@ÏT½«¨›VöoD”{ræ¹¼ºýoË¢¢ý"†Oz8ü˜d¦Sî}<Ýù‘þý§Œñendstream endobj 35 0 obj 3574 endobj 39 0 obj <> stream xœí\[—ã4~ϯðÛ&â±.–m‡8½˜Ù!<±{8!t7¤“ÐæöëW’-©dŽã¹p™ ÃCR*©ª>•Kª‚Ÿ’,e<ÉÌG¬î'Ï^ÉÍãÄ'¯?oˆ‡›ÉO“2æ;@éÕ}ò|¡'2–Ti¥’Åf’¥UUfE-•%*ׯò¤`YZh†ûÉ4™-~˜0–æúYwžúý|I«šUbºä=$×ÜòÈGGòÆ Ú”Òho £ÿ‘-žûÝ~ã©Ï¢}3nŽï±@ég%iìe¤â6lp öiòN;ÂQ2íò®¡AÉ´C  KVPØ#$×@‹¶KZNø§7âKO}å©#ÉÜq ÷ù3´ V;‰Øj74mHŒŽ`6Þ×^ÛWžúÂS_¿C»=@¥Æž „vÐX>K8 šÜÎ.ñ®\ÓòHðC@rì‡ÓáDøY<žU2™Ú¯¡g%Ú8ÜÁiØQwC Dîím±0ì3ìß ¶ Ä›…·ò•§¾|£xÃý,9ä/ìü…ÃJãØŒCúU ŸòåÏ Â˜Æàëyw†?m$ : CÖ]Š™p²Ãiÿ *ðňØ ¿86î¡nÉŒAÉ ©÷`ÔÙ7ºæ0nnN$3 ·™3‘fªª¿8.ë%ä~à–r î8»æŠQé¶ÛšDZäEÎÉ­bG3u-öUaÙºŸ6!©ßÓ»Šß@¸¶ìê¿Ìª´(µÿkyŸ4Br˜Ü¶³~„䵇Ø ö±«•ï”zÇ ê÷ž×Få÷£î"á^ÜqÖ[‚_àו§n=`p;ú­Aœ‹ &loRúlN"öÏùA´'¹$³㑵“» 8I>ëŽaH€ã5Õ«Ñ ø5Œ…‡m„sa„=9ìÆ-店' ®ð-xæ©—€ï ?yÍ~½€æoU¢Ó6úL]ŠÖ3µÒ õ~ò­¹e©âœ—|šZ0&¹<5^¥¥¬±U• ©L —’ªàæÍ9ÌÔ»3à‘ܘ͎–²4fÑ´ÈE–!^¶y ÏÒ\1î…v,„N|e'r.Js@æ§VdÚ­¥þ3­%äYåtÉóª°;ª¹›Q­¹æžÙ¿hÀ•0kî)HVÙƒô÷)xVÂðßæW&õ½…©¸±­ÛÈ+i úÚÁ"«˜5lس_=dØD§á“Çÿö‹?r ‹Gz1hí^¨\Q†=4Í’Žz^R,õƒõï•)vEõq•¼dôþqgQŹJY®4Š×ú¬n½$¢g%»"[q–¼§6Bxƒ+t”V©’yQéÀîûÊ&^×á² rFj3MÆð[G˜s6 ‹E6æ"›S?"¦ÝÁ³àªW:.~%`% ¦6mïüYò”÷,îLK[¹´ !¬¿¶ag%3^:Bfò\æ:z•U2o¢“.ã!dD'D©–ñáñ4œ¤Ø‡Ý°'Bý ¶l_¤påL¸\À#¾Î"£ˆíOÀ“¯gÚô1œú}àÿmÆÌe˜ÓŸ°¹¤>_ÒmöŸ¸úyÄþn>¤7ÐNçìÖ‡'% €4²ÔSØ+9{5¯b¹ì9«À»„þÁ° ¼+mÉCÓpŒ$Go×ÌžåæÓgóƒ»ëöÅ(ô;׫a5æ…þ·IkæÎÅ!Թˇ²ü•ÛÚAÿOè_ý¢ùÒÆpâéü+]ª?¶Á¾ª}@ü}t8\N–?£±¡çÙñÒàÐkãÁÕ÷ÞëCÏ¥ÙáÒìpÖѾô:œoé½—F‡·jt€‰.lk¾t8t^:ÞE‡ÿPëz—ârz—âòÂsŠËLò”¿YqYÀâò§¦«²Š3û52õØ¢ åÝGRÞÝûÑ-‘ÛªWi^jöS¯'Ü3|%X_Éú—š•çqõúŽ,rlŠÂUVU»3®-zEæeÂòM%[I‰¦"LiÕÿjÕºª•-„EkÃû1B¾´3‹L1RH§«B‰3$dE°;ƒª°=²œÛ^^ØÏæ¹5uÆ‹4{ûš:ál 2LD/Ô.]kr "KÑ>qSàì¤n5å¨ä³²ªmBB6 ’º7)œxÌö¥Bºö-×b£nX¨_už½ç\hs*_ÓGä@ÿ3ÃVîijÁÉu5?gÛ*•–\HW¤êiLÀE°I¾Ãàî„CšŒ^ i~Ð/ÊÐQFÿÙl.S%MMn…8”Wƈ¬&»¯Ú^nù;yþh[ë•ôÕßI\=ôëãî‹ÁÓ ¿ƒP³Ä$j‘*!áµvËt¸Êõ¡©á¢¯Á¹/Ÿ»²çtñAm˜\G3wþŠ’ ®á¤VÍ]0M‹¨æ~4î/ _êAMñ¼ˆŒhÍ`û*:‰ s¤}s…wÉ=Ä`}½ Oå0ò­èN»Uå¦á !n&u?rãÝôá÷4èv=ŽÄmw±pá?UÏ¥v÷DqÏ2÷ál 5€çvI͉þSn¼ëN£Ç‰“K0‰Ý@¦áÖ¬¡b% ¾+¸ÛèKYÞ¶à9ž[/0 ö=Ц-³‡{Àîk\­ldÝ©¡÷¨¹îº, Zªµ­4ãhOHœ_Ã í°§¡\”QØ#‡3ôðnvã©L©‘:'ó@â&ߘs*!9Ñ“x~!é#1|LH ö¤ÍdŒšîcO4¾òí…qw%ê‰*ÛÞƨ!¨Õ¶Ðú¿,¬`¦AÔ}¤ duÌÓY¢Mý›>#÷IayWçóo£ŽŸødãWÓ“>­Fyt> stream xœí[YsÛ6~ׯà[©NÅx oMâvÒq7VŸÜLG¶Û,9–œ£¿¾E.–ÔGC’¸3J^6Ðb±Ø {0Ÿ‚8JDÛ¿5prÙ{ö.Îæ½r9x÷k\Ÿõ>õŠHÚ?å‡O.ƒC³1Ié,~èÅ‘ÖEœ/©&A–šë4È“8Ê Âe/ úÃzI¥"î÷†?…Ãþ É¢<ÉDxÞ7x‰PyxJP` ™fY,Ã…ÁL̲Òá·~’G‰JdxÅöØŸeªÃ/}å…ÌÓðÚ¬å‘á¨/úÊ J­ "¹Âû3;dúšùD<†²º‚&Är=Û@^…~†· VBO•@±,<6±:éu™7ÒkB˜µ3évv»’j·v©öÚ©6•]»œ»#âírnø¢írî]Î}»a=œûéÒ¥“û5ÓÉ»åܘi&˜xÁ)&æUÃ5DÀY cgÀмsZÿxUÐ ^vá“–³>Fwzÿâêe»jHßs5tWóð…žïR1öÙ»Ó l¦×PB—pÛk²gê-ÄÅ…NNØ*~d. “8–. îÝK2⌠³„®2m˜šŒH Ù‘(eaG$GÖL:.„(DYX&‰ªsÝ˜ŠŽ C%)Ãc¦#©²²ð‹£4KŠe9§¥®êEa¯ZR+Ta·µŒ­‹Šr¡(æYµž „(­NiµEZGiQ“?°ËZe¹1äëE¦2N;ˆ,Jì\기k>ªæ žlÚAøŽÁêL³·tÓjÕf%²*j­3ÇRrY&Õs¬ÍêkW~¶‡JÍò:àÕoý3+ñ.l‰'Ó<lû3-B°¢ª—÷]}}ЗQœée<«ïd%HT-€X4ƒ-: ]>£#Xo`åÌú3ˆÐØF¸ãvimeåòGƒªm# qÑíö‚ö„ &ëˆ÷$¬E™Å(–ÚXÐpÜ;âDWl¦Ý7`¸Æ€µ³Y”©4×&–É"[§™@¢ß®™ÀN`Û® Ë'Á¥×êT›°‚½â™ÂþPÝÔ1ñRèêFã|“†Oƒ3„ðµ-~™¥Ym0"7hd0 ÷ [²]ÀÎóº—–”PAÃKØÈ'ró0èÜž>PÒlL‹`Põ‰,‹¶pZ1íÆ/]£Sxk˜`:wÁÓ…·†s!»3‰2øü÷š$WÈH ×OÁ¶–mÖZLKUV4w­øÜîì1¹˜Ûvñr±Á/mZ±ÂXdM#$Ô–*Ëó:sffdÓÊJu¤báÂvÌeÔ 6¸Öq$imXe»)fŸ;ž>÷S+Ö¦;M»Ïu–ªNï' ˜GÖ·^1}gÚí+&j7rµœ«†õ‡5E±yçÿÉd&¥­4,ޤ¸tVC¯ÿ©çš?&ßåsúy ÕŠyø§ÐÊ…Ê_l2Î3 L¶S•"qƒ€°š2-=WFENžÛõ‚‰¼X&Ä|>`vŠ,¯·vR­\uk« !¬¹öxàÃ} ÎCþìÀðˆ=N,‹ckc+é½Î"Swä 3kè,!"aÞ‡#öæÞeGB«dy…e4(•E©Y$U:P…È$ËL6%­yÛµM( h§àôxÅÇÍ!ü&㌶ôáîü“;‚süÏ­!ܤLôºÜk¿a›£²ÿW4}Ìy¨95Ó´º‹)™s޼îP”|ne(Já|E6Zª:‡¡»YèÃÎB Ú…ú¸ƒŽ§"¸u»‰#¸Í;7Å=QÖÄ­ÔHlA4þZ{Pà¦*à ”à›ªàûy{¨›|#îúà³hȇ$'Í—÷(C¦á+ÈéÉ÷ÐCªÝ”õ¶€ó=¦¬ÑÀ…d§7˜ªõ¶Žf|ªlê‹üýÞV£_´xÄÑ,¾Õ^pËqž§CÁÝ㌵Ë)“ÀÃSüfu7™½Í’¾ÿdÖ“8›qNîÿo`Q¯iQÀ66Ȱ̮ ø¼ ØÄJ¼¢žÕŽ|Áå·• *<"(&è9A €4AïBwâÝ¢ØÀfˆýõ®œ<Ó·Ûuùåþ£äX…ËÀj"íjÜþÎ{3u7Âõf&®ïÿ÷ãlʶ*sÜSkÿĦl3Þ— \‹‚})î†Ï%½ç‘´ãã eJr  ÙÊÚŠ¡Ý'ôendstream endobj 45 0 obj 2201 endobj 49 0 obj <> stream xœí\Ys·~篘—TfSÚу9à¹J^,Ì@™é*Y|ÜÉ3­›¼é~IUšë2©ó&Y\í|Hålžg•”²‘ifáB%U´½œÍuÖ˜)DšÌæ²ÒY¡ªt1›‹<++Ѥÿš‰:Ó…n‡F™žõ³5ª±Ãøl®}˜¹ÑiΚŸOw/#³¿1ˆL Q¤¬ù„uÿÒÂEYäezCø.m³®e]Wéën·”î(—Z¦?¦ÝÐBçMúÒv¯rCÿt¿\Ê¢I÷¨uÉ:G×¶ùãŒõ^gÍ…›ÅµË—f›Éì§Å_ “‘hIj0I¥eVW–OÌ Å?w 1U²8ØYüéC;w•Õ¢’é§Yn©jƒì%*ʪʋôݬÈòJ+eQ“Ÿº®Q¤óÙ¼Ìò\H 6Üž\-”NïÛÆBæuz7›kпÙJK?Q¯³v®¢l Þ×à/›¬nÌ·k8ÃP r Ã¥¤Væ°ïÓjv(˺6ܧlÝò^m™nØò eØõ(Wͧ\V|öì úæðÊô²]ÆÒïNË6ÅP`“±VFµkÆæ½2Tf]£ÎZŽ’ªÈ ÓäÉâÔè•SN·€eNegUF Ô¹V,[TºA40às±<=`ûŒÆ%p Öz ©Àâvð·nôkžÈPÛEàs¿ŸÍ«¬Reí„å3­,t«–Æ'±„§Ævqig(J͸Â?e…¾PF»Û™Šªøª;N®›NVÛ“bÇ74*+ÀÃT‚ïÛ(OÓ\[Ö˜÷¼a§,eÇ Lì; ±G€tvQUó‘ÄïÆýÎÈ3ðl“6Ó ¬o¨rø{¥ñ\/dn²0~­.¿¶uùÝôK²ë7|}n­UYd²Vž¹öL§qyê†Ûíñ½k¼vPÔ&õÛÃLÛur‰õölö8U¹ð}Xd‘/ILª*_¶ľ"â—®k`‡ÅÍ5£Ù‹b‘‹ÞR™¿þhn{††Ûž¹VÍ`¢ÌªOs±Sé{fe]Xß|€Jôüz úýÝA‡zÆîè­ƒvôÂA`Ä€•ì|ׯ8ªíåÆmï®ë ´¥º—&ÞxMàÅÔ°kØú@à—©ÉÎà gp‰# ¼„ÃNPölÁïòOÈtRß;hÏA¯·9Çÿ#eÇtCà Ÿ×;G/Ž#Zϳ•xs¾òÈåÜJŸPëSéƒÇ:ò);çz˜¾õ;™öî8ââ_VX,ÂsEdÝt$`ÊM—Y+†˜Ô(¢4—o£r±J©ò|DdeH™ ¼7ìóa< [+Ó>ÌBòDò=Æx`…÷ lq¶+ñn²\ËzXÑóÙº+¨¤½Ò°ûÌf±¾¯rá yà¤ã䣮±fr2Ö®xºöF³ï:+άò…/ 1„t¿<ÐIdžÍ)DãŒSnÒ ¢XF¡»¿4†QFb ðÃ*«õ\񱯮£-)FðÊÀjÜ—txEàû^GÚÖ¨}ãr¨” Uý1 sŒOÑDÊLHù44a3¼%p—öù…ÔËÞÈ^UwÚf®j™Z :è7=7îÞ1D]¥‡ôüÛ…H˜eÃÑb¬hå [-1ºq¢ÈËeëµ+3³Ë5Œüt8›ïõõ/ëÙ¯$ŠHÐ4HjØF‚¼”†oŒïáÅ›Ø Ž/AÔÆ—µÆ "ÞE}õ'þ˜!F‰+«â¼¸·Ÿ¸jgC±ë 8N·¬¡:^tÚÇÐA‹!nï´¹h¦ñŽ™üñ‚Ü}ãrÇb»!²¯­4ìI^Û.TŸ OQà‰ªÄ¢²Wú¶¡·øwÞwÚ‘,¥Œì8ˆöfOº&•;2ÍQÕAªáóÕÊÊwŠÆ·žÝ@¼¹F–ec>Žª}î:4y]G½˜ FFeK/£lVxI{ÄÈÄ—éƒ c÷µ‹Nß7£ªôÀšlTŒÉ—Ê#AåOpcwu¹˜Áx ¯of£TN¿³0•ÍPÌlînfKýX¶Àà;è–¹V9¬e[è¬}„1 ¸‚Á£%-á\¹z³w¿åfŸ`ßÑí=v»lÕ{€€‡7já°Þ œŒ‘æ˜öx4µÄ)œa†caŒúSø&6ìW0,êÊühe]/á `†W¤x2UÌ-ôŒ]os±xO ew¹Z\*à-|”ê*ÃÜ[—Î|Ù‹…»uÇòt+½Ê09ú—›]å]_ì·’o‚«.buìz:äŠÙ±B©õ±}Òr‘°®cÒyY³iÉûÔ§Yù“WÂR«Ó˜%k˜†MðŒ¦Åþ:ëëÕ"zN¸Ò}ì•R®Å b&|³bþóH^‚¨3§Ç(4»¡°;½Ð¡Å¼µFý…á~¥ui†e_;|Ø)áF•é+{±Q² ƒ2©kŒ&«µã…˜¬ù-­ð,ÁÛuKô]µ±Dì›ð%\à®»ÙR××¶æ#Êz톗bë0÷C[n¬dÖPŸ]i¼$‰‘º¤Ï§ëFPQ®cÅ¥5 [Væ±F …´™²ˆY‚‰[ë{ˆÊSwí às·?ì*enƒpîV! ÇU7ëW;Œé¤8,(´ìydÞåw)4Äêݬ©î+êÒê?YdÊÀSÈ_ÈGÒN#l(œ ä&ª&¢<¸Ó+“t¬ô@¢í÷J1XaHÞÄåHžmqëÄYËl‚êŽ'®²ô *‡‚KQ¸hçÍJ+]_ç4ÅjŽ¡Ó/ó\Ä\ƒw³aÌŠ*³¦Ê×¶c³b´1I}hÚÆ¢ÐHÈuYþ*™ÔašÍ05:RÚ­BaØŒJ+s*pµÌÁ=rH©Œ&ˆ>Ðu :$0C3Yà‰¯›©}×… yQ*d<†ôÇ¥Y6_Ìðì¼,e´sÊ*³~çV–É»uDƒ3±-Îh¯(qŸTw0š9ùeÌyÈ8¯4ùíÍ(.¹Uá^Q êÊA}¥/¨ªrkâëHœŠâzúuý™N`Øè ‚¸¦m DaÉÚñ1JT˜6.¤Y‡Â’Þ¨Õ¥…l(ÔèÅÿØ0ûitÃexá38[b7FÚˆùâÿÄôJA¦Â爚/IÍfŠš§€„2BX7öhü‚ˆB‘:"Á·þ¤ÑrµœÜ¸ýøX¾©ÁüA K½âßN«Ùn/á çáÆ#E5ÌûOB\`ÀT¶ËXàJëØ3.ƒ¾€}Ù È!˜P´{ÛÑú?Aaœ dNá '°gßccsº%fØ„0¹‡˜]ÂaØ ³…²ŽØrP~p©]ú•8b´ùŽYº®Xã`σ‘Šçs(G[~Y°‰ÎЙTöì`ØyÓŽÛ(¹Q–ï•éû:ÂÊôƒjý4u˜$Þ“îé-$/ÎfcU€3êÏ Ñ±R|¬­¶É72ØßÎé\YGÍû®ÒÜëJ'AÛ±p¯7ð©·ü”h+ùøo8ªÕŠÑYˆ’Øþüú-€}¢½)o}»±Yy 뀮?ñ°ÎÇv»Üë;§k|F%1¯`,lƒZLV†?ú0=ø„ÌìÆ}66D#)¸7ªŠ^6—nRgŽÓ xÿ“ñäQ¨ TÂ÷7Fu4<5§à$^Š £Ãч–!%ƒ· úªÎ\d•Ô”š¨kÁIðÊ;^ñ-~[ñÁ Ÿf ¤º*©5Û"N˜âm’&ž~Åq€WÈ>ñ0Î,Æ^_B¼=oÆÕ-ɺÊV¶âÑt™C3v¥ñ/¸x™yc4zõäo¹²a5ȨàÌþ¾IÁ™—Û¾ó¿@y‚p%:Vpc©§~ð§§øé ÌF±—¢¹!6 V^†“=hÐ'†éM üPÁð«_˜¤×Êz,ÿ:]ö«…0O„E›|¬<ù ~d"|^ ­f¬fa¸öbÚ¼¤ú‡=·$Ë ŸAÎZñ>U̪¢Ì+NÍÆH žÊð(ê]AQöýHÁ ¬iì7ÁšüZ×Ϊ¨þ¨ç\,ÊàëlªScþIK†´ 2ñ<§¼°§©0Ÿ5Ô»Xdâ¥Ëá721¿ }×Ã:ÔÄ ¥Sœ°¦ÍHEÓ¡LIãâ‹ð3PÿcO_È­cS¿€«be^¨ô…óbW°*¬ù«#u»Î+±Zf¢ÚîØ ¾û®}¨TUµÔÃC¥Z ׬«ö« ÷–©`O¢²î‰»lì·nôÄ)áÕVd¥.+ïýÔ;6Û'7î(ò,ŸÏVDô/Þ» ë°GÈ]²‡Yo\gþæìçOÇv´jþFì›ä55º·`U/‚‘]?–+zÐu±ó7óçß.Ôü¹endstream endobj 50 0 obj 3799 endobj 54 0 obj <> stream xœí\Ys·~ç¯Ø·ì¦´£0˜Ã•¤Ê"[6#+]y°ó@.ϘÇZKJ¶}€9Ð à›Á.MÙ©Š-—Ýš@£Ñ7ðã,Ï„œåöϬnöž¿­f›½öóìí=ðþbïǽ:SöŸö‡W7³G¦£³&kÊÙÑù^ž5MWV1+µù{£g•ȳÊ4¸Ùûn~´XŠ2«D)ç— óYÈ¢šŸ9hf ¥Ë2Wó7 •åeSóƒÅRf²®*á>Šùr±ÔYž ÙÌ…ënZŠÂ h$Çtµ(Ìïª)æ7S‘啪æë¶“’y5¿v¿ŸÑGÖ”}½%ðžØ,–ÞÔ©ûzŸ˜¦ExIgßÓº K7­¢¬çpè•Ág:IÕô‹µÄº£¦ì÷ ;¡fþŒ0\»Ý(›šÏðÊ¡e¸.ùd–BØ96|ÜõâßG_íÉ¢È % Ï9ƒ‹=†”hÖ‹·âž/ÇͦcžRèbþõbiçlRºVó_Ú2›éšAJ00bëvij'Ü †3Frýì+£ëGæ–~ ¿ž„g¦;¼íïe1œ9Uê’/‚-íaä$u»QÕÁÞVž¨Âà•UeàÊrËr`ÛIËŽgޏuÐ)“*n»/€Ð¹íWذoçÙãŒÇ‡åϲQø@_C ò3¹ròAB~¤ÖS¸ÙrN!1\0)q ™S)¢m€ÀëÕ Ù %bH+ 3V²žpGš±5ñ2;C¿þÈãCé¥`\öû-Mñ"¸[Dg¨hÒ³a>ÐÀp6W´'cêèÛ 2œ¬û=ä#¼ËÁÂO,bû´Ä2¢TH_¶ c–QõÀ0LŽuTÑF¸y{ÄåQQˆL(O‘è!aÐ÷eÉ$ʉ=¡ÒI°áW¶6ÓE*#s=ÿyaøZ£Ó)$Î9ñÛ‰‡ðØñÙöô‡@åÂŒ`kZ³£Ã=ó×?íõ >Ü;úó.†ÝŠAƒÞ¹–ìg§þ„lì@µÞƜҴ,~5Ç~ÂŒ·é§)‡½³‚6©m™¥æ3 ?Ëø·­>k(jYõ{ÓîÉó·R9{ÜØÝõ°?Fˆª\ší³ji©2]Åzì õ,mŸ2·Vñî ܸ^Wúe{L«®h¬(sà ϼ%ðžÀÍÊžžk"¤cÛmH´ïñÚAGz»‰„ëUz½Dm<•V#r\Cʰ?8ƒ X·‡émßø ÁC¼/ÃÓF-é«&°IƒežÛ³Ðm³ÙÎ23ÒÛy ºì=ÐBç³RÔÖÑm½O±(3©…ƒnÛÖaña ]eem4ö‰YÉÑ|ÞZªF\ Û¡NûVƒ´ümåc$nl¯¡½E‚˨E¡g :ÝRvÓ(sÏm_;¹·thÎŒ n“à5³lfàåœ@I̱éc·mC_î¬ex«qVûýžô.Iho»w1ÐS'v'7ÈH¼€d<™ShŒƒ°MË”‹4ÜAdlš³tÎSŠ4i °‰½³¡§*oÄü…Ð_Ù„®µÈƒ|޳ÐÜ ìÊÏz»³({¶X„ŒËAþ¤uØ;§y^6séºC} Ú¾à›rtÐ?@»¿:h0C:}Ȇ‹•(Ó4^AýôÓ?Š@ ÜÀ!˜2[ÁnWpचe SÔkˆa¿ÞÁï|Ñ2“jŒþ)õ nÞÁ^×pºà×kˆÁ­}°Ã–ÃÀ–‰e=¸X¿Ÿ’=FÜù X{¿?ŸŠsb†cL„yï™eŸ¾€,ýiYöùÛffl9nÖ*Èjh¥{“ð;+Gò¬”RÖÒøÌVB²ý^ ’ÕK«=dÙdÊÈ|k"æ™6kçÙ7Ê@ëác«SZluaÝKÂV²ïªÇ\7ÖΆÍm’$+óF ˯®É};Ôyã}æ=÷©çMë¦ëJ¶ŠÑSMQtALsÚ‚»í «Õ®:d¹lÚ Œ$˜‡¡¸±‚òÚ¶o2]Wªå_§:Ãy¶¨ßuK‘ªfÓ8áÝŒ¡%ªÒbuVêB4®Ü*g>Nô~yeUzÃ}?gí:jÔfk{rÞ±32-в’6­B];vS…™Eé Á4ŠC·–ïÝØº*ª9ð{GRrZÚÍ#æ‘¡Ö’bQkØÇÙH¥û¨”Có®ÏûÕ…‰².›Ô>ucf£Ý;ií,ÉÁ¯¹ mIë\´.Š76ˆòßAK¸7ëºÐ™oD¶IMz;ñz‘Ð4×#Õ¤c°†&/# ¦.Ì‹ƒ‡›Î]Ñe–7…$mGbÞÕîdÒêWD œ¡ð=% e´b4®ÜeŠ8Äþ7úQ 5êmà,ö<§2AŒ<<®]Œ;òbÛpv|` íÖpÓY¯’¨¤¨wh½]Ï(<"΢Ê \ ŽÃˆyží¿F©ÀÿÑ‹P>x»Ûž†JËšùq˜vŒ­ð‰b º™íÍÖžØ*7ûȨ0fóta]yD3ɦՖäɺ¦ŒŠŒH8‰äE…P´'‡V}Ddyí¬½cH£nÚ$=>Z¸T#á‘ 6ì÷dª F›CØëS‘É d]'’?0ÇVÈÔ £¥ð¢àE—´ŠGßR6¥²vSÂéž@\pÓgwí!pzˆ2tl¶ÑZu[žà¥íŒ”ºñ¤K=83ctþÖ !>?‘´‚Åž¾öË&Ô‡Çk6µ‹šO>ŒÛÂ’„5çü ôU`'áÖ£÷ñ:k5:.ŠNéòÜøæ ‹M^†œreT´z_Q l¾A <‡'»Õä8„êÙ!ètáŒ6î†ãä«”éøÈ(-¶¼à*˜þê7N5O°¶X®õØq c+CF1ØLe´©ê9¶¦.#Ž‘ÊúÄd-3ÙãEo ²-»ðX݇Ç>‘0‚èWЉ}isb‘û§Jkák`³Cƒ ;I»dZnßE&‰¢ýë§ã•_“{b"fj—C£kŒíÝDOœ´Áã&#Zo¢QmÅrج¥Ø1Òÿn‰ã¦ Ý;Ðî/Z;èÎAïtï ¿%"¨8ÊŽƒˆ8àˆÃ,¼¹xqQŽŠ^ÀÑØXLó=-U–à…`>RÉ3 ÁÀ/4%îP¬¾‚*”Hû‡ÌŽ=›ÃÀ*çÈ2 ’¸^a ;7)y´$YÞ†Ûz¾`“UµáC~H£¬`.KN\渋³ÁjM ¨x¦j»ri3º¶HÇ*­›”, M2E8«!çxdN £®O“›È÷y¤÷u ²K­$Óöûä,¾qâsjúŠÀ7(OŒÃ?Ìôè=°2×q”Î~ôšRλh]…c‘I­ÈØÅž‰óXÊØržáB!r Øé?™f;tHx@çf¢ˆ% êÞòõª~ÂÆèËW˜{„+‚ëcÜÌÊYtÅ?€8î_Ñ‚ÉüJ6}›nobÖÔ€pMEÈ•ùש¦~OSéGÅû䇟:—áSÁ>] þxñ¤¸Vºt]%x¾­¾3ÐcçÊ–ŠËÚ™BX§àry¦X.v_A¼XyD·ú¸ÞÜ6òÛÈrð/ \ƒþ8IP´iǹy_ÔƒF#fù09.&Ì)§F¸ª@L‘o$.ÁÃ:_—`šýÍÏ!±Ìݬ/´Õš•4ÅÙÉëOD‘g´Ê!Ù¼ìn0›žkbú•äÉ@OøÏü#›ÄìŒã:ÌK"3Ép\ï’oÆ 8)“pa#O˜û¢ äMLůr]8è’h»MÛ«¼©'`¸iUsbÞ’ŠæG|oû×pˆdåì.AäßRy³¯ ‰ƒÉ€]5,f¼­ØBÌTnÃßø'»WÿWû‡ìš]»€}pôÑŠŸøÀ;N†›Ig}¸³ñÇYÿϺüMÏ:s àÝÖ Þ —YžÓ¾*õ%n"ÖHz£I¹²ËIOÞ¡Þú†{ŸÙµšÿüÕ,û©XB@ªJ‡€¦RÑaÈïîS/Y†øw[DX6%U÷Ú^VÚýÎÀ¾ZZZµ"dÞ+JÛ>ê05^^‚dø -ê^ºøŸ¥›bAØ‹ô¡ÀäšâÉúAøÐ+ÓóÈ‹OÙÏc[2bU5të<‚'‡ØU³—Qvk4Fé/÷õrœVû†À/Fh†GÚvJ õÿÝ¢šËKòæ:«j—føŽÉÊõnû W»r”ô½pxèÅ.\—+ ð¥\®1’þ@ƒ±se]îe¾í+#O“P¤4â-èK¿~ã /D Ås`òDFš|<†¦¢R#*û°qü¤ÁÞ'ÍKŽ%Ѱ9pÎG®76ªØ`Ìr@ÏäŒ$ Çí' w0 •FLUuõ´‰òi¾¦Dù7 çuTÛ䙨T="@Çï ‚*¶dIlôJ…³k–¾0Æ>‘rª™= Õ*Ò¥Û«U¯8 ‰N¼¶Ô[–ã%ñ±Á‘|Âçâ¼bã„Ð÷ƒ›Òç}Ù¿¶×ƒý«&ÝÛ#bÓÃYCX°ž,¤ËyÑlKUè¬É«‰Ä¿áþÒ!ÚÝ‹ 2ÿÐúg¯ìîHñhMÈ<Çe‡÷¡Á­c´{˜Ù¶¨° n0´ÞÉjñ_˜] ŠUñDzîÎ.Upé=Ф [sª´ÒcÅáþJQ°ftü*š½hÊ’ø5‚7QiVÞk|ñIù JœTwý!ýð¥–ämš] GÇnÞ Q“¼ys…vrº¸¬ß× ¹½»pÇŠ<½Û6øÎPç# Û9¾ew²tPzê7úå¢23v‰wáŽ.Ï¥ Õ“.>«¹½Ÿ´Â@X°ºóchc…×»Üc‚–ñ[o¡€§-a%ìø‹àÛò³ HŒ±s¬O“ÜŒ3nM•™ȱ»²çY5á a¼O|¢·%Á§zD|l Ûß|Ü•DüŽÓl£ƒ"¡ä¤a³­=×v{@VZt·>¬¨½\U ¯ž=ñ’oâYT9ɺDÚ+¸¼Í$ÁYHËñêð-nÅ´/˦¢°æ#íðe}gåÉ¡­Ž%À$ŸéÃAÙc'½F æë*ÓeX1‡ü€÷ŽkæñûT|Û2 ,ú÷‚/a·£‰Ha)ùÉKíqqå´M^,ƒ\™©Š*ðR[ô9$Ï—;íá)Ë<öÁ€ø‘ðDÐu¸¹–¤âpá˜Þ,¤Ù¾ºô}½ÀÅ‹ Šm®×0†ÞkbBêŠ9>ql÷é#'üýòzã˜*Ý–mƒ«~ÜéK ú×Ψø•ð½ÐKЃ"_¯&±Äãns¹ƒe2pñ$NA²p)~Ø/ùÞö.u”8ˆŠ‹®RÈ¢4èË£½š?ÿwÞqendstream endobj 55 0 obj 4388 endobj 59 0 obj <> stream xœí[[wÛ¸~ׯÐ[­ž˜Æ…Éô)iÜm¶ÉnꨧÙ==²$_ÚXr|Yw÷×IÌ€ú(’¶·iOçøŒÉÁ˜Ì Ã/S‘H5î§–W“£“|z~;ñ§'ßÔÀÍùäˤH´ûçpxy5}=·•ž–Ii¦ó³‰HʲyEUN¥PÓ\Ê$-¦ó«É§ƒ³Cd¹Nþ ïô×Mô&@¿ÐG€GÏæzž½Ý;âí Ù”q ,dš¨Ò?Êî1=¸­d©.Üø@à”@6ìŽÀ|zÙ7Œ!,!.›ø .òâ. ]<ñO®!±%ŸøÇù·¥‰)R«4ó•Õ“ ½ß@Z½KØB Ê®ÆÍ{ØL|(U¢M5;IüèÈPÝ| ÍÞ±H#zVÓèx| c°„9 ÆjºêS§eÇ«C¸€Äðù¸î›˜mè >eÃVp¶ÏpØ©ézf„H„îÝщ”ÁšDH™• b2‰ÎŒ:è]i’Üd^זּXöÍüïg,§ów“ùo?9AJ‹%²<Pinçl éÌŽMU!®f‡*MD®óƒûðúó,µ.SËë0ûexh9içÒYɉö?g‡ÍTZ‰ÜÙ„ÌmÔ+R vë°–îׂP7¬æ(t?-þp@öœ¨DEš9 w ) {Rt"Liuù‡¤Fûd¼¹¦5´÷ì2Ôê©°Gˆmç.Þ¹UéhV†pAÃØ l{Ê(0Þ¬‰yç^g”.’¼hTªÚ¡“#Z- Ì²~3Ô—UÛÿJÖôݬÓÈÈ¿K‘z2‹Bn «­#ƒdDžH¯¾aø® µ£.ôËpJØr]ApÍO÷^ËÕx Ëå·oÿþMtTÙ^‡Õþ%@Ч‰½  б9WV¹âܽ¨TæIñäî“YÒ{¸{fO!îWY ñb”©Eİ}Îe 4²SÈ-¹’ ɳókÊ•P±\Çpíý"°ðŽ¹ÅŸ!&íÏÂù«àÑyÞÊxÖhÅÛÖ'P- ƒ0¯pèþï”xß)BÜ|µÃ×!Ü̾7y쳡Ìî_CÃØN~ïp‘²Œçàçn€¸1å+31ŠÕ뜼z¦Gà à܄׫]º¸Kgy¦\nìB0i C˜÷!,_ huê_ç2åa;£¾é ;‡Ø6W´s—,À~Gd\¬mÕB䪨©"ÏŸ)Önç,–Þa»ýÑv@8§X{O\í ¬ëX\$)%‚ûBl£…¤»Öc›ÿí ;姺T²{LáSÃ2°{PÓZpi s{Û›>V³ao <"ð{ˆ‹.{Š—Þ[“Äå¢1^ÙäÙ î¤Êæ§Ö´×Ç’ùˆkH+D§¡ØP8¬ÔÐQÁgh øî¶|à#à×.A­Èroù>æb¶4{?öžTN-5wo[¸Ä=4íO3‹Ÿ _¯ ~TVóFª:ZæÙ°Ãk®yo¬¿J(Ψ´E©")D8Yl^½7$™WÊhlÛ<¬¥ö¼[ZÒâ·3$àãî™û΋Kz‹=œª—ۊȪ°Sà TÖÄEà T®Z#Âek .4'6“\Å«=d©â;¯æÒ&3-ÍIÝuŸÏÂm­.Y’eY£.l…÷lÄ|•ç–n^ÙãÌE¤†ELŸX´¼ äaV&y¡óŒÅß; èb3RGÁy.]FUº¥ÕTœ]l Cý¶mYAu¼ƒÚäËæp;ßI;„<:Ñbj ‘Òuk™Ö×­&³—Ù4Mµ›Ü•« Wl<)MnýPâ$–•B¸ÐÇòØY2ç- ë\²¨là)”ª%+µÅ»w‰€p|µúbEPkìokš™Î3i]‡U©<ñ‹:Ó8ø!$&LdÍCypHÒ•hxmÑ×™5cÒÛ‚g'ût†‘‰”„oò)v,£•Ó@UÍórX&Ô6 ײW¹Ú¾‘?Ϥ‹hUl‡›LŠEï5$zM\ÄF2J`}†ÊM$³TÜeTÉ•»> ƃ*F=fXcÉ( 9®3±'z­ÉE)Á%’»BÁwZ«'[ãF¹±ÒPe¢¤âÌÄ"T„Bœù2†‘å_`ÖwhhÃ÷TJ{<9Çß[dËñ\Û;ž7î#!G0…àkc°ãÄ<ÇE¥2©ry…'‘ɋĚ–ȇPzKhqÈÛ¬ƒ·ñq›Ý‘dÎ`Éé4ˆ;‘DU üǬn‡¯·­P¸m~½2J çÉÐ`\¦S¿ãæ&˜ƒ¶çò¿°d{âU1v›Éí‚,“T‹¬1Lz€@H wÔ™6ŠØ0ÞŸvß@ —·Ø<%9;‰£H›zr—ƌټ $Ë@(!×W'gP›¢Ø³Þ=ÛÑ 8udn[–Ó32ÔÛ*ó“ÌoãüÛQG˺‡iWT±cB˜s§c°ë]Õö1¶Ww”ŸÛ¶a ú¹PgáÈBaY0R‘›ìQ¿¡AK) S2œ=F~‡-aÞkÎÆëÊ"X/bB‹Eä'AãF›+ÑÛ§;QX~Æd!*ÛÓ)ró‘a ï{ÏúN€¾Gpœ]xÓŠ³L^–»Ñ¹ÊP{;a¾pÃjö½ETDAD÷ž:ØæÃy¥nýÆÃV ¬í¹tC3›ÙyóP>32œä—/Q*Q¹§õVµÍe㵸8Ú.&×¥(8•Ja“î÷^´JJíãh°²Û‘ŒëÔjceã¹öìò|qÒAž/® wi#NŠÛë¸È€ÑÛÞû©ÁS»<*£Ø!¶–¤èÒxŒ¤èºØôZÂÍÜôqkã²c Ÿb6ãþ®gT‘Ž>%:胆㈋Oë¢[Á¥²ORð2ø*~ùÙÛ§ô¨~9dH¨“ádêlÁ¬®Ëôá‡õùZéó/j°ýÓˆ½R7‡ŠGíʸ+Üþ€%ü²ˆéÓ?à0vbú&fÄð'2­3j–röFô¾`ó}>p (,<¶²ŒÛÌFâöeü>pC 7˜˜á­³$P2¦ô7 n‹Š7¢K;õkȇÞΠ1íK2[£_ŽÃ=#‹“Øw¡Ly%>›¯f so·¶;•û­4‚½bõ×-Ïn@é€uHÔë"\FVD²†›ƒóƒuЫmwžU Jªø¬Ð¯q–€é¼îSN€àл÷ËÅ6*m¡ŽÄq¸·oïÀÛe€.Dj°Š=ÐüàHôr—áâ úéáþžËg¸åìs½ èáâ#SÉ \6æí£¾:e£YÈN “ßkÅ5£°Ì;-:­23dza_û{ÜÛ?Æ®Yå‹Í}ÖWÈÙ¹6¬:|àW ȯDe-©¬áÙˆ/|COItuܪwÕuöž‚ñuá¾vÅÝ›©ª<Ÿ&eÆ>Ôf5:tµÅ–»¥{¶„}Ê·=âK‘1·dÖÁ˜Ä¤Y^úf4þê-Gp¾ùÛÃì)âVÌ®o½ØÛWË&ftxWV·‰Ð'ËÇóÉŸíÏ¿s¸û§endstream endobj 60 0 obj 2947 endobj 64 0 obj <> stream xœí[[s·~ׯàô%dÇ\/€]\Ü'Çv:Ê$Wf'Óq:‰”dµ©HT·Óÿ^»‹sv÷[.iÉu:SÛÇXàç‚sžä™“<üm€åõÑÓ3¹¼;ŠÃ““?ÖÀíåÑÏG6SáOàðòzòõÂ/t!3§'‹‹£z;•³yži)¥•Ó,ÀJˆBS50îç»Ìzb:™Í¥v™*ôt1›‹<+µ°Ó3a2§‰á£Þëà °Ãމ IdD¹¾N§þs‚Þ$è/-JvG-c×såó ¼©K8—±â ÒÏ„•èñL÷«DíIz cÆÈÆ+΃h âŽD¶¨íÕw«†L«Š±«)ÜBØè\†ø{È.Ì#ì2^¦©LåžÀ-n 2,½ýMÄȧËLòü!A¯ô7 mš×‰áúrÓç;ÈÓ dÎNXà Ç>%ð8÷“î,EÉ3 R6|•Òq–y2pƒj “nÊÜ®B¬ë$ØÕU„N•|– _Râ kݬý´ŸóO•Esú8 VA6´ë‡™ËŒõ¯ð=«‘” 7#ëöɪϓ^ÝhÓ«”TñQ¡Ïq—€å¼SNìQ¨Óš€£ŠS°ì dŸ $Ž-àÞ)øº_— z— RƒU[ÐÊo wÙ\×>Ü73ž3³†å³¿å‹YFÁ|„‹8ÚfØ5¤` y{H‚ÿ±Kø^6º.¤#úµØ`[q)v,ó¶k@;5ef:ÉôŠë{Ü*‡²ziB}F£¬ Ìöf .²ÑUª C¿±†~…qì|&¤7üþr²Á Ü5öCD!§ LC…ä´ë(Ø]á¹OøÆ±$ìOìÊ¢* ¿m¿ÓV\ Áa—2T~¼p-ü”¶]w…ÔëŒÎtQüTkïcŸÂl.æp/†ÙQ^gÈ–4zÊ™‰@=l¡F°ÞrÅggÊâZåÿÕò7î×÷ÝD”­>“ë´™”q™¬Zƒ*´¯Š²Ô1.ež•¦ûÆYm‚j«ÐêŠÆÄxËéò"¤¬ÚçƒBFB¬ÎÖ7#²:ä0 ^¶¶Hñ‡&lYµÒ°g¾õ;û?±µC¸m&u^›'§ÙÙ&­ïÍvkB°ªÀ+ÌÖý4Ö–´¢¾¡Œî-R]Ø•ÝêX­ó*þêÑ“G  ×%ìäR4HF‡YúüÒt×­²õî/Jj†q…à£_©gv/e¬k¥HWÙÕ©ŽdsÓš‹·`æ™uØØ…ÂËöH‡¾Kù×ó™(³ø[«½&”™ÕyÑÜîN2¸Žïýoïdix‰³gfkÑ{ãùk“f ÿLç0%ê¹ì¸˜Œw®û~½íàR^QºÜ]¬½·Xu¾›4á—™ßÊäíŸF eä¸vÝËÈÈHq¹…âÝ' ëÿ‰¡Äàd'ÿ Õ¿Á< ›VB»Ýõ ±íý´ãµüP)ª,¾¹Èí ¦— F‚ÆìŠ{U©fïîK‹vÁ¦¹–çú¾ëžvß(Œ:õ7õáqnø­º\Øq<ìo›²’ͬ”M\4ÖpÃ-Gv»ñ„Þ; NdÇ0´àןíÐp4 „±Ð!Oh°šT4hAóèr@|‰´¼-ÅÚÑž<' ,°6LÈFG{înÞˆ½[© 6âM ùÉDŒ…!»®ê£'_H¿ˆoj*°ä-kR¦ïš C^ôжúÕâèOþï5[vendstream endobj 65 0 obj 2788 endobj 69 0 obj <> stream xœíZmsܶþ~¿‚mÚúç$²ý¤ÚN«ŽÒ8öe2­ÓéH'YRsÒÉg)n¦“ÿ^$wÄòå®'O2Ó8V ^À³Ï¾àÞEi"d”Ú­°º™}þJG—ïg®9zõ§FØ^ÎÞÍÊ$³ÿ¹*¯n¢?.Í@™EURÑòí,MªªLu=«ˆ eþ®T¤…Hò2ZÞÌÞÌ£x‘¥ÒüÏß1Q:ËçW ]ƒô¤{"vûOV m@zé6+ ³ð?–1{öjÎny23_žÌÌö”ûkùé›ù2^ˆ"Ñ¢FµÔ2×ó "#eª(ÒÌîA'i*Š‚ô¼Žs#e•Û„™X‹¼2šÀ 2~…ã70þÁI™LµÙ ˆü¸–U…~9ukgªÌè,¤Ã[­ö¬ZeOñÓˆðû-Î]+›åü&^ÈX|ƶ‹À=}„<ö:¶cJ•Ó‹@OƒÅY6Càã²Iý6,ºNé–Ð_ÛöcW.Q•©¤™óž]¬q™dÀ¶+ôÞ幺,2¾Œ4Ƀ6†?VU k‚„ÿR¶CÖê²™úv}Z•Ÿöó™À("vñR„ò6\ºDЬC ¶°««bÒîæ@¹üX9å¼'ÉÚ]hnލÁ†Èz ’éŒX÷ ¬sTÇi5ÿÖ¨¦’²H=+`òéQ@x¨AðÙ)ü¸üн .xê¦éd¤ïÛüY$§Â{8 !Þ -Lö 'ÀØG }Ëx…ÿ€t ÒOL¿kÆÜ0þó(§Œ_ºñ#IŸ+ùPçóZ l#5¢¶H&%½ÄúyÞlûòûÎódo¡*¬úu jÀ´hmœOe.º˜'¯~*lØd˜ÆyuÜ‘4û=x8컊 -ÐÇøªóŸ•™á±ú->7ó&&Ÿ/Ü3‰’i¢ŒE=³­¢Tõ³C)DU–Žàš®[ -´ÕF™˜ž¥-E¼å©C7ʬe¶&uVö*3=ŒÕÑÎ÷±“¹ßÒÕù\£ä`Þó{ƒ¬Ð‰û¹”M÷‰¤^³ës4™¶Ñ{á0¼)Ód^Ƴb˜˜\˜ONéɧ¾”Ön8 ÌÕMóoÄů¹@êìé‚Õ›$S_àµ`¾GFý›† °À‡¸²C^Q+Æ€g˜-dh¦S=ýxèTÐȧ»$žá _ää3î¾×Oš½| Îküá„@¤=ÚB­“È•(Ã^Ó-»Öè s|Œù‹Ô/œ…*C(–Èr«AÞ{i+´zŒ:Fl`Cò Í6:e½„uç¡Æ7‡Ž¯äkë|Tÿàߎÿú7rÌëû>Å[#é[ôü3’Ï…ëNaÁzÙ€Â:Æ~Å.5ÁØë‚¡H¤,ZŽé3ûéWA¬Ãû¡ÿÓmÐ0ç#¡7ÒóšK¶> %î÷LKRî³àûž‚xè­À“Cµø""OAÄ­½Ã¾·’µNƒ©êeï3:ÕLåÐÓË+æ2©2Ÿ“Ϊ¸ï²{¦싆ûÈ3Ä~€åìO‡¬"5Ŷ6Gˆù˜Ea·¤sÊÞYÇHú½¢2QK'ˆïû5Îhš<’wn¥jµa”€äÝxò{ Uä¤>g›WoãØ+Êu´cî;‡ZQP™öô0á7–#›|„Âh©°!!‘?g#ðŘo0j OÂ÷]Øu·sÎîlBycÄßQ±Þ¥jÎÜþ:T†a£‹rV,3ñ^%ièȸ,kàÔéÔ"/„á±× p¿X—5^‚ô„¿Gà¢Oö¹8ŒÇ¾ Ð¦ß 0¾›tÕVñ§ÃxÌZòo]3€:áýfúý™ê·¿Dõáð·@1ø+H˜†c t‡¿¶'I,m¬õxÄòÝ|ºþ!qïÚVP(ê!„}ÿošî™ ðš*FSÄħûÃr·÷ffcÞYgmˆ×˜ÞòÃäÐm=~SͯÒÏ"&ßÑ=ïêþb9ûÚüû/íHûøendstream endobj 70 0 obj 2688 endobj 74 0 obj <> stream xœÕšKSÜ0 Çïù9B醨qâMß¼J¡<·Ú}QàDÚc‡ïÞ K‹“•í锽üñ8ÞßÊŠ$+¹JóLÈ4o?wâä"™éôüWr3œŽVoÅÏóä*fEûw3€õÉEºØŒ/”EZgu•6gIžÕõ0×WiUŽÿ¯ËT ‘©aÚ\$‡3gEVêBͤ³©„Άº•E.Ç“–¹™ZXJVíL!EžiÙq½½”¨‹»&d»Âq³žŒ9å˜s#iÎ 8¤¥QÊ›´©Rušq@u@“ê¾&çJ£„7©)\&Íý@ó ¹Ó¤‚Cš4iî0i (ýeH@I˜ÒìyáêQAŠÜsåGê™tF¦Ò´ Z:MZù‘V!I+ê6Ò~€:$ &Liö|È2TÌäž×~¤þ¡S’2é“þ ?ÌÔK£`ìt¹Êu&T×Jö¢-ç%H4zê´óÓÿ¿¥~ÖŸzÏL]2jޏ »nGNíꌻÏów•àœjÖÝ^„‡ï /Á ÿ2<{WùËa§7þUøßfê¦QkF-ã…¬±m££®‰+¬‚ÅŠè·AÖÎݹrdJŽnƒl@^“—Q…‘vZë.„w (Z˜®Qt& Å˜ìÒ›*ëÁ­—bÂ3ƒ!‚§‚¡1ürLvåÍNÅB0üJLøÒÞ:´Ì¯c2WÞÌÖÑ;ËjLvfÅ‚ØÝÉçMxxmM úZÔIÂ~-&;3´#v*´ƒá×ÃÃ+_xå€o™ßÆdfæ#Äìl3mÄdg¦#Äî®Í7cÂ3ó‘"$eø­˜ìÌt„ØÝý©í˜ð̼„àÉ–ÕNLff>BÌÎ.ÖnxöÒ㟠ݭQLxfHGðΣƻðìÒRS²KB’†obÂ3c;‚·b{˼“™Ò³3¤ïÇdg†tä!Èç g9ˆ Ï éÈðΣÆû˜ìÌÐŽØÝGáá kŒß¤#»Gc2û7!ý0<;¤£iœÅó9ÆÑQÿ R¨Ü ZAgR5œ à8¹E\áh•Þ˳wð@ …ÊdM@Þkzîƒ\9¹õU·ÈËl›vñÞw¢ã˜N4M¨÷t¢O‚_4QSß*è,þ ‡ò¡=øK çÈMëzp9±×¶“¡¯Ø!ýbô7$‘›Lád(R‘]¿ÏáKYc}œkšMp=pÀwÎŽÇÿLÔ´ÿÌãºÞ‡¢<ŽÞx*ºÀmùµ¿‘~þÒû]V,1ûÍ™ùfÕ̘ɪ÷Äšù–ï€ï~¬Ì÷¬¦g]i’Ýñç†gÒendstream endobj 75 0 obj 990 endobj 79 0 obj <> stream xœÕ—ÉNÃ0†ï~ ßHA5Þb'WBB\¨Â©âTÚ²¥¥-ûöìÄ]ìTi«’[$—?–m}ù5ž0%ŒcjÞ…èäh¿¥q‚¦Ã¸u<ã>¡„óLʺ“レXÈNIªpÖC”¤iBõlW†U\|§1ÖŒ™à,Gí¨Ûh k!#ÜhrÉ4I´‘‚òb’,Kj§*«â¹âÊÌdœQ¢ùšõ%9˜-c©ˆ†N–F» i6»ÈNPÌ äS”í¶£ Zy„– æ>ŒY1úª´¶Š†0úÆÌ‚}SZú‡6¬·0V"(î`Ì"HPä0hÂèŒ9L= ½ÕÃzcõYGlPŒê0ÇVù,#.(Æ0è¿IÉÜA‹FOê0óŠòkô Ú[n6¬0Ö )ù Æ&%?àƒ´ø/0æ0-þkha•·ÜlXß`¬>[{ïÛ3»ÞÉ5$n¬»D¯¨&L®Ûé÷Âãc{üs;õЪ½J ”käò)ÝüÔk'Õ&ë?ÿ·õ_¿o}õTüÐzæ¤XuB/+žI{÷ÚæØ­º‘û,@ÓØ)>wÐQ†ÎŠ÷ºÒÔ7endstream endobj 80 0 obj 436 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R ] /Count 14 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 83 0 obj <>stream xœyXT×ÖöA˜sNì2Eâ9¨(Öh,Q¬`EA,ØP¬ ( H/ÃÌÐ6m*EP1V b!v½Q£Q±]K½Æ¿Mn²N¾ÍýŸoŸdææùòÿÿã#0{öÙe­w½ë]ëØQ](;;;vVtüΈ°âߣ7;áã.Bû (Œ–ô§¼ÇoïQÐÝu·GÝj>:ÊQ(ï{ARoÊÞÎ.±°bVtLÒΈÍ[â܇/^>lĈ‘Ö‘O½¼¼Ü7$}øÆ}vXlÄæíîCÈ»Â"£c¢Â¶ÇMqŸEfGFFltß™³%Ö}ý¦Ma›ÄÇ–­ Ûæî½Ë}è¬aîcÇŒùtù160"jC|¬{@ôöh÷î‹Ã6ÇG®ßùƒE­]à“´}㊀™ÉÑ›VΊ [µpöŽð 9;7/òݲØ/.bÉÜø­KçíÚ쟹~ÙüĨ ˇ>Â}ä€Q?4ÚcÌàO‡ŒM›>.}Æxï !Ÿ­ž¸fR¨×ÚÉ릤LõLFQ£¨Ô*j!5›ò¢>¡Q!T5‡šL¦<¨E”/5…C ¦S~Ô§Ôj 5—KyRK©yÔ8j(LùSã©aÔ2j>5N-§PŸQ#¨T5“šH¤VRÔ,jõÕ•êFu§ì¨Tª'eOõ¢¨Þ”„êCÑ”#ÅPR*”⩾ÔZÊ’Q('j*åLM£\¨é”+5ƒêGySnT*õ1µšêOm¡ÖPµŽ`€,³ˆºn7Ýîb—É]¾·_cßwXëÐ&Y 9F{ÒUô¯ÌLÆÀüÆÆ±¯>šóÑ•®ã»žîÑíq÷Ý=Üz”öôè™ÙóY¯’Þν{?è3´ÏŽ>7—9ž”öÆIûzõÕÈ8Ù§p§;ÎÓœóœÛ\¸h\ô.'\þÛÕÍu‘kL¿ýºyº©?öøxKÿîý“ú?ãFrçø¼–ÿÉ]áþû€¡Žx<Ðeà¼gþ:(qPޠ˃~óçQîñj0=Øsð&ámOá-2 ñ&;˜Ü‡ía“`”ݧ«´š²r…&‰ÇÁtR–2-]§¬â´ÇÏ?¥§‰Ÿ?XCWªôéiJy2‡ÇãCŒy1ð4A‹ÉN ` „Û Þ$»ˆ=%6ÂÚ¨Õ”—)4‰|8W F ^G')È>Zñëî4ök—DÒIrò„^AWéÈYäHSésà)“ gÐ@A‹¤‚&;ãÀ&¡Òd× ÓÀ &ÙÃ1§ÙØC‚¥d[EzºNAI#÷Ò–•)´I¼? :¡RRn]ÛÓÉ–Vòh<?Ç“à¹äK› ¨ 3´J#oÞ’\ö…)ÆäŽ&¨mr–>„)™·xqV_‡_„Ò‰–U<8[¯ júˆIoØm`¥'÷~~H_ïö2*÷&îßU¶E±E0ÖÛw¡+-‡Hæ#@C?šsy\Àæ„‹8rˆäÃrŠç&gi½°Uè.[lYu bq¿#± îûãhp¾y³úX+ÿ¹qw*cur]–"/?KÁ­™;‹Ìì2ãþ¿ÀþAtyvwý²b^z;¹P­,w+Õ茼´þ#½]¥ÔepÒúTeVÿ5Ž€gÂ$G~û‚—³ô5<“ ü xEz¯R¯)««“Eëvâè$GáÕx ¬–4‹Ö-+7[·‹h]¢uÁ§ã Œ$É´ôµ8ÎVoN†­à…·Jæ[=®"vE'ñÉt'ºïš„^Üaš >…ß$ºöxo<³.!Ä×Z5”Æ£ñ;ü)¼“ ј–.ú£¶=> ñi½ùéDÞGˆ×Ò0ÿ&±Àƒ8'ŒÄVo1$ LöäY° ª¯Ð¯Ún?ÿzÝ鉇ù+‡÷×£³ì3Ÿ¯†sØ`Ýì ¶Ž†¹¸F‚#0VÐ[A3Њ´_LpÛd/ä9 Æ:Æ îa" ˆ‹ù¶ö4¾,HÐÉ–#TòçEDÙâ±ÒЉ¨4\n/èp‡Æd÷Y~§P,[}rá¾yh.Z¸#4dýšè9(Ä`7è…{ÁÇׯí;u†Û¿¯´éYC–V®ÈÎS¨¸àù+vø#wõ|=ø; tûÇ[°ùdY`W¢V”!¶\£'aäÀT*;¨Ý­ð‰ ‰dG.(H½©‘âD0‹W¨ &Ÿ×êd`bµY„k‡A}jÞ…¨‡ˆ…Þ?þÝxéè6êîí½"Æw¾dïø =Å{ØÐ©÷ß¼yÜöO z`¦ †›ì„Ý"C-yÕ©G¶<›Ò:‚\ ¯çhÜ;ýîn¯ ûé:}–>K•“—­ä7n›€0…¦ÜJû59_ß¹ô½EmA»Ç°Á7Ipti‚KMbpd;?S¥Ð¥¥©Ì!€ோx"¬“\Æíx“¨Í*/Õ’¨®p$—ÁOÚ×I¤‰AL§OZMÂxsˆÜ“aÂdØ{a/ ¿‡þàÎ0¼12<€„ °?`{àþ#1ƒ%˜ ¸ƒûäË[32 Þï-œa‹hœÞ‘%ò8°‡£m±jj— ñ0ˆ9êSm1mØ·-cg2Ç ùgo^3ÁÄ $(ß ká…LúäFHðáén¸×ˆ1¸îöÓhèùèâ¡›-<.Ê$[R©Ù½Ò7F‹ƒyéÛeBå9óHjGÑÒ'Ø/†D¨Ù­C=§·>jû™ÿ5í œ\­˜$+±É°!×è³'ݬ1åäíçN«*Dl)ÙF§Ò¦ò‘j_Mb?¿l&8žéL¬ßãÃløøï$Ì;Ò,êÈÓ‹MPd1éS²¯¤È`* ô‹¶×?N¼3Çm·=Þ–£ÓèÎ[aކ(‚æÑÍ%~¾³ç;µð[‘éÍ„ëÑô»)¡ÑYz–lCXÚ#”N²À§Šgfi ³PìaŒ´Z†ÑÉy™wðí>Ì_œDš&ÃxºJ¥KO5ãô,qÐ×Ì‹F’Ýg‘À¼ ‘"׳b,f˜#ó>~1ÂüÉ™·¹h å™" úÂZZÿºœÜ{ÒmªÎܧQå©E«ëK‰Õ“Ëù5ñúíh Šþdû'ø|e‚Nï!Â'¾'ð¹¹zùá™nØaÔ(ÌbæÇàp÷Üá›§x,cü–ûqø#ÚŠ"†nEÇ+Õ:Rv™X²ÈÐóŒôM'Ì0š#{ÓF`4Ãû“Q>Fmm?ŠVÇŒ)µQxbr4Ç c¸LœÊÿ2Y" º€3 Æáèþ÷z'÷©¿2;G…”®äú⢂‚bîDé׆C¨íÎý\Nä˜ü !w·[ÓßW—Êõ™¹9y¹¹ü¶„då.”Ž…±:VšhˆŠ+Úî¶ ¥æGªØ3 Ùÿ;¤m´€Gôm´·…GY™ééð€¯‡1Éjyy¹Ú‚à¿ÂÆŸPjI½„yFC3e'œ„c§œ¥a¶ “m9Z±±Rßa3Cü·Um?ÄH> lS^TîWîOÛŸZ¹űRÃ2ÿMã?›SßÂi©o©BO䟿˜øIäs¢FQÎIóË4Z#o`šÂÏ$ß$ô.y~ñáÑ„ºÈ=ü¶š0ÃBÍ8ª0¾Œ¬µ«,³²²_ÓÅ#m¾ŒZ[ÂI¦j¥ne=A¶áF:htNjÈÈ";X,Ö¡Ì6«Ö¨†‘ÖKŸo7®±„½ˆrp±Ê°PÐÒ†² ×?¯¼ˆ\Ñ¢«k?èêPÆVNvZ-”$©§s/\¸Â¢"EAr‡z3»«XF6y°¥yé <½d¥­(Ú D_ÐüÑS§Ñ öÛIwˆ†+øKJ™n}Ô›†bH†QÌÝ[Á ý'¯Ö‘ù˜ AÌõB·F{’ÿÂdâY+JÍ^&Ò}Fªù<N/ݱY±œdEqÏàcþƒ®¸ÑÜØPß\y½@w7”¬`“Õdj}÷Š1æhSSU$³§1¡Ç—VÍCþ((.týŠÕQ>hëÃ౿ †I×¾®ª»ÜQ`üJ¬0ºÉVfnŒQÕ±îæceá6¼‡ö¾òú[ êyvn^Ž’Û;'y b}×må¡¢­:îsý ]ìàñï=‘¸ÆÙª+~ÇÛä¾ÿcavsª˜@‡ ÏdJE¿˜!h,åji3/5«Mç:b`2¨`L´ÉñSR3\nv– å2©Á´>´*ÈmàT¿ñë«6ÕÅñ‡âg|“q'½ZU›z ¹rÚÂúÌ þ„`~ òiͽ™¿›ä Ä’à$¼)!’@QZN¶ã2RCˆéJìm7èþÝí·­qÍaûøHc´vzY€>VS[™zbo\;ûôѵЅÅfì«•enejmÿ #ÖÐfkþ™&ä…y„#pJ° fXm°Fˆß'¢BÔ˜ݬ"à<õvÎæÛ®Vº¯=>”NP|°2o‚eBÐí/,éìó¦õï¥qÂ|§Ö*Tš!t'9ÆÆùå´ôäÂ% £æº­Øh:ÃÃ…‘Öö<ùvê¹å¿yõÀù‹\g±.:^@n9€è€ö¡ŒßªyîkS±ËèW×o¼æ…E¸Î 4“HLït|·—.ït|gSôÁ”&ð"Àºo‘×L&E—YZ®&^º}5¦™$K9TÅ݃)íñÿwuoUde“uö®UÆŠŽÇ¼UÃ]+'‘qq¡“Ý!X2Ø`/`²Ð]ÒhÃÞ#¬x¿E“Bï…$ÆÃà…䆵{`;ÉDÃÔöî’ÁŽb­®M¤\€ûC ³´fÁ\YK©©çHbhé[Ïçl9ŸœÜ2”Æa©Ž%§hi»u/'ë^{hìƒ=%B·‘VKk7.³Îxc 2Ù½³TB9Ä^uæ†R…R(²U’B•J̵›“WôØ9sÆõýæ%¯Úþ£^JÊ"³Ì‚–ÌÂß Ø¿x\çD¼žc„ìð“MËꌵ‘@Jñ?èiþ“W®ÞÓ²‚'ƨÔf¤*‰ÆÜ×{±¿|ûÔ\o|ð„ƒ—½àôŒ¸¦ÊÆ5]¬÷ÑH?'Þi¥_‚á6XÜ$䛳šMïDʳö/º¶ÿ/”÷!6g›ÀÅäH~Å"Þ“…‰N?Ù„gêR¨¦ïø}ùI¸ZÒR.3[¥B™¬\›e(,Ï£½4Ç{ïìÖUül&¨"R½†¤>‚ÆóÒ33¾ZþúΕý—®q'æ.X&6ˆ|CëZ¯ß9ñ{ói•ü¸ÅVBž¥Éçëì¡Úé a‚QrØ\@W(,žNPvðÑ#·ÇKri(ÄKš,Yê$3’åf8Tñ‡éÿ¨}6˜twÈžÜ{Ù¼ð^ëú½5(—ÐÞ°4¼ux#ì„U.»E—•‹8‰îƒûOÒø(.”@á}hMÿÿËËʱâIä ½`"þLŒ§—ð¶Ë`"Œ•hhéÃÎ~¶³òá)÷ÇN¸8IZÌq[n6‰½Õ³š¬7/ 1™EK_ZËWë)ühÃa4.ñ·aëŒ,Û>äÝF˜Þ¸ñØ‘b!à@ò©µ0ªÃ/:ÛV•榢¡#ö#Z¤¦K Gj¹ÕŽØÈãÛm6ý–Sâ÷ Wí%9vxk/Uã»¶¾ˆ€cô•Õgmغkå&.þô¶½ëÐz´=)"„ýsz™HÙÝ “,r®³ DKÓÛ6©îÍKZÚ:ûÔÕˆ»nÐãí;è½G¾Å½|–n›½’ß¿É~~8yа)^ÇN}úîÝã'A, ¨~n²«€ð†{èåtVðûv¡# ­¶é:¶¢å;zq{ ¶>–Âiÿx‰#yfCôÖüZ-šö\³>%—#¥29f6î² „<‘ÇÑA·°ÃÞ0\‡Ø2ÎPœ_˜WÂÏ>QïÐïè÷C¿~= K Q1kÈÒeræµþfAd­LyVfÊ+Êæ¿Á}zŠ ÷¨A¾¸gn6ÊC9l¦.Ë ×h>¯äî€ÝÐýŒ~Ùö/?pˆkëäH4·™XŽþ2ìzBU[ƒ\årEf¦6K›Ïçƒëz/ì ’ç碱2hKŠ Zî Ø?Åöºô¢< r5èµuŽ&WÍO’L*JÔjdp%§•ç*óó²9ìºNaS]zš2+Ñ|…”R¬®§[3 žóè*K;)‰Ãóðt¦³CÓ±¿Xþ–—‰åoOó ù²Ù¸(?—1Û5µ,cw™¦¸ÚÀÁ`¨4õg;øA¸*ÛÍìI®ŒKNŽÏAÙJ® ?.H$:¹–#µ¶ šÆ#eÛã⣣÷ÆäËéCûö8»'Šï8­GNÚ‰âöÁÖð%„'„¢‡ÔauÑN¼ ¹V!¾¨¶Ô._:™GõòJÛQñh5vp>’mŠŠÛ|(ê ^O7>ÐÐy ̼ë¢&èCÌô˜¤-™ ¡M¤wÃ΂J¥! ¹¦#•2?9ïrBWQMT¢â¸²cÜÛzÖ»tî#™hUMOéd¼S¢`2ôY†2®²€Û ñ’Ÿéóh>YÉ@p©ÕUòâø#k£Î›Þ}dâjCh-ôM-ìú£‡¬H[X„JX½\+ÏR榩8ü÷/ÈQægÉõYz}II™FtÞhˆ(&u6|äS’éráFgi)îɤ~“äA3û¡ðªðƒ‰ûSf]Oÿ:}wöÞô}©;Hy&-]0?t¢ÏÜúæN:¿2/[“æ&ö¥ù©Dª)ˆTÓVq»™ýÛÏǵ䲸#-%uM…xŽÔ%ï˾º„¡‰Ç£j¢1ÚÙå ê„Ò]¥iµh?{ùbËík—"7“êsQà*óë*þ*Óñ+1ŸÛr NZ̶’… sŸÅ íÔF;r± RàWŒ›¶Çö30…Çqž¤bz€)°û@ÁX÷F†®WÝ:ز¯îhUjBuI{"­1ú¡ùìXÍOô‹Z‘°žäÖêØºí-I·Ðu1v3êÁ§úÖÛÓð]ÃɆïëí¡2dó–.õó»°äÖÍ‹oÞ >?‡w8Ó¶rí¦ kÖl2µœþ¢¡™Ç½„1²¹o\2Oô%O×›'†®3µœjø¢…N–••”¨‘ŽÕ+tY99ùù9\{OÜ3-'[…䮄I´…Å……‹˜¶ƒ£¤ø»/2”’“­§4i‹P1.M’ö2ÌÛ¼¯ºkÓÒã:EË$™ߘöOú;=²ÎZDÇ4Õ|^uª¼Õõˆut-IY—ŽíÁ×ek«¼íeO4ÔŸØkT$Öp‡+ +¨‘–°’^ž£ÊËUqYª¸ò]ê8ä:wI°ÿö’Ì“>¼oÂò太•ž{Ç÷yK}MóA.®^·i[üÄ.o½y¹ù‹s¥m¼_…S8k 8›dEÌ¥Ó_]~ñ=+]G’êƒWû½œðtxÀ’èp.16= %±„õµ¦¼˜«¸z½ùbï½Ð741zÔhgâXI# ú? ª4IK¶ˆ-;8Lˆòp¢ EÄÅlËMÌSf£4–¤–ŠóÍ-W8ðaPý¾Úã……: *CåYêÔ‚Íűµè8K¾»²¶%85K‘‘¦É1ærGröî@,&Ïl0$ݹ56sÚ ‚I¨%…RûgšS˜]”‹\UH•“K å’—›Or R–¨´Ù à•.XÀ«9ÙÙHáŠr r É?—BáD¿ ¸ˆ54š/S¢¹Ëˆá‰¬  ¨ ±Òo¯îoh½Ôïû_{._™µ‘KHIKBr–àÈ -**Õq‡êÎ/"öá•ùë¢7­OâwÉSòW!69³ƒÅ¤·ß<›7uÆŒy£—.©>¾šW–ä©+Ï’ËÓKÓê’ùÖ˜Óég ÌO¾µúÚ´#f>ÝÒxðx*<#I87'e×Îͱɱ(¥ëÖ_z"M» E ØÔˆM qŠ4¤ Rèt%Ĩ\ó%ƒv?ªDFymÂWËkÒ¢Cè ±®±²Z[Š´â+s^NýYXN‚ßµé÷FÂc⻤ô¹ÜØŒÚØW³® X±M½€Säf¨ä¹¬ÓÙé51_î?¬=‰ØçWWû/^¹3«xòµ¥—WÅmÆ=d»#£jêøtÝÞšƒ‡ãŒ-‘¾&øN¬'’&œ`š ÷cSšÎ³¾â\}ÏX+ŸU7‘Ɖí.’Õ ì3Ép*Mréo’}b˜`ª ‚Èe>2ýd7²ôL’gëHÕ -7(5…<f^ù݃%^„ÜFqø3Í® <¾¬~ʼnثˆm{pä°˜”ù(3“OÙ˜¾­D[Œ1 Q'“/¡«,ŒgÀåúëW·Î¬ôåB˜Á>h";ûrð#½¾ °škrHÏMÏÏDì¢ÇÎ^½tâoÕÓF˜VƒHºõh‚$·ƒŠ®¶Ü2ÇÇñ:Ÿ’’"u)×TÓlh$–½0vàÜ™ÃïkáUšìB‚—̬,yzyê‘]üÙ­gSμtûöàÞO¯ WìZan÷6ýÑϼ¡…×®ÊÞØoýÿ ý­ŸGÒ–C¶šìþë ±ÞwÈ–åóË‘ݨ¼v ùÌýG-‘ótÙÚìâüÄ4:ƒqËîuþþ;×,ç½l™€0ËâYÏpOèúöÁ·@½Ÿñ|Ììà^¹â`Ym³¡ªb÷‘ã5Daß¹4oê„Ås}üVœzÃçèPj¯YÖþƒ“)˳‹ˆÊ#zNž][¬à¤buº&[‡\õz¾8¯$Wc–/æSDСI†Ç·ëósòU(×Õ¬ KJ*J8øL¨”Ühgg0 –äZ̓f¦ÃBtg‘zd’l¸Uû÷G$aõÎÏo; Tm3~¤Ø·Š"úRè7eÒ‡wë5]ë÷ròƒ#&Ïxdý7áœôå̈MËçõþ|ê/¿<üöæ¦s3rÞ°›ÌpuélÿÀåÞÞ_ÞüæÚ…û<®w¾|v}±·ÏÜÀ)^ó¯Ýo»~é©9øÏ X†çàXEV^.R¹f)¢-)Òk8ð„ðÄyI®–¨Y¢ÁKrÕ*MÞ¶’ŒÖ¥ N7Ø AÐ.+H*LA ’•¢×†xO ™QR±¼DQ’S-R"=éxʉ7j[Îñm_ûÁG,Ìœ=q×áÓ?ÃÔà‡_Ý=×øÄÄ©.Èb×f&¥¦lÛ¼qW8bç,¾ùø»K7Únµ®¸Ÿ/ÎBùê8ó;”>gíÁ[öo‡•8˜íGdÓTpùkñ(‡sd|{xø¡í–ñƒõõ‘-¢rÙƒe uv‘ÿ™Š¢l’fˆZ¥Î+A®â 45B9%¼B+j:h7Ê Á?ç?E¶®¸ZgÙ3aÿ_îvL¤A!G†êöî?Rl,Ò£r¶4K›ºtýš NLCá;c7ç¤ä+(¥iU»óŽçÕîDábŠ jZs¡B¬¨”%‰…ܶ¢¸ÏQ˜Ò)³fë¾£µ†jd tJdœV’W’W„\ÕHS\¨.,r),.("9I£–—àßa¥ ú’â¢PQ~Qùï’«V¤£œ¼ì\öƒ”ϰHùâR^«/þ Úáh£Ø‰¹'3—3b–þ÷ÂÕY¤(!T.Nozßd'á")´ååbç¢=ˆTŽª´4­ÂÈ AxM!d­¹{&T ªÁ±ººš>ÑÕÔíD÷î¦î=(êEÓ¶3 endstream endobj 84 0 obj 7738 endobj 85 0 obj <>stream xœUTy\gž2L=Pƒ4“Ÿ(‚¨ˆº* ŠhQI¹‘Â…å°ÉÇ¡¢€”û((È­F Ô¥ë¹*Uqu­]·®-»EßI?ÜßN°nwÿ™ß̼ß<ϼÏó¾04 x<ž¹§L‘$SÉ÷„.tU„{‡)äûeúÊÖŠÇÎ4`gñ5Xù«µN-˜El)ÿi*šÂGS kf~¢1…{&P; M'ø<^Ê‘J·Ø8u¼<2J%±Ýé+µ³·_ðûG'''I˜úcEâ.KG*%6ÜM’L#SªVKÜ¸Ó …|$R¡Ž‹J„†‡ËÂõŸù…*dÑ’ r…<..6Ibëf'Y²x±ãBî²d«<&,1A²=T™ Ù"Ñ·!Ù¤ åPþ¯@„ÅžØp7™·»O|d‚1_Îdž‡ /±`½à0éC^&¯‘OÈwFb£tІSÆlŸ aø¼a°æ³/Ạ.ï:ŽÎìïRœ’u—{¢…h}´t‹G(þ-@þÍQmaÉ÷ÐèÍÃ3`IaKRá¾7n'¢¼•§o÷•^­»À4^mo@-¨>½4©(5?=/œ2fá#ÝXC¥žÒ‘=OÏAŽ»}œ·(æ¡?PØø™ Xñ×3OÅ=7šFÏ)˜¾â!ž%NĆô`g°{@š_B(£Ø¬rŒáÚ» |8ö’ù¿¤š]Æ‘,«2}Ö3„)¬9HHpÐŰ„ÜKTý¡õ‘hr –® pW:"'J¨Å¦·œ~¹ÙWßw^ÁšÒý§w{`Óõ˜Xî'×Þƒ©Àà)c¬‹P{ÌxïÀŽ sùº“l]ИWŒŠ¹¿œý ;ài?›Ub÷¥»±Â4…—ýbkÁ ,Gaí91¶&•îR{DÍ÷ïix¡½ÃôÜ¿Þúõ¡î”jyira ÒK5á̬W0‰SkˆS+[¯ÖH¢K´GΠt~Ol[¤v[+rA«A›=ƒ9ƒæQxúó`SG‡€? Æb2Y¶;>Q¾²öc¨¨ ‘¬W4Ž#5U”–×5wUv¡VÔ´¿4öhj~ЦŒu•û|Ëõé v|¶U§¦ñtdèdëâ„M6¢°ð16€Ùkí?Ÿ‹¿ûA Æ&Qà)y‹7‹S°ýóµMØÀ9niˆ7ãï±%j ¢l¶ÜÞw/Î 2½·þÔöqtLØ0áéŒ8®asè a.úKçŸGï¾é†i˜n.ð&1î kÆGéQ6AVäß´»;ï›#÷aBÖÄ.ÓC9 j8ȵË¡±?rhpJið&¡+u}—û/·>CÃèkUOØ©ÈÆ°bODáA;¼À|r„¥éó+€Ed[t2#<5‰Iˆ’ðã Yªfsª¸~Ùv· ¸¹„®ò‘KÝ#HT†Žk*„Ùó¾@9h3ÚäZûÖ‚K¬|961ø†Ê*Ó°Y¤ŸÌa¶ŠR¯¿¿vÒÇj-Z'óÛ´Í= ù6¸¿zôzWåÕ!ñ×4/Ÿ]®H$ˆ¨ibòJx™›®É@‘¬AÖܦ>wð:‚Ux÷¾3˜ÌX¿Ç'çÐß´®ÇÆk­—H£ÏÞ†i@0Âü¾ö¦ ;°[ Ìš!|Å>a è .Š©—k°©³g’,D"‹õEþh{wø å£x áèäÒ,•etˆjk”¢èDŠ8­$»XSKáYä¡TM6ÊB»š‚ÎÅwªz2¯# Ö1f×Â;WÖ3öŸ•¥GU¢æÖê>±ðÕ@Krt¶&'7‹1†Sú‚ 'ñ½Äò£ÊúíÕ?Çaý#§è+^À|>ÛaFl»~¹7â»ûWI¥^H俯áâ‰ü’ÂZæ<Ö—VOÉIÉU#µhõé‹ûý_ õ‹S7 &HuîU<ÖPÏ0ÿ¿“ÀIô›Ûÿ2‡~=¼-‰?gbXF{Ÿû¯ßÒ`”;3ðuó8„ÙãY)Žþ{#‘ˮ澲ü_2cl–€C¶W~¾7g/Jaêõz°1ÒpaP|YÛüMÅSîˆÅS>¤—i/·ð90[_uåtjyv)*CGòŽæÝ€>‹ÂŠü2T‚šÔ­ªÊ膀„Ñ*ßXlNÁ ²|àTÕ¢.Õü1&)-19“IÎ@È-s[–EàÁˆ4E¢ŸlïVäd²¦.ø’êOgžµº‚.Ö^ë?”о@ŸS”™Ÿ}8ƒ‚…$ž1´Pë¤ Þh_ÞÒÓÞòm1Ó[ôeÁñü£y…y¢âü‚¼’£”ñ¯S?(¦+[>¸ýϒ܆á²;Ú³W¨£^•®ÉÌÍ`ÖãŸ>–îi»o Q{urdznê¡4Æ ¿Ò—†áßGšòO  Ñ#iãÆñ¾;Äi½ÍŸ"/ïãUþÄ‚³oÂAä‚Þ>¶™pN¥UëÜ8gÆu»hüþ½Bp“„÷:…àƒ¯Q¯ÞqµÙ4Úz>stream xœ]W Xײe306(èÌhwÔ›¨¬*.Ád_GÁ% ‰1QQ¸‚€.Â" QˆQDÀD &j®×,jbBPS=·&ùÞéã{÷}ÃtŸîS§ê¯¿þª‘1VŒL&” ÍLÈÐÄÅxÎÓi㥥 ¢«Ln!ް$¨3tôòÌ¢’GvÄÖ’ØZî°ÊQT9Àǃ!ç5ÆR&ËÞS8_—¦O×$%g¨Ç­xk•ûøñþwe²:Vÿ÷µ_ÂFMRªÚ^d&hui) ©³ÔóéÛZ­&N¤Õ§%oTÇÄÇ'ÄKÛVÆhÖ«4ZMZš.S=n¾»zʤI“=é¿)‹5)±›6ªCcR7ª©%ÿÿk…a˜‹ô©q!ºø5‹ç§%,ñ[꟞°1ù­À MhЦåÁëWdic¦ÄŽsWOðôš8iò”©Ó¦ÏðžÉ0žL³„ñc¼˜¥Œ?ãËLdF3˘f3†™Ì¸1¡L3–YÎ3S™qÌ f3qgV2Óf3ƒYÍ„0ó˜ Ìbf>3ˆ±fdŒcÏ f^cGFÁ aƉqf†2Ãoš ÆŠÅh˜˜¿d¾²<Ù¿-ÿ°(±0XFZvX9Y­±ÚmuE>O®•‹lÛËùsÛ¸J~¿€ÏçÏòŸó ò´yPµ‡u´õe›Q6«möÚì³9osÝæ_6ÿ±Ýl[aû§]žÝE{û"ûkƒÃ Ú^âÝ2Ù·àa 9†=þLwþÎÚ‹m¨0¼V&•b­€VÆh#Ž£ Æ«ò=ÜnnuB|Øö¼üõÊQ\ùîÂ=%ä iÜìàÇÅ%× Ý *R.z‚Mj™c'¨! ÔΊæNjÑXKoû8EßµæÆžŽSë*ñ]ù‹›àäæ…¥-‰S*šïpÔ“ ï —µ¥X ? ;`èÔoЕð8Ýs$.€G¯Ã4pþñ.E*taõþº´„÷‹¾öüâÁ–’Uñ©êâzò9žVìÇÛn‘rƒ_™ì:xˆ^RðZ±Kgc—Ê…¾OFº­òóÕ7.ÞøZe24ȳ\œƒ`Z™ãIjm»„ÎÑÝ Ôôp5²Éík+ƒh˜‚;¾†ÞóŽÎ=»J¥h]U{]sݵ›\¨<ÓÆ£µh'|y!`´{x ¿xﯿµÜ¸¡²7L4ÇGi€¢7T°YŒ’ãlUxÝá¶g±°ÓH×*Àƒ³7$š7ô€œ—öp2aàa|‡ÅFñšz 1Ÿ¥¯f€£¸†ÈÎÑ ÿ3…Ûêµ ¡·&7 Œú‹Cp,nÇm À±4ëÁ`÷f¨ÐÍõZºÒ‹¾Â>†©0ýËŸÁúR“>ù„ª8ë@vA}5‰#ëK²Nm+Ûy콋üû„‚{g¾ Ä¯É)ÓeíËÜ›HùUšCÄ)>õ07 Lƒ^C¼5!A üÇz ÊjGwtÞ[÷Œ<#÷>êìéìþø À&þ¾àÆÂ^ßšQ„ÏÇ! ¾3§ »¯ڢݬ~ê”ç·ý`§’rFKx#8ÈúhÖ>£õ9A –äFUëÕ+=Õ¿°Òþº¬7ìj@%ZÑèèsc¿@·¹r ¿<§ã´ÐÙ(7sòÀtÔâ×ÔâA©82¡H€%,¸d„—æu_4/8¢ûéïç¿ìQ™„Ö‰9Î[RœRœ_Ñ8¿¢kÑø ^*Õ/ xŽ-ÔÃàŠR¨ýâû¢…ð°S{q Mˈh™tH[š©*Í.ɽ”³V U<:³½pû&—uñY ’R÷ê•[½{èÝ“ôuö´þwÌ$·ÉgÅõ5õ5Ç>!mäFr³_9ú__¸ã9ÊŸª)kU*ú»ÉGóÃøWrLGÖM½YIõ«›*?õTRã/šÏuž:¶}S±²PÿÁV’Ìã}“&“n}Kbµ¶*òp$ ktñKy(3K”S ÞGÚE &àpwVˆ¼Évذø]²cÏ6Õžl¢'ÛIà›J“y˜~œÍ(Ê=LjvÁú]½êïÕ4\%ß“+YmÉg׎(!¼ÑÕ̱)VÔ~0×DŽì:²…W?H÷–sIõaí¾'©<à|7…òÏ–Ã8ê÷¬ß²kudé°Å¡‰“ƼÕtg§’ nNúc,¦¼^ –i sË:еJ…ìQu¹¶œ´ û´õLß“¶¸¹*Í)í¤(î3w‚FI¼>€ïüÎÔ Æc6@•4)Чßq¦6f3^ Ç$Ö¦Ÿ-Ö9‰µ4Ž9EóÂȘÀ%)5]JøÆhË¡Íõ@àn¶ï¨W*²rfN~K±€·¥Ã±_ -Ÿ»† Æ~©„$À¤ÞÔ)Õ™æå¤b®-’0§LvUB2î âë´MH|öç.æÊa&‹kà{XÑrœjî_CÄþ2“ÆUK‡æS›Æ\njdŒ§gdãC¥˜Kû^.G|Ž.kj ïÛð<$}Um-­çŽ^!÷x1—²¿Ôt¶L48§X+§2âc´÷í娖®¦KWcX˜`¬•Ãhöèp×è ‹9óR,&oÍ:!Ÿ\.N¹ã êþ!AY^"¤W¼s˜œà¡‹7/£º½‚¹Jpe÷_®8ÒKøöãú$}îæ-yªì„lÝ1T­_µœxˆ¢„Ê4JûõÕ²[\;ȧU­yE6ÑíÝQ´™‡ ,ÎAÑìá·6µ²¾ñhÙ•BeKAíþ}ïöj,x)q»…Ûw oË!ÅÛÔ×sÐ-Gëð¦nšóÂÙÿÇîÕØ×ACyÓüqV´vüŸÑïîõ³õN¦E+ÑhZù¯{Ñ‘{¾ð dÃ7†Æ(7\\q2ˆ„¸Ìˆå¼¢µ›ûÿóÓÕWóÓÕ—zFÅ»uåé é=® <…Áà}sÓ­¤KªÄ¶àÓÁ$ˆ„§kVðPÈ5~"ô¶úuóð[Û3ðÔ4(ÀÃ:=EI âpn8* zôjA%Ð{þ7v$ÄjÀŠÀXn§}Ñüß»ìq­Ìm%O^á DzÏe_`ìiÚ†q,A7 ²#QÚUMÿ²Êd×ÀM@·»9˜–¼ôm>”køðÄ¡ãåõÕÿ¬%wxA¥ãÄ« .Â+Ñ*MÕn2X'íö@7sÁl-7̧žÄ"ŒýS+ïf!Ö •›Xf+¢ó«4qÆK$¬2$ o룗Æ.ÏžDpA‡’)­h êM¸OúHgõ¹Žúö²Ÿ %à´ù¡æÓ˜ž€Ú™T}äVgÉɬ’Ħ˜C¹6;oÖÝæ˜”Œ8’Ht¥9g·žÊ»M“ûî7ÿ²©æŠAèÃb¡1@ŽÖP‹“i’&ßâpð†°—_æŽåsªšt¤¢{·ÖÆÁì"£JŽìá35¥M„ÿ²iõô™Q«ýkª?Û-°ïãk½)'iÝ?{FÇÈ1“CçµÉokcU0Nçÿ§,î–7R’Ô€´0Œö±Yxç|n…¾*ñâ‚Sÿ SÉüøh¿˜€œÙ‚ö‡ß8ûVCð­øï¨ñ‘?¿/îýí—ÅçDD©öÞ&dß©¢kõu—I39¦?É»c¨ÐÕ¸zÎËCFµÝ¾s®½Ke&G‡U¥Ð.Ú^°Ñ3PÐ…„N÷ÊÆñR‡.‚Œ›TPfCÿÒá­O`1ÍæQšLžÖzØÑošÔÑStŽÚè ¯øåäñ’¦ó.`9þ.:ãÐñ¾hx"úšF©ð‰Ò¬ðvA—/p‡qÀå;M»Ï9%ºÀ&AñËM ËWDÅ/‰úøÓö¦ºNÕ{xZP ô6EÌ ŒŠ Œn¾ñÕ¹óÝ*ûŒ ±VêGá,X[ÃH°.°µ…‘míæxÏ©Ï endstream endobj 88 0 obj 3830 endobj 89 0 obj <>stream xœuW XײîqèEdQ& :ƒ Ê"›‚*²gAAEd ·,Ò³¸cQMTÀ Yq7* aÀAe J®&š[=÷ ÷¾Óƒ7yyß÷¾™¯ûôé®sªþªú«ŽˆÐEˆD"ý ¤ô¼¤ÜÔ„xáÉž·ñGñ“ÄQæ¿Ü5…ä$B~ðCÎ@ÌèUNdÓLø•ãà†1Œ%Ä"QÁ® ×gf§&§äZÚF†/³›1Ãþ¯gOOOË5…ÿ}cé—”“šœi9 ò’Ò×ge$eæzY.Ä_§§§&X&§f¥äXÆ'&&% bQñéIi–©é©YYëó,mÚYºÌœéì€/.‹R3ÖḻŒˆÏ̱”[†'%oLÏþÛ$AïËf&,õ-ZŸ½haVRL˜ß†µ‹ý³“rRÂsS#‚6®[œ—™Ÿõ~AÆše¶v–öVŽN3§:»ø¸º¹Ïšíá7Çk® A8VD Føž„#aM¬ þĉ˜B,!/b&1•' gbA6ÄR"˜p%l‰H"„p#ìˆ(â}˜N,#äÄ,b9Jø³ {"šXD,$<ˆÑ„>!" #BLc‰qE˜BF¼G°„)aFŒ'&æÄ|‚˜HL"R9v#¡GHñVß="Q¼hß(›QòQ_Œz-–‰CÄ5zcôÖéíÖ{AÚ’’—ÈSÓ¨dJCO¢?¥o3³™ï˜þÑz£?Ý0ú}G}¹þýô/ékÆØñÓ3æß®©× - —r†­†7  ‘‘ƒ‘Ñr£SÆÓ×·]2ö£±OÇ-W2Nk’fRbòµÉ&ƒNñ-œRC—‹zÔbX É`‡3øh%ÒRF'®büÌrŸ ëÙGh=ù†B{4$2 PÙpùª Ö“ð•‚EÅÀSÒˆÿe*5ïa™ñ|‹¦jí‘9oOºP௭LóØ´¶ $…›ð!W²«p73Ÿ>¸õà'‡¹*®zÏáoŽ:pø›óP¢1/¨UÁ/éL)7éèòyŸõšIÎuà%ßk+žÀyZÒùÏkÏîŸOKÑzyKº/äÚ4ïè ßX©äœŠÆjävñö]¢“}b> ^³Ÿ\Þü}~ur“¼Ú—cÐd'¤‡|‘÷€%XÁ¸îGðÞ^Ùlªdöêx?ŽqZúÆ‚ÙUÅ/×øî•é€Ðøa‹,x}JH$¦ÐbT‡Â ŽD$#@û´ú$àñJ¸+Ðuh áÃÉG*þK•˜O1…xH-¨4ˆ¥Qʰ1JדˆÎPñ@A‚Ha;%ìVB±Ò¤[ ÝêEj3 ßm Jª»|¨î<#y{¦¶üÊusN•Ðâx†‘ð÷Ož¾úМkÉoH:•t:úlb¥FX(† ú W½ýhÁ±ü¯3¸.±$}C~vAæöeø£ ôŒ…ÝO×s'6ÎÁ+ÎÝŸ–lÎ¥|˜¶1wcNÆæUƒ±ä:y£N† µð…z Ý×óyŸN¤ áÞû½eð3}ÐÒ'†£üÙ¸X!?ä§²;°x ó!Øí š!ÛéÃ^óA¦Èx‰·“cxHÀäºB-Q,­àå ð)79ÖW°Û‹ùm8,=Ð*¥%â¸`ÚD$B^2IòïC"˜üSsÕ½z©¤8à!Šøxvàº'¼6\âåê´è1ÑíÇ¿Hÿt$4¨Å¼Ô±PŇ霙ˆ¸Np,MÁ)-ž«SÓFšµ#Ÿÿ¬â¥*±Æ«áªUSÚq¼šœ&xLEñ†Zù;zm®’RŠÕbÍtžc3ÁÍIÜ8;nnzdXðÂdkésÈ fJ»ßµ%?e½à €{õò0D23¨mÑ[R‹33ÂBS½±uS€S “o¶®?!«Ê>¾/’ÑÁÃOWस*$Åå>3É÷WQ&ûŽQ#›rÚ8,úAœNðs‡QHæžšˆs¢Û‡–܃"9ûüÖ4Y4g¦ë’0ã=*ò]Ôn]¢sÏÄü^ØÄr=5×¥>óºb‡Õšæ€Sf>šÿËd°ƒÞ6 *pÊä¬L⢸UGÖ_Ì?±ýDéfWûåÐÍ»O8Fy7Ð}·£t^8%G)âDíaOÞ‘Ò†hïžü¥6„æƒø[8ð€X«æ`Mв( Y!GT€ ßa­ª»ªù¦¬ûAŒaàC$´Nê©.¿!$G~Vøæˆœ¬1AÐ|s” ®Ôí߯æ'ªÅý¦pŠ9 …|ÈCz E¡2tŠR[°ü˜OÔŸ§£EH>ÏM‘ý/ÜßÅd¿ Ã.©×-ð§%]­‰‹OX ©3"1«ø<³igó±ëu2t’FÓ4^l?ŽHdðg8ÞV>—ÁIÌqm`Ñ)RõŠaÎ.7%uºüÛÚ/?ãJIÛé‚Ý›wqŒOlÜ|™‹`‡ve/¿²¡æsJØ£ý¢†þ!16æ …Ôu®þ`Ýé‹g5p˜4§ M‘¢«ÃjŠ·Ðƒ=E÷·¬ðòŠZá"á™Ï”P¢ `òž²üd¥v2”ð3•Ú]¸”+µ¾”<Ãjúª°õí8êÌ$5íW <|”æj:[ÍHÚ:úÅÌSºd×ö›¹HnMÚ oFRósq Êì€q=àEªûà^å1?/3– ݉_^d&8#ù¸Wú_ˆ–ÕÅ]ÛÐÊÝç.o¸ÏdÑœßÖÕy™ÓW.ç’¸Ô½¹ßØöõÇ'™YÔÛ®Å`Ì)¸GO?Ûxà>Æ ^</Œ BÙ–yÈ ‹œç:s©Žg®)úuáX?üÍtx­D¯±é–Jm‰€×Ÿ¤¡¶â"±¥¿Þ@†8ŒæÊ§{žO„±2EÁ’Ê<.iBLÌ:ßUIe‡ó¤›|tàãÆ•Ú Û–À$œ•ãŸÜ{­ˆ»`}Tæu(ð›ÌÃ\Í„Æ 'ÛœÉ\ºKú®ØýóÊE½¸Ö‰;p– gôâ:'T¹ªª­ÅåÒ#E{³¹µÌHµSÉ[§.X•û~ŒNŒ\І‰Z h`Ιä-,ÌÿÛ©Þšg™„/ áV}W–1ÈJ*åÈöc;†JÁ àÌÜS˜õ•g¿¿Ük&sÛ‘µõëâÅRW†¸xœØV™4¯lǸÇ%ÚJuy*¯€:%T)M.ªùMJÌÒšhžeÑuOÈΧ0õšÎp@U5¬–®¸’ÙÇ 0øèƒ#ˆ{ÂÊ¥/¨ `‹ôž"/Î…[˜¾r¶ÿ DrÈŒA¹0 Í„èt×tÿˆY½r:yáÉÜN~z›è)N”/qà¸Ã4Å/ðG­%u&ã^yù®ÏK;èÍŸn*ý€c’·ì­‘zFA6Æ6¿BËE°ìt,ë¡­è¥4z³­ž]&× “FU°~Uª…ìVóûÙR°¶ëEs±·Ýl=Ñx—†ˆ—Ù2^AJúŸçœ_nÎ%æ%¤oÌÌݼ˜›ËEJ9Ÿõ݇§wÖcÇï *‹«Nh|’ b®û±¼áLSÝÉ{Ü]n â¾mz¿i¼¤ËõHÚñkæï5ý ̽(ûÒ?â°l,õòú˜qd¢§jM†#jÓŽ6Ü ÂS!¥r;£»LîõÂnÌH|ž)_ášiI½e„¿[püÙ»RžöÐÚÒn÷"~ëjªjo’J |@PæCÍD"8Ô„š>D/+*Zþ)Çí,’úÑ{w|^ZÆ1ƒMµ?Ê4†4ÄëyXõRº¤ÑPJ|SæŸ ¶)µíž°}ØBMéÈL(–¯Å…Ã×k*$‘}ÔšB,_A:PÈVë,yo<¹¶‚üï'‚:7°ƒ& Å^ˆýÕÔãðG5äï.Í/qI &-)”²I]¹ÕHËEOÕ:”š„¢²‡¶ó C£#ãÊŽ¦H“Õp­ ¿ÇÏ#Û?œ`̃Qƒ0MÊïIA-þ²)8êÔ²ÖÚOÁmêTÊFko‰NØcí©×¼ÓZ'RG‚}mOÄ`÷›=œ£û¡Ã¿“ŸÙer¾Êt~ø® †[?á>¶ÈüàÀ1ܦÕ͈'ߘ˜"ÍÉÚ’±có„úò‡ÚjÇ<:¿>Z¶‘æRòŠƒ·!ýâÂOÒ6-ÊN_É2’ûûaÜo©¼zSúEÔ±œ«ÜA®lWÕ—¸C Ë­ßV”›š¾æƒŽ I:ÙÒZs¼¿L¦ÚÿíîãeÌ_=ëDSøwì¶Z¥FëøaÒ†‚¸wÿó“nìÎùð cðo¯ÐÅŽ¢P ïÍЬëôÿ£%IžÄ¡§¦ÿrh=aÁ¾\j‚‘ „“ö,D{Q(*'_P0bÁ Å’C#}ð»nþ d 3I“.òpQÒ’Ç?·Þn»s:)HІ… áñö™Ä`ᑟDF^±óÏ[#M¿$óçb7¬’3’¦‡ôß[Æ«½p7EÅWuüùè-iZt¶%³Ó¤¸Úû‚û[$]¸Ãf¬„ 2“£9sÈå. y…ù­•rë˜Ë®Mà­+æ,|,V£ö˜Ý…œ9p‘Cˆ ˜1K)²nÂ|„»J䃼]‘ vÿ‘Mm¹¨ï­¸ï k}«æ6å¯ÜÁ$ѧëŸÉÀçü3Øþ“jà‹¶.„­8]Aã_.º¢~ _ÑD³HLW!s²ŽªsÄôÓÿ;#$øQß1Dà]¬ßŸ_¡YŽ•.Ć¢-Ã[ÈV ¶h¶èμB!º05b8Ž7æÂ2ׯĆæÚë8~[’€=LWâ’\ÒÏWþ ]Û¼øxÇxë½h˜*6ÖÁÎqå¯Q /^`t@lGE¬êØ].ªn‡¸v°®bX¡YÀ:úø88tù ª»ºžÍÿy†Ì_ïzýªð¥±«–FÄ^j½v©¾UÆ;²ZöØ}Š—a¯–iÍ9GAî¹¢ëùË¿ä"urõ×Z9]…Öàƒ0ìW‰a?¿‹ÕîRi¢—ÐéH”_Œ&1r!¹>þ|—ˆ÷óŸõ±ÚÑÔžßÕô]~uuü?®^îÄ…àfNk|m|í²oƒ8g.(-1,;isÜŽ…ŒŠú¬ñ‹êý••.kᘞ[áó"Ö­'Ëœ–!ÛÙ«ƒ¶¡™øtL7ù3PÎjK œKóRä¡•"wa(Ìèr*Aq—Iƒ ÊðÉ6€‚ÝGÕì$%o‡šãf¹,rš±²ñ÷d¸A ß»ö`Ú™ÀŽ”!ܤLìó”æYÕ¸_¸ìûæÍÁhN'²@Öó¼ÑŒO¥*jç…ÝGö.¯»x¤•c ±ó£ 3?ålIÿ$´”ÑÁvݼ#Õƒ‘êù¨Àçm·¿:„F´á¤# WºY”MA6Ü}§tŸœúD¼F. G‹v¥5%SçšNÄ;½¸b5uqè Ÿ•u=yXïýKö®¯Øp6ò§ô¬·õ«70,_¢‰Ñ ›2ÖÈŽC ÔÈÒ·½Ï¢‹ýp¯þ1&"`æµ#‚óæü“âåqù8£XùvÞyymøt…pŒz2ï¬Fÿ¥YË’d;aÁµ—¯¹;܅įÌîìÓ–Åö.a!s,ø¨1. 8&nÁ‚˜‹Úš.>”¡f=Éïª[³Ã"œÝî÷öÞº6ràS@¸ît‰jhZ`{1?ÛékÏÝ­o/ïã€Å­ÂŸãï&^=)œO-¦Û ?àï=h:ÒzIVŠD̘ƅr«ëJÚx_Ç*ï:¹- uŸuóÅÀ­{…í_³ˆ„Lp·Íé†7ÐþF£0§¬á¾Bïµè8÷^¿Cöšð µÿ? Ç?®‡–/A d7­•‚/wa(ÌA„ÀvƒâÆ¿¸îìŸÌ–[ÉWý‡¼’Rè÷ŽQ|n`ðt!AüéÑÍä endstream endobj 90 0 obj 4759 endobj 23 0 obj <> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 91 0000000000 65535 f 0000040302 00000 n 0000064172 00000 n 0000040142 00000 n 0000037876 00000 n 0000000015 00000 n 0000003222 00000 n 0000040350 00000 n 0000063294 00000 n 0000061394 00000 n 0000063685 00000 n 0000061840 00000 n 0000040391 00000 n 0000040421 00000 n 0000038036 00000 n 0000003242 00000 n 0000004952 00000 n 0000040462 00000 n 0000040492 00000 n 0000038198 00000 n 0000004973 00000 n 0000006950 00000 n 0000062327 00000 n 0000060472 00000 n 0000040524 00000 n 0000040554 00000 n 0000038360 00000 n 0000006971 00000 n 0000010608 00000 n 0000062999 00000 n 0000061005 00000 n 0000040606 00000 n 0000040636 00000 n 0000038522 00000 n 0000010629 00000 n 0000014275 00000 n 0000040690 00000 n 0000040720 00000 n 0000038684 00000 n 0000014296 00000 n 0000016876 00000 n 0000040783 00000 n 0000040813 00000 n 0000038846 00000 n 0000016897 00000 n 0000019170 00000 n 0000040865 00000 n 0000040895 00000 n 0000039008 00000 n 0000019191 00000 n 0000023062 00000 n 0000040947 00000 n 0000040977 00000 n 0000039170 00000 n 0000023083 00000 n 0000027543 00000 n 0000041029 00000 n 0000041059 00000 n 0000039332 00000 n 0000027564 00000 n 0000030583 00000 n 0000041111 00000 n 0000041141 00000 n 0000039494 00000 n 0000030604 00000 n 0000033464 00000 n 0000041204 00000 n 0000041234 00000 n 0000039656 00000 n 0000033485 00000 n 0000036245 00000 n 0000041286 00000 n 0000041316 00000 n 0000039818 00000 n 0000036266 00000 n 0000037328 00000 n 0000041368 00000 n 0000041398 00000 n 0000039980 00000 n 0000037348 00000 n 0000037856 00000 n 0000041430 00000 n 0000041460 00000 n 0000041492 00000 n 0000049316 00000 n 0000049337 00000 n 0000051648 00000 n 0000051669 00000 n 0000055585 00000 n 0000055606 00000 n 0000060451 00000 n trailer << /Size 91 /Root 1 0 R /Info 2 0 R /ID [( íž=Èöfæè¶Jc‘«)( íž=Èöfæè¶Jc‘«)] >> startxref 64373 %%EOF simh-3.8.1/DOCS/lgp_doc.pdf0000644000175000017500000014262111143604403013475 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’†/ úæ‹ê\\Û•ä¶§²dËNmI±uÒ¦¿¾Hb?œÎ4Îx`\,v¿½{~Þ”…Ò›Òÿ¾>1›«ÏGáñæäÉ0øtuôóQWTþ¿ðÇ7Ï܇v£Ë¢¬7goÊÂÚN•]x¯Üóª0íÆ”ªp¯?½Êžæ[7§j[“=É·ª°ª3Mö"WEÙZ­³M®‹Rwmv½}ô¬nÛl×SilÕeÆ×ç^­U—Ýå[]ضnêì¦~ c74•,ô2ßV…Õ•UÙg¡Ä®òÆqRÕuöfxè>þÇÙwGu]”¥“ÅVWaû—n¥|§ÆÙÛܽ¯ê®ÎËZodâEî5Æ1¸¾µ¼/û÷‰wyÓ´ž /~Õ¸¢øK;H¿j뢮7­qJk½øßg?¹/”ÚØä £‡OZGÉÚfÓºO;ùD©¢Ñ›³§þŸ¿KŽß« Ù§Ggx•=òÜëΕ=Ï·máöQzÝV^‘N|?ä[ë$¦KÈÔo󲨚¶-+ˆñ«od™Ûu[Õz¹Ç¹Ïú ]ÝÄŌŹ@÷‘Ì=¸Qƒa¸Í´uÜfÜ´ß~ç¥kãt8Ž€±·n¦rkë°7¾ÿàThTekÝ„‰™Ù¿r[˜®r†ð>ß÷È4:»Ž_…Q¥Ëd 75ÒèªìV†¿æÊÊ!ÏÞœz;Gy\Ƚ“á° ‹\Ë„: ½¡¼žËÓ[FŽE*@h}ö´à o¦+VmÓ"‹°É„»8áÔc”6ÁâŸÝ°léTUí­Ôh];?R;“ Fï¹óÀ®,r³“í9=8R*mgRúJ–„ý½– ½â<®… t<èÝ™OB5n¾Å^ Yàà’jþBvº“§}9e…;»FÀâjðT6yƒâÔ?öšuÿØŽ*ØöèrðTUëÍ» ½õzõ€3EL Â/.ϸ¬¦{ ñr o·ˆ‘8|Í­a x'sÞ Ù*¸ö^…§uÛeVÞð C¥tK#Ä´L(qùê ×í]íF3ex€á>8¬€êë™÷sÚ†÷CÀimçTL¸*}ôMø5•,px—3;E/A= (ÜìAvþO÷ÞÅG#XD,CŒ•èâ(»¤®Ü–͵-´ªš%ßyH|?ÛÛÙÄký˜Ë˜§(|¬¬Ô#I·¥òH)|1’âðdpË6q»à:ÐdTYªÉ8ãÔå,©™{ZVbÈ$|!id ÀÔèB+3µùð×{È&–ä O>‡7<odGs{Ò(­+úý9X4wZ\»G%,rçÊÒ 0q×CœÓ ]ÑÕ¾¢t—R}û,¢)”Yú­HêvÍ/ñÜãzM6`%w”Å!³©¯Jžµª©’Sˆò\ôÀÊུ5\«幞Ø$ˆÈU}¦ò‡^øè\‡‰ÊU˜â\ßÒ€4¤ì®”áÈYʃYàñ芘¤b`ìç¨Pˆ›,-IÒ¼8R˜@{—+í#µAçÔï«ÔíbzÇžFà¬B¤- ü0~>©¢RiÌ‚õWK€ß¹µ¬6­rÌtM“C0CËu9MÍ d- ¨Ü ´guÎT°àìC”â†èƒ+!‘£? ‡Êǧ_ØK´ÑA 1Ç5òù`Ÿð;©ÄVRÎH°¸;:KÖ¾è'‘´›W2\ϳº_L\Š5fZ³\ÍQ^Õà´PvÌyÞD3ßa g¬Gã{…zûU'!5 s“ @ƒ‘–0é*Ti½±”G©Ìmš½çáé.gžjµîO"䞣Pâ³X¢Æfp›‹»¦k«±jÐ \ñúþ€< ˆE›”†q÷6Û]½Ž†<«†ÃC5¼RjR+ÙkÕÑ !?ÚQX¬ªòsC­ï‡s!~~>èÈhõpçkŠÙO´¦ã˜¦NƒÊbv7¯’'LÙr&Vã÷Ëþx°b ¨ÔåKkrLaêz=`ýööÕÚÊNnk.7))€;¢\-)xB=¨ {¶¯2!ûƒoP¬ ‰ç’ÆWÊÁÓXv¨ñSzï§å6¬²£{‰ú¸Ù1ßTC­óÍ<_…?Èå¶xx¹‰k‹ÃŸHªŽaܺ-ÚÊk_ÝÍ,] E(×ýì&fpb³²B­œN²yù&f´¹ÑSã\)@èì¼—¥˜]~¿K«sªL`ç—åË‹Ia:¯çõB]º‰bîô€›¨¥P:Û§7¹Õú»g#\&­Ÿ*üFG ´`¸p)Âtp€tzÃëŠJvwAƒ%ð8q¾”îêãÁÒ²œÀcœæ"h£c@ó™E{mdá…ð2oMp ü"®ç‡VesÀ¡©®_³ãµll’•9ÿ–ÃÏ ÂQòkÎiÿÜž‹¤Y•àí9«Íuü³•»kr¥¸z +.]‚,õ—ÍXjŸÅA4¼7ä>øIk>\CÍ/,þŸýÜK× ë×#¤U–âzLîRVô²Ôjàì‡Í¤åŒ^‘Hl¼Šƒ~{þƒË8ú…Ú&WÎÖÜ8HãZzgÀûU¸9¤á‡¬!nÚms@7æäöÏûE`“÷3H‡ÿ™Ëö;æ·<t-­‚)úø[¸ª‰uÁúa ‡=cá2ŸSH~U¹a{¤À¾¢ûºÇ὚Ìïq?5i?©ÓÎ!À*ï„!ûÚ"Ë.¶iì’‰’öúˤ;Š÷¤!Ž_äÂXŸ5µNå ~è?þ—ÞF§@ööúÕ_¯ÜÌZêuH(\é)¸çoÓz«­ ;p@tI@œßrñ°´C—ô¢4¶¨êY+ÊñÙÑ_ÜŸÿ™îJendstream endobj 6 0 obj 3139 endobj 15 0 obj <> stream xœíšKsÓ0€ïù>Z/Z½uåUÊ0´áD9”R -Òaø÷¬íØÚ6ÎÄeÒ8¸n.[Y»+ÉÒç•´?2 ¨2Yþáä|öàÀgg—³ª8;Ø[?Ïf?ftùWpùä<{8'EÄ,BtÙüãLBŒAúÚ*fÎÒÿÑf%xªp>Ë31ÿRëïV#ÞPpу1¥ÂÛEaAzŒ†” DgÈV~( *(ãóÏÁú@ÏÏEÀIïm~•´¾¶Ï…h¨0ÿ% V[íòo©æO¡@»(ué©yþT4PÎ[똫dô4é_Rû‚r:‡dà~‰Î†»m°×KßÍŸÏ”’ ˉ7ÿ@“f{³¸4УË™ƒÁ…0èÔ1hvEDTàÍuÍ5žhXŽS¡#‚—!+PC¨KuÂùE*Ý…•Žù«„ì¯Ñ_ÃëiËçãd¡úª Õ×ù«ˆ¿Æ¶}Ú"ŽÅ·ÇB hÞê‰Ð€wÐl¤‡p?W±¸éñk¦š*¦E­©JÈ NaDÛPƒËßPÁ¹v(vbã‡]Ô‹éëiÆÎâ ŸÛbñFzh÷IJiÕz,«.,ÏI¤`88Ÿÿ¤S^åß“> u ªé yN‘p:ø•ÞÊéšãý$^¤ªÌéUg(Î,傯C‡ Ê´Á<ë óq$¨8øO1úÍ3YPÑnœÉGý‘é ÌvÚ%v!½žz+ìÔn-;_Ö‡z#7ÑV^xC*Š^Mk»B§  ã†³EÇDÑšE«Ñ9(Ý2Ë»Ð#ª4]d|–6û‰rgíQë§kG¸ Ó±@7D™ø¡µÅ °s…tíÖmªfk0./ZÊrðó¸´1uºÔìòýÔ}Õ²u é3P7 EMm ÙóºÄUf‰€¼(|%,xí¤m¿1ÖrG%Œƒ‡ õðˈ11ðqœb£ƒñÐAÙM&Ò&›™Ó#‡4Û{àÈÞw-¼jêöUË ‹ž“Òl´U,ú)÷ÇsPí¬=8‡ŸÏ+ˆdÁ¨Íop' NÝQZ,ù:E@í8ôº¢ÅŽ‘§½OZ,G(%þ$zñe̘ó¸"²Šç(]²ºß»Ì2¼¤,³Êž_pèuas_x0A=tVoKIÍÛþ¹K»097 (ãèÍl+dº'ô?o{C½;Á^KѰLQ$ QÓ Qv“Ì"·ãåhJñBÌ[œ·V!W×$}£À¡š’~î’Ï=—Ó”ŠùßpkÇÄí§bvôÐLîÀs¼‰ç>I÷Ê4)éþÉ|öš~®F!endstream endobj 16 0 obj 1011 endobj 20 0 obj <> stream xœíÙrÛFì]_Á·’!ýwóÖ:©'gâ&ÊSÒ騒|4¾Å9úõŊ䤠+¶Ó>´é¼\`±¸Õû¬®„Ìêø¯¦—£G/]vº-—³—‡-ðátô~ä+ÿ[.Pxz™ý<D!²P›OFu‚¯]CUdÖÀßÁdNÔ•ƒ —£7ù¸(…­œ°2?+`YHíòóB¤‚ÎE骺ÖæY!d¥¥ùeQJ]ÕN¹|W.^'BŠÒDtòÉrQÉÚåWμEÐÊ`UkkE÷^#8wɆv oº;P[ŸDrñj”<ùt†äçf *é„üW‹ÒVVòãBUµu ¶ï­–XÊx•×”VØ+A†Ü%¹†T.é;‘&ᛈ…H¾j6¨ZÀ±¿?Ffã£üýXLÈ„éYLŒuu%{pJcC”‡‘ue„Ï_¥‚U¥=Ü¡&$ÈÝÂ-êªöZÀ¿å5¼Á{¸F³Ey )}‹>êøÒ5p|{á D ìÇóÚ/QêN ™Î‹«”ð]tgtØÅI¬Ñ•Z: ÿ|C½]„XÜ£ÆEœ_ÚŠ„A¥4ˆ§ñ¦Æ`oPSÕÐî¤MšŠ‡ü!)–‰?¾qÍ:Þbðé"‘šïÆ‘Ž6tGg¬« Üt #5']Œ8AïÆ×ßEMÄW‰ >°èjcòè¡LÓ¡ï¶ÉýŽ'“°u^ÄäîGï ±ìa"ù @É2ÿ1w<GØàÿÓ6¾}?¶ý`…Çïæv÷tò7Øþ}Ÿ|gÛ¿†ö³}ÎPuª(zV»=g&ªhz§©‚¼IÐ#ZÆ)½—\‡ØË‹Ý`õµCÒI¦¸5鬗÷l #Áí]Í~³—7$v8[#„˦£ãM8Lêük!\%´\ÑÈf Ü¡¦UÁD„XÓJ¶¦=Â"ô°—ð"H(ñὡ_¦­s¬g'HK[R“Ò–`-:,¹G±ª\÷‹U¶³›'ˆt Gi±kfjÓ63a©D­ØÆ°§•5y† ô§¨bü|=DÌ’tb“IfȉשD3¼ˆ7PÆ ŸŸ‹ûÙºµ¸ÇKrÚK·¦w’*éFÙ(0“•Ð^ûl<˚žÈ8“i}JÐy‚¦Ì¾,AW š$è’ÁX´´Käb¤ÆUzbß&èbxб)NàÁEJ”Šô4þ§Bè Q=NÐëŸÊ£#Q– x”°W(~™U‚jd4CðÁì†#<Æ«–¸*,±†‚ê.>ØûÁsVòg,šFÜ8 h·Æ‹dÆèy$OØ —¬Ñ\®98Úr¾rqŒµôš¯Có*Ûïekg³fpÕê¡g{b}MÐ ãTŸDdÄëiÎÊ3cÑ®¼Að–¥K(¼Í~ÇžüÁ?Y‰OX.gìq–a~ï– ‹ÆKêm1T|›ÇVýâ»+Zî¥h"n^¥ßªóödÞ®öaxEó¼´)ºVµ›²FT3¦TØË=Ô|–°0Í&è )aÊX°wš³àVQL¶ãÿq ÷èlDz“Hªý_w÷¶¦/±p”1·5%|¯‰~ˆZ~[c¾òî¿ïóîÀö ä€îRð“QÄ<ÕïW,ßrRmß{ËYß& 8ÚÔ'Äöó¼ù(k§ìI·ˆNö&ü÷ì$€´à=åE3’`9VØ®nâ5ÒˆÙ6ò°ޝN½~-6£vÏN‡M\\\?Wlhš(ÿwõ\ÇxÙZygôè ˸/URƒ‚õ cÚô´è?À¥­½8NëäþDíiñ‘ˆ(‰OëG“'B;ëY73îëM;Óu†?ÅOü¸Øõ§«¶Ó5†ÔZw¶Ä­âA„ÍÏÈ]yaÌY¤ÇGkö"·,Å­“Et›+60ÍXnž!2/}ÊàŸÉ‡ã;zÞLbÙ¹ò\\R¯4ýåýÎ)J:ߤ>22‘5x€w]ÃfÿE‚~JГÂÖõJöŒN¤­îH¬ø¥Ôë´†%Y )SL}’Ž ç¤<Ú¥á|Œ¤8ný©Ä0Ýñˆÿé¡H!æžqÆJ~°’¶QѽÀ[y¯#gñ†_µ,—¤ÉD”*™çêÁ TX_Ù4Эc~qPñ1]™P×"’«B,"bçA[:?(de}úqÄ *8š^. —ï>FU’L`¿±|;ˆ3aé¤h{‹÷PS1Yg·¢Š ÐäÆWZ \)Axn—Û JÚ.ý8µîÇ6»çz¾R]ÇW„mÈ‹®î3&‘É‹›*N›ÊˆvúÕXOÙ­ Cn/T5sf|nû°W)<=e::+oÍÒ}›F°Ìܵí¥†<îÝš^qÁ¶WüücŸÁ¬B°.MWoÜð¯ÉK&HüäÅ ²Ù&UHþyw÷Ð6c>“³õm‚.¸0³’Ÿºç¨]ܘ‹ {õÜù*ÊÇçsXZÒ86S*7ˆK` a<ÔÂ2TR(3ìÛ»¦1̧‡Yþz˜ÄIKÆ÷¿¸õ - ·§ÊÇ& Á­ ÙcêMãs Zö M«¢×ÆïAiÔ2¾RÈ>~ƒÿájÝQendstream endobj 21 0 obj 2002 endobj 29 0 obj <> stream xœí\KsÛ8¾ëWð¶ÔVÄ!A w*'q²Ùõ$^G󨚙ƒb)Žw,Éñ#Ýšÿ>à Ý > ’,)vì$‡4F7ð¡‰^D ‹¿ q<î|w¤¼“ËNY콨‰‹“·NÄÅŸ²€ÓÇcïI_7Œ"/òÔë¿ë„Ažg¡ª¸F^šèÿ牧¢0PºÂ¸ã{Ýþ;Q$ÂëtúÿÕïw{Q¨(þû®® ©ü‘¡ åÐç‰ÍþªQFºÛBª^#V¯6àamÏ߉جmëiÖ·×íÅ¡ˆ)ýgš ˲°¢ö@ÙGC uf¨kCjJ¤EãHkLð1Y7‹òØÿ@ä5‘§D~$r@䑌لÈ+"=X:uUx»`u rÈWDþ+@f/!‡CȬ9¼…š¼r ~Rš•Œ³ “ieì•Aõš²ÂÒdµØ¾1F°o¨>0¥§†:4Ô ÞsC½4Ô þo¨Çk›«ôÿ´ 7‹d òoÏDŸC;€¤Óß@ <*í'δw”¢q–ÌXŸÂV¬‡×°³M¶!¿oHÑxÑFÞ¶E6cÜk’Ì&ÞÁ¡L ¤¬‚Ó”˜ð”3.a…— ck}™½ƒÌÎ ‰Åa–päêâªo ›1;3ý•C›‰Žk’#×hGwW‹«8&Ù9¬{ ‡‰Ïš 7·R˜´?ÚtP#.¿€2â2Ôt`zˆjs mŸÈ_`)‹9xÛ´ùZDµ‡sB«ûÙåfð~aä’×aŒÛ #§P—Pª 8ÄcX—ñŦŠgWÀqæ{X÷æqñVM5 wx®w®™oý²ÎDl4ö£U»éÐÿƒ¡`yZЯÍwnô¾¯o:ñ28qu¼d¦DEôp-„ÆDÿŠÕfc?Sõ'¨T¦³c¨>¦3§¿XeØV‹­vìf˜è®QŒ`]k¯õ•dpQÖ1 UÛ4ÎøÇœÃ.ç°Û“%‡Íê[ØñJ6*ßE@ ¶{ EÓGåüï*|~ìêOÉmކ|Y…‘™ˆ› °N$¨9<Ä‚­Æ‚¯(æbÁC¸3xµƒ;ư~‡-m. °L–áâÑ4bYiQ™<¡ùÛ=M¥`lešÍI;À) g$ò¼\ö üz4ókµJÒ°ð\ê~¯Œ³Þ&PtǸµ¸ÊõŠ@©´Y›XfhV/¯Z†9 l{çt2Á=Àåß–Y ©#j6Ï,Ù|} ÀÆÔ¶P< ŒÄ»ÀKH:¯M°%acÅØ/îß± =öù– Õ1]Ç|½[ É\JÃ*<¥ìð^ñÅ9qk’ø²‡©˜ÍøPæß|¢CºG¥)‘1k×uÍ.v$ØaSAúœgløVk õ³Ñáß$1¤†ìVó2ü^ƒ.V¹º×þg<\@=`LÔs©d2»Yt€œ[Ý,î`.ñÔ›°d]à'„¥NtYB9‘ ‘A[ñ-ð"¸9~1?³ÓðÀ^tƒzëtÿ(M9wüÑûƒÜßtã Ls)¹ô{¦Pû¡­-ÓöŒHíYò EáxZÒ<ãÂXx„!?·‘<´»”a‚Ö½çy-³ ó¨ð£z¡h^ÅF«)Ä} a_–ÒL]öi#«1¥Q"yiPöë­SÓMŒæÀêãm¤2 *ª©ŠŠÕH›š!cdæ|öKžXÍû’‡C_KÊÓÆ»æÁShv1(´ÛËÊ]^c…48öYΉÁÚÙAy © ¥å¿¿a<áW?ì£+ÆÙ|@”g&@,0.üì§`v€.i¨'ÖždñÑoOpŽÈZ¹ZV]–Š/æÖ <Öž’í¡ðÝÂMï,c($:oï:É9½oæ’¦ï–9ì󣯷^-lA}<eý~Ö)ëÛ°£UîåvrÛZæ¼çz#“&MÔÚå½+¥Ê×2ìñ»‰É‚_›süƒÉ~“eU™í^–ÿvæC£gh²7uq¿«ÀkðkaÛe£@ÃynzÜ][H¬ÇúU6›øºêžúþ͘,ŠMÙƒÉîÐd¿ß•‰sÞ·EÚp¦ÖWG:lHøæ!!™û¦Be x9'‡=ÝR ›…Jùàã1’š=a}µ¡%ÊK«;cÜøy†ÑM¢…7,"OÓ2}YàrØ|–ðU¬%5Wÿ¸²n§¢Z=¶«‰€`1FÒ YÒŸõ†€¾‚å<¼ñµÍRdÊTÊRëÎùœ1”³Ha”öóE¥4b±®XÕTº×LT,ÖøÓ SU*2 sËšB6˜çµr Á¬4Ãf3‘g`ŒfŸŸB«£Ì¤$‡q - +ž©k(m*|,º©†‘HQÉF£zÛ}[Ð9ë´lɆ>€æŽÛ.J²ŒÓ$õWÉ=‡2åš-Wr”™tSéX[SÈvÆc·ïK˜ªÉÈYgg êAu¼âW›jŒÐ£K©eStó¹+ÄÔdþ‡–q“Šk¿û…W´ó3ç|¬r5£ƒ%ce2"3“¹«»•ųz±ä$óâçmÕ–¶ìu…Rº²ªn¾ã0ÈíÜ£¦HûM‘º[eû—Öriñ´/JÊmÎ2—"³Q§×5ƆjÞÔXɾ4­N õ¿å9áœÎ1$k]¶w‚æèßìÿLÇ—§–´‹“L"ÓJ¸Zá1â}í’Åy˜†õœÝ⬼xùàd,üüÀ–â¬PG–“»'àòÈ5€õ†}Þ)À ~RK¶Ž.ð“¡žÛêZøÚ!-l†lØø6l†žžAÅ}‚ŠÃÍP‡¬O¦sdã[ghAdã™f¢1i63¯5?„³"ôóçÇ™“‰×Ó¹ó»°›~²épOÌɽÏ[ú‹œKWoáH1bÍòWÂ1…ãw~ÌR9ŶU ½Ò1i+ýtÌÖrºUï÷;ÿÑÿ5PWendstream endobj 30 0 obj 2628 endobj 34 0 obj <> stream xœí\msܶþ®_qß*uz ‚ Ùv:ã$rã6©]Y™¶Ót:z9I®O:Y:Ù‘}#] ÞI§6‰âYÀXìv÷a’&BNRûÏ'—;_“óÛUóäà ps¾óa§L2ûߪÃ'—“/Í@™Mª¤Ò“ó4©ª2-j¬b¢sów•O !UN/wþ¹;Ù›f©4«Ý/ ˜äE¦vßx¨l ©mO!Eš’b ð£L£T¢HÊ‚?®¿‹*Û½!pFàï ¼&pAà;¯\8!°„­·~‚x²/hÉð¯Ã?íbJCÌow%§ú9Sm›œ Pó•§Æ_]ßzè0 0pcš0úÝÅ(ÅúÎ ²ÓØ œC¼gÎáyŸªB&&ëׯÿdeT[@d³Ç£âÅ×~‡ßwønK¼¸ˆ‘ççÅ–ÃÞ [>*…ŸŒC[tû›ßäA‡‚clN!RÔL°}Ý@ðS™‰€7°õdôÊÓ”O„JRE^S¥Z^“¹!\í5ɽišh¡ C“doª’¼JSeš‹¤Rº\m¢4^˜åÚ,IÓ¬ÈwïÙ˜kß:c­ÆnyвÐv‰*©D–®hîð/mçªHSm®æÊ+Ó7KtžÊÊN뺾"ðªžLfnZiÁA'ÿîÁŸW™,š-(ÙQ ý{«ž2+<5…ˆ¹ :M“L[bšA‡ÿ±Š —NI:)„–æÜ · © ³_M ”åZ§ïy¿g¸U(awIc¦qfhôi¯JŠÒÜP67›†\†²…ùZäÒPÁã¬'ÊdZð¾lR6ìŠú^x×L[f/Ã`8´0ŠÁ`hní§w{ÊþmØÇü)ªÄÐO5¨ -›N Cu¶š»*X2[FC‰TòQuÇ—å÷A+b¨ì2 ydVíþî“õ½ \3>¯ïð¾Ù©A6£¾÷+Ž’Fûäea8èðÔHßq}À:V»º®GðÜNád†»};xÍ!W='˜ROÃÿˆKŽhe -^Î’ÈAfð|Ÿ±_Cò::˜)¦´È%_d[$#ê ®átp !uz ÅqVŠñ ˆð”Õ3™ÕƶG¦sÝh#)ÊÄðã }神.Õea”lUXvf¬ƒÔæ CeUÑ«D<2Œ¡O‰°5xðth -%ÂÎ ó7ë{c€€/ý©a]É&HvÁÎ’Àgòšq¬kU9Æ9â-¤YRb¥…TÝ—”ÔLG±íž!=3÷y[ëóykm›K‰4¾Rå»o¬+©«ú®§­ò¢2WgײŒQ=Fµ”ë97Õ|×üôZı®ºbvm),H)Ö½íŒ i–‚b¬ÊŒ SÛ¹h È 4Ôþ‘Õ,™cžuÜÁŽª0×® Î$Ø<3 À’u9¹]3H¼ÄŠÉ8·A½¡"fÌLÉo ¦¬»›æ-Åç|åÕ i¥sê@ ”qµAvÕðM–HïVŸsÒ:G‰–Yàù4¢\ˆ²™€D9Ø"R¬\k8/‰möÎ^IÚ(«ÐküüÇ{ˆ?WcÂ\ù2f‚"ÍóFUØ»}»;æ¨El™˜¹>ÂÅ[µÁl¾ õX3<Þ—OjR™±FÕªoòS×FÜRøEÜì/ [uMs\[„®ÃGÈN3–š“xÞÎ’ÉÂ:!`Ù–gÙn١ι‰d©h ”ù߯ÂÔ¤Hø¥àù>üN@A”Ë`Ä  ÿ ŒuP.-Ó^GÖ@¸ô†Ï`T’>ç0@9­ Ûãðñ;8[t V³Øê%\^:›øvxAà[¿"ð-ó(s¥3RÓXîâouài=×´ÉKœ† ò Á—±³øqlZd{ÒñÒCß‚9þÈÈ <Ëădb §`x¯ †ëZ<¤Q¥Bóº"¦–°>“NÝÄì*ú$¼JZ»›jå©C—ǣɣ˜­o -12¬˜X&•2ã˜Ïæ#CÔ5Š: ³8êËBoüþäÔC%}»²õ¨t}8ϰ“azæ»Øy1dû>¨ž«š¸Uô´¯BŽE²©´ä*È™4‰`Šž/Ðg ߢ€üK[c£• O·ÓÄ>#FÂQ^a0Y©|*9°Ëê/ìÃò¨.+(ŠiUwøÌr)PžÔ,“IÓ(,&úJ"ê²4Q¥râ”áyÂi·‚ŠWp°*Ö*ak[lÍ;©RÊLÝzÛÉ!»T=ºþSeS¶K £¯Ò‚jÉ!{H_é¢ÿÍžNÓŽfÚ¦1ó°|Œq i tV©iš#•ñº+\ˆ“ÐíZÑsŽ‘q–Ï\}ö9ËÁ"PY…“ç®´+«\^IW%.Ïë«‹•#šæ:ª”KGÁx£7Ò(4>|ÞÎë1.Ì0ÁC¢G¿÷Й‡Þyhî¡™‡þøSÀLþ$bÖëåq¼‘ÛNþ¦ʶì”UF…h/¸cƒÚnö{6ಗèó3›e³S#h‘œé°™ÓWÌ~0† ÍÝ/è¹Õ 0ËÈpÒ£r]=–)¯íU¶+‰†¬°­‚èT¶|¸(=¥8âÊËö‘‡.;’?&q tÈçñ˜°Ž¾„`4´ä…²“ýv÷ô“^WÓ Á—Áº‡_ºi®cALƒ)¬ÄX+ÓìQ6»ªŸApèØW@Qzù5¶{¹á¶× rÞǬÊfOÿìwFï—)(ü6 Ñ°pd~” )ÛÍÖ`A`'¦. X‡÷´ŒˆÇNaS·õw–XÈ‘X?á)F… ØÏÁž6¥]Ù:Çû‘1톜áï<äÂÞcN_úQ­ŸÜèž>>QlÁ°¦ŸA }‚°9|ZyÇü>‡ öʱãÿˆ,Q‚ÿ#E©åJbªÝ™ ~¡LîŽü 3 Q…Çnÿ_øöþhb·µâåûþ5óÉHq|¢{±»ÛÅËçtWâ…×,x‡‚ŸÆ¯d*ÃX²4÷ÀBÓóÍàÉxføðûâo e¾Å§…(êÚ£Kä¢ï6{böèêUð×<ã øîs0dê–ݧ¹MR¤è@‹N[ç7VÚ†6ÂuL½0UKåÖ !ak]réÂODZÔ;_—à뉇.šG«ÆÛ¾˜1‚i„ŠÑ_B¶Wö\Ç—Ê»} 1!1±ã)ßN‡EÏÀ×qQë¾ßqzfBvXû‡;5ÿþ »¶µendstream endobj 35 0 obj 2884 endobj 39 0 obj <> stream xœí\mܶþ~¿b¿y·ÈÊ"EQ’ÑH\»q‘®½4E±¹½·æ^6w{‰“__R9CéÑRÚ;».bß—YŠ’Ùá¼püÓ,M„œ¥öÏÇWGOß³³»£ºyöö¯-p{vôÓQ™dö_ÝÀáã«ÙW+3Pf³*©ôluz”&UU¦EƒUÌtn~Wù¬"QåluuôÏùl±ÌRi~«8˜0É‹¬n”JIYðï7ÍwQeó{wÎd}Oa6lMà–ÀJ3ì_«¿™]I³«oŽÌï'ö—²?V6ùÚïáßM ½òÐS½ýNao=d¿GõÖÏ!/õ¯ñ®.Óꌑ.Ù5îwݳ;Ù’o4Á–“¢s)Ú³ˆÇϺ R3FiR‚X(E@_xh¢+QTT€_‚(ëseuP{ƒ—¸xÕ@ô™YLóÆ)Yøù2 h†±‰/àlÑa8ðz—ƒ—Î&¾ƒ¾$ðÏ |ÅAË<ÊAé.¯%Ü ÃÝà“ò­.ò»læZ¶qûÍÄl_ÆÎâ=H DÁ‡Ž—úÌñ]  ;ðY$$;8ÎqÜñÍ×Ò!&‚|Ôñ|°„é<ºtËÇ`Öú‹‡þä¡ëP§Ksùr€KÝÃ` æÒò öö=…ÄŽ—•:8eÍ Ø¯˜ñæ:6lׯ†y^£ËóióÈ( ʬ>fÍ­½ÙGáRè «ð}&órƒ Í—6º¯•5Ü»aïò†fŠ“M‰ì1Ñ{!v»¬áèä ›‡£º¥4XU) ‡ŒÆOi³ú¢nϦMâdi¢JåÔX&ò„ÓnC²°?k•°5ƒ­ ¶æöõñAb20[Ÿ±Ù}÷ ‡³±31†WÕøãEZ kq¹Xô+êôŠ,÷%Ç!VÃáò d@ D±vUtš„Ÿ\G¹ÔÍÒ± …#æ.]\–£PY”mΟœs³¥EY8f$×]Vô•܉¯:M{j0Ó6’;´ýD¯Ñ“E©êè—;e€Í^ö>f³ñlv°…ùÈžUÍœºbwc¨Æ˜ÞüÞGQ’ŽÝ‘  ŽžxÓšuÅY=¯§¢&>KL!Ëø}Ìî_h½áÃ59æªÑçÜcåä!SNø˜&hä_§–W†¢?w@§ü6ÖÙWÄZ„´UsN¨D´øÊ¯öÛžæ>Ð\ç‚PSX©±V¦E~ E%N!ˆ<ôHd‘öú<ÜëÞð·ˆì•­t AKÀ‘ F¬~ñÕr 1œº@v kõ ¸`ßÄÚ/™%c$Æb€£Q>e¾‹Ça0<Ûÿ"¾Ô9':xNQãàî𻆤½ˆuØAÚc±xxhóQÙf¼ÝÑ^$¤”Æû»‡\hxÌáK?JÅ(¾ ð¦qÁÃÆœ §¾žÀ3Q^Ä×ÑfÅåXQà3öÁÆòø•Å4;å'ëDZä߀¹¨_È\€Mbwäøê”!AÄg1…aªÇ™èá1ó•=Ê H>vÈáÉç¤P ŸYýÝG×Ý÷„Зèý{BþC¯3ü(ê_gÔøžµHrƯ9‘pLÈêÀÊ/Ï• }±:{ãS¦j{ßA0êÃ#ÛsR‰yXt;@½5øº_=tî!bƒMxÐ#5ÑïªÄoèVˆ‚Ÿ~ÁiÜÇý\pú‰q㨂SQªXÁ©êœ~½‰.m&‰ŽžùzÓs_´9£Ïïêúά*xY(¯LåðaxCÖý:×[§\5­Sº ¬th<µÎ¨4õ­ß-ï°î-WÉ"èÁêkg¦CU(ØAk³#••ngqÐ|º ]&:¨sE#"‘Ü-°Œ"a8y×¾'…mp‰5 +0[‰UpE£Â¼ð‘3¼¥Tá@9%¯ë4ã‚ s^wIPç®™õÇfg† .añ <òfà¢}/!“BW.-ÅÁ?RŽuf3ÊJ–¯֛ŭ ×õ©ÃÿÜ [ýS7N¯þéÈÇ'Ø0\äÆ2Î4Y¯*¨C.üÖbÊk Ã‹ƒ\‘2ãJPT”sW=dôô™LV”6ªÖ5ÏãÊÅô:‹ÔÌ{zˆ…ó6ä«­½ÿÖ“¶FÇ‚,X5˲:Q-„™½cb9$7~NnT±§h¸ÈÍÎêo÷< ë¾ëÐB†Ù¦ÚI­æÑ²€@‚u¢U^ðWD_ðº3ðŽkdùm•ç…Q£ê|"µ ~‰ÃÕ>}ݨ‹È°ƒk#ýlßÜèjð…_tÞ)%ÏCgøpËÖ”Êå3é]Ö!™ê©öÍžåèg0‘.H)ÃØÕž¤S ·zòÿ¥Ç@lÌ¥ÉgtwLª BüúQë‚Þ€¯oA¿Ãª>—>TŸÊë/PúðÐw`2ôçÇ9TÉb<Ž\L)Ú¿ÇÏÒ0]ã‘Z#:)eÆœC°ËÇ*z±:ú‡ùû/^êS^endstream endobj 40 0 obj 3049 endobj 44 0 obj <> stream xœí[m“Û4þž_áoµâʲ,Û`ÊÊÀôhà La˜Ü]wÉ‘ËAá×#ùE»¶Ÿ“p×2%½ÝÈ»+iµo’V¿{"Œ¤'ì_ Ì®F_¦ÞÅͨhö^~Q›‹Ñï£,Œí¿¢Ã³+ﳉ!Œ"/síMÎG"ÌóL¤%×ÈÓ‰ù'^‰05W#ß &¿Ž¢(L¤7ùz4ùàµ? Æ‘ÓHKŒ“PˆHæþ40$‘T©¿rÐM0Níg­ýk׸FŸÏ\ãÜAžâDkûÏ‚± µR:ó/e¾Ç¹ªhb)RÿáÄñfˆ‘ù‘ÅÕ¸ ¬›Ö–8,ÆÖ+ؤ‘Ê9—³²ÃjP9Ô™í‘ñf¤+j]ñÎM´=îeñ¥µCï›—C ªJî2Î9ÕšFÁ–u³ ~š|5’±U¦ŒæLæ£×þâŧëäþ˜ØFÄ‹µJØÃV[j ùpÈô¸où.Ã9M“©Ùm{ ­Ì·P8Ckòi0ÎýWAŠTä‘ÿ4ËPf:Ïüç„T±ˆ¸$ΠªÍ¨¶Þs¨€ó¶$bè¦)f¦g‘´m±ê"¬È”6¬ÆŒk•WeNÆú›ŽƒY8o@ dšYn_ÊØy2)Œ²´VF#¹8LÒXYnˆ¾>uЗ¢dËd¬C¡G¬Ø¶m{D2L33ª+3hiç--(•MœrµXAy3†¾xãkuÁ92ã.–"M¤ÿwXuI‹Òu}Nÿ r3ƒXHÞ C5¤QngcQþ-‘f3íÂÅ´ü im¥‰#•IÏÊ1bºÑXf˜tV+б$3î(Tj`½½ ’ÚšHG <â—ï#;hé K9è‚×`XM€Ë(kž¼$ðŒ@@F¶&pCàS·™ëXÚEŸøŠÀ§> ¬ÅߚŊÀœÅ’ÍáÈžêçÆ…hg¸Xzoh`•ÖdÖúµ…¸ a0â0õÁyÛî#rzµsõ!#'Ãÿ(á5• ¨Éoıh EâÝ=テÉwA´U¨åÛH˜«½?ú'öLçfÆ,Ÿbàz ÿ]€ì¹>AÉÙ±ó%¥P¾ºâ9 :s]öžÖÞqÍÎ×< v „xM½ÒMÁïIÅ$aúšwI(iÿ¸кÓÖÙ]Þ'ôf|öƒçL A¹ã^y.åü´[ ½)øº_gZ8ˆÔ`Þ\è=N·pZÂpql?È¥ôÉœy8¼>»;Ñ¡¨0Š)²N †{pŠ| ÄÜÎje„“$M!½œóyutõ|ÝíP«ï(ë¨iï®ãÝ(¡|»JØÝPq墠ðØA/Þ±¯Û£òDN&žÉ_…¢Ò‚\µJ b›Ý®--xm7K"Ô‘N¤B{¯˜äB(? Ò0·gv4™É]ìåœ uf/×–„wa3B§‰‰a$‹ ÔŸ_Yîyœ›ï×îûë’ÃsÄá”8L.çVŒ4±‰hêDˆâ–Û¥BèÄd05f=µ–ýfYžWý*‘æÚ˜ŸEVÒ&«54cd ×Êüè³Apn‡}j†®¥L”ÿc`À$U䃄¼§B$VYïP!òß9Òv¨ áž(WU(Í·‚­rê6$l¾§î²Þˆ•\·²9ÑîYw¯ov̶¸F…ÝÝ3„5Í”·²â‡»‹H~£bÏÞ‡+™E|o³,«E¤£DÓ…œj¬r·fÃуšx‡š Z ã÷u¨U’òbоÃ~7×áÃ~´TXSÖÏÉ:kȈ ìSÖ´‚¶Pß¹$‘ÿ ä€'Étê¼½zLÿ ð®Õ+V•Ò¨0+Ê× ¥Áe&Œ­­ 1.9O‹Ë\•ï•åü2—öùä1vCNª%{hR¥ü† ¯Âš1uX°}Ž¢¾ƒƒCÁ·Nli÷+…rpë.á ÿó!$ý…ðÿ "»Í–~³J¦Ðk©šup¨ˆ¥cpåÉKñ]G’gr0s¼bQà[b¤óÞ‚ÆÁ~û\ ¹p=Ðc¶¯WÇõ·kÏÞïŒ[Ýõ` gõxíú-kƒeÇ—ðÐ’•ª5NUÑÄËìÕ<ÁEÅ5”˽\·ÓîòÄAÝ¢\Õs 𨈈8SâóTî8Áæ_²]â9Üù± ý%Üã´u?ðbîÖ\ÁfµGI£,f›äâÚXTËæJÑê.ÆÕH«í@p‡Â¦žÃ˜6 *”ûpþ¿ᾌá> íJ»Ða&]Â~Ï%w]){ë1‘‡SÑQéËǪÇ{owñÔ-”;s' (ÁGìÄ=‡"\${RÂ5˜iÖ5F†Ë Yç:­uÌ‘o˜zß01²òäõø†éø†éø†iÏ7LYv|Ãt|ÃT¾aêÀô–ß/¡Læø²äð~ dÄø€é¨fá}zÀ¶Þ0©÷ÿ¾wßKë3FôÉR;­yo–ö¯5ýÿ<^®`:>^Êßâã¥ãËœ°sƒCä`õßÃ>Ò9™Œ¾5ÿ*kìendstream endobj 45 0 obj 2372 endobj 49 0 obj <> stream xœí[Ks¹¾ëWðf2ðÌŒ©òîÊ*¯½/™[9dS)‰¤%%Å•¨¬¶RùïænÌ|òä]§Û‡6¦4~|žˆDª‰ðbq{tržM®ŽÊæÉùYMÜ_ý|”'Úÿ)8½¸|1w•žIa'óG")Š\dÕ¨rbS÷ÿ"dR&&ŸÌoþ2ÌŽµPîÿf:wd’fÚLßê›@Ö”²¾Y"MÆ»3R…^&ê%siUpÖmÅ =½!ò–È‘"?yOäu{Cä ‘ßA^&ζbÑ/†îà¸Øí2,†V¼!ò#ÔäxÉ”›ø¯ó¯œÉ(g2ïŽæˆŒæ}Øj2Ÿïõ} þŒ‹øbãf"«kTFfIßÃú6P“ƒ{±Á0ýÞCò´6ú=9—2œ[©¼+pÊv_xÕ§ªQýéL'ÂnÉnˆ4B:E1òn&’L*w4YãÄ5êÔZ¡§×áûE ÜÒÝ:-¦ËÐæì&sMYªœ…ר ãýg­D6½ªûäšN¬nC2þ傺¶?}(‡Ê¤i¤/¹˜«Qm©ÁfÖ_fE’ånÿ«ñ^Öƒ¤Ì`MPaä¥H%¯‚‰Ýê®ÓVÙ*Yå³RŸâXŸ½²Sæj˜y?@’1,á¹Ýàvõ9›5ØŽ-ÐÞøº_º™Á2Þ葞è*dµËx·Pc¹”>3‡÷g¼mv­µ?ƒ¤P›ëqH7Œ÷dØB-l€v÷2B:^ïùj:ÇðHâ;ÐÛÔ2<ýßG…Γób"Ó(.ZÉpšÚ$µU2¬]IŒk)sT‰$•¹ßf2ŠÜfÓ_gR'VºðtëÓ1…0ÓKçZ“LIU†¯\Ê"Ï}ør¡Le:·>Å ô"š"4å›ežºåÜN¹›ÙýqËæc牲"õA¼dÈ Ëd›Dß›éÖ4À²"…Í^|ßT»˜jkfU¸Ñ6Õò„ÊÝ™ ­Û™ËJtQ =è);Rã‡÷švæó椛FZÅR”U Xºð.4žyɬIEê"b“1NŸø ÈZ–˜Üú“ˆLgårB¶Ñ|g‰Ê–†b‰ Ϋà4ÁŠ«³W!”ª[Û‘5 4¹ÿËÂÍ¢ŒÛxU$Jê”O}I£°¤‰¤ y4²¶©õDza¯˜¼ªkU*]Ä‹*s´¢4³º3CŸf¸=™P^Ç”º&Ö%µFšô¥t–Ø"w4_º“É”RÍoejø2U%4/–‘î°)çÛÝð}+l)¬J—•NŒ*2¼D8›tA­w\hQ>d!jÕ£Í Ö%Þ™¶X&N¾ÃÔY°©TÆ»H¼‹$âMc$¬ÏªÝ§´¼Æ¦ŠªìÞ”z-»;oVk¨deÚ¼®ìEÙDhÛØ ›€mGS$dyí¶ŽÕ‰ £“ã*ô/k×4¦^8 %.^¹ ¬7zÔ&PÛƒûÈ÷ë謺.¸†Ýîaëbœ °Ù~š’õ; ÀH^2®ë?BUþ4ëK{7œª…ÏrÃq 5˜„a‹ÀY3$¶Op°%äeæ…?6Bظ!4êèó³Ü¢wDžùýgªëݧáöwU5†iñ`fýµs b{ VvÅA(É€ÝDò;Ûý’¸0Ä“ð!Â[?­¸F8}QiŸÁ‚IŠ™45‚þ¨@BKGØ7`Õ¾'ò”È9äý:°!/áœÈWp°·D£#\ð9‚²ŸïIŒ7Ø âlïDž ‰ƒ}箋Ž>ö¿u¾%’]&±#ñ <,mè‰ÝWHá´¼¡"m ”M µ…u4qÒ«W!æBLW°Hí¿6*Á‰ýêå7H%ú2?t fÕùlÚkbí´í›-ZbKY|Àö¯iÔJÙÂ9!ÖŸä¬ f2±"ت¬˜]]kD¨˜·%¾‘å–^²â6Ý:¿ú\¤é0€v‰ñ•üäyІZmlRaÂÉáb7œ×3„DðD‰o¸ <ÑìÑq•îƒO¼à°ã8ï;_iõ@à‚áuøN1‘<ð˜ú“£ãsäqúà_7íeD_—tî†k(¿ <2-æ¿ NHÉÖíµD%# Û¤¥E¤O`‡èÞ–¬¬¸³E-…Ž/‹û ŒG8Âå8›m…nüôcüÅÇuˆ·tƒòD™7½÷ ÆÆc)~IÀnêÞVHVÙäÌÍR ‰ÃS8¿nT.9ÿýÌ-!2o…€F´ÖÝ÷Ã!áÊ‚cöäOäÉ/ø(–°hFaG0 Pð’$—è’„±n{äFïw"X}@5&øv„Ýä0†]õ,¡¼øéN0"»¬S•«YËV[Ñ +Iá…MËøÒÌ*iØ”NKÓëM;$¦£Jõ ´m#¿ÙCÝ—s=/dÖÀƒ>#N¬¸$¬¸aõÓÙ¡ãF¹@éM[ʤ—<"R×n°àÕ@fÔM’*ÑwDHDî!Èã9‚À‰$Q{HòåxI¾Þ[%zA¾/ÈÛýU2ø–™‘§ã%ù¡-I¯é¼/ÀŸÛŒ}‹:ðfƒ¹aø#®úëÜ„}§§¬„S|,“W%6E¢Â8Â:Ž'=`ÁcÂ8× ~Ø' 5]©žç%Uågr•Õ[0E¯V-²Ï…¦çßB% E”E 9åÖª4Æa«Éƒì/üë i4ß`•èë–rò]†ùÔ„³´yº¡]º\[ì>¨¹€[û€’aü ¾ðiŒ³…A!ØöåÅÒ'½*Ÿ&(bä+T `Ëo%oómÖyÒJ¢Wý‡d0çŠò`F–É«(’\˜&yíx‹v2ˆs1 ƒÔ2®vgÝ޼X6ÂïjR!ªÀÎk–%…ªYÞž˜²L]}§ÍóÜ­€ÁÞά^m“››Ä•‘Üãw›éÿôÄ*†Å@¡Q¥]²Ì²iÖÍƒÞÆ¿.Ô—¯ª)R£ºFR~oŽV–sÁîøÄÊïW‘•ºÎœ ’™ËÐU¥„ž2yçãB*²a•L!wü|og‘ÌàSÔùú½Äг+^C3/ë›òY">Õƒ¯ù,µjâý#ô_Ølàú±Àëé†õ"öá¸lJÀå~¢ë"ѹd(:-o[Ö±‹Yê}´ —«û<æ tÝ©”wãøðö…”°ˆ2¤¸WòïáÙçbޏcóYî±ã¿}Tp;&UwGJGÓ½Ä&¤G,87fs\§FÞvèçT=ÌAÖFåýèGÀ÷m èç¢_vREûÎG\НËÍMÊ<%¸"Qf8°3ácÁ·Ømy¾àù4¨›–•l€A·¿¿Ç7&r‹Œ3m#\~å ]ä´4- ¬b-ˆL‰|µýÚ‡~k|,ª ª±ü˜ñ¢²LçGú­üï%bàiWõjݤãÑ;*ž©£›?#¼ì´E·tkÐÅvð þµ‹yá"¶ ÙM»® £ÕÝî¹Î kKD´¾—1”âïY3Cj½:ÁÚA(Öo¢ôH´Í¡OçG?¸¿ÿQãÌendstream endobj 50 0 obj 2729 endobj 54 0 obj <> stream xœíZmsÔF þ~¿ÂÃËÄœñ®×»6 Ã4…0é„—¦¤À$—ËK›ä޼øÀoïú|¶d[gy)M¦3/ÊZ+ë‘Ikû>{a ¤fÿ atØ»¿n¼Ý“ÞlÙ[6Žw{Ÿ{Ieÿf XzËC»QF^¤Úîô M“ÐäV…§cûw{Fˆ@%Þð°·á{ýAJû·òC+±‰”/J)$¤sIêÙî|q¹”I•1° Ôg²¾ö‹ÎrNV8Ô*¡îŒW[dOÉæ8&uÀ‡Ê`-WgãÔ„©(g¬INV™RH\’ÁªTÚh¦Ê¢fUÞ`ç 0‹î’<ß$³°Ý¤™Iü_Ê`Ø“Pš½Šè®BoZÍ©¢f…VP%˜_WÅ¡p Œ°ç”5ü(T9è {¤Cç¢ù ók)MKi\J_J鸔öK锨q\9øê0›|&ÒóëGÄýÎ+ÇftüFâZ~]¤Q»Bô@œ€xâ)ˆÓv9Ë–Ù‰ßøõeÜ8K«M¦,36*Ã1)¥m"þ¶?ýRÜ#/@±_Ê(ŽÐ³G‹ŠÍg{¸ëìþ#Îý»î?jëPãj‡6ª 40é1JWªŸ=ÑÙ|Äê£6võî´ ;Ê).µ3mcZ];ÓÖ»1é‘3ŒŒÈÆŽ}‹éq<àp¶5€ÁNäûÎ0s0î;ÀxÜ1W;’ÑCý6àoüåñÞôRnÜì‡ìt…·¨]Yó„cͶkžt#?;]cg·9±ŒÛÉÏN×ÀÇGà€ãC[:àÃNYø6µ¹UÝÔ,‘æþíËZTdny€ìð;rÎÓ .OGyzQÏSÕ}väig÷oqîk÷ou£;òî9ÃØà`Üs€±ÑZõ€ãjGÞÿócÍvœ:3â9LjCF<ïÈvžgß9ÆÇ÷öþÂŽ±sg÷ßpîŸ;¸ÿ¦­¿ÀqŸcK„£èÃ2õUu²‚Oh§Ä^ø¬VûXTõ’C –ˆ!쨛8ÚsùŠËåÔÇ«n¹dG^â ã&#q€q³c:Ø™7qÆñ’Ã1qÀñ’ê àþõ¨ûw£"ÉŽ:êW/íDxÊùÎáiGB³£.uÆQ¶à…@R …5®Ã°CïÂÈ[Ç…Ž·­ äëŸ.ýØO— ‚ìÈ„ŸôueÂkŽ è -Ë„×X·Nh€ÁŽÌGÖÐJe±éÚŽŒ•Ž„fGæ5¡[gDòzz»Gòé°÷»ýÿ¬Çendstream endobj 55 0 obj 1910 endobj 59 0 obj <> stream xœíYAwÛ6 ¾ûWho‡Ú{µ*’’(í–åuiÓ–T{=´Û{ŽíÄIãÅŽ&½ô·´$’ ÓJÒSä€J €ÀêÒ |!½Àü–ÂxÞ{uª½‹UoóØ;=*„Û‹Þ²—øÊülPy<÷þÈ`¡T^ê§±—÷?M“@çV…Gðï4ò´~˜xÙ¼÷© †Ê´ ­$ã¾7ŠD„¾L¨ úamU=+ÝXé–y›UL–vš&EªÌS í'mºÕý';îA¢xßË~£qÄ®8.:ÇqäŠã¢CGLa3 í ãÒª^3ÒÔJ—ÏýÕŒqãä5+NQ$Á]Ù’’gÞ¢ˆØFìSù˜T†ÍL W&gkë«¶fjëÍÖqHWWã8vÅqÕ!ŽãzU÷ÕÐÏèð'Ϥ|¾.ëäãÎ5u誩q‡š:dzƒ'}øÒ9Œw®0¾tãÝŽ-nϺ#ÛdPÛ ÷´ái]3é¤ ˜—]kêÀUSÄKgMl­)ŒÃI–ã8qűìÇɶ‡aìéÃ/=LåÏΞÞe´aÕ¹¦>¸jjÕ¡¦>ìVSNÚpß9Œ®0î;„ñqÇ·§ ÝýêTûÍMHóò /_TÀTBε°Gàk!Cm>òƒ@@’×ðPEq˜ÝÊ÷ஆ§:’ýÿìC5Çæ=¬c”Ë÷#Î|þPÉ@oÑí¦øŠ,X-² ÂQQ¢ W6 ˆ±8CÝ)«û'(Äð<–Ï„(”TõÁlNv¼¤¾Nੇ—›•*Ý|HM-´%²5°m‘ú–¸–|TDDý34pƒâ¨btÁ@ÒŸ°ÁM¨.i/6¸‘2õãÐ|½Í&½O4}=kà!?k©ÊÈMÎVø7«0TNÍæë`  – ¦Õ¥%Jã4é¿E#DôÙ]ˆ˜áÑÎpGçèÉõuú´€†ñ½ë!Ûªòà$Gy†º¸ïê‚ÅñëkýxŒ­ ›rhåIè¤y9H„%ºI~*Žb*z©µŸ¦Ú¬†ôv廑̗­°MÜÙæpf%òz]GN^^ö!.'5‡še>ÒövEQ1b“úäVÅÊo$ŒÜ±0NZ pÇ3¿Ä÷w.ä7 1«p?’ZotÁ\%=ÁæÌS~_I‘ø‰”%¤Æ¸UëÉ”{=êd,Ò­îç>{íÍêI%جYp½ÓüY«sC ’HÐr&A5П׉«IXUzSÑÄøyoòqMpYKK䜭©Z IûѺ@Kl'.ÑÒÖÒíª÷øôh0Œá†“IØÿËt4…¯‚Òã”ÃP…¾Ju¥m-JƒÏXÅW6]^6(\NÖ°Ô©Þµ)û:ˆLÏŽ"šÏ\û{n[‰w„±òÿµ"ð-%Ï,kþf¥óq®À9úšYq[aáq`h6ýh ›`×8¿¦á‰|~}ˈìœ}h÷ÇIiÂx‡3“i,…8ÛF¸9"ŒˆÎu%oG7þ¶«™-…•ªŸ‡ˆb‘ÎuL6ÕÈa‡ˆÿÖǺ.þ¾vìƒp»¬h2S!¿o˜·»&wNåªá8çIå¯u¿kcÅF^À?l¨ƒZ ¡o"rªhÿéÚ‘ÆùôEî—6KW‘Ëkd™m/'·ÔÍ íÆkšvPÆæD䌙¸·DÝ»F×O)Ñ'Û^ç·¥ 1Qy[N)c´bcPK!=$zB_WÓFØÂ¹•qUã’oðç8GÀèäÒ ö˜,z på÷‡-õbñÈÆÄ¢@,ðæ£±4Ÿõ,å'Ëxvíáìv^Œy0 DvÌ+°ñ²ŸGé}Ù>j_ͧ„ªÁE«<ŠÌ»³£©eT•j *°3Ò¸öM‡?“ Û&FÕØé«_ +è|qÅ"_²¸&Tç´<¦*Ÿy¸PÛ4ߺÏ# –ýa1“I¦ÒƒÂæ¡0ˆÎ‹v¥ýD$%ìÎꉈx-UÒÐ-(nšR]ÇWÄ€Me¥ù9°£™8òF§à¯œ8Êà±@àMvÕ{õNà÷ Ìi endstream endobj 60 0 obj 1583 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 28 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R ] /Count 10 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <>stream xœYXT×¶>sæD¬ŒC=•±Dc‰b ( bÁ†¢¨ (Ò; 3CÛÔiôÁ‚±¢† b—%K®%‰^“x}7šÜd¼Í}ßÛg˜¹ù^Þ{ŸŸÀ9gŸ]Öú׿þµŽeÓ²²²bFÅÇ„‡ÆOâ­øýø‘֙؟çŸ$IyNÛ;°p€`ØìñÁ;>w(Ü ÉC(k+«¤¢ª…QÑÉ1á;wŹŒ \µÎ}üø æ;yxx¸lKî}â²(46|ç^—È ¡QÑ‘¡{ãf»,$£#"·»ìŒHŽÞë²cGèáµµ!¡{\¼Ã#£££\Æ.tw™2yòGÉ)þá‘Ûâc]ü¢öF¹,wYº3>"$æßnRµa¹WòÞíëý¤DíØà¿0:tãŠEûÂÇì\é»k•O\øê%ñ»×,MØè›²vYRä¶ucÝÇw™0qô‡c&¹N~ÿ£)éó¦fÌŸ6=èã›g{l™5;uŽÛ\ŠšH¦6R+¨E”õ!5† ¢¨ÅÔ,jåJ­¤¼©ÙÔdê}jåC}D}@­¦–PS(7j µ”šJ¥)_jåN­¥–QÓ©qÔ:j9õ15žZOùQ ¨ÔjåO-¤fRïQý)[jeE ¤QÖÔ`ʆB‰¨¡MÙQ*˜â¨aÔÊ…’RöÔÊr¤æQNÔ|j8åI9S#¨MÔHjÅR‰ÇÉË+©›Vó¬®ô›Õï;ëÍÖl8›-6]¢å¢S´]Kÿ"^ Ö‰eâ˜Wï-~ïzÿiýÏÙ†Û~= z óÀòA®ƒ²=\:ÄaHÒ‡CÇÝ7´Ón­ÝÉ@Iœ¤y˜Ç0•”•^·³¿ë0×!ß¡Ëq¹£ÊQëø™ãC§Nœž·þÂÙÏù›®#ÊFü6rÑÈG¬{‚³æ¸£.\v»T »xnp4ò»ùÒSkëüƒ‡Ÿ€ñ°'CggÝ©vî}u-ª`42M¶<¿ [ήZ»Œì7ÿÁ?Áúaô{v/dm '¹“RT¦¨t.Wiôœ¤ñ•Xr§V¡Éd%iŠìd®5vÍà‡ÀL˜J~{ƒ‡ƒäx )ø@ñŠä~VUQ%+K¬Û‡£34žˆ7áɰIÔ*X·¢ÒhÝ~‚uÖœ0¥Ð’ÌÀq0{ƒxpì¼[´Ìì9p°+Ø8™K¡ûÐ}ÏÀ&àþæJá#øU¤éŽ÷´À£»yÚp>¾Þ¼©±4ž„ßâà­h¬€Æô ÁõÝñáf(oko'q^|¼š†©øW‘ Ä9¡$¶†!Qh°&H-P}~Õuçù—[ÏÍ8Î]?~¸]`žy}1ŽÅ:ób—h•B(Ìß¿¼ÒÏËÇvÃõRHwqjPœHjL8°ídüVoXÄÞCH€Ø Ÿ&°Hâ£0ñ/è$UvE%q*{ JÄøþA´“ïÿFLœ›žA<ËJά& Wá‘[¼•ÑxýÉÞ/ò ¥Øî}7<}7êßýÁ0·ÿÀøm6ÿ|:kÌØ³\]g嵸ÏE’Œ54Ž‚wÒ·g¹’‘n³g¹»Í%«>~ò¶ÏåXØÙÜ‹¬á¨¤°ö‹è­º¼B¦&xh¶p3ÇâD<EŸ *0_’qD/bhX‚÷‹pø(±4%fÐŒ6#ígÜ1Xóùö¼¾Al·»@Âdz®«{;¯ñ…¢QtŠi 5Ü%Q–x¬Ñõ!j; ׺ {Ü¡2X}A¦áK¤›Î¬8´-A+ö…lŽZŒü™™bì ƒñ`q³ãÐÙóìáCå5HËè²Õ2yN¾\É.[¿Ï›¹¿Û;È݃íßÞ€õË'ký‹ÙÂÔ2yb*UZF6âEµ›¸>4ÀcìÈy©7àJ 5A8‚fá5äŠp­VN&$Ö!\›M¸ÖŠé³K/G>B ùñ{°å$OÀvâ+<Äs}´÷Z¼/Kß=ôrŸ4ÛÓ}윯_Ýõz`Ƭøj’¢¶üº´»žÍnO0Ìm„ísçW0à\ƒ6[›­ÌÍÏQpÛ÷,ˆ÷C˜B³o§ÿÊlô?ܽú½A]Õ“™žà…NýZàj‹9öà+®•kÒÓ•ÆÀ£ð—%bxÂo]ÃÝxª8I]Y®&QýáDž?éÞ*’$ˆû|Ònà§Cä¾&ÃØ{ù Œp€ià‰)âQ$T€ùÓØœ€ÅX„é‰ pùžüb8sF&Áû‰3,±Eƒý[2åhî²F6ØžÅQ–X5tÇKùx#¾„ÓNí2l;´­eˆGO;áÞì0ÀïÄ $(ßð[à…TòäVPàñyÎxðøÉØÛþ} z|åXg‡‹ÇŠSL©Ôè^Ék½ÉÁIœäÍZ¾æ¢ñNj'Ò’'Ø'šD¨Ñ­cÝæ·>îú‰ëÍšVFŽVB’YdØ ú™Sû ¹ù‡ÙsJ„ŠSN–Ñ(ÕE\D™·*1-[»€óŸ&îK¬ßáî|üWæ=iôäéU(6™ô)Y×R¥0‡úE×?θ;šÃG-·;Þ’£Óé¾Sa–†T(?ñãÎÕ>Þ WMãzµâé„ëÚò›!±ÙAr,CXÚ5˜N6Á§–f™ér³`ì ¡bI;´¹Ó)*Y…w,pÝ^â?Ù‰$„it­R“‘fÄéâ /Ž «/$y"®g„XÌ4Fæüb¼ñÊ™· ¹¨u•Y zÃ>ZÒøú9xÆùªË:§Rç— V×–«§TrûöÇk÷¢õ( êÙÌàó…þF8} ?øŽÀ§sӺ㠜±Íĉ˜Áâǃͽ‹Ç;ÏrX*öYèÃâ÷h3ŠÄt;:]sªñ؉ŠãÈÀIÆ^K^÷áÁ£¹ÑÒ×]Fó=?œèE`ÔÕõ£`u,6¤5óO vÆØ#a ׈SÙ’_AŠ`C„A?p ÁØŽ.ÏñP cØ|9¹J¤p"[Ж–°Ÿ•©;†êQuÞ'2"ÇdGuyÕÎ-9^W.Ófååæçåq{S (É‹b5Œ$IW¼×9¥D(™óü éÿÒZÀµ†5[[£¢Â ŒŒ>xÀ—îâ”2Yee™ †? Ô”z óL‚¡FÊN<§Î:Htü"^*Ýu<¸jb$Þî ‚|÷Ôî=’ÌI9¢èR\QVN?œVƒâ‰n­ïŽi/nlcUb‰w¹\K䟯¸™ä:I%¯d%*µžÓ‰[ÂΧtz=¿òèdbCÄnÏþPÝ ÕT²(¾‚Ì•P‘US3¼åʉ®‡ŸGn)e%£S‹Tòrç •– [÷•X2šh V¢ËÌ&+˜,Ö£Œ6«–¨† æC_êÖo6…½€rp4˰`PÓºŠË7?©¹‚œšÑ¢«ë{uu°ØRNöY-˜$©§K®Mò‰ó_oR„¢ ¥G½ÝU"%‹<ÜÕºæžÎ\5ÓV$í£¢N/oýþÄÙsèóÍÌ»DÃþ)¥Ì3¿êIC‰ ¤ÀDñ½Û+|g­wïÉ|Ë Ð$äzÞ¶Ùšä¿Pi•°×ªr£‡× tŸ™f<‡ÇÑkö픯#YÑuê3ÁÝ£ëGnµ675¶Ö\C/нm¥ë™”2²2m-ûJ¬ÏU§¥)IfOŸ^S»ù¢€¸àõ›"½ÐBÆKŒ§üú>Ììø²¶áZOñ ±Â¤kØ@˜lÅzeÏØÖËÌÂ]øíÙôÃ7@7ÕÊu²œ¼ü\»-vqÊjÄx‡œlç ¢Ì:®—¹~‚~Vp‚ø÷¾@\S- Õ¿ÀÓ,rß™˜Ý˜*¦ÓÁü3i¦BžÌ­4–³õ´W‰šUg°=10 ”09Ê`÷½!¹®µ:HFó+ùJ©Dg ® p=ÇgZH펆8îXüé̯2ïfÔ)ëÓŽ¤ÔìA»¯ÅÌOF^íyÕ$!†'áM‘òòJ²{T,Ñ®ÇÞq†ßÞyÓ×zˆ‹ÐG©çUøicUÑ5±5iÇÐ1æVÇ…§;‚W”±_¦¨p®(S×r?‹…ÚhÍ?Ò<¿”pN ´Àì(³ 6óñ‡T“ ÂÖ,.áP\¡-žö7+ÐCÝñÁt¢¼×Êœ9 ÖB*Ý«Û_˜ÒÙ'-!ï$qü2û‡æ*T’Î 9ÆÂù•´äÌŠÕ+"—8¯ßn8ÏÁå æöy:ç⺿vÞ8ré ÛW¬ ŽçG‘SŽ": {¬ØgcÐR³¨Ø¥ô«›·~àø•¸Á 4‘HÌès|{®ìs|:kQôÁìð Àz`‘Çqª&«¼²Œx èîM˜'›Ê¡Zö>ÌîŽý?«{³"3T/5™¬Ç°÷Ì2žPt<æÌšî™9‰Ü&Ú`°:! …mÖðÒfñDÍì=ÞŒ÷Û4)ô^ˆiì/D·ÌÝËAætõØ µ8µ p9 þ’nXK¤mM¤¦^,Ц%ÿiÞŸƒi2rÊ`o‡­¤:v¥%ÝæµìÍk ±vñ¶Ì–ºØ­_kñÚ^i°zkª„r‰½Œ ¥*EY’ÀVÉre1W5&¯è)‹OâýÕK^uý[½”œMF-…m¾ "°~ñlؾˆx=+ÄYáï-«óæF)ŧçúúÍÚ°é@ÛzìÅz…:3M!Ob±»øÞÒöØ[ˆùù›§Æz£×7Ö¼Ÿý3âšZ ×ô3ŸóíïOµh==«Â}Ñçÿó@=ýœx§~ ºWXgr_`Ìj&4½(ÏÜ¿èß­ÿ_(¯76ÀÑ`G~Ý$ÞSøö·Ï´5PGßõùüÃ0ßôä5lVŽR‰²™:[WT"(žÇ¨N#æþ…ݹE‪ˆ²Í$} 0“œŸÿźî^?|µƒ=c³dùZ¡AäÜÐ~óîg¿µžSÊN›lÅ盚|^°Õêì¯ÒÊëEÇt•ÜäéDE=¦q`w¼(Æþ|¼è‘±ÁR•]–lD²Ì‡Zî8ýoµÏð’î>é“û¯#ZWÜÇ[œ¾3åjÚÖ„µk†ØèX-ยRÀ±@t½î?C㓸HE, 5ïÿ——{Ê®Và™d#ƒaþXˆ§—ðöJaL©hÉ£¾~¶2óáYÄöx8؋ڌq[i4‰µÙ³*šÌ7/'1™MK^šË'ó.|hã`'òµ`GóˆlË>ä½f˜×¼ý-X‘B!`Cò©—¹0jÀ/úÚV5Ʀ¢®'öà Z$†«M'êO97ßÎá;]ý–³Âó¦ëÇÈœ:¾;”“”á{–> ‡SôõMçWnÛ°anÏÁ­(íMbþØ‚ÁRv·ÃL“œëkÑ’Œn½Eª{ý’–´/:{#üž3 |ó†Â oð`¯5{màÀû/ÒŸÍã>ÛcÜØ9Oß¾ýúIOó£êž¬ª Êe²4Ù:­JõI {¬>…þè'ôóžú€M\“L#C‚¹Är‚÷•b§Ï”…y…¹ÈI&“ge©³Õ\I8…x`¥¬ å EN]Z¢S³OÀú)¶Ödç«“N«Ö•åªòʸY`C’IUiYÒ9‘ÝÊòù9,vÚ*–°©&#]‘dÔˆzVâóÉ*}ì~ßzdÿ„ÐëÄš ¸¬8F#«ENµH£-®o€2ÇÏíwµ²˻䭴ýVpޓ Ýy,òSNK7?ÒÔq$T˜V¶ÀPrįIÊa—ZÑ ºb kºt䔔Ђ”Tœàø]Eîª u¤Šî+zî{š÷zÎÆCE3ÌŠç)‚cDrq¦6[W¡ÒÔ²Õ/ú‰N5Þ- 3é¦ÔšN¸ÿØÜdó¤÷ÀP©0Û´† fâ~(-V£RF+S˲yéJÿõ_Ës9Äù2m¶V[ZZ¡ ? ü¢ÁJH ïÙÈlø™¸ÙAR'ùûR‰ÏL™À‚á(¬6ìhÒáÔ£Ù73¾Ì¨Î9˜q(­j)­$åË—ÏðZÒØšËJ–Õäç¨Ò…ž27‡È,9‘YêZ¶Z|x兩¶<ÛŠ%å¤&©ÎÂ’šâ]ÅWÑcôYÒéÈýQúhõ¢ÊºÄ²Äò„òôzt˜¹v¥íNÇÕˆ¤rCÔ³Òøi¢–»!îù•TÀî:§ .aÚÉÄz¹±Gb„eZ³9ŒPÁñ©ð‹¦Î [cëù˜ÂSY7Rí<ÄXƒõC ` ‹¿}-E7kom;Ôp²¶µ †äáÇ6ë}Ð2fŠ-Kò‰ÜžBnw]lÃÞ¶äÛè¦w™àÕí@ÜômÓ™¦ï­¡2¥K׬ññ¹¼úvç•+w/-æÂlÎ7…nزcÛæÍ; mç>mjåð`~²tIÏÀ[W½ÉÀsÆÁ›B mg›>mã@‡S¤¥¥eHÃhåšìÜÜ‚‚\¶{”ž›£D2'Âꢒ¢"•I[ÁIR¸=ày)öHÍÍQCŠÕŨ„ÇQwæ,¾5ݳhDZ¦ì²í;ÞÏF P i±øê¹/®½øŽ‘l%Iq`ÝÑÃ_N:ÎouTP››‰’BZ]™ª²„­ºq³õ6b|¹Â;8)jâ$gáXQ€XÀ|Hë•=ž’”îZYpœÈñ$) ‹Þ“—”¯ÈAé ¡ÜªK­m×Yð£ÆCõ§‹ªŠ4*T*³ËÒ w–ÄÖ£Ó yv}K[`Z¶<3]•«ÏcOä܇ÂLÞÙ¦K<³;6+mÅד¢ûcHË-Ê)ÎCNJ¤ÌÍSæç9æçîEŠR¥:x¼Áóx£<7'ÉP^a^ùçX¤$¼ˆHP–3F€A³ÁŽ÷0$»ožH ‹ #ùæÆá¦ö«Ã¿›ÿ¥Ûº ©‘ÛÙÄÔôd$cˆtêââr {¬á¼þ b]Z¶5jGH2— K-؈˜”¬†Üyýléœùó—NZ³ºîô&NQš[¬DŒ,[&Ë(OoHáÚ£ÏeœG ˆ|úÓíMsO¹jWóe‘iðŒ$§¼ÜÔ„˜±)±(eh¶œ^óYºz G±iá;ãäéHN6$×hJ‰QÙÖ«:õaTƒô²úÄ/ÖíÏ8‰Ž¡£ú†æš:u9R ŸŒù*í'~ ,§–ßš GßX ¥0ôZs+êb^-ìµ~"j9+ÏËTÊòq_Ô þüðqõÄ<¿±ÉwÕ†-X¼‘#M=®Zv'( OHŠˆÜŸØÀ¡î?zܸ )6oH_[#ÿ#Í×z6Ùn°úO…z\ Gš¯/¨DZt«¦ãHëùÛ!=ÒçkrÔ9%¥ˆÑ©4:ý®ê­¾¾1›×qžËwMG˜aðÂgxôóð ÞÍ>yQàvílI ´¾UW[U}âtó~¢ðî^]:gúª%^>ëÏ>‹ær5¨PyÐ5ÍÆªE°ý6¾EЧuk r ”(ÏɨIJK«JYø˜¯Ýêfæ‹M”_Çž‚Vqϙ龲çÄLé8³šö÷Â5}×ozŽ\gòP„Ð ‰$äó’·…N©äѽÆc-Ã_Îz8~ü¬ùSüO„|ÆJ^.ß±néðqÏçüüóó¯ßtà$ë ÕdüÃkùú¯óôôÿ¼ó«ŽË8Üh#yùìæ*O¯%þ³=–u<èºyõ©1l.òXŠãXyv~R:e‘©.-ÖªXp?pÃ~*Yižšè#¢êJóÊ”*δÓÝ„gûµÀ¹&k>º¥…ÉE©HNr…_Ô– Ï9AóQJ.‘•ÊKs s„à•g$ŸNýìÖ­ú¶‹\×§¾Eð f Üܼ1õþ£¯î]l~b`•—¥±[²’ÓR÷ìÜž†˜Å«:¿þöê­®Ûí[fæJ²QAYœ±+?ô‚Ï{Jÿe³áw£ýH2ŸŽ.idp‘Üßvl¯éþÑÆÆˆ£&©Ó$¼˜IdØâ—aš’:I†-€Ãúö)ø\)j8xøD‰¾XU‚*™òluښͬ@Èa1±;sS är”‰ÒÕÊêüÓùõ1(L 뀖͗«Í­(M*b÷Ç}‚rG†¬ý»¬×Õ!ƒpà{P*å§ãôÒüÒübäT†T%EeEÅŽ¤À-&ì\–[&+Å¿ÁG ?´¥%¥H턊 ŠóÉǼ2E A.ÊÍÏÉczc¦I0–ô Fµ¶¤WÂ4û+^WˆæTWV ¥kw)”ééj¹žãð 5`.º%ÖñÓëÀ®®®Žþ¬¿Áö³ RÔô"¬ã endstream endobj 64 0 obj 7268 endobj 65 0 obj <>stream xœeViTW®¦©¢DE¡-¤Q»;"¨  1¸DE@\&* ›ÈÒ¬MÓB ´‚FA¨öETP$"n€`TŒ¢q!¢ÃĘÍ-æuæL`rræOWWÝ÷î}÷ûîwïº:„@ 0v“+äêÈ N±ŠP÷`EäŽrÞ2Ÿ!`gê°³„ 8ö?䈆œEl,ùy2š$D“t+gNÚe?BýÈœJ‚¤Ü²5±*M\dx„Zf¹ÕÓÛjÞ¼ù}±stt”k>XdÎòøÈp¥Ì‚û“ WĪbäJõ2ÙnµB" WhTñ² ÐPy(¿Í+H!–¹F*"UªØ™å+™ýÂ…v ¸ûÏ"c‚wÆË6)ãee|²uê ÎËß A|¤Q†„®QÉÝ7Å…Gxª#7»íܲnkb¥•l½Ã¢ÅáK¸΄ 1‡ð \‰…„±™p#ÖÄVb=±ˆ°"¼ˆÅ„5ñ1ñO‰˜@耘B"bÁÆÄlXB—[\( á‚:¶: ÂÂa“îjÝÃä22“l¡Ü©,ê!¥ÕcôBõ*èít ýýŒþ}ÂÄ òõ­õ£ôûá´ÛcA8B— Ø\¶™ÁÞÚYáì,ÛPO´ÍÙɤg`¸kꔳW¢Æ‚¢Ì‚¬ $¾€Îåž:öeIeeÓøÏˆ‘‰ ¨‚Íà|ăµ\yGzÚfdÔè.ì¸z‰/ÕªüöLÉØ)uÃ_ó¦~øïᲜRT*ð¸f¿z«jÃVIé ÉI º0 BA?˜ Ùà5s¨:; Ö]-ŠÓò³%nhZí½q“Kž€æ#ŸRßúˆ¦à®ÄGôöI+˜ÒØ”R8G©¶"Ú]y¦¯£èFõ%iÝæÚ.Ô€jR‹ò’sR³Ciîì\8û vtQ¹ÑS0›.Jb­Œ¹‚íH ‰eÔ\¨î ªY‹ÄK‘c€÷*_g¥r¤EmØèžãow;j:.JÂX#¦óL  6Z‰½"Û¾ÉAç{©ÁH˜¦±G`šàw°bg‚¹pä›ÀªË>†ŽÑ0uösl‹§È¼,–Jœñ „/úÍ VÂz0†•$ØŒRÚ:{ÏC´µOç»ÁÚ—m÷¥ín5 t>©"²(ñpÍ%žËâWÐU–±é|&Íp‡gCFáÅdñ¨î­±÷‘ƒ 8B€TÔ"·Ü6Çl €PÍå±Í‹¸<{E´Ý£G óB:Îˬ!Ðç¨é3ØÏÓs˜Â¶ÜVt]ÜÕÛÞæQí„V e ß n=si<õÅ0„Éý ì’` •(ŒóC´§¼¹+媓BúƒUT~}^iQIu}KY jD'wÅMÎÙ‹¢iƒ‘² ¾ç@\VB¶qDÃà©ÈÂÏÑr…­?6BXÆk@ˆà ÓÞ´ýúBòíë60@ Oƒ›ì=Þ IÂÓ˜_¾Z‡u–«¶»K}\6F|Šh‹÷@ðmÝË 7¥Wî}ÝôqálQìãÎ †öG.œ"Ü©§¨§º£»³»ñ9êG×ÔíÁ§Ã낹!ߤ°í1$XSƒ,Ã\„Xl¨Î¦håÞÐäi|Dän/Î5+ÓÊßdrZJ¯žDâ㨠«4óÌ6Ù—}e  hC¿SÕ{^dXùûQiðüÖ‚… Ïp?[Î9Ò|BŠ^ÝRŸÚ4c%Z%÷Zçá†u5u-¾ÕRv£W²Û)eý¦mNH,ê÷ «<)Í.$ñê`jÖ^´Ékåç›4öÝB°à›0 &²]&¢WøÔæz“ßjl°ÒÌÞ;úË>˜òÈAé¨ìGœ?È>n$†C`Lößu\ëEâöZ•//û„ÿ—ýSŽEΛc7zIz šätÂa^Áaî †FìQ°ÅÀÒ¬¦‹^Áhe`%ê®Ó„¥fîI—І“\?ÛaŠRsÒr“hp rOì)8P€ŠÅ(7» §€ UÁ”#5¦èìŽÞ%µµ%Õ5´èÕ¹–šÛ×¹»Ï©NDׇ{ Z;ùOò^²öL x’°œºˆªö—%ïÉÛ…’PlªB“¸C©HõáH„¬Û€µgk™Ôi0组›“®T¢ ÙG¹àç™)SÐñ–ÅÆêCyu’ò·)÷qP‹ÕÉÇä.‘Þ‚vò[ÊNV¬àŠâñ¸ZÇë‚ï@ü» ò¯\]ŽËÀè X:¤ƒ&ÀbºèáH{˜É­Ë)Få46½ØÁze¼§ÄÓ7brFŸ\ „‰ñoMDC¯5'ýMÝÜÍz7tÉÎÏΗ` *31+ ¥ ¯ò€¦¨K} o§¡Æž»­í'¢¯Qÿ–kæåXPi"z¸ LõÅYÓÞë-¯Ÿ·+·ÈJÏJ—Œñw›ã/—«ˆGðŒqÁÏx@Ǻè<Ê×’ø<ÕÜÃúO¸ïÃ3òÏùñ’«#ö¬1è±Í<¢kñƒ]K½½×#±ÏŽÚËÅfUÒû0À›–i'e$Ô xY÷ËG'z;%ÉkÇ= ð¢=Æ3ACRûÝ€qf´Ã|ÔQÌGKW—ø¯ãŒwëaŽŠ‰0_82—3ÏÖ¦'Ùù„,DâÛê;ŽçúBúŽMç³°Ð*?ʈB;Ř~³æ½¬½tSÒÝV½ô{n‰ ×·Æ'cø{!û‡1”ð™™Sx‰Ö€d ð=ì 58Àð›yÎçP°”zD;u|ŽM&£+\¿Í€Ùühª.a’Kö¡ã\™ÍÉ» &‡KsŽ£BtRÓ¨.‹®õ-tAØ-õŒÅÆ4L§JºN—÷"új垘„”‰iÒĽ­IóH7ñÛ–¢Øé%ú ¹#y©üd<7ÔjΤ}9£]®úª33@ŸÓ¢$”–³ÿÈ^Px Ä0¢6G_ÀÚ~q% íÍ wŽI¯ä}q¨ çhöálñ±œCÙ…Gy‘Œø” žÁD!wKàPöıKðgoCÖµ˜¸Š'пP0'å úq3˜<†™M½M]~ú°Ò¸ÍO±?Ó}§óAS×30¹æ$èmÜo[`2=—jÇs‹ Â&aØd%ž¾!ÚïS3nÿi΄wÃÔƒ\C)ñ jƘÀA°àiW`‹QÖÙަˆ¡ß¹?²)Œ™6eT}óF|H*d‰ãvl€mLøu`Èîæ «ÙÜ CwP5Í'+aâãï¯5Ÿ¬?Ûz¥ê&ºÍiðÓ^lŒmÍàå;$`JýÔÖчèÎúŸ€D¿ÈH©"*J)g,ýÄ÷Ø„f¨QÎ @ùo¡Ñ]Žô0› –ÓEï î3—Q~VÕ~ZÄ6¥U(ƒM·¸»yF×]Î’Têex&•¡>˜Œ’¸®ÿx¿}Úp몤åJÍÔ‹:Õç|OÑ¢w—[î<5}ãÔ…E»¨|$a!šíÉ+h`¨üs¹e%eõ Üø¥Ûê•ÁË7yÛISÇà™@³‹¸Jî‡Zf55ĺ‘Ø‘Âï´®qXŸ„³T$¶ãA;väC {Íàä@79žUè¾ã”y>¼fćъ(l¡•’`DUži.kEtO}”‡…ûÊ¥^5çŽdçeçK¹ CF"×9÷ЖëaÒðË =’Öîꇨ·>,ð—§*ü%`IÂeÊ@]Å6óÝÔ¯ŠRÌ'Y>stream xœ]Vy\ײe3д(ÈÌh" ˆ‚KÐE@1ˆ&d™Ù—°¸D‰ ¢p² ¨àa4Ê•%‹’(æå™ML¨Õ¼Cò{§ã»÷þ1 s–:U_}õUI(#J"‘Lö‰K‹IÑDE:{jã¢Å¥¹‚­D˜n Ø"¬»5¦“ÚQkK‡Í©!25ªnž` Øj'Cæk”¡D’‘W´J›¨KÒ¨Ô)Š9Áoorpršûÿ+®nnnŠ-º¿w^1ÉU‚žü“§MŒIHñP¬"§ãâ4Q Uœ.Q¬ˆŒŽŽ‰¯mŒŒ‹Ù¦Pjâ4‰‰Ú4ÅœUŠ..®ÎäÏ‚Mü–ÔdEPdB²b­BôÿßV(Šr\«KˆÒF¿°*1fW w’J™¬~Û'E䛺Á/8.2~‹ÂÙÅuÁÂEK)Ê™ ¡ÖQ^T åM¹Só©7¨õ”’r¡fQ®”=DùR³© ”µšCS«©E”µ‘ZL9RoRþ”'@­¢$”9eAYReEñÔ4ʉ LQ3) uŠúKâ#©•Ü500ˆ0¸h8Ó~kdg´Ö耔–ºH;hcº™Äx2jfˆ5gÙd6Ÿý‚ýeRö¤ŒW+CŒï™˜„™<4•™ž†:sX÷Ë%÷ÀÑ2Çâyìøg¼°,ðSÚ\èÄÚѱ×Ê%‚Lhà±Ñ¸6ì¤x aã×¥yÌÌ;1Ñ!Y¹ùëd3™ŠŠòJÑÔRpìðg%¥UU7!iŒ›*>R!8ƒIB¹e( Ö\[±8Þ@~2ÜàͶ–¾î“[×Èð÷då/Lüú0ã’¸.JƵ}ÃOR@ ¿Tr†BüÄgÃÔ…ßb[ÄâÅÎ3ðZ¬~õO÷/–cZç­M F¬WÄÍg—·—ÖËKNÖ•4¡ÏQmb‰KœúÛâòd± ]Ây»Ùc öÆÊL;, ¬„5s^àåò|,å/*gØoò^ñÖ;££—îÊõ†&9Wóa,*·šÎ`ö¤ÿyËè̈è¤wPÚVš~rwù¾c.±ó…CgšoòÖg–k‹Ó?Iû(–5+K+ác‚OÌ^ÓfLÇÏBK4þ¾«½cg!ò –4¼qKÙã3´õô:ÕÓ×ÓûÙs4Š€Ž}ºz`M¿{ýLÄæc+&3/ÀîöØ›yŒ§œï€™\Ì)Ãd° ’¬}Nœ4¢šŽë×úê~C E`÷ÛúþëÊjlD¢#ûŽã#<¹fË€ôê2¼/ Z†¥œ\°-±x—X<,Vwó°Ž›ïð_zÏ% ¹@ ¼9«`î5¨W¬ŒU{èMûëÉÇØøV¼¯ƒ^žŠÊn‚XŒ#JÑB¾åD)¦‘Ÿò‰·æ^•wÃõ57}z8ïƒ2Ìdró÷¡ýh ŠŒ÷d¹†ç¤¸'üøŒpå'=WH…Ãn˜fÍÝæ+Kî×›‘aÇým±•¶ÀxÙClÓzË®_–{2«C6{oTW6gÉð,:?²$ál|«º;óaÕ’G_''v®øLñ—=g;¼ãëéÖûäéù/úäzq"u2ç×bœ)bœ_’8¿$k‘ø õé5Èže;ñp5Ø’FB> ø@/ìdzHZìÞTbCÕ‘¸²4yYFiÎåLðØ4•>“U”•j³5:}µ*¡ H'Ûyd'Èqú 6þXŠî ÏKšê›ê]Dh@ÝæU½/M.Ê>„*Ù“õå2n¤JÎa_I*QI/ñf#ѯ^¢ÞÄSQQo´ë9y,+µDV¤;¸ ©Yü@¯«¨W×[W³ùÓÍH‰ÞÕF²P>!PA0x,,I'€˜‹­ÀÁš ZxXAß:¾=`/ÊÎÛ-ÏË@:”…|¦–©YX\K§ç|Šê÷öý‡ç5 Õ7_G? kéê³[O‡•ø#vÜv‚c Œˆ}?¦Ýt'Ë=;º³0IcƒÔÙÚÌ;v¥ï‹@/+NQùÄ™í`eÙŽÖ\ÆX¨0Çmowzÿ–ì¤Ôí,×®Ú°}½-6[ôœ8ì:ø¸Ëçv&œ’WèŽdǰ0ƒÖ#6Œ½HRbñëx>NÇÛá ¼Þ}rûÌÐU9—qÔIú#mß ¾Ã’- †_³ˆfÏRÎëÇÿM¿Ùùs]õG‡ªeO˜]ró²›s¤]HW‚:’…ýå7 szÅÛQziiOÅ#„ Deç§€))SI¼]ðº¡ð‡pG½;Ï©šBºÜOyÀ«ìÝðL,ý|ÌÑÊŸ¦ÿžÑ EÓ‚b]f½ÝúÍ>ÜBìòâM ¼Ǥ„™õÝØ¶FŽéJEE\êœv¥ãÌàãΨ•Ë&RÚCPüd¢´ˆâu¾ãñwúnà„kp3Ԉݞì~ÇèÛ˜‰“Ž^¬õM:Chœ"48~b¸¶5›#}ÖÅ×ß’Áï$FS›Üö櫎Úî&—±†™àä=‚¼/>Æ #á‹üQTZØ–ëèeþs8¹þj8¹þR,ˆ2vl<}!©Ïø'0–|•úµê²<¶Óï´òE¡Iš`Š0ÆùþïÙ!ÞJ¯÷úFŸè»0™ á{ˆ[ \‚Ç<®Æ¶P=·©p„ÌVâ4-f£fLÍ¿…Vo‹ܲ!Ãa;„-Jœ nñíy€QOݹòŸLE0eÇCÍ•È>eÃRRR£³èDziì?â-' /ËõÈÖO‰B±H[–yv×ÉÜ;èzpèþá%­'5: ²êádlÊ_Ïvñã»È4öî<&u‰g6GìÇ#njdܨ[¸&x‰ ¶0gtl¾Ót¹“aHå¹_o´ÆlðÿìJWkcü>Ís£ý­a+}Â7ûøD´ |yî|¯Ü<¥Jh(‡åå¡U4à 0.45…‡MÍ(êÿ¿j3» endstream endobj 68 0 obj 3227 endobj 69 0 obj <>stream xœuW TS×Ö¾1ÜAÒ šD°Š ƒ*¨ÈÜ ‚‚" ƒÈT°Näh}ísªµ‚ ˆ¶*âXEœ­ T´ *•>mõ½}óNXï?7ôµÿµþ•¬Ü›sï9gïoïý}ûH(“!”D"1 NJÏKÊMMˆÿ¹vaôaŒt=Îü÷}!=†RxnŽÌ¤È̤bô{‡­„HKh#(©DR°cÿ¼µY…Ù©É)¹J§¨ˆÅÎ'ºü9âîíí­\Uøß'Jÿ¤œÔäLåxr“—”¾6+#)3w¦ry;==5A™œ^˜•’£ŒOLLJ§Eǧ'¥)SÓS³²Öæ)æ9+=&OvŸD~<æ§f¬ZŸ£ŒŒÏÌQª”IÉëÓã³ÿ2HQT°jnafÂ’0¿¢µ‰1óçe%…û¯[½ ;90'%"(752xýšE!yiQùéñÑd¬Zìä¬t;ÉÞÕm²»‡§×”©Ó¦{Ç͘éHQ“({*– §ü)oÊ•r –R ¨jåF£RÔdê}*‚ ¢Ü©ñT$L9R‹¨Ê“r¢¢¨PÊ‹r¦¢©¨)Ôj1¥¢¦RK¨0ÊšF¹P1Ô|j5J™RÊœBYPRj85‚²¤¬(õÅSÖ” 5’EÙRs(;j4•B…èQ&”œló5Õ%q”ÄKö q¢òÅ7R…4TZc2ÌdÉuZJÇÒ»h5#aü˜vF`ØJΊû„»Ì=š6t÷ÐVÓ¡¦Ž¦>¦+Mw™¶c‡v~˜ÚÌÂÌ߬Ìì_ænæ«Í¿1/7¿fþÊ|ÀÂÍbŽE¬Å)‹ç¿0üÕˆÀFh-WY¶Yj-ÿcånÇ-„&¤Ñ³e’.æê3x× !Fƒ Œ…Þ •C¬Z˜\&2a-ÿ¯¥ß2x—>ƒÆf Þ7Aÿƒé€µ4ü]ÍãbÌà)m!|†35ú÷Èœ‘B9ß7¸`[Á…ö` ÀP‘6}Ãê‚’4ê#T²£p'7‡=°ùÀ§‡PªÞuèë#÷úú”è-FŠf• ÛSʬÚ:|ŸÀçÝ6²³mdAÙw†ò'pŽ•µÿëjÛ³{çTrüŸnAÉö„^ï“á·\.;«e‰¹‚K‡äxTHƒ7ü§—6~—_Ü ªöCë†M°öéS‚=Xv>‚÷v+¦1%ÓVÆû#ÎmÑ#6WÔÏÛ.¬òÛ­0¡÷'Ù ¦<”ÐXÊงC-i>Á€÷Li ÷Ëà,Å×h` „g>Ò _j¥BŠ5Ä @@«ÏÀ<‹S†ãtýpóZH0-n§(ÖXuê S7_g#:­Aô¢KkÏq²w§N—]¾f‹´ M®§8™pïøÉ+mQS~]Ò‰¤“1_¾ÏC1³—QõÖÃGò¿Ê@ (±$}]~vAæÖÅä¥`üŒ‡Ï^DÇ6Ê!+ÊÝ›–l‹R>J[Ÿ»>'cã Ä,Q»`Ñ.ÂI v~p‘Á÷ fôÁ·«YøA0'qoüT4õHá°pŠÇŽöØûkíÁûÞÁñz‹'*¶ûò/®úbk<|¡›kDÈÀêšZ§Q/*Tjð-³:Ò—IØ‹…-$-§ãJ&¥)ò¨èÚh,Á3²Ѓ%0öAcÕÝ‹rYqàC ñ|ßµ™ØR|l¾p¦§ÛüÇ`·?—ÿH¨ÓI¨å¡J73‘pX–2V«c-ô«_ÿI+ȵR½-1ÃÓ c –‚Ž/FLËæ-ýŽzu®Fè×HêuRýñ™à妯^ÈÍJ ™—쀰)Âf5ãZý¯.|õA zýêDq™-1›R‹33ÂÃR}ˆwﻡ®ÆÞh.\{LQ•½?}Og„G˜ &EqE,ŠK=6²ï®àLþ a.D5ä´ ìzAœvðŸC°Â?"-,‘ÔD§/+» E*þç›3°%6ÆdÏ…]0†_ïÒ‘ï€àðêœ}&vÃu}\_\›úlæegbÖøI¤dæà9ÏÇ‚#˜u·SNJ&7pYr0ŠF+*×^È?¶õXéenG ÿeÿ;O§¹4eÚVº”ÐÁA|Në„ýûQúB'a{ìŠ p+¬ÖvV5ÞPtÞ¯Á0> ˜„×ȽMÀãWl†UØßž\\±›aš`~K.® 1Já¶FÒ«F뤽Öp‚Ð ‡|ÈÃ& Ça |‚Ñ ØñÂm˜Ã¸Ÿ&àùX5ÛSü/HO·a ATvQ_47€•u4'.8h‡åî˜&„áûÌäíG®Õ*ðq×Ïä{I²Ya³?2Íü–æg…'ôÔvím·öÂñÒ0'˾9ýåç¨ô ¼•-عq{â|—ÇÍQxµ–u ËzØAÖ=«]ÉsôöK‰3·y(d®¡‹jO^8s°Ýç`ÌŒCXÕzâ$¼Ûð pw£]¶ËãHÒ²¾LÒýB0%XO2?Õé3\q‹a´Æë<K*·=FôËên7ì$ŒT äY åÓq#+»¨Œ ð ‰?sG.°Ó N¬×ÝÈ_;ªZä²?œùP?ÚˆHI5Q®ûÙÅEEK>Ch{‘ܟݽío¥û÷¢áô ½9 ñ&Óí»cÑè¾"%ó/½[4†VoØ:`§cŒd&êàëRˆ&é;‰X*‘} œ¶†åB9=‰ÁN¬|È-¨ åô_͹N4VÔq1÷W2» ×п1Du_µ ¡• ÎÆÙ´QIõò2ÉS¥2ǰ‹uöÇC£âöN‘'-ªAÍœ°Ë•Œc§ºÁ\˜ C^Àx¹°k°`D³„KÖàj4ËÁà2Žt ï3Ž%¹qcÀ…X÷‚y#¸ýÓàFóHô¯å‰\È~Ó2Hö8´´ “;¬ÎõÀ>c¾m °mó§è»Ì÷QÀ-VÔˆùÙªõ‰)òœ¬MÛsO˜/8]­FÜ£skcëY”’W²›~š¶a~vú2ÄÉ \î…ÿó^SÅ•ò/¢ä\AоU_™… ­ÝR”›š¾êÃXÄ…&oj®9Ú»O¡ÝûÍΣû¸?ÛÑÑÖðiƼÂ0€×´#ûI[þÇ+$œsàï< #ŸÝbƒ:„ÁM‚?4B£±‰ÿ¦š¤žŽý3 ¡’øB ©ŽÀVA»00ïÆa¸Œ~ÉÀ(X6x9Ý?ØâþÞ¨?,ñk#k0f +{üSó­–Û'“‚åx@ÿÞ:•"þư/¢.;Äç-ˆ•§_¯ Bhùº*NÖðýk7x¥.“~§øŠ‘?Ýge óÏ4e¶Û¼¨½øNy‡åó–d-HT@5 ãq9¯luÂþÚêüÛ}±5„ülkèÅ3„^‚žJnÇ[ ”¿20Mx³ ¯Hž<ÓÇ”IÚKa#1ÂÛ¨p(Âî{ÜÁ¡¯ ¿53žà ³x!ðl-Øs¿Oƒy¥:œÂ¿›;ŠÀ‡ BÁ†[Äh±Cá#Ò0bÏXìã‰ÅiÇÉ;žî-“ô¼“ö¼ån²(pCþ²m\Ûvòâ3˜±'Œ Ù›h씟3‡p]Ë‚Ÿ1™æðDTzØú@5p—Ç›çÁf2ú€2ÉeÝ}ô²>†ÇR¶ Û’YU`Kƒ”½oýGÄ­ÞJzÞJ!’ãðV$†ürý²w!ÙoØD73°I¿ÉxhÔjÉù>¨é“ÂQ²A Ï\»<,×Å([º° \`‚†(w È×ÿ¼ìùêÆGƒçcò²n"É»åË'9».û¢!ºîåK…QKõä4 {µRØ+ìà ;´ú˜…l:–äãÑEœJÌÔ#œëÞ}RáóÞ0”ÙuýÛšžK¯¯ŒüÇ•K턲oä4ÇŸŽ?½ø›`䎂Óó“6Æm›Çi™Ï뿨Þ[Qqþû#Mˆëº1;rÍRU²Âm1vš¶2x ž( )^˜O’›GxØ7³Ï©NGÜNW‹‡•þ× €‘î:, X”µ8I±æ^}õÝFç¿ âHñNáŸ6-pñáÖò\{óVÏ`¿GˆNdz 2ûà±S4.á^0µß ‹Á RLðíCÈQ[Õô…öA)ù±‘ýFŽòCyYû…ÊS÷mAê¥&Dgé郥!•ÉVÈe¿ÍHLXàc‹­q%(é¾3á†w­s°‡L|T7wnì…û- *p£‰ì7íÍÈéÓÂ#ݧ„_ëî¾yuðX¥†5Üîœî¡i‚©üÌýél<}öÎÅÖ²<9óþ'ñZØqñh7Á+ðÈ>Wxï~Ceó÷ŠR<7râx†VÖ–´pðž°†×Üróš6ejô—}7ï>·Ãþá¶"Â#ôµû-´¾•ÀR·këV(7ù½|ô"L¿“B‰šøÀ·þd)rFý iýŸŒqæ~È­ÊE±WU0jÓîaê¿™™=ÝefNQÿ Ìe© endstream endobj 70 0 obj 4593 endobj 23 0 obj <> endobj 25 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 24 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 71 0000000000 65535 f 0000025999 00000 n 0000048833 00000 n 0000025867 00000 n 0000024249 00000 n 0000000015 00000 n 0000003224 00000 n 0000026047 00000 n 0000048034 00000 n 0000046161 00000 n 0000048362 00000 n 0000046583 00000 n 0000026088 00000 n 0000026118 00000 n 0000024409 00000 n 0000003244 00000 n 0000004327 00000 n 0000026159 00000 n 0000026189 00000 n 0000024571 00000 n 0000004348 00000 n 0000006422 00000 n 0000047064 00000 n 0000045227 00000 n 0000047696 00000 n 0000045748 00000 n 0000026221 00000 n 0000026251 00000 n 0000024733 00000 n 0000006443 00000 n 0000009143 00000 n 0000026314 00000 n 0000026344 00000 n 0000024895 00000 n 0000009164 00000 n 0000012120 00000 n 0000026387 00000 n 0000026417 00000 n 0000025057 00000 n 0000012141 00000 n 0000015262 00000 n 0000026471 00000 n 0000026501 00000 n 0000025219 00000 n 0000015283 00000 n 0000017727 00000 n 0000026555 00000 n 0000026585 00000 n 0000025381 00000 n 0000017748 00000 n 0000020549 00000 n 0000026639 00000 n 0000026669 00000 n 0000025543 00000 n 0000020570 00000 n 0000022552 00000 n 0000026721 00000 n 0000026751 00000 n 0000025705 00000 n 0000022573 00000 n 0000024228 00000 n 0000026803 00000 n 0000026833 00000 n 0000026876 00000 n 0000034230 00000 n 0000034251 00000 n 0000037172 00000 n 0000037193 00000 n 0000040506 00000 n 0000040527 00000 n 0000045206 00000 n trailer << /Size 71 /Root 1 0 R /Info 2 0 R /ID [(ß6ø{[\\©LËóý'ÿ¯)(ß6ø{[\\©LËóý'ÿ¯)] >> startxref 49033 %%EOF simh-3.8.1/DOCS/simh_doc.pdf0000644000175000017500000042477611143604474013701 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[“·~ß_1•—t§˜vK}Q+o\6@lÙ]H\8° °kØqâüŒüâHê–ô©ûÓô,NUŒ‹jéèèÜΙŸ7u%䦶üàüãÑ7'jsùåÈMoNNƒÏ—G? Ucÿs8>ÿ¸¹wf6ꬫºÝœ½=ª+­Qî»ØˆAT]¿Qµ¨ÌçG/‹ÓRTu¯¥,—²ªåÐß—[³M4²éŠGå¶©´lû¶ØøïÏǹF‹âK¹•n¥Š7qø¹ìÌñøñ?~oòÐ}ƒêŠ;¯ét3ïý÷ ˜Ðw€^„ 4qYå¿Ó\±-›ªnÚ¡-D\¶)ÿ~öç£^VƒÒ›­lÑ. Ud\##„­¬tßv- P»¶·î›¶-nÆ•]­àXYã°­êNIƒh×õË>Ñ™í}µž¸×ôª6½2<ï-÷ þg?™ Blt²AÉiGoiÝmú¾5;a‹;„…ïì—ßÛtÒþëì/‹ûörPJOËm_™«×]ñÌÞ¨×æš?”[]Õµ¬eq—>.ëªéú¾n,ý®GqÁ™¹}_)ÑKsdXûd\0´]8Li\ pïǵÇ6bâeB s›>s7üÎVB¶Ê0Ó±·f¥0Ó­.®Â÷†mJ4º…Ñ•[h$¶øg©+54F´ß—[e¦T'‹Oaó¥5²NÎ97KŒ¡1↿–BUˆàg3§*Ã^«(þ\÷.o"Úpȧ¸àŠ®„Bo(®¯âì5Æ‘*`}±°àM^ÍOlú®Gá’ vaÁ©• UëD¿7úWj0¢b„Q´Ñv’)ÛJw­Q§ý;+ØFlvñz†F×k!õ‚Jwâ‘p¿×qÁÈ8+Ÿ"@ (ðxâ»QŸj¸(ìÆ^D°€Áåüy¼é.Î~4úeÌ’êÞì ˆB8 fã%¯@XúÇ‘³æÿ‰[Ï‚íh.&KÕôV½;7mµ×²Œ0b®öð8ÇiÅt"^¬ÉÛ5ÊH¾æÚàGNÞÅ5oì¼ û.ÜlÛ…Žßaظ¡F®¶QÄd\Pã0à5dD;œ;»ÖŒ̰,ÅùàbP_/¬Ÿá6|ŸN¯Ã*§ÂÑïºñ*üšR0¼)™ž¢•  æîoþóÝøGE|Be|lô.²‰EÚÆ\ù×RêJЦËÙþ€CbûÙÝ~,fVëÇ2NÀ:AÅGÇ“FI’}-¬$y_-Iax2™e˜]0p2°,ådXqjb–”‰Ì<å™è" @Ä0(F )sŽ7¡šë¼ûë=D \f¦4l‡/ÜeÞÄ-õI"µ.éþW”8 ÑÜhqîBe˜öÖ²ÏÄ@Ø:³Ó“ïJ¼¢7¹¤¤ºCáæbîvÇð¡«„á¬êzÍ ñ c”‰^tm†6 7Å)Ó©‘ZÎñ¨Ü;'= 2™-©'Á*Gy•HdÒ?5LÃH|´ªÓBÑ÷`UßRO4Åê&‡á’“ €™íçŽàF0‹Á@Ë_!CÁa²x$‰ïˆ]ì])¤uÑ ­RT¿\\Çfƒà¬š- vdà¿}– ¦Rgç¯Æþ¿3gi©za,½ dT“‹`"ηõ<&'òÏBOÊ­ˆö"Á™óŽ9û¨¸!üàLH(ÇàÏ=ã¡ô±q×úttâBî_z@6ð‰²Oð¥`+1GÄiÜ ]Ï>‘x›§0œÏ‹„?ªxÌvT™i²r¹”ò¦£…´cÆó*¨ù=8C=(gÔWH1¢_51ùcf2P§¤ug'¼’®Š*M4r¡p ÊÂÐè®Ûûðàfw%³T« â!÷¼é¸ÜžùB = ¶—ɱ¿«†¾ñé‚Ì`Åû=qR­{y$À‚NOLC¿{œí.Œ^EÞ“¯ä˜TKöjuÐBˆvT,VYùeô¡‡&öÓƒ~~9è­hñªcµÜÃ7Td?Ó\Ž‹lÐtêS²ÁÝ2;Jl¬Ód›À©…ß.øã¾ŠÅŸc>Þ }Öa‘ç‰UõÛÙVSH7¹®YÜ$£Xìir_F± ½‚^O,D½^Ük™‘íƒ7°΄¸3Çq×¥wéö?0¢'î~žfÃ); p¤¨}yËÛµ‰C½1Í<~_þ€Ëupïkˆ?‘H Ãä·e_õ"Ú¾z›E´âR\ÎûEf2b‹¬ Óâ8$¹ ºô)Þ‡*×(Ó‘5ƒRƒ’Fkå?BÍî–RC’Ó­(ðm”w”·cf ïGÉzgÐm8Çï÷æt' ãñÕ z¯ðuNžá~œçOf6z¢ZVS]X P—J; ±S€–UYÎð½ýwvh PÇy ŽÀ³ ÕϹòæïY@îâÌ1Cý®“l“ìW*Vy¸5å—£H=¡ÍŽÀ¥%±³@û“µµ`uŽ#KSYÓ\B+*V`È{zµ{#©@q²=Ä®?c¨÷í$X¶Ñ7>p_áPË™{¢wî>°Å:,?@¥£0;¡´–˜¹Ñ Ú‡øøÄ“†L*î_ø«QLù›Ò4´T¼#`Ûýml¦PM‘ð0į<¡nøßQýÚcfØVý³3a‘ˉ”âF<†}‰€ Õ;YŠ¥‡ÔÈÛÜ‚: K)µ¬D߀¥Üë$ >?vÝ»ešâ¸-\NPê¼üܧK)7xuÞ¹¥Y ŽŸ–ß¹\0ÜzÎÎ)jñG`Ì3ÀÉÒø.¨OÒ4žyШ˜Å,©öjé4\Ÿž-;& ÝŠ ÏÉ ,ðèƒÇ?P'tí~9‚ áV‚7ÏöHÛ^%¥…„ÝûrãqÓþïùçŠyLµx®hŒA[Kh –óPû³| Äã4øýåQrh÷¹c9€¸ÔIšñ  1g²úäu$ó‚Âòžá­â _”ëå{UÝð^E«(_‹—µÂ·ÆÆ5ÿŠïžçqÏsð:É‹œóæ¹=õФE•à½9¡À 3•9¾mµr½((®–@àÄ\ $×\ÆàU8HÃCnS¾Oò¡µ,Wü?»¸sEÐõâi”€£8“Jʸ¶Ëñ%×gb¬–)ZÄ”Œ×³.Â誛œ93±æÊAºÖÒrïVáTä" ?‡`ÝpóV›Z1gµ?kMÞÍÛëø[¾²Û$`Yà9 eiU˜¢û_À5]È àë6s¥|!ù-UÀ†Ý‘ ö%½×-ê…·ê0¿EijµÝÈË*oˆ.ûÚËÊÚ´ 6§¢¤¹†þé†Ê{Ò Ç˸pæÅ­sº‚ú·ý¶’iÿϽ¶?'ÿ›•` æýô8Ä®tÜòi£Ö6• ý7@ºÄ!.kÜÞÖz ã’N”NWM»hD9>;ú‹ùó_07'Pendstream endobj 6 0 obj 3149 endobj 15 0 obj <> stream xœíYwÛÆ€ßõ+øHžŽg_úæÔn’ž¤uµé9MTIvÓÄ´bYNúï{A˜ âB¸aÁ~¹±q8øp÷ùeÆ™3žýÏ…›÷/.ÝìÝÃÅzóìòË­ðñÝÅ/ž©ìßz–oÞϾ¸‚…˜ììêíg!xî6g3kàï`fNpæ`‡÷óÙâê¿›càÚÅaçÓ:;àŸó¯KÉŒ2ÁÍW‹¥a܉ çŸâÖ É”õZÎ?ÄÏoš âü1n¼Y,ázÆqéñ ~\fœ‡Ð àZ¦…³í®j/zóôÖø6â¿®þt!‚eÚ˜IW·0qº`ô"én'q=uœ`ÖûãLFZ Š(„dN›¹ÚÀU olЗ@m&´š-…b~3ò"x–j5¼9æXpf¥Â`D¿_,=³Ü93¿[#Í.¤¸ ½BÞ¯ˆY¤ÎËÅR3é…·°kþùmé¦ò]/×7%tøZQ÷?Þ\>{mÑ—G[¿‹[ã™Ð·ß^^¼›ïì9ŒñuqþOqv|ˆ{~ÑÁcuÃ*gF¸!2>,ŒNl ¡zÆŸ—xZÒíe ñò‰:©ØpB«5•‚]ü1ø·È¶Vª ñ.Šý;pUs‚?Gñë¸Ã?†9å@Çî›ÝûwªUdÝo‹À¸• ½ˆ“â[8H ¡ЯØs«õ[½þŠ}ÿ²X*¦ƒÊé*¥œ/×_Åy¸»¥4Ù«Gö¼ ™0GÒLÒ Ǿõ+z¤TmMkÖçï™z$¤i)5X´Þõ>YË.ˬ ªXòº‹é¨Xí€e8S¾™Wj˜°M…{ûc“¹´E·ibÈ%Þ÷ìJW†IyÒ˜ãÄ$¹Þ‡ªÕ(æh³„!éYP³ i](Ÿ"¾µ{2¡@”Øu]0y•>4Xw´.‡ˆö¹ˆãÝ‘Ÿ£ká<="Éô.mxüÕϚʖÂ<ßpš3x©uÜ­$¡uÖ©sxâ\ß1Óvd‚^"«ê”©–kV²×TÒØm‰gÒÂq&ª¥)$ìALç)[ãUZæÌI]uƒSçYbâxíCÝ=Š•·ÀR_ƒâ|êRèP†âó5Åßö¶ú5©¹ÞR»¢TÞkJ nÑ7ñLQóF×ÍA±@Æyœ‡ÞC¹¿Ò»Tfþñ |z ~a:"ß¡Yðʲ t3ÜÈô ·7ô À}yÁ„(¾V²FrêâšY&‚|‹9*I@%0uº˜Ù}[Æí(Ê-³¢Î_‰(ªÛG¹W¤ˆpvSo/œv°¢yé¼ ÜJ°ˆÇ¢2ô ÔÎaH•ÜV…ÏP©ßÍö|³xPõÎ+aúC¼²]3§/LFøEúahÖòB>§ ÄVbP¢úžŒµG5ãÉPxzÍÜXŠè|[$}N‚$èF•×T :^ŠÌxŽŽ;ºæ 9ôÊ–/ÜÈ:ù§ïyyT6e¥‹!Å0Ê8’˜ØsÆ3-æHóDî ϼñÍH³ HûL!-fáÞ*Á•´+ß& ö‡¨=ÖQ•r7šP¾!¡„®ù¸/A¾µ  ÀÆ:cêú“Dõ¨eÌ-·8¢Mäh•~„Ü3®(S4…)Ö¨´gêMŸž5/ÆÌãDJ E•…Ù£^nUöêZ7*RIGg›ÄÔºR –tËÅò_:‘¯cÈ*¤‘Ý“j\ð¡PîVq×Û(F=¯ƒò·›$s^ :Ëܘôµt2 u¬< ø@•»oËŒ{ˆìô„úÔ¤¹‘4Aܺ'/ÌhÞ½… Ã)ŒÆ¡Õ–\Ùô£#2ï+Gm¸’w㤹Ѕ+–²ŸJ0ÉÓV÷Æc4,äTk£‘P”hÿ¦EúXÎ)7oèéôU­„pƒ µŸy¢}‰xAÚ—HÏ©tï5 ”¤š±Ù••ç9/u•ëõŸ†ŒªÄÄñ“sPÁLÊ®lÌ N¶”ŠèRÓkJ¯Œö,²|ïvÜlDQ®mÄÌpmA³ãIWk5á{«Ù?O†Îëžk¶èÉý µP{=Žùçœf\ä)óY.ËÌCìB+6þï°âù¢QÙÄ4žISû¡¸©YÚj:q6ž·T!A]¾/=},µ‰®[‘`~ Oú3{Îàm´¶µ÷h‚Ú˨ÏE^5ñ¢Ï ýS׊1.·.ÇUî74ùåbBB•ÛÖ"Z°³Vï9X–T6ÚĽ„v8Ë€‹ ¶-A@“ÀË?JÕ•CȪ#q§¢áS¼§jyZLajŽ2#owvÏïŸÚyêìÙŸ.WÁP®˜ìfÔ*¡d‰jMtõ±‡«…°n¹Œ=ZŽ¢³þnÛQR¹*€Ï.3è–ÂŽgŠÎ=«'oðàH\<Ûb‰ýxòt•yB3Zd¥T¡'BùsD 5SJ7÷ê8…ï%nþ7B&Ë)J>Á¥cÖp!Ðæ¸1¸Ço¸ÊÆòí}^•xZ1ÁOº&l Oï$vßôˆG­ {$âIŠxÈCWjövPw¤£–?4å攓õ­FPµÕØ:<Å<ËàW™zlŽXì2¾g‹^:•¨ètÏö,"]Ô¸jº’Üaƒ·`aq¼“¥bBf2\Œ[ $c¸+ò\Çd&ì©X—@tcbÝÙÆ7½èµJv÷è†!Pöe†8b²ÝWd²]´f3×d.²x£ /Ž*&6£ʲ5ÃÈ’NRÇÏæã,nÖ(¦ ¡ÖÙ Ìùné0vŸhòišÐyÐÈøHà‰›—2«ké°4[:3û”OŒÔðÄTBÉoR{ßÞÚÀG \TºE3ÄG×Ö´&ã{æRÕ½‡' "|“‹û,¥ƒŸ3:K‡«U•MkT óù½˜,é£R¦&p¥¤T¢ti .5% ÒÉu·ü¢à[¿¿|)eºÄ&T?º&ÍÒªgD,\圼ubš¯H]˜ÌÃÆI’5™™’ x/¿]ÊI±©ƒG0A~oÂã;‚ E‚ 1CÑVš[|‡_Es¼Rù¶‰xì³ÞîÙP]ÂÆ$àŸLLìãÉX“”;&oèÄø³‘ô¨#.–• 0:¦F³EX–$–±ú3Q C«ÆdFbWXï.þÌåî3{âòDë´¹Ü8Ò#à²"¹üín~PÙ红ܤ½Ž3¤j:`_cƕݣeu:Ïš×SîÀÄê´©œ t«ë{¹4t5 ÝoJÎÝmHª„Z¢¼ü UòÓÔ*²¦wÏRpÁD\J)HŽŽÎt¶×̪~“ìG'޳ç'n—‘N˜ÎÎ2%B3 IgÔ° å. Dû˜pMz-Ê*m×¥@'±?LžŠ µ©zÏ‘NËA0Çý9s#/šÎxEe94%Ï6-ýPíì–íúXz+d=‰Tˆ?ö(Û×c%•‚‘µ+|Ó!Ÿø²¡™<Ú½RMKëR“µÈïºô9ZìõV©\6ÛUÅÏÑl4E!ï‹ÅNFrÀZµvgM*ïÀ‘wRñl•ÿOÓ56&VƒU†i'ktÌ„·ŸŒð€èòëf.@ ™+{K1…®×'µØÝ}ž¹C¶çGv³¸bÊO–ÿÇNb¯¾Ù^Q„âJb‹K!f;»z{¤ ž¯×Íâ31³þf&D`vx¿]?òõÕÅ_áÿÿrÕ·endstream endobj 16 0 obj 3490 endobj 20 0 obj <> stream xœí]Y“ܶ~ß_1UyðLÊC‘àm?I–â#¶£HkÇ);¥ÚK+ūݕ¥‘_€ Ð àAŽÖ»vUl?Ð’¸º¿þúöõ*Ï ±ÊÕ¿æâäÕÁ½'íêüÍÁp{õäÓñâ§óƒ×]Vª†üúäÕêÁ¡|±_uÖ7«ÃçyÖ÷]Þ?«FÞîûzÕôe&ªÕ᫃ïןo¶yÖŠ¶«šõå&Ϻ¢è»nývSduÙwåú§ÍVÈê¦\_m¶¼,Äú”žÜÑåÉfÛeò¼_å¢_¿(»†}@7%rùèjóŸÃ/d·‹bÕ;Ýn…ßï¶Í†nËwÿ{PÈ&Äêð˃ÿ~¿>Ül ùí¢ëòÛm!ªV¶]É«²¯Öo6Û6Ëó¢iä»…È*ÑëWrhU–·e»>S7Û®ìùÍ+û!9 µz]Žçh¸Yм1^žÒåŽ.Ù·VònY7M^òg¯èRNžzwèFÕ‚¾U¦ÁªQók?§†Æ?Ï~zAŸ?£Ë•œªB}§_?Ý”YÞôU¥äÀÜüJ>*oŠºmן©Õ]WÕceS7f6EÙS,Xë‚OÁ…ýMë"›6ɬŒž]ÁË`团®äx•T‰ºÌ´üžJagŸuæÃ™ËÛkö>‹»øœ ?{6«Êz}__›÷£oS†ö’Ë.öš‘ÆnàÓƒPHÕ)ú¬*¥"“Jè-†¶+"W;8íÌc’Œ‰Ö´9§ð5ÆR/ùp=;Î’mâ ¾dVBÈ‘/¨Ê«PÐßB㸀X¾€o‘H0°ÇЋ=Ômº7NK#eüœÈVKŒl®©;#1g|eìdžG4Ñ%i3…K¬jëÒ¶ÅúÊ%l*iU#c8 ë:+yßÈiÜÂð#c1g°ú,tàO6/Ô,8…t¤ÝÜ ¦.ôkÀO^2#RKøêt¤C̪ºnÔh¶µŠóÝúu·èj‚³¯ÔÀ«Jݽ¦»<ÍqÁ®ùýK›þ8§åäÊ7’íÒKë öÜ)nÛe’ ×íú‰îV%y;˾\zÉ•"ol·º2·ÍçÒ÷Õí‹^#ºj¿Pí×½\Ž^˜¶t†`[ʉQèô’î²9ØÑ§.XcGzBE?8GjÂK9²+zxÌ+µ•Yµ9 ZtÃ&´çlš[áœLEýHa›×uâCuȇð¤$³??—Af|»ÙöJís¡½Ó!Rû¼é?4¦‘ZÉë %E}ÄS»¯~"[ÃÕ™?d*¾Ùf›}k ÌW„œO­û|SMÛW¿‘ÏVêÙjý5…J?§¾‹˜½àD#ì¸Ø~óöÚÒìk'û7¢â±Œ½| >Hóòº‚û*Ýó¢S4qŒÍê¨#vÒÂn»ß^.ÔkÇmÅ—Vã8í3qFÒÑ_H›qß)“pe_GŠÍb‚nÂØý®D‰JbÐú_rŽåÊ¢ x©¯X '~ÞôÊÀå¡ßßÓCjHë{$LRÊÝ£w½›§.ï¹Â9*ñc;InüÏ8\‹11œ– ÓR6>ua"tÊ!Q+÷0h£ÜZ•ô ±TS—ñ´fᬸLí±ê6AŠÍãhç°sŽA‰Ù.aç3s¬¿”âÞâ, ›`Ÿ‚k€(¸ï|0—„¥š(ÇÁ½ö¦Z4¶: 5„×öаƒ×yä:˜;Kì%@p,t¢8à /És ìR…€L{þh°Q©|3¤ ¤ T”™`Aå˰Ô8öâx"‰\~IO4‚”A®8ØXO»ó ÐY*,a¶H¢£V?#`‹Ëöc7Ž'VÖ¿K*œŒ ý°†¢m–©ø3©ƒ>!Èlã«úæHäK’#ÏàJž9’BxÂo’«‰`ØÍJ4¤Œ²Âéˆã4uaâýÌbù ]×7ègâ#XÞÔEìÝ |=òµ, ,Lxp¤?¦YS=xãÏO¬ûó#!JfKò†æ~Øø])›¦åÎýÊï 3Ïö)§dž•æ©s˜•ðGYÞ¹‹„¡t2 ¦B”±Wö£#‚\§^÷&ÇÆ¸@)Í;‡ÓF©´¥õÿœ˜±ˆV¹SK*ð\)0ôˆ™MÌmílÐÅÜqDêìëSö}r2Ù§@M™X( ™»U¥§ž¥ “™ŽgàµmšrbrÉÒW6¯-Í. G#Kƒ`æ×ÝTE½Èl,įòM½Îôõ½4EÈqDB ”¤z$ac&oÌ ßk…`&´Áp=‰“7÷@µ¯2±Lh£ÆAôRY$G&µùXË_._-{€VL€—© š‹€ži§j6'\RzOB…Ðs›µÖË,*tÕ&¿ç½+Þ§ç‘?Ížs 7öN: ss¶ŒÊSŽdBŽ=Âäõ<숣D2úõ¾ì{7rä'éãTäLpQ,i"«lÄÖ°RB‰\èË|À§ù¡æo*†ž8ä£[žUyÀçm¯áÌs˜`8êêÁo†Åpz²„Ó t†b£™Ûs/N½`ÆDoÙp+Š@¥ÊÒ’×0?~ÌåпŸF©é²¾ Ô/Xž¹ËcèÓKŠØa&*¹ÂeÕ9ôégª$-ºÍኰ×´_Ûß!Ñ¢OBŒÀŠë¯œ²æð&–³I4µ_ùwÔ°¦ÄsqNqÇå” `?+ç•o¶pEŽ)ùo,<Œ¶\ªÅt^Ó¥ãjMSœä$'Ja»¤Ù‡R·@öÚï2æãV@Vˤ£•¿Ùè5T'Œ¼†Ça ?9ÆGºªýÞš.Úx©æ²Q(äæh¶,Š^6+mø(ènG—Ç4ƒ·@Ög+Çk{^ š3B²“EjÀ@×NîûŠœ©Úð†è¤x:U׺ð°ÎŠºÜ¶="¾‰ !ë-6"†Ì×1›Œg?¹‡h É~_lÁ¶:j±‚¢í‚Wͨuw¬HÒY¸HžVErxzx%½–qìöìð𨼕ÏReîC:„é1UîV)C‘$ Kj Þ˜|ŽíMͱ”u »âyý¹)m§›íÍ,±f“q6Oµ|\Úv™AÇ^ù;5k¡ï~h}dÉÜå/YA]©Sùhœ>XV}aHqTpµP\½™á®é2,<ãÌ"¤šZ]–ûù‹÷§õ×7jÆ&?‡ŸŠÕÚ£gÙ lø‰pL(˜B°†{j¢‚²pG‡¬yGLV5t—á>‹ª„8Š}ß)×ÇÓÕ™¨sÀM „}Ÿ-5€rΖ™„©´œÑød¢) wÒx$È)-ÒJ½k5™ég[°¨àì‘‚^V~ú9AÞLã¬4B¼Ýß#e›‰¢å5Q–ËøžBÝñX¢ËóMvãü1ò ™!U$iwÇæ á¶™äÆÄ j˜ »ñÁ>ɽBçx–wF]$ô¡Cø˜ªŠTùÉ…'¦hTSÑ__X^"wl_<ðüã'ë„%31„°¨nÔ±3½@àÕœÉR÷˜Ê»&%¸.»Ö¾¶Ož÷è%:tí@ç}”—âi}û~tíÃ|ï¾kt}ïBE¶qS:õ]“WØ:MmÜT¾7ã~"‘5UÊû3æ‰Åd²G~[ NEüGSƒ‘‹1bï¹/^ô5RŠÈ­®±¢Ÿ#>ñ9úhçáð+áìÞYÁË´ÚµÖz¹‚'Í5“Y®K¥ú}ýý”vÍ~Müþÿ¸åáÙOà].n •Æ{ú.(Ž‘„ ˰ áRøÖŽ ‚Ã&ì%ϲ£Ÿnûþ1í‰bÎI“uMSº‰°=Ì9›‰)úéâò÷rfÈ~''‰½« ðgI¤ØY‘°‚qþ`Ó¯S/ '˜Y„ÅSˆ½Ç’ê®Á×ëI”Ï8¹ÔnçüÂ÷¢öËÔáϨˆÅm sÁg‹ÔggØO;çßZ¯’Îáô­ÛÅ2Ø]L“ÅÐID}¥±¯©³ŠŽMrˆO•‚û’Ù£É ©•ìùG‚âåQÁòhÖ䩨ÀÂÈ^0vÎÆŒ 'zÖ]r²qM^Æ%r KU¶ÞÚîoŽck_ÉJÈŠ%'¹ÁrIH"yøï2yà2:ÁLEÕ¦6t ”&fØšÇâNé=A ÀŸž0-hn²¶ŠYÓ3ÅÑ÷IW¨_{`]LLG²‹ÁÁglªæÐ‰ßk:†~yÍfc Bd]5îž×4q+¹VÖí™?&‰ÞÿùãtjÄ÷ðÿÒ‡Ù*t2ƒ¡îvµà¯¼ø±¸X±‹òGv¢—œŒ…Í#ka9½l¼¢¼ô™q¾—l5FÛÔ¹ànnt´ÆàL‘ÃR¢géfÏ1ñ[PÆ– Acåcѱ§¤Þ,~0°e15úýp#y®hä$±P0;¾î‰=£ïïì »?+‘›]PÖ--(³¯ì\ožÓºŸg û| •e{ÀØÝ¦ÕßÝÒ‡a˜Ûjö}n1egyr×piý É8#. "ÝJÖ%Åð5,…¨¢–¶6ÃR N&k0Ž¡b‚†D\%²Ä-ƒçóÌpæ(%¦SZU&©}XÕ}i·%à <7–((ùEÂYŸØâþù–•ºEè7)zÔ­1É SÇèF쨚dnG—žÜ;Z¼§ˆ1ã©âgàr©1yŸÔ™@á1¸{Qa:*šµí‘6N9V‘³m‹xËûT–.ذ‹£­mvÁÜö‘‰`yŒV?ÚC+ßp%’>Ñtký&ÿpK’Ïàm\ï#ƒ÷zNœ;e’®ï’` ½Ó‡Ù&‰¸ z9(?<ÔxÁîǵ¸•êîI¡&¨‰C(iå š¨.‰FpÖöÒL?éëßÕîH¼ÎK ‰Mî ™¿õùO_=\ÀOͨñB÷Œ†kVZôFvCÝEÈ@¬ƒ8¨ýeq½1I Œ ¼ÇW&¼ò#õÏËg×MÄAÕ:¥êˆppZ\ ™,Ûë Vñ⣈ŽÜ‚8œ ‚#-±Ð>â:.=wLD®PØùô¬Žˆêõß+Ê•Á;Äq0Hû‹–䜱¿%i×]K‰<ÖevÀn|ýû“Ì3yRÿ~åìÃóN9»æŒmÖQF.(+œfŠÓÞ'«úN2§8+‹¹ÝGþs»žÐÝ©ªû›Œµ§÷·üOÄÊòÙl»hÁß$й¸06s§[:ç–ìë£ ‘"‡v4^U8¯ñ·!ä˜OÑüSþû?„µb(endstream endobj 21 0 obj 5254 endobj 25 0 obj <> stream xœí]isݶý®_ñú¡Ó÷:~ Áé2ã4n“Ö±][nÓm:²¤ÈndIñ'iÿ{.¸àA>Ûδq>\ñ Ü{î ð›Mš¨l“š#qúü裇õæâåQwyóð7ñââ蛣&ÉÍݤOŸo>9Ö7*µi“¶Úu”&mÛ¤u߫ڨ4ÛÔ*Mjýãó£¿l÷»}™¤©ÊÚíf·Wª-’:Ûk²JjUeÛ§;ÝXåYZoÏ…Üh2/«*Í·ŸíöY’5MQnìò$­Ú¢ØfÒT™nuã&ߦr5¥}ýz·/’ª(ªf{" ^îöõö•´â7Üßí+M–u»}(C‚7«·wûV¿u–fÛ{ýÕªm°ßîMê´UÛ2A×2¤S=$ÓAÞ⬼”«@Âm/p¶íÃþº•ËðB@Êpþº£]Àl]É|ŸÑI-ˆw5«R¥ÙãoÇ¿=Êj¥¹©Ð t|¦yÝþJ&R†ûg×< V…3#7B¾’Ïv…AQcÓ+:Ñ0˜Ýsy›oä¶×BÊô]JwVgM€Á+¹ZH·Oü¶yUVC·e‘á2¼’ßá ÒàÜáÂHl^èn³ºÖtm–`Ÿ•yÒæsO™õëÐÉÅk:—@röqJ¨dÁr}*‚#ýÊ_®¼-pœÉ´M?£Bø wäâߥƒÏe\÷¨l;kÄeìŠN°Ü üHModmgxý­¥'ÑáÅe™ó9,¸#¿ê¥¶èž>J-tpMWwDñÿ”"Ýs=…M©p¸"u—–:¡K€`åäjÝT¦/çÎXNvúbÝšL`²ÕKÓ­~DZr©žÌ†ãÌÖ}šün§IºSx¯×»ò?¬&ÀSÊÈ3êÒL[¥¼ž®’çKK¹E¸vŒì›FÁ¨ëføZÚâ2™»Ò¬B–r°—15Œ/–Ã3¨î,WzÑË\/Ö&™ÊÌ5Õš™wÞþ|º4ši9 |»k 'w²Ò‰{ž%ªjFqwD˜Í¿V•ž¯³Tr€ÖDïÙe†øZn;P!Ý¢Z(„IŒÔŒ]35?í#æ®j´‹YÈçzZØŽ›‚€ÚFR…L²‘‘VÔŒ¯èWõ[Ñ~(¥RÕZýˆÀÂl<™­®ˆ-ó#•ð÷3öÄj!„ØßoQ†[cĺvò¨geRaÒáßʪ8RÇ4Ìe²3´-‹ÜP–†ƒŽÍö/vÚìÝ SŒŽ=Ü!@¦}ÁŒŒ ¤ó”NÆa¦7šWðÚ€`îÂUÎ_Kë¶]ËOE@€t­';?ßJ ;©—Äì€[žàJ³9?CÎ'«Œ ²vÉpáL~ÿ™°(p+8 Orë`D¿æhpU⿎g¹ŸÑùàj©)™k‰š’!ç׺eïÜ7}>ãsv.›0žs˨¸¹&üZLx_î´ÀmŽ•ëÁo?¨#Ф¨ó$Äš9јç;èÁ¡] ‚£²"¬2‰¯t† <áC9+žWÏXG/r\°ÿeñ]×m4&à.ßœõ-׉ê@IaÈ"ú%ŒÐ¿ÿãh_‰J 7¦«ÜOPϼþ¼«ªÂ 8±M¹E7çZš»ÎC1{Íð#>ÁÐ¸Ú ?ðhMŸsóÒ}?m·é%®,ggèq#q…€ N(ïrÅË¥vÈéðB5I–©/Þи'wœfµÖ5ZäÆ"øBÿVµF9L#7Ãè8¸6oñzÇð£Â)¶m!ÚÍ9‹;cœè Â*O˜D‰0¿£ÈT–Y£xékÊYk¥×/Úgi¬z/ ù&hãEšiëàÔÁŽ™ˆ¤Ûp4ÂHØ ,;ƒqðûW”7¸Ò“ǃÙŽ^±¸U–L¶+ÂêçÀ˜H>TPãˆàáÖØ}—y.ø°°Ë58ˆÝ }0‘8žaÏ|­fë=áF†ÅgÀ¢5€Û.3•Ò¶r¡ÏwsW“·ïUÜC æ›ÂR^ì˜äZ—êû]i"öîêrßV÷)áyŒUf"¼vª´¼œÐ!;\âºN=· ¬ˆ9"4¢EÈLéÍu5wy]r…~*¸ò”ô °óñ„­r7àI­©ÆÚŽÙ€7f©ê´,ƒa—HøƒÁ¦³!i3Uêá÷¡0æ\÷÷ å›S׊›(Qþ{:ÓcXDµz=ÓrÄžôu`Šá˜•®ØÏQ]ýÞ,u¹¯MpC_<°ÎqC}PYŒ>q4á ÞTtèœë™±¶ð½Çw>Äá«$¯½´[çíe`o,|‚Ê\(ˆ~_a»I$mÆc¬¬KB«© ŤÒr©€˜çsqXâk†' Ý­u´BÊŒáe»³êd4¿¨p‘ì™d“§˜¬Õ™oR-²äBpÊ’FŽ×4ÀÒÄë ¦3úTœ†ßi*%Ž[Ï©àB•ˤA®ÿ·ÑH²`tDÿ©Ý˜ã»GÇ?ýËÔ › Z b1˜s% ]HPI"æÚcn¾è‚ÊЋ“¤ðe&±0פµ_˜0>½WJéGŒÃôpqfL!îXãz/±3I%A/?F Q||×Ìï߯…©œÀ˜nQ­#wñ ‰›?Ó^ŽÆÆ\ŸhÖmýÎgZ[öµù=NÊáx>”;Î).EŒÂãƒÇh®ÇÆæY^¨üp¤¸§••ûÖA§ý¹ÿèaÖØjNÝoZ){û§»}ž”µjMB•¦æÅú{ Ô0o6æ‡ÐoѤI›Õõ2Ƕ5¸uÝsqï†c¼™åwC ®‹±ï#ºÇÕ»æÑ?9RiÒØ?œW3ÆJÕZcûÇý«uu0#uc©3rMY*%÷þ`©—–úÖRÏ,õÊR§–zj©sÒË¿fŸösY¢—ä!'=U4gŽ-_ ùZÈ!7B~%ä3!/…<ò—´‡„<†s!ä-¿mÕ´M¼m#ÿ5!‹fÌ`8ܼùd`]«­{&3ÚzÖñ”Hº8Gž_dÌyI=×UÆpõ0·Õ3#¹ýµ@F±Ü6 äQ¥-LFŒä¹àŠÕÉÆü Ñ—ë!'ªÖãn¦yj—Cãkv¬“øZ[–p?¬Øåš6êös• ªFÔ4à>u\¯“xJÈœ×Ö’4ÕÁ_ZL”oßGukÇ2©VÕ…­zŸ Ödf!ú8a$óû„‘:Uï..¯÷,2-lªp¢“‚[¢ƒ'Ƴf6ÑGÒ¦)(~‘OñRÀ¥_‚EùÌ´û˜¼= r•ç·“4-G‰{a’'þñý}e¢ f…lçåÞǺ:µÞûмQU”C#Þ³í4a’Í"þÑ”2Q»êCB+3=²HK¾žp•'õi¡ß¡1hVº,Dõ¡£8úx¶v 턨*HÜíZ4YÚ s ë½ù 툓¹"œªû˜>Ô³Ù^áIlÒ>ëÜç)‰͉{¦•\ôž}c±ŒWò’rJ‡½²—.à0êÑ&-Äå%ÂÀk…@ÚB5ì îG׳Œ_aÂC;<ÆtâO\8 …4.Žv*£I’¨1Ã뻫}®yO;Ùœ61„WŒDjß°œpÈ(§•-½¥^%Ú§ÙåýØR•žüÃwV®½{fü,îr”ZäBù¡ðõõœ¶¦$lد¬Yñt]ØbãÌU‰vuùЀ—2MÊùÑè/L Ñó,uÎìâE½¬hIŒ3ÌzæÕÙç[±ýÕ.KªÆ,ǵQש©¨2k””MÕ˜ EÝŬS²c7—BÂÕ+ÛôÂRùù±}4<³Ô9 ø…¾Z•i·¶yÒêyh‡»UVoïÙ~>—ο4··y«ÿQU¥Íön×}‘ÕÃ0›¶Í‡§›‹¯-õžÞßßè¶Û/ô£ô"«nã”mpÚObW~o›Þ×ó4-ksÎF7”FѾ£ë4­ÊaˆEZ·41ètdµIÕvD2…ÕõF¼fÀŽe¬ZŽË#nðƒ/^W|O|´Žê„^ý£ftØ© Ž~ð„¡¸0ÍXån§³b< !/GÃÌ–MðsšX>?Åù²3Vô>`žäE‹e¿ãúhÁ€Êår»cán¸³‚«þÎѰÑyd&ÀÂvöNH b¾”ÉöCÀNߨ~ ùóŒq£™8wáÞΞËð¹.Ý6t0dH1O™e)‘MÇx)›.ì‰ÆK¿ƒ­ÍÊ£J€‹·…7#„`ÊŸÖS/¾ã *ßo9s¸LtÇ„Ìjê¼ñƲnë3Òy´æq¿Î•*ð &H\Á’/³¿qO"”:ë8Ö f-ØåVVÞ†'7¿F)fFêòÒÚ5gßð b,‹¶ Æg×4zx‰£€Ø›sx ÷CÉç%n`_'[½]|É? ÜÔ<èùÎ=ý¥gÄ18xð†…‡§™Ü ,ýKÀ>öARç¿;~O£ˆè[OB]}èl¬ |gÓLÆC(' 5­æâš”´¿+æ|¨R+6Lùp݉>ïå¯Ë»äßûÎdp`ÉÆâ(0sé›zˆÚà)]$ Ï­Ó »GÚ&Yƒ.*·æ<­“Êr˜Ò}}Wc@-‡3S ²+ç%>”Uè•fÑä5bàp A¦°î¨Er¨…K:1&o´r'/Ï4«5Ò.´Ê”uÿ’ž¶G,µeáî·¨š$O§gð¸›;†à§1L|1Í´u„{Þ’]×–zA¶ÈŽ„û–zd©cKýÉR,uƒ¨š=4X55 ÈѰ-6!|C77<‹ÝvÛýpM{€¶h[Ø)Áw?ÀÈ^ÒÍ ðˆûäi]s¼ú%Þf¸E¶3(-@=Ð\ö‰¥¾&»QØÎ“É&‡G.H/rÇ?-uÛRw %íÆÝ2Y5Ψӵ áÊß8cf(²Ñ#ñözÄK [9BU>®¾“R¶;=$´É÷MswÇÚï‚d53êöW‹yà ¬}r¦h²1$ªœM\ÿG­ÿuÔÀj²êï‚\ÉZß±Ôß-uü*<ñGÂ;-õ;KýÂRŠŒ¶ÄÍÀ¨ÙµûÝÎ'7òŸBÞò.%¡í)]kx,û•ó˜: )‹·ÜE”Ö-<+\–$ñLæG2‡‡<æ˜7¿óÓ%>š–Ù–„ªPkä»J…Ú3 ¯BÏ@äšaˆz—m‹_Oðü÷©Ÿ-ôƒ% ©ÁÉ3}ËsÛäc/¦6ŠW8bÅùô´œ :™àÒ\c§lŠé9­±(öš ÙƒÁŸL•ÚÝÛ°Š„UÌöÂeQ•>‹ §;>òŽÇ†šO£kÏ™’ŸÄ+ªÄ§‡Ù“Á@W|ØÌòL=»Un·xv¶³Pɰ{žX¦ç¿q0azSx—!—]gà]nˆ§§YE»ØÌg·Îýˆ¢ÀÁç³Û7“ Ú¥Ÿ5èšò`(­f6UwȦ4·biÁ9ÞGtùzHýi–lk9/dº}…‡:þÊãå.Ô³èKÕ¼”äórØùì<Ô<¯>JÅ‹fŽ(ókùéîcG©†Ó9"×3r“sýB¢¬bYþ‘øõ¾VäŸvª4ËÝ2ÕõþRïQË"¶$t®Šm˹*ø©¢ù4ÅÙŠ˜Z\õ¢ñ\¸T•ÞæËéÉcªànÆ›Ûäœ}Õj{Dµ“¨ä‡±n'éüd “=nPÙò;]¶-íŠgVx¯Ð ‘ä¼üIÊŒÛ<fŽ‹@F(Ýyxä÷º€]l[ÐVÀR<ÝÐ8·s¯ª’V8<ö‰¯59à…_ÙåÑs4¹íIü‘'ômø÷Ù¾³Ó=g¿tjò£ñŠ1çpæƒb9án¯ìî¥Ç"–küD^:SYðtM'=°á‘UÀÚÝY‡j^ßñÊŒb€Iùã: %•ܹç³âüW^l…ß鶪çz0£âú¤–2‰´éwC¸Õ ¯åÀĘoä;‰¸¯qFI^wtÉЉs¯üOëÄî Û |¨c±û•Få¿zÛ±Tx‘¯Œl§…ø’y+ÑEzÈ –sMó4Ó3´e$wŽ~¯ÿýýàendstream endobj 26 0 obj 5118 endobj 34 0 obj <> stream xœí][sÜD~Ÿ_1/[ÌR«»%ñFH–b7„8Y¶`+åØŽÖ±M‡eýžnI}NK_¤±ÃRÀC[£¾>—ï\Zü¼Î³B­s÷oß8~½úìQµ>{»ò×¾êoÎV?¯ê¬tÿø²}üz}ç:źÉ»>|±Ê³¦©óªµX[C7f]yVÑ ¯W›õöð§Ui³ÜÒóƒ¢ÈŒZž¬~ØlL–ç…jèú¡ÑY¥6·¥{UëÍÝíÊT]k²Sá:e¥©KjÒtE©òІÊ陵y¹¹æ.ø…nžró\LáF°ôŠU›{a]x]ßÐ)µùš{}»=°™Õ¦j6C/1êê¥é¦Ùoÿ.ó‚úþëðo«ƒ¢ÎòRׂˆ-q?{¤êp Lé<³uKé¿lÊÌT¥[Eß:­þWe»‡wCëih}ZÏBë!èÁÏ ÐâÅ\…Ö x6ê[4e?±k~ÌÍŒ›ÇÜ\só-|áj¿^qóõ W¦zŠ»§çp\üî%\¤ØÐ |Zx–*s›™¦ Ñõ|Rð -ËE¢~Pj•Y[¬Ï‚ÿÆ|ysánXv#‰{Á/Ü®Dçî¡2U5iÚeUTå\‘vƒýþEz·Ð67Õ:´žeñqhý)±3%¶™ÎÀ$·&œUSˆ‡Øòæ·ky]·ªÐÍæN˜÷1IŽ3±¹’JCLöŽ›xX:¥³¼*+:í^h´\×7ÏÃï±p÷FúA'çµ^(ÜZœHž‘ßw} zß!ÿší|îtOBëqhÝZ˜É¹i¹©§Dê.7ïpó1|á6ÿ¹¯ Íü·À&Àhÿ‘u"/«¿’<’kÍ~H£~ˆ1•×z¯[tCö³°•öVGó\oÔ¶Êmk¿°šDQo¾ÜЩª¿tj(/+ã¶H ´¶uA ô•'m?Ì97ÅÓ‹ðêYh­ùç'a"~ñ$´NÅ‚ßÐSkr¯$ˬ!ék6ÿ Þ¤/TgäÚ]¨¢Þüâv¡¬ª¬ã?º)M h³&ë!»º´Ì.cz*¥jåèIÚ¯(´rÅÏ §ÈkÄ)Èe›¬Ô–hM+²yC»¸ô‹«5A¯–Ú!˜vÕ=Un—a°sÑ–Ï,Ì3c Eï;®Å Ž´ eê¨ß;×&²6y½y ;Þóoh[åñÛíy©‚Ð¥3?Ô.‰\Ư)zƒÚu4F7¯rZ+ÌsÍ»a2pë2´ºé¬.y`]C¤ðØMah(öØ;¸puøñ5¿ôÆRéÞ†»ÖzKöUÓ(Ä’ÚdµõR›Z÷âIh]†Ö/Û&«j'bÄ‘•3Ù&î)íA‡c÷Ïó‚á?ôïcΨÿ¿þR ‹€p~<Ä ~ÆËì™ðjüfOã¿è–Mñ¼äE·è@H6eQw4q$»ñø®Ê é}É(ç‹ðð¡çeHèuÙ[Ó4”ñ¢þW}×1¿ûo/š„ÓùÂ$}6$}c"P8âXß‹I€ {ÅÔL.±oN.QŒ+8S0F„|ç˜à&1”¢ª6Íxc¥5V>mG5än÷¸˜Ì`Áóîw%wøŽ‡Jq§ªhÙMå‘oÏ0Ò鈘é@"á,ÆÏ»“i:6r]ºs)¬í–ê^¡ùáàXÏ‘‚_r £ÍtlØ«ÞÏÛV-…öRjɧpçnߥ©ŒÚüº-ª¬ð–*L"Vñ~kœ “hˆ‡¼1Ø|GjgtÎîÍ×È‹ºhµ ¹;¥8–¹«·¬~ݪ&S´>ñläZgGz'Ì!˜µU%ý×»jýZ†qgüH"Ë’LTÝ@›Ær§A«ªh5¨ø…‹„G:Tþ˜qžO1$tl‡ &8GL †>Z*‚ ‘‡ÌsO2Á;È0x£×,ÛjœÜÐWíÃÈLŸÁáS6_¼^`Óx—½þÎ CÔ&°î’¤„4Âï>uq6Æ>ÌKD¬Ô; Ž)õœ.Òï.-`ÓZΊé*:Ý »Àª3ŽÜR#•\/‚]b±‚sÇŠf¼]íq©¥‘ë4„%¥×Ý‘‚9ó["xuÉëëÐo®$7œ#:šÃ-] , ËÚ[úå–|@:ßJj©5<4q¢)èü9?ýL6/£Ð5rÄÓ óÝXÞŬǤ l—M1þÙˆ~Ös&Ÿ°ˆîTÂÈÃÀã œ?ºî‹qŽ{? dÕŒqË1OþŽ ÉiÐÝ'{‘úK)LòˆU™iÕT-!g¿™8¨ü3w»žO‚‘-õ3d[$RX<¡ý;MŸÏ^Ès´ÐHrѪږ=Ñr#¹ ã}¼úO¡f>ƒ½GÖër‹¤XX6dm0T)P1½%N­e@ÉÖQ«²Üë?–TúÆ)üx}Kˆ±Ä @VXÉÝÔóKÖp¼E>÷UbÜ@›O™¢Éq#L3ø –GÍ6÷¹ÞÞ'Lp¤c<Ó[KCSdÜMlþÇ-Ï1ðOsåCèÒjïg 'Ñ̬؇b¤#á´v$fÄ_êZD Þs¬É…¿Ï…"ø„sk¹¥’ž5©Š`g [b<:ƒØH½âÁ®à»ÒÍäiõ‹Í*SõúEüÎÄyì„ä(Î’†äP)^Hº„YGæØõJšãXÖ "çºÐZ sfMó4jîýŠ«¡&1ü i’ºw" ½ÛLYhDÖLrH0»2&õÍöÏ9Ýe&†dÍ3{ˆÅcK)@ø l-Ô°Õ¯º€uAÊ^–Ä œ¦Ñ‚”Jý„‰žÉEÞ˜Þbx¼o‰é5t4pP+¥™{õ Ög·¥Ÿ]äxÂÆÊxrß$ÿGÚÅj"%­pÑÔYÕE‰uËÂÈŠ]rsI4¿;%É‚vGÑÓ^Ùp¯’éÖ ó4iÊvx81€Ž£ç½/ UC’cÿ-Ø£Vu«t¯:†–IVݸ@Mž73,c«HÆ5-èƒMÓ‡´NUJœa|Æc=à^üûá¶"oÕVµÄ†ß³“¼’¶e‰GQä^ Js‰\ˆƒÀ Vèx>Ԉ‹e ðÖe´–I €à…N[öªˆ¼Úöì…5å(ÙámI*Î=IygîmL‹ö+b—eÁ剺;1íÓ0í.®ìƒË£°çœ’Òû„¨ßEAÈž 5N¸—¬ÙUrŒoÓŽmtœZuÀ.XûÉBùaÙÃÑQyhÂÏÏ%KŒçÜ# cüœ…4އQAœwÄÌÍ8°¤tˆ»]./£]D-ÊŠ÷ °gU .ˆ~o£®¶1D9"F–ˆ]ÛŠÞ‹ãf;ª«[…ἃڅÁÌ3È{ô…`¶¯6IÇŒ5#Ëœ.§Ä+ÆÊDÔ Æ€L §Ÿì €™Ï„àH—ï ‚EQ3,³&”¸ª6<&—†9“)ÉÀ¶8|<*#hã-N'ScB8onZµt cˆ.=àzŽf_¦Zpü@UµÛ½ÃÕw«Öøv·"‹›ÞŠ,ÎìšÄ5«»ÚÙßD}õéÒEÚk™nûOTùœm±vÅúÕJ5ÄÊ4j]5.o×Î ÊZ¿9]½¸åK§ÚäqG tØœ YLù²8c!J X× v½ºï%’©@¯TÁeM#ë8ðŠô®(ŽcÕ+·FTⲆjw”ĹŸ‡‡8xíóÎ3Òš@«âcˆ3š8 pdö€.Ûð ® ÀÛOÈqÚ9x‰•¨k¤$£2ª>j¢L¨Û½Þ©b±÷ u}˜{´7W…;)R¿mÝÒM£Éo!來 x~ì¤:ºCóŽ'£i‚žb7GpYL:A/ê×q y¼ÊF©ôd‚¸EU§êãÚ‡Z&xE°"Š[DÐ’H_UqØŽUºB0ŒeøðÇô¾çˆÔf£üÑH„UwW"¦Ä¶W¯à«BN.w‰á°÷x_F&Ké¦n0 ̼}:ðˆÁç3Œb”D*·uYê¬QH& EÓÛJIJ¶Óõ@{^Ù¿PWZ Pæ” “Ðd–Òª.âÀ‰wÙÊ&™ ë¥gÂbÂ*ßï …:…®Réܶ^·ÈTnÇñ$ÌÒÖ}+¥Zvƒa€ÞGY|½g 5¬v}®'Ùð’Ðq_.(”˜¥–&ï’—§Æþ÷œ–%-ƒœñªž–åTÖ¢™ˆ²”"/¤¶¬& ¡(´áb‡šFeqh톨8ôD‚j‘gðè3xBKë¾°zT’2'U¡â»ÀóSå&UñT\/}åveêºÕ4áîêuûH Ø­ZSõúûªˆÚ›OÜxUIúÚ5óLâ<½ Ë¡J›Õ©K¨œ7e'¼\VÉ8’3¬jYð蓎Æ½Ò“е€º ‹±À—Åæ¿¢lbB¡ï¨õÆY·ÒÓAç®ë[¼ûÆ\®;£N7"h KódB¥‡-ŸÐk!Z£Û‚Ã¥‹ãø d4p!C ‡¹£{1˜vãpôŒ OsœÞ>w”ÀZF€qºÐ©Å‰,?WHl𠃓5íUz÷QmoÇØ‘¼­Ž¡ˆ>¼[ºcµÖ²±s³¹*eã¾õ"ï 雦`t{·InPÄøÁê¨cü;V硼ֹ¤ûŽvQ`›,Qá¡^@õ×eQ”sÊš^³}¸jé„Êëý¬¨¼¨V¸ÛÎtE–ør"®<ÇŽÅâ[^¶vêö“§í¸¨ü§±æFÁ}”ê)ªEýÄhòœÐ(´e ]s3ŸN (UÂÄ›,G…õícþ×4H²ˆ ‡Çãƒ!)tã„O’Ùbñ/¾˜*ÂQ úÀ:û‘:¨vÃvYø:‚~¢H•Ü·T™#œó^ò™DqE$ÅÓŽïÜ—™<±É®•‰tq¼š&¡‹O!±bfì… Üëû1\™U÷Æa4ÂY¼oˆ#ŽÒ!KLóÅèbbŒ†paЮۆéŠÚè¶a+Mî³r¡ìþ ÒU 0 £,G¤ºº§8e‚9 s%´¾",¯TŒÃ^8)5Ê’ÏYˆÅ ©g.`‰ ¨°" çe½-ó‰±lî y$òQÑqoËDR@„dÄ…dQ]'ôœ¸ø(n’Ã,»?Yú§ª¡düD,UºƒôÄò ïŽwñíÁ^Ù¬.CÐU¾îÜý@þæyd d>Øe»„jpT>ø´)êµçµ^¥ÉUâèåõP4ˆ øG¶Óø˜e¤ÄTÕy\4ˆ?•àQ¶¬VAÌŠ7'neÌø"Á W0]ÇÅtôÿÕyPV;è1ãƒ8³Ðçx»|”fÇ7aR7#P&t:ÏÂP9…‹ ˜‘qV;r—Û8žÍŠÒp• ÒwØ÷‚Ø«F ÌN¦h´äžÖ^ô,àP)%lÉœ½®›s‘F7zånŒÎŠj:8us«~¿ßòøƒZ¾—ø^É +†TaxáØX9×äwdœ0øñøðŒdê`…©ïÚà Pó/÷âE͸~•QkÅéAnœBÀ÷:Y®½ÇUÎD’€ Ò–”‹‹” âüè—¨Ðm¯ÒTt´Eü9P¤ñ%‰°ÂûHø¶Ñr´_þäÂÁß1þ$žÔÝ)Oã|aÌ#¢ “à¿×WÎ0 8õõíâoÅuô›ˆÁÝ8°˜ú[ôÊ;U‡¢ ‹®ñ @Ð0 €KÚô'Ûp„„í–Vœ…är“ö.™Ú— âvÑÈ’Rš½stÊz «ç2¡žËT²\j¿/Ä`¾ÌeQøs­èŒnp¡>þ4†p?’æ|æW1Òæ|çg1œp>‹1÷ƒL©¶‚ͽ邓ɢñ[ôç˜Ì![/³™^䚘$„[°žBD²;`Ý î€ÏáÐØ¥*vÅêM•Õøÿëã/È|·úË—ZKendstream endobj 35 0 obj 4647 endobj 39 0 obj <> stream xœÅ\k“Ý4ý>¿â~ت½—Íu,Y¶ev—ª²ÀV L¨@Qóbf`^ÌLůØýÁÛòCÝ’®}'É.¡@ñ•%Yjî>ÝÒ¯‹^<ý´/\ïüºc³ÂýÓ>åƒóÅÇ»ô¢R‹&kªÅîO;yÖ46¯»VÕ¢*éïM¹¨UžÕTá|çÅòóUžeUåÅò§ÕZ©¬V¦Y.úb©—¿­t“i¥ëåeû°(›åKz©VÚÔTÑ¿¾ç^¯Öe–çJ7Ë#ÿލɝֵ߬«YUËÓ•¡‡Ec–ý;¶X·5 ×~H4º­Š,¯cÄK®¥¾á¶ú7ræ+‰1<¤ßX³ü‹D™«¦EQ7ãâ—«µÎ´­»|äG±KmUÔE¥—÷¸ªðo+UgÊh?…¶c]Sû&Ëëbø˜\WÃ,é¢YÞÊö}ñ6žÛ¶Y± °³K~*Öþ§Õ»ÿÜѺÊ4 I¾Ø=$1oÝ_Ñ ÉÑò¬{©6Ë+nªâ¢i‹•*ƒ©ÿ~IÑÕÚ._¯š¬¶†ºÝ >üŽ.êîóŒŠÏ³p WDŒ÷RÖõŽøé¬Ð‰ž⑨E£âß8ú²S·Ôn»ˆNxùÅXI)•ü˜=øá’ÃG’%6àe¸œñæ¾;"@>CôýÝJ•„3y“_õudÖ”¢Û³nõk‚}šTo xŠ·Nø-!ž¯øµ#øšß ÝAÒšÒ ’¶þù9¸dx ðO¨Aßn ¨@¼ÛRšÌ·j¸BÃD±‚¯9:þ7¹51¥X BìW"€„…BüÿÉ¡a]%:–³ƒ„\´%VB,+FFˆV—ò­Nn4él;ÈMakãÌ¥ÂôJ¼mk–ÚÊÆ®¹n\†ÚÓ:.Þ)l@uÒTÔ¥@¦k8ŸbN|U gñŽ®k…M±@Bý>m ¢~‘^­¨Õ:/KùBF°‹Ñ"ßç®Èž( š²'ÔJi·ßji|-`ñÉj]e•)ë@âN N‹é€xA©7êŠ\%¾ìnvN8{æ\6©€ÿ‘fìíW©†õ–æiàæ¶F-ÿlh€–¨„€ Ûa> ÜgÚýœën¾Ûß¿m*úŽr¼)c?z›MÉ#ü©ág"zÿ¬RZHüÓ¾Ü<~ж0çMg=>è̲»ÔíØÖ_Æ4Lþ,ÆGÒö=¥Iû@u“Ün›±qeLL† -h 6*f¯ ìn,““ &.¨P‚B<ñÇŒæ¶Ýßb«oç |ñ5ÓØ§aìÄÒ „}¿Ç™×$c3x+Ø"Ÿ€¿Õ`b/"ÓFœºP–îw¬ÊBwX»Ø@b°}ƒ÷zΘ P§m»¢•˜(œ4+0âÖ5æB o çƒ9e6+ð6¸Dä/v¾P!Æ•î3í)ða±9½;¡{>ìö ýÛ›áëáYü;[»¨œ6(ƒm±Ž”|CéÞâpŠãVÃC%_²Ð‚~ GŠèšµûháЭ•ÍòÂXö$»o¾ÿT[ï“ðä™í¾ÿOä·eeÝRdC锸×ðë•/eà×{àÝS_:÷¥}éƒ™í½ˆë¹àáSËÅîöÚ\ ÅÁ!ì–¢óíÐDŸé¸ð¥_|é¾/ùÒðŒ§èï`iÁ3{×vß{ÉÅ}.þÌÅ{\¼áâ)ϹøãÔìnÓÛ ØÂÌ®,éÛw*©¡;)ò*+›Þ¹ö¬àÖE•gݦղJ³Pe@54ÓPæ$}={£Ùaʲj1¦ÔyV*댂žj¸5!@Ò¤ +Z2îȲ£?-YBEk°¶U Û mFéñíŠÀÔä}ð¬¯{íP³ÖP®?ÿÚC÷Z­l©eå‹ÖÓÓ%ÁÚ«!T]›Î¥±™º3 ûªÜÙ©kJ“T‡MµÅ¼²NàºOÞ†1¶$—'ÁÎ<@¬L™2¬çØ’qÏF†L:Âp#!q¬ç2-nè" ‚€=¨ão"kgd0³» ©šIâ+ÉW¡­ˆ‚³½å¤vÜ) ø™[dªöüKdgnLù˜ü\aâ «‹maSÈÑó(åÖ ±÷“D§´“5€njŸB”DLFmD݇Ü3:C ¿^~x€œÊ@4D 0obÚ¶ õàhÌ uÓ‚ùêŒì"Óõ /@j+Ý<Ñ+°òuΑNÆ ãΘ4±§a€fh±I¢*Ì*cî; ˜òH¹,ØJ–>ø´¸.nWtÜá³ãŸE[ûp`¸Yü•Øà ÇÒ3é:>å}%v€ø%DåôÜr¡3Cí›6KëŠùHÓôãÞnx1+þž ƒùdXôÄuœ áé¼[ØíŒ´š˜¥ŒÔج/'WZÐôQ¥¬ø ç@8ñ ÁׄÉbefŠ“XÂü5C‰à¼ŠÆQÍ-8L±q2Ã`˜€)‘¤ÊÄÁsÉ-{ IÉ«ËÓp„PÚÄÁ¨Çõˆãè;æwÚQTMò3q¨h|"}‚¸ºP{$[å [Þhñ{ç—ÞÜõ9Î\e“CØ!x™E\ wXrŒ¾œzíΰØg8ÁxÓê°‚´'O<^olì¼/üF1 œWwËæÐäZ`6Ð^ü¹ŽöÒÅÊŒ¬JNh qÇf672ÃBìŸ#K㛈sr‚ý_7¬mDqzc(rÌÖ&©Í¦Y\ïœÅƒ]– à§’¥À-ûÈ3lGw§PÁ÷Â\1‹üâ ùxäX³|æ¡Æ–Þ³ûÎ×ÄÛ¾'ƒMŒ@æ× ܘÍ¥,ÏÙ1ÓØ`‚ÿÛEdÏÊÂM™DÀîÛª"ÆÀä"g¬ëÄCQ5uDeÈÔ °©t–—j›Œx/¾ÀÕ\/~¿¾UÛF~¦8KÆE˜FhrÅS$Ûøh# ç$ú.‡ ½t$£t›½=€o£±ñåbüwsæ˜ÌP¥ÎvºFª5sÄâ1N²@“ÞñÛ§2!;骽©pæ‹“Ù/á4M@˜ôë÷1r…üMç_9¼!iTÅ —y)m›:ƒF|˜Ðk«Tgd”Ä¡Û:LØ|(†«œž×-\Ù> oÌÈ¢ôv©‰î úÏÔ6æ™6åw <& °…¢ù-šÁJ+JIŠchrçLÀ™Ü´Ú.È Â­ôÉïòg>„ö™/=ñ¥o@ î!¨÷¥/ý×>ç˜Ò§«*wkªFªÊÈx-ÁIÁdØþ-suÍ <à wÁgÎûMÁ›&Å ö›™ Ôld–ï¿[X<™RG ú÷qO£´oÚ9yC6 &þÿƒ[Â:‚k)d%Ž(ë¢Ab‰€‡ø°tE´…LÚ:º…õppÜ3¤ˆcÅÖ5Ð;K웆úå-{}Ä‹þ®,ƒ:á%Þ}ñ&‘¡Gëk a?Ö" Å°øâØL¤{¯wŠåÙ@Ë…ÓßqÆ´f*~æ°¡’HB3#†øÚ†rÅæÛk›ÔckÊôδ²™©¼3ÍF‰`qF@ë~Çy)Û1 z•™ 9e¶E¤“4˜"lÓŒÅ? iÉ´tQƒ1OÐÓl ¥mÛMÁüQ"·˜¡ÈîpÕ";»Meœ&A„ŠlmÁr„éðwv,mlx‹Í³'}Š[ï°Û\Ó'8íÛ‘×Tü0öNIÞ&ã‡Û¨Œ`Á*ÙÊ·À;Lm†³íÒUqgÒ ÍÒÌcqÄ5Ràj)¸šuî¸t$jÀ«7(Üu€Æè1Š nP¡Í«ùm‘è$û`U`}ƒ¯,h·¶H L Z­àä"ƒ‰®…i˜¸xIÜÔ!è (†#ÍÙYÑõÉ|ßÂ$Ãýº'ˆH•zŽq$Ð-„íÖwèéL’!ñ$÷±Ã‰I~'ÕÀŒ¼[m2s8…›Z2†Ž…,zRç‘;„c²ÁÊwï¼H‡ý° 4c‚Scã‰ý¹ÞznˆÉ |a(À®#ð-ó lÍD‘PÖÛ°áâ7\\HbqM"‘•֎ΊªkíkÏG>÷¥Gžéj ³‡#*98ö:`Ž `Dñ­¨I—¾Š—¦ã´â$ÙŽ`¾³A!öÚ+“Onàwtè Œ…Œ$F1Ú­R/ÛuÝ&õ2q[¼'Ä×ìsêyø…§ ðí#ØîG ›²ÜŠàÅWÝâ[?ÇQ…&vV6z¾0‡×`':cÌÈØ~ê\&•esÑQ B±¨LÁ|ß.b£R„’ú K ®œ…G'¯¤ ®n  JŸÀ1Æ'Ìáy†ØE”·÷¢UHîFq”#ßž:^=y‹(”¯@; U7âˆy¨À6ܺ“ö¡ç&õÍ „°Ó8[é™<²ä tÍÚ­n0U¥m+ôüºÁTo0}ìï-åL÷ÄÛ|ÉèüËJ÷F7‹†×•ŠªÃÅ¢Q¯¯DùÔ_cÚµEfîö+n«‹=©RýgñK®Ôÿ\Dc7·ÞôË6wk.?FŽBë8Š+5¬œ"x@S:8ˆÅֹ˲ðªé1È’}àKŸ8Þz4V'ù¦2édXqÏâÝ9µ„ €íñýñfHÅКnL ­K„C _ñÝ`rbÑ$º)W‰%î˄ȌOOLF†·1mPÄ †¡°zD¶ö0¤_}u”µ‡¿z2ChÒâÃùÙBÀmaUEªY…„|w+Ø †‡9—K_Ú÷L-_ó¸ó q8 åsPï#ÐÛï ~÷*î×Å·ŽÒ[£q5o¹¸ŸŠû­ÄÅO¢…¬pu·ÆÄÅZ E¿ÀA˜G»;_ÓŸÿáýö‰endstream endobj 40 0 obj 4946 endobj 44 0 obj <> stream xœí\Ûr·}çWì[vSÞÑ €¹).Wù¢²årâ„bò"çÞ%i%I‰¤|IåßÌÝ3KJåªÈªr³Ü»ûô鯼YåY!V¹ù7v¯ž׫‹Û£îñêøë¡ðöâèÍQ“Ió_÷€—w¯W_œè‹bÕfmµ:9?ʳ¶mòºoµXU¥þ»-Wu‘gµ®ðúèåúd³-ª¬.*±þi£BÕë3[Zé’,«*—ë;]³ÐU»þuSÔY¡ ¹¾ïÜn¶u–çEUñׯíïçÔû=í«J‘×ë·ÝTÍúõf+”.Ëz}J¿ßQ¦[Þ`÷÷=U½Åë®[Y6R÷µ-yƒgTkOEÖÃ)ç¶4s-mEszµQæi«øDØ8.íïýû¹¨lͲà^ÁÙã5é{5‹Îæy's»ùçÉ·GBªL•æÀœìõ鸡·Øvfe©és–æá®?B¶ëŒ:YÁâ÷›m•Uª¬[¾"Î4tc²tN›Å-õÅZýaM{ÂjÜ»óžþ”Ú`ÜÇ%Ø)­Ù…Ÿ7mV7ÊY.&\l(vÞxËØ¡;å cû¿ §lû~Øôï)¡†ÖdUVÞiVFuH…NsÝèºÝa)ÚL 9–þ—Jðźæï‹ºÖÍÖæÝ­*eÖÔåÊ´©\ׄ˜EòeÄaà*32¹ØŽ;ˆÁ§dí,rþ;š¡3L<Ôe,pÌ]°U ÆP £:&êh‚rb3 l÷?|†%@b;4=4¬õƒv–çôÖú9€Ò׊½{Üq%¤w‰ßTwŽ€1«×”,úÊ÷ª€[Ànkì ‚T¶6ä‚`5úo]µ5ÒŸºõ&‚©4sêÒØ¢w>58Ë­3±Ìis¹ø&“BuŽ…ê#èé‰g\Vm‡G¬eº‹­©1úoÇp ‘ƒÆ^`M_O늸”OꦤPkð#)™z„ê•i¨ÓØû7U!ká ŸÀr’Ô{Ü=Tž•ãqë¡ýv|F.©åJçQ¥·€Æ|hÌI•Š*Å:ä`Ó<¦ssÌ™±D¸üÄ–¾·¥ã„ë£4joŠ"âúüNœûôK 8Pp1žn†·¬´áˆ_PñĬ¦`Ê™t Ë—ŒµJ ¬!ðkVÄy˜M¼ÔÞC,å×4Çgs¦«G.U“pûðvbw ÛФÉwü»"7‘Æ€ ùÝ{Ø-\\¾GsŸòt±¤°à×uhÀ]L¢)m…`üx?{ÊþtxªFu¸U“µeh@ 5-•Ì4.6ÖKŽ[¸Oõj”Z ¿³·ù–Á16í€íž~ÖwP¸|ŒÌ“šÿ‹~/M]ÌÝ‚ŽÛS® ò¯ŒôAf³¥—³ê=²@Q–f˜˜Ð:¢ØÿÏÈDÿ·^6ED™ö0&‰°f¹ƒ³pL‹&d ÓÃߘf°ê"Í¡•ä†mõ[Õñ²NWZW‰i:]†tzšÖ>‹ðμ<ŸÛþ ö÷®XÙò–v‘AÜÒ,æ²Ý*—Y+ˆîþ0@ðƒ„H+h×0/ëö÷úœ 1}8'…™ç1ÝIìr§à¨ ¹ú`ò358›ÇTqcŽuEô: ,iÓ -žã’;i¯öá]êcUÅ 5æ6ŸûûUéc릡%[¢¥q¨“ösõÃ1¤.}0áxÙéÙì¹6sz “ì¹ Ùó/-›}Ý“Ýu9àTÒ¡¯w>ý½d­ÝÞ7ß4më°û|4×¶…¾UQ4CI‰zÁºNèÅ [ZÑÏé8€Ô*iûÒOÝufÑTbIÞ»PeVTNâ»õ¼(_ê—„¦$TyÇÙ‰x€Î£X¢Jff{Ž#ÉžH‰3ÄQL…:x_Ô¨’i_'”«äQ³(# ™€‡‘Û=œcš'cJ´Ï嫳ªåúv\ÙGÏ ïÔñŒ{{hñœ5Àp -œ¥˜Ä]g©£Ä"Oè ®*ĺ—zîd}Ž9Ösb¹=ÄÙ„ô®ÂíèHÚ>Ò ³¦µnÜœttUZuÎý1bÛI™à$¥‰4ü@¡XqÚ~ŠÈdj'y-ò «bqØeI–‰—Ä£”¶~_Åñ£[ÒNðÊÌñœÁ&—‹+–F+ ¬%ì:çKeM©è|%â8‹ŠÉª“›>`€ˆ{ Cælb¤ç\¼ÕÞpÝT’ߨ»¤)•¯¦ÜÆ"(ôc¤W¤ˆÙ“† ¤cº“Q8@#²¨"[¤ñr«2¿{€"‰SöÒ [:QIVttšÔû˜÷wbM†Ö~Î7­fd_|ÎX´°D¿~iKß`¶ÍMw~IgăñKf+™©F(´&‡'M‡{5•7ý²ÈøÅlˆó–P]l®{Á$D9~Xaaë‰@OŒ,ͰЪ«æÑ…éÜ#=ä!I%lð^ŽŠ{‡Ê÷MM.CÒYº²7c+›ž—\ãà±SŽž¥ÄrY›Âf{G@7äQK0(Mãø$wÌÜ:œÃ³äz'®;V(œ ø›:8ÖÕUèýķÑi2©ì=‚Là¢HÝL¸ZßçÒuC·L¨ñY‘ÂÓj*•“…W ð´æícÒ;\®#ðHGKÓ3œ±:P¡˜W=…ò(nPB+Œwls|Ç)©˜¯¸@?$òÞg+vn˜<Å5)„Òb‹4ÿZ)®Û7f¾p Ã&ó_ñG²0pfs¿ mÄ€±ÃZ§¦TÖÄ—+ø#)i/¬ÏØÚeÜÉ´¡Rux%•_áy$4楦âwÒ`íÍ¡¨FpÒ`}½Oø|O®ˆò^–G¾”I1'ßâšZ¦9š17#w!Öçoµe&Jv}ʶ³Ð £„¿­0#ƒÞÜÌÍàZÏÙê¯k¢"«ð%¿‰^íŒÓ˜²‡#–ý »’©68ˆ1›mÂÅ‘’ñNdM—8’Ê ßÌsÝá©Ãœ ä³%ss±NÇ’]$1“T,ˆ¾í¦>ý1#§‚a³ÙµÛólÊ–E¦¥ÆµG0Bk Jðoø_è4'/n#ä¾ &Å‘ŸýMÿûêgbendstream endobj 45 0 obj 4224 endobj 49 0 obj <> stream xœÝ\ëoÛFÿî¿Bߎvf¹CQ’åÔ½¦&Ô>ÈÙ™ß<7¿L ÆÅ¤ð:âôúà³7vr±<¨OÞü½%î.~9(™ôÿÕ0}z=ùzæ&r>©Xe&³óƒ‚UUYØfU>1Úý½ÒË fÝ€ëƒl’Ï~:àœ•ådöò`ö×wÙ7ùT1£”)³EîÆq¡lv—O5+ .*7£`RSÈl™O­jLv™+7TV*»Î§B±ÂJ›Ý‡ùWáçãðl ÍÃÃ³š’¢°x#´æ1 ¸òÈ9hx¿S÷ÒxíU>ußo¹ªðÚ·õS©Ëîåo(^ÉìÛÜ…?„fmtøÒ0^Ve·6zÙӿ܄¬Ú#®ßÐ1™ ÷XÚÀoÍ1O“écBë&Ó„)8!;~ì-É0´Xsx^<ÎÝ‹IfK#³ßrnW…8Ì¡ccÏa,â’tš +òüÑ hÝí$  w£±‰^7:ª°ÂŠÜí6÷R$¤dªêä&yEit§ïZ‰À%!t“^ ã’-ÑZWšƒþ}ÞNRÆÍ÷ï7UN7'¯þ[+Xaîo‰•Ì)Baìí¦dŽú6P“–&›†‡çÉÏ*û"P«@ê6Pó@ý'P÷º Ô%±Þ—k÷=ŒXyä5Ç@®€üÈ _‰¶¸ò È97än×䨰±hO<µiwžññs¦EgÃÜqrã$ɈV)£ã«±MX&d0Š ¶YÛ¦[ºÓV :ùŠÍv‹5^Ö¼j‚3¥²·AùŽõ*Aáqlj /"O%ß‚Šù ;”Æ.I5EªwO*/ 4– ÝŠ;Î;þ ŠÉ‡¡<¡Öráý[±káM8Iþ?ðSìË÷:±|»³s/òÈçý¤ÀÀwÙ@}ýà/DŸ…„`N~À)9`%¶’( Ó¦¤LÞ‘ G_ñó£ddí¹N]¦nnâW×ÿ~»~Nb™znm%éР8é0Š6Z`6k§aAŽ?±èõT:FaÀ¯î;œÖ”J#³M‡*ÛEYÂZŸkHÞÒ™N‚ dÑÏcZ^²BªOÓ"? ?D¤´ÇFGZ‹˜Ci: t0îsãH¥a E¢›¦p$ô9–‰Úû‘ŽÁªR±k½ëöLË}¨ïõ&Pथ,wÿÖ}UÚ}ÆW#&z}ÀúÇó6‰9£¹Wý}Q4½à5ðvãXðáÖaé–Ø‚—)Ð÷¢¾$Ôf©ˆ Ð6& JûPj£²A‡ÐB¤Š.C½øÅƒÕ‰lmÂŽè•.cãÖ¦†wïâùw\gù”¨,Êò¡ŒÒú5¯˜-| ÇÞ&æ“LØô60i=˺°…f\”Š0g(ûÈ¡ÙK™2ê±-ðZá@F?ò!?àúèÀÙSÀWeå6°³Eí!àx~b‚_WpvSXó$üŽ¿õþh¡ I:íæ^ö^­J:­H%瑼Æe¡Þ,ühïj›:Œ?‹yH. Ta¼ßU©Oåv év8­X·;r¯µ' íKIÈB }srÙ!öÓ0šõbØZæÚ3kp_Z›î1o¾ð9k£S•ôÐÈTPiUZŸŸÃ²¯s鉊gŸå¡ôqäë|Ú}Gý÷®S2Q… )Våkº²‹ÒêYD…D»ðËÈíA}Å ôûñ+Á4,u·faÆQ{—jd Hx§ýÂØãi‹ ‰®6;QS¯&:š«ÇH6Aˆt-øp8ie£Ý½™U˜“coHÅâ}Ū…ûôyö žjL†$9`ȬuQŒ²ÓÅ+­ÓÃö‡I$B§E©¾à ƒB‹ZõÊ{¦+ïQl1Âfºªr†‚ÀºÍÀ°Þ÷Hø}4ÿ² åŽ6jèi­EåÄ– gwæýЮ¾Þ¡ÿ0S)EÙG\XDÓ×8€=+öœ¬V¿‘(n–{7S>¤ ï¬-«îl|÷ÉÓO­¢cm¬Þ¼ò2€÷Eß›ÆFïEãíaÐØQÇ/íÔ~<¶‹Ö9W– ŽÒám-—ÔÙµ¹0À¤ÛÙ– Gj6ȑΗ[Ç‘z….¡n›,må½;ÛywïÚÓ‰ÁâÕœ‡Ú`d’ÞQ–ôÒüIÝÆ÷ú-PKbeTã‚ÄÄUŸ¢<ƒ‘([ùHTÈ­ÿí¯9fç„(™Ýrá¬-ƒª¼| rüû@ì3@ ¤Ã'$õvûàãG[h\ÐBJK¶„.H߇æ‰q®U)ÔzžP_Ý+]ý˜“®Bk³œ²¥‚ð_ˆgÔd 1Œòë~Óñp;ôH¶2äv$ÖUHÓ0¥KÏÉ¡t”BÛ–{R„ÆCÙØ©Š•F€–ôf®£/éÀ‚„"èz½þ3²<íÑ.´gØ2ÛGç)P(™9¤’a8z-$ =‹èh-Ê'dwËÔ–Á¸— µem°V¤åAë=i°ÐØ+’DcQï ²sÁ_(Iü¤. N}…³ÊÊžs”t÷r¨‚E’kfTÜ™ñ“CES0!Mè¥aØ7ƒe1á^*à¦T]Â7¥„¾%ãÚ- óiÁ 7Öi6ó&IWE¡2;+Ôô^Meé6¯É-ç‚4}D“Ží–éÒ”Ü+U·ÖMî팩íz(}ÎBÈì"Pø¯ CϨ¡ÏsÁLéÅé}ø¹®MU‘;%/"xÙþ¬„mW/K7ûmýÕª°•—OÛ¢0:zAü|Þ>×RS2puÍ·g*ºº¶q"Ÿìå'ñc#ɦ‘{R‰é|TTÂo0¦#G1¦¤R£ßú|ìµsŒÕwG=ûhß½†":Ù¡¢]Õi4å¦S7U[ ÝDɶÔ·Øáb‹r;»W€r`=Ÿ0.ž‘\§/“EÌ$‰›¢cXë‘ÐqsŸ€+ŸÕU:¾x :î†&{Þ¦"²šï:tˆH÷o"ŠÜ_måÀ¿!AUZàlS ‘…¦»Œ¢ÀoK¨ºÔÄÔ´a½¯­Ÿ¦^˜p¾¥íw¸û|ÁW¡/]Þ'ÛçÖüÃm?Qb±ºcbqMБPWCî7hÜñÊÂÚÁôdÿfŒîÒ7M®·ð->›ü3§†¡§óš  ™ù3"—ÿ_bY}ê¶\=€þRhø=ÙWTD€íNõÓX²ÕP•RPÖ}INÛüŸË`XgÿtþYDÍendstream endobj 50 0 obj 3023 endobj 54 0 obj <> stream xœí]mo·þ®_qé‡ö®Ö»Ü÷Ô5ê¦Fá" Z[ 8) ^S½œ-c£í/¹»ä ¹—»§;EB›uGrù2óÌ3ÃáÞûY%b«uayµ÷ìM9;»Ýk>ž½ùSWøp¶÷~¯ŠRõOó//¯f8 “dVGu1;8Ý‹£º®â²í5™¹ü»ÎgeG¥¬pµ÷nþÇžˆDU–Éüd!?NDVÎW¦tcJ·‹ý2Šã¤(檘æe.æwòû4/Š8Ϩx%;Í¢¸LËùu€[6US—óËE¦ŠuÖ´êFÑ|Ãê_˜J×ôýr±ŸÔòÑ"c¬éûcù}"{¨Òn¾n¯‡T¡ýT. jfê²f+ªË†Ø ™gMVÕ/“¬ž\är”qžó·ϋEÁ›³µÂ‹yGuÏáØYÝÁõ⇃?ï QDqZH¡98–2rÌ[™nÙŒOéSkªXË)G°leC¤V·RŒ!?NK#Qy§~Í÷Õ£¦ßTþ'¡¦³¯ç£F™ 5)ùÍÁ{òϪš|³wðëwóù´BöQˆn¼JhI'>,ös%¾¢fö¤wLÍSw Õ÷lÃY{ÖêŸ$ØPðÙ4ÈšúvJ.n&ª„«ëú°¬2«¿öyy&xsÄ z3ˆ¯ l6_ýÔO•åXW”HÈp´U©Á- Ë—°KYMƵ™Zu&w'䋨(j-ùϨÓ5;Ói…µG Ûý)äÚq±eÂÌÖÏå–ÐÔ=„KÎfÃ4w¶‚*Éz Eêm:)†;<‡CŽ/H`‡”£¯¢¬ÔV={#*cé¤ÖF­¡köíÕb?ò2©Óùß©ø’ŠßRñ5¿£â«EÇê‘í£˜QÍä“*ùw÷¨vÊi‘Ý4š"ûšfGV¼ÃòvöeÅ;;¥OI/M‰mEÛ«%+¥é/0Ì$V"•`(Ú¼]ì×jÓc!•²–ÚŸ–:Ù™p­™㘅g;J`zhÚõ;qÍmÓæj0›è5lܸià„‰ͯ}VVT¼ 6y+(‘¬ÙGÛôßÐxÖÎx.‰C}^$e”dqn¶Ï¶£¬(%¸ÞéóaãȳôÒ4¹è5ÔV<ÌgXU€tUâ1ü4@̰®]åqZ…°‰!^™tÒàƒX¦>r ìLÀB5&¸>4³ñ™=G¼ ­þf éóQSߨ`P} ‡ø¡3“eT¥ÆLâ·ÖÞB "‘Z3:öŽcyÄ©ò@Nua9 }bËucP¨8Žð°…eŸ~Rê d<-11ó¡ ¢g–3×O‚×í4õÜAëÑdaXñ5´Ê=v£(vÐ|è8†Ow'£FX¹-’ãènÃÄ6"9l.˜äLq¸¾–u35®lþ—Eª,aÌÿÖ⢮xÕïç$Ø ¢áb£ˆÙ&ûËßç…¨#‘ˆù÷ ×jŽZ{¶ã—hAÙ.0åìZÒΰÿ-ç2€ 1‚¤E&•hcèà!_ ‰²BŠVÒ QÆ]Û÷ûÈcZ«Btdp†b >/A×Äz4d‘ý{þ—ßðÁ‰!,'†1µ"XRÓáŠ)p¨2=b_ñMÀ"…–S›qKÞŸ‰º 窳{Rbâ Ø½ šuŒ«È ͸äs£JÔ}ÆGÍ“üëWvÐ:-pýW}–ûÜ”.M鯔ÎLé”–¦tÚÎ@/+SzêÑ>ö¼6¥·?Eÿ¿ë¬j¶\×<¡â%©xGÅ *ÞPñ6cÍ`³_ÀºÏ©Ø®EQI?b 'ôBÏXËŠuž°¯Å  ÈžþÒor4Ù —曯¹Ñ_÷¬ˆv~(*Y/Öù4öÅ&,/×<Ø5¥¿DÄ€ÇS‚ñXúÀ°ýïEeu]'¾gà x;ü‡±yYfÌ3'zÙAÃ82€ÌaghG–Îp tÓùZ{Àù)á{ì<«…- éß@ é1Å«6fOÈÙ€'Î`:øü^‚OœØ2Õ½$ª2ÃÛtIÞ´/ª¸`%ÆØ”p)r¦‚•l^﹂iƒ^.%ÚŸ°ŽPí£ÊDú¥Â¢û_@VLpeÍGg8|Ñ0ƒ²*Òn>užú”åÚtEâ¸Aèȉ+ä cÊ µ£y¬,Gp°gPPp¦“/ŠqC ç°/¦£S\yFá¬%SEuŽ9ù˜#ioE<޲Øä/mmÁðÌ”'=bÁ&X•ç­ÄY™OÈ@ÙˆC¸)û;Ü{Ña;ûö«Äà·†Ïï½L:V䶦 .±ÌQû«¡-Z0hCá×T<…ügêü&/ùÙÅ}«ñÑÅpvN¨E<µ‹Ný`ï±-#ë>1L|ìBU“ÐJ? FÖð¡‹7”á'!ŠºíŽ×X—f(²q8' ùXYCI ¡3úž¹L¿ðX†FîsÈ=bGÍÂV$’‚;:‰XÚ 9.$¥…FÈï’mœ³»1xTKúR$,¸“\®Hs‹ 2¿xP‰û‰¶~é$ ³Â«FU9²êRÏ/v$¢ÕQ‘ÅûøX¯]$S´/:6xðÉ…Â7Hð²ÀÆ‚'p, gÄš—&׃´†ž½éð:e#A…Ê‹½Y¬Ý¸[+[Ó¬×:ÔoчÖÓ:Ôî¶× ±N;Ë`Á ~Ld—ˆƒ€tœÔï’¹¦—2ÄBfªnZ œN-ÛÛ8úŽ€¾ !U‚M«ÇН ¦¾aje¥I£s&Ë©wW¬C\LÓ̧½õ¦<ë^f`?V„ECãös]"ùàÄwFÙÇä2ù0Î ‘0g£á”hkÇý:,ð+ ¨8·ÜʦÔF#O¸í4ž¶°Óq÷ÀºŽò¢´h‹uf¬!†ÂNÖM;Öêèg?Ù•:Ųóû…ºXöé6€— Ø2 Û݈ŠËå9ž¶ÅA~Zàžœ•ƒäm F›šÃÉìv"e§© >5ç|­OÞžÞ|+kÄ…tÇJRÔkT|'´‰7Þa–DÒdr¦¹³Ãó{çiá >@X Ö¥!6#8º†ï° ”9jÂB3}×н6'åaí*ŒÃ)ôÁs•æÔ&dbp®n;Á&a£»ZÁl¬ûöˆ áóJ:ÇÇ~cž«OÄ{äuÓqçxÕhúê¥[é˜ve¹ 錼0Ñvù¯l¤i”Ô¶÷MXÖ V +Ë/qðÚ{H3eÖh7Þ ùn 2ÈŒ¾£ü~+ñáÃ{×,…²¿§«±s×|Ê%3|´®›|F|Öo—ô â‰Å™ïZa¶šÁ Óû£æà•ß´¶pêQ]ùyDߨÙ.t#w#+S 5öÌ5Tùfn£VÉU²P‹°Š®£ºï]°AÉâZf]`ì‘¡ Ž1ø#¾”#;¥$Eâ0 çwÔÐ÷C;çDq$¢é§6€8ÛýiFwªH<ש°ˆZ£o-l‰ÜH`ðB—OÓýxÝÀUðhõþ 2âÎêðÅ;ë½(bÏxPÀ®]R7`—lv åò©µ(‹„qº¹ù£{CÉÉôÆQ#H$Ü,mz°mVô9ü‘Õm¦~SøÇN§œÔc&„c‡½!(‚p)µZ«¸ET²"FÁù-t„Ñ€ÊÓšÙ¹É( é;Á23 å&oø6‰¯ ö©õtµÏ~sFæ8*í5I”e:7×ÊìE™Ç·¦tZè^:FY)<¨ùcp‘%ûžÃ”Z–Ÿ{ syYÝ%Ì>…Ÿ²fÇði8eù vÆFv›™ÎD·e2¥„vodaJ?X[/2)ôUù ›,×&[8„>À ŸyJLÒZ1–€~WdÃ4z6µ/]IÝ×cPà"á¤;o èu°ˆW/ž ÿYhåï`…]-'gveàˆŠ lÆê®`…;8üŽá$ô4„q?ŸxiJ߀R•³"¾ãˆoT>>EkÉ1;JX2GÏÀÝÀðÖ±û÷×óܫܶ @ouÛô¬?S”fŠ”îŠ®`{h# ¼Ú˜»þ{µ;÷Û~Fûº«½A®",ôV‚cPBêÛ+1†O§Ùq™ml¾·Óµ Ú݇Oó<¿‰U8†Å’QY° 4 VÜ¢+ļîèÏ#ƒá;ƒ`ïG{w¢ðÛˆ@9Wz *ýôôx¼´´"’©ÃFöñ{¨nÛÕv)X *b îñ3¶ºo»‰v ŽöÖèí)½Ÿ½²´ºûpC|ªúÃ:T[Xà áÓ– nˆÐ …¼‘þ­nÎ'­ag 3½ ÖëÀ¡~L¿/Ž+ÔÛÝþN|)NV; ¥ö¸¿v|ux?¿–ØÝµÄL ;¡—`a†üøÉ® ¦Ó80{5zµbtþd— 3àO°‡,­Ã–9U¾ç/[\,T›Š])³~À–xä„—¹Iþý»­‚_mó²ËæÞA&jxÚy‰uû?º¾³ ñÓ[ºÕ@ÇÙzXï!g£·(–4÷æ¢LÓ÷ý"¡Nß׿â™EUÊOÁ]Æk¡ô¯²¬é¢¾!x 9ÀŠûo2ŠU.rëE{ð6vRðû4ñË˃—éa¡ëìS^(ÆßèØ{A¾Þ—ú¿:Øû«ü÷¿±]@bendstream endobj 55 0 obj 4229 endobj 59 0 obj <> stream xœí\msܶþ®_q_ÒÞu| € ™éx{ÜÔmš¸²ÒiëäÃéÕj$-él·?£¿¸_°KðÁ;I®fÛÖ</»‹ÝgK¼Ÿ¥I&f©ýÛG—{_î³³›½úñlÿ›–¸>Û{¿W&Òþ©púèröìÀ¼˜e³*©ôìàt/MªªL‹¦×l¦sóÿ*ŸYš¦ÁåÞ›ù‹…LR])5ÿ´XIšfZÏW Ó ª˜_.–B%i!‹ù;÷ðb¡ %+5?qÏnèí¯ÌC™k™fóÙ⧃?îeY’‹ÙÁ·{æÿ¿63¥›¡™Ii9øÍóãR¦"K”íw)“¼õ¬Zjæ¨ÌQé%ô| ^{Ù¼Â&ÉvjY%»¹YrEä%‘çD^Éz˜™™ÆHöÚ-‘ëX¿‘!‰K ¡;~‘xH(¯õ|¢ÈT€ÿ:-’̨TT7§· ¯ˆ|~gF2î©Fzü;åä)p`VVf*UœYXo! ¯¡bÁ×X¿`gDþ 2ö¡v4ÖÉ‘ùø&P`ŒËðé–&êÁåÿÿ't6É·±¶Ì~lˆ<„ ‰ÌI¤¯ÕŠWX_-øKÌ:Âe1Þ³¶GËX¦kÈð8ÚN»l‚Èð$g°S],é§DN÷³S0ŽQ®Á¯hÛ÷5§t`×ß ¢ˆ]÷–$#5|úv¶Oäß`ƒ œ›/V‹Épf{ùѯ7Ž:tÔÆQ§Žzã(Iò#£Nâý)¶ë?}Mä3" ò÷D¾!RùSlcdµãÀ:60^|H­®¨´Lв4ÑÒÁ±Vþ¹·ìž-[E:ÞÛ¯u(j·#¨Û3­XmÁ£K8{ÊdÊì0öúQU˜j|ǹþ+G€z:àzX‹0ö_@·=†$ö}7Ä Øh‰d®xë, >>¶XsŽá®Áÿt1Pˆ=æç6å 2æŠÖ½¤§ÿçQhwÝÛöó=w ˜ý;ðë xF{|~}ç¨ÄQ·`Z·`0ÂB¦¢ Öœ5K¡²")5WV†W˜u|÷Œ* ˜ò^€9tbïe3ÊݹLžÕ‡ï Ì,ŠÌŒÛåo›äŸN%K~EíöÊÌcUñ¦ôþŒÞRSêÊp#·yFãYnòÜe$¯ê–R¤E›Å¬ÉÁ¬ê§3JT²¶W¼›a0Ãú)›âªn 󲎿zó ?,Ló"Ís+‡®9ãCýæšú¿´Ê g^Á1‘O¸Ùæm…´KÌ´A:’8›gmÖ·îàÂ6•yeÔ0³^1ÍûÒ²Š#DiìCƒŸÞ°†ïheCØ×Wðé!½¶‚‹bªÙ¨«Zðî­&UmgC/a6ÿ8'¹±9žÅ8h¢cSã/þ¾Èr³Ü\Îÿ±XªD+¥Ëù ê–Íöc³ytUZO xsrÞÉ€ýÌD´¡®nádÏ?˜àÙïל®¯÷|C…ËŸ,ë¬Wê\ó˜È’¶ÒÎØÈtfoªèÌ¢RÞ™EnR5G&àHéBÕ¶_%y•¦6˜*’ÊrÜZ¾Ò/5a[V²*r›¦r/­}AlìQH*…ì5¸5Ýæ•Öȸ¦W®é™£fôóKÔòvzm›V™ìÔ,%·Ü´+0Z-øðk×rÐ}‘—ȆN‚ò´ªÙ~Ù;F\¥UF|Æ dZíì/YâÙBe3;Щ٠eÑY€ΕþÕQ_;êÛ…NÓÁ\­éQÚ϶‡Lë…±E©v6‡¬[¦Èd‹FÄšê ´GØ»Ø=·p7fÆhHkVE•ˆLæ|‡õ[V­™h-q‘ ¶¡à†¦ß¯F¶«¿÷™±<âm=wh»]CF3K·$®¡ f㲉]7GÊÄX£NYð"›*#U¶\lv±Z¼§¶lÎG\³ƒWPk·X6sÈhÄԀϩ9GÄ‹ºø…hÙb»Í•Š ~+ð»(ŒÑ« +Ô¥¥Ë\Ï–{Z¹nÐA3Ù¢S_CúÇÔlö¬%Îkî¢(=D𖉚ƒCø¡‰ÉB—ŨÐíGdkŒ•ï ™µ?Ûb¬¨¾Mp35!æfÐüuÐJS÷Ñ8ëŒiö[6+‘……m €³ìm¨¨}Ô3,»—m<Ôå »j†ù ÎAj»ýò©nq²þ„ÊäR0| ÚÝŠ2׎ºrÔSè˜t“¬*xvtvS2·¥-cà­‚·ý…HtiÝz Œ Š»ò)!·€e_»ÙÃׄYOVU’¼ÔeÆ`Ê+„ÐNØÚŽÑ^ÕmUZTš¿Î)Q´ÓV5.”‰ÎÓ&†t½vÓbØ9òTVµå>òöÞüà¨ï°•I*U9 Ú5qè¡êØ{‡ÄÁÏkóÈÔ†p u0÷²ÔèŽ.÷ Ï „]ó~<ANµ½> šò!N€ø…(æ³Ø2{ÌêpD$8d|B½²©ô`Á®k ;£q£‹7E v²C탵òƒªJ3;…¼a3ZûDãƒr¡;mÁä$`¬€r­ÐÅõÁ³kðÊZ²"­2[n%Q‰²K Ô °kZ”Áýì%iê×p¢¡/eÍ`ùE¯õ¦Ôe¢eF@“Lß'4™™"  ³zëvþº³ ÇuM¾„Њ5` Zû òaÖ½"¿aÊFŸ>ìé%å7—¡z5”Kýä½[݃š·ƒYUÁ€íðÛãti~+dÛÆDàЮ{âíƒìJÛ{œ›fýúÛ²N@¶9ón³vàÞbøÆù¼ï§ø¼p‚Ú &°§L}†É·4-Ü\ i È)Ô-½Ê—i_Õ-Ó‰lkwrœ}Ï\g¶¼°1ä“‘ ŸÜnã Påñºä®é}‚ž¥ù§®YH°&䳘K^VݶW© gô¶ µÛXR·º¨<=«?d¶‰·$(±´†‹èNjøÈ—DžR·bF:'Ä@îžbÒ·=-íÀ¡ˆ7& æíœ„µ{yÔ;‡ ÏC9$O(&¼4A§j—bƒøQ@ÔÐÓ¦­õ( ºÀåÐû–lgÜ‘de RÑøû¹á¦Ç‡rÃÏ™¦À½£Çé̽„ðäûÜVˆàô" üóí@MÀô÷ ²è^,3$2Mt^¡#‘)€¤ºÂ g¨#GÏÇp5Jˆf Bþ*=N[D2ܳ멈mÎÓ0£aú½Y9Œœšc8ûõ¢gjG:qPs©d™ÂøÜ¯{ÍlZGQ þÕ/vn ;g˜Yçý«t’fY^íž¡|Û.‚NǶ‹ØØÞf³AC^ŽŸ~úã†B ¼žé§Ö‡¼‡îÜ^9•;€­_ê&žÝ±¸A9œê+Ëâˆf±AepZÅbƒ3g§| n¬`q>ãê¡‹ÚBº¬‡ Zc+kœ‚±²éËöò²‚÷œÐô¶ÊƒeÇ‚(?ŏaùTIZ‰O…ž¸ôbUjà_z¦(R£âï€:¥øYË{b…w ëûéàa¡—´´œÊgõñçýw>aÓ÷¿„M;†MÏî%lšŸ yGc ·«ù!èc÷Öv1 ’3+‘f{u[-6 +hÙð$·Á(%]Å‹#\!Þtɘ¢tcÃßòJ´ÂÙµ3Ô4šÓe '&^42–“+KTi#%¤»‡»¤Ö©× 6ÜŹÐ@Àãgþü(bà¡mV’ê™9ÃΟ~Œ)Ñ`ÖŽLGÜ&‡œyø, oèZíÓ*‘‡_C€ñ 6ácÊ` jq (+l¶xÜ#d8OÖÀb›(«ð†g ë›Íž ެ;M†qV ˜£c èËýjf¬¯+¥WfzNtï[ c,KaËñLoYfc¤"ð<[,«¤4½ØZÈ¥ÐFÛ”®ƒÍD§•Èj•µùì¢á»%E[#WÈ*-­JšžÍŒ%\»l NŸÛñò²T¼»3GÍXÓ÷ô­£NÚe”ª®,ìºjjþ”.ês=÷¸©®«T­>hf+Ö_ÓyÓ&•`k²N“\gnžuƒý†YF0Áþºi«b>½ÌOt_È8ÔKŸê=³‹4:• J(|êkgߟ°o˜Jãó£mªîoúåvÌm±I¦ï¬hr£ y¾ÈúŠ‘£¥‘h¿¶#}5fÑV¶àƒtx¦~ÈùÒ¢Õo›#äÂh”Ñsˆù.y| P#Œ:Üd±8k;ù\NqlÍ<ûB—¿G±èÊ—¬¯k¸V|bÚ/‡¡À;Ø ¤áïfÿ i «æP1ìà…aGÈØHƒ5z¢³*,¦Æ G­Îä&0s)Šh2 db„è«"‡³JÊ$«x‘‡³´Å™¥<¨X>ÞfÛŠî•C0̉û5*UÆ TBãÛ0¦fgPƒÁkw´¾v† §‹á,p&ubi'©Ð\¡ØïØHa«×­FòøÕìá-'C…2;1Ÿt˜T‚ϒ‰ª‹Êæ^j˜9ˆùaÅR[E†ÁQñ;ÎÉìþÎ%´;Ýøûî­Ž#cy¦hÞÂ׸Á7Å,Lf“èÍAB–¤T¦Éꙕ„Éýжì٨ܢ螂WÀ‹Ráµd¤XGÔ’^øp@Râ&PÂØÃPêÐOJô ë+Ší‰m©òùÒ¾ßbaɾŸ«ú%ö4´‘œ Ù¬0ªllJ’…B1”Ê4•áøÎƒÿ»/Ò`Ù*þúÍY•¡)¼!™}` P¼ž€m¬zðûýâç58F?_­Ñ³?8jæÌ‡Ô·l ”.Db¼kèë¿à…©Å "ßR[‡á>´èÙoëGd:¬Žß& Âé‡ @ðF¡Kø07ËpvL³§ÌVƒcYÏ)Qâ8úåö'ä$ÆNŸêM? «™¤³2Fïž™$³è§³øv•iÙ´\…’ ÌŸï›}…3`Ì{™lód*é œJgqFÙÿ˜Úû|ì˜Óûl'P›Úµìw@kÑmèêû WVã¬3aÿhì»­úŒ(cÎ@eèæwëf±ÁŒr«g\šh³L„@e‹8Oz3¡kÚÏX©0çð'þ0æÇ©‘h(ü:€òXÝRènd]\\ƒ‹¬¾¢‰V$þ‰ú”+ü?7DöLŸÇÿ™î{‹Þ®ºãŽøRVö”Ý¿¼Eï_f£á»ºñ½ÞÛÜ÷ >Å÷ɲ›í£b—3>&¥øÓ/Jq¥øy¥@wI>&øb7¸€¼ÀÛkÊM¬¦l4,g|yäŽ"ÿ4ñKè£×Çb>4Y@[ ^*—xÆ¿´rÙõÑ+(ïŠÁ@xåõ5ŒäõCtJŸx+ ‡uš¸ ù¢N*QÁ| )ðö²s”HïU„y ‡àêŠâŒLE’ÂBÑÒüæ8 ¼MMR´tûÜ}eYæ®v"t &(îà©tFëN10ÇG±¸ž âˆ#š ðaoZåõíÃà< cJüaÓ ç§KÁR)tÌÚ«íÀIƒÉ•¬ía¿Ü©Uô¾8Øû‹ùû_ Sendstream endobj 60 0 obj 4236 endobj 66 0 obj <> stream xœÕ\mo·þ®_qߪ+|ëåÛ¾ÄmÄ6Ú´Nœ8  IY'ÛªcI¶,;Aÿ^’»KɇËÛ“ì¦IŒövÉ!9œ—g†|½ª+ÆWµùw"N^Ü}Ò®ž_ØÇ«'‰7Ï^t•0ÿØ”>yµúìHÈØª¯úfuôì ®ú¾«Û¡U¶j”þ»W«–Õ•\½:øáðh½aMÕ²†¾Xךà²=yÍi4P…næÎÜ7[ÚÐø¹ÿõÜKÆ´ñ”ÈÚ/G~Nekçö :û®ë%`טJ½;§ ZjÔI6£‰”*Ü¡y5i_¯d¶HÝøŸ‰Õ¸eÚzT×T4¢U4_DCO|0m¢6…{Ë™Æwf'´\…÷}Nó£È»—°± ( ä…avkÞdtyõŽŒT‘mÁNäù 9$«š¦žË0 ½R³þA,Øž2.2¿Á—˜¸Û±_C¶ÈEl vÚ¥älà_Ðfg}ã;»…ä»y ÏrÎ]èº Ú;wºÖ50Í­’Ü-žŽo‚ų"£»†«Id’u´­†ë89‡¡¦’ ¯xϬ¦’´±Hé`ƒ‹t–§°téüD¯©¨J›~Í(›¬vž ô°Á6>‘s3”ØÇz¯×¦â]Ów™Ýòr\Q.3,]@žƒSŒï™ÄwF©íá%÷9=feŽ™hPy5åæ)ðZÜLãÝä¶k`È'™/lfÜgn‘ƒu g‚<Í9Bz5¥ÐÛç×5k+&kE™!Ýþâ•q¯|SØëpã~G|Å|`r÷‰¨WLIz쨗v¤õCÅìH¬7Z_°¦ÕKeTêëZvÚIîeÓÙh½Ó´41¾nNô­2Ž&Ûºn” aCe¥·¯¥¸ Ô™oòÜ=|ÿùÈ=|á¨SÂy•pBÚµn4O2:Ñ4 ¹]éçcÒVÊ?×áÀ  õH7—e®©ûª³8œþèè?6üÊ…‚ÉF3ëìƒàÄ㈂F$'ÎG´'WPz±Ëæc`¨wÄ!œ&Ÿs`£ ½§‡±…l$N°whßF¦¶¾ÑÖÏ6 X}bgõb4Ê¢b €ØM#³N¼¤Á—môä•!¬Àu~V*¹Ï/ ÇÓh4ä9P·^îéãc½I”$æ0‡àœ4Vpé0Ú,)ñËø³ÐŒªþÍûþ¹Å%q†Ð¦VÔÓóN#ܪñUÙ8¥×¯”^'"¥§Zn Œ7 œóŽ¢[cLrcAðs¶Þh©[1kÛ¾ºïA¥Ë¦åÖâ¹· «¸j¸õ°µc%¦ò/Ýt²›ÔºèëÎŽ¶®”ÙÚoì+zƒevŸ>0 jýÍÙhh\+úK®ê>x|’鈾³u,®Ì(UgP¤qhJõmÂáðòå@ k`ù«yÏïüuÁ»ç~\¾·3òí‚>›¨«‘w»÷vµxJ5U¼B ‚Æ ÕO7~Oç¬ò×b둃º¨*ž<ûU± ¶Æ¸Ø€]E,Ω–éƒ0œÜ ðßše<ëJvrR]ŸÄNêfzÁÅ–ƒ,±®ª…ìply½î•›t‘‰²Ú–~ê¼­GNNˆ@Bø;žR2, íIJP\©À1G”—gÆ&ex'•¸Ö1Úºèà÷‘_2ôÔˆÑÕGãß:…Ý’¶£|éÁrgñÆÄðN°Fäæòg èD®]ì_$Áf³7î*Ù K7Îg(6ñZ™ •à„=2V(SD•.Ç&n'¸Xš%#¢J€5ÒÂØóÌ’.)‰€JÒ-†ÐžÂw‰3 V(ÌM$+…‡oGd™W—àùãa˜KL½áŒc’§Ðeÿ Ÿÿq­ŸšÔï&¯Tÿç$¿ÑƒóYÉ—s‚Oôí]O>^ošª‘ʺ†7 û–$«Š:k’ùQ´0ßFðÀ8A4“j±ñû4ñÃø,Ïx/¨\Ú“[5”×VÍ$kr‹VðàÂ¥œvIÎ.íÏïPÜâÞÅ•'l{˜ì³P¦cÛ‹ci-%B 0å4’I°Õ ä…aml¬|דG™áU3%îõGZßl¤ÁN›ÀÒzó9·Ïò¥,Dª ÃÛ"Ÿ‚°cplÞ)Ò¦þ¦&vA¢xhÕ€ÌW‘^ öA@ó™ ´á3´f},ÑÙT7ð± øï L¾Û9¸Šj±¼Ä•·a ïûÒÛ1âæÜ-ûyúɈ±8\“Ÿ?™¢ü…LADBߦâJÍûU¸ºÛŽ}BüÛJ÷LÉ=(+Å”ö$—%ð éÒ™kè~<ÍF˜ßÒµpÅ‘ÞYõ°*©|¯‡5%Œ  ìÁÀÈJ¶Â'Nƒ¼vÑ›^ 'ÞFabÜÿú•‘AÍ*[£>Nèe›9Ì2-k¹7Í!ý­‡ƒ¯È‡ßÍ!ɽ¾ôm>¦÷³®µÏOG»>D•aŸyXyäÀÊfœ &l èå°òØÉ qeÑuUÏ=°¬EµëL‰íˆ0óNï½¶ÑÖŸ÷g|Ò¤ Þ®ß>Ï:§â³ ÄÃÅð ãùzKª° $O18m¬j>»X6#ГœÙúÆ4ï‰= é0-?µÃ”‹‘Gûˆ–Ï"·³|i½ÿN‹˜ú¬ÅÈýÕgxnRƒã>‚ê@|BÇ–%iEÕøäøwð È·ÙÓ[ž–Hôj •IUæ Ÿ‘ ¶ÿ¸ün`¼ÑVŸœ§ŒyÜ—–oð+ï©3î¿òîŠþ<=$FfÛ‚3ó¥9øEØ ¯&ÚÛ¨1¾€6°×2Vcñ>‹Ùfâbl!p%^íT_퇯à«Tÿ½6iåN©Ã‡£B•–’Ip©ìå ¡ d³ŸG$ Gô™÷&ϧkÃd9M¿¿$4ñ=.bÂöèêYìç8paýcâ8ÈÉoðÉÁ\¾ù&õö%`‡´yœ(OžÙ”KNÃì8@Öè¥Ù—n«ƒh+–|Òê%t_žÂÆýĺ“ì;o^*¸Ò$Awˆ~'4OÔî΢²wZKäË¥õ–”\›»à˜Î¼‘¼±mþȹ"4±K°q¥|¨_P„.á4’á ˜¨ ÐIØ~æ*›{á2ÍÎNøŠ*¯¦1¶yH¾ìeàDh•Î¥ €É ë‰GA¬l|„/9ŸMw^yÑäß‚s™QÁ3•Á‡1Nýióbßð®6mÂÜìá§ÝASÌ­v_™4+ÕS¹Ú‘ݸ +ò±‚5•.—p3©(íVKUuMMõVÀËtê1eÅø!ú P4§»ÑúãTvÀH(<¡|ph |[÷,T K’U‰¹V,: OPNóUÌ®àfq>+gÉlUœØ£T%=î‚éD·OGJ%È IXˆ[SIÉÅæ‹p>TvPP½5sâ1ÊÄ”=¢†m$R^ä‹•Êl@tµûDv|rœYr@Ý»ÂÄÿ…ä ò3N'cDœºyxïsE0æþßß¿½Ùœâä~19Óus¥ñÄ ™)-ÈU¹Òž™ ÖàëÝÅŠBú¹Ø$+T™¢7[´†Xxåð6P¢Mžf‹£QÆŠ5G$Àè >íSvx`KLÛl¨Ð„§qv‘Q•ò™NìÍ»¾ÙÃñRñªiÃŒ×Ûx%ÍÜ8ÑæÞl`;œÌWú÷Á¬üIˆŠã˜1çß/V ‘˜Y?6¥å‹i3;%¹Þ‡Zv51D\,(–½å$y::Õµ¹  Ò4~rÛBüX·g€ô0˜ª{¤†qÑAq4*]¢ÿˆD½†bCºÈªÚ ÙíÊ’…BÆpƒ½pÊ…¸­.&ª•‹‰ˆ¬¯K"«zj²ÐÕ4ÊN½ÿTP4¤‚ï½?ÆnéÄUÅözU;Õˆm¡9.¢1x%wP$BÇ2Ýd ç…Ï’š’ÿ‰ÜÉHÛ=¶‚£bˆ’*0«ÚrÞ”[_œ½,Ôö”tI1‹¹À«Úóì>aGo8ˆ/e©w¸úÈžø¯Fxñ‰|²ðÝvÎðñŠu€“3›)µòj»´f‘ÖPÀ\ÈSª–b¹Ç.—¯»ð:&aDf¯ºÕðpv $ïñÁŒNÃ,4Sz¶›aR_ªj ÞõÜŒù©£þo­u¥òÞZ™©cRiO~Ê7Ëް®œ(˜[j®M—4#M®ìÄM/ým¤xc*aLJoõ«£.uê¨+Gý>Ó´<ü‹£ŽµÔÐÊ›ø[SKpîÉçž<õäß<ù›'ðä‰'/ Q o2¾0 „0¸ß¯Q–ßbÀ0cMHr¨‰$ŸÉ çTJÈz1.7 Ã!Ic¼Èð¸- à‘bœmÀYNß<-ã-LJçÿoIõEyF ÎÁØ™c”;> ]áOÇszk€2ˉ!Ú±9ᛇåH·>9‹»ál”\~yí²®ë\ ÝCoÊ¿÷ä§žü“Ÿ{òË].—šÓšI0àðëu35ø€2áȯà]èßÃ°Š½TGÞs_bÅæ[HoFÆyf½á©Ä7“‚cû°þ*¸Ô¼eU-”/Íõ10,˜=FÝ¡dCßçÎgC90Õ°tN‡ÿ/1%5¸ßJ[ÌÑBï7žâM[.÷˜\" Tàûåá°."Iäç;£FÓÞG4Zè^LØ-u±zÇñ_8r÷®p58>þÑs⡸áïÒ%œt}`ãÐ~ÇÆ XêÓ°ÄÝ•hœ§•hCqÍ'ëw›d&ÆÝ>ðÂlvŠä9H༂¦‡<} Ò‰Í_ÂŽK“þö†;Æ@ i—ÐûqtOOzóÎ{ÂavšülŽTPãâþÿ¯#÷d ÈiêƒSöÒ]| äÜ4Sê.øâðì§`‰˜½–‘ÿÑ–è 6vÓÕ È; >c°·÷ž<ƒœ½È¨#1²6ç?]:d îJÚúëü48ñœz£ªá–ä“ÍJå=ÐÞ÷ ßÇ}ðíðÅŽú:þ•¬žlíÌ4µqÛ?„l/h– ("‘8òEâàÐÖŒ´\; .[‚Hãm‹ÃXò‰hï—x$½mý€§¤‘j*ÄZx|ÿl›ÄÐ_ÃeÿðèàkýïüîËŠendstream endobj 67 0 obj 4654 endobj 71 0 obj <> stream xœí\[wܶ~ׯطj{² âB6±O}SÛqko뜓ôa-ɲÉR¬Kâºùï@3 >ˆ«›ëôØ~ 0 >Ì ûó¬,*1+ÝÿØ:Øøò¹™íoøæÙóozâÝîÆÏMQ»¾Ó[³»Kû¢hfmÑêÙòõFY´mSšnÔj¦•ý»U3SU…lf˃6góE] û·Ü¼kÉB™Zn>Ôƒ@Ý Ôã@ÍUªÔW=%´{E—¦¨¤áScr»{­jëÍ"÷aë l¹"r ¾¶Gä!‘oá`“^ùrÆØùiŠßØ¡"²¤°}ÿ¹ü‹Uˆª alùdÃ>ùÆզq-ÿøÃæ½ùB¢1¦Ú<—…©„]˸*ʲ-'wÂó·:±T­´.ýêTm!mÇ÷óÊ­så˜ç ãÒš d¿Ïrc*Ùò®¯üóZ”&Þ·®ˆüÉÏj5]nQë!‘{nÞZ%z¶}ëIÌŸ}ËýÝÍ uÃ_ŠXtŒ×ª©9_¬Ã6uØ›Kס•ÝTŒÍýðÄ®¹¨ðÌ =átÆ ¡+aGcs‘ýÒ­n›~ŽÑмœ1™³;^e„Ð…ªŒÅŒå¶‡ašN•¶ˆÂhå¡ÇwzA*øÈ­ -íÆF­/¡ß%ò9‘ˆ¼Cäã¹.K§‰Zë²VBÄp´ŒöQ·\,‡Â6×Éô‰-7úŸºqë²ê÷ÖB4­•T3[¸µí6Öò_±˜ª²»ŒAÛ‹€õ,P/Â~ ]„Æ“@½ÔQ vu¨ßÀÐWþ ß“1åVï?D~ Qn’ =g°•À x‚îm"?ùÅÇâL7vFœ9%ªkm7K;(/㱘"KHÙh„û‹a§ì\W%0¨ç“ȇÚ"¼^!¸g¸J/ÑÁÀà>E,wÚ*æ+¼uÍçÁ0ï¹ÇA˜ü0œ¯áa…¹Ç‡ÂÿY/êol.jÌÚí)b]§H©Òù„ö0&Xqñv§@U¨&œkpýª{é[ÐMŽ9÷<=æ,ƒ;4û¬"…Üž-×[ÅšLöÌÃùBZ}wë5^éž?WËÎ^d¸|Aš&½…d»Z5k˜5Cû亴pl”A嬶ԡ/[j?Ì>S(´…ðÃ&È+`·ä6ÇÈb*½O€ Œª1…nÄ ßÖ1«âzìi-€¦š´ë—´eþòymý2Y”’¼µV޼5ÕZÇPwÞZ=_”…®´±&DátÜ>,åf;7Eë”ÝDÖ$i½aÇ«[ãwVxéÄ7›²Ô*¢÷h°·óÚ}p‡è@Íèñý¹°ËR {Öq Z>Òë°:°±þJL®ÂówîykÑmÅ^wb¶Û^7UösvÆ#Ô¥7||gU+t.fd{ŽÞ?î Å!ÊÒ2 íBjѼÛà´é36²U7CJFYWK§”©ŸÖem]ô¬Bºõpn5Ï4V5%´‹bI!ö®¡lØ7i»³÷^A2‚†°)Îâ³k £|J» ºAÒƒ2³uÀ‹çÄåBßpÄChº Oçcƒ5<©ÜÑßÀ„‰* vJœá‰ÙA‚’C¨˜³C¸¼¬Ã XªÊôÍhâ3ú 2ÖⱎH"ø b­ŒÝŒ&z#§VEe‚ËÆb2Oý¾º­ «²Wñû8¤nÕ×ÚîÜY öµúÝ>wdrWzÚÀÈi¸-zùöàîhÝ‚ŽÖô˜X”ïúrÌ÷aÜ8Åœ»jäªM†ÙصÌ}1ðû.òí·§Ä°®çx wQ‰A­ÉáÈB~bŽœ«ü@ìLƒ]1ZœBôƒ'ã…êkcLm²3x#`é!E6Âôòq›½$9 Ô[€JºqÔˆÚÒ=E•n’\3&÷ÍuÁ›Ø#É5Û É' !ÚôÔî€R·B¾«YÀ(ÚŸÌHýlæ†Sãš›s¼½Ç.÷¤G<ìÀ0íÏѨÁBvèÀ¸š´Ã"«Ó(:32¾›“f³„²Q«!ÞCÖ\»™x_Þ.ÞÔM‰œðû<Ãáãn”Së+ÛŒÝÉäHœœ…÷±C?¯Ç‡Ö9 ÅÆý•ÆÅ&* “±07[pØ((ļ ô•¬uÅYè… Óá€ì¤â0¿/Ú×Èï[ š°èczN(%B9ŠoÁÁ÷Pߊr”óxÞ¸=LcѬn‘†æ”…¼[窗"ÕÆÎ =#Q"ñÔµhOì?ëónv{¡ññÚ_g1»²gÕ0îWPå`l£SÎ Œw±öq2Ì€—寋mÌS´1OÆòpS[˜ŸÃ¦~³õÐÔæ’Ê™ø>‚úI@Ëå-Xض„í[äïJþ.³ñÖª)*¥n:]{1¤t}k­t6|:`(ÍÉ…1EÛš.l M¡:»¨ûôäàï<#T.A†ùP,mÌ‘zT£%Å«.®¨ÃTÞ’XWIEFG“¨xp2½/älÅ;.­[)‡Éå)‘ M÷ECŽç I‰‰Žä¥+ØØîóD¦¤»\¯XR,ðûo·­M©T^bkÇ#;$LÖIDI.ö¸O»6RE‰§ð|NuÍA·œÍ µ­íSõĽÄ,èêÎ*a_5‚¿4ICйs¬Ã vxG`}YI®ùZÁ×°ûÉcõl¿ÂnÁÖf mG~X÷> üÂGu|Löˆþý÷ŸEÑë±è×ÊÍÖªòŽsr³k.)OkϘBÃÒ©,qzÌÞ9 IØ}êÀS£ïQêu"‰%^i¨w¶¯V¥už{ÕEk´ _eÓQê•Á’¯Ž3£ÃÔUÙD#w\JáL¦êº6¢ËaËÒ´:šn•ÉŸ†A.—ö­kUMyߨ¤äzÀiÕŸÝ·/SNÝq*µ\+,ͧ’ fýäÒÁŒ Ÿ#›~t…t0B\ ^#ë*[¿”KçK_®–žÈJÿþ’ñþøÐÜtX€åm®n¦vQH«eªUoë熭æYùˆ5ÁB¶ _ÞæK’8ëôdiŸá²å!„3V ËÞ™-8ÎÁOÚ² ÀŽGÜ ‘ÑÌ•Š œ§Mü#Oe¨<.5¥‚wl²ãRT\qÏ>ót Ý}ØvÈ©“„{â°2™ÞUf6zßg¤E÷髉̪ÇÞ.ß…ýÉtõz®†Uä²€•x^ì}AEÌøF}ºÐ«‚ÕýJd¼Âáf‹Ýšä®v èóY3t݇å»4I=Ežä’ÒÂSÉ/èpS‹®GÊ:äjàãó4´»º3º¥.ƒç\¸šð~'¯ÊÐXçä’–2ôvXŸkº¾(O•N ²2t¼$¸x ß,TMZûÔቼfTˆÐ«?ûÖãNŒ(º´ÊHêÕoøá4öˆÓôZ¬( 7Œ¼ãý¬qJ„ɯr8ä— †Æ[£lÌÆ^MÊÖZŸ‡b¤ áÖ’ãÔE¤NAuócEO„nµ=ÑêÚ£›ƒÖœ?Ï]¼Y7>îŸ/%#qƘv0Ý›%k,{#oX¡@f÷dh¥Èíj¬•Ž‚±m¸Øk‚sü[ŸI×4)ØL~Éâ[*‹?áO=ìL}+¾Šm…œ§Ï/,ú)ü­½‰@|Z £ñª.„%ûÍIEyTbG b*Ôû>[TÛfeZéà)‰‹ pr³u³†BÇU²oâšGH3±¢Ož|ˆê°d;+¥9ž’/È®½Þ'ò["ÂVv–]‘}‘]%‹ª”õÄB‰AÑb!Šª$?Œ,ÏôX|º]ô›˜YÃté…Çs¥Ì]ä¡z€‹id"AþJN+:Ã*ð{T½ZlÀ3½J ­¬lwŸ¢™*QÁY™{ ݸxŒÀoõ÷@!r ÞEs ¼t¥oBZi¢Ÿd…k¬Dýˆß/D^½ÈÀêÒXê†% X Ä1&ñNmÁ¾,)õ²³ ÙÁY§®2¢Öeᔥ‡œ‹ü\"þ®P¿7ä6UcµU6QÚû’’Ç5…'ð«ñ’Bâ¯Ú‡$.|LË#FK³ûb&'$õ=…}÷ tÂlpmäe±Õ ÿìò{ ß0[·Ë×.eý¼á/³áÑh¶A=,7þfÿÿvÒ¨endstream endobj 72 0 obj 3565 endobj 76 0 obj <> stream xœí\YsÜ6~ׯ˜·ÕleAr7›*ùØØ)G®Øç!ÙY÷–4’uØÉ¿ßtü@ÎŒ¥Ø§\mLãî»ü0K“LÍRû§/w¾}SÎNowšæÙ›:àætçÃN•äö¿¦A‡—³'KꨪYÔf¶<ÙI“º®Ò²5›e©š•Y–èj¶¼Üùm÷í|‘'E™ëÝzí _4sÐO^:èß í¹ƒÞ8¨_2v’¬Êt¢êf¾TÑ:µo[ܬÎwϼbðƒwÆàƒĸaë9A ¼‡Çp¶K¸¡#8Û lãÞ€sP4Û–?îÐí},_í,ÿ¾Íýï;hÐß°¯&-“L—‘{ÝòŠ·¼× 8î)<Óé)ĸ+ˆ+îU\º½ý¥üì _ÀU Û:f£É’âØêŽp,7‰F8‡Çt?ÅVwp‘7peâÌ?ÂÖWŒð®ìx¼õ˜m „èÙºÛTYš”êaYl«ëŽ]¾Ík8›èv4µLFx9˜úN°ÉæñŽoá`-Áè´Jʪ"e½<"ýü†ÿ…ÁýÌ}·E+G޶Ríï€ì*jI‹<^¯ZZ|¿ÉÁÿµBK¤„½åˆqWp܈€ùA v wáMl‰&¯MRš¢'µX®£¯wÑ ´íì;9èØAtî C€÷½GjJgeRÅ,‹¿VÖaÆÆDu?5±XäÉg¯lÛÓ×Ä”ïYM-}‘>*òž¾ºqÑ#<QÝ;hHén@@<ÊCK­ÿ“Ò)ÝÃÁÎÁrzRùöM–9—v‘›$5µFÊúËD7„¶ü¯ïøMUŸay­oQ›4,Ñ#ŸuOú³yšä…1©=2¢ù,WiI{wàƒ³¹*«v@±ewªL? í'+£u½æ†ˆÀ—Ü“h#…Nr­+°±äó¹¶`ÝðHiÕ~^TVµö¿©¨<Ѫ.wÿœgÖTr´;y´K+Ý“´,2¢)q@ªÒÞYŠßi9YMäµ@£Þs¯ ÷»¸!±ª+n%½^$išQë¢UÝl@Es]3®X÷!#ðbNš³*3-¦ØÌ=¤îuçí»àŸÜê·½²2yi!ï ®å@žµæEM€%•šÄ”ŽJxÈoàìW<Ñ •¿ÒáMÆg%zÝðmŠa¯½›§‘ì©$‚8íc8o;[n #qgÜ;×j…B™ÔuÙêM'Ê(+ÿÚg–i»Û-v3eÆÈÝŠ-Þ;ÔCˆÊƒöË«ÅðâØŽ]ã–ý’·Ò¾ßÆèèþæ“5O3§òt–Ñ­kkÖº kš‚þM¡¨¨—jC›ù|‘&&3%éîd¾ÐIQ§©ÞÍDs6§CÕ¦¢)hóDÂåîÞ\%¦J•²4Þ÷¹³}ê2MMaoÅõ¿™ç‰)ReiÓ!¯¨1MseCJ=4㟗®ñÌArLú––gŒžh?KŠÊTV|ÑÁÑqšn­UU[.å±bë>jfVYÕÍÖt|êv~Õü¬UÙíÅB'b¨sîtêîtc®³<µR¿9`¢œ;ÜÿªÝBiúcÓRùAÛ]·Ñ ÂegXUÿ’i Rk/;ò²“éqfñ5IpN´B¢³Ðr(f&©òÖT$öˆHKÑ÷,"-‘Å@ê)ÉëX lÆ“౜4- ¿g ¾dð-ƒ{ >aðƒÏå`d°ØãPK¦L¢+ Œ¡ÍÄ=X–R–ø

“"ུÌ5Y¡#RÎ3&;Ò_.C‚¾Þo ? y«•Ò ÿª.ѺŒj¦œš‹ÒñÏ7ÝÒÊJÊ6±JA`w·%ñ‚œi!}Ù§m¸¤Êb²eÖ @,²<»(8Qæ¦öÀÑyj²¯5Û#{Ò@üëSA‰D›Ëªºâ #Ò]ža%CÖµŠPh*¡Å½x ÅM˜‰‘àIåB»@>Kö×!4N$>ˆ"ÌørH‘c­]–…°A!· áwÝy³ºfäá~§kØgð¡LP{J (Jl*b¹†‰`Òl<˜cé Â;·0¼#$ÐÅ”UèH`Äh¬]Uc¡³îœ¬èàá Q6$B#_•„3¬Û«lxšþý·Æqý‰Pè¤TQnä™N8¡Ã‹ñ–÷bµ}Ý×rFì¼Ð¤;m2Ûé……>Íkr¸óTIrö\y83æ°1;¡‰ˆW3¡É”¼c ½é±_Õ¦e•Ê“ÌÉLOø/ °|µ>/ÿ#¤ÑE·”EG¬8Ï’&ÆåYDºì-V¢’¾“s ú´ˆ¯ÓA½ºo’€a-ÖÛÑgúq±½ZÊI /]fóG6=@äuf=b¾Zx0®v€‡Ò¥ž.'i˜iŒõ,f=Ïò’›¼ŸHà&ª²I€s!·Ö* %f›"Jˆ|ï¡tÓä"õfñSe¼|V˜yK‹iYô¾såHøÔÅ0± E©i̽lÍ KôWh–‰ ¡0µÞÁÐáS‰³ÅŒIèÊ~YžÛйªJê¬ì†VÈ9mbà%‡ ß!æ`Þ9rÿ*¤ýÆFÖúa¬³„!1¢µvÉט£í«d‘áIÃC}…Sƒd›HL˜¬VŒº3Ù«áMTœ» À…3iq &’F¶8ºƒíÿX Ç¯¸›A_hµ€òÒñ®Ñ1whk4sc¢âÒ^Øîu‘·ÎIo9“4$kHD®ZŠ!EZ—;‚^h¦¬BÖoXzé§°¾ª—†!:6àÈÏú\ Qv„³‹Ô–Ũ=už»zAG™‚çpþC˜öØOˆñ$…Öå_Y“s’­â6HûW˜o M<ÝÏgáõ*ä=z<¥ºÝcðhË—f_žos[FÀÀ'ŸŒý¹™<Ð)z«'ƒ¦ļkÆ*@Ü ±•ÈÚ=_}6ñ¯]!­Ò,!ShÝ i5¬ë„íõ®t+:_¹gQK©u~mËš•Q¥Ù½v݆Eà .˵Öý¼E^ g•ijb.Ù",M‹OÛ°OYfƒœn™åŸÉB Ç¥ô×( ˜ªèî6O‹Égi›Ä}½{‰Åðâ"ÅÇH"޳ ‰ È Y%­‹=Å’x.íáÊ[Š©ìë*œÊÐUR)?h8!Kb±Ÿ¢;­ÉqÈMêypàVÅãðŠ $tö3Áb$ºyÿA…l”2R“/އòq¢’*ÃzJqG!_„â ÎÆN¾`é«›lª½Íñ‡íuã= ˜È]¤\bÅ÷5‰;¼øŒQSâ¦ð•ñdre·=¨M¨ PõÞ%%éYãy&ýxR¿õ(†Ü‡ÁëkkSzWÛ V…+*ᬧ=Û½ªu',Õ±Uב{Rkp1Q[i @|cHÊ·^~aé!¤w?]‰Õß‹”B|wjkGu¼ºí)pc÷N©üÕ¥¡O\==öGWõ/‹¿á<̃ºd„®Ê »ÒFŒŒçàïóP„kD7ü±Éqo!Âäwï ‚c‹ðþâ•¶j-†\éZ¸2h2œÊYÂÕ\L>2ü9°Éœ:^÷Í™L넜Jï›3“ßaÒTpƃ–,IÝÔ”¹r}̶zlÏ€ÑOvâ¯VŠÛÚò™ÅCˆjÕTÛßúóåÎÏôç…ì—¯endstream endobj 77 0 obj 3631 endobj 81 0 obj <> stream xœí\Ysܸ~ׯ˜—ÔΤ,.q“[‰«|e/×z#k³ò>è¶bk$KÛ[©ü÷ 4ȃyä(Y[~è!Aœ>¾nàݤ,Ÿ”î¯#Ï·¾Þ1“Óë­æñdçÛ–¸:Ýz·UÂýkPúð|òx×~ÈØ¤.j=Ù=Ù*‹º®Jãke­ìïZM + c œoM'³Ýn1V(>Ù}¾µûç½é‹Ù¶.´T¥š.f¶ãÒLo,%”Ö¥˜^†‡ðõ$’7³m[³a²ž^„¢Éû@¾nŠ UO›’‚—IÑÃÙ¶i+i^Í#yí_ùï+1};“îU-—UµËÏa©ƒXÀW#uEßÇVHÓ§±*BÇG°­fg¡ÂóÙ6—EiD7Ã%×m{½¹Þ§Òëoó”ÌØ"±¢,¹¨mµÛ¬.¤°‹ýûŒ™‚IKMf¿íþ°Å·lPYÆÙ=ÚÚ.f;ŠðÔwF3%éØNb³0„8Ò×oš„ýßv`»ëÁvËŸG-»~½Ã«Àâ–•uÕñ®íŽ(9+¤œ¾´d¡ŒÓgÚ Ô$POõ"P?ê%xûÔ<oãßê¯úK Nu¨·:ö«§v÷uî)YZòaGòf…*& ^w•º‘<ä$’‡°ì<’×°iâÖKÊ."yÉË\ |z œ4Œ$”´<";N>[Þ]Þ±_÷™c?é?½üõÓÒ/† ë(ò£×¹)Å¡Ër°ÝŒ]yQ³®KK"–qw“Æ-Yº È•1)„Ž‘ §qC4,w‰Â–¡¸($¯Íô÷¯ ΄ê€PçMn;`dùˆ4Àã4×¹ý@žâž‘y&€?îúxgX ì£l³—°·-úë‚w,r'†4ÿÍ`GÙV¤W©H_ñù<;ÄS-â;Äy Itðkh•|È™ªo!ùy#ÄH"VÇÙÊ w¼5‡ïÉúÑúvÁÛç æ¡Áî¹À›´fKW˹ ¼…MdýÁkX`>؈÷nñóa]:¤Úlv“ßÕJo0´¹E÷îÀ-w~[ûœ°ý®þ ÈFf±LH²¸dEñ>?‚Md¿+"ã÷Wð/ÃÆ¿¬~fõ? ~þ$‰ðßx¿°D†%6…¡Þ_‰±.Š:ê&ü¡ùäçH’0ÂwPtÙ²BvJ„^ø/æ”ÀLÈF‚«!s—=B^´.7 /8·2TàÀxÁ%¯ÉȘ*´”õ烗v‚ &®jŠ EBS|"ÂtÀªbÜiŸ!iåL ”–DºM#ò”tåv;‹Jböº?3 À¼NÏH–ÎXS(p0È]jR[3TŠdH²£ÌjוädQp¯a£Ù0á˜q%‹Z…x²»_Œ>nz¸àÑðI¦2¨Îþ„ŽjN<¡Ãp‹Ë±­Y_óE-uVÔOk¸rÙy‹¤ÜCÐÚ¿À·W: Ôb¼f"§…ì—þ™Ó|E,‰É'ªÑãŠ6ðj(¢ñ!Œl QaçÌ*£[¦‰´G>¤âš­yä#Íäèêègrl6ŠN™ùYƒ]ˆ/4»ÐîÇOk0­ƒä§ª—`vû Î+öå¥ÝêÈA·‡`ÒvDd^ÀªÇ‚Ó(¢LºÜ0|‚ðFë‰W‚ÃîgÈãÃzn?œm°öBÇ tù–Fϱ·u;HZÅ.!Î,f§ô<$BvÐ’ô<_¤ëô4I¨Ÿ&ù“3¾xU•UXg%R‡èéÆuÇYÚHœ6¾]¾VSµU’§Ö7Ö¦¬W„º²Eš¼ƒBhº¼©œ¹é÷UEòÀbÁx²‰ !CpŸ|“9½Zǧ`îG/W&{~h$™&µü‡j½ÏПˆ’žŒÊW·ÙÂ^ȦÚ`QAd[rfŒ˜VÝîKäšßþÖ](ƒ7¶‹&b¢¨¸îk¢5µð”Ž\GÃZs8H,lîLÂdΫ S@bô' »9†âãË2+>øJ]Satå‘v=¹¤t!U€¦ö¬im=Y]«´t'”¢(Š¢f°¥ÜÇ¡î+úê]$Яš§“Lk²âfúêd,h–gZ{‹ÖFé¬ÜJÜàÏÄVäõbÉœ™h€ßÎÃ%«ˆú ­& Sçò|ÉXIûYh'g‘r,1Èt.‹¦4æò³§ñê“þ\BæÇƒ òøýL¹VºLíÚèäj‰"Šb!ÇßóLÌ\Þ›> ѲD#wÒ!RPxD)3´O{ÉôbByÿØtŸS1Ò•ÆqŒÃžœŽÒ2¬9³MŒz,ÎpºoS9½©$íÆ²‹IšuÉx8æ¹™e¶Òfúµ—%’YPÄ™§½jn!Ÿ¯ž3Œ‘Ir _Ÿr€Žº½ƒ¿{–7ÈJÀyÓÙå ƒ|—cà£%÷®´ó NÄS,ßdžqÊ÷`fl:·»Ð†w!Á<^÷«râ¶íŠqû±òBÉ”* úû"m?Úg‡q“Çï¡¥ƒ•#v+×Íü=̰:ʃõÜëAÎÍ.µ¯ƒc°ú½?‰®£lÛ-ä˜ÄCÚ‰Ìäì+ö1·=5È.¢¨Á*t&ž‰á葱/oîÐ2Ã'E°]Bfˆ¿¨@È{/à‚M»ž$0y€´*–IeoŸÁ–ü5Ô k(êWÓö¤(*ÇVÿö ʃœ´5Üm $}6uäSSC²«Çg"±ðm7&Ξ§rWD;‡øi=÷ŸœC9¸ˆÅ$1“;xuå|œº®š€–÷­dÑ:ž'Sس„[“´C*ºÍ׸žJRT‰1Ä3Ã=?,Ù‘Q.ásuèý¨1t3–êØÚÄaìWâÙÃM˜]6‰°ÈHõÇZ ÷qìúǬ݀ü(Ü:M z¬ì1òœ;‘ëÍ](a.c€¾‚-«ãh5=}ËÁG³ízúҹ欙KЪ§ÏÂϸ› †1æ$†5´ÍðÙÒÒ6*OR„ý9äFŒ{)`Â!MYmåɘôw¾8.¡2eíøkŠH! (^®:®¾pBïþÐ1eÝ_©ê ^xšŸ8A¾?.à‚ãg±*R€°Õq_85°iH°W1sÚ˜O£)Hî¦ÍÝâ·,⌂¢3¶´ä>×+® É&ís'٦؅ F ”nV×¹¢˜K—°@óFÈ)Ö%8 +k¶ Û§fKævÉ Öime]È*dzé@À†/»ÌyǬhÇZ1l쇂c;'Çž£¦~?!bɽY¯q4ѺSbƒuréçÙ9'È‘aôî!ñ·a |NÙ`ƒ#oŒûÞ:µþŒ ÌuËæ2„)WÈœèD“µÙ¤**]J ­Ž@-ãNyc¥Œ€b\ž^ŸÕí½ÜÎ[™Ô„_“gjëu~ÉXLdn¼§ýînšˆÁñörmnhäAl¿ç¶h^0Ãc:i‰ÜÙÍÀµ:>´Š$X÷lŠ÷$|f#¯{ùìÜ ï•Ä J- (©¨Y1tÒï}å@Î8S #2¥YY|›I¢À&ÀhlupÅϳݭ¿Û¿ÿì±endstream endobj 82 0 obj 3732 endobj 86 0 obj <> stream xœÍ\Y·~ß_1ñ‹go§y4›[äX‰ÄG¤5ÀÎÃîj´R¼»³ÞC–ø¿‡G7YdÝì‘F%@(õðf±ª¾ª"YÕã«Úþˆó«£?=mWwGîóêéßzâöâè—#] ûÇ} ôùÕê‹S±[1^ujuò⨮ºN×ÚÿÎVª1ÿïšU[ëÕÉÕÑk±9®+Å9×|]YZ0&¹\3ò}¦LWiÓ[¯6Ç\vM¥åúËÍ1«TÝq¶~E ßõh©×7¶ˆ¡øú’”85_»Šµª[¿Ý°¶ê㶉®j´–íú:Ôº”é—éJ«¦Y?Þ0UuRvë[[§c\4ë³P’ŽåÞÒ]+ºZÛÂf\¢ucGÆØ7" G³ò£i…XŸ˜–ëªQæë–T{Cè¡©mÍŸüÝl c«.Ùš–g[£:^µÊïÎjcÆÜ(U‹¾ÆÜoÿ8:ùãnªj™âë—¦`˸Y¥m VÞjß+סW^Wu«[ÓåÉsÓÓͱ¨3Æõ_õU ¾Û¨º œËª–JMAžoŽÛª®™RëÝÆð`k÷íÊ “›¯œ[ÒVnEkÖÚŽSðÚnl ŸG’4ûj#í×ÎqQòË),ÿ¿Þ9¦2«,û•q__˜¯f%™aRö2t³Š¿ÿºéÌ4DÍ-wpQIÞµž#˜ã¿ÐéÎ5$•¦­ã¡^»Âò™û.’÷´š+ÛhA¿úá2´e¿€‹Ñï íaØцM1ÇxŸM!³Œ£¹ ÔÖñ+ºâ]7°Ê]KEYÛ’9Øe™¨„lr–exÖ’_EÒq­eÃ=8÷&ÎÑœüÆÖæ^f² d>#Npm=Ð=E\{(6{:blúC²½ÃžnážÞçËß<Ìò:ßt¡ÕWsäËXvKËòÖË®µk,ÌÙÓ¬]Y^3šÍ-z"#ê°¼u‡~Ž'3Š·?ûa‹šEÙ(Ñ8œ)CÕ•Ž%Íá6UeQÔ*PŸê.P÷º Ô«@]ê"PŸ÷W^Ù¢[:0Bî"›?Dò>’7¥«HÞÁ·‘|ÉëH^ÀÆH ;XฆÃ!.#¹H¨O{›`Ê_C)FªÓ4+‘TÝN諞›Éïƒ0é‹ÏtÓÓEÔ6P¯GU®›Á· •1Ì #°.V¾ Tþ¤n)Ô$X” ÉSùú"I?À’¯çÛMõ6öl·ˆ|ŸÃiÞï¨aAYÊïsE P?ÝBÁ8høbþ™9pQÙƒT÷’p0t{L ™ÌPn•‹Ñ9ˆ^ч<™Z¤%“°¤«‘÷v8ÒN«„þ½…ímݱõ«kÙÌúÌ3‘m÷OÑ¥àÇVsU¢ncLY6v”äþç¡h2t¯n-6Pá’¹ e4êÖ).â)… [/Øúi?“&ˆæ"«Œœ¸Ù}L0²cg鎎]Ú7±1\ÿi‡“¸€á„Õ÷2ò’ß‘wè÷Ý&3zß´cs@ .þW¨š­'½ÔRtF»z.}µÜ‘E= ôð-d5‡:QF‘õÂ+‘N„¼¿{l¢íAδÍl[qT÷H’áêÉAp»Í«Ž€‡Q(´§Lf$È`H³£qOséi/… ÎæÁì¬È;ÃM]Kú²sì3‘àä$ Df2/9±€Ùš²ÈQÙ;(×p ã%<†¬vPŸ#Ñĩ̕ÖáÌÛ W>2+ÝñV±‰’E÷<£*S~à²Õ/é`RG`Sµ]çDõgeË ‘uÊAö碅1Ô¹i-[=N0íεgÓ"ko9oZ])ÁÔ8ûà_Š ç›@}¨o ÜAniL¡š¡ï¼ ÝMÖDåaÄ_¸õ=ˆ>c?™ŒKOú |-ÓZS’é /î³Èd¯acÔ’8„.CR8Æ l}Ø qꔩ‰e+v½„¯Äõ‚ã[¸? „™5²1*0ÎDȘHÁï1·ž®,†§¹1@”™íì ]°Ô²2¥»è8ù" ÚU#ñž ?ˆ"±Æ$Ú;Ú$Ÿ@@:•Ë3÷á L¬C­—ÁþÂÌFÆ}JY%3²m_o2Ÿò¤ 5çÁ«yžCß óy@í o´Û”A=°ñØÜn³ã¡lŒ™yçÁ!ûcqâÝ©…kKf P·»Ö¦cVÒr=gÓõFï>Žl`Ï;1Æ mÃSñG˜;ÌwA‰øpÞ2dáAGqüˆ…]â Éá¬OÙ3—w<8pè"I´@êû~¦ïíi“,ê²ù„RønÂØLÌF9$€Ÿâü°ÏkäAØÒ)­)‡‡ã—ÚTÁµZEI´#!)¹RÅz¯ËB™~5HC*žä”<›"pgÑßQÊ4 ~¨̋cA.éÃE^ "­þŒ‚ètÈü18 »ÿðt­ÊÙAáPèÃÀ¾—Á‡¡¤û0–yaœM#›½à™Hº^œ$Ö›ÉLTm›…ú™4?NL¼ndI©n“Ï ¨ÿwÁZ"‡ÆŒ©94$»„Ó&»i^rStnEešgâÿž~;R}^’äa®“c¢X$,B"°ôN%BrÂ{3„s*à·0˜K„KqaI¬k˜ÄM/ÜsN›1œÝÂ)Uö¾ÁœÃ=Űmîf±·+zÔÕÀºÉ´mvŸ¢n2A"dml£6ÆT–[7(©}‹(lÎf6÷ó,5w¹Œd©}lI&e=>€Ù)0)´òÙU%§|J“UòÌú¢Â9TXÚÊ.X‚£‚s¦ò2^~2Õe "íÈÿåÌ Ÿ£,D3\š<î[Ê ñ%·i?©Â(>=Ë3€*Þ«¿k{¨ÐÇ9hù P±•qšnÒà‹˜¬L’tç.­2{-§°`ãûÌ üPñWt‡9;æ1ĨA¹¸JqÕ+6þ ”û¹§¸*Ìü À ßÉÍ Ÿv‚– ±ãcðm>‡MóÔ¸-#Ö»­œ‚Þ®ó~ÉÝjÔ±eGr›¤|ߎêOg¨“´t’Üý)l¡†_Á¯°7O*ÝiÚ1©v6S&ÌK]b¸Ã\¤ˆ­<¡ÀŽäô~ê¢Ó™ôíì9FÌ¥ç6ŸÓ•Lhn ”{3Bð}f ™'2Åú6¯Œ_&XÛ«> ¿Do(ÍnâÑ}½\Ä0n®§%ÌÈÙ/#0xl¯¯ë¦Nr ‹·ˆ'o›.½çUvq& (Ì·.­Ò‚ + :îŒò^^u^?³?< ¡¢§ÑóLÐ{²°CÑG1Ž :Oõœ~£àCazþå$);j¢‰ÔÈç7g@ç2a·lá™$e”š/—^Yqþ(â2¼:áßõ©!EaðUƒ‹º²§”bö]³$nw–»ÑÛ$C¢@,ýïÅKŸØ¶ÈùÆ;²ŸBÉxáii|Õ&>íµ_€5zéCSÅL{³h4Åq?ŠÙYu‘4•æpã}ì¸ O ¼Ç…UºncFL! «q-÷Œ«E±GXíÞâÃÄGÇϹ¼‹ ,:‘öB™R8:I$Ïu–ìàÓÔßÜ3ÚJr5u2Hí: ƒ¹¡ƒÉÝç|ׇRâó'(ëx¯I64ÞÞMGk†4ÇpgbÆP!&xèDÔ+fŸ«‹kt2{\ƒéÆ­F|é„©V:“WVMg”ÿЉÿ,7­}`Äæ±Û°Rmk¡Ži[tmÓ¿\éKn }Nè+jÁÅð0H]+›cº».µŠ??‹Ý½%ÍÞù¹ÌÁØ,ÄÕF™Ïªá}kZwÁ¼RÚzÿw¡3ÿZŠVFùò”4å‡(¹=Á®Vx¾Åÿ¾ümfß6QÉÛ&ñúrñm“Üÿì³ÞmÝ€'<Ò 4¼lXôUžgIÁ8õ<»¼]oE$r$sŸZžÃnÎ…±ÒEÉ.…ÓçdM1JšHÖüÉ—µ0MnÛx3MW"¾€Iº‚i$Eí¢"eÿà ­V"³0À}²?dbdŒD}LÜ^ SkcÏ´f¡(UàŒî—ò]¼Ñ„øöÄËYç=¨»›ñ `otleþ5ÈEÞâíJ:¾Šî¿¢wï ’ä™ü@ÅüÛ©Ÿc×èƒ P…‹*|ÐÑ£‡lÞWŠ Šcôå'£²žz/|Íò—ã;û<<±s‡•(×®z aó‰CŠŒúeqñ¨}tŽ_Ïoš )£jÅÛÊ8^ŸO¢·µ‘>u)xŸGI„øîôÔk¿Èð^ g±…;VC^NP¼‹™Æ*ÚG3ÛŠ‹p9¬7MvQ3wlèÔ¯1~šÜgØLfå¥)Âk†ÄrÀó¹F¶þ““£š¿ÿ½ì²Çendstream endobj 87 0 obj 4286 endobj 91 0 obj <> stream xœí\Ýs7÷_¡·ÓÞD[~-¹lî2Ó6iÒ›LÓ&º™»iïA²lÇW;vb;mÿû÷ƒ¹X­äÄÍǵé,‘ ‚À¨×3QJ5á_Ož|ñÜÍN®šgÏwÄ›“ƒ×u©ÃÍ”><Ÿ}½„ŽZ̤)…™-Dé}-¼iÈ™­ào_ÍœpMƒóƒŸæºXˆÒJ댙—Å”•ÂÌ%ù¸*\é­ç³BZ[Zçæ‹…,•Õªž‘–×öN[%ô)2~UèR­ôü$R3üúI¡J[ ¥€o3jåçgøõeÛÇ„¹ügùX®”3_z‹«u*[­õX†ÕB§å¤,køóéÁò¯?Í—°[:iÕüe!€PVH»!ƒÊÕíHªŽ#)Q W;f¹>)º¬œ6óG‘z© +Ä`²*ì‘5=‹Œ¨+k…ž Ø¢ž_R•®†yœÃ<| ‚2tvÚÍWÍ<µäÉ ’„í%~ú¦XTaåagLøÔÊà{]ÁdÂþ€Ðœ4f[ݰH[–÷1Ë‹Ìh°¼ ydäÚöÊRþdÅD:k$/š èªÖ£+ bÛá)í)³~‡´£ä>;ÄË£UteDYÕQ]K³²2·]ÚY#Ñ ígaeºò£«LÓÕ6VI`Úë+¿Ø+äõe3‚†ÿ».T Ó~ŒÃ"ÛÚ;)ÊxpAÏ´P²4fòøµ¨ ô±`úds®c÷Iò²å ½ûÒ“§H¾BòÉ’+$ÏX’´}‚ä#$Ÿ"ùÛíÉ#$¯X’Lçd{7Õm¨Šº½èM?Q þ©ÃH]D꜡V‘z©M¤$Û+kð}Í¡¹›¼åæ#yÁδ=dÛž³äŠú&ß½@•®ñ‹;‚;ßDß}ѺiWõîÞ¥xàMà饦oª4uó„Ü:<$Ð繎MoHwÐg°n¶cd”Û6Ž2z¾Üª¬T7ŸíÑyZoz祌£âŽÄwxà´`¥® ít;E׸bÍÿÚ|Þ‘OIÛ· Û ¡›i4ê” *½“5ù½Áò* xHB¼L6òçt´Çü×'¯xºö1Ô¹E#%˜(r½ì`p{¬7ðj¼9™ÖKv]è·/©42ü¥RoL¾'ÒŠ+\Q è½(8oN‡ŠMOØ=Å=ˆ ‹Áâ]cü¨–è±û&“ɬ9 ' NdP¶ˆÆV¶,'B7p£Ö×”Ùšj¯ž-<êtfÑ” 8hDÐ\Ó…¶L<ÿuzft÷~/”/•T‰Ò¬©*rû{Õš‡8}þ̉ösûµ*8í}[ÀÎ9QUô{ØO03°3u§1•Qtžà„” j55HÜI&÷^u:¬ì*E'ÀÇ{¹:l…ûÄšEi ú…K.Y˜#…íÍ «»ü‘ìWc·®À‹Ì5±EéÄ**áwó ½Ì~ÿ4— ¼ü&RÏ"õ}¤^0ß>e8#¿‡Ì·_Gꟑz©¿3£-s~¦=cØt ÓtñìDÀ*ºøÈCØ Ûmä {; .ضd´Ë©3öÓ ¶ÁmÔHÜt"_6;O¬‡Ã‹žCÔÃÏ]ÕáóœúSÕn¥jG¬p"ɪZ¸Aºö”­o×ÝÂXq§¨hgT2ñšþ­mg´£müY‘¡xh·ÏU1Št©¡8àãHuS;6áÃfmmc†Õg®­UZ+{ äõvBÉzÐ v|?.‡û=Óc_¥l ž2!xænkðøÐìz»p?2mÂX# ‘É&ðßü%ͺ}‡ þžµ‰÷U ‰­™Ëî ˜ey¥üVÌEöHaåQ.r¥"!>åD®ŠÉM,’÷&oïd!Ú:¸†¯ÇÃdd+v>;ÿAÃÉîº1ÌÅÅ{ødXS¢ôu¼»’ fàÃHüå•òÄ++½'óÒ¸%¿b¥AFzÉΕ(_É#m¹,³‡éÕ.âxêHîa²ËaqÃÃW—!S>žaÁ%)½‰fÉu9yØW¬Ð6¬¤ê<¼:’ÈÛKö„ôµi›hSß°¢"ްm®ôÞµNNÛÒ`þ²OÒn‹æ§ç»5h}ô ƒù4+os ™Æûò0îŒÝ š5àb·mÇj!1X¡aŸÑ„u˜àC÷6‡zIÛ=`8ßί39Dâ^IÊí½ºí·,H8¼³ƲÅ^ Þ䣙æò}"ºÓ\6œã1 ¶ I.úÖ!·H D’ô꤫¸êÔ€Ž ÐÞª6)ðý2ˆvöò©»Ìõlƒzy¨~o¬—%°BÓ{¬DZ’é RÒ[ª–x?½fçÕz¡ìVW„Îç!õC©W2:¥÷à”ö)Vzw»qÌÁW(qeIæ³ØYÛÂ*'‰K¿Ï|­Z*D´îcËr;™D´þ0sçºp «\DëÞT/…$ßvñB^ïtýÐPLέõ¨ûSBÞ]qÇtí#‹£í§ÃŠÂ½Ñ÷t^›'ôr›ÛÚ×;ë°©mÔánfŸ¸ï ç·¦Bß]Ç>!ÿšsË4ž»îþøã¹I1\߈”lÛÔøŒœMÄÿ."ÜÎK¦®Ù¹LÖ&¡cR®#¼¼ ~-|éjPuÚtûC€±Ê/"Ü&ê_}ô¸IùWlJ0O?*I¢«¿Ñà^ÿ¼`ƒqÞÁ¢LRúÈÇ^“ WÛÈÏ*‰¡÷# ƒ{mt²)ÕÕIÿ®?_Å8Y6öØc÷sÑ,bÖNuâºËäìX'î†uâšJpí]67¶$Ø×Щòz{YvVþ½Œ¾Œ-'M_{«@t”}([.«ÚÖ’TŠŸåÁG„ÜEê²Þ'¥ìX¿Þµ›øfg—jqåmi°X<½·ÂFÚÞÄ¢3ûW¤¾#nw¿7f?ÏQ§@i¥—}Ù‡¶¥&i´¾¯šâI£›xSª+*èL*—ʨ@Œµ‰FáGÆß~1IŠ‚$pH¡#ûˆO•Úê8'Äÿ&‚›I–Xù¹`ÏgR'É¿ö„{™»¨m)¯œy’òbª™w­¿ ñ>¿uDÛroú. Î_‰­X³µG)yÔ\ZvJl0=.ó ÒŽoi\53~ñÜÏd•œeŸe£KÛÚů ©ÁH ¢žà”T„ÉÕpà¿fój)áÆ¥uäi°/N9 Pü7l<#Ë`­Læhþevˈ¼Ö¥²¢róoyngØâ¨å`œK?˺j±w?Æ%²{R NÔhƒ«–„o¿<çûÌfÍB…œZ‚õW² Nv_‡©k¯;&ÖªPìÔŠGùö0´c±Øt̺*0ôfä-ÎWñ-ÎYöã#”ˆ ùü=€)tÄwƒ*êþë¬xü‹ø¶|›Û†Ü˜ðEó|®È‡igBOìΠÔAJ`»`máT¨›D±IÓÉ|ó³ ?ÖT.1¦ü†øîå¤Ã®¾t†}lÖ\À)sdÖ\­=±¡ücþåôÛùtàX•[¾ÿ¶.Å;ßð5.g˜Kåpuîÿx€L:ä¡£O¾£]çî,{„™:_0ÒÅ[¬ãÎÊÔ¥V½‘9%öŽyâE`ñ>oÛ$6°H®Ù’MáM6ÿf­™ã¡9_L8ùë!ÛrýÃзv5µv–ðŠ ±í¤L·ºg°>÷Jß0èêî=‚žà‹z­i휯µŠü©*£,®Î]VvÑéäAªŽöCE ã¼lùšœë¡Œ“ ¥Ø3‹$fïè;“w8£jùGhMÍînRrÉV4!&fFzqÛ±BLÎOkªÎÌ;ø?^EæÉÃr`vÛÉÊŸÄ[lËKñî¬FdFÄls1ß)øŽc%rÎ_¢ïØ•!°k¶ *Å£ªoãO|ŸíÀ_¹Ì)@0Ëéa€_Êh{wH+„[Éo=ZüÿþßÕ|endstream endobj 92 0 obj 3243 endobj 96 0 obj <> stream xœÝ]Ûvݶ}×Wœ·œÓѼ€·öɵ6Yn“ºjûôA7KjlI±$7ýû$ly$ee%Îò¢yH\g6ö ß6yV”›\ýѧ^¼k7wÃíÍ»?MŸ.~:è²Jý7ÜÀëÓ›?É‹bÓg}³9zg}ßåíXj±ijùï¾Þ´Ežµòßo¿ÚЬ¢é¶v"k‹ªÛ›| (E»½…«Ã²ÊD•×ÛÿíŠ6+DQm7»Ã¢ˆ~{fž¼2åÜíÛ,Ï‹¦Ùþ(Ÿì³¼­ ¼»‘/UuÓäÕöxx¿*óvûiwX«Ê~{nï³üÛT ªbü÷µ½¼·PrF+?ö „Ÿ†Rªº«°ìá©{ÚúûÔ©}ªjêâ½qZgZbï^©q®êÛ⎲œF)œÐ}sPVR^šZŠÊÑ™” è×{[¾)t”5ßc›š¢6^V=¾~c+…’àÎ>pB{uF{p“bFʽ2m æw(ࣔi!‡ .°å™-`ƒ—E™‰²+¶G²¬Fõ¸Ü^Ú²Îm &ýš]X#ß×´X^‡A±P/‡za ¦ ;ÃBåe.çZ0h»iàjQb9 Œñ‡Q`äˆTeï Ì ¼ Ç7~§L]—^»ô³eÛJ¸jUE‡BTY)‘K½T—cu JIäÚÝ?¸ÉRjªѼ òñºpC'ž}-ëÉÊ®5>ûàW©p ÞúFÕÏ_ÇÛõ…ª "8:ßíª,oz!°ãÍVöþжµ£*zeµô:`…ëxlˆFì’k“‘ÄÛ”Z4ðƒÖ¨„ÞL¿3PTL| Áà­¼g:š¦oB‘¥ý½¶e6‘à‡Ã öʼÀùhÌ‹ƒ¢ ô_*!’(Tàï|ºÈ¢h^öño@š÷inFQÉ«¬í+-+Ÿ’4©>æT­÷ÊÁœS‹ i®ÍÕç]­”E®çæ É=.£?® :]˜w`”NÌMX   ÿîú¬íª¼Äi€G¯éôÞ§fÚy* ò´]òÌ6 hŠgäEKh.$ã/XæeÃÑ&²BzC(äÌR6ç£,IJ\¶Z”`¬'é¯òb¨ï*IÁeß…%æ½ðˆ¹¬$›xùË]™5]^–²() uŸK\®v‡ò¢iÅ€Ñúö_䳪…Ãm¸Pú•Wm-Û5\•jÐìï÷êºo󼩕6éÂNådõ¢©{¬áÈ”€5Üî”j4R°Ü»éº®jÓ÷´MÒˆZ™?²ïò¥£ÿÈ’åH½=8úÝ÷²¹R«²nõ\«y¹Ô*dÕ.ÀU%C§Ôò€Yµ¥ßRµð…ð©MÈC£ÅShµÌßSeàfÁ¬ÑÂØAÒhQêëä=-Ðé³é·«`¡Â%èï¸ÐËkDokÄ à›S†U¸€àš.¦Ãûš.#•>¡Ý:£þžÊÐÆ_ 3†’„[Ö÷´‹I"2ç’PÍu–7sÉé '¥ÎÒ?BeVtвˆÅý r=“¡ ­ažò=­exÀ‚x/}Q™qm%A,ðL+ê<ˆ­¥D—QCå>-ÑÕrfxY;˜‚>/‹Éx¼˜3§Vö'bšÏÔ€œ£F#E§øY®fëá Æ.cnÏ™cömÒYÎÁ@(µƒ]q4.¨œa­§UagÁ8XŒFÈ3…r[…ï×ݛͦ'›a)ÊåŸá¥©* }]/€}Í1߉S”!Üý{FU•WvO‡ÉÑSØgÛ¾gËœøüÉ t²qþ•ÚŠcøÖí´ æY^ -5|Àà­Yç|ÛEê²»sΔ¹xWª†;€÷`à/4.ßsh }löåÀÝ9UûVyó¾pœy7J·¤nÛÀ0´®Õ 9޲ÜÖ^ÃKf¤ÊõÝk çÃÿð×CQþˆñäÈ.{JBù;§^ÌC`¯) ¼ž•SWüQCµÇWÀG½a×('±Ö”ÀK6@n$øØ=›p@Mî·ä`ËCGR{ñœ´°è ¾OË'g…MöX z®÷2Dº(I,Èäpcd ªøýô¬h&I9Ô¢b±lÜ"xñ®ì̶B‘g†96U^JÌÛ\^fu+«øÙ\åOz5šVM®f»Åªß›G¯ÌÕsun®6æêãxUôi/Ö¬ïþhï˜-—vû«'½ŠvÛvìÚ\‘ÎÞ˜«÷¶ƒ{ ƒqn/Ïì啽| ¯Á‰_t„Þ,¡OæêØ\ÝÅEDõï‚ÈÈ@ÒÜ^;[n…³ëòog_ovÂbÛyL|Æ”£Y‹o)>ò…”?Àóiym¤¹[•¹Qê Ûá|ÿÅYÛ†Zä™h •å^)X«¡1Üx…z¹¥á´V-à]Õ?q¨³B¡¨Œí^%ö cÄÄT….)ó;TÊÈ8 C§t`E´<:f¢qwZÐŒ°´=˜g×(°öàùÁºÙu£Œ÷rMý•F« ¤µÍ¥ÉÍcF(¢á}ü) lü±Hš+pS‹íœèäéKo£çÊÒáæûQ~>Üä&|˜z!¹Ç¤°(1l<vK’NS3“ˆ•}ëØÜ ]±Ð{äЄ…32CÞí0qÇ_þbŽ…ùNîãÚÐ! j! }Ùµææª¬o5à]wí¤Lb‚r‡VTóÀþoª-õÅRK?pNÌø$ÀQ‰ìèϲB¡VIÖ_×3L¼4€{³‰ç8J,‚ 6ÞܼçòmÞ—¬µWº" ( õ¯ŠB¾ò³¡ôÉàéç\ûædc•G6TdMÓÇ^<Ò{)+°19Žþ°Åiéл\‚3M'ƒ€\Æ=]fÀõŒÂ[¶YÐVh!´ePÀ¶êšPÛ¶…R@¹Ž‘í@x8&æ]nmèÑ £‹¡£3¸Éšªµ[Žƒ¶é²*w£‹‰š® ^ ‚À,žXäÑþî>tw§€É: -¯õœÏœíIÄ•€ ó–ê* bâ³Ì½¨|A~c֊°-®¬š»ãr´x+jAôN|ŸÇ߆_°ÏÃXå|WнÍ2‰K’’xøÉ“í`EclÀU=0Àػܘç®ä鸃ÿ¾/,s{Ïùð÷Q÷ô¥%øŸ"TñyHT®¿•wòé¶réN®v8ÄÛº€™q~AEàINUØÐ¹8ŒO[¬:"³ízrÌqæ”u›µ¢Šx{--Aëd.*$Äܶ-¸Sâ’ÁÏZ>¸– z²2¢^,¸Eðµ_ùs=xC³®ð™Þ@¸ˆos.9}!Ñ>Wg“¼ÐOÅñ™¦æð›œß¡ùΑmßÓôœ», ¸£C¼ËúÜœñˆùœžŸ˜O.ÝÊòä ¤ èjpg‰+jO¾:벉áðcòë½Ö‡ká8˜ á_Ú»<æš­øŽB»¤*ëjq Yð°0½e¨~ž}I”l»¦âûqö`…õýNŸñŽA·97_’› ‡6ÖðTxÀvéQ±þÞa2ÎE#¡þŒi]M‡Í$½)K{L-Þ¸*ÀÚ(¦ŠeÞó„ç ù ÐBë–<ã Œ/ÿ2”Td6=%½ïŒïµç­›bFÕÜsg&9û\Cu¡ 2Úm磣TbÑÅÜëÍÀþ°e @Ò(_s61{Þ¯Ô÷aà!5ÝË<êNSæ»È¹ ¥4×Áò7üäžIñI{‰ƒ¡‚=®8\D¸å¯ÜWí´ÀvYIp÷ƒý:tCkÎ@à<^”ÿ£R f€žä­Iõ‰<®ÍÍs–Òã;y³)e'…+ª\×ËÊIb‹‚·öËø!ßÌòÎfüPöÓ[£-ó'‚} €À´ºNÏlÅâïÍüóŒD·©œf9lxzŒ%y µÛm¼ë™_à%šÅŽ˜Ç2 ½ÜöÓöíèå6¾U°€¿ÞoQauYW–d%J.º,pVʤyÁM˜Îh¸µ³ÇDì±Ãó‚ß{ùÐp,¾\1" L™ç7Z‡3±„[{§(³ 9çšýy hµ…d¢&r9mU¡”`F¼°% z·µ‡»=}B‡j:´¯§ƒN€GhO‚ÌÍœç$½¯ >µ­¢µt´å°Äì7‘k<,È$–Õx#ä?q#–Gñ@_ìg%2y<Ì ¦Ú^‚ \‹àlàûˆ•çXw¾ƒä^zè 0UruL€cSP îå1F㼡(p“øú"ñš$Å-¶]0ÒÏVÞèI¬ðŠ š×VÃ<ÒÜà9m~m¸^SOýèà¹pcA$”YÅlb»øc„ù°í ¥&³ÕnðÒ±:Z¡2€9Àd¹Ì ‚x.-KýÔ-õó <ÈvñŸ=Ë÷ù ßÈÃÔ…^) Sfî­ôL‘b¡gJ*THKÎá=–’µÛ9À¨UI‡bÉc½¨ÏþæAŽÁ>Ø’= þ;÷÷B·¸û…nÓS9µUôNQ²£Æ'ÆÉMË›2Íq*Q†ûµu’ýÞ…}óþ¾°Ï¶jnÙ@âqBu/K«#¡‹ö°„ZɬXœ"Ýf¦Q-ƒzs5äEg3¢¡VÑT¨\4¦Ï.˜ò. þ8¦½Nù—\ê¬krAû|߉6øpfƒ~þ1Nß—¨µïè‘+Šü7XrŽw$üƒªï±°2x!âòïÓni,ú%‡ÀÖqÆ_@15—íÄ/j.Aõ~ÃfiOÔ\¤ày­¥qƒ~k ˆóÒÃÆOn- ‘&°–’Y;Ñ\THCæ’:6è›KþºÉ-ow‡MÖˆºÅ-¬¶|Ž6Ѐø$„¬ _`Z­ÙbÂÏÕ>vF‹0¥‘ Ù0ù wœÌi¬Qn`ÏÅ-ì‹P ðQ'žš…áÒ„Z„òZŠÁ#pðͯX}zŸÜìylFߦ˜ hµ~fÂ=–¹x¼$ë‚3 ×ÑÎ:© Œ¬:²8ÖRNz“š8–å’l2Õ®ÙrI®ÓÉÕŒæ†Yñ9$0[¼ˆM9œh/¤ 3ðÊ’­šßá}è)P°T¾õ™i ’ûÙ\ËÊn".LHê‡î†ìûþ–BVÖ=顨¨è)M  Ha‹|ŸÍ3{—„Ñ„t¹¦꤉!%–cÆÝùšÉøá~•40²ýsûÉTOû¬äùCÔâK3ÎP¬-xvO_¦'KRHGÃbÇ8eñÙq=vRëÎû¦‡FêSãa 4°’òæ¹mjþÏh_2ä ?ƒå!;>WØsž™•'bO'Wµ5çj}ÖpuŪé÷Ê`ÙŠå%fÐ uÁH|ž0K.ä6ç}<ëþF=ÏÜ5§kâ{¡¡S<±Ê!ýÊ*O¤²×þ—ƒE”7ugÚH³¹``DÎm¸Ž»qú$XÀ4+àVãïdÍg_Û˜ò4yÁ!$¹*;æ[¹’¤#ž]$©™„h O®×L<Žb2ïOÂÅÕˆû!“\#VñhQWYeýÉ&8XusÓѸü'Z$ég)Úsõß3½KÄ€Î$¿ðÁÍH> stream xœíZÉrÜ6½ÏWð2•¡$ç”HÎVYEN¥*Éa4#”X‹%Y¶ÿ>t“lp5£¤*–-غ_¿^Ä·‹¹˜ý©…õÅâÙ‘ ¶·‹b88úºn¶‹·‹u÷{ic@i¦ÜF·BÄY¦ LŽ71¬âOÑ2‹3™*m.áV-_ÐiÚÔª;Sã~îÐ/£ÄØ’išAi—’å`¦´— :ó‘ø>¡[u…UQ)tÒ0¨;ÿŠDÌŠ<õ¹[‹¶ŠÄn3ú´Ø ”Ñ ›3^’ÏÑhÓ)¥%ŽÜ[ˆ¦>KÈÈXª¤†Lyõ$K3H•7—µ.›pWaV€E¡”9‚²û,¥T1—"°‹¦¢ÜÍ EÄ"WŠÓƒ>“”vÐèÍ“Õ5üÐÙîrHó¥%LàMÏKªTx¬ØæšU4@ }¶”YŽ·¡_!„§ž ZB« + Ó6ÐÕdÚSRiå€Ñî6:¼Ì¡2Í }ŽoU@‘ë8aŠôUù6mÊjk»Ÿ1VøæhQ?,Ñv =0 1Gú4¨ôØ_úf­ÁP”y®Hëv“? Á Èëž\d0FA49­`Âb&Ó&ÝÚpߢa€%sõ¼(‚”–2<€Á»®õt•¼4s+šeˆ|D{“×üDr4™V$¤h±÷¥2{Ð8D•ˆ\Þ`±Î²Ða¶8© 0xê’&t€†©4-À¯áW‡«”ã¶í½*8@KÒ8M$„FtY§#H1éd Í‘M¹ÇÒfšâèýÁÓhüšÜbZ>C¥l$ø"…K„VŸs (rJ^X¥1æ‘–5ìÒÂÀ<8þkQñÈ÷‹ãOwI4K«û´âLç´íÚ¬uñVú€9tÁeŒd"¨¡ ~Œ å ž¤xS¨ö+Xü37#h£öÿ•Ë[&F#¬ÖL1èÝE^ã#š9üIšK÷Ý6›ö]´P9ÎÐË¤Ú "Qͤºx³‘ß¡"ÂR=ã¹7U'òŸIÚK£m I ò¢ónÎ4"þ¸›àL‚ M$¾L«Ž€£2­Âoöᢧ£ëÀ‡”— G‡Õá䤯 NNÖS\E\XÑØQ!° ì€^„A &:0 €xßêg´,éÆXd&ýé_Δ ¿²el&-ôæ+¹LÇä+SÓ'K hÖ!™†øê‡¹ÉZ#·-S›’¾&WBüµ#‚mß °NÛp…ÐnA·èàGgpt99á`´;¥h/ºñ4›‰ì®€*½H¡}ìèZ*¨©CsZGU SBóKÆMŠ€J†ís¨È9ðˆ ¤.j!ªŸ‘„‹,¸…zz/¾€Ž²ÕV@/X‡îsö›{²´L‡ÈQŒ&·Ìƒ`>ØFÅåúêR‡¥ŽWhÆŽX¼•z]’ÖÃiŒ3À!ÌzáZÈ0HW×^ëwIŠ.h8<°m7˜kì´g:K%.Y¬)±¿¿‡¾Æ9òáâ¼[xEŸ{'·zÌv½=èÄVx}Bä{`Âý ˜Å6I“"çÃE:8¡€f¡\ö慠K´ãU%•¦ÝJª•ó5,SöÓTœ‰Úõé ÷Zu”›Ò5¢«‘)éÝAlÙáÕV¨Þ ±:åô…õÞ(5PvÎ[+7 ¶" y_Å SÄ$µëëÔ¤Ý+&!‘îõµz¶i§i³gÛöþ1ª Ҝӌ…º·ðœh£(£´íoa<ÒÍöN3¿Åq[wmØ2iD£ŒZ µðíÞOZ›Ð­¯&R•ž*¿šø"âIœç,³éD+Á’Œšr“9›…!rÎÍ/…«V¢©Ì…Jò,ü/hX˜ûÆÊ™ŸGÈ’ú€í<Åó´±ßI±‰°m”w°ßD¼Ä/Æ&,gÜê)áq¦Jº¨¶½³vÐZäº^Še#•/ˆJc>81olj°ª+» ¶ù9þÓ ñøÄIïœÃn©ÉÈMé"¸VÕÀøÎ2¼U ÅܯbŠ×ø¾p/TÓÕgÅ«^•!רÖóÁÄàŸ·Ñ(Ùƒüa#Ý-C‚A0ãDM‰UÓ<ùÂù%Œ[ôyÔíç—„¨RBùÙ‘á„þܸIÝÜ7ÚJ˜0¾"Ÿ-“8U‰í®×Ò­“îœtê¤ '#¥—N:ìŒR]ºÁ|ÎdNH°Ì¥Äµ¥s'Þƒ¸1ñ%ˆ‡Ä¨¨÷·£œ\æ žó’<=zFœÙ©ù…[ý['+>sÒAÏ{åÕ꣡û,©£ŸÍ½Ï²×¯+'^o‰§oh@RÚ¨ì=®Ýª+']wN*m&Øá˜»QsË[fÌ~8ö·Ä0\'†›mˆ;ÞºX3ºš*ïìCëlïëG+`ò’8{@Øñ¦w.1[x$sŸ‘£#Í ‡„§˜ìoA[¯ŸïA³½ƒ}V„ûìƒsʶ«Žúªè’›<Ö”@{Ò…UÁq¨~ÇÝWZnZœ—û³³CëkâÄóͽí,*¦½ð!¨†ëRÎyMHg^¼ëØ»±2ÅŲ›¿~ßuÒk©¡@Ò78ŠP©4Œ5 dt³MIf¥=S.DòÉqÒm/}ߨ¦ÅãË›i‡¶}Ö°: xÅî*_¨D¥“ñ”š+ˆ÷! TƒªY–­¬³”¿1!§eä´@<q â ˆ¨$ÿÄ/@üÜG“·`pã>æ™S9¶žºtžàîs+æñ9û£eû¯˜w÷öT1“Õþn‹‹'¬œÇ›oDŒŸl¾§+ûr1 „‚ãÿŠ™pÈUÌs¹fü-þýóÄ‘½WÎû£bª–Ü{é<·7’‹ÿ-óø¢sn§`_óŽ[¯;­˜ç¶пøTÎSY$´¥óTÌ_;éÈIߎœ uræ$¨¢*f*°ó`‘K‹¨ÞE%ªšö ˆ‡ä(*r%ˆÿ©"—Œ†äàf±n´ñn[;)vg¹Ÿ%‡«Øœùñ‰ðÅñâgóó!endstream endobj 102 0 obj 2612 endobj 106 0 obj <> stream xœÕXË’š@Ýó½ “*Gª²Éd2•Ô,2†¬¦² èèTD5©ñï*}9iÀGTݯý¸çÜݼ0Ëä6³òo’T»îúl0×VfÖ½Û€Ù@{ÑÓÉ?+ƒŒ“”}ˆ²‰vÀB3ôXô¤Yf–¿^•3¯“ý;ÌçÜt¥Ú£ž\ŽÙñWŸ4hþmíé}adW†írß ü:–íàêCô?4öÖF:dõô%´eëè‹–ÑÉHG÷ZôöQŸŠåcÈFÏd76hÑhnF<›âY¾ÉÝÑu ¶ ð˜˜õÇÐ"3ªJ­yK nhi`k%Ê#±ü3HÛ>à=  ¹‹zZó>B¸IªLú÷WMÈê’RkÃb¬Œ)û t§øÄ Žªø¸zªŒm\‘o-¸kÚ!¦­ ónÍ%§Ô…ÛÔ¨?—#Îmn™¾}âl}¿‚ðíË´âæ‘lÓ“ÔO¢‹Šs ÐrOâ {q‹jÆVÔ‹E”ÉaÊßß•‚üg¤N´bù[n@³©?€óÏqU¢‘óH§eû¬gT´ÅæWË.’ý|SŽ—ä UÕÑ¥F“AX‰òÆx/Ð@_i v²ÀÚ õ „|R' hÐVÖ$‘ÿKR·’¡R%7\ý{ݧî±TËÉýiÏí›gme{YURá³@ßþ›Ýïl zÖ¡’µ:·kX}×UÈ|fw«æ…s¶×¤ö u„«Ï¡êñò¯3JåÏãªÒ8cÐüRîEÝ^ÖUB™='¿\Ä‘[µÝó™gG9ÏW‚|Ýå\¼iæ¶8®—Þ@gµ ÞÙõ'²Qƒ£ÜyW§;Êÿ÷À†’hª»>‰1#\|&(½Èr 8'¸„Vi]ɇ.6NâÝb¸Ø”ànÌàØôa°=À  Û8Ï%dzL/ì0‹E=Md¿—£`l*9…»„Q_?LN ÎCªc¨ލ”) ‹.zµ ¦:Hl{ä¶•éC‘ÚTVìVÊI<‹/MKàÆsõbRtàÛ Ç@XttT¿ÿèm;ÛÈG£¡äÙxAãÐs•Ôhp=H!ù©îOËÛH{Ⱦ¦TêÇendstream endobj 107 0 obj 867 endobj 111 0 obj <> stream xœÝ\[wܶ~ׯؾíöd) AâÑ·ÆiÛ‘Õ´çÄ==ºYrkK²åKš_ß/˜àp¹+i¹¶` sÇ`?ÌòLéYîÿö£÷;»{Õìôj§éží}ß5>žî|Ø©3ãÿ4Ø>z?{¸O•š¹ÌÙÙþ›Ò´<Ó%5ÿÛ⣬‡[gÚæe5=_,ý†tíæGŒ¡£ šV°þN`5¡PZã±ñÀTá:Ôµ£±§Üœñ€ŽNVÛ@(å e³œ¨ ú ×&·sÝ®¦™gзÝEUE£›Ígªnóë® $ ° /+—-©+gç¯Íruî'þsÿ¯kñ˜¥F]3)AüólgÿÏ¿Î_-L–[GôþDø›ÒÚŲ̈Y)]TžÞe–ç´-ú¾¤™•ßÔÛEA ㊆òíÈÓКñÀ¯þ¸t]U æ 3˜Vßk'ÕEÙLQmˆd„H•—%Õü=ãÞªj4ÄwÜ hó€nFpïw‹eE}U©»Y…­àd·À@IŽ9í!_0¼sL)]wP<õ<ZO‡»X€P·˜Ã­Ãü–¯4ñ§ªñÑþ1é™#þ~0…à!7ìˆó†Ç†ŸE¸2yÃÎÅÏoRNõ$zZ2V@žôžX± 3+Õü_"1†ÍéZ/þÀŠgü½ë5¹êh¼ì‰¼ì$ò¸P/—{,9'AHº%j…ô6á3¬[†Îïx$ì–ž§†›_ŽˆMhæL¸¾ ›1ÖÃ+›½-Ó,™L ºŽ¨ªç/¼Á±EY9‡‘¸@<H`”D®à™FÐd~ƒ%=£§‚1Í»q§²vrŒ½¡y˜êž–{;Žç.P£da«LªèÃ3LýáÜ‘`aÅ12†¥~ Ô?KçE'/qUÅó/°+ªéšä)g-‘µnYLŠ 1ù‰šI—UspÚd…¡]vôÖ#æ%ç­”“v3cG™AÐ2½ˆÌÇ$ï÷jÌø±Š¨t›©V¦½‘áý6í2ÒQ%î-5ýå ßeIön$ ŒTdûœÛnÃØÒ¢6¾· ‹³ºŸà¢V_=» n2ÜôœÁ_­ËJpRé×g¦ËýËà1óeß©üª=ÛDrš»kª{‡¥žrógU¨Ê bQ"çƒU…‰ÍAVèŸR³¹ÏÿOÔÞoâ“JYé=ÐfZ\a0¯jb*˜g®{6¡¹æÙÜã#‘ÕñšzÌP¼ ûj´þ¥8ÞÇÒÎãQ€ªâ#ôùn Ôíç!”z¤ÏûïQ(3árß–pÞ>H®Ÿì‚¾ ô†Y¿°¨MÉ_sªÛ'„ìW\Š[Jíö¤”p\ñ$´@?Àn!xÎ=c¨?D†SÞZ:»+Âú /FÍ‚z½1 Ö¯K¶½[å.2ò_ù»èvŸ‰|òŠàÉ»çï€Ä?óaøòŒ‡?îHQGèTRAÍL´ñ|m3ãtÏ?ô_€(|‚äòºÏ0-ÖYa5Gv·æ}ë ‰æ2Œž—'Ùùü¥€÷{öW"{Ñ/ Öœ½Ý1X=§‹° !6îe Oñ©"½†Ž_ú6Në–5ð)e] Adž¤¡bþËĹ….I_üTG™Õ6÷“ˆ@…&vž4Û#׋]#¡ %gÙ.qW×ÉPÔZÕä@l?K°“ ¶rʤ€cÆCòü¿ñw#Á1 0™®¢K%œp Œ3¿ª¯/Sôï[2ù!êíÍ ™¡‰Ajì#˜6ïAáT <Àk “Á1€^¶{<ëUè„ÅžówÆó§N“š*r&6á­ÆP;›Õe0Ôë²YøfÙË÷²‡uéõ£Lõ[› ’rGçדð“ÏÚtv$²®0: ^®¬w1ƒ$â0“í0“6ðö¥à'l`Ýè7ɵ«í”ŠÆ:‚ðÅOI˜ök|Œæ¾7\¹ü®9¸uÚ¤'G‡°“׋ ãBŽ®[ü^ñ+„K0Â:HЬp¹>‹»Æ·(—zýìÓ-&Ž'»à‰™á<%væüÏö®8I,øÎKÉx¥znù<¾˜GfÉfz(n/bUºw¤ò2&0ùV³‰Àiå–Ë‹ ¸²´¿¿^ò ‚ðžˆBÓž«õvÚ/ºúpva‹`ä`€tJçâWb&ëRT22S©.1¿5L™¸” ;¬š^br²½ïá\mØJw'w_“ø¥¨ &g¦¹éÂHv×¶wíòMÀ/_DÚ-'y'‚–+•Ì‹¢×\Ù›:Žîâ:J…Îa¦Ä' Kà›4ßäM”aBßùM€D%0Ï™x.âHï³k ¼yˆäâÀBNˆ^!‘—ñÖä<ý N*qŒ9£&@ŽÀígS*RÖ·ŸFôõ ù0fTƒeif Vä-7æÕ”Û°ž†þÍÛ¡pLËñp#`ì¶¼y8õÎäk ò%ž©Ø XŸŠ È„´$­óªиWr…P+ªäbuš€]¡¿¬° h”uMºWÁ½"×sëÍî+“r´`ýé*‡ó.¤Pp|ĪEÖ~ë”F=(H¤|]8@©<8])˜ëHv_ýšˆåGˆÖO‹¸L¸ð;¿Ã ÇâYÄ^toж{E~¿Êjñ<À£d»üˆAÕ¢Ÿõd¥ˆV¤˜ÂnäÈPà èŠI¾oëFû)_«ÂTä.K×Q%Ž d×Μ-Æ<¾Ã챡R¯©11rèç ¦mPמž ÐIvÌÄ$lÞ­’‡³t±†uåJÐHiw<ÀbÃä5™µuo¥¶ =–ýá6ø~)ù_¼ð,†åXÃcáÞõPæy¥ä3ñéa`{>´”)yʼn:_yÀŠ·ôS>ñaÂn²/ ½ŸE†^Y?¼Ç-$ x’5r]\öi!iÍ‘«ap·6H¬s1þHàÕ2œÍjc{†H?úû8@åÎr¤ål3µ”dï¼Í&n“¸søRßsÄ`ó ÿr§Xœ ~Fcê²k Ü+¼pü¡‡Õz×óUï^ôL± _=嬦|[&3ôËÀŽ '°ñ2lX|°EŸþýŒç-éÙ>nò8nª¬7{ÈzË”áõ* —‚Å•"~'‡õ'¶øCˆÕçâåˆtå¯ux/”ŽÆ K®êkžLœ“,H” Äe¢z55`T¯ÞÉAÝ‚ÿèÎlu¢O¤WŠ?në•⊠®ë™éÑ:çžïnȉ9ZëaÔAî ¥‚¬$+'´dokÌãcS)ÝН£  ?zÑ´¾ùm=ëý!Åj#ž‹”_˜*§¾î[™ú\ûu_\s–×Wŵ­\[¶È鳕›ü†Žx‘´“£ùŠEþñØøp'ßäNpòáÖ ë-¨©†—ÕM1µlA;ÚE»ûdçgúû?³VfÖendstream endobj 112 0 obj 3120 endobj 116 0 obj <> stream xœí\[w7~÷¯˜·Ù“iºÕ÷GƒÙÄeÁvBN6û`bØ Ø`LÈþú•ú¢ú¤þz4¦“³ð"k¤’TUª»úÝ,Ž5‹Íÿ¾qþfïÞQ9»¸ÞkºgGßv÷{ïöª(5ÿšlŸ¿™Ý?Ñ“dVGu1;ùu/ŽêºŠËj2Kb5+“8*õoöþ9_.–yljªç³Å2Iê,*Õ|‘FqQgÙüÅBMR—º©Ò¼Jç/±×6gº™æE§ó3é}¯§•‘ÞA…ÓN¥ù= 6²ù•ô^Jóõ"3Í:›¿•βX³qÝŸÕö|±tF½Öëv™+„ ¯ÍŠ<Óÿuòýžš«ÙÉ㽓¿N†¨7‹¥ÊôáS%tƒ/ñôöÈ‚æÄ°¯èî`šÁ@…ÞÈøßìï§”&í|•BÚõÞÒõ~k S×}z¼Ÿ:½ªŽ’,Só?š½’,é©¶}bYT?µôú‘ªª,·‰YµçôDæ'Ô£…*#¥;®÷ZFušŒÍ’UiWC—”…8 5]¥»ÓÒ’8OpÀ™OmCÎ2çíÕQi Ca3ð{ÛS¹dÙ4wl‹d³=Òc3솂=ÔBöÂÜ ˆ@16Ë컨+=kYë vTI÷ è‘[u½’ S‰º­‘កúÑ‚’]ý¤cN+DĉžTèI…šÿ]÷ê¡*/Ëù±ö@Ò쥌ëdTˆÚÃÂõkÏ­Š8¹ûh#†Ú¡è3ý{¥ÿÐÌ´Ï0øÐv‚ÖzEÕHª·-ŽÖw4VÇØ ¢®(¹.éjî/sñ«ì \¢òꨬ2×4ùØð‰*ª(IJm–¼Ð–˜ÜSn)|ב¡Ê`èʨw°í_];îÀ²ßͲ/¶nNÁÅŠ)~€¾*1Ò1Ÿ+ºƒQÙÞKéŸZ)mq¢è’ÀSTm:l›ÁçˆSÀôZÿÏ&¤@6ƒ€;áY##f)PDÐ¦Ô ½Nc&pg×rwcL~1¡µ®üJ﬊)}'bË Í5D\äfë…æÀOŽÜ‘”›Á…Ž`€ñËùñ=ÒiûJM˜â®ÚÚ!c〺½9vgMÛo¨ÍñT¸w V‚LvèZ¼J›cUYhM¡d•h5æ8ýl(¨,$£¸MaÅÝ)Þ—þ>qUÓǹ%5v¾v!Y­aôTâÓšØÍ-uH¿‘»‰žz =½˜¸n´Êg ´»‰_}2({IS_ÑÆι¶YVˆð³Ä=uÊvKBj<Ž[å%`WúÐú/Y¡ÿÌ{ÿ…Hð.º±Óy?©F.S4DÓ&àÆ¸z +s gÒÄÄŸ1 í,+ªnRZäÅíˆb—ZEŠ cÙa>¥*ÀvŒ¬·«â²«bhIìÀVŸÎ,›„4gx@ áÒG¬/É@£ýc±,¢"ËËÚ ÊÁ#TÙ½“§q¶‰ß‘t—ý›_Á„À¹§ç®6ívÝ:ćDŒ9ªU@•}mV›Ýí¡`YÌŠ’B@ùÔÝM`ø˜ÙSÇ4R¦8;Ø,Ã2Oèr‡%xÀ唢ơ“mžÑ±4+?Eêݨî3+²Ü;Ðß ‰Ý]Ó¨œ¼ AfzBâ$26ˆ.àµga6˜€¦fº$œƒ“¸{Óœ·K(Üm%¿‰ó·:2ŠÔ‘xÕÈm¯VpbÖ>†æ&bWÆyÞ-Õ˜krË€:°WhrQD%ôÂXˆï‰ <§‘ÿ“ô…ÙùöÁšì)˜ô%*Ê”¾¶pƒ9*„(Žh’Ý-ø–N Ý(è°JÿëÝ#üøÌ² àîU†{µ©îü¿“æ Å+,qÎÔ±[ÌÒÿ¾v1K½S]ÊX1S!¾ñ}ŠØš¢°+ŽÑ# ’ášR÷‚F•á;¹@FöÕ"S/;¨nóì²-¸´2êS(V~ÝÐÒ(+ê6€¦7uòo(kû¦ ”çí8ë3@íÑ .b"cCsÃá"×p‘±¹¿2&ê"³Wlà<”Ç‘¦ËCU©R5<”I¿H¥Ë²W¡fÕ.+Z%ÈÊþÉîkÛùŒD|ß Á¢èð'øo'1Ù «[T¼Ð´0;ÛIÜøÜ-{LŸ{â’Ö¾r£˜k« % úö3§:çPà”%Øe:E—Z¿s³ÄlÆa\«ÌýÂë ô‡ñ‰X)BÄû"| ò´KJï1 ²5 vUkv¿K1-·zEOeVxP}H âžÈ"FÚ¶"¹ùå±ÐN†Èp÷ü}ï!¥!WQ]—E|#QjmnÇ™+Qº¾ð5œ”g€fBþVš€•'4Ü„àp€)ÝKc´TŒ^¾f-úop CÜÁ19I ¨° ‹q#™Kî®ÃË q3‚U~—xuÂÀ ó&eå0ØÐCxüÍÊg?o³Ècø¡Á½vVÒ2u, t‡€w·nÆßØœ2-Êõ% –WôÁbœq¯! /šå]ÞÛpä†~ƒ}ê[™|·x4ÓHreìÀ¢OЯ÷|fÙÏÚ"Y Qì\dB¤ÙÑñ$è ,‹'P5,;(ÄhbŽíZeÝDUD$ç&4¥Ùèùb™åQUÄ;/’\#W9ÇÚgêá00 ~ ëƒåâȺŽÜòtÿ¹Ra/‡;GŽ£Ö U¤¯PÏj]Ì®J¾˜Ý Èï¤åênƒX0à…±T Ü9/SÁ•U0ýÅ…³S=åç½ò*1à+ÝÌôm1’Ð/–= qÁ)~ƒÇóÔ…¡#-±^;´½>êë¢J]3¸ë 3ÑápçãL¢ïJYe[æž }ÊdüîÄQ™ tzÙ ­ì)Y ä%e°`FîõÂ0o•ÖA¦à¶Hm?a3žf²ÈZÿ}ÃVå’“ï¢. œ£O]á õŒÁrÎÏÁɪ¼füEÞ¦ò„\ШñqÀpåip'1ÄÐF`œ Le¬yYŠÃ7=šË ê?9½¶ NîÏ–ýEßš ˆwù VðƒÚΖ_žå#‘0€+¸æ»±‚Fj¢ZkR9B jGœúÍÉIºÖmþJÊÿÀÕy&‚“W‡ÃX¬<Ì$“»ËÁöCµ$ýÁ{Ó8V#a*O¬n]"±“ú¸§ù#û=W¤³üøÓ%Zz=-xYÒJgL0¶æGZS5×gæ|*ÛÉs> |s^Î\±-¾Lºç€ÒqH² 4sël4ÛÍ#4»á :6øœFEy¹7Ïfmú)»¯`íÙ©u$Ιª ±0P Ó†©ÖOm6Ùqoo|]"JF~ç0yÿ•†ç‹ešjATÕ¨Ì:õ]ŒÌg>A[*HyxG¨0ii#'ý2Є"^†±#ÿí¡Î0›h€µË3Õñ}c8e{=J)¹AÏ^cC¢ Œø>*Zeö…õ7‰As °ËzúÛòNd Îw×N;l=MÏËOx¬âÍ|K<¶Ë+<áËOé™Ý¦— šb¸cs³¡º¶pFqy½êê#Éwè.puÆA'ÜSPþ€•¥ò`·5Âð½£W›°k´+Òj /~§²{½Ã±|t¥C‘Â7JðZ¯mÊ¿ym/8mqB/“ÄŠ>û;f ·¹äçàÍ8p›øÊ—-w=}wæˆzë°ø–Pz'‘±òû¥[+c÷-¬Í ºnðv¥ ·§âïù㤕I2GûóB‚ Žw>^aQtE!xO[‡©þ% þéþ¢ ªD¶H™Á+ ¯†Ò³aÆ¥©W—0ö‘§±š\¶!ÿ,P‹?)á\®b¸š–òOöžéÿÿž2è­endstream endobj 117 0 obj 2951 endobj 121 0 obj <> stream xœå\Ùr·}Ÿ¯˜Ç™”¦ÙûR©<ÈŽlËfl…¤’”픋ˈRB‰”hÑr~,ßä¿0Ð pœntÏBJ‰T¥jõt¸çîývQ<åßîâüõìਘ_ÞÎêÛó£/Û‹w—³·³2HäŸú^Ÿ¿žv"^Œ¢yTùüäÅ, ªª ‹fÔh…ñ¼ˆÂ ?¾žý°X-WY†Q\-æËUUiPÄ‹ÇË$ó*MKñl”Äa!.ÅïA’•ÉbwÕå\\&Yž‡Éâv¹*ïõ 7ú)¸¼Ö—ï4?ëa`Äõ`E”V=¯Á³—‚ü8+ŠÅS}ûÛå*â²L³Å—Ë,Hã,/_®Ò,(ó0#D±¸[F¸¾7úòN,JL'ÕâÕRìS§×ô­×bÖ4‹¬oØv½I þyòõl•ä’ül.WœÅó“‹™øåä_³ö¿‡³“?ü°8jVSݸ’š;±.ñj5Ülö+V?z3 uó‘~öðXA`ݲ±BÖ=¢L 5Ç£fü\­ •Ê5í |Ö|þ^¼–‹Gò¸%¨~6¢¯ý¸ÐäÚ8ƒ;®&ÀæDÏöÒ‡rN:@´X–Æ‹«ú*©ÒZ¬Z`S{CÒø´õ–aõʡڎoØ£k=Á/Í ‘ëÇ%•Yxâi”ЉÓ\ü– VÃæ™"Ap:J弩º™"ª8ÏÑ…m4jh£|ÕѪewo8?¥l…^)àÀî¾’J²"‹{ «‘gpJíÙšBdCS`L .Mh»x–« 0Îñ÷ñì*m8Ií¹'–€Ð T_è­+éû7z.Ø¥s¹7ÿ?UrWýTÔÏ¿¤l²&hõ …sw„M.$¢M×#äŸ3¼S1Ã#9o\Þ+ÃkØj”ôÒÏ×]ãvg’ê,SLÓ^Õ½ `sÅÂ\…¡xàƒæ:çï•òr´¿sæÃÜÕo­ÍÍ¿géG·– £Þ9l–ý˜bo]ºjžÄ1Ψwßžñn0lÏæºòuÅk:/×/éB["çÕW\—œ6F?‚GÑ?ÇÔ¬¢øB~Š ~«n¢ýYlE&¶~GS©FäZñÖÒï;ÕGÝpµé‡¹}4·‘¯EÜ0o½ŽÕÆ× ³4ÈÃwÚÌ€FP>m½ÊUí{ûwúYî(ù;·Ä^e½ý;sn‹”kýàJú•öEA%ãPÝÌ£d‡žÇËUµøJ{ß-ŸÒJDÇF„¯žo#eSËÁ"o™ÆtfðüûÍØç:¨ãNü%Õ¾\›Pçô_®ï¦ú|PÀDÜÒƒ±xÓëÀe? Lœæe;V’g¹>a!÷¤Ù7e›¦¥ž„Ä¥U•I¬eEP%Âù¥Ó^´¦Mà±Rñ, ú˲ Š2M+j§š·üvê ø OåNjŒHÀ¯LÎô¾:.ÇãÏŽ¨kÍÖ…h•¾ùãô Pç2 —ÜzqãÄUH¨8Í»+æx\Ðí\W#ûäª}su`XßU%7F(iH·õ0×J Ž šyRìRË7^T!öÚp¸_û˜ÉÍɲ˜]rèÀ×”)>q’_$Ò² zƒÛl°a@.wÞ›`h_ûc€ðÄ[%$M ïÍrr© GL¡T‡ƒ¨¦q:˜ ©­É•TÖT’R´ÎN×oéßaTxTØBX‹ªhõ»0"B±£~Ÿœ ø™riÏ`b¢2“´ü21L Ã6°¿ ~Þ—D07”¦Âgž´à)>JóT°ƒ&˜9Ü•2B 38i×£ÞY£zí—7:á0@­L]6–? eÙ‡ÐiD9‹;×jvC;€nTÞØrÀÜqÅÚ'žÊ#'zH<·qlEÐ[ךkÕáÖäddö½Fˆã•”v°½*#_´lÈJ}3OŠ÷”!´ðûÆUÇ$OþM²G9-P¸öRZb²Ä/?Ã~‰5þb¡Á3å ØŒÁÊQ—áql†Ïv.?<\9*BvöŒzÕ„¶²ýÈ5ˆ÷0+XÑçmƒBiã¡Ûv.ɵàu§$!GíËzá ÷ç–oÀñc9šÜܪŠûjÔ,èQù¬¬yÙOÔ¼àêCF¶{q&yñ–û/·Ö:xàye^bN›c÷÷G°ëc¼Ð…Öo´Á2š”¿ä— ³02Pï#ÏèŠ?wMDD5ÛÎÝbµs:'bÄ%ê÷?‰ñ²¼Ê¬N,6TŸ.Úwè5~¿ŸÈÏcùÏ¥€îÃz<Œ)(-‡š=VŸ€?×0¹¡káÞ唚ӷza î,l϶Áôj^^0-w§6¡JHSïNבA{ë¿PÕ©ŽWª²ø|ß.ßb=SÝ~p0bÇ€ÃnIÛ58XÒe 8ÜÒâ$p8};Ÿ0:xÁçPßõ&æ¼’Ë“c˜Qk*xç›e”‘LCý˜„°®CœœgÑ!l0ä% Û5jx54d¢ú: clyŸž7oØ“ô­L3×w5cÍ#EÛ4¸çÆû¼%¶óÆú[ë÷Ix8RºMø{îNþJ«€„J{®;Яeø¬z ï…ÀŽ;_o"ójÓO¥ÌH½1)Ùð ΔN­C:›Ñ|B²ÞfÖe^RrÓ8ïéÿc¾?ø"+Û½ Ÿæ4BTQ a$yT·#õõ•±%tyºúœe5ûg¬7Ž+º ŽkQ'«-÷S÷¢«S͞܃^9£†ïĘT0:m{ȼ ˜ÇqØâñêÞÐI ¸6\²;ðšáF§!¼ž×‘¶ÃL²gŸ¹áúÃ8>< wiÃXµ+Niõ¶Åp ×(£vâ×S§ƒ i=pŒíCfIì>"Þ:Ç+Ô<´SƉpQ«bñ«lé.¹­­[iZ0Q€ÓÕ5xVG‚MêÔ­ö³ÄÛößÿ.#÷´Êôú˜n×yÑ®¶éÑ.Þ" ô–ç¼}D»L½ª©à¬(7xÛë4§f Àø0±<Š=™qµ€º·];n*¸Î²û¬îäž±vÁn·¾àç‹ðw6l·9Ï’MdéV›÷_—qÄQ’¡™á­~Z]kôŸ7Ú&‚™­çå› ™dغ”JŽ[–ÎÒ ÓÄîà“K–»8’‘;xOytÅÏ€=ån䆧yCHï>Z¶-4[PMåÔÀ]èhõ’1Z‹H_)¤ŽÖ¶@4yÑö„KóÖ^ûsvµÍ‹/ØØãè6öÐBšÜ‡ ææ;öÝ ‰°«/_µÓ4Õ©ú™Ûé¯[4Ï[Y¹uf?l–jƒÖ—ª'b®è„–øŒÂRšÂ§t#y/Mƒlv|éuIÕ©óújOl˜ÑŸC˜X{Dß#s¼Í?ÓnÔⱮ܃/ùÝr• S“ØÖtÌ‚†˜:T0êߤK n(Gž9Ñ]ÑoZ$Ó?lÍLøBóá ;Èå;`xp‡Z+ÁjyÕo½·ØÆŸðcÑÄÀ¹¾=~þÀSsãáΚ Õj~†ñ.å$5ñÆ–‚½ÚE놥L5Á–¥Ls[­Öj„¶+Ò$÷±§vK|²¶{ðgnÇcßik7öˆ`H(3æðü6>g é!úÔÀØ#ûjÿô6“>ØPUg“hdz#Žã‚-˜ñ…~Ö{j~C[~åÑÚœrþÝ$Ø£Dª¨1¼0r€€»Í}à#ɺ€«¢½Bœ3EçOE3ƒdAÉ_ølQ)^,F|ð±¯tгa¾ôJ—¾Êá+\eC5š#תhIˆî§RÉ4íŠ#ÅωòÃHc[ÜY«Â”âY*pà“n›iØïA dè0A6ÞÚ›'G™mãþWãÏt/üÌàQ0˜Šgâ&Á[ÅÑYV~+6 gzù²© ðª¸T^æ°ïÍt¸_‘ðåþ{³9ÞonÚcñš›–4ÑšÁìI ͳõ)H~–c„X°Ø‚¢Åbú—=Ú»Ù®°Ì ~µ†4Ì}ï¹–QHÆ8‚çwë·Ð£2Å- Bú™o¸ñÙË鼮΀XÈ×´Âe(0üY±Á £iïZ݄Ɣ¶h»þ¥•úØècºÎÇ:÷ëm_çÕ;°\ÌÝå"j' ê`¿ì9å³!4i7_ƒ =9™ýUüý¬•<Æendstream endobj 122 0 obj 3241 endobj 128 0 obj <> stream xœí\ÛrÉ}Ÿ¯˜7Ï8<­¾TßÄ.v°€…Ä®ƒðÄ„ Hh°ûcû{®¾Užª>=ÕsSá…—VO]3³òr2»>MÃ Š§aõ¿{xýart’O/W“úõôä§öáöròiRIõ¯~ϯ?LœêŽQ4-ƒ2›ž¾„AYaÞŒM³Tÿ]¦Ó< ƒ\7ø0™Mç§ÿ™DQÆÓÓ'“Ó¿¾šÌqyÍ.æº]«|öežaV‘î°ÐÍóH•³Øüè§$Ͳ0™eæåߤåT~ÿY?ê‘â4ÏgK=U¨$Lg¿Í£<ˆT¬{™¦0UXšÄa>‹š ²°ZË¿Oÿ1I²jÀtºh·ñfòj¶˜/ô‚Ã(.›QJäñì‡yR5UjöFÆ{SÏ’¤EÒî·}kaÌO**Õ£.J=WƳæ÷'2ªt:’½ëß•þ£,g—·/d(YëyùDV%£ÂúšU«¬h·Æ™fÝ"¯º'åìÝ\µ¼y-/aÓ«æm”ª–´z\ÕÉÅHú ðXHú¬’UjüEÏše(DÇÒTèðÒ<E²–Ýœ‘lSä¿û4 ßt7ÍÑÊïÐô³ +M¯e1Ð D&¸¦«•±Þ äe%3í** k‘dirÅd€™»IÒÜ ùd—î*«ÉV"ÄŸeÁÐëYifXÒð¸”M´jI+¨U-)qR c©µ©V3ò\tïîAÿ<7À xn¤YϪ—««hµdÝ?¤Œ¨ˆÌḓöïÍï\ú®û¤¬õvEªž¥ZßWÜOÒ\‹,£þé¿rÀ8ßï|»YwñN¦=§‚dQŠ#˜å£ì¹Y§¶Õ9¬kºýÊJtŠMÛäFò°ŒŒçSi ç³5é¶fáÈÙ*”y©ËÅÀPõÙQ*Èj_¤>"ãYŽö·Tªªœ»ÞÅ¢[öwu¼‡èlŸ‹Ó04–¬py»v %oImÌ•RÙ>Y}äˆ9œTÐÐ72Ì…Ë»º-[±Š{0?ÎZÂTeSYAìnæp¾˜Y”’·\…ªE¡­†U.:û–Þ#>(KbVlš4<~vÅZÍʵ‡«^Eç¨îz®é\Ì»-çÌ$pÓ),Á©•mZ¹ë¨´ï¾+M2z"§{·ôŽJÓÇ& ŒË\JÞŸ‹˜ö‚¢¸R›ñ€Ãƒµþ‡5ÔÒG-.ÎàȾnð„œ Ö.1 ÷ÒÔhLH“8áÀžÖçd~®d5KÚ–K]4hzIgàZÇj«SÃÎñV~’¥XôÔ'~ÅfËÔšn–©5Ý,Sköû¾õÊôÉÊÎ+»¦÷ s9Xtã~Wʆ{’ÀSÝ6Óï³€ Þëh —ñ»]¯ç~| #ðCz‰¢Íµ•oX¦n…ôu¸‘‡iŠR.g–IŸZãW£‡Ú-~Rc ³@²7ˆd¹Þä(Ñ£Àß;ß`œ«ëtfåL…ê€R>›/2í¢¤yÙÐÎUŽ6"Ú1äLn-²^¢Ë&¿C|"êö÷ÅF>j£äe (|I©v1g§ÍÒ¸‰±o¨X° Sh‰[–qƒÖ|°b+³Nk lO  †üp&@ZÇà>>1tã$ãgü¶5n¥–ƒ¨3n`µ¿ÊÞƤ*^KF7î‚xÝJοž.¨0;MuH¯”^œÌpþ_SÑ\7.ÉjUÛø†¶~qŠ‚o£Ž]Bßêu'‘vîõº›ÇeGIZ½ì"P1+\la.ŽÖ-é´ï¡‚n<nuÖhy¡=“OT+sUlel˜l¢4Â9%±m6þ*ùý Á:ÐÓÝi!2Äʃ­G2¿΂"É:=ÜÀBy}Àwî!™¥öûb—g¹è f܃ꠇ‚tÒ˜’ß°ð=hªöë\«Ï"Ñq8Ɉ†1šƒ²ã”ÆÂÞ@š"å­ø tÁÏ×k<¶·¥¬Ü™ 48Z=6ðEvm·^[Œì0óž5—Å:5Â×ø¡I‘aš¸Þ8VuF˜åZD6‰6O˜"îD»]õAE[ ݪå~¯¨,žI9…ÜÏ8šXçwz4w¶z8ÀÀ—f8hÉþÍÒV£¢þ¸ÉÍ.êäŠs.Bér µ,~·´ AFDKÂ|¦ÅAÅ©Þ÷ æ€6ùv äÇÒEý˜=ö†Üó¸£çá-Õ¸‚‘\˜CÒ쿪ònÇò!Ÿ[x&rCùÃ…z-/+x´æĤM›Ì5¼ çWtj Èü5mëÄü­Ì9 ¿QŠ—G´maY]n#±½¸OÙí×Û5ù¥=¦-—:ç»)#dñ;‘œJoaèjãÃW2g-.×\;H4,½¡:Å‹<žÛ©{×5…ƒèÅ£¸ªB´À ïš5é7òpÄ冮û]7Ò8Û®qº‡œüªÉ¬yŒ<„•ƒ.òl⎎¸¥‘zÜÏÀ(›,Pæ@ãÙýÎFàBÍsÇ 5¯v–NðkNÛ8tÜÕ!ÿšGieƒ ]Öü~¿*,ÏðÔ„+b€ò¥;ù/UƒH©Põ†â ¨ €ä¢ºh$ ùÍ“Ç%†Àqn»–µs0 -?o4îèU4í-­ß—³)bFèt"¢Õ/¢×ñêéÝE7iȵ /ýLY?¢Dx<±¢ž™åƒÌ[Ç ÂN%fÙVÿDA”©NÛ¼7-ÍÝH±E°7Žœ†e–*`»¿è¦ýVböQ,¬1OrñƒE«}Ï€8l1ÑwQGÉ J·«såµ§V4Øe õ9y.@S(´¶êlø†ã\›UŠ1?èOæ"åúÅjð“GAÃ’Àài]ÎÇÎWS]í*{u2÷$ÔyÝ"·†¾µ1mß™Âý07Yt>/¯úauaÍ`‹&vú.ÊÂ/}–rØê|p+àõ®" T,, Îô‚‰œ#ѯ²aJö§mjÅ¿Œ‚nW Üg~ÊÁÙ’œw¼y÷ ¡-k©zg´®ÑÙà+: âñĪ^W•‡äC_4yT /]÷ñ@‰3³är8´äç’[p#1íazñˆ“ % Õ–ù'‘xŒ DîŸÚ^iSca˜.ááŸÄÁ°w”>t†žÖ?HººBÂOâÆsäøGf$Ò…±¼ÐýdE^RýNÓžþ/Š;u–¾bŒ@Û6Mã‰xì)øðBuN…–WzŸÔ¾êt _ú¢1ú£Û$l2 Å€L=¸1#X9LÉWzN?|1£BøØ°¢Jý­ –YbÏÑ3ý€¹¿gü@ÙhnšË)$ë¢û¶®àóH£ÊTzÈmQ„‰*:I–ð“Î?"j3·›Ç¡)ز˜—››øh™¤ Ä}b.:âTÔŒâͩɱU7Íí%\VÑ LµõÚ ø µ@¦5è¥[Þì–ì™vèºÒ 'xAåå’Z˜–Vྚ¡-Î<#Y9Sb‡~0Wÿü(eÊY¦Öº‘¢UFôþèÎïš-XÁx xóm u© ¢P%‡µQCÀ¹}ÁPÉA­Ä~.€Yo™ú`Ú¡;éËž!Œø¡ßÕúlú W‡  nÄZÒÒåML¯Ô¬»„Â…2îŒ`½x¥öÒ¦„ñ$œ¸\)«ªaŽV Œsƒ 0IUë]–-Å1…˜_2C±IŠß&õ’UÜA/pd„k®‹áUqܾ Þ²#5¢5+ÊÄOémÎ'­¿†´<èÍqÛt±”:€Ö„ýÛõXÝyæA-†_x×:Ùžžƒ@=ëµgÁ±'˜DkB9¬ D\“ϳ\C“a¹µ´îøèà@"¿ -WÀP¹+ æL®±N–ï`\NÁOb ø–×Á"¡í£×ŠìnFYéØÖà‘d‚ûhȽxšÞºðq•g_oÎÎ)ç×fs”›‘·È³_ÇïW±ªëq¥ECXzãG ^;™JnÞГÀG¬yÄ=&®ÉÄ ãçÉ8gëî'¤²|8/‹^¥¸2!¶sõ÷Ž.ÇÃzª¥> stream xœí\Û’ÛÆ}çW°òD¦Lƒ;ã•Ûe+›e»*NR»”D)Ù íåJñßg@ÓgbIn©ìHz€€Á\zÎtŸ¾€?OÃ@EÓ°úÛ^¬n&Ï.òéú~²»=½øKsñËzòó¤âêÏî^¯n¦_.õ‹JMˠ̦Ëw“0(Ë"Ìë^ÕT…Ñ4Waë‡7“¿ÏóE„¡ŠÊÙt¾PªL‚<šýiaV&ÉìÍ\·UqæúR?â´ˆgoñ®¹œêË8Ͳ0ž=HÛ[ip?_ä³ó¤ú™ÌÖ´Ñ[:"ô-ÜÊó­<‡ÖtÊ¿àšÍk°K¹ÜîzÈURâ]x ü:Wy ’0mè¸Õòõ¸Q\Ú˜Ë;º^x­^z”ä³›ù"J‚0OÕì_t3`ÜwÍÓÈžÂ?–ßLôƒ4š.¿,ÿx20¼ÔmýŸ²Ô+4 >V`à2ñ.“ÿ­#zÈ(ˆŠ"IQ¨çzyy˜¦ö¸f„„ÎæC%³8͵ønéf´¤íж•ͼ£+냋™î]Ÿ´«¥geÑYzVîÀÍv&oWû‡¥š½®oæú²FË Aò•€äÜôIJÞ ÝË( •à9†óŸç‹$È’$+@“ü·£wŽ °Jé€ðÖBú¾–¾a_árC! ½§z¥¼–»ïi¿—øhC¦«È·s†ùg¨d"Vé´[˜¢èlÏv0‰â,(Ò\›Äåm ¿h›.Uù¦×j€¬ït/Q%‚hö\T×÷æ À²¡/ œê+¹  òNaK‡¸ïG,µÃ}ûc©ýf¯û64Ñ¢$;¢7DK%…ÑO–ÿ¶mÙEóZ®šåT@jÀQ(œgd2‹ÄÜüBZÂ,¿©—¡²¬Y†à´~ $fÞ/eÁ¥«¸º‹êƒ™¨Çk³—öaÒK6;Âwð"½\þa«¦gVÎwoYŒ`Ë&ç%h?j4TÈ £ÆT­¾R**PZ×FuˆùU¿ŸÇ¾¡K×25uK_3 j¨_HM[>‡fé­˜Â⳺Ç”ãõØÁ¶ü¹Øò×¢Í@ǃb3o­œÝHû–íÆy/ 4—ÖÑcÄ ¦/ÆBäÍ„ØwŠFPókÆö.{$i4µ\Sà ßQ;Þß(ÚôXP˜™¼2|%˜økå2%¥VÒ?èQó ŒUaÛŰz)Jó\·]dÚ¼¦y‰6òki öî¾0ã^È[¯D—Ô£iÙ’Áal]áЬϿ‚iYд|åû½h?'k:Õ·P2âÊQà@ o®)á5Àâk;g}Ìë~Ã(ëaýä}˜‰Ê!|˜{@ìÕ,ЖëÄÖš½ ²(j5;§îÜ"‚leeµ3•&¸ñS*Ðú2ÖÿÄJÔ³9)ÛÍDâàr_ѶKZ[h¤X„ÒKwáP¸¤),\tÖ™ÒóVw¤^¯ø]1¯r탔Ï-ÚÇW¼¡˜bc‰»í–K{?Aï4oMò27Ü¡HŒW– t! ¨4`‚—>û B¦öÎ8ÐG®8CÞÖg<Ñ0Èͧj ÈF3@ŠÚtpÒcÉÃ<² ·b^f ÚƒE·=PôŽæÕW¸%ûp½C /Ú !‚Ç"ÔLë¢Û <¯ùOž‰²cÕ$ør`­QëG-èšÇ•]0 Ú=?)¦.ëþö­´}&"æm᮫9,0÷Å=!W‹y³`G„ž[¬•h3üë<*ƒHEèEŒaÚÔUdq´=tõ7vJUP™¸wN…æïiÁH$*s„`LÜ ÛrªN„ ¶áõÐEÞÀLá2øý3»H_š )…Ž×"ñ™ºZ`ç\Yn]KsÂ? Ë ³£[ŽpéßÐ •²kèq–f¨\?I€RŽËÖžlÓbëÝ< zÝiyŽ ¶¾©ªQ[JíÕRgèt·¾ IFಒ&[yrH‚mYêײÊqŠüSâò©<äyLëó^Íkô(¸x˜( ²¾[/æ=´ ˆ.…w½§H¹™üA·/£sÞºL¬¿­30ÛÏ^Wñœî±ÛìÈŒÚmVY±¡ ‚5>PÀ}h#;œÍZ ên8ˆŸùŽ>ÏGn:CU'µ[wâAÈkÙË—4‰û#qˆNÍé±éwZe%¶²iA(¡^L…ûU"rÈm?­IoC^Wæ1·ÍßáŒlÀ ›,ƒn–õxƒ~°û}/{óIÀ {ãUìRÅ=Ò³¨7y±9ŽE4“×Y*ÞÞ¥$¥m¨õ•$ )ÜT¯««¼i‘}ÊÚqûBdm~!-‡ó úùé§,ŽD ¹.œ·kØ^Æ÷ÇÔ¤y‹÷ýV¬¿te:EnXöp½¦‰Ÿ*æ î!&Æ€xdÖÚž[eý)Ì6w˜A(5 u_O€C·J§QBÜ•ô( ÓU¿IãY¢~戯’¨ª ›7~yÄŒ×@ã ™†[ß ƒ"41ª¡‚¾|ûÚo0\ÅÕ(XXŸ¬…>s÷ÿ~Ëx¿øQ“ŽStf¸ÐYñ³uúNÁÆÕF5ép£FÊŸÇúÍ:Áš¯àʉM±/ÄÎ6™Æè¹¿øÓLÖ… £_Å]y_Ü„C|ežCÒ¬ÓG­^£ )LÕúPH²®qéÖøVÉr_ÚŸÌèŒv ‚kºª]bfÚ¤:üˆ:ˆÎY¬ˆÑO<ÉóÀáòÓ¼:_eYÄPQiš´C¾|áÛòR¢ñOo1FîjÿÓc¸\Ží–ûÙ ÷KWήœÊm­À² ÛâÍ‹,¡xÕ§î¼w?úÊsX¾jÓq5"þìÂk;wÞ½M1d埠|ÄKëœ O½Ï!¾ÅxVÅ\òGû‹Ãùwäþ$ßß0çŸ÷@ãnè9æžÊ µmÀ¸ÿߺ¶xg4êi¦IK*‡•3u^}å- ÷×öÅ=Rá'*ZéWXÒ‘[aþa…ë~ÊËp °Ë-hç,ü½ØRçóÒh1:GÂìÑå]1êãËÛÿ“CÊ~X›8Þ|ç ·:ä©ÔÉIŽpZÿçBLåsÚbå ™6ãƒñn2q¨0ߩԅVPš½/SÎ mŸC²Òþ<ƒzƒ  ®~è 9ÒïHéOÚõBm[Pߟ£ÇYãaÕÏ-,Úyr£ØJ|J*º­Ò7–XSÌòÉÂGÿc’üÞ/Í@™S½ 3§\rƒð¹Äœ q ÅÓÜù£±¯ ‰9¡nøšŽöÊxŠO¼Qc c¸+D¢áƒß‹ïŸwÜû{qÛ}{äã þmýB”¦1¦®ó;OHM ­œÏŽø7š\([jkx€Œ¯‰ûó\5ևϪfà.¬ÏHZßÒ‘¦ÕÍ$*¬@ô@ž¹¿vthd <!d$™Cc‰ž×ú`d.AÙiy7FÕ÷m¤ë¡”=J±CÞ_,'Óÿ)d8endstream endobj 134 0 obj 2901 endobj 138 0 obj <> stream xœå\[sÛÆ~ç¯à[ÉN 㲸=ôÁ‘ÕLRGql¥í´ét(Y–ãÊ’ÚJâ_ß@ìùvñ-—IÙžZ~€€½ž=ç;×Õ»i%é4n~ú‡ó·“GÏËéåjÒ¾ž>ÿzýðËåäÝ¤Š²æ_ûŸÏßN¿:Õ“dZGu1=}5‰£º®â²5™&q:-“8*õÇ·“ÍóEÅq’Ö³é|‘$µŠÊtöxžEqQ+5{9×m“,Ký¨¿GY^e³ |k§ú1Ë‹"Îf·òöÝ­Œô *ìv>_”³Ÿçªù½V³UóëêgýI?—y:»–ï¥Á…¬ëR\X+=šn°%w‰ËiÚ–‰ªuƒî1S³ßçzc‰Šs½]FšfSzÞ43ófq¢ÿ}úíD7ÏÓééÓÉé}‡ð—ùBE…RE„úÍ!'ÿ™¬çƒ4¸¤’±¯éX+û¤ÞΩÒÇ™•³ÿH{8ïy¼’£»2@¯ÿêUÔ k(½bgKiahõ¹’È|fÄå‰~©ô¨µ¼LpíUh-tª¥ÅËi­™N¥=û¥8Pü†öb Ñ#öTŸŽ;[– B48Õ#ú}‘ξÓo5ÙÓ¼,g‰4Nh?`æ%]É5’ˬº ª½`¤ªC4[Ú›8ž ð¸ýËA†çšzQZU*Ÿýc¾¨›ÍÇ©¦´™DýFÚ»ÍZN€ßÊ—Ò÷ö‹C   @Âm»]®ð±œÂ9ÓKýS„ÎE&á Ÿ­ûfE^xŘ锲ë}K8¾Ç‰5Msõ¹êAE×Ò…0˪ûdÖ Šn€ Ÿ=ß‹N’µÀ²J:X[\u^Ð^·CLkÐp>óÛÏ·‹¬hö”OAñr¢?œ¾iOh­=Ê2éOPOq7Ï›U‚ëJÍçH¦M×çQ÷ö¢ò‘è‰;‘8 V3_-­ —·pCš~¹wÊ,ã1r €yÿøÈÒÄ=›^h }¯TzØ2›ýÍ48u±Aµ”¤M³Ô˜Ky‚vM7² IåÎÞæi ‹ÖOã÷ÀÐù ñ¦—(è'Ëu¼ÚãÁá·]hné#H,ßí{ü†RÑÒEf¡wbÖR[—+¾ëì;»j¹#ͪ(WÓ¸“(îÛ»íƒ F[¬Ç:œˆš½šB[¾útõN¸À38?ghû<~E€[W ^âÙε¬ìM'oµ‹²k>X“"ÛBMï|.+ ,ü*€)‚ ÇÒÂÝë°õf+Øö¥|d.bÜÐN[>š¥ý>5yŸ§u”&H[ž\/çÕY¯£úÁŒ–œî§ Œ0ÆZ“£ŒóÜ ³ˆýÓ©‘JÝÊéwP8—*6[|- ¸œ·Š¾Œë¤u¾;™iýn­°³›Ÿtr m+ÓSÍ"âðTÞ‰™ṽF'*™áØÜ!<2n/âKÆ!®À³ 1ÇnªBc‚ªë|öwý²Œê,©83yL]ã÷€ˆýì`œ3SÄò6q¾šípDÎÌgîˆ@x†+SÞ­–=ntGÔA½‘g̱|ÖÞ¤ F]õ´i©´.%†Í pˆÏ ³žw̯1N¥ú|À?¹öÔZ›F_ú¬Öµ1ó±coˆúX溜 ›ë¢:W-ÎT5àVòaì» °/è @¶}÷áMŸÓsò뼎Ê*³XøÎ]Sp ¹ëÑ2fXµãI@k ÜqÛ)I±ÎŸŒHR0Æå¬Ô:<^pO~W{b÷þ%läÛµ Yfÿõšª~qØ–ì‚ò3p‡ŽØ¹K-ûüÀ68vîÿÂ`+«Ç¸´+f—ß›Í>†â.Ûjà…à v·‡1z U¶kžãX$JÅjLäíëù¢Ðž—–òâ~‡J^‡Ü9$láìl.sG¢À­ø+ËöP› ÔbžÓºþmb·Ûnœž‚©€¿¾e$0ލ91ˆ§>™ÁYŸÙ`P¤ÁMêF_ð8õr”'è nöï¼èÕÓ>Œ=ÙºÐJ41gÛ‚?Z}Bã”>£ÏÀ`f¢L„rr8TFaêl6/² [øÌV!aï¼€Ð[Fì‚‹ÃEªÀ¯}a`êŸ2øU(yÌÀgõzÃÖ§f/|fN¡×F•2/U¸âiLàÒ £8¶·]a€+;,ý`•6ÉÛN´Ð‹[ýÃ\8¤dzÛxج,ìäpÛí+iˆ#ŽÖù› qôõÙÓ ³•t3ë úa°sš‘‘“ØxBF±Œõ@¥oÐ2 ·S&O+A§“›þ;”;ô`±‘{¶¨s¨ãf·X±ž¾Pw–w €‹¹Ú¥Ò=«¢ÏQ‹úa¯of“ýÈRÄ„ºÀg18Î7ÂWÀ1·!~¾¥|D nyù‘yv-&Ÿ0p€e›ná<*Ť·xËú `ã«… ÚÆìšáôÎPžû¹Söpe[¢VîŸEâ,Vp´XC’kcNˆ^˜3;BNXX»äù~(&šÌ¹ø„áË£ð[§ð‰{È»eU10ÌŠR[“Ö2&æQ¾‚ÌѦaÂ|;F/›¥I'}MÙ“£9äÏU;¹JÑRèýš¬fe&eUy1ˆZ¢?±c´21/¡å ž »M3®Ì]Cxûpåx`",ˆø<Ÿó4pÐø0mš9†/÷†i8ˆ«BX3ì„›â܉ Ü!'ö§9V*»ze\â¡\À¢¨ò4ÊŠRеÖßk#ë5„x~œFØ#µâÄÁ‰+L^DÅ5ª¡X]ȉ=¼™£ ‘y^H+‚ïxr”é :ëªã§¦6¡Ìz~²"«Ì•ã €½ub¬„¡¸æ€zYÜ~¨NLjžÌ%•<(Ç/óðÐ3´å‘û  \Ã9à‹Ùœ”U³ÆÒÄœx¶TòØîAœ+2søð×’¢/O¿¹«à˜ß„ì–Obfƒ Ñ%V¦´aÉ,ÇbRÈqá§`åO”#?õOÁ¬&Go„\fÝztBW™]DYYPtètj2”²2ɯ\Žn mº«-à6(÷ò{:ø¬ qÇ »¦T,ì]´ÆiE†dü:›[‘‡zï2\*‡> stream xœí]ےܶ}߯˜ÊKfRšïy³dŲ-ߤu’Š+£½ikwíÕÊrþ8€ôx0îÎH)—-—Íâ Ðè>}ºÑ€~Z¤‰Ò‹Ôþ.N^}ü¼Z\ܵ·Ï?ë/~¾8úé¨N2ûO{¯O^/››…*’¦\Ÿ¥IÓÔiÓþ¬¥¹Ý4Å¢l²Dç‹ã×G?,?Y©,©ë´\ž¬ÖuÒÔæŸå¿åòjµ.“J+½¼^™å…ª–¿¬Ö:MÌU¹üqµNÍÏUV—˳ÕZ%yQ”Íòt•&µR¦‰å…\¯Wë*Éóºpwóª2sϾYe*)«lyÛý\ùr±úçñf€J-o€•GX™ÖíÍ;Çÿ:R*)ôâøÙÑñŸ~X¾XeIZ6y¾üÜ|.+Ê2Í–_™KsS¦OÍsóC£ÍÛkón¥òÆ ºIª:« #3d¥ójy×þlž4rÈͽ¬ÉÛ¡w¿ÂËWíÍL§Uÿv{ùF¾Ͼ’6}ûu¶|»*’´J‹ÂÈËýo½”»gx鸒˅|÷¦}6/kì—yÿŸK7¬—òºŒ•wŪG¢ë:/à}ë«®©²Õ)×ÀÝ ¹œ…±â¾ÁθÎ¥ŸWk#ÃTé¿ûºU*5‰Ê•Q¢ãSc‹•ÒI®ke¦À>Y*£~ü}è7ˆø”vf,ûlLŠVßòÜûtáGkAVõàS Ž]~.íO}êŽÉø”vÕuår¸0¢£¯YãŽIâš>°‘»'¶Õ45S„ír“’¡H§{m)sý¿ 3Œªªû.feQúª×ª‹ÎiQm§i*ûÛ:/³$«²Åº‡ûÄ+‡ gîª[Uè^w;Ðcožã´qm7Ïdß“¾–i§v :~A'î†*6|ö†#‰»ùQÛ–õ˜>Ð~äÖ>z¾Eß³TãÄz°6ók´lèA«§ôYPC@k˜·ž7½FéDÕõ Qì.NxM¹ÏÊ£Ãh ±Å Í$‚m@x‡ú$°á¼bB¸`€fÿ`~otU*v ¸§iæ¹ î~@`žÄ™ìàø0t|ƒs ºDÄݽ0Ó—_ÔTË_Wª2^*-Ì;É3­Ê Ê=”¹2{ó_bW‚"ýPTY¢\þÜ]f©êI™ùß-ÍÊ–õȱ¬Kf®h¸n¾?qïÀWä+èÕϫʲ!U£­nÜÏWÖ}7`{·®-Wí¨D–åF”ùò[÷¡O㆛j¹é(Êšl÷" ÐÒ¦‘ï ˆä gºíé„êÑVŽ49˜àà Ö)s  =u÷Î@p}Ç;X16’T J(ó±y ´ÜH¥ž%$@ôû ÉÊæo¶ƒfXuãyªV Œ žK£ ø—~xÏG«uc­9Õžº†ÀŽ´1ùvß(vÕb²|&PO ¢zgE7"ð¡W‚ÞŰ×iÉY0/#'BÝ~0bQBE¨áSõ¼ƒ kô™§”Z¥&T«Q)Eý8»á -ƒ»ÉÔ vžL™ßËtgµÆ TÀí`#WÒ;¦¤(ÎájP‚¢ tkCAò]pÒ}iÁU…Ñ/.Hn/T9’|Dgâ”N xe0(N8yÙÖÇÎm3öÃû¸¡ÏrÖuK‰ÌyGš4©%rÖXˆÀY¨së¡-$ Ï»™¯*Tá%Ѧ˜}8CØfç{¨ 3o¤ØH™²L@-44ÆÕUù«ûÐW=×Óuø¨Ög ŒC{ nó¡'»¡—sPdŽ^x(î^m¡Z:‚l6CöáØÜ‚9çVÁxE n):=%Ú0…¹ ôi‹ Æg¿®tRèL­|-ö½\þbÙ¼™cáÊÃñßã~ >t¯4Þ8§ÉE.ïxÂïdW{ÁÄ5¾Á{QhEµø¹4œÒkÁ½—VŸ÷ÉÛº§UÚ¨Umo~,/=q7g4jugº—¯†`DRfùXá/h¬(Âû7=ö—I]¸4VÁÁÑÐ./YðvÛªËů‡ Çxæ´ˆeÊ}Úµ*^­Tüw¤£ù˜ËûéØVqóZ›0P£ù•8ÊgÞP>ÓZø+ªÚs˜äîdûS†¦oEš4øýø˜d{áç_í¢CÙ4Ì¥M¨øtlúž{ÕÔôÛ&Œcxví$p¯#—sX²æfÛrÞAìeSw£5(G ÅYîö[#Ÿ ¶LµZK_¶Ê`œu’kH1 ‚‚E¢»PÇB>àrùÛ´q=ô€Û8àV?Μzæ(³é~•—÷%ЖZk<éLì_™ªjŸY h¸IžIM"dœ"=„º[Xxâ¾GWx²%<¬z ñ<Ü­ E“ñø¹–»‰ˆ§ÁgI>áEØk³ð$€ÍÚXsÊžu›Ž›G„B‹[”Ù‰ÍÉ õwdöNanwÓIàíqnÓ@«@®÷G$ëEçÄ ü/_½½ æF«@¤6t”ÔÑžiaª¿CrËN²Éù’'Ï\®,·Å€¬PãäªhIè 1i…Øã{×/ÄwªÉWDÉúM3ð ˆÉÏ0Í`¹0XdR u›j4oç‰nö©õ\•ßúœ±õ?\ f ;4þ¦ë¨1¤9)s[I›3HòšF%­ÿsøqøu ¾ •——GŸ¨¯€xž\Ï󔊽Æ{63Ë8ÀÈ<â„8È2¶dm¸ÊÁÏsñšà@¿±9bóžñ-樂"8 ¢In²ï""p /v¥<ŸÙÚ¶2/Òb9^óè)dK¼˜¨0ú­òâ@–§Šèi×ôU´›UªÌÐÀpˆ7H(¦ËVT–]5ÖïK›ris`‚™vLðÃÚÐD4ža)-ðÙ“Ê+èwÍÔxT—ë;Å®*®ÈŒåRcž…çZIÈûúP+UÁÏñuS\™ç ük“eaCÍa…dŒ[}ÂË|>Êž&W€²¿ Ï/£ÄU¶>¼ªËL*¤>LºÑ-u|IÖúÞv$³VdÛKN<Å„M0Úrø,-BÞZÁ2-PæJ €Iß’Š2àp÷ŽÀÑ=8ïuNRЋRºJùjzË$òD%B°% Q%ÕsM?eù"0êó¥%È ñ<©²èÍÕŒ>ƒ~»ƒ2‡01àk¸¦¦ ÂEáq$½Ó:«H­ãÌÜ‘Æ8@ž£€ß…»w W`ª™*v+/Þ9°ü¿ª9¦\co3©·ç—Z³óòKL^[ˈ+Ó§LªˆÝK±R„! C2J]SÈ›`ƒ†èÇÝÂ¥a\0g˜¼›7uÑg(d§äž3æöÂ7­R[< Šøáz1¦³= vÎxPŸ¶b&‡þÁû;í$åây¥§,åX[ª×p.£E»Æ}[µx0oå¸ú¾ÈϽ:<;}åfq±/LR¦+æñÔŽ}ÅÄw s£Iüï$÷µüþ [v„Îî­"ºÉ3–Ã÷ëÚ.zÕ]t[ˆ)·Wëz™Ö[i; xIåÈ=øqˆx»ÂRÀ ÂÛèþkÑùÑîépiþÖ…tóæ%P¬åhì™™WÀû™h dZúOÙ |A À«$Ûaeÿ½£èâmÀ1è»÷¤ F “Ç(¢ÓÌ`²'g˜FÞÑírÑç'”ƒÃ)†`«LjYÍ›$ àç¾—$êç« «ôö%²j€­É #º®¿¨Ýì<˜Š/ƒ´g¼‚Gu¨åi§j­uÙðùÐý&Ç$¹Û(Îv’_¡Ê»a—r×[ø(3¤#ŸUà) 5;Þ®WÉ€w‡‚HöÚ&òáA«Ô!èTÎ{Û¶ÝžáµSðâØ>¶jOA†Ó»ÝÇ¡›±²å†ÈWd=€Ú¬—eóŽ!JÑm%g­Ü3£rlªãs°¥ºˆg®'¡v$¸0 žÞÄ];MÃùÄEt½â3·ÁP´\ª½´üV`\K”î>£bºwù-se6™q`ÌœR¹˜VMD¤øžFtâ¤0‚YÝ¥PºØá(ª,² É>»àþÀ;1É"’ÀÌÐæÔ‚A ¯¦"A‰·±š!Ê^kº\PžÔ©;îUt Òƒ]ºÜ9O·ÈÌm‰ÛŽâ­ó*itãiÍaò±ü04x¸侨H¦)ïeõuJ…1¼gÔÊ ÃÈ"B»–Û Úó|Ó‚¬äHoO¨ºr yКؖ“kbã½Ù‚iSB5b/4ŸÉ²æ­{sƒ±mŠ…wªœ›¬ž±uŸáô2P øÉy; Îë€`츎 r• çÄ6#.ÖKÂ#lØËµè² ÎÃú V!ƒž>R0øË)ü£Äùyoßíá{¹}©“÷³3\ :ï…áoX‰Ä ½yŽØÛw5ÿLm+ŽYš˜IE²—“ôÝ‹ðp,½ù=oÙ'òWœÆO¬·;~é9<]/Š\£åA/'·Å²"3ÿn[š³c{ÎÂÿ‹¿k܈Væj(rÑyÄ[ò\Dv ÎÑ‚ã#§Ý‘ m÷¬h¢6îeÀØÒÊ5U¾gñN»Ü ™ÂL¤qGÝæëâ.z+9f>Š£ãä.ú­9ÉÒ?"ºk·=á%¥„£¿Ë´PöÆöäøè;óçјcendstream endobj 144 0 obj 4314 endobj 148 0 obj <> stream xœÝ\Ûvܶ}×WÌS;Óåa¼õ-–UÛ¹5µd']Y}-ùVY#ÇVÒæë Þp6À ’#Y^ËMÂP$œë>ó~•&J¯ÒæßáâÅ»ƒ¯ž”«WÚÛ«'û‹__¼?¨’¬ù§½×/Þ­îŸØ•ZÕI]¬N^¤I]WiÙªVEnÿ¿ÎW¥J“Ò>ðîà—õñ&KÒ¢6f}¾±w•6åú×Í6OÒTézýj³U*Éòzýfc쟳ڬwîÁUû×R™zý#çŒxÚÞÌtZ®ßn¶¥»Ìµ½Û}§ª›A3•%yªÚËÌd‰õ{ûš_çe¹~l¯³¼(ÒlýÃf«]U&_?ܨ21u©Ö?m¶&Oª"5v÷è¹|øR.ÛØ‰•ižÃa¶»a^¾ôÎ~ÕØ×rõ£|fp&ÀkÏå×rùJ€Ë7ÄjüV÷g]´òú×É7vˆ\¯N¾;8ùË/ë€ÊRqËÍÛ×vQ™Â93½~hfa_/Šõ ¹|MWwÑë¸jt±5Z'Êê +x ºnªõV¦ªD:©|À“¯{À“¯»ŒÉ×Ýíl Y㔀¹Ê´O\®³\8[¢."¦FuB=÷üBUV~&Cñ/Üw㣳À”¬ÉÈe*ËWsb•‰ŸRùÔvrÙÛ‹Îjt ‘õ ù;€öQöÚ >@‰ÏÚc5ÓMæ”ìö\ïÊöµ@e¬=eÞ¶àm± çÎ<¼NÌ û”à ráöUî±ÿ 0ˆ]%Í ¹æáä#¿P¯ù»bª²õX‘»ê£Du†Ü­ŠÙE˜žsò—TiA;ry`E'ÿÒ._aLšƒ¸£ê”+T‹qºj¡d¡]˜!ÿß%IîÔþ³·¨£ùŒ; À“u‘ç‰ïÉðUâ6‡AËw†f‡Š”)‹vÚ§òxknCø¢%/ú2Ne(òãçU¼ b›gé—-çñ~‰»ò`eÖ™< žX •· û0Þ{zE–Ö†ÈÚþ© í{·ß¢ä‰†7Ú&[»Scü‹iû:.¨[t¡+ÎÉ•„ƒ3ò×X!æÆ·K ¦Í)wàM‚–\§~ëÕgþdµËÌ7vðn†A™èf˜ð¶óŸÝ9»ÒÜuÛ#JÐù<zGñÛahõkzB cŠŠ#ltßCkfÍ N߀ ¡öŠÚ7qû¬.{yÄÈ èñ&ñö@œ,Ar+îÍqü[gu/¾?Çíẃæ¦ÇžlÒ³s1¹[Ì[²I?³€ÏìÓk¶ux° ª†§âÝ"BN'Òö÷O¸Âá ¹Ñ“=XÛAôè§°Ÿ&UJa9i ßuÛâBÖ»o{«kt Ül3,,Z_¥6óÎëWôrr£Í'¤¡z” •€ˆCàP´Á‘v©þ!ûÅÿß<—£+ŠŽXoácY' Àë³Q#\ÏØÇ–Ÿ£+ihT9êçèrm5 ?UçR¤’ôêCRx¯6Y‘äþ/Ðã÷;)[]ëÌú±¨òKëH›ª¿vvGW¶yxG q%º#pÞçÀh»ü‰Lc%Tĺ†às7Œ# wW¹±€›5.8Æ5뤕–'™Ä­¼uÛ…)ñFÛ¼ÞßÖ.˜ÕíÂqz8ªLe²ûÖ[wÚ>Ìz_>Kûp\­M¡jD†ÉÿÏÊ›ÍJÙÌÄC¥R¡âj°U™­õQcOÆz2kÎÝêÀúÔîÛnâTÊSIº{CØ ÁÊCñŒXZ`îÙûM·ríþÜjxÔi”“’dOWrÊLG†˜‚ŒÍÚW72Ï6>b¿‚ËnP1vtvÖžSXÞÖpl -×LøË5íÍÁ9:oËÆï(€ŽÐT{»ï0×èæN‡v#èáÚ›ÐðèE—lM¯rW\âa¼`'], çô†ºI·çt/ý¥'£C¿<´Å~&…¤¼”Ú Îít0äÍ<‰ÿÀ<ÀÒ·ÏÞ°]›7‹?ÇKÒoÒyH¡$íÊlÚsÉ+¹¼îŒÆbt£Ü±®ê¿öoI/¼1Úb÷¬…F6õ¢5 íîƒå>”³hç$öÐú{®mìÖ0Àl3éüO7Äa} Š7„UÀ <œ5_Ô¨ïP®ÉÒl°O!³Ÿøf(‚|†(ß¼ÖФÝl”ÒˆïÑ3#¦Óï…|ϬkÙ@a€Þë÷ÝõæÂHÕS'‹e‹¬p{‘mlø~sÒÚ)c>=Ð{´f;=LûˆY]> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 24 0 obj <> /Contents 25 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 105 0 obj <> /Contents 106 0 R >> endobj 110 0 obj <> /Contents 111 0 R >> endobj 115 0 obj <> /Contents 116 0 R >> endobj 120 0 obj <> /Contents 121 0 R >> endobj 127 0 obj <> /Contents 128 0 R >> endobj 132 0 obj <> /Contents 133 0 R >> endobj 137 0 obj <> /Contents 138 0 R >> endobj 142 0 obj <> /Contents 143 0 R >> endobj 147 0 obj <> /Contents 148 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 24 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R 95 0 R 100 0 R 105 0 R 110 0 R 115 0 R 120 0 R 127 0 R 132 0 R 137 0 R 142 0 R 147 0 R ] /Count 27 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 22 0 obj <> endobj 23 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 130 0 obj <> endobj 131 0 obj <> endobj 135 0 obj <> endobj 136 0 obj <> endobj 140 0 obj <> endobj 141 0 obj <> endobj 145 0 obj <> endobj 146 0 obj <> endobj 150 0 obj <> endobj 151 0 obj <> endobj 152 0 obj <>stream xœQMkSAI¢Õ ®lñ.,~tQ¤íJ*!´ÁPÄ“÷îK.Lf†™ ö nÝ8‚6*´BBwÜ—”B‡RTŸA®-ÈÅR­2ò¤•Kà¥Î`J‘„Ç)š hA;"çb ä``…ò˜×@*•㬲ù\+Æêˆ"¥ºÚy—Z2¢c÷ÕëE~(|åë( óX™ét<ÂÈÿƒyAÊÇc_ùô2rFŠ"úF)ciÞÂØ‘,Ý[`q l&ÑÍu«­,烦ÆÈbÎÕóª¿þäÊ<éy¡²¨‹#:è,—ÿGaŒÕàcœyVç¼±òøòÛj|áCørÊ/?‡ÉƒÍݽ'›ÓÝÙl:½˜íý~þhõjûôfy«¼ÍØ5|Êã endstream endobj 153 0 obj 429 endobj 154 0 obj <>stream xœcd`ab`ddòHÍ)K-ÉLNÔõOÊÉ,,M‰jýfü!ÃôC–¹»äÇ•Ÿ¬² >sÞðvó0wó°Ìû±FèûDÁï]üß[˜+¦,tÎ/¨,ÊLÏ(QÐ ×ÔÖÖAˆZZZ*$UÂd\R‹3ÓóÔ€Œ²Ôœü‚ÜÔ¼kg êœœÌd…ôœÊ‚Œb…Ä””Ô¶°ÄœÔl·ÌœÌ‚‚ü2 gM#C] aä—™›TZ¬œ˜W¬à£”š^š“X¤àY’4EŽ9/¿„ÁŸ!€!ìw†p†}ŒüßWñý8нàGÐåŒùB?ª‹ oøîùcèoæ? Xï³ø¾•UøÂw¦ÅÏ?•Þ»!#¶µ³¦½ZÞñ÷G äí<}3z¦wϼ¶ÇÂÞ³ ,\NxÃŒ3¬@#KnüйÁxûñ÷+™ˆüX/ª×m™áçõ›©ÛŒã7ó ûï,¯¯oºqXnç©5ß»u(ß—±6sMÜìÀnŽ:;ѫۢ­ª¼ "åSÝBÓݺ9\â÷Þ>2çì²½òÎÞp¶›ƒïûª’Ë?tÎ1¾yÈüƒ èh#¶UßUXÍÙr~«°º°Õz°^b³ùÃÖÙØÕÐÝ(µ6÷ÐüU}ý3åf_Éiiëªï–,­6»¯gBÏDù›?˜YŸ°ñ•,ú±`þwßù>‹Ønr=æ¾9‡‡Náß endstream endobj 155 0 obj 543 endobj 156 0 obj <>stream xœY X×–.„®ªˆ+m³H¬BƒQ\âE‚"¸¸àEEAˆ‚ìHÓÝl—µ›n¡AÅ(* ¡q!îò¢F£â’ç’DcŒÏI4yÉ©ÌåÍ7·º‘î—o23ŸŸtWÕ­»œóŸÿüç´ e׃²±±agGÇmÛ.}#ºÚˆïöÙî¢øÇÙ Êkâ¶Þy½lP/[ÔË®æ]÷Ääþp³/$ö£lmlò+fGÇ$nØ´9ÖmxÐ’#G޲Üçééé¶>ñí7Ÿ°›¶¹½O¾ì ‹ŒŽ‰ Û;Ím6±ÁmSdbÌæn¡7†m”^[¶ÕÍ7"2"&&z§ÛðÙnãÇŽ7šüµ>n‡›ô¶h·…nKÂ6ÅE†nÿ·›E-ôNܶa¥ÿ¬¤è«fÇ„-òù$L')¼¤“H£ªøzü4„N0ÏjäÁÉrì(¦ zÃn+?±÷Ó:}ƒëgȨޛ°gÙvÅbÁXN߃®4o"‰-ý`Î¥ þ›â.æÈ&’È…÷É.žNòq‹ØKztyubñÀ‘£°3ðãpº~½úhÿ©qw*cK”%ªœÜ ·2xÁŽÙdd™wÿ ¶÷: ÇãÛ¡Ë yùͤübu¹k©¶ÄÈËž3ò›Uê’]œ¼!E‘È¿ECøãY0&O_ðt’¿€ (ÀÒˆWäw*õÚ² eq’dÝn ñh¼…Õ²ɺeå&ëö¬+!@².øá4ˆ‘,‰–¿°ÇÉâ âÁ©°<ñÙ‹çÀE®dãD>‰îF÷mAìKÀý5|¤€q𛬤3ÎË –i#ĸZ˦†Óx ~ÇÁkÙp ©i’?j;ã",PÞÖ›ÞNà½Å8 ðo23<ˆsÂHlõ“B"O°%_AV¨¾L?ï¸ùäËu§&â/Ú߀ΰ½¿Áaƒe±s´€ÃS˜;Bû¬ýÇÃp­’ÁƒiGõêà ;÷lB›Xÿ¥«|8ÓÂë¡ iác b4&þu Í(+'NåÎA!ƒÏá²MbÏW qnjñ,'?±”2\„2¶D“ñz’½Ÿg+°ÃÐa¸/îÿf8Ô¿ù úÀ€aÿðëíþùhê{Ã'OuwŸüðÍë'áMïoàvÕ5Õ'›AlÚ}œäib¢øZ1Úâ€sXI|{É‘™Šö#È ¼¼ ÷}õ>Øß»|ô«“œ{¸< €ËK.V•!¶\«'adÇTª»¨ÝÌ­ð$²# ¤^;Ô(阥#T‘+µz™ ˆX‡pmáZ( OÎ;u±ÐïÇÀž—?ûÑÏq?¯•1¾Ë9ð=¯xsÏÛcÌ4/áÓï¾|ùuÇOfôÀ,F6ânV Öœê”ÛOkI0`ØÜ;þî®Ï¡×©z}†>C“•“©æ7lç0…¦ÝHýìŒ/n]|…^¡ŽÀÝcÙn#¶ âD¦ï(0¡ì„=±'OàaØL/L&ØöLcw“?ä/f$ðòWËÅʳ¦;]0MËb¿R&? öñÃƒŽŸù·iÎÂÉÑ Iv`«”ÜNŸ9qôz•³Ÿ;¥A(±¥d™.-Ÿ,öÕ†!v܂峸€‰Lw&ü?õ°"п“¸ìÊ‹8°+±. ÀlÒGd]OHVÀtè§/~œ|kZ1dgœ5©¦Òݧ ÉvPþ̃ëKý|g/™Èwñã¢o$j61¤{óïB|““ü Y†Ðª{¨Ë(/Õ•TqàÄT©Jv¥ª5†`wcämÐêA'i•eR pÀwz3±yªé*MIZŠFâÖ3ÄA_2o-IVŸM"é&DJäÌJÁ³ËJwñÓ‘¦+s(Ý l 3”§K´å ŸÐò†×ÐãÄÞ®{Puú¾X­º §X²º¾”X=©œÿ¤&N¿ ­DÑLaÿŸ/øžpo >þðÏõÕ+ÍrÅv£Gc3?Ž»Ûg]?Écã·,ÈÃïÐ1t:Vy´¡îpÙ!$°d’áçùËn<˜`ôQŒâeÑL¯F{utü(Y3BJ“øPp0ÅIJp‰8•+ü &³$“÷'ŒoÃÑí îôvnÜ|uf–©]Èô…yy…ÜñÒ/ u¨íÎþTIô“ò !{·kóßU—*õéÙY9ÙÙüÖø$õN”†Tù;JXy‚!*¶`›ëN”’©aO‹OÿwH[%o÷fÐdk ²2 <ÒÒºá_z0IÅÊòòb36‚þ B©9WæýMŽžt’DQ¡Ø|(¤b bå¾³‚ço­Úv ‘?t@Ý¡¾ Þ¯ÞŸº?¥r;Šeå†åó7NüpNC+§eä¾¥*=Ñkó% ç§ë­ªœ“ç–iuFÞÀ4‡ŸNºNøXöäÂý#ñõ‘{ø­5a†EÚ %šü¸22×βôÊÊÍwÜû–¯‹;¶ë«]·Òª5µ)’*·¢Í¬÷œ æÇ"ï¶ìë¹»IB, N›2&A§*-'Ëq¹!X¸¼ã¦+ôúöæ«¶Ø–°}|¤1Z7£Ì_¿CS¹£2¥Õ±×ÚÏ;ÎÊŒžž¶¢¿ãcbÅ*++ö°lé í‹ïŒl%= ëfÃÙçÿó@#ý„²~†çØ`¶¨˜kJ@fÇ¿–ØÉÒèÙiü_Øémùà,8,^6ëì$q²ã?¬")eTÓ·ü>ÿ |~jâ2.=S£Aé¬R—aÈ/”ÄɃ½´Ç{çÌ–y&°"²x aúþ“'òòÓ3¿XñâÖåýÛ¹vs.—š/¾!õmWoÿ½å”FyÌ„Ý1ÇÜ@ó†u¶Píx‘†0Ñ(;d*N+Tf§Ä«»¨ãƒ:ãdÙ4ãd÷MÍ‹ŠŒâDè”&ÏUñ‡è+S6‚·”?Q<¼ó2²eѼÖå;Kü,¥½`YxÛˆ&Ø;ï– WV.AN⤷=š4>‚óe× 3þ)ô-öÚ`žB6Ò&ã%è?ƒG°M“a¼LKËïw÷r°…ºNÒxvÄÁQÖj ±r“Il-žÕÒd¾ñx! Ÿ ZþÌRi¸XváG“ÐcðÙ|+p¶ŒÈ°îñÝn‚M^ƒ ù#iv;’ú¼-5L=~Úݪ45ì ]aAÐ".6®=êZ(bovXõ2NJÏ/×í%9zhK//Æ·­|GéË«O/^¿eçª\Ü©­{סP´-1"˜ýs‹ú ÍN¤F™bV^Ý ZžÖi´ÊJ/ŸÑò6Ÿ“W"n»BïW¯¡?ôõ ÷õ^¶Õg¾Sü|ê{Ó*d %éœi®¿ÙB ™+]™‘ž…r 2ù¯pÿƒÃ¤&[Ô{¾¸Ov&ÊAYlzI†A¯Õ~ZÉݛϠ'úý²õŸ~`Û¨,Q"ÉÜ&b9,ÎW`—ãš¼ì¼,ä¢TªÒÓuº\¾0\B=±F™›²¤zÅ +*4踇`ûÛ–¤äh‘‹A¯3gi³‹ù©`Gx¿¢¨¸\Èn•ÙêÜœL»¬“lj¼NUg$˜Žü6™I…ð KÀ3`]enÕ$rxžÁtwŸaž/UªåeR¥ÚÇÔì'‘ï®ðÁ¹Ùd‹™.)e»v—i «  …J‰AS~¶Ä+ŠÝÌž¤Ê¸Ø¤¤¸,”™§æªð×y D’"—rT¬Ë«f£ñ(Ŷظèè½qùrºnßžvì‰â»v+æv£¸s¨%|¥Ejl༣ض©.ê3^O7:ÐØy Lz,îü£·¢@—_€ŠX½R§ÌPg§j8ü÷-ÌRçfË*õz}QQ™V:Õðg($µ"¼ã2!Qø¥ú“‚¾Žˆwr¿)Ê€ÀYQxUøÁ„ýÉ3®¦}™¶;soÚ¾”ŠOH‰!/]¸ d²÷܆–,N¾ 2'S›ê*5CùéDn¨ˆÜÐUq»™ýÛÎŶf³Øž‘—m^A @RâÙ3oʾ¸ˆ ã Ç¢j¢1:ŸòE†øâøÒ¥©µh?{éBëÍö‹‘›HõQ‘SO½Š¿Âtýt’Ëm>«)dÛÈÄF•©W`òyJ“ 9ŒTɈÉð«&Ì ÛbÛ™˜Â¸aDõßÃØ‚í= `<‡¿}©@W«nlÝW¤ª5£úÄ=ukŒ~h;žA ü¢ÖÄEDćöØR½£~[kâ tUõ®ðn€ 6À4~Ûx¢ñ»[è„]ŠyË–ùù_zãú… ×o›Ã‡Ûn [µvãú5k6 ­§>kláq_q¬bn×ÀkM}ÉÀS ¦!«Ã„Ö“Ÿµò`ÀIв¢¢bTÂêU%YY¹¹Y\gÜ'5+Sƒ”.$Ätù…ùùZ³ ´#¤€¹+Š ˜œ•©–†4è P!ÎͲÎ2Ì[ýHrÛª-Åugói$ /…:B‹)â Ç–Q‹é˜æšO«N–·¹¶Ü]H+ƒ“×Ňc[ðuÞÒ¦¬E{Ùã Ç÷U 5Ü¡J.ÅHGÂU¯ÌÒädk¸ MlùÎâXä2wiÐümEé'¼yßø+He&?=þ–ï“Ö†š–ƒ\lƒ"vãָ͈ XÑvýRËggK»ZQ¿:ˆþB8k8 Šæâ©/.=ýŽ•¯#Y±wõÁ+ŸMz4Âitp8—°#- %²„)ô†bmy!WqåjË ÄÞýr‘oHBôè1ù眯!ĈHàç°¦ €&ÁAôL2 yyyyˆ•secÛÅßÍürØŠUÉQ¸øäÔD¤d Ž º‚‚Ò®®þ´ñbï_^°.zch"¿S™œû1b“Ò»¨H~óåãyÓgΜ7fÙÒêc«yuQV±Ê ¥2­4µ>‰o‹9•v±Àüøèç«Û?:ÜUà[IgŠŽD0;ÂG <`¬U-2Ïò#ÔÇ0à´åçÔEn2:e«Ø'(p Mù7Ù> ¯þL „ý;Â?p%SÏ"l]O´§®Ü ÖîÊçáóÜïæX,ó$L0šÃ2ȧ*àØò†•Çw\AlǽÇ$ÂTç¢ôt>.yCÚ:´ m6Æ4FHºˆ®°0‘ç«/žß8½Ê— f†æy É¬Ï¥ z}^~5×l—––›ŽØÅŽž¹rñøÞìƒGMðQ "ºÚ½jˆH† ]m>e<á=tIŸEEÅ¥\sM‹¡ ±O.ù2w–Çâ }-Á¼F›™OŒ›ž‘¡L+O9¼“?³åLòYb\ûo¾°óz8tm¸jçJS¯°ŸðÇ@Ó‚f¸¢xiU úW$ ²\¢Í›llþã38I¶ç™Šcn9Ò£k•íZNß}Ðz‘1§$S—Y˜[„Xƒ¶Ä`ܼ{ÝüùÛ׬à½nž„0ËâÙqèùêÞ7@½™ùd¬OÐÏ \a¢¶ÅPU±û𱦢Ón]œ7}Ò’¹Þ~+O>Žá³JPžf¯ úM¦ÚCÂÄz±Y'vês³r5(ÛŤ,ŠŠ*Š8øP¬”]ëdg2ñæÜRÍ…¦ëÌtwñrxŠb„E÷G$!µîëW]G®¤„)µ¢ƒ<íáºB~ÿvC]sûÀgSï9uæø€Ã¡_…sòg³"6®˜7pÄ“é¿üòäëW×7žu„ó‚Ýdü½+Ë|æ¬ðò øüúWíçïò¸ÁNþìñÕ%^Þs¦y.h¿Ûqõâ£.,l!ÔÜ£N5ڊЩÈKÌOF*’^ü£×{Mž‰Pb¡²HU”•—)Å’*-ñXòñk×j[Ïò_ýÁ;,Ìš}pÏ3>ÄÔÐû“Ÿß>ÛôPà4ç;Ö¦'¦$oÝ´ag8bç,¹þõ·¯uÜh[;y?_˜r‹cM íþglDÑKñ/»UØ™,AòÿtpþkQ¡„³äþ¶ððºmæû"šÄ4J/î"6çßeQIau‰YÍ‚ýùöÑ& ŸY T¿wÿáBc¶•³¥º”e¡k9‰Ã·ïØ”•œ«R¡](U§Ùs,§v; —¸3°yÍù I«‹ò¹­±Ÿ¢z‰k‘^³eß‘ZC5¤߆"…8 §åå —b¤-Ì/Î/p&g!Ëâ¬beþV9ù£/*," ääÿÎÙÅêB‚A”•“™m’°Gš¤ÚøŽÂ$0¥ôð¯ILbq‘‰%U&±9QxÓl#ápºòr©–ì $Z^“šªSy1¿g%í-UPŸøjqR58TWWÓÇ{ öÇ{õzõ¦¨ÿ£þ endstream endobj 157 0 obj 7104 endobj 158 0 obj <>stream xœeW{Á²ÐHSçYDllL¢©…£¥éÔ)Sl&“S—ED…lL0]`êf*¤aºXLvùÇŠ¢lݤD¯]¹4fÝ2ÇØPw§õ ãܽÉ#–»l\±81Ò;Iì“âk:ÙÊzŠÍÔiÓgÌœ5Û–¢&Sc(Êr¢l)+ʃZHYSc)OÊ™šB£Q6”9µœr¡ÆS+¨ÅÔ4Ê‚ò¦\©é”%åCÍ &P¾”5“ZI-¥¨Y”µŒr¤fS"J—Ò£†Qú”%¦†SeH Œ(cÊ„šC Di‘ÍWQ•Ô/¢5¢"Ñ}©yšÖšÙZƒµ’µ.ÑÚt(]N?e"™ÒAÓe°“ÙFöã`‹ÁƒÿÐ^¬§¥}\ûíCâ†d u5´JÇXg“ŽBç¬Î=ïuEºÑºûuA/Gïæ°¹ÃÎ ûY?Ó@Žéò€@ŸQ&â9e‡'0ØúcÍïdŽ€>at•ëQ95‹ÉšÏ•àñ4Lf&(£h¼€QM$ËÉÏ@îî}{2` ´.߆c@SÉ’×rùFûªF‡ñ£ilÅUgßIã(u±„GFüûÈ*×§ »Á J…,mø3ÜXd³ÚÃ.ÀM6Í`±îK{0ê_-/®JZ»jÿƒ^±0lÖc‘Û]½í'iy‰­±ž©ù‰Ó´Õx$‹§¿·„yà &½0ï´›1ÑÖN¾;Á¯½¯»êµâ®´õáµúç¨ L.(JÚ“ŒH9yH iÿZÑeüV!õF¸!Ò”Á3>f Ä»õåî-1w‘1Ì ÛBT|Ä.Ï?sYå$M9I|•3“Ägø„+nƒÁCÐx% æèÐ&µ»Nj·]¨Ý]Hä ¹-¨ ÙÔÓ¦ð<ì€ìÑ\Y ÿ— ÂÐñ,öjèƒNïuÐì` “º:>±^¡ù(owµ¶öp8–ɯÍ+)*>\Û\ÚŒêQͦ¢˜}©9[P$!jé'?íÀR“¯W¦px2°µ°·Äb±#hb8Ã𿼒<}«]Ú,¸˜~ÀK$Éx8÷Ÿ+‹±†]ì´5îR¿…náŸ#ÖÜí6ˆžV¿>}Uzþö͆LjŠLô$ôEÊAä[O ¡‰ôúöăÞ{?½þ=íáÅÜÄT¨z¹^>†‘Ì÷ŠÕSìâÆFxH×Ì÷™N¶‚WD¦õüð^ÄÛóUÜl¦°-·æàJd\€öfɬè3 ÎÙ”‚,‘Op’Uš«Ñ<&ÉŠ~ÂX©\„‚õð.t7Óß©° C‚âß‘ à8qà΢‰£ßôõ @Ö*0·èú„/#¥Ì¦Åß]‹ 9ê1ršê³ØÓi=Ö@X¬ñpnïµæÒ¯¯K¾pHsõX倌ÅOü×WÔH³IŽev¥gmAÛPhUèɤ†”ÓÛ®!˜ƒ@tÿ ‡!|‡‘ø;|t,w©!`Ög6Õ7òÔÐ{t·´_ÆáÌ'N ÜW˜ý Àïê¬óÿõ/±61XÂH°0…Ñ#Ä=ü ^ƒÛSBd{?ûf^6°sI ]#Yã…üÐò“ë^G?‹7÷v%m•›D®‘/ —åH–¤nߟuˆÅ£™ÌÔ¬íh+ZUx:þ„¼5ãbás ºaø•u'fWJ'V¯<˜V€ÊŒkëËÛ$➎º¤ÈíY;wmUçÑ/Íýã(^©þxÕãèEÛÅëȸµ*Ö_G‰ÿ=ól›î´<ÆÍGrÓêÙZNPñ}~XãÁ`á–#ÄߌZ8˜Ïˆ{:«SÖ§gnÞ&÷&;¹>Î¥çdä&³0É=²¹`G:`Œr³ r XqÏ!ÐÛ[_i‚šâŽø·UU®dÅßh®ìºDn~q"öHdíÚžˆUéü õk~*× ^4Ø1gÐ¡í¥©6çmBÉ(&]–’-K÷CaZÙ$Ü8ü*„™¬làGqXÁÄb}æ0›CÒâcãˆØ¯Zåï9ÒÒññïU½mèž}x»é%z‡:ƒóæ³ a²}ÎcÁ‹^áYX‹ü&ÙJì§`1Â,‹@ k‚SÏÍÚg%âä/mè7 ivÞ¥GÄO%nσq¸‘áq´³ÐÒw˜…øefÚ®4´ÙxE³ìAÝáÝyÕ’²ŸÒvm#¬5–§Ü›S¸§Xz Z‰‹Ò…cýÍö¤¿ ¨ø@‹ £LøÝŸ”© òhð,·‚%¦À|„ø2˜ßÃåVç@e,PV×ñ´ óü¼$^þáó‘š}~5 Iø)ÉHÜó6¥&)ÐÄÅeµÙߺνÙùÙùlÎd&ee 4äSÔ°áìê;‰? ¢©=õ—oµ´y€n¢'+.Ž+â #ñƒÉ¥±_5™\¿Ôüöek´÷ެ­Y[%j u‘šäR>„o¹…ø[¡¦êq<‘ñÄU4>É\†ª¿Fèæ.|Kÿi­^*óM†0ˆo]„ïmšãë늌ýâªÎ ˜’ޅ磹ª¡;“w¥ ã¹—}_?l?r½]’ºh`§ç‚tîç{9W÷ò 5€ñ æT½ý--`Þß=Zóšê^PºÿB»†ƒWª¤Q¼T¸|HÊòiI½~U×çSë5ÁÂÛמám4Ì&ß‚A*„ÐxƒðzzÀ!ô’׆À$MåxòîÕÖd¿µS±ýªÚ¶ƒ9v%íã· ™«¢¿Ü°sÚhŒÙÀÄ×ÝUg¯J:µ—J¾!KŒH Ò°šüGC(ÂÇàY*]"åxì "¨èÝרN¸ÔýMGýžrXh‚ãß&áxrð®âÔedÜT)OÏÊØµEºÿüéÑ}ÅÉ.dÜXž–¾+53Mêˆ{Ä8·†ô+cßV›EÞñ^+$içj—"WïáZüÂèÿÚ\~»Pl U'*Za-VøTœŠ¿5’xÌÀQPqçîÏùÄ{¯’v@!Ý‚Ûs ÓP°¹‚ÛÓÔïö~sÀë5hi*£ A)€F»Q¥ØÍ+h<•ÁrÕ©Ýü)áîáƒ#Èà/îQ]hD¤Ö9µ‚w®d¢ñH*Õ &‚]`.t— ›t]Z¹Ò‘°N¥\Åáeô->*eêó._Z¢$$8N[e8‡idíºk[‡âùÈœÅz÷ç_Ê~†tHZÞ6êeAÏá*‰%G†Ö£V.•c“ØIÒ‹ˆuˆ Šª»ðã9ˆ¯k“nª)­'¤Ÿá=¿’@Þñiœ™*­_k'*ýh+fí,Û5X%Y ëˆ;ü‚Ø`>Æ'**Û˜ÊÆš ò蛋5µM-ç]E]Dq?¿Ž ±õ¸ÉØ.N&LÑŠ¶;ˆm¯ò J ˆˆÊ6lcÆ Kgû/ ÇF,Ÿ¨n6]ˆþ7oq‹$fãÁb„¸á.wågÚΊù†Œòè§î.^‘Õç²$ƒ² h<ŠÙ)ߕВ‰]z´ zVw키ù|å=tµËOøeÅ}ÏÏ5ßxfòƒCÏ\ë$Y¿6eMª= “"·´ ¸´¶Ž˜pVQbçák#MWK Œ–ŸNhñª¸ýæÛ2¸Oåµiâ’#°Úq°¡0ØX»1µ$fB«èð×ðà)9¥§3Ø\%¥Á€©8ÞXÚ‚ØËµ<ÍÝçÍñ ¯<±7;/;_JŽ ;“ÈœÜÌZ/ÁXg3M¥¯àˆ3ËG{ÙwΗ1m:Ãû3W‰Ïô@솰.‹µŸÎWÏ›ïµIÎ\©~î±ïÝºÆø¦„úIНåì+ª­¾Ûx´E‡RòƒX<{sçêÂWZ¹î°2¼ñâ›®'¥j*]Îs…üv©ÍûbKl5ï`É9± –?…Ñ0fõe4òïÀ”ý ˜’_¤og‚!©<ŸÎrÇËJ‹¾*fÅïš+Î5y;§ Æã°ŽÖ_SÕ&ñ‹K‰‹"^¿z£Ç ,úÆŒ¿y\ÓÖ.i»P÷ÝFWä ËY+rl¿ëhŒ^³Äkå•G]mçnI—ãzNÜ×Q¹rÆò¥vÞk·~{õæSé@d°¤|š·A2ƒ„-ïë>Ør™_d}‰2Xën¿‚Æwíï¯H®~86tÈùÖþéAñx6&Çe¼S7\Þ_ª?xö’tKÀ– é1 >k¢ˆ}÷FVÔîF{²÷JÁ•_N\z˜×„%ös¥âýeÇÛz/?'¸Êñ„ 8Ä­ ã†}`èPŠú;Úé endstream endobj 159 0 obj 4400 endobj 160 0 obj <>stream xœ]W\×öžvfèÂ2 w×DŠú¢HW° éeX@Öv.–`"X *„ª ‚'JKL$±äùLA‚š3änÞïÝYÔüßÿ»{çÎ-ç|÷;ß9WDi¢D"‘‘olbFlº":ÒÁC™#tMá-EüG£øqZ+ÿtV‰ÇQ‹Š ¾Ò×>ó‘I¼ ïh MF°i4¥%eå,P¦¨Òñ éòÉÁËVÚÚÛOù»gº›››YnM±‰Ê”¤ØäôÙòdtb¢"ZŸ¨JIØ Œ‰‰¦­ˆLŒ]/÷V$*RR”òÉ lå3¦M›î@¾f*’¢6nE&o/’ öÿOEQ³ç«’£<”1«¤Ä.öŒ[â•ï½!a™Oº"Èwãr¿ŒõÁ™‰‘+²’¢VæO¶µ—Oqpœ6}†“³‹«;EM B¨Å”'åHM¤–P^”;5•šD-¥¼©i”åCM§¬© Ê—²¡–S~”5™ ¦ü©™”-µ‚r¦ì¨•Ô"jµŠ  <(j5H- \)–Ò¡t)}JDP†”eL™PÊ”2£ÆPc)Kj9J›šKí¢:DcEÁ¢»£LF¥Ž*Õ¥e¯µOë•ö íJí‰'Š—‰Ñúô,ú3š9Á±¾l2û\ÇTg…ŽJ'_§Iç•®“î:Ýzcõþ¡—®÷P–þý"ý>Æ`†³A˜AªÁ.C±áýFF £âÑö£—B•áp*0æýJEü~°áZ°i|x8IŒÝi\ðW’èV Ý[À˜ÃËè—P+6äÛ°rhx4™%åk8¬­‡õøqbìHC˜úŽ8—ÙÇ¬Š Ù±3ï°J:)ÛW[Œ.¢†üÓÇ>/*./¿iÃ’1†< 2Þô’KM:Aá 7—4w’Õ5䱟‘ôßknèé8¿n¡?#=ÿa@ϯ3!)‹£¥’æobI:ˆáW‹.€\‹/¹lãô-¶D,vvaï‰0Ì| \¡ [Ð*/eJ0b=#î½¹v¬¥¸ZVt¾ª¨ÝBgRŠæêûb°£ãï‹Õ÷Án8 g0ØTÝÇðýbœþWy+ ư”|ÜÀØä ØÁ)˜ˆ a’¹äÍ3xL‡n5Õ4Õœ½‹n£/cn¹µ|yùlº‹®¦µE]ЍYU4—Øý vؘZÂ÷Ö$¯ˆKö–akƒ-yó v]@`ì:ö¡ ˆï¸H Ê'(@;™ÃnÖX„½°÷ ¦À\‡@óaáä?ð\YsýW½Ç[¯ôš÷ñª¾¡¡k}eš…tÊø© 3KMΑÕv'²…·59qXŽ è„ö5¾ÄDÎÆ.§æ_Z)“´®¬éRtYv£+ÛX¬Ëp_\ñždêãåÚûëo-}}2Ãá©#¸Hì´x(ç`3.Æsh,Ã_c[øZŒgÓ°GMúÊÁŽÑ°S˜ÐC`¼,Ì9b¦ÁÝN½‹Æ ü.B7u>OÀ=Lø|05‘Ãÿà3¸=Ûê냾š^EÄÞ(lŠmð¼ $؆0Í ÞÀì£2lMç8.YáH†°>/À œ¿øt¯7ªÎÊŠ2da 0$?áéí÷¨4Cˆ@}ù_¹¤ç"}aq‹²±`ö3‚ëÃã¯Ê$ý×â}/ø[ú ÐdÅbt•#°„x{{…ö ¶ôXÿ€ `ô‡ˆ/ƒ .¯Fù¨}çÕmµñ¯fÞÑX?u ÖÆ~xþ‹ñà¯zßVɰ”Þ“¶ E£õř緕î9½ÿ{`€;úäbý]lÕ›J•…™ŸdŒ#œ.ISþ0Á§Žf-€i1¬â¬‹"À×ß+Î ‘m°¨fÒ}ïNŸ'ë^£×èÉg=ÝŸ¿ECè¸ßýûöºWO@l6åÀè<Ûº[c}l0{åðh d#ÀocQ?9µ[d£~3¨¥A õU¶Þ¹ÝSõ+1íÄ_—ö†Üñ®ÀÚÄ;òÞN=È‘i– ˆoÎÁÎxfÐ,–Â3"N/_‹~xÝÅWr®môÙ‚O+ Šr÷Hß0©ò²ë¸6|šÌÿcLJj_ó¾O Ý`'‰D%1æ!1æ˜ËPÈÁb,ž½„É`íúO!äÓ/Ïi“±ræQM¼ùq ³5Œý;šEß óUÐÍÁï`Œ7ތէÿJâȯŒâXbìX¨™¹¤æƒ’Õ1’Þ'õõONËÝW"… Ìμ=h/Zˆ‚"“¾ûNºÿWô5ºUTW]W}ú*jC} ÍžeØëÚ˜˜‚ì#è{¾º´U*ìFŸmÈ a?d"V¢nbÍ B‘îá$ŽX*$»ÍMçOïØX$-PÚŠXüT“BP·ª%®*±r퉵È­VÆ,a¡tDm Œ`°Œ(÷#—"˜‚MÁÖ\ÂÃ&hà`}ÿljàn”»M–›…Thò9´±$ç3tzaÎ T½Öï=æX÷¤ºþúÝÎlK¸´îBXQbÕ–#›¡MÖ÷cÑɽ'·°’7'·MSX „lå¦-›·fî‰@ï‚U^yĘT05i;sIÖp(ÿ‡› Þîôި촩¬¤9<~yêRKl0ó-1xzÿK\oÚ’ü™¬Lu|Sa, ãiÉ$†/±'9”8<OÅ™8&agXýªëâ“›2IÖI{ñ4‰4Þw@ôt${TrsÁŠÈ½•·c/þ=«9ò§ªŠƒG*¤¯˜­ûwæf#6.çx‹ ® Uäö–Þ#ÌéfGkTQ ==œ¤M!!=5ô‰¾é‹ša"´ÃD-þ5ˆCÝ[šâëBÚÝÏeÁ ¬Ýð,¾µ&+e¿gþ–U£DKÆÅM³ZÖøÍ)Ñê£xÚ³ ðz h='!Ì,íÀ–•2LŸ’—%–¡¶±7Z/ö¿l‹žX:r¤ÅOF’Hƒ {‡à;§I$ö¸×C%¶Î¾c4PÏ^pG£óšz$‹¯5ãkˆ?2’æ…k#}'Uß—ÂoÄG}ëuùó õLGT’µáä#‚l6ãøA$|RÕƒB € i­Sˆ3áëÈþBliÁò5b<™Æ"µ ëð2¡ KÕ5â÷ƒ`n©èŽw(<åø‰$ ¤÷bnþ8œ#W¯†ïaDˆ±ÓH~4åK5BX%X–G6Vç0Nk#Ö6<—ò9$¯æ0ÈíÔÒÖð¶ÐþÔçè9ê¯lkim:u=aù"%#òÃfà hE»© ÝyC1– -g¡eEÃb(L¢ŸñÆÕÆâÐCpXc툘ˆ§±ÉYbþb'à]VÌ¥•ï:Î²ÐÆÀ¨y7±5¶þ8çHÁ’οY~²±ígTñªœÍ[vʲ²òÞ”=F®Z¹Ù¡°ÂØŠë«®dµXv •­×XIRÌ.ÜÌÂÏ…$NÒìæ¹&¹¢®áTéíiËÑšüOû¡NáÇ›ÁA¡¶§±“ú2žÅ_ÚKjâ¿Ï  ‚O(ꘙØÅ \„xâ2ì‹+Ä0‡þ æüˆç-Ã? >Ô»Äßy#sIkÇÿ©yw]ªë¸r.%BŠÕšžÿyæM˜7 ïbÊ'tCP¤4õZð9_€¢3–³’Önæÿqw>qwÞ)#I­+.\Ië±î˃_Å_—ŵù]ðC¾(4MÌBºø*×Ûêecâåí¹¦g蕦ZùÓsœád3X+€ã$p´‰”|M¾ŠÕXÌ7 ý%\,iÐã»ÀPÝ%ax5©«?b¬NMÊfØ ª N—VŸ)ü ÝbAD$å™0Â… žŠ¥; ìØ‚¥@žÙßèñ¥m6¬/ý"Ø÷³ ñ˜ßRCAŠÉ3kC¿ÅQHÁm¶V`z<&³øÇÙeà z"r¿ÑÓâÏC‡ýÁëaò§GZþ$Ã!pÉf©èXsØúÙ>ù¤$,ÙÎ1õ‡Ï?SVWõi ú†…q‚ –"¸/9\-¡B÷`-‚uÂl;lý>›êóÇIé.Û1B|T'p#ÿõK¢–gMCxÂÆÅ3> nðí}ŠúQgUSG]{éOÆ 0Ûü\q#²Ç»Æ•¨§Xû:—Y÷iÒ‘¹$ æìœ­Ü™”â²dÓ¥­çw~^ §G;WÔxþtI5' üApËvÏá·rê­¤Ø_íÈltñˆÀ†¤´.YBU§š”&\´Ž ·Á6údí©Ò×Áè‡KíègâºÃsl‹mœgb»ÝhgÞ)X”ÓßÖ\¾w·6ÂsnZ"ž†µ¤Xß-nÑ.lÎòï €”°†ˆ¼9ÖÑ}\+:š{z7+¸²í|ÔJ‹€ðµ~ñµöJñGtž00H±ö¿|Ò§ñí³?—I»êÏ4ß·]§¯ñ8wåÊh©2a[(ZÄn3&¯ö`Iþɪê’zÄÞ¬V,ñŒŠw•moÅiÐzC¤›ÔuÄÁ­Ã«9µ½H-ƒ }âbuI#b¿h\åì¾Ê+PQukŸP‚À£_¸¦]yýš”ÁVÓÃæk¶'FÉÊa².¿“õaÝ aɽ$¼¥#ÂfaN2)Í1 Û ‰ºÒ5˜áì;s¶¾„@Ê)‚ K5 È/Áfˆwà͸ӅÇóc%¿œ;SÜxÙ´ìcs<ÆÞkûœ¸§J†ÜÂÁ.ØbÈlaòÐX|§hwk’b ØÈI~¹Û»<8<&0 üóíµ²ýø'êm ›ï¾ÖÇ'¢¹ï˦ËÝš*¬!K`¯XHZES˜/Ö~Oæôr¾FÈK¡å4èêÂx=Ð=ª¯ãéPÔÞµ" endstream endobj 161 0 obj 4021 endobj 162 0 obj <>stream xœµXw\×öŸu™"R”u@@w,(½XPA¥¥)HéHPÀˆ±ÅDGcb4c"‰ Ø"ˆ••]K¢‰æÙÜõåwgÉK^Þûýþü}vwöιwî9ç{Î÷{GDè !D"‘®_búêÄ¼Ôø8áÌ–7ñ£‡ðcÄ«Pæo¨ É1DÀþ§úœž˜ÓÓ94ÚL߈ß:: ¡`8!‰ vìóÊÊ.ÌIMNɳ° ^<ÉÆÆö¯'777‹å…ÿºbᘛšœi17V'¦geg$fæÍ´ðÂw§§§Æ[$§f§äZÄ%$$&ÃÂãÒWXÌKMOÍÎÎZmaí5ÉÂÙÑÑÉœ¤f,_•k—™k`œ˜¼*=.çoADÌ-ÌŒ_è¹&+!bWvbd÷ʤ…>9É‹æå¦û楆ø­J ¿zE˜~z\øËﲞdca;ÖÎÒÞÊÁq¼Óç/=\\çLÙ=5jÚô·ç±3fΚ¸Ö ìK"’"¼ 7ž°"– bá@Œ#󈙄#1ž&| 'bBøÎÄD"”˜O¸ÖDáO¸“ˆpâb 1™XLS b HxÓ["‚X@xÓ †Jèz„ˆÐ'†„˜0$tˆáÄ‚"Œš©„ŒI°ÄX˜È'LwbaJÌ&̈6baNŒ&Æ)D4!%–â˜ãÁR¼ŒoˆÑDQœè«!‡ ùbȱLì/®Ô¦“¦³SçiM~D^ ÿIM ’)5½œÞBÿÌ81¥CG š1T­k¥£»W÷¬î-ÝŸ†Y[>¬dØÃTzôŽèÉõ õÓô/éÿf0Æ Ô€3¨28cÐeð»áC/ÃPÃzÃûÃm†—¿="mD«‘‘‘“QµÑSIíHvdÔÈÖ‘JÖýŒ­2¶0Þe|ďɸ“á&ö&ž&1PaÀ7q 5]"êQ‰a®:ƒµŸÁG(†2P;p¥)çKD|&d±÷Qù–B»Õ$Ò£Pñû ò'ª ²HøR΢µèÁcÒ€ÿe*Ô#ñ˜Q|)‹Ækl‘oK:Sà£9´búº¤‚¢Îô#®hGáNf½ãþOre\ùîƒß=°ïà7g¡Hm0JXV)¿¨3¥Ä¨£Ëã|Ök"9Ó'”|¯)}giIç?.w “Kò§ç—{î‘i¡öÆ™óº,l ‘˜B Q5 ‚j‘lA€¾Òè’€ÛQp–¢+$ÐØ…÷GÞWò»”b>Åâx 5 Tg –F)ï QºÚDìû %Äk€§€ X«0êVA·jÊDÂwƒ‚jç.¨>ËHÞª*i¼bÆ)ã›ìO1¾µâä¥{f\S~mâ‰Ä“ßø`ë)ºÃÂZð£¹òÍG ŽæÁÅs Eé+ós 27/Æ7ù¡',ì„|º†;¾þ`.žù`ÞÞÉf\ÊG+Vå­ÊÍXÃ1Ø—\'oÐ)âƒp4æžPC¡V9‡÷èDrnñú8îMƒ?ÕM}b8ŸbÑDgK伕–0 &¼ƒ90ßõ-²‘m÷`Ÿ_ö@ÆÈp‘»ƒ}pHÀèŠ\%&‘‡–òrð(1:Ú8ìkùM–ÓÑa*¥)ä˜`Úhg$B3e’zäÓ‡D0önCÙí©dí¼{4ZÃDZWf¢ÂeýE3]<0¸þð©ôÏ@B­JÌ›@5 e|6˜ 8€iB`i Nhp_µŠ6P' Þþ@ÉK•bµ^†‹FEiFð*r‚1%Åëk”ä;è¤<ÿJ!ªS‰Õ“yŽÍW9rå&q³ÒÂæ{%[qH—Cz•ãÚ½//º›ý‚ƒyÜë—!Œ±¡6ElH]›™˜êŽ­ï øCh{­¹0븬,g_úWaŒÖ=üd9NŠKBR\ì3‘| e²à(u>¬>·cÀ¼D€Ó ÞS`’y¯LÀ9ÑíAKnÚöYË 4 [0ÃÑeQ‚áÕ¥Öó]à×®]¢3OÄüXÇr=×­­N}2³q^Ö;œ2sМ§ca"èõ¶UŠS&o^T²ÎÅÎ:Ÿ|óñmÌŽ6v׫k7qŒâ¦ï”­ÜÖm[ñôÀ)ø /ÇNÔtãí)¿æéÆ7*4þ4ïÇ·`àtb•Šß‡dª.dQ"²Dö¨þ‡$ewYÃ5Y÷Z 9ÆÀGHv(Mê¦Î?#=€¼-ñŸ=r°ÂåÈüÞâ?{™JíóûUüh•¸ßNP$H!V#¢@:A©Þ›³ü ˜Cø“Ñ0{'û7¿ÿÉ.~v»¤F½f®-éjNXx|ž9’:!W'– íl8z¥Z†*h4A=“íLj4BzÂQÿºâ™Ì*pómóN‘²W _áìrUP'K¾­Úõ·í€´.ع~ûŽñˆŽ#söñíÐDõòQ}ô`i>£€Ý ÑSô¿ccn°PH]ájöWŸ<ú@-w‡13ºÐ8)ºô>CEñæ:°Âéþ¦¥3g†/u– Ö™ÏP¤ àâÿºõÕ6Ð+85ë¦Åéï/öšÑ¬vd%EýZ`Zè` Z…Yêø¦CùGV§q±Ì”Øk©6L¥P­€2…Ñy¿Né@Á³,ºâ9ù®ñÆ6vÈt®_Yí2éÒÆÌ>n€ߟAìAÜlW"}A­k¤óÍäœ9¯¼à¨i>KÉ!åÁä?uWvÿ€éãÐdòF{E^'?¹Môgä.ŒÐ)0n:çíûƒÆ‚ ?•q»¤dÇçǤôúO×mûc’7ì©”zB@öm~ –ˆî౓ñØéšÒ^J¡3Íò‘ §0æÞÈê”óUð£ÒW%”¿—ÝV“zÑ,mWk74ʹ6äeŽ æ/%%ýÏrÏ&›q «ãÓWeæG¯_ÈÍ⤜ÍþÛkpà·ûÇ–Ç7ø>J1×ÇýPR{ª¾ºâ6w“iµ.CÔ’t¹^qì²Ù½Ûõ?s;Üv»t0Ž–u%¢Þç¼..¶u™è±JaÚ4à ËÀçðXÈݼÎÁ.£Û½°—¾~µ1_:5Ð’‹×ùq§oJyzºÆšv½òsW}Y{½TRà)8eÞSÖz¤CM¯èÅkÖ,ù”㶯‘zÓ{¶~¾­˜cž×Wý SëÓ§3ݲ—Ò&šRˆàkœ2ÿP›³°I¡iwƒÍïÍU”¶j ¬üú¹Â1|íðJ…$Òz_UÆÍ—’v²Ö¸# Þ7!@SJþëa9Wq€Æ ªBÀþ2êaø Jò k€—˜{ç“ÊA9¤–×ÕÒÑc•ÖKõ{í¦'y¡¡a±ÅGR¤ÉÇÖTrÍ ¿Û÷#ë_`.̆!Ïa‚”ß­•¡rµe‰hÿ X‰y"ŒßCs‹>N[“S˜•º&‰c|jËjm? ‘®|ŒIw7IùóƒÙ&ØÄ_4{­MVÛqXL§&jl-pÃ[lÚsê ïð«ÆÔ‚PpNÛ#1Øâ‡M{Ÿ¡‡ƒØáÓÉ;víƒbm¿«Çôµuã'ÜóÌ÷•ÁuZåÛ€ØÙ«R¤¹Ù2¶.fQ»nU•Ë9æþÙ¬Ù*šKY½vþ&¤»¶ð“ëä¤Gq¾Œ¤À¶5è×Ö¦C—®I¿?š{‰ÛÏï(Û…ø²\Ö¦59y©éË?ŒäÿÄŠ¦æÊcýÅ2åÞow+fþRÖ£á ¼¯°¦PŒæ=Jãß“)؇wÞÒÝ7¾daþì´ö 5ñÞÐ ÚýÈï’äIŒ[ýjà0 Þð…€#ŒŒ ˜´¥À íA¨„|A)Dƒ Š&_ ªõ?ö [øšHêµ°ÅT§ %4_o»q2ÑOŠÞ ÂéõS ó…S~ ý<¬q’OÜê…‘ÒôËq‡}9.zeL#©¿Gÿ]Ø^ê…F,ÝÖ^ÒßûwhIý‚ÓM™æ ÀšÄ<¦¼CR¯%Ù dPNÃTʪU[àßUÛoÞƒÎQ¯5†@ì?+ ™iúÑ ¾ûMÅÍ1¸iEAvåÏLã_Â,ÍKŒ“•êˆÑ-•ø^ƒU½}QQvÊ&‘nùüû \ó§ïá–·bXL?‡œ8ä|ù¿Æõ³™r«H˜Å+.õà®KæaàõP¬B)ìk0¹ þ8qàþÁ„ ¥”Ȫ×;,‘K$rwAxÿpC),èÁž1_%,Їxâ}:Ÿ ¹¿hbU‰¨ï¸ï-kÕRHsóÖåGmÅkï8YóDzôŸ%†ã4HÐîžRQ.YM„\Ö™fõH6°ù®*á6‹6zÁFÜjŸQ£êŽJܨŽ`‘˜.CfxT˜‘ ¦ïÿg𨷢¾·bÁ‹±z+äZ~)·gÀ.T²T ZB¢Ô)XBΦ’…}_ ¨täI"Ê÷ •ˆ’ÉT5$“0üßO†¶YRàIþÚfO9û²ÚûGPßáüàüRõlc!¶mx¿l¦`ƒzƒv'ÖÉËå¢sP9 †cØù\PfRdt`ž­–ø6õ ØÂdDE ]õ,ê–4©aá1?Žq×yQkƒ%:Ún’}Ôáµ/^`¤ét@tGiÄtì,•·Cl;X G1,UÏeí=<ììº<ž«ººž½šóÀFæ£s¥&&84:&4$úBóå 5Í2Þþ9«‘a$¶R¼ £µXc"Œ³Æ=“w={ù׸0í¸šËÍÂ8­lQ;b[÷*Ű—ßÁjv(Õ‹èt$Ê_‹F¯aI¬Í°v5>úTs»ö˜H’ø ÐÇnOþhi~ 2…i£Þ^;qŽ»Ë<ó½m+•dÚΞçu ñ†»Lr ÷JŒp4CzŠY`X_[|è”´´øÛ}U•Œ$ó†; " €Må•ÒV®ÅyÅ.èØ¸Ÿ;É4ž8ÑØP™““éŽE’)ªe%ç†,Ë[lîµðjÇåŠÓO¯j-èãÏv‰x·1ÿY«Jí¾ú]eßÅ×—Fýtéb'æîk¹ÍqUqU‹¿õãœ8¿ A9‰ëc·z1J곺/Ê÷:tîÂÑ&Žéi ž’¶4 Yæ°YO[æ· 9šòéšëjbT·ñÏDÒcù•ì[ZÒ´”Úveß±Ÿ3UtЖ´(n3–8ÂNã·Kéÿ¾‚Ó?…“OSñä,š—¢é)š"4…mƒC÷am—Q­Šæ ˜HxX rö+ªr;)y÷ª!vªsh¸ƒMTÝ/˰0 Þ“´Å)ߎ”WXœNxÞf) S˱Nl=ú}Ãf`0£™#«ÙîÈæS©’Ú~nçᯖTŸ?ÜÌ1òÚè9… Êr7¤¸Ñ"&uó = =àÁ"%x¼ë¶ä—ùÓè¾&˜´§¡±›E9äÀMÒà7YvA‡ÿ-x| ,»ŒN<†ñü.\‚©íø­ìªù9W⎇b%o† Ñ(4^&i´ê˜¥j:w°á‚TòhÉ7Íïq×Ë/¶4ߪxÈɈEaàŽŠÐ0d9ÓE:É9™is+bÐ<øâ^Ͼ²»Ò¬;nyÁÃðÌ)hÞ7‡@’ / ·¦ 4 ŽÈ©Ùy%‡wïüöËC²øžDEËØËÜ™¸OöFdOËIb$˳’r“ÌCc§ëçGƒ;R蓃CŸˆŸ‰!•“\3„ ד©3õ'öcï½hñ·¿0ÐÆ#ªºg5ŽÅÞE{²JWž»›Þƒcaõú-L û—htDüºŒå²cIB€#\êU1çÛðÏDò¸ÏmEK×QÿÍ’â;Æÿ;}hWx]Þ[úD.:ßçð"·`*]ÌìvDpîœOb\@ì¼|L,‡†};ûl@Uðt¹ð ãÕkÁ('’ø„f/N”m‡¹—_¾ánpç¾öe0Na7-´uòŸáØöTÙr½OögA¬Åõ0—BmEÜñüˆ½´_ðP¦+/¡;úx²GEÌs\fóûa1¸BêàëøîäÊ*ü`>˜H~áÍù¡¬äUçùÃ§î˜ØUŽ•Äw$ž8ùnŒTòËŒ„ø…îfÈøG'°‹€íŽ¿æV-E |…Þ¯‹7?2vîÜÈówÚêÏß“¡É/Ê–éÓ‚Bœ¦]éím¹,¼‚IÈ"â±Ó 'Û)Ї&0@M¸‰øáä_°nt‰ªŸ6ÁTv?bÌï¨:s³¦½¤–ƒa…ân&\ ¬^*™OžˆdhÔ€=Œ¼S¸ù‚lšb3 ä–Uµ10’Oc7|\N™~íÅ@Ëí‡ÿþRæ—ü¼B4ïÏÚ k²…‚I`-ˆ¼Dü}d„÷ ü+­Ü íoE0“lÚ Éò¥:Z®ýC ðÓ…ËïİL¸`‰ÚÿeÀ?¬H–/Bñd7­‘Ât^ S„¦Ðc!Bêž‹ëþ"ÿÓQ}± l^·ˆ_c„ýsnËos…ù@Üñs‡K–_YM_@×ü7È;Ä— »€C”\·w˜üs=½Ç»õô â~±þ endstream endobj 163 0 obj 5651 endobj 124 0 obj <> endobj 164 0 obj <> endobj 62 0 obj <> endobj 28 0 obj <> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 123 0 obj <> endobj 61 0 obj <> endobj 27 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 165 0000000000 65535 f 0000107770 00000 n 0000138196 00000 n 0000107509 00000 n 0000103097 00000 n 0000000015 00000 n 0000003234 00000 n 0000107818 00000 n 0000137182 00000 n 0000134514 00000 n 0000137591 00000 n 0000135020 00000 n 0000107859 00000 n 0000107889 00000 n 0000103257 00000 n 0000003254 00000 n 0000006816 00000 n 0000107930 00000 n 0000107960 00000 n 0000103419 00000 n 0000006837 00000 n 0000012163 00000 n 0000107992 00000 n 0000108022 00000 n 0000103581 00000 n 0000012184 00000 n 0000017374 00000 n 0000136203 00000 n 0000133542 00000 n 0000136792 00000 n 0000134053 00000 n 0000108063 00000 n 0000108093 00000 n 0000103743 00000 n 0000017395 00000 n 0000022114 00000 n 0000108147 00000 n 0000108177 00000 n 0000103905 00000 n 0000022135 00000 n 0000027153 00000 n 0000108240 00000 n 0000108270 00000 n 0000104067 00000 n 0000027174 00000 n 0000031470 00000 n 0000108333 00000 n 0000108363 00000 n 0000104229 00000 n 0000031491 00000 n 0000034586 00000 n 0000108417 00000 n 0000108447 00000 n 0000104391 00000 n 0000034607 00000 n 0000038908 00000 n 0000108501 00000 n 0000108531 00000 n 0000104553 00000 n 0000038929 00000 n 0000043237 00000 n 0000135978 00000 n 0000133358 00000 n 0000108574 00000 n 0000108604 00000 n 0000104715 00000 n 0000043258 00000 n 0000047984 00000 n 0000108678 00000 n 0000108708 00000 n 0000104877 00000 n 0000048005 00000 n 0000051642 00000 n 0000108771 00000 n 0000108801 00000 n 0000105039 00000 n 0000051663 00000 n 0000055366 00000 n 0000108855 00000 n 0000108885 00000 n 0000105201 00000 n 0000055387 00000 n 0000059191 00000 n 0000108939 00000 n 0000108969 00000 n 0000105363 00000 n 0000059212 00000 n 0000063570 00000 n 0000109023 00000 n 0000109053 00000 n 0000105525 00000 n 0000063591 00000 n 0000066906 00000 n 0000109116 00000 n 0000109146 00000 n 0000105687 00000 n 0000066927 00000 n 0000072170 00000 n 0000109209 00000 n 0000109239 00000 n 0000105849 00000 n 0000072191 00000 n 0000074877 00000 n 0000109293 00000 n 0000109324 00000 n 0000106015 00000 n 0000074899 00000 n 0000075840 00000 n 0000109377 00000 n 0000109408 00000 n 0000106181 00000 n 0000075861 00000 n 0000079055 00000 n 0000109452 00000 n 0000109483 00000 n 0000106347 00000 n 0000079077 00000 n 0000082102 00000 n 0000109525 00000 n 0000109556 00000 n 0000106513 00000 n 0000082124 00000 n 0000085439 00000 n 0000135760 00000 n 0000132814 00000 n 0000109589 00000 n 0000109620 00000 n 0000106679 00000 n 0000085461 00000 n 0000089045 00000 n 0000109666 00000 n 0000109697 00000 n 0000106845 00000 n 0000089067 00000 n 0000092042 00000 n 0000109743 00000 n 0000109774 00000 n 0000107011 00000 n 0000092064 00000 n 0000095053 00000 n 0000109807 00000 n 0000109838 00000 n 0000107177 00000 n 0000095075 00000 n 0000099463 00000 n 0000109871 00000 n 0000109902 00000 n 0000107343 00000 n 0000099485 00000 n 0000103075 00000 n 0000109944 00000 n 0000109975 00000 n 0000110008 00000 n 0000110525 00000 n 0000110546 00000 n 0000111177 00000 n 0000111198 00000 n 0000118390 00000 n 0000118412 00000 n 0000122900 00000 n 0000122922 00000 n 0000127031 00000 n 0000127053 00000 n 0000132792 00000 n 0000133268 00000 n trailer << /Size 165 /Root 1 0 R /Info 2 0 R /ID [(øB¼ã +-Içê„&)(øB¼ã +-Içê„&)] >> startxref 138397 %%EOF simh-3.8.1/DOCS/simh_vmio.pdf0000644000175000017500000017464611143601720014073 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœí\Ù’Õ¸¾?Oq®Ÿ)Ž‘d[¶ÈR•3CjrÁLQM7t »éH¥òy•¼^~Ù–ôIþ}l˜‹© P”ÚÖòëß7÷ûµÈ¥Z û× ŽÞ­n?ª×'W«öñúÑ7ýàòdõ~Õä…ýÓ>ÀñÑ»õ_h¡YË2åúàÕJäÆ4”í{¹n ýlªu-êv»ճì/›-Í*L]eÇv¨¥®Ë2¿nÇ¥® Áó¯7Û2¯Œ%íXçZUM^oŠÜÔ…2Ùƒ0õ¶øãf+s¥U­q«{•ëF(•½„­>l¤Ì#´ƒ¬tØÑf[åt¬ÑdØí€N¢PEvN ºq¯¥hz¸UåÖ41Ùã=¥¨îoO€•–uö­±»aY”ÙSXä'd‘]¶Ïk!t•]ûÙ7˜Ãn‹éì-¬[sÂ%û»×2;õ›áÁHˆ9VmüËÁßZ–!ÎÐcj•pŒ6%ݲã˜'›­ÊUSÓi‘×R•5ñ‹úÑ5ÑTÒ¸4th<±P¢¶×rïEx*»©w¶µ×BÜëmʪ߫K÷®í¢Ì…ÍšöâE‹Da1§E‘½ Ÿ‡¹—a˜ûØ2 ]¿$òû‡ßÓŠæÕ¿ €Áª§~UÈñ {I‰¸±”¡é•Z|·¢Ÿ¿:øêñðVjš¤‘Úáôõ¦´ëL™]õÑVŒÄR52{G ’F¨‹špGë†ÖÀÃs<Ö_á‚Å Ìý°¡‡$zœð d²PáÖüÙpÊK„È]£ãœR7øú:ìzνõ£5Kô³v(”Æ÷‡áý1“ý-êÏÂÃÀ5·ÃÔ&á¬êˆ×xì}èhª ç…‡/[V!éÏ…©IjŽIHá6WéúªTž•$ýãzë÷?dQÌS €ËðËø §aÂKVb‡, 3çaƒ›˜‡0²¨…]DØëF•ḛP 3`ím‰û¥‚œ! x°UMºÝÔ–Ú[Gîm¯",ÍAøo¼n¸`Fç~ÄßhpÌ’î]± ©4uöϬsY*ä¸)ìCUÕÖhnÉG!¼RŠíŒ‚4¡×y¥Wys¼](½m’`èòιè$¿®ò¦ª{sÞz;w½á~‚·€öõ,òŒßÁN~à™Ûáá4³¢ª?ӥѢr“+m’sýðÌÃuÜ<½C?ùˆ /Èñ*qúçܲ«´j‰¡¤õp‰P´æàÁºYÃ&=Oæg¤cÄöÒŠ6²²ÆÚÀ—,£N¼ìôCÿ0ÒÓž}A#¡àôÎ+ÖjXaw>öœ½Ûˆ  ‘~½±kruœ‚)‡ªÛº%ªŽjaŽ,£W° ï °Vˆ5oìðL'OÇŸ3+ÃtgÕì i-ŒÌ~Þ°×B±Öo+ŽF¿¬u‚´õ˜mžØõº7Þ‚TXéŒ÷)K+À÷ ‹ºSÖt.1³Ýµõ§—Ü‘ÛÊ[ư0@Ý CX~¯·M™Ý÷¤}êG`Wîr3a§¯HáÙ 9t„øÁd­Ÿ³n_=M&.E„µÛ“ÄîÔiJÝCÕckÞÑ­ùO°îÞ³3'ÑÝÛ²c·=cU>¼ÃUÜû?Ò{2mU§ð`:D °4‡›ÐÚSÿôÏ~¿çM¤±SKrZ”É•,ª!¡¼.ìŸòxâÝgžÁ#¿iBÏÁ¾ç,R_÷úBå’âñ^_°ºm@”V¬ÇtN„£0hÂ#Œ´ DZl[µ­MC»5‹ÄÚ_ô˜e>Ô„D eœGì\˜ÀG¬ÖVÙÇ¥*ñ8k—Jë%* ³#cÝ ÔCGnTµ>êC8A šÚd7†|‰B¨yá'H$¸6Cà"ìù¼p©ÞŠÖ¦Ô•€Ñn}§=ªläO0–Ò»¨R‚p¾'á¥È+*|ò£k? o/ýèÌÖ~tÏîûÑS?zàGw™ykfçpîG?zîGÇ̼ÝH›¡p˜‰èFe]xï]7’|‡›;cRÕ¸—_æÊ_ùÑ•øßõ#¥çÝ>ZÌ]>òÌ~aÀúóì_˜ öµ Ø†<Òß1'_øÑÍ2:ÒèVz^¡­Rµª ì4AJ¡-Îí¤.ÞŠÀMêMHü 0à$ ׊ø/º€.}BË!ù¿Ï?ú޲ý;å|<9Ä­2äZ£Øt~¼v̼^¢@'Œ(ï=-rÚÆ²L#†¬hd[]*6ÕêМÚ,oMù°iIÿIp7~Cp5€¼W#Ž_ët)šTù ë#ôbœƒòM—×UM”úzìßÃQaì4v>ç“-nÌÀ²à¾bßó¸'“üæWEÀÀ8ŠAŽà¡ícÇA¢~$¸¢Nù9*_É'›Ê¹&Åæšv§ïETô¹_4…”ª‰GX ód‰ˆñ‚ýËAm£Ú‡lß’øIyj›±U‡ää[zÅÞfHGG9Ž8ëЗ—¼ÛÇ«ºTÁ‚/Oˆô°±:G²—U–×ìªô¬S–èlŒç“,3òL”¼|Žé˜ñŒQJ>«Eáv© §0wCv…Ý„>'5àÛJB…pñTZó¤E¼v]V¬ãã”=òŸO†]ŶV;ÂÁŸØZàó£ãùŠ6z÷€7GI Ûs²ƒbpÜNP·/:ɽÃd7¯&¸Û%·e‚ùØÎò©ŠQeàòU§^…Œ¥ÚÆõDW÷ qW’2‡¨²5N§[Gå!—“á™ì‚e">¹rå=-QèPËžLÂ3îeYøqñÙ‘6šÑ¼0»èà}Ô ý#˜•¡ý>¡n?æÅÏÏ} Dæï7Y)IËòÂß„ÃNSh ]êlü7K{T¡¥óÛž¥Oüë3? ©® J&=„¹ ‚SÑü޼£lq1²«– ;ÔDÃ4¢®‡±êŽl3I‹ñÉ3äò–ƒHmä†CvºBg¡SMn”tê!ª‹HD“&¿Ç2ñì1kÂÍåÑñ‰Õ°Œí¶$òUѱ"WwwÄzŒdó͘bÙ‰õ"N°  @Mƒ–rÊ€ü @ù "|ˆ×ÑWˆgs?EYØt´ŽéÁqhÛecz°ª´èþÁê§•\¿^IQ‰µ2º^µíyÐëÊþ/ÌúòåêÕ¬ÖÝ‘ YË\¯I½äMß·kcÔ‚\«Zá]ï²@óÎË _.1N{Kö‚ŒCN¥‹/ŠœBHÛœ°Ó±y›*áø$äöyÐ~ù‚eðÉŠYƒ;`:îB?ÔxZ“ž" `æ³³­Up5&Ä0ªZ“.G0¢çƒ «êiÈ'jFEWaW)Ñ$‚m·j>ä°ªølÈÑåÈѣˤÖUN‘š+5Zi›‘ÂC¾;…Çbð¨áÆ8^p'¼å*Ò|À‚qG[­eèÿ„§Ð*ò$ Cε­1C‚|ȃßQ -º”U._+ñF>5¼u3|Y¦Ó4£ÉÆ"æ‚>Ù²gîþÒ`€>HN< ­”i’jY¤I½:áÒ4ÈGDw&tCb1[Þ)‡¸s¯?$©»mÌ÷W àKÐv”ô´]°áèˆذàèóE燒ø/‰ô#çÏE±h­©ž¨Íál 6Ÿ¶_’>}á}þB9 ä)à|¸ ½õÿ/XêÌ(]p± ?„ì|ç4¿×¤êÜE–€;#=7§ˆ|Ó Ù¬þ·Žºóm»etis^l»:ß$‹²ÏèÍ=³«ã”ÚWÇŽ9³óoz^&&¿Ùásq0ׇͽÊÕhªÜú,Uþ¹­³6(ó¦ð•…™Ýx¦råº6%ɲ_ÅÊòXv¢)nRÂ÷Ì%r °)@þ(^}G×åê(ûz•ãHj5t½Å8Ír±ªèHº"(ˆPT NÂÀGI¾MDï„S—“¹q>å7àó$I;dûƒ5äÎû²®Ïè%×HjÓ=ü<'.¹ÍN5wóJf²ô5™ƒ~L=æ.ºªc«Ê8ÝÀ•dzõ R¯—«TË/»ÑîÜØä¶“fÿ5ï“q>ù Ø+v‡%÷ìS™×ôÜ}%Ý"®'v´†n¸A8>‰±h µðß eÃ~†$"#Ö”ˆ„‰{º\Ÿ„€ÃwÃò¸»zß³ú¢p£-éö;@ІC³©eÔÎi‚‰ ÷du—ï{ ) ß_”„Hek†UŽ„4B2uÉ1C›ä¶MêŽ2ú—^8ëNðf 7nALsž·É*ô96¹(JÛONZ¶—›ÿnlco©œD}‘ýüþÞK’Ì“%ô%e¾å‚µœ|E%R¢ó=}¨]&V¿)¯(Z&¢É‰"Õo‡( ~»€_*øg‹ÁÇðë¥7º£Qÿ̶Ó=¾åØ|{Èÿû-æ·'qg¶iÖ6fÌ­|ßMÊ®,n"„' |{&)F> Œ¼ð}QÔ„»ýµeðï6‹OÐXÓçDÿe|M™Ïoð‰—½‚7,Fñ®YAXxßrg ãDvmÏÏÐöé‡\»óÑÈTó4(‰¨·²‹Fм1Šqf4ì{Yëfìþ\2(]Ô±T¨BæFÕÿ—ŠQ©/xô%ò²hibÙ¾ŒÉíµäÎ]˜ì\XNŒ\S° ÇÔ÷ö±EþT¾ÏΚPîÖ5®´Ü×B? z„}¸KꮢœþÎ'˜ ñÚKúPbÓúëÚ¸ýÐð›vOx£Á™ª%ŸÍpXœƒ3 hô…†ym§ÍO«ÿõÛs—endstream endobj 6 0 obj 3634 endobj 19 0 obj <> stream xœí\ér·þϧØ_Én¤c0·uTÙ“(qlÇ¡]©(*oÑ¡xˆ¤—ÿúò2y½3tóa1»¤¤(EIUêÂât÷×F3‘fr&ô_Cì½Üøìûzvt¹ÑϾÿÃ@¼:Ú¸ØhÒ\ÿé 8½÷röå–jØÎÚ´­f[‡"mÛFÔ}§Ù¬Qåm[ÎêL¤µªðrãÙ|k‘dUZg•œ¿X¨âLõüÀR¯I™ ‘ɖΕ—U%òùNW˜Kªv¥¬î•*SåE;³hÓºÉËr~«¾\$²HE×¼¯+ªð‚JYûD^.§ÇCü ÎyFXçìûNù8ºn^6ùüdQèÒ¶˜«UY]J^õˆŸá¸k¥×ò”žýýNѬJ™ñ 죩Û¸j˜/»ô5×Tápñ|ëO2—iÓ´J|¶ö•´ò)fRñЕ½êŠ”y;Oi€$¿HŠ´*ŠªaóÆ}±O¸ûÂ’q×~·+­²²p?×V8Œñ ¼G“Ä¢â¬sdfŒ¿;¿N3Y7šC‰a‘=Ÿv¬®ÌªŠ“¤Ë—TÈÈc+ÕlÒ$êxúlξö]óµ±ÒF£\A®:(`¤ºŠœ©Éײ™¿[È6•Y^*´së{UXèLk~L‹ô´üÏAœÕü‚Z^û@µ+4ÎUœ(›ª‡*Ðy¯ÌR}lYef01¦‰Ò¼¨pΛuõjmˆ}™v ØìÃºŽšØºçÐN\¢Ï½ò¥ÅgùdôUÕ1ÏCèh•z`Wê/y*ª¶(˜&Žl†'q§¶ Û™UÚ¦…jón¡T)+dk‘¡\Í´¬µk ¬60!9q@/±àQ¿ÆÂŽ%4}Ô˜‘;´¤ï­–J,Òꈢ8ßcë‘U$1Æ&3ª—è\ýSƒ 2ÜOLQèÉ©¶~êÔLaAAj[xjUe©hzU.‘VYU+ÁO• ´u^t+V—iSÖóïôïmÞÖåüÉB¦U#¤´…㤣k!ªrÞ°ÎúiNð—«¢IU¡šMÿƒN~½±õ;==£WL§3³°_)UGyÙοRd*›ºÎÔüŒ.ÿÐ6EéʉåÌi@èlݧ$=ŸQé·‹¤RîUY;ò÷„Fc̽P\¬…rÇÉöu ½RŽ¿ÙÉ_A±ºöôŠ!ÆuL¯ŒRþ¨ÿ~Û-4J“dÎÀµå¡#Ç<&¾¹©ÛÁº »ÿêàVµt¼)f®°K4Údxè¶| 8?óù£Ýša¤ÖÝ¡Œ%<ŸWpÿ¢HQÕjñ²ù3€w­m6ÿû"Ñ?%jµ…PK¼ia…ÔyS)‰ï€¯hS¥ßøþAû jþœFŽnHØ'Iú¤Ý± tØNª¶ l'÷G"¶Ñ3«`ÙÂ6–/Rcgû“it guå lïý¿&d}ù¤Fq+“ÃÏyUV¼=û~æS¾ hg¨V\÷Æd`6Þå,—uëÒd¿ìÏXÄåTåtCÛOö-¨SfÌMÆÂ¨”…Ü'ÛCañb²n„¥´“aQÇÌè~öç~ÕDéMά*û¦A9s‘qØï‡ÞÎFUtÉûº/Ø|9xŒ8ï/ÏR+ˆ¶Ã!Ý÷”™Ç v ©ŸsËe¸]ǵ‘üûÊŠ½]¸ßa¿cµÀ_Œ?ž­¿ƒ—Àïe†íÄÎëÄÀ‘³Ñ)¬ÌØzHŠçoqý¼‹FÄ۽ϗ:Ç=žï8+­?š5©È ]I›¢ÿÎÿ,2ex i…û¯¦þ°$¾"ø—Cƒ·ÌÛð{ذÕöË‚#ŸCÓ›ãmE›}ì=aÑ»à›TKbF²~Ç»ù½ÐÀ8Ð “[{Ž1ñ‚iZ[‚LcK:UR“ZŪ0#`u/ÒÚ§Óm+$ûûm@?’\?íh>ðàʇðáÄ š#¿Q϶ˆŽ5ðØîÆŸ­5ÿzÊv~,ˆÉF;5Ó —ļ¡hˆiB€s ¶ô¬’• xfxž|l@߇øöZ;ÛðÓÏ ±¶Õq0v•0ƒÛ cý)ü„•Y„ƒ ÿ[Üš`~ÑZ°º #Æh¤û qrŠÄ,cbô¦ëäLF±g&ûlq9ò"¦XŽ’¼Ò±ªÒ}:‘CŠçÂÓý¬Ìöô5:˜æB:ÔÚœ#t¦Çfïƒ:l{%ë!@ol‚»upPÃD}YðM¼p;0üÔ(âëDâh¡åêݲ<•mfðd•}iÔï;ëâýQO÷>朎O½“BÒ€ç 8~‚ªb—Ø‘ð®ƒ*k*2Sàt}M´¯¨Ñ(S› {YÈÊÜ'ß¹ƒ|U¢TƒõRÓ¤ya¥Æõ‹âí’-Ô*Ù qssîh²ÅRƒ< »‘ìÄ„#…aQ¾Á®Å ?$éI÷ÈãêÏðF=ÌæFGQð;{8åá›>b‘gDž‰Á:Ú ƒu´Y¬Y €_¹½ÂhCn#‹~§âŸ”Š[¤¥´Üñô“çé~;¥ °í‰ñ;ÒLð;:Ú2ü®=wõpEävl´8ðI'%y®3°[–™o¾Çœ ’Až†¸Û(|ª °Ä?«ârê!býL›pÚ4¿µÔ¥öGmO™g É¨ç¨âëc¨ša]Vá Tc¦»/`³ÕÔxÜÌ›ºŸ ë*-» ÙöÐ71e: “Îi°õ¾¶Ñ3KÙÀ)<ö¥«4$»ÇuÓ¤e”Ç÷¥®`äÊ»”Vœ}ˆå²lØþ(„´¯?ZïÕO¤äs9ÑNs2o“4Æ{e©mK½2~4à(ûç(éí ÷(ãf@MKäDþ‘È'wóù7°‡MXáK6òÓQ'‚OyÙ®ªÛv­±«ò†Ãëæ ÜYp¡4—NàñÆî¦ˆâ®Ôƒž¬»{[>º$y!ÓªÊ4”²Jú 6­T]àkà£D ïæ)‘ßÐÙ*Ë`e'aìòÝhS•¶ø‰/k¿‰Ž_RbúǃåÉ&}UzBÀ«égÌã««n†ýøÈçRÀÜ2x8 }ãK/ÔÊÁ?t†ÈNË–Ÿö ‘R»›Y2æñ‹Ñw0ŽàÁ¶1g±ïQº#s}zU³ã+œµ|xcƒ6ájþ4÷Æ@w2ü^n ×i«ÂƒÃ=ÿÆ€Vú't³”NV§¥yÚ¿zÝèAˆuÎs}M]%C|ÙUJ oRNM29¬Û&»È9þ=…+½{¸Ê•²–z½ú%x÷!”Îns4Þ{ê$~c`7‚ ¤p²[Ð=§RÛnqú[,{mBr=Jìof4Ù¸™ïØæŽ…ó6)à‰ç`ן-—.¤6Ñ· |ý^9A_÷ˆ$vÜÆU?K¡Î‹¹Ã|0†^HÑÈz¸ÍÈœ,pgßvJ y*šIçðò […½ð&kâCUèöášÙùcÍ–cÒ¨­ß‘áLám1Ù0È{K?Ôt a"šèÀJ ƒ9YG!î/3Š´m¬kv «ŽS[ÃëÜ X«T‰=Á¬LàÖT$Ÿ.|½uÙUr,1{Á€½.®pݽ¥/ºå­³_Q®2¥_ý@…xûƒWîߪ5Xî<°42cÃ-dzöù8=Gè¨òóí®FR¯kâ«€Q?_ ˆ^WíÑ&úŽÑtL÷¦R?L÷¬¾xÇ^ž`ÏTD>‡Œ›ðZàí%‘¯òhÞ¦~(cg€Œ2-KÈŠ¦Ûcoé{z´ÌÂb¾­tM᳊ŒƒÑ˶æuh?™L8Ç‹ˆh•E^vÏ ͧDø$›ÂEvSz)Á^ŒMy'Ç[p‚nMpÄçSßêNjxQ €Ò ݪXáK/=_µ¿¡Óý¤u´a‰Þ/Y’V¹Ø;¼¥#•x4öxÔgxµÐH!2s1ÝNsÌÒ6ÀИ¦£§ú¹ú' ª£ºy‘ëcÔS¸öôBòôoaØ—Æ1³GjâeÕ–‹rÎÅeû;~;aâ#ƒ½8F|@S7ýFÝšæFB¬/ül ŽáX¿_ôr_–©j`äžù‰:&oú}°ð’èÓ.ÉœûZòÞP,nÚ‹Ê•››ÿa¤÷a/²UÍŸ½Ê¨» ,¨ÂcÍŒRdÝáD’ ýB^ÁgËÈG}]ÑE0Y—YÚ.ÿÏÁÇ»¬èËØ´ã»3ј2=Qfïk ³>(ŸÊ (?Ú#6,= ÷gåEêÅÝC‰†ô˜›5UZfqŽ2¡‚–ñƒ]jƒÇÄZÃFŸ“èy»¹µñWõ÷¿î˜Þendstream endobj 20 0 obj 3569 endobj 24 0 obj <> stream xœí\ms·þî_qg:ÓîM¸ë•VZíÒ$3€¡¡CHk’’a C†Ø’fú±¿ ýÁ•öEz¤ûìÝ{íë)¾ëêmÏû9:ÒO³"rV¸ÿðôõÎî¾™Ÿí´Í³ý?õÀéñÎO;u^ºmÂO_ÏnØÍ¬É›jvð|§È›¦.L7©˜Õ¶½iô̈"7¶ÃëGÙl¾(KUæJe'sÛ,JY˜ìÙ|!D^êºÌ>Ì›ÜÔe!³Ç¡ñ(t= à¹K]UEi[}ßÏæ*×U£Ë¬ "ŒºFÝ à×óE•WJ›&;°Ã*Û»r[ðÃÎ˼¨»ohÜ·]•¢i²Ûþ÷óE“…´ŸðÀ5š¢8ÿýùBæ²®š:ûb¾0¹. ¥[ĈºÊµÈ>Z+×*y£²3×j§-üð7a3o8 ž†aÐ÷ˆâþZ3¼´5B*“=o»¡üèø&ÌյʪÙ¶Ú=YgŸ[H¨BÛeý¨ÓùBÛ- é¶øÃÁŸwJUçUËFG;v©ƒ¿í,†6·€–á‡þÏ{;Ÿ<Êd·¨Ýjö'‡M5-:-WZ‚e{Œ@º‡tÀ·º®µ¥Rè: ¸€ßÅ'Ȩ¾/|öÏ–2HÊeçJ4è{g¾P–³TUg¯­Km´D19 å!a?иÅ¢í"ÊáÎþù‡Àû/<.Ÿy–ÁßÅè[’Ù\šÚmaw_¯@*«’„n:Â?²¼‹(xäglµÊ¨›%h!Ëa¦Qå0 ìùû,Ìâäb”\~Ä r}?“3€Œt‚SZÙ…i^ùFNÎׯʑÈdï"Âá‡a üðQAtOGñ ¸N‰:ø{Úúú\½¼ŸkGýh_OŽl⩈ö ’a³ G,ZmÊ ÆË–ÿ¥y-NáÜ{y¾å•ߌ]ûcÿ]E3Æ€{Á €°ÜôùëbŠ™§XÂèýW„–°¬t…­yh!(±^€qØ^ÈÆ.‚*ü’ G\Záhç¢h3pÀó€òW¨A—úížÍµ®RÝ#g£%Ñ=ÓzÁëîMÈD·?½¥„o•_؆ݛݸ©«2ûÅh`ŽÎ'S©ä3ÂUÃ)ªÊ+Ò(-ú”A®P€go)È•Ë9ŒëÒa³Ï:•c‰¨ ³Nÿ +<°~HÇ»­*„¤”Ø?ü¥s>A‰Ü ˇî yvÃt|M). Õ q¥ºÑ¼OØ;S7¼ÞôPºµ¶q/¸Rÿ¦<“šm+V‡ÛU$ H,÷ŸZÙX­˜½»×¢YUÉZô¼äòvœ£a>ž¹¸v¯¦1f¢˜ÄS>‡Ÿc“ñ˜g}»9¢ƒÍQ2$O¦õ8yš¯ ï$OмTh#ùðÃ//ÔX²Ë¯–¹l¼×8™åyÅDåC° ø“ñrÐÏ™|mU³ð8g©5›é×Ë~ºñž€‹î¤Èq)&UÕKnÈŒ¬:ÍÁD'Œ ˜¤¡¿Fƒì"t¨ÂÅâêHiôôd’:ûÁɼ‡®‰¢ Ì õçÖ¥÷©Z?~w_ˆpòUبy8 ýÝ|QæÚ”.Ê gzî¡—:!ýfÚóÐm=ôÐcÝ'ý¾í¡V!-„t²»ÂÕé&DSPŽŒâàn˜á“Îȼ-£ à9í[ðmô•¤UºãñÕ} Ý|Û."jé8Ô2[8äŽË'óˆKºjôtæ~¾hôÅÕaª&騳`öÒ`†'p`šU¹»º0#!륢,gš¹ž‡¹NðxÞ³?ô€Ac¹ÎIýÔÙk“ðù3•ɳñÓPá±ê«[ý¦;G#ʱÉšàMË'ƒ€n0Xܬòp¶§Æ`G‚51¼-%зi½\–ª•âh±?¬ %LPÑósÖZ#Íq¡?ôê¡k…r“¤'MDµšh9áZ ™ªŸå h’èàXñ ,Xx)÷˜D•ÇXvàÊ TYh8©½ÞGË&·>rT45´¥Yë5”è5ÔWå4Ü%nÁq˜#q@ 0ŸŠ\ŠÁö;1îêìãýðqÒÖtØîÓŸRç   óêÀ±© GG½‘ ó4€¯¨7~¼€û€N¥È uyþ°tÚ6ƒL{š°ÜGÏ j« >î³~¸+”È [à„íkŠ=2Ë>aŽ[K3÷ImM UÄsTEá Ã’ JéǾ8XG'cJtþ­áñ9ÔÀXåEë(€/xJÙ ”®Îh‡C:/ŸaïZó§rW…^ÔuཫÒu—ãð ÐÀº‚"ê3 B‡Ës< »Ào#½1*âIUäªRè½`»~wÕ>e5Rþý?ÏòCÞѳJ©÷ó¢sc^9¼>9½ªÝåš7;ô+ì"®’ÇE·c§S;Žr®Â£¼×>­„OAo#LLj<Ú¨ö‚aâŒBéå3Ú•“9TtÙó±‹-‹†„‰I]ò&Ö *4nѹ׋ SŠ´¶p d›/ôB-EøýËÞBPÌ3ÆÑ'lñ ”ßÕ8Ÿ³X™ ë&á­Œ*4Æ¢ÛõS›E·¾•%”âà¶ûCÛ‘¤÷Ǿ~ã¡;‘ñîœ.seAÉ5éûÔ6ßqA=Xo5(yG=?pOé0ð3!¬ÙÜüø\½2ªÝ*5[e ÿ«¨ökòë—bÞ+ÌmƒƃÙÚî—IJÅcY×e'È—½ŸÒG/hUè[Žp#ÜÀ_U祌¢§…*jë9ÖmÄ$Õü‚Ÿ»¬¯òøø·èwKÑoGKý®_Î߈ÊyÓ²è´àìc)Üåw–׸&êŽ* QÜ:Oëä–'|=ìঠ£Ö½:ˆ_¶ù½ë>1áJkÃïã5`ë%¶„(·öØ%Z¿ÀÒ½dÔp#X*.©2í¤©”¥ê>2úéÉò&—:èñÜ– 1‡K‚†ƒøµïeÁ‰8/~—¢?pZ›È˜<äåõ~œƒÂ >¶‹ û†ÜÌÙ™¨èÖG rˆ9b»˜M:÷Pé!IìTû†Œ=%m'©í‰æÓtámA >ß¡ª †[<¸BàdCÓ&ñœuJ:Cw RÕMŽ#zt>£ÊüõYÿ· ÌæûÀÛ!p“ÇfêÈFxÁg7 ͘Kûž8£¡_ Î2ßç¤íWÒ¶a %ø’à«jÑÔ'”-^SW•³Å9í{Jû¾£Ã8ß@¸tØ‚sÞ¯´õ÷l=ЧÚÇ„Š*ס6¡ßÍ×…%äÔÿ¤­ÿè@SKÒ Ò3’ÑX ß¼l2£ºP!ApV¾†²ìpEîö\h—¦mk*×U±¦£¶»ÎcoëŒïûñ›Üé»|ÝÛä8± 1 ì1Ý#¸Uïè0–{|F^]낪&Wámœéû“eÚå¯S’ø™oôțΕ´úgä¸jôõ‘rbøhpÌùíÀˆVìûÓcíõ}~Š‘ :@ë‰hxi"Üß‘WƒE2 Âë-ù…´•k‘»VLjV-›^¨\}<žÞ¹Œ£½¡RjIéÚ1ý´±‹:˜Ïp'¥•PÝÝ÷pU$÷>§J“KQÖ¨E·ýHTZŽoM^vLï i5þœÄÕÈö6Ãm^ú‘{Bmñ€}êJèdNJðñ%_ÞS k˜EÜ ·7×éQxo¸C¡wËrR!Ëq+—>†“<óGXºµcð){ÞãÄgløÕû%™ä–L¤«BÙXë¦åi],½ œ ïþ€x%‹q{=ªÉvËûöoLiçeûJA~²Ï“AŒY÷1Ý¿² €'Ü ü.^}A‰Æ5÷ÔÛB‘g×|Æl|ü–N‡z×`C4uŸ2õÛ¿_PíìÍzUŽj } âü ½°`Ľ5â¢;—;nÞnTòF°eޏ7‚Ëù¢È+Q«`r«jš>Òƪm²¿¸ß›²1®|GæUí^ÿkÕÞ¨³°)ŠJ» ÏO†ðµnâîñN«ŽšÂꨯìt¥vž½l»*Sè6ƒ¬sYVV/ŸÂÌoæ–ìªÒÎÄû ݰ•´$Ⱦ °~[pÁÃÄ×ÚuÝ »lÁ‡íR«~5}甿.Ja¢¯70m½Á‡þ÷ÁxèÚ!^¦©6$ÇðͺÔÙò½Å‘Ǥ…L:óˆ¬ñfÍðö¾5ŒÏßÚgñ—¹lœÉ£–±oìüÕþÿ>Mendstream endobj 25 0 obj 3787 endobj 31 0 obj <> stream xœå\ےܶ}߯˜§dFñPxÏ›d))¹äÄVÖJªd•jW;º$’veie§RyÌä!߀à< È™YEU‘\6Í!A Ñ}ºû ÷«4r•ê¿ÃÅó·'·U«—NÚÛ«G¿ï/~zyòþ¤N2ý§½×Ï߮›U“4åêôÅIš4MV]£bU«ûMS¬*‘&•zàíÉ“õï6Û<)ó¼¬×—u[ȼZÿ´ÙIš Ù¬WêfV”eš­?ÚËWæÑ¹Zm¶B¨ë¼Y·É’´lò|}OÝÌÕ[½)Ö[Û¼h_ÏdZáåWöKðý›mµ~½ÉõSM¾~»ÙÊ C&FJèãs+ï3_3ôïoZÉç™j%`º—†«KÛ¦ó%LDÝ:³p9²J>³4_kÍRi”RÄNƒ¬*eù•ž×m?±úBv³ÛÍH#‹ í«›(Kü°‰ÇzJÕØS¹¾cìý/ö&t1³i¬˜R{7©ò;*Gxvº7bNe¨i†-~L#;;|gæ¡ÙBøQ7]u øC€boüi Išç8PÍ*hw¬(D2KrÙTÜ ¯­¬(è}D3ʽñÇ\^°Ž9øc.÷ÅŸNp8lYÕ}öòF=3FgÝ[ßHUH°I°YûNTý„#÷YŽºg8:,Np Õ4u¤8$Åçx†zp Ð¹é<É 9Xg(d8ØP'#øê3ÛVÜ|M3äTQnm¤¾ÈÄÑ¢‡R9N×Gý\ZÍ?Ô 3+‹ÒÕAÓhbXá¥ëä;µÐ/åjÜ5¶fCv ׿ª5$n¹£\`ÂÊù³ _;lÁŒÓµèÖd>ˆ\vã ÿa£>R¥Å0C*r``KßøÜr‡„†¡·:< ½Q]’QºfúÈ•! ˆÐ®ã:¦×+¤˜Ýu¶‹DÔ&s) ö'œñ>}v”üõÞM"í⎎‘‡Œ—´ ¼»¼çð þµe`; Ý€mï~Õ?ªÑÎB{‰[ U¿œþõ¤ÿ߇'§·žô)°– DÛrPiÝÁ­¬›6Dh{gÞ➎Çk07WTS@2˜ñ‘Nùµ+zlåšö£.ÊTôòQÿùu+‚Sõj©C/à=5ÒˆróðãˆÉ±3»å83Šï>:IŠRE¥« k0³Àʃ›)9 Y×Î#‘ýÁ©A€8lJ¦IS—P=‹Iô½m`”÷5*À£ØA4õ EŽýDtEûýš±…»ÙßA+iû6‡ø‰À> :ö㚺Ð_b8%¢¹w„”dYUs'Ðéõr‡"ž?Ь òx~ˆ\çE<Êáí¤…&z²Ö B‚ß¡­O^ÖŽ Î¨öpG=ð2ŸœÇ6š[Ƭ¨Gæ>gÜ‹‚‰ý¸±·C”iø¾žó*mtßaG™¢°ƒ‡ß<5ñ¦¡¦ÿd"N×À”ñXÄ6ðeË€*­ò8ÍAMÙàæ:ÆíüÃ"è‡)ðËÙO f5¯ÿÌÜ$IAÓTÿüľÃáÿ“K>÷¬<ÑÆU>´¿?µ¿s*Ù‹\»…lÃãŠ\óF®\Òì8áQ®¹Gpe2Sñt‰®r„]ž¢ RËœð>ÜEs2t}¾®/“Àøw*n¹¼Æ~óö£,7Õ=U‘ȲÃB1N‘¦Ý«¶.H¥áBý!kºÜa¾ðµì†gÏù…X'¥¯YlÉæQ¬·¿í1%/^'K"Šn¥Ì-¯“•zÚ D lþ`Ó <Àø›@âÙ¦(ÊÑ´fYÒ¤E~ÔiýåNCœ%{Öm“Óp¿š< çôkÜ[\R5\¼Ñÿ­æN½äÙJëÞÄð%LÊÜzÞSÙs•ïã-…––†YøÓ·•¥jª*¨~†i0+êË¥<²Ìw=Ü@…,oXc„ ÜÑàeÞ[—Èö\z9À4¾óˆf÷zS*MY{‹œTÜLëm@tšù´6UñeSÞœÚ!«ÿñ˜Qhë9¤-ŠEÍï3È^f«0¯3Ê"÷´ªp ¡´kB¦³JKwéŽúHÖXe«;G‡9dñ|g8·{6Ynæš|žœQÄm:ˆõ;T¬N‚a$üØt掹ÒÕØ´Sej.*SXA-·çz*!æ{²¨Âw‹ªPM¿Rª÷ ˆw8ƒm!’llQŒIS³:‰Ñ*®Nê9Züoë¸aq'Kyúû†d×#"s‹‰TÞç¡–Á¼@±õô”=Ôùí—°ÂRXÒtAÊÕ†@5ÐV˼8sD>ö´<öý_…P*ç~{´-ƒVu“ŒÑ¹5û™±ipÍþšv&Ê ã1§¿-Þ~kWлT¾ÌÜ„.¶ëª1T Y¦^½žmìk›©<ÜøöªÛEGà ä3¡ ×Ù×v®2‚.£½|­CS·nœÑ—D^.€pï¿` ýˆ»}ö!’-v6™ÙKúÖo”P …ÒÎ[À½î÷;¨¥ŽTTƒ††£ªÙ!çãjOõða4O®§¬vÞ7*û=-}DCO5 …üÁ[Þ‚åoªØ“$7sW4• ’ÜӉŬu™iõ3Û@@ÍïO}tX'3ÎA&ßY¨Œ°,ŸC'Z€áœ³®Lp17Oå}YDH‡MM" 4Ý(#Ò*F¶jÐg»¢˜H죘}•?Œ¹*ÈiƒQã‹¥“.™£s4¯UÌR$©]€]THb>Àøä®Y?wÅPŽÖšX TXKö ¸»xîYeõê7Á˜Ï¨Pý…@ê³Fƒ5^`=ÔËFFn g¸BˆTÙîú;„w¬7çtnÛ1‚x ®€eÿ.9¯bq¢æ¦×fzUà (øÁ Ú "È&æõßI~Y"³Z.œ9Lã˜Wp«ù´Úæø¢ô’ 1]/4]á³ 莆3àqêí¼ª%Ïcò´-VCbr¸¯ád‚Ú.{sht»Èø&=pä³–‹Ó¦,r¤ï ‡ŒÒµqÈû¡~.¨¾qd޲@\Õ`ê.ñÃ>ÌÚÞWÚ×ÿšËP-“0ž×Àâa±íF'[Š3TCÍØÛ⫳ŸF½,‡É7]…(2ÖÇKDlÏõ´e½tÝÅÏ·HM•KúŒ6 h¤¸©Ð—o ã¼¥5Ê>Wu’å ö…[²3TöÌk#¦2pªÄLà0h?7åÁ†¯DñM8­I ™£Ûã¢U8ÖØLàP¹>ã(Ë}0é5Vt-*ØààÂ\M,^†kú>·öРv-žòtÝ.#gÂãçBŒvt1¼aÅôÜ4 w>ƒr}Á­t `.û 2)ˆ IpÑ` ¿&v‰E‘èá u<+»¦úÁck™þÆãÑ[`Ž"‘¡àÂMÏ{ö¥Ÿ¶èöR "Qº™o醈ÖЇö)¹{ºéÇØãܿϦ{%Âæ|S:ªïx ÈŽ|r¼eQ\J`Ö.vy߇4¯DuYúàEs:5‰$xæyéÅhÝ”Åæ¡ó,.0@ôE÷°¾„ÊUxHËšæÌ’Æ8rCWd¼ â˜cÙŒgŠÜfúYÜúÜË…]øå¦ÊÆ5Œg /}½ógàœ¾æ³ÇɽfæâGs†\YxÁ5ï-'ŸјK¾¾âä%\³:–Pù”´$Nv裗EUJ—XÌ]îÞ}gµ£þ¹F f8«<®œI:Z ]>`!xB3ý§ó|Æ¢ÅÉ•å…:7/XجFŸ¶0À‰`~¥«Çè¦ø"½Åµé#\Ñ;,‘f*ïÛ¨@Ôèù×|ÏNÀ"8ÿö¿T»I“¥™ƒ1ÿ¤ÒêïÂìŽÐ DY>IÍ|Ðt7­¡'Q±ê°Å!îÚÎŒÂb–3OÄ/Kj!Îhãsã®<“A Òç2dPÖcT#3^ÎWØý£3¢»—Ðm‡¡·§|"1.:Ï=bRïý¼ÀÁ±Æà.?Ã0và.Ôºòbapç[ê}¬;n(O!IåàžuŒ¬2¦ÿ‹2ç*óÿ1-)<#µ‹±Õv¿«aábÙ|kr5¡ò—Ã0ç@þý3¡»‡>¼w7 >½Ì@œc‚ä4¬ ‹>o‡8*ç´qÕ !N÷ †8 ¤-…dŸä;üy„¶ä4€½6•s/áœï„iÊP`kYŸh5Š_6b¹½ãfrˆ”uñåÞ%‡ÔcCi·-•NhÙm°;ÁþÔû²?Vô¨Ùkª)Sp¼†¡qhÝéË4šCœÛ( eCuÑ`ü¼„¸¢˜B¦Çe¸u7¬,|ÆX‡ß¿Æa‹Z´ÕìÑQó…x¾ðpÉF\-&u¹œÌ 8£C7~9Ë~,6qNPtyŒÂ¡ÀàÏúRùôºQ³48÷æ0]ˆïoD¡Wc»'­uqP|f–œb5 4rw…nâÛù:xÀ½ UG˦ɒ,4ÖãªãÚ÷–;Tˆy˜#¨®ÑÌê‚dü5X> stream xœí]ÛnÝÆ}×Wè©=J#†Þ›'§qÓ´nÓ8Š )Y–e§²$[–ã¢èc ýÞÎð2{Íp ‡<ç(i€ÆA@ósÝ{íµ/ü>L“L¦æÏxqöêà£ÇõáÅíAwûðñgÃÅ›‹ƒ×M’›ºx}öêð“ýb{Ø&muxòü MÚ¶Ië¾Ñì°Ñ÷Û¶<¬³4©õ¯þ²ùü(Mò²ªÒ|ó‘\~qt\%UQ¦åæPî¾9:.“4ÍT»9×wëLõæÔ^=ë®r•Öú¥ã,Ó)Úá÷îî•\ògçS·øìGmR7yª6×ÒŒðY÷h^69 }yT˜ÚbóVz=Ç÷íÝ»®¯¢j6·GÇõ° þ4âCƒ·lÓßnt u¢·«Ávßé~ô{*oñ.,å·GÒ24w*ãF×}²›ÝÝ[éîöè¯'¿?Pyž¨RËÏÉ3-.°*ðäÇ]·méH ¬å iŸ¯Øe·Fˆ®¥ƒ~‹ÂÙö芞Ó`\O¥‡—¶[,< =—!„æh›=§û» ±—Ð.Ü…uÖy¦´ÌÚŽûu. åÊ ªk µÙîãa¿Í+¥ê7ý©UqQ{ÀK3€¼¬Kµ¹°¿_Ù«k{õ† 8Œ$¡»—ŸË.„÷YåI¡Úzó÷#Ýo¦gíè÷¼êÂ`øÀÞ¿s7ÎßÈ€ÂølKý›©ªîM†øÒ…wP¼ƒU%i^¸À! ¦3€J‹!> x ½¥C€Ÿ®¢¶Y“eœ tœáüÓb 8MÅÃ>˜+Úª4Àu}b´Ì£ÒÔ årýïæXÞú¾×Õ‰ ]Á°_Z¨¡¯nàæ9•Ä~¨yUV#*Tz;‰Ió¤nóQb\ÔÑjš4ªu`gb@‚tÖÏ «*«!£M1Z{)%ª pA÷ä –ž…%|¥±¦Ð2×=†¹ —§™îÍL^wXž<:ÐýåÁ0ÿG'8älý™]¢gð³Ý¹wGzZuªí¶¬ËY[L)@ºN]ñÁu“Fa)ºV­c‹Û5ÅhÎÒ:¾ó€wYßßÉÝËQI®ÝF%áðÄ[=¤;(º{îÛæžHwú’Úƒ¾0‹CY Vº ôo±˜#ΆsãÑ+Ykt£åøø’¬öm¯tŒóê3Š…`#øÞÊÄøÈ LÕMÏ¿†mG$ôQË[AQx\ÞYL¼¡¢Ë \¬!`©Ïm¸âúŒçb;¸ «¨zên‘ž1¹iÖ BáAç“£½•Ëk|À Õô{ŒP ^€Äe߉þ£kà£b“­'Í~LƒÛ€ý8RËù/}WbÖ”Dî{T©Zß#´ò+-eÕ–9îò¼v”qâ#ú:¸?“4,‹ñ,ÿ3ÄnMJ¡Q “Ü ±æGÊ\ä.qÁ_Û3ëa ;ñ_§½‚ë,ðc§êýŒA90W° °ÁÖ3ï/ðà¢Ëz “ÑÑlŽõt²éu{A@¨€‚ð·ÑY£\‰Ž^‚e ‹Ø~´@®ïp.„ \a³3ÞÇ>6ÒB>ÂÔ¤õÐù?.CÖxáá`Z€w¥^®eñBa_ûÄFÑs¸GžÞ‘ î…zÞÞ°ˆV6„éùAfvYÛ{Oomx¦, š ®Ó6 Dœ8T×CU(Èë\JØU‚µN¬fëC„Ùúã>éùB^Û«;{%¿ jÌÄA¢¬§yöi%–RÉ´ã I¦}àÌ‹Q0U1/˜MQ:À °ßÉL¹‚Éæë¦Ìº ­qÜŒ±Ü_ Ÿè#Õ]„Ú¦õ›y^hD*88Ãr¤Šm å…ýŽî"ôÃ¥i™pOØš—Ìë³4§Û|gæ@j¸„Ð" xT´æ eŒ>¥^íó†r*ØÍZL‰Sy'rº- Ì9Ì^ŠtW×»¡ö‰­2iËz4šiDÁÖqaNDâˆB^Ü©Ÿ²|Cá3w¦Ð¡Ž6Wun 9&“ñÂûçþ6tàc bh 5$ŽãktcÓØúE[Dݾû K9-â\42´¶ËÂåžæ öOoÌæïŒ•ò#Æ“º•eeìãïXÆ>^ñò"Ê[/óš*É9åX3deËŽo‚‡#J+mjõ8x@²‹¢¡ñh<1W ûíë²Ú¤ÏabŽÇl®&zÖ û­}²ì{å{Ñ:Œ:’V¾†èªa»<ñôqWuÇY‰³> ûÁÁTÛš,¯m‡KîMBöòU¦‰–'rj Vs"+ª[ÍšÆRÇ1úóÿ£åÏ-Á‰Ø‚9ÂÚƒe™B~æRÓÇã³L¨~àÜê éN´6\µ€vw9f­9²É˳b9ÇИ6í›…¦Æm³lF†èÂæXÀ·6Múr”ðaŒ„¬HFÉany’?á†nÑŠÖz²¬Fø¦ËC¿Û<\ùrkÖMìÅz ´Ó€Ì(¡Ü]tCõE E¢‡Ez]¾%&^{³=örgÛÃ#ËV<ŽÈƒtÑ“õ ý¡“õŒKðï£tw{0˜@IÇ~ÇœóÛÎæ e ½Ä`t`ùùb9c(‘54w,0°{,*ªN[Ë}ŸgT«É–ÏP§1A“ôâi':H«ŸbžYHlïUîÇÂ`mŒä¸á" èJ|÷zÆ'šSÛCÝnÀ¦¸áÌ/ÚšTN klÍ!âa»‰-±üÌ_º¶ª<¤ê:ã–vƇzKåŠÏ<üxRwÁÝZV²ú –z%›¼~hBþ…_cP4ßüa|´«Q*Õ÷•Iµ—»ŽqrPÃÍ)xS ¤çé ÿ$‹ñýÃæù$^'Kí·Ùüx`Ìöœ€vzJ-U4°uGE‰×‡Hg“Ê=PÂE)”þqo#Ìï¾£ëï^TÈWÅ]"9ï°»Û‘DXrúÙ¢ÐqaŠ}v'ö;©é:µœuë*;æêð€;?~"¾}„P%YÓsîÎhÆš ¾ç­®q\–ÍFµ¡T£C|`’üôŒ÷9…ò¨‡´&ÅÎ~4 ±†¢;g£YXn 'ÏàRh+ý¤Çàé1+/Cǽ4¿žÑŠ)«T~Í™ièÜ'Èf7ÞêSg4ëa¡­ÍzÌ/m´ì^cõðÚwô.¸Ók HèÄ .;q¶<#ö?jûÞv‡—릂]Þ°õ˜Šµf¸„Ï0ó׋¦‰èÒó~P·8sºâ¸?·Fâ,¯£·n3™… ”1´ Ψ1µÝÐ×& H¢ÞçJîËRFË)CáÙ5ÛçAÕPµdÊNâÝÏ¿ˆƒ<~²÷ýW×’éGaf¬"œÙàe¯·ä¸3g³¸ÎönŇ4éI»bœR—=PMÛ¹DŸJÌK¼Í'â‚‚…†èÑCöÝ*ø=Ä/ؼÞ{ ±.Y`Ÿ…œ l¬ê…#£N4i\ARZ‘`Œæ ìœg¡xçíPsƒ,ì#ü“C(á/…Ô {éqÇhä6ßNk (ïŽÁL¥ÓÔq?ì¿6Pï«Úþ/”6¦µMž9²9Nã=ÞÍó¼oDþ7¦‰¶`5GXn=vÂIû{jO½_óå1fÝBþ#¼Æ?|«à›Ï­ê逩9‡QV|*É=$S&e^üôuô“"ߨxŸëE®·þxJ§à(f¡º?òEEît 3åûæ›Z[às¬}|6o\!Ÿ‚àLRWen1⹬ë%5obpnʲò1Ç|©(Áœ­¾>¸;8efëú”繇‘¥ŽŽÇS¢—ÝW.óo =„Ë´)µpý÷M«I\¦ðµð´‘›¢W*ðì‘zË=¤\vbσ’‘ÂPÞA†¯u ·w¿¢ôq€²‘})ÙÊ5Ú=|eˆ.K7ðSdJ:…˧øìX­—µö‹+ðE38 þ·9nMÞÛVùHún»¨¯€LÊNYTxÝWdìàeq°‡©B¬‹­ppªU…eGj¾GÕÍ3«ºôk† V=J2^ªÂf+²K™S°ò¬p@Nîå„ðÀ´ž|©ÿüé„•Jendstream endobj 39 0 obj 4149 endobj 43 0 obj <> stream xœí\ےܶ}߯˜·Ì¨<4 Þã'ÉrRJÉŠ-¯WÉ)•ö¢•íÅ»ºØß‘|p’@€‡Äpw%+‰,— ÂàÚè>Ýènð—Ušdj•š?¶pxº÷åãzurµ×U¯ÿy(\žìý²×$¹ù¯«ÀòáéêÞ¾îØ®Ú¤­Vû/öÒ¤m›´îÍV®oÛrUgiÒ4«ýÓ½§ëŸ×›m™¤i¦ÚõýÍV%ª©ëlýÍ&OÒª-Šõ®ôl³Í²$/Ûõ·›ÔÔ©²®×÷6ÛV÷W©Z?éû7E¹þÁõúy#|¥ûåeU¥ùzÕ VgE»>×µu–«´^_JÛ•´}.  öp³­±ï©ž½HÒ:¯×RûjS˜b[¬Ï†å7vÀ¢jÖod€’ ÞÃì(ˆááýKFüA0K€ä zÉïÝîºJ@¿‡T¤Ïý^Ôª¬X)cšéûFhò‰A”`WÛë®äžûçCÙ¾Œ cü‘Õ±Þ!r|¦’º): ÕÅ4ÍMѰ†ªµfª2+¼ ; ž &°!¢izœ²«°z&cݰzPð®koNSµOi†bK4,emRäZ”E¨¿‰›’CΤžÔ6-1œPÌŽõ|p7åŸëBVS„w ² á‹¢T»@¼AŒc!ÅkWÉ1¾“ú JºÎAc3…øñ~ÓjžÏSuä%‹×#¯[ç6pS¼Û”æìœcà„Zr¥v$ Žèj¢ ã^áyŽÂ´}×㈪´†©,‡p½ ´óÕÕpÑÕpëËkëöþ–žõ¹·2-çÚªôlØ0µ_Nä÷·1òwÓd*m¯WyÓ–«Žf‹Ìu«‡ÿ$ 桬ÔÎ]j­x™§™^˜YÑ°Ž‡{úßØÛ¿óÔ»œ»ù®ÄBàÖšÀòž(w’0áxŒÐlÁ:£ ø‚ºÉ¦XD-´ÙŒ1ðÒéýæ6Áq0L‹¿n¶•é²ö†= ìè ab.hODuÃêÕ àZ%¦NxÓ2¸é‡mË2~ü^6ÝÍ5¯Tçt0!áX¥¯ÿŸdZá„_¡-ß<_¯½&)…ãÂl°ž8y€4/Ò¡GRæ¼ç°åÉ`ʵ ÜM™K ´`\0¨5°L93Ù ¸cHBÕÒ›K>tÉ Ã_Àƒ2W¥Õ‚ˆä‚LímX,ˆ×[º}TK¼4ß;Ÿæ…€ò˜[„£‹]Õ'–€Ê¼<¦ûï\öÂÒUVuÛ®·²®LúCñ \¢õ‰Œõˆ^åþMåŽ8⹬ûâÝŠ7sÀñ¿ê3u©¬ÅÚdÐRzóÛŒÄy×Û¯EŒ¾Cø±•sÿBmÄX®)Q?æC¹¶£-E?ÅOöàmeáó“[jJg¥4e§»i÷‚-ý¤ÕáÏÚ {ÑZA^s¤–Ú†®Óã{`ÖÁçÇA6f¢ÀŽ÷œlð³\ÝùHS¸É,º¯èZw >“Œ(Úî žlë“B¹FÃ}ìÅ«ª±hrƒËA® ?I%¬/—•´ô䧘€èñ7‘ßC-êi P`²ëC1Þ ÕÖšJÃçe9eN¹Yà½Â)UËu©p·%ww2צÜQ´TiÉÔ)Å2픓wP0‡-}hšŸµ¨6z_ÚXSYîyšåк ½b…dx,dƒ™ø½ÔÁeJ{3,‹Fî&9AR™ytʲ,M”ò-'7ŸðÈÈ0“^ítË ^Z3í²«,§XÓgnïãMXÓ-¸lßp#½qîG÷î‚¡¾‹Ëª“¤è%>,£\£ ™)-°e†ŸctYÛÊYqPqõ€ñ øv ÛŒ³¼pM§GK}ê…3šE¿+ƒñÀݯ©¡ «å6ج|·óÒmæú㮿½öèÜÚú‡ýì}ù8ËÅ#]f3!¨4i,-¶yRÖy¿Jo\)w%åJ+WúÖ•ž»Ò…+=s¥Ç®tLz¹Ò=2‡¹´Œ–õ,&ks;NP¼”âJŠR„¾ m_IñLŠo¤˜KQÅf;ŒÍövfâªiœ¸¡#Ü¡ø/¤h.t¶üU_,š*Я8?%6ùÛg6YÄ&™«[æÃ&¹¶¤Êvˆ^ö ³µuŒÔGDaKWzEf[Š9/ürSÇB|âÛÍBªºuZ Hÿ<43ñrDêr’F<ôt¬Í<†ò $ ±oÅ@îñ£H†ïª]ŠÿSÂ嬻3jÎdäp'~ÔqÞ«§lÕén“Ö‰3ñĹ²qÎõíd4Ô³c{óµ·L פJiaÙ´ÄàôkôÕ«÷¶Û¦WD:.ɯ {N(óžJñ˜ÖžS)ÿ¶'`¶+RTBX5O¾Ãù¤éo³È¸ YØÝëÈ3à XoA–{ÇsŽØ8` UjÜ5v¿ š×/²­Ëpƒ¾ÁC848©w7i[Í_Ð.Câ"üeŠ»Ó ð(¾JÀЇ#g¼ùý9Å8pX,Ž a q®ØÊl"&Ä}øC~B$-™ç-™jÊeŸzN½ö4 †äyÙà· âI úÃÒ‰NÒCPÂ{nòΩ’׉’ü6  / ¹°†'ãf^KÎOÐÉ k¥PíçåLyFgýF<›FiÞPæÃiÅ‚PçüÀâ+å®É3ã“;ŠÈ„Úip@ÛòÈáN_Äü{¡Á2nì, 9¬ Â^RxÏ.Fñy@t‡ Á°zxÜ µ¶v%½€O†3ì€ãËèÄU•zÀÜ»Âëº)<ã“K’3x6«‡vÐtD [¿y¾d4° Écö©ÒrÒø`П2‚Á(@äYâ¶Ý¯G4Þ9å_v³àô…yŠèò‚P¦BVÂu¹LÂo¿—I<öå§ÆeŸ±ìÆ\V¥©ÑÚ£o“¤¹µïÖCžU•&CÊKÒg½ô?,sï~Î~¹ýì—bQò |‘I›ï2™Á2,¯Hinp/‚'®@í–«à‰ÇÊD1æ&²J>á$š´ó9 fÌ & 擈u-É™Oôð?ãwká)­å*7Y £'à ~ °‰óã7â®ð².îV¡*ö·Úy<%ÊÅ/Òà XòQ[ öìcñó‰vбŒ£K>¿:˜³ÄTì…xÅ8ËzÁ<‰mõçŠ^”QàÅÏ”Áœ[⑆D]FbúZ£ÏÇ´†({XÚ{lÌÃãQj”A,è4WÙ!^õÂ*g(÷=·ú¬‹4Mʦ°b1"zÔVeøf€ôa§Q¼¤kðëÆ}1kÁNw›”v›zøia ¿ÍÇDlÊaË<º€õ¦Ü¥ö±§ÄZ­â©DãíðRr8ÂûI§>®0ÿ}u HaE%ˆ‚ Ÿá%,à½kf 1ë—•/ê„iq£·÷ëÙwå•ö²Oíå­³‡}ܶÇúlú‡É'¡ÿéý¸×µÂl¿‘›Ü7û{ßë?ÿ…žendstream endobj 44 0 obj 3493 endobj 48 0 obj <> stream xœÝ\Yo¹~ׯ @2ãxÚM6ûòfäc '‚W«¼¼ AÒȲb]kÞÅ"ùß!û`Ù‡Ó:l%¶aÔ°Ù<«ŠU_û×Iš9IÍßžØ?Y{²UN/ÖšâÉÖ«Žøt¸öëZ•dæOSÀéý“ɳmýb=©“º˜l¿_K“º®Ò²mTL*]^×ù¤iRê 'kï¦ÙLÓBªršh*Ë‹"ͦ*³¹úGUO'†ÔH9}1›ËDV•ʧÍ[™LËéû¦n)T==š)SZ«é)=ç…]«ÙôžO¨Ûm]¡Ðå…œ~  °îk"Ÿùýl^$…ÊËnàí¸6gY’µRÓ]jëFƒ{ØâÓE#?šÍK]XOϨ¬¸,R¡›úeûïkú½\N¶7Öô↓m?zyšó1œÛÝÙµT?ð:0îO³yž¤©N6]Ú‘3*d;v¡çÅd‹†ÛfupUc<ÂÖwÑ”ª¢â­îé­È%ërúûL”‰P20@¶ë{Mi* þÜÌÍL W ìúCi*,bKе+³š“xŒ§°‹–]d–%U.´ o/´È†ffè–CsÍ%=Ç3;îÙõ`æ±kÇ –dM19dúaÉ1â˦0ܳt7ß½ ZV£v§åJ#]øùΧgë²êÈ5_.cën:²,µv.Í~Ïû 75ßt6QCV2ÏO-ÅŸÙB&vŒlÄû€ï6ÚÌ]È»H‹øª7ÈJ?ÄTïå~n2Û‡ÿØœz=b?ÛëáNÙïç))•×p›vIZå½vXŸÍëé+ªôÒœseVÓgæÉzó3­ÅôGóó¥ýù󌺻½TG…v¹ðØ3¡eh¿Lá%ª>º–êÞèöQ·[›Ut*4L¦ …ìø8+ò^&Ê¿/éíæÍ;“Àì ï KQYo†ÕšêËv8ßEÎÚFÙ€3æÌw=Ë ä9_³ßL3'úVf]Jnî°­Ý¥£ö2á9œõ¹•\AÏSz>vHçàšïfs¥…Òh%¶ZŒùXݾҔ+‘’òv' c²ô› ¾"ÇV(bëHò沤l)>Ò>·öqQWÕ½i´ÎlQAõº€ý4Ùx's:©D¼±HÇ)|íi;‹ÌúB ôdKëµjÇ2/Œwd£?ÍæY’—™22ÞQ–zo©#K‚zK½¶Ô÷–zf©uKíXê¥^Zê'ÐÞ¦¥6,¥:J KWB%zÏru_uÖÇ›D®ùŠÈ—D>ƒu„u'Dþ–¦D "KH¦4±¥e3ei°©eÈÖx{Þ#r—È "øk†q²ÌˆbMÑ>_@òì—æ y#Ù}†{eÕöó%’ˆ˜ï>2µÏÙ®§|9ìFš§R»%*@Fwúîé)‘‡D^ù6ö-,­b,ö;ì‚ ç¶°ŒY8î&d’ßi¸V½iÏDzÌ%c^Ž—ä¹Þ„ý 5[õ|؇aöÈhÿð6È«{l]GfC0×Òb<~˜8°f ›æ1ò>Ï꤬”ª Â& FlÁ ê!£º{|»Nf‰,{%†÷!ä0{ƒi¬Áq&Ìý \ØÌdèkaŸ^cÌp»À<äìÿí1ä^°Õ9ôV½õÿzÎq@ǰtœ¾vϹ½HÊ€à›ãÆžUzÅ;ÞÎzVÖü‡¶©MG©ÔÖ@jA¾2—CTÚGFß>bÕ«‡ äü€€>¹¼Ôí~«p´I°KÖ4ÆÚ'æ,ÕÅYHŸbÍÂ0jf7{¡ƒuy…ÞA³`oºÍ£]Èf“ÈG‘W¾Çï·à(! 2ÝÆ`Z›y@C|¥ÐBCÌ`ÊÒx´ÊÒ5ÆÜü° ®8ÜZotz¹†Z›–ùw  ÙíqšñRÇ È›ÝgåŠ#k€AAR&LÛ8!h° 0¼íj¦µHYÑÁkp ²ïyBïsaÅ#a…Jú¾„Я…Б~Y2ð"„K £•ôq=zœ¦BŠo[_²U±õÁuÇEgJÊDTU/ï›sìÕÀ9œ‡Ûùó9r0zmé”UÁC’‘àã54÷h1”vcÿQØâò(P`ç‰åîœm7°_¬Æ28ÙÒ_ùËdlÏph±cn|ú ³ ŽÈ|$ã”iz‰q –\;ÏÚcêalºiåF“£.)sShílª´ ßQDI»þ>Ç%7M {ÂÃ…Cž#ñNY"JAþ)z *fG³þ' óOá¬Vn»ÙHMV;$B™ªÊ*Ì÷«ÂlFbkw D#xM‡ßA1?´Ó•ÕÀNéX„kß¼¢\Ëûú'¦éë]8ââË<8æO±l¯€L:™ÛS4…âÆˆ×Iia®1Œ7 Óà+ÀÔì„:ÛD¿L¾H´‡Fp’ëÀ * ¦qH!¸[x³ãr#€ÛŠèùyä\0*Vèë¬Lÿë#ýº“pê'Ûÿj€†€=šIÓD™xl A±ÂU›ß¾¤Ú–hxݘxJYtêÉ–Èx±ºÿ("c¶A¨æ-(£`ÌæàÝ®fÝuAÅ Û`—ŠD|xéø™. T…fšóB©´ìVÅXx‰Ey>ú,&˱°®‚¥ ØÛõ bJœŸ{ ö¡mó&he+qì7¬¸Ÿm¾‚<¶ ø‹žÅB‡°â¹toyTðiD¼Sõµ÷=.Ê @ÌòX\ù .àß ‰¥ò ‘ÛDîù–²×XNÄODº¹ Ý&É*´K|ïo_Pÿ–û¢:&“%1Yž&²Ë¥h-ÏžwMäB*ªã¨¤ð²R˜¡§®]Çß·<ìÙ  Ffú Ëm z½ÚÙl€G2ç›Úgpð8:¼+Ž/@C,_;×ê-`ÎçwÅÄ”³’‘D¦³"MÍþy½¨’L³qÙ»çK¹}õ hƒ ë6P‹ï‡îD¼&n<…<ꈠAkRQqÉÂHÄ{ØìÙšTöcTŸùh6k›s?WÚ_Jc Ô»\¯öÊ[/äL²´“côVŸ:+•]µÜ…±Ê-šÈ϶Âá†ÅðpÀ«¾ì‰Óã¯;ŒB%u®zŒb„ßßo•”Ú£_)1;š¤ŒYˆz8&¼¤;±6Ÿà ] 8Ÿ—@³_XŠžî‚²ÉÒå3hù8«&tf0ÓV—þá¡) Fóô‚ÆÏ’OCëõˆHæŠ2¿ƒb¶0&¿õ Œa’Ne“¤›I^ †û ílúowÏû÷XðûËò•}­Ü{±í[ÎUšê6TUåƒgª¢ª«{dªåü˜J5À€©útîÛh‡Uy‰ú Ý·ÔGK‰åœvm©3Ð4Û´~ti¿Ör<Ú‡4yÔÇ]OÞÇÓŠ 0/lMòÑŸÝb}`:{ ½oAÙ läŒ@ÿËêêÄh!|oecÐæX+™?CE‡ÜÒBLy-®h¡¡ÒdJH–Úh’ÎÉF¬re©À*$ÄR$J$¸$!tvÑI9ØO‡iÈg'¸é9xƒ‰æ\-´úoaÏï’‡þ¸KÎb§>¾>)b# pfÂUGc¸PÆFóXúo"ÆšždJ&E!î +ÀIm¡;§žV±©Ñ$—·¶ýæÓÿ4ÿaš×쩦:K¯‰&.>ªáÃTÖé™)ù×Ý,}ÿšãS,³ßøÆðÍð’V$xo†…åÛ.bÑ$æy àªa&}H ;û<úõ|S£»¥”i6ç¹wËÑ;ìÄå ®qès9>¸z‹$<”y×ãÌw¥y•THó:wÊðNü„¼–IìÄÍ`(í—]#í¹æ®Þ&!.$ø]@fˆÈ!åƒdŠç9˜¹·>vÕ«at)«}²€u]Ñ÷?.yâ°-Ýq6Æ{³BßäRzä2U¤VNG;‡ Çò¦q®)»ÿ?½³Ã®nÅ”-+³ó˜8…¾£b‡Mß, % bå6îÖòÊ Ç’²ö+«Ë¾'„͉1wŽÇ}¬©=E´„Š \ c÷^àwÌ™ÒÞ]ƒà•׬Î¥d£“ŠN) ¢^Bðàs8î'÷é¾]=ºt_²Ö–žCä ‚˜%ñuáܕ妜q¥2•™ÀucœàOÑàÉ?šeU=Ð3…LD^ÛÛ> R#§D:÷N«¾Ò²HŠZX[ûuªHžéö)oâïy¦›¬*>#Fr_ÏÃG@ôÖ{ô°ÅÒÏtˆ‰EoðŸÂ=b·£J%âE½]ڔƣ>AÈìz[7Ää‡àðÅ9ï ÑZ¨«¼è9æ1Ü/¶FøzèÑÌh¤Ê½ÖÚ]iT%§v™6úq—cL_QgEaîC97/Æîg´b2vªi¢ÊÂN›‰;ÕO )«*­ôjçy‘¶oøÚ͵*ÕírÅfpy›Q˜ÕîOÞ` Ä»‡Û=ÆfFHt:¡˜þM„`ŒÌ.DD¿]ý˜áçl?ª«$Íœ„¥æ@•© ñþ2z™•Mš>ƒ;-øàÈ8¨ s€œoÙ Uöopi1_ áïHþššâ: )*!JÒŽü€4¿‚|ãh^fHüWãHeÕƒËf@¯–y^n¯ý ÿþïsendstream endobj 49 0 obj 3723 endobj 53 0 obj <> stream xœí\mo·þ®_qŸš[Û·Zr¹ÜeHb£P‘&®s-Š:† éN–jéô.ÛEoùMý{%÷…3Ü}ööN/1„:·—çÃáðÎFI,ä(qÿ`ïxcóu>zw±Q6^ÿ¹Îßmœmqêþ+8¼w<únjš‘‰M÷7’ؘ"É+¤bTØvc²Q.’¸(FÓã7ãQ4IS•ÆJ¯¢$ÎE*“||)5^Pãe4Âþ¡Ì8¥VIà(’yñvúKƒÌ=ZÛe噥`:³îPÿãh¢bYI1>q VJãY¤…¨°ÐJ¤Žµ‰¥ZHÇ"ã ` ›eÁóh’ÅI"¤Ï©õ"šä¶U¦†ƒ#Û!Í´NRG²Tq’gÂ’Œð2&•L”*h¬ÑŠÌ‘ëV912.DaF޹™ô뻩$•f”^—XÒ¬H×”J!D¾²T®#Ësm,QŒç{ÄèKbô Õÿ$öiµ‡´Ú„åÆžËb¬¨õmdÝ@G>–AJ+.ŸLYÊM|öi‡žeI>sÈh&”ï­P¬îhSŒŽRKdbÄø55Žh‚;µPÜá †‡ÑÀVIKg]¯"!㼨-˜$i°r&ЧpÚ2ì™+³þXpÖ°Õ,8k¼‰2L÷™ôXÒ}Z×EÇäÒ’‚Ê ’8ÉR}Vàiëè›Ãå$QyŠ<ɲRÝë>ºW­™,—Š,Vé}iõ­ÝÖ]è¤ÐVý,Ô§“M㬓Ne¬ç‰…–äfîQ;›¹H;í—é¿7ê?p~U:AáœEAFµ¢ e©h’ÄZè¼Ò«Wqj)ù1’±.)ÝNè;\Gv²Â$ÚʪjÍÒ¬¦o…èKËÌ"uóV”O½©%åo²ÆàKw%6RYãbó\Œ_9«%éïUc¡2ÎÜò‹!ß"ñlRëOѤœ5„ö‚f›Ód²dËÎH¸ê0…úÙÏ̇ÙÌUhjܯTŸ\T²t ­)uÂñ™&%ÛI›èÀ‰áÈÞñmŒN¶lÌN†tÞ±RÛú‰f?ÿIK‘R$Jݺø—÷v¤q#_ý)÷ÅÈê{Ò,·¾sá9¨ òa5(3®VŸ"KºP’ 6Ø Ê™L¸?ø‰!Ò‹µ`¢sË<Á÷Þü­3þMÜ'¿9¿ô_X#E%[¥fHeâBy×õ/ xiø[š9îø£ë¢È9e›ÚqÖi[o5>ðJ4÷ñkÛ´l%>ò¸•1­fr¾×$[v¿ÂFWŠÄºÖS¯ƒdt q`Ihª¨Lu¦{wr°ÿ/àw¶âƒ¡ãÅ3þvT*›´K¶®ßŸ„½úÓ6xÔ˜ CO_ÙD•»•‰ì±Wo{ìó.šÇÎ_W\µÿ{M¯©oï¼›¯í¹¼Ùó„ÝSSå:ÙBUëüo$ìn¡¤{dÓ¿fIODé…’ÒpDÙñmm Û=bõxϨÃ×Ü}õÆ3¬`­~²A_UïŒ00dX /kál ì8¯®Îû¾lŠtÖ;}ˆ^2ÆwÔ•}ÇqdmJ–¸$ó™‡$1Hxùí‚ý©Å¤áÝpÓÐ’¸+òßïÂé# <§Ÿ-ð ÌÛt=>³.ƺh!¹(Gä˜WˆDežÛø8¯\M-]¿©¾éeíò è¾,”åŽ&4ŒÍvÒæbÖkì>&rðØHªQ6&óJTR'䘙'Ÿ|nW>ƒžíÚ™ç6\ú T26ûó@7=ÍÌ èp k‹§+¸´ZQ"ó]_ä¶ó>9®BÁJ" îÔ#w8ÑÊ|ôñƒ¢6´©ÜFŸ&©vgã e^ssZw`nZv®=l¦Û²=*²Ê²Ð(Ÿ×dgx«ó)©p.ããté¾D —î{lÁ8ËÓÒ5¯I}»!¿{è1}¾‹©…I!(›A®õwS‡ }ÈþØa•\…óQ@Œ¿zvzèÄC <„FP¿™‡öÁˆåXÎ=tæ¡UF¸ež¸K௜¨õ@Œìã͆}ºÙ°ÿ,!]¦ư ;Ì;}U¡kÕøbØܰ•OÈ÷Yù™>H™öújò£ hC^ö6#š6+rïh“@κ3Â-sAà:ÎzF F†õà0쬇-sÖm Ì_¸½ÆlƒØÒ¿ûƒ2öÆq#‹ÿ"Ê%Êá ûó8nî…)x¾ðÐr¿ÍBÎ oè·1²A¿‡ úmH—òÙÙ€Ó%ăüvY¦ƒÆu³›ÞlÒ±>Cßè)k.=´í!ÊU‘MQÖŠŒæhûe Œôn¦û4˜ß ýHànm/|Ià?ü%‚Óáx Yõ]ŒÊÝz§äÄz4”wbíÖˆ–ª,Ûî=ØÝäÛü{Z´÷s/2I•ŒµÈeøsà *Y¤ò§krÐgÀB©9¼â[VáÒª&ZvWRÝOøñ­»’›Ö,+ÒsEO+Öè-)téÞ5· ]j®i c×JA5òÀëTƒ,¹O-k¨éºk¯®ŠR®L Q^|9·LðÚÝa •6­uIæQájm;—LÝuUÞì\IP±ÉJÃñe0/(GëênL¦g[z%çáGL+ÔR š¡ðåVSo^«j?XÁGPdãxœÙÝUêµê=jàONA˜q|Æ%_wÓHcp¡[K۸׮ÀÕ=÷u™ÌÌÓƒFÛ}¶]¸Ü¾ö­L]2§h„Î;w½ET‚RðPÕgžÏá}~-¥SXlÄØ°×V- ý½u¦þ.VÕ¢rmúŽx»ì¬ëÔŽ2 ¾Š·9ˆ²ºX)€+N[<®û@F²¾}[iUs§â"SËuŸõx¶!k©b—Ç Ú"®öÆd±Ø6•FY_¥ÑàÙ°[«LÊ©U³/àíjðQä.¢Oê׈“F°k¸_è‰w}O\·<7Yæcr³ŠJ h Æ5öô`gÂ*ê¶SêHÓÅm½Â¤Ç]Ñ__MŸHݳù®T±ßíÛW·¿ÎSÂÆöMîm"Ð_?ïy䨔ˆÂÇR¢¢óz^Pti”Ý^~†M=$)&¶j<Ǿ‘òÑ”£~ï¡§x0ȲJàÑçØ­Û%<¸CðŠÀSˆÑ°úu_@ä1ŽìÀà£|t®MÖóÔ*Èqùïø1WŸDàû}gË Þ-¨LÁظSHÃ:¿õmbÎËð²yY6†1‡Ñˆ3V8ðˆ,cCmé0>6üÖ½Ãýžÿæ‡ëol Ni/øô?es´‘ÀÓn|XÏÝŽþ~ ´32ýΡŸ½„£Ò—ûµ…o¼°gÑbêðÜ .KDý“.‰û‘àÇ/ø¬ê›¨r •y&b³|9*<- æ5q~ Ÿ”‚“5:á⌠÷ÞLŰÝÐá£zAدЛÔtõ¾c]«ÍË*!ÝØaüÍï7˜F×^N7þfÿý Æeôendstream endobj 54 0 obj 2988 endobj 58 0 obj <> stream xœÅ[koÇýÎ_A @Kæjwö&äØ)Ô$®+Χ0$Q–ÕJ2mIVœ¢"íîìcæž™=ËåÒrl¹Î{îóÜ»o§a©iXý7ÄÉådï0Ÿž]Oêæéá_ZâÝÙäí¤âê_Ý€ôÉåôáR,§ePfÓå«I”eæÍ¤Ñ´Ðíe™Nó( rÝáròb6/â8‰ƒ$™]Íus«0ŸÎQÄiÏîæeq¨f/¥q%]„¼ÑdœfYëVÛ÷«y¤Y™Æ³ùýoóEdIš—³¥îšé)²j;×óy„Y©·Ë>³ßÏe†Joë©ßÌy†‘žµ:WTdAá¿n:„IVµª<‚2™E²ìT¶x®÷G*Ég¯êäQRâï×Õ\zݸœýKwÐ{È5)£Ö2ë„;- ¯ô¦sõqX÷Ë¿NôÈTM—?L–_ü¾K#Üë—2jW.°‡ºk¢§(ËÙcûû¾pAÍ%yXF8ÿýÜ*²²0\&)r g xd88\üš>≠ƒ¾+r÷esŒb{ãM«Ê4,t«ÞC®ŠÙ¯sMEI˜êeí¨wóEª·©j‹ÇÄIdµ®X®&z©å?' Ó¶hÙÉü ÿL s%Íšz§l/“óéÛLÃúfšGÈó^î¹¼°Æ·M×B’®S¹ ønø ùÔö…Sÿ¢_rJ¹ùоôýn¾Hô‰’¬˜]TO§yªPJ΄¼6ÊÁ—Lý÷Ÿœ›xm/õÔR°~ÏEʽǮ°Lç*/ª½ìªÜZ‹LÛŸ(-x¡yïÒðÊ/ت-O3‹˜Íiy™ÄfØóÏ3™¥’Þw³#6¼ÛÏs™ ¸d¥ XË0Lsaù»^ê{Mª·Êg·Î %Ã@ À¡D„¯„µø\·8ìÁ^£¾¶S3ñìý<­^ßÙ׉حSº,Áþ— ›}bnQkUn8ÎkAP‘ ŠÈp çØË±œåÂn FtíqXö1à#1 ,­å@þÚMAóóðó½=…¬ÐaÀ8K3l ¤uФÊsíòåÕm/T©AUþÙNZù£in8à•\ùªePªv»×ó4Í|Ý£*'UD÷ ë«ÄÇ*†‰®ZÓ‡¯•ŸlCïMo´”¿µºñ‘øTÿ£<ã›m-Z‡ëU H,w¤jÙØ¬˜­ßW_¸ˆ@×¾ƒ×dœ½‰Õöz<8±SJÂZ\óQÎï“!¢îû¤È.øe7ž¦J‹ Î ÃíÇ#ÄœÚða.1Æp.˜Š›Ï-Ô“ózÍÜ×wŒ±Ý³A%ºÅKºþUš¨Þ‡tÌrûHh—aâ±Y»|f) »³«xhÕ'7v!ÁV¾±“~Œìã6¸FPήqôíÛèï˜k{˜fX¨²ÁEÔYÁnB¹ ™°p£;è:CßÚ$ì7‚AZWù¹U\ ‹‹LÛ;ó¡X˜ÇÕÏ:¦L;P8ýM×Ö£ŒS¶.Ì&MÈ P€M È)ð£î ùM¥¹Ã Àgî 0ãÃ\•ŠbÇGÂÉàm×òŠkÿ+îºò_¹FNšóÖÑyu&sSÚ+»b®qÇòyàw¹›ùQ Wµäaš¢þM?•ž@3 oN“À4r_”bûÂÍ Gx’y..3œ°Þ ÓÝe+1Ü" V$ísæÚ럊kjnaÍWÔDÀ \Q8º®ÅD+ÇÔŠb3W¦µý°z°øt®ý&}¼7ò»ê™éˆ2Ú8h_(CÃà’Äì1Ä §Ô›€¾Ô7j·˜¨Cax&Øø…5ÄžÉmßM®ˆîŠPÛ6 ʲÛû"Ù÷ö¶àí^×ñ`¢ÊtUê«–H¸}ˆb93®¨:讂5ë´'¬² þáÝä¶Ðu´ $ªˆ†Ù¹£¼=PíŒòˆ<ðpó ñÆÎjMeF¸mX;ˆMQÇ€ç:üYº0+ Zˆc䃴ÙÉþ,ÌI|à~ï0Š-^¢ûh£™êNaöÌqæqm[êÆR±¥”¥¦–úÑRG–Z[jßR+B½³T ¶äΛ‰ÊZIòHȵ„„açB^ y#d,¤¢3Àj+J¾²Š<ZµO‡€ÖB^HU¹aûŒ•_r'q¿¨„NÓ8+Vèï\Us_tŒøà«Ü‰³ÁÏò×ânþ™ ë”Ê¥‡µÒ‘ÊÄÅ&çR»xŽDhA(0iª¨Jš^n`lUç #UåÔÕ¶ƒPj„µo-%c…WN)q.¾òš¶“^Pò7ôÙóWpx9ò&aoî. +5–ãð÷Dˆ†¸!7& ^0e0F&OéÝ]û'w²^NBOâ¡ív'k~¿¥LMåŠË0™a®[*xžÇ·+lÜçÔ˜hæÚ»îŒù…giÁ@6dïóƒU¹ƒ6gð|¡‰5tÄ)ŠAh Ùm©¯p¬ÚÑ» —>МÌ÷8za‚ÂV;Ïk·;¨쳺ˆ°iý‹ôÚQ€ÔpÒÍyÓJàîqy«2,×I¸ é¹;Ò9EÚ")åîÌ^Mˆ+Œ¸æ-ýݯ ’p·€Ë*¥}‹o b%;´ÂÔ8ÄÃöå,Ékœó4¥Å#à<¿æ€µ–œz4ûå,a™Õ0G [7ë2qG4NŒ™DÓ·íÑÝF£h·Å‹œ(øöŠ>p¼À¾¦O¸-âm¢Ø×U·Éçã5aNJØ•NÏKçüyC-È$`‹,*ëË‘|¾i¢ë<(¢Bꋼ䈢Áë9OÉoóA¦ ¥.ÒxÃzv*ï|3ڇʲwØ19¶ÅååÛÚŒ…y ÄÅ|ëÐÜ…§<ˆÌQBFÔûts€hþDZ>¼4aȼ/Âñ«mhËÔ17Ý:šŒ0šL3cÇÿ@â SK½Úˆyœú!M\'ë[ê±¥ž[ꥥž~? Å®¡¿ Q=£8¹'3|AcʦÁ¿QfàA2 [’‡ÎÐ!¢à{x%Ý 0ˆ3îÔA͆HÐ{Á“]?¨m¤qã©Ù'‚|×2¦¢N9eSƒ2}´Õê0«²œTD¡§x^ö¸¬yD׉`üiÇz¬é¼h6ÿ~ƒ•_µ… Ójn‰‰ÍÌSuÔœJ¤R'Waا¾âŽ1Î(ðÝêbú…ÁKOñ†ƒóWÔ2ïXÕ°ÁϨ½žº«v #Õõê—'H–»Ý²ÍÌ¿·[Ì6u±¥ãÇÆÔ1G”8,Ãy æ6Å¡pÇœÕ}Ýñ–½-ÂÜ ©ªáÐñïläˆ_#¿›á‡åZ[a£JôåÙ=Xx°å`ŸïF[WýŒÊâó_Â^}$ús½ÃNÎ$¼¸Ãñ%¡ÃO„\ ùRÈç´†=ò'!7'ª´mQ=øÕ Ý’ò|ü¼+xÄÑ[ 5I”Ð*÷º4®"Y£Y°d·¼µœx‹lr:š\\õ»…÷ø´ÚÜW8h+黟ƩɫZåÖ§YSûèdâ< ŽÏûß •ê¨#øhe°ô‰Þ4¯÷©í\ƒÄ ~–4Xð˽Škv2¸þ iFªý¨ÑéóÊœ†WxaΚPNÚÌ…¼ŸÏYt4X¯´(1ä[< ŠpLÝ^Ÿ‡Í<îgÂýÞœžq¥^"ÓžjÏe;¹è"xì+åFVš¯È¢ -ªÈÃ#^e'è`«>ÉÁô޽Ù-—¨Ô-ˆI®µ§dpûöIæùù=IÂöp≟²¯¿6ÕŶzƘk€¸ÎêApóõIó<¶nmŒY¾ö>i1Ì‹QßKŒÀ”¹Îì”p}&Xñ)ñyGù·E”ö肉†S´µSi²ZÛü5:M—*š.½Çüó'MŒ²I a½ÅçºÞFEœRW©¯ZÜv}Xkoûu.ÿ þ“k4Çï²HÃý_˜8ã¾×Ä=… 6êD”$ÿ¨ùÜÕdƒ/x ‘ùʆª·{Nc™¯6Ë4‘²Ü­?”ãvNsëß¿—iåþzǼœ7l[ïë¸æ;_˜“Tö¶—á{ý&âcJzeì2VúÝGÑ®S)•}RÏÕxbºlÜu»…¤ÈG•ðBJê«Îú÷YFœe3ì ù-ÃaâÙŸÍá2k"±–Q,s¹% ÂRSà!¹cæFÜ—aE^å˜Ì÷5iû7i™÷D |Î6Â,ƒ0Ú ¤ÉÙâ)íÆÏë3Ë)çïòÄé62SŸ¿õx9ù»þÿó^¥Çendstream endobj 59 0 obj 3395 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 23 0 obj <> /Contents 24 0 R >> endobj 30 0 obj <> /Contents 31 0 R >> endobj 37 0 obj <> /Contents 38 0 R >> endobj 42 0 obj <> /Contents 43 0 R >> endobj 47 0 obj <> /Contents 48 0 R >> endobj 52 0 obj <> /Contents 53 0 R >> endobj 57 0 obj <> /Contents 58 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 23 0 R 30 0 R 37 0 R 42 0 R 47 0 R 52 0 R 57 0 R ] /Count 9 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 16 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 22 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 45 0 obj <> endobj 46 0 obj <> endobj 50 0 obj <> endobj 51 0 obj <> endobj 55 0 obj <> endobj 56 0 obj <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <>stream xœ}YXT×¶>ã0g&‚q(¢ç%"–Ø£XÁVÄ‚ %¢ ¢ éÈ4ڦΠ#è0X° ¢v‰=&†X±äZ’èMñr£&7Y'o“÷Þ>3ÈLò½¼ÏO˜³g·õ¯ýk­ƒˆrêB‰D"ÙôxebLt¢ðy8ï%âûváû‰·ážÿ}¸¤0fK·"r#§]}Y?W~e/¸ÚÒzRb‘(µxÇôø„´Ä˜ “½…-Zæ7dÈPûÈHïµio¿ñž³a‹÷@òaktl|B\ô–ä‰ÞÓÉìØØ˜uÞbÓ6&yGFEEG Ë–FÆFoöЉIHˆßê=hºŸ÷¨#F#?F…ÄÄ­U&yÏßï=Ï{QôeldâŸ)Šš3/0m˺ùÓÒã£B¦'D/˜ñÑúЙ‰‚’6.JŽYÓúä÷9í5ÌkŠWº×©¾LßÍ}‹ú~Æ·uçÛÇ+9Lh‚ºãbˆâ-ŠûtµA_Y¥Ñ§±8ŒNSk³²ÚjöA»rªð”%<ñÁ°š6ë*²³´ªtÁuRëfàËÁiNd€µ0Ö‹ùU\ƾ‡¥0‚¶ôU•}*»žÆ+x‹¯¡Ó4äƒðµ ƒÛ•’X:MEVTCCèj#Y¡&WšD__ ”q ¼ NKvÐädÒÄ›9Q3L/†#n3°Ëɱšìl£†l’Eì2TVj i쌼YReß‹étÛÍì%ÅÏðxx&ùÄa†”NÕ­…µIŒ}Î%p®àÊAm“»ü!LäÇ*K„YÕl~A§Úvµ°àn7;Êéz®Â´Ó$“ŸÚ³¿®¢ÑëcdÑîIÝ·µ2ÅÉ0Š‘Ú­ïB›m—Hgc@O?šyuôü )ó2äÂ@r‹gœ»¼‘ßÄ»(",­ A2ÜgÈPì{ÿ8Ü[ZjŽ4³û-;«Q¥Ì¨2ª5…j ³<|nÒt2³ËÔûÿñƒVèòônäÒRV~+½¸\[åµ]o´°òÆï¤ò[ÕZã6FÞ˜©U§±¬¹Ëñ=i¾‚É  ¿JŒíÊ?ûÙÁ‹á•µÂSe•ð4ˆÆÃñk<^K ^ÎÊì¬mWÆØ!VWXW§²¼Ò@Ãhü«Ä;1:šp¶§@µ"NL>(ÂØrþ®õÖ³/לwˆ½vh_#:/{øù`›ì‡]¤9­€h/½w9báü?`±/®U@øI¯£m}jãÖÝÐÙüÅ+f0Öƒ×BOBž8ÿ2”NÕ«+«XÌE(•â‹øÉ¾k›”€–•Mcä§“+ðƒÄê3^d¯+¹û~º»¾ç‹{à^oaÓ›Ÿ ;ôöý7îÍ®uúÏ“ ›àã3îñ›×ÏÿÌZ×'rð†§š]5®gš€?¾º»Ë³ù4þµb˜Ý±Š^ywÑá©ÄÅÎƒÉ ¬¼÷hή¹}†‘gz)ít‘<{ ãáâõ£ >d¦ïÄ ~¾“É©ÿÄtºügnqb¾À·4H;Šýž :aa[Û×Ñø*_$y—N·¡mf/ ®u$†ÙÔéÚu4\m/êÀEω>'Û'ò¥ŠU§ìf¡E„G®ŽŸ‰Bdã¥Ø zàÐ÷Æõ½gÎ1ûön7£ ™ImPir 4:&lîò‚ˆµ]}ß@7öŽœÿÙâ—†”0EåšJ$«ÒW˜Yp’šµÚexŸƒGB4yâÛ6ð!¾*˜ °J0ÁFžˆ˜ThÈa„ÍDLÔDLü „>3ûRÜC$ƒž?~άü18û÷ Xž´” KŠ7ý†O ð4éþË—_µþds#Lã`0'âwB¸.¨É¬ßøtbób@oßá¸;vûͼ¾—³ ê µ.¯ WË®ÛêHË..¯`sV‡P1’m'Çu†ìb6¶çàŸD » ô™ßú´¬Zvhšv6 ˰ôÇ!àt÷¡–3,VHƒ—„3øÚÎ")ÝŒŽš4ÖÕWBœŒl2è¢Tþ²“VMNP¼l%4šðþ°@B£ÖÖÙ¿æ%Ÿ&è}\ìxe¥ðììNÀáK?iz¹ªªªÜ†vØß¡ý¿ÛÒ‰åáÐ˪Z)§àÈw¹‰ŸÁ+EìX„dò ¿iás6Wo9ÆH? mÕ^ÖîÓîËÚ—iNDÉ2¹i霨1Ìl<Íè¥ò íš ’âç¢ÈŽ'Ï©zM#/¬Ô,¬IÚ´þ\z Q8ɳ˧4Äîf7ïŠ6-Ð6ꊕ•d¯­•9fsŸ¦Ëõ­>‰û°Œ‘÷Ï(Ök¶{Uê+WL·¥òþ$f3rÓ659Á†XG³bV+É`CíF_l·¬¶’Àð°—` M•—nì7_FžÇÑnR;Õ¾­"¤vur·£Atúɬ«ÃC"“C–Û2 zHï¨$¬î*UCl<¹ä9+»b‚8ÚÞ?:ïä÷õg΢/d_¿Cꉢ¿ Ò)ö¥4”:A: “Þ½¶`΄å~âÿ 9xx“Vðg©EgÈÌÔ Å´s ]JZñn:àzø_}ì`…ƤÊÍ/ÈÓ2k“f¦/F² ÈÃÍ,Ô@¼=¿ ¿WÐEõÒ{Bôv¸[Wüqðÿ¶É“UïÆÒüSÅ6­&]$%ØÎÔÒV\!ÅŒ!›é ÝÐÁˆxÎõ{.í$\=é.ïÏ/ä«rQêÕRð˜Èꨆd¶NytÛímw²ktµ™ÒÍ›ÑFYà̰÷ ÍF Àæü–ÂDH‘ŒÄ ~‰4Õ Ù^EŽcJå¦pîZÒ-/pùæV[sòÉè½l¬%Þ0¥r~E’>ÁœdάCu²/®ŸòèzÄ‚R+Ýʵ•^•å†jög©ÐšXÑükd–ñ³IXâŒ0š¼kÇ`5¯Ü+F¨lH¨9Û3ÙEk!ú‚÷]íuÏÞve¢y‹2k'ÞRÈ ß–mÏmš¼¿)ò»<™ŸëöÀ^ÜËScx"”ί¢å§,^7Ëkù:î —†Úû‚‹äÛI–ý£åÓ/3E9Llâùûœvý§I3Œ9Û«Ê ¬@·¯Â´4ÍV®V3÷`b»²¿¥ôŸ‰dÍû6Y6›:,¿k¯îˆl)1k/•à®=Nɸ°Ñ.NT‘ €µbxáxÉqEb'äMšâÏ%)4öƒç’/ì]“ã$ކIí.’·lW¡Ï&R¡ÏÃý Ä]ÞÓa–âô1Æ3% ´ü¿ì÷s·ÝOE¬Œ ñ:XCºwÉZÞn?ËÍ~Ön’–wjGêB»e©}ÆK›y'zm+ó^ ÖFz‡¶œl7˜´…ºL×N’þ¾£GÍœ9zTÐí |×ú§2:MMfYË&2 ;}3$ ~þœýa=ø‹ùùnO ŠÕ(v±_é<„ïƒd9=¦Ã=ÒËþŸ-ô3d3ýLßa“ Q¾Ð*Ê6Ç¿䣳9†®í–ÿG>Þò|œ+áâ5[5—Îsû—Õ3—@ }'ø“÷×ÏÉJ[Âääêt(G¦2¨MÅ¥EE¥Ì£=ôG‘ìÞùM+ÙÒбå«IYÜklèV~nêçË~¸smß•ëÌ)§Yó– =lPDCó;'~;yV§:jåî.¾Àö"Öˆ¡Æí ѼErHàSåÍ))ÚŽØ~Dã°v¥$ŸÆ!¼RòÐÚ«îP—§YI§²z®š=ÔÙö»6Ã<ž8¢ŒÃt{O`‹ÆÁ(‰ž–?¬¶‰¬Ùãù ûa7ÜÜ$§­´®²^ClGSO“ýFáy„²jZþÂ^CzÚ3˜&t Ãñ`ɇ ó°ÏP;¾ž¸{¦_÷Dä‡P9‘|h¯NðóήÛl}×`êâ!9wåX}í¯†C1ëX|«Õ¡]<#|ìZÝâ¦#‡6E³òr|×qÂÇ1p„¾¶êܵ›¶®ˆb”g7ïYƒ"Ñ–´˜pÙ_»XèÁ4¹“ês¼­èìaiyv»ÅAª_¾ åÍ3Î|s× ºµ½†^Ðshî¸dóŒ,}¦xõp¿‰þƒMzòúõWê û5Ï8чC·ë4Èx·û‰ùŒ£Æþ~¿' "¼Ž²°ßЋÚ°ˆï+™Oâ±½ïb>AòÔA}ìù¡F€v_ó#‰*¤Õ¦'ÌÀ]¢ð;Èùp;í‰6ªŒHVi4šJ ‹ ÊØ™Ð+î5ú ýV÷Ëmè^\VTŒJICkÌa¬{}&†P²WŽJ“‡ JrÙÛ¸×A_¡ýóŽ„»ç碔'Ë1ªMzý~3sDCWô ý¼ù?Áà”|LeT!nk0×ósØó„®(¿(yªTšœƒÚPÈ–‚g¤?vÒ© óQž')†M†²R“y â'XlÌ.)Ð#OS…ÁTž§Ï/g'~ϳiGYy92y’Ûªòµ…¹ ö\#Fj!e^–Vj5!ãmZœ)váÅS`6]mkÂÓ<O‘v¾8ƒ)xŽÐƒTU =Hwë{JˆÅ \R˜O®˜ë™Y¹mg¥¾´ÆÄÀ{`–tœÄS:Øþž=ô„ › ¹ÍWDžàßÚf½‹ÌZSòÌF:mazÞê1ÞAFõE&UEŽ0®í°o{—Vã^’qö·ŒOètœ(ÑH·U¨M•z£¹ˆÙ JÉ+:Ã:ZHv2÷ŒfVd‹@o†^ a·´z ñ[ï¦(1— 2Y…Ê Rkó³t þÇóò´…¹ÄOª uEEYY¥^À(ó¸$Ö:–Ï€_0z*PXŒÅS1…G3¾R<êiøÅ ~Œbð7/èFõ̓§÷6®nBM¨!mwLÝjK0š+%EsSƒãV+cbR"IÌnªIjØr:í&º!³*‹“Âõ>Ï+pHhF^®©ÈeT†C *eÀ£IÒ^‰Y‡W˜wzj¦3ILä s}ÉÕ‘ÈÏäû¹=²ÏZH'4íÚ_}¦ªÙ³Þ>:V…g¬IYÅ䱩YU‹öÈNk<±Ç¢IÝÅ2F”#ad…*OW¯cԺ䪭åÉÈsÖâ°9[ÊrN²A)Ë–¡é2ù¹Qw‚žnÜuò “ܨHŽÚ¬Üˆd!Ëš[®žüøÂöŽ>úW~>·ž4ÏeàÎ)J¤WÎ~~õù·2ù"üÝj~ÚçÅØ'ƒç/Ž_Ϥ&eÇ¡4 † S¹¾ª”Ùñé“7‘ìþ— ‚"R㇠gqN’„Jå¡|¿´ÅÖ6§2ò²BÏ ‡HJU ˜ä„Íù©Ú\”%#²²ãâÉÓ×”¢Æ½µG‹wõ¨U©Ë3‹6”&Õ¢£2òݵO‡eªI{­Ï³ä3õy{>B12LÖ¬5¥NÜ””³­<Ç×’§ýÈÌ+Î-ÉGž:¤ËË'@yä}AÚ2!x¼Âóx¥&/7i†ÇŠ¢¢’¢"Òx~ýé¾cÍWú|;õKße+2âÖ1)YiH%#jd2””l72u ç,—‘ìáµð¹kâ£"ÓØ­ªŒÂ•H–žÓZò[/ŸÎž4uêìáK×]ÅjËòJtH¦R«TÙÛ³ÒÙæ„³ÙçHG*ýñÉ«›«®O®·ùÆã—ˆdÂS"Àùy[7$¥'¡ ”müðè’Y†Í(%eÆD¥$k²†\Hc4–P™“WL†}ÈŒ,ªÚ”Ï—íÊ>ŒêÐAKÃqsa;2/s:š‰Þ|#ÔG¼Q7˜¬À½G8ÔгéÎä¿zŸ³ÿ5e%ÏŒ£qj»‡d•ör œIUûU²×úŒžÜï}v¡š·¡õ©â¥C½ÖïXègJÛàoæDÿþÎæxC®¢ÀRX…*ÐæëNž»ÿèôCdA–c®!·´° ÉLz£É²qçš9sW/cæm‹°L†§?ÅÝ¡kÛƒ¯z3õÙˆaëü×1¥aŠÚ“¦ê;ëßEü+³']4+0xù™§ lžéöhþéÕ«W?‰_=S$(•ñ »•öíÙ½ÿ@òîÂÝ”É dlÿþ½d,I[í–¬Lˆ'ój…1%ë°î¬8ëÇ+Û“*0¿ÇÉè|në0½†4R.VhèâH|¾à¡E!x·±®ézŸ 2aê¨úÈÛëù‹i1QËf÷ülÒÏ??ûª­%ê´ÃLì$ó|ºdÆœe!Ÿ´Ü¾~é>‹ä/žÞX8+d¢ÿÜë÷[o\yb½d¯ó"žPüá´âw'ë%À&‡ÎÂ;Ѝ¸øè uq³ô±CŽ‹=ͪàß²~}ÝÛøÁÆÆØƒÑVšn#)mæŸSš±´ÆhKiÓ`ßß®>r\ Hž5ìÙW_j)Ñ—¢*Ùvµ!sIäêPFüõ‰Iò2 5´ et; ŽÔ&¢õ‚(„6­¾´C¨_´e©ÅÌæ’äý¨AÄåìÚ´÷p­©q‚oïB™‚‹³Ê Ê Jg9Ò———x½„¨@y^¹ª ÿ+<€ü¨(+-#Ê‹J K Èürm©‘b¢ 7ßZ~>.ô÷Öâ@н?ÆJÓÊÕ$Å“>T˜0†{Ó$â-p™$pCU•P{·‡’:L—•eÐXX>pHí¡ö ¶{J ?¶†t€55ô‰®œó Î¥Eý/= endstream endobj 63 0 obj 5990 endobj 64 0 obj <>stream xœQMkSAI¢Õ ®lñ.,~tQ¤íJ*!´ÁPÄ“÷îK.Lf†™ ö nÝ8‚6*´BBwÜ—”B‡RTŸA®-ÈÅR­2ò¤•Kà¥Î`J‘„Ç)š hA;"çb ä``…ò˜×@*•㬲ù\+Æêˆ"¥ºÚy—Z2¢c÷ÕëE~(|åë( óX™ét<ÂÈÿƒyAÊÇc_ùô2rFŠ"úF)ciÞÂØ‘,Ý[`q l&ÑÍu«­,烦ÆÈbÎÕóª¿þäÊ<éy¡²¨‹#:è,—ÿGaŒÕàcœyVç¼±òøòÛj|áCørÊ/?‡ÉƒÍݽ'›ÓÝÙl:½˜íý~þhõjûôfy«¼ÍØ5|Êã endstream endobj 65 0 obj 429 endobj 66 0 obj <>stream xœ]T{PTeÿî>î^1Ø.jèîu ’ƒD‘ÄTÜ]VÍ)m„ÕÝeåÕbÖ€¯F¯Ú¨åZ˜ H݆lVÄGb*¯ÆWã¤R£¥D»jú.fVÜ;çž{¾s~çw~çcˆJA†bα•äY³,‘ ù¶lÙ5V e¤ i¤²¾‹>»z$™µ«g° TûF°×ƒÁ[‡ÀÊ'ˆ’a\Ý3ò¥Öܼ"atê+icÆŒ}쉎‹‹–”>ú#r ­¹!œ%9¶|§=ÇQ4Y˜A£m6k–k+uæ –ììœlùØ‹-g¹`²Ú¬Ng~‰0zF„ðBTTt$}½0Ûj_R\(̳8 …Y‚Œÿ?BïÈÊÏ^š[Xd-.±Y\ö%BLzæbBæy™I"1“ù$•$‘$¤ò™Mf†ð$ž¼D¦‘JQÑÀÈ-f:³Šñ)Ö)šß+ÕÉÊlåU”j»ªWõ›:J=W] ^ µˆUR$ø;*ƒÏƒ‹Aªm8/yø~ý¼¦Ñ^»Ðp´£õà²dÞ¤ž?5à?³5 s²tÚ†¯5RK¨ágP3‡@PJ»¡›/‡aã¿ÁP‘à ‘£pšzž†Ú}øz|Š-5æ;SEÎðú…¾æ÷›vÕé+ÖVÔ‹gÄ}Î GA=Êx˜–ÜF³ÂIéqáÈ M÷À‹0©˜É£Ç)úM¨æ¯7 O3N›újWoos×Uý@¢A‘UÒó0b*ƒÐler‡«¤ˆ|vÐÍæÌ¨6S¨|>± {¦IÓk½iž6k[h»ØX}¸…C?i0ÿU£é™ˆôD£1½óç_šººôp“’7¾ç§_™dŒmR ?©…Ýïþ Æ]±qƒ[×§Y±9o“KäÆ-Z¥Oš:îj¿É|ƒ’èø¡Âê†A”{ʼ Çj¯Kc)ù“4Ú;,™ûSBñÉ1„“1þ;TÁðóŸî>{BŸ IZ¸È¸ oÏge: c7Y*GìŸçµ®¼!rûã%ÐêižS4nEÂwHx6âUsÂÌÌö»÷}Õ¡˜˜ú@ÜDë&A(ɬܓ6K ~°ã;1Œ2r¢ •¹;l»Kô»]»VŸX “Ó†i{—¹ËŠŸZ–ýFR®c›»T·jǺëÐpö]ô»•“Ä+♊úºúº‹-bW^ƒ¡ ÍòÝåÛÅ=ÜÁºJ¯N{¯]ü¤pÓBŽr(™{˜g\ÃO0:”0Ó¸Nü–Ø`¹][½e{µî®æ­ k6–‹ÜÒÕ;šôÐHeµ”üw*/@Ó.ŸÎ¢¼¡ Ë–õÙU4 í‰P€ÿ90ø‹GúvIŸ†HÑ­Ñ6$/²$α×]ÔÁ/ôt€ýÛAsÉ»¯µ^§u%Ëò¨Ã@oÌ–ª|VP± „F|„<¬0ׇê+ªd5àÜNFº‚|ÿ“t‡ ,Ú+y*aJeú^üü`”?ø½£ÞLÈ_’€Rö endstream endobj 67 0 obj 1560 endobj 68 0 obj <>stream xœuX X×Úž23" 4AqAE@@Tv(› (›ˆ€€"¢€÷ÚÊq«uA´‚¸¡­ îeÜ7¨h T·Öªý¿IO¼÷?¼·ÿû܇9sfΙoy¿÷ý&"J¯%‰ôý’Ó—'ç¤%&g6¼…ˆÒ‡*^†3ÿœ¨É— ¥÷=7Dbd W>ÄTm̯Mý!o%‰ò6—x.ÎÊÏNKIͱ´Ž›=fÜ8›¿f&¸¸¸XÎÏÿ×K¯ä¥i)™–£È`yrú⬌äÌWKOrwzzZ¢eJz~VêRË„¤¤ä$aYdBzò"KŸ´ô´¬¬ÅË-­=ÇX:ØÛOO¾‚Ó2æ/[jž¹Ô2Ð2,9eYzBöß&)ŠŠœ‘Ÿ™8'ÈcÅâ¤`Ϭäè¯% B½³Sfú,M óÍI ÷[¶p–ÿòE¹é ‘_äeÌŸ½ÝzÌØq–6ãmíì'Œrø®ÐÝÑi⎘I“¿ŠsÞå?ÅõË©n5žNES!”åBÙRVT JySvÔj&åC¹RöÔH*Œò¥&P£¨pÊr FS³(Ê‘²¦"¨Ê‰CER_P©±Ôl*šDÍ¡‚(j2eCEQÁ”'åL±T_JŸêGP"Ê2¢úS¨”1ÅPR*šKÉ©AG™P¹ÔTÊ”LåS˜2§¦SÔ*–JɨH’dJ fQ{©ÑhQ‚hWŸÑ}û|ÛçØB¼FüXÏUož^žF*)’4I€v¦ï0ŽÌlæ Û‡]Âöô•õuï{§ïG};ýTý úûôëõ?ôsì· _gàlapÐà††[ « ÕFr£@£l£eF{Œ~0ª5zÔ\ÿþo„Ø9àå@vàÆuƃã •šK=¤ó¤O¤¿š7({ЖA Ü`nÇø¤Ô0¥¢µfh28ÛO|”ki#*ƒho_*â3a1÷/–| ñM†иøS†äWº Kà;‡ h0€§#þœ©Ô "kóe©µÁæ¼Äomù"ç• ò S‘Ù*T¸9+;Ù·vß×ÐT±ãÀÞÃûKì=…£Á‚YeüÌÖÔRã–6÷'°¥ÓTz¶…l(ýQ[öÎ1ÒÖÿ¹ÚòìÞ¹Ä@þg'oÉt\å•á'“žU1ÄŒœ6Þ¦Mt¼KÌ/‚wÜ×—Vÿ˜[‘RXáX<ÌëaìÖc Ãa`û#´S>™.œe¨x !Q áqJت„¥q»ÚÕÁjS)ßnJº]Úú+ýxªª´þš9R%6Øžb¥ü½ã'/?4G ¹ÕÉ'’OFíõ&.ÖÒj|ŸƒðcêQÅúCy‡s÷d D”T˜¾$7;/sýlr“~ÆÁVÈe.¢c«,%;ÈÙ½(Å¥®Z´,gÙÒŒÕsKb‰Zy£VB µð€‹4¾§5LçÝ[±‚»¼!É{CoÀOuAC—ñ§8<Úa8öÂ^ªá0F÷|„éàïô“orç^^uÇ&¸ÿL7;Û°6‚ñ5…Z.l¢˜UÆ*À½ÔøpÔ“´ðë,ñA:µ!ü¨àÚ,®ri-öîÂ"ö îHãE™´Àç!ƒWð \Ï5W¶‹éåÚ³JØ¡=WC÷1qæ6ùô5tqßé“Îì¯F÷Y:¥ áËŸ2Ô4o¡; ’énˆquŒq÷Ç%*E=„Í¿€§?L©…¼½R»™{©RëAÁ3b¦‡ŠxßLPg*­lˆµB ÖC z´¿öL+m>¼ÿз×7±O™ÂÍë7­Fhþ¢7VZùŠkÎlB½“M*ºà,Ùå1oO¶™ÆHßÜN˜SágÍ&`»O,÷>%?uÉtýt´ú›Å ¯µó–g.KŸ—?%£´9ß畬۳á8;‰ÞaÝ ý‘Ý?tâÜ™š’{ú³dó²1Î âz¦aS<0bš£ý,q\UtËÿÒ4KЏñmÍ¿œx‰€1#x¶xùûôÜÜ{ò†ì£“'À>..üaм܅ëâ‘¶ñÖìÀî ïølˆ`îËàîßb ï”ø‰±¥R[($æßÚSÝk‰üðF\Ñ{ßëØTÑÔÀ±.ç’`@†\‘w½°|9J6‹Ž^è17¹øÀrÙÊ’¯J6T²ŽôVlØ4†’òü¤ñ"þ¼Õ!¹ë~ß½™P¥YÍùãM÷OeÎÚ,û,“`Z©¨¥“¨¤¸…”ã§ŒN¢‚>9²¶ TvpÅÎl´€íÕIUà•‘3ææ|-ƒcŒŽá'AnŒÕjÀ=SÞ˜J?B0åXOwV&NKù¢4÷»øâ 3”Ó©×Þø¦ òNM=AôByæÇKæ`<µ[Ép·˜–zDS–Ù9¶®<÷Ðòâ…(žŸd-Ó¥)° N+áˆÒø‚š_©$ü®‰â9_sì\š¶É¸ñØl†ß‘êy²˜úÌ.ÔÂïï ¶ î_*{E/k¬÷»"ä™;Ù;K6eq Åöõk{eûÏDÊÇJn´ÏiåÇ6‰ž’ŠÜN:F8ã^¾?k-éÈS¥¥›·•µ0«¿YYô%bSÖ쬔~ÆA6‰mn)•Šî“µcÉZgmY'­ÉЛ<ü‰Ð̽SרüÕðVå«hDÍïæŠÀjL'žJ²ídí‚;T‡¿Î–ƒŒDÚýbé¹aæ(iybú²Ìܸաh*ŠØŸz.ë‡U'7]$‰ßäW_‘Xçû$Ĩ ý\Z}ªöôñFtõ„ß³>‚¿¨,ms<¸èèUó‡µomŒ´ÙÔ«l,kJE/y}B¶5™ø©Z“a‹›´ý ‰ôu/á©P»9­Q‚_ư•P_¿Ü„/sÆuŒô¢e¸·“™;2žqÖZ3Ná¿·Õi®•Ió<„€à̇š!ºˆ´¨ ÝÀföŠs¾AhÓ ™³sã¶¢bľ¬­úY®1d AÏyx'­+ ­ÁR2ÿ£±à`RÛìë?Y¨ik ªüÛK1DøŽ'– E¤‹¾ªL Ž/“Œ§±µÖ [òndÚ2É¿n̹N4LhìÏ£w€7®”¼§ñBxM´×_bIãlœ-ÑéºFV*zªÖE©VP¯Ì¯Ü7"¾øPª,åèŠJt…åwØ’ylý‡Ì€iÐç%Œ’ñ;z F0‹¿d¶:³¬´6#Hƒ;’­µ±$;lˆu/éw¼ÝZ;‰G‚MOÄ`Cž7ùSAÉC‹w+oßf|® Šuyø¡–(ÐÆµ_£ ™_––Ã-Fí[‡¹iË’ReK³ÖdlœÍ>¡·ß­ªP öѹÅQòe J]^à¿ëä½hepvz,òe¥y6÷Bþ¸×P~ù†ìÛÈÃK/£}¨xó‘íDôÁ—C‹×­ÈÎIKŸÿe4b’7\©<Ú],Wíþ~ëÑbö¯nwˆ |Kz}kÏÕ~ ùO’Ñ4”®ÿß·´“tN‡ï8èGþv ýo7ð^Puºw„j%^B §fþJèE8È|+¤Ô‡ac“ØÐà‰wâ \*yEƒÄ)Ž“¼éí ?¿<,ác*­Õ!¨•’‘>þåÊ­¦Û'“ýdø“0!œÞ:•ä/œòC™—õc¼–‡FËÒ¯&ôEÞ(nÉÜ@VZûù{³y¹êI÷UpYÇŸî3ÒÚà3 ™­ ë!m…¸Oüˆežs²B“äPÁÀ(\Æ©{¯ ¿7^zõGS`A$~V46×vã)|7‰žD†CÉЊ†Êßi˜Ì¿†©Ú×'K4Q¥¢»jñ]bƒU ³ifaVê6™¹¹íÇŸPûŽTà3á–ÖÇbXMîMqÂv¸ƒ~#x…v«h˜ŠÀ c-¸©`8ûyx>«q*÷˜Þ8BÀh0egÑ*lUK(‹t¸Ø1»9b²Œ4lÂ2'ò¨mXoXU^(P>u)wÔ·m/òîêXÞaÉÛ@ãã5eÜ"‚-bþ8”r8½q"NôòÕäT¨uòÁ£«ºKE]Å]8«›ù òY™»‘8Úròâ39~Âú"¨„F¯õ„µ:’ø êú †p²Æêƒ0‘[¦™CÌË'æá5ŸÖH®Ð°F³F÷&ÒÊ+¢ó=PÙ#†£š(Î…d.ˆŽ ʱÑéĺ,«$ýC!È–½ˆ½+[PzÔ±nz¯ªÇPÆÅcû"!²úÕ+¹Nh5äMv«Ä°›ßÌi7«4Q3™t,Ê-ÀCV°B§Ô?ZŒëŸ|£„í‡;L¥ ø5ÐÅmJY“›ŠÍ`òà7NœGؾ62i¦Í4ÇØýÉ·ÝäÒóx€gr”½96PN…þµÕÅå§deÅß—TU²ÒÌÛnÑØÆU^+»‡nšá³o¸à–µûÐI¶þĉúºÊ”è¹Ù™nDÖÍp5'=>/g¶…gèõ–«ÇÏ<¿®ó ‹?×&â]zÄü–.NÛ—Þqý‡Ê®K¿]üëåK­Dmn,½’P•P5û{?4ù-J ÉN^¿Ñ“UÑ[j¾­Ø]^~þ§Ã ˆí¸6-|aL`ŠÜn6¶ž<Ïo¶7ãӅƲæâ\u#ù7•6À0~ ÷‘6ÄÐE×JŽžØÆV1!Æ¢©ì(Fj[M>Ä0ÿyźt¨µ‰+X«é–35rpeЋý'®íf›™°Ýy'Ð –Lu&ÖxËböÒu{ f˱?ƒ\G]]ž½$9³ä|vaA”ìKB¶ùS’ÚBH”LexvÖÊðDa(Ìè˜ÊAA›qµ Š{|zÈ7,·‹®Ü$‘~|S?ÉaV¤Ý¸Øš÷_ÉI{¶sÁ¾E§|[RßmÔË.0ïI­›TAº¥{‡¬ûÙŒ¦´b l5Í ûF¦¢7ßzp×ÒÓ^A¬¢:nzT~FÒ—ò¥kÒ¿*buè‚1í| q¼ƒ¬Ü9ì®÷íÃùy ~¤ “Ø2PßÎál²áÎg£»`×%â]IJ³ε}èH­‰ä1}¶öÄ>ò¤W7† ç{ºc9±{÷̋˖œ‰xÞAì¶úíŒKÛ×xHTâÊŒùò£-!ïRãZõÜ MäßTú´–ìmÅHŸÖÐÿIJÒâû&ÿ…­ŽãÔšª¢»D·eüü`„¦ê=Ýkÿ-EgÙ3…èB7œ'.l ¼¾Øi͘BnÈ;9!0Þ'—0‡p¿ï§ ¬ »®^‰ßür@¾ðÃâ^Ög?þå"wŠË ^!øÏ8Øóý§ªŸ]üá!,UWöô@ù2•¾ç-ø¾œôMë…ƒ§î›ƒØIAdp £ûLy0W&}?%)1ÔÍ›¼–`ù¶¸öÄ.§e˜…]dᣚxÿèø3¢/Üoª½ðPŽëô¤ïU7Ã'‡„O˜r­³óæUÝO‹ò× ‹jïxòw>¦€Ûm¢ª!ä 0‰Û‡Ù_|€"¯\?W½s±¹´ G^ÆóI¸“t-è¸ð†ÅØÑXŽ÷ØÂ ûµ¯ü$/Â3ÂÇBAhÞéÂ&ñ 9åm_;§à ‰“"o¼ê¹ÙøX0{Õ‰øGؘ4ŠQü›^‡fÚ>$¨ {ƒÈ—éý+–‚0ðÎÂåbXDÐ \Ž›ÿ›LtáÔFM}£ ÛÄPAGû$Æ8Ë1¡ Û¼…¡«XæÆªýiÈŽ%SÎgbše?5ÐÑ…%õ¤!gPç’Úð÷–øÄŸ©ßSR++èâ"Ds|!N”´3Z8ó2˜( …rÄ-¿·ˆˆÊýÁió¼ù>stream xœcd`ab`ddòHÍ)K-ÉLNÔõOÊÉ,,M‰jýfü!ÃôC–¹»äÇ«Ÿ¹¬² >sÞðvó0wó°Ìû±@èûDÁï]üß[˜+¦,tÎ/¨,ÊLÏ(QÐ ×ÔÖÖAˆZZZ*$UÂd\R‹3ÓóÔ€Œ²Ôœü‚ÜÔ¼kg êœœÌd…ôœÊ‚Œb…Ä””Ô¶°ÄœÔl·ÌœÌ‚‚ü2 gM#C] aä—™›TZ¬œ˜W¬à£”š^š“X¤àY’4EŽ9%µŒÁ•Á!ìw††GŒÌßWñý8Pr#tÁßßíæ ]y,.\ñSîg®¨9›ÿïµÙg’—ºtKþ¶ú-ðÛèwº¼ðŽßœçì?oÛ9sñf¹•sgÌš¹œC¸¢ÚžõÛo…I¢W·GšTûå†Uz¤DeúI;&ì}xxÎéeûä7^ؽút7ؾï—¿Ü`¼ÿøû•ÇÌ?ޝí›3e÷„­ÝË$_[\ûÍ`bžä)ã“ö›­Ûª;tQȦ´u™Û«tçéþtuÕws¶t·°tn›¸=ôLíŸ%?÷²èÒçWOß鿏´#Ú¡±«¥³ZžïGG÷‚åó¿oû~úÇV¶ß¬¢~X±ê³]ý³€•¯dÑó¿ûÎ÷YÄv“ë1÷Í << pªßÕ endstream endobj 71 0 obj 530 endobj 72 0 obj <>stream xœY TS׺>rrj­m‰G‰´ Úª­ÖY[kíuÆyVP+"B˜ B ÈD 0„€€àˆ¨¨µµÚÖZÛÛöÚÞÞz[;XµÃ>}‡»ÞÛ'!(¢}ë½¥ 4çì½ÿýß÷ý8˜ÿŒÃá<µX’ž*ŽI¼H’¸›ýà:ˆC?7„~Þ/ƒ‰úÓïÏUÜ籿å óÃüëž7"€yö< åÏ`~ŽÜ`Y,‘*Rűq²à—¶l {yÒ¤Wî2ýõ×_Þ¥ð= ^“&ŽMþ‘“(‘&Å$ËÞ^ŒÞNLGÇ&*¤qiÁQ»cv³«B£c‚Cĉb©T’üÒâ—ƒgL›6}2ú1c­8iWzZðI²$xu0kþ€O0 [²z¡"9z‘d÷ÚÅÒ˜uKö¬_š›·q™L¼)}óŠŒybThfÒ®°Š—^ž8)ø•É/L6}üŒì™³f¿úš~ÎëoÍ}C9ïM ›ŒÅ"°uØìl=¶›Š½ˆmÀB°iØ8l6m–c›±ØLì%l ¶›……b³±‰X¶ÛŠ­Áa¯aáØZl16#°'°¡Ø“Ø0Œƒ=… Çü±g°g±Œ‡ñ±ØŒÄFb£°@L °ÑØ,{Û†= ±¥(`èõ2Î|=Äå7Óïcÿ@ÿëÜ |þ!ïež‚÷ Ñôļ¡ÏÝÿäòaø°«O _>ü›§#ž÷L׳˞=°…?”_3â‰NòòæÈæQ[GýøL`D`ƒ@$p š—F z:èÍ Ú ÿ~îèó3Ÿ¯NÚD‘QôQð„àú1Ï™<&nLÃØ'Ɔ½NßNßͧ8p(?h¢åä9¼XJŒeU&“ØAµ¶*Ǭ®Ð–HÁÄã)@W¤Ñ«4š,”FU¥Ú¤³q®—¿qðSQg+¬7㠱Ȥ7©«”&ô¨têlÚ Rb*“ÀóØrž‚釞gõ¸²GìgÒY E¸T´Ò¤6jJ$€‰¤åL".õ®Uk”>KÍ:³Áˆ÷pfj/?hÐcwo"¾.«Kƒ7õ]]†×ŸçÂpŠd¦ã_ÀÍ\d.EÑk)¹#À SGñ¯ÂüRr3ƒq'ài@cÐä°ZëñLÀ뀕=Àlªö‹P—¤"‡YôÚ¶ÒÚÐ,Õºªl3Áï¬Ð ‡ASq-0€ÃøÁ¿}üaMk—°½¡Öê‰&”Í9¹ùyyB©€<}ª*Z»]dØß®P¢Ë¼ß>ùì—öªIج4甥íM+ѵ¨ôµF›¾2¨?—- ‘ø'¡±QoÒ 4V {åês„Ô ‚rjð {·á¢÷ÉÓÑ]Ê ,îÞû>-âß€ä ·™à5Qʨ!ÿö1†à=Œz÷s£w­r`°O5æ>L\ô&zýåÁÄ 8:èe:’< Ú4.ysFíO,]>{æÚžk­µÍN¡ËÑf:¼Hqóæ·ˆ=ŽÃäA2Cî2/3ãÿq(ÿŸƒœIpêµ_jÇ„ ‹ 8wFT“k(ÌNMK-Øf‚P·ì"Qz“¬zûBÇu$çãª7žÑS)Ž“‚ÿ…’ê ý‰õ,ó<3y¢ßp |òáópÎO‰Ÿ­¾( íYÙ´üïüc Ãc†¼>eâËs¿‡\èãûŸEýIŠÜÛLù}B‘12E‚3úsh'þ ¸Xòt÷‰†Kà:qœ§3þð)f¼‰ó›—¸ê…T/Ÿ¤ùpï 8 l‰kW¿¶Ûy`kNœ$11!>w]_q@5õ/D÷‘è§á·$œ4ˆî“ý¤N¼­©úܾùúv¡Õ`.*„ËY×\¯²K+D ¦ÕQè­'nœ*Ú³'LÕZ«]æ8£Þ0°†<$bêÃÇž‡Êå$`D¸ÇhŠ‚«(B\Åï~0n#Æ¿=ö®“­ûkŽƒnúMÿœá ™ìAÇ#ÿK8Í÷‡Cñ«Gâ6nŠŠ 1C½VM€hg‰àë;l•œD–_ëpV¢)Ó˜TvT=HË&PZd*p+j¤@TZuNŸÒ½Š€>'œ‡«Ë4fU5ZÜW¯æ#ppbïÞ_!Qÿõî ßõ~'ÀWü2˜ò–_Z A¸Ƙx¾ü˜IÀšà>$ ˜ < dêÙÂV>$göÿo‘u>Ò©6²pÎïüüÇ ­A6`ίÊAóm€hª¯w×è¬vQ¢+Á"@LÖëúé; ÖPbKK-ÈÂôzɿݶ;Ò¾%ˆ=n#`™GŸ>ZÕÞ-ÚÌ Û¿QÈp¥#ûÐLJœ/¿ø]ÿ¬~%üj”‹Aˆûø·Ñlòów×/X¼zýüùk.|rýÒ…¿‹<À3ÎE)p  p’Tôdx…´¦Û4È Úh¶¢0²9ÐÉC &A¥Ñej…»kv#Xôž½ñy¢a°Jô¶ÞY`×9µ•Ù ‡…ÊWm‰ªt% ù™-¥-%(  ek½)ÏófšYY­­•ÀXj1üc™‡N©ÏAò‡ _v§}«A”d¶,0¯2鋳˔åj05'ì§Ž´iän¡O ~BqŒG°æeúhΆõ>šÏÓãkB”ˆúûšÐö@‘°šp~îlõ8ØjóíÙöì½¹@L0óYé9í­nÒWÔ˜ƒ…‚®Æ©%ï Û!ß-ì‡U„©œH%¨n1Cz&_<\àÕ¹&uE^I* rð¨öto;|ýÐþà=â·é×™‘B&í/iÿ¯°dâ ,ɽÏÿ0ÕΆþ¼ËÝ ;Ã׾æ•[Èä$ŠÓ†4îq¸œtmvÐèm|H¸˜uù£S˜ Ác²vk#Áj^%nNteç‰Ë<ÐãèikÛ·ïXõ;ˆÊzÒÍqÆDÊuF­_ØÍÅ\>ç$Ì:6dµ67-ÊaÙ^+SCÃBC‚ùÄXÚ¸ìðΓá_'ÿ >§«þ}¼O¢—#RÁQöij¤²ÂG*x ¯TVÜÄ«¨‚Y^ÒjrQF«A 13Ž7MÌ>¬IÀ_½ ñÓ_ØëO Ø­• Qx½4OgÐd wÈ"óBA TäVê+ &àì'¼¿qP†ûÁ{,œ… ¸v¤•=âsFÍ | obªû\9Ϧ$3t,êÕx¯Â$¾üÿÊâv“èâHqimìÅå@£U§ÆÕ½eÙŠ–?3eþøûBO%‹Z­ê+yæV8óêrmrAìÜ¿pÅêæµPÛ€®‚àgš49eÙAg$¤+x;ª3öWWìuØ…üc)'Ï伟¿õѯ§3G·Šcl³ªØòKäæt‹ªùçhOÛ{—NH£MŠ*´F­MoóQ–—uÐUr¨YN:¸¯Yœg³—©ðÃàè'ÔýÞ–š´NRR÷!8 ƒ“e-%–âÀjô*X_»óPRkÚ‘œcà,hµõ'ø²Èyv—Ù<}b™¡þ  …õZNBÎÈXê4eÛÕöÙ ·ôV2[x}Ï=¼üµ6ßÿÑË_9(óØQC¥'îÊÇ‹ƒøýŠ7Åää݇æ'Þ2ö¦ÉÙÇ£ÊÝ^þøÁú¥Þq荒·y6óõ/ã=‡;Q«Çgáp’áÀnÙ éMÿ„ <™ðnÃáÌ·Ü_Ñ/ø-·ú/6 zü&f¯7àGŽoI<ä8‡¬±Ý›–›S Ùï”e(‚‡G EÄãÌ,$„†þÄuûLÐx›ñG&ä´>¾ÈÞ«±fÏ[¿õv!ûŒxK9Dûf>DŸ?ºÃP'Át?ø6\A^cVpoáÿ„ßµ•ôdX;5^/LÁ3€²P«ËÑh•¬óFÊsNÍL^ gpgâ똻áäF`D÷ØLƒF>ìX§ä$Þ‘K²CÑnøšù.ð‡¾¬šçË*–’CðTßÕ”@”*[?põÎ{¸0Ò_—À_sÂÅ·.QG~ÅϤGÂ'<<^ñÈîÀ¿_x–y:|aNV¤PŽ$ B©dgºÛd+Ùkv[;ËÚq¹Y%Éx»*KÀ ’²#}6L{0œU‘S›³6ï8»»º5œWÀûiç·wmïÚä\ÖmÊb¾NžðL£R«Ä ˆÙi”D*ÎÙˆ™kÏ~Ñfkkl±3¨“Þ6›Í•n”¹g` ù=ÓÂ…Cð‹´¼¥Ì^š[ÜQg„3xÓ8fp_Àç3±°‘{€y@Üï¨ú7bA1Hª•-—Pæ£üA&Ñ”ƒ%ú‘!Œ¡0E]/PÙrìV«±Ú(DˆëdÚôŠ?‡‘é W#‹]Äpv2#@0`†t0¼k ·y[er[s]]“ÕP®®…@ÿ„ŸÀ/à>‚þæê’r`%šÒë’YІóÐNÉééÒܽY)ºÊø·NãÀøÄIKµÒ ¹„´.½¹ÎjÚ_/¼‡…#À‰…¼%›|<Ç’æq¥ÿ$4A†-Q¥hAŽ@Z/kr˜ËÝáéO/vJŒz' ®±©²À¤rŠÂ®ýËÝa±U—À%kHÕé3„³%Ü#‰Ë¶¿÷e'RNršÁ›íÎ(/,VAjºL*­ÌîÌcáfùðf‘p "$ë#¾`}?ÎyXÕê4ÙZGÕ2s˜±ƒò ²g:Ç2sðÇJ“ážïh ŽþËÀAš󠂨»˜¤õŽðq߃=–7ÏØ Dw~G—‹:MôzÒg—Ô•Ô§4¦Š™¬B×Ir™yKpô´¸¦ØÞÜÿ4Ï +JaŸ¦ ˜Ny[C³ÎÄÂh-®Ã4Bö~-ƒÌpáyÌì"ÐA’3½¹Í^wXh…ó¸³ ™†”†Ô& @ µÂl-+¯¨/®²ÀyM(.}5ÑOn,¨£âÊÄ¿s[‘Æ}ò œ@"›³¯±é_BÿAš(‘-D£¬^*ËQ‹UBæ“ÿÌÕ( Õ@-Hm¹*ÍíUlŠ«(:œâ|Å~ àGÛ C‚ï$7ÂO‹;"k7‚ 2S&™ ¦¯ðÀ4÷Üž°öØ™çÀ9p¢¶½£ç´ûøŽ`¾ù…üµûÃk׺—½ d8¼¶/[²dû‡¿ze®ýH&ns´:'$ ’2—¹´¸¬VøM=·wòøGèVíŸ}<íÇûÆüôB*àS*ajÍä8ŽËoÎlÒÔZF»êš6mJ³ðp]kÉê²ÆT¦° W˜b•—¡»déÜĊ̦í"~çšXñ¦å£'}¼9Óbt¶ùq÷Ýǧ¦Å­ÎŠËᆒZ'p»Î†=¸Š o9ÉïÌËÌ+L Ú,­=ØVÓ´¿ZÔ7y›B è ÈìNM‘7ÿ~¾§©ñη ÀúMûо~£<6Z(NL‰ DJ}z“³Öj/ZZ‹KÌÖ:×þªC€¸Ò³{«8+}Â4ÑŽ]™Jf#"øÒ±tÄÍXAð;RQë)”2‘2 ¤Z¤(Ù Ï"©NÞ~õâÅkB8‡šÊ®ÒÖRõÍ %Ëž°7¥TïéBò®­»¸4>]ž$qd4„nƒQ ¤3Âʲ’e}ØIøR+€žD½‰.ø5l¦ùdRzF²´6«Í :j¨485ÿêñÌæ¸=£,/5K)MÛ£«@RyŠ]i.(É„$=]²õK1|ú›3¶¶.Qw‡ûp\ÞÓ3¿“à}å «óìhpXÑ•°/æ`Xí @¿ºAZ'k²U–›L¢ÖýnËY@\=š¼-*jO„×ñàR¥bŠŠ?‡ˆu}ï8îØÃ{üL$Ð7 ôªv8À;Tì´ƒVïÿ&Œd&âp"ÌçzµÔÏ(ÿŸ¤àÔ’HtV¤¾©ÂY¦Op¦5›ÊKÊ+…G뺬H€~p,bá¼È=/¬L26&‰TV]i6 ¤²té¦[;á°Î;Nt‰:Öžç 8búÌSÉb}^¬·¶a ÇÜãÐËÐé»ágd=|úÖÍß ”Y ªõfƒ îúz÷Ám®¥S_z}¹h몔ùHæ.®yÓy`Ù»â¯ÀàdmÇ¡öΚS๯޻5&sk¤ÐÌðȦ“=GñöÑíVÆD¯^ÛùŠõ1]‹|º.3±wM~–A 4‚”†ô&WeeG¥Τ%Ü;½Kgò2ïÕ5¥žÄqráÄÏ_?ÈQ‘ÍÍÉÍ{D <6))6¶%©C“Gþ>öz#ä.\D|EM£`"ú1Šž?&ù·/r=;ššrƒá1ÄÔ¹ãBÝ;/F ù÷VELJ­|sGÞ¼þÇ¥øÓ« ™aP‚]îŠ ß»zõŽŽS§º:.‹·?ÿÞû§¢·†GìY¶,¢óTÏ™C—=G߃cØXp ʸ•ð4)cžž:s Ѐüâ¼½Êru‰¥ºL&Ù}<å£[_ùòѩˮÏÀMpMñ”ã1®mŸ–ƒˆÌØhñÅV°‘`¸_Oý½ç@í©B5ä‘ÒˆðØ€Ø²³ûü•‡Þ»Ò±gV{2ól‡¦ÿñÿÓŸEK’ýë €/=Ú­5Èìçú?ßãs <Ç®ûK_ß!w$IwG·Hkq:œìÚîYZŒð.­ A“Ñä.n.q8–´¤;Ä!ëÖ-2p¤zmJaÛF$ƒ${Vk«°\‡P‰YÈ[rqÝGmuŽ·Ü™\"””hYMàœÎ·§5×»Ëíà¨ÕöÂVÚ  Þj²‚Z(7T–•êmJK6Ðua®¾wH/b’,zK¡‘%âjK¹­¸$°´¢¤”ZUfõì&¤þ‰"†ÑsÉG -=²‡1hFØO ½#˜Õÿ¿•Ãåz…8üîлO~o6ìî°§0ìª Œ{ endstream endobj 73 0 obj 6491 endobj 13 0 obj <> endobj 15 0 obj <> endobj 74 0 obj <> endobj 27 0 obj <> endobj 9 0 obj <> endobj 34 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 14 0 obj <> endobj 26 0 obj <> endobj 8 0 obj <> endobj 33 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 75 0000000000 65535 f 0000035169 00000 n 0000062084 00000 n 0000035045 00000 n 0000033589 00000 n 0000000015 00000 n 0000003719 00000 n 0000035217 00000 n 0000060740 00000 n 0000058372 00000 n 0000061551 00000 n 0000059142 00000 n 0000059666 00000 n 0000056947 00000 n 0000060225 00000 n 0000057452 00000 n 0000035258 00000 n 0000035288 00000 n 0000033749 00000 n 0000003739 00000 n 0000007380 00000 n 0000035351 00000 n 0000035381 00000 n 0000033911 00000 n 0000007401 00000 n 0000011260 00000 n 0000060441 00000 n 0000057992 00000 n 0000035433 00000 n 0000035463 00000 n 0000034073 00000 n 0000011281 00000 n 0000015355 00000 n 0000061327 00000 n 0000058934 00000 n 0000035526 00000 n 0000035556 00000 n 0000034235 00000 n 0000015376 00000 n 0000019597 00000 n 0000035597 00000 n 0000035627 00000 n 0000034397 00000 n 0000019618 00000 n 0000023183 00000 n 0000035668 00000 n 0000035698 00000 n 0000034559 00000 n 0000023204 00000 n 0000026999 00000 n 0000035750 00000 n 0000035780 00000 n 0000034721 00000 n 0000027020 00000 n 0000030080 00000 n 0000035843 00000 n 0000035873 00000 n 0000034883 00000 n 0000030101 00000 n 0000033568 00000 n 0000035936 00000 n 0000035966 00000 n 0000036029 00000 n 0000042105 00000 n 0000042126 00000 n 0000042641 00000 n 0000042661 00000 n 0000044307 00000 n 0000044328 00000 n 0000049692 00000 n 0000049713 00000 n 0000050329 00000 n 0000050349 00000 n 0000056926 00000 n 0000057903 00000 n trailer << /Size 75 /Root 1 0 R /Info 2 0 R /ID [(›<’ |¼ÆB’ã\nT²™S)(›<’ |¼ÆB’ã\nT²™S)] >> startxref 62286 %%EOF simh-3.8.1/DOCS/hpiop.pdf0000644000175000017500000003554510040102673013210 0ustar vlmvlm%PDF-1.4 %âãÏÓ 75 0 obj <> endobj xref 75 11 0000000016 00000 n 0000000885 00000 n 0000000516 00000 n 0000001147 00000 n 0000001379 00000 n 0000001882 00000 n 0000001916 00000 n 0000002137 00000 n 0000002213 00000 n 0000004327 00000 n 0000000718 00000 n trailer <<6f9ac1ba678dd643ad7d22da1a522399>]>> startxref 0 %%EOF 77 0 obj<>stream xÚL1@@DK¬„F/q ­8ˆ›¨€d …BI¢s•ûø‹„Iæ¿ü)fÔŠ’[ó)–Lá¡G3ÂC-Ú~?¯!'ŠšvwΦÅ&RšB_V*a…ឤkÈ¡^÷Ô] 3> endstream endobj 85 0 obj<>/W[1 1 1]/Type/XRef/Index[14 61]>>stream xÚbbâ``b``I ÀafÁ endstream endobj 76 0 obj<>>>/LastModified(D:20040416211632)/MarkInfo<>>> endobj 78 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 79 0 obj<> endobj 80 0 obj[/ICCBased 84 0 R] endobj 81 0 obj<> endobj 82 0 obj<> endobj 83 0 obj<>stream H‰´WÛnãF}÷Wô# ˜t_ØMr70¶5-ÆcÇ&YØy ©Ö˜;©eSvœ¯Oõ…å!io€`€±L‹Õ§N:U}vƒ~øáìêb~‰0úñÇóË trv±À(Wð@ÿC*/O*àù¿àùWur¾<9[.1"h¹> pˆ1ŽÐ2Gö#ƒÇÏú½¥BëŸÀoËbjBÚO)F1æ!h¹=ñ~º¹§”*4¿¾Aóín#·²l²¦¨JõDu '?¯~Eþò¿'³åÉìJC=À'-ü#x婆DÜáæd‘â0ŠÓ4чßyçÕZìweñ͸wŠ( >WO…—ѽW˧BÉà>ìjý4º÷‘ÿÛòßC@è0ÍþG,B‘`Ì Ç¹-vYÙEe£ééwóWQ “š·Øo·YýÒl¾€Ã˜¤iŒ8 ™@8LÒ4¨–'ëWGãéÎ$‘ Ó;w¬L|0\½N hQb`f:œ¯:}Mv˜ñ&˜p[úVxò\*…Ô‹j䊞ùÂC›¬‘èIÖ tˆªµ)¹•95õÔçDæÆ”c¢K¼¸Ëb+Y ª9ÏT‘ƒ^rrÁW5Ï•IÉ#¤‹Æt´  DPuWWcU+´)Êoéáeh—ÕÙf#7¨(yʺ DˆDŸ1Žbس’±“£tI˜›8 ˜8ºQg¶C¢`ûAäÙÉ™ÎVz:‚MºIÐ;¯Øî6r+ËFÏ²Ì ÎeþÍ„;,(&[­ ó×Â'žÉ9Ÿ°DØ[`[·¸ïàëÝ»~A÷ÞªB° ?åWT¬M"yµ/T(Ô‹Jµe½ {DòF*Í-@üCÖÕ½ÉèO,Õöf endstream endobj 84 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰t’QoÚ0…ßó+îcˆ±’€TUZNVµjý0iì!K x%¦¬ýõ»6 …”’‡Ä÷Üs¿{pu5¸ŸÜMÂõõÍtÁ`òL¡¨ñÂ=P:` ðþ+Þ/ëàF!(0‹ ¢„RÊAà¾øÄÞU‰uï7<‰ ÷jøSÈ舌e›àgÈ ô¢x”„b%»²]-k°xÚÈ©^a[+ fkUï—øÖ´MŽm3×–ÈhŒ?×± B£çœóR*¹Tµ•ÌC¥ñ­óõúµï›ÜBOü¹ÈéîÍn¹êÂr‘`^å »qµDÎL)JËòSVü"+;úÇ®”S(üL2¾(y.Ä8‰qÃBÿìá.7 endstream endobj 3 0 obj<> endobj 4 0 obj<> endobj 5 0 obj<> endobj 6 0 obj<> endobj 7 0 obj<> endobj 8 0 obj<>stream xÚäW]oÛ6ý+ö²>´â7% 8Í$MŒ8ÃŒ<¨ ›s$C‘‡æßï\^ùCiւð ؃}MêðÜsIÊÊ )”Jj¡¼0?ƒ°F U ë*¡*d%´•‡Q¢ªJ¡–N m„R@h ëÐX­eÚÃ+t€¥.…2ð ÁgðeàÓ”VòeÀgC) øH°T¹R >Ocðù€1øb0à ႯTJXð•Î ¾ þ-ø*glU ‹P¤uH¶Þ#Y„ªJà(%Äe‘ Åë$RƒŸ”¢Â!t«Œ¥¦¢8ð9 ø@p­]¥§|¾|Ixð…R ¾Eñà+ƒT² õóà«> endobj 10 0 obj<> endobj 11 0 obj<> endobj 12 0 obj<>stream HP’s IOP Implementations: 2100 vs 21MXbsupnik endstream endobj 13 0 obj<> endobj xref 0 75 0000000000 65535 f 0000006996 00000 n 0000007226 00000 n 0000007833 00000 n 0000007883 00000 n 0000008060 00000 n 0000008155 00000 n 0000008280 00000 n 0000008412 00000 n 0000009574 00000 n 0000009608 00000 n 0000009632 00000 n 0000009690 00000 n 0000013349 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/ucode_bugs.pdf0000644000175000017500000011453110127355607014216 0ustar vlmvlm%PDF-1.4 %âãÏÓ 286 0 obj <> endobj xref 286 13 0000000016 00000 n 0000000999 00000 n 0000000556 00000 n 0000001252 00000 n 0000001501 00000 n 0000001887 00000 n 0000002432 00000 n 0000002468 00000 n 0000002690 00000 n 0000002767 00000 n 0000005188 00000 n 0000007858 00000 n 0000000818 00000 n trailer <<63d00f7e8f02c6498862179c233423e7>]>> startxref 0 %%EOF 288 0 obj<>stream xÚb```b``¾ÇÀÌÀÀ!ÁÀË€¼@1 äxÀà0åÂÉ Ìgô½áw¸ÿPÖDue±Ó¬pË¥NųœÂMT·›¨Î*šÁÀ`Ü€LI006 kêåc`ˆ|¤9˜l— o`œüîšS¥¼xŽH'ºeâ€f0°30°Øii†‚°«¬Àæ'…i ûùÔ 0¯ç%3 endstream endobj 298 0 obj<>/W[1 1 1]/Type/XRef/Index[32 254]>>stream xÚbb’``b``Ń3Î ƒÑø 50MN endstream endobj 287 0 obj<>>>/LastModified(D:20040924220615)/MarkInfo<>>> endobj 289 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 290 0 obj<> endobj 291 0 obj<> endobj 292 0 obj[/ICCBased 296 0 R] endobj 293 0 obj<> endobj 294 0 obj<> endobj 295 0 obj<>stream H‰”WÛnÛH}×Wô#µ°hvóî àKvdz6bŒ5Áñ>´È–Ä„"µlÒŠóõ[ÕÍ{HŃ‰Äˆu=uêÔåùå—ËÇÛû;b‘_½¹»%‹ËÛg‹Dà"£lAIÏÿ Ïwrq³^\®×¡d½]¬,Ó²,?$눨Ÿð½µ$Ô¿÷uA¨gZL™ÔŸB‹ø–kZYÆz/È-—‚|Üüü˜H™d;òôpMÖ¢8\¼¸ Ëõ—ŇõâÃ#†Ù…N›Ð¡M8ôÏô‹ùÊçcy”Ç‚ÜT;IîÉoüUÿdù)›óĆžh¿^S¨ÐÚ¹öì3>ÛÏÆM¾!ÏÕ1K¾.WžqA˜³zdz,‡,ÿ»þ}ʱ=éØR.tæ¦ ætšÃ\žÉ`Pæû˜NKÍÎD£”å9í“hÓQ¥ñ­ôËC*.Ä1呈ñz \OÚZÚ@•‘œÇøK¼(Gm›Yü 7UÙ¯g·1™­¯~Öoh œç0dÛÖ´·˜¼wòM‘D0‘j³ ާz/ÍÃyVTObÌöD²s^ Ñia=·~ôâ:ˆd·¾öIŽv‰ó¨:våˆVj²™’)ºè4ìv¹ûp;‚­ÔÄ´ËK’c÷ÐŽ~eØ$¾y7OL_MçÉÂf>ˆêéÍAÎ\dtZÎΔ œÙÔVG­L¯ñ¶@r] YÂ^º"ÿBv|üÄûÃÓåÝý§§lSV :aŒ*¨·pêp£ÓòxŠ3Ñ&8‹36­(*%, QÅ[Áy{Ójâ¸/‹Ê¢® ·9ÐY%˜ûžö|°fÛûçZ.ö‰Ýƒ:6XÅÝÄõªãqœ¨wƒÍ­ýi;M¶ÚÄ»¼:+ETVФalRh6ß*¡¦¤„Št~¹&³[ùùô´t»é?]ÿuA -µT'µ £AkãñY¬ïÆMž«°ÉsVÌNT#ÐÊIn?Þ|| @ÀpÝF¢^J#õ̽ݨɘÛÊçû’+¼&X|ƒdòzNÅ¢"¶GÛÄÐ`ЮP!Ö(›+¡_¶·XŒ5 0õ›J¥£þø‘o’ êU~€M;¦¢-eዃ@€Ø2eZƒK j\yQÊYâbÓÂ÷ŒÀàäøîùµÀ¦¥ÓûÇËb¨©qNêñRõbPzÉl¥Ñ2E"„&À£¯×ÏÆ6)`§Ôêqè%XRÜ2dÀ¹·ÏÇq®ÿÑŒ)l^VÒ·é‰mG5lî6¯é2ÄüfRá鉿¡µ˜g;Q´›Ÿ"½6L&»ìe©•¾®Á¼ØgHw,¥ãZëç(óÉ¡…³çE|âÅ4Ôë¹…‹ÒnZdu<æE©””é îaÞ&ÛÛC´™¸Ànm|6xü¥‚¨Þ%°lz4©Xd$MU4ŽNÍþaEó .p¬¶]5„Ýüt™ùmPµš†sÃm"Òc¶\y†¹\Ù°‚¯u{e•–º•]P­0×m »¨¯ieS£î„Ò§N„), 2IQ×ð+Äÿªw³L¾abE·3&ºÂ{Êh 'µ¢¡T…z§`>ÍÇ?žñ¡r8sêi)ëÈÛ<ËS^«°±yÃ@R¤¯õIÐ+ž60 °íÀŽÂuM†¥q:Q š æ}9GDŽ9ì¥q†óä9}ÃM‘g¨bÔÀ]hÚþD›L õŸ]‡­—0PËÕŠï:=à‚Åó:õ™ÚOdé ¯úQ…­r¦y~¬%³Þ(°ÝòS}E¢?Ûn˜:·y+¡i€þ,ÙlR!5mj´´F¸û-:>D‘ãrå(8`‘Õ±LÀ&ìÑ­¾më‡d Uÿ/ÀL÷Þ endstream endobj 296 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 297 0 obj<> endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰”WmOÛÌýéþ‡ýèTĬ×亮%åy¨ E©• WrìMâ[Çε¦<¿þžÙµœ”¶j1Ïž9sæÌé-{÷îôúâjÌ8{ÿþÃø‚ N/î9‹+|@Yç‹¥øü/|>¯&ƒÓÉ„3‹Mfƒ79ç6žcFÂe“ ½6©˜Åéë?ønR2K¨pøræóÀ ñž`“åÀ(âx]–29aõB²4¯êr×i‘³MT±G!ìUÿNþÛœ&è0Ë5—B'C&l½Âï9x¹.T”¹Ìee¬”ó´ªeY°(O˜ ¢_Y¦å„aHŒÛû¯ïf«ä½:²’µÉØùÐ7Xý´JcÄÉŠbÅ6Å:KN(‹5‚Ñ9y:f’Åø ¦ŸÈŸ2?aÃï“O¾sÄx`$éÏ4‘lúÄêM¡Ñ @Þ¼š›œ~VäiUæsÍ#ÊûG–›%“xéñ_ÅLÁÄmNü®¡.ÀîÙÆßÅFþŽ^k/ .·AêÒ¡0êE±ÆÝèæ¸¦°±³¥Œ‚<êâîVƘ,Ò ‰/àŒYšKâƒÇVeAÙ¤FuG›m:¶ -[aöMÏoé²Ã®ÑDM´‘e¹^ÕDB]™&,…T mQÇÙCؽ<ÊPýŒ°ÍËh‰k'Mø:Z®dÉ6i}°`X4øê䪎jüýÄ-Ó\}Ô‘ÙT¸>N¯©a·MlµM¼×¤\¥ŽÙíBOx&zÔñT )zCö_b/º%L›s×QÑϳLµÖFâŒI3u£¸@KÐçDy\´I‘ XMbB·¥ÚÄmÓ‹®ÌðƇóñõpç7 ×J¡H¼Ã3ÑatÚ†Ï*ˆ¾eëjÁˆ­Òކ\¦qY(xÓõê±­ÜsâBN\ÞõƒA€ÎoX"³t ª%zkåóL&;uí£j˱6ÞÆ="ô¨4ë8Fl1ÞÞVj×½L¥’µJ¢Ä|-t}dÕ0êâí«è"Z­dŽB|%eëJ͹QÕýÕE”žöUÉRUô¸Å«5ºy¤/ˆS5¢;š> 9ÛFÕUîì%Q nå¬Hö3ÊÖRUï–[e´s÷°ƒæ6òÇ½Ž‘9é2Ú¾$öÕzz€ÜI Ý]+CJ3‚„k8rÝkëÁ9jbîa‹¼. Ý59€œa(ù›‚§ÛFѰ„éÛ­€ìOQýv\@æ%šì xØ"®ASGÅÃ9"[âsïex0ÌçwUx5P¤q©2S8šûªz—¶-™×ѵÑÏ œ&7W÷¬X©¼T‹tm_Ǽ É®v‚m½¢‘_…{9 aÔ t kÂu;¾Ú"zV,Çt½D/Ê©*-t¹fé¯#¥rÿ¤TN@ç-ÑS©û:¡ ê)V—RÛyßwHéµ›{@±$»ˆª¶QÙuZU)  Ííçs6‘%5Ær8²·ì¥çüþïSü»PÑbS…iÙ!8†Bš¶6q,å`ö ¿ÿê¤Ø~nûA½‘w“@9â6s…Ôêf£bEÖ‡DvûÔî^ö “ÿ`(¿È•voEžÑ&´« ÅŠ¼Ø %Isn6;VŸæ Š}ôÀ§–rí£mk=s×ÖNßÒš3a†aKaÞ£ž f¶ç™Žêù`œÇoÙPØ! þœáïã¢yÀÏÍÃw½Hø¡£]×´ÂÐÕ"÷o‹¿iþðƒWµ{è‹íØfà†A¨¯…®BБ†ÆÃðkÈÿÆO˜zæìÛ·o»ÿ¾¾¡ó*–pM¢Jh Ó˜ø]zͯo€D%óáîò„Ý}üÖ€La`œ±/²%Zù-»»$GxwwÁÒ»/ÖeŒ¡EΟa™ûê 0= {§€µ~÷õŒ°6¾Ÿ°1ìEšëVú-:ïuÙ }Êžë3›‡ð4V;ÂoÎ/Oo¾ÜŽn/›Z >#8Wc Òäø>¦Ae*<ã|> êÈ|Þó"DQÛN9ÜýC¹¢ì…¤±–zÕ™BÓŠ$Ñ"4…ö@yÊfbômjaÅd·JŽÁÅÅ0ˆ•ïúï%eçíÁ{ô»^NxiJnŽäFðß)UQ…ë›ÚT<cÿþš¿¥~ñCïK'ë2¯H¬!àx‹É_2^Óµ«—è×Ñ>HŽk‚Û~¿pY ¯'üÖÚŸ±;9O+òñ•¤B°¢iråÇ›Î>”²~Iíe;¦p_û/¶Eb‡V‡DwèAÿ.^­¯NÄr,]ð#úª5Ô|¾Š?ÕWùx–Ûè<ÔÁüÀH5eÝR¢•Ö]e}$°eS¡Çá~¼Z[‡Ý±ãÚJrfù¾ß²ùŒaÌ’³Šf3¢ ~~MÿZôR¶¬À3=NNøXgö‹às?Z/:ßÂ2Õ²t/œg™¶G­«eÊBm$ylE~(YRvk§vu>\qCQ´HKV¬ëÕº®NØf‘Æ ö(„µIæüf,|'ú ¨h¨ ¹gøëb.I?•Ï^W´S<±UTÖi¬ü(=vôà™sy·t®³míl0£¯ðr`zžÓ™WhÓ£QéæoéwÂzv"W_Iø&·ž-1ÉÎðÝÆÈ寭;o7*¼æ7'GIRb À¤ŒÛ)Ó(R"ó3µ-PZ•ª˜FmD;Ð)V÷àD«2z+ zrs8­íZÓ¬´{“b£BIJX•iTKÖÀ–Õaùꟽf–’ 1ïxgôÏÁþõd/<·©1\G‹@»Û%’6¶”ñ"ÊÓjÉ6QŪ‚­ 4 „Á˜­3Ý4©Þ4>ÑÖ›Ö-¼ê—û¶·É›ªë=eVK}γ‘n,ïGHù†Û­.DTNÓZm–Qù„€[ËHSµ³^ØF]Ð=¢(Z¨>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰¼—koÛ¸†¿ûW𣳈oÓ4@›K·‹l8ÆYIPe%Ö‘º–Üžž_†”ï•9(šÀ%[£WÃ—Ï nÉÉÉÑŸgÏ #§§ïÏÏHïè쎑¤Ä öŸ”IÞ’áõxý©ì½öކCF€ {£Œ1E† ±#! ~³· KÌÿ‡gÃîÂáÁ0²ˆ¼“ás¯_¥É$Ïþ§ä[\’Ǹ¬Ò©&qNª´¬²ü‰|Ϊ’Äù˜ ÿéÙçðÐ>æ¾ÿyçÉÄþ$ÎÙ»é«4/³¯éô;™—é˜d99ø4üc¡VÀR.WµÚ(HcPûy゚Ï_æU\eEO1D–ÛpêcH<+«Ù<±ß–›Q¹ Ú/çɄط˜±“þ¥ÀÛÉ»»ßÝààŒâÞäbØ»øÓf|= °œ…­,3'ØjÝJ£Ž€ Æ til ÉCî¦ÂŽvÃë*|1éÂ')ù—YBâéS1˪É3y,fö…\Ò«ÂæšÜ^YutXEé¢ÊB<±¯2îõß‘âk:›eãÔÝý\Œ³Çï$&˜º1IŠyn}°ŽÆW³æ‚«hëÙàÓcåâäÛúçD³Óã¶$‰½ò.Œµ¯0Þ¼Ëí°‘w60àpÖldQ¹õŒNÄÜÂ1z%àÆÈþrž>Æóiåò~1FÁÈYk°àæ?ƒ¥ÁFÁõŇp.Þ•´öhƒ>Õ¨¯I„TH&ëÕúØo‹?Ööêºsh¦)ÓÌÔ¯‹z1d Œéßÿ.Aÿ°Cò÷èSë[„]¥Œ¤!ÖÏZ&õêìz8 ÙSaTÿž‰ð .>.„¼!WÖŽöWäšž”“챪íÙžÛ¨³ªHP–Ûªv×ï.pbƒ^0ʺ©×Î8Ííâ¹¾¹ n/Á9#/"qÜ&ÐtrŠS_£º]àîñ ±ª¬ä+§éKQf•Åç‹Ê€u–¦ æµÒ‹|¥O±Óµºöiž·¡¤¦‘0ÆOfð¡ywÛá…ÀIalEfßÒYZ×½j2+æO“b^á0%ÏÙš¦kòƒ¶ñ9£î–¡É¬HŠqjÓôÀ¹Àr:‹“ ‡Ò…ùO綺“uÄUPç Ž¸çÛ5£l¡XGÂÇ$XퟬÀ壬®UœW–_-ÉÛ ÙŠ)*%?²á5Ì–ÎWÀ^°xíýw78sÕˆ¼Å|r]¼è»Î¬–§Q„k!êhIT2­?8{öÓNYèLtU’…z{Q"Ÿ²÷ƒK„륃«ÃØFi+[ÕÊŵ6й'9K€D`p¬*¢lÏ‚ë²âlºKYÛ÷ø0Ö\8Å'8w6È“4,j?±oÈÁBš; „z°”WŸY9ëëžL5ScÉ4ˆØF°@MkÙ í×”K‹µc°cëÐh¿WS‚­³ B®œ¨S‡;d§í9àÐÙ- ±ËC)íÍÉhôãRrGÏRâ¼³&¨Òv–ÒÕûwô=¹B/SCf¦6ÏñôD"fúw·Wu£øpàIèªLŽÈãJ5/$»€ Õr&7táW'pêÐ4ÅF n¬qWÛ£‡ƒv‰Í€f-£F Ü#xÍÛLW¿y…6T3!¢MóÂO0/øÌÛÃBE”k±Äð>æežn›wf­ÀÈàêÍæå¡zÙ¼úº›·sË-„B¢É_ï]}›drI“&òzW¼¼° i%Ô¦wùOð.÷xWt/®Wª9n<öö.~Ú½+:ƒ—ì°7[Bñ²uÃCÞÙº¢3wy!Ï´ µwÅ~Üå:¤ZëzÓØîÝ×p—+M’ÍlzWüï Ÿw;s—Kl"¶õßx'4y—٣ǻݸ \ai `-}²Ö±êtø²w½™èŒYì_¡‚_îÕý8Ëp¾ÀYù΂Á!ж9+‚W¥g†dgÎBQ!#g›¼ÊF~¯Ênœ]{m€Y4;^U¼êÍDg®‚VÈ+#£_íU¹WAaO¹¤Ï«¯á*HA™bba[³‚ë‹¶¥m÷éÝàìDãã- €r“§d–=M–y(òéwÏüt¦* !0ݶ¡SÙNç`ð8µs7 œQaläúæ–þEî— £¢?ÀD :/H^`–vyÎ’Y‘åe5›'UVäžät-0C™_Ck] €l·*¬7íE´îâ{šý˜jBŠà7þm—z R#M%p¾±ë‚€íITh *ópDu&j¨( ¹V{Üqצÿ`h`Ì endstream endobj 5 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰´WaoÛ8ýî_Aà€…|°)Šb7›EÒ6w=tw‹ÄÀHS@µéZ [ÊIrÛ½_CJ²%WRí¢MàÈ’ãáðÍ{o†oÈååÅoÏ_½ Œ\]ݼxNFÏïYäøÀþ’|‘Œ€Äøü_øüC>º™.f3F€ÌV£)£Œ ³þïì“ýÂ,'ÀìõöQ†7”q«|§QÔWZ‡d¶=xd<åZûÞë›W÷ô†<LÈÝíã„ÔŸüLþøh²,^²‰ “E›KvEÉøqöŸÑËÙèåo6ïÃ^ ÞK+×®D£à3!Û‰Ô×߯o/@)…÷ t€i¼HI’$­³ÁÿèOƒw¦Á,X¹aHCÆ”osÁfuÅ1e芬•œñ î—óÙLáY½«ã]w€ž&¸Nœ7¯î.剒¥{ 5ðO­ABT5xóúšaRkïá@æònNæóà ìæ½ë˓ח> }À›2ʯ“qˆàäD|A%ÿ‡pQÇEÁ)¸Ü Ãoá"g4 Að&ùwà"(>¹LSéƒ:›Šøê§"°S×—:¤(T¦ú¸(¾ÎÅ! àdg”a@Àû?†pž5J%ÑÀx(éßâ2ð)÷¹PM>ŠïÀG1T†“ÍQJN5ãÊ?“‘`¯Œ<ÙQ Têfd ¾ÎÈA(N¶G)åüµj8Ï%`¯E8ìð-)™¢óÛéBúCU8Ù!}Pø Î#$̇ É»-ɯ};WveúTsÉê!rOI‰”;P1Ç\h鑟«g{˜ÊÛš£Cèðn×d}‰)ìã¡ôÐPydÐdj•I™†Ô4 ÊEg,GP¸¡ßñÜN[Á}‰í•à{³uœ“Üüwg’…!»Ü,I±6ÉA¶f±Ž’8ß³±Y”±ìƒK[SÌ÷°yæC”›¿oÍj/bãî‹”,Í"E|Þ§ÅÚm‰ÕQÀE™–a¦€3½ÖÚF{ðl"Ñ6Ý%…‹¸Œ3³(bÔHºrIæcé­ÇàÅ«‚r›:ÜË,á¥pY*j˜•¡3b>ã·£íÓÆLHlãÏ öò"Û•«¼7qò4‚îa eÒuÔvÞæ³YŒ§·+ÍOQN®ïÿMþlr—Ll≃7ýØ}Œ*q„ʉu‘éxª<6)ßCuå‡J#ˆÜÇPåî֋ʦ>ù”î6KÜV}ƒxš%b7C47cå¥Ñ’,`ôl1LV·‡C¬mºŒW±%OÚËX¿Ÿýì – ÍPL Û4—ý*häÞ¡<¨Z4‡$Å™gü‰à_®$þ%ì©È/葬_ÜÁYÛ e“|x{ê[E.|Mg~ Ÿå‰Lb>£”–ËÌä9ÁraíÞZS{;nWò öˆLiÇ€ºÊ³*H¾.µ÷ªdS=‡G´œýñ.µU E EÇBijj€U@ƒ«¼^ÆößnöãxXŠoÆSl2У‡~Q¡O…RV~çdã4Ó§ ‡Bõ¦Ix§&<‡Q\ûðøñ•¶¡Ãàh¯­ø!³Œ*ùcÁ œ¬VD©•@í½zì¬Ý#˜Üœ€¹×±GuÜŒ®æ‘<¶½0‹?¬+†#QŸãièaÅ"o9ç9VݶíXxØæŠj·)J3ÍŒ£³ùˆŽHŽ_Ž‘ÆzÌÖE”²J3r®|?æžÕGÈ–háØæ>µ¢ÃFÌ{rÔ=Ê̦USóc´Ù™þÙD Ì&åàÉ0l_bhFù¢C·â Ùªð7»‚|ZGYGOO&Á]b¿¾LÒ+縷­;7n®Ø¨:î[¾Wjû×½W¥O¨T´‡ Á!™>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 8 0 obj<>stream H‰”W]oÛF}7°ÿa€¾H ‹áð›i"uÒ­ƒdãM´mñ>PäHš5EC2Šûë÷Ü’¢dJiÀ–)Îûqî¹ç>»c/^<{sûšÙìåËŸ_ß°«g7Ÿl–Öx@ÿYWœI<ÿžo꫟—WÏ–K›q¶\_-l˶¹Ë–)Þ]îéÀ²fܦßÒ#…?,ÛѶ̧Øf!ç–ÆqÄ–»«Ï36_8qì ¿ÿùê—g< }üÍÃ8˜ýÈ>|JÉL0|õ‚¿dëR±\¬Vo%~ÞÏ^}ú•%EÆðûæ~Îæÿ]¾½z³¼zóž:Éû MÜa“û¼óQ;Ä¡åäœZþoÊ”3iJçAR>ðÉñ()Ǧ#× Ûµuò—µµÈXÂê\n¶MþÈ2¹^ %І%ù¦T²Ùî,¦ý0æ]ÞÛ·ÃX_Y¾Ç1%<»šÝ6,/“ F›­`iÙP£óþpܡӱåÆ~w4©Ù^ä9Ão:{wûqt.èÏñ˜ÓÁ…Z1¬-¸Å=ºž,¬U¹Óg³¤IX•4[]˜²@`:Щ8_‡áYQ<„ñyFVÄ×F%iS3¹fuÙªT°² &k¶’Í|΀7ÿy¿•0…×.FÚÃÁä‘EY,þªDV—Û¶¾Ö°1.ÖÛrß%N  ä¬ö•ëˆyl…QŸ«´aåZ¿¹“u-‹ »{×¿ïøº0‘:¾Í»¯ê°ErxÛ¤³/ãØÙϳ.Þ©pœC¼{ñö)6uœ ÈÀv/ãß»€ÿ`Nxî]àyúŽ_Ë=@¯³^°I-j¶H/Ú>/Ë‘ýD•_ß-P‰Bà ¶M¨k@»$¿€*{”Î×Hg¢Ò­lDÚ´ çÄW¡RY E©e¹|ìÕoØšþ,ç|¦t{õ°ò>ÁÉBÔ&÷pA®¥˜/¼Y†Ùê‘©¶(dAO6ôÄ€ñäúL&›¢¬™Öó…?»F¨m#ê8¤õc݈ ²nö‰ÖéSàK‹f_΃['2'ì!kdOàâÎA|£€èÑù©tìDŸ7¹ÐÇk„I\úƒ^,¨Úƒ©Ãumá¸Èaàw0–NÛªÝ\ÍíµµC©Nxraìµjµ¦üª¬e#¿ ÷IÞŠ>_ ¾oJvï8×/b“Œ^„O÷Ó=Nw/ÿ~5ûá+þi§®Ù~KÙ¢lÞëzj‚öœÈ@pĶåu×JއtÖŒ K¥9˜âlº&â`¢åg²¨Õ¦,áÞJ¤ ˆS'ÔŒ»ô…º–ç÷EÄßù–Tåñ„Έo/k:Ól„u®éýïgÉ6&Ì%6 .°ÉiÒ¦.±=Ë 8 —ü¾MÍ”2%’šÒØáÑôF¨8 ºüéLì(+´Bw”ÇŒ!—2GIQÏdÝtÆßÂ7ÔÉÍ€±Ÿ'i¨EÆäQ¡a®ÉF¯)K–Ãs‚²BÑ1†éŽ=X÷…sat@ †¨ة ,ô1PSžY†*tÜgçÿb0§}óµMBÝNPK¹½IE?=ø[d"—«ÉXÓ"޹‡Á—cD&êA¨ºŸ˜HÜY…ß2/p!-9w&@ö©JŠÁvtgaM¾Õæy &ÇÈÕWù>y¬ÙÍ–ˆ^¤:Åo2Mû‚ÝÐôzÎÞËT•ì.6}Ô“lV‹£Y­‡XÆul+ôIÛhQâj}@ü—“ãñÞÜÁù³ýÇíI»§©¶]æ¹¾å9½~=koZiÿµ†¦[nŽÍ kÜìm[ˆk šÈ7­<ä÷ö¶Ÿ˜<’*§Ðr¢‘®$á°¢@·µE:_³­uÞ„á¦y"w×fzžÊ_Þ7´=ánL“‚ݲM #L7ñ ˆu½g”;!¿÷«‹c£Ê¶"‚ª1ÛÔ‰¾Ã²=¯h8›È1ùO‚?øéFBlÕ—y™hïÚº)w 2LU¡)¨mQ)‘É´™]®Û'õ m!«VÐ+ %6mž@yÀÛZmØAôh‘q=%îKmíÈËz¹EçWe¢²nÞ—H%\nðuŠ&™(è6£`ìÈ$«T™Š¬UÈ)Á€6u_K²­.©cëXžÀ’®-ì®rA{Ú¸·l+òê§³ý1½>ž'9ô„ë;Ô>¿ÜygÿIãuûÌÄ-ndE¸%6dw‹y Ñh­ÙlñSœâ†ÔVLÙBä#žäésû‰uè ßÚ€¾Wk©j¨(·©¡eô˜;Þ”ôÅðW„%†®ËTc¯%Mlcˆ‘D§ån¬ÓàE¯ÉB64¥öþÃo7_Çuy³«¶.Z¤%PuäÖ¾¸ßËÊ ‡j×ÕfÅi¶Re‹*Qðì_«–”ûw!éÓ@);Uœ"µ, ‰ \žSç—ÅZn°5ò¼²-¨0¼~sC›Z§Ÿê¶ªÊ‘R>ež»ÍݼԻ ]èÐÌ7•‡§’ ›’V[ï:uý$¯Ãç1ïqú¼Š¦­ dV§e…¡²,ÍÅ(ÂPemB¨–Àì)Ð&67êã0ý( ²ÕPDr“¾ U'”ÐRáX±“Æ´íÑP»‡ ¨Q`ÕVèžÇ4Ÿ‡3aÜÔ{IöÎÆâyÃÂöÓm$-wU.ôû9-wÛÇó¬æ}?«9žêçß µ3‹ÇqbôÜŸ¾Ä‰ Z87¢…Zó©u^îk½5Ï$êy~p´cšSZ>@™|NžÎÌ µÁ݇wÿùª7‘‹Ë¡¹è¨¨á#µï—2ouœËØôFu¹6„§ÃÝoHÅi}þ-iÇí¨»†G!‰u·›0´,ÜÈ?.‹.Á(¹àR’,˜?›¼\%PUy²!"ÄØ–)‘÷è(DLùq¤³ÇLo“¾†.¡Ó ¯Î·|½ ÃÆdhBÂД%yŸC:^Ó87òL®AFž×&±Ÿ¥HL±1jyGš1-3¬‚*)Ò­‘<· Öº‚ùá¸qu -„ë¤ÍZ¤z<Áéeeº©¦’ïùàp“|w2ùL{ƒqRd9ø’)hh¦ù”tw¢ž<ƒpÐÝ")0Ô|Íʶ¹ƒænÍžªÁ¦·¹33˜Ó…8eQÝí^?|in+ ×TbÚ[*xMò¤™/œÙe–Ú½îîTW"•k™"ê.˜>3¥q¦÷½ƒ¶™¬47 L=¼¾¿³Z,™ºŽQ\r}PüC‹~†4?ÉL•¤ÝR†pÆ&CÝpå°~l†JSEQÎPSRCï ±¶Ñ øD+*¬Ä9wŸÞ½XWÙK³|(èU˜öÑ•ÈâÐGC­x4tå)Ž&ô~îÎêó\åðïçñ0 ¶`yBãÿ`(°G endstream endobj 9 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 10 0 obj<>stream H‰”WkoÛÈýn ÿa€~¡ ‰áûѤYxlá¢NÒ] nŠI³¡HuHYV~}ÏáÛ”ÐÀ€-Éâ{ïœ{ι¯>±7o^=ÜÝ¿c{ûöçwwìæÕÝg‹¥%> V¦ùÍ>ÿ;>ß–7?/o^-—³Ùrs³°L˲\¼N½´ƒ˜-Oôܲd¶E¿ãÝR2ÛQñð'¶XhEfŒ¶ÜßËgûDäLä—òx¨Ø&+NL”呯YÂVÇ’¥ç4›-ÿ¨O´»m:ÐŽL+ ƒÖ7gUÁ$OÖ¬Bà'žV…4»ß¨÷M,Î(šŠàR„…±°MÛ‹ãXE:%%KV…¬øzÎ É„!yu”].~“ŠªTl3ëTr¤¯`ë;—Å\…Еª Ú§õ´kz~ýtÛˆRìÙ™ñgL¨ºîð®õޝÊp|=î—ñÕyYÉcZ‰"gkžkNýxñ!›-cëtÇ×u–íWæ¬LNø0©ØìßËL—nõÎ}‡s?}þç›ÍaýV_òjÎÒ$ËzÁg‹È¨XôÂ:mX¯¾\…êIyà©Øˆ—PV‰¬^´"0Ãæ»²8V"çs–äúÈî»ê„…㙑7êÕ^¤²PÝ ¤WIú ™j©,ÍÙ"4¨wêúºŒÃ6c…;4}»¾Æ¯tb%2QÙ¨\ñÙÂ7xŽû¬x^Î!žè7ÇW(‹¯q=ýàqݵlU˜ùo»³Êù”¥x"˜fꤤäl“àüœÞm¢ß:üûåÍûùŽì†c®Aª*ëqà&†Ü Ô«öN„t&CêÒ¼¶2÷etÛ1]`Ì£è_K|ãTâlá%ÝŠ‚c²_‰í±ÀXvµÎÙ£ã¸U :#¨1äÄÍ`MCyhüàËíïs–‰o}ÌŒîxQ‡0àÍð,ǼÐU'yÿvãÕEôºÙÕ°ñ;êHwlÏÓ]’‹r¬~Ûñ\%ÜáSÅÑ rõ©òDUb<=ª³‰HÊyKzš- Ѝ³êŸG=n¿§2Cgˆ¨§%1ì™ P™È· 7ÑŒXŸšç†PÍý¦3ÇŠ˜Y¢Ð[tðT»á0ÀÛ{‘s0 V7j‚ÐÌógTºÎŠ *½˜=9q›³F†¤OìÓÝœ˜‡Wªæéîé&év=<Ã,Íi±évx|N9q=d¢?¼si0†V@il” £ yŸê§¯èƒkãõøFŒú8öDZ¬¨À㞯;ð 2¦…¶’4,45’ÏÇ¥”8©Qz]«^McÁ¨Ë ˆºþõIqµòú\ ;4¾NGÓŠ5ÈjZ^T=9lhlÔšJ{ ´tÓAë­›£¬EÒ×¢ñdûfØ<Ô﵃rhOëe® é¹?Ôóäb•¡é•Û7A²¼ìPBÓõ­7_ 4àáã—»g–ó'5/’¥W¾G;ž­€.ɲš6ÂÐ."š–!ž²À€@¢+>ÉnçÚŒV™²ØÊdÏR0øŽÒ¿ %îÈ“Á8X¶sU¼+ê4îÛ xèAlú©Óû'ÅÏŠ°þEsÍŸDÊñW¬éŠÉé°-ϹLªñ¼PòÅ-›ñ1”ó¦[¸y¢é²óÞŒ×QF¾Mäšì4Y1)H0y® ¶`ïï>2þÄå¹…¢ÛДÕäc4ÊÈÊ3À°o®z#xF€»e·¨ã 2êøX¢We3»£”‚¯á…êv!çI㯽"ûóWhR £ ¶>À ‚{’•i¢>€¢ö˜ g(æ§K-µ²ã`]_©ÿC0ÅÑ(ƿ+8 ÚZ"ûexË' ^·k2ÉXA¤%l'Ú8*&Ü 9A~vl×ì×ÉV•ä)§8|½å‹”ô£âXO{lNóhIú ̼;ç pÐhB_Ý0—Öx“I‹|-ˆ:1 åªK”×Ð!>{7 2·Úõï’lRæk]ˆ{F+[‹ ŒcVÑ`ˆ<-$ZøÐAÓ«—íØ†ÆŽP¦;¬m)VF4Щ+ÇE _u=C×O×Pæk½÷­xohó!©¨²3²Þq\ìT¤Ã¶NKÅ«ìYWKf™.<N!L›.ë"¸Ã7d´;ºŠíè ¶Ç›Õ ¸ 8"xÔ@›ô§Û/¥4 *Âÿ§ÜÂØ"…¦ÓŒžg\·D ’êW€Ýx7þ/éWÍ '*Ô ñŠ@^+lÞ£ÙÜ®Pq¼<é*ªâõãìR¿âº+2ÃÀ±¦dðóJ»úYW®áÅèôpbόˊ´ÂÝæìýóÌ7ÒÚ{ß9ܗ샪uI?ת54Þà;l°O É¿rlUgí@b¿Š-ÏØý‡Ï_ú•¨$3RàwýÀ }e¡ƒô‰ä7›qµ7Ý~qž×„ÔxÉöô²ÛïuÓr¨ƒ¡œôµxÓ†g¸S§pjº3œ¢{×H-¨«bµ¶Ñ2¡ã4ÁÊ9:nK˜§‚ySxRË©¦6vhYéïH õã\!6$Ôjs(ŠlÎn‡ã9Êœ“èÒÎÖ, ª`Çê¡JC™c¶<Ø€¬CÅQgÓÖÔΙØt#;T]ö™0T L¥D¬8À¥äz·IÔv(ŸoóqËíµ6GfÖ9<jmPž·ÌµgóÖóÿu#\g1%Ž:ÌPKñ/Ú´ÒäXªu¦â[Ì\ön2ÈcqBK{¢ør9höµÜÀ3èæ`m)ŽÙZ‰‘äÊŸeâgWl­¾ùQº ûðñ“ŠËDØʘ4ÑÄM=Æõþt*ׂËÝ%‡Ç÷ø'ÔL>XÌCå7òÃË¿à´Gœ—a'9ÁÞ,Bc v¥ТJ¼aYÏgöˆ’Ÿé®6(lg@6|ó7º)º:KþœÐ õ׋“=½xL“x=ÍväÁ`XAx3.¸ÏŽÆ3têýrê”Ђ`X¡§Ùœ¥™ÌØ ›ŽoH—Í¢Ø2{ÍfNèXFYeZßýT.×l«Óæâ]JLÒœ:•}ñ„Tºeüù?ÏúÖ~™KÍÏ2šœÚ¸˜Õ Ó‘*eeG/G“–çwÓÿ'ÒrçÒÇ;mj»\\Îæšýù?zäÄDñq0™LˆaœË÷Û~—ЊôøbBÓþ¢7Ú*¡àRBð›~`[ ~0áOzMµÃȆpah‘ÎÊûmÚ)bÑoÔÈ_ÌÑ™6+×ÇDzl›€?Ÿÿ 0P9f endstream endobj 11 0 obj<> endobj 12 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 13 0 obj<>stream H‰œWÛnÛF|×WœG*•.ïŒÓ¾¡p‘8ͦEe?¬È•Ì‚"Õ%iEùúž³¼ˆ”%Å©À² ÍÎιÌðíxÿþí§Ë›+0àÇ‹«K½½¼7 *ðúE”$øùoøù²]„£·ahƒp1šºa„Ð;ÓƒpCß `ýüŽ¿…˜©ÐðG`€gøz€_3!\fZø$àæöþ+ã©£=åUæ_,E&$/E ¤(„|?†¿7çší¹†:—ùºë;t`<Òðù¿šÅ°àUZê7YQ O )aà ‡ÿ4@v ÄÜ€¦ Ô”éÌ‚@!–RÔD ärûù˼Dö™øVöÐöä`¶n¸ ­™– YEe’g ¾‰ˆ®Yl–ËOÓ­ê†×áèú•cW"Ö–hPC¦ê‹ìúL·ðB‰¬)v ̓õ5Üî>{ ïzºƒ²<•p-óy*VGQ%%^h."^ò xïj'c¸Õ”În+·X$Q"²2ÝB”“Z<ÉHŸé†oaŽžÞ»õS9–î†ež”ÈB²žDÌ"`†oXõŽmS·½ ð25·~÷éóÕµÎtÓq޲wuߢÆ4,§fN¯³é_g8Íïð+G9¸¯æÀÝÇɳwfŸ®ôÐz„÷Ói÷þîîsÚýåÃx5S˜¹ ó§ ‰Àq?ÂÅ{5Ü(މºãòšê­šW8Æó-¬ó"Qcy„ÿZ6Nàȩ́™ïØ\žß_ÃìÏ‹?îõÛ¿¿>Ây³~[<*6§qÉ;rŠš!!øŽÑ ^MÇwtßf†ùÓâ$ßÅÃJQ”°ù fXÏ#l˜qŽqd@ÏÆõÁì““ÇØÿ=ǵtæ2¯Þ{g¿ÂùÇ›ßn?Þ܇`°7oPÖ¾îèýfâeQ1_­¶ÃtßgÍR9h‹~ …(©ÏŠjŽ;-*i1η¨.îFHól¹Ée ÓéøJz”ʉ7XoŽåéîi‘í°ïdxÓF¡q}î\œ§É2KåãµÅÒÖìóž{ï<€YOÚ66EcÝæ?#¿æEQ­Dm·åS‚BÍ“òƒ›Nãà-ÚÀô(Gs*Öy'ÙÊ\!æÏB.Ò|C†×K©"ø>Q›°µ˜Æb£r@œg¦i–8»RLŒ—…:d¸o§FÙÕHÛ¨ÜÃS4¹­¾ ™«Ì‚ñ%*k@꜠¹ÇJõTͪ릶×&)C>3­[‡ÈñÔÓbXsÉW¢²Î@Ivª¬¾î­·—b)d_nº´J x׋íÉ‚Ö8ýrδè 〨ÕY%‘Ì£<ÐÏX…HÅxjie,œ>Œ"Bbˆ(›(¡®7vîz×èÔ±U®ðÍHÃÆÁ$I}ƒK@‹…ÀSžÅAýè„9?Jls›kاssOL­×I¼„71·¹†ãÔS{Ñt…j×j‰¹›‚(j#I'(·©?*Ÿ®«q•Y ŠÂ”a#±VJã̯µ†ªüp3-G _ÏÿÂZØZ6é$õ7<Á´ïjêoñ¡4ëÇÁ·Tú&J×tî’¥úb:65— l/$üMÄ'²´÷3E±‚@\ƒ^å~*t°þ‰ºì·ôðßÅuÚ¥ék¼Ùçœgp-ù;¸EéÔmÏÓõ¾j |ðt? …¤-ö©¯¾±é+°¥£ÏÆAÐþÍèæ¸ü,Ϥ¾6üÓx‡Ÿq~èFí!ŽOÝÍÚîÞÖ~‘¬D­ ¶õ.Ä¢ÀyØÐ)ïõ›µ[]NÓpžÙ9Ò ·×´(ù²¿j°‡ 55sêA1fh0x‡C©–]o²v`M;£ÄBgù¶^lTó"I4&,*OÃßmÉcÃi–­ÓLØŠœ@V´ºÉCz~¼¤GÚÆµº¹5vßÖP–¤g­æB¾BNe%éñ ”Y6®øíq—a*ï$UkwŒàùЂÈÞ!К—¸Úñy’¹T®“£¶i_Önœpèáz(k™Kæ>%¬xü¢xœò®ôÖ…×wÍÆµ0¨Ñ½×¼(I ù½Lš@’Ä‚SÉ„€ˆ°G†ºgQIZ‡²#‘&«$ãÊTjb{Íâ­}iž–ùR@Ë…Hâß*yæ)õσöåüã¡´JÕ˜j$H›jê"¾‰¨"×TÙ…Y Vb• †êkÓhXÊ턜 ßÐ¥ÐlsYP{Ì©˜¾”Ÿ0zÓ2£Èœ¬Ö)™\m0gAµŽ±Ï àË Q†ÀÔ®„2OAµBê,r —7÷—x©k/ ¼8 Ú!Åš‘ ^Ô2Íy¬îXÕÀwpǦu/Ó³ÛÑÔêS‰t’U¢ÑGu™ m Ñ.S™V[Ál‡”L+lN®\Ô{?æÛãvhš‡ÍN4zb³qM˦µN‘V=\ö÷ú Ö½ ‚ endstream endobj 14 0 obj<> endobj 15 0 obj<> endobj 16 0 obj<> endobj 17 0 obj<> endobj 18 0 obj<> endobj 19 0 obj<> endobj 20 0 obj<> endobj 21 0 obj<> endobj 22 0 obj<> endobj 23 0 obj<> endobj 24 0 obj<>stream xÚìX]O7ý+–úÜŒ¯¿-EH ) Jš ,}ZíÖŒRT²‹–¥ ÿ¾Ç;gX–@’KšJA€Çö½Çö=÷Ã3Þk¼7bñÁHÊÁøh$cÌ'#Å&ã³q6Fã‹q.a¾m4ÁW3AŒwÁ›Ð l5xô1f š}¨fè@•6ŸÑFô ÚŠ~$ú;©èG4”# mAÐÒúP‘6¥@cB[Å´¥<6 ÚâM$&L²hs4 xщIÀ‹)›¼ä¬Ùl)%“€—Å8has9ä& äTq@†­ñäd›0qB<9µ={ oNáÜìþôiw|uÞw“õêòd}¼êûwËåº{ÕâÉšwÝþÙüââ÷ùy ¬Ö?š¯úÅF®…ØîÈ›þÓúUer÷nyÖo”RÙÛÃ*¯¦1âαõlÃЖq<±ÍlËÐföåõõSdKýDù<ŽWö-[î'/O¨/Ô®/ÔwÔwãyÆ>õ÷㸮#ž#ž#ž#ž§¾'žç~<ñ<ñ<ñ<ñ<ñ<ñ<ññññ€'´Ÿ¤±ØF¶‰mf[ØøBû í'´ŸÐ~’‰G»K&ù“L¼L¼B¼B<úâââââââUâUâUâUâUâUâUâUâUâUÚÓÒž–ö´´§%?–üXòcÉ%?–üXâ ñ„xB<áþ÷ëÆ>÷Kú“П„þ$ô'¡? ýIèOBú“П„þ$ô'¡? ýIèOBú“ŒþˆˆˆˆˆˆÇxæa>æa>H<æa>æ‰ÄKÄKé'û$ŸÜ“z2OâGÞϤ]H»v!íBÚ…´ iÒÈha°0V*Œ ã„aÂ(aЗ"ŒãƒáÁè`p06d†ÄÒBVH 9!%d„„ÒA6H¹ d‚D00Í0Ë IfÖ™Á-»I7éOÖ›ñf¹ú8?C!jÏË~þþtñá—xn8f÷|ùþ Ç(5‹×ååÍåÇ‹©5£ ›1':±7ôâvÿ¶lèÇÉ Ž<Û½½\Ÿ.PÏç‹îÅû~µíû뎺¶üóå§îÅé?ÝÁjþ±žP9Ëu9üûmñ~Û™ü5G‘=8ýp¹ê»ÃEƒÜ:~û û­=líßG82¹<ïW'«Óóõ°ÉåŸ;Ýõêôï~yÉî‹Õò|~>®pØÝK¹iÇîhoïëÅ™ìoÛ{Š4=ÁÑ}A]´FÛVU¼gSugÖݶÿ«j>›þ,ë?tYA?ëú÷ÔõÙô¡…ÄÍË;éÒ/¤_H¿ ôϦSÿLcó]×€Ùô?¾¸æ®ë@{A4¶Û¿.±ãûçäzäƒjx›4¢va¯Þ©¥[á[jQ³FÒgpyØîëÃÔDG°ŠaQQ,*Ž%¨¤UÜŠŠ\Ѱ;m—ê¬{f†Ê¡ôCc{±ý,TT$9]*HzÛ½ž_µËls¥ýÉr5_Ÿ.›UÛÝïNî/Ï–«©}‚ì3üÍÆ^5öwnÁ7vpc˜_³BU¤¢ªÈDU‘ˆê­<ô­²Q!›²Y![²õÛe§¨Â"Íç‡Û#†ëãmŸ¯šÜQ5©£j2GÕ$ŽªÉU“6v„5ÔHÕøþ·Õ£Fÿp‰zÌèÏŠèÏŠèÏŠèÏŠèÏŠèÏŠèÏŠè¿!;-fS—dx‡<¿½ÝŽÒ¬¹=dÍå!kâ?kâ?kâ?kâ?kâ?ïÆÿ³© >f7õDFÅ×íâö™^Ùè•;ôÂõêF/ß¡¿ 7uöú5jî/-¼{á«ÙýfáüÑøí¶ÁŽ…dü/|”ñ3¯\À¼6Þ5`ºp|q¼o²Ü7y 'ëùj}ˆðX¬OOì&ý±ÿ«”'öŒ4^=GEŠ4i<*ÒxT¤ñ¨HãQ‘Æ£â—8Êþ+ÀJ½Òa endstream endobj 25 0 obj<>stream xÚ”XÛJAü•ù‚ìôen 1/’›ó$>‘0Ä@üûTMÐÐzØs¦{kzk¦zOZx©Å"J6|eYŒÚÃÕ‹MÆ£¸3žÅãUÂg-ѧ•¬ŒU£Úbœ¥%ãVÚdÜKÆ£ôÁx–áŒW1æNCÜ¬ÌÆØA‡q”Ez ôÈ÷* 6¬dØF1#Å61 ǶŠ9I’ ó£˜“fw.™ gf’ä0cÇÂîAÕΫbjŸÄLÜÄ3°s6p{ *Og„ìdÊ˘ÁÌE¾£¯u7ƒdÛZ3«øŸMw›Èàrwf>gFñ0f® ;ëY™AålÌ r.fP¹%3¨Üpß°}ÞÃH ƒÁ *r^¨<Èy¡ò$gh蓜QË9/T^ä¼Py‘óB/ÔØ­ .v-S¯Qì¹\ 3­D8×Ý1Àny%Ò˜™tfP¹!r´D4ô€ƒS4>ÙP¹'3¨Ü¹JCejAÉc@aÇÅà* •g'•—ñ*S Góeqwv0Ú› :z˜ îžp³Ñ(éä =ÒÉKÊ gd3ÈÙWÉ$g´yf§DÆwb•½½í-šu;Ø>\ß|?¿Üްp¼zÇÛ§¿™¯¥1óúõ=ذ+àPÀ©€›î x(à©€—vEAWtEAWtEAWtEAWtEAW EÁP EÁP EÁP EÁP EÁ<õ…¯“»Ûûƒc\‡oJ%&î1gÏ”²Çïþ3ØxÜe6l Ø&`»€v Ø%`Ÿ5ÇPÌ1s ÅC1ÇPÌ1s ÅC1ÇPÌ1s ÅC1ÇPÌ1s ÅC1ÇPÌ1s ÅC1ÇPÌ1s ÅC1ÇPÌ1sŒ§æ¸Ê??:Sæ)b†"f*b¦"fún­©®5%_TtME×TtMåÍLEÌTÄlŠ˜íåb~ÜÞß]ÿ¼ÝN.~Ý^}¹¸ºEß¿ªÏßÄA¬ýÿ f/;ˆù\ÂQÌ„£˜ G1Žb&ÅaO{gÛ>ÿ)³ƒÁHêÃ„ÏØÕ›ËoWÿÖŸ—¥y«VT1EStÁ1 ¿79_ò{cʉ˞ž¸ø!<ä·ç-UÉ endstream endobj 26 0 obj<>stream xÚäVMkA ý+‚ž›Ñ|C8i¡¡nš”Œn¼ ךߧ±:vG[Þ]žž¾fFfÇdˆ#ðòTD dˆ‘l9Õov™¸0Þ…œ¬yC®8¼-yÑy&ŸEvXdO!‰(ØùH1X¼%^Ÿ)a}¡”à/Êø10Ř °ñ¢*¢¤BgºÈQÅݹ{Tå†]YO»éý~âÖÔñPí@‘ÊñY ŒŸGðhu]×Hëtp¯ƒ\×r«êùÈbЖº“ßìÃïCÖ;Vž;]¿X×/Öôëu¯·˜µ×Ûfs_Ôÿ0ëSq¾îÞú¬mÌx›jø0Ë¿žœr±ïLç†Æ à¯R¹t•{ðþ½Ÿ-¡øÔMÛfØ_ýx½LÈ÷‹é–$ñ æ³ÛÅ ×\ÞM®ÛÓö¦ëÛª¯òàfÙö´3^÷fëÿâù}¨mV endstream endobj 27 0 obj<> endobj 28 0 obj<> endobj 29 0 obj<> endobj 30 0 obj<>stream 6 The Case Of The Missing PLA Term, or,Bob Supnik endstream endobj 31 0 obj<> endobj xref 0 286 0000000000 65535 f 0000008086 00000 n 0000008330 00000 n 0000010965 00000 n 0000011209 00000 n 0000013037 00000 n 0000013281 00000 n 0000015172 00000 n 0000015416 00000 n 0000018061 00000 n 0000018306 00000 n 0000020852 00000 n 0000021088 00000 n 0000021334 00000 n 0000023282 00000 n 0000023514 00000 n 0000023988 00000 n 0000024350 00000 n 0000024403 00000 n 0000024512 00000 n 0000024662 00000 n 0000024877 00000 n 0000025078 00000 n 0000025267 00000 n 0000025436 00000 n 0000027287 00000 n 0000028253 00000 n 0000029058 00000 n 0000029093 00000 n 0000029117 00000 n 0000029207 00000 n 0000033204 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/simh_faq.pdf0000644000175000017500000030503610364504250013661 0ustar vlmvlm%PDF-1.4 %âãÏÓ 1159 0 obj <> endobj xref 1159 13 0000000016 00000 n 0000001140 00000 n 0000000570 00000 n 0000001399 00000 n 0000001655 00000 n 0000002073 00000 n 0000002556 00000 n 0000002594 00000 n 0000002817 00000 n 0000002895 00000 n 0000003124 00000 n 0000005103 00000 n 0000000936 00000 n trailer <<32db4102a26ca7459a39ce5d9f3e8169>]>> startxref 0 %%EOF 1161 0 obj<>stream xÚb```b``ád`g``fàc@>V æà—©€ º%j´^dk1½˜ï²†ç† ‡K*gΞ¹sæ f…®iºR7–Lir8¡Ì².‹íº€k€ÇÁìnö)Â1¶@%×%4¤cÅø Ìv4i65Npm¾õ„§ ¤¤ ÒCmÇA Q @ÌÄi0X„QH600©FJ IV KP©"'(ڀÙ@ÏÀpPHs‚ ‹¨1ðð.¬‘¾ÀÀ=AƒûûÔ%ªx\q8+¥x‚Çâër†SÓ€´&ƒ Hh£ôgpÐ1Øñ†gÇ€4ƒ©.D @€4|K™ endstream endobj 1171 0 obj<>/W[1 1 1]/Type/XRef/Index[177 982]>>stream xÚìÑA 0 ±4ü)LÔÑX%?ŽÀ¹½i2ú¥9ðâÁC>>>/LastModified(D:20060121134117)/MarkInfo<>>> endobj 1162 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 1163 0 obj<> endobj 1164 0 obj<> endobj 1165 0 obj[/ICCBased 1170 0 R] endobj 1166 0 obj<> endobj 1167 0 obj<> endobj 1168 0 obj<> endobj 1169 0 obj<>stream H‰¬W]s£Ø}÷¯èš'”˜Ë§Hmm–ð˜¬$À;™ÚÉ’ÅF u¼¿>Ý÷¶ÄT&™©²‚>§¿N÷½]ÁO?Ý.¦þ Tøùç»Ùnn§‘ › oШ6ù ƒ ïÆûOÕÍ]|sÇ*0ˆw7²ª¨ªªA¼º2lˆ_è­¸¦ÒçŸø-.9Š3áÅ•fÚ > 2°U¦h¶ãà›Ï7Rä/àÞý;Œâßo¼øÆ[£3KÖ²|“08 ÂÿfØŠÅÃËž(ºª:Sm9J²¦ªæ5<í2A1DÕ ¿²LÅ1À² ÅšEB¸fV¿höŠESW4MelТqÑ¢H½º~­é<@— ¦Øø8ÎoÒ4X} ýÏ#SzˆG–Ë ö§Œþÿíó-Ö‹”:€©«Šf|Ï9ë²í÷¾™8l¢Ø•Ù·xŸÂ®8Š—ldKù: ›âøZft¹ÉLª!/êl“BrÄ[ÇC–âGu5ÁµÐNÍd&ù¼E Äá%]'|½Éš´IǰÎrüšNù:†¿ä[Øø¹9=R^'uVä½oûÇâ­ÚŠf©LŒ÷ä¢í^¯ ‡µK8 ûÙч阊m¨êDÄ<Àà ·küÜ’Ÿp<­YµO·å€uÆGÇûØ•øW5ÇðRfuæ°~…°XãÍ”‡­†D§cžýëjˆœÿ¡lznL EÃÒ1š¶àe‚Uó´¯á›´ù6ºÈ©¦G¼7@“©ÿe*{ÜlM±-´Át26ÏïŠCÍb¤žm$ViùœUUF¹Ì)'U°OË3õÔV{’×£‰”nǰ+ÓŠlöTåS:æQÏðF+Þš¦µ D —ä¯pì_9ë:á+ÚQQÇW¨÷ÈCØî悘0B[™)Ì ÁC@U±«_L%§mŸãõ’ª*ðb“%5–è/ ìPDM›%iÜe‡´”ßc^ðé›T£.|Šˆ2–é'4øm4&g·)ö¶=âçž7z/]öuo^²z_œj(SäWÕ¼`7DwŒ¶7üvÚfÄýiˆ¶¦˜údâ´¼[£‡ì9Îs–Ø×{J2ÇS…ò†RŠ®½Žti ÏÅ6Û¡Æ¡ªÉ¶Di‡‹y—;4îˆn3ÊõùV#Y—öcØf;ëS¶ªæç ýœ¶•H$j»ímQB•Prz~š6sX£ÚÎ{¸õ´8fÌ)/§s"Èê–Ü™Îc ›‚Ê–“ÛRH2^¬ Óëc…i?.FªÎÅÈ#ýº*÷‚f:Ã`†£‘µÃ…z².þH…×ø‡ó};Å©KyêHŽôUhVVñ€Xü&‘^`X_Pí¬¥5ǹ‘Äì¢:ðŒj§“~]ôû—1±-w1#:­Û,Õ(žª5S!†^/‡ïkS¹žSã‡sjXí êð~Æ./¢Ãôs³±ï€á¡`¢6»3æôÁƒ(¸¿¸¡~«0øÕŸy3øäFàdSŠFLú4îE×vÎ`VÓØ†¦ÛiòÅ‚ÇÐfè.ã¯܃»ü ¿ =CZÎq’Û÷zÍ*yÿX…^A‚¿XÍ}Ÿö—ÓùãÌ_~†;´º (@^='šqÓTe‚-"¨H1Ìý…£7qäiÃÈ÷"ât¶ó>|rkçŽ,¼pú€¯»wþÜ¿ŽáÞIo—䑾GÖ—ç­…1ÇË&B. 9¬ÜwýǹÂê1\á­ ò0N³^%tPnMõ\ZKybˆ¼…·ŒÀ˜a Àû¿AôÐyz1y–cXµùÂàÎ cZ«‰Ö’Áÿî< §{GÎν³¯W‰6†ß„Ê`:wýÅfîÂýìñL˜›c£clû¥Ö#륑ÿòàñwÐM·¥î4öƒ%Ïìõÿ…»°Á—qHïNã1IJ Ët;ä¶èŠP¾¼ø‘7šâ¶bXŒu=à†~D{è*5CpOòpÊW5äÊì}5]mníŠíà¿ötIn.=î¥Ï¿Ps"‘.™Ô4yÿãC!Ýõˆd÷ŒC#]ÓÕ2»‰þyçt¢…™çÎ1 $$m޲oÒêôQn½)˜>7]òôwà¬60<±Ù¦ã ÕØÿ符›xÈÁäX";Þ¿7鱯巙hü<šåbÐá í¶1]Ç|åÉóy~<`áþcw½ÌO!¾|¢Cû|Òæíè«a©}Íiqh+po–lÿÀ3YV5‡1Ÿ ¤È¾|áKfJk ð² %¿¨SîZ•ÒÁõ^7½õmLK0â¤7æŸv“.fݧ0Šƒ_KÞmûöãåízÜîäG¾‹-x)³ºNq+9Õûn}Èþ[û®$vÏ8=éœz!µì\«åå…ì #qj|ýcÿG€Úä¶ endstream endobj 1170 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj[3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R] endobj 3 0 obj<>/Subtype/Link/A 39 0 R>> endobj 4 0 obj<>/Subtype/Link/A 40 0 R>> endobj 5 0 obj<>/Subtype/Link/A 41 0 R>> endobj 6 0 obj<>/Subtype/Link/A 42 0 R>> endobj 7 0 obj<>/Subtype/Link/A 43 0 R>> endobj 8 0 obj<>/Subtype/Link/A 44 0 R>> endobj 9 0 obj<>/Subtype/Link/A 45 0 R>> endobj 10 0 obj<>/Subtype/Link/A 46 0 R>> endobj 11 0 obj<>/Subtype/Link/A 47 0 R>> endobj 12 0 obj<>/Subtype/Link/A 48 0 R>> endobj 13 0 obj<>/Subtype/Link/A 49 0 R>> endobj 14 0 obj<>/Subtype/Link/A 50 0 R>> endobj 15 0 obj<>/Subtype/Link/A 51 0 R>> endobj 16 0 obj<>/Subtype/Link/A 52 0 R>> endobj 17 0 obj<>/Subtype/Link/A 53 0 R>> endobj 18 0 obj<>/Subtype/Link/A 54 0 R>> endobj 19 0 obj<>/Subtype/Link/A 55 0 R>> endobj 20 0 obj<>/Subtype/Link/A 56 0 R>> endobj 21 0 obj<>/Subtype/Link/A 57 0 R>> endobj 22 0 obj<>/Subtype/Link/A 58 0 R>> endobj 23 0 obj<>/Subtype/Link/A 59 0 R>> endobj 24 0 obj<>/Subtype/Link/A 60 0 R>> endobj 25 0 obj<>/Subtype/Link/A 61 0 R>> endobj 26 0 obj<>/Subtype/Link/A 62 0 R>> endobj 27 0 obj<>/Subtype/Link/A 63 0 R>> endobj 28 0 obj<>/Subtype/Link/A 64 0 R>> endobj 29 0 obj<>/Subtype/Link/A 65 0 R>> endobj 30 0 obj<>/Subtype/Link/A 66 0 R>> endobj 31 0 obj<>/Subtype/Link/A 67 0 R>> endobj 32 0 obj<>/Subtype/Link/A 68 0 R>> endobj 33 0 obj<>/Subtype/Link/A 69 0 R>> endobj 34 0 obj<>/Subtype/Link/A 70 0 R>> endobj 35 0 obj<>/Subtype/Link/A 71 0 R>> endobj 36 0 obj<>/Subtype/Link/A 72 0 R>> endobj 37 0 obj<>/Subtype/Link/A 73 0 R>> endobj 38 0 obj<>stream H‰œWM㸽ûWðù`Žø!JB»PÛš¶’¶Ü+©ÇÓ0r;AdÌȯß*Ê–HŠ’èÁ`Ü’Íú ë±ê½¯ä§Ÿ>œöÕÄä矟{²ù°ocòå;|ÿÈ÷/¿où7|ÿ ßÿëûæ©Û|躘0Ò}ÝĤû«º?pi÷°ÿþ¿ú/4æÚKÿ¤šK’ÂKªâìÿ»‰È¶ûϦì6å C·ÿûçïCFìžQ‘õÙÍãÝJs*øÝ3ÝÝ(š%yžEe’|ûmóuóaÜ9·ãp7NʦqÒÞ´wp`ÌôyÝÿ c”LL£<—uÙ/ä×·²íªsÝšQï÷Ý1Á©ðïPzw8ÖnÌ‚3E¹Ð‡ºß$¬L2¥ ËsÕ—‰Z…2RU+ÅÈâ©·…äÒ•är>uwAwº^Œõ’"Ǔʩ̴­~`±œš‹ŽT­ÎIZ0Ï93‘Sn^£¶Úî’ètüeûîo¾ÃÉæëh¸NÓ{ CO)+¡4wé­„|¦„,«¡ån©¬5‚[-—áüÕ½„ÎUw°„Òž \F¸ˆ"jIQÈ©¨êÞ lÎÕVÂýäð“®j?Õ}ýKAYªoºáêŠÀÈðh¶Ësÿt œAÎO‡q©ÒàR4™©._k0}umwKé­5˜¾À¶¿£;a±ÆÞ¼È¶=_¬í½—ȧr$jŽíÐäwÀœ*ä{^ª}Yë&Q¼-¸—äN´¹.Àך(ç|êm©a<.I3‹Ç©¹üˆœín)½0&gû{"–탱lˆä 1í®Ñ¡j»¢™àÓ›ž(3hé ÷ß<‹–µvÌ‘ºÞ–ÊF%-ʘÎä'Â(£ínIƒ…QFÛßHå–ŒÍ ËæFµ?’ýy»K£Óë[W6¤}oñ gGyjÉá\¶«\ŽMdN^šT>E“ˆc*õŒw²Û7Lâ¥Ð©”>$‰„9ç`$Öú¾HÒŠF+“jŸŽ´2›K.ŒVÚî–Ò £•¶¿CbžmX7àÏ=V4¢iBb„¢ÂÍÂFÌŸ`RršO™†œå y«É¹Ö{ñÄqß%rÇj6k³@0éu¸T0²šˆ”‚èbwF“Ï¥ÆWmwKé…QVÛßÅeZ”,É´îEIÛkŽÇÇ¥hS%(òŠßû¿ã@)žV·ßˆî ŸŠ ï›ÄÓ —Dû8#S%=Ö (RES¡«hmkIÌÈ ®+)?}HwAy‰{[añ\Š«dW9Ζ2[eº™ãl…¡T…O©²Ø6D” ö‘ Ùø¸ZD¦€V˜ñ+£‘Œ,JHJcšiÍc&ñ\vä¤çbS’ªþØ?jTtÕÙF–OJawÈ23§EDùÇÀdæÁýëfXÝÂØ±ÌA4é@Üôw[å(×ÀIòí·ÍW+÷µF ½ze)k“ì‹™Þk‰Oæygbä_ËÊ…UƒЍ.^ȯoe‹l-%{3ï7Ê2kÿfý-×7p¸T”ëξõ0‚*“œ¦}õ9e3JÂØ©ák!±$ŒšÎFs»±xO<“¼W1†!H˜Ã™Ü¦¸­[2Êô)««ŽÉdB3eX\£ªn;=3^/“ÞbD8ôJɈ8Дxsé¶ Xk¢°`&döJåq™g}UΗvÈÀp K'Ý¹Ž“¬ žçSoK #Æ 2r%Nù\~aÄØv·”^1¶ý=bË6Ç–A(”M£D³×QjœÍãÚ²»Tq à$’²dº¿rÄùp˜Ò£T9t_>ÍóuÇÙ]Sk´Q‹DMõ´x5Ý8š½tO¢òpv2f–útj¦&6W} ‘˜^ZŸ<•<ÁñoÇ™½‹kT öØÃt†ü†Äþ.йüÂt†ín)½0aû{ð.Z¶!wÑ2½‹¦ÑÞE+nÈp±w67_¬Uo÷³cÂ7abʃºúì»Ù*¥Êõ=jµÆ?x*¨òœÁpT˜D’,»³cNå\~« )v|-%¶ª¸ãìA8†!XW‡y°øAC lì¦&ÎX˜By\ýéÔÎÂ-HAqhÿÜÍx©® *–¦‚¢É\’kþ†9ËÝRzk}þ;Ë߃ȳlCÀgT#0Žàž87šv0웢n÷ˆÀ¦z*IAš²ÐxÜò¨‡¤Æ ç^å•Q™L“ÙÖ³á*£œ wl?žòÖ–dd=Æ :p+ÜSÇk1‹ÝQiR—Ž01)òx“j&Ã4LL¾KÃĤáì1Ȇx5VÍ2fl@‰˜Åìh;ì¹;jš pmHÑèŽHP«O#ŽOø„KøPmw"*ˆ4§ù:Û1 5w¶±µÔ?õ\ö*Õå ~"U–ðKç2 ~¶»¥ô„Ÿío`{´Ê p–-`n_Ô+=‚I©¥…eik1H¤Ä6¤lëkô šqÔ•Ÿh"êÈG”Eàë¥lI…/5BõA¯<¿ZÒ‹Ö€&â”Æý}±R^ÄšÚ¹m ®’în¡õ Ó6B KÛdsI†iÛÝRzaÚÆö÷ Ü,Û‡àfYšpãC»“"Ÿœi?±×ˆ­ŸÞñÇ>@þt  ˆt-;õ%ØeþYåÂ.Q4ÕL0¸´Y˜ú2ÕG>—d˜ú0|-%¦> gûqÐ)(ˆxgæú)À l1­ ÕÕ°›×2t´ ¬x‚Li4½BfItÞTêr›Fû>Hßá[ñ‚o|üTm3½¶Ü²¨%ÚÞL]©×|À¯[ø¯½ôžw<:ÁÃ+˜½ÁßþâòFÚ¬Bæ¢ÐüÒØô"$ƒÄ‰ä™}‹5S&B$–2añ\Š«ÒDMý-å·*M²©¿Ó2,YLy昼”Þ„dNŒÎåÐe•”‚MŒ¯QUÎØº.-9žÛz.v¾ºGŠOƒpŽÜÉNá/cÕã|÷ô[ÎeÒÍ¢žO'Ìâ ?ô,¯öºõvÛÜàŒøW¿êpJC-Å©ÍJÒœáäu½^wÛ]·iç)LJüuØg¯ÏLŒ‹;aµl>Ÿ òÝß:'Œ lêSÙø 3Ð2b²'¸êC<“p¯U:I3c8Ö½.ûc¾· g“‰H)@®€ý¯clþvc9»“îËñݳ·,§ÊZ6ÛuÖ¨Mž8ž–îs š@˜,Ÿ±™äòUE©¦þòËWEe6õλ2ä]–mïJbÍ»,ËÊ…†—uešu™Ö×µáé\zÉ(¢¦~Õ’ùNóä&u­4ÊÀ,x’z²€ÆÒÔ%öÁïÂ!H® ëI—•ÊÒìÌÃk $®¿ðÁˆ Ó­„ Ëâ¸÷'L÷ ª" [ö·ß6_­ô×älʦQ–²öÏ}g®á“y𙘹F—¦êªúY«ÁCù´Ý ˜+…‹;°e5‡)YRÔ¡ Å*aÏñb ŠUÄ–ÏYÃ’V*¢H¾Ýt­sO%Ÿ„qkM£¡Æ0ü ÍGcÖZRQŸq}¬t" …/ÌLqR6#‘„¥H6Ð^øÓ T5Šiøég&eaR!Ò ÔyJ4ë”J¹°¯ê)ôWóÃ+Ì$œG†ŽALÖ²Š:¯Ê“F—à6rEª¡ñRˆ4¾tãEL#L7  b¾‡J¯¡³®a;&X>üwuÀ ðr‘E=wè–:¯hÛ"‘xξƒÛ- Ôoêë7 Û+þs„±AÃ`ÿš`ÈRV€äæj¦Û(ÀÔÚSºÑª§ñm½T%d[•ƶ6V,1•Ƶ6ØkR´ûdh'JpÞðªüì“îÓqt•{Ôexƒ¡{~«6?¾§HIìfljmîH¥órëþ4Ñb¦t¥?çÛ`zk ltÆ£E“[ëÌFi<q›—Ðø*pI¸ŽÕF….±Î|Ù/+)5Ó×Ö#\¨_%ðGg»àª& f‡ 0íê‰ýªeЇÏ;ƒ?eTû¼`UŽ¢s˜è`OmšKv½ã»}{#›ø§»eÿiðßù`ðXïÚ3È+´òŠm^–NöŸÍß°†“lˆVe^ïÂjù/ÀÃêõ endstream endobj 39 0 obj<> endobj 40 0 obj<> endobj 41 0 obj<> endobj 42 0 obj<> endobj 43 0 obj<> endobj 44 0 obj<> endobj 45 0 obj<> endobj 46 0 obj<> endobj 47 0 obj<> endobj 48 0 obj<> endobj 49 0 obj<> endobj 50 0 obj<> endobj 51 0 obj<> endobj 52 0 obj<> endobj 53 0 obj<> endobj 54 0 obj<> endobj 55 0 obj<> endobj 56 0 obj<> endobj 57 0 obj<> endobj 58 0 obj<> endobj 59 0 obj<> endobj 60 0 obj<> endobj 61 0 obj<> endobj 62 0 obj<> endobj 63 0 obj<> endobj 64 0 obj<> endobj 65 0 obj<> endobj 66 0 obj<> endobj 67 0 obj<> endobj 68 0 obj<> endobj 69 0 obj<> endobj 70 0 obj<> endobj 71 0 obj<> endobj 72 0 obj<> endobj 73 0 obj<> endobj 74 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 75 0 obj[76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R] endobj 76 0 obj<>/Subtype/Link/A 84 0 R>> endobj 77 0 obj<>/Subtype/Link/A 85 0 R>> endobj 78 0 obj<>/Subtype/Link/A 86 0 R>> endobj 79 0 obj<>/Subtype/Link/A 87 0 R>> endobj 80 0 obj<>/Subtype/Link/A 88 0 R>> endobj 81 0 obj<>/Subtype/Link/A 89 0 R>> endobj 82 0 obj<>/Subtype/Link/A 90 0 R>> endobj 83 0 obj<>stream H‰”WMÛ6½ûWð(ÍOIDƒŠ­®XÒÂbvS9Û¦hEöP ¿¾ʲIв,°+h9oÉ7oF›þŸ_¿¡·o7ͶÞ!‚Þ½û°Û¢ÕfÛôòj^Àz}ù¶¢èOóþÁ¼ÿãuõA¯6ZD‘þº"H¿˜Uú_Xª_%ð÷?xõQf!ÌŸLb%PN \p¥r¤ÿ^%gkýתҫª´*t¤2¤¢Cª+ZA¦hÈÃòvÆ|8Â)6…ÛÁ„\Ù-šG&`Ÿ ‹ÂFÚJæAl÷Œvåc暈òÓ\)Ôvq 9(³ É&$.2?ú”l÷eû°N‹¤ZÓé½}D}Ý|Z§yr(á·îŽhfÉÏëT$Ç®A%Zч-rz¹Å+YN9VT©" ûT~Ïf ëSä\áBM÷ØWǧê8žÊ‹›È\gIÄ•ÑÒå`œ{ˆ\ƒ yCw°Ñ1g°CÁ9¦|Ô\î^L÷'„À"ŸÕÛcw‰&½<{Y3…™Œž+‚-¿Ÿ) -ŠØ¾$+03 òå%"î”ÈP¿™’˜Øê7Ÿ¡*Õ¯vƒ\¶¨~}¸úZQÌ))5•ŽÊqÆüèSÒ›2ªŽP8²õÓ¢êó:¥<)›G(¡C…:[K°j }ºÖšµõך°¤­amû0Ý +:ɱ´zjz@xoã#GžÏ«Ã­´ ³bùÁËTQPÌ8pTQ̨B-S…‡vƒ%Ëdáá]m¢à„ÎÙºkl}[¶(l Ń=A¤ãïäêb±´’ašùѧ¤$׬®ôš%Ȫ¯6O‡u*õ3-Ø]oµÖ€Û‚¬`ÉÎq|Óž1‹ÌÊÂgo¤çô'õüsÒ®Ú'H5šžÔ6–G«¾6¥8» ²ÏÙ!½7$šGoIêÞ¤p|–c!É΂Wsã–=Q¼w‹Þ=—>+ÞÃûAÅ{±?¤x/Òhîøï¨y7ÞˆgpÛQøFBÚQ|Õ#g &¡è¨w·ìÁ3ÃÜ >y_ð<¢w)0™Rn!ý“­>[¦–wk;C5#z–c¦ŸUü½¶ óÌí–žî5ֳܹę€B<éâ øy¬ÍÅF¥ß_}õ˜Ç»Ó5QN§‰n7¦Ôf"ãSìÇÝcJ©›`\5îÄL±|f3ñ>Ü,e3›oñ®Ø½†v>m™Nb:#¶ì+ʇ»EoÙg”÷¼Ø}Ä$ø”ì«Õm¯Ëán¡¬Ýùiô•À …Õàú>Ö±Ýo*c´& Õ’b\œûF€uñžYçiÊë•}Wo‘.­•UW?FH†eßLJÄÅEeÄEevŒõ‰Žß‚µí»0?˜Ã´„áÅ¡ØÁ¯®u¦ƒüòÅ+!SÊšÊPJ±Òü÷7ƒýxì¶Uß#øøìÑs­÷¨íPu„^Ÿ%GÔ˜–ð\]Ydã,7ÈàÆ¤"Î\úÓõ»-¸–BÀ´á­>%Ï{Ø×/6•?ÓP2ŸóXv¯es*¢€h0èÙì hbϘöi(m™¶2Z‹ÿ 0¥M´$ endstream endobj 84 0 obj<> endobj 85 0 obj<> endobj 86 0 obj<> endobj 87 0 obj<> endobj 88 0 obj<> endobj 89 0 obj<> endobj 90 0 obj<> endobj 91 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 92 0 obj<>stream H‰¤WÛnÛH}÷WôÛH€ÅðN Ìbcg'ŒìF@ìC‹lI=¦H‚lÚ£ýú=Uͦ$Ûb€,Ä$EvÝN:õá‹øùçw÷¿üòñþNÜ|¸ûꋼÃú'º¼º „Æó_ñ|×Ý|\ß|X¯}ˆõöÆëo­_èÕu'Ÿþþ—µ¸ñüO±Wiâ­b‘á&K}ßnfb¾þóæÓúæÓ#™>¹8w¬¹Àš[àßÏVlÕK‚Œ,éh&=™I——D«Õ’Ìü1 įªR­,ÅŸÍþÙ«ÎèºêÄü?ëßÞs ¼t â%û!™·®$ì@äÁ ;ÀWƒIäùgx˜/¢U8ßöÒ݉¯ódöOg»êGtéÇ›¼¿Ÿä4ʼ ÆW“IŽß=ÛFA†fÂÔ —ü2ň€>S`f¯Ä]}h]oT+æéì³îLÝÅW}èKI©ݱ3êà ñ`™t–לƒÅ«‡l?Œ½ÈObй€¥ Gw”8³ùÂt¢ÞŠŽîÙBÝvb[·Bâͦië¿ôažÍ¤QåQ„¾(ôv«ZU›xÿd.&s goºÅ«ÌZÍO1u·B–8°/­6FUB¶u«BHñÛaÌjòTô² +£pÓnɥܚ}j²âX#@;q¡*ÑÈüI"®²ÂßBtÊp´}ƒ[r©n®v¢ÔPƒ^k“Õõ1–¸1æ§×ÇX^” •ÆXÄc,˜9uLÞíÕ‚? µëÃ,ðhšÅP±äêÔ:ü¿R9b/õGRX#º¡aë?UnDg¬BÊBn‘ŽeŠõ áš÷»Qh7Ì~‰®¶4ð§%téã@ø»lIuj•÷ŸîX“wBx¨@W5nд–r¸×˜²­àrtz*¿µ;J.6|Ù\í OCªQYM' šà{šAma­P¿3òy\qg§®³ù¤œÔ[[Pt+'‡}ŒÉë/]&ZÌõƒ®ô¨õ@ˆD=ºÊ9ãð©ºeϨXÄ¨× 5!yHGr²¹‹øÕ_ ¡¦dm [%ÉN ­š X¦]B‚¯ÈóŽÉ¼U–žÈJ•EÚ›rÞUøÙº`¢~‡E~âÿĵ¶çCNÁÚÔro-ÎÕÌl‰¼«M^o×÷›(J— êåpªY£ë#ü58¯ØI2Ï9íµ?uV_† iš¢»ŠhŠô“¤‰æ^e±×–<å[Ú%pá^g5ÌF_‚t5àòlþÑ€q¼ó¨3ñw¾Hgèö»7•8×&¡Á¸U ê4–®NežÔqQê-iO»©Á*õâALoˆJ '陋äó¿x$+Ùæ{ìjGñ¨Ÿ”xÌïdköÇëT9øÁI…ߊßáë?˜)ˆžTåHÃÊÒõF|ŸRþdóóp'˜HGRcã—ÅD¦£Ù‹=ÓG\:[KÒ°@ì-k*>¥óËý4É‚üœ,‹6}KÇÔßÑåË娻£ÖÔRëVæºÔ†JEF Jdz*ë†Ö§ µ!þØíˆ:mgŸ,ž³E6Ês2y1„?I¼~¡f'6çtXtPÒ¾ÓTŒ |àΟBdzI¤a’•Ž”dQ ·…ò¬Ûº:ŒÙ°îñÒÐkåA4üè—!ã‹ÛÖ[~\8ó—HÖâ’…Û!K}Ð4‡œke·K4~¬ÐÇ”û9M?71ÒÙˆUYç)o•¡òÐN ä(ôÒìlcªF·ãÿòx+v+ëž Œ\pÐÞÁ.C€Ôê/ÜqoÆìÛ}@¼ÚŸv‚_»ag%™yù^Wja·’‚¤Êà«FGRnl¿í%NÝðnW‰²îŒw]¥Å×¥cè¤c° ®kÇ0 ½péû£vŒY;úÐŽ53ªFìp *<ªGø±B¼h³v QÔyOƒ#1V3R÷j–ž²ìhM š.ÉBñ2!ˆ“šŸPq¯Zë"ª‰ëtfº|·›²ØPlù“´›§ GòYžJŸ .¹™ÃÀ‹üdTŸº¤’ñª‡R–ϼÁ麷2Gýó2XA‚è¯ôèAJKŒx÷#<·B RiþœÙM[«IÞ„b–]vÞÎÌl=,@é='À¯×*ðz¶´èV¯Óãë펟 —˜£›¦)k‡_ 8u™Fó¨0MJ6eÅ%‹¶uuãXØ4,@ÎÞEk‰¦ý©ÏNö¥ “Îié;’.. bÎl &f¦§‘K°œ3¶®ï‰8ú±Õv‹KN*A¡Ö¬ˆÁu½Ð`wð¼]1ëȋӣ™lÔz|Àºû—ˆAv ²¸M’H/ºzfcÈ"ªp¬ nŸFÝüø4«É¾ÌêºI¡•€ÆêóƒüiÄ'ÊââÇï}} 0³n$s endstream endobj 93 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 94 0 obj<>stream H‰¼WÛrÛ¶}÷Wà‘šZîz:ÍÄ—ÆîT©¦bSÏ霚‚-¶©’TÜôëÏo¶\BŠÝI‰A€4°ömaíÙ}óÍl~~}úöÛ³‹st2;_”T°àþ¡*ÉO(Jaý¬ßW'gÑÉ,Š¢(º;!˜%hÚ<+=¸?‹*D‰ÿ‚YT"ʱÑÍŽí“’Ø… 506'¿‡h2冿¬Ód’b³ÝÕ¶DÕ§ª¶› ­ [¡åõ|¢‚«É”¨J7»,®í4ù_ôýÉetr9w6<ÚE{»ZÜ´Ã퇕lÖkžZ`ÊP¬4!Ò Ð$úmlo6¾7øòÁ%\™çh‚©"0o°œˆàþϯzË&”vBƒ Õ°¾¶è†²¬˜„ÁC:ÑA~ï<à,xka½œL)xçL«fͽ®¾†'¯£øë¥4Vܘð £ÄþÞ¬Ý{úè)† læ?E†X#E{Ê<ÎwwqRïJÈ „æÅÊf¾£åèÑGÍ Ki ;h–:f–gs.1c„™6èò ¬.Òû´Ž3tùÇ.Ýnl^£ó¢Üe\§EŽ‹)=mÑa;èv0§ÞȆ£0¹œq¬5QªÙü4 œÔnfto&ûÙ‡·7ðnjâÇ¥GqyàP†¡”=…3O“²€“º3¹Æñf|yàu¡˜)*é³(ÅuŒÞÙÜ–ª§êÞ¿/>Ƨè2ÉÒmeý¬D^`»ÔFµÞGr}6€R£‡×ýH)‡PA RT1x^6lŠø)ð/lïÅH_“72ÔXjÆDöÝO×]xö°ÒÐ(ˆ"¼büØKü¤BÌS¼;úªÈí§›e~Wœª™¤Ê€¿4o¤„Aqò¬º¯ìCfë-âä÷¸\EîjÁ(U§È„´ãüÆmœU=¨„ÀPÒB=Ë¡®×•Ké™DÕmCÊÌ8;E~ûs®ç˜i®Å>°e’¦wi‚šŠ[v `x±œá/w:ÎÍÇ’—Qëk~~-!y¸‘ ã¼Íê8-»lÒDL¸÷â¿ú¬q.ö ‰"BöuÿSñ)ΦóäÖÚî(ÀôûŔ@Ü Ð{ׯºä…ш¢oCj¼rˆ{ÐCL8H‡˜èÁâôΪ˜È Ùm@ÔX'jr˜Ö´™š9xÞ¹å ´c€6E ea9ƒõv ¾¬vÛ-|S”µ…aŸhp 8_Á ÚÚ2Ý®á©9ؽÂ^?²gôýT ËÆ™ƒ¾B²^õê=Õ».ªúïŠW‚âÁ[îr°Æ/wÙëô®za'Äýzwœ?_Ç (v¼<Lm3·»”º¼>åŸf î&-¾uЍòÛíåéÃv3†¹ ¡8l÷QýêÙRL@¹°}nûqkóóåÌ)—AV\ž£sôüîÎ P¢·MîBâþÁü²^Û2·u¿è÷ÈKX<ä#•féÛl»ŽÝ¢cÂq¨õ-‹éÊ®Pš£mo§7Â7N×Äï[®5æšu8rã|{4r<"p_îûã—4_2¢¢ìý1Oóû‡Ù}’tkîÃÒ‚ˆ/¡tá/`<ºƒwŸº—(cCHXHÇ¡2 ¢XSIÒjç„ëW_5‹>ãjùHus $Â8í«»‡ÐÖôÍ¢¿IÏŠ2ÒíU@ñö¤ã2ùXUsÁááòpUóq}ûùlÆ9Å¡à¦×›ó8élúq9»é© ~†lhßfé­+ˆnæ2c²_BíôX–ðq6>êF° ‚öÍ8¾À7Äõ ÕçÂi¾ûsP-®Xúà 9gœ²ø…™‡ZÒg%•;%ÐÏï¯! SiLp”±?»Ìù8Cñ,Ó !‡¾ô­CÖù~Àë6ÎÀ ¡Ä”È¡#]Y\¦ÂÆA_.ã_¢ž¡Ä¡RlЋÙÏ7ºùxê%ìÌ$NTªÏª÷¶>[^ü+0Îᘂb ¿¨»øÎ6 _¬'zXf°&Jö½ìw¥µÿ*Øñ‹ãñ2ª±PÊf^ñO56#!¦,ì/%¸ŠØÐÂ^ιâµé/¸¿÷ ÍÐüÀíêí~fC÷c†î'Î,îê‡:¸mœüßCèܤÁ2}ÒI6$kNXSêÜ­N‚ø#´}ñmfQ]t½’ëž®ÞxÝ-^Õ/QåÌ!ôp(åøÞf4&P÷ŸãèCc[gEk‹²z¦â=š:QAë6hK¸‡ç=xncå¾HâÝöÝ-ÈÑ`*ÿ@ø¡{Ûì•ÖûïüáçClšLð†ŸÁí¡úðS)©B‹†9œ×èÒqSÀ4Í¡L7q],'SÌÁŽ+Hšàføº¸‚lч[ ¡Gwn³SQ¥†ú1Àí×pPôÔÿÎùnÒµ;½ÝS=Å ·šlsý× ®·_ÏfUºY㺄d€>djW÷Ot›14~çŒÍÑ^öûnù¿+’œ endstream endobj 95 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 96 0 obj<>stream H‰¤WkSÛXýί蚭²«ðõ}虙̖ÌÂf˜Á$™ÝlQB–±6¶äè…üúí¾W² ±D`’$Y¾ý:}úôø~þy<=:;¿üòêøÆG—ÂÐÈÃä@@ŒÏÿ‰ÏoóƒW³ƒñlÆAÀlq0âŒsnÃ,Îlá8>Ì*úÚ,ÁéïW¼›e Æ¥>Ñ\96ó-p¹Í¸³õÁÎ7Qqš+ÚÂü^F9Ýç0üïì_'³ƒ“)yØz-¯WÂx¥rSxe‘OB1ß3è+ã€ã:Ì®í3ÑòÅà4­`žÂÄI^«\žM‡öàt(&ð.Næi•ÿ£Ó)yß)Y;¥ýÑ®ðm.x› ÇL:œ rgÃÙÿö­öŸMa AtE¤ÝcÇò™«|ß5aÏ–äñzè6Ѭ"Œ4/  î ΡH1ø¤JVi0‡_Þàm6ɽ9¢‹ÞXobýÝ9ÜÄ þ è´,Ö9Ò¡K‹¹¾cQ æhWÛÁ|ÀUò5&èü<´£Y253z-,Òìªe´µ wi‰.â}R˜2Œ¾I¢Œ«#Á„¥\aŒãùY™P]Oü‰g…ABé@üÑVKò)$—ø,¢x>G…G†Ñ£bµ'IÁ:á`ucTn1ê÷`«ç ïkœÊ8ŽÐå«t;=…*.–p‚AeIT@^n6iV|~ígá×ömæxº=øuöžm*fõîYL`sÔ9¸Œ6R‚13(´„„@¬Xô[ ÆiRqCg°›š©Q,HŤï[¢A.åAû*Z¤YDPI´¹[2W#˜0‚Øw jòC¼]—yƒ×í©™Ôt6ÆtZPCq®OmŠKMÇ`/Ž&0)ñ"=3Ÿ"6™nÅÎʺϫ,2“ðêäwVÖ»¶Ú©¬PšÆÛcÅæLyܪ­,‹bób<®ªŠUq² ƒ K³Û.ãþóSS’[²70Á»9×jÁâ‰CÒažÃ­-ç"µbCD¯3@ë»UŠWÙGdY|@¯r?"JçYL쳃T×ß޶í0%Ä–a Ìënެ `éÑ1{PAH, tÓnây 6SÖ£ î˜ÛÑí1Äó$W~àÊ\·ÊNJñ7± þÁ6-©û1Љù3Zà&œÁ|MìŸÐ¯|'ç²õFjn°˜âö6å’RÚL)âZ3S åÒÍaM.5eDða¯7«Ø ÎØp3^cÁkÚîý±æ¿ÿ0<,²Ž ]/5ý Å!€ˆ;*Tãʦ!É6ô Æ;Æíe¾áCɸëzMÌA©CÖ[8bpPSà:¾ü./¢5ÜPL­Ø¥{Ÿ²ýæ0:©@•ÿ„>GÚçwÚgÍw¿N~7ž=¬…­ÇçHÙLåݧÔMpiäω‘ýñ¸ÕPМˆ|Ny"ÕI`¡’-éª2ʈóž®PQ~Z¶b–»3ÚÕ÷IЫ$þÒ=¿…õ¬fµ % C¿¿Y÷«ƒ®áÙa ùÐõtj(î{£ØÖ–Ägð9ˆWÔ.7«£^ÝQèÑ7øFkR~9„7QñêòøV‡D_è¹9‡_µ.ÐÓ3wg¶öK’G³…Ô†¸{,[û'nÞñQ°Z4-¤4Òéõz4ÂÈ…ðœÁ1Ç×X‹e2¤’Ϙäi™…Qþ {¿¡t›wËX4tˆ €h'ƶȢ°¥°;Ô96<™Sm¢0^ÜiÓ£rT™˜ÜpIfË„ÜÒC¦§I5þQÎ>à®M–5GzB…iòyKÉyÓ› CަÇ0­˜ägzóþ”QBߥÛÈ¡«7gï1¾jÓÈFѧ’>M0O=]í=¸CbÉl (_˜k×%ëÆD‡XÙ›®Nx(Ç×ݪÀãlAìŸæ%ɉ5…vî ýZ ßÖŸ£­< è¶zîÖeºWGî8 xZÍ Å×$²GËd¹ è^Ïݼ=í›Õg¾ju5y î-0<}mÚùEg­d‡6ë¬U›?ÛÅF¨©·³VR<¦jBj ”å Ò¹2þ·kÌ5™wëÐAûÌ´˜SóQùôK,©zXÛi'¼×Ÿ3\Z,‡«‡”d0Ußnœ$CG΀*zq|_@}5‡·“÷vù2-Ws0P"V¡k ¥YnÖX½÷lqd¦Œ)/s}Gù÷'>‡†z/pzfäþøV”sfI.û9_ÚÏÄŒô@z>s\ßsîaæêòäúÍÉìÝù¯_Š^9‚Dp±U$õhÞ+I$.{4šýF’X}’ÄÙJ’·tsÙ-JäóvO‰Md5ît§|?e?M”H”\rQ‹’c­’“ý æ’×»ŽZ3!M|ú1Æ…‚`}õæßg¸~ÄÉm‹ÛŽaŒ2^"6 8ž¼Ÿ½œ\Í0•çð© Vñ"®¹é y¬… ŽèŒ¦pP´×³¥Œ;÷Ú¤Y³('=5oÇþîOSì¢)Έ״SL§—:Dí9‘‡}oÃÖëÔ¼xØM¯Ì;7zÅ )÷\Ù1‡ݼÞªý3b´Óæz¯«-açÛR-ã‚H FKæ‹N³âY1 dÎI>™c8æ õR‚ë‘ru´rüˆ…ýÒifÿØØË’µûŽÃPÜÙn¿ûûÙ·K²î·d[D•v-€tM&«Í2€²YG£nþWûטG«c)&lßîÎù ÅQȸÂçv[œñtrôÇù˃®¯'¿]œN®¯_Š> áÞ¿¿½ÃA™–Dp——î^/{\“‚q×·¬Ç\;Ä»‹£ÉEã§v|ün×zxG]8vðU¿šSþó ãcÓ¸^]yCóaÀM„ O‰´ˆ#Xï-²ä"‹¢*È"8:Ή:Yº†ªªXœ,Ò.I,Ín»œ´øëâ9tQ Àéô5&úéž­ƒùm µS§cûµp·c®ÍhòkÇ.[ÇVq%y47öO/ÆG(ÿ‚Oããø6FmÁî¹ðcÝO endstream endobj 97 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 98 0 obj<>stream H‰œWÛrÛÈ}×WÌKÊ`•apGv×[¶¤²”D¶Ë¢Mâ”k ¥‰@€‹I3µŸÓ3àM"°«ø"‚0};§O÷ÙGöãg7ç×Ìc¯_¿½8g'gç·Ë5nÐ_¦óê„3…ûïpÿ^Ÿ¼œœM&ãl2=ñØ$ÇS“=:ÑŒ{ôù_ºÕà‹ëùæ{Gn²_’ØóðþìÄa£ÉN.''—7dzç߸s`nŒs^|sÉ٢Ŏhë†å¢ªê–ÝIv·PeËFãÌUÁZ¬®Ø—7¿œ}¹¹=ŹÀM6ú÷ä/'O (¬ "7òü,£<0N&/.ÏÙ9ƒµ¼žÍU)6…Usˆ…^ÝÄÒ)cs 9æ.ƒ„ÛÓ:OXQÆ©#5#¿õb>¯›–ÅáøNµLUíîhäds²ï›ŒÅ®%ßø'ïe£]f|„‡ç/þ½)ç‚}¥Î͈;·ìAh¶ì³BÄnÆcs*‚nä¯ Õ Ö-œ„<ÏÅ*U»fm½¿Çwñû?pyBa›ø©’t,•§`¢ENÁšEE‹²dõÔ”–jJ¥x.q£©$egÏP°gˆ›zùžú‘·­×g›l*<”k&–B•â®”OÒrË7sxSà&îê¥tmPG@킚wÚ¦Ò‰n–Z4›«Íaêf¶’„fßÐ ãÎU½Ø5kQé¼Q°@ÞEÉÎ/ Ä«+Õ>°Ûë8y5òŸ{] ŽòÎм‡f(Ru4ë¥s8@çguj( Ý$ôB¤Â÷]J‡6 c¤€ó4v>Tìóûë_NÙº^-Ëæ(›Aó w¯o?P‘¦ ŸMÁ¥(9%Dö?÷æ%êÏ‹7ì3¾ÇI†Bå'><ÞßËŽ%{>Rï¦IÆ6'”ßsûEv¦¦?ryÖˆÕ·¼ø†K•KvV/ÚŸÎæ¢}8Ë 5÷ÒUºî=ùã˜Hbr+òY”&nÊm¥z£NûQᇃY`(ˆ²0z‚„¿©jñý”èÈlI×ìs¥¾³¥h”ùÞêSj ÷¼ÆOP|¨MfnàÁ¶-hex)o™ÈsºÒšt’€³O°6mê±íªOÙ ñ´Klºi…þ°|z/ç¾ÉmÀ¢(t½ K­@k5{Í4Zhó+gyA‘õÙäGm qeA´3$ZkÈ€ÖXûV4j){múÿ6P2Ë‚á ötBÂI¸ÃIʇq ñ$óãçëïªv á±³}GŸŽ2‡€Ô ã?„ˆm©›OåŠÍ›ºÀ£‹¼=ÔàgÝÓçn’ø;m§‘§ë‰UA'€+‰±1\³QA·Œ¡âj6ƒÈ‘½…»Þ§È^èF¢_–ćMÞ²…bÂ&´ÿÇHî”õ ¢|#%8ˆ‡@œBi’U#ž ë``žxQ¸‰ \VKÕ,4fÔzÚ®Èêœ"ªW¤à‰Žb€T:’ŒŠÆ†{rÆeo¶' ô€ñÆìá fMnŒ i&M1aö’Í”ê¸UmR\“]Ü™–Cq"¥Q§ÛBà1Úd¬¶·tí†ÚÁGÑô“çô„V5Eˆyh½7 îÛˆ6u³F*gfˆ©iŒÆ•¥°ª*¦é—”’[ª™¢ž‹)f/À½ªE™-›¿æ¶j­I…qTÏ%Á“há²ÛzFsS™£ÙÒgè&&ìÍ‹yÝ4ýŒ0v½Ý()7økiŒ´Ûǃºà[#ˆ„&}Tbx$‹SP‡äIšùÿUu¿è³Ö3Þ<¨$L– S ¸Üò{½‘»@eóhNЙ֜þ°8æÜ8 6QQz>ÜЛL?P †ß…YEÄRšFCMÄ’{Ö?óã³áïH!G™ÃÈ3Íu¸ŸÜú6ãj…C užùÝDÃø×ûTWu¯áø¨á~~âò,ë|ôbhEì°7°sùÏ1ØN£,0H»jä~ïÏçñq­gÂéõ†G4¶òä‰;øó®6‹¾–Ìj«tÎfTçj±iJZ–vV¢ÏêÉŸHŸ·p?·lEßË æ+Kà ;£Bo½¸ëhNuJïT,/^¢× {M8Õ(Þ6÷O”$ô•‰1r·hÛºþøØx¼Óö„G,È|€Ž—>/I¿Œï·Äd³/Ub&»Ð>üu`Ãõ^zr%åÄa>qå-Á^è5»‘…ÌßÊ-º3ð• PÀçýiÚ“B~tÛÜó-Έò<íãÀ†Žç¥Ð𤋔)ïwëø°j«íÜʆK~E)uŠg9;„î%ÙDèGÍ~³ $j^È%æLÚ?·ªTíºßÝãð ™Fþ†±é%G¨vÑP(êUÅ:Å¢•²hF6¶ÀˆVâéÄÁH$"óÍ$ÄËÁÍÝ<ïxöØmɨ¾™X˜jû£?.K/¦ZQ›áñóØÏ·-âÕ'-&$Ô6:뺯ºnÐïá²½N~`ºAO'Üu½× HÓÿPC8®‚ýê¾ç®Ð¼tPßý!¹{I<Ï½Û ™ô^65´6êµ}\‹¶¤èl‡Ñ°i?͈ÏÞÓ>s; TŒU 3ÉЊ„¦OšJq›¾5¿ázNàÆÒè(dbBO>öWeHG^4?IˆÙôÊ¡çOÔó¼³_i”^´óEk€Ôá¨×ÏผXçv~zÙï¸dž„O¼$R×~¤ì݇~7žè ßÕÙKv^t}0p³Ôºa®âÈÍB4ÓÔM»&ñ…AÆ+ìvX0®™Y¼òFÝIV›¾-šüA-E‰±Šôo •Yèn¡¼½¾AÚ®hñü¹ßçãbÓO¼ÎOj¾Ç9W†ã J"·M¥Ä¬z mvÈ“Y\Ìb¢Igi{¡õ«dzßêVÒvŠe*!Ênf;)x.Nˆ* Ó­ÐP 0ÚÜÂÀnݬ_të«#Ý{÷é^k³¦ÎlÄÞ^¶o2±{Ygòp]ÕÍ£¢åëž]\"–óVÌ¡g¢•©¶;¦Øþ ÏÍ%MÃì¦Fr¶Ñ,¾~´/{Ú.c‹n­%ÁWÒä4¯çtvÐU'§”gt™BéGCÎlŽc´Æ6O#T£ 0|gšÅ¬§²1ûfÁêe'*y½3°×3ý¨Û:½Ô°É]éºDcƒQ@” 7*“»½úP´%ª°v@¿*´ŸyœG$s3ù`=½oÄ 'hrT4’ ã7እP¥À–k“Z!®I…%Ê+>ZÝ¢®Ö }¯ˆÐ3Ñöëfþð2hj¢ð_ÉMŒ…Òê°GÇ‹=¨· š :ãÏ÷½M•jÞ2@Øìæ½/›yäÜNN²¦Øæ7å7jîÎPÃ]yP-Ql>#ƒ²NúBPYùfbK¹Ä–tç!lIÛ½@”GP/qe¦YH }&l·ÝÈáÀü_¬C§+§þŽ}ïÉ‹1ˆöh ç h¡0©)ìŠÑ4ºŠß„äÅ‘6E‰+ÙI¢ÄVíwOÏ»}KÌŠGý£ªŽ89ypÐe Æ-º_ÃòËù×?~ /˜ÓÅî"„êãei}íåÞ$ý´¸'h¯«·Õ{çk1K¦ÍÐÙ —,ÖFÍA­‡ZUеÂk‰4"°©Î~àÑ‘øËÔdÊ6rÌ2¥èuÅKþʧüî‘_¬ãY-GºSÜE =w+µK.J7‡ÿ‚èÇrhh})Ö/£¾ endstream endobj 99 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 100 0 obj<>stream H‰ìWÛrÛF}×WLåEà–á"I%åPJ¬ÍÊV•é$»Ö> Á¡ˆ5 °p—ûõ{ºB2 »”ÍÓ¦âØ .ÓÓ}NŸ>sy'¾ýöòvzs%ñÝw?\MÅÙåô#Ò 7è?Q¥ù™+2Üÿ ÷ª³fg—³™#\1[žÛqœ@ÌRÁ—ž˜íè«Y%\‡þý~ÍJü°ñŒÔWQh'ˆñ#ŽKmÎ>X³B4•õjZJÌ®ÿöæz&–JÖM©.DZä9=HkQxI‰w7·¯Å蟳¿žñ.\÷°öáy¶3ñ'´ƒ–ßH|ž®2^‘²Qlåb6Ьë‘gQ´ !ó…ÀJÕ¢–å½^ •?feAßnT^ë°:{ï–Ó›¸c×vƒ$Öѳ»–â¼T´‹ÑØ·TöH×çbS,”Z­²Šbãï¦jäz½UÁñjlúA¬³ª÷YÕ=ßv½06Ù–Mžsª£±k!r­þm#K-²º(mÄ£ð9?®ÕzÍ ·Uß–å]ʨG«à—sn£sÎ~ìêMœWж°8¿çu)麢ԗXSÑš%ž¥8ß≬j].<í¶îEì£ëSH7´CÇwÚ„WR§·/±#¨RÅPEºøeª="mlÏõììú–hh״ª;ßáÈGÉÅ‘í…I2!2£X³[Û;º¶N1î2t£d PرŸàŠ¡iˆ¬T¯¢ÁÿKNTçx!Vò‘vr”1.Ö ƒÐP='¡¯2Ü,Åšˆø@^B mS3tÙòotc"Ìó)Oª†°-J"3„s¿~¬Ò²9b©¨g‹Žµu*sÑ0¯|Î@ÖÔJšÎ£ql©!Úz8±g*p`µÝˆú¢q÷[uÎ,MeÝcç2[C–@±E†Í¡A{ü[‰¹Ô!m¬'ªPwÅ4¢ÖrAªCI>f)¢ìXKEe0YÓ&ôJV ú£¦ÛÚi~û/ã·ïØhçò;à÷§-|<;±ã8IB=ne¾×U7ú Zªà÷hU…‘ˆI ’ ½©–T¤²l¥~-Ë!2øv¹n'(ŠëX‰ÇLŠß.ÿ~ù[õFÿÊQ`‰ŸU¹Éj'KWGȨcuË>^V詞9æ¹?X¿ìQãywuwXäS2yvìD‘id@|!~yõÛýè¢ëR¹¦Béģ,³¢©b»¢üøy9ý(|ÚÕi±Ý÷*jêÞúqvw!®H¦w0‹ä¾uDKÕ’ã4îÉKp‡¡Ù@ä3ªí:½ú\'OÇ‚%C·jYø`ý˜ådb5ÿj[bH}uQ•zU¶iÖP´LnÎCnÛSj×éÑ/hçvßëbUC+îJ¾25Cñ!Ó²© (e–Ò/4¼g‘µ^%nRHÙ¾–÷gÖ†$_¦?8\ø«wÓ›Nh4-Nj4žXÞò™¹t[$)NÄpÒ_\ßN&ºÎ|¥{,€Í `3]dÏžÀõú‰k½.v\åöƒs¿D–Ùó×ã0Ú$J¬Ô–‘å)ªKä¨o_:Ÿã†ösŠ„‰hƒ1ƒœ-6°¯Ø(ö H±í×ä9N›0ïe&̃´D4É0Yû°ShEú¢-•äUݶ÷FåPõŽz™–ß"GÿW¤Ìa=Žy*w€/ø½ð®ŽkÐÃáhäF±E@ìYP~ÍòE±«Z´d~Θm TZš(@Ò…ÒíÛ7 M»¥4SvY½â|Ñbh²[hQ@íŒCÛöŽ!ðqwCˆªóz¦‚ß`Bî¿? åI+2Ìü@ÀA’ ™‘/:@Bq'öMÓšõ«F™Ó!Ï[ä§Ø‡€6£ gº"¯úâNmtº`„ƒIhÔ àWöÏwÓW#™£qbÙäÅ€×KÀóOâNí¦¯Þ¼¡sßÛ™ù¬Hék%èmÐm† ` ı1‰eÌ <è¨ÁkÅaÈ¿}Öw§Ög[fÉ£ú±o2ÐEI $f…Pì  ›5½6_gÕêÔ¾´ÛmË}“®œž¬]™#V5·¹Â=mµÿ 0ÌZ endstream endobj 101 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 102 0 obj<>stream H‰¤WkOãHýŽ4ÿáj>ÙZ\¸ªülõöˆN˜†ÐÙM¶©­ŒS!^;ë!ûë÷Þ²Äî „Ø§Î¹¯soŒàãÇ“«ÁÅløôéópG'ƒ± q ô EœqHpý ®ßGŸ'G'“‰ &³#›Ù¶íÃ$}åÀdM_šÀmzÿÞMrà’…Þ¯¾ò\:àÛ yˆ÷“åÑwC0ÎÁäžoœgkˆ£. *,7°NrµPEgå\å©*ñã|ŠËåÆW¦kœ›–0~óÉoGg“£³+²äÙ:ÞZW³ç {Mœ8s¤/j‚úª&è…œym»DÐsòŸ}{‹½{[Ú!‚ô¥=8͸§Ÿ&Gܵ¦g(ÓâFm4Z¨ï´íIøi‘-MW]£œ'é=d3ˆ`™i¶¤Åœ$%ü¼Æ¯r#kW X$êgíMZÄM€†¥{„$÷ÒÜÑÂÒ Ù¬\S t¶)íêy‚ÉSdçc:àÚ½KË?®j&W§ƒ`ïT²µ…Þæˆ¦Óâ²tX`‚­)˜ùQÐqMSË$KQ= ]He¶‚u–?` °´~]»Bb–Ém–üŠPê)Z®tÕ›XoóÁõÙFf€EƒÚuñ n |l¤ï¾a¦ïŽ·EàAmZ¡-“>'$$¾\Eer—,’r£ëáÖ„2Oú.Ó‰Ô¡5¢Ù¤- tû½ÒþF«ÉË(t§ýÔ8‰d•ví«nî37ð‚íÆ§§–m[¶c=a…=™Ò°žžð+ÈÂêºFóõÎ)l² «\ªìpÈ|i;ÁËSÝHõŠ¢µãN!®Kœ¨ÓsQZ&q²¢|¤Œ*£ü^•;6í¤s«¶ël#ÛD1" ÔY…’AÊ„QŠ.U-¹¤¢0&ˆ{ßZµ?V ò¨ ¿®#`‘UÍšNÏBQÀtUjchèÖs|'2nc‡¡ÛÚ¡ùoZk×èrõߊÊ1§´î±6J§)펻 ù+7Õ}‹Ïí•’¤?F‹•DM“ˆ ¹5¦Õj±-|ý4aèu€ºÙ`6Ï¢jQv+®ûŵ¹®ïi)”½¢ëõˆîëí†Â+œeÚaæ+ *ë¤PÇ0ŒN.FÇpy:9Ö%ñíj ƒEU”Ú‰ubk§ÏP8×;iä>ㇼQ!Çã|[|¤6sr«n›Æ¤Yj¡làÎ+-û»âQgÚ,I{Zœÿn‡»’yþt;|'§¹ì‡rPßÜm—« °MË1ô?­©£n’tš­ T4 Y:Kî+TOšBºàå»Lóæîò~Óœ¿`š'™ð I8ÏŠ®£¥Öñû./¿žKëì÷‘é‹]¬Ü½¬z¨¸œùŽÀ‚¨©Œòdå^a\Ífɼ¡MY1CqìäáÊñ™c ¯Ñ<xPd'›UŸKÎ7w9¶Ê. þ¡pdâBò&î”eÿÌ*}¦9K£»…š¾¥pu‡ÂãDèÒi* n.0£<{Ú¼ ><Þö˜ãÈÀmàw`¬¢<žÃ%ðN±_Ñ~P‚8-1n;¢¿ŠýÊõ§JPÕÅ©ÓþŒæ àÑ hUª.³8ZÀi®"šTé³NgOûUîG6ú‚¡ÆÙ^¿û%ìÏÙèQs¶¦žM±Š•Š“Y¿ªón÷«]¶²ÀvѦ“*âvæ på« A÷§šÄÛDxäã¾ìÎÈýÂÙÃÅ ™°ƒ°í‘cgé´Í—ÐcøÅ.*ï…ã³À ¥ß«áò/ŒŠBzÌa“ÿõTÿíô÷N¨CuPì´¡Ý =´sG5<†e9|“LX¼êPA\2ìwN-¹ó †_ɘׇ†w/H\ UV+m2úÒ,_¢Fw1:Tã„-˜ëÛ¡ûÌh|6ßÿA’þ÷.¹GFÍ#PfÍÁ›Ô¶ `èìòPùã!g8“JñLñ3 Fÿ‚΢¥SÀÊÀÙµRŒ±N6=§Ù΃,bñ ËEÿÜâ¼’8¹“ó¶KúÍZãaa åm¡ð€Ò‚|7$˜–ËCãÆä‘›7=—“CuWÝßÓݵZ㌃çóÉo{I½Ò>»±—89ÄI_ùš“$±ÐœôUÃIp’N KÃ’!7næQ س*UQ® zŒ’µj˜a!­óšnŠ·åUüÒÍStÇÅÖìö¯=œ÷úÃ"÷î\ÇÂéÞ:t˜èÀ‘铹‚ñÅÕ9¬•éwhS©0ßð:-£$Å÷=cÀ”–âj©êOôð‡8mC’–f`( ežF í‰:9HŠÚìÄè»Q@6Ó€Ç$kµXлé8ÆÂη—©%}æº^H^›zâ«ÅêMTÈ]oÐ]ÚÁj·°h˜}Úè»±R8Ì"ôœðóhQàŽt…âðØ,g²t•­ª…ù™k$%3t.—‚ü Iz¨© À[Ý endstream endobj 103 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 9>> endobj 104 0 obj<>stream H‰¬WkoãÆýî_1È—%‹æðÍ4ȱµ ¸1jÕ °Û#r$1+r>¤U}ï½CR´,R‹´¥(yæ>Î=çÜ›göÓO7Ow÷Ìf?ÿüËý»º¹{±YRÁ üUIqÅYïÿ ïWÕÕ/ó«›ùÜfœÍ—W¶eÛv³yÂèÑ…·{ü³yŸÿþ>ÍKÆ]+ŽèDýøVì±Ð¬˜Çðyž_}6\ËaæÌ¹ñ¯µ¨Y*Íj•+¶I¶ÉêLVL”’‰È6b±‘,+ØË㓦c|dæ¿ç»ú4¿úô„‰“ã]r:xÞOacÀ‚wttô¤£ bn‘mûÁÌùïçÎvΞ=Ãr8ÞÄá‘mñ~£SRUͪ,7C£1gÜØˆÚŒ …eŶ¦o”æÌ1Ô.KáY²z-ÙRm6jŸðb…?„‚Á#Õ Ž²%b+ðM[»GëãŽÕg4ú ²7ŽÃÉÒxç퀒0]¥ðÜ=ÜæV 7p¸1´xÔWjá< Œ—C¾P›,aú_nŒJæ‹Í‰"e)– ¢o*ÊzöîZNXå¾åF±Ã)¯PK–Ë\•'$ªÀfÈ¢®¬Ñúã¹zÇ;#~!WÏ·?Žã“\ÿÞä")1«„Éo˜Xnr#+D©R†)ËUš-³ßÁçÒäι»FlENàu™¦RQ VÕ ¦ j Š€î̤„²Ê…€Õ–ãÅþ?w=‹Ã“?YŒ ŸE™¬ÔaϪ^wÍkË’âOvY¢Ó;eD_¶s!lDZ‚ð}_³*£Q<°Zá¬cMU VãÜ–8Çb£ƒ{×0Ÿîô¬ ð®aU]6ð7IÝ”²ºF¤VôyÍpêi"¨§xúÎtÀìF6‹Æ‰r"aÎ-  N¾-XSl²àGìIõÅœˆþ¼`LÜxô „à\:ërl {¾cÉZ*#žõÈ]³¦›ÉFl | ^=4ÄbÅHÜåv"úQ©ž´2¾ï'¹“zÍÏ ö÷`{‘ç誼le‚‚¤›PDªÆùDV ÑHŸl[ª–¤²ü]šfXMˆK)áºò]3`¹)Ew<ËècÉ7t( F~éÈ=ë c«Q ¡,šåR–×ìîùŸè$n`vHõ4%›RÔYw÷ ÑsC®CØ(´±M»&ƒç-J‰‘}EwG4Â0Š‚¬\+`”¶H+Jþj_fu_‰ ”œ¸û”;þÙsßv-×ë\¹Û»rY°T±GV ;ÂÐ5•¶¢k´¯Ú¯ËŒi90ïb i^SDz g,”ÃB½&~E舳8'P:/„‰íÕ8òÏKÿ÷‘B{Q`[¾Ýn Ÿ¡Vð“8"?ì‘ϲŠ-dëÑÑ @©+™^³ `Ì—hΛ6p´ÆöÎzK(=îU§°¬¿l°7TÍvK¿¨Y¢ˆ‚4LÃÀŸrÀ¬‹€ðŽÛÆÑ¥ÜàÀàŠf1vß”ˆƒZÅ£ç]ÛH ì Âè¸çt±•"GšPåBÃè‚×ÖÀ†í ÷ï‡!6‘~‡ð„ψPò èÁjµÕE£_åYšBÏPÔ——²Öw¾I›¸¥#d¸½oå5Stg]Š-Ò,Kx‘Ð5ÛYƒ9yç’áJ°Ã½õcŠhK·3€vÂÿ HD)¯XµÆ|šMJ:/!ÄúPȃY£|NYÎYwñ[Fë&+ù¶Ïj36Ö2œß¹­1‚0K6Ôí¯dBÉ6Ù¢º*b¢Ð`¤y #ß&½Ù ¥ zK#w²ãKHTÀÊ’vøÉvÀTxér©#Ú‘ù5g¡jÌàöµìgb2ÿ6†7­®Aß›IÈg{Uâ¿)¥OMùÁ ‡=¡ßží‹]Ðï W|q ÕÍ[ÞÑÖ<êòÁ¸®JÚ9sY ‘ÚAˆ€íšK T¢mŠ*z³ vÃÕKb4¨Ã©…W(%HK ˹"ùfCVàŽœCÑ^×ü¦m?€±€U©bZï‘m~0¡ÿ«Z€Ñ؉2Ct.°“Ä\/²®[êªN¿ÆÔ9@PI˜ÐÍ0¶žgIJ–8ðù]1l{6fvÄ ?î/Dpö1‡{´3M©qËá q(VmzQü Ü÷Ã2 ~1†DÇ÷aï¼qàzWÐy¯÷¢FÃ:€ HQ|,;ú‚—Ç'ˆÿÊþq<´‘㸎oE¥Þù_ü{÷,O‡\îãÓ¦ŒéÒÛª®^ ÷D4…&+Í78(«Œ1öJHž¬hò…& ÌBî‡c3˜ÝÖàÆœÂÒS£ý1H(2=¸‹¼s8ÜìŠÁ=&‚;–c»=É~Ã֔I‚^ÀŒ±¥0S\é†çnôvèæ8;Ým>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 10>> endobj 106 0 obj<>stream H‰¬WÙnÛH}÷WÔÃMQ™Åâ4܈íÆtzà,¶œ ”DYœP¤B­ö|}Ÿ[E-¶X œKuîrîrN?°Ÿ>½ºx{É\vvv~yÁNN/n\6kð‚þX3+OËñþŸxßœœONN'— 6Yœ¸Üu]MflL·^°É†~6i˜péú?¯ß_1U±·%{{3úÏäwc˜–‘ažàRD>™4Þûñ¢ªW©êŽÊWé(pî3¶È‹ wœ±;ç&ËØ·6kT>Š z<`øÓ(ã#I0c‘ðÀ¸rá'‘ËK–W8ºdúî:lYmÈüyÅÔ(v–ô.oøÝÈ@ü:9ùõŠÒ²O•ئê8þÞ’X)ýqãˆGñqÜo2Åæuþ±ëˆ‰ »¦KÙ.ÐÜj•×kÕø9C¼«¢ƒAIü̪7J¥³¥æì¸~È]Q²îqc’Ev6ùª-R•Íw»±²×X‹Y¡Ï#™àn XãvÁ"3Õã:cÕ‚(û•=V-Û¤¥ 5÷Áð5)=q$¶¬<ÏXÓÖt˜iˆêá }¥ÃŠ´6Îg%.U{¿$ðeUàsöéêÆî¹ÿÏɽȚ”M‹” øjµ¦/çOBe7/`Q° œH†Èí FÁ3«Ï«ÊÄóâí݀Ð^\G}΂/]î/Ï‹ëe¦;™ÐdÅb”8c…ÆÂfÕ<£òšUøX­‹ o_±)=m-&Ú_!¹|„ìÛL^’Zó(:Òmã”î¾ó´ ÊŒM©µÎ¾¶kJj “é}={]gÖís4*ÎÅ©=)uâKÕЯ´Óù .l‹ÔÚžˆ¸?”·/øa¿ˆ$¡é6â‘kˆÖä¿}Ç'OD’@¬çIÈýЕ]~9ÍÁwgÈ«bõ7—ÕóÀ·÷o·Û\ÄnY›ÍëjeG/DŒ$}!ÂÄTËlóåaÕð9*ߊë½7ÄxuE ­¸äé¸ú…šMÆó¦²ƒË‚סHz°§Ô:fëÖŽå÷by\²2á±çy±!ëk]EÐ 0\ óÀ±÷Ô¥³³Î# §Õ£Ð8T¥çz&†ÖÒÑמð8ô‚ø©GÿÀ¢¶+ÖŠ-¨¨ƒQûÒ½‹×Ð65Ðh¬vø c`¸ÇˆäoìD{¬ØãÒ÷‚®ß°M« Íx¿BRw…ÿxžÑ~ .ý£ÅéÓ¸a³´¤eQµÛÎéueF¶lÓécN›7£Ð±U6°ñ=ëcÞÁ¬Ô³Zß…Ú9 Ø8§ï´sÌ|žì÷ ŸK$T&Âù +1öá·,/‘Ê¢`ïH¨ó›Ë_ìÖXÖâáÁ| mwÔõú·Û~•aB1¢2’.—yMI™‘”ÁÕìuP#ßÙz—÷4Zße «|ßf꾓Qú( l“MÅÈÐË¥¤]$Øî– %We¯ÐMÙR©õëÓÓÍfÃÉsâ´Á†¡¹Ãa‹tîéöôCU«æôÏéŸø8Íô^§¿0>Àv±oq$¡£*¾T«‚[ã,í” w” ì”’qÂ)›É–R¾R·ˆDAùç¥ú—åïQJF>n§®ì”Zuå§Dï®ÛAa`†¾Ž ¹{«byÃJ´÷5Vµl[êªxdE>Ó‚¡Áþ¥5ì8DR¦xƒªÇ"Ú6¨vöv¡%Ë2} å÷ù¼û¥V/ÏÍ‹È:0+3G† XI[^ƒlzÓ–ˆÎTº¦%¸aû³ŽÊG¿u“Cê|vèl“Õ ¦ nÚõÔDy˜ú¡ê¸ÊguõéÍ¿™L\—ìi2mAÖÐÃóÉ(—ÏphA­ïF¯è¸RÇ¢YVm1§æ™N´¡:ÒqíÈd¿÷êH+¸{.áOWÊZ!Á8Z°QO½Ö¢K¾³c –è;‘Ãlì2õ9€p!Ä’IrìMG2qRkuØÌê|ºm0 %VлVS“˜A9´X¸y{õÛxQéáµOÕóé .c‰Ûõ¶¡ù…™¦·C­C0úιÉ2öÆÄK¾Ÿ›(ÃÚ=Ê!¥ne"áAÆÂ$PFÂÀå%~ LsÜÁ0F›#'ÐlÔ(v–ô.oøÀâ l ßO@1ÛHè?‹þUÕ–F=v/ÝU ÛDȾfdi'ÎêÅ?Á¿>ÒKw€—ý+΀­Øf„8ž72öú#BéR§F^ªG˜\-ˆ6_u9nÒR!­ç¨Ÿ¶ÎL+0¼Òš’é‹nEZßSÖ™î{U{¿¤s—ÕÜü0íQ²¯$%ˆæa¶»žf[ÒýëÁAl‚„ L¯¨7•)›º ¾šHPµoDsÊä¾™PQíÒxýÑÅ×8fµ5‹²w:˜êf9OzGÑ~¤;|æÀ9© ÊÐŇ[;—äÀ’uÔOŒ!ÁqÿXÒèéúnV,05Æ ý€Íª¹.‰Y…ÕºÈTfŸº×]¶Õ+h)âÖQ­í·s”Ü€×òÿãµçóPj¹ù´ ï"jì{À&Çܺ‡Çm÷©¬z-éº"·sZ-µØÐå¤Ï§6Z© ÊuW36Ç_º]Å’|¼ù×/Vw~Lï8O 0å9ÿè endstream endobj 107 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 11>> endobj 108 0 obj<>stream H‰œWÛrÛ8}w•ÿ›—HU €à-3å-_R3Ù]'[ñ¦j´”DEÜH¤†¤ìh¾~OºÐŽÀ,')Z$E¡»OŸ>8»e?ÿ|vsõþš v~~y}ÅNήî›T¸AÿY5ÉO$ËpÿÜÿR\OΆCÁ$ÎN‚ !<œO˜9WçOôÃaŤ Ï?q5,qÁ…2kÚ³Àç±f!.Â@üjyò{ïcÎv“MÊâ¡õ.>3/â «ç)»ºýĪ´ÌúA/Y°¼ï÷Öýì-Ç)NËþÀ뱬byQ³²ÿŸá?N 2uæÇLy\Åž&HSÄK“i‚Ÿ)Kò)ÃÚ“$gc|Ò –”tšõe¯.“rÓÅ:åŒIåi|á³§l±`OÎ˯̄µ c ,(â`r ¹ÔqH{³,Ç:ýáOÞ OÞÝPÚ¥»RØTK›j»®YgR™cfÝmÊ„­¥ ¸ŒQâmÊJ6+‹~Ø[öUÅz¸øŒª<¦¥)« Ü;¬&išKÏ3Yðµo׺¡ìR±·•þ»ý鑜¨ç9ÙÒOÐòljhÁ}+ 4ô\©öŽ/»{ù=wLŽÇQ1Ú0Ò=¤¤núq¯ªÓ%«7«”X9)@²¼.‹Å™K§l¼Aš^-“/®'¸¬ÓW,ËMM4ò÷dljñ{mð‹UlLk‚íwoÀÈË ›¦³¤?m[,jÛ5Õæ¤¹zpX=²Mk(ê…u}Ò{^YÄ{êP:]½&b‰a€Â„7Ô¸Ù••·½½Jj´údnàŽ ûFø–%›!aÅSõÖI í¦…h©™¸7¼VnøÏ×V Å“Þ1âI¶óbTÊ}®¡ )zh²sHWÍVuÉÒ¥Á®ÁBÅ-Ux–¥&–v…ˆŽ‡àaècÅi $÷„ vöýßøqÜ“N*ÄG£eA#Žy(;~ì[M·…r‰³èªD¾ò ì·²MÊ£ë6lj¥^àæµ¯®·¤£qV¦¦ŸBéAó¹'úÁèìN]£GÇÃ0ŽãÝô ÍôQ½÷á(ÍXM¿%ËFn1;Œ ïFI)–W<ò£ý n«’•ë<Ïò/ì„âÞ=5¤Cß éörWžNåÒh)OÆq°YÕèÞ>=‰¹¬ÖËÔÞßk8i¹¡—?(¹•£Ánâš‘I­÷ éMmý­Ór%ýÙO^8™zÞW°ÓÏð}9}sIå[¦Ë¢Ü¼¡_CÄçýñ. âä/—ÍU¿ŸOòÈ‹¼Æì¡Ó¬úʦeö˜¾Á‚Wט 8!×7ÅA$zGtÊѬdÚ ;\än:ø…|×Ð Š¶“"h!EÓOxíÁ xá,Åœ†}U³÷4ÅÿÙD`ÎÓý'J{Eyø0ËÓtt:u"ôº"ôLß½Ü6 sBÆ^9è® Êvƒò9€òÁÊ$nú›q-ˆù4ðG½» Ã1üŠó)À¼àغJ-ÈY¥:ìÀ,$:SÊ÷_æK²²œêc käKÒXKØdZK'¤û4IÅ‚û‘²æáY– Ì$jn&uUÛTý´{ÄB™®—+›'àCú¦ƒbtšÃ¸0:6znŒ!Ü”€Ÿz™671[dù±N¬×eN_ÛÜѳ.8]E_Ø ^¼„ãu€ã¹àx]å^ù”–ßWðÛ,­çGXµ/ ÐXtç^öÎxt꜒^WµWÚç*Ò~C§ÆfFºÿöuRÖÚ’Bh íf‘ ×q…íŽByØŽjµ{ï¸6ÿ_žE)ŃÀ÷¬IøçæÚà’=øÜ{| _:ÃvUdì’¸§üÐ’Idî:É÷úæÈ5™O–5KV«4)Y6Û9wC† íüÅ ±«.Ãò8 ìÌ øœ·¡3¿¦›q‘”S Ó´¢q —M›ÁÐÕÙÀ‰­«@Ë(â¶öRúHßýcŠü%mèrx"Ol2OÊdR§eõ7'¤®-ÃâDvf !$çì-ó1º ¬ Ü=[“۴Ħf™¡æ9>ñ^Û­NVuÅQ»ê¨ôá¢0°Z νG„#Äàðqh…ïîá9…çžSxNá9…ç\°tW=ÅöA#a9‡‡áÂ+„—/^"¼ ¨8ð —xNR‰ð\Ü‚ª«†J¼¿‘¶"‚àˆÐˆLùi ÔÕöJ¼aD[O7¤’C–«EZ§Sw˜®ÞU ÌtlZmñÏÏ1CçÅÓÖ7»­N¡S»)€Ùe~’àÞÎnN]øº iq¡ãÀvݧßîïoÙ5Mî«Ý€,ñ£Q/ •ô…Ûµê®ê<”ؼ™¸ƒëObk[btU¹£'ZîcHŠqw­_ùŸÂã endstream endobj 109 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 12>> endobj 110 0 obj<>stream H‰œWïoÛ6ýî¿â¾Uj†¤DJZ‹‰l¦«Õu@»´ÍØZ,)“ä¤Ù_¿;Jþ‘ÔRÚ¦@"³ïÞ»wç“÷ðúõÉåøí8œžžOÆ08O9Ì+\ PÍó€×Åõe58O'IÂA@r=àÌqWrO[“ §¿ÿÑR‰—î”æIpÁâB!XÆqI6ð`˜ü3¸H—„½¿ØÞçÞâÂ'Ô®Ó9g"à¾r§_Ô+[涆³…¹­m‰»¿xaÿ2ì‚–G¡;ðt±ˆó0px£¿þ8#8ÉóÑÙÙèü|4w£ù?„†LI.›ÜžžÂ # ûçÔv½†y‘WÅÚB]À¬(j˜/Ê"ëºSðCwÒš‰ˆë&ã_¼ó««ääƒúEÀäã™è\á=Üj°(¸Ð§ý“ÊÑÏ ‘’1ÁïÂcˆöîÁ5A84PaŸ¡Ôš{ÒÊ Åt³%|ö0.ŽüXx¿÷079¼…4»-ʮӵ­ˆ¶áßÉïBˆ„ØAð8¦ª[àaª4Û¬MmðçåP{S°ù]ZyfóúMsБ ¢ÇAɧ%þ4om$Xp¾Š¾”øøÙ”%¹‹FªøN°0àAŠjÍcÕ$m„)"ÒÞÇÊ‚ñf,ön1ær8’^±¤'“½ü»No,\˜êvâçqiñ·©‹Ší ðŽÖÊâ%%|ŽnÀÑéÄZs÷æÉ1ø°´M2ºx‘¾ „…±`$БüP4ì`!Ö&†^žæK¨Wa[’Š Ü\ÈkÂoÀÃÔÞ»ïQ {0Â’h{q¨·X™F9›Œ¢¨P]˜ÒÂ:ÍRRL®¦Ãȃó_AÜÑΓêÕ)ÚëTŠàÝRi®ô‰Ò‰Ô·DžÕµ™¯0)%Œ)Úª˜N‡Â3Kçaæ› ¼»ã0½³=7ÝBløë¾4ºê“O.}Ylˆ¯#w6ûÄç‡B9P¼ã½@qŸ‹-y­ÖðmÞX=TµÍHz›|A$•XØÓž(åÑ(»C $‹°%ë'¡‹ÛÙ^Ÿ¹ÅØ®Ë/At´q» QKMÜÈÏÁ2Ýw‹´ºé¹°ÿSÞ£|á¼§¿!ˆ ›óƒ*ŠDˆY·3ËglrÉŠê?¿qΜ8ÞaZ›ò®ÑO°ªµ—VPmÈ$–ä)•Æ5Ä®,¨ïi+BŠî¾Ä’)wрŮ¯Èƒ¾b¿ô•k7l;KÛ±¾¿Sã“v¢¾·ýSœ8âøM‘ôq>WÇmGéõ¡!Ãp—­}C©ÓuZ?ÀÕd*qš3w&]›b‘£ÇŠûÉzFdjòJmÚ…«yg¡ô2VĈ,I:ù»¦¡¼%ý/½®°@ÛÅ'¬²:j2ï4CM™Ç &Œ÷"ÐOL6tµÅ·*Ðy¯íîùa(½flÖS“ÀuëMsÛ ÉMì×­ú0q˜7˜Y¸-Ó¼íJ¸²w¾öб:o¡T®Ó|{ÙãK08§¬æe:k,ˆ’?+z»Cǘòœdцdض³NÉÊ'MÓ?L¹rL ­›¬ê>â’ñÖ¤¼Ÿ¼‰¡ŠvØ#–ÿ¿òÉ endstream endobj 111 0 obj<> endobj 112 0 obj<> endobj 113 0 obj<> endobj 114 0 obj<> endobj 115 0 obj<> endobj 116 0 obj<> endobj 117 0 obj<> endobj 118 0 obj<> endobj 119 0 obj<> endobj 120 0 obj<> endobj 121 0 obj<> endobj 122 0 obj<> endobj 123 0 obj<> endobj 124 0 obj<> endobj 125 0 obj<> endobj 126 0 obj<> endobj 127 0 obj<> endobj 128 0 obj<> endobj 129 0 obj<> endobj 130 0 obj<> endobj 131 0 obj<> endobj 132 0 obj<> endobj 133 0 obj<> endobj 134 0 obj<> endobj 135 0 obj<> endobj 136 0 obj<> endobj 137 0 obj<> endobj 138 0 obj<> endobj 139 0 obj<> endobj 140 0 obj<> endobj 141 0 obj<> endobj 142 0 obj<> endobj 143 0 obj<> endobj 144 0 obj<> endobj 145 0 obj<> endobj 146 0 obj<> endobj 147 0 obj<> endobj 148 0 obj<> endobj 149 0 obj<> endobj 150 0 obj<> endobj 151 0 obj<> endobj 152 0 obj<> endobj 153 0 obj<> endobj 154 0 obj<> endobj 155 0 obj<> endobj 156 0 obj<> endobj 157 0 obj<> endobj 158 0 obj<> endobj 159 0 obj<> endobj 160 0 obj<>stream xÚìZ]o\·ý+Ès³ä gHÄ©aÃi,DîÓBj¼H…8’ KEýï;Ã{f%­veG·I ~0‡¼äáp¾8œU®uJS®mÊ©ZÛ'âNSni¢Ò‹y"eûÔh¢N6¯ñÄ9u#ÊTl¶2•R ¦éT”Ĉ:I"5¢MÂÊFôITmyO“ô”ȓ͵-l?•ì#RÛ#³©›Ü®3û)ÕöÈÅv'kÎ⢬Ù)—®ÛvVxõ=ªë úƒ«ê{4?oõ=LEÓW_­Þ~¼Ü¬Ž¯¯n~¼~{µÙüpqq½z=<6M?¬ž¿?ýðáo§—Ãu}àèôjs>f'¾?ôýæßׯ7§Ì«.ÞoÆ:sp›ôì™mõzm']?"ÁA” $ ¢Ñ‚è 8ÈÈÈÈÈÈÈÈÈ%i&šÌs[š7hi -¶iiFhëÀûêÜb^|Ǽ<³Ð(ÚŒøøóx-ж¢mhçý«$´˜/„–Ñ´‚x8gà ðx <ž¯Ç<à(p8Šï8888|UðUWWW×€×€×€×€×€×€×€××€Óñ½c}Çú2÷5ö‡ah‹6£Å<ì«ØW±¯Æ¾05žÚÁ?¬¼Â#*쬦èã |Âîjº¹Àþ*ì¯fÈ%vXa‡v(½¡_-Î_Qع›ú÷ûjnc\ÑV´À‡ì@`;Ø@?ýK>p zèAZà€È_ Wüò—Žõ°ëÒ*Ú†vž_0¿`~½àz^3·‚VÑr.³$ì¿è] wÞzè] wÞzè] wÞzè]BïxxˆC’‡ø'ˆG‚Ð-°SA\Žy!/Èñ¼ ŽØG=ØO_•¹ÂïŠFë÷ âSAœ+ãÀC|+—Ë…Ñ´‚VÑÆº†v–c?F\-Ð o÷.â(ƒO†1ì—þEÀ%‰>¡e´­ U´-ð¡‚ÜöL3i|ü”à§?%ø)ÁO ~JðS‚Ÿâ5!^ü•à¯ç…¿ü•à¯%ø+Ánq“à·$È‘à$ø#Á þ˜á‡~˜á‡~˜á‡?$ø!…^àWû'øÍþq²:²ìh@®ŽWÇ›¯G²sôþôìü Jo-'òülœiõrsúîìü§/|”AÏßf­®Þ¾y³ê1£mSªïo~ù°Nãõ4Ž2žOƒ¹ñ~æ0PÃ0Ç j¸èxB >ÞP㨑`ŒW”S}<£r©9!O©AÎ)—Â`çÍÍõû³sË'/OÏW?·¹ºíº ¾¹ø÷êÛ³­^\þ²™)Ë7Ï/®7«ïý¿¿ž¿»íÿóÔRÓg?Ý\mV¯ÎéÞÐÛ7_»P¼}å„ÿ÷#/0r|s¹¹úðãÕÙåõÌÅñÍ?îu¯¯Î~Þ\Ü ûíÕÅåóÓËØá¥åÆ~‚Ÿç¯wUº:º«ËÕË|W‹«—ú³yPÜêèÙ³?b|²ÞfÀö€A ŠPc´3kjHm@w°Dñàv¤iØ"ÌX‚£&Ĭ;±„c€v$–”ÐÝX"1vJ,Ñ(»±rNÜvbIˆ0Þ Ûxhˆ0^·XRC„ñfÙà’o5D¯ÛX"D^qg –„ã%„ÞcEA_wú1_ÐO÷ûa+0€ÞÊN?æÏ²ëµíôc~CŸî÷ÃJpuÕ>æcŸ‹ÜöÃBpsuÜÈ·ý˜?ãtd·ý˜yºßÛÀ¹{¼0·ý˜yÅË1úacã¶ó!¯xqnû1òÊt¿‹¢ÇËwÛÇü¸>p£F¿…=àRiNæþɺ…ƒZx϶…!8¤p·}"•káŸÛ>žð¡˜Òï÷CHÁZD€mû…⑊Ýö I lá:È·5ƒ5…¶¿v€¾åOÔ½‘Ãok Èá[h~[S.ÔNÖ¬Øÿ]‘Á¹"ÿaŠ -Š m§è<¸ÄNñád­`PÁ ‚Á¨F(T0øÔª„FBØöW' *T¤€ VdÙ WdÙŠ;A£:„˜P£ŠõûV9NÖÊ`HÁ‚!C † )R0¤`HÁ‚!C † )R¸®Âu®«p]…ë*\WẠ×U¸®"È+²FÅ% ÈÙƒR”sÒNYxÈwÊ;&°ßª¾ËXöâ:,[ÚN½–-°ìuŸ“u‰wþYú• X Àˆ‚  ¨ aA‰ L¶Àd L¶Àd L¶Àd GeIïW˜V¤+M%*QeÅ —`Á%Xp ÞV €‡Kp§u²f0Î`œÁ8ƒqãÌQBIŒ3ÿ­JY ÆYø^I‹%J\º¿´…Û›q{3noÆíÍ!ŒÂ!ŒÂ!ŒÂ!ŒÂ!ŒÂ!ŒÂ!\‘EmJÔÚúµÆF80áÀÖÚ®<¹Ø†˜Gˆy„˜Gˆy„˜Gˆy„˜·-Î!æQŽ"ðó1ó1óv‹zþså”V_¯iv¡“ÕóúÏö7Òã?yYäöGÏ)YˆB¥,Dá QÊ@¡…(2PòB(iʺ†á9ÆÝJÞÑø»†â»o.Þ}|ÈA{¸-¬ù“Ìwgžûæi¾%ÎwÄÞõåë‡iÎwÍÞõòèúµÿUAÂîtÈSh¾Ça„÷ɰ~ž{•§«Ýÿ¸"çz§Oè=ë¦ç0ôi¦« ŒjŽ>%ùmHžªpά9Dßwþ¼”î -ý7¹5å¹û$žg–h³úyÌò‚Ø[¬Gñû‘ïUH™4!1¦øéŽÒíOK±Ïw[Äô"î7Ò@¸3k‹ âæùáG>ôñÍê»Óã—”ëÓ«ëWçï6ç×SÑ/ýãŸ0ø—ܾLO^ózŽÆ_¡í ©5‚_±ˆAC ¼#5b|à{Ú †JmÎWöòIÌ¡zèãï%ÒÿéÖ?ðWÏUh¨wƒxéŸÇôS̲`­.X[¬­–ÚÉlny! ¥“–ÂøûƒóÒ @4`–Fâ³4,P0K〙—3—¥0#df^ 3¬8/µbVœXñ5Æ ð endstream endobj 161 0 obj<>stream xÚì˜Ík7ÅÿA÷}ºú‚H»i°¡&éÎta° â@y…ä¿Ï9w^Ò´±Þ¸UYxat<3ç7WWW£'ik)'m=5A3’¸&í¸$†׺ãO“•‚Ö’kk>Ú’Jíhkª6Ð6`àíÀ…w¤^À9 oH¼¡IrpX1qU49 „QTˆJÑ ø¶Ñ“˜R L+-YÎB¸P€ìN²7 K¦¹È¥R€\ÈU)@®$£RIÉr#‰‘F²€ÜI;Ér'é$ ȃdy¬ ’ Ê$+2”IfŠ2ÉŠ!A…áÈ‘T ô_ò®JÄk0džCc2Ã4Ù\ @öÌ[ ;#4 Æì—Á‡A®/5â5iãØ;Ç”¯ ƒˆ‡Y%£ñ–1×ÎôQf„‚ v)î …"謪L…ð‚|¸€\¸ñÅ•¢­ñ2£ìHAo &ª€Ì³š“g^®’\c« ReÌ7#¡Îa4Ô¢[ãÃ5yÁxrä%îÉ£$êHÞuk 7Ô aÊxdžp}0ÏÍRÉìbBÑQ`Š“Ôj*æôìÙá™:¼¸f‘åôê÷Ãχ«?nÞÞÿ ù·»÷ÇÃê«óÎáõ¿n¼Á´á|}õüù†bP´‹ L_ÅX`Ú*ÆSW1%0eS㫘[ÅôÀè*fF1UœW«ØYÅü¨/b40«Uì˜Õ*vÌj{ Ìj{ ÌZÿz¸¼ùðî¯ãáõñæÏãËûÛ»û#æÇùéîÓݧ»ßèîŽÅl¼Š‚Û¼¼|ùy­¶ÓÍ¿'íå÷ÝcòUw,Oºs‘¸º¸ÎÖ:ÏŸŸÿéÝí‡ícU¾øæIØejçíöÙÚæÛo·¹«ˆË˜Úå¼ÝÃÞ§v=o/aoS»·×°×©ÝÏÚ¯[ìÂLN™A=FÿÚÃ=Él¥úÒ:þÛ"÷ºÉ ^Yðê‚×¼¾à- Þºàm Þ¾àýÿuuo ÷´§¹ðËÝÍíÛû7xZc*øÎ×èÂâVù1A«.½í¶'Aï|…Ô º<.è…ÊÒ…ÊRnÔ|º5²¾·lôðÏÖ ;ë†ÆÖlº²±³pl' Ó]]ÎûãHaºý±±³tlg Ó}µƒ>5Üq³µýÔŽ­mùÔÊ©ÕSk§Ö?->6Ý9ñØð|Gâ¢O;RWÇÛ1D/«˜8†è¾Š‰cˆn«˜8†èºŠ‰Zﲈَ!z^ÅÈ‚7ª¾µ®Oç„~m­í¬?ôµm»/þ(ÀRØ*Þ endstream endobj 162 0 obj<>stream xÚìXËŠ[Gý•†¬u=úÆdcCDœÈb`ccˆÿ>ç´îÕ`IÝ’G›¼ªÖ­>§««N¿d¥„¬Ô  ¦…T‚Õ*Z â´¤ÐzÐH›‚mši Hhk0Ͱ-x´`-7…•àY`58¾Y³£6Éi £ßrÈê°%äÂþ5¡¿…’8ž€#J¨‰chh1÷±šÑï¡eƒM¡q,ø$*‚@ÐˆŠ™™£ÑÐ`_‰h°³`Òªˆ#ˆ& ÓVdÆÅƒ . ÌÜÌL‡#MâFTe®ØÌI –TÀ RÉøì æœ°2¡ wÌ™eÌÕØÌ• S07†¡(QìӸŠb ÄÀªÈ³¬Ê™¡4@8&©=áHŒz" ÕJJÊ•X#sfZÀ¥™ps)l€¹`®,¤ƒ¹Çã¨PD¶Ü… Ô\˜ŠE<«ª@8ØÍPÒ,³ÂrÞž  ‡”õeoe±ºX[¬ïmí‰ýãLf1RjѨ¿™SfN9mæô‘s˜ÓÍïÿ¬¿¿?¤ñ[÷Qw–}Ï‹£ð(9¿ Ô0} ´v–cIó(¢Tyu+q±²X]¬-Ö—þy"mÓáˆpÚÌé3gš9óÌYfÎ:rž­®å¯÷î_ßýí.öKIWØÏ›_îî?>~øw V©ÖÍOŸî?o¶p«êŸ–‚ Û?ï>>@zbtÅ|é8†ê)ôYÞs¨½ê›w^uîqì9Ïç›:\†ð2‡çCxà ᥠám¯^GðçðÖáe— R‰Ÿ‡x½€—ŽOCü\ª;>#lD>L£_äŒüÜÒUò±tw’úµýüBÍ—²—Ï}Ýš‘òòõ&õl{y²tÿl$ë‚ÒõܶæWnk7ìkzÃÆ¦þÅ<…!§ù†°ã›ÕGŠJö2=§(¿2ØrCekH‚N‚>#)¾O¯:uâ '–Üpd¥õ ÂÇññmôpÇ=½®ÕØö7ôñm4-·Ð4y8ñy?b8¬ˆm‡;¯ª3¬Ï·À|z/Ï:n¥9Lhv¯‡ðzš®Çâz¾­Õzà¬Ǻï¡e8rÿ¿fâl§Å™SfN9mæô‘ó¿ó~ûW€}ÄI endstream endobj 163 0 obj<>stream xÚì˜]kTI†ÿJ×ëéúêÂîÞ( ºwƒ–Ký÷¾UÎ(‰ã»/7p*§»ž©î~ΙîXkTÈZÇ/~ #ë…ØW&îhéB¢Ž«’ ¶n¤íNª×J:â „GÿNΈû o•lª‘?˜jð‡PsÅU©3x¸×xÃi(ª•¸”¸Ñ´èщÙ"e yA‚ýX½"T- "@†#öŠ"ŠW+@n(Ý È cñrG1^@2ƒ%ÈXi+AË8ÈaÙ¡ ¹Â! r…CdÌÔÀ„c= ÓŠdɬ VƱÒ‚×ÐX£3È>¢3ÈÕy‹g‚xèÚÐîagÇyÓ[޹ñvÍgæ ]\l¯·W—_>~ºÝÞÞ^þwûâæêúæsò´lÿ\>þý÷§åÙ³ÿW÷—X!Å»èͶË7LDo·W/Žý؈çöØø­m_ò„{ï¶¿·ç×—WnÞ?‘"è :ÿõñê˶{Oñ,áÖËHÙý{ùá QW~ºrî6ÜO•ŸSÔv>UçSm>ÕçSë|j›Oíó©c>•Ëöç>^"£f1¼”1’˜¾ŠÑÄ´UŒ%¦®b<1¾Š©‰±ULKŒ®bzbd3ËI‹}ÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbI‹mÕbM‹mÕbM‹uÕbM‹uÕbM‹uÕbM‹uÕbM‹uÕbM‹uÕbM‹uÕbM‹uÕb]ØFX¹³åØêùÝèÞ¾óNoeeœßÊÚ‰ iœTìÂŽôø»<‡ÞÝ·ïC>VÿîÔ>»¿ œ>stream xÚì˜M‹7†ÿŠ çx¤ú’Æç“…,qnC »CXƒÙ@üïóVföcº'n_ ÞKÕ–º•¤W-õh­)'­-i‡é©¸-§‚m%,%ê ˉU`%IaXMRÝâ&XäôÛ’©Ûžj¾çT­G[#ïŠR‹g9õ‚Ü.© ¸è£;¯[*9Ð+vBƒ£žê%"Ç2j,Uà”TˆÁA–96‚#©{DSÑìµzd3ç€\Yá€Üð ¡âÒª; wqã/áp"*îœæŽbŠÔK$äN…ÓÝi‰ÔÜé‰Ì» œ¨z„‰­ÞÜœL w'ÈÝÉ`qv2YââdªpœŒ9dr2õÄìd̳7a´,X#Ãz°¢^c_´êŽ$ö°—Âæ…µåªÈ«o rÃ܃ÜÐnrÃ:˜€ÜÉ# wïB@îЄaí$cp†‘HpÄà@U&Rq 48H54‹«Ëб¦Ö¥"$!8Í#œ°n™Õ# sóÈBYÔ# K÷ÈŠI2Y]²‹ÈðÄØ d3€\]æ"öá@ R!Yo–F¹©G@nP©U;gÐtì«Ð}vAºp³zJ†já`Ëu÷‘¾~½ûŠÜý¼»þëæãý”ÿ¸ûçaw 6lÃßwï_4|HÐ;Þ¼9¤êúT[ŸZ×§¶õ©}u*嵩{*ñVCàO0~¹»¹ýxÿþš: ®Þ~ºýrÞ-wë¯Ê%óúŠ%Þ·3ÓåŠuªâº¨âõz¢õz¢õz:Žë:Ž1+ïŽc¢žÒ®N‰rJÔóD™O¬‰õ<ÑfÿŸ|ëÓM³6sOqNªÈ€—*zÚé„výt^ЩÄYþ´÷èèÿ%MìbÙ4—iI-·„S-e¶ºXKì<[‹,©¥ÅýÿÇõä`ûÁ–|ªÑúlr±ÆÙm¶F[$µ”º‘R‚b)ÝDÙûe©Ð˜fV†ÕamرÝŸ5žåRIãH=v=E8Ñåìãá(øéÆ|šG)ÏÛ]Ý|ùô÷ÃîýÃÍç‡w÷·w÷IìUËøãï|dÁ#%/§ì|§à¥½Ê_ûÖOík<òÅ>æÏŠ‚A}|KœÎ }ú?N3õñ2•š¦ÎŪsïû‘ù¯ƒáõ endstream endobj 165 0 obj<>stream xÚÌXMk9ý+‚=Ç£úJHö²a ;$¹ 9lB 8°ÌÂæßï+y<ÛÝI „ø`•»õžªJOU-W³”Sµ–JÃБ¥Úr"+)±FNÜ £$ÑŽQ“fŸW’ÖŠ±¦Â>ß@£Á¥>ödøž“UÆH©1x:§Öý¹¤^}ÔD¹ø‹ÆŒ ×얈«-‘¨pR‰À /µ¹A‰ŠºÁ‰*û¢T‹¯f£̆`-ƒ¹I…æžÝs¯FOœY“Q†` Ë07f7òa¤ÈMq£$Vv£ÂènXâ2ž4c˜‹£Ì•ÜsU7À\Í 0{(ØÎ#¥lȧ1˜ù07E8 fO¡Ïãîk ˜;rhBI2`Â0“±…’}/D“(†!v$BØ“)£:ªÁð(¤'økša`Û )ñÂoQOº ð˰ۢ…‚¹ÀS0_TÁ\ÕQ`ö}0p‰ùZÌ.ƒßÒ >C²¤ÃCÒ¥CV\k¾b#O]©0ÌQ–”·A»Ê¾¶\ÅóSsR—¨A:êuý©‹ÔVW©U0Ww{¯.Ã<õ‰VÁ<«`nÍå æŽ”˜¹CŒJ† †° aóÍΙâ< Ù†? û6!×ÅÕry¹Z'Yý¹Ú|½þr÷ç·ÿíVh³á¾_}xöâsBðâõkGnuœQüý Ý^ß|¹ûŒ¹ ߯{†«·ßn¾?‡¦òrÑzÒ¢©FÝMF¶0²‡‘”ãPŠC9¬’Qjgä`‹r ÐC³“<.ñ`khç9 £(’É2Š"ÕsXþ^]]ÿöïnõawýÏîÝÝÍíÝ{u‘ã·ëCYÙŒKƾ(½{x¹¯SǸZ í¸aå\WžžýC» Ž›‡q»h“‡oÊ}ÝŒ;ÍS—¶ûþÐ,Z§ ׇ†=ÅtèQ/'ÝÔºwó8ºWÔ–Mù8´å)Ç´ËÛ÷4a¿ÒÙЮŸ‰›qY|.'±9­÷͸S¾ö`9ù…ZTîU¢´ Óy†<®­“]¼:òc—ã¯É‰&®:WHŽ‘?÷ÙŒ”0RÃÈFÖ0ÒÂÈFö0ò'o¿=Jüöø×Å…Dq%Q\J×ÅÅDa5mýÿnu¦&Yo‹5‰§ŠR=­(ÅåÄq9q\N—ÇåÄq9q\N/N/N/NRÓÿ Ék<µ endstream endobj 166 0 obj<>stream xÚœX]k7ý+ý–F#ÍB ÍKC5múdò° †àBéCûï{f5{¯í»w½»D“]3GG£»ªJ‰TjGÓ)g#µD/Ô2qih™¸W´…J´B’ ÚJ¢Œ¶Q-­‚tfÔª·àLøëÏx{&co™ }µêþ.”SB$É© Co¨»"N»A[v2)F– 2kC)3à–A) Ï–À\Ä0 –À,Õá`®Ùá`®êp07‡g07‡g0«t`öކagkŠÌÏ`îpÂÀÅIçä!hN؉ñ£OÌ·Ö…á)O¸ hq¸"ÀÔÒpƒ^c0·R'¹5¤`07÷³€YÁj˜ž40û̆Ä>nƒ³lÇ_6†Ùe›à`îì(ó¹vT§’à„IBà>CnÉÈlÂÌ_¡  èþªR)Õ_5Âø YúCõ`”q¥º½èWš;VÁÜ0ÕVÁ¬ +˜Ý?C½Sï æQVÁÜ]sU’ ¹RºótÁ?¥ÂHkçB1IóqaØ(Z¤€}2•"ò‰M¨F2Í)&X:ªÓPµ5ù¤´N5Ã~ÃH09`†¦Ê 7¬€ZÜ-P¦BUÐPUµN(¬Š\ïÞÝü ƒn>ÞÜþøöøô§/ÿþss‹23,Äßoþxõâ;A^¼? rZCÛq¨‡Úqh? •tšCù(ôNÊ´áâÁWpüòðíþñé;:3(|Ÿþëþ¿Ë´ å仸Åõ¸â6W˺â…zòóe‹âãõ$Çëiw;n1´O§—/ÏlŸçw1·Ó!xl×MNÀ~ ´ë@™~¤¾úq{ ¸oÉägîä=uÑ‹x†¸ãé,_¬?ÖV ´°íúaƒL9:À»:Ý/®È-«rÛ’\Ý"Wχè¸$]h^h^¨¿zm©Ÿ|¼ôø8´¶)ûá²OÜ×}ªK>µMŠÛqÅ:][¯(ÖuŶ¤¸oRÜ+æq›¾¢x}à…‚ò;úÅ|¼ æ ·Ó]ÿõÎ’—vÞÙÌÛé'ÁPW€óâ¿.€}˜O@¾–|˜ô”K`YžŽ¥òÚœ»8.æ½ÿë†r•‡×‡ñË2òû/àÑåϧû‡¿<>=ø|©<_щ²÷ ŠÛ—ß>Î…Vç¥gŸ^3¬íœUßÊŠShÂÔ3F¶(þKÕûO6îRÚFV>g-o*µÉgLÚ¢têDþû>õaoÞ%5¹L§+Nð¬jÍc*NÕ‰°m;:‘ÈãB‚ýyŸÚ1Ÿ§êžyÖÕŽé84BÞ¢–‡Bu@q?«ÍÔŽ9=ßóéŽx]-O’úiq¥¾aqE'ìÅ«‹w./‰Û9ñÛë‹®yëyîüÓôýÔQ°þáa´9Z޶D+ÑÖh[´±µàkÁ§Á§Á§Á§Á§Á§Á§Á§Á§Á§ÁgÁgÁgÁgÁgÁgÁgÁgÁgÁgÁ׃¯_‰ç¼<2ûýjä©írÛ†ÓçRü<¾z(o•í‹ËKŠù÷ÄyLoUÒK‚pп ¦p1óNŠX}þ™3&.·}Q'8vÇ’H;)ÂJ÷2Ê“eEÌ»›u¸É;íŒB(n§ ;ËN;£¶ŠÛ)ÃβÓÎq  âvÆ BvÚ9®M$ng‰ÒÜi';Åí—<ÛWÞq©nç¸ÃPÝiç8Ú©ºqC¨;íŒÃ°ºã ¥¶ÓÎq°Ps;ã„j»ìì=Vz£>¾´PÛU›=ö6l¹=¶7å}ÃIìÉ=öSÝjäÿ níâ¡ endstream endobj 167 0 obj<>stream xÚ¼XÍk7ýW½w5úâZZˆIÝSÈ!¤¦„»šÿ¾of×j»]t0;»ûôFzš§ù­{k)§ÞzªK$¸í9‘á¾Sâê÷œ„ý^’áªIKǵ¤Ïk*Ƹ6Ю=5®¸Zj&©8œc®œÌWI”qCÙQÈJ¤Ha58¬!¨>¨cröYlÓTé’YæD¥*0W*À\ æ`07ÌØ2˜»4`6œ2˜­J2ʉ³ƒ dã5#=A€…ibWdž@ÃtYÜk€;¨gd‰‹€™³K fvm!–1˜›ƒÌÍÁ æŽ}0h˾ 1ÙJ õ$câÆA€-‰ošáO°0„ÝÂcl›@Cb‘‚9#h€ ‚×$ô!¤  Ë–ZÀ,`nÒÌÍÁØéÆ6Ho`V¯LÓ%‘±!¦AÁœµ&¥7îIË55 AÉ :!)¨˜œTã• Æ –/¶ŠìX€Vò̵cÌM=sW`îI±1ê[dÌæ%Q9•Œ5ù—ìÕ‚§…â•×±F–ØS\ò #ðí®–ŠB6k9•â¯!@fÃtKEYYsõmÂjKÃ>Xsól`îxõêÕöÓ;¿ËémrMÀoSÏï·›íîÍÍ7œy»M½Äóíöãvû{"¿ýú Èû(ŽiD¨SXvŒª±úûî£ÜZ=Bž#à}”Æ^GXÿ% ÿ'Ð]DëQbRžSQó1Œ£¶÷xNGÑcX Síñœ’¼+I^SlG<§%ó1l?FöxNMªÇ°çWÄ<''rºÅé“çä̇œ~näCNž’ÓûD óFf‡œÂs‡œ²÷¨=®Ñ¿üíÍö×?ïŸ>zøÃÇcÆÓ×)>}xþôøpÞÿ ùúåÍãçǧwù[lëþ÷~d—ÈÞFv^˜Ý»ü™VùB+ÿ0ƒ'!¬ÎT¼¬ÎVjËÙedïKµígZu»Ô¶Ná‰[¬î¥r“/Ó¶DvÙu¥¶têñÞ¯´%žÂ»+ýê±:¢¥Ú†k¨Œìy¥¶|êñÞ.µíSx¢p%Ê¡¶R[ ×0ìu©¶ç¯WÚ²Ná‰Â•<*‡—ö2 ×ð8‘xi/“s—+m%Oá‰Â•2*‡—ö²®‘q"ñÒ^&ç×Kmëžr¸RFåÈÒ^–Ã52N$YÚËôÜãr¥­òžr¸RGåèÒ^–Ã5:N$]ÚËʹÇùRÛ>‡Sê(]ÙÊ,>stream xÚ¼W]‹%5ý+ùÞÔwË‚¬‚‚ú>‰‹;Èâ2#Ãî¿÷TâNº…~ÉÃ%uorú¤Nê¤úöˆRKV´aè¥chµPF*Ì9ráñ»QǨEºb´¢š££„F1ϱ¿÷âK﵄‚¥S‰ž#—æ†Ϧu¬éÝ UQwxjïQˆ$ƒVˆ‰tN˜¯Ø¥H®¬ø*-Á• ©hF‚¨Y|µ‰À'ÉŽm"ŠÌ52Âטp4ÉYGpS÷ä ¨Q‘"A„$iašCÔǬC½‰ˆÂBÉA ÒGÔ «æ®¸"†Þ¦9Ëàð€þìž ŽÐÌÁ=wÅàhŽÖÇ,8úDô"ùPˆZ U…Š¡Ñ@ΕuÌâde" Q$$ÕÌ 2‰æ‘ä!‰M8,O'%¤%ÁÉ­I˃ª ަ¹+¤ m"ÀÑmÌzÑ:(’”J–¹¦œ‡\±Xy ŒŠJwÅ•0A=rr˜µ<øj¨Q³Ü•Ã'nc1àˆ–Žf™Š“»rpôp)6ʧº"šCýKr8œÀYHÕá¶Ü•·b2‘ålÔb:AˆFí3ÏÜ "‚ÜU€Ã'AcÄ«W·ï†sky{{sûöó÷OŸ>>ü~»+m¸úííÇõëë×c}ôÛ×Ãç9}‡èËŸ>Œ…÷·»ß åñÃíû÷Ÿÿ|¾½»ÿëù›û_ŸÞ?||x‡Çþ òßÉ7ŸŸ~®_ÕòÏç—/ä‘ä^È}#yÞRGJù‰RyË]Y6’³/Éá²Ü¨¬ òx!çÊv>TÊΔÅewe}P&×_ʦõÉù0L—ò¶UÙcw멲~i½Cö—²é¶SÙa˜þruÝH>^ ޤ’©Æ»Å€§%Ç{ÈÌo¼¦lT—&½-úºU_:69ŸëÛ.¬ÏûJpg/³ôD‹Þ÷ê{lu:Õ—ôÀl&¸ ˆvv4›ö¡Xô¼U_>¶{=Õ—ë%€Mƒò* ÚÙ×tÚ‡×õDm¯¾‡v÷~®¯_è4(¯âÝM§}x]O¼·¿É¡Ý½ê+|  Ó ² Hvö7ö‘u=ÉÞþ¦‡v÷8×·]È4¨¬’ýM¦}t]O²·¿é±ÝýT_ÕK™ÕU@º³¿É´®ëI÷ö7;¶»êkõ@¦Amîìo<ícëzÒ½ýÍŽí®çúú%OƒÚ* ÛÙßxÚÇÖõd{û›Ûýüÿ›ó%Oƒú* ßÙßxÚÇ×õä{û[Ûýüÿ›·k€iP_ä;ûMûĺž|o‹c»Ÿÿ ½˜U@±³¿Ñ´O¬ë)þ¯¿ý-À zI endstream endobj 169 0 obj<>stream xÚìX]‹[7ý+‚>×W£I#í¶ÐÒ”.Ý-}yp³7‹ébãBóï;©o쇹ÆÐ>,;Í9gf4Ò]b­!ˆ•CÍú»H$‹·¢RÒ-N£Æ2Ì JEÈà\Bvx Ù=J6T 5*ªÅP«¢6æ–WE5 Í=º2±¦KSk‚“kU’t.Öt›Zš¸n€Â°&µ$„È|²$¥‚(5eBµD£«%¥™Oè+Z«’³XÂ…Ô’eCõh4VföDTfH!ÙŠÅæ#i*3d±X™¡„„Y‹¡ùX¬jÌ-¤œ”9ÅŠ&I4JÖÝ$ÕI4X‹ÑãJ\Ì–ÐÛ”›UåÔ¬ÞÄÁM,C` ˜,{!EÔ¢åœQ$q¤lq$G®½ ƬÍ,‹õE£’±ˆ[O¥MÈM3%ÑhÖ?i,E=_ $V5 ûdšÀ}2NÉ}E,÷Õ@è>Ë}-™O&ŒlAF…²ûD#»O4J4K4ŠÕ›mdµ^9 b;}Y昴¶Ì!+ìÅ‹áçáÕòÃæÏÝð0þµûv|»Ù.w«ÍúáÃûqøuý8nŸWëñ³ÍÛÍófû:.bøøóæåK¡úÑoQ ¿ ·Ã÷B Ø?†»À%š÷~òN€8ÃßZÝ¿ó+í¡S¶wOÁö sÖ›«ã¤ÎT·çæH·r;ÝÞ2 P½¾º¯O®ùåêË®Þ&uºhwmŸìnK³hõµiz\²>WÏ“z¼˜úY ᜶N´3Έ¿Þªœ¬:Έ¿Úª©«‚N]1¦:#þz«>úlS>Y5͈¿ÞªÞSJ'«†ñ×[õÑ{Ч>ÑŒ<#þ¬©F½{^®Ö_¥¨ýŠìÿ:Üøô;¢À›×öwª¸ÞÌ"’>áçI½<©‹XêÀæléÀÖ,ûyAïy5ç‰Ýóãƒèß”."ŸDÿ¬t%'ªÝDèD¥›ˆœ(we'¢n¢âDýÏGu¢î÷|°©{°¡}1xÿ²ßï–ÛÝò–¯wË"ß­?.‰ú¦XþGü×:C7Ï«§õp+QãvÎîoÛÕnµ~úió8¯¶¿ÈÇu]Md7Ü¿_¾¿ßm¶£íÛúæÈìÃ'ô å‹b•ÎЇt˜&ðõžsN¼`Åà9óø‡ó_; àà lýÅgqö)ø[€Ù{ÂT endstream endobj 170 0 obj<> endobj 171 0 obj<> endobj 172 0 obj<> endobj 173 0 obj<> endobj 174 0 obj<> endobj 175 0 obj<>stream 8 SIMH FAQbsupnik endstream endobj 176 0 obj<> endobj xref 0 1159 0000000000 65535 f 0000007774 00000 n 0000008049 00000 n 0000008303 00000 n 0000008448 00000 n 0000008592 00000 n 0000008733 00000 n 0000008878 00000 n 0000009024 00000 n 0000009170 00000 n 0000009315 00000 n 0000009457 00000 n 0000009604 00000 n 0000009751 00000 n 0000009898 00000 n 0000010044 00000 n 0000010190 00000 n 0000010337 00000 n 0000010484 00000 n 0000010631 00000 n 0000010778 00000 n 0000010924 00000 n 0000011071 00000 n 0000011218 00000 n 0000011365 00000 n 0000011512 00000 n 0000011657 00000 n 0000011804 00000 n 0000011951 00000 n 0000012098 00000 n 0000012244 00000 n 0000012390 00000 n 0000012537 00000 n 0000012684 00000 n 0000012831 00000 n 0000012977 00000 n 0000013124 00000 n 0000013270 00000 n 0000013414 00000 n 0000017518 00000 n 0000017570 00000 n 0000017622 00000 n 0000017674 00000 n 0000017726 00000 n 0000017778 00000 n 0000017830 00000 n 0000017882 00000 n 0000017934 00000 n 0000017986 00000 n 0000018038 00000 n 0000018090 00000 n 0000018142 00000 n 0000018194 00000 n 0000018246 00000 n 0000018298 00000 n 0000018350 00000 n 0000018402 00000 n 0000018454 00000 n 0000018506 00000 n 0000018558 00000 n 0000018610 00000 n 0000018662 00000 n 0000018714 00000 n 0000018767 00000 n 0000018820 00000 n 0000018873 00000 n 0000018926 00000 n 0000018979 00000 n 0000019032 00000 n 0000019085 00000 n 0000019138 00000 n 0000019191 00000 n 0000019244 00000 n 0000019297 00000 n 0000019350 00000 n 0000019627 00000 n 0000019693 00000 n 0000019840 00000 n 0000019986 00000 n 0000020133 00000 n 0000020280 00000 n 0000020427 00000 n 0000020574 00000 n 0000020720 00000 n 0000021976 00000 n 0000022029 00000 n 0000022082 00000 n 0000022135 00000 n 0000022188 00000 n 0000022241 00000 n 0000022294 00000 n 0000022347 00000 n 0000022611 00000 n 0000025647 00000 n 0000025910 00000 n 0000027875 00000 n 0000028151 00000 n 0000030547 00000 n 0000030810 00000 n 0000033827 00000 n 0000034092 00000 n 0000036834 00000 n 0000037112 00000 n 0000039652 00000 n 0000039918 00000 n 0000042642 00000 n 0000042908 00000 n 0000045288 00000 n 0000045554 00000 n 0000047983 00000 n 0000048262 00000 n 0000049794 00000 n 0000049954 00000 n 0000050194 00000 n 0000050440 00000 n 0000050802 00000 n 0000051045 00000 n 0000051501 00000 n 0000051995 00000 n 0000052228 00000 n 0000052285 00000 n 0000052442 00000 n 0000052577 00000 n 0000052772 00000 n 0000052914 00000 n 0000053120 00000 n 0000053320 00000 n 0000053516 00000 n 0000053716 00000 n 0000053878 00000 n 0000054056 00000 n 0000054209 00000 n 0000054397 00000 n 0000054617 00000 n 0000054829 00000 n 0000055021 00000 n 0000055209 00000 n 0000055427 00000 n 0000055623 00000 n 0000055781 00000 n 0000055941 00000 n 0000056151 00000 n 0000056372 00000 n 0000056569 00000 n 0000056778 00000 n 0000056977 00000 n 0000057198 00000 n 0000057413 00000 n 0000057562 00000 n 0000057711 00000 n 0000057854 00000 n 0000057967 00000 n 0000058136 00000 n 0000058340 00000 n 0000058512 00000 n 0000058696 00000 n 0000058844 00000 n 0000058986 00000 n 0000059146 00000 n 0000059302 00000 n 0000059444 00000 n 0000062397 00000 n 0000063510 00000 n 0000064669 00000 n 0000065723 00000 n 0000066950 00000 n 0000068144 00000 n 0000069635 00000 n 0000070885 00000 n 0000072027 00000 n 0000073057 00000 n 0000073094 00000 n 0000073119 00000 n 0000073182 00000 n 0000073317 00000 n 0000073402 00000 n 0000077392 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/advmonsys.pdf0000644000175000017500000013417010345704024014114 0ustar vlmvlm%PDF-1.4 %âãÏÓ 376 0 obj <> endobj xref 376 11 0000000016 00000 n 0000000964 00000 n 0000000516 00000 n 0000001217 00000 n 0000001454 00000 n 0000002005 00000 n 0000002041 00000 n 0000002263 00000 n 0000002340 00000 n 0000004747 00000 n 0000000780 00000 n trailer <<1fecd0003cfe6243bf264a6c2cd9ec9f>]>> startxref 0 %%EOF 378 0 obj<>stream xÚb```b``ÞÉÀÌÀÀ*ÅÀË€¼@1 äXäÌø(ߕҸèíl•)m& ûï¿}ÔièÞ¦Q,’dè¦,ÒždèQ¦ e'00400p0€( ÚÀÀ(e+5 ‚ yì@„9Á"* <<“€NÚÅ4ÓyÉ‚i<À¢Â ù ?PéU ­ÆÀйŠìk Vb`(oÒ, BªOx,' endstream endobj 386 0 obj<>/W[1 1 1]/Type/XRef/Index[43 333]>>stream xÚbbRb`b``Ń3Î ƒÑøŃ7> ´â endstream endobj 377 0 obj<>>>/LastModified(D:20051207200712)/MarkInfo<>>> endobj 379 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 380 0 obj<> endobj 381 0 obj[/ICCBased 385 0 R] endobj 382 0 obj<> endobj 383 0 obj<> endobj 384 0 obj<>stream H‰œWÛnÛH}×Wô#µiÞ)8v²ãÞkef{ZTKê˜b lRí×oUwó&‹J²ëÆêSÕ§Nºy"¿þzóx÷pO\òÛoîïÈäæîÙ%™„ð‘Y1ñ‡Ïÿ ŸoääÃbr³X¸Ä#‹õÄv×uc²Èˆz•¤dqÄÇ’x.þý/¼[”Ä‹×Wõ«Ô%‰9øènb}--«-/6d±eäéþÉö¢Wß÷%ù²g%­ð›ç“¬ØN’éâÛäãbòñÁv xM€ˆÊŸ{ˆÊ3ç«ÃãÔuÂ$Mçxþ‹õA,És½/øÛÔŽ­ù7;pÉVě۟ØÒö]7"Óÿ,>_:Ø¿|°ªÉûs“؉çóË%ÍÅM0®8€\EU¯0pì:)|ï«ÀÏõnGËÓXüpòm;P¤4½Š6ºMSÃo¨á]¸ƒ ÀŸ|) û‘ž°ÚÞŒTgD¸]ð|· èû*`ä¤aäzXêÕÄ¢E·ö( ^‰’ˆ–;Rq‡©¦Ý>f{ŽªW@Yg“r]çù‰,…¨ ˜(”ç‡Çß§‰E^­-—ü¤ùÐKRÁ™;ª—krxfWçžx6­ÝÔö-ü„CdË!äK_‘À³UT]»Àks0¸ÝF>ÓKÌÈý—gÈLeY²LX øáÍ‘å¹C ò¹†¼ôo1')êò%•PUQ‡zÝ¡úÊÜÞi÷ë¯?oàˆY2²u±ôØ´+.Õ‘\øbÐú±on Ž¥ÅŠ°+*IrFWxOõÁ6ÉÌE©GùîxÎà ˆÀó¼–Ü1SùxEsRŠßÒJ}öPT¬,XEö9=É6ÏH忇=2ˆxAXA—9âÈÄn_äK¦e²ªË0Ï Ôp’ÚÖ’S ¹E™¯ì#_ª©XbêYøá™úV%6 `–PÊÒœŠçïK¨@yP$qF•'þå‰"×ñ¿'<ɨðh^\ž(Ð3Jj} ÙÛ¦DZŒ1ÿAí‰|× |%xW§W´§£Ÿ½/‡;p„ªð÷ï´ÊxóeïÖÈv‚žoÅhÖcD¯U‚¦ùƒN‹ð÷áL=–àŸñön²Ó™j #}Ók qöÏEÌÂîk"ù¦àkžÑ¢"š•<#›š[¶‹µqÓ¥ ÉN“¶Ò”¶Hk^ʪí+4Ðg/К¥¢ »dI%S„LJt0 EÙЃw!TýÄsO3ö'í©bk6|SsßokþbÊ=BÜS{n%¤¤Å9ö•’ô‚ž—¶ ;(-V‘nØû¹òŠ‚ׯÓa |Į̈d5»ri( ™ºå_¬ß«¿ 62E¾þëá/ÈñžÉ=×ÂÇËV×̤jxà2Îðw<ØíEYá°ÔƒYo¸~¤1ºRÕN#'9ßqœ’k^@8òkf¨ÊYÓC‡ »‰6ü¸@b] q{ogMÉõØÚ"êªIÞ‹SÉ(nÊAö¥XÕ™ÒRdÖîÀr±ßL“ŒQtz þí@o±©@`˜ÍŽƒ1LWë®3µïé+[X:´³`ai4®©YM Â[,.´jW³>QÏ5Ç6a‡ ¶® •.– Ic)Ììþ›Æ††»ñ/uÂâajoìfa]1€ìùpw¨°‹L¬¦fЍä4lKŒ,+H.”úÜÂp6/qæaÔ)Ð’mx¡r80Ü)`ü7àÀJ õ9kmopª*—2ºì\ÒËÄ»£Êsp Ì]HÜŸÏA<Çy4¿>î¼Ë[Î97¼ð}ü(„FsS=ÿQ ïó»1-O¥øÆ²Špl^mœ~ìŽÀ^ÙKÕE™Ès¦¨‡·Óº¨Ö2_(°iÚK·ôbõ|¶‰ƒÞ™pUZÕ€×-z@<¦×ħëö ¹’¿æ¾œ¾·äª…É­Ô¡ºQÚp2©G`ÊȬäKÖñJ™Pœ>ã£ß lUA˜íaµ’usۆƳ‚ÞOm¡¸·|‡lãkè¨ôÓĉBœ<ùÅz€E ‡Â½ÈjT[(ñ4´D1žÇ®¦þ!¯Ê³I“ÎG- r"Ù–Œ¤vcÔW‰ºÚô“13ÃuðÅz¤EMsÙÚ9FKpM‡½ZOàáŒq¾4ÅP­Um:ƒÓ÷Í0;°‘À\dY{ÞìŠHÛ&̰;.Ív9ÃíK]–;Vñxˆo°?ÂjØÃ–4Aƒ´qoh‘56 W,…*Ñìº{XÝ€ÞSìÕµÛuh Ã+‡ëo¬Ê¶0xKœq6û$€WvTM®2÷öHÑç£Ã×úza.6 %WªX|ûBßÀ€'uÇ¢€´‘*yÕçüù&ÇMéø•±.Á*J–âa8ÙàÙwª¯zèmÀØÙ‘!A{Á0xÃŒÛÒN«ÍÇ'wü3bê%ã†n<¿®Dɨ˜ö·÷>|”:~è&‘VÔûAÎM×i¤ÊØÌ3Õ|ÐCi7Mô\ms+³nÑ„úíÖ T âë:'˺VW½•6l¹‚á@KP™© {Í’U¯·—ÁyèÕ—Je€¦ïW¥ bƒÈ>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰œWMs£H½ëWÔmXŸ;ᱻݞmÅ8ÂÚèC{%(IµŠ 4Þ_¿™U|èÖD¬F"ëUæ{/3oŸÉ¯¿Þ®îŸˆE~ûí÷‡{2»½±H,áþ#2Îg6áðüžïäì÷õìv½¶ˆMÖÛ™EÖ1üj}Ÿ®%±-üû?|TÛQ!àOd‘À ÍȲ,‡¬³™AæëÿÎ>­gŸVxb‡ÂnPèSìúS½÷#ú‘gÂy¶ ¸P;` |ÇZÚ ¾î!HÛôZ23žž6ùBód#Äy5$‹Ež–ðŠ‹üu>†Ð™F¨rчÚæ`8Õ«ú£m}ºý“¬h~ é®åÕ¸üÀôà[w—ߦ/„µ¢<¯XNó˜ýž{5·«ï*ïjTËY¼Œþ6éòoÉÊ&[¯FQ²”g<§åû8éü«q:>jÃõ'pÚÎåÎâ¾<ÓCÂóIŎǤ(áGryp5rÛA¹xS´œÎpÔËð®ðZ\^¢\ü)»q;XQ×w­Ížq^ÿ-/eun;ÿYÿ18êþà¿}°¡‹âñÃi³µcŽÜ¥?°P¢×wc½ç’ÈÃvËc–JSÉ+F()(/‰Ø$ù<0€çó¥T'v¸!’g‡”V¢”7DäŒlEIª=#*W7ê£Ê†ÖzƒÇöt»YئíF€{~]b„¹kÀ›p#%“•9šPÛ¾*£îiM'ÔÙ÷*E_¿4£PŸ >á!X²EZµO9p8ò"¶Õ‰–lôÔåUÓ\8ãÔpÇoræu³:%1}Gµ¤Æ7^í‰æx1÷ :· )Ï*Oè‘ò”nRW¥1'ð³ U0#gÍ=£"²b9Q‰´‹Eš²ž5CF½äô¢ŽÚQ ƒW@48è$a™ÈeURT')e!´‚¥ sÒ¾(ÅQ“~Œ—$á(ƒCZá¶ã¼€ÿ›ÑŠÁÃ|£ÌŠûuŽÄó8€fD~ÎßKÍE¦ëy–S§ç<¥¿(Åá0pñ„m;…½x9Ç,Úx½´%ØKp¢Š¥ïö3=¤dÿßÀðeŽÚ¸÷Ó­!„Áº‚C–gºŽü`QóÿÖìÐâÛØ&ÝP‘½¼nwzøl} ¦ a»¼Íý~#ÎЭ‹@AÔi¢ie‘ ß©hÁê‚Böè‘ýÀH \¯ßè:È݆⛾†‹”¾)9òа¯æ®˜Ë¨“RJJ‘â­k7m"I£gz\`mÀPÕ Ï4 ü¥$XÀ£ø ½iâKÓvšÞ'¨0Òpí™3ðö|fÏ‹b¾p Ý\s¼¤'²á•¼èrµéØ}W†®Yöój®œ#WÕ 9íŹôºëû ¯ ü©s™0ÌZÒÙI[w2Z)ïçYë¶fãn ¥—ÀêPBM  «p=»ããœ±íŽ•êºø6=T{¡ÆR­¬ò¸XƒëÅêD\ÀÆ™xJ¬á„X?Üdà” 4CÇÆïá”Ï(F5AÂ-aÒ³nHã7ɧU¦gùq/§óîóëºEÞÒ›Tì)¯£ßé-nѾӫ׿ÀôÅ©>VyîYÉ(T²RPº*ŸÄ tèup”ôd×'WñŠJl¸Åþ]òX©Òq בút‡1í¦I%sÉê#N¬ÄÆ —ðyhÐ].`ú„ð&’HÀcQpø-\ãóŸ÷ »¹c|œÛlÇÚŽºÍ Þ®Ó‹“ðö@l|z‡jw‡áTOáF½ëéÆÖJÏë†{u‡äªã鈽ŠÂ%ï¾’òçJ.äNKQb'¨x›Â½(x!ˆ ©ßwãÈò]ýÄ@ó55*<|ºW¹W7¾Ø4¾Eîb3wÉÆ=µ$Ýjö‚#o6±lF×kÛŽ|ÜÙ&µíX£›š¦ÅÀži‡0ºp= ܤcô{zoó³}ئÜi°ÎO‘}aDÜsÌ¥ ­_¯•kÑÔ‡š¬ PU]’›©é-4Ð;›Ý^žV_HÅhFNâ3Äç 8®qÀŸ)ëPøé–0š*oäÕ¾åžÙûkÙBÚ&‚ö‡˜|ëÕqØ¢Ú:X;ßw)S3‘jCÝ–¢“SÀXól›!"š˜Ù½¡‘"ƒ™™¨­i§61@B2ך5DêA~ÂìÃzÑÀDc,ÝœD-w4Å V²£^û¶ü/–,šKvþ¸}7ö ÖØ„Ë7œx¼Wr‘/p^>bx0LÚz1}…À°¨Špx¼§iÊò“ÿU/4¾ŸU/P5óp­å€lÿ/ÀȘs endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰œW]oÛF}÷¯˜G °h¿ÙœXi½… Ñö%ÙŠY³‘H…CZÖþú=w†Ÿ²¤DA‘ÚQÄÃsï=ç~Ü}d¿ýv÷ôþñÙì÷ßß=¼g7wï?Ù,Uø€þc*Ío8“øü|þ¬nÞ-nî ›q¶XÝØ–m;l‘⛋=}}¡·éçÿ裒qGÃàGl³ÐެØÖOlo&36]ü—°¸Áš˜ír ‡_]/&Pnù•Ý|ž<ÌßWÉN(¶­UÅ–‚)9 'Ûz“T"c{Y­Ùs)’ŠMÿ³ø—áæŒÐŠ<ƒ2Ù•"•J¹ÅXÈT±ªöI)Ø>Qš=Ë}ztfžq‹{qkˆJneþ<ËÄNä™È«_™ÌY’e²ì-“+ÅF‚R‘´6$[CòÈr¢&¨Iµ,Yʬ¬*˜x¥ŽSáèç+›§xk&_dV'Ö?Åû§\€ãY±ï#Øe¦‚Åël/:—±*z(§E"Lðv¬°ãÍŠ’íKYU"g«²Ø²­ØåÁÒLæ‹›ùi©×oõuA?#±AhùøWï‚XúX]w¤•^*TÕL®V2­7e¸*“\¥¥\  5Œ;(ÛD J”/¸@Š|ƒd%‡1 K:I5@ôúêÅFI-â¨ÐHBÉßt(Y)_Ä-Û¯eº&È„¥ƒ¢ô<[šTÝÆ!ÛÝF¼¢é:Ée:àfâE¦ ™gƒLh‹—Ùoy¾ "Þ&Pþ mC¹“‚æÇ²å.@šÜ™°VE¹…7ñC?òñáã,¾Ã+Kñ­–¥¦.ìR×dNƒ©lŒàh¶Ékö¼8·¢ŽÔÇY¤SB<87a¨ƒªÄý¥@Ùȯb£k½N^D׺j°!©ÏU—/(^¦‘Û'a¶¯Äê:ù¡,“þ6qÆ6iÔnŸ¶ˆGd‘€k‹œsž{òL†Fð‘g…€4ü= ÍS—KÉJP|’·E ¤øŒ~ûôvÄ›¾8­R…2)¯s”¾ØPΤR¤ØR¦˜y†FÕ¿uƒe—»ÞüÇkWl…Žß¶“É4¦W¬§ß-·¯Ž,°a.îþØžäíIÍíøžšßÏAêo±Bÿ¾8øÕÛœçä-ï’<Ž¦Ë†åýÇÇkX:W³t²˜ÿËÅ{à~iWO}ÌUUÖ)Uü{TÝ«©ò˜ìæ?á·Éß fÍ4“ê+˜þx_àÞÕLmŸ¬\è÷:Ã}¦7'â—±Oíµö©YמŠ\ê‘?´å)îßZo¹»1's…ü§ÌÕtE)žË¢Î³»wIúõ¹Øõ:BC£F2ñ‚é­ûÅLo;Ñf‚6§íMéGšþ•Þ‚ RKÊÔZSÑ„jˆÑÌD˜ÉU³)BÌ!‰ãKe²gKY™n­ã bÃM£ˆ­ÊbÛ—‚"lÓF)ÔɽÐsœkz÷\,#N|¹å¸g—œ~±ÍÌÇž|ý½Â -7j/v® œ{·wU8x«i™^ŠÇ¿ÐB;?:ƒÇ–ټ٭î±+P™v%Jü­–Šü(Ñ-+¡a¾ÈRf×Ï쪾Å£ƒû_ÇQÕ§3o‚5Žö Àé–Ýø^0Äî-ڊ߀ÛIV¤5íSÍ~ÒÍíÛ®ïc¡ZB·óñŽh Êuû¦gìc¨(EM/Õ§Ì­—œØrÜvÿÂxoÖ‰^s RŒªöÙr Ñ endstream endobj 5 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰¬WïoÛÈýî¿búé(À¢¹¤(Š×ÃvœÄÊ5qÒ¡âû@‘+‹1¨Ü¥÷¯ï›]’’lÉEÐ"@DKäìÌ›7o/né—_.>¿›_“G¿þzuýŽÎ.Þ-iÅðìy…´ w€ó:¸z“«}ÖI^È (_n·2),wqmšÜ·™vé¡Î9XÝTRÓª©Ûû›P²ÚÖ•’¶Ä-G’;˜p‡¨>&M^·È®É™·‰æñ) ™êºf£;Ù@ibŠŽ“é.C}n˜Ä÷¿«Ëm‹Gè&W/ëòøQG3ñÞIŽ{NY#ŸvsjÙ5¢à bIõàšÛß/ÏÞfaÚ‰•èÅê@Œ¼á๙ ß 0Ý#7§BúGCÚ’¢ñ«èa<ÃäySa¢Ï+úœ<“Š'’%H@ò{ ®ÝKzÊõ†nŽ [0é…¡ƒ9©Ý¶ë¤ °üc#+ÖEúÐ@x$%«ºÕ¦Gà|AM#sôÊÌæ ‡ß$<Á†°c}ŒRS“ÓÌ âaþ­ zµÆ,õªƒœ7ÒD݆ ã!ÈAj vI5=3 bV*mò•ìåé^V î+r™Š;ÄL7ŒÒÏç„dTšT•´ÝGà¨pVŽ9:Â$W³6( ®Cw¨ÞêÊ&_ç]™Ð‡¶€pA*A´ÌvBaîNʘsðœÙ§7¡?<=3kKqá4Ï@;ê8HzNyYÊ,‡öèvùc™˜‘x£ÁÂõý~÷\'Ø®¥RO2¯î|ßW¬ffWš—ɽT®Ý;Gf<øÙEèFž`,ÞÉñ …/^ÇôœÖùüQa~t[DÎ ˜S`©È}©öÛŃ^x6xèzÓ¾1¹R-$ 4¡[Áª_µ÷L}îvÓ‘>‹™åšwÐ7•—m‘@Éy“ñÕè7Ÿb”D¬£Wæñ¼>?,^yVeWIú`/iñJ,zºšæG~8¬k°÷¡oB à óæ\¶º. 2Ýb6° vå5íV› µïœËÛù‘%=ÀüÕ¹aü7yºa­ëÚµ’²ý¶ð®£qäðÆã)¼jLЫþ»È¯=f™|ƒ¶nÚ&ƒ>ГlØ JbuÚ–æ>ƒT}ÔùtÃs¨ÝQµ»¨ë_¦mÃh®¤~â"LíÇõzPúQä:yQ×>õÝ¿T4ïT³Wñó©© `P sf4 |­Iüæ¸NߨòÁŽ~~g}pÝd®„?¥‰ï¹qh )Ñ«y§dÃN„¿…Îýh,¬Ä^^/ôõ7ù¼ªy‘|®«\׸£ùó¥›ïÖ‡ï»"ަ=ã(a °CU&æ‡ÑÔ¹ùÎŒ,‡k yÆ×š‘xaºÇ{óÝÇ5m "aÃC…JÞiæé©£Ø\k„OÍ ’dpnÊ|K¹ýHhñÛ-ÝümÙŸûŠäãƒøÊ® ÂGäú§.ªñ쵋£¬¯ÅN{¦J~×d~ ¢æ-9XRK±ñ΋yEA¸k~;Á MÓ¢³èi›"'}PÄ_mMXé¸nlÄ]0ë¿|כųöýêÏ1‘êáGï°ø3|à‡Ó‰‰ÀÀèƒ)ÅØšmçE¸Â#ïN\f´•œ÷ù¢v²‹¼GŽÎ®ø!L³bW;ŸP£<¼ÚpyU¾m „J¬ÝóÑi!Måü=¿fBÇm³¯Ý‰{- üÎfÅb:ìa~ƒÜX8ŠkGçB€ÖvÀt޲øA0¼QÒ–Çó¨±Þ{‹zHå½Õ`_êÆ>Û¢8|Aå«/_–‹åï—¨ñ–Þÿs¾\Ð?æK–ä> ì3ëó‚Ì=Ë›÷¿Óòæòï PÿÛ-H½¨¡V£1Pã¯6ÜM»tä÷\ïõg/7+»Ç9vœÄIwh €¬Ñ$Äw#v`+‰Kú„ÜæüKÈ¿Xw¥ZC7YÙÞÍ Ž;¾Ö&ÅuÛ˜Î#ð/‹šš–%¨âéa¿Õ o¿ë3§ä÷Øçº=‡mÐÔæ½;g±eMHá^,Þ=íÂîØ" =Þ:+˜LV½ÖŽtBUŽ÷iï; 4¤ä¿”‚ƒ£ù—Û…‡¿‚ýÜ÷a¶èO:˜h÷òpaIóøgƒÜ3x-» î¶J”½…¹1¿øBÊxŒ}5Û33|¾8Ê{Ťq‹ü6tmÚÀŠ ìîœE]ʧ)µÁ5øAEfãcÛó0s_yæÀÆ¥6]/%ÃÆº¤ÜÑ l2‚“ôËèô–Á›E4™ ¾æ>\\±½êû—Å-1à˜Uv;ïl’ÅvjÛp¹ª~ÒCUöµÅ?œOk‘dOOvIÈø¤aNû€#»™kŒ} ¼Ðeóò¶qŸ½á^uò,?ž¸“(Ž}ë À\aMÂp•åHÀ¨e†úª:ß©{‹`V!;ÔÇ·ÒÔy³Iߦu’7xR­å‹%CØ 2¿‘²Ö€õ 2ùù$ºñ)t߬x¸Pxú&ºÂû_V\$\6 ÀÂeɛŨ<É(@¡S†ñΙACû÷›UaxÀÒÆ¶g382Ì=~M¹䱤AxKYÖͳ¹¹ßDvõkhlÚy^xÞI8QÊI¶î·ót­aìÆQ<묥ß×Ú*ø'SÒ‹\BöFÜø¢NÞð °"“I<8^±y Á˜²‘Q½+²fï{I¦š…åT™þÿ«µ“™‚I-7èË…|2‘9›mÆ#ƒU¯™åüþrNßÚrË 3×¥uçzñ*‚JcûÓéüƒx¹ ?¡_os}òߤdç…cCÙ1ÿÊË¥7aÂ%7ÔŠFäè±=´§r@ꥃX@¨b¢¿¾³ëšÔ6âœx½;ó 0³¯gÃâFàºÁ‰Žªsœ^7ŸþI|£0O/‘ uÁü ‘zÕ£ Y8­¬šØÛ,5+%ÉEÀi©v4ÕkõMR•cÊ î"ß“àðÖ Ì‚ÅÖWg{xsô¼%ºEyæ¦Ù>‰Ãß•uük†«Žp&*@HúÄhÛT Jx€6î.|g]CrêöÃwÁäu‚&ÄÏ;ADñÿˆí¬ß!ÆJìW ‘—ÀÂÉ×C.Am°¤›{£Q¥¡¥äŠOxøQl”¼30dŠÖ!Öavg‹8,WÝÀøvÚ|H,{iîÚšÊHï¶„ý Ü‚¶Ý¦y<Ýe=±,ŒÑ„™¿Aìž~Gƒ`Ø3¸¹™÷÷)óqNÿ¼¥÷¸Ò0ôÕ݈Ã}’MÉ9øÔ=[±Ý[ÝÅC¬Å¦F~‹íîå`oës$gfG5å–PbIìX' v\V"tT–Á¨ÐÏÐëžöE*ÐápÐDFÂkQžÉ¶JŠF¢’pÄK ‹cƒô…‰O4ŽúF”¨×o÷LÌ×p©ƒEµÛb3Õ+óV6œ'öº3`P ˜vg=ö±ml÷Qÿ d9ÏAôÿ(¢I¼ endstream endobj 7 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 8 0 obj<>stream H‰œWÛrÛF}×WLž ¦Hƒ ¸\®²%;«$ŠYEnmmIû0FV¸0@\æëÓ݃EN¶\2Aèéé>}Ιëûðáúþæî–YìãÇÏ·7ìêúfm±HÁ üÇT”_q–ÀýŸàþN]}Þ\]o6ãlótµ°LËâK¶‰àÙÍ_Ø(Æ-üüo•ðÅ´lŠ¥¯¸½d>\úKx“m²+cµþÊf›ÿ^}Ù\}¹Ç ú¬x›Õùª¶ƒ«^о CÓvÂЧèë_VcÑí‹Ñ-Œ«—pqO—|Óš <?߯~dÏ"ŸyFœJø¿œ¹›ýgóó¥e±eGÖò—¦í…a@ÛÛ‹;^)ËëvÄC>ºÌÒ5}¨š««V°¤bY­*Ø×«d[)s&X7ŠŒÅò5‰$[ÿ{ýÓ—ßÞÅ,É1+½˜Ý—ÏÆµlÇä8¢!¾2ª‚Uωb¯²TI‘³â ¾K–yR%{‚?üÞÇã¼è`ÀEqÁMî:>ÇÀF’«J¤©¨’:ÏÜ0»c³ÅÒÈ‹=¤‰+lªf¡XQWs&rúî²Jì%Û£$%{‘Ç-Æ«ŒY6ó&Ï=5yaE¶Ç`?ÐJØpJÚ$íëÁ d“·k\äï*ö’Ï–Fq`Ïø Ø­Œ7ü‡ ÓªxÍôñ¢æ´ØXCÚéë’?L]uÜKl[Ý­æT•êYTtQJU§•þUo¢›èE«Ë?¡7ÂaIø8 y`ú~z„Bö+²ÀÈzËñõ¸Óe|5˧Ѳiµ»o«µå0Ó4ÇÖóÇ ct|½Ðù ¦Ç7˜¨ÜÛ‰º¸Jàš¶ßLÀ¥|‡`gªJÒ`8*% /ÜÜÖ;wèq1gÛºÒS™(€úRcð))aúU²k§ g5Mž$Î ^‹œñ`Ë œYÂÜå»~ì&œ@_õº†Ì~˜¢Ép¼Ø¼Ù¾ÍB‹y^hºîdu¹5Q^M0=¿œw=séZ–îÞÊ’ËÿU,ǹ®ò «JÁAA{êrÛÀ\^šŽ‡š¡™PäöˆÕy\Du&sÀTd‡t[q[*´ºm=”m(]Ú0¤ˆ«ÛÕ"$BÃ+î±mQT 2Û«÷£¥¹,±Öy%`2-—˜ªôeMý;àYKà÷N[×2*HX ‚qòô¸ÎA}€«pÈä¸ÅN:8<ìÑïj@Ƀ)ò†$½Û$¾¾§i!€ 3±ƒ u@(rhIŠì½0ú„&¡€ÀLþ¢]bÀ¢P§6©ã‚°8'>,ü³>g¤ ‡¢Ô<• ¿œž@¬qpR$1îz’&&L³c0¼ï†1\tÀò§³ùÒÃ¥d„S¿›· 6«ai»ŸD’Ö@(ð U¬zF"`©Ú€å_ã øŒfŽuK'•Â'°k Úé:©^Ö‘/¢ߥP||:Ò‘‰´hDöŒ{ ”êlÆaÈb|›Œ@„•ƒÜ"ï‰ v&NJUE«å0Óû2É«>™Á/V;#´$‡=d­í`™TJ gÊa´õÍ·{V"fvàzæLV‘9Ê||Ô!NèŒë&PaȧçsÂ'ŽÛ]è†i…*?wìIÊG,)øšTÍINÑzT•ÌV «RŠlÞŒæ^“EÔ~e _¤ZI/ ‡¦ ÆoAʈ$t÷[~Ò±3G´èÖ8™Å}¡T²MÒ¤:✓!ÂvQê:û;™c"11 %“ÉŒ,ÚÂ#¾•¼D†@. æ´#ûùÆKõ|êzÜ 9zz×öLÏ'ŸjÛegÕ Bb€ü»Ü6!pøšžpNt7»aÖÝ"`gL'l“þD*—ä± Û—Å«–BhV3Jš—t±ebßt—½0²_Lh&ðÝNÂïëýª_@ÙI)ˆnpd(‰¶d¶pƒÔ6.Ù/-eÜ7\¹ÖH&è!´ì’ x“dèymŠò¨(‘>Ò#àïóo嘵>Í{ÝîÅO§‰aNt»”¦),ÚÉK±ü4¨ß¥¥K‡æ²™&Y’‹j¶ ÏYTJíÊàà–Jéí—:ã$>"vr‚ƒ¾ëu/ÀÂó ?ß¡ Ë^—gu^¬uíŽz!º#â¸Ü´ÍD_QÎÚ¹)ØW¤¤RîÊìÕõg½èËñýNØÍs3䨾ɧGÌþ¿=§[ulj5ÄÿWR=Ó$Œ¡–pwÉtg°EA¯Z V3M%ºMò¯0­%b¥ÞÐÖSÁ(zµsúzZìÙ(P²Äo*€ÌÈ:$jÈÕN_ ¯ñe–ß³A)¯A¦c&â8‘ÍEÊž¡ QÊ÷'\p>km¤“izª`lI†E £3¡TeÒrJ¿q‹œç¦ß8E¥¼zYÖ{°0ƧÕÝãlN¶ûÑ—±lBp¿mDZ=õîT#NâüѶí ÜH“?`8+ðgâ-½Ùö9o(´Æ@k²Ä±O¡¶ŸÀIÁÉ :¢$T‰(//¨T-ÇÏöèàöm/ì[Îrþo¼Ÿrûìg»."Þu(ä‚b^>< `b¶Å¼×öµ)o’íÓ7`¤ß’úÄ5§2åò„eKG.©ð †í¤Wá…S¸CÍ+‘äð [‚ËìÔtTã„j;Ó¹Ph8åù¶åU9“Ò¶*ˆKÖ  ¶¬Uò*›&L ƒ[b™ŠZ›ž›Õ?™Bið6• ™‰ý¸{?`ض–Þ_êr35ý~Öw÷ÿÐÍ{A¶ “=jcV§U²Oe7S½¸l§FÏAàð¾3 ø)KuÞ“ø¾‡è×m"­jÐp™5W¯nW‹ðš{HŠaS£´Pðó[šnö»Ól);pfÛT{ Å- 4o½ênpò¢ª‡-ÑûÁ)Û ´­«ÎœëÀÇ2¥P q“‡N¥k&N¦$¨ÁâÙ)o•o8­Šæå·<;ËH½T‘¢åÜ!¡—´ŽwR;Rr­6Ðþl’²ƒçˆÁ͉\—:ÝÌÀ—8'G@Lå¶á„²HSðþ¨| ãœ6ÝŠ»s*~c‘ 'ì~ …ommÁÙ “B¨šHç¬x6Ò§‚g^܉ÈeQ£½…ÞBvB¦Ow‹.äI-·B%0/¬5ªã ¯EßXd:ë¸ZŸ[®Æ§`Ÿú(o'b¡£PVžV·š:ñT§)™k,š,îÅ6aqstÿºs…á mÓ ø%/÷§´|ÖS endstream endobj 9 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 10 0 obj<>stream H‰¤W]sÛÆ}ׯØúÅàŒãDêIG–ìX‰k,63»+`I"± «þúž» € MPm:[ DßÏsÏ=÷Í={ûöÍÝõí sØ?¾»¹fo®–*¼ ?L¥å…Ër¼ÿ ï—êâÝüâÍ|î0—ÍŽí8.žR|u¾£ïÏsúùozU1×·“™6ež‡ÅNd'n‚óÍ…uóùaê†l2ÿýâýüâý…°ËíÂ:tKOXŽÇžŽ“hËc6½“6§”‹é\ðäÍ\ÊÈõŒO›#;š9N¨Í.SÁø’çå%«W¢lÇã¬å’åe-ª'^PNoÓÓ&#;„ ¼Í.¾Zl—×+VJÆ·[^‰²fÛJ.+¡”ÍØÇIlɉkíÄ“¨.ÙäŸóŸÛ8ƒÞf˜ÑikuêÚn$ ·>òR±ûfÁ ÆË ¡Õ‚oû Žäc!Ø‡Š—)Åh¬†½ÕXG:³ƒ6PK°ÏØ£%Cpµ¬rdÉñ ã÷7÷Ó„}³@­‹®a‘¸&0md×WK.¨b,•E!Ò:—%ÛOœ]çµ`™Pì!LC+(±¦íu1&@Çé#¬Yöú¶ÌUW¹ 4ïy•«oòsÉ|gzÕ,§'’õƒ¤‹Ô9.¡Ÿº½ÏL5i*D&22ý(eM 4®²½Q? nl‡Qßç'*4r¿“e޲‡gU‹ “¥©¢NSçµññ¬x•퀓K¶€ò¾È+U³:ßè”ëDSŽ"vØÍkk†¡ãv¡:s˜áOÏäÏÃÓwìþ½mœqxzÁ  ¶°6ã¨e7‰êí4h LY^ÿ­Û¥yd"·MÖ=µÎÆáÄzýó™ž;¹öJ'øîâò¶ý gw%›åе4FK¸Òw{ G¼ÐŠ{Çm-Њƨ@0?÷7ËH«‚ý"•’;}ã(hªRk²ì”<¨scñPn ½;1ý ÑGL8 ,vK'a)êošc¬ïãD¶7#Cíàä)-˜•=–‚J¿6¥Ä ÝFý`LÞ™ÓÞÞ9å­§ÛŽÌ „qåä„NBgP;˜Q[Ëh¶¯¥éD+Ì÷Fµ¾ÑêWKÿïîÂÎÊÑ}c(ȈiÕ'H–Ý8¾=p,t§Â>j ‚8ØH}Ÿ¸üÚ^Ô¡¤óùÁÑ%{lhž¾£ÞuK®¸Aè=ÄA–g%õ 'ŠWc¿Vk*„0Kþ‡ñ)ô_✃ñ€ëY ÛrnúN+õÿv FÌw=›€Ôjô+¬8€ñ0¦Uø{ƒK Â’é+üÌð—fõˇ‡wó¶—gÝslßý^ó’lPÕ±í6í.d¯ ƒHË@­aþ•æð9-ɦÌk5˜‚igò`G”Íæ‘Â4ëµ[<£p+­~ 7Åtf²|Mc§·I¯5Û¡wz©×z&mv}ó,hÃ5©¶‚(X®õâ¯&%›Cù7zdô Œ! vÿéjîÛÈu#/ ð!F<(¥0xé˜sO‹îÓÜwh?òìöÍ®{ÇæÍ°^GÏwšmµxi–K¡êñ9¶ˆwÙ–×`e#"ÍBkuêè‘뾨$Bfv ›ó%9§‡öÇæÆóc31ÿÍëJŸU¾\AÇiB'À`YäZjÊ%(b®ë†ß< r“iluËͬ,OüYÕÙn?ë±=œ‚ýV=ïjEÎ}fú|Y´ ÁúÐ@R[#_>¸á_qá =ݬàú;™ÙÎÌŸuÃÜIcdYj—¡µ™¸8DÉí1¼À6ʉPØ¢’fdéYغ8¼mñUÆŽÇxŒãqmçþïâ u<›(>> ï´®¹N¸qÇÆi“´Ç•Rt§nŒúéE?uæU@«%#8lyE¿¢â ¢µGÁ¨ä+½Cç_®p¡ý:™ze¦ï´fKW+í¬~,ÖFYáãr¥/·5Y¾<׈c£ˆÀ¦PŠäJÚPœnPV6E¦‰—ÎLº ‹´Š|MgM®Æ7£7ªOÏ4Ìb-Tƒó ;-Oi{º #;ˆ»ýø6•kUæ[j PÓÈÿJs°#>ÿ¨û0šé‹àTAòb¦ÿ¯p}_O€kR£e@`á5í¬Bp­Àn4îj2õ­¼aç.¯{ázË:¾I› 8@#åR/P¬Îf¨"/Óé5x2jÏ¥šµt¼hÊT£2Ï û ò<Õ Ô.´©*zßc?Ü×F-T ø;"þ¦ñ¢%…ùÒWz¾Õºai”,ñw;˜ãtäÞ¿# Ìupýß^ÞéøòÉ•@RFÝ:ŸÿG :¦¸4- ÒÉWHO-…´vWÁ×,GëÑ«ñÜ`õ—X éii¤ä•@Ú Æ†º^‰yÀ¶›1žr{µŒ-¤,,A™ßÈ0Ô30µÑ1B ÀÊH? endstream endobj 11 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 12 0 obj<>stream H‰œW]o£È}ϯ¨·ÅRLšæ{îjV™8s7«ÍN4¶®´šÙ‡6´mî`°ˆ×ûë·ªÁã8Q”˜`8uê£OUÝ<ÁÏ?ß<Þ=L€ÁÇŸ&wpus7exƒ~ ˆ²+ ¼ÿ_¼¿,®>Í®nf3ÌWcf2ƘEøìlK/Ì °}þC·þc2®±ê+‹{àã¥ï1†ë+ãIåK%‹âŒfÿ¿ºŸ]Ý?7kÏ­g›‘ÕSè^šÜC_£òÓ ä'díwCr뤕À7ý ñá›Q$ëæ#Ï1¨Å(4Šyž—¦JÖàûxÛ³ñ£¿f¿¢cŸ¤Ó‰oÍgï™H6 :lD‰D ÎG®QÆÜÑ1ÿúùÌ«ÅBª$[Â"I%$¬å:W»¡|¸ƒù°¬–€í çõMî÷#°Äçƒ>{ÃeÅ­8–écœÝ³µåŸD>‰g3]«ÖY¼àLvìCt8Ej€·˜¾ßðþfL¾LǘÌÿñÛQ` (¼Ü æëòã5<Àý³û¯0!øÙ=|7o&“›?ÿü>‚ñ =‹½–‘Æ.‡Úfœ›uZ9êÀÕe¥ Ìy 0Ó²÷yþT•噄|åJB±+J¹† —XPH¹–±fQc{-tX§$0Ç%'â+£Ìa›«ÿ!¨ˆ41ÏU‰Û¤\È4k!lB7cË´œ0 5Ò×§)·ð`…XJàW‘P*!E†óT®Äx‹êi^¡éñ=/@bôR,Ÿ“?Tò,,r¥ï~ýl¹×í6|Ü­ ÖX=‚ÛU­`+0DIY’ (b¶”¨—ùfXõúPîÎPµ»¶kºžN•ãÃOkX‰g2'‹EUi™H’EÔ&µ¬ðJÆÜHF¾Ïä¥vx‘§i¾¥—PE¢<ƧähltùjüDeÚ¨C®Oè±À31äç9éïh·ݵ“:¹ä®ï2Éòw¿ßj¶5@`àkæ­Ñ} ÆÑמ}í?|™b±ó1Ç=8ÎBÓ!{°‘˔Π±YÛ—2N ;ÏÄ ¹éó>Ò{˜øaS ]&î{˜t‘ÞÃÄ LÖÏŽ÷-ÌÛ Äq=Ó{‘ÿ¨F¼‹k¤‡÷ž8®ÉºÉ±òaï‰Jé=Ll»st4 ë=4Z˜ÿ0¼yƒ³“ÃyÿÌh$ “éôó+ælL¹ßC鑸`€uðÒ}‘Òߟ0tÃ{¥8°EÚN©Gã’A{~ç¨ GkÇdz7y- h¶óú[`ã>á½ÈÛ €e_€ÒÛ€›ÄñÁp´\\ƒ.ÂÛ•ÂÆUâèõÖæÖtІߨ ,ò*‹A@AÙ['$*›DFòEí¤Žûíä¸jêµLqji2hL§ðõ3¥Xaí)à“(°Þ›ôy¥"y¢qPp¾G«ɸF« Î,å)Ï¢´Š§_Š[Q€(KIµiÄš9šà1ë¾CG²&‡nþSŸµ¬ZÏ¥"/7)!(Â@P!’Æi_îrùfÄBäøø¾PÌ«%zú«È @§“E¢™âQÓÒX¼ù¼Œ´r‘V·NÝƒŽ¹:¢¬cs‚6µ£ó” ë——²„E¥C]£õ(¶e’âÁHJ…F­'OSni9QðݘK*wí> ÎVîez|”Ôº'õèi™lR¢ŒõQPàö`«) )C¯¡x}ÆL“‚²RDU~aÐîŸ&¼ƒÆ’Š&¯–+mKþ] T•:5±W#qaÒ¢‡ÑŽ“²¼º“èâÃl`ö)¹‘,>ìMžïîÅh#æXåî݃(•B¥;Øæ ›-öÅÙ?€’Ï x'”N‹Y'sÜ öè’Gú ´E$¢(W1LÑ Ò&ê›.#¬:ªŒ?Ëù5ɮ˰S0ºX’ /N¦Èݺ¯GR³Àêj²]ì >/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 14 0 obj<>stream H‰”WmoÛ8þž_Áû&‘"J–dã{H“öîæ4Îîí} %ÚâF" RŠ×ûëo†Ô‹íÚîY6‡óòÌ3ÏÜ<‘Ÿ~ºy¼{¸'!ùùçw÷wäêæî9$¹ø˜\^Q"àý¿áýÚ\½[\Ý,!¡d±ºòà È,r‚OÑ”,¶xja ñïßði¡ ¬5ø3I΂¹;V_yÏ¥ÒMµ#MÉ5g«†ëkRrRðZIÓhÖð¾cÍdñgw]Lñ>šé, )^P\}ñÈý§çIæù?òhBr%&$fdÙ®­ ²ÑüK´8ùïâãZ£1ºìÖ|Ðé|>G£^Íþu[ûFü g> É×(Š YiUÝJ)änB×lø ÚBSiÒ;æiÍsŒÉ‚¬¹ä}Dœ¬„6 ºíƒ¿cx”öé´‘vÎY‹Î+ñ†5y S"$©•æ§$qHvœióÞ³=£ÔÚ¤qO; y×66A.:&ƒ˜Fé]`={¿¸zÿˆàC{À"´×ØöKž¦Y8|@ÉÏ™ŒN›ÄÌFßšLâ ƒ9}ÀŠˆš­9Âäêk€”‚âÔ›Š7œh>f8r1wÉHƒiÒ%£;Z2¨{å’TÜ—Õf«È–óWs}²Z=’¬±ƒJáéWƒî4lÃ;ø|ÐLæüš*okå>Š΂(îJ:w¬+¥¥`×ÄL+Ö ß7‹ß™8pë‘Ãò²5¼i &è4‡–¼ñ—ÌJsUUl©´³Œ?(84Ó`ñá‚FA†Ée\LOšÜ«% y>sfíXNæiÐù#W™WxM$«yµ»¶ätŒþ`íL[¥«b+ ` Í äÅšéWÞXo¹#ï—¬ãg9,[/çÍz”“O¯u¤b@’+¾uŒ0%¦fU¤Þœr’&ÎIglßÅ/ÞZ«­õlâg›PÏ:X2C z NB?‘R ñ‰‰ÝfÔ6Ð8ZoZH&aº+–7& ß4ß8² ‹Æ:ÜVµ‚Ø0¨ëaÒ³á Í¥ÁlŽF´lw“ˆìÿ#†W•°Ÿ×Dåy« Þu©'¿—¢‚ Ù¸NV×/d[/!$ðI4¼†ã£[Ó1ž9þ<šÓtÂÞj3ÜeÑ0¸.>U¢Ô…ÔŸ=À“;²0¥IÁvDWík¢,DÍçý¡I¤}iÞ˜¼±M#ñÜ4˜,TÉÞÀÔf âÅY2Ë~€Ìì4˜ˆÛw]d‡Ùv8FÍ©KfI0‹°EhÕ®K[äkr[‘_”™ø‰gÔ–lÝl‰EWdÓêQ÷ÐŽ’lODåÐ ˜a ˜6”O÷O¨OîßßÙá…vQp˜à¢= Íœõ4áL"¶5V’­Z™ãPÁ‚Ø(ÙŠ¦„ow”Þ,^’Ô¢ù«‡S‡¶)ÄJÀQ€õxøQ•rß‹c@„{×ß_y¿‹Ê(édÎF«µfµE+pTÑ»2³±žœðý4N³b˜‘¯ÌmGѶQ{¡",¾K¥Ù‘V° aO½J/0K_£ZóÂ.Ìæü¤Q߯F÷½'Ç Ž¡‘ÄKð|1˜ÈðS-GÓyOMÉì¨ÊÎI`k¢SM‡IÑqÙŠª@ŸÎ“„’!ëu>Ù±‡ñ>wºØBFÁ`äE À®”¼:9_²^G;{‡:Ú³»o{ vHz‰›BŸéhœ¶-=‡Û¯lÜ[ˆiEc{ÿu¶æÐ9ñt¯ãœE\À³Sˆ˜®å.ÀÚ·fãè4 äÇ5Í ÕÜtQýjìUG ̳¨Ë18ÑÚ²2£²ú:¹ÞœêŒ(¦nt¤ €`S‚Üä³j+Ø…`YÞ…‡L¶LiÞ™žeáù²pœ‘8K‚iDÈg7Û a'Àïq¯þþ)%Ï ß˜½ä¦t üâ)L¯ø{º’žÞ’¾?:HÓ ›†njX]ù ß-Óy&ä\“°C®õzqYœ÷Ì ZYܼcù+¨)x쎞à‡~ÄÐ]p[wݶqI€ï„í+= E`C0—¬5혽Q^}aûk—oB+‰Âä•í\¤—±;1·ßx‘Ç0•¬xÃ}®ÀõóµÖç:(DÜ3€Úp\° ç«™Iâß'ŸŸÿðGI?Ó©ez %Ÿ~}yöºÞ‡®ጜtJ¹áÅ–ï"Á銓S ÷ÃhCk/OÏè’æPQ² Iž¡y„£Ž+źG*øP¬PÎ6l)*õêQpakáh¹haV,9ž^ã>1ºA­ƒHÜ T=Ψ™õ)ÁÞ§jú™çn:(D@~cÊù$òÆÛ½pósˆ‡Í-v"Ö™þ¦;`< ÀØŽ‘JYヌ: œQ}þ@°YEVìIø‡U?ýNô\BÇAwèÕšK 30T¨¼EÜ»OùÙyI#˜1}¿ÀH\b{@ËÛ©nʾ&0ØÇ¡®ÇK`‡ "Ýï ŸÊš•LP_WñŠ£JW&…Ã`+õyÒˆÂzN¡²'·dƒÛ«:éD*áóBÁY:”4€z|™dÞãÄž-ŒNüÙÊWîx©lè±yWØ”ŽÜ<[óB0Xb­K }l0ü<2hí‘4q·ÀÎB«­AI•\­´#ÿ$=ÿÌŽ$LÅ £;$[€(Ø]Y1¬AÛÐöäë|èÅvË_^Ý!K1ŸŸÂȉþ-€‚”íšÛ÷n ¬.jÆÞè£\®îÊEirÍ6¤älƒºYV tð=Ü%Ì ©Þ n`:”£½¾–ÈU½©xÑ3«ø%©ÙZòFäßQ^ÎÔ7Ë ¾æ[ôåºk"³A™æ¬Ü¢ño½4Áž¦—•Ëô¼Ú ÏÉ-Ãz‘†áÜš¾Í_¥ÚV¼Xs7¹Ï]•üŸš‹FQ0MÃï8ž^\GcõÐz XÏÅ¥ù›x*êúå¶W gUTÏ lŠ=kòÜõ‚Ø)ªãFó÷‡ÂîlÁkØ4 ŸnŽôxÑÅÍ.:Ö:¹ N[¢UÅû0pF‘sœÏb œØyß3Å@«iÁ€½n¥p² ‘ߢ`q}w±%¨PÂÖäaWÌ`âÇÎŽ»çŸ<ïljT—¸9B›±qä`׎ÍÞï†oÉ«ÍÿkkÂÒ @€&À ê endstream endobj 15 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 16 0 obj<>stream H‰”Vïo£Fýî¿b>‚0`Àøz:)±ÓkZYwRЩRÒkXlz°kíB|é_ßÀØøW{r$lÞÌ{óf&ã¯ðñãx9Z€Ÿ>=,æ0ÏŸH4> èDŒ\Èñùg|¾Ö£‡x4Žc\ˆ³‘åØŽãLðœ½â½kpºþƒw±×kàð2s`êDö ßó .G/F.ÒüÍ´#7=#­Y¡a·‘%<Áw!wPJ]ï ðIaþÿÞ… öQ݈¢ºSÛ Ç¥€)ÂÖºÁ|ãÅ; Ú(Y¯7xåð$*®¯lÀ#°4Í«\Š;8B÷zN>¡[=¼åÚ®?›Í(Šq ¬(䎧a®ŠmórüÊŒÿî ݃N“&cß Í×PɆ‰5™A*“ºä¢Òw eVí˜â—lÍñ)â1`›_ 6È.“Å÷B*n7¯<Æ£Ç%UøPuw_õAUBT, §vÐJQŽŒkhÞe´¡š ñv0±§h¢Î OÀJØ2eNŒ*Oê‚),>Z„¯*”¶’Ú"]?¹NÆ9:™‘'³›Œü!¤{`äkä…6zÞD«<íÿÐ(³&%;ØÛuÉ~ÀCM>2­È€Wã¾Ö•bEÎ^Í»ÆR¦ÒéÌ eZžÁ{§0²/àùÑV ßuÅKm_+¸Íì‚`®gO°éüôž^äžÐ»/à©5öó«1GVHH5"{DënùHŒlÞ{ÿ*ˆÜIk¹Ý:4ÂÃwzP$R ¶å *¶Å;Žšú‹²¡~[Ž_žañ8Ç_rm›ÖÔ¸ªhø³Š‘OÆ£ŠñrN%]°71Ÿy3çôŽçµ]2õ^ ‰¡i®Ž5œj4HÛDZÿ·ð‡yÖÎ}«ÃLž#¢¦8ÐyQЕîϽ¦îWÔô§E ¦Ô…Ñ䆨§ûª×Ôø _ëŒ(寊‰„ï…ÌhM‰u/Áqväw‰ÂU’àûe®5ý¾a»’²¢nÞBCz?Ä/¯«ÅîÌE#öŠ ó vyµ‘5®œ#œ“Ò¾½É9(ÎR®~éíݱ¹ƒ=°95VØͳ&½ {6ð.µNæz½¦¼°W(w3j•¼á;ïà»#Ë‹‚Ó¶ÄÐÀ…TÔh¥¬5ˆº\!\Šž˜%ÆÙæ<¡^½”£7m%ôÎ> endobj 18 0 obj<> endobj 19 0 obj<> endobj 20 0 obj<> endobj 21 0 obj<> endobj 22 0 obj<> endobj 23 0 obj<> endobj 24 0 obj<> endobj 25 0 obj<> endobj 26 0 obj<> endobj 27 0 obj<> endobj 28 0 obj<> endobj 29 0 obj<> endobj 30 0 obj<> endobj 31 0 obj<> endobj 32 0 obj<> endobj 33 0 obj<> endobj 34 0 obj<>stream xÚìX]oSGý++ñÜÞÙo E‚P”ˆQ’ª– X4ìÈq*ø÷={÷˜Ø‰rMD©Z ˜{÷Î9»sfwfwÆïX5>q)+Æ'Ø‚ñl¤Øh|1jc2ÁÕhM£>Ôh Þg4'¼{ãDÔ„`œ‚$Dã\Å%ã¼²qA² 69*5›(°ïj\òj¢3.c|N|E“‰à+ß“ñÖ3,ȱo³7 ‰ˆIA\6 !HSyuj’‡Å:k¨N£IÍ[gø<‚Màóïà Àgð¼gðÅú¾ºNüñ ëÆ”>SøŒ¸2ø2&Éà+Κ ¾‚Á i,â*Ζl ¤ˆ‰ƒŠ5˜"(‚+UJ…_€ÅbJ„¤~Ð hHR_DÞÄ‚0BUX!IËfHLVÈÈXf¬H,XK¬ÎÊ#§b!¸ê\sª3DՊª¢ºú€4ÔµÕ¼F-õ‰ªê æ‹.Ô‡€‡\Àì¥>€Ù×íU¢¯ÌæP™µæ³2+˜CeFÄ1Tf3œðæ„…‹‚9Õ1_ÌX‚Ôm‘0©û¦ø:‚„Z„R#I9W·€«#ȹV¡’ª•j&W…ru[`# –’| €ä±…úƒüà¡n•*¦s„~âÁ±½#óÉ<~Ü}¹˜v§ËÅÕÛåÙb:=™Ï—Ý‹z´¬9éö?N./_N.ê«ïÇ“ÅtÖûÕÓ¶9òjúyùbúÅ”îdþqÚƒRuÙÛÃ,/FÊà­!Z¥u´ž6ÐFÚD›iK³‘|‘|‘|‘|‘|‘|‘|‘|‘|‘|–Ö'|WΣœG9r¥¿rå<Êy”ó芟|Ž|Ž|¶á•ñ)ãÓܾkr´ž–þ)Ò&ÚLKžL¡¿Ð_è/ôg¼Êx•ñ*ãUÆ«ŒW=y—2.e\ʸÔG]ÕqŽx×Ö!̇0’,­Ð*­£õ´6Ò’:uê ™|ÔU2ù2ù2ù2ù2ù2ù2ù Ç q…8ê%ÔK¨—èê;y¹?„ûC¨£PG¡ŽB…: uê(ÔQV::ò9òyòyòyòyòyòyò1¯âÉçÉçÉÇs\]il¥‘Q JQ…*§ÆrY–˲\–å²,—ÅcQË~³YgÒ™s¦œg™o¦›Ùf²™k¦š™f¢™çÒXX…X„xFyD[EwǦiÚv§Ó·Ë¾ð½š/>M>âbлvÓÉ»óÙûGа]ÛÙkc¸8´]Þ=¿û‚3ÔTŒ5Öƒ³—G«ŸêE£kô}¤ùé›GØD®IÓf¿út9²f•BêYcÃjì ËqíÝ-(œ +r6­${ê×WËç3´Ž‹É¬û}önº¸~m«êŽ»ºþ§óÏݳó¿ºç‹É§i{B™Í—SøáŸßfï®_Nÿœ =?µ˜v‡³J¹1töú þîW{Xê?Ï9òœ#§WÓÅåÛÅùŲ-çôêÍÆërqþa:¿âë³Åübr±šáý°†ò¡}]KXw ë¹êÜzšíZ‚ðv35ÝñÞÞÏÞÇ£¯PH,$ ‰eÕ(IÌÆâØXËn¤ã‘R1¥bJÅntV*¦TL©˜R1¥bJÅ”Š)Sv*e§Rv*MÚ±ÐC·n*¬ºjáä£ÂÔÒÇ£_O'››°» ÛÛV¯G€ÿf?ýÝ~<úgºþxtg÷o3±ˆ°†°„´ 2îI\ýÚz¹‚¿‡ÏŒíŽßa»wû›ýdõ#ìtswý»ÊÈýaa ¦»ÁÜn0ß=µ³}3ÊV#@pÔç4T¨»Zî„Æ ÕoCy»:J«ÔU ¹eöxçìù&à>RÑ阷ÊJÈE±š²˜²–¶UŒ×3q´¢’Û¨¸[¿ýÉÝòéuw4ùÒ_›–“ÅòW¾Ù¥óWÛ_óøþKøùÜqüÒ-ÇÏ]¿Õ½õZÊÕüdëÐÝéì¶ŽÚ½Ýgçõ êbcx †Ì‡8§!Îyˆsâ,v7aDv¬Áº#ÎíˆÛ1ñv›Ï¢ Š~-ƒŽIÅÙÁ8­¸v©„s=.Æù—ãB‹ƒq±Ç…Á¸Ôãü`\îqn0®ô8žw;È[y·Í±µ7fo“l~Ý&l»Æ>a¿ly8Â~ŸÙüp„ñþŠ³ÑºxWþÿëþZ+ô}BÙºÆ GéÊ}å¶QµÕmüÙj}xÕ¥øµ2'èNŒ»ôµ:†Ý ú¢ýî}õŽnw‚¾ŒGÝ@ú @¼× lVöгƒq:ÄÙ qî7T(ß¡E2]✆8÷»"äï¤ßá;ΖöÛ"ìv¸þ` â, endstream endobj 35 0 obj<>stream xÚìW]k\G ý+‚>7wFÒ|A´éCLœ6$~3y0Ø„Bk—°…úß÷ymv·övg]h¡y¸]]s%ÍÙ¹rv“$Ù]jÅR$›b…=:Ö&Zk3† ±†˜’Ä÷%‹WÞ«øàjR”«K)\‹”εJÍ\›T¾£t©Á3¤%¬5I3®YùªJ#_5éäC|xÖɇ\ùj“A¾Úe¯ɉ‰6\‰‘-KÎNÊÊäl&Y =(Ìøv"­Ò÷5ÖNOg‘lƒÕàOOLžž™îºò5€v#w=ƒƒôð°¡JOcgéé¢Éè0<#‰²ãydQM¬i˜¨ ‡Á*p©³ŠQÕЃ½*æ±qZ˜øsMp'0WC`sEššÀÜÐ(¤£1Ì=‚ÁܽÒÀÕIœ”U _ØLÍ FK™†Â n²Á 3Úg™è¬e2£YÆ@ÀâÐc 0ƒÑl’‚Ù³ó] æâô€¹01hÄj‰nA LYZ«äsWä‹Ë:¶ZQ¶ Ön`á|îÐc|úèÙÙõƒDû\ ƒ›¸ewì6Æi@gîìãwáì²tæüòåòÅ-ßó7‘äçåõòæìÝé7š¾¿¹¼]Þ#Žþåã¶û³ º_½ºcÐ`ðg0X0Ø3<ô %ò3*`?Þ|ùõâ—MĽg'¸Í÷ƒƒZN/no~_-W_V'×—W×+ÈðEúúôðô­$¨˜‡ÛZÅûDÓ6–f³0 ˜ÎÂ,`yæK³0þ°ùq˜„Õ€õYX X›…õ€ÕYؘÙã4œg‚C©L«.„æÕJHÓrÍe&˜Ç9Ïy÷gW¬6[îX;Æ#þÍÕÅåÏן’·¾9›îØ8êmšŽƒåã`wòx8&¶8ÞQœÞUÛîlÀñ ðØ>W15 dIO±Ô½)”Gz@ÝÇ)HTÐ:¥÷1u¯Ë9Y7bÝäû~ÝWüi“ûô(=Iÿ<ñðÑOSî/ÒÂt×÷ßÒño…þù‰ã\ –³Ûß®–w¯?à:ùC´Yî´ùwç³mM!ûcËÖèqx¬MÄúDl™ˆq¢ô‰,»ãDñYXŒÅfa#`: Ë1u–¿têm÷`-ÛCqé\߃;çéÚŽu”½/ÞRÿ/ß[ž| —™i£lO¬dz£sħw:‡B|~«C"žgqñ4 ‰Ø˜ÆÅÌa}÷>stream xÚìXKk7ý+‚®›O3£'„@ÛMCB{Iº3YlB¡µ!¸Ðüûž£k™ûЕ-J….lé›9g43ÒHck0ç†àŒCt‰Cr¥bÈNÌ0'I1V'UœF@¬`ļDŒê̠挸c•ctA9&"Çì¢Ï‹‹™vªK’œbÉÄe“¸ìFu:MæŠð;¸’ø]U~'W3¿áŸ78à §g@‰ÀmÍÞ‰zøŽu„ iVCv6L ×œ=GLè@NNb“Àrl†î)åÄÀ ,gJ ,ç&åL ,—‚ÕK`¢RËŽÔ@0,W,¨T{:V@Ϭ1Ó¡k…-Ï_ŒDb&Q}­ ` L`BE…Q§˘µ!EZñÔÊ=Dìæ=7-a¬s›¼2íc~3&Llå1`´JLbjHgBá¯ù #&Œq›Ð]…e¡+H‰aa¥„&*%?”€©ž|ðT˜`aR€öÁ¶õb’È€: %°›„ç¬I`9EJð“]ÉXΕXn>+,ìžá [¥Ä`¹6‰ºài§=øLIÀù3à‚`c !ep–1  ¢‚6pÅ„àà]h B Ç¡`‚Íýzûu{ýõþ¯‡íãÃõ—‡·w7·wpÿ•óæYí;n6êôökÕÉÙÇíýÛ½òŠ• Q«Ôý9~…ý~2/‚2]RŒá•ß~»ý»/eÏ·…¿0—ÿnüöÃë ¥÷§í—û/^ÿ|±ZóõãýÍ×m÷Ùñbýð¸CÒn½F"?øaÂwzˆåEºÇvÑ1ØÁO`Œ ØØ|·3ßyñM|O¦Ú4äÜh2 Å­4šÐfä*h¬ásZžÑÄ7^ðÊ”'—¼:åiã¥s^õSž5^ðdÊ <ž÷öˆôÏ÷ë<ÐùÂéœez2%/c)+à 0üN=¯¢CÆ‘ø¤^ýÂ*+`]ó*øùöúæ÷»ÏðTGâÚÊ­Ð÷f×:¡Ó—i5ôZïÅÛ«±—W/—~üûqîdz·~jÆ/[ºìHk*'J›)ÃLgÊ4S晲̔u¢Ìþ’rø´ñI]xcÿ‡ÿÇá‡]kõOêõ±éíȸÎbºdà[ûþÜ­*‡77pþ¢³¥?yxä¨Ó"mÐàYšÑ¬Ñ}å-4ZÐÊŒÖ:±´:£µN,œ?æüÃeBkX°M&´«Òþ¬ë=oз6[¶a%Ïw{éhÈ X_ü†Êqǵ°FXÇpZçpY×pkžÖ3®+»úl%§mÔKÁý¤îÚNï×ýåÓo“~=ôzïÜ+²—X/•á}ÌÂ\Z°ýcf¢Ô™ÒfÊ0SÆ™2]R®<ÿ0"1" endstream endobj 37 0 obj<>stream xÚì”[K1…ÿʀϚL&WAEP¬lÁçÕÝ–BÝʲ‚þ{OÔÖV‹úÚ‡e6“ïÌ™Ý +¤I¬%%‹à‰-#â#qˆ‰L4$ “ÁDä!›y'd#Ê8KuÅ9r1sž|Ö»@>f.RÌ% œ×s=σG4”LÎ ¥/o‰µÀ;¼tèÑ!”òh‘=zăL| *”,eãsp0ÄMJb£‡„ÊXpâD‡‡êF ª·ùK¯†}ÕõmÝ´=þÃV£æu±Þω££þ~IZªó¦ª§ídÏhV·8'8nwj¸žž`)o|éxM'›uò[g »žwOÕlU²Èü¤eËîl‘‹+¢}¶ü‚Xä’ŠhÖe8—áe#æmgÌeCæ²)sÙ˜9”áeãå’ù.¯€ûnÚãï]ÍëF ºÑÃÚ q%œµõÊ*_dzé¤ýäÔð¹zlNšñ¼k>ö?ÖÇã¾é–ø·zç¼sþ—³[wvœß¹½ endstream endobj 38 0 obj<> endobj 39 0 obj<> endobj 40 0 obj<> endobj 41 0 obj<>stream Unearthing The PDP-15’s Advanced Monitor SystemBob Supnik endstream endobj 42 0 obj<> endobj xref 0 376 0000000000 65535 f 0000007417 00000 n 0000007661 00000 n 0000009873 00000 n 0000010117 00000 n 0000012509 00000 n 0000012742 00000 n 0000015889 00000 n 0000016133 00000 n 0000018694 00000 n 0000018939 00000 n 0000021737 00000 n 0000021972 00000 n 0000024231 00000 n 0000024466 00000 n 0000027125 00000 n 0000027371 00000 n 0000028431 00000 n 0000028617 00000 n 0000028856 00000 n 0000028910 00000 n 0000029110 00000 n 0000029226 00000 n 0000029344 00000 n 0000029454 00000 n 0000029594 00000 n 0000029785 00000 n 0000029972 00000 n 0000030121 00000 n 0000030232 00000 n 0000030359 00000 n 0000030498 00000 n 0000030615 00000 n 0000030726 00000 n 0000030861 00000 n 0000032742 00000 n 0000033822 00000 n 0000034998 00000 n 0000035506 00000 n 0000035541 00000 n 0000035565 00000 n 0000035669 00000 n 0000039337 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/pdp11interrupts.pdf0000644000175000017500000007566310015774532015176 0ustar vlmvlm%PDF-1.4 %âãÏÓ 225 0 obj <> endobj xref 225 24 0000000016 00000 n 0000001217 00000 n 0000000790 00000 n 0000001469 00000 n 0000001799 00000 n 0000002265 00000 n 0000002802 00000 n 0000002838 00000 n 0000003060 00000 n 0000003288 00000 n 0000003365 00000 n 0000003968 00000 n 0000004595 00000 n 0000005005 00000 n 0000005238 00000 n 0000005700 00000 n 0000006080 00000 n 0000006388 00000 n 0000006973 00000 n 0000007161 00000 n 0000007541 00000 n 0000008011 00000 n 0000010681 00000 n 0000001038 00000 n trailer <<5a8c55898f9230498baa7cb77c966ac5>]>> startxref 0 %%EOF 227 0 obj<>stream xÚb```b``9ÊÀÊÀÀÆÁÀË€¼ ,@QŽ ß®ºö00¬¬„H±9Ѝ¥qœòi³€BJ ¢QLC5 (f`PaàJ\'ÀÀ½àÓ$s•e<«D”fˆ6nZ²à D;ƒã -ÄÀÀÕ¤¥ÂëÀîe°bIŸ,Ÿ[¢ Àü% endstream endobj 248 0 obj<>/W[1 1 1]/Type/XRef/Index[32 193]>>stream xÚbb’d`b``Ń3Î úb€Ÿû endstream endobj 226 0 obj<>>>/LastModified(D:20040220202238)/MarkInfo<>>> endobj 228 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 229 0 obj<> endobj 230 0 obj<> endobj 231 0 obj[/ICCBased 246 0 R] endobj 232 0 obj<> endobj 233 0 obj<> endobj 234 0 obj<> endobj 235 0 obj<>stream H‰|“]o›0†ïùç¤âØ|†©ª´$ÝÔIÕ"ÅÚ.¢^8I¼a’¬ûõ³ ùèšTH`Œýòœ×ïLáþ~ð<~š…‡‡Ñd Î`<£P(=a.P…t=ÿUϯ”3â΀s øÒñ)¡”¦ðìééƒÙÇ0jžõo€%„V²eRš¯w:™úzë“l±ivÛV}‚y#òVÔRÁw Ÿ¯±Bðø/ç‘;φôLÏŽô»¤KŽpÁбÃ2$%QšeCƒ1wGõf»­¿=?qþ\øÞ Þ ÿÖK†ì¤[IF’46…–Ž;op/–ÐãæèåxpœZù÷°iB’!¥±õì–døV²?© n]¯•µ#£ IFM•Ö†Ù®ªòÆóc÷ÕKÝ®ò+ŠnÿåÑgÙ‡ÜñgœüŽÞ»Â†fIŸ¤ ª­hÚ5¶A…²@¨—PÖÅ®B¯T›ËR—§1èI7°ºÉâþ$Kz%àŸ¼Ún„¹.–w¶ÿ; ¤6dþIÊg„E¶öR;Û½Dcî^¨@­ëƒ¡+)–¢0ÁËÍWÙÂÞôÀBlD{5|,î¡ã¤ßÜ}!ϤB®ô»n*ÏO]} \Ó\àçZèŠ.TÙ9Òá±#{‰ïªº²fjsvt]‡°A˜7›Wë ü`>dŠ endstream endobj 236 0 obj<>stream H‰t“ÍrÚ0F÷<Å]Ú3XHþÁö “E ‹t†¦8«¶  P#[T’Ióö•d›BJWÙ>÷\ÝOF㪸Uœ•P½™£lž†ªž¼=sÍ_©àHU#XXýœ`÷b õ¤92¥A±_W¬÷4òÜøÂMÝÛ‘ç¦eiWˆØßÒø°ß'Áwa”Ü€îN'©Œ+ö.ÕU²kk ¼…ðGõe'Ä‘I†H>jÖìÌw jÅÏÖ,e«­kø˜Âfµ‰¹r›_Ü ÒËõ´OjM'¨‘¶½¦ÓY3C)Þš0*æ½<“ ”x(IQQކJu';Ù%¼ss„ƒbÔnQÅW{ª&Oë%LfX,fëåó æððð¸²{ÕdVU,x?¹Î}±•…­…‡U‰!K2„IYÚfÛMudð=Ž“mHÊÀ„qÐ0"A[SUÛýž›“` £yІñðŠár8ü;zù­ô¼™—Š{£Øë•V§p:Áÿ:.î#ï𠧈Ä~´ÿÒ.Àfh}¶)¹‡†ý @vAùÓ+Qž ƒÒšX$·_Ù<÷ãÖ øó¼jk‹Ó¶öÖÛåæ*VŸ¯Rä¹×¡ †Ó)P!ÆlŽuÜsÊÛá&õ,ßkÆ(Q?Æ4Ó†šÎ]À׆)7I›7ìåö+Üé8®£þ£Ö—¦cÊmBëØ zpwðÕÞÐEîðG€L³˜ endstream endobj 237 0 obj<> endobj 238 0 obj<> endobj 239 0 obj<>stream H‰Œ’MOÂ@†ïýï,;û½†ô€pð`b̽­ˆ¡­)5Æïn ›mfšgžéÛ1gœs…ð‚ô$´GøæHƒ#¼f£EýÖuW¶í×g‡².–›o›b«X®»©Éq>2þG 4Þ‘ÆÄHyï{àó¨éÞË/MU55Šív½ª«²î¶ø.ÛqHÓö´qo§ÿ˜¶—³ÌʽÜö«ªŠögoAz°]5û ÏŸon{Ø"d‹‡;d“GL§“‡»û9<ò|6µYÈ&!ám·C?JÄ›§Ës(å˜<ŽU6:‡$~ÈsØ„(¡©R3o¸”5~+R ™ºñíH‹’p€  H@ r#䜴àGã=#›RQ‚˜3\ø^b1™2‹VólôôŸÅ°Á®©R ©VCœÔ &ˆü¡ Î\t1G2gÑò4ú¨3JX}-P]ŤÓZ^ Ô—€N3«”×Í à–)gcFWí% ð,wô‡àW€ž9ë˜ endstream endobj 240 0 obj<>stream H‰Œ“=kÃ0†wýŠÝB.÷¥/‡’!´-Ûf(téÿ*¥´Mƒ-NÇó¾:½îà®|¸]q»ÃÜò V«åa»ï¬×›¾7Å-K`(GÇTŸúO,…&Œ)d(Ÿ®» ÌSÀ(¨™- €Ï HP^ˆ¢À‚‘Ê›ëú¶·8Õ™[K¹wÝË- B£Nì3ÙcÎyàc?Ô¢_#½ëv75yüØ(¬r)¬ìѪ°Ìœ¨ÈÄDU#Š&‰s:ô ƒÄs6L„Æ^g;ô@#©—§qö ÃP´^ŠØÐáÃUÚ2èY8/D³¢7j£Ój±òÑÒéÕj_ïîxÕ¡0×< Ú̪”¶ò?éºë¦™0ÙœÕØ¥ñýz¸(—®yß~„Q| 0QÙÜŽ endstream endobj 241 0 obj<>stream H‰Œ’Ë­Ä0E÷©" ¾ š)`ú_¼8ÒÓ88ŸÙDr@Ç—cÞ ‘+"­’¡+ÈŠ Ñÿ}^Ë{!q@ÉÌÿ–^]•€ãÛ`<x!àv±oáíÄ—„­Úð8ÊIFêÅ #ƒÐcòÁ,Ð.S0+Ð!Bj!¨ÍF{uäl=V‚œMÓÚî5”÷ãˆj^ÔÎ3E€kp†‰¤òB<%Ìâ·L&„ |»r"TW†ð£e1Ù-ß©é=?X×ÉòÉíɲd{J”þ‹e%¿·¬û"Çǹ@ ]ö ¤õO€€üÛ endstream endobj 242 0 obj<>stream H‰|“Moœ0†ïüŠ9ÂÖ6ßI©ÉîaS%ª"_ª¦ fã„&¨ýõ›ì.ÑŠÉÏ<óz¦´Vßàêju»]‹áúúf} Ö ·Vœ3 ÀK‹Ï2 øà+%à'Ä ã4Mï-þbm¸µ¹Ç“s\rŽ£Î%!$ÐPýE#àã'|y4!„iü›? (Ä»ÌÅc¬Y+ÑuC« oƒèŒY︡ rßVbïüäwUâC–š*‰ûX†ˆˆ)`”ê²D±Ћº—Êq[¾ (+ÙºeÕ´ÀŒéÓ£zJ5Õ°.õh`2),{ûÀµ"üú {èd/ëÝT©)ͯJG·¬{AxPmv2Ï*øò°Ö×ßѸ€­Ý8Ô¾i2€¼Y‡M-Ú²p’5à3Yô›òÅh¥’Y%ÿdJ654v?æ­e5ÉgÆ/‰†„N’6dùkÝŒ•nïÒøIÕC3¨v˜äöYÿŠnœ5ì|ò‘åzfh²ZNᣧ‹ñ§™ò¢™àvƒ÷¨g /^vrÙå×Ñ1«ÄØà¼ R'ú2à˜©±1Õw™8p‘=:8åœNèòØÁy'Z9ƒ»¯fº@O×Å?÷']ܳŽäóÒ˜<ëÕñÿ¿™>Y\ôyŒKì$ð#~¬ÙðW€û… endstream endobj 243 0 obj<> endobj 244 0 obj<>stream H‰ŒÓ_kƒ0ðw?Å=vHâ%^¢B)¬š-Mƒ²—ÁûùðóOfWgX"˜á~w¹¸¿#eÃHǵcìz…û™›˜C Iv‚í6;Ôm¹€Ýnßô›{›dÖJ`?!û§Ÿ*äˆ(JrH9دdã)vï©Æ\Ó¯„i7| ¢2UG¼Ñ®y…9¶O`ñthÖtÝåd¡3Ïs¶ÁDòèD(ç$‘è®z§íj¿“þW>stream H‰œ’AkÂ0†ïýßQÁƤMl "¨•áÀ1F.cìPÛ¸f«í–FÇöë—FsÔÁ¤‡@ÛzÐåÏÎŒ;³Åœþ- ‡ýÅt%0MbórÂ>çÈ9¸ï#Jq`ÙgqÞ)Îßá†<…6.‰ÐÀpC v-ùĵ¿Ñf¯…°ø;Ä …gNg Z%e-µ¬J¨VpßÃø&†ù VªZ[ÚŽá„YEƒ`Á +s\-4è\€,µPjóªA‰·¨5:«ëÿ­‹íI-Θ¡âýáLÈw`ÿÔùÊkY>Ù°ûˆ=¨” nzH ùi?¶¨{Ô’ ü=Ë2±•©èAZˆD‰ì¿ÐË*ˆ<„)޼K¦þЙ6YK!u× †²£7%˜É/‹*}iLºüÚÁ¿zdˆ ØQ¿Ex.kH“²¬4,EÛ%Ú5éîa.A„FQd™™¬›éld› ö¦‡¸?&vÚ(| 0V‹ôW endstream endobj 246 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 247 0 obj<> endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰œWkoÛÆý. ÿa?RˆHóýh‹^8–që&i]‡iQD÷¹’öš"U.iÕ…|gv—)‰Šs ¦déÌ™™3¯«{òý÷WnîæÄ$?üðv~C&W7M’rxÿž‹0xÿßðþšOÞÆ“«86‰EâÕÄ$q ŸŠ÷øÑ˜ËÄßã[±l¿"“fhD¦iÚ$ÞN42ÿ7¹'·Ðâ…Õ²XÑM¾é [ðd{ZÀû‘g€qK ÇÆIʪ´a5Ç]E9-jšV$ÏI½¡„×ÈAâ 8Ïð|$žM´¤È’*#Ÿ ¶l8ÙÀËeY>rˆÇŠ4o2 ‡ ’Ÿ×ñ O—€ºeXnEˆûYkӤʟ§º§‘ûù½nY$£O,¥$-‹º*óœV|úŸø'…lwÈ®`ê‘'™~Öf„7é†$\àÞßÖBÛ%;Z‘þ_LgdþÞ²¦º A?@S— C¶ ÓŠ%9ÉYHS=sƒ‡w q¡¥IUW,[S’1þ¸˜¤oÆïÌâáÛóø¶IÚÿŠfM A­KÂYÎÒ©îke1FÏÆÃ¡+Ø!÷_1s ’^UÍ®&y¹f)I7lœç7¦éUtHŸ‘¢=”¢¥‚¨“½åQ(u(žPжgDV/@‹Ÿµß¦^¨%SÝÒ ”5+ >jÒ9«~QcC±C-…v«ö±ZrÏ£aÜì“úñ ~L€¤µk©¦8~¯·J¶,&˪L2ZÐlFž’Š´¬ÐÀp\¯-&éô’®““›ìv ÿ6¯‡ 4õÍ~.5%’ê´‹ÊUõɶ»œn¡²…‰^):VËÇò‘è>—‘ò‰BÝ‘5¸P%9Àý¾a9p¨Éü"mC°BÙÑ,æ€(8Eþý³¡¼†Bè‘/¥7ã—£âx5—bM(ÖM¹"ó?fd¿y>}<”ShµÒ—ƒ€õÈôŒÜÝþKÆoUž¡èˆ@Y®a;Š_ž—{ü>fZø(…ߎ)Ï×±yª<'B1;ÁE1ûCH»?,ë¤B<Û7Bßt< Úûy£ãÏñü¢ë‹Å˜Åà¬Å¡¨yÛ5]9ÓînW—?/s€·^ä3Z³~Ù ¤ÕðC‘ åË‹òååè´Gî~Žo>ÝÇäáö×O·ã1ÓÑ+L‡¾aú­ƒÊ ˆœºQ¯Þ|ÁAË|…™À6<³“@ÏC@ÿ¯ÈÖ‹ðîjÔŠõ +tÍÐt+½ ŽáÛ¯Àw=ñû^´j@(Ùá;¯Àw,#€Éížhú"s÷ÈVh˜¡éÉyr÷ó]LÞ»ëwä2ç e>„7°ç^^ýü »_¯¡;'Ä #L¬¯Ê;X(8­gç!X6ÚCT-ÒnVÇ}±ß‡a\üRÀŽvfjq” ¥®à­¹ã•æÐA‘’-›<#˼L;ôv)jÂuÛRc$h{´ä ¦¼Æ9»*+:#˲޴kåèHÒ%Ø€"+X Kû[6}±óvî&écQî'5@§ a.".<Žn·Žó˜‚¯"Žnh†Îe‰…ÿïyá8.J8ê݇±Ó,jØ,xò„ª!뤦dÏ gBoÉ4F0jÛH%.3I*‰US¤ ù|ÛÔ¸lØ] µ ´0¦ZÜ%Î …;š69‚õó·Ã]B…}p]‹ÎNdR¯B£EYƒÀº”.Ÿá¶*×U²%ã’Ð%Ö€`Ò:xÓ+ƒŒIðÇ¥g03p¸Õ¿o8]•â&yVXß‘-”Fµ¬`_„äÌzƒŽbwý8'çOW¢Ø0 Ù„£ªu½(«©j²jáµØé;ªjÃa¬µLGä¯VZJ3‹YÕãØ ÒµÛv‚hƒ8®šº©ÄÙ¸j8jqO+DÛã…6Ý kú#Çí‰!„ÚÛªîáÇÀ”WáÖÔó4 oñ³Êüšº¶±î¾tèØÖ…²>¨Qhüß6>¨º¦äCÂ9žŽÒº*óœVp •¥lÌ);“ž¶_xF`¶{y׊&QÔЇyZ±%ö†%!3’d™/òêi¿; |ˆa$9Yض³¢ fÝ™jË$+Q |ª{Z9…°§|HÄ^fÔ”"-ôW’ü¬^ni½A”¦` Jäœ ÀÇAz 'ÕÓ¢éo ’$ß'ÏSÝ׸v2lHœ7XðºÈr4^>õ ßo0¥Âq[ÚUŽ'H†uó(¸bßÜ6yÍà"DGjŽ¥%‹²7‘[œA},WõŠZÍáœ=Š;OÒ?ˆŒ=£aGPÃB¦I ó—ñšØÖwpqb.¿#Kèéùª“hÃx¶þMuWû › Mb Q[øCÝë0½hž!ëùÝš#¥$ÔÝeû Þì\grº%nP¼ .^ù™ÆQ†y†/XEÇCÊr=¯M;W¼þ"[š¢¦@=Œo9¦ _ÊÌ“øfg}Éžì=x8”8*#ñðβ@?¿CË/1[8ïÁ$šL$Õæ}ÃÚpngPQJOۭٶΧ¡dg÷F7Fí=Rë„ö×4WË2 ªÙ¿Ü]WíåjmèãG>önµ“õ{+tC¶.°©Àö°+¹X™8ÃÔBÕUOï¢9_²B®Zu¹øFmA‡øÌ'šh#T& 0—9ÝBþnU >’¬bPÌ—öÖÀ1ËŠ.ÐV?6Þ¤®a½ûØzÉB»Ž¯S²Ê“5é‡í Zøæ endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰œ—moÛFÇßûS콓a“æ.ŸÛ"€c Wµˆë* z‡äX“kk/©’”úðÙå£M*Ì)mÉæfggöÿÛ«;òÓOWïnÖKb‘7oÞ.oÈÙÕÍ{‹Ä%|€ÿHgg”HøüŸðùcyö6:»Š"‹P=œ–iY…Ÿc‚?2›DÏøXTjá÷¿à]TÊ”| -â[ÂsŒD»³Å]‘?ÉD$¤Úò ¾çYUäi* òÌK"2~ŸŠä<ú_5ᨇá¨oŽ‹q’³yÈ "³JÅa_•„g‰ÉòŠìE¿ÝÉì‘p‚rV›7C!£V2¨I0 •`Â+Nª‚gåƒ(._å—Ò„âσ(+2šb¨r Lßwu–<ër$ò²üJ®£kòòG•n)*“ôôÜ6S_gÚèõsý¸ˆ »2¨žy!HRÈ'LñÜ_¨,·üI*'q*xqþŸè—ZÛkµëj:ŽÛfªܦ–?¨÷J¹Y5–³KÔ¦ÃÍ1Z½AUyU‰¬’y«Œ¶‡òRÅèíÛ½Àt’JµQ˜ÛTÒóurŸ…Ê'O"5J‘•²ÂÌ@çÜâœ.j¹:ñÞªývÕaSQЖónygØB熳 øŽº¦YEg«w8.ÝÑf„#¢{LeÞŸÏqìPÍ€*àˆ$•|ÑbÌy­Î<æÏñ”z”¹Û§b%‡2H˜Š$‘X}žB7ðRÕGfÝ.vMaçtWPßmÊ^ï’ž†wðüý¡ìOE‰á¾žî¢_ñ®5˜Û´Šã·Í'ÆXÒ¶]7&ºÝšn°|Ý ¶ÙŽ>l󳬶êÑüPíýìVd©¾ÄI¯Ÿ«{c³üw/Z,‹ø qßtœn ºo5Y€Ù–q/b~(E»Œæ„ài!xòÅé½f=4@µ‹õêRYMFuØÎä‘Wðþ…¬tañIÛÓgn [”ïdU©CUœ{p*JÜëëÛ¥.ÑzõÃdÛÞ£'ÛØõ)Džw²QÉþÆ EÝÀ¤Ukö^¾.ÔÏGÃøôi* ;pÄqL׳`æ1Êze´¯ãäéQýL ™Žã͈cSÕtôÕj޽¯êû ¸¾V›Í‡»ˆlV¿X½¦bû3bÄÙPÈ@ņvêÖxSÇ„’’+ÿ'¢3¢X¶é{–ïŒí×Õ~Õ~M ¿Ä -“YÍRÈðÕTrBžZ3ô}Ït Ž#ÔÇ‘½PówAÚÖ¸˜Ô§3ô=Û´˜å…ãùO)³ÊNh øz\ַ듾þ•œÎÙžv«¡¼í6Çí‰ §Î §já…†ôÕ âàjYžÃÛü™lÀ BoN¿ú\/éW€¸øJ^Œx¸mwì²/r@¾JŠŽñk qÊöÐîpsŠ|;æšdVq@Q´9I¯×HÊ«x 'î€íjÓjÎêδ•j.´[S0öüš';ï¬Ï3ƒ°yÙ6Ï 61r^†Z@‘Žœ´S¹¦gmJ¡ ä|u-òÇ‚ï±B?¢Å¼»OÇKî ä1zþg-Ør¨VTkÀ—Àz£PÐð¢d™g±vÒ—u­T›Ô…¿¬Áv°hÕ5uBR]¯H"}(³ê…r ½^ T§¦Å†­ zhË÷{‘þ?ê”™ø¢Šq-ñþBv²¬øçK‡•;V“«À4ëÚcš¤DzG(¬/!E!â~tЦ”(8Ì·%AD ®¦‹) Ẻn$NlCfHPˆ ½œ_B'T$è®5˜,ìS;VsU£ôÛF¥ÿÃäâ~?Û6.ŽÚôô±4nГäaÃŒ8eË\'^Ó§ë s¶)5Fm·uœwqÂ#fX4 “y”…ýXG2þ:Öv==t†a³À1=‡ÚÞ«¯)ŸOY!›aâ h¦Õa-Ó¡êÓéDŽ: ä®Í·‹Í0zæÂ…×믻”QÞ3¾µìÀn3Ô±û˜w1Žy¤)û$Œ±ñ À0¢ 4PÛÿ¿‘ß †Q¨gzu^ƒå·™Í¸0W¸ßÚAË|ó'œÍ@™Ôq¿‹ùØŒ³ƒú®ÂÙ~ó±`&óQö{xJ.—5Ï¡¾ã÷6öwÊ5oR¾t©M;sçr-LBP×3•4ÚÚ÷32 ø1˜å½Ào IågAÀIí8åe)cžÂG§~|èwË;’É£0J¡ñ ­¼¡„Q ¯qƒ¼¸]:À:Ï[ æÍ¥Sâ$KO[ñ—ø1Å¢Z|py–(¤Uέ Ƴ(ÇœÚ5Ûú›¥–‡Æá ÿ—X2Ð6š¯N®‹åê†$DÁHÙC C%Sù—¦Àæ G7¢æcØX×í®x h€dó³ŠùáønvK¹ù™ÒKróávý¯;Òpí˜Ö ÖZ>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰œVÛnã6}÷WÌ£D4©»ŠÅ‰mt½Å[G.°HZ@‘[­.®DÅIáï’uñF®· (´yÎpæpæL¿Â‡Ó/³å(|üx;ŸÁh:»§à¸ ~ ðÓƒ×ÆõM1ºuGS×¥ÀÀ})”PJ p}oªî^ìr `Tüýÿss`ªDÃ?‹ÚÄÁm*¸Éèaìg)ϳ8ó|/M3y™¦QºuÌóèU™(Ö˜1ØG|›•|ò»û¹&ÖØ‘YÓ3C`C0£1$Qù…oCx ·ÞK”å=CÄ ˆRæy¹ã0qÿ¬Ñ,G¢ ¥BRaºã80Î6‘Oä÷îhñEäªÍ;æ¯Ê«ò#qY%£L†©Ç®"ß0'¦iMÅDNÆ.F<_¬ïn†ØÔ>[] ÚÄßË·iP¢2Ç©°‡ µw!«Ì¨MõïÑ5“è¹~y±ÍöæÞã²^ð¦\¤W滬Û¼wòcx•ƒŠ¬W…TžÊ(æ°œ=ªªZ@ 4ò½Jà]ž=ÅaB SI³‘…îÈr6½Š¶Áæá.öü0ˆ±Wp¸¹›ÃÆã!FÜâžEbëÞÄV÷‘Q²Ã˜ðÀ²T*W~þÞ¡Uí¨8Ö p>£Lƒu=•lrØÞð·Ñî§¡rê?¢Ã1U©jžUˆÑ‡T» ©ŽÓµUBM)úÝs¥ˆçªó6Àk¾ËÛç2m¢ë²´\Ey|Dtã7E9ÌqáŠr¨>"³. 3tbÛT5$Ùjþ jØCÍÛü>È VXÞ¹‹ÕjýÕ…Õâ×õâÞ À¾ Õ<=mubñfÍÂùÃ:p©±èQËœk÷ÇEUdô2¦jSM=)ã´UÍàY»ŸR¢câœI¶Ç"Qÿ›D·M1ÞÄD&Â×.À·4¢Úu¿]Þ-]¸‚åÍ/ 4Ïp Ît…>‡a ‰ëÚÙ†ÀŒÿ93tÝu6*¯Óç,çeŠÝ6~»–}²j|[O´zS;<Žì]yvhPÔ)«»1¿†,ßÀÃÖþwbCÛ9Ç1ánËB|êwÛñIgW¸^?æ!Qqbxim&ØIhÒRÈyoëu`h9äóö^!އ [{’à¨8B°£M0ÌãxññëoÀ3dN²Ì¯'ž ºÚ à]ÕéÉ ¹)Úñ³úÄØtõÉ¢×PDI{Œo½8Øc¾žªü•›-ïˆ>|õ„Í“÷áMÞ¢T¸=2,çGL™F5bÒfÊwõó¯XØD² endstream endobj 7 0 obj<> endobj 8 0 obj<> endobj 9 0 obj<> endobj 10 0 obj<> endobj 11 0 obj<> endobj 12 0 obj<> endobj 13 0 obj<--- INTERRUPT REQUEST)>> endobj 14 0 obj<> endobj 15 0 obj<> endobj 16 0 obj<> endobj 17 0 obj<> endobj 18 0 obj<> endobj 19 0 obj<> endobj 20 0 obj<> endobj 21 0 obj<> endobj 22 0 obj<------| |)>> endobj 23 0 obj<> endobj 24 0 obj<> endobj 25 0 obj<>stream xÚÜXmO#G þ+#õs»ãyé„tǸD览é±¢¨\‚BR•ßgÖd#zĨU_>$^;žçñØïf½3ÖxoÈ:ãƒ)ÅM-ÐR3ã³!o³ñÅPJЫ¡ Ö8›³ dœ£b‚ƒ¬Þoœ°ÈJ&Dã0C‚ÌÕà'ü d‚_5.92x)E—©P¸œ‚‰À+:ð ¾"ðJ…¼ ’¼Z #4Ûô =a_„-%‚o–Ö'ì  ½GРô¾$“à­AH~À‹Ø^ÌÎdà%—L^‚“ÏM^–øb¡¯`3x¥B^E²rK]ŽXð ç‚‘ƒŽ”‚A‚kz€l:R盞 ›ÞRKБò€d`«!Zk*ð"’=¤J^jµ^Y^F>RÈX\W¼3xÁ"%¡‚© É!`U·­8H_ë‘H¶õ…ÇKùvƒ…s±Y.j³d”.4KÁEi–VD ¢”D@Ž®Y€ Ó`rJÍäLÍäŒl¹4Rt]l©¦Ö.‰myÄÞsk¨Ú–;TØ•ÐrÉ¢xäZOPó‰¸@F UM®‘ºŒ 4µºûaÇ£¨uF@,„ ¤€½¿yÓ]<ÜõÝdµ\Y],ûþ|±Xu§í4YsÞÞÎîï?ÎîÚ±júÙlÙÏ¿vÂÆ–Oýï«ÓþÁ„î|qÛ‹Rs98Ëé´E m”eYDV–’%‰t"½È Rð²øGñ‹âÅ/Š_Þ(¼Qx£ð&ÁI›/ ^¼$xIðˆñ*»UöªìTÙ§Š 3V!´Bh…Ð ¡B+„V­ZÙ€• XÁ#Á#Á#Á#Á#Æ“ýÉödw²9Ù›l-1QbžÄ4’îÌ(™Q2£dFÉŒ’%3Jf”Ì(…Q £F)ŒR¥0Ja”Â(…Q £TF©ŒX8Çëƒ bdÐÀ Aƒ MºJšJzJZJ:JJúIÚ‰»é²;3ܿݤ›ô_VÃaø´X~ÝGƒkwÜÏ®næ×ß9Ûî>vÇ6Ü™vl¸‹8F}·¸z€ág6ÿxÞ>­¿ÞOm»ï ¥o7¾!7íÎ×.|»õµïÏëÕíÍcàn6ï~š_õË'•ƒíκÆñnñ{÷þæ·îh9ûÚóæÅ|±êᇯçWOÊä—FËÑÍõzÙw'ó92]|~‹Ïa“'í¢}‰åH,“õ]¿¼ÿ²¼¹[q8“õÏ#uµ¼ùµ_¬E}¿\ÜÎî6 [™íŽÝvR»cÚÎgw¶SÙü}Ë Þ¦8R’ÒP<ég’F&éd’V&ée’f&éf’v&égŠ›Iÿì tâç6sjøýrZÝq€^Nÿçƒôr* ø¯¤Ã3„§ÍËÇÃs|NÞÛ]›Èsï²;|W›'˜É£åÚ<ÇSCðÝž[ FæGða•{aÛ×+|ƒÂ7*|“Â72žÏHØ]U õU dDgM9ISOÒ”4¥ôªÆ¥¬áЕêþÎSçŸ?À~¿L;xßÃî¾^á¾Qá›ö>(~çïËP^ÅP5Öjœ5Õ$M9ISOÒ”4¥¤qÖTòÅÃê5‡utj4tôªŽršZ:ÿ:MU¦ªNSU§©ªÓTÕiªê5UùÊû7'òèOÔúÈ<^åö¾Ÿ‘bJ“bJ“bJ“âኳ™^5›I1›iw6ë)^œÒ¤™Ò¤™Ò¤™Ò¤™Ò¤™Ò¤™Ò¤™Ò´;¥õgâÅyM»ózogE-§øƒÈÿè/»·Sâ—q—ã9’àC› m½ãw*}&ï–7/W›ëÉæ=2É¿vÚükOoÚ6}xìA÷§@›÷Ïÿü<=|ýV🻳‡áuÔj¶\̯úù ÅÿÁ¯ÏDÿ¾6Dn òÏ•¿H×xB¸ç[Ð=S»:eìíTÞ~ïc1^T,Qåô1ý!À¸tÙ endstream endobj 26 0 obj<>stream xÚìWMÛ6ý+ri/9~Áë4@‚¦Mèa±g­l8V¡¸höß÷D*++U=ôÐú ˆ"gæÍãŒicIie¬Uìñb•/‡'âÁT”qP„ÌQ4x'e))ÃZÙð6Š 8&Å8¶ÊpÌÊyÁ9åà¼ò^pA#¸¨‚\RQç´Š8gTÒÀ9RÉ'ÖÓ1,¬ê„/‡Å¿‘}&"h‡‡XvÃ" /áÃø.vìð$ÁÁª—(Ö½ØO#ñ¹ˆ//8‹^f¬zl²]¶0@úIê eÄ`RÜq‚ &%£Š­‘Ôâ¨Á½’„2ã ’a $ýÆ)Ä$ë :‚ ì“èË¢ X–´ÁrB ŒrÚ‹D¤\—6Â)¶<Ø:F¨ÏŸW?ªP½¨~nÚÏ»cõ)Añ¼¯6ÃÌ=ÐQæ®®:t\„N‹ÐF/ƒ›Çp9¨3xãgíÛ1žçð<Æ»9¼ãýÞñaÆø8‡c|šÃÔ•ŸÆ“ãÍ~¬oœÓ—ÆúÆ9}i¬oœÓ—ÆúÆ9}i¬oœÓ—Æúú9}iYåÒ²Ò¥eµk—Õ®5Ëà"í«z·?œîŸ¡å>Þ3šþËF»ÌÿS?n™¿ ¾Li»Li»Li^¦4/SZ®J‚¸¾‘9Œºß8¼o»š(=lûC¿Ém¿´óÒ¦Kû-mµ´ËÒK{+m«´£ÒfJû(m¡”{)ãRž}T¥Sn¿µê>ÐwÝ*/®›ýðž2K¹he;Cn¶»ǺC®×Í×›”Vˆ6É]&ú•ÃÕ10»®Z×\VÆàGo«·Õ›ÝCóû¹Úœwíùõi_ŸÎªì¯^žöy*øUðÉÈQ>îîêϘ«ÖÇæîÓðÛç\–Á?-Cw£.x›ñ<‰ÞdOÁVÓÞèâèŸÛ¼°ýõp÷éTù‚Iwqqqqqqqÿï¹Ïæigügû©Ù×Õ›vûat-†‰G7bùÚÖ_Ï}0®BÄwuW·Þ}_<×íÿ¶û?èÙÐØµý?þ{žŸþO{uõ§ýˆ¬; endstream endobj 27 0 obj<> endobj 28 0 obj<> endobj 29 0 obj<> endobj 30 0 obj<>stream 3 PDP-11 Interrupts: Variations On A ThemeBob Supnik endstream endobj 31 0 obj<> endobj xref 0 225 0000000000 65535 f 0000010921 00000 n 0000011178 00000 n 0000013318 00000 n 0000013563 00000 n 0000015572 00000 n 0000015829 00000 n 0000017116 00000 n 0000017167 00000 n 0000017330 00000 n 0000017443 00000 n 0000017587 00000 n 0000017803 00000 n 0000017964 00000 n 0000018146 00000 n 0000018293 00000 n 0000018489 00000 n 0000018676 00000 n 0000018896 00000 n 0000019005 00000 n 0000019148 00000 n 0000019267 00000 n 0000019403 00000 n 0000019554 00000 n 0000019669 00000 n 0000019810 00000 n 0000021422 00000 n 0000022647 00000 n 0000022682 00000 n 0000022706 00000 n 0000022777 00000 n 0000026807 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/simh.pdf0000644000175000017500000056046611143601727013047 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÍ[Ûr·}çWlù%³)íx€¹`7‰d$Ù²¤P”—œФ(9)K¢ûëÌ}0sf±«ÈåØ.ˆîÓ 1?¯Š\éUáÿçï¾>1««]÷êäþÐøpuðóA›—þŸ®ÛçïV÷NÝD»Run›Õ髃"·¶-l÷³rÝ:oª•)ê\W«Ów/²¿¯7u®ÜìÃz£s£ë¦ÌÞ¬7…kš²m²Ok•×U¡mÔ{½.òV)Û¶Ù•4W0âl½QyU׺Ÿ­7¥ë. íéµ¹nŠÚdšBéì¶£§ma²·ýUÙ‘\eLÏQiK“ÝÈPà~X®-‹&{å·gmÙª’Á¯e𥬌ƒÇØ*{Ýß¹ÑîÕ´Ù?QµnƉMÕ?Ѱ­QÙ¯kUæRMöÑïÔõÙ¶w)½€tUíˆýóô›¥óÖÑ[m”ƒ€Óõ…SæI¿b]V×M^k­²_úUÜVPƒE?—Ò¼ˆ”9ªGË€ÐÔÙ&0 ²žß¯7[M)©E˜m¶\Ú{éqY5¥“NYOU3bì{™W 9hÙî²ìUã-ÐFhô`­ë·¶^5¦Ê‹Îݜӟ”£°:}äÿú“ÿ£Öþ¯Ó?¿Èýuk\žx6U]ÔÙÓu™­ªì‡õÆæE¡AôCÛªvû.ò²nš¢ÌõÛœvøp8õx cË€'2 ÆÝC{üz6!ª-7I«²Fß@Ñþš#ìíÇlâ¹~\KŒÛI#SšÂ§®Ú”£<× Â÷¹ƒ&ƒÊbM†.Y³±™‹ZVb—UødBR"ÎÕèGK35ûîo …à¦0ñ§a:ü£!0nenPÅuEçó & Ë^¤Ô IUÄb¿‚|€ÂÄcq rF7tEW»Cé.å%<÷>È­cB*Ž‹…‹Xg•OÝËj!áИšIa›ºBý‹C±ÜYøy¢zF{Ï‘Ù@á=wÞÚ´ƒìÅ„a=©eÐÆëé˜m|œ$Ö¯h’¤ã ˆËäÆ7ŒëŸdaÝa<—‹¿_ŠÇz–’± ØÞŽÓ/Q8½¹ä®4£-D/Ž…žX£ê(Ÿûʯ¥ªþ2hŒâK¸ IM_¹mYmÅSî#—’ÈÔa‰YÎÚW‰d#%ˆ½õBE{Û&Bz¦êBÝöò™%Á±`šXtÄbEt(ãÇÞÐäëvø‰Ž’ÅÏǸo÷9Úp­3Ï –)*… ÁA.í¤ lÚ\Ñ€¾HŠ ú›¾êΦmö!ʳʥ$ŠƒüîP §+¡/æó”ޝ@-l³t?ôåš;–ãr>Ò(;»ð‹É²ç3ת> úËûf4Æ$/gHk’št¡ñkŠëô$ázª1Å"ž2H‹&>ynƒ{'…}þ9 ‚½ áðý;ŵýîë˜OúžÅCÄÖÓ'’`¡œVò@²Ÿ%ÁЇw-;Ä&¦ÿ=áëLÐ_CªZL…‰h\°ê-•2=•q?˜< $ñsAÎ-ðˆ6ˆ#Z—bE¾04šäÄñ¹Cy€å‰³Â$Õ}e'p÷¥J;Ëþx¢ì¨CóLfqýmzSc5,ÜPÍÊ][,åŒê ÿe¹*ôS‚Ü})CüL/<»q˜'ÌJºSš1-h¦`ÛA<}:Ñæ¥]Ù9ï°™i™‚DC>Àk^œ2=²F‡¢è-UÓ®õ» .H“p#’ÏHÚÐ l{ºNÒíéy“g5Ëi×B¶<=»&/h…£Ù–'…¯÷AÑ=;¡ù·/Îmí»sÞæËDɼ-§žT O%&õÁü9B•MS® –õ·¼MU5­#eœ‹lLëŸDUuÞ6E•Ý “Nøk‡@þ![ ~Ê('ß{é‘#‘ÓqHš ,Wî»îz"’cEìÁåÑçD)„ÒN­B< sa{F`ÏCʽ.¢¥?ð¶Ö1ªj†*,BónØÃcé¢?„ùÓkòZîù¹†Adý®ÄU×ÑàmÂèn¾ ª}ÁË™#iò“‡àóòx'HÇ’õ#Ãï‚*Š/Ø2 ^_lœNY\õ‘ø J±Çº1PÚ¡4‰e?—Þ#©1ÎÅéÀû%XížGñs Žù{¦ Ngc­Ó>p·ðPºJ¨ƒbëÃ\E.ë`ÒÜÓvû:s¨Ö@Tέ«ˆ Mñj€ZþÈëxgÔE†Æ vÌ< ”ÛáPÊüå÷{ìu‡2uʰNà1ê'ò¹Ôû='1Õ:"¦ÅPÅkßÁ ž†Nùù„Š&z çÁ⢧ûÇçhŽAJ”¨Àm h¿ñ<‚Øè¸ž²hûL¼ Ñ.GJGè͘»… —»fPÄÉ~GlpÄsi “ƒ4§Ðâ)Ãc{Â{MIÇB—>Ï¢šÛÜÈQ9F)¸8ChÆ/C€µÑ÷ÄÕyݽ9ï Òëâ;A^O ¡÷ŒAï©@‹ƒà[\ß½ £˜'ÝP˜C!h/ú€í±zžž´þ¡/ñ,‡ {^eü}ÅBa¿Ø’÷Eß#à*îöæ¾np“iðã"õ‰`™—MpwÝ6¾hŒÄ°_h!?TÇ!§+'ÔÓqG6Ë4šG^5À¢-s_º¬1ð©Ïâ’&ü‡t(=¥ð„ꄟB` µ›^7íBÞ ”ZåmÝÄç%âd{Ǩœ=™v'ÅÕ8ª"ôè¶ŸGx‡BL{ë·º6Kï±¢£_âË‚$pæNq½ùæŒ/ùbb97x̬ï3l£74]啵õ®gæ~Òöß—/0:4í{1u Ç Îz»üÓÇ“0²L´ C³¿/SòP8b2½K£vÒvžÑ°¾äž·ˆmQÃô…Ï’²C3u­"Râ÷i^NÂÎü}æô3 ¸‰ä%¡é›º-Å(ºðkS^щJlËõ¾É·É¢ÔòsÅ¥ûûÉ‚Ë/’Î’µ1`äà îs«=Õ‡2ÓXÐäqÃnÏ»MaJ59h|æóî¥wéâyKqeF•®,@Äõ‡ÉãÅrð´2ûv„UC€Â´äÇu24µ ^]ªƒ° m²2_GÀ?-°q'Ÿf¾§ìò÷ œóäÂÉ¢ì"*çô¦äÜr­èK!f–P`š gúˆ6Y²ZRUâ™Ê~/_gy ž×e“od{éA"¼ï‹sˆØÜ;Yˆ"ü…'óA1´ç_%-uá°ùÔ¥OÀµzþ0/Ú-ã0pð›;„¹„£sâ,ÀB;«gF/Hú7²e®m0¶¤CˆÂá(«ÙGNÙý‘9Ù!i‹>άl^6jöqæñéÁßÜ¿ÿø`}Jendstream endobj 6 0 obj 3080 endobj 15 0 obj <> stream xœí]K“Û6¾ûWè(„ÅûqLâdË[›Jv<É%µµåÌ8Žwý˜dl'ίß@S‚† ÄÑPÄUq›"D~_£ÑèþmE ã+êÿtÂÕÛ'»0«W·OêË«‹¿·ÂﯞüöÄáÿ«/`ùêíêËKhÈØÊ§W—¿<¡Ä9KM£•­,\wN­ £ÄÀ oŸ¬WÕå}»‚¾C ·Õ@;F¤ô ~Z³j£5ÌÉ5©¸±ÿ¾üGÓi£ZG54¿¼ÞUÏ)4µ¢ùô§õwÕFéÄúSTú2Š¿WFŒÔ >—ÄI¸¸~]m8QB ïühënx¢Vö„¢²êäûç‹9G”eÝcó ß}ù3½8±~t X°[ŽdòQs¢¥[Ëj û#”9¾û6Rp"¤^m˜#¶ŸLŸV”hÎ7ëQí‡ :ÕŽ |qqä²Úïh£”^ŽwÜ ËgoATþúÂpv>Ïì Þ~ÿqÊP·_‹9|÷%ÍôâÄIuŒrC´âvŒŠ aôÇj# ·Ìêõ·˜½Œ ž–`³FëôK„²ïÐed¶þå/;¥%ÛÔèÞ÷H¨ƒ¡rªÌãÆù B ¢LÃó!=ˆì|ƒ Ú£ÚF& ³†õqÌ­˜2.Þ­iWï°hrʯޅh–î,De²õë vœ¢ÔÀ‚Ðe«§¾€1Ex§f¸_ÆkÿCèö«J.uëïã´U®×Vq$­š0já~÷(K•t]aÂ8­0~G¹b†ŒÊa½ÿ:¶o‘ÕztF÷~¨4º¥½{ß­€«Nkz“RÄ©E¹÷óÆÒ5x…?›Ÿ›¸¢'ÖHª‘\m=9܈cDG4•f­·¸‹Š3¢v€q¿ÿ•1êSC3.#$Æ¥ÿ6”¥5€¬¾(8ݺ:ˆÓ`s¬?cHŠöÆs F­†Î5°ô’UrýØI‚™*ÌúeÚ*íK@sª”5ì¶ŸßVÿÆ)b•cø«qbQÐÍoýÆiéY6¾q³™†%ÍôâÄúÑ‘`3 žõè<ÔLƒE¯'7\ƒÝÌ\.Œí–I¨#pm,“ðl& ú3™çyà„±gD7À4 ®[™äï}ÑRj»þï}Å+Ï*궆I¿(ýGN‚Eî/¬3w,l˜ÄÀó"'“Ö9“œt¦Œ>Æ`uCÊY2ž=D.{õyäFó-ôÜÁ•é­8Ð:½÷øÊíúsÅL™P˜Rj¯’–Ê8«"”2îÖ¯¢‚‘‰Ѽ¢‹Fñõ_•wÒS¥ðòf!U­èfòÖ,N¬aÀJ"˜* R8h& 2þËþ›,üƒ¶-ü[ÀE;ÿe6þý™€Æó,ö‡09½’@¨ŽD´|¸I¢ö8ŠÀk‹â‡)"ô€ní¯S˜õ÷Ž8â=6Ö»)gíSx,âÍï}."îÔ!Ä0†[f0>Èo43¨lfú3™1Ãß’Žpý©ßVçÖJ…lôz_Q2ÿ`×~¦_+´èÚ#xúô±3y¨—)f¢JC”HëÊz`ñø}Þd08ÓÓ³…Íc‹1Ðܲ…íq¢Ql¡³Ù"èÏd 4žô:â¶ÙD¥— W[wíÛjHlc|6?þlvÁûJQ'Åtâ·ÜhHcÿ[þX!°P̼b*GRÁîÇš*©ˆ¼+Ú4c﹎6 вãMQ×1ÎÔ#oŠBJQèMˆý5HéF/*€®Y¿‰ÍQôêÓÒŠÚ PŸWñstëªRÄH*DFl§ô]¸¶k˜–ãÂ`• ‚É¥ÇÖá¦~ŸÈ (šK¹QQLjm0¨ËÍmÜ6Ÿ…ð ÷›Ïg‡ƒFý™æ3r  ö“·a£µzµeâ";¸NÚݶq±W%´'Rœ£¡p>âñÑ;Åײd“÷MéÃfú¯ òmÐADî8b "·$Á¹Ï3–$²#=£þL’@ãù*’Ä›”‹¤æ‡ÿyP÷Q:ójŠöM£–È?Wx¿· ø¹ÙjaAc™É‹°Lqç,TQhe†TqØLODÃÆ{Gcp¹¥ øë.o|š)ò£:;õ™DGƒ|ñ×ìXï] ø#“楷Dh½H¾˜ÏL/Nœ _Ü¿8 ¯:ú Iæþ]Ñ¥iÊŽ.úóˆçirÙVè*Z­<‹{µýÐ#%‰¬¼ÖKƒÍ&¿€&Z/Oz¦'fQÑ9Ìô©™&ëÉÔo0¯õ¬Ê:vç!Sh>ãâ­1;ÜG”Ãí*¥ß›õ³n2«ˆd%;æ ò‘5™ßØ8¦d~+™ßÆ$Kg~kÓ½µ‰Õ3µ¬|êõíp†(˜½± आZg½¬ŽÉ5e‡åàÎo«‚bPÞzü¤ŒƒýŸxÃu*gT8M¸áyŠ10(°æcè iúP'¢ PðcÈ5‡½ô˜,‰’é°$±WøpVˆ.S¢‘v¢{D_“ ÎoF9xÇY|݇Ýyºƒ€ÃžéÂé%YMŸï-êÏô„ ñ|QmÀܦœòõ•#` SŽ÷O‘£¹Gй¨Uô‰ôÉñ:y/ê7éõ¯÷Œ½1,Œß²]Ïg=´<ñ®Uhc±•|DâY9•äþgz"ÿÅðÙÖŽ Æ qKrZŽ&ˆìàž¨?“ ÐxŽE ÝÐN/¢ ”eáúS›3=“Gx™âT(T2¸¼Ÿ 7L9Ó'gŒ1ðÜ2×!ürcdùDý™ŒÆƒb:{Ñ—Y|þl{ÂúŠ–×fɰôˆvU œŠ7Îl¦'â˜á ÇŒô–c˜$ã½Vùñ;úL†‰£ÙJ®€ÖC'ËÒK”}~«Tb‡ôrå²F”wÛ_$!ÚCް‹F¡v7Ý— ¢^@¹=_É/Ÿ€=åýt?6p_Øœj9rf˜svâi«Z¤hå4Ø< [h†Ç¼‹eÍÙùÙ¢þLpFã¹H"äí–‡¨†A¼ÀK‡©“ÍÐ&E7¾”ÔcÅÀ³x3˰9bàYˆ‹=;œOc ¹a áXÈ«<‚-²ó³EýylÇ+‡!ë‰é|ÍIÓÇd"ÏdâÎBg^e§¡ÐÄ\c†; Ùä1 ©[ò°ôà%“MA&y ñ|‘*1–>ŵÎ9#l)ßýó!©l·Ë4å­s¶œ>¡‘? ˜¯u6“nbýÒ;E„131‹x&ó;’/NŽàcà²Epm‰.²Ùôg"8Ï…¾öÆŠ“ÉÂ,=çh„v«V§K‚¡P¥´²}Îüôº$‰ë¥neê¼/¿~óÇ æÇ`õ°iÛaõ`l±Z™Câz\6Vý™XÆ“¶}Q^6„s)ÓÙÞÞ'6’Aé\ťؽs|ˉ³„Ê9ˆ':1p¥WŽã‰P>Ò+„ 5NÒGzsíGMÙGzqçcŽô~DÉæ»#µ¨Q<œ»çìn8‘»çðnw6¸v·ý¸vWrâ _?ô1ÆE‹Ý‰wNá7ÛÇ2g1Äeˆçut7Ss wPLœøöotð¼Ãf<7wØðGæóo•gðÝH~ˆ¶72¸÷D'ñ›˜¿?šÞ{oyBÑRÍÄšX¦x¿fb|é¦wœŸ›X òSï3˜é‰lþaGLG*Ù Þ2 @«¼+†çÈüÿQ&¯ ñD¿{º({þaá à ÿp.'>࿊(ÊJAÞ@c ô,$tz6¹§™>9Cì ò°Wˆ»þöÁ®W(ãp튚²½B¸ó1^¡žc&\}qsÂpçXòXs¾s¥¼q½ßj—GZíA¦ÕŽÆócp¾÷ è&j*¢MãÊæÞ—§¾Tã=•ñYb2‹™?oÓ}”‘.6Ò‡ÝßEŒä–%$àÙ]±ûi–ÈÞ¢ú3YÅîoyqn’¬\3Ÿ+f“ •€3µ3àm sÇTyš²JÜù³Øã»`«Ý&m¹ü>´íø>qŒ™—_óòÏ*UY•±Ïh‰îéhcàgsªƦÜ*<£çœÅóšÞ©w ³ÍË1HpXÛ°;—ÆáÜ£ùQS>£Î¿ ‹s„i‘ÑŠÁð犃½hX¯€ö?C£÷©æ±T7jƒ>}&ïŒN„ˆÇ(ŠäÆçêUÄØ —$6+{xf(ÛFáó†³g5½ãC¦›0PX=JÝ=8!ël È*]ȘFÖÜã-QS>²¢ÎŸW=õéÃ#^"Èú9—oâ(#ctÈ„E€ú¬Uk,FöA»6â1@¬¤BÜȼ¾I|÷tø ß$”š°!{ªRªº;lÛ=s81rè€z>àw^nôoÔ”z¨s´Wt“¿nN`Ò›5KúNƒvUÖÿ/±µjˆJªõ/Éã«ä²¿ömZ"Xy îüÊ‘¾‚æ=9“éC]Šy"þ,¢r»Nõç…}áñÔ­¥2½,Ów$àH7Ó•ùÇá`°ÐæYßÅ/œY* ¶/X9‘·»¬œPµ/V9•{°Ú‚lÁw½hµ¥ei*$À*ƒ¡åÿö¨£©Îb~ùš(4+ÍÇ@g‹æÔô\ SñFý™hŽÆËÝ\ÇA ½Þ®¼Yÿo³Q,p¯šÓ>†Ñrápùä7_@#áIÇë”#áƒo¤uÄF—\Áß9‹ó™ßIâw'Âõì"8£@´Áu§Cn¢°žáNn`=¨ÏCu4š^M\Çû@(ç¸Î>ÌMYò=„G…ê>ˆé8½Þ|ï©ÉPÇð—@ÿ!>÷›XÅ'¶JW{¸Š ^&Õ"î(yÉÆÿ@ÔÈ™¼nG¼¥fMº™И°-RÌ=ú†Äà}õ¤ms䎸qm[Ô~àf¸²Àí´çámK<.Ý”³Ž2í1©ƒŸç`kzH¡ƒ%‚Ÿ«~6Ó_R.‰«-s{RÐãÜkxrK(Y3²€Z!áGç45#Áî>V~}ùä_ðçÿ—Šìendstream endobj 16 0 obj 4273 endobj 22 0 obj <> stream xœí™Ko1Çïû)ö¸>dê×øq-j”½UJ(¡Ðô] ¾=³Û¬=Ù®’mˆH.ÿØž‡ñ/³íu)AéR6ïNLçÅÞ‘/g·E;\½\ˆ›Yq]0Í«àz:/÷k2T¡$?õ×BBŒAÆvR•!ÒLj¥—¢+ëyq\91A^E[Ð>|ª_7ÔÂYø(™×_Šªõwî^K2 æaö¸z'&l4ÕÐ`\”¦ú&&´'ç]ušC݈‰o’ˉ4h\õQL,è ‚«^ NkÔ¾zŸGÙÚCæ÷DXˆ–üVÓá,¯=Ï’æÏ„Œ<Ã[Ú Ò·K2Z)†ÿrlÊK«>tEÓcÀ-Iq7å–¯Uv”tœ•n Gyï]W9^ bC´³x£CY±T ¤ÍP ~AÅHŒC¢"± ¡ï‚Š0D íå& ”¾RÐõ‚-V)Í<­N„iX̃×Bi:X⥥FÕÓ€D±š7D”J+Õð¬KîB(PÆÇ†‡Éê<Ï—‚ÏJcªWCFWIÝ'u'‘7H[í%cb¼%ú¥…”‘“Úôü,¶Á¦s‡iáeRÛdûÉ1Ÿƒ‡0KÑË<ý&M3›Ïi% 2xò¿(¨¦_DÍ«âIdËM¤ìãæ“fðÒ> ë¤öfSé%5ITX:ט_KÔ p3´$°ùÖtØôX°%OãÁÆ‚ÿm°½mÑÉÀáÃVfz0÷Xl%Ëïg–T‚A‹œ.B@O?rl#÷CîOš mJCË.6ÁØcŠæ@ËÓ6BˆÏ~yÛliìnÂíÀØq.ŒæÜ¤IœC aæÌXÌuŽÆS.‡fb¼šñ½P§C} »ÑÓ<Ï.†(s5ät MÔG4}4=fä`³u–{9°Ý%ÓŸÇœ àÐo ævLÛÌAä4ùj­’ì88E ƒÆÂÊnÏŽÅ`çh<sè}a@##¿ß NŒ3?†8v9D’‹GSFŸ‰r÷ϵC¾YnÔ‰©¦“îÙkl§¥íP(=˜¸=(Ü9¹clàjûg@Œ`5ö*gT+ʼ¾ìÜÇ -oi— ¼îýƒÅi †ÙÔÅzÿG… =endstream endobj 23 0 obj 864 endobj 27 0 obj <> stream xœÍ][—Ý´~Ÿ_1o=§åK–ežJ -]@)è°X“ÉÉ$@’!™IJFÛ\Éioé“eÏL€Òµ0>¶uÛûÛß¾HóÓi%¤:­Ü?óÅų“w¿hO/_ ·O¿øótñòòä§“NÔîà z}ñìôƒ3û¢”§½èÍéÙã“Jô}WµãWåigï÷}sÚÊJ´ög'»ÓýÙ÷öþTjQéðN¯£wLW‹F¹w¾ÞÉý¡FšVëØ×¢okm¿tm#º¦ÝýÍ^ ejÕí^ï[ÑkÓô»#yéåþ E/ëJ“žº›M_Ù›Góý”d×w¶oÏþºj|ÆtB’ñI9tý““³ß½û‡ípezÛ‹÷•¨cªz÷©½´7UÓ¶»¿Ø&µý¡WömÿÄ7»ý¡U%U¿{bo·²VUk;­Ýe¯w¯ö‡vw^x²ÃU½Z«ÝÏ{ÙÚ+öéáÍð¡göi»mÝînÂ7~ô¿Ÿ‡›ÙææžŸþfnfY×eo[TzúÝÎiKÛ‰;{„]8…½yl?.í´}à*<€{~=½¦{Ú‘‡Ã¥2•´rhÝ*ÑÎàÈW_ _µ¬w—ƒ8);ð¾rÂ~öÈJ6y”´:¯H#‡áÛTÝïÞÙK%´ê$mö;m§m¯_:}p“ÚM3­4›-ry ½zót+þùíŸP±Jò]?ƒo‰ìºn]„Q= }‰V}z ¼E¦%T÷;T¼\¯CçpåB³D^H³¤‡§{ÕZüè[·À‡y… ¸e'Á}Œë¯k˘T­Ü“I_ÜKá¥ó0qAW4KÛwˆâûe ßyN§Êÿ~„3q䃦½¥$‚Ž&ÜõœN¥]L+ÎvUq‚µé2+C€h@.Ïl3ƾÝK,‰lý[ Z©½Ù#e%}eq4)JÙ›nÆ€GaêŽP¾H»£!ë;kÿ†·ðÈȞБùv‰¾’g1ÚyzTj8ôñBN=ÖbÚ‘~Ê¿O†pWäÑ>'¡)(áúÿ0Y0?¹µiL)®ÃäYfí~LÒáz G yîAaFîy’ê¶°éàáíØwÒÖã0áÅg)òoEÈ `§›¬Jû÷Âd`²^m£F ?!x‡Íd'ÙJ™’‘$_b,€ˆžÿè ”¬]G„ öÈ(Ñ×fF“¢Ò¦:åOdßÈFgÖ•4F€åÛ%(ÏxšÄ‘½%PB¦€}é jgïÚÙàUL!Š’Iúú/º›HÌQŸ0ÖÔqüY¦ŠÜlW½çœ b<˜”Q`(AÙâ¤0Ð.kKÒú^b¤ebƒ–<‹‰È9lOjìL`È Ñãî?6¸1­šdæ)"²lˆ_C»ød4ð!8´'p0/&ÒÒ 5ƒÌchlñ²buÆó†Áë*|lmci-LÌtð"­Ö6ï!øŠô#:ˆÔpÒ\ä´ŸMºØé2¾-0Ý0¦¾ ››yŠ "—ž¦Üø«§±.,f¾bkVæ–¨AšÅÖy;¬;ª´ŠûêÅX/òsÇ‘:/Ó‚ ÎÍ ùÜ“”ëd"Øœb§˜’8k‡dJâZK×åLâ’°ÀRÈʳlv/ >\²È÷˜>1怖ÃXä:`€Áx»î }`f®Ð2t/^E)'_ÅÄúb)Yl;n18:ŒëMñawŒbpN"óVD½íʹ}‰ðGò–™yÌURà QÌ—Ô=ãxl…çÐ-Žá0ÃeI„·u!äqGߤí!c&²uë}“I"(l$8Sˆl|F‹¢Ø*6 QÖ!V[¼§©™9H"£™ÈMׯ#l!'%þƒ9/š-$aS¼À‹ºJ¶3^,Dþû¦)[‰ ÈVDs¢À{Dd‰»‚-- ’.¡H­0G…hX’'˜œ¹ ˜lcÒôÂr$e)Â22fÚJBçW×Ê6ÃZÓ‚gXD —à*¨;Nµ/¬2["žIü7n2h1¹ùï}ã´Ç /v`Xje«~IÖ8Jzÿš4NóMÙ¤‚ïUú(z”Mâ­elŒ€v¢×>8±%æ‘ jIZQD¢W§3ë¼ÜÇ"9Ãa òÙº KB¹÷’ѵ(%Npå›óa ÙQSâ/bYt¿“›—IÖAíÞ£êá§ñ#ßý˜\âXëÄÿü*#yŸ¹»-x#ˆE8)F“òÕ>i ÑG"¨„Ñ(ƒÎC*360ðWm7vÕ•/ìRJtÚWåä°jàâ(Y‰{]Š·GBC B±ã¾iLR'¥Ê›ÚL½ZàB¨úss›³J¡¼6½ûa€r£“ª€…RH£‡ 4éI|xW’Ë/VÑÓIZ·TýàLƓئ{F†Á£X‚62<Å­ÑB¬¬¨®LxÑJóáž4`žÍfÚyÎUܼ™BJö3]3 (V¿¤z#Ew9D—0odô>"£üMÃbQSk4ÚÎÔ8Uç§ÔÐsH-1&oñ_Šðœ!qó•ªòpÞ‡‘#ôÃȯm-¥«FÁ ,0¸!\Dã'3ôƒB ¤ÓI ꦺ)`úW}~:…3IØ',cNñ H91è¡ÑßRŽ·%¯ƒ7¦“ÊߥLu¨4Ƴ- çÄŠúÏÊ9FöÕ 8N Âiœwbéò”±jÛpÓÖ,Tpà‚ ]y›G‘S›p]¹éƒ–»Lw‚»¶PG¡òeÿ¸–>G¿ã4D®&V¡]!Îy Å Q¶J4u½\íú–œ@7U•@XmG£ŸJÂXDäßa"òpwÄ€ŒøËLæ¼@ç‹9$d_¸{ùúÂË’Y ú LFÑfýW•MÃJcIA|ÀìOš„§ñ€È–qhiëbX|`a]t`%Vz‰.QWhÚ bö¿V„à9•BbõÇ@°UÓ‚"lRsц|)Åàà/\ÁŽa Ëp,òðr\õHõ¯„¿U%dSƒG¤C¤¢´š¹ æˆR¶7tJAÄþ аÌX¶žïÆÁ9z,ÿSÈTr >`þŽæ–áC!"·ÆhG+,Oâˆsc…Šf ÍF˜^6 He1rÀ &|‹5ÑtA¨Á„õ¶]ßνÝM»©2‰Ö6:o3*›¤ƒsb!µïúù+Åò¿¼ ¥Ñ³göjJz%;ãX ¨í¸`×uíâÓ©{_ Ý)Mçä k)LÍëwáÞÄ%š›­åµïÛïK­Íîç½ê…’uC=lNîbxÀy±•†lµJº/ɇ7p± عáÞ×|ˆO­È c'ª²Ö`“®!½„ß^!†t1ætDzÆyz+…‰¶•FƒAi'æ–Ú#RÔ"¬EC,F±À Æ]ñ~×·³ƒ™ o’;µŸ®E­{’ÙÝÛqùÔÈØzdøâ­8x«ñÏІ懰Á©Æ%¢È®tn¬«j’g- ÓZ6­^‡ÈÝ·¬€[-/Åš?ÆW#‹°r£·fbGªƒ”=W슼üòaLì±£“ŽÛ†-E8ž3á²Jü’ wûЀ w«pù«z­¬¬â2*¢(4Cä¥ì8›¬Ø–e<¦e%Ñ’?97Èh§ Án[N̾¿äÂ胒áU§hpã‡`þñTÒ×›ý vlåðêz `[Â#Â릠èl~ŸP83Rq&q`là .Pwß*:tä’ñU`–¶g0*¿%j!a:asl;Ø`µEÏcvêl‰²pLoû§ Z¹"ÆLè!µWénN2´ƒF–Ÿ-w˜×“ô;Ô¸“èºSö|n+b¾à>ù~D»64ÇEUDM0¬ü7 ˆº±S,æ½J¸®‘U:Ï™ZHlDg*ÓF¤îOöw¿ò±ý ¹Ì/¢Gpàs_úÚV>À†°(U €¹"E¸%ë™PFOÀjáaïNJG*ðönçGü¯”5˜mH8 Ëׂ ’ö \'ÖϸÂÕIʼnû6õÛâµþÙBŽƒÙP<ã³¹e†­¤¸štÍasuk„ÖãasªpØÜ‡{%L7V_„'¯÷Êõ¨ïÙÝÓpÄÜÙÞ¨áŒ9ǯ]VíÂ;CÀF ÑCÿҫ麩›Ýú3è,!UΠ»O_Ô¦e°yO9 †õÔèwÂWŸ÷Ä ¥ä=Qó ³!,Õn‚ÛWÅ `ÿ>L,Û4È\_ÁËÜùvcéF%t‡14ÎŒ±s|Øí¬‚‰(LæÈû™ìOÞS'ê%Ÿ‡ðìCdÉð4áBàbp–æáÔ8æ´tòLxOpë² l<€¸ãÝ×,؎Ǧ £÷7‡o½%…Ú”¤€0b5·ŠîAêÃz“Mñµ«=ŠÅBAxòB<Ÿ8H€‡HbSdX6 XÞP‹ZËŸƒF¼Ôð‚»6‡ê›z5µ)ðCžµ >bŒ³j})_9#"Al¼ǧ±c’¦ÎÈAb±%ÞŒÏ$“¿TÕNåj$|»85Šc‰ lćg,Ã`¿ ß ¸#È($þtKB‚ÈÐëÌ×C'®-OvðEñÔà<8Û©väv.‚¦gMÌüâ&&q4!îë#O(¶ä[ïlÒ¶‹CýÊX|ÑyKwÕ·!±…“Jc«Uîd­béë´;‹&K¦†ƒ} ¼Ùš”ºvÑS½Abs7Y»jŠÝ´~C@³ü‘Žö–\–vbóÆ‹ŒgÄÂFd)¶ã‡?‚q lç  ÃJQ¸ÝÑMÿ»º%=sòSKW•3‹íÃmïÅkú½ ]™y}[])&vÏÆŠo ÂcGmg,'<÷™¡Á嵘¸àQù$;è#ŠàÜâÆ>ŽÊ0»äRÂÜÐm×ÔÏ aĆ-¯´Ô]èúXÞWpêr>¨íÖ+=åèî¹·ˆ¾_Ç6C’}ÉÖ>AœûY{æ7ÿ:k_8·8Ä·¤ÍЮ Õ Ud‚Ⱦ‹MAÄ0É£D«5]dDÚq –ž5F–^öµh2ëÏ×-V±øœ8ι]–†ÊfpQX’°IÜc¹ÏJŸWnS¬é³.åÅdïa*_iD,ñB">â£^ž:æoÑÐJQϡԽ|IÛ9è^»­Ra(áÁ}Ö)±Äê8Cöä¹ÄïLÑe­ÀaXÆÎw1ÆÅôx½ÈG˜1Õ~›z¥XÃÎ&Üiè+í‘¿ ¹ðŠóÚ« Úö@Z"_*ˆâò§æ3 Ì-¿oö×Yþ!RNm‚ÛÙä1ïÑýVýPá>M?É….2½(ÈRƃÆ È­$h‰¾Œr3$ÝY’ç3ÿ‰æHø­(Q$Š…7”1»8(mOc¹ET!"„C¦^‹{qu6Þp¼"Ôî—îsŬ GŽ·ÀZìDÈnËQ›f@®«Dg6†â¦˜ÜßK*Äöq¼3ÌudÕT±óñQ”vÎøtÄطÝá„åê„ÍÚ»áYêdg8­CoÁ¬®›ä+·p3ýuøH’ÏæQaÛ\£Ö茂·š¡2?‹,äñWþ•,ò{1?Pĸïnéÿ¡.nÙQ¹ÒÍÛÁbožN{Õ´ètÆNàcùÄs÷’Z^}3²+4÷GgüÝÇpÆ`ÅÙâÿ÷­¦ª´±—Î#“•ÌžûTü›y³Ï‹w‡c°-&zÖ°Ô±NUdzŸ¸“/&˜Öžz¼¡5å‚ÑI­ jFõ«Øâÿ¨þk¹\ 8:ã˜IUçþÛRâ,(z´GeK2:9 Þe ·ìà>ulºïZ u—“‡êÛbz.‰ëqÞ•âv3%—zd6ÙTx²A;fIé·£ÀÛÿïÀn+Í3<SRDÆ;z¬§£%ÓÈ3ÂÑÛº)ᎅ w2'wñ,›ÔøîIkvœ òr ÍG&>JÑÔí²ÝÃ~Ñ÷H¼ÙQÅ„3нáI@ pËôÞK•eš„8:§Tvs¥®ßnœˆúm)1 Ç|¥û}Ô˜6Ý^Þ ÕÊ>Ô@b!X6œX„ùÍ’bÁÃ"à)İ”}Ý9ØÁ's-é# :´eš÷^XÚ/Ãz}.É‚ží”é©¶%E­;HÛ'Ñä2Úä'áo¯AŠ|$XA°þo2ýb ÷ÑÙÉßí?ÿí4äšendstream endobj 28 0 obj 4999 endobj 34 0 obj <> stream xœå]Ûvµ¾÷Sì»îͪÕ§\AJYm¡PpsX,Çvì´Ilâ8îû}âJs~i>mÍ8B(¹`2[£‘þã÷4ù~Ó°–oÿgº8{~ô‡¯Ìæòö¨¿½ùêÓñâååÑ÷G– ÿ_ƒ^Ÿ=ß||âlÛMÇ:½9yrÔ°®³fm7ÖÝï:µ1mìݜwÜWÆl¿ÙQ§–Ù+ÿ/´tÊSú¢0¾_ã5Ú^¼ºÈ¶43~Ð0GÃ5Õ1òzòbˆKF;Ìðz§ü–óLTŒ,()™õq/Iœ7L5Á’µÜAÛp·0ˆF§à.›HÑä5†$„²Wp,^ùe|ñ§d9ÚFâ¾Ü#V™Ã$«Å;#cgäç"/x†ü,„ô„û¬ß–ÐJ›/‡Y|iº6WËq‚„ZÎ9›bz;< LOFÐÒ_AkroÆ‘‹tŒXBg }J0 És®û„s8Äê—{Óÿ]j[»çÙx`&SHß…MV›gô{€MÃ+úDŸS¸š«Á¢Å”(è.‡ºUrûow·óŒëVYÂ+(dë„Ä?ì:šeWšP4¼¬¦`C„Ùƒ¨*|í'èU²–ãZˆxÍ@*@Ò#‰%?2”j|ÔÞ7Q¥OÃM¢—7á&t›V(„wÿûÝÑ€Þ¬B3ö÷„t{<¥iœwJ-QŠ2eÓ]¬éÝYºü„—#ÔÆj]5³5^.Qƒ «¶L H €bωŒ$þ20ãŸîmÒo}€Ò¼µÌ.Hc`°’xQ¤I5õ¹‰Âð  ’niì]1°k¼ –ÈwüÔÍD1ÝL‚5Þ1>¬Õ35ccä o[í­2Ÿ$Üt}z­Ã,I9Þ@Y¼"\…vÉ •Œ+ZNUâ5œA–We‘xÇ(‹$æFI‹ƒ¶{¦R‰ÎèÅÉ ^T„‘Ì9é˜Å¨Rœ®GÇÒ{”éÔ£‘:ñhéÏ¿x9—Üôºu³1µ6†¨•â&B ‚_—cŠäq¼y …s†YˆΔ“ã çÔ$×\¬QÖÃR¦;ª2™MžÈ|Áí“Ýß‚áúrwÜù•öù(çtŒ³w¼N˜%'»ÆÀ¹³ã§îmTáQ"oQAª‰3 d²³)†¹£ô(¾ˆ˜^ø!ã}ô¼WoÛ¦)#¢[½GŠ Étb=%s­‰«p’‚¬»êY"Üë,~vN>hžƒTÿ³OaoP"¼WŽN xgˆ­"Òs §ÈÉ­C£}\(Ö6bò+? J-S‰­r¦š±Njn³Æjj˜Gn îÀñhìª~¶!.1È“e©äÇ5Ú6MóUló’]Fÿ‚HŒ¹eFA Þÿ.ØÛHM„vk<Î#IZ©¶ÄËmªQsfáhIC—Ä’÷Ê$…ÊŠ»ËöFmÆpo/ð„§T ÐÈÕ`ÁÏãr ™ŒÑdž$´Ã2´N~÷õëž3‚ª{5†³G3ÅìSQ8Ä@ç ™ø0ë1µðtîð 6E¯éKËyú½¦ a¶Ü*7v„;Þ áy8=DÄ7/ò¤  ÞÃRUÍ»ößoD¹LLA P¼9RÝ(Nn% ¿¯o8§/ ¯òcA-)óiYy W„¡3'ÙbC¢`%&U.0z$Š^Š÷{ív´ïbY¿¬ÐM.{F3(äòïQ“0+p Àìb¨vRÀbQ5ñ^ÕÏq¯>òY¸éïxÉkìå)á–žIÚ)E"\=!};žŒ‚š5kL-ÃÌwe ¿b•"Ú±¿Ì˜B ¢ÿ9,¿ÈåEŒùιºàJ`bP2dÊ <ëÅ>í¡˜,®gfŽºX´A!T†/æâ‰µgM'á¨ÁYºÇŒ>!¥ T@;ÍMétœ„íä{‚×î Ã÷P}þñÑ!9˧„,&[ˆî†¼ÀXù«…iO,óö®gÀ‚ÆÝè@C®¿/L²øÞ„ZŽƒÝŒpY£‘ï^™Öðu(Á? (vÌÌ÷7iKÕñý²¬Šæ^#îá„.öm=Aú†©´c`à(ˆIާ¯_ĺ‹ýf†dÖ€QÉœ•T#©J4±Õá~·ô`Í%X.…­ÊÙ"F„û–òIËÅã­NѱóXSì¸; ã!*ÜשÓr‡ýḵ´/î–ò­ÈûàÂÌ!º,÷õÏîÙ[Ò’ 8;¶ô¢šå{Q@#V Ádq_«ÒÿuWîùÑ®äà•îgj«æ¼Ð®8ã«ÏëÞ3åN^>´¢]ÖJŒØNaÛƒÛn+}ùDª¾g†ÜF½’¾’ù~ì$³Ó…ìÙø£4.†O2w3Ûá‚®G¤ÙDSb¿ÄtIæ­&ª..0ªqs¶…'ú)‡œÊ9œ½‚HV6M¥OiÛÄÃ¥©Ø=Tau&÷/ð,€ÿ™´ýѧ½#“¼š?!$ø >ošÊ„Û±U(ö5“BADXÇÖJXÚ)s,åìK©âøçX³]§•Å3¤>¼ÝÓ"•wVãÚJ°àô`„²Ì·§¹O>+|î7æLmc·ß%VoÚ.q‡d,!ö£¸ŒowBˆY?»RLk#’cÚå¤ÚÿöÁòlBìï=Um=¦vþ\æÜv䬳¥f‰Â±Âcíø¡L²vÂv9×e¥”ü>\¿‰\?Û)¥gœô=¦çŸ´r›"“ý½D×tsT¶æÈbµ›õ€²TÊ™¾=ЦZ5-,ÖÌqŒMÏ~Ú…¢ÌqgY§š¢àÁ@†Ì-â’9Àމ 6L­«2è`†GTÉÈœ7P2¥eÊM$SpgÿºÒ9ËØˆû† ‹Ç=>rM„ôèac‡b€‚%»š¾‰`¹´#´°š‹(f6Nðö§ÆÊð5±œÛ‘¢ùeÜU ç髈Sñ %›„†Õ¸ %Ûûõy4¬i:&HÆ},¤þEÊAŸÅ>«•…1m =žqinÔ$3!½ŠqÉŒ4TMeãôSÉ’£Y i+8‡³V9uu¼M¤æ“’ DKà/ÒÌ[¸§ËNßS¡ÛF2+“ïG4ºe]©ø·¥Ðx2 ¶°ücöÕÕäÿ¬]Òq·¸$îdƒß¥ÓØ—dǺŒËXQI$?°#üg tŽ!ñTºmçèÊmÄQôóÐÓ»ÿq—þcVÅZÇhΔàYlý˜®¤Žpç!=³¶JT¬w±Q!’P.ÙÍ3ykÓÏ)¨ñ¡~ćXV’!HÌ)²_²aZXQŸØ‰7³4>(!\ôÒ5<ÕËA§•¶ŸwåÅ…¾ÉêË„7¹«¶1ɉìàÎ4ÐjXH½¿[Ø÷=‹§˜à¤g=é~] °(ª^†,ºe6~öþ=©à|æÃÐÔ7 ùÎ\n&p'lËÚìÌV5ÇŸÖÀGÈר°q*Ìãé¿5™ùª ßÀ¬%ûÉ“M“†¹ÎiÀå¶ä·7³f]£í ;0FM>ñœgD;¥|Eî©øÖ¯ùÐØš¸j>nœ&àCÅúƽ°ê¿®íɃT8Ö©ˆb¦‘k\eYE„‹¬˜-}oU¤*À$ÄÄJ.ê@y]Ý8'ݬxë˜@Ñ…–icƒ§ü%nï­ \ú¢ªvi'vmcÖ 1ÈŽqÅ9SöW€êpGÙÛ¦½JßBɰŸ!‘Ëâ,Jð¯É¸÷«^z®W”Ý´‹'´‡‘c-Úì{L¿19ÆÛ™§•EõTû?×|pOñÞàä0uhn™ÛCçÝué?˜05´|D¾°ûèP­C9UÔb…½¿Otp–Ä4 hå%íd¶Êïɲ,ï'3È‚6ÉQùûeAá?5q‘ÏÍ.×hn© *KÏúÖ@êgѳˆà““£¸?ÿå.„Ëendstream endobj 35 0 obj 4113 endobj 39 0 obj <> stream xœÍ\ërܶþ¯§ØÝítiâJ"ù»nÒNÒº¶œéLÓÉØ’,«¶.öJŽ“ÇèËôõ ð€¤,¹Ž3 Íq;ç|ç ¼[Õã«ÚýŽÎÅLÁŒ+¹þuûâZQN!Sˆ§kæi¢"|ùiæ$†…)ÈðX‡G6øR@žA`ÔïŸÃãà `ú£©û΢©ûǯ)Cà Muè‹ÃY (Ê+Ö¶#ãÜzC ŒmÈÖä”Ç’ù㽸‚u:c›H"¦»B²S´ÌlGbwìcŠHË'Ž#´‘rýGÛ½tHdüK¶Þ†î‹”_²Ú6<¾„4ZF{ß6‡vŠŸáшð¶ÈF‘a¸†p÷ÄËã¸û­$/ÿcŸlO”>å6¿ ]ÕB„ÉÐÞÁ†Õ‹ï,ZâhÕŒ/%Ý·6L1Z£ŒÌ·%o}¼/ˆE›÷ÄÁHi} JÂ×y³¢§%1+.¼Œ:¦5± ö-9íÐJEwÇ @µ*¦ÍkH—\ÃÇ»~©Û •¾›"œ¤¿÷ÐîÜ 2Ù8Òvÿ).\@NÔ÷Æ‰Ä U)!ƒ­å»ý.°c?‚fJR›ÎZ \T’›†BÄõ ÖDðR¼ãBæ­Û£ÂÛ¡­‚¡KºåîQ€×Ï 9¾ÝlµÕŪ1ë§a“z[BØ©¨Êá~Á&]%’úžî#Å{¢^y¹=öOáݸåf¦OÚŽ…¸’¼Ä†CÑbúÐÅj¥(â|ì#&¾Ñ}¡øNÊ¿ñújÐuLtläòÞz5o¥•y;ùÙ }FâÒ>„ù°SòÓ&4þªŸ‡¨Ù0âv2ð¥ýåðßÖñ“Ü;~¬µKºFÜ…èºÉýwì¨H>ö9œãÈáô‹Î îb)É¢ˆÏÂW1¹i¯Çb„ü´Ðu9\F‡î|Ø{å4dw½€ý¸ G‘ð׸',c¡Sâ†GŽ¿GÑGbôwRã|©Ú3›Õ*½ºáÊs&aå-W²ÒŠý¿yùç)ÑCà‚÷Š”î(8ÍöËÆTM+cÅM³È@&ß“(@–áö 7–„H {Q²l¨[ JÞ2Å£š\:õ†ñ%XÉï¯)& ^À¡¢ Ï}ǰ3˜]¸M›˜å Zý!Œ:Fžd%oúœÀ¥’m™ðù–3QIF3›} iÛX^†Ñ|G¡æ*õ»À#Iîþ@þ»ÛHV†øxk÷)¨”;î4äaªg—ä˜çŸç”ö~WzÅþ3Ü(¼âÝ„3]=£«gö’>éQTå/ƒ+”fˆ÷ íÒ @`¨Ë•Y¡Í%$)–É„@É„Ýû2†1ÕÕÊElñUj|l‡NþÃ_¿?8ü}—tª,)‰’5`Ÿ¥ÓYèµMT¶‡z$æ‡Ç°~3 ·öaCg‡å¢`ƒãhëI§“á\[«°¥“!|4c‘±vèJz Ùÿý.¥Bi"QÖnA½AmZS©ZÒôõšNhk¤µRX›é Ô2}lnTx‚Ãzo‚QŽMÉ®¢ ÈLø3 eãõÍt®+\€Hfý5cB1·{@S$¢8ål%l›6¢x1K¦ µôtqI-ÿ-$hI[oa…‘^ÂIÅàÈ+…äÖ‰sì`Wf½­Aá öÊøŽØ+39†9`’SàrCpcD%š¹ ‘/‰Øe¥š©ô*¢ÂõÏJ·Îùfò:Šp:C´U2Sœ;uízªuæµéSÜ-ç•‹¥x: IPq1-Y£yBKÉ-ŽÑjüj±Fûù.ˆ•B쑯$x3‰Rª ®@㕨‚¡óÈûSoöXºžF°„3º5ö½n-m‚ƒÜ}nâõ+3³ }e?}©ÀY·Ûm?ÇÞ|Ÿ3Â¸ÉØ¯ÈÑÞÃó}!í]ÒþÚé(É[F£6]ÙÏ `嬮oCèé "ÊêæˆÛ«Ó^1ÖŠ%´½wã§”,ƒh~E[䯹ìbåÈ¢ ^g¾Õà±Ïkaª†gTì5-ø~RÀ N©Ý`½x¡8õ¡ÞøEœÌ)DzrÅtNëÕ¬¥@€#lxÆÐóƒ’c‘WßÝÀ¥GûPˆjšs[‰üdNŸúßq …ŒE˜Èb¹tIðΟkÑÂ×=¡òÔ¾ÁìòÔ|î”0p’V#鈒?¸ò¶Å›@ÐH/‚—0vJ­Éiªp^Ím~QÄKHVs Éb.=åO"Q‡É’{s‚ã1%*JAp‘T‚â`„7iÈð˜®e¦ï™ûL¢óÝLo ¥&É©©íæÛ3„¸“ÎÒƒvKÜ=u­96÷¹]ÑÛŽwwÏ›KŸ²q²|åêtq-ÄeÂ/P(¾+ºjõ¢º^ÄÃw´9y”³.‹Sâ²Kj²Ô¼!5Y© Kš.@v’K)ƒHéÄ •J’ý¡ý“QsÅëþÑW×§sÉ{™Ä36—$žQìõh8ï`¹D5ÐïFŒÆ’kÑá´„e7=`;U±¤RãC§”mü#>¾…cÛ8!KVúëÆzƒœE|±4gå†[9¶#íè1@x|÷Ô¿ §Ã"é =Ö¦gYWz¾ßõ¶ÒÔ83÷ãe©„ :Ñ&fu/Jòò…00ãFv3µÆë_tC•Ì>oŸ YéýÎ>yÛXGX°8°UuïÍ;wú׭v\nÙ i»såS]üßwDb-™­2ç-uäwÄ¿?kÚÌxìÜnµŽv¨f&òŸ0k8¾ /ÎÂp‹/Åʇå&á#÷{ü|coÜ+…Ø¥&ÒàV”‘!,(©¦¥-ÒÀ½b)QT¶‡àc¸f:w§µ±$Ëp6j›¹6阛.)7ì$zôÇÛ÷aI¥0½aÌçŠÁø¢‚ŸY˜d0HÎ(íALéLú#ÒÛ(zòR…ï›ìb3P'!=\l91tÓ;*Чë‹v&Þ<‡½ñÈÐsóxb¹2nÚ`̦f.B>ÛÞ—ZÍå%+•VÑ´ÙTðEt¹]àÁW„·×…K*`Ø;Ç^HG’^±<âM.²GþØÁ’èg¤Ÿqh?‚„3‡¿»ëgŠ,‰ ]ʉÑÞ"(Ãnødgò7žû«(¸ð–%JTecÌÝúèAùKÊÃ"s7¬®·F•ÂÊ”> stream xœÕ]Ûr%µ}÷Wø‰œ“ÂM·Z}#UIAB%¤€âJ¥ (ÊcÏxHflÏØž©<æò_ÉïE}Ó^’–Zݾ ¨™¦Ž.[{¯}•ΫÃ<+ÔaÞÿ;?œ¾…iÒâ¼/äåùÐT×-výløÜ¼îv·t²§ÞØÒõ%¶F© ‡zÐa¿‚ž¼e7u tÎÍ_7 û\Ú>Òh!F)Û|ítÿõñïT©3]õLs|f8äRššÍ4‹þ4½×5îPéL3éü>ü×ä]±;6ÝÖæ{µÚ}d_~nŸœiÚÕÁäøŽÝP¢@ggÒtö÷Qê®ÅÎVñæ4ðâS‡Š®Üú™íšvzYi…_:ñTÓ)nú:šwªÿºùkØ®qº}··ö „ð‰}ùÔ>½ÞUfÖ…r rÁòÛ}¿um¥#òÎå(+‚´€_íd¢@¿ÙJ‡}Z ò Ë•aŸÐí:ó—PÖUm¾ev¹¬ºk¸;ODÒvuIɽ–²ª‚ŠZ1ʱ*³¦*f9þjÛl7Ôa;ĸðÚpb‚€^ökI‚@°â+w3C!¥ˆ9³ZU S½°ŸÃÊnd½òý²=ârÄœzN™ÚþÙ(ÒݧæÿóºiZC1Fø ÊÑõi7û‰ê™g ~³¯z-Þ\ªŽ®©Ô´Ç¡Â±D‹-Ô63‡ñáåçöÉ#n¬W”B9Ù›—†vDÃôÚûߦgóÒч¿û[ŸMsU§ÕÃ3“2Óªkvßï‹&+t^¥wñÅ,Ö 4¸¢‡ÝâêÍÁYcwƒ* Ü-Õ´}ë÷¾è¬…X”Y«êfþæ5"²äå°¥fîÕîW•Mss1Dgµî·æ”Nã…0 tv«³Ëw –º(ÆÙƒÛ›¹Óõ<ý÷Q`©)U›u• i>9þ«éE+!B›å¥n{öS“lýw_ý¯U1õä ;aöÃY’k+žÐ ô [jd»To"f×ð‘C鹫…Yx_QnD¥ûÆ)ËÓ ']53tSæÅî?´ó ´&ÌT¤j‹%yÔE†v@‚é¾_gVöÊ’YË܆̄&ª&Ït^<:ý'Ç?ÿ2T:®稜jG½Ó©ª q‘SýŽªÔË…ÇtQƒØ“;…72ÑN¨Ùëxâó‡ç‰rü« ‘;•Ä?m6²Í[#I`PÞ ²´Ñ&yi—[W—”¸×T¸B½ ×xçþ˽½±@™—Ž–ôƒpC¿®™Ñuf~s4*Ó¾¦Ñíy£Z$'Œ IñcythOØÏ8ƒ:kÍ3Xu×Vªc,˜[ºÌOPµ¥ôx¦ß–, U(;þÐÛ¢M^U8¾ïR¹§¾ ·¸özM©qí©Ç-A‚-±— j³pWóŽNx,Ò3ÍI×*†:ƒn3†N©­ ³ý.åã:æª8´}?kì·èF¥7ÓFg‹GÁ’–·äì2Ð{ ËÌÄÆ'ÏUY­´Üùñ°Á _.Ul‚…àŽO¨¶ÌUÄȘ<»õ‘?n•ðÙò7‡€=`ËßM *Ìxí9Žç å"Šâ ÂK±•¹fs08ŒK9VÜU2BY5Ä÷ã+ ç}œöñbÃô9p á¥ÛÁ¬¼¨ÏõÉ3 k¼mÌ£±±žÀ¾(k]£ëY(Ièt^>§êTÖŽéœr¾1f…éǸ•ÙÃfWàÛ&ÓmY4¦©Î:Ý蹪ܘ¥]iàÓØ!E1Z˜ów ÕڬΛ¦êwk~ûz¯²²î }¾7òš×ªéCVã˜e½ûÃþhìRÚ#=Ûn.äñ[3xÕ´æéÛ#´¼‘Î¥åeØS³#.{$f|£‘tÖåê¡v«Ï5íiQ×ÜKá9Ú4¶[-–ù2P6QP]¦ŠÒ‘¡'"±“¸”• Ç Ç9°3Áåâ˜e´(ž×,§’¤(=0|ŸN’çëž²ÈúŠ˜)³3Fg«lkô>¿éw?p¢¶ÛJhîÙÝ q̦­K "dc!`f÷&#Y×å¸R³ñÓÊâçHÚ;iMŒj] ¿îM)¥¶Óu’r©‹GçÃLœ€ …¼¼–—ðè.Ý“ñ^°ÈH’rz$B@–éÛ£~\a'JÐvŠœµºŠ`F9º—Ù§|:‡\“éÏdˆ['˜–i­AEc›<„ÂGq/¶xüLpç ÆÌûcÅŠ,óÌ@ReÁ|Žž“ÉHæ2†NŠŽªá) ]ë0‘7î7B…°Ä‚YhÇz<׿äH«ºŒìÃã3ú8aXàD+²@´Š|•¤"º8à‘>ÌYJ—~#(2æ3è‘ìP!,Pо8Gó„žQdôkRf@ƒ;„q¡Íl`šÇ¼x€’o5}À_lŘ:‚ñyþ’F¬SŸóàvlr=JÌ®œÉéð+Á=P¸·8 ÏAbIôÛñà@„ùIR÷©¸)ãÄù3ºðgl˜Hp™ñcH·>ý½¾AAâG¾è« +°Ã¿”·_ËãŠ4³¤bìè1IT,ñ3; ª!qYèÓÁO³­lyÈP×Ã=¤ FãÉÚD'ˆ¼•«…Y‡Ûj–st×;"T>Ë»ºr¢ŸëŒ‹¡‚Œ—Ô¥P®™à3µU­µ‚¸E,áÆ¢¤Ð¿#3lgß”IÝZßTE²ÅƒÀÇTîm„˜ÉÉT|ÈFS¯—^N$ª•–ë¿IØb…AÍ-ã z訥À¸›ÖÊcuU•®S²d‰%]˜Íãd{‰ÄpƒpAV‘`ðÁ’©¿¤caªæq>nä½+hÀp#±D£‹ãÞ#H$ @À$xn!gºÀ£æ¬ý$2IhÀ aÊÅ&4}ÒG‹¥e=ljó&–¿ãü2 2Âs_[½DGr×i¨XhtK¦øf*W2S[´X"[±ÀG WðlÚRÕ2áѰ„“‹jr AɺÒ E ‘Yœ6n.ÐòßißÜl0rn;›Ÿ¤þ?co¿œŠ„„bçBÉiè ÚNi7o'&ê÷ÔÍ/É^ h±æ”k†§ØG©^ú΋T¬;ÅÔû S€BÊ÷Ì_?sJõÌØeÙWyi¨Š¦e³xq[nU[‡l°d•q2³1ó6ºèÜ öñü’9ò`È‚{r«ÚlHÅæ·ÓW΃ƒ4À ­ùèÚ*€c`Ü_›6ÚÛ`°>Œ¨Ý¸Ç9<*:wªêªÊÊG«äkéÆ8n5pFQ¶MÖÖØ<^³Æ`¿·>u†Zs¡G‘¢G󓧇œ{‚‘vÂP-âîQÙVYWFæW ÷Øñ·Q 5êÄÀîý)põCƒGfy^é3ÓV÷Ðä˜XSí02PUšOÛ6BÉMÁÁ·NɈ~¹E7°Wû£¥¤¼î!³Gq€>°O1ô)z8†JÕd±Ùñ!‚ëfÈSâQÌ$¢C¿Œ¦]D/º:«+\ã#I!? ãlCô€/Õ¸o’ÕYƼÝ\›¥YmÖÇRÿESP%[Rt%O·òñ•­Ä‚¯ÿßo•ZIéÖ¶Š-ÿìãœVCz~Àà!*Ü`Ú$\ödüp#-¿åǺz;8R›Éãþþ‘´¥ÏÞ;âž0 "Ü­^hÅÁ“9AÒɹžT Øw4øOsxÉñXçw¬ ¯Á$¾ÄOñny †fcNÑʱyÓJdž¸Kþñç’wn28 ØïÒ„â5dÞ·fÐϯô,‚•Àãö`ƒ;0NÅT"òåN¼mìØØé£‡D¥£vˆª¶‹°Ä …K E•+ŠkÉ“/[ŠÐW@páx ¦È꼕ã‚ëOÔð¼(ÈÄhå ©ÙäÒ’«ê_¶6WnÏ¢î`)ßðþH©oòþ®s@´ÅkIYÚ’‹·p²TW5es”™8 :zºµÉÂcœ»YWÑbiKޤ„óÚ0þ5^&Ê‘ŽGåSþ8b9%cîçCj‡—ÄKHUžè!#±)—$Ÿó±ÌϺ:Æ™#ÄôNQ›:l{åG…mõ†âHšÕ™Bî¥a6Q¤KÛ¸²| dŒ—ô‡VÇ9µèAgj¿ð4…®5VÅËØ›kvýhÞæH>g!_0.ðˆ¾á8Ét4ŸÙŽÎ=H¶Ájxj:7AÄòJ\µ8uÜ,—û1xñbƒ¥<©Aõäa®´·’Ðÿ\¨FÆS,£3“^´§2åãTNx騲·§ÀK^LßÚ™D`pÀ/ÆJztÜÚâˆGìž´Y:g¥’vOò¼"0ò›0…«[uòddŽˆ#q·¾)¬ …£)âû¤r¤^Ö–'/ý®;Vÿ㙞EæÀÏÑŠ—H ع p³ñÕ~öQné”9‘{q¢|êQ4ñ-55ø]r#jèáê£ðR¨í1¬¾$iJ¾ ï. 68HÁÆuÊcçšØ%óQ”­¯?cýÍ^<Â÷÷Ú6:}íÝn…—Â]Ù'z)ZÄOƦ¦úH8ד.ò—EP*7ü›«žSöà—ôñj5¿n6þ \JÂÖå”rÇvû­(á3º£•2+Ç3qL Ñ‹–\ìà%MCý<ÏÖôÊž$äA¼ƒ\GŠKÊÝ/¶èSÆ,Ôx3}r&ŠA3-{‡q>øæÇ..Œ»¸@Õ;ó(h…˜  ‡œû–YXÖ¸îSfͶÕHeÆže”4¨df5ݲX…»‡Þmc[4ÔÒM#Þ ^Õô†y¾Ü™LÚœÛnŒžkÉ‹¶ =Mw­Îß»ÔqJ‹Ló³eÉC·ÁÝŸ>Z­×áœÉ{\®-HñÂR¶Ø˜Ê°1„i· ¶H¥\b‹ôð•/8àa¡ˆ¡À:èâ-g¤ «À"w IIŽ£ÜU„T *›7Ä.¢_w)~pì2e4oɘ08žŠ½s•U»§â¡“'ät°‘o)ÖNbÖꨎ›ÑLFuø ™sú8^Ü3Ä?8!ùµÞv9ÜÀ[q³‹•üicQÁ‹ãrêÌ»%.€Sˆmå*¿E³oÊQ ¸ìt7ÑgiîœiMEMS¿PáÃÉ–òñ^¶¤¯ÜzDã$Êž“W¡<²Á™=z/‰åñ™Ï™*ÓUE’«Ü¸t°„YûÔyH Œ /µŠÎ)·UG@Ö8ÁaÐ/?f˜dÔíªÉ „½Æ3-P–áòìÒÀã;O‰Ö)“2² ba@•~¯ˆC/á‰]GLÀ߬¿áVbžJ‹˜)L5r%xAY;ƯÞÄ{ˆáÆ/Oq…úÄâ ÍA>èï,lK^Œ`Sf­ˆÝ9Áöƒ 5ö·u÷xYª`Xj‹&à'hi,9ÂÊ(ö€¸u´‚[´12G$9Ð̶BK~Í ø&~Öû®‘ù÷‚rgiµ‹Õ{°õ'ý k$[ ¤$…·j2b—€Òd&§ðïX>ÅCÜ\¸3Ìi›ˆtüŠ’`K}a¬ŒduE°u9p«wfÞèJk"im¬ãàWÀ‡m]‚7âŽQû…uE€@ ëZøÍ¬àNgਞӰMSw¸æòXËb¿Ÿ*÷“ø2Þ|ó"9±Àëú¬vÌtã7uVI-Xdô7À1]t'+0âYûÅ ŒTî*v¯«êúÚÅË­žQŒæs ŽeÍ´¾³çDàhPªö‹Rºø›=C1h»é×JìlÞ‘+íÞa¿£•¼·ç›=cx˜â+ªkøEs\EªþÑp_4î\Gõ/ÓqÖ•¹œáIßdêcÃl3›Î³c$ â°صÎKW–#§ÎGÒpR C`òéA(ee>NéFL6ìÛàÖÂQ6<»À/ý¥W(p7–W Žù¹õù%žÈ9dÞ^”ç·}n“÷ª$Y9Xï*Üž“>†ÄKO™`oÅœ»¬“¤'ÌÚ%0*µE‹"~!§=­þµP+‚ƒ~þ¿gô_iM 5.ÃÓýoB§ÎC-DÔS¿Ìü<-ðcp;$¬ÞÒ$ÐþÅt^~7]ÎBω?’êßì=¬#Ú/{v©ò¢‹]Iö™Ä !t“Ÿã„;>ãâC[×ô‰LV®Ïù驸Å3ÛÛ d€ÙºÍt¿êÈ5üœS˜|UH$zê€)x¹ HdfK3ëb œ[ýÏ£^<;ÈÕ 1À"‡Š¹§Nü’à{‡Ÿ}ApbóP\èœÌ5¸x̃;I[‚±í GÑ«LlÏö˜_ãj„—nqu̺”^ÞÈ ¿ ÍÏüP¼¨ù7$ûFÕTšÿ&”øèøàæßÿ í­endstream endobj 47 0 obj 5049 endobj 51 0 obj <> stream xœí]ْܶ}Ÿ¯è§¤;å¦p·+ñ—R^eì9åÍŒGŠGÒhÛq–·|S~/AÀÑl”<8®rhH\w9wC¿Ø”…›Rÿ3_œ?=¹÷ Û\½:oo|:]¼¼:yqÒ•þßx¯ÏŸn>Ýí[õ÷¦¶àþýåô!œëu½¶ëŸzFëý‰ÀFê·ô}Yôí/ì°Àöî¯ìö‰ÿÒt+‰š1¼íFž°ïøˆqOŠ1,Á ÆÐÜ {Y4u£ÈéŤ/öó=ý|íîkÆßÔŠoàsá ÏÈÚÛµbPTÍíÍ•#ô1ݦëñùZmÍßv¢+D-qá™'zÝ«¦S¼yåî€Kºá@ê( Ïݨ§jSjMz·½qw©¸ü¤5_Õ( _; -Õï¹{0'PÍÙ”S —·nÀ ¾Íæ1º{4£<$š¢n&zˆ“]¸À¢Âæ=ÃvÞˆ·†¦·f¢àynA8á7” á‚Òx VOìÍ';ÇU²/¸6l¡lðõð±|`~PM?ì-‰ŽëœýßdßÿÒ4 ¨ù+ºlæUÛ´ÛGÈ&«Ž6;Ùu †tš/ö3cì'¢¹Ãiˆh µñÈÞtÏL˜Å®ÂýN`¥gtˆõ~s£Õ¤âµï'%knC®è[a'¹Î‰6UëiøûN…è Ï€¶´“z¼ù”R¶É´ýèŒãi~»‚‘v¥^{‰ìà1` k£;*©Àê0ëŽK:ï7[Ç*FFZ¥c*œŠ+¬ÂHM9´êÜlNà¾ÙÑfîiDÄ=£@ÎÜ£&+¨•„ËS5¶Õ$óvг‡i|,W·?ëkßN¼¦Ìâi(OÔ§}DQ¿! $º›×΢i5"‡«ý`…•/À û¥ûÙ9C˜{?_yjøDtŠÖ!¶õÄ.£ú¬1xPâi7ð +ºz–p*h¾p ©ˆ Uóþ ¹RÖ²y±âýa°«òØ»Eƒ•‚Ùò-3ß"º<gÚmîqäÄ}btçî§½rRì ?øŒ÷éâ%ö 'lù`ò kó+몸«‰òŽ¢ Ôåؾ%ï‚ÀÎòŸœZk¼åY¶g@š¶„™û»ö%ÕhéÑûo5G1T¥ï½4’Þ¨×7í,ê`,§cyNÈŠv7ðR~¢ 8‹K9Êžcd¾Q¡™2ð®@×,Ïš +Gpåm¨lÂ"a²Içß%2ÚKꆾªg\Ó*ÕÊryþÏĈ;"Õëv*€DŠ7Ð!±+¥E~e¸• ²¹k‚§ÌK`™-¶ ÕVÅö|lF÷¿cmÃLÏz(ÓÂã9ð¯Èœ5Oš®@[4€Ã!(žËŠÏn“™W ùmJee\Ž•—€,Íû¯Òo*‘]o3zSQöåÝZ‰véû—aöÖJDŸ—Âpµ: ñ^ 5…®ª2ȱz¸Ücš1 RÎPXí´!Šä9ô+:9„A¹¥A¶Ø0Uaï«àò6÷y˜0á!IÏ>šH¨(Záp2SQ2ið׬=/º‰ŠRéâe?ª ŒÞiRzÕ $AV6NE_ô¥˜5¯Ò§Æ=¿Â9ÈV.],À«À?gŸ¼øê­eŸ˜»CçÂ*®FÁZÍÓõ±ã7ð²¾ka#‘[yn$G­çœ”%k‰2G„õ{Dך›Ø5@%aÉOкÈDç9UÃ1.G0éúüͯÀªL3Àl7¹½8ý:«3_ùQެ–ã<ŠW³9òHÏ›\&¦•Jà‡w©*Dgub˜À ŒWë6 f\tî×8eÌ?ßiQUmWTÍæËšiÄž½ dKT%ö8“DÈ®Ø%ºãS&ÉåT[Ï;O’ð½@O²Ì³»¢„jš³QлÑc¼çm62$Ä0URê‹aÏÕQpÍËõ›¤e™ŒDÜŸM6éÅR¢y *…‡R™p^;Ô`ÿL+Ža¿8ûXšÜB/•…Ù“Ëœý­ ‹âµ#º"^Ãm,ÿ@Ðo^-M[KpÓÒ­†QÀ5sÕÛÎßòÒyR‰“tîŽÃÂè •Õ>GêxØi`›s¯ÈXùRZVJÑ'ÚEnèeñe |2õÌþŒJÏ$”Væ“ëVUEßWßæ§¢U¸ËùF†é ÷„…j³­c\ÌÅÏÃb >ìe9ÒÑsÈ)ÕfEß§{Nmè€Y•)xFs ¬JMá`I´2AðØ[»ž|a8á'[6Šç"olÍÒ5æA_~Õ5º)ÿá¶¿£ê­…n­i‡t[w»¢î+Ñm5ÿPwõ¸ÓM©ðá óT桪ÝÞs—_îöæÏwïcEd+e#õ—¨mêz—R6m-FŸlžôÌ]¾vo8W–ºle§ Žý»{×3wó Hµë‘9ª jKwNAà“ «CzÙ/žuCuüB}Ç– û„ý¸oÜ;˜+g"Wˆ‚] Ú¥ØÙâqÝ-Ê-ªQÃ÷ÔÒn¥è‹²ªuõ«,ä„þ£Böµlïç&ƒq)yóŠ‚¢Ž# Ú?i '¨T 1c± ³yu>³%ø™Ù„#cAkÖõÀÄAäÜ“¤_ƒ‹p½B#š5ãFWUãô0¹ÚOå¡H1r¹ë¹Žå“k*7Ö´…½Ñ´êô¢pþŒ¼R!Ny#,˜Ò,vìi–9i”U™2e^‹åE)-ñÙÄ ï¾Í†žŸ°X ËaG"ëA®Wc˜¦óvúº ôjÜ›|çjñ°.CäQäÙü‹«÷hE÷U«%+sö‹ƒì³Jp&ûÓ‹Ùþô" ®˜Ë™áþoͶ‹¦„(î6Xyžu7­Íå XRÝV ©Š^ ?ßé^N)Ë‘ÍMðjSc¨¯¸à"O`B£÷¡ynš:³¼óèYNê²È|l*=nïBaŠ—”ŽÊµÏâ “Ûš¬i'Ðg6xGK%)@h0ã{ ÒHî&qC3+ÒÙÚIoU-ÓªÖ•$ÔÏf—hÿ@|v,[”;ÖrêëC€õˆ¦ŒeƒØ¨‰x…º/…®‘—¨H¾G†f™#*ùZN,*•Ú¯LkfŸ)ð» ŒM‰Ûh݆ÿ? wþ%î:gÙT†g±ó'þŽ0³­ñ…Yn]Àa|6ÖÂE˯ÐâcœìQ]©¦àUS3 wzR y‡¡¶°— à“'U²©j(j¹/‘-íóv‘ô¶\¢ §B9'zhÑÄRØaµvtà‘Û²),at©\ÙtÔG5ç9kpÇÑ%(®ŽcäBb¿qà•/ù#¤ÁD*NQà›5ÔŠVQülóýkB¿«‹B‘Y¬3Êc\vRÄ9ަF‘<˾ӂ€Jï,Òyœu•x´â(Uï1MÆë¿¢ÉÂH6*ê¥Ó‡¢e“»YTÊ«rAð­kwõ 7Ía%¶æ H÷{cÖ .ÅâréÊFHÓ8ŸÞK‹¹ÃJ¥wH;±§+§cÓæÈ»¥}•VÈm*¤?& é3»ôæÁ¹Ó´ì_íìÁþ.œ½{#£ãàÅL+zS´¢"ž‡‹…ð•lÓÑ‘¸TT{Ùʪ3; ¥Îì¼¹^®ÎV—™’ô&˜¶&QRH›2˜³ád{¡ÀÎØÏ¢bGr>k©<£,KϬe_ìÑÕš ’¶?º00­qŠð30¼â&Û•6Þ:sz+×e^ÐR!¤K#qúq&´N<ÞÐÚg‹‚mLîšùï\º>wZnM|pTŽøÄ~ ó]YsF#û#×DÈk #òm´Ø'Óë9üSçƒqñNd³ºÈ#UÇ`(?b’XOkØÇÌc%§á ÷ØWîŽî7yd]¥ÁóËNL$Ú8ûp©æI'þ²K«6*Iz»r‚|å0ÅO1ïF'±ò˜<•®‘y¡ÞD`Ùr¥ÂlL‘çd?rÖ£¶ŒrŽ[ìlŽZ쯜ŠÝÍŸ…úD±Hu8}qC®œ^ɉÖÔ·ð.2ªzhp2uL­CHÌ(/tÁL‘ ~ÆÍOoTäÔ~ñ~Ð,·e}(/DÈ- ÈvËâžÛε7aÈU?«p°§@¹ëCëGñÐñXáÙpžT³Ž©Œ»™¿«ÊŒîÀuÊ„"Öœf±gò€s2ß:ÿÜÎ0Wx¬~C [¼˜ûgb9êãc?:ªŸ—8…Þÿýㆉäa)nÈæSÏÐ\=ÊÌ(þµ(Z'þ§¤™Ûñ*Ÿ²±ÕèU•OóÍÅÒ§@­,¸ýh²Ò aÑü§:ìÝÈÜW¥Íÿ´™hÿ§à@×64‡¥“bïÎÕ=2$tCÐ_”€¡ÙSìh"ŒÐ0Ëm¸øeÇè.wÕ½ì¶v(QÛ±²I¡Í4)@“ö@—ÁË,*euÇ&Àyì9ëšž›yÅ€3@÷Ã'º·Eö¢o•30·1\ò `ÞçòV5Óç0ôÓºnç_d¬çdô+}i½›BæÛ¤Ë˜¢bá¥@ÌA¤ƒyˆ§P|”Ìþ¢Up´Ï“À¦ø¬H¿ö$[Hðµþ?,wñäÞäïÛ¢ll±ûºø1³p‰<ìËT4vïÁ`¹¶®‹¶´¦õÚtêõH<æø r¯ýéÁW˸ö­JɃºŽ¤FÍ_÷-ÄÍ»û”…Ž;ŽúÍìDælIóšžî?[‡€f[ d—M &R©ÊF±¬9¦¾¡µ±9=ù“úç¿ø> stream xœÕ]˒ܶÝÏWô.Ý)7M‚à+Ž~%q*.;öØY؉kf$&‘Z#ÍÊSYæò3ù½|ÀAöL¬Š´á°Aà¸÷Ü'ÀW›<+Ä&×ÿ§‹‹'ï~Ùl.oNúÛ›/;^¼¾ý½ê¹3ê¢j+ÕëéÕÉjßB6Û+ûä‹Ý¾¨Õ]µý¾ÿ½yãŒ×1t $—uÖty7õ ”\ÛN^ª® u¿-Õ€Rßíäö`¿µOÝìö örÛ?Ú²S½˜ íp¦—×»}•åy!ºíSKË¥íÐöÒ? ]=µ èû‰íÚênT[QvÛ { = #ê5¸¦q¥ŸR«¯} 3pn xn:…§`ü¿©¦]–7êò»}›3:Ú:0t̉¼Ýi~Šº:o'±/-ïR“ÃÞùcÕE%‘Qìv>6öçgöù36uÎâ›ßù¨¹e&E×lÿ¾+š¬y…cñéÜ¤Ø ._¦ø¯o –´ºÔRJCCÓFiM£7z‰ö¢VÐT5ýP-‡…zjPâͰE]‡’¡Âæ& û|àå¦ɽߩ盼ªp>öw5.Ë\Þ¦]µ©Äæô'êï_迤þãô—ßnÿ´Û—eÖ´­^óB¨«²ª-©O»¤OÀ`¯8W¼ƒ„~™Ëg”Ç¡­BôBj™ïv¿`¾•6=о8a!¤º¼Ð¯ßO—sYåÐãÅ9?;ìJ¥Ëù‘°[Úí=•UV•Òª+C¼¤Lv.qÊhu>c¦­Ã7ä¾1mï( IÅ¥¾JìªÉyBß-£Ë —¿ÑÐSKY·é†~ßXzÏ,ŽM” ÊqÀJ ì`º²®jÑÆ•Þø`Ug+)­fèç‰.…p¦ü@g)©.Ñ3mc†JOµÂ“=5_,hr¨½‹°›y ÎM?ZÀyFß2 i äš Œ†Cþ¤ª„æ6ÌÛ#DžU¹1–¯éúèRÓW¸ô¹Õg»¤À\áË:‡tõ˜M%FmDá,ó!^Yè¹£¼p Œõ¼73e©Ðšâ6¨X\zÖÁ}Àbʺµhá¨>8ÆåGà°ºÿ¥1.¬QtfnrK'”j©•qŠV{÷ž½xºW0sÏ™iã1Õ±šÄã‡þ®òeìÁRÔÛWŒà¨‹Ñ[ØÈÑD¦g â´÷c׊~Å-¹ %Î :¡ëvØ%„Õ/3y)Âø+æÚqŸâ ñ0,·uœya¶#•lˆ7ž0ç]]I펒Ã×F=VjͫҨ`Ãe3ÏÕXàÙÊÞPßP[#²‰ÆE6?FAü¸¡T ºèÀ9ÀÜiý‡r@ðï)²®,ZÇòøçÐ*aì^Å™Òö»-å XËLÙûšŽ*/:oöLßÄsfr¯HîÔë3V4§UžÕ4u×’ˆÂlúýc,c LÃ@Çã»ç²&DIñÄ"¢+3Eþ$7ÀM‘ ¦æ =®(‡äeãsHßVÁ‡›•ˆ5]í´ýÑV2t⇻÷Œî]y"´_ÙªÄ# ¡½‹4̪KM’ãä˜ß5ŸDâ 3!ΡØ62j˜ïâºó5fvp»€Ú ¿òQª5¾û¥6ÅÒfy)µÚ:/Ôsæv…-)¦§œÌÉôÀÈÅŸRû-Æ »îöBÌcKðƒå:ˆŒÄVK4­ŸvRV‘TÒã¦F|Á ‹Ôœ·˜w‚Zº¥£Èè4\+4»§¸ûÜ PµÊE5I]ñ@ç—$èqc;*2Ëð02ƒp .××tx^ˆGø »ñYÏs³+i±‚°cÒ€õø8¤6)ÙÉÀ%w˜\Ž)<¤€\¸é,í8Ó…AYW™¨‡Pä˜[áæÌòÇlºÉ‰3|…Éá0•à…RÐåF:A…BÿZVLÏ®-¼Þ ݲ,´f™n^P*n»Íå÷4vyOï:dòë€%ÊDÈ«Îä’\÷¡‚,à‚:É8YR=D¼àÅ^òã€'ü­ù|²)iø’¾‹X®†)|*Ÿ`ðœ÷\IM'<蚘o qì†x>O%ñô  z&ò‹tBLNWf铿úѺ2¤»zû‰±&ï ÇÄOºÉkÜ{N0 ³¶ƒ&PJgj_xNéœ.eÒ€à9×KdpÆÙím†Fä"ªY‰à뺡†*¨fÖà‘<šìhþ¥q®ê2«ë6p®eE—]Ißq yçf0Ožدßû¯/Fâœä‡ÜÑÝ1dýÚºî©HÊiѲÖz¼B3bÍŒ<, ~¹uÆQ–'„ †ž£ÂÑ,…~NLg”ßA;v8W¸$‘åáŸY(»z/eñ‡ÐÕÜwyV4åàk ¹Þ×ü(•âíÕÀDµp,Ç5Ú0f?ºf(Ïåñ¼J,K°†YÄ["ÝS¦tM>yƒ/Á úÿ•“f¥C,Ý;¨5í9¯ýÍÔ+u Õ§‹3‚ô¹ò\Âô£FQÞg'MdÞÃѦX‰éî)ã:–ðh&¼¢bÉú‡{àÜHã9·Xb’¥v¸Cóã®S_öM‡‚/e”µõÖ]C´t«kŽ®0àÇ4NrŽ©Ê’5å$<)îÓud–Gg~?M*gÃz^LÃÞ9(Ý£‰ô1ÉÚaj¾úÓÌõÙ…×ê ²lY>,ð † Ç‘X ÅÌâWÆ1œB^­B^ýÍuA]3Éz÷#Έ¬m;R4öz§-‹¼˜Š°zõÈÇ]ÆÐ¢æü¶Ö†³5|_yQ ºè°¤å¶k6m­É“ñ¬ýXåi.?µ—·ØÀ-Ö¡*RÞmТWƒ:)s±°€âlýLý®®DÕ8 ’Å×|; -¢âQ¢X=+k;Ù„€^^Ä-Ê$çüñ9·ý¹¥›Ì ®sÔX©·ª!²¼L:t¹ÖÃS|I¿1Ðö™ÛpŒŠ+§,Ø äÌ­êVжXYûgøU`Á¸}ÐË­ ÔÆjÜ*ÍaP®Ñƒ‡$…É”\G&×´üÄHïJ§_•0ÇAË·Â2ÜQ:"Î1Sâ”{Ô(²å vUFÈãe3G–óðm?pÉwðè÷óì'…yp±1ï*'kw³ÌTˆƒNǰr1ÅawÕ`ý|böH(\Á¾5鯣3ÌøÊ<Áës¸8EtÛϦí=ĵýr Ç‚ÑX­C jô¨?ÒÝó\žzõé97˜2⽂Vcûã¼RÕ)»'¦œ Öaœ/ø&1Œ x ¥ëí‚ʸÂÙŽÕÂǤ- DŽ—ty$ÓIé"‡3¿Ô{k¾$¿¤FÜsà0†\‡óÐ2]n£YÝÁÃ'@P5żm,zÁ&3ùr•I,*"¡‡ —߇bìÔ%wI‚ø¹ )Uoóø»VFócv?^j¶1Œ6ì× ˆ(ˆY³ÍÈÏYÓ}d¾ }x œ¿;ʉó:K.ðMŽAÅÕÑžZ WFá­«32¡•ÅúšSè…;<”[Hsy‰—4ïšr{ÐøÁ<|õ¨!ÐtoPŽ{T”ЧêQ‘aÉ—ÊúU_Ûû¿ÕMõÔLÕ0Å%rÒºú8*¿ŒÑXóW½ -?vèQË ø,•6kó‚T ª^ÛKßeðƒ3Ã&Û°ðÅÙ~Ì7,’ŠÜTµàÏ.LüÙþ“ý4¿P&ÇÃ; *k—xAV D@õ¹<÷<—z{ ÖA‰µ&ÎÛ`ŸÔgñH®µG›G ’ñ^ÉsÅ€ù¢i){“Ó;dO)Û¢6vTSZzŸgšs„LìKsâjfo{P1¥Œ¾v££ÕÓÁ€w. ~R ç–=7E§K¯°VjUš­PsÒÉFê.Ë&«d^kðÕ;+Þ<¤)ÊY÷YÿxYkÊÕ5•²æŸÁµó\›‰FÔý!ÓÍç¶‹½üX½`­Ü¡îé*úbÚé)øÂüCAWŸïöe&»†¿è«Ë²DRÃí¶iºi€ºj·?éÛ]UË¢™Z«ÕP<¢f´nZlú:>L×=â–yäüF½ózX§ÁüבªÀ³¾»·!ôÜÜ´ÇŒxnO(ìÌT?2\8£¸pÿÞ¤§„2ƒï1ºŽü¹X¨€…Šý‘út6ÂŽi{f*%ÎàA{n/8äLµjEÉèypyXl{ì™ÃH³1„äAœ-x†Ù(I#‘W=B’{c…[áE+¬œZA$ñ¯©±l*ÞX+é~`¥&æÖ÷  Gô‹Æ2}s&8•ÈÌÓjHà\œ¿êŽæwbäNÉë9ež£O—bvË-«ŽN&Ö’6/¸KV—&Ïôãmy¿±‚ar$êÃÃ<†¹Æä›²è ®öö(ª85Ó‘©WÖç;HüxæÀˆOŒ¸»²“ çù(ò÷ÎL ì)?çÎ…>ÁÌ9a Ûiòh¶ÍÕú©ÝôHÔ@K{U¦"\™Þ½ÄcE\Ï'σ`~3W¡üLϧ¦x,/áTžA1´¼öÙ²×òGfP’@¼¦%V-GyI ˆÏ8 “² îª?ÂÖÜéƒÔèª=hÙ/K˽ÊxH{z =0@dÏ _tÖ9JMüDŠˆýú1[,`¶TH#—°dm…“Ô¹èKÆCIÿšÜj|Bç볉Ú@.ÍFˆ(2i£ò©Âv’ðŽ7çLq¿ã¶¸»¹‘£wù'R*4&Ï]/žHæÿæŒ)êý´5“p[“8SQ[aXpI‚Ö?3g“n¹ öfLÀ,>@Aýì y3‘ËTá+ªU:úcC©íÂs Vƒ©§˜ŸÀtá‘A§b ‚ûŒÈ×̽ào ƒA¢¶—,Ðâ nÌéx •0ógg넹KC4|°X =V%€Ï1K‚¯Ó3væ<Éa¥~¤ZNV28YdÖ_°’}o!ôåëZÆ7VIÄE›)µSq~ÚgRêÉaìm .s¨'Pß‘lèoº@Ýóx(÷•ùq¨k`ób üYn?ÏÃçò÷`jóа8¯Èæ&Qj»?°žGXx¥./¹›?´;Fà/0=2Ç ^ ª ^-ØàK÷Ÿ KûxbßËTPj´ÕÕ–:±Iz6Ä% >,A3«•*ä_Y䏸8² j¾oÜ(9Œí­³v58¾9Ñj’ðÜ ’±¬ß«“òóQÍúÕÑ‚€yHæ5èÇ ’v M$\ûÄÌ[ ò&òÎÖˆ6:»3¾üœÉèSü1V9ÈK`ÒøÆ>=ÐoòsAâ€>=G©CžS˜bzaÇ?,ýäÁÇ´@‰¥CpŨ´èázÅÜÀ4Óß@r޽Z*ìÕIáå5À$Ò²{§Í‰¾?þ¸™ç”ù¸óóIÝÒ2ß÷FR'‚®8pMðà¡éböSÛë»öîç»}Õ²j¸Kö,»7ÖìäãÆ>cs”­ 6ʲ EPGÒo#™VæoØÏ??áCÓÏ8«`.ù*,[|oŸ=r' &6o3Œù–ºïcn‡—œ ~…SÊÍÚòsp’É”+f\ð‰N8Mk¸hwMî1y03L˜?¤ él¡aÔD&÷Èo¶%«,œºMåfܽM¦LÒßE²^ |"òšÈ¾#cS4õ96?>1áKåœRã}Jˆ{–«J§ÙÒÁÄÛmO±i¢[ >ñäóxÉ$¥¼ Ä‘©.Ž í¦#5úÔN»+:áló}µËön~.:”Só¿`W›=Z,iTš·‰Õ×ÑĆ´·ß»²žþ'§'Tÿÿ ‡ž6‡endstream endobj 57 0 obj 4898 endobj 61 0 obj <> stream xœÅ]Ëvä¶Ýë+z—î7‡ð•¬b;‰ã8‰3V’…“£‘dâif4Û¿‘ŸÉï HภH¶Û^Ð-Ä£êVÕ­øv—gE¹ËÕ¿ÓÅåë³gÏÛÝÍÃÙðóîùïÇ‹w7goϺ¬Rÿ ?àõåëÝÇçòÆ¢ØõYßìο=˳¾ïòV?µØuò÷¾¯wm‘g­lðúl¿;œÿû¬(2±;ÿâìü—_ï?>TYÞôBìï²UQŠvÿ^^UuÓäÕþåá([Wu/ï4?^™–×㟻jÿáPgy›×õþö 䟫^ì/GÕFµ®Ê¼Ý?¨ÿß ÷´…è÷öOwöòÊ>^úˆmMû.èôø„¹õ;l:'šÛ÷Ãó ›ð+Œ+³¿Âèô«¦nöŸŽeVvMßSÖôòzjFÕ”ey^V½?qã¯ð‚{ÛངÛÞŽò yQö8pg ÿ<ÿü¬¬òLtBÊÌùÕÙ×8÷ö.X Ý¡l;uã³çUn„¯hÔÊé)ðnèѽ´c†Ñ½·£ããx8Ôy®ß ‚_u™h…y÷Gt=¾?ôYÛ yõ’õfþ%•@hpO×ODÕœ)4ݺUUþ–.ò»C«T¬èö¯¥< y]á´/å]Ù„®¾° à¶G|ƒîWÛ BÐJ|iÕbˮΚ¶Ø©?×¥^F,bLWžÂ©õmšPí4 1©pä”M4ú2ª [ç+Ú`ßLGî|rq”ÓÏ®EDûn®_—t½’·ö™¨òzÿã¡h³BÈ+®ÐMGÌ»©¤9“læL2¬Ø7E™5ý¤«ŽòsÈM.¾éb°ø½\¯ØÚ³gÍYÏ4BÇ3:pù7‹ÿ+ËǸêÍ]%ÿ[ðu—•e=-¨ ãájàÞ[<â/ò¬©ºÊEø¡½ DBP]9@ô½žý¶-ˆ\-ÑÇþzŒðVÕ843'G½2'ð)}ÕG¿©gû¬ í=REm'Ï5—†ƒ<«óšÌÁ·CA”3=q\Šùþó0ÎÃÛÂÛà°\pÛϬFB¶NñÀ…ËÆ9NkÉÕß#0±Aì¥ÿî‡^Uð ÃÿßT ÜÕ[Zű*—•ƒÞFŸÏ¾–Gܘ‹¹ìëaÞì¶]T`Ù A¹^ÑÛ8pr)½Õ^d}mXhzƒ³Àsݶ‘q›iÐß@|þ¤aæ¾ù5íŒCq%ì9÷R¯S«î8î £³ä¡¡wMçÞíWYe¢ì[ˆU×…—®å×ëÉUtÇjù û³¥!@¡n³¾Pú†¢ÝS{xK`/=ÜHÎîè(Ty1ººã\|¡˜à_ ð7{;Ô?Û@‰²Á/EîW†LäOòRþXÖZ>“m…—>¢Ì€"vF¾³0Å-¾"òføù=ÊNÔGÚ±²fÅ|VZyŒÐ p'¥IQ¶Ì)Êôn»§ÓvEàÃHÒÈÈ;6&ZLPs2ü¹žtáŽPÚ }ULèNc#Œ‘ÐŽ7Î7ãËÌ<¼â‡”GæÌ×Ô󤛿‡jy/Õ+ª9: ÁQgŒ,ëãA¤•ÿ6x¢L/WtbøŠq’}6eÃÓ|¦6¹Ú`Ôc ¯úUå–,x'%öƒk ‚8m ¼dß å–Ý“æV¸gÏÇaÂ4`ÔY_5„Ö@øæ0>a˜öQÆô}ÖLë$¥²M¿;EÖ5…÷Lî-R·özcî[Í€¹Ç`+.²Ôæ6ÍY77joåF m«ùû`áFÞã3kÖ’Þ1ধ˜9Hb~¿=d.íßÕ€%Vr”Ú?ïåðJ’¥ä ô$_ö/C7³( ¢5™:LÂôºÅã‚ Iäe¹ï#ñò©[¤DNÛÂ\Uªp¡r̶ܻÝá%اgÏ{“8Nâ¶êÁ Á­#,B©IÞíÿeþ~eÆUQ;¥°– ¸uÝ©ŠºÊúNDPŸu² GÚVË™+aÌòòŒeÌË<•‰KIQõ¡’a‰üº~Ž˜ï¥ênU`'oMQ°bƒZâpd}7«ÅòµÞàêÿ\ŽðœgäRuÎ:p)bplâŠ<4x´š|g/o-‚¼?T•”’¼ SŒEVý˜bÔNÈQyÖUUá¸6^µB°Ê^.!XdÏ—™Ñçäê®­JæÎåST9OSFìôƒç½€¾È tâé §bädj*é¯sOž Ä6j±ÞŒÄÈWt:Rå?A·1UJD3 ‘WqyÖ=.x¬Œ¶ª,0î½ !ѽ/ 1¶h¡åï(uæJÆ-ýxP^BQ9.28Ã’é4oR8>_à³I%$P-¶€ÁÉÙUy`^Ÿ“d¸¡Ç¿1Ô/N@î–‘ˆIê‚ÛäXšªñ–Nï$õîúMø3S@Puœ e isÁc¿™"°1ºÃ)t1gº¦8ŸkêdÛª6Ø‘÷M-N±m p㨵YŒÞÌ0ŠU»Z­Wµ:p×ÍQjmƒ'«àwNCì>bê웺5ÉÎ…<”Q­$ÅË’æXËA׿õ±—l»¡# ü&ÅLТ>^/l—{®^+¾k'™rpâ4‘ž~×_³jÆæÎÀ]$éÀ¦1ù>'ñÏ•3Ť„ i/ .䙽üËáØd¨ÛÂûÖ>—£?÷æÖlñ ð›©|Ò‹Žà°¼ª‘EÆjù~K¿pI†WÆo4$«§ÀW¼ˆ-=ëoÊïXPeŸð¸9q ä0˜›ìÇ—)ÊŠT?F­Ý4´@¾Õ†ˆd\ÉvY$ -y|§%Æn¥ë…ãþIŸ"s:pŠl™¶g÷Þîl‘·ƒ–¾–Ó^ «Ù*×?SŽC-'¨Ý—øk›‰®*Ú}!;Ò‹V eKG²¯öŸÊ·7eY—ƒ&L7}¡EÞ”­ª©“ˆÐvš¥‘Ý©Q8Mwã[«f\âHÓÂmr&º¬‘QÃwöþèUÀ¼D¶§W}9N€e0žŒöi\û—`ËÆ@1aÇòÐ%å-ñÊQÚ˘m2á¶éTjîiKjëÆ6/†f,8(Ž•ÙiSøA•1YúµÂ% ¶«Â Æ©éï/S2Eâ|SmrãHì‚]¡÷ÏV°™ŸO[9æÊì¶Ð …~ÄV}ßš+› µœXÂGi/}¾Üê”rjÂF&eØ)Stë¼M’`3õSu–š¡D²ö1ð¡Rfø5¨ó……'_ï|T ]¥õEQŽ£Ã’*ëÚ’°‘ü.#Âò{[åyÀÁ·ZðLß–˜ŒCSõÔY·U5C]ÚñÁTÔpÎxM¤³ªf‡d¯‚ Ò Ou(B7vÑ’bI ÊL‚.Ž=ì¦c1F·À¦ÍžmŸ Qe“X0ÁêãEÛéXâŽÊß4å9* eÃ_À¹x¯¹ój|÷‡Tœ^2GÀ.Ñ Ú¿±¢Èм w"Ä ÌÒôøXZA8÷kjZTlœVÐÆ‰B˜ã{zÛºÐe`F¥ýM34‹ý2¾g!ªSy'7.£4Ò|À%Râ.@hq@€°û¶Ð‹}ÄÃeX¯9e›¥‚ZÕ¡E ¿r³ègNý”ñ}0G^ 9<‹ï‘²>D(÷te@ÀMÁ~é ùšR‡$…l¢¦ë SÒ¼=ñ|в\ "¼žDE¦Ivì!Žé>;”¿%¤köW¡>¬8‚׊úgCh–3šž@ÅOŒèõFx gJ±]à1®wŠG@ìynî 5'Ážh0S,I7Ù8&£#'Gd¢ïëý?ä“å4UEçø9Ò8ˆ²›ÆñÂãŽ}¸¤<'”î;Ûc¬Y‘UmEk™6¸¸ GM%„ãx Vî(R°¼X¾Ÿš—ƒÆÂÂù°ïJ:2I¯*)k6ËnI³vœªLõÚšZ¨m[½Ý´é\g•\ÿ"BÅ(3žäh`~%?xîØÉ¯z%YžÊ¯Ù®˜~¥í¿F½˜OƒóÎ 2áÒ8®¯ôn.«ìÞœŸ ž.uÁMi¸à=@[R×lÑxŽOtpH$¨Yï¸:Ü´¬—=«÷‰Ã_虓©zï™^ÜÁö÷cðE\Sÿ3§•³q¢Að‚ù+'$¶^ WAü)&µï¹1ž”;tí(ÄkÎJf÷Ó™+ö[°%­Zå4mÌÉŸ‰ƒ5¶¸Söô˜AJ‹sòxê€ïstJvýlÑ)M`…õ Ù‚‚Måß`³ €nˆPÒ«Y¢ñôŸMÚ;WˆØ¡‡;Ô>%Tã4°ŠÉU[JØ”5›`8´,É„MAa€mqké¥XS\´¿™­prÕ¯:è)YÖþœ¾W|EZ"Ÿ,¡IáH’"þ ΑGzzÞ¦8NµA±±ènùþ·ä©§û€|ä}V•伜§ñבÊ\§6ñt×¾8¦’ùøzÕœÆ'mE¦:al ‚­mÎöÅqY-ìƒí‹r ªJÈH{¬½´R&ÏéÁHU¶ 9½ôݰd2|þVÝáŠÒ¡ªÞÓÕ]ÖÔ8è(EûË5»ÇT›HgÏà’ ´?Fæ¯þY&MŠ”È´ LÝ/SY?SÞÁáe_Ø»þ›¡‰Ž°=Ö„[vzMý#?oNŒ5>­/…b8¾×Œµ°w7T›ŸfŽÏmxRÈ3åi5þ)Äp(Ûº\O”M6„8LuŸJ_‡Ð AÏֳ޸D¡{¤1~v'x9>w€V³Á1*‘ÐæAgÛ™/:JÛ5íÄ©˺Àˆ'<>p¶2yxô™ÄwcŠMd¢6‘ø!À9™Y²•àØf²•õÌ+ؚ٘^6Ctò¼ìo¨OÀùIç‹R0ñÓ~£ŠžGÀýTß æ^4Ý‹ #…r+€ Ï|ý‘=IwÍV"ç[UŽeJi§ØA‰u¥žI`¸U‰õ<¸¶ëð#q㚣›°HÙÁ©ó"šÄ‹¥WÈÎ,Ä, ƒaŒÿN8õYv”rdO€¢»ŠÖœ@êÔcnŠ’šÉS ap¼@ǰö)ÉÛÃR@â/MN¦lm`ÁætÑ–ÂÁØö-¼ù½ú?•cEÁá?òGyW4Pä¡ ÿ< ‹ à&.´ÎjfÌ}ùl`HrˆRøG@ºI¹æ<çb1b²CxÚ¢ó…,n?YJÅóG÷Ÿ{×.;8Ÿ$æ…×벩¦3ó{¨#ä ø˜vöŽOr á ®žûBIbÎ…#@!,$³Â¼NŽó É4Ï! ¯Ð'À—«m.Žªò” û$Äâz¤Ü›.Z¸‡….Ê“ƒ¡£p!ž`3-ª›9¥uá÷@h)Q"fW ó0€—ÆD¾Îb~LŸ 05º ÆeI¹é%6hœM± ï£åç¤qÖ÷ÊTúuösÔ÷8ñ¥_ÐÓ›'}Ð+¾µšûÏÇO|?ß7Üš­KžscôQh¦Š÷€š–Žeõ+•H oa½à!™Ãaae£¹^qHó ú!í[bP…)¥¹hZŸŸí`<)Æ"‘8<9Y+NЊ•sÌŽ‡ƒÞz>²C“V}±9ÚÍ*Ìñ¶Á§à®­_“dKÜ8µÆ.¾ß‰Ÿœá}U+ vCS/‡‡,‰8Û'}µ¥s¶NÕa³g»¨Ìˆ¡>ý“–öækná8Ö/Ç^1/ÃN{hÓxèñóÌWQ†3ùNþ)”$íçl1ðPÙKzÄN/ŠÛ>élE)—ö5¢²q_/3q?\ƒK— R'9âñ>6ý•ñ¦é Ü  ÕYûiù¢%F€¬Þ¥-ÎmZ¶,)÷Ý€eXÎòïE¸“fþi*‹Â·TDd»"·³ÜDp ˆ¥$æõzŽ´ñNÄç`Ÿ> stream xœå]Ûr%7}÷W^àt˜ÓÓ­Kw+y"@QPÂÄ„‡„Jylc˜±=vì~þ…ßCê‹´%-µºÏ±“I¨¢ç¸oºìµ×^{KývS•5ÛTæ¿ÓÁ難ç/ÚÍÅÝQÿóæÅïÆƒÛ‹£·G]ÉÍúèñé›ÍÇÇúºިR5›ãWGU©TWµÃ]ëM§WJnÚº*[}›£/¶oŠeÕòv{RèŸk&Úí•=º,„>âJloìo÷öèµýëp-gU»ý¦ØÕµþ‡Päê«þG.;¾½p§nô!—MSq}•=üÚënpWì¼ó_ëëãV²ð$r'rþ—Ûb'˪ª™ÚžšÓÈ»f»ž»¿Ÿ¹î¨û´õߨž{N[g;‚<ì4xÇËá‘jûPè÷kTx{â[÷÷ðy÷îòë—…kx »fSüýøGLO Q5z¢Ÿéyñ‰»yyÓÁú^Œ+ÚÝkµ*¯äö_EÝ–µÐGÏèÈŒ[;\®¤¤orãžtëÞúÚýúàž?Œº™…g°ÝäUÉN`×⇑þôÆKX+AÏ%·}åÚ} [FÎ=u¯ëÍ&ûˆS;=þÙw·6Y彌½ØÂ—álÆæö‡Ã¤`)˜±aưRÈiœ ¨ÐTõv×ÿy'+;)7æ¹’ g]Á;õ¾‚hD®‘Ùé”3h—dܾ3·!ÿîí”Zl²%RÝŽÞ†4îób§ÌhWlûGýkÕtªªÝqv4úVÝÈÎû7s]ñ*Ä[ÓSç¹–ˆµ»×µ›„Wð^ߪl;!”ƒn¯k½«ú™Ãë²êìÔÁ}ùó.Ó$”õø€qÂaûEƒGžp û€’;ÜúC3ž0*×ï0´l75ÍMzý—ãéŠÍñ'GÇ|±ýnDɺ¶µÚê¦!c{ê~tgÎõ‘¹ˆ¸tê€3øvµdá©€}Þ·C+;!±Ý~zÕ°EЦKÀë_ÝþäïN8Ö÷jô…ª¦—aFÆ÷N r.žä%ŸÁé–õS§ƒq°¶d¼›Œƒ<Á™ÁKè"†j4ZÇ8,Üç¹–çAÁž*ܯí°Ã»‰ªaÔ&³ï˜rö„x³Ú öu`+#¸‰•µÝ`øã°aoçØ³³c2²ŽzC‹Æg¶0lA7ð„hÆ ôßžŠÙnÔš‘Z[W: ¯-ɽ.0 ‰’Ï«„¨Ô¨ŽžàFzlô² #ÆÔ–­˜,ûšÎ(d™køã ´}Ò“¤ ´µÈ2×0¨›h¬SÜ"ËÍq@…#˃Ûx Û¸§¥ÈÔð´¶#,úvˆ‘Vçí%Ã4¡HòÖ„ãÍçä·‰+»ßR±ÝTüjÏýµ>W˜¨í§7qˆÔÿ†¶£¨+:”!GnrÂ=MŽÉ+–®€7䙉Rl…¬“um­z4uÔ$=牬ª„Ó[O Q ¥äöoÅŽsÝ ib'BsÍÆÒèã>¤'gû¨KõêoŒ´¤â¼ñ„Ô0ÌèÈ0=£Ã4zÿç/”ÝvÓ°ìÎo†æÎšå¥k€&ÂØOÕm¿"DAÿØÃzo f~ìÿ?šê)ìÁ;×Cwîׯà¹Í®Üs¿)8çC«ˆ–¨X©¸bÓdÃê ¹!‰B<GŸÚVR&<÷jèÅð’@äY±öCˆ_ÖC­ó ‹…?û†o)˜¢§bá3Þ”CGBTÄМækŽR ÛÄ4eÃ[7[lkÎhìý]\z\кÊLäB:†ÄF)]1Šœ®W:f¹¡‡ÖÇÿÊ8ƶêƒMêÍy­JÙ6=4ÂË<'¾—àÉ÷öµG–%æÀk?ÔÇ!W(’_ù󙉻žÿ^Ü008X¨ Å||á'+Ö8{ÊJË”¥!=–ôh òž;è`áå‚tLm”ò¹_ÑDp29]'Ú¯# XRAaŽØ0Eýkz.¤3“?YʘYš0{,~\Jã#‘!£×™‰yìþ.ˆ“½ A7ã™OàcÈ ªÿïšHf‰DÝ•F(a&áÙ÷ËÿŠZ3!Á¦«<æ1]PE}øUð¦-< Ó“¿e6,'êÒõTDÖFâIò3´ƒ]ÜoŸMx× <÷¼²‰ZßT%ãM xMi.ÖBÏ)|ÐSLÁë„j†ÒTj„Jd®q÷+ƒ9\ÓSIÍŒºø5º/æ1 Ì(¦r)Q EŽH=†^B¬wPšt¤Ø"¶~ã¬üº>J$™†§Ê”Ê“s4^}wk8¬A¬?¦'-éZČʺ«=6›Ÿ0ñ!H žŒ’fFÐ éDìåðÊ erL!޳v¿ ¢Ëú„Öºœ™-¤ƒ„§¡`nßš²ôÁPºR k(«dT«BöàVaꉉR/K ½Ák4îÐc¹S?‚/àa у°÷ &IS÷vb_"†’+’pŒÆ× $ƒbÓwA°“ q‰àÑÆžå6áÑÐ!ñ뇳å{ØŠ P'|ƒœ÷„Ò¦Œ'tʇ!l“ ˆ"«©ôÆ]u†®ôÙÁÝæQ?äA†˜ÞÍäl¤‚@;Ⱥ¢àútH ¶àAÅ5H8ê¼€ý“’IQR.¥Dyf> ‘R½!¨ÛÓèÒu!QR2¨U ¼1ÃWüd¡SïDTƺõk÷¤R”u%øS†ÖÂ,p^—u¤Î‡Ž×äÆrÃ=¥p/äHÑÑ¥!As®JYÙtØ^~-•Ð&Úvm³^`‘ *.—äŠMpÓv $‹güö5L;²‰äV [‹ÜªRÃ~a&œ#nkf÷ž¡ ®Þ!]ñtÞmˆ{4ŠK‹±{×?]Ö—÷ [ê1þ´ž–5’1ã®=© ùœ@ò¯ž:HLNÉyu0¡ʲÖðð.é€ï°SÌ€Oº‚,Ž)pÀL„ŒY,œ«ãZ/¤a°œÊô«N¹Žú‰OR å“çà=“çjCÇÙ4ÌS³.ážÀß{÷tx…æc&s²¤0[âL&G\³A„ÉåÖ—Ë¿&`î—ú2ÙÖ:àªÝ¹ŽÖ`(ôyf6h €kË.q(üÑ i 8-SR³j4ÎÈvMV8E|ìåO7 ‘ž°…S³ÐöÑ¢Yƒ‘@'‚Ò¹ÂdHuÖð“38$^ºO?¢*œ•ã¶ÅÑ.vþY¶Šbö=–L÷LÆâÕÙa̧¾*äSA™ñ‰¨,[€ ®*+Ý_{BÝÔÅ®€6 zÓ 0èI^òީՠgó¹KP`„zý2Ç÷õ@~’d1ƬU© ämÎàp æMTû´—Â-/;ŸPH¼^#*­(QL¡<¨.ña~RuÓ+Û(Øéc.<°»ó×!Ç¢‡F’dñXôiˆŸ$,„àŠC< RX®°­Û”’B]ŒèÜ;<Ñ—¨k?Vií-L]»_ïÝavå‚lJÖtyê:­L ³7¥²×ôv?¼ìõ. ùl1‹—ðÙL-­ñš(,gÚ0ûÜy'żnvP&!¬:u˜Lý’ÓÈ 3‚é7Tˆº…yÞˆÆÂ ×ð¢AûŸxúúÝ]uÅu›TNõwyÛ˜†ªµ$ôRì÷"¡ëÒØóûÙ¸ØÙÞ~še¶p g s£Lè5kªü­×Ý{¥Û*‹*l9×t„{Iè¬Âmv¹ Þ‘`Ø,•q^@eQÖÿzƒco†O¸DÜž˜îŽÔÂ; ádÕšu+ï†âŒ®T¬²Á¢:úÐU’‹®!^`ëaf¢Ýè®k !F¥ݰ5û-‡#PÕïYÊý JÚÞXOޝ¼IÔžu¥àÌ:ÜÔšüENÆCPiœ3ë(STA¥BOžéýbÇvaè—©æÿðºXßå—™L7»}Wzi, §C?›Z¯0íåÂDy0? 8‡ë¾ðè¥&¨ýu.§nö—ÜÙgRÀgÊЪ’Õ*Å0{ËVoÍmϬœ\Ø¿oç{Ö kh¹Ø¦f,ã‰3t<Ïð2»Ïì–iSpŸ’U-©V´¨ (°¦ Žxå,Ã’gÍ* þÿ*C´Ô°ñÀØ[™²\Ûsëëõå´ÃxЩԀôB,5J„ bå,¼r]ˆ×ëì—å#Q7˜t1õhCÿ÷Ñ„ial l¬}Ü?˜÷Õše;¥™ RF/›»_ÛRt¼6?j­è§³¬¸‘Éõ$Ö·bL23;ÚnØïlº\Obý²u¿ñ©=ÞŒ㠽ʼn»ÐÝíÒ½t6ì€kúÇé5߸›^¢—¹Û- L‰Í–Y'‡¾ÚNû@|6‚ŒÓ<ÞŽsdrÿ1höPjÕä5“\Jî—Š6|u§ÜdKxùaØb³Äž’$޲‡IØ`ô\°ÇâŽMYsé8÷ˆn‘ʘÞãW+® ?í¯ß`§a\9…Q7«1ýÆ9\ãàŒ´×9áP/uÂŽ+^Xe³¢È¡Ha÷CžF—r¤e´&²)ŒS'ôïek”kºt‘ÌSô 8·oÀ‰IMn²2h‰]U3>#—[ WL†¸“È[8•Ùm•­…Ç5Qß Å†Nu\Q'uOÓð@êÎûÎ(1ÎÎFºW †ªRTvaò|ƒlÅÛŒ?€‡~Ýï¤ Žz´AXíEwWŽlgÅKÚû(/×èy«±L^Vä{c߃Áy³_FØkÜ”çÀ¯ž‡…·–´kIör”w`ÓŸpNeæ§Ú¹Äž½=JQλ¡9—iV dSdZ¥¦Ý4&^!‚çýt,k?ôô‡w` H¶¨›Û3$ô@X‰z¬°w×úï4áÖlÖ}rOžÇ6w€§¶C‡“©’ ͼ"¨ßi—€Jº©µÊªŠý…¦£ 'I•Ûm»U;Sc‘ù’N,Çc`Ì×4ö¬ªÂ;h,ŒF—æ¢ñû¢@:šv ª+»\ r`ƒôõ¾ð r‚|öa/‹¦®c~Ñ’O+-`¤[²õöéœJ ³DÈî‡×‡c± cµ·w̼®kï¼[[É/ìá;µ0iürŒ)׫¬Ù`â0Ýkd JÂp.‚ÑWßå•ò‹¹ ª¤@ÐO3òR42& ,Ÿ[c²l™+± nw¸CK’Ñu MÌi:4R°Wþþ*çcµ7OIeüy¼\5¼.iþ‚À¬®ÏíÐìtÉ’¶h—œ^ïOqÉ=ÑÖ>"V L~¯R*rç5ùˆÙòÜÕçv!Åð­;Åd›¢æáÆ{éÊR𜎠ûïôó[K„òš0ç%²©<¤ïž¬ˆ4‰L;oÖÐGâ4 Óàp…É¡_2ú0KZ­(LéöSò€‹Ú)Šq¬‡ÓÁ)äE_6áMUvuã¹ßåŸ6Á¶pG'Ž÷o Þ©xºfï´ÓÄ—!x†mÃvƒäØ!2¥¿˜+PePÚÛDŠE®œ>«†dwì¿¥*=º wþ‚šk`^ÈŠºç†=V1øY®©"Úõz™¥2»~!fÏdw°—V¬Ø\ ´A}&'NLÌ3U™gÌPæb¢žÂ/๞DØt¥h»Nln{&‹åçÃè×®¥¿¸œ##?XØ4eÍZé˜QsÓ03‰ ð|&1cv‡Í½…„lÊ ¯ˆ Ä>Ϩ=«t²›ÿ’—ïýÔuƒÙQ¶´ÓP%˜jñFÇ hÌ€úšmD<Ï7ˆ¦lä¬xð= ]’í[cz`ç)Ò|/CF ÕËð#JSTöãS]M ~x‰ kô¡[Ø·5_<¡‘ÒÆ|·OèáÛç®/Ü>´q^}‘–á9åUè¾Aoñ|¾AAC\GápýËma€­ª»íÏÜ w$I/=XR–}Sû)&ÉüÄ\BBý¹ MO]×RñÇþH^#µ±¬«ã° ¦Šb¢vÀ‹¶@ùBºý1¢Ç×,‹º¦¯J¤e‡/PjìRö3o ;)ðÔɺ½tí!c¾CE³œtyƒ\n<·í¢œžLž…‹§ˆÏ´(ïüβ箤ÊÁÍä›H-=þ¨R9¿=>ú‹þïÿ3Öº‚endstream endobj 67 0 obj 4937 endobj 71 0 obj <> stream xœÝ]Û–µ}Ÿ¯è§¤›E—«$Õ ž @€EHb&y,¯¹àñ$ö´™±ÍåCò+ù½Hu‘¶¤­VÕŒ„åU®®*ÝÎÙgŸ‹äï6eQ‰Miþ›/.^œ=)‹¾ïÊvüjµéôý¾¯7mU­~àÅÉ×ÛÍn/¥’…RÛowúv%EÙn_éKY7M)·»}»-ƿ˲Ò/üãôó“ª*j±9ýâDÿý÷'§ïxzäÞ~g'ô7E§ôïþ'Ÿï”i­WÛƒkwøå_»}ÕºÛÞKwæ'èâín_eY‰~ûf§¯Ú²®·×ö£ÁãÖ§GdXÊŽÊŽ1ÞÐ×Ò‹Ý^(ݾl·O\{gÁ `FÝ«®ãgtîyÿ¿Ùê jÍuÛßídQ6½Rá¤Â$B¯^»Ëwé:m¿«?PéÛªVáZÿ¡o´µ°ƒ¯+lÖæÕ®…]5tP߲ߞ»GŸøó4=Ó1I¨ß fcn ¾à^›gBß|BgùÚ> ïÃ[wîî“AJD]Me´îôR«ÐK÷U.ŸÐ,|k|¶©j…íè ]ºËw]Ç}•š»KGß‚¾Ù¹N¾?|LêÿOº°Ÿ‡¹ŸáÒ¨Åé?u8ÕŸnL÷ÅöÙð=ÓÞ·ö zóà~ÖW6r»G©!ÚŠÆã^¸â¾?È6üòÚ_6ü»×« pùãNkh¥D ;sûñ ~Þ!‹ —É™°Š0tª§#–sx¿tϾOåí0*‚~U «ÐÃg9Ø8x¡Z›í-hôëŒ6ÆÅ%X_O-ÌíS×óCbhöY€†C¸¼f”vqÝ Ÿ—u'·WؾmôÌëö<“À•5ª:ÌÜ3ÚÕÃÔ˜Vúá5ý©J`Wùmv¢m5—hGœ˜¤ÀáÄר«¯­Ö?gvÆ©ÂKò ¬ÆFùŒ‡š›Z;‡®Q˜-®`·ÎØ¢¹¶‹ô<‰Ë@L5]4”‘n©N´¬N³CËéú€Ò5¥*z1R:9|­­tk…n­¨µ€µ[wÛBu²jõ£ú5Õ*£ÛûºÔô¦—ÛϦ—d£5À¾ô|§W·íz^óÕûù ¾d{œá¡µ¹:=–™}óU¸HÓcówK‡þ¤oê+Q·-_xXˆ—¡j’Ç —œPÎv á05™ÊF3S0‹·;#‰eÕ%ˆ`•pNïR,B©sªažµ²ß«Öj0;B“¶žÍW|OjfZ¹ßKzù©~K™TÛŸŒ¶7}¹Y¶Ã°:á’39ψÚåärç½f»Àm/ôqÐ’¶ì+­%{=5e)cu–k1Â@3è;Êd½èF[1-ÚŠ#:ÉŒ¹·F#)×Atß¿†+M_eú¨uÉLD¶ UÓ¤O!…,ö­ïw}Ñv²f}„t¡±Ÿ±Ñ”ª0îKÍ Ò–}+ð £·ÀôhðH2»¢bÖvΈ¯8ÚVoŽÌ …¿eh=Ð8ŽS%¡áÈÂéù5Ó7øývÀ^¾t}‰\ÖÁ ÌG´ Ü|Ѹ÷C`"Ó¶:.¨lê&¢˜£ l„ŽLGÕ4žè§œá^^c‘¨âóUÊFà ‰Ë¯‡<ìÒsȳa(¦G’ö½‘>K²ODì½ ŠG×ËP¨Ž°ê‹ÄÇî§¥#)h‹Nö3L ¦MÁ$3I«5fF@÷÷iÿsís¦uC!3åWg ëzæRðsÊyy%ñ‚0x… …¬ðdÍQ؇GÜ2¥ÝÓI0ö£{a„ƒÂ4à\Vçe¼ ×ÖG¾rçÇmpŒ"KZÁ&võèqo]®N’Öj޹ǯ==W…躲Ã%ö&H;rµäÁ—0ß3ØdLx•S5ÿþ6r ‹Ãõ€«²tèÝ‹¢uV*‘¿«Ë2Æ}QˆF6 ýø8ñްV±t æ•¿pÈ;¯q˜åÁ—KºöœvÃTrÏ”"B!Fq/L̹÷c9÷47Î4¾ÂÞZ±¿µ9û7î¦ÇœlAHê®Ðˆ I†˜e)yÖ­ö’,ìÙ[4ã‹UÖ–ê5]Ös*6‰lM;T½ý›»üØæˆS¥-LŠ’-sqÀ÷®… ¤(Åö‚œat†8±«;à賬(†ô“5^måe á,Þz(T¹B c¤7ªBÝËÆt®ŽJÔPÕM5Á¨ß##H³Òà÷·¯áì; 6†óó–¬pšN\_=f‘ó„3*`ÏŽÓ ÅTI otql;JäŽvñ6n‰>q³ù ”|á>ð•ë8£Ã/yX%¿¿²Õr}©¬C'ÙÀR|ÐÖnÁÚ:â—qêfUÅ]½òæ­È¸$û…‹’»0¸ ‡ØFeíÁYð5ž0aî‰yÑ>‚Ä^ƒèKO[SsH3k j1^PëÏCj4¨’JãOŸ3¥Ô´ˆtÌZ_ºÒÕ B&ðh5y«Ø$ù«l ‰¸û%¼¾²úþ‡V›¾Ûþ@€æ)—Wëpø"µ Ì8æ]?ý5.bÂûrú¿¤ûÒ^mè”r÷ [!Ëpù^hô+Ü£ñ $î/ŽÇh¨—!´Ž¶BQ/ÃÇî¨4—± ÇÔŽq–¦,ªªî39&Í+†Š¸¾ß~é(Äk€ŽD›lŒç*ÌÓ& œ'˜«ýÝËr1)DK’-É-¤xšƒ=´å¸çÀ]¿!W†™#†-ïr¦-WU'¼ì;j–¤ÍFSiåj]ÅƒŠ x," ¿ÌvóEJ—õÚË®j E_{¼%ÄeÃò] é;¯Û€.Ÿ3ÎÄ)mäyx¸ÿ‘¡ *TÙVgÖâ…·"ó³./ððœ{c>@ûàá ‚ëðÐjå½1¦©¬¿•A…“‡‡)€€H,ˆ,µŸ8tü€ëM‘@ò%µªoW°PÛÀ —™ÿ5!±)D]%гFH¼ ­›}è·‹?#O´—ÏÖÜå÷;3^ l2‘­ÈSÆVORߪ D~âZ\„˜ §8-åžG„™žj ð¥SŒß.=ýŸå†\™g€lÊÎI7£Å’$)Á÷¶ ÎC–ÙM1É”¥¬J-¡Ý/…o“.Ø·ô³Ap¶®…›9^$“-uŽË9üpñÄ¥†“˜ŠÌVK©ï¬Ýg©Ø>ËtÓµô~~é•Û} w7î.lÏ|46Ðv¦0Í|T´kö_Š^y›/]8ø4%$@Ä#šþ>ŒU»–á1|4y¤%¹ÝÉmWøË!fNèÆui• š7°1^7 £ÉŽ„=^³cÝ‹Ïu¥J)’ö ýŽWä·cLðdu6-Åa‚Öè¤õ̶p'>;cM8ü>/1Z$îĹ¾“\ŽŠ¯[T;d–¦2XãvèLŒò¢Ñ¦lED³ª*ó´ïa|eSO?¢êž t<"^Õ,Å{æmÒgþä퀰C`)ƒDR*šÎ¨(|klÌ$’è쯦?+õé‰î6^Âj©Ÿªmù pÁyÆ…ˆ·ë¥NÜD8ŸcÍ1%7Tq³[ºøÖîTxÛPlžŒ+8%‚pól™%&ÜÖìÎ:Z ßµQÁß´ŒûÊVì»ô‘Sè7ldÝ“TÝô±;‹˜É+4ü»ñ”QŒhG‚A„#§™¬ßûo`C´fý£Î»ùÑnœxYi*^KW2|gËl›šÅSxz.É;þÞköH’¨|__°\öM­ð¤ /ƒËü¬ÙÎ8'K‚óH¹Üeõzy¾:[¢!VMàÁج3­-šø§¡†å%¨ñ>jÀúÂÀa vø#Ü~…õ,+`ÀJS8.ZaÈ,xT—gÁÒíà䚘¢iwõQTƒË˜~:i¼2>/ zVÊ{çpx¤ÓQ ‰Iœ*XäT QRxFdà“:ò5ÁR6¯xrÕ'Þv’xqó狱HOD[1†»óĵFÕ@•€Á…‡;Œ²ˆá`ÂÕ¢` ³}ö;Ïø¬ôhd XÖ¥¶hÍÓŸÖÉGè¶Xtù *‰±™¹ˆ™ZW>…Þ"| ™R?€ 2ú²è]ZÖÅ÷Šé¥aÛÆÄ;ÇG$m퉕ÿÇ!­lô _­¡%š®sW(X¬Åh¶ŇÑèkßÙä¹°¶ —Éîòö`B¥ é6žž™aC+³´Â)G’i×Z#ßYÿ'ìQÇ£t ªuf[œÝ-ŸµrÌ©a›Röó¡Q ÇvdNšÄ<²~ÜìFžå‘mpÞc(õKýï`7úÁê÷ÐÁá¾Çà˜™#¾#l.â)I‘xÀ^*|€3Å×ÇÀbk µ&í(¢û£øQ„ðÖw¡f “ï¦œŠž•ÀŸ”ðßö)Ë€-æR!\zŠ ç¶bð² ~Î OïEb°+À8S‘EŽâ!'p0©Å§'SÃKYcÏá“ÁcL,C)y"X„êðˆqx"ýC²93’¤ €Â5ñ˜?|DáS¥¶_íŽ9zçá2D9SûŒ} –è\r£ þV¼a­1ç^ÍòŒŽ“[ø*9o—ç~²¼ ×?8=ƒ©o–“rKÈєƯÂqM«k£f6‘Ñ>|s(ÐÁ\)?Sú)Åê,CwÿyŠFà,zó¸¹wÉyq÷A½’»¶á(´U¸0¨ \Ì3œœÔØåÜX»L¯³ §¤l¨™Ä°ò˜f¶|l-+²|qììAü$õ;("Õ.9=tfµg²\¨¤ƒÌž­ÃÙÁ-ÕQŽqÜ8ß³;ÞQ?3áÏÈ ñJ•¨[!ÝNÌ›°æEþûjçú!È…¼yÁîÆLnpM”/]¢ X•¢Àó øÑ?Ü0¾ì?«Bþñ’"Óœàâ˜âeЉ‹É 'x œW0{–{NH‰šýNԙϞ*N´ªZ—,¢gdFk8Ù'{åòÔ>o€ïÐöІÉ=ª}<|ó˜Öb©ãʨýòÓI>>=ù«þï¿ZÛ¦endstream endobj 72 0 obj 4445 endobj 76 0 obj <> stream xœÕ]Ysݶ}÷¯Ð[uÓŠ!Apk¦Iœ4q³Ôµ•ö!i3¶eËnd[‰,'éëß+@Àp@WW'Ë u/ bù–ó­÷‡£²¨ÄQ©ÿ/ž¼¼óþƒîèüêÎøñуO§‹Ïïüp§/jýÏø^?yyôÑ©zPŠ£¡Ú£ÓgwÊbú²3£VGUÙuUY”òèôåoŽÿ·« ÑKQíþuúWõlUÙg«¾(kÙ«OÏÔ½_îÔsí š®;~¤®»ªew|î.ßìNªJý!¼áÒ]>u—W»“îøêïºiÛ²Vï·Ï>/릯ÙU]QI¡¾¶w>ÑO¾v÷¼r—owMQveÓà›^Ñ ¾ØIýé ý¡ì­ÞÔìåµ»W/ (KQø2¸÷ýôÇ݉šcY ï±'n°×îS¸÷Œ;õÈßÚxf|„gnGø‹_îN„T›ÚTøŠ7n„b$Q·EW·3­ÑÝûdW«‘ê¾Å¡`éÏoÇÔcÕ 'á=öŠnž!!½aåð)¼†…ÍÍÒÅ…¬n¼5Žwª¹VB­Æž´™V£˜¦}ŽƒŠ®Slßés9™F?.ÍáÔî^1^ж¬ŽOÜÖ=v786yC“>œ5ÇbG¼°¯ñ×®ùœ’Þs7{.I¢{C)´f%„^²dvâ’|„Ǿ=v_âv#`à3\»6â'ÅÅJзCTÍwÇ(!DQõVöóé*ñ#êBŠ¡›uÙà"ùFg¤¤¹yó§6žµš®R‹ýDJÀ¹­bMŸ3íÙ¤6Çyadʬ „Iƒ©î,Â&µGœ7ì ½¢t š’;qáÄÅ›#A`t%Tãí)o‡±Ì»BTÑÝœô`;=[8Þ­súü,*ÏêxMï…\ûÄ3} —O鼨¹þ6çšñ| T-…L¸*Þ8uìÓöŒ¾Ól;¢ï­Z#¯vc“ßYÖæœ.ërF'u«ÅJaéÏ/÷Íñdžºn£åé>„)ßs¬ñ¾ûôo»“Vaá¦óöãmz?^0¯BˆÀh€»@­±]—Å6ç8îXsÓøybž‘ÆõY 5ÈœÚIíÃļðõãP”¦í {Ï­Ëç ãÇ7£Ÿ ë[íÐ5¡/jA Âîp€E)`̬dà¡-v.ÛeXùö”Þ (# Ѐ`à’‹ƒ¬DçÀï…}]áJÔ²°'¼1Ê­Ç–y¯íÕÒ:ô÷Ñ2ô‡Y/¬xdxÇS£x ù;*ì|:ZÁ³iË4‚VahÎö\x>6òüªŽ9l´ÃNļ®mÄÎ\fˆh$0‘r§ÅàšUiÆ+Ð]gÍÂgn;à)å°é¥è«Ø™4(#ð-•8‘d× È‹4Æ‚ÓfÉd¤*%Ò&A B€Ü7»×±1䕵à¸ÉšzŒi5ð7Ì„Ñõ{¸°WÞCÒ™È  (˜ö¥jÛÙ͇™‚:0Òkzî[ìÝThL[eÕ#bÊ‚¶ç”²B(â$ñæÕÞ£?!¢w¸o$²Öõ2¿1Lÿ_K°®?AåÁšr®I1Ö‰Ò&mK=‹L29ÆI S­1(ÀcžÐÏP·o¸Ó—/ò‚‰4>ó,¹©“Wç]–µ' ¨¤zêÓÔ´ \†P„&CóÆ(„ÐÇo4þÌÊPý롱H˜ÿäƒS¤ûáP$·Ðö‚6(»û÷Væ³p€§ò<Ðö`%²¨»zæzzÞ¶õ³T^±óLݽevKJ7ƒGs½nfÖRJ7g¥FVI±ü º_µKxPÆðš¤ý#m 9³2`½áÊø†Cd’$ìòUàääJ›xìêTÑÊ­Eå«=¡Ï-TpìŽN,1=¾YÐJi£Œo7tlÆ}íB ÜzeóÚ¢ )ð#J¹Ø3bm‹°'«{LBBȲ ÉŠE<Þ!w½L¹{ö5Ž2N8""†Pt`HD‚óWPC#"ƒ9jµÆ˜`›Ê½e—Tq.â¡G>B6ÊeiI’Â’WI{(øz¯ñ© ä¶ñ§ä‹ùzxL¨ëªÛž² , öÜ)±GŒ•>Èɘ ",¸¯ñWœ{ø$V¤Š@VUÆ¢v/û™¾,Ê`¢ÉІ%O´ ÆÙ6 5D’(¢‰lè€FmåxÁ>”vÀ‡Õ¼3cè[M´)”ê~à4{˜ÿ ‰Œ)¶á+ÜÀJ3ËahŽÿ¹;‘MÑ·¥ä”ˆ–R¶ì­«·J½)1I×ÖF•Œ¯è ½á¿ó#I§j¬VÝÜ Ï#DD¶3êN]ú<þ! ˆæ.‚g³e-^®ˆ*ø#F|«gÈwh!tň!ÌÓ¥àF59w#ºïyêüöàZ×Âl«)Ó¤é õ?Á~i¼¾˜J’Àvqfמ‘Óóâžö2÷¼¦ƒ‡ÚÞ¹%ÉÛ%ûD‹¦Ãçï;-âyTž™EÇ~R$´ù./pz5M@hƒ²HÄ—ôPÑìbr ‹=焱ÖÚ¢tU"Y »šÐq¢_‘Ï÷rõ¡‘Þ$V6¯²ï Yö  âKvÜýãNèÔž[çÙ®®ë({S(03V›C7^â *w€g³(·˜H_»ÐÂWîòÞκŒ€÷|»¯–j¼ÚÀµv6`ëñÌtj­î{΢•ªïªÇy“xªH6FN¥ï´Œ°h€k#ßxwæ«é³t¹åo´z;9î` (œÏÊKmàÈ" ß®+Â^OÇÜãÈýòg™­»ˆÏíÄy™Æcz¤°!^õÙ¢!mökôòq‰}ÉØ*®T‘!¬Y¯€2såþî$ÕíàëŒíâ`å]W–!w¾Ô¬­@XÙOar«Í5ŽÑK”#žmÕVU„@eWˆ¦ª‰¬(L ˺ÒTX’»B¤•^Ê,Pæµ(ªzõyWò^xîDKÓ´ñ²†¢mêK¡Gæª)ÄÐ °0oÖ/@Рt•Žó¬: K¶+"ü4ê¶®n¸˜è4´7jãi4uÓP"˨ šèÁ#œÜè\¯ò髸pz02ÄChÛâÙŒžÐ6† Ðʶh¦º¯VF&ªsœ9(Á]l؃©\ÜØ¬sƒg.f½IÛä{›CÄ f)Øõr«LÞ—Ýb(€‚µ<ú6¬ÊÁ’g Ãé[\Sý·2ª-…il¸¶e!KëkC~büÂ]¤Ù¦<‘q íÙlRä=~ì,›Š)Ï,å:Þ»27ÐÐ,‡=¤½Òmr“ƒE°)|ïç{:q¹˜ïé\ioñ¡¿Mòœï\‹ÿí¥‡ÿ‰«mÿ3©Á-ÒØK¹Ø×gº½nüTUû=×@XKÉ&iÀ.;[Ô¥›T¹},N#×VlÊ i,–º¨GÁU¡Àvnóå$Îï†önÖ[å¸,SÊ6^_c†¤GºœñðÄ^Þ5À“Î~¦¦9O0n¢:'¿äÃa šøMs’ö4a^ Ï}Oœ0ÿô÷j’M;45ö³I¤<åÎdUKEë}öq©– ç–ýùÙDdŽ©Îè–ðq¹µtå‘£ á5E?X›V²†Zrt,pž¾•°¾›²ÚúöçNç‚Éæ•gí̆Ô÷Ôdª»¢wþÂ=Cù©¾"F”NËØç°;xRë¾teå!ßlßs°g\|{ 'æ{RLµg£[ן©éÌ• ]o4\ÝóHVªkÄYkg~óÒï=õ+8K3êiKÑXtÖ£0åÙìŒ*Ö)†¨°c±ÆC3 à|‡.˜‘pM¾ž ÿ!åš`Ÿ—n/ºP˜3;¬&Kä[[•Íà +3Â5Ȇ²d»¼­Ùßu!ªcÔ­ãQ®%(l0þÙnJ Êö, ŸÍÆ>‚nšÜ¾0 ªòö»qÿ0ÓÎt~-Q^jC˜]£(›¾3ΕE½QÕúT#þüŸõ§MYÊ{0~êlg—pÀ¿qõ¿hœa½É8Dc±ÛÚI%AŸO³[äT‰Í…Ac¿I¡}Üh¬QÿÍsXx©â?tô ‹ Ù¦ò³ös¤ú¸#IKQãòy˺Òþ½Úœùë¨õÉL)(ö£#Íg n¯DÆ Uw&{¨Ã2j¹¥0=êGÉA8ÈyÛº{e‚{×{—¿ÞÙ¢oböÒÌL#R—„ù±›\>pC™)jkOR*æÖ>¼µ. °g>g/à8’¨‚14ò¦ÆÅN~f¹Ï¹$!®6òû‡›‰µdSY¿£€9—–Ÿ¥ETpOX2 ˆfë³C ÍÚ)d:ð9xÆ?öU®VÞqß7ÙyÑûtFyÆØ €ø‰¾£ts(î§VÀ~¿³MÄÂYšÙiž€Š Á Uvܺȵ¡ÉøÍËL›§,†–…†½P µzaþ„|ë‡c.Ì}ÝÌÛ»RIÆXôHa9çtÃRÈq©ÓÌï;–Ä¢Zs¤N…/·&¯ÄöÖäÔ ÜБ¿®ª‹Zº¬=Ó!®¨ €KîÑ œ… è ^(hŽú…ühdd¿Ï&ÉðÞp< É*¡²~ÐU[¬ÿÛÂév¨T6jìöM¸g¤Ê½žÜ;å÷ÑÉèŠNö²&Žçn¹¢Ÿkœ©&4ʨ+ê!T(^tI­ j 430Z¹q~²ÕVœvÒÍ¡Sƒæ¹[z"ß5¸¸­dB"´e!\[ËÙ–€¦ã\.š¢-›¦Ÿm®—O|Lˆõ5½füsNí\RÎÈÕ~Gà0üüIÑ”!ãéSm'M?/¦ýÚV:þÜþ ²iübÓ‚´œmðŠŠ*ÈÄñ‘nÕ7ÊxíSõ‰õ^’+š¯Íçbo‹º%(Áë¾"&õ!2¡ IQYÈÂC´fžÚÚMIxæëȦøð~pƒg|1·aŽÝç(g[”Uãcv(’ö?Ýâªj¿æаýV¿±y†>°FˆZhÞ¦+Iqƒò»Iÿ¦´Òàâ4® Ùò©fäŒJ9EÏ­4†B¶ïn'¸dÛ½ DÌ»õNatÖøH Ù2\ú rSz9Yô›ÞZvtûÙSt0aJ«e݈¦Ydð€%‚.Ë®^0=ÂÛ7ô˜²?)pó2GŒûfÙ—t0N]9t¹qÏÊ–$dÈ’·^?µéRÿ9Íg&N4Ÿœß Ûu t1Ùv]w©'1 óß\áÂoY^–ÛÔËètI½ë÷}+ˆc’LOít¹ ¸N•zÊ9œV”Vøè9gíe8»-ùàaÃ/×X‹+û\7ͬ"âAòƒ:Åx%¶¯9PfõC]Q)DEfv«¹Ãì5¤½„æ 8Y{¤OÜN纲JÿŒï©Ð)‹^‰¬yÎè\s„u? ¼ Ú¯3ÇNöLæÍÙ–#Œe·{Õfk$c:›;å Ð:À“¿ìÄ @$rä¶8oتy¿Ñ-ÐÍ ²j±Tv•KNØb8ðßÞà`|Ë-UîÏl¸çÏ–%4¸.k…#›D_®Û¹q–lï1oÙBš•§íf¬yß~ÅÈÇA>·ß<ÿÂ횃¥ŸÒB—¶½Í=’=7FòKçà¼ÊñÜ’C²%Ç‘g#z3›ƒxBÊå Þ/62xépe6ït‹w*×.‰ï(oPý¢ÏšDß·œ5, µØ¿w•ÚînhÁêÿ¸µSŽ—ÓæüáOz˜cDÖßÈÚó½‡fí‡ÌÌpåž¿y’Õ–øìÒ¦Àd¦!Õv²ÙVý©Ÿ¼a«Éò]*Mq6)[Ù·s;òX”M&ËkÜÌa経+îæ?'˜° éçJˆyûã¸a^òê`\`”Fœ™,8H]"¡)Õ7/juV›ƒðk"3ªø0$l^ûQzõÒH¹{VnàOpÍM ¡ièAuü†f ©È˜}Ї iöÚe∠÷åš´qÿÃV%e3wš@±}?“VªŽ~®ûQÀm¡tAI»®µs{EÅ|.5ì.­f¼GÏkÉAÏ!,$0›3n-¦÷z ¦àÚû¢è[éå,,@ØáMj^~#5ô䦲TR³e˜†C¼Ÿà ÈÍ&¿íW&]Ú°ÒtÒú  mØBñ|mCFeB ¹ýܽ‘+<ÌÍh`K©ÝAm¶}—ÆCÕ«!ë…ÕF¾öœwYƇ¹øýXÌÑZ´ñÍ¢½Óû%Χ°8s3Ý¢ÁÇâ3 3Êbî`Ù'§wþ®þý?ç »endstream endobj 77 0 obj 5569 endobj 81 0 obj <> stream xœí]ْܶ}Ÿ¯èª<¤[妀HëɱT)§dÇ‘'΃ìrͦ‘ÑÌX3#e©|Eò/ù½\p.ÈÃÉY¤”m¹ÊT.îrpþy•gB®r÷§{8z³÷ø…]^íÕ?¯^ü¾}x{º÷ó^™)÷Oý>z³úÝ>}(ĪÊ*³Úµ—gUUæ¶éU¬Jú½ªôÊŠ<³ÔàÍÞzµÙÿ‰¾)¤ÿF”Y®ŠrµÒѳ¼÷rýßÈdYHAí¿ßÿC–S"éšKo?Ÿm¶Õú›"uÉ+±þ|³•¤Ø¦*×_„ž¿€¨†ºQ ¦ã쳆=ÊhÃçùÖ™!‰*ÚÑê¬íŸ“´–[7µ­Ê«LWvåÆÒrI8ÒxïLo®šW~gÞ°XWY`VzÆÈW°íj=ICjýß9µ¾ý ©ÖcŒŸ`#µðƒ±)ÃÙ\CkÄ4`9áó%”®bËoÔßf¶«V숽ˆÓ'AóŠ]wX‹Eõ $á¶Mrn’Π©!•ÇÐ6²¦`1òJË¡‚Õ&Ý(¨.$ïê "Å'á1¶þV´KliñÓµYV‘ŠãnYÆE¶ž°¶}cf=Öhmª¢ph- §çÕúkÿã3Btǵ\r'ây ó›­!×C[üÌŽž@„KÎ-Ëk!R=ÌÕ¡©\$ ÎáªEªb ð¡CWÚþJ ˆÍ–ÏœÂGŒ‚æ˜u¤»žòÈSaL@ؘT]d±êv>gOu½±G¶Úržk¤×Î ) Mzíµù™ÿC &|Ä4ø3ÿãsnlöÚÓ8E[eÜšÑÓÅ*ÕŠL,/B|S½øÆH)éâ›—ëb³%¿\KcgDEek/†x£³RÛõSrðM™KéÞ·¼ÞH§BUýºr~®ò¼ YÒÔ¹eÔÔÒoJvn’PôúÆÍ7W²¶,Ö+=W6Ïf-Øg'¬ñUû¬•FQ×Hhgò¦AÚµÜ{¾·ÿèåÐÃo\Óî‰)Ú1xýΡ^ÀÙÚÙ9‰5kÄp˜V2 ]1ÞÑËœ ³ûÖæl»þqbÇ·ë¦ù]hÊ¥qd(L%ÅÊ­Ô”l[cra³gü»„ "ÕԷ~-ù²WR£¸ú–8D`ž‹-6ü3‡5¹(ã)öü]§FÞüû†ðI¹æ”0qöt«E-†øŒÔ°ŽòpЕ!N…ž†~_§ÅÂÉÈï`z‚8ÛñÛ–Ü=;îëë¬nÑ}£®×¢K©Ô” ©-cï#Ø4¼ÇÎUßxÞ‡ådjðØñ¶çâŽçO°õ¾‚ºÞ‡°]ªáòB¸é·aAü’~¥UKj»;’r16XÂ{MzjX‚ƒ@¹ø—áýE’ÖESu`ÁºšJõåÔÚû¬ŠþÅy¯ýÉpç°0'#¨ñÔ¿Ûh'Â(óu-w,ÁÄœD$KÂG~Zúí"ùZƆAð.[M<Vº .½ _ø'¶JžûK¥ûñ¸ëXe<ÕÌ©oWÉ_Oˆ`ðÂKÓæZïp\X'ga¾¡}ß?h&H¾×XÖgÙØA’›FÏd^Â$7÷@’û8$®Ù¬ÞqG‹£PI6ÚË@û÷¥ÔÀï4&#yێб¬2©x!K±þ G޶!ç6ØÓµ ÍçP6xQ=†z‡³ØŠ±¯yÈáA+^œv9º=ÓI¦õXl’É<Æ)âè o“pt‡é ¹ø"tVèÆÉ0-Àx°ÉOfpu˜¾‰èNøu;²Æƒd™£‚ñì žÉµFƒ³Úc/daë ×Ç0Žå£P! ~í÷»üÂ"’–ôÀ•ÉÞ S#Î\ûY²¦ÄÒëç>ÛK< ž(ÕclF£Ï:Z‰’¹ÈcÀ1Êù@râ 3ß_@Êâ#£3äV½ßT.\ìçøpJ áÅ $ƒÀ„E|R\Ó&…}EŒ¬¨ÔÏ;á™( ý:cS€ <‡€-ÌÏfà õa£oǽ\Ž¿ñŠÕ鞊 Š)WèvPJôîË æ«Ý‘ÐhÚ,®!­íÚgHûƒ"F5µ^®Žþó۽ƑlòL_5¾»µ‚qº»T9vG <×+Øé"õ¡çu†Vìt‰…¹º‘“æ—Œ¾8X¼pS=4¯±R B;.E4^3pØ–¬®°ôܧôMéFˆâØ cù'Ìì(.«‹$5íŽës÷˜s©2.c'ö3q[Æì %‹»cY<†žm”²íÈñ˜eŒ‚A ¬&öõ /—‚,&¼ãƒ‘ÌÄ5Ë:»*‹þbœv_BÙbv¤é*§‹f§%"oá}È” ÃÍ\hÞ%ԘЉ¤ÆœìfÿN-£Éê<4F{ˆ‘þãâ*Þ³’F¾I %àÍ“Ã3Ÿ)ás@LÂÛÕ§Ëcül¿S]Œ{€žŒ ~5³q¦G8äO,ç™!>ÿ½—®…ÉŠ1+K‰ò.i¿Õ‰È¦ÛDÍ~¾¹à’‡%*€3d¸tÉ{è?/Pê9³c)‘Ǫ̈£5jD;ªöÜÁ} ¨6µÀ} Hzyø0Í|qK¢ly`½Ìû:2ºÀ[} yB À»_¦ ‘S!«JeÊ.bDÛô9¿É-¾sbæ2ÉóVÙ®1ŠÑöALñ]|?¦E÷~Å,*%–'ÔðR;‡â‰Æ\ç€ïÚ˜…»[ ¬¯¡ÈèaRšäFQQ"ø÷A¢ý9sI¯q±Üf¦4ü ð°&8&ªáý`Ô›w:@ N8Õˆ #ÑDfÆþ[©u&µŒÎ¨Üìɲêý­‘u§±eÃôsUʈ1k™a8bz{®11kuœÅ<Žm|¤l¦Ù A!á.¡oÿcåµû?à»TÔ·¨4|È]‡_x\òàk»ŒO Ê:‹Q¶ÊMVçÐJ˜oŒ)vBëí5rÆœ™¾ÌÙ¦1»~˜ïr•ðYîàüýà^™*·v)$E,gEoPfW_°p¨é=8E gø|Ë9T¿ÛúÊw³Û錭Oˆö±}Oð€·ÌdØ€¸ÚX' =&Ô1ùªÒÝ5!†N–04 öúsø.,xjñ/&‹l{ªAK•å*Ú!þ¯Í¶ˆ-¡nIŒ#‘ˆÁÍPbÙUGp¿jhyÆîHæíšEÑgòŠ |J‡0 ÔE'Eý±3è`13û”Þ?ËÇõìŽ<ç7åréw›gñÖWiOr~ý¥>cÓÛ9õàÞ3ŇΒÛpaRõR“mÍdv,çÑ+¾äbEN}^.ßnkùÿ“‰b©å¤ßrÑ¢Ä~½Žh IdPÅ)œþÕ ¦Ô®EIæ¹»©7²¨sø?a¼½›7°#ÆÝ-­Ryz·ºê]gšD2ÜX¶õ _*Ïf¦.ÁH_¬ÔÆ&ø^¥ôŸíïý‰þü×ö÷endstream endobj 82 0 obj 3910 endobj 86 0 obj <> stream xœí]Ûrž×Sè.»©¨™>O‡âÂĆr1‚„‚%K²L"KË`xŒ$ï’×K÷ºÿžùz{gµ² ¦\ãQOÿÃ÷ŸZß6Œ‹Ã&üNŸ¼óÈ^¼8è^>úpxøîâàÛƒ–Éð_÷‚>Ÿ>;|ÿØèsæðøÉAÜkÛwʹ0‡–7¬mŸ|¹:Y7Ìr)»ún-ü£h}y¶>RÌ(eÚÕ7ë#Îý{åV¯ÖZ›ÿÉÄyJ8¦­õßùž×GÒJÖj×=J%™R~#Íš† —¿Y«ðè”äÈúO&µ1\=Iã_ÓYÇIÛÔËUjú¼ë@êV®^¦·7é+ÒÁIj{çy˜f3›½PvõÂÏÞÏKHGǽŒ?÷¿#ÂÙÕknW¦¸N‘…“dÔóôøýÚï†qÙTNÓTÎáðIœe›ì§è©¨¥=%¾€C·¿£»(GhÅx$Aç[ÞÐí@çé­ úMÐJÐS$û‘Îãe6mîüj¥#›ø=\!ëf(ýÿò(ë‰V2«ìa˜ŽýJÉ^ÿàÇiý?l Wr®‘ãnm?]Îg'ý2U'0F–ãÚx&”åü0‚‰¶Uš°Åe€¸]Oá1|–.µÕÙ.’­¦Èá:8„GŠ)ü–Ñ}—OðÑg©¯œðǨ~Fâñ‘ïFÙ„œÕÊ«,Qvwh.'ìŽó”aN›‘h¶#q#I̸ܶ"‰º¾Jt}J7X¶n®I$sÂpB×JÁŒÁ‚=,»pTÏ<ñ«ÀË™”$ßbñÿ WÂ)?¬³­RŽ!kŠŒv÷,³…ñ‡µ„t·Ùôljc5EºãI‡ÏlkdF§q^±`Ú©o]G¼ÂwÍ[ªÁ‡ïe@ŒêÂ&Ž _´‰£´Ê÷p<ê4@£ ÔÎ1¡{} ú5@S71âˆØÄ k´uÔÆÚù«ñ–Û¤«º1H¡aÅ#`¿%$5"©õH-<-7³¦@×íÅXX`&Xי L±…™ظámÉJðrHЦxÓzÑdVÂ1U‰FÇþNæíBû@­í‚ñGú¡ôI>~wíY OOOM;1¸ñMŒð= ¯ƵU„V…óxªá·‡PKˆj7Ìp‚„pFªÈTØÍt%}•h}†7‰Lü¬öÙ»—é320¥U´^,ÝKê6žÎÇ O}=HFG±ö0uð0;Ó×¶gN8‹L¯v‹Þño!NHîªïû8×JE¥9œ S̳^I¥‰Ç£fL\© •x¸ ™{,¦Y›{ÆÍñ‘0îC¸„íxØxz}p¬‘̘vî'Þ³ž_Íy¤a±L;EÝ ¡B»¦æd îÌm戱×l?cÆ ~Ltø×¬ÉéÞÀÓ%›ð|]aÃŒ‰-Y`"¢hÐÖlËDÖñ=3QÏš9-GR)±`P«ËxmbšköUYÏC8„‡€¿š!o hö?x8¨´¡>NÙú”oŒ³Þ~€xYDÒöÌìÊ·7Þ(?ŽL˜Á=Â=gù[¼á‹Ôr«ž'd¸+ð‹Ýf 9qjÍÒ7m¬äb‰ñÖ°Vñ-Œ·R &•5ÿ•#·1Ù²Ø+GÞÚ²}ô¢d€Õ8r7wÊ„#;6܃)öú8R2+LÆ‘§kÝè[„3¹eÖ)™¥ìhÃZóÆxïóµ á•þì_6ƶþ%‰3nÛóÈ$»Oá‘–¢L„MÔTåRÒÃ2ì$y1oæÁGq*«£~º²c<ë-š6ÒI‰G>ŽNwGNÇ„ÍOÒD.¡|¾ ŽN˜É¸PÓ¼:çÇÑMæÓ,“-‰oN"8Ö|Ûg³D.Œ –&^€Xf%§Òå0%-z]¢[¦ã0d¬`~¶Ñ‹„w‚p‘Ép/ºmªr,ÉX m¡êŸmî/ì²¹ãªqnC¶òøv-FV©ï9Å×”¯P =,}²X%’.'°‡Ì?PË:Ø &™C²Ê¾†*Sh!V=D€Â¶/áÞ,ɮۇ%5õã Í”Ôs?Þíq[Å9ç, ±\*ŒHXǘ~ê&”Ê–µNR?‚W Óƒ× Qž¯¹Á™ŒRñiþ¸^iq1¡½„¡(ñÊYÄ`ç) (‘£$òî²õœvoç»GN S &Ë/ËIÐ2átÆ\—t©Q­à¤ŒsªlŽ”ôï¹y›õNÂH×PC+£æ!¸‚|“±õNšrÈÅôl3 ÁCqVªØrCÁ–ÇÎùsVm ¡hKuYî÷‚žÑ^ZØÞ–ÞÚ`<ððÒ£keUçБN®îõdÀ½ÑùÚ7]àÃOÙ’m»¬¯ØÑÍн4AÄŒoÓÛ“ôö*=žÑ¶ÝTlKGMc‘ü±5¡0!L&@RiZBúm±¤-îÕL„ òßÿsbu‡èÎáñÃãß~¹:NIH½{&œÓy|Йܔ߄·ŒmÃC'±õ^ŠxZÉ‹Å+å÷inBâ'™8'ítê¬ìN#ÖFš*Fœ ˆCõÄÂØíSÈøU-ŒóÑñl¨³ãÈotÓH»ú·é›ÊRX† K"ÎÙ%‹,a¥Ä€ß<0àÉ€]d,râ/“>["‹ÿ“Þ–2ÇAöýÓ[yü ±``? ×Ï'ÛØ8-æ.i´Fè±áä€õ¢ÅöU–3.$5þft7•#‘j> Tgœ‡§÷‡À¤åñ%§Î6’£Ì§Ó6H'm™Ì¥S÷刧 . ì<•YH\Ðcº ÄÈS7J©žª"‡°Kh_ì”_æGGó‘byò˜ZÈ#ÆŸ1èX4³¹–aÇbÅÌ)3òÐY‘Çã^LÉ.F2) b@¼\²V ˆ·T=çÛU†%o‘ç4®:¦gZ5¼,kÕeîðàæGµ-yY*¿¢rG?²ìq¾QtsÃG(< ji5"iis³0nàà ÉLQœ ¼À‚¦…1¿áÏ+zî¸< ÛV1Úfڙ㬸?ÎE;Gæ£ÐÚLØWì*–Ærˆ ¼RI,´_ý¸ÿšYu‡ú{Êœ%Ï¢¢ÒIVŸD„x?9!Æ—*×ÕñûLWGNÏ89·Þ‡#h"™›Š¦{ÏA¡fºm´+æN =¯]øj•Öˆ‹®æu±¢P;‹LœñO,‡ñÆi|µžQo2®Æ½IB´·ûÝï­Â 3CÕ2å‘«˜ƒ{:¹2Šóº€’•t,qÃÄ~±ÌÕŠðá|„O×GAQ‹F¬þŽ5áb`H:…U,31Ò*-í ŽHãL,œŸ_Ã}Wµnqüà$V ‹äýŽgXN¢ÕCCÓh2Ñ‹ ~ЩÞÂé%ø¦ƒ½‚UâUÝë1ãPÖ0šéPQfjÇ”™†3F¸ñ2rù%‚/‰•Ÿƒ†$fÉýÌ@Éd+ÜÎHº†¢Kf}î]éøyô®1ñ®ÔÕrõئá9}ŒÝ`•±„f«“{œÙ†€#£ðh©vg“@Q„—`ËóÒÉÄ[æTT‡ÕĈ/:¯~\̸ÃÿJöÓθøûîy ´ú{ø‹bNÃ14 ÷a&Âe´ñ6MX|"WBöåäÂë($®ât-¥†Uç(ÆSSƪ8¦Õ‹)³ ±öû)2Vü0à±ßxÙã©(ºZÖH2{D¸¯Û•ÿ­¹_€YÔdúÁ@†ŸFY„ó5±/"ÏÎ{MŽ´÷ü†iãtƇ-<"lÆgJ,K~ÒhY¸6{‡ šFÒ{/p‘neV.[ r»d¢?~†“UÑmÇËN)éE3Ìa)qdáõ'Qõ>Œy`ˆq )‰B,KLôZÅÎ;PSœ>BÏïÆlŽ%>¤yUkºbI*Å„P™Sl‹ÆpÖc7£%âéˆÈ§_´pº°&qDÚÂÜ]’9°Äûóæüx3Gg5Ë…Òy¡É„*’¤; UìÖ…É‹s‚·0Õ{q®ÓU€‹ByÃõhã¡Ì¶½‹!›FÉÏoÒc‰3·¿Ð¬m&á&Rџ̽ïKäsņšxö·(ŸŽ½l¼a`’2TÂGÒN ‰OÑ—ç÷kîÌKøyvè(=Ú{Ñ•Âå\§ï•+ÜÛÿUª'HqîçÉŸÖWm8¡mÙù6j›{é³d«?H/wgRæ^謰³ºH®Í_ÖGÆÓ²¶.o?6…]DñîÜ¢ö ¿'†³FE)°U2^ÙfÍ/¸œŠÅ4£a(þ:§‚i õ_Ó(úÔ÷>…žgÙ;”FÌáçbêÎ…R<–ú¸s<Â:§™§ÂLèA‘“,ᙉÚûh@ìcjQCgùrƒ‰LMWŽo­ þ‡jtlC0_†³MIH4»òvJž2(òñ%¢Îjèo–=7ÅÙÕ÷Úm•7zïjRLÚa¥@$¯×*Óz:ê’¦0€«‡Õp,bÈ[2ÅÚlaCÕÉWº‘E"Jqòiœ®rEkãŒVôX*äSÊE4@Ýe10±€s'Z5¹šT¿4":½wÈÙðz—€! ÊpRGÆß£¿½¤~†Y½ ‚Ì¿zoéùîDe pÐéšœ¹ñþ)oáŠÆ$0î\f PÐÔ/åJ5T†ëÂwtúTÅÔÀõ\—!ÓŠ!X1È–ÊÑùyƯ6ÈѩɮʹmnSa®ÉÇ@Tp óåÆŒzQºy Ö…w¯¦FÑôÎè Qï5‘y?ÆàŒN [rñ&»±kè½¥ëé6—tV*-„òC‹Å•UZÜ÷£!´è<ãGß{¹ßa)„ Õ¤))µø ´àÆjOî—ñ;R€qÑÏ€w£ë¬í’f`¼P@!„¾šeÇì¡fbS)슮Ÿ$=Ss°ƥ„çÛVJ Y„Èi…oÿ…|5ÍÑÎP'§®"ÿÅ•ŠD.ã$ O«@rŸW¬àªªj¦LÆì½‘ ™pÈUL“‹â£´aÃðÕªí?sÞÈÃË…š°*™²<<¤” 5ì±/\ÃÞÇXIMî> stream xœÍ]Ûrݶ}×Wœ·ž“‰X ÂítÆ·Ôé4©k+~I:[’­´¶$[–cÿFò½HØòHö4ÎŒ×}Yûäݦ®¶©Í[8~{ðÇ'róúê`¨Þ<ùëTxÿúàÝA_qóÏPAËÇo7÷Žô‡M³Q•ê6G¯êJ©¾–c¯Í¦×õJ‰lêJêo~Þ>ÝñªîTÛn?ìꊋ®«ùö….ʆµrû~w(ªºn˜Ò¿6®nÕö×]« \µÛs×òõP⬖ÛoùÂ×’þ7¾ørhËEÏI¯AS××¥ïë¯v‡2øÌ’4?÷ã‘Y<زŠõ}+¶w‡J¯˜ÕlûÌŸ=´]¯+í†=÷½þä¿ÿNÐéúŽm¿WÌ;ÑÑÅùÉœÒís Èö“oõhmUKÑÐý—kŽâWº±^ñ^ù{³û×ÑߘD‰V“Îщ¦2Ôë`û½I’+Úר¶kD›Yi{âkOý?êc5{ÏYñ¯úªU§G8ô#\ù—´[WÄK²ø™í%ÿ KÊÿ]ò¼Ä"ÞHú:‡}3G¢ñ|”\“G×YYpAOÛlxh–z†V—°øf8ږמ¼.aíQНcEb6…°ÙöSx†˜ ñyçŽmëUæ`Ü/‘¸þîe ‰¸þWw`ÎeP÷68e¯"]ßI^)6êúvX€l4;VFb .”Ü6´VVmϹåz"ª•í €EÍ«Vñí}=zǘ`F»Î}ñÃÔ+ï ÙÚO;;“ÌýÞ :ýZéD?­@tôï½míæèïGßü¼=òÊöÌI¥SWÂGê…’oùÆÕaá0ˆ¯F ´±jç!-d„5©½ô¤|ÓÆºÜïYªÖܤ·²O)Uïp°’ÜlÜ¢<àùAÿ^w²e}³VéZ¥·\éæBI ŽÈ¥®D-¬´ÂòŒuÕÞÛOP¤`@ÔaòIßâ ŠD}F»U”„ï|ñ ÅœÞrMsxÇMa04új¶ßûßп3ݯ< 4 z%åÄ2“ýUÆSÅPe:ΦëhÑsø•¯¼Š‡¼‰»ç¸ýŒ„™Žˆ˜ ž+)&.fƒTHE´lŒ–<)gˆ¸¼˜‰†‹¸Ë£X÷ ™âoZ7ȾmUÍÌÊ^ÿw v,«×Ì)·ˆús¢!=| Œ |äW7ì?жzæ˜/·/_ÂÂâ£~²˜¦‚“pú5œâØv°œIýYkào3P+¶rF*9Áƒ¡²`2‡‹–a²×ÛfN(xÀጫ~D6Ê®*!:niôŠJ×Ý[³"Ö÷uO '¦¶k ‡ªÏ‹uB¥H¾^ÁÉØÕQ]7vvÁf ‡Kø ÎŒ‚ŠP|R@¼n–Â\¡TÕt«an‹`îÃñ$š¾#Ðõ…ÿ\ŸT_uF ½>­Šd¢`}7ãH²§ß“¦'´©ÃÎvÚm#iW—¾HÀ·¦x½!bhì'óÁ ë'ðdï ¯Ý÷€`‹8×p{ìIÓ„´s5¿Ÿú¢µFX äEßNg䀼Qã#’ÿκ÷/`‹ë„0ÞHæjD²"v9¤ >’¾¼fÔ}~¶C‚û=b¯èŒX„X~ãñyΨÌí‘Ñ5ë2– ѧp E—n@ö÷¶ @ˆõ=j^°B j'¯(ÃC¶:3ÁÔJ“‡‡®÷uK3Hßn»J¾‰¿5p^€áISR‹éïUÑŸ@Úb³„Йtãn_À™HÏcòñœ(&§üiK—®ä¹ÛUCÈÃQbœä Z!›k¸¦ã}…ýA†ýÁ&ÒRÖ%^â¬#“ð36 O?¯u—P41v¢®Î ºËÅ$Fçá±Ìœ“ŽÑ°¢ @™ëBéXÒ )‚ýºâa'2_¿ˆ”·Ñt1cùŠù÷ðËÖO'ö‰¾e‡Gˆ{:s‚¸½4øä poÀ¥ê¥Åd|b¾X 8Ý îûxÝcçÞ"A¼_vnȯÃCÍÈ-ì7 'õ‚ë¡2á™r†ý¡[- ±¨%)°}5†cCì8eBË+ÞªÐE7 óaÞDÀáØ¶`È)$1âüßûŒÔŸqÕŒJìQ˜÷•d¸;ç DÂ2R‡…ðøt†Ø!—¸Må]dÙé¿þpÀ;‚ ÂOÏwYVË¥8;—×Î*ašµ¤÷Å~°5¿ŠbBæ,Xò1™]@x®·k8Ç\¿Bt‰=%[c„:ý™ЄªÉœ°îð„F:ûƤ;è)5"ãfG²… …#Ϥ¨·˜q#è$ ÷…ùsˆýN`1P÷H›?ûŠ”Ÿ²Àu™phá‚U¼®{Ÿy… UpÂháÆç-{9¿ðŒXÁ ¨V[ÍÅ’µú±€qÕá~Ï©×öóNÔuÊ]Õ«Úò”(a¢Z¤ð…ËnÈi¡‚hA¸ nB¸Õ£ÿæetoLþE$]4T‘’ ]¼Ÿ3¤Mî÷#‘ß_Æ»ÙS„Î虄‚«œ´ézù³®Ô8Bƒ<Zÿâ*טÔTq4|çŒÌë¿ÚÔ xäW@›ºüZcct££ˆŸAʹˆÞ¨å›ªó¢byl/—;‡Vѯ±/º˜÷)á´;`ÅŽ â¨ÚÄy=µ+˜g¼ú³ñwÚËÁ-½«Iôk°‘hÚÓ7ŽpÞaÖ2·ù™PÎ}©ë+ÁYS4MA%*PR쵄®!yäÌd«H©| ³Ö|†§³·yã Æ½í›ø¼Õóæ¦rgœÚhNeãTüYûfàÐßádžš´•lÖ°UÖ‰!÷†b^¨c©kɆ}†Þæð|ZÊóAAŸ@p´šZêÆ Ž‘+Åã€= è1‰ªab0³uñ€92aíž4§;ÑIVµJ,_´Ö £ ¢Gˆpoƒ DKŠj‚kºöæùŒ=Ö(ßËMܪ³\[èÊ;˜ '“8\—¬…çHEÀ ÎòÜP“Î1 Êp¼ÿ¿èêOæ0-K²2(ÒÊ[ëA~u©ówm üæ(î6@JË3™ÚZÕvu&“@™LcÎϰO2‰hΩ&Éû$‰tq1Ñ(›5}5› %·Ë…ô1M[…–§üÃDìƒÃq»!¤å45YíœØè`Ò4È)2èò†–å0/¼kdbŒû]5*z‹°(sÃ9ìÆŒ!-•'ïýls—?Ýv¬æ6nû¶O¼ž1—JIêÑCW"ÔŒa1é£tìÖË™hý÷Ülþ±;ì´RÑbþ}âϸÝv’¼û –Á mÁËm¾‡'sAz÷Ðr¹z5D>ІX#5¥ëÆ G%Ù0ôgÒ¨•œ‰@Ëy«%}Îܾ‰C ãk=;å9:äedÖ®Ç'z¡0¡~ÁTÁèÏÝì¶I„‚HBàC”HÌ]B¨Esw•áøÄÏaOɹ'¼éÍ­âUÜÛ¼ÊXÖˆC3÷(G=Áª®SY33¹@ŸF« ˆ‡.£)X(‰ËÞ ÌM[ ‰º– ä9p×sHÚ –}Çmì´iI^U˜æìr±€Ý¾H­g„À’r!€z§5áøÈÌLªüâÇIñàÒVùŽÿ<ùfîø“ôž¼‰Ï:H·ýZúØ%ËD¦Sƒ/—þ{Éd†€½Vô>7bVŒ¥‹ü‚³ËV¼rs¿JIo¸+²,=qü25ÔL¾@|6Ï´'O!ÙS¯ó¦ër&9Éë Íé«K¸“ÓIù•NÇe·¤g¥ŠÎa!Ž,¡Ž8ƒÛ€ pÌup {Çè4õyô„«õW£:äP¸ç¯F‘KHe'‚½ÐDš–ÖݰI^åî̇#É>‰1síÈ–>z¦Éß;ZF³˜âfŒ#,ÔÖÜì¿ç…Ãde-?mÅ 7ÿ§(§õ>XûŽçÞ%ÂKØ;‡Û5 I¹‹ŠE×Θ‰\Wo¯öØr—Ê"SrÑþ¿\J’×ì\ ¥å`͆yA½@¡:—ÂtT%~ ¯™£KÿðÕ¡‹è*A¯‚i_¯yÑÈN^ôÃp¢èUˆyô8#r [¡íYàV  جÿJØ Èðkº„–gu¨;!sÇÓ È}-S_CÙíÕç°)Y(‘Xœ> HëÑx†ïÈø>ܺ|&sàúÀY˜«\À ¹N(¡ ŠÂàkÅ1l(D€d¾6³"Ⱥà„{Î  Ió5£r…ȵøØá%$´zæÑ³+ùyfq )7æZ:HXÃ2ÇQ|qÝÍ×5‰9®ícz1¼Uôù®Ì›²gçM¥p2ó.@Xñk¸£‰‚r“1ÙÕM”¨rÃ>Š™‰œóͳ¡ t Ø‚LɆôÅ–|¤Î¿3l¡†RøÝ…b36TŠ78°5Ü6½ª}żš" GÄ*|°Æ °¿oÍ»É7 óÄ׸n1̳à®Jù6’Ìúç.ÑU|½³K"g×]ïì"*R$oú“œš3ç¸ÚÀ¶Þ±•{þeí|@O …âΨë-Jßù*þhÜ4ˆ\Sö‚” 1Ð ]$yáKè½àbšuQÿxxäŸ""°»T€Y/ð#_\ž‰´Ö±Ä§É ²Ã*Å2ï+âWêöôÏíý¸4Ñ:n½ä9pœBbƒ¸ídż1Ž/¨8 _.<õSt‘]†/âçW<åâFXõ°éÊ·\d/‚{5þQ• ¿5côÙ èÈ£›SP²§™!øDüôÉ©GG‡‰!ê çiá't©QÅÃÎñDÈ·ð5±Õùv±“/e_Ã$·Ì¾ÖsQKî.®'¹¶;Zg­zÛ;Jâ­Úçy ›0̱AQŒ£¶ ï9ð}À¤CžŽ–òýòŒÛÀ‚·w‹£œÙY¿¸Ûßµš,Œ,c}v'ÎÄIÜå_뽜4„ûŽŸ.¼å+xΊv•>ww³ŠºÏÉK?z3ð‹½£ìbÌ}˜à2o¦÷6Vy¿ï:\‚ïçäGÑÁ[WÞâkÁmx]Ð…yóÄŽûÿI¼JˆN˜lŒóKƒtà/†Ã×lЪH¼!§ªNø‹†¤¿„‚> stream xœÅ]Ùr·}çWLž2£2Û ô†ŽãTÅ’¼$ŠåH´“*;¥QJ(’IÙ–Ë?‘‡|o€F7îp0èRvœhƒõÞswðûUY¹*ÍSãèõÞ‡OºÕéÕÞðñêÉgcãÍéÞ÷{ª¨Ìÿ†xûèõê“ýC!V}Ñ·«ƒ{eÑ÷ªìì¨b%d»êDYtúË×{ß®Ÿnª¢lûº^¿ÚÔE'ª¾^¿Þì˺(»ª[?Û”æCYvë«Í~·þaÓªJɺ_ë>UÓ¶eµ>2}^nö…П¨j}ü|t½ ïÏ©É|NcÑ„çéQ¯‡þ¨{½ ׋-cEÌ Š²”U¯×!¤ÞWÝ™½ëfYV;†Fèe uóaÝ®hX{Zµžë•ù°júð°ÆŽl­Q6èax²öΉmk¸Ñ®ìÅú úþo´­g4ÔÓÍ¿þ²'뺽Ðäqp¬)â›ý¦+z©z~ûOôaRµ½Z?tÐTzÐV÷m%_5-Ÿ}ÏÎâ9$„€’ƾٛǃ±Óò:¸O= Ô4/9-R45ŸÂ®¡j›vÜOSK²ÓçÙwæ¬÷§Ã6S5Òžø!¤Ô‘dºFòÓ;…470ã•3Ç+Ô:¡¯1áj“UQ˾[ÿ´]!j¹µ\_M-¢6ôÞkjÙïÍIhÈ ¬aäòxÓè9›Va 6éøÕÍz{†}£ Þ\«LÑÞ.û™wJìgîÀñrf®åϾ¨ZÇŸìWFüÇšôý}:ÛÏ©ù¾·°,¡ÿ¯§ÉÞîÈ^sðï=ýO©Vöî}ËÏ.”;!C––2S}ø¤*𓚊;YOGw %;eÏàͳ³ºH¥ÖɦiZ»X&©ÛVQ׃{þnM3~m¯OÕÍúKj~A3b(¾·‘æëJðu^ÂC`ÛûnyUŠõÿ ? Â:gœn,ûØ`7œÆr²]½¢¨+wõ3v­•¨èN ö•㽂63v« Yua·µGØõD×L["0`{½tFPÄd£h[¾^"¬Rº5 L¡‚†á|W‚Ÿ£Þ_±3z9#aÈšLL¥çoµä~«…Œ–<¥§qþGß5·Ol;%ܶ°Ö‡ñäÜb¹P…’’ m–÷1œÌê]•j ®‚VÕë¯Ì\~s¶†õ.Ä÷õŽmTWÔ¥j¦múJÖ´ k(w¨©é´ÖLUkÝà_¼ûôœš¯øµ!Nnª¢)+$Egè `‹ûZ»):mwqMî˜tâø7Ú§eê©u·»l5?(G_ Q„Œy oþÈŠ`ÀcoÐ8xJëÄ›\ãØOi ØÏØT?Z—ë*“‚rÉ45û)>–¬š‡ÍH̸Q %OzÂ$bÇûý^sú`+Г•À[dL‡¥ŠÕÖ‰=ÌrãÞÀq]6ÈTtÍ ¸‚Œ]ëéœ;åß¹gôuuÑsá[f—ųgà‘R4s•TX¿Äv³ÇQŽ8p,70OÎSÐÙ< MÒjQ#‚>2ºÓï  •„hUk$w£¯,ZeOŸ¹Fó®ª3cl]¹}û|¶r­3׺ýžmÍqäZ/ÁlF‡›_Û–V, ?5¿ æ5WÔ¼GÍj^Róššo¨ù숚/©ùŽ€×p4o ­Òö€±#Àh?Ûf­KtÙƒú|wwýƵ>[²%â#ô•ë÷bû²æÐócðÙB: ®æ¼ýsj¾‚—ÀN{ZòPv~ý Rø5?†žRó>5¿"{Ïê15ÿš][ò ÜÓ¥Ò²-r˜²_o¶Sr¢>ÑËÉ€œc$?˜N Rëb1–!“Ð2 o¼Ó²nÀtç`’§®ußµ¾r­‡`›]‹Ý \á/l>{¸!žXãÜ:›îWæ^ÕñÖ³ûmeˆ\ÆÀ1~Ó%p¯‡éW'ÌüSºk‚i‚öPŸ-õnˆü6×ýÛ=¿åèÀ8+ÉQ ùÍ3E÷;YhÅÔ ›4QMî³Ú^à{Áki' Ùr~ü /LÑ.é¹@s.©Ò/©»Å‡Å"/ؽÿgù8 »7rÔŸîçÔdC¥Ôq·âÑ Ûë›{ˆâ ‚;Zbè„=“›Å*÷{½ å0®ÖnªÐ›G¨ÊÆ5³0Ë5³¦!t0ã®lŸøNpÄ„˜Èý%N΀ìjp ‘2^cä¦â˜8{梛£¢Õ§O!Of{AJ?úi/ÙÓï3üívÀù»4-©uª[’k]eÇ‘r[¹&c)æÇÝÙØÕÄÃ0*v8†ã‹„‡Rgî ¼wÞÆaD Š׸868CUTµ ‹Ùy[„ÇÎ*Ìùlc˜ü=ÇJð² ðÊç[vtg½‡™XÛ5œùGGn¥Yœ v¶Ó±SyŸwöŒ|¼°wÏ}ìäC§`Ú1ÒXSÈ•Ù[‹…!¸—ùÃ]˶Ër©Ø ÆÉoÈø4Ø ‡aõ 3Ã÷Ðh,ïWÈ7\„.¾Á³g¾§!Du0à„|‘³ßÜêA˜á(ˆ¦o.Á>¦R®ÊLt37©VhªGpÖ0ýíöi,#ÿñ–`#F¶HLBl6B¥iZg÷tÆb¶7}Ѩ Ú±_~V (šŸ••ä_Ru7MΡ&A%–ûh&“µ£Zž&³Iap(ì%X²ô0§¿qp÷×Þ ^–‡@Œ2Í®¨cDŸN·ÿ«w­?Åúm¢Oé¸ÓUÙ5­'ÐêTÙ%ÌÊeâ¦ì¼¥l=¢wø§ƒÁ×DY_R“YãŒ4YXšgœÙ b[”³Ú¼Ü#gµT"–^AL§4ú Noðugž±!Ö y*¶jæ¡_#Rè‡Ö½ôâ@1kÇ$ ™\5X‰3bº¢Œ¤Â!% 3š]ܸLwÊDI_y0”ÿ>V%¢ÀÛ’jñ˜µ î€ý´´9!@+»†Œä!šˆ‘m_„7œUA#&F|’=Q (ëBu.]#ÖŒ4LÈ=tÈiaÞ˜ @îåm@s{šî'H²Ãdèœý7ÃΞ¨< Ô‚kß4 l Ý"V%™“?ó a5ÃñÞ…C‚±,¥kºÇ2ÜC}mâîœQØÿûbp‡vªõô lå`z°Ÿ ¡Užý\üŒlÌi€¹X)eá[\:cmJ)E!Êv»ù€1,R¤¶xJˆÕ°6½Ä×s{b››WOÀ.wN Z¼˜("í` ¼pìÄùÙBŒ÷ýž5ˆ¬Óüýû~°ö†5²·~ìê(p•àÀË…Ó$²šæ9¤³„/h²I~M9ò5 aHqœªû±øQõúŸ}³Ò l¡”-€¬‡:¡wR™ÞhºèLä˜>íŠZU¢[+½¼¾îêá$›²*êÞTÖiq%Ä ;ÝoôöTÑj4n ÚNŸ¾ÙÈ¢j{½‘Ÿ´¼*[Ù ¶ú0gÕš`X­U ¡ŒI§)¼Sú7ï\O6<ûÑ}½ùVÊF‰ÄS‡çÔ<§æ©]¿èý±†íu¦þÊ %ª²ž­maƒ”SóÚNÛ6Ê.P•¢›Æ/Ù ;êPÝUTj¼ŠµKÍàábj&Ù±C\Ÿc¯PÆòwK^cŽ'p€ t]“—6LBžùÔNxÁêäå¦*VæÊøi#ûBß*ðÞFÅ9ŸÏQ 5U5y[C{×<¯ž•ö}Q—.™9L‚°#ÊAÐ;·1 ×L ]øDG—hÔœºãù&¹†ÃŠ uÑ&pK2J’S¡Ôç¤ëx¢²Rõ£<äÁYJr{—L×gt<wÕâ®ú,q_YèXúÝf¨&uÑY½ñ0GÁ¡€UÏ.N'ÔJ|¦÷mPûàÉDÖ(j{š;”*`D_š2q*(@4rII.ìTŽ`¤TEÕÕ(Ñê§_vb·ŠC#зc¡×¢p-B \iÉ†ÍÆkqÌ®€pš3êlÜl½‘¨Ñ@=ÃtöÔ'Ù&SI9¾ó°/ª²òJ(¸g•S¸ú>õü-™'!ƒPê Eÿ c–SA^ßÛ¦¡+«Ù宬åß™2â†yî”ý­æPÒŽM˜Êþ Yn¤ÇöÁ$Fî@O®÷ØŒ¡Þ¹Ãüã»b©U&ß¹"QL? ·Ø—Z?÷âC@àosæOVÍHi³ §ŒXã¶s¦}ŠÒí¦+ ã·|@«y`3báÆý¬{¿L{8t LV€WFKü=Ùþ°;ç­çÏiø¼Ú±>*ëXz“BÃozt'ð/Å*¹aò£*æTT‡E›,8±gk2H:‡u^LA)É30~Õ­h)UÌ­f(ï¤{·h¦B¸vûb~¸ô.TÔQaú+ Âd NSþfÄd‰Jot£·FÝ)ų˜úM$e¤~òûx­ÜïǼžª?Я¨°Äg /`|;Íí0uÞÈðϽ‰€Ï{‰ò O õÜ O7ûú8ë¦ëý_MÊ cý…²YD‡¡5/åÑ0’aRЂ¶+T5/æ(È’îVÇ ØØ/ލÂz†T[Bë½°3n—˜Ä:Ô`I¥[Ôûygç6ö0[Ñäâ‘ÞU½Þw0\~ÂêGƦª Í rÞk;³"¾lˆÝ^ÛÙjô í«¢£“îÞBUŽtÕ 7f©ãÐ=>úDÃG+×ÛR,Ú9µXÚXɦ£Ç‚”12[ᜇŽl¾SY(å\ìù•e<½³†¤b%59J¥èùÞ´¨ Úéš<ÌÈ×ÄÙáöJí2/¸–8¨‡w—¼U5…jåݾ…u)FÛ©µ}ݺˆ?›èhß÷ñ"ô5ÕuKOXí䀯Î@.TQ÷T¦qÿÌ33CTN¥BgÏ0‰¶lÀƒK)LhÊ2öL -,Jµ}Y ϯƒà„ï?ðžuógßšArÆæo¸†‘1ð˜r8q^º'S>Pâ[e8ÝóàIðH0pA•:z5ÈÊ0³,aPö8Ù Í ÚXi€Åïí‹r¹À$gQ%¸8ù„gÊ ža>YͽÔ|×P^ÏÃ5¬KÕ0ºú¦i¤Ýr}½b”Þ…9Üó9¸Ÿ÷2[Ý­Š.Èf6ˆ^íâĆ%6<ظÌ–.pHÍ”Y€SXËG¸¦¾”Áºž¹,}ðz¿M«:µ ·@¨Ê¥< š#ò¼å¾žG3~H=á#ÖÙÇ]Ãw YÅ2öþfkr°;„„xÊE¤ 0bÏTâ5)䇈2¡À8dI¾· äLÁüèoÂ+xÇ+\ÞT…jÈ#da‚H €“•ÂÈñÂpÊ(ˆçgÓª%Àü%ikC>|I€móg#ö¥.¸Ì\M¿PöNìC?ó¹m´ðe&÷gö÷REÊÄxc¡í32ìO ˜\L»ÈJLlþ@î²(5Ã¥Þa-a|{iW»½=D¥†ãZö€!óƒ˜^ KÐÙüæË\,cÙ~ð<àÆ@TËâCØ J°s?á`wË¿(aaÿþQo´Ñ7/¶$™ÎŒáÿÉ|ª•ˆZDa»¸˜ÛàÆ„F(Oˆ¡·kƒËkR™?AõÏkB 4èÛà0X<„*¶À§ºŸÆYóæíb˜F†ùl˜à8\gQ¶ o?õ~Jltz“Ž_óxœ-.‹R¡w‘½è%XR6?zw…‚´¢­Ø,üEJ‘§é;4}ßÇî.^Zë|—<æÕt¨xGšë¼‚Ý…’3óêktS­ÊßÔÒü™èˆã{¹’:Ok¶É°²Ê9¤†bòoƳ?ñ’P¶ö]h£Öcbì|_°Û×ï~Ê_ÄŽuâ„Z€Rkã3åb÷ˆ¯Œ¥HOÁ…3Î÷¶µ@Œ¹z½l$5xœÅTS(Y{b5 ù0ó²à"x]bt¦Œ¡„k(e…žã“ÛÆC™—Q¶y¿»x&ÖÆ:4:›ÝpÁ{µáíRèuÔf#XUÑuNÒâÛÈ¿þÃøÊÒY2Ê|Ýãlcd± Ç;újIìŠñm/x»`Ǥ™ÔßÊü Ù†%+g>ÃNŸ¾ gýÙÆùp4 W0Š®ñS“Ö誵šË~]ÉBéyú·Xµ~y/|ɉ%ûF¥LB©¿r•±—¿ƒ’-p¹€Ã°?Îì”GÔ—5#7@ì.PÉ<çl (Mž0 eªá. ¤´i(XmÏÖê@XœýÑcƒa—ÖbIVöí]@ŽÐ_d² jÖ³·¬"Æ›ýé®XB‘çzêFä‚ êù°@æø „÷-6 ®ÞaNm2ãq~+Fhv¼†Ü‹YƒIË <‘…mã;‡D1½±”Pæ/c-Ðy]JORéíÚlÈ9ÊfÈýùÊå¢g†\WvXøoÉÌpyl/ͻÂû]ýþ(X½è‰†4#<<Øû»þïÿKcj–endstream endobj 99 0 obj 4926 endobj 105 0 obj <> stream xœí\Y·~ß_1o™QÜTó&¥§Q‚†€Ø«'Û0´‡v7^íêZÙFÿžbd±»8왵dD–µºÙìb_U‘óvÕ2.Vmø3^œ¾>zü­]]¼?ên¯¾ýûpñîâèí‘c2ü×ÝÀ×§¯W_Ë~å™7«ãWG-óÞµ¶Ÿ”¯Ü÷^¯,o™s«ã×G߯Ï6bF)ãÖç›–Y.”]Ÿ¤›¯6fmË…__Ãs©iåúez‘†¾ßhm~<þ'Ày¤AqƹŸÁ÷Xšd…/…uý«‰zÇ„×ÞoÎ(•¢-“ÚpC”HíÆ.¡UH`ÐðEDà›nB©\ߦ ¯6…›V‹õMºû!½öàiÐ#4ÍjÊØéÝÓ0Ím"þØ@Ç/Ò幄»tˆþ ½K F—/7L o׿m¸e\µO€>ö*‰…”8WšI3²ÿ}"ø*M÷:Q8׺õOiæªr|À£†ó™hÍŒ±’PÄ"ÄxÄ ÄÎSrìÉÎóš~X§Á:†[g$bø›4ÃyâÛ¾»X Î1Ã[7®ÿ¯ð=à¸ñnýl#Yk[Ï×_§›Çpé€kÖÙΜ6ðØ AÙ—¥ÿ° ò%Õà¤wWøRX #l˜ªQarp%z£ú9:ŠŒW ù¤óܘõe¼9—bx~µQ+^½ 'm®WO°ýQž ˜|w™Þì¤1«DB÷…7$ÍÝ´i¹HÒͪÒî íç ü¾IZˆœÐõè£ÎÉ™ÎÒK4»ïÈYW› BrӌڅVx©z9}ßp­ÖOzƒÿ‡©šq®0Z‹0!<9þ÷üãGÁ/4R*p}jý]ÔÂÅ;Õh$W\-­‘ÿ $¶ãÂ'ëB—Ý—i".t³4\:ËœÁïÞ¯ëÓVÃ1ŠÆ3ÕÚQžO76øîÂÌÊ+¦­,Њ.of^ˆƒñ µ‘AÕ²•ÜÖªÀ¢‹ç ó½¤­åƒÌ‚Aîe‚5+BYu6t3Âp°Ëq?¾‹dþ¯¡JC´’"À#ñE´ä¼Ê¡KÝK²… ¨BKHE…—»F\yIšó^3‰åºœP µ©’3PB•2ÝY&Ú%Š3 ÖgºT”ØêÏBÙ„„—-CskCýMîS8»K%²›t‰^û°‘RîP)]P"‹ÔíY"+×ÅF6ãºUQØ^ë2Tx¬n$,ÝK*™Ãyž.ÿ‘È<&댡lªtV×òd@šqpJ{/(MÈÄtFÏ~…¦]+™.†u³’C ¼V‹èÑ(Q±CJ(œ*ÿ¯¤b?ÅILc­˜æÚûeÈ»@{ó¹†õÆ~ÂǘíÓUÇí 2U/C!YL%žaFÌ:pb“ öI8[—˜oðÁtw›$þ¾Ÿåö–ÛS” dB‡|4] ¹Ã,ÝÚ9ÚGԤߧp˜DIV?®©Ê5BÕ"ÜDvÊ&KY“’th9‘¢R&ße‰Ëꌙý-AÅP®ô ²û ]«¯áå@rt´GïOÄI±_TÞ.¸‚Æ+š—!“ õhá ´V×…ÔÙú}%÷1ÖGn§¨¸¯ âr/Ði˜æ÷•])É­+;~¹eú>Ö¢ÐÍÛL£´~Û@º.xV÷¨. *×½dõ@{”Cü²ñ-Êc¾dxì©”êø’±¿O¼œê`¿e$~?Kí¨$©Z6¢ePZm‹–»—è òŠ3É´ð\^œkDzó Ž`°º-\ͧÚOH$ªºd°O…ˆd³rl˜–në¡<6g;QÕ¾wÜ©õéÅ£„¥T¼ÑÈ}>Îæ¦ÓË•è¾å-¨ÉO1S1¢—V2§ýý™Š¾°„šÑ àb QEX¦Ë¿«Ñbn úŠY ´Æà'vÁIqd•X’óµ¼ìSûìlGJ©rcôgí'‚êw¨s¨¡ƒ,¬…NÔXXÀmbo"t1b"T®¾Ç€‡ã1 r¶x{ëÎJ¢‘Ä”¡VÓdÞåÍh³iIÿrZˆ|»Ї‹ú"–åb!ñÈÃÈE‘Ë'÷ôHæ‹8ŠƒêæÐ;È+ŠÏ¶¨~ÙUv ]e¸í‡å2ì¼!üŠÍ†êœŠ–ií nIÏ‘ š«ÇMª/;Buš÷ì%Õ›ñTƒŽj;,h†vöÛ4µ®·$pFcïÈ…EÒý|Óorjìü†RÕl'Ñ4"}Íû¬»Óíc0aGOÔD"±Õu«…“LdûØ¿ÏùŠ+*£Â\eZ’¶=lÏ”6©³tzR–ÏR^ê²|fækªûûöSÅe50a€Ë n¬Uk`TTÉÍt,L§.ë±ê€]¾Š" rÆŠôŠTtL0ïÊŠ”·¬Æ=)çô‰¼–éÐQ¬&xvfÅgá"wi¾U—.uÓíp¤YEõ$Šô圢ã½Ý‹äÌñE“C Û®¥£ºì«¥ïb÷+kê‘qÜ2áŽãD½ù“)i©ǦMw¿ïñÆAíP9„ET &mñý%‘ @|-³ˆ.’kÅxÜ‘¸“iP•ÐÌOÐì r5#ØË¤$š¹ƒwm¶O0ö£eüL:ji™ÃGfcãðþ%ËÒ¦J:ù‹—;$t{%óE‘÷×]Ó@ÉV£þÀSR9êÝOj›m„tsó–dM%)­òsq‹¢xr]ÒvóçÁ€Ö»HùR/ü–Jú9‹‰Qy€êy0@?”ƒщ/×,Ø+6*kÕ¤‹s2Ãbæàh…ý81ÇÒ6 ¡´Û+ý¥ñ~uÄ;I+Y>—é,Ц!ÛE±˜¦—ÖvZ-È«{”dAW"¢.Ùe›UQÍfVö†dZÝŽD{•šE¥"ûZ£|:Ûæ‚T‘´-pÕÖjl[óCÿg¶…<$µ-¸âwØÝz`Ó£x—…*nÒMµLA{Ë2ÔUúm¼JqkéQ ¦—dwôµZ“,ÚÞØàXf{*sAØžQ†¶=Í<çY4C!LÒ¸3c63Á!ôç;ô¦¡ ÀªQú\ý±5²v@Wc¶ãâ‘þ¢ÕdÑ]ýáP‹ÖiÊli3>Œû&ÚqŠÏIÌÏŽþþâ!endstream endobj 106 0 obj 3195 endobj 110 0 obj <> stream xœå\m“·þ~¿b?%»;½FqåÈMLH€ÃINQ°Àq1ìA\©üŠäGšukæ™ÑîÝšPÛUÖÍJ½t?ýtK=ïe!ä¢ ÿö…Í›£_?°‹ÓGÍãŃoºÂûÓ£wGu¡Â?Í^Þ¼YÜ<ñ Ý®Zœ¼<* çêÒ¶Š…ÕŠ²¨ëÅÉ›£ÇËËÕZ•ÖU½¬Veaýï¥X.Vk)¬,L(þíä¾G!b—þ¡œðý[Ň+U”¶tby¼Z»å÷ñÏÛ±„×B²î´&ÈÔ?A4[±t ¦s Úô늿Øo¶Â5<·/~¤â†Š7¨¸ â#*Þ£â*žP‘½í!oSñ>uíWà '°ÂM*Þ…#k'_ÕÞ¤³É—T 9¿R¶+aÝ5B£Êª0Vûºý]×½P%€µV¶.¬Zö$¦"[¾„Í3a¶´"Æë„ñEðÁ˜Ç0 BÖœsÒ„!à3Ðd@¶ƒ@€¢>py·ï €2´Æ‹Œ B-à%ÄêØ| ñ÷ÓʶÖdVÜ)_tÔTø•± nû9+ÓŽ3dFŒÒãÍβoÀ,a€EsSìÛu¡ïÚëŽsfaülÕ†¿u#*^ µïÈ“QãÐ.%j ]+a—ÂÏÊi«›¥2¥g  X•E%¥‘M¡ot¶òZhk×`oÓ©jÂ#}ÑÓ§Ò;Ã֘ʋB_÷)upÚ¾K¸ „~Šl``ÝS,]7ň DÞÁ~ôR+é=[Û]⨡$ŒÊö…’½3^ºR±®Çø4ríÂCLŽº˜ZF)¡Ϊv1"ñu2ä‹Ù˜éŒ²*Á¨ü%ìÇtðB¤G`Ñ®0¾Œ½5›rмò6±´Qî(äËå6jd=b‰å·þ :¼!‰³Î8‰Ô—Cæ ûÚ‚*ÈéçÎ^|vö~³Ç웬«eVUpÓ@õèªä`²‘² gu™#ñ‚¬r2£=‚HîŒê;ƒ2užUÅÊ€#•Û¡Qˆ´¨ñe ²0Ä=*2±b0É"Iœ¢Ç8²Ðždö¥»ÔÜsL†Ûœz¬UˆUf2Œ†#ç9-EË” ó²p%A®(Ö†©dÎqT^¶^ÉÒ…CE-õòwQû–ŠS'oùAB0425ÃJ^Û2ro±É4¨Æº¯°n£ØƒHó>rÅŽŠú$©'Tíç—\”ñ&ˆÁTG\በÉa\IŽð2 ¯mêNàð×^“Mda1T·Ñy©U]”õ—µŸ0žþ=Ž™wE&ñYŠ”Ê{•ºöÞ`£³Z:нß_áhqÊ)ñÅí ¶oïè]—p\XÓÎ !frÈåž²“M±…3)ª²ÐúËZïŸÙ2“h|Tœgm2>Ü‚p@r’Ä|–n½©;;ÙRÓuÿŒsÓ+Ê1)V•uð]°Â·*¬û¼b…ƒÓ¬\e½SÝï"eù¼ú€ß‹=JaNLöŽÙ{~ôW,»ÐøªFr맨Ú3AGQ*d˧¦+ì/Zw/;¨ ²)8&ÖÀ½ƒ8âwP†pÎç^vzÕ gãç1`E¼™S‡Ñ v€«2ü¸ˆ‘jÊýÃП»JTXȺ“ð¾C=o¾èÌp†¶¬ûª×÷øÖ¼‰6%ŽýïÁ×~¦r*f|H›¿óÈSÉ-”Àˆb¨SbÇî·!þµÂà1ü´’®"œq4V™¢Và “KxŸlêX7ëý¬ûwþÜpØqÀÒˆÏn¬¿ 8BÞ½ †.s±´ëEÏk/Èá[SwšÌ1XU ¡q;Ôˆ8ÛX7ÄfÞÏB;g>¯`¤˜ºÚ’9°ÍÇõ`€-§‚ qàXÝžN4cZx·åžï-X— ô^$P8U|fÙøBÅtƒÿEoIŽn†ûþIT.ü£#¾læ‹'{¶vÉ[œs¥Èúà(ät­9Sò••µÿ‡Aãøës4ê$™&Np|Éz*á ÁìÆñJÚ`¨ôò/a·¼y«íÿ,Ž‘ÛCÌÈ1/¸bÌ*Ãp@hVNÕ´rSA3ø0œí°ÃUvæ6À|4ó°tI.Ûìm«„§eƒØ$S¯ø~ëæÎo F‰±}|~Ñ1ïãÚ1uÀþiyR•¹ò 4Œ(L.’ÌMp(½»~Ùši câÝÿîx÷—ðÛ®œIÒI§R ™š£]2ýæ2Á†k˜€°7À3f5³Ù Hwçn˜%œ,ÛðX¼Ò`¯}'AÒ™n˜w²”N7LzƒìœmÉ@'#œ °Û–t k? Iï¯ .»‚¹3>_ÅDü©)ÄW°ÕV+Ö}Häø6L]È<æy nc0‚÷Þ5Ø'oi§TÍÙœŽ$g°Í4T…‹WöHÄБ'`æµáîüu7™_Ø‚~âÍL/æŒ=©3 ß$àéB8nÜ= 9ÔØv¸Â>‹ºó^ÞF…loÓ*Ë2käXéÜ+™Ú¬œtÖÞ³Ü î"îq™lxcríds½‚'Ô&Þ¥žöØÌ®ËH4öYß´:ªš1ƒ8í( m.N£âs÷a°“ËÇó‹›ÁC`Þ¦| E~‡ä d>"&0Ä®^Á£ùÆw>§¬a‚ ÝÆs` £L)ʳWgƒÍ¿ŒU/"p\$²‚V2Ê„œØÑìÞ"×áà c2{d±O~6Gx˜g&!áhš¶PªkÙ‡¢³R‰bÿ‰¾'kQÞ½_ÉByu ŠéIL%­dI›îN¿–7b V1­à-U¸¤"Ë+ ~YBßm²$™¤é!6Íг)©üŒS…`²~’K;ÀÆÑXãtû`‡7ï™5æ$8ã/”´d`”!Jß{eéÀƒ‰jÔŽ7­xÔeÍ ×] ’âW¦þv|¸™Ð†À±ød ÁÓszŦÁ:o:Ó Ë'?a[ºC¦Ó|l ÚR|ñ&ûÝ ,†WLÏφ;ám»hæ÷ˆ1LÝDóÂ{3Eäç#€]JB¥û\ĵ,K¯$ó) ÝM¶ROÒ-š&Ö2jž*)eÈ«¥•ÉYã„Íú˜’ÞÍ æ؈MÁJF|?Õe;5Š»#i(­çÂwH °üUZ%Ÿ‡¹€CfÓ'ø¯…7ˆÂªÑnî”.†øLvcg¾0„‰ nËêb˱ÈïCDc…Yáë?ú2-Sör7 Ì/Ñg₯ &˜Ó†8ƒ|ƾҺ1(µÐ·¨$4£<’ïe0DGgÍhЄČ"#˜˜ÑXá \ǃ›Ñgpµ7f_eÙáÀ4K²3±°?G_™…f‹!Øû¹Õ³|Z(UÔs|Z1ê¬latY…ŧ|ÙX3Øsï¢I<}ƧëÂsèJs¾|ʲhwfŒ|ixðH «+Htlµá„º¯Ê+P³1¡žüÄf\QN¡|@±Ú–^³Ë°žÎm|!n&¯tŸS…aI?õv,ºRO}âF#B?ÎË~¯»Vï€Cp Q³m>P‡¿¡v•c‚jøÃ(Œ0Cb‹ƒ¶O²ßÙuˆG0¤}wØm8PÚ100ûðð+žÚ¥P;}?âéàþþÒ>sÆÄ÷_&Ý&ñ5ã²½$Ï6L‡Ú½gúZŸñà'CL8›˜t)“°JV6“(ØÈWº0}ölÒ’“C·†¼àdá+!Æâ«Ãò8 C‚mçe*y2‰“Svøí¬ (ÚÙïÃå¬ÃhDôØáI÷7+‹¾¨l‹ˆ‘ ~ìÐ:rÉͤ².„ŽéöÉÑ}ÿïÅŒˆCendstream endobj 111 0 obj 3936 endobj 115 0 obj <> stream xœÕ]ےܶ}߯˜·Ì¸<q!@ÊO‰íÈI9•XÙ79å’vW+9«Õeu³SùŠä_ò{xÀÁ€äÎJ‰å*±8$.îÓÝ êõ¦®ßÔöÏtqöâäÞC½¹¼9éoo>/Þ\ž¼>i+aÿëoÐ볛ߚÛtU§6§OOêªëÚZ­² ãj£Y]ióã‹“GÛ›Ý^oßîêJ4JÕbûf·oªºf¼Û¾3w5¼ÖÛ³è© |á¿pé/ɳÿØí¥}¶oáo§—<_—ô€y‹p]5·" ;ÈI·8Ö½†£%AfFÚ½ðjÜEøDò’J?Å72‚ 8‡P&”Tã’ UÕªk6û!H:7™Â?Mðª¯}p¯ye€õOZÇ~b~9ý¹WšSÓ—2](>"‘ÕÊ w…õ×ë”ò ¹ö>u!m`€ñc$~–ŒŒ[µQA9»5…IàˆÜ£÷©6[¡š¿~c²¸Î%q`9®m.¥¤Tm”YHÛkÝšÉ4²­é WÆîêÆä‚}+0úÁÆb:™JŸ¢a]û©“ž/©Å>YUï¥s¾Ý½…£¬7.ñ âëÌ B_Gl“Æ®§ yß*þ}ìž´dÁN®dÖïJ"†b;ƒ^à¬W?ÞW '9œ†‡)Ä\·a,Š5ADÖ¸àÇü?Õý=o›ªiÀ<2‚"Ëëìà j<“•äº%ÏÚNF*`­/ƒX‚3t  ŒÉeÝ@Ñ–ä¿óµZذXuÏ^î€` .º¤žÎP]¨kÎ,°Ÿ¬bŸhÏ´ü:ß8Î#Ì~'=zîüƒ¨¨:®Q)ÈÍLøíÒŒ<ö8Xàsyc LàaT9§ àFé ²Ô¤_†îÀ$ïôö—Ó“uª¨{ «(雹‹ý //áÌLÁPF0ðj€SÙUš;m! B$çZò@Lì°Šs-Œæ¶B~°0gúÑ‚ª3Qò·^›žíc«"…uìK*ÀaÉO«I–g3EÔ›¡R®_¬RlJ ß.‡F6N¥`èÑô:ÎÁ š4®ùzMÒÆÐ•–®q¢PSëjR£„~‰y¥pTîò&ŠK1èý¸5#­um ›¼KöåŽqKæAv…^äÄ>+'vÁ¤û\aBþ¸óC?¬ 4똴¬{@Ü`mîj¾Ln†LÃ!ïƒ×à™äøÆŽÚÅÑnàÖCÇf”lϬšñ–®1véDÍ`Æàìþç 0Ýn}Ÿú—¯ €.ýRßä‚#)Nƒ£¶«š: b’g™t-g8ó),u¢AÐ]Ê9##ztÎ` èÔµÈé©o–ÄL¤‡¸ C—ûµ_MlÃov¢íÒœª«¤ÖÔvÁ~à’(ö1\¾³r ¯ö¿¼‹í$R"ÜUÀÆ¡„st8 X’=ˆïž}¸Ÿ . ž“þ'tä1,Y¼uUdËžl‚‰D7é f–øÈòëa±›(„½`– S 1I·}‹¥Éc;âµé°ÕÓ|‡h_ÌùCg늛lÒÛÒR4RzŒ~þ¥C£Ø‘ê9Ž@` _ŒîÿSà¹Î”g‡… ê¢Òì%„yö߉3t”\þv'lLÙ±ü}"ì³¼‚{3X«+Y;-¹£ÙÝvM3‡Lw‹3‘âÄŠ¹î˜p ñu§™Cn—f´ð=ot¥µîáBDÑØSê`‰áS£Gýïx.±]fÌ ¡/ykò"ãSâÐFÕ¯è£înn»–DÐÈu êbñ޼Õï$Á¬v¼½4?+T£2QYÚxKæñiæ²} ¨†hôÄ‘ã3Š–†çH۞Ǥ‘$¢ pà„µ3}¤_âpŠñ‰5ÈŠàœ7öÑb0rã sVÙbŠ‚xo÷"£*Ã$tKª{ˆ¦ ËÓ_è"Y©\¥!ôã<2D»Nð÷ iúíÍa%Ì¡2ˆ40!G|òûˆjIãWHý`8ñ%Ìão¢ ?®]bøØnÎádJœÚ’J¥@<‘UÛ8@Á£±ñ2bK2ë̱ýã™$:NÁÄÇʘ‰nBó2L{]Î2ó‘±Û#Úä’GR>>F ãÑ}«ŽêL±’‰DÉ++E‚Ñp­«®ÓCT›¸@© ¨xré®Â¤! iü;Q¹(Tþa·WfDîÖO´˜ŒËÝ žé6•%ëXÃ%Ñ F“"q9.œªYènê}ÈÑIvo$ç‘çWœÒ‚ª $\zKЯOà+Ãzz^’Þit9´ bCP™©…ŠŽ3¶+Òç`äpµJ°3˜ÙоB¡K´Hºßˆ"ê"üõ Gê™! §€³ °3•DN¶ƒ¹75O°kXzº‹q!ÍíÝpž~%ú*JúZ\,Âþàg±rMˆPw‹!‚··&rˆPØÁµ¥¼0¥#k˜ß{XÇ…Œã…!Í$ŒãkDq fb ELZÙlÔh<ã‘—ö°«Û(G¥è1(EùT ÿûûøf,GÈ€]&oõ…€qe5> A˜\\m‘nDZNÅ0çæ§H¦p¿K¶|ràÛ¡Â]rŸì·U-ú"6nÏ@÷+óŸ3é´ä~;›²ã £mûðóVsò²¨LñxP$õœ{®ŒÙ„J£>3B=0$Úd'ûe›ƒ¨„”lOznÖ‰~Lm¹2z¢¿†Yþ¿ºôé~Ø2—µÒ¦ ¶ýŽfUE)äñ¶Ÿ˜n%^ÑÕ>\#X$7—;#dýxh6ÉúhÀzª C÷2ÝM2 rOÄžncà—‘7ö/1|útûdgìAJ¸<)'ªB¾€wòð¶ õ3jO ™É%…ºKNÑÜý=Þ¸r„:oØ)ÇÇcðÉÅ$±<:éö÷žË.FO‘ƒOL2°`WƒX° ÝÎûÅEú…õW>& Âì0ú–æ`î6jˆ»\êAcÆeð ¢æ´h,8[^â†äH~k_Rðë6…ß!«Úo"Ê6@³çRN%=P¤óôNãK¢ÖQêÁö2èç²R«KVœ›Þ}SZäe™DJ°+3á Žù»v„[¾[ ZwÏà 7sôTÄàUÎYÓƒÌ<å•1X30Ü[hY·Tq\®q Ï3˜¼™ wÙÜ:Ë"ŒºŒ-«gm–o&–5cë0 Ès–ˆœgN&$f°u0I[LÝ®œð{À"tKB÷dùÇ·2[ÓÓ‚ÓX`í—%Ö:«ÃE/ÅÞŽg^•îrdOÙ.oÃŽ|"ò¢|À•±«“ÖÕ!?ôo}k¿sb”ß„ƒ| ÂOþ5Ø€‚Òûüˆr+–=÷â·Ž0m×C¯¨+é7ÿiÚá†tè˜e¬Å¤p}´• s-‡ë¥Ö„M8ù£•r¨MÆt5ÁK¸EŠ ÔqSj;–Îì³h"S¾×äÅõ† NãƒVVµ•(µ[WŒA%+:—œ›òv‹¶oS¯»Jpgê0,¢ÆœDdÅ©ƒYq.Þ¹ˆpn7£'þxWq–CôC‘Â̺³£ØIN´î»«³J—ÓXœôÜ&g¡Ž»ã¦…?¤.èMPk°¿ÃjPÑTMÛºTþÏÞç§üÍΞ<—‚I¨Økå5v*kû‘:fnjΓÇ~®æ®<4g03ak!ÇÇ¿iünÖHxk”J«üHxͤ ñ…˜yÀþ 6kL¼çO›}片þr5ïºiÔZÎ’5þÌF0ÅoÆmj÷RzNÞª-Ùúyz #,<[*.ÁŒ‚-Þ5À„e¥ƒßK_<1Z~DƒÉ{HÑ…1!:›ùM3àqÕ'êÆ“ÔniD’¦¢ÉàIñ„$«|± Ñ|L4`½z‚h¢û±óaI%'9#ÞÒ`bðs0¡>F€ž’çÓצ[ÇñVW\H䎂ÿ”GÞJg‡îƨªnÿ/óý¥Éå˜iÀZÏá ßPÉy*Ï‘I_Fdp GS¥Nd?=Sßžžü`þüë.å\endstream endobj 116 0 obj 4017 endobj 120 0 obj <> stream xœí\Û’·¾ß§˜»Ì¸˜vëÐ-ÉT.°ÁNRãTlµì.»8 ¬aŸ*—yH¿ÔŸZ3³€IÅÆÚú?û‡E]1¾¨Ý¿qpôüàãjqúú {¼xðÅ0xuzðî„û¯{@ÇGÏŸnìBƦ2íbóô ®ŒÑµêwe mŸÓ,«+­›çß,ÿºª«º5¼QjyhÇŠq©–G«µªêšµíòÕjݸ!7Ë—þ÷ÅŠ+ýÝæ/ö8ãOuÕmìQ›c»ó§«5¯¸Ö²Y>ÃÛvØVR0¹¼µ¢²'›åfµ–öJaö¡â¼ß™¼ˆ´Ëk&Ç­«5cö*Ò,»; ^«åI> ž­¤{jäòE÷P4ZЩ¯í«Ú kûCÛÖbB÷Y€'ØáaXö󊩊InôÏÈÉ®d=¹ÜiXÖ¿‡þë9\˜åeØà^‹@êÇ•©”–vtv}bØö^ñ8,#0¸ 'Âeðâä÷Ú LU«øÜ z‚Ÿ ·ÐäH‡sVqÞŽÔ‚¯…¡Hp÷¢[Ö²FR”aìCJù¬'ûÖè<ýXú° ªçé§n–þ9à¡°#¯¹±Ìت…Û¥•=䦼c–oVšqàQȺ‡˜ÎŸæød¸zÃÚ¿¾ØÜ=°ÿÁý%Ý›:ÎBŠJJ+_„»ˆÅâ…IwOãÄËð»/¬u¸äþ!s;¹%•âËo—ø/ ªŸÛÉÒ½`$en@8Ÿ{ ær5怗—)ñÍœ‚é…À§—²‚S +àƒ¨ÇLJ.KtÁ)}êç;ˆîm:éÎ$³¶Ÿ$ÒÝþ²ùÞ¡·&øµF•´µU¤½|U£„\Þñ£/üháGç~táG—~ôØ^ùщ‚ýþžý:ŒxäG—&Ã_Á>·ýèxvË6“gÝÉk¦­ž±ìãtÇ0áË~ÄŒXÞÇax# ax†ax†Ãð* _„á3¸¬‚û¾ Ã×¥›‰0daèDõü:ˆGð5î†áça¸›ý«tÚ¿ç—6E,ȸóµˆr $D´ 5F=rè ¡ F.¶Ô001á½³"€Â¼m*Ö6Þ¾ýê]:ZÈú'Ê&ãheõcÎÑBÒuG‹ wE¤çáiy{Ѝè·áj!Ø@}…]­ ªŽÐKDžƒß6gõ¾Vmáî ¦h%a뤭Ãzvà»7˜óZRÿfK ­n ]ŒFòÚW1/Nç^EÐ’.¬"äT% Ë•ÃÁÃ+e'«N‹ÖšOÚDqMμŒ œô:Äa++´$·~×B,½ŠÉ1ã4>É A&5€!þDTÿžõ1ÙU§\O =_¦¸˜±0£Í<Ïmál#cso!g®v ïxÜ Ì,-cÿþÞ &Ë1–û󊛊[’)ê‘Aýó‹%B`@,‘10òs9}“H7Ϙð— ðTÝ4ôÕGAÄeVý_'þ›Ä}óîÎ0Ì~ׯ±Q'Y×\;Pªß#¿o1ò‹½2,Z[D”ý0Š(wRO[FišQê•4 ¹X*SaL.>S¯Çƒ×}ŒzŒ¬Ñˆ5´òƒÎž!¡$›£#¯‹²¸ãrœ$jDË‹lŒn.:çTZûôirÓ¢9 £À@Tz'‡TñªnŠi*Ç‚É~Ô9m+!j=nø¶=R`L 0p1à§êXå]A Âc¡SIzVçf˜1Ùƒ/ñl ßCŠÀÆó¶$o²_¸ªO9Ü!‹…Ñ7#ƒÃÁl,Õx‘8µ $öMÝ@ø0&Gb¨”W#. dÀËýàBSG‰ÏëÌÜ€T†…F(!ÃÇaBg;¨Ú0jP܆ÇãÞܲ0úÚâ°©t[KjÙ…é°ãIÐEnHæÃwÄGLƒ V$µÂ‡Ë¨vck«ý¶áêkªÞ½³…ä6…zž( ¼E t¯°¹.&œ]ì V›†OœƒŸÔ9˜hèS?‚qÆI ;/&xt[åñ¸¼I Ç/%,uÇ[î_¬Ööud£ŒËX#¦ÿ½0ÌéaÿtbåI›Ù‚€¢…s?\qxcøò?«µv«j‘;^‘“DÈâH&è`DÓ±¿àkõ”ý½¤µG ­;†'wždLå \ü¹xÿ~NÀ °I^@šÏ%Ïeå`ʾ.Ôǧ"q°™ˆˆçÞSkã&Mmžøg×J°ù˜Ã©µ‚“‰ù§ÄáùÎB>Ë0;r ¬±%C%ÍW¡àl ‘´…k@ˆ>-‚Vx)ˆPª‹9™Ìd“ àÐbl‘ Þ„€Ë¾.V—&H:Á”h¬VÕ´èìE«q¦å/ReRÈq5/cW€Iš¤UŽD]S€ìd¯ÝH:<ðª±<©œÏžªJjÁÔ’Y61RÉîô¦v²Ñ¨®ZÎÞéÝqÑi?•9>·,¢t¯ìušV²ÎiìŽ-]õjÅ+ÑšÞfÿÜ-c­j¬æ;÷›¢³Æ´ÒKP ”i‘°÷ ±G4h ^Û‹iµMýeo£{‹ê5ÔG±þ¨MÈoW‰™¸Ó‰o•Q%¸N†dÂ|qÐ1Úd&^ŽË4oí¿ChLcŸQq Ž@*Øp9c%¾¯]m”³¨û«Ú`[Û–¶–Ó“øJ3ãKÏ`Š•â¸`Šêeƒ¡üF4!*ߨJ)5ÑÆ¢uf¹É;ø±ØŸË5ż‘.ÓG3Y8·„#_øba*6‘ 0/(_÷¢Æc‚äámqïKïÞ?³qQðDþ yzß5Nòv²…óZ'½€Ì’ Q’ko—]é cž8ìE8£WDèû~4’¯ @Æ-Yþ³¥Z3F6Z#‰‡ð,â@wÒJY$ômÂí)¤ï_B'ƒ¨šîã¹T&ì@«İê³Ñ/”=©¶vŠþTÿH Z´?vs·¦U¹©Þ Ã`%1»(B±¶\ýÏ*Ä$Šá{½cúœ`Ï)Ár¸ɘDó)Ö$šoߊ²íA¼_Mæ&œS!&#ŒB_š‚á9Ò0zíu/’ãB ×=ñaþl5•RÇì„ùp„Ð…ŠF‘âÜ40|QœÑÂ=Ä#âÛ£8$^îÓëu««ÆxèŽÏïþÝÊÊ®*lø‚²ïÊ(eÜ *厛FÀôÐï’¶§Ãy¾rˆC±>\“š ûÑvVh0c|cÜC»ïZhà‚xƒØ“¥ÐM®+«îEžÕÎCË:AbÔm²Tïµõ)×Â~×+8L$XQTÏ»t¬DR*‘‡ûº2ðÒVVp6&(§!ÃP¿[ŽÛ2¸æ•z?©@GQfÊ4E„2ã8»SJ×ñÚÂXi\É7#èfzˤ õ€ï°· Àh á¾$÷ÕŸAºz,¡Øyeªå‰GðÛ+œa„‘+rù•þå ¿ÿa¤ÁHÀÙп״À};áà7zä§ßm"HLT~ÛXt‰sކa¥:v•iUÄ>­¾»„£#Ý“<ÃÔYÇ›a…³æ8tð¨7L; ÖÈÊþ:ß’½E—p!{qw@2™€}>T1\|7)i}͸;†“ÞŽÛ±ŒÖ#þ(=ÉpüNé×¢tÜè‚‹^±Qt?pUø¤áÍu+ç¿oñ‚he{=^ð{“h]Ö|ŸA¡bÖ%[é¹H¬Τ–3+‘ß—«qó2‡\Ä\ƒ$ò!‹’$—³î¹‚U† Ø€ÌP\]*0Ä{y<Ѭ †‰ÁÛ5‘&QÙIýâ„O¬Ñ­g«T\ʯÕsU*’¤U5²nÝ‘ãÌga¦s0í¥¸qÑr?<½ô¥'¤²ä )=Yôe0J»epÍtKV¹üI¥dÛ8cÑ/: {…©tBX6–Á(S*\aFT:Ô­Ä_½éÛCz«sˆv*E»Çß„:ð¸"cxHRÚë¤#ŸJ&[Á┣¤€ã¡†¢±,«ßyËEÜm`Ag…î VÑi1Œßäù`‰ˆ\ñ5>»èÂ̶põ:‡oYT€‘RÌJ=ô]·²zGa¤=«±qc5´¢`fËV[¤¦vÁnÀ-}¿­ÏPËqqtq4*A²Ri25ù~')ðð ¼£ïUÅý¢°Ë4É îb‚¦%Y)áN± )ÄZZi¶5ÎZ:Ðg»£Ã§jD'³ýáA‡ô’IÍÞ"]æL Ðß:ó‚BÐø…%V±‹³Ol½ï< KS®î‚B†´â”¾óð6DÈÔxBq±Ú¾ì¢¼Uj%÷†H(=­„迯ä“k ›[ÕVž5¡ⵟý,Bˆ3Œ\?ÔèÛúðšÑO`ÖÐYË,…Ý.þºOõ˜%öÎø½ /AHë Ö0ÃGADCËþ X]®|×/ ~l\þ§RZµË[+Î*m¥Í§ð3~všQÇ Í=æDø88d ÜiMVBÆÞ-ï'”´@÷ ,*"®ÙúA”¬4S“o®¢Ôý8倠¥…>åç”&nygÓ?€ùÕq+ü4sòerÓºÂÄt±=Ðr‰¢ÛN?qÓÉÔÑ!%ß¿Ùw_w•þë¶í: èNxøqxÕ¬Cfï'XF§D´ .°@[}œRÃ4àC* 3wKÁ×ô®`Y¼ÌÖp…tÙ±MW™ÈýA ­ª®eƯíã:ª‚+&ýJbç&MYè*VõUâ{€4W7°Í~nÿ—‰³6γ`Í;ï)„ž¢÷¿Ây  q•P¿™ï„ô:Û±L‚üɉT9Âõ@³]‚)j6¬ªDPõ5aè¦ôùMúv¨Tf ¾Ž€·àײpñ¼E6ï ˜ÛPsLygsð7ûï¿-i`.endstream endobj 121 0 obj 4243 endobj 125 0 obj <> stream xœí][sܶ~ׯط.3^š¸’°ŸêÄÓqÄVšÎ$Ž,ɲkY’mÉIúßðäîÊ­<g&4¾s?`Þ/ŠœñEáþtÇïî?/gêÛ‹çß¶ÎÞT¹pÿÔ7èõñ»Å£Cû c “½8|uPäÆTEÙÌÊŒëEÉŠ¼´?¾;øe¹ÈVBH‘K¹¼ÎŠ\(­ ±ü—½,™àE¹ü˜­JúÓQøé:[1fÿ¢¸›¦ªŠ¼ÒtÂ_—ÙJåEÁ¸Y~•q;’Wry&x“IwS6o±#¹0Ë«ðû¯Y˜€NFï?lH«_̋͘ŠRA.ï‡e4ôHážò7Oê U‰$içþ÷£lÅíìÜ”Ëÿd¬Ì™,ôC õ2ÌCÙèǾqïÊ,/ÂϧárÑ2\Bÿ}Ê€þõÀ‘‹Ã§‡_EÛû)³d”…R”Œf™ÂȰvS¿F+FŸ‡;x¥åÓq¦ŠÂ‘rÿ¹ñ@dB䜕…‡'x[ìK¤‘¹*ÅȾE[ØÌKÎ8Ï ¡º‰áΞwŒ½ ‚PsmyˆvƒÆc¤?uÄCÄ7¼ŒÌ•[,UƒðA[HÝ1ýUXç9„ô™Û[-³öï¢ÈWagˆX«È «£†EÖ§×úßVª4Ü®ÃJŽÃÝ×™flH³É¥ÒAž, KLI¢»£ÀÝ S7Z¿Ru®¼LŒ& Mì@‘Éh´ÿ"[™åcï¯mIó‹Ô;Éb ÈØN mØKLTRKNY@Gû“à(|ïL”4VµýlwQå•.dXP­ †j·ŸªÕ+Šsª>nCmê "Íë¦PTÇÙ'íô‚ò†0¯µî¥â³B¬L.¡!»ö:µ?äîñˆŸ Ãù3I5I"]ü'vù1–•ð2}©!0?‚/Æ|ž€íÚ+Ten]†7ëQnŸ¿—1žK^±áÊÖø‚Ï‚ßûi ¶¾,ŒVË‘ÕÉ’GrD”óNåȪ*S*G¼dyÁör4[Ž®¡ÇÙ¨„÷¼!ögjû)æêôÛìŠR9W¼ÃÌ`Òm³ˆÀ¸wb¸u§D$Ÿ\wˆ{E€‚ýžÛx”º'Ú‚=šî^bÓZÇìý˜Ÿlý+YÕ®65¦{®Ó¶ob÷­þ‘¬r^Ûwº˜#Jò£Ô œö"åSaM»1¸¢:¦U=‘ H5›OuÔ#Áѯ÷Rr?#S——nñôYQ”.Slͪí&]êcgs÷ˆ™¨„XQåÜÇ™¦ðÀþpr˜¤¾ ñØ_1Á ÅžÒ©Yg·'&g'–RHu¢—•îÕ!†é/Žó}Qè§V\Âñš¾-ç ÞÅ5…d´e=Pª ç:ÎÛ<¤Ê•òü9QÚþµ¿p ´fÖÎÕ$ȱ@GNçYiã²²ÝÙ¾2Âi d^?KæÂÊfÓ'$m¢TÞ ýb줒{¿þ]½8kEÕízˉšL:û;Rõ©óib$'Ü+<¶wóZ˜°(±Åã_0,üc …¾ÉÃï+qÎÇôV¬]¹½wµÏfí?|—‰á¥+åo!0Q ²ÑhÊ®`Q€HaôëPÄŠoâBbqÃjÒ'‡Y<7mI!àAŸ7ƒšW­Ë‘ô¼ÖO‹=¯È‡ ¶+°|á uPUõBÛxdjÇ‚ï€;,‘s0!‘®¹U{… z7ÉÔjJGÍi#¤Œ%µ§' æ@vXäu—ó‹¼Mœ€RcU^ÿ²±2/ö©M sjÝàwRxSœ`—P¬Vßk³uÛU"Žg0þ†å^â2m½&©fÔjµQ´ó$ÔJ£hO•¹¬Ú<ðAHй03îYû^W 2ûS ÿk"Ÿ¡€Á@{fS €Pµ‹¨Žz„´û*BÔÓÒèH¢g$J)‡Á°ÿzW¡rü$‚Ÿ«°¸?ÖwåJß•à‡WyÙ«E~ÉÁõêl»1Û5{Í÷&w{a]Ã£ÞÆ½ ÈH*¬@Œw{1­¶*¬“ç=Å ½u°ÄqƒÄvÍ;›Xû«ÉRÞ‡±dø†]FŸ•ÝLð’¤;"¤ ;‚F½$k·›ù»`—IV½Ò}(@»Ty!d¬]’aëwPÚ’ÞìD­õJtÞƒäwûá[$èDOÌñ÷ºÚpÿT¯k¹O*VÛÇAУÀªÃ¡÷! “DÞtr9jžLÄW„YT¥‰h›õfññÞOt`ô¸û=7 U•7'¯>c§Î0ä,u!K’1GHäµ=ñLë¡÷€wgÜ^xVnvzdŠß3ÜÌa¿8²6DŠåâœîS½.ÆÔÁ’ñxq©@ú³œûh*,¥ý«·%¸þ }Ju•ÜU÷ذº‡›ryuû]¹I¦ãXrÜ‹k_TluiAj©¥yÙèÄ[ª>C¢Èi΄X›<ôM›;¨ãîSF¸3”ÎÉ fâ³pcGqf6¬cNtj­óð¡ÁÍxõ÷À«o‚£ïî6\’vÿ“^c\ÜÄ7§‹Ÿ»üö&¼ì¸o81${GDwɲŸËžuSSÏ–•Ìå„6Ý1îº4Ÿ“Ea¤ï±i e‡:haõŒÃ´ä‘©¾ƒk†.:ñ¹vÍ @Ókßg.°4f˜k+øj[À`³å—82„3àM„•¸‰5àH»ŽÊ%WÖåûÙqØy°]$Íðáî]îÄב¸r¦ 7-!gb‡plÀ'3„½Æ®M4-RÑ!³¹}]…2§§Ã{A)â€-™º"3\A“½µƒì}UМÔÜÌâqìÞÞ»-ãù"¨¹'Z?ôŒçôfµy= QܰzC%¡’qäö>ú°”~sòV=l¤Ïi”ý ¥Û:¡äÃñý¤¾îÖA„ÿŸ=.Íugô¬Ùœ-‰÷zî¤íôÜι?ϳ¥÷¿?ϳ?ϳ?Ï3× £Œ,L´‡Ô …ÜW 2ÿ3‹3iσ¢Büm¶ÒvŪœg6{_޽‹H#hÝ· j‚C×]/OIÆ<…\Áß¾!4OÿB(¢hír¢ZÙªã|\ˆÇ;ÎWa¹ø»‘§p—ƒnè”—'«—Íß1¢P“6]·ˆ(BhPçp[ÉÃ0{ôláö¶›+Ä]¿«c¬º†’;*ºï´Ý¶¬’ŸÙºí¶MךÈ¢ƒ‡cÝ¶Š¶Æ%ºmëɽ»¾Ü÷ÚîîÆlÐp‚ÄÜ჆_F§´qcÕ„ä°¥hÄ^N-ïì²ObNüu—•¦‡#G§¸kÃê(â¾H„÷ ‘Ëöšò"?ÃŒä!£Ú6RÿS²9cIÂ]6ðLˆå‰îìSÔIxü­NfD®Mü% u 3½ïXÒoZw²YwÃ>îD|ê^Œ¨o’OH~ú}ƒÏû÷òIµò;HÒíý^ÛÖ0¹÷—¥p f¶½7†(t®Ë dÌqµ®—kRØŒÃ}ü™ß±}µŸªf}Ïwí¾´þo·ó¤¨I-Œâ1ß#USUNÑèfMŠçúzßš'býI=æú£Ø÷FðX´“e¯$>pã6áðà(îàcº}°õüøðàGûç“:Bendstream endobj 126 0 obj 3367 endobj 130 0 obj <> stream xœÝ]m“µþî_±Ÿ’Ý;½ÌhâTÙ`ˆSÆ8ö™¤ÊP®ó9ξÃöSÀ¯€Êï4/êÖèÑjfï !!Uˆ¹½¶žî~º¥ývUB®J÷ÏX8z~íÏ÷ÍêäÕµîñêþ'Cáåɵo¯5…rÿëðòÑóÕÍû¡«¶hëÕÁW×Ê¢m›ÒôµŠ•õʈ²hšÕÁókÖÕ¦,ŒP²4ëÂUU«R¬W›mݶ…išõíþi]ªõW›­ömÝÚ¤i¾<ø»m«õMɺЕ°ÍÛš7[]ÔZ×Íú)5òŠŠG›ªªû:X¥,ŒTÍXËŠšÿz£Ý§­¶•l ÿËÝÍV²itµ~hû¨íó¶]ß¡¦Xñ=úlEãa=|ë*§¶^ÓûÁ¹Žou[£›•«§Ö}ë}gÔ?ýÔ˺••1ëûFmߨåúÆF¹ÇZ¯oúÒcjìsÿðÆfÛe)KÉÎF›žÉî¿Ùd°Ñç‹ç]­ªjÔú>{¹ÙV¶B¶]®3*˜dVkâ½ðvü£)[±¾åKl†X­gôý9Îd¤6üÕ°‡l•ÎàÙ»?ÓÓ  N\¤ª ¡ªQ´ñóÉ:sÝMbU–ºæOq;Ìîn¥‹sGvLMôï*û¾N¯á4²ñ¦Ò'»o6•Vûðzsêgá"÷}´Pª®j;v ¶7e¾ÐfšadÝ»©.Jc윚n{KÑ¢ÛÜÃÞÆspBÅÉÎb3ù4'9lY¼Ü:¤Ü_Q+Oà(¢îxbQRZ¶fýýF˜Bh‰q¼­ Sê*ä¾$7Z•éºÉv7دùôšt~f‹¶+•ió°açËÒj¾fýÝÆ}®,NÖ‡½ŽwÐ°Š™ý?ØÐÚÂÉV9+£Æy{î&ߪ¶²á»ù5µ}DOŸmj!âuh «H€>]²‘<h½€O­ÐìºXŠ*È3åÕ"%ì™ ¦ÁâMeÁ/¬¶jÕï*&´Ý®Rº,dU3‹ÄþåàßV¬ŠJ®î\;øÓ£õÇÔv?*7C•x«âo½‘eŒ°ÛÐŽT·¶ôOû‘ÅK%šE*ˆ½0µ¼0d¾€ Åd /ß§öé´šôf‚)2¤p©ñªÃΔÝTB[üäk„™)ÕÈüФ2£*Î7Ú°­–Õ‡ÈQ;3EÛu®=bDÖO-*ÍpâÙ.=âjØ1HëØÔvÏQZ­j°ËRqÝ rºÙSýùŠ&6svÃæI¥`k~†}‹t°­T[_mX„sÜ+¾â^:^n¤[…FÛþøö^Ð>?ºAé¢th+¶µ°2„› Ü×Â:$Fµxk¼§|x{[vx…©CoKþ‹RSÉŘ ’\HK€>Ç`²Ä?š1l®|­n5M­˜ˆó¯PoÎzs=’ ±©rëœÈA¹Õ­³1+«Ï+»g¬rÓUÑÔe “Ûj sãUvÚÙÀÅdš*ð<±…è›ýbMó‰UàZ[Z±£œû]± ˆÔSŒ_lÈ#-†ÇÖeÁ~Xê̬ۓ+9„ƒ:žvÔ[üL|N{oÑT’‹=Ú? ëØt19rå]{/r’š&¦ðĶ-6³ŽbùIIwcm1*,šå³ BìÕD~—Ö Ü¼¯nÓ–¦÷lol‡Gd  ©q9XUXûFÃÄF=¡¿nÿ^a'>$¤Æï1ÍúÁlÒi‡Nµ¯!¥?z~Íþ­ÖdÍ̧›˜ û"^—ql§E€ãª}›Øµ¾[)~ œM_üc¸øÏà÷˜Í2ì³ocƒ„A[HžÉBÉ–Ã=Û/9O:jÑçvŒÚµ°” FüÛtNRZ¢'‘Jr ª²Å¡_ÇœP ã°ˆþ È™ùÆz¢ 0?¦ºçÁò¤˜þYFVu¨š¢­NÚ¸ Wꤩ¢-«œó‹Jm"ÎÇQ=;ù›6aâ>‚–[4gj®ÜÔ¶’ÖZÏÌbpch_B)û‹Ó&ÖáWÎà~~=Ä»ø¯^[ý`<†SD«l˾Ø) SVUhBy#±e)k.TóOnÕ$f¸^ _‘eÈÞ=‡ p„l}¨Û#±×’Vëv§C“ Ú1=¼¡"#å_gµ…V ?ðª6ìq3Íí&x–YµØ"Å®;ÅεûZ\,v¸3lí²ìè j-×ƈ)ÚaA™¢í;N¾û¨ï\¿êz)æp¨×KÖVYa9øúi{±‡¬V"‘ »’-¹hï/°ª0·u¥”sç÷¾…±ÊOD¾EG5Üô¥Ë1<Óí„”s2ê6¿­¤i ü›ÏWÙ|$¤  M67½cz-‰ а~¥Ìeü.4«üÜÏX±mÊO~±7ŠmyrÂ}˜ú”,fFVÙ4¤=AˆaÕBzˆh“°1"íÊCTFö8‹\ÀYc°P'Ùå͘Շá´ÄÆt©œ9߈³Î¿k¹º ÕžÕª÷)§ ë}¨í¡Þlj‡p`˜jõ¢¯Øž9å– Iш)1ÂÜTŒÚ¼]:D‚½Xå€î ì¦PäâM<Î}_ð ÒœPkõï¬ ð-‹3fø¶ÚªÊ³©?.Ú]ò]©‡´ª£ÈIvo~LZ"KŸï¢¢•ƒðã ”éÃxÃt)ö3z³‰‹ d!Ø@6‚æÆŠNµÐ¿ÖJ¸)£±ïÍ9oÙ”UH¸Þ%Œ× &7ÆC,;l±r`Ú‡õ+»]˜Qõ LÖœ<¥¼¾ShËeLL m}‡F±cÚŒuà¾QH*È¡ÌB žCj4•wЏ7¬;ÚÒ§‘–p#o'a‰^PÝùmâe‡>«íö4iR$xï5… €3r}@m¿ |´çXx–M &Õ1'øá½P›.ì¶«OàÓ¾† ›§›Ái.ÅÚR0c»hÆ44ÛuUJë’h—¨ep£´‹¡t×núÒÊ—Ž†’ìü’áá…/=ö¥×¾tèKO|é‘/} ¹žý@ ÷ã¶ã•hL¬ø¨ç9è×+_úÆ—Þ߾ߞ‚Yx:­Åñœ+*þLÅT<£âK*>§â!O©ø ³­Ý¥âgT¼OÅO©xƒŠw´Æšxk¸³£†º±6û¥jˆûàdEÕV–>·×Å*ø)jA7>¡b¬l;Hàôœ)s·í¤/>ò¥çÔìr“Š©ø9?‚O„Âqå7qÖðY4Å“5(Èõ*Þ£â-*~HÅÛ;D¼“¿XÄiñEi±¶ Hß»~²úÒPB€}{)Iß«†?PñÏ©ø”ŠGp}©øu®²{L´ÒÇÖØÅ»óÇ(¸'p—Ó[>xŸžrfÝåLñl)Lñ3³{!)6ß+¿ÊeŽ6nìVÚ´lÄ(¾îÕ{ž¢{H=±§‡3WH¿G›ç–ø!m£Û4Û´ùpžX$ÔC  5Œ¹±äÏ1ºœ é]غëK œ³š°Ò¬[8|>‡ó1 ¶cf,Üf{„Rq”zÞ®ƒØñ¢ †“fóÀ±Ó øý11-•óç_ ΨõSëøÀíèÊUp©[θ©Ê2Ö=. *ÀZ]ùÑÞ‰ŽƒuŽ^“Œa1™“¬JkÊ›^X¦=Ås>Ù˜qÃá×Z ËÔR¤Ænw#°ÍýHätÔ—ávñ<’•³ìr»%Ú—ÉÆ|ÞS5-ظSsrqÀF¢€ÍG;¡Øˆ\ÛpåÄÅM@Æa›ï-L”µ42‘ñ¯¾ãŒcMä¾™Y§§æn>•ô*¥8“‘Ž6ß$ƒÏ5ÆYÙ»rú=¶×’w™èÏ[ÄhÍ¥ñ§£T÷=*䦫m&LÒH]Ž4ªÒw#u&ÒÂ÷Œk’†ŽîØQa ûôÎQôYC=bÚ:eEÀZÕ "ÓÁ·¡:N§ßè†ýV2o'4Õ‹8ƒ¯¶û=ò«3Ó›Öú§ß»·C¡^Äœ2ó qµUœˆ§á.»”‘¥Ìv¿59K–nÌ2á èYdº,={¥D,sa0 Jwú!™¿ ÔoL~rÖsºhè›™óœêÇZercI„£û+Š[{ì ÀÔrG—FœÔµm¢';Ê¶ÐÆDqîº wÿÉÑÄš™‡6ØnŸq¯#)ãî&Òž¸ JyrS¦]“cÆÎ v:ð´Ë‹¥|Év$ðMшŠÒü—$nì}[è˜æø®RHgÓ28ÃÙˆL"<¢eC‹îÕ`‚t}ì%ˆ¸‹Ù¸Ãäž¡1^$ :ï½ì"’€L­K+ˆaë—WÈuœ²jZ\ð„RYC"ZÞ™W2ú‘·[Á×{(ãýL\J ˜@« vùß#Ðæ?K2Ò³L?[Ú#þ®o;À»nM¾šßø5oâ¹b„g¢Õcƒ,èÆ®éo ©ÂD—ws‰Z´Lˆ=¹Ü}ÿ?—Ý÷ŸF‘«¿| Ðü~¿fïã\ SÀ›RA÷Р‚ ú3ƒ$°®³ÐçÂÆu) úfÌ>Q—è—½ýxGü2Öá’ *ë ¶]Y°7› 8æ´M˜c}äã‚Ü©TÔiŽý;›vœCy]ͲA¢cd*\—u›wß6¶(`‡ÄïŠIÌÁ„f6²”‰™ ц¶d0ð\†L*±úB×(Å-gîÏØO0œ:—ÕÀ)œÈÓ ©±,á"Ó™ñ£JÝ;oKIª /+‘ÅôgÈ‘¶íh üÃJÈÚyƒ“›UxÒTMÙ™»gýy¢U<†G7“ÍŸïådg.°tÙTÑ,W<¾Û]„wöäeÄ̸[iSN)ª uO6¢ÙöõÑÁÑ¥†X1ÚÏ[ß+h†³)³wÊbNò<÷B*œžJi?)¦ Qy‹jÑýXL©.¹]­ß÷[]ÚoŸq„¢(“ŠÓ\øÒ’K”‚¬Íùó™^Ȥ=Ðï£s¨î#ïÅÍ(6•³¿Éí¨»£Sþ3²S€iOƒ¤L‹‘7d;c1öçைïÆÖT¨lÑ06†×Zf]À%?F”rV ½…> stream xœÍ]Ùr·}çWðqÆåé4v UN•e9ŽR²­È´ó »R‡¢•¢DZ¤d9Ÿ‘o€^€‹îƒA7E¥,½´zÐX.îrîè—ã¶aü¸ LJ³WGzjŽ/nŽº×ÇO¿Þ\ýrdþt/èóÙ«ã'þCÆŽ]ãôñÉ‹£¶qζ¦ï•[ÿÞ9ulXÛßàÕѳÍw[Ñ´ÚI¹ùÛvÇna›o·Ì4Ò·ùçv'Ucu+7ÇÛ¶JëVl~ö† ÞšÍiz¼ÙîÌæuú÷}×þQ¸Ímú”ôrµÝ1æÿaÅæmúŠ´½®5 ¦¶¯Óãoa!LrÿQxg˜ttò/ýŒ}C£ø0o©íæ"ýÞÀ¡ŽS_3êY©"õX ž2ÌÒïݺÆXé?¹•¾.Íæ<‘q•#QœŒoÏÓ#Yþ>½ÍHEÞÆ!ÈgçÛŸNþ~Ä¥n¬ÐžsNöžQÒ¬/{ªÑ鿆3Á x‘Þ’­$Ÿ½ñTkÚ–qGg} Ù¶§[<Î1°§ï– G+‘ÛsäÎŒlÓ*:2­«ÔÿÛÔá±:eP·ç´¯Øà-¤ é—,ìÓî3¡•öCpãyÏ™°—»q3Ê÷;ÚÏ'Lò}ßÓzs–Óï×ñ ÙÒ«Ú>fœŠ„š|Fäó<_/Ýíž3E˲ ' ò^b'Âùˆ¾öo[ÓÁøæÄ¿Öþµæ›Ï£ð?ˆOÿJ|Ókçxß6^|‚nÝéFKe\‚GŽ»€‹ ;i9(0Ñ2ÿY`oì±73Ñ8¸‰mÐF4Ž÷¶Av;a˜§g£¼Ð›òósÒÈÀÂ4J¶:,Ê‹Žã®çЮ§«7iÚ(x)vå÷ׯTê \}§Boú‰jÎïV=6M}‘—ä«ï¼uò$fV“¦©ÿ·dg¨WÚ }vÞ/¹@ßî#“¨W5®ZÙ€þ£“ùäöøäñÑÉ'Ϩtýeê4>eÃíÇtqHîÊ+¨Q%ßÄ_¦-ÕÓĶ–2ãëðVË`ßW‘¶„ž¥Yü´õèb¶r¥íùdœaÈ$€ƒb£ßzPù;Í´‡Wž¾–¾í>Xc6Naòx:C ¹Ú?K2vE17Òµðeüþ"ý¾ÀÜ¡¶?lw.Ó1ä72!…ßx.=q£œi‹]° ¬åÙÐ2lwdj9òtÚó×ÈLˆaÊnÃãϘ¹e#½¬Õ˜Ûk_ß™¢Ì}ž-ll@–û~ë%|ÆÆÒ6R ظã²õ»h@íÙRd“0*7H˜|»@Ž`€¬’tßnð,ÛZ̰és8!,R½‰çÚ³ 1[à’¾ö9é³=‡{h²1ܽ…à$ófbg¤Á傽9T#‹ì?XT©qGªî.ŒP 0¨XðWd²<›¶í0䀿´×ˆ„2¯){Ú;~2G!'(Txε^) Ý;˜ YRxÝ㼉)à„¯…Á¶éuO–#Â"àëÕ®ãÊ,p¿ŒŸu’ ²0³y›•Ä)ÔPX ÖÕ,ƒÒă¼Ê‡¹0êÈÜÂýÚƒ\ílæÌ"õe‰WôËåÅNò¸·Å¬¶ÔY™ò5™)¡çkÍ¼Š¼.l5V À*yùW~¤Ü* žå*³4-²i ÍÒÄÑ‘3/kTX¯Î­º4óZmjÕƒ5˜¢@‰[À½yëQZõ¨)™î'[a §µRß…žû„a ißQ+L,8RÄd´*ÈÕ~Ú¸»‚\¬c– ÑƒA²ñØöf}x)À6ç[8¬–.ç:ᎀ[üwi†i‰gÐ+Æ‹0}mï”U«¥¢²šò’eˆ UàDg qJÕ÷’`=È §”/{Õ)—>rœÂó2¿v¿gn_ |“Ê÷éñq´ëé)£'"×ÿ= †A¥ðࣘé»mpù[¥è.c€„§0<úNsM$ó‹d_ŸD;Çu@ѵÅîLçp×p¿Å“­™9Šh%†ÃÊcÚ}„ʹÈFÙV…E¿”ש$Ó^Z˜›ãq‰I’9~ŠlìWC17 ÐÜrÛ#MÕܦQœrâ·/¶†ó™~^!IÂëWtÀI²e*Ns¹bhJ›E”3@tºm¸ÇÁã¼ßlyà+é._l5c³3Õ¨Ö!Hp3 K-t²Ä÷«BU ´/ðgª˜ÅŸ3G¥RðŒD ²¬FõªFtTú|A$¯¨úµËyè ê/õôé »D#ü²Ætrh„ÕÔLõ¾‡ó¥[-fiïq¬/“`©£F£ Úh@YÌ7„ðõmfDŸ V¡`°pÆœÀQb0Üè ßDÓ”r¿ILÖps‹²Ã)+Ž)ÎΖ9A‚k[²[Ë2?Ï6ª¸¥lø Y°5)Š-ÓÞkmÅÄÄìÝfêmô'¯Ó#ù,K '’<žR–Œ)Â;úž¬••ü^|Ï„+ÇÐâ,ð=¿Yå×¾'ò׈ŽÇE*ØzaË\„ް–àæe Iê~ªô°Íš‹mëÛÊÃÁ² –U"‡Ñ¾áy¯ ¯çTÃäqQäXú6ÂtFŽò ŒÏ Q™ †MËÚ Ú#ÆKDTýí¨C7“}"±í:§6Äïðd ª®5 Udj ß®™ÖfJ7*é²j@ò×¹2cŸo‚Å«™ÇSe#mŠÍÜû¶Ïµ‰JÏ8óÀ¸3¬p8 §ÐÈ6þÅë#eX›ô–âV¤„ª/ ¸\;Þz\ÎmíYV½Ð m’éUÖ{„™>¤ôÒx£ÌÏèöÇ%ë­<ZT7;`–fê¢RêWÏÅa»úEï<°[½ü$¾œ#½qmÓÔAØmÿ½7A©²HN*c¤r —}i‘ •7ši#Cm‘hœ}îÀ¨Æ*³ùakÍýt6_‡`ouwô”W®mÃý÷N8ï©ô6ƒ‰6X?óVðàÊye$uW¤?ÚÇŸÏÉðé-éÿé–7Ú¶œ“>߯§ÛntÓ¶Z ý[ëg þo†g%Ôfa–w»šÖFJ",а¥Ÿ_“¦›ÔÁФ^¦z«+Ò–AJ²¾L%Yïc©þ"CБ çŽòì±Y^Š%™ȘܑGS9A·‰ågA’\˜ Ž$¨8€áP—&ÿî¢ÒY¥3pP«”]„k¨¼É2_PxŒQž‰¢î3ΫêiËËŸÕÓô1çY†«»Ä>— „À ÝÆTçºò10*@Ñý”w[äq1é/‰(o)é‘íÏ¢ïeÖœ„.þ<32<@6ÁfFFèNéóÖ¡¦2P•P*søw Šdj>qšçTÃ%äšÈ÷„W+C(÷ã&1\ÊPÍ2¼Aê‰4þ¸€ßü^óÓÞ'{·Ǩ†ü°ÈWCÇ ôk™-€wŒ“H·O(âÑÔRl¨"xR+V¼ðCåudŽ_´³1ΜæõA•àB©FðC „ò` ¹™c*Zò !¥Ü …ü뻓Ϯpû †ïê ci:ôcR|N1IàËÆóšîB'áµÕl þA ‘—‚ççñˆàhÚ,Gv4¹eõ¿©mÇx^°´Óc8d‡Ý[~y¦Óà )苇)‚^P‹U¹y˜þûäíÐ2¤rÍYqÆ_ T™.ÃYBÏ’³ÝךƸ´˜’¯uZeµVIæÉ¼Ná6Þ«Äè®ìa¿„L¸BoÁa+€*pÝ0²‚Ù‚&y3;€~HÄ1ÙXR®™ÎF]ÆŸa)jÕJ-EÔÙÁÆœf#ºX¸›“ÈB¶›P·º±×îU Ñ%Õä( Y1ʸKÑH/V+ 4ªUY¡gkóïCÇ+*Xbh¦Ú†»ƒZþñôý°IAý5õøˆžØy¡Šu¬!fúRƒ(­j6§šÊ²¡a Þ#¢€p™ØG—ŒP=PT!“ý&ÇNˆ/“Âä…Ã’È¿v<É.fͤD€Ê9ŽÛE›´"k˜eQ+’JÔ ÙÜPÍ7Ó ó@Í÷gwŠØÇ‘¯àneæ%µ‡m‰Ñ˜áëJ_R(¸Ø gƒ»_?/Ÿ¦æ_¥dªr]MõÄñ„‰ ^ošÂCš0É.bª‘J;–‚rá)õβ—BN«¤I ›Êc¦w¶ðíñÕ [L…™ô˜ÙÍIµdàò’}ª„\»‰'A¿Š‹CGw<|…«>q./I´øÖ…ºÕD*Ý%Æ…Ê%>œ[\p ª,ï8 aÂ*ë"Lܦ3¹[”MãÁöÉCÙ4²iÃG¢$C6$¦¸™ç… ÙeÊn‘‚š³X‘S«Ø9…£‘® ½BOW»£ìø•ö¾ ÂÐ`&k±Ÿ·Ö¾“>]×| …EáÈÏ5i|EžoR“—©~žVêé<æô°…TNô±³xÕ3Z©.€µÛCU1Õ{cÅ*—ãP¡¨"Åqº¤Æ2§aì©ïß2zÅÍA-â`@R4¸’ýŽÊWÕ/ɘ?rwBvðæÐ Š  A*¿Â}T)¦+ƒÐZðgÕ;;àAüj‘2qŽàAëR•„sï ‰¨aãݸ—$Fù"Í„§c¤¸ ˆŒÑÂfêáÇx©•AŠ5­³ 0Õš3¡Õ¼àu¶ø@QI²Ðñ[oÚLªŽ¾†F8Qó†na$OT%­‰è•~Û¢£kˆóÍÕ»j?ªgÇèG7ãC𱾆®”Ð$\R9°æ6a"Ÿ<§ •Ø„<ç=è…HÊuŠ3»*÷I<¾8L%÷‰-Ž2ULT¶(³3¸‰!½‡ ‘q R<ø³Òtâr[¼+ÈÒ A8?£\Hf=M S žÕ÷É×þ&=’­#Úfl‰\f †¾^P{Î,S%²§Ù ñ-¯)x_é gª—Ô±ö‘'¥ósFérÞé]奂d î|]ŠLk¯¬Œ%—ŠùÉüš¢‘ðz»áªT”ÃóŠÈ:aAö8¹XµG4xNù4úñ8 8s!„K·Èà &gD_§®o*Ý$å¢7L¯ÈªXâ5×ÊWW/Y$„+]—“F5ZEÉô/@"TÿÂ4¯m˜BèqMæú¢ú¨ýúhˆj&,3ÝISÒâœwîõœJ^{J9©¿ŸÞcE2“tžéb5̱(fWÎÖNu%gWRiˆXá¢j–.ø_`JQJš‡»Ñ)þ8\cHLLiïpÇ ÎK] "ÌDÄë9õ<˜8 íí<òÐÉÂë?6¨ÌÜ£¸¦çIkA(” R‡ÊÈÂlÏzÇ{SÅiˆÒlDü[½¼úÙÿ U)‚ ëh°Ã–Mqç&`Š\ˆ”YâC¶*Š=žaíÚÉ 9tb§øZ—ý¯)1Ü6ÅRÀÑuü}jÉ;eþ &º&8qî½NJH&Ì«Áϒؼ7Pùµs 9&Ð>CÆÞM¶ð|:áçS®ëíRÅ+$2€Ç"øóíé꾤j׬&Œ6™LnU™XP<Ý´&Î…ÿŸ 58¨ˆó§Ë\åÔ´¢¥÷™dª,ú™AsK^CT@ª6”—‘¢Ûô² þ¢†Ê´\5¬5¥´50Ðó„S0>ï}\…dZˆWG^N°"üòäèþïÿ1«™endstream endobj 136 0 obj 4799 endobj 140 0 obj <> stream xœÍ]Ù’$µ}ﯨ?¸ »’Ô–Ê„'ÀcÂ6DOoÓöô2Ó Ë_xù`K¹HWÒQª²»<˜!fÔ™J­÷ž»JýzUWŒ¯jûg*_¼û\¯ÎoúÇ«ç¿ oÎ^´•°ÿõhùørõá¡ù±UWuÍêðì ®º®­õÐ*[µæyש•fu¥M…˃õjsø÷Æ*ÅW‡Ÿ¾óõúãM] Õ4µXŸm¶æ•f²3õÜÓ[ó´«j-ØúÇ ï*θ^_n¶\Ú‡zýÂTÕŒK½¾îK‚×zýj#m±“ë W:Þl5møÆW³Ùªª®ïh#ý§þç[ûsÿ—oôª³P­XŸûš¤_õvèßÍñÊ׿ö­ÜõŸšUÌ´Bf~ƒæKÆ;UUŒ>%ÝÞùH…_üµ¯@s ŽT¸ó“|éëžÂõ¹Þ|{ø‡nÚ)iÈæðäàëõ=ã_¥ûxÁ:¥h«3¬¦3ßùšÃ"YJ!ŸçFežÖ]£dfŠf3m·t9ƒñ¡ „T­M«\t´éë ¶HbǾ­#¸Ï¤…ëÒÀ^Ð sŸa¶9‚„@¦–£ª"°K´ÙXüÌMá7Í;L‹lëÈü.±P &R¬1ßHï#¹¡òÊ5òfÃít3ô@¨Æ½ÿ.  ééV¯7öE›¶íq¦¬ÖϘg­÷¨ ¸ƒ]Ê驪J¶ºç÷fGE‚kWÕ[ð¾4…Z 1GÞÅÅ“°`'ÊEÒSÈÇq_Ðè¹;©yë÷ÆaHXõÜ`þùeï›5û,„•”r/Òž)ª¶R´)\ÌKÐÇ-7uÿ¥”xO9£ßä¦fãÜó“a G5ë_<¸ýuÃTÅdÝ­ÿæù‘¸P~?ùado<3 ¬¼_8üøˆâGžnñ?p‡ËqjÑ$þd¦nÖˆ+Ý£ï4௜·ˆP×sïMª!öý¶RyDÐWIC8]fž’|âpá—F–†«Âᚺ; WsèL¦u13ùô’qèwœâ5FÅÖFQà×xÀUïxÕ‰Žï—ôó¡éÈñ2üuOŒ‡ÂE—Y ¬c ÝN @†£Œx0!›Ù(ñhBÎ@%äÎf݃ç¤àN |¾®_߆,lÖâ 5FÐ ®º«ïïhÁöeqè‰bê=Úñ`HvDK É¢´¨X]wæ9ŸØRY=\©4ëÀrÙ×¾e¢E;<½ bšâÑ´9ÚÈþÊt¦4ëÛŠ:#FγÃÏØêÂ`ƒYÉ5[ºí3—éß7§g;¼VÃkÆ…ßq%Û™O¹2>}*ÃwBÔÏZx=Œ™iåwÊWöõÐxöõS²¡ŒÞl_ÈnЩ/Ñ {›ôr„p‚ð.Q!¦V³ ¤5îÁ,ݰºÉ~:Ð ›¡›¹^Í êyÂȾϾÞaèî„Áß&a|³öuù ów¬gĘNLed’€žLßlfh«ž§­z†¶²Ÿ´UÏÐÖ\¯æM7K[ù×}ãù×û ­F?‚¶ÄÏE[âç¤-³s´Õ¿ÎÐVþÓž¶¦OmÍöjÞ´ó´•}=4ž}½ÚRÊÑV ;m™º©ÚT£"‹ÝÎ/v;³ØÙO‡Ång{®WóFç–kz]øzvJzfJÙŽ§×³3Ö33ζ<½žÓ¶ò¯÷‘.¥ h=¥‡ËÉçø¡³È½•ì-zι þ¢“KMÞ Â^ìi`΃H± ÍR ︉µ,çµLcÖIŠÄ—€"Wô£4tu ÍÏR,)çÄ*´…MF¬ | ¨ãý2JÞéi ê@àDh÷ñ\ìœÃ9MAä¿è‘ë£É5¯¸ËýÙÁ5'CÊÞÍ7G¼P¶jc½Ã?À nK¨ÏS=r®JWÏ1Ý"õB <¥™¤F{„»ÏtKrAòés£O‰2D”®´Öˆ5ˆSñÆ=„ÓË"ˆzÕ§ëºöœK,-`6„ÐÅ,“9§ýàaÙÝЃM2?ƒUœµK"-.i‘~*iÕ„´<­=dŠ„Ój+£Ø QK Í»¥±¬ÁûKüX%<ÐãNA‰óNt¥š®˜à£¤þác¦d2ªã(Ç™¤2`þÞ½/H ÄMg….Ék¦áV„CwnšQ<æõ7& 6{Pßœ-Ê>¤”ì 5³mQÇNmñXv»Z•Üa7a¨DTL¸äÔýùÚwIð›rpþOqqI]¼×~yKË dÅj)æÁ>ˆ†xTDB豨¨ ékP‘žbÊG&{ ‹Â¼½:³×*›%ë³âÈ.¾öï}Ö³õµƒú³¼BeÈ?Îl-Z¸£åBäÓ‰“l7:R5ŒyMáÉ>w–ŒŽ nñÐHBÈV9ÂÛˆyTi£PÚÆ÷>µ–¨'Pýx¹iXʵ‚UmÃ$Ä̼£" ’^d#E -üŰðÈÓˆ:ìvÁsX`ý º¸+‘5rWæ¢åO&7°ˆÓÑq¿`Žº]'Vð–×¢2FZ€$(µ~6_‚¬ö BöÄP•¡l¹_ÿT °IVŸ> Dxù³û¨1ËÞJc¶0%’è|¼[È]œÑA# ÅpƒYçò-J)Dl=«†Û…é÷×§¥Ò ÓšF[ˆ%éÓž^¡ZU´µÉ’‘LH(O°¼Oa£VîPh¢¼äRk#wv†&aþw²gØ`/zÒÐɃ~Ãô €’¼jU.[<9=mêBT™¿$bU¢»qF\ Î< H¹?¶ãVV™SuÅv8Ù± oÍÙ¼˜ˆ‰œð!fƒ(ƒÓ!òK$Y,ù58&~‹-W¬®Ôç9ö1ð±<ön™hY$‹` °f­Õä {™údVÎ4ßiJš<+>×›¸Õ#@Å ñÄÁÞnR‹á$Vc‘/<0!ÁÍVT[XvPu&ÿ2š3Zškº`å1wzÌ÷¢ÄT†qþH2k£bñðÏ/D6•Û@8•Õ¶e„/—W&¼dt¡Aaægçƒq‹G«Ÿ…ŠÇpÂ6ö7æñ`º@XU:¾Wáà:X‚± Áw½ŽŒ&\ðº × ˆ<Žd@måøN·ÁöžCŠ+÷öο¡´ ÜBFý^9GÚ} îC²8d!`–¥'_\ìÕbžZÜÿÇŒ·`qC(>â·”âßš>Ñ£èçï>gíÊfÍOIA]”d³˜úŒ2Õ÷¢™ÙøÊIÊP·=)«NjiÛºR²nì)K£ftÙñ­QM­” ÀÛScÝ@(ý÷¢ã¹ W¤|ä{{åk¯|ñc_¼‚œú¬Î^iÙ(cN¯IÇÃXG¿¹‹Ú/T1г.s·fr=BtN“ìñ9zOqgw³{{ \L6#§'7É80“)0Ï\û$s™ð…›ñ’kèÆ¸=p†=„KóÞ»ÔçAž’1:àHÝ ÜgOãã¨Ës£¾7m6…-1T ’˜¿[i›YÎz)® ö°äŽ!™_DÓôbˆ—'æ\$D¦#oYÑe/ÁlTÌ´*ÜŠ¯Ò6šª$9ézH5éH0oÉiÕED¸¯° ÑK¨Cž8ºS—®úÍ˶§'à[Õ¦9‹ –Ϲ÷Íåì ë]sÃmIGŠVm–ÃYÄ\Œ/ò44Ï&¸CbÈ'%$Á„ôÏ_ƒ9.;ÉÚ)ô‰è³˜æ¢üÙ £–¸g¢QQòòù(céÊX+ÅD"¹f™Ú+Ë.ˆÖΪøÅ¥ÀáœEÌ%è>Çy’g£g 6˜Ý€€>¼žŽlN@ÀhX_Ž*P+w½(Ù:˜ö Xy¿†sò£)‰Þ=šîÅhðBéøðFpq»+úÔä¨8¡ ;h’§6&&–^f7R•ì>²™ÜN¥ƒ4›@žBÚŸùK°o­ÉR Ç #½aÞMÏ£Tà’H¡'N4ÚB×CJy˜–\8³D(*‹Ñõ@"ëJ62ÖöÃèv\/2Î-ƒ«»àœÃ\Ø&¹AD‘t’ ‚ÞÆžsF0©+cuÏú#ÈS]ÉV ž'ÅVÕÖ9!&/EŸþí=[Ó±²‰‘Ô@ÜŸnËíLÒÏ^m ëëÖ”~4KR7\ó¼«búèÂ}tçßû‡G¨yÿú'שºöïÇÞk?¼FµëçfÎDíêÚ ÷¾xçëöͶ5³$o×´í¡s¹§Äи‘i88ÁD­Y½ë5ÚÒÓÌ(búî¾%Ì Û^â’¡îÞwlžn’HXälœg‘d¦^ø)^ZÏ’‘uK³XIš®€?\PÀB sÃfö#¢b:é_°^éõ“´G*û¥ì–ÌûBF qГŒì2&úHâÆ‹TCÇ/`[¾êüÞ‡Zd&[º q0rIî6>ûAº û;^oT\´Õdn6Ž˜:Ñ¿è³WãŒjmèXðàRx $Q4†ËIÝÄGo1õˆ¢ƒó²æ’Þó©Î0Õ18Œ ®œ5ƒ¨>±aòc):ØþìyåK?îO|]R,ÚFŒ·ô=|å.Ø •ª×üœÀ¾‰m™ ˆ ç9«äš\b¿#íkØ…Þ/Do7‡>àùl<®É±•†uÊ¿þ ]ÇZô_füÔßB3QÁYá‚YÔ–K¢_p<“1ÖÛˆ§CæVgƒ_ÎwG~ý :'É…Ã…éî}pØ‹—¬ÛÊÞ;@mÌÝO²¡PõN×%úÑ¢sg¡Ë*valÚ°$¬”ˆÆèæ¼xý¯pË5êž’ ?‘´Û.±UýñYL‰;Ü3?KÔdmÞ‹Í¥µÍs2êYMRæëª¥i•5g6áëÁÚ:J‹^ÃK®tâJ+Wºõ.]é;PïÆ•N]é´räJ¯@{¾ÞUüŒu½Ók*®|ÑêžSùÁ¯}‘4qB¾ÛøòûSqº{»¿åóƒÿ–^éendstream endobj 141 0 obj 4857 endobj 145 0 obj <> stream xœå][sݶ~ׯ8Oí9–H€l&Î¥™tOË~I;Y’/©n¶$§Ééïí‚<à‚<’ÜI’d2 €Àbw±ûíçݪ,*±*Í¿¶p|~ð—ôêõõAÿxõÃ×»Âû×ïÚBšú¼||¾úüVeµêŠN­_”E×µ¥º­V-=ïºf¥«ª¨ÛÕáùÁëÕf+KA×ëT,-ëõ¥+½u¥WZ¹Ò?×®ø‰+]ƒÆç®ô•>L¾õm/À³?„Íh4U'mç¦xé‹o}ñÄm¦¼òÅÏàÓ?ùâ5ìøÜ_ÀºW¾xê‹Ç°³#_<‹ûUm×òº°‡_üt(Ö­¢ ýëðï†Y<¯ó~{@oþ|@ìÑô~òãúp³­T¡+%Öo6%D­ià¶´¢’l”*û9ê¢,+¥hŽaM)Jm¦¨ihµù»«inîÍ™{H”¦¯ëªîXÍ‹þ¡lZÉÞø¿5–nDØ©{Ê*üºiŠR—Mëâ¾.}… _d~¿Ù6f¢3Lf‡x÷K¨ñlNa·=Žâ—Bqê\ù÷¬6¢W¾î%¬@Œ*j¢ESñßuýe#ºBTbÇ8¢ÒÍ…”Êá é£p©!»¨õŽM ç$Ë&÷Î}ßÔŠÆEê®å­N _ÒäeQ‹NÓÜÌlê²áïo<õÞ@J³¥x¾‘†¥ºjý½/•Öm]­ _õËŠßøâ«¸®¤ÿ8ScpØg‡Î¨ôàVæjq°T²’¡ˆPX2Ž|ƒ@7µe˜­å˜íN·¶á"aÕ›³×1˜«Üûc¯xNÓLu)€Éy…'àFY7mVá6æÈGIF+X«61P¦®ØJp- ôéh¡ÌÃ]OÝ !UWêPCFý¤…9¥ ‘ðÝöš¥TEÓ´V³ÌÒ•;}3Ò˜±œ¾BsðœzæžéÛ%&ˆhf¨GPÀ® W^¦*Xç'€ õu辶¯‹D_®*âµ§ü§P¿½ûyÓ¤Ãn?8– ÕTmìW²ìy-´òÅ›Ý^Dzœì˜Ǽ‰¥À,-ëj#´¦nu¯–êŠÌh%¦L¼†ñ ŠMð'/{·î=3‚n YØzÅŠM>®Åäœì5m]vç‹Ó \– Þ ÏHÑ”’„̾ Í·m[7ëïÍÞ©4mÃϨjmªÖyK€­ç)gYDï™FZRÚO.#S©kjËeÅÐF–7¥ m®.ò¹”ì 5¸\M?"2Ékc$ˆ¢¡/êuÍŸjòÏd¥×‚X¸«uÝ» MIœNvüc#¢­ÈŽ?ñXñýF’Ä©w0ìC¢¬Qu¥ƒòjø˜nÉ #•Ñ»¶ÝÕ0„ª#š¹‡7¾ªoäß_Àq­|«/‰€JˆFÖ&áÓm?ÄŽ˜Aha|¤ÞL׆Ýéu[VÚöoþ2ÔГIx½Š–°_jsø“qrjëã<í¹°#xÇšZWÄšÛÎ0Y)Æây6£%mçOX%¢vmÙVû¾ñ¬˜µñ«¿ð=°!œø§'¹Î>!.#J´½ã-¥Ö¡ó¥li‰µ™k>×ÉùÀËmÙÒˆ\…~¸Â•yªj£ƒŽøîdFTËÊPОuÎÙöuâ}Ý÷Ùšå¯FìÕ©¢+•3Q z`©c[þrà$ENúWÎAxN<Å ÿ/P%¦u?q3z•3ÞÀZºìÖ—8y þ1døŽ°†Èà öqà]Fû|;¸²!¥Sg]¶Ã\Wn5wc£Ù ´ßô°óɺ¿„ä¿A>óÅo}Ýo}¿\Á™~f4Ÿ@þ}¬¼ Þ;㦊µÀR9Ô›èýKFx‡Ý½_bôRÈ}õGý&RuÄ<ÊÊ5«ÿÔÔg¢ú=ÕŸý{gFœEnÆ`@º&>âž– 1Éöb£`Åз–9wÀÅÀS„q›ÖHØ:ô a·Î\xKÿ”FFhdèÇ‹‹ vÒñvÆôMˆœfŸÒ°ÿ›­×‘3ñ/6HpCÂ[§k;_2Þ`à#¶Z<Ç` d3F\lõï0M! ¥¦™ânfw%?«Håñie¡@¼ï-A<¸¦Ù)>*7ê×°O,1ï áXFäã„Ìñ­BHòü•LÀÑ34ÞX–¹½¹µ+¸L{ot»»3“G&¥yÿÁíî3,N>þÎPHInß&gÂÌf&§-ö&'27‰jh[ÚóTP S öû¿zÊíÀ\;$óMÕSzp©¤2Pà ð8ÖwÎãk-+2>ºga|…ž…ëx™çxlI’ŽÅ–‡BÇ¢)ܱ¦:[€î³Ù o!³7CßæZ-LÁÞó ÃÒ 2« ì„{7+/'ÿ½_Ã+Æü4H ´ƒ[]÷€a©Ã˜ƒW=PÊtC°SpÌTÖº¨5òVf²ˆTÎ(À3aafMÁ±S ¸¾¥­Æ€@#zƒêŠoæŒ.ËàÑqU´RÔ@4'ð\KŲzߨ#ÎÆ~—m¶;†è=4Ú0ž¼(ÁXf‰k½ÀD6\à@aîˆe@^q©ìœµ9žó{³L9+&̲¿³²,âÒ-­E&ârW_e„WÅú'þ²yÒÛA)fwòvf œ8ƒ2q^Ì@[-i6'3kk‹q… B¿Ȳ gg:¯…1g úÌf @«-ùE£:Ä2ùm]Q¡iÑdRyjF êë:Áì-ѹ!ÙØuÎ'ZÖ˜}TNFÇ-PÎ8»Â¸v÷M…C}}„ˆÎ¤»‘l*°àž'ù†Dž’HþcvEÛ6zÀØÎ¬SuÕR,> ѱ€/6.¥þÒ7:ßTæxŒì™¸5°®nÂL}›ÞõƒC¶ê•¯ °'XûŸúž¸NËá|¡ËôçG쩃¶Yoû#Úäböæ…,ÛݱzÆš³æ§¸W^~ë\øÊ¯µç6¨Ër·°ëßãiƒYѰéx›5ù½éZ_=ïˆAîÓ|Ì(ðöt²QÕØ|T´é))sX÷ÞÙ).'ÛL·¢’þ®{ö)Ã×}-“[ôÿKþ‡°Øw!æ€Ppþu€Š'øÎš3¤D$£¾š!ëDz€ÙÁ]4ð»®Õ íèP6ùÉp}íc# 3Ǭ—!ÚY…ˆ±Mb´¼5I´&B5‹ÐMnMb˜Z“¤MZ‰€˜}ZáÛNvmÇéþÔ¬Ý8àÐóRìàz2zjÑ=ÚŒÑd<ßrŒyúŠÄE@Ë-6ô÷5ñ_Â…erò¦/ï˜áX^ÅÚ¼Ð.s\÷ŠŽó•¶;ñ"àw†GÍ–Üôr”B’â-þl~“>óy:0ç?Ò™<›¶—ÎÀ[`¬è'ªªÛ:F'08A…ZF15¯E[Ý1®«—ÂÙøxµcøïA©›Ræ¦ ]„äSH5…Vþ@PJE¸¦š”E+”‹law†õr•àúLŽ ”²šÄk-Võ–³Žåô\„l‚19ÚM­2{¾ñ‡½ƒ5e9&][´Z¡³K€œì¥x?³&rïù vt[¤\™ì9HqŸ['Â×±Ó±àÎÂt\¤÷&üÐ{‰ ¸ L„ûT˜ƒ¹3N ŠÓøŒ7ä·½†CgW ¼í1™®ÑÍŒ8ã<°Ãpµ×I‰…át©m,‹«Íßão âcu±C½šÃXî7Îõ¡²‰;哹´àŠÅ"²¬Q£CëÝE°”¨…Üo/ñŸù1—¾xͺ4ó‚8ã%$>+¢¬Þ”ÅÈ-\k ¼pÆÓøœÐÒDúŽ2€NQÔífÈiO¼Ú 4˜ëlFÊü°¹”¤Yœm 3 §ÞÝàn:o$µÝ9»¥$ý¦ƒ³è§q”@ÒvQ‚*E¹ØjŠ<%@6ÁÌ(ßéÍíN™ˆ FõbpÖ_ÿ=XhÈ!\’Ÿñ®fÜ­2 —§.XµPY9Mk'ߤãCl¶L¼ÜÄ0–‰·ìm<¬³Ô¥§¢3é+ßK}¯ÈÀ2eiõqNâjpg°?’:SìÔG× í³ð ŽÄB;¥:#Âxò ’pÉí»LáW«Oìx1f‹¼ûhÓˆd=.¦ÆgÔ†ôàÙä7‰IY¯'Å hËúz³U¤ÄÝñé8ÌòÑíñ®ØSwÇA¤àÎsÛo¬y( Ü¿§Ù±çy„‘±0'v<¾ßÀ„­]6îÕ|pW-2ŒÑ_åä|ÂËt,Ë‚+qìuê%àåì™ÔÏ=‘(l-û‹uÇä *UQÖüÈëã#¸ލ·0âÂÒó`³SÆÔIŽÜs|ä¨@•±·Ó”’ÝÝóNK6f ®c|ûdÖ6›‹ê‡¿òÔ…xà5™Ù#ÔYˆ¨×0m"¸[pˆÄ´…ON{D0òVÖªm*ÝqÎŒ€%ì W·ëª¨ªNÜ9®²}åxiìWÎ néGº:âbé»ô¦¦á*÷ÌÅ-§¢$i+“E;>c.mg«<.¿°½Ò]¿â S³k”šýÜߨþÝÝ´»æ®—÷šeV³»ÛÃebE³ù½ê­þ¦éG{‡úqpµ»Ë­’Ãm6Ë÷iâ©[ÜM£¶ö©ÓmïëÏ͇®T½£¦ ^ˆö-º ìw˜Ü>öý§Óºk!ùåê²PÝÃà‰Ç*µ’ÐR vb¯ä¥»ÃocCør|‘•—E„à›$¤2{‘*de齃ø›ØÈ Š™4Ãüh±ÍdÈ}–…”™« ýÍÜ»pHðË/AFBG/ºðæzÍÍŠ2¾P!ˆ0….t>+)¢±ŽBë3u^”\=ÿ¬g†Ãª#‡¿âòh1ºÁcÏ_Zb…dÈê(¹HGÓ‰ ÷<Û±öš‘ã7=lŒ„;]øï JMý¾2ècŒ`q|$6÷vkÈípø£K>¤‰E fd_§¼z˜ p ÍK>‘¾ñ‡áŽÌÕžR?LÆ|«ÈŠ’„' C‰÷_6!ûcë ôiŸw‡äê€Æÿ>á®H²qðÇ,‚ÏаžG›ì…H¾7±7n×ÒŒˆ!³þƘóôN‚g°xCËâ¼ÐŽcxç-»èLÍd¼‚ñsç_Ëk¼±›Éª÷B‡OÅ×Ka`ða†¬õÆá1ðœæ?‰õö¥Ò—ð`ƒ8ø`‚§ÂŒ%˜YIr,¥ØÇß99hG²@ªgSÓ’—öŸµ½FMþ”>ÓòžãŸV-êþ}ögICŒÓ‘Ô…pnÝ \=œBé)Þ–…Ò;ú®¢%ûF+R‰¢*Zƒ@|¾ÑBŒˆ(t¡µ„—AŽÏT¥éÆ:ø«Ãƒпÿ©«¹endstream endobj 146 0 obj 4961 endobj 150 0 obj <> stream xœí\Ûr·}çWì[v]ÞÑà2Œý9ò%qbGfüb§TKJ&•P-’v\©üF¾7¹™ƒÁì’”å”,— šÅµÑ—Ó~\•…«Òý §¯Ž=5«³ë£öóêég}áÍÙÑG¶Pî¿ö/Ÿ¾Z=>¦†B¬š¢©WÇ?•EÓØÒt½Š•õʈ²0ôã«£ïÖ×›­YßlÊBUu]ªõ›Í¶*ÊRÈf}K_P²4ëÓQ­U(¶¼ÜhWµÑëW›­ÔEi”Y?K·ß…ŸNB‘õúïÍV»i´cýýøGB•\ytüÁwôi«”V…Ö]Ïç¡Öó°”¶­PÖ¶æm?ØHª+­^_†µTbý"|ý¸›¥*EÛmU•‘¼[\|׫•à+Þg +Ô×#>/G²¥ùÙÎŒ÷Ÿ}C«ôª]µåˆ|߯¯Rîâ]§ß¥jø¯HmÖ¯ÛTe£Õ¿ Ýò!ø÷x¤¬›¢´~Á„髪š¯¯y9Ù 7&ëwIשi\t:Ê÷q•â›$&Ž‘5‰¦ÙóQĈ‘Qê+¬`³,”(†Õq ™‹¢†àPËZX{|w³·©ªÄÄmæm,x÷oFPî­Qñì.±µÕr1ŒÄºGú™6¦¶n¸b‰hÙ.ªÖ²_ÕUÝWh‹)bKc›Vƒ ÌÀ• ¹×^\x¥¬~®Æl|±ÌW`ä”ɹV}” ôÏßEJVwË> 2‘ƺA=m¼ë!M!ŒÔƒ íœF(sXøK® ÿ×Ú©¦ËP|Æé5XSVõ6q·¬¯›ªª»3oÉI¡†3¶vH46s á’Ìì¾cíéM?î ✨»sŒÍpíá*®àÀL-±ùbå>Õ n4ˆ"®rl N FðÝF"ê¹Y•ž7D£ KÅž9RÄôM[ U4ºn_À¼†”KaäEá Wt9*-}íÆ±jU.W£Š`)m¤FJ»A]£©(=hгM-Ätoª¢*›¾e·M]Ôu­ÛåÔ:üùÇÎÝ¡®k™ÃývŠºžeÈ9›¤nÄ0Ý_¿dÐÈk8ÂMÐå` Æb<¶4<&!4%¬>"9ïF4 ÑHdŒî l†u6Ö:7¸ºPÆ[Ó®…[Ùȵ˜ê²•Ç¡Œc@X6u¥×ÛÐ×ÉXŽÝX;(òÿ¤¯“Ƕ›w‚å„3¦kx›-âôA–²/Zp›TÐKäAíFä:9–=#pxé•ÃΗ&šÏ},ÆØÍ©:ê°Ô!¤Ûè>¤kI“5å*)‹>ª[o¶eQ‹ÚhM}‘ñ1ªS¶2믨XÈZÉ(Q¦,kçÔ(·lé6'tðƦF¨ÒuÑöEûôMÛL5¦ZB ¶¶ÎÉüzC Qvcé¢jJjó©ïtÇ:=Ýg+Çö¾ê…ïž}¼¡šU£¤Ó(ÃÏýôªºi%Ä•Me=­„] É(5"”nT¡¤§±ž!ÛN„" _‘X™µ ñh´Ñޱ ¯ËÚ©û² Éltã­œ/_ZI»“$u²qÞrוªÆóÍv¡ÙE¨±¢j«H·}›]±ò-+ßáUÝ? ikUA+LÔœí‹&߆>e^werq¼æ~q$+ýpƆBOº²ÃÊj¥æ åU¨ò%ûÌZž°Ïa lÜðñ—žÒ&ðDòHij…±=[xcß8&®b\Vú3飬ÈÚ}NCk§“eB…aoü§M;wRäÁ\2³=‚ÍÙL€3}³¦Dð_Y ©sÑ|¥° ¬ß#íûûýfÛ8%Qu„&Ó%H­x÷ŸÑü ØþÎ.Ã>®UERÒ‰Ã`è—Ø8íøŽ1 pÙò0Ò$„ÑÏÁy°¥°ùÝ .â_aæW“²dÀ¡žoý®w2eŒÕ"ö‚~‹ìð¬KfVŒ]—a#¹¡> €=D€‚õn™œS0“åño½ éÖhêŸÃ³V o¡³ÆŠ0&˘;‰½€ÉR2UÖK˜ÕU,a(à¼àäc úòpï@Qègnˆa¶9¬ñ "^JDÇNà ;‘™(}PªÂØZ"ûCY+6HÚV.:¯%ñÖÄ¿œ² ¬ÁÒXoÖýacõì_‡™Jão<4Œ†"ºÐd퇽¹ÕYðïUKOç6ñ@â —[_|{`áëPdÁ̟”o6S¦ñEjdµIgà (Ùç~q(ñ–¯ØN†TøŠ£™ŒÑï=‡ÌµW€{g—#ôD°Ö$ÁReˆÿ™‚½g¬p ŒRT…®z Dý‘XEÌE­ucPôOÓë@D4|¨1§¼§'4ø,‘Ùçˆ[U6ä ÆÇ5¯6†§0:ý™Æg¥)ŒQUÐ[3p˜|“á¶H¤‰*¥ŽïÂ9•AÞ#¥w))i«XbÃo)m§™ÁkŸî{P6Æ­ÿ[±®OÉ!Ú¿}?>†™‹¹µ_QŠÃ’£äõãαþ‚ÀŸÂL;~ÊõÕf[SÊ4ë?„vO|D™5³‡àsw¸XÓth6ÔÅw€öMCÅ…“#bµ2¤9 ˜~˜¹×¤¿êž˜Üž˜‡ß“}N±Î¨bQ§ó@]f—²-«ÿOи©`⼩À§×ž­X¢M¤KÚ‹BÔ@]Ñý€É¡_{Y¤S˜}¾ Ü÷õa܇ÓQh2:ÿñò^Qú[ás„_Y¿˜ˆÍû4KØ•bsàãvil.ŸÓhä†?ã]À×`›÷³M°y¶ÈN?1Îßñ™¢3D¨&r7qäaA>/ó¾Y8e¹{Ž>¯#Íýv3Åë-§¨$¥8=Ö6³wÜpziFÝ|8FoÛa.ìt6Nùf‹é´À7F;o„=§ ë÷“!U«%žÔ’¼ ‘7>ÙÌŒ,›â«Q8×ÿžmÑò”Øs![´äÊÒÆÏ+²Eøqé®þH;׿š1šÅ\ÝÄÿ/Œ‘×[¬Qw¥UëT˜ê`Ž_¼­©¸·/ÞG(mÑö¬fXîpœv,¯mÁiæ(ÆSn†f÷Àp÷¨þvÒ÷aø3 `]’r.ræ{C«ÈÞ‰«b-z¿FÛ–2Úú‘Äžå&y„ŸõuQôm°Äåp#es›^î*âS&¬‘Vx;°¡Ã ÏGŽÌ%B·ÓHÞHå–²~x•Îû è¼D¼Í+Ú¶öX@›B4>çm÷9êÎm`—<Ý@×î‰O"fÍþ´Ù6,©üi¨Ž³‹Ù¥ósD1d¿/ȼf§á™[±3š·Ë_žÉƒè(Íü‡ "xæE0ü::£œæýÂg#¹Aqø,e[.L_òKx+ø8 'ãNNWûáåasˆÓŒ±e„æ!vŽÁáÌÉÑÈzœ;rÐqHøS1û,Éœ MgkÌÙÐQvû8#Ã?|ÉöõXªÎä9!÷âîT{6ÃýΓž¾ègma¥?ÂÇP¢…Žd-*Íëž!ëÃo¾sC…Ãø¬ZÑÿC òl0ºÄê9qt‹d3Ìe—ÅÅœq0HϤ¸K­²éYÚa•Ò…3ÕÏÃWÆ?o6ÊZwKiš—å¬béb§€Úcç/rk¤¥-DïgÍd…°ì² ´G'Þ\qk’Ðò #Ÿ cå± ™yTåÀ J¯ZÃG9&ûß›Ázp¤zÒ«-&w¸°–Ég5íã0w/úЄ°N–ˆ#-Ъø«Ê¦ˆß‰Ã°˜'²ÊìúÎüŠý—ÄÙ"ªZî‘À”brߺ¤Y}j“þ q‡ÖiÍúËÐÇ\t{aìTy»×C؃ã÷˜:þàÁ ´Âô’ÏâA×ðóTÐÑñÉXÐÃ…9æw|GìH`HÚ +ãï2bVÌj:5âtz û»ƒ˜ã‚‚³Oä­ßã™6Ío: RÖEUÙ²¸»f‹Î[ƒß^Ù•ÍÏ+¿[ÉmÙE^˜g¯ejêŠj†¶Ã¶2ßJóØ™©Ï 'þgveëטּÍå~ŒÞD„oø8‰Ét/Ê9ŠjZÝ/—BË„Ÿò:wdb~€C£º8¨-`æ˜ }w¬/f%¡“{÷Ž“G w— ÆþAþ؇ž¸(ÒÄÒæqkÂváó`E™Ž;d#² ®»ày?Êd±”@øfû\HÎÚ ‡lÙñ»þËF6…ÑãŽl‹\*† z>á"zýtP AñǤã—q¢úÒ‡,V€Ì94‡YùìÑL#.|IJ™ÇA:ÑV8^D™¦¿Âk’‘ìƒ}äk¼¼w¹™¸Ê¦$¶­ôW˜ Í>"Étò D<üs’§oa ´×½¦”ò`FQdUX›¢rÇmW0²W¸.rALXðø âæåW˜Àí#ð<[\x%¥¿ªà}¾h‘ò¶¡ÀHwŸp“àÑ| SRV’'ɘèîakÜBˆçÒàØü‚)Déè-–á ñkÿñIxµ…ñ"S °^˜Mÿ,x¬ùL?HS§ÑcšfD~e÷JÝcæ 03Œ§"»èœ"ÁáËÃ"YÑÅoµ3!Š®bõP:Šw~Ø6•qŠÉ³éð(W˦VwlÐñ³è+Î2a«îøÝ®‡/.¹ã¤Œ,´±QÜ9> stream xœÝ\ْܶ}Ÿ¯è·4]nšØЪµúø”bUåU¹:ýá¤È«Ê¶™U¬„,WV¹s«ÓW'ß®o³"W¦, µþ]Z¡da×o²ÅŸžÆŸàî*“Ö}úZ° ëI› +5-vzAó¿‰O¾È6BкZ¿Ê6:—Î.]5\>÷J­K·¾Ž—?Ñ Ž†Ø„Ø›8àœ¨)W5$œÈ¥-mGó*nñ»u¶1yQY­?§ÕKš¨”ë/âˆÇqòÏ2•¶¨Í¶ú^&ý…5Si*©*dâûÈÏpù)M é§aÖ¿†«/âüŸdI¬,+7·þEÂ2ú¦WHÊë¸Wžª¿Äµ¾Œ—@Ìid<I¸C8•š%áE¦é1©íú '¨xWÆK˜à‡8öe˜ëiädzøOÁyæõG’"*-:•yŸJ´-솕ÈyÔˆ†¥0w|—5×Zê–Uêrý_ܤ'l£$I Zù9Œl¨»ŽƒîX¶ƒ>ð`ÿp÷y|ìé``AÛ\!XXó½ªÕG™Ê }ßF&Àå2tòPÊ\…bà­GBiÔ™»‘ÍsŒºÆAº 'Ã~2<@?.#9@.qTj#P,ª;¯ÈTFF¹À2aÑy¥­ö(¢lntQúh‰âýÒâØe|ФJÛÔ¥©÷‘ÛBVÒÏõTªô{{G¬â哌pWˆZ›âc0–`ž£ª ܼIÆÚ\;eÒø.^^ÃØHú*<õYë¥îöãWh©))sé¨)ëöCFßf]G—ÿùžº‚ëUònÓgp;R ˽Î%’¨ü¹Ý[QM¥SA„l ñvÞË}TÒ…'tI7¥±výûÖJ*ÉÑ:.ß ‚rXp›N6¼Í¸Ã»ž‘ðx$@hÍ£”8ãËàänûäoÞp#Ǭ°j3 %rB âØà€3 q¸Ë² ‚}Àÿ„Æ.g kî\ï@·#·NXraUXà]pÎú>#\öó±E0Œ-´N‚¸^!ù ë’ Ðz¡×äÞ—;ÌÚɽíHFK\·çîZ᣿ƒt9†¼QߣŒ¿‚Æ3ðk{JÔ†0@No³4ñÛE¡Þ•7ÖŸ²*·N§îáà(†Ë1l–±Ž7¢g¬~ËÏœ±ÓóŠø‚ ,yàLè¯ABÊ\8Ǥ¢‘*Àˆ›Œœx UúÆ b´åOP j]”Ò/õöíâß~¿´Jȼ#­UJ“¹hˆ¾À1~ $·ì9WH8МÑÚHGĨ–t¾X—Z)šúYRæÜ•HùAÜQ<û´ÀÆ`L¶Ž2g¯>èëC· VÖ/Ž9k‰­O|¡¹¬ûˆaZ¶ÅÅç’)³…0.7û3\ÔW>jãaö‚¥h6†íöüß=1½Vî«P)‡€~ÔXh û›²T¹±c<^Àî-ô”Kà1Oøˆ­$®¼B)¶åbU›Ï£TÜi*±¡j÷;ØüQäGH¥%ÚåRî/²AW®òÁÕ-:8®":{œšozNË¡9¤rQµŽ“<I:¬Eð§ƒfÛ—ïù4LÚù}ÄR}1—ø¹ãaȉ™ïX'¿Ç5ÁS» —S’’8›%^ý 9½º˜¬ê¡#×vòC®J r,îáŠç°Øõä³²îE@c ÅÅÊû²úóX{µ-ü5EnßuN1¼YKº!¯c ÛxöUnc´Ì]ÝŽøY=B"/#Ç-•nñXÑ´_•³ÁXŽËW|z5’ÒÐÚêW £%<Ø«ªµˆ'"äo#M½5‡³-ùSÑôÔ¿Žw8KÃ> }ðúßд¼øA½šÕ޼·Ð.gO›îP;ùãàN–UÝÅ5G“%ßæÂѲ<Ý>r›ˆòœÄ\á—å±Ðx,zä0xóëIï?´óÔ5_(4X×Üñœø–7û>8{p…{¦_î=ûÀ”cø6ËìÛúüqb8©KŽq…Î-LCð!jz¯µ_uݼ´»ëÇÛ¾8|%¾®r…—챕W²ñXio×HÒC@Kü4|{å:±½ ø“ôj8#Áã»ÜoI*ÆŠ"ùÀ ¨[Ñ=$,eKèqÆ ë†sE8gü‹©ÆllB+à°Êä°6¦hù[iøvÆFаOõÔÆœ¿e°m7­þ@ÇED)·„%¯CŒ&ó;¶Gd‘S•Èi«^o’Ç›Ú.ûå9ô¶÷ËaÛ_rÍ}gÓ “¡é ›!jßfÈŒ1”y)+¹Dòï’¤¢;¹u‹BcEïe?§â!ÒU¹)’¸š?GÄ¿¾P+;Š ÁÇ@~ óÅ£ÂK)¤3ÇóÙo]ü¢º´”©ÃÐ)ª ÏÚ9 ûÜÓ½h“ëJ N«EÅUÂß°ñHá¬Þ^D'΃øÙv'¹¢Ñ!A|Ð|?Ç–ùc†»‚Ë4>^ }‰Í¥*Ôn¾ä]²r'®›1Á”ZØ8 ~QŒiЭ4g?`Ë€û›{ûqúh8ø a„8°4Ãå öCZ´q#-†MÃNÛCæ^ •âöiö‚»nÆ™Nš%…_U¹cœ÷ù ¾#Hó6SJÝ·4ûIÖdo2ïX®cVÓùëIÿ¼Ã˜ˆÁ­cÆD³½³dÌ+ÓïG$+QawMûv…²mêÿ7>d÷t^¼¯ rg|$öÛ…„¼€¡Û} xˆwY àÝ~U༲³g»—É&Ýž¯}mãø¦?NÀV‡­<.jÌMEÿ P,‡ÁWR†Ÿrä?D•~£ çð.e®Dšv»_0E.~_z#*EHí~µj°e{RªäMºŽaQ¢£7g”5{@~[«sñ@3¥îŸžü™þýJ4endstream endobj 156 0 obj 3619 endobj 160 0 obj <> stream xœÕ]]ܶ}÷¯  ²ŒHJ¢”ô¥M‚6­“¦ö6à†½þlíݵ½ë¸hû'úß[R‘‡ÒáPš™uí$hD‘—÷ž{ïá¥üjU©V…ûwhœ½¼õÙ³zúæVwyuç÷ÛÆë§·^Ýj„vÿt°}örõ»Sû`»jE[¯NŸÜ*DÛ6…é;•+©ê•‘…hšÕéË[÷Ö¯7J©šrýpsRŠº,ëÆ^ÔMû·Ó?ÚŽ¤ô=I-Ú²nm7§ì“«Í‰lK-dåšZÛfYÚGO*QRµëÇ›Âv­UaÖg›ƒ~¾)]³-×oG¿¬lSWu]h7 )í½¾7<é®Y¶Ø„`QgìxìAhþÃÍLF»¤¥j$¾ìE7zUF= Í7vNîUiŸwb<© QȺ\¹.*åäg9ý{´PºEÝVƒx¯Â(ß…®nj)§+cïTÑÂ4º•Ä…¹ ²ÀÎã ¬_nNT餀˽páî³R]g½œ¤(Ú¦êädÅåådÿ¨¬ÂÞ¾uúɽõ©í¬¶}Ôjý¬ëÌ­Äcß‚q]ø‹o7•“®]G®)‚®½ð“ -èóM¯°þv.‘ *¸†ñÎõ˜]xöŠvc“VíŒjüÒ)KwNu=̱›¼å9ûE×U½~LçùÜÈ9ÞêeöˆNËÚ»²ªªZ³þçÆNE–EOÛ¿àY§.Êê}Yy‹‰PD™¦7“``PmÕ¶ÄÀ^v¥«¶B[ëPÐÝ1±7e‡Q«`o‘rØ'”E¾ `gôÞkªž Z‡;®:á™¦Ö ¼Ë rè!»|\zZT{†IŸoñíVÛ,Þü¸Ñ¶Q´r}gcµo*¸F”mé—à§M˜Àçý¼t!0Е±¯l'`0eÓ{½ŠÎyÂ`¯<Ðîu–ÉýÉ¿Üü»¶ƒé! p— ŒA‡5RC³í0\Vˆ|k°Uì”-1èáÛu­”몵]%öÊ›Ÿ…~Ò…¥–8ÿ_¡FÏøÜÀZTŒl€+ö¦ÏÆÚpsR½ \PãƒncaÚ;MÜ×~²|1È*à& ¬;¢­¿ø”"Æ’¹Â²Î_—»êÖ¶þ¼9©­ÙV¦]i-Êú怜Ö²*èæU¨õ×þVpè¶+ÕXóÓQüñÀ§iæÈæòŽu°îƒëe@»TŸ³ãâþÖã¬VâHô1Õsb<8M%A&û·öûFU³¾^ô]¿p¦íäf;JìâŸ=lzD¥r–B¤¦±ÉH}øºDªMmä7?oY_l‚à¿ Xü‡€êß„N¿Ä[·7ôZ ”}-€¥åª\å¶´©¡lŽŠë1ë¦Kv¸s”ÚM5‚¯_¯·¡‡ðZОÌ_ÏÿX ˆ=e{Obì>ö^–ORBgçáΤÏÀ>P¹ñiG"Ç`6¶ \ïÎìU®0:0ÀŸƒNS8|nýõݰýã¯ûÿí ^ ºùæyȺ"ÇQUõ4×R¢)K$…в¹Z9(=D^©¹g9t•T3éæ»Y¬åÙŠ¥pƒOI”jDkåòî2¬ÓÒªÞ]m´ÖS¯¥1.™nÌhÉ’añ¢À“¥’;:¹±lƒ© .3¬â%Õ”3†”Šâ DeÀ7#ÿäÿ­'7¡~ßz¾ºmÖÍT¤0,¢c¡”½ ¹Oœdœ÷¬WcuM{"æBtW[÷X™f‰î¾t k!®hâÃgÃ|Ï@[RÝÞÄ Ò¥­jõGªÒ©„Í_]’±¡¢S•}AZ)5w1MCò,+×ò¹Ž åè]‚šw ]»TÅ+t¤ðƒ6d´\ka}ò-ÿú(Ìó ®)uª6O6‰ zLñˆ…o6l—@-ô£>H¡AÏNŽ›t²àéõ·Ã.ÅÉ EdpaAÎ3Þó‚TŠª¨Ñ§êª(„6ê2íɬCȆd r4!Ð⛎±Û쇑Ýmþ”*qŠËê±³UÛ Êb1N–nMÊ=0®²—c\J@¬õ(:¬›i`\/ÛÑêW¡m(cÓžÖô`++Ñnù„zk™Ü©g'1‰ÓXIO@®ÐK§v³§àaÁÁÕ‚GŒ6à;lÜ«ñi¦öoå #l&‘G˜Ïܺ¸-wqû¢•&e'pkTÚ³.ß› Ïkôª–ò¼çþâµo݉ø'sÊcò2Êö™àÓ%O<”èm c’ºB*g¤¶ˆX˜\æéí{fæ™á4eF¥¬=–¡h0™~ *ðÚo)ìH¿hµÔ4#Ú>#6Å#äWíÄ^>k0ƒ1{Ú3y»ÛJ¯5ß4¢Á0wÁÕ³ª…uh7·ñ†OçG'öì.‚·Á£®°‰ÕZ¬0* uC!J@¬H˜$Œ¨M)š2FMLb9d}ÕLµíà[ÝFTLÁc$SÛ;˜òíÌßRÙû¢¼=Ã,e«¸ÂñJï…—»3pÝÃ|² !Ïz·<„+4 žƒÇ€ o­t[UzV E–ïs,?E@ô¬#ß%ã9á>`KÌ[Ç37Gã-{‰%@B7µ>ØgzÚ’€F?.$ü%ô,C“s[Y¿·+€Úé¡2|õ*‰ëqFú¬O)8¯¶ £Ùo»(Š4ücÜéC¿”ˆN+ @!‹zwjð!²n;ØÑÈ2Àæ o Yó®£^Rv‰˜¾P5 3'ÖžÕYyãÚyr ¸O;$¹„ÒÆ¿m[Gˆ4Tm9D ®yÛ#Nh7F˜A¸ÀkÆGÂÆGlà-Ô^`{%yž$†[“,ÀD”ŠCw~ÈÖ‡n¬–©×¾b¤Ö÷œQš+O´UÊS Ë@nOp«³Ó›cXˆ…Õ6‹h¤OKâÓ:C»°Ä!?½.ö³ërèà %@žo>£§ìlžÔÔmd—ò/¥]³‚x 5¸Á`ƒù<„b4›íEÕZ×o}ÿ6zbb¡7 Mî#Î8#b1$ˆh ùÄ{I´wû¼%vwø·g8"d?³0ï™5ï÷)œé_4à$ãsí%‘Jt°„$Qo7Œ2ƒn³‡E®)èì^Ó A¡Dº@HÖVÚENAM?¾6c8Uì±)ÀôyúE¿)à¦' Sõ=÷º”èdÔT¨ˆˆ^ûsõ µ÷GÓβµUíÌŒ*Î]ÓoÃ@ñçB~ëø—â®tžßÜÆ'€ºœn`K3ˆå n².Š!ùáÏeî†÷MDò­.3x{ÈJ:Á`Åi€‚# Ân~_q>ÍŽQzœxÐZµ(u@áƒv½Ð¾÷‡èG[rãó#9úäZÅéë‘'O¨ÙÏBß Yá—‚þ{ÿI%÷urÄ4ˆöào±¸Ë>ÿš€³c|énŸíÖìV~ÛÔ°E³=X¢­þùj×¶é¶\Å¿ö.~tøÜSZ¾T’ỢÙCí/ißÛŽó­E›,ªÅA¥2úËÊî%²tÑÌðeبÜÞó”6yŒƒf }}zë/ößÿýéendstream endobj 161 0 obj 4303 endobj 165 0 obj <> stream xœí][o·~ׯطî¦YfxN‹H€´Hᨡ’°­›[I–uqœ?Òß[r.äáÎÇåÌjeKìb–—Còð;WRãbQùÿCáíÅÁ7/ëÅéÍAûyñò¯}áúôàÃeÒÿk?ÐòÛ‹Åw‡®!狆5fqxrP±¦±UÝõÊ\˜EÍ+V»/–‹Õá8gjqøâà𫟖WšUu¥õòýjí~ÚÊ廕b5—Z­*_U횊Úþûðon¸&Œ&8«TeÜP‡G?-oWk×]ÅE³¼X­ÖVvù)ö|½®?aÕòUìùÊW5Jë‰Šç®‚ÔÆT’I37¯ñ µéh#K¡f¤h[ÄN~^F:]/ÆuiÄòï®Fe¡ëzù¯Õºq5D%–/Wkág¢4íã«v&Jr?S¡š»™ê~^Å1úf²âËÿÑ ‘vïcñ¼Ý¡h‰4:i—ÕÑÐÐF×£a}]Rá.]Á¾‡«L…Ðéìíj]ûµ‘ÉÀxÎ}]©›å1ìõ.aÅKXôpëâÎðh× §„¿[y&¢aZˆq ¯ÑæÜtËÁµ¢ÃAºnâÒ‘U$ļ ݾ‰­HÉìv¯¶®nìו;\Uº=°µCƒº;² c-"ðÆ.|+-ºi^î¿‚ç‰Kf…©‡ua”9|åõP!vÛÎcÒêšË•á|4o¥™±–ï$/âðÿȉCG³‘=OùåÈI·åZd€„p>pwð\޹ÊaBrÊ3’º¤29Z˜‹hE@#¡ì5ì—|%ÍŠ¨@N*8Ï0rÏz,QLÕrà"D%„#$t%À5Ð…!ûÜžcú5£ÒhƒöÄ©p‹9n!x´ g¢â EþÀpYÂ÷<жû=Wßëdpu9WA1ŠàªŸ¯‚à ÇáiÍ,×Õ<µšÕBËvŒJñ”ìó«i“Ÿó'ŒÖͬê~›Œ:¸…Lý6Ö=ƒ¸« ³¦1_Hû¤»¾Lc8÷ÅbBŒ¿+¸9„_1 sÀ’@:(’à¥Ï eÕXÌm1ŸÁÎæèxW±Âûv-kDÙ×”§«´—p>¯7õ—N  6¤ìZi#‰¡è(¹p‡@&èØ¢Ù\qWtd6ÑB'¡ù*mné¨99‚xãyIeÏY¶c½ÓဌȄyä}†‰ˆ<ÆB Õ`(=`›u°¡Gt’Fw”öklð[;˜`B2%šzÃ:o—’éÚKíf챪䓠Q¦[ƒµ—#¸òw°nF§Ad+§KÈ&˜ú0ò™[šFò¼<Sæ”…öÍã”ÇÄŸB¼,„c.ãWÒì¢bÎl×”â(Ü) Ò6£eµ s2™ðH8f[Åx][Å[1>p/iƃXmg ðÏ.=';ȉ!@p%!"Ì\E“cžtEë´³LEŠÁàƒ1ÌÈ:ú`°xEúzÆFKÄ«r0çõgñú[¯›Ä!aDP'~IËT­DÁ(H&zŒy*Ä ‰‘\=' KÒ9bWWPÙ.Fúðò¿^!9ZwN³19^ÌíÁ®)êS˜„íÈA:q‘C†ÖGæþ/«ÆÉz¥Zªî| ½©„æ<–®pH3Â7Ÿbd{Jâ k F^œQ™^_ǪYV°¦J$˜2,ë&$[ ZéJVˆÌª#|çè›’MÉùÒžM앞£^¯¼©¸]þ7ž…ܾ q‹„Ýöê–;M¼&PŸÉÈ(·„Ž?B,HV­÷:üq‰ÀwtË Ø-R̚˭I!51tB¿}Q µ¡G+ŸÛ-U/Ä¥Q&ñ~±ŸNd+9W¹ÌÊBï@*Ïkq>n@å8îzÁMýÙ̉kDÀ×ûwC”§™סªŒ_ç‰kl…BÜ"e|Gi…¶ µ¥„ÌÏ#­›ŠqÞÕþYZiݦù<.ix2:iíÀÖʲ´‡žev'³éO±d'5pbo-ÎÑ  `Äm’7Dåg=~Ein˜#£Aán°ß’l/Ö•Š6âøFÿ 0zQûä† ðôêÆ¬×º°3~†{ëÈ¡ƒ¢­7ÿÚ|ÎçÑ%;q8ø.JרÑ:“´›{–,|…|ó‰RÅhG1 pÆ{OMÅŒVõ'@„®ª‘ì>Ο%̨̃Xšò³TšÉ*yIä^•½WÈôÃ6†Ü Zã¦ý’sTÙÚkrÃQ…µsŸNË_”¿*É7Œò˜YïïãÁ;Øáß¶è¶¶½ø°Î ó6ßLéeóâºÐ>a<œâ³ó83:9ø€“CL>Æ/áµ|êÈjŽBñí½ ”Ó†; #\ÝúÒ{*úy{UšF¶æø‹|K|.ˆ Šw¹Y‘«ÿÆ;ÿ¼g»bÚ .ªµvO¶.Ú¾tJÇ¡Äû’0žiûS‹ØØA‘4Â{ÊüÎù¯¼i7f(¾ÅãX¼ŒÅ£X|+œÆâAúý‹w¥1edˆ“X¼ŽÅ÷±xA›™ªòûÙ㦱Q¤iÁ”%™…ùbøF¯tóÝw]Ó—f‡F·Öm:æSFíB ˜Hà²O0q,kT“\÷™Ë±jù `ØcÐö4”nCé,eÝÊm¥žY÷©²îš×Š9®J½ôû§rù{Ï¿éØþ MrIã¼´i§pžg[(èñ½`MÉ&‹Ó=³fàPé_9ú6pý‹Q©ã«´dæ·Çþ„yN`2ãsX\À¯E~º=Áº'3ú=¢ý†á•e¢j(û¿‰U…Ô$ÌWXHwpEná$®`]2Ä'ø5@•èÙ:1@Ö\)&Ú×›6^}˜81l`ÞáÐãÎÎä‹Ï:_€!WˆËm¯süñ1vŒÂÄxñr†¡‡»TCøê´•°±–¼ì=„]ª°[Lüs¬ÈQJ®ñø®ø×\&»÷DnetóXžû¦ÓrüÉ_G. ˜Úû "£ÔçÔ[º(tŠÜà|'ʵŒàN}ªÎ¿k–DVЧê¾dde‚'óO.¼XðÊ䂌ãÌø\¨1ô0!Ô8”&àÊ™o,œ‰‡7rînðNÂ÷‡ÿtÿÿºož]endstream endobj 166 0 obj 3835 endobj 172 0 obj <> stream xœí][s$7~÷¯˜7fR™¦%µnðDŠB–Pl *Imù¶ðÚŽ×N6ü (~/R_¤£îO£îYÛgóÐÕVKGÒ¹|ç"Í÷«ºb|UûÃÃÉëƒ_>Õ«oÚ׫§¿ë®_|`*áÿk_Ðç“׫O݇Œ­leÕêðùA]YkjÝõÊVŒ«•fueÌêðõÁ7ë›M] ©T-ÖÏÜ£f‚×zýf³ÕôOGñOäíjõùîðn@Æãºbš7n°ÃÓ¶ÿ­¬êšq»~½Ù67¦6ë·›-c®#Ö×îºæ¦IÇ/ýWªi”Y_ÆÇ]Æ5Ñ Ùç‘¶ øYì4¾<‹\o„±žÜnVd­¨jiôVq¤o×qŠŸÅ^?-žÄ>ݸ~tmíᣖªF°vÝ]W\XºÎ·«åZXúÕ×›-wË©¬Yɰ‡î3å>Svɺ‚û{'†Ixµiür6Ú/÷°¡¤»å±éà‡@Y÷÷£ø÷óÐëÇ”×ñ£ËøQ$å4šL{kݳf’68KY¾_ú“øøí&®Aß›¨Ùú?p BÎíÆsgMe$‘…Д,ø-\:Ò-yû2¶=“ ”Tníé®q-û‘ w›ƒ&ì¥W;å =¥[!Ü4Zùþ%ïèõëà&¾¸€¤Ë“‘ÚxÕQ£%ÏtCv•4øÎõ€÷êz²)#%Õê¯7#ò[êIX©Å%>…„þ¸±•6{Šl¦ÚùkæX½ò.™t†_¯…£Ï6ºñÌ&t%›Z9e˪†±5„o^l¶µs)´t„\ç³ØÄÁõJ(ÛyÑíByþþ¾ŠoId«¤ã–2féwŸv¦™Õ®w¥kn¹—¹ðÙyl|zh)sm%¥ì²ûÊhm‡)!Ä0œ’fý„¼~_“×n×Û5Jóp:мü©ŸœŽ[– t†]sh»ÛµN»÷:ýÉÁáGßÝиYg-—Z¯ïÆöàÉrÀ²ŽèLLìéܤeCáAˆ²³…‘6Ñ|ƒàq><¯ØsA“‘Fª¹¢q&£5òÄ´Œ$¼jÿ\ÁnsyìâªbBÚ¢¦nŠÉ&2z‹&v‡,ÓUÖ–"ìWðgWpÓ©Û@cÑA,BUâ«Br*éWkC‚œçá)·H‰KÔïí8ÜÙKõevái’ñ/I8‘xã“·;0î1TX É' Ž7çކ™b轆ф‘ŒS±À¢q{X25¬c®7Ú›f(/a C˜å×S] +çu‚ßËÎÁRF4î%" “Ý `¥Äé,MÏ¡a  —¤EȪE¢Ž¡žÂňgØ@‹»¢ì’j~·ÙŒv{@…c}Ðí¯ÿ¾gþ+ÎqLðay­$m‘˜=”ð)*|2å> `I×ä rõØýJÂHyéòèÙ‘ÀÞ;~€°`@W¦É*àÿ!Þ!ºõßqî9ëæúUˆv$»u"]×BSŽëœ?]ik•€ýäsS”´UIב¶·opfÐÂzbÀÃÞ xôîu7Dí| 7¦ä >£üš N'éî§ï$‹úͤx€&ö£Å€à4<­&‹éMÿÍdfZ¶Þ“µ[’yN•+è’ò«O>ÖÝIÁŽuÁ«A{Tp¢4¡ŽéÀ¹Ô£ ?pH‡PCX²˜hÃa˜éÎ,íËÞç1·‘S|‹TXëS,ñÝ~殕pÿ§Ê·|I@ÿI8©_Eš¥-š4ùv':t€$ò4Ò&=cPm2ÁžÜD”Ñi¸aTònCËèC«|5Y•‘Ì﹟3€ì$(@x.j,ô1Ç4Q ¦Ö:Ff”³ë¿õª#(5—[04ó»Á.CêL ©³’4NÒ»­Æ€8#—¼ãÐ뎄~NôiÉnâQJºÌÓ-@XðÒÏ´XêxÑ" ™;ôíìðŒ‹u è]l’¨F°¼èÙãTrÔ¹ ²«Ž.ܪ[8ˆ›ðòžPvëUw&;€ã'ΪÚH’i†P9©¼¸ëª¨¼¹›úÔÙJ· •"ž\¬pİ×EÂ"ŽÜ—xÂA{9›£¬OQáHÌ9_ÃÐo;5©”Ÿ’§n:ïªHœ$«FhMØŽ!"_Á¡°é¤‘tdZvýU÷iÌUwQ‰'^Aþ"Í`¸ý¢qbÙ$á¢?¿ŽÁRžülÃ}mªb뿺—²2ªnh¡ò&½„wÎhãÃ¥’FDc²£ŠÈr ÔVÂŽ=£Š$\›€C€=ó¨°Úí*ûÅýnáãã‰|žnâ6Î0=3±™¼=¦CáŒÅÞšy‚uFW)PXŽ4¬5˜×Èú“S8uv1¶ŒJØ sü¬c€àøAl4’éžB¬¹q¸óöPçcjÆYª¢7D ”1ÖR†cý8ü-r1‚ˆ-Áa%£wg@0nÛׂ@q¿õÔíV¼>(œ(Þ0È_BX™¼ÄêG˜.Ò]Í0=(-‰1š¾â¶×Ž­{Ff=s~Ör:¿"zZR©YtÇÞÉ'…?Qe¼ëNaHYqÊÐ!|ÆTCYÁô£ ö%éb>1Ε[#jÉbÞñ&õ5©Ûa¥i­ëU°Èó‘ˆ,EoÀ@Aœ8Ý#S¶¬Ø Ԫơ¬&pÌUR×:H›”jRãx˲F‚ª4h€§Eâ^µì©)pð¶4YÉ*Ãl¤êfxLN¶)6­ÕõÎi…ìAÎÇ„· ¼TêJñfR^êå“ÀSqŽÃ»‰&é_rë”FÃõÁî-|OgÂhYHÓ^ÄMˆÉ&¼†*ÔU4%3Ä¥’$\|N¤¸%N93ÿctÄ3µÈHî[‹œ™yÔÊ] ;Ki@P£x€ˆx¤WÑì,Éÿ=hIÐÞ¡R°¸wš®è|î4>ÙmÊ8>IËÛI=Ƚœ¢&#^%&t8Z‘È8ŠL^Áƒä³#Øîì$>¾„ªX›J2V:-ýuÄ’q‚1ü u{ÐZàdk$t@RG˜„E‡‰ÛÓ7%¶Æôç¤k|¤íyÇèà%3¦Ò*pÀDKíØ)Ò!µîÎÇÒ ÎX&Xg’'Ĉ„ÐÏU¥, *ÙfL¿b Å<÷rOV"Ì4T¢KE~fĈ°oÍ«¬HÎdÆÊ¥‡ (½‰ ·Þø„}‡cáøR‹7¦‰ÞÔ¢çüåØqBtÆ™–©WU¬¥À¥0Ehƒ•Í®‚ˆ±„æ”+`ÁÇPvñ֘Ū`25<ÖoÏ W-ãõ4 ;#\:X–«b!’Ce«bé2¹¾æˆòeM¦Ê`_árÑ%NFŽo½´%U™Üü•bYaÑõœQh;”õ倯×H/9à1q ‡,N3$q>øˆïÓÙ>Vq­ôLøhš€‹°Ûo#9gPý5ÜŒC!ì—߀®•Òz‚ƒÈé3/!òšØcp;Ì ‹ôî8r™¢Åú¬´œ…¸²U#, }Zà äJÒiûÞp¹Èøa íy…0äøF,Ä…»oD\5…'3éX¹2·½¼Øp'Ö ÿ¦0ó%·r…9Ä¥¿ëK¹:ÀË*ƃ‹Åþ¹CïKbö¤íýœ\¢Ãye›B™èÅì.²ŒÕ K°à®rŽI] ¼h©©¤ŠI‡’”±Ì ¯Û(„DwݰÕ×v,ŠÙ3Y; âŒª·A  )OçWàaÑ-õÀ@À(í»‡BŒˆÆ(IøöG0D%\úa™Æ%wÛ,±’V:u‚ò~qE%R£ïÏ ïNÅer ûÎÖEö·†N›5ÖJZë×&øå×fZ|ž«ŸÂúÛºÖ$%fsL¯¥´•]Åóh"ïÁDŽÛ.8NöÆE*KÆðÏ3ã|NŽO°åt =ÐÑeŒÓÇä"Föû*hSqûÿì>’ÌrtÈI– ÕÉX]ÉÚ”ƒL?Þ&& £ò-ËØ§ _í›ÝkâÔˆRÞÛU|:÷žgŸ´ØÓ¥¬kâRnmÖ›,$pf˜ÊBõÂôgeôôg앎BiœS¬ÚßÍH1§¢Í‰¹¢ÍÔÜ£yoAÈbyÆÞ…ø†ÐüšO átåÇgåw+B7¹Ä ^éXÝ<_M›Ý>RM«ü>±ÌgÎvB¹¬«FÕA.snºˆQ›<%*Ë{Ê> <%öÆS¡ƒö7[ÐÁ£ZÔÿñÔ§½ì´%H¤–ß5‘»çc ¨¬²‰žzDTˆêQ="ªGDUDTè‘Ü2‘ü’°¢ú,VŒÎLï«DX(ýrfÓ#›»ñwÂ2ãªûã)¸%‡ q_I"bH(‰.s/"\qëÊãÏ•äîÛ8ÎUå…êÒ"ä®mAÓÁ'Dó,YMnvoOwÏÑ-pAÆW³w?²FµÈØ¿ÁCë©ñUZÉ&¬ð7±-Yúïâ[|`c×yÚ1 ã_ð+b£¡lLòu¸=²»0rý<$9çèHƧEÁx[1ß«óè]Šûw‹|ý¾ü±Hã]¤…áu¢X›—‰­Á}¬caï8î#:Šß_MðȼwƒÅÉ»1>—µ"w2ý>©‰FjúÓê[áuWp×oTOË*ÑÃÌñ%÷æÅ"Hs_^,“þý»MÛOb‘ŸüÙýû/–Åêendstream endobj 173 0 obj 4637 endobj 177 0 obj <> stream xœí\Ùr·}çWÌ“3ãÒÀ­»WReËrJ‰¼D¦¤l—ŠâªXi‘”œ8~Ì$©|o€^€‹îƒÁôpD‰Š$=@=h,w9wAÿ8+³Âýíû§;<¬fÇ;ÍãÙÃßuçÇ;?îÔLº?ÍÚÞ?}¼k_ä|f˜)g»G;3¦.ªvT>㢜U¼`•ýñtg>[ìþu‡s¦f»vvßÿv~¹(˜ÔeYÈù#Û¬¸E5¿X,+úÓ^ø‰<-DU¿û{»ãçã•Pvò݃oíHþÍ'‹¥¹âÊÌOKÅD]5õr±Ô¬(¸0tÂs×·Tª¬ç‡áé, vû>_ûTÔʾåºJ]Ëù~èz´°DlO¨W•Lª†xÍêga³ßÍÃê¾¶#*ûƒ1óÏKawRšz~?tÞµJ;S)èï7KR’ϯè’iŸ‡ÉîP‚û&>7KW¡XQižìéB9ª¨jþ,q¤`µ—ÖÃ[û–Gì¨Bšùw‹0C7‚,øü¿ô¸ü¸_-–†îzyß½ GùŒ2V¿)Ìœ²ðçË+ËžïÏws@\Âí FrÀ%GTga@{ö+”õüå°ªV*bqr@ÅÉXdߘFd²CHÚxaÊé ©èĬL–ºìÆíšÂϘʑc)LÅj®fn‰Z´t \’[e£až¸EI]i1ÿûÂU‰Dw²fBb,'I’êÉ= Ð€6«¥e;iT»™ìDŒ®à4øhWM>äÏ—¥é¹5%ͺ(ÆŒ.™*ËþMF¥«á^IVIM¸ µ0·Ð²xž‰‹-ËÒ¸®¥ŒÙ‡©¬ÍØ´÷{µ¦i±vëiygZRJůõÅÂ>,M¬2H׋ľß`ÓbÏ—ñš7J¥TïLËbZ=22Ë‚œ¶v¢³œuøÕmv?‘xiŸÖö?•$>‹LöÃBërljVJ^æ ƒ³µÒ7`ÖѺ\0%j>ÿ“ÝŸfuY(:TœXfö\†:œC>&¯3…ö–LÆv&¡0Ó_ÁSÌC-ì1‹íš‡1"“…f¥0DyzŸ_Ôo»Ô–8 œ.jÇj;¦ ¨xµÔ9Q{{¥sü!ìК QÚu-ügáŒzj ïráÀBÁk*•Øü„…_¹-Z9Bœaß)ùîg"âÑÆBï8HH;ñ~‚ø;oYO»Ë‰ñ·K†*@r©bÆmÞqŒÛc.2ÔÉÌÚb!‚ 諲½ŽÐ¡$H¤9Ä:„Ê4Pµ*´¦ÝŸ6ç¥ìÁü­‡i˜ŸC=Ñè¬ÃÁÿ1VÉâF ­†Z1âŸׯ@Ž™ÙSöNª(Þ­ùqx+«äü6€-ì…r2– ]ä­ãÐóVðݱֿÙû*:oçn™ä ýÓh…È‘8G}¦±QûVE™B¦YKA­ô¿ÃSŒsû¾U4X‘þšUEfWW§™/éñbðÊêªTÛŽm  ÄQv-Öü—•%E¿o»mF¼<1a²ªÀ1ÏDÌŽ ÍÐec )T¶Ú9'¾÷Éúo‘ßòïix OÏ•²ØßCÎÚ?òÁø`ˆR°Ò¢ÌRö°£Ð›ò£íļdDzŒHšÜJèØ á’ ¡5à¸Ç‹ TWàüSL/ ‘ÎÆœÄ¦÷n¦ýñ­w2¡gI È“°âga¬¢PÔ‘‹ø2 fìêd£èYw t8ƒ³mÉ_Å£-f"3¬¸ÇG‹5!ÖÂÙÀý%$[´Ä3GˆÓ "øÿò5aÑ-0©\[‘¦µÏáYÂaa¢pâcÀ€•wë\Ëz$ï·^ÎÉk¸P#¤mHyÛáÀ[/è¯h έá”ÜO“â^™J6ƒ}§s¸ôýaVÂÕdôRLÜÀC!˜VÄ×G‘½Å>ôSj È”xv\‡á>Y)y ¥±¯àFÆÉíQVùÞC-¬ªÄi`‹ù WÏv5òÒzƒ±_Øá„îH)N8ðb}è[Сz:öá©èTF*¥Y%. œrakeIŠ‘ ¥¼»\‰lÏ¡—ïÕø$~ÝôUµ¨:©Ï͵™:ç Y‰–RIk{çŸÙÞVd…®*Ê&ÁWºç[Äô±X–VeëÊÌÿàî”[~iË…»²¯a»UD8 ‹¬ëõ%ûžbÌ–¼[!((¥0fþ‘ïJFÚíV%‚ki¥*"xþÂò»r5Sª•¶Q1”‰–¤*†RR1ÔMk¶y¸ŸHœÀ­[+» sýƒ$Ç—ŽVêŠ!a°„d=œìåÓHJQ]Ì9d¶ç4ª‚³VÙƒ³aõž`_EBÞqœ"yã·õð$ Dˆ; ®vk%Ñ­~\j#¯Í‘$HTù½^•û½ÓG{}Ô(eM6Wš.×ö`?'U]š)«ÄîJ„ó´‹‘Ü­ÛUÍ Ãoîö¾æ¦è§ï@H÷¤böŽ ®¶Â5¢„£1Kdû[Ž.ÿ¼ë-’~t±§ÉDzÞmWðªx—˜Êo|Wº¨:_–Ú®¢áA¿Š&}WèÅxÁÇÚfâ—F`Xa踦6¹—ºÜÓMÈcs€uÚOÔÞ¸C®e¤þè½é†o…de‰î¬Eç1¶Þ¾„©K²ŸR(ºy—m}ÊV˜õaàÐðûÝÀ¡÷"ô\‹bÈ¡éϲlÀN8>³O8[ߎ×ð ²dä᎕v6º¡ö$F8pÉÈ$k+pô¤ÄŽWµ²Ž1íf¿‚5 >TŒâ°ÕÄ {ðJ(ý±HìoVãRb¢K¡MÍWØ¡è)èÔ¡£q×8=ÿZêôHXÎ÷]uzCÆKb“q½R a«ù¢³öc¦},ìÕ@žÓAžv±[E<÷(›÷‰˜ÏlS¸-— ›;7!V'«¾‚2t–Sl ¾]’ÿ J@߈âX´-)ž§{úm~¹p®š*ÚB>QVŒG~R[Ó'­w…bçÙ»ýq줦•ôe«Q<Ýò÷_<Ǭá®d\6\9ZDH§E[ÏÉÅkÀÇrV‹~÷–Çã¼?­|~Õå(PÍE q*ô›T9Ô5S¦¸v•C®(…jîÕE)7Rå°i©J®l㪨Vè_Ã¥EôÛ…õ`Œ÷DzysXÕàÒ@U¥!/x™{ªt_¾•Š¥r¾¥©¢Tw‡YxÙ'ÎÃ=é`HIÐÑ¥¯Cv,º1’A‹Õá¯`eÓ{ûƒûa©ûz~!$yŽ¢ó£¢Ãº¨RŸÄˆ‹Avæ ›ñKeïS屈|^×;€^ è{zi“Ý•ýÉ'»ÿem…}˜x¦‚Ó -!)ôõýØ“î3Ì ¥J¡2Î>Ž—ƒë›Q§‡dÌ6ý)í¿Þ.÷´ׇº©e5Í‚·øp)™®d“‚ëZùÖǾ5ó­KßÚó­sß:ô­G¾uê[g¾uà[ßúÖ÷`¶ß€g?w-Ñßγ¢Ð~—ØÝÐt,õ3xqÝ͆¥ÿ¼ñ‰oýc‘¹‘ýЮù(4¿ ÍOàÓ;¡9 Í"×áß¡ùih~šCó³Ð$ëÝ ÍÿÜÔlemj0›ªý};zÌ$€Ùqr8æ¾Õ¹ŽµÅ­Â€ßÕü=ߺð­'€oCLçþ‹á¯ns—!ŽBóöÅ'ð^h^„æ8Ø#8î^hž‡æ!|Lqšg¡ùrK{kâóðôëÐ|›„$¿´~޶ ¦&EæC޵+ÐÑ·Xq¢|¾‚–“Ç](©_§¤ï}¨Cþ²™:Ys Ÿ\o %8½ã TÆ~Nˆ÷`ß[¬HVí¸a…);¾žz)J¦ ¨—A׎”Nÿ1c¿øÿ°kylsowçöïÿ-!^endstream endobj 178 0 obj 3756 endobj 182 0 obj <> stream xœÍ\[·~ß_¡§V*¬Éð:3N 6Ü¢—®½v€:…±ÖÊë­½»Ê^;EDóÐß[r.ä!ç£8Ò*Nã¦G^ÏùΕóì,Ÿ•öÏÐX]}ñ¬šÝµgÏþÐ7®ÏŽ~8ª aÿkÐöêböèØ¼Èê™çøíQY4M]6ílV7柚Ue]4zv|qôj®KU”kä¼X,y¡„jª¹\È¢‘•óÙBT…’¥ž?Z”…æ¬aj~½X²¢’ZÍ×þõß|ï››ÅÒlJWJéùiŸ÷“ =¿$o¼º)íÄ®ÃóÅR¼fµžß‘¾›D›NãWÚ\KmFþûñŸ,•جié0Щâ¡t#zB™—ŽÿqÄX¡ ]ÿrtü»WfU¢(u#åü±]*¯«ŠÍŸ.–MQ–¼äæ²Jk³—iVŒËÊ®ÇP¦d¼±Ë4ã U‹ù?–\¥R–,•yV)>?m_¼¬ •]óÆt CßùŸ.ý€ø]2ùÓ·)¤i}Z°ª`’Ó©/ý;g¾If½0;–fÉb_êz¾²K{çûŸÀ¥Ö2O k´[1#1n%º­YÚ‰†Nuë›Wp-¤Ù ™|åÇ}ßnÛ¬ÛNa敆«Üf£ÔüЮӞ™àÜ=ÜО–Ÿ¸Ð…fÚðÏñ©‘«µß1YëxkH3²V2¡_ 9/²Ø€žv5æ˜:À[ÿ”¬ñÍ@º’&æ­5=w´°àðÜ`=#–Ê ó@»ˆóûíp,ÒüÖ4K]UµdtÃdˆn9B+íˆSÕó—V¦«²af×óŸÍ¿Í»¦@L^U *{ìËáÜ—=8ØÃ÷ÿ"î~vfæóF°×M%ÿ¼ OéM·Vm˜Ýo€X&Ä^y—£LÇ/Ô*AfÇ-$¡ÿbO7¦$%ü7Î Â&ù;È–‰& ”‚‰¿YÔÕÒ V¶1ûEÂmýÉaž m"v¼¦…ìr²½…*JÖ»›æ¯ß¶nã±™F›nš÷´ñìáþKGÚNã6\UAì±áµ‘ż¤¥R.‚«s"#d›ýeÏx.·Dd¡Ž€©šM<Á×t8KlÇM±‚*"2˜ûçȪ ¶Î‘…@HO„fä¹5Jå¥nÛL‡ÐMÓ#Þ™î÷7ÈlÃx‹=B,Œ˜°Ø’ ³ÝÑ5bÂö0ÀïK¨Jvüeþï¥|Ùž‡Ý.pôųÆÅšŒ¿hh¬¨xCYšÔ6ìU—õüu@î¯m°É¬I ý®Ãëp+ýÑ}²ác²[?Ô @Hx³PJâb•ÁF1ÀŒÿ…'ED|‚HŠÃ®±oŸ’?¹ðX¶8ÀâžR5Š”\©¢`@¤J­wßÉS즴A8a–ë ìÉòž±æ›Òvó§n[/ G„ƒìjMŒ`m ^F·)^Y•?æˆ&”qפ†=8P6Á?ÄÎj ðï} Z­±³ª¦‚S¶\¯Œ=ç[‘VëB Øh7>ÄÑŽ²Í F ß3 ²·n7…-¢å Ó€Yþ²`yêû¾¥rÂjBˆDª¦PL"}}ˆ´d:Dnñ4bˆÄÆ\V¶Åƒn¡ß¿&Èë8°?1¶³ÅeÁ‹Á~!6«‰ƒñÁ!$«¿ìæF2HÍÛY6ÌRYÉyÑÅÈôv÷šµ’¬3¢ülŸ›à †×ȸ‹ºnÆ($Iã¤ÄeÂNb’§ñ‰>¶1SøíbPˆŸ;%ÿØ+y›œL8ÎÈpqв5AºgÉÎ1¿»¡p\:‚Á¤Màv †—Hæ‘4±ãóiÁ›‚3>X9¢.xÓ L’J©EáÑÊxòè²à`V3f¯ŒSÞ¡ü@ͱ|¾Ú+\w?3Oq&20Ý\‡ \¶ƒü’VQ´ÇÃð ;ÅK‰p FÙèHà­g’Z§­`$[×d€bœ¾ê€|É5+ŒŶZ÷¯®(â~MøLG¬>)üÕ‰'ç ö¹¤ç:*l˜!~%f‰ÑeKwIÍb²©([¬|A–Ææ*v¡ÇãÇ(OöŽ5Ë U…RΆPó6¯êN9{ëÁlUíë Q×ë·dŠì9ŒÝDGÂ.)­#Zpmü€¥×C?!øÀ­×G¹‰TF ÚýaR²‘@‡DÉš¨ZŽ¿©…¸‡þHûSaÁ„Æ9¦l¨òB1{Ÿ«Å æ7>äK´Nù>i:Á*@°ºÂ;üQâÁŠÞd¡&ªó6¼âyE-:ž¢Ô§º:P’Y‘¯T¼ ‘`¢Ÿj=´QòzK¸‹èôÓÄéoÉ-7•ͺÊHî¶Èn/âèW$å@ëå@ìÈîåÝ"‡/ŽöÂ?å1OÙ· Nƹâœeu’Y°=Á¤7ku¯ÛÒÙaæ0t@±)c¯UMƒÂ÷aÁ…{üâA–‘Rü‡šäµTú}{Ö?8¦i†T+Ú®kIîÆ5H†ý{œMÅù» Á/÷ãCu&øI÷!uhÆ î£š;E“ÐÎSÎô¬0¦8᥾3Äø·>˜FÆsôÝÀÔ³[ç!JƆëGöZûû¶LH_O¸ÝUYH!ƒ(ÿ.I?¤këÍâÚÏsÒ?cY§ y%>b„7Üà|å«”¾„¢9º;H’¸•ŒXz[eÏ0ïf·kN,‹aq‚0F¿¨øfßP÷^Á¯Y} ¯õ«á0¦,‘Å”MdËž1I_b|¼ƒ'–Ã&¨±`]†=È·“j*ç[ÕJâç4¼òÈÅD¼Ÿ±ò~†÷µî\+ö| #Í¢y)âYwV q­ïÙÃX11 ^B‰Ä1µlLšÆ‚ž…sUÛ³k©@{ícõB°Ã#é›3«°Ä8¸Är¸oCרÙŒZŠ@]˜©,T©@†TCJ M«£šGœhÏÚ„)ˆò¸½¸Šj™Þ&”ö÷ÏŸ'{HwÜÂÙÐ!.…beQ»àвd©D¿áHQ¨JHK®¾5s-ëÁôÍ×ò¯\¸Ök×zãZ×®õôóãÝñ.Àb~Zý¯¬À¶yùÚ7ßøæµo¾‡}o}s g#ÈrÒÙöSß|ì›à{Ï}ó;ßü¶mêÚHÖ7°ïŸ³‹øo>ñÍŸIçEж &J](¯h2Ë¿º¦¬‡kŸKnìqÅ|åÎlíZÁ³à‹[À{W®u xq!9½xè_ÃÝý{h‰É@D—/Œþ²[å9áï°æ›Þ  Vi^²©œÓ|Éh¤*°{=¾›)REOéª\úÂ0 çÙlÏÇȹؖA±Ù˜ÃWbÄ)klª‚•1vS÷z’ºa$#(lÕ¨1¹4—Àâò; “ŽHUãõM¸´šqª²µ×ØT QÞ=cÖ÷ɪuùúSØxÈ" œ0M\4Ø ¤O¬@Ñ…Ù‰ìmÚ'Îëˆï àk®*È\r…&Ìš2Óî8D×ÒG6 uqOc¢BcÒ÷Ì“3/¼™âþ¸ÈL.ñ*•®ç*¶W›öûþæ$ZÔ.ѽ_ï²û}£¸({9f—Ϥ*¼†Lëp‚TÎㆥÆàÒ”«µû_²Àž-Ù(§ÈÂÅïc£bKt3V¼„nÓ*4ÒwÁ0pd•еž™Ù¨h|ÕWX*tf˜®<Üw'®{—׬±æ R¿‚ÜÖ¼””­ÝƒoåvˆF`œypx½Ûh%bIU³à}ãl5¶3pú-£$¶µ"` ·;»3'>Œ¯Â€ÅTÞ ŠŸ4=´œAHÈ^¼ÉÞħ3\YišîÊJk€¼ð·Ò·eÛºëáî÷Ñh§çG,¥‡{jh£LrT€#Hþû ‰ìk„€c6 Jr²DÎn¸KÈõ‘A鞟58¤žÞ%!öY¿O“­‹ÎŠ>þ†Àø^uî»6-x$ʺ=‹…_¦iŸ€xŸ Ø€–àºÚ-_¡ÚìäÛvøeŠtS»Õ4ì“H9HœùœpùpOƒHž«;ìoóºÎzÀ¹÷U¬÷­rÚé34·ˆê 'çIñV¾²OUYJM«C˜o>ô‹%5#ÚOöµ™L™c `zBýÊd«˜Ð…Ô\šRµ ¤B·ÍpÅ1䈌’pØ60„Õ uaÆ Ü’‘lw¶*àeF3¦Ç ÿ‡Z©;6xdhȬ&T°Ò|ï;bà®ô„2¼Ly(¾lCˆd…ý–¤z'¥ýµ>³§=p]Ce#ì ‚a·qÝ"Kÿ”k£å%ï>Q9*xr|ôWóç7^œendstream endobj 183 0 obj 3621 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 92 0 obj <> /Contents 93 0 R >> endobj 97 0 obj <> /Contents 98 0 R >> endobj 104 0 obj <> /Contents 105 0 R >> endobj 109 0 obj <> /Contents 110 0 R >> endobj 114 0 obj <> /Contents 115 0 R >> endobj 119 0 obj <> /Contents 120 0 R >> endobj 124 0 obj <> /Contents 125 0 R >> endobj 129 0 obj <> /Contents 130 0 R >> endobj 134 0 obj <> /Contents 135 0 R >> endobj 139 0 obj <> /Contents 140 0 R >> endobj 144 0 obj <> /Contents 145 0 R >> endobj 149 0 obj <> /Contents 150 0 R >> endobj 154 0 obj <> /Contents 155 0 R >> endobj 159 0 obj <> /Contents 160 0 R >> endobj 164 0 obj <> /Contents 165 0 R >> endobj 171 0 obj <> /Contents 172 0 R >> endobj 176 0 obj <> /Contents 177 0 R >> endobj 181 0 obj <> /Contents 182 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 26 0 R 33 0 R 38 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 92 0 R 97 0 R 104 0 R 109 0 R 114 0 R 119 0 R 124 0 R 129 0 R 134 0 R 139 0 R 144 0 R 149 0 R 154 0 R 159 0 R 164 0 R 171 0 R 176 0 R 181 0 R ] /Count 33 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 90 0 obj <> endobj 91 0 obj <> endobj 95 0 obj <> endobj 96 0 obj <> endobj 102 0 obj <> endobj 103 0 obj <> endobj 107 0 obj <> endobj 108 0 obj <> endobj 112 0 obj <> endobj 113 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> endobj 122 0 obj <> endobj 123 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <> endobj 132 0 obj <> endobj 133 0 obj <> endobj 137 0 obj <> endobj 138 0 obj <> endobj 142 0 obj <> endobj 143 0 obj <> endobj 147 0 obj <> endobj 148 0 obj <> endobj 152 0 obj <> endobj 153 0 obj <> endobj 157 0 obj <> endobj 158 0 obj <> endobj 162 0 obj <> endobj 163 0 obj <> endobj 169 0 obj <> endobj 170 0 obj <> endobj 174 0 obj <> endobj 175 0 obj <> endobj 179 0 obj <> endobj 180 0 obj <> endobj 184 0 obj <> endobj 185 0 obj <> endobj 186 0 obj <>stream xœeWy\Ǻíºm·AŒ3Q .Q£"* ‹²© # Ã" c@£* ‚‘QD@pA Aã‚ y“<²Ý“¯'5Þ¼js“÷þ€_wWuu}§ÎwÎebD‰D"sw¥*I©ŽÜ:Ë9VµÅ3L¹=Q)Œ¼ÃOñoñ“WãØ?è´ôdÊ£ø_cÑc4Ƥì­ñÌø·ÇÃSÈG‹DÉy%.±qÚøÈðµÂÆ×ÇßvæÌwþóÄÁÑÑQ¦}3¢pU&D†Ç(¬ÉE’R­ŒQ/V¸Ù*UäfE¸J‘ ݲE¹ExÍ/T¥ŒR¸Eª"ãâb“6.¶Š9³g;Ì"ÿæ|–˜ X“ ðPe(V©CÉ*ÿ (jއ6fóûÎ;b·¸Ä)=]·z­ˆwKˆðQG®M\·*)ÊW£ õKŽSØÙ;Ì™;oþ» 6RÔ,*ò¤\);ÊŠ ¢¼¨ÔTÊ›r£fSÓ¨•”eM­¥Ü©éÔ:j5—ò¥VSó(?j>5ƒò§<¨w©õÔû”3µ€  > \(eJ£Ì( 5â(sj"eA-£æ’³ LÈ‚QT‹hº(Lô‘¥‘Öè‰ñ ãr¹É~“Ç´‚þ¾@ÿ̤3çG¬QȺ°ÙË#ýG–ìùÕ(zÔ{£rGuújtùèë£_Ù>æÞ˜c¾k2ö±¹b+ñfñ]SOÓL‹à”˜ãùg'D<§‹æð Û¿Ž¦ù}L5Œ§ñ/Œ˜oñ`¬cÉŒ<¾žÃþúÉáüdÛ1ôõ9)´ÏÆp·´½(w—LEE™Ù¥Hz5æ =@‹ø/ôzG&Ó4ä2ø+Ü#\òë˜  i\ÍÀsè!ÄûÁ J”øKÜTä°ÑË)ÈC5Íg±øù°ê«æg·d­·ë~F/X·àž,KÄ&Ü­Æ×ÀT¿„P¹jmˆŠlÒsKË=‰Û_“s!»œòÛcS…Ÿõ"™ëÜx‹çýj Ka5XÂÒ‹2lÅÄØ»úÏD쌀öWýU/[îÊ[t}ŠÚÐùäÒÈ"ÍdDØÁC©â0‰9aÆg•ÔÃß Ï-ðø&œ-õl޽‹¤0Ÿ æ!rI/HÜŸ¾í¾6Ú+DÆjRÇ7,!uÌ÷‹hùÌ€Ñ ù0Ó'À(rÝä(öGq’¸Â–¼fÔ€.íh=Þâ]጖ ÅªàÀ5î!„ðÓY<îÅB»±ƒÝ`Ü!Ã2F£Ü„Xe}G>:¼¿FŽcòë/*®¨k*iBgQ펢ØC)¹»Pá}É# :­1V§åð8däh³Ä>›!<‚Å.`ŒEà~hùå…ìñ·- F0ŠwÅox,Oà~¾¹ 9ÅÍÝä)Xáñb­=>Ñãš—oɯ~þÙ¹G„¤C½Bôa¼H7‚|«Ï˜†þ«ñþཟ΃éК.KâU2ÜÀ”é¹A>†IÌ·lœí´}j¤—|Ó²ÀØyd)xAÔfîÀ¿Šø%|·)lË«=v´I ÐÁìê̲W¡¹;r´Èù…jìRW[,e4vtc§wl€w§û±Î•Ân‚b,Ùÿ=Ùœ"<™'èzE[g{çÙç¨]S·† ¯ ;âN:ñÖ°Ì`úyŽ»±4Ø1í碂cvmII’'DD~è'kÿ7aÁèáà¦Ê ¹B›óâ!ÖW r³áVNYjÒ¨è-h*îÿä|?’CÙÇ3O‹Ý9{Ñ>´­ v.ÿMÐ`óÍ«¡¾ÈZÖv]ûød!íBZòuW\ØI¯IKÑ2¥ß*o×­ØÍ`±ÑƒÅƒ]M%7ºe:§®öÚàŒ¤’¾À­eµòœBOe²Ò²w¡ÝHY¥<¯9§½¸» Á"¢/ªaŒæ;,$_ã“S¹OÏ-Çâ¥Vsü£.ô‚éC ûåC®—Þ€pQá ƒñÜþm¨:ÿ/Pÿ£ýf [˜6 ˜L•ÆqŽ8Â~³´›9¹')7É6)c}PZ{~ˢ'ñ’ÁÛš¢ µeÔ&õªÃG“e©…{Žd—³x2“™’½e  µÁãÕ­é]ˆ…÷€ê‡ 7·4.¬”ϬY,µÖ-m“I:Nk¢ödïËÊ0Ô1äCî/éÐ~ îö¬íZ7’¶VÅ î–ôÿÝí‰wÛ<×µ±~²ëP1d¥PJPñ<ãØã‘`ã)øÆ×£f–1’ÎíÖ´ÌárÉ`²ÛG[·[¢´Üô¼dæ2yÕ; ö £R”—S[@,¥Lž­´D Û«ý‹ÛªªŠ+*YÉ×M•·?%?lŒ«ŽªÛ|Ô±ú±Aý’ŸC<Ň'æ*ßS’rtçá(Ŧ©´ší1ª´€¡NãÝDüÒeK˜S0MnžF» ýÔˬÀÏ3S³RÑNéº&ÕýÓû×ÈNü”šµ›PFªN)8v0·ð@±¼ ZéÇŒN 1–r?–Ða~ ¶ ÜÇáñÂ-éŸam2{6΄`‹)°ž(¹¯ åpy5¹GÑ (»n#Ù!`ól$]²¡®íXîÑýË_ñ4Ù’µ>æ£mû¶¡D)fX3_öW]¾%ël©ûôø—dŠq›á„þ›1ÿÚŠ…íNc𽘈!ž:ÏÕ‹÷òbáa½@Š© ,âÇÔ£ÿªþýÏ jafW‰}îƒ)BÒ¨(æRŠ÷¡c¤Aå¾ mŽçC…¨V{V]UX¸a´È'›³0‘)î8u¢±Ÿ”íŒNJMÔ¤Ë5»rI÷ΰÚ½5U•è§ÜöòDÊãÊÚ’Q’*Ϥ_˜t])¿Ùž™‚ö¢XI2JÏÝsp ³l Ñœ¤ÅQ²rRP|ñéÖúÓwŽÈ¯þxA9Ò#¹ûs  Ù„á`§šóÖB0šÉàZý¥ ü%áò;Rå_GÌÛC5éPBc'ò¼`=,.‰9—àT\-ô1˜‚Ó ì$\ŠÿkxOwlŒÁåo^Ò }Çî¶\¸Ž¤ •ꈴìô¬]òåø_o†¾h9IëK5áiY)™©r<0,gyµ¤éˆœù·:¬ô÷Y'K½T÷>Z‚ã½V?³ø¿¹ß#G l#§&I~sh…´¤Å¯ìB|Ï$ .‚ºõ¿½Ò/Þwƒ¼ éÜ"ä%gâ'BîòÒø‡Cy‰ü ¸ ‚,°A…­‡$µTçB¢×màðë×*º‡×:Õ0|¿“±ïùTÎJŸ:$73u´³yã&,F; aI'’Æka IB0žÃmLe}mŒ~øåµúÚº†æ«å·Ðm":ïucsl?mvÚ.K¦èÇ–¶^Ķ×E„h‚"#åªmÛÔ˜qÅò…ïG` –O2PU 1ßAñ¶RwXM›‰’WPw¹+(?»|+áÏ¥—Æ„Yº®ót÷‰ª¹’-+‘]@ã·˜}ꬔLìúá0ÿéÉé®OdMW+ï¡nÔ®n <ÉJ^=½Òtç‰åÎXò€ÙÖÍÚM)KXà˜üƼ’‚â’ºÓ$²-u1aN^þò4ƒ€À[ÀòóÃú Š[>ް#ƒ_éÝâñ(š¤´Hì €vèû ö–à äB'=\U)˜¼"*vÞ<§ àô[ëå4˜1egêKš{½n›·µçÒE~•sçäËIlݧ!V±“µyºÆ ¾¼øôº¬¹³â>êeÁȽ‹‚•iª`Y;ØÐpå †ù`ò¬ô˜ˆžƒ9‰nVÆ:¾…#É d¿w»ŽiÅ|ß·WËüæc„Å,õx)Œ}ñ´é^›ìÒÍšgèû«Çí)þÁZe€¬¸+÷PQ]ÍÝú“è2*׿‡°x ö宜ŽXoçùžóúˆúkßÜî{ 7Pé¦_ õedú7ûc/[l·ïeÉoÃ6Xû&ÃtXð (WxZ¯ˆo]ÊédRÜhþGF¬.çëA*g€ÓF}tÌŠú_ü£Žj endstream endobj 187 0 obj 3855 endobj 188 0 obj <>stream xœUV PSg¾!Ü˵¥Ô%KÄæ¦§ãƒVÅ*¶¢€¼ADyBÈÓ`xˆ"ü!€€‚²>Å .**Êy¨£.º«ƒÙÚ™þ—ýÓ™ý:vçÎdþÜóç|çœïœï„CÇÔY"K‘$IÃþß)–I’%ú·¬9‡]aÀ~ÍõBòù7ó òk½êÝÀ˜ Œ O¯XòÔNýÖ ó–\'­¤Î>^‘ž(ŠN­òñö[½v­Å§76oÞ,§´ˆ$û¥QrÑwø"‘Å+â$ò¤-"{|[&“†‹¢déŠèý¢°ˆI„þg¾a2I¬ÈQ*“*ñ)¢Uö«E–ë×oøXzJãÄÉûE»ÃäûEî"oIT²,,Qä’†ýŸ ˆåéòðñ IdbÔþè$irJlª,,-N, !ˆb'á@^ÄbáH8» gbáBø®„/áG¸{ ÂŽð'< {‚Cl#Ìq Cü¥Œ³„³‰Se`lpÐ`ŽkÉ•~ixÞ𿤙@öÏ©e”%6â¹= i:¶˜°G‘üé<¿†Ãæ³µ|tP·6ˆ]Kn †tgýlaÊìH ȹjE¡r›Yõ¡Sù§Á(èië˜` çMÌLØ^PËzEט²f¿âµC½®®Ã݃WIÞ04¨zm®m:œŸ‘§d¶£_±ñ k\X®>Ê“¾7~´uQøú yíåIQÒk1Å™œ…·_rÙnø¾ZƒŸ2P(­é—eçÅo°ØH|÷yJ¬û*š¢ÛB»ÒÀ30|îÚ­†æÊ¶Ê>ÚšŠÛ¼'ÄÐŽ!7¦j'[o2íwµÍ÷ýàR´÷a[Ã`H0Æ.ã°·pôºT†= ¯S:Fg’¾dm)͉f(vû¯w1¼'³p|–Ëþ•½ÈÿlÛ»=Ø-+q§l¡áÜ䥩>áµ­þ zS{¢Û¤­Á•»­ÚÊŸè´ñ:àªðg$Ž>QŽ€vÑ>¹]õ¨QË´õµ?ôŒO-ë1·Ö˜ŽãÒ¦Í çãøÖÔNÔû0¼Á ZŠ,QÃëBKmë¸VQYØ\]®©h¢yiJ[ò)…D¬˜?Ñéoå¥ôŒóÃ7#¤žæÛCµ/ûª{˜¿wŸø€‡ÓÒyDOd1’òŸSý°­ÅõJÂ(LƒÇ{ÜzÔöø…Ƭ;?…„¨Â…±©™*•‚æµW?'m(lŠõà?îôÛä³`¤“Œ+ í‚´Oú«6ãêjÛî„Îcpýçùb)û¡Š_XUÒ}â*hÌýø³;û ÷¹G" ØŸ3{.E^v*oh ÞO´@Óåèå 蟂o¼¨U—×0Õcü†O <ôhWà¶ì‚Ü|%c[pŸ:˜d “¼‰Ò —i««:€ Š|ͰYúñ,µl»ƒ•ÛÓ,ÍvPÉj.·ë;6ˆ|I™À—ØÝ,®¯:jõ;ð,Éëm€&£÷ A^õ 3Õ±Cê,° 'x8мڪgúÆAò1Èc×ê«Üú"—yŠ­ÃNüœÑš]‘\)à½:[&5ß ~–xøºH Öш3jû냻½Za^š­ðñùxSNá]L‘ÑIk£UÁ!pä ¶Žøÿ>3ZßÑ%Ôv7Þk„¼7¬›ïÊñà\ Üæ‘¾=>€‰ñô’ì´s˜v¦¿jàÜMæâþözFÏôÞÙ,ôõxõoœ-cMø'jÕ% œqî^ãà™!FEï÷û€çù{ÅDšYkŽæpIÍ{SœS”‘³¤+eJUbJ¨*]D£¶H]¢.e,©#I™@Io „4\ýn ~~/æüÆ:¡íi’7¾»B^Ó°Ôœ:[]U¦¹´Ðw[“"rñ„b>ê ›þŠ “ñ, uÄ-ÊL=4úô´I÷fpXÁØ¡¹?‰Ì)ÁÌÞëÖÛR¼Ü…·á·CÒk1Èy‡Æ3`IµÀ•¤5%C+I=ï£ÔO:*?» d ÚâþQÓRx¼BX9.Ë=‚s$,«,TŸP1Ó,—|…#Ç—Z=j8°;´¦>t\·0µÉ+ñYßócú{¦¼P6uä²µÔiI^-"’vX™ûD7_?©.9VḚ̂FظI·j! •À馸åôíú¾;B^h¦·^' 9ðò28€­§ÐQMkC®£&0öÇ 1i¢øP±òû,¤P'9‡ÎÁDèB"‚BuHNšÌG.úe{—ÁªE¿{uY¬ù-…vé,Òði=u#ÌQе:¥³"òŸwÀ@ýZH˜Ý0Éy8 §_sÙ=üœü<è„Ì’šã P]È À[E€RÁc×·ˆ±òBÜ4ásª¤©¸¢¢¢®îª¦ÐýבyúN8šž#N˲Ë å™aÙ2¿˜8_°‹Þr+ðõ…«Åå…çË4¥W.Ò¹FyFâ¦Äî²BPtVxL]„Б¯òØè»ÐÞ±õW[O·6W3õšæ“7Šß–ŸTwjèOe}køð ,%×Ph Ú³î! àg¨B5ä/´ƒû^£}ä[Êd~âãœ?±°]?IÃ|†·àdåÐýAó­ñâ¬ÕQ³ý±h«œî‚ëçääH{ô[fØ%…e…­ L2›RÏ$×+Jƒ€+ðNðòVöE4º'”ê¶“þ°3ÿ¼-Ø£8†.(ÁLðÒ˜xLUM—ËJj2¼®„†+æƒ`´¥ëúàh'¤À{Ú>ÿ.<83=Nx K•%‹¹›Ì^£þ8rïnò@Fìä§r§PíÌýꡦnæÒ£Þ ýX6ô"z‘ƒ–Ãv#S¾nŽÊò'Ù9Ê$é [«o>÷3Ôôg³ŸOŸ06&ˆÿ<¥¶B endstream endobj 189 0 obj 2280 endobj 190 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœ…WyXÇ·íº§eStf\@Áý©¨à.²É:Ê&²G¢q¡Ð¸ Q6Å¢@XTˆ[De‰&.Ïg4j4ADoOjÌ÷ªg¢yyÿ¼ïc˜šêªê{Ͻ÷œ["Jw%‰yGÅ¥E¥(#ÂíÝã"…©q¼¥ˆ6€®“ŠÿtQeÒéy‡^"d {l˜i ¯0† ƒ k0¥#eääÏNLÊLVÆÄ¦ÈÇ.]äocg7‰®®®ò•™ŸžÈ=¢Ö*cäÖd—˜•â&ŸMVÇÅ)#ä1q™I±kåá‘‘Q‘¶eáqQ«å eœ2))1M>v¶|Ò„ íÉ¿I~Êø•©kå‹ÃÖÊçÉûÿ5CQÔìy³2"|ݳ#—ûÍNŠšï±&zgrŒbmì"¯åbïÔ%>i«—¦Ç…/ˈ_éo+gï0~ÂÄIŽ“‚¦8»¸†ºQ”=5’ ¤æS”+å@¢‚¨”'5•O¦R jeE-¢¼¨‰”5µ˜ò¦ÆPK(Ê‘K-¥æP“)jåDÙRþÔ%¢ )#j5˜2¦L( eJq”52§†Pÿ¡†RÔLjåAFéS‚¨2ê™ÈY´FtjÀˆsä x§3Zg¾Î)]=ÝDÝz ½ƒ¾Äˆ™)ÌZ¦Wl!>&îg7°ïޏhàÕ¼ÞT½`½4½B½n}s}?ýý#  š - ý ·"ÃZë†?ö¥2‚Aë5 6ì4¸vðsc±ñ©I Tñ€À˜P,úlu KÏaÛñü60Æo#U4*²À§XÄï€1\3Cƒƒw«âi<•Áùãi°gZ€L¯c/b^A mÄ·âÄ>Õ`²KÊWsXW=ëóÃiìÀ@°ú #Þ.ˆŠ ܸ9ww¦t¤¸d{~Î!tÕï=²ÿ»ÂCee×!Y%"XWÂÛƒ~B±I;È!äæ’¦vr¢ºšüìKz®7Õw^=¹j®?!3‰Aß§‹Ý“æGH%M÷ÅÄ’ áw E§@®ÃÁsn qü [";ÙÀó°âÅ(˜ æÏW ÃL¦gbÒRÄz„]ï?¿¿ùP¥¬ðdEa-º„Ž%z°Fªñ¨DåA\sæYRAEcw[à-ضÐXÁ@2þ g©uhpg@{@Ž÷Р pÞÑî¼ ¶¼ƒ€wƒsõ làø´úتâqš›ª{ð¾‡Æ)ãÉS!` ÉÇŒM‚-”Â(l£Í%ýÍàó],l¬n¬>~ ]F·"/¹6ß:s¼]Cç’[WV…UN'ßgÀsàÌHø®ê„¥Ë¢2ìÌ`c]ðb$ý·kVùúE­r—a/ƈߎv;>TÝÓ°“ÁŸƒÆ4¿h|L ‡¡ƒÄ>¡|š„a/AÚø3vµÆ"쉽˜‚©àÒ"˜sÇ~ÀÓe¹˜æzÎ)FXû{ΜÐÝ×w¾ûžLsÐ@û~< „ÉÅ&'Èi…¨¯ãmÌ@N ‘ã|&¶mÅQoâ gƒcg÷ÒYUþ2I‹õMåMËtöèéVëñ†Üg£m‚¼<=ƒº~ÿ£¹»[ö9v<[ÞÊ8ø‚¥ñ4Ëð]lwiìÆÀV5™+[±¦„ ð3ž}fšÙª·0¸ž'ñvâÃç J~/˜ŠÉBÕø4nkû†Ú¸ºÅw&Öa±w%6ÅcðF¼$x Éf0ì·<¶f²,s KX¯—àN?þz2cË Ó÷eäE²˜µ’_a ©…ËŸPi‚@¡¼äsÈÌiæÔüæÄNÄ‚Ùo`.÷RïÅœ“IzÎÇxŸšcé…‚”óYÐKÔ¨Pxuöö6wXH‘|€‘/`Ð_i\n>Úö¢¶Íç6Ôļ™|EcýøqXûàY/G€=¾éz_!ÃR&+,29E Õ‡ÒOn(ÞzdÇyvç .ïáéºk¤ +³Š Ò÷¤íŠ&uS”¦ün‚O-ÌZÓB•ÉY!g¥¯÷Ïh+D^ƒEÕ£o(Ú½®z‡Þ¡‡ß¶w¶w|÷õ!`¢ßÎéžÛ5µr$bs±)ƒîÁ“°ÍTkl€ Ýz‰Qö?÷‚¡L[*üZ0õ¨]"/ê1ƒtPwy˕˿“´F ÷û®À+Š£X—xGžÛª{9²ÍR ôÅiØ O^< Ó²!ü3òüX‚¼a +«–Ÿô¶ÄfvØ»Î(Q(« èP^Gè܉o¯°˜T„XÒ¤Ê!kñLø½Fj³òüð„Мã‹WïDτʹɗs.­ÌñüoÊó sväKûÅkvÆæf Ö!$t‚lÎ ‡{joóÞÄš„†Í„‰»÷ˆ»û^Iƒæ3`ñäŒk—Ÿñ8’Þ"á9]²V.þ¹:væ¬èX7MMüÃ,¢Ÿ„ý™ÐÁÁ[AÀ•7cõ¢ õä[FHa(1v(È´œl.©þÌǵbI×úº‡÷çl/’ÂHñæÜ­hš‹‡Ç»³’ê÷„'<ë'p>×ÀI(6ÀPsÉ~9ÅE,y}=<ø¸¯%6µÃÆØ O{Šuah{MÑ• 2wñœÀÏe±¥u¥ØŠÉ /L¨Šoˆ½šõˆ¤¼óËÛ ‘‘s¾'gà=æî îcl¼Ý}‚;Þ¼=óc§L£&¤ˆµ~ÞüLü¼Eü¼Eæþ"þåiÉVÐLš‰…sÀ ®öò;ùÜ`»°É™áSX'æ@\Qš¬(ãPö…,pó"yqzcþÆT‹U‘ésböægJ×øêÀW'Èræk¬÷ß¾à‚î¢K…µ•µ•GΡVÔÛäQ‚=ωÌß´•²'+‹[¤’ÞôíÚÜ@ö³:uk–‘é :M,$ðZScûÉ#S ¥ù™_¯G±,~¤BÔ‘Ù]Wr0)ÐòÄÈ,kù J‹ˆŠ͇BGtËÆ\ÂCÔs0“¹q|ßWhSÎYNÊD‘×שE±,8cR ²¢Êm°zÛ~‡Ú‡•uWÐ/èrzklÕªSÁ…¾ˆU[jsl’.9ßGÜ€o;¼Ž•ô^——¬´@±›³Ö}±>}kú›ä%KŒY¦&õ`k.ÉPñÃ8ÜDðžÊl[¹)9u +i Y²f¡%6œüž<±çH.4®KøVV’y « Š…L&Q5£W؃%Âãq:^£±,sóôË2IÆa;úC*÷~!z¤Õ§rn:XA±R8táÿb¦4…ÿZqt×¾£Ò7âõ;6çlBltöfœ%mT(l+¾N2§CØ¡á]!íU¼.9„$‘€ñ)`@Ô@Ô£  Féðïø¯9Ô±®1¦6°mê Â]x¶µ+‰éKK`l¢ìmúÕ‰hÁP¿ÅѬ5Üß*%j‡'|˜~$¯€ÎSRÂâ…W±e¹ 3¥ò’¸Ô:ôû–Ó=¯Z#fí–jCÚNPÜ£•©zY¿†Ç~¬‘*;\Žë \èëÈÓÇbÆêÛ îh”DÓUeð5f|5ñã9᪹!á^óã+oHáâ£ëßôñí–cWk¥’Œ¹bmNþL°€/…—q|/JR> kÔ½B € ÂÙ.Ô™òï¾R[[$EÐg~|5Ç2X¤–á¼LÃBu5ýiL/]à‚G?Šôžâ‹?‡³ipaðrøæAµ lÊ÷kˆ°B°,—¼X-v ··©*峉rg‹‘ké–ÐÖ ž5OÑSÔSÞÚÜÒXz=dùlR"EZy•Ø Z1®j£©¼åÂÈIY10Ž £™'¼ñµ1ýÙamzk¬Õ’ =‘<Ðý˜Ã¼KqÉe[¢ã,´ŠaÀÌ‹Ø[ÏðÅÆÙR°dö^,;܅ضc™1™Ù_¬Û,ËØ„bãâMCä™þK- .ˆ:šDjcuÅÙŒfË«èûò–ó¬$%îÚTð ã<â9IÓÔˆ` GkëK‹/çK›óª÷îÙY¸oè?]ì3Ø%tóv vTŸÁSø3ÂrHgÿO¬Žrà%B§4;;‚³0\‚½ñQ¦1¿Â´çxš02úÓðs×~•ø;Sû1—´\ý_ûƒ›UµWÏžH “bµfæ_¿yqÿÜk˜ò Z»8\ºæüÒÞÈE¤/a%-âÿÛ&^ùÜ&^ù›‰ ´,;u6¹Ó¸70œo§Þ‰¹ ‹nõ9僼QP²r) ùbÐÃ縮Ï16ž }o4‚ü§‡U‚„à8 9ÚHšÊFM¾ÒêÒ7ó°R¸Y2 Ïß#õMÚPÕr™Ã-Â×qÞö—Ã:^vØ_nð~ ¢Ó©ÝXC;³b,ªJU™CþÒ‹E×ÁšÃÖO¶‹‘WRì‚/ÙÅâºÝÇ+©­ø¦Ýga8¡¡'–"8¯8|[ÂQM¥µV »m±µ¶øÖ—¨f#"U!^ù1Žî †ªâhM2ðH£.´Ö‘B®–«b¹hÎê°+—dL@x8ÂÆ‡&}»´Þ»+êêAíWkÛŠE0ÙO•߇w*ª]“ѺUèDú¡èoâ÷M')9m³Û¦Ä/ÂãS"P4J,ʪZró]ô=Ú÷`ÿ‰Â†“Gj íkR*öÅb\¶ðöl~=§^OZûåâTg÷0lDÚáÚ&ôp™ÀfÑ‚…«[žp¿le×”¿»ƒžUµ¡ß(öO± ã4Û~…6çn”‚EóSõ™ë×jÂ<¦'Çá XGŠ \£çmÁæ,Ÿ¦­GHz +HAÂ{r;¨wˆ‡n®ååùŠ•¼8»áäJ ßÐ?Ÿ˜šÛÛ¤x“‹G¾˜DøÁîõ/ }ÓæöLÒ{³îXÓ Ðs¼‹‡OõJô&ÆnBóØ/ÁLœ[³«hïႊʢ:Ä^¬T.ðXã"ûRKƒ0 XžÈ:ü ½š–s³xoìJ®Õjõ O$AšxGŒWËa_|áâß1„# ÓO˜´gdïzÕrN=ˆ™§–Ñ`Â<]YԀ؜\B<ý”—¶ ½úN<ø¥3H‰ãÞ½#ý²ÕÄ?°ùŠØ/ãVÊÊ`, gÄ+Þª”oÉÕL~hlë_N°õ-ÖU)A÷z0àAI=É¢JB %‚Ã7q[Îd—e–GŸŸsò?ÈÍŽ óWdMC˜CØèàŒªEu>w"“·øí=8À0ç—ØhadVp¨l×]„öœ,¸^[s5¡#™‡CX¼˜»Q0}Fðß¹¡­wï7¶Ýi³çªàöQÁãmDoÞ3aÁS0—è8:Ù!Û í@¤Ü&œ3 BÙ<3âÒa¼‚ƒ­:á­V ûQ‰?{´þø‘|(%éÀ¾ˆCòMÒ¢·ç͸#öØÏJ^Ÿ8v¨áŒèØ=ÀæxˆÝT¬ëu<ìºR*és U.u¶À}`cû^€Åce›k£[@*'y}­!jÉÒÐH?ßÐï¾ok¨i—íÀ§8I_WCð,¯Ð/¯°¦î[g:×s† E$ûÃPˆ%}D ¸r»°èW70BÏQWMý­êöÒäV„ú²®n ¹çY;…TäH[+<–pÈ0©:µ¿°TV·/¯¬œÅ–QÞÓ•Çê·KŸñ ¹Îúe.ÓCýçÎ8ÿàaí…kÂ[ñÂ.pê× þµ éżžÀ,´ áôÿKN¼žî'þI)ã«ñ*c@OFèƒ^žŒØo`HQÿMÎGÀ endstream endobj 193 0 obj 4470 endobj 194 0 obj <>stream xœY\×ÖŸuÙÙ‰bcŠè Q b‰= 6°`G,X‚QAQ"¬°4ÙFÚîÂR„ (Šaˆh,!1ÆÞK|)>¿¨ÉKÎä»ä½ïÎ.²›üÞû¾ïçÏÝ™;÷ÞsÎÿÿ?ç\$„S7B"‘P3ã”Û£#·‹¿G ža@7a t' „ßGÉã·õÌw–pÎRÎÙ©vÀ›Þ.BL_¸ÒT}©D’\P93.^µ=zÓæD¯¡¡KWú>Â~gŒŸŸŸ×zÕë'^³"¢7móz ÿع-q²×L<:&&zƒ×¦Uü毈#7Н­ˆˆ‰Üê·ÃkèL_¯±£G‰?ÆGÇ®W&x-ŠÛçµÐkiä&eLÄö?Ý$"da jÛ†U‹fÄm\<3>rñ¬÷£Bfoß´$(aóÒ9‰ÑË”ËçíØš±bArìú•Æ¡¾Ã†{xsäÛ£Fyk¬)}Ú¸ñJÂôk'½g˜œ:%m*AŒ$Y„ñ61˜!fþÄ(›XB£‰!ÄRb1†x‹XFÌ%Æ>Ärb1ŽJ„ó‰ñÄ b1ŒXI,$Þ!V‹ˆÄDb±š&f“ŠxƒèNô œ Ñ“èFô"z}ˆ¾„ !'D4N°D?‚&\‰$b ¡"¦DH Ö‰µC,Áa%œðf®H¦I.tóïö­t­ô®“«S¾L*‹”=&—“ ä?åkå©`ªñ·ß¨ê>¸û3œ¥ÎZçõÌïùC¯ ^BïÕ½zÿÒgqŸû}ýûîîûµK”ËGŠŠ_ú™úý‹ÞBÑ_¹ú¸Z\Ÿº~ïF¹-t‹uËw;åîí¾ÙÃÙCåñaÏþÑýÿð õl@ ð9àØÀ!߸r`é@ÄÌe¢™ýÂó^ÂsŽ”¼ü[ ¾Y  }—¬6Ë+´F‹BI•F—žQ¢«fïu(§‹Wéâ•0Ö’UúÒŒt:…AãQ½Ü:øðp’—˜`=L†(©!ôä#sxF““±¢\kLf£H´Z°ÈÐ:R¥Åë˜ÄÇÎ$šÓ¡”Å*5~£T¼5œ¬.Áohð–¦çÀGÅL&Т%â¨j¶µ‡“ɶY-,¸ÙÍyˆ/5ï2SŠ{ö×—6y~ÀYt{’÷í(ßÎÅRˆ‹–Û­ïFVÙ6‘ÂFƒ‘|0ûÒ¸E›’.að&Ryȃ·ð.žðnŠ&a‹àLGYQÌQ¨ÿðÈõûq¸]½Zs¤•ÝoÙUÍ•S%ê67O£eV…-H˜‰Gv›~÷ ½wº=¾±¢ˆU\O)0è*<ËŒ%VÑô\q½ZW²“Q4¥é4*ö5j\šaš“`þ?7Åðxæ@ŽŠâvU©±¼RmH½Û…£$‰Ö Ñ°Fv\ôny…Õ»ÝD sP Aœ,…Tü`Ž›=8‚þ°üÐÙ{äÀCÄ®èc›Bv¡û/ôÆàþ¦Ò0~••t(ðèkŸ6ZPÖÙ75”D£ÐK4^ʆŠhLÏãQסŒ¶CA|»Ôúv2((M$ŒC¿ÊlðÀÁ‰ÄÜê#R"Ÿ—ât¨ª/“ßݹþä‹u§'6°—ö5qg©ÇŸ cÙ¾Øy’G‘4DÂ$ùí áK¿÷‹|P ©à+oãu‡’›vìÞÄm¢-[=Ë Abµ¹;^òœ0“F.C|PoÔ÷ÕPÀ`}õô‚~>ÿ…ú±ëþñÈðЉþÞÞ¾zùäáϬõýí<¼âÁ©¦¶ÆåT Í —›"CP /é‘v¿GjòÝ[KOÇê1 ¯À*ZQïçoA{—Ü8Å(ÒÇ>“wyV‘±œDqðŠ~ùÀßô™ìïë3¯úàáOLW¤x˜yµ¹ fIá.i˜µ²F²ªÔTV®6aõ´;¦™DCQ I²Db‰‚•lñh$ÌEµ2ý¦Üë"{¬Ùò3×y©ë*XåvLúŠì'³°w:6è’/{“L±m¡Š=/ÁFUæ. l áRG¾Ì#/ù O¿](¢×œX¼w7—[ü~xXÄÚ¸Ù\05IŽ<¡7ê ®´í=u†Ù··¬Š+¥Ì“Z›•«Õ3¡ V½„ÜÝçôdoÊ¡ÇßžƒôéÃÁ…L~ªA[ÎQÆRŒ~'y•®S‘m’oóð@Ô(l €â9xc‰!š bP4¡*_a‰,Õâ B±w°Dj°DúB!yjÞDZ÷9 úüø=ô`¡ÇÈïPŸ€UñA+ú˜~u/ÐwÔäß¡Sî>{öåŸlè< ã%Â.£¹“¹5i‡6?žÜ:ÐÏgê…\óÏïÀùtc©¦T£ÏÎÍÒ±¶ÎP.âÁM¾–þ+Å;Y~¸yñ9÷œ»²k4ÕåÄV^oÅômaÅ@nÈùþ‚¸Áx@ÐèMŒm ¾G$òFG 9’!r$ÈÁ ¼¾Ç_kÏ|˜ßÚ¸éÌ8\_â)±ËaÐ[à„\ç.¾CI J,?Ï5¥Ù̯߻˜[AÍ7tÄ_Ý߯Ãïüz‘EÏ…÷ V<ü<,´aš'ê=|4êzü}ôzp¡þêI•§ØR–5Šg[D’YÅóBÕ9ëN˜$ÑœxL)k†úLÅqxpçû:;I ›V„“„9d²°6òì‰#WkùìÜ}Ìi=ÇpT^¦DoÊ(`c AÆH޳`Å 6x¼¼+}‹Ú}tï+ÌËÎt†B:óáR m.}„×õƒT¦@¶ßùálj7±è ƒ°u(µ0ì² 1$¤:A!,’?¸ºlNÐÌ¥ãm­‡Å_‹Šê".àÝòŸÔì¦8‹—ÁjèNªLšŠ2SI5nòjmÉÎt­FÅ„#oˆ”+Zá¤/™bT—‹Da€í”ÿ‡(Ò…POVëK2ÒôxÅY /ä¯=ƒWŸ‰™tb0“%’g§•JwQûpë•J×°˜Ì™¢lÁû¤¢é%t;±ç„çn®&so¢QW˜k½^Z†½žRÁ¾_«,ÝÆ­âBâÞžDý>Ÿñð7,Â=Eø,‚o1|®®YÙ0Ã9‰($ÿq88Ý:×põ‹hùœå¡sôiG‘œlåŽViª?TÞÀñždèy¹âY¬0šO?»ƒa4=àí‘Fwîü(zÉù´fá!ïbåž›".á 2E¿ÍÁD 'ànà†ÉøšŽ^OP_ ·3cæë²²õœÎo¡´¨0?¿ˆù°ì s=WÇíÊÙ¯Æeú 9g—g˧ 5eêÒÌœìÜœvkRŠn—Ái J(E²96±p›ç.-/FOÚéÿ›Ò9×»ú5KáQ^n‡GFF<à _yŠA]Qa°a#ô?aã/(µåJ¬<£ ¯Uc“NÀ‘Sn ³0K éÍ á•K9Jä;#lþÖêmT씺;º º}º}éûÒª¶s‰”¼bþÆñïÌn:É加2m).³æ‹ÎNÂ×ÉFm£È+7š,¬YÞu&å*ÖcÙ“ ÷'5Æìf·ÖFšÇ•è ”åx®å™UUý[.ºsï£Ø÷ŠÅ Ô£¶Ì³ÜXŠ‘m¾!W Â…\£0ïÔàlëLõVŸÕ‰´Åi~„Ýèó–µ6Ú‹(w{¹&Ò\þñ•ýU8fn7®_ë^ׯárDz­Ëká8«<š{iTpDbð*[ÚÇ)U’5\E4^äÞæãËÛÑê¢]¶bI?x3îèÂãß:ušûœúzÒM\+åÿGI™f5€„"'H‘ò[×BÏ÷_åÛ™ªòpLLÎBf)NX‘t¥¸×Ê2k„Wˆr¿3Íj‹†‘Ëßߤ]‰Ó˜÷¸Ç0€½-ç.øüxó±¦ãU—¸vîÖúâUTŠƒÌPZÍ|'·d›ÒÒô8§ËÃ.¯žÇÍçBÃ#V­‰ äfRr4ö×!0©í‹êÆK…ü/Ø £Z¤°+ ô[ô v¾ƒv“ma?| 䱃¥Z³:+'7[ǬO˜²Œ£‚"·²PqöÂëµr½€n8„ã{[®qŽêŽÚÑx‡Ü÷O›²[SÅ2\xLïÔiUìR9FcSGZÑxQ^­3e0ð=ŒŽã]¾çUÇáÒq7Å a‰PA+Ì|Dxuˆç )sÆGTolLdë•GwÞØy3£F_—v ¥j+·™ œú6Æüh.°5çjÞ.œƒ8 “ë¦LžlÒ–Uà嘃r…9Œ¿œpÝœ¿¹þ¼5ñxä^6ÆgšV¾¨4Á_•P•VÏÕSŸ·}ô -|q‘û]¹g¹ÁTÍþ,{U«7_W¸í¶<²¿%â•›"QXàzÏÞf)’£g,î^¯ '/[;×sÕþ °whçñÓ)çV~uõ“ç/0mLn?ìò»¼X*øÍ§–d–U°=@v¬A¤\eÔà”„Eé6LîPú÷…ª½V±…ÏB+ÂT¬R1LoÙ+R,^JÄÚË;¸eg+¾/NT ÀKê!hX/…§®à/8Ëštm¸ ×HÜj´Ë’Hä í²Ïíý«ã ž„)β×0±mè Ñ@vStÀL˜KŸ<†»ºÙ²xRñßöý¹Ùö§ÆV†“h¬Ãý™›ì©è°¯åj_k7‰‘Lè1Âî©s–öÏlª,èyÉK[QŸýÕh=Ò¨Ô’E«´ú4ì®]8eGŽ={ÜØ OøîΟJ•²–zxrúfÈ@ÚÞNŒƒý ü¤Â"×ÇØ‹Õ^ìfßÒY2Ý &Ù*r2̈́۲þý@ ù;²•| æïÙæQ!Ï*ͶÀ¿ykov»wXþÞ¾Æù,ÜyŒÅ˶ 4E˜èúw¨§-‡òæœÞŽšŸ®ZÎdféõ\&¥6iÌEbÚ~°ç€ñ(GÝ>»å]v–<¤2ưk`ß !ãYř韭üáæå}Û˜Ns®O‚Â[¯Üüð·ã§õê£VìÖ ¹¶¡@X'…׋$D Yƒµm«ÔÚ‚’¤ë¬¬(´C)Ë!Q° ”Ý·vã•ƒÊ :µ5rÕlù§~#Š™ã}úáíg1ÇßFïy|kçÏ22–Gµk†íð®û.rå"äPwû¡Ã F2(¸ë€‚iÿ¿äòû.­°MÂé Ñ;"ôŸÂ#ØFÃD+3’Šû]‡Hb×–S$ˆ\Qp•´R¬Âê©=²FÏ7-ÄôÑŠ§öÜþ‹9$¦Î0…†Éæ;€»}„ÆñÐêV3LkÞð$øC¬fpR´W÷¨½ëŒ£Êzeî¤i4F‹‚¿xìPÝÏÆ†è ,º~Ç¡Ë?%>?v¹~†Ì‘†-‘¬Â€n9ø Ž—×œY²~ËŽÕåé­{ÖqÜ6Utõ×ÃèÍ´¸áê}’­&é:z –P»<{J*Zgú$ú–'ô|þúBŸÏQïÀå[g­f!èSúÅ}ÿÁ¾“ý† òèåË/þÔ Ì7kžð’Jƒ“Bo×6(Á¤ÞXî>Å ­q8¢bGË7äÒŽx$Èamè°Lˆ—=vÐdo{§X#º]xˆIü6b6*ÌËÎËá²=RÊwî*/1XJÕ"Ûaw›Tƒ©9.%~ê¶½ÁùpÞ‡_CN{"KÔ%U^Rb.Ê+È-fgCߨ—ÜoÜoõ¿Ü€^Åù\eÖ”d2Ö¹>•Bž+S­ÉÌær ³Ø¨ïA±ÇöŠ„zådq¹\6•Y¢1—û«˜› ùºs/¸Ÿ·þc8%S—¨91&Võ9$̧‘LJúüœülÎC­Öffš4¦<¶(<"ü“^mµ —ûfSq‘ÙÄ<é#$-É(Ì5ræR“ÙmÌ1°þ¸©öh©,68³Þ­:G——›Å u2`ä\¦ë4ÉVR_g<±œfÏhÌ#«m'*ÍCÓä]g®0 ͽŠr±Ñëe=âÆòàMÏÂŽÏÁ[ÌòHo,ª1‹Ž¯ŸöBß ŸÐ»ä»Sª”‰))Êl.+_ÇT£/ó“qEÇyTpS~ ‡FÐÛ•qq{”Ù ²~ïîvDz»rñN» Þ1ÄÎqq‘Z œ†7è±q‘›êc?`KÉc Ž‹9)>vüÞ“.4rÅT©Ú¤ÖèrÒõ úê…Ùº¼,ìYu©¦´´¸¸Ü(Z•Ö,™µ`RáÆMI‘t:"Ð8Æ—÷RÞÆ2è›g4w¥úÚÁ“{W·p-\£jwtýZËn5VÎ-Hž»V©¸¥&¡qÛIÕ5î e ÆEé]A QpHjv–ŽSãͨM…¦B®ˆ÷YG9bΫo950]yh2*Þå_ & t}`µ„Œo©Ý_}ª¢ÕãýîBR–º.) I!È}K«ºŽÛC}x¬éÃ=mr-ÓP…chàLC¥êl}nŽžÑè+v9¹ËBço+Î<È%­\‰«mÅ™±7ƒžœlª=~Il¢7nUnæ¨à•­W/ÿà\YçñÂ/.Â">ªÙMQ n<](¿xú³KíßRŠuXÏ{Öü¤ÿÓ †-ZÅ$'dÄr* ÷Ôl0V1•Ÿ\9~£î~±8(<9nä(e¢Yˆ\" ¼GZl§ ÉŒ¢x³x”±‚}”…ùE\¡GEzYÚNµ>UÃàL •Á3× Ìï|–Q–š¡Ñ§jÄàgâË ˜ É4¿5'9W—Å¥SXj*Ï?y™@9×´·îhAeA‰‘+ç*4†´üME uÜQ ?»üÞÉÐ4vgº1Û’ÃÊÞó>M!üÎzsÒáí[2wpëŰ u¸ëxÒ² ² s8=§ÏÎÁ^vÏÍÉÃrÂéŠõ¦,Ðjw$ wµÙYYœÖƒËÉÏ)ÀÿÜ ô˜ê†r~Q!e…+4ó.‚Ÿl=:AðÎÇöåãöøëOök½ØÿÛé_ø¬\»IJMWqj ‹ÙTXXVÂÔ7ž±\à¨û—ì‹Û¡bw¨SóÞ娔ÌNr)®?{ÏþÇ„w¡ßûŸÅÞ˜‰$Jîp—­‘Ã^žFi$Ö˜_e{I›­šaj-‡ß»jq°‚±¶€ž¬±Í–Ä¢£¸‰ËÀÂ[\\h(cZj››9êÉ¥EcÍá»$tïñ0VoÌ*ÀFdj4ꌊ´C;س[ΦžÃFôøúoàN‡¼¥Ý±Š}]‚°Ø!¥Uä°—gàìb¬ÄÙehe?¸ÎþÓ£ “±ÒÄVD¡>üïý­¶1ýú™C…:ðh¿!ÙúÓ‹/~’¾xBÇ+•qñ»•öíÙ½ÿ@âîxÂ\㕉ñøÞþý{ñ½ñÞZ×øDe|W'ÞSâ{ë’]õí¡Iô0{Úæ÷¬]×Ï;}[ƒÛ6#UÄbª>zÀUZqÿVS}K[ÿ§þ÷†÷Ÿ>6øPÄ(FñtFôÆ•óú{2å矟|ùüêÆs33° ¿÷ÉòYóƒWtõFÛÇwYÔä¤xúøÊÒ€À¹Á“ý´Ý½såâ#ë&ûž•Bý‡ÓêßÄMÀS o‚’ ZäQ.’W“•¢fÐN‚w ìÄIgöŸ“NIQM‰-éÌ€}8l‹ŠªßfK›šbZicMs{ö*²‹¸ ªLcJ[±6„¹µ=aSvjžVËíäÒMú]¹Gsë¶sQ"CZÖ~\)–!ºâäfkaâ~®Qä=ÇgÖnÙ{¸Î\Ãñ"#oA1-L@鏏…œ‡3 ÝqÍ_ˆ‰kÈ6¨‹Ño°ÚðGiqQ1VZ®0¯0ÿwÏ1èŠôÆRnV޵@8Ü,¶'·ikúuî r•Aƒ“0nmÅãùW-Ápz4UTˆå|G.§ôéé&­…BÐ`‡Äb/D{%ÕjpSYSC~Øïñ¡³3ïÜ“ þÙ;â endstream endobj 195 0 obj 6346 endobj 196 0 obj <>stream xœQMkSAI¢Õ ®lñ.,~tQ¤íJ*!´ÁPÄ“÷îK.Lf†™ ö nÝ8‚6*´BBwÜ—”B‡RTŸA®-ÈÅR­2ò¤•Kà¥Î`J‘„Ç)š hA;"çb ä``…ò˜×@*•㬲ù\+Æêˆ"¥ºÚy—Z2¢c÷ÕëE~(|åë( óX™ét<ÂÈÿƒyAÊÇc_ùô2rFŠ"úF)ciÞÂØ‘,Ý[`q l&ÑÍu«­,烦ÆÈbÎÕóª¿þäÊ<éy¡²¨‹#:è,—ÿGaŒÕàcœyVç¼±òøòÛj|áCørÊ/?‡ÉƒÍݽ'›ÓÝÙl:½˜íý~þhõjûôfy«¼ÍØ5|Êã endstream endobj 197 0 obj 429 endobj 198 0 obj <>stream xœ“LgÇïh¹žˆlk×LÛ›‰ A4[BF†† §Î„E# Ðj[ €ý¡å®”QAWZZJå,ˆÆéœ¸lQ2gf&›™ûÇhœ†í!˲°.[Þ#¯ìJA‡ºdÿ\.ïÝó~Þçûy^’P&$I.+²´4±Æ¦œ·,&C|a½”NJIÒ*à*éÁ¬:yñ桱eª€Te,#ù_Bü ¨íEBA’m¾¾"‹ÕÞÄÖÖÙ˜µ»K+Öeg¯²’—ŸŸÏTÛ¾0[Íl­™É”_Z&‹u¿Ñl{ƒ)’ÿ6™Ø¦Öd·Ö53U£!^U^e260Ŭ‰µZ-­ÌÚ¢u̦Üܼù±i'»¿º¥™Ùa1[˜íLüø‹V‚Xb6ÊG`My›â]¢˜x›(!vÛˆ„šÐ)rÿ„’%—’#Iŧø]¹Sù4&MƒˆzÄzAý›ˆÄW4—P…T¢Í¡<àƒvŸ¹Ëa+4ÄÓ˜óªšÀãã;]<ï¸z!>Øê)©îî=  žÈ¡ ç÷t[ö¢m”æ‡/ÇÎF/§ß8mÛ£ÇÅ*C‹½¡>渤°PD!JŸm9nСm*?ðYÍ™úáêp%”ëÚ»–ë''§DRC[´UH‡“fð:œ‰•kðrœÈl´ñÖÃãÂ%Ýp__?ôéÖ˜•wûºÜºÍMìƒÍP~Úö Ý3©|5þÉm¸ ×ëÊâ;ã*ñYA}NDgä$&¤ Ò­fúœaotw:^¹& ¯ÀËf¡•W?||Y_¦ªx¿¾T‡Iª:óÞùPœ½®0ðôùNÎ…‚Èïÿ­G³ª!è{Üf<wÖLø9¾ÛšŽiJ3õ¯iï}»kKÑö]……;Æïܾ1~W¿ èŽHÊ‚êEª˜Ujåÿ›_,‚ v}#@<*ðROk sïMýÏÑd‰kz‡úúZ,4Á`gô`ôàQ7°4.ŒkŸ£q¼s¾Cy¿ðbíÿê‹óó íÛ)që÷¯VT¶U×èæì¡EQrõhJ‹J¨„žªmOŒ .™¤`ä°ßà#Ž€Ì—‡w{8ôæ:Õ¢Ès˜ˆüò)Ð7¯Þc-Y¹úÊêN\€õ´ÆºZÚ3©‚a_ s~&hÍ8í]µtZ› •H-5“2³ô×ÞÔÔ™ÔeñS×ÿ endstream endobj 199 0 obj 1162 endobj 200 0 obj <>stream xœuX \MÙþßÇi?P¡cÅ9)Cè¡„B¥wš^Šè%©TT¨¨(ï1ÃbT_JŸ’PT/Ê’Rý(=ª?5€2¢XJF¥Ps(5Š¥Ì)ž2¦²©)” 5ˆÊ¥\©ÁÔ"Ê2¥¦Rf”;5„Ц†R1”œŠ%ñ'‹È‰9{¨G’‘’8É®^#{ôÚÞëT!õ—–ëõÕ[ ·Uï%mE¯¢/Ð0#˜$FÃÎc7°¿rö\Iï½{§õÖô±ì3§Ïî>gúÜéóK_‹¾óúôýVßK?I¯þ÷6ë j žš®0ÜoxÐð¦áë~’~ãúyõû®ßõþfýÝú«LðÙåÁ(×è®ÌF'«(8làŽ>çõøu|Þx¤±³ñLã¥Æ‡ÿi2ÔÄÞ$Ž õH¥a‹%ÔRpפñ¶Ò„Ö2†;T‘Jal±DH‡EüC¼ˆ~Çàš4ë3¸ðCý Ó ‹høJÉã<ôá)m(|ÓUšdÎ ¡„ÇŸh­±©`M;0à­-]è´r~N~2¼ åoÉÝÊMe÷®Ýûù~tÙ¹OÙ¾¢ý{Î@¾ÆphV‰0½%¹Ø¨¹Õõ |Ùn";ÝL”}§-ygXYË?®4?»{&>@ŽÿÙ.˜³þWF¸D¤yÄÈe§;YbFV«`Ý*9Ö!Âþó‹«¿Ë>’TpÄqx˜ÖÃØ¥Û,`@ÛCX ˜ÈäOœç…8»¡?˜\R>o>7Ï£@¡s„Æ‹ìÈLèÃÃ&K‚+q0TÒ˜f`¼KÛ‡ÒŽ†«…¯ÒÀ>è™ù°SØÑ)’!NZ š4̳8ùC?œªéGcþCZ§ Äk§‚­*ÈSµ©¡M¤6‘ mÆ bšÐÅ}•g8Ùû“ÅuWMQg|½íIN&Ü=vâÒSTŸ]•x<ñDÄo²ÅFïñ~l:²þ`NYö×i(%ä§.ÉÎÈI_?‹ òÃÏxØ ÙìyttõþL²òþ¬Ý “LQòª…K³–f¦­žƒ8âKÔ"¶H„`­™œgð]­>=UpmÁJî$îõ=?ÙõR8(œäñH ì…½:-`Œì~Sašã;X¼\ˆã»¯NÆÄŸ ¦Ogô Áðæãçò? Uj©`•<‚uÁL \ –eถôUªYCÍüžá?t òN©Æ”˜1N«f´5=BŒX'#h;é÷$Ðó³TÂ+•¤Z-ÕŒŸŽvJìˆF¡)©3ƒ§y&Y"ÜaýòáM^W¦ß_üzýÓ~˜ÉaÖE¬IÉKO Lq!»ûÄ8ð‡àN`aØõ˹‹Ž*e¥îšÉéÜ#ŒV’¤¸$&ÅÅÙw—p:ÿʘs3k2f] âœð½°Â+ta`ɉ6WVÖËøoLÂpß IcÇMý ßµG:Ï·‚_#8¶JN?“ °’G>«Î«Ly6¹n1k„ I™©xêóa0ôÛ)!)“åä‡ÂÑœ‹Îe]tS·¥‘ßñêúí'ˆSÝö¿mÜ´‘,H%ÜRI%q¢v¿³`Ëhýµ·hg¡N¥õg?áþ¾'V¨…" Áš\'b l‹sp+Ìïl;T{]Ñv¯ h}9X…`ƒÈõÀáW¬°—¹Øb;KBG~à÷Ž\lb(uÏïR CÔÒ.c8Î@Ð ‡lX†õ@Žø8£þ`Æ ·`* Ãp€Û(<\ño~ÿˆÉVaq»ì¼f¹»7+k½œrÔÇ Ëí1MXÅõ™È[jË®V*ð1ÐLæ»"°þŸp4¸©úQaÇÇù6‚Y‹¤³] »Hv9ª˜ÅßTìømÚ'obs¶®Þ¼q®1±SÞ¾ÍÚèv!ºƒí¡æÓ*Ø©’"òðA=ÜWsê'k*ÛwpûµÍÜS6ËúÍ«ÑL4oa” '+I¸¸§7ÑÈ"G:à4Yå±0–,ãÆÊ^ÝŠ›}ÄÏ ¶Çv_ê}6BQ{eÉet]8\u—[Ì"¯µs—¥/M›;%¢”‚¬orŠÖ}½á7ÙiÕýÝ;xü̩ꢻúqdq²0Î ä»ëݰ 0ÓmÜØ:ž¹¢ìRüÒ4K6ñ6­M?€Lð`¶x>yý z÷oì9q]þÞ‘Æq°— {87{ÁºX4Û ÿ kv`÷ŽÐ×&Ãcf÷%pço>†7*ü†øØ\¥Íó§TUuÀZ¢Vþ‚!¿é­ï5l@²ÎpJÀhç3 Ð?M¡Ì¹–_º %ŽŒ\à1'±pÿ2ùʢϊ6”s㘭ؠq: %é?èIÃeìY˃ŠÉû|÷¤ïG僫Ïk¼w2}ÆùGU½nÅ’æv"ªÒf’ŽÒÚ‰ ŠrzèÐÚ¼bùåh>×#«—?qŸ“õi¤Žö0"\ÕÉH­Ü=镉ì=šõL{y¼½B&lòGs¾Š-L t)“|`}ÙÆW›@?çä”ãD^T§¾»Øn FSš°¥wé€i®G$h)Q©£ëJ³.+\€b¹ñ± Vr]˜J R‡TFçÔÂJ‘M„Àóøª3dd3„ãÇØàÁî~‡ªæÊ£êÒ;P7¾¿B°é£P›bùKf Xa½§x2r@žY¡Ñ½£0° ‡³`( ¿´•·}Oä£t4} ýXV‹0ºQò”dä‚Ðñ0Ü ÷òý^k΄ŸLk(.Þ²í°¼™]ýÅÊM+—´¦ \økÄ·ÙÅX,¹GæŽ&s´%íŒ&Mo¢Å±œ"˜{£4ªîœ¦†Ÿ;}Õ"¨…Ýü&°Õާh;Z9ãAUa?e(`Z-ëú1óÌüPS”°,>uizvÌê4ÍÜ—|fñ·«Nl>O¿Ù¯0öH|­ï“$¢ô}qÕÉšÊc è6ê»kuZ3HÖ:îÀÂÃWL4Ôü \C¸õfyO ,«‹%í/„>„l« 2ñSµ&Í7jûB#)_ÀS1w³Z"Ä}5´ÃVB}9Â2c¡Ä ײ²óæaÞŽÓâNÝ– ¬“ÖŠulûµµæPS\–ã!:§?Ð Ñy¤™@M,^±³–/ŸýB›—˽؂Û6"îEMÅ÷  qzNíŒ.i4ŒJ_“”ù‡ÆŒ‡u*m“3¬ÿ`¦ft¬)ªòëR'ðµ!–ŠI¤ó¾*Œ!F(¡ml¥uÁæ‚ iB€¶„þ×Ñœk$@ÃĪBÄþ\æñ#ðÆåô[†Ô?íF›38gÐ:]×È‹%OÕ:/Ո굓åŒ{ÏŒ-<˜,O:¼¼]æ„¶¤[ýfîà½^À¹°³'aD³„‹Æ`«3ËRk=œÔß0#µÖæ¤aÇ€5±îóF°ûMkGëp$î¯ñ‰¬Éó&~H#è!qhönƶé€B]¾­! ´qíçhƒYúŠ¢2ÜdÕ¾µ˜w Xš,Ï\¼&mã,î ³ãNÅ%âžY¡XÊ¢äeyÓÖá>y¹Ÿ/\”‘|9YŽõÝàßîÖ—^º.ß^–y íE…[í ¢¾,qè‘d|¼¦‚š%PÐ,ŽA1ã!ÞÇãxo _Í@nÅ\'o<²¢«XÒñ^ÚñŽ·¼‘Ë"Ÿ•ÙÑÉF›Oœ¦}öOJ€þ$gt§…çÌ~œIW2û!“Ƨô&Z> X_åÐÀ㵞°–ô€Æ»XR§¾§–Öi"x,eaS2ë˜Ò eïÿgø¨w’ŽwR#ÆX¾3»BÁ£Ä,ÈÕò®L2žMãÌI˜M»1Iâ/PL*ö 1ãû†IÄIôL%$ÑÐÿßoz7Z0ÇÀƒþ­Ñ–qðåuã0ß’òàìÍl²Ç\²?¼æÃú2k4kt'¯A©”œí†òn)&™†‚ÓçGÆfYë„nÝ#,k­"P>È—þ}G>¿6ä°â\ô^V!Yc3Ê6úg‡ðª—/ºJACNþ°»S »…-¼vK§&b:›Š%ÙyxÈr.@¬BuñG³QÝ“/TàÞVöÈD6_Xüæ¤UQÙÉx0Lôîúñ³è>÷£oƒµ\–níæ3.z_â-…ì,îï™1Ö뫦@¿šªÂÒ“ò’ÂoŠ*Ê9Yú-$ 6„1åWKñéW|PóÚ½èWwüx]myR䜌tR— ÆU¼ìlHØÜ¬Yfž!ך¯;õüšn™V‰àÜ-¾ìൽ™×¾-ï¸øúÒ _.]l!ry=ór\E\Ŭoü=ò[˜œ‘¸:v£'×É|Y½ýÈîÒÒ³Êê÷èF¨[Ø‚¨€$…Ý,l5q®ß:ŒjH°‘¤x®¬Žõ™ëîyî^c͹ \«'{Ûy#Ìibp˜ýøà«íí7®èþw5I¸F4I{ÛS¸M J5 "D=²Ñö§›0€z0Äõ¤‰í„þaª„[­’Šn1*õ0ß‹¹|€"Gáï+Nß>ßTÜ€GÐ7÷‡¸Û W‰ÿ-™‰xP·- ¼WsàòÅ&ì6f Ds+ó9(,àU·|íƒÇO¿þ²ûFÃcÑFìU+b#RÀG¯t5ø;h"ÞïEü¾ ÇÏB‰^»3Hç ’áÿÏw=‚.8‰ÓßKa!”8Ñ7ý/yïÀÉ šº ´Jái ãå¤À„%ms„¬â|Øë«ö¥ ;Žt9Šj’_hÈc"ó‹êÈAŠEíKjÂÊn(9-Òè4¶îë¢y‘È’ç!’òq<ÝÆjåà$Èa¼Ø{ ¡Päå×7¤¯Éÿp¿ñ»;+ò–´ù×f ©X~ãµ9ÞBó‰^ÏÕ0«T(¥Œ²O{_å6}ý§;õ (êÿúr Á endstream endobj 201 0 obj 5719 endobj 18 0 obj <> endobj 30 0 obj <> endobj 89 0 obj <> endobj 9 0 obj <> endobj 101 0 obj <> endobj 42 0 obj <> endobj 202 0 obj <> endobj 168 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 29 0 obj <> endobj 88 0 obj <> endobj 8 0 obj <> endobj 100 0 obj <> endobj 41 0 obj <> endobj 167 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 203 0000000000 65535 f 0000149248 00000 n 0000184344 00000 n 0000148939 00000 n 0000143529 00000 n 0000000015 00000 n 0000003165 00000 n 0000149296 00000 n 0000182143 00000 n 0000178783 00000 n 0000183680 00000 n 0000180662 00000 n 0000149337 00000 n 0000149367 00000 n 0000143689 00000 n 0000003185 00000 n 0000007530 00000 n 0000181244 00000 n 0000177777 00000 n 0000149408 00000 n 0000149438 00000 n 0000143851 00000 n 0000007551 00000 n 0000008487 00000 n 0000149490 00000 n 0000149520 00000 n 0000144013 00000 n 0000008507 00000 n 0000013578 00000 n 0000181614 00000 n 0000178226 00000 n 0000149572 00000 n 0000149602 00000 n 0000144175 00000 n 0000013599 00000 n 0000017784 00000 n 0000149654 00000 n 0000149684 00000 n 0000144337 00000 n 0000017805 00000 n 0000021882 00000 n 0000183196 00000 n 0000179806 00000 n 0000149725 00000 n 0000149755 00000 n 0000144499 00000 n 0000021903 00000 n 0000027024 00000 n 0000149829 00000 n 0000149859 00000 n 0000144661 00000 n 0000027045 00000 n 0000031879 00000 n 0000149911 00000 n 0000149941 00000 n 0000144823 00000 n 0000031900 00000 n 0000036870 00000 n 0000149993 00000 n 0000150023 00000 n 0000144985 00000 n 0000036891 00000 n 0000042323 00000 n 0000150086 00000 n 0000150116 00000 n 0000145147 00000 n 0000042344 00000 n 0000047353 00000 n 0000150179 00000 n 0000150209 00000 n 0000145309 00000 n 0000047374 00000 n 0000051891 00000 n 0000150272 00000 n 0000150302 00000 n 0000145471 00000 n 0000051912 00000 n 0000057553 00000 n 0000150365 00000 n 0000150395 00000 n 0000145633 00000 n 0000057574 00000 n 0000061556 00000 n 0000150447 00000 n 0000150477 00000 n 0000145795 00000 n 0000061577 00000 n 0000066304 00000 n 0000181903 00000 n 0000178623 00000 n 0000150540 00000 n 0000150570 00000 n 0000145957 00000 n 0000066325 00000 n 0000070742 00000 n 0000150633 00000 n 0000150663 00000 n 0000146119 00000 n 0000070763 00000 n 0000075761 00000 n 0000182594 00000 n 0000179257 00000 n 0000150715 00000 n 0000150746 00000 n 0000146283 00000 n 0000075782 00000 n 0000079051 00000 n 0000150812 00000 n 0000150843 00000 n 0000146449 00000 n 0000079073 00000 n 0000083083 00000 n 0000150896 00000 n 0000150927 00000 n 0000146615 00000 n 0000083105 00000 n 0000087196 00000 n 0000151004 00000 n 0000151035 00000 n 0000146781 00000 n 0000087218 00000 n 0000091535 00000 n 0000151088 00000 n 0000151119 00000 n 0000146947 00000 n 0000091557 00000 n 0000094998 00000 n 0000151185 00000 n 0000151216 00000 n 0000147113 00000 n 0000095020 00000 n 0000100032 00000 n 0000151258 00000 n 0000151289 00000 n 0000147279 00000 n 0000100054 00000 n 0000104927 00000 n 0000151355 00000 n 0000151386 00000 n 0000147445 00000 n 0000104949 00000 n 0000109880 00000 n 0000151450 00000 n 0000151481 00000 n 0000147611 00000 n 0000109902 00000 n 0000114937 00000 n 0000151547 00000 n 0000151578 00000 n 0000147777 00000 n 0000114959 00000 n 0000119160 00000 n 0000151644 00000 n 0000151675 00000 n 0000147943 00000 n 0000119182 00000 n 0000122875 00000 n 0000151739 00000 n 0000151770 00000 n 0000148109 00000 n 0000122897 00000 n 0000127274 00000 n 0000151834 00000 n 0000151865 00000 n 0000148275 00000 n 0000127296 00000 n 0000131205 00000 n 0000183413 00000 n 0000180348 00000 n 0000151931 00000 n 0000151962 00000 n 0000148441 00000 n 0000131227 00000 n 0000135938 00000 n 0000152041 00000 n 0000152072 00000 n 0000148607 00000 n 0000135960 00000 n 0000139790 00000 n 0000152147 00000 n 0000152178 00000 n 0000148773 00000 n 0000139812 00000 n 0000143507 00000 n 0000152244 00000 n 0000152275 00000 n 0000152352 00000 n 0000156295 00000 n 0000156317 00000 n 0000158685 00000 n 0000158707 00000 n 0000159081 00000 n 0000159102 00000 n 0000163660 00000 n 0000163682 00000 n 0000170116 00000 n 0000170138 00000 n 0000170655 00000 n 0000170676 00000 n 0000171926 00000 n 0000171948 00000 n 0000177755 00000 n 0000180258 00000 n trailer << /Size 203 /Root 1 0 R /Info 2 0 R /ID [(f~šyÝB±œäÐi°*H)(f~šyÝB±œäÐi°*H)] >> startxref 184541 %%EOF simh-3.8.1/DOCS/i1401_doc.pdf0000644000175000017500000017172111143604372013461 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[“·~ß_q*/žIù #Í=o\6˜Ù]œ¸p`;ÀbØuâüúHš‘úÓ̧3³8U1.Jh¤VKýõMêóË®,”Þ•öoœ¿?ºsÒí.?¹îÝÉéñéòè—£¾¨ì®Ûçïw÷ÎÌÄa§Ë¢¬wgoŽÊbzUöî»Ú©Þt´»®T…ùüþèEö(×E©û6»—ï«bÐu[gßå{3LUºj²ÿ®ò½*†Z«>«¥YJ„I§¹*ÊvÐ:û)ïMW7tÙ{Û7è¶ÍnÜ:U3T}öÎOy)d®ó½6ìÖM]ÁÐO®mš]%+=Ù¯•}Îë¢l:mHMK˜ýz¢o¦þãì/Gm[ôuÛìöºrGva΄l*Ûçæ{U÷uö@Vz-ÏǵÌ{áZãAM¼ñ>Ìy¹s¢Ôn°ò’+;=I®mÌ¿‡f×¶uÑ[Ñ™9g?)U4zwöØþó+'z?_9?>:ûã‹ì¾eI÷]§²§ù¾- se“=³[j‡ºÎ~È÷CQ–ºÔÙ‰ }”—EÕ´mYeeÖ72àÌl¥-:ÕÚà cŸŒúº ‹uŽº÷eìñÄb‡a6ÓÖa›aÓvo@ø­!Ü)]wF0¾Œ½1#•é®'ÿý‘A§ª¡†Ö•hà’ý+Š®¯ºÆàwß™®®ÑÙ‡0ùÒµ*]F뜛¡F_e¥ù[®ºB8 ›–oo(ûuÜ[i^ Û°ÈpEÇÃŽ¡×”×—Òû›c9 ´>[Ú@ðZšWó«¶i‘EØdÄ]pj1Ñ•C„ÅïL³l»®7P1`Tµe´žP£umLCmTÆi²åλ›Ùž‘Cc(=,NékYö÷JŒ‚³8ø á@AÆ“ÜúDTÃFaöBÈTòç²Óé}oôËØ˜®Q¸³€BX ze“WH ýÓ(Yóÿ$½Á~´@“¥ªZ«Þë¶ÚkÅÆ1W»¸ôñ³˜PÄ‹5¼}DŒ„æ+® ¾å4à­Œyd«à^Øwåzë¶ÏùÍÊ5•ÒÎ×xˆiPb3ðÕ' Ö]mZ aX€â>8¬€ê«…õ+ü>9œv訜 WF¿ËÊ«ð+z²ÀáuÎô­µ( `0w°Ùù?Íwã;øÄ"b|¬xCÙÄue¶ü[®‡B«ªIÙþÀCdûÙÞ~ÌfVëÇ\:`œ¢ðd¥Iº-•E’§ðÅH Í“É,‘ÙÓ¹A’Ad±$ÈS³ÄBdæ)-DIØB ‰€)oB«`„ÚjÔy÷×O5pM˜™Ò`2à w„ÀÄkÙÑRŸ4žÖ%ÿ’h47Z\ºGE,rãÊ 03ד ƒyI7tIWûšÒM…$ÜûŽQDS¨!Dh·©kv‰Ç -i•I®)‹S6ĶjÙǃSðòüè•Ézi“´Ñ#X•(õD'áˆL"×õSü0>×i 2I£×7Ô!M!{©ÈIÅÁÌpÿ tÅÌB1PvùM–Da^!Œ£}“+m=u‡ÆiÜW©ÛdxÇzpV! §-|ç§Ï2‚ Tê³`ýÕàf­Aw­2ßGºªÉ!ÁÁ¹åºœ‡æ²:TnÚ‹ËK—é¼NøØÔC3§¢Ržt±O«r«é÷Ȇ{KZ¿Tø†hA3ñ&Âd°átFÅë‹J{½;§N637F‹‡H‹øëÕ!«9¾i1;ˆÞäL‹6¾}Ìèó{˜Ã;I5;©UC³~9‘ »äJʉ'q#…¡q ÀI$V8 ÷EïD%yœÀ_OzãLzGíñ†¨nLòزõÂŒû·12÷ñÅ8«*}¥€š*Ö@ÙÂq=h<E Î÷µiZ³t–wÆt¶]ŸýÍt6Eß–uv7WÍùÂ+2D䱕à¨äâ„–O|/#©¾ì:[в\uê†è]@î¶îÚ{!MÄ$ˆnsℱ.êÁ¨•9ˆ¦+†Jõf¦ÒæxzEŽÜ^-ÁNžG•øù†hår_¸ºÆ'BhÞ [x"@ó‡0* º¢éÂÍ!3œXDßW Ñ0ò[V¦“<óž8Cû"òü»ˆ>B‰a“‘§2ò”¶ (ã_Ýݘ=ºEa¯ø<_<†%@lP+<—^u|ÆžÂCÙHæ^ÌsŠ‘eÙÒ ­|,?|¨„n qÒ\€ÄŽ}Šû¡Ê5bÚ0â†cPjPR±Vþó—)Ô\àn(5$)ÝÀ;¶cf ï ²¾‘Þ»a(”ÏòýÞÜ‚¤!`<~×w­Wø2…gØ—ù“™žN-©©.°ªK¥ACì°eU– ü`!žš§ã¼gàY@õóWÞü= lÀ^œƒ9f¬ßuÈ6é~ÑÉ;·¦‚(?!õ„V=‚<––ÄöÂÙŸ¬«s,"±º9B +:<¡V›[®]NÌóFT†³çÚ¾¸³C¡ÞNÀ(&/ñïˆaÅH–€ëþŒ¹m8Ão'xÙÂd¹è ñ–2úDûÜ~`‚; ß ØiÇ"<±EÆn4†6Ì!ž>ò§!£’ù ¯5‚•ß-=@sKA.„B(c9UWEq FüÈê ‰èÇ3ó¶ê¥!)G¾ œ7åüE¾BWïj)—žR¥o³ î,ì¥t¡Ú ìåAWqÍ!Àéóe×}üÈP¢FŽ[dàåQçñsŸ¥Òà±ÔI¸Kæ–f5D~š7†~ã2°ë¹8§ØÅ/À˜ç“¥ñ-cP?%k<ÿ ±1‹\bíÕÒixg<{òù1JëVlx /0Àûý¾ ~£N ¥‰>¦‹WOáVÀ›GiÐvPIF´àûP†> stream xœí›KsÛ6Çïú<’3Õ‹7®iÚNúLlõ”ä*®Ó‡í8®:Óo_e´ ]×2)YÖe røñ¿x]@Yˆú×ˋٓWœßÌšââ䛵ññ|v=ó ê¿¦€ÚË‹âé":"‚-¿Î„à…k£baMü?˜Â¡o¸˜•Eµø½õ‰u'ð‰ƒ ´®^•XÍ ‡AGç9¢Õ1VyZÍ5H/µ+«ŒóñúE5÷`…s¦\e¯?Óõ·•† caùW5—`”Q¶¼Êw~¬$(„ªkê®]ÍcGYgŒ%Uå gÙÿ&¶Ï"H«JÈ—ißµi¼é—¾Y|;“R€¨Þâ]4טC67:úàÌzä C°Þ:t4꩘ˆœ6¥j¹¦ÐEÖý4WÁ _ÌQo;K²p~¾>Á•O3§ˆÈÔˆ*$ºÆë„î:ÑYäÂ>ý9&Ÿ%··ù^Â÷Ç÷>´¥DP.û `t œ½ÏI&•oŽ|>R{\>M×[‚Ö‚A·´ÝcnèÙ°O7‚•Š·øòËJ€•Д/Zú¢·åÏñÀÚ48¦@±Ã3Ç| j1¾Í¬>tÔæ¦`nGíôôØ,f°¬7±ìˆ¨a>åØY2IR‚åžÒM¦H·JVé¶0—F:*o³}Çzd/V¯3.ê“Êöµ$&oUŠtÙú'Ëe­Äƒ±:vÓ{Úcé ¿.›°.½˜”Ðå³lždó‹ìEðn놩س¼ÛieÀ)+L¹ˆ!¦ Þ˜ò»THª}]ÕOÝ·¸‹I›!:ާ€î±ÑÁPÄlR¤ƒÚNqÚn"ŠÅÁ÷¹4¯`^&§3Ö‰Ìü êN’íŽ,LHÌ5 êRÒ¦ͤtÒ×35åõõ¬”Öê²ØÕ0ÔD†ª‰Œ±)˜»‹SŸãûn޼1ºX ›T¼êÉé/2ç%×¹jTV$ý%{]qiç6‚% JÈü‰ÌTȦÐäú%»}ÑKá;œ^æý RëªæèæÌíjNaüß+µOæ¡äÉx ýû_䌪1óR0â1²OSrj–œä8Í{vm®]ÐóZnˉÚ 9M[!¼¬O˜ ܲ'òHÌ‘§S{Ø\ò“YmÜcóQ½F=m>]3çj4›Æ³Çêv9²ªäõ!9JsÅÊÊ¿“e1MÅl–¸|¨šÓA‰ÿ»|¹oä|й$Uòx´æÈébx*䔟’ó6ß^Ý}JÙ~{ùÕbö2þþÚ£/oendstream endobj 16 0 obj 1216 endobj 20 0 obj <> stream xœíZKsÛ6¾ëWðV²SÒˆ{«ÓǤÓÄUN™NF#ÉÚ²dKv’ßEb—äÊ’m9>´Öå3 `ß>€xå™Qî ÏÇ6:]ªÇÑño5¸9\\Vø¿êÅãYt8„ŽBDeVšhx2ȳ²t¹]*"£áÿRGVä™…³ÁÇx˜¤ÂdVŸ%ðXHeãóD*J/“Ôfy.Œ‰£DÈLI'âY’J•å¶°ñÔ?´®(éÃyè&Iµï.ËxT=,dnã+„„·ÉX<-´1yAÛÎŽACè[©¡,£›j*ãâç§F‡'¯Îpø)BÒö5°&üe|˜¤%LQæ2>‚¹)¥¶­Æ¢"—&VU·B»"Îq\ÁЍ™—@íÚ’ÌN J×exOH^¡Š„­Æ CYQò÷ðwp2ºå:eÇsŒƒ!åÚsȔ֦„þ©–y¦…‹ÿJÒžÊÚ)È•`Š5N øUš;!Jç@óu“ÂÐ=u¼@+e]W”&o4öBIÿîP¬Ÿ:á2gcqÙyu†š~Õ&lÔt"ö>À2ª1ÌÃi²Å F¶œïFAË7žÀIœ„‹»Ú\ëç4C× ÆAèÅ6þŸ(™d`’Ȭ[|5ο8:­Úë g¦gUˆ¬¼aUñßsGù;ÆÅ=ih\§9­öìè{ÌG=¿YØ“äG„à¾%?2ŸQÇ… ç¯OXŠ›ãþÿnÇá¢y/Â[”„oj'ÖÒOUÖ®¨7N5µ­›ÓOØ`ÓR"fëR½Ù÷+W>H¹ëRê¹7½6“ÖNu ìp»[(m÷Üýh6ÙYm‰ÿÉî…\?5Ýúq=÷¢_õÖá#‹à8…ñÖÔ‘w9 '0Ú~ß »€Î3í¢€®4cz,k$MÕ9—¢ÞìõäaïÛ€.»RDéO œ"\6P"+ÒVZ&¼òó*ÈxЇ–ÖBú[I'@àëÐë0 #†1B­s„a„ðÂw?°m?#'ñWÿìõ¦|Å*Ðý`€ôm}ÜP1`„lN"$-ÐnY)羸ò¤^Ó 0PÉ„@*2ž¦5†w"©€¶"øñ=åé„ÑmÕ¥ºS+v®njÕZŸpðOo”¶%¯i±F%_BÌY¹+Vg(†ÒÞ×Ûô^4§Eô ž¢ˆ¥ŸܤuAàV?ßñC•ªèï}E¢• ÜIÙ?ÿ~´v)3cÊÆYV8h[C冪eOZ ØúΩ26³¹ŒÒúl Î›ÍMGÏa=¹‹€HË-‡ x¾€rRJ¼(•ßå~ðßHç”nGÞÆ¤±ùÒñ!YcÓ‘ ™ˆ "·›½—Î?çcœ3OÃ{#?©J%>ñù‡Ä0¦—pª™¤¬ô/u JÕ=eãæM@ô`?kå’æ¡?%µ^´ y¼üŸï‡òÍ_Þqß¶ñaÏÃ龆gþ-2Ï3²>É„ü†Ë'¿Æ“nÈ鯝ò<|’o-ã÷d…ÎaÓ§H> stream xœí\msÛ¸þî_¡o;'šAì´I.™ö.É5Mœ¶3wý XŠÆ¶œ;M~}RÀ.À‡"¥œ-'½$“A`ì˃]x·“,r’Ù¿®p|~pø¬œœ¼?hª'Ïþ².¼;9x{P¥¹ýÓTðòñùäþ‘é(ĤNk=9zu¥u]eeKULD&'¥ÈÒÒ|8úý­­Š¾_DÓû@C±E\Q«ã¨ÕkuE­.à°g~Ø%oê9xBµl„Ó¦VêL¬9vóŒòTΓ™TiVæ%_kK^!ö.¡N°vžR«9ì0ïç?ÁÔzÕR·£YžÊË`:¨z)+Në"ZÊi‡Ln=‡fu÷- )#€œ5¸¤â’XÀè^%Èph6Œì„¯É7eØ_òqûê` VœFWP½ðôØJ6a™ÒÕz¢¹.ô6PöÏd–çiYUµ™Œ5£LTXÎK8AÖ`Õ³Gkñá™Em 7À¯›¢‹Mm6×Ia7fO`SŸûÝœ­‘˜z¹'¯Hêp‰¸A ¯‘êGÖÄðÈ-'ˆ'Ø1 ãdµ“1d^÷lãØ1`m[}’*Øñû¤Ïfƒ8Êf순ƒ¾ŸÍføÞhp*«J=¼ÅŠqï[ƒ^À)u[A ÂÃÄXf—\”^>ŸY§²%0Îc¸çMh§y˜…'h[‹}Ó>&ø…}Ljžy&C7Ê“¾$ИA? +9¶äEŒ@Ûy‡Tü[2Ó©VE¹ÑIŒ7.f¬±n§c¶™V,¬ºa7Ïdu|v^~lÉ™Ôñ¨klX&1<åû½‹Ó··¹®ÍÖVfE±ÍÖ6¸ª†Ê ®ªùk&ÖŒ[ATý¥>ÿÿ3*vVƒ˜à«´ˆ/ý&îà=pžiÐQµ¤×D…0±jˬY®í6WLfí–¸80Žþc‘½ª²™ÅhÓOKçÙ+ïŽ6ž‡,+Kìð™Ìý¹–ÌÌdªr’Y¢?O??-Ê\Y]—îùÒƒDgYK‚Ùõ(­‰®ã$´6,p+40E™²Ç]ïle±<#!âxt­×ÈáMƒC¢×Ùy—–aÑb‘Cà0Òã'p Õîµw(ó½Fô¬÷­>|3Ûº'¦bÛ3èÀ…·:#¨ë«ÉgºÏLt~µƒ¡\JmVìØN7DÖSfÆ]¥5=h-EÔùôŸÚ…ñWŒ<µdׇŒÈ˜‰î7¢(rV”Åí­È2•ù0Á)ïµêêXY5,)Óº.ו§J ƒP²]Yióì ñé«T8Rå…/uØ‹#¥Ø†ð™{¡ømÑN€…à쀻ÛÄzJV‚wZr·ÂU†.«;õÿ”˜’PYáp"ÚXñ§Öpt]ñ]hÄ!'â&óvBð˜û$±þšQx!Ã_En¡¤áx»íš¸[{!.„ðm¸T¤š­ȶWn¥N0ÛJ¬ÇÇŠ]-²µ?Ô¿ †(˲`²eù}–…ܾ~³‚hÃL:Ý0è%ÍN(ëÒñZþúiA ÿ.õ#©n Lè Û!7©tÌVyê—‹«:ˆÑÏø~¯ädHˆó!ƒÅ[>ŠÂþ›®¢ÚGÖZʬÞÛ¬œ2s<$ëjwûÀ™Yêc9ëidÒÄzjú܇_}éÈ—&¾ô½/=õ¥ Ý¿|éy'ÄkCÖe=\\’{AÅ9_RñŒŠ¬Ûv[Pñ6`£Cº 8Ä%ßQqEÅØ€ |‹¯áÌNàÞÃé,á‚^Ã9œ5*¥¤N+™S¼à¿¿‚dT¼‚ öݤÓ_7šÕ_YÑ7¤¢?Sˆaµ•Ê`PUö(ëòõ=äÐozË¥¿–ùÓ_}éq‡òé2ì4ÓCH—Iê#áÀo¡qÎ!Ý_VÝíÀÓhXQ{A•aÛªŒWºüV•Ήà>(Ý(`m1Îb€?…ÄV°éÀØöÎ!€‚›vLöƒ êºùÆtõ–€ç‰/= d¨3ÞöÉð¦ð†ùjL×CÝÅB^Á¶Øù¼žÅ(4¸aÝ@Öß§/›•ä¦l~OúÂu qÂCÐî8¬ö¿P‹ñh Ø`›@‡qƒØ‹•wûúƒ=Z ëê~¼ƒoLYoÝ;xàKÿØ?ò0Ø¿‚Ý>@î]¶Ÿ¨È‚EÌÿkX‹•än¸y¾"4Ö¿ý ÉW €·}˜©|éQ  ÑÄ{Èt ”ç°-ößANcëd<ý<¤‚µŠŠ€(nÃ]¬nOòK¢Ú,‰[1 áK²G(¬Ëÿu*Êý IGÿ&P:zƒt‚,?Ñäî™/¿ 3ù~€)+Ý”¦&ó±É¿Èëîuï(íft!‚l"–`ÃÒL>G—8Sªýq^Î`9´ƒ< –Ó]¶©}ÇÑe–ãòÌ—ð¶Ov‹nõìšaÖ^_ëeKÒj3kŠ´ÈYFtt]W·HPŽ ~ !J&êæwã qÌ® ïqùÌÝ‘aŽÕÚ¬}F¼…yCøV¾Û„¹0R°2$ÃÜP§5RrIù,½Nz`s5;T0es‡sw™hæô†çêÍQ Þ›¡l<†EaúîrPn¶Ã*øÞ]Ç7=‚´cpÏ‚)H_Φõ¼8‹u”Ý4†oì(ÿ>ûì½ne?xi…]¿Å·³¾6H#Š´Î5å𠤎ÎÛ•d%ØuŸ|Ê÷£|j6'Ö”-5…zo“v^«ÀYð@œ¹:_ ¬Ç›Gl#´­{6uH¥}ÁCôZKTZÑmJht«k.}x= ’#ûUvÝ6ºÌc¡JQ.rnHÔ¼HöNOøà@{×Ä{+›r‡)•?Ò†+„ñÍ?œ#<˜iŒßë`^v4°}æ#â¸uŸZ P•t7P\äÐuH›Dr]Ë¢ä|ìó8c Þ+ÝÝd6\Š»ñ[‡²º2óލüäKÏ<ülstqö>=Éeâ,c@Æ2ƒÍhðu%ëzö?®Ô…9ìÿ0äé`8X_ÈuZò"]ݲvË6’6ÞÞaäÿÐ"€ùG¾KK—LhÍ#PwÒYt"ÙõýÉÊCÖì ŽuÊQ¥ ¢À¶Vó(Õf¼68Bq¦†"ö~Ø•/½ó¥<žºßYŸrg³¹rQn®\tFê÷¾¤s…˜¤ ;F°nÓ9Ó¹iøòòåþû ,ò3àô…/-‘Ι?óÏ›ÆUjO <'ðAö ¬ÅgŒ3ª_”nU$m.è"æfø“þìÅþ¨ªsÛ‚oÂøî~üHÜW†. ÁÇòà#/lŽñ¦‘µ3n<×½§};nÐðãü2…mü$CÇ™²Óý%/ÏÐÃTÈ“eérhÈÖ×ɲ´È ·?ê%©ˆw Œ°¢Ø­›$` ºù¶yÏh¬›oË(¨Ý&Yì6I=<ÉN7³Ã–[¬MÀù®­éÖÞ>ñÚ??tä²pO~èÚà€{ ®é;u:kB&œ“ž"gös`Ä”þK¸§ù¸~ð@ŸÿÀÓGÆ0ª¼†oVõ-|T(d= GJÇOŒ›Jm"‹ÎÊx °UŠÂ‡+¬8ï-+Çd{e7~²‘3šh°(¥Sݼk,þnþþ†µhóendstream endobj 28 0 obj 3201 endobj 34 0 obj <> stream xœí\Iw7¾ëWðržÙn Ñ@·çå`ÉK<Î$ŽL'“çä@S²¬‰$ÊZìx~ý½  Ý_ $-yÑsœC ,lµ¡ªPè·£4r”º-°8Þº»kFç[Uóh÷qœl½Ý*’ÌýW5pxq<ÚžÙŽBŒÊ¤Ô£Ùë­4)Ë"5õ¨b¤sûw™ŒHcŽ·^Žw&S™ÈÂ1~6É’T—J_Pãh’&Y®ušÏ&Ó‚¸{<ƒ#œCpëv7÷¶cSü1&ø; cRIÒ2)ÊÆjÖº5mۜҩښ2Žß¿i‘ºÿɃLn•¾£æÛ7Í“ío<‰ò¤Ö–Þ1Ô×5~è¡ÝTó iŒ1näOa’ hÐsHlµßAÁn{pÜ×°b}ïôŠJôSmûë¡Z‡Xϱî„Ò…KrCÊ0]Àþ FÀ–â=¤ Ó1漃Ýï’c@óY¶o/Y¶Y n!ºìÜ^ºìº¬*.n/Y|Œ¸<¼½ty¸±}t{Éñè#´çñí%ËãUµ‡œ–_<ôâéÂöú–@_c§d÷ŠÓ§p0ìLGÝ¥=ˆ°€SD‚ŠˆD¾ð”û©g·ˆ«äb<¸„;ýÆ™A¥øÁ”T=Ôþz¬y7ÅB>¬Þ_=å;ÿÑ“égývVè®êýí¤í€½!Êþê¡ët Ø’ÞAZ0 á2ÊšÏnÈ5wb¢Z·ƒ›¨u¢"bð½<ד§p„uÈE_z^u»ÔÄé/|-• oœzÕ ®R…jVØUý%3 ª„™û¡N`éB{—Ÿ¥ T~‚kPQ-È| bÂã²Ú›nKXðaå&5²˜_ÈñŠå+MAŽU!M¼ „Õ7,a9®¥ •‚"¢¦k ÈêG8ªª1T–æv“U…L&ûS[!Ã&c…7Ãؼ—p2†»ÇÉ¢tESWŸ, nÃï œOЫì邸´‡ÖTàô€^À‚#Ì0\ÝtäpÔNU[‹\ ›•y5òæþwS4“V•XCåWµÍaU6RdÖÇ­ô°Óç!3n$ušºíT…1Z–)ubí„Á|ìNiÛœõ[Qâ1¹7±(KËFÖ¬«m´“õê¼Ðñ PîÓPxuG†ùÌSf>ö"˜ó“[ëTe|ˆyàÌz Á *"¾D†©ñ÷j㵂cœa˜Cªb~ß gW¿Þû2¹Ñ–q®À H}~dã;p ¼ ]À•±Á¾‡­~°•u2zb}2y°ÿxõM@4¡­Yu$«™|ñå(è—Ú•Tõ&x´QFkDÐÍÁàõâ„&w —¾±Ð99ɬ³f´w^¯KüpÚNxcMþй†Ê¿+D?)à~þ›’ ‡7N‘+Žy麛Â:Ь‘¹¥ðõ s:`„õ†ûñ¾•ÅøøQH/òïfê5(]Œñ;ŽðYÏ}D“ÚÁ&Ò4F^¯I iAßš¨'(^Œæ1Ã(´ñëëüBš¨Bµ‚ ^s4Ý×륮–¾1XUKþ˜«óË5’ÑÏxgà ›Žµ.:i&ü¢&Jìj˜e÷Q˜)˜;˜ I¾ä,™š0+ƒòiÖ!’™UÓÒtaýgZi¿òµ“ÇM©¿vbo†½ÙÀ²•"^66AñœÈœR!ƒ©>Àƒ÷u¦S—Åà«H&îáÂQ4!†—òæjŠGCsör.x½×O^½âù¦¾~”+3QÛ}¿h¹m ToS•ÈáM1饷'µ¼È2Q©÷(†4&)KSg r‡§™ñyÙè ãœ;Èèì!óÓ¨LV­TFdœ@”N'SÓ>ö,Ù¹»¢c­ÀÖ:±Ÿ¾sr,”Jû§‚JXøÛW8Í 8®óëÞðcçMj á í‰E«øÆùÞÚòä‰,xAÊöÜ1±}mÇ~kLá}úã"½Ä9ñ@X{ÙÝ<ÐGRçý>j‘ ¬ps3ŠlæAWµë;ß8G2ÁÆwOÝ(mÞyÞLZ\ ðǤ˜bµ¤/Y’VNØû8vG¶Džë)ð?;Ü} ]ö<Ü+¶}ÂyàÁšµ6ôêly*QG¤qcÑÃvÆÀJ³ï@Iì³qÿ›öOÐí®îÞÄßÈÕÎfu9ú9ÒÇ,SÑÝ0 áQyê¯`-rÓJQ™ÔOÛ5ˆÊdi“K3â<q1%Žäשs½™KôBýú#™±º+ü=Û;Îô¢\]POÛÁ¦Mf§óöºU9ÿFùZoÃ:Qüˆ|Ìï§G x¸Äé£Ä±“ßñ²)“á 7^[ò™Å«ÂCw¹…J¬ 'ÁÕåpÆdÿe"°3‹l=ÿÕMÎ&ë¥DîîféHX'Aѧn*”êFè"1²þÔœLÓD m¬2%“©Jò2µÎ¿œX÷¤Î?M³Â’¦*ô˜ŠÁ)ƒùh#mg"]¤R:ñög¡YꮽDUJ¢y¯]ßkŸõâ#ì¹8'Íd`œÙV§Ö7¹k¡ÒdÒEDö÷2+Œ².}¯-Xÿ7U«ªN~·˜¢(ËÊ=²#˜4Õ9ÛÏÑjïP/6êS¯J¥¦Ô —.${„™_ÖS0©uÉŒ¥›•¤žß<yVOo”õRø„ÀmyþÛ6ZH榓ÕiýQå‡bᤄY¥Ê…ĉ3æMÞ¥Xæ±O$áÜNík;e`êØupƒDÅ 9Ö˜5ku>•:²FSBÑTδÔ«Yˆ,§@1‰çQPž…­ö”S…†{1âÙ`f ¾õ$h†¾œÅ¥¶ÍDFe7šœÇhŠÓ<ªÛ¡åƒ ,eaܱٶº°N9KaÏÆ:èª2hAþÖǸCÛÛZnxŠ VöˆÒ±0îR†RðlÈ7¨w‡öx´ö§lˆP*g 3Ú'RªhBkè³d×Ñ`ãQkÅââˆBõ,ó4œÕéX¶Fp"ŒkGø‹Ôo¢¹ôQ.¡«Èýj¯ý[é|îøeÒRðSê©oìÈôà·Øº – F3à;–7!KsÉÅ`ÇßX,8I9€€“°èd¸ ¶â4:þÄ6¨ìko¸rÈhXZÚ»ÖhlRµ·>ðµÈ«X'¥u¢e`œz›3ld›àw¹N¼›oq];Ï凎þlâb’;qÌs¯[æù»¦l£¦CFçá_¹NûSt‰+¼çù94Lø”ò’°cë¯ÅÊyÕ>Œ^Åœµ6ŠÍÀô“9©EL­‡NšÈG-/á °ž-b㪶Û“u¤0°H™#õûïœrè2ô ýU±×‡Bfÿ÷U Ï}:žŠ Ø+—Íœ—Ö*¬î¼¬pžßˆâX‡eìëylTÙOÀWúD >G†î:‡‡óT†î‰7r¿ØÍ¾’ó’Õ\g2ºÀŸ‡m•£Kî‰]vz0ƒ<¿^ÛøÀ×0˜”!@·bl£h÷½„n¿Öt9{©•;V¢â€Ëeð" î°1¡  —\I=mM [ZóuL)‹¤”‚œÛÐIZ”CÑYhz¨‘õ‰}iºoþÂ…ŽïÚÅ”o|8ÛúÅþû?Z 6endstream endobj 35 0 obj 3400 endobj 39 0 obj <> stream xœí\ËvÛ6Ýë+¸«Ô1øNWi“´nÓºµÕUN²%ÛIdÉ‘å<úõøÀ ÀKQ´­4nÓn&$0æqgùøBzþ¿&N/RïüzP<öŽ~¬ˆõùàÝ óCý_ñ€Ó§—Þ÷5Q/÷óÄ›œ ?ϳ -¹ /‰Õ¿óØKEà§jÀå`è&oÔš9ê]æM^&ß¾RoÇa …EÃcEúqFÃ熚Ê3Ô†zf¨ßÁ¸ï õ§¡hCýÖ=nP2Ѭ“ õE”òm3ò¤+òpxCä5‘¯‰\9‡céyJä‘Óš”Ã5xjMÛÀ…×pµk8vC«y£¿&?„ðåçÐè Cêp¦gOZ.õ(¤6PÙ¢ÇtÖÑ>eò^Bqv(¯Ù~•g¹³!”&ÕËo´^£Z­Jº"ñS‘Hµw%;!•Ì å)*Œ“$•Ôo ¥ö’úA ’Dù@¤†y¤„ÔäÃj’ó=Õÿ.×e* R Ö³dΟ6Ä`'°Š•¬Ô¹Ã8+d¥ÈTDÖ¨òq$;dk±}2†lìŒ0ä­ÈrCFzl1¿ Ã$IÉ!cKYÖ_§¤Š©yÏ7C“Ø{bJÓñ©·hã†F]‘®èé ²Ù8l,%—ˆ’ŒoÆÉ4+ƒÇ¬P 1T›Ì¯Xl9Ôd”A÷däQbX¸–Ír©JDJMêyi,¸A,‰dõ‰£ÇItž8öE–¸çÑqÉ „˜ˆ|Fä‘>¾VDCQæ¹Lvãº8;g…2´ÌeÊU豋ñb½³ôqm±XçÐŽ-jÝ0ιª˜Q®ø$c ³*{OÀ¬ø5šµì—(}2 Äp¬x©©Ì†M ˆ|TáKœT ’ñ»‡tÔ‡L3[O2MU˜jCË8÷¥ôÆ:iëø $¯òŠ4ÕÁvÁŸZ]aj–>^ ²á[µ\¿|Å)ìXPE¨ì(n«ÊÊñÆü–Ð (Õ ²ífX¤ `ïaÛvñðÚ‰’P-Û/v”É{Ág 7ƒ'ms%{mRf¾¬ac ]–aM]î;QáºÅ%ꈛ‰5c÷a^‰½ÎÞNÍŒ%gLJØDìnoU< Q¥ãR ã*½˜UÅÉ6V~a±2‡n9-ÙÛ”ç‹Èð=r[–ÞYF¡¢}š±`¶-FÀq=Š5¦Å±Á4ñIgm\˜¤…ÒØ‡?Œr½»Â-͡Ȣu¤ÃpSú¯Úa„µ3FxËõX¸$gf&9ôA&K…ÈÁLÉ{á ‘ %!ZUJG¹€]§?xÒ°-V„9ÇÝf\¥!Ô cÕ–žš|AÇÜXM«ùèªÉÍHZ“½HßR©Ä¼-‰äiHf~% å^a|‚  ]æÖ”j/÷F±ê`šõí‚ÓÓÑ8×G äðxúA’GŋΦ²(ÐЮÃ×ÌËDÁ)‰ \£³TyoKîvóÆf —Ò1BµW6Ò8WÀ^ÈÒv²P¶#ˉ@Úφžó”ÉHàÇALé*õ«ì5É3æNUP v–ï ³à¬Ã 0ƒJv~çOãc±têÓHè Ð vvUcD[ÕY»¹áBƒ5¬Òn¸`ê÷XÝl¦-7ÅÌSí_i }×™×N¦•~PšJ˜ø‰HjS9€°µ!~Ž»!´³°*VÚÂÂ*î‹tÓVÊ—ƒCª¦;¦q=;‰¹ƒySÃR¶7äÌî;#Η›w~(;»Ë¬¬«&D. ;¤Nå²ï,Èp„§s®)²Ñ3ZݰXXYFÍÔOL±4‡[ôT’tOFÈ Ú@§Þ÷ލ“«T³ÂIû¶t¡ _Pº€kVšNë.`Öu“H˜¶áܹ­¦E:ùÝ`Éáh¬`C9w^ðâ¡]¹Ô€ÌXF§¯6¤sEmPÁ̺Â)áË ¡‹9 e"•ɸ@™¨ÀÀãÊø"&[ÿ¢K¸s«ïîIÍs" ùY2GÏ'ƒ?Ôÿÿ÷QÎÝendstream endobj 40 0 obj 2712 endobj 44 0 obj <> stream xœí\[wÛ6~÷¯ÐÛJ{"† ì^ÎIÚô¶ÙM6Õ>¥}e[ñF¾Ô–›¶¿~’À È%;·³m{NÇ ®ƒÁÌ7ƒ~ž¤‰È&©ý׫‹£Ç¯ŠÉúö¨.ž¼ú¦%nÖG?•Inÿ© 8½º˜<]˜†Y>©’JOgGiRUeZ4½Š‰VæïJM !YNGÓÉlñß#ógfþ|~´øókS2ÏÓÌTÓ[C&ªÈåtë©¥§Vžzë©SOÝ´T¦m™ERÓKÿý ô=£lGÎá ˜ÃIC‰*ã«:w¥ùtMä%‘WDÞyJdÐïO‹ï-%à&_øùýVþ§{êEÀKÐå)œñMlI"¯a…sÈŸ-ìáþs¸€ÝBr 7Ñ7c%û@’ñã .á;"_ù%‘ßùØÃ-dcù#Ø *ÛUÌý÷µ•l{˜àœÄV¼…CøU¸]}ü*7êL&©$%WÉŽ’Ó²Jt­ä^O³Ùs¸Fòú¾ÐV2ýey0Þ -tI²f¬ô ÎzXQ¿WµteYš¨TiZœ˜cÉXùËLYF¤E‡“Že=Vk¡¬÷uÙ°ŒW°Â“Ù¼2Æ×°¾H+aÕsfÌ„®J.“ŒÄ«cS †ñu—ppÆÖf3¬,³0`JTffõfˆ,‘Y)¬µq½R£§ÏiÂIß¾w@Û³€æúÒóÈX3h© Î8V“‰$ëjYkÃÂ\+n{-+fë éDeB5Yûk¸€q°UÉÌs°(Yaç ø3FÁV…Æ\æe’I5™7 ÏN†í|DÇ]z½uç?_X8l§WLAk¬>˜„Ť1º­—°Â¯VUl¡øŒPUjÌF• ‘qbĹò¥Ç±õÂXOL‹ŒÏ<ÐHY›à+´X©¾¶;·mu\f4Yé«e¶Ø;(º`gÇ´;¿Í̱2Uzš°äÓõ¬©ô¼±¯_Ír6¢G¢úõ ws#j”Ù—psØŽ!gVPqc…›6R“´še@Ý…R)-$«Æ@Gi‘èªVQ‰Ù–^¡œxê—fT¡5û|é©}†ÊŒ‰2V{þsC1%‚ÎkÕž<-D5ÿûÓžPsãGîΡiâ”tvò9!Jj>vŒ·žé>Æ-š(V“X…PNÙC¥×ÃLt¯`«7]õ]—Jª[Ö¤‰ÍáIb€M ëYM¥-š×fsY¿Q}Ę‹•SÅUR–¹;TÑÝÙ%¶tTºÌR¤Èd"•É”Å]YŸ»%ÐÑn|® Ì'Ò9(¼ôÌS rzî©—;ëiOÉ ¥SkRŠxøD“O/ɧŸSéŠÈ70˜pKW0‚€£óŠÎu‹M=ô¶IO•‡m›¤E”l[!uÜ£¶HFA>¶iBœæT‡°w;Ò”é!Ä?”͇`ÿÃÑ€¨c„0F”Hc'2ð'c¤“4×ÎÙxÛA¬g67ˆo¡Ù~sÄQ‚CV( „6¾¶àVKÃŽ[ÂC¸=¼£òð“b´ïâ[ …Üsx£”†{Z„€×² £x'!Æ3LNŠ„À{up|O GnúïÇ@©UßÇ#O=õOÑt‹ö/0î=jŒE<&£po/° Š^e}XÓö ¦8rYõ¡‘Îמ¢;ÖW •=éÉI# "3b\d’ð5­5z­†ù} ÙùInÞãðù¬k1l!L7(Œ‘H2ìcû{^i× Xµ:`'5ì+¬:Õ(Û+l2dÐw›r‡i+éAlSJ†qF@+jböÆ‚©8Ü89²BS_õŒ@Yè>벬‰)åæ?ïg·3ïúÙæÏÒ«.v%D;€Ðss¸äýÜâ-’Ý+ß„úÆáÜ}£w ‘VC‚Ø¡.[£–Z>£ˆm(¾ß¾)²ah\Ãy°q/¬†·Wàä‘(ÁË¿Ö•“ü7pÞKXŠƒÐlâÜ/ár{ÛY)^â0]ˆ¬C7N%`)ds¹†¢5tÇáÉ—’YQ6æˆcÍ\'Š|ÏÉŒ±ì›'D~5Óij—×3lV¤î\7ö/±$È‘CþNgSê¸þÆßB²n™õ`­zZ®î`r‡üˆ¼Ì“¬²×™»gïÇÚ»¬]…»$Ñj“¥ïsݱíЂÞO­ò9P{¾‹G0£¢/„ü @Ò¿zêÌSçžÚxŠ’ÿQÖáÎÝBRqîÕ ÖÅI_ç°.ʾ‚Í ;28ÅlûÝÂé\Ǧs´ÍÞf`÷Š1›ç’Š’GÈèb´GÍqJÎ?éÚàøñehvpk¨Gœo7 ˜ÁñfÓ@¡¤U'jI€¢Ì^BÊŽ¤¨hÆE Œ ±iÍÉ]wM¥¹0ÕÕMï©a0à ôf¤ùØR¢–ø™Í—jÔŒŸFÔ¨âkáz2i¥•Œ]ŸaÙÇ “Ié5–íI;ž`/\Þ9æý”ÎPí½¿È­ü½ÿ²ÏÂk£w·“‡¦‡GÔC7»©H„΃˜#©-˜°a.‹ûû®Ï¤ÇÖ𞻡’r™€Ñ{ìÕ¡îߨ¿ê‰FGõ}¶ËÀ›”M-–FÚœl§QèèD†~ؘ †k¼c„C§Û¨$3|S€z \­l”™J”R»¯ÑÏ™¶ò]uã1Qí‰Å›tæ¸áÎJïl"eQÊ v3Ù’ö . Š-ð>±ÇŒs%F¤n¹KˆAïÄ-xYs¡(˾Ah5"YvloK¬÷lI-.²Lòª¢$ß7EyR)¨R ‡äK2-D©³¬9õy;œëÍ.ĘÁ¢DíS¨Šp~™ðªfÈŒEŽ8»Üʨî_à"w@Ã+ʘ|€s‡šE^ 1j‘RÂX)ïcÝñû@ 9›8µoÓ·øàb5d±"û¿ZÄæ!pÜyˆPË(³f V DÉZéÕFa¬»Cu£ìvÄÿ šYò¹Ô†Q­H*ÉÅt[©a™ÛÝü:;5—¤Î/ 7²WtÄÛë%è!Ã#¡`Á ×Zn0Gžçg÷ÞØÂ®§ØÇ A§æž·Å69ÁzÿIïIé ê´8-C.;-ø(¾Ãe×Ĭºå‹<$µ9 í\•§l"Áè+KL*¸ ÙçJ›õðö° š½ÞâùÆî¦…çó#À{*+@Y˜gçÞ>GwGÊ6pÍpùx7‘øñ¨ŠU(bJXzÿíê½´>´aiÿˆH ‰«öÂÎ*ý´ŽÍýå†u–}¨Puèܹ =\Ñ=(‹Äûf‘EíhˆÈóè .«€ŸªŸÁº7€Ÿ|&H'{\†_œ '°¤ 1OßE·ã¡ŸP»ê™ìwйÄï­‚hó6k¸]J…ßÛáÔ.Ö<ÌÕ°4pZÝåxQúˆÀЫÝqY Mâ'‚,vþÀΕãB?ªP‡U@£·±ÛÌPLß™il‚_6Žgî“…;”1…êâ7¿ø6ÇO˜÷ÖQ&Eê#ãìu YIí<=)–¯D/{ç3MÅù˜Ú+¬hß]µ¶+²n#Pã&ËÌý,„3Ö£8r³ÈY»ð”s½Æ¤ÝÒ‘÷ûøž°å¿€$³Åø~y’ÛAèJPÖÀ¸´äµä߃Á¦‘Í1\5NB Œqš½?4^Â9ì“îŠGûvvß”ìo…2•©ŒI¾/‚‚ð'xœÁ xf¬™s ‡‘úß`)“»·'L°ŒÝÂèh×µ¨He)%âôn¶ε%a›».X¼“I—^Rˆ¢”x—X?F#Ç”Ïg,s&áßÙŠ¾Û@:k@uìÞ=§DþؽCv%ÓYÃ!p0†«ü£lѤ<Ì6Ëß5Ðh”e›oÔ`ß¹+Ãê€`Ç«5&Î3<÷ÿ 9||N`,´8^;jôƨÿKc8“ûVaÌQ”B&M>¼«Šõ~R¶…¥XV¢\㇥°dÞÁp #«ý31÷Ò‰¯=Eñà/> stream xœí\I{ä¶½ëWô-ê|#‚ é›g‹ïcåËÁöA£–4Êh_<±}’ÀÇ¥{&¶³Ä9Ô°"PUx¨º]噫Üþçˆã˽§¯«ÕÙý^ûxõú/=qw¶w»Wg…ý_ûéãËÕ³C3QˆU“5zuxº—gMSçUÇU¬tiþÝ”«JäYe\îí¯Ö‡ÿ0sdáç˜ßêÕá—{‡þÁüzPäRdJíŸ2+«BíßêzðLêvv÷øy3ÉèØSáÕ÷%šÉ“@n¹ ä™&Í€Ÿÿº'D&ÛýšÿÉþK‘Í_‘õ=D»ë¨#òëù5ìí-Ùå&a¿†;2ò†¬ë.}s" Ö%¯éS.Ϋ9уšè+n¦’(â¿¹ï‰ø?÷ÔÓ ÂoÈÀyÂnÏ=le`!Gü—”? ª32 dÎÌ~– »>TGÐÁó°ŠÏùÅœíÀFÁŒžÐiwtÚÍœ„ÿÃO˶ªc@ÆÀ(˜øû°Œó9%qÍÌ*ƾ äkJ®èr¸ˆaÀ1Õ —ö]*í§¯ sª,WávmTr»ê\eÒ^®?ìËõAži¡+#ðl} ²²Ésµ¯ÖUÖ(]·{¨ÍÅ«öŒT@ç@W@¯ÖEÖ`jì)s|¯ÌÃß#ÀZ±kÑ«u- ‡aZ(ŒÓ%r4ígÝ¥ûm‡†ßÿ›äçÔˆ?éÈ"¬Kòx!oòÒS‡“ÁûמúŽŒ{æ©¿‘Ü ãò’ŒsT—hѹíj$r}´G[òpp6W%‰oi¼xGŸÂ´úbž!˜ ‹çBý­ÑW$móšpÏ>pîô(¤M4Ë=¾Ú"#Ãå}EÅù‡T^² ‰ ç]ÂÒ7>“˜Ït¦.†…áq&wÎqâüq‡*õÀËçAMð·Órw‡ÇàÉSz?Cœ|:xYÖ_*ºúŸreA;ôŒ%%Ò8tb¹LÅùü-Ý%§j‰<½ä4ÃÄ„E—ËAvsÉmsïg…d÷¯Ë9ªÈ]Rò„"ÛÁš4oï „ÞÓ¸ÿ| YTx[He챎†ò‹÷–Þ×0€õб<£Ïùòkžç˜Û›Â:âÓ.Ê9e|NqAI.?È¥Èç­ Yï·®…ó­{ÛØ‰‡ëržÁ/@ü—ÙÔoV‹XpÊã³ýÇÔÈà‚JœÇÙšÿ磊à|å)ç¡/A[ég©XµPY—gH*ÒÉR9DpÁœm!ÄYÐûy±F|âlÌ-þ»™XdU]ÛÜ€WÕ¦'¯˜«5pG²™ª ¤LÀïzJßú² €f §È•å¾ ¯ð€ËIJªS¾dŸhdåîbƒ K¼îldà<ÇRLËUçb¤ZðQÓú~ì“.壊¬)Ý­µ >` „-Î&1a]gs|¤9ðÍH"’;ì?a¦2©–qn(/–¾ä ˆR’IkhNžÃ“9ËŠª_Ê[Þ‡ãôºª-++þ÷¨‚Øú0½¹`Š#c‡ï×E—ªD)„¸hX«Eš-åMŠ©=Pá‹Ê3×PLsMA„e½CüKêµ`LÎ꟧F`¬e« Hp»€žÜÊý!fÓ˜Îû\rž5µŽsÉ© Ç²,.ïTÝV)¸®ù¢¤oƒÝ>¥ûÙu v\Áå P))ÐLDøjùùBÕ?k`³ \SÑ݆I­Œ`„„üÔ]g5¶)HÎjh½* &#WØPÚÇf…­i4›‡ì^@™#O Òxö!̘Êx #Ný Ë!Æ„¥õÛtºTÀ$ÀºxE¬WGUJTÈö0;QOè¤aÛ_¨õÌœ6ÊËqÇIÒó"Nš ̵ïx™IoDƒ‹Z:Ó,3UzWé`Ú=ˆòs%3ªâ§wX0KÜ–aó=ÏÙ+;Ô¹ÜvÂU¾ßöwý®™Xc>ÿöL,¿çSû†×Ôhøâ¢€ hß3ÐÆ¢~HY•­¦&"Ëé†Èh1BCsá ßD-÷¾ÏñŸõmÛ‘X4Æù;òcaìȼ¨•ñÅÚfaU¡»º2g²o`”Eµ¿¼ÑV™€4¶3ðHQÚ_òQpªÎx5”í%ü©¨Ç›Áxà$°K Ô²\•7·G{kHEznJz=l(¸—~¶Ò1*.Gnlî´\%>E7 ¢QhÃáò衽ÈmíƒvÍyú´1«åQ2×ÈB‰·T`ÐU2{O¿ècœZqGQéFeÈÖJ ÅgÁv€‹fû©ÿßõn!â5oæ¼Ð µs>Á§@PÜoFè íˆ}xc®. 2‡Ö ü–½ó0âÅa_¢–E[³ä\€”7þzM}bIyÖ>Ü nä´qç¶q}.qÒ9+órŸóŒ* ›Ùfc^wº¥D¥qWs$Öö$öPuÇ Š¤QMðcv½ðÔ·žº"ã>%3ºFBwÉ«µ¤b±Í×$4‘Íëm+:–ѳ¡$¯SÌvñú/¼ :Ÿx­˜Aà”3_~ýþ6‘t kÿ·„í-Á3[ЖT{¬èóŽU%Î)zò<dŽàbp—À %$äˆ}„Ìcî&M6#"«º@dSšKVù`ùåÀÑ”ŸÆe¦•Áü/Éyy±Öy>²2¢Ì…"•‹±T[§9 Ê¡¯þ(´'|¸µ.ø ®KõY]z_éC¥4|ù6Ÿ*rUG‰fæóÛæˆú¨K¾ùÅã_ì¶ù ÿïpÐá{ËÈm‡\¯ÏÀr9psWœ +Vp¯¥CÖY!6„oNEãø‘!mâ55†^!⟭ºÏö‚ls}íîG1\šxÞ;át$¤G2 #?ì$o³H›ª™P¥-t˜dÌ«ôÅ=ñö'æ¡ýÊ\¡Héã_Šá_‰çªóÞ“è½ÐpÊŒ Tv4´Ñ.¦(ßÃZµvüÔí£ްèyZâtºXÜ}-WjJÁÙqvóa-sîQ˜BêIƒôb*ÛãÀ+Íi‚aëR3Ž÷¡£s¸;véJíi€`?¼(Ê/Ë’ ŸÐ“un˜ö3p×ámÂv­I¢”3àBÙ“žß¤E™G)¡¨¾Íº¶ñ8®ðÄû%މ‘Æ“iætÉ#Ü“µ¥TånöàäoRxjA€W?;À2x7â£kâ c$Ú×8Óñuæ=–…¦ÑPè ß0Ž‘Í3 EUçøÏ“$ÍÕË܈ÈjÙ%¾CÇÑvÅÀÙë foà÷Å0õÃü,Þµ?›(%¢=uc=6Û«³¼ðȱmã®(÷Æàæf lç‹GJ³õÌÎ>j¤Äµ|AîŒHÇ̲'p«nõ¾‡·¾·Ü]•ÎKÚF•þ¡­"ü¡‘LtÞÅiaXÝ¿jµG0¸2³ÕšÙ¯|®è€åÝ3é÷k;F%i~œŸ·©þñ¾"0ã`óg;ÀçÒÙ’ª‰‰6ôŽøíÇ94Üšö(5ºT˜a+Ð.Î=g@ýÙÌ  ï¤ñ&ϼpOgÌ9u…RÊ JZ/÷¾3ÿý ‰ªD¬endstream endobj 50 0 obj 3890 endobj 54 0 obj <> stream xœí\[w·~ׯà[Éžp³ÀÞÓ“‡:u'V“:Ìéé‰û@QÔ¥‘(Y”œ:¿¾À^0v¿%H[©­žÆy-q™ ƒof÷Í$Ž”žÄö_G¬®>ULηGõãÉ«¿´ÄÝùÑ›£2Jìõ¤Wדg ÓQ©IUùdqvGUUÆE3ªšä™ù»Ê&…Š£Â4¸>úiz:3´Òi1];j9›+%Y5½š¥æYR¥ÓIý¬Pi5ýeVEE™Ùôr6/L»"ÓÓ{Ó;ÉòËþœéf´3QÃÒy{Ôs÷=–ûù\[G™EP…¨ä¾ãÝÌü¬R•଱{·†@r›N’Xµ;¬•ìå‘ùûwö¯Ôþ±øýOÓ…™+·ÛIƒ•YA&qÜÂ>8ÙŸÍ@•u<*¤ˆU³!JåúhOyµ“ÁÀÒa¸d5„âÛŽzbãD”¶¦£[έϥ#ŸåžXmϾ-˜ÚŽø¾¡YŸK[ºÇøéä¹¢fc•‘ÛV_ôMiÞü,›Å<_üË„:qa€9îËÎÀŒžÍÉi,Ξsó$ÊŠ$µ{±¥®µn)sð˜>yl7CÝܺ^—Žúuÿ‘VMUÕµ#¯)¹r#ä½ÛŽÔ²ÙRÜkšéâŽÛ¯T÷Ò6jÒ#P®×ÄZ¢q¥'âR\R£\[JN¨î(yCŸÂ—T‹§´ð»¤g³uÊï©ù{§¦¿ÿ&j¾¥¼ÞQ¹@‚wTIA-þrÀk:èö‚¶…??u[ÀsÖL,ᥣ^=¢MðUº¡RŸS ]…ÜÏ9ÕÅìŽÆ] ´=¥ƒ=­Õ§ž÷G·|uTçcù¨€ ¸7B>P¯¨â–T[§T߇8÷àaôqÜÕ<-SaŽâOn¥pÔ·h)K*?¸Ô°¢ÿ Þšî×gdþñˆ«ðžš X‡íÁê -7ŠUɹ)a¬,ÎÂQö–IV¬RX¥kÊè–J²ÿá§Ãá= vFWiCu #\RÖ7}8!Ñæ G;ê¹§áÝ—&íz¥þö)U57âÞýª§6 ùžàB³ËT'´‡+¢h‹Ã0 å&ŽR„‚ËÿA(úö%^Â]Ï[z‡öÀ„"@ÀÇ*-°8rÓį‹8ËÚ¦›€‰/~o‘tCÁ\äÝ6õ:XPØá<4Ê1;˜žž0˜¢å‘UcM:‰Š2·î¯&’8JË´ƒ&"Š9½§€çMfJGi¢Ð¬ å±!ã¼(ÊT™Ý•XW¤µ¾ÄÄC¼¹§^Ô¼â*ÏÒé\@Ã_82 ðJ„b Ðn"¤¢Øï S’Ö˜Z»žWÃ%"Ó·T-¦mjóKI·‹jX·›½è`Õ,µÉ¡Ú^Œ;Œ—WðÆ rå:ÉHƒµz´Û-ng}X9)ŒKÆ`å†ìîZ­Q§ ì-i¹’ŸÅãœ1ôsÌ+ô ð>þyC5Ä¡Pžn9Ûa¯e\Ü©r‡9Pñ(. ®U2|a Îû æZøö¨çÁj‹5¿¤:é,†œ˜·óƒs«Î”] ÷ã@~»o¹OãG pN{ÉÔÈ<+ ¨AѤ%¨Çô¦ìE;¤‡,+—J8]ìgÃ6«¢ÑÿuP|k.èbÄ Άž7ÑyÌËèôÔ¶å¹£XŽ)åñ[}Œæ•‰+w%±Æ]! Ñ6®d@òRÑäú9ݰ~6k$ìëÿ4æe7S÷E;HÖEõêöɤ¬](G¨›Á³=.`·;Z9J¦æ·Z¸$pqIº 0€i?OIÓHÂß½']ÔK2ÝjId» Ržú*$ Ö;y@HVìo?îe~ h ’’k;#¢Êõô;G}%l|’ŸÛÈôÙ7üÛݪ»á8\Æ1=¾8zqC¤¹.GŠ'õC×àÎuÒì>Ö;"î/wçÝûUàYù f9ÿ !Ì $¿íºíöÂK^UãFí&T‹€¡ÊãÝLW‘VIæó°b± _%à'oëUåt•VEY‘š™ êŽÉÞdu,ÉÃõ5HuŒ\H†õJA\[ãVò(O³¢2Δ\Ìáÿ9í5¨«é«.XXÃCW~l( ãrHÀ¸@>PΚp,1ÿ;W‘Äe 4•ÂÌ*íf&E¥eS˜©gó8ÊU^OÙP3«â8æ3s¥µP“u\¥ñ=é´€–Ø«z"#Ï´ Š´Å¥¤Á¹]Â8ÑÖOvÔ~¿·tUÄq^—}vƒ­vjçå.Ü8ÃmÜ,¯¼n¯§0‡pÙŒ¦U9}=3³eU’*æGŠ]“¼®«5:Ö•1{C™yJ[pJ— Ý‹8_ ¿7ýÞ)¹Y¶1/ <¾QÛ¢,V~8V”ÕƒXšÃÁÈcUY>ùÐgÑZâ(<Ô²£öªþ[´óoº¡O½Qýª=«è·ô2ΗCnZœDæê×ù`î…'t=ö8ÓšÅU•Ð0@¾lÀ\ï/¨oÎ¥A–E„0˜cC¥¿gÌ*&xFÎpIA[z‡ç[Ÿu¯¸ò!¥•àA>ળ ú_¥|l¨†( ½ѯw-"ãç<Ô@\̉£.ÙmÖ+]x^JW™£‘ >ª–MPeT¡|‰8ÂHó+{–ñ³7 øÜëþš÷*òO˜Ë ‚ªce燾~P”#l;`Œ‡\y¼6x¤1•~ÂkA›ßß§TòÅÏ%¹N¹V“_7¤ä¤×O(ÛÀÙº»ùh4˜å—HžæUzÚöQKþ š®–¼Î7!Øà±RÊó^9êéûœPÊ.¬yIzà ¯<¯m„ÊNxÉáÀöÊ×7|¥³b,víˆ]±kž X½+‚µýƒg»wpôÞEá‡ûò›¸i²Aƒm/ËÏÓbÁ<È’>í’q5˯$ðµú¢·uLñ¨sBíï«ö)£R»· ‚µ¹4Îð™:× öÒ6äàÍqûN‡‰3ž9ØÀ Ð]Ì|llqƒ<à‹ŸÈÁõ_ÊPöÞ² …ƒ  `óQ¾ðcÖ³ÿ×#Ø>Dzâ&õ¤£B>гÇw$êêÆ”ÐóÿoGtäû@²ø¼‡cБœm^Ã:8£ à)\Û8Üü)UCŠ’_ ¨GVòx3—™!+SCúm§¥nj‘¢o äº+ÚŒµ»orzòv®öš4ŠWÞn©šöIv¬ú—þ{KM€¿…­èÓ `š÷ðùâèoæßxNendstream endobj 55 0 obj 3252 endobj 59 0 obj <> stream xœí\Ûr·}çWL¹*¥Ý”v4À\0£7Iv%q)‘é'9U&w——X$×äÒ²ÿ>À`ÝvviY6U!õb€Ðh4N_ ³"2+ÌïXX^={«²ó»£¾:{ûסp{~ôãQ›—槯àååUöòXw"ëò®ÉŽÏŽŠ¼ëÚBYª"kjýwWgJ¹Ò ®ŽfÙüø¿GBäµÌŽÿytüçw³¯æe^4]UÍnç‹:/ !;^¼™ëÎBVŠWfº²¬›¦(gîû‰+]ÏzŒ²îf+W÷~¾PºJÕrv9¯teÙUº¡ù\ÊBÍ·>mÉ©SÓ;ÝŸ9¡®á§³ž”Õ8û¾›À{Gõ†Fý0ïrÕ–…´ôžDtólþŸã¿Þ=Ñì–¥c·fkÓŽ|Ìæ ÝWäš‘k]ÌkUö,J7QlúÞ¶’ÑaÅÍNBKW¢¡ïlIt%/®©¸¢bFÅÐm䀿Ge–ÛsBÿ!Ñâ¯Áü¶Þêlé|Ý‚¯´¶ °Ê•ÏÂa· åÌë69`ãÖ-,ÞÀZÌÎë)Ö³mÚÂ!6ÚbÁuÄ6¾!Ä–3ð•ê.]é}Äæ”$ß ABœCù}9ÅoÖ` · ³Þæiƒ‘ÿÇ߸U|ÖøÚ•ž¹Ò›}x±ŸÙ¡0hçH¢]ø8r‰aĘº1a‡ò=YŒ…O³êqK_Bð&‹$茓d’<¾¢YüŠÿ˜R`k¸¤§°Û-ì¶Ù͉ò‘ªl¯ é´÷³·]&jÇuŒ««º?WGïf¥F'yU×Áb‹Zy-Z£n4&éÚFÍ~™‹2o„Æ=W÷TUWT³Óù¢É•²ÇE­]Û-®1’TeÛùpå¥7„«þÒT‹¶¶úÄUkN´zdý£×Ìi·¹lŠÚ Ã¾ê6·Ìû>wMV¶X4íØÖ º×v` Öš¡±ì4µ]^!ÛÙ=Õnçî–]‡6 ˜kÙiŽ%󱞨F4’aßµ+1úšŠ/ÊþZWê’¬•×V8È\9RWÑqT "ë–U^¨Rõëw¸wüÎ ó–ˆ0ˆŒ>`Ó÷ªš–Ñ_%Ö’Õ^à „ð]ÿ-:ÝWVZRôNHQÖŽ`-´;*1¼ï†éþ-MUQÈÒ[ΊúPÓqLÝt²ÎÚ6 Yæ•ìú“¥rQõ P²Ôr]-@Ç+}2OÂå6¢®ø Y'4>ã̆¶úò+§bÆ‹Ž¬>¢RkЦkýŽÌ`#L®0‚€ûΘ͌*¼ã‘ypÛí%“ÌÁS ÎÚ›—N!uu©¸\õ;§E@«Çëĸ†nÙÔÍ8C­¹ÆÔ•×kÍ{I¥´^QFX£´,bDf`¨hšÑU†¬3W·ÑzLS¤|¥ç¶SYˆ]ö+T wù“íeXÞŠ*ï5BdQÆ àEh”lvÏþ2ußMݾy18ùBFŒá± 8‡[X»„£6Úw3*¿¤"C[_Â~gFì_Tü6lÛ´ZOìzŒ UZÚQÓ}M_mQµ’0Þˆx¶ÓbÀï¦ç!õ…2¦^ÈJ¥²‹5`;Édo*Âïæ§C›…ŽØŸöïˆ}˜b ‘+H—µý1yl]|̱ý4éê:äÜa ;5³s:a‰ÃïÒèˆE‡µ?b7p,¦áØfÇ2ÉÎcGÌ«ßU01;'wá~J13º˜‹Ø‡Á¬aL ›ûø¤%…"­¹wŒ¼¯0$õ’ç!üäÛ_S±€ >Ám·i3Õµe›€å™QÀ®‘¾ž×Ú¡^J)ðH-ýqI©nõ2Y$YBnkQ˜Tó§SÂö¬Ô’–±Ë<"=Êãù— H÷ây9½‡ëĦS;áhÅü/=þ³a/¨)––3´Ä€Yœ`¸ð ¢j™]ÈfÇ9|2{œæ^îD‘WE3ÊݶwU©¶ár·áâ>Á¸ËÑ¿t~îêÔ‰öù¾ÓUv°Ó)îåy(èÉÞÁyƒg°v.xX}F<<ÜZfLæ¡NÀÇúÿ\ÃÙ»¿‡D§Ôƒúl6Iþf›ôéŽLûÙìÆowdýQÜËè|áâA+¶"˜¤¬¿ÃÀ”Ge†~™÷v¼„È1 upú;tˆƒ,K›—먎 [JØÑ$ÄÆ##h¬Èøc&"vÌG¶@à“Ç6Î&¹$Ï"ÄØt^´›À3k5H• áâÈoLÂvSÕÊ3¢UËÞ0ƒ[ll ØÆÆr:Ð>$†Ò‹¹*:‘Øn ƒ \¶B/êèŠä¿gSéÎp~žíÇBG½pK>Ò)””¼øl±´‘¼£ùŒ,4ªLåøZyÑf~­€cÂÛLÏØ«Ë¼Uµgìýi¾¨Ì©.Zs’lCŒ±8ov›\\ R“‚Z‹qèç <èi ”í…cÝ$ùMŠ&Þ¯´ž2™û?ùÏBýâ)K4c?%;ÉIªn&Ná¡îáP<`…r†Ê¿Ÿ ÏO§ˆOÐûž»‡r¸¥¶áó+üv¢4x§,ü%ô H¤o“-‡ä ø(YXÞS3„Ä=°4è³­H›Â`b¿£ÒkYåTbîf,‘“AˆÔMìVûÓ¼6èo: |ª¼íÿ£^D–Ó€Ö ÐoN+ÉøV qwªæsa…ç6iœxQ•‚ŸóIRJ&Èû§!¥vÈý˜B˜à¡Þ–¤ÉÕ Á»„¥ÌËÎåK0qMiûʼ{-ƒg‰Š®Ú\Ë §§¢chU;…ÀbÀÔlÏ×΢(†7¸ïú0QüÚùÕðÙòix0Ìžßî*©çµKz¨Ü¿ ®ŠÁE÷ÊØûæ> EÛ…ä·sÍ­#Ú>ͪ¯„Ž‘Ä;ãVCGzeüÕñÑ¿õïÿhOÅ#endstream endobj 60 0 obj 3018 endobj 64 0 obj <> stream xœíYksÛDýî_!ž‘:µª}èU¦ IË´) ÐÄ<›Â¸¶[§q\†ßÎJ–ö^É'òÚLgꦎ¥ûÚsîÞ]'W^ éEÅO F½;Ç©÷êºW>öŽV`þªwÕËBUü+p<ºðÆQ/óļìEažgQºŒ*¼$6ŸóØKE¦Æà¢÷Ì}‘„©H¤? Ìc!uêO,ò Rq’DÊ?$x¨0Jr­ý#óÐ § [a¢ ó!÷µ •HÉ(µ¯3ÅÞÁuЯ« ÏÌtH¯¼2V*tî'ôô7°n£"Ì”’/Ãè$óçA?£HÈœÇ.íäÏ*bí—d5&8iW¤’8)L©Š¼¶®ó@W¤ï '0([|©Iå¿ôe(³$ÏüY•§¾q3iS™¢ž÷¤–aª3Ó%ƒ±iг:ë¬L•ˆXsZæäþG`P™n³P-ê’…¹À‹Á¶,î™eèÂ,Q›bbÁK$OR*¤=O‚~Îõ3Á…4Î èW:0Û—T>3`Ý3íh†6o+MÚnº!¤eAÕìU]L¥ËTÙ².d•±.ü-ˆ‹½Ý.·è—~Ý0…q,—]3³[ý͉ Wöõ‹ª–KcYçÍ„-&¯ôT¹fa(ߊÉBÎ`—íÛ1vbQµy2›g-aÆK€;ºŽóxEͽ3»L&õNË1òb=È [˧‘Lxñ˜É¯ÒöuGØjj—³EÊ0Iòz¶L i Èê‚‹Y¬1Ö’o¹Ïeûî²±ÍÛ­U Ô­æÍàsXJeKs(&™7xÒÜzf¦i_…qªÊ†¨Ðƒ Æ aeqŠÊVï»Ñ¾E' öá ZÍIjnJE„Â"½D"Wæ¶PÔ°Œ.2¡Ãò0³‰^“íœàÁÁVpn˜Ö5™#[É¥Ec‹&'‘9OtZgdïmAJ÷×àÙÌ¢…ECðì T8kÔUóCk™‚ˆsðlXRu rå Š³S‚Ãuš\MdÙáæó^C¡ÈVÁU_ VVØÖ²‘²™EÀ,Ô±K禄>²åƒW¥ ¯ZõEÅjè¬ÇÐ7vKœ@礓¥ú¤ÅŠ5Úg]ÅŠ5’æN‰±´‘[b¬í`"ÒÞžƒ·÷qê½· ¼G{ÏëÞœ’<‚n[mzÉzbeþ·úlc†öþ5C{Î ©·ÌnD¼]ïr.¤6ßx²t[^PMç°hÆÀ¯Û±µOðöÛ¦³ë”iR‹‡Ù§;j7£4-ùº3‹îFtÎOx  ]ÀUÌ¿7ÝiÞ ±hÐ5²?¹«E[D ’Z4Ágpø2ʈØJ¢†Þÿ‘›Úâ;Цm{“/¾¬ú¢¦Âw Sb|;øÆ-1>0¿uJŒO„ïÜã¡÷}˹ð¡[>Þz?t«ÖHûcW±j¶¹ïyô}}«&»1lûÍæöÍlà~»íÆn¸À1sI–è>Zü›EÚæÎP‡øÜH›$áõ·{ËüîÒ»C}Ãy·žd§§îjí.·7M <èßßQ»e×Òï­ðqØïâäŒÏÃÇ-ç¦>¿XMŠÅ'ηbñ‰sä”â/ÝãÁðU'K¸á¿î(6^#éÓ®bã5’;%ÆÒ¾ç–kû!`)^#é-§b±¤Ïw#eÛiMÔâ†ÿdGí–Óš˜ÅcágwfWÿↃ0l$âë|GÅ >§?½àAû1Yäƒçë~ˇ«O¾x¾´|?ôžšŸÀ~¨‹endstream endobj 65 0 obj 1341 endobj 69 0 obj <> stream xœíY[“SE~ϯ8$B†¹ÏKJ°(Ð%âXå² ¸›À. U*üv{ÎeºÏIçByP–‡Þ9Ý=}›ž¯g_R(]ÈôÓǃ«{¡8<TËÅÞí†89¼”¤Õ¥Ž‹o' ¨MEôÅäÙ@ŠKj­ªð~®J [“ãÁã¡pÁØ¡i(í‡Åh¬Je…ŽÃù{1úeòýD5ˆÞL¾ Â–¾¹,l—e+{k»=+üÝVVööºCþ(Yá;=ᮌbeî®1e5+ûõvÆò¹[m̧öɈµUA”!‘FjPc)y”Y™ú=S³LÍ3uXS*šö³Šz½‹|=ÞÞÊäS4êÉ}$çHþ†dä –—0|ƒä–á»z€äsÖ2~ãÓ~h»åÖWÿGô#"Ê)¾ñ¼ß>²‡™õ„9[g™zEU¶F³0¡N^Œ½ BY÷ßÊÛÕ=7¦Òâ=mïuRT_£Ð$¥ðʈ´U+\”25ü ¢õeeb ײ>€„ í.SìZÆéÈ)M¨ÂÜ.ÞiáK©5ä¶ú¬“Ñ-õ–l9#ôIR•§õö.&·[­œ®ÓFÞ— Ô&ጆjH¡ÉËTìN·Õþ0)÷ z3’Â8ïeJ„Ai’•6WP÷o " –m2Óabº ZÎÃLÈøN\!("C='By÷G£q„=µÔp!¦ïFËéÊ ®–òR ÇhŸBÞ+¸ÑK|޼³Š×¸ÒP^¢Ì"ƒÄUÂ@Ä A¡õÔWGXCqà°gþ£‰}\$Æ-p•„9ª>–TCÚ;…Ítœ>ÀUâ4›£{‡4*©º´Rñªi2…Ó3¥>gkH:lPÎXg,ïRm4Ö¬ v£ŒøÓÆÜ©b#B‚NV‰ÉÕlE®8#gl¶ ïÆdÖbѹÔ5Û¬ñ^>„CÔTb:^$*wªê‰ºlùw…Ty¤Ç ·M%ödXµ±XšTÍuÍ@«zPU3së”ÐóbHcë½€óPŒ›Î”ä¹3,Û´8ã:q?4ñ‰7‰3æ5˵&äÊ{Ò«þÉ\5‡ÈÛÁV!'`±M^ÓREë*„¿ß—+ö·‰iA? NÃ'0%H¨'Ò§l=u²Ð«=Y¤üýÛŽr-6¤¯YÑ»Q¡´ÕÝÒ|×夢0Ò´åtÈ×9ÒYç>’u8¼r¶Ûä¸óöe]Ýð¿¹cÇ­ XŸõEÚâ•yÜ9`ÐÞti’ªaeÛBºõzS %“›ÆK³m† O˜¯8Äí3|8¾`$N:fÕ Ú|„8Í )tˆÞƸªX€¸`/aàí$gŒ†¥Ù~¿TeLeøüüNãÔùÝ|»¶IŒÇâŒÒf¢[‘O‚º ïý±¤€ú¸ó’´4¬t紥Ǖ´ˆƒŽiŠùûÃÙÆôw2Û}!áòôtÓ±¨ fãÌÏO›®WìèÂ?W‡éÖ«ñÎÀÅ¿}Eä Õã9ÿbS—n€…Ø–îÇ¿¤§Ž‚9&ü¡\° |Ën6gã†>òºÖÏŒë[ÏŒäæx˜ïüëê„ÞA …ôý!S?1|2u/S7-Èw?S[ö2…–Þ]­%Ý€ £ ,ù„¾ßgôýü™}ï]ý¢¾ý+TjÏoÓQÓã7)üiÿ»íD g™:"Xt&64WŸem=©`¯EYªüÚ÷ ¢­œð©Mmî*-^Ê¥(—äJíZ;;/oÕyU6åm1nò‘¸Rkj\—¸nM?ÂÏ_è©×¾endstream endobj 70 0 obj 1642 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R ] /Count 12 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 73 0 obj <>stream xœyXT×öïA˜sŽ]‡"ñT ÖhìXÁŠ‚X°¡(*( B¤w†™¡m``}@ÅXQƒˆ…Ø5Q£Q±äZ’è5‰×{£ÉMÖÉÛÜ÷½}fáæûç½÷ù Ìž}vYë·~ë·Ö±¡ìzP666ìü¨¸=á¡{Ä¿Ç .6Â=„!¶Ø_þ/ByNÞÝ·  êc‹úØÕ~ள´áQH@ÙÚØ$VÌŠNÚ¾}G¬ëÈÀkF3Ö:ò±‡‡‡ë–¤÷߸. ß¾ÛõCòG|hDTtdèîØ™®óÉ숈ð­®Û#’¢wĸ†lÛºM|luHDè.Wïðˆðèè¨xבóG¹Nœ0áãqäÇDÿðÈ-q1®~Q»£\—º®Ý²ç¿)ŠÚ¸Ô+i÷Öµ~ó’£¶­óŸº~Ù‚OÂîÙ¾Ü;fÇ ŸØð•‹âV-Žß蛲zIbä–5#Gã:vè¸a ï6aÄÇNL›3)}îdÏ)AS7LÛ8=ØcÓŒ™)³ÜSgSÔ8jµžZF- <¨¨áT@-¤fPã)7j9åMͤ&P#¨”õ1õ!µ’ZDM¤Ü©UÔbj5’ ¤|©ÉÔ(j55…M­¡–RS©1ÔZÊšGM£ÆRë(j>5êIõ¢zS}(ª/ÕƒêGÙRý);j%¡R4eO1”” ¦xjµ‰r¥dÔPÊšE9R³)'jåLÍ¥Sž” õµBí 6RL¼OYNݰ™cs©ÇŒßÛn´}`ÇÛm²k—,•§ÝéjúWfc`~ccÙW=ö¼Úkr¯3½Ã{Ó§²¯KßÒ~ný2û=ë_<Àq@â€/ÚÜ=ì×Ù—ʤ«¤ß š4èsÙvÆ!Æá™ãhÇxÇ:Mršê´Æ©Òé¬Ó/Î/:¿|Ýe¢Ë•z~P?dðŒ!·¸î"?™/pus-uýb¨ßД¡­C_ ¦î<|ôð°áun½ÜÖ¸%¸•¹µ oú oIˆ3ÙÀŒf8Üd Û£ì]­Õ”•+4I<¤“²”ié:e5ÿ°#n®ø)=Mü$øÀFºJ¥OOSÊ“9<fÌ‹» ZM6ZØ3!ÌVð„Ù%ì.éö(L ZMy™B“ȇÑx`”àÍt’‚죿îCcŸŽ8I$'OèÅ¡1tµŽ<‘EŽ4‹>î(6Ép ´J*h²3öoªL6m0<`º-wX€Ý$XJ¶U¤§ëd‘4r/mY™B›ÄûÒ ª$åÖe±-l9`‘ÆSðs<žK>ï6ƒ¡d†ViäÍ[’˾0E›ìÁÞuÍŽÒG0S˜"óo"Ϊæëñ‹`:Ѳª‘G뵃¡„>jÒ* ¬ôÔ¾Oë\>CFå¾Äñe{P$‹Q8c½}ºÊrˆd>4ôã…W&ùmOXºœ#‡H1A>|HNñÜä(mv }d!ÇW×ø#3;áA?Ç[·jŽ·ñŸ+«Q«“ë²yùY nmÐ’˜ùdf¹þ ¶ۡdz{!«Õ¼ôNra‰²Ü¥T£3òÒ†WŒôNµR—ÁIR•YIü{ÔØ7žÓaùí ŽÒá1˜dàéÄ+ÒûUzMY…¼$Y´nŽNÑxÞ€'ÀI‹hݲr³u{ˆÖ Z|p:ÀH’LK´ÇÑê âÁ°<ðNÉ«çÀYÄ®hã$>™îB÷=“П€û˜-ƒá7‰®#γGY— â꬇Iãñø-þÞJFŠhLKýQ×n…‚ø´Þüt"ï%Äii˜„“XàAœJbk€&[ò‡,°ª¯Ò¯Úï<ÿjó™iGø«G4 sì3¯/GsØ`Ýìm¡2…éÌýKÁËýü7Må±;®“A Œb®£zåÑĆø½ÛÑvÖo府yã-0€ˆ½¸ñ ‹D!Šÿ:–NÔd••§r@Íà øGÉv¡×†87-x–“žZI.Ã3¶³ñz‘³ŸæË°ýwÜ|7êßý úÁ ÷âAü»?1|ä´nnÓž¼{ûüÉ/¼ùù=&xg»šÚûÓÍ 4m…~ŽÒt!Ix+guÀ,§×ß[ql.bïÑd^Ú†û¿ùz?¼züëÓœ4mâk¦ËEÒôU4Ž‚w²·g¸‘™î3gŒrŸMv}üä_\—ËM0ÿVÓuX` @#ƒP+©§«ôÚÒ2¹–౯ÕÂM4‰ð8H|&F¨È|‰æï[OÃ"\+ÁáC+hÔVÐ ³"íÜ1Ù y‚±ž±‚{”HâbF¾½c+¯’¡t²åUüQÝñXeèBÔV®ttºCc²ù’,¿GPË6œZ¶1Z„–}²1j!òg§3ØúãþðÁëûOŸåì/­BzÖ¥•+²ó*.pÉÚO¼‰‘{¹¿ƒ¾ü]zÿý ؾ|²Ú¿ˆ+H)Q”!¶\£'adÇT);©Ý­ð‘ ‹dG.(H½7©±âD0‹W¨ $Ÿ×êd@bµY„kGA}zñÅÈGˆ…?ý½yéè=îà¹6Ú{5Þeïz?ÓsÔÈY^¿þ¦ý_ôÀ<Œ6Ù•$C­y5©Gw<›Ù6†\`ûxÜ;üî.¯ Ï™z}–>K•“—­ä·îšç‡0…fÞNû5Ù¼{ù zƒÚ*'°Á ·Hpôh†ËÍbpd;€/S­Ð¥¥©Ì!€‡â¯Ô <6K®à<‰IÔf•—jIÔ@/8šËà'›%ÒĦË'm&a²9DîË0a2ìˆ=°ßÀðG˜ ž˜JBØ0Ýð±˜ÁL\Áõò‹å­™ï÷ÎèŽ(Þ’%‡ñö!ØaGuǪ©#N&ÄÁpæjH=¾Ã´eÿ2´šÇ ›4r쟽y݃ |#l‚2é“›AGæ¸àþc&àÞ¸÷?ÆC¿Ç—ßjåqÑH&Ù’JÍ6ZœÈK߬ªÎ›G:Q;Ž–>Á>Ñ$BÍné>›¸õqûÏüû¬iaäjj’¬ ¨[† ºNŸ;uüV­)'ïwF…P!bKÉ6:•6½(ñÖ„"öã%«çñþ“™®Äú=~1ªÿ„ygšÅyz… Š,&}Jöõ€Ì¢~ÑþãOÓîãñ¡n„Û×£Óè®[aކ;(?æñ­•>ÞóWLæ;µì[‘éÍ„ëÖü»)¡ÉQzŽlCXÚ-˜N²À§šGfi ³`졌´ ZGÑÉy™wð^Ì_œDš&ÂdºZ¥KO5ãôqÐWÌ{‹FÝç“À¼"׳b,f˜#ó~1ÆüÉ™· ¹h å™" zÃ'´´á-ô8µï”Ë^T“¹?V£,Ê+­®/%VO.ç?©ÓïFkQ@ÔGÓÙ?ÁçKüpz_>~ð=Ï­ kŽÌsÁvãÆa3?»{çÜ:Ícã³*ЇÃ=i+Šº ¨:ÞpøhÙdbÉ"#/0Ò×]x0Ãhv´ìu;Ñ\ÏÆyµ·ÿ$Z3¦Ô&á‰ÉÞ{$Œá q*§þ d¦±DôGŒïÃÑõ9ôîc_evŽ )Éôꢂ5w²ô+ÃaT‡*s?•9&?dÈ­tiþâHM©\Ÿ™›“—›ËïJHVÆ£t¤(ŒÑ±ÒDCdlÑn—x”š¡bÏ /dÿïî¦ÜšaP“mwx”•YᑞÞøj“\"///±`#ð¯°ñ'”ZR/ažñ0ÐLÙ §àøiG©AX Èd;ŽW¬@¬Ô{Ô¼ ß]Õ»&ñ“*Û•—””Ò¤VíA±¬Ô°ÚwÛä© Z9 #õ.Uè‰üó3?|NÔ(Ê9i~™Fkä LsØÙä[„Þ%Ï/=:–P±—ßUjX¦™¤SÆ•‘µâË2«ª7_:ÚþðóÈMÅœtXJ¡FQêR¦Ñd¾f¤ÃˆI礆Œ,²ƒÅbÊÁl³:1l‰jk½ô…ãFKØ‹('« -m(»xãÓªKȹ í%ººî½®fºËÉ.«“$õtÑ•ñþ!±þk-*‚P$wª7³»Ô2²ÉÃ-«^à)ìe+mEÒ04êÄÒ–Žž>ƒn²ßN¿K4\Á_RÊ룞4¨í Æ1÷n.ó±vTgæ[j‚F1× ½›lIþ •Uˆg­(5{xµH÷©æ;ðx4½ê“íŠ5$+ºMzð÷tõàÍ–¦Æ†–ª+躷¥x-›\B@V¢¯æ^1ÆmjªŠdö4&øÄªêÅÈć¬Ýé…æ³^ žøÛ˜~ý«êú+Æ¯Ä ã›maaèÍU àÞ^VnÇ{iÏëA?~ tã!½Â ÏÎÍËQr[b&¯D¬wȱ6j ʪãÞ3×ÏÐÃŽÿÞ‰kR7CõÂ/ðän¹ï[˜Ýœ*¦ÐÁÂ3Y†R‘į`K¹:ÚŒÆËDÍjӹΘ*˜e²ÿÁ”ÔWZ¥Ã„åB¹Lj0…W¸ ›å39¤z[},8îDÆ×wÓkTu©“«v¡¬×ÂÀæ' ¯¶Ü[ù•$!–'áM ‘ŠÒr²wˆ‘‚LWcî¸@Ÿïî¼i‹m ÝÏG£´sÊüô1šèª˜ªÔÃè0{óú¹§¯/S›±_¢,s)+ÑVó¿0b m¶æŸi‚@^XL8§vÃìP« 6 qûETˆ“ ¢·U\À¡¸BÏwû¶—Uîïˆ ¦ï­Ì[£`5¤Ðïuû K:û´9ä£4VXâðÐZ…JÃ…>$Çts~9-=µlå²ÈE.k·šÎòpq¬µ€½@¾u~Íßn];xá×­*ƒ™ÍàA<ÿÀ$*yLŠ.³´¼„˜èŽ ˜f’,õJ5wfvÄ ûŸå·U2YPd”YîÔyó{VM84óVÑ ÷¬¤AÆÅ…jL6‡!d°Å^:À ¡¤©½Ž±ò6M*±’‚’›Öò¾û$ ³:úHÞ£ìÅbœ›I‰¶Gi̇E²ÖFRô.”DÓÒÿe=Ÿ£å|rrË`o…ͤ|u”œ¦¥Ö½¬{í¥±v—½ÇZ-u¾Ã¸Ú:ãµÅ¿‚ÊdóÖRªä{Õ›;>Ê’D‘N’ªTb®J¢^Ñ.œ4Ñûë—¼jÿ¯‚&)‹Ì2+N2 Û}7 $`ûâØq];uyZ1ÙáÝzJg­•>©•ÿ gûúÍX·aoëZ£R›‘ªT$rxsoq[ÌMÄþòíSsAðÞ7¶‚ŸÃ3âšên®éa½ç9ÚߟZÉZz Ö·û’Ïÿç‰Fú9ñNý ¯°Áâ&!ßœv,hz+r’µÁÐ«Ãøá¤÷Á³ÀN&{ð«u,LsøG·øI]5ô]ŸÏ? óMKZÅef«T(“•k³ …jQ’<ÞwPs±÷Ïí\Ï/`*"J6~8%`2/=;÷Ë5?Þ½zàòuî”Ý¢¥«ÅŽwp}Û»'o9£’Ÿ°ØJȳtá¼`³-Ô8\¦!T0JŽ˜+Ü …ÅÓ ÊNÂxLãÀŽ8I.ý…8É#s¤"«$ÉŒd¹Õüú¿Š“mà%fÅOdOhYvorþÞ”+iOXÖ6º öÀz§JÇeå"ŽE&zïþS4>† %Pø ´æüÿ%Î÷eßËðtrþ0 Oãé%<…Ý2˜%Zú¨«!„m¬„ušÆC° ’VsÜ–›Mbkõ¬†&ëMÄKILfÑÒ—ÖúÂÙz šÄãhGK|»±Š“uFV÷Fá½&˜Ó´õ-Ø¢R·# ÏËZ¹Ôã]}¥*s×ÏÐûá-RÓ寣uÇ]ê„oåñön ‘Óâ÷Wï#9~dg(/-Á÷ºOø,ŽÓW7œ]¾egüºm\Ü™]û6£´;)<ˆýsŸú›H]ÜÓ-z««KCKÓ;ŒÝrÑë—´´mÁéká÷\ ï›·0Œ}ƒû{­Úµ`Þ_È~~4cø¨™£GÎzúöí7O:ƒXZóÜdSAà A¶Ðßá: ¬à¶n„C¿ ­éÖaEËwôŠŽhl#| ñ#„ÓñÁJ!Zò¬Ñ»Y`hvØ{ÝVø˜\BŽ”Êäè¸Ç6ܹ#·cÃoc»}¡:¹±e:A_˜WÌ/„‘oÑïè÷ÿ~ ý ‹ ‘š5dé29óZ_ØBY+Sž•™ƒòвù¯ñÀCîb§Á5r¸7î—›òP›©Ë2è5šO«¸»`óôB?£_výÛìbå:9Ím&–£‚¯ ;ŸTää g¹\‘™©ÍÒæóê|pñÀv*y~.Ê«ƒ¶XmÐrOÀö)¶Õ¥åi³A¯5”ährKø`G’IEqI 28“ÓÊs•ùyÙvÞ,ް©.=M™•h¾BÊû )–¿s¬™ÏÅtµ¥ß“ÄáÅxÓÕ†9ØW¬OËËÄú´Ÿù‰|7Ù\”ŸKŽ˜íœZ–QY¦Q×8U"ƒ¦þl?×d•ÌÞ䪸Øä且] äªñ7‰Dˆ"çrT¢-¨a£ðXÙîØ¸¨¨}q‡ørúðþ½Æìä;O+ä‘“v¡¸c„5|Éá ¡è‰GêqIѼ9W#¾¨®Jœ>w0êåUÝGÅ£ÕÚÀè)Ûºýpäg¼žnK¯/..ÓˆÎ~ÑàjRCO{˜’L¿ 79JKá˜p_&õ™.÷˜7…U‡JÖë k¸f»ôÜôüLÄ.ßzüܵË'óX=m‚Ùµˆ¤[·f¨%qáOr;¨èË-x|ï¥Ó‰ð)..*)åšk[ MIJWü&[4oÔòÀý-A¼J“]Hð’™•%O/O=ÏŸÛy.å<ÁKïoÿö`çùdĦ0EüZsow€éÁæ -¼vMöº[ñ6ä?0Äúy,m9d›É柟‰õ&¸B¶,Ϙ_ŽôèfÕõƒ-gkO?‹æst¨@µÏ,ëÆÿÁÉ”åÙEDå='Ï.ÈU+x ©¸$]“­CÎz½N¯Î+ÎÕ˜åK“¹Æ´Eh–áÉúüœ|Êu6+ÈââŠb¦ U’›ì\&Á’\k¸ãÐÂtZˆî*RN—¶j_àþˆ ¬ÞõùM§jLbÆK‘„B_ ½á–Lúè^Ãáæëƒ_Îx8fÌŒ¹ý†|ÆI_Î ß¶fñàÑÏgýòËóoÞÜÚv~Þ1Î*Éü‡×V-ðõ_ãééÿù­¯¯_|Àã;éËg7Vxz-òŸé±äúƒö—Ÿšƒÿ¼€ex!ŽQdåå"•s&‘"Úâ"½†wðwì§‘çj‰š%¼8·D¥éÄÛN’Ñz4ÙF[!:dI…)HA²²_Ô¦ ÏYAsQ"JRË‹Å9Ù")Ò“N¤œ¼y³®õ<ßþåñïôdaÞtè‡{ž3S#M{uï|Ó§º(‹Ù”™”š²kûÖø0Ä.\që›ï.ßl¿Ý¶iÚ^…òKbÍ/9ž³OÙìÖýag¶‘M³Àé¯Å£ΓñÝaa‡w[Æ54D²ˆÊFñÁ Ì ÿ[4ëÔ5:‹hžþòéã"­ 92T¿ïÀQµ±H£Fåli–6uUÈÆNL+a{b¶ç¤ä+(¥iU•y'òêö 01å4o¼X!VHÊâÄBnWQ짨^LQÈ”Y»sÿ±:C 2‰¾Å2a N+Î+Î+BÎ%H£.,),r*T‘S’S"/Æ¿Ã:' ?ôÅêb’×QQ~Qùï”[¢T䢜¼ì\ö½4ϰHsõ{i®Õ«ß‹p8Ö$vVîËÌ剘uÿ3…po)25‹&›Þ5ÛF¸Dä¿¶¼\ìDtJP•–¦Uy!ïVXkè~ 5”°¯©©¡Oö2õ>Ù§©O_Šú?ù(nM endstream endobj 74 0 obj 7634 endobj 75 0 obj <>stream xœeWTT×}¼çS„ñ! Î ‚ÄEEQ:„&‚Œ´¡#EAc VPT¤#(HEl€X1ŠÝ$ú5†ŸÄ„DÍy“‹?ÿ>ÀüdýµfÍš™[Þ9gï³Ï¥¦B‰D"];™m0r›°hwÄ:Fžîlɽ^rAZ~½¶¬ U¡Ò”ܸƒIY)™A¬¦ò1*TÚØwèBÿ'Öëÿ“† ˜)#hÅàýíñ|;#k',éñ »HÚŸ"í‰/DkΟç&!s§E>ò)h.‹5_X‚Pÿjì¾)i¾]ù zɨyOñxÉ&¬ÆÝ¬÷³ñNöˆ Ê]ýäž$Ø ¦ ~úþ;éàõ³ ù9ä!s tžÃÄ1âÞD—Ôn0:CÆ V´”®@ú …Ÿço›HsdÁŠ›°Î=‹ww[J[ÎK6ð:\ëiÿåXg)¦>÷mz#Ÿ€Ê7RMå†ÁÔ•í9&0XÔ»ñ‡éOD’Uå À­¿€c†PÀh~Œý&ü8˜¬ª<ÉÇq{Ê3¡C$­ /°Ö2ô0Z ±™íÇ"̱xÎ;°{0è«s<‘‰4³ñœ†Ø©^­ï{Ê^5Ý—6?¾UÝ…ZЙ„ÂÐÜø½ ˆÀÂC,IûWP‹,ÐáÓ„ÔkáŽ@,CÏý˜!èT:6FÝGú0—”Øü¤âNÛu}fçáä'UI|­-“Äçz„4ÝÇ òR:D±ñ½0‚`×A°Û!`wâ¸ÃMûQ:¿¹9ª&¸É¹ÄY¢…r_ïUv~„iSX<êå<І‘} Ú&Á&^æãƒXYm[6:¸§\ i½Žf²+ÏÍ+©lÈo@Õ¨bsnÔ¤¬m(œ.ÿS?".U¾Z™ÈáQÈÈÇÂØÒÌë <ŒÅË@‹ÀFÿØôëKÉ×oš@Áì ?àU’<šûåÆJ¬²(zö:G©×r‡Åˆ5r¸¢¯Ë_»)½t﫚§ˆ@& Ú"å0ò¬gºPÇÀdômý£¾oÏ€ÖÀË,Ÿà•\Çõ÷q}|, c™ï›üg.Ú8)ÔIºn‰wÔôOL@Â#l^H‹;oÄûWØŽú»/ñ²‰0¨¯uÁ‡{í×›$ޏZ@Œ`8W™AvãêI\kµ?f Íqiêí§Ð(ÍP!l'%Ž"ó?ˆá4äràÈÓƒô¢œŒã»O½í™;Ñ.´ ­ ðµ.þ ÈŽüîý@« ©—‘©ý3¾€\”8Ÿ¿¾xÒi¬Z"óXél³« ©,Vy¼°ïVCþõÉëd{§µÖH_üÌ{CQ…4“´Ø$&=%cÚŽde²3ñ5‰ç¶ßB°èá  ê|›žø5>9‰»Zã³kZMœå~¶´žÝ#ý‡@4D"¥õoÜ‹žZÐbaV^…GûN™/±2 ;H*Xþàú±Ó7% z»i {¹ nYǤ„Žô× =?ñR ÐýÛÕ„B¿Ý\‰U-§ù9Kÿ'Î:  Ɔ0~Œ¸—ïæU¸½Ç‰Lb¿³jÃ:‹ìâdë$ëdQ.È ¹ž zù7Ma¾N±&D~ðH‚$ùðŽCÅ,ÏìNÊØÒÐÚ ßs1õŠæÔ[ˆ…Å@õÀèAõóK¥ÓÊ¿8šœƒ ô+« [$âÞ¶ªøð»ÒÓ¤ãg@ÂÆOŒ f|?Ý-W:~sY´·0~âþüB\S÷`bÌõ÷ H n€j3YÂß2¤8¿éB«†1ƒ¿è—FðRáãcRÛO[Šþû`‘?‘·–ß»õo§a>yü ’ Ƴ‹7ÐC³¸S‡éªÊ)äì„þ´s¯õ3‘¾åÚÊ–£YGö“¾çÓ„qnÔùeØ®0´I³?.…i¯zÊ.Ü”´7U^=þ Ù¢÷WðŸ1Øñƒ.Dç•»`‚`CJò¸¤¼¹è(!ù¬ƒ·¡Eoïñ¬£è0ªH¬V䇗y^ް9Zà…uYÃäµ*è@ì墭qÉ›âS¥ñÛZ–Ꜧç³}C²|“‡,l rD²ã²ŠXb`âJO§ž{ ],¾Ñº; íD_²â”šµcß6f0X "8q“E¢ßб>1yU͵UwI/<¶''ë@æÞLýCY{2`ÿ29p¶roº`)¾Kà §ð¦ðÒØ–Á¿ãG÷àýwèþ9r/«Â²¿MŒNxvô~ÓÙkH¿®T’’‘š¾MºÿüiéaÓ™ÛH¿¶0>8%=iw²tî’›ý¤)ˆÜx6›¯pqq“$_ò©\ì‘oŒ“}^7Áà¥Ò«@ô-¨«[MPpÁQóð„×¢©e˜ºŒ‡³¿0þ0)ù)ú~-è=q55m~b‡ÎB*9üûríwZÔ´} zç… AocÞ¹ÁHv ÓŒ§±F¦ëmÀzVx\ðªpŸÅÉùSä_ úŒ896¢_2[W­Þ–âª*Çn´çƒJŒä‰ÒÉ.R-aã`$*—‘Ç÷+×røãG9}—Jù ™×äs@MÔM¸ƒ›e¡ á0ÌìÝðˆ3^‚ŒˆUxhEÌû}¨·IßÔ5êcAËú ÖDŸÜ|2ì‹Õ l=]eä„X¿ˆªË?]„˜ªiI]E~µ0à…^ éýòŸÌMìOЮiJ/Ú”Y?ÏbÖDñ¦zÂ>b‰¶ïÇ'Âb¿@›Ã-LimE¨?ùæJmEe]ã¥â›è6Q°ÅX›Mžm”€“ûSSK'b[+#¼üâ}BC¥ò°0fl°t¾÷ê¬Çòqƒ=£ ‘ÿ?2(ï’ÄC`â0#~µpŸ»ˆ²3Šw°b¾&µ02ÐÀÆÍÑÎ%¼üb†¤hXFÇ1»éI(8ˆ'«@÷íóª[—% —J Ôª¨÷>ÉŠßw]l¸óÜàGë6,þ|y´—ŸdÃúÄuI–,pLvýþüœ¼üÊ*â<Ù¦ÊÈÀENžæÒ”A5‚qÀòsˆT=ƒ2n)ÓËÛÑØ‚ÁïûmcðšXÃPl.í4˜Óì ,‡È‚ö!,ÉdT{O$ñ:|Bñ”œÒ‹ë3ب_JƒStº6¿±×*Ü­x„”ÖïË<˜™-%^yW<™;[Yã.{Ðè{u®ë𤱽äêdAÅ®‹|e)r_I+ÓpñS ³A­»ð,¡Ï Ð%–p¢ªRoâˆÍÈFûØl¯aÚp®ûgö9¾ØaMøÚ F¾ìjxÐ"9£¼=`ß9Üžàé›(ó’äÝÊ:[Y~¿öd;º€Š³ýX<»s«B¾0u\lýEHí•ïn?{,¤Ò Á4/òKÔü öÄN&Øt!ÞÉ’?G-àú5Œ‡)0ï=PzC!sÀ’"©êÜÉ\²¨]‚ûC°àvoÉø¥²f~•×­ïnHn~8 æˆô%Œ²ì&Lÿ„çcòO ¯ÀÔ»wW«^¸*Ýæ³-,%*Öc]™øâ÷¾áE•{ÐÞÌ}R°ç]‰= v™ºÊr¡Tü“·ütKßµ.½¢˜¯FŠO1ô˜¬ô Šú/ô§¼o endstream endobj 76 0 obj 3976 endobj 77 0 obj <>stream xœ]Vy\W¶®ºªd3P(Hw»¡€àÅe¢ ¨€QAö¥hD¶v `ÔÄKÔ( / › Qš°*¨àƈÊ7Œ¢“q’ Ñ‰zй$¿w«;qÞ¼?ª—[÷ž{Îw¾ó#¡Œ (‰D2Ú;:>-:Eî⮎—¦ vaœ`oˆ°zøæ°FjO­,0C¦†ÈÔèĸÑRKøÃ*GÃÖ÷(C‰$c_Þ2u’&Y—¢˜øá:GgçiÿY™éææ¦ˆÐüõFá½E›¨p ?Ò¢ãÕI щ) ËÈîøxU¤"6^“·E%[½I¡TÅ«’’ÔiŠ©Ë³f̘éB>fù©"R·(·(V*Dÿÿk…¢(gMb¤¯»:ê#¿eIÑþ«<“c•[â>ôJQx§®ñ ŒOˆP¸Ì˜9köœùDùS”+5‘ZEyR ¨éÔ$j5¥¤fP“©™”@ySS¨5”5›šJRË©9”#µ–šK9QïS¾”;åG-£$”9eAYReEñÔXjš2¢VPû©HÆIJÌ7Pä úÖÙÅÕ=–ÚIó¥—h?ºq` ˜;Ì¿Y5[Âþs”t”lTԨƜqšñ]ã>ã& &¹&¿š¦šjÍ>€*s¡ «‡†ß+–2AËc£{l"ØK±+ !#פû˜Ï˜õÑQA™»rjd˜’Ïòö¢3¨áÐñ#ß–•Ý€äanŒ¹¨Dp“ÄbËNP@((l¸æNbqDKþö1\ßæ†îŽSWÈð÷dåL|º1ã”ä)ãš0Ä“Â/ •œ…¡P?òY0föwرx®Ëx¼+&°ùñðùrlKk<ÕIˆõ»ñú‘–ÂjyÁ©ª‚:tH*ð`͇abÉ-p\ÁÉâ…›<،ܔ‚ ¼pS:rœ†pƒ­Fúð¡OŠS~O o͇cXÀjò¸…e?8A)LÄæ0Ɇ{Ýo è'èrA“¶I[y]E·£®¸µÜ>[Ù€®£óÉm5aÚõ‹‰ßhpÂ<Ì£9¡G›¸6&Q)Çóhla^4÷úNíF_¿èîrìEÿBà èAÚ…³~µ¡Þ¸fìyaLT^ÖaTÊžª.n•qƒ]èë-9Aì»@GÒE¼YKô«k8'žŠàzsSç©ã™©²<Í(ŽÅu}uiZbªâ+6|µ)ÑGê¨U,ë%Jù}HøRÓ°8Úpl…–Ð7+7ûíFYûvÊ÷e ÊD^R‹âX˜{‚NÉÏþ Uï…M{¸ÖõW×_CÿBWÓÛâj6ž)ðE숞c³Œˆ}¦Û{l;˽>¶=7Ye‹â²Ô[·oÛ‘¾' ýYqŠÈ!Îl+Ëp²á2†ƒ…qßµ/ ±1ÙG[äpŽtQ¨"YØ[|ƒ0§K<©“6‘öôp‚1BBTvz ˜‘2•4ÃDh‡‰†ÂoÂumoŠ­ j_p’È^æà†'`é•50U-•þk†VVõ ˆ™1ùÃÆ{dDpsñŒ·ïƒáõ*0|JJ˜YÝí*ä˜.U”Ä— ¶±—ZÏô=o‹\zP¦Oi'AñK}'hÅë<áñ]7pƸ*°“˜xÂèÚ˜‰³ŽN¬uCE†Pk-hI?2\óŠ á^þ Õ7eð+‰Ñ”Á&·¼€¹Óz¢£NÆe¬`ôœ|H°€OÄËxa)ŸG†Í#ƒb ‰€‰½©S¬3á«þ~}'²‹ujU%Ï!oG²™ÙÂ]\64<• Ù¤ƒe3È­tukh[pßæ§è)ê«hkim*½ŠúY!›yg^Ÿ ѼB,}éJg–³AjYI¢ó{1º’B>¹ìÓ¯P% m ,¹Œ°Ã¾Ø"[vô¡ËeÇzÛ~B«ÉÞ¶}—<# !ef@Ö…fÝä„Bò£Ë“7UËh±ë@—*Z/°\RïÏÊ߯Â4/†žk^âñqby]CiñÕÈ'«YÈcÀŸç{Z=§8y*=>îz©ëÂd¦…ï!ì$pžó¸ÛA¹nSá(™­Äi(JÌFÅpÿZ¾)lUÄšŒÛ#lQ8ëëÀïžèǨuV5uÔµÿ„` ëmOU—»•Úù¤2¤F5èdzaÌÿ$^L@_´ka–z[xBJ$ŠAꢭ5;Níº‡ž¡Ç‡9YÐxêx‘¡ 7ð–ŒMÙâíÙÂ~d™Æ>reR繇asĺŠS°Øv5@“þn !â$œ ±µwöÊð8:O˜¤‘€ó‹ìqlûÂoäÜà­úÍ7mÁxö=l¿ÀK½.R¦ŽÛŒV²Ÿ€5“S»¿èбüªê¢zÄ^®V­òˆˆ/ÿ„Öáã€H›€¿ë'ý ~©àÝhœ7¢”bcÐâ™$I3ï2x#°<ö¥Á.Kÿ `Çsð#”ÿYÂŽX0#ß$Ž!ÁE°æç=tô˽8y¢°ñ¬-:?Â6xŒóläUvC%ã†ÜBUól±í+8ÂÔ¡°}¢jwk’a[Hå¹×£×†Fùù†~s©½±¶Sþ9>ÍsC=!K½B7xy…5÷Þn:Û%7O)´Å°¸8¸Œccoƹ¦¦0þˆ©Eý/™Ç„þ endstream endobj 78 0 obj 3354 endobj 79 0 obj <>stream xœuX \Liÿ?cšsŽThöÅÌ(—¢‹’K(tQÚ©(E‘¤Ré"•nʺ­]=¬å-V ‰]T®K‘Ü-ªš2¡©5‘—ž¿3ï3½ïûœÚwý÷ý|þŸæ3sæÌù=—ï÷ûûþ~O"Ê %‰ }c“2c3¢£„ov¼¥ˆÖ.^Sþ9Q—#N)÷ÿjŒŒÄÈÈ t˜™•)¿l0ÜÙƒ(±H”½m¯çÊÔœ´„¸ø …MHÐÛqãì>ÝqruuU,Ëùï/ ¯Øô„¸Åhr‘›´2596%cšÂ“<””­ˆKÊIOWDÅÄÄÆa¡QI±‰ŠÙ I ©©+36ž¶ çñãìÉ›s@Bò²ÕéŠà¨”t…R·:)*í/7)ŠòWÎÊI‰^èï‘»2&,À356ÐkÕò¹Þiqóf§Çùd$û®^1NfbHVRTèçÙÉËØØ*ìì­Çrrž°Öeâ¢I“#¦¸FN]:m EÙSVT8HyQ®”eM-¢æRÞÔTÊ‘IÍ£fSã©QTåC9Q£©`Ê—r¦ÆPó©9Ôʆ ¡ü(Ê– ¥>§&Rc©”’šD-¤ü)j2eG…Q”'5…êOR"ʘ2¡ÄÔ@j5˜¢)SJJ}FEReFM§Ì©!”;5”² fR–T5ŒŠ§™”%#Óí£ZEcDQ¢ÝýÆôSöÛÑïX.öW 0XapC"–„KvIT´ˆö ›7&йDZkØ·ýmúûöoêÿ/CWÃ4Ão Ë ïö p:`³‘ÜÈÃ(Ëè„1ki\d\oB›ô7±7™a²ÊdƒÉ~“ߦ Ü9ð÷A™ƒîú×à°ÁYƒµ¦Ÿ™:›VJRgi€4Fº[zŽ›ðµH­cŠE­Z1ÌÒ%s=É|˜ëi#*p?¾XħÀJî1^)ù@ã]ºd 6¢qQO²äït3¬”ÀßTΣÁžKLø¯qŠZ÷‰—px”Þ[ðvg¼õ¥‰SÖ.ÏÎGC¿@ùÛr¶³3™ýöu•¡ò]÷9°÷ླ¯3",«„Ÿ×_lÚØìþ ¾i3—ži$JÔ—<ƒ³Œ´é×_Ü?­”á·ñ ¦ÝïÚh·°d™ôŒ†!ËÈhæíšEÇÛÅ|"¼ã¾º¼îǬò¸je¹bñGl€=°[§¬`pËcø¬P>™ÎŸ¼4Ê ±Žóà 0¿ªúµñü2By/:/²#KÞƒ Óx.®ÂP%Á6cÀ»õ† ׋á:,Â×%ÀõE>Öð;5b>Þ ¢xèA£Kƃã{â$Ý@ æz’5<Э‰0¶«!OmÚ¢…m€Ö\Ê·˜šn@—Te¥OU_¹n4ѵ§X)ÿøÉ«,PmÖÅØ±'Ãöy“-VÓZü€ƒ<ðe® òM‡³d}—Œ¢QL~Òª¬´ì”M ÈC¾øÛ!‹¹€Ž­;˜NF>˜±'1ÎÅ‘¸:cuzòº%ˆ%X¢&Þ¤IÄô–pÆ÷õF’™¼{V1ð3oLx¯íüT;Ô¶‹á0ŠÃcœ­°öÒX-Œéü3aŽËR^X°ìpšÀk"ôÃr¯ Dÿ’-îŒôä*¹—·¦âÁx@ÀÔñæµÂ@x£UÓ‹|3øÖƒK³èÌ 1_k9Ôú奼ª„Ӯؒe¶')3ÏüuŒ£¶z KHÊdÌ^ç‹BÑ’C+ÏgÛt¬à »­žÛÙ}óî3ĪïúLÜ‚¶l!)t ŸJ-¿—`?T—ÃáXl…p6Îò Ë5-e57å-.‚Á¾Àr°Ç+d®àü6ÂJìeE>°£5q_ðý@>äKjþŽZÔ¡å‡iÅfp‚%H@Y‰ @†ýåø­í±äø;0“‘OÆâ¬œa‹GÊÿ¤È­™ßH•^ÐåÎòf¤Íu1sͶÄ2',!†áþ dM5G®WÉñqÖMã:ˆØL±ÑŸJ3¾­~)7ãľ|êÁ²I¤iÃn’8.júdñ÷•;¿Ad Lööu[së9SîìíÓ¨_ÜÆ/ngú\÷Œv©E¿j¡£[L6s‡ƒú:º°¿êäùÓ.¢, ŸÚŒGÊðÕžd-Í[À.e:jM›ºÈYÞg!ߨ!_-ê$¾þ9<çøjýÈçÇ«õۈūõ´ ¼ ËôÐÝ7A™K+‹-,ö0ƒ¨>]ÎJŽ8¼ãÆVö9“¿mÓÖu(-K\äÆJ+^›­Å)0XÈ|2Hy;œ!£<åÇ“af0Òî;Q Ë}-ñP'Ìb÷‰¥ÞçÂäU‘×VÕ¡ûè§£ﳩ òÚ°43euÒÒœ…(%f|Ÿ½wãw›³“è]6Ísa R¡‡Oœ=}iï}Y2¸gûsµ3°92cÂøù½rMÕñ¿JÓ­*à웞œèBÀ %z°¼œüýEzoí;ySöÑE‚£`?üÈiÖŠ‘h(vùIkŽàøÐÇ>Í#$€(Q?ÿcx§ÆïÆ µ>_ æÏ*t±6BäÇ›pï}n`c’P&Ó•c]ÏÆÀ d¹*ûF~i&оÂcIlÑÁLÙÚ½_îÝ\ÁN ·cãúy0œdög÷Þ©"ÏY–O;à³/å ªzéÜñú§Ræo“ýQ0ÂŒbQc©—âFbW=Ém¤V •²¬lC^±ìPnaZÎöUL²nÔ¬%Ÿ‡ËàXŸÙÁOBá1ÕjwNí6—~„ 5dÝVí$—ò~hÉß"‹’‡‚¤”Ž?´éÈ–î0Ê>5ý©êÓ?^n³Óé ØZ†;z…©0 Õe5)@Ç6–fÎ,Z"Ù‰‘16²^š”%P¥†2µéy-¿VMœ^Æs¾î iY4±o³qöxè,ß²‹Ke‹®¤´£N|~Cpqk}±ì½ l°Ás< 9#ÏŒ Å“½a Âæ,΀áx<„ý½¥¢åRJÇJnµÏhâÇÖ‹ž“ŒÜI:FNÁ#½|~Ñ+èÐSÉ÷Š‹·}{TÖȬûzmÁÄÆ­/¬~Á˜@Á6«ü‹EHìX;E_ÒFë’ &[=:%¢¹w*ÓKš9Zx£ñÑ 6¢å÷p`mÛ†§¶]l\ñç‹Á¯Óä0g‘DÚñ2ýìò “´:%+bÝ\4…ˆ?›úÃ'·^ Äoõ-Š,®ñybÔŽ~)¾xªºêø=tuß·)ßW‘6O8”xôšÅ£{Õo€½j·UÖÇ#‘å¥bQ[oHÌöQ&~®Õ%;àzý¨'^<r7£)LØ—é½6ØN¬/›Ï4ãK¦àFzAìí2'êô]ÏLÑÛ0.÷‚k®.k¨–I³=@pÊ#ݰ^D‰Ô„¾ ›Y›»ðk„¶æÊ¼˜Â-ß!¶«ºò¹Î˜(ƒ)VmtoÒèhµ¾#)ó%ÕúWØÔc©¥{]S(¸o»ÄJäkOV*$Q/ú"¨4ƒ¾DbOc½Vðnä”úÉ–sƒ4Bhí/¥Ÿ¶‚7®¼§IyMÊꉂÆi8MÒ[²u²bÑsm/JÕ$F¿‹±õ ÄýC"‹ÇËâŽæV :–ßå@îc›ßaÌ€~]0ZÆïêKaYüe3pè]–µÞn$iuGÑcôv ráHƒY]ýŽwü]ï(éÕ‘°¿úgb°#óMîI&ê!<4z7ñã›M϶CQ/?T“ ´eÃWh³eÊš½Gäp›ÑúÔ`n†ruL¼,=u}ò–ì3zçÏ•å*Ä>>»2L¾šAñ™ys6büœ¯×¤%-F>¬4Ûî~àï÷kK¯Þ”í=’~íGEÛÊv’z>Z¹17-#!iÙšpÄúů­«8ÚQ$×ìù~ûÑ"öSß;Ì v®ß†ÆKô=xß#CÃ^ÒÿÿùH ¡s&üƒä¯Pè„ûѸ–÷‚¨é=-ü[/‘ð"=-ó‰Ð pˆ/Ø!PjŠƒ°)IìhðÄ…ØK^Ñ0"ÀGHºûzé?NÏ Ux™K«{•Gª•š‘>}Rw»þÎÉX_în_oŸŠ™#|å‡3]!Wl½£2ç†Ë’®EòAÞ(bÕ%+­~Äüµí¼ÚWHc•wµ×??`¤Õ§kSš,AÖIÚ pŸøË<¦Î‘C9£q §íë©üÿÚSýÓ«]žøü¬il¡ïÀSù‚žD.‡“Kk ”¿Ñ0™ Óõ¯‰N^èŠEMOŰŽ,b 6Wb?„v¾‹ýÞ«£'€u8LGà‚`B5¸iÀŠý# <ŸŠµ8ž{ æwÁg%øsv>­ÁÖÕÄHgŠ'„c· X;N^xLeG±¨ý£¸ýg}+‡A³×f-ÞÂÆ2'/¼ƒÉù°é¡*à‡7xÂ’® ó.]Ñ>Њ¯èÂ8,fʰ…¤Š. ˆ™fÿ{GHð¢öb&³X2>«D·,:‡l¯ïY/©£a½n}ïy¢‰W©Dç:¡¢S GÉsP`Êòðÿ »^ߨХ`cÕ¤öçƒlõËÅ?Ë–×Ì=ê‹X7ƒWÇAEDØÛ:,~¡zñÕ+ˆqv#D4–4Â’ÆíÅ¢òˆlá] ‹t³8ww{ûf÷.msóËî™OÆÉ½ ®_X4?bÉüàˆŸê®ýt¡NÎ;tqz9aì>ÍË «Ezs!ÎAˆ{©j~ùúS\HoÜ…kuB\o…Ö‘Ã4ìшa¿ÓoÓèÂæ1IX”•‡‡å²JA‰¨?Û,â];Åü7휾?½ëÆí—ß^ò÷«—›H!¸™^UU¹à{_ä„|cÓb×Enñd5ô7—v”ï)-=÷Ó‘ZÄ¶Þ š¼b‘2Nî¸ÛL^ê»Ê'õÚ JC^³éE uÎî$ÇKX*n7]±U"ýØ]9Éy~¨ã¸Å—Þ)'HPáòý‰§|ã»I2º«,:ãk&•“†àþ‘k~±“©MØ[ÏpÃã¾–iè­ç¶Ú}°¸êü¡:Ī.FÌ ËIŽY#O_Ÿô•9Å<…’ú“Dü¢Z1¿ˆw䶆æ­^ŽòÑšo³öàþ Cr ³wä ?»p³=[Ko+ÝV¸½°ðàþ¢ïQ!*ÝXøåå#kŽ­?ŒØŸÏÔ4Ë÷õâV¶-¼’ÀÛJàmw»kÀýc‹¿ÔÁõA®´p8†4¸+é¢]Ží"~;•HLßÕ›IžÒgªOì'«uËÏjÔ\ÿqî‹«Z3 {æ®,Yu:äaR+ÁÂúí ‡×xXXôÚäeò£.K‚¬qüC]åCÑϤ}#H;à_³ àáH]åû?¸­j+y¡ï€sdîÍÄÇrÑ€)䆼c£”‘³³H’rø~ÆYeeÐ$•pºë~ râ¤ÅRïù© bå[aÖµ×ïÐt.æ;–˜ÐDîyí\;ç@¿©®þõ¿jnÝnïSÿ†ls¿°Å^Jc7üållŠóÙ.º ²:`¸@ûié`Ú&.%>ÿœ…<ß÷TŽücé?<‚t•iE§_'7sé{Þ’ïÏI»›Î:õÀÄ.*bçƒ'¸añœCq—È¤ï§ÆDÏu³Àfoœ@Š7ÀµDßt­’av“ÀÇ—"gÏ œ5+üüƒúêó䏯@ú^s+xÊäÀ`§‰×ÛÚn]ë;¥ª HwšE•pµ0‰ÛÙ'³"…_*ÏܽÐPÜŽ€#GÈœ'Qwc®ûÕ–cÇ`9ÒéŸ=¨>T÷“¼Ï 7ù£¥Uùõ,|ƯàÔw|]ü'N ½ùªóÖ½§ÂtØ«FÄ?Ʀ¤½ ã»{;”ÐðAýˆ‰­è31¾Ä ÏË^èbˆü,uÙœð?![l#¹Eÿ×Zù)BÐG1$¦…Ç­pÃÿg´Á‚­^ê_údª§ÿ´ÐŒR¾Dht”¥´Ê°m€ê[#£ç»ŒŒ)ê?… +Ð endstream endobj 80 0 obj 4909 endobj 23 0 obj <> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 81 0000000000 65535 f 0000035342 00000 n 0000060471 00000 n 0000035196 00000 n 0000033254 00000 n 0000000015 00000 n 0000003212 00000 n 0000035390 00000 n 0000059625 00000 n 0000057653 00000 n 0000059955 00000 n 0000058077 00000 n 0000035431 00000 n 0000035461 00000 n 0000033414 00000 n 0000003232 00000 n 0000004520 00000 n 0000035502 00000 n 0000035532 00000 n 0000033576 00000 n 0000004541 00000 n 0000006482 00000 n 0000058576 00000 n 0000056679 00000 n 0000035564 00000 n 0000035594 00000 n 0000033738 00000 n 0000006503 00000 n 0000009776 00000 n 0000059240 00000 n 0000057208 00000 n 0000035646 00000 n 0000035676 00000 n 0000033900 00000 n 0000009797 00000 n 0000013269 00000 n 0000035730 00000 n 0000035760 00000 n 0000034062 00000 n 0000013290 00000 n 0000016074 00000 n 0000035814 00000 n 0000035844 00000 n 0000034224 00000 n 0000016095 00000 n 0000019625 00000 n 0000035887 00000 n 0000035917 00000 n 0000034386 00000 n 0000019646 00000 n 0000023608 00000 n 0000035971 00000 n 0000036001 00000 n 0000034548 00000 n 0000023629 00000 n 0000026953 00000 n 0000036055 00000 n 0000036085 00000 n 0000034710 00000 n 0000026974 00000 n 0000030064 00000 n 0000036139 00000 n 0000036169 00000 n 0000034872 00000 n 0000030085 00000 n 0000031498 00000 n 0000036221 00000 n 0000036251 00000 n 0000035034 00000 n 0000031519 00000 n 0000033233 00000 n 0000036294 00000 n 0000036324 00000 n 0000036378 00000 n 0000044098 00000 n 0000044119 00000 n 0000048181 00000 n 0000048202 00000 n 0000051642 00000 n 0000051663 00000 n 0000056658 00000 n trailer << /Size 81 /Root 1 0 R /Info 2 0 R /ID [(îÛ\)u…fhƒmg7_p ®)(îÛ\)u…fhƒmg7_p ®)] >> startxref 60673 %%EOF simh-3.8.1/DOCS/pdp18b_doc.pdf0000644000175000017500000026171211143604443014020 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[Ûr·}çWlå%;)ïh€¹çMFv¬H I9qÉy Hв#‘²Äuâ|}Ì}0s°3”SË¥‚0¸4ºO߀ޟ7E®ô¦°|ãâÃу“vsýùÈuoNžŽO×G?uyiÿsؾø°ytf&ö]äEµ9{{Tä}ß©¢sßÕF5U®šM[¨Ü|þpôzû2SyÑôZoŸd»2ïuÕT¡³Üî²Îû¦Öí¶Êv*ï+­ºíƒLç…îšmKúzÒ§¤¯Îª¼¨[½Ýø§„}×ÛÕë¦Ùî³9CY÷e·}ï?ŸËrwU]moaè'×6Ͷ”^ ',{µý,+Àb×°ÂÕØm¦ÿãìÏG]™7mWnvºt¬½4¼+d¦? a˜ù^V]åùiw»’ÃñÍ;¡\Ë÷³ﲺn,V´ª6“‚h‹~”li$[UCc^6V´†î³ŸÌ ¥6}4£Õã”Æ¬Ô÷õ¦1S;™¢T^ëÍÙ3ûÏß»-ý|åPóìè쯷-õºk[µ}‘íšÜœ£¨ pJ+ʪÚ~Ÿíú¼(t¡·'2ô›¬È˺iŠrûTf}-ÎÌ©›¼Uå{û|ÐUuجíq,¬ûXÆÔ¨Q†3Ìaš*3Úž ~gn•®Z#CßÂÞš‘ÊtW½AŸÿþÞˆ°Ue_AëÖ 4ÈÚþ+ës¤¶6hßµ¦«­õö&L¾v­RÑ>fhX£+·¥ùk¦Ú\äÈ›–ogVöûÂrï¤y'dÃ&72à–Ž‚AW”ÖséýˆÍ@±p„µ>ÛµaÁ;iÞNw,›ºAáuaÀ©ÅD[ôÿbšEÓ¶Š£ª,¡Õˆ­+cI*£2Né-uØeÔìåxFµ t?ãÒW²%œï gqp# CAÆ£ÜúD«†ƒÂ,ì¥, \RÉ_ÈI÷ÒûÁè—1Qm­ðd7€BØ zå·¸@úÇA²æÿQ;/‚Ý`.GKU6V½k×mµ×ŠŒbªvsé㼘PÄË%¼}DŒ„æ® ¾å4àŒ¹B²]ð,ì»r½UÓï¾C³tM¥´sKbZØ tu h‡}cW™ÖLV ¸€+XõÍÌúiÃ÷Ñá4}gDåT¸4ú]”^…ßPÎ…wÓS´Ô¢€€ÁÜÁääÿ4ßlâ#‰ˆeð±â]ÌÊ&©Jsä_3ÝçZ•uÊö"ÛÏÎöÃvbµ~ȤÆ) Ÿ^v¤›BY$ù¾I¡y2šå>2»`:WH2ˆ,–dqjb–XˆÌ<¥…è" @H$QåMhÙNuÞýõ#D \&¦4L‡/ÜWr¢¹>iäÖ5N™ÍÖ ]…¦â¨ˆD;­ÐM"ÒöÀk=z0àÇ9=Ï5eØWtÝTDÂïDÔ¹êCf+,õqÉ,ÍBFÕU\Iî(‰cÖǦjÞÇcSpòœõ@Êh¼tßr,J”‡z¢’À"“öµÝ> ÌGÛ:T&ÅÛú–ú£1b7™ GN* f€»oXWÌÀ$]?G‚ÛdQIå…Á¸µ÷™ÒÖQ·h›DýRÑë ÀY4^Àmà{?}’¡R—û/f¿3{õºm”±÷>8ÐUPMÁÎ+WÅ42'x½ÐŸrО¥9SùÁ€³÷‹".„ˆslý©\Ë}­Ð— ££BˆÿÚ/dÃÁ>¡w’ˆ-D:œ§qwt”ì}1 "Q7Od¸œgi¿¨¸ä{ªÌ4e¹ž£¼¬Àh!ï˜ñ¼ j¾G?ÎHÊ)ú é Æõ‹FBR@f&#€:%-jÛá•tª4ÝHÄ+3CÓ×õÁë׻Ϙ¥ZLû#yàfÇeøÌë°\FÇfünÛ5¥Ot‚*žÞì,û6'ÁbA§G¡¡ßýœí>´ÞE> /dšTKjuÐBˆö‹¢ü<øÐµéýx-ÀÍÏ«nŒfw;V{Á=< ýD3:Ù éÔ§$ƒ»yŽÙX§É6kC.~¿àû*YyÙ5I‡E.)ýÕo¿j[L éäYÀ’Å2 X‹]PòËÌ”‹§G¢^ÏÎ5@fÂd{í ¢ƒ=!îLIœÇu±Ä]Ò½Râ+#zâî§É6ì²§ µ÷oi»6J¨1¦™Çï‹Ðá׸\×a÷.06²l¡ù‰ÔÁ0Œ~[7yS¶DÛO3‹V\¶Ëe?{‡Ø,«O+j¿>×å>›+=UÎ…ì˜ÎnÁ^ŠÚ ×à÷{²:§Âr~I?í\LòÒy:¯>6õÅÌéŠw¨”'ÓªÜbú=áž’–/~£¡†µ ™xa2XÁAñº¼Ô^ï.¨“‡ÃLÑì$Ò"þxuÈjOZÌN„E÷Ó¢•O“õù=Lò$NËRA bíÕD*è’ )'œÄ}ÆÀ&‘VÝ$P@n‹Þ‹Bò(¾œòÆyô†Zã1Ý”ä¥eí%„­Îo#dîáóaVYø25–9, €š…ãxÐh*þ”í*Ó´Fé,kálÚnû7ÓYç]STÛ‡™ªmÆž°°Š!Zþ¶°J^ NhíÄwRž1,ån䟰]Çξäf롽ÑDD‚èv8'N«¼êûÚ2¢nó¾T™©´aO§ËíÅœäUpãQ툟o-]æë™«+|„æÃp„çÒ k~æu}›×m¸7äbŽEëû\4Œü–Õ(À¤'BÏ»GÊоˆ<ÿ.¢PâŸ×d䩌–s_)¡–8I.@bC‡¾ÀóPå0m QÃ1(5(©X+ÿùËj*p7”’”n àÙˆwÄÛ13†Y_KïÃ0j΀fùþhjAHÒ,`<~ÛµWø"…g8—ù󉹖ÔTÖªs¥@CìeU– ü`žšÜq^ƒð2 úU„+oþ^2à,ÎÁ3Ò:d›d?o啇[SA”ŽzNKAsKb{÷'KcÁê‹Hc,‡nŽÐœÂЇÏ©Õö猼aÚú]Ìu®ç³[9;ÊìV€hòÊ íÑ@E (N@²òƒØõ—Ìa÷¾e«—傃ø GZÊܽsçAë0ü JWQ<0’Ok‘™Ì  pˆ”(ã¶h9AÔyü<¦C©4xuî¹¥Y Ž_dµY¿v¹`8õTœcÔâ·`L3ÀÑÒø.¨Ò4žyШ˜Å,±öjé4¼5>=ùì%t 6<…à=~׿R'úÂÄã…«_á^À›ÆgÐvPI´°ûPnyIÜ °üŸgxKŒƒx`¤q=¿¯*ê÷U$±|ÍnÖ¶¾@VÆü[î=/¤ ×sp;É9§ÅsÞ+¢BU¶€±è¢²ÊÅÊ:^Ò³ør={P\|SO ©â2FåÍ5¼0ä>Ï÷QY>ºìÿi},{Ö¦E°)%Å5ôWIwïQ5Æ…#0/>«hòìÐLêeúâúŸ•…½¶>gñ—+·³zúZ­q… ÷ü]Ú µe®Cý °.rˆó7nï‹~ ã¢J”ºÏËjVˆr|vôWóç¿•.endstream endobj 6 0 obj 3153 endobj 15 0 obj <> stream xœíKsÛ6€ïú:Š3Œ÷ãØ8i›6¤¶2“™¦‡ÔvÝ´µóî4ÿ¾KRÄ.¥U(»²HÉŠ/’ À ðqü~,…ÒcYþ4ÂÙÕèè$Œ/?ŽªËã“ïæÂ‡ËÑûQ¦üW] òÙÕøá •'‘üxöûHŠ”¢ u©jìü?¹qPRxàj4³?k¨;k¤Ÿ‚°¶Tøe¢Š©2¨dAyª”·PÖä´˜Z¡£¶aò¦PÂ…÷¯Ši^†à&ŸQëï|ÿuaE²pqò©˜jáŒ3~òŸüPha|’¦¬©¹ÿm1…ŽòÁ9OªÂB/Pÿ#´Ï+¡½™,à~‰ÞÅ»]tí«¿Î~i-…,ÞìÍö³ÏâRGïXŽ”ð1ö:t¬²C•Ò"X715׌ @òŸ¦&)dO•±î,Í™ ;¢ø}øË¢Ÿ#ÇRx­ÎWUô«H|‘Qþ‹%TÿÌQ½j­•0ûà90ÐnŠ ŸÑ}`u¿€î¯k’Ö §Â Ò6o< m…A[Y¬Êx$NŽ+ª¤é xÀ&á}'C Øþ‰ýÎà µ >lÞî;j÷”xDízºo3XvËXŽFH°a:±¬9,SóÃ$üÀ[²s¸Æû—Ùü%ö1=)•ÕÿTà`ýòúh3“’ÎÊ_ 9o¡GÚ¦t¾ü±HBzt£'A\ «”ïýÕ¤ºr¿xcLÓÝEÄWèé(ʆ1WnŒd¯Dô;CäZ¸ÕDÞ8ìÂì¬1ÂÙUÞ>á`Güz–%0¼MøG"¹WÀ/«”I”9ç­¸C°'™jGxñY15¦VL—„H¡ÿdx!3æ•*Ãðbl8Ì“й½yÛó@¤Ý$þpHÇ@/.COE¡\ZzäjÌl{ãN:µªàÞ¡xÁ…*Çe3b6®*€1OXÄaì¼³ùU¢©ö¼p"/]Ù*ÍŠÎÍkRFÚÊP Â;éûÑÛÅž7ÂH³}ïÀÅ»ïÖ×¼%ö¾Â/ó;©[òKˆ_¤ÚÏùþ5ïŒþÁZƒsl•­iq‹s6wÛÃÜE†Àµµþ4ÃÒ†f8†5ÖŠmZALŒšº›l¸Œp #w¬ãyM ×`‹$£I`¯ª¦)—¤ÙOÈÃÀªDÔ:õ>ʆ#Ö9æ˜ßüJŽ«öé[гóyc*%¬ö·Ä¡í‡Ï /œöð›ñäÛÁýÌñ½Ê†ÿ]:³ÅÑ4H±ŽÄ¡åf\$û0ç”)D™\¦š¶"¦5R©,ÕGµ§9Ï ÖKd“žTŸY2Îz¼4 Ê{¡¤µCt¤,~À%eŸbҽ؄Kê¡æÐûxÝÔlÐC½&RÔë¹{N¤®É7 ˆv·ä›çø¶*¢¿d‹…É43‡à…î“aRª„9ÇX»P…ÆÞþêÄ)ÀìÇ ³’`.@—zÛûØâvÛ®‹{c·© ÛmãÚ7h‘¤ä9gͱËâÞré.*IJlÞf,Àòí6.¬O­ìqaŸàÃ/3àźÝÚ¹Udøe‹ªÚG¡lÿD ^ŸvfõÛ½¦j¯Kn oôœh nÌsRõæá©…Q ½Ý Ë-L;a7GkE²Šì„nNk|7‰jÖ"ëÌÞ°˜Xa=a¯WY]ÍRµ1v$±¹ÈšŒ…,ç½[ž±£m®ÄÝ$ WÜš¡u×äò°µ—ß:Žrľù’QÁ¯Ù ØÑÙ’²ìýÓŒ%6ö†´ðõá"ª“\1¿ø\@]€ ­Õg?e·¶2~L2©ÞÇèþÒíºAöïðèfoO7ßaÃ=DŽXc‰Å’Ë¿™û ¾_Míÿ4W›sÛæÜýý(w/Ä{K¹ÐA9J.s¼«Jõnââžr.*ÑkE¨Vl#¬j¨F«­rÚ Ï7!ùcÄ‹Êú¤ª#Né*‘G}ÖÇšÿ£EH*ýDÛŸ‹ªß¡ºµ›µ3F{ºþIù¨€öy …’¦V.¶{ÇN¤Úá9ݼ Î9:æôñÒ<¨¿ïÍô;âæ™<ŠÞ䛎‘ º$yºF" as{g­¯øb«`¢óõûë‚5p@Có }¦½±ò¦4|ùû‚5"‚0phDìðwbÏ»zbùeÈñ`Z瘙0˜ã|ç‡R4 LÎŽ$ë0¶¼Š)A˜w\±„^_3ne©äþuWðhilÛT KáA-¸õψÜ(¶¬‡7³­ã"(Ú“þ½éq‘C1ö˜ãw°ÏRrZ–œä°G² š€©N°•Þʵ] Û»€D¿<]*ºæ¨\{'ö<ê³Ð0±ßUòû!Þ«C¯‡¡…Ä©p®ú+eo¨8ÿCÑ•Ö[k%Ú:úÂÂTÔÿôÅãÙègøù¯u?áendstream endobj 16 0 obj 1962 endobj 22 0 obj <> stream xœÝZYs5~ß_1oÌRÙ±îƒ7á U„°@Qf×N¾ðÈ¿GÒì¨[³=;³¶c’—¶Vjõù©5ê¿*ÖpQ±ø¿#V§³ƒ—¶zu5KÃÕË/7Äå«Ù_3×Èø/ `zuZ}¶ 9¯|ãMµ<ž±Æ{ÇlË•WF‡¿½®,g Ng?×Ëù‚›Ær#ê×ó0Ì…²õ›¹ ”ôª¾š/lÃ7¦®æ\4J8^ŸÎB5ÌJ[ÅAë¤Çƒç™Ñå|¡ãráëÃ4(³õk o€D¼ª0*µ1Lâ¹ç@®‚„amCYB6Õm¨Œ«¯]T ³G?½öG@VÁT<òñõça—F8§týl.f¼RõSDS_¤ß­·qWa §ÚAË‚`![=Yú¬Â*>ÊʦEáwY²Êä«_—ßÌ„ñ’>DÌrqõfò¶¥}|+e8¬×˜UáÉ0W„(l#W @ÐGÖIœ*uŽÈñ(®Pè"þM;W2ˆÖZt把im~YþÑW\‰è{yh,kD›†<Ø QZm¤k4wõó… £R¹ Ø"ˆ&B8› k˜S<üOº9νsQ·4E:=áš0èbÈÁ zœÃÜË«VMâ~yÙóEÁEÞ/ŽbÆG èU·Gg‹)ØctpužÖL«};[~\€`rô Ð蜞 ÁN2uD±<$ƒóFÉ@1[m (çlM3Abl¡'Êœˆ[G8„;®H`âŒThpCùæ€üôjå–F›ú–!QîABæÔ{7¾‰1ö£íß͹m¸b:X"á‘°µ¹,LJ@l¾ŒÉâAy‹ô¯ú®ª8’V°*‹|Ó÷ÉÆ|=“ì ‹â³ìš4ò°¬Ì€Á’·Qe¿È÷ãÝPI™èöå®qBt§,Qæn&ôË\œú1o¤’1o&”&½‹U¼x¡ÛØÀÅŠ:{ir’ù,Y7Ý¡ßKÂ[_œ6eÓ.!ýÑ©1ˆrú ½ú[—ÔUÞ„Û væU™‹håo…Jš2Ù›~õ´‘9 ‹·¼í?P©Å Nš;vNì$Q5†t|;×Ñ9ZïÐÎ0ûÁkwÕ«ÃVY¯?wºŽ{É?xåÄU9´…¦¨ª^&CÑ,Æáo¢Nãè:Q7ô鮫\ãf8` ¥Ú/gmh2οgïx´CÕU¯"@RwVyçÍî`ÿ{Ž¿¾àtè]o݃EOEî£{“$ÞË&}"ý€Ój:f¤_LЩ… ÏÝeï 2Ê8%ε;AÆCEᵇ9£‹»ðžuL–>Â5Æx¦\¦~ÏÔÁ&Iµhë!§U|µY“Ñ€Šî⻂êºBß0vK’‡{ ã–>÷a‹ò¦‡>Ý e@£‘ñ£ü[*P¢°JG-ÃB·o:>ŽfK‡œÎBõ˜eä½5Jå ’ÙœqÁ©~@Æ GÇ´ºò^ý >&(Øáé£×›÷êo”®ƒ¡ŽªΕéá=U÷ªeÀ§P´2+ž0¬¥´ºwµ˜À§O‡·(a‡õ1Zþ·½6åf5Ùk€S™„.Uk’9úÊK~«é]»kü{­lÎ8’‡+¦Ìo²RÇÁ¤«PÃ` ËÌÿ¢ó„âžI>”èĺg°®ÛG¾Ñþïgªm d j+q@Fõ7} j+I/J*"}Yn¸Óé)ØÇp©òܶQ$³@!d× êP‰@áa•²) #iRlMí#áÞ´½3uz†èuÀƒ4ù Œ°ž«n2E·hì¾ö?Ðwé½Ç¯Å·®ƒî:]¼‡üRÃþ;yñGŽ-_“X¡çh;Ÿ^ºód´elÿçªÌð—9é'Ô„vÙ¾m)×hh+š^òª·íÅ©¼oÇ.)}%PWÊg¸ù¥£F[ŠŽêªDÏ-ž¨×$Ô×B·=l=¡Fq_Áï7c›Êx…‘n87¥›n0êÃh›S”*šóÚ® iT÷ü½è\¿Ýœ7ÐAq•ßÒße Æ®3u”©S²o`MÌ|›©7™ZóªLeêpk?¼""Æ–°1=dÜ…œo›ã(÷²#’§@Þyä!×@™ùŠº×‘Ž³[¶§Tµ1îÓ­Õªþ±TVÄg!òHý9/@Œ* + ŸËd'CoîߤÁ‘_“ËÏü‰œ{ä19¹÷ˆE.|Gø´(P·m N¬ …žº$6ŠŸQ)Q‘.œ´QE.ûg,jÏ€\“Ö䇤¹FÝK»ášä°Óí„¥·8#wC|;‰ô3 ?%G;d!r}80 U—™z™©'DJ/·ÆÊ®(Äü͘ªÈ¯Hß’†Eº çÒÑzIr¸ÞƒÃ·L ¤Sè†4ŠÀÃÓ¼Ôׄ¿·ç/7‚"ò%b $rç¤öWä:éh'Ó”ŽÚÑHĹú¹ùsMªAƒmo…ßîÈò)÷-SÁqQ‘v¦}BóEÓ§À(Ñ@üÕXҹϩto0ìx¨Ø žïáøÀ÷K’µ¬¨éÛ>…iCŸ6GþìÛùÙrö}øÿ/ÀTcendstream endobj 23 0 obj 2275 endobj 29 0 obj <> stream xœí\ßS7~÷_qo5ø8é~ù:mg€Lh)\ÓÌ$}0ØZŒI !ü÷ÕÙwÚ=û³uwølCò°ÈÒJúv÷“´ |¶[HËI¾2á¬ßØ>­‹acÔl¿N…/϶í&ÿF \>ë[»±(}+²£ÀŠÏŽEm'kVà«ï#ß …°½¶÷šÖVËu¤úÞãâK%Ú~èzÍ8•d|.Ú³e4cTÚWDnóÄ{$Z$úPtHÜ6õõ¡Þ—$Gâ-‰ùë•Jï?ño œTÀ4âÍÐké0¢ŽÊÅ‚(Iô`‡!Ôð…ÄKˆÆTÖ…îHìä¼8õíy4LÝÖÒ¡–¬œ”·75ÚkþUø#è9G´‘µ†•=0ÌÍÖ0Ñ÷Ï<òæB‹¿%ño8l@â9ìÐ'±[™fñB'oæÙñAöTÒ¡Ý Ú ›ŒiøÍÀ:\CÿïÁV6E"g4:¶È-ÔpfÚ›Žb9üx›·PïÇ&$ÖØúq«‡.ÕG]¶k‰E8cïK8ñõ$̳Ñ%zŒµDgÕ @£ñTÛ˜Pi*ÚÃ3‰Tf8¶sö° ÜF TÖƒ0-°aì&S(FcøÖÃ4°ó˜¹#‹O#Oñ¤c·…PWĸ›œÏñ¿VÖÖzwæM‘\ƒ¤7À™P¿?LÎÄεá¾](Zpß(nr1Æ:\™,‚c—1Å%œ]·Ìa|0'Pk¼h0<ñézeÚ9žâˆ5Ð/~/¡ë#û¶œe–w7h¼uù‡yO ®-Ÿj8^Ør°Ãïâ¥Í³XÄož–9Ç]àÓ2ÒÒÊŸ–Deó´\ìÓ’m‹Å-ãf†¸­ïíå8ËzOöåÈ\àU5`ö¹ƒ[@Ä:°¯Ñ(Æ[Ž#ì.øqZéŠ5‹§o †ÞÀfüöfž7,Ú ^›œ`·z[Mv .2=2ˆ#é®úî?é1¯bäÎ`‡âÈÕCBï«A·PÿÃ3¨Pƒ+Öš°bË.Y÷ ç£ y#™Ï•ŠM¢hU‰¢ùÈ¿/i1óåÃ’ëìaƒßŽ$²çiÅ[XŠød`§/ÕzìÉÜ«8C¢\`ö¦-ò@eV0žàëšß3кæK÷×ÚMêt½R§tùÙÍÃ<7uÊŽCö”1ù9ÄàÄ  Qüd§?0…ç@W±WÕ3¦ØUŠ ;yž@›9áÑÙ}|—p6Œ¥DkOÞË|ø ÞÃ"Ãö…]## ¶øQÉ6θDeÒY•~øî±y~¡%?‡ý*ýÌØ=79ÿ:sýFø7 þyÛ¬+Á_KV’Ío4û&©?%.1©¿ŒÑžV6º–ˆ9,×sÈ@×ãtñªPÄÏ]ã[ÔHb8S†Ù“ÁŒO¿U˜z!f•šw0d¾ ³ÎºÔ¬g ){öó›}¾64ÿfÏÎv–£š]±™Œ¶ax_˜ð˜4à3Ûh1ff±!‘?— ÚÔúÊÜžoÏ5ø ¶Žõ=lΧYß{t‰ui…>Ö@ p͇áv‰a8­úü냛ºàÔ°Ç•NË”1DÈ™åÚBd"–MoöR©ÀgðIºðL\Q‹V«ŒWT\­Â1.!Å‹FîR «‡>¾ºW¯gZÝ[NUÏ´±ª‡oõüÙCÄnŠz¡Ý>Bÿ½!y}Ï—æ‡[Ží‘ôÃP‘¬:h„ôÂÄ…CÛqD¨yÛõƒÀIV£Å®îª¦U ]?j~¥A—[žúؼd3Yc/§žkK:©]%»Ò ZZd½NÓ™Únªj²C—:Іº™–«dm®ú2Uèítg“ªÔªŽC?ÙÑÈõt‡©e«ïPë)µ^éűQãyåÛ/ âjÝÒµ=…͇-õŠžãóÏoGªÔ"Åfz* TjéF|cý‘×H7°¥ïeùCâÙÒ³Ð)ˆð=¾E†×Ôþ4šÌUÿSglez[©ûe¿û«:ꦃΣMûSdÀ¯ìg-uµÔÓÒW-ý ÆÿÒ»ŽæÝÑÒ®–Àª2-#ŠšwÄ.cK¤ùwZVåLЉgMâj"HüD¦~8&‰Lb^8ä|‘Œi+ÏÊüÍ ›wzLG·Ý–ÎùL‹þ}~¿©…(+M‘[Ù ïóyŽTÐx²-ÒÀH(…a‘ì8#F¯iMw> stream xœå\Ys·~ç¯ØÊKfSÜу¹œÊEÊ cKb(Z¥*9•Zr)J)^âaYþ!ù½æ@70vö¢I%–šLèÝ=ûy”ÄBŽó¯N.¶ž£³Û­zxtø×¸9Ûú¼UÆ©ù¯àðÉÅèù‘~QˆQWùèèÃVWU™ V1Ê3ýw• ‘Ä…žp±ÆGÿÞ"ÎäèèÇ­£?½þ6žÈX–E!¢«ñD?J³*ú2®â¢L‹,:µc¿Œ³8É«Rè1OHUD7ã‰L„¬¢m=˜fyž¤zóN!TÍìTQ‘dYôi¬ôÓ´RÑÉxR´xS™Ñ­ù{D(ëçSz~I [íŠOh–+Óè\¿«¡"“Ñ×±(b¡$é¸~Iå%_Ÿ-}L˜~ÞÑÜSš;ƒ;üÒP»Tüð ‚4Ïòè#ÜÂ!`Øb .# ÁŒ¿eÁ„v:þçÑß·dª%#ϵðͶÞG÷œ†Ýl/½r‘)¾­VdôR´AÃI½A™V.½-VÆÚ{/4á”›L´ óÃ~KKÄ iõÿz sÄIwÆI«3R ÕéÄ‘ÞMnÎ#[z7ÜAìÜ–:fí©Æ©!…RÑ) 2”z÷¢Ò'•ЉHwüÔ!ʹ}>…âx$_ÐYEHC9][­ ENûìH\Â]¸gs^ø…þNdÎ79õOéÑó~ŒTÄì3—"ûk8“‡xµJ(ç¤×$çžuj\AjÌÐï8 jzmð»¾@·;ô:Ílelü}4áÖA?¨ô^d´cÅðÒ‡M€Ây„sA¹‘y"\cÊè‡,íÀ£ÇÂdŠÉ̉7ëùÌ‚ž¾2¾ô|žoŸ°ŒÌ l2 Ø­bO8ƒÛÕÐNOÛÇÁqF6‚.ã2/;Awt®‹í-[ì¥"©·¡¯Ç“<ÎUV´f°~þ/BµOï¿jŒ£30ªa6mÍМæÈÃòR¿«5DfE0ªØ²`ÇŠcìkïÆ“Ê(‘Ñ. Œ Œx|k¤È v­~¬µ@ƒóWèÍ™¬ßû ð1`š.Ad¬CxµhêOÌFïmÉõüRuÚ¢÷¨Lì*&–ŒÚg«g¿¼©Ø…­Xî€3Y™‘–F‹¬•Tyí#Ek©µ¾+úžt€‚'AS³rUˆº`ªq…=„½ƒ•¡Yƒ¾é#Ä0‡œµ¹´… ¾5dya´Ã¦2å>nïUœKÙi|X1ô‰u”ô‰dSìŽOûÆ&åÆäf7+9ÂïÉ•‰º´g“–?n¡&ï×}¥ôÝëºG®-Ø*{äþ^@§È±%®‡B~¬ì¾uÀ©vmf ãõ×@|{ÂR öB¢;—Q}Åpê³ÎàÚnu´&y§£!Åp¤Ø™?ºâ±çLxHqé’!(REÆ»Ö?–æòQ«¢rRšà‹æ$dQdÏef‚RdžEYtLûq@kÅvÐ1Çå6Mž!OÑ i–Ä"Ͳ$ƒ#mŒƒªžD‰2NRemJ/Gf |7(¸s*HžÑö+: g>Ò|í`®©MŒkº¡ /†¥€ahÓä&2¡ë: [Zf”ö‰÷Í…°Ðê(–•Ž.Ê¥cd´\ÉR,!žØ.þqâ"‚ÜA÷2BYê¹IÿªKù'×åspmqØ1•ºÄÝAbz†Á¤%^‚¶0E{¶ì”ûgÏU×ár+JªñÆÙèÏcº w*Ïl„–1v&âw0 ‰rñšZ•,rÑKµ·2ìÀ²û÷J&W,Îa†‚Övu[(#¢ÕºmìH"ÊET{×3¬Û0‰?Ì]B{ åý²1²ˆ5¡: ÁÉ–=2–;6Ü…aÝªÆ nvPKœp±µ:,C²‰tOG –;À %|ÆÁl`ˆ­Z²ÑaõÏâ<í¥‚@ý+¨þƒ¹"V]dEF#×ñw¾U–ÏY®Q»œXT÷-(•—©X$*EåSê)X¼~zî¥Ó'ÖA3ôг{Ë ¿Ú~i>ù‚±ñ¢Í¬o`àBQ;Šk8ëÄM®ôæUÕ°Úë#¥šãUÁ¥³WgNmW‚1ƒ0…À NhÌ»8t…uýç7{_ømàŒÁwg› ò© PgÛToNÖ šªÏƬñ̵ÙÙ7À§7Ö|c6¢¯Å}U?úBé]ÌA'æ²£kºK…< ©ŸcRcGG2P“õü"üš~jž®XÄQíÃ…Ùv.­3!ÿÔ¿Æ™Øjëñ;»¸ 5S?2T T‹ÄOl/•¹öî9eR=×Ýš»0Ü"wÞèJµÉûM g[ß#’4•-Ca±¼¾°ÍÀÜÀ)¹A1´ìàl…«+åqµÓß?*Ó1´S d ù•GÊ•NñÉdÛ5£í¯”¢™Kj}=Š tRvvô¥qü ×öv÷)‡-²(ôpQÛ:%uŒ%$ÕÞ³ šÙ(Þ7)û´ê·d#ï^g¡÷œuÊìú±Œ¯ô‹DìuR"Ђ³Ðn”Z›;SgÚB­†f—ûwm»WëÐ;áÒR§9BQ0¡ê?r2›s"¿Ùd¥©5­f(UR}&ÒíáÏP&û÷Øj9[ä¾f³6'-Ò<É:‘ŠÎšfJF •E?oÛõU‰bÆÎ»_ÖIvÇ0åi\fn¶‡LGönšˆôüW²f½EÏ.Óè¹L‰gêò«WÆd·f‡Æ‰aAvÌ,Zk0³°^Ë/•€ôↃû&‰[Å*±Á×`­²qªU–ñؾ_Ø$/ V¦C:sÐkZ+ h¿n÷íß3œ{Hb[È×{ÔnE;jµD=¾dÎ.ƒ‡½Çõ …>YèÜB§à©…fºïÞö ½-CøR_ž›^Œî|íÒ¢ª%¹§Îx@à^J>:!°"ðÙ| lTr ‚À nçÀ}_¹à‰o¼€th|rZjáS’µØ´Ÿdä=¥£µrÙ!3r)KÂØ²fYÐ?ªä,¿å'Aç»ØQ. <ƒ„B x ‚7€Z)ßú­OCÁ> Ú”Ö¾YYk-›Ï© *\H]ÔÚ W<ž– [P·ZZ[0·×±…Èêï[è%À¼A».Í磒”ÑéOýÅ`S»•èï¼ö .¼Œ@mÃ×fpá;8Š%®É ¤E)‹•ç‡-ð•Àë%ˆÇ6s÷…‰õÃŽ’?iÎp'kÙÄçߺMü‚çPš_=Qó¸ ·¹Šu$I"ëøÊ‘©ô§,²òM6&Aß´dYQRߌ„`ßQ-(£°9ŰI‡ÎéQÝhÅ5'ÔK‚l¿ÁŠ>vGgð Ïó‹(•½L! '/Øz¹êuË¡ §kýÒ’é¤Æ)‡møþ¦Mp«ÕÔæñKú ÷”»ý[Á|šíYõÛÆÖï Ã_¡° ’&$Dôàz 1î2ƒ­|<ɹD†ØÍ•uyi'7’¶8 ~à¡e%(ünök rY?YˆBñø …9ŒÊ]ñQ}x“ŸS„ËÊìUüc&~«Rè£ „+þœ¯-ûñ–ˆ´ C)úQ›Jy?j#Ký–4?jó^Ký$‰s‘ªTœUIb¾ -âÊäY*5 T´;–q^z˜wª´*2;zPȧ~SG檞Sgñþ·]ôc ?Ñ £û ñª–äø'$‰mŸÿËÔµ¨’ع×sðfø->l‹þ¯-cùæ¶.äÂi}3Ÿ².`J¯ÕQOpNß|­Ç‰°•6öÖ!úµ¿MqŒ(QuÊœ=—Áj£cµ}[–ú†•0=™»(,®ûe|ç͇‹ÒÛ8õ9ÛÂ=þ)'Öòa?¾m¤‚ÆæþDlm^µ)|uaÿ€>¤ê¡ÆßNq·0Gúš®¡0ø¿O±yƒÞ94£'°„´’d ãæ§;¢·­í(ã*±·£À×<Ž£¯[÷ÚfÆRÙAå²Öî7Â/ü]UP¼pxnÀ²Œ«A] –¿Ùß[Q.p3ìIXÀäÕƒR²˜®rs­ú:–U,µoê:„TN¢aýPY1G‰hJå~µÝÊL¨]Ùç×àmJÊмˡÄÜ ÌM]@åG.礜óPnžòáç=(˜ç¦bÍ DÕ.‹úûw‚À²Ók)Ær&í&0XNäx‰äÒ wÀè@ ïIñ•…^‡ Û;U›øËy¶gÚœ²P>+{‚m e‘óÄXÞâ!‰¼æí;›xhIeDdéÊ}ŸžO›ŒHV{¤íëÁeuAÚ¬À£1==Ô>rû ’Š=ÅE˜¯Ù5œË0àJä œð b¸ä$™g“BL>›fê!À×+ ™ÃìBÅyºüÅ](¸Fy·¾‘?"{ßYè­…^:›à¤{¥}Gà[q»É#ñì[ˆƒëds@¦²®Iî¤í¡|ÝïiPC=%'@u§I¥Cî µšàÝ ˆ>u|´x-Š‹(«oZÔh½†¤üÒ÷7lÔº³…›~’Ñ¿ÀQÖi÷Â&ýI\ÐÊÿ7F•óõtóä8d€gü´ÿS> stream xœí\YsÛF~ç¯àÛR[&ŒÜ9¶J–åÄkË–%æØr¥¶tˉ”-ÉYç×g¦{€ÙŠýÒÌÙÇ×Ç|?ö=!Ǿþ_G—£Ç{Éøìz”7÷¾+ˆg£÷£Ô ô¿¼ÓG—ã'35PFãÌËâñìtä{Y–úÉrV1Ž#õw!¼0Ï.Go'ãiàKõw89P¤%A8¹¨Q2Ö=…¾—H>hßtÝ6ÔÌPcCmj×P?€~ÂP±¡^X›Ð¤È¾‰kj=!òÆî[—°/k]ùÈOp2¶ð;"‡K°aßÂVAdLä‹’ÔlÿeöTÂ{9šýóþ‰OÊhâ“Dúíâ ï¿ôÂ,½ðó7¾ô‹/ýL/r| Ò   ;Å ‘-´T„žÌVšÞUj”ÎæN¶ÙÜÐÂênaQ>8÷ÆJ‡{CøøÊŒ¬êÁ”̨埃ŠÝ…–“{ BË Í}†&U D–øÂD& y/‘uÄ×’|éâë˜<ÞÂÔȄԂTOþ¡ÅÉR¬;¾çÇ™Œ’DmGÿB†‰>° =? uà²QW$ªQ¦ê¼2󤉚Ð÷‚(Žýü¸ê±/âX7QÍI$Õq#½@*Øôºg®0B5„Ùä,H?_$Ò“(5:¡Ö"ohÁ8ŒÍ[ô |19ç“éA”“9µ²a)µ¾ØôîÃôÀZ·äÉ ŸËô]PëœÖUY—ȼPíöÓ†/BiÏo:~\rÏÞõ»°`ä…¡ØãCZòb)‡¬qs˜Z{d §òóïF¹\~q…¨¯Ö̬kØÂÚÌm¦°A™†¶ Z¹V![Ž1UÓ¬$PÌIW3¤J%ÇØÔ¬So¨[§`‹ÆØÞj§­vM;BˆôL;‚8Ô0‚uŽm—¾Ù0¼3vâÇtâ'F˜<âi“®r©x­©än­‹½è²r£bÛØ$^ŒçÌ4qN)ÐË2û0Ó—ƶ†¹—Ô«tVI¦¯¡Ê^d£ Ñ[ùUSâaNŠSÌN.«f9.G ceÄþKéC^]61²´AêK¨Ì¤a¡ò2æQÝÓ˜Æ t’Ћ¢&âÐÒfYSxR-:C&¨×¾LX«¨Â‚€‰ä—t¸”mé%@`gœàˆ¢Ší꤃.¢âd7]lgÞ{2/ò%…ÏMl,¸–$©sSœ€ ¨É4K1QØíúHe£™ûúÈ™ÎoV"µ}³à~÷gO›(°Ù×ëYS—™žºZÑ]ŠŒxÍG7Ìõˆà =¦1·Æþ )Ž UuŽy¤]qŽÆ-Êp}·¸*^TrŒj)ä†uX¾ýÂ1Q!Ó,“Üí5( SMÿ¶‘黂ÝÂ\€›i6âÕ)ü$‹ÂC$^$àºù_fŽ¡…øŠÊ™"1èrÞ)²Še÷cc‡[MTICͨ]A.z¤Oµ2Á´ÎtY08ÖƒÙÿòBÁVÝ©æoî”íWd³n˜.À›6Q57gÙI¥Í*˜·ðØMõîîêGºÑ—]• ’štcËÕ †\=ǺXSwVêVü»‚*¸àœ]@S/•È'3;v…¾5ÕÏÝ*¾®%ìa‹]Ôa›SS,Ò=N³âx„?øžd6Û–WSDë»LåœoDáãö\p;‡XxR¨ŒJS“B²Ú)#™³­%^¹.W‰À÷2aWL Ÿ»}±¦bO•×ßj`˜×J­ÏO…ï¡·$¯Ík‰Ÿ Em7†:1Ô¥ýîp1ÏÜ8ó’¿¾,C‘xiÒÀÓÁ2|vf¹LÞ¿Éöf=;¦ë›>ëÖ <õìË:`…ŸRë{¸ð,,æE–6ð³N›b_`%bðw Y?8õ¦§ô]Aµý<ÝO‰åÏ õ¡^Y›(å4àmê°Iä6ä2ë°;`?μÊpyÜë˜öa·‰•‹ñ…!ÃÆñÂÅÙß “ÙÂÌÎáµø¦_'KbøÉ5èî1M÷0 ,rp†[K0½Â_ì°¾{D>'rö½€ó"F u„M¯ E_S½5}Cú•¡è[à_ £Ó›¬‡#ð·Hæ>®à c8ŒÍ‹ý9V ”“G]óî$û°õ+"x\üùó»P_±’2~|tA"¶$¶â8z Îæ°ƒ»“á \†uƒ8\²õ׆Ú3Ô¾5¤ý«8aÆŠ×e¹Z'»d ƒ lŒm1âP<|åä ëþ\ôà ó8‡íÚ;¬ªó•GÕO E9¥ÅÕü.(¿,AX“øfa>.îñ»ì®DÇ÷Öäeeãž™ ^ §À‡°åøÈ‚èW ªI¨É v×·ÿŸXSv†­U"ˆAjê <ÛÜ{y–öɳ^ÃT¬¾gpỆ{ wP¡jÌlcpá²2æ;fà|Wž˜®½lzý ç¥ öNÑáú[øö½¥Ã³aÌU7_^­ï‰FN@pØñÿð¬¯ð¾AéW`ÜËa‡¯.aë*õŠ7„Ký.Ö‡‹&syØ”bš¿ÅÓÑàp©–­†¯… ÷‡_tÁJTE¸­Å‰"\|ýˆ3H¼³Îuº;B(ý^ÝÒhŸº†;8†7ø'Íðõ8І H»±‡{"ñÌš¼sÉapŽ5y ¶ð)$ÿß•ÿõ¢šò™i¼¿üï~7ס×QVýŠèUõ> stream xœå\[sÛ6~÷¯ÐìKåNÄ évgrÝ&MÚÔVÚdÒÎŽc'ŽwËõe›î¯_€$pÈ¢(Ëvfš¼C¸ Îå¤ß'i"ä$uÿ=±ÿiëžoUÍ“6ÄÙáÖï[E’¹U§÷?MÌí@™Oʤԓù‡­4)Ë"5õ¬b¢sûw™OŒ‰*&óO[o§“íY–Jû·š––Lr“©é@‰@å %µ£S“eøð—¡ëN ~ÔCÛ•D™¹V©„I Í;œR‡3"ˆü/l=&ò=‘‡°u24ÙλGäÅаOJ»Úoóg[VÒêâùÖüëHh£Œ„(¤H#ù Ç¡ëë@ÍõâÊ:`²ú ™~8ͦPÄŸ €ÀdLVêfE¥¦ÿ ÔÓŽ1ó¶9\ðK•hŸ­3Sf~94öaˆ‹38Ãì{[üCg¨Ï{‰B¨D–¼ëƒÐõ>Pó÷=g¨is„ÞA©1ÕþçzË5ÊdÝÃâ=ÑíËî¯q,È_1Ga€Ü Er²ŠóW> Q&µS(”ñgaDÀX%û+¨—·$¨S ’¾0r;¶%y=!˜<ÈS Ò+‹ï1uxAäS"w ø°HØáÃjanaöÈ o@…Ð-\1ÿ'}‘^w;Ú|D ©É;X6ŒYÉî_ÌБÞX9ø 8ÒOõüªæp ¥÷”¶›÷ ôއ†ábpå“·?îP½Þ“ü0P?êm Ò@Ý ”T¨ßà‚ŒHžq&CÈÁ>]߇C& ûÁC|û²X?øä²aψdqƒ1tgİݡaL:÷+sQR'…tèÎü`ë­?)ý 9yÓ›ùÉfÄàÏÕIìm“]ì(Oá ß@¶q:ˆ”+‡Së}ØŠb“ 9›÷#4Fä¡ûê æyŽ C}Ì/q÷kù ß y¨åuaw–‘ >ÈÌpz<l/a\8bœÀ¾½¹¥ôâZ‚ajœÈDÈ(ñ§ºéu4¹¯O‹!#`n€«ÏPÔ'P|l†4 ¦L¨`‡Á³¿¦Á¼†NgNäWÐŒ0@q ɾŒª O¹(…ðá)ì‘BNÓaV»œVÄYË®P.ùjhРéÜ@èc¸ÄLJíÌ+àîŽá.HHw¶í'_¹“žKÒÿ¶­’R-ìZVpBÚ4ÞªÒöÉòÒR¾ÍΞ'i*dɆLìÇY®uZ˜qŸkíÄil³É¥ e~üI˜óÈ7"+•;.¶ÍÅßWC2™šjN¾Æ‘©0¡ ÷¨õ’–!k©‘EgI¶n‘ãL_ZçXØÄ–öɦº e?òÍ„Ý.¨•ñ¼gµÌЬ%iQÚgåôÏmáòí4÷â—¶‘ÍÅûä,ߎÊ#­ý:%µŠÊh¤=yaü©Îh†w5ÆÏ"®r—U-·–þ^ngv·i)\½ï¤¯Ë¢nÔ¦,§3Ú·")Ý¡ز‚È©ïHvüj›`G(·gÇÀ}íÑÆO 9¯$:¿–3|×XÒøZt™Îu³lE ZÖôXBe4™° Fã…hŠ^›ÆXÚ4q¢Ht¡ª8á“›¢#[çCުͭ.k`ÈqkŒ‚KV”‡‰~ݦϓzÍ,­T¹ë5mÚN¡eÏöÂîv7U…,ßéØgeùiÎŒ7ÌEî¸ÇVÐÀ ãÕ÷$UÓ¡ò’l2æ0±;;{ˆ”ú¾‡}+É3÷vIý«ÏD÷ OƒGü(LÀ줶ê˜Y‘¤Eî­Ï˜H`°GB%y*s—ã¨Üf(©êq+Xx|+AvpØKâ Ö/üF´ì(¬îe7 ¸¯H3aÒ.Ìú^ÂÉ0ë‘#®H­$ ÄÌyž‘£ÅÛ]4׺Å\y›¹Œâ=æ>ÜWÄzìRu’feåR‹íY°\ 8*pøJìÒècš’¥мÎÉg4Â’õtp œ‘O¡;F%;º‘à :eþ,vxÑFÉï,†|um»äjRf›Þœ„äK±C±àÌU)v‰8;2l½wµµ¹¸e ommÃ]ÕKSÔ%{nf¦R Bx²«•9¬‰Ø]»ÞyÎgÐiêäÐ)lTaSrNæü°•˜Ó3 0·ÏÝ?6BÚæ,"ÁüȽÆ)íËøÌØ“ëKªÜÌ6ÏÿKÌVÜ9"\n ¢Û‚ˆ1|úvø(PÏ;kÄWô˜ÄÐ.+¶þƒbÜî`Ä0 #2¸£Òk{ðÝ´vÏ%xŒÆÀ$FâkM†j}QÊë»R¯¼•0œNtñ€Ç?¯;ù~\¶°ìˆ÷adïFÀrÕˆ"p«sXïÚÉ,ç¨ÈbwÎb]è² ¿r¦x€dQÕþl\Öm€I"JOPvy?”}°k]À¥RóX“<\0µÝib¼Lt ‚Ƙëa§¾pUqÖS%áX…ò%XO`%aa dõ’ÂP}ŽÝ¡’‘­új–Ië’+¬pBlöT+ˆÅï·g¥õÿ>ÔyHŸ¸ŠC+¥‹é]*džÑŽ^ØVk &³Y*™…ŸÛÓ¯·¥%„Í:fT½ˆí(çbuŠ,“LôsÌ\‹wBädNYEúc»LL‘™œcÏBê(Ú­Cõ.F˜Í1ò’„¿Óçï|]y½vªï8ê¸=§§4÷A0ƒògºtꟕN§©œ¾ ŸïŠãb »å«puÌZÛö£m¢°«•Ú~T57…ri ém•¥½ ¨ŠzežG‚yíŒÿç°4ßÐ]Úòy¿Ýз ÃþÒ´ƒ¶Á ìÎFøÖ(…'Ø­ñ¢áãHÆ<…ZîC‘çc>î‚ Že•#ZX®ÜŠ·vóxÁÄìécx,òŒ¥xÔêè;¦6m¶¾Xå¦d›é`Ò>û+ŠkÅ¢YÖG°4NðTûŸNmdbÝMàjMœÐž3ës²–l¢­Óæ~á,V ÕصË©äM/¸XêÄú „œ®.zÿ6\´(+L¨Ea’¢ Ô·Œ-nÂÜz 1’AdÂï€ôÄÞ a$ ?d“ý çe¨{Íô’Ë^Ýܸÿðm ü3ù¾ï!ܹAÍ=êüÒµq²º66ŠåoôpáWúxøù÷Ü›ì[ØÚA‘{µ?ªnÍ‚¯1rð;#ƒ/öÙ‹YöxÿÕ Ìå^òKÔÌ’z*Zëk}ï¼Yëà;ð~ñ°Ç<gsÎL2›£ÞmÊú¢4p扒°qÏݪ´®],À}ü™? vó¹â=/ÝpSØü5FhxÉÁrÍCXXá—¸k¿ïlÝE´×Ní®²zŸ:,@Óm YÓ9½É©•=ØÕ°L„˜0Ž@ñyÞÀ i¢Šð0¡SÝÝÉÌDX]*ú!¢Rµ~ˆÈG¢›"’Û³4ÑB› ôš©$/ÓTÙf“”µ°gYaMÖ}w4«ŠÜ…Rßo÷ØDŽ.MšêêfÀw>©;Kk=5¡_V£²ÒN¶ŸÃávwy™I·\Ik+ 0)2ÚŸ¬j+·¿\—N¬´×óºi‹&>]½\QØ9¶˜|:×»¹gG¥¦Ô¼®v¬ò=¿%3[¾K§¦Ú5Œ¯ö>]fŽøaç%<ÛK¾Z€ßWGð »bh=ë܈W»×ö00@“m”fï=/ÃŒ2è{ÄÎEsÌg{<£VûÍ5­é³pýŒo²Ï!ÂÀ¦í{¤º:û„L”nšØƒöŽâWE óé®~hšÍ^ákZ_”ÅÏÍö];pµ;Dá 6¶ú–·ó#WrŒ4EŒ áÇÍMMm,ü˜ž¦Äq°sñ.8ŒvŠ‹é¨;Zf*¯b°hÝgmíCŸ¯wìL§ïú)­6_UH¯äÆ_0µ­·rûDA.ñW`ÎǺf1£¿;V 蜑ê%0!‡=êXáAÕë^[ð!å®4à+ÃLe®œ¢7 dÿ‘<ªºŠë³Âl«q>œ‘Í7'‚YüÏ' á* ~¯’»¯oj>2‚…ëõúPá•ô㤑ûK[ˆúz[„ ýˆH÷WƒVÁbé×Ld<ªûÿÖ«¶<ø%â­ýº þM„C8ÞÙÈ0!„`øoƒSc1¤FviÅ ðûP.XÞÿv#þ=ü½n ?¸ä¢#¹€¾H¿‰c9 FBœÀß„{BÛ{äý²³ f/‡€í|r9[ß­Å–‰FÛà0›þHö‹÷`«!ò+ƒåL?_‹éÁ±&Óä?#2_Î?4fäÄ \¯`¿&Öãp›óç[?Ùÿÿß@Brendstream endobj 47 0 obj 3526 endobj 51 0 obj <> stream xœí\msܶþ®_ÁoÕur4|ñt:cGv¢8©mù:ýÐt:²N–ÝZ:E/VœNÿFo|Á.À‡ÇãävšÆù°åb±oØ]ÞOQ+%ö_œœï=:*¢³ë½z8:ú¦®Îö~Ú+ãÔþWHøäψ$â„÷+º6*ì& ÅÙõ´Éòó‚϶ÑáÁ¡×5·­Á"'˜›Nú„bùRßH°ûv.Þ8>s1÷ZRl1ð2¼µÕ,TØÀ(aøif¬“óÎÅÀ{Ý„ Pmå‡,/’ %<@±¬q)ýß:Ÿyw˜¬ö.‚)Ë_aS«¶PõK^Ûbï%XXn,ÓÜŒ.Ê&Ø•'1å¦q"ø%€³úá]:ìÅJz7¬°ž]½Çxäb€nðz~1Õ;N+I‰*%pè€ã ÌÑ3È;¦üâ¬È)ä¬p¯Òà cÛ8¬¾¡½nà66ÞĹɄïLíç™®bM—º^pW§}¤ZÄ5+_þE,¢I+²I:x½r¯w§B’-íŒ47¬H“n{î~Fÿ&Ð9ºC¢\еS´Ïâ±:èÜKþuxNÜóUo¦„Žtá ¥‡¤ qì‚"“#m")wæwº\iÍ´¡œYæ¨ýÊAA5¢Ÿß³l"IÚKÁòÓ£Þ¼Á~„ŒÂ)l‘?;bðÁàÜ)¯@ê¡äúÅ‘8‹ëY7*b åäo!e™•«Ò¥r·\ñÜeò;1›wsçÀ-÷¾¼€¢¹|Þ{¼^þZ…lÛMêÞMàöç1 âÅâÏ!9øðÿë»–¯„î;±¶òµ•ˆ³¶Èæ­‘ $p„Ö§_õ鯚®Th´§Jm.$‰À}ì¤%ßöo©%»Û¯÷cË„|ÝÍÅš‡òX5ð6ï£â l³x®²~‚h{=#vü´H”­°-|¨:µ½ƒl‡(â´÷co¯°éõ~Âàÿ$&ÏF¬AåXÍŽ”ëÑÆc?èÀ˜³ýc½Ùøç&$нHîÑ Éÿ-È6ÄŠHZQädÒÎl¥Nÿ;d_Tÿ·2YCÝ\ÿÃDocA¼´’Òò2¸M½“K„M]°(”TÖHt¹Š •D†dsDmUpÄ‘œsE†ê8È÷‰15"\ôp¡Ô¡ÈPÞ5œ(330÷Ô¥!7L '¤Am%'O<®ºÎÀ‹¬`N2‹e\¬\IRê´R–ÇJel^Ðz\$¸ ·UŠ"Û ¿ß ‰'Èâ0+4ïÈÛ-Ž2¹6¡UpÅŽûXâ§_79]çIÒSÕ<¡}™j Lç¤4]x2Nýj-óg£©ßÒ¢ƒ QQëªÏ«0ºI‰†ŠÖO«»c½›Uvu6ÕYD¶òŽ{8I|å§©.¯¦ÖCÄ {‚JèC¹gT`\ÉûšwpȨaÒ}%n43‹³T :üËVþÅ^ÌæåÛÑž_ˆ=Ñ4²¥uŠ9éAðAϺÊüKÈÀ ,~B;%JssF[(¼¹ÜÎæm'æGS¨Ð-<¯£À9Ö}Šœtgº*:3«÷‡ª`¢$‡ôÕR0^Ê÷ÄÔ¡ùèJæÂi`üVÒÑ9KAçXé^ hmé%S±*]y½¡». ¸$u*aI ˆv6T1¿aÂ:7ºQ­¶­;Ħù©}ýª%YýØp¨'6…†ßÚ[*ǽK½¦šk:¹Lݹ8|šk+eÎ&½mäE™83N^‚¢²lðÉã<óú{nÅ4¶=lezî Xû.ÜÌN +ñFÌ3¯hJ¿šë vPÆ_Ó$±®!Á†Ý›4$ìØ§Agyá,Ô´þ7„XºÁ̆-]¹j«Àæ(lÄÝ.Ø2¿²®sè…ÝŽ#4¡ß^ÓR Â|‡yáûíÝC ñùWQqÎë:O×HŠXö>Ùõ´VÆÙ“uMõܦ·¾*›2wpµK‹¸T%¼Úyf¨HêÞE‘b“Ò÷Ü•¸dñ¼·î)[«ž×¶hN¤ ½¶^{û»Ž1Â[Ëp$Ü\“†z@P¯î÷ ߨ»;[â9x¾Yi)lïý˜‡¿_g߈¹&îSÂ~ß^óÚ% ÛwSwqû¡«¹'·Ï¡ýš ®× Zñ¡fXÈ;&àæÌ¡z„2”.ÎPØ*á”6ØYÏ—­±EnÂeüÜÎêà|‘ïc; ˜¥¬ŒÓÌà u3®·è²™q =°’ïâø»D«Ê,N´=ç;Ô5õ+k eŸ"œƒ ]nÃ×°]zF¯ÙY1áÐ'ëMs¯‹.䆘Ûë¨ÛGt¨?Œ»Ù¸Ã»Þºî¹M¾ûæî»úesLÞ+×´Ç9]¥Sþiƒç½ë¿/e2ôqˆrá1 q¨¸„Ëpu´[ì2R,_._Á£Àªã*ÚÈ×½èïCÇÄ?ôòèÓ>ÄLJ€[tF>KêŸçcnbaÅŸþ¢æ¡¡Î1,U½ž£¡ÆÐǯ—€óÏ<Îïö{5¸ÿìd2¬øÓt¼l+¾!1å_‰8êA÷!¦£Rˆ·Úê`3‹øÈýŒê®_ÄŽV ~OA6úI=>s±OÑ"Я—éWîXü_lZ¨©[¥ýUý.‰K¸ü3M[þ"¶'Ä‹›F‘‰]ÜB¼XG~sa 1Š«©ì#øGj¦X*íVe³‘s¼Ç1Þ,ÖYì|EÇÑ#_N8‘Q±Á!Ĺ¿x2JÙ=ºØ{ÿ•˜C0Ϩ/÷#1£üŸ"$[¹‘ImEÏÜV\IzŸ3 ß!QñÂ]ǹÖñ‘›ÂZF{BµžÞã&g|©ìåÀèð|¹ï`âUŒ?UºЪ7ÆÛ)jÔÛ z]ÆùÁoC‚Ónˆ|Kæc¸\;OoÀSþê콃øøýïÊ65;¸ËÇ£ ·»‡¡»I‚,fwZÏ{¯éß¿ËvÁendstream endobj 52 0 obj 3336 endobj 56 0 obj <> stream xœí\Owã¶¿ëSðV©Ïâ’ ‚¹e»nëf“uåÔæåÉ’,»µ%Dzw“|ú$? ’w7ÉK½Œ@`€ù‹Ì8?FIœŠ(1ÿ,°¸½º(£õnTGk‡õèÇ‘Š3ó_=ÀáÅ]ôz¦Š"ªâJF³«QW•JÊkÉBÿ®Š¨LÓ8WÑìnô¯q4™f‰Ð¿ó0˜h0.ʬyZƪäß·Í÷´ÊÆO>Èæ^Á lÙœÀ{WzÙ÷³Œ4UBSõv¤ÿÉüÊÍÙŸ="Wކƒ–œ®Ú:è |¥±Ý:Èî!$[ÂÎøøˆôHgÌ{1zç>„Eý—pîg ¨èI­#¨_QÅgÒÆœ+#$ò£á[ egzå w`éô€¶½±V/Û3üô¢$µWU” Ï}ª¬ã>¥PqÒºO1™&±B(1Ž œ¥i.òq60®çW±ÒXÒšÛ²Š³\ŽÏÍŒ*—¥¨ }n¥>žQœ\¾Õ£*W†/Ί,) ‡{¦g'q!Sá˜é _x^/"SÆRÚÉ-زk7Êü{Ü`(’ÊÒRUYŸ¨™ÝŽjÊõìIýCkœ"¶§ièÖÒtÅ*7l׋fÿ16UkSf'—©úˆ‰D^jz-i(+¤LŒÈíàÜA÷`‰ær'IªEÂVk5Òû–i^õ–g"±ëk0¢©lÂ5‹Ëš·©ÞHe|GÃ[{óI'²ÊsNo=X&UZ3–Îìvÿ0©âRisæ4ÝLr³¹Ç#íÅN¿3'bH–ôiN [;ïž>“…ä¶5s>êæ. YsPÃfs$}z‘Uãÿê:3„j½È…Jç¶Xoj­BÆi!µÍ–ÚVo&FgL›²S±£XNgV³úrvsIÚMËXæEYiÏnÙL&‰Ѻ&¬l,*¬©lÖÏñ6óxØÅ°zVáÀ!9´h wæêчÑ~ñbÀD2ºêJé_ î“^=aÓ*M«:¤®•†Mý©+ˆzU4e©=TiOóB{/UEÓÖ;Kàq<Ã!/Õa>4O3Ó²++İE®svÈSè@@;a…Âü CÅdëÙ=í, Q‰\ªÆÀ¥—4ÿç‰>fZ_Gî³Ul}11™Ïù1‡-ÎÐÙ~7éò)`çøÔŒe×Î=ɬšÆ¶z¢³2ÛkæÊ´È cAsçP>X-ØÜô¶Ø«àeØG2“ÅGgÈØ5ð~¢§JY)~ÑlŽØwÈG¡;×»¯»1d­ÎgIÚ†5S+âi“ ,)tQêàÐE”ª‰‘Xf/-åtçKÃÏD_ÿâ ¿ƒ<â¬=ŸÈ$é…h2‰Ó´¨ìöÅÓ2Öwr©G¥AÍ)Á}VÉ%”›wSúS¥õY÷SV”…ÿbô§LŠ9H7¬O;2ŠÆmæÞù8A¶È<]ßI½™µZjž]° .hC_:_D^IË^è`ܘÐÍ<ëÀì:ÞB EPµ_Ë“8—9PŽ30o<·›éÛsn쥈sÑÁa¸Æb‹gz”$Â9ôP, ¥}Lo|hV–)¾»ÉË0à÷šIŸ™y­Ý Ç>àômdå‚5ú’[5 ªë‚nél+(RvŽž&èìsØI¡ 㛆å*/Æßø–æ¾…>¤“˜˜ûÁ7zBnåãSg-v8óÆ8³4–tq0f³{uÑt+ßo²U8!˜Ã¹ƒŽÔb` Á=12†¡#um'†IÞP\º?÷šW±|sK¬¥»#ºïÆÂè© ï(<[k4IÅ ËXð^JÃò'©n»6POÊ.üôHřȽôˆ_˜t)iéM¥dîhGƒ;ôR®~vSÜqá°±“øÚÅqÍ­sR8íä•®¬™eÒ¡eWÿFœÝ4Hs%J_èsÐÕÞ@×ÊÔ¹»§ºú1üý^G,cŠqƒ„Ëfâlšq¿³ß^êøÅ»Óð+ÊýñðEWj ðR™4‰Qi„ÊssÝ ”Ødb¼ªw X÷ËᘰrpAVÞØÀÚî[pð‹–¼v§üÎAõΛ ;R P®¥[XVÁUVVYÂe¸DÆ–ÝÀ¹+ÈH¶ŒUšph‘í ¸Tt+v!Pî›^~|ˆBÕ^Ƭ3ÁšÙd–*YUÒðÉÖ/+›b­ZvÚèPë$Οaø!Î/!/ÞC`%ÃF±…|ÃËžÅ7¤¦§ŽØ‹ô)Ôôã+·‡“ê-30 ŸøŒ ó~bG+O âc¥–9£ógFÑäP[ ¹~§À!BÍÜ*á¯R:—lª„v*.è3׉Ý÷MˆøÀÞëáÛ;îÁ1à¯à!oÁ¬Ä:‚¢Sº.¾~–ÓnUG0±Ø|ñ=|Fà+ß!é áhâÃmÁ“}ÂÛ6঩χT†l›ìý P4ÏW( !o~xgÌ^î™Jò¬vŒ¤‚wZQWÈ©ë—`Ù |eÀÙã5È=mz]±G˜1oxFµ?SìætóNö¬°P~Ù{u2…Ýß-’Âróà¬ì™­fN?)ô9l 8çÏž€ õ·—,RÂLbx\›ƒ¯àëÂA×"ñ/=®½t¶âÎVtÁ?B›õKcëo©ŒÂ¿¾ml5/õÏìkÍ`_«íÿ,K×{jÚVU¦uƒ@\T…4W'á£îÓy‹Y™‚ Màí§gp!õ½RSëcÛyZ%ulÊt)˜ÓSÓ*A|“½5Í®ŠEºW…q!©{ÕØ‹mÒˆ6èéûðš,5†Ú2a%Ü ×UPRÐà•=1*[ÉôPÙ²i5€ µ„µy±­¥ %¬Eè^Ï~ô´´BO‘²wqFX¯úÓç- â‡}oÓ¦T[ƲR¶êÆ"¿`¿®-òVb­Û¹Ö=S}`µÖUá1Ô¶ØâU¬ ÎoA¶ËžÙ¬¯ #qWÃ";°ãÇÔ¿÷õšî ÍŸ .^u@Ï £ý„×kyÕª ¯±~å¬ë[]§ÀÆSºL‡Œ34ó5žyxç’¢ ¯ƒÖÄꬥWFò+Їd.w n ?üÂU ªQp}ï §½a6 Ÿ)¸~c €E@;xÖcDô46ذ䆽ɱ`åŽ. ^%òœó+bâoúË<ý‡½,Fbœ÷À×¢´©Ïû½ûbVy•*÷ÚÃ^'™À?„˜E‹ËøýDYÌú>t†™Ç½è=Õ*“I}%®E…i8®x},G¬ªNíÆÓVi—{ÿ¨¶kߤ"ö«V]r,÷ú4úz-…–;{3e%škb+”à4WÚ° .áÆAç„ë‡üVº"¾ñ÷™ãµ¬tM¼I“Øú÷ê%ØÝ9HÐÞÃt÷±hȆ=‘â÷¯àCú1Õã_ó’J¤ñ»>&~ÕøœJ›¾,¬Ï9¾ã`j1 8ÒIÑ>fúþ¿S5$Ôû›øãlRP€S¼8¤ø[y ¾8tæïTzï;1¿m{1þÿãYƒ&""ƒ&²¼Âpì׆óãÿ‡Kð¹÷gŸX=xÿ_¨°~ßà“Ͼ‚±;ÄÞ÷`CöélôOýÁEendstream endobj 57 0 obj 2824 endobj 61 0 obj <> stream xœí\moÜ6þî_±¸/·{ðª"E‰b>¦iïrp/iºH‹Ãzý_ãõÖk'Íýú’zá ¥GÒjm·Æa“ Käœy8$ ýë$Ž„œÄîo-¬®¾z§'—Û£âñäÝß+áöòè×£W†¸ÍS§Jšéõl.UëDO¯¼®5]’Hm±nŠ’4O¦*¶%l¿µÌ+]*Ëm·¼VÔpzL¯X3§¶›I¤¤ÑÓ/3«O(É_ŸQ/˜¾ <Æ}!Æ2«äÌÈúsL•&$~ž™HçÊJW¬’ýÿ£óX’k¤Ÿÿ<’2ŽÒ8µ YœYŒTî–‰i¹Dr«Å›»!1‚–ëƒbÓWp‡pRÁLÒœÊL?¯ƒtOâŒ5„0¸ùÖp0§¬À–|q>Ô0sû Töíl®¢L9œ¿²FŠdž™|úïÙÜv7¶Ï«²I–fÓŸ¦®°'‰òpÕ¹} ˜$JŒ¨SB#µÈ'i2“ZÛúÚ•Ÿ+•D¹ RNO*ËZg>VÜ{iSÙôâ‡{ûšŸfäð`b6&F¢S ©Z)’&Z&ç^Þ@0À5Eñ_óa0ûê^Üø¸Sô§LÑdÌtS¹t­:óWF?9²?ÿÕý¤Ü‹¿½±´¦{;s4y_´v—`]`¾âUöšs­µ;ؘ˜øšfØa:6ИÔ_’še} _…ßØÚ´nàäbàÜ/ LÕ/hÎIaµ Å© .h@ dl<«æuÅIVÏkÖÖŒ-·Ðͨ›Ù€Àgöæà"ñz†"l—-›¦+ áÅCV”=]Ã.VÁX…ÍzÇpK.I¤‘1]ƒ !+û¢,›Ä¢ óÚÕ‘í›Åí†R¦~Ci7Žy*l“XÚØQLÒ$Jµ]–^ºöÒy%`g±C£æÕ™¸õµ®¼ô¿Ý5­ÊÂÛ½Z¼†â9‰kïHÜÖ¢¤°©xÔ”È/}oôÒ·A¿…tûoÙaÜך8$Úù˜CüH\‚¾#˜x«ÝÁ§¬Ú,{ ʪmH¼….YAe[(žƒQÔ.a|5•^ú——^®þHüáVšº{oI|žÊ霞*h8f"Jæó/$ºe ¨`–c>ýÔoÏ@sΨì>eÕ0VnÉá„jxñ5ðÓbÄ.»‚Fès ÞC¨ã™·z×Ð ¸g—PïÄÇTcæ†!ë•·×`ùoF„ï!ËŸA[|‚À!‡4ÿ”¼A¾¬‹[ClúÅ­9²Špµ7µæ«uQkÞHD­%öQk%ÔZk3ÐÚ¼·¾H¨µÎ-àÓRk]穼ÿá%:YÛ26døÿŽ o³ï 8xþŠDvgð`¹{¨ ûùAœÇ’ë=<"%‡"Ý‘ÛÉê\œSR(ˆa ê;ݺèªñv–DqfÊ U 瀞Ðq’jzØN8²&Õu†Z,G±&8¹3ËY7Rä‡á<Á%é ZëÏGaéHAâPÁ;>ã3L©>ä 5_´ðˆŽ(˜ÁܖЉÝ$ÔŸž+ÀÙœ8'y…X« DÃ`gƒÚˆQIŒcôÍ)Ô€É0uy§Òb&MËŽÙÑŠñUÒi†1]Cf {ð[ÓEî*Kcí›"E`Š)«Ie¹ RV)\Av—qº}´f#žÂ` òXYºìHÐ'”`¸ç¼bèO„o¤ JìjdÛŠ©^Â~™oß,À,E±×Îtë<•jÃfúqÛ#Š€ Cxⱄkå±g»ˆâîÔð"KÛ“•]Áx‹ã ®†#À ÇÍ”±…àÓ,u&ïôä`»c¢ξ¾ªsø1¯ŒÃUHÛÒMÒ6øúsâ'(Ü´rQëdá*i½vóÿ/V“‘:>–¶ì»JÒH`¾CJÛ·BÜ\ƒ Ù{x¥œ·^ÃŃÁedLÆñöìëò«I®Ræ®ˇÆ@^À€`ýi¡ s±Tj¼Z³ZYµÍÑvc]Ç KŽW %EèÚçÌ ¿ÖQYãÒZ·¢ª8î;zïbÖ‚~Üñ3¢àLû åÚ£jÍíCe’ò÷ã¯W7‚WëZ3‘Ãõv^~Í8Û‹ìd%«!NgÂõ3ó%RÙ·<47ñ ¶p|{æ¤` ᤕÁû>Ícꚬ“()ôY^N<ä?õAéÉóŸdoþSçrCP¡ÉMþ?HT.€ÄˆU ›j­6h÷1à´ð x:1Oü¸ûuÄ{/Å^zá%$㥟ã“™÷85Œ¹ªøÐ5ŒòXú—~mÔ>×àF(/mÐ5·àã#øz9&Ogg&¤æ^èyõ!= >:â8½®ÛUOE„”Ÿ‰òܳAòTõ¾)¾oî~Ϫlë¹ñ Ã÷¬þHîã›ÅÑ÷öïïC…-»endstream endobj 62 0 obj 3067 endobj 66 0 obj <> stream xœí[ËvÛ6Ýë+¸«ÔSÑÄ‹ìòj“&iRWÝ4Íéq-ÛIj[¶$·M¾¾Eb†ä¥()rœœ2Ù\ƒÀ¸Ì Ôu…B‘û_‚ã‹ÁÁaœ-yspøCægƒëA*÷/oàøø"x0±¥ ²0‹ƒÉé ³,’•TÄÆþ™ "Ôi0¹¼£±Š¤ý[XšDéá¯}_ »žBŠ(L$Ä`êGÙF©E¦ ~¾z.25<"¸ ¸$œÂaKØÊ†½ƒ}O^ÀaWçg¡°„'`޽7“v¤ÝƒçƒÉ·•]xêYüɣɻ :vñrÙEÑÂÈÞ¼+(÷ð¢ZfvåžÄjuøÖ€îGž®—€øÇâã( …Nv$~ ©ø®+>3H¶mPM¶î{D¦äy —_¼æbueTý ¹Ä¼ÿ>$üŠà#Ð*‡cjÍ 4¼ãlØ›GÚÐÃÚ‡ÝéÜœl½å%õ|Õ°±õU¯?ôIǪ±;éÔd¶j¦f7 1ÇØ϶˜’[r\£–%{úËj¤ü(Y• ʬ…[¶˜\׶⃄ƒÜÉ}ÛeØÛn „«®ø0°I"CÔqîIï)òxÑb´×ŸéGé®­ÃÛqÑÅ [468¦{Jð€àË-v¼S“pdºª-aßΙíÑýuYÍòs¡CNÿ QÔ¯ªQ@7º NfpmNsG-ÙÉ™uDŠ”Ö%bzøÚ£È£{ €2ÞTÈ'šwHá°:âsû©>¬dîàPŸÜÚdÙ’hŸ|ã5²¤Ô*§ˆÃDÄÒšdëi„´ëÄ£À"eâ8R–ñ²ñÕH…QœiÇiÙfY³r•Éø+ÿØN΄Q$¬òZ*Û!1ÒRáž+%V9ý¨jeØXï'Vg|¯¦ 7ÖîˆÍçSÞ÷œÞ~>Ò®5Ó­otKJ„vs.»^¸¶RUR¬® D±®'ë&ë–´pKbKdÞÒ&Æ °ó”*ã­L[ëpFpµ&·9LÖ2×)­çi“éàu =lÔ½‚)SªÞ¸”0.”mêToò¾Zp±‘MŒLç¥?WG]xt²…“]øQï<ú¸¹$–Xb·‰}0v8‹úù,-[y(;‚ïÝÊL]!÷^Ã,ƋŸÃÖc(wikq }5(»­jTξ´ž·¾ Tc¨£ Ô]úÊ@_¸›Ê€ì+{2‰Ÿ1ÿ¾Í|QùW‘€ì9ÿ2ZÞYþÙ3øõ§_¥ÍRZ­­R?Ž<ª°&d˜¤Z¸1©’V» Î`ÎÓ¬#áLœt‡)×ËtµŸÆÄaó‘Ç´ŽYñ¾|F‘¢ù±Ù.:zŒF)V=÷Ü´jW†hÛ…iuTÓvãÊ6€Ìâ, Q›Cóu­~ñéÖ‹6Šš‹Ý"vã÷‚à}Èvz¬o 0¶ S1ß軆þ †»ÉYûÜ«ÆÐ§å^ÄËÃN…]ÞÞRÕ×ênvÙƒþBüó]ˆ·¨[Ÿþ¶*úݧ¿ðP|íùo3¾/·Ôâû ñÍþ.Ähû?\ˆ[¦tk—e‹Zù„åø”4êÆÖ…÷¼Q‡© Êíï¨SPï_¯08k)³°RG‘¬—k«çÚSê@ël¬Ãqw«"t^\|…ç¨Z[áƒN©W¥TŠ"4£éý3ÊÂ$u•€J¡Ä°Òˆë'^³çÍm²Ot–¸ä[ÿ¸¿-ånÏË㊠}5@Û±ì§KðôØ£·‘L«½¡óÁe²¾ߎ×UŒsœ¢³¾›ûÍ®H±F,vFÀØoŸÂ¸ö†ëXng®Ô)·á©Öéy3!âÚM'ýœyê·£A+æðo¯ç<¬È!+?"KUíGd"Qa¤W?"³ùMÆRÊTC‡•ZælGã,L­”¼/ã,T:veÆQ&]]œ8Õ©‹Jr!R÷3 ;µDeQšß9Ù£©¥sÝYhÒ²äšÌÄÒËs26‘‡ôÊsÖì¼nšXHg3ýÈ¿Z¤¸_wø©<ÌǦiìî·ÊY=£÷¸däu £¸–ßì “„ùOö†ùµÇŽ7n;œêMIwØ©©ã;ø&ª~£Æãß)b©™»J«•ŽÈòµ–GiÉKµ)î óÁÏüE¡ã|,´šÜe]$R>Åk‚7ü3Df]VfZNòCyûS_â¹ê¥bÛP½¥sÁ¨‡ïW!hÆgY¹VcA²—:]]¶)¦F”—m•Wù®LÖ†))ˆCÀTëlm`:.§5^y¶ï'×ßaQÑ{2jº*äRîø ô‹="ÇüģߠƒÀǃ•)ìM®a€Ëà,RÅ>U“˜`Dð Á üAî{¿»cY7ýö¨’„-¶gÏ_gLÁãÓzzëoêþ#ÓÜ>±”¸‘,ûN1Y›ˆ ]­ÐÎ fvµÆscl RÞÇ“ÁÏöÿjœÐendstream endobj 67 0 obj 1897 endobj 71 0 obj <> stream xœí\Ys¹~ׯà[È”8;ƒÁ`frUÙ^ÇvìØ›®Ú̓$RÇ®,Ò¢dgóëÌn`>Iö¦¯·\í!ÎFßÝÀÇIšdb’š?=pòáà»·åäl{Ð|ž¼}Ö×gª$7ÿ58|òaòx¡;fÙ¤Nj5Yœ¤I]WiÙŽšMT¡ÿ]“2K“R7øpðãt1›g*)3%¦ç3ý9²œ®,4ÑP^(•æÓ“Ù¼LÒ4Sjz9“ú÷¼–ÓµmÉ~þEY'i™g¼ÿÅÌLTrúa6Òü^N7Mÿ\¤%t5Ë„n©e-WÔòŠÀ«Àçc?S6̶[¦ÝW©*>Àõl^˜‰š÷:#𮸙ûF—™aœöl”¦Õš d%Ìbÿ¹øÛîSˆÉâÕþ÷ïôù‰ÂžŸ>§Êü²øýúÇyžŠ,‘Rïž'E™Ëé‘…>XhÕAB™>*-“¬9HÛ[ÛëÂBÿÞ¤“¶AVçš,ø‚+¯¼!pÛƒ‚#*‹/ì*_[há¬7†ÌE`ç™í514–•Iålí"¶\¶ŸkÞ¸#L`Œ§%xegpÜS/ <ÝÒ% ¹ï-ºÞÄ?Ar1Ä/!*>ÁýŸÀ¶ˆ¶5Dîv'´ ĪO¶Yè±…^pyžÀů!Ö~ÛÇ$ȶ §X¤ ZZØ ÿýN´$l/é²~•ÉD ÜÅ #Škˆ«÷12Ùl¿úhsTxFªÁ* ƒÏýõö@™ß,Ô©]•OçÔð†«í^ÉaeìYÛ iÔüò é[ÖéˆZÝB½îÓ-]¥›ëÂXyQB'Ò½Gpl×(§¤ŸB–„ÑñyQåwÂ^ŒÈk2j×@’Ý™`Ç1* ¡ÍûzmÛ-–Ú”û4Ó›UujPdWyi…3miQ2_²–þÜâ¸æçÄúã•.9–ìl„ Dã"»¦Ý~Ö$ª±ª+ŽC6.[ï6ÀgÍÚ6¨‹b”­éRHøt8cè„M† üÄ`Û’ŒÐ­¤êI¦5W…rû{‡Ú4`gÆÚ¶[(¤^¢,µƒQšyæ2×z©’“y'µÌl$~ˆAzÌÖ!0¢4®uY"ÙÆLîrÊtJÚNyšY)[O´›Á¥*÷¥¢LYµŽ’Ö5i¢„•Ѓi8Ï2©EYø^ÎæuRéQ²FO«:É¥Òº_KíZ6<×öi¡²Š@aä˜áÆÀµ>»´r¾z×D®Q~e¡£n¶Ê50xa¯º%W²2¤UM&òÂàMŸU](wÎ$¶ß¥éWTU{\æs¦•‘3áOS¶nv¥q@ yѶN…Ö}ï›ÆR•š–Ù÷I;Q™ç¬#Ao¨LUæŒðÓ¬›[;ZH­<ã¢(’¼ñŒu§ÅÏ#5í V‚ˆ@!Õ®-#\1-íÉqÓKìq2V ™®v0²uf}•Æ„ì•u–›–/©P&ßNi÷˜“?ÏjíýkÃÍÑgíJRmô-¡^fºeå¯ß×N¸ÁM'5Ký¡v¥f³lg^ 6š¸©`š– UÏêè¾BÄ1êcà j ©¾ØSx£îà2QÒ%ü^½…þØŒÀuá%žÛÈ Ö}¡ Žw¶_8ú‚UùŠÅ–†u Û´ç“*ã4çPÐxŽ™ÓÐIžk™“í֮Π•©²DÕŽÆ$‰²‹ë=tí`zhú»2…õ¾õ7Ý /àlzÚ¯0žíþ/#°ˆ:„Ör•,°Ü<Ÿ!É€éÙcƒûTXftqb(^ M8=â´Âǧ•Öl^†zÆ(4‰RV±aˆKÌðQ;•)ÆK¾šÎDkÑ"yóLöÖhË–wi´0Óo Î`s1M…ý¶˜˜ÇTvsC"‚¯XM¨]˜ ƒë¤4’gÓÑL–d©µû%´›,+_É<)KåÈ£;x¢‡Ò·:³† uÁÚù$·VÃ5»Ä½õ„LóoÏDYÏ_íoñ°Nl¦ÏðYÅN ‡7°> ÆÞÁʯ?‰Ô æ°¶›àò»D»§\{¶$T%Uj•›³G :ìÅ<•É"f!°Žá´Ø.ÅmÙöÉy_A¹æ„e->²€º¿¤¨AA ˜óLî4"JêuT XÔL`â Â'“m}í›j>é8xnÛ®ei8ƒøÀ1zÇcEJ;jòâ˜NÔMÂ#0¥ÌTñV?GM™G¿cŽÂáëÎ[QiÁ%ì£Óxø ‹I—}‘‰(ò–hÚpá\*IsîÃß%ŽØlPÕmÚ½—ZýÇŒ›v5êÞÆêL§±É|™Õòh“wc+1Î]BÎCçÇ$]´gt8Ôcæ‡0SÙ)·Ö‘¶$eÙ‹}àš¬ôn¥%sÉÑÍeGe«}Ê1Lͦ4ã ö+I7' ÐBñpbŒhÇÇ<¿ýk‘D#xUÍÝf‰Âä¶[š)’¬ÌÉC†úТ"sœy²dpHþ ¤3È¿·éúß&}5F8­6žãŽ<¿c ò<ã'Ž-/í¡Õ ¬ÃUàˆhxÅÈ—m)Ê$:eOQÌÁ J™²´þV®ÿy?˜¯}L!'ìÝ ˆ;R÷q³^vÃ6¾‚?[¨¯¸QS¹—ïż!憱ªÉc8Ø5Œ¹K§°väp5ÜŽpgC 8gÉýë™–àçSßtC0{3о°À†H(`~_£‡‰°ÑÓÖZ|A£ÇüÞª5·JYÞ÷äþ—óãùï“cÇy½Žø…ÑÀñ Àm¢ÅƒXß~9yì ÞBLvŽt¦Y=åz¤7®¢iÕfÿ8þÒ¤¡ßY¯ï-¹Š¸}IÛ%_çKZÀãHÓ¨?\Ñ´4PŽbêiGªò§áÒB¸^Ÿ*ûéÞoAx‘t扅Î-Dw®Á·0Þ ôØÂµ²­2ýr Ð&Ö€©%Vn{N «h¾†_Oà¸XG®/j¡_³ã­ƒšþÃ%8úÏ;QŒŽñhp¼/êA³ù|Ú9/FÕ’ãÇVhãZìÏ#¬vFìä°†»á›ŸbkˆlÛM"`7Ý6$“+e’¹½ødŽ/ˆÜ#=©Îû‰çѶÕ+Ⱥ%!þ&¡_I®lô0äz…-–ñ:3xYÞáœ01‡Á0Œ/¦à#8†GeºLS˜æý£õäÍ9Îݯ3 nçà é؆êê1˜©tÐNÊÇ¿baçH7‡ÑK^cW_SÇ8³¯8—‡7¿jEN­9_½ÈÙÂaCb¼•ÝÙQÚSüÁe×Nü—’!É Käq<)Ⱦ)¾ ¤ðM¹;‚ß„À=…aÖ·$½óª1Õñóÿß0Œã«q¿ ÌÝ®)õ%r"Úˆ:ŸQ3†å±Ð@œ·_ÐÔ<ÝðÛMQ¡x4¸3,Š¡8š]ÄK›ö~g¡·]>´gÚlÿ¿¶Õ~¹ÈBÁ<-¼lªk¤&·_gæ:¥ Ò †dý4[¥ aÙFôRƒsGer€¸¦J䆃zMn‡ucóF‹–/)Q‹Ò_g4T¬Šå!Rª¢¬ZFrbjYRÖ»~G ø”Àäq3cahKòo|Ùð v{6Sij"‰®/+s« ]9ÚQ“Úý½G!ª“Öò¯–¦Ð»•4îUM”¾ík8‡·=¼Í;¸oÆPiøKØÔ0N¤:l”tÅ)¸<£¤A5©“Ù'd{Ÿ„&½µA Í+Ð÷èñÌB”Æ<öÏÀÂÉw>0ÃT—,ó€úÄQ=l1?hœ÷AS 8O;&zÊgk¼íRp -,üæzJÚ ó~âg”`ðìˆ-…¢Â_ƒ–_ƒ˜¾µèKÆt©‰jø…ÔÒFKøabwë—°´Ù¡Š}`:¬¡†tÞ êÔî@X6ZN…dä,Ö1ð\Rº~­QAIôîîþ¶‡“Òì ¨TaÓ”dØØòp6^ʈÖ.w6fZ ß#˜ç²NªRØ–»Öšü•6C =éeöò´ýïx+2\+¡o`ß@Ž> ² zÕ}UZ†¦ðóë°€^sÅ{Üý,IYEkÀY/|Åój;ùÐ^ËBoÏGkÌC&_¼H±*Ä^ fú1¥êØf¾‚r…I›A%9c O"z· ‡Úªc^ò¯ôµ@w]3UCðÓ@•+üÚbw\¡è^ •I¥;ó:Rz¸%v7f¼gתËÞKèÄg–FOH}õ;Wô»_­wÛ#ÒmPiÓ'ŽÉÎZ$¬ÐüQ:š6oÉ„ïª"šjÐ(I3]Ûbv%+P˜ì^ÒoÎ8wöɕлQ¾F,þ€8xÐ{{ì!zŽÀ,sø­ ö(;ixëžiÀq&\W0Ü’iRUþ}O2t†gewXù·´2ý„×ÙIÌdZƒ‰®lï—¼#ÖÉÊGZø ‹=îÝi,™0e©… y\&½ûdƒ÷Æ2ä7îË„ùóÃ[Éø¥eu6(ˆ'Ò*)ÉÃûºA¼ï |A [Ã8îk>…ÝÁˆ`& –B¾íÃÝR¿cõcôäJ‘ˆ¢ÎÙM§Ý'÷âçyèhø‚m­Jio«,Ük|ªL¤2·e:Ç‘ÍGy½3„àÊʾ ܾÌyýÙóñàsðÒòÐ64kÝ€Nl+ÌÛt>xÂÞ¿fxº8ø‡þóC6 }endstream endobj 72 0 obj 3821 endobj 76 0 obj <> stream xœí[[w7~÷¯Ø·Ú=ÍF·½ñF miC¡ÁÚ8=&έIì ~}%¯W3ëýÖ²'!D"–*î§"öÏ;›»YttÕ™4G»¿L‰Ë£Î‡Nk÷oÒÀéýóh«o¥ŒŠ¸H£þaGÄE‘‹¬Ô*£4±¿I”Ig–á¼Ózý­ŒJ¼ŒNc‘Z¦ Ë”Gýag¯ûª·¡ã$Ó¦û«§^xê§"OõõÌS‰'@ó€Ú¨¤e:Yèªc“MÈTd±4Ù¤U(Ó½"Þc"ÇD~j(sä>‘7D^Bò€È‘×!½c(6‚z÷¡Þ“2?yeÇð®ÿ[GÊXYgØéô¼wx$ÏÕ‚$èëÀWõ¦RGJåv…Zƒ01fôAh)°c؇¼¬ãC8ÈKÈ;€zqÇ¡oÕ¼Ó9‰6yœN‚Í$j| ?ݨÆ`—36ðEÁƒ|d¾—’_“—Ž€ìÓ¹¹Žà6õÒ\šXÜK‡!oZkìÀžÇŽ¡2¼=.B³ ÂV&6„½A±Qé°‰‰ÓÜT{ÐK…ˆ…î[-Ú”(eRTŒqOÄ:IS¡½Û©Í¨.i*ÇR¦U4UM»ŒÒòÈTY Ù@#•Í7žŠHåIÏØF]8G«>_xêÆSÖæ¶‡Lš‚K™r÷]'…Û¿S»ï~-»ÖJÔú~Ûím$ÎÖÅ숭yŒIs>øgÄlwDay•PÝ·=’«Mʼnå‰éž»ë˜Õt.Ní™åT&™®qDä5é¿r3aý±OÇ$ÀÔ87 ]´à’æQÙ/×Ý#â-ÍèìËt±~™Z¦‹ñ>šxŒREœçºô¨Ò]6ª6×­¡ö:+AYéR,PHxêÜSµÀN3çY©O}Y\Ž5ç âCŒ›½ýõ‡:&aCØò£}í©Ÿ=µç)á©GžÒžÚœË'=•xê]Í6ÊÈ,γnΓEÕÈFËâÓÌ|„ZǰØn›€µ»5VŽ P¶—?ó¬ãš·U¶ª–@· ¶¬ñµýïkü©¹6¦ûj™"ÑÛÿ]O½ôºéßñUt[ÿ;è­ªSZ!~rrsK,sñAyY+{Ê‚9¹¸z`‰ŽÕÎødÔcØÖ\÷B¾Ï€æm@QuA’èeH`+¯îyåqqÂp(;ò>ƒb '˜9,æÇW¹wŽ×”X)ùñ2ªešòP¸O­/?Bé˜>S®œdYœzÞÃÇQ}<ü¬™ 5ê©,/MÀ‚[‘Ä™0>a=Q&îºhqÅTˆ†‘5¥†Ž{‚Óºµ±í•–ùÏ¢{ǹ‘ÛD>m}›còX*k›£¬ QéZÞæ¡ɬÆIËyþý½‚™þ^¢ÿàQþ òîÏ iû×iB7¹Ê¦k2ÙÊ'ôÍ”WÅ8t¶5ïIìrg[y]jn. ¾`½óªe‡ÇË'xšàÖ70Á-0A´€ ‡Æ·Ú¬‚/AOáøñy3#ñ1õ CKüü®MpWïïñöf­øÁ5>´¿ƒÏØ™°í“»±ó§ÐÌñËF1œýöÎüÕwqænŠ)tÿ]³ü¢×nØòÁ‡ëø†¿‰a¶`åî·½k xìü?Ui{¡D¦ºÝ%\h2g‰eîIðõ"vm|wΖþ34;öýáü.B®M_ca° /ó'Dx— GY5cc«aÿ> stream xœí[QsÛ¸~ׯà[©NÌ€ wÓ‡¦IÛ\/ÍÕQ¦¹NG¶ǽXöÅrœô× Eì‚ü$HJ|smd’5¹»v°»ÌÏ™(J™ ÿ»'N/'Mv~3igÇZÎ'?Oš¢ò¿Úœ>½ÌžÌœ T™-¬Îfo'¢°¶¦ÓZfZ¹Ÿ­ÊLYu“Í.'y6ýkâ~”îÇï'³ß¾qOŽ*!C¿td¡LU篕êy ê%à; Ô@]žIÍçð‰k”uiŠÆð÷k©ÒVý@ž¼&òŠHÆ»"2ƒ>@+cæD.‰\@Þ8+° éÄþ1ûÎ9·2YY¢&ïÚzà]mdQiïÝ7¹œ‰B—Ú8KÓ£ºPVˆ:WSSØZ7íl-u>›V…•Qùg&sžž±§iâÔ˜å•l+Û*iÇUÖY¯*´ÒæÄ9g Þ;ë‚ÁrUO§²ÐÒ[¿.lY¹‡·S¿Jk_NÕ(£¹Ðy;#„VÓ”U~t½pT¥tiò§n‚ÊVu ^–©Ý¤•‰¶’’ýVš¹ùè”Zæï¦Â²6Î=•9Ê«E ƺt‹ ÷nÊ?lT»‡•õ6ê_¿Ϙ¢—v›ÅM·qò+)Œ7‘tñcªhxo¢~¨§ŽÃÁAS+g¢@¾pÌB[©Lk¥ÀÍ”\LýbUóA®ihšo·DŒóŒ8—D®bññØ«w$ÀÔ´ÝÏ¢•ÀÏ,ËøÏ‰ììí͸öŒ¬¬ß¤Î½nâ‘ÓÅx¿é«DIîŸßÄà\ºXG¨» ˆ7ÔåM;”ÔÂe"#o‚ÔE þ½»¦SJ—<ƒ¸·`×c™?ƒüê[Ãl:ž†Ùþ>šmÙ8ì°¬Û&Î Ç ÅoáÂ0¶/ ‰Ï¢H2½~÷¦âš4y-S‹;I}~«c· õâ G•AJs—¡S¸|H'c1ƒÆ8ƒO±‘?ßw€0sÃA—¯¿j[^:€12iy¼E.R6L^•˜›®S^¸†zñ Ïìê}Kä{è¦ó¡½7âÏK`ðg{`qÊà h‚pݧ7ƒæº‚æÂbû›k< ‹=Q_#:¿üž~ÐRáNü{˜ë÷úËk­‚”L!ç\ ¼?3¸Vf"O?A1†Pw©™²wÛá.D³`;:föÙ{2HÕ)ãb˛çPl ÅÞCã(Ä]Bk³3éÑ(‹÷Ç\ã “ÚT: ‡˜þ¨w_ìnà¸dìžlGvÀÞd ω|LäKÈ{lQîÏRÑRò«3eœÏ|¾®mÈ}ZÂÈ+”\fÃDÊ¿§±0ï#>° éfl•¯éÏ“7qU4LaÁ½tƒéRÕ| Û‚ÊÈT™jÓ°Ë¡“†¼îˆÑ…®•±þ¤ŠÆ~Näc(Àx±…Gט-åW¦ì”žÎ¹1É.+l`¦—‘·pfEHîO¾G#§²>mÜÚÈÑãFõ5žJ0i‹»4ì:=²â ™OŒ—7€™iøs޳Î7ˆõÝ®¥SJÃǽaB?ç[Š&îê 5ÔË©ŒZ÷Îò/iç¾? 7r8-“ ;òŽd¶Ü†çðÞ‰¼o±›s€¿Ôñ©ë¤ ðGjQæGå¸ 3OÁÖ‚ ‚s}iý¬½ç¨×²)Óèºc‹¨=ãýÁ;Ê[úÓÙmM7¡:<¬ù:mÔ•)´mz .8^ ð}§€OYêÚý?Æ´8ש3ŠáÔè¼][&᫤•óƒ5}ðµÑàh6l½ëMâlèÀÇík]{ Äk‰dGÆ1yçUpY$Æ|‚È‘OÚ³¾Ÿ£iÖ©j9vDË:ŠÝ+óäÉúxW…RªšõfÔBmðH6•ÆA¼5^ø¨ÖU!U•­qÉ«8 éùÚ#eUti$¤b[ˆ.tû¤ë!ëx°¡yÿ°H°[WyØå¾×¶òà’ºc[9àÛN åu+¹n¤ÉAçt—ÆCGyºµ9hYîØšhb©qIç͸º{»e]_¥'N5p‘’º¯^ëÉûýtUQ‰Z9ØÃ²ã&ö. œDU=²Þ6·ÛMŸªÅ|iÇ^¥¤þ Zë(:ëÎB÷ÐY¿¯þéÀö?Ö?Ýädñwæ{,èjÀàt §Z”N¹É’F_síØèûtT2ÙC‹ßÎ /Óð+m†? f~(úDf|75÷?oØ7~ø6‡¯©_Šõ(\>HÄÜ×üà—JËÙ§ë¸N9*F ‹“Q Õ4O`rŒ+QÍÕ)sÿè³kZÈ)åõ­¦}ªG¸ßBÖ|}TïëWºäõ+æ,&ÿ;77ç÷¸NÉJ]ø¨ ŽÕG»še]h+ûêÓ:i4j_(¯ê ½VêJ¶øp)s‡6#*{²R&®Ò°µ±éºÞ¶©þ9 pa•Œ»^ƒrŸßi*ô0>ÍÁLAÕoa4‘ò-ÑÖFÓk*‘²"ë–hkk™m´éFè¼ïD:Ôq@³–’–ñˆ×J£ú¤j )âú䨞¶‡úê³]›|ÐߦJ/ƒKxŒ•Ñó’¡»¹Øý…»~ÃÊâ–v#Œ‰13®OBÑsMTEº xuàÁ“Ù„»(ž{Ü­6•Šùàá3µ?S 'zþ«ü^-E=|®öð¹Ú¡—Ý_ðsµg³ÉßÜïÿÅwendstream endobj 82 0 obj 2325 endobj 86 0 obj <> stream xœÝ\Y“ÛÆ~ß_Á·) Žäʃ´’bÅv¤HT¥Ê²+Ž7ÞË»\]¿>€˜þøÀ!÷ЪbUY­ÁœÝ=}M7ÿőң¸úÓ»§[ßšÑáÕVÝþmR¶tg:`@?Z¶’¼"¯4ûoW-¼¬VÏÓŠ¸¨Z.¤œ ɘbV¿ž®?%qN/KW_À€3ÿš,eL%½¯©ÛïÊt‹.o3GØ$OGùÕ­;–d ÐÚÓ©˜tA/þH´Hœ}ŠD>Yð4€¿ë¥VH«5[­Ð*¶Lái}±™V\WQë+àQ sz2¹¸æÒxAu#·:Bšë3ÅþÅèÇIVY±A}&g¼FõÎì Žf8ͧææe<õÍö¥ÖÒ¹Ö„Qº¯sk,¤ï#im %qÙ²Íù ©Gµç1…Om¬š)M5åÔ —(ÏÕhº´g«‰Åô¼Ë´ÃwUã’)Tžã5üÚ§PùžéÜœÆX!=½{ã@0oK˵·º-•t™SîXˆT¾- ïÙ=Ä´]…©¾Œé\´Xç·ö7mÀ=Xò`b8Ýò`Àð|Òµ”¦íxá·F]z.¨Š£¢u­ÀU|ç\Éš-!·_R™1-ô½Ï%¤ôƬ´ª,´°K{NÝÐ}ê{^xB}Ï÷©÷ óZÆÁZÀLÀ\ÀŸ`ܤëàZBèM±®l}[dR>QŸÿ¾}Ä; [Qj¬Äû½ ;¹Ct…"(߀ãÃD0”ã‹õ™?½7ÞOoKŒÊð×!Ùð)¼Ók~’(»Cj@¤òãwv¸$‚û4z¸«‘ß–€ Æç!*§ð>˜;$ Šß‡3е‡# h’^˜o¯º‹ÛÒÐWâPÀ#ŠÍ‡£ „h‹ïB‹?uÐ{2ËkÉj¯ôkoëÐ~M~N± DÿJ‰3¢3ð«ùTÀ¡Ã¶€?vI³Öó™VåÖg̵ qÎ=òœk¹gžGòä-ªëµ?uOyï›pP‘f¸Wï}¤í /#¿Jøö…ûÞ{<¨Ñ±î4®ìœõŒ†Ê=¤ ±ðû½÷;3Rt±ŒzÔx„¿ÊÊÁ:Ć'Á`سËh¥NMÀuâgNÌtê }OBäq¿6âÊ:vÐ×ÀL»ôöŸRkî;]Ü^î€á"ô©·ÙÕ‚Q"7…?ªPiT‡û˜qÛæªìzŹ:'x>´ïþ” ˆ Ú¥Ûº¤`J Ïo˜fÁ“$öBÛ¹ZÑeýo‡£í 0»GðèÑ%=`€_›ë P&Ï1ù%< íý ,·¶±í· 3o€òÛ&ÀŠúf¬øÝ‘‡]ˆgÏbw¾ô1~WÂrb™ËM@ƽÜ( Í…lœžÆÅ?L¶K7ÁÍbð…ntÏÄø§ƒüw‹u)9¢)tœkƒ€@¾ :w¹âÔƒ‡tÞŠpFôÝ,ägýä ’ŒÄ'’`Èï¡Ë"ÔÈC]?QdS s? 0¼ ÌËÕ6°ÿ§ÐÂ0ÙÝ›<6Íïö&Æ_-¦­Ü%†¦æ“4ÑQ‘¤Í›å¸~nœ¶mÓFúîùY­bžÊÝýÅA¾/¿ÚRÓ/õFn¹¤§ Z¯ÇtØv"Ï>í 3€|´xà|±¾I6p噎”hNßÝð^‰o&l¹Òãq/.@7tÁø‹G½qÐ7K¿Ndj•Àãá‚^P4CP¯|,àkÚ7¨áÝSŒêa¢3F©b^æU m‡M zjg0QltQ© UV`je.#­’ i<È‹à¹sðT4¾ñÐnaØËÝ$$ß¾Lú(ÉeÙ¥©h†»yÞV¨¼hÓb È“ê*—ª -V™f–T•X¤M<±þ|ãÿf÷jY¸ŽE¹ÆXöê%¢³¬6žsÍ3€8З§_CÀ,X$Às ›ys•¥áB…+šµÍÆp6ÈÄ´¥á¨:B¸LqËãl u:ôØ*Kµ¿ƒ†kâ¨H]u`ôD¦p™Õj¡$µ¯“Ï—[ÆQ‰—Ï'Bb3bœ»’B $¸½‚ó2óxM 0#ó²r ¯„€U-xéÍí=ža"sˆ›;+Tâ·–©A6.(hþÇ‹úùÖ-#žQðLÚ+&¾`‚Cäôšå,%¦$)@;`TžÙÛL™e7ÑXM’¬•¥ÓvÝA4ƒäAeUMI… N)ÑáòfÔ–ä”ðÌÅUX´ÄTË2®H>‚ ˆj…tÿO“22…µÅšùž,'ÉÀvÌôFÏûÎÞ»$Ðy¯­gTß%t6.ñØ.BF#xÉÁà"tà1å9Ö "µ&(³üå%Iȱ Ø›“¯ òu×AG6Øó ½¦[Àñ»Ð—ûL7¶ïÎy¬Š‡OM(ôÿÏÊc#ß}JIlϽ©Ä¬5}¢ØIiq¤nì_|£ÊZpc:•µÕ‰6+¬e–ç@i-û¾²´6SÇ õú–®ÞêÒÚ¶?ÁótQð²TípsòÁJk™Q´Ni­**û=Wo³úÚ£|“úZv¿FÔŒY§¾¶-aZZ„yš7¨¯UY\‹¡õµE¿¾væªj¿À˜ ׺߫ ­gÊjic^Wëw¯7QWrJë#WL «Ì\íî¶+SÅ–Ç´¾W‰«É°÷Õ0S¨dpÃ\ÝìHêc±îöÌÕÝÒ Ü™k”-·kÍ\­í{7 {æ¼nWÊ}_ÔåÉilÊ^Ô­ÆÎœyg¸p›ôÉYñVbTTÎ!ªž!ÝÏ'•^Oí6å˜7*VIAmð ‹oœÏó\4cÛXéÚ»Âð |bŒ·T”²è§K* áb/Óµ®x°ÀÍ¢I[‰Y˜\Š{/p[ “?éíA‹´q.k‘¼-3Ï„`ÕÖC¿Á½f×z1aÒù¼QYb@Çœ‡´+L{Z=%×’ UzÐfØä×@zóBr_9ýÝXU¹*3®Rû¿´ÐÁ£ÏþåYÁAoUŸ UÛÆyº¤ÌÔáé~€¸§Q—U¸Šsa¯%9ðà”ÛróåEA;ˆÛFâh! qðzî`Uûú"Â{ÅØPZlœÊ™c[xùn†‚û7â´þ«ØÑt˜' Á„eÌ8[ʇ8*b÷C°ØÎ7ñZP4¶óþ"æ)3\‡Äû%–UâÁšG!áà]·M¬ð è…èÞæ·P}Çsù~Ð>p#ûÄ}?©££ÖÚÀWL4”çq~L“o½e¹ú4n¾+µüs7F"ŽC÷ÃáR²Ÿe¦ÝNª6ÿm’` ”‡p‡D».Ô½ïF㡬?4´þ gÙÀ&àÂcVŠÐ•p«œSP(+NêTiÍ¥ÔɆ¾ýdyŠ[/žå1K+W8VMŠ­ªF7«%z–j=+˜[JÚWÑ-Þ“a7ĪnØå¤Š˜Äª@Œ¢z¯ÝGî¡óC é°3é‰=S >×%µ2F¹`ÌÞŠÝt„š÷Kn¡Ÿøb’ìa$; µÜ“’êcÖŸPÌ“> stream xœí\[w·~ׯàKOÉq `±·ôI¶•Ô캊|úäôP”¬¨Ñ-–÷ר]`Øo¹\Rt/iœ‡Ñ—ÁÌ`nà—‰H¤šûÏËë½ÇÅäâ~¯þ<9þ¦>]ìý²W&©ý¯þÀáåõäå‰é(å¤Jª|ròqO$UUŠ¢UNòÌü]e“BФ0 ®÷¾Ÿ¾žÍU¢Ê¢ÓÃYšˆ¼ÒzúŠ>>ÌD’fy.Òéb6—ÒüQMïÌÇB*]LÏ=t?›‰2ϧêsæÿdz›…*§—3m>¦•žþj:µc¤J4ƒðî úÉôÏìøªâXÛ›½2>Rƒk³m¦M‹é)}mFÐyÙ3ìœAÖ_¥4K˜S·’æý#µeÝ.-eҬȔÁËhü †WÙ’Y¥UKFK[·ÈLò¥_ùßÙ\·¸û³Oþ¼§Ò4)3iÄæäÌHIÀQ!ëE¸ü:ˬ‰"\€íd(nÉ…–EýÙY/¶,Ægêõ)/¨WÂøaYÛv#äUi6¼r{ÀþýònˆU·Ôà2€ÐgM™ˆ¤½š¦¹Ìôti¨¬˜WŒíŒhgCÈ6h¥y–¯”ÁºÁ‚œ6ÒbæÖ*uÒÒ |.²™L«m =ʦIQVéôóÌè©MC6üCËŸ¢ä":™©¢0ú«°Îu–&ªÐÛ2SÍ´l«/fR™ 5~v„‘LW±u?ú7"ýĘ…õÛÓ¬²ó…õëwÃ9—kfûÙë‘AUîm‡XÓÀX„bkį|S6ÕÒªoGq¥{ÐLZVçv@ËÍ–‡G{æïß ¦2oÁŒ¥ÊKûÓɬš„eb,ÔwL²"Õv³¶Ð‰‡&z ~½íþæ¡c½}ôÎCzé¡#ÐÃa¥òš¤¥Ô‰ªøò²ä÷à ¼!ðr¨Û üúDà§¡ÁÎáçpЧ^ÁngT$ú Èqê/zå¡o7áãnXª¾0K›n \øóz| |SI;Þð·ô›üùžiä%}]0Ýî\׎si&eV; Ì³æ÷¹fvý®oà“¨¢lHÀ”[•%…Й3›ƒEfI®îÅ*%¢CdmH)¤nvþ0žF¬µùîF¡ýDò;Œ±…# ¬q¶3wñ.Q©ÂÍ8oµýW¦£V¹±ÿµñ7ŸOþAvÄÊ‘Ùë2·î‘2M7\a¤`-[D¦0Äaæ/ó bÞæ>t1ÙD­wYêŒwS|äÐ2÷“¹Á à'v³ÌØl’ÚfýRÌ<Ç;bñ $Þ;qïm\YˆJrÛ}Ô6”rþŽ >Òš‚im'!Kýà@g?¥k68ñ 4ÂnˆªCÝõä(\ýãÅŒ S½d0‚žî߾ʑt8 ëU/?98d”â\ÀXðäÎÌŠìkÀ"Ž@!ÎÍ[Óe¥ˆß €èWr)þuvšÛø9:› 3} Äié• ñ0`ÝÈqÖà1ƒ ’ŽDÛÌÍ®'qÆêq†¤ð¢Ãö’Úâí,³ÖcÊÒÍf¼àeŒB0À®Àà*ÌcŸËNÁº17Šrlº™O&FµO+!•Ï è`&œ{"ÍEkŒÕWZ0ÿ{$OR†H»¢Ðuî÷ÿBVG|Ï})¾'¿{N_KèÚ„®=‹®a ÷0‹\{õz±÷_r±‚»Y7 MiÙ‹m—ÍfˆøãGÙK8#Ñ)¤žâ ŽÀ0á)ãÂ很ëöOÐmÈs“ˆáYMb͵lBOäà³ñ˜fmYjbÙAƒÓo‘1†uι›éô6_ã¼&†ru©H»,b×óxŽsŽ5Ì|cbm<àSÛ}ÉsFÓÖR1\ãŒ}ì/mèçcO†â"Ÿú®zVȨÁXú6ÀöQÉ)g={¹ãÖØbı^Ks`…eØcÌíYÅö<ƒf0mÏTÅNÞ ;ïh„ÆLYh 4l€}t,`[–UŠÎAtÕæAè„Òf·Â‹:û¥=³¥ÃQ§ÇèH7ƒâ„ã^ä÷ÈÖ‡¢FúuUe6yŸ’¤Fæ)ÓñÆžMòL©kŒæúúÖ€¦©ÊŒæcŸßÑ ßÌæy’ë¬DëÀOÑ6­Œb¡ñ„Oa¼GC`Iø€ù˜š~k"1¢¬ÕAkkÓ´íZKc®’’’i,B NUÑNì9¬Dú|ø0Ô×c‰*Ïôjµ#I)t´Ú—”B§mLYôYi`ýLi#J|X`¾×?ÑPGñ¨µ*pŒ*ÊuUwcÚèv 5ˆ“®AxfÜ[éöİȬêS&øIÍ ”0ÅÙøNyE£Œ" ŒtMt>2 GlÃu$ìPü‰vuØÖ¥&©b$Εâ¬V`Uü4}x¬e0é°U¨|;{oAWqÑV…ä‰L}jª“‚Ž3 ƒ¾†÷–È%À¨{4ò7Wú¾í’ãì…±':KÊ\h®f»öĨaÚ?…:{ÃçÉs Nä$á²6V'%™§BrñX'I©¸Ï =GYÉs]…n*¢XtZRï¦O.¬ŸP4ÖLã´#d|„%Ì5]C×¥<€¬R'§éRE(ÓG+¿ê@mYˆ²å¶ŠwB)Ýô^±”¦ H´º’ˆ­¥4ƒü"ëÆoŸ 9/ ñÄçp06ÅÁº™åþåo™ð/cÂïŠÞ¯7¢w9Dï³!"/à²O!>BÑpà `#ªQYÍ»ýz;…çJ›À¥ ´5¬±ˆ|‚ºƒ#L`¬y¿0fp\ÆvN°Ý†Håi»ï!é¡lH6QµV(ÐÿùLÚ¾Vs[ÎìÔ¤~½5;ØA#«[{5¤_°ðïDŠ ¿Z¿‚@L©ùðé‚åb°8tPôð9âxj=‡D½ŽŸ‘rƒûGïbŸm©©Ž)tXmcÇ™ä<ÑjЧŸ@A{A6îS‚q¸Á¾bæ~†n,ìæ\Ânøü=%P°Íj©1uR‰ökZ—·ÌÝ·y«Žšµç’(º„ñ*hº¥D= Ñk\âÒ|,rÿ#¢ÁhmDcš–êÙÉv“üÖC‡Á@döÜ8ç>¡ÀdÁ\Âá¾Åñ`E…9Š]@¤’v"“‚Å–/Ç \ÅœÁ–ð+öoÙVÄ{jCÌp0ÊLû#Äì vÃ?›Øû£Ï¹k)oòÁCämÒ¯¨º{¡.Ôô*X'p£°±Æ6þÜÞ…“rdè ú¸Œ‡Œõ§°Á`>©Sÿöœ‘Ö{ß”®É·¿÷ðÐW¢håGÈw¦4lw8©<ÆîCþ`­¼½dz¹Ó‰ÝR¿ꥢqý´NJWWî\¿ö[ë·¥ Y­$ÚL¼œ¯9ÆkÜðZíFÛ´Oèþb°SƒAü$Bjåïàׯt 'ŒÀ×·–㊙Y”>êÓ±ùÂÞNš¬Ÿ<UýžöÞâYyý¶ówvÝ)4ê^ÕåÚŒCåF“žÃawä;æ®".#Áë¬#èÜŽ²Ô„b0*d¼ µŒÞÌé[Stù†&*ŸâÂø¢ÌeSú#d’«ŠJüü¸¿«ÃKµQÍÀŠQêÚ"vüý'™ëµª–%f5["®“à SÈ®“ö\ˆD\ˆD%=¬.(ë<Ÿ!Ù¬3Àå¹Máf]*Íꔃªé¦bÌ SùÚÓ>Š%ÍiiôxXÒül¯T©Ä¤sÃÀþ>æ†SQôÚÊ%ª!—†aõ{1‘ºõê_öÃa1ê{¦·&ˆ ƒUAPØÆž•i ée $®sqÁLc½ÊD¤º ¤eñ ÒÖ18òB0\ï ËılÄ5 c^Œ¼QŸüY¬5{>ózÑ3;ÖÐc±}Æv׆ôº™!=†k°W`f•àá½>Ú‚ŒÒzDW–Ýý18Òºö]‚õ°ºÑU|(ŠRKoetËòyw{4᜺¨Àü/’ÖŒIUdòÚNà•ÝzL7S0«¡{›ü>òúü8tOœ5(H2¯HgM*KѧS™ÒÆ5¸‘ £h³[QêSÇ@ªúªýQ 4—ÅæÆ’´æÜXê¨ßÇi11nÐôÄd¥£'&¥ÎÕ¼0©fs‘ä2/Œ %–~Y%„=+2&¿1Nó´4q‰ Óú2Lf”JòÒyf¬wÅà4RÛM¥Ó¾›dMƒ'Ôí­il¨]¿%A .ü`7:g¿?X¸Ê«Æ”»±–x ÂŒOqWÕÊ2¿¦Aý,‘íT–UUŸ™) !ò¬E2—E;–íõÃÌt˪TG†ßü4””Úr„×à´‡/Ö•·RtNŒè‚ƒ·¾Ë¯Ž1˜GÝçùS-¿üÅ‹éÚQ¬û†ÞÛί*ÿï~DïK¿‹ø\èõålÎÈÿ?ˆ¹[ÆïàAÌÓ½¿šÿ9:»Ùendstream endobj 92 0 obj 3665 endobj 96 0 obj <> stream xœÝ[msÛ6þ®_Á¹/•n*šIl§’8wç›ø’&êÜ͸Ž,ùíΖ]Ë>ûë ð»$ rl·n’|Ø@xY,»Ï.–¿DI,d”¸¿ ±¸í|ÔÑÉzT6Gÿ^×'£_F&NÝŸ²Ó‹‹èõÌ"*âBE³ãQ…It5«ˆTnÿ_ä‘I¬m‡‹Ñ8šÌþ;"Îe4{7šýõ`¼?IâD2×z<·´2ÓãO­&SÛ?Í‹ñ‘o»±Tš+•¤ã³IfÓ"/&S'‰PÊ®â¿)‡k‘lö«zN“Ö“¦2Ñ|Ô-µ®¨/­ÅXÛeùPÇF½T=ž-ày9¤Ö#Ø¡œ–ýÄVlíΓ—p#sØj'El*³¢@Œ”›¨¶ëäuL|Qc-o™|êëÉ4w§ >ß ñÜOÐÚO³ޤ0±‘N{fËÑçq9oqYn#ulYfœ’°ó¾Ÿ‹,ÍùJ¾ã Jfß—–ÿT_OR;wRˆñ×\ö~z&p¶é-ÇÏp µä¼áD}1™ÊÌ2“ ÖȦg’^Âm±³šÓQ JµéË›Ê4ÖF¥X“|üM¹@jÿÙµÜN›œÖW|Yßø2÷VÂZešëoÅ‘&RÄY6þdÉ8×i6~ë©™§"Oíƒ_W ßO½òÔÐFý¾óÔEMIU˜tLrf¹®úŠÂ„'oˆŒˆ¼%rEäYhØ ¶.ˆœy[¡Õîá¬Ã%ìp[÷‰| ;ü8&:=¾+5*KŒÕ:Ó˜„ $Ïá6/B›gç¶dLNZ6 Þðã<#žzüO½÷Ô¿G]®õÖˆ,.R@oOá1ß}*|â)ksà-x#JÐbùÊAãmØÖ8ÆÒh-j;î,æ Xèc†\£îé¹s÷󹟾D —dÕY÷!”Ñ/Ì9·P†Ô¦³ÝEë$Ë›ûH†ú_@­4E«ÌººwÀ¶ïNT’ô„œYQ&"£œhËá">­Þg¶½™e×/ºç©O˜ãFÞù–È’g·rŸo'…ÔÍŠ1—dåíÀL*k:˜clAak„²[TÒ^Èæ  õF M2i„Çédî„ÌÁr|a-Zt4¶,;³Ë.tÌÔ¸Gç²^&SfYј$›4)c˜ue­˜×5ͬÃ)” †%…ÑñI7Äp¶® q*mèT4Z…1›µ¼Ð—ÕÓ„p‚eäŽæ=x”5ž³v8*qÐPov8vÔ™§~Ý~&fÿ™“º€$sèÌè3_±îZz+i¸}Ï€,È ï33¶=T~” 9ê‡ìvw»Üâ ð&òêyv…™ºhUnà ¬õ k$0xœ¯BŒ?z»L#Y+;ÚëaÛ]Bò®¶†äžÄãwÔGÕ—ðòCPíNç.ÄõŠßL ‹–/BâÐ:"<7k‹qKK9o+tlZ&ob×db¾Ú,æVì–¡c8óÉâÍ9Ðpdx)^¤øîgOí~ ¢ÔóЩ`ëz ÁXs÷ˆÜ!ò=ìr¦ÈxãKDwŸ°æíˆ–ÿt ß?z³ ÿnRÄÚXiâÓj’¼‘f)ºmbÑ#¯™×€ºìµUzKÎXö«-@Ès^¡¹!W¿h˜óPÁO³ä+R¡j'ªqè×õÍ} éc’NYi]ÜU¢WÖ±ÃOX Âjêz(‚søÁ\¸i¾ŸÔ^>êóš–6dW÷ÿ!w¢‰<|&.{~¡„Œ[ø…dìû7á„©Ô‰5D‚ãGRTµOiß;OáFA¤ Iñ!eKLõúU@-{;™½ Úl¨OÞ£kK%ÆWžºeÅÆÞ0«@=é9³“V«ƒtþŠÙXænÝ:v =bvÍí—Íï™GÐz¶s.LQÚʵʇ ö¾¸sts8GXnü™j6ï,/Žêïô’8K»IýoÃZEãÁ “WXçüÎî9s]›rl(¯:”ëêepK×è’ZYV zƦg°Ú}‰WÑçT|LYÿ%Œ~µª5Åfô±=ÛQî ^R>ê/Í" r§`Ê#@=œxÒÁ ˆéI) Áª#iS?J7’´é|Èí7g:XDˆÂLIýìÁ›|tðˆžWݽ¤è“üTÞ(¸%¨8•§ÐIGp_·P rm²og£ïíßß0TN±endstream endobj 97 0 obj 2667 endobj 101 0 obj <> stream xœí\Ûr·}߯ت»Z~yB •Z–a™-OÎQX–E”7VÕ2KéÏeºÌUæTáj±Z'ÿZ(¦zyr¼8yøru¬Uæ*Ó«×ÕS:ÉW[[ZR)N³,ŠWσ8Œ²2IV_kê"Ï•}¨Vë`†Q¤t¹RdSQ³r•"C§íë"^½ z—Éêm°ÎW×uõXGNý[¶¼å g^ƒ ¶ºã§—T‹æ©^ý•kJªŒÉñmñ”‹·ð©hvÇÅ-l¶äâ͘± ± 4ö„‹ßrñ÷\|‹ÂÂ[8 ÑõK.î¸xáºKø‰÷\¼®}*.É ÓØøœh¥¸XpñüBk6/´q)o<0vݺµƒ¡kÓ¡µÒ@[wPóï­…¸ K*œÊ21B¯w¶ê¬ÊÐqmkî,–v¡ªÈ¯ûðÀ6Z24ˆ 5n½?‹OÕ¾ëH¶ØTH²BŽ|±TñÓ‚ ¼‚uŸë’ŒéH›ðS$©ˆIfty!»Ø. á–,Šá dß}cWTÃqæ§AÎ,Œ)ƶ^üs@¥’(m½##èï™ÄÜ­P®*vbu‹Ý¨‹[8‹§ãu­“ˆ§—&(]ð³×cN&£Z¾=bá Öe³×ÒÑh…V÷qñô±¿ q–fÖ{´¦f„y‘äòáµ3ÝIEÏ(òüܸŒ&—)3>çT!¯m’‘±\Ú—Ý«æàÜ_¯ê¡©Åâ!ã”~}=î)ïº'v«=µ¥£…—èä2$1Ã<´ù~Ńtø¦JªÕ+ûø¦5˜BWÆžB>¡Ê0!í~Õ«ï¶ŠgD ÿ_‚ufIš—rgâf¢7p&Äê ~,ü^Ôu>ÑÀO&Eb|é˜ ^;ˆ ;ž–ÇEfBKV¶såäs@ÙÐðt`zßzZLCCåkŽS¼œBcXZ×ýš=P®înKæ§©ª$ã0F0ƒ3dÃ•ŠŽwéËYœa¿ÛâÝ&|â kA¸áÃÖÕRC éjº÷g²G½ÐÄ0Å€—Çrº€“‹ÙQ÷}ÜUÀ8vKv©Ý3e«N¼µ³vm©oU Pðá¿? ñ:| à­kf‹¨ž‹.f4~D¸o?Ár¶^^_º"åú܊ƯlÉîwª.㥕£EŒ¢ØÙÝœsSŒ©Å(\>¨b±ºÔ!Z¹@Á_s²~ UJ´ÇtL4 ÑúÄØ”Xâ«!|ïä$êæ©”êô HCàÄ(>ÈEÉL⌧6öÊì´u  C7³†ÓÌ5 žîD‰çyÝS×~|g=àÎ4Ô}¸³b"°cøÐb’µOðâÚnÏ$¶ÛkyHð.™NîOjDlÇôâRUì‰ï)&›ts 9¾–^€Á ‘‡ÑËNü1<ìú£—6" |WÈ¡fæþ†N7US·z‡îçÓ0Ë '(ÕŸI@2§“dm¶ ið¥æqÐ^nUAãRó‡zÄÎ'E”w¤Ù„ÜÚÝÜdx¸ þW*k†Üø]‹M:̲ҸÙ(‹» ™¨©ƒ“6@|n¿ÜßA ?Wh‡is[œVà«kOø ‰Ž%±fÍ* ÚøI.SzÊ×;À]Ѝ¤Ð†°­¦Þ¥àsãSp–ÌäìW[ú–¡SnnòÖ–^‰v>ÈÏg|gK;Ðö·ƒ8ã›éXG­Z;Æ3%TBÆÈ›7ÖgË'à¡Óå¶óÔ›/u0M£Tàù¿¢Q*ØÜ_£xN9“DÉâ%Ju/»¹‘]ç3Ÿzgì@•a)¨JLÖw\”€3xƒ"iE¦ª’ÎPLi5âQÇF€›}jdÒ‘é½Ôˆ—Ϊ[õ©ÛqWðRÓSÚ«JË-vˆ‰Ã¤¿È2gŒ}ïÿrA—ês#ÖÐhðE‹1¯Ùˉ·P´Þë‚ F]x­n)Ì{ aH‰T×V2¨DP®·s ¡–8y:ë-„x>QÒŒØOh6eàW…èÎèÞéÔ»þ2Prò>§ŸðLPRþzÌúÑt0Uâ9RÅÔ é–¹M¦¥—@¢l¡ü0¨°ØÊ(måK@_ï@‹ÃDÒôÄ:»¼9ù¤€&Š(`\úô5£˜ðC+°Ï09ÙŸ7Hy½ç;®â³›±8´Üð·î½[•fð'{ûË!pÁù3¬Ý&Ðß&rT0boÛ ¨ðÄÓNèêK4ž/ª5L£(ÉÜÀp@hºŽî»ˆsÝÉ¿­êøl³9æˆGì n OÄ\eWüh?Èýh‰+Î ñßEÑýK+æª÷ŸÎüÿ”òRœ؇ŸÜÈiP4 Ë"ã‹|HZ^;ëaμÄ5a|áßW¤CrâÀœŸÎ’(îs³Ü9ùÑw£¿Ò½ÿYjóûӜ¼Zý5°·àþ¨´â¼Žt­g¯Éô]¶‚^üVáo6ïòŒß{w†[§‘Ò¾ËDø* d:|¤ˆ¤ä1ו8*ºnÙó.a6…ÿv‹ú/!~/HÛ>ó?%€€5ˆI0v‚¿‘y÷Áçcû°ýU?-üwä/´B±êÉ#¸‘_JŽ©1ølŸÂêEr'ÄÏ$«Pˆ÷çûôdñ ýûoÆ$endstream endobj 102 0 obj 3136 endobj 106 0 obj <> stream xœÕZYsÜÆ~ß_œ"d/„¹ò“LÅ WE‘¶*r(.I)¡¸¹dœJå¿gÇtÏL ».År©Z³sôùu÷ ¾deÁxVº?=qöyñâ­É.ïÍpööuGÜ^.¾,ªB¸ÿšLŸ}ξ_Ù…\euQëlu±(‹º®JÓîÊ2­ì¿k•Æ Ye«Ï‹÷GY¾%·ÿ–G?Z²PFÈ£—žú›§2O}òÔg‚:÷ÔšXqê©m²‚k{ÈßW²20æe°:Yý¸°¿<[X¶Uó¯Õó÷GÿÊ—¼à•1ìèc^†qiìV=u›/UQ–Œ×hÐÊ`÷0LÖ–%i Q7¬3n§rîH.‹Ò¼Óº¡/ Zt ƒ[K ¥u)ºEÍh£°ê._0qM®ÜÂÔ0>Å ÌŒåWsRWxÛK¼ÛV¨Jà 60áÎEÊ©í²ÒZ—ÁïX0x”šP´'ˆ’uþÑ8ƒóò¥,´t’¾ÉEQêÚº1ó–SÈðžIÒÆhpC¹Ð¿s^œ 5 ñsRwh4qÇf™ç̹Æÿ,¥âv×JãKÛ€›þ„ûhÛmNøâ†d£óPʤÍVÍ_÷Þ¢7Vo¢¼6VEVL–jÀ»xÕuú‰ŸëŽva(ºQDž7~À¥(„¬-¶­ÖÊÞÑ.­¬Î’¹rÎÇÎÞFס†ºó§`¤Í5)èG˜‹`äK·#Ðд@È œ»–¬òþ®XŠdÂþßÚ²×ð²ƒÞµ‹½Õ?Â,ÃÊ¢êQ¥“ ð7£iÆþã©çžú/‘€N‰Ô-qÆ]Bý¢ÉæyÎÝ`% gZ£¨¼ì,[wnä6‚_/¨Ôq>½r ¯ôÞ»ÐÃGàñÔ†kŸzè }ŒÒ¹%©‚<‘+{ª¶³5O“cÙ˜×+ë” ÉuN…zÄl’w-àX°³1ÓN’ãµ|@P)·•\3%1ä¡àü@f}Äm! , ;ñƒJ&Hw(û\’Œ%Yïëò÷2æ<.,Äîº`G ‘T ËÞj1ˆ¹*¸ð•„"tCý ƒ7dÄFœ<ÆKÆÇÖ61N3jfð6銎9jº0•MÝžm¸>² Ã•¬è ÛD¶¨lÓvR4!:¯ ¢f¨ð£ÈpÍBaÇ–Ü€ž»·r ÕÒ¾ªM26/+' ÌÊt®…ßÏ`b‚zMuì¹#…"´#ú»Œ'écÎG|Ê6üf ºOЩ½Ç&÷n& TLå×kд³ßá ôBÐ%ÆÄÖ2>bÓE4+ÊJõ!Mk!ΙMœ#£%/ˆÀhK#£Î˜ ncêè2F•Ö£t{#íÉ…TJ7µƒâe¡lt»QV©ªñòбºª¤è¦Þ:´2Üæ<Ú³É]#-0JˆWT¥6•T8â@§s<šð2_Ö.0¬âßyßvט6¨È½nÏW-ÖÙ<4ð(ÐÔïÈЇÚ’Aé¼ R^ÛÊÛý*ÝGkr™Wô¥[RÇ®ÉÍ$½Q‹ TÝÛ+ÁBw›dˆ„t#ð½³­)k†=â3@A?2Ó“ú¢ØQÞxnlh1鸑~P¦¡Õ¬ïb‹+¢øZé.b”-ÿºô¦­S]ðÒ f*œpn]5n‘¤¹:î÷ºÆêh»1‡œ¢÷™sr}–sc,mœ–Æ!!¾Ïxß)µna•€Ë€¨¼¾YÜ˧·ñÈ Ñöœ!щÄ)Hp]:†‘Êj·Jv\;pšSq…ç¬À¼‹î<“ƒ áå5Û§—›Î±ª¢*}›?æïi±¿£›ÎK=-âbhNjé*#а7t©M¹ìŸ½°¶¬¨²™€ i¾¡lÖÃs˜Î\³_i±3!9¿èB(¶’¾/ùä;Gfj”¹ Gk[Žȃ(Z’(‹š–$Ô¢E»úŸ(aÓmÎÎ7 ´ ÝVÅ•€Oƒ§À|tÙ‚º@ +¤c@Å‘û¼Gœ$Óƒ·«€„«Á©~…N‡ˆªO=6Œ'î°SD.B^G¢:†v,š«ûœ Û cRŒ‘·@?à òW²Œ jHª|ŸUCú߃ÒcÌOGmªv—N/CÜ<†ë¨“.AY\QsN0~˜£ªBWrt¨*ànœJÿ»ôO06窗šî›X9qi ‹rjRêèŸP° yÅÎ[… °~Ž:¹Àk´%Elu–Ó>¶ˆ/k^è»tçÓNp }Ö“ ˜øÖª+F5ãÓZ²©7&ózT’´÷7eac¦‡‘¤Ãl.VépF4mêúw)zyrz¡˜Ç ×N¬?‰GòÓ·=ö}4¨Pÿ¬w…:Úá[\†x½¬¾nµß~Ñ}_Òº .Plc?z¡¡k•]‡$òJA)ª@G>l@ºrŽxƒ½Á$24{gÂÅ7'iµ!+Þ¿m6gSÞè†Éö•î§^%c¶`YúA|´3ewV‹ò ¯ˆQÞëF%ÞÁ‰n̽¤ðUë;O{ê$¡ÚÞoT3â-óšøê~½¢?{µ‘`‹ÿÊDJhG5>ò &|òÈsR7@ÞîÚaKnv *÷Ê•øa4]z!)*Á—É ½Dºbœ¹^kåzÔ¡ªué®ÌnßD*ÙŒ«ºW‰þñýÙUBMaå¿êµ§¾!´Ã“ÀãúÒáfàtö³„úÚM8:Øõ"d¿²…LáÔ¡cºÿ—2¢xŠå.)¾F#ˆßæ1bˆ7âñt>Ûx`oóÿc<ïÁbá³"ø<øl€¹ÑœNvدcÅŽQpxŸœOw(’tà_á“p!±”¦ÞCÚòׇƒZ 0Çoö/Pê r8ö»ö–ø¡Íà½éwÓÅøzp œè÷û·Bšão…gûÖÁ ÌáÚùà 2cQVP@*’Ý­Àáœh¢ÒL+BåÞsžOçý ñ[Ê½Ë xÉ7_%ÏrLÍßîßE†Úöɼ ‚$õ½Ü?ó"{¼'Šw<‡T¸yÏf\áÞY^ìŸwþTÞ9lÔYÊý3`8§,£™§J|¯x¶ÞÅSy/§*ž’ùGf!Ê=óX-þjÿüáŠ#¯endstream endobj 107 0 obj 2609 endobj 111 0 obj <> stream xœÕZÙVA}Ÿ¯è7ƒ˜aöEAed¨€$pd.Çã¿;’®J¦¦I¦:‡H¸tº{nßZºRáFX¦í+{uAýÒ˜ª…¢yg´‡Em¹n›Æ™nöÓÀ¸~)æ’t¡ã‹ØŒ‘4 ËŒãÈ vµEà§ǾmÛô"‘\ûw¢êš~èz1Qu<;4£0ƒ®å¤“< —äÔe‰&%²$rrcNP°%‚¹vìbèv¡“Î=LV”º“R_3’çûO?y;wŒòä=‚¼×åîëçž?Eyî¾Rø`”^ã³É*áCýÜ!ˆ<6÷P)|4J/IÞ&ÎÑ%ŸqŽGéé.›³M.åå(õ†à~„¯¢ÑS¥ß¼å9ÂQŸCšczðc\É©×ÁØiåÀ MÛ+Ú‰ö  •çAú™ñáìôq¤¾ªÈ+=â~dž›¨drAÜÕÙüíëýˆ®\¸P£›€ÉeTeD\غ³ƒ DÝÆ½õ§íØ–:C™2HÝ`ʹ2L‹<ÉÔ¡b{¾ Á¢»ŠIªê¥ÍxJ::‰ª3û"©Vï¤êu)é¨VÛSµ9Hº\†)x©Ö0ÊåîŒà»2ùl‚!@eê\á)éé$ªN«¼$ÏxOÍTJúžG”Fˆh.Ç÷HºÆ3¾­“)Yi®óÂ(ÔIPY_nðˆjµ¹º'³ÉcÊOˆ©²³ÅsN~ EDÕõù6/àùLÑ6²µRãIÉOÈ%QK%oóeˆB¿’6OxaÄOˆ©²NÞá9§VIÕuò.ÏKùÉ1ÍåÐŒàOJ­6W¦Î¼0Òª¤:u~â1å§NÔ„QÖÉŸyDù¶GDÕuòþàLGÒ-¾u›±>8xbڨɅºU»Ö®D ± rÙ£ý.t+ãûð¿3q‡~SïÜ"­3KZAä,{C˜…³²'óõ‰µBÍœ$ÏôH»@ÕIÀùSçjnP]ä¾n‚Åãé¯gMhBWöN¢ßÄÍGÝKtBœ½EìòØ¥.Ñ7%ƒú,'+žØè\¢ÊÇÈKCáž‚ˆb®ž£‘Á#€·ähÜ÷LÅ^F? Ùÿ„°¿[yAî€8\µĵÓma‰äÄØÇ{ ’ãù04·©×xOŠƒ6›xANh)Ôëß·ã;aä¼ ÅiX–iYv¦\µ+]µ“}2ýÀÇ ØÁõ!ÀÛÀ¹aí¯b¯ì™×"vGÊw«‹~„<-üIlØùß/tû2äãA™Ã÷êVÿö=¼ûqaµ«';ÁÿFïBžÒ÷ýËJÅ=.zÕºOíUVùᔘ!ÆÀ‘N‰G·ˆwo ‡ S˜MŽÎŒ(Ýõ+Û½LÛ~¾˜ÛéëàzýÃendstream endobj 112 0 obj 1081 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 105 0 obj <> /Contents 106 0 R >> endobj 110 0 obj <> /Contents 111 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 28 0 R 33 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R 95 0 R 100 0 R 105 0 R 110 0 R ] /Count 20 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœy T×Ön!tU9+m3H¬BÅ Ñ8ã (ˆN(Š Š‚™‡¦»™ÐÐM7ó b…Ј8gI”hTr’è5‰×{£ÉMvåî[ïT7Òܬ?ï½åèӧΰ÷·¿ýí]”UÊ‚]½?$h¿ø÷$ÁÁBø 0Â2û“$#(·iûæ °@,Ñ«êœYk!w(< qC(K ‹ØÜÒEáqûCvíŽrë·z½ËøñÌ#»ºº:n{ÿãâ È]û?$  Ú5Çq™²ÃqWh\ÄîHÇÀ;ƒvŠ­ Úëè~Àqì"Ç)“'<‘ü˜â¶=:ÒÑ;|_¸ã ÇÕA»¢C÷ÿ× EQþ+ÜãöíØà½0>|çFŸEA›V.þ$ØwÉþ]«<"w¯öŒ Y³4zí²{ý¼bB×- Û¾~¬Ë¸ñŽFNõÑèIN“Ç|üᔤùSLs›î?cóÌ-³\·Îž“0×yEM¤FQ›¨•ÔbÊ•úˆMùS¾Ôj65‰r¢VQÔj25†ZMyRSRk¨¥ÔÊ™ZK-£¦Rc)?Ê‹šF¹Pë¨éÔ8j=µ‚šA§6PÞÔBj&5ÚHùP‹¨YT_ªÕŸ@YP©>Ô Ê’LYQC( 5”¢)kŠ¡¤TÅSÃ(GJF¤l¨¹”-5²£æSöÔj8åF9PP›©ÔnŠ£6¿“VQ7-æ[\é3»Ï÷–[,XñV[­:%+$'igº‚þ•YÈè™ßØ(öUß%}¯÷›Öï\ÿþß (è0°hÓ ÔAÏç±;ä‹¡ÖC÷ ëÖ'¥2éZé7æû\¶Ë†±‰´yf;Îö€í?í¦ÚͰ[oWfwÞîû¡ö—íßow˜âp탾Ô>"eDÈÝãÇóWg8îv|3røÈ #³FþgÔÔQgF}1Z2zÑèÚÑ¿;Ù8­Þ Þ ƒm°€ÙÍPÛd ;…JÙºB«).QhâxìGÇ¥)“’ •üîèâ§ä$ñ“à [èr•.9I)çð4\Ëg´,´°æ@°¥à¾²+ØYÒëQ˜LWj5%Å M,LãB¥o£ãd­øõ{vEKBé89yB'§+ ÉiäHsé‹à,|ƒ §Ð@A«¤”&;cŸf¡Ü`ÑóÀfYÂI›ÅØI‚¥d[Err¡‚,’Dî¥-.Vhãx/ …rI‰yYlIÇ›XÎ_¦ñtüÏ‚ç’Ï{Í`èX™¡UVòÆ-Ée_" Ö`m€šf[é#˜#L—¹‰7gUðõøEkZµ’[óµ €>nÐéËô¬ôÌ¡Oku Ÿ¡J塨#Š÷£0£Æ|û>t¹éñ|hèÇK®MõÞ³bG‘`€løœâ¹ÁVÚ ìÈO®«òA,>~¶ÃÃ~š¶U'ÛøO+Ë*P1[(/LSde§)¸ þË#‘™}<ø7X>ì„>Ïî®SóÒ;ñ¹Ê‡"Ma%/mxÅHïT( S8iC¢2-Žë&ðÆ aL%¿=ÀÕVú#<ƒ žíDf:Ï™íâ<ìúøÉ¿¸—`QGS;,¶„ ‘Áb¨–ÔÓå:mQ±\Kð8Ðlá&Å1x"ÄH>#Td¾XãŒ÷ˆ­§a)®–à‘Œ4j3hF™‘ö‹î,…,¡²ž1ƒÛE¤q±J¾³k¯ 9’‘t¼éåü%Q½ñX®ïAÔ®uåt»Cc°ø’,¿_PË6ŸYyxZŠV~à¸%| òag1ØãÁðÁÍöÃgÏsG•#«OÓÊéY ç·|Ã'ÄÈýœßÁ@þ.ýÿþ,_>Yç“Çå$(Š[¢Ñ‘0²bÊ•ÝÔnâVøÈE²#¤Þ€Ôñ "˜Å+”û‘O„ku ²€±áÚ4µ.GŸ]v9ìbaÈO?@^úúO|…‡¸mˆðXÇÇeÙ»‡î.“渹Œûàõëo:ÿeB,4À8ƒ…Pþ2ÔšU•x|÷³9mãɆ9Oƒ°ÍïNà𠜫ץéÒTYéJ~ÇÞ…ÑÞShÎí¤ßXƒUåw¯¾AoP§oÙd¶;8‚ ƒGŸf¸Ú,Gº x1ŠÂ¤$•1ðHü•š'Â6É5Ü…§2±Ú´’"-‰èÇ3ü¤k›DëËôø¤Í L3†È}&L†m±+vòxV` ÓÀ S Ã#I¨û¦±13X‚é‰À€#8þ@~±¼9#“àýÞĽ±NƒÍ[²ä(þ2£>+lÃáðÞX5tEË„hÍ\B ‰'w¶^‰Ö± ™QSÇNø³7Û ð1 Ê7ÂVx!“>¹åïW7ß?÷Çýÿ1 =¾RÛÑÊã¼±L¼)•Ý+}]irp,/}³N(¿héFíDZú{F5ºu¬ó<âÖÇ?óﳦ“«©I²ÿ^Ö¿¾pædGµ!#ëwN…P.b‹È6…*mr.Zà¡ BìÇË×-ä}¦1=‰õ{üÂ¥ÿ„ywšÅ¾ÝyzµòL&}Jöu…Ì¥~ÑùãO3ïŽâñ±^„ÛÝ›£“èž[aކ+ÈoæqÇOE«§ñ݈Zù­ÈôFÂujþÝÓd+½@¶!,í@Ç™àSÁ­³”$Yv‚ FÚ­.t¼F^,Æ|—;ó'‘& ~0®P&'qz8è+æ½ECÉî‹H`ÞP‘ëY1SŒ‘ù¿oüdŠÌÛ„\´ú’T‘=àZÚðúœ9tÆá ªJ=¥QæeˆV׫ǗðŸTGëö¡ È7ü£YìŸàó¥þN8} oøžÀ§cóúº…ØjâDÌbæ§ñ`uïb]ÇYËϵ~žîK›QÄÐmèTùɆÚãÅuÈÀ’EÆ^b¤¯{ð`„ѼÙëN£nMt'0êìüI´:f ‰Mƒµ1öHÃ5âTNýÈÌd‰0è¶$߇£ãs<èýÜÇ^Êô RÚ“#èÔy99jîtÑWúZTƒÊ2?•9&?¦Ï,shþ¢®ªH®KÍÌÈÊÌä÷ÆÄ+ d¤È,d¥±ú°¨¼}Pbv¨Š=/¼ý¿Cº—pj†aM–½áQ\l†Grr<à+&¾@^RR`†ß_aãO(5¥^Â<“`¨‘²cÎÀɳ¶R½°XÉv×”®F¬ÔÃe¡¿×ÞŠ}Gãø£ñG•Ê+Ê#Ê#IGË÷£(Vª_çµsÚŒ% ­œ†‘z)tDþy‰ŸE>Çj%œ4»X£­äõLsðùøBï’çWˆ©=Èï­Ò¯ÔL-TåF“µ§–—o¾r¼óáça[ó9騄\¢È¡X£#ÈÖÍHG ’ÌIõ)id“ź•ƒÑf5bØÕ0Á|éK]•[La/¢ìÌ2,´´¾øòÍO˯ û&tèêš÷º:€é-'{¬@’ÔÓ¥×&ùFùl0©BQß­ÞŒîRËÈ&w·¬}§³WÍ´F»ÂÈðS+Z~8~öºÅ~;ë.Ñp9I)óͺѠ¶‚x˜ÈÜ»í·Òkö—îÌ·Âb®ú7Y’ü$+ÏZZdôð:‘îSwàñ8zí'»ëIVtšú >àï3èúÑ[-M -å×Ð to{þ6¾€€¬@WÁ½b*3´‰‰*’Ù“˜€Sk+–!/ä¸as˜;Zĺ3xÊoc`VûWõ׺ Œ_‰&5[ÂFÂ,П©Tu/€û»›Y¸¤ÝÚýüèÆc:…^žž™•¡ä¶G.‰_ƒXÀm5ï}ã Áàj)xÛ<#®©èåš>æ{^ =ðý) •l §cí"¸/ùüžXI?'Þi£_‚þÖ›Ü$dÓŽ MoEN27úuUþ_8é}ð,6€ÁšüºI]Ç 3mþÑ+~×B}×óó‚½’âÖr©é*JeåÚ4}®Z”$ÕœBìý {6ñ‹ßÒЂ-„߇N÷ÆKÏ/ørýw¯¹ÚαZºbØÁñ¨o»y÷ôï-çTòS&[ Y¦.œ;l³„*›«4 •’:c…[ª0y:FÙMiì×-ɤ±-yd쀔¦Ä‘,7¡‚¯£ÿ«8Ù îbVüDöäþëЖ•÷ñVûïÍA¹†vƒµÁmãš`?l²+q\\"âXd¢÷î?Cã8W¹zAkþÿ_â|PÖm°Ï" 3ñ 1ž^ÂSØ'ƒ™0E¢¡¥zBØÂLXgi<Ûàá`#i5Æm‰Ñ$–fÏjh²Þ¼‚Äd-}i®/ìͧð¤I<ŽƒIxœÄ««Ø™g¤õnÞk‚ùM;Þ‚ù!*u+’ðÜÍ•K=~ÑÓW*7výôݱBÐ"5\m<^sÒ¡¾.dïtöjˆœ¿o¼^{ˆ@ædÝž ^Z€ïõžðYœ¤¯o>¿jûžwrÑçöÚ†Ѿ¸öÏ}l uqÌ2é­ž. -Mîªì•‹^¿¤¥m‹ÏÞ¹çß¼…¡0dÂ<Ø}íÞÅyðøBöó£Ù£]渎;÷éÛ·ß<ébadÕsƒE)øƒø[Â`›vXÁ ,»œ‡~A@ZÕ«-8ÆŒ–ïèÕ]ØBø@âM§ëƒ5B„äY/¢w2'À*Ñìp°ÝRø˜\BŽ”ÊøˆÅ¸ÏNÜ9#§£oc«CA…òBÄêÕÙ¹YùüöýŽ~¯ýõk”›Ÿ“‹Ô¬>­0•3®õ…%ø’µRåi©(+/ÿ=æ,vÃF{àA™é( e°©…izFói9w,>ƒ~ègôËÞ{‚UT£¼PŽDs‰å¸à%Ãö§U9™9È^.W¤¦jÓ´Ù¼:ì]±•Jž‰2Ä*E¯ÍWëµÜ°|Š- “ó²4È^¯Óê 24™ül°"ɤ4¿ éíÉiå™Êì¬tÛo“GØ´09I™k¼BÂû )–¿óÍ™χet…©ßÇáex>ÓÓ†ùØK¬OKŠÅútñ‰|'Ùbœ—IŽ˜nŸXœRV¬QWé9å"ƒ&þl?7deÌÁøòè¨øøè ”ž£ä*ð79±Dˆ"ûT Í©bÃñÙ¾¨èððCÑÇøºöðÁ£G#†ñݧ²ÈI{PÜ5ƾd‚ð„Pô‡Ä#õ¸ o¡¼ÙW B]^M=Ø}ncÕÉË{ŠG«¶€sÐW¶3,`÷!]JF59zR*ŠãÊîq7óYïÑix¨d¦ùEÄS:ï—(˜]š¾XSXžÃ•A´äg:Á8šMVÒ\j Ëyqü±¹“æFï…¡2qµi- M-øc ,O››‡òY\+OSf&©8ü·ÿ¬ÈPf§Éui:]~~±FtÞ$ðŽ;P“BúZƒÄgø…\¸ÉVZ'„û2©ç,¹ïÂá(¸"øXì‘„ci7“¿J.K?”|8±ôR?I‹V,˜é¾´¡%ƒ“./ÏJ×$9ˆc~.‘j "Õ´\sdߥ¨ÖL÷g¤E¤ð(ï‘Âá]ñ—WÑct:öTXuxe„vqÉJ}LALÑ¢¤t„½v¥õNûÕÐ]¤<M$²Êøþ¡‚¿Át¿fŠÍævÒ¨Ù6²p¥ÂØ1B;±É‚\F,Ó„øUS…-±åLᩜ3)ib ,Áò!P0…Ãß½–¡›·µ®?QÑŒšQ}ÜÁÚ-•žh9;…AËc=öD‡„Ä’ÜSY¿¯5î6º)ÆnJ¸7À° `¿k<Óø}ƒ%tAŠlÙÚµžž—×Üî¸r¥ã®ß¥%|°ÕùÆ [wnß²e§¡õÜg-<,L–-ížxëªq¢™x®Á81`s¡õlãg­<èq¼¬8?¿²:EaZFFvv×5JÊHW!¹=am®:7WcÓp‚TgA†}|2ҕ₆A!,&Ïl×ǜؿ'2õÚ.‚I¨µ¬k$fä¦çe"{RedCÙeef“ƒ”ù*m:x£ð&EFz:Rأ̜Ì\òÏ.WEø‘ÀÏQç±Æ €&ƒµàjˆ5¶1<‘åääåä Vúí#mW‡¿à+çõÂvp1 IqHÎéµyyE…\mýùÊ+ˆ}tÝù¶ðqüyBö&ÄÆ§v³˜ôÎëgËæ.X°lÒÚ5U§6óÊüŒ<båiryrQR}<ßq.ùB•¨2«0]›®ÎÎG¬^S¨¯Ü]¶ÍËkÿ–õ¼ÛŠÝÓfY¼èýÞ<ü¨w žO^ì·Ãu§ö“Õ´è+JËŽŸjª&øîÕes§¯^êî¹áì³>£å¨Uפ?8™²$=ˆ0"·äé9™j$S$kÒ ‘½NW¨SgågjŒê¢ÉXŠÚ.4Ëð´.]vF¶ eÚ^~~i>3„rÉ­.vcÊ}UÜIhaº-D÷ÔÇgÉÆ™¥)p„Òíùü¦Û@U1!‡Š}Ÿ0Âp/…þÐ!“>º×PÛÜ>üåì‡ãÇÏ^0Åçxà×ÁœôåÂë— ÷|î/¿<ÿæMÇ΋ OpnPFæ?¼±v±—Ïz77ŸÏ;¾n¿ü€Ç VÒ—Ïn®vs_ê3ÇuyûƒÎ›WŸµÎEËð©HËÊD*ûT¢´ùy: Îà ÎØ[#ÏÏÔ±I$r~fJÓ·=$áôi†s–‚/tÉrâr‚$Mïð­þnsý X§–ç+ò3rÒE†P$ÇJ8}ëVMëE¾óË“ß!èËÂÂY0÷7¦Æ<šùêÞŦ'NuY¹55.1aﮂ»duÇ7ß]½Õy»mëÌ#¼: eDßA ½`!n²ÿXmüÃÊh?¢jæ‚Ý_k;9\$ãû‚ƒk÷™Æ54„3i¾FñÁÌKþ[Óª« Mšv!ù˧OŠ´&dÈPý¡#ÇÕ•y5*a‹Ò´‰k·ør"ëïÜ•‘­P ”¤U•eʪقŌàÛ¼år©XÀ(ócs¹½yQŸ¢z1ƒ CjõžÃ'jôUÈ ^øäË„é8)?+?+Ù :· 7Ï.W“GR@AF<ÿí€üÐå«óIÚEyÙyYä¿]fRM‹2²Ò3Ù÷Ê9ŤœÕV§~¯‘ašá]³…P WˆøÖ–”ˆ}€._R‡©’’´ŠJ^ðÅ£{Ér_s;(¦J˜^ÖUUUôé~†þ§ 0 HQÿ$$á endstream endobj 118 0 obj 7518 endobj 119 0 obj <>stream xœUWXWמvAYY”Ý(‚ÄŽbTÄJS¦¢,E–",MAŠ.XAÑл‚TQD4€Xcï%jŒ†ßÄ/äSs†\’üwX4_ž‡2wï½gÎyÏ9ïyW@i¨Q@ßA®ˆ–+ƒ6ùN± Sø9nTm‰’ó;“¹ÑnŒg¤Ãþ˜Ý'4¢VäügÒVGÚ…côæéq’p^ö§Ô‚؃y‹ÃÂã"‚•2Ó5.îf“&MþçK+++ÙÆ¸Ï;2[ydP@¨Ì„5Š2 Ä”!5$‚Ò Ž hÁ"Á5µ j{Õ>¨;©§a­±Gã¡‘ÐMØC¦k‡x91äÎT2Ÿ44gizhÒ|¥ùÇÐæ¡ÿÕò×*ÒúAëöPm©¶•v™öaÃ.ëu"u~Ó5Ñm¾Nêp­8 Ôû˜|w«e±{¿Qg$ÄæôÃþÚôx¡ËúûÄÝ(#I¢Ä‚ì½YiH|ÕÃVƒ•æÐe±"’·ÆJ£"¢vº“í­øÛÎB°§E½QÞ)a ÑRePX¢ búŸ{ÆÇƒfÉcÇ!ËõNÖ^+ÐL뼜@ýÐøìš¤¹«òWôŠá³b#IÖ`¯ÕûØz&¸EúJ®> òNG¿¦; zøñTe~Z7ƒ¼dF¾Þ0%ŠåÌôI U ÉèÕø¨²Í·d ÏEV>î =mC-‘#jÂz7­>Üh-i='ñçôضSëí°Þ"LÍr jº ÀÚ÷R£Fr`¤àw0ãÆÀxõ¾\4»¯<ý:B¼û[`]™›É\‰íôõx4Â,ƒg|0ƒ° {aÁY 6¦C-lÝ'!f¢GÛÇ祯›nK›ïwV?E­ètlAPvÌþXäÇCI¢ø 4Bóõ¸>’Z¸Î—«ŒÆ3ÿLãËò*T86†ÝFb˜I³©èˆž~åàâä#u%‰c=‘8fº6ݽû öJ:X¸F=0”¤¢›¤bŸŠÛÍm:؈êй­Ía5MÎÅ6h>š§ðö\îàCêwƒ‡¿šMªhXo7¨·K°„Ž‘¯ðBŒ‹¼¶=ÞW.…”‡Ó™•‡s³sŠ+òP5ªØšv(># “2Îû â'¢5˜©sÕ}q,ŽL¼¬Lç[xc=„‡0x1¨cØÃÈŸ›~{%yü¶ t eÀAö /—Äâ‘ì¯W—b5ëðé¥v+¿FŒÉŠ› x\þúì5é…›ßÕ<$57Pú¤ÝGú†w=Ò‡:Æ£õ÷zï¼? º6Ï€—Jp]ØßËör‘BMÿÔ´~ªõ–qANÒ =Ãfð¦,Pì á‡kÜ;b NA6 Žôt¹¸µ£­£ú%z„¾U6o<P¾ñˆéˆkƒÍ8‘~αì9‚9ÝVìšä- Úæ¦2ýOƒ  n°ð] #„ø7šo7N‡.#«þ•=®Qµ”Ò:œXEiÿb³kÐóüâéçH|e¥åî=c v¤ïF{Ðr´Ü×Û¦èÏ…8ôÍdž䫬LÌù:{ÄåCqs„¢;Ã7žp½-”»-u¶õÇjh"ƒÕîÏëílÈ»Ò-Ùf“°Ìi ‹yúVHÓ ñ8:51- í@òRù阚¸³;:ÌE ¸[#A‹k7ýˆOŒc/Õx-Â: Œ§¹Ÿ¹º@ø\:ÀÎpî3gyîÄ‰ï €©¢Îüê?¬"0ƒÑ`*#ÂlÜ3NÝŸKØøófA;Ö³vˆ–ol‡¹ äzÚïuè“QoWLvŠÒ0xƒrU âð±XIÂÑ]GÒŠlDïOÛ…Rк ï³õÊæäNÄÀ×@=‡‘Wýêç”H'•¯=ž…òŕխQO{ULð®´=©)´ñn˜Òxm¿4„“ò÷IZ>È×ïªü¨®Ëè:°»Ùùïà=fሇB<Æ‘Ø_88 {É5-˜¬Þ7ÜÛŸké±i*Ï_WÙz<ãØ¾o¤¹!“þЛ÷lFQbÌü¼&½~^zþ𤣩òRî÷äˆÁ—à¯;¡joÕH×»@ÕËÏôâ6>gW6:NúãPÆá.h5ØŸ›qEqÕʼàRÏ£v[¢¹.aXŸQtNûÉünÄ\,Ü“,IBhq²sŠ×ÿE”›|ó*äˆä¹òŠH¢¢KN%Ÿ}µ]mÛv£Œ(%gì:ÄÀëB+j²ŠóòY2Ú+"§ª¹¶êúé…ÃßìËÊ8”¾?]|$c_úÑCÌ—pPÆB2ä ±5¹Žg¯…Ùü#™qy8—ñ= º`}[ó: SÝëÛ¦ê°ø˜ý<:~»éÌe$®+Q&¦%§&Iáÿ|ÞºÛtº ‰k bSã÷&HãžA {°‚4T®ø‰{³å’5.«% ¼*W¢eÈ;ÂiYÎ3’„W}ù‚ ¥N´2Iƒ ›W!¼M,ÅÔE¬ÉüJ¯‡q ÑOĵ`ðÆÔt×´ßù…¼KÔR‚½ÙŽëmwjÚ_€Á92jA‚ÞG|X Ø t3žpÌ™#là à1˃½¾6&÷O’Ÿ˜|Á;0aØDøŠÞ¾$ÀoeR¢«¨}sµ#Ô*ÀHU(H%§HYñUPз˜¼¾¿o‹ÿüS!¼Aß} •B×á²@CðŒâUŽU_ ‹…ÈbÙj<Ô×BŽ"ëÞ]@´óS7hµKßÖ êe@׿Ö–„ÕÙ|bóÚ•Jl>Yfä焟ª‹¿´@DU«´¸®"¯ æ:°çwâÈ;.5îOà½I}BszÓl« XŘðçˆ"ÙF¤c‰ú,n¥Kj+ AëÁ÷ßÖVTÖ5^(º†ºû}Ýõ±Åø)Øz‹ éì_šZo!¦­2ÄÃ'Æ+(HªØ¼Y‰i[,ã¹20\´ªit ôÿÀ‡ŒÅ$ð@0ž¦£D¡n³-(3­h#âj’ B7Ú®vtp .oI“IËâ1ôej<Š%“þÁrÐÿ¤ªó¢¤áBÉÔÚ”õž'Ñǧ- ןþlÓŽE³ìÂ=|$þ›â6ÄÏg€¥3ëæeåäUVáÇ4U†n´vr·”&ªèÆÃÍ íðJÙEtç ÄV4þØo‡ ‰2 –N¼_^ÛçÁö‹hlÒ/‚]xª6¯1—+7;›8.˜ëXR ýpz¦”HÕ=1dfmgLŸ.íÞ×gŸ^–4vßC·Ps¸…ÞòD…·¤ L…ÐòÃLÐxVp†”ÏKÐ'ªÏX½Oƒkb‰¨ÈD˜wö—±P6sÍWË$n3¼ñ „u<ôñöêiÃVɹ«åÏÐæÃŠ®±îÞqrINgÆ¡ìÊòÛµ':ÐyT—éÃà±x ÛR¸ÖÜñk›µµß¾ézt_ª*¥«¼fÇÇ—ª"ýO4vÇNfØ|Þͯw­àúŒ`Ìþ”Á ËÛÞ#IûU0ö&+¢èf>É<—Èé³§òó²¿ÉaDïêj [ξۅ5ñx<ÌØÒ ñˆØ·%„$Ø}”ÓjC,øÞÄß?¬hm“´^¬z€n¢«ÊšUµŒ9ùª z×^ºa¹ËZ»µA'šºZ[nH]q5+úØ^¼v¦ëJë5›N5¿¸öÝcé g,0$}êz7A2“¸­ðë.X±{·¥íDɌŽ¿ƒÚm®J®}:–ˆ0 ŸÿŒô è<³¬%˜ºîðáRõñó—¤I^I›Ã"Ý6„#úè\X¹íO? …eœ+˜.—ÏŸ'ýâ©8ÕÚ{ù)ÁUYÄÕòÓΫˆáP¯ÂcÚÚõÿ ‰è9 endstream endobj 120 0 obj 4061 endobj 121 0 obj <>stream xœ]W XײeS[tf4w—›ˆ0€ˆ® û: "(ƒKŒqEáŠ²Š D…°ˆBb¢WTÀÄ(Æ%×kbÔÄ„àRÍ-Ì{§ã{÷~ÃLŸîS篪¿þª–1&FŒL&è“°.&Uáâ©Kˆ––ÆŠö2q˜‘8ܘ ®÷Z¯^>œ™{ø‰17&æ&džY}c-Ž´‚š1ˆ1–ÉÒwæÍÖ%ëS´qñ©êÑK-s3fìÿ­LðððPGêÿº£öŠY«KR;Ðëbtɉ1I©ÓÕ³éÓ Ú(u\‚>9~­:"::&ZÚ¶4"!fµZ£MÐ&'ëÖ©GÏvRO?~‚ ý7qž612m­:0"i­z®ZÂÿ+ øͥOŠZ®‹^1ovrÌ|¯Þ)qšµñ‹|Rµ¾i‹ýV/IˆðOŒí¤ëâ:nü„‰“&O™êæîÁ0.ÌH&ˆ™Ïx1ÌÆ›™ÆŒcÞg2f<3Š™À80Œ/ãÈ,fü˜IÌhf 3‡™Ì81K™)Œ33—™Ê0žÌXf3›À˜22Æ‚±d2ƒ+ÆšQ0ƒÂØ0C[ÆŽq§É`Lè‘ÁL)ó“ÌM¶FvÞÈÖhŽQšÑ·Æãÿi`²Çä‚IŸ\//cDZ›8ŽÛÈÕqwù|&‘ÌÿÏ€)6 È5u6 7½h6Òl¹Ù.³½fçÌ®›ýÓìßæÌKÍßXd[4[ÚYæ[^4°*,E `%Þ+’ÝgcÈèMÐùM¢¸¬ðÖ²7–ƒ5}À¯H&~ŽB#:ÊÁ•Å=½‰rœÆbÞ›D9¸°M@—7‚•€‹ØgP-·[P×Ó;ˆîRŠUšô G3q¸]Yé»$ßÉíà–ÇDmÉÎÙ£WŽäŠwäí/8\ZzRzC%tÅ¢ ˜%Y·Â@m£hh£ûªèe§èºÚP×qùä*%>¤+r`æ×œgPòü(¥¢áG‘¤‚~¹ì¨ÅBx,dÂÐIߣ=áqŠËœ‹š'ïÁd°y|„|Ú±zo]òÂ{…_}Ù| ñp¥ªàdEA ùŠK.ðâ-{Ç‘â^/êš›È ½rôdÑ·â`Ø*G )ø'fôËÁ“ì5†¥îü…å4»âVñœ€(CoÔt#ÓÀ½d0 üG¿Æ™ª” ]_hF8,óþðƒå7zzšoÜV p)ÇÁ˜\d}‚ZÛ"Åf£è4Ô½‰¨Æ<6¾uåQ_ê¤à„ƒÐͳdÖ™e*EÓ²ªëÚëöíäüÑÓ-<šŠÂ7ç5ï;ûx{wþö{ãªwB %…è¥lÃä8ƒEÞB'¸%Çé,lë£k¥àÌx"mèg8'íÙ?œ) ç¾­,Ö‰4*®oÁYÌ‘(• Öâ>,;Kìý›¸NØÖ¶¹&¡6ð» µ(£x#q0:âÜ t¤9÷‹—0=W…l–ë‚¥®ôÞç)L‚)ßü¦êõñÇUë÷§çFó40kiH~†”1_ÿ•’H¨~³“®œfOÍoÔu†ü–à~;ívÜ*EWsœï©9ö>$8I;ŸS]X‚4ïàŽîîÆNJ¥×0ò |-‹a“Gö}¤5û‹ÍÕqÏ'_2 7MÐg=.`ñ¼óU… •lFxtÊrEV^rsѶ²OšùOŸ¹÷O×^¡´¯Ì(Òå¯ß»nW,eWa* ÷ÐøÔЀ9HÁ´ëÕ £ˆ›6ÀwŽwì(BAYÕû×4m>÷W½ /ÈýÏÚ:ÚÚ?Ez°±̹áß9­r$ásp°ï8âDt𿀿h1½›‚r¹Û *)g´°×‚•¬‹fí+zPרfÁ˜Ü(oºôuGÅoäL~[ØtIsM¨wô¾s_·@·Ùs ¿8§àäÀ(ïç dƒè¨ÅÛÔâIPÖA¾óY°{ø Fƒƒû]K$qú¬š»[ÿá¬ØøétÿBúñ 6¾—öë¡]€?$1Ѭúʨ8ÕÑo­&[*¶ êEÕ;a¨á÷kkï:°sG¡FrÙ9ÛÈvâO#=yEÕ+* €I?½¤\ylà ÕØ ¶6Š{âXjÅSüz5"äx€=ƒV8g[›Ä¿cª8²vŠf)Õ¯vÚ0(RI‹¯4œm;Y¶%­@™§ß½‰ÄóøÀ È¤]ß[‘Pz(”hÈ ]ôŠú%Ši •5m>Pc©€:Ù(DÈ€:>d¯_3ïc’¹s³jg:Ñ“-ÄgwZa<Sޱ©ùY‡HåvX½ý€kÍýÊÚKäGòõú–ø3«N…¾Ï¾ŸcM¨}?®žÙ~d#¯xydcnŠÖŽÄgê26nØ´~[8y[qêbÈ¡`ÖÀ`ë:p¶Q¤÷‹Ãl ñžÆnÌLI[Ã+Ââ¯Yh“_QÀºžâÂÙIŸ©Šõ3òcxÁêÁ-Ÿ¡MJ,¾‡ãp=®÷q ¬x~ýôý‹*Eú‘1òŸXKx(ú>‘=èoåÂLE5{”ƵÿÉNmˆø¹âè®ýG•ϹMŸdïÌ$|lÖÁFœ§ý *h¶]¥Ìi—vG¤M¢=Û›hBP‚P•— æT¤Ìe ð´Â{Æâ q·@Ú7ž« jv‚ÊÎvðÀ‘(ÿj1ŒÖ©þXÿ{z•Ž,°;~Ô¢ú;Û”Tpsqüë©0òz?¢%Ì-¼Œöå*dKÔŠŤÅö˦Ó]ÏZ¢fíQö§´Fqo'¨“Äk7ü à†n0˱Ê¥ƒÞý3´1³1’;±6´÷t±zˆXEýxÌ)üC#|æ'V^SÂïÔGsÍ®ûw³éØå¥"ÝŸëçä] øH:L»ÐÒò¹Gð¦¯[*!)`Roj“êLûvÀé¯-C$af‘ì’É`x ˆïÑ6!ñÙ›»ø0Kî,®€a.„ËqRÿ,v4®B:4‡ÚìËâ&…F¸¸„Ö=RŠY´ïeqÄ£daSXKpךGäé*oil:[ò5¹Ï‹YÜ;Pý)”@Ží/xù„bqÈ­Sóa¸“âÃBJéÖCä8-}xÐჴÊR‚=»ïbé‘N·ÓÇé³6lÌV¥g¢Ù˜9T­_¶˜8“ü˜£É”¿«+Χ7Ú_&_–75óŠt¢Û•™¿‡±,΄DAÑ0-*ÄkeÒÑšº’¢¯ó”¹Uûö~Z°ßö]«U;„[w oÉÁ‡Å[ëYh—£7‹ÕxS7ûÌYþÛâÝôv™ºòaÿÇFÑtùÿMp÷®Ÿ©¹|þDr¸û +ÿq-Zs/ý¯ ã¼60B¹¦yÉ _@¢Ö…,æMíÜB—Þ B—Þ Uᦥ§Î§t؃ð‚ÛÍ´ïâ.¨b[üNù_œ¢]ÂC¦ø…ÐÙäíèä­ñZÙÑóÜÐñáaï :…I âpV0* ºtmD%ÐkþwvDjÁ„€#‡SÀ¾‚pþ¯]–¸Rf5‚’€ ×`P"½æÙWyŠöSt$è EvJ»*èßú"ÙUpÐááŽø$Ç/øˆäj÷?x¬¸¦âïUäÃ){BØË ž xíá¨æà ƒUÒngtègþ¦âÞÙIto¨€‘oäí,Dö&È ,3ÒAT£%–÷Æ 9«ÃD.NOp8A«Ã?[RçÛó€t‘¶Š³—kZ‹~&0”À ´_FthªÜ©ŒÈMÎëÇþ=qÿLʵÙÓ3u"S£H,ÑfœÙt2ûyJì¿wàDAýɲª¹†Ú‹òš‚Ë’NÏ7 }›èèºÂ•Ksó GK»Jû4£è¥Ã„H|®ô–Ñ©.)zqþt¦•üBƒâòÐqÊdtþ˜dçlQ‚])û}Õ¹«WªÃ½f¦$àx4V¢¹GìÜ­hËëú Í’ŸÂJZiðŠÎè á†ÐDrw–}Ì+žœß|2r™]@Xè<¿¸ê›Û•8ŒÍÁ‘O&íº0æ×Aù ®uúç*E÷õÚc ×ìÀtÒ->ÍG·,J©‹ßLæòÁ.§zWá¾#ù•…µ„¿X©]àç®ú¨_ƒ`ð"í©ðþ”ra–è‹ôåªO#GS¨Â 4I¾ãpð°ßæÊÀø%•?:ѽ›zW}Ù¹}*9X³‡NWÖþ›úåSÜÖ{ÏÓV|µCšE?ÅAOÝ('iÝ¿xAçÁQ~G›•ñ%DªJa´Îýœ|0ºW\GIR JH[*æbƒ°õ\V©¾<¶yÎÉ¿‘Idvt¸W„&cA å¡Î,ªõû.új|Ä/¯À†¹=EË…Ñ!aª]·Ù{2ÿjMõEÒ@ÊôGBy' ®Õ-ŸùAÈâÿ°–[wζ^Sõ“ã²äÕQÉ¡íTË_±á!SQнŸâšŽc¤V›©7© Ì€0þ-àMÏ`Íf M&Ok=,è7Mjè"Êòî;x€WüzâØáúsv`<æÚàÐ1ÓÐÄçxøU­RÑã¦]âf‡v=®à£{ž€ÝÚV³J´ƒ4Añë•ú˜ÅK¢ç„}þek}u›ê<%(z:ëCfù„…úø„7Üøöì¹vɃµÎPà¡r÷8ØB>stream xœuW XǶîq˜î•Iƒ‚Î ¸ "(‚ŠnìAPPDd-`ܲhiŒ¹¢ÆÁ ÑDEÜAw£ â¨äj¢¹§æÕøî«¼ÉËû¾÷Á7]½œª:ÿùÏNI“~ŒD"1 HLËKÌM‰­뇇KW“Œÿr×ʆ3ª½¿˜#3)2390ÌjŽ%N wBÁ F*‘lÝ373«0;%)9×Î1"láØñãþz2ÉÃÃÃnyáÞØù$æ¤$eئƒ¼Ä´Ì¬ôÄŒ\O»¹ôë´´”x»¤´Â¬ä»¸„„ÄÑ,2.-1ÕÎ/%-%++3ÏÎqîX;׉'M ?®óRÒ—¯Î± ËȱSÙ…%&­N‹ËþÛC†aBU³ 3âÏ)ÊLˆš77+1:ÄgÕŠPßì$¿œä0ÿÜ”ð€Õ+æ¥Få§ÅE~X¾|¡ãØqãíœ&Ø;»L5ÉÕ{²›û”©ÓvzÄN÷œ1Æ‹a&0öL4Âø0Œ3ãÀ,fB_f:ãÂŒdæ3~Œ'3‘Å„1þÌ$f4Î0c˜L 3™qd"˜ ÆËD22îÌ8f!£b¦0ã™EL03‡™Ê81QÌ¿2©NU9ñd„ 1!sˆWØÃàö‡ðA©r*[2uYœâ]<„A`}EýKËùåsJ•F ô>Ô#[l*Àf‘²$”T“¨– Ÿ ; ¦2 ã%p “k2à(„ú,jñWZ)N¶‚8 2hõéDàHò»$M?PF„wéZ ,Ä@&.§m(ÖX¶ë ]7Og-ÇíV a›Ñ¥}ÕgyùÛ“§Ê._³AÚø瓼ß;vâÊÔ_›x<ñDÔ7¾ÔÅ:VGî P ÜeT¹ñPÁáü¯ÓQ«Öqú}Ÿÿ¬Å ­ToC·1Ù c ƒ±N6ZŒ˜–Åæ­ì- ôŠ\ îÕH.ê¤úq àæ¢&nh,š‘87ÉSD̪F6û\ÿSÖ ~èÕËýÁg7D­K)ÎH Nñ¢Þr‚ D Œ¸ÑX˜yTY‘½'mgo„SÓ¤¸"&Å¥.kù÷WH†ð³ç#êrš¶Ý  N+ø¸C?¢ô K N 9ÑîÍÉïB‘Jxvs:LÌ›>qòüG0^¤5"ßMàÖ&9Ó)Å¥°F@>¹X\Òéyy,ÝÖè 4ef‘Y¿Œ€1`ÖÑl9M™\¿%I(-=˜y>ÿèÆ£›/ó[›„¯zoÜy‚xÍ÷MhÓæMtz@|[#Áj ¢a¿vf A†Û2|Ycâp¾I‰¿¯ÄS:¼‡h¨¾P ‰Äž8“Rô +´íõ7”í÷kA†`%L +&àú1#*âcO/ÎÄÅÊQ¼¡g¥JãúÝ:¡‚ bѰÔé€ôLïµ–¿…y@õ#ÛQ?I)Ç›ƒÐÒÄîN ²lòÁ‡7õn³‚“3ŽSÕלþþR‡ XÎh& Òmä‹ ­ «iñ8ºá@þ¡¼Ý+Q,ï›à¨0橪ª5P¡±<¯Ãk4T¥õQXÈ5ÈÎg©ôZŸ@†Î¨¨]¦X|9£ õðàÿ˜‚3H…M(S¼`W#1yJ<‘+š›¶dªïb"CÄš'¹0œL„¨¶WµÿHUýÀ8Ù JÂc¹­x\“ä)M”¯(qÜaä42ÒÇÿGƒy2ýnYÙÖ/(Z¸µŸ¯Ùüâ“Ö•V)trM±Í/ƒà2É}j;ŽÚN3”w°út“©öOÄ.‡ŠÎkµåEm ~ÕúëÄìÖá]ÂfpÛAfÐh»9z!®µá/³•¸X&ï~–svE˜ JÈ‹O[‘³6Í@û’Ïf}÷ñ‰-54ð[vÇVÆ×û?I)êB?–Õž¬«>vÝA=á÷+ȇuCäm“¦¹jóànݯÀßtÚ¢è‹#¥åÅ2IÇslJ5ð"e&yªÓ§;“&Ãh¢ÝÙsx*¦Tnk”è—åÝØF©çYáòi¤ž“ר…ûºƾ£ÀÜ4ƒ#çv7ü·¶ºŠæ:…¼`ŽÉx fD¤…RM¬é½Ü¢¢EŸ#´¥HáÕnúrónÄ?¯;õ£RoÎAœÉ4ûÖ˜4zV#¯iÊüKo+À¡Ù6¾³Õ±F1‹å«çRˆ¤ô@w*&‘} œ²‚\.›ÀGƒ±Ã^t*C¹ì?ŸˆÛ¹N4B,ö"÷—±/©’ýÎÒÒü’–Ä@™K²I¶ÌXnõŠ2ÉS¥:±¨ìàÆú„þ±»%+’ŽU¡Fïp¦Ï‰ã.0fB¿ç0Zw»CµÞ¾L²÷¬¢ÎtŠö¥šÿÉÊ¢ìÂÌ”¢ˆ÷M¨}ª¬uú#¦êY0äíí`©Àçû²Mô _²g£O§‘´ÇÅŽ18ÙÑ NÔµçìkìò‡ÁEf$¡NÓ)8ÑŦ¾K§Ô£AlñmÅÛ,ÏvÁnc¿«£UeÓúÏЧ¶í9¬„[œÎ¿ž3U«’9YëÒ7-䟰_ýpªRø‡g3£”«9”œW¸˜~–ºf^vÚäÏË œî…üq¯áÀ•Ší‘‡s® ½h÷ÖŠ¯h!en(ÊÎMI[þQ4âƒ54VéÞ­ÔîúvÛ‘Ýü_ ï0+ØNÛ}G–,5¼#+ñ;ÙöÐÆÿÏOÚ)|³à  ¥b Ü% Øê¡ÞxLø·A&Ã2Ê[÷jà >°]äƒ% #–&sba.)%Á¤Lö‚…¡Ö$FÖÛ×D¿? <,ñßZ^g¤-­@NþøçÆ[M·O$(È;ñx{ëdB x‹‡sÏ#.õË V¤];è|Q̪¥*^^÷€û{¿y¥.ÓŽªøŠQ|ÞçäuóN7d´Ú‚¢‡¶ sÀÛý-QÌ]”š „JF“rA××Lÿ½™ú/Ÿ>pôÅVLñs`‰¡›LÇÝ?2…‡Ó¡ !ÊßX˜Š_ ÃKÊ“N}T™¤õ±ÖÒMŒ!Ö*„È$D\ï WTÙÉà 3¸!˜\^Z°çß›ÁÜÇRI^õB0 « ‚Æ€5¿€Õ‡:*f´%%“£‰×dBÍh‡%š¹Ñ¥¾$&#N„“îçZŸtGwÛùÒIßn`ŽÜ‘ø%˜Ð4}¼®æµH ´EŠA™@â!Þ—Ä“x_ ?-@oE ÿdÌ©î2I×[i×Ááf!‡üÖä/ÙÄ'r-'j:•`F5¨6þ$*¸+õsa=5½o™ä²î¾NzY%)WAldÕlØÈ@ÊÝ·ú¿ODÁy#éz#…pºŠÃqíürý"êP!uˆ¬{·NÖÈÂ:ý:ãÙ¤«Õ’s=PÕ#…#t@’±":&8×ÉXs6<"rp‚qÚ"”€bõ³%?(VÔ‡ @¼—É‹Úñ”£11Æ:/ù"!²öÅ dó‚ˆi)o¥-ÛÊ$•ÍÛ Žâ¯ëg ÎÞÞ&´y?×µµ=ëõóx¥¯Éµš¥a b–.¹ÐxõBM£;? JJ‚{,VR¢ì6X‹v΢Ý3uÛ³—ÙEíj®6ŠvÆŽAOæ°K+…]x«`تÕGÍçÒˆ$¿˜ +âU"¹Q>Û&Á=RüE—`èÏî¸þ]U×¥WW†üóÊ¥VZ˜nä4ÆŠ;µðÛ4 ¤&„d'®Ý4—ײ_\Ü^¹ëÀs7 þÑͰ™á+«’”. ‰ãÔeÈÄ¡8*Øc|’RÁPñ²Viq‡âczÁ‡PÜfY«…Ý=~=ô$ «@-ìd«¶Èäo{ëc§¸.ˆt¿äâïŸ(iÃVºboêIÿ–ä^Ú4~Þ6=ÉõS*iÿrïð÷õ?Ú€ÅôVbKfz‘ñŸ+´ì–sÛîÜ_V}þ`#âÕµ1³¢ Ó>Ræ¬Kû,x3o„ ƶcEêEêx Ä[ ÞoÛíñ² Ž<4„Éœ9¸Ü.l²áÎûMw©Á¥K‚=)rY”-†~l¤ÁJö˜=Sw|/]éÅÍ ûQ¡Áã½—T?Ê£ûÞ5¿4³|Õ鈟ÒÑ};¼zãÀÎù%¿&}¹òDËà"Û7õ-uGy§Zr¾ÎÑÙ?¥:VüÌf /ä›§Šõ˧I* 2àÛ™gU§Ân§©Åc]ï+PÂI:"÷]µ0Q¹f_}ùÝFç¾ö穹 OB\C‚¦{7ý¢½y««&øWêÈ^щ 7,g‰ùÄ8F’þ9[ ùݰÜ ¥ïp ß=€µeUOPl¦?Öòß±-î/È{[Ï7@|ê%ø!±¤ýPî5¶4o ùúQ•YÙ§2¸Ü佨UO_¿•B*£øÂž4ÿ?š‡×@´€KH¼¬3(`V€»8ŸX@¸¨ŸK/þ¥~§ÿÔ:ü¤-¿µH¨ðþ! |q;ʤïj‘{—‹Ý“ê«6í þÒÌìé3s†ùjü7ú endstream endobj 124 0 obj 4936 endobj 18 0 obj <> endobj 25 0 obj <> endobj 37 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 24 0 obj <> endobj 36 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 125 0000000000 65535 f 0000060951 00000 n 0000088255 00000 n 0000060746 00000 n 0000057496 00000 n 0000000015 00000 n 0000003238 00000 n 0000060999 00000 n 0000087341 00000 n 0000085060 00000 n 0000087738 00000 n 0000085508 00000 n 0000061040 00000 n 0000061070 00000 n 0000057656 00000 n 0000003258 00000 n 0000005292 00000 n 0000086061 00000 n 0000083932 00000 n 0000061111 00000 n 0000061141 00000 n 0000057818 00000 n 0000005313 00000 n 0000007660 00000 n 0000086301 00000 n 0000084092 00000 n 0000061184 00000 n 0000061214 00000 n 0000057980 00000 n 0000007681 00000 n 0000009565 00000 n 0000061266 00000 n 0000061296 00000 n 0000058142 00000 n 0000009586 00000 n 0000013292 00000 n 0000086951 00000 n 0000084615 00000 n 0000061339 00000 n 0000061369 00000 n 0000058304 00000 n 0000013313 00000 n 0000016496 00000 n 0000061423 00000 n 0000061453 00000 n 0000058466 00000 n 0000016517 00000 n 0000020115 00000 n 0000061496 00000 n 0000061526 00000 n 0000058628 00000 n 0000020136 00000 n 0000023544 00000 n 0000061580 00000 n 0000061610 00000 n 0000058790 00000 n 0000023565 00000 n 0000026461 00000 n 0000061673 00000 n 0000061703 00000 n 0000058952 00000 n 0000026482 00000 n 0000029621 00000 n 0000061755 00000 n 0000061785 00000 n 0000059114 00000 n 0000029642 00000 n 0000031611 00000 n 0000061837 00000 n 0000061867 00000 n 0000059276 00000 n 0000031632 00000 n 0000035525 00000 n 0000061919 00000 n 0000061949 00000 n 0000059438 00000 n 0000035546 00000 n 0000037944 00000 n 0000062001 00000 n 0000062031 00000 n 0000059600 00000 n 0000037965 00000 n 0000040362 00000 n 0000062085 00000 n 0000062115 00000 n 0000059762 00000 n 0000040383 00000 n 0000043843 00000 n 0000062169 00000 n 0000062199 00000 n 0000059924 00000 n 0000043864 00000 n 0000047601 00000 n 0000062253 00000 n 0000062283 00000 n 0000060086 00000 n 0000047622 00000 n 0000050361 00000 n 0000062337 00000 n 0000062367 00000 n 0000060248 00000 n 0000050382 00000 n 0000053592 00000 n 0000062419 00000 n 0000062450 00000 n 0000060414 00000 n 0000053614 00000 n 0000056297 00000 n 0000062494 00000 n 0000062525 00000 n 0000060580 00000 n 0000056319 00000 n 0000057474 00000 n 0000062578 00000 n 0000062609 00000 n 0000062642 00000 n 0000063016 00000 n 0000063037 00000 n 0000070643 00000 n 0000070665 00000 n 0000074814 00000 n 0000074836 00000 n 0000078864 00000 n 0000078886 00000 n 0000083910 00000 n trailer << /Size 125 /Root 1 0 R /Info 2 0 R /ID [(©ðx ‡ÑoØðàOM])(©ðx ‡ÑoØðàOM])] >> startxref 88458 %%EOF simh-3.8.1/DOCS/nova_doc.pdf0000644000175000017500000020443611143604405013663 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’š/ úæ‹êÜ*»’Ü6ãôA–m9©m9¶ä6ýõ@ûüxH9iœñÀ °X`¿½{~Ù•…Ò»Òÿ—ïŽîšÝÕ§£Ð½;}<4>^ýrÔ•ÿ/t`ûòÝîÁ¹›hwº,Êzwþú¨,¬íTÙ…ïj§¬-º)Uá¾¾;zžäûª°ºnëì:ß»ÑUÛšìs®¦m³‹|¯ [kÕe»\¥îÚì,WEÙZ­³ŸÂlSš.{ç;­vSn{:­ºìí8èÜä{]ضnâ’aèÇÐvMSÉRÏzö*«²OyíX2I]ÁüWC·›üóojãëv{]…£xé6[ÊL5ŽÞçî{UwuöHÖz%/ûeÝ{á[Ë÷rd ˆwyÓ´ž / Õ¸IQ¥$QµuQ×»Ö8¶^ŽïóŸÃ ›L0z˜Ñ:BÖ6»ÖÍ´Z¦(U4îŸßûþþèüϳ‡žSÝ£²'ù¾-Ïe“=õ;mm]g?ä{[”¥.uv*C¿ÉË¢r2/«ì±ÌúZœ»¶…Q­?ã8ö¤ÐÕM\ÌX tÊØã5ÈëÞ©RqçnKm·7è÷„ß8ÂFéÚ8y-`쵩\wmÎÆïo¸Œªl ­ë0С(ûWn ÓU¦ñÈ6®Ë4:{'_…V¥ËdK74Òèªìƒ4Í•)”C™·k9YvŽò¸.{#Íay/®éX`80ôŠòz!½°9–S‚@ë“§ o¤y=]±j›Y„M&ÜÅg¦´ ÿìšekLç âÀ¨jÏh= Fk§ Míô#(¸çλ²ÈÍ­lÏÉ¡q”¶³SúJ–„ý½½à<Þ A8Pñ w§> Õ¸Q˜‚})dƒ—Tò—²Ó[é}çôË™#Ó(ÜÙ{ @!®½²Ék$‡þ±—¬ûÀ~ìlo•ªÖ«wº½özñ€1ALÂ/.}ü¬¦ñåÞ> Fbó׆±4àŒy…d«à^Øwzë¶Ë¬|‡fšJéà‚FˆiPb3òÕ-@;®Û»ÚµfÂð"Å|pXÕ3ëç¤ ß‡ÓÚΉ*¨påô»¬F~AO8¼É™ž¢•  æö ;ÿ§ûîü£ˆ,"–ÁÇŠwq”]¸QWnË¿æÚZUÍ’í<$¶ŸííÇlbµ~Ì¥Æ) ++õHÒm©<’F _Œ¤Ø<̲MÌ.˜Î ’Œ"K%Gœ¹˜%"3OËBŒÁ‘„A5S£ ­ÌTçÃ_?AÔ3Ñ„‰)Óá w„ÀÄ+ÙÑ\Ÿ4žÖA4š-.]ˆ£¹qeáP˜˜ëÁ…Á\Ð ]ÑÕ¾¢t—Bî}û(¢)”QÚ­HêÚ]ⱇגÊâ‡ÙT«šzÚÆñà¼å‹XXÇÿ†­G%o§´zâ¯R Ñ“êJc|Ê|Õ¡ÓÚäY@®¶îûk!MÂ$ˆnsÒ|±.jk)l¥:7Siw<"Gîo–`'Ï¢OªGÆùŽhRßñpu/„м·p"@ó‡8¨ °¦hL¼8äb†KèEH4ŽüŽU)À¤GÂO¼Îоˆ<ÿ.¢OP2>°ÉÈ3yÆ@[”qônV7fO…î÷¢‡°×|#_<†%@lP+<“^uzÆ#…Dz7̃(™g#óª¥ ZùX~ øN Ý@"œ“©¸;:ô î‡*WiÇHŒŽA©AIÅZŸ¿L¡¦C©!YÒ-|`ñŽx;fÆð¡ ëk齇BÕð,ßL-èI:Îã›Î´£Â—Kx†ýq™ŸLlôpj‹šÂZ :WÚ4ÄN[^e¹ÀÖáù )p:ÁkpžFT?Kp5š¿§‘ ØKp0ÇŒõûÙ.Ù/Œ<ópk*ˆ‡#¤NhÑ#ÈcnI|/œýéÚX°:Ç"Ò˱›#´ °â¡Ã µÚÜrírbž‡0¢B,reßI”%*Õv‚- 4yð÷ @Š*nn @ˆqʼ6áwº|Á²Ür‡X[²ùDùÂ~`b;ߠׂèÀ"<°%¶®·…>Ê!Ž>q§1’ù3§Õc•_,=BkK1.„>(g8•©’°„Ç"ãÈSê ‰î8Ž=fÖmÕI;&RN\A<)nÉ%öK\D®£§¥\Ž”*}—]ðhgf.µÕ…j+0—=ÅÃ5§Ï—]wñ=C rÜ /§ˆº?éP* JÆ‹dniV#ä'yãè7!!Œ»žŠs]Æ¥Ó4p°4c‹‡Ô'¹O?hhÌ—Ô@j4Ü8Ǿøø˜du+6| /0`tû]ÝþF°¥ >†[ב€7 Ò í ’ôh!±÷¡¹ŸtøûòÅ4°šÝYTΠ­e4[òP‡S}J àqýþ|)ÝÕÛ½ÁÒ²œÀ#æ> stream xœíœIwÛ6Çïú<’ïUìË1u77I“8ꡯé!µ]§‹Ç®óšo_€2‰¡™´"‰°Dë2‰Eøã3>f(ˈÿTÆéåäɉÎ.n&ervòýq}1ù81Àý_™€íÓËìë™Ëh3 Ve³?&¬5D/ ¥™’î+3M “Í.'yVÌþrY(Í\Õu»”AY Bø ¿æ´˜J šZá2O)U´ÊßSÌ0¡ó? Rwý²˜PDk™ß†\ÿÔ×߬p‰ù¿Å”ä’«üC¸óº`À•%Ü×T]ÿ®˜º~RZJ…ª …ž‡ü7®}ŠS<‡PÀa™Jšm›ÒÈfêo³'Œ ~àÍÎÜ Ù]cöÙluôÞ™~äPMA9< 9t©˜”2ÐBæ|Á5Nµ£¡ï§)·41Ù”r0‹ÎbQ8ÿTPŒZ*1]?ˆK¦ŽÃ8W9Ò¬n€AÝŒ¨}£vÅ®&pNăBñêÇŒ1Zò‘Ð#‹Ó&ôÐîÇb£@ÐU,®ÆŒ£*]h^QjZʹ»ÅäGЯBš•ÿìn”ªGZ Û?sØ'¹d±5*±ï,>TfîžÅ;èé¡YÁ²ˆ`™‘¬Ë,†åWÁ©D-Rеÿ"\®ä±ÆÞË‚º·åKâ³¶êvP:.4õɟ„Ä—Å”ƒ° ?È7å;ƒI¦q¡w¢^3ä9õBÞJ%¨^–änð¸/ÈpNÁ,i,ëìÍÓxÄßã|½ôÄÐjo“y²Å<'CJÛ‡y(µ¤]Ö§’H.,JÑôÿ*˜çq§m…²r%¬* B)BÜIqÁk|ÖÙ€ú§|›—©ÚC]‚æŠHß*êše¤¼«‰r"ò·E1Õ $QÃèÝbOqàd‡ÀÈÅíšÛU_kbï~q÷|[º&¿XBüBÕÞÖ×ç!fï£jð[¾5 nùÖ(ÎyUwjÕƒ‹1ã aƒ¹Ã`Ø®õoÒ Sf1¬R+J7iåÚÀaxº¦~s,›"øèÄsŽ Wa xPا‰ªª)—¨ÙÇèfÇ*«Á0feé˜%Á¸u˜ß¼o}dÕ>½ ž|>‡”‚`jMŠ!pø²P ™rß,N¾Í‘ÑM?kÿ^©áÜüH¾ÃÑ”¤¹ðÄq`d«q‘‘‹Irq9×pº 8&ÀØ^q†àd pÏÃQ€G'ŒhÊ&²8dÅå}¾3Ùœ„¢F-棚ŒbÏ/j¿BßÉ–Å$i.Ôž®énäW’üÚ–™ŽÚëÇEâµÌº±ã⪠@KÎé|Zs ! /ODe÷ŽBMA"~ˆ»ïþFv'…kAy ¿£fÜáÝwËd”NëQù=^ó!ý;°#ï~¬™ˆÜ3ÀdÕ~Q¬éŽE6H¯­'Ý‚^C"nYºi V¿$/…á»-®1LcPêf¯e×C¯ÃëÅNnµß¸:M o:Ïãê qt[=ÒˆônÕ­Øí <?ëb-Ž,ÿ*$¢E-8Üð&V®Ë»½š^R.ðJÙT†ôƒÉ© ® 9÷ ?2‡@Q§$¬PeÛ2OPÐL¬É*cÕÓÀª¨Ïõ²Ýyœ`ïcB¯µÚD©(4qŸ ¾(|h†“;´‘Ô ¸» T)ï4¤\X\±ê¿ú»œ‡¯«åw‘vøÖç½®b!ˆh¿Ôk_â |VÁ<u¾¨ç÷~R»_Y‰-ñR–ã{¿“#]ø ªvh@i ÂܤQwÓ„Çö"Ä=h=k«7üÐå7‹gCÛ Bµ7ò|¡"T{VÏGýÓ)ÜÓÉ‚çKÒ´°X„¾©M £4ÙSúµ@8X¤ ‚DÚXþÓ#l*bH ï÷ö© PóØ–­ÔÐøÌïöNjW Q"¬“sÒqè•޹ˆH ló‚Ý}6Þ™ºFÈÇ"äS`¨ì&Ÿ¼Ÿ|ïbhC¼C<‹ÎåNãˆZsÃ"[h"r©¡Ùoéâmv­aRʃ¡o=)8’íѓ틀˜’l "8J@äÑìÐIƒŸë¬[þ÷¨',¸´:Ù×)ë®bÅ"Jg*]Ÿw ËÖV ïÍBµ.­8vÙ´ìÄa #r³8Tî—ÙUD5ݧ|?ÌõïC²JűØ:ä¤<½µãÄWáØ©D8ñõÛÙäµûü½ä¶›endstream endobj 16 0 obj 1659 endobj 20 0 obj <> stream xœÕYÛrÛ6}×Wð­d§„ Á¾µéeÚ¤Nê(Ó‡L§ã‘äK#[Žd+ÉßwÁ vIm(Y²&~Y¸œ={vqÉû(RE™ÿkÉÕè褈ÎW£ª9:ùµ1–ç£÷#'rÿ¯j öä*úq ¥ŒJQÚh|6ÊDYº¬¨g•‘5ð»4Q!3Q@‡«ÑÛxœ¤ÒŠBZ_$Ð,•.âËDƒ•—:^%i!²LZG‰TB+'ã«$UZdE^Ä3ßX¸¼¤‹0Ñ2I®Êø´jÌUVÄ×hNѼC“ÌAkn¬ÍrÚwæÂØ †.lº]P[ßâtÞ5:=ùtÓÏÐŒ€*éç)ãcXE(ç´(¾57.× 8[dÆPgërklK¦¶j†l$e`¾“©BBáXÔòL²2£¢ñ‹üþ´QFÒt´Qö¤a‹L¨ZÚ[zÏÊ„‘.~¤9´æÚ)€PÀ°/2‘9-á¯rÃIY:nÔ]rgÁ‘Ô ht> €Kg€x}—žÓB|¿^öK’‚>•Ta=ßJ'ž!ÐU»FKÀ.é`y•0hüïhüí[ª{L¢­£: W©B—Èñfžk §Î›Zš7)ÑW˜²!R~Q2¾? âŸ^,Ø[õ>ÍÃT³Ý ˆ´×ЧlRõr ZÍû!i«Áæ1ö\ôÊ{¦àŒ½¯49\‡ì {§âꜵÛ"LrŽËxp®¶[ÓÐvÔ(×(ƈ¼“YDZk™„º³‡kgi±uþ<É÷Ó/Iwî0î‡kd¤,!TJ*¼=L‚âàº}ŽJ;Rr8‚  ÇÓ{Or•¯žO„ㆣƒ zâ4ypn±d|Jd!¤VOž++ÔdÀЯ_BÇ]\î&Ñ]_”ƒ9š'Å> ûV錫ÞB´PœY'2Vf;äl$¦€k}¸ø“û;é{ë7ܲT®¤ ä‘ {í¯Muk¿²Zh×½ö³¯a³`‘×¢æ¨(ä–]›ŒiÅšKþL°ñþã¿ßçý‡¬Õ}£_Žæ³1è%76ΓöõìŽ]•œ"È–A^ˈ }0dEöJ0'GôªðX©âIéÏòÍQ¥yQû>é½}5WyBn}tL”ÊLXˆü$=õïY¦Èýí£µÖÁº Ö„é뺱TõÌÕ4^1CVØÑΔlΦ âè»`̓լ"Ë*õ[s†æª5Uo/¨hʵ¥øY˜õU°ÞtpJå_Œ…LÌã0jÁPˆ||×'Îã»·#!¨Àa §_†£ ži¸ËV—Œw܉ï†uˆ÷b lÐÑ{y„U}ÃÈÝ2Ö4è„ÃñŒR3ŒhÉðyºæLûàzÌò»"Ñ©9$l‡wC²ú) y¬ç÷ÂÅ ËSÎe¾|“€¤¬®ˆO‡ýì0Åo”«mÃ&,«¼âùÜœ5wÎ ž»›íp`ðrÅ]Y¸pÎqÁbç÷Ò¯‹ö‡8Ÿ±î¶­6e;Ô WW–5 ½¡O;ËdòÅKÇ?ì%ÌßsF&˜ç|íš°ïy’ûÚô0c‡³ÃnÙ†¤Q™Ò¨³!ŸÇ£?áï?ô¥Êendstream endobj 21 0 obj 1610 endobj 27 0 obj <> stream xœí\ksÛ6ýî_Áo+íD4ñ Aîlw¦34µë8NšNº³£X¶“­m9ñc“þúøÀ½ EÛñC»VÓ™Ü@ÀpŸÀ•>EI,d”¸? ±w¼¶¾c¢Ã³µ²9Úù±&>®}ZËcåþ+8½wý°kJq‘E»kI\yb*®"‰ŒŒ±Î£Ýãµw£—㉊S£ôh×S¿Õ”ÌFÑx"¤Hb#©iGjNj?*ñTÚ"OW”(Ôè‚È#"ωüHä)ì»OäØú™ÈˆÈÿÀ)ØÄà° ¸œr˜Ã‘v8‚+;{;kH§œîþ´fõ*­^^Ûýë»Ñ÷^â?{jóFš•žJµÒì=hv}GïÒ•ÅIV¤‘Õ ‹»³5Ûm÷ßNÿ©lôoýYd±™´{¶zR;GCE–Ri–%j´5žHk8Æ»·æóËqê&ÉÅhŠÆØŠ"NŒv7Ú~®ŠÒ¤¶ÚÊÓQ21vßÍÇSj<'Nsjµ*µ“&B|ª.e¬–ÀûÔxìÌÛò’’·žÀؽ{cYk±BUi®8›rû7{Á'óc«-8~¢FÖórÓ¸-¨"”—çp ‡íѰ9_³#©Eœe™MÖhÞfô9-ñ¼œÀ]°Æ9"[áߪ¨DÔ&;iærÜ4)oe­n®xûA¡]\Ú©ì2×)Ð-ošÖ÷^¿Î°1F4Ïä8ƒ¶qЖž3̨jÆ€­æ9‘ëDþbûæv  úÎ8_ϬvÝ@©Ì(Ø ž+¶ÝéÐvÙ ^•uȤ<ÞØÅfq¦SSŒ¶ýçÿ"V¶«v¬ôè™ÿü§".ÂÒjS ³¢±Z{ж·ð½UZUöo5›aËÛ´c­ejLO|ê³$¿òÔ˓풩|‹l~ƒÈMŠfß;1e…MÐoǓµ%2Œ–VÑ*5© •×rtg•x;¥£°T¼-„l6’Xô0ƒ¬1C$Π²V¶‘é°Üý?`€"ÅŒªûpCQm~Il¥ ‚æù„]£vPÛâ ò:¦Ch0Sbp;Ôê,zRŽ€Œ¼€ÙùFµÇ•o¬|cI|£A{©°+°*6¹6¼14>and>ú:–E,E w,£o]=áôÉræ,G0…CѲǪ]¨,ÍºŠ”™[í6]}–¤]‘—šg±4<TxÍ%à ¡c!­#BÇáÝçS4GD~Ö4´ñˆ·YÛLØQa0¤œðahuµ¿Ke­¤qwÆõ q%gH-àäÈ"M{NƒÈ›)”ú)u¼xViÀ([Î%mg E·÷Ó&_ÙÄ5„3´:§Ù3÷èGÐ퓈Ùê¡°æpùm™¼æ•jÉc AçDbƒbš°RŒ?¾ ×gSX¹ðTÍWd™ÇI rÏPG&.byÐÎØµÓ½Axv2IšòFêÙŽL¸x’n<Ë"ëˆÖä°}Tº a€›4Œ†=Žé³ðÐÌÙ.3ãš)SA³òt÷¶Š#JÄIž6„qí8wfmÓ¦u+¡m‹ÚæSãHpüÅù¹ï¸ï[ßð‰/fp$ÇYϦh6IØÉ<¡ uH 3|OÄ:°Ñ`_¼©!}áÆÏÕn|\¸Y¸™ûì“IVã?g“Ï CJclSF%ÚÞ© Ž;§>^0Ï¡ BGeªÓ–ªMU¨ÆƒèNÌþõwK›çW¾¥•&¯.€Ù›Ž´'0“û,L÷û¿xŠnÿ7ÆY’tî‹éL‡’äׇ.˜Î9ìnYì¶ï¢¨Ïgx:ÇÙš-“í‚»Ú3 œ—„NÁš½/¼G —ÙTÈ‹N TÀ0ÀV€1M_LAYƒžë@–À«(Dz(ÜÆéz§Ôƒ^4^¹Iä¶³M'€ëÛgëÀsL©KrÐ@Ôz €ÜŸõex^íSCàˆ«,Ø¢ƒG¾@"ݾþø>XÆ1ø6‰Ã~ܼ=ñëúsàÎfP›øÖŠ3r§Z-sñ­/Lq~Tº¾KôñÔSÛžz úmv’¬½ ÊÿØnõ#Y]«V›ù~¨€UÁÝjàW"׉œA—°uÖ «)ïPSÄå-àGÅ´TùÂS¿]\Ðy«zÄå™ç°ÃÉby­ƒ•/ˆüuÈÂöa+«ßd5¤_‡˜Í{váLE'ÆåU¯|~Ò´Mj;ª¯¹ïÊŠ|Ó%àO WÅHm£Znz,æ ŠòJj JŠy349<%r›HqÄÜR߃W^Užz ä‰ Ž¯.Ï{ s/¿ d<‘ßB^æ®#ä°/‹ Lô,бòò]~SÞêÌQƒ‚…P§V&ñf“)ì{û2•0T€aVž-‚Nà6ÙlÌX Ù»ÙÊ£ãM¤‹-*sÈwí‹g7ôº[ÍÐgP¼YÜw°áŒFw¬|¥Gæ 4pOáRxоøê„ y*DÊÅÚ¹—PEê¡£ÑJ=%ÉNLå<ä2ôÝÏǬæ1É8©§/ãß’zäÿœz0÷“VúPÀ£uš%ÀÊS+<àHv{³ x€Ô³Â-õ,@¿ñ˜µÃ²Ì2à ¨•z.Vx ¥ŠÇVþôÛ'+@àÈ”Èe¤ž h©g úq¨Ç¬¦’e‹Ê ¹z‚¾ºÕ ò‡~ù¦,r¿¼@ìp }Ú.ë¶”ûNÃâxÀÔÊ™Þÿ ecf؈3üóq}ÆéLD%Yœ¢©½ó·ed¦“f¼ZéŠö*3h°´Ý¯ÁNÁÔ'ŒC(ÅÁªµe/p\`ÛWp`¯ ùß{¸\*ø¤c˜Ò$q*E§Œ®­DDQHú ŒâŒ sCƈø‘ùîõ‡Âky%¡Š‰‘±PµWêN !óO*®Úkœƒ]ÑN¿<¸£v}¡ —O¨$¸Ï´¯X[úÜÝUÁ–N©Ü—Š ö{ õ•°vLü¯õãõ,Á×IŸí®½´þ ±'qendstream endobj 28 0 obj 2880 endobj 34 0 obj <> stream xœÝ\Û’·}߯`å%dÊ .sSʶ¬ÄrdY^Qq\²+Å%W«÷B/E]üõ03èÆÌ‚¤´kÙ–«ÔpÐÝhô ñë(M„¥öOK,.õ‘+ÿ³!nÎŽ~=*eÿsœ^\޾œ™†BŒª¤ÊG³—GiRUeZÔ\Å(ÏÌ¿«lTˆ4)L…Ë£ãG“4QYž§jür2")„®Æ£‰‰–¥_N¦R'i¡Šñ©-,JUñÂkÓ¾Rã›É4KÒTÈjü~"ŠDh¡ #Ï~=™ö÷<Ÿ[ReE&Ç¿ML£"Í2ÃÞrR2-x«ó‰¶¥•víÍ/~Œ'TŸ5e¬¯¨ô òf#>u|UVªñ’Èk¦ór¼°³n–D~F ï9q¹‚ÍXÝ×4©WÝI¥2çU[ä3Ñ.‡Až^S{3;³ fõK³²J¤ƒðù±žO~ž}s$U–dJ¡™-Œ\Ñï5¹Èt8„é†z`,j1FŒæTÊ À(³Þ‡kˆòk¸ µ$YIeU×Ä‹õÀ*t{H«ÜÌ}JØ:ΫP  ×plf…yLÀ¶ M·76KB¡•)ùJ]¸ß3-ÃqYV*ÏòŽ€ik`”•+6ÓVnìÈ2Y ÏÜf~!’•²3[-L_,ªzåmãD¶äfbW/¥GA÷çÛ±?læL>¸8LϾ2$²,u6ÐQh÷®à’î³üCê‰tÇ:<ÐÀF¢~VT!“¶ËWgh„Ù’TެάÆ6Ø­ö°maÄ<’ž`qágcbUÙT(ŒÛƒ[S›q{Àæ¶·À>ÐP†€ðUM©™šªbp§p;wš š¦hööÀOQ+I«þ¯‰2\³T6 ©\çÞ¸4²BÆÅü2ûß‘íÑ83f{1ž^¹Ý¡d£v§žbxPëeQˆñS×k¥õø9bÏeãY1a_ñí""÷ƒtóq 0•Ei¸w,•÷ìD*’’çËÉT%Ya,ñwœ2ˆåÚ,Æl’§i̓y‡F>Ì”<ŽIåSiÅÆlŒd"ˆ]œ!Ûˆ&”YqÊegB¢RíŒ:äŒÈ‘ˆ|Jäs;}»=t™¤•,Hæ™NõvL+Cf3âm"1åO üÚÊH°½>ÕòC eNïÛZòª¨{ 7¬/¬÷ȪӴ?†ãøiLló ®³ñ·Ø"¿m›¶nË¢‹±s[VÁLZ·…õp+Ìù܆¥í0 Œ, S·pJ*‘Jެ†ˆÆý¹èøµœÔV²Ø²Ÿ&йa»Ü)kßR ²LÁRµ…CºèÉžåÜîOŠÊnº•ëÞˆåLÉ+È…m;u7ö ÅÖ¡sôÃ*…}B+g-ïgÞºÓXà>©W&7¾Ü~± ¾—yò>-«P4~O™H)€ÝÁ6;[,öa§¡ýüK_aÉ+øÞxÁõ•VY œX²ðÙnÈÐ/”ʸx¹ÚÓÆî7]t:Øšõ®·ç{ÃuRË‹4;y‘?ùº»Ð®vÕª0âd`ôJʪö²tÝÛPˆhÜA§kÉ~nm`l)˜Â³3UÜb"Ž–ô/¦¾ÙçsÁ,brF…ÍòTØC~k"\/+(8jêp`÷ëÚE/º‚ýО2`Ɔ$D°ãóFMAéeñ˜Ü,×вµI‡¶˜v\ %&í›.¶‡%\}xVZsKƒZáø""«ü¶4¤J…?Ù4ˆ‚“ýg}²Ùã¼|µªÙXíÌCåiÔ¬·»v<‰®žá8«“jÏÍ&|Ÿ ÇJ2sùÖôOÛë Ò”á% 7Häˆî²ç„ í ëОƒ¼çÐ?±Ã…÷JéOIøh%;Jõ„]<7ðmü|ÞN*/ Q¸hOD0ô0‡£:0vµO°#ç;¤!è „/œ¯º^ØÅÓíÅ2·¬Ç>gAîÂyd‚1Ö"cŽëeÕŠ ;Ü0’¹š«.´ÈIPiR™c4ózœ„§¡'ö!)éšEó×_»A¤lÍ¡=ô§Òø(6@Ú†<枺ôÔiC¹ï(Ó<µá­‚7gäÚ·:÷Ôo»sZPÈášÈKHžyÕ’Ò‚Ô–®©´ÆÃ 'Ýô0 žúQ>F)J¡'bh¾Â·Êb­V4²8Í3Xa'Ï"6³ @GqtNAo ( Ää‹DÚø-• µP1 ÓàXN¡™ï=õÂS»ß[pŠ}éñÏ¡l;öD 4»¨õ¢wà‘!»†°ī͚}Cä·D² }¶G³g ™Y<Íýv'+/ZæI)û8T ïSïÊÞ´e6m¤°¾Ù‚¬É>ä% º4Q5Â>=fQ@Ñ»38¼=c»€&Ùú‡^:)ìrJ+}«O!Œ…ÀFtÛ§„ನþë‹A¹,oõÎ1»C×ýðU-ömÞý‚ÙI À &¼*ú8=t§”]ñð—º_Ô·^3—ã¿§ïým§DE¯ÑáïÂøŽ þ}á>ƒj•ff’u.•H2xËšÝx×íÁ}‘gýn`gC©Rà>ÎyŠ&n-`]#ß8dxí4–=à€^Àû]hà¯DÁf÷NxEý{QòÏéC÷Az×â¥PÆ]òE™ðÈà.<»ê>ÕZ'eVº É*¯Yø´·èÖÈêkO¡ó;:ãÑÎò#Ì7ÈóDHºÄˆ ÌP²$ÊÆnjYuÀ ÆN£ûϱMŒ¸30`öA•'ÆN ì,À,{_ˆÇà1¹_çõe볉R'ya¯{²{g»\º Ù?8rwƒðîŽ ópœ{Ìùð‡ˆÊD?¿ãH&€¾„ä×>ÅÓãÏ=Õ=›u×_ÃÛŠ‹½ŠñÝ}ew–}š«ÑÞ©Úa5>ê7ŸC§o<|@{ GƘ}KÙ.ø¡aêw3£Û?pDc2Ñ3õPªÞÝ)æv+ù)®Ì ½%:(1Ee¥ÑÐ /ŽÃîÔÝO欘(㣹÷Y?–ônà´í$p‚ËÿÑx®…ÂïøË+–O™i^sƒvéåºwnÑã×Uðýc\†îäIÙ\AœàÚKèÄ"ÀKݼ‡œxd”,˜Q)KlÏ'è˜ !00ÎÁásÝ„ÒD—þ‰–Þíéi[¡›TrïX¥#aYÓ A•î¼$eV?$'Ó4ÉE^hw1V'Y•¦ÚIU/ÁT•Fõ3ÌTUdÍÅr¡R›ôªÜ%3Bâ(©øÏó†}¦2wºI²2wRœ'•ÑiuÞ÷¿ôF4’G¦°2V°ߣÂïÌðTjŽV¦fûóW™ä¥Í[ç<ß0úœ,X1¯¾fãm5¹íáï-•ªƒ¦¨ .$@¥”¥}ãÀÐJÙÓƒåb2­’Òp.òšW‰Òy¶Î —GM-í£ÆáÔí$M¡.-à†V™ªãž·µ*i’Yž³ê˜ oxì–6Ç’WáL–œ‰›AYuR™å¶Oqؼ83BE8µ™gb{4À§ªNݵµ•.¡9xôJ”²yôªÖƒý"·+_Hfuš ²NØ•šo‰aöò£›DAvÆGÉ£ÃÑm1Åž,o «Gƒñ1Þ6xï%«ôbÌ.NÆv §cqhÀšÆ¡LŸQcc­ñðÁ·ÞKBuxPBà·5Ùþ©XÁ¾èQÚX¢ª“Ú]áYM Ç€£RÄšañe+ê,mh¾Šá¹²}teçr6s´ÉøQ”¼ÂP·N޵{X'¤çª^Á!¼ë®ʶʲ$sQ\ʶ¢DjøB§&{b߯÷)§c£Y²;X1OnM˜Ö›ÎC ýìe³ <|~j²}Ví)Sç«Yï¡A·žþðÞ6Þ'¬è°‚ó“ÙzÔ ª,x”n<«¸€vÛ Ü,še»-ƒµ»Òk 8±>è}óÜçc#~‹86±ÍË æî¾Wy?'üq[Ü wà¶’1óÄD Mƒ7K#‡­­ïq¡U[w^=8«˜ì>:â¬âû“]’dó?u–lôœæ×4v¾-ÿ`ÜvæÀëdKØì5,Å×¾ðµ|Ÿ2šÑ‹/K­!‰®@FB­·‘²€—ã•§ÐÂöpvô½ùóY†endstream endobj 35 0 obj 3646 endobj 39 0 obj <> stream xœí[Ýs5÷_qoØ ¹œtß}k§Â@[RóÀø¶“b;MœÒò׳Òù´{w¿³ì¦uó²Öí®¤ýÒJ»}D¡ÒAdþj`ºŸåÁÅíÀgßl€›‹Á›AÆæŸðt<¡Žƒ2,³`|>ˆÂ²,¢¼âª‚,¥ßeäJ…IŒƒ_†Áè(Ž4ýN†O Ó}a,öÂkVƒ ;eð˜Áç{hÆk>‚×¾Ö×»² />æs‹m†=›½ýw`QŒ×´(`¾0{ë“Ù5ŸWûX‰WÔ+0ZË÷øL)—†QZG’¦/_¹§º–ûÉ(£¬¤ý}F‘") p5¢È©49£ h0N³,2!¨þ>qí›æˆÓ’ΆzŒ"FNCyjâHBƒq™¢ùë(7ÑÞÒ±äΨ¤\~™0iûÓ¹e•«¤^½Å ¸r\W<ë_£2Ì R~ÅïцIZKÓŠ®‘ÙÒ™‚Ìwîìê@«ÎXe lŠ }_ÑùÚgœ8ƒ¾… @¹…ðŠ ë•ÚÔÑY´êXéMÀ×5ø:uÐ¥ƒØ fMEï~n @p"pñ)~¯8Ò'sœû ÜÝ#§ï(ð‚‘GŠbRoª#pÏ!Š%È Ñc„l lŽ3¹Ž‰žƒ¯<öÚAW#ìĘƒ~>FXÛ_mŒp“Ý«“´2>ŽôàÝóìÛ¬áÝÁeöVJ7ÑŠ¸õˆ–”*Ì’ênEQ˜i­ = +•èdÛxÄEYige'™Éh®$Ë)Ë›JZž1œD¹ÙÑ")Œ\ŽÓ8J Ç›r JŽÒLiÇÄ c&’ð…%Ô:.Œ§l—š ²K7*ü:¬8¤QYï%MËÜ®¨ÂÞŒÒÎ {dÅ0iîy»Lò,´R'šñÍ|ÚL”QÞ˜i‘Ï$ÒÔk9_œw¯9Ém“ÛtwÎ`À¨áŽA‘†OMî{ sq#Úz!/Ü­Aì׿Q©¬\yÍnvN±Åž8Å_ó\óNvï˜Ìø“¸¬åÍ µú8K3‰°‚¢™ÈQ‡;cfÕB˜Í’hõ:.‡Ò÷’v›’»&ºPòòÚ•ÖY¨ÒŒŒh<#W½rœÄ>CžT¬J,¥–t\[VWÏ—UAQ:£‘æ%özP` MÌy¯Ìn« [ª Ãö9eòd q ® ¯h]L·øÂúœ<´ ‹¯ ¼IaSçmí û³à6íY„åÆh’°°uk4õ][–*é<§•â£$¥àU”ÁÑ&:|çÑpŽR-áC÷4˜µ¸â¼G +ÁÔ bÉöE Ê(x)" VB· ¿B`#b;¨L"ÉŠ*}I_1þû‘2·b-?׆­©ó‰\f¿ÇUï#ö»9G/ œ<~ŽW-DvéÂSçÀÒÄTw¼Vá{n¦Ò¤Ç1¦Œ;úÁf!p§0Ú¶£JTfiÒC†c¤pY¼tÁLoG©9úìÝàryûb:sçµg6Álcóq¤6YÍQ­b.wI>ýÔ…=JÄ+ÁçÎí&,BíHï1d pðoÈâyÔw £0(x ÜGmëp ìÎoœübÁ`‹žW‡íÅœ[ð~ñ÷ð2µ€ ._®Á%m¯ÇD.©ÿä ¯ëÞ^~,<·^\ÅJ\&ÇE£ $è9(®˜`ß# Ò¿dü¼‡E~ÔÆÑ peñ’=X¯Â¡ßæÐo³{ ùûm}6]=xúlôÃ…ŠC›M¯){øïÕqïÑqƒÔ¡åæÐr³“k:nv—ô¡ãæÐq³ úÌ;n6÷ˆ·ÉŸ@ƒÿ¥æÐãð?3Æ]zTž…Eq¿‡ö8Ô½yîú aÝÌPäÊ$›td—if»Þ?îD˜l8¥9æ‚lE8…„ÜÁ ëMBÙnˆ2L Z-¸»qn``HÎ`JîUãCŠ’‘žN•щ›ýëVX¼™º]¤OéíL¤™Æpš+¢\†«b¸ràËN¶>ä»Éq­ÆÖÄËV¾Ô˜›ë ˆU]çÒ Wb¬êdÁS¤g"oœAþ8_ÃéfÝÁ B¹±·ëvÍt£ 7öl™¦ÒÌ¥‹¶õséc 7Î XE—ÍEcóA}+¸×õ¨•2›YErÛÒ~·UîE´Bð^Ä gÒ½ÅE®œ(dóº®jÁñIHÇ”´gÎm¸]ÖXˆWµ±¬Ú6hQ;EdáÁ›~†Jy1|ì*ÚÏhü²²eîSÆl5AÄÚ¨³Ñ!‚ß´øÖã›ïJÆþ +¾§TÏÓj«E’v®^Xµýx©‚z]ZK/À­T¼pQšçAxÃìšH£'¢#~!ÑÄ~:uÊ/„œÞ8h<ê¦rc<~Ïç—æ—Šf¢‡Á¥/÷ZÀÜë &\ È Zvÿ/W#°§,’Ç@bß‚éÎR¯GÝêKßÍ´ïŧ¸ûK±õÌŠ—ž“ñàGúûHðÙžendstream endobj 40 0 obj 2151 endobj 44 0 obj <> stream xœí[msÜ4þ~¿Â߸cˆkËòß`Z LhKrÌÀ´ÀÜå®m ÷Ò\Ú¿I¶µ+ùq|—„´€)6ºÕjµ»ZkŸµßQ‹ Òÿâl5zp’¯v#3œ|]—¯FoFE˜èÿ̧ÏVÁ—S51Žƒ2,³`úr…eYDy%5²Tý]¦AGa®V£çãÓIFY)åx9Q£±ùøJQIšeQ¢È£8VòŸO¤"’RŽ×–ó•ù9IËq@sHÐùä(W£y*¸Ì×öw⼜¥aÅ‘4Uâ3Å‘ N>&Æ»1²]#L†™”YÁɧ“£L‘iîðžM”€žðõI‰âÂf$‡ÞžYêÂþ ý±&±Í²ãyÅ`Š0´*³c]Nbô…l¼¬l%?JZÏA?Ñí<—tå"»Y–`~i™ßÏ‘Œ÷óŠ7‰bèÿ'"±— uLŠ&”=“H¨è0nHÂ4Oô£¡¡V–ZÖ”Èôœ,Êø:~ÍtFîì¬sKýµ¿¤³Š!.uÒ³ä ’K"×D^¹kHA†‘ü\d‹/­¶?Xê+GïX軘è°@ag:àÔÃ¥p¶xA ΀®Î"`šà¼38íÊ]Bƒ²i["/¡KΠ°$—`Kö0þ©¥~: ã7, ~ï åðf™¹æD¾…&øN{I$ŽŠW]ì™í¡ÝìSK=±Ô£ÕlL ­uß´™ žu²Ûcn_XŠÂò¸ÃªÀT}¦<‡[Ƨô’,Ö¶P¶5[xí7‡¾Cg &PäNÈ»—¹·P.[ûc ìöÅþ33N°wbg wV¡ÊLS3ú× Ï†ìÉ3ïó³Àƒ1°´óšè޲i½A±ƒ c?náæí©µ.‹ïn”Ñ…%ûüô;Ü7{v1?múL»€6ÚB <ä\Üðt¿ƒ»°:4ÎypRªbà]‘x]šdæÚ­:eó(Ì„…P噢“8–BvŽK]¶JJl’WV†‰ÌLµ¦«ÚÜøv⥡U‘Çú ©Ë~™f¦Æ·òÖõL¡we$¥Þ•eýÔ0«Š_ê­ç]i†2Oʨ0µ{¦Ñ \Ú‹1ûaJ3ëÍ$ªŒa ¾˜TÜ2‹Skë~04æ²Ò7ùéoTð|D5nžˆLUeN9k©wßOJU®ªcÊá*R W‹í*ÖSîÄÉ)sÄf„Ã2œm† 3ƒ;ì…¶†Á¨g°z^Ø’º†¸DæiìC\ư-£:œ ãaò+wH¹@Ë¢ò Oå à‡¶Õ‚ÐÕñ©No­!3rWœY`<~ yIEfì¡2VæÛ‹ ž,¹‚Qf®ëe’ÉŒ8'½Ï->zAøèy1qGY1Ê:„ ó4ŒõÓ6Jë &YšÕº²u´Ìè"ýW¤¡‡ªJ©‹ª~a›>Oª ÍUþ?%¼wXØÁÜ æÅÞcyA-"˜ú2FNVÛ*d «»î”Áw€BœHoÒÒ Üö5¡qp¿¤…äí àQÑúÈRt… Àº`•¾TŒ¶fTIpçÄwÿ ¼‚­®¿‚9·¼†0‰¾êç® ö˜ŽÖø¬vÒwoãžXò˜ «€:܈Ε,vZ tïšÒCSÚiJë4vó¦t»¥:´¦‡ÖôКþw·¦Íc㣫ү)Žî£+ ‚]m×p©kÛÒ=ÚÒ~4éÇÄÝ·¥áe6ÚÑÿ™v´ÃûïlGwô¦†¶ômû«ÿ«¾´Ø×nC;ºÚÑC;zhG·GoߎÞÀm2a{v¦;ÈéP3íY˜â[S|À׎ÇD> ò)äŧßwØ(¾õ3Óx–Y^¡:¦£ž˜þZ'-ŠºØnvs|bgü˜šà|Ñ©•Þ4ÁU(#ĵ£ .Ò¸þ"hlºà‡•ÕTŒ¢²y9pì¬ ™¥é½u"“ªmÕ SûÌÂÉlOÚR@„Ówñ¹VÊ.àWÓ 3ÁÝ¡,¤{»êÎ4ËËVs! ºznðÐ{‰:±¼Ûí!/5Ba°4•‚£a°­˜i7R-¯ à’#ÝôÏ“"Û[ Æë¡Ø‚÷öA=m U>óí¨Tñ ­‡1nìà^ó°7ZÄpé9”€7ɉi~Èg Žfˆáßü¦Y ÐÞ9Þ{fˆó¼y«"•‚³‚2„ðÑÛDMôZà-ÜÙËis˜¿ü·hò´7M0ŸSò¤ôvÈ[Ì3,$LòÁÈòÜ4”¥(ó¦¡,,l=ÐBó5~è|^âågíg¢œëq3èèÛýF$27-«g€v—©÷- ¯¢´ e$À«=sn£˜ÝęěÐVôv!¶ ÞéÆ`Vvöcz×íE'·e€,Œ7cÇTÁŸ¨ÿëDqÔø€Žêø6¯å}è›É~e†:6 ß4‡Yæ>6³í¦–5ôÙàün˜ä:8ß{K"ŒûoúËûú÷n·üo‘~á¢~Ãhû™mø­ÇÖñþ¶þ¤¥Š{úñÇF;h€¢¯Ø„žÅ/iÞ!bßå0¦$:N}Ij@ú¤¯£MO. :ätðE|n@ÐèK¸øN»[çÛÿ éµ4z¾1ï£éè{õïoºIŽŸendstream endobj 45 0 obj 2391 endobj 49 0 obj <> stream xœí[[wã¶~ׯà[¥žŠK$¤'MºmÒ³M¶^õ¡ÝäôȲ|Éú¢µåÍå× HÌ€ü(Jò%99¶ý003˜ÌG~L²TÈ$ó¿ ±¸½:ÒÉÙݨjNŽþ^·g£#“*ÿS5pzq•|1s…HljËdv:ÊRkM¦7\ERî[$Zd©v®Fãd2ûa$DZÈdöf4ûãûñë‰J³Òæùøv2-Ò,Òròfâ ™kÞ˜¸FU”e¦ÆçáùrQ'-3+…P8¯i“Ç*&Rã©§­V63Þ»{Ë}|u¬ cœƒ¾ò…-Jøy [È—4å%kö‘1K‹RHïÛÂÈ=\¾³¥Uc)[›ç»IÝ97$îá<¦ÐÆû˜ÆøyJ®ÝF)#YŠe,Y‚ܥΠÜÒ§Ôq2’+HÒL«f¢*Í`3-|ÁsЩzâD)¬/s>È ²™ö(¤$l{NŽÒIÔäE%G’Ñ”0Ó‹eISGBUßDÞsf›ª4|Šmíç‰ð¹‚¬çReQVÛwHeyw 3Àãž…‡Õ2'DþPY•³ÓÔdÂYÑìÄÑû8g¬—°&^KÈ«?ë¬9ìš{ÚZ5ÞΚ̳h2ÏÜ a@Ø¡ã50/þ‰È9XÃP\ü"ˆîß" ýgË*ÑœÀ|‚À0&¬c°˜žqh:ŠävÖ–[K\ ›¤šÂ7ãý͉‹1c&¹OÞ°£¸zÎ<‰Ê3de Yc×#l„ $5$É#™‰±âNј¨ÙÄwP|ÇP¾¨ xäId¹±‹Òà ò@Ë}y÷¥Ä¸¢ˆ«\xeg'odût Áÿ³cÝtè Ûâ]ë͸„²*p €`\z†ËßàÄÚŒÙç°5\?†ÎÌ®JlÎÆ‹ŸO‰¢­Äu×(äEwϧ;ùÈNT½èðÙu¨Ú:lUkZ½ø£¬Î}«m6sT8¹ W.ŽabyÇ5=þ3¯LázJ!öÒp("”‰¢‚þE¨Øã2OTZ©0w(Û`Ÿ|¬´™QÀ7€3EРÐQC }àZin„ ‡ã T"ãÀøFoÖTˆUi#Œˆñeë½€®â]›Èþµ ís&²ò­†Â&þ–ÀÜÕ8•ë•—Í0¨lW°¦Li3XT°›Ô:µVWR®ÜÅÆä CzÏüF´¶¶f•¾¢JC¨ä‰Ô;ŽÊêT†wÝ¥±¾ yê®!êþ¶‚£óRKÛÁÀ=x­«bj€·9ÍQõÛŠvzÉŠqwwOYd6šêMÀÆ %?—:¯ÞEk°ñý‚Ñ XUÒ×P±Ä¿Èqì·áÃ7d¢ly=86½‘Ǧ`1eKȈ@æ0˜oùÕV±‡:ìñd­j|g…çÒÎPw<ø92—¥п1²£ì®«Û'áä¨GS)FgVøÊŸ0®¯Vãw¡±/_ {Ü€¥s48zõÅI¤m6lwÀ<4MÖ‘ ù”~‡ÀزÅ|mãÍœ#Áo(àM2[b+?0dáw:~‚âÇAº2bŒÉp ´‰M{žt*•i¬æ"6hÙ•Œ^ú`1+:Qqx2©Ì‹(<CßvS; ]ÄN‚PÍгcŒ”ÔmBÒ>W­\Ë]SЙ<¦ƒÒz †’Òsèð{ÑÌõ]ŸuMƒ¸&vÂÍ¿ïÅ´…ã¶dd鲄ů Užæ…l ò±Å^gQøTõ%¹ènÄÜý§¡­ N¼ƒøYÄw ¶òÈì`àN6mÄÛ~Óè×Ï*`:ÙÚwüÑc¢öAüÕ¬¤ýûh‰ûèeŠà~¿ïTü·¸t¶72¼ p# ³àב1à6‡}çpØ컄ÅpÓÓ|»ÑSÄ~Áé<¿àõ=bƒgýwØïà?~}ÀÙð ^oŽ×ËxØ6Û§ïüº_í"oFň"–ðAwPkØŠÝŽs¸ã{>dÓƒ_ˆ­†:`±E¢sõ `w羈Հ³¯A§rlØ×D¾"òÛ=4=hA8iÚÇÄ_ø~r3àAŸâ»^`CŽv÷/ëú–ÿ>Frз„{»/_Éïø•üëÙè_î÷ÿÚÅ-´endstream endobj 50 0 obj 2341 endobj 54 0 obj <> stream xœí\ms·þ®_Áo%;æù€Ãp™¶3Ž­ÆÎxìÔf§í8m†¢(É­$*z±›üú÷‚]ž#Hù-ãiœ«»ÅË-»Ï.üy’gBNr÷¯'V_éÉéÍAóxò껎¸>=øùÀd…û¯yÀéÕÅäÛ…m(ĤÎêj²89ȳº6¹n{“ª´×åD‹<Ó–áâ`:™-þ} DVÊÉâùÁâ÷oì“yQ¨"SjºžY>!•ž^Ïæe–çBÖœÜDïóµR˜ÌT–̳¢¬ªæéîäëY‘åUm'°°}UvˆJN_ÎæUV©R×ÓüûŸš 2×ÓgÔÞ²ÚñµÐÅôp6¯í¼d^ºy•…e7fzEÍ¢¯iž®fsÝ}}ó÷ûûfðð˜H6ùeØÊõ]*ËðÏÅ÷NÚ¿³ $ ¿@v!*Ã%ŸKÑJ~^d¥.”›`Dm¢g²jZ·?6õÚS O½ôÔžúÉSÏßa0Õ*×™PšóUË ê¢ÿ,Gnˆ\¹&ò’Œá˜È ‘KÐLvëd÷ƒr‹Ò¬—ýC¢%ºËq ¤·ooÁÛ•§Î»£Ž=5qý–ÆXë¼ð׬ùuCÖ›–Ó ÇQÖ¥õÐo©u»díÎÙä'ŒþqÚÌYunš!󬬄ìG/ïNì‚uù îR–%Ÿ8Q/Ûž•ÖA?κѕ¡•Kc¨Rgn „b°åÌC$S -ô¡ªØû=$„ué©cÔÑ-‘kˆn.܆Ìr]h»PÊ1Ôªë´ae¸åÜ¿g4È)ްOLÁ›6Àˆ:mþlçcåÖ½f=1œuÒô§…ªG¾åý¬¶ˆÍšÞjB­ø§ø©1`ÖÊ"·VêŠÞ3]r8—``Ób3ó=¼kôHUfáŸÕ›Å±Ýàíd»µ[Œº«r ±SiŸ>€ÀØê•²XU†«C¾"IƒïE¼¬_ ¾ÙÙ·FOI#âmÀ–$ž#Z>F’ðn‡ËïbÍ ”Ú ·(Çmû¦±&–†jú·k'd¼iÊÖ~6óÓA  œQ±nò„x7­ÊH7KÓ«ÌõLgíPýT¤äz2™I­m_Úµ«ªÈ ]L\ŸÆp¥£Íî¾ ß µ4¹—*ZÌ¥çLØŸÍö.Ùî»ÙQhUΡ ' i¸)`Òg#*†,H¯¤*X¢sd"ש™#D iØä;“å\ÝÀÐv«ð› ÕŠ‚M69®µ­EÊ-SÑ«×)ÔÛ~«`ã·‘®Ÿ{z £û.°Ù,ïœöãÅ[ˆ ‰>ñ†±€Ý³²6ñ¼ìdا¹.f¡ÇLžÔ¦Å<  ª¬²_–G„*d ‰|:«r·¼"Â0ÎUµwDIms¨.09;¸ÀYPØñl/íˆ ç\DÄí”h9ÝCjúÜf<†Ðë^ë à{ÀánÏñ=ÁaC+P§=ß4ü’êm¹#”—x²5DÑÛÇžz â@Ô–bHZüÁSW[âEžûøŒ!‰ó98¼‘XÀpNäÛTXÇF»„ÍØÓÓTœŸˆiaÐ!$Ï?QdñÞnIÍh- z3í…á9þ8u;ªñd϶Ðêw¿{‹Ý@^¨Q‡,(IÅÌìÜцa&êh kõ_ìÑ„S§¸c ·s;›¼ÕÜ^; oroE %10²à Ìá6¾]Ùy˜ª7ò‘%lbŒ%üÜ(¢b¶…™hæÆšˆÙórLâb$’„Çuœ@Ua‚7î…9ÛíYÕ¦=¤Ðy-¦‰•…jôo(æ˜ÛyUYõЬÊKb–Ω7¡‘°öÉJ1k•F¨L– lSsUZ¸PÔMQJzá P Ú±("X¡,Æþqë UdwЖbkv š¼ÅÜÄc8o= uüz%5f@ÓÇ´œLþÆ1uÞyû²VlX]¤6ê± a¶ ¤“EŠä¯pBã%¹bqîci°·Üî5{k¸7˜:χ Åvïrö1tÛÇkmPÚ ^kíŽÕkc:­ayh·Æ# åìWÅlÕa¤PÎX lü£EÄ#Œ¤lÛðÃA2ù1æµÍÖpaaδKïÚ¸M›p²0[ŒÝu× Ò¼(â°Ó˜w釤ЦŒÝ²3P2«ª(挂ÜOœþ`YZ¼cÛx\[·ñV ì’…Ø9ñºCº ¸½ì7 UúÄCoå™ÎCƒ—¼·Í˜&3þ,QøÂ>u/Š’À4°O<åؤÒd&WMre6®Jš†°¦3‰#Ÿ^xXÇšDΓҴÃ-ò¿Ìœ f ë=þÖÝ£K¹KêÔܘB?ÄÏp7±NðQIÒÈ"S²ÖPKo¤F2vÆN°öÁÖ ì÷ÊØ¡ÜÎù=³“+-ŠnÒ/c‰¾ªÊìÚëý}.#Él_gŠ\t±ç\–"Ë•Óy™ 3𢌯èXÔë vÒ±W÷ º ŽQeœ?lŽKÙS–¢Œ½Ú³„ÑãX¼+°ÑŪq:CÏ»¦_}ø™Ì°ù‚ÖΪL•>óÍÌÓ¶À¼jkêXÊ ™úwè€},Ž‹±äöú8U#Õt2dk£"¬ô8Ú}B‹¸rÑ}ŽÜº/¬õdÇ6£6¦ÏÑ1…Ž í iÁrGÜã¶€Nd"¯Âcu÷ú¶±éÚÔÞ¹5uŸ¡o.³²4A¼Àl2'ÐBÁÈ‚½äkT_PÊ– [ŸAÜMd¸pl¾š`Ÿ'Þù¤€êÄ=EÙþ9  S† *­oÛfOA™X\e9žG¿€å\çб^ÀÎX½—ÏûGwŸMtO€i´§`ÜW8…tÅÝrDœO ´Ø!ÈYª¾îCʼnJ4?Dš/·JóÿŠxoÑ}ÕŠ88lËÂó6¯£d°É ~&UzÎSµM†¹õâm¤dT pFõ’¥ZÉj°$|ÁÙú ¬O s? y#„&\n 0óý  î6:Ã玬ï> p,1Ø×Ð(£zøÁ޳T;@,äàñâ0À·!(Œ!F²ìÛ—p4F’Ä–©5LFLÇÙ.ãÅy¢TŽoåˆ]O¡˜¢3ĉ2P JOµÉ‹½§Å¦ôP—ò–ufŒ¯½ÂyýaAŸD¥ƒ‚>ûÆÅ[”i þzêÎÛ¤d e¼öo¨áÿšYS/­û{ÝJÆKˆò†‡s; P+ƒã}\0|u¤™Ç¨B¡™'VØÕ0g’';XS¤öLÙYnòª3@&Ú'´£B¦Æ$à-IÓ¶‡ :ÕJ•o‚0uw#Q¡!{ý1òÜ0wgdÀ_§rwÉÔÜK"löò~7šÆÓÒމ0ר¢OT¯'­ó‚êõ{ÐÞ§£®#oÎyÚ¨0o T¤Ó…ùc¦_8ðÝšŸ¾ï<õGOxê­§Î=E—°è’!]¼ˆøF7‹>0ÆkÉknW)† |º ìv+«cóÅ%x>Vô§à=ÇöÆìœŽF¶CÚ «ØŽO I/Ö.kD¢o6*³ß%GÎÐ €”œÜçÊHtIÃ{zÖ KìmÆ–Ó<䥑WÙ£¨ŒUª' ò˜¹=Gg›8Õ‹?fŸræ:q<5R¾7Hh·P¢ÌªRÅ7”¨'¹ZœX—%èø6WÅgnyÙyùÂþߟ%…»×è«Ç§ñ;V…v¯Ðù>åèKàô><ë.Øn¤ÚØþý¥½Ç¡ºíR0Þߘ·n1´_OmùÛŽ–l{Fó1èù A 4ê…a¸n`¥üÎN217õz,öëÌýá›ÇØyaW¹‚s¸„ä6cý¾Mu†~뀋ŸCЯI ; ϶¶ ·_?Ú(0º§àÛKø4yÉ€1¬ /†KûÜjO*É;¨[v:%)”É*íáÄ—ÐÓy?g Ud¾>]öéáÇ0i£‡ÇPL7P¤‰•é•ÀúÆp">y±&ІÑ{iöyI™Œ àï €óóõ(ð5•ÀF;\¡eø Ÿ¾s j>“à“e«.7†ñ>9c†õÓUÛl%Zhk¿‹Ê>Y«(ƒ0ïy‡7vú­¦}btGb¯d~ݽ'l. ™D(÷ûQ¤oýlÿê©?§œ1# 7ÅÀ62[ƒ 1Æ:†Í° gÍÞBÞ5(¾û÷i~È*qœŽ|×?öPÈÔïK¡#ÖT`ŒƒýÈ‘wP¿Àf aí8bƒÊK>yõäï—})¹±Î0ÀÍö—Û¨¸D"HOêø|D˜@B) b¤w)ŽÑð…á¤ýÀŒ‰íÊõc­ÁÈ–§ … æv»ûÛ7þ94ü]û\ÊÞ_܇‹ƒ¿ØÿˆÑ endstream endobj 55 0 obj 3488 endobj 59 0 obj <> stream xœå\msܶþ®_qßzêøh$@²i:ã8JêNœÄÉu2§¬Ó‹ÕêåbIvÒ$ÿ½HbÀƒÃ,'Î4žÉ¬xx],vŸ}!˜•E%f¥ù7«‹½‡ß´³Óë=ûxöÍç#ñút®¨Íö§W³O–º£¨g}Ñ«Ùòd¯,ú¾+ÛaÔj¦¤þ»—³¶ªŠ¦›-/ö^Ìgû‹ºúïf¾Ôd!Ûº™?qÔSGŒ”P¦*Û¢jZÞ‘Âõj¼^UW5…èyÓÿ ª¾žù‘‡D^ù’È×D9#r G8‡ä‘—DžÂqqÛ¸!¶È7pn BOñ¯å?ôAV•;H-Ë/öô/ÚÓg'í_Ë?¿0GV©¢­”˜¿Ú/5!ô™;j¦©Z*UÖóëýE[”e¥û}E¯ÜÃKG¡nˆªEÙšýI3’>Ü‹ý…hв­[Í¡Æ4è›qPÛô%‘çîw#•¦›~\‰ýý–H6íÚ¶­eWû ÜÃjµàó°e­Ñ ØfXSöô.Æð5fO­¤OÄv`ÃŒ!êž?e#0V²§D{2ÇÂÆbÛ?¶ò#jYȺÑy¤ï9•õúË0m]V£Ô-¦n‹QÎŽŒÔ-ÿí땪,ºIÙ]¾t×þ¥£.u¼ƒ¹v½ÎõßíGZÁÉãÜ-¾o¦æŒèø}l/>q«ý§£>ó•¡ÐllE‚ë53ÒXµEçmñj‘k¸¦³Ž`·ø«:¬ß.`7¦y_Ã#YÁÁ®!y vÉ§ð­£žï ‘Uæ<ŽàBß@â]3¾1›w yñìvM;ÑÓo»>u›üÊQ_Þ ü¦ìbƒ1ÙºÌuÛ]‰»Nl#àDRöÈQ$_$˜ 8•ãäQ#™ˆ­á˜ÕlâkȾCÈß{< xåéHr—“7+Þ‹ßk8.›H»Þ›ìÿ.Nà Ü46êxÓØa+ö„ȇD~Ûâ3Àæó 27Àzm»]í°²{”[r\úY%<´«Tʾjð@õ¹—…Btb^º®ªF4Éçýþ¢/:=Jeo¨ê‹ºQZËjG¨o oEW4Äj3©¯o_WÂlY_z©4¹ÇíšNCt÷øµ}¬¡{) vi†~EÏnu=.¥³ÒéVøÔbqQU5k{Îܺoë¾ì̹˜ç¥Ð \ÃÖÇlÁ?2š?mVÒWµ&¯Ùãüû9›õ™©-Ue}ÈaÒç–BÔÝüëhï`Ñ©¦™¸¬Œ;±Í~¿?ÎÐtЋM„#dÓµ0Â0¸»9¶ÏöeÑÕT¼ÏóýJjUSöܱ|™tqG̵|´_¥ÒÒd÷9úšOuSýPÈÖ›ÿ%t‚©ß6¾³SÍ—ÎyžBìä;»Q™çz‹š²Ðø9wøGè±b‡’1ñ­^‹ÕwÌce xYÏ–½†îñ òn¯`SeŸªJ“‚&;wë:3[¨eÏ™ö‚ÆŸqÒ z`„§-ûÊÛ¶ûG7½½fºy÷`¡ Õȶ÷/´aÁó>r½"ùoÛ®©xÛ±†Iç%ä÷anãµQVó2©w‹8 eUê¦ì²±ûü€"1ƒ.¢/šRL2sÈÃH޼á½D«ÍGßÚÀH£mJ Œ¼`z„Âg7P>¦ÉzÜ’tÜGa+T\Nc†G·Yu¹ H„Q,öJƒ½:­Ѩ.à‚¨µ¶îÛÁ4kóÆ>‚âqjð14覂êm¸¦Úµþ5udda6ˆå%Œ8NÊVO°ý$Š®ëAô {7Épý4QYõ¾î&ìñ{J Ø`ØØæíDÆ”f­üÊ;> S˲öN2Ú5våÚ¬Ö’â¤Ãß%NJ±G‘5sÔÏŽzÚ=wÔ/ Ö@òÆ~sP;ä8ÓúhÍ;jŠ÷&Ýj½Ãžn6 ƒ}:ê½…#à‰³ž“á=¥fH…˜/©åaâæ–áÛÀž…ÊáD&ðèé5˜ew²ñÁ¤¾*Úò{¹6^dItóFtÕÁŒ`ÌÓ͆¡–{,Øo÷éU׃áVŒ´¼3q³wÃêTYÍÔ–©å3dç 9¿„ëÂÙ4Ï•qmÙ˜W©,k°N4@+Ï•ýJ:ÐÏô>vÂØÓÏ”e˜[€}Gnqh£cYþ„ȇD~EX\Zk¥ÜI@ª”>Ðb&™Oäï$Íä4ë2<ò)Í‹p:Wewøo÷{£ZœD?ÉQÛùn¢Ãæ«”OÚ˜CÝLn¢Ð=:9I cÍåÁ!ñKU”uï!~Ò¶k Ÿ"Ù |?~C'­~Š´›Áª7Z¶šÎ·U–^¦•m«"a3)Þ–:ì¤aÒjþNžê‡)˜¿9Š/è.ØþÎàpÐH¥i4É×i‡‹!|þGE‰”žrXbòª0T¤ðÝ2ÃtIÈ?CP¬ÎÁÀ¤îBh‡®×.öï¬Ù¾Ïû²—]d?#ó.Ø3R'ƒ3¼,«nS-~°’‡[#Hû¯|]k¨#˜~=¬…+‡ ìÒÓû8qdb·<ñRß¤ì¼ØÛeìð&s«aEC¯F{Øê.,U&cøyv°Îð1ÜM\kgö”‘® o³ ¯¯l )ˆÂ‡ø’xf$¬Û-ÜE,ÎuïÙcÖvÛqï$ÔktåSZ©íÒ®J£ R“Ø„ÃØX maÐòŒ¡F]uÑJi-f× >†m ÌÙcGý}_•edžje\9 Ä:Ô‚“[h}ßKLŸ"ÓGÑù ÐI&®3Ü.þÅ”é&‡ÔLî"ö‡NáyS]·¸q:¬Eµcxí.Ò’†Uzw ÷Õ5þÊÑo Êþ óñ,†År÷,Èõ7ÎrOw ÕáB€l,'â²–]jYR›7RTׯ‘v+0õ°˜Z{"ç!«1XݶDVQín·Öæ^¡>[z]»ƒÓ(«Ñ¥<̈ã€0µÌtC,a8óÛ²íA}£¹ºˆ²R¥Pœd‡§¯3–^QI Yó õY+ÙèutŠd-ÐÐ<áÀIä•ÕÝø®ra^â¹$w0ú¦Ày& –µix†6S.ék—Ãf5nûeÆ7E -²UÊù›}i ÅøÌC¯)å§‹AhLq›¬ ’ <õ™dª1Þ †iùÀU"ƒL: FÁ§ŸŸüÙTmâ{‚ùb–8vOmŸù(ä8Uˆ ãÞ:¼<Áû"دÈiÉkÒ²£©è‰#FÑM›YÄoÓÈ*±ç[Ȫ¬u5ûÆÈØ$}—i»0˜-7 Ýy]RC±Lô¡Dl¬?Âó³ô,b˜É’©rì²`ÿû)ð8 »»è¢•ÇÏ‘uxÝ,öM•3LǸs*"?ÉKW;yÁ%Ó EΓŠò°Ê ¦š¾¨:•RS¤R"¹NÇÑkl0Ⱦ˜‡F%YõÞ–Ræ£Ø@³Â%<åUØg¾žUJ¦rsbåûžö=Ep ¶ñÒ|ƒd - ¬+~c§ üšže5›)œQĺôSB¼Ëàtr‹C˜²ò Z"éÆíohYxcGQ9–F0¦ˆwÚn6ý¹K¢e,PìÚ¤ìEˆa&yàÊáîxÃoH°çGôV,–—é¾{úuC›Ý9 qïPLàž¦b7SÙãTÊ :Gʱ61×0%?z¾¦ïåü;ÝßèçK„½÷zÓƒH¼-„øàÀð¹kºÂn;®|“3!ØEXpâ½Uîc«=ôÐõ˜¤&9 ¸C ƒ<Ú2Ú hà^¡E?·˜N+xXn;^q–3 Ò'ÆßÜ0:Î.àæ]`ËP^¥R…8‰ƒ ZpLÃ+U˜²ªn'M‚O!2Ö6ï|ŽB)8¢u¿è iÆã+¸š-J™•OAÄ:U‹ýº‹áGÖYÇJ ã.›"©˜ÜŸºÒK¦¢Jô…¨w‰˜š ¼ë‚d†Ù™ÅèÜ(SøÑRáÇ‹)`ÔUÁó=¨†!Š‘eæÆPX‡W<úeF1<Û\=x·d~ˆlnøñ£¥fœ(QÁÃ|*êã´¼^!,—‡´Üé-*Ç‹w‰lmUc…CÇž›á˜—R™`t0Ù¬æ–Å^Ó½ŠUYÇà¦BɃ—…”.³› ¢³þl¬Æá24ºi%ò<ˆg¯žå>ç‚p'Þ×.÷,´Mš__)ÛÝ¢Œ­1zܾ¬pCÝ„)¤|ågcì]Êå'޵݆`å;™[§ ×Ö«›`¤»¯»B4Ò¨Oö‘‹4Cc$yËѧҡ4오òWYfk›ÂÜ÷¤†«¼iöÎÁ0ž@š1ëÈdq}¾â #Ê}héP*]Èoõ¨Ù² à';VÙ+€T¥ëqhæ£p)áwä 3KbÙ ¸Ì=ÍÔ<&uÿÆâã"UfƒhY¤ÄŽÐ‹ø T:öPØÂ¢˜ wl·õí 7ÃRîÝôîjKv!•ãð&YÕÂű./.Ú.ªþÓ3W%Õ§~Kå<D.aÐÏD>ƒmŸù ‘ˆü‚ȧDþ gûT3@jdxor"Ùzɾó%$`·¥)a4ÂWÙ7M!aÆÿg«½×æ™*;p ‚£Mw–}QÛ·fEQ«aùôõ,ôžÃ®åz÷Wp‡ H{UT½¶pøµ£ È s?òyÓ”^MZàg\[Œÿ™ÊGï21~ÆTfüšFœâAß °%¬ç‘ù`fO)Æ2»ýÄË–a¤’ ô–5¹Ö—jLÃ÷N}äŠNmm)SBžÜÑçñ¾ûÞÈy Öò% ÂL+z/}Ã4]ËUÓhNû Vz|Û›u¾‚u˜o¡þd¯m³jRüÝ©ì:³¯ƒ_BòvÛâû[¶°´ê U9M}$t150B+î(JBý&²ô-Q£¯¢_™¤¥ågüªÛå×g?£†»áoM^ÉOà"_ö/á¸ÞÄV”´uPÂ9sì#˜»|òàže> stream xœí[[sÛ6~ׯàÛZ;1C€$HvŸ’6ݦI7m¢Ng·ÝÙ‘-%öÆ’ÜÈŽÝüúx>’/²åF3›äå˜ï\yð»øBzù_§«Éã׉÷n;É{¯ÿ^ÞM~Ÿ¤~hþå>]yOgz¢ ½ÌÏ”7{; ü,Kƒ¤à*<뿳ØK„ð£Ô›­&GÞtö߉þSê?_NfýU?9©DGO5éÇIýl©oKJ*3RHø‰ÄI@¦v–~(#‘øi‚¿Ÿ¿‹,<:säÜ‘èS˜våÈ%æ9òđ׎|KIÆÌìòß³ï ZÑH°ÞXêŸ5ØTø"J:`°-è:?:òœbµÜ•­#ÿ Ó« *¤w6ªdßØÝ¾²Ô?,õìsã&;pfG®‡à¾nLÝlωº=±”SË— ¤þT \Ði[ŠÎ …oPSu;Åœíà‡à„mƒ ú6 Át/)XÒs¬ p÷Ä»ó&§ÜîÙ×-èJB¸ò+jÅ\½Wt×§”ã:öbOhv8 ç ~&¾"˜¿¹/ª°Êk Çš[ÿ ÀÜçò$áAÜÃÍ—„jàj$‡öwY÷3©ˆ|™áÐ5”õ„âÃs¤ Ål‡˜Ý1\Ò5\PÒ®a@ó]8dúî¤öK='ãêÁ’`È2n—®ì0à¹#;ò;@6äiïã×BØO ý‰¢‘Ö¿üÅÀË w ±P~"”Ô¹¼Ž…Bê âw‹»§°îEÁ@ ‰¼áåÀ̼Q?•¡]RˆŽ5Ã4Ÿ.¿Ð)•„JpΓ_uø Í2a,8Za÷²i‚—“KÜ¡ÅÞyê6°¡hÀ4`vÚÚ·S8³°›µ¦hÁæChÁ./à =B(д³n´ÉèУ²´´­66ìeÀŒO««žÈŒUf€Beª±(M5Ÿåö;/ÞªX5´'2%ˆÜ7Ú°ß³ReR?”•Æ\S®5ìe’h¶‰™{őلw\º#Ãba}ÃÆzŽ›iæ'i˜Ä%ZTÿÌC"Ô³ðowÙrp€Z‡ƒ‘¹$±DYõ891l{¾†çKYã¨kŒ þ³ðQ*&ÂÀÓÑ1ˆ\é)‹¥§8JŒ¨g+-%Ú¾*ÑAÒ7šgA`r-\³:¿RwLÑI;‘ÀÈîÜ»…éK Å`™›P5ø»©ôUHY:šöv®ÎtÇ;é=Ð0í·#½‡8 56¡©7flfz/,ÛߦùH&,ØvÔïâPøiT«ßÙüç[³ üœ2ÜÆ#”‚°¹3Ý®4Ú œÓ)Ör@'ê÷¾t*2ÂWpO_³ kܧ7u&¬jìÌîÚj}ËšÎðªGçX°›7ÖÖ|.àX†Ú)S^¦¦Ä ØÐ°ÞpI1,c±6Ç4Ž*Ϻ˾ nBŠSù6­¹'4¤p·b|ëÔÜ_gxibÓóµ£ô] $<…庸xKÑ_ ;ë£;g›¼D g;ö ³Ø¢¨¶˜%M`€­0OuʳĂW’ê%ñØäVmÖÔÿC@OÝÓGî)°íJ jq<Ô^O_co ­á­œgº¶~‹ÎÙ:Îâ“ɯuV%j* 2ã®ü‰}7@ЇT`ws‘J§â#ÍÅ8•.s± Æ}"ÁaÔÆ|¿i-«í©/=êÉ5íº‘‹Ø\øjêOm›7¶t³Û€í§,ð«©ýþ)œçqÅßidnk-6ø¬&ô¦U?À*„+ÿ´«n‘ÎÚã\‘áÇZ=¢¿#ä.M (HðÒØ%«K:-²´tdìHåÈ0o:¶·oØå}axnhµå¡`ßÒW vAå2$þšÛ~à ¡÷ÀjIÉÏ'–„ZCz`†ÝW.P^k¸¦ }>a€aÔF¢Ã2—xbòýGŠäAˆd6Â:sQ÷• `r¹=0a@H ©ëÆ2¨µÐÆ !Ù£ø±ÞO?Ñ@Xɨõ‚ûrÆî(! ÌÎ+œQ|?Ÿ„@à³ ªZà{œÀqqMÕµÖ2F¼»œ&ànò•œ<Ø¥íúÄ‘3J€¯ù]S`®ª_o¬&jtcµÑܬÿ Ÿ¿X,éí»zÍOfÖOîËYÛjf¾ÑƒZÃÑhyU=€šã%÷°ÖXÙíªjË$-L 7ZÔ©Š+Ÿ:S}U#Eì«(wr*ÌšZÆ«V(e;ÞƒÝé•9/˜÷³ìjæ7›îP  Uæ§aè—îcAúAãÝGŸFêµ¹×´™õ1vííA}§hµXM[h¨£ ®‰Ö_—5 WCy—~7¥f®ˆw€ÈzŸä]é>2?–²×}ìÒgaµÓâ w©®mš0·ÔÊRËR‡­un©Oã9ñ£‡+JòÔ²vj­Èت˜?ö Û“Ö³ÝNÇ«Ú,r¢ŸXã90»40þÈÿ“¡¥Ümƒ‡JßÓÔv¸ $/nû!`býá¡àšÉ;îøe‡Áeô•£/·h:k$~yfÄ•/—gö…ä—Ë3ÿW—g:Tû¢²/v€{èÎæ …ð|yîŒ4gÐ雡ó‹¥,4 i¯Ëºœ»à½‹ƒu­ä¨.ŽvðæçòS±s:mM§ñKGÜ5q…„ˆöhSám=^Yçwxú®*|¹ògÜñ».<3ghUV¨põØúï>d}Ö¨ª:}–AÙª\ãW.+6t3çõïꎪHW-¡â G5ËÃæ,ø}U2‰+@sèšßŠ9 ¥Õ­„Ú´žµ.—í“z{"^órHAyZľ7FTüçdZ3ÈVªNc„«W8q\ôæä×+òë©¥Î,åÔ`QôHÄ¿Ðø­;Ë“ ;ù’.ÌyîÄcÀ÷HÉ`Ä;–ÖtÕZ.'þþGsߣ.êþÏŠŽ<ªÝ8~¿ìvòpçÎ¦á“ØÐË€×ÉK¦ƒM¡f4 r+o]-ŠÍ‰åæÜ¸yqO믆6Õz"3_ yô7ìzuàÈÏZ÷L—é’"0xsðöàX™÷><ú ~8ºãÄ/iYq èZ«#™Ò'™ ˆµw?wäc· ˜À7Ý—-»Þ&ÞHê»ØZ×g7ø€ïi©2:ýKSrYNöû%¯H•Šöl6ùIÿÿugÈendstream endobj 65 0 obj 2672 endobj 69 0 obj <> stream xœÝ\]wÛ6}÷¯àÛJ{"A€Ü·4ñ¶»MÚl¢==ÝtdIQ²õW-Û=ɯ_€˜¡x)Š–+ÛizN&00\\ €ÁпG"–*þO-ÌÏŽ¾yk£Õú¨(ŽÞ~W W«£ß²8ñÿ\žŸEßN]ÃDDRÇBGÓG"ÎóL亨 #“ºçid…-*œ½©ñDÄF«õ(OtœæBè‘Û8×&EãI’åN½«8IÉGã$"±éè–µþD­ÏË *­‚Ñ¿wšL&”-Yû“¨ÙËÐŒuµf­~+NóªU–åùèב¯‘[!LÊTü¤7Åϵ°¹ý:v*Ò<ÑÒ©øïôŸO)#7|CpZµ§É¥Såát¦ÿ;’2NU4}u4ýë{‡šˆ…ÉUj­ÃMÄV*m=nn „4ÆE»Â$÷¨M\ÓÄ™¿ #'yÐE2ú~¼ ?žiQH‰¾¯h¥Îy)õã ³«‰Ìc7Íû¸ ú—$^SÒrQ™“%ÎðPµÐñún IjSUÕòœšûJ§AÓ 5_Ò%PÐwÝÆ£³îŒJO¨n9Ùii[Ί®Ç•Ä63ÉèóXÚXj‘vtÏÆÏ*œ9nõÙTºî¥rª´ÇÚSI©466uÔ™.ÜÂÃ63,Î7ç.ÕŠ°.9¤’œÏøãÜw꤫ñ$õS¬kb–pZ1”§A-kµ€j±˜]ývW3tÍ DDsÉ:š@5ۓư™©Ï¨Ì sØl5Ø|68k¤ª0ôS»ä–„‘™«¥j”$&5aà6ã¥IA$e[Ì­W9ÑZljµÑ¤rR¥âcEz™¸åßö6­ùô…¨5ûù"ü¼=ÞÜŽSï3‰Õ{§]·ê¢›¥7[>×|kr{Ìš/Þ+Ò”çö‚ÐÛßJe‰¨÷÷×_¼£×ÞÏÿøæ­JÂN!EœÕ;€ßA…rUõèãÔ&zt¤i¢ ½ ÒAz¤sÐâU~ Ò  å¸Õ‡*餜ÉVqc™¸.ëʼðµxMbDâ ‰ç$~êkvKÿ ñªOÙj8%ñ‚Äy-*U('¼žëƒÍèÏAz¤€>Žôcžé[À•lxÂXÂ.f$ž@ºô°Avÿ}ëR¤_€ïW+ñ"HW ¢¡Á‹P˜©*ÓYé& wúq;HfŽ/¬î¬ð¡Ï9\Bñ3T¶€ö® ’$ÆÝLòpàûãTlÆu…IE'_ë d¢ï@ÙKÐb(qˆ#NÊ K+ÞhÛô1¸œ^ -`fC¯GÁʰ§ªÙdü™±f&龌g„-m˜”»áð•\܇¥°”Ê,a°ÁR#üñØ> –¾„]ôî{XYB¢ÜäÅÁ6Aj¡´ ˜¦1?ëvp~d5Ó6yª­±>U’1êá£K·¥³ØØp‰ÕܰҧTíSèv$·J÷ë]<0i|Â{à hÙ9ÔÀ,cgx¼Yɱµ–ÁÖÆžtÐCÔvíå–3b`îg›ýyg$ 3›ZåÊM-ýʹV:##kgÔË:ä­J gm; HÕOÊ46>¿O!Zx»Ë˜RÓôöæ› ;¥Žµ+¯µo ü[\³â‰Ç$6ûžÛvg±ÈUˆåÇ0ù„‰Ž–ÒKÕ™ä2U¾E‘KTeʰŒ–Ô„¬XNÌ©•sæ)¸†¹8m^û Ìlf#£ÛL )@<-©'u„™uCƒa©oLÄ”êJÈ’±†H©\MIadû ŠÓN²y6h£A3¸µV{©÷Ü©\”Ykn g>bíÇ£§Ûp:ÍÜC/Å›y2!räsd¼|îS;Mîv¸SJÀ!µgQÃi†Ä¤,a²ö”·(ç‡õ®h²Rª``ö™„NøòØHts»<÷Ãl€lùl&S22îÄìfJÙ¤+'Š)¸WOĺ¸Þœ‹ªY™™Å™A9<æj¶:‘w6ƒã¥Mæ6—cät0âxÍãΘ†2Ÿ,qÿ×ÅJÌ+—ê.ie pƒø¤Û -Á¹vi¤ÏA:õÖAº–~ØlÁ€ £«î|ˆß(®ZíïçXY_Ö¼üšÄŸY1hØxô;ƒã·ÿSh;`³ÛÓ{‚Ü–ÛáÏpe`;^a,ƒffœn²—@ª9RÝ}*=-jídæŽe*o^žëÐEÖ¨N9Š l1‰,lÀF²Æ|Õvá¨OûæÝ{%¥ÅIœ^‰–ËÞÐ6ñìBV!dD¦KݪdÑR@n§ õ‡Ö¶eˆtæ4]±²ºQVª+ ±bÁÝ1è`¬†X¡f![ah”e µ´eQlKäèœ.ô¬žÁFÇ×»Ls1Ľë‘M†ª\²‘­Ê4i-ŽpØfí–ïPâÒœv_+ítÓï°qaç@ðñ -vÞ½keWžîöÆ µkÂm°›¼¨B?ì)°_î ÂbòvyŠžt„>Ï>äá{ëèŽF[}¼ãèr÷"±+‡¼7ƒÑKÐHÁÑC—Ù¹ƒÑ8z† ÛÖQ8ÿ“oû†¿ûä4ü!“oáðSˆ„Ùþ“Þõª F½ç>‰%]®FÀQ 8ê]_«‡¼eîEù ¾—ò½Ž]-Àãdåw{KÜaò»6÷ö>Ýt#÷ƒ–ñ¡NzeH ƒüFöH°;½m¿o5‚œRù¯çë£gV+bEáò£C×T«Lò 'ÿP»Ž1òï´ëŸ®Ð»K#À¢V°9Š–µ"\=ŸjÏ˧œ¡¡rÿý5E[Iê (†¸Å€b6¨ŠU-K…ãXÛR/Þ!zŽâ›_Õ¶pmàŠxc¢ã¼ø¥ E€dµ‰ûFdqH°õ™è¤îì.a=z†žé,HË>OÐ~Kn„¿ì®iN«Å®Î Ø›ŸÙ =5¾¢íòŠôùÛ ý2 r{¦ÑŠïéÍ—·š…äØ#ó «ëD<Äq1 ‹ÏÐôO}zQ´°ãBiï€ôbü§ÌÈ´Œ›'œ)Ì&}¡]èb 5ôF¯‡0CºØ9a†’Yž·Êîy׊,½¸ö¡^·†ÒiÊÝ€ýñ÷~ðÀƽ€âìm½4Ùô 8Z¼ws¢L(Ä.å.Y¼´@,M ±aØWÈ{ï\¦> stream xœí[ms7þî_áou:Í!étoÌð¡„LKK f:`:&qBšØ1‰] ¿¾{oÚÕÝs>;f>,:iµûh¥}ôâwCh3TùßZ8š î=K†§×ƒ¢xøì§J¸:¼¤A˜ÿ) ¤|4>SC³ ‹‡ã“ ²,UI©UãˆþŸEÃDëÀ¦Ãñlðr4ÜÙ •¡ÿÛщA”„vô£“žV’‰óšÚh$F6¢q­¨ÐXi"¿ÏÊï: G,Y¼fqÊâ,=bqY‹¹¯Ç¿ ÈSCž>Œ¿÷|}îl;鱓‰}Ïvo­ûÖ-Õ60™¬Š½â9eÉâ‹3¨á¨aë^ÁºÖp„ s×w!Æ‚qyö¿Ž‹âÂ>¥g°Ù6»€XcÐ. †M(ï=ÓÚMyZBUúò]qdjŒ÷wÂ@ÅYéd(¥Éu!^îÐÌÖÆ&²pH…aÇ*½uß'N"©0ÊÈþºŒlN¨(‰ Áb©0Ì,UÌ?‡F%£ÓªMJí\•¦F"¿L¸ióÓI¡*Ѷ¶¾¨% ¸pZ/¹×÷;Y¤4⥾û•’¨F³€Î[EiÍCKÈÔÅÕ.[e„êb­¢#'q×b%Á‹Ê1Œ· hÖZ2 $NÏ]KÏ«Rš€¯Kð•}z ¼;ö¡«l8vßÏš8t¬SÏkÍ„qmMÅzU«çÌ=Ó¦]¡ôi>_O - »‚nÒBŽj2ˆ+'Íš*7‚ëºe1 '}+âÔ‹S$®+*|ìˀβ­ÆôÐÁô܃»™Áî9éÔ»á’âg/OÏbMkÛ16œ1u³3Ìô–° ÇY8ß…Äfm ,sÜÌ68nd) ˜’ãPA¬ã„@ vvmeJÙQ´“™Óš”Vûœ`˜"»d¹ ®Íiž;U˜DäM!™$÷}™ËY¢TåXë?U†\ù Y¬³€à²‚N¬X_É|ò(ÂV.%…êñhÂÞp…3DhØVÏ„Ähâ<ÑžK[Sé ã(–Ø^ÂNgù^Œ4õ¯ i"ðÏ+¯Ã¬ÃÔUW†ZÚ$ß~ŽižÍ¥Ñ¶×åè›° ݵY%!K|fZ9ÉæØ’)[ å…³jÚÁ5Ñåâ†ÙÖ,šp§I±ÛžÝ®=ÛréE[alP2ý«fùn=»ó?®¦ò&l–ÓÖ¾“x Æ)ê|åã‚9hñ¤¼='ý ú}䥷õ‡˜õâĶ‚i鬯Ù–¾‡I+ÃÔIlïDºÛîs¨Á¥Ñž­ôçÑ?œÄÛõÇ@Ë>~}<±²E4|µ!`d)>.xÃe]4ltp'úŠKç\Wf:/“Ȧ¹ò¨`—\‘ ðš\¨ŸD8¥ã”:¥×Ì$РΠâxƒ d uªE†šJÕDN6hE×iSçpa»°râYYç%‘¤© #ŸYU癃|Z ›–¢³‰Êôè‰=â0øœªkpœ†Œ’‹ÚB†Z£¾´Ž)„+AG:Q­ëÊΚԎÿývÚ¯F°/íÇ_6ïïzOA×{à¤úÔ¡\óc•ûîÝ%Ü…å_dj|Ÿ1uqo`xG,*Ì`é‹a…|3YËJÖÈÊ*Ú$q½"<ØI|;0ësŸWÒ6¶‘ÛvkÓvKÎs<èˆåŸÄœ“YË—Œê-9ì[8à‚h|ÅÁŒï‹zÂvZ“ºuí-˜I“DWk{¾Švˆ6olÝr´R|yáá]]ˆ£Q½‹yø„F$ly˜$-!ëy‰²Q=I{ ¶Ž‚8߬b†+ÕÙ”JÛº‡ öÆÈNM[R*¯µ?ãÃ.‹) \؉p:„â¸3œòñ¶1šóY¾Ú»@1¦c[Ò>ȉmìÒœQ¹]”æó6p­ åñ*Y>jkRí »6zíÆ½µ[¼æ–¡;N;Žcýå{éÆ&&7FÜñ–Ýäç¯8DÙÄB†}ý}êa]u Gïöa+ša¤ðžî´¹1ÎìºÜÈ…Äâ¬T¼SZ«ëïÖn¢Rs“CD&L|¿Øº@܆Ô{·›7×$Š Q3(â3Áf¶»!g‚øÂ§€íç&BÔ®UÜ×jßz/âð3¡Í™eÏdûIÙ'ã B$Îè0AÅ—üÇP¾ìî•0¼Ùû<À¼ïƒßÆâàÂñ°i ·vÌô´çÉúUa ÿñµ2~¿%ÜÃn¦Þ4Ö#d¾&„ÌV™&B `xò´ÏÂ>Ds÷_è.N ø‘vLlœPÄûâS¨¯lزŽóžmC‰Á^Aç°ŸW}¨®=åéXFø–è…“xâüy£I‚ãÃù±£>èß@°8‰WQøè)€ ..¼åæ³0>$Ø¿«¸ eøQ-nv#ÜP¸myR//KPR}Hâ%Ï%üF OW<Ë{ßÚms!ùYb˜‡€#wì¼þ„õná_áãL³Mrød±ø/|Üý˜#\ÏŸ e÷ÊæO»Þ–âsæÇ,ÞcñÖ½ÑcȾpÞø~¹_9`Žã;ŠyLï/Jnõ+‡»úCôC¤íHð­`é˜Áˆ"1P/¤œtßI‰“^÷Ẫ¦¯ê o«n¿K¹íG×–GÁÒÞÛdÓE‚—Yô¸ü3 ¡î«ŠÉþu|½?ºë#Ô¸{ üëGûí'V[þÄÊÝdŒ¾ýÖJž$w¬åwá7WHÏ´fk¾äo®ú1¼ ¿¹z0D6´°îÚ3`öǃßéï™!§ endstream endobj 75 0 obj 2332 endobj 79 0 obj <> stream xœí[Í’·¾ïS°’ƒÉX 0?˜qœTÉåÈ‘%Gâ)*vIjw£]’Úåê§\z…ŽzÆ0ÉĶƒmŒkT!ôÖÓº'êu£­äKe³žQÏN0l:¼@;ŒdÅÆûfÁÅmÆ€µ2À8.ᰦ領§¥‹¼Û©ªä(cÃÃ1†) A@/þ<ˆÀO0:ûUaŽ£VCªíÁ9¹?:¦g]͵±í¸¶iÀ‘þç°Óч€›¯¸ßÝ‘8p_eÙ‡‡d®ÕQ)ÎüžÿN9’ûžz¨Ï ‹üá6¢™ˆfë¿¿ÌiÂM,ì/ÐÁ lïë Øè‰§([Fj ,áTgsmejœL ;꺄X’‰ü€ÂÐiXÅ› @ÊcöIœ†u§+—#Rõnì ád??ðḈÁŒ7.ÍaW ü”ö"8Pôy0O€ÉMR⯀K_^Ç“Ç>¹ÅvŽ£i›ç«ò‚ç=šDŸJu<<ž x-–Å0Ýæ‰a› ölªNf4Žûl…b`ȇû¨”Ó›L•¬ì°´.‹<är_« Â&»¬õ Ƈl<ÏØ¡µ`ëè©S3óYN„I;ådSRFÕHΤÛì¬q%R©¤®•Ef­‘2{ÞU“Ù»P#ìI^‡¹ú|nŠy­'&ã_æ…ª¹õáa‡d&— ÄÅøF²7nü‡=6y%]²¸ñÃÆýþ¨¿hËBñ ˈ¯¹ôœ];í ¾…b‰Ì#W /­Ñ‰ÙffSbnªk/ª˜+Û`p1N/;‰ÝWWUõ\Üðe«"KEñÚ¢Þð­KAèë €­ƒðÇO½#þ¾— ÐªX†éLÒ.!¯TO†j‘Í["߀œ$Ä9h?òÆÿ€Jr÷&™9|6åÐ3-<Å:>ó£™ßá,ûOfc5¯È†Ù!)lÙØ›õœUVó§ÎÚm•*>-;Z”?ÜÌi\xói­Á¼ÌŸ{<|$hU«ñ¿'Ó\ûzã'°ƒ7uéÜ,#·£tð½£l!&K¤r ç ¦4ü)ÝîĹÁ:ÄvƒA…Mð#¥p~òs1å1kÈ‹étÝñE¶œˆDtZ‘~ [h—oaE‘¥ÁüØ1éðh7±BÞº´8Èš ‡Aq`´~ÃÝi:…ëå° ¼áïÇ;Æ›`„å Ä ¨ÚÌžŽLöü~‚[!qtËJãüŠÑÔ†9# ï@–݉ß—kP/¡À¸ai wÙXÎ;1*ªi] å|pjb #Ú–,¿Æl ™íÏd˜¦¤›üp TÞž[ý¤G|ë©¿€¯«#¨8ç ÍQa %Xk ²F”¡‹¯=u—˜SóýÎüuçžÙ‹+ö ´—_h/_y—48X'Îäø¿ 5ÓÑÄ“÷©ï(ì¥(Մ넌N@ ÖYœWe}O‡¦xe&‰L[ÉT•¨Ìø”>ß\‘ ’?w›)ì¶>Ä¢| åw5€“çƒiÙA—ù2…9ŽÉ¾ÇB,OrdÏd¿ïÈuníŒé‚*¡CeZ’¾ë“øŒM‚ßÀ?’idC¦‘ù®¦!É4²/¦ñk›F/FÅ ¨Tå’¾úÊ.±=u¥OVՊ',}#N<>È`×6wÙk _Ë-—Uß ðÞž÷¸ ›ôs**žŸÆÏ»U‰[$G?ÁrÙéʇÓ³óø.8O±›sYGXo¦êñº³öÖ™™w/ªÏÕüâW(<5ãœ\4ÞsvŽÀPR÷4X%¨vÓs—ÇÑrýÒz@ì»ÄîU?wpéûRljzÍóOOýä©íñ­¬ì}ÅÍ£Þm÷ìâ$m…~¼Õ}Ôä··gr¥Ô)¥õ=‰< ùø—F£°L)«Š÷æ.Uø¨×¤ÜRh•ð#„s»ÌÅ·Àya¶•_E\Íú}†ìùÆ!©TÌaC~€sù×6©ž«DÕþ·0°ÕÛû*ú±×_YÈ<í± ¯Ü4æÛ> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R ] /Count 14 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 83 0 obj <>stream xœyXçÚö ìÌÄ.ëRDgˆbKì+(‚¢ ° (** B@¤—e—úR·Q„Å‚±¢†Ø‰]5KŽ% c<~'šœä™|/翾wvÝ“ëËÿÿ——,óî;oyžû¾Ÿ‚ eײ±±açÄ$ÄEFÄI¿]lÄÝÄA¶™8Pÿ-DyMØÞ«¨§ êi‹zÚí8¤È^Líwû@r_ÊÖÆ&©¸zNLlr\äæ-;\‡/Yî1bÄHËÈXOOO×õÉ߸úDÄGnÞîúùegDTLltÄöÓ\çÙQQ‘\7G%Çn‰w ߸1b£ôZHxTÄ6WßÈ¨ÈØØ˜®Ãæx¸Ž3fì(òc\`dôú„x×€˜í1® ]—DlNˆ ûAŠ¢‚z'oß°"`vJÌÆ•sb#V-òùdSÐܸ͋}ã·,ñÛ¹t^²ù;·û'F…‡,HŠ^¿|˜Çב£¦†P¡T5—šJ¦Ü¨Å”/5C ¥–P~ÔXê#j)5G¹S˨ùÔxjLùS(*„šH §–S ©IÔj@ͦ&S#©•T 5‡šB}@u§zR6T/ª7eKõ¡ì¨¾T?Êž’SaOõ§Ô‡”5r¤œ¨™”3C  ¼(j µšDm¡ÖPB¼L^\LÝ´™is¹ÛÔn?Ø®±}hÇÛ­µk•-”£ÝéZúWf6c`~cw°¯>˜ûÁµîºŸíÙãÛž»z¹ôªèíÖ;«÷ó>e}û&õý²Ÿ}¿íýÀ~¥ý1¹B¾Lþmÿñý¿Plv`âž;wÜéxÎñ Çïœlœ&;…83ÎëœÛ hv±u©pùy`ÞÀÿ4rPã r<§åÞ“÷çsù+®Ã]]+\O¹þ&¾í-¾E‚˜ ØÀÔf8Ôd E£â!]«ÕTV©4É<¦“³Õé:u-ÿ¨=a–ô”‘.=‰~°†®ÉÑg¤«•)ž€1¦ÅÀ]€Ó‚ÖÃ4Ød+zAâ2v—Y½ ch£VSU©Ò$ñ›h¼R4Êð::YEöÑJ_÷¤±_{‚,ŠNV’7ôÒкVGÞÈ&GšN_w” œI§eÕ4Ù6‹5‚M ÌO˜b Ç|°› ËɶªŒ Š,’N¬Ti“ytb¬Ê²,¶¥S̬á/Ñx"~§À ÙV3:IEfhÕFÞ´%¹l›+؃½õÍŽòÇ0Mœ¨ð’n"ͪåp[d^Õȃ£åÚaPNô†]V~j劉ô.Ÿ!£zoÒþ•q(šÅ(’±Ü¾]c>D  úÉÜ«ã6'.\Ì‘C¤ P‘S¼åâV±§"üXH] bñ€#±îÿÓhp¼}»îX ÿ©qW-ªduJ]¶ª 0[Å­]?‡Ìì6ëá¿ÀöQ+t{~?<¤”—ßM).WW¹ThtF^ÞøŠ‘ß­Uë29ycš:;™ïD}àÙ0Æ“O_ðt”¿†' (À2ˆWäjôšÊjeyŠdÝ.¢ñ(¼Õ²“’u+«LÖí&YWB€d]ðÃ8#Y -mŽ£ÅăSa+xâ­²ϳ„]ÉÆÉ| Ý…îû‚؇€û[˜¡€±ð›Lמàe…G˲‘bB½åPÃh<¿Ãcál˜„Æô Éõí ‘(HoëMo'ñÞb‚–†ñø7™Ä9„[}%J ¶äE°ª¯Ñ¯Zï¾øzÝÙɇùk‡÷7¢óìs﯆sØ`Ùì"-àDÀæÁå°Åk'ñØ×+ <˜¨A}$©qçžÍh3°t¥gÚx=ô%±—6>N`‘$Æ`â_GÒIšìÊ*âTî"”2ø"~-Û,vËç¦gÏròSK ‘á ¼–™°%Ú˜Œ×œý‚8G퇺ã>¸ßûa@PÿþŸÐú»ÿîϯ·û׳©C†Mžêæ6ùéûw/žþ›Þà½vu»ëìÏ4ƒØ´z;Ê3Ädñb”ű’^uÉÑYŠ=†“xy îóö#èñèÚ±oÎpòôqo˜.É3–Ñ8Þ+Þ=™êFfºO›êá>ƒìúäé?¹.— 0çvÓ ð±…‡ Q€ì–5Ð5zmE¥RKðØËbá&Éx$Ê>“*)_’iF'bh˜‡wËp䇌4¥Ð ¶ íî ¶bƒhl`,àöd@ZÌÈ·¶o ñU±Hö!b>B QB”5k ]ˆÚ@ÃÕö¢wh›¯Èòqb©bõ©Eûæ£yhÑ'a¡ákbæ¢@v ƒ] îoÞØwæ·_E Ò³†l­R•[ ÊႬøÄ—¹»û{èÅßc Çßß‚í˧!%\Qj¹ª±U=¡‘S£îv³¶ÂÇ<‘ÄŽ\P$z nR#¥+H`–®PLžˆÖêUd`b¢µÙDk= „>3ÿRôcÄBߟ~„¼ü)ôõ ÷õZëÂï%ÅûGÞ£§yy ›þðÍ›o[ÿiFÌ`¸`#î‚P:]P—vdËói-#Èú»Æ½±Ãïnàò zžmÐgë³sò rÕü†m³¦Ð´;鿱‚ñõ½+oÑ[Ô´k ÛAޏMÈÑ­®4KäÈu¦V¥KOÏ1Qˆ¿.e੸Nv·ãñL’6»ªBKXÝáH>ƒŸ¶¯“É“‚˜.Ÿ´âE(0Q2ìˆ=±'OàaØ#L/LH¨ì˜ÆnxÐHÌ`¦G®àú#ù`yKD&äýÁ¬Ö؈¡ÁáYr0‰ÁvàpŒ5V…ö…˜C˜‹¨1íØaý¾E(„Í ?l䟽yC€?ˆA)ߊk¡M!z+4øðLÜgÄÜ÷øÇhèýäò¡Û§y\2ŒI1‡R“{åoŒf'ñò·!bÍÓHjGÑò§Ø/–0ÔäÖaî3ˆ[Ÿ´þÌwFMØD®VJ‚„ZEØÐôùSÇnïò ösgs*FlÙF—£Í(æ£Ê}5ˆ» d68é ¬?à6+=þ¡yG˜ÅAqz‰%f“>#ûzBª¦Ó@·µ¾þiò½Á<>h%¸í ÖNwÝ s4¤ÚA 0On/õó³dߨEßIJo\·æß…Ä&Gùy² Qi·0:Ù ŸZ%˜e¦«̰D0ò8íA§h”•ï8àÛ½™¿8‰<] † tmŽ.#Í„ÓóÄA_3"»Ï!ļ Q’Ö³3MÌ|ˆÛF˜žÌ̼CÄEk¨Ê’TÐ>¡åï Û©½§\ö º¬};4ê’‚rÉêú bõ”*þ“Ý úíh Šùx û'ø|%À߉¦÷’à?øÜ^½üðll7jf1óÓ°»áðí3ú"zm'œZ¬QU¸TjôÙ†où`’ƒdprCf6ÙÁl±ŽÌÁd³z‰¶$ki¹ôÅvã3í%”ƒ“% -m¨¼tóÓšËȹ í!yu}g^ÆX§“]V #AêÙ¼«£Ãw®0gD¢ ¥#{3¹«TA6y´åä²6<‘½b‘­hÚ>Œ9¾ðäGΜE·Øï¦Ü#9\Ñ_JÊLË«^4”ÚA Œbîß ^ä?u…GGä[(À )Ö‹=šlIü‹PTKg­®0y8D’ûÌ4Óx<œ^öÉfÕrÝÆ?‡ü];pëdӉƓ5WQº¿¾l›RN@V®¯å^1Æ WO:Ê‹‹Å*…Ü „‡Õ¹ žî7!¼vcÃþPÂñÌo2ïeÔåÔ§H©Ù†¶°Þsƒ?&˜ƒ¼[òoî"1±„œD7e$%PUT‘í¸ƒŒÜ*\‹¿ë=¿¿û¶eÇɈ}|”1F;³2@¯‰­‰¯I;„±·nœöäFØ¢RöËÕ•.•åÚZþFª¡MÖü³LÈ‹ó‰FàÔ`+Ì~h±Á1aŸ„ )Ç$¨èaI.â\¬¾ínÉ@÷µ'„щªN+ó„@*Ý™··™ÃÙ§Íáïå;Ä,U¨<)RìIbŒ•ó«hù©EKEÏsY±A8ÇÃ¥‘–ö"ùvú…å»}ýÀÅËœUUÓšÁ“xþ¡ e,ž³™T]VEU91+Ðí«1Í$›ë•ZîLkOü¿§ß–”ÉŒ"£Â|§Ž›ß·äÙDC0oIZá¾E4ȸ´ÐnÁ愃ÖÛÂK˜*ö”5YÉë ïФk“%ÒØÚd·,å½õ$†éí=eh{©çfR¢-ă ÐQÞs`žâô RôΕÅÒòÿ¶œÏÑ|>%¹e7À:R¾:ÊÎÐòvË^–½öÐØ»ËÄ#-–ºÐn ±Ìxcö¯˜#ؼ3—*yÄ^ ¦ŽOµº©•ÿ gøL]¹zÏé80Fµ63M­Jâ°s~Kü-ÄþòÝ3SAÐéOØž¶b€ÃsâšZ+×t³Üó<틌­l=kçÀÙÿûD#ý‚x§…~ †WØ`v“Xh ;f4½“4ÉÒ`èÞnü¿hR'y|pì À¯™³ëq²Ã?¬ø“¶ êè{~_|¼É?=y—•›“ƒ²X¥6ÛP\*¥$OöÐGìƒó[Wñ>LPuTù¢ïý&Màåçf}µüõ½kû¯ÜàNÙÍ["up|ÃZnÞûü÷“gs”ÇͶ Ì]8oXg uWhˆ²Ã¦ ·Zeöt¢ºC0žÐ8¸=A–Oã@1AöØÔ©Î.O6!Yi‚C-˜þâd#xKQñÅÓo¢N.z€×:ÿ`!åRÚ –mjÞq°Êi—„ãÊ* Ç’uºÿâb?´‚ÖÌÿ¿ÀÙI(ûX„§ƒôÉx’ħ—ð ¶+`2Œ“ihù㮆¶±ÖÂx8ÈN›x[e2‰­Å³š¬7/$œÌ¦å/-õ…³å~4áãp‡Ëü­TÅÉ2#ÛºQx¿ f6mx6䇔©Û‘€çm©\p[W_©ÆÔõ3tp?’ E.\9q¤þ˜KÃáÈ <¾ÛjÕ9#}âÚ¡½2Çoàååø¾õ„Ï"á}mõ¹Åë·î\¹‘K8»mï:޶'G†²îÓ@€ÔÅ-0ÅœouuihyF»Ñ*½yIË[|Î\¼ï½Þ¾ƒ~Ðwä[ÜÇ{Ù6Ÿ•<ø~©øùñÔ!Ó<‡›þìÝ»oŸvXü°î…`S ¡à¡¶ÐÇá ¬è¶ínDC¿$ ­³j µ å{zI{,¶Êˆà´\*ÆÊž[ ½›%ÖIf‡=7lűäJ¤V§ÄúànñȹrÛíÐ)uˆ­Ôé ¥…Åeü\èýýŽ~?ôë7л¸¬¨•²†l]gZëK["ke)³³òPAI.ÿ îwÐ]ê4¸FñŽósQÊc³tÙ½Fói wl>ƒîègô˶ùÝŽJIæ&1¾C\¤:t¦EâñL˜Oך/Éžg2]½d˜‰ý¥B±ªR*{›Z÷„‚n \R˜_˜rÓ*3wUjJë  …YÇNbÙ¥ íC- ħDç>"×jÀå%q:e-r®E:}I}”;}á`Õ+k¬GÉ[i»mà,| Ø±ùPôg¼ž>qøÀ‰Q"¤Eaq3ô#Wü–h?¢ÂJ&Ó» ®¨FmHGÎ(G]˜’Šw:}DW“QM‘Ô[Ò¸ºcÜËrÖût6î'›léæ?£SpœLÅdê³ •]M· d?Ó©¦ÑB²’8W««á¥ñ'–v”½ ú)¤Õ>¢µÐO2“¸ó^Šmq *cõJ­2[ŸžÃá¿ý{ažº0å9+õÙz}YY¥F2|Z“ ÈL5˜ ¿*`ü, °-¶…)<žs'ùó#L-Ø> Æqøû7 t³öÎÁÓûŽÖ6£fÔ¼'òУZÀŽcЂ$¿è5 ‘‘‰á„‘[ëâ¶ŸN¾ƒnš{U6p”äÝEQƒRórÕHI£Ô–hKP)NͲöJÌ[ý©à¾U7…3kÏú_íÅaS“£¼ E såìWWÛ~`åëˆdöª;x}Àˉφ, ÝÄ%ÅgD£d6[§ÔÊ5U¥\õõ›'ï öá׋|Ã’bFæqŽ—1ò qÐ#ÚhnF$qò²-R'TNR È±Ûò“ Ô¹(%„¬¾xòô5¼Ô¸¯þxqu±Nƒ*QUvyZÑæÒøztœ%ß][{:8-[•™®É3æsGòö~‚"YLÞYoH<·5>k'Z/YE¬'•Xû$HË+Î-ÉGÎ9('/?§ ß© ¿0©Ër´¹ â•NXÄ«Ty¹¹HåŒò‹ò‹É?§âM~9"ž.*-aMÞ„&Á^ô’LÍ OEE%EE¤@þîúþ-Wü0ëk÷å+S£7p‰©éÉHÉ´%%:îPÃ9ãeÄ>¾º`]ÌÆðd~§2µpbS²:`+¿ûæùüé³fͽliÝñÕ¼º,¯$±Êl¥2£"½!…o‰=›qŽTÎÌOÏ~¾³úÆŒ#eQ¾—’ÑÈf(pÿ1VÙï|ËŸ9VAÿs–äj•ÈM¦qR»“l5ûN£‰Jü&Û'a!@€éü~ üC²ôl¢ $1ÑVÔšÌb3¯üîŽÁ2OéQžÄ ŸÚÀã!+>¿ŽØÖGGK Q¢¬,>!uCÆ:´m1Æžˆ>•r]gaN7_¿ºsn¥/Ê -òD“YŸ«ÁOôú¢â:®Ù.#?£0 ±‹7;ýÊçOx³ž5ÁŒÝˆˆ‘[3ì& ¯+ ‡®3ß2‘ÇÇIy™A´µ¬¬¤¼‚kÞ}ÒЄØWÆ ž7Ûcq𾓡|Ž&·˜7+;[™Q•vd'~ëùÔ Ä¸=¾û;؃×Ó¡k7©v®0µú  0mh&ØuÅ«üpп£`åy$m>d‹`ó_ŸI)-¸B®¢ÀXX…ôèVÍ'Ï=|rú12"c.W›[ZX†XƒFg0nÙµÎß?nÍrÞká–‰³,žó÷†îo}ÔûY/ÆøoðÜÀ•+êOj«w9Þ´›ñ{WæOŸ¸dž·ßŠ3Ïcù<*ÊÙk‚~“)1•0±^lVà íú¼Â”ïl ;eeÕeLkd·ÚÙYL¢FEŠ'mw N2w¦»2Û#SÃ-¸?¢ˆ`t=¿í¸r©eB”TFy)ö€Û ùãû‡šo x9õшSg <þÍ&NþrväÆåó 1ý—_^|ûööÆ ³r^°‹Ìt}™àr/¯À/nsãÒC7ÚÉ_>¿¹ÄË{^à4Ï7¶Þ¼òÌ<ªL!«ê/C9ÔV¢‹Ýšáì [1ÚEÉÅ©HE„4 fm¨×ôÐY( %—*ËTeyE¹ÙTÉÇS?¿u«þô¾õ«cß#ø€…ÙS 7î>|æ$L }<ùÕý MO.ç’"~mVrZê¶ÍvnBìÜ%·¿ýþÊ­Ö;-k'ïçK³QaùS´ßyQôRüÛnåv&SL§¿Ž‹J¸@Æ·oÚth»yü`ccÔAs¼Û™udš³ŽÒάC«/íÌ/àh“Ty=PÕºŒt)¾ü{"“\žMr]­)› ¼o¶p™ÀD[U%U*íA$SÌIOתŒ¼„‡X(È’c÷N¬'Ö‘"¼®Žþ¼»Ðãóž=…ž½(êûÏqç endstream endobj 84 0 obj 6724 endobj 85 0 obj <>stream xœmVy\Gîaè¦EelœA™ˆr© Q h8ƒBäP!\"È(ÈpFŽ„QÁ„/Ñp*ŠrˆD$@ШEE%*Q×ËnbÂnˆ¯'…›­Üã÷Ûfªºê½ª÷½ï}¯D”¾%‰L=Uê•&fcÄ<—u”O¤:æã­*ae.?MÄO×ãgˆSpüïKtéô Ê»äç h¼ׯœ>áwžš Ma÷$J,¥í/sMHLOŠÙ­QZ­ó´ž3gî¾Ø;88(#Óß®(ÝTÉ1›ã•d¢R'$Æ©â5ï)]Énµ:f£r³:=1:Y¥ŠÌ"ÔªX¥GŒ:&11!Eiåj­\0¾ý<ò³`uL\äÖd嚈ød¥·RC¹RA¼üÏEQ–Îñ?LˆrMTù¸ù&möHÖĬñ\»2%v]„WZœ•µÒÖÎ~ÁÂE‹)j&åC¹Q¶”/åNÍ¢ü(j>5›²§ÖPž”%µ–ZG­¢QÖÔbʆ ¤¼©w)j.D­¦ÆQ†”ˆšHM¢L( 5…â(SÊŠ@MéSNT.¥-ˆ†ôÂõ.ˆĵâ_õWëgÓ2Ú›~ÎØ2AÌoó >2øØ Ðà[v:ûdœdœ×¸Ëã°á{†Ë ׯ4|m”gtsü28i¬Û„*À&ó+ËE|-Xr¥Ø’†yŒ.ŽÆÎÌðœ7qÂô ¯E0™Ã~ ØBm̪೉Q2؈Áƒoä°Áp# Jæ&t•ô·_º…d_U'†lËÑfoUxâo…¥>øçÞ²üRT*{ìwyóºD¯uòÒ~â¬]ú0bQ˜‹ùà%·çX^!:ˆZ>iVŸT5…•x¢yÈ96ÐÛ×=CsQPip]tCdgê}zõ°ÌXlƨݶ$®C¬Oüéžöâ«Ç¾RÔ\m¬îD§PÕö│ŒüíyQ¬±î>ªÐ¹‘»ï2…Jþ,þƒ†™ Ø A'0øÀpW*ßEã8Û ñ“%)ÿDz‹„ýö¦ý`eÂmíùóÜ,dîëâ­¶D‹YlüÌ ¤@ý©åÉ5yÛº_Ðs&-yˆgÈ·b}îÚ™0·`m@r„B½&LH.Õz$‡~PŒº_PÁ/"‡,*7yæS%i¼µ)ÁnôvJf->¤éˆ¨úÉ–!‡°À÷ƒÝâí‘+iÅ&·~½Õ^Õ~^¾‰7á:N‡»cgL½Óz&<½ï$ߘÂïƒ)¢×`ÍO‡ÙbÝ >…ÛS“W„ŠÈ-g>Ãvx¢2Àb™Üma8ž†0ÇâE¿Zà Xfƒ°âœ›3ñvnskÔ1Ô_ý¢õŽ¢íþõúǨM«ˆ)NÝ›†Ê<$“(þúñå&|–I#Üx¢dðâ7¹¾ú Ÿ–„;H‹ b¦ô€Äóñ;žkâ|Ãä Ö8Ö{` ‰cq@tëm0¹zÏcŒ™1†$Ý$»„TÜîPëþÔ„ÎҖа¹Õï˜ rBï©Cƒ½<Ãq,Y<éù˜ »AÜ)Çr&Už‚XUcg!*ØS£€¬'2…u¥Å%ÇêšËšQ=ªý¤8á@FþKøSöÄ߈Ž`-æëuéž„,B¬œìB± Â,v1Lù±õïÏåß½lc†,x*Ã^ò4<…û囕XÏ1qáE»wôrÄZxßÑw5/Î]S\¼ýmÃCÄ 5J 4&‹tä¬>Shb`6zz¦wð0qħ«Ó¼RŽ›˜ÊáAnO¦aó—ÖðùŽÏŠñUlx?8a‘àÊŽ”ûN~ñÆÿxƒÓPÌó]9ÖÞÕÑUÿ õ¡Ëš¶È“›k"‹<‹¯U ÓÏsÜyH Á–éhˆ ß•‘¢HŽŽù4€¸æe£‚ð?Zp šKú/íG²#è`néî0Sº3ïs”¼WD¨ËÑߤ„'8þ‡¡‘ª¨R ¶Yúørâ(})-ùóõÄȾÓV ÷U+ýÜ6a=dÃb½ûï ^o.»Ú-ÿÔE»Êw½ ’Iú‚7UÖ*òÑx“³=wÚ‰TÕª³© éçv^G° èÞq˜F|§Tòg|b÷uCˆ36^a¾ 0ö˘øè~ň¶Áy‚’A ÎÁ-î1Ça2{™zp áŸ#³áÂ#ó3 XÃ4°RÂŒ©’þ ¯Çí-%ZVÄþ°¢›8z¦¨6È7¨üQZs6êEü£$©dðFjq–Æ,vƒfu´ºàpš\{hWQîQÏ`vgäîBYh}m蹤3š¶Ì눅å@õÔo¢Î,­RÌ©ùèˆö *—ÕÕW´Ë%§Rcwåfçd)Œá9ï9 âðÕœsf ò¡Æ³i&Õ–îaÜñ³ÝÚ-Ú&[Û¬î=ulOA¼ü•6g'N¦É8xd_þ¡½%ŠëÐFÇÃÉ‘ü‚Iñƒ±2˲ MÂ<O¦„ecõaò¬\Y`)°˜*éÕEð{¹ý5ù‡Q9 ”m7^h³"9È_îý>rCK/†ƒQò«T©dàezmj¨™§g¸ùüÀS]ûò ó åØ‚Ùš›‰´( <¬aËWá=)?"RTõWnµ´ïEߢ¾µ—g—cQ¥TÒ;¯,ñ‹&³î¯›_>k‹_÷ynVn–|´áÝ ¹ÝOôÿ><åÜñS!ƒ£ò:‡ñÃÕ4>Ë\ògóïüÞ§c-ï±P8Eü Ñ£$x2vƒ¹áÁ.H´}›ÿ&ÉX‰üÃ:øF[1ø£aE¯†÷ ˜o·Œ üzÕQs%Óî·¯?Â;iXJ΂?ADÒx!ƒ“ñ&zL؉™Ìë,‰íÌá¬4û ó‘Ìi}]û‘üÃ{¾P ñYB\ÃñŸmÉÞ‚¶Ê0û£3ÌyÑ_ýÕ5yWkÝץߓ-ÒQ>6"“‹D^³a¦Ð‰Ž•p%»ŠÑ´?ï@~Á h—î-Í?‚¡ÚôzMYluð!w„íÑ2ÿlÊÂT¦¤ódy7b/Un‹KÑnMÍT¤î@È5Ó/K²s“V½5@µe5òAªRUm2éa)U§3¿œv]8úMÇî ô9úŒ•¤¡Ìü]ûv°ä‚'B'iuH û`ZHRÉ©¶ÆS7‹ ¾Øs0ÿ@ÞÞD«Ph’朗'RRݺ rÑS0ÃïSüq¼áõȦS—ð8ö&fi¢¿ 5‚ôLoènè¼û;fK4?¡\×ÍŽ» OAzžô£WI¿®… ¬%Ó†-» [„¥›°tž¾Ù+6d¹9±?9Âr䀅@q5¶£¾¶BçJ<ëÖsøÍ5}‹7:õ±£^“µ¿ñZÎ|X;¢stA´-³q‰ÃlŒRmâ ÍîSÒÕùt˜I«ðèlgªk+ÁèÁ÷—këšZ.½†nýXÞM±ÝìyØñc9˜1Å?µ¶÷ ¶£..(,5$&F¡Þ²Eƒ7¬Xüa4–²|ʨRCü_!Œˆõ-Bíh0·«©’!h„;ÜT˜{t+á2+â#ÍÜÖúxúÇÖ\È•Wä¤ñt&[““ÒHÿyযº~IÞ|±ê.êFš3Á'XÉÐã Í7™ýèÒ‰%ïº'…É7mLßáÄÇžÙ_v°¤¬îyS°­uñ‘޾öŠí£ÄƒéÀò‹ùú šsfxO;0xhØ# Ò¤éÇ`{´Ó`O÷2ØX{3]ôXT ?Dôé*¼x:NÄ Kl1¬ Á„©<ÝXÖ‚Ø+u[ü,|V, ˆ®:³/¯ ¯PA^AÙ©Dõ·±VWÁøÁç_‘·tëE=,èyö`Q¨j»:TÞV4\`Œ5GùFA£BŽ2@Âl# OQÿϘË endstream endobj 86 0 obj 3123 endobj 87 0 obj <>stream xœ]W XײAÀÀØ" 3ã .¨,â5"«,î¢ ²/£à  2¸5¢FQ¸¢l‚ D…°ˆBb”ˆ˜D%‰šç31jb$¸Ts‹ä¾Óƒ&÷¾ï›¥ûôYªþúë¯j c ÇH$’¡~qIéqiê˜(GOMR¬84Q°–#õ„Qú5ýýZé(&ðèb¬OŒ NŒ43lÌ n(d¾ÇèK${òçjR´ëÕ ‰iªñK…ØO˜0ñï‘)nnnªhí»'*¯¸TuÂZ•-½HKÒ¤$Ç­M›©šKg'%©cT IÚ”ÄTUTll\¬¸lYTRÜ•:I’¢IWŸk¯š:yòGú35X½!Uµ8jmª*P%Úÿ_# øÎÑ® ÒÄ®ž›7ßk÷ú„…>©‰‹|ÓÔ‹ý6,ñO_³4)*#9z¼½j¢£Óä)S§MŸáâêÆ0ŽÌ&”™Ïx1NÌÆ›qg&1㘅Œ3™±a¦0¶ÌbÆ™ÊØ1KÆ™Ï,eæ1Ó{f3q`B˜@fÄx2+˜`f.cÈ1Æ„1e†2fŒ9#g†1<3œ±`F0–ŒãFãÁÐCØ2æ'‰‹$UÒ¬g­¨·Qï¶þTý|Æ`¾ÁƒVƒ?¥›¤}ìHöûF–%{Í9qK¸ëÜ¿ = # 3 ~mdi4ÕèK£?‡D iòÆx¢ñ$ãã5Æ[ÿmbk’m*3õ4-:rèü¡Þ[•¦0îK¾}ÈìOæÑáda˜áï¬i<)s:Á¿X"|v|ÚIÁ‰ÅýýÉRtg1ÿd)8²Í@‡7ƒ‹ØgP#5ZPÓ×ÿ]¥ªy4…C„QRtb!|àštl·ly\lhÖöÜýZÅYÉîü=GÉYRàø¡O –•Ý€õýò¢u%‚# Y[lÞ*ˆ•…¼±î8PMo{dòžõ]m§W(ð!ùSCü»Pæš2?F!oüVF-I)üRÉPé Eð˜Ï†Îß¡5ápºãh DŸ'caX<¾|­X­·&e)á¼"o¼ºt¨éh•²ðtea-ù‚œH)ôâLûo“’~¯bÉMpœDÔ’„,:¤àÀ/tH:À¡?Óe8l G=RLû#™>¥½³æ,5÷µZ… <ºÙ¢½Ñ§p×>Àÿg)sQÊ÷|æ3Ú6ÄûýÙËoõõ]ºuW©ÛÈбD˜†0­ØüÝ-KDg³`?Tôp泉­+Ëý¨›¼=¾‡.ž¥sÎ…(åÍ!Õ7Õ7­;ÉÅò³- &üW}ÆÙ‡ùz{‡uÿö¢éÖ-¥iÿ¤A¡”:(¸@›„)z°¨Ä;hw¤8“…t¬ d:¦ˆ ºÀ.ˆk×aà0°ƒÅza ½ˆ€+’* Ì…0LržNìÿ‡ÎïlßZ›T·øö”:”P{£qÚan9ÚѨûƒÉ+˜™§D[6ÇiÁ2':…ó} Î0ý«_Àèrƒ6ñ¤²pãÁŒ¼XŽ“J!ù )g®¾C¥BEªþØCGβgæ7iºÃSp½»ánÂgJyÏ¥¿3ó¬}IØZõ|Œ4ƒ°„úøx‡uõö6uSX(™ÞÀ˜'0ôD(t>7Ÿì'Hëö϶Ö$<ŸvMgý¤‰h€þ8çéhp“çݯ+•¨`3#c×/'1dÍѧ·ï<þá%î£'|Þý³u×)ñ«2‹5?NßOùU”ÄýŸZ ˜­¦U¿–·!.ê ¿yÞñ6„ƒ’êq>í¾÷W¿$/ÉýOÚ»Ú;?}Mú°ñ¿Ï»Ðí^5†p¹8Œ‡¡ßÚáT´w·Ec4™ÙKrü¾L”bÌhj§‚™¤‡Fí zPÏp¨aAŸÜªh¾vµ«ò7RI¿-ì½æSŽÔ;úÜa —§Ë¬e ½âÓqÚb”r¶ƒhèŽw鎇ÄäH‡æ³`õðŒ[×ïq"e.Þ€ÎUɾ¯N|N|âLÝtýBúu£{|'®×B'¿‹rn‚ ˜ §òTOÿ•4›,© ¬ryú È•tŸÏéø12ü­ËžvöËý<ýÃ;Ÿÿ~á«.åĸámŒûûøæ¶Êä9y×ÐØåöÉX2–¸­ô ð‰7ôšq7hЬî#wÈ•#WÏrÝïÞ“â1ØÇÏvïñLÛ¶zÇb‰Æ‡L¿³ÃIó³–nˆPêô”¦ç ¼·ExÓDx¿¦ð~MÇþ¤°æ½**›zð ôÌ›(0óÀÍD„{…=þC`»Ñ†²aÔ ÔO8œT”®,Ê8šs9f†Œ?9›•ŸµÁjuìÆy käk›pøƒSt:»þ7\©ù_ÖVÕVÿŒ´[‰^%è}iDl~öARÊ®*nVÈ{;É'©¹¡Ü_U€ ¤“Z³ŒÊf'­TÔR±\o<ß~úxÖ†BE¾vß’Èá]) Ú¦øÊ¤ŠUGV²B»€ƒâA¥‚ŠÁ"03§U a"{ ¹™PÏÃûlÇÉuÁì=[•{2ˆ–dß}Š9˜~‚M+È9BªvÁš]‡œjïWÕ]#?’«[Ï­>^D¸ëAjO5 ûûËȱ]Ç6sòWÇ6ç­W[‘ÄlMææM[6îŒ$o]U¹Ô˜u0̼,äýaÂH)Þîì®èìõÖqòƈ„%ëZ£É´×Ôà)=Ï@~ùü浟(K´‡3 â8ÍjÁMŸ¡ J<ŽÅI¸×Á8œ+žß<{ÿŠRžql‚ô'Ö ~O$+O? lh©°ñqêÆÿag4Fý\Y¾÷`¹â¹lˇÛ÷d.>çp“.ÒB •4 »ŠoPætŠ«ctŠ*fÛŸl@7¡¡â>) Œ©6Ka,´ÂX}ᥰ'›Ï'Ô†¶ºŸ¢ª„smÝp J¿Xã5Êß7¾È¨Ö–Á‹ã'Û,jøv§‚ê|N~3‚i:-ýGT9d ÛкB‰l©ª$©„´X~Þ|¶çYKÌœýŠÁ¶S?,@õ¢fîƒxüAW„&`ÖA…ØÙЧ?ÈtÕsÈÑ]ÐõBÍp¡šúñX&o Xå;?¹ªC/¨Æ2rÓdß4Ÿh«UÈ3dƒœüžbÛÄÃx¡—5MŸ{†u½b ‰€‰%±]Ì3õÛÎj0·tHJ o8 ÕRÏ¢d@‰†‚R¼†…ÕÒw“`V±äšw<à…±´„‰¤÷–]ùxÌ‘‚+‹+àG„H):ÖÖaBo±N+EËréÁ92çUQŽŽ«ê)„Z“sdÄ­tasDKXϺGäé©hij>_z•Üç„Ù_–ÆY´\wì *H§”Î 5?I혣DàJŽòëËv!'9h‘ÞûWÐmg¡YެÙWÊŽu®õ„6A›³ióveF6!>Y‹³G¨´!Kˆ /ˆ+O¡$_Sy1£Éº|^Ñ|‰“gÍÞì‚MLdq$óòF÷˜p¯•kËkëK‹¯æ+šòª|üQáAË¿ñ(çÁ JÄ>eº8ƒ‹x^X‚~X.ögðxŒâ•é¿Lþê-Û¨+ï~-äÍmÿÑ_Þ»y®¶íâ©”HèFþë^0—½ ¸ŽŒoXêâ(źKKOù‘ “¾„“7wÊþ“ví¯&íÚ[õ¢¢yÙ™‹ë»¬CÁå› ·.+ã[üÏø?¶^½”ƒ|ág|w³·}¨·×Ê®¾çºnö¯ ^Gˆ’áœ0Tt$èÔ„  ÷Ü v4D«Á€€Û3À¾†HîÝ*S\Ƀ æ4‚€#§0P ½çìØ×}†Öz´#h«Fv4Š«*égc±äØòhûp·Œø¦$.ØÆ-–Õí?yøDImå?«É·Œ¢ìy1`-KðŒÇr´†r].€­V‹«Ðö]…1ÓVXl^cEªUô'ò³É¼5‘ ¢—dL&8Š ÙÑ©Ÿ,­÷ëŽ{@zH{åù¶ÚÖ⟠Œ 0|Ó#õçQ]>Õ®TQ¤çÈ©Gãÿ™|pe”Çö™ÙšMQÉi1$žhŠ2Ïm9½ýyJ¼wèTaÃéãµT~u´I|ò†‚‘#žž#lá¶Ðæy…“lƒ‹g$šÎI|»$-°´³‚pñ%"O|ÓiaÕ”¿¼ C:×J~¡®;>B{´›> > Ûs³`UÆ~W}áÆõšH¯Yë“p2ê+ÐØ->pZpBú[ å)¬¤ù¯A…†º" Ép‹o&y{ŽÀÉŸ\Üz::Ä*(bU°BÍ7»8’ÍÅ1O¦-À0á×Añ ¡uæ§JyïͺV`ä|G¹ûjBbšÄ­a$ÛÃe¹5{‹+¨¬*ª#Ü•*õ¯èWå¶A9‚‘À ´¼Â—ƒ/IüÁÝè Þ€ §Ð M¹-ÃÕÀñÄB\‘¾uà8è¿¢JH»3ºvKÿ ~`(8 ”‚9{älUQá¾jX>Ý5b¹w°ºò‹Ýb7ü¾÷Ô…2f÷Ë—´U±™ò-V&nKŠV–Áx)\xNèÝ+©§$©$%Õu¡‘ßq!§L[iÞég276Ò+Ê'Óƒ OÐôÈìs‹êüoÇþ@7ýËkp‚‘.OÑtalfx„rïB>>]p£¶æ i$ǵÇVqö¸˜ï¨_>kvø’ €ˆ–;ßžoíP’£Môª\th•õ×ldø ä5A¡ãÖ;eà±ê@Ú7T6< ‚{kð–gL£YJƒÉÑŒNúOƒÚ'8 Ãùã‡>ÄÉ=uâhÃ+Пp-pÄw4ð=yC­÷¹E¨—ºX¡UŸØÃø¾'`õƒºÕí¼­`/ÿõzCÜ’¥±ÁAŸ~ÞÚPÓ®üÏðò¾î†ð9¾«|}#o}}þB§èA*5gpPD¹{,!‘Vãpã÷¢äç™`J“îšú¯«ÛKßзÒ—yM˪»Þµ3h>q°Áñ4ÏíÀüÜ™C…¥Ê‚¼ƒyeZÇùy„ªOÔïVü$,ä»ê—¹ÎŠ ˆ¹tï~íåëJÓ´2¡Z,Zae,Áè!`”gl £›0Ìÿ—#,Û endstream endobj 88 0 obj 4044 endobj 89 0 obj <>stream xœuW XW¶®¶©EdQ: Ú`”EDP‘]AÁ}-`ܲÈÕ8ɸcQITDQƒ ¸Ä-* hh°QiHZ%ÑDgNõÜæÍ»Õ8ÉË|ßûº¿®ÛUu—óŸÿüç e0Œ’H$†‰éù‰y)ñqâ?ÁJ"Œ&Œ“®Ç™ÿš¦-¢ÇQ!û2FFRddP5–_j&D‚k¦P8’’J$…;öÍÏÊ.ÊIIJγ¶‹ _j?eŠÃŸw\<<<¬×ý牵obnJR¦õD2ÈOLÏÊÎHÌÌó´žOÞNOO‰·NJ/ÊNεŽKHHL§EÅ¥'¦Yû§¤§dggå[ÛÍ··v:ÕÅ‘ü¸.LÉX³>×:".3×:Ä:<1i}z\Î_nR2¯(3~Y¨OqVBôÂùÙ‰1a¾ëÖ.òËIZ쟛—¸>uIP~ZdpAz\Ô‚ÂŒ5Kíì­mœœ§º¸º¹O›>cæ*ØYžåHÙP1TåKyPN”-µœZDùQ³(gjµ˜ò§<©©ÔûT8@¹P©*r¥&QK¨ ʲ£"©`ʲ§¢¨Ô4j2µ” ¡¦SS¨eT(åCÍ ¨hj!5ŸšI § ) eL™PRÊ”I¢Ì(õÅSæ”5šCÍ¡,©¹”5–Z@|HPr²ÍWT·d’$N²gؤa!Ã>öZªKë F¤ì4xAÛÑÒßÑÿÃLd’-»†ý˜ýsá*‡¿7D¥;ŠvrsÙý›÷rU£š]¿:r`ßÁ¯ Tk2Z+å²3j–#¯Spè”ÔöJ…4xÍrqã·5IM!5>ˆÃã±öÁ^ýÖ`£ºÁ{»3˜Ò«ã|ç¼äŒ‹ËÊŸÚÏ­ñ٭Сõ%Y †<”ÐXÊàE¸‡A=i>Æ€÷è i ãp –ãk4°‡C3©…/ÔR!Ùâ u Öf`žÅɃ¦8]kJc~0C-ñ: ÅíT°S%*³. tij,dB—9¨˜6tñ@}'{{òTÅ¥k–Hßât’“ wkO\~h‰Z 'žˆþʘØÄhð=J ½„j¶.Ä$·DMëŠ%ØS!kÂ~½Xã4Wß9/—•ø?dq±Ç÷_óÄ£ÄÇÆ‹=Ýœ>0¹ùø'ùŽ„FT°€zª…0½3ˆSEDz ב{õÖD»vèõÕ‚\-ÕZ’c¸é4Œn” ¡'ŠS3‚±NM¿%Ž^›§T’ ©v²€øLpwVbwdf§G†ÍO²EØa£º m¾W?È~À½zy"¹)Ì–èM)%™a¡)^ĺ÷ƒ`S ã¯_)Ê:¦¨ÎÙ—¾'’ÓÃ#LV’ ¸,ÅÅ^ Ù·—q&ÿŽ0ç"›r[V} NøNƒaXážš@b¢Ë›•Ýâþç³ð(å™ç%{r¬‰Ž$dæâ¹?‡I`ÔÓ L% ™<ÿI( ­:”u®àØÖce—¸­ü×o?AœêvÀ´mh[Ù6²< •pK%”DÝAÁ‰ÑënÑÂ%•.˜…„ø†@<¥öÑñ8Û`'\ˆ \a­º«ºùº¢ë^#ÐFpð!V€#N•{€ëo؇`_rqÂζDŽ!ð ¹8)DWê÷ïÓc5Ò>s8Î@Ð ‡ÈÇ Ç¡ |œÑ ZñÂ-˜Ë„'ã…8dŽ=ž ø?¸¿ãd§°…À.;¯-žçÇÊ:¯$,:æo…å.˜&ªâýÌäÍG®Õ+p-‹'j=ù>ÂH3lôoª~V˜@-Ѹ€V°ê¨{¤°‡D—»Š9Qñõ©/>CeämláÎÛ‹ç½2v®ÂÕ/ ]·¢GXÑËIóìRI~Ò@߀”s‹‡"æ:¿¿þĹÓÑ=ÆÍêÄäøò`††¬ `D±}-Ë==£–»*†tæ3”ª$ýDüÀS^¯Ò‡RaªJ·ƒä •·1gä˜>jb}a…¬®MÔáQ‡³èѦÓ5œ¬íÈß¿{Ê–îØº}#ŠDkÒ–{q²ºD‹[pf;Œå,RÓ gÈ*…©d™9¬làVܲš@+<ÆsØ{Z•ßÙhE}ìÕuWÐ]ôÝÑÆ»\6‹|7¯ÎÏ\Ÿ¾ºhJD)»ó¾.Ü·åËk¹éÌ.»ÎE`Š”èÞáã §/컋À”#‹“…qa(ßß2[àQ‘sܦ.ÑëÌUeŸâ¿˜¦]WÆ;v¶ýxü9v რ8áµäóê=¸ñÕ‰ëò·î4Žƒýüʈ‡¡« R·Ä¢1ØyŸ\sç7„€Ž9>‘ õ¼¯„þ‚1¼Vá×ck•®TtÌ©ª±6“l,˜ðe¿|Iԙ̙ìÑ#3ÊÂïK«òQ☘˜TŸU‰åóåö}´ïã:ÎÙ‰[Ã8þ£ŸÜy­Œ=k{Xáy à«Ìƒ¨nÌ…³µ­÷Nf.Ù!—UÀœ I{IªÒvŽƒ=$¡Šé´ºzsI…üPñî´–J«ê+ïÏ[•· Fdž¾³“™F¸Ö€…ì-,’h¶2=uñ. ™PŒVý=¶ª&Ó× Ûkó:„É­’§$"¿ fâ ¾÷uÖLÔÉŒ;;þvTÞÎnütCÙˆKÚ´»Nøk9Û‚ ­Ü#s'“¹3u•=Œ6Ã`†Í±œ"œ{­4» ÒÀ/ê(#a/_¶ö=x6ñ¶»íÚñ2GAËiYßϹ kÃ-QB~|úúÌ‚•¡Ù(ò@rCö7žØ~ž8~{`ylM|sÀ“$¢^t¿¢ñdS}ítõGܵ«Æ šFË:Ý¥½jùðNÓ/À݉rØ.ò#¡å… IÏsÁˆíÂLüT£Íp­ºÐJÊÀçðTŒÝ¼ŽhÑ.³;=°“H_¡o.TÎÄͬì¼u„Ÿ{PÜéÛr©³cÝïDüÖÙTÝÖ$—úˆ€à̇ڱzDÚ ÕÄâa€]Z\¼ìS„¶Ë}ÙÝÛþVVޏçM§î+´Æ,ÄÌ´éaôA£eTø’„Ì?µVIÛ°0'} àd…wÃþq·¥êòuùçQGr/£ý¨|Gõ¤b€em)ÎÉKI_óA â‚k[®Ôí+W¨÷~½óh9÷ge=Ö>'}…ƒWéqª0HOb`é0þx¥‹À7þÎÃòÙ-ÖÚÃÜ"øB34ëû‘ëhZ  o5ìŸl8‡xð…ÏE>˜áplá´óñnŠ+è Œ•`WÒCÕú»žã d‹_ Y“ž¶$Õ©XÙã¯Ül½u"1PŽÅâß›'‚Ä¿Â8öyä%{¿¸üE1òô«q‡Z¹nU'kzÈþµ°½Ü—HéVrY/¾î±²¦…§[2;¬@ÞOjðžöËç/Ë^” €&âJ^3Tµ…þµjû—ï8Ús%øÙ2ØRׇg }?< Ç‘¡-aÊߘ!¼„Ùº—„'Ï´Ñ’ŽÇRØH1 [„à`„]v½ƒ_q¼Â¸m ÌFàŽÀ­ ¼Ô`ý›óK58™·! מÜFm›ˆ˜‘Ú»Å`/7,N«%_<éT_…¤÷­´÷ o{£ˆEþ VlãÙöçŸ)ÀˆÆ3Øú@up‡Ç›çÃfë õ«\ÒÜÓH/i£y,e«±%]ÏTƒ% RöžùßÕá¤÷"È.¶oD¹(¨Ô.#‡."†âMƒ›è+ lÒnÒw,‚R)9ÛuýR8J6Ba™kcV†æ9èÄ–n,˜¬"…C)È×ÿ¼âùÚæEGçeð¢q !ÔÊ•ŽöN+~(ˆj|ñB¡Ï°ZÒ1Ã^µö ;xݵ6z1›Ž%%xl1"’õ Á£_*|ÖËë†3»¾ÿ¦®÷â«Ë£½|±ƒùõÜ+q§âN-ý:¹ À´„°œÄ±Ûæsjæ³ Ÿ×ì­ª:ûÝ‘Äuߟ‘º<$Iá¼ÛÍX¸O#¤ë#^ U ¤Ó¬Q åýþý¤‡„u ä÷0uÛiÙÛæØé®K¢œ§¬¸ðûG RA„ï^»?íd@{ò©"&>ïËþäæé5$¡ß=òmó}K0™Õ­°í/<åS¹šÙ~vç¡=+êϺ‚8eãʹÑE (r7¥ZÆéqû.!„@ÑM èo{«Áûm—°:˜Åtá´ —ºxœÃ@ܦ‡Ý«ç^‰àI É&Ñ c¢tæôcæLÓñýd§7‚mÞ_:Å{E}w>9÷ÞÅ»³*׎|ÞMÎmûê Lk§—xltü†Œ5Š£CÃ…wxÜTöT>SJÎõÁY²úÇ$°‹›Ó†)ä…üãBbý ky„G|=§!äTø­t¥ØP ¼ŒvÑ`™ß’쥉Ší0ïêË×è:›ðeG¢rÿ´e‘ƒkXð,ÐÖŸÔ7nöñ@ø…²_4"Ó]1Ø äí¢p)÷œ©‡‚>X î2ÔÖÁ7!WiV×ÜeäÇBö»`% çeç¼g Rw%Q°Qn^Xt(éÁ*¹ì÷Y ñ‹¼,±ù/.` Ö¿ôßÝ£^Ž9ØC&>ºë;o^̹{­Mç*p³ìwõˆ™3Â"\¦…]ëé¹qUlýRÄ ƒh1Àu#é6Œ¡Lp bgaä;Ï„+áV§äT¿ˆ^ Lç÷cîG Hí}ÿÔ™ÛçÛ*zð¤++ú1îvµÐZ±™µš< +ðè~'xï^Ó¡+ß)Êð¼ˆ)Q(Z]_ÚÊÁ{B*¯ºàì¾0tÚô¨ë/úoÜy,‚‡}›%Â#lF*†ha@Ÿôß@Û #¡:ÚB¥Á»×K‹0S|üV iıâÜöÿM^•P)Ö!UŒÒ°g„òoFFOwSÔÿ°9Ìy endstream endobj 90 0 obj 4743 endobj 23 0 obj <> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 91 0000000000 65535 f 0000041711 00000 n 0000065727 00000 n 0000041551 00000 n 0000039285 00000 n 0000000015 00000 n 0000003222 00000 n 0000041759 00000 n 0000064863 00000 n 0000062991 00000 n 0000065257 00000 n 0000063441 00000 n 0000041800 00000 n 0000041830 00000 n 0000039445 00000 n 0000003242 00000 n 0000004973 00000 n 0000041871 00000 n 0000041901 00000 n 0000039607 00000 n 0000004994 00000 n 0000006676 00000 n 0000063926 00000 n 0000062007 00000 n 0000041942 00000 n 0000041972 00000 n 0000039769 00000 n 0000006697 00000 n 0000009649 00000 n 0000064514 00000 n 0000062568 00000 n 0000042024 00000 n 0000042054 00000 n 0000039931 00000 n 0000009670 00000 n 0000013388 00000 n 0000042108 00000 n 0000042138 00000 n 0000040093 00000 n 0000013409 00000 n 0000015632 00000 n 0000042201 00000 n 0000042231 00000 n 0000040255 00000 n 0000015653 00000 n 0000018116 00000 n 0000042283 00000 n 0000042313 00000 n 0000040417 00000 n 0000018137 00000 n 0000020550 00000 n 0000042365 00000 n 0000042395 00000 n 0000040579 00000 n 0000020571 00000 n 0000024131 00000 n 0000042447 00000 n 0000042477 00000 n 0000040741 00000 n 0000024152 00000 n 0000028293 00000 n 0000042529 00000 n 0000042559 00000 n 0000040903 00000 n 0000028314 00000 n 0000031058 00000 n 0000042611 00000 n 0000042641 00000 n 0000041065 00000 n 0000031079 00000 n 0000033979 00000 n 0000042695 00000 n 0000042725 00000 n 0000041227 00000 n 0000034000 00000 n 0000036404 00000 n 0000042779 00000 n 0000042809 00000 n 0000041389 00000 n 0000036425 00000 n 0000039264 00000 n 0000042863 00000 n 0000042893 00000 n 0000042945 00000 n 0000049755 00000 n 0000049776 00000 n 0000052985 00000 n 0000053006 00000 n 0000057136 00000 n 0000057157 00000 n 0000061986 00000 n trailer << /Size 91 /Root 1 0 R /Info 2 0 R /ID [(ï-1¦/äF²¯§H —%#)(ï-1¦/äF²¯§H —%#)] >> startxref 65928 %%EOF simh-3.8.1/DOCS/pdp11_doc.pdf0000644000175000017500000052041311143604434013643 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[wܶ~ׯØÓ—.{² ^@ôÍÕIãÚ®$·Íqú ‹-7µ-Ç–Ò¦¿¾H`>—+§çÔöL‚ƒÁÌ77`ö§MU*½©üß8¸|ôõ‰Ù\> 7'OÆÁ§ë£ŸŽú²öÂ_¾ß<J ªì¨ŸºkʦÙtÆ©µó r|Ÿýè¾Pjc³/Œ?é%kÛMç>íå¥ÊVoΞúÿþ6,¿WA÷OÎ~÷jûÈs¯{cÔöy±ëJ·ªu꯽*›fû}±³eUéJoOdê·EUÖm×Uõö‰|õL8s»îJ£:íVOsŸ ú¦M‹‹sî#™{¯9\m1?‘ˆÞÌŠ¼™Ðç1ûw²”Ô Ï&šÜw.%]r"”³p u—Ìö"¦™,O9¨dªLɳÎøzÍ›ÒêÈéV ´7§£§¾`Ø¿Ïy„/‡¯ê*¶ ¨±Ýaoƒô.§Äƒf#йð‡b׸¡wJg…q޳3ýö¯îa[ö]Õlªõ%_ºÊÂn†Œü·l%•ÜDœÐŠ¿H›Æ@*ÔÙªãCk³k9Úzà…D5“ º~“׋MÙXÛzA´¦´µêÝ—J;ñôŠˆÜŸ,ÁN^¦0žõÄïÑ:”¾Q¸ºÁ{B>H[x&æ÷éû±?Àš²5éà«$–Ñ­H4ÍüŽõ*ÀG…?^xœ¡}þMTŸ¡$^³ÉÌS™yÊ@[”q WoV·f/„îS±CØk¾È/½a P4Á /å)€:—q¤ðDöšy˜4ó’bdÞ»4A+ŸË¥€·•ðH9™Ú ±¡SŸã~¨q ˜vŒ¤¬áŒŒT¼U|ýe5Ux˜JÉ’m àÛˆwÄÛ1s†YßÈÓi*ôžÏòþáÔƒîѤ#à"¾éM ¾ZÂ3ìëüÙÄGR[´ÔÖչѠ!~ Øò&˾·ÏOK鄨Áx‘Pý2ÃUt/°—`Žë²]±_¹æáÞT§#¤žÑÖGÐÇÜ“ø§ û“µ¹àuŽE¥9–ÓcŽÐ’ЧÏ¨×æžkS÷<¦5b1ŠÉ.uuЉCÏ` @CÞæü=£Š3°ØBœû µA„ßèò­ÌrÊAF`mÉçã ûIˆí4ý»Dá‚-óuƒ/ôY ôY8Må”|? ZVùÁÒcô¶ãBØÙƒrŽS™:KKx.gžÐXH‚ð€À8÷˜y·Õ ü˜h9 IRÜ“Kî—…È\c¤¥\FJµ¾Ï.x¶3s—ÚêRu5¸Ë½‘âÑZ<éóe×CüÀÐBŸwÈÀË ¢.âçJµÁS©“tÌ=Íj†ü¼hý6„i×SuŽ©K\ €1-GOG<Å a<«ÕxùASc–¸ä:še°pãûâåcVÕ­øð%¼À„öû.…ýmÂV.ùO]#…{oš¤íAÛ^#ÐBrï}òðÑþ÷ËgÓÄjvfQ;‡¶VÐDl)Bí/õ))€ÇiŠûó¥tß ––åãÔ€4A[ršÏ,ÚS´‘…cvÀ˼5ÁA>0ò‹¸žZUí‡V¤º|͎׶±[Væü[?/egtpDÉo:§-t{.-²~UF€wè¬ö×ÍÖÓU™ìùõõìVqõV\ºYj1c ð«XDÃÛCîs‡ŸõèÃMÔüÎâÿÙØ½tº~CBº%`)®Çì:eE/“n£–ìlá~„\EŒeÁ°1ÿÁUýL­’«ehnM¤k-¿-àÍ*\~ÌðÛÖ 7í´9 srõç="°É›¤½ŽÿÒå ›“óû:––šÖ‚nÒÏáê6Uè‡uŒ…›|N!ûaUâ†í‘ûšîë×…÷ê0¿ÇÍÔj·QÄ*o…ÁúÚËnµiì’‰’Þúã¤[Š÷¬ŽßâÂXüžu´Nå ~è?®èrÏòöŸ{}{ÎêOWnfýô­:$®4ÜóçiƒÕÖ¥Ní7 º,ί¸c$¬ì˜Àe(­-ëfÖ‡r|vôg÷÷¿Gm$vendstream endobj 6 0 obj 3138 endobj 15 0 obj <> stream xœí[“Û¶€ß÷Wì£ÔÉ"Äxtl7Ií4Îz“æÖÛI·¶ë\œIþ}Q$¨CÔJ"Èe<“ÁRÉÅRß98×_.+BÙeåÿ5ƒo.>¼Ö—/»Ø¾¼þx;øõåÅ/†pÿßæ¿xsùÑ›Hé¥%V]ÞütQkM¥ë«ÒK%ÝÏV^jZíNxs±º\ßüç‚R"ÙåÍã‹›¿}·¢k÷!eB»Ï®¨4”TtõtÍI¥¬«Wká>æV¬Þ¬¯˜ •æzõ¾óºýøY{ìw7âR©Š¯þ·9ÈY¥W¿®¯$©*ʬ»Q{ÂßÝ=•;E1p§pÑÃüßÜ™ÜmÌŠ„ùËðÈCîþúÑÑßüã‚I$5ùáâ»ÑŸqÕžÝpóêÎF}uæ¿Ò·r%Õm‡âÖWè{_íçWÔ2¢íН[q¹™t%”&NŠ]^m¥œŸÊÖˆŒ{ÒʸN²f´éê*È+ºöãÒn…åövíB:!&ˆB'°êS ß Ä͹@"¾_c±#ç˜ö"qì7cÖC\ÎYE¤¶‹œ[è;d¸È¹ Ë®sɹFb‰uWbqæ´kc6Ktø1dìFŒYÁ •«û»ÂK¬¾¬!¡œz„ÓÛð?Àò'h*¬Û^1êw{rõךY¨9Ùä…Y{[~wèö›rü—bÖÃú+î_V)¢ô²'[`:hXЬ*v¥Ï-Sn3ldŠ\ïÈ.‰2ºg •¤¤aœjÂU#i¼P‰$ ¥±¼¤¿áü†gûŽÔæ>åÜÑmPY4šÒp¨hYVº;,h»’î‘=̉ Ûg¿4*{*{ÜvÄ’ªbsÛwˆ ÖìFÚ™ÝÈÖWö?×A€s_†apAíÙé³Ýæ,pƒïWá3ðÄß´Ò2øÕn‚ãë¡?¨+KWŸ¹KUJk#èêûõúÊX·¡¢£¿–§‚ãœp]Š˜Yp4õ-d3¶î?ü «Í.«ª'˜L³š¡Æ§Oà †áçë+E”:rõ?@éû‡Ghàî ÿcì aÆ;Wäèï™ßÍÚGâd”Q¥P{jû*eÎOí;·ÒcK™!ÃF"ÙõŽD¢Nm¶Y)ÛrÜ!o׈¶ÿ<8F€¾«Ý‡þÛ0!rŸ´ç~Dàópž‹;éH…ûÁÚí.À2©õê^»a¾™­$U6Š7øc-ý©í/æWÆ‹S¿ áÝNÝjêBµÞCb+Z¶8ºs(*~}Ï.ŽÊØÜÎÚë–ì08㦓ï!9N¶ÁðWwª¦f¦ÃËíÿÂM€yéJ«Å²²7kÖ/½à´4œçÕ)ÀªsnПÔDÐ+K çɘ$Žš…‚{ËA.0¹ƒ€~ýÆÁzÅ•AS>@2®uŸÃå·'zô·¬ a }M´8úwýü@?GæY鱕ûý’$8T5ï•{òF(§Ú “Öþ9v7ÍQ‹ÀSÐ`àð~œ«@j¼ CÜ_ ®ü¯É+Ä.äÆLNx†šƒ’ÏàÝÅÍvãIëOv =ÞL)÷i&£¿ÌÊ÷MWj:ŽƒEø±¾çcó÷6T6Œ8BHeÜ&3 *·fð¸ïчÿ3 t²ƒ28ôÖšf'ž‚¾ØÍ—áë»ØÍûÀLwÁœk,GÁÌQ0«Åãî½»Cœ«8ƒ•ekýPACuÜò|£ÜÀõ B6{@  Á=fƒâ=€¦–HÁKô‚âi¬ï´m*eÆ­äÑ^UDꌸ”öbdÚÇÍíEçú;4 f(ø›¢àv3ˆQ³2Á×êxET% üÊ.à/x}Ë£ýØšy6ÁÙ.Á5%Š« .Q‚l#°žàN@ùücùñº¾ò€Ü &/€gMõ[©›ÃàÁž`©LÏL"+6ö‹xf~Ÿ>êeÁóäð;NÌ2±ýÀd4ÿÒ1;Ïá¾õ]4ÿÃ@“ˆ‚:i¡y„ÁàYC1³† Tð×h/ðÁÒëã“Þ¯Ç!¤7"ƒôí,ˆúOzQ_¹¿à¹’Ië°D÷z±­ê ê Þ\ol fcVïbÖr¢¥=³xÀ»dN}GYl€æ}…%u«ºGLs\½)•¸û3f5ò ‘  Mሇ²z¸©épV_£áæõò j•{}ÌícºÀúè¨=Q’ÄìàSPEŒÈðŽÓ 5ìCƒXçu¶0ÁYÏ£ z`oO?“éYÅ]œÿUg×6³x÷]E‚Ü|JŽý`gZé±èl¹`wäw|·&ÝÜAa“VæO˜€’Š›,À´¾W­B{;É´¿¨å BS’S€†›Vk5â^kz>xF|ÖGçȽ¬UhK8]Tèi2úLÃ] I…>RE,( êgdt­G©˜4é>Á,²bùÞp/Û‡ ‘M7q@ð^Àøg„X­PsFô”í .cÅÝ,±| }á2Oqć‚m}ñÞ{C¿w³oQ z I'ú½u­â4µÁšmBiu«Ñ¡ì£o»Y\E€`¡vÁÃ~(ϼ:À/VôMaÒ¡+¾£ÆÖ: ìáݶA‘ÀµŽ& tNÔ‚ŽÃ±[é/ÿ‡?«s¿8EÏøåKŸÄÚ®Éi- éæ'I¦bÑ8ê° ;÷~9€U)r*Ÿdò¦kD ÇMÞ… £%ƒ°€Â€pÔ¢‰,Y*x Ï f%ký¦á¥6%™?@ãå? '3~îçÛËNå8ÿîý$âÁìÑ,Ææ&Nô<šZKXF%åLq[s°^D%•õꂣ禌…‚‹`IAmŽAn‡E øq[¬ž«6ö°ÆMä<øóÞ1Ô̬?n‰Ã¼ERé. ë–›  +A1¤(H¥‰4ƒs(,q0ÀÞ·©<š 5ÍMë€5áPÏÝûÆ×€¦í³¼ !zï°Ë‚üEQdòm¡E߯é4Zû±ö¬£-41|Fõ2 `ʬ‡õúm››ÙÙ$ß-‹Â•q }FêM‚ä"Ertb§ÓÅKM{تæÀlÉÎöZ@D ”Êäa†ììnFì*Èú¼éÚ±_×{M‰U­[ÌŠû°P-üMGÿòÏy8!°ÕFp÷ÊÓ!å^ƒ8Á¡zPðŠ8Üß—BÜ—¢‘û<ÌUJRˆ§^°ôsoñ0´dk¼z»dø£”ïÿ m^bmëá‹ØŸßé9Å7 Ñy0çá1Ywdª)BÍQ»– TÛ͘ˮÖ¨ÆQCˆ ¾NEnÝÿƒAc À ᅩW¤•ó’5KYa–®CYÿv’+aXuÝ{SN…»r¶Ãóη2é(äß7<¸õ^Þs͈´ƒ´Xò>h‘;ÍÝ:^0 ÿçÛÐâI ø o" œm½i#‚»­Ã¢b þ¦ZI\/oñ4`|ÒWg²/l¥Ç6gËœmýëÈ8,ˆbƒ‚ÿø8a‘ÒC‹/|z€u› lªaG˜Ml-À xOžSIÉ­ ïT˜!*˺ݳÉyÔš+ÆFZÊ}0áјáŒP{üò¬óÀ¢2‡Ç7e'úí'®1„13 ún*5å㞸¼$¨©/~qÒ,r ã¸y÷^K6`=¼ÜlI÷nkú{Ë"6öË$‰ýqœ;¤!rãW«-ÖŠH9nA»9ç†lh·>~—Ù[ E6#÷ý\âšD¼¥ ç4ÔóîiÐÿZ3Ke‘Žö¼Ô|7-Ië·hô ƒ…As€¿ÖMT0¨j ¾íQKÛ[G©qXËÀÝßÝoZ bî¨Ù¤®ŽQQËÈ=7Z”6<ÛúÎÇþ1ÖJ­lãÃ<\:Õ›ÃöÁî“›ÿ\<¼¹øÂýû?ig©Ãendstream endobj 16 0 obj 4660 endobj 20 0 obj <> stream xœÍX[së4~ϯð6ƒ]I–%û‘Ka`€)=—s¦$M[Hš6I…_ÏÊíºÙ´Iœœ>œ­.«ow¿ý$÷)‰Tp?1š Î.mp»”ÃÁåwµ±¸< ò$uÿÊjfÁWCØ(eP$… †“HŠ"¶ò*“ÁïEX) fƒ«pÅÒ$VÞE0,•¶á}¤ÁJ .£Ø&BHc ’*Ñ*—á,Š•N„Mmxãmžtpî-¢8sÛU^—ƒ©6|@sŒæ3šÄW£ifŒHéÚ9š#@{KÚ2Øts 6y¸Bw.4êžLÝ¡û4H•t~Šð8%Qy®³ðüa 2à–@™áxçÿÄ͘ +S8qvY2kÑ­xÅ6cE¢*²I ÑYf —¡L‰$“yø!ŠSMuaÅ€KAÑ &‘k ?ed¹”EžCdÕ’47[œ'0œ\EL bŽk®"Ve&uçùmßF1P^IåÏs£Ôñ ]âA™ž.f2(tÙaUšê¬ý8~Þj9ìRæöÜœk¯G¦O§Þºá\¶¸íj,LÛÜö .¢H&”P”æ5Íe›±Þaìè ²®R ðO$m"µ‹ÙozfPÉ‚²]WÔ5' $¤ ö æéaCg{_¿Õ)ß`¿‚@ÆG\ê²ÓäÅyõû¦Îõ®Ûq¸ô¦EÍ'•ÉD]µÛU]Ýré5Û¤|ælàäÍc#ñK”†€D%zOsî×4k|™µêÑ>¿ºöùMA”wïâ"jžI‹ÃL:¿G³É·ÍýZ0k¾‹Œ†ðLÓí—þîžÒ¯.I¿´Êaj2SS&n8ƒr|EÿµâÚ[Þ×gdó˜Ó¶—¦U=m¦6ôÒ²ºšýÔ#†DÄ_E„7ÿSO<â†,J‘wÂrBŠ0m=„(Ôíä§&þ»êó+>>x!:wC$ˆPïg¢a%&io´FK nÒØs­P}îÞmžNؾ|%ļDÓౚ=k“^ú„J½¨ä•ÚN%E­RQ*â¶TÒ‹Á½ŸŸ³Å "e-¼2l%U½ÝB]Õ|Óë¡”Nhr»ò’À´?¶½IÛÚAe3Íc þû ^L*õ/&øöÈ›—ÐÒ½ü2›:¿5óÖYm)'{±®ÝËÆN…‚p5Ù>òÖ£·oÝQGWqÈúæ }dNž{ëÁ[Kfvê­›ŽXõ¶P•a±Ž™“' Ö~2‡VkçÑÙÅ¡²ÄÚ0ëæ=§fÁ¤áS¿ià˜‹\ÿ«ç„ C®™NïÔGûÁ­;aWÌìËÖ`ºWêm­.Ð}K-õÄm W¢vÀêm/(‡¬Ïö‚rÀÔì",ý$©«²¬¡)ß4SÝž|´cfL2ÿ\ÜÞ'8¸ã™A=gÎØMm÷ƒzÁÌ¢?$þª#êwè¾ è1wÈ·é.g š-¿; )ùÄ@\nñ°e~a ¯¢œýƒéo´°gÌìlst²P½¤úmñÚí9¾¨nñFÚ·–îô$؇FRXÇ«#Rjþ=ÔL˜½S¿6º¾­X„°¯±ý ¹=~j¶ÿ ;dj¦ÌŽÞ©û¸v vt~Ä㸷sßE[0ù:ZnÌä¤Rs×Í¡AœwL×{»Ÿúìé‰ûÃ}¬9~Ÿÿsr¥endstream endobj 21 0 obj 1356 endobj 27 0 obj <> stream xœí[[{ÚF}çWè­Ð/ÈÚÕ½oIì4­íÖvHš¶é×/ÅøÒœË¿ï®@;ƒtÐJb’b¿ bwvvæèÌì…Çs…t<ýŸ ýakç$vÎÇ­ì±sòãL¸=oÝ´××Ù.÷‡Î“žê(}'uÓÈéµ<7M/žjNªÏièÄB¸Aâô†­?ÛN§ë{R}¸8R¢Æ~Ð>5=@úÛH·Fúd$×Hý™$#5Þ_½Ÿ[Ê©l9hõ¾_¯5ŸkZ¬Ï˜±‘&@ I#}ØŽ ÇïLráÛ pÒ¤4r¥5ë6b¸Iñ¹Ù¨øltï6À5Ä1]ÊuÑ²Æø1µâ³s"„ɹ*‡+»Ô7ß©ç©#¹\œRqè&ËNWY†QªçJÏ EÒ>Ò£ª]ÒÞÕ DÒC?iw; O¦j&¡JEzNL“çÆ2ö“¨ý¬ã¹‰i’è “·P외ê™z:1ºî¨©Ê Ri#_÷Ê›Žs9aAá¡æãFž­êÔûWG1”y{JcäÆBi¼P£ÇB±1—%ùay¾š½ïzQª‚¼«m“I óP(tC×óD撮õ©IÄíËN ¾ñÓŒ4eàz±góW¥·ß›ïßÒà )¹¦§·42…ÈÆcßôõç뙉߾"UgÙÓX)ëNßßÁQôô”D‡t±)a#N§ä)€O'¯ÝF3øc'uã$P]´6e„ôÓöS¾'fИ½ ¬0õ#«Ðé 3§wªÐ_]ý\‚ôJí#]¢`z¼iƒ|ð¦Ãßì®HDàÊ”5¸:‰!ÞÇ©ŸS½$ŽsQ)>óg·§Fë‘‘^ÎÛ)…~Åð õÚ-iRý»‹)Qêø”Ä#_¶I¼„¾¸€Ý$‰!‰‰û$>®I<ƒ †0C¨á–ÄÏ ¦cD>îéÄH@{¥gÓXÊ@ÄnãPú¢—`Dv3$ŽàÌo¡—& 48PÙj8µÙ°G»#ñŠÄ>€SžYB)yû'ÔîW[Tèe:¨fî5œÏ6xos:fæSîK8ð[0Ã9 ¢“9÷äD‰ ¨Ü {j§ˆsmÏ.‰ŠÌ}8#æˆSØí¾ …Œ@ (¿óuXõ¯†à Ž2Fψ¦qkÓ`AZ5dhFû ü°ozýVᇹÊóÑUíÇ Wîe£µƲ!ÛsÁyoöâ‹t4Þû–@T4§ DàêæCZ òky{:îj˜ê32`ðÏYØŒY}µGâSè¾&?ÉøÚP²Ô¶ÀNñ„¢vJ ÕÎ~¤h¡ŸÃ¥èçÐHË ‰e6L?øz4ó"¾T…o1õáÿ+<é§A¾›±šÎw«»÷ùb)@¾0Rm@²;µ^ÏÚbsØ,`ÖK’§°5Öš5k±&'å^üòUh$:)cÅ?æEvž„‹Ömɉ-zWåªþf^å‰ÿªÀJÌZøÙuãTOH¦E.dV| Ëöè6 r qXñ½lü‹ù&W°Þ¬¾Ú²¢{­´CÒ„déÈuÏH%M|9Œïù³5 ÛKþ…ÄÇÐ7¬m“ãù=ès|ÅyÚŠôÊõy\÷z­cõÿ÷tÖéendstream endobj 28 0 obj 2179 endobj 32 0 obj <> stream xœí\ësܶÿ~¿ù®õQăÑv:cËò£vbY´iÒéÈz8J£‡}§NÜ¿¾I`äòÈ»(ÒiNI&³¢Àb¿Ý!~N²”ñ$sÿzâøb´µ§“O³Qù8Ù{U_>>ŠT¸Ê˜>¾HžØ\$&5*98e©1E¦«YYÂ2žhÆRY$£Æÿ˜LEšk!Çõ¼¦¸'“)ã,K5w¤È¸)19 £Nu¨«@]ꤢ˜å<5ùÈ ßùÈg@nõÍ€†}Kμ¼rFC«Íü ÈS ¿yI2ÌÉ%޼"g˜“K Þ_š¤*LAH& gôümd„['y7:øv“ÃØ9 &Sn:œãEµ¨w­™¤7‡¥zíy¸„=IUo’•;%ã`eÙmäå°`ÿ®°v,xôß)±`€ï8‡ºHµË6'#+ÀÁÏ£iýhZ#ƉM+o[‘?/Ú£ä˜àEBJú+¹íSR/'$í˜G¤ÏÉÕu/úìˆf8&F&½î›2¿vå äW+™æU ›†vÎ ’¼!ÄïÒÐÑ*ºè ÒoÜ­=ÆBÙ7*Í”ÉmTrU¡9÷p`m®RÍ·û±°Î¸ÔvžJ,%r¥²N¹ m­ÙøŸ“©L•”ª?ÄûÝD¸…­Ë¼†Q4«ÖºœÈoW¼…ÌÇo€a«%‚c@¼ï'Se¥Éµ‰—pP¤™4xØ»rg‚gÚ.ª%l¬;x»–äìâmØ;ÍúºÉªîžÕ £,0oףЮiïË>óìÁdh“OA!¯ÉsË꼊ç2•¬ÂûÆ»Nl™.“"Q>`Å5¦_Àûœ^!L@Ú†§Ëöë‘ÑVG}.`•úý„å)“¼ËV(èžuëR¨\aÖÖöJš÷ûÔN«ã13×Úö‚ÚuZ[uZ‚3-šb¿Ç«Wf–ǼŒ§’¬ÔZše<ëf $Áëb0hÔ!Dð2¼.È·0j‡&¸ÎÄÔ%'%ƒõÆ´~®W°[ô{4êÈÿNr7ÊÎz>‘5TÛ¼¢€ÂÔø]®:ƒ§hVÄ KIø±dP,w}Ü–ÿÚ£b‘"̲óföû.¾ &ž»Dí¸ŸÑÈ”4U;i5´3»xj½Ð–ˆ²KÏKf»„±i4J™ºÀ °(PsZõ€Î {²Q´öE =j»#ý7‘ÜE`’»cåÛï‡ãÔ³žÀõ¬6šöëÔÜ5k«Â„7K"šÇ§îŒæ¹¼Wõ%N‡b~-€º#ÀË*â³éLøˆ?צö³ÒH†€Æ–Ö¿2Æ#œ@s¡àf´‚„õ—ÀŠ 2Ú” ›œléõu´ÝuVZ$á‰ñ7 ½ ¤„ý¯®Ñ§^Ÿy«Ê|¹‚|7x*ÍýC6ž‚’Y­G9ÅÖzÖám3Ã¥5¥ð[CÊu¿G[G»¼"Í‹V!¸Æ XóŠ\ç«ñ¢§—¤å>šûB£Ê¬©]ž’ÂVËl' vD>qC®^‰mÆŸ±G ¤M¹reZ…×UÜIkVEeZ”À¯H±N çMc¸ ^á`§Ê†?U£DÆ‚ûÖb5ݺM((§Øì/ŒtG‡Ä(|i¢«‡“I#üšÃÎÁ›Ð¶>‘hƒlô«s™ã†ß\“Þ†V M€V¨8økxø[·uÂEkú‘÷ï“©©.ŠÈ1Iç†_´ ò)]Ò–;%=†>‡¤jhGC W˜!ÌÛ: (¿ß“­[T˜RQ„aáÕêMzÝR§tµé‰@á*Ü.É4€öã§]?¬jîL\Ù¡c)º G;§­ƒåîLˆD-לÒEqÙ JÈèh1DÆ +Ýu%!Ç?N€&²ÔÐâë¶[Ý(f©#St|µ‡?è•1×}mcd_ÊPuLµ¨y8#ÑÉ„J­eÀŽF"Z2;БÝ[¶6MçæzZG~©rè3hèS$7,p™ê9j¥LŒ~Žo¹‡{›Û/Uá ­Vko,ŠÁ.ªwÑè "w/¤Š€Ápì=qmàÅDeYëu¶ U©d÷qSʺã6ñËòèÈ’vÓÇù2]¬Q^“$7º¡#Ï¥h E"ŽN3Ñq ‹:M2¹Åsù¶‚RnO7­^­9ë@J^Z­Bi·ö¾á‰œ± ù"åÆx/‰ŒrLÇ?º5†®}ä®óM§€åý³q\çTèMö:h_ØÓuƼ†‘0Ñù,œ®ã-xÖEpùâ‰.uëÓq%ý•Ó­=‘'Ì©n.Yß\V¹ýÙä‰ÔªÄ'w{™O¦Yª˜Ò6Õ§(s“eî:’NCLw˦°6r¯õxª »nŒFçÖÊþaƒÐœ—§3Á]Äy ±îÃT_Ѩ¢ç%‹Î2åÊDxî Í ¼w“X—À—¶ Ñܺ4®?‡sÎ îvnÉÊ[¬û¹I ;Iy€À•I…]Ú½MUf8«Õ"•æÕ[¥ú1qµ¨q£<'dÊ”“.ä ¹üU£íöû éß…¹‡Deiü©ƒ\îôª‰ÍôZy0eá Y0Ð¥’ílpÂG÷"ˆt/¥t¡„ÇcdX•>ˆh.…ÕfÊbÍmšŽ i®9IÞ¾./ÊV›4Ï8ÕÕ¬®¯êêCo¯ÍìÍs+VÉh5:ÏùCQ ¸3ªª£±3ö? ryÔPô8Ï2õp¤„᯺*§S¶?ÿê}£ ªi(¯9”Nã’f¶OAÀ[G=¹ä±ÿá«2·àkâûOÑëÆ²?¼ £â‰›ÔJxWîZÚŸŸÄuËÒPQ“÷çáúóAó>­(¯¨ÔÔn  >ê~îV ²@‰è0—vs…Æ‚µÿÞgÐôm\9Q¥UBÎð•¼{Ú7ìŠd`$‰þT R4oÔF³†’·g(þ % CÉ5²SþP%™¯g@ñ–É6ÒPœ°ÙZØi£3²Óšf¨¶¡äø‘ÉÿqÑF˜Lx•Ä޵F»Å²âA›j=Ë ©¶Š#ÊNk€‚2P]VH ׳¬-jÓí´öµÑ 2T«¡ºWûäÚèú<’ªÏïðT 6:1) ©Ä´€§ åH½ØP÷P`§»>¾ÆvZÓƒ‰¶¡Z_4Ú¬ƒ Êd«dMÊŠâ^¢kíLU,Ž®ûGA°ÓF÷QÈNƒû(꫉óßÍPæ1 i–¨û²ÓF²Ó½LÀ30”Ç»Ê(hHÛ&ˆ¤¿/‡Ôø…dèýÊý±º?“*Ÿ‘“Ñ_.KH^ú“ië|tÏöõzA  µ¶eOž»‹zd±MWëž7Ú¦=ö@-|wš·¨G¨]Å”÷Õ.|¨÷ÄÌoå×]2<Ïû´Œ>ÉKÁé}’÷ û$/Z˜þŠ(òäbèK¦Ç$ïð•ÞuLö[mhLÒÝÝ(£ÝCÔ½\Ó¨{ù ø›à“«Á–ì·Ú½DÝÃ4ÚÎÁèƒý÷ÿŸýîëendstream endobj 33 0 obj 2938 endobj 39 0 obj <> stream xœí\KsÛ6¾ëWð©iăl§“©Ó´qÝ6/ezH{p,ÇNKŽ-O'ùõ_ع)Y’eKMk À»ß~X€+}XÈEÀÒ¥prÑÛm‚³ë^ö8xýK!\õ>÷â0JÿË`ùä"8ÚŽ" ’0ÑÁðC…I3“Ê­ì߉ 硌ƒáEï]?ìELØ¿eÿCe"Ù椡“'=uÒK'½%Úýá¤Nú™è[JB§…ä&Œ žGy[žDý ^ƒx â{?x bâÏ ¾$Å€T<&ç0ñ ÄOȶhÜÉlÂNçïá¯=ëGaýø[oøÍJ=Ùî5Ô¥î4$ž’ Û6§É5øŒŠ>è{è¤Rï]FßSA|³ùŽ\Kôµ{íN¢ïÞ:mÑǤt´Ï‘NI 3_m/H#!3!»&Íüµ ?O9ˆÄ#î*º"'‰xJÜ;OE ŠnžZ±ƒ¤“â](YQ‚w¥up0¸l›…ˆùl8/!|¶ÍžJH§m@HA›S# n£P6§î4¸–è§Aœ5æÛå4h›‘\€_T;'n¯WÔlRà«h½¤;PacƱ>C;b K9 ¼¨=mûœ†¢‹ÞÔ6 û0%nsöaHFÜØìƒVçIÜ÷¯¾'Nºš©äwè<ðÁg¡(\¹™wm™ à`ÖUÊB8%Û‡äq„ƒ½¬ ð_ߊÂ(¹_xËMÃОÑ}s_ËFd€˜u1´ @ÉýÄ’/j æ¿kÚ͹+éà"­±è°>ò¸ÃbëÐp-úçkQ.¤±ó-¥ÀJ‘Òšeoî„Ř1ÜB$ ™Nd‘ò!jjm.¢ÐÄ:µ7!—<ê_fƒF‚•ãg¢µ]öÞØÎÉp™ôá£1ˆ#QÛ)h<ǃ§ "§ÞéÓ$C™ ã"ÁóÀ“¬­æ*µN© $oªhtÓ³:#UMIÃ!ï€.dÂïrÚÿ‹¸Ø+—˜öS"]§ýdø_éàÊ6baœÛbê¨è‹“.t걓f)èMùfÜ~~ã¤k’Ç.j#I÷Ìo ”0õroIŽC¯ð/A|‚™Ä=}"ýÞÞï–+V¸ØfÁntÝ­µ¸Àë–¡o %ðs™Ž/ íÛaä¹ ¶¹½ò½sÚ¨o Vú\ÛDý‘PÃijRÙ€. ùm eh¼$º bbÍŠ›Ôò—®––³-ýö®-}‡ ªg„®&U;“. ¦õ£ôΦ·„)˜t!Ž­+üϯÝç°×f™|9Û:Ø<$Ü¡íÂ8†sú·;›ÏoóyL½Ðζ<[ÏcÊM7{wz‰ngþyÞ —‰ùC€ÿ’öËúÍÒCµþ:“êŽÐ—5iÇ<ó1O¥fwað/”€ß#ð¯zÚ¶ÚªtrÉT5Ñ*:˯e°9iÆÜÎú[FóËÙZÁæ;–¹5ËÌtHêv6_Å¡5®™z‡ïUï¢õÖó¸ý|„‡¿s¿[zЇæ×…y°ù¶QW`~¯J½ EïÅ‹×之ÉWÿµš†JéÈI^¼ÀµFŸ_: ž•oó“9J-Š “|õµò‹jÍEö7]J0¸RToBi I;SÍ?™1µÒ“1)N@¬ “©yOÕ K ‰úF)ŠIŽaÑh,\JAåû¼XÄBM2]‹ÔìŸöŸ…Tã‚a’O«R”ƒ|‘•˜Ôªròj“ *X(LÑ3EFAsB ðœáx>/Ô#¢5QoH£"Ž ÁãUÄ›LéÆê2¤ÌÛ ²Sr\Z%^˜T%^j Ó6ˆ‘=¢úIꂬö¤°€S¼Vº“ÎC¯´¢ bç"ý¹“™ÈCæ'ªÞ 5¥-„Ì=£6,çd*”8ÓvšÎ˜PçùÜ®_…:­ƒßpùs «¾ådÀ~eÜÄ[ ) ;ëQå[²ÁˆŠh”žµ‹@C#£»µBéG[Û>ˆÖ„ÚS™&þ‘¢Ý*ìt˜ªZX¬†f›ã¶¶`™3Òö%äï¹bؼJNGŒ—¡Æ£2ÔòÒQ²JÎÆ³ž]tüÜI‹õœ­kùñ!1Þ¢ïÄÈe´;ˆ¦D”jiYVÃXŠˆ¨êlÄd[¤eh¨&ýx ]@ŒÚŽHñŠášéq鶨Êù D”¿zåÕ)ž$¡1ŽiPÓ)¡ÎNË1ªì” Ú>RÄÜçdûÐÄþèáÄm(Ûƒ"æ >iRûRÔ¯ÛÉ0¥êu¼•¤ZùLï²hh¯B–Û+"F†o\}sÆï§$uÑ E4Q½[ÔyuQ,‹>×Ôã«ÌÒ#± A©±õ‡HBÁE£ùÐN’%‘ U$úÊÏÏ £¤÷*dÓ’Pé¤uJ&G´¹ÐÒÊSÒ détS‹’=_K´]ûØ6°N|"+4mÞÔ YḠ`³@SÕFnˆ%jl^‚<•'fJ ^®®Þ˜Lú2ÛpòRñ7¨Ϊ(þÿ=U&_!ú¬ MÇŽªÐHøÛ ©ó1“ÕÖ[á´r„tгŸòü8–ªAÃq5»Êλ wLºt÷7…';Õs6r=}´Ez.ɳRéÙ5?á¨0‰Ü6G‡3Ò€†¥ÊÔ üX>l¡Ç¶s.þ¢Å&h©!‰ :“S|å™ÍeòŽŒ`*MEíX™ÆN4}úIÚ›Ã~z)X³†§Gƒ½¤P|É& ãì÷u3”„É€A,QbâìXh,_™üh%aÌð·ZÞuÈuʇx(Â¥rÄÜ;^w¸„£“š,Té/BUÎEí{ê´J¸iÒCÀ©œÆoaU™çœœ梸߰!© œ¥×þÜ"¤”¼m²²šêl›¦èœyWÄ…Êžn %¢¡N³(moæ0y[à! h½´KΰAºç¾KKM,yÕR¸¯n¤­®n»1™õ5¹4#¢o&|^)!ƒynî!îÑ !îË 4!oâP—c×ðSõ’º’IµF`ó}h(¥ —’ɆkÝÖ£‰Ù¨<¨éFªé`Ð+tØ •ôRjažÇ–åyò#CÍÝ)«C¼»Ù)º*«^õ>ö^ÙÿûÃ"»endstream endobj 40 0 obj 2508 endobj 44 0 obj <> stream xœí\msÛ6þ®_ÁoGu*–Á·\çfZ'½Ë“:ŽÒÌÔÓé(¶c»±$Ç/q“›þ÷E»¤¤^l÷êøË†À³‹Åî‚ÜŽï éøÙ_INßìÇÎÉÕ`ñØÙÿgA\ž >/Èþ-púpê|?Ö…pR/œñûï¥iâÇ9WáD¡þ:±ð½X7˜Üñp$"/‘tO‡ú±*v u5Åžï‹(bMaùÿýv˜zqÄ¡{6Tºe*÷šZRËS=¦ÐÏ“ `H?гžÐO—ÃQ˜õ•)ïÀÚâÙîœ;¡ŸfDže Â8”î—¡æK_r.GÄÀY±Pi¹Z¿„mÑô˜šÎà<Ûß³ÉåkôeäN‡#©ôà¡(àË€®Ì’¢±Aæ86÷#>cC^ ÿ{ •òT ´¾Œ´zä¼"ªRÔ2HÙd˜€ S>ëÊÍøpl;º©ÊV¨Ü½a AñSá¾Ñy2‰Ò„7e°…Ô"Ñ\ˇ‰¨a!âL¼‰ûy(SO é>É9¾ÐL³åÊõg“ e‚þeü›ÞW20ûJïŸÄïÆ_dëÒZ <¥Ü‘&½0”ûÉPÙ²½š\eK+y–3i¦I†¼&ò˜ÈK"/àÓcÈÁ!rBä$1³+Hb¾¸Á'"Ïàhl¾7ïyIÊB‚Zn²]8GÎ{¸¬)„kjk{b“c6ƒh¼ÀTº1™~ ‡` šC‰B¾b4ݘ‚\À¶hàRè²þð(ëmÊúƒÍ Í`ƒs:s¸Š%±ë½®Z·úÕ£ø·)~¶g™u¾€Ý_lõ¯lÓYWU,ÇÂÍ£®lSW˜‚\Á¶”t»Q¸xt“ åmõAñög£aUYK)*a±³µ~è_þ6(¼ü\[vò#ŽEwD©özØpæñXZD;Y\C0 ¯ëq`Ö’1ªÀ,0Õ‚ÚsÃÿ†¢AÖ1†×‚ÚÚNˆo6Å|ØÓzÆÙrO`ƒ Ý»JÈQ´x|ûf±¾¡<@Q»°ÊzøíÓ„}"=H²¶¡Í?„ ]BÒª{Ž­2Õ¬)I.¢”¤-T{mÀß³¿šÈð²&4?†ý¸B¶iæ¹>ÉÜ»•ŸºÃvëºñ¨]u !=€-xÿ[¥ÒÖ¬­³=øvQGõèjáþc k´u2»»•ëÛºfn-†3¸SÃiÙ¯ïã{K>Ty@¥swY1ù álŒÅ·ÛAÑà!¾-^_—ñ1NÞÚ>ªÃÜ`EQ;†zQ°=•œ“T:¤Nb>e‡ÏȈ㞼@É™á³wÈ×D¾%ò["‘ìL("ÿÑÕ´ )ÑFÙ¾”T*|à l“í뺫È- ÛöäõüÆ I´Ë ó¦y¶ÏnE¬Ë²4‚•¤±bØÌHìuaˇ÷*Þ•¸ãû¯ä>Á§¬íúýÊLî’rtЉqEþ]½‹> p ·¼T‰Ý‡bA­­å\êjæ^nMÐ@`¯!nXSÙÀìòøò=Ù$òA;òÛÏ[‡þ‹ÍfÍœ:¯Ú:š ,¬‘—6‡Û5‘—›=°×Rä­CÁÀ‡/>ïV§ýdhrvÈ÷|U¶Ý÷¼¯K6|éxA¼[ß ûÕ!3œ1èr_ژ芬»†"qW­Þ%uȱíÁ¤5…O;ß¡ý`VðÝ’Ùà·ÿ2ºhç.q©JÑ€´¡ø-ñ;"wàÇ×VÖË„Sˆø lÀº1ïDÂVß´«¢w•)©ü:2­…}N™öñ¶#HËî\Nçn8‰kuŒ¬Ñ;7N .+&½q²d¥ÜoÃQO0?[Rú?ÌìÀüHã“'9fp4ö6ĺŸŠu6k$¥>®2½jîÿ«hð›U«|ñÂ@GzYƒBkCÍÁ¯åéÐ(¶;Êún=ÎÓáh„u›@xq`‚~o[áK>½†˜¡“ϲSÿk¤Gþõ7†z žÑï†".ÏA§­=è¸$çepÙüö뜳¥ë¥7dTsg„åVÿx5ï­äã~Øô~hpP¬OÇÍ+C}xb¨ØP¿Ø,]GùÞî?°àØAÒç ì“ða’Å~ûs"Yhw“F/^¨‰’‘—È ±ß]T•ÏF¹Ì_ø7Ò¢$i£&¾xh³‰MW¬W%p±Jè [ô-€“¶mõª=1Wq\€Gx §Ú”f±Ø#ì=®{ob9‰»a¥Ü_±{ ú~o¨Ý¥M ¼9”WŽ1Êë=¹®È,¯µ“|~†ãWЮ©{£‡Áð3Š Œ5hYÑpmÒÀôÀO†ZM;¤ï^¶Œ5v²pñòWvv+ñÞÅGÇúZ`}ypejáÒKU~Ügq¦Jå•ݰ›Ð_ٖߊ]R±¶—ðïVÅÖ¿öøëj[â%*ê¯my·Qq¼ÔÔmù3ÁîQMd( Z¬ñMÈ•yð;Ï_ÉêãíãÃk1ëö‘LPS8uɼƒSÇA7Û3§6¾”t)¾9Ød|)9‡¿/ék¦1ʸu|go•>ÍprÕš°ê"––ÎŒ`ï—=C‹gFž¶èM[žbòË|[—¼ybzá£ãnÓVÜ­ovüžÂÍ?C¯” ñ„© b©“Y­^صŠÈ„}loŠ*X¿ ¿€%êÕCØOs^IÀ<ÅåB«y,¾ú—e¹Ä+Ìnóõ%*ìU³R­×¡:¬ò#«-B% p©k™ X ÂAµ+ò ðü$, )ÏáÁò#ÎPÏ?ôe˜íXzI䫆ú<>Pƒõÿ–rèSÿì%3–Ul5s8/\k„4‡³¶7™µNQz5R²,~á‡.+äq™—Íj”âé΋ª!ÊKCX ú Öâ/µ¢ aäùEQ$áÅ[©ÎjÍPÍ—b“F?K\óT Ñ6£Kòz>ö!5~Œ|­UÂ¥{*ÏȔٳ–òEdwæ6›V­ýäÖ*ÜTkÛäê$dcm›¶B> stream xœí\[wܶ~ß_Á·î6^7DÏÉCû4N­Ô•Ö'íqú ëÞèâHrc÷×wÀ fH—»Z®ÙŽ_F 0æòaf€Ío‰H¥JDøW“§».9¹™ÍÉî_+âúdòÛ$Ouø¯h ôÁEòl¥L|êm²8žˆÔû\¸’«Llû,qR¤&Y\L¦Élñ¢t¢m*,ôÉâpòvº7›ë4sÚL_Dj©$RßGêu¤Þ0ý^Fêy¤^‘9¬ÿ^üØÜ…S©Ô^ÖK Ì´Ñ©1=äÑL¤Nj%ÜôÉ}$ßÍæR¦:Ëõô|f UGG%Åw'ŸžÅ:ÄVÒá;ÖƒÙÜ¥B(í¡ƒþÖ M¦½ÂiËÍ(+$L¤4—"O•ðÉ\ªTÛ žû×èO‘úûFZÎM*­RDËJÂÒ•ïÑ22J륹}}Gm‘® }Ç}úf¦½joCßJ¤J« oT6ðÈàÏW“ÅŸß‚äɪDų\¿öÁ5Ê•úæòEc{êkh¬Ö.­íÚjà~¹_²ržAO=Óê´LtG¹¦LȤdÔ§™t©4*άMîŽqidÄI¤H’½‘µ€ŸI ­VMOÛ!”¥]ÁýTªrësð? '¼ þW7²6KÌ¿´% T„Nb|;+H` =L¦›ž¾[™5 õz6Ï‚1(?¬ƒ³0o°‹C*Áª÷)œþ˜å>ðgT.§?Ïæ&Ks+Ìô»(´—ñ3?;SS ,“=[†p"ê()v rðñ;ð BÖÅ8… @ÃRUöªmf‰¹xC08~ç-·\xÕYx1êw´WìZ)ÞŠ¬ò¢¬ðHåD'®À9£ÁO­Næ¶…ÅT¼œ“•@ƒ¼ˆB~­MWƒ ¶Ã"BÞEþ{Áv¬7†nŸâ ±‘7ÍË!Ï€Y¥ nàÉÿœÍ}&HhŽk•È€;³pøCg5ý¤™ÁI¯§O(qHذ®/YøÕPßŽÛøÐ•r°"ÛÁ]}Sökdê…­í—ÀåOH’#2º‰Ú©ñÄ­9¢%ÆC7ÏëC·ãͳ´‹êÁÈ^Gu¿ÁFÒ•¶û‘®^L¿q\âAz9ã0¡§+Pûì´§Ø·x—•H@ ¥©/8€!ÉéQÌõä1ÿ–ÎÍ…ØŒL2Iùò°N†5N5_ùä‚0;/pÈhpÞO¥Qj0k› Záz`׋ã¶×¹“¡/Ll0òáVÞ½aD=¨ë]-rÙÉÒ$ÆðŸ~ߤ/Æü\ Â »j‚Ôß]^MZœŒ û%.W¦E${ƒ$r ÙÊÞ¤×uúÈ’IH®ÂvʃԘ4Ïr’ ¾]9±ûIçö˜”Ó¾ÝHý‹Mñ¬M¥rã û¬’RV.ï pÓ(ßMm`V÷3Ý£ÌRk@e÷&ŒZ% x›N8F ¼_T‡½vß…G|ä/ehäò˜RBÞmm¦” !K‘Æ“.D(òï±ë›I¸¤Š vKvŠ.–è+çH!¹ä5õ«Hž"y†ä ’·H^±Ì>±|ß!ùÉc–˜KB‚CïÛ…™ÖÕ_ÝU©á]Û™ù’ßÉ,á\—páKR<(î³K$ THÏúf×.p‘BSQÚ%’) ãþŒ8Á×ÄNXxj_DZç}ó"’lq®4œ!–Ö¿ÞS£àKÝ¢{¹ÚvÑ}•²D™:Ñ…!UÔÑP`‹]¯"uØnêB›: èÂI"Gü p®/£% ]\°ä`duÓŽDbjÿ©?ÞmìSæÒÔo3ª®2vEêi¤D¤L¤ž0Aòª\2ÜŸåI–|ФBR0ÌÝ&á`ÙD³¿³J& êäsJ7už°|ù ”+õÊ\ýˆ«Xðär}©HéH}³þ5.Ûll„C¿tk‰¼7gã“&¢>RÝ#Ú!“[–?ÛÖíf¨„q»0 µ¾5àØÍÑÀ"Ùg #û†cPm} âÏ6Gޤ^.ŽÑl"Å&<'; Á3XÑS5ÂwŽ_ÏÉeçä`ážÔ¬H–`#w²õ#Sm÷Èû D~›Ÿ“‡®ê±sRõ'¢ão×Ù•½B’\v?g4¹ <úLðy¬ãêqÁsŸ!ìÄùñÚkÛíPÍüî>ÂÛ6À dåÜÄùt“´®sëÍãîõ³1°?Š*u.¾ÎZÏë¾óʺê_£làUYÛÆ™Í-e±¶ 2›ˆ£Uã€Ì}¾½ü{–ˆo ‘jÅ¡Ý<ö‹†0’Ä“ˆ„¯ÌðOEËüm&é{Îò#’-€ 6š×°Æ`Yñù+’mÍ‹3”çË‘l @û.Rø\l¸  ©T¤.I”³¹žVº Ï׉ÕÐ ¾|ÓEZØach­÷K4|FQØðWTãJU_˜=H8Æ=ôÂç];Ì×½Uâš2Ò¥¹û²c5þÍß-;ñÛ÷†%åÍÉçHØ„œîsË{3Eî­êXõ¯Nõy“ j”ãbT@Zçé–·VŦ±|ñ3¦¯à%|ÆÀÑßpļàZSÐZ7±QAe·D<+%hÄ‘INÅcÅ;¶ÿ3¶_Wzß‘á/°­ûÿ º[Zß½²[áçKë(‹ÈáÓÒ!:& x‰äs¶Cxa\Ó‡,còԈ蛼/:g—Fæà¿DA®ÿù‹`2Œ/š~$;š­ˆ5?F¥aÄÒ÷qmØâAu/7»«W6¿ƒµÌM¶Éû ÿîwS×¹û߯õÜãºô~±˜üþýI›ô³endstream endobj 50 0 obj 2625 endobj 54 0 obj <> stream xœí\ësÇÿ®¿â¾E—p«Ý™ÙÙ]§’*@@€Ž;å:鄬X/$$â<þ÷ôìcºg÷··w'dŒír5{=¯~÷tïFq”¨Qìþm€ý“µltx¹V~íü¥.×Þ­å‘vÿ”$¼2z°K•QaG»o×â¨(ò8«fMF6¥?é(K’Èä£Ý“µï×G㉎ýÙ¬?#0J3mÖ_z衇vjHY7ÆÆY”˜LO<*CRÒúƒ‡îyhfA󥔺™Ð÷1˜@pƒÁ NöÌ«øð©œÌIž˜HëçŒ0eðÁ¸ß}Ϥ`ÄOv<6µà½‡T¼ÓÁmAƒ¦hÐ\UŒ?ƒ{™§-ÝÉÁèTC, ~þ™‰<“"•'r‚¡b©w’Dh/§ÁbÍ ¿Œçöãtý«r¯šþkïF$&•û˜9;³ûðR(‰#X²ïxÔñ"26`¼ïæø9Ës0 ç¥æ°GÛòÐÚ3?>Pøˆm°­?Uɳõw0ø”Á¯|Äà—P1ƒOüf‡†ý «¢7›¹68Û‹»ÓëƒHxjÙ š%–äϪ®c±q"5åÊÿ^´ÄZáƒæÚ3‡:õ¨Øˆ9Ô}-jôO™Uu}9{æÁÊ4éÂ8WÌ͵ÛÝá_õXX?ˇñDQ°›”^x™àÈ~|ß‘U–;!L‚Šud3Ýx’×P¤^0øW$Ѱ$¥¨…MCýµw¡>=‡“=cð!ÔÓ-dp.ñ˜Áû0šÜ–›´qìä²¢’Ôc"]ä •°§‚LR¨8ÖØ¡x0ªæÕ¥ŠU›ÐéˆâæØð~aZwú©&«ïô)üŽ#›ØŒÌzä"É´ˆc•gQáBJGæœNeœ±k~Ý`ð!ê8Ž]tà?nŽUdóX)û鯫9É31ê¾@È—5L{õG+FDY­Èuûd”å:>™R*WîdD¨$1Êô~§Ø²ˆršÅÑr¢licã“ÈÆ…J\áBrCQä‘·WV¥žVÐe½FnJ zÜi½F^øÙÊ냒¨¯Ê¯Y’U+ÄdÍ“E–Ø,‘óÜ–Ú¢âˆÜ—"Q&û\˜Ò┸*M³—G„t´ovv=Î#›š¤äS¥y¦uͧj¹7Û(Ó‡Žô•L¡"úHlªb²C©Ïg6+c–e  v!žJ‰ä÷ÇÚÁUòØ®sÅrT…bYªfÛAµÃèN4錩óUR‹…µ¿‚JZúa‚Ù.Ÿñü؃‰ „G5 j#ׯ'Àa¡ØÌ1Ó¥ŽUOª‚}ÝyµC'x­Î*í?ƒŸÉ¹DV%0Y”ë¢1¨ÆE”å¦ô]È9rÒ‚}[_~<è=.y²³´®+’hfq‘8kád×ykZ¥i»¤’Ÿ¥M"ß»êÞ ;ç¯gðdâ8b²£q´SÄÝ=þˆ“VÌèz»Æ‡cÚ¦¶ÞX ÎK/K2”–" ²F´x•R£É¨YÕHØÍ=žÀÅ9ä6ЬL‹ŒÍ¢,VeZ䌒׎94óP7s¶ ]°tä«×r­˜j¿á@°Ã$gEú™Ô–¼Pæè¢D.œE̪ÊI¢rç‚›mìÁs@ˆ-œˆ§‚{R<¥( Êȯ¸„›ÌF&?†!õ$•“¢ª"R‰ /ŒJ™2 9"_çëïÃ!tX~XçƒõÝ3y䌿Æ=¹@cÄ ²kb ;¬ÜOØ®õ­…cYäÕG÷Åýe _Ü`+-H÷Ø?Õ€' {V¸Ö8÷™ÿ‡.IÀÓ"è7ž6O†zWM„4¢÷CÈz5 ß.®3ÊW³—švHÅ×°òºv—ùxžÓ/±"¥E«Ê6$ú}@i¨ÒLêA•þõ;ꆸz½¾šå.„¯¹’Ç]ÕuõÏu´M¸4,T½;Jý ¾ÒToû*÷\ìuÞÁ ö¼Ž…V]ˆˆÜS¹û¹U®[¦6|É­k®JØ×t>w@L¹¿ˆ›©Ä¤¢W‚Ëa¢ûà* ESü›Ê VUG6‘Ö¾Ž|Ä-¾¨ê%jU,Iv+¶&Ö}eu´×zWå›ÀºÚ© ×ðhtœŠîƒ@&{Ä«¡oF âRt ”Ý"®NáøÁÞKÞ–˜V¨ÂØ€˜t&範$kΚՕ̲W@ÌßׂPu«˜Èp“d_CDÐwæQ®[~/»^;6ÈÙv uèæFÛWZ5aÙ›.S?b@íªS(l3ȵš>iVÌ—g"üBòÜi^v®Wœ{›Âjy^ÕÈ®$ШÁj)nuX5R6_¸MÁf«ku]} y÷†ûNÑ^—éû¹es XÓé¸s`+=hšŸòfúÜ” 8RØ!÷…ä¿|ZT…˜â¥eÚdî=G2sß·®µ­‘ŽTf‚.¨ÁfËvì2Øå4õˆ•rFF/G¨wµ`¾×þPÒ| f9Á]JÓ6Ó]ð3ƒ"Úêªþàö‡áW BÞ*^¸Ž~l ŽºaS ŒÝ~Ä^ûÕ„Þ³‘lÆvØóX²9­3÷ ºæ@oæ-Â-¯77²TÈâ (°TÝc§¸OK²Eê¸Çþ8ÖjÏK=äØï “£!­E&8v¶üóÖxR¬?èµ@ª|g"-PX”­|ÄññÌ|ðµÄð“‚DÛ(]ñI‚O ž Œ ¿WÿI·¡¿~0¯Ißu5ö5é—I}”Û4œ¾Ö¯ øÂE9Š8I¡Õæß¬,ˆµë÷ º6a¦9?Ý~”Ù?¿ pIøKŸC‹»ùäà‰÷$ü^9på½”/jÛZ¿Ò4‹ô[|i…±²,‡/W³8M;Ó„KÚÚݧÇâü{±m†¦§ÞLj½à·¸ìeðÂ0â}@W}V'ßy¤Š‚“)ôŒ[>±Û3è¦À£ÀñììGI.¼¨ò$ˆÙm ûvEl ûc±÷×¼€w2ƒ]Ê}ôCË Ÿ:ø0‡c# ’ïÞß8ãQÝßB«Z€k‘®C„•v ×ÁÙ²|§Ùbt˜sHËisðºµyg:±â ?6â½ÿ³¥äØÔL!‚§¤8:m\ _ò`ë2Lžë@D®hoø¶A„0ØÒÂ@vVÇäºrÍi²Ä…@ Ãz!¬ØÎ5Ô,ÌÈc”’0?ðÅiÂ`FÚŸ’tŸ( RaðyjÛ畤|»=ŸØÂÛâ7çÍPG¹ê'?Àè\ÖÒ¢£<óájðN¯%, àI;›ÎÒ´çM‘0MÐJñ•ž´=M¼#ß!µ®ICÄë0åœ$ ÑY"±Åsúeù°h@8áy‡K.î”Ú¾„l¥ƒçrëM`c¸¾kÛßÖ»Ò¹6fð¯5ÁÁÜà}~ß_€ Œ©ˆ5‹µL°Ñ“àû3,C뻑¿WsýW¯Xce€bäÃ*åy´»öŠþýâ¶‹endstream endobj 55 0 obj 3482 endobj 61 0 obj <> stream xœí][sܶ~ׯØéKv3M€$H¶™Ì8¶“¸ãÄŽ¬4I>ÈZYv£[$Ùqòë ð‚s~ È]ÙV[Ç3Œ%.<8øÎào‹4r‘š?}áðtçÎ^¹8¾Úiª{_w…Ëãßvª$3ÿ5¼|xºør_7”Ù¢NjµØ±“&u]¥eÛ«X¨Bÿ».¥I^-öOwž-«Ý,•úßùò¾.&E™åËÚ’”¤2mTZ&"/ysV<°­N@iaK¯lé¬-‰:[^QñšŠTdÏRñ¨/JÞŠ/ago@N³T<¡â9ìŒÍ÷œï1‚õ°†ÅK8É+XDH=Ú¿öÿ¾£×]êu´³ÿi`忱¥Fx _Þ±%Ôöû(ÿ0V¸ <“}ä[k%S*)ÓR£ÆþzG»ÿïݾn× ‡©wXhÏ.Ë£-ÁãXêK[º²¥kŸ9Â$Üž=¢<ùçœÙ[8Äöp£ëü"ÚÃuìÝZ&WU]¹LnX)OeR–ªe¥g“Ù½,ë´vûv;ø rÞO! µ›píGV½}¬ÚÀW©9FÔ=ÏÍcÚÙÈÌXµc$‰Ê¶Dbö;[º xµ}zéÛ†ÄìG–½%,;³Ï6ÇÙ­X–‰u€®ßZ>xjK÷lé‰Ï%º´F|õ«ÃŸBŠ4)åG½… Šh¶èàµJʬçÚw©Í`öÜ·\0ŸQ‰ƒÈö¸°¥#‡Q? ÿÿ FýÂÿΞÖóÂ4iÜ:ÖŒÒl\ÈÞ¸J“¬P*5=k ²a1[y`+5!uˬ¨u©¯{µÊu)«Ó¿–"¯Yå¹mB}F}³a4 Ë$M…Rº‘y4“i¹<]íÊJÈ¿2:‡ÓÈCvF×1õ<õŒ«Î&³,‘Ùz>¿ƒ1€Çós‘™Í CÖ®ýï÷€Å#Q£gáBpj4Á"$Šõ–H¬ÿ Ôÿ‘ÙLeåÎ3Õ5| îV™šZtÐÝ ø'¦(òB0è S…âµ×TÅöžaÊ$/ªžc4]rÏkMl²,uuÙºJËÔó…lÛ+< ÈC…sXàƒ©dÊ#+:}"ö\sŽšçQ½¡Áën3¡Ã©I{Ý Up¯÷ÓÓ<é׎•j3ªe0±Áv%m:̾l.'ýö9éôÍ4©+«o²Iý¾ªÍîÉݨ\·ÏjÞ`V~…°¢…·:^jöÀä(ö¶Ïé H!ÛkÞña±ídõl§ bÃYqfMõ«gñÛRÝŠkµÓÝ"£ˆY¥,J`¢`“‹¬¹3ªt”¸ò2)óÌ®xÖ1T–HE¢ºc[:BýL°çØsóTÅ1 4µNæ•,¬ÍÔ0à|e„ÊÍ ¢;„Má-Üq‘m+¥¤‚z„E*Ú žÙ\ötmnh˜/^‰Âè‰9'\ 2±¢ÀVæ9ÀÔ#È C'‡Ñ#ø¶˜<{Ôü§Õn=á}ìïS8sˆ.XIhŒµf@‡â½Ž4â¾2„ s’c>A†Ÿ-<IQXõÚYh”ÖØJá BÉøà+PC?Ýj€Ø5k©lJôÉÛ2ž-æŒÙµŒMν5ÂR-”¶‘b‡ak_½ÿ/Ï ŠhE{ŽÐ’eÕ¦ø±Ã•ZÓy?ÞSJ |@Å}˜Çø9Ìd)o`î!Nøüq—Š÷aqÏ™¯(ôƦÌYSût¥ÒÔmÚ(¤¡œ.2l¦h>RWgN1Ç0Ý.PY±× j<ÄN‚D Fž•ʸšÌ̼ò=À¤º1[0´Hì>’9NÓ8âó¼É„¬;`ÿ'¶µ&dŽº&`cQÒG­Ëw?M#”NÓâI™°,bœØ8°Ñš`8†Ó iÛ"’Ñ_T–}0LêacÒ?àtîÁ™=æH"c4äóp¨û{ú8JbªݳAvÜß)…’ܲ.w0A?Ô…y,Þ “%7B_#¼™4›9à±±1]çæCŒä’‡Àaa ƈ€m' @ï-«RÑse Ö‚òÊÞz‘1^†ztr4NËÓ´Å1»2¢G#áÛ¿¾¡4°à@”*«àã¤cÖARºþ6Ƙ7N0G«j~¿k%U4BîûúËDæ‚DzÀa^^~³%Â!r¥øä˜B·éï¡YHQçím›gÇ´ÂŒ5?"5'EMž|ö2Ì0¡@WL“ؘìPj·p•&"Í@~)eÒà„æ¾wGÄ#`çyGS€Ò“DØè±Úò„ºÉ$â¨ç×ÃdÂz¦£ÿØbÈXáÒóWuH¹I HÌ`9Ä”.ÆXâ° fëÞÁ&{OI•–e ï‰$˜ØÍÜŒ€Ãxã`™…ñâîkyüiE|ÔKì9ðß`e;ŒEÞaŽrÔcÄ|7ÐQx£ù[£E(iÜÎö.J¬ ioS†9ÿ ïºÞ?-Ña¿xXnB—è"*ÝB‚Œ>`,—UÏœægôå²UƒªßùýƒÖNçñ½,†—ÏBBÖqÁ–"ÕÏ~Źãh¿s+6&A_ù¹-Ñe3G¶ô\ÌqžûÜ Â'?Ä<“÷`íwTüŠŠ©ø5 È=ž…a¡wz‰ž1ÆïLµhkñæLÖÙ(2f8(‚„ W¨ƒfànåE¬OYÒæ–h®Ë“ZzÏÍFô EÕÛ¶¦©`Τ‘ÃÞÌba½t^ïTÆuŸíã'B¨D*G=ô8x4 tÍÐapq~½5Ð"•9ƒv¶š!Kºk*²ð<ä\&JV}& ‰Eó}fo,1'$Í’:WÎý_q+D]Ç«.WÆœLEe’ôk>‡·P«hj® Ó¡°ÿ!v†õýË’š}º2—´åZEùeEµÎnêŠT9·a÷uä’Z‚ó30¦?@oדý¥œÂDSœpåKA_1Á0/MnC/K([ŒL<º-þ1éY?­éF½íòä€pË“ iT¢PP2ê­¦ÁlKÑø²gfúô’z$Óçý¤Üá¾@ ã‡é‡7¹2/+’8(3™ía~ÖÅ‚ 3‡)äsXiCšˆ÷Ó )lÿ.ï ÂÁŽ3µ¹ß‰]Û(}†+4L!ï~ýÃ&/×tÉEŸ4Tôßí¸³— ¡¥uN_u©sï«.Z Û¯ºÈÕnj®Í(µ JŒ£ ¨Ó4_fZº×íáýݬһ«QuY]GAd©Ñ4²fϘÌMIfüçÖ½Ù`IQ©æö:•Ô¥*ŒIA¬m šÉÕ9k‘Isz×V>ÖÓËR­/ë'ûŸïk­*Ðèú,TÝÈHÛÿ+êàUó)\uå"+,Eë…Æþ™œ*ó ªDã@!‚J)+3×åL4W‹dzaÒC*ÝKs ^ª:ÉrÕ;W¥~1c¢$*­esp§é¤Ê+§¼`ê¦"M¤ÌªŽúí#F–kDÊ{ò´µ—MYƒV{ЏÖëcŒö}ûp¼ Öl¯ºªTÖ?!´5îô±›†wû.©¬e£‚™—)ÒÚ! Ío¯%Ž^”F3K 3äm&ß8’¹jîÝ=E÷îNw)ú’apš„ÒB@ž'F‘1òí)ÉÁSàêî«—2°Ëe¸èáƒ?>ž¯y“äÚ9.ÏÜqڀˉ‹y‰{q;¡?tX—6/º©ý[(šPÕ†÷¸a6 ó‘FGªŒù“±xÑbŽtøÚØ ‹¹hŽ„¥Xq¸¡7«˜ŽðòvßwïØ^ …wÌãJZ`ýïñ4⋲ ž…¹Ò¨âyS¤®žÀ«`0y†9!ö»Çî^›‰s_á$éȶñ²Z]“üÀî3`©W®vNÆ÷ȵH>ŠEvðM¤¤õG]kUŒÇ­ÙzP>ÕœãÒÏá*BÝøÞôˆÞ5:HÙ_-3š :‚>©¦q„]Ä@8cüràiâán-‘[éÿHk¡M6¸õ¡iå_û`l|¬9|Ýï,¡Ã2>S©¦ßqƒÑƒ=ûWŸK–æD´çdKÐwϬû‹¾pr:pŽMù(ßp³ý9½'æ=bŸÛ8…Eæ^bŸÛ`ßã¸ê‹ƒÏZ.»£â9 Å— øú•3oð1"V¬¸kRæš×+çÙ‡ñçVð×Ö°þú þ~Ì5$Ý)lvAÅK¸$Ñc^ÁÖÁ%ñVBåä Þ›± ôq>嶪Dž´·@óf)L ¶¤wbKŠï5lý¦ ûäç«ØÀG ³~<â?´Ä£oÍíoD|¼ð‡r"suŠŒ^°ÌË2ÖŠÑ/«â·€fŒÙâPÒÈÞ täŽRóÖnôªN3ãS^}`aoT,¨ÈbÆ1 Gš¢üìPy\²Å¨ü<¶›ÿ¸e¤ÓI‹¸–>4ùÀѤìöá',G1ÚÏùRó;[¦rK¶X þn-nc°f$g;î6ûpë§&®Ÿ·l”H{ªG«)’ ³­dlÙùÙ>`p…5ÇW°Ù$f—ì—}„/ ÅkØ/fÖâœ~uìï|¯ÿüU…qendstream endobj 62 0 obj 4862 endobj 66 0 obj <> stream xœí[_sÛ6 ÷§ÐÛì]£ˆ”DI½Ý’v»ìº5K½§u·sl'éÇ®ã¤Ý>ýHÉ"@é'ÓNÚ^Û¹éB ‰ äm…B‘ù©ñ¬wx–—·½r88ûq ,/{o{y›å‡Ç³àh¨ ea¡‚áE/ ‹"²Š«Tª/Ò "Lò`8ëýÞq$õïI¨Á0Íâ¤b¡Ÿ-ô| IehT”…"É89¥¥J*‘‹$”G]U¢ˆûoœ8%0 ð‚À%sÈ‘xHàKˆË–sGñÒG>„9ä@²ù+[A™-!xGàpz¶?†?õ´Hm'/zÃoKyeõK6óÒB§úXá¹lCXT=(‘…¹cq·>™- ø¼ ØÅJ¼¢žƒÑZ¾‡gBXgÒø¿¶þø}*kÑ?Äa¤ ½eÍ" £HhA1p>ˆÂLHíl0ЃqªT÷¯ì÷‘…ôÖõqZô'vìzpé¡,•ÚÆ=‰F4ŸceýË5Ms’ñ/#"m~º(Ye"©W_b±\[®sšõÝ ³\ë¿â÷tÍ$e›X:¡PD!²ä©5±%€æ­±ÊVÉ*w‚¼Äï@¤^ø u íû‚S2ê ô ƒúêŠ67@+ ½øº_Ǻ²ÙÁÄUÖ–¡h %¶€Òe¸+(±Å”.™³‡õ³}­µÖÐŒ<ÂcsÝùdÃp/  Ja¤»“’9Nø¾Z&z¾ÒØ ]·Œ°'öFØ6ÂZk_Ÿ1VöWZccsã_ñ]´Î‚C ½x<óÖkx¿7BžÀÎÛøm+·­4Va¾¾mé‹Q*)e.û¡c!™ôãŽq_„¹æ"Ji«"Œe=W¢2åë(TQ!… %“<É8` 5©Î„¤Œs#‹²0\4$Äí貄ã4ŽRäÓ<×™ÄÐ"ûY026õ]…GUZ‘ÙU5ªU¯>’Zè¯ûåγ¸ˆrG Ã’wª„t†_ÖøISîŽûqª-9SFcšhø—›‹›©”Î9•diõÔB,Å]€¬{Hpξ¢¹I^¦ÊSBew²~lòæ+˜ÇéÖ 9µ7¶ßr0‹ Q –Ölg§ôœí‰®+škÚºX&úÄî+~«h¬>V©âs(šµ¸bV-ÔˆÙ,I¯^ÆEÿoý½Ð»ŽÍFµ«'2üòò¦´*)U(R¥­h8Ñn~m9±}†4)[[J-鸶¬¶ž-.©BGxª$Í }(Ôƒk¹èÿ\SÚë%q…ÛÆªÂ–ÊȰ}ŽI†4™#C .ÆÕñŠÆ¥vƒ/Ü@_˜™SK/F‡ÔsÈo’ÙÔES{ÌþJp“öJ„›µÑ$a^>Þ•FÃPß7QR™e:Be†ø IuôÊ‹à` ºÉSÈp‡¢TCøÐ= f-®8ëPÜ1µƒX²]‘Bç:çx),2VL·Ž_!ЉØÖ*“HT^ 'ôœðÿs‘–üsmØ2á:ñev{\õ¶R~7çÖ%”“ÇÏñª™È®lxRq,MuGke¾Wá*‘&Ž1&ÜÔ6 †;†ÑGL†c$sY¼tÆŒ÷ƒÔ}ådvðf‡y»b:sóÚ3cR|¯ÒšƒZŪÜåëNYÈ“Xœb éqjÝaâ€Âô×|µô?%úÎAÌžT}1 ŒÃ}Z­.ŽÄÚ*à3¨ÚüÆFïd³Ž‡‰ÍµŸ[ðÄñïöœÆðâ5ƒàÞíVàB·ÓSÏ‘]íoúÁY·ZŽ™ì@î¹!_ÃÛ)¾|²Ûé’á\,Â"\)û8oÄlžwœcðÔq¶ƒè­L¹Tí269lQXL£‡PXKlô’pb&ÍK¨g<1zKñúèIé ¤ì ½’ÙeÓ8tL 8o ¼q1ûöeSÞ 1?·b:kABÌFÛ~‹™yR¨ác_xFàw S¿gŒ[Æ‘gV4ôJ¶¼KSƒOÈÌx˜¼ï¡Iáà‰ƒ=ŽIÞ¶ˆO¡¦l{Ýx‹·à³ *8’0‘ŸC|úSó-*H· Û„­ØRI—ª­~\?`·/ÇÛõÍs®|ŠôV‘>ll‘Ș|·oñÚ·x¹–²ïìÚwví;»ÛÙåƒ¾èÆ®®rù¾±«+ |©í ûžš¦éí{j>¹nÓSc®yò°žšöÔ<£>šë–”,Ûª¥†Úa¦kÎyá´ÎÌ BZ¤Êœ½DG/#¢»f|’HH=7wÔ³ªz^d–)‡…Óßù¼+Wññ«¦·Ø|ï[ÃÛÜüG:+ cÒEè€>„¾«®9ÞWQü¦‰ËºÕšçNœÙm:ÛúÊ#©ëM»GR'>(’:êü¢"é.à ”Ò #~õóVvépø” M×ó×ù±ÒvëH²&½¥ó×½b{çÿß8íñχ½_õÏM–Âkendstream endobj 67 0 obj 2265 endobj 71 0 obj <> stream xœí\MsÛ6½ëWðV©S±$‚d§ÓCÚ´ãNÚ¤©zJ;Yvl7¶äZN¿~}‰]¢¤$NÚQœÃšÄ.€Ý‡ô°ÖïQ§*JÜO#,nFŸ>Ï£‹õ¨z=ÿ¦î.F¿ŠX»Õ)/n¢G3RT:*ãÒF³—£$.Ë"É7VÓÈfô{™EyšÆ¦ˆf7£ãh2Õ‰¢ß͸ 1ÎrmÆjIY÷>-R«r¼ôïW^м´ðÒ¥—æ^bÝ /{iôg“t qŸûÀ!RùŽ¡gEŠî0ì? ÄÐ.á(EÛW°ÁßpŬ*ИŒ>Ú²¡0Ü;çöi£6­³ûYHN(âãì @Ÿ;ckè! œðÉ Ýø„ ;#ûâá@¤ ®¬¶€ð'CÆî®Ò"éã§ÿÐMÁ‘l—oðvÉͧ“,6*³E‹Û$?U¤„2Î RMWL/b*i ͹‚<Ö><èšÉKÈÛ ¯0o7‡®$$5¹©c²Üd.¡r°Í°:$pfTGëO9æ¾,òÓ3žâ¦FP¿•ò™B+ÙSŸ ©ÏÎ^Óù ÖI°Gê³÷„ñn©Ï¾ÅG4 È‘…â‘=r ¡oÜçÃ…gîs{z9RŸï#|»RŸ}éêʪ÷ÇÄ~ˆÄç=œ>¼ã€w%|Z=aQœ¿žîáb|LOñ±í r ;¾‡mßœ’Ý=q1é4\©é#ta+€Ì`¤¨z|VUü›hv©€lT«úÆ2Ί¢&"|Y$T“ÝÈzIhDÖ2JƒR1ê«q|RJf6UM§YVæ¢Þ±)r46Íõ×S䨓2¶\ã¸È¢úšÚ:P,¸¡ £Æêƒ\EÓ÷„9g¾DWÌ©§ŠqC*Qv ê‹EÍ`›é|¦ÜæÌüæ 6Ô|[Ñ[X±\38¢ÎW$2Gi® ²°b)y'ºeŽÌla0 ¿¥K6¶&ËKÚеdÔh¾#~+$Rµ[8ä7àΠ÷0q‡<èA .WPާО¤€ÓKÈ•îC=/aƒ¿Úî×6³ `TNÍ<`‚åág)´Îó†ÎŒ’M«Üö£òœ2[¾¹<Ó¤˜‚°|Ñå¿[9íæ/ÁXWi"ÏÓ„ˆ9'ONoÛÊv·,)‰*ù|u鬪tlíuY³ y}Ò¼äˆ`lhl–Ѷòs»ÎV8ˆsn{‰Wƒñö_€¸Šm|eÐr@—$ï»ÊðmÈ–5JËØ$ŠÓÚ¦3ší©ôJ ¯XÄù«áõ5x#ŒñÈþ˜dιá¢ZîÑoà}o·åèÀoo{8<¿›™6‘hß0øEïû|þNC\vn™Ü{Ötnûn™2wÿšeõ\褗çö*ƒìp¿äCÞ)­÷ÕÑñ†[Á;ÖÔÝ]Éñ©çéxyÔU{ÈË#ÄxïŽÊ‡½;(l=^µüý¦—GôÇ»#'¾qý<„òçIõdýÿÁ%Òñòèƒ Û—G(]=àÝ®–Ý ù?ÈícØ`ò°&õM áûP!‰À4”÷HÇ{¤–6º2¼Èyáÿ Åí³úP*Ç'Ùœ^}Øÿû€ä CÓ^_àòØswâã¼Wˆ1‰ÚÔCXû½ôÔ 鋸ֆ6[Óæþwúª–y‹çüR1æc:eÇ²÷Ó„[h ‹ÅÏVg„2ߪô.ÈÎïü#{°,uÑÄéñlôýü H endstream endobj 72 0 obj 2336 endobj 76 0 obj <> stream xœí[[wÛ6 ~÷¯ÐÛäEáM¤¸íì¡Y·nÍÖ,õ¶s–öÁ±“4m'NÒ®ýõ#%‹mØò-­×¦éÃ'ŠIH@¾NXÆEÂü_ zƒÖî¡IÎnZesrøóŒÎZ×­"“þ_Ù€qo<ê8B!›YtN[,³¶`¦âÊ»g›'†óLIgÐJ“vçuË= ÷¸ßê|}äZv$®ƒJ/Ìr#U: èvŒ„.{VÝ©×ÁÛ^@¯: ¨³O‚‡÷®Q(n²Âà÷£ê=·²fåáÀ!@Ô÷`Br‘f†8t^ì“}oÈé ‰UGö²ó«×”"ÕYƒјwM2B}OÉW¤4®æK­°²E÷üÕ¬õ> ËyŽ6F¿´Ð3¢XÞˆ@é¶Ø.ÿ}°KÐÚî¡M¸ˆ¼N!'¼Ž–6“Ú{£T´wX¦……H3%çJ¨TÎh×훎 /¥­'¥Ó§ížifOÿv­™È νÃ(yªX™tÇCk¤eEºï ]?}Ç8/ ×ÏÑÈ—¡õ„æÕÁd5¯oÌm®ÌO¶s¿@à ·¢{óð½™p‘¢‰xŽ,+ '#XÊXDNé‹ö¸³*@mœ7 -”K!Xø­è׬3ÃÝò^µ™^('%É\k&ÝÚ”k”V9éÔèr’D3îEšgŒqa½Í¹}o¸²ˆhà7YÆŒ¬©¤`ÑHN^ 7„Nå'Bn½P˜È °v¯4!§œÜÉ/?mËŒië6´—_ݱ8…ÙŽÚÆÀx§x ðàII%óBŽÅQÏ´žäû¶ó(¼´Ðx‰]’²ê%u®ÓcrĦ ú_;殇ų¼ñmŒ é•è×~iFBŠÌhë̦ÓoÅC…®ˆ-=¤±JÞB Õ»¶ÍL¡ búmÅT2>6îzZž.~n•GG$¿eˆqåq@"†ì´ÐS¢ŸBñ“€þ!# oȨ@‡S2* ²k2î£(Vô¾'G£cê 2€O~˜y¸gõ䟑zĪêÉ—VO?\ü÷qNn¹¨5·x¼è¯‘è†Æ»€Àç#•P1Ä{ <˜úså=¡u νCÏsw™ð.SÇÓÏ&&ž·ý:Š<â5éœ=¿“¶Ó¹©Ï;Â;bg‚âRͤЫWàã›’ zVºÀ¤#êVqÑ\ŒeåÅ:+”*Äk^T v]À"¡nÑpL]B*ߢ™úf¾o‘ÞpjôaqN=Ò HHûäBn&}ÁB7ðÇàÁ£ysáÄhÄ @2@ÇT…۲幬aµôbè‹ÔÀݦËQ—l½#Ér`$ñ3€çM/t—žÐ\ŸŠžËi¾úž7É»ñ:‹wEr Ó ´ÕöIq^’ò¦£3²„.AÖù1ˆë!øÇ‘àç;€&Á÷IQ¼%×ß#û&¤Øè D“-.¶ˆÌßÄj¼ð9ÀC€ß4@ÜÚËï…X„GÙÚ@›=ù1©“­ÐºÌÕß„Úà° üm¥=%•j2€)´9P‡7¤”V´ ´ƒ»Äîï8¾åGU”ɸ#Ïš“lºÔ1ö¼Ê|˜\àÜ :vIÞ“'ÔïðícÞéVËÈ€9sìÁ´DœFs.}æŽÎ¹eÊÆËM²yë/:Ú²(½¹r¤(¡3qC['¹DÑ£*š»ûÉ)Å!©CÄñ’JeúOÛb:¯î-ãùÉôE ô¶ÙBPên†…ÔÚÇ™±ÈÈç Œ6ð^°Äলáz)§ùÊ”c—œÁ-t@jŸÚ‚y™äÆdÖš2q§¤Î &ÊĪïû£–¬2J®5"¤B$‰EÜ”'šºû/Rí_­Ôa6[ê8(±Ò¦Úuù7JJ§)–£úÃY@#Of¹¹yÕ8UéÁõ8,p‰ãdF1cJ¨yXòÎým~é’ÈAI+„,0÷MVGœugF¯Z»E›qϨ×CId·hOnµ*i³ÉêI]ð8e’=ð£ûàúË(„'sJQñ…Ê]‡_¨ò­ãâ‹Û´ïí’ Pˆ9Ɖ¬Òw*™9s‰\§ÍóÍÖb(ÅvIKU`ÆsY¬#ÝÿP€©ÜøæË/ÝG!†¸ØmU¦ÿãb¨ÔÆýéi™ŠÌg«§µ+2_V9æ‹ ¾Ó‡V7•æ2 q _¡L3J}|E‘€^åÜb*÷R¬Qµ²V£°k 3£µûk5 ÿÑÇ>­ú{ÁÆ{LqJ†ÚÖü㺥­eävGLt–Á\ÂJh ­¤‡’ßV•ü Ð÷W@?´ ~½’2‘·MÒ¦—‡8¼#¥‚K;+~ˆ õœOYkâÑÉz¥²áCMw jº¥Ü­RÛ¢¥Ü>óÏ ÈÀ6«7×&¶­tn¤· òŽ—¤àÖ?I H´ Ø€ÆÔš^*îàAe›ô‚èˆÙÐé}ÔÛøÓÔJ_>Êäz›ì0ȶ[¼µÖ;®ÿ‰:éÓ;gBd‹ ‘´ûž>QéQ@ÑðÆ~!9$ÞÊ€^Fú!”‚lBà%üŸ¾a‘é7óÍaÖVD£Ñ÷vF¶6æ^%–äÜ/ \Á]ý^4cÅKøŠi扭—>0¾i’cÃ8È4¦y×Üvk˜ó­ÕãNë÷÷4„Xendstream endobj 77 0 obj 2073 endobj 81 0 obj <> stream xœí\Ys7~ׯ˜·%·Ì€Á\Γe×›d啹•T%©%QGl‘´(Ù«üúæ@7f¾!HYrâT¬·0@軀ÞG"–*ö§N®ööòè|½W5GGÿh€ëó½÷{EœØU‡O®¢çS3PʨŒË,šží‰¸, ‘×Xe”¥æ÷2r)âÜt¸Úûq4Odç2S£‹±i–J磹ƒ"%i–‰d´r×ãI !U9ZºÆsôyæ¯Ì<Ê´*eA¥c‘'yó=Q"øn¬-Xêf!U#[ÉÉxÂ;-©Sõå­™ª4ø•æƒØT·¨³btCß»Xf|=“ÜtËS5ºt³²õzžHiú ÇͶ±¶¸¿¢Oõ “,Íø€ B3LKͤlaiÜÙBó½%‡‚K7j1þyú¯=•ÈX©‘“é© oñn-l£Æ©%±P>¡´“;ȹd3­iQ˜1§þZÓ¼l˜åh׌¿!üs8žMpKXÁKÈJÖá”0, ü8.ã¼Ðf jdgË]Ây=†»Œ7]9i†¹)ذA9i™¿³œ0¡¬@Cm J¢Í+™‘†EyÙỂ‰¬9Gåñ\å¹AÛ±­‹Xgid÷’ª™‹f]2óÔâÖ}§žL9£qGã:ßiÙ²5Mä c(âÙfÍ‚rîp<ÉâL§yÉ%qÅσŽ2¥Zq&.8ë]ו#f}–©1ù®#ð-a'3V‰œ¡µÎ“Väª!Ò!;Ɔý4¢q’Æ ºqߌÃiQJN߯F ¤éè§1·[À·.ž±]Î!™†Öå&¹ÒTòa‹±ùn,˜g8±9õäÙíŒ-¬Gl&&û½P"3Â]ÇF¥¤òŽ¿5·+¥‰Jf´‘™D¶2Ó˜†LH³¦ÚeE\ í™"f€º‘FÇ>9c'ûþÕbÆs)-*Ø“LÕ@\äDû¼cCš$ÖªÌGwµ|ª‡ÆXtÇ-Z³qVØÒsÿ¨ŒŒ`ñ´H:!h–R7!¨ODœ)¥ e‘™EJ³\=JÚ‹ñ¤Œ ƒÅ|¢²2NtV  08e1z66¢XZÇ,›®EVØ ]Œö¶iž"1¤ ql\ÄÆ½¨æ+ŠÌF0Ôe=Ï|cáÒxGQÁJEÙv‘F ¼™ì c3Py­êÖLgîf P¥ªl[‹Ü.7΄aÃg Vµ]óÂùƒ™3N”ež4ýu¯Q›o÷¦ß%¡`=ŸYÚÒ76Øö”hLH­f;§ç!ŒÓÏV¶¶´ž×-½¢%ìÃ( ›Ô`§Î\^>é%LpPËœŽˆ#ä^\R§ˆªQ“dh“°*J2Üø†¨VšË‚û)0¶°=0pI‡ó±žIn7iŒ¦‘ü‚íìœûtV8ÛŒ³:`ÍY`ÂcŒzâ{:å®6Va›XÂQ]¶ÔN{Ñí[åÇ’ÚªNõæèXù(ZÑñè䥙Ñ-å¹rÒ ²ÌiCÛ4CÌäôjuªÑWC6×=˜’Ü@éÀ™ÎoáqHw #Ô£ZûrÃq&Aæ{"HÂÍRÛ„¾½qâõƒq³£ ÀøűO ˜o¡JÈÐ\6Ñ¡‰ŽÊ>.àFvÉš^4›.´Ñ)³Õ×nç- ´¿}·xLð%— ¤L[æµF"è—/Ú$þn¬ÊXÅV`X¢S`Ÿ:-+v7éÝâÄ&‹\u˜yTjs oÜŽ´bÁ Ó˜ÿSy‚ôŸ¬CÏÿ(—x ä É &,_TaïÙ€´9pÝÛèV¡S6ÁB¡i®³®ÌZ\ÿ³nª÷UœÆ¡Ûke}I5nªä)·¡ýKî}ç^×"Ò8M]É´$¬ Oh^MÁÅ88:`뺀 Çš~ .Ø@05ñƒŠ€ÚîTláÑPå”1ß‹1êíq³E¡6_(ΤDþÅ6³l-üIåzÿ(I#(MoKÝyC(3ë‚?•Yn(Û»ùi)„Ãó¸´Wíj ã9ôèkëqE’§VŠÛ~K׸ª!•0èŽ!hÐË±Š³Â¾ç»¤Æ5ëûvhœ•W´A&Bsذy½´tˆÓ$mÿHV&iœÉêv ÉÕ¯@KU½½vu¹ò߯½“=¢÷®|¨±¡_·Osmø@ÍK÷uåAÍkÜêÚgiŸ“Ev/iaop¾¤g¬— iûvÃkX÷ì¶ÝMêâžb¥F&ÄÑ=%ûÞÉ5 õ€—Ûx±wùD‚þåßö.,ÅtÞ àÀŸ ÙåÏÕü7€×Ç"øFMïñœ½Ý}ß ¿û²õÝ_)»m…^Q16õ(‡.üVI¾¼ ¿¢ÌRÝ^º¨ùÒ\ø­iô˜~ÉŸÿ"-ê÷×…ß?ø…ßmùׅ߇âü#\ø=˜îýÇüüYßendstream endobj 82 0 obj 3241 endobj 86 0 obj <> stream xœå[msÛ6þ®_ÁoGÞD ‚ ÙÞÜL¦mÚ´iu®½´s£XJês,¹‘ÕLòë "vI>(Kviœ¯A`ì.û†ßƒ$2HìOœ]ŽžæÁëÕ¨jN¿Üo_~qjÿU >» N&f A—:˜¼%qYI^cÎÌßeä"‰sÓárÑäÿ#!â¢&OG“¾'ÑXè8Z†¿E¦Ÿ*ç ”fZ'ixe,‹<áOѸŒ“D&2®«@ƒVÑ87=…ÖáºúžÊ$¯ ¸$ðm4Îì0Y†×mdùµY»0CT¹Y{5vN`ɼøuòµ!“L™D’źP†F“ÙèExÓ8ËS>3øÌ'eð=s“H'Iƒ‘ZªØìI78Ø¢Îì"ÙV. Ri‰%98¥ gÆ5Æ4¹Þ -ã" Ìo;§ce&瑲‹+•݇Ù’§-ö½qßç­Ý7]çpË;Ùë@ÌÞj@ý·ÒÊä‡õM m¦BÂ$i¥°êõI5*d¾á‰ùõ®lÑj!J¤ˆ•2;näiê KÍ7ÔvŒNòXT´wør£Îôa8¦³ºƒ(S#¤¼„àœÀ×®Pnbˆ¨ìî+˜?$¢Å©[íOzä ç{ÐB¸Q²5JBņk}²u60…­ë;°§Ït²Çž 7*°gGäqÑê:ó­žµ¾„Û{AÆ÷·`ÿƒ™úøFLõ­®.a+£ÅpY;ea/œö š´×¿„X0!›ww½Ç ˜8è³# [òÚ·¯ `+Ã0…´º8ä\<ÿ¨È²‚ÃÎà0$n¹!bô¥E…߃¯óÓ§T:œRK!ðYzA|o¤xð¹ƒFöëúâv xèeÒ"ʤð¹1žK ‰Ú·­ïzTÙÒúJÒkn`²ÜhÍ-p É‚ÏåÄË&žù¨ùâe¼c²=Ã|’é¿ü£÷á·ß𽶆ýüˆÀçžø/ÿfˆ£.m;$ÝvˆÅ»!½7š¤ž÷”ãˇMÁ¤õý}àU>A^]Óœ‚c0ên5 R+’SœÝøüëØ§‡³ê.rÞ(D·F›§CT¯CÇ(÷TÞg>*3dØ´¹Ìªü¶œ- :¤óöí.“¤û®|ÇuŸ V¡3HL/^v^ñic§ø"[À‰ñ0òdËßc°£ÂÀ‹·Ÿ† l‰á{ð '³Cnähß¹? £+W¬ãÞwɽ‡ÇJ—9\ÿL¡~^³Àwo`g’ö+H¯:xBàCŸÁ¾7²À‡k‘Ý®í %úÄAýذjl:ýê9¾`Ë¡A‘£Æf[Ȧ°>¡=‰o帄´JÍw‡áq”Ɖ. XŠ„K—?bA7c¿O´¨R0iVn²`¶íͦYžI–°Yð„N=¦HC˜¨êf”¦ÜûôŠ2@KžÓr ؤ·tÅÏfÖwQç…Š&AT#ɘˆgDÂn¦ ÿÜ Þ[-{m=>&tšËWPNÙ±Áfï ‚¬¶³¦`XO%õDž-—ryÄŽk@½)øz ¾ž9è7‘ÌÚŒx9à›‡ÚX_¯xÃ8Ë>vðð{Íw‘xÁÄCEAc Äå.ÓöF&Ξê§û“ÝéþÐÑ,M>Ec˜%%Ìth7wU3NÞ³¾/I³¢6ù+_:jå…jJ.:åü²A7L«ª¡ÁÄq%ËûHXÕ'ÃO S¯Ü!Õ™¶z ™uÛúѶñ´¬ï>± )³X¥YSÆÒ*ÑpK˜A&Ô“i‘)¾„]ÂPßè „Y;¥iq] ëknk•好¨Zs?!ð!Àúb ÷¬TbORÚBvF­¸Ä‡Ìޏ†Á&fx¸†+«K‡Róß©‰20RʋĊ´S$–&E¥D/ƒ9‰µ”²™SaĶr a»´J¥0XD¥Gu§J[­#b”²Ò:fZ¥s³ºy¡ {‹Aø¡E˜öTmÁ’°Þc@#¿°#M£5Ýh­K×zÕ‚ÌÒ¢R±—–H£o?§eœ3l+ÖýbËP$3ëÎÓ2)ªíï0¤ú™mdž¾ª>ª€º}KeŸ,®Z•}T†µ¾Zêÿ¥³Ç¯˜þg*Òiu²ö™pwõ;ë¿h‹5+[C¥ƒ5Å®â?©«²¹ÎeUuxÉkÀºÆ}©˜ê7ÈÔ¨v’æÆó/áYuZ·Ò-m UÃfi½êG§pÜøÚ±%‹L¾½®Õ¼Pq‘)RóèRÃÊŸ)dF:®ÆÝ]åßAŸ-9pÃi)X]^T‡Û\Ë­›­µu)"j5±ë¹åNëˆGuSbw˜-†ía½…òÂàSÑ:6è*íí+3b݈zecÖvŒÓ¤læ™&mÎ*«¢*KZæ¹óªhU¥æàè4×ö¨EF–ã)Ò&¬v˜ÕQfc…p䃺D“•Ív¼É×¥´õ„ûˆRÙ?;hú=ž0%.¾³5)§!‰Bìðbc =“sß0|]Œ çšXð»øƒóƒGgÃÇÿ€OæÇ¢„!U4QLó)ᕘñ¸JeŸt2’Où8B@øP Ñ—;Xz¯ó bÀBÞ?Nµà$^o/¾qË\E9~J4ì:¨÷«r #–pŠ}Ê6ŽÈÕ­¥x–ô˜Âm¼©dDYÛ¶1TØ´ýj‚qÝ•™"·([¤ðøHÞfùïyô0äÒâèïÚ7 ‹ÓŸ˜âZÖá3'dÕ‚•È8Ïݯ¯úâ´é0ÞÜBÍ£¬nTVí±Ý`ç½ò=·=¸snÁ ÜáYÌ„îìgŠ2ž gv÷åÁ]Ç d`y;‚ö7¥b߆qU/AÔp,¹›8Ä8#°k+رÛImyì Z»€ß§pÙ?Pp•¹«LñnìyF«eö¢e,…ä=ñü&Ô^ªwµëªt,„‹up' /ˆê¬ÞHúŽC‘ä©[ÙÌ{f"‘Q_M­ßXÁ9±™ö<)EǯN k¡›× /˜ÛÜŠÆ[õ#¯ÜÏXâö2›Œ½0ÔÏeá^£¶ÛÜ»3woÞ£:æu#~=Hõä'ðƒÔýë þ>Sý‰ý¾mgV{èËTísŽø2Õó,Vòmöy¶Ç¦n«¤iAœDGõ!ƒ1­(ôhZù ÈX8WOìS$ŒÍùëî¦OF?˜Ÿ?=ìv³endstream endobj 87 0 obj 2506 endobj 91 0 obj <> stream xœí[Ysܸ~Ÿ_Á·ŒR+ AÒI¥*’•”c'Îʳ9ÊÙJ4c¯"ë°Žu9¿>t“üFàhF^¹bÛm º~}äÇD¤R%Âÿm‰“óÉÞQž¼¿™TÃÉÑâúýäã¤HµÿS púä<ÙŸ9F¥“2-m2{7iY"¯¥ÊÄfîÿe–äR¦¦Hfç“·ÓdgW åþo¦GŽL³\›é?õ ò ‘GDþ–HIdFäï˜à±!`•kƒ*` D£áœÏ „1êÏ8¤²%˜Õ~~ JËÇk 9Ŝֶ¡¨/ipxaˆ³êÇö_N}vCG£†åu ({tu:¶pÇ:E¤žþ ƒøI e& çë'¡œ±Q°§“ƒ€$…Aò³?¯Ð”£Âï¦ÃÜ—BçÄ¡sAŒÊe~нŒyï)vÆlç`‰H¬Cué—Ó ®ºy¶áÓëéwP.ÄpúÛ´a‚1ê¿(î_EÑ€ƒ+ŽqŸîcÍ’µaÿJ¡yѲ –7p‡Ä¼þ " /ˆÜ#ò5œû R||›»¨õ‡@½ ”Ô³@‘%g€ú1â±#„M[ý­Bv„Íáì¡‹ß;’2œúJå•ã~ù•WU¦ZUîèTØÒ¡à¤f©Ò.#/w\s'•+ˆØ`âuf­ÐÓŸÂïó@9Stkè¬tÛŽ9/ÍÝPž)¡Ô¥qýÏZ‰Ü×$O¡¹tšêt”ó_æÄÚÿé]%*—¦Ý}5‹màCzI«~Ú)Ó¼p&QË{ÖÉZ4+è:é®íE¿ Öv ¨ËÁØÀj·I=Fxò4Næ+¸Ö½$›€‹¬9`Ä¡ÖÔQпê¸èÍÁ¯·àד@ý(2ƒEWÑ#3N—W]67Úü=ð”e"x|2‹e()"(âã3¶A\O^ÀmclT׬£]ߥ>y.}•Xº ¬\˜!¶ª@±(¸ù–"ãñ°ÈI‘—Îåê Ëâ1KŒÍ=¦¨{GØâŒ\®+•‹Ön÷õc[à´qÓʹ·•Tä:g’Øà%\ôóŽôOMC’˜ÐÛŠÔ6³> ´«®Ú?zl¼,›û_Ø›RYjt–ˆd¶˜¼åSÙƒ/ êÅ¬Ì ßÂ}ÆP§ñ–ªӲUr¥ÍÌŠ¸übSk²¼ôiª³ö "÷ ›‹”0ý Þ€½'鎰s0ÉŠ‡[hla&—‘wpgi-A ‚„S±k)„¡ûûÒôîï $Õ彫•Ej¥Í]øKwvMš•Bør9OËÚOvuáBŽ?‚Q©-„R^mçÚó”R‹*×¹Õr!læ†uj3¡¼5¡ _= ­ü™RK-™¬„¦>‹1þ6÷¬Þ`Vb.¾¾v›ÒŽú¹fÏlYv–mÄæY,ççüˆB÷Ô…N‹‚c¨”*”ÇÐéC:ǯz8.}X.œYå![¦ÚX·ejE©äô%‘²‘Q˜¢Cï1yžÓU—…ÕœS°Ù£Æy@œs6庢]xY«U]Šîø)³¨ÄdVªª2wÙG‹¼Áx¸èójRè ÄéI5j¨§ŸuYË4+r]:†Í4x9Ý´OíI’XM1Ìš+ÞgѶp±Â«Ó1ÍþÓO¤/Cß#C&”¡o8±ÿù åÑÓA/Q61ÂOìgIØú¸€ ]RÏ•a ˜M™§iÇ"sY€>©&{t«´K’VSÄË_ÆÒí<¤Û3zêÁN«$zƒ.¥"¶l'–ÕÔ1dÀùë êÌ\  ŠS Ë«,AS2mìäíû2p?ú¯z‡»«Àt¶ø)YãLÔª@¯Úì³Zî_ãx»-\»Mºh¼«ßÛûÏ‚èVyÆ ø†¢›æ—ºó^VõðÒjíEИë;Ü‘âà¶§16|f÷ öX¾bG¢¸?ƒB£2úÌt;zü;àx$Š®ñèå:i|8¢6ð+~óKÞ ÜÓ¡ÁÉ ÄJɯI VƒP-­e·ELjƒ>“Z‘p6ˆSÏ13Ú+ÞåŠJ^åE  neæºFÔeæÊŒ±)Z! ¥¦]aDÆ@ûtfmÜx+…®Àɳ†/äßñ¦ðŠÈC"«=û•‡û.RW¦æíŠ)ì‰é[ZŸ6UsŠ|š|ih¼†ªXR6E[5K®Ôë°Ëº"E_ñFÁºª¾X§ì‚ŠñGµ(1¼_Cr¶o_S‹ì„åxölçþ]-ߤÑY‡ëWp›ËŠ€mëÑ [už‹R®Tf]ÈämÍ“ÕÕ„Ê]ÇXæUa¢µ+÷Kí UÃAç_ÝK„A˜9a†"Ê2üº@Ahx¾´"F±ÁïéðŸõ{…*?ï¨2UÒµWl—¬Öf²>켬ïÝâô•=ÑŽ„ Mãá;Ê È¦h4#€,VŽ¿¬ÇE|–u^×úF¦²èÕúUÑÎü¡û¤mÕÝh@5ÞÐ)‰©ý’¸s„;ð:BëÙI´G%Є2•±×7ì¾¥tú›@ö`\Åýãáeh0+6Ÿ÷ý/äuÖk㓾æºÎ/` •Oï¹®ƒ] U¤t¡s¨ögÌËa7ë4Pÿ/iw’¢o²¯üz£-Þ"3PÅ~0¨¶üµÏV_Kß<öw/„UYë|âð°/£Ö¹ÏšÓ31 ˆkä~vü÷0ËþcÂ=ƒâûMüš¾ºÛ¬Q|ð8}Š¡±Î¸ï½í?÷ƒ\ç‘ V ó"ö–.î®·PF»Îðû‹-Ãòä¿/¼ïƒãËêѨ” ¿,ðe_ÈÿÙ–÷ŽÓŒéoÇ€¥#`]À²{ÃaÁl윀_òÄ–‡M¿1}ß/ú2ÒƒÂÊ·oÛzÐ}û¶mÃG}jß¶}=ŸÜ¨Þü„?¹ùö9ÛÿõçlßÒÄ㤉ÃÙä{÷÷_ ‚endstream endobj 92 0 obj 2506 endobj 96 0 obj <> stream xœí\msܶþ®_Áo½ëX‚ éét&¶ÔƱ'òe2Ó$Ó9é$Yµ¤S仸ö¯/@± òáÔ‹_&ŽóaE àÙ`wÁû=Jb!£Äþkˆã˽Ã<:{»S=Žÿ¹!nÎv~ß)âÔþW=àôñeôdf:Ê4*ãRG³Ó$.Ë"Ék®"Ò™ù»Ì¢\ˆXÑìrç—I4ÝMiþV“W†Œ³Ic A™C£jÃ{€¥P"é¨õôË f‚Ã/•Èã"ïQöÄé"‚d ž¹GäKØ+;–vGU÷…pnƸ-ƒ´yó {&ܦiœè²Ö¡,NaPbäršÄ¹*ç#ó0Í´NÒÉk÷~î(³n3Fš•F=šgF%ró(ϤÑ:e¦¥2 íëT&ùälÓ§H9wjj¤‘ó7sêÚ~uZ±Ê…jf_µb¸p\—4ê»iç…~Íïñ†IÖ YAçyn‘ÄÈœ8½ºÔ²ó¬ã î“z[ÒI åÙÊuH9™Sf:ý’7`î…YÅvkùçJ^-Ñ\1¬jsðvÞ;êµ£Hü _ÀÝÎ Dê¢ÊÚ® R·ò}XãÍ’µî1;@L ²ùÃùáƒÊœ5†Ëi1B;ÓÒH“Ho|á]>oéÙ¹£.:ÚÚwù_hdй&ø‚b­Â;î)lpKmÀmYƒ!Cs3äÈß¾â¨wüòðÐÔî–ÛÏ@yÿ©¼ÓÞa éÅiEÚŠÓ²TÇÅ&N3ö$ÖRÊBNbK§B(©&YÏsÓ¾Œ ÃEThë2N•¶‘„ˆuRJ1yN¤Þð(Ta·HïY~YQ˜½3IXcÎðјŽùfÖEušnú=­„¬P\㛊N³4É,êÄ<-÷\«´°vf!I¤~aÙ˜ž²:&q¦Eu p #ÖxŸÆä<ÞÒßôôüuÂæò-±yNä¯Ój233çéž°=3f’k«¦Óì¿þYÛ`+ÍâòÜbÛ»µ;" wlî;Ö.Ýëk÷zÅÊî|»d'ñÞS2kÏZÛVt>^ÓùxANˆd Wtì~ÝÓÖ5¨OïJ“#zŽNèþ25Gtm÷Sa ²gø%œ_›ÃŽÍl]5HŒW¼ê̬ä},;É´äÝë(B)/†ª×PËË*’4±h‘)£8³…q'0ôa3½¢s"xlcȔ伆еVhᯠ –ì…[ëÒת VÆ"E'Æò86×G\,LºhGŸŒÇhªk»€x9<þ˜fÖŽ«îHŠl¨ÒwrMMƒ¦´ÜhLfv&ÝhLÝ4ÕY½[(ËKÕ>%OJë®WMc=¢^ÑTæ¹ñX¹e¾«Ò$Vi´»qVvèªçWrç‹°¸BYƒ¹ë¿F¾Œ‰¸²Zæ>XÀó$Ë8àx,Ep2 op&È–YØô€]ŸQŸItëkÆ~p¡2^—Vw ó§ô´íºVí•ÕN -l•Ö[¹[ÓÉzó“ijöÂFÉŠ™¬.í‘Ñ<•r³E1çd%L~ÐMÀ#ÁuáÅx>qc¢A?„Õ€Õñ´v XÖLy®zB+u¶e—¼@x1G×çÝÉ„?¡ð¸öæÿÄw7"'·Pi†äÀ(€9pÔlÚ V¾uÔsìR» äy úÒhû^xÒ¿Á²ŽYÖ0â8uéàw0¾ÁÌpÈÌ2í8ÛörpR¨¬qß‚üÙQT0y8ê{G}ã¨'@EF(Á,ù8 ˆ –”`{=æ~tà´£„ž†y‰».{ÊÊ‚¸°28UÀøùœÈ„Hý©lm;Îùç„s_Jf Îy熗œÐO€Jæ‘£ûwg.½¥•+æ4pj7Xœ Ö?YŽ–ù;ì¢púŽuÃ)Xœ!ô€² ¢çU}Cïgd žùm[ïv¾»µC]ô]á¸=¤-4r¼v†¢‡wß¼°ša…ÂûXð´ƒmaÌþ8F©ñ‡`éb…ôðòB«„•~¦~ìÔî%"º mÂ-Yýþ%»ù ‘²ü#ïÓJ¿Ù‡\;»þÝ»°+÷á³'ö°?š0*Öʸ>*Þ$}Õm¶ÿ÷®¸ˆ“þ á¦ôR]%q ÞÕ~Óh›NKÎ]±×"XÆ;¤¾ò%âÛçÚü$=Í0fö&F-4»ËS¸]±•œLæç…ÊÃË£’mm^u$ObQYX”5.¸ºÛ}RòfiÞ­»µ6sÖG$­Í:,Vf<í¬/Ö™3t¸j;O h6—´m+UÈæˆ0zI›)t›÷ÒQþ­ÈAI9ï~å‡áœXXÏ’ —D)/C=î64J0QBˆÒTÂ[Ë?uÁ ËÍ~”^ðÂYv…­ _ d9—=€Á€ï_Ö°ËÏ(*‘ (î€:DF ø¢Â°Âù4 <®n­ÀúaíÐMLÞ3GpúX’8͸€¸¿P­ û þ çÌñJƒ—f×Ã)„î¿íÃÅÆ—†™7ÔÍIã?•›“m,G©4Aø‹£¨ÎøØQ¹£~óQ“f“ÎåÝ5#þ€ú…ð–Pbžå̰ˆø4X5íEG8-*OQ%ùË”ð-?4øb%<Ø¢:ávÓGÉ$blws°vÙs8…dð»q¼Y¢’ãt|¨À§ùšAHɰŒ€°p½"øAþxìBˆ.]ŒØ1éGG,`„°Pq.Ñã›dì)>Ø1}Áºu§ƒ7Þ€¾ µ8:•üЃ;éÅÝ`g˸†øaL>&hÐÈè:ݬœù‹í:u6­1Ї·å5D_¥ÀW›°KÂ3;ƒ|™+À—<ÎÞHIÑqêðá¾ûה×êu³Ó54»Å*NìîÞ߈DfDþ1žUeÚ^i— ¥sÇ.„2ÞØØ9éVÐY2f88ÄÝ>†œ˜p˜Èòé09õzšà¯²|¶þåî÷­^‚2,AMdÈÒ†þ6Ï×ߟú NÌ_êëïOm’5øúûS_äïOÌv~4ÿþåˆHhendstream endobj 97 0 obj 2911 endobj 101 0 obj <> stream xœí\msܶþ®_qßz×ÑQ‚dÛéŒmiZ¥nœÈw¦n§#ùNŠj½Y>9±}’À.ȇ‡»“l§â|X‘‹Åbß°ØïÝ(M„¥öŸÞ\î죳÷;õãÑÑ_ZàölçÝN™dö¿ú‡ß\ŽžÎÌ@!FURéÑìt'MªªL‹†ªéÜü]å£B¤Ia.wÆ£Éì¿fŒÌüó®ÍžïÌ~ÿÚ¼f©‰Rã…“¼ÈÔø@×½gR×£›‡ ½ôÐÌC/<ô‡þã¡C€w°ªÓ"ªàk¾iD•¹eYðšÀ7.|A†0'pDà1& ¿gßì‘ÈZ+æïßÙ¿PÑPÇHï¼]‚·o<ô£‡È æ¡¢[ŸO*Q$e Ò[((]†»„[@\¬ªÛ˜Ì¯búyÙ¹«èi­£¨(˜F¤È<‡ ¾…+¸‚lcÙbEC!}ì.|••’‘½Îùz{6| ÞÒ³s]ô¬´„Z~ŽÈ¹bÞx •$¶&6Œiè"li—!|Š9˜çl£èDq÷%—~/>ïHMx[nCkêý7öŽª‘A¦PfL!¯²¤,m¦ðz,'Ó4ÑRÊRŽ gB(©ÆùÀól2­’ÒPµ´u•dJ&S‘è´’büÜ‚²TEa·šF©ÊþרþQY•–Ý¡i’k!ǯj ¥ YC%'3adö,_yiæEÌH|!/»½qF`ãøä£F&©4’Vó]–F&Ç å¶†3­²ÒjϳʟŸ32s?é b10ç>1Ëi¼oÕSV6PtFlY@é‡kv‚Í‹:sjÌ*ž€æE™hE ¨ †¾4D‹ÂêÂä§B*k}ÈphBL!TeÌÛ=¼ñÐ’^ŸO”²J1Ä+Ä»ÁÉdZب°K‘dy‘Køý«9‹šJ–—Ö%ør­Së‡ü1†{LOOj\¥KÆã€š•Ê,)Jmw`a³W90ý5œ³À‚8cÖeri¸häLÒTf—ëO“Ê0© dÌÙ°iÌ¡ÄãµIaU›Ìl¾óšSb¼2Vz¢«Ÿ¶sƒ´Èרˆ Ç?5 æfBn½6áQ¯CÃjeeÜOT†j¬1» ³„KAu iÙóت—çÒC;˜˜GºJ‹á}³Ms»;­žtÈš‹‘I)œÁ´DWM j1S¢*º’ÍtnÓY&<–âTImb½Ùñ¹ ‡*õôÐ.ɵggö}`f,¸÷Îåª׎{N!á“•x‘æ9—f☻vŒ!cŽ1ÊòY{F„&ìˆFp¿‘p©rs8Ϭ™¤ìY3‹Ù¦{Ák9°0æD~ ɯ<¥aŠÇ­í.“‰Ð¥3<æÒÌD/m¦iÔc¶üž\L @‚—€£ò \^ ÃÝ aC`“ ÕàÝí®,? ¶J´Ù/,Ôay±§LûÇ+…` îM|0ÿ·*Ÿ:S`X¿†FŸÍ&ýCΑ‡žƒÃò¼¥£Ò3ý ̶‹Di«Ùë@×®ðYçžTÎcîàÓŸà¹ÃGî xbÕº·‚?YÅ 8­È€‡€ò€¾õÐ=ón`ÿÚ_À)ØAúZIÄP=ã¡máQÐÕUÃzÆý—=ýHàML̸ÄÀŽ|N`J øZ¾¶ZÎòÿMÎò+™3£=T ¤€÷Ï/ƒ-š/«ÿHÕâÚZäPí ±xQP²âuÉN9Í8Ë@¥ œ‹2, ®Y9Öæz•^WöKÊ8¸núi¢ÓÜY >b²:ärÒ9xVy>pØÄÇÝ9X}Ÿiº0ÚÄg~ ¢f,Fáp´ÂCs«ûsÎÜN&ÿj°åÈ¢löOVÛ2¡Ðuå¦9 }÷[˜ >…'6l¢ÓÔäz[µ’IZ A hÏ1,”q‰ÂËeŸf>$ðåjÞåæ¼—IZIïü 4-#+îp°¶†ˆ)œy0 — ¬l7sŽ]¾ž©ÖO—´a­‚¶@MÓÌD9YWVe’éFl”ž¿ÐÌJ¿'y[mWZ«iIhÍÂçåÄNhV"98ÔN`nQè!Ì'¢gU´°‘‡m8 ¥£j—Â}œÈ*‘"Ë]˵S]“"[£ÙØSlZаu®Ÿ‹²dIæ R8N¢ž î­ºöÙV…?WôT^÷ŒÃ…Õí®Rñ…nÇÒ Ð>æ1õ;Ø\ŒTI'1CšCµ­±H<2«=#ð+ööΨ¨­oNÒ‰Vf7iVÖdW8ObAÆ^Kq©%!KÕö_ëq ´7Tr˜UIm’z¹sàkï4™pN#= 6fô\§ zÎàaòˆ\3ÇAyöÔî^y¡„†ÌÛ 8ýmy«%X‘Gxå[»ª¬Ÿ²V•ÜÄy$ÍÅÈž@\|4 œ§‰¥É"\¨èŠtÿiö°£X“î67+Dls{ŠtÎŒ¸×£î¤» ŸÙ;é캛 ZËòP®ÓÞæ•«iïªRº-al/°®ÓÊ¥J#Ý—¿ôPxÿx­²up“ùÓú”XÝ‹Uá.!ˆ›c¬ôùî #T¥ŠéË d@_ è`èMâõâåà»¶¬¹«EâúøÝj‰¡ö ’U¡Ÿ|YmyC—œçP®‘Ï^bÍ=$–~ÁþÅ‚=‚=ų?~Aa­kB¨¹öÌÚ‡Xhš¿÷á­’jùÿ ݇=Ü1‹v2lòé`Ü|}E  P2“û¸Çß=Dß¹ízhÝâcùsËí…R—™ò˜nnà°[ˆÀôˆÛ³#H!œÅ‚'¶0ü%Ç;ÈúbõSg£ >9]ý™…"—öO”ØéÙjñ¶ý°…-æRAì§xcg.ë£ãæãYWÌa—ºÐG=è!Ä}ÿÏ„Ö_ê`$c {V eñíO0¨åþy›øFõȨ7¹ü“2³"&ðжðW½LrŒÞ/ð°SË—ÐS±¾rÝèaõ¥Ñ_Wp¹ÿmÀ¯çfúž¾…¾‘'Ò¾¾Aº(ý{3 ¼é20z…Y NCw!¼ŸàÀÉ¥¬¼#¡)¶mÚ=¾†>ð~=éF3ãc(7+—Qè}G½«øp?ÿd‹eër»Ùe)Ö–dб·~öïE É¿à¦ËPô!«ž10öY.é.ël¹ïoÚË ýOƒ*ª0¯u© ûŠ5÷¯y¹Ó3pî`ѽ¥¶vØɃ=xü)œª=þ”J ýVJErÜÇŸRyü)>þ”ÊÆÑéñ§T::Ýó§Tf;ß›¿ýŠÂ endstream endobj 102 0 obj 2868 endobj 106 0 obj <> stream xœí\Ys·~ç¯Ø·ì¦ÄÑà`Æ•J•®ÄJ$›¦èrUl?ÜÕK$-’òñëÌn`¾!fyHŽK–KÕšÅÙzº¿ÁÏ‹²rQú?qünçþ¾]¼:ßi/öÿÙï_íü¼SÊÿ×>àôñ»ÅÃ×QU ¡‹R/^î”EÓÔe£Ûba*÷ï¦ZØÒ¶ Þí|¿”«Ý²0ÂX­—ÅjWUS–ziV¶h´©—‹Õ®ªG/Ÿ¯d¡*cšå!ës>A­TQ–Jªåe øï šë…ܨÆV¬íìõÛÄlí¶,MµÜtÏ+Ó4Ëw«]QTµ©ÅÐÜV~O?ü˱ªYY4†8U«„Sƺ­KÎ))e-=§ÊB ¡¥g~.V»MQ»Q„߬4M¡´Yîû™²‘bù%‘¶£Öõ²dô}6žïYumï)XkN/XÏ箹(¤ª]×ä|‚>j'ªŒNì[+Œ\ÔEmªjù`%ŒÓÝ,×~Q²6n“|–³î±–^Hn0Y•M/£®Áû–VF«xýJÙÈåËNÀª)ëÀˆªe¡tvãz 1Ë{¬#îæùW9‰=ìH'ûå«n’ÊjtDˆE鈕©Ž]˜ÚëˆëtðßÇêʩ̳ƒ¿~¿ç–=—ƒW¦ 5b¸G"«w­+?= ¾xJ½[ÙJŽÔ‹é´çÛ+x E:vt+>±¶Óé`6X ìTíý”tG™ :©7 Zÿ«,3á·­ÊiiÇÚð/¡ÀØp ‰ 4ëpظøÈêí²aaÖvþ‘îåC ïph4:SÆBí¡Îqïƒã‚î}üðȧ·ÞfÃùÊÉÎGÙÂêA]Ž8W¿4¥ç‘_Òn­ÖF~é0¸ˆ“@‘¯[uo‚ìýŠL}´UožÌƒ±¡˜\â¦\ G\ Á_àmFŠ‚Ôþ)‘'œ;HØÓ(ö „5[ótì8ÜöœzPÓÞïá—:ö€•nË“Äxãë>†ÒEÓæÄzý[Œ¢´¯£ã¸…l€mòl,ó#²´6—¤jÕ‰ƒ$ü~K›dkìΩöEö#ÄS‚Ö((^ºOÎÆRƒ2¡mÀÄŽãäݬWîYDð"u Ž˜gI¸;.ÁÈ(z1 ~bÄú›„²Q’ LA¯yÑŒR6X`n2á_íåðV\—ÖòwÊSÈù _Mä=³çå”Lvê_êØÃoÚl¥‹Ôœ÷ÿ‘ÛsOét>ñO©Ìàc¶÷ƒ{õaAÄSnm½úÜüõJ-ò/áa1lœ]£ÌJ{¾öÝó^¤–îÔ‰vžfÒ‰jŸVš¯aʉ†×¸“Ne´*´i•aF]ðäR瘦¨ÊŠy£.³ìÊOBÀBnç˜\½;M¦–‡(h$ÚÎouRl®«;0…Ë|ÂHÎþw`5ŠK$çÖÓŒ“CƒJßY_|@2)bÂ$}’µ¤/º¶ª½˜»4à3/Û¿ìÜß—*|™b( øú[é^´O„甆²Ê‹~ ÞjÓS­ïÛ5¥·@Ë»3ò<ôz¨ßçtÜ5M{^ ä;Hnˆßöo‰¼xpŸ^Ân 81Æ+"ßä&ÞÀÁØ"•á Û‰h¾ ¬}”cíõò d2[ô®Ë‘q‰”íU"=|–ýàn6{´…B¬!6p°‘NÈ™Ÿül~wi~r®J>¼}˜ãíõ$²Î±ënkòKH"n ûÎZ vñÓ|U4¡—phÔÖö`€Ñ¸¬í¯9í9ƒ1ýCVt sWŸÍý.Í]Í5÷§·ÿQÛÉë?æ7cV¸Œ1Ï8™Y|y¥»Éq–Mñ®7+F^Âå\ÀÙ°~ü Ã~ûòá¬,cФ4_êà•æã²û ŽË&f¯ìÕì>ÝrFJnóz11æòyŽÇp߇p[Y{Ìòó>Å#°Ù<ìo ù‚È}"‘ƒO+"ÿÎæX¥’š š¾¦ÇW¿vç„ÆxÇ$õòî¶e¼[çøŒ»ÍWìÛ–”)ž¬ßJB¼ÙÑÇõVØE1泈ŸvIš™’œƒÀVµugÍõØ2‡ÀÞk¿ÚX§v­' Øe?`Ýxg2Õ$<7q›¦¨êºEòÒcü¾Ç†Y$Àï~Á ÉÍÖ"4{j-j¼ÞÍÛT4$oRO€Ó9¸ú1«ß°ÇÏýÓD×½.UáÆi1}ϲC¨Ä“öØm"Ó±Ú*ï;ëÂTZ´YÝa¸®†£¤³uSnê ´y? W—k‚Å••…¾U°øC0¤x_8 ý 1í2d»1vá®þIeÇC@ÙZ·NøoyŸ…ÅV¢ŠO]©k+¸Ó€cЕô3 t¥Àêó¸À1¬°2´ÿz#²^h2 òšf\f™‹€Ê!'™NbøF¨%F¸® ,BFŒ* áKØœœE8°j)«F²ª!!£»é&ª°u£"uêV²(ë†*ÒÈðŒ/›”¯mƒye çF9výør‰Õ U ß¶‡ÀþÜ&ÅÇD‚˜©YôÕŒ ê|Þ²S7Db=5/ ÔËÌ‚©0>-zF2ÞFw,;…N‚ÔáTýûòfPiÃU:‚s&ØÔ9÷̼ÆÉÄnæCN!4êWèW®r²#Ä5âýo+Ù¸ðNN Z·û*&Å\cšåïÌˇeaudS@×í{À¯ °Ä§\àÁ‰ufçÏ)ˆãÐr$ä6üÒ. 4FGÀ¯Ç\Ú‚}ªƒ¿ª‰ÉˆaR lÁ¤µNõ¼ˆaÛ45Л)ò<‚‘Óaå’°‹MÂÜ*FEmA8y…×» åíò£t Î’)<Æx_ÓݽíÃ1U¸·+áA¼‹Â›ÁîCÑáE”û¿?âw‡%¤° 9ØÊ2> ÔA ÚÔ^ N@»g )F¥ ƒÙ†4Úœ¬ Ë^âL>ËWà*=Î=.`[ö”áÞƒÁd¾š€“ï,[÷aMSÌMߎ ¿=ž‚‘ŸŠ² T_¥šë3Ð#«TÉϨ#Ýöô7"qµw;… Xzò9‘%‘³ë§—ÏÕŸÏUÊçOÀÞzÄèÿoöÖ€ÓŸÌ[옫}œúÿÊæ=¨ÅúÓ{ Ägógã³|þôêlÿll¶€ÍE)DüŒGo4/…¡ÿ­e ”û]ʃ…t,ÐÃcèqÁʸ,ÍV1Àï÷\7Œzë4E—²°6|õô€~?€$kÀ É_¦ ¸;ŒË’Ëw¨‡:ÆÅÎ}ã‡7µÀj† ¿edßE±-\zUj HÈ‚Ü.bè!Ú%ü„©Ë©\³.¥rÛt¶ WiŸ$YE?uZð1:ÎjÃ(s °#˜_f â"h\™HnüÊ&Þ‡[ú~–Tð­Sàƒw[Ç]´j–~¦\|”ÕœsµAú­éiŸ ,ýõqàûO\â»X%Ù@Ÿ"Ä@œŽÄéá©:¾•¯y¡¤T j9óÖ–+ -7Êû…B†áš k;U7Þ@6³ÉR»ê>e…†)»7õ`˜ÁÔFi5Ö˜.\×CÝd\ŽÙP±á…’rk4ÎH£¯w ­VEGV>þÆE¤Èr^·î_f³ÎêF6£k9qyijdmæ[ºs(磲·gMùù¡kÊšn]ÉØç$€mGÚº;?YÁÁ¹B§Ôyð„Îݯ`(øÈgD²nW¦,}•dtTkqèz´©(Z°Ðþ¢À—Ç4óS"_ÜúÚë¢ld0þíïúBr9ÍmÜ~44Åh°¡&‹!&ÌÕq2Œ »€ƒÅ¾P9_(¿«LÊŒÎc¯Aµ0EÕ¸½| ¨/摈}¹U ÔÂñ† ²¤äµZlÑSw¥$}]îÂÚ¨ZÅaíg”á  Ñ>ç\#0Æ ùB÷ÔM ˆˆWIä š ´ºûL¼ûw&mpô£Ñì{ìÞ’«øqÕ6L^Ø;¹aàû+<ö0·Øu'ð¼~ ãÇïiZèø‚Xzú# •0æs† "_‹´‡VÓ_³åNœÆ2gšžA#‰Îˆ(˜õwžJ{ãË&‹Ô~¾_#Nª¤yõ–/SÙß«.ÑÐ^£{Š2å_ÊêÇh§ Ü½¯Yás“eÊ‚˜‚>‘œú¬gGg~@ýä`ç÷ç¦øå²endstream endobj 107 0 obj 3867 endobj 111 0 obj <> stream xœí\Ýsܶ×_Á·ê:… =“‡ÚqÛdâ:•ÏÓ‡$Ó¹H'Yît±NN¿¾?°KòÇÃt–\;qƳ& `¿°»XÞ¯‰H¥J„ÿÓ§‹ƒã›\ÜT““¿5À»‹ƒ_ŠTûÿª>]$Ϧn ÒI™–&™žˆ´, akª21¹ûw™'VÊ4+’éâà‡Ãdr¤…rÿοv`š[þ%@?Hèi€l€~j eœxCà%ï .£@ ·Â93OáObSÌ!…5×` Êûiúírúî`úç½þTDÄøÇäò2sj ÙΞނaŒS`Ô‹°—“É{² ðáAY6‡ˆ4 ³ *׫°™¿>ªr±mœC[Ùì©ÙÐóOņöêæ˜·ù@ऻ„ĢŔœzTƒ»£¹ìÑGuˆ©>'·` þƒê]?Ù÷ãºþ´‰çÄ,cÖ‹í‹b ™M!w·iâäãÚô'ÆÉM¨äK`Ûqä° 1#ÄçÁn~ )°§8ZN «Â\öw9žW•?_Y`é<ýñà_Bäk¸9Ð0óþqÒ׈ž"üý1Å·plSÌ;a¥¸€Ìz‘> ðFü)%¿Ó} ²õö«¥ÐoéÙe€®Z:ü7Â`dŠcNѪf%Ö&|äžC„;jÆe¿Ç ,¬l'ïD~÷5çþÀ?OMxw<†¶”ûåŽOÊDªÎÕr¡{WË™p/T}µì"w(¥ u˜zXK™©*7†Ïõä¨L GEVÜ6eª3ãl¯ ™µ>ðÊ"]#EV„y<|Ìèµ#U(ug Ž|៖™±.Ü“ Ûl)ÒÜHuøÆ1ÂñÈ'.;ß<§q;~S) Ç˰9œÔ#…*;››1”‹°‰e€æìýºÚšÕ¥[à%#wÚH¡(;³4ëk´,PYAÚ7#Kõ•·5•‹’1™qÊWüªr[Vƒø}¤¡AyZõ3¸1ÓÿtC{?q!¬Q,JŸˆEÌ Ó-I¹ÅÙRr„S<»Øß–,´&ÌAêÐßmÄMМ^ãÔ‚‚ÿ…wA©°Ú:¶¢ŒCeO—²µö‡u?­©02 e/ÝSaÝ£îsÈÕÕ}TpñçTK‹iiå팵E&¹Ý+¸ÉëØÊ¦µr9â"·­rL¡ ôGdKí EàiÍž2Ïùz²ZîýÅÚõÒKSQ±QVÎF£Žj yÞ‘DX8v¦ ÔÇÎÙ9™„ç#SŒ°‰kãÊÞOr_Šìïx²ZmòTëà“¼Âf>6ª+\­ÂVeM+\#‰;¼žWìù/íæâxî¿@xd¥‹m[o’è T¥X Ó·´ZŽªŒ2¡ŸÝMƒúÞ±c+=2.<µ£ÓV¶âýùûÞŒMæ¢:-<œ;y¸NTÓš“Ç¥C´5猬´PÙ4/ËV ±u¤ÝÛ@–•а‡gïÂVÙLì ÿÅ¡–>î­:€Mt¢…l,ê¨Ý é|MlUí¦5ÍL'Ãr={³ñ-]•Qm¯½ ÛâÒ,z[‰+§°€p]â ‚ÑÛJF—ÝVâûN<ì"L d×Ó‚Àm;xB¾m·ßòÝŸ|QÓ ¬6ÞGÀËxDo¬=d¾“ùHŠ5ñIÍXâfA^ üKÔ3óÇ7)³èK—”àN½u ZªÜòn†‹Q_/å(òr)®DõU,·¼…ñ*¯ŒÖ3«¢NX@ùs,3lR0_ÒÛ%kÆ Å >½i‚V•d½ÕV(Êgj¬8– VR@Ýa]ki¡Tå³µ³UÎ:«lcoЇ¥Âšm@\Bμl*BÚ> 9-®~Dë.c n¡®„,ˆX kÓ8u9ƒÛb²Â¥ÙÙ¨ìa­äiÏ»ÿCRÓÈ.–Ô<„ÿ¦èûÒïÁ3Âû*@‹Žw—Ê-Ùª=xwv/ÏÂàËØ0ÜÓÆÂkv»‚OOc³}€SD…|Ê‚ãg÷ô ˆñU¥P™(œÒ,ãßÌIÜpºˆm÷> ûƒÚõ5AH»¨ Sß7u üë•y´+.nÿí³ÐaÜyÑÖ‘&íF1Eðc¬„H7´£ñÂ0LaÁ jéd˜tG/sšÚaðîˆAÙ¢ÞóÆ6KóL‡ò½ÿêõ,@ßoýõÄ1àZæNÒBʪvh™Ò/38ú¨–s^ïk½E*J²Ë½b´½ƒdGô5Œ ¶¼õï\[Go­X½ŸE#+…ÂB*øøµð—°ØMª)d§É+N½Hýšbu¡“³.Cjáù‚b@|i>¨óWÕ뎺Tg†ÎÊÔæ™;3X@´¿žûöŒô.`ïÐ2rï¾ôйE–wç«÷¾ÛêÎ…³ž‹~éÁŒdìz¹¾ñ0©ÐAiÙ¨§àB¡Æ=ª+Y»„ÞqPwõ"@ÝnUЇ>Œ):}¯¿oO WŒÄŸ&²pàn]êÏÁ";ëÞœ˜0 ÷p²-⟅ÁMžx‹¸7úáÖ>Lù‘~¼c—Øs‡OÏc[ˆv8c­Âqì¶?ëAÛ~ýÑ·qñ½kFoLöøû8[þ^Çÿ5»öøÛ8H¹È}$›bñîŠ!à6}La·s¸½b¿g}¾_Ì·¬}1=ø§ûó?ÇÖÊ€endstream endobj 112 0 obj 2730 endobj 116 0 obj <> stream xœí\YsÜ6~ׯ˜Ê‹g¶"ŠA‚ˆk,Kå8ëS’+ÞÚl¥tŒlm¬#–äc·ö¿oƒC¢äÇáŒ%[’kã<´08ûB£ñŽÒDéQêÿ5ÄþñÊÚ–½9_©ŠG[jâý›•?WÊ$óÿU’Þ?­ïPC\âŠÑÎáJš8W¦vÖ«9ýíò‘U*1åhçxåãÑd5K5ýmÆ;D&¹ÍÌøaMéÂÿ®JeídUAªÐªju1« \6Þeò¬!õxÊ¥#&÷™@1žÃÎÒ!) vQzØü¼#°EÄ”…Vz2 ¦38ésÈ¢e´÷Z¥ðuX ¤kR,—ß‹ãz›C\g;¢Bm”MÊÈb+À.n+ ™\còù‹{ÉSPÚ°wmK©°GÓžOœ¦_îy¶çºáûæ$KÒÂÑz©}ž¤©". òtB:®4í¢pD…Y^i6~~ß © ‘åŽt­)£½ÑR‘Í5©š¡Â̪èÎtjý~Xµ)3Ù;W%iXùË.7mÿtXue•if_Õxz=åQ?N\bKþ¬¿ŸêNò†›뢰‡¬¿@žcë= N;e3 e]ÎälnGûâ¡…öžC}:€ · šµÍµQ"è6OÀü.¢Õͨ]ðëø•×ö¬ò faFýœÖ<á4…EáÝÇs»³Ÿ™¼;n{‹m&w ·xÁäïÐɈº›,R¼7,îó–RŠi›ÑBtR ¬2‡àW.; Ô»ŽXû¬i(šCGyú¢ÙÈ1ôleÝÐAÅÛÖØÀF¥-éP:¾c´> Z{Õ½è–é}_L´¼Þ¯meùˆbŠÔð Ø™Ö 8×&)ÌìLQ íª°$’d²j’Ü¥©ámâLQV³)ik1ã­‰¦%Õzü’6¡D™.Ç¡ðµïÈeÎæ>Æ }®qŸ¯B]nõ P¹h” zÄ<¥Ê´URåíÙ`¥Ms:®7]¼à)ˆVÕ`¶T•t©°,óœ #ü1[kîêVUîõÔ‡"©Ñ>€h¨‹j$›¦Eî•Ê$Ne©Uß嬩\ACMŘu£ÔåÑTF¡òoc1D‡õÚ¤2M]1mXs=¶¡X$MsKË÷Ú”9êB¤<;¤+?r›¡!gL/ˆ'¿M¨ÓÜeF¡ø¯'G“§®rÙÇÞíü+Ž ÉðTA1P¡E˜7 ”¹|Èäcâ Ùû6enüÙ˜ô?µ™¥S"ÇMìÅñâw$¶(Þ G„¢û3æ5¿O¹PT¥"ú¼ˆ—B*]*òN­¹y[½ì™e¨úÔŸ©¸² &À~H³ f—&÷äüÏ©–­¸Û8ðõU3Gö³ªœ_F4Ö>W8Z\'°—¡±«™ÇRn¯ð¼Ò+IªrVÖнÓÖl±†JI[ rƒ¹uqÝ@Öu Wʺëžq6uj©VÄdòÊÑ›¥šmpi›‰¥=æÒ)±š‰‚Ý¡l·­ õ‘ˆÄi2r­Ÿ'Ê§àˆšËÍëäPa²ºYVäEݬ"w¥…ó‘PÎÑD[òäÎzmXmÔaµö^'ê‘­UadZÛ—vNr­&ô2á\Ù1ïyļ÷xÞÐ!ðï}âä&ÛªÁ[]íkžH¶W*HC ¶·,öжc±µvý³÷GAÿ¯˜+éÐX?wá´ÄX¢×Hž·'’?3×AŒÉtã:¶ØX±-R¬ã(ÊA\~JdZX[Ò~· A˜ÿ‹PT¨Ì#¾‰‹e«gæÒ‘šËóñGæÏUŒ„µg?w¬¥S¹Šƒ¨[ãcŒêLá;Șä~óKmÚ$ÐÕº¢]R–™Ð•–3£Ý¥åŠLaÝrEœÀ:CÇÛ×< ”p/#؈«îsU¶ÌËV*¬bÀ î‚÷¶§n¨¹ºöûQ(F¨7-;½€š3›!…Žu¢m µ^¬ãÚ?{ìn¬†åœPÒhAG0§plá‘öfªEÁ™N]£ZÐ$àƒñél 6Χ¢Ø –§"FÚ‡fmgÄ‘@´¥£]0ÒC´Z¼™DÊ;‹”Z š@Þ²7úÇ-HgÄý"ÕÙdÀ–×{t¦TÊT\$1(zµ¥ìêKësí¸Íæ«uO¼Å+hù8MœQ.òqœf‡QPÕãoê±T&|$G`¢§Ÿfͳ´9c.žçØf ø~‡“][z ²†\ï H™= ÔßÀhQzl>ÜçÌqBø oYñ5óG˜DÃáÛ9B ,qþì!d­¾ûr9F9Ú_A“Ç ëM@= Ôƒ@­YB î°ä§p‘ÆÞƒZ2 0£ÝÖÌõ^*TëM×\¿P~¢Tä¼1te™›î-&_3™3™>-cbsÖ¡î6›3@Þœ:oŠÕYÝ]>o@uVm>ß({õ÷Æ^}ãÞ±ù{ Ìæ[ê-Ì÷Ægs‹Ô™7¿«:e}+Ø,6¿ŽSþöêÌAyym|¾Au~Àd9‡Ï7ÊÞ;¼ùaö.ºù0\Ï®Âf¨ïC‹-“·Ê[0Ÿ¿u|¾ùXŽÙìu‡؂͎ItÀ¾1uf>ê,øÜQçoÆ^Í™@Å Ðý·—Ñ8°3LâçÇÖÐax8“|*z›Ü"?…^Lm‚ÿ Ô_Áÿ²½[¦áP_î'ŒßÂf8-Ô`É{L¢—Wp®$ÕPø¤óóíÓððëÿº3#Ÿ@Ýy6¹Ú2±Ï1OŸ"äêjÝÎ#*Uuá9n ªfy¤ê·ž£6¢ !ÔS?h¿Oªà_øF^Ô6|Û}P]ðê æAª}4ÅЍ10„aoá¤Nà øîЬÅe7Ä7Ú,nA’D¯bõm}–&E0ª©"p!“dÁ°Zp¿zX ÎYK€<Ãuc¡QQúÀƒé^ñßÛþïÍ€’5—à<^À¾®2YY´Ô8‹Á±ß†4z¼™€Zp!õüP£:\¢d&Ô·Æö]a@Ü} ]‹°¦EX"¦×U1h^ 0¸&æ›@¡5êíH­Åá9¼ní°ƒ«Ñ=.µ¸&m<•ÀuýÛÃmJ&Çî ƒ®¡¾÷¡ŒÅõ‚ìƒDM.û†¯U#ÈÙ\8žÏûZù²DYÕV>𤷮#«öé+Öè#êŠIˆˆÁHBØÒéO@/û€s¡‚D¾…qß´…%€–~0áÁ¹=ÞDaÿûŸŠ¿Bä àô^xÅ“ ä5½ø´ì†’±¨Ÿ±3¾çIerß_WÄï/ÚÚœg£˜ÔRÐmOU±§Ú þƒ#¥ŽøûA²ü;#ü…wbç'5f߉ôæ8ñìZ1|Kté!zdx•˜›G’\VÀÖDýáHع0^ N!{"¸.Š›EÔ‡á¸ËuH‚‚Sb g’ Ͱ‹kÀžB¸a\r× 1QçE‚‰(ûm Üϓ弭ÅýÒ‹/r4ì)>±è@¥c`>¿šŠ2ætØîmQ¼¢é}Ïx ¯½ÃeÃóÊqýh¼ i“Wü8ŠWÙ‹šïºÛ(ðc~5\Š6ŹagòGG#/Êrv@ˆ_k’Üd¶‘÷ÕЙEšv2†¼p©PœÔU i*?Ͳ鄓ƌ5ݾ®ù–Iêt`ÈB/̆›ƒûú,!Çu}oBpVauú¤ {´¼GD»N@ÙçYBª‘ˆHM=‡äŽ—«ŸSG¶~?4…ºñ¼Cd¶g3Ž6q¬Püç6„¯§ÿNöÅûrÎâs{ìÌi,bKéSs! h^|žh—h¥£ð†"øš÷z%öÿ]kð缾5Ìõæ"\X@#e·âÜUÊFà»+¥Œ²§¯œ9<ãÑ7“ònƒo}8Á‘Lä1@øcši}JÉ“<32T :qÖ g‰Þ¡1û¿µh¢ê¿Ý™²&øAº8$/`žaŽ}æ‰JýwJ/ÅèäÚg‰¤Jæ‚ÕÉšøÈ.ÜÑ2oÆ„3YäEf£2Üþ€S\g€‚ß „th¥Cô³×#dŠ_h•(g1ïUtŠ^.ñ£ÑœEùºnÈÛ^oÀ_ÉOêãmE vÔ¹Ÿjð_–h?¯ŸÓÿu¼½Út¾:»-jîŽâ/ß ä'º[Më»8 ? Ú÷ÕÞ÷1/÷U”&¿Ái×¹EY& C°ÎƒøÖY¬ó9> J8¿Ð·ó-ugÐ8JÞy¢âlçRtވί݈’â»"ÏÂÅTNê@g‚ŸaDù뼈’Ô¥àápß[‘o]EÙÐ;¯8WuNGe›@nf$ígóoø¿Çä§}Ë_æ3NàïzÑ9‚-‰ïWÙâ]Ü€‹«ì«'uñ‹þ…A6Wá2ú•¿|XÔ9E.ã¯nCR¼dèƒÁYÖâÅŸÆè=u±*` =¼5Z±¹³ò’þý›ÿZendstream endobj 117 0 obj 3634 endobj 121 0 obj <> stream xœí\[{Û6}ׯà[¥nM¯ykë´Ù¶i¶ŽöëöKû [’ím9¾´I~ý¼`â¡@ʲ봛¼ŒÁÁ8sÁê]…B‘ùßÇ£ýÃ,8¹•ÍÁá·5q}2z7ÊCeþ• œ>¾¾šêŽREX¤Át9ŠÂ¢È£¬’*‚4ÑI Æy0½½“=Iýw<~­É0ÉT<~a©W–úÙR¥-õxú à{¨¦¯LMg‹,Ì3>1FÞT¼¢PãS"WDþAd@ä‘3"‰|Kä‚È[(lIä5ì¶€ÝÞyyQ«Ô~›~7Ò“Zc?Œ¦Ÿ?¨Î¦hŒ}²:ʺ£±™]BÞ‡Ñoüêý·¥~´Ô?-5}LõÞA”Ï|ŠÜ e¦½Çv¨/-õC‹ªÐe]ÚàÞgæ;çÄ^ÂÁÔsâ‚ñÎw©?&áp çp $ìÄÏ}!µ_îÌåD.âP —ëÚ<Ù|¯w9_ËÛ(}ÿP›éKë_?ùÌC"kx>Qa”ÝÙ$ 3!ãÌl6YE"M5ÎMc )•¤i¤Œ HF²Lõ¥a'Q˜½Z=žJ =?#JÉ(Ó °¬‚İj¥¯ˆáÜPI–HMƦµˆ5Èö9ë”d".4D ë…‰Þa”©LïpL*¥Y…Ô²¤¬[ã4V­ƒ{tZ/3W\vÙýÀu3†“JL*d½ƒüM…¦T£ÁxŸU¼*µ=”Êwe…)Ú È¡g–º°ÔÂqÙ4ÊB{vIÝëÌRûK:†nsÉt›[à™ëQ±ñ ÏÎø¥3ÛÍ¡JØ^©¯×À~V£Æû°Û’ÞŒÇ/Üí‹gPÚ“˜°ÞÛÁyÐRí&=‹{ë…-ã "%`+Î+¼1osxf¬Ó7¶#ÛêË ¿ÛýW±9HÛKúâõ›„Á½g¤ze£°z¢Ì›R°®’óçà,ôtµtp>Ïœât㎰„°¦Ô–ÔþXãÀ6‡ÝÞ÷:èñàQû !o0þ+E@úzàèótµµÃèÓÛ98ü<]°ï~Ö€íwqß'ê$Ã÷ùû1\Ž‚Þ÷(;Ml¾€r‡¼Å/,"Ÿ­ ƒêé:›õJóGÓðN[žË?]Bw¥Z…ÍIs[™Z][is@N½e5ÀVñÓWášæèÞ«ë’±o–€½ _/yÞj:$ƒî JðÞluÍæ _¨‡Ú(p/½ª- |„Ë!9vï `È{\ÖŠS Æðòbaï©¿¹îoIþ:†3xOä±oŠÞçÕÚ–¯òc"ôô,ÚúwæYô+v:õ~‡õ©9Ùg¾!ðhøðé¹ÌpF{ð+;èYÍ‹šÿ›Ìö&Ó¿â¥kf×/s §~ x½weèø@×”q*@}ÚHónÚ6—9(r¹íáß¡/·A·+_Uüòâ7>lm§F“¾!ºîtÄ÷<ˆ‚é|ô†;ʦz~§Æw¯’`êYM¹ïÜ6NÿëÖû¾¶õ¾+[Ú˪e¨Þ÷€ê}©\økjd¬+ªƒe…·¸VuÆ‹lAïe«.Öˆ œz\QèyËxüa"Ìý”¬Jn2YÛ³¬ô­¾ & ¸‚ÀlÆGN±²žF¬WdgáKk­¨TÄöYTò í€V„ Y¾Ô­Qšey,Æû$æe]¦¬uô¶H¸Ë°ÎH˜F;6Ã)§ä¹2™h 3ÑÜï“Äh¶¥-Yèé%Õks ½Y¹1ëÅpd gUM·SÄlÛàèN©µ]ÞªƒÁS‹…Q•uã‰`Ž "QW4Ölâ1r6š—·¤;,yUš¤õÖÉÚ)Ó"¯+÷Ka3¨æSÕ‚­åKj› ~Lʇ‰¶{)Të³&`C¥åÐ…L² 9´”•C§E¢Y|9›–¥Ezgs »® A0dYtcÅ:š¨$™j¢ÉʉAô­†éU$ ïW瘯•åõÇ9ñÞAoâ¾M}ç{™ÌAÜŠ­ýY%ʺX ã^½i7{¸û©¸õýŒ­ùšƒ’³]Z¯œ3·³ß ØŒ‰uÝÃ6ë–ÐÈá×?LLŠ0ËurZÉ{V Iš¤hŒ>tA'¯æû{ r¨U«­•J¡ºòð«C[Š&‹¿ÖÀ'aü²ûlZb×ù9Íë¬tžÞ‚§´¦S°º¹‹W=‡¹}~¶Žƒ¦Þ‚Ù°T›‡Ü¼â °;ÿgD‹õy)¾¦ ¼O—…ó†]–6Èug©Ö§^½Ðò¾ÂõVWá &÷-ì†yÃG8_Tø1H¥ô¥ìkípígßR¯ß–1Ƚ½qä\mèwèæþµ1C>;À¦Â†°tÎUI ÏQQL?ðQÄk?ð!TnàCŸÄ£0i¦A Í)()¢ÈT:gaa¾ð4³Éõö`Þù©ò\•CjøÞ³î FÏ+fY.§a~1‘ašW_ëÌ×íÀv;Ó ­Ú2Ù·Þ2ú†æ]ÁQ:{ä?w’«u4ŒÝÆ )e. zÿú˜Z~Ãv}-Â\K1‡û=™¡ŠÓò(¦Q!…9h4¤¨eäqîГ÷a×­f©Zr’ç:ÏxϘŒž[f.â…nÕ»xž*‡yæíx@ÏXs…´™Hk¤ÛÓ6//"£™"Ê›Õk¤›ÕÒ¼(YâÞÉtüM‘–W4uz÷|:úIÿÿ£©gƒendstream endobj 122 0 obj 2411 endobj 126 0 obj <> stream xœí[Ys¹~ç¯à[ÈÔr<À`€AÞÖWÖÙõzWf*U9*E‹Ô‘ˆ¤Ö’,¯}€9Ð=39C‰””*Y.U Ó¸¾>€n¿ ãHÈaì*âx9xqd†§Wƒ¼xxôç’ø|:ømE‰ÿ—púx9|9u…ÚÈêáôdGÖf±)ZCº¿m:4"ŽŒcXþ1:Od$3cÄè‘bì8„T†QÃñDG+;Z‡ÂË@]Óçó±rDbc\êj<©5FÜO"c3:öLîW”¤&•£ú4'rA$kðÚ•&©Öq2:ëâåd’fÉèSΠtÆFÄF@…n¦2‰L¦“Ñïca"¡¤k2ôɺ_SëŒ!€ ‡UÀÍœQ3EµXjκ¢ï §¥¦Šb“ŠrjM>'iÇBZר¿¦H7-k½¶LçN5ÖÄzBˆ±~/‰á"GÇ‹w–j‘*>FâxP)»K,?VxOšÚåö•zX@mbÍ~»qiÞA© –ƒŒµ*Ç;¶ÚaP“s`¸-LMÛŒ30rÛp¶3^-hÂò**ýÊt]“Žò$Q%C¢S]Š$'×…Ö)a*­©†k2>°›6 ŽuÆä^à¬ãtô-È¡´4Æ ÌøÞ'JÅ‘ŠíÐ7šÊb -àå¿`ž¬e:•PýçÊpSN½vÍln=Å»’‡e7cý©àJbQšo ÀO÷÷ÜB “°8‡¯3ÿiúÇ\BI,E¤Ôè£#£Ô8 ¿ Ô4PÃ@êø*õKIåþi"¤_h$ï“뢚°¹@*rAäÈK"/ˆœy IÖØgØî?GDK"S"5‘?²z㊖$u0¬å}±f˜Ü¹><ÖW°3¬Fìòp:žì÷3ˆÏ’' 5?{ F î+Œ¨ù7]>â%ÀŒ †ö ž„i|¨¿‚V>Šz{¨¿·FÐG’ ñˆøÂ|Nä7(9 ^Ã÷DN!É^ùCSLµØMÐRîÄ—ñ¹f݆Ëí°e×Þf>Ÿ4·Î¾¶>,ÈaA ˆÕÊ(íVÍp¥¹ ½$†²ClÜ …¾ñV(‚Ár ¯ö¾=T+ƒáL¥U0ìI¶ëÆð9Íg »ÀÁÍ VÃÑ km¥—Å^<‰W"8™ Uc#³>ñ&ÆIš¬PS¶³L„‹«þ^’r€ät¬c?BÑRw·ãZkŠ"ÂhŽiêl>Kï#ü\kn-B1Þ&u äÔÅGq¯ˆÜ¤. $ÅzÁ† Y[9‹ztº=¸­‡e,˜c!ë‹1ÌjÊTE†Œá†k#K|ðhMfi”¬•‘9!J‘ßÚèy¼ éZy3æ<(Å¢;ìr¨§< UK°¼°‘rþe€˜?h8LÖØ§–´>[Јß}:jˆÆ¶â£T+ýu<Ñ‘V©±¼WæGšÎü*ŸžÃÈÏOÚH ÉËp|ÊÌ0Úšî¶ŒÝ)CÙO]š@½&§ÿ¾ô/RºÕ;ñIˆb RuPËBL󼑉Ӵ©EŒ§'‹Ùƒ Â)#˜‹e‹&n‹‰•ñb—Ê\ÄI×w Ì­óÅ­XÆœ{IÂ2Ö™&,&™:+Äy3,íZÊ©ò³Ì®N›–_¨(²»×äþßÓ”+މ­èƒrhöŽ(j•¸ÿ¥ÍM*)3æ¾Lÿó°æÇÜ-Ì€’lÊ„^×e·Óî…%Ý>Û~ÇwnIYvNeÒ”â蟔[…˜hF1Ñ2.j’Žýòc6JW¡Öy ¾õoéAKH.`”Äb²«f¸S¥ª†¨(ôü©zöÁ‚²”ºjfBE¹› ¬pÞŒü/ŒYXza 8ÔÄé SÂ^A”Q®¡#'ƒ ~}hˆ™já´ †«Ö⊱êÝ’x8W€ìJº ¸)ýrt¸q Š‘kXú`ªÙ#¢>#ìí°Æaä˜ßxÑ…ÎÞ€jw0ß¿µÊöŒ>:`¨`ÿ†!¾ÙŽö] Ѝÿ3ÿ…WÔeÜ¿? Wë«„ïõóad°„3=ïB‹cKaµ‡\±{«<-Ù/÷‚…]Cè´”HÞs‰ ·G[ÄkÓ* 1tÆ'»°¸­É ˜Á‚÷˜LßðF…éÐmWǬ1¼íkÝt^ós ¦uˆ¶ž×¼TÂDYMw1Yìb™f]Âð)vxÉNa»Lñéà)€šw{;ØÜÞï9Dà œv§¼ÿÖòNÀ!=%H§Ž¤±õƾnñœC¬:oÂìOßÞmÀ8¾'kÕ«ú'×¾ m Jçß„íû (o×Dºð£Æ²Á6×sœÃjÄ ¶ŒÛ©~·ƒ{Æž£Òû^ º¹ðK þ äJ|ÖÓe2x‡¸†à !¾¡ðŽH˜~€¼wòç=®0@÷7á@…eMI‡uø2Öüø°ºÈILJðööŠçxQ.ó‡Y#Œo¯yš; à‚݉½ÞŽmd2'ü*g\4’VhŽPÎ9ðEЫπZ·ÊZ›á}R‡°%à#/»”¯»(¸íq hªµ–ÕJÕ‘‡¡¬>‰ã 7_¯Á×ã@ŠÔ`^tO÷ƒƒÙKˆ.ãí\ï¸/Ä!9–OÏÙµê$ãW°ÿõ? }%r—œÁ3â]NüÅ‘ Y{[”%·ERfþSþ¶Èm]âHKW$G‘§!”Tþ),wü6Ê\+"GVÛ(QÚ>"Ò±•bôÖ“2SÆ;md*«Ñ/X{¾¦ó—þ¡ÎG_l•6Òn¬:dU«žüÊá•fYñ‚$0/=̼‰¨÷’Yá¯Ñl©(ÓÔø(ÆOØë;÷ű´ù‘£“Y/±æ¨syÔMÓ$ÖÕ=¢fh6þnhΡ2(í /Ƥ»¿‚¸,kË9;!~ë/iåOXñ qëi”­=!c‹pX9·¼!CìíGdÕÂËî'mzC†îvÞÁx~CV{C&Ý´ìó²½¼!k1<…7d•öyCæ¥ãýáS~C–ØÈ•æ7eÔ¡ŸéX<Ý7d½#ŽÝŸ¼_Ÿ_ŽõÚ’îìç§c[pßÏc%ûó˱‚ÜáåØàùéXAîðtìp¦‘îQ,¶ýñ{Â``öÀDôˆ¦¡Ç÷”~j÷âóx"`KEÕVy³Gäñq$N=–<$—[-,4”ÇóOÙ}åÂàaÁ®œATÏNìÌ)±Åâi¬âÏOމ¼×“ã7ÓÁ¯î玻 9endstream endobj 127 0 obj 2468 endobj 131 0 obj <> stream xœí[Ysä¶~Ÿ_Á· S;\àéT¼’œ(ÞÍÊÒlùaJ4ºb]Öû×ànÔh¤ÄU‘^z@F£»ñKGB±ùo‰£ËÉûý<8½›TÅÁþ_âötòˤˆ”ù« 8}t|˜ë†BeTfÁüdGeYÄyÝ«²Tÿ.Ó q”ë —“iÎÿ5"*Š`þq2ÿã×é<œ‰,ÊE&§g¡®'d’O-hJ¥Y«é|>Ñ­…¦“rº°…–ºM©2™ÞS?µ9·ßï™3ØuÕ‡’q>½ªê«´PÍÀU)«{ChÀUecßÓØ¬¯Ûp–Fq,d98Ÿˆå/=»3ê3¶Îd$‹"I§ß…³$Ê’$+¦‚ê Ø¬Ââ>ÝTd,3>ÓkÈþ½Û‹.•ªä}³ û¶ß üÇüoZ¬¤²b%¥ŠŠJªæËÉ×é‡p¦¢4¥š~ÖíR=-•²-‡Y†êž˜€Ê$Y–µ=1¾Žˆ[6³KÝ¿ÔÅÊ!\6,¹$rh-Ùzÿ"fe1f;¢m6^Æ.¬Ê&yØ•ïzÚÂ%äo€ È}w·r•¢Úí«²4ë ¦3ó¼\&b¶³zêi"]”džës#?3™Q¬Š@/-5HѰ^ÛB-2ZàâÜŠL§&iŸFú´l²š¦P¥y*9ùk(òHh†ÙZ÷ÖÒj´Íú*±îŽC$ÀPE^ ,3ÃÕ¶ú!œeFR•äã²¥ëê[ý[”+™èÊ2’B¥¼ÁÆXfGÍ¢fy£B4+©4§Œþý‡g5ÍžËsÑßs¦ª@)ÆÖ G½ýŽgp å¶£QÇèRäf‚Y,x[¦³YƒÓAAò\U­oêT5V³‰]G©k›ÀÚZpT,uU#d•Wæm©KK7”>Št›,6;!çÍyg[[ê·ñ=ÑyrMä%$‰¼"òžÈ»–”ýC",ö-·ßYjËRÏÀBØV™ÓJ"‰déŸ7žÖ-¬{Aä{ÓÖ>Œ@nV?öPÛ0VOpþ •%‘‘â‡Õh³ù¯!,Ÿ,õ­3©¦p“Ró‘`¤w·0¨ð&»ôáþ+ìŒ Û’˜É;@¶«1b¶ »Qì0çŸß ågìù>ÚÉtçUDþ¿Ó/«ûo¸²xéïà,æ_GÑj|°Ô~øÊ«Ï”DíîH2o(c%ôÓÓ—pÒluñÎÇò±€¥G°Yr*œú¶ f¡=ZÉøh©ïÇ‹ ²ß¥÷'8ýsßìjÌrº†`c•Â|ò Ì:;[­hޏ] Èß-5w¡‘Æß'½‚˜«¾ÌŽÌ?GNñÇ6ò ì!€°üc-Ï*œÂ~™Ú`‹ºÍ<dǵߣ6÷ðgíÝõÞ¥ÁÖÚKqƒº€ŽžÏ@b]“`¬ê}—¬+_³ÉÛîD@ßýÏnjïIvÑÚ ²P"ç$ùÓÜ«%RÚV‰o‘ðæóÞÀ=¶ Óìâ†w†]×Þ=CMc2þR>p‘©ú(ßK¹§Éÿû›¡Œgl#½†èÏkÐ=KýH3Õó*"xÂlAo XXY…]"™sê3¬»ÖqÖÂëÄ„äξÚ-ý¦é; 0 ùî­çv›¢†Ÿš —”ú:­¢8++¹fA$}4®`7¸Á?GäJÆÑ±]"O`Ý–—/Ä+ vØï `ÖþÏš·4+S'd‡ï`äõ),£¼H’r öÙ‹0uô¬.ŽQÝU’#UeÒ†;OÆxÞûñ½¬‹CŒ¬Å1%zÀqAó=i2Èy|*b§öó#qBJ<ú1rQ§mÌLSZ‘ÐÉ'èÊ[—¢•7ŒeqÙû}•¼qfÍî; H²Ø$‹yž ¤ ÔÊF^fj1BC:äjuäÑ‘z¡x}O|ÝñŠ,N´¨ÅQ'ù b‚êm(49 ìmP±ÛQ©§(™o­(õ¹¦+µ¢ŒÆ‘ižÇci7–c4©Ž°’ïE2êFÜÚCÆží;V²fŒ¤x5^ :˜øª·¢BI9,¨ æt@ eà,\@ø'† '^ÞözM£Öú[m¬²î$e  V[#æP×½²ž¥¸Iê5 7p ¹ñYBøŠ‹ý׬•-@³Þ=«'òŒ]Š'ÓrÜôàë=øzd©3K‘,Ý…iëbgé D—Õõ^~×ôÁ`—/^Ÿñfºïvì%cŠW`|ÉKg$,yŽOzƒÑ³C4tiXë*°´¦"-P›“®…d*ÎZ¡¼‡¬OÛ7Nbƒ{Ážyj0Ð!ä$áh䵕ïdæ`{ßfb9FeçÀ_a¾á à³ ×uî)ÕÕAs\¦I{up2Š<éz×d·c#¸' ”`KvÿŠì0lŸëœ±4/ÍYæŒÍn„ïaV#Ü3xÆÝ¢Øu_ÙÉL œðØ1uû7.lö6™m*¥äX•ÚòŒJº.“NÒµ,ÓHI“týÕø ã(Y®ÕddTFZÆq2-Ã<*ë­2S…Ö<ÆtPUŽk:Ý e¤¯,úæ/XkN¿§žšfRé RÛ,eU3FÔlÛVÞ1JU:ßW…yg©$ÛÃM5Z"«$TÔïOSÖ²$/„j¸4í~ õÔÓR%éÞ4v™×Ú4v£‹·I“½E…÷ý­P69®«;hȺÖò µfª`Úì±ãYYC ÛºLA01dJ©éc«dW)áîõ¹$„žÂŒš4.Ö£çtààkž“éIÕ¿CN…v’©àS'Õ†3™±y׸pTT¤ä™3ŸkE¼<†©‘¢n6¸U½œÖ@²·mŦÅÖ™Za}Å“4R¥½|®É¬Wé± Âxdˆ¾¸C>Š\ÇQ^¡31Ãw9èHbœÂ˜Þ:ài—kåbsƒ¾åÇ"Y…-"ÿ:xªÌì…´î¿vsÎ’T›#…BàÙ~w¶~ë>è½nvuö3j&øÎÚ‹÷ªœŸÆ·ß÷(µ£»)»ÊÂ{m5ä\wb@®@âÊ£7gljŠ0×[íúÔç«*€åðd»e¼³ôrΚ1ÿ!mfÌ ö¯Rj ÷©"s›ÔO$LÐ)»*+¯r`²¾ê7ø,\4k—„¼}õÎã3šÙ«Š“5±ÚuƒÉ=R@ÛDîÑ}—è x»fIÎ8‹?}Co¨Q®Íõä¥sp*‡¯=+è-¢i-^:-6üàÄ™öâ`§Çì,€È¡” ™DÞÌp¶ ç>~QŒäÍ~Í|™á;óÉúÿ?=²W7endstream endobj 132 0 obj 3046 endobj 136 0 obj <> stream xœÕ\ےܶ}߯˜—TfR AtʺÅÙX²ly\~°]©½iwã½Y³*Eù|o’@7ÀÃÁÌjI–°\ݧ/hà™(¤š ÷ÏŽ.÷¾ªg§ë½îóìÕ7CáÍéÞ{MQºÿº¼|t9{¼² ¥œµEkf«×{¢hÛFÔ}¯rf*ûw[Íj)ŠÚV¸Üûe¾Z,¥)jiÔüla?K¥ëùI(Íl©¬Œåüéb© ÕÔµœ?[”…0­Öó'ôñ–ªØN¥ý£ß$}–JD-–õüš~ºZ6%ïïÍbYBHÕòº¶­­QWʵûÚj> k5뺭¥nçç¡êÚ=ã³NfhIWÏé+«pKö_¥TÍ| çÉÖĦü~!ëBjÅg`g$ÛÂn˜fÓ¼´$Ööc%çoùâõ@ØhV¡«kH‡¿R6,kv¶ømõ=e,+ÇV«cË#xën9M»eµ|®ça‚WÔüötÀiŠÇ°.á€/±¶ß„læ¿$,Û‰2j°Ž•ãh[b<€ÔÚe¶‡|ë==®ù°–pB(;žøÕe»ÐOÁÈJóq§X$–v6éÌí¸Ò³bi*3P¬+Úδ’Ró†=7¢²Kë™ÆYkÀ4¬ƒÔ-ìÖ=¡Áf U×ösíZjÝZ™™û¹RýpWh®Céîߺ§¹4f,/®£ÝIÀ±ãðs;eÅ¢vëb¢Ûáæ­HÃê#ù³Ü¯-ˆÏ¶Ó´$)-Ï¿êA¸ÑÕ|!•¥M#Ã4ì” ®ñ4÷éë [´UUe‘}þŽFøf±4…ÑU±Ö£0ÄPµm•צmxÕv`e \Ö}«Z´Ò/ÁUýv±l'&ÊjQÓAi[uhÚq£QE£jϯ ¬ØN0)`’È6á íçKPuM]±ßY+Ö×Fu=°ÙÞP7ô=gÆ= ,¦4Ah†‘‡Õµ›!šhtÏ`Ýf3j°¼é¨ÇÏZwHÂfsÎQ!­©ß#ÉÀnºç2RFòÒûVïÈ‚†õ‰!çœ ‹Í»»Á}¨pÈ9Ø‹&ë ’bDþÎÕób«4C6ÏÞºÒœ¬úùžýûÏ{«¿ìâ±²šÌOM¼Ï˜Dö+´kS¨'DÞMø˜{veÍvŪ²¯Wˆ„;«®ÖñÀ¶’vŠXa …SƒÕýª¯[ 9lx·Ï_©2D1¤(Çnó-C–BYYÓNôË¢ª{¯d(]†ÒÉPRNN–F8]󿬏­ÎCé?Û÷tÔW­³P¼„Å*^Qñ–Šk_T$ª ÐÃ̈«0Û'¡ôc(­v … ­LÔJ6ÖØëõcB¶dðë[°ÂN­ùâH£S*žÃna³7“DÜ‚nTzñqè¶ ¿0j2~9þ$Ü‚r?¾Ý3åÞAʽÉü-$íí' íBý˜Hô(|¼Oî ³ðÇ9²­sý~>Xðôccè1¤&†ÓOÍûaYßME*w² ²d™9kÆz™MDA6g,hÎQ‘qìMN¤o`¿dñÌNa¿¯©x·ñ2ä³@¯W£Ò}Ð;KNŒ±´Öd‰$_/s=‹»­ÌáÅÃe±Ý<¹§f…'^À„ÏVd°œ0bBjÝ#e¡œ\„¹_Lžè è^†ßå"³[ŒTl7°É?ƒ¾†²VâMŽØ˜ ðG`“s ƹë¡kÐoF”ž‰û}+°ÎÞÁ XäX¿p:צ}ËIX¾£lýÿ)¬òu {8‚_±Da)ÙEÎð̰‘Çò-œÙl¶»œ%»OƒŸBéq(à š¹"ô-«æ—I Û°‹}އPŽXç(x»>–Î6†íç!¬ ¨„©gÀòû@e²°h  %J_…RJ¿å6“Ä[åªbõÁÖŽ÷ê6cD‰ŽAñCtj4SÀ ¾¯¸‚Ô€ ´o+¾Tºk}° ³Ð—…ê/`/‘h£­$!'Áÿ'øõo ”õ-sÞÈöš'Z/g\á5(*—w0›QXiÞÉöûå\JŃõýÙ•Û¸}x"Ï…ð;¬bG)ü,tÅQR&?¶rÇqÚöCI 3”+‰ª¦rO2ÉxýÙÓJ6‡ƒpðESŒ@Çåø4MFÅ'V ¹µ)Gƒ eæøx•¯£É.Á@Ȩ– 2Ù’ãôwÍóAQ -?=GY€ìHîl‘ï¹V›NÌÓ­fKžoXØ.ÉGOQòÑJ\ÀpûE‰¬N[Á«8hжŒ×@ ÓKŒ´,¼ëER‹;è×$Çe¬³¶dܺn´ zF{¾ ˆýeõ¯Ž(šY`¬H¸™Ò*Éüâ9dÀ.»˜LiKx¯!cÙ™¾"“¦ MYr R¦ÔÄ U€Ò?9eg¥BL¡*ƒmœë—` ÿ{$íÎT›dÀUØŽa,M­Ëáb„t,Àň?|UV3kØM×·Z\ß*eS ··Ôb) #Mm­pô«Z!:G-| kôªÊ2^a™Ùrµêæê–MOË —ë|Õ—Tç…ßo]¹­…0•Ó•~¬#VeFŸW¡>Æ ì™5{b§f¡”¥¸+cÌ0­êh ½m$Ká„À÷zA= E)\sG‚ªåÖê² ÄogREWçš2¡½j­Šl8ñ•RrÄ·[)¥Vžúýw1]§-Û£ì}é¶*šÎwa­ºK¶(eºkt•g¬;_½‡ÙPå4ôwJ'ì÷že+œ#EÝM Cóã£8kGØ­’qçCC¡¬ŸüëœWùëb¨¡èVO\eTµ)ÊN{”sE:rk’dÏÍð:• üÐ…ô0Û&‚-äŠL¥m¢º ½Xý9wóý˜•ëÎzè2bd' Q#Ü ŽÕ’ Á7ÅþQÊ·Cb‚Ì$‘5èìÄà׺GA-ß±N ÆÄÈÆ´ ÖsßizõnÒ}›ôX†ø«ØúÚá"!6{­SÚÿ½%ã “Z2Û¤áR’òUVÂþºc(-ËtG„,%q¦ÅenñHõþb¬Ò–~>ËÁÂñ“±ðßC‰LL²V>>3ïh¨žÁ}ffÅÌÃÙôÄ­Û1MÀ±Ÿè8‹ÜøI{aãÉ;™)dúŒŽ%6ŒEê6²TÝô‹bh\ë¢Òe~˜l„QM[}ÚHÉd8§AÑ4¥›fÊ%sÿÇûšoSˆ–žõˆcé@@Öm¡Œ;ãL"òwº6Ìí_êÊ©½˜$z³³yïðN¼ ƒöEË÷áí"¸Å=YÆ!WâÖ¶±k2Ä!““—ƒy¦/fß+¸ãƒ:Ø78„D¾†"‡o,ãC8Ú}¼Ó‚Ã]xAhgpfñhã(St…{ltd\‚©—¤úÄ]´e f”´Q6†'m`ÊÞ’Ññz‡c‹·FÂÃ2[²Êp@Éó(ì§¢l´Ç˜èu!þ|ƒ“l”¸„:²Âðrx~IT8 ÞÍÇáÍèý¼$Lå?Ç_‘Ì`¤Jülw6u ÷"Ä-¤Å©±×= Ó53RM,5ûè ë÷ßĹ'ÃñŸ*Œ ù]G3¢°u2Xl¾ãÉ àl$ÄȦ˜`\ËÆÆ±‡¿KÚÖ”¨„˜ÞÈÆ_åxx=•äcö˜¶›Â ]Ýî]¬þi´ÖZ¢Öòñ‹}B$Ja']kªŸOÕŸ¹ùùBŠPÕQ’@1]Z‰×ñ#X#ÖÚôh‡å­ÂycÙ÷(› ›Ù竲JòH’(nôô;à"€Åf^6ò³…Y6ïÓIrbMQ* Ó7¼)9®) ΀ë¦!¶b´Þ·ÐžšÚ´]ÕÅr'ç€qÊ:Fù/»XÇìa¾^h+¡ÆXÇn;„Úí±·Žq¬‡Ù˜€fI#4ªe!‡àM3orñY´ ô:?²@ÝGz›YGlœìºø¨üÞ ;$éB\b"V°XÆYRÀØ1HòåG®Átrm4Xj ÇøfOP¢¥÷jÒ….ZUõÊI’Wa—Ü DQ,ÌÞ.ë‰O!rãÛÒÂ$n¼ ‰½„ň¤©ùa]j[ñ°œÂñÉ(2`L!U]ÁẊúñ£LŸƒÉ²CeÚnx˜@‰÷f‘Æš(ä½!é‡/çü¸‰k7Qð;à \%Nܺ?»D6 ÎeÆê z…XÛ²V?ôn§.ÕÄë~ SÔ›Iïª-”,+þk\Fa¶©d}Ã<Á—P° ‰~Ã…¸\12FR#GQÝŠêúÚ=Rü½¡™Íur›}ÆÕäB}Ôg ¿'þ<[íý`ÿýª/endstream endobj 137 0 obj 4032 endobj 141 0 obj <> stream xœí[ÝsÛ6÷_Á·“n*†H‚ÌM’Ösç^ܤŽÚÞLÚéH¶üqµlÕ²›æþú‰]?”-gÚæš>¬¡ÅÇ~/vÁ_¢$2Jì¿8^î=;ÒÑÙz¯ŽŽþY7g{¿ì±²ÿU>^F/§f¢Q—y4=ÝKâ²,½YUDyfþ.³H‹$Öa¹7ŠÆÓÿî g2š¾Ú›þýÝh:žˆ<Ö"—£ó±Á2Õ£…ƒ"©,ÏÅ1Íh’—2ÓÆñx¢ã$y>ºv+\9è–0oÆ“ÌbʲÆT2Ñ£ËqjÁ2eЂ~f“"saÆÓrtáP—ã‰LãD+=Z¡EÙR u12Ö…;išü¤kC§‘ýtNë±¥« ìïR½¨PY¡Fg„K´¬7¬”ªä;bf0Üç\•³ïÓ¯¬Äÿf”D*§$Fò¢‘¾á£J¤ˆÓÔP?Qq¦U:š9hé E ÉÜÎÉ‹J;Üt®Ý¬ ýoøJÇQ*£\BpAà·®PÖ 1\O-õcÌñâÐvê ·Þ¹…´F%{8 ܬܟUˆ4Þ¨o‹Y­cÏàèݺPòÅÓP²ÜfPn'€¨ÁòùÂAôë ïÌõ࣠Åú·„äßøÀrâ‚7p·5® `ÖKõ¨Å®˜5‡ ¸…D= QUìò- McÖ„|GÀ° ¾îðw;Êl,:.ƒënsœ5\!£Ø‚¨‹dÌd¸$ËIP9TB]AÒÖ!î]ÀiŒéty%!ùe•Ê6ûce+0†M¬ЩªØª‹W5iÕ.ìï3V©ÙÔ$Jã#š±K[[P™Î$+N\QâŒêlu¿ŽÁ™µ %ì§Sªéx5!wªä\Ó®ïÇe¬ £3›õž×‹d 7a¹°…ÓÌ]wÆ\“V÷.tì Ú]ëZgfÓi˜2›Â_$ñ­›]Pÿcîà"¼%pJ Ë(Þøw?䳆çE[U¿mF3Ñq1Êœ‚_iŒª€—±öYÑ(:CGyZéàâA@!ö¤d홃^¼z”Žó˜Ù€¸ÀôÀ‹í_Ôž•‘^¿¤P­~‰*ÊXTý’w6KIâ\JYÈQla%D*«LÔ'=8fnfE±¹×”Y\Ti±ÕºTZ^™¤¹6¡¹Y¯H ~ÆÖófš(&¥*Fß±E61cpÄ9¬¤¢r‚åÌ­}å E}þÂö ì.Z•IauÀ®–HÃÔãî.Õ8•o²BKói?ŒØ>˜Q?Œk”´€ SOLi—2Ü{ëò*–™P2µBͬ‹N^R²®×ššb0CbÉ”á¦(csx×Ëmbê~?¡´7˜¼æTwFsÞéö–r´cao¤U½­»mó¬½)ë»±Lðg¢{]¨éÆp¿®ˆÄ -Á±“oÒÈ4õ’è‹J½¤¥S•F¦'Æ#àv\ÄGÝ®L¶˜Ý—îXlÖ \–17B{H¨äÙ×EdÓé(מF’xTɧ²SÆÕÚÊü_[ä¤áÙ¤¶³“Úìu")æRÑ—Š CQúˆÍÔ#ú7Ø£©»nâ0¨Cá’Ô¡^-Ù´÷0.ãi¸þÇŠ:,&²BîÏpÔ†C%ƒÝÈé{QaÿÌÝUÅ_8ˆZ[¯ÀŒ Œÿ‚}|•¸#ãAU‡=!•* ž³jâ̯ãÌÎ)D­üÒ :Z†5ç±Õ)Úo5¼Ø€¢Ï5_ƹΙ{8Ò^{5FѪ:=Ë 4xܪŽxoGYä¿»/rìÓoÙ©fÞ©7Ï£% øU4”&Ã*ò&Fí³óB’Ïl›yJsû_Z•ЪÈM^8Þë˜HP­ÈËn\6ÑÎÚd$&I‡9ŽxgqƒËs;¥J²úQk¾‡"ê®jð(Œ’Û}Æïs5OІôl·ñÅÇЮà(ÃÅ^ߤƒ¥Ü%=$ð%D°·žÆEäÏ·?—¸%µ •&…É Êj諸wùcÒ,1©Ë(µ?èjÝ¿@*FaÿéõoH˜éÚ9”ó{(ƒ v¸P°ž°û‘ݾ ÝIdS 9ì|}$±ïú"_z¥.6Ìð|eç‰l €ÒÆ×YœÛ+Õtœ'I‡¡&ršX0ôFʲ~Rr…†+gb±4‚7ã9d2Kin;k½?eñb]_nU¯°™×‹yiBO7yLªL¥€Êf™+€qfVºé¢é¤ÖUHãÄLÊÙC; Î;™Ôf9扖…A¨ò§Ô§¤¯2e–ùÒq0ÎàäwÍY‚3k/¦ò,çDÆ4qÐèk*ÍLÛ:iW…:ãb°ÙT–Jy};Æp©u\–ºŠ*¶RW$"šÔÞÊr©Cdcó…óI_ovÐZ0Gu«?Tê¢nèÜÕòm¢\cg¯,£ Úd£ŒeÉZºµ2iÖVF¾6Ý:¶Ó k(_}'ó>§€öaºÆªJ”xc÷àÕÆ*#RFG2üî}¼Ïö˜¿«Ç¶‡ïd,ÉljEàÅ‚@-o< M«¶HÊ‚ª$@Ð$Á7h¹Üa6Z7Tg*môãù$Þh>Fb˜ ¾kŸGîfGŒ„‚À9<,v ćmŒ ÔË-q*ËB3.çÍàè7öfgÕÓÂ;xdN¼ìrÏWWÖ𤓊ÉÝ?ƒ®ƒÅÅûŒªB€:Ø”{j½¡rÏ;æ+èbÆßq5Þ¥]<íø÷t­L |ÿ{°Û˜”–µ‰}çQqÿ/ñ(‘ènˆúÿ‡ ÁWéþAQE’ £-xñ? ðþ‘}­uW|ß2øÃbØ^< Ûæ[pe‡_]~\nÖÒ/ãÿ㱕ÕÅ÷ÈV¬¹ŒUX\§ÄoqCü·[™À¿zZ)·„KÍj£pŸÈíð[î¾ïLqc¿HÁ-¿sˆ‹MûN u™ˆñ¯>aÆãwE¸AöTž6ø9îc?ú†žö~h ôSÔ fŒÌûA„§QÁæLïÂú>Ýÿ´D‡_ˆ]_Ïà çmÆïO÷¾1ÿ~žzÉ¿endstream endobj 142 0 obj 2759 endobj 146 0 obj <> stream xœí\Ksܸ¾ëWð–QÊ¢ È­ÚCl+‰c;Y˳µIesi$YÙÑÃÒ(Ž+•ÿžèùq8’FÞhwìKl4Ý~ãOQ+%îo ïíqêþT>:^Li¢N£2.M4=ÙIâ²,[SU‘ÉéßeY¥â¬ˆ¦ç;ŸD»{i¢éßÙäqnÓlòG½òÐAiãæ˜ÄÆ*³rº•Ÿe‚YªPY¬K‰z^#¨23xÁƒ§pZÄàœvÄàOÂûqÝ9D¸nAMÄþ1ýÓÉ]“ÜßîL; ùýÞC/Å’¿cò *áò°€KœÀÕÄ4,¨îíòp¶)l"[iõÐ%¿b“ÿ†¸ÇPðøˆÎá7î ŽÞÂi‚Xrw/€TÞªq«òÿ•«r½Õø“ѸÞÌ!O·*ß°Êõ£©<íªüNšÎ¶š~2‡;[Óÿàõô;qõöq4þ™ùéëÙ˜\qúúÊêNÛ¨IˆÔ\¤ÐŸ!.®»æß‘C0æ­Wk´-“·Ä>BÖO!£+wõi}ë¡?{hºÕíZ§»êSHÁó»þ±û‹‡¾ÛªfÕ\BºK¸!+t×;?ƒÊá¼f\MÍàVK]„ÇÊ€¼rõÆ aÀ‘rhœ¨u©w;­4¨3eã"@ÅùÛ¨Øî²i!Á¯Kªù "ø%Æé pBºŸ#Ū9„‚½„Jíï~bðvL ˜îãÈY?‹“#§÷ëÝc >Oà¦oঅ€žÁi¸@ÂÄ‘æ3” .¦p³ü‚3Hû;a€·PP'pCkßѬÖs[ìoõüØÞsSzÖëžR³ÉþÔ«ý¬°Ç’ÏP0_ÖôGœ€±Å~Ñ*‰­`?õ³ôûÂt„‘à˹õcp»¿çJù›l¥ÝVéËoܾsÝn|7S3¤à‚ÝÍCÑ5< :W`6s#|ð=-Ümt5íùƒò¸Ë—Ø©äš%|¾òØ™‡=) Ý!!â¡'ë¡Hx' ô®\“ãÊsࢣÞïxN€e-º+? C@a6/Á(»ù2R:x°T¤K©-bÕ–(*%±ÑZz;8U*ÓUVåÇ“Šme\EU§^eUèw Íôä}Ye³Éš¤Ôj’7ÔŠ¬ð” ÊLã]åyµR©³ÚPf,Åœ—L"4ù^­ÓÄ+Æ8Ã7bÅŸþ‹²™hdC›WE„š -+6lZÒ÷ëŠZj²Tâ.±…[#/lZéÕ/-'ʵœê=I&FM~Üm0²†öGj©IÝ{8Ò9Mšþ3 ûn!CáÍhÁ=$¢)af±É2S8ž y’K„#X)/0F$ Z2f/­hsEˆ>æï8拓4§çÎãĦ–’Z@Z¤ ‚¡cÿ"‰ YXy®`+Q+ÌVD¶¤ÐfV9ÏJV(YB³¬-2EæOlB£Î@3ÇšKOÛAA`Ωeâ$S+F§¥³{U:ñ”¡ærÇ!ñrYY•ÎLLg†¬h:'GQëШ<[G‰•êÊFälM]ÆL)⇣(á ÜÍüê´üÛÊx²¤ +Ø÷ŽHr·y>¶yüxË=èäû-?8ßg æÿ¤=Í}|[¾c¿^üÖdôŽOÍ„ñ ú‡W'EñËzð!‚Ñ•}\‘oÙ!ÓËRªq(iÕ¶¨Å,ÃCZÄybÛ3?ö3“$=M¹X™™l e“;9wÞ¶’Zcs5x×èÁà©ÅøšAœ~ÌàhSóš²Ó8•9„É/~€qÃy'Hߥ <ƒ;{øCœœ™¢×2žO+D€¯ÖDÓk ò¿DÇ4bWó&D¸ ~Àq‹ªVdJŠLI—1%ºV‡$¢—Ñ·$—*nÜ’6dCasÂOÐÒzFŽô‚¸R²™¦Š2™NÆí[g¿ÀPŸ%^!ªÀ‘¥n¯m+­fÕío4„­g—÷ýmDåæ‹,ú ÕwCn䮵N%ß3ŸYWLµÊ{Xkãž½¶®ðçG”ÖªœLC°ýÁÅ`§*‡É>ÌêÖÉ`4nÁÑh,¤^à䊘ÈZ·7ÏoVףɿÑ׿>ü×Ú÷}wÉÜŸÀO›wïOwÞÓßÿâb-9endstream endobj 147 0 obj 2856 endobj 151 0 obj <> stream xœí[[{Ó8}ϯðÛ&,u-ùη岰,%û±ûmÒ´eÛ¤„(¿~åØÖŒâËv“RXÚ—‰<Ig.Ý>8ž+¤ãeÿ%1:ëíîÅÎÑÇÞ²ØÙ{Tó£Þ‡^âúÙß²€Ó£3çÞPU”¾“ºiä '=ÏMÓÄ‹s©Â‰Bõ; X7HœáYïMßìøžT¿ƒþ+EºaìýÇšz¡©×šr45ÔÔKðuOSO4õ‡¦iªlWFYeˆØMbÞ1F~ÌyEê÷‰œù™H‡Èä=ƒä>‘S"ÇPnÞ9‘‡D²ÃNN!ï¡­‰(á“J»áÓž2 ©ŒâYoxk«fñ0‡€*ënÑ,âL“ÌXþ…ˆ/ °‰MÕXgˆ¼€¼¨”é,¸•!EQ~·*›žÛrÉ«ª÷Z\ò/ŸŸÔÔÝ¢~/ Ì,šaMv‚Áëp¨»šzV¡rpY•*¶W‡™ùÎ)$;Î*çÆŽêc ؇p=~6ݘljD®L[7w²þÎ7Ù_Í[*}wO)«Ì[©_}ù%3†P–Æ 4!"7‘T˜zŠA¬†SRŽ¢ü0ŠØ­Ycñ} G …m9üÜ\um*ü4ÀxËQçæb¼¨Ól§¾IÔ -¸Ã~beƒf‘šïÉK^m£›ÌÌú-K‚æA¥nþ&ëãÎÀ¾½î+Ñ MÛ¤º6¨:† ,:€ãÇ{mV…ÀÖ@—[RR㙘rS g4¯;…®%Zleà4ïÆÌ!/Þž·jn«1°ëŽ‚×BH&K[Ôm²ÏÀb¼ß ®«Ôû<…ë½ Õ¶ÔŽšsØ{dž ÆÃvÕ`ÓÜyÑÜ÷ „-f¼6ûJ#ˆ*›(ñÞVÓ :Z·Ë'¤ðÜXZƒVl Z±`Þò –2pL:€ xž_@¹x÷»±½=®@ôŸkê…\½½mî60¹6¹Ÿ,|JÜáB ù1ÅßÖo4åiꎦ|M½³¾ÍÝGp88 ZQ6šØÜ†rÛzâó ÏfD3Èà7uzÅÒ ýÚT¼Ñ…@ÇúMWæŠéVB}®¼1¶È¤;ûwʃo¾â¬)']S¡œ½£Úªº Vë&2Φ™oZWñxNÄ ŸÀjLWß^at%uÒþÁSM‘Ú7æ™-T̆Ƹ÷PÅ#ÎOůshts¾[Ö'I¼ml¹¤` Âs(Áºý×iûÜæ0è2•QNoîbV°¼†ÍÍ69?ž­Kú6÷2X)^*0†¯· [·kðMÛîVGòm6ü…È‘­g֌Ū¬Ž7r"ßF"Êà7æPt•ŸEo¾õ‹­ ÜÞC²lN­mt…Úx뽕.–Õb¤ì û”¾‘XǸZ/RÕoô/"Ûk–ùíyãÆ»®y–-r†ÛÄÀä±g¼ošõø`I !“,›SÝÔˆt/˜Ð=õ5È:äØÇ^*²¸Ÿ*ïÓÒõò¹*õ¢8NÑß%1ϳÎ+à”Ž~U)ð„9 à> 3 °™ÝÈP‹ÒÚ> ÂL³mÉTH ©›ùbƒ½`µŽŒá$“a¼GÐe°uãÕ„Þl {V¡Ço–[w‚P°BѨsjk`1rÖÂáªy¥ahœá.Yý(ŒŠ.¬’…OFiÂß–ìCÍ0—ÊÇ*%Oá«’Ü^vJƒÙ)Jf5•×,k_ÿŒ Ñ)û<áF[^hŠ^Þ°f˜¡F“nòTéÚÁê{ü˜”˲{)üÊû$Ž&k æÑNîЩ ã9´”¹CGiè·²øeo*–橜™Ì1¬:ƒÁ=à‘PÅ"šø¡+b¿Œ&3ƒmë ØgÕš ûÕ²¬þ8&Þ èNÜ·±­o|.Ë3×b ‡öC_ûXãNž5–Sx6sÓÔËÚ­<„ËÃïÜÈ?ÉצÚ)ÇÌëôÛ¹s±ùòŒaЬ~š@g gfÊøèöªm ú·#Þ_à+íŒrlâf–rÎAmê K·ÙR²Íî+¾ÔyYáZS›VFÈ{KÈNÀW*£—‘§•ѯ³´º>u*X©Í ºô´º2.Îr®ó B›%»šzø:ºzÑÂÙ6•®¶¼~'Âz¾×éêY‰ìÃaï¥úÿàÖýendstream endobj 152 0 obj 2227 endobj 156 0 obj <> stream xœí]m·þ~¿Bß*Öf—Ü]îmÄv†Æ´€ÓgI÷RÜI—{±›_r_8CòáRòÝ¥E›8Æ+¾g‡3Ìϳ<+Ä,7FbuuôÕjvv{Ô}žýðí@Üœý|ÔdÒüÓ}àôêjöͱ®(«YQfy9;>=ʳ¶mò¶ì ³ºÒo«™ÊUWàêèÃ\,–yVµ*Ëy¶X–YÕæy9/Øçb¡²¶¬›ùlQÔuV+5±YÝäBÌw ™å¹TÕüj±,²ª©›‚“÷ýïBη–º ~V¬ŸFߺUy^W¼ø´v˪Íô×VIÑÎ_v#,„šoØïŸ/>6]ÉJ7ýã¿hþ¶³BdmMìm¤ÇÞZµY+8{…0ìÍ3Y¥ùÛ/âeÚ¬Ñ-ºó¥(Û*kJ=1ÍÛ:oE¡WÁ’cMÙ8´®X4YSWÕük½zzËv¾Öó¬ª Ñ‘™.) ;lÏ÷e›7Î÷-½¥&NXw—¬ðŒÑǶâf˜TÓÎoºŠ².e3HL[«zì0mßM_ôÒ•˜1úµi»j””¬"ŸË†5Â{?eeNh€+VœW½ulûÿiÞqETyÛ-Sž5Me—©jºÚÒ_1zXT-LciC¾ëDNVMV—J‹ØñZKÔO‹~°•*•Ê¢˜µŽP*á e]j}0B©+ÿó¨ÐìÖ2úæèø÷æ6ªP—FÇw‹X¥[ú˜Aed¤Ùj]¯i½ÜseÍ´‚•5÷\YSÔY"O è0¾0XóÙŠWÈüÞ!›šá£;>KÜ”yÁ„ô–« â+k‹-œ£žöëçXô:ä•ÝÍvP^i[¸lá®árnáÂáxfN5[ö“}Ü 5_ñ^ñ’˜éòk^ÖSÍÖ™p»ïKmº—•jW[6pdb¼`x´%ëªæ,Ûð¶ú Ie¢°rÃjQᩬd&TÙa“±zL L¡ÈæY[Šp†ÔiÊÀáA“ó{bs±ùAšÏ:`õ3 1[ô‚,±k Bþü»êfŒ×‘v¦ýÖÄøy¥kåV›È‡è¶%bÆ[•NÒ„F(Y’Ƭ(§‡Î ¶¦S«X˜üàÌFèX©v„d…»Í cKDðó$ƶ̀fTÞ1`X6ümD«Ká Ö¸½©Æ5eû=­É„(|‰Ã!ëöÞƒ®–‡ce©O—µƒc„:ddqx²<$”»GÖØh^·øg¼.çp]vpVg´òXÙ™Dnü•ó0o“Ú¾o Ÿ †uíþ±ÓèØÓdDÙø@ØxWÐfbÖ|òÈDÇç¼{Ü›£ÞߪQþœmÔV×GWb'KV[R+dñœ"%MéXò4ø¨ðÅÖŒÉ#C°ƱCL^‚JÈvÅPÍo| ÝsÉ è0üº@KC›*G¡a’T×úLû/µÊÊÑi´ :öÆWÓø eÊ6½KHëÄÒ5Úªµî‡÷‹¥Ì*¥aø•¥ŽužÞ/Ĺ,×L䛯±É ™W‡™I1kÛ’ÏûfÍa©·–Úf2oô$;X|lyÖ›þÛïÜùÉÚXÕôüFjf©—–za©×–ú;¨ñ”{ z£üÑRÛfZÖ¹q½u"!s¡Ïø%'oû²EkÜ’wDΈÜyOä‘ac7°±‘§°À ‘kH^Àñ^À.¶°ÝKØñ%ll çv [èO>R -w-˜X¦ëì€ÏNõçÄæ*ħú5d%^Œ°ã$6]Â.0Laé VÖÛ9²d;Ä™¥Î‰·K°Ð´láöƒõ#ènz˹ô[>Œ‚6–Š«8R|gù¯aØC„ÝèIÒ¯¹9³¯÷ ÄÁlÀž| 3ÎR0#šg”]ñï]Õöÿ+‘ý ¼çh|;Ú—ü  ?²æ[‰¶‹ MaRþo~Sþ„ò ^àAùß$dƒ)È>ÅR]’&ä95 0‡¨< ¤y‘«%~त§ý½v(*æ)Pä ¯ÅA¹I¿ÐÔÅTÔëS·]Bg"ŠŠyç· äL#GíTàgÊí_Ÿc›²w+0,wù´ ŽÁu®©úÈ_‘ Jú.Ç»>Ú[•ãG…!sì ûcQY½µ?‡Q¸Ÿà…¾Ø‹…X£ÐPvkæÄƒ²á ¡ó â‰rã M _Þyú¨_&"çPœœ¥è§ÍÊ\Ð=$Šf·KÎ-²{d²` ç‰Ð‡€Æ¸Ÿu°a%-¿°»8•ð9² võÃÀ€® XžgòR纛ñ*Nʼn?FêTï‡/1®ñ<â‹ù£Í÷ìÀ¨ z¡oS#80šÙ;,B™È—pÏÆV8Ë»ƒ$¶îÏáppø ¶Ï’þÇøuzÌ¿hdGcC¦‹S@=˜ìq0·Ã–5¹á®%´»Æ>̧Õ· Üã‰ïFfqÐ(6âñ‰ô¿Y:ÇW«-âÚKbUHž°¯ Ÿ±³8ÖtP<Ö”½]€òQÌÄeg¬•¢eÏàÍqãm~^22³ÔôÃÇ“0K;s'»šDÏDz³ûa^äÇŠâN^²êÏ8 ŽAÀ“1âc×Â(,¸hÚ¬)-vCUÀúúžÈWD;e‹J a“ùú‚ÈwP|Yo`ÙoM$²Y@ µ8K–þœLƒ’¥C”ŒtY°FËc†I.;(L,.ÆF·PjSÔ‹ ™´ÿ¬M/°°0'Y̒ϼ¬ÇýQŸ²¢ú‚°ã§Û:߀rßZŠò3N-EθÀÝæôAþ« \ô¾ßKc÷í£Æ'$S;ɰHæJ$íÂSØÛåpaÔdJ²\ªéMíÜ‘ý¯oøiS-~]+‹7JÝhÊLÏKÄS7˜ìs7Fò˜„ï{°XÅî—(Ún¬p8j› äTî'ìDÛ%œDlk¸÷H¢O5;hÄà úõv).œé¯z¯lÌq‘'ÉÛ–°¥“[£1í^ì©Þo‚0Ñ¥”æ±Ðt%ôø&‡Ý®¢oÝ„O"˜|ˆDâ'\IF/Rìãsü°I ±iË~öü†Æd°”a>|Ý"öÈŽfŒsÎSVëÎsëÃÞÛg_ ¨¥‚i Œÿ(¢5ˆ÷íüÐOófò¿;/!Ï>6ìI_H¾¢[8/?òFE“†ê!(9‘Ïø£'󹇕¦n:t4Mp|ñ‚%VdDÎ8é¦y‹¬n¥ãºŽ'ã™ú—…h3Qž?›‘tÇÆTsg·ÄòÕ°¾A‡ò` ”-Xïm“/O Ýtå=Š\Ù£wR¶Ið˜z±#¸ë¶ò¬¬K€l=¹q°j#uuƒâaêþD“ù·‡á“%ŸðÂá—Œà.€s$ȨÝ+`Ä©&²+ÞqÔꡬˆ¿(8 8%Ë0p€ÇyB'ƒrg&ÊÎÂW=›0¡ûK|ìéSYU ïT:é½Iì½{+;:ýX÷°æÓ{ ÉW°ÚqÔÿWäM&EûÔþ¿©«Þ «×»——H‰L*zLáej‰¾†ìùîK×0ÆÕ¥¬›L¨ñ¨õÁÅîÙN>ð{?+8ÖÍǤ?vx óŽc)ê¡Ùg†x *±\°ÃÌdè´Wdï-xš7qøˆÍr¸w]Iß7Ïßöt*½åËé- ^Ç eÔÊû ß¨W”]7²{œ¸ƒƒÝ˜ÿó’‡\ê%sx°WßR&ß¾‰¦uÅx: –Ñ­1ZdTã5è-ºðC(åÿ»/“Ïu°+X»­q ,¾“NJN ‹‰dÿ½y‚Þÿ¿šp.ÇŽ Êú©}ÞTÉácÀX/›¢Ô¶bã„ðà…[ÈÒ‡ÃDìÑ*¤XDX 8«o'„ob’™‡{\×£ôÞFsPìe+ ð%qÆ(~lþ,'2¯y”òh&|r¥+ïÕ^|Ø5H£ÁÂþÏ/ˆdÞfr4é?e–ǵ}k‹Ž|øÿÜ> stream xœí\[w·~ׯà[És½Àb»>mω/IÝ8±ã0OŽ(’–ÔC‰².vÚ_`/˜ö[‚”¬Úµe¿Œ@\sŸÁ¾¥‰£Ôþï€ÅéÁÃ×zttyP7^ÿÐGïÊ$³ÿê/NGgf £*©ŠÑìÝAšTU™êfV1*rów•´Hm:œŒG“Ù¿Í™¹1æ·r4{q0ûëóë4K¥H”Ÿ0Éu¦Æs:hÕB²°cŠT'Bi>œ—nÔ‰ƒþ»ûL‹¦ƒ¨²ñ†ÀS®<#ðŠÀË”f‰·³‘Èúøæï¿Ø¿ÀÅ·Û_ôÚAo”:葃„ƒr½õÎ,¤½É¤A…ßµ*‘ïz|Nà5DĈ@Œ`Œ¿ ØwMàC€ko†ùö{ñvÆVc7{DàIlµœì\m ç=ƒ“]ÖÄ£d‘”Òrãlyð†Ï•® öXA`Rê´[mÚÒl·dKÍüæ ï¿"=„}ßAðî.ÍpWí„R ‡žéóÌA¿ì!“o}wQc »°s8C”åßÃÉðÕ`©òEݾÜzûžFÒ L?å²#Œ™¹®"Ñ¢ãã‰aN!Íu¯42P–Ej÷Ð5^;èŠ~>‡?[1£…ªøDK¶Žý=ËËlüa2Õ}Êü’Uµ¾î¶‘ÉÔÛÇïãÉ4OÒT {:™JCå¥Ê­zíÀ—†p“Båºÿ>¡ÎlsOÚÀ:—VñK•sG¶G¨×[»¬&{º4»ízfÞÎÎdظ´ûg ²ŸŽi@3Mj¸Æö7Û”Y5pê :ëpDlpg±Êæb˲Ql.Ö÷QM723¨E£CoÚµMËfyoY«8AÁ{où|o~FP}~û¯t]GV¼ ”úÓæ7eôÝÛz_¥­·×¥ÏÜUÑõÿ®ÿ¶Ü &ë ¤’+ˆI¬…³b-rʱãZŸÈtËK¸¾XÖŠ·ŽUÒ Üä\8J7æ¬FÍÍ8­š‰òØŽÈVu43`ÕqCð‹ä¨À{º­ë4wK‘×Ãü%jÜ´6~ÕºvÈœì~òT°»í~æËÐJÌ•™#W(tj–´‹ ¬C3¡*ÊÖy ½¤Qà0]Ó,ÌcàfxÆÖµ™s?ÓŒ*ª4ð.ÞÙÁÏhÔ2î“ÕΓ,“Lv†ý0†©žOWˆ\x~7™Ví­XZ ˆcÑÇüí3x¢myq6 œ×\À“¶Nv0•zz¬Òݰ1g^øÄtò œÖ£ ‡šM  ¨Jß Æóà]ßzc†„lûfEîyó wåÊ 4\·ÔR$R¹¬]O†ƒ+šÔ§}e“4µÕ,µ6°n$t®’¼jak\Y¸ÒY•–^û†ž¹s¶ÜºéœÊÆXsðÌõ^±Þ3•È xj;B×E·`=n¿UžÛùòRgÈÏ,nà¬PYimP×gÞ^WYY sÝÛ¡©%ÍKÁn}Ètó´÷dËÜ“¥9C€Iåt ÛëC6…7ª;ÂËÒi!êð§]"×J#Óf Õ« e ¼.Ó»Ÿ±ój’Y «TÇäVJtb<%y œ<h¢K?$ÜÉOÕ ¯À8é ÍÝQkw0m‰L¨ÝJ&oF¬N®>ýöOϬC¢ÏS]ȦYrÄŽ±únÆ㠧­˜Mг[äG{÷:­¬r=Ôú°§!¬š~ûŠ^·[¾R²co³|ªthÁÑÎ~y/cƒþÌxˆš‚=® -3lY-°1`ÈTé¨KÖˆ ºl×Ö_ìH†[#=TYôîæ˜4ŽOùnIßlYP+e®ó34-°#ÁÎîÓÜÇI•èÒø¶>W·îl‚8qåL“sxŸØ'Áði¼a^úͳX-¶¢ôù®àq®æÙvŒ:¿Iɲ„8lçWf&6?Kú}R¶JõW1ž gùp–æóØï£;q3AœÍV A¶@.’QÝÉ·R0‘²Mì6r„;7 •pa„94kN>Ä9èH’è’PjE¡*(g‘+#¬š²À®žK¤IAUìÞ)³ÂFoòà”^6™²Î#=uÐ “mýõg°­ûwufƒ&ý² «ˆë X’¥JY~úNv'cÈw°K/!M?ãì2›w ¾}úÕK´Ö‘KÕ3á¬÷’•V¶ƒDmAàÛ­9Û©*)û$.’²Ü9‹ø]¦ÖNÓÝé¾ôOýJµÿ´² ·ß€Áú*š¡ÐÈɽŸ¨‹ÃÒ–Ù=%I¾Ò^žÈmU)^X°ú¶,6(îËŽO!Õm9´Õÿ-:pr`ŸâŽ‚:äÔïÀ‚-µÙ¯Ì>Jgö÷c]è3V½`ñ°õÌîùp˜r<4ƒ`ÃÇÄÇ6¹ÆPذ¡pCĉª<SéOÈÞåP,¿Äâlô;f¨^yKXh]"ÍYQkôºvÍ¡8ü€Ñ8|/Üç&lf±ù—C¾¨;}4D`¼™%JVzüŸ‰°²’ï dœ¼v‚mÍV×3 “qøøø›/P\4W„·ìíFPh0¯b6j™ö"röfq˜>—ÁRõb²ÕHFãW©‹SlËÕV0TàÑ#Qˤ.Nj:0‰]œUÑ´…Ç‘®QÕRÒ²4×Ö 3ú;Ü‘ÝÝî?ö÷ÒAøÙ=p£çsdÇ_ëLö…ƒŽDOï.@ÛÌ·#.á^ÙQ™qåÜð9Ù1 g\ÀÖœÇgnözõÄCH÷Äa .üãV̞÷ÐÎÇ¢´͇­ëb-9VÂS>ÔÇŠñ…ÎÁ ½÷ˆ>+üÛCä˜û=ð\Û&Ø%ä©:¹z~Gé?ÆèøC† NÔB¿’`9Ч¡Ü>Ïô£µŒÙ™Ïûc‹bÇ~ q‰oã.Nk¸–S˜üzWœ>r¼7ÐG:&ÜNÁEÓµõõ¾ÔC°Üv³gÞ’4KÎyq¾wýçp‰ès©}¢HÿK…ä-ÌZ¯#"£Gð±œñ®b9£Ý¿òY»Uÿç$+÷"Ù{á5øñƒ‚÷BtâOÌúޤýï[—JÌß}7à«bþýôÕ·Æü1ާ/;`â 7£ÛýXKäD´õT£f–ÇBqÞÝ}›i ~îýw°L”Šfº!Ú,~¬F~ê'B ç°QÑ*Ðç4 †÷¼g(çn¢œn1”Ì]ˆepðº«a‚E–^„°KdT_Á¦ÇU~pšhÁVlëÐó_¤Í²|Ž@Fk³n˜ÕgèdGÿ³Sû”¸ãÊí¡êP´X}޼Oí=Ë.± Wœ´:v¸ ª\Ù–¹k§uà†âÂ#¸XtR¶š…&‚䉢Þ3Ý ¬•—Å:la¾ƒôQÿ‡²D ”m&€¢ãüOô]NÔ÷ž­Y)vÛtH­gï˜Ç½Â¾¨õm ü^-F ŠjÛ¶ƒ=>E¬|™Ñk¨×±¥Žkö® ˆ=€c¸è7çö‰RÎaë2¶õ¡(dýõ˜T&Z³Š´‚}Ñ Î¶{XÁ¾öŲo-M»¥"wG«?ƒ~ŸŽ~?Ñâ’Rlêc·õKæ€:> stream xœí\ëoÜ6ÿî¿Bߺ{Ȫ"EQR€~ÈëÚÒ¤M·(´Ööúq°w?’ÜýõGJ"g$ý¸Üuì ¸º)‰4Rä¼9³’,2Éìœï}ÿ¶LޝöšÇÉÛ;àòxïÃ^•æö¿æ‡Γ§s3Pˆ¤NkÌö²´®«¬l©ŠDæßu‘”"KKƒp¾7I¦ó›12÷cÌ»*™¿Ú›ÿã½y;Ë3)R¥&¿0-Ê\M^xhî¡ÄCÏ=ôÊCo<´#ÞúÁCG:õЙ‡–`Ž…‡ÎGxRÛ%i·BòíHŠ:Ÿ¬ <&07^xCÀÄŽ [Ù)+—pØ >½†'p §| Íÿµ——UZZ®›î½çÓ²¹žƒ%Ú=·¤zü:ë›s±¤[î"­ÞØÞ! gÑaXÜHM¬úœÆYÄ-Ôø#¶¡ƒ³‚à€Áû¬Ý8e!;2™q𣬽˜"ñÃÌΗ!œ!fÙò¨ÊR¥‘bÛöÃo'ÙHZÙç§pøC±¼µzòà]ª'|pLó³C†F §êüÓNch%#œÑIØ(^s¥å4Ù™‡ú Å1ˬ‹v,Çz釮 sÖsÅ §zȱbCF¶Ó¾Ç¾Ír¸v!#'fÈ%Øží¼I¹Ñ5óžGp|ªÊ±@”Î’ŸJP i(Ó&øð•GèLê@H*ª2-´„¤6B~AàæÏ |EàË®0Á‚2Ž!°Ùžø†À×|‡Ím,kzÏŠ¬JsYâÙ±Úp¡lÞ£Qí#x¢Q§wÁ™ Q)Ó¼4M·üç±#z·ç§Î0´•:K…([Ùø%¶’Ûâè.:¯GiÑ)iޝÒýˆnÈ܇~:ë¦Ã.À ¦¤NYdã´IéÖ ç9E7¡ ··dóLt;aþú®Ï¹ñ>uìf©®†Y¥Ÿ@Æç-óK/Þ3@ï5€^€s@Qùm4o0AzE {“ˆŸË7Ž=H…^BgqÚô®aÁ%ÆèžÆˆ]ѵÌa¸R¹ Çý±Q¦}²ñÑzû à ¾*m5æ|·±^À§§±a áââ 6[ä%Ä]@ºxâ¡B,ÙäË eô„¾sÎÜÄVæú¾Óß4ñá]¨±–7¥2žcUrÞ<ŒñЪ Ìo áÃBq›˜}¾”aÃál[Ü\ŽÜøšÜŸ^ØØòVm¼o½)㽬 ¦ìkò ( r íCFܹ û™ÓÆAÀ1,ÿœÃÒež+Ut™çfK=ÿ9$S™çÁr]Û›üÅÙo4>Êœ¾S³dñH$t†4ÑT'£Åpw)^ã/æ]¶®å“™{F)´íï¢w¹áÕ™ 2{ºblÒzwÊÿÝžV*çŒú4ȵÍç7(w4Ø%ë¡÷Ê<ôØCÂC…‡þê{c÷é˜Ã€uÍ.wâQ­Í†]B\¦ú¾9Ø'ÁÊ{«¬L êÊ,1iÓ ·Sàl6Ë<ÊÈW%sŠýû,f¼p=€ °ùn¶YdznÊŽžzžùÝCÿü¿aÒ}ˆ{Atèù:¢pGí•Ò@‘ÇGÚ‡<¹_wÐÉ;œÂøtowDv{ c„ H!ªJ>@bøÈ±¶ú6¸ªQF…1äÚ§¯6«’wÖ¼&¡ƒÌc½ 6¸s1W†ܨ%Ä}`®ûVY-Ÿ3T Çg#æê›9ì»`Ö0Xñ¯¿$–©ò‚™xã²·e=ˆ`Ð]*Ë B!Ó² ^PycC\°µE ªR®àÕã¶L®Ò:ÏûL÷ì! öC ƒ|’¿E s7â!©<*Î{>D/;ï7½<8‚_ÿô‘åAd?6øRñŒ“†¹_a»Œ/Á°]9‡ÃXµ³6o .>`ö/=zˆ å.Ìpk k qÚ„&¢²æ“–BÀ˜Üü&E«_ø#m'ò÷¶¨¨4ö@°®ý5]'±XBX„+[GÅä¾¥¦Þ±BdTd»ò—Bk; ›n`Ó q!T˜¢­ºÊ߮Ӗ» ñúÍ"›{sz‘Z{7d‹ÄœáèŒíŽô¢5­O¦3ÞhCûŒ5Ü>…ãšÑP ,¶Eï¸wjÇ*fã"šx=ÔPkˆêñ…ßšOm&A×U Jü‹š:6¶ª–e釓(KíØ…ªiG…î Q\ë?(å/TZÔºWËÏ*ÔHMÁÄn5"EEšèu2‘RÚ¢`‘·B§ÀªéxŸ¾AáªX&ø¬ jТ4*TUÓÙК‚:1€w Wù YV­‰87›-§³,ÕÒ(iˆ8BÉÆ#òÏEÇøO¶í¬.DÕEZ5Ib‘ꬖbò΂²RD¢RUN¹¹Ç^v„«Úž†A6•M]¡á–BÛî·òã4îŒ!ðI~n6X ѸÜݸ9“Æ¢^[¸.ó:«Ü,Íó ˆ½dßò™Áüy÷ Zåþ{›±6[hBNYdµÛ4sXlÓl†°Y‹9_dé æR릊ýœUnoúYÏß;+µZYîU Añ²ªZªÚؾiQ^N6?Òät?®› Öˆcz\TàþyWf+ŽZØßÉD\ðMñ67$º@ó®òµF zFÏi†wÄK‚h1ð;‹k¤­ÙLž'Ïiœ„KG½˜ Ødäú.-GƒÇÜäëE<Üw§}@ŒwÙzYÎþ€BÛì×3ÊÈj³Æ?†pæ}3¶ßm}èÑ {ùŒ(T²ç°t D ÙÞ߬˸'á\Dò.7ä|¢‹ 9*»e›&èêÛñºÙ»´KAáàýüØÈ;ð•T£2Sš~lÄÝÌÓ1ã»—Aè³p·ü |}p)à‰£Ñ½§éy<›RlI‡—¨ù¹ßávÄÇó'g±™¼´ŽŽ„yƒž[À§L$n¼·|¶9E5Æ84À?×PB¯*¬1aÌ ´¸Á¯­•ŸyüÖªg³%Ú«g6 [ˆvû;k©Œ¿ÛëñOR¸ of¿‚7:êᦂ éÀ~ow]޽ ÜÉ=ì°O2ü9Œ¨WË&¸€ëExÃßKtÙhÿ8ã#6Éá‰3›àQרmØMU O±lF5¿*ñË47ô³Z>Kšà©M6á®Ä]Re!<õ“?sy·úXýF†–Š Ö:ô.8Iå2âºñÐ50É:ñ•>\‚g€ÞŒ¸‚keŸz§¿ÕÅì1kõ`7—ð)î&Á—ÁN3g„áEfé7äèÁжÓ1ÅŒ™ΠÝóK罦ÛÌ£_t o,j{‘tú?æl±Ò†6ƒþÑ%˜"t`øN_ƒaV:ƒSlÿñà*påyKp7iwñ.;z#‡ðÏï}M) uoákAüñøb‡˜£7W1ð»:•áÿÛ¿“øOPæ ²átÔÛÚkK\D,µÈQŲ‹¨ce$n‹+ëó½_ÍŸÿÔuCendstream endobj 167 0 obj 3257 endobj 171 0 obj <> stream xœí\Y“ܶ~ß_1ožIi(/ç¨Òe[)Gr¤u%%Ù³§6µ»³ÞÃ*'•ÿ€º|³‡dU$=´8 ÎÆ×_7üe–&™˜¥æo/ìŸí<~SÎŽ¯všÇ³7ßvÂåñÎ/;U"ÍŸæ—÷ÏfOwõ‹Y6«“º˜íí¤I]WiÙ֚͊\ÿ¿Îge–&¥.p¶ó~¾»XfERf…˜XèÇ™PåüÐJ3-ɼ(R9??é·3-«z¾²o¬tºPZ’µš_S=3zçÄþ~µX:UMR¤åüébY'i*R1OÚ2ÍtÙŸwÿº£+ÊÅl÷ûýÿ¯vvÿ0e<ÏK‘ˆª,³ù»ÅR%…RE5ÏlÑ ½dú©;“E7̦‹\4£“y%çkzz¹Xææ5Qó™ðMÓµ¦ZŽ©–czJ3wokÕk_òn°²¬Ù•ÿZó”é0ˆÈçKÒ©™™—¹Àý:„ ìQ‰Ý Ùk‡Yö0ŠÄª  ë‚Q!³$­r½v´î³·ö©6_gº.¡ËF*IË<ã½eƒì¶GausÙ·fz©L‹ú‡Ýé*¤Ý¡z'VFêêñÈTè²jþV‹I^J5a¥]+ͬôÜJïÀ¯ß[鵕¾µÒŸ­tj¥+[é¼qÞ8otµdu£eÝÃ3¿ *e7gzé©ÏhsÛ­n¦Š6ô¹•€öz}«Ù–¢WØf*Ä7__²ßPu· ͳRvgë¡Åàƒ+ËpáÜz q¤aÌ(2Û…Gl}­eõ7±Ý†f#XÃn_SgÚ!¦¢pGèu–¸ë¬‡æ÷ývgg2uÖïl¶EY£›Æí¯5ÐM†óºƒBHB!#Y®î{Òºhêm±§›O†=¯öÜ-‰bh¨~ˆÄ_\ÚÑAA#Ѓ™n»c8|ø@âYndäßðáOÆøpTÛ8°@w̩ݧlŠ_8„Ogh”‰ÈkºB&JÔåü·….•©4ç`üì‚öô!Üœñ:°¶MPÔKCX„Ñ7Än9¬ÚVÙ¸-ÖY†ÿVîèÓ(¤ÐÙ².Ðõâx k×pÙñƒíî;€Lÿ¸S¾5„V˜ßD°ê‚yXv«­,luk–É[PfºGp€¬hÁzÓæ®8Sª¯¯L#ŒBo‰–ŽÊ÷ ĆÕy‰•ʹ—8 ìz ½rd·ð_Ù$Ñï— ÝD™fŸÙ®_…„l›bð`Õu °uZ(ÐóRÖ æ¤Þ@õaCB„¾k«`Nµ¶® »¨Ñf±afÓ±æc@ŒàCk¬D©ÔÀwe*Ã&ü1èC8š0¾1l9¡¾&xߊ²]fŽpwÙkŽâ{D!t9°O‚´‚½vGª³ÔBXY>ÙG³MýÉj‹–oÀ…;e‚~Ò„ÜZµ)’T½Ú´À#‹¼àuÍèé&Ç¥ë‚(KÝDÙ3fe—*—I™·ªj||’è×gÌiXJ©‡QÔB7ÆÆ‘9뙄\ܹ ÉC«'ÉV3}€œBÕG†¯}ŠÚïYÃím´Ý¾öÙ²_¬e6‘‘m£-!ûq®¥}ˆâOÿLñþd¥ +­­ti¥k+ý…Àe‘fVò1_QŒìÄkg$Þx œ’xB⬗µv_cOak¬k/Akb ûýH‘yĘöCbta©ì½ã˜i@/ºí±CÈÕšºÅ*Ä—Ó.[+C%Æú 4‚íÁf1ÉÅeÙð?.꤬´ò8±­ïm§#ƒÓ±Éy÷ËT §L”¼£g=Sº•µg–6$»0¼ˆI [‡ÎS-41ÅšãL3PFÃjœB}°çÑ^`Hô•)›)•†'É,xκçÀ·¦‡e¥œ8û‡í(Ç­kO‹¤ÊË^óNîüÂʈ©Bûÿ‚÷ àö¹»{·ºW÷ÆÙì~Ú|×pº ”ú×ÀÍ t¸uß#޼#ŽåIÿ$ÂQk¦HШLswû¨t!zÁé ÛE%•¨²pPé§9M]$°}©s ñb¯`ûØlH?^tÕºPnCwËÛ¥º¶•{§/ˆ ‚gL_S5 ó±Vð]‚’­,¥º†1Ö Ãd­àiéª ©M_ˆ?ljßÐŽf”¬Sè4ä§ xè8T/UfBé*Oª"U¬«àÖþj…,n ´[qÛûÝ= ÚZv8ñ(â‘Ó‘ç´ 8Ò¼çÕ`¨€ÃUX"‡-³ÈI¾iU¦ÒËFdÉÁ—2KŠ¢¦se@ÁÉÌ´Ù`a•ÃÙÐ× Ún,úVþVüH[ï{¼6Aæ©ÛyË=AeQÝæ·9„n pÁ”æOŒ°UÈl9=jµFCoQÇÌÚÒà ÉÏ„˜²]>—¨÷›Åh {6¤ª]ç3lɘþG£wƒžû˜SïB‰áìÀ>ú 2pJÎÏ»èt•Ô¦í8þ±;…†××bîÐo Q'"Nr„“Cg¡VVæDôpÏÖã'†øÊÙ9`ˆ_ã|ÍMYPÎ œÒ¬’ Ù×P0ón^>±|½XÚXåZWˆ2V;¼ÄÇœþ¬—¦»)r‡G†Ü Ï p­HkÅ1Û+ëõpÈ•CÛñÊ=[`F­­;^¼µ5˜ÑÆÂ_ì­›˜ibèÞïh)À†v[ /%ÐO¤Al kê¤ïAîKáœ`;‹}Ê)ñ¸–vÈö۞Ŝ{le|0Ïcì|¶ÔtÓÁ§±G68§jœã½á-ØxÁƒ‡ctjË:ŸLt¬ŒI•)Ç̼„€Œ—\àÆGr¤Ãé„1†ÂÚ[ÅŒ méØhà5 qîcw~%#är0ç))Vhì‹oKQ?¯í%]ËèKç„k÷´«·¾1D]îš"pƒ× §>ÃxG4ˆ9ë9‘Z 7p¼“µÇ§±\8é…§CÜ6þÖûÒ±øý²Gšó»Ì+$- bÿÆ3Ïq¼¥ñ•£™Ål x :a»„qfÖ± dÓ¬ü”¡oëƒbOxшžD¤V)E­y^9;Úq’’ƒ™=ºKâ waÀsßÅ ¼„M<#ñ5‰¯ øÂy-Ë5lÒDŠ45릤†’¢Ì¹17/F$$‚‹ð‡¦oNðw(µª&ÉÙr¤ç±µygö;¾6l¶âSèêÍ`eá´ÑäÁ „,3/BŠ»HÈrAŽMiàt.ûVÌ"Ï ø–˜‘Ãìjˆ²' Ä6ØFS‡ÛbsrF°â5.c›„¥*QrÿÜQ"ÊÊJ“¢³Gwÿ Ôò H/À”èõ’r¦P5oƒ†»•Êœó7±4\õ&+}„xµS®.¡ˆÓ¨pþÖ>ìÃ9ák¬Þ“Xevð}öUÆ®CÝõÊS}(a/\[üýú ”s—[(M\ª¡\º)ËóãV±Yǯ±û°,kø(–@7ƒÝ‰êïPF(¢ÑYh| TÛûUÉeß·=ws‘~E:w7H5êÒ"›:ç&c-ª*ûœTSrÎ÷%Ÿ¸.47':›õòNë0+ðV†7ÚE¬a6 6P$µ†h³I"TÁl§Å:cbªJÒZ”n}ÇòZ®µ PxÉÎN>ë=“z]ÌS‚‡A0*ôuݘô”0Kùð(öò+gN4ßÞös-FŸ„sÚŒ¾O0œ»šgÿÿ{|Mx£žA1Ê8á¼ͺ€,î+½·Rj¥¯­$­ô³3R@É2[´ˆ¢ó%,Ë6þã˜áÆöþ¾6ƒ ³…a óSñYe`kÖ:>̬á Öõ4èm£’"Ñ^+ÈÀœ÷e—Êõ/t+JúôÔJ?Zé›Ûj›0_6[j^È}øt ×b)Ó·Rù0ª€  àa ]‰þá3Á”T ô"6Ãì)†êè†ÿô˜ò@ŠtǘBlœ,اÕ1l–Ø„"ÈѼ¾W°6S¢SÌèïDKŽÝ/ðÅ´ôoV}>vuÇxWç N—À£>©ñ ´´õ]RB_àáÐ鬫]D«Ä—¨Uð&c!En)þE11âîÛ)ðIÙ¢‘]¶b§“¦þ4¶ ã°³Íj؃MàÔg§5 ‹<]ñˆ<áË?­DaUR›Ú…¦%YòûòÇ0íîx&âkø8RúK.IL hu"ˆy«L–åÁVùLÇÿÜ_g"£Ä èr¨zG¾[ñpc~{¶y_nÌ"<:hes@VõG0ÙtE‹òö¶ÓΆqg /Æœ&Ýï ŒúÐÈ=ÌP²ËPÛðå´¡‹?CW–¶ÿy[c.‚Y²Is-¦Lsç¦Ü ÞZ~O ûàD{Üçßµ={b¿:±9ùßÉ ÙwN¦ÖgÆïf=gær°¨æ8]ÑÎþT*þTJû­ ¥ê»+øfþ.KôòÍKü Héû…øâˆ‹¬-v©!¸i^²·OK}­Ò˜3›.Á.¡8Sã^g“‰ªÕÀ·CF~àw$Võ7xc*¼}pÑê6÷¦‚D¾»;×ÿ¨$?endstream endobj 172 0 obj 3684 endobj 176 0 obj <> stream xœí]ےܶ}߯˜7Ϥ< ÞSIªd­l)eIöj¤$%ûawöêh/^íÚñß t8 ÈÑJvªb¹J-ĵÑè>Ýùy‘&™X¤ê&¶—{êÅÙ‡½îñâà›¸=Ûûy¯Irõ_÷€ÓÛËÅWùa»ÈDÒV‹Íé^š´m“6ýûlQ•òßm¹¨Óf±¹Ü{·«ušTBˆF,EçYVˆb™±çY L±Z·I#«Ë–‹ÕZm™4ÅrµÎ’*mE¶|Fä÷ŠuVSÕMÑXôBUW6MQ/7]é¦ËVà¶£ó2OËå¥*Q¶e%–ôÝ•ùî}÷žõyÁè²´,žeùòÞ|8NE+–wŠnë¼MÕŠy~Kó®þg˜š¦E#¨Š¼Ñ=é>ýaÉZzÛÑEU‹VÏ \Áå«¡L!—a±úqów¹ÒY¶h­•®…³ÒU+’ºR‹-?Úü´'G\,6ßîmþô®›æ*©39‰ç«TBÎቡ’Ê˪Jsµ¨Bޏ®»EÕ¤\Ô*©ŠR®Gf¾ÊÐ÷«B>ÌÛbùaµ¶Þv‹—M.WO}™‹Ô*ÐôõUi¶\˲u"ǨÖTWHU_QU'°ª;"Y[ªõR±±$óÚªÖ=$’:À`ßߣ¢whFnPIÖ?ÉK²{©È[§×¥|šI&a8í¦ ÎŠvy+«W¯n—GTö¾ã,‘ËbU!9is,¥Äê‚ZO³V¶]þ¶­ÜI‚—¼£’¬ßzФÐ`eêÖ’oz®«Ú†Ï^ÛkX €O÷©ÞgDS#÷¬’Pš©2ªá UV ‘²¶ç†±5š}Ì–¬Æ57œ4}À«Šû˜>ëç7—ÿ¢c­WX)…Zf#Ô?ÿ‚=½óÀfç np¶,êjÚ…$Ó$•‚²¬õ/ªfØYÖI¹ByY—"°„¬^½+äóÛJ¶+¹„‹-½>á ®û½Õའ˜ÓM°=ØÏ»èÎN,溑·¶”ëDD)«(µ„ðĪÒwê¡'¦‰;KVÃú÷N+pNózŸLÓį«6©›Â–­¬ !ž4Á.OF—猷.yXÊ©ŒÄzw¬H)¡3‹qÙHÿÜ †ª¬†Aw$–ŠoW¹l m³€P\¬D]K­£îLÏ$_Þ #¥Ñ(ê8 M²B±àÒä ‘\šô]™p†xÒ¬ë5ÞÐXhŸÑ¸Ýs޼J)9sç¼ÙñÙªo¯-^Òºc̾ÆTTxsÑáæ\ðÝ`ãíøsøÕ]/;¤¶ÐtFIÇÖj˜|pA••–¶u ?ûeU*ažÖ¬[[(Sæè_¯Ö…<ÉÔz…+6FÄFl-¯`à¶gïy±RŒÜäÎiaÈ/á˜(‹òP/ÊYº¶ö}fxЉ6åÖÈü+{è%N–áòäÞHan²ªbê -t”_¡÷'Fž\ µýܼ¦vð.f°)ÊÎÐTÇJ†Vma1rTU·O­ÌXÚŽ™WåÏZÊŽ!-]q;IùŠ `û€È[Ú‡ØnQ½íM‘&ecìöþ†DÊÇ“öÚïx_…d·©îw|°¯=-‰¬º#¿­äÀÖ¼]dfÛ’JóÕ ¶J¥]Á:¹4r[±y× sŃ4´HvÓŒ‘¼Ç¦#F†àbdKƒ¬ÒU¼ ÅNõwô;1Ù„å2Û#‡|mÌÌ9J*(Vp]•Wëž2qnÄ>»˜ÐqNÕÀ@ñAà‰žîL8vU-Z·qÖ¥k»>u‚Å ”4I.´¨€š·ÅÍL⪓ۚû!\#ªuŽ€ÁÓoÍ*;Á\žišÈÇymR>¬›Ns7ë ´c\Àzx§.Pbl,zÙµÐ/ÙÛ˜k ézk­ç–…Ð&°‚¥lÁér«äbÛ6yÁ«R0¯~LuY°'—UEYI¢UEßFY˜]„±Èk ‘¨RÀ2´­?Ö†ÑÏë´,™‚µu¤š-Ê4^roä‘…¡Í±1WÊ13¶;5{°‡šXŒ±Ïlpzü³‰Ò¯++¨€€M°ív1 ¶U’fa»wá’ø!ƒ®„¡‰e¥ùæ05XÅ)+{¶O-N# ù Œ é¡ÜÝÓS$­ÞŠ}2°M“æÐ³Ä–%Šê")ò9hÎ#Wñ [J\éÒÖª2ŠÂþSyÝ$s¦‚k£/É{+ËôYFbŨے¡aO·“Ð!W2AïÓ1dKÆ‹XÉÇŸá“ÓÀ½– tžS._zKKñpFò'bÍÁò£ð #ñ!ðy„‘æ¢<öøtvèŽl2Nsô:ÛöA0ZþÎÓöÊÂÙ”¶ÑÕó—Gf[CœÅ•zCEI*x¨–è(¥Ë>5mõçÎkIIŠ“UdW±Jþ4ª´#oVC”6¦ q`GË%ŠÚÑsöôžÏÑÅ PŽŽÒòa™¬Z«Ý^ÁÎQë4¬ÖSX€IsÚl“·±'ˆo I¶<­ý \Gëçõ¯†]^Aœ á2ŽP^þ›´ó n­ø{®gó³CÖíË< cQ;1æ~‹ûävBÜoŒ_´SÈ ÚkØxޱþ ÎDo¡£ÕŒü+©ia}WxÊ…‹áÚ˜ƒyg—‡)rz®ÛQë¢S¦Ù)0ö©èÖ!nC0:;ÜС) ¥Ó!‹ú=H›Ü/J fhÞ·ÌòŒÈ7ÚÏ;>ÅÏb+B%a¸Ùq£2ωÞX‘ÆÁþAÆvö <¥ßi’6Á´|qèàå°$Rv¢q&lÑq ® µÁ-yðcñÃ`á°×jP”“"Xq'»×Æ™Îók¾ÚÝ*)L¡òÁGK‘ïØEžÜUeœð\B[¦Õ&uQ”w¨¬OjúOd¼C·m@@Ú1‹F½e7páð†s…Àäi ý¼w`'š‡ 0{ÊUÏÆ"りƒ|ÆiÙ Ê:NáM^|P•§Ù ÎÊ¿¾Ø{t rxœ¥I£åòTÈ“ªX¾–dRÖ’ Ÿjc¨…¡Þꙡ®@¹}PîÍ@u¡ü“¡˜¼ï‹fm'e4yBä‚È;"Ïce÷‰|FäXö†È["¯‰<ƒ‰¼„ä‘W°²üŒ5|æ!lâ–½å•)ö)D•4Â3Øs¶*w°7· ¬Š_ë&ºàþ¦oç3ó¥þöÌ—oÿÏ—Ì—b”/­d ƤBåüàðkfô¿XQˆ2! ;¯wîéç¾;WÑyÓýû#Ô™‰°+Éè}™b'üŒuŽk'cŸìf­X¯F½˜±‚÷̬Ôq`ù°†–èqá0 «"¨‘TìzDßn—Õñ¶ÃˆM£ã؆ƒb“†*Åø^xÝ›÷NÜÇØÑ >üˆ¨#/lÀ]ö,)êf°ëûUdvý† ²Y_€éŽs4›ïcóbRª°¨:;ãrŽu³ÄŽ<žƒáMZ[›'ÁôѨ?b"ß1ñsÎEð6+ò´GŠÒ¤H¹SblQE@“!E`ªúô€/!¿áùb;Ú˜#är@Û¸ÎfK26òe-í)-x†£,E,댗ä¶Æ“‚|¹ ÀöºYþj’‡ º¿ ¸’A›ž› {ê‰=Žf*pX‘&MkÉ/2ƒÈâQÙ$ɼè`î o¾p0H“úœiÌü¨kõœú\£›Ô ŠV] Šv(Ü:˜Ýë¨?ï_F‰ÄI©l9±ã£¶ž›Þ=±ÓÒbD`yÝ„2÷: )ÍÌÏÁâ;ª0!rÃ|ö~o¯c73éVhÛG]UXb¿hj2WEµ4)ídRñH‹ß­~i¨W†:0Ô C=6Ô·f˜5Y‘ô>‚D v 0·1í} J|AäãÝ Ä-ì:ìîa '`ðd ßO¾¼_ƒ| j&pØ_òa¡•rU‹ÀBãÕ½ƒó=gmX ×°Æk/` ìé!,€“}†1cÌsZcLüÛ(; °ßöî­l§;ry`J®¦º[¦ÒâØÎ7ãˆ$ Ì¥ûnœVh×XEèTcâ;Šz Qž9è+>áÐA¥ÍXÕ:X‰2t¢Íâ3à Q²Ìü#Ù‰Lk•œ #/ŽnŒ7\ €’Ë:õ&ÅcpwCᑌxŽlA{h>;çmͳdXYìþߢÙcûáÁK ñ®nÂñ(§ÑWTIš[:!e™ÕÅTè ¸3‰,Ug]”yR—=ÀÓ žP:¯6€¢·OØIX¥©'1UºQÕ–€ýYÔYc8}ŽíÓ‰µð*2yä­_‚a8Óór&9º:IÒPwƒN‘óOwân2t:ž96ŒÀ4­ 1 2~Ѷãc—ò¡eÆFöкsS¯êcNt?ÊÍ5µn.õÔØ´€a6¾áGýÏi¹ ë‘“èÝ=ÂÛqþ,–ƒÂ—Ø ˸.€7ã.w heW¯ÃœÐ7ß ŸòØaiúˆyymdÊSCáȪ~ÃÖuSdüê>¸«•J¶Â¨Û¾Øâ¦S÷‹ÌBËèîîäéƒ[ÜÜ¥¼ŽnxG¡¶OÔ‘¤©‘‹VæŽ#RaÌc¥°A:hp¬F4¨ßm9b6Y¬LI­de}aâÜ\g)ؽ´ ðôFq@Ëã‰û_£èâéËSÃð!…ÙáwLý €à—ôðº‡\§JJ’ëívªh¿âŸZ.°Ù¤(0ãèësˆ°’á ¦<ÒRØ7îq꫸ÒFe¿_†5³mR,±Þƒ ±öƒ¼½h“–B½CÑ ,ÄËÁñϰ/q0è_è€e‹ÉÇÀN!ÄŽßQ6é^u <P·üd)¸sŒQucˆö1h;šEôËoâÌ'€¿ŸÈiþÎP(:âGà*§·û /ºÜ„,+ì?f0,s%Aô–³bíœÊvÌ{º„5la ¬g mÆ1¦“,¢¢øñÕò|ñ¡Þê;›KþGy#ÚÉèÒ_Á&ØgØÃ+³ÂN›ä­Œ•qnèýAl<˜+çÏéöÖÁä¹Wt7é3LŠÅpàhCê¢ Y†Ø¤4:|âàë~Ý›pBWï„ú3W%¾ßÅW þÐc¯(ï>âc>ëW.X·ô˜Û)õå¾MšQ~§)ºÓ;6ÊæQ%Øu7ÄX@¢ù”Mw¼ÁFhÞ©ÀêF‰æ8 vÑPÊŒ+ŽñoI…4P´?¢˜öáý ë9)Á§ƒc¢LÝ£J¯£wsC(Ä”äïP·æeöÅѬ2☛‚‘d°±-|‚>ŠH„\z‘Ïb×ôØ¿•úIX¾ßÕèÆÎ¹Ö’Ÿs9ΜŸn‰Œ¡~j˜GrGU…ãp ±é††…ŒM`Fwuô÷™æ`),æÝÙñšáûÝ¡­‡¹‚‡2]‘±ÇÃ=¯å”W¹èPÏ(Ý7•›íÝ›0’ëŠQ:ªcΕch÷yv­°㨠á²4ÞcšºÒ)EÑ;¼ähÝ1ï 6váó´¡ïwŠd÷Ö`Èueb\©*ñòD/C79ñ&![ÎR ¯}M–ÁS"7ж`7›< ˜,†|N$kâ ‘¯ˆ| IÙ©2TEÔ¦É šTëíY¥")òz²ÀÆ)”ÑÈI<íÑ bg]p`i-Uݲ5â~lmÙµÖ†ÍÖºÈ[)©êîn”b[å8 GUó"Èñù1á¨ï&d8ØX\¥nÕ²Bã3bR†"nè4Êcdcây€ZÃ(Œ€ÁÔ“Æœ#|׊}jÑí—Ö(ÒÒ»=ŒBS;’íu í!,èQ&zûÔòPOÁ„i=­åµ×n0‹6ìê&}üÃöp^N‘ÂàßÌppt«ÌËØR»å¡eb¡Àäç£_ÐÛ' œ½Ì¢ZKS?À2ãÐ`œé(f¶°,køvò–=„õâ†ì3;X±J–eðiYq­[S‚/¯\a4èF—Ä!ž{õWC½7Ô…¡ü©³øA¤MÍ] GÞGOtJzL_~f„óðœ[ñX 7±†Ù€z”(—"MvÅW\=‡3¬±‡TÇ¢IÒ–î÷ 힣¨øïñ‹Þø—háí¨(åÛ¼;)L¨+-§Ð¯ý:&>ûµ_£V…Ã:gYtî$A†3þé»h¾å{P¦Â·î\­0tY³“½4bš²§ì÷½!rG ï¶lqýût8–ŠBu`ÜüpÇÉæÌ É}ºÙû^þù/I×cÊendstream endobj 177 0 obj 5309 endobj 181 0 obj <> stream xœí\Ywݶ~ׯ¸oÑí‘(·ž$çH¶S»q¼*©[ÇÚ¬(µ%YKânÿ½HbÀ.ÒUœ¦±}|æ’X3ƒ™~X¤I&©þÛï×¶^T‹ãË5óxñâOqq¼öa­NrýÇ<àôÁûÅήª(òE“4åb÷íZš4MVm«Ù¢,Ôï¦XTY–Èz±û~íõúb¹™'E•ËQ§–Ú³Ô{K­¸·€Êš|¤à¥¥N,õÏ)”uzɡɬÎd"šõ*pFä{HyJä‘—¼‹7»^Së&Ôº=^S¿?Ó¿¤þ±û‡Õ/ã=K½´Ô K½¶Tj©?Z*·Ô›»[ÆÌR弦o±Œx•.`ÙwDnuZ؃O¯aµ옉ұ‘”\Ȥ¥RûÝC¥å'cC8‚ÍnÀ~÷á„÷` ‡c“HáÓ+Ø+ûª±ÙÏx³S’Ã;0n¤;–úÖR_ݵ~|j­ÀwŸ2‰û ÀÅ„þ-$o,¨@+öh–Ÿ\dåÝHìcK=Rü›·ãÌ 3dv äù˜YcOñ†>jZ·ã¿ ;þÊR,õÄR»+îw²VÈ›6ýIí8V±Qéd$3ÿç°…K¤¿€xçD&L9§1A&ý¿dì…³%fbÈÖ•™l&²W ·»”i ÈlŒ¿J™Þz‘e\`.4r¡f Šíþ¨ãÒBôéëešäEY¦†íªFZå•|šT™•p‘¤i¦VóÈ>\P¥+"ÏÐû}ûV?4s‘öï ©øQi{çt²”ú}#5—Õò¢Îy%ÖTB$¦l•Ɇ?}Ó’yš­ÛÈ]òÀ¤T3+U¥Xÿax.÷ÿ”%©ªlý!‘Ï—›eRÊ"-”/Õ×Ê0/ì´Î¢lÑÌ($¯vJΨ¶.¬¬aì5U8ç$êñ Å=ªðÓR•¯Ò¢ÐŠ×7Èʳ¢§T€­+Èç‚È›Pøü1A™XL°Þ¶—›Ú!•ÝÂÅŠ·aŠÈËD­BoΡ8žÂ)ÐàÏ 'NáÊÀ9Ÿè§yá5…… `TÙŠ–.ñàlŒÿ¬ÖÏ­ð—M­4f:kXc¸š'uÝNì{ûUd\Äilæ¾à(£—™ SjD6ïõ+/‹’÷õµpí«š_v±U¥Z«Œ³Ñ‹Ìfg[´ÜZ#Ðë­æ]“Tu^“ùv9ÆC2V}ë¬äwËó̸ÄC!D-4Õ’d™=Ûçeš¤V-êTZØI-õ†˜%eÚˆ¬cµ,+%¯õcQK¡÷ÆLÕjÌÛjUT%ã[º­¢®•äFxn¨²Ê¸Ð"M²2ÏY j¡+š–™ñRÅTÅ«k;‚ËnBµÔl3J…ò§xwÝåMÚ´¶æ«y¡;T+§ÈSX€7Á»¹gÆT׊og¶"5Á«]˜æòRæ5+û®j•DÛ¶ÛõjEãcÓý~õôÊcµbšTÚ²ÁÊóº¯ZOY;™æýžÈø¾Csü~Ùv£Â‡ ¹¬‘ó°"SâdÎì+ç»g½÷¤”êD‹JU’û¼×¶Ò;kûöì³QWƒYi²¬ùsKÒï™eEñVyåNÅ5ûJÑ¥¨3¾÷²a†Nj- -ãÞ¬R¡TÒ–íýÕªQÕï[49§ý¾jœJOºíDé¿©_¥MÆÀŠîóí»-¶Û¶­–MQ¸ [㣕ÊrÙË#v«°KëøhÈ1cÕ˜œñî3r>Ž Ð`Mkc_â•öJÏ6ZgCýã5üR;–[Zåú¶8]F>âQ…9yXÈ÷܃Óîˆ,³*ɤŠ}6 ÏÍÇ­Œw»Ú^‹ÖFGœªn¹SEªOiñ¾u…¸öÏÈ!Û€ìÙ³%©m¹¼"ÕòWÅè4¡£e›v ©-SÙHÉ9¿,BUˆŽÚçÁ±1‚§4`f³®Ðˆ±zøóhÇ™Šék‚Ù® Ó¨6¶‘Z–¤µÔxLÇ̹Á[_Hµ• |jߊ°¶¾Q¤²ˆU-³6ž¼çÄVŽyÓl‘F#_FbY3ê˜ÁC`BÌàQØÜ-16x¬W®q~ ÁÆôцn§£Ìœ° Ç#*ÜK ·)Ìs SB&€ôA)‰È•cQæ½å˼EöÈx]VÛÁªZxÊ€UNB‘Ž2ËF^y\íà)?äÁD¸™ êçàí7–Ú¶e£|a©Ï-EéE”rÔfäÊ(l‚ׇ€º°%]ú”†E¿t°h!ÕBÔ%Ç?B4–ÕÔØ&,‚ÈX`N±î2HDÒ.2IÀCK=µÔ_V*j=!må2€¢§zòL ¡{@ïÏ´;\ÕÊœ)ÑSdª£o¦f‰,ÞÁ,3+ëy².BíY&ëÄ" ofÜhíÁ]÷8bå=ìdc‘ËÇ6š ‚i}+mò¤PA9{ØäÅö![à›nµÔm·Û·¬“¼iúí;ØL¼wÐn¦1ïÈŽñs5s¥iʳýËC¼:Œ~“ú݃»á¿´Cð:oŰ³Ù´¾l\¡¢ÅµN>örHjd™¶Ê‹ 3‚èyùð¹õ³QT‹RïÑ© 7ÈÁ?Ü’ùæŽ&~ùÊN¨:­ü5«´ç®v„{AòtY{öØ·:­¦Ù(;_¦ÇÑX!°ÙÚËŸàdÎñã\2'Ó˜ Ñ$um±'Ì’¶6‡Ìãg+<ü%T±˜!·=¤ô´6®kSºf"¥1¶eE©´œÔBâ›”ˆ©°fˆË6ÝWßÑ! ¬ÈL+#·›ïè3 iTìl¡€’€TØBì×(vë÷¡ {¤ˆKO¸ß[#“ ²ÌÅ8ñ <7"ÀçbT'ˆ3§…l£¾G€% Íÿ”k”µÈLáJ„*°ž_Z#üˆïçd¥lè»6¸›¡˜3‚%Œ†œ¯hV¡\‘Z_ƒœ,˜¸ƒÇT¶UÀ\EOH¡æÔQ(—)vP=s‚ª¤Év§ÇB8Ï¡CšÎü æ8MAQz‹6J™äYiáÁŽUÏ™ªæÌ' ²¹R Å»Î?Õ%dƇÆG.q%‹—®[ì{Žãt$ c]SCIxO óУT¨€Z®ó¡ºµÔ3С?ÿ²Ô}PŽúx°„[ê5A¨™Ç”Šø"+oˆüÏ Dr›\ËUÃ+Ó˜–LÜ(‰íwl…,Üo [ÁáW»wåIS¸ç£&$ZÝéæ Á~ì$ݧY\YûGÎs:Ÿ©à4Ô+¾>1À ‚gÆØžp0azK~Š8yy¦,†`œ ¹·ÓÁ0F^¸‡¨½æ<ø Ðz(£ù¶¤oœ3P)ð,¦Ã3¡¬ëíËËÅÐN 1ŒÏ89»žÁÌ2Õ ÚïS~ÞêòÂ)µídµŒ. 3ŠÐùw* ›tZÒK‡½\"øÆqíÚ×<)³Þª` ÁRO±H'Kúpv”NŒÃÖ,NñèyZK–Xâ&±ôÆæ¦vÌ¢`±ÀŠÂZ¸}B÷0 ©ìL¨1v+2F¢gˆrp†7¯å&Ɖ˜²ºA Ï– 2T§„O¸áNDi`›¢t/¡,é÷=Tqèmd,õ‚)ø×,m$À¼šµ§±²7¡˜œ¿%Ûªs°‹Î²”URdì¦å°*̺yyáC¾e¿ýzgpÜ/L%q ¢K[…ˆxÀ-lé!“Q÷ƒ9gœÍnžW»ôÜH°Ë6¡Ö©!<¡÷Û”ŒÅ ¶ÃöäHMÜÑÅêÏìÂvžUuÒÑŠÞB&æpNA>0ø£[T›T”3·Ëï?fuhïncõ¦¬zvqç8dÐòêZ_k«vû@R°Ãçþ²1g[øí)¾6²3á2J=ΗÇs,LC^–Îö„^Ö9þ9p»¸›™ý¹ ¡8@`ƹ¥°ChÎì;6x·µ 4z"먓oCÊøÙÅœ­}úvðÙL:_Áƒtn:”… ûLåžyzv9Ä&x0¦%—]4Ò$E*Ø×ZÁÇ›l¼nöõiãY7iKŸƒ¥ö)%ôôÑ |ˆpÙ§ •y¥rC¥èõ†0X±ÊAά0½9g~3 jc¢&˜qØ"–‘î›üöŒï!‹¨!Æ7d7Ø®Áž²ÅaOi[a{={ÿ7ªEEqSOù¾eŒK‘'hÜÿðØ-¿Qág G!^xÑuWäwI"žÓdÔãÇÞ~²Ë¯l,£[œÚÏe"›¦ÐJ.‹¤.Sý#£ jZ[ŽçŽ4 /ë*¼dÔ¿ö ?ãî£t8Ù*±ø£[i  ­²Ïºk3ýâZ(û:ª™&ûf„,+Á•}©x¨µŠʾ‡føaìãìx1(ê09~@Ôû?Åþ*väg¥ˆõ—NTƒÀ0 ûS 9cgË~œ5Çñ“|¶ýMA¦¸}ÀÝbokøº8Ò‚p5C¶ sg†0x:0‚Szpü ½ƒ-ö«Ø…‘T–Uû‘à’ë.‹Q_Í-Ø·fÀqŽŸ6"ånñ~¯º&Œw+4VþIÏYìåÏp—FÅå`s¬J¯&ñAŒFøWÑV)&·.G˜þæˆN¶2U&inM²? â;Ž Ýì¥) ðG@0èŠ=#lµXcþР膰IVàb-næ§Ú¶V,al¥À:bþ®æ¹?œ—e–ã®No› Y%•†È³>R)^UÆòjéS&LóØÀa.Û¨%ꯗ7b%°C˜¡·Íš½ñ ÊØÙ‘bûîý(ä™™Y™9 ßâlºýñ°LšÜfên¾N—Hʪi&¸=ìøõ ÓA8«¿°WpŒÓ½„Ã)#pzÞÉ.òñÉn`­:í·6aÈù©¬øh—ÇUífÜ ÷¶À†³-M½;ç(^âïq ,½?†ýøØn ƒêN:ÐN!#&ƒ§Ä"Œ–ÙœaνøïÖßþŽ÷ÏÝHYÛ-‰É¬õcF¯àOŒŒ²°§AR¸·÷Ć`W”’r$âíÏï`ž!k–]q=ÊÄw³˜¾í¬ˆÝŒÀ† s¿Ýù»›t¦¢]ô_úþ Õ55ú+€Y ÚÛ%ôÀyúhkBè;»ÉÎ.ŸD¢;ëSò¢ïµŸÆÑßúÿ„k5%;Ù†—±×…³Ø@ 2 hÛ£Vóê7Œ ²õ>ì2[Ÿ• ø”Ø@Ï Ë*O Æ2«pêJ؇]áƒî˜³„æ…SùYf7ê? ãù8aôkÄ%­“JXyqÒ7ݘ×(I,æ…žjwɾΘJÂ$…¡€5ú-fü-!Ö»3Ü/™<Ň.~úÁÁÌûð(ƒù:g’¨#’l9;òõƒk8£˜&´p[Ôi>:7Ænä„›)q Ú_\œu†3‡pfà¨g?­Šg™…-Ü;®Ñúᦲ™G‘Mdžxìß3oWŸÛ¨²,EwÏIÝ` à ç¾^Nˆ)ü#¡<†+Œ#£a‹5C»2fÈæ¿"Âæ€GóH¬µb]@cóÃ_œàà΃$¢õù—6WCÏ6xöÒwµì®=Wÿ WËèÅendstream endobj 182 0 obj 4526 endobj 186 0 obj <> stream xœí]ko·ý®_±í—îÑxÈyEÛqÛ©Ó:JkÀ-Š$Ënmɱd§ïß^rfÉ{HîìHvÑ iŠ€ñyyyî“ÜoVe¡ôª´ÿ¸Âéë£{OºÕÅõÑøyõä»ÂÛ‹£oŽú¢²ÿ?`ùôõêÁ‰i¨Ôj(†vuòü¨,†¡/»©WµjóßC³êTYt¦Âë£õjsòç#¥ŠF¯N>?:ùñ³õÉæXµE§Z½~±1õ”®»õËMmJÕP¯¯7Ç]Q–ªmMÛ²¨š¶-«õ©|½Ú(]t}5¬_›ž´ùªµ-êº(»ª[o}§—c©Òe·>“"ôúíf°=•ÚŒoú¯š®ÑëW~*¯ä#4‚®ÂY¯ß˜ù(S­¯ “­™YUÔzèÖÛ¨®PuÐÛÍØ¦Sõ°£FÝöës:Û­|µä˜þõN> à+Îļ{­Ú¦Å±Îdþ0ìnC´!ø |bi´òK”¿Ÿnþxò«#­û¢/•až“³£gØ&ð\ŠW2êÛÍqm¹¯ªCrúâ ©Ëé £É¼•Ñþ=X·_×y†Ó|…G›ÊtZ*œ¡§÷y°°Æ²ºÏ)½Ã¢»ûRé’6ƒ!®p‘#»°Z>­bâó3»Çnw'ýL~MÎý•?¢ {O¼á;Ïýß"8Z$\­ªæHìwN0âŠã/;ºëzý ݘXÒ6ˆ Êâ=Ž’cF_áÑæx°ŒoÖ”LN)Ýg¹‘±k–ÝÙ ¸Ñ÷pã•*ʾqPÀùrWàóƒáù)æH;žƒ Np‹@‚`ß= øæ@gW”G¶´Ð†Øn"®™ÎÜ,1*˜Œô2|ÞKñ(bÈ ˆþ|W¡ë3=ŸxÆœ¼¦®Ïüd'ÕêÖáRÝ”E«‡—î=Ñ•WbŒ²Ò;237GÝ V½¾oŠEÓ™Ùœ’üõ¡/ýÒ—V¾ôÔ—~ëK%©÷_:÷¥_zAúû§/ýÔ—ÎH/ï§’¬xvOãŠuß­ÿ$5/¥¸•âk)žKñgRü—+ZÇAÃ1}‹D…f•b!ÅâFŠ/¤XÒfWR|K+Ü“âí=Öµ|T †Ïgçt„-™˜vŒèz8ž$¢íEö~yéKÂs_øÒïäCùë#Âá®gíNS ù«QŸ7ùQ¨Ýk¶½Ð}שDôÚRSD x*¢ ¾‚v~ÉZýȪêªÊÈp@4`ת.ëp¬@§Ï r& ¹ZñNê:±d .j¸49ð\uK­w„üv¨ë´U©Û(ú'}¿)*U»ƒª¤N¼\Åf”ÝíX'mUSgÈEIÀuø%R<ÐôØh¹™ñ}bfWrRƺ[FnœB·ï7Ýso±‚’= S¦g\⨉‘i4+hVPN€bbô ‡õV¹Ó>Z¤öˆî G«ºh…_‚S•Liß©SQw)w“zP8Ó=¨Ïf=×ÎÎRÆž×C¡•îðãn«RÂ"ŸlVoè .©Ö¸Å Œ¥g·å3)Èâ‹Þe‡ò݆*®æƒ ¼Ä屸µ+›&؉y;á;D¨ß2i° í&3¥,êÞsä¸÷ pÇÅ~–šûO’9ÜKäÓNæ·CøBMÞ} òû¯0þ¿"g™#WWeã8Ë”r¶ãr¾b‡ET`¥Lënj:' k‹¦ê„c|£+º à›‹Ðª6dÒuHªm‰#%4ÉtUt}[9r©Êêa7~¤¡ŠzºÎ¸8ìq>Ü ‚se¯6ñª”ŒÊ³qçîlj»>·´.ôÀ]NïvPUU¥Á¹ê„‰f8°]¢œ|éqõ—rö¿0¸]ëÆÐÞ uc,¿P³~ê¤-ÚºéÖpò ÿÖŽá€IÈŽá€* <’ E$RðŒVx{š-Pä0úwÚ\“V‰k¾Ýò¥‡ â˜U ¯‰^kü† †œÞÄ·µ. ¿`H†$p`û£œíϨÜ~C™™óBbhŽ_/ö¨!j.)+,1¡…ò8àôŠôãþM‚ÇæXp Li²ÝÚ¢¬Z¢ùШO ìw«2ŠÏ!Â~R&PÔûàà8h3þÆ99Rùn_Pýø<ÜÌihà ”Nà7N‡ªí†LŽÁ ¸Ÿû›/iGÒ®GðÙ&L*?ÐjÖÆ†nérG)T”´u\˜Æl­÷E!¾$§\d'Ò&²„¯j„ê3Cðj”ž}ÝÀ iW0ßÊTƒM>LKÌLkÛ]ijˆ‡Ybù I8ÁZY4¥;Ý)øaÕ(®¥ä`ØyéöYå‡o5o³M²ì¸Põ‚W±ðDÆ8Òb}Âàsuš_h«Å)¶$–»‡¡§^¿ç"ˆo|F/‘×€ºÁ%G 8:±“#kç‚4ßn¨y¬¹èç¦Æ1UÓ>Žý8:Ö²ðØ¥l}éu~ƒÇmi¥Q‡Í¡xí[I|êï‡÷tJC?¯i"7á‚èÒu¹q .Ò¡-$#aÁ2˜·Ò6%Jg( |«6lÕ«º˜="V4í-ýú’R¾¢=œÑâ[JÅkZ„~¿ ÌÑfI 3If(¯¾£”×ÿ”W„ò3½þŽ’þƒéuLú(^}Oñ;P¼ºÌÔßSþ”¯ï3Í÷¤¿é›™þ‰§Ü_úÔ—>¨¹_ë|+=·œ§ô+ìÁ{ZýµßIñ9-òüž¥+߯SÚÌ÷ eªÙÔ¥W´3Æ·sÇJòþ‹›{C×Çã’.êk)?…Ï)¿¦„¹¡Düe…ˆ$oQ Öù ¬ÜDãØ[™ö]íjÿjs€ýŽ6[Q2ÃN^LfÎ,3û;OÄû{8DžÒ•ÌrßÇi}(~æ©øØ—Nnµ +—õ %Lš3=‡(÷r¼˜¥á7´3¾7¼_€E8ŽÀ 1½£ŒÑ"Lõçá qå‰ÿúÑÁï{ЦÌ™¤Žm©(‰Ü[öô÷Ïęƀ$¸£ óIL<§B‘Óû²K½è¡ƒ= AÌqš/¨C§n]yÿ;¿B–ùZ´ïè¸Aƒ»6hEBŒ:g­~Läÿ…-QÌ„GglŒeXzeM>|}Ë+'ù@מøÊ¿Í ”Ê "Ä üßùúkS,Û®ëkå¢"m­!Ùâ~÷¤x½‹,¬‘ì–è@<:¶".Œ]æ‚Œà“ƒŒuiv»7((¥ÛˆjÃRò” ¸ñXR¼îKôã^ЋñÁÅ®Ïe¹pî³HɈ ËÂ’Q$ÉîÕ\Tò:Ÿ·‘€Îâð·Ÿuo$ÄW¦nY¨¶ª ˜½0¤²üdújz›¿™éà+ùü˜õ°’ ê¥öåô÷®š2[¦ï|éz·È¾¶»9.²Ô£0–)ÜŒå®Ê~dƒ©åùŽ SZ›éÄh/U#ŸCרÅJfôpš±Ù¬‘k¦vÒ¶zk[ ªª±ê«qƦ½îvåiö84N.·Z (~$£AkWkSÿ+™ß¤¢WáÖ²¥§Ñ¦•Eßy ]X¶µ£4ÝhÖ%&bæ9‘ª¬Æ ‡?'BÇkL÷²mú†\±5šKŸSRˆm ÝC*Õ• jpó‡IÙ4™ÊœýZ÷ sL¸È›U¿r )ª‹¡¾éþ>)¬Ý06rÃ>–âýé&—o µ¹EŠóôSH\O¤mÕ·!Fb³é‹¾ˆipû·5Än¼KúÞ¼ÝÈ l{<]ѾJr!×zà•”K.ˆEìÇ´^O~‹ –9ž«éB (Kbùq2ƒD3â£Ôö ¦Ã2͘×H^(¹ð•o‚³ïœÚ†<»±Ç ¾}·ïÀ¢…i=–>”}¸œïwœ˜d¢UÍÊL¾¬E< u$´±ÄU‹jj;³„ÂêÈÍP–N«™>W›ÎJ)+ÿ̶Æ4RXmoß‘zâKØË÷¤Wiöél³k¶… “I ªÒ¾âPÙk,ÚžÄÊ eCŸiböÖÕ94¤Õ9ïvšBo4/î˲mèê HtfºU­Èi{C 1½L:fDf.ü6w, Ä>”ñDŠ¡Òýa„½ÖË…=7Z£§ƒøõ<…,×ôÖR’ZÌ_ëÙ‡7š½t]Išõìõ½[fö>‘ùDŠJš)Úì¿Õ¬ ç0ödTÉ|](Z¦tΙœ ®"6OSŽÞáyÉ[Í»ts—ñ©C#yW`¬q±‹“Ki…èŸHÕÈáÑš?´] |*NU‘ê€@"àéÂɋw¡Ê@ß(ùõÆ?V¨e%-~B#B¿‘ :hh|48œÓþG/ ¢ßë榘Cp¨²×Á“8ÍÀ†—R– ­~3j“Xa”‚žœ·¶)5yub"`ì­ /³“CøŽgô(u³1”¶áÃ/¬%¸À‡8 °4«£€sfà-ŸkôŽtþŒ$€ÍŠ—‚L®ZclhÏ3Ò†î×¾¨t—øÙÕ4À"w¾R¡µá,~ŠPrI?‰_çßBäoÈÄÝY-†]¼ä mOŸ ¡Õ6Æ‹Hõö<Ðÿ×üÛ]´­jà¶;hŸIׂ3øCÌ 8<Ÿø«¶êö?2žI«¡è‚ _ Àtn1,´w’ËUÑ5X ¿– %*P8éƒ"·"AAI°üq‡?T7XFY—²cPXã°u_¨²´¡àvjªÄì3òA¦²«©ùE2Ï,±0à« jF’œFâ¶Ø¾W‹íé•s»* ÷ÀÝLy;šÄÉxÀò–ˆÕñ@›S^mšJº-F†~nåY[ÛÉrÇ 1Œ„[×’c¥ª*tC8é€pÓä¸uåÖd6Hð5°q|‰oèx7EvÙô¡¨ö‡û¨ /Í/ˆ„žËJ£fÿΔg\ž»Õ»DËÝE|ßýUË/0ƒ„ {œãÜɹ#.(“ˆ ÀÙåëäíô’Á:æï“]ø´2É¢Êac0K¯‡¡qÏØ4]q”ÿjÎ]2«„ÏRw‰§8¯¤ÎypzwohÃ'é³7É»QáÛ³yŒ A]Sq¾LÆ0[Øð7XÌd9}Ÿ¿Ÿã2/†b(kú*ÀɪuyLŠS*$Aã›^. !Id‚¢ÒìÎĉ«¹”¡™“>vuñ[3üÍ»¿Â”&ÛÎt$à-ñSú×á·?:OÉq {Â!+x½óŠÛ®IÒS¬ËÃ6Ñ×<—xF™ã€¿mðvcuãRõ ‚þÅ%.ª”¿Üø§ÿrN’™x¾Ù,Ψt±R”¼˜¼ÆaC™9;qLUTƒ"ž¨ôiÀ®ž—–PÞ—¾ät¿A#,h¾2¥& Î&ÿN»x`†\3Yn¿R@- “yNÎÆL$ h˜üäJÏË·R"Ó-"­*«ýYAÿYnž0‰¿C½ÀK¢{~|áé›Ë|œ^U:¦ªÏ1…~ÿ²aé+sNNnñ¾fs]x$æùÎiÏuôd÷.?òôdÞ,uÀä”"Œ7E¨ëSÎ&ѳe¢¦^ú+.]“¡ÐÞ_q±†çž—¬xŽçÜ+[{ŸÆ:ÆÙÀÉè 6ÀY®Xr:q/:ß¹_†$¿™¹Äã—8“G¸Ê=‡9õpÈ‚e¿ö÷òãoï &¶Â,Öp!¶G,u yu3ûû™NŽ~kþùš#vNendstream endobj 187 0 obj 4998 endobj 191 0 obj <> stream xœí]ksݶý®_q?5÷v"šøÌ´±l§v'q¦²2m'é‡këa%¶¤X’óèãO´Óß[€$° à€ ¯ävúˆ3˜Äk» ð»Užb•ë?&ñêíÞƒÃfuv½×?^þzL¼;Ûûn¯Í¤þ¯ÀÓ¯Þ®ŽÔ‹B®º¬«WG§{yÖumÞ ¥«ºRÿîªUSYÙ®ŽÞî}µ^möe.Ô¿Ëõ •̪F–ë'6udS+›zdS‡3-‚”¨Ù+¬ ×öá‰MÝ€Z^ÙÔå*:¹¾ ä %ßQ’å}“'ðµ,÷GJ^Áðk—0Ã#JR²I¡^ûãÑoö” …’ág{G?ÿ R|ü)î"ÅÇó¤øà°(ìt-„F%RõãGZÀ•0V,ê¬)j±~½ÉUB”j¥I­TJVuK=¦M–çE]«Fšß/lê†rª±©tNѱœo6¥JÉ®ÔÚ¨¬M%ÆŠ¤Èþ«TŒjnS”ТɊRèáTÏdÕJþ>{éíf_”YÞÈf}K”î8­d/¼¤\'TøAÁÜø.«lF16yJ]¸„ÝÅØòÚl CŲ®jžaÝ(Äþéù0ÎÝ(¤þٯ˖ºåOµö!3Ù ÜŽ–¿ß¨¶Ö]_f9Ê“u€Æ- ßPh]Tå({¬OºÑ[8.ǰ­ÇpYaߪ¼–¾§H6y Ç›ex «ˆµÁvk+*ãêcKeÉ?l¤êAÞ¼±·°ÝFÙ«B £mË8YòŠg}®²*l¯»výÅf¿Îê²j:ƒBŒY{åÂÅääèT¯2²ÌÊJ•qN42m_Õ™µ¥\首CÞ;#‹Å‚°(°(oÚÚŽU¡:޾ê\¸ÊÑ”-”ƒ„ó,²·(ëõ¨â¢ä å †~Z¶Â=1”dz*óbÄ…~ýì[êüä É„zÅ`{Ës*騮¶JN¢ËD!œ2©T_–Ä‹ÍÉ,‘ö Fç ¬²nyÖþU6wU7Õz¬æ–p{¬6­?6V%~2«Ÿ#©¾ïu!B5¦ÒáÐ%ò²¿¤¼oÆå¢ÍÚ¼€sß°:ŽKøH]R¡ßo:=ê(flö;ìëÄ)œš·¼CfôXß?†ÍPFYQê·zK× kAÅ4¯i}²+4q¦)Ô–`j{’Ƹ´òaFg½&d÷”4\§¨ýCÕf¥jli0†IÓNˆ¬Ëz}£*û£z¬1åè×½”µ^H+•)ÏÚA§˜€“KÜÃ:×úÒL—´ÀØv\¦ƒ !C¥E«´¥_„P¥5÷bD©ºµ¯ä.ÂX}ij<¤3›:·)ò¤nÀï‚|c„’H#@kæ4\‚†Ó¸Û‚¶ m[^Mðë;ïôõt¢¯CMv“"Ó—Ï)ùÐ×ÝÝW ÉÔcÐ]ÔÉPxQA ›5Rê¥}û°öÖ}ÈyÆQªyñÈ‹“ÂÔåM+‘z-Ôö×¾Ÿ¯RW …4±[ðn–à$~aþ”]cý/V“Õô°Ò\œ«œ×‰±aÉÏ'‹ü)â55—ñ&Ôrœñ&?s†×4…ñ1· \iº¯“¯aǰ6Ì51þèÇ7èû´ÒYo(¹Ò…=Qcu p¤Hóu"ÂaÂè‰vMëNUþ¹md¯3“–Ì}ä–]ÓÛƒ¥g–Ú2/ÛgÚ(kËÊØg:Y­‡í3ftûsÑn­© MÌa%™…ÏÕS5º¢Rþ\K95º£§yŸÌGÄÍÚ h“µ5ÊÖ.Tm¶ A™Ym,âë5¹+g *²¢.3Àëxai ,…¯7TXÌô·É‡Úô' Õ:;SLˆe`Rbì 9{Í1²íXyddhocwˆ™üÆ£” w$|Ÿ`°ÞÇC|&Ò×è—¥Þý‘%/ °•Më1?F1ÈôÿjTa-?aSÒ¦òqÈ»pd¸käèÑ Æ?Søö‚C‚$ÙÊ20%mH8;jÊ\JÜ•}b'Ç{|¸€º€aÅá m;íûÚ/¬—ß ŒÚÑïí‚2¶âr’²§• °éåpžÀ[µXiP<‰Ø ôœ.óŒ,Ÿ †žÂ0V2ŒxÎ÷Ç!y síHIÛœ˜â8glš)™ÎjCYU)´¿jšåµg¨JdEÛUñçºÍº¼ä6V°''Š|èhšB9½oÃÁûݱ] r9¤®Ï5˜ ("ý{HiÚ–Þgüÿ8Ã»ÓÆà &ç`%šK§’+ìÁÊö¡÷Þn¼b<ûBën.˜ÝÂlÊg”•%ñ<ê­G×1°±b‚×öÞ\÷ WëÌWŸä• €¤Ýàa«‹i¢‘¥ŠQ¿œf¦ÞÉÝT¼6à ÛíÿÁ ÑÝH“dÉŒ¿CVVfe×Uëß)Ø«²¶ÎËÈÊŒ‡«ÁÚðÈ:PsïB<~¦—UÆÿ)²ø |f‡…u¼…A¬°—°b–wKIvê'ˆåàqIRÊÏþG¦*2–Âaå¾…%œ¥ZÆ:t +Æåó~V8Í3hÅ8Û¢fy¼ôJï%/ˆÜÓx¼ 4ì±åÃÜÓÈz‡œjìbŽ×›$tÑ-VÔ¶ô TŸ,˜QímÜõ&«à=´ÌÅel¯<+s»åí´EŸ$}®¤zÿ½[;SÄQÅéXÖoæ\à °hŽ])…ö;:èÆ~fƒÄ65o} öp†¶&hÕùc´`Ÿ€:T&/3¥…Ä ÿ„…q¨ç  ½R÷ÑVÁ>¨æJžQ&–ô|¢Tv,ç)‚ ©{Ï" o üvVò9~ÇØ òuZÇeLAfé8šàà¨g£k dAÈÚ½[š›[fM숲HÚI”…ÛL[G;nuÇb†40æE›Âfá%Ì;‡-6F€ØëC7&¢Ê¤¬È?`Ü+Ë åïne7 ›dÌ"KçAÛÍÈ.«ÝO?ÆÎDûÀ»„í2áòD«ájBüŽ÷¡xX â+há†Q9kavTÎÖa9ãßkïÔËKïîÇã1¦¬n~¼åKÔÕ1Ù³áL*mV&cYb—´±f @­[)Íœc›‚ð|vÜíªjB_C¾VÌÞàšr*˨çoD™º§Ï<ς׉dçÝ#4w¼$ÚëlC˜Å¼è2z½ƒÿRe]Åbíﺄ«– ÓêÍ"ptgԙ„hA$ö8ˆ™†4‰€… 1?‡aÅgTÀcZIcjC '+‹3=Ði x͆úvr½’>ç”(}%vd9@U“ imJÌÙ”.câDÑÚÎá Ä.þ`sÿŒ'G Cœ@X÷—úó±©exéÆkr fÝ™0¥ È„"¼ýi4 ƒÈlÕx¸°±²C€~ì¶R¦Î'rc!öXÐ-Ò$ÇcÚõ§²®@ù‰æiŸGšaíc ŽC^1ÿ¿ä¾°Kk6]Á¬ØƒPšì»ʆ;]CÌ` `z˜Z—¬ÿŽßdÂÜÚÊ^äœ[´ýвvC³›uÌ«Jú¡ÎGëÑ`sShKÂ+§Æ&nÉ%™¹}pÖF|Ôwv¢¾®†/vpä}È€Š­2ã‡2ƒò\ÆÎh•rOÏ:\æ¹s†똱Ã3Z2™bºƒ`ÊI-ØI\1à ií謥Œ²ä§­ñn÷hÈÖ—væ’‡²în½±\Ýf Ï•]ÃZ“xŸ”^ý¿¶W½¡{^ |{ 9=£€8е{nSTÞg •BAw_€Rèâ¯?ÙÔcÐÒOý:tÜØ—  cÅeÛ¬ÿL9Ù­_ìR¯š’Kò~JÉ#J>_PXó½­[…’ôß©ø fø‹é¼áÓ¦ï2/'©û`gÏÛpø´‚d7‰VÏ¥˜b·²x”€nvbL–Í0‚.r/Z›ý§ÐÔ™Zøûë…15ƒ÷^ð¯ÉƒË_ß3y cÙበFçP3ØÎ\pë+6Š“„Ê ê90ü]fì &S<_R’LË٠•8ª¹ƒ ‡/†ñîž…¥ÒÙÔŸwˆÍ츥A·Š´*ÙgäPõæ<ÏûêVl -Ñ8j™Až¢ý¶Oè÷<éS.Ú2ÄI¦“}÷üc‚·¾´9ât²pà]Ÿ€ËZûWUƒZ_òbý!€Çk¦¤ÈÊÜ’æp¸û¦?ØøËÆ,î™Í\Â%+>¶–¥dÉæ.!Iاœ°d^ßÝ)~L1aOPH©ëµy÷ô+¯ŽQÕð^+Ù4}n†ßiØ(U\œÊ ™¼ã®³©€&Ž_EÅ 2ˆSÛgŸj¬K=uÖ³èÏ•¹R´EÄ›Âwïx­‡ï0À¢"+M[gŒÐâ‡zxgûÈùD„?Ü ¼_®<Á’ËðŸ#ú++Å€Sà«@νuÈLRo†w0¯i#‹=¦n°›ŒÊÙOÃRê)kp*™­Oš¡¬åp«"I©&ͶЀ®‹˜HwÜÅÜÑTþzmÃ×’tk™s]…ïLGG$>s5f[0~+ØÂ‹FhítçÝ<ê¬ß!¿žüè\°õ·–’J÷³ó]ÙxƒGÛÓV ï[8ô™Y÷Cáwfteüµ÷9IŸãHäüLŸ@¯ /x°OB4”dO[ø”Q»ì`ôŽßMÀ‡³ñaü{=ÙÿNOÀCYÓæÀxöü®Ð͹ù)tÚò / ¼› FâÇ>ÅRÀ l¿ÆžÖðéžjµ‘µòø»Š. ¼mÜ7åîzi6žíýVýù'£ôÏiendstream endobj 192 0 obj 4920 endobj 196 0 obj <> stream xœí\iÜÆý¾¿b¾e&ðPdó6’º+p"gµŠ(þ0{j“½¬]IpŽÿžæÑ]¯É×lά$o€Ø†QÛì‹ÕU¯®þ´ˆ£D-âæ_C]î=Ú/g·{móbÿ÷=ñîlï§½*J›Ú¤.Oô@•.ê¨.§{qT×U\v³&‹"××ù¢L’(«—{o–‹Õ:•þ;[îk2ÊË4[¾²Ô_-õuO©¢SÄe”d%OlW%]×¶1QÎãØRlˆ´ö¤N—¥ÐZÑÖZÈ…7B¾òJÈ#!ßRé~<øÃžf§ÒìünOÿý«æ¯¬ùãà×wÿb7þGÂçÿswÄ]g†s: :œÒ¾ï„¼òŽÎPÑVØÙµô….é&íÂ[‰Ë=‰—–ú>(8}ã’YðÚRï(!•°cM;$´CLÅ-¡­mýŸcŸ¸5r“ETÆ¥†òƒcÜAÁcü72øh?I¬™X›™×Úé¹uŸƒ¿7‚™+#™¯Wk¥¥¶,“FžË(Ž“¢Ð¯“Ee’Ö™^6Ö”Ò˜uf©…¦Ò¼(âVãôte’Õš1æù ô´¯Ò(.j-¹ý’U–7Êè&…j´¡ˆŠ,/ëåsÛ¿Y­3MfE…ë7{î×KU\êýÀÖ,‰¯Ól(Í«´Ÿf¾¯¤ç‚Ž+£`-è{Ôlc#´²ä +U½<¦ ̸éÖ)…ÃN較Õu«Jkܼ*ô=¤ó:mÄHÏÕUjDrÄá:Ï=¼ºÆYíéþÞã\¶ ÓB¯²(.󇎳‘/þæö¹†ó¤Ž²4Η?¯’ÆöÆÎ¾ÏèÁ^…x¿¡¯Àù¹¡ókZi/ã:1Ò^%•GÚùë… †]ÓÝܶlŠKÝ¡ãmž)ͨŽ÷i‘ðÜQ¥VfTU"3‹•*K헖ͳuVè‡E±X÷èÓô¸¶8q*ìði û8±ª£Š8i`ßð%é÷Z›žƒç±ðÂÅZ ÚZÒÖŠ¶ÖT3oPEl8¥dÞÒ¦7Ä'8û Û´Ñè]:” #c†Ø¯QL]¬eªJ–?hϵDľÒI>iT—‰¤+úʰ/Žÿ3àɶ>íLRQWËgšYÃÀ Ï?¡Ã©`^ÝÛ8Î}Ð׫:*«,«a`¨>— ›·0êàt6ò‡lÚTuÓ–UÀJèÄJ(¥‡uRSDIš©¡˜u„Ë:ðTê =îà)ë&wH0g®QswéˆGÍÆ<'ép(YÍNM±³¿¡¸Œü½^Ì SÅe9«SS°^@iæ§y™+Àª #òü…źb ñ fø|­¨é0¾,,@,¸ÿ(fZðBK+^Ã0‡ô\¸ÆÂÉßÐíÀ‘QÀ…=pD4ôÙK¾"GÐ轿z w®é#þ¶hrE‡Ü@_¢¤²2*Jê} ,ƒ 6ØêVžk¿¶rü)ÀU—FÑ©…™ÉØ¢™SºÞXJÚ@ìÎQy-‰Ðc¥®÷Ê•– Ûö£GLl‡'«uÝp-V2Þ<Î}¡Ø)03hü<lÈÔ;z Âd[KÛ2{ ~k^EUl«WÄT{o ­M§.Wàd“8ªHFKrVÏ-%Y.É%=µÔ>yúØR¯É,/ÉÒö æÊœLÜgÙêŸÈ>éö$Žö˜…YEIÞŒ@Ò “ÇŽF˜Æ÷–òèýrZYµÇV¹9Uf®8.ÍŒÆÚ¯|Zaä‹…-PƒÆÕõØÅ]2×u¶üº¯d|%¬ÉéÀ:ßô*¯'µ–B'1ñØÇî 2÷mÏ ¢P'~†_°»>|faÀÄ7LA¼xÚˆïfg‘¤ÝÒ‰ ãNIH&`HZ8d—Clm¢õ#ñ`1TÔ M®Ý“\R8àÔ´’¢Åg†gæ:**J5€Ad5J~ºžê™ 0ˆ~)Ž<š §xF¶t˜„u<ž¦a™‰ò• Ú¨j†wãמ÷[ü0@sÌ $½C1öF>­aæ„m­ìi« écW•ÌíGQ$Gÿ,9’¬a %êµ]|ë±g"},aw†3°´|Lõ"& œØd>¡ûÆu‘ghB9;®ù!ÄòYê.;ùUGZRL žf‘#4>uuðHG`ªPNà ~” «™­®@¡»´\ZæVPk'ü²Q‘ÏœÎ74 }}ÞÛç*ª%ï¤á¼IŽéÿ+1 ôµ ¡y>´0!Ù ^ˆ¹¶ #6Û'Ö¦' †U«±Ž2fjŒQ©v›ês”Ìç©xâÝŸ˜EHê^¸Éw€S/@Gí(ÏÑæcn,±d$¼I°P;YIríöY4Ú®ta§«lG;æ…8ær.ÞºB>7,öa•7¹h”e¼=¦ù)åm ëX²ôõŒòeÖk ÿx­  ìm/3µVGv£i@:¤¨J·°2²Ón~ôb¤Pµ U›{u³/ÓüÈ2öµЯª°”³í R«ð´bD‹4N¸Óâ7›Ãõ§Ü38Ûy¦tÇ+S¾ûyæzJ•[£æÍZgÍ%ýÔqpöE‚[3øÊVŽ¥†ì˜F;tÒ¹Ȳ€ptißTÿg‹>ý‹„Š>Å—Íd–©™·.‘HÕN”O”+p}5X ©é”Ç„V<àÎùîHáaÂië‚”UAT²Ñ޷袴žašcÒQ4rXê¡+“zq<­W0C؇Á}nû<¶±ÓÜ$Š«Á¡ Ž4w]_1õ„âî÷öù e7S¸ @Uœß˜xÜ -pîfDÌl{³|Ðñ=1Z³áî"t²è©Ù O¨µp÷i„›CGÁq‡;y‰õŒ))‡ðQÃ+¯´ÕXùG‚y4Ç"À4í({ÊžbK >w² Õho}³ºp $ˆ‚Þ<ˆ˲-$ñ„ Gp=üEïÜ ^púv>ªÓZð‰]¸„Å %p¾jP©J4Ø&ß>ÃS'áƒSíð„ÃÅ8´O…= s3·ÑŠ”Œ>UP±µìq™¶;Ä´™åԷŶ {6˜µ‘O5£Ï¥õdáÖ8°žá!Ïw !&3p1q"†è~œrÉfÝF|ê΄þþ® L¶£ëÑÝ6Ôó¥J~XÖ,Õ&]}é3†„ 9C?ï/w[ì;þ=¥/ZÀP¡ª¶¿Må\´lq7'2ï& ð‚B '¿íU0u;uˆ‚·ó–^¢¾ÅÓwª¬ºAIÉË(3#¦Oȉ¾„ó.âÆ+KFçÚp?+2×[›ðS $¦¾ŸœqS¼Å| =ƒÐ&GvC}émê;ÛÊÒ8žÚV¢œ„3É‹{AS—r'¡0”4 ÈÙ;]ªŒTZ‘ÄàœÌ¼IòÀ›÷x9¸ ä‹žÌ Ï­à/>ûá¨Äq¥Ñ¨2?ø}3¶s^P£i ©ÚN¥# ‘Ì~±áO¥ ܆3ê&ƒó|áHa?sòg¶ƒèG.ŒN\¸B·ŒËª2$òäë耚4‰÷J¡)¯‰Ñÿut­ÛfÂZc·{* ì}!ÃòB¬Ü'ø‰ ›ÁÞÞà¬d*èzbë©R0»ä6ÿð`0¢wtîp3˜õúzxà˹k®¬=ÝXêÒR'è?±–ykG[êŸógâßE¸¤ä ý|ô#ðEŽÄÏË;ÎU| ¢š}¥ò¼y…~©Êt$V9¯öX6öJȧB¾ $|Ýáƒ!ù7JNè ÀÓSÚá‚.ü¦Êý¿žÂ?¿ñŽÎ{D_ó–’Ðᘈû$ 5L€1GäÃ)…;ªÒpÝÁ×`©fwûôzWB9ÄÏcC[ßÓa Êc`֙硅OÈd#•›ÁdÑÀÄaÜ4â|~v¿jsH™ÅUåÛª\L3Ö·ÿ ÿR͆λ¡ÃæŸó\]2ǬIn?>€c®äv<Û õâжˆýbÇ<[Í÷Ïf^Rð5qf\Ñøg¡¸ùý¼Pë=¹´<Γ}>„}¶ÅápÅá'Â}@ÍG!&>TC9  rŠOGÔC>Ž@>ÿÛaf¦„›âu<~¨Ü„·~GÉ`(óÒy¹Èƒ¦Sr¾ÛAÎÇgô0Næ˜2ˆLñ§”äžßWt^ø ÈÝä'ŒB &Ê$¿{‘dý–’bñú[ŽÏÓñI\ l¨ÌøbëŽüá6~§ ÝcÂ_ØW—o ¸†{®WÅY·÷Ã^æ†ÎÀãzŽW ²|ggtÞSÊæ À ±PÅó~ìróš¶îôªÎ°¿-…æî3d¤~Cá/òw0ñj.0ˆ(?w9:Få+ÀÁX€Clj/|”ð­ÜÐù1ÕO šo“Kí(åŽ?ÿÂsŸpz F·t²;ÚÊ#…phM>¦3,èd×tÜẠ}w ÎåN„h¡|œÜÕÇißJlq:Æcºg.ê?S^ðSâîfÐɸ?À•û“F7#/ÊwåùÁÞŸõ¿ÿö…zendstream endobj 197 0 obj 3679 endobj 201 0 obj <> stream xœí\Ks·¾óWì-»)ïhð` W­R9V"™¦+•²}X.)’1EÒ|È–S9æä`€ÆÌ73»|XLU,š 4ºýÂþ<+ Æg¥û×ë÷;Ïöôìøz§iží½ ÀÕñÎÏ;u!ÜM…×ïg/öí@Æf¦0j¶ÿn§,Œ©Kí±²™ªìߦšiVÚvx¿óý|±dªÐLñùÉÂ63.õü(B3 ‰J©RÌwK^ðZk6ÿ2,vehÐõb©‹²dJÍOÒ~FÎßÛá²(µÐóÛfफ़ŸÅï«Ôx“P:T¢ÒŸ_¤ç œÙ­0û‡4d.ò}mØY"2÷a3TTµ{—ªnÖN÷BÖr’†ÑÄ{¡´C;|p‹Ô‹ ø³'†¶¶”ªÝóaýFÑßuwï× ëQÛ®‹÷ÿ¼Ã+ìË.û‡–;0uoèW¥X%–½Z0^èÚNð“eÜ©›šmCßÀŒÊÔ-3°¡z-ç/ÂÎUFG54(²«%”¹BèuMÏ&n}iCúå¬Z1ïÂ]^ÃEf''¾X¯&T¥æW‹¥tú@Hº²ã./U’çóútMG\³†o–-ã¸Þ÷Üs5Âm„ìÈÊ)ž ©?–ÓQ"éóEüìiÇUÉæË„2͈‰´šb<²¼UÒ ç9m©¬åDiÇä—•!°ˆ_¥!JÈüzÇþý«õE5³¬_ʤöì¨}»œ"h}¾XZ˜)-¥Emϲ2e)­ OÍr¡ ãÔàlaµw¡´ž±à…ªKÎÃù3aÇœ¦á7n¸Ñe©*+ÁVþJÁe`×Z«šY$ü7e×ÃÚOZ5‚Ö6¾q¸âZYb¶Xé\ÁEìp¾` G*GÀfÖJTxÏöÌ̪%zIÖ¢K-! ‘‘‹s^sG.{ŒIÞÒË·Ëá>¦¨-FwrV-šª°êék·'UÎæ/›H¥-/´kY;ØêK¦•±„·w´‘²!‹±Ätú”Žû•Œ ”¦¬Á}»“¢ÒR„ñ溳*ÓJ:íZ>«‹ZUU;u£>ÜrLÙÐÛm¯ä†g³Dþ̲Gv4°4ŠdMú„j!ÂÁëR1{ð-ê!±ÙèŒtøaNFúÛswt´Ý9õü‡Eè"kYeÚžR–X\:V±ƒöÿ±³ÿÇm쪯Ë*{ºv%-Ä¢ÆKVÕñë ªŸ_ž ªŒ|:Oªìª£ìúAÚê+¢XOÒ íåÏ9¡gs8뎅EàåÄÐ!0vuÇÍ´[GH¬ö$nÌwÖI]Ç\Â=“Ù>¤»1O=û±{ëJ5«[ëŠPªg;¬ÐN»¤=[ÖÌ?.ìN™´¢ùYÚ ¡Y^B´ÅF>ƒ᪠HµÌ û;m×ÿ[\?¾¶cG|êxyøþƆYéF'Që?»ãø&Ù^Ä #LqÒål¯wžSJSH)[NÉPqm/I£³KJí®ÆÌìZ'ŸkµÇeÇ2rÐAÔ2I®ˆ=°Thzf™ÌôÈwÞB¯eEO‚øs©µcÆR4o£ü2!l33%²„ï?¶«•÷ìâPì1‘a„V¤o£ÔZg‡ËÁy&<œ¤©¾J$øy5Q:23µá"®ŠR¨œ‹ºvîm¾ô¾¿A}Dt¥`‡ë~ìülëu¬ W›÷‰'U\$mÆé/ ã|ÛüªÄÞ"Ö\g ›H®+¢Ã1=/ÁψºF÷ö/É#îj£.ïµ£´3μâÖÈã-Ë ±iõ6r$q¾ÛFi…Ò«)n¬…¥35…"GÏÒ"yTNeü<É=ÛX]ÖÌ´”-91»`©«,zї؉Ü8Ù·ZbÒâj5²u»?púoÄ%l#kD <-­¡«ƒqÒ8’ö«t^åýMXHKùKRZÙ陲H©l¨k`µKtJŠö‚$ñȱ½M4½žwiÝõÞ¹ˆ>†õEêö, EÉí‘4!QTÖ3q\ ÷: wöÁR•Žs4NÀë8ê4B¿mŽií;0㬾¾‡àQÏx“Àëä‰0¼Ž„¡ÜH–ðE\ín¶ZV[äÍy°oG©©Q«´Â5oávIëYWpç„vWƒDèìû/qß<ξ'÷B6pšÀKØ—t8‚›]¦ÖŸáÄSOð¡'b£oïÄF*ŽjÂ9VI׈—[È \ô;¸«ä³ H—‰½BÞI›Ý£ßk½ù^ÉV0Óõ_Ãm_-йCâ‰1›mò“UÕDPïòãS!¿W>°Å´Ü*â·›0¤hWµUʃööÐÜÝþ4æ¡S²Dq[…åÖfÃàš‡®ó ]0l”s ÚïBI‘ãºññ¶ª4[ ÛÀ`'^Qw†!0»’ÉMm0|•fi†•–z¾yÀP ã.ù6^­»Z]vEªP²²ÎCϪîdH`qØØÅI®É Ù1ì°ÊÌ;+¥Ë%pÈ>&Þa‰«ð&l_›ÜCždÏk ÔÆ aêxÒÌËüéè 1ûþ:!ÞÂa *—Éh½³#×Ý"ý‹àó¿êÒhØ«$á´±pg‰“N>9;ìÄw9óœ'RtÄšì»Mç»ä‡÷ã^&øˆMî¼qÖ¬³Ã²Ü«wѬ͈ìéŠþbBrÖ³ NÎ&åòUkp"UCÊ#ô$TH¹ßë.¦IïÕt;4a6ò©çZv½`,ÃxÊ“èúá®$Þ{†R#1Àæ>­õ¢rº¹Ìj%:Ccè8ŽÂYš¡€Öc^·41ÕV·tR)-ÞTWJ0d^‘§UHf'ìCÜådîœTÆˆß M’ýb:’¾DôqˆëMÒŠÕu?bnïæK”%èêh_žS; ÇÓ“=xG¤Ú.ˆ ‰…––Þyêä.|U0‹S%ËÇEœ"Ú´mBáTTºñH83ÊP눋’Æ8'uäÜÝ6n +>ï Kzªöl—rÆ8yšÖɇ w¿’„xV7CŽNá}֌֩Üòµ?¡1U˼ŒÐ>üu„^¯ßEè¯ú àK³ý)BçhYuŠnè½;î‰sZiCÊkpái=ÃH_RÄ‹éÆëz2d¤LÊ?Ÿ¬.“Éb›(£:…›ÀuJß'°„Ålu o£öÚE’Ëeä/–€ÍÓØÄÈÿŒÐE„.#t¡Ï†û‘c±ÿö¬õü_ËbF&­¾šµz4ž>„#æ;† áÃóÀȼ°J•„BÆÙ¿O±Ú :Ã(§>õkX†Ú‡bÔ'‘?4jJ&7Ó½Cy/µ¾Là+Øw ‘‘ªLr𧘞°.ìöL"JeÝFâ/íãTxfS` xk¸¸–Tßb?„Doe%Ðáÿ²òédåmj}“ÀשÃß?ØY¹ãeC4=aä3¸ÆsØ÷=\î "ÛFİaBÃmn,bh6=!èÉKØnj%²DZ_'ð%Ä€…°~‚s‡‘‘„‘ïgp2Œ ‹ ‘;rk`ÑÇŒþ¿%ðÙaŒ9ñ¼âþçeæÁ‡Ÿ®ƒç2þ"Í£Äw&¥ð(O³˜O'ìéŠ6ÆC>" ùÄ︠|èu#ŠÁäFC_\m€çE?eAÓ >õRuI KDý^%G÷ɶ©ð&[ÄE´'´c¹öN´,Ë‚¡aÃÝ`èè#ÑûÞYÌ`›§¦¿Ûå˜M·ÍÖÁ«õ÷ßã¼Jwü9ìƒÒ=ü>Be„žGH'4?þ§>ôuÏcž:>Ô+ØÔØ2F6ð[¼BVϧ{)æŸÃÙH0ãYÿ6"æWg”Bš»ÚLd”…ROJ0ŸŽ˜|6NðÀ¾D\ˆÀý1dž£»âB²y[×p—÷—†7 ïï+ )è¿í…ðà2ðD¥{z“"AF”<~5ç@*†û+n¢¢ïª•äz¡”Šz¡þÏ<Ð|lJ†=0o?)NýdÁÂ5œ" ÈŒëí l$G U–ηë=É—¼(k¦š7®ÕRȺPÚ}f—™±* `tCŒÇø…p? «J5>•ýP±ÚñžuÕL­´««…b>®  )M)ݨ í ª[j—ejçú/]ê]‹Zùß+°pÍû“L»¸ßÝ*X]yo(6[jÕvfû_ãg'ÜuÁUY5E†m‡´¶Yö½îŸ„¯ †êá²§Ÿ›6/……Î_( >+ð¿‡ëÿa©ì`UlËU±!”ª+J½émÇuEú…`zÞ-}{¹¿óý÷_ð2^endstream endobj 202 0 obj 4270 endobj 208 0 obj <> stream xœí[Ys7~ß_1•<°ì0‡ff'O€!Ä„±7GxØËGb{°‹â¿Gs©[£oF³ë )ªÙ•ÔÝ_jI½ïÏõÇËþTÄô w3qvNzùÇÎæÓ’8Þé½ë Ý0û/ÿ€ÓÓçÑHNô}'uÓØm÷<7M‡^R¬ê;q$ÿFNâ{n"ôúÎÚè9'ÕùÝÐ=ï~x-¿„^à»Bô’t£$ý±¢ä×þÐnò‘Œœ©¡{Š:QÔ‘¢öë‹ûiØ¿ Ò!’ 8¹Eä:‘d+L‰Ü…Œá§lÚ)‘s0-ÜÞŽžõ|ß Ú±žÞn¬™žˆ\À±G6Ðþ… Í¾T#3̘½E«¹>«¹÷ˆ<„–?…jžÙÐaë. &Ã0‡ðb{PÃZž”yWšE~s'³QTFÚXóÜ0Šc/ã s¦ˆDb[QgŠ’Úʉ‰/2s©94r¬( \äzž/ +eJ22Êl(ä÷a*2MäJa”öwò9aà%¹7”ËÓÈl:ç6Íþ½ I‡DžÒ(ÆŸÝÏD £$ $Y1˜—¢ ³(d²¨µ&4àbÍO\_\TÆu—FÎáRÛHÃV|ÁºÞ»´jµÄ|þ˜¾Ÿ°¡&!‹M+|%{¢ðb¹‡Žf½×™±ƒÐM†±RÛ‹J[çó­¸íh§ÜDìëEƒ(w)€Ò„ PC¤Ô­%#Dz¿ázIäsL˜ XäÂ+2ÞSԡͨÒh~*yI9>¬¥1¡Ì[ —¸¹ËTä ©ôc>-”ÿ—æTö”Á;[¢®¹ÃSnI-ul|+ݪ¡ „4Å0á‹?Tßo)j]Q€ºMeÈw—fjæT}ÿ,N ê`sUCÍ*äsû¤]à›EÍEðSùP™LÚQò‹½,±h6£¡‡ æ %É5¾€ÚÀRœÖuÑÊ´9й P c(‡YÚzA &ÝÂõJ¨*`⸡ i ‡ÒV¿P#©rÐ@úší¸g°Ôa¥È™¾“Ôw–Ú¦¥­¢í’lcWd1@ÄC®9ÞE_­…®§Òòå|‘©‘–&i~¨æû4ß'V‹íŒœk;µçOxÍWmÄMµÚÈ/ŠŠ(Œ]?ŒªŠH+5èeÑ{~ÿœäraÇÈ‘\!–ã ¡`2âºc¼ÄX†U&ç %"ªw´®VÄþŒ ¥Ç‹L ^ ¹›â*•–]@·ÄE Yz¯1K®XĪä* sÉ5ÉŒ܉A9“e=óLÙó¨5!J泆í‡n–FÏáFϲ/»ï؇‰XÛè-ŒÙæ¹€Ü ¶Eœ60Ö À<ïgÖ" PMvÀ›–F2ü‹Lþ «ÁçDŽˆ¼«M"Ÿ¹?¤U‘‚€ (¡zâ´#Úû6l®bq.ìkEyà[æzøj0!ò­…èý žiâ¯iíÔåAPñõ³“sp…œþ—6Ÿ~‹H‡Ëœš¥ †¿5±Ì@Ìl¯8c#,Ž:;EtÄ3ñlÜ?oˆ #7!D[u02 ˆ©¹©•¬Ìa$ºr'¡§@ÍmðY£š­¼Y*üšÿ‘ÍØÕÛ7mÓð= ÊÁ¢‹¯iqe78G_­‹‰Jm-–®ÕZ¬Dc× ÛðÓ¦ãx©[ܶæ/"_.1åœßÛíIɃÜxµÀ¡ù”|O»À~™kܼ´­3ö ÏD²Ìþ¬€Belh§ˆøK ÝH(,ZV®¡Â¯1‚ªÝ•å³ÇK˜Î ØtÛÙ“ÛÚ®%¶®Ì•Ø,†$žÆöPö¦¶Î%3Ò(á‹®~PÔMÒèú‰V®^Êš®ŸÆ€ß p!7]b‚‚•‘ k¡ö¨‘Öi¨4Añƒ²ØDÓª=~n¢"7,”iÅHö¨Š®]ë7°8ó„õÀiñÝ)]ƒÏFÖãîÕ²ás[^y3í¯« ¬v^z>¡Z=O³¦8<íïÕ¦½à en¦±›ÄêÑÌz§?è1ÈÆp,zm¨DT2 JŸe‚4¸E¨Ù=ÐX³®¾dGO A¿Áuøau>Ä÷ûV?ÄÓ¬~ˆ§YýOÓü°û‹Ò$wxîÐ÷+Ÿ½*?ÔzõY ¼zLÔ:~Z7Ù;;5 àçjÖL¸: ¨S{YUÔà°£(­«S=·²GXc¾ÙÀüÔ Gú‹/_‘µÔÛîÁ&‡¡0ðûø1ÜÚåǺاï3–øMú2Æ…N±ß¤?{N¾Qij´@‚n]Aàzñ°rN§Þz`*ÊŸS›BMéRÜ6Õ RË®¨ºÖ”ÉzjƒÔ |­§v÷3Á~…)jx0Úo‹¶‹psë[ÆÈ13ÀÜ™€d+Á“a9 Œ£ªÅ('µ’ÂiBYŒä4j¨Öž¡–×MšÕÀ’ÄMÓ$ß`…ˆÜTäû«(8Pâa(3mXb£þ -E•ý2aîž‹~õHo4Ê޵”¥úw|åT¼ÑÊèvb=0YÇN­Ö6åÛïÀ±S¸.þqÛ \öd¿é+xMïý7kæíJþåE¹éb¿”tìèe —tÎÓ ÐîF ßwD²s˜÷Ðn¸¯Ǻ?ÅýHˆd Uˆfô&dWÚp3ÔÕ$¢·»q‚Ïæ9†˜¦p!˜ï\7•¡ßžMKëÚÁâÖ誛seeM£[£"÷܆?Šþ}R…ôÿ¤¨ÙŠ˜€$³ãï#iq—ÈûPÍODÎhðe2ô â`Â{Ä |àûîìaîLl³´Š¥Å‚´¾–„—$ãïv5}SÜš"ÅÝE²Z•{µ V¤h33á¼¢ÆmÅÍçדj…XÖýír]1D—ÑŸõx¬¯ ›Š*ü&ΦY[h=æ²ß™âËwÜÌŒ¡ ø—ª‡\Íæ(¸´›€§é%â7YÞMð)Øz¾Ù2à[‹'£ÞoòÏ#îœÍendstream endobj 209 0 obj 2475 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 95 0 obj <> /Contents 96 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 105 0 obj <> /Contents 106 0 R >> endobj 110 0 obj <> /Contents 111 0 R >> endobj 115 0 obj <> /Contents 116 0 R >> endobj 120 0 obj <> /Contents 121 0 R >> endobj 125 0 obj <> /Contents 126 0 R >> endobj 130 0 obj <> /Contents 131 0 R >> endobj 135 0 obj <> /Contents 136 0 R >> endobj 140 0 obj <> /Contents 141 0 R >> endobj 145 0 obj <> /Contents 146 0 R >> endobj 150 0 obj <> /Contents 151 0 R >> endobj 155 0 obj <> /Contents 156 0 R >> endobj 160 0 obj <> /Contents 161 0 R >> endobj 165 0 obj <> /Contents 166 0 R >> endobj 170 0 obj <> /Contents 171 0 R >> endobj 175 0 obj <> /Contents 176 0 R >> endobj 180 0 obj <> /Contents 181 0 R >> endobj 185 0 obj <> /Contents 186 0 R >> endobj 190 0 obj <> /Contents 191 0 R >> endobj 195 0 obj <> /Contents 196 0 R >> endobj 200 0 obj <> /Contents 201 0 R >> endobj 207 0 obj <> /Contents 208 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 31 0 R 38 0 R 43 0 R 48 0 R 53 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R 95 0 R 100 0 R 105 0 R 110 0 R 115 0 R 120 0 R 125 0 R 130 0 R 135 0 R 140 0 R 145 0 R 150 0 R 155 0 R 160 0 R 165 0 R 170 0 R 175 0 R 180 0 R 185 0 R 190 0 R 195 0 R 200 0 R 207 0 R ] /Count 39 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <> endobj 104 0 obj <> endobj 108 0 obj <> endobj 109 0 obj <> endobj 113 0 obj <> endobj 114 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 123 0 obj <> endobj 124 0 obj <> endobj 128 0 obj <> endobj 129 0 obj <> endobj 133 0 obj <> endobj 134 0 obj <> endobj 138 0 obj <> endobj 139 0 obj <> endobj 143 0 obj <> endobj 144 0 obj <> endobj 148 0 obj <> endobj 149 0 obj <> endobj 153 0 obj <> endobj 154 0 obj <> endobj 158 0 obj <> endobj 159 0 obj <> endobj 163 0 obj <> endobj 164 0 obj <> endobj 168 0 obj <> endobj 169 0 obj <> endobj 173 0 obj <> endobj 174 0 obj <> endobj 178 0 obj <> endobj 179 0 obj <> endobj 183 0 obj <> endobj 184 0 obj <> endobj 188 0 obj <> endobj 189 0 obj <> endobj 193 0 obj <> endobj 194 0 obj <> endobj 198 0 obj <> endobj 199 0 obj <> endobj 205 0 obj <> endobj 206 0 obj <> endobj 210 0 obj <> endobj 211 0 obj <> endobj 212 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœYXT×¶>sΉØ`ŠÄsÐ`K4–(V°€¢ l(ŠŠŠ‚ée˜Ú¦Î0CT,€(†A¬DcMÔhT,¹–$zñú¢&7Y'osß÷ö™fn¾—÷Þç'0ûì³ËZÿú׿ÖXQ6½(+++vvTÜÎð°âßc+áý^Â`ët Œ‘ ¦¼&ìè›ßÇ õ±F}ljßvß^ȳƒŽþ8€²¶²J(¨œ¸3|ËÖX×áAKVxŒ9Ê<ò±§§§ë†Äî'®sÂb·ìpýü±+,"*:2lGìT×ÙdvDDøF×-‰Ñ[c\C7m Û$¾¶<4"l»«OxDxttÔ.×á³=\Çûñhòc\@x䆸Wÿ¨Q® ]—„m‰‹ÝùoƒE­^è¸cãJÿYIQ›VÌŽ[½hΧ›çîܲØ'fëߨð¥óâ¶-›¿k{_|Dèò ‘V ÷1ÒuÔè¡}0fìÇŽK1~毉ÁŸLR­¬ó\7ejò4÷”é5šJ­¦Qs(Oê#ê*˜ ¤æRS¨1”µ˜ò¡¦Rc©aÔÊ—ú˜úZJÍ£ÆQîÔ2j>5žNQ~ÔʃZN- &R#¨ÔBêj$µ’ò§fQ“¨QÔ**€šMM¦Þ£zS¶TÊŠêKõ£¬©þ” 5€²£ì)†’R!O ¤\)5„r ¦QŽ”•HÍ œ©(jåE¹PïSk¨ÁÔVj-ÅQÁÄéäõÅÔ5«VzMéõƒõZë{6¼Í:›ÉBÉÚ®¦ef1:æ76–}ñÞÜ÷.÷žÐû´m¸í·}v÷ué[ÖÏ­_F¿'ý‹8Hpßn¸Ý§v7ì—ÛŸö•ÆJ[zTË8Ùe‡Í·§;æ:v8-tR;iŽ;Ýwîë<Êùñ ÛAqƒNº¬uùêý1ï§¶¬ü7.†{Á;ñUü·®®®‹]/¹¾²}HÊC~ºfhýÐ/‡¾úÀ]xÝOx BœÁ ¦´B}‹5lô²{tµF]^¡P'ò8ˆNÌT¦¦•*«ùûq3ÅOi©â'ÁÖÒU*mZªRžÄá ¸ž1.î8i°ÒÀ˜ ›­/”]Àî‹Wa,­×¨+Êê~3W z ^O'*È>ñqûvÆI"èD9yC+¤«KÉ™äHÓèsà.bƒ §Ó@ÁII%MvÆ­B•Áª¦ƒ'L¶†#s°›Kɶд´RY$•ÜKS^®Ð$ò~4” U’ ó²ØšN2°Šÿ‚ÆñS<žJ>·˜ÁÐ 2C£ÔóÆ-ÉeŸ¢ ö`o€ºVGé˜*L”y‰7gUóMøY`ZUσ£ùÚ!PB7´ºÝ:Vzbïzm³ËgH¯Ü›°WùNÉbΘoß‹®2"‰5ýpî¥ñþ[â.æÈ!’ ’S<58J›…mBYè‘å5ˆÅƒFŽÂNxàOcÀñÆš#íüýîjTΖÊK3¹y™ neð‚˜Ùdf¯™÷þ Ö÷; ×“;¡Ë‹xé­¤‚e…K™ºTÏK›_0Ò[ÕÊÒtNÚœ¢ÌLä»QcßþxL†ñä·x:J_ÂC0ÈÀÒˆW¤w«´êòJyI’hÝ ñh¼…5’6ѺåFëö­+"@´.øâ4ˆ‘$‰–¾4ÇÑì âÁ)° <ñ6ɳçÀYÄ®hãD>‰îA÷ƒÐŸ€û[˜.ƒá7Iigœ—=̈ quæC §ñü o%ÃE4¦¦‰þ¨ëŒ 7CA|[k|;÷â44ŒÇ¿ILð Î #±5@ ‰|ƒ5ùCdêËô‹Ž[O¿^zR¹a3:Ë>ñþj‡uæÍÎÓ&ƒ0˜Ìܽ²Ø?`Ý'#Td¾ãŒnÄ6Ñ0×JpøÆ š"3h†š‘ö‹n¬…\AßĘÁí!Ò€¸˜žïèÜHãKB¾dd:B^D”%«t=ˆÚHÃ¥Îü.w¨ V_‘åw E²5'í›æ¡EŸ†‡®š‹ØÉ vþ¸?¼íê¾Sg¸ýûʪ–Õej䊬\…Š Z°òSbäÞîï /›Û¿¿ëç–rùÉ%ŠrÄV¨µ$Œl˜*eµ›¸>2ÀC‘ìÈ©×àF 5J¼‚fñ UAäáZ­‚,D¬C¸6“p­Ò§æù±0à§Á–—>ÛÑ/ð¯•Ñ>Ë9ðùBöǘ©^çÝ{õêÛŽŸMèYa°vC° Ì­IiÜúdjûHrîcp?ì𻸼€>§›´™ÚLUvn–’߸}Vœ?šz3õ7Ö`£yûâkôuîËvGÜ ÁÑ«.¶ŠÁ‘å~Lµ¢45Ue <]ÄÀ#a½äîÄã™MfE™†D ô†Æ?ê\/‘&2=>i7Œ!rW† “aGì‰=üƒ 8ÂðÂÈð*Àþˆi솠–`z40à ®?’_,oÎÈ$x0q†%6¢hpxK–ÊÁÀÐÁ;p8Ê«†Î8™0çQsÊ‘­† û¡åì,fèøá£þìÍ«øƒ„åka<“I]j˜á‚û‹m±í?Æ@¿‡êoœäqáp&É”JқœÀK_/ªÎGºP;š–>¾Ñ$Bnî>¸õaǾ;kZÁfrµ"’¬ Ø"Ã_¥Ïž8r£Ö»Ÿ;­B¨±ed›R•&­€(ñQ‡!öãËgñ˜žÄú~æaÁÇ#aÞ•fq`Wž^b€B“I“}=!YÓh Ÿu¼üiÒí¡<>dA¸q–J÷Ü s4$Û@!ø3o,õõ™½dß…¨E߉Lo$\·Öß ñ-ŽÒ³dÂÒn!t¢ >Õ8Š0KOU˜…`7c¤ípÒƒNRËËŸã€ïôfþâ$ÒT!&ÐÕªÒ´#NÏ}Ít[4‚ì>›æ-ˆ¹žc1Ý™÷ð³‘ÆO¦È¼IÈE£«ÈYÐ>¥¥Ío¡×‰½'\ö šŒ}±jean‰hum±zRÿimœvZ‰£>šÌþ >_àï„ÓûŠðñ‡|n¬YÑ0ËÛŒYÌü4lîœk¸qŠÇ2ÆwY/‡ß£Í(bèvt´êHs}cy2°d‘áçé«<a4=ZöªƒÀh¦×G£½ Œ::~­ŽCJ‹ðÈ`oŒ=Æp‰8•+ú d&±DôGŒÝáèúÛ½“ûØO™•­BJgrmQa~~w¼ìk]=ªC»sȉ“Òåìviý²¡¦L®ÍÈÉÎÍÉá·Ç')w¡4¤(ˆ)e¥ ºÈØÂ.»PJ^„Š=#<“ýß!m¡ÜZa`‹µ%<ÊËÍðHKë|íÁ$•È+*JLØú+lü ¥¦ÔK˜g Ø);þ9å(Õ s™lkCHåÄJ}e -‘~bFà'“Ï jE'Í+Wkô¼ŽiÝ|&é¡wÉÓ Ç7Eìá·×†é©Ç—ª âÊÉZ»Ê3ªªµ^hì¸ÿyäºbN:4¹@­(s)Wk ²uß0Ò¡Dƒ¤qR]z&ÙÁd±.å`´Y¶D5Œ2_ú|§~­)ìE”ƒ“Y†…€†Ö•qí@ÕäÜ‚ö]]×­«CK9Ùcµ’¤Ï»4& 46`¥IEŠ‚¤.õftW‘ŒlrkÛ²gx"{ÑL[‘´' ‰:º°íÇÆS§Ñuö»É·‰†ËÿKJ™a~Õ‹†"H‚ÑÌ›A‹ü¦¬ôèÊ| pLÌõ‚m‹5Éa²Jñ¬•eF/é>=Åx —}ºE±‚dE·ñOà}þ.ƒ.¼ÞÖr¬¹­êz†îl(^É&••h«¹Œ>[“’¢"™=• 9º¬z>òC±!¡+×Dz£Ù¬7ƒÇý6 &_ýººéRWñ+±Â˜VkXE˜l½ªklëmfἇöºüò; Ò*tò¬œÜl%·!fnÒRÄú„nç¡¢Ì:®›¹Þ@/+h$þ½+×x CõÆÏð‹Ü÷_&f7¦Š‰tˆðD–®T$òK‚Æ2®Ž6¢ñ"Q³š4®+¦€ ÆFì4$¶Á¥6GéPa±P!“ê ¡!Õ.C§ùN­ÞÔË×ÇMÿ&ývZª.å`RÕv´•õžôÁüXäÝžs#o7ÉAˆ%ÁIxSB$¢¬‚lÇb¤º`Ãå˜[.Ðçû[¯ÛcÛÂöñú(ÍŒrmŒ:º*¦*¥ճׯž}üðjÈ¢"#öK”å.å%šjþF¬¡Öü3MÈ ó Gàä Ì1Û`­·OD…¨1 *lÍ"à<ÃuvÎâio³Ý×BÇ+º­Ì›£`9$Óݺý™)h }ç(8Ü7W¡Ò„p¡É1ί ¥'-]9ÏeåFþe.`Ï“§ÓέøÛ+Ï_àzŠuÑñÂrË!Dtg|WÏçð@‹Š]F¿¸vý%/,ÆMf œDbZã˹½tEãS9‹¢¦¶‚'Ö=ƒ(ˆ’ öiÆûMšzÏ$ñ4ö€g’ëæîå$ Ó:ûHºƒ ìÅZ œ[I¸†Gi'̆y²“ÇHM=WMKÿÓ|>GÓùää–!4ÞëIuì(9EK;Í{9˜÷ÚCcoì.lG™-u®S¿Ü<ã• >‚Ê`õÖT e{5J•Ê’‘­ªb®ÝD˜¼ ÇÍ;~œÏ7Ï9xÑñoõRb&™e´d¶ù~çÔ•ð;.Ð÷õ[°ƒ£^ãþÞ˶ÏYŃϗ²7¦|à1ÕsÄðiß¾ýöQW Cjž¬*!¼ Øú;\¥ÜÀºÓpè—¤5]Çaf´|O/éŒÆVÂûB8ï/¢%O,ˆÞÍœ_kD³Ãž«ÖÂÇär¤T&EÏÁ½6á÷;r;üÁMl³7¬T^ŠØòÒR]Q^An1?ì"ߢßÑïõ¿~ý Šó P«Ë,ÍàŒk}i d­ yfF6Ê-Ìâ¿Áv‡ÜÅF†kä>¸_NÊEÙlFi¦N«V¨ânƒÕgнA¿lÿ§/ØÄ“—Ê‘hn#±4 ~2ì|\•Ÿ“ŸœårEF†&S“Çås¨'¶QÉórP¶Xé4ÅE: ÷¬cëÒ´Â\5rÖi5º’luN ?lH2©,.)A:grZyŽ2/7‹ÃÎë%À6-MKUf&¯Ü!Åêz†9³à0Ÿ®6µ“9<Ï`z:ä0û‰åoE¹Xþö3~!A"ßM6æå#f9§”§ï.WÕè8U’®„\²K;‡™CLzýX³ —î,•W#çjTª-¬k‚§ÏŒ£Zy•å(y+¥Ö NÃ{²M‘Qa[ê#?ãµô±†ƒÇŽE …Å­`G®ø-I92 2šDï†ùUJ]*rNC*e^R2Þåô!]IFÕù:REŠãÊ®q/óYïЙØN2ɬxÓIx§DÁ¤k3uåêÒª|n7ÄIÞÐÉÆÑ<²’Ž`JSZÅ‹ãÍM6/z;ØÉÄÕ>¤5`'šIØõG_Y¡¦ ³Z¹Fž©ÌIUqøoÿZ˜­ÌË"Ηk3µÚâârµhø”++!~•Áø™@akl=Sx<çNª‚û˜k°¾Œãð÷¯dèZõÍC'÷5®nE­¨)qOxýZ½/ZÀŽcЂßȵqááñ¡„¶ÕÄ4í8™x]3uà¬à0©&î ‚ &gg)‘œF®)Ô¢"œZ%嘷øäŽEˆëÉ‚S h°e¨'t’" vxhžµ˜Žn­=P}ª¢Ý¹Ñ<º–'¯ßŒ­ÁÇi[»¼íek>¾W¯H¨åªÌJ†À\+ÏV忍¸LUlÅ®’Xäññÿ¥QÁ›¹„˜´H”È’ÓêJÔE\å•km7{ïëE>! Q£Çð8ÇHi 0ø>­7µ8iñV± $òd(<6z{NB®2 ¥²„«*Ï·¼Ì7ƒš÷Õ-¨,(U£rT‘Y’’¿¥(¦eɳËëN¥d*ÒSÕÙú®1{ï§(œÅä ºøÃ;·ÅdìBDÏ uDyw~)ÙY…9ÈY…TÙ9ÄPN¹9y„´²X¥É¯rÂ^­ÈÎÊB g”“ŸS@þ9¨¡ ‚Æü¢BÖˆ8h1Ø ž†cÛ Ã#Y~~a~>b¥ß]Ù¬ýâ f~í¾bUräF.>95ÉYBq:MaaY)WßtF±./Xµ)4‘ß%OÎ[ؤŒ®Ð’Þzõdþ´™3çY¶´æè^Yœ]¨B¬¼«h€ïEE(8jq€é2\03,ß;Mbç\ z¨ÕæÔp­6i9iyˆ]¼ñÈÙ+?äM^ÜÓk¡h·V¨%° ®È@EטnÏã£xF]qqaI×ZÛ¦k!–½ä?nè¼Y‹ƒöµó*uVqgFf¦<­"¥qvÛÙäsĶßýìÁÆëѰu›»V[…  2nh¢+²Wb}ð¿"`°ùó(ÚtÈvƒÕ|&Öà Y²\}^Ò¢ëUW¶¹÷ðä¤GúÜÒ,MVQ^1buêR~ëîõ~~;×®à½nˆ0ËâÙOp?èýúþw@½›ùt유ž¹¢ Y]›®ºrwãÑ–Z¢¨n_œ?mâ’yÞ¾+O=‰æ³KQ¾j¯1ØZŒU‚ˆ‰ B« OèÔæeç©P޳QWsð‰P%¹ÞÉÎdâI!Lªàî´‰|Ðþó›7o~¶~óT½'îàþ½{ŒÝÍC°Ct\l4;p`‹ÇÖ:DÇÆEG‘yuâXë²ÝS¬4N–0k àþˆ dÜóùu—áj ö$‘Dˆý‹HÂ|Ï[¸!“>¸Ó\ßzuÐó)÷GŽœ2s\@cè7›9éóYá›VÌ4âé´_~yúíë›ÎÍ:ÌyÁn2ÿþ•esüVxy|~㛫_Üãq³ôù“kK¼¼çLõ\põ^ǵ‹‰¹Â(*þRCm#9§W+œ>f-B§,?± )H’òZì5-x&J@‰EòbEqv~–HŠ´Ä£Éǯ_¯;yŽïøêÈ÷ÞcaÖdè‡{˜ñ ¦†=˜ôâι–GNõ…,f]FbJòö-wmFìÜ%7¾ýþâõŽ›íë&íç‹2Q^I¬±mnwÖJ¼dÿ²Yõ‡ÑTàÓÀé¯5‡Αñ›7×ï0jnŽ8dÒ"ÇÄÓI<ÏýwTZTSjÒI³`ÿ_¾}Dd6![†šöîo,Òª‹P[–©IYº6‰óΘ-ÙÉy JG©ÕîÜ£¹u;Ñf1)¶®ý¢RÅÊâ„n{aìÔ$&dȨݶïp®Ä ßb™0§çç"ç¤.*()(t"h!É%Ù%òbü;¬ròC[\TL2/*Ì+Ì%ÿrJ”Eê(;7+‡íVté&EWÔ­è4Ú¢ní‡[Äbú®Ì¨Hżø¯‰„~3‰®$ì,N˜`x×j%èቦ¢B,>;‰øW¥¦jz^ÄX(Ð\6õ‹¯&Ö€}MM }¼·ÁöxŸ>†>})ê¿ë’# endstream endobj 215 0 obj 7198 endobj 216 0 obj <>stream xœeWXW»žf•"¬ƒ,ê.E°€ØQ J¥ H³‚€,EXš‚ v¨((*Uª¢T bÄF±W¢Æh¸&þ!Ñä›ýIîMrïóð,sæ”9çýÞï{ß#¢4Ô(‘H¤ï$ R„®Y5Ñ6Rè²Zº>6Hè™Àñ#Ôø‘êq8ò¿3”‰ôHÊ9ï?ZHSijÐã'êB³ìB©‹D Yv‘Q‰Ñ¡Á! #SOwo³ñã'üýÆÒÊÊÊhuâç#û ˜Ðà#ò$Œ ŠPÌ6²#£åòÐ5FÁòĨ£UAÂ4¯Uò 0#ÇPyhTTdœ‘©™ÑäI“,'’ŸÉKBÃWÇÆy¬Šˆ1r6Ža´@±Š¬ò¯Š¢¦ÏKŒX³l±md ]T‹½«Ct°›cLˆ»"ÔÃ)v邸0Oùª„ðÕ¦fFÌ-&YNž2uÚô3­(jåK¹Pö”eNS®”5šr£©IÔÊ’2¡<('j25–ZJ- ¦PžÔBj*eFyQÓ¨q”7åLM§S¶”µ„²£Rƒ(¥EéPC(]JSC)ŽÒ§†Q”„2¤f¸PÔj Õ)²Å‹êÕ¬ÔrÕ”êê-_kœÔàiz7}ñaî  ÐÍf³¯j¬øfÐÄAžƒ"e j,ù¾U!5«[ã"àЇǧÁņŒÜ~]”'b]"Nv6ç^+9/«¸V[ÖŠN Ò”ܸýI™)¬¶Ò)íÉÞwð,W@Ic;/©ÖJcGÆà?q{¯: v ¸Aæ[œIÃ|F[ù°Þ6}(æÿ½Ñ0Š òHïëm‹çÛhÎ` }ÒeÀÿµŒ¢ºd-]°]=þ˜Ž³aâî.}xÁüŒ:K®´°âž ç«î^3D­ñuËXq÷ã‹§:: Q}RýúòŸ¼yˆÅO™7ø W V4Ìdn–'¬ŒNÚ ‹ŽÝêMº7àÛ\¸ÑàȈ{nVÆúG§E&ÇÉ¡‘)îˆ%Qû tC¶%Ž,W¸Zû9ËÇ¢i,Ö~5 €úîô‹Ò¦öªŸÑk†ÌxŒGJc±w£>ÀÞ7Ù+f•Lî 'ßt l¼âÇŸÞÊTËO.â§’L-Ô{ÆÃÄ ¼™> ½ $#f)>¨hYU:If!«ï¹¾ö–ÈŠ7b½;Vo7—6Ÿ“®åõ¸–“+°ÞÎ!_#ÖÄùˆžV¼9{CvñÎ75 çú¨OÊ„®H9€|ë‰>Ô‘ C/ëôÜûp túÖ´›ó/â:¦¸·‡ëáchÎüиb’õúÑ¡®²•s}#§"UâÂrüH²ÿž¬'!—æºZÒÜÖÒVý =A—M«W¬>àD2âF2ŽcºxŽ;‘4˜3-5aþ©Iq²˜Ð^ª¥ÿNP\€Ûü”.a„tãµ™rÒê-U­ÇŸV5eŒ6/Q•ÂUÁÐ×uéT’F9éù;Á(ƒ-ÛÑ´-Zåo{ô7¡†âˆ·ŸúR`Y˜˜ <{Â’…gÒâïoF­>æ:ÜÍ òZàf¿«¡q,V{8»çfCÁµéFÛä…®Ëm‘DüÄwmq¥,ã G3»RÒSÑTt*¾&ñì–›f!Ý/‡¡0˜o5æ®ÔøÍÃÚ6Æ“½ÃÎt‚Î# »dÿ ˆú¹¡´ý•{ÕuD tX˜Õ@DJåSÿ±3¥6–}\aU\Q‡{׎œ¼!íœÕý‚ÆÁ°‡›8ë¦mtJØÖ0$1]B˜ñ¬üµÀŒ»íÕ„¿ÞX€Õ­ÂƸÉúäÎ}Fÿ¬€þý>€ñƒ>ôáOÜÙ_¢ù·hèÌ`8˜ÁHRRù¼·'ŸÈÇö­M+Ö³vŠ Z)]éŽ|Ç©À7Ï¢ Ä=íñ¹i ð•Š%!òý‡¤É·H?Êâ‘ÌΤôm( -¯ô?]¯hÚ|±ð5P]0ôz`ýÌRÙøŠe‡“sP¡¤ªº¨Y*în=¶-}Ç®4Õ9ú$¢O£•áœj¿*Y|Ñ|¹IšÊ¢|YŒûÿ²øÌ­yª½G¤³—ô*”Ð*/"¨¸ ¢±,ð@0u„ã{‚Ñiæ2âĵ);7¥ËÄ= Ž[×®7D)™›³X˜Âd•oÊÙžƒIPVFNfÑ”£ ³·ºÔÕ­/÷Îk.+Ë+)eÅß×7”¶_!/7ÖG•‡U­9䆨^­/P¿á'Qq§Áš9‡Žn+H:´iÿ”€"Sä‰ñë#ä)>$à5ïÔ-â'óeÜæ8Œн¡™xsº“qÀ¯v&ïJF›$KäN”ìÞ_!-ü¼k áªD‘”sxoæÁ=y²›ÐD?e´áx_JÁ’Uú‹nb B"´£°®Ð$‰ÛOY½Ç`jKˆf˜“aâÊUü.«"ó*d2ïÀSÆÙÄø¸KÝ}Cæ"{4óâ ó!Þ@Üý.±2ÞßÐÉi…ñ$ïm{3²3²¥Ø„ÙŸ¾%#¯Â€šuçWtÆýˆH 쮾zûtSùô z²ôò˜B,*6?˜Xu¤Î°ãJûWMžÛÓÓÒÓ¤ªøµ“øeF<„—œ~)ªÃñŒ.£ñ)æ*ã¾À}^Ò_üÕÂ#¾Nðµ¢óñ½ ³¼½"‰Ïú² ‡fGewá¹Ð5»WsG®D”(™}ÕûÍÖòŽiÒüþ•ž óßÃAˆ*‘^¨Œf0×ÛÓ—Oæ}ÔÕ÷ÏDë¯o¿êC‹° S/ë•…ó2áñ! Ëç!}ñú]ŸÏ¼¯‡;7Ÿá-‚_Á|I°šÆSƒ×Òý‚ÞC¦ † êʱdî¨Þ´KŸ5“dÎòªæÃ™‡v‘}âÓ„Lz#¶®Û±ÅJ0ûã<ÿ¦«ìü i[cÕ•üoɃ/g€?ÿ½ U±P9½‹DowÀ(Áš”äqIyÛrÑa’û2÷·C³ÁžüÌÃè ªL¬V„•ùt@ØÍrÄú, còZv öRñ¦ð¸äØøÍ²øT„ì6»¥ømY›,õ Z·¹  ü ÊbjâJOn>3ü*ºpôzËÎ$´meÅ hsæ¶½©,1çXÂ9q£U¢_Àüá~Ñy'šjOÜ: »¸ÿÈîœÌ}{2$2wgÜÇ~‰oål†[“éxÆ2˜!<’ò[€“q¹£ Ö×°µð¨ý_-Õ<å0U»T'<9|·ñÌU$©+U„¤¤oÞ•*›‡ÿó¹ë~ã©v$©-ŠNÙ•´3Yf‡»ûKUV%I(Rª¼›,ç{F»/•&_ô«ZŒ"ÿh×…y/ þ¯ ä· X7Â:qÂçˆl¥Å^Åg¢o'×c°jœc×Wó½¢=—ËZá ÝŠ÷dKDJp‚{Ò}ÔçžàµÒ§Pô«“»‰³;Žœ— ¼+ÃÔ%<ý™Y£“£HjÁàŒ¨é¨i½÷Û?— 8XýöçÚnµÜ«i} 瓢ї‚;–iÂcÙ"s„ Öb<"xQ˜ßׯdþqò_(z&œ›Ð¯™M󃧦x€Ú‘ëm V #ULÁ.2ŠðV¨J±ä"¥ù|¯r9‡ÿøCNßfà¥\uÒæs@Cô‚€7h¥ á0,.ŃVYá¹È„Å:÷mˆgdè€Á­ÒÓïêÀõ° cûkJ£ˆ;o:¶nÙb6Œš ‹4 tEl@ø‰K?]€èͲ’ºÊ‚jÔO¦îßÉFÞóÉœqor_a¯ô¡Í™53¬Vbmon Œ#Îm#±¨|"Œ".Q¸F63¥µ•Å0øÑ·—k+«êN_\¯˜Á&½2ô˜â“µ§{µj›‰‹Í,¯Òú½û3²eÄÒïˆ'¢¸‰5}¾4{Þœ}~Uzº­äêdAÍ©‹üƒRäþÒ0¥áÂg ³AãEÑBŸW Oܱ±ºRƒoäˆÊF{Ù÷ŽW1m4Íó«…R¯©þØamzjZ¯Ÿ7Ük–ž»^ñÝc?:·òöO ò‘æÝÌÜ—[Uq·öX:Ž&f°xöä.œYfîòµí²ÚËoÛŸ<”©¨t]ðö³…óíR©Êo öÆ®fØ|6ÞÎ’kp3x<…‘0f|Ê Ë߃ ûu0ö'-R¦ƒ>‰<ŸÂës' rä±â÷uµÅξ›ÕŽâ1XËë®Ìo –úD¯O\N¼vE¬ëRC,úÖ$ß>®ln‘6_:ñÝA×5KjYsr¥¿o­X¹È}™Ã²ÐcíÍnËE–ê—º.%2Æke81JâOþaÅU»ÑžŒ½2XÈ{#ì>nÑœÙ2ñO¾ò“Í=WŸ\GùZANýŽ2@‚1ƒ>¤©IQÿ ƒ endstream endobj 217 0 obj 4372 endobj 218 0 obj <>stream xœ]W\×öžfv@@aPÐݵ `A¥¨OEª öÒ;ÒD@ V4¶Kì D°@TUA…Øž¨€‰c{þMŒš –3älÞïÝÙUóÞÿ÷cÙÙ;sïœûï|ç»2Æ@‘Éd=}¢2¢Òâ"ÂÜ“"¥¡á¢µLì§'ö×'˜ü§KwÛŸ™qà… 1Ö'ÆGû)nš‹nfÐв{1ú2YæÖ¼©É)Y©q1±iê¡óç,´6løß#£\]]ÕáYŸî¨=¢–ÇÅ$©mèEFTBrJbTRÚxõTútBB\„:&!+%v¹:,22*Rš¶ ,!j©Ú+.!.%%9C=tªzôÈ‘£è¿Ñþq‰áéËÕsÃ’–«g¨¥øÿg„a˜I3¦d%E,òsOŽ\ì?5%*`¦Gô,ÏÔ˜Ù^Ëcçx§ÅÍõIŸç›±t~BØ‚é™‰á ‡Ú©‡;8Ž9j´Ó˜±ãœ]ÆÈ03Æ•qd1³OÆÁ ff3^Ìxf$3„ñfF16Ì\ƇÍØ2ó_ƉÊÌg¦1c;f3–±g23˜qŒãÎ83ÙŌ?3•qa #FƘ0¦LO¦cƘ3 ¦7#0Œ%Ó‡éËL¦Ib hL1ó³ÌY¶LvRo€^¼Þ5½?õ‡è¯×ÿ—§A²Akˆ³¥ìmÎŒÛ&gå#ä ¼!¿Û7œbcøÐÈÄh¦Qª1ª0zÞcxÏŒãqÆ Æm&Ž&KM›Ü3ùÑTÏÔÂÔÎtªé%Ó_{zõ¼ÑKÞ+¢×S³ÞfG ÌTfâÃBׇٰìîDíÿJ7þÁ™vG“"0§øÊÄ-`+4 - ŽîèNdÑü¿Ypà¯3çp¯ ’5›0¹«»¥+4ÐôÇb9Ò\a·Ê7ËEE¬]Ÿ»#K9P^´9oërŠÔì:²÷Û‚ÅÅ×!µ[ÑGŠ®Ht€I…æ- †P[*ê[芚 ú³C®è¸^_ÓvõDüt%>¥#ÿ–Cß6”»¤ÌŒP*êïËi$iÀÂïÀÊN‚Z_<Ï…uÐÇéG´&<Žu€3ÐëÅ –Ï‚¯B+.Ë39e>á=B¯¿;¿·á@¹ªàDYA¹Dަxð¦Ý#HQ·Ýš³È Ý,ºsh…°7l`Ñ‹ƒTü7fkôYpç@;A;Yð¢pÞÑͼ ö¢£„w‚xCKÍ ì9ĬæØw'b†{k:°ØÁbÚ_‰ô®” 0ƒÙôã fæÀà 4…Á–Šw,à!÷„\,¨«¨«8v\&?D^rmøá̱rœKm ?Z±¨`"Ýñ}ìÑLgN!¶W$Í_ä¥BgÍ À›S¼»]ïçï®BoŽÂÿ »SÜ]?hÏèjƒ2ôD¯NdÀ \º@S`úÐ8Q•‹¬ÐqÎk€ÍBÏÉ“Ýêê:ëžJ»¡C‘8 aL¡ùqºÚZ)—+E; PÓ «1‹m^RâCCì°:»žrz¡JѸ°âfÜMëVr¶äTF¢‰ðýY¯ÁvÞžží¿¿i¸uKõ9#{}ÑŠøB aq‡*¼‹vp—ÅñlÔбb°—ky-Mh£0ž‘æì¶Ðân¯ÙÀaH³è(¡.æJ¸§¹¸ zËêèƒÝÿ3„-««ªçÞU2o8öF[\‹«A¶”£¾`òÆïQ¡ —ã8k#}„÷~ N0öû_ÁèBmVì1UÁŠÝ™{"y Ìr É/`H~ù*õ ú¯­täwrfCráÁâW0—{é÷bΩçc|NN³ö&Iq3y0JÖÁàååØÖÙÙÐNa¡Ôÿ_@Ï2±2„Ü<²ƒì"ÍëÏ­®Œy=æŠ6úÃÑ}qÊËà&¯Ûß—©PÉe‡F¦."dé'Vn<²å<ÿÕ aÏ£SÕ×h™–g&ç¯Ø™±-šÿ( ËÁL&ޥꡩG3ÑÓj–³ôªÌ4¼\äÅå¬i÷¡4è-î HVQhm$Ø­º³„!Ä9ÎÏgšgôBBYÅà^-Þâß’·äÑ7-m-­ß¾']¸è?¦ÝšÞîV>ð¹Ø[€ž÷mq4Ú¹Ù 1šŒï¤á;<è•®T´!uÐü^¢/ê°€JôÉ­ÒÆ+—ÛÊ~'À0Hø}v{À¯4 8ÐûöšNN³–{qŽÅ1s' «2…§T„œ^¼z+ûYª€›b©àÒÄËûº4¯`ë–<å;ù²¯bs3 ï2R5m’ã=¨EŸÇr-1a=­ÙdÌ=Ì^©ê3 _€™X=}CÁÆå§4Õ–¹(ÐgÕò±“§DÇŽ×rûﺗý(ÍÏ‚Vþ\EŠðªØ5ô[EK¶/ ¶/¨tŠi©¨ø¬–UrEû£êêGû÷nÝ|H åës7’Md:™–èÎ+*ÞS­Lúù%äs-!©`Âjèk©x(§«¸È¿] :æg½‡¡ŽÇ ÏÐú¶TºrAå.Ÿì¹ öpõZ%árà ’N'ÖÆ^Í~L©ëüò6(Ttïè¸áÖw[»E>î¾A­¯ÿ8ó}›ê¿èQû‘Ý]BãÕ²7ä5ùÕ5½}F›ë7ƒÈ â?Ã{ºWÔ`_*_§|yßEî’‹û/ŸâŸºÝ{ÈâAØ.LrëpO[¿a)鋯3L´Å3×ÎOQi[ Õ¼w$xÓ$x ðþ@ÇþMaÝ£S`©“èÁ;Ð3o ÀLk4“î¿õ„-À9µãJ¤þã¼P?f_¡ Õ¡Ì9²aüÂ>Чֿ­M·Š\1-&iW^–rå¾/÷}yœ>ÎmG£ÿóþ¥‚ªòªò#çH¹[ïQ„žçûDæ­ÛMó'Ê •ŠÎVòÍòÜþsc¤j*k¥Ñ, Ìl¥åG#•úâµúº–GÖ¦(󲶯"±<>ÖvGÒšÕ]–P¼?˜x‘ÅÉ‘³x(ÔÉ!Q æÐÖBÀpÚÌì,"dC“¹Ç–ùIÖm]­ÚšI²ÈZâ½=ýP,criù9ûIù&Xºi¯cÕ£òê+ä'ryESìéø“A~„×Xë¨=Ú€®ï+¯%7\É+Þ\¹'5ΊĮKÎ^ùŪC ¯#ºri0Ë ·y Ø[*2»Å~Re7nSøºÔôe¼¢>$fÞ²ÙÖh2æ= xTÇ+P\¨[™ôª(k_v~¸,ÚêL_¡MJ4¸—Á` ‹_ß<õè¢J‘ypû3G \ôy!{¬ko¥ÂDBûÑ/Çvü7®>ì—²’m»K”¯å«¶¬ßºŽðÑ9ûTp–z (£YØTx2§Uš¡•m©Ú¸îDº%í #ÒÀ˜ °±¬A3 ÒߊÛÒº².¦* Ùí84œj㊑½4†&«þXñ&³"™Ìêë?7zä9µ÷7*i3Ùƒ#?ŒZN³@ÿUùì«h]ªBî°º(¡ˆ4õý®ñTÇ«¦ˆ);”º”¶Pwêº\$·ÛቀO´n–b5”JfÞ}"×¶èäíh‘ÖjeŠ•bÝÇs¹¢~zp˜÷ÌÄòJxC÷h,Ç7½A~»ñèÕ*¥"sº\ÇÉ X#½L;ˆ£åó°LÓ)•˜Ôw[¤:‹ûh6uµ¥ER]à/V°8”C™F…†¢Jº†Ùš öÓC0±PvE‚; â Ú'%Ò{Ê/þÌaÁ…ÃÅðÌ€Pt ¼·ØY¨Õß2)²\©OåȂÂkž)ÅÚøsäÄõðìÆ¦ÀŽeÏÈ3ÒQÚÔÐXwø2yÄ‹9òÏ‘ëò,E®}­NØQ´Ak~ŒÆ1úKÀR‹7ì'Çxh’ƒÞä‹hƒ6“üÐ,G ÖÜ®‹ÅÛ ß|4+&+ç‹•ëU™ëñZ;w]uÖÂyÄžåG•¤P’/-;›Ù`}•|WÚxžWd’ämëò¿àa8‡!QPÔ»Ey,I*©ª9\x9OÙ°§b×ί v÷ýÛ£°€m’WÆ¡“æ ŽÏH×°•úöÏ.è£êmîÞ‡õx—¥FïÒ ÕA+‹žVâmnëR%ÿ;U%x@‘䳯 ³8KWàEèƒ%,Là~ Ïq‚teú§Ég'•¢4Y÷±T4^ý/7ÿðæéª«g§„*Q£ùŸß¢¹üÝôkÈx.Ÿ¦\v~þqâG"2‚æñŠÆVùÿ7™W>›Ì+…‘6ŸÆ'Ϧ¶Yƒðz‚óíô;1TÑM¾'}‰ L›ÏCžŒðœÐÞèikàé層­ëµÖMýé¡C«;É‚%H$ŠÖQKZ§¥+«©EV¬•Æ!\:YsÐC¼ ¦š›,•˜îŲ‰8rœˆJ‚P ô7ÿ†áq`@À–€ÍIàÞC(ÿi–).@S@IÀ€c (‘þæm¹÷~’Ú´%h‡Ü”f•Ñ¿…²ë`# ÍÓÍrâ;k ?W^½ãؾ£EUe_Wû<ô§¬~ `-ƒóðJÀ´†m‚ â¥Ùöh£+ÛUEÝSi$‘ÝÁ†ÿ•À¶Ò=v'°Zö‹ûè Aòô‘Rq”vÇ “È´¥¡³ÂçeŽ$ØŸ ÙÑß̯ñizL:HKYÝÕªæÂ_ô!`ñų¸ïÂÚ¼*\¨²§Éñ¢¿NÜ=‘ÖÀ„õã×%–˜A¢Iò¡ìÓ«N¬¿K^’Ç»î=^P{âHmZ6ƾø@ƒË‘Þž#®4«è™b±£<ÝÙ=M©/Ó’–ZÂ,à¨÷´‚ é$¸G:®6q+¾½=>ÝL~¥ 8¾ÐÊ/$Øß7¦òö&%öãrqà‹Ñ@- ûí'P>Žiÿ­JÑy³úhý +0rº‹ýݼ“F(“cW’ü°çVn;´ë`~Yù¡jÂ_,›åã¢Z£Pè¼H üSwÒ-¦ˆ>èJOé/ GÑ$º#ÇxàôãÀ.~Ì!ýw’‰7‘æ®ê^,hzr34*̹ý§ÊÕþûÚEc]ByúÇ•]Ú,¾Â^/)'©½}KÍÕQoÐrIìš„pU1 eáÌ'pòAïaQ %I9(!úÒN$Ö Îäg•FŸŸvâĉL õóÊž@P hºÒé9Õ¾w"ŸÐÅüú¡ŸóK4™¢Úv—'ò¯WU^$õäHÖÁ`Þç 7jMœ4ÏozHÓÝûuÍ7T:r\•vU"mhmDï¹Ð q($û NuÌÄa’Oȇ´ÛT&@ÿ1àU¯ÀŸfó0M&O…"Lè7Mj—è ZGò÷íÚ·—WüvüèÚ3V ?ì!ZbŸanhà},ôzœRÑå7ßÙ ­ºÁ†v½«'qÍ®uJ´‚tAñÛµÚ¨yóC"ýýB¾ý®¹¶²EµO Š®öÚ )Þ!ÁÞÞ¡õ·~¨;Óª2M++¤†XÌ‘ èF{ŒaÀ^c†ùLåx endstream endobj 219 0 obj 4313 endobj 220 0 obj <>stream xœcd`ab`ddäuÎ/-ÊL-ÒuÊÏI (ÿfü!ÃôC–Y–Á®no7s7ËÂï BßK¿òÏ``fd,ïœæœ_PY”™žQ¢ ®©­­ƒ1´´´THª„É(¸¤g¦ç)¨e©9ù¹©y%Ö Î@Õ99™É é9•Å ‰)©) ]a‰9©Ù n™9™ùe Κ F†º@ÂÈ/37©´XÁ7?/_ÁGäjF f`bddaÿñ†ˆÊçÿðœÿ]hþüùl¸>p¿˜ÌÃó‡—ýQÔ endstream endobj 221 0 obj 233 endobj 222 0 obj <>stream xœuX \Liÿ?cšsŽt¡qŠb&åº\B¹uÓv¡•J*]TRÑe˺­]=¬ËŠ«„Ä.*·t!±îT„&&4Å »ûþÎì3Þ÷ÿœÉ»þû¾Ÿ÷33gžyÎù=—ïïûûþ~ψ(½^”H$Ò÷ŠMZ›‘%ü²å-Dü ^ü`ñ œòçxM¶d0å³÷¹!2#½’A¬LøÕý Ù²úRb‘(kóîYËR³Óââ3,m‚æ=ÚösÏXgggËÅÙÿ¾c雞—b9œ4VÆ&-KMŽMɘb9‹<””m—”Ÿn#˜G%Å.µôHHJHM]¶ÒÒfÖHKÇ1cÆÚ‘‹£_Bòâé–Q)é–>–±q+’¢ÒþÖIQÔ|ŸÙ)Ñ |gæ,‹ ñ›•êï¶|É÷´¸¹éñž ^+çÍ^¹4È;3)*ø‹¬äÅó·ÛŒ5ÚÒvˆ•½Ã˜ac¿w·Êiüް Ã'9GNž2u„ EÙQVT(åO¹QΔ=eM…Qs(wj2å@ ¥æRÔj 5Œ  <©±Ôp*ò¢©Ô‹ûõ¹ÛGm°Æà ÂÐØ0Ñð¢áŸFƒæ!£*£v£gÆc[cWãTã5Æ{ŒßöMî»­ï‡~ÎýôS˜Ø™D™ÔJÅÒ!ÒíÒóÒÇýÃúßèÿ¸?Ï⼸%ÜZ8jÄ×#¥†)=R‹a†&™³ÿ˜Ì‡(±–6Ò8 bUðcŠD| ,ãàe’÷4Þ¡I–`~L–üJ·Â2 |¯àp. ðTbÄ‹S”šþÄf_ÌáaZ[lÎÛJip×–,´jIV^<øÊÛœ½…Îì]»÷›ý¨•íØ¿çоÝû÷œ†<ÑaYÅüÜ–ø"“æV×'ð]»™ôT3Pú³¶ø œf¤-ÿ¸ÔüìöéhþW;oÉtx_î’<3\&=¥bÈ22ZyÛVÑÑ1¿Þrßœ_ýsfY\­OÙLÄâ!XÏÄ.]–`ýÚ@ÿùD:oâ¢(7Ä:Ì{}Áì¢âyóÙÅ3 ä: 4ndG¼>ù,¦ñ\‰ý¡R‚%4lÀ€wjõ%@Ú á2„áË`„÷{,¨øí*1o Q³:öXìñ=îd‹µ´ßá ¼˜ ¨lýÁ¬C™?$£h“—´<3-+eý|ò~ÆÁÈdªÐ‘ÕûÓÉÈû3v-3Gñ_-]‘±"=yub –¨…7jñþÄZ‹™PEãÛZÉtÞµ+¸É¿×÷~¢ê;Äp?ÁáŽVØ »©¬`$ŒèúÓa¶Ó{T›ÞˆX°èpZÀm<ôÂr·€¥¾1$&Ú\é-Èñá^\Œûá>~“ÇŒ›ûŒÁø—G*ò­àÕN­¢SÏÄ|¬âУ¯kr+žM¹0’,k¸ ™éxúó!0 Ú.&!“á±0Î £ˆËÎfY$ÿ»¹‘ÛÞ}åÆÄ*oxŽßˆ6æo$ÃRòו"^A@Ôîwæíi­·öºÄ™¿ Ôz3¼•_ˆj~7qÐ@M6‡c±¶ÇY8 È7,Qµ•Ö]‘·Ý© ‚>,|…å`‡eÎzàø;6À>ØÍŠ|Ùck"G^àõž|ÙËWêæïTóƒÔâNS8FƒH@™°ë ûÊñ1ZýÑ‚ã¯Ãt†>…ý°Ï´‘x¨üÿáþ‰“­ü:»´J“3Ѷ6ÄÌ9âaec±„¨Šë3+µÔº\)ÇG<\3…ë$Œ4ÁÑÑðšò…ÜŽól‹‘ª] ;It9)éãE?Vlÿåï“51Y[VoÊA¬kxät¹£»g³va;¿°ƒé‘æSJØ¡=WCg·˜læ:ÙôeTµ·òøÙ“ûªÑOnÅCeøâÇd5Í[èÁf:ëæL s”÷èÌwJÈSŠºˆøO9~ˆR;òø1Jíf’Š”Ú™´<#Ëœ©"»o"¬3“–7 :\&èðA=ØW{²Œ•6ÚwpÛ/›Ø§LÞæõ›V£ ´xi˜ +-E´¸§4C?AÈ epŠŒò˜C†™ÆH»¯G-(ó²ÀÇb»Ž/q?"¯Œ¼´¼ÝFçWßfSä¶vÑÊ”I‹² X”PñcÖîu?l8ÊN wØ´Îc¤@w;}²f÷mÆ,Ü› Œ³|¹®úiØ ÷ š6nÌ<Î\RtÊÿƒišåùœ]kÓÃc/0 ¬À/!¯¿QïÞÕ=ǯÈ>8IpìåÂïû.ÊL\‰b‡¡Ÿ¹æï =íÒfùéx_ 7ÿ†1¼Uâ·cK¥6OpÌ_©ªºÖ’låÍqùï<Á†$ꌦúŒr>}“劬_òJV¢Ø¡¡‰3#b ÷¯”­Úýõî åì8z 6lœ ƒIøxrë­"òŒõAù”}ž{Rö£ò5gŽ6Þ9‘2o³ìSV½ÓŠDÍí$©Š›I8~Ln' UH§¥¥ks‹dr Òж'­ª|†ÍˆÈø"TGzÎ ÙÉD­Ü5¹ÛLúü€$šõt{yôX¹”Ï÷FßG&I `ý¡Ýù`ubê1’^”'>ßn&S›°µ wêˆi©GRÐ ’¥Ž¬+É<¸²0E²ã#cld:7ùC¥J•&gÕü*%IšžãðegHˤ‰Æ›Ž¶Ãgx•V/’…]Hé@],xþú`âGvE²Wôr°ÁzOñäˆfe,œè†%›±8ã1òk[yÛ]’>JFI®¶ÍháG5Šž’ˆÜN:†NÂCÝ<ïj-éàÉ·ŠŠ6o=,kfV»*ÿKÄÆ­)(—~ÆAÁ6³|‹Dwˆí(b;I[ÜNk’õ&Z=Ê)¹· “Õl5¼QyªQó»¸|°Ùާo;Ù8ãŽÕ¯Óä0;L"í|‘~zI€9ŠY´"%3|õ4í‹?úÓWÇ7UÇoò*Œ,‹®ó|bÔîUŸ¨­¥Åd]¤&™ ®ã?`Ù¬©sbäPÆÀp\Ì©{ª6ß¿Wmºõ€£É5_‚Ÿ5͵x2ßIðÃHs0iZÓàO ü†‰ük˜ª}Mx²\R$º©ß$k°®a6ÍÍKßÀÆ2W·þ|=bß’ð}&<ÒòX «É3#°™öFx,ÂŽ7°÷oD?èq` S8!W .*°b?™Á¬Çb5Žç~³à`,Gðfìº0-01Ì'Nî0ÛL\äµÈ' ~¬©ŠPß"3i= á—sïi}y÷ác[Ù ÆCâB4•ÎHÇÀÓ÷aÌß!äOÚió Z2•áex’V†Ç M¡GíPòr[MªUPØåÑENð°ÜNº|“Dú¡».r‚ã¼`‡Ñ kÞ}-'õ[@Á’½KOx6Çw“nøË0›PFʩۇ~®»kF“[°¶žæ‚G+SÑ›Îl9°sQåÙ ˆUT‡OÉNŽùRž¾&éß|M¡¸ñøV/æÃxnSpîŠ%(}¹5sîÝ4 § k[6òF± 6رõôæ’Í[ öï-ü ’u_¿ñpèË#k"ö橺VùoŠadïC\øˆ¸ð¸rØU®Ú¬øEÞ ~  Ø3p¡Ãi4¤ÁO@t(À¡CÄO!.M%4Öö¢ƒµ¦’Çô©Úc{Éê_]õ¶6Çw´ëÂÊG+ »æ,+^~2è^Ò#‚…õoïaXڿƃB¢W%/–†P Ô~$ÑgR«Ž8ÛH>fÒ§µdlkFú´†þo1“Þ1ý*wÇßÓTÜÝ$%ÁwBúýsøÝª©xG÷¬ÿ𢽸™Bt¶Î-l ù ØiM˜B.È=6Ê'Ò#“(‡pŸ§ö©¸ž¤ÎáÝ¿ŒUc©û¼Ôù±òM0ãÒë·è::óƒ'KÄ|<÷´~Ž­£¿÷dgßÆçª«×:z‚„CÐÚ+ •âÄKiì‚¿öÀ6Á8}IWBf'Ì'H`?/LÚÅ%DlÁïÏØoÖ»C?-ý§û®0)ïòî‚|r1“¾ã-øÞœ´»åìwÌAì¤ i±ß8,ž} î^„LúnrLôslúf,X‚å›.àÚ¢¯8WÊ0 ;‰áƒšHÙ¡‘3f„ž½ÓX{ö¾×éIß©®Nšè8v¼ÿåöö«—zþOP@€®·Š*ºàêa·³=€"§µ»§nT5u àÈ9>ûaԘ˾G…¿?,FÀr< Ëúß©=ÐpNžgŽŽ|ѢʼFúó‰œòº§ƒ“Ÿïø ÁW^u]½õX˜»Õ‰ø؄Ԙ!|·®L|MïEЋèmbÞòÅzŸdW—øIÂíbXJ|*ܰÂMÿ#Ið« ”ãóp´¤ÑÊ`/ƒñBSè1‚@!Ô¼×|Î'ÿR}Œâæß›E$SýÁi³Üù,z˜^Ï·QF _,T¤>%´B¿½b«ÁÓ†õ§ä%ü endstream endobj 223 0 obj 5300 endobj 204 0 obj <> endobj 23 0 obj <> endobj 35 0 obj <> endobj 9 0 obj <> endobj 57 0 obj <> endobj 11 0 obj <> endobj 203 0 obj <> endobj 22 0 obj <> endobj 34 0 obj <> endobj 8 0 obj <> endobj 56 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 224 0000000000 65535 f 0000136844 00000 n 0000167490 00000 n 0000136487 00000 n 0000130083 00000 n 0000000015 00000 n 0000003223 00000 n 0000136892 00000 n 0000166246 00000 n 0000163789 00000 n 0000166897 00000 n 0000164414 00000 n 0000136933 00000 n 0000136963 00000 n 0000130243 00000 n 0000003243 00000 n 0000007975 00000 n 0000137004 00000 n 0000137034 00000 n 0000130405 00000 n 0000007996 00000 n 0000009424 00000 n 0000165218 00000 n 0000162763 00000 n 0000137066 00000 n 0000137096 00000 n 0000130567 00000 n 0000009445 00000 n 0000011696 00000 n 0000137148 00000 n 0000137178 00000 n 0000130729 00000 n 0000011717 00000 n 0000014727 00000 n 0000165846 00000 n 0000163334 00000 n 0000137230 00000 n 0000137260 00000 n 0000130891 00000 n 0000014748 00000 n 0000017328 00000 n 0000137323 00000 n 0000137353 00000 n 0000131053 00000 n 0000017349 00000 n 0000020226 00000 n 0000137396 00000 n 0000137426 00000 n 0000131215 00000 n 0000020247 00000 n 0000022944 00000 n 0000137469 00000 n 0000137499 00000 n 0000131377 00000 n 0000022965 00000 n 0000026519 00000 n 0000166656 00000 n 0000164253 00000 n 0000137551 00000 n 0000137581 00000 n 0000131539 00000 n 0000026540 00000 n 0000031474 00000 n 0000137655 00000 n 0000137685 00000 n 0000131701 00000 n 0000031495 00000 n 0000033832 00000 n 0000137748 00000 n 0000137778 00000 n 0000131863 00000 n 0000033853 00000 n 0000036261 00000 n 0000137830 00000 n 0000137860 00000 n 0000132025 00000 n 0000036282 00000 n 0000038427 00000 n 0000137912 00000 n 0000137942 00000 n 0000132187 00000 n 0000038448 00000 n 0000041761 00000 n 0000137994 00000 n 0000138024 00000 n 0000132349 00000 n 0000041782 00000 n 0000044360 00000 n 0000138087 00000 n 0000138117 00000 n 0000132511 00000 n 0000044381 00000 n 0000046959 00000 n 0000138169 00000 n 0000138199 00000 n 0000132673 00000 n 0000046980 00000 n 0000049963 00000 n 0000138262 00000 n 0000138292 00000 n 0000132835 00000 n 0000049984 00000 n 0000052926 00000 n 0000138344 00000 n 0000138375 00000 n 0000133001 00000 n 0000052948 00000 n 0000056889 00000 n 0000138428 00000 n 0000138459 00000 n 0000133167 00000 n 0000056911 00000 n 0000059715 00000 n 0000138523 00000 n 0000138554 00000 n 0000133333 00000 n 0000059737 00000 n 0000063445 00000 n 0000138607 00000 n 0000138638 00000 n 0000133499 00000 n 0000063467 00000 n 0000065952 00000 n 0000138693 00000 n 0000138724 00000 n 0000133665 00000 n 0000065974 00000 n 0000068516 00000 n 0000138788 00000 n 0000138819 00000 n 0000133831 00000 n 0000068538 00000 n 0000071658 00000 n 0000138872 00000 n 0000138903 00000 n 0000133997 00000 n 0000071680 00000 n 0000075786 00000 n 0000138958 00000 n 0000138989 00000 n 0000134163 00000 n 0000075808 00000 n 0000078641 00000 n 0000139053 00000 n 0000139084 00000 n 0000134329 00000 n 0000078663 00000 n 0000081593 00000 n 0000139137 00000 n 0000139168 00000 n 0000134495 00000 n 0000081615 00000 n 0000083916 00000 n 0000139221 00000 n 0000139252 00000 n 0000134661 00000 n 0000083938 00000 n 0000088296 00000 n 0000139296 00000 n 0000139327 00000 n 0000134827 00000 n 0000088318 00000 n 0000092079 00000 n 0000139391 00000 n 0000139422 00000 n 0000134993 00000 n 0000092101 00000 n 0000095432 00000 n 0000139475 00000 n 0000139506 00000 n 0000135159 00000 n 0000095454 00000 n 0000099212 00000 n 0000139559 00000 n 0000139590 00000 n 0000135325 00000 n 0000099234 00000 n 0000104617 00000 n 0000139634 00000 n 0000139665 00000 n 0000135491 00000 n 0000104639 00000 n 0000109239 00000 n 0000139718 00000 n 0000139749 00000 n 0000135657 00000 n 0000109261 00000 n 0000114333 00000 n 0000139813 00000 n 0000139844 00000 n 0000135823 00000 n 0000114355 00000 n 0000119349 00000 n 0000139908 00000 n 0000139939 00000 n 0000135989 00000 n 0000119371 00000 n 0000123124 00000 n 0000139983 00000 n 0000140014 00000 n 0000136155 00000 n 0000123146 00000 n 0000127490 00000 n 0000164977 00000 n 0000162601 00000 n 0000140058 00000 n 0000140089 00000 n 0000136321 00000 n 0000127512 00000 n 0000130061 00000 n 0000140166 00000 n 0000140197 00000 n 0000140241 00000 n 0000140615 00000 n 0000140636 00000 n 0000147922 00000 n 0000147944 00000 n 0000152404 00000 n 0000152426 00000 n 0000156827 00000 n 0000156849 00000 n 0000157170 00000 n 0000157191 00000 n 0000162579 00000 n trailer << /Size 224 /Root 1 0 R /Info 2 0 R /ID [(ö້›åêâÑI[ k\)³ô)(ö້›åêâÑI[ k\)³ô)] >> startxref 167692 %%EOF simh-3.8.1/DOCS/gri_doc.pdf0000644000175000017500000012221611143604360013474 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[Ûr·}çWlå%;)ïh€¹"oº0²cGRHʉKÎEJ”I”%Ò‰óõ0ôÁÌÁÎRNU,— apitŸ¾½?oªRéMåþ„ÆÅû£{'ýæêó‘ïÞœ<žŸ®Ž~>ÊÚýã;°}ñ~óàÌN4]•U³9{}T•Æ ªüwµQ­*›fÓWö›³÷G/¶‹*úv{RìêÒè¦k¶ßº¬ôÐ™í®ØéÒt­î·¦hʪíõ¶òs­Û›÷¦9vI;n¨º:¬Xµ\±iœ ÍMXè´PeÕ­·oC×{×et×mo‹=\Ýšzؾ ŸÏe•Ùæºhí:uÓl?ùIvN_Ë6Ï…ÊÏ2–ºŠó_Mvò?Îþ|dº²ÑºÝìtíù}i ÌQaô®°ßëfh¶d¯W2ðbä¬= 0GË÷Šp~ˆsFZœØ¥^õz’z×Ú¿›vÓuÝÛÉÝÎ9ûéH©²µýÎýõ÷Ggx±}è¶×Cß«íÓbו–ªÝ>säwÆžÿ‡bgʪҕv² C¿)ª²n»®ªœÂ¬¯eÀ™%»+{Õ9ÆÅ±OÆCÓÆÍzƒca݇2öx¢FŃ+OnÔ5ñHñ€îl°ð»p¯tÓ[!„öÚŽT¶»1:áû;Ëï^Õ¦Öµh1¸ýWaÊ~¨­½-v½íê[½ý'_ùV­«dŸ ;4®1ÔÛÒüµP}©,t,bmËÊr°+‡}a¹7Ò¼²a“2àšŽ‚=A¯(­çÒû›‘bá ,k}vkÂ7Ò¼žïXwm‡$Â!êâ€S‡‰¾2 ÿb›U×÷ƒ…Š£j¡Í„­­.´Õ¯µŽ:ìÚ 5·r<+k*¥Í‚K_É–p¾—2`œÃÁY 2žänÕ'Y5f`/eY à’JþBNz+½ï­~Y{Ò· OöPˆ»A¯òˆCÿ8JÖþ;I`Dà†xã9Z¥ºsêÝún§½N<`Œ@s…p›KçµÀt"^®áí#b$6_rm-¯odÌ+ ÛϾ+ßÛtÎ{ÆïЬ}S)íýJ€˜–6#]CÚqßÑØ5¶µ†(.àƒÃ V}¹°~VÚð}r8¬¨¼ ×V¿«:¨ðKÊY ð¦`zŠV‚Z0˜;8ƒœüŸö»õ½@|"± >V¼‹]ÙÆMmük¡M©UÝæl¤!±ýìl?ngVëÇB:`œ¢ð1²Óˆ$ÝUÊ!)¬ðÅHŠÍ“É,›Äì‚é<@’Qd©$ãˆS³¤Bdæ)/ÄI$QLhÝÏuÞÿç-D \f¦4N‡/ܯäDK}ÒÈ­+:ÿœ24úU´9—kÂ…0*¡ÐM«t— 4€;°ÀÌXO ØqNsEùõ]7pß;Æm©LŒ!ÐjÅ¥>æ¬RãbôºÉD\Gn(‰SfRKµìã¡)øxÎz e²]Úôœ«呞h$°È&mý0E#óÑ´N•MÅ´¾¦îh Øm"Ñ“‹‚™àÞÖ+0 Ä@ÕÏQ à5YP’yq0~íÛBiç§{4M¢~¹àŽõFà¬B¸-|¦Ïò(Tê±`ÿÕàw…KmûNYsbÝDÕäLààrSÍs‚Ù Ý)·íE–3—l8{¹¸!òàBH8ÇÖŸ»ÇCùクô%êè$…á¿ ¹èG°Oèåa+'ÄkÜ %{_ŒƒHÐÍó.çEÖ/*.©À-Ufš±\-Qn}…-ä3ž×QÍoÑ3Ò£rоB6ˆaýª‘ ™É ^I«Öu%]…*Í6rñpäÊÂИ¶Ý{ûà{o f©V³þÄCî¹Øñ >ó…Àz l—ɱY¿Û]r¡*,:¨Lؘ‹“`±¨Ó“ÐÐï~ŒÎö6¶^FEÞ“ ¯$šTKöjuÔBˆn),VEùyô¡‡f÷Ó­?6?ta´¸ÚqÚ îá…ì'šÐqÈFM§>%Ü-S¤ÄÆzMvY\Sñ»ÜW±øsLÊë¡Ë:,rG±ê¯~ûMÛjê9'WÀ5‹›d°»Ÿ\Í(x<=‰õzq®23&»[oì qgNâ<®K%îsî%~`DOÜý<׆]né‚#G]*œ·k“„:kšyü¾ ~‹ËupïcË›?‘H Ãä·uWvuO´}õ4‹h¥Sm“‘ýâf2b‹¬<­¨ýá¹.÷Ù\é©r®dGÀtv >òRÔn¼¿Û‹Õ9&óKþeçb–—.Óyñ±¹g(fNx†ÊyÒÅ9Ê­¦ß#þ%iýRá7jX š™&ƒ¸3*ÞPÖ:èÝuòp˜¹1Z<ƒ$ZÄß®öYÍñE‹Ù‰¸èmÁ´èÀ—ÙúüÆŸ¤2Žì$¹ qèÕD.è’ )/œÌ}ÆÀ&“V}È €Ü½…äQ'|=åMóè µÆÄt+P’‡–C/!\Àx~!s_޳ê*T Œ7+uP²p@ÁŸÜ]n×8£tVôÖpvý°ý›ílË¡«šíýBµ.ã‹/XXÄ,ÿ Û X%'´tâ{©Î—ª¾w%'Ë]§Nc’7¹Ùºïn…D4 ‘ ºÎIÓŦlŒi#Ú¾4µìL¥-{EXî.–à$Ï£OJGÂ|»hí3ßÀ\Ýàó 4ïÇ#<‘NXó‡8* 0}ÙöñÞ‹8–¬* pÑ8ò[V¢“ }<ïž(Cû"òü»ˆ>AIx]“‘§2ò”Ž (ã_ÜŒn-̞ɺ߉ÂYðºx* [€Ø vx.½ê”Ça…Çr6̃(™ç#Ë’¥ZùXÎ|¤„nXâ8&¹‰ úÏC•kÄ´%$F Ç Ô ¤b­Âç/S¨¹ÀýPjHrº%€÷d#ÞoÇÌ>d}-½÷ãP(9šåûƒ¹Ý#I»€õøýÐwA᫞á|\æOf6zâZVS}X «.•v ±S@–SY.ð½Exnh pÇ{ NÀ³ˆêç ®‚ù{É€³xsÌH¿ï‘m“ý²—WnMQa8Bê ­xy,-‰ëÞŸ¬«s,"M±»9BK +:„¥ ãÅ–¦­ß§\çz¾¸•sC¡ÊN`ø€&/ìÒŒT¤€âd ?ˆ]Æ6pïÛ X®òX.8ˆ¯ð¤åÌ=Ñ;„°ŽÃ—*­‡&Õ(³'žÖ37šAàŸxÒ˜IÉü…¿aÊ¡¥ð–…]µ¹µ™ª¯“ˆ„‡!aä uƒÄÿŽ c™a[õÏÞ„‰”/9Ÿ„}‰€ 58YJeX©Öw9t–R]ª®K¹×I<\sÀ}¾íºw ÊTÆq[ ´œ ê~Ò¡T<Š:‰wÈÜÒ¬ÇO‹Ö®ßú\0žz.Î)j [0æàdiB‹GÔƒ'iÏ> stream xœí›M{Ó8Çïù>Ú ÞuÝî.°J'ØC·Ý-»KJ¡-Ï÷gäÄšqê@ZHí¤i.Y©òø§¿FÎÇB P…LŸÖ8OžL}q~5iŠ‹éÓ¥ñé|òq„NM·OçÅ/3lPD]1ûg"EŒAú…W(œÅïѤðXa>)‹jöߢ ö[Ä•.zaLj𦄪¶Bzˆ×Π¯òUU¡‚2¾ü·a}ÀëóªÂIïmyC­Þçë'•Ñ`ay]ÕJXmµ+?PÍO•ÚE©SOíõß«'Êyk늜þMí¯p|„rºäàq™Î†m›6Øn音?&JI!SàÍÎ0hn0ûlÞšè½3Sä€áB4t ˜±˜JxcK½àš4LóTëÂËPÔ EXL–ê…óÓªÖÂD¥c9­¤p "Øò9UÝ€6UFr ÉŒ·Dâ(‚œ5ÆÜ2¯+\5‚öÓŸ˜¾j¼ë¸~ ÎË îñ£¡³Æ€î€ç©Ç‚ç¡9»!rµ kÛÞüèQ%7ÒÖ4Ò´Æ*¡<"ø/ô2W¾Æ & çrÈŒbûgûH6ÔÀµÏì;u÷~#¤îÌôÐ,Þ ËA iÕ÷±¬ú°|L¹ R§, qžÓt™É\–å˜W êl&nÏzÅóó,ŽŸPá‹…Pïä;~m e•çN?£ö–NyÅr §)3­3«J¬€âZaôx*WplNöÏÇñ»¹¾lÈ¡õ8Þ&ôì-è¨Eã&Ðc¥ u`U Zi\m7©RCÚö~™'Hü“™‰,M`zI÷ŒF¬"¦¾ëmÄFr™«2þuøš®§äEÑüwÊÚ6Í¡¥á$éð¤ÙˆqF1òð×{²æŒ&GÔ½æÇòó|Ÿõ?³™‹¥2JC˜V8‚æ:£ÈQ7‰£T JCÙødÎø6ñô¨122LGd1óT³øTËŽÞÒß{=y)cøÁÝ_ÔêÃm‚™5‡lý§tT÷ŠÕ½ìsËhIêŠye×/6?P¼ UÅz½éŠ:læíæïE!*ÖŒÃ;óP²ë€¸=™ß»¾7tέ‡¢~•¢›übĦŸQúÅÈo³ÉKü|¼ÄÌ$endstream endobj 16 0 obj 1138 endobj 20 0 obj <> stream xœåZKs$5¾÷¯¨UT^U’‚ˆÝa1f!0ž&8 ÄFÓm{z±Ý=nv‚à¿“ª‡2«*û5›˜Ã7j)•Jå[õ6…T‰˜ßL^œÛäj3©‡“ó“Ü]MÞN\¡ÃõÅó›ä_SX(eâ _%Óˉ(¼wÂ6TeR•ðo_&VŠÂ„›IšdÓÿM¤,J•L¿žLÿñ:f¹¬ ++•¾É`žTƦËÌÒÞ¤›,·…²ª`­T…QN¦7Y®L!¬¶éE´N{:¸Š„î²¼ Ë•Ogõ V¦·Z Œê²ª„¦sWçÀ!¬­Ù0–áÍtšÊ¥÷H.’'?½AòÉÜ“,¯ŠÊ”Ö§ç @ãÞ§§õã”Ms<¸G ¡géOÒî}{Š©”TF×ñw"Yr""r E3A Ûþ4ý È'²ì)èOeE¡‚ú¼N%ðW˜²¬<,ÏK%ŠRºôU–kÕÆ×9l«@êð- ጄ¿šq'¥wo¦hWë¹+`Ð…›µ4x\á\à®PÃa¿¸ì‹ ~«¤Šû…QJøÝt{à‘÷ÛLU› ¦Bl „èÛ‹Æ@¬ ¼Jã+N€ZOcKº®ñnС֩*ÞMØ”¬’b™øïÀ2V¬Ùm?]GR‡±>Ät.ä,.††Voq90Ò=Œ 3;ž“ÎC\ÂÉà#<™¹z–k"ÖI„ÿkƒV”åSn=TƒyÜô—}räÎÄQ-³€\iú¾{‡÷z†Ht(„RéÿÃÊ÷?Ð"vØÿ¼õ/¥ù0ºýŽ`Äã³™ÝÚù=tÿCïühÝÿð §ûœ¢Öºÿü¨¯µûcf¤zÅe¸ˆÌ”Ôör ¹k¤®X±!^Ø j§K§©°‰2î ;Û%~C_ððXÅ?bïú„D[¤!žŒ·: V&}—I[H£F7²[Èjµ/ÂÕ*6«=ÉÀœôªNñ éJ~›ÞBêY§øÞk]B2 „dôEÈAA}JËÕ†£«\‚ïñØûSe’ñÎÞ#±&W¾¹€.­n—Ûˆ½^¦ŽH”µõp»ýD™­)/"bê(Qi¨B9ke[GÕEÊ¨Ž DD|Õ/_é/m$ŒJ}ËZ?®¿bs”±ÕŽ˜M‚´f“#ÌÙ|M"b$ý[æC½-Z‹ødXÏEïÛÚÒñuUˆ ®,— Étš¿•[iu÷ú5¢eDsf^ÑmD³ˆn˜?¦np0k¡ªj¹9¨¦•'– ͇ˆ®‡{KJù/n:¨PVÊѸ¸—‘êYDß÷ùT¡«¢j)%;gד¸ê<¢S\ŸÇA‘@F=ÂOž#<ÅCå,„/ž!üžûÂ%+ã7첄kv¡°b'h„ áþÀ.#Ä.Ù 7¬zܰî¾Ûª?Ae¾Œ7ùjtù‡¨ ®2)Ne§2†=(¹rsWûnnƒpÍŠŠÀKáŽ;c—]0ˈ„Í.Ÿý]L–;»E8gx`õvO‹èôÙÄ*ÙÓMYI\³£Ä‹¼Û'V²Å’•/Ö{F€f—ü¾}6ù©¿J~«Ý’:F”¨‹çŒ,_!Ë»¸ “LI®9©މJð‚ΊuÅNøexæ^^cZ¦BRvèÍ3æÓÛ›£÷ˆðûø="ìÌ¿G$\ÖM6Xsšo›ÜŽ8xìƒÏ®\=Q/W¯¯ ò޼æÜâïWìVly‚ÞÖ:¤tY@åµXÈ“EüÚFŒÒ~Ë#Ñš]6Çe|Ù´ ,Â\]ú~ƒ‹‘Ë ÿØÒ•2ywƼµ†Î8Ðbºñ×T±kò&¸‰f:L5†îy ¬ŸP/Xù%ô^"_kV5ù‡Õ{ª¥‘B˜Û×UY‘;!Ò'›óÄx>øSâOý1nLx˜íS¶mürƵl,Æ„ë‘ÅðÑ?š´¡ åèf¯‚FÕOŒÄ+‹ÏÁgõïÂKÚÐb?Gï0ò‚Û/gÃʳ9†ÿ£í´§}oaü2q]Ø $êwÝ8[*²¨×aZ4„èÖ{è¸&DžõPCeâä-íípÞsØ"ɤlû’ã["Oö3V}Iߨ½0êÔL/pîàD@Y×$¤í£0âl§÷_²T*СϳJˆQŽÎbªlÆñ!ô÷WôŒîxŽ€ ã3 Ê;ºh<Ù½¾´£¢ÚòdÄ¡ëovpÂö¸#ªvÕ¬ª¤â>mÍØ&ø\eÅž’èášÕfâˆÑÇ€SW.­Ò]§]”[¶í¥HœI_·y‰)´Vªá ¡jš÷\«tÁB>Ø“²‰žG´®Üϳ{–ÒôEñ¹µ­*öYL‡°!úiD—-Gu­Rm¡~Æìö;³/¶OïG”¡þ¹c~æ+bÒË åßÉ!omXøÂ?v–JàT¤¢ýÚðUÛßÁ3ö¬Œy&'j‡?±×,™ÝY',#¹Ä=»ï…ÈÆ?ã(ñäTƒÒmœ¬ñI0_ÈðÉñøÝóÆÝ+ù詨_òǵîQA"è:}᳋c Ó÷öûãôòsÎÃÏØ ã ‹Œ‹sž²—G&ÜR"…çwG@1ÊìçwT@²?emá½mƒ x#JWÐ׫8‚GX3>Š8 ÌŽÙWI\³ˆècV“†Þ¢ù\”s$ "rå;ÃĘÏvt^flÎßórƒ”,|»[?Þò¶¹äžJùtPáò9.»³¾þQ¬›'mDáEŒ6ŸcÔ#Ïnß < 'Èòø 3h×tQG[ ‰:ûªÒ‘)îHe …6à¬Yb=ÍâZ‹ÑÒ¯œU™È 6òAˆ|Êà]!«øEˆ(*YYHÿŠ,7EéÁM¤2 ªR!×ÚÓàÐ]èga×Þ–p¡õ ´\³åC §BòÙ}Fñïéä;øû%d׳endstream endobj 21 0 obj 2245 endobj 29 0 obj <> stream xœí\YsÛ¶~ׯà[¥NÅàÞ{Û™ÖiÝ4]G¹m'ítËNœx‹e7˯¿à†sH~$(E¢íÚÉË1g'ðAoáJåˆôI쟌ìFÎËÅ(kvv· ââåèí(v½ô_ÖÀéýçÛ™~QJ'q“ЙŽ„›$±ˆr®Ò ýw8‘n¤;œŒžg“© ÝH†jüj¢›¥ò£ñ¡MyA o|fO u¬ßN\_%ã¹Ò—eK?öõ‰ïLÏØ{JDãKêp4™FšŽUÌ$ëpJäBwà÷èÑÅd¸BH=Ïju²!#é'zCæRðøÚÕ°=·±Íæq@ËaSÜÏ!¶ùš„ LJ4öœIâ1`ð¾l„÷©Ô„P^Â9póÉ_³GÊ ]éÚ`fsmüy¦öPH»˜&~a" Ñf]O´ø®ˆÉ'SQwÉ`Ÿ–ÐWöÖ9—šÌÁÌæ öÅ+;­K¡®¾ŠzL_¬ê-¨0êdüq¤î!ºÍ#kÅöÁÊæMR<­³õ 4o)¥Ç•Êb?âUùi8ñ|íë*q•Tc·°éêI䣹Ï^¦~¹‘PN:Õ@у»Ê3áI‡¡Ø™ý4š}þ±õDêµOI¶VĤp×ÀV†¥°g3°.£é„C•R`$¦©ãìyà«ê¼ œ ÕŠ3ƒÒn’ô¼`“ÈÀ"Y+›ñaDV‹0MsHpüd«'Š%“T{BÆ<”5\ @éBí0G{H`L<Â^a‹(bã6peKë¿Í?[”X÷  b¸i:`è_E®Y¤‘›x!;‘SZ¤ávY€,-ñ‘LjcóœÍi± .´ Æ‹%áS°šÝJ€ ©´e  Õë‹ZSE–ºEÐ?ºá”½åçÑ[ŠÖü8Ã0BBËìÎ)LEOÌKJSaËÀºé *Št¨Šò“MÏKåÂÎç&p.û°ŒÓÆ+Cÿ¼œ P Kùúën†¥ÐãeÊÕùΩuUÕ\˜a^Ë©u‹À¥t–Èø¯`NhKCÖ”e:ü“sdŠ‘ý™Žú‚F¥©´×>Õlk):{f›,ò,seßj8ã’ÍÃ\ìÆJÑå”\¼î…±ÏJöJ$f¾˜¤±*…ã2ÎÃä²gµbu²L=%g&Ûæ!èC°’ d’Ö€m78jNÒ\n£\íÐG›Í´‡ë¼ 5’©Ì17Òc“Y]ÎH–P·Š,[M’ÂM øOYÊ9IS1EÕSó'þ™áEÍh˜mGÔï+„h³ïÔlóìêÄP•ý îú…yëÈPûsÚ‡[@'¶#z¸CõèrŸÆ²ñ¹U™m÷% :º loá²·D^ÙÖ¸;8°Ã䋆€2–]|‚&þ¾’´BÛ[· hÙÄtàNáhW¯ù®ŽR- B‡p¶2…?îMáö™BۂЙEËéDNùÞzXÔ—pàk2K. ƒ(ºÒVž>õÂ-a3š».¶s=²€Ñðg³˜§ ±¬Y̼1ôî\+†æâƒµK8k½‚¯]·E>N X°ÖèzEx9ÜZÕ@é‚Ò®Y7 a­YñeÓaº9„d—d[ºyóíPŠ-¡ÝÏ& ¹€BoX%¹„Ðoí‹6LŸ™áûáÿg +¿7´°æz|»íý gCü9&úS¯Ö·U©lõ̰X<øsÒÓ×(rÑM‹ÿ­¹xS‡ lŸlÿØ~%À8¼ƒCpåüLäS¨§ÿB× ˆüÚ"pè%?Y$¶„pÙʱk¼Ù”üžB¨­ý{`må—kkSü›?R™ÅãÝÄ}ÛÔqYƒa°xõ%‘ [is”îíÍÍD¦›`+7-Ì1,ùJ±>ú­p¦ÆßwŸî%׬jfï1ëÅ÷ø7R-ÐÎJ-j¯K¿kE'cÍ!6Ú’7À¼a ÀrDGwo7´­ƒ÷p€Æ;nƒm´Ô„ôpÓaÐzo™!LãÌf°Ç\óÞõ ãêcRta'z|è‹ïpãì`ý\ÙTÍ·V=[ú'WÖá!‘¦~X¾$àhžTôÜo¸±;ÂØk¬¨ì@}ƒäJåñ/U¹|Ry<€±[}újÌ-¶‰~p‚¢8Ùë÷ ß&±²ÑÎàk8ì°éX6uÛü ¡Ö¨Ç†ZíèÒºe­¤Þ@Ù2yYÕ·™°Ðû>/Ý=|b¨ç†¢»Ó_6b)¿ø—MüK@®Ø7çÓ9”þ ÃYÒºƒS#Ž¿ìµ×PU'pê¸.Xp²ÖðŸ^?®~ýlE¡Y%Œ]cì¬þfµ4ü‘°‡À'>x;î.¨Ë[ý—jaÊCt¤øwçSë·†-Rbuá2×ú¡É:àíJk’±â%¬ß½øuÓïf£'úÿÿ§AÀ¬endstream endobj 30 0 obj 2721 endobj 34 0 obj <> stream xœíZYsE~ׯØ7VT4ž{wò˜J€P€#ТВ%Y6Ø’ã#~=={Ìôî¶¼’;&Qœ‡ÖlwÏÌ×Ç\ý6áLÈ„û¿š˜žö³dq5(š“Ão+âr1x;È™òÿŠLOÏ“gc”*qÌÙd|<à̹œg¥V‘X¿I2!˜Î“ñùàuš GŠKø­Ó_d&S:= Ô/%­çRp–I,„È9%ú ê…cBŽ~Ýón Ån!ÅíýÕå[zƤÊëgÎþB ôLúá…ÝwØÞGëþGœ )‹S÷i´YIQcxŸ•V¡Ý¬’¢3Zz’ë*)BÑÄõaY9fyQÉZ8 b}ß6D!Õ.ÀþI²Q€k)jêaj)Z©o]))êÇUr(ôs"²ívJ!nÂëêZŽ-õíådÄ•w/T9©·Þ‚5‚ì¤ò4Uái¨«›8ÖNÁEZw.¸ ÜâÑ\„ÉÜ‚ ÔªÕX¯{zCʪ* øŸÖU•‰GåA£.²ø¼w,¹–¢ó?A1ÅÆ·¥»Š8„] …{5ñ^cWCáv5»Š] E§7úwWN±+§øieWN±©“ìÊ)våD}áå»2Š;ä‡}Á¦V€Ïà9á‹ñàgøût¸XÐendstream endobj 35 0 obj 2141 endobj 39 0 obj <> stream xœí[[wÛ¸~ׯàÛJ{"†¸$Û·ìíx/uêª=iɱ½±,m|Én}¼`†äGÓr¯O£Í>ŒA`Ì 0ó F¿EI¬t”ø ±\O^eÑéÕ¤lŽŽ~¨‰§“ß&ylüeƒ¤—ëèÕ‚jqá¢Å»IEždW¹”þ.Ò(S*¶y´XOÞL£ÙÜ$šþ¶ÓC"ã43vú@E:ÔË@‚~'ú¨M¯M;¹†ß%GmUç™ü^R…i&òä–É “¢ï5“ä𒘙àpÌä%“+Ø÷ .gv¡iØ?’.‹Hé–.sÓÑ¥ËmL .u©gó$vZë\OcO¥¬¶ƒíf6/✸¨RÚ®ˆu^ù4—u™.¦VŒl¸Øl:/»d¦HòiR7綤K†y1U¢9s.fsS«öÒ=.üÀ4ω·l&9)j×ùô™Ba”öR äÌŒ”¢’8u­q7ºû¨9$šÄÿï©øÀ+fê@vžÕmΊSjì:wuiÐâ× É”ôøódñõ›r&gÊééÙ,!B—’i¨ˆ(“:—Ù“¿_ÌÈ(”)¬hó²TDÛ¢¦²Ê›3÷ŸMšÉý<0ºäï×ü½bot’ù’ÆI¢ÈjÞñTÇÜa9›grÀ•ÿ[Ìå…Þp ]ÙØYëò°AZãw~Ád4£3bu®Ð( ]RŽZÓ¬(UÕL!f.W&Vzɤèõ¶lMè¾:ßý~ˆ·6…ìp-HѺ‘Ì‚Ü6r5Þ¤ÈÌãçóF+óúô®êÃÜòÂt¹¼9Ù—­W\õühå]âOlF²ù~¨m nu <ó2Pg:>úŒ¸î­Kޏ‚k[^îºÁíXáH—LžAWü¶.!__ÝÜè>bò*¤?¿òh¤ÐŸ@uÔÒd ™öþ®§úwŠ©ñ¸§9àÙØ|nj%å3W¹²1Ÿ Ö|6V­ÐÜ䀇‰‰EDv;¶†‘m¶fÃÑ›èpSšŒq䀒¬¹±……o?“D^t­vÞ¬a^ÙïªðwÈîIgcá¯|Ãlõ8†û±XqêÏ™<ׂö¡êÝÑü Š+ã-œxô–º€Sà [_O³›§/’#ûŠÓ@±lç@Ѭ¶¾#ÂJ} ¦CÎ)úZî*l@[t˜[*ÝBžƒa-5á€þ$o3„oàŽñæOªË§ ƒïÒæò¹‚l‡.ôêê¨9Ìko×¹;² Å×íãZû‚gj†|]t9ïv&5G:éþBâiøbÚ…Üߟx ÷8ùÁ¢ƒRa Ë‡ÿÕþði‡äÄç#¶ÁÀÅ—wcTËÖĦ1ŠBG/˜]Ž<¾4ÐɃYA¥%œ|Hæo>÷2o¾ñäqZªÉô˜l<Ó³„uN/,¢Ìö¸ÂÚ*ÛSR„ˆÉLZÓ¸âaU‹¨”dzO€naî ‹´“Õl–*H‘ÕÈOŠù„Zª=¦V‹ÝvsªbMg¼~¬Å2 ÚÊ;¶SµfÐoR¹Àé}™¶'3*¤Tk¡'©¿¸šF1ŸHÀŠœÜ e?Å\8¥I h²$Óy_™^bbo[^ Vf õ*È^¶j^od"•ºíj’-¼l]O RÄ\"ë:¤ü‘d-Î' =ˆ§LòίÑ÷Ñ÷Ψ¢°ä¸ŒuÏQÜÇ=0Zåˆâ¿÷ç„óQkHž—¢¥X.òUXí?õ}Û!÷+Ðã‡XE༎CVpØ5lÃp|s*†áèX¨d ™íŽ–;Âç·ò£@}¨í`ˆjD 8^¼…’Û Œpd(fûrxÃLÁá´+¹(’%ø] þ6Y># Ž¢ÎÑ’ óFë.¡´BÀQÿ§ª^=¯ƒÄû%÷¹zL¥Û£jÃx!¸5”À(žÚ@a<‡46«‘7³‘¶Ræ³öKïÔÝç„é0ÊŽéë=Ü¿¸K„¾6c"Ư3¸è’»ä€x´oá.Âv)õ19—míZêc?c©~òRŸ†ÁaUÞcó| ¨ç‘Š~ê¢Ã²1KœzhÑÓ–‹~žÔÅ!‚ÕãÂÞÁ²œ¦ìä㬈³Ü¶aȹ‡&ÍR=3î@À¸Òf%µK|J`kSŒé„XÓCY U 8^_’ ȃð¼ÁÛ½ü@UŠäªÞŒÈrˆ´€þ¬•–ê´p1Ò‚XÁÓââÆ® ‹Ã°!X„ôX°ØYǰ¸2„±J£ÿk\lÆpq6À…={\ü”¸x"00Øä=@Þä=@¾¯%`€ü©õ¥y¼?#ãì±yâ;»‚&_2y¸ÃáÅ>èœE‹Ú9\ä%œx´âò³_+»àtm²ÿ$'…8Ý?™Æ.)´ HØ> stream xœí[ßsÛ6~÷_Á¹>”º±xdîÉIšœïš¤µ5¶é=ز»gKª-OÒiï?ðvA~$;vÜæ’Îd‡‹ÝÅ·–ê/A ÄåßV˜^îüí@ï®wªÇÁÁËF¸z·óËN%åŸê—§—ÁÓ‰™X"Š,˜œîÄQQäqQ½AfEè8¤ &—;oÃd4‘JÓ¬ƒÑ8•q”Š<<4OÍÜ<Óá¯#‘D™ˆux9ëH©"VáñhœEZ .Fq” Qäyx1Çæ¡Nò,~^>yš(gôõhœ›•ÍŸpéèÎ#™Å©êºÈ˜mó¾]nN Nj1Îòv¬PE¸_/œ'qÖ –…Ѷ¬·Ë<¼¡§«‘ˆÒ¤(wñïÉ?£… ÇÑZv<åET9ÚÌ™ü¼#ŒL¾Þ™üõm81všUD&Ã3³ˆRépf¥ÀHIšeq¾,ÍÉT§áÁh,#™k-ŒíÕ€$áØD.ŠccfXX±•êg‰Œ­ÆÑÚx^•¯ UFWª(Ö‰®öÜŒ¿°ïèኔ,èéa¯LŃ´‹‘×Ìe)Yh¼C¨‰-Ê|½ðÅ›¥ÙÙBÒÌçÂcl¶Ç,“LBšÇ åi*øñ¨ì6Éfб·n’¥Ycbªdã˜Rb›ev#©µ]fȸM‘qƒ#ež4Y¾‘ΖxZ¦žN%÷Ü”†"Àašž´ÔXgþùÒžL,à™ò·Hf"ÄÒ€®*‘(‰R”xÑJe"äBE50´#™xb‡ž[éÚJK+]t•‹¢™†)‰gpá+ø”M[‘8Ódã|m¹Þ×ÓÇík¶Ï÷$.àØ¥ÏiÿN;ù£™ù¬÷`˜/?i˜ÏIœÃˆ¯àön|^azp fÃ%0ƒ°²shN/¹3p/*ÚVÁ Câö ^çI—Vº±Ò ^yd%Và$jË}¾ãÕ0®.5* À–¶ËfÊ’Â)›¿Âv¨Oâ¶ì A]§h‹gq…ÝŸ‘Ö#KÙ|VÁYág ™Y×­Ø›©‡*ÎZö¶ª˜…Î3Nù–°Ä±»†<λ¯‹4dÝÂlj›ˆôÜØ–±cj £Êè2˜väw¦½¬9LbþkB4ncTš£Ê8Õ—²MhÍ—yia¥«Þ[“YLH*Ž\så{öý¡•žYiHŸˆ…(à™¿ÜÅ3SgVöý{ œ\v}ýÙq†!ŽH¹w¼urôØJ3+QˆM´±35ëeq 2NðhèPRI9ò©‚¼'ªà9 óî^Ö6{r(8 ‹ÞPîFéîëúàx3è£-X#3˜mYšÂ¡Tõv$‘¡^kÄåJÞÎc%7nAé¾ë,-`q$[N»–×þ™¯¢¶½?]”½?CùÍfŠÆÁ*—ºßû«4Ä$:Ý?»Çã.êÖÏNëZ¡YLtúyu‡K™T’-Gbðž‡Ù0²@ÌYfá2+‚!g+ôú²]’Á Äæh‹±ì^pÖÍšÎmÀ¡¢ˆSõ¸âpË’‘i&z7Y¯™Ô`Úœö¡üXQ̬ Ê®y–˜z·ÌZæi›5 1Îâtà&ã¶Ì”RQžæNËŒm˜€äjd–Õ±Èù‘¦KY§áÜ€S}ÀÄ7Ô¹§«öò6ýõ …vኧÆæ®ƒ‡PÕ&J=^eùº¥†.¬{ÉÒ]£ºW6†®UŠ8·v“¬ÚúrÎð9[pTy% xˆœÝز0ݤicÒæðF5¶²­0[ûìàý;ŒÀÏíj¶ÐzÂÛlïU¯Î°wSgº nños‹d]ƒN)îëe6,!(6ÇFIð «J2Ngµ:_Ò(M-ÁYNî98”Ê(Ó Ý=ß2pa½êá7ßTM¡ö |6ßtÿ›—ÌbÑÿdR|‚€ ¼ro”DqVÔmFÐÇÀµ£:Ü[ð©þ‰OR7Kð“NsmE½ŽRŒÁ8‰jg½¯?–eEŽOÊ´ë ÒµÛ£$2ÁkSËéåõÛ@ðx²Cíd9Ø ;ªNqmïé„;›Óª*ñâ~¬ÿ§¢áÒÛÖ™5½­z7½µ†cN‹áì¢2’$#ý4"½M¾Ó×®qb ›·Š^€+à|íÍ®€ 0µUn€–]0ã¸zÎÁ³«5·Èê:º®Å@M«_Á"è‚úÁJOPÈûqãÝOßÀÌ)¼ì²+ð-Û »pûd…»5Gpµëi/Ü_nQc†‚H‹c åÌmÃY‘-ÒØÏúe¯Iü:˜…“M; ñ9‰?ø4¼¹³Ö œ@eö©¯%÷8t6Èéè#ÝlÀÕw4ò&,«‡pŸÌ'›»òÏ€\·æÔ¤¼äÚK£ï <òó/þ:ñ_>8z §}ç›ÆØïk°Ú:"쥇·3ioý4H„[›!ª¾‚›ô•ÍCÛEUˆ2`oDÂÐo«W`Fÿgp|úEÛÕG]—ƒéúâq¿XÊê²FM†nŒÏ±Ž¦vìÇM7P™—tîBËN 2‹»>óÔz‘ /Eµ°–àí} °(v¡%7–¬Ø½cÀØÿâá± gì3¸ÀßÃM{=È4 ìï¢þ¸|Ð|Ž0ôãaèÿÐòÐ"ÿ”Ђ³3Z¼Ó—›ít#äøTDÝLɳ§ÜŸ9˜›kÝ9<ÇÚQÆB}w”±æ|NÈq/¤ä«ÉηæïÿfÉ_endstream endobj 45 0 obj 2473 endobj 49 0 obj <> stream xœíYÛnÚ@}÷Wì[i\ïÚÆ¸oM@Dz1î-U$(R R«¶ÿÞ5Ä;Cs‚C+ÂËñ0;»s;;87±¥NúÉ@/±^FŒ­©XD;0X7VÕvÓ¿©€ã^"c½P¹"´ÃŠˆÏ-Çê̬JQñõsè‹@JÛ«Š8±¾–Äó²ë(ýì•êÚ~àz¥ïu J º6èÒ 3ƒ^Ý!UIm˪ôlòmŒfº2tK‚‚àk‚Ÿ l”TÚ·¸ei/•ö²méçgé“—>Ä/æœn™ƒß.tU½+ƒz Á·}ƒ. šZÛÁ`Åè^ü¯dÞ¥šgû0þ=‚C‚WpÙÁ ”" ÷rõWz:ƃÀ« Õî|%*O¶vWb‹Îö“à‚¿¾…Ò7Cc5¨À‚Ý…aeå˜en +*úâ;½ƒZƒq=‚€¸ÓÝáEvó4x Rµ…8Zº|O«CV¨ŒA 4—WزÍt$ž£%k€ªf 2Euv ì]‚µ¹9c„l InQÙ9öƒ=(¼ç8¼»ÅoìMÑ¿Æo켬vÏos¯Lw…³tZ÷¤õ$¤…ÿ{é®s~gbzªÇÖ{ýùü­ endstream endobj 50 0 obj 784 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 28 0 R 33 0 R 38 0 R 43 0 R 48 0 R ] /Count 8 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 53 0 obj <>stream xœy XS×ÖöAÈ9§âH ƒè9hi‡:Wqp©ŠŠ¢‚¢ È 3’0mÆ„„AXEÅ¡„âHË­R­ŠC¯C[¼Özýnµ½í:þ;÷ÿ¾} ¹}n¿ÿúädŸ=¬õ¾ïz×® e׋²±±açÄ$ÆE†Ç‰\m„!½„¡¶»q  ¼#JùLÜÑ·° êc‹úØí2|”ƒ7ÚûCÊÊÖÆ&¹¨zNLlJ\ä–­ nžÁKWz9Êòdœ···ÛÆ”îoÜæ†ÇGnÙáö!ùcWxTLltøŽ„insÈ訨ÈMn[¢Rb·Æ»…mÞ¾Y|mEXTøv7¿È¨ÈØØ˜]nžs¼ÜÆ;n4ù1>02zcb¼[@ÌŽ·ÅnK÷$F…ÅýÛCŠ¢–.öMÙ±iUÀìԘͫçÝY2wgDм¸-ŸøÅo]ꟹl~â¶å vm^˜¶bQrôÆ•ž^n£FÿhÌØqã3fN˜8)äãÉë¦x¯Ÿ:-mºGú ŠM §ÖPK¨¹”7õõ>BQó¨©ÔÊú„ò£¦Qc©¨¥”?5ŽúZFͧÆSÔrj5ò¤‚©…ÔDÊ‹ZA-¢&Q#¨•Ôbêcj$µŠ  fS“©QÔj*šCM¡Þ£zS6T_ªeKõ§P)JJ…R<5ˆ’QŽÔtʉr¦fR.Ô`ʇr¥†Pk©¡ÔVjÅQËHr);²63m®ôšÚëÛu¶÷íx»õv’Å’ã´]KÿÊÌftÌolûâ½yï]ï=±÷9ûHûoûìéëÚ·¢Ÿ{¿¬~Oû—p<àÁ@Ï;¶;¬p8-í+M6 ò¤–q²ëŽŽwœf8å;u8=tzç<Ø9È9ƹօqYärzðÐÁÛÿ·ëJׯ\C²†´ùy覡/‡¾ã&qñÜ#þCÞßÂ× ¯û ¯‘AH4ØÀÔfhh²…Í‚^vŸ®Õ¨+«êÓ)ÙÊŒÌre-ÿÀ˜8Kü”™!~üa]£Òff(婞ˆÓdàa€3 l„ia+ø@ì öX½ ci½F]U©P'ó4^-è%x¢ ëhįûÐØß˜(‰¢Sää ­øh$][NÞÈ&[šN_ ”dx7 œ‘TÓdeØ,ÔlZaxÃ[8î8»K°”,«ÈÌ,WI2ȹ4•• M ¿†r¡FRe™ÛÒ©æ Öð—i< ?ÃSà™ä « ¬ #4J=oZ’¶ÓkpÔ7;IÂ4a’ÌG<‰8ª–oÄ¡t²yV=N–c‡B}Ô ÕíѱÒÓû?mОtý é•û“CÑ,F‘Œåô½èó&RùHPÓæ]›°%iñ'ÙDš àC²‹g'éIa›ÐGv|E] bñà‘£°3ôÓpjo¯;ÞʪßS‹*Ùryy¶"¿ [Á­ Y?‡Œì5ëþ?ÁöAôzz7lE /½ZT¦¬r­P—ëyéÉŒôv­²|7'=™®ÌNá»QãÐx6L ä·x;I_Â#0ÈÀ2IV¤÷j´êÊjyYªÝ¦ñh¼…µ’1º•U¦èö£+"@Œ.øãL„‘$•–¾´ÇÉ’ ’Á©° ¼ñ6É"KæÀEÄ®ã>•îA÷]ƒÐŸ€û[˜!ƒqð›¤Ü˜èc…G/Ë´‘Bb½eSž4ƒßàqðFâ)¢1#SÌG½11Òñm­éídÞWHÔÐ0ÿ&1Ã$'œpk€H‰Bƒ-ùCl…êëô‹ŽÛϾÞpnòþú‘ƒ'Ñö©ïW#8¬³,v‰6àp„ÃæÞ•ÐO×Ìc\/ƒ4ðbÚP£òhòÉ]û¶ -lÀ²Õs9ÓÂa!ˆƒ¸ð ‹d!†ÿ:ŠNVgWV‘¤r— „Á—ðKÉ¡÷k†$7#“d–“ž^Fˆ Wá¥Ä„-ÁƼÞdï…92ìðî¾õ‚ú·ÿ€~0Èã¿ð ~£Ý?ŸL}ßsòTw÷Éß¾yöøÞô~œÞÀ®noÃÙfš6A?'i¦"¼‘¶$à–Ókî.=6‹@Ñ~Y—¶âþ¯?û×s–“fŒÅô¤Hš¹œÆ1ðVöæÑTw2ÒcÚT/dÕGÿÁõ¤ÜsÚ›Ú`®-ܵ æÂ^I#]£ÕTTÊ5}-n¢±'N£!Iò™ÈPQù’M#ºÛHÃ|¼W‚#‡1ДX@3Ü‚´_ pÛ`+ä; úFÆn/QÄÉô|‡q¯ …’atªy 5ü%QÖx¬Ñõ j ׌…]éPl¾"ÓÇ %²µ§—X€æ£%;CCÂÖÅÌCì»B܆Üh;pö»àrôCÄ€Ÿ~{^úìG¿À|VÅú­àÀï²ìí_¯1Ó|¼<§ßõêÛŽ˜Ñ³ 0Â`#ì:“_—~tëÓi­#ÉyŒÁý°ãïîàúúœkÔfk³U¹ù9J~ÓöÙ‰ShÚ­ŒßXƒþ嫯ÑkÔ´g,ÛEŽph'äèÕ W›Erä8ÂB¦VQž‘¡2QÃ_—0ðXØ ¹†x“¬É®ªÐÖ@o8šÇàÇÆ irÓ““Vƒ0ÑD‘{2L” ;aoì ä7ð0ìÀ &‚¦@†‡ªû#¦±;: 3X‚éÑÀ€¸ýH~±¼¥"òþ`Ö klÄÐàø†L9œ¿ÌÀðÁ;r8Æ«c¢LH„÷™Kèdúñ­†– ìlføÏQÌf›Þ‘€R¾ÖC§LúøfHð‘™®¸ÿȱØÛÿ} ô{t¥¡ý ‹=™Ts)5¥WúJoNp2/}½B¨¹hzÒ…ÚÑ´ô1ö% 5¥ÕÓcI룎Ÿùîªiäh%¤XAˆU… i£/œ>Þ¾×›;§B¨±d™r•&³ˆ*óS‡#vÜ¢³ùÀ‰LOaýwzYéñ_ Í»Ê,êªÓK Plé²®7¤É`: tgÇËŸ&ßÎãÃV‚kL´Öè ºçT˜£!ÍŠ!€yÔ¾ÌßoÎÒ‰|¢–|'*½IpÝ›7$59I/eˆJ»‡Ò)føÔrà$Âlw†‚À,»C8#m…3^tªZ^)òŽÞèËüÉN¤B0L¤kUå™é&œ^ úšéŽhY}!æmˆµž¹¸ÛÄÌû¸s¤é“™™·ˆ¸htUY¢ úÁNZzò ô:½ÿ´ë>T—u A­,Î/£®­ QO­âwîMÔî@«PPÌGSØ?Àç+ühz_>ðOûÚ•Gf»b»Ñ£1‹™ŸF‚ÝÝ‹GÚÏòXÆø/öçð{´E ÝŠNÔ?Ùp´ò2°dÏKŒôUL0š+{ÕA`4Ëç£Ñ¾F?‰QÇŒ!½Ixlp0qЮ‘¤r%¿ Ád–ƒ^àDÈØMG·gx Ðqܸ…Êœ\Rº-hKŠ K¸Ï+¾Ö5 z´'ïS9±còú¼=®Í9RW!×fååæçåñÛ“R•»P&RÅ—³Òd]tBñ×](½ JÅž:eÿoJ[y÷fÔdk ÊJ <23{à_{1©eòªª236‚ÿ @©¹ôåM’tŽŸu’ꄹ‚L¶õHhõRÄJý¼f‡,Ü^»ãP (õ²CyEyPy0ã`zMJ`¥º 7OüxÞÉ3œš‘úU(´Äþ-+?…|NV+ª8iA¥Z£çuLsÄùÔv"ï’gWKjŒÚÇoß®[¢žP®*J¬$síªÌª©Ü|åhǃ/¢×—rÒáiEjE…k¥ZK­û†‘'$““êvg“Ìër¦˜Õ‹´%®a”åЗŒúufÚ‹(g‹  ­«¼|ãÓš+È¥ í#¾º¾ÛW‡2Öv²'j¡¤H=™mL`XBà*³‹ ©]îÍ”®YäÁÖ–åx{Õ"[Ñ´7 ‹9±¸åÇ£gÏ¡›ìwSîWø§’2Óòª %v £™»·‚—,œºÊ««ò-6À)±Ö öM¶¤þ…˪ŽVW˜2¼B”ûÝé¦3ðx½|çÅJRÝ'<…!ü=]?t³¥éÔÉ–šk¨ÝÝXºŠM-# +ÓÖr/}®&=]E*{zbyí´%„†­Zí‹æ°¾ ÿÛ0¥íëÚÆk] Ư$ cšma5Q°gôª® °½¯E…;ð>Ú§-äåw@Ÿ:¬Uèä9yù¹Jncü¼Ôeˆõ ;ÖÊCÄX|\·rý ½là(Éï=Q¸&Xª7îÄ­jßÿ5+»©TL¢C…§²ÝJE ¿”!h¬àêi¯7«Éäº80T06Æàð£!¥®µ8I‡ ŸU2©ÎZä:|ºÿİÚÍ |Câ‰Ýß쾓Y§ªO?”Z³me}çD0?ù¶æµì!5±„œD7%Ä(*ªÈrÜaFª 1\¿í }¾¿ýº5¡%ü¥Ñ̬ ÐÆ«ckâkÒP{³í“Gm¡KJLØ/SVºV–ijù_±‡6Eó2A /, Ó‚­0;̃uBâ¢Ç$¨°·˜€K8×CøE«o{[ècb(¤èŽ2oaÁ H£»}{§¹œ}ÚöÖIš ,r|`éB¥É‘BRc¬’_EKO/Y¶$z¾ëªM†ó<\ei`/‘o§_\ù×ö/]ºÂõ4ëbâ…aä”È0z2þkBpxUÇ.£_ܸù’>Á œ01³'ñ•Ü~ºª'ñœUÓÓšÁ›ë¾A4DÞ³™´ò¬Šª2’5 k1ͤ˜Û¡ZîL3&ÿÏîÞâÈÌ ÕËÌ!ë ì]‹'ˆy‹'†»M"Ïʼnöl d°Ñž;ÂT¡¤ÉJ½GZð~‹&^§$‰Æ^Ð)¹i¹=°d aº±¤›Là öZàÒL:ÀÅx(:I0æËΜ"=õb ýÛh`w°5º ý iÕ­ã´|O/5Æbaˆ$€ŽqÈ2!VòÔJèÝ-õµN ;ìk³Æ‘CÈ‘R™;÷ÚŒßCÈýØû·°Ýþðry9b+ËËu%Eù¥ü<ýýŽ~oøõèWTZX„JX]vygšë/¶DæÊ’ggå¢üâþ<ð°‡x‘áý¾î——ƒòQ.›Už­ÓªÕŸÖpwÀæ3è~F¿lÿ§?Ø%œ’—Ë‘nqºÍà.›‹‹ ò òPŽKzåî=•ê’:@(BÄfØù¤ îI¢ñ zÉá1Q¨ɆqYq\¹¼¹Ô¢rmq}#”9áhzª•×X?%o¥ïµsðžlstLø–†èÏx-}êÈ¡S§¢…›&Ýõ®¯¬XSTŒJY­\#ÏVæe¨8ü×-ÎUä \¹6[«--­T‹1Io²‰É{ ið« &Ì ÛbÛY˜Â8âS` lÁöP0žÃß¿’¡µ·Ÿ9Ðx¬¶5£Æ”}‘ ëôþh;žA‹’ý£×%FF&…hn«‹oÜq&åºa¾²cÄßÞ JËÍQ"9ÙŒ\S¬)F%87KŒ•˜·º’¿kukÁ™I¸ñW!ÀAZÿRp2ÈŠ™«ç¾ºÖù+Ý@´£oÝá/?ŸôdDÀ²˜.9>3¥°Ùår­®L]UÂUy£åbï½Ä/49fôgáxI# †> õæ¦?™“–n;~8BjÇ‘dŠLˆÝž—œ¯ÌA,Afõ¥–3×9ðeÐÉõ'Šª‹ÊÕ¨Ue—¥n)‰¯G'XòÝõõg‚Ó³»3Ô¹ú<îhîþ(’Å交cqÛâ³v¡bT„zⳌCznQNqrQ!Unž*?Ï9?¯€@)KUšðjg,à5ŠÜœ¤pAy…yEä?ç"•:¯ ‘L–³¦lB“ÁAð6$›.)0<–’ö›4¢ß}yðTëÕÁ?ÌúÚcåê´èM\RZF ’³Ð:MqqE9×Ðx^±¯‡,Ú³9,…ß%O+XƒØÔ¬.ÄJo¿zº`ú¬Y Æ,_Vwb-¯,Í-V!Vž-—gVd4¦ò­±ç2Ï“•ùéÉÏ·Ö¶Í8ÚÕ~ 2À÷b5‰“p„2»0~øüÙ^Ÿh áUêœ"rˆ¬ìlyfUúÑ]ü…mÒ.’CØ÷7p;ŸÇ¬PìZeº`x7Ø´ È_Ê^Y’¡ÿŠ‚¡–Ï£º6Ùj°ù¯ÏDn#Ë×T!-ºYÓv¨åüýGg"=Òç—çhrJ J«S—ëô[÷lX¸0nÝJÞgñÖI³,žó÷ƒÞ¯|ÔÛYÏÆÎ Þ佉+ –Õ·èj«÷=Ñ´—T;WLŸ´t¾¯ÿª³OcùÜrT¨Úo‚X“É ‰±ß(4ËðD£¶ ·@…ò\L:WZZ]ÊÁÇB䦑Å$³Oœ~wZ˜®3Ó=VêèÙ‹B÷.гçóë®#ב®Fbˆ»«hÂÔç‚=´Ë¤ïžlhnü|ꃑ#§Îx4ì›Nú|väæ• x6ý—_ž}ûº}óÅÙÇ8ØCÆ?ørùÜ…+}|¿hÿ¦íò}Ÿ´“>zc©ïüÀiÞ‹Úîwܸú„7¯¹ÈK¯f8wÊV£¬0¥( )ˆĬñ™2 %£”y©¢4·0GĬ"3åDÚç7oÖŸ¹Èw|uü{ï±0{ ôýGÌüS<œüâîŦÇNuY¿>+%=mû–M»";oiû·ß_½Ùq«uýäƒ|I6*(K0ÝÙ ¼`#>²Ù­~ggŠ8Ãtpþsµ–ÃEò|GDDÃóóÃ'OF6©8œ_ÜM*̼¯0å%uåæ 3þéÛÇ›D|æÊPãþƒGKôÅêTÅVdkÒ—‡­ âDŠˆ‹ß’›V P Ý(C£Ú“"¿>EˆÔ¼îrµX‘•¥ÉEÜöâ„OQ£¨iȵwÛcõº:d|JeÂ$œQš_š_Œ\ʺ¤¨¬¨Ø™Øßb"Je¹eòRü;¬vòC[ZRŠ4.¨¸ 8ŸüsÎ+S– ¢Üüœ<¶»î6×Â’îZ¨Ñ–tW=8Ö$:ù{2½²<3C”éMbRʲ«*ÅÖR0Ñð¶ÙFÐÃRo5UU¢ó5ç¡ÊÈÐ(ô¼„ß·ªÄAÏÖ/©N˜TGšºº:úóÞûÏûô1ôéKQÿ‘È0 endstream endobj 54 0 obj 6462 endobj 55 0 obj <>stream xœUTiTg­¦é²Æ„¦D»úˆ"ˆ ¨‰¢à¨¨Dpƒ°) 44ÝMJ€!*pA ›,‚²(j‹cÄuw%Ž8:Æ '™$ÌDóªóᜩƘÌü©óÕ÷^ÝWïÞ÷®ˆ27£D"‘­¿B¥Sh•Û£çøhT±1*åŽt…)2›wñSÌø©â¢þÅј%™JTþ0Æ‹a¼ùñ)L­5~a…µ–¸o"%‰2UûjR²R•ñ Z¹ó–!.®®³¿ñðôô”Çd½‹ÈýiÊxµÜI8è*MJ²B­]"÷²U*åvy¼*+%!M«ˆ5}­R$ÉW)UÊ”Nîìë"Ÿçîî1GxÌ[¯LŽIO“oŠV§Éä¦6äk´ÑÊÿ(Š²Ý®‰õUú¥Æ§)×è¶D'ËçºyÌ£(?*ˆZIM§VQîÔ ÊƒÚDùS›©µÔ,*„zŸò¡ÖS"Ê’šHYSRj’ÀeN¥QwEóEÕfffÄîb¥˜˜4¿h~ß%«hšÞIëéÃôIúþ˜÷°Å‚ïÑ¢9£X4€Žbþ%~Ã×£pvçU‹¢3²ÒæÀФ€ •Ñä0B«ÂZÚcú2²ðý£³8™!“i•_bÊ`Õ§n÷”_­¿È5]íh샓а»\W’]´»0–±àñ]¹AtÄjSIþ;<¶y…¨fÂ{ ±xævHýýì“~Y÷Öá9ƒ>"SeéÄœí?é¦N‹æT›"U!BÅXÃ]”>zõ’{ ?¯–_ YPcý'I3y[”ÓèfL–9½™”i{£VƒýbðŒ Yæ§öOFj Ö·<ú²§¡ç‚,Ž·f{Om[I¬Wêý`¥áNxˆfO9 cœmøƒh#ú]ø)8Cl<ÁëØâ¦ÂR(þrÚ3âF,åÁN‹e~ó· ,Cüä‚Ëp-NÆeçeÄ‘V»ù…¸3+´÷Õ`ã îûÁõ¶¿BteÖ*Ë3d‚‰ªQe¦áX­›[ù&¶î Ž-3: pag·¦=Þ°¡Þ¼a‰*"l¤ ÐL†L|¾­pÂðM÷ɈŒÎPlK f£¢£ï”7q˜7Ä’úHkIUye}ë™ê3ÐÍ;Ë5‡³‹r ‰±0V¿ëóµÐ§ºˆù6cK&‚S¸§³·[±2†!¾(&"\…6ßþõ\öÕ7´Ë ¿ü5Y'Ë$6ì×Ö3¯”ùQ\èÊ€„¥À8ÜBÑWM/Î÷s—n}Ñþ„rq€V|Z‰Œc„Z¶ØIã øÛéûÃw¿ïBËQL_ï‡dŒtÒÇG†Ùa>M‚ô? ÛܽvLWqQËÃ4 LPnP‹{„ö5ÿ­€†§°œÅ@ú1\©ï¹Ü{¹í ÀgÚø¦˜R`H?MÜÞ$Kp=ȳìÔHp.ÝÛž¡Î‰ÍÖqi Ê]Á4oµüÞ¡_¾ƒ%cF:$Â\õã™ÊÁO»Á¾ŽTí;Óìö~ {a¬‹Žð©{m'Œ%Q¿|5:øæêëFtškšÌ¾FÊZ$‘~}=%æDÃ2X®^³Á/Ž˜Á,†˜=X2|ýLõÕ›²]>úµA[}À^:w¼™+,“éôþÝ9°Š®Œö¬ó{®.ÝûÚà8¾ÏNú591ý¼=|±Xæ8/$éÜm´|ˆ’Aî÷µ·F)º :Ëqê$éÿ„7cT Pʼ\ÖG¬½üuŠ(Y”B³BaSWì õãT;éðŒò<íä¤(íúUɱL™¾,¿´ Ž!Sé}Ùù[›#ΧžÖvç^—"5ˆ6×bO/jà\›>¬Ð…ûÖ¶Ú™t¨ïdFR~ÁÞýyœ¶Œò‹ÞÅMËéw,›¶×ôžB¬L¯£©F¿onºŸõ›~Bc¿jôo[ìå;$Ä™&ŽpÉÐ~Ýáa!gÎg ÓFò2=B·»ƒ½÷ÖÖžŠ¢cÅŸp¯ø<‰€ì4¢þ(qo"¤Ûæ»èúb°ñb¿ì²¡õóª§BŠÝ[*ßzŽõ%aM÷â4“éÔW²Ù•ùåP‡ •ÜÀ»UEPÍYmÚê¤Æ°²•@<`ñF ±ep]Ù×Rs˜Oÿ1Y§OÏÈå2r|s7äÙ…ï‰Ó«Òƒ‰ë!UŠæ4Á®t §rÏ9\?×]ëÝ— ÃGŒ4r‹òæ08‡&–˜ÌJ žYá‘«ÂS+OvwœüK)w©ä“â£E‡ Ú—–f,~™ð–1ãt£ïÿŒöm¨¸c8wì;´ » r÷çp+ÈïB÷ ]7À¾£6#~÷þì}zΗ ™BøŸCÍEÇ ÊþqH·Çê-©7Ëô—Â[?€µ‘´¶ò‰ ߨ‚"ÜN&ùTÄiT=lÑ×}eFŒ[YòæJò%oŒ*É[]†~bßòzÖqD?:®ÆPÉ\zûBÏ(bsMj˜œd—`™|N\ ­XÒC7t4ÇqŸ~ÖÑÜÚyöR]?Ü`Zz“Ø·sˆ×N¦Ëÿiè¹ LokrhdF¸RÉ©µ„ö#Ü¢°ˆÃëh mßaÑð:%cqÆ8”?ž¢þ ù endstream endobj 56 0 obj 2014 endobj 57 0 obj <>stream xœ]Wg@T×¶>#Ì™#Ma<" 3c¡ ŠX@ôªHG° ^GAš –(Ö¸‰FDá ÒQ€Š(¨`½¢R’¨$Š^Ÿ‰ÁË:ó6¹÷í3Dß»ïÇ”³g¯þ­o­Pš£(@0Æ#*.-*Ef㬈‹ä¦s&nâ(n’ Õ•R8‰ZV0 ‹t4Žæ©‰c³ ¸±úP32ÆRAúÁÜ¥ŠDe’<&6Efá¿j­¥µõôÿ=±stt”…+?ÿ"s‰J–Ç$ÈÌÈ—´¨8Eb|TBŠ“l)¹'ÅÄ)c“ea‘‘Q‘¼Øš°¸¨M27yœ<1Q‘&³Xj)›5s¦ y›å+OM–ù…%$Ë–Éxÿÿã„¢(;eB„"r½ïÒĨå.+\“bÜ’cW¹§Èý™Æ*%kŠæÉ}<¼\£M1ƒuÓî¸uº÷o|Þ£þï:»;»¾ÿˆ†ÐÑï¼z½{æWOALǘŸÍñ,l9ß ë`]§Aâ”ÍÃAЕò5}.ô}¤j׈¡¾ñPOƒê­h»q½»êBšq¬ì ¸áVŽ5Itäw«áA–ˆ™ˆ@xužƒgû-ÀÂÌÀnÐÑø€hÎ>ÎIœ?’³‘ørÔ9RsÐ(ø£ Z‰‡^`‚õùP¹o¸Qì×@Û÷`SR–IsݰFÌñ¸Â4iazAæå pZ;AŒÜÐzEä ŠF(ŠIV¾ÁC‚—|˜ŽÇ¥¡˜ƒ hba1}çôfß½h×ÁÒƒéH‰v"÷é…± Ì9E§äež@ÕûaÓþc¶ ýÕ7Яèú–öسk‚ò}3l2‚±YšD¿§¨Ür#þpr[N’ÜÅîRdlÛº}˾PôWÇÉŠ!‹8³Æ4•¡8]ÈMdq É÷|zø®¤ÔÍŒ¸%$fõæ•&XwöGâ°]ßK_>·-á;i±òxF^“i%Xa½—Ø…%OÅ3ð¼¦á9°þÍÝÚþ«RqúIkáo´<å<GF@»L g›ºÙöàÒs[Â~¯*?t´\òF´ýëÝw!&:óx«.©U¤ û‹nätñÒjjãaO«â5‰²3R@‡”Ž ¦BLÕàÞs‡YÔµí\LC@Çü3„ðR3G< ¯­ …ôÝ–·éu ´ÂÈ×/z¦éªæŸ÷Iáæà™Ÿæ‚/Áõ ÐxFZX´ò&6©bºDVWŒÚ®´Õö½lXò­d¤¤$‹GF&AO^‡á ‹Ÿ¨§5®ÀP­øÚÀ‘zŒi[óá¨ÉZ=¤Ó¹úñ\‰ã¹HÜâæ¾<¾úŽÞ’uDXû®;ˆ~j;u³A"N÷`ò!É|Åc¹Aä¤}‘4lä[ˆO?›:ù>“¼ŽØç{KIXX$¸Ág2³ÜT2&x<»Š®þXœ)¯‡_a„ ±ýÈüÇ ©9®Š7šEtgŠìƒÃll‚›žI¸L2÷2Eȱde[H{`ßægèê«hom;Wrõ3\¦è‹S#%äR›ix¡]1gBƒÓÄå0‰ÏIq›Tºç:Í@»F-¾ŠÍ°Ù"¬Ÿ):ûjéÉÄtœRÆ(3·nÛ-Mß…ÛN¿]dʵ«‘ Ê‹*O$øÝTu!½Õä&ºRÑv‰§#Å¡]y[˜Nã…ÏŠ[æG¹lH(oh*)ºž+iÍ©Ë>òMþQ#½ÿÖý²MÝ$N-yŠÛnþŸêÑݳ 7/œI •àaõÉKéá ,ˆ`I+HØ °  &ÏŒ9ý‡×ɈÍ6“cz2楪ˆ D€‰.ÁK—c(W̰̱Ù6·«– ‘ª`‡ÿ'ì¢!\'Tã@‡;NVE~¹‹äaR¡Še!¯M¡+ÂW§ÏDxÂú³¾óoòè‰zŒúPgÕ¹› E¿#˜€`üÖgò+aÝnu¤Ñ…šgÑ™-Ñ?º aÁn§]Š­añ)() 3În¯Ü}½@>:v&¿¹²¬°¢º;b>ç2yë™Üvvx;Y.×ÛŠRç9‡b=ÄØòK2¿E(&ëŠ1ñ‹r賸>Y_RôþŒùílzÅÀ$›g؛ϙ­ö¢ÝY;%`\JÿRwþö­úP—…Iqx&Ö`Çèe{°!Ã¥´‚$¾€ ¤à#ÈðhõìƒxèeÛPÎÁ²½ŒxàÂŽÊðµÆ>!Á¾ž1õ?í—à‰tž20 È\ë׿‚äqL‡Ó÷RñàÝÆS-wŒAËþ>ž4ß]±6B¢ˆÝˆ–1_ÁxQVý¡Âì“yUÕ…ˆ¹Z-_áã ýj„%`"0™zð‘?ìÎ;Ò8wØMˆµ Û‘"ÙÝáÀ°Ø‡¸úW ¡ 4>‚"Û ‘Ý®ZÏ¡— K…`@Ÿ¨­.lFÌÍëæ8„¬sõ•W];Ào‹ßà±/欑Î|ÿžll¦vo±á†Ø¯âÂ¥¥`!„óŸ““£7TƒâÀˆÐ-×Âî9ŸYª¬ˆ¾äUù7d–F†º„¹e,@˜EXïÄ¢³«=ïE>!Ê'¿ú¶0qÞ ¬·22#(Dzè>BG*ón7Ô_E-¨Ly2˜±Ä~ì¦u ­öñi¿ÿó¹Ž;Ò¿¬o ¾¤4%¤2 iÈÐ%Ÿ¤BCœ 7ž-Ë;ž}ü#~}æTAóycа~„ ñëùXÓýtèm¹D<ä"÷ŸgŒ‡lÁ,†Àø‰¼ÃñœC*+~}«9jµH¤¯OÈ÷W:šë;¥_ãV<ÔÓ´Ä=$ØÝ=´¥÷Çsç»xw’‰;€BÄÓ`±dâÕƒ#{ ~w=ôõÔ7ýX×Yò‰¬Èh(£S{ð׆¹¤9¦X™b löÔ ÎÖË/‘æåÍ)­`°I”Ç‚ù©¦’߸•lwÓ‡…!k½½#.=êo¸|Kª—RÊÕñÓ#°”--˜¬ Z9::0ù˜Ž.EýÓ¿Fà endstream endobj 58 0 obj 3687 endobj 59 0 obj <>stream xœuW TS×Ö¾1ÜAâM"XEÁTdnAÁAd*X§r´¾ö9ÕZAPDZq,‚"ÎVZ´ *6*}Újß¾y'®÷Î }íë¿Ö¿’•œ{îöùö·¿½„2BI$ÓàäŒüä¼´ÄñÉE°“c†c¥ëqÖ¿¦ê‹è±”êÀæÈLŠÌL*ÇŒl±",áÊp(AI%’Âûç¯Í.ÊIKIÍS:EG.vž4ÉåÏwooo媢ÿ¾Qú'禥d)'F~rÆÚìÌ䬼YÊùdtFFZ¢2%£(;5W™””œ$N‹IÈHNW¦e¤eg¯ÍW:ÍwVzL™â>™üx,HË\µ>W••«T)#“SÖg$äü¥“¢¨ Õ¼¢¬Ä%a~Åk“bÌÏN÷_·:" '%0752(/-*xýšE!ùéÑ 1ïf®Zìä¬t™lïêà6ÅÝÃÓkê´é3¼ãg΢¨É”=G…Sþ”7åJ9PK©*€šI¹Qã©…T 5…z‡Š¤‚(wjESŽÔ"*„ò¤œ¨h*”ò¢œ©ê]j*5‘ZL©¨iÔ*Œò£¦S.T,µ€šOÍ †R¦”„2§,()5œ2¡FP–”%£FRt÷Ð6Ó¡¦Ž¦>¦+Mw™v c‡vn˜ÚÌÂÌ߬ÜìŸænæ«Í¿4¯0¿fþЂ³m±Éâ ‹WÃG/.ŒpñÑÁr¼e•••Õx«¹V9Pk!4#ž-—të¤0OŸÉ»¾Íb5ØÀXèÝPÄ©…)å! ÖòñZú5ƒwé3ilÆà}o3é0°–†¿«y\€<¡-„q–F?’Ì%Tðøƒ ¶\h •é36¬.,ME£ßG¥;ŠvrsÙ›|tU£š]‡¾8rpÿ¡/ÎB©Þb”hV…°°#µÜª½Ó÷1|Òc#;ÓN”}m¨x gYYÇ?¯¶?½w6Q%Çÿî”loèÕ >±™~Ëå²3Z–˜‘×)¸tJj{¥B:¼â?º´ñ낚”FUâð87l‚ý°O¿ìÁ²ë!ŒÜ­˜Î”N_™à8·EaØ\QÿØ~~•ßn…½?9‘`ÊC¥ ŽÀõ8êiL3ð!¼Ç`Ji/ƒk°_£%>œùP+|¦• ©Ö m­>ó,N};gè‡Ó˜›©€DÐâvØ©U—ºt t62¡Ë4Lºt°þ,'{sòTùåk¶H›Øìz’“ ÷jO\y`‹š ’'Ÿˆý"€±‘Ñáû<”@0{Õl=\x¤àóL”ˆ’J3Öäfm]Lã§<ì„ö:¶ñP.YùPÞÞô[”ú~úú¼õ¹™W Ž`‰:‹‰N`°óƒ ¾g0£ç ¾XÍ·‚9ñ{ó à'{¡¹W ‡…“`q±À÷_›…-Å׿ gyº-x`qëÑò? :©`õ ¼ÜÔØ 9£ÙÑá!óS6Eجn|›ÿÕ…ßg?Gˆ^¾8ÑÜ$fK즴’¬Ìð°4rºwÜ€ƒP× ãn´­=¦¨ÎÙŸ±'š3Â#LT“ ¸"Å¥^Ù×WpÿŽ0ç£s[v} NøO…!Xá™–Db¢Ë—•Ý…bÿÓÍ™Ø[0sŠçÂnïwkÈwBp+xuJÎ<• »aº?¸XRŸötÖegbÖ„É$dæâ¹?ŽG0ëi¦‚„L^ಔ`ƒVT­=_plë±²ËÜŽVþ³w#Ns'hê6´­l ¡ƒƒøœÒ û ö£õEœ¬î9‘ÙfœÕ–bä“Ejzá Yå‘0…,3‡• ÜNXRl‡G»cûN­ 8«¨¿º®ÝCßm¸Çe³ÈóÊü¬õ+‹– d”¶;ïËÂý[>ÿ°–›ÆìrꌀáHî>~öôÅý÷ çÈâ¡da\Æ÷7ÏÁ6Ø2zŽç”EF ¹ªî#‚ß·9:¼ÒàWäèJ¡TÄëäÐÐ ›I~,ø²_ƒ®csÂs‹Ùª‰Þg“`D¦B]x½´2%Ž‹[ã·"yß¡|ù†ýìÿ°ŽódvbóÖ…0–ܨÇw_©ãÏ9VÌ:ôEÖ!T7úâ¹ÚÖû'³íÿžÇ¾‡9å’ö’ƤíDEÞfö&&°êêÍ%åòªâÝ9h57˜È´ª–wæ­È{7NÇ5¾ó•N¸æ€ì ,"í[™žºDw…L( E+þ¿/s4ЕLjÕÖ#ÛÊÀ¬ðäìãDÐ5§¿¾Ôc V³Û°ƒ÷ù¢4!¢¿žä…c[* çï[ƒâ¹©ñINrcœª* ^Õ«ó:aƒ†°>Vày|Ír ¢ªÖ“&ãÑó‚«VÊ—^ÎêEýý¦à ÒîÈÉåòçÌ:pÂ&Oð,äæçE.›°ÓÛp8Æâ)û®º®ïˆ`WN¤oÖæu[%OH |Fˆ3ÆÏÀãýƒ¾3(™˜“™wËËwüí¨¼Ýøñ†²÷—²iwðSÖr¶åV.¹OæN$sg*z}¦ÉtûÇbCDç•Úê¢6D?kƒtbt넽|88÷àÙÄÛ^NÞx”GCÔ‹„,¥e}?åž]i‹’ò3Ög,ßf£èƒ©g³¿zÿÄö ÄñÛƒ÷Å×$6=N)êEß•7œl¬¯½‹î þ¨{NÕøÝÆQ²NϪô£WmÜmü¸»1.Ûåƒ~$´¼X.éy&˜ ¼H˜‰Ÿèô™®¸Õ0 ZIáõ žˆ!•×+žËênì$ŠT(ä[ 3p+» Œ ð I8}G.°3 N¬×ݨ_:«Ûå²B?œõ@?ƈH;¡š˜®ØÅÅÅK>Fh{±ÜŸÝ½íoeû÷¬ñÔw ½9 &3ì{cÐè>'!óO½[4†6oØúÖNÇÅL̃/ŸI!†Ðw2±T "#ú8e Ë… z2ƒ >X)ø&¨ ô‡ˆæ\''æq‘û+™GÝ€ëè_’u_lB+œƒshc&ÕËË%OtF”ÉÃ.ÖÙ?Žßw8Užr´¸µpÂ.WÒ~sƒy0†<ƒ ra×`Àˆf —¬ÁÕh–ƒÁe<©@ßa .JÒpcÀ…X÷Œy%¸ýfp£<Ï×úX .d¿éo3 {ˆÚ:„)Vg{aŸÑ_5’İmóGèC»¬÷öQÀ-VÔ„ù9ªõI©òÜìM™Ûs™Ï¾=U£FÜókcëY”š_²›–}”¾aANÆ2ÄÉ ]î…ÿv¯¹òÊ ù§1Gr¯ hߎêÏHš… ­ÝRœ“—–±ê½8Ä…&×6·ÔíÛ§ÐîýrçÑ}ÜŸåèkø”ãN ^ax‹×oiGö“²ü!]Äsáï< #ŸÝb:„ÁÍ‚?4A“±ˆÿ·¦šPOÇþéÐ PŃ?|*ºÔ Gb+ˆ¤]˜wã0\N?g`4,¼œ,q/ÔC¶øµ‘5™G’ˆ†•=ú¡åVëíÉÁrüVìoL …±ì³èËÎ ùqòŒ« UA(-_·BÅɰ­¯ôÀeRï”\1êçÃû¬¬qÁ鿬;÷“lï¾Sß`ùü%ÙI ¨aa®àuƒ¥NØ_Kù‚£/±†0‚Ÿƒm }x¦ÐGðÃÓHs,i:0N ü…é ˜mxAxòT[.éx$…ÄGl£Â¡»#ìq‡¾$úÖÂx‚CÌFà…À³|´`Ïý> æ?’êp*ÿlî@(w*un£ÅDHÁˆ=ã°'§Õ’/v<ÕW.é}#í}Í;Ü,bQà†‚eÛ¸d¶ýÄ…§ 0#1ÿ¶~/:¸ËãÍóa3 WДK.ëî뤗õ±<–²ÕØ–®gªÁ–){ßúÿöˆþZÒûZ Qd‡×"÷ * ²üZA$Z‘>Œ÷eRñ[2'a =‡Ĩ€b2°™ WL2N¡`ê!…†ÿû0´Õž©?ú·VWÆ#ˆ7Ž·d¾"=dã‚ ýNoz»‰na`“~“ñ:Ñ!¨Õ’sýP×/…£ä !(§e>¹øiÍÞÊÊsßiF\÷ÍÈ9Qk–ªRn‹±Óô•Á[ð”ÑB†QYÔPùJ:­´°¯?°Ÿ\ð`¨ù=LÝvZöf )~šÇ¢·IË.þú‚‘»WH?Ôž:@ Ž ÏzÁ¶?µiZ Éý÷Ž|Ýô-XÌìÀvØaŽžô±\Ël?·³jÏ¡òúóU-ˆS7,Ÿ[”™ôž"wSÆGaeœpîTŠnE7øòØW ¾oºì…•¡,~hˆ¤]Y¸ÜÅãrà=ht¯Üz%Â,M6ñˆac°¦1g ;=¿jÿNDØ$ßeõÝùÄî½ w¯­Xw:úûŒnb·ÃË×0”®/ð˜ØÄ ™«G!ކ‹¿ãqKÝSñT-9ßçÈê)nN¦ HNPÅèàö圳ªS‘·3Ôâmgà%(`”»Ëe/NVl‡yW_¼B·Ñ¹¤Ïƒ8ýSù'Í.á¡3½ÃZÔÞ¼Õ;Èágrâ!²¼ƒ}ðØ)—rÏm ú`1xAÚà ¾z¹j«ºþÐ~(#?6²_;a(/è8_uò¾-H½ÔD)-=}°4¤*åûrÙ¯3“#|l±õÏî åÏýÀw%Þð®—cö‰/ƆÄÅÏ›wþ~kãù Üd"ûU{3jÆôð(÷©á×zzn^¼—©!R ·;%§úEhšas?EJãïN¹s¡­¼O.ME?$ÜIºV+^#í&:bÕï #ï7Vµ|£(Ãó¢&M@ahe}i+#…5¼æv›×‚°©Óbn<ï¿y÷‘¸öo’±©b…cò m¯%0„èÚA}*LŒ2ñ»> 3Ä×o¤N¼&¾°ÇmÿZåU b= ªdÔ¦=ÃÔ33{²ËÌœ¢þ‘Mo endstream endobj 60 0 obj 4623 endobj 23 0 obj <> endobj 25 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 24 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 61 0000000000 65535 f 0000019168 00000 n 0000040584 00000 n 0000019051 00000 n 0000017757 00000 n 0000000015 00000 n 0000003232 00000 n 0000019216 00000 n 0000039745 00000 n 0000037979 00000 n 0000040121 00000 n 0000038415 00000 n 0000019257 00000 n 0000019287 00000 n 0000017917 00000 n 0000003252 00000 n 0000004462 00000 n 0000019328 00000 n 0000019358 00000 n 0000018079 00000 n 0000004483 00000 n 0000006800 00000 n 0000038894 00000 n 0000037089 00000 n 0000039458 00000 n 0000037594 00000 n 0000019390 00000 n 0000019420 00000 n 0000018241 00000 n 0000006821 00000 n 0000009614 00000 n 0000019483 00000 n 0000019513 00000 n 0000018403 00000 n 0000009635 00000 n 0000011848 00000 n 0000019556 00000 n 0000019586 00000 n 0000018565 00000 n 0000011869 00000 n 0000014294 00000 n 0000019649 00000 n 0000019679 00000 n 0000018727 00000 n 0000014315 00000 n 0000016860 00000 n 0000019731 00000 n 0000019761 00000 n 0000018889 00000 n 0000016881 00000 n 0000017737 00000 n 0000019813 00000 n 0000019843 00000 n 0000019875 00000 n 0000026423 00000 n 0000026444 00000 n 0000028544 00000 n 0000028565 00000 n 0000032338 00000 n 0000032359 00000 n 0000037068 00000 n trailer << /Size 61 /Root 1 0 R /Info 2 0 R /ID [(ûŠVÿi?²yà+Y£)(ûŠVÿi?²yà+Y£)] >> startxref 40784 %%EOF simh-3.8.1/DOCS/simh_swre.pdf0000644000175000017500000030175210445054223014073 0ustar vlmvlm%PDF-1.4 %âãÏÓ 1212 0 obj <> endobj xref 1212 13 0000000016 00000 n 0000001141 00000 n 0000000570 00000 n 0000001400 00000 n 0000001656 00000 n 0000002104 00000 n 0000002635 00000 n 0000002673 00000 n 0000002896 00000 n 0000002974 00000 n 0000003203 00000 n 0000005189 00000 n 0000000935 00000 n trailer <]>> startxref 0 %%EOF 1214 0 obj<>stream xÚb```b``áf`2î1ð1  rð+ ‰2DVÙgÉÄ9 çðœå)èËç00Y£´ HÏZ ² š v@<áÚe ÍÍÀ`5rj¶WR endstream endobj 1224 0 obj<>/W[1 1 1]/Type/XRef/Index[162 1050]>>stream xÚìÑA Á4þÀ+6èÌ>ÎÀm{ÒdôKs=à!<ă‡xð[{ 6Ý endstream endobj 1213 0 obj<>>>/LastModified(D:20060614191937)/MarkInfo<>>> endobj 1215 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 1216 0 obj<> endobj 1217 0 obj<> endobj 1218 0 obj[/ICCBased 1223 0 R] endobj 1219 0 obj<> endobj 1220 0 obj<> endobj 1221 0 obj<> endobj 1222 0 obj<>stream H‰¬Wkªfþ¾¿br>a#,/Wiš&®²GZlOOzúWZØíö×wæå"ë"'½d“Pæyæö̼÷+øî»ûÅÄ™‚ ßÿ0ÀÝýÄ—a“ãúƒ|“Ü1ˆñùG|þœß=w÷A ƒ`w'K²,«x¹ºT^¾ÐkAL¦Ï¿ð.È€Y’5âË+¦$L™IŠiY&Ç;Á3ðÃãéŸîŠ—0‹`n~Ÿ£Áowvpg/ˆã…7«y¿ç¥sZDè]Atf s0Ì‘¤Ê²epŠ,þpNDE–[xÊ[Ç®Äÿ²1„—,.Š(õ+xéFÊ"ÌÔs]íaR FB´Â.‹"Hw°ÙS dÏÑD¾Àkµx+ŠR75\˜¼Â©I|ž&®‹wnÙ¾–uz%€b‚0•œ6¶}‚×[ó<Å‹MX¢[¼L±C5ª:”¤q¢ R¼Æìðé‹P .|ð‰r9÷> Á/ƒ!9»°?°è'ÍX¼x£¶ÒeÞöæ%.öé¹€,B~yÁ vCt‡h{sÀ»ó6&îÏ}´IWG#«æ]=ÄǸtž³Ä¾ÞS’¹8žs”7”Rtíu  C8¦Ûx‡‡ª&š¥:ó.6hÜÕd%(×ä›DUØaWî¬ÏÚÊ«¯7ôuTW"‘©í¶÷iyt@Éiù©7ØÌb•j[#ìáÚÓôcl0§¼œ.‰ «[róDµ_Ж# ‚o‡òZ&ÅâMžÐÔË>=rDlNð¥ÍÝ9KJ}¤âHéWyJ~#êú7ò˜»^ðÉtkØT¶œÜ–Bób­˜Þ+¬gãùšÉ*£þÕ‡uï>×AÓ­~0ÍRH‹êáB=\§D¥×øó}=Å©KyêHNt[jVVqXü"^`X_ïC¬¥5Ç¥‘ÊÙE_4à1ÕN-&íºh÷o½>71%:¯ë,(ž1ª5SZ½V¯kSºSí_çT3,Üg,«?cÝË_Ùaê¥ÙØWÀtS•eÀs:³ÁwƒOcÏLJ•çþäLí)|ûà D]ðLø0lE×´.`FÕØš†¦ëiòÉ fîShÓ/ƒÏà>Âxù~D{š°œVâ$ÖﵚU°^y¶ïƒë³XÍí,'ó§©³ühuéR€Jxù’hÆN‘¥¶HIE`î,œ½ \ O+FŽí§‹ëð‰µ7:²°½É _?8s'ø<„G' ½]’[DúYwÏ[cŽ—U„Æ(ä°{¸ë?ÍǬž¼>r}ã4mUò{ÅÚT[À…¥»t–†È^ØË@Œ ìŸðüYãigò K3jó9xîƒí´V=­%5‚ó#<ØÎñ9;·/¾Þ$Z~B*ƒÉ|ì,†0/Æmžisã¡qltŒm»ÔZd­R)ÊÈšÙüts\S„ñ$pÜ%Ïìí|Ã]ØàËÀ£w'Á$%pÑezìq[tE(HŸH^ßöMqSÒ Æš{ŽOûè¹è*5ƒûHòèrÊ75äÆ캚n67ŽvÉ´,˨O—äæÒæ^:ü†š‰Ì¸dRKÐ@äý?òè©M$›ßØÐ7ÒE’ ½™èO¾}I'Z˜Úã9F„¤ÎA¶M>е±7Óæ¦ vþöœÕz†'6S·FýG5öÿœÕT9˜­ÌŽýç&:¸üVŸGã¤t8C›í¤œ®C>ˆ’ðx¤ïX¸ÿ˜M/ó“€G†»OthŸOÚ¤}¬#8׳¯:-ömÅ%Ü›„…Û?ðLçÕ¡¡œÏRdŸ½ð%3¢5Šx–Ò’Ÿw-Qïz¯ê–Zû6¤%Òò¤VæŸvÃ.fͧ0–'¾–\mûïöã åíz\ïä'¾—[9ð’ÅEáVr.öÍúÿUní»ŒØqzÒ9µ#=µlݪå÷å…Œ¥"qjü޵ðoCì endstream endobj 1223 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj[3 0 R 4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R] endobj 3 0 obj<>/Subtype/Link/A 40 0 R>> endobj 4 0 obj<>/Subtype/Link/A 41 0 R>> endobj 5 0 obj<>/Subtype/Link/A 42 0 R>> endobj 6 0 obj<>/Subtype/Link/A 43 0 R>> endobj 7 0 obj<>/Subtype/Link/A 44 0 R>> endobj 8 0 obj<>/Subtype/Link/A 45 0 R>> endobj 9 0 obj<>/Subtype/Link/A 46 0 R>> endobj 10 0 obj<>/Subtype/Link/A 47 0 R>> endobj 11 0 obj<>/Subtype/Link/A 48 0 R>> endobj 12 0 obj<>/Subtype/Link/A 49 0 R>> endobj 13 0 obj<>/Subtype/Link/A 50 0 R>> endobj 14 0 obj<>/Subtype/Link/A 51 0 R>> endobj 15 0 obj<>/Subtype/Link/A 52 0 R>> endobj 16 0 obj<>/Subtype/Link/A 53 0 R>> endobj 17 0 obj<>/Subtype/Link/A 54 0 R>> endobj 18 0 obj<>/Subtype/Link/A 55 0 R>> endobj 19 0 obj<>/Subtype/Link/A 56 0 R>> endobj 20 0 obj<>/Subtype/Link/A 57 0 R>> endobj 21 0 obj<>/Subtype/Link/A 58 0 R>> endobj 22 0 obj<>/Subtype/Link/A 59 0 R>> endobj 23 0 obj<>/Subtype/Link/A 60 0 R>> endobj 24 0 obj<>/Subtype/Link/A 61 0 R>> endobj 25 0 obj<>/Subtype/Link/A 62 0 R>> endobj 26 0 obj<>/Subtype/Link/A 63 0 R>> endobj 27 0 obj<>/Subtype/Link/A 64 0 R>> endobj 28 0 obj<>/Subtype/Link/A 65 0 R>> endobj 29 0 obj<>/Subtype/Link/A 66 0 R>> endobj 30 0 obj<>/Subtype/Link/A 67 0 R>> endobj 31 0 obj<>/Subtype/Link/A 68 0 R>> endobj 32 0 obj<>/Subtype/Link/A 69 0 R>> endobj 33 0 obj<>/Subtype/Link/A 70 0 R>> endobj 34 0 obj<>/Subtype/Link/A 71 0 R>> endobj 35 0 obj<>/Subtype/Link/A 72 0 R>> endobj 36 0 obj<>/Subtype/Link/A 73 0 R>> endobj 37 0 obj<>/Subtype/Link/A 74 0 R>> endobj 38 0 obj<>stream H‰Ì—koÛ6†¿ØöÉf™7‘0 hâtóÚ4Æä²}ð¯È.vg-º_?](›Gæ9d–"ª‹6´Ñ¼‡âc>œ.’o¿^œÍg K¾ûîtv–œLÏJ–Üìªê?Éîfs“»êóï«ÏßíNN—'Óå’%%ãå'çË“ó‹ºtù~µÙψw3j+ò¶"·‰]œ.LªT“ÅAÖáÑD Èð^1)éÍšTÏǘ©ã›½ØÅl1É‘9*o.ƒ‰\H7/ýL¯_¾úŸ¿X?˵;ñDp6’Éø×åÈf!¦Yµ\¦Zu_žbhu ­Çi]ã§ÐÀ-z,¸<Ž>/ç“·ÈTó(ÂÊô"?àúõtÈp‹8¸ºHsYÆÂÈŠqGÄQ ÅßQÚÍ«ýxAöË˳¯uM×ßgú„uS¯‡EA•û›ÞÖL§¬zªÜb•Ø:ù{ÝVGaõ·”«h(;úè ª4:¹,§X›æþ†s8¦—>(èÌþîw„Y‰T(ƻ֬°¥ò7¼#Ì ŽÂìo.ÎÑÛ|˜Ý*×–ó²,§ãI>Êá¢8âào@^Ú9ëŒvËZQ¬E¤\‰¼“ ¬I‹H»:$Œ¥WÊ«W‡Ü*G0R° 7q0‚{’P°Z° 6Ò¯¸rC ~%" ÄQ€ý=¦Ìë_ow±È ÿ6u~~3›\eß$Wºúk°G Ä«·‰ë•`Ç0i–ÝŒ9îR1F)ËhuË©[aZ\w”ë€,Íq ›9á\GZÃèÁ:tbR®q–›”·­Ñ"ÆÚ´ I—E )Ä„uÃ*-bÍüÊU!Ñ_4ânYÄF¦¢¨^{ĘWËpuˆA"…˜0®0bP¥EŒµhõÏÒ0úKF¬B–ÕÆYV¤™Å‹í_Ò¬ö >D\eY¢gDö;³Ïýi‰K–Š“,eœ¼è9;ÇjjhÈ®ìžUYjÚ»H =yUȯºõ‰[B°ˆ[’ݳ JC:¹Âîw*Þ«x^Àì¡6m â8¹Êdu­i·‹EŒíÜ,R®`"8#äŠÉ½C7CdPÇBÎÐS%{„a9Lsã,dW:kVŠçŽ¥  2+ÃÃ(¼þshÎû>í®½ýV‚"o¶VÉjs›œßüu÷~·N~š]–ØSÄ[Vµ¼°Ð³cŽA²+‹˜©Tî7Öê²YYÄ ŒBLˆUÛ¤À‚èÅl1ÁNM¸”³U…ìe>3BÔîÞÐP6e'Xª`Ö6zë8Á:D|5%Xjß U¯}¶çÀ¡Bƒ9y=/Øœ ßê÷fã$æÌ±XCše±Ý(·h±X‡$Ë¢q^±8ÛÀÍÐÔi!ÏfKlòÐ,Ó ÿâ9‡\«mÒJ‹4ß_%3d¡LȳÚ& ÃʆÒ,o“†Ñ5XìÂk©‚MfˆÓBýMÚ„lËîfU4JÑn‹ mÔ&ä[v7ƒ8Š3á[ºÄn`]Î/rlª„T9©ªù9ׯπ9†oHµ,_™9º‘¡ÝÚ„dËòqßGÈVdÏÎÏvŸvÈló8ݪô„Ú™-ã†lN‘ÍCzeû³àÖX4¶J!¹²½yDðÌ)¹Ê¼}¹‹½nšr1­ÿÖgðá få`­DÅ i ÏH¯bÆvâ<Ò«@E–ð*l§‚ì——g/^c“%<ª·QÝÌ¡©F‚ ‰T VÒ‘ ¶à"¤R-XG€-ü}¤á*_žìÇau®G/n?¬67ëÛ¤Üþþðqu¿NÊO»‡õßã|4}µþôÛvu›\l7wÛ{l„€õ¬Z*ÑŸÁ“¿ H‹8k’9sÄA§CgM0ŽBJX“8~ nèËíýúÝýöŸÍmrººù³¢T ßêS­î½i< fAÂŒS$©µã:UÌ8E‚qÌÇ+Ìž]–Ž]̪%Žm½ uðÖÛ‚åŒ"ËYœ#ÉLXµÁÅIÒ!‰ Ê™¿}ögëK‡ óÓ‹êzÂ0àŒÐ¤Þ†ãŠ;Á“un82§!lj“”¹c#'ÎâÌ æ‘¸ý ¦‡[;eʻͻ¿ÖÉY}Š~ýj»Úì¾Fƒpª~Á5¬ó¬»;r¤D åXA%ŠóH‹yd§Q™(ü˜A¡ÙÝêÝf»{¸»I–«÷kô!âE‰WNj ÑÀ£HóH·âÜ‘!ƒºç‘ròHÒþ~Óàå‚ûOd^.Jt¶„%9©2ëE>?Ì>Û®gï1 sȺڣYÆQ™]¶tµÇ3L#!SÖ¥š fGÇG4¬òÃBÔT¹~•œ¾(çgØ#BÄŽ6³€E±1‹Y’˜Eœ‰\¦Yõ4mû+Ð5г0˜Fa¡fc‰‚ÀÅl1©Îå墬š”ÉoÜwʾ«ˆ^)Ü3D¨1ɬWùI˜-.Eã ¹”Å¥ GJ8C1¤Rm/‡q$/\¥jÙïNÙf v ãG…®GóÍÃúþv¬G«‡Uu:VƒßºÛm1ÚaY.cV¬_ô Á+j§ÆAs+‘UìÌñ/¶ ºU~”GA—„Z‡ë’_¬Ü*=â¦ú9OøHV1æ£äç7ó·(vo[µPÃÂÏH=йŒl|JÔ’q`NPµ³êL=Š$±Sž¥»#¸ìÒU©±&W9á\ý“7× zƒ7qȰ,b‘§òÀ½1Ébu|÷y$\¯Âp÷%Z²›³z„SÕd»Ü/ «û@½VÈ,„j±ì“4#{Jsí#ñŸ@ endstream endobj 39 0 obj<> endobj 40 0 obj<> endobj 41 0 obj<> endobj 42 0 obj<> endobj 43 0 obj<> endobj 44 0 obj<> endobj 45 0 obj<> endobj 46 0 obj<> endobj 47 0 obj<> endobj 48 0 obj<> endobj 49 0 obj<> endobj 50 0 obj<> endobj 51 0 obj<> endobj 52 0 obj<> endobj 53 0 obj<> endobj 54 0 obj<> endobj 55 0 obj<> endobj 56 0 obj<> endobj 57 0 obj<> endobj 58 0 obj<> endobj 59 0 obj<> endobj 60 0 obj<> endobj 61 0 obj<> endobj 62 0 obj<> endobj 63 0 obj<> endobj 64 0 obj<> endobj 65 0 obj<> endobj 66 0 obj<> endobj 67 0 obj<> endobj 68 0 obj<> endobj 69 0 obj<> endobj 70 0 obj<> endobj 71 0 obj<> endobj 72 0 obj<> endobj 73 0 obj<> endobj 74 0 obj<> endobj 75 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 76 0 obj<>stream H‰¤WksÛÆý®_±ßJ¦â‹Å³I=ãHv«LkbvÚNÝÉ@àRB `Y=âK [ÿZ¬²šjé/ ]%øSnÚ‚m4®Ò–î.Ã…*›š5ŠÕ }Ý-Wb‘ã›Þ6_’J±e°Ø%égzû^-ÿ½þábõl‘’é<”ŽOËÛ»fÉc’åÉ‚5šU„W²/Yó`Ð>Þ¼ÿ+«³¢Íšh£«š3f œclb¯ºà+Á…‡â}Rî™ÞR¼Z±Á*}BÇgµ\¹ Õ/æŻƒÇ .â0è¯X‹¼ËªXžY£ðmcîÕË•ÄÏy–ª’¾Ò/õ·l—«k¨Tbž¤ °FUEͲ2¥dÛ6¨eÐ/GD&Ýn9&] »ª¾xX„·ÐT4Û$nƒ½]_¼}OwœBÑMá锞cò£ 8 G4F ¶\ÿg,¶{[ØØ6£Ø p_„4'¦¾N0ÀñBîÈG°ÛëÛU4'OáÜ!œoádìºMrÄ4hæê€&"îѸ`o?Þ¬þ1…罬tŽä.@âÙÒù£±?Ü#Çñìö¶ËÇOJ †,I›ì;¡}}OWIÁ¶ºÂÒ¶(3Ì`šä,ÕÅ®mfw´t‹nW$ ½«KìÒ›†¥@U_UÚ6ŠÝ!´™G hVj“%¸ <_,Å")7vS–ósß#žìóÔ 6lWé{$S³O‹<ÃúL»@±ïß|¼¹ú´Ä¢úzàÉÇlƒW’záüC~BŠÐ¦·¸ÓX|­Û*Ufx7awY™Pm9¨Ða®÷¢žóª=ËuB¬‚I³Œ ¥ªV >'7i0>i£R062Š%óƒǬHîÁ ì:ZÿfdKÔ•á³YÊE¦KV+e8eÓµ0m²€vëwñÞ ˆÏkàzÜõü¾iÞR,TŸøŠÞ§ðÔ<;ŒœªÂÞïq;)?×$× ¦öïÉçúrØ®I»v:@OXñAS†_*ݨ 8—O§Í?¶?¶Ísyè=éc!·I›³ÛLu3°Î ŠýcÖ4—Œ€*•êGEÓ8VMbîD2êeÛÁÚVº`7©Ò‡ìÛ6 ÚbšÄÃ0‘ Ï硘‰I2Š¦Éˆ˜¦ëÁ<’ô¸´=3ÆC›€¢çˆi7çŸ&sŒ_œ£‹.†ç´J8§ñå I! e{<ŽÙÔˆ5޽¼Ëk›¤ª³¯D1-Âb|Ñqxà9¡?‚H¥ô=8ÁI0wÌå1[H/й „{ðŽì—õ?oß²¥vÿèÎx 9Š8ß6/Œð]øñiŽÌþû³¹ò ꓉¶Ô}`îɦ{£–áÔ1ˆÇàùîÁ.¸ì݇«7?b‡OÁ«ú9ÇàaëDg¦wF!Ž*1ƒ±“ ‚ÐÖÛ¤ÚA6ÐK\ôׯEà¯%íàè –ñB¥äŒ3ë æuÁåôÂŽ :I´½ª#mÄrLë&È¢Ðd*ð¼Ñdk(hF¶ÈÑÃYöƒxž•ãßÎÊ4È|‡ñ+»3¤ìŽËÏ<)Pßçž'ÄÁÕ±oNIyp\rÎôÍó¸€7~¢;†ˆ=š¸oFÉÆÁ.vÝÿ“‹%Ž.9b»8Áew\Ä{‹_EÓÅ/"føûçŽrîÄ9ñ7»w¹P^ïhÝM.`e²&Å?’3„W׆ÍèòžÌ:抎zwæWeì–mÐYíHWOlœ‹SK:=Y•÷¬Þ×*,(Î?GÛÎ6êQåšÎ–Ää8 üuð”yìñNª|Ì*]ôIIRת¸Ë)¥Q]Μ ÑøÀ z±>ëîß}øyýüæ§ŽÝ-o{¤,Ç™˜<½Eû2 OŽmIŒmt"Ue­.HvR#}.á‰Ôd8¾Ð‘G·ÈQQ+.!VìnÏ®³û¬`¾ýo›í ú¥¤¬veÎc;Mí¢Ëòpz¶ÞðжÈd¿^*d©Ët'¼‚$»J3à´µbt¯Ì÷¨Éëm®’Úh]¥£4]ŠD!Íu'vjÐO‹t’`ž¥æ ¢÷•2ýöR,ºÎnÛteg…À`éhŠ(%zÔ¸ÅFOs³Ó›ßUo6ÕôÆ0Ø3)¹Žáìym·P}Ÿr"‡}í`ï´nºEƃ†qÚÆ¹Ñ‹ÄOÄ!÷üsù[¨3Á#Pz`lë3+•4 «¾:L×Ñ/ÕW¾™™L9îª&0CŸKØ7SæÖ;‹:n­&P'ø7oÕô‘RDr']£Š™òú.‡ûÂ6oª{RžëÞ¬?Ž{xErüúÍÁšn6«¢(VûýtnÞï©¢¤/®Ø„ødFTunÖá(üü°Ëqް›9$U’6ôDE‚:Г!'›S¥ëñ0Ævа7n³ :ixÿtQ -Ìù´4Æs-ðŒ±„ÆîA·èÑoYâÉk=€Gk)a•³Ã;uGeJ©ä¤‚-Ì¥ÉËX9ãGwÔÒ[˜ç4.@qkžÄ*Í-q,YAøÀiŽ—/9Ïz,’Ü?ŽÞp6þ'À¦Ë'– endstream endobj 77 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 78 0 obj<>stream H‰œWmSÛHþίèªû°ò•=Ì›¤.•+‚Ù@6¼h/—bSW¶6¶ä“dﯿî&‰£$eÉÒxž~º{žîÞ¿„W¯öÏŽNÇÀáõë7ã#ØÛ?ºæ0©ñýƒzRì Èñù[|>«÷Þ${ûIÂA@r·Çç\C2Δ ’/ô£¤Áéú~K*ŠYãökï¢Y 1˜¿'˽›@0 —ãË‘n$××ûxû)y·wœìŸ‘ißÌ[s[să9Î2B e²Etw-bd‹ ç!!0HþìÚ[vî="®R÷ln8®ié´öç54ó¬åõË ðk¾Ìèfžâg5É /f0ˆ‚zS7ôf98m`U•÷Λ AôZ;œ§o‚|êöŒT)­Ço³ÁHUº„ivŸ-ÊÕrYÑ@ZL!ûJë'´~Ýä´º€¬¸Ï«² äŒ>› à;Xõ,™@ &´!#Ó»²‚´®³%’¸%ðEV áÍáõéÑСFQðëÅU‚ï¯&8'Kghb<ýôdl!EDˆ2bÒ*M±îäd>ETüá¾äͼ\7p—eC¸ÝÀ8ŸåMº€ãÿ®óÕ’¸S¤Û­å72’vm·v”T,á&8*«U¹ PŠ®*†@< ç¨Ñ¤\:·:WOr$–:â°®3(‹>Ø ·³ F?->#¹‘ šÞ•ó>äô¾¨ , ¼ƒñmÞÀi1A"óZrB‘šÒ®.ƒ¦yý¹uQòw´/_¦³ KkÜû¼È›<]ä9cñ14é*ƒº\oYL2´è¼l²Ø”ë M2£ —ÛLY®ëæé=Àý¾˜Ð¦DSµÎ&%5ÉY³YeÿÀ€>+]Ð# Z“´(Ê*‡B¹ù]kÞ¼D#Ü™XºÝù·mÝ!”œE:ŠÛ”¸ €\Vf5$Ú±^¯ðÁª¬ty®OÏNДÿŸ³¦†E~[¹3ˆ‘òÉŒòÉŒW¤dQl­íUíW˜ÿy:5´6z›e:ݦDµ.Ú“uàå¾HB‘EÊZÙK0êÞ{—_Ü…c°D(âÃDüÈp„+„‰‚÷Ä“r~EçŽ>²jÔ¤xÅ„¿-ñZ6uS¥+?÷øeÜãˆEèÕËÝü¸·ü.¸T '¾Íé"¬¶-[$+­ÕA/_·‘mêÚü‡Ž9»Í /3ëgæÁ 3¡Õº—”à=)ûSaò‡TK¦”Õ;=+×EãBš\£œPApz‡*ç ZØ*-‰%-ºú•|æ¯xY{*ÎŒ°ª¿=ò…FÅâ±±²#ÀiÓ¤“9Twm”«;6ÝÊ}—^Yòbsø0&êgÖ§KÑ·’lzc¬mÄ´5FíùÛ¸|V`iIŽ.1Øû§—ÐÊtIÁŒâ€jK‘5¨Ðnu“Yåª/?ûŠGš¬ZæEºp ;–.R/Ó9mBÆÕ“‹^– :ÖL*+#.4Mþ^9Ïüí5-P6|\¸½j `Î'éb±/eõ¹ö{¢GõJ²GÎ9´ý‘ãš™ø‘ÛcäÚÆþK¾X`Q.ÛùcFg¼*׳9äëÕê&­(ªëJ~º ê=ó;¢Gøzb«¬b*´aÿY–úe±U[>a…ú1¶ï/ÇCÿ~v9„ëäð*ÂqrôÏöÞÏ0ì4ƒ &cþ|vqžœŒÆ‡G¯`¹<˜N6›]íØ^‹õ2«rœ>p 6UZàdqë~òÛÙ-|’Y«{ZJc,>¹:8;=‡³côpÏ–Kaq°À¿GûÚצ‡unˆq·X–8Tx;íËmXD©ÿ£kÿhâIEÅj† µ&œþøÑ»žӫTÌdÄ£'´È>•¯OECâÅþ0«0êîýžGÚb< :ä;Bpýñ:9>ƒÓk_|8Âéùûï ¢%o| endstream endobj 79 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 80 0 obj<>stream H‰¤WkoÛ6ýž_A`_dÀfù)nC±.¶në´Z1` Ub­²Hr²ì×ï^R~$‘œØEXŠçžû8÷òÅùöÛoÏß¼&Œ¼|ùýësröâü=#yðty}ÆI Ï„çËîìûôìEš2ÂIzyÆHšÃªô—¦á ÿÃG-ÜP&ü.áJÇÔ*bàÆhÆàûÕYDfé?g?¤g?¼Eè9|cÎ=¸ìÄB¼‡›'’Zi­&ÚZ*àÊàþE 2[pžè(½r¤»›Ù¨ëÝŠ”©›YÝ’Öeð[ÀªèŽ\6-Éñùj•Õ›ÇN Yºžd¤([—÷M{Gª²ëK\´ÄE_“ÙßéÏcTÄ(6F"xH'†šä)ÉûÛò=q‰aÔÚQW (FSk¬²ÁO„¾#3_Dç¯ÒI.jŠËPZQË­’ ÅÓ!gœ#D¿<KʬM’Àé÷þʵd !êf 9¸hg ud¦£<«IÕ,IY“OwôºÆ÷9„t9'—­O‚ ä©«j×{—kÄÎÆAµT]P +óªW÷sÒ7’gÕl×t=ijF‘kܾíIwíòò²Dä-Áwh4¢ÞÅ‚Q¥ -z•¦¯Îò>](I9ã ' Ž%ûiúæO|ûÐ¥fK‚k‹ƒÝ ¦}A§"¤O‹¾H¨NžÈfsk±oiìhÌu0”ë-ŒÞÁÄP÷*Fð±äâõÅ‚Ïâ$âÆ©ŒNîËÇYçãd™$ û«Ù0j÷)Í‘‰þøíÍŸäC<'4ü›IìI~ã˜êxºi!e_^V±RTÀrzZ·¨š¹,[ÈêÂݸª¹vÁ¼®Q}*cÌ7Xme¿Y{[°Æy_<¬¦àe!CöåÔ]£Dk(u—-îv‹÷ݦ˜B uÄA§Ñ6ÐxrÝ67eá6¥U¼W¿h/6P8\¡GöXb™f+LZžçÊmXBG¨ñqÿº|Ý—`QxG73•mSï/Æö’u[}ªf&l† rŽfo Àv¡ýÂ-¶*Áv¿ƒ½# YÃÞõàðœ17Ueîð¶ss––Ý|–DþªYƒ¥ÞbçŒ@ϳªØŠgFΛ6èÞ"ç¹· ¶®›z1´O¿>/³ ¢æviQݧ]ìå^$L#<#iø@©rYçc¾I$öƒ€W fWöº1°Îý‹ðˆàòeëP‰áµÙ[z¹®*Ò»v…ŽÂú~Ðà·E‰„|Ç¿‚ÃT—÷6^­ñº'Ÿ ×h:)ðMz Úº—sjGtç&rèv0ä. ×w¤¹ôDF¿(»Ï¤\e›$Ù&8ÝÇ2vOÛ5¨ˆ¼½ƒÍ³ús‡íéâ‹÷óRØC…‹å– ÉpÜW·7ì¢uko2Ÿ÷{øñÞòP_ÞmîØû&/]ïɾZw}›Ue6'·We~µWÀÅVNö‡*ºcµÑÐ ôÐÜÈB&CD¡îãøg!µvÕA'õšóÑn› ½m-•;-UV€j'ÛfíÂ{s&Œˆ>ÄÓ¨G’JÅ;¬é!tˆi;#%5ÒO›ÛþD ~ ¨ú¬¬Ã|I‚‰K:±*_Óï~\ñUÒzé¨ý©izï”驚Ÿ6Š*ÈDÁmb»$~j¸žØsª`¦&P?Qwåê%éàø_¯Éš'Ó¤ô(îc”K›Ä#`Yß“ö3Ý/ÿýxl?Ó\<‰kŽÀ•I5æGßG¸>pí d艣Š4f^AûYM†ÎCEkÊÛœñª«Ãçèº>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 82 0 obj<>stream H‰´WioÛVýî_ñ€~¨HÌ[¸¶A0‰í¶n¼¶ÚNÑ Jz–8â¢!);ίŸsI¶H] G-ßýžsî›OìíÛ7Çg'Œ³wï>œ³£7Ç7œÍ < ¬˜¥G‚Exþ3ž/Š£“£7“ g‚MnÆÜâ\(6™1iÍ&÷ôG“‚ N¯ßði’ãƒÅ¥±W½s ßõ„°l/|6IŽìÓ,+Ùpòï£ÓÉÑé…² O4áí¹çä¯Ã8ç–°¹rÈø_6KÄ7¸Ô÷ìüŒØ*ÍîS6×wÑL,Ì5[®Ù²dùŠå1Ë׬LØâù×ä×CɃµE8”ëû–Ϲg×AýÀ†&ª<þ<à#þy˜Çr“F_;½ªƒ^;œyžåH.ýÚÙwµ³Nãö¾qÑÖxÆz íâÅv¹”U;úç¶éáJ3IÒëñã(¼pTé¼gEƱÎY” ½A8t Í¢‚Ͳ´ £Ÿõœe) Y™Œ3±ãÌ1Î`Üs)Á9¬¦‹Æ4»þÈ6ð¾X1zI†b.´…™ÎØ4:Ìêo—gÿü¡³œîëÊiKË–\ؽåôÎÁÎ6vWÂr½ÀÛ߉"JÞ±B—l¶Þ°ð;3ò_2}’[ÊÆ¬ð–´fœÑ¨¹ó¾ä+kŽ:w¹ ^â–ÔfÛ=àÖà ùír$øk–Ú <Ëu¹¯þ{1Ž9¾k)Ÿ+ïÿ„câU@æxŽÅmî¹lÕÙªÇÄa 뫃«,ë«öëð»™Lþdãóã÷7§ÝîìW%éHK¹Â©úú]gOa›|‚mØ„@TŽ€a~åȼ«IÛ² 0ÕHK²ëÉ£ÒåðuÈâ`5žAiáuôÜΠۋMÛ ÆT‰–Ë¥fÀäO'Ÿðo¾/èca€zk¶)tnÚ7~B „Ù¨9ošek`°ÎÃ2J¬x(JXCÀØYÉÖyvÍ5¾P Ǫ&¼Ž$”… óEc—x„þ~×Ù hoWJ¿–·\„Ì _º¹ gåÆð<ýÑ:Åk8gÓ8#û+V†S¢oMb€),Êç´Qíq2Ê<„µš>ëU1l®+T}[A%ÞØÙåÙ'úÔÄw!Cñ§!Kq~†ˆíe×—íRBM|Óyö}£•?[¸ Á±Ði±ÃMó MâÊH¤»C(H]‡ŠRù7ž”¬ïPçM¶¨Z³œ„䥦ÂG)‚jºo³$¡…¿Y×j­ƒkj@Bn¹Ø©:Ó”étÂÞâ.-ßA>Ÿ|8¿:þØØèŸTaÖTË–0–$Ænæ‘îAæá ç´÷ÇΑ®o)ÉÅ–rÌI§-å8>)£T:#è¸æº—OÈ¿ÝKîÿí%—6ôGu¼”uÎñvvTJC&;LB«Ëžc’×1³JÄÞ_#Å_Ç$R–£Ç?€ê…¨Çœ~dgVJ¼€C$Çý×E\±!.G}Éã^æR‡‰ó°W¸XL®ìN抻™K©—2—ðmš5é÷7ì0#–¼Ž<,Í•ªòÂVÔ3Vg[5æ¶—y4žm ìAµ³UTbŒÏJ¦‡coð(¿Ö³²0@Wao‹éO– Hç®Ý89‰mƒQë‡æŽ!‡RØ´k ˜JÛ² “05ô6ïï{/ÙÆáƒE©“ê-pTÒ5ãUÄY°$3KYFé.¦qH ½¢â¬ ïUÚï\XØ}ÆúrµQdo‹Å,uZY$û+¶+^Fáp¶¤ôÌõA…Àmâ°¤*ät<öãÖÕ^š;¤„&€÷ñP€“ Ȫ Žå圳û¨\Â5].ìfË?øFCAê% Še¥{ö;ÔÕ3ˆ%”où˜Ëþù?ÌV`qQ‘V= FÙ7ŽÜG<€°iXKa±á .³»Ð Úé,ŽÖ /(7v}ruÓg'oõæ(+8ˆËÿ`MNÜÇ endstream endobj 83 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 84 0 obj<>stream H‰¤Wks›Jýî_ÑßVª’03¼³·r+±œ”ïÆ¹®Xß6[[Æ`T²£üúíž Ù€m¥ò ѧO?ÎtŸßÀœ__\-À†÷ï?..àìüâÖ†¤Âôª¤|%.+tït(‘àÁ&³Ùc.Yݧ¡ A Û2 ò,e%f0]þ¯ñÇ?¼j–Ú#ßtfüŠ+´6ƒÇ¬^c¸ä 4s·ƒE\ÇðY”BÇ5‡ ©(¢ó‡A™a$´£ËéTYФ‘aH÷졤Œ”óD–ˆP U ZÝVXø]™ïˆúÜŸÜä"®t)ãÿ©.9A’\¶qmJ[¤£ÜépÐO‰Ö'%Ë7µÙ¤ñ~›çP EU¦HBÒ{iF+ôh¹Æ`ˆvÊsKDËwÜ=ÑmeˆÑÀkO\(¶U w]lLgº%x¡S:У$Dx³{¡|ŽB ïuÜt½hFÕÈ lC$u½Ãïâòv³„‹Xåð‰ÂMÞf‚:.ÎÙd6Ê›[‘ï²–76¢ÊâZ ÇyN$’ZbÍ?®%¶¨ß´è%Š–ļE>®ã¦ZÑ÷†zóYÁްaÌŠBí»õqM^%uÛjûR<—5&Æìåòìòšú Ú¬ícQ&h[cö¨®Ù 9+ð¢È#áèŽî1Ï{ÍÏŸµ#cã`Žmq'Š•_J¸“²n»AmKSAÔ/xOôIùß ²vNfÍB+^bí›gÖÌ!aûv€9FI6DQjx¹“*+ÞC\×nlP©¬þ›:ÌJ±.‡z½. âz‘où®8=¸•¨¡®3Hãj">„è¿1ô,Ç·y؃¨“KT‡°‚^,l¹ÈMŸ¸xoûmx?]}¹üúáúòO˜jôï“D}Ÿ†½°ããùÜòCf?‰ìâÃò¾OŠâzc„=f9.È]àåÕ5¯×ïŠâ]Up{=,ö©ñvuÍãÁ§ÛåÛPÃ0öFjÛsî»yVÕçbÐx¿½¢Û=F|Ÿ³h´ÛY¿š¼Yä<Ûó‚· {…È]&y¶©Ä°Î1÷Tên„Ç5çlœy¿Ê¼FèÜ[aÈü`Dè~‘Òިα7ÊŽ0Ës¹íž.t¬_}†!}<¼lî²S”ŽõkÎ˭纡ú<ˆN“:Ö/6/X^È÷÷µŽ÷KÎ04÷-îò€ý¾Úñ~1zEÈ™kE¶cG/¨çoäf;–‡³³ÿµã'Ï6N„­a›ä tO…wÅ.Ò –Ç‚Fêü=”ßB¹àà‰Ø ¸p³¸™³A°'úâtÁ¸C¯Í‚Áœœ ˜¾jÀÇ šõX‹‡oM¾\ÝÞ BçCÕp<ˆÁȉñt•ÂAW«¨i5CK3ÊhK„¬Ä1Nê Š‡)íwúÉF ü_Ÿ!t€|ɪMg‹aÃÊiö'š Û…hqŒËÕ³£]Õ šø)’-®Uw´ÉÐV†‹¡ñ ´q…~;ÅGÝeµ{Bíî¤Q6E:«Z¶[®gfcs'r¥âfc­Ð¥åš– üm¬êíÝ %¨Vzo¥«ÝxÍA:¼Â¡Ì öÜulcED@±Áý ÉôŸfK”ÉØÞæX œ°5µ-DYëuÏx—äÛ?Òc†KÈÜñ,×ó°ÓŽKr7Àº¦â‚æƒvô¶YyOŽ«Âãß\RvRJWV®°hèäFh#/܃:0®EÚÊZ:5½MáÁàOçþäz‡1‰ËÔnt½-Èæ¶®’õ וÄj þ|rTÇ4gz’ðY***¤ôY Q ƒQï6ѬFæp¯d1¾Ú|>™qœ¿"(^TÀ)ª­®ä8Ó£q¢¸ms¸Nþ’¥Ž°A\㥠p愌¯Ò9Té âódÞgeœC*î¨nWDš …2IÕ]a¡*WØ+šO4Ó†5““f97²É»²¹X,‡±£“T“£jâ”4>l:öˆh>k” ÛÇÛö»ªI„2”&Ó'GÚ4ÚIy ¢[aàÌ÷"z³8¤¸ë‡×4‹ûÖG<£šM{VT&Õ®¸“9¡é›¬#¡Ñ÷ ÕÛ&¾Ëò¬Ît½u´«8êClCmBbÙ4 éV£¨X¤ÌÇJä%òöJTëŠî ‹1‰škœß‹ ){Ÿ®às--éX³Ûú_ ÷(A*Öª‚©Ae!…à* ʉțf×ìJ¬•ݤíãiºRª`‡TÁ,à–‹…北0ÿíá ÃdQÔÅÓ] ‚9Ã:àîuÀ–†ãlÀuIS³ TQ€Û«ëi4 )ØÃq>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 86 0 obj<>stream H‰œWïoã6ýž¿‚÷Í.b…¤$J*z dãd»— 6‡¸(Íá Øt¢[KÊI´S÷¯¿7”䩨t³‹$²$Ïã ‡ï½9»e?ýtvsñyÊ8ûðáãô‚œ]Üq6¯qƒþ³z^œ–áþ'ܬO>ÎNÎf3Λ-O&Üãœû¸ž3{-®_苳š NÿÀ§Y…—6fs¥B/ X„‘âßÊO~ÕY¾^¥FÃÑ‚ÝNo'±ÇØUY±¼¬4ËŠeYå©ÉJ›‹ô”D].ºškûÝY–Sä/™1 L¥çåFWÈŒpêrMo.«2g™©“÷I$¢É¢Å°Yø‘h Òjþ”mÒËõ"Ôűa.g'—7Ô>û–]Kµ '$Ný¡"åÉ0IbêˆÏþÛ[öÆî-–F^ä'¸²7+ÙªL¨¢m½j]°»Ï7ñÎäü÷%úžŒÞJ.8Ž-’>!8‚Ò bîÇMJl<‘ÈÎÔ‡&9\Å^E=âH*ìv ùÔÎnØléD„”ÆñÈfõ’â ×lš=f&]£»üß:{&ÆgeõLêQ¡]G’ÜK)[6|-aBËøm´Ì§ª ËS°^]ÍÖ¦¬ÒG=y°·À±%þ>ãGWˆ\<Úw¶5ÔLò±AF¦ºÎ ze_‡5 º'Þ)’„òœX|ý+»'£)­ŸÒÞ)ÖKÉðŠyªNS9’Ñ…IŸIU“o¶Ñõ)nÿaÉýV'•”;Ì ÉÓ&#u¤º$´ÐLƒ$I7 ‚H¥cMïlôªDÍ»¯˜´ $ŽºØdUIÔk7‰r½²»d*äÜÀ‚–-tZ×:XmÙÊÞ\ÓŸÇÃ5ÔCº&C/ d4ùÑžéúmU.u]—Ô¼ý½@†…+ÕÕ§¢ø™: Za­_¨øøYŽý‘ÞTú”•K«½n1Ÿøà€«;*É/ [Õ™ÙR„¯•¦äÊ‚dÝÚtøx 6O+½„_xnº·vϪM×á¶rˆÓ4Ü?hÝD¢Ñe”2„C}—ëbN‰:Ù±ÓNÉ;ÔåÒØƒf;‹0OÇrd·.]åem›lÒa» |ùnuZÕ,ÇKØW›k=¬4•šjÙºš-}¶åÍþ´žWÒoÞ"· ,¹ 3µè·ýŽÔùDnqâ4& y»­‰Þ—¡^ oâgØïÞ4'A(<s"{ìBj +P•‘l¡çHÏ[˜gw†ýæÁì'8ò<ä…ñ1倲çOàßã"{ÜéÅÚ,ɉHnc!â÷¥@Dž" ¾Ï(‰¤ìÍÖá`ÕX¼q8$«uÞHÊO0¥”­ÁøtîDzå1üC$.ÏЍ9†j‡¥°0¨¨33Ê2`rF¿q\°²×¨r µj±Ûªù±‡õ»úzqþʼnø¾1ė܋߰$r<ñú\  ðÄ ¤}zÐ&ÆFý2ëŸ —­P Kde\Y+AZb`Wôü wŠlޱs^ÈÊ¡i¢•2;´¶R–“![RÁ¢SE(þgˆÓ˜X€þ¤kNÖÆ ÌC 7•åí|KŠb lÐÙ2Ï­ãXXwÙº™ïHºÓ£¼!eCŽ„ÝVî[£Ù¸úx~÷ùâ~ŒÕغPQÈ.ÒóÃ5le¬Tí„›¥ì‚)¤‘HFWTÃ<5Q’}ë°ôžH’]Œg8Æjb}c–ÃÝ’ABÌ-‰ûA˜×ZF&F %ǥثó».a¬^ÈÁ¤ï³´ö…Ïz݉ˆ=‘D;¿e“ËòÆôy°üŒí%rAqˆß®¾â9•B·RÊÐu|ú{ÚOP?^‚¶ÖIÙ/Qoèdê’}Ri]–8 "ô27“Ë~µdØ=”àrÚÉïRÙ/Sn˜@€Ë…hÉý0û×íe‹su÷ÏÙýHÞOÿæÆs*ÕPn>÷„*8®ìßþa9%wáùýâõꉎ¥ä¢i—\ ã‹^ñØýŽz]Ò!î%mÏ(™L 5£óÅ&-æ8ewÖ WšÝí§Î¤‹X.Òói”iµ°4€iÄæ.Òû¼µˆBOÅ]…\õ²Ö¯F* +µÓ£hÇV§½›1jƒúüU‚ÙJà”Þ¡‚ØÑÊ‚f^•µÑG;í`æMÂxÇÓÀÅ¡z^‘ÞÕÑY¢ˆüA²öNId«Ìdºn(ÿfËöÒ2M¡#ì“{ëVbóì0ŽÇÏV‘vm@„aËØèêÔ Cçùôîî k¹¾ñލZÛ ñOï ›g×7¨½[„ü÷kB&žRoÉ߯pm }â°D1A"Ü’æÄ‚Ïjôµ°²Æþ¦,V[06,ö‰ µÊþÐØu£QjŒr¡i·Êb ê}¥àŠøƒÃ¥èWÄEƨ` XÂ/ó ©µUDjh“ÿ‡²EdìΧ?”<–KýS&ÿ`ÞêN endstream endobj 87 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 88 0 obj<>stream H‰¤WmoÛÈþî_±îuÖû¾ÜkÀ¶ì4m|1b!À!)4¹’ÙH¤@Röé~ýÍ,eË‘Eº’$¢6ÔÌÎÌ3Ï:ád<9bdœ’!£Œ EÆ÷øƒqM8ÃÏ?áÛ¸‚/”‰`«}Š%uŠXøb c`e~ô5’ÁóØDË$#Í­'‹Ž8ŒðÁãC5lx^xrSÂgÙÔM•,H^4%Y.Ú×tT †""s?ÇwªÕ/dðßñ¿ÎÇGç—Ü&`þðv@÷Ï®l4\™1MŒsTHç,Þ:"ƒñÿvY?Zæ­å%.1aÛö9ãÔYCLS圖ÁAÏß‘f$óéë%­ò9±ÖHÛå[vEõÜë&*k¨’NŽQ©–CTŒóÂ2Š:îTÖoå28+â‰cш LËØ!v2Æ|9Ú¨†ú³¨ ¥Üö­6ÄÚ Ê ·?'õA‘†2Ê'«€¬Ð$Ë*_×´º;‘/`DKª´S®7›¦'›b‘Øåªí £Bv•Þê /¾j#Mø³‡ ƒÝ2ˆ#l˜vF’ùŒ L”–Uõ˜¡f'=-cΉdTAý{S¿¢m¸£ÆÆë¾ mãÿ ‹´Ë•ÛéŠSî¬ÙÈO,¦R:Ù†ruö aÐÊtyâìМig¨°NöæŒw°ÂHò Žl7Žt¬¨ÕNlãèì64Sš&ƒg?I–³†Ìòè¹Ê6*_ ß›ð¼B>^1GªdÀ£ö ¥ccÌŒW@ßpl”%ߢ«‹FWCߤ¬à×\oιþ6èáoqpf­¤Â½”Yy8µÀâ±7p¬=$nÑ,¯“›ö^‡[µšÝ¾4ÀV;®·}9â‹~W=×;é(sŽõ³ï£7þ—²—ÂR#W[¸¼,—EÀt’qÝ%EŠ–¡D ×夹O*O®Wu;.ZŸúÑ%w<ø´¦ŒàƒÂÏ–£ó³&„æsTÓåAÊ‚„ÿBùñx¸,ò†°€L—šÊᢼ?ɯ LÍ4•66fƒš¤i’ô–d ÌÆºæúw)¾Ó¬YtºwÀG9I%T±_?up&Ò>Ròn쨘Sëbm·°óyÙÒÐ ÔF"ÐLÐÝ5*ñ¥*Ë€¾cÝÏ2âZD©Ä7%¬ ¼.G‡C¥,U6fýs[ìV†ÛRF»žzIp©­s[õC­FçP¦Ð–8_îóÙ,È’AÀÀ :(—Mw –rJ(äq¦ûã7¯¨!—Ô¸XµþÏ嵃©G¾èSÈ]§?»çˆPLP©ã5R~ê´éæ0ê^HQWü_ÙŒEc¹äk„ÈkR`3ß“ ñ‘d+2!‘–s'ø^‚súä³OË)JšüO|5{|‰GAéd5ì’(‰gKüoßÍ r_Q÷$c‘mÿ)ùá’ÚP ÃcÍ}#è%XYu„ŸÎ©h–× ©a<ÂÈËü]ž‚¸Ë+Ÿ6eµêŽx7OõÜBi nhoñaç-’»$Ÿ¡FÁR̬@çv˱ž HIcç(åóÎ ¬ãOê:Ÿs_4=ØW˜I!¨6LÆí®Ï>]‚oíÜ®JÔ! (Ò¤ÉAmT~Š—9DºIø®anŠ~èí–nûleŠI³Íæe˜Pدi2{ò8-@KÁá²±::“z§@ñ°” Öðõjöî÷‹Og'ß&³f^fžüš¤hªr6üú®Óå¾D%$§JˆÖcðG¾¸“NóûÒp ÓîK?»:'âïâÍß:=ìË3‚ÁP„Öl'°Â>í.ˆÞÓ8w Ô-dî´»Er[±‡'ú("n}…§Ð°0‚–]ó…¡6¿è$ Û]öãZçCï?!¤ÖMÐÑüø¢ò¬`IÌÈi’~_?v]ü º zA Ö¿Ÿ©ÝzjøŒJùÎMfí :"†÷×¹‡µ²[h I‚UTGYXG79ÄÑj #œ+?fôUZ IK¦ls¡P(•G®‚ó AV6ùÜ“yYäM 檠¶p¥º] 9œh¼ ¨­ Ê'_­'jš·A?XrÊ•³­ý ^-ÂpŒÊ%<Á%“öãæñæð¹DÅcØþŸ.afâ›ÿ@èdQ•Ó*™ƒì Þ‡O݇eQPgs1â>q¹‚p’â{Mš’Œ’»<#ï1Éu}ïóâ ¹¿-Éâñžw°›ä™3ê©+óè Éøb~p‚––·Gç¸Ù€9Oò9F9õ5䜌®¯¡Ž§´sÔ©Qýv/$"°¼z·ÄݼæÝZþüZ6 ¨Wå²"‹w—W%(.¬–±!ó%ˆ¡ÛäÔ™,ñj(† v[%Dz „ò‰¿›\Ôh;-[ýÌ7«…ÿÖ¶¥á ^…¤_( Ô%OÊ÷d'mùÖn~¬6:lqYdǧP²Ý¯Û QJòI÷-¾µÑÞ› žo@Ž Ô´*+ýzË/¸ô–U w øû€¿ü©Q ¥ß=hXlÔÖš› ›€ÔÕŽþ`y(eR endstream endobj 89 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 9>> endobj 90 0 obj<>stream H‰¬WmoÛ6þî_A`ý 5Í÷—" ÄiÑ¡AƒÆ(0C¡HJ¢Õ– In–þú)ÇN]“^œ¡¨EÓÊïî¹çŽ/ÑÑÑøâìýt||:9CƒñÙAY îj³j@Q ûï`ÿ¶œNãé” Š¦7‚¦¼5½w¯N[D‰{þp[ |Á„y+ýJIlÒðE+Bàïçƒ §Χƒó çzsúxœŸÜÀ!Ò9õKj©ó½Û‘²3n­v޾$ÓÍê4Gi5”IŽše…N&WWã·§oÐð¯é»NÁvžbtÀ±ÑX›}òp„Lì2n8¶ŽBJ+̤µ¦l„†#JJ>¸ðº»- Â6·(Ü¢u)¬º®áYwmפ TV]–‹þ5™4ÃKм˜»wš‡p^D(/Á”(5^DS"6KŸ¤„r—ômã”PŸ n ;ÖÊ>çm9?î«ÙˆÊ%nÊ9ÒZqò­Â¥„$¼o!£!éŽ)Ý'XÐUÎþ¬—ÞaOŒ(íl0L$7Ö2D̗ÑNZ(MiÚÁ‡Ëžë×&C“¸N™¹–Hó"GC•duÓ¬3ÔÍ`'Ò+””iáA-×q𤇷Œ4SmVøò-SüƒYÐÛé‹bjµÚÈWšcÃ-÷iO.ÏÞ Í¹ÚMŽ{s¦(ÖÚ²=9 °×¦%{$É(’¤$Ž„Ù6’.êeÕù–yëY´¸}$Öz «*Ÿ¦Ù·Û^ósÈÃfô‹sîœI&瀿³.â.çémœÍÊ1:šœ»}ÇÜhù¸YvˆD(Ë*7XÛ½YU/@"Ó@ÞFË Ó®K³;”wèæšÊ¯œ}Ãy·:ßM³ö–Ü(j7¡ÊÆ Xh£ÅVNÚ¶¼]4EÓ³K(÷øý%ZÔ €¨v8rd„¦Å¬*:4+Û®¨ÖCÝÔ‡Z[du³¿hæåP'U:{†Â@ØÍ¥û€ ,žÞ—3F‚00‰¤%¶OT—¤ u]IÑ‘ÏËoÇ@Pv2qOë§€² îaQfélö€îëæ[w»5f4Bw<Êâ Ø­Ÿ9©„4XqK¶EÞU—B Ò"\•³YYT-i¿´D8‚ñÐrÀˆ £Ø/JJ=0-ôÖœ{‰êp\À•ÞXVyŒ‡qSÂÅIâ²K¼€Ó˜ä#hÏ9'h2=!h$‚¾žKjLÀŠÓß¡^½ûðñd4Ξkœ, í¹&yÛÿÌ‚”,øsP‹!Õàø«+þQ:ëæu^ ß6ìšz6úrôa¡@Í H:C¥Š×< ¢\Ħ-L\ÓQ+±fTmµ…ŸóÐOf¶[ÂÌöüÝ.êÃã¾ìî‚°ê ØS#”Têx ôá°§šcc«Q~ºo” óLÜb0eL°ø(Z¤FÂÅx’ü/£œrƒ¥` å!LÈ-áOÎ&=00÷M8wãÁŸÌ¯V) 0¡ éKa&¯FP¿P."r)‚Aë?Iò¿ URßG endstream endobj 91 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 10>> endobj 92 0 obj<>stream H‰¤WkoÛÈýî_q‘O`Ñ3Ãw»XÞnÚ¸l¡E°)´8’ÙP$—«Þ_ßs‡’,+ ö€ÌÇð¾ï¹ç^|¡Ÿ~º¸¾ü4%Aïßœ^ÒÙÅå yƒüGͼ8“”áùßð|Ùœ}œ]Ìf‚$Íg‚fsšGåÑlÍÌ’‚ÿÿ»YG(#«¿ |'ö(ÄM)«³_GÓ›»‰ô)k(¡q0Z•µ¦jìÊñDŽò¬yиIéQ×MVâ² rA¦w櫚ñÄe®–Ô>hÿgö÷3c˜ðžmd•ï* %[—BõíÏÁ2ë±;¢Û/¸KÊÚq4j¨yjZ½¢¯5›ò˜ÍµCtý%Iñ½¡¶Ü¨Ú‰HGzqØ ÿ%)¶ÆÑ—n‘äç´~(©Öó¾ôNm|_ÔlÄŠ¦W—m¾kJŒhã„+Ÿ½Ù_GEJ©N­n®SzÀE¹f{ 6ÍË¿Fq;ŽáÛDá\Ki6GÍwÊV Þ/ucô"âc9¢gurOß'ÖøåÂÁÙ”¶³²AÉȉ›YÃײ3J EíI B¢á»Q¼…‘U7ž„ÜpR ã8“‡ºqÈ‘ ·ŸSS1Ì Œ-žLYõ½”¦µnÐd¶êßV"tZ{Éš™¦ÚõÜ:Ës3£¶c»ª3ôhÙµöì½ s¼X9a$ʼn֋ޞ>/*{"0 6ó/õÁª*>ªj@~Ö*DÜàÕ?gW·4ý0»¢o£ëë‹éôâë×ocšXÙˆxm½x> Þ±f9ÌxÍñ¼Ð‘JDÑA½\-³TMšpkkxËwO:}AuMK÷h~Ío×ÌgtA¡àz POqŒ߯ ‰&#¸ñQtøu××ZÖ6¸ÚÒЊ+1å] àGkZfRéÓ?ý±œ0¼=ŽÊ3uQÖ4/ÙB ™ÌˆaÆ-Ø÷²` þÐÆBâS›<“²bžwüNÛ1LYõT(14:A\ÝS] ÃH­j¤ë¸žT›lÒ'ƒ-¶ÍXák¸‰­’6+ »›ÇáÔ¢U Ak6T™îŽiÅn²È–]}B¯ÿ ½nŒÆTJªÞÛczSÍëÛæ&išlY¬tÚ³š`ÁávC4-ÆT0œáu÷3lšvË¡'Øé öôx18\ï1HöÄ—áèÓÇk @!ÉîÔ¾z? cèû àõŠÍÕF±Îvf@µËÑ]V,sM—IÒ»”IѼ³aåmѾ¸§v׳$<:ÊuzE*F“FBlœ¼)ÀJ“šQޏoþå‡ Ú~1kàü»Ù÷–ÄXÒ0·ç³‹±;êûÊ%¯€ cyI¯† 5I<ÔTá@WÌtʨ4Gh_³‡Zë=0ŒŸÁÐe—Dƒ<¥¦0¾IVU>ŽFº!¶©ff­±ˆF£È¥ËçÈ” a2ågJ½vÖo¤Âaá›vÏ8ð¿ÍÛ2¯œyÊs ÓJ'¦ÓÍŒïÄá£TÏ¿SÙÓÕ0¬êŽ6/«L¿4 égÃ̃Šo7ë5°¬Üð(¶Ã 7ëAçyi×»_®>¾¡ßÜ~ž¾;1žÒ |³¨ûj‚¬”ÏË“RÃu3@Õ?Ž TzŽÂ™ ɬ¤®Ñð‘Ù þ¡?™tsŒ)ü/nŸ´9ç^ÄnPåOèX¾ÖµYjW} &‡v(6C)4DrÓ09IÚ~cÒËŒ…hy ”I¨Õÿùž)²\Ÿã¶^eÌÍ“8pÿD…^çæ+íоÊ}®' ºM¶ZwˆÀº?´m‚Òbý«2ÍIJªå‡@cŠ]ŸóËŸ$ûêü6Ë‹ «;ïsؘß÷eC¯¶‹~Ÿ›22û‚e/´óoíØë+´Ï2¿e˜LÊ>Édè;˜fr;ËM³dY”M›ÍiÌSm–TÚ®?z8JÀ—81ÃŽÈ>·´Oz‘ãƒúl–>ÞõšrÑì×I­M¬×uÖê®âj[ëšÛ¹|ÌL¤S \`—Im?0„øfÍ soχg]p¬ö™$3ÁhxfÁÙ(°øá^`°_HžAõ”§4§{™o¨M*Ã3'·þ}?“[®ˆ¹„6ɞǰÜ3Ä7íà¢÷ä®L‹c¸ë:ãXö¿oyCÒ,Áã%‡˜›½4±6Ö`ï2ý¿¤E]®(¹ç×è ´™l•¿h|ÊM2¸W?¿wŒ%õ|*š27Nï h·¥ñWJ°³á×S]æ|ò1K fÀ2lrUÂ_ê| DÞ¶5I,(ëÌÏsí]cí¯:(–Žã-½Å\¨»0oœìYö¦U—'m¹ÅE~ô€é9¡ÎÈŸìŒÊóÞ0v‚#Sñÿ ^}‚³ endstream endobj 93 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 11>> endobj 94 0 obj<>stream H‰¤WÙnÛÈ}÷W4ò$V§7nƒI[6nf`'ÆHÄ÷¢Ú’-p±âûõ·ªII^ØtäA¢É>µœªSõþŠüùçûËé_g„‘OϦäèýtÆHRÁüGª¤8â$…çÿçwÕÑéüèý|Î'óÛ£ £ŒqIæ ¼;ßàóŠp†×ÿá£~P&ìYíïÑH‘€sª‚( É"㉈"5ªÒü#‰ë:NV$¯9áŠñeßѼ&ãÿÎÿ>:Ÿ_¢•{ËùÖò'–9`£p¦ôܰٺ&º,M™¥UMë_n`q°†4d,P=À cjë­ H>â-è;Ðߢˆ`ó}ǪÞcmJ™ÂÃí­À¼:`à"BˆÖ©ùJphìšñ„²¸6%Ù¤YFVqVÃ]½"ÍÈ¢¤¤„œÔ¤^¥Y›>)¬ól«WK”äȤ% ÔÇäÁ4$‰ R隘uš±7**8©Ä»æn·p–è®Õx"áT€?GÉJW$Æ–m¼[ƒ…'[Ì ªD"r›òº"éœ %¨ÜÒÁ™ ¿—`jîùáœqÉ(âÄ—œzŒãk˜¨á òŽ¿æyQ´#à5¦'‰›J“•Ž—iq±4d]¦EMn!k¢ÖUMʦ€¬¹8üV1¡ñ¢Ïm޾Ô+]nR°¡è ÁŽ–9&Iš’4E¦+L².5‰á¿#`áF0(JÁöLlÑWÎG½g E"à®LË0’Ä‹ê)!»dgoËw*â…>ÌZÒæ{6;…Ï!Ëàè.ÛƬIzK ~¬·Êo©kÔzIÝ ¶¿Ã:éÝ:(ú’…ƒ çý=ô÷\ö%õ ªÁÞåéK—¯NPñÀ%Ëâ$® Ðq4U×é«ßå[|÷å¡ ‡«›«á»b4bR†{ßÏ^úþ^뻬ÞU01÷º„ü/íÍ€ßÞ[ü…¬Þ°ßolkÖoR!W{¿Ï_úý åÆÒüá%ωٵ›Ä`«iô@‚ÃZ{æCsS¯q?Ð_Î÷J(] lÁT¤hÀïDøz¥QÜP€[­´i½$·¥É[y„xXM¾©‘ÔdŠ úåóÍø˜Ôýj, àvbܪ}ƒ2gtJ§Øƒv o㼨qSßÁ[qŠ¢lùgY¯uz봿ÓŒ·ZÜ">’âï#‹S­Óq0*P÷AïK|ÅýHÖ_„ªT´óÁÜ’E\¥ 1½úJpÖ,LUãIÉP[x¦âqʨ˜¤0æÚÌÙ»vPžGCÞ À£€J2»š¹¨"˜›…Ì=Ÿ)è ÙÑ wó°Í»ãt¨v³„÷hú3·8cmP¢1Ç›2­u³ÆènPº×¥¹O—HÐ¥%èâLWq™aÞðA…o~Ùì<!\!qÚËA†£W£á¶1±bO&/‚ògv¿ LµÆé“S³øƒ§oO¿{*vOKè]0¨bUÀ# O]Q°ð¯Kè_ÉÏ'ÜövÖðˆwòô #tÜÆé­›…ßÍò…É€çW¥¹+ã<·uˆIfU­ówÇ$ÆÑ+Oëô~[“ÏʲxRŽqUé|±KaIZ\mw7¸‰‘p´^ƵÞNäöðk¡9ô|爨ô½;5¶‘Ÿ…Ù–Z¹m:ÐÓô¤UjŠ8#'mH‰Y œèß~^+)JÊ¢.·nõ/¬V2à¶tíü>š‚:tˆã¶¦p‚Ã~±nÓ …„‚-š4ð”·ä ä)¾7pÖ8‚èeŠËÚÍn ¦FÿŠóGÉÙ÷fÐ-8óûhá&µ¯jPÖ•ÎàËÌÐj]Ù, Ñu²5ë °g¸ZžÔxÒÍd½mK!a~ƒ=fЦõ—ǰ.\ u ÀÉaºc‹û²8ÑK’ †Ùv;Í‹ê79ü·‘C…¶ÃÈarô¯JóOwº hÒØ =|u#ÀPo7ܧè¸i ”u³n«áfìvò÷¶¥\øTùl»_Àj:ÁE¦EÞ‚M¾€"QM «Ç0m•Ù¬t™»M \êbœ¹ª:.j̪ӴþeËa÷(œu³0,6.Ên,¼ˆ:óªô×cÃ!L)k]ê¬ë¼Y§Éi²Is˜Æ$ ÷ºqÆÖÖ4kÚ—5¶©?È9Ö“›’² Ãýs@£EÜ–3䄌mÊ>tדåÒ Ù¿œ €¡”}!¢®ãÇözJ>S(ÏdÙ°á‡ê\Nr³´¢Ì€6ômeÿFæ0Èh!÷]Û› õ)—y\þ$`Í7Sh·ý»ÛkÙðBªBɼm6¦Ï²15ùÛ›Ö¹•9!U@“ïÀt þO㲄®‹Í£¨Kƒ¥“ŽíCièßâVHv}ØàVÌЊ —d2„¦8aû›§V(X”pdúa;Ÿ¿)¶0Mï¨]K†\?¤q úy¨|é°ámÈÐu ßOK>'nW|t°……jG¹³g”;KQ»˜ª¿Ý 0ŽG°tøžräú w†r`ùf¥¥ÃMW|‘WýMÐaHQ%ç¹ ¹îµÄ*B…Ó‚PÇ$ÓÅhüò'Áeý½Òa™JBŸì·ìüóXvã(évÂsÒ¹çSO¹ºáÅôp/L¼´[Y'“Nà7µ@®<èG>N{-?=ãã§8«Ý˜ýýÏ%%D ¶•G.ŸA]6Y®³7\"¿½ ÷/§çàKs¯íp|jçÆ>¤ÑqΩP!wâÎãÎØÆªŠ|Ó¥Èï!].ŠhÄBÏQj—¶Ô,þïëá/„‰Û#G%]~Þâ~nr]¦‰Ô;d¬ @SÃH90¿m1_Nÿ`ø ëå endstream endobj 95 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 12>> endobj 96 0 obj<>stream H‰¤WïoÛFýî¿bQ €„³ÖÜ\.‹¦Ë6Ò4q"X2œ¤éšZÙŒ%RGRçúþú›Y’’¬ré0S¢È}³3oÞ¼=™_=¹<{sN<òÛoãó3rtr6õH\À ü#Eœ1’Àý×pÿ®8ÏŽNf302[<êyLY ÏÎñ…YA˜‡×ÿá­¾PÛµªOʧ¡$cTa¨Éluôç€ G< åöúáê5yEÎÍ"I ù'wIJ&Y’–dø×죋ÙÑÅ%ƺ‹Ÿ5ñ?‹Ïîy”IOøíà€žlÒøžœEù܉È{ *­©ö¼@¶#^╉æÝ€¢SXsï@ês7‰&S2´˜¯êëts[æQìάl…õ: •¢L{Ê‘Þév;-£øÁädj–¦Ûï“c_R-|o‡½AXS’›,Ÿ¯¢üÁ ªú€JA}9êŠ7yRò‰ì ú N™bAÐŽøù ?›<#Q:'§s7›tPîQ­™p¤öótôEB…ßÅc/¤¾dvÛÀâY´6¿áìk«x})뇚B“xެ^žÝŸ¯?’OŸ>‘¼)BP!‹<[‘h>Ïí'y¬IV|º%«fùZQ­¸t0íÝÙiW„Çä:MJòñ‘~Sœ}”Î|ЮÅË™¼jâ´Ò÷#‰lWFG€JR.¹p”z?‘ÿ ðÇòØ®¤Ž0}h»&ÆÙµò¾³ T;ÅÙ‘ò²KãXe…¾£¾À×®X.c™>$k«ãe”>TA9ãiÝDÁå|;Ç DR 5Œ§ú4&CÆB= c8Å:ŠMýÕ‰³ŽaËúˆ²Ï`¼k¡ƒŽ@®š@®ÌcI©îv'¥]£»tÌ ¨ò¤K¨ëJ]Vª ku.£lNÊθÚ¼=$*Ê• jò@^¿p®çMsÁÊMQE–“ÓQ¶69Æñe°ŒnÍ’ÀM9šƒ, Ì”M´´]Ïw4o×G|ZÒû^m¸¨ãM|·ÈÞcò/ eDJr󯛢܋õö‰D$ÎÒ¢Œ:*ï#÷2_¡}YS›Ë:*ÕD%ꤤ›Õ-ø(GXÉ7’MôÎØúH¼TÔo$ž7 šØÞ¤só7Tðß]5ê£ÙÒ÷h¨|ÑÔHWˆB´qh¼M€»KRÅa¬UØ¢®\kìñ3~81û®ä`G´buƒÀU˜£úÙ¢©þxT·G]}óªßÇñJ¦àÒ˜{Ñ”B÷*~q•žš¦qa6ˆç£U6O $ë_ÞÃŽhÆ|—I–RBÞg¥)~q‡ÓÇ ‹PRÁá08'`…ϲÕ:Ê f~“šÿ h9¡ûH©Ð0u¸N8=âÃÁ*ßqщ/úH¥UR{a;þ ñqÓKä_y¥äÔ ÜG …ò¨ð´TíÀ× ð]n¢Õðì>j'¤íÑÚe¼Ûñ=¢És` ·æ £L…#Ùo\BýŸÓQå>‚'¸¢°éFð¤Wõ›ï7ýÌZ™´,ÜpZǶ¾%vøÁÀ”k]ŸîÇ:¡Z—Á:žçãâö# ™Û Ó@iU3ivoÈt2%s?â>Û,aj¨ ò¼‚W{LÊ{°EäÃÕkb§ Ûbmb»ý ’íЂsŸ’ËÐgó£A²x"÷%!%E™åÑ!%nÓÛ¾È­Ï á û´¯ý9€èÖyv—G«mTűµi¦oÕîÅ.ÎìöGB¢L×£L ô_¸d³£‹÷ç$Žòùñv€­q h"†þÀäÃBÈ‚®¬©/xØÄ¥°À’ˆë<&Ã`€7îpßpÍ;c3WÃl„˜¿M¼<ø4ÚÉ¥ÀÍ%§¡öaÈ„4ÐZ½@*ÝA*µ+«îÆR0°VõP˜+ ³‚­Ý‚Õ, frŒ\«+zLÖK<”5‹l“Çžöä֔Ƥö7x~fôÃø[ùúyno o+Bt’ÁÛ#Áù’Á*9XÖõj.YD+csäj'8 ­FWÑ’hQÉíAH0þqK È»÷øS³<¤_/‘ò1­!iUân—¦#– ‚Õ«ù—!Lø*± ªRDÚk6Ž)8}ØL„AEÚeR”IzG ©ßMfj›Ëõ&òÇ÷ضFvûÕàáH JØ!~~ \R,/9;ŸÛð„ Ï7]|]|vÐK̲°ªÕÉdéý(“-ç8jwJ®ß¿ùLA.¯09Eœ'ëÒ8ªJÁÆf¡æ·%À:JR´Ë'Åwì°%§/µ«ìh×C±lŒÊJç÷ ©¶’ ®Áæ£ âNH÷hÃkM×J‹†­1´Vž@Ýc’¬Ö¶¯ÑçUžØQ-¿Ø£ûž¾2ŸÊ&Ï–ùãíÍ;T…sóß&œXöph­íãˆà6?èÔ8Ëóm bf8ú°Gl‹ÌÍ"Ú,KR ÏXm–Q‰¶(nJh5xê©ãÈ'Õ‘mæðUSÉ­˜vVµší­;• àà;‘BðþÜ“µ“ÉìŠ YªÁ+ m˜çÞYðí^¡´çíéæL9›}:€bn(Ý*@KÊ8ÛîjrÅŸCý_€*@(§ endstream endobj 97 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 13>> endobj 98 0 obj<>stream H‰¤Wko¤Êýî_QR¾€äÁÝÍ{u³Ñú¡ÈW²¯³žÝl”EÌÀŒÉ20ƓɯϩæáVžÈ’èSU]uNÕÕýòËÕÃÍý- úøñúö†.®nžÍ+,ðUóüBRŠõ?c}Y]\O/®¦SA’¦‹ AÓ9ÞšnùÕiERðïy©Ä%”Þ¥¹ò\+tÈÇï ïW™Ó]ÜM/îú`ŽìÌ9›`!\Õ—2”ŒÝä…¡¥ì0ôèïÆô%¡uY,ËhE¦g¤E4/Vë,©šmjJ¢ÒœF¶£ëOÏ÷7å1½DyB›|Sm¢ŒÊäß›´LV‰ùé¯÷Úå°¶m¹ž‚Mx2¯?P”e¬é•9± â«9‘FUSšÏ3¾Ø£wmÄÝjì9nÕ{܇ìê=aXHžïYÊ Ã`4›ì‘lòãPžcùÈ'GCM ÊŠ(ÖYÂQÖ9óaÈçlÿ\ÛRþÏüsO·—GþI›A†÷w”åš"Áù)xhTéêcãß,ªÒ¹´¢Y5xx^/ú0¤Í ,|¯’#É2å÷B)KŸÊXehBr08ŒŸï>Ýþm(˜A/ÆOŽI–«„÷&ŒRÐÓçûÇ)=ÿåóï†úÝt,<Ó17ô-HÑS!všaLŠ÷˜¸Và€tÚè}y¤mùÎ}Çruáië[Û¥0%‡í?›.@ –ãHߟ7|¡Žù"Ô(-EÊÛCyíqí¦l[í ‘®ôŒ§Û§ ‚?5e`üÆŒüôÌ÷¾%ì˾UçÍ*N÷«cæ,ž!WúÑ$ÑpTÜþÍ9(¶Ø€d+çTPÛLDbÅiõÒU´d” =ì°%¦ÞЧ].°¦º¸´.GÊd²‡89½.q¶/ÍIðÁ-7ȋϑLßób›!ĬwHÌ6º»kdŠVØu„œvv›¶¶ãÛò °e2oÒ¢a(ð±`B>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 14>> endobj 100 0 obj<>stream H‰œ“ßOƒ0Çßù+.ñ×ÝAbtû£‰‰‰}óW樊ÊXVæÔ¿Þ¦f ]2ÃC¯|?wßkû—p|Ü¿a0ŽGàõGW3mö=›{…ÉŸšü“ö†ÒëK‰@ ½2DŠ@ÎÌ·rmíúeSK³a6Zm”Ä,ã1žf™Yz×>½0˸¯‹rZÕPÖ«÷Cp+Ͻ‰ô&¶¸¿‚é§à­‚4DF£¸ƒ6­kè)ÈÅ*½7+Ëõ«îM„`1å»°´ÁÒNl´6MYbØåíCUÕM¯.ï…Ì(Ø;ˆIÂH`ÒøëÃÐBùÒ¥ïÓHÌ™àdöm#G4£n|:Ä›`5/>œ­$û xÄb$Þ6pàª>Ý–¤VÏ¿C<2‡S´±Ç¡-:µ›f˜A†áe,m/? îÆPW æÓ‡7åê­.VAìkµ„²ÊM¤‚ùÌ\×gK;·ÅÔ¾×ëj™C¡!H|›fN—³ÿY‚‚ñ¤Ó’oH: endstream endobj 101 0 obj<> endobj 102 0 obj<> endobj 103 0 obj<> endobj 104 0 obj<> endobj 105 0 obj<> endobj 106 0 obj<> endobj 107 0 obj<> endobj 108 0 obj<> endobj 109 0 obj<> endobj 110 0 obj<> endobj 111 0 obj<> endobj 112 0 obj<> endobj 113 0 obj<> endobj 114 0 obj<> endobj 115 0 obj<> endobj 116 0 obj<> endobj 117 0 obj<> endobj 118 0 obj<> endobj 119 0 obj<> endobj 120 0 obj<> endobj 121 0 obj<> endobj 122 0 obj<> endobj 123 0 obj<> endobj 124 0 obj<> endobj 125 0 obj<> endobj 126 0 obj<> endobj 127 0 obj<> endobj 128 0 obj<> endobj 129 0 obj<> endobj 130 0 obj<> endobj 131 0 obj<> endobj 132 0 obj<> endobj 133 0 obj<> endobj 134 0 obj<> endobj 135 0 obj<> endobj 136 0 obj<> endobj 137 0 obj<> endobj 138 0 obj<> endobj 139 0 obj<> endobj 140 0 obj<> endobj 141 0 obj<> endobj 142 0 obj<> endobj 143 0 obj<> endobj 144 0 obj<>stream xÚìšÛndž_e?ÀNWõ0Ärv,ÂT®^0ÒB!,“EÖÛ§zö«].—:µ$'Fr!õa»ê¯©þ«zº†!Ë4O!ëæbmœTk´Nš4ÅþKž´ÌÉ:eгö™:E Á:mJZëÊ<¥ÜLE SÁÖ™rŒ¦°è”KëÄ©ÌѤJšŠ©¶NžJéÀ¥L5,zêTSlÖiSmÉfê<5M&UÃÔ²YªL­D³§êÔêlâfn«¹¯ISkÁÄk¶Ži ÕþͳöÕµ÷ºÕÌ¡ëlfè,³[è={ÂÐÌÔYÅ$úÚY %4ƒc·½¥Þk]"[/¥.Ñ1rè#—.Ñ1ІIæŽQ SæŽQÍ'2wŒf>•nþÜì±d6 3A­—zÏSfÃ!öža„®AæÚ{yž»ƒÚãJwx°Mš–] Q{Oz¯ãÃ)VëuŒ<÷^ÇÈö¼:F‘l½ŽQ̇:FfUw¦¹É¬’ŽÑŒ,"†a`¶ˆôžm‘˜ÝÁ€ Mbï™'¤ûÐþ3 ÓDCï•Þ³1 ‰¶3"­÷ÌÇÒÉ%)švŒlÏ Ú1²ù^´cdãœhÇÈ£ï¬,ÖkÇ(ÉôiÇ(µkéÕ,íÕ˜%ÒRhÒ÷SšùXbÇXv!†Îö\bOo½þlk´KäÞk]Â0Œ$]Â0T»¥·ºìB2 ö„ÒY¦Ñè(=Œõf_êÙö»Ë[Ï"ÀÚz¥ã¦ŽQŒíÝs†»¾ŽQS’:FŒÎ€çîÓÎü8›O¿ývõüíÕzu|s}ûâæùõzýËååÍêÇ%ºçé—Õã×goÞüíìj ó>qtv½¾XV.¿?õóú÷›×o§V¿\¾^oär_ôè‘Aýxz¸Ûp!wÔ;Ñ;É;Ù;Å;Õ;Žù›Žk®®¹ºæêš«k®®¹ºæêš«knÍMPÓ3YïT¬©Ódƒ\®àV`+¨Ð f²‚X00ßÐÓÐÓÐÓÐÓÐÓ°#læÛh…Vi#m¢õõ…=sCŸÛƒ¾€¾€¾€]lS§‚SÁ©àÔâ¿ó¼ÁýI‹Þêz3ϼÅ/™ßÝï‚ß³ÁpyA^À÷ýTä¿+òм"¯þ;òùˆ|ÄŽÈsDìŒè‹èK>ž„žävlôÖ—ÖÓ²¾°¾øzxQ’Ë)cä2rø©dä2ëðkÙĵµ®Ç×£‡ý*Är!” û[äBøQˆ›BÜb¸ToÑCüâ§?…ø)ÄO™±¾ø^àa‡xXf—ÇøXàcï¾xYàea? ¼,ìwäáe—Eüwäág‰¾;àe—^E¢GÑO |Ì)ÑfÚB[iY2<Èð à 2<Èð 9£>døáC†>døáC†>døÉcdò]®Þ"×ø=x ûÓ_¬6-ëÙ§LþȻٯLFž<’É#Ù÷<’áC&äÈ:ø–É ?¦ìm ÚÍsDž/ÂÇ#qÙ%hBޏQp”ýRöKÙ¯HÜ)û¥Ä­·Ê¾)û¦Ä­rîió=ì«’·”sOá’ß„uÂ:a°N¶ë m¥Ý<·’7Ôí'o(yCýùÉJÞPò†’7”¼¡ä %ß+|ÔàcôÀG… >*û¥ì—²_ /^*¼Tx©ðRá¥ÂK…— /^*yDÉ#JQxªðTá©ÂSþœèƒ§ÂX¶cöüøÛ_…7-û¹&œkBܼx)Ä©ç„<'ä9!Ï yNÈsB|ñ!ćç„8òœç„<'ä9!Ï yNÈsBžâIˆ!n„¸âFˆ?áÕUxs•ê¼ö1zˆ!~„øS%ñˆ‡@<â!xăB<ñ ă̾¾;ˆ/B>x%ð^à½_‡¼x/ð^à½À{÷¢>ø.ð]àûöàïÿþúïoÿþòïïþþê¿õ'z6ûpº:²[Ì¢ru¼:^¿¸Y.3?_^ÿvöºßf¹½>;¿øFæçvê³›»ÑÓõÙËó‹Wß,WyÙ$’;“Ëm¸Ü›Ô劼L>öØEÛ‰.¶I:L,wé°½hý|ûÛ›“y©¿,²`-˜%x—Ì’Æ'¿”¥ÉoeyòkY™ü^V'¿˜µi{3›§ÝÝlÚÞÎdÚÞÏtÚÞÐâÄít1òÙíÍëó »{^]¬þ~ñr}½nœ»:Zuo~wùûêûó­ž\Ÿý¶Þôì’zqy³¶uöß_/^îÇÿ<³ûì“óW·×ëÕ]åÞÔógé>ëí½Óÿ{ÂÌfŽo¯Ö×o^\Ÿ_ÝlÌ9¾ýÇÞðæúü×õå-Ãï¯/¯Ÿ]9ÂS»P÷Gùuóë]^ØÝ!Äêi¸K…ÕS¹K‚ÕSõíï~Øìû¶§«£GþŒ×éÓ¿O7ºqœ5Ž³Ý˜k©ø5»í ÷ÆñÖH»18¤£Æ1·ƒGújZïÁ#í5޹Ý<Ž»Æq·ƒÇ±×8övcð8þZšïÁãlƒ»1x‡ãp7c±¥¶?æxlãq7c²qLîÆà‘eZ®÷Æà‘g\ÝÁƒ± ÂîÆàAÛkwcðàn«ó½1x¸ÁßÝ»ÂÈ‘Q8B¾^¥}Dò'VÍ¡ÿ©’cð’cÞ/=Âüó?¹ ó ÌÿüR$ú‡ýék’0'Ü s¶5J˜“aÎA­’3 W¯m¶»µKsЗ*b²ã™ÿâÅLv<³ã™/n¢\wPäı ‡¦êó0+á°„ÃK8,á°„ÃK8,á°„ÃK8,á°„ÃK8,á°„ÃK8,á°„ÃK8,á°„ÃK8,á°„ÃK8,á°ä#Ç'r|"Ç'r|"Ç'r|"Ç'r|"R‘šˆÔD¤&"5©ï¨:Ÿž¼³ì,^–Ž´‰6ÓZÊË88âàˆƒ#Ž88âàˆƒ#Ž88âàˆƒ#Ž88âàˆƒ#Ž88âàˆƒ#Ž88âàˆƒ#Ž^NÇÁGqpÄÁGqpÄÁÑËú¤ÂH*Œ¤ÂH*Œ¤Â˜½Ì>Ra,¤žûuMþ=€z0†*†*†*†*†*†*†*†*†*†*†*†*†ª—¤0T1TÉÙJÎÖòqß%”œ­äl­Ÿù‚œ­¼·â÷ŠÓ“ÿÚ¼u(gÐÿ؇ Û˜ÿ¹ø²_.ˆ,!²>ò ÆéÉö ƒ¡ø)ƒ¡º>QTß}Ú B$|¥ODˆ!øÔaŽûÂß:ú_iMóêñ½²õöïÁŽïýðjÚìÀæï»¦0,)Ã’:,‡%Ó°d–,Ã’uX² K~‡Ê0‡Ê0‡Ê0‡Ê0‡Ê0‡Ê0‡Ê0‡Ê0‡Ê0‡ö˜ð$gQ¦ÑIP?YNMÇÝO}GËoTüôÝåË·‡°0É¿"Èâq*…q.…òÀ3Ö÷=£V~¢îûGÙô~ÿ<À#ÿþ¡¼0Ì£“þ7ë”»÷,ÖÅb}¯Å"Yüq™l>stream xÚ¼YËj9ýÁìçªêTé!d“0†˜Éì.³0Ø„@p 80ùû9%w^~aWC¾ªn©N=tZ*ÉÚ´Ô¢ Å'+"ƒ­ém+ þ5ÊÓÙŽ7¶³˜ h¯Åº²•â¶JÂu”fÄëVz%^÷Ò£·2”xăx}”iÄë³H­•]Ò!ôD 94\"æ@AÔ½„~´"&ÄèE´0ˆì“&è–4§‰IäFTDîBäIäÎДãdùÇXeŒÐ"ò´LôZc0ÇÕ0ˆö‚x+ˆJw#u¨T€‚ía†@,s¥@Ÿ\C g”¨‘g!EP#s ²Âž"´ W<„eÈ Z·ñ Ñ­ œR„*_SàdA‰nàS c’>¹q`øÝj¨¢‘Ä(èñÃÔ 3-ˆ°‡q Ra2\(!f#A­<ù`µ‡A ²„  ÀDVL`t×–ã$™l¡wÅŒA3N¿yäLjÜ"?FäF ‚Cú…'$“H€yð FäA>ˆ¼\e6€Dž€NäI^€f¼®LÈ5Œ2}.œ ½ 9 ÷âA|0_ ðN!ââÄ8}¥0‹ûÊ_¥@²©ñðùÙ³Ã_¤ËáÕáôãÙ‡Ë?´þsñßÕᔜ?Ä¿ïnt¼/$#;ž?¿VÕ¼*òª–Wõ¼jË«ö¼êȫδ*j^5Ï&äÙ„<›gòlBžMȳ y6!Ï&˳ÉÒl:Æ>ÉÝ/þ%Æë‹³ó—ï9Xûï5ÄÉËOç_o›½ƒN±å?Æã<,O'ËÓÉòt²‹Ó–ÍÓUmsñæûÊU·Îh'[ßÓ¸„ŸLJZSÓšHkZZÓÓšíðâUÇöÍä@ú©û@F€°"Ú2óT©ËþØg_òŒ]ôäé'¶h;ÈsQÍw:Ðóª#«zŒƒèí­G §ƒ›[Ï/kÍ \œ¸³LíXáòKœæI¦ùENóÌzb!|!|!|!|!|!|!|!|!|!|!|!|!|!|!|!ÿ‹jžM¶£þʳÉòl²<›,Ï&˳Éòlò=k“l„¸c»qzø¶ÃÞ>=¼=œœ}ýôåê o.Ï/.¯øýþYK绫³ÏßzÅì‰ÝwA?õ0$éÃÐOšG]—Ž|^wŽ×mßÚ±µ[áþâhr_ oÕoÕAòðÉ)n=š-»÷Õîñ´f[æû>ó=«yëvwMÃúgJ´ø1íÞéhNÇŸy\'?ìÐÿ K‘B endstream endobj 146 0 obj<>stream xÚÔXM‹7ý+‚œã‘êC` I.1^ÈçÖø°°‹1„5„ Äÿ>¯ÔڞݸÕQ4¾ä°¨¶Õõªôêu$Žä¼ãÈ.Ù .PÁ¨ŽBÂ¥ˆ¶Ø˜xÅXœúà>á—‚‹l#ÆFÀ)p’¸Ìöžº\ìytEìyrÁû #Ã0ôT\„ÙÃÀ<ç€\Ø ²¤lŠ]`EøŒ4% …`œ£ ÊÈZ29*bf ' H ¤h3!zr† [`GÞVXÀ€ŒÀ„<`$È…KvD!JÁ”x•›âÉ3Ã`àX<-²ñG¢öÈ’í 5x@Ö d5@<¥£˜ä˜Ìr"à€J ¼G©˜3%@Î(‚ ä"ÈD ¹€u!¬Äƒg{¬[lµ`Ö<`€F± Y„L‚ ÆcMÞtSs6!DJjå-C+L*fÔR™aÌ"²°}Hƒµêny³¶°.Æ¡Ô"Þ<‘†¶u‹Ë ‚?5~l:z›Â?Ñ‚¢®Rù`e *@ΚHÕ1æ!õÆ*ÞÓ`®¨’1"”ŒC§5:$¨VX5Z£krª5…l‚BýšB͆½œñÎë×§wýé‡Å æÝ¯N?n¿ûôøùßþ:ŸnÁŸ·™ÓûL|tP&Þ¼YQ¬ëÙWŽõ+_Ç´¦c™I'3ë óãçû/_§Ä{)ÉÐj¤Fæ.'c(:뺄X;[å"ù6†6Ò…êr“¹I{©å¡Uå9t¹C)Å_‡B~Öu±^“dcÒ—.“|È$ÑN ‰‡²ßhˆc®2ïªó®qÞÕ÷óÃÝý§Çx'TféˆÙÅÚyÞdvñ%c¸”㪔ªä¡NÆ~z‘æ]iÞu^F>stream xÚÄX]k7ý+‚>·W£ÑèB éKƒ5Mß–>lB 8P\hþ}ÏÑ*¾v³ZÖ»vú4ckçhtæÌh÷Æœw1©Ë4ÑI¤5'µÂ&§^a³S<SqQi«3‹K+.)mp©Ò΀“£Ë8Ù\ñ6¹¢ 6»’h‹+•¶ºÌÅâxÁ&EàDD”a±¨ñ|9ŠF8HRx, NE:%; û—‡ ” §§Y «Ñ²:@ŽB‡§±™Eä&èÙ {U [¡ä$ €œˆð@NefF2Ži Nrá3@.๟rAšZ¤ªÁrMt*ɇ#Þ% ‚`CÃÆAÀIt'51& NæRv!„¦`Rá Ž€œ@‚¬jÈÔZrÁIyìP™3 £žÈjHŒ‚Dé§È N…ƒŠšz§jÀ4’èEY=SuX Ч ’ÓÆÈÒLZªùš¹R^ ùxÀué+X¨@`Å€,¢¡ÒA©€B‰JÒ!µF1Y¦S¨s:&“oB†™P(ÈÐKN§é#Ö¥“H ÌCÒaÞ•“#Ï”©e“`YÜ«W§ äzz3Q‘ÞýöÇéçÓåŸWŸnþ÷›îN—H³påôá? Ñ+\xýzFQ¢Ìÿ:€÷†Nä%Ç?Ú€˜mé¶Î6÷C2Ó2Ê4ÝîýÛÏ×_¾M1-Ä Q¶œ.sçœG;oD) %D©»CÍ·ìP“ͺcürsuýéö#­ºZ Ku“m5˜»l×AßäÝý¢öENò¾ØÖ~=½¿úòùï»AßÝ^ßÜÞaXüä/~¸»ú뉫÷|]¶‹çqFï &Ý"݆nµÛØ­uÛ[ ôÈ]ç߉¯Ã9ÂV5®-–•ÅÖ×`㬠‡ü?¬åRž¡˜/²ú‚i>·˜÷žáIy<‘ï¸õKãÿbÿÎt1ùöfÈyÄÃÙ¦ns·åëÕœD7‹ÍWç£k!?˜í²":ºF†¶ïèh#ˆîŽŒÜÞ×cÛÛÞÈ)µWðV† ݆nõ¾,¾ ËRVË’—«[ŽTÚ¾yÈË&Ú@Ò1ñ{C'È™-3]õ¡«>œUïmHo]WýÂË?—¶œJÛÎq´óF”Y¾zÅö†N2é5Fµ XÙÙ0Êl~¹3»¤Ü°M/³t‡“l#JÝ:ß¾lg.ºú49κ"«œ„¥!«Û†,§¬Õá˜Ûˆ¢{C§Û—üÌÅüIÈïøÙvÝÄ>ø¢vÛ9ŒÃØ;8öŽ÷lu8 ç—ö1§KÒ×¼‰ÔvŽÈ(¹¡¤ƒ(ehm رÔ7”xEŠD™Å¢hC‘ƒ(±¡øƒ(»U:ñ×Gëf½Ñ¬7šõF³ÞhÖÍz£Yo4;7ÚðW&;Ò/6š. ô¸i+ºå¡*6¢4çp %6g9ˆÒtžýA”¦óT¢4§r¥é<åƒ(ÖPÒ!” ÷o]üyËÒºLã’Lm[ò 3øüÃÏ(ô_\ns… endstream endobj 148 0 obj<>stream xÚìXMo#7 ý+znG")J ìÇ¡AÒÖHöfä` A`›,hþ}å±×ëxÜŒPì¡í! ×"ŸøñÄ‘6S dB©B(ÅX EÍF1'ÈL\bqY‰a“r ®.# »d’äRHŠK%.©*¤Qbì“°OÆ_ž+YÄZ d ûÉà› S­eaH¥ÂØ1¥bTÙF­. Åà” Ö©d$J„b®0”êŠPdvYsrisqyKt%CQW€,Ù +jdÈŠ"Y²Ã  {m,Ù= Å‹ÆîdC9,9÷r÷rö D —à%rxE $mÈÕÝ£¢#î…òr@:^?FPÐ,¼!Jö6YD»ÄCíX¼¸H›U`Ì É–9¡öÆ@ö"ÊÖl€l(µ1[¨È„[¨h&ülä‚–jÍð†~qa ¦‰B¥ ’È®ÏK2¤î^HõóE@NÓ@¢ˆ6ˆ6†8Ë<0²wÆà)–J#º„xÈt3tH 64rÁ 5’â & ·PÑ{©^ºäê™&!m1'08x``•†ì^N#]K ç+«{URñžZ€âÁƒpª¨ú›7Ã9i> ‹Ï«û‡8|ºýs=,@Áù»®îœÆÂÛ·Í5…~WDåf‹v¬7gÛEd³øí¢­ý6\¬žÿXWëÕÓúìáæöa"ÿþ¥«¨…î • µôÑ…ŸÚèÚÈ2ʺ‘ æFÆQò¸Þ |}¤Âçm®MíH)ž\¬'YO-žò”S{Jž®JSKµ¡áõðn郻©/H<ÖÕ¡Þ?Þ<7ö¦{'[6¸ûøïcÙõPìÈeòœ¤½s"¹Z¦}%JétÝ¥º—ÙßS÷xþkg··<ÿd©¿Öÿ;ù@Ÿž¿Ü¿|¸ÄßÙG ûGìzÖTöŽtœå¹? ¸ÛSº=µÛ3u{Z·gîö,½žËÍ­wœò?ß®nîî`Ë@ð{ßô×TðkدO¿¯>»5ÇÑzûÓ¡yÜ7ÿÞ”9Ï3—yæ:Ï<Í3·yæyžy™g^g™s˜g¿áMl¼áS¼Y‚oþè9ιñÚ9Å9>2 ü)õšùÓ? ¸pÿ8àþyÀ¥ßµöÏéÐ=†$¶wë'òINÈ‘/‹¿†_qÿ§Eúé´ nÑÞá‹m¶ÇžH[¦/Úsý…cÝ-Æo—ã‘ܺ]O ËK€ú8Þí“LƾÓUCº¯Ò}Õt_5¤ûª1zþ%À²\i endstream endobj 149 0 obj<>stream xÚ¤XM7ý+–òÆõaWYBHKFIn#H „Á…CòïóÊ;3°Ó=MoíaÕµc¿WÏå×¶Û½s©¥w)ÍñÐBÔñl…Lñì…Eð´Âƒñô"ðE 0«Ew£ÒxcОÀ(xÀaµáÙŠ»[/Îh3+îà3/CÁg£P­ ôŠ 28A EÀ,š¤3’9D²G•¢Hë½VäEnRˆîî…Å/`nHט» i€9Ø1 êjðG&H1Àl Ì#ÆŽ>ÀìQ‘f×@Ùc|ÌE0è¦!˜:ZåµaÌU  E€*E58Fa¥eΨ­htöÂZ£ó@ÐÑìÜQn(–˜»RuF`¶ÈE`vüg âbÖ0}F`®KjCÀµH”ט ¼Æ\d ÃücìÆZD¹¸Qè5îP:cƒ8‚0&Ä€”®È…:JÀÌ£0;¤ˆcªMÀ<)̳b˜­ )06 š•Â7¨¬2êg ³1È ŽQfe˜aCbŒÒTÀy†‚ªÿè¬*F¢³ªpŒ¶Éæ_X ãgƒó4fÆP# ÇZ³£6ÖÀì˜g«#²7+­N¸—F5‚/õŠe‹Bà iåÅ‹ÃÛb‡×‡ã?¾|ýëߟþý~8†7ñþyøë¦ásñÙðòåDz9ÒHªy(塜‡JªyhËC{š÷åÍDy7qÞMœwçÝÄy7q¸éO>~ùú}ÈØ6ï^}ûøß-ä„E5ö>üðþ–'¶oa±!,•ÆŽºGiÚF§Ø÷±ÜQ¬ÛŠÇšâ]µ•¼$ï#á••Íó“{õ©›õ]«ïQzévœ'žs’7—ÆsÞã<ÏzÏã¹ÀÞ?*λ+»- ®ìç<΃Ô"µž×XȲ%°_}¡yœ5Û†fK‚ ûÓÜD?¯…i$§‘’FjÙÒÈžFZéiäÈ;á&Ê»ˆò6¢¼(m¤µù²º ÆþvA¤_œ”âËgbË+öùé´P,S1o+kŠ}×ú÷?cUÊû‰ó~âüÂÄé•é_fl÷&¶nNìÚ‘)¾÷(ίNœ_ž$o'ÉÛIž±Ëåí$y;IÞNòp±°SãBcËN²f'Ùe§K·ã¼,¹9Ø\ò®ˆ.v=Î;•[ Öû@ò+P–@Þ¶+°-÷†OòîÃAï²xÏŸ9'¿Ÿ¼ÖËôÜÖz,æçgšµÃZ\)ýZÀÓkšF¶4²§‘¶ôüSüZMO>oÓÖË/ÇGÉÖvÕ¦{dÒ3¬DóŽo}»¼)xí˜7‡{çDy QÊCÿ 0x Ä endstream endobj 150 0 obj<>stream xÚ¬XÛŠGý•†<ÇêºuWƒ1$y‰ÉBDœ7ᇅ]Œ!¬!(ÿ}NÍJ£ÍŽFLÊyêÒtŸS—>}So\jéMŠ 4Zˆ­êm+, m/< ­±hGQÒÒ{-Ú-FË !´Rš¶cLoÆöÞŠ3xÁíÞîe(úú(T+{…`º"á08B·K!V;‚” vG”aƒŠ4âö#w/d¹ƒÙN`¶BÄGA8À|ÇÏ÷ŸŸ>a°€"Öó3ÅÝ_¾.ÝòÒmlc["–|²š‡Zš—çåÄy9q^NRóPÊC9M«é :ƒ×õï~SÿrENqºn‰8/'ÉËIòr’¼œ4/'ÍËé<ûé~ršÆ÷s§Ÿ:/lwç¾Ó¶Ÿ®1¯£®í”%ouÚxélÿâÆ„/gØÇvYÌìz‰yY¥±V¥_ww÷_¿üuÜ}8Þÿy|ÿôðøt„¦ßÔ]ÌÅé÷÷äoê)Ò:]Ø«ŒÁíÝ«¬¿<—ˆ[àŠ^"ÿÛ†ò)i¤¦‘–EÚt¯ŠûÃÁ»žëüºjoº_+tߺ§“餩N7õ9kZ˺Ûòº¦/Ú¤/Ê Œ$Ÿ¸NÏsâ—ËÛ"ñv;q»–¸mоåïùÄ}zS͉Ûjâr;ñq-ñ±iG©éę҉ãÙÅ9q^M¼ÞLüÚ¥;ž¢[¢Ïïklyh^gÜóÐüvÆ#䤥ušÞýqˆgÿY'6Ötbý¦N®ÝÄã†-YÈä¹­yÞÈ’W›äÕ&yµI^m’W›äÕ¦ùL)͘ú W²¼š4)ÓçÁæÅ¸zZÙíÓJ¯]ËtÓµLóÒÒ¼´,/-ËKË燌Ÿ^S/2>?¾ñ!³:üìHxŽÂ—Qôÿé95ŸÚûéOÏ×~ÚÕ7ê? éñ% endstream endobj 151 0 obj<>stream xÚ¬XÛjGý•‚<ÇÛuéK18~‰°Á"vž„F`d0 ÄŸSÓ»êH+yfÛ~°§¶§.§«Î™éQ+B‰ZQ²Š‹Q-¸dâd¸â̸Vâ^$5\$gj5‘4øU&eÁUH³ãª¤ ~ÕÈùj&C…†ÜæáW) òÕF¹†ŸSQø¡Fiqeª†ü ¾ q ÿ p –3ò·LŽ ¾œ40yx6bÎkN,ŒO0´9« X[Jl†únØgBBÇÆ3vÝ™‹ ³#s‰=:2WC ÷h†'dn% dvaÈìØ„´$u¡ž2 ç £šâ¨'‚Î8:*ªØZB[ œÑWÃ.ŒÿÝ•Œ]8#sÁ.œ‘¹`»ÎÈ\1^ÿ{øý;·éÕ+¸¿¥†±%úkw¾3¬»wg‡›T¿É{ 7—{uò}z\§üº:|¨*Õñ_V'Õû:zT§Ê¯«3öSŽëägê¼¥´{³;ÿrysû›¤ÈýÀéáÏÔóì#y:RùçõåÕÍíg¸0Cú{\|½úþ8âB—1~z*ºgæ3¡dÇ0ãá¾fžÞ`™Ž¬Ó‘m:Òç)ðì™§Ë|¨Î†^°-¯ëg8XÈA~‚JqØ‚xžKtžM:Ï&g“þÄ›ÎdzøÎxtÖq~þ¬sxäž/Ÿ#GõùÀýá|ùj9 ¼‡#ü0ðb®9„}z:{}"ÁÙÌùŒÏ^ã%[Àñirx,ýãã·/7·×B.>$á¾tˆ,Šß¿ÙÔð{hÏÕ¥h©£h[)ŠSÖRFHÞ€sñ¡îËçµSÖqÖ^TGQ[Ãé=DFoÀ¹üšðOÝ–“rŸcöQ7­Aå>†Üîcr]ÇÚ}çØ¥Ûå4¬}–9ºekE¶£°jÇ·ÿËÂÙtÖ>ÏÌ£®¬a•ý,†¦lƒ¦l¯©•uQÉiª’>O²²UYIŸ… ]Ù]uœ¿ KNS–ôyÚ–­JKú,lhË6h«ûà-þ][ú?m醧UŸ§mÙª¶´ÏB‡¶tƒ¶ºÞÒáßµ¥å4¬}ž:´¥«ÚÒ> ÚÒ Úê>¤¡-íÚ²tÖ>OÚÒUmÙ~C[²A[݇,´%û–ÄWëó”¡-YÕ–õYÈЖlÐV÷! mI×–µ“új}ž2´%«Ú²> Ú’ Úê>”C[ýèBy³¶þ`í°æ endstream endobj 152 0 obj<>stream xÚÌXM‹7ý+‚Ü3R}èŒ!l Ø$ÎÉø°$KX²ì†eñ¿Ï+•zäijQ7ø°‡aJjUÕ+=½îên™B -sPŸ„ÿR±qD6.JÆ ,6nÆ%‘Šÿ4Ù˜‚fsÈ„°EB®6ÖPÄÆ9”†¸¥„šmŒkÉÆ­çn5"?c¢&Õf(¤Ä]9$2l˜M¤6¡f¯–„l¦Â@°V[Hj"爙†ÈÙjÔh3ˆ\¬Ú†ÈÕÊmˆ\ 'jLÍ nˆl£Öj ˆíi­J ÓQ)C“mW·–Ž‘±d³$¤n),ì!,l°™…-ÖÖ-¤È¢f!GFÆS4º…•l{°qT‹1–Ã`ÂBŽ6& Õ° %cëÌ*°Š¡J ’Ȱ$PIVgÄf1TÏ  ±”nq`%C…b-†CÎÒ-ä(ÑPr5, ‚m;a!G-†Š‘£‘åà„49ß 3¬Ò-ÁÁò9 ¢=2N¨_-A²_­°|®)=Ú¯ rÔ~U£ùNzìñ°aýªâøúÕ Ëç áñjPLJÍQLJULJ%êøp¨Ôñ)r8>l„:>ELJ³«ŽO‘Ãñ)r8>m!;>$;¾œB6|¯^~¾Ó7Ðx‹á§Ó[˜ ÎÌŸO¿Üÿvóxw{szû{H6ùúµû°ûÔ³ŽÂÿû¼ßÖ#3áu;8]Þ½¹úŠb²@ƨÂä÷ŸåUÏ«3o^b-î#Ó‡w`eÇWm=u;ÇcX›çM3/­°æÁE<ûĶÆêkp‡´õÕm™Xi5;Ÿ±Ì¼u‰Õ¹ˆyúè¬êø²­·ë1¬Îgä™W–X‹HÓ'íÀÚ‡ÁnŸwºM‡Î@é|Æ6µ—Ú*ì>gmŶÖÖXúí»¹¶J>´¯E=¯Î¼Km•â>2}xV×V¿å7×Vǰ6Ï›fÞ¥¶êà⬭X×Úk‚µ ±º¶ª:Õù¬eæ]j«:5OÝÕµeí‹õ:Ý>¦­ê|Vžy—ÚªÎE¥é“v`umY«k«Ñ!¬Íù,mæ]j«9ej«ìЖ¯ ÖÎÙó³ÛÇ´ÕœÏ2µU–ÚjÎE™Ú*;´åkBoòŠk ÷œc`Ð2ÅU–â½ҦºòuùëèÌ¡ŽÁ1}árO§Àr]ãuFòTXÞ¡0_c‹í'cpLcØ$O=E–e×IÉSey‡Ê|=v¼ÿóÁ1¡é©u -/…†þɦÒt‡Ò|M°f­÷µ>Èñ:µ:Ŧy×IÑ©6Ý¡6_¬îýzб>·§žzÓµÞh2õ&;ô&Coý Q†Þè ÞÈ©•©7Y뜙z“z“¡7[z£z¯S+So²Ö9)2õ&;ô&Coý½ÑõÆN-O½ÉZoì¤ðÔïн±øû°>ÑïÁëÔòÔ¯õÆN O½ñ½ñÐWÏï‰ñ:µ<õÆk½IBŽ·w×·÷Èòîæï§e¢÷tô5.´Æ£çñ¢ñ¿õ£ÿ‘ñì–Ñc µ¶qci:âéˆ7îîm<ˆZñʈ·õ[ÏRF¼:â~±¾¶µ¯x㢑oFÞŒí ž¶f#mÏÈ´=Ï)mÆöĤ-2u÷¶™™ç^ö|súñúãÃ_O'Ûîoo~}x¼~º}¸÷ñÏ›Oˆú÷Å«‡»‡Ç÷ñkö߇óÔY‹ß!€ùþÑ)¤M&çéé‘x|A¸_ºr½\‡<_9ñxÁ•Óå:Òó•Ç#/·òñ9ï¿u¤úlåã ÞN\ùeÕ&}¾r9âñ‚+¿¬ÚógÇ •§#/¸ò˪=<ý¼òXx¼ÜÊãeÕÆü|åºÛãeAÞê endstream endobj 153 0 obj<>stream xÚ̖ͪ]E…_¥žÀÓõ×?2‰ƒ€‚¢q$‚žA0Ü.W0oïªÚB@»Á=ÈNÝÚ««ö׫ëÐ\ªP!.U©yüoÄž ')T’+ÓH%5ìz5ÈòU+dcDÀäÍ"ª–¥:ZFÍb9útÉU•úÈL£aA§18‚quæÒ¡d‹\ǃ{¼èB,E;V)KDèª=s(iu; XHJ¼pïáQ5s(U£hèÑ$*Gó^` hÏωÊ/FnÓˆÝIôíáÜP ·¨<:‰HVˆ aƆá“¡0‰ñˆHa[)6Ù$"#©œ9ô¨°z4ֈУ>Ÿ¤×Ì¡Çà¨Ìè1<*ãO-j iH)"Êlp²DevD¹Å\IÕ3Ÿ­Deîˆ<+R—à€sZKä¨é1 z¤Ép…´µàôè–9ôHŸ4fl§‰Ã42‡Ó”N³â8¥Óð–LâL² ™jæQÊ´tšñÀ‡‡VD=sè‘Nã\ ÊãƒÖ<8 =ºòu¸,f`é4Îy)Áa†¨eÎÉÓi¶Š(œfºXpàá*™ˆÒi¿œÆ&ºÅñar¯™CËiGËi€ù |¤wÎz\Nãûå4LöÇ›k¡Zjäð¨áô«W·ïnß¾ÿüøãåöîþçË×÷_Ïï_><žÞ}þt¿ýôôÛýù㇧û?^¾y||<ÿ\¾*ô÷¿_^¿þK}ƒÀñ.ôÃíÍí- ÄÚßoßGZ3ýã—ô—²³â`ržs”ùÀoÇÆ‚SÁ~ ÿ1ðk±Â®ús©}JaKjÝПK-S ^R— ý±Ô}6¨£÷uoús©§sÚ}Imús©§sÚeIÍús©§sÚÆŠºõ ý±Ôm:§­.©}C.õtN›.©eC.õtNÛòrVdžþXê:Óº¼›Õº¡?—z:§uy7«º¡?—z:§uy7«eC,µOçÔ—w3oús©§sêË»™Û†þ\êéœúòn漡?—z:§¶¼›YßÐKmÓ9µåÝÌ|C.õtNmy73ÙПK=S[ÞÍtlè¥Öéœêòn¦uC.õtNuy7SÝПK=S]ÞÍ´üWý_ Hu2 endstream endobj 154 0 obj<>stream xÚìT]kSAý+>›Ýý†RˆQP¬Xlć҇ØÜ†‹áÞr¹‚ý÷ÎÌÆ„}¸n@‹BÎÎÎ9sfw²Æ Æ Ñó¯ã%@«œ@D°h$°Y"œ#‚‰\F<å@63 aìè F’ e²@uÉ&®–ôÄnœeDK4ì2Q-Lì.ц ’GFœe7Ésì"‘K/‰–Á‹ Ñ"²ýD5bf“™RRäØO¶œÇýä$Èês„JÌš£ó1%±Ä¡Ë@!B¨5¡$ÈЉ–*1ªá ªá ªøPSÈ- µ…e—nAûÌ(ƒ•¦Ñh°–;G:D묆³3õ^]¬ú¯£Z6߯—Ím?¬Æ¶ï–÷úØ­›aÛvÍ£ÍE¿í‡k=Ó°ûÞœŸŸVê-dÌ4zÔB½&>S¿¨KŠ&‰^¢ûü8!ÿïí:ü´ ÿˮ݄ü“ZÕj~-“I•nÈÀåvÕvÏP3<ð›Q<olÊ¿”¶v:¦è`­Vpm×Up}7Tpc7•û2µ÷•‹Ž®žD~Jk…LJÕBX„bµ-B¡ZÈ!_-ä‹« E¨þùˆE¨úý0©U¶É¿MÞ?ÐWãjßГÜ`ÃL«WÝz·tif4}ÂÆ¿ÎàšoÛM§”Õ Sv? íØv›wýºQÃòóQy}TW1ÉSW÷«ÛæEs×ìËz~Geöéö“¨ŒfäŒ'Ô7xlÀx¾×œâ#Ísì)}üМàãÑÖZ`¹'1ÒÈAwêÒßîð|f endstream endobj 155 0 obj<> endobj 156 0 obj<> endobj 157 0 obj<> endobj 158 0 obj<> endobj 159 0 obj<> endobj 160 0 obj<>stream 10 SimH Sample Software Packagesbsupnik endstream endobj 161 0 obj<> endobj xref 0 1212 0000000000 65535 f 0000007860 00000 n 0000008123 00000 n 0000008377 00000 n 0000008521 00000 n 0000008665 00000 n 0000008810 00000 n 0000008955 00000 n 0000009099 00000 n 0000009243 00000 n 0000009387 00000 n 0000009533 00000 n 0000009679 00000 n 0000009825 00000 n 0000009971 00000 n 0000010117 00000 n 0000010262 00000 n 0000010408 00000 n 0000010554 00000 n 0000010700 00000 n 0000010845 00000 n 0000010991 00000 n 0000011137 00000 n 0000011284 00000 n 0000011430 00000 n 0000011576 00000 n 0000011722 00000 n 0000011868 00000 n 0000012015 00000 n 0000012161 00000 n 0000012306 00000 n 0000012452 00000 n 0000012598 00000 n 0000012744 00000 n 0000012889 00000 n 0000013034 00000 n 0000013180 00000 n 0000013326 00000 n 0000013472 00000 n 0000015701 00000 n 0000015940 00000 n 0000015992 00000 n 0000016044 00000 n 0000016096 00000 n 0000016148 00000 n 0000016200 00000 n 0000016252 00000 n 0000016304 00000 n 0000016356 00000 n 0000016408 00000 n 0000016460 00000 n 0000016512 00000 n 0000016564 00000 n 0000016616 00000 n 0000016668 00000 n 0000016720 00000 n 0000016772 00000 n 0000016824 00000 n 0000016876 00000 n 0000016928 00000 n 0000016980 00000 n 0000017032 00000 n 0000017084 00000 n 0000017136 00000 n 0000017188 00000 n 0000017240 00000 n 0000017292 00000 n 0000017344 00000 n 0000017396 00000 n 0000017448 00000 n 0000017500 00000 n 0000017552 00000 n 0000017604 00000 n 0000017656 00000 n 0000017708 00000 n 0000017760 00000 n 0000018036 00000 n 0000020543 00000 n 0000020806 00000 n 0000023006 00000 n 0000023282 00000 n 0000025144 00000 n 0000025420 00000 n 0000027835 00000 n 0000028111 00000 n 0000030466 00000 n 0000030742 00000 n 0000033064 00000 n 0000033339 00000 n 0000035490 00000 n 0000035765 00000 n 0000037545 00000 n 0000037834 00000 n 0000040123 00000 n 0000040387 00000 n 0000042678 00000 n 0000042943 00000 n 0000045259 00000 n 0000045536 00000 n 0000047777 00000 n 0000048030 00000 n 0000048511 00000 n 0000048961 00000 n 0000049525 00000 n 0000049975 00000 n 0000050208 00000 n 0000050451 00000 n 0000050688 00000 n 0000050869 00000 n 0000050926 00000 n 0000051058 00000 n 0000051222 00000 n 0000051369 00000 n 0000051563 00000 n 0000051695 00000 n 0000051846 00000 n 0000051971 00000 n 0000052064 00000 n 0000052223 00000 n 0000052320 00000 n 0000052419 00000 n 0000052564 00000 n 0000052659 00000 n 0000052758 00000 n 0000052903 00000 n 0000053010 00000 n 0000053115 00000 n 0000053257 00000 n 0000053404 00000 n 0000053556 00000 n 0000053688 00000 n 0000053791 00000 n 0000053898 00000 n 0000053999 00000 n 0000054100 00000 n 0000054214 00000 n 0000054406 00000 n 0000054588 00000 n 0000054718 00000 n 0000054819 00000 n 0000054920 00000 n 0000055017 00000 n 0000055126 00000 n 0000055246 00000 n 0000055360 00000 n 0000058566 00000 n 0000059672 00000 n 0000061009 00000 n 0000062316 00000 n 0000063431 00000 n 0000064587 00000 n 0000065772 00000 n 0000067116 00000 n 0000068653 00000 n 0000069626 00000 n 0000070357 00000 n 0000070394 00000 n 0000070419 00000 n 0000070482 00000 n 0000070615 00000 n 0000070711 00000 n 0000074723 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/simh_breakpoints.pdf0000644000175000017500000011152710461544777015453 0ustar vlmvlm%PDF-1.4 %âãÏÓ 222 0 obj <> endobj xref 222 13 0000000016 00000 n 0000000996 00000 n 0000000556 00000 n 0000001249 00000 n 0000001498 00000 n 0000001900 00000 n 0000002441 00000 n 0000002477 00000 n 0000002699 00000 n 0000002927 00000 n 0000003004 00000 n 0000004980 00000 n 0000000817 00000 n trailer <]>> startxref 0 %%EOF 224 0 obj<>stream xÚb```b``¾ÃÀÌÀÀz—xXÀãAÃL‰_\z& 5=@f§Ü£³;æ5;å\±R%€Â @#":€$c“*ˆÅ× \ Ó4'óƒETxÙVD7b`ˆef`UŸvßÑ„G ©±ÂjXCb=f:ó-Z“?ƒìfk c`HÉð¹u Î0§]#$ endstream endobj 234 0 obj<>/W[1 1 1]/Type/XRef/Index[29 193]>>stream xÚbbc`b``Ń3Î úb€–ø endstream endobj 223 0 obj<>>>/LastModified(D:20060118200154)/MarkInfo<>>> endobj 225 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 226 0 obj<> endobj 227 0 obj<> endobj 228 0 obj[/ICCBased 233 0 R] endobj 229 0 obj<> endobj 230 0 obj<> endobj 231 0 obj<> endobj 232 0 obj<>stream H‰¤W[s›V~÷¯ØÉê ÌõLg„cRÝpÒLÓ$[´hÅu}wIŽ$÷2ñÄî·ß~{9·sxÿþv2 F ÂO? FC¸¹F*,K|@ÿ \f7¤øü>*oñÍm« AüxÓWUUmâ%ˆ>~¦÷â4•®á]\€ÆU&ëOš£*Ìr]˜k(*ƒx{#ÅkQ0¹‡AÁ“?vyšUíåKYñ-ôâßoüøÆŸÊr­E^#ÓŽ‘Y-0Ý&\ZƒÝÛ®âšÀlK1m×uÈý¯Ò _ ¿]–þÑë[’ :ëÜoúºªðU*ø·´ä+„Þÿ˜dô”}íAï·øã9XúYXªtŠÅP‘Æ4…95’q)^ãÔðI&ˆþ%ˆÏº!¼¨íâ“«3݃—_¥álþ% >ô,é>î1 ¦³8úÃ2ß ëŒCÃQtG5Œ«aYç ¿ŽÊºäDgŠÍT£Š„ô˜o6ùsÚ³¥ì £ƒe¾{)Rú¸îõ5©‚,¯Ò%‡d‡v›”㥄*‡JÄ~‰PŒÅÒ, v…ŽZÁ–ù_/z}]Zri†· ù)^dHð&[Á*Çër¿%OY•Tižýx‘höˆÖ,E7U]%Ú¾R/ú!ZýœM·¬YW¤Z±1©zMø ™­cÞ Ñx]Q°Û/6i¹Æ¢I3@ui®kàs¬ü_e2<iUñ /æ |ÈgLšB¼Èó?ôrÃruEg¢=ˆbAÉ<­+¬ø%Õ÷Ô•wñÙ˜î¿Lã4ì¶)dw%›šz%æ‡7|1—JTwkæ¼Ø¦e™R"3J¤%¬yÁ1MOµÎ )ɪž#ñ• ç?Ö¼Ö‘å ×L±æhmÝ,×ÈkR„2A%jM·Û#¾Œ£,Ô{ æÚ¬‹ç$Ù¨½Ç}‘Q»£þ'”’£e8ËM¿5vÐÙDŠ~G†ø²£HØjGÖ 6'âVT–)%¾¼XcM»hì&íç`:’ÁÿeúQ³‚É|øø,˜Ç£`úØð(1íÉ0&AŒø1×ñ (¼ƒß£ÐŒúPÔµ' âÀóć÷xë ‚q‘á.ˆ§„£×gÒ‚ñ`î…¸™?Œ=”gHÎ÷•Wnæá|ùà!ò©@:¡Ö¦Áô?‡”?ñé;)”ŠCÆÁÿäOcˆî½ñ˜Þ ±ZfÔw~ãösL²{ÄòŒn.Fóið3 |$ÍŒ}Aa¦†c/˜È˜MyïÑøêg1KüßAˆ7×&åk–ï} êžB )*ÿ†q 8u‰äÛ¸¦ôÛ¿—!(ĉÎj#DÀgz+ˆü¯­W ¼0ˆH=wál"Éuv×FDKüÿœˆ%h؆âÔ•T÷HSâCЀ7TDž Cˆ˜Iu™iôÔ'¨Ýo|xˆücVŽUÿ]ñÆ? ×‘ï‘ïTä–uÝàJ£û·ç £È-•&‹j_ït×ÎCÿx6 È ±ìÿ¹ä» ’²›b¸_¦Y=SptµkT3ÔäæÌy!·–ÂLŒâpäÌ’­X¸Ã¼™)ߟšÐ˜j´+äõywÁ¯²ÆÍÉ…;d»lˆq¸ú†Ÿ´¤U÷µ¼ZwΟӒ‹«È·×¶b«*ë6Ù¼âbä•Ɇ¶UF!o×wò v5±t×{ry´{¿^©Zã§]ì ÝžÖ®Î;±‹ n ±6GÑ+i¦â:xlÁ'{A@m#ý«9A”•-u´óGÚ+b¿p¨½(vÇEý9®†jR•«î÷Zÿ[€<ÐËì endstream endobj 233 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰œWÛnÛH}×Wô£X »yŸ ÄŽg66Êîƒ=ZdKì1EjÙ¤åë·ªyI‘ °Z¬®®:çÔ©7ŸÈÛ·ož>~ &y÷îþÃY¼yøl’PÁüGT˜.(‘ðüOx¾W‹ûÍâÍfcJ6»…I6!|ksƯn¡&þþŽrB™¿“x¦o¦i2²9,–dµùgñ¸Y<>቟D5MJgÃÙ£áÖ:ˆ«Ÿ˜O±c‡¸žáÀ!~U«Oÿ"Ç<;ÉH(ÂI,÷qr!»D|“[L Š‹)Õ­*®áØÞh±LáiDÄ·B¤Jná¿ø~—š: Ïð|§þò6üõ˜É´ ªÜª‹*ÄáJIU™’ë¡vs¦Í0Ìš™†ë9dM jA ãEb[î÷2Ý%e ‘0‹„A:X›¼]'Jéc¡ÈNð¢Ì¡2 “2¿Mß™F‡9Up+@´YÖlWÝnàöõ%˜SdŽÁ\ÓÒ0ykšž­ww‡áŦ _ŽXtæ’Hîv"ÐŽW™FŠd;rm‘šJÖOv¬ ´)ôÏ76?JÛiÒ®Ôæý¼ü’&ò ±ËiyØŠ|ñjí/!ë¿7¥íÿJÚNà¦mZöÓ¾!I[îOy Ì«ºËr"xwŸªtðK)ƒŽØà—ò¼|_Ù2b‹pµv–e!³Ëf ¯i¤«ðJhÛ(•J¶az:¹+“d]«\4ÄÝDÍžLîx(¼ÀY& Þ 2HH ‹D J©€‘ÆdÑgFëd;™‹Zçú?ô ãSvbt¼ƒãÀv‚ Ìýµ2÷\ÉP \„U±tÄ]„`F\ŸÒ£¼õÝA“Ÿ>½ëØø7ç§ u±Z¶o#ðk‹¢ÝCw^ð\0æ9 ]äò;BmñàDÜF#9).Gq^‚ð(‚Y¬à3 ^}8Ö:íIÖu¸îµâÞiƒ‚CÍUä@P°n—ãÁœ f¦m6yEuMžoe‘ƒÏ¬‚ÓHìdZ]¹ˆÅ´ÿX×ñº‰>/O2/Jž€\„1„ìû#bµö–ºF(žWím“EG§kÚQ‘ç¥vV{L‹“2]­í¥ü_)H" èòÈܪL=:G›µn#÷óÚG`»©ÇE©1–úeù˜õ?ì—U÷2xŽÎ: u‰­à*6 3C9Ýjœ%D;ge¡öÀ¡s@h‚õ Ð$ØÅk–޵‰6ú×M ÞŠêëü»¾6þœƒ©Ÿÿ·~>£©0a[$AÙ8@2¨c·O0 @ù˜¶üÊS°’5rdŸóHO„ •ÇLi~G„±7îÈ1¾€Š¬FF4Ôìü ´»T¤ÁaMB ÊùðôžÔž¾Ûº×Èç/Õ-qâú¬ø¨2(¿1=ü½Ÿ˜Äò¡ç 1;§zþŒê µ©ݳ ¢{ƒ¡Hª‚S¾!²Aœ$Ü–dE¯æx€Æ¹gÕ¡ÕE P- JwÓ’áìºB•ÅG_3¨=îFY €d6p ýD•ÑojúoâäÌèõh¯=š6ÔdVkæâ&Æ¡„}-æ'#$Ò9yÑÒ¢û,VtYtÕdD1j/áž•×R‡ÄøÝò7Žö\Ðn†\N=«:uíSS-ƒ Àð°Í®:àšT6áz9›Ç|A5”ÒÖ¬ÐÊ4½pR÷œÖÙÁî"t"JaT‚|ÈFR Ãi$›Õ!<ÿz7ЭA¥@‚*ÆOêÙšY¬ŽýëèWÿ$E‹ÆÇ—Õ´… ~Š”¦ƒ´ ìYV²q—?1Š»ñYUazI„ø`Ñ`ˆÖ#Yi†x"£iŠQ·#ÍC"¯µÙ)â<+÷1)ÎÝ dѶë:N¯Ê'žKD›úm²ã ‰.lÿήmP`æ¼Ò±Õ¦šVã²ã?™K˜ÃŽQ¦]#llòðu›¿~…»ãæ'*¿ñŸèµ–TÇLËBBÉ¿ 4èAcŒþó`-ô)¹À×ù­DA ¾ô ÇèU=®#žV#ÞÀ8Ó‹–zÕ{ZšòxÌòBŒ6œ6³Ø³n®~'`ìàñË2ÉÎ$ËÇ&':ÿ†ÍÀxò ³®pÌÒHõ Êõ`·""› "޾G;Ó9P Óþá¾úƒ(ÂIz²ñ%htëÀ®›°+2í$æ°db©Ó—~T8ˆP‡é¨ ”¢]¢çá䨭 cv+•7XÒhŒ:@ÂupbÑÁ¸¥†ë9í¼­ ²Ërý:ˆ/“î0­\ÒUw[ã²ÛfM7Á¯×ʺ~>èŽgØ.ué|æ¶¡;űP*÷º·!óʵCÅó†GÕÚBÁ={íNº,d®,¯”Mwv+`÷©fu4Í«Ÿñuù}Q°ÛÆþ_€9>Â' endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰œWkoÛÆý®_±@ƒ ,š»|·¹bÇms·F,À¬À ©•ÍZ$U’rê\ô¿÷Ìò%Ò$ã^±iZ;;3gΜ\²·oO.Î>~`{÷îôÛœ],Ìñ‚þ±Ø²8¢D²LâAæó…­©¿ïÒhþeùßêV»¹Ô¥Ko´¤`Q¢"gò/î‹ùÂÓ¢4aás¸•,$3éœkY&ó]š¬£äž)sFmˆ ^º¿à:·|ßgË3­HYÀîàË#îÇÅóNãVȼ˜/ÿx‘®’ å 's)Y´aAò|`"gt¬½Ö¤Ï Sw>¼]Ï´tƒ€ƒò.døÑ“Ôû¸)SÜ‚Î-ªƒ­Ï8Ÿ§ÊÃ0ØnËËP2>P22`éŽ[]›Gñí]öx[GÖ/4o ­|vt^½Ñšhç®¶™/„ÖÍ" X¾“a´‰äº l¥¥™zÊWså8^°ƒ÷aµà†îYÝ@)G@F¸GUqK°^£º9òô«DÖ`3H€† nëä4F=Umî’Ñ2þp²ÉÒXÙÜÈ"|`»‡ o÷  ”,ÂÒÁà¶öÃVë¾_úïöý¿Ñª„Ì}APmöc™‰óåìü‚º²íT^wj§¦žVsLŸZÍô©Õ4åÜ€IÑ5)J“]CÂÓϰDkhQƒ7D¯ëFõiõÄ…SŸ >ʼn“7l‰D©,ÀâNâE•kJb"ÿ¢.΋lªŒ¼9sÚtÚrÇWõ0|æ ¦Õ‰bÀ²5hy"ÜÖ¾áè†cø%±1´ï[`¥­šîÊ÷1;bW×ï¯>Ñ„àçø&Vs´ÁÑÑháíá* ¤Þö-Ýœˆ‘¤¯ÃÖÆ¥—gÇ#À‡·%6€|7–(gŠðØž¡»¾ÂüDÚÝAkÃìÚ5ï¸*ÿŽ2ÿq38L¢ÝKbß4Ù¶]*š.µ”aGÂ6xÅÏø°dQ²&k{Lw$Mïwù¼#‹ÆT—š[Ī’4Y|“Øs¿ÙD¡$ö:’‡Lò"z_· 2Xqo¤BÎÊQï·EDLv—ͮ֙[ùñ!Á6¡óÊáÚl—b ;d¯qTah©Ð÷QŽ=Ázœ†D=ר0Iº8…aËf¢•ÑíD:“4‹QÇlŸ°8]Kà³½¬Ñ¦_† Plo*F_Ë»ýý=ÕŠŒ€¨×l—¥OÑZNÐ;·u׬}¯êÑœ‘ÕSø¬¡Ùû74ms®ƒ*M{ A®v˜*µU¢G^L¶Ã,ß+IiYAög@ï´©i¥¼C<êŒM <¢›,—š-“³MÏ+n¼:XA L\P{4J|xе¦<Œ6| fy–.à›;mOLä®Ï—¸†n• ˆKNô`•ÐxŸÓº5+ÏÜŠ$™ïӔȑVŽP.v2àŠY,ã4{.ýªNu³bzåu‡iéF|÷ÌÆóäëÂj z£­iŸÈdm_Yïà´2WüpØT.YŒS57_M_ ˜šð™–4Üú¿øº½Å5i.8¥²y߬3€ö1 Ó$ÇüÊÛ¯Á5wÊ1\g¥î»¯Qñ€“”ëDQ{ zëÓL[dú˜±¥_ùŠÁ£TNc1‹ŠH®?iy½†YvícšÀ£]±Dg÷º¡×²žî;-”Ü´)Á‚¹)*%ÕÈ3!j¥`ñê,“î#êERfUN  ©èx5ÿq´Òö†† k š&ÞwF ó½MDÀžU‰¦ÞJÂLÈÜJucùa-7T”¹ZJ>þŒÓ·5™Û¾¯1¬håŸÔ6V=Óþ5¶ˆº¯ZHÈÇ0LgØ“µ'¼ï tw¤šÿûÎxcëà`n„ïé¶c@Ýt6µÚ±Ïpi¡\©þ èAùX½€‹Í‰~˜ËFÛèÀèß²¹Y¥ “Å>K*V¿Zþ~y{úér9^Ls”±&ou4Z 3Mns©huqCÑùi,Ps˜Ë&n²¹.»çUtWg—ç·¿¿e˜•&n1!„#Ê€þµ;JIÃðžîXŽË'‘oN)Ù5gvÙç–î õØïÈC(«'(Xl}Ñf#³ñ¥€¬(¬QP@¢l‰J®©w`gª}‚©·Š=©9¬B$/H¶Ê9×hÅ*°cоr°côWDaéTƒ b»_×ê:’û-d0Zntû0G•ã‹íÙ„HਠÐ1ùÖâu endstream endobj 5 0 obj<> endobj 6 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 7 0 obj<>stream H‰¬WÛnÛ8}7°ÿÀG°’ºÝ>4ÍÞ€.ŠF‹}H-Ѷ6²dˆRÓì×ïÔÝ’ÒEÆ Ìáá™3gf®nÏ,Coß^}¼þýÂèÝ»÷®Ñêêú£HÀä?$¢lEPÿþ~«÷áê* 1"(ܯ¶ØÄ;(ŒüD=>ÊS¡@ËŸÿÁoaUÑàG€‘‡}3€c…§Õ9úÌÏ){Zo}*ò]ÊOˆe1z_pöpΓ¬D×)‚‹õ?á«›puó ªX®éYA j¦íÈO¸Ž^ðÕ~u5x%i^9xVø%ô§E›p¸Z‡ÿ¶®>µ‘édä.ž˜,ð8&pC£Y“Ñ4Û„´t[CÌÝ>1-ø¦§.ùûÈ3ÄЮ#3¨d<Û (ÏÊ"O«²’XôvsñÕ žéú2p¯Œ"ƒã9*!o·Éé·6Æ™EìÀM„Ô]¸‘>¶m¼-1‰-9†°wÿÆ#)ªLò ‰êÄÅFÝ#؉÷ d ƒ[Žë›Ô¯‘>&iŠvb±èÈc¥)õpÀ…(¯ÒX#Õçúˆ !¶€½ ñ,N¹(Íóó=&åQ!“gS:މ]Gß~gˆäT¥¬Ì ”ñ¯¼@ç"?@L‘d@¥äž"ÖŽÓjq[’Ô‘`®·¾ÑS=¯ô)ÁPWŠÒvUéLljR~œV%¡R•Žý¬*•¬ ~â§]§¢‹D×/„J‹cÉ0Ê÷=ý¹ò úH©<)3Ý&Ižê+kK°!uÒˉÝ]¨ÌPÏ´œ±€ ÁK!•ax‘±íSv¥ý¾WÐ2þ­loR²iÃÒZ¸+ÿRÑKÉ…f%o¥6màäU¥Ô,ÔDNÔuûNÊ;‡"Õù,€ßJKQ$Ù‰RÎjÊ“ìB¾m©CŽ4QÜ—ðX‰ <Ó¶ëï×Åß–QIJÅ׸¯1N ©¶ôÀ¤€ÓуvU‰”U4ÅÜ w:‘aÆLÖ[Û``ɾµÁT ¯Ž3²–²êùh?kæ\ù¹ßݼ­9¶%K.X,ko¡ÙЖc{î h”âÀ×Ýü¯lŸe•±’§O2 _Hà9…G‚Ã3óìžR ”çkÏ($w¬—dõåÏ ˜àOe"^ú}Î[DåÓY¶ ©Òöü®Xo=£ùŠÐñ”m«LyU…2uÈe›$eQE²\gûÙË-TO®*‡Ï=ìfƒ>o&›qëŽÛ÷õ˜¿{ʫߧ̦Å8v<. MËÕ*,‹äpÖ núÂ~”æ¼çeÛ5Ÿ±á'„ù|F¶õªäñ…[eèBʼT]Êž[7`I«HŸÞÌ6>ÿåUb»$L,{±L‚~àkúåbÄ#à?u@Û1}—X– øcϗĽƒ¸ÏÕ—Ù4´_$ùýìnTs¤iœ\¯§š{è_eë¦¥Ø x¿ž{)ÁÓO"‘P§nï¶E ¯Ë{öÑʸ»§^ƒq&YÅgá8“Hˆkº6±ÜJÓ)¨³2P}CûEÍh};Ò%Œ^NbV²&G I¡¯IŠåðƒWðÓb½0)–gÁÔ_Ù"’‹çEE1 Ø~H‰þpþœ—ògÛ¦í~¨/zž¿Ytî«È‚QÀ§Är^_wḚ60Ïœ]žA î€}b-š=™n#½×¢fV ÓÎ ïŒ0GGžžå€!—6µ9 ®ÚZ3¤·ÃÁÂjkTåÈèÚ}ÁG)“£:ô×Xs§¦Ë.cøL'ùc††c8ÐnXQq,Mâþ\¿A wûõ–êõA <虽j„o0ð VÀ4%DrÈp ø™0e6Ï¡È׫yaÇ[·†/ßÿ$‡:·õ|?¼¤‚2M/÷³fûÊå8È‚šý gQ„’zys½FÄêÄž%)PYèó „a0Nö{^ðÚ¥v§Ç f0¤=·`¾&¤I-KJÓ[v(~ÍRÐ]óœíŸ¨KÞ_hUg³Ø%¡¥MŸvceY°â %1ð–ì=¯ÆjÜõ4±{m‹ó3ëVGÐüX$°žB8s‘j^ µ¼d›gÁõôæ^â8N»àÊ’‘;fÊÙwÛb‡dlx0çw ª÷ˆõ×RÚ)fMÇ)¥Ì×ÚkI?[ãmÅô­°¤ëË®xø"Õ¾8õèFׄ©¶=÷fV8ä{ÔØŽÞÄóÁŒ)^vcJÔx1.LÜâÚ yJ´#p%bã­EÞP÷ü;ØøY~€æeèŒgÕiz¸7°TVÌ÷ –I9žÍìÔšÄ8…ÆÁPZÌuÞ`…ÆÅN‡f—”èÄăÔxËc¿| ‚Ñ4JåT8 ©úÔ-”š¾Oõ@eü™ƒ+ƒ¼ìgªÈ§–Ö~dµ€:¸±Þši/yQóCž±´íIMä.Öñ¶Ž6¨«Úi*¡;OœíU"Ž=C˜àº¿4A›£gúv N4¦ ؉“ ˜ÖÈe·:ÒÿÆäذ‘no?~BâI”ü¤& ûkº•eúAg;GtO)Éà?ŠÎEA·É‹]x¢ïQ¢#Ó£-˜f¶%(Ê«4nÛׯjÄô¿y ¿a endstream endobj 8 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 9 0 obj<>stream H‰œVmoÛ6þn`ÿá>ÊC¬Ô{QH¬KÑlÆ¢ ’¡ e:ÖªW¤’¥¿¾GÒ–åT2Á, æ=wÏs÷ÏðöíùÍüú¼{÷þr“óù-Lâ ý™U 9¾ÿ€ïää}:9OSÒõdF\Bˆ‡ÏèG@ú¤¥(ÑßßðWÚe&~%"» žc–çj¶lÿ²­óJIX× l›:RÖ„iúïÄ`D‰ŽL=—„:âjâ×­fÔø½¼]ü}YÁ¥>d3£‡Ì&‹&t $Œ°JÂ|r›—¿Úª2¾å˼ÈÕ3¨úˆ%ÿ €é†Ä“½–¥(—¢{‰Ùn·0òÊûéK9¸i K.si%îÉj‚ö%½sñµÍ1ža_æe[pU7»lk£®Ê10v¡¬5h)° )P˜×•Ì{&q ˆßé»VØu]õS^=@Qa¼g&@ð"ÍuS—À+ ñ4t–°¸\€|–J”ol‘z¯ìЃ….޶žì 0°©††‰ŽÏ܈yÃÍ¡Q(Ó˜æ¸sàÓŦ,bÄùåÓŇé,qFk Æ¡™%RCûÞrĺ-Cjë»ýíb¬Æpœ<ÀØBì»"Ä»Ú>Þ,vµ¹36ZV4†6Œm±Ñi•âqª~ð»! Ò­{äz“õÂþL ¥§Nëv¹êµuÔ!XsÝ(î\ïÎyÊ‹*(ßQÚ•å¶iq6ôJ4WGN*^ezÖCçl:óœ¾gw¶ªMǦC8²ì¥È8:>h B¿±Nb‡¯V8÷èkXü´¹è÷Û&G3>Ô‰ƒžnî‡}u€¶ñ€x‰†¾÷([‰$Uhí¥ÁX 6wœïüSàʔ޴™qñŸÈZó´áÚ“Êm!® ›/ï“nuÙxÇ«ç®~<Ü“Qó0Er!d·ˆ^ŠmmUnök´³=[Œ~3wéÏ£<ÔJ‰ É3Ôk×.ò—™ö:¸”ê…kYÀDÐì.×èµØ(¸+V5¦•Ë7cíž¼Þíü8ƧìäQ2x(\䣭QÖ[¬øÚŸ;C•·ßüyùyùe«>gE#·Ùîÿ½Mk›#ꡃ°E->stream H‰ÜW Tgž‚ŒB;Û™c{ˆÊEA Pöͱfo_ABiü.3nÈúsýA¯C9Æ ¾b– A†¤Bù³šY޲A˜V‡É00a ô5´An›a¦“ÉâÔBû£ìîS›m‡„øÏîœã{ú¶" ò@Ý7ýD]ð)Dº/ÁCÔ-¸Çõ /WúØ›áÁ¬q ®BÕ%&ƒ!õAûq={f˜‚Nãò"¸ 6Ã=’É`×hÐtTÔG¸apI ’Ð}«#B"ÄŠàÇúFÁ«þØ~Kë\kæu.K8—R÷,A~¨Æíû.êf6ÂÆ ÊN,¼^{è+Ù‘5‹Ëš†4iõ£Þ/¹2Ø’ëéôm.+ƒÍô×ã„Ekɱ‘GR@…Sb†t@øŸÀì&‰T„†÷L„ôZZl8ÐR›ÓbÏZœÈ·˜p q8(é4ª¡RƒTLžˆ¥bºI@®P(ÓuÊ$n ‹ ^]<À;v$*“F¡#QxM†b¬4*Zú³ø¿¿׺¾gÎà ,×bxîåL— 9%wͳDb‰+p'wW-¯¿÷ÄóÚ¶¼öcÑá»N?òzoÄýϼúµþå·“ÿÐüÝ£²Õ BoÎÎô#§Ïü:7 ëp棰ºÌ©Uì.±Ñ?ÓØ”[y&83òÌq!g^Ì—•[ÒÆß¸\¯_5'h­µ´q|ÊŠé ›bÎtz‰O5Ä®a²`R¿–,È+Îí|Θ“7J:ŠÎly°­°“Ó¹|tnÈ–ˆá—?àåÏD M^mlò¯-y°w¿pï ýªžFåá Ÿ—s‚/bv)§v–WÿeBÅÝÇýÓ¾õX²ÆÏšùŒ'[ÑT¾î2Û¹6|¶aÉëüÜÕ›dG/¯ ŽZ\¾ði–ç;O>…ùÛ G 3ùÊõyÅí ŽäÌyåMÉe¡w„Óþÿ’x›tÚãxð?§ñb§ü7îôߢøâ|x¿8Ô—žðxbv 'ì8…ºª‘Ò‹`Ð)]g¸ÝP¿¸"¥âBƒÿTË^±±‚+mny^öqòY,®òÆiî»ÕõfN¾õ¤Ó¤TïãÛÑ6ÄÔ‰½.ßs «óž0#S·èÔ­{E‰müÖÅû¦>ßSÒÚ^ÕPŒ%úYO­ÜÁÐo<ød]܃âÍ™›Îã×>¬›¹öçRÍï‰gwíf2X¿’жi«~ÿ©å‹SEÎcÈà$0a{HÀŠùûqØ )ÛJsež>ºtewÕõEµ¿k'ŽóªÞq~Ñù€¥M¬k^¡zîwªOS>;11ùô(ýàæƒCãÅ¡Q-k®þilÊ÷m¶”ükèFß’–â¶ø95O–‡K#žÞ¾¸ãF†Ü™,ÍAÝ^›àð­a1L¦_av•}îŽÖ=Œ·ìÕ xn_ÆL˜Ð†_9õ7G(•ö<üeF(6N˜,+Ð:²©ƒô<£ÕBšq‚ ywJŽBGHcPôeJÒbT´,V;u3Þÿ“&£I=F£ $ùЄ†“à ;°ƒ´P¢0R‘®¥×pN 0 ž-Ñy-IÕ%ѹ#ƒ&ôø‘%Yr,\K «$A4ƒ4‹‰pB/½ÁjÉ2P‡äGIù¨mÏ03´RêO žÞDi†¥G9ìR?Ô§ç(<4x–ÍaÏ’Fi KÐë^9:ˆn·/æùo˜‡ ^¯"7ÃzO¦›Á@*NÝœõ÷›ŸÛŠäjÞGxn‹d vSTÌ•Óæ¿Êº°·Úª:ño´B°Ÿ}샇ǜ¶Ê[ǿ؎®ŽÊœµgˌМUW ¾ç\û¡½êq=ÿ7›>O˜ç¼ú“cŠz¶ÃW£\p¿8í£×[WÄùðC·ƒ¾Kb?0Îå Ô©©ÞVZu6A•9Ú]tÇK¦ßmnLTnˆ—nìh[Þ‘qD´yãÁ0u˃ewYCŠîÄmùikú\ŽÍxw‘ lÔ¹ö@òwì—ÃÞl^š{dö®õºàoù9³~ZPX¾-›·uÂÓ."¨³ôýÃÆûÜÊ4„¤µîŒËº"ødêÑù¶ÔþÛG{ÀBÞèæ\DÝœsÝÑy[Àf¢ʧ_}Ùl“SƒºÊh‰Áv• sJüŠªþvBÑe^yÔq{ü|÷zÓ¡Üfü*Dƒh&lã9{*Dé/¿Þ/»þ,¦G £ !<6…ä¹cQ7;¦†G›ºÙ!P=¤&¬d˜™¢œd\dä¿(ŒõnÖ>—›Õ 3[H` ʒm1(Xº †N6œ¤«†À³q·›p0س€…"A a$ )Âb¢¬…<2Ï87Q€rˆeÆAï!¼ôK×K:a0QtC„­‰Âm¸Ã!“0¤IÒ©…‹ä,VƒÑJ3yÕ[ã½i£ñ4k¥ØÝ@€+ˆ <7')rì«8ÁƒÐÀWc*Q²ØhFìò|*ÒyvÊYé-x†ÄŽ@GDó2´rˆs–3E7IillÌkî[­@C#HøCDžŒgI€B©ÑÉ1o¢\£‘«t˜R ’0­"UŽ¥)“€\•Ô§§bilÃVaª”8 §Z%P'ÃWLÛíKÆr@Q«Ó` ]ê$ ÍH¯Tè€NM›ðôJ ÿX©úà1µ ¤kä ¦PB;è M©ÒAÚô˜V›×ò0_åaQ]WüÜ{ß›ÁAEA\Àè(" †.¸ D–a‘UFPD-Ã* Œ²Ì0¢T×Qĵ£EEH­X­jˆšˆ –ªIÜ ór,m¿¯õë;ófæÞwϽç÷;˽Ï‘›Ú"é1Rу@êîåëéþÖfù_?¹B!íC…$x;yú;ëféë• Ý^r?'7lö ôñ“º¸/òÖ©»à©¯Úèäïéà'õõ÷óõQȧt-²ØÝÓSêí³Hâ(ï"ÉSÞ¥àäã­/ôGãÝ<§ Š·û"÷€·:=Æú *?©³ƒ—ƒ«\a-UÈåNÝ~¡›ÃYŽ£<È´“ s?]¦Šè‹‘QñXÂ䱪X]XED…‡)ºÁ!3#$HžŒú]Á¤ŒN —ƯTbΤ!áÒP> ëšD/U††&Æug`„*.¦+g$IÝÛ ŽÀHÕYàî`-9h«žþߤyO´*Re!Ë8¡«$R.ã¨L-S‹ôƒ7¹‘M/åDLvXŠô°ªð‰q7údo°ÑÝÃfëêR¥UF뎌 )»¡µàîåÀÜÍÜKyG³´—Ϥ>˜vs²e‰†\ÃS]CŸ/D6r»Në‚,£êÿþý•ÃÙ¡…ÍNǽqY’™ýg—¬|ó'Âûj lä»qªßÛ ÓÞ'¼îÕÃF†ï2›Ó¦/ý—0m/Èt〤bývá…ÑNþA•¡–§® Š™á˜šô݃›k^õ(ljH¯"}ì㟥݊˜!^.6Ø>^Ÿ-,µ*Jj­LŽÏMÿ~ÙΧOÔñÅCK˜¤o¾4.sjư𫤙ÍíS³‚RîÞLKwØkr‚Ì·Ý>øLfèX²~¼Oæ·_ÏØî±ûúÓFïÖ-eñº¢F¸+$x¾Ÿ†Móî_¶"¨á@Æó„±ˆòbèwy©bU0¿CÚ!ð[´.dšž>©U÷>å—ƒ ï cñÍv€)€p÷í}_(<æWƒ™v•pËÂùöî¾”`+À @-tÀ92 |á¼p Ba ýÞÇþ<8çá8CP0!i Š` L„ °fs&B%xÂC=`QŒ!ö‘[à8‡¸B6Äá÷BìAfáXŽ«ï€=pþ?À(œÑšÐõ/„¯À +J(¤Âi¸Ã;ò›Á à(”B < ÖäigO„J¡AøjY ØBVŸØ%8î(\¦fì `"¤ .Âh´¾ Q×À\ë9‘’J°ík!V(C¢Íh=Š¢ñ†8Œ#›à €¢¡Rú ÕF€Æb…›ŒöùcÅ[Y°QB1œ€‡ä²’\!Oè ª¦Õ¼¯Ø[ì= ºó[ÁUxŽk „qhíbX ɨ¹ ¶Ã.Ô,Áµþ„ÒÄ–Ø{âNüHÙD“—t2ýž¾aƒ™›ÂY0Kc-ì•ßé£Ý­½&ø ÉÈ%AÎ%èI'Ĺ–Áˆ‡! Ôh].J>²W†RŽ|V£|·áJ+<„GsÔFÒ54æÓrZA«é}ú Z9‡ÉY<ûˆ•±Zv‘]gÍp 8%Å%r;¸rî[®ƒ{ÊiyàÍP¬y%¿¿ó€ÖC$Lì„a«ò9ƒh&‚âñE¯†âމ¨ÖÀZ”än#"Úû;{'¡ ¾Æ(­EÿÖÁ5hF|·¡^À+$G‡Ï˜Œ#ïäwqEYŠ~J"iDMrI!ò\A*QΓ[ˆR‹h ]A“hÝJwÓ=ô4=O›Ð¡'F2WæÁ³ ¶‚%°]ìcö ÛÇŠY;Ïê8ÊÍá|¹8n—ÏàNpõ\#w‹—ñv|J9_ÉŸå[E†"SÑt‘BT%é¥èµéiá ¨‡ ¨ìŸû$‹ !ðicSÓº„êÓ&¢á® ôÀ\|.î¶?£…ï‘ët&YÌBÉRäOC"Hìe£Ù¶øX¢`¾$ Ünø•ÿ”|ýœQ>‡u’W´ VB.]ÝY*’Á  ‡èŒ˜t˜ Vœ 4ÑÙÜibN­hµø8©{±ˆÍfsô °uˆÝC3z¤”¬óç7ò«-¶‰ì ÿgf<3q.8&$NLð˜ÁÎ&Ž.¹¹‰ÛÅ$›Äz€mí8¦ ¢$Z.UJ¡tQÊÖ,‘W+ÁªuU¡ínvÕ¨œÕ–æ­}Ù'¤´*}€riJY­Xªn îÆNH¶¨íc¥Žýÿ~þÿ\fÎÌm¼·¹wð™pŸüAz «[äŽ>§ “\~Z ï4.JÖs—Éîų‹¿ãý1©äþ°Xºèãü¸ãödg¸ëð.=ý»p ®s7a>5âúó)Þ{ßÀ'Í^xÂãýÆçÈ„·««óKžŽö¶Ö–m[·47mnlp×»êj_¨q:6©íŠmCõzkU¥¥¢|]ÙZs©iMIqQ¡±@–Dƒ€o­PT{¢ uF©àTwît3Y¡"¶B¥ ªzVûP%ª»)«=½èyð žÞœ§wÙ“˜xÜõJPUèÇUÉ}ä/TM¡t¾Wç§.£`·c„´ŒJ¢JöœM£ì/]hô«þ„Ñ]ic!²…ÈÑ u"M*:‰ÎpÁö4r1VE«Ô@VªVåÁØíˆV»]s×Sâ«ÃÔnºÆ¥»€_OCE?•ô4ÊœWÒõóÉ×3&ŽºŠFÔ‘ØåcËQê¼ZñÍ»–g"vnöGέ´ZùdÐ2¦01™<§Ð·"+­vÖjö±œ£'šìÁÔ¯ã,†Â f㦴%S˜Ra#a£Ê/¡™&zH¡j·:š<ŵ©JRœ´ÏVUyç²· *¨$‡"ªvYU-XŸ.ƒäàä•J¯R¹Úâ®O›Js›.Y“gŠŠW2‰e›ÎéîŒ .Ï,a©/⎠J\ÁJ"*Ž©•5‰VHÆ[Ñ /`Á£þhÒÔÎô,ž&UI~¸ÔY­‰å5¢Ãô0–í“彆ö%žº\´®ŽmÉkŠ5vêò6wý‰ çS'L œ>èǹií8ýv;[àó/ £@Ï Dr²ÃÖYð6º4ÊE™e~ɲn³œY²,‡GUÜÉWñüXGeçò©|mp´’òcNäì¡°ØQ‚Éh~nCC«¤œ½uÙ–çèZ„·ry޳òº7åeg&DЍàÀ¿¨oꑌ$ã®Ô5D顦èÎ\«íöÿ2(“ý„EéäYX¾LÚîZ-w¬’W•W”ä±`ÁÉ…†ö%“Æ•6`“&>íÄvïÓ÷Ÿ4ÈÇôi\y]>ÆS•]Ÿ¾Ú!fà®á*ć0â ìÛ`'ÚÑ6„p£í ´9ÐÿHž¾Áµe³¨ß…øQ#Ä0BCìF| 1ÀµÁˆóëañŒò Âxï¡Ì°6"5 ÷ J¸5¢v 7@Eóo1AòÃ)(“ªYLöÏ(ïèóW¬á(8… c; SP޵ï@[«¡ºÅ˜ï”c??ÿD!Ýe ²à}a“ˆþ1öEÁ;ø]8¾àæ~~¤A´¯C4 ?Ä1¹àäYý-ÈkHÇЧc]hßóéÃZûùOa?ÒFìw?ÿ[¸A¾—‘. ÿVá1¬%Ÿëy=W c¶ã\(œ(’ÍHÿ†x,ï…Zé„°ÿ——(¿²¹Ã~,?§“óøøŸÁ¡ü3lb¹d€û ®M†ì»"^Ä5?nœ›¯H÷È«8W}:.B i/ö׊hAtäÑn¸JŒˆB´‡QÞ%BœA²A3Æ6`®!¶7жëÔ‘¯w¾~b8¯¾¥xqÔaŒ‹7Cx`ð}ã~çè”\ƘãßÉ5áwÐ)îÀÏ›³oòfîåùïècÉeXï[f®NÎ ã¤êíKzÛ¥·¬ågm¶ ×0û6#õ³ÕµH6y oWÙšjÌ6O “+¼‡km·f*m·ï×4Û^ó4ÛÎ"'Pf~53µ¶ñšñ¯wüœÐåå¸ÊæRÙ›!w~±§¬ ¬ %•!¿ò¶I©_J©+RêkRjDJ}YJõH©íRªAJ¹¤”CJm’Êd³l’Kä"Ù(˲( 2'ƒ\–ÉÞòºØÍ_&šÖ :oâXËnt|pDæð뎮åC\(ÜM[]¡Œ”¤-®•ú÷GÒ„Lk¨¥ÜkC‘ É2Õ”•Ús@Hvê‚5O5„è|Bà }V3Ĉ*ƒÚM¨9¡¡n ”Ÿè²t™;KÛzÏi¢ùÖõ첸V^¡þÉÀF޳/rìŠd{SbÚ0jSº6Å´)]k©¦Cá©Öh3c²Õ¹â»æ=ÉÞ¢j0ˆÒó'F-ô̰¢¤½×ò/Îèp|”ÑX‚^SêUJÚwò9æ“ÌìSi8ФOzYŸ×TcmúÈpºnzUºï-¥›ƒ:2ü¯=fÈ0벎eì›~NÆifîc§YÆi–±ÏÛ§g Ž…»I¨?’–¡[ÃÃG§W¸B#.UÔj׺ËMúºuØ-§­ @Þ…B<‹‹ð½®ÁLnŸÛÇL¸a˜©„½òåM–Óvë‡äݼɄêRµ\Ç]_¸Ž² ,Á±V2—çÎÌšmÍ.3;‚ðëoc\´ïQŠ£Î Äy0І8ÏsU’'P)×¶Z\}¦GžÞEOŸé±§×´è.Ï¢‡¡i³½Ô^êÀ÷6Î4G•–Jõª ƒQô û·‹¿K -¥RAÅD$f/±I£ð©%I…l}—NÝJ炯à têÔ±kõäjÛAÂý;÷ÜïÜÀíÍ9×0Íz…®YU-­•3 'I¬œ ¢eàûV§=(ÒÙx¡m÷ï.R< À=<Ü1*¼Ð\L$·±Àœ–X 7¬"æ™÷ˆO…¡ˆhÎW™µ÷N|©vÜLaÄŽÆã‘ë1îõ0ùÕQ9dMA[œÐŒgBîÈ”Ã÷bZ„%½!ú°ÐáΔ©“b†±¨ÙÔÏØ!u5÷‹þážÒÝ_çŠZy¹ÆÎøÿ×̾òEA²~î ë×sÿ¶xùm” Ÿ»ïY|³6ÛíÏ•ñiX~/72Ì endstream endobj 11 0 obj<>/FontDescriptor 12 0 R/DW 1000>> endobj 12 0 obj<> endobj 13 0 obj<>stream H‰T=oà †w~Å­2@h¤t°XÒÅC?T;Ý œ¤Ðþ÷b%êèÞ»çîåø©}k½KÀ¿(˜ Î[Â9,d.8:{ Ö™´Eõ6“ŽÀ3Ü­s©õC€¦aü;'çD+<õýq'ž’Er~ÌÊAž²Ò-1þâ„>¥ÀâÀøé]Ç=!ð >Ä~²Æûmv°8Gm´!Ä‹*ÏñUzû?Ïäº æª‰=ª¥Plƒ)¤T,³[UéR~xwe¢l¸®¡Ú*†œÇû¦bˆev9ìO€Im, endstream endobj 14 0 obj<> endobj 15 0 obj<> endobj 16 0 obj<> endobj 17 0 obj<> endobj 18 0 obj<> endobj 19 0 obj<> endobj 20 0 obj<> endobj 21 0 obj<> endobj 22 0 obj<>stream xÚìX]O[Gý++õ¹¹;û½R„DLQP’}²üàµJldLþ}ÏÞ=c'«¦jUUæîÞ™³3sfv÷b²ÒÊj%Ú(+*û ¬Á('e­ñVY§ÄŒ½’`DÙ $¨GeÄa>)cak³2PN+ãµWN ]VÎ@f«œU&ÀÈ9È$Êye¢^€ Ð^è/yè/kQxJx9cŒ¥5œ(®iàxϱ¾÷Ñ«!É  *_BÑQù €€mö*Àe§K(.Uë±N^¤ðBÄ<ð¢Á<ðbÒª¤"!)pÙ¦T^n,)(cƒ øˆPtÂØ!ð/–”DŒR‚e ¬(䀸D€\|rŒe¦)eÈ©,!@ÎeQröe¦œË ØÓ®Ì€Þƒð&¥’áwXfÀ¬1eÆâ!”pj¥Ì 8,2#…õB˜ˆäRL© Tüë×ÝÅÃm߯–÷—«‹eߟ-«î]é%­ÎºÑÍôîîÃô¶4UŸN—ý|Ð+ýµ9ósÿyõ®P¾;[Üôƒ‘-*XåÝXŒÐtž2PFÊD™«ÌšR( ¥¥$^&^&^&^&^®xFkJ¡$žã:‰¸n=¿sHé©ïéW¤¾§¾_ëÑ/O¿<ýòôË3Î@¼@¼@¼@¼@¼@¼@¼Pñ.£e°Â`…ÁÊ:XMPMPMPK§4Ôë1ñ„xBa³š/V=ôðç§ùÕãàü·)öµãÙõý²ïNærcêâã!~GEž”‡òç˜3Çœ9¿¿í—w—ËÙíªºs~ÿëÆpµœýÞ/î9>stream xÚìW]kc7ý+‚>7W3ú†e!›º$mC“Ò‡w}7R{1.4ÿ¾çȱ³‰ù¦Ý–Öp¹£Ñœ3£™‘t-ZŒ5â¬ñ/1…#Åá3=ÞxŠ«:UÎE£AðNF3í³qöÅxx°ïÅøTðV@-Þ™¨÷& õÁÄ^M’Œw2)qœMVŽ‹Éþ‚5>>KÆ|€ÎÒ ``3-¡h‚zIÔ jðh¤&sÔ.šÈØ 4‘Á%Œªʰ™¹þHææHæˆéD戧ÆÁœ¹€æŒè…\9Qs±€¬Á –Mý %*ú„¼29`…@fP¨Z@‘FUOz ¡D:4êQA”êX6°«g˜Ù³>´s Ó æèkP2§Àœè+‘Ñ93%Ì…ËAîµÁâ;ËŒoœ(\”¡PˆÆ)"αêÝà-…‹³ýá)ˆqQ((FgÁÌ0Ô‚9 `ÎUæ\5`.UæR5l4jf©´šTB¨g¼V‡P5m©"û“B2Þ׬gô?âU´¥O*̺ñéU |AÍß¼éN±Âî¤ûa6ÿmtÛ#'Ø;?uÍqÔ¼}»4ö-ÆáÅÆ?vg£»Ùï‹î²ÿcñ~:î§ dóÈþ3&¿é?Ìæ£Åd6½¼ûÔw?Ãv~;™ö&Of·³ù•=ÂB—Ïõ*‚ºsí'Ýwýh<™Þ|…nÙNÉ–úùÐN}&¯r_„+Ôýº;®[ŠfçuO-÷ëØÅ÷r~ãlCƒm쎯x @s½ ãѲĽ›ïÂR…ÙG`q,–Ê#°´vUê^AÄçGðnŸ[Ù©$/‰})iÈŸhK“ð†ªm’dÝ&IŸki©§ÄãÔbœ[ŒKƒ±Úã–ê(«ÃŽÀ–çÇk¹Í·¡Ï? rµó>oøçðÄÅÒŒk©¹¶Ô\[j®uÃÆÜþËË¿9ƒ/£ùÓ÷ÃÙW¹[‚¶Ý-§›sæ¼~°­Ž§÷ë£`y¾®Ìõ ¸>É®·©Ï6„áIÂú)¼g2=5ùd·/ů%?—ò/jþ*öÒZa‹ÝÁoÃÕî¸7ÇqÂÖW Ûe¥ð/Ù`»@m²æ§Æùíh2]tÛ˜]ýC¤?Fƃ‘©¦Ó­ÓyI®$:Œ¤TF"˳Yj“Ù2E+KÈâ*KÈâ+KÈ*KÈ+‹ÈR;×ìÜúvôéƒ÷Ûéx5ôùH,~ñÄÁ:¾ÜL»Xõó–Ù_æ“®Ãïgã¾;›_þºãÞîx·«‹IVíº‹O£ý»þãlÞ×ù:>þ7ó{ô_æYt×µûû\¹E¿¦ç}öo]ôòÏÆŸ ®Ä9 endstream endobj 24 0 obj<> endobj 25 0 obj<> endobj 26 0 obj<> endobj 27 0 obj<>stream 5 The SIMH Breakpoint SubsystemBob Supnik endstream endobj 28 0 obj<> endobj xref 0 222 0000000000 65535 f 0000007650 00000 n 0000007907 00000 n 0000010152 00000 n 0000010408 00000 n 0000012726 00000 n 0000012957 00000 n 0000013214 00000 n 0000015294 00000 n 0000015539 00000 n 0000016796 00000 n 0000023869 00000 n 0000024059 00000 n 0000024288 00000 n 0000024585 00000 n 0000024715 00000 n 0000025220 00000 n 0000025273 00000 n 0000025437 00000 n 0000025534 00000 n 0000025699 00000 n 0000025840 00000 n 0000025969 00000 n 0000027676 00000 n 0000028829 00000 n 0000028864 00000 n 0000028888 00000 n 0000028965 00000 n 0000032954 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/i7094_doc.pdf0000644000175000017500000021736611143604376013511 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—òtrh¼€è›/ªãı]IN›qú K²œÔ¶['múë öùñr:Ó8ãA\»ßÞ€=¿lŠ\éMáÿ„ÆÅû£{'fsýù¨ëÞœ<Ÿ®~9jóÒÿ×u`ûâýæÁ™›h7ºÈ‹jsöæ¨È­mUÑvßÕFÕuÞ¶S¨Ü}~ô*{²Õy¡Û&{°Ý•¹ÕUSeßmwnž*uYg›ðÝlw*·•VmVHÓÒÞ{2©Ê‹Úh÷qhÀ„Jšq—Ó­Ê‹ÆjýºÞû.«›&Ûwt•µ-Ûì]ø|.«Ünw:·MUWÙ ý´­7d—ýIuUeŸe2¬s “¯úî²jÜÿ8ûæÈºšj³ÓeÇâKÇC8¹šÙnë¾—U[eúíJ«Âb~àEÏ·ÃN¨ÖÈÍȹØ×nëºñ4x«ÚMŠ".ì ᲩòªÚ4Æ£ñ"vdŸýìf(µ±É £‡)[ÉÚzÓ¸©­LQ*¯õæì©ÿç»-Ã|Õ¡çéÑÙŸ^e=õº5FeÏ·»&wç(êì…?}c‡Øîl^ºÐÙ‰ }²-ò²nš¢Ìˬ¯eÀ™;u“Õh·{û¬ÐVuÜÌX ë>”±Ç5ja w˜¦ŠÇŒ‡ögƒ…ߺ…Ò•q2 - ì©\weòÂ÷wN„F•¶‚ÖM7ÐA+û×Öæ¦-Mí¾3®ËÔ:û'_w­RÉ>nh\£-³Òüm«L®ò>¹>“;ñ¶nå°/,÷Vš·B6lòAÜб@pGÐ¥õ\z?b3R,\a­Ï~mXðVš7ã˦nD8dB]pê1a ›`ñ;×,cZFUyB«5Î`ä¶®œÊtJï©óÀ.-R³—ã998»S(m'\úJ¶„ó½–½à<>È‚ÀPñ w§>ɪñ 0 {)Ë—TòrÒ½ô¾wúåL”©žì wƒ^9ä .‡þ¹—¬ûÀ.ˆ`×[ ËÁR•WïºëöÚëÅÆ1V¿¹ôq^ L(âåÞ>"Fbó5׆Ðê4à­Œ¹B²]ð,ì»êz«Æ;îøše×TJwn)@LË€›‘®vÚqßÞØU®5†(.àƒÃ V}=±~E߇Ө։ªSáÒéwQ~M9 Þn™ž¢•  æÎ 'ÿ§ûîü£ˆ$"–ÁÇŠwq+»¤*Ý‘Ûj›kUÖs¶?ÒØ~v¶³‘Õúq+0NQøXÙ©G’n å‘Vøb$ÅæÉ`–mbvÁt®dY*É8âÔÅ,©™yšbIøB ‰€¨`BK3Öù jàš02¥q:|ᎈ¸’MõI#·®éüsÊÐè«hs.—„ aTB¡ŸVèf&ÐîÀ#c=80`Ç9=Î5å×Wtݹ€„ûÞ>†¨sec V+.õqÎ*U>l/«™ÈƒƒëÈ-%qˆÂlj©¦}<4ÏY¤ ¶K[ÃY°(Qé‰F‹\ÚgÚ!z虦u¨\v)¦õ uGCÀ¹(˜9î½a]±£@ Tý ^“%I@Ó­½ß*íý´AÓ$ê7ܱÞœEˆ·E€ïÂôQ>…J=쿘üÁíeµi”3÷!6ÐUTMÁS®Šq`Nð {¡;å6 =ÉrÆòƒ-gï"7D\ çØúc÷¸–?>øZ¡/QG)ÄÿUXÈG?‚}Bï([t8!ÆÝÒQ²÷E?ˆÝ<árždý¢â’ ì©2ÓŒåzŠrç+Äh!ï˜ñ¼‰j¾G7ÎHÊ)ú Ù †õ‹FB2@f&€vJZÔ¾#(é"Ti¶1G®L ­ëƒ·]ï~Ë,ÕbÖŸxÈ;]‚Ï|!°›ÀeplÎïš¶)CΠg¨g÷…mêjMœ‹E„†~÷ct¶ûØzù@.¼hR-9¨ÕQ !>ÚSX,ŠòsïC×f÷í?6?¯º0š\íxí÷pBöMè8d£¦SŸ2ÜMS¤ÄÆvšì³8Sñ»ÜW±ø³OÊ˶™uXäŽbÑ_ýþ›¶ÅÔrNž·-YÜ$£€µØýäbFÁãéA„¨×“sõ1Ùßzƒè`Oˆ;ç$ÎãºTâ]νRâ+#zâîǹ6ì²§ öõ©ð¼]$Ô8ÓÌã÷Eèð[\®ë0€{›X¶Øü™Dê`¿­›¼) ÑöÅÓL¢•FÕÕŒì'Ï0ƒ›dàiEí×çºÜgs¥§Ê¹ÓÙ%xÏKQ»þün/VçT˜@ίó/;£¼tšÎë;÷ ÅÌéŠg¨9O:9§W¹Åô»'£{IZ¾Tø†Ö‚æÌ‹“Á îôŠ×æ¥zwA<flŒ&Ï ‰ñ·«CV³Ñbv".ºß2-Zùò1ZŸßÃŒOÒ([Ïj7€ ±örb.ì’+©N<37Rœ™Äêà È}Ñ;QI'p—“Þ4“ÞP{¼"ª[“<µ¬½†ðeýù}ŒÌ}|ÞÏ*‹P' †:‡ƒ•P´pC@ÉÂ_ümnSy³t¶5Ît6¦Íþæ:ë¼mŠ*»¿UµÏùâ–1$Ë?a;«ä â„O|/õýRmaŒ¯Y™î:tZ›¼ ÈÝÖ}/$¢IˆÑípNš0Vye.:FÔ&·¥jÝL¥{ZEXà$/£#OŠGÂ|·hÙå¾¹ºÂBhÞGx&°æqþP`M^›xsÈÅ KÖ5¸hù-+R€I„>žy”¡}yþ]DŸ $¼¯ÉÈSyÊ@G”qôonV×f/dݧ¢‡pÖ|.ž à 6¨‚^J/€:åqXᱜ $ó Jæ%ÅÈ´hi„V>–sŸ)¡–8Ži.@bC‡>ÇóPåê1í‰qÃ1(5(©X«ðùËj,ðn(5$sº%€ïÈF¼#ÞŽ™1|(ÈúZzïÇ¡Pt4Ë÷c z@’nçñMkš ðÅžá|\æÏF6zàÚ¬¦v-¬:UÚ4ÄNY^e¹À–áù )ÀÎkp^DT¿LpÌß‹Hœ¥s0ÇŒôû²]ºŸyçáÖT†#¤žÑšGÇÔ’ø^àýÉÒX°:Ç"Ò˱›#4§°â¡Ã³‰ÕöQ/·Ü0mý>å:×óɽœ uv+À4yiö §"'`¶ôƒØõÌa÷¾€åK—劃øŠŽ´9sOô®; BXÇáS•Öm•j”€¹#×3×›AàŸxÒ˜KÉü‰¿êaÊo•¡¡¥ð–…*(g3•)“ˆ„‡!aä uƒÄÿö c™a[ôÏ )'^ rŠq û/Akp²”ʰR©ïr èL,¥¶:WM –ò “x¸ä €û|ÛeïÞ4SÇm1Ðr‚¨ øyH‡Rið(ê$Þ"sK³?ßÖnýºËã©Ç⢖°cœ–&´xtA=x’¦ñ̃FÅ,fI tPËNÃóé³IB·`Ãçð‚Ço›èñWê„-Úð“]XáNÀÇgÐvPIz´°ûPnÜO:ü}þºbSM®+JgЖƒÍ‡³|ºÀã4úýéVgxÈÌmËx„C;¤9ZëÐxfVŸ‚ŽÌÜ °üŸgxKŒƒx` q=½¯*ê÷U$±|MnÖ²P!+cþ-÷žÒ„ë9¸äÏœãò¹/I¥êh®f™Wç,ÖÖñi‹oד'ÅÅGØqîd®¼ŒÀßy`sà-ÿáÇ]ð“º|x†š>Xü?‹¹çžA—ŸGH©lÕ¹{Åä8æ?KXQiàdOa3O$ä-bH}¨Â›‹oýJu“ gk®¤n-}.àõ*\8¤áW¬n\l³¢sôúçí"Éë¤ÀŽÿÆå ëÓžEK‹`J˜~WÖ1/X!Vox%jÀWH~R©ag¤À¾¦çºÃ‹ájÌïð4µXp°Ê dæ£ËþŸVȲ‡mZ;§¢¤¼†þ,é–â=©‡ã¹pæÅ'5­c¾‚úÿi¸ÑiÐÊÒ^_¡³øÛ•›IE}­Ö¸Â…š‚;þ0­×Ú2×±X—8Äéwð‡…¸¤¥¶yYMJQŽÏŽþêþü[/Ãendstream endobj 6 0 obj 3172 endobj 15 0 obj <> stream xœíœËvÛ6†÷z .É…` îX6M/iÓ4µÕUÓ…k»vzêKì¸çäòÞ’1’F¡äH"%ËÞŒA Hƒàǘ?dR€Êdù;1ήGÇ>»|TÅÙñcãþrða„.ªjŸ]g/FèE]6ú{ EŒAúºUȜſ£Í]Ðêr%ó×b¨…‰S #àºJí§…\Þ›¦ð‚ëC9ýÖ™êŸhäÉü&Aò [:¾²÷Æš]K3n¬2Çnûà [¡ä0/bìáð];=¾Àbo–j{ ŠÝ1·£Åì\‰[åJÔ¢%L‚-Ó2Ÿ‚Aв4""(!¥/kHI<¦PÔÛL0*â¼Q1€Z«Ù‡Q¸jzî Ô:päÀéU_×ÚŽÁ¥cd^I¿ /Ii§ÙÅL+­0@ƒ7ÀšSê'ÀØN¹¹î9ëtÌ’3IFâµò ºÒh/“y<£ÜÀ‹à 'c¯[^Zœ h·I•·›Dسµ;Vy+ÃLc»Ê<fªfj#0#ñžGnÒKÅßÛ?Á% {[Xᵓ¶B˜õØ{Ît>øúcÖ4s¨¶×³Á:&Øf¹¶Vñ7ášŸçš BÙ¥&µ ×ôò"ÍQÀL’qH­ö‚ÒÆCøawÍ.ÉZ±7!Udb,±ˆæZHEô—nðÀO6WصÂG[ɶ˜nÛLR`¤&ß ¤"A“¤ËÆi(ÒÕüÑȹ-4}{[–hسj£[üÙ?¾÷!è0Á!$Êy2‚\;ýò.Ìm#™Ê÷ ™‰‹IÌ]5>ÆZUwì¼ö‚‹-ð±Ü„Í´œGRú汉‘7$ÕWE¼p.v?Ü·ÀDرcýv€æfÍž¤vÁ<»ŒnUZØELÛ0A¶ë3× ©|cèèT”ß—RŽoÙ¶ØÉl}…¯å6õØMÚ8÷1Ýëû ßnÓáJ@‰>É"Ûî—,5“8IB´M*áܦÃÒå4µH.í¼êÑ`­:ߦĩqjõuòŸ`Ø«™5År¸hˆxÛ{3ëÜa³Ó ‰/AkX‘A·}¼àSÐS‰ý%/vrµhE‰ïÙØ;®YÂOMži•Otã%Ï«†ä #g}œfºy»üWú0תiŒÃ;³­”؃zÙ“þ]õë}A'³?}S™†%gkn­Ê—8)¤”\!žxR%Fx,ì—¶‘oÒïÃ~Õ½ÿÜË,<•:¬?9ãù™ÏêÅÐé§_úCbk«on•½qt aüY®`Kõ¦ëŽ!íjùЗ‰J(“>ôõÝhðþþsq˜endstream endobj 16 0 obj 1615 endobj 22 0 obj <> stream xœÍY[wÛ6 ~÷¯ÐÛäZáM"¹·¦k×nmO›ºgíNí\Ö8N·]ÿýHI$ ±äØIº¼|¥÷%a óLƒƒ#œ\Êåäè\ ¾ L&ýåÆÓEr8vŠœ'6³E2>°ÌZÃte•'EîþmóDs–i'°|HÇÃ/2Í ‘žÝ2J§gCå´*½ŽtÆ/Š4r‘)axºŽ„ʘ–:ûEm¤Å‹Ëhèj8ʽº°é¤\”‚éôà àW€ÈVâVe^LbÙ%À©óÐé–n(Mø¦Â†ª0é Ìù£aóè§S0?ˆd_¸¨qoÒ¦‡Ã‘uGL¤¯œ+¬ÈuCX—&˜(RVªÉÜÈÔ‚]EnQG^¸ÐV×!ÐérŽÃuGA^‹(ZèB²j/ɸÛöŸñŸ'ž‹düràþý‹K$›ð¼‘Hv- Í2Q¥w»e*Ï ë”G¹`YÎMún8’nU*ãÎ0rNw…;…‹“QÜý•Ç0œ[cÜ1*i w‘ÉÜ¢ñWæüRLTç¨eÝ9ÜÝ ç¾ß/ª=Ž\2 .â~~žƒ£×a€>µSä*“eí8¥ñ¿ƒñ¯‚ÔGW9…"ZRõrIÞyDsÊd#YcRÝ”¬öIÖhµF¶FG –@¶ýº Ó,Ïë`ø²ûJ:†B€ʺ\aÓ £ÃÞ¿‹XttPÞ;%å¡\–Jï°åéSžôȣȽö 'La ¾îâ†*öÙ$T‘å.cX2ž¹ ™4OX—6òè,*X:&K2·L•)x†ì"ÖAN+uñH"£å1ë꩎×(lT‘ÚM¥ã sSàšÄ:¹ˆhFÕN]ZŽ:pºT‘±80CªH‘.= > F‘sUóÔ¦¬eÝI½Ñ‘”Ê1dÕg«VvÙÚ:’(¢+~S¤¿nŠtev¹Õúm úC?­3Ñ]»vS‘7÷þÃI'Ë{9:*vtôo‘P7mrsGÖo7ÿÜöFAìà",Ä'—dZÜáe 7ΆžðM®šã冩ênBx Àÿ¼æ~öß@?†#Yÿ<ôÓrm·B¸ Pì¥ú×â¾ïç–%x‡nÜ®÷ïÐv%xcrÞf‚@±¿ë8ß°ˆlD*¢ƒ:›sá,¤Öî{¨e§ µPêñÕ!w¥ûýxv _û]^6p—®ñ‡J1ª=ö¼×ûñ«$¨Ëý(°õˆq‡¾'ôœï¡QÎ4üa‹Ï%äWÒ?z › .óVËkyÖ÷uUÈðÒø!äóê ô¦zè%¸ÉMúÊ?ºr㎟F½€  %WЃiS/<Ê"=ÕðÍd®!æ:}6ôoµÂ–£A˜\yF°V‹ß€Ñ»n­¦tèJMlñR+¸)S ¼Ô‰<êp·cÈŽ±X®¥òïc5šG”Dô"¢Ãˆ^r:"‘%Özj(bëˆÎ"ZTˆ[ßÂâyD“ˆV-#º"69[ßN~ö»L.^<xð¤å§‡Wçg%,Œ5xã Àk€ ¹1ò위Hà{™\’ùwHnÑ¿…0áÍ2ÈzÒÆ+TÉ& ÿ¸^½eªÊÎŒH®oD¬§“ð‚¸Ï¡ñ1%råã°†¢(ß! W™°›’IªM¹äc²"ï-M ƒ¿|bËx­¾‰è}ÓOáÿ‡eùé"™àÆkØ·Ü­GÓû©2€ øˆL5”>W¤À6[ µïdí hŸ’jO޾#!R;øà[€¿t•ÜÒÂû g+«ºÇÙÊšÔ&Ó2”¤„ŸEµé_°úw×­µ-xØa!äòHh×€®úTZlNïF§Ö1)€N®ÒÉù£+:4£{¿$P?@w‰nø©vNxCÍ™›(Ül„C Œñr=˜½XØz"p(\$r|B:žt¾*š1w÷*L¤÷  !™e½"BƒX3ŒÎŒºû¬‹NɃңݦÑê p=‚4ÔÚœ$?ל¡mlã•(êÖcÏ#zÑëÖZ•w¨î75*`hOº¡áQ`-–ôm H×Ö F×ú€hF;ëRC)©Ôš]s|aÔÎÔ×Jøµ¾¥úB`‚šXbµ¾vsw²š €Œ É  ÖEüî#‹%´¤Ú>Òµ,\7=ʶðØ‚ªWøƒÏ¹fmnî 0ïBVœD}ºÍóS22+2tÝæ Ù½‚(u~òЈN–»â Ç÷ÍLí–Ц(Ü´AeÖfV¢4š ¾¼˜Œ¤ì i ÅògÍüûtæ£Év»"(s*WYaTHªmÊ¡ý%å¿c»¹$ž·“¹öŒžèh ž„1…¨5º4ß PÈéá‘®Ý ©F]e8úÓñà­ûûx>2ùendstream endobj 23 0 obj 1895 endobj 29 0 obj <> stream xœí\[w·~ׯà›Éžp½Àb‹¾9²Ûº¶[[’[÷8y %JV-Y´$ßòë ì3À~K´œäœ4ÉÉ-q› ¾ f÷Ã$Ï„œäîßž8¾Ü» 'g7{ÍãÉÁ_;âúlïÃ^îŸæ§/'?ÙŽ²œ˜ÌT“£Ó½<3¦Îu;ª˜ˆ\N´™ª'G—{¯§û³y‘•ºPÓ‡žzÞQ²šNfs!EžiéÈ"—¶§â¤ö½¤§„§&ž:öÔ¢¥„)¦×Dž9!rEäG"ßyLäÛžtKýùèï{–Ki¹|ºwô§×Ó§îÔôh'>‰» ðyá©s¸àeŠO&<Â-ìp¯zæIµ‡žz²óÄr±Vݧ‹‹”˜:¯RÌ_öpÜÏp9l°·°³¹lp ×À"sØàŽp×€ÅwÇýiÚÐUmj>Ú9ì÷®±˜B×™v8rtba8¬®%_ã5œ÷6øfSü4‹íwÞ-gÞmc·&²ãO=ûF°ª=eÖÛñ.üö›¥ð}Ì¿£Ûþ„ñÿüF±Uz*Gûhq—ÄÀ~¥²€¢8O‰ o‘ï íÀ嬻ò©U@•ëL(ýÇRÀ;Ë›XÔ÷„ðA쨲¼2¥E é Â¶9ú¯ÓF)™6¤UŽÖ‚½5v!­¸ž²³ÚæEi,Õ?[zêb¦,UåÖ¡³<U»o„}¬ÌôÇYáæo´–ÛQª*oÔÿÞÎXÈ\;K*ÝÒXYù§‰<ëVR¼÷o-'µ*§?ÐÓäÄK>˜o`u«ùBØòÉú:Xoèÿf}ßyB乓XQêRòg ™7ÖïnRª,×¥è4ÚÆ3°%1~ÏE9•·Š’… EãÉEcF²¨3“‹Þë­â *QJeÃbÎÙ ª½R/Zy˜Î¨ÈÐbùßÐTXÏ ¾?þ 4¶¬·Ôv Ûž"yÂÕ†‚ÆênkÁø^0˜Û($¶q˜õ¬Q˜]¡ÃuUY¹q•;.Y Öå8/•´ kL¦9>Õ½Éô›IסBÐ^˜Ì¤ÖvÝD8ª´3ÛæP¹ÑŽ _®fBfº¶ ¿tÚ1!=“E-xõÔ¹*6¡ÓµÄêjöy$GnÆ;hÖA6ßjüm[{xå|1K§‡_~®\#¿Î„s»Áª¾Ù"n¡1>l1ØXwô˜vË¡s:7bú`6ï¼BóçSï‘ØQÙ=ã“46&ýI’ùßí¡UT˜äp#Ç`µëF¦ ³£ºû~ ÜýNÞahaLà~köonªMA¿S‹€‰p\è«1; 0{Â*¹éŒÆ6¦lª¬RãX3Ä’uâ ƒ¢¡XZ؆T¬å›”ZÌB0jþG{þ Jbémð0®®`ƒvZUÕjRôO*fa§ŒWˆqq€u•£®œ áf¬8lÜg\ =ò¨\ÆÞMq8Ãâ|KtgA.caÿëSýɘۀßÅù>#y6…ösOðì>q‹»xVd%²ÛgÐä~ @l«L¸ðÞ–úl¸°B3,ÛÀBi¾–%4Ûˆ¡£ôM?ÍJwÊÇÂÙ¡7œB€‘‘‹wbþÀí#‡—E‡Êá¶ê-y ÌÆRWPeIWs‚–Ï!¸ÿŸ‡6Ý­vÞ&n{“¦ƒ-áö<2£\îæ% qðÉd5 ¢Èj E½€ìiZ(§B3}îϾl¨#xYcI†AtáûDƒÁ<ÀrsWüºÚÂqŒ;Z1Œ|0𠄉SÄXN¤NáÖc0g=<4mâ­ï\røðelt™bwc§}½H!'ÂI“ªbêªb«ÒþmÊIaªÌ[S+gó<«D¥í©.sÃ¥ÉsWí¨3ãæu…,µU“K{ȬªÌŸ»>¦0ºœ¾l =õ*QQ®Ÿ¾Î“»D,›'¶Y©Ë 9ç ÐßѬüH¦‰`Rep¢oaŽ®šýÏŸa®hƒ2oc½ Äç €É€*.m‰1´5,îJwp2þf(ržc'ÁÄñ/§c=&ªZ+šu34énvC¯@“nd;ÖðÕHt’SàB)žjŸn†Ø– «÷CtMu@žûß_ÌæîN´ÔfzÏý.”,ƒ¤¸yêDûŠö…ÝÅý®eU|T{ýÈSG vê©Tý%hGµ€ThÏÚúBPT8¬/Œª” x 3Õ<ÂÊEVÏÈêO`ƒäÛ I1* Fõ+HqŒr8 îÄR”ÌŽõ÷ûDyIÖí€ÈçD¾ ò¬LÍÖ–˜ŠÆÛ_î…nù™Åëa-rhž´a± ULX .Í&kë6\…Ô…tÌóþÒ^¬Ö"ðö E;ã‚àW”>™‰Ò9,ãÒªÌê*¿‹{ÍÝXäzœf¾ O†ÝMÒg*g2ÙGpÿ’⺤Šê§íä%U(/´*iu2&®ÆQ—*S¾¶$3…ÙÊ ®_k…ù êÉ™žuˆ†Õ51†ñÅ)¾©cÂaA";Êú4æŒqxÅN”ƒìiÌî6 Î1›ÏrñÂRï®Òù¼Úô&Ã^`$C²U,Ú.ÔáÚ¤¡ðr,. &ÚO2Ì–uî¾à %Å«›NßûãÀÂS—žZnñ²Úïuî©_6i›7רûaøµ½Á[cþÜ:ðl t˜ÚV+jtµw€oz ºLõ¯{3vÏ`ƒBòFü~ßæ¯‘ÃþƒdD/ש^ ÈÖ1d ¿Ìx%‡_Ùð±Kïm¾Ø‰Ý*Õ+É~ÝrÛ²X·szúN|•š8±õ6Np<þ>âÄohã·×™qœA'P8_´Ík³x9㘠ñ‰e‰þã©0«³²·ì)\sòýi& ~ëÍálûu½‘¾ò|Ó÷Æ3@¡:%³-> stream xœí\[w·~ç¯à[Éžh½Àb]÷ä!VÔÚb; sâǧGw)%Y6¿¾À^0³Üo‰%E]¢SÛc æ>Ÿ‡q$ä0vkàp:x±g†§³A1<ÜûGÜœ>²(qŠN‡¯&v¡L‡y”ëáädGyžÅ¦Ä*†:µÿÏÓ¡"RÙp2| Ç[I,íÿÕè£Ô$jôÎCÛ$µ›)¤ˆ##ù" ¿ÊJ%L”€_äÉèïxHàÇp¿!†s/ <‚²}o ¼"ð¦>Mþ9°Ì”–™»ƒÉ_ìœxv´ç¡o<ô¾Ábâà¾d6ŽÄ81#ðrø²5¸Å5\v i`˼˳àõöÃóšéÌ<ýŸ(ÿ ²þøð œÁÓ!âzû8l•ë¨åßž«'•WðpŒiØ[žB¦]C¼Ã#IksF@¡÷ö^Œ n=…LBþÌ!SÈ$wGƒáñ’~ )›Â¹G¡mJö "ÿÞËç'í5¨s£ ˆœ1‹3ö:t¾-ˆ+ÍÓ‘G‡ç\üIaûÿ'°P=ƒ§p4¨ë†À˜ÀŽ~êë¥(×y |ÓŽ‡~诲Y@eq6ŽóJâ°Îøsç~†Â&`§Éðâ ˆMaø¾<Å$¶£¨þ/½·…òÖCß6Ä*«Ð eÍà| ·–^(ÛXÇ–‹â @{ísfïœpGýÆasÓh†×ç*ì·^æîpųJ¢uZ°1׈"•C¯BÚø©qsj&T$ó'¬wM›6æ s¥<˜Ê! óûçÉ8×Ȉç€Ì­¨53×U¸Æ–±Ôr›À ?B0˜.2XвAdzäV©’Ùi•2Ò±‰„2k é)¸´WÄ•-ˆÎÏL ÙÖéäý+Âd.9ÿ/;.»‘¡¢z]w0K=m¨ÃŒÅÂv} 2ìíñ² ‘øè®"—÷â¶Q§‘òŸ½Ö§=cû±nÌ 8„£ØÜÀ OJÈ÷ä“ɘQ˜¦±?”q»x GŸ¯”‘)“Ÿ&qS0¦Ð ^œ"pÚXÊ›÷'ìÈØuoÔ~WOà„GJCæÙ– 5M}+™Õ˜jôÒCÚCT‹~j kYãîr½†,à Þä_Á 8³ 6_ƒœBÒ±azƒŒ¸`Ä u 1ü­Ð%u”I÷¨fr4°L~lÕc[•2 6q]+Ø ‡±?„£Á»Ø ¦­ÒA ÞKâ Æ›å‡x`ÔtZ¥÷¾F „ïð°3ÆñULwËs×–½l–YË|-’%¼ß¨N‚{È(èMŸ‡¸6˜Û,ê°Š‘”Â’ëÒ %®ˆÊ;{$*îšÎ†ò‰Ÿ[ºKÝÔ>ï'C,z€¸ “±šãL QÖÑYþ‡ü:ºZÚmA.áÎs‚Y Ö¼9œ€›Ö8Xâ[ ì•9ó~±'„Œ,¤{ßlÍÆ~øgD™7"+i¡##´´ºf-DHeìájhh¡$Õ:.‚´ÖgŒ°ªD±ÎË+‹zMµ”›(Ž…ÖV?kT—”ȸuêT ŠMbª©Å„ó±r`®ø*ËKº*ïšê>OÒ,áìÃmÏhî¹#6IM*tÜ6~Ö­µ"²Š™ÙJU¸&®äøØÌ"“M¸å{û¹ÇKy’ ŽwV2U&9GÆ–YSÇukm ï!-c£ ¶z Ö7‹þ‘ovL üÏâZ¤Šï;‡›1jŽ8<÷JÉ.¨æ;úÍ­äsÞâ "Èf¶Œ‡i¤£›áðÇŒjL!>7›{á ¸¨· 2û€äYé·U‚ús“U›&:ÕMýe&Wºö)ëúE­=¬1ÄòŒ ÷ž:ŽÝq¦-¥T”¥™­ûd”èeBèÖ”š;ðyCß7¦¿82Z>TkëCM LaJ©Áfi²6w¬ãT2_àNƒx¢îg~H‘FZY™­Ïþná2i0$בu0kðÔ%av81ÜÁ™ÇŒäåØ"P–—•²ÙäBEÚh§"Ô0h0Ù&}ËŸï>ç†ÃýóT–à' 8Idi ~„²Ê[hüå Öcf)Ì —¥¤áâOGbjôµ‡š¯aØvm!âÒž‰`Í7)mï;´§)ŒËþÂØè« ×Wp LîÞBʲ¯áhï.þ !ëÑüéòÁV~°‡ËÚ$¬ûÓCèŸJ"KLô>D³Vûë«Î#“%&eÅým+½t3©eQaJbáÞ8ÕEÑ6oO,–kQð/Ák%¶ëW°t´ð‚hŸ ¢K˜Ù²Šé“Áh+^¹D×dŠ’f);:%ÝIsG‰ÒêÄ… ¢©¨½÷®ÕYpQ¶ìlY«ãÒÎ8Ô¨¡XýL{¡ŽOW«Í­DçYÙÔ2I¦E Gh¦:[F€1}ûF¶®n41àÆB°[´ïõ†õdÎÇUf!6sÎ5¬dHIô¸it m¤b‘ö½±¢_pB]„&c”û„Dñ¹6CÙJ"éXƒÒ‹À”Í‚ÔD:–Å%q*ËÉäš_ùŽ–¼Ü̶¸DÒí:*LŒo²R’|¤=NID Ò!E£jŒL*Ø­c–Ìz˜lYá"rŒµóNäÐêw¬èw*rµð;2µ«dù;öLq¤…66*GNjiÇÊ[ö+Q'³Ñ H êO/5Å·ߺ幉c:#ô¨J‰ü… ï—hSç3».)/j¼“¯,Bk=è¼d”f:v‡z9Ã?ô˜~1j¶Ç2Ò™ó­»ëwŘfôëØ¢Js™õ:~ßC&ª˜0­bàj-öF—²V4æ÷¹žÖ²gëÝé@èÛ%•øÎwîíñ ¸ïõbLpŸ·\HÒå-ع–D&6ªi4&ðuå—ó|ôÇØÒnâ4ÅÆ‰÷fÄ1;ÿâPé¼¼Éÿ5ÄâóŸMü5s»ºÈe@Ìœ™UÞí ÔùZ­ãܲ‚…K8“ñúõnMbûy#Š£ FÁQÀŽ]²-±ÿên_Å,rñab„žn¶uz•»Æœ* ië©ÉÊ.ȳ_Ǫ 9–ÜóãðÌă/ˆNµÆ'ƦSc)f½\ 0Å%cŸ.l]ú·³‰—•øÎL¯:~æW{èþ˜ðW!¦ VÝ_÷*ÓùSEá_'¹oß0|!?àÁ/qðÛŽûú£þ_J t6Û?;¾ÕÓGÒ¯RUàAf[ðQ6~¹€ÏJøY °NU-M“¥Ià ßÙ‰I—ñ¢¤ìÌ'`ûl£K?JO!/ÕŒ Ó½×úƧ{oÆÄZ"ΖàqÐ[ö©u”Y–Qç’‰¢$<œKŠX♢~Jÿˆgü`ì¡ÜCÆ[¹KâÆsVÜÌýT–^S(º‚‘aIÆ7çhPH¼] ‰‹2ÝÚ­”ІZ1¼fê¾øß¦LôÌ7Q¢6PkÃIáw8©â½™"“T"Òð¥FÔS%;[3Õ®vŒã\§ÊçaÕV‹‰ØÎdðƒýû?š\~Gendstream endobj 37 0 obj 2784 endobj 41 0 obj <> stream xœÕ\Iw7¾ëWð6ä¼°Ó@/èÎ{sð–Ä™dâ‘å—C’ƒ,Ò²'ZIvœYþû½ >t`“”¥8q%k-ªÐü:K¥g©û×'矚ÙéõAS<;üª#®N~=¨’Ìý× }r>{xdêbV'u9;zu&u]¥¦íUÍÊÂþ]3£T’W³£óƒç³Å2Kµý;ŸK&…Éòyé©ÔSý¯ºtmÊÔ$*7ØÈkßêÆSÇžºðÔjø«ª³ù•+!gBž ùNÈ3!o„|#ä[Zw-äZzEçp"äk!a”\“9hÛïÏGßX¡h+”oŽþ‹öTå©z±¼ö­ÞxêÔSòëÌS×”}kJra­('Anç[óWßš¿ùFöÖžJ÷b¯h¸èõûú²#¡.°á--}I àŒ–rcé\OMçcÀç‡Jyè²Pheeù‹\¡{É=Z,µµc”7MŒÒVÇž²CÙêYQ[ª/[{Êiì¦pC¦–*Ë4›?XdIZÖ­0mk£òÚò"·DV76`’4Ue‰¬2®T×]ÿ™NÍüW!ßu3©2è‹·ZIUáX*\H…•”BÝ7²07ÝH7°üßub*«¹Nd:Kr]›ùï åô{cÀ"#¥eC¦ÖJR™*üžH_3JZ±ªÜ5Ë;±6Í8 .¤tÝ(ŽÎÊDe…ÝãŽVvK;kØíä݉Ng5ŽõÐÉÛdU Ï!aV^–%v­æ—(ZOžÊ´^SÉ|ÝêkYW†*Áô`5T¤ÁÊ ¯—#YôŒ‰ñ¥ôß)@Zàï0>ôõJ*SΈ€µøB=èÙèA3…ÒªæªèŸc;nÑiq ý¬,J+ýFe´ItVõ*3[hc¬sdÜo˼̒Ìd³e‡<®ÆH?ÜÊ@v—¾ð§N=I“±j8(ƒÒ›cªóP÷%åTX/˜mÔ‡\gbÂÓyR¤jþÃb™IU¦ ¯©¼bØpa3”J‡0Vý 7cš²:`Ô@ÃÚ¥…Ÿ*©SÕëpì³f€º(¢HÃL‹3ç{`¶w" ãüçÛÌjÁtèý¢p{¢-ÎÁ\Û¬+gu!5…B †ß,”¶[M¥¢‘Ò%rP‹-º…ÞÒºv9ªvA kXk Ó:j_t˜’—ÿ²ìApÃþrô¯0&Si¼üçÞ)|â©#â‘?òÔמzੈ»ú4~2ªˆzê[Òâ±§þã©¿yêÇQœˆáŠxÖÿÿïHÈB>ò‰ÐÌ©…¬„¬i³ÇB ùBÈï¦F+…Ì„T´Ù—B>òÛ k++»å™F2ëΔyÝã L¡2¥ã>ò{º4 ò-™W½*®ø²ŸÐRR^ùS±»Wt_o\6ʵÚï™Y°ÿ6Ç5ôãGdôÎìÛÈF\ý!†ÄÑÃ@Ë!n4…€’íó²êö„¡ÿkA•¶F©!yæ©'Ó!JRxÐÞ¸íg©ê”£Ê|4ýÜÏN„·+™²(„xI£ ´¤±Û¹/¨ÐÏüð3:½`«ñŒô¥¥BMëþ4—°‘J§†YSºÄ9ƒ÷­ëDÛ@ÆË$3[èȲvBµ‘\«$Ær?-d±­žyò²rþq߉‡Nâ)S‹Ÿíǹ+¥uÚC?o+O #\õgÔc d ±¬!•R³`ÐÁ§Ë­˜/îÉ—CE•ËÜ÷³ánߨ€^D1ü|‚»R—ÔðFÑjm§81 ºzMÁ  ì@áÔS숔‘n\o¥÷ØnF&—TîÐa4Ö͈[ÐjòÄä¹þÚo.7tup:“p‚éît8&jàN&¢1˜÷îÁÚr«.tÏmzDiaX—©ÚÃöÊùƶ`¬q„Ë9Å¡]½Òïl] ƦÊUŽ'VŽ™eoÇ[(ó8fÜâØm£xc¼ýyb t?ƒÑ–Ûèp 8ëj¦ÕÄ ‚>ä¯Án¥?¡qˆN3*ªÑqb£ØËOÁ=Ò]"}8§ÜŽ>Rßƒ3Ï­¡¹á̃z)14›Ôz؉[t©“<Õ=¼léRd»…KÉÜLJµ¸]£í@íæB¼Û1„cÌ‘Ú2˜kàn[ÿ´bõ¼9BŒZiàe‹“Áþ„.ß œïã~xà\$eQM8S$¡±çÈÁˆÇR‘ îàÊìkÚ´—mÓnõtí¼¢XP1"²N†ˆ`Úª¤?´Ž»)ýq$º)ÜÝn4ÎVÒ…­&øÀöýbéÎs´%ÑKÙÊc¹×ò®ðÇ{,·‡4üì5Žêš*òý$56++­â¬Å{Ô(NnSûo_7‚u÷8‡X•¡€‰uLe³zhI;°FÇB¾Ž ØÐ@Mü0ŠÜ#„uÎ Óˆ-œ “Á˜5)U9õC8Ø9k,:×p M‚L¸é_ÑWm妔®†¾¢ÛÙ“Ÿ†c£/åXsÒíˆuRˆ'µ· ÙY+?7Ý…‹§ØwòÉ“Ýc~7_Áá s&9¶Mμ7ÊÌD¾ÖA¿á—?tš2䇛YÔçéÕ#Š#Û;%©—%Ù»GF‡[—":`˜NpøÅ,&8:ûʇqWtßí}ëÕÈyrƒuàF­#³ÌúÓû¼Ò}<¥TÉa2i‰(fÃq]á†Þ0Xò0¢n ¹Ä¼æØ¿¢¶‚Çn²øö¿µ§6U^„'eÔAú†s@¾ÝMnõ°c5ööÂa˜B ¥LǶp¬<¿ â¬;7!MTšÉgÈApÚœ×óÔøRÑ8ÿ&­P1”bøAA¶>àØN7A:àÛF÷EßþïÏŒ:ÜÏýgžz±±žôò|Déþ®Š¤ï>-Éøûåà¤YŽîGOŽxxG Á”ˆ#Ò˳QÛ;צµc½»[º”=*¨K7èÔ(7½Ýñ’ŸÜÃ’e.ß{jœw!K¾MÏç_Ý«´‡F—~yÞ4y›Íû~št“EÄŸÞå;kÌ ñÎÅ´=¸!ü‹-lƒ,î0^ ¾0|A½Æmv=¹Ü Î=µFÔc×)ä¾Ðïžz»}s¸«—þ-¤¿ ¥‡{€{ —BžS.3À¸Pq£ ßð3jûlë²`ë@óÝC9Qm¥­ôŒÆF@7sî«2ª2˜Û^ø.’\SÎÓÒ5á'CQA$áé£)lwý-·QʸlúfV?`>ÅçÕާØÈY¥'¤ß©[hŒ¥/> –ÖŸ(KƒÎàÆX»‡5¡Û²gOk9AÈODË—„3Úcö¥Ÿ¨ElÄ‹OC“¶ñ'Ä-íh ¶/=ÇÄ¿•]Vüå  î o»ËBâxŠó\ªJåIsèwçî+Zú=%¢›% óľœbÆ^ÔÁšˆB¿¡\{² A\ÅöTóÛò•‚Œ“ÏBÎŒõe/vSì¼+-°Ùpï{EúPHvz5éîÅA¸RâäîÓ¤Ï(Ûøq~ß›aì¾7ÔåhÌãæ;*„+Úç1ô 3[Q’÷{MÈ)[Ïåpqb‡åC·ãÍ, z€Ó™—´ßßé\ °a¿¡oibQÓ‡[„Ò{=SR˾Ç}ž»cŒ‹½ó–ö0‰0ðŠÎl—=q[—xìwìtõç’ò$ßÃW´î.ýÞ‰×!¡ûwž cÀ//³øƒäŽï½Ì ö4Gí_†âøü0Ó3»•¤¹š g´Ö•ËX´t¦T®kx¹r7È*Û‹j´¯¬“,/NúÚŠÐUÞðØ÷â‹“2­µrÌm:¬š‹„¶vVdiÑÜF±6פ?J»Ci·–vÇ0Š´[C)öŒý¹\jkŸºHëfNiRUesQË cY‡#ºüfËQ“—J¸ _%#ç)]%ÎÇWà!çe"_Ÿ&¹Â¸kD“פ9ÏÌæÙd˜uþH¾9>òPÈH&8µråÂû0'¦ÊŸ7áWûoª–kAò_“næ™Mæm¯†ŸGOŽü"¹ør*’>J.êéÂí¥¢"Éå*ž–Ë)Ãv™éü[0E–Àó@ea\H“¹Ã“)õ6t©!) ÒÇà5‰§ÈJß·Ln§ ¾}ûúYH’ßoçë^ðák–÷|ôV n>øØåÏúœý ¯+…\$ŠÞ—Î RUx_s!ú¼†î¥7»÷AþÁ8¿a’&&3ßG°·!-BÞ³€|ùçð´ÅdVIƒï²Xâ­ëe¨þ·z΃emN&Z¦:È=à‰ôüe#å]òªKº·[fZ¤Ù4Åa*'O.ÿ€91³ÜÙ© ’læð’Áä¬É¢¸’ÁÌám"3ÛÅø3X<Ï9ò¿ùÓm/– _Y˜z…1¾TÿÌ£ro¨yPý™Àדò$7åž÷åjÔä@žËÜ0ÎßÁ«i~¿×ù#†U¹*ñë£]Ú¹C`$ïýµŠQ†ø†±vID‹¹ilïÞøÅàõŸ'Gÿ´ÿþ8Ûeyendstream endobj 42 0 obj 3640 endobj 46 0 obj <> stream xœí[msÛ6þ®_Áo'ÝœAÒÓ¹™ÔMÛt:Mêêf®ÓöƒbÙ±ïüV½4I}¾`àCQ–•ÔnäÆZ,€‹Å.°ûk”ÄBF‰ýÛ'W£gÇyôv9ª>GÇ_5Äâíè×Q§öOõÓ'WÑçSÓPˆ¨ŒKMÏFI\–E’×RE¤3óÿ2‹r‘Ĺa¸ý4>šÈXy.Ƴ‰ù,¤ÊÇ‹ÉA'‰åxî>F†J3­“”ÿ~ê~ŸM„0¼Í)ÉUmr¡ÊñÅD"-ÕøÒQuóT&ùx99ð†pbÿßvY¤ãkbe\§Ä@òWôû9µb}™±Š<6¸,ƒa>~ykUÈÜÈu“a ïí@Y—lt? ”jq½¡#7wÓL¦¥™€›àÌ›@+—}eÍV"Öì—é7#)ÚÚhÌtn„õ{KÂÎ"B€uƦV/­Õ‡5}¼2*¨ Ü™àSÿy5榧cÎn3YlÉ/œÆêC.ËX(%Ç&F{„J²¾µfŠò®Þ~º,øhûÖÚ2h‘©½¬µk¶$ÞºãTgº±•5*©2¼2Ï [¥9hµÆ6Êd­:Í „ÖÍä,|´»×î[»i‹®ýÈ †û‡‘S3ZmÑ‘ Ld}BD··9¬[²fyeB]Å6¶kÀvJ­dJ=R*Ëw mBÕMÎŒ™À9—ª`YJ¦äØF²V³Þ±6ªÄ†¹ªíŠ(âBJ`Wð–e»³%¥ôát$›Ùa­ÂæŸé¢VͦcRMóËô樔™;*Í‘¨‹húíhúO;¶ƒ4‘"Vjüƒ!ã,7úþÂQSGEŽ:rÔŽ:|S ï¿_¥æcÀ䲿eu̵äŠÈ~e¼ï‡š]yCä“ Øbõ PüÜQ/õ£žƒ¶?zÈŠB¨¸>°î‰ì „è’È5„ó {Cää¹ òÞÏó/jÖÑüòQ³oêU%KŽÏ_²IãD—JqãÎHú½ña •¿&24Ä7!㼨Cƒ#ùÉãY––uîŒó!9z5Z4¡ A&„Ôq ¨ˆó`/{ã3G9êÂQ—Ž:uÔ¿}«C÷dtäÇ7:rwà?²Êx0ÝÜÁî?&c¿ƒ¾u~~„úþW: ×–¢ÞÈ1hݽl©¿ÒÒn8å ·äâ©à@gAÞ½/Zj£ìoTá êYTÆ‚&|m° ÄlòS:šÌ‹Nî¤ÄRµ1iã+@M':I:+bÝ¥UO˜H¡“‰á©/–FóɲX:œU«WœB="ò "íôíBt PEœ”2,áµ¾ YÁfÞÜnÑ1ºdXã‘Íù=Š‚ûš¡‘u¶RØñ|øÖ]xÝP3¦sÔ]± "6†[>ôzcåg`‚{¥¬Œ«Ø½¹S¢‹ D  Z¡ËjÃǘ¸Í&IAbF¶òO6‘Ú k  `ñ[xûÅXÙ×ë^]¸Ó֪୆ú-ºÉ_wëƒ÷›‡á²nÄ];³=sÔUÇ=© Ø Â|³ßá9:¿o/ »"Wd~ s/˜‹³ ç÷95‡·8äò!‡ð…7‡þµä¿K%ò¸ðf»€“aòt<·)³õ0˜Á¯Û;tÄ¿HOV“‘Ù{u™0溞Ar×[¨’x88]´aüApÿÇQ_:ê'G%Ž:téZê¨_¼Ò¾ŒJÞ!±jŸµ{ƒø h@mIYÉýa ‹‚kÇ‚µ‚_o šÑÜøJäâ6(ŒÍb å2 gPØ%èxHÝÉ’l¶½Ç_{Ã^ÚjhåØQƒUw§?hfCb£~ …}±¼„¤ý¯}à¶;´>-„lÜî·KAX}ë(zÄù²ƒé~ñÛiF}†_ó¼ƒ ØubrÙÞ@aŒ÷Ž—Íâ7Øq¿wÖ»Fdèaì+G}ýQÌÄ#^£ÂÁåXÜ÷ öÂ=>±@c¦óô¿’‡!¢ô^ÚØ%ÍòLvÖ°¾Á…Ù,Áp^„—8ãÀCo×ë»Iç…Q…ZÞa#$ ­ÊÖÝô.uÓù¶…½¹Ý(èÄQÔõ¨ˆ§oØlÏ6h–DßF¾ã[y³‹ƒt¨˜Û9˜åà¼ãZ„=`à°oIW08Ùþ¾ï‰Ù.H»èУ™_wÐâë@hœ_·{ÒèS`’½Þ¨ 1ÈÜŸèA«BðñÑAôÌQ¯ßŽÿo¦µÏŽËÈœK<¿·Hƒü^©eœu~¯9Ç“XK) iÌŒµBÉêT‡ß FЍ®!t§JsÆÍ%І.”M•%)GæˆuRJûQ ,ªóÑp§YšdÕ­´Q-%½v¯-]*›Órí8ìÁ˜Ä™Ò*³ëðа‰–FFž–IQÃ3…®^ì \^/6Ëв+-2äô¤PK¥ãBYˆÇU.Ùãz3ãɨ,yÆB¤¬RˆZÿÆÏÃu§;û¬ žcr¡C—¿”à|»ì%Í$'ÎÃ+d »þ¿ù½´Þv¢f)YˆnN%å°)¦}ïjuî£éDí‹ÖQtїɈŠß®p³Á·+š{1\soOyŠ®ë'“þewàÏ90DŽѮ<)E«}6ÿù%1½äøÕ²EÉwQ;¥eOª¼×3Ësµ«¼…ì¼òvó~Žý)âF§Œíbo\Í´1„ž3_çßqÊÒdùk˜RYœ¦¥—eM/Y§.€hvgžõ-#Y’%Ù¤»g[ox9%KòÊFöå/­ ú¸¯‡Çd¥„Ú¯ôé^ëµé‘+i¢åF[d\€‹K°õf6ï¬Ö˜Ôœƒ ªØØ4Ī€ñâ½ô>|³©íhÃᯕ2hùѶ§ŽÀ tέ ï‹¡´ã6a À Õ¿ „7…ÔDeŽ«JÂêÚÐà´o#µ ùyâ,Çêå¹pûcü¢$÷ì·Áu¬UfÆHOlû4`ªajptK/°¶ ê©ªmë HÕ± !+¶{ƒ…J8Eã·IfÆ,ëý ‹[¼ãêSØœÖPe:ÅG\ï7åFìTUxë>®ÝùD‡ó™ÏQëž²wú ׺Ԋ]k ·¯ŽÛTK¸ÁïØº–-æ§®%dó®Ý‹Â¹À[ÔïAÖfMd !oëO?n-ᆵ}ÕòÂQ­dЬ¯–°V•ƒú¾é©”ð©”üÊGQJ¸ïJÂ׀禍p(>U>U>ÈJBXüK ÑæèVçHý'×>èš¶û û·/%¼G½ÙS-áC> ^LGß›¿r‘Ó]endstream endobj 47 0 obj 2549 endobj 51 0 obj <> stream xœí[Ýs· ×_±o½ëäÖ»$—»ëét&NìÄO­Ø—¾8y8ëôÕJ:Ew²ëþõ!÷ƒ¹¿½Ý“Î’Ú&δ‚$ þ%q*¢Äþk£Ëƒgïòèt}P}ŽÞýÐ7§¿±´ÿU8|t½˜›"‹Ê¸ÔÑüä ‰Ë²HòšjéÌü]fQž¦±*¢ùåÁ‡I4ÉD˜¿Õä[ÆY.Õd ÈAß9è{¼¿8èÄAçºpбƒþê ÌöÂAo ¡ùú¸®ÒR¶Ô-¸!0"ðˆÀÞxIà$ö‘Àsˆ» ð†À/Ø%\ÙìM˜a¿Îÿf¤Ÿ¦NúF›æoÌ/:0Ϫ¿æþ`ešê8Oµ˜œM•R-HfZ'kò8IR­ÍºÛßͲ3ûQ”f)`еûxë « ’"É+šÍ¼ÕßläÚþd¸mV+³¢¥Ta1pE [Ê& ÃÈ~$ŠõX¥ ޳©Fä’#C\6Ã-!Të8Ÿ*ûw©¬´H5•Ä¨ïºæ°%é_&´Ç”h+%_[þ5dØy¥0B*cB¬˜/5`kd 4‚ócAà¹]¿ÌJ.l6òÛ©Œ“<)ÓÉ/SZ[&#Å(,!±W¤ÂƒêÀ¾â)z¹íxáqÛ}•D·•; #XÜèÇh7ŒÓB+øq:±(tY8V+¡ ©3íOáè.¼•+ë;Œ­e_j•:N3ݪ̆ÈCÑTä¹s;x¦tb|OÍ“dI¬ÉÌ tóûóú«LÒÆâUæÍszi­©c.á½ó/÷âÝÈýì šãuÇWñyßw Ú“é$ÓÊš"§Æ| sIë!s q×Ðã0_x6ä³pØN|gðÒ­S3š"B¢¯ôÖAïeúF“‘¤Â(a.zäø ºõÈCÌo{Q~B¼dÊzýU¸]ß8ˆr0¯:<Ý/ÿî´£>›ŠËCŸ!¾W0ºL†+HŒáâ–í✸ÿêÒ+#²”•üÁAEîÓ^ÜWFâñdÔáð¨¢ÚK[ÞÐe}wk¯à „¹ª(LØßyªª¾ûó¼Èeˆxi å õ uÀ~Y¹ öË­xÎÄ-€Ò'+šõó´ŒóÂèAMïyC$k¹9:õ@eá­:ßF™ë­„ŽDSãŒ9S2l«[ÔÉÏ5ôXØ+°¾·»8ÈÊl´{;»\“0¯Áºn™fàk>‡ø^¯ø÷u“; ¤%s1a}æ ·ïŽ:ý&gïÊ(^K!ƒ)-Kêã—’X ! aGŒKS[ñT=ßÍÝ¥Œ C%­nغŒ¥Ò“œa§ Ö \¨Â¢;*&ô±Š£¬!¦¯Öf'q¦SaÙ Z¸T:¯=„Á‘™L²™šø¦"(²¤ôòœ¸­`ê¹,“¢Zb³˜ÃšŒE•®7´¥1æ¶>l±•N3äízˆdªc),óÍ ù?ïÚ^Bþ#teõÖûÁZ€W\w.‰†ïRœáeÿ7äÊOg{ò» r{ªPP9¦ BËš˜_Þ@o[UVÁæpÙl~|ÐT þePK[ñ6z–Wê¨Dy‚î. êH̱KTÛ9€^ÌM¶46¶mùŽš^Ó̯ÃΉ2Ëz$þoš×ÜjÉ)ª¾yˆƒÍAX¹Ú"’11°]‹¢âˆŽ!óàËq‹D‚s‚’Tà8d«4ÿóY;g{KòÐÉð­½"Œ\2Á²vÖ,rÜ´˜FQÒVQ:5°L ¾ì][PdëD÷µ ‰!£Åê–tT:' ‰v #¹­BZGG“I‹ÕhYuò#”¶pÌ.°e¨\öëž­À笧£‰•£G˜5ÀT|²ÙÃÁƳ „›FU¬rÙj#Ò œ•ñŒ‰°s´¬Å‚…ÿ®xò!ûN2{ªœ ©:„˜a[ÁQgК³æÖ¢ÓT“Jg¸Cwiá]äÚÍÜÖŽ3k·0«³Ëæ^µÏ¾VÊÔ£ú4áiùIÞí5kjºÙ+ºÙÏèëÃö)^C„Ášt´Ãz± ʾ¥¦TÜMjŠöPü!µ=¿£ylÇ„ì÷…_XA~/G&Äî ”#|¾÷G‘IS¤j¶ÄfÀñäøë²—ÀhŸF$Ò=óšÄõkmî¶ï«öñl…Þípc{y¶Ã·ˆ¾GRèÑÌ.w¦š{:õ£]Ë*÷Âcx£* ìbgc:ç¿¿È8æ¡ÍÈfaý±Èÿä›pmôJç!?é|ýG:÷ w–àç6ë˜ fÅè=|z‘0bè\3CËÞ¼éyæ¸óÛž4·¡MQ¯gâöåæ,qOˆêì‰Ô:ŸÙ¶{.\ìI¸¨œž2+jòˆ©H9–˜*ÝóÆ y—\ ©ƒ x˨¾p* <Ì(PñoK;M—N%—,Ph¿ÙêØƒ"——l¯âS+NY¶ñ)޽ª{׎ž-_SÐz “céúÁpš­üÊt0³ô}»d7‚þ]nA¸_²9x¦7ø$ŒÅÖ´KŒzõ¢B0Am*Pê.ou¿IL×›<²h‡%®74ªïÌz9l¥b™ç”èòÂ_j’ƒ×[ØNûÑàEùùªNb:`&»÷ÂÄ62MM¶SL^Ý,UÖ°–]¶ÅO\ûâ´>ÁŠ%¿Ãß¹wpZñJ9pÖ×#ž7Æ*uÒè\µßèúôx‰Òï=ŠN)êüPö[m·G¢¸•ö†‰ìÎr#ƯõÌwvḿæXCôP£Ëwñj é~®TL%…9œžŠ½œüdþýùÍü>endstream endobj 52 0 obj 2806 endobj 56 0 obj <> stream xœí\Ks·¾ï¯˜[–)ïÀ<0£*¬„q∲B­“ƒìÃjùR,îÒäRŒ}÷/H~p€y 3ß,f—dLU$ªJM ÐîÆ×€~ŠD,U$ìOK,/'ÏŽut~3©Š£ã¯âú|òÓ¤ˆû§*àôò2z17 U•q™G󳉈˲ºæ*£<3¿—Y¤¥ŒÓ"š_NÞN£ƒY"”ù=¾1dœé$:jî¨ÈQ/õº¡T+¾rÔ·Žú#èäOŽúÊQßîæÔ]äu×L€‘«ú»,“éšÈˆÈ"O‰<#rAä-‘ˆÜ@¾k،ս Uøòeƒ¼†ä–2ïá4YOf?Ì¿™$eë<3º3?1ª‚'¼€“ØÀ K"/àÔܪ(Ó…³c):ÏÚÍÌF1C2uæÿœeÎ̯/'óß¿µ*"M™+Óƒ0„Jµé ¥"C%Yž »’©)LÊÔ¦¥V¬ÉÌðM²’7¹rŸ„³X©J+Vm*èL±æjTwž(ᵊ*þZ¦%ëþò`¦ÒXèD7]U­h Œ«ÊJWD²Ü˜^µÈ²f.Æ"xß/¨ãucæÅgr`óhåT$ÓsªKSª¸ô¤!ÌfLl%Cª¤œ>¯«&B6 `þùoÔ¤ˆ‹v¹û;ÞX„…£.uꙎ\èXVê€lÇkõÞQ¿Œç´„{ò’§p÷±ÍsÓÝFÚªš~%kÈ,È’“Yþ ”zsÙjPÿ®R©ã›53kÌü¼Í›µ`Ȧ f=auäó7™£žcªe!•Q3­ž˜0vQ­ì­g`%ógà¼_ì°‹×*÷%[È4®- Ø1¡ ¬ ¸>@qÝß3fï`Ý3H¢…d:‰¶ì8Á§Ó¿ß²2 ¥OJ<³P Ø"³­t9œÁfŒÙùX[A¢í(² Ç;ìˆ,°ïàüVpøLn??þz0EX‡°gm@Ò¦Þ:J8ê¹£È*“íù!dÕw0SOj}BjK§«‡©)PùDÊf†>¼=”ð:2£ N–÷?…­aAÒ“ÖàÄñŽbÁÓ° ,eÍVPHXG#ÈŸÆË•žÁA~cX ²ÃÛ#ÞãÞׯ}n íÒÐb„”]"Úëü0výÜÏ@”Ð`l—%mˆ×¾ãQg†Ç’åžöÊr»Zv…Юð¬D¹×Œ†Ìë)”߬€³Œ/F«î`]|`³ø;>/®‡¿8êkGQ4þ¦ã^£ž„=ˆN*ŽM.wxÄ"/ÍÔîÃÈ5‚Ö¢.Fe¿/µr݉+û@h\Ö«©s«÷^ž,:€ûtFÞš£unÜ­©×»ƒ2օуšßó†IÖJs4Þuê´íPë^Ù#sµ•ÑÒQÔõ Ð èaÆû\+0¾7»¸nµs»³<;ÿÔ¼ãºîöÜÆý‘ýÔ3"ú`]8—ºÓ‚ôf¾ dXŸ9ê[PoOþ?[gÇ‰ŠŒK)åøÊ´“ãKÊÒåøŒcq.sm„ÌÒ8+…°x„ŽË4/ªÑÆÜ¤SÍjòV%£#âpt ¬­SÊÎØU8·¶^$ÊN¾¥NÙ÷¥K-DžÙP­e¶¬‡S¥\\áÜqà=\uùfyé5û~Êú QÖÜ”,ÌÙ@Åy!LÙ¯ÆÇ¥(´n*æR7S¥ÿo+~`˜•I*§Àñ ¤Z“"µY]³ Ó~²ŠœâÇ:"óœ%«ŒnISªUabiÙ²ÒœÖëhW3w”p>‘¾2Ÿµ´>‡å{zƒud½‘ó×í‘zÞ´Ej·Ü=påèÄ5ö~Ôøºõ¢X˜Ò€½urÔ}\¡‹a `1è-ŒW1Áº¬”a“Ã"Æá ,f ‚·Ÿ5ðÜC¯ã?… ¤þm NÑ9B«)aƒàà| Ñ)všb¨J„. ¡Sµ?/U¦9¾t&ŠÎ{ñ”µæKè07]×Uûã{„SØsŒ‹ÊÝX˜ïÄö·{ Çâ^¨&²€¥7ä9¯à@ªI·`p[(~õî§ ezg£Ú1q¡Të½Ø ›–䃚X£‘F’MûT9pZ:²yF{¾ÕÆ$öê^)§_pÙ¸`€›-Žã€ã“íIÔìØrá'pZl­ðøæKµ­ë<*1Ež4b5‡ëöÂ`-—ÆÅ×+òñÿ OîœÌûkPFõ¾tT{yqLæù7³ùÌ)33}K—¡Þp¦£b¬Â%,="ò¬`O›--`/+…JEa”®h-BP’8•yš<Íí±Ôwh³v<³˜=ºÅ.’Q¬òøÊ¼c”Šo}àì০ÃÁ»H[B˜ØbtÞÚ±ïŒ}4¡µdçòÁx¡¦°à%Ùz½Î·y™3Œµ ~üÂü³h(]ÔR`æ»Ìb-R÷2!OË,Îí9Ô¹=9§F”B¦´ë‘õ|.§QýÔ”·\èñ Þà·Úð’ÈC"«1Ûžûã.bQ*Ýö„²z&¶,ÓÞÈãèÈmOäñ… l´,È¡ÐoÁG\]ÔF¡=HÛUÀ6ÆAƒo3X]Š‘Ï–gºä$[_†.2é¯äYÇJ:µ¹´‡“`•ßA‹Ü‡Z,óˆ“Jë¸,uí¹L'fÚç;±lÊ.ÒZdœìŸ°õmn±'Ã6/Z”×Z0²€];ƒulx}ö]™-±ó\-B;x<K{Xä#åÜêÔº¦Þ>ÚáƒñÑÀõpŧ^©c*Íú:û¾†sÄ™¶íñŸbCX£Çس5”=›#Ë5Ä$¯öÌÒÌwVCVû=m3»*U…ì+GÌè73vT‰¡s×üHÍ» rŽì^Í©Û27dþûñ^Õ1+·³Uº5ÔêÜ@wud——>¯ö‡*b‡Q0k‚¯7õðÜ&zïõXÆÇj|ð cïÙž´W#Üç þù! nì¿¡ã|Y„Ÿìy¢ÂÀÆf—!fXàè®äø~ŸŸ±ùÌééÄççkŒÙoô|mû{ªû¾^£åØå¡ÔS}{xêG8Á«žØž²°°áe:‡ÂºÚõuÞ˜Z¶tЂ^ÃRìX‚ÂzPÉs4™a–Ø^@q ¦RâØG‹ÃÏî?Q£ê;¼z¸ƒ Æøý¾° ^ØÅ›Ûæ5ìw<òyÛˆ§XÈ­>γ¶§»Jÿu{;Íÿ…D2ýÃþ øPðôÞ9>]…¸ÿ¡eü Ìv‰ç“¿™ŸÿÂfendstream endobj 57 0 obj 2868 endobj 61 0 obj <> stream xœå\KÛ6¿ëSð¶ÒnL“ ’¹5±³›6®£nm²%?¶–åØR³ÝO¿IaäŸeËþÙÞ$‡ 0óÆÔ× cDæï8™ö²àìvP6G¯›³Á×A&æOÙÀá“yðf¬Š4(ÂBãÓAEeÕ8P©þ‘Y‡2ÆóÁ/Ã`´—DBÿ_Ç Ó,‘Ã/¢¶Ïú±†„*GWŒ…í—Π8e( Žú­Bˆ‹d8!ð‚À%le¸sgpØ) o îŠÀ«-Vv Øl×kPèa¿¿h±-–ƒñ_;ó3Ç‘…^€`Ž!#ö "0ð ÒeðÀ§¾]Ü>>ø)\Ù Àe²—@ô?m´É_,[è5h‹,ô›+mGa&ø„)W!ã,Ì3Þ¿Sc˜ÀVƱWز”­áHì‹}r_ø†E}íøGË[²^ë3±¨ÂEm[ù5Üð-d/6m6ŒiÆó”àþQÛ( ˜µ8uç_C=%a¤ Í>½‹4Œ¢X3•‹‘–H,dÆݘ¤JEÉðÜöO,¤y§§HÒB3iÝv9ÚËtS– Í#©“BjDÓˆ(žÕcò„S'TÍ·Œ÷Lhh³ë´$•År½ú‹-àÒR]ЬßFE˜åZ—*z¯k")³ˆ”Xèä(Zi‘­Ì¬êÞhÑjëó®7:±M} Uoõ˜iÖ kù†–2±•^õ-ÝUÐô.A/ííìrê²°^à À¼ëºiÎÜ`Ž ôæ_ŒÌ±`Öß#éÚ$bÁU‹m\ Ä–SÐKmºl±¹K“o@R´,g[~_øøÍN ˜0ëíl˜õ –£I›|o¡} }îÃŒ~ndƒÇ Ñ‘*´Ä°ÅÇÉ2vJŒûg`˜'Íô‚/Xù¶eÅÿ‹cÜ?JD óÄHR)¡RB'¡U)Aç˜Q¨b•i&…£=¦EÉ¡ea!U^®&×a_3†©œ08fp@ÔÞ™”+J„Q„$,´ÅƇ®»gÕdiÁǼ‰På‘z¿ëÑWZš‰Š,ŠTjø,Ã"N"ÉPùÀÎùëPÏ”‰Èô±Û5ô¥$•Y:üÁÎùë¨ÄI†’ËŽ,ôÁ«,ÑèAãSe´Uc¥Ó+%X9³Ëæ2Û¨,”X(FCXH $Ëÿf”²Q'&íc ãå†KÂj¥Æur¹ž†,óÈQ¹&|²ÔÐæs5Û$ÀØØE‚åÌŒï”áZ„*wt8™Ñÿ¥æ¸ 1+ÎÎÆi–ˆÄ!zK­¯hŒþ·R“„ÐÖ—iÅOµýÓxHÿžJÉM:öc±Ž©uÉd„‚ ÖÒR«`¬—š‰œËjnªº5;™bÁ˜¯MûÖ@|c“ý®Vè%hðþKK°,Ûz ‰JUM«K¥ñ/eˆ.&ÑQ/Rk[­VdÚ·™¡³'SÊL¥BªŠWÝ“§±x¥h1o !ø“V©³,®UÓ` iSÕ¦‘å*þ9ÒZKÁ­¾aiGc歰Ηc1ÙÒªh•-§šJÇ“N9Ÿ™‘—[ÏeêlØöŸC]ba?s%™WˆŸÃ³4E¾V/”8©(2$4ëª o©šrh!ºQ¢“ü…hÄ öA#2 µïèÚ¢ª°€›¥û_2᛿ xÆÇhï[Ù3þ ßq#ßrx7‡+'NáÈè”Q˜ëp^k5»…½„ FÖ8!·r°·¦¶W•›¦ƒGÒ@º$# ŒZ½ÏE™Ä#ˆ°ÄrŠ 4ßäCeÄe.ÆT\Hõ(®©>Íuoo_€æŠ§ª¹â%hîcº\ ð/Aq™^>3Å}®.×)"0-fi­Sûî_bG­öéÝ´¢7­’g³´nÏ1Ú@ “·G‚=aÕF‰ú?QEç;ÿÆö ãÈ!õT£T‘sªnBh²÷|ÝëÉü+0Y‘ñx 0l*\JžÐ Sx„g6[f`%FÿUu6“(@mq2ħµG©øxª3 8>12³åØB^—W|5jÆšc^ž’Ž5§ÅïêH!íY\·NêCT[uG5ÒJ*{”ÖóÄŽÑï,„Þø~´Ðw­y;ß¶_²4bsÜÞ£sýÞWÔs8Å‚øæß>fð¼`x8y|ò¸¯ŒØ$mõ8²?!i‰Òj<¤äï¬(¦¿3×TJšJ"1i.£8· áÏ$—ÐK6_GÌ3½»ßF¶ÜœEÚäà ’÷>‡²·RÎqãæÁ0äVNWÜ­ƒÜg§×$'ÕUüúB?uÅ‘‡µ.Ÿ_†pS‡6'ÅwC¸–Û Óe}µÇ}¶'L³[´Ö“WÙLsš—w&gâ©Æý/ïæb T™kIøÂãæèøà)µ½ìãÇI ì:z0{0 ýæŸ䄽åî?…çUÿVÎý!+7΂»‘‡Î™¡vy­ÎWD=îK»Ýœñç—èM|IAý8›eSá§ ·ÊXÎÌÖΨ0÷Wy•Dq-öaß7üdµô}n!÷½ófk¤ç§ôüö¿ý)@}žC'ÈÌ8ýòdûÄÁ·¢À—ä>ðf`ìá*cÏ 2‚½0öêãøs,Óͬìñ•Ê =oÎ}©)S|¦(O6~è²¶ŒähW2êaçOAFÌ/- îÊ7qG¹™}ñ¶G­CnUj;L‘R.2;2ÅŸ)·ÅL7 ÊAŸ-bbWæýØwðCïŒWã o <ðùés¸vô¡r D_'wèÁìݵíÍÑÞgæw¼nyOà>Ÿïfx­HÕ£E•¤vïVqß}KÌ»&ü­:vl«8Tã 4®êèÛç¬õÈû½>ï´}lõvz¿-ÄŒµ'\Ü3všÞØèMqn½ÍKÏ[“ÊëU)Ã<µO¢Ñ¥}ÑñÜ„ÔUQÖª×Ç£nQ½ì¹‚ ΀Û´ûd%+¨`FìâžB‰¨Ãi÷c¸þ«¿Óö+¾]?&[bì'˜ô™pYìÅ&| ‡¡/U}¹Å6?H´Ù¶8æíTýûyO68GURRîO€›G{ªtâ Îd§ÕNÏoñ8.g€àú½‘?ôñÐí…бšO?,C³è‡eŠû3À´¶ˆ,÷—ß®Ü~—›¡3*q”JMwJÚÛ¿ÛÓÉŸ…oK8i÷ þù›žœ™`Пô/ì‘èÇ’ºœT_GÿÄ¿Ó"£ÛÿÎú›Õæè7>¾!ìTû±”Zì>þ©ÿþÔª™endstream endobj 62 0 obj 2860 endobj 66 0 obj <> stream xœí[KsǾóWà"%.wf籫¤R%KŠ¥È*;4+9(:@H1¡E'¿>3û˜î™ý Ð’¥JlùМgO÷ׯÁO“<r’û1ÿptzf'—·GuóäìÛ–Ø\ýtTf…ÿ¯nàôüÃä›s7PêI•Ufr~q”gUUæ¶™ULŒvWzb…ÈT99ÿpôæx2=)réþVÇ?:2Ó¶PÇçz¨×zÞRÒÔ£›F6#eø®¢A¢*“ïzÛtUq¼„俈œ¹€}¯‰œùn†·ç9r|Žßÿ>âÄÙåĆv¼&r ÄZ¯à°výYx©+ÿþEYy±…çÜЉN¨õru™ö¹ÅòÛ/ÊË%äår±êvÀÚ>‡w„ùΘýc¢/!‹oXçé¯&ûO¿è}Í!?@’ÝmÄ)ÆKŒTsÈÊ[Hb½„ ¢›!Ð(ºÁWz¨ ”;·[£Ð•c\׿˜e]“ÕÒá­rE¥\Gÿ¹¹õp[) >;uug¶üËŒ†¦Ÿ.ꩬPÝîë^l×aÖ5­z?­2[:ùjæ{ÜN¢;nÖ¬‹"‘gÍ—I7€Z÷Úö°¸7;'šŠ–ÆÒÂÄ ÛÕÖ³^5'’C¯À¾¶Ñ©j¾nÁW:Ó{pºE̺8f‰zÞ€}mÒ•& Ë0äg`gðW†]ö7ƒ¯“"i§ÌЧø>’BjºòOõ=è÷@Yþ?»Ó³BNœ›”+JxT*IxhQdR5 çAç™Æ:&eÓ•é*ÏÕ±Ú¬R¦¬wS:˜ñMÔ“*]1zB³½˜ÊÌ”¹ô€/=Üç…ôÀ]Sªô!‘Û³Ís£½°8º¨¬vÇìº.Ùœ^„GUîú«ãgaÕ_µ…[õ.ttöÕYŸÒ”¢ÝjYVU<…-Ð gzí(‡îÂúÐɺ³J +7lR•ÍÊ:Ùäÿ3¶€ÎûÆ!½‘̘-Å ‹ 2Pe *4ÄŸ¬³š³`¯c³Æ¬ê\\Qeî0‘„kÅ'Æo‡‰Ü¦¼îÕº@vè°`A]%‘9uxœ áBo!ô%Ó~Is5›BÖRÙMàwïHYTN(ÙnÃ\Kj]@V°VæÆÜÑ N.¥S`«;±’Je¦¤ó…ÓØ†×ªÏÕv_Ø3a­Í•“ŸTßÒ—pÔŒv¸àgL¸è§eWzÕ¸:‘û³‚óo¡«Ä:¸àS:cª’ëÉþžm^ Fe4ˆ1žMõ·éItLwÂzù.]¤ì(¡r͇Çà!·¨g‰heß»±Âh4͵FšÈ{âBÄG/('¤œ4vÙK ¹ÂïEŽô]À-çVή+$r¯—¡ãG¯ê¤\sÿ'ãú#¸äÊdR’Ć1ù]1p ] Âð Ì á¨à.¼eª?yí¡ô5eH=raU4¥Fi2¡M‡ \gN"Ö¥6àòzN¢»œ"j°pÿÉlwpÒÚ»Ekþ³z$dtvr6f¬Ÿ½ÔŠgàMp9ÒCk4;ኋf̳= ZǨΚB3ÇÎÆF1Ó”†îMl´\éP¥ìÐbï(’* Š>ôüéÆß6¹GO;<Þ†QWúÏþ3’ÂÍö•9"T|Ú”Þx©sæ§ŒNÍBQà¤âhì€keËA^$ÇÖ;ô`j&t˱Q<,;Ö’8<šÈÂÃX>þjŒáäv)êýáAÌÅ⃀vJä=dÂÕØï.ˆÄE‰Ë”£u£ CðÍøS„QfŒÃ kÞï+È \TfÀu™y3ÖMöö½€ä.AR÷ý8¯¼?½/Ú‰ëWÅŸh²P Ø-3åÀéœO¦‡ÔOwkÄÿüãß*Ÿ½ò`>KéaH|-EˆñÜ÷oň_3÷©z6¤íÏšðØZqH¹:¾MU1‰W™’óxsP}g0‹Âý0õ;¯³µ/ÆÄ^Ò2‰H9X!„ŠÂeC¯áªmªMÿaGî©ÎŠáŒ>>Ü7Ê@5‰•UZu‰’(öGéÏ^ÞÊ­øv Cc(_8’u]¥—”öuž™ÉŒÒ¶êåu^y °¾˜Ã=kˆ’ ý 4KÍ83¹K¶±…Ù¼ŒÄéª^6j¯"™,D ;Šdåî"§5£s\${JLë^J^®¢Ö*P¬°6góϽmÊUÒòÎk0×moƒui,Ý ’¶ä©-«›±zu½® sªðæä5¼ë`tÞx°÷têí¬*Œ]Gi +ȱaüÜ´ÇE à±ÚIsc٤߅1m½ÏÉ28õ>)tf ÕûZæ#J*gC€¡i5R3€Õkˆ(Lÿ¡©Âe­®(®Bõ²–^‘˜Ö²}B_y†¶‰Ïy‡ºFEª®ñ&d毻t<;ùÏ0ï…I9AE` å)³Pâ+`¡sGAJ+É-Ô®¬| ªØJ`fP»††‰­+]IÚ¼o/˜Hb+ºE,ÆÃdÈG´ÅÑŠÆcÈVha¶ôµëà0ÄmMô–þúþÒÆÉTZgp*ÛVT¼ÀP=åM_âÉÁø¸#!ÑÏ„ ÔˆCà9uJ½È¤c_ƦXÏ"¿ AЯúJjëqÉÔp,>Z¬„½ €UÒ[ìù8Õ~?uɧmbÇa°ÆÜD‚†XÜKÝAKÀ5Ç8¹ 8*­ÿì-¨Qµ±CxHIlÌMÆ~&)ü4d<û5W¬ÿ¸V}È[je¤&’=^aKÜÀy?Fãï8JïÙ OÍæ¼ÒÞ„Ôãç“e'17PâZu‡´å  ²“q S…‹ÒE¡œâì›Vë}.r±SèAZƒ™X.²Ða&æºA? ÔÛä=Dý÷móf"@!~7“‚Ï<©ÛÓ ìÌ›ºqó50„„öêfpxòùPµàyq0Aðöxv…âAB %£ž,zìJ¦°»A r WbGÁªËnh(G²?a§’­†³ Ð`ìe߆](”ÀùwA†”e‹T:3Ux×Ò{ŸQÃS{“ëèqW„CÊf¥ipÈ?ÎŒNE/½@¹><œë§ÿ18ía1‘‰¸ŸY>³”K©ã=ìàaˆI 1G‹UÐa—-ªMÔ.[UÉñH¦a#_ºlbxVÌð–O•€žP²ù’®ƒ—@E?ó­/¦&Ï={9‚Âd¢¬ÂüC‘UØ%3ÝŒÜÃÏEQÖ'Î]ÌN™}ô © ­w·½¿)Â|¼ 'Þ¯òiŸ¢Ë<Ô€³ËGhõa¡u¨¢@Ï“‹&8µËëPTÆI¨@nÕ yÌ]¨ƒ^‡¡×OèmzK0_é}ÁkÐï‡U°õ§¡ãàó´¤Ö…‹aw uÀF ­à°Ñ³= *œŒO±waüžJe(SÙ«ª$VŽœuXAßóI- ð{…9€kÝ#KhÇÒ‡LIÁÛ|ç—`¤Á¶÷eÇ'‹ˆJ@iÆ1æÖ¬ u`(# Þáxó³·28_=ô¯KaØÙ8ÀU<¤>±ü.ñãòc〰Ñ|èyûHVÏÀÌ ËbýÞt³KèÒw¬Pói’V©-ÑùνÐ7F];û6”}nþuK -œ€L™Ûk¡~v”â`/q˜À`C™FaÆW²Cxí(?×dsÔ‡7=Øbˆo+ŽÊ»hb÷Ö{gû Ôy©ªHrS(¿‡í ÜhÑ`ô…v~yÝífLÖ¢j¹,¼‡YàeŽyŽôýÂ+Žã—¡h¦s d&ãP„È—DFðÉ`ëùùÑ_Ý¿ÿe5y´endstream endobj 67 0 obj 3325 endobj 71 0 obj <> stream xœí\[{ܶ}ׯطîöó2$dúõÁ‰ÕÄñ­–ÕæÁɃ´ºXý¤Õuº¿¾à 3  rW+É­ã<Œ¸¸Î Àá\O “°ø×‹‹ïöÔäôv§|<Ùû©nNw®w² .þ+pyq1ùa_WŒ¢Iäédÿd' ò< UÕj4IýwžLTJ¸Øù8}5‹ƒ0Í¥œ~0ÒÞl.‘©<šÎgó$ÃHäÓx6¢ Nò©šéú‘j:ÑRœ¤iO/t%„*VÓKóûQ)Å"TÓcŸQ-ÖÀùl®´¬¡¨ºÊâé³^ïè¿ÿTü%‹?öÿüqún6OƒT&AKÓ袚j”¦l LË?Öd¨"^ÿ. Á.¥\4#¬© 5ŸöÂ2±M6Ÿ p ª ìˆê­ÀêÝZ:dÝå$hNŒâNiUð™MX¼ l›0«µâE,ÅíËü~êá¾Ta9 Ðf1fDŠÊ&ô4„‹EL 'nÑ š"V3.…[eÝ&,L 8ȳfãÂûN=Z)ó!<É ƒ­Ã”Öãºêx”¸ÍÅ Þë²4CW5Š@м­ð†nï¨V@âŠ/9ÉšqµIT/¥R‰KÞ1q5‹Dá>åÜvQ%K÷-†(ŠRO^WdYnÙZ?ªÙ²…kî_,mlq=²áÂò§ÛÚ­ ‚4Çõ5p'o¨Þ5ȃ6~ʧûº´à k爵ØïXå–W\¦±WžJp`†d\ÖeÐýž¤×ynœ‘œãñU½œãáÇí¶Ð•D|™ o†—iSÎ²Ï²Ú ×Á_åëÔS,H +  Y^ìE9x,¬š97t([2©9Ò'°*ŽçÛ:Ò±5?n«±òÒ<$Ž·§CÈc¶S 17©\sk1Ê scUÏ踊¬•g4"‡!ªs ùýƒ1ž]#1ÒpÓ4ÏŒcªrí˜VL"iˆ€5ÿ‚*½¤¦ªNc}\À¾¥§ZŒdlÉÈjá±bë×f¨·§0Œ-±Ë™õ\x½LÈ/Ð|Ø$ž£IþLbï‚dJV'¥µ‰Ö†^/7gË^ÙÖ]êF¾-Ò†LöLlà?eYs,b³¥ýž…è⇤ÄH!«¶I6vÆ™dP€Ÿcå·­ö˘óã4Ë4c#òÝH1 °\“ΣT}Ðo]%,è”UyZ¦+ò Ì.ðÝž†Ls¿åŪ?TŸýã QQmÄw$þ:KðÀWÕ»),Ô-SIŒá7ÝfÅ›)6Ýfk*R•D‰\ª­ 4ýxÿ_­I†ª–V¼>ÒËf²ÚÑþÙHïŒô«‘&Fú”{3°Ü[ í‚ûFzÙÛJ3úšü3ÍAÕ¡¸™Þ­Ú‰·$^‘xNâ‰_`µ ‰ W$Þ@ñ˜Ä%‰w¾v/aµ%lwÛ=ó5v[â(δ¥HÁv<ÐÖ%.+𠌕UPLH Q4oFVµÈhx[‡1µL |Þ TTã%èׯÚGËÔƒ—=½ƒ-` áj¬ÀÂ×ñ ÄÓ ,{ÛÅ®pœk®LÌ£ð¡ Ìt=ȇôߌ´X˜¤÷>v}X¬Ža˜,Ëz»OñÚÃj¬·CÙpEoc'°Àq‰©— ëG5S®Ç` «VçÕÿƒ¡(‘öó¿w`ã7ì93^¯`»¬…kPVðŽW|8% …v&µÛ’¯9ož=8›Ñ¯ÿ0m¼_1Ž.á ÜÁᬵÈqç"{f±ææ}ÎEGý,†7dwgåXÜ>¥-7åWµ±>P®‰Ä.L ÜX ø˜Y'³‰²ó÷·ë¡Ç¼j½üÙÆõÐ.‰ûwBQ„1½ßÖu.{Ãò}ûÖr.Â2™Ø8þÛ 62æÔkN{MQ1 àhô©×ýVÆBÇŽ±þ0f¤Ó¾c_‰Š!§¼޹z•õε>ËiüÏð–°Û~Eï»tç^Ж¦íO؈íÈI¡÷½?fÛµf”qêWÐ^wnŒ»r˲ÆN;ÕÒ¹ô¿ùnÍŸHÞÕDêÑ„÷ØÆ–÷–=÷© ß Žyîø<-½ÑîG¤¡Šn†ßØìC°·B÷h¼ùû lc·PD¸êB&Þ™½wÀl _KØ›Ð!lÁ`Âs<'_½9h¸aO’o“ZµÀ‚z_'zo5Ρ®îõ,½ép Ó¼œþs8ùXùIéÇjì3„vfñyr­M¢ÿŠöZ—æuþ‹ð-ǶÌ3?[ü«ûÓ „6—6ŠÐùh¤ÐHß)Rn¤ß}ê,t_ÄÑ©T )!„îùÃDejÝ#.Åöc5Æ0Ì]Oð‰‡ƒL¯Þ<¶â‘n.àS'0«‹1ÆÆï g/cPQ/ctű!‘iló˜ÃÀ€q Eø°¸µuu.·Fè|;Á`¦~ˆþa¡5>ýÓI4™á•Ø ýO,NfÓE±ü¤g#¨Î²eµ§Þ{ÕÅ’‰6œ|ŠdÂt+M°¢m­VäQ #ÑF–‚g6èš}nÒ¾âPgï«v«9w|­Ëñ ë~ü=%!YûqôlÒþbÖþ¹‰(Êœ,èÎvÚþ$ƒÝJrÛÊŸ4i‡V••XÀ¾¼þƒ>±¼„—Õ=Méêü—5]P¤–ýíh“ÈQ*n¨ÿ¿>e=°Æð§ÊDõ¤¹XÕùª@Dª!ŽCg¸EQªÅz +û«Y×û÷«wøü\É®w(À –ÍÎ5ô½Óû2Ÿ‰ÞCxÖxÍä¿ÌD®—Nعö·a8\±Ì3š°„$Õ—r…›–ˆ”Þ |$•iH'™LÓ”•ZÈJR"U d̘ab_ºH^‡€åúV«ø/VIGlÍ[¶S›c/Ű½¬™ˆ§ïµ\kÊŠ^s¬"+ZuÀÓ,¡²â»"æ¸5sòX¡¬õ6JÊžãM7Ç…묛„õÂtkkš½ÅÊ]xÚˆ‹·€­¶F6Ndf–‹"•½ÙcXZŠ*Â]„™öŠb; ¹d)œ¨1ãBØj6”ɳ ZŒ&² Kí´K×½îV£BNC8gÅ¡)Ia -¯Å` ´Ü–‘î†é‡%/s’I”^iጲäx(U”³[I0Ó™¦VÕæ6ë Ÿ€Ìߎ–èËÃYïveÖ1Ñ$“j1ú&Î3ÊzÒå<·üÝ"÷Â%ß¾{}ç¼×w®„*#š 2J=Õ Çšÿ8ØñŽO›‰Lq¨ Ö¢/áSÞ]š¤«OvsÁ)Íþ]s»>˪¬ßÅiµXÙúÌVœ 9‹oØÚ&,–5»GÂby¯cÊöO›œüûö°BS‹±Ù3«¢ßÛ ï ÒÚä…Õ‹ý/"2Ì6–#pa=5u9ƒØ •H¹Ëóô¤¼Äù|91TGîâS©|ÕwÊm'eºÅ«…SU1…‘?…³Ú ÈHæñðÉØWi¤¦OøAÚy¹ùÉX×wM²=Có”í@Ò]×UÁ½^Ôi;õÿÍUl³þDÓæƒI9>€•'“³3ÒÙUð¹D¢ÖIŠ£¬à½ØŠD}M3¼‡ TsèSÌ‚&"&Õ,¾“ÚÊιQLª@í[pê¸àT”PˆÂ éÍïø—Þw_TTúÞHà |üfžéÁóV¼ë¥~¯Îàɽ®2¢ó’jwç½þ÷_ê Oendstream endobj 72 0 obj 3381 endobj 76 0 obj <> stream xœå\Ks¹¾óW°rYrË0˜ÁÌÞb;›r*ÉVlݼ9P$-+%‰²(­¤òßÌݾ!†’“h+»{èâÙhtý€>ÏóLÈynÿˆÍõìå;=¿8ÌÚÏów¿ï‰»‹ÙçYöŸö§7×óWg¦£ó&kªùÙÇYž5MënT1¯JóÿM9×"Ï´ip=[Ì—g› ‘•r~öÇÙÙ÷gË•¨2-*¹ø´4í„Tz±sÔÜPEYUy±Ðî#Q¥£rÖeefÐB5‹­û¸Æ.d®‡åÊk~C?퉼§%°Õ´}Ú¾EY‹[êp ‡¹[®Ê,Ï…lüÝìíˆkêðËÒ´×yYò%³ö¬)[ø–·uÓ°ÉÃýß÷#êzdµ¬CYÁÖôÛea×܈ŕÛ4h ¡â³ÜsÆýõì3Y䙪•‘›³íìŸó†š²‰6vé{ÈF²al³!Yxì¿V‰‡:P§pël‚¯Ô–͵ãbá6‚EŠ}ýDmwð@oá&Í© 3³–õâz¹’Ê¥”üŠWÆ64ç¹.„ôyî¤ì²ˆRÉ‘±æÔ´[MQ•o;ï%ÆhY Ã&ø79_J­BÒ¶óJåTz¾ê•‘¢_®¨*¦.nu@?wÒ]4ÊÓYNL¿˜SÈd­µè·m[ÝðËÊnÄ—e“éºÈ¥:7;õ2qMÙh}Œý½G¸_p­ÊžªªGDÅ“\7Àó}ÅIÌä´°ÚÔµ§ew›ÍeÅ'ÁŠ*˜¤—<¶ 6Á=lËFØ:µÔTå dÞ!µŒªšºçIeDÚWq‰5¾¦~Z®ª¬R¥n2SHÓ¸ð˜vé”èØj‘¾¸‡2³¼ÙÑL*;…_˜ÿú[·8b[+˕Φ¿|×ÌEéá€&€•Òf»|Xf¶L•eew±*ež•¢^¼7_Mߺҋ¿/E‘UÂ,áÚ.W©&W‹sË&-E§9j!šº¶†Æ,Wꢮ¬:wôÆ›Â}~c?‹º,”×Ú°µ63›ZÕJc×™¬òÒæ¶n*¶¶¹÷û0Ý °íÈÜ\¢¾­ß·ÝÄæŠW}cÙtÂn/7Úû¾Þ/†*š¦?€)€«*ÌIJp©ÓñÖ["_Y³^5JñÌ-%K`³A¥kHŒ·áOÓ¡`÷ª¸ÊÅÈ5ƒÜ¢ ØÍaM“v²G‘üÿEc°2’"›LТ俸ÂC‡—èªoF®ºëãi…vNÓ4ÚšýÝH§,2%›öféL¨¼tvÔÈu.7U:5Ló³¦ æ`}›‘oH1B½ÇfHîp²õ ŽŸ©v¶&h{Ämw–»Ïi*¸j ¼®ÉF„0¯^ÇüMõ+4š‹Á1 }Ü4H ÇM õÐE—²Ö¥äœc i6Ò]§ÂÜëNN×hþÿ;£eéÔ 1õ ß s r2 O-V†ÌJm4ýÆQ–÷µPY«#\KFn]ÓKGuë¨+G­;J4ö 9'’58À"?Ánwð+ëvOät“ÄCyœS‡ÿsNy³`7Öö’È"/ÛÕQ®_ÿO¹Žw‘ÜüCŠ¿lÜ=œ‚­á6ØÁx°K¸·‹éW€øú,¯€&²"2‡ X··Dþôœ¤`ò¹ùÕK¥ÝþãÇ"Ÿv,ž»!Èþz1Ü×å «¾vƒKô‘!†v¨“úüŒdÈ3 r°6aÃ\@„Ámë)´cG0´sRB@W”1dvc°õ~¤F 7ã¨1²x,sƒ}ã6–²Fš òG„ñq„€íà¼ó'„YDãB&Ö2w¥CŒÊ›‰Ez¢`E‹ÅƒvŒæ1¯°‰ÃFìtqÙã–q=t]€øSâº88ƒ#~ìqt{L6îô0Yþ®|zM4Vç”0÷Û·Yfš„†MÖfAª&’teC^§qNÍXdÙ +ã64§D€ûµÔÂÝ…†ý§¬‚ID’‘›£éåœO·¯\Ó׎zû÷YÒ5´"kh³~^½…hî#‚a‰+8¶‹Ø chˆÀ¥x€˜}j?/OF1ëégDÇùP !F;È“gæIíàÏMz~„bpÇ8UÕmV ’UW§Àªé¡Ú-ø™ÙpB]Ž"È‚M!Ë(`³¹†V`CÁÏ1‹2{E` eêyü*0®BÈÚÞ½Áò…Iã-ÅŽ™qñ2½Él; l!5ÔvË82:¼¿u¶²6êa0•W 5¡uŸ(Ð9P0kŒóP'Dd´F¾R6™,£À¨ˆÔXÐÙ‘6Ý”íæ .Þ{2ó94][M r"›~ée±Pp¶W®¼âu·ÜJ<\Î –džNldVUÍ 78.ü–²è4W›’ ÁS)2ᇅÓ-ÊQ” 1…ûióJý~JcIß7 ô× ì–02)¤<¸ŒÁ#ί§²KÝXV†±àbµ‚/Œ—ìGhßjvÒ?ÐI³=$Ë1ÁukðR}˜MêL5¡||ÏܰlK[ç×<$ú9jÊùˆŽ‡Ì Χ] º‘ùŽãŸÙilH à4z΂̮àâ%fpYƒÖ^Æ FsQž`æ+¹n¶Dä"ä[°Æ׫]±dP:0Ø—¾ Î ÐÜ÷µg"«j jϾR÷Èu·f›Í…ÉŽäŰ՜PP~V¼‡mç|v[KÕúÁ»DT¬ôŸ æØ«‡aØ=DŒnx­Z ŽýÄ0‚²uDI“Òt3ÊÂ`Í?zmˆUµ c/]a¿™¡ò ü£Ã â¤0ûLII1Íã±kÐçX %WBõXLðk T°¨&! /ßø‚¿ôQµÔ T~gÕ7/&.°0Èž³sÎḎoeúÁ„`:Üȯ}åwO7²44ê±_¸ŒÐx“ŸÜ<òÄkBdùtاÁ~ÛzwГ³\ôB¿~tÔ?õ½£þà3–- Ð@Ñl/ÆA‘E2áb”.0¦ÁˆhPræ+)os ñ”[ñÖI·ÀÉ7»u®AÜ %‰û¨ÐØØ°Š'c/â¦É….À%ɯÏò¼óN»ÔYÕz$˱;Øà~eÞ†óߦytáÞ»Â-¸eÌLä}ÜÀûå*OpPF. ù&È ‡VƒS¯°lxU2V§æ–¾)¦xãZ {ýwŒ aÔÊ‹!†>ùöð-„Aãï—ør\¢v Ÿq¤×7™CqQ"Ç4 Œ±=2%—45‹ 2(œ³bþÈ`Oõ"•ÇXã;íï­“ÑZ ™öÝ”´:s7×äƒzîÈxx¿Ï¡yGa¦ìeä¬kH?Ñ$ŒÀÔKMÓ„‚[ŠgÚêgµµ,ÿÀ8gËn†2ó’Èá•j^¶-S3«'a À¯©Õ«p€ýÔJP—FoséYQï?® ÅÞ¿j‚ùñ;xk j9ÈïÓÜ€@ýD¸f‹ÿÉ÷¿ÂÞþý¯Í]Æïm€}›Û=áïŸÐ²‡µwVìµ4 â_7ôt·}+«òÞË{ñ‡Àï-æP¥}fÏß/ sŒØÕm˜¦ý1ÉÈË[a¬ŸTôòö™½¸eä ÑK¯ÒËIWE_Ïyƒ(Ã$ûS“ N®a>š³®%»k0Šþ\I2±ØJ ŠÌP&½‹wj#Ö5ø+'cqžJ¢17hÀ÷;ñ7O“0À¹/èQ.VŸl©£¦h#pO>¡%d”¬ÛašmÄ€=FE+Ó^–%ï©^Ñ{™~ïcäæyþÅÜYX§¥óäjyUWŽÀUWœ;šù¨è ¡Ìú„¹ˆ+’E&ø°Çá®Ä‚®O{öéŠÀd^a, ãȤ>¦â R;®?P'|ìÉ'1¯–«Æ•ÙÕ¶Ü¿•.eŽ·pè,I1VÁBø ‚->ªá ИöšìÓ¨,T°™¬èŒŽ½­n«M¦ìp4Þs‚jó%T±˜¹¸˜é© ¥ÍL¥üîlöóï¿£7^endstream endobj 77 0 obj 3351 endobj 81 0 obj <> stream xœíYYwÓF~÷¯P=D,4›ZZ’@IXÚ’ø-¥=Æ1INcÇq/=ýïŒ,kî•üY–³<áäåóènóÝeÆÖ¥…BzQþ_‚Á¨óô ñN®:³eïàõLO:—4TùßlãÁÈÛéYE!¼,Ìb¯÷¹…Y–FIaUx±±Ÿ3ã%" +0êù×A*Ç‘òO-L„Ô‰?tÈ ºBX¬3ÿ<ШLûgg•É**ÎâÄ-N­`ÚPÒŠvù˜EQR2š)™0Š„ÌüXHÌÕ>Á1Y`²Ÿhõ‚`eûÅ^RÅífEŒq$ü.…3º3LJÐ{ŸVÇPÉî*ŒâLk7èÊP¦©6þKû\çLgþÎ…ƒW6$Tld-hË»ÕÀœ“ý̉ýØ{Ó‘R…*¶vzǨT÷ë “íÃܲU¦¶²$؆=YŸú509¥KÓè’À% |¡Õ¢ªóZþJq±ç«Ô(±0šWÂu€ê›ŒâšÞÎ+%‰2á:´;/”TóžØ‡5Ú9ˆ%dµ*sY›8g^çãDé¥dÌjFÉ0ÒIY3ÅŒ–Kô½@&‰]NrÝ®6&Ô*õòÇFŽÝ ÁÁ*•AA¬JhNFTš,·9Ût^„üs%¥¨û™ï³¢$#yÑkÆŸ Ê ¢+çܼëØÏ[vÞKãæ½ëqš?ê=:²“««B“X¾»嵓ÚòyϰÅ´íСC»í/ ϼÈüŒ‘3;‘¡æ¢;½‡¢‰L•›É¡.¡¬ogîhB²S‚gǯ¹±9»ºdpà"¹pèØ¡aeÃq”„¢(“ÚsEèéX;tíP¬Ç•¸J~h/§À⬠KªŒ±B®\’qfá”`UN®@Ndê*žg(ª×Jm×ô¼f©ª, ²¨)Wu$Ô‘‹A° êªvÁj¨¬[96P×´s£17’”@IC¬bEFÓ¦XÅŠŒf­ãÌþÝÎ1Ní0ç¨c§àé󊲣Ö¨£¼U-Ƕ >Ïofw¯ÞÕUÞpU¿X›·­[óöâžxÛº#ÞpÑâÎ~Æ’Ú~K“›òò‰b:‡ÓœÅÿï=q¸W5Á ¬<ˆ¦€dÔÝ1dù— Ë7f”2>4þkO2º3ÑlÈ ÍѨ9^Fõ²»Î÷–7êºòàûáÚ3üñ­gøÃ{bøñÍð*oø²²Í˜©7 éâËÊ΢.J¾¬ì¶rŒì—íãÓêU+ÇxÿÖÎ1ž-¯kʹÝÆqYï5«R[¹3ï7ŪV¤ö×V~qfÃvŽqjÿ Ö9æiÄ9?ZÇèæìÄg'‘Œ›ãç ÉwpÐËxŠüßžåpþÑ™øÅ¡ 7YF¹æe¥øeÊ|Ï9tB¿’à¡Î)]È?)ãû 8EHOó· Óœtñ4×.X<Íß·rŒgöïíãYôG#K¸³þlÖ¬H釦`ÍŠ”´rŒSûC;Ç8·?–ÌŠ”>j,NéÇÍ q'±Œkÿ§ Ë·ŸáD2ÿ´'yñeŽ.ÉTèg„ùR¶£M^¨úéUžÄô¢k²À¬f/¨*/ÖÊáGÆñ¤~Ú0üHOêÚî«^çƒýÿÜKendstream endobj 82 0 obj 1239 endobj 86 0 obj <> stream xœíXÛNÛ@}÷WXE(IEïú^‰ h«R ½÷žV¢¢I!á­U¿½NˆwvíÃzã’‡V&/‡õžÝñ™ñÌh®l×aÜvg¿ GV¯Ù§Sk¾l÷Ÿ.ÀäÔº²bÇ›ýÍd<Ù;iFä8Ih§ß,×I’ØnNevdÿ'1æø±Ž¬Oí°Óõœ òü¶·@jŒ%.‡Ü_·Ë}9q4ƒžË³S|NÄÖ†ýˆöß –xóspDðˆà„àE¹^ApFjàx[—Õм=´•ƒ€l•pªtNp 7lÜ€rÓ–=x×P~üÙý6¨Jø•ŒúƒFz‹©¸ W}‚1Ü0$x¦÷\eU\œ–ƒFÝzêâ,€ø½Fäz"÷úŒ‰Þ!ëE2½³'-µ§`®#’ÉŽPú‘@UŸðlÄ¥Œ«GÛ‚³Ÿ•Pù–…ãh+y.,ô™2 ™Ë!&]t ½Íë×ÚÔŒŠñ±@'ʇnä0?‚EýR ôt ÖÆ] tÖÎ…cÅ®\ z—3p⬠L^nc)\‘Ë+;ez[®™Ç¾â!WX‹»¿)ðAé ‹¾,¦7º·‹L<1DdÜ/òYåàŽÌ+_ŒÅ™of,îE£‹q© Í.Æ$*± ßÇcY…K±¬Â¥®ÑÅØµk #êæMåœ:%umEer³Þ¹úv™Äñ½µ´€­¿pkE¶îH@U7üi?•iz´z0‰ŒsàÃFäÚ"£€‹ÅOs•QwE‰|Ð-7lA]rÿ'¾jupÁþÒ|wðuʸµéI*çÅ8¸£9,p°WkÌ«‰¼ü¼š¸5æÕD6›W«ã9µ0–Zv³1µb¬WáRݼګpí¿7¯¦72›W«œfN½Tã â¨S¯.y“Êͼzu©ÜÌ«WË4iÂ%¯ ò4qp¥Û+pd¯Wºçe.2•}£‹qq90»çÕZ•p–xid,ŽýWî“Ôzýþ÷/‡endstream endobj 87 0 obj 845 endobj 91 0 obj <> stream xœíW]OÂ0}ﯨ‰Ñ¸²vݺEŠ ñ…eoH ¢¢‘© >úßí`´EnFýxƒí嬻§÷îœí$›`ŸP†ýâ\€aŽê©À£74[Æi»Óš ˜Å1[0ñ0ÇÍLYˆ’D8»G>I’Øó])ŽBy„XPJxŒ³õœÐõŠ€;~‰Xä`×£1å„%NWÝÇn?»@’Ê$õe5“LArºJæ«\rwì yת1¹5»Æ!Hîdq*H, øLŽÀoæ{Ñ$pÆ4|ÖðIC¬á#Xk44< :à*×0 †>€£O•*/‹âÚ‹{«Jï+ô®Ð`©‘ù$îüÅЗëíKÿk‘u´DJE8‰÷*2Msá nTeš&ÃIÜüF^æÀ|b5,À§vÃÂܲj çË™]cø#h*‰5–v*†k,=¯V¬±ôت1l-±k {{åVÊ{ÚÛæËßC\‹ ¿üG[‘ÿ!ĵÊpJ|Ú«> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 75 0 obj <> /Contents 76 0 R >> endobj 80 0 obj <> /Contents 81 0 R >> endobj 85 0 obj <> /Contents 86 0 R >> endobj 90 0 obj <> /Contents 91 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 28 0 R 35 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R 75 0 R 80 0 R 85 0 R 90 0 R ] /Count 16 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 88 0 obj <> endobj 89 0 obj <> endobj 93 0 obj <> endobj 94 0 obj <> endobj 95 0 obj <>stream xœeOÍJÃ@ÞM‚‚¦þ]µ0 HK[H‹—ÐcЧ!ÔzÞš5]ÜÝ„üTò /žK¼ö(‚àÅgú›£‰ „ù†™ïg`024„16'LФ煂Èz?-qy¢•M½‰Æ+½±4õ¥i<©÷#õv¨^÷ÕËÒ1~þøtÂ(Y0O¡uå]·;îÓ·mfù#š°@ÂY5,(#Ae:§rsÎn ày4O€ø>õëØ”pzŒ³( ÐrÚ0°¬~¯jƒ!¸™ qØ&o™diDúp)h@@ŸÖF‚¥qç“¿i—‰Y–À÷§à†6ŒÁ£AÆIü_Aa¨€4Œmõ°WÕ}QN 5)¶Ö;›Ýõ£inVf¡/>stream xœy T×¶v!tuÅYÚfX…ŠA¢qˆâ (ˆN(Š Š‚ €Ì Mw3 ¡'æãˆBhDˆ³Þ¨Ñ¨8ä:$ÑkŒ×w£ÉMvåî¿Þ©n¤;Y/ÿÿ/—@U:ÃÞßþö·wÙQ=(;;;fNl⮨ˆ]Âßcy7;þÃü`ûLÄó¿ ¦|&îèSØÛõ¶G½ê>‘éÈW€¿÷ƒ”þ”½]rQåœØ¸”]Q[¶&¸YºÒkÔ¨ÑÖ;Ÿx{{»oLyÿÄ}nD|Ô–î‘?vGDÇÆÅDìH˜æ>‡ŒŽŽŽÚä¾%:%nk¼{øæÍ›…×V„GGlw÷‹ŠŽŠ‹‹Ýí>bŽ—ûøqã>C~ŒŠŠÙ˜ï»#Ö}‘ûÒˆ-‰Ñá»þp“¢¨ ‹|SvlZ8;5vóê 9qkÏݦ†Q¡T05šJ¥<¨%”5G §–RþÔ'ÔGÔ2j>5žò¤–S ¨ Ô*„  &R^Ô j!5‰I­¤QŸR£¨UT 5›šL¦VSAÔj õÕ“êEõ¦ì¨>Tª/eOõ£¨þ”ˆ@Ñ”#%¦$TFqÔ@j=åNI©!”•DM§œ©” 5“r¥fQƒ(ÊúZK ¦¶Rë(– '0 -¡®ÛÍ´»Øcjïí×ÙßwàÖ;tˆ‰ŽÑžt ý‹x¶Ø þ•I`^~0ïƒ+='ö<Ý+ª×7½«ú¸õ)ëëÑ7«ïÓ~%ýû'÷0`Ä€n:®p<)é#I4ô¨‘²Ò+N‘Nwœg8ç;w¸,rѸè]N¸ü·«›ë׸AC-vót+ýÐãíƒ{N|‚uc£ØÿpaÜm÷îç†L?dÿP¯¡+‡ê‡^6uXܰ_=hÙY/‡O¾lxÂð&þM_þ 2ñ‰&;˜Ú‡šía3o”Þ§k´šò …&…Ã!tJ¶2C¦SÖp:g W² áŠ÷‡utµJ/ËPÊSY<›'O´™ì´°¦A¤=ïÁÒ‹ØSdó*Œ£ZME¹B“ÌEÒx5oá tŠ‚¬£÷¦±g¢(šN‘“7ô­Qt޼‘M¶4>ž"(1Iq& ´‰*i²2já«Mví0¼aŠ=sš‹=DXB–UÈd:™$ƒœK[^®Ð¦p4èøjQ…uZlO§Z6XÍ] ñ$ü Og¢/lFˆéd¡U9ó’ä°ÏMq&Gp4A}‹³ä!Lã'I}„“£j¸ü<ŒN¶ÌjäÀÙzì0(¥˜ô†*#9¹÷³CúF·Ï‘Q¹7yÿîò](†Á(Jl=}ºÚ²‰T. 4ô£y—'nIZ´„%›H3A|DvñÌä,iä·ñ½¥áÇVÔ!5»à?Žç›7kµsŸ«jP9£“ë²ùÙ vUèÂø9ddY÷ÿ ö: ÇÓ»á+ÔœävjQ©²Â­L£3r’Æ—bÉí¥.“•4¦+³S¸÷¨ql†@<¦ÀòÛ¼%¯à˜¤à2âɽj½¦¼R^š*X·G'i<¯Åã`­¨U°ny…Ùº=ë ¬ þX†ƒ1¥Ò’WVà8[½A<8¶7Þ&Zhõ¸ ØlœÂ¥ÒÝè¾kâûp3¤ð ü*Òu&úØàÑË:mŸXoÝÔÅoñ'ðV4B@c†LðG}gb” ÂÛzóÛÉœ/Ÿ¨¥aþUdqN‰­þBHšìÉÒT_¡_vÜ~öÕ†Ó“sWïoDg™§¾_Žd±ÁºØyÚ„#¤SÄ÷.†- Zÿ)‡=q½ÒÀK| 5($7îÞ³ma—­žËšÞýI€8 '°Hæc 0ñ/£édMvyq*{Ôb|¿má{¾çfȈgYÉÉe$á¼™±Åۙדìý?GЇ{â~xÀ»@Pÿî_ÐzþÈmtø÷“©ÃFLžêá1ùñ»·ÏÿÌ™ßße‚w&p¨­«u<Õ|ó&èë,‘ñ)ü[é«Îc9½æîÒ£³{$+p’vÜïÍGÐëÁ•c_Ÿb%ã_‹»]$‘-§q,¼“¾}4ÕƒŒôœ6ÕËsYõÑã±Ý.7Áœ›Í×`®=ÜæB¨®ÖkËÊåZ‚Ç>V 7ÓxNÂc Iô¹¡ó%›G¼Gl óqG [A£¶‚f¨i?›à¶ÉžÏwâ b+¸½&3r›h|™/ ¡S-[¨æÎ ˆ²Åcµ¡Q›h¸ÜYØåÉîK2ý.^-]{rñ¾h>Z¼3,4|]ì<ÄLc7è‡ûÁ‡×¯í;u†Ý¿¯¬éC¶V®ÈÉW¨Ø…«vú#÷ô|}¸;bèõ7`ÿâñŠ b¶0­TQŽ˜ ž„‘ƒ¸ZÙEín…MðH ;r@ž@ê xHŽ €Y8Bu¹"\«W Bˆu×f®õ‚búÔ‚ 1ýüzq’ÇÐkÌKÜßgUœß ü.Hß=ðõ;ÍÇkÄôû¯_Óñ/ z`¶ Fšìø*•¢¶üÚô#[ŸNkE0Ðs,î‹~ó·—Ðûtƒ>[Ÿ­ÊÍÏQr›¶ÏN D˜BÓneüʘŒ¯î\zƒÞ ŽàªqLWpDÀM=ZàR‹9N ®Qè22TæÀCðWj1<æ7ˆ.ãNi7ñÍ!rOŠ “agì½üƒ8ÃDðÁHñ*Àü€iìÆb,Âôƒ;¸ÿ@~1œ5#“àýÞ¶؈¥Áé-™r(wA C?ìÄâX[¬š:¥|" ŸGéǶš6î[ŒV0³ÅC'Œýgo^3ÁïÄ $(ßðëá¹TòøFhÈá™n¸ß¨q¸îõϱÐ÷ÑÅC7Û8\5,8 0ËÌP˜…aˆKÚ¡Í‹NÕÈË…¸cëôÿÅN$|L¤kT:Yº§g‰ƒ¾¿·h4Y} ÌÛ-p=#Äb¦92ïãç£ÌW–ȼEÈEk¨ÈXÐvÒ’Æ·ÐãäÞ“n{PmÖ¾²8¿T°º¾ŒX=µ‚ÛY—¨ßV¡àا0‚Ï—&øáô>|á{Ÿ›kWží†ÆŒÁ ÿ8 îž;|ó‡¥bÿå!þ,þ€¶¢HL·£ãÕÇ)?ŒL ™dÄy±äu7Ì0š'}ÝA`4Ëçã1¾F? VÇbSz3ÿØähŽ=Æp™8•Uÿ R“" z€3 Æ÷áèþ zûI€2'W…”®d zuqa¡š=Qö•áªGUyŸÉ‰“4äU¹µüípm™\Ÿ•—›Ÿ—ÇmOJUîF2¤(Š×1’dCLBñ·Ý(½ ZÅœáŸKÿß!m£k ç,Ià:=°V¡’ä(¾7É16ί %'/[3ßmÕ&Ó.Œ¶°çÉÓéçVþýæÕç/²Ýźàx~9å¢:Gˆýׄ.`ñ@›Š]J¿¼~ãÇ/Á V œD¢¬Ûñåì^º¢Ûñ¬MÑÓZÀ›ë¾IDÞ³Åiº¬²ŠRâ5 ;×bZœb)‡jØ{0­3qèÿ®î­ŠÌR£Ôb².ÃÞµÊxBщ˜³jb¸kå$r_˜¨ÀdwÂA íá…Lå{‹šmØ{”ï·hRè=%ÑØ ž‹nX»¶ƒL4Lïì-zLà(ÔZàÚB*ÀEx09K:aÌ—¶5‘šzž(Ž–ü·uΖýÉÉ)Ãh¼ 6êØYtŠ–tZ×r²®µ‡Æ¾ØSÄ÷mµÔ¹Nã ëˆ×øð*“Ý[K%”KìÕ`n(U*K“¶JQ¨Ò‰¹ªˆ0yIŸ7oÂx¿¯_°ð²ãõRJ6e´dvønˆÀþùsp`»W âõ”#d…Ú´¬ÎX ¤ÿž8uõÚ=m«Xp•ÚÌt¥"™Å^â» Úão æçoŸ˜ë÷¾ñ†Hð¶çž×ÔØ¸¦‡õœgi?|oßÒäó… öPët‰†Þ(:l. +O')»øèC:Ey4âEÍ –ÊìÒ3’åf8Ôp‡é?Ô>›ÁWHº;¥ï½Žn]|¯wýÞ”ËhXÙ>²vÁ—*Ç厢{ïþ“4>Š‹DPtßZ3ÿÿòòû€rl‡Åx ÙH?˜Œ?âé<R˜ ãEZò°»ß„í¬|xŠÆƒ±N¢6sÜV˜Mboõ¬†&óÇ‹HLfÓ’ÖòÅÕº šÄãH‹GŠlXÅÅ:"Û¶y·f6oz vä‡P8|êk-Œðóî¶Uµ¹©hèŠý(‚‰éRÓ‘úcn ‡£6qøv‡M¿å”ð¼éÊ¡½2Ço‹à$¥ø®í€Ï£à}eí™%·í^½™M<½}ïŽv¤D…2nA?)»ÛaŠEÎu7h‰¬Óh“ê^¿ %ísO]ºë}Þ¼…ÐôÜÏwùö¹«9ðû›ô§‡S‡yMó9bú“·o¿yÜÄüÚg&»Jµ‡~N×h`x°ïô ú7ÒZ›®ãp+Z¾£—vÆa;þCQ !œÎ—ñq¢§6Dïaͯµ‚ÙaÏ5{þr9R*Sãæâ›ñÈyv ;ìÐÉuˆ)×é ê‚¢ün ˆy‹~C¿úåkè[TRX„ÔŒ![—Åšçú›=“¹²äÙY¹(¿8‡û8è)42Üc†ùá¾y9(å2Yºlƒ^£ù¬š½vŸCOôúyû¿ýÁ!¡I®“#ÁÜfb9ÂH±ë Ua^a.r•ËYYÚlm§.×poì ’ä¡\¡2hKÔ-ûìŸ`{¬8_ƒ\ z­¡4W“WÊM’L*KJK‘Á•ìVž§,ÈÏa±ë°„Mu² ev²ùiï3¤P]Ï´f<Ð5–vR ‹à™âî9ÌÄBù[Q.”¿}Í$Hä{Hçââ‚<²Å×ôò̪rºÖÀÂp¨4ý';ø¿*­ïI­NLHMMÌE9…J¶S˜Lt.r­@¥ÚÂZ&–îHHŒÝ›x« íÛsà@üž®k·|>Ùi7Š;‡[× àŠþˆx¤—ïÒÉkk Òé‹ë Ôå 'ó]½¼Úö®°µ:;8 H7ÇÄFl9ó9§§›hjŠ>a^uI fú†¤-x© ¡M¦«`WaµÒ\eH¥,HMû]>¢+É]M¡T¢Â}e×}ë^ïÒÙx€h²U5=¡Sñ.‘Bœ©Ï6”ktÕ…l$Š~¢ÓÌw ÈL‚K­®šî?²6ê|èí0@*Ìö­…‚©ùÝ¿÷‘k‹ŠQ £—kåÙʼ ‹ÿþŸE¹Ê‚ ¹>[¯/))×Î qàjRgÃŽ 2¥˜~&nv–”ÁQþžTâ?E<{Ь‰<˜¼?í`öuÙW²ªœ½²}é•;Iy&)[´0l²ïüÆÖ\V²°:?G“á&ô¥¹éDª)ˆTÓÖ°Uâý;Î'´å1¸—XRFêšJá,,©KÞ•y =B'’ÇÔÅã´s+’J“Êv—eÔ£ýÌå‹m·¯]ŠÞBªÏaD«ÌŸ7j¸«â®¯XÉìÖ ú05ÓN&6*Ì}3´Ó›íÈa„*Oƒ_¤0aPØÛÏžÀz’Šé¦ÀìãYüÝk)º^së`Û¾†£5-¨5¤ì‰:´Îè2ãÅha²̺Ĩ¨¤pB’Ûjãv´¥ÜB×…ØÍlßFØhâ¦ïšN6}ßh)]°|¹¿ÿ…e·n^¼xóNÈùy\¤Ã™¦ˆÕë7o\·n³©íôçM­îÇ“Îïxã’y xºÑ<0lm„©íTÓçmpª´¼¤¤é½B—›[PËvöÅ}3rsTHîJ˜D[¤.*ÒXÄ´%Åß}ž—â à´Ü¥0„ ¡X[ŒÔ,¸´ˆ:Ë1gó½ê®MKí-ÓLbr|m:DØ?ìôÈ:j ×R÷YÍ©Šv×#Ö»‹hyhÚ†¤Hl~.ÛÚåõh/s¢©ñÄ^£"¹Ž=\MX¡i +é幪ü<›­J¨Ø]š€\ç/ ØQ’uÒ—óKZ¹’Tµ’3ãïø=kk¬k=È&4J6oOÜŠ˜ •í7/·~~®¬«÷‹#hŠ$p-g“´X|éô——ŸÏH6äß§öàÕA/&=¸,64’MŽ—Å †¢ÞPª©P³•W¯·ÞBÌý¯û…%ÇŽËá,/ K‚ùÁh£ª M’’­BËbxémH,*.T£b׊Œ²ôL¹*-›%²A!‚×N_Cba×3YYš,[•¦`1Kž /&,{8YŠ¢â¶ç%ç+sPCòRåùÖ¶+,øŠQã¾úãE•E: *GÙ¥é…[Ôñõè8Cž]Yß’ž­ÈÌÐäóØ#¹{w¢(“w6’ŽîÚŸµmÈד*«óSHÏ-Ê)ÎC®*¤ÊÍ#VvÉÏ+ )KTÚàñjÌã5ŠÜœ¤pEy…yEäŸK‘Š$DX£P]̘#šM޼·)ÙÜ¢ÄðXZHÎWˆÉ·W÷7µ_ôý¬¯!~M0ÝÁä0˜þi72õl’¤HÉ¡­0(5™E¿ô¿=‹¼ 3Žañ§b4·&èøŠÆU'â¯"¦ãÁ‘ÃBQ ¬,.1m“lZ¶ãšbN¦^BW˜(—ë¯^Þ:³Ú /ô B“™¹—Céõ…Eµl‹ƒ,OV…˜%›Ž½zéÄ#Ϋ'Í0£‘\íÑu$.‚ˆ0]k9e‡ã=´Œ¨¦’’âÒ2¶¥®ÕÐL,{9püÐù³½–„ìk åTšœ"‚—¬ìl¹¬"ýÈnîì¶³iç^z}ûpŸÇÃ×G*v¯êÖ~DøáD³B!"ª\VU®ÓTêXø¢WNóþð¨B«©Ô²À‚9xqÓïƒÌ¶êUék›Êqð¢a°õz4m9d»Éî¿>Š]p‡i¾± éÑêkZÏÜÔö‘1_—£ÍQ” Æ ÑŒ[«6ìZ·’óY´u ƒç<Å}¡ç›ßõnÖ³qsC6yobÕ!ÒúVCMeÕ‘ãÍuDÞß¹´`ú¤¥ó}ýWzÇåêP¡j¯YSŽý•*+rЉÄ$bRžS˜§Vp@t@©L“£C®z½N¯Î/ÉÓ˜mÔl.pnä[¤xb§¾ ·@…ò\Íòµ¤¤²„…OùjÑNf–8É’ÙkÙcÐ*î²Ý]!™"iÞÀþMRJ÷õ›.Õš¹-4Íb¿à{ÁM©äáÝÆC-×½˜ú`Ô¨©³Æ ÿ:’•¼˜µyå‚A#ŸMÿùçgß¼¹¹ùÜ죬T‘ñ®.Ÿ´ÒÇ'è‹›__»pŸÃ’O¯/õñ4Í{áµû×/=1“Ç9Kñ<¯ÈÎÏC*×,¢ƒ´%Åz žž8P#/ÉÓ)M €’¼R•¦ ¯ÛH:íѧ›ìù`蔦¥!‘±ëC}¦‡ÎBÉ(E-/Q”俦¥O;qãF}Û9®ãËcß!ø€ÙS /î9r様þpò˻皛XÕiüú¬”ô´í[6íŽD̼¥7¿ùîÒŽ[íë'ïçÔÙ¨ 4ÁügÀY;ž÷‘þÇaõïfûÍ6\þZ¹Êṿ#2òÐËýƒÑ-жIx1“Á¼?*vºVgQì³aÿ_¾}L E>WŠöî?¢6kÔ¨‚)ËÖ¦/_Ì i)rWü–Ü´…e¢ ­ª*ÿx~ý.)¤¬à–u*…òLY’\Än/Nø 5)™²ê¶í;Zo¨E&áÀw¡DÊOÂ%ù%ùÅȵiÔE¥EÅ.EêÂb’£JsKå%ø7Xí䇾D]BD*.(Î'ÿ]òJ•j‚\”›Ÿ“ǼÿLK] ~_hõê÷mÚ:÷¤æÚHHùÿ™D¸;›T8„Ú…MïZìx#\$µ‡¶¢Bhƒt“2T•‘¡U9>³©J‚­|ߤZ~R-8ÖÖÖÒ'zšzèÝÛÔ»Eý2áû endstream endobj 98 0 obj 7792 endobj 99 0 obj <>stream xœ}WyTÇ·î¦mEPÔ"Š€ âÅ% "¢¢ì ›Š22,ÂŒ‚Æ(¸‚¢a_YD""ꀌ+®`Ôgbx&&äir{RøË«f0Ëyç¼æLuUß¾Ëw¿û•€ÒÑ¢‘³L/S„o žî-qÝ(ߺMÆïLãÆ ¸ñZÜíxõÇ\µR8rÉûYÔF#uŠÇ|0ä>1€–Q°o4¥-$*X£Œ  S˜Zx{øZN:íï'¶vvv¦•wLeqá¡Q¦æäO¼L)‹R,0]JNËåá›LCåʘ°8ÓàYÿšO°\aê.‰‰Ž7µXji:sÆ ÛéägæšðÈÛâL=ƒ£âL]Lù0LW(‚‰•mP5ÓÅ>jÓêè5Kcd®Žn±¡NqaŠpOçm^+â½åÁ«"-,M§[Ṵ̂9köœOçî™gGQÓ©‰”+åHYSnÔ2ʆšD¹SNÔ j2eKyRÎÔÊ‹ZAÍ¢¼©•ÔlÊ’ò¡æPV”/õ)µšr ¦Qk¨áÔJ@éS£¨Ñ”eH‰¨1KQc)cJI‰)j©¥CL®£ª&oÁ9-VËI«^[_iÿ¡³L§Hç‰0BX@Ï¢S‡M¶}X9cÏ$1_3?6Üj¸røá#ÒG|еÓUê&éÑ­Ômi3Òwd“Þ½ýzúÛõkõ_JMÃi}p½…ŽUG²ØŠÆ6"…Ü^ú$ñ¯´¾z3*Crf9S SØ|Ê÷¸_™iï³Ê[’ÿŒS)@úA[ðÌ´¹ïá5»¿43å Æí òÓ²ú èhhOàÚ…8R“.~˘ûó09EÂþèé30ƒÞ[[î; Ù®w[à"Ÿ‚æ0XÿÅ"0ê¿{¯Kš;«~A/=÷ž Ù†uØëgƒý“}â‚¥rÏ ¹/q6¤éˆ½ÿ^ª1?³ˆ›M>2»Ðð)˜%r–F$wïLi/|LÑ\¶‰ç#» ß%þŽQ¶ÈŽ5aÃÛvïn©ÊT$›9C¶õÌúeØÐSŸú„7ݽ‡ õ­t°Þ|èêvi,8†½¸cÂØàƒÕæ Ák P<@0†;c¿ƒ%7&k«OqñìþŠÌ£è( kâ lƒG™ú˜Ï—8ÎZÇ!Ì2xö;KX +Á¤Ÿ—`3:ÊÆÑw*b¬üZß?+ÕtWÚüàFMR¡s‰Eá¹ ) q$ì_A'ªÐKãC¯ƒ›<°Li<çC kPSäÚ}‰aI±IEw@äÜó‰³g¤[´$ðuNXDŸãÖt €ÖKéÄ&ôÁR».R»=|íîB<{¬éP#ªG¶7G׆6¹—: Eh<ЕsAÚ~9 @¯¿ ´Û$XB'ÈÖÇ ÆCV×–Žì¯BZ‹cèìª#ù¹y¥U  ¨UnÏ>œ”µ EÀ|Lâo$‰ ÁR›«Q+Y<™ØY,² ĆcðRÐÆp‚1?6ýúRòäuè#Á€³éox•$a¹¶k-Œ™µÁUê·Ì%ì3Ę»ÜÁ“ŠWç¯K/Ýþ¦öbø"“ŽŽzùÖc#¨§a2z~¶»ÿÞÛs0jÐæÒEñ ®§‹úÙ~.NãèšÖÏX¸uR¸›tÃÿèÙ¼)»IøÑÄ÷†Xƒ3Ë‚+ýu”ªÚ[Ûk^ Ç芢yãéЊGƒ¯!ÉŠ~Ʊ숂5ÝZµ+$)^¾Ã‡˜æÄùy\‡†¼g—Ï=Câ('#ß)˜h¼;ó ´­B«‚J~3&8ÁQß¿lC*å`n̓å1WH )ç Eß݈ÙxÊmÜb´Dæ³ÂÝq3ÖBV Öz° ÿFCÁ×]’É+ÝÖ9 ±è±ÿæâJi&ÿ$:=%cÚdå²s µÊó»o ˜@pÿ$Œ]®ÍXô>5‰½Z`õ›ÍôøêŒzÂgÒA2„ $Kv$Kpn±p_C±Ýt Ø á?ƒ«ì¿2ó7å‚,aX˜Â„±¢>®—Óbäò;Ê|¿¸ .tŽ—mlE{ ?äy.äUÔÓXcQgBnšÂ$bƒbM˜üÈñDIò±=G3J<Þ—”±¥¡u•çcÏ*šSo >ꌹrv^™tjÅÚÉ9¨P\US¤’ˆúÚª"ödìMOÓÄ1HŒƒ¤ËSÄ ¿RïU]éBâæòžÔãÿ/©?uWÍvôŒvñ‘t@©P3[ŠHV<ÀÀ; 6x8X¸‚åXÑw$G,,¡E}íÊÍ)ûv¦…JEý‰NŸoÞj‚R²R%20‹>trgÎ9è¸ÊÌÉÊaD}%0ê`M™ ªßzÒ7OU^žWZƈ¾;ÛPÖy•<Üq6ædDÕ¦ãîˆÐû+Õ¯¸™lxa!}•ì)H:¾óÈv”ˆ¢SäÊ„­Qò?FxÉ9÷ ¸™\9»ˆ> “y¦•ãÉB':ÁZx‡^†_ìKNOF;Å^ òîêÒýG*$…o“ÓwȈI9'f;'½ÍÂ'´>œD6,"à~8D`CøæYœ_Ç`~Iúkˆ …BXb ÌÇŠºÕÁÜöPEÖqTÈeÝ…gY-Žóóxø‡-AŽhÞ¥õ ÷6ÁXÔ÷ZY™hâì¼Þl†ouûÁÌìÌl 6§÷%d¤¢däST»åâú;ñ?"B'}5·›Ov£oÐc¯+“ ± ØXÔ=½ æËz“®« ¯_4Gy‘‘–‘&ÑÔ¯“ÔïAÄxÎ.ÃÏù„j&ÑTÚ— ñ9ºÊÿžVô]xþ·:xEpÄÕÁ0®ŽÏèr|oû|_ß•Hì·µ¼å8ÉY‰ô.ôð[ FîMLW"¥xA‡ï«­'»Z%Iˇ,õðäs”ëg!LÓH½šÆÒ˜èì'>çƒÐÕù‡ÐÑ4ÚÍü´ònXÐxí€4’“ò² ͸~rF¦i«§ Òmý6Í@âEëªT'²ŽïÿRúžKã5ˆú|ËÞ-h›3?ÚÃÔWÏÊ/^—´7U]Íÿ–1þËø¦Ö{HÓóšénx‰Œ ½0‘ï¥ylRÞž\t‚ÀüpÖ‘NPÈÏ:Ž¡Je¢ ¢ÜÿØ2„mÑ|hlÄÀX:¯ítab.OÞ–*MØ…ÐÒT÷4ã€Ý›“åÛ|d[Ö W$Ë—UÆa_v&õ«q¨¥äZë¾$ôúœ%¢Ô¬=w1DÛáQÉŠšì”AËÇÄæU7×Uß<*½täËý9Y‡3dŠfíÏ3#ïŸ&&`;Õ0xÃâ2: B™¤Hs¡rl>„Üä"õRòÁõ:ø Þ¢áƒZ®‘Åú\èz Z/XìÔa,"›•^xD° /Aæ u1‘5Ì] Û&i|]¨ŸQñHI QœÍ§¶¬]­À&1Ó¤Ñá!nˆ Ь¾üS ÄV«¤¥õ•5ü8æû$¬ïwâÈ.™5Hä«©j?¡5½i®Ý¬¬ùsD\ì *ŠSÂD"dø[Š.««,݇ß^©«¬ªo¼TruÖú¬ a›ÉÓñ­0¡sjRÝALkU¤_PB@x¸T¾e‹ÓŽX:Ïu6f¸xM—èCÔCŽ·Hàa`6,ÆŠÞCÜe[PvFÉFÄÕ¦Em4qôruöˆ¨hÉËÈâñô^EzJ$óþá*0zû´úÆeIÃ¥²{¨ µ*ÎúŸbDï{Zn>5ùÑ¡ ‹>]ã$Ù¼I¹!i,}öPAN^AU5ÑpLSUÔÆ…n¾¶Ò Àx`¸Ù„cC9kO÷qÎBlGã÷N±x„ˆ¬plË'í Ø »iì ‹]hÈ‚ö¡Z’i¨óžpÙ×ð ðÉS³j?v@Dcó© éâ3uˆé¨Úânîºx¾OXÙÙƒ™G2³¥DuîM ³f'cѳFö¿:ßÓ!il/íFwÐr¾ƒ²y ¤,„Ðò1‡Ù Ó[ôÏ 0"ÎL[­Ã5±DZd£ƒÌ§,4ãýÉJ‰Ïì@ì‚°>ƒGG©ŒM·Ëï õ]ë»k’ë¿}¶ˆð Œ^ÔKúVôž‡É /ÇÔMçwWkN\¼*ݰkKJtœÏ†H¢YDï#Š«ö£™¥°’ó$Ò4ÔÃjÕ¢RÑOþò3ªþŽR E WÇ+‘€„#`².IQÿ ¡&Ã( endstream endobj 100 0 obj 3965 endobj 101 0 obj <>stream xœ]Wy\Y¶®©”l ±A“¸€à.":*B\qÙ×(DD‚K n-—ÖVQAv\@dUÁ°Ý°Ÿ£­vÛ¸œÊ»tÿæVb÷¼y„¤nÝ{î9ßùÎwJw%†yDÄ$E$*ÂBì]”1áüÒ$ÎRÀÂÖAX©îT«„£©%¹o ‘2Ð=5ÊXß„35†Êa2œÒ’Ó³)ãU ЍèDÙDßklìì&ýgeª“““,Tõç™kÄfETœÌŠüHŠˆQÆÇFÄ%:Ë‘Ý11Š0YTŒ*>z³,$<<"œ?¶:$&b£L®ˆQÄÇ+“dÙȦM™2Õžü™æ£ˆ ݲY¶2$n³l‰Œ÷ÿ¿V(Šš¾Dæí¢ _ç³(>b©ë2·„(ùæè•[VyúƄĆN´‘Ù;Lž2uÚô³g;Q”=åG-¥\)jµŒr£æP“©ñÔrJNM¡&PS)+j%åAYS«(Oj:5‘ò¥S3(j55“²¥fQÞ” åC-¢†Rz”€2¢†QÃ)cÊ„S#(–2£Ì©‘”5ƒ$€Ò¥ÆR êõ‡`Žà¼†¬²Èá:±:?è:ë¦ëÞÑ: ë…ÿ¤cèK"WÑEÑcÎ|Ç´ ¥†Ž:whÚн©z™zõmôƒôCõ“ôèŒ7ð28e8É0Êð•‘¹Ñéa£¡ÌˆÆÜÓ|Á°Õu,‹måöƒ1þ@q­X9 ž/à$\‹uGc}n´;Ð0xS˜.úV´6"Üo×îŒïU’±¢‚o³ÒsÑyTw¤øØ…œÜ¢¢Û ›ó—pö —oÒ2™™¸±ƒX¬"½"qïíÆºîö³¼$øYùCúžÝXäâ¿4L"n|,"ž$‚~¡ d:\¼fSÁ|úØ1x¦ý¼Ëߌƒ`öú)°ÙRlA«Ü”ñ¾ˆq ¾ý©åXSn¹4çlYN ºŽNÅç¸2Fê¨@íš/¸¶œ| ×É‚Ù`§li`¹Ná`'تcq’ìÅæ\¯'þKÞ©# j°œ|œÀؤl¡Æa#o&þÔg OéçèZNCUCÕé[èº~Ý©éÞÅÓu躜ÐZ\µ6gñû1 ¶Ø˜GZÌõTÅù®ŽŒ“K±#uÁº_½ÁÛ'bƒ‹»“\ÀŸœ'!(@w‘ÅNVX€Ý°¼S0f€‚×Ä/xž4 ÙÞËò1VkÜÌ_{w` åî#©ÆÐPûn2 …ù&gˆµ]|F¶s6¦ #Ëpݶ¾Äƒ¸ÈÚàáØÑ¥pa婸yMÕÅË.t©ä|+ƒõ8Cö‡Kòñ6þînnþ=¿þÖt÷®ÔH=Y‹+ÄV‡s„"¶qAB<—ÆRüÛÀC!v¦aß Y+[¦æ@7ñ"権wÛÁ=4®ãöºñ¨s<î‰`‚²Qý7.‰Ý×±³&¦v僩µX@ü Å#°5Þ…w‚[¦y‚á'pΔb+:ÍaÙj²…q Óaæ?ƒÞ•zUôiiÎ֣əá f3ä'JxzãOTÁ§¾ì÷t²rž®XÚ¤ìF ˜þ F0ûÑ–GQ—¥âÞ–(ŠÅ–îÈ?N±”=¥?¹ÜÍ¿»¿¿©‡ÀBüƾa_\$±Yè{tµí¾¼³:êýŒ›ï'OºØ/|;ìÁð}Ïç2)–Ð)Áá kQÚ˜»õìÎü}ÅZ˜ïÞ°™}çko‘b+OÉWfo=œt0’p:/Fpß|j`V<˜j;9*¼=»EN@ä,¨ß)ïpïÛð}D}ç:º;º.|FèÈ‹ïzõÌ)‹˜ <‚…a­ñ4l3Ç `Cç~â”ý“~0”j €Û Æ‚^’µëä¢^S¨¦AÝ-m¾y£»ìWBº1¿.ïñ»)/Áº$:òÞv°Ÿ%Ç,E ¼6ÏÄ3VÎÅB-g`7)'%±øˆX<Ædd³°”‹ï`"XÍ~‚'i*cuÉ^™èIUô‚…‘ÑÎÿ)IÁüyt±ðW2pâ Áx°˜([ù–’jI$i$Hµ’d&®úKŽjDâž¾ÚÚ¾ÇÒ¿Í“ÀXÑîŒ}h?òB+Cb]qÕg"F€ã^}"\y­á Q$Ø #ÍÄO¹IÄÊl‘ø—Û!§½-ñ;lŒñÜ—XFvTçݼ"u-ö t[]X»K‚'Ð!9q•±õÑí)Ï«ßÞ±”عJlàØbï^q±¶YëáâÐõþÃź¥1%u¢óg"ç=ç=²ö‰/S«R¼fO0Ĥ‰x¸,±1j?÷7„=ôô<¤eô,9Ö‰:“—$ÍKÎM»’ÎkÌÅoÎïÊÚµÅbCøÖÅQqG²T’íÇ÷ß{†l§a½ÿñ†Ùè!ºžSS^S^|µ¢»Ñ®Ø­Å<<+õ(*dΖç7KÄý]èÜæ ?æ¯@GÐE¼YMô«‹tâ)ßn56tœ-Þµ%G’¥:´E3ø™¦ .USdYLià‰@$Gë”áËÈ×J Vù% r`6fbR Ž…tçéM>{QjúNiz2R¡]ÈýЖ¼hfž¢³ÓN òý°qÿ1‡š¾òÚ›è_èÆÖÖèÊ 9Þˆ´Ôrlš.±ï)ªG'÷ŸÜΈ?Üž™ °@ѩʔíÛvlÝŒ¾Vœ¬2ˆ3›`„IØš‰“ÕþÜ(7¼çÐûCS¶lbÄAQ«6-·Ä†3>‡§ö¾ñ•†íq礪ã)Ù Œ¡U¤½Ã®$)‘xžŒ·âM0Ï„uïïœï»&'Ÿ´¾¢àçñFðLÛJÙy0hö¹Cþ'=«1ä§²’ƒGK$ïE;ìNOELdÚñ&)\"]ÊHöçß&ÌéâO‡i¤§=­ŽÕ%FAˆÊNN"R‚Fm0N‡ûÈbQ×ö†¨¿¶9gˆ<àEVNx,^_•Ò[K®R¢e#}VFN™°¢þñ> ÜL<åË,ð!¼^:/I ‹–·cËR)¦ e1¨uäÕæó½ïZÃ~/Ѧ´ƒ xXÛ êxñ:ÏYü\Ó ìp)®…R~:!oŸ‹4mLߎG#Öš¡"™«6åªH¯EâF¯À÷¥±åøÄh ÂúwÜAt¿ùT{Dœì%Òrò Á¾á/c¹~¤|ž6 öó%ÄÆ÷¦¾Î_§#íÀ+m¾F­Êøãäí`šhz`ˆ½}`ÝK —F:Xš9.ojõïÝô½D½¥­MÍ …7PÃ¥‰þ2¯Mo^Æ„¶t…S ¸é 49M¢[ £ùè rÙ„¢='ÐiZE0dÁ5l…­æ{cã4 XÒG®ìALÛ)U”*mÛöÝÒäT„ä»V¦šËTkV![QO˜¸±ìRr“e;ºZÚ܈“‘ò`jö6&ÑxIJâÆ9a®ëãJjê óodIš2«Žþ.çèH£ÿ5ükŽk'N-Ð~ÌÄÍíÿg–{z§²¦ýÒ™ø` Ô¬ü×3g"úäu Sîþ›W†H6µøžñ@Þ(,)`#nîýÿáäæ_ÃÉͯbA”±yuÅ¥„nK`ßÃ0p¼¿åAÔid«g…'ò@þ _²D ‡/³=ÍnÖ6~nr×õÝï5]^¨×‘Ñ$ˆ‡Z„úc Âö;4a gæ7z „*@5«  ?C0óç)#¼ž,l {þ Áä™±¦?ãÐ Òã°5ÂV LÁü©2rÂÀR-ðŽÅ%ØJ4”+l+B&l¥eÙŽõ"rC¸:Å¡¿Ç»hUÇ5<0àŽ“¡ÓÂyš”ª£ÙùhñÆàe¡«’§ <aãÜiç|ë=v&§þlq Ñ7®•¤<úÍâ\{·ƒÜAÆÄu¢-Ž.ÁØ1üxÎÏ* Éàaüˆž Æ,n¥OVæ|Ã^U¶¡ŸmÿÛ`ë™3°í^´;c—,Šè«.Þ¾Uì:/!OÁ:là¹d6c¸$m)@ü[XOj>ƒ Õt1ˆ…»l3ÊL/Þˈß\Úy6t…wP gTõýý<ŠÎÀcßLÒáÀî—äYT›ó©¸ÿNí©ÆN Лþžã®\&QFïôGK˜oÀT”Q}0ïÈÉì²ò¼ZÄ\+W,s š-ý†ÖࣀáHÿ‚hÿ)er؉ÆYƒr!Öƒ*<•$iêÞ ‹½ið†k_sÙ0äiAId9H FqãÙ=ÓŠT¥‘-‹Ïþ MG‹Âƒ]Cä)sf6:1¿rE­çƒðç$‚1?åø-O ’|ˆÐá³Ù·kª¯¡FT¬:ÈØà•lgÝÚyóVy{µ>|ÜÐÖ)Õ&°ˆW¢^Çî'Úö™˜…Y¥·ßø‡dlÇ·žlH¼OŠy.1_Þñ|â…p†ÔY’oügÏ™²ÅÙÇ?ƈ9s*·þ¢èØ=ÅfØÜnÖu?|[!8)|-°Å€ØÀÄ7`ñ\ÑæÔ Á°…ÿr«>b•oP¸wÐ…«mõÕÒ¸‚ôÔ,t twn¼{¯ábÁfâŽ90GøuFB4iIÕàÄÄ‚ŸœÁ½F=Õu÷ª: ¿ ¤ôml |äV3‹p~¬í<[½°“ÊŠc9…ÒìÌ£™E¥ ¶Œð˜ë§8U÷­ä·œí®[={^Ð/¯°–§}5WnI‹¸ª|˜—ï_DƒžŒÑ½LsÌÀ¢þ ¥† endstream endobj 102 0 obj 3791 endobj 103 0 obj <>stream xœuW XWמf•t@A“* ,²¸ ‚Šì ¢ (¸ ² ÈV°n]äjíâBÕ ¢"ÚªˆkY÷¥(PÑ0¨Ú¨´Új¿3é ÿ÷ß„~íßïyþ'y2“™¹wÎ}Ï{Þ÷\e4„‰DÆA‰iy‰¹)ñqúŽ‚µH3D+^‡3þ˜ª-¤ÇRŠý?š"121ªÃKÌ…¨‘pm8Œ Ä"QÁö½ó2³ ³S’’såö‘á‹&OvüûŠ«§§§|UáîÈýsR’2äÉI^bZfVzbF®—|y:--%^ž”V˜•œ#KHHLЋŠKKL•¤¤¥deeæÉíç9ÈݦLqu"?nóSÒW­Ë‘GÄeäÈòðĤuiqÙÿ¸HQT°bnaFü’Pߢ̄èùó²ÃüÖ®^àŸ´0 '9<07%"hÝšEÁy©‘ùiqQ勞Zlï wt²qv™âêæî1uÚôfxÆÎô²£('Ê†Š¡Â(?Ê“r¦l©¥ÔÊŸšI¹Pã©…T5…š@…S”+5‘Š ‚(7ÊŽZDSî”=I…P”E½KM¥&Q‹)5ZB…R¾ÔtÊ‘Š¦æSó¨ÔPʘQ¦”%¦†S#¨‘”9%¡Þ¡xÊ‚²¤FQ…ÔhÊŠšCYSc¨d*„$2¢¤ä5û¨.‘(N´{ˆÝÅφ¼ËÄ!â£aFkŒnÐb:†ÞI+ãË´³ÞlÛÌ™pïq¯†Ú Ú>ôŒ=³?5®2¾c<0Ì{XÖ°Md&¾&ù&'L9ÓXÓ2Ó3Æl¨™“Ùl³µf›Ìö›ý:œ^0\;ÂiÄÇ#´#'Œ Ùlîejžb¾Ïü[8n&4!•–-uiÄ0W›Î;¤ Ñ*¬cÌ´.¨b””r‘™ü#œI¿aðNm:M\6NÿÂt@& _(y\Ì€ <¥Í„q†Jû3J¨àñ#¶i7üu•©3Ö¯.(IF£ßG%Û wpsØý›ötU¡ê÷9°÷à¾sP¢5¥«BXØž\nÞÖáó>é¶”œm#J¾ÑU »`=º>¨/®MyæuÙ„5щ”Ì<çÇq`&Ý-ÀT’É X–„¢ÐŠC™òm9Vz™ÛÞÂÞóîÄ©îNÝŠ¶–n%%t`ŸÓa/Á~´¶Ç‰Ø;ã\ä«ÕU7e÷ë€F0Œƒ÷± œð©§¸ýŠM°ûÙƒ3v±%JAoÈÁY¦Ï’J¸£õj„1q¯œ`@4H!ò°Hq¨ Ÿ`4Ö¼pæ°0þ‡Ix>VÌvÀãeÿÒ?éÖ!l&ˆJ.j‹æú³’Ž« ŽXc©+¦‰`ø<³i{ã‘ëµ2|œÅµ^|/!›96ù‹i¦·U?ÉÌà8‘¯À°n©»Å°›އŠ9YþÕéÏ?A¥¤­lÁŽ ÛŠç³S+ýÏGËjc¯­½Šî¡oÖÝã²Xä·ie^ƺ´•…KP"JÙ•ûUÁÞÍ_~xœ›Æì´ïXÑÝ?|âÜ™ú½÷ çÈä!db\Ê÷5ÍÆ–xdäl÷)‹ rMÙûßLÓ®-å:Z8ñ;šðÁœñjòùõÜÚwò¦ô­ã`?¿<âaèÊü5›cÑhì2þo®¹€ËBÀ@§lßÈù„9@õÝ?0†×*üš`,WéJô‰ùË…êz`1¢ÁŒ/ý-ð6%e6K1Éó\ŒH—) n”Tæ¡ÄÑ11k|W$–Ì“®ßûÁÞk8wf6mYcIezÒüZ{Þö°Ìë@ྌƒ¨ftýùã-÷Oe,Ú.ýÓ0ÀìrQ[7ñKq‘«ônâ•z§¬ªÚT\.=T´+­æS­¸:aîŠÜwc¤plPìà[½ñ˜k4€ûfö[JÞÂ| ²…鮉w•I„Ò´â‹Ø²ôÑ@W2ɇ¶ÙÚ_ &§f Ρ:óÍ¥n+0ŸÕŠm¥¸×@L¹q—uÄ€Žm®Ì?œW¶ÅrSc쥆4)* VU*ó a½Š(½6Zày|ݲó"ß“ðè¹AUu+¥K/gô >cpqW¸S¹ô³ì±ÑSì…ÜмÜðeÓý—baKçÂX<¢é¬éüž8Cå$ú&aûñÜvaR‹è)©ÈÏ C§Âøx¼_à÷:9u*½¹¼|û§G¥mì†×—¾‡¸¤»jd€Ÿ±fM°Í/‡ÐrÑ}2v;CWÑÍhÓ¦Û<ÑwJ„s¯•æõê` ü¬ÔèeD#ìáKÁÖ¡Ï"Ùö°÷Ä£Üê"^fË x)-éý)çÜêp+”Ÿ¶.#ù†hŠ<|.ëë÷On»H¿-¨,¶:¾1ðIˆQú¾¼îTCíñftõEܳ¯Âï6Œ’t¸J=zÍêasÃÏÀ5G9n“æ‘в¾\Ôý\0&b[O˜‰Ÿj´éθE7 ZH‡÷žêk7·=Z¿.óænØA¤¯@ȳ*fàFVrQáïwæ®T`gèìYæˆ_;ªZ¤’_= 8ã¡vŒ‘6B5}_ÐÏ..*Zò1BÛŠ¤~ì®­Ÿ––!îyÃéïeZSâŒfØt3†¢Ñ2*|IJæ_Zk6«t­ž°eÀZÃTSo¸¯ž‹!ŠÐ׉Dª/"ú"8mË… Ú‰Áö:o,¼É)(tôчsƒ$hœ¾aÐs%ó¸ üq ýCìý%±Õ`ZÎàlœM,[+-=ÕPj ct;Y¿0<42¶ìp²4éhQ ºÊ ;Éulÿ» Ì…Ù0ä9L” ; F–pÉœ aÙêÇ“Vwc§s”“ItϙׂËï:ÚÀ#ýúZžˆÁ‘¼oú@:aÉC›»0¥Ãü\”òðuq ­›>BZg¼·÷ˆ n³šÀFÌÏV¬KH–ædmLߺ˜{Â|þÝéj%âËŒ–­cQr^qðfl\\øQêúùÙiËP ')p¼öû½¦Ê+7¥ŸEɹ‚ö£²íUŸ?‡@en.ÊÎMI[õ^ âB7]­9Ú[&SïùjÇÑ2îï¾wŒ|Fº~{¯Ð à5ÂmÇÀ^ÒÿÿõH'Içø‚‡aä³Kß ap“àÐhØ-ü[GÓM¨§aÿNèE8ă|¦O©9ÇæN;20œ~ÁÀhX–x9Ý?ØKÿ¹#xYú¯¥¤ÁÀ<âV*Vòø‡«·[îœL ’âýýßÛ§‚õ…±ìóÈËþqy b¤i×â"´|í 'ixÈþ³í¼Ò —IcU|Å Ÿî³’†ùgš2Ú­AÚGÚ _ð™úKç-ÉZ ƒj&â ^3ØS…þ³§úÃom±„üll¥ëÅ3…^‚žFNÇ’S[”¿20]x ³t/ Oži£ËEíŰa‡-8aW„ÝîâWDß®2î`³x poo5ØpƒyŜ̿˻‚À›BìÀ’[Ĩ±mÑ#Ò™b÷ìíŽõÃŽ“/¶;Ý[.êy+îyÃÛÞ*dQÀúüe[¹D¶íäÅg20!5ÿ ¶<A 4óxÓ<ØDÊ´þå¢Ëšûñem4Ål¶¢k™*°¢AÌÞ·øï+ú#êy#†òÛ7úŠÏ¯Ð.!A’…âé« lÔn4ì'Ú¥Rt¾júÄp”¼ …e¬ŽYšëhÐøÍ]XŽ0IE¼¿¤ë~Zötuã‚£Aˆó6zQ7™jùr'çe?CDÕ½x!3˜¤–ìgaZ {„í¼n»Z½Mâüb<¦ˆSèÉ€z„s"Á³O,|ÒÃë†2;o|]ÓséÕ•Q¿\¹ÔN´øfÎÕ¸Óq§„\QPjBXvâ†Ø­ó85óIýgÕ{*+Ï{¤ q]·ÂgG¬YªH’¹,ÆöÓWmÆSF i†ŠWBå#(î0¯SCY_@ÙáÁZPò»™šm´ämcì4·EQ.“—ÕÿöŒ4á»VïO=Ø–ÜO‰Ï{Àª/¹qZ5ñä{G¾iüÞ Ìf¶ckl;ÛOþXªf¶ßqh÷ÁòÚ ‡®"NY·|NtazÂ{²œi…–rÀ¡SP(º]àÃc5ø¼í´V†°ø‘.œvfár'³Ȇ»ô`Ð=Jpé ^š,’Ý&JgA?fÎ6œØOÞôâVˆÍ„¡“}–Õv呸÷,Ü•Y±öL䃴.·í«70 äÎ/ñ˜èøõé«dG!††ú?ñ¸­ì®x¦]è…ódöIa7»SÈù'Æ)bò ky„‡}5ûœâtø4¥~»Óÿ d0ÊUƒ%þ‹²'ʶÁÜk/_£;è|—©Ê©üÓ¦Žna!3=C[~TߺÝ3Èág²ýúEdx{ã°}.áž3µß ‹ÁR8³?Š g}…ú¯2© Ïþy\Ä×!Gi^ÓÒ¥äÇRò›`- å%ýíºob%Qº‘îÞX|(éÁ ©ä·™ ñ ¼­°ÅÏ® ùÏ}ÀwÆßô¬•bv“êc‚cbçιp¿¥áÂCn4’ü¦¾1czX„ëÔ°ëÝÝ·® nà”®„;¢Ó}z›`¿s?EzèïOŸ½{±µ¼OvW…?ÄÝM¸z\¿ß´žd‡exTŸ3¼s¿áÐÕoe¥xnÄä‰(­¬-iáàa ¯ºèâ1?tê´¨›/ún5?Ö¿û5Š„GØœ8´Ðo0ï7ÐJ B@Z3ŠPaôg™ôE˜¡¿ýV ©$»ú6¸õÿS›½¶Ô?×ÿ­,gþÒ‘ÜJ¡BïöŠJFiÜ=Lù©‰ÉÓ&¦õ¿dÏ¡r endstream endobj 104 0 obj 4658 endobj 18 0 obj <> endobj 25 0 obj <> endobj 32 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 24 0 obj <> endobj 31 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 105 0000000000 65535 f 0000044290 00000 n 0000071036 00000 n 0000044116 00000 n 0000041526 00000 n 0000000015 00000 n 0000003257 00000 n 0000044338 00000 n 0000070178 00000 n 0000067901 00000 n 0000070560 00000 n 0000068341 00000 n 0000044379 00000 n 0000044409 00000 n 0000041686 00000 n 0000003277 00000 n 0000004964 00000 n 0000068847 00000 n 0000066690 00000 n 0000044450 00000 n 0000044480 00000 n 0000041848 00000 n 0000004985 00000 n 0000006952 00000 n 0000069086 00000 n 0000066850 00000 n 0000044523 00000 n 0000044553 00000 n 0000042010 00000 n 0000006973 00000 n 0000010297 00000 n 0000069779 00000 n 0000067429 00000 n 0000044605 00000 n 0000044635 00000 n 0000042172 00000 n 0000010318 00000 n 0000013174 00000 n 0000044689 00000 n 0000044719 00000 n 0000042334 00000 n 0000013195 00000 n 0000016907 00000 n 0000044773 00000 n 0000044803 00000 n 0000042496 00000 n 0000016928 00000 n 0000019549 00000 n 0000044866 00000 n 0000044896 00000 n 0000042658 00000 n 0000019570 00000 n 0000022448 00000 n 0000044948 00000 n 0000044978 00000 n 0000042820 00000 n 0000022469 00000 n 0000025409 00000 n 0000045030 00000 n 0000045060 00000 n 0000042982 00000 n 0000025430 00000 n 0000028362 00000 n 0000045114 00000 n 0000045144 00000 n 0000043144 00000 n 0000028383 00000 n 0000031780 00000 n 0000045198 00000 n 0000045228 00000 n 0000043306 00000 n 0000031801 00000 n 0000035254 00000 n 0000045282 00000 n 0000045312 00000 n 0000043468 00000 n 0000035275 00000 n 0000038698 00000 n 0000045355 00000 n 0000045385 00000 n 0000043630 00000 n 0000038719 00000 n 0000040030 00000 n 0000045437 00000 n 0000045467 00000 n 0000043792 00000 n 0000040051 00000 n 0000040968 00000 n 0000045510 00000 n 0000045540 00000 n 0000043954 00000 n 0000040988 00000 n 0000041506 00000 n 0000045583 00000 n 0000045613 00000 n 0000045656 00000 n 0000046028 00000 n 0000046048 00000 n 0000053926 00000 n 0000053947 00000 n 0000057999 00000 n 0000058021 00000 n 0000061900 00000 n 0000061922 00000 n 0000066668 00000 n trailer << /Size 105 /Root 1 0 R /Info 2 0 R /ID [(3ŠÀžÉªìS¼¡ànwÂ)(3ŠÀžÉªìS¼¡ànwÂ)] >> startxref 71238 %%EOF simh-3.8.1/DOCS/hp2100_doc.pdf0000644000175000017500000052757611143604370013650 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÍ[[sݶ~ׯ8Ó—’š/ ûf˪íÄ•UIN›Iú K²ìT¶[Jšüú ‰ý@~yœÉ´qƃ¸,v¿½{~Üä™Ò›Üýçï÷›ÍÕç=ß½9~Ú7>]íý¸×d…ûÏw`ûüýæñ©ØntžååæôÍ^žµm£òÆWÕ4™ª7&W™ýü~ï»äYº-²V—u™¥*ËëVëD§[•µ¥VM¢¤™§e–WFÛFèÛ¤:ËuS''aò»¡ë½ëju]'wéÖRTmÑ$×Ãç3Yå6Ýꬭ˪Ln`è'ß¶MSÈF¯\ÃT¥I>˰Ö,pÙQ\8:ÿ}úÕ^m¬ ³ÙêÂóçÂ2 ÀÞw®Kùú±Û£.l…vMÍfDÍÃc¥6­•Ý ª.3Ó¶Õ¦¶ÆIÊÎ9ýaO©¬Ò›ÓîŸö’æ+/Ñ{§ù.Ùw$鯕¼L·uf‰Ë++Þ¤,“oÓm›å¹Îur,CŸ§yVTuÉS™õLœÚ£Ô™Qµ¶»‡±‡Ý€¦¬Âf¦Å±°î¾Œ=è©QŒö0uŽíÎ ¿µ ¥-.C {cG*Û]¶TÃ÷k+£Š¶„Öh“üœ¶™i SYoí2•N>„ÉW¾Uè<ÚçÜíwðŸ,8,¼JÝšä—T™L•Ú!¹²WºÅ•a½·Ò¼•¥7r„2àdž°®§è’{†t"ÉžMla­ÏnmX¨½¡;$šQÎpâ@aò6ãßm3¯i,V,Ué-{Øh]Z QZñÊì¨sÈ.Z¤æNŽ‚séò=4_Ë€wž5dA`¨]ÛŠÛêp3ÞêÏ*M˜‚½e‚ *ùs9éô¾·Ø+yTx²\g²ôÊ!op0ô¯~haÿï%°D°íõô¢·UEí¼òý½­kiĹ« jÒÇy-0jb1ƒ·0pL|͵ahy ˆ Ì6Ü KG¶d {å{˺IZùÍÂ7•²ÀÚ Û´ì›ËXh62€£°3w¥mM¤ádÆÂq«ûD à{ïrê¶±²êt¸Íš¦tæÃ^@ad ©™ &$ r‡3ÈÉÿc¿[i㉂ñÈËŠéaüKj'iUThÆXï?ÓD8Û÷ÉÈl}ŸJŒS>$é:WIQšÂ'ªš%+yÜÛå6²»`;WH2ˆ,–dqb£–XˆÌ>Í ÑÇ.„@Hâ j°¡…+½ÿëÄ \F¶4L‡/Ü`T¦ú¤‘[Wt>w0 Ñ°íÅXº6‹¤"Ý´\×3ÆØ ŒÌuï€gôOÏhï9Vø¸`ºE ¬˨Ù Ë4½qí¸Æµ¨lò(Æõ uH}Ðn“¹@˜¹€‹%¼ˆÅb ìg(Qð› aBç…±~í»Tiç© 'Ñ¿¹øŽõ4-Z/à¶ðz˜>J ‚P©Ïâ°à9ÀŸì^­6µ²ßëfánE‚nrFpðn¹ÌDZ9Áƒì…•»¸[ª‘ü˜ˆbÖžw1RäE…º¤c½ƒ\Ë~­Ð— £½PG–HÀ<äÕQ.ÆÝÐä”x•»¥£dïónжWÍ£â…pݯ-éäN³–«)Ì‹¬ò.¾ zy,ç]@˜À 2BŒì­ÄýY`„P¯¥yå:-]Ä*M8®dht¸2±4mU-ß@Ü¥ÌT-fþ;¸H‡ëo.Ä/¾ñiƒiêbHô U<ÃïÙY´f6R‚Å‚R÷Bƒ ýcp¶w¡õ:èñšt˜§š“S‡©sJ”$n<.—4úsçC×&øã‹!ÀýçUwF“àÛ)/˜‰‡±ŸhJÇý:@h¹,éàE&Ö+²Íãt’ñ]âùÝ|•KË‹¦žuXä–bÑ_ýþ˶ÅÜácÊÔ‹¤s7J)—îK)Ƈà¡W·[QWu|mLÖ¶¦Óý^Î3]îÖ%ü†Øs…Ô™%> ™÷J©¯Œê‰ÇgܰË]0Ê®‚ñëæîçxÖ<áF¥ŠÁgÔ*z-á—¹\ßoÖØµé0ØËH´Æ£2R.;®³º0Ä$`/Ž~‰T„åkÈ,$äKA°ºCÊËíL£>x8N¦vì>¼c×ø>|·×«3*P ç§ùGžÿ›©‘Ge÷,èÖbfmùráwlX šK¸€s­àN§[MVèAµÎ©³‡ÃŒƒ­É‹H”£óg¬û,g§^ÌV„E礼Ö>‚´3ì‚ëf6¢esaùØz¯¸¢˜ ¾äbÊ gæ^ ä<†ûÌs,~KE-yÌÀÉ_Îã¤ú‹#ûµtªÊ¹§_ ¡ù(áP:aÍoÃü¾< 5Y%û\ÌÀ±hý¡Ò #¿f¥ 0é‰ÐÇsïž24."ωè#” l2òDFRÁ‘eÝÃ[«+ ³#Y÷…¨œ5ß@Sa Á¯¤@óxXᩜ $ó8HæÅÈ´vi„V>–sß*¡–8™.@bC‡¾ÄóPåê0m !Ã(µ cʶ­&ÖÊþ2… Ü¥†dN·ðžlÄ;âí`l ÕÞd=\ˆ†Ú3 Y¾?[Ð{$i°îÞ4¦>ŸÃ3œËüpd£{®Ý¯©ýª¦fJۆةȟl[.ð{«ñÜÐàŽ÷œ€£€êW®ówÈ€³xsÀHä‘m³ýÌ”ÁZsk*ˆ†#¤ié#ÈcjI\/Hôxi,XiŒåÐÍšQXñÐáZíáœ1ƒ7)1ÏßÄ\çz>¹šsC¡ÚN`ø€&¯ïÒwTÄ€âÌÖ»~Ä6pïëX®ˆW.0ˆ¯ð¤Í™{¢wþ<0a†¬¦*]Fñ@O"b9²st1‘+ q¹ÌŸ8¬§üÙâ ZZŠoYØê‚²FS™" Ix2Œ<¦~;àg0˶蠽 1Gn pŠ[q‰û–Baˆz…Êa¥Bóïü<Ò™˜JÝêLÕ˜Ê{½Äþ’/îóm—Ý{GÐL…7Æ@Ë1¢nÀÏ>J¥Áèãp‹ÌMÍbtü2­ìú•OéÇâìÖa«(G"æK Àà ê£<§4,fAKl¡µôn¬SŸ}{Œ2º#>‡0¸ü¦.ÿ ubŸº{ˆ³aw}h-ŽGO(íð|:d²M§Ç#ät ›OŸg¯þà A;Ä}”š6ºK€Ì‡}sqî:§°Æœ>‘¨@†ZŸÐ8)Ö3>ƒëÒ óð‹Ð> stream xœí]Y“·~ß_ÁG²*„7ð(k}%rÉ–Ö)§â¨"Ì¿X \®»4CݾìºýJ%¤ s:¸È>-%*ÿ×Í_¯”4ÂIе› ZG¸¯s“‰q8¹Ô5iµPQODÙÎt FË®6j=×5%„àJ"(Çs©À ³¥Ô"ÔƒªhrújQ§d”vþmâ)|²DŒS02Í?‰iH*ô÷q±µ…”9?Éü”4cÔpJÔ³ =) zã'ç Ÿ),^øiŠrË=†â#Bô÷`Èk©%øŠ“þ…Ñvþh±4Be<æ¤ï LÎÙcCôYËHùL2)¬9Gº ÐAež†.q~AUonËÃ=úÓ@fû5Çû쇣ˆ¸©Erhš–®öÏVÞœ²ÊÏ$G Ÿ¡Œ{‰Ì9Ÿ-¬ðÚ×Ncwëß”ÃD…ܵ&¢©{1Êαûs!£(¼AèJB_ÑkLð)œ‰;hFg\jéDpRÙùsìPJ§à•«c+âžÑGÆÏԩ÷þýOÏã:¶ÜÂ}$à>ú2£4îõ8¸wHÆ¡6O‚ó´·L'˜vÃ6ëq…ŽÎÉ–@\ v~.„¸¤¾¼Ô…iO®F ñ5¼DÌ ïi–¨iÄçç¾M¿¿Å„Ò²HרŒ9 oÀÉü º|7cßFó®AQd3`®°Y×é±ÕPƒ.¶³¯áÈÂ+ü*þ†– Å¢}˜È¬6Àì0Ú‘Þ0zE Üá0vèp½ ·ŠŒxi6Ä‹!IÏ¡xQå÷ƒè²XŸðp%ž[Z)”¹·¹= @ò©^H3ô[H/?@4’ñùÒdÅs8e÷ñ¾ê &I¨0ê#:Èx¾Ýn*%‚ËbÒ"QÃPº`À€µÆŽ€MQ¤WmO¡žNíó[;ÿG Ejèpš.I†@ôSó.¬§P¤ªyà¬˨‡+c•¿ªÕ±‚GAw:ä]Kï\N9ºpDʱ£A¤Z£¶@¤^v"aÒrŒüŽqˆÔEâ^èC.íã )Ïò+°K‚ m娌=þêf=žrdu n‡ãçHvЀҚÕH#G³hê=',w4љܽg÷˜p´Â¦PÆÊT‹Mì4ïQEe™uˆJØ®–vÀ% ¦’¦‡¸Œi)Ð…RäX—Jƒ?'ïLY¦€%‡€XuØXzAúuÀ‚„3{`!«ÀQ0PæÜ@¡·Â@†Õ𲏲7ºþÙC¤iDí0=:{åq ^¡\lòg£¼lelÛpÌq4©/àF­êY µÝïÑu9_ÖåðP!6/ôÉŽ4<šQ—^†"Û©sSž„ÔyˆÉÍÜÚe d&'ÓÖÿJ?¿¡t)ýŒ Š4LK±º'6Ç*ÚŠ²àÉ‚el‡âªA’dþÃhó€ÂÖFîùÒxH­ÁVè´P°·DÅyáÝÅÜ=¢<¡\ÐZ¶m$+Æ€ì†CØ6ïu-Ûâ8Ž[cô£Qî’í*Ôb[äèï‘m1¿p+62m[ÖÓmû|›ÒíRF0Çá÷‰L¶s”|U²Û=eÌÈd7…†­z#44Ú°¢¶Aü×ì8ØÁ„Y›Ðš·`Ë!$*ˆèPH²µ’sN·ù’*ÂÓ”:2R°º à*$6G‘+Þ™lË¢ËèEPjŠô[¡-K™Ï¥ÞtªvÐÈ÷£ˆ‚àÖÄ\80Hý …çÝ®Å`lçpÛ 0Œ¯”rèèdlÝ’Ø™ã(‘ͺc1 ¶ôÏÂÌ«îýƒÌ(ð@lÛ³E‘G”ä6Þt]ÈŒnæÛ&²¬„‘]Ö뇜=¬/Ÿç\–ÊMR>Ò¨íÒ¢Wñ;ðŒ…´ŸÃ»˜õ:*‘†c߉wã.±ßìÃönfpviºÎôe¼ÉÛþ%Êö g0¦ÒûmÑìû\?Ô¦¸º·Î¿Æ;8]«ƒk{Hž<·gÀ7Uu€WJ6]ho1Ž¢KTY2‰(/º3I!Ô * ›¡Þ¾9Û¨£A[t?(†ÇU„ä2"=°­£«xÈTÆK©M» }{{Æ0 ÂEüq0ïë…Ë£pYn©é»*…IÉåÞÌS0ü%b0=4~ð“4‡ËiWW½lRw~~êŤ9š|X?uÓ ˜(SÀ~긷d;æ8c Ø …š¹Ò†EÌBC²*—zß½rã:ÌÎîâêÕ?ÑÆa½?|¹°3 ‚ÞÅAlAx‹èM”SL¬vR|‘ÕXT»Dy• ¨ï€£ð{g{ D󪬱ù–^–¹]x}¶‚BÛ>%‹°Ö\aU¶º|°èXÚ£°¡ÿ5¢™Î÷V>Á÷?r0g0ÓQ¥Mþö }As|‹uÞܾ§ŒŽb"#{jLˆ¯œ,b´‘(½g(Ÿ¤^¯_µaÙTò¸<ÕUÍzïÒêïL/¼¿*·/:ó¬Ôò(ìeQQ0ª6ôÛX¶6Y3JÊ}X뢘÷N1}ò_#RÇ5ŸK[UÇZ½Êk YÕ,÷]r—yOÉJ­EÞnì[lˆÀ-ø9,€•7~Hû.[æ6ÀPCÀÚˆÜÆø…× £N>0§çɨ:÷i„6ñ U3lJ¨FNµìðlä] …TD}F«X[@ÕÈ­¸züõëÀÈOh“ÅÜ:æ¯ðf¹]ÅíÚv`d5‡üDG(€ö*‡¨!ƒµ…³ô{WP±›…’žQ›©Ë!M¸ä·#BŠÀ­î?1Ù@œE•uA_«´ÙÔ"ÜI€OJ8~ñGäÑy¨O5ÙÛ¾·®¯Í?£Ø[TÄR©:pý{ÊöFӜܭ+zfvÑ[z'b´*»Ý‹|Ðô3áòéçcOŽS–Ï4£9½ôó™ÄÆìDlú9"ýì™ã;J?X~zŬôÀ„me>K„D·¨ÙñãKèÅà ÷Ïži9,_Ç•jGÙ £ìˆmƒŸ¤+Ðã†OâV Ê!®³xëÏiéSþ\ö¾¤U-d@ºÛ&xÀGßY$éBÐ;€’î Œê©CɳQÕtÃq1Â6ÉTr#ÍêHòábÜÙ÷‰[ ܾ`zJl :Ùöå°¡äEù§¥üãÓ3œÆ×óG %»Eâ;Q}nÇ­«^Ö©+%äÉm¬Eñ$±©µÖQ„ÄićåúLeðÕ‡@Ë5> stream xœíšIoÚ@€ïü ßjWb2ûr­ÔEU]¸EU•HP¡$!©Òßg;xfˆÇ$´¸<†ñ›çoÞæå2¡„ñ„æßµ0^ Ž>›äl5(†“Ïoï„«³ÁåÀ‘Š,É«ÈXâˆÓÉh: Ä9KM©•%ZãœJ £ÄÀ„Åà8eC¦‰aš§ç 3.M:Ë`&N¦«lh¥Lë4É'’[–.²!—„aÒI>h¬pxpY)ºÊ†*?œ»ô¤œšô§O½xãE¤+Q¡´¦Ï]zq ±…Òl“ /xíõåç†õ£¿Îý/&ÀŠÁéÒw° áÖJ•~Ì¡ÚI‰ÿçþ(æEZLÊ ‘Ú “€žåptJŠaFóêÿ“àÙ!DhȉŠÃ²_GïÁ_\ÂÔ†¿¸š»h#ˆ¶¥»08žH¥´ËÏÖHMé—l(`TH fa]Ø5N þð-,·Œ9kÁòrаlZƒ0|1¢$#—~îUÎÙp¥E¾ž%\SeÒ7~ )Ê'ÞÐÕz VssŒhi‰•åI£ ð‘í¨Œ c .‡’‹BHÀRQé§~SHÝÛàÔÖÂ)žŒ> F/±¢ºÎ 5ßj±± Þªö×¼R5igã#M; FØ´ dwFë|1õ1ëg.Ÿv›P|"¿24T©}nCÝ ÆÕ¢?šø?re”ªP»ºÅÓ˜1ËòJh•Ü,"á4ºGƒP™ªOoó#[®ß2.îÉGã»|§äáæ£-Ÿ, ô´r‡ì{åŽ!¸G3º…`È_Q:‡âî¸ß/fo·}áCBQv¹Þ¦\â•Ä*‰¤#¼våîa‹¼dÕ´"רhpC]-ëàÂpô°9µÁЇ‰íÚ@U´¦ŠkfÄÚ€UÆDÚ#MiöHSGš=Ò4‘fï×À±¨÷s ü÷Õó²ë}þ;]-ýòÀèmiØ@f´$€w_È.ÿ dÏäp-Ÿ—DzAzõ[БÛ=ÜüMGÔÑÌ[݉Œuxçálï‡Z€åµ·¦Û#ö^».ô)»ù§BôbÃJ¯x.Õ‘^¬zݸ¡«÷ÆÀe/Êôš‘_¦§í2`,)m/b•Ô4n¼ØPi ¾T#ë~Ä‚&…KgÜ΄Ê™¼Í£í½‡ÚêYn‚P€ §ñg^Dæ¢òt[úQñ[T¯¼ >Á÷Wâæendstream endobj 23 0 obj 1033 endobj 27 0 obj <> stream xœí\is·ýÎ_±ß²›òÌ`&ù¤ƒtl‹¶D®¥äTŠ"—GBîR¼$ýûs 3oîŠ,²$ÛåjbÑ8ÝŸFa ä(´ÿVÂÁùÆæ®_mäɣݟKáòxãÓFDöŸ<Ëç£ç3£˜DdÉhv´Y–†Yþ³%*ÐYtRfçÆr2Šã$&S­’ Ñø6U¤q:~=™Fæ÷H¥<« 1„¢)+ ´ÔQšŒ·'a ‘¥éx>™¦Œ¸O™¯'"ˆU(³ñ e½œL¥)!N"®uUiISÅ¿g¿šî 1ʼîjYëo’†AÙÝ™ÑO-Œþ‰©K ©´)¿’FFŠâ$ sÈ@¦Z cƒ(“L)c‚*§µ€0ym¯«´•cZ¬Ç§Óej|nŠUA¨#wÖ$ÊPÏÜïû”xM…,)ÕØ%ÂPÈÜȦ Z¨ŒU×Çê?°3ýE.ª$!õcÊy+Sêaiƒ4â²ÔÛÂj,Zʤ컵ڙɞ›tIždN•Ѱ…™6È(ÿ­(2 E‰“;–£Ù« ó÷_ &¢Ða"JìØÅ£©0ÐÍ -¦c)‹¬RºuÒ©“@¾‘“NÚwÒ9и*%ÓU«J`Jåõ‘ö“Î@-×…$²¨ªÆŠW•(É(2uF1)ûÇì¯Æ/\Q¯ôÖk¦ÂÎ_Þb&J§%€¤œô˜íµø5‰oI‘ø™ÄSY÷O Ú ‰0+a 3#ñŸPmICp3œÃ‘;‡Í¹$ñkëÐÚÑÄc4x¸b'={ªÃ•®2FÑý‘úFC”<ý‘(éh­9£B =Ùõ†køäê#’hŒþ;‘uŸ­ ;$¾'ñÏ ì4˼IâLݦžN)ušèf¸‚Íyd¶!Õ`i1eÆóObä^àýtZÕ6-™ÅVi+ãòªÀ¾ÇoôÀ䶓بý;ñ ‰ÀÁÆj/!d÷úÔ¶¡x—ڞæcµw0oo#÷ ÚÏ$þNâ3_ÕËMÒ,ååî’8kÌme¢ß)¬øläV¦&äI+œîÀ†y½LÂÐFgîŒëÌÃ…´P´é¤¥“.ëìík±˜Û¼ »½, 9\÷ ‰öI\xØlD<ïÑÀɾ„–°9sXKýIq»y¹ìÎëI\ɰ@†ò°ÅÁ2üK… ІÎõ”ü Š"R'Q>â8FñŒ×™/ŸÁ Ï¡E×åm¯° ˜—•€Gêd€þåKgŽ`"rHÐ2Э[™¼Æ$ÔÈ·£:Ç.Uk'=>v1‰Ž(›{+!†…±±csÍ6Œ=v¨Ú‡ð*r7EÉ IDEÿlÎb}´€]k¢Y§²Úc²©g°ÃN¼›²‘ŒK˜h{‰Ò†Â»»è]è&>Ê:Ñ1Ä<Ù}`&oÍc#qÕPœƒrÛèã ƒw=`Ôš3–Ýh¸fNÚõ†«{w±{yAƒØ³Ûñ¢¯K_ -1C®¹‚ÆqÛ–€Ç—ÀÂL0ØY1°cüÌ^1¥Pí‚72'9m|ܨ⸋®NZ†Â‘ã>¬À•àÈJjãKá¹ÄäàÒN=¹Éè„ ÒÈ!ÜæP!ì×\«•±oô€ÿ‘€¿×9i€?Ÿü2tÐD‘!{ <¤}3ºœDù›à¶ç!â ˆÁÄRq<‰áˆg!V„ù£œ(9 ¬MÄÇœv£¤¯×€ŽÏ@ášè}%ƒuÅÌä1ó"ñ°äÃ'@‚\ÿÈg˜aÿš{ÁØÁ\ÀÔX[¯g{ÅXÞãaݼ†y—„|¼-3*á.-ÜÞPZ²_iÎRXVVÌ'Ï+‡rNöÖägB$bjˆÒû"ćŒrîσN`ê¶ Û¬Aà9JX½ÉÏæ×:#´åZµŽ¡gqhµGœ#ôõ·kš7çX†XµϪܼ3h&2°|ìkÙÿV›Œ t×»#‡v=àA›j§¼Ê9]•òh‰î7p!HP^ƒòϱcÝ'Myäûì~[lhU%ÏÆ?ØaÁØØyÄNV;†ðØ\Â*No~Ñ£yZ¥ªÛ+[ZûÊnK§ÔÆØü}d®7ö¬YÓ1ï²óÔyÇh„º9ñÞIÕéByBÔÎÌÊ«u”w<• ‰Ý1¥uܳ†@R~½ëöİ ¸Šo ©œÜE$BUì>\ÓJÓ;H±}\Jì>Ãjž\µž¯w/$ ßøÅ»²Ã7ùˆ]Š#؆›¾Úð.s?T;„åžP‹U¤j·°ïÍË•óZï;‘WÐ}õ¾!ò˜½Î!ÂÀÀvöC(€ˆs·´´ÿé_yswÊyã¤5)íñ|Ïxº†%àÛ«˜g˜Aµ.åâ-¢œ.8!ž!¬°Sù¡ðÚuÒJðBWz¶zŽ %ð’Žw¯O`Þý>µï×ÍÃ^Œõm¶€Äµ³Ìáæ…‹æZ;Ž9´jöÝ bægËÉ9´î“FsÝêÎjùª‰OÔ‡Žñ ßF 2«ƒ ¼ôJÎWÒÈ?§z¾?R™Cµc¨†·Ô{ý0Íaj9¢ðÅtO-'ÁTZÉŠe£ –ÍzëEwcÙÍŒ$ˆ#3‘.5N°#þ‘rËçÅ3ëª\Úº`ûàÝk~SåF±=›™ŒKÙ2Íàƒ·0ðF›;+]¥yÒ7ÒëXóɳ‡S;yÙÎÏm6¡+$& 2ª Û='ݶè¾Q÷ ?`{°¼Øö.Ø—m~Qba; CfèÞêYÁkíFdÏ­‹ˆ|@DZ±þuÆ]p¸Å+çäß‘Í#óêÞß³¦ãv« qn—²Qùƒ« ü&‰NuÏ>ÔŠë½{áûïrêj}½‡Ú¨×;òz l¾M–^é4ih ¹÷‹-pæ*0‡±Õ{` SñœÇÓxƒgEÀàó–€ ™wÝì󞹚қFÒÎ^“gö_zÉâdø;Wå³J"IŒ=¬NƒWªŠç™üGªlZã*›xÉŸ¨r/5УNOD}¤Ÿæ\tº Jej¬s¯p‘™nHÕ’áæÞ¦ú\<ý•ª˜=ŒÅ 8Zûõ7¶:ÞÈboY±W¶.¸èÔðÛ_×°0l‘nH‹&É Q)¹Mí/t±çÉúŸè:­~e8†ÆZÂ^ÏaW½'ÑjO–Ù6-ʦđ_7Pc ¯a—Ÿ0æVÃØ¨æ@,LÞØN8“X¼<™ÿ\_ÚrZοj:Ò­Ò?Øózïí¶LY¦–TgØ"" m¹Wé~¢Döû6QÎàÖÕpMøÌU–¿'—ÿÏŸhÍ©Ì8Èc!­ÝeEG‰áf˜=[“3Áa»1oiµÃÈsW–Yy¨h»xºV¾(ßù˪·ä,}âzI¼`\šÄ {¬ËRi¼^° š"½[×^×.ÕÅì¸5¼.<æíêRs6}×;˜ÕDX™ôð[`1¡ÔM^™«bÇd†n’òɨ¼%Óí 3,Ð:Ik^EéYÙ>›Æ× ðªâÒ©PÙØm;CÌ¡!îõiI‡ÔcØ Æðm¬íÔ¨ g9+éˆXòšØ¢W÷®á _óñì§ÊV¦Š÷=sš(à"eˆ˜mýV­RTRD©¥&°ª—T­Z¬‡ÌÂ̬KØ[ÖE*ë€~džٳôÇ@Aª ®Œÿ±Y¹Š±ò}3Ög©ÓÂ]¤sW…]!ERÙ“Áš-ýlpåcëá {ÅMë™C@œ|á¦u­gζ6áW¦•ï+EIœ8_I§µI¤ì3´ylêòB{]S†ýzGb%ˉc%6¬A©¤Ü†¿’æ·¤8HHÔZ†€kÔÅ0AÅ·,Ò-tTóŒ„É´n­_§Ã8îá°$emaåÝÀ‰[zÁ:–ãO”zSG@˜ÇëÈ‹gsûɵç}K5ï‘_´†òY_„QÆ‚>S«~^Â~u=À;­ÊZÝ &“`·kîÃe@ˆ‡2|©"÷mÙÈ6—/è­) Sµy<Èy-cj^8¼`i<äÌ£¢t@À˵Ü€6;n|›¡Ê‹¨¯žTà1}“ÑP£PÔÀxŽ9é´Ä³Åƒ¹|¿îv’¥¸¨kYcáµ–¥6¨ÞŸ1EoÙ&lo(€“fÇz䆽§%4rd{øp hí´º h‹ÑKCmÃ×6æ¬a^\·.¬‡®ÔœCµw ÷-+”9ŽÅÓ€2Ž ËºòŽ9®¶¦,Ž×‚§·¯QVÕOÜE¸•ÉXë–h¢ oΨ8ÔǼ؜µvf°Ø|‡¶hMyOá!ô@ºVLû;îM>›®ëàÈ1‰Ö˜µÍAÃܶW‚† Í-EÄïMÓ¢* ¢,cG²UY¬2FÌ_®/CÍýCW-Ûœ›OÌhëTy;v>M¨j™š Sf`œ0…r‘›Ã«Â89ÂsÿƒÞ_`ç°o`0ü“PqAªãˆ4œ»­ÙÆóïÿ^äsœendstream endobj 28 0 obj 3493 endobj 36 0 obj <> stream xœí\moܸþî_¡o]YE¤¨·¢(8qšCrqíMpÀ]Qøe㸰½>¯}½´è/©ÎP|$íÚ^g4ù2àRCrføp83ô¯A ÄæKœ\î™[*e^ü}öƒf›Ä–­Œ£8/rÍsvªy¼ §I”扚|°Ô K½ ³8®Y°™IÅ*S-‹@˜¤Y'““pšGq,²l²…Œò")'—zšR·JiHóqžä“£jž‰ŒóÉ‘§D2¶KÍvrG?]Cr¡Çú“"™Ü„ÓÔÌC–“Û›.[öûè”X3‚;[3x.TÙôŠe69¦ß—µd¤–Ç‚Z/Lk’–|qlBsÈŸq=o¿¿ê›¤Ð£æ²˜| 5%Tœr|&¦ wiðZ}¥‚³e3Œˆ Bc(2É#)r`žµf"UʨPÖÖ^Õ¦)Êdò‘È÷Dî‹5³_ßj;ºiÍ61¤f™åy¡Z9È,½vk…zªf/2õ³ƒ¯eÀú/¦”knJíïsg UW½õXëUW•I–f¼[MÔtPY£ài"‹HjØÒÒ1’Ôͳîè E0{·3ûãÏ“=j$‹< ˜­Q#35¶Tf_ †fèwŽ{õî_OÀŽù1YWÛ*O%çÀ{&Ú JåØïIN—¾E©fÄD›’ìJ¼âòV;¸ dsf¢ds:æêÚÂ’Rñ9žA9°}±0´-kˆ"Jé Ç$öîϪ¾£p’d‘(Ê¢süäø@ä "_mO¬Žƒÿª÷UV °˜$ÿJ_í‡Ó²{úeò¾åƒ—ú8Wö|ëó°kÁIe Ê@y‘cSªŽc£!&*jÇF†Ó8ÊD–ë ¦ _EiÇJ~•*+ÌêªÞÊé)&éD±öÝPFYa\‘çÄ11r†)kjùH•dë2ÉêhŒ¢y<#AE¥6»¾uÅ€v§Tó+4ÎOØ‘‡.·©¡Ë<޳TŸ­Ò(Säz±‰nTÚ-|]ý®â¼lEP·gP1NTÑŽli¥ûì«u«¨NF;‡ÖZƽàL»ªqƒwé¸Ù·¸ü‘™Á/ì!qmŽ!Ä6¿_YjIÞíèaQQï%O{Ðʾ—™_K]ušwÏ-©¼:5ú(Yî„/ ä°iÑ žqOÏ’Œ×¼vø…o®[IΨ,µ—({°ÈzµÿS£ÎØY)žÈ‘õ>ãÒ-W]©W.'tª˜êØL˜m0¨»sÕlû¿Ï9øû®6 ¶Æ[Ø!‚+gä¡Áw¦½ß8sÚG]Mמ–]Ó$­é¤ÒŠ9O§”ɨ}9{]ê<©¦Jòõ[ëžVÊÀIbܶÚQ¦á2×0Y敟«2©40#¥²îÀ€v"Ûw*hß! !øÔRêžšÍÀý׋qd½m­®$Ö.•ªpÙ»Â7פDf•b¥þ4—ü#øýï–ºµÔÜRW–:íþj|¦S"èTÝyNä-‘_ˆ¼$rû2'p`vQ¼X’ä%’ïž]í¾+ÕB;7ÕIõÐ~u$xf© OÒšû”_m‘çpùluKغ€BaRÛ#ò~v4¦ Æ÷ ±ùbHG ¨è-ˆgu”5¸¤ý*¤žƒ­ÆäÏH¦ %$™DÞùÞL°ôn  ×™Ã0Û ¯ìÂß[êp QÓ÷_Á¾8²Ô%Ø?x§³8³ÎkH®c¨‡D~…â»…¢¾’D6K°Ò 0ƒ‚ÜB[À8̶ˆœÁV†à?n»I¾´ÔÛ5$Iß/,ug©ã¨V=ÛA˜³ø3Øzå‹ùb;dî 0hãsÁÑ* #÷Ož´×ƒ‚O@ž'À‚I+7«Ëý 䃰BBe  ßXêGKÑ¡óÎó û±.KÅ!AdÓ;g/6?IvZÂÑÃÜŒ±$¥F+ºÞÃðTÑfLœxÖ´ål‚Á Š„ÑÄnϽ|E‘¯:›ä©¹2〿Ÿ*E7ðþTi%8õ’ R'ÀušˆQ,Šfu&ø[ý°B`¡70<» `6É;r¨‡1©ÜÑL(Ø8Œ¯è}"ÊH%qг²mlB÷‰%…²˜h­»üMtdcL ÊÇÉ©bbéK¸ óprõ¶•¤1&·côV´ ®1uè’q™¥ÊÜ«[£ìF͘½ž£q.wWn¢I…ª£ÐzÑF¡M¬¬g^°@–ÊŸZí÷DŸ h‡S«Âè‰9µXð¨1'ß½øæ1§mpßÀ!p4{?]?uß+#]eȹ¦k$i‹]u./«ÇudÏ¥è]$WuÃ×¹ÏÐ÷'@€È¨=A›é~†‡cÛ`êÌ'ºû8‘ ¸±à³ž$m£°T (¯DËÍîá³Rô$è¤aõíiP–~N²Pië‰Ôärå 7tH9~;¯;÷CÇNðu2@öØÂ) k8QÖá v`ǾS-QùCie¢lý!˜¹c\seFìhÆ,öú2– 8‹Éš<—>ç±Éxâ|á3¯Ƭ€Mðw˜ècjÂõ…#ò±pÆmŠ %œbÇëit̽§d´-)e¯_òÃOÞ×ëj÷«ŒÌïŽooKätÖt/Nýidÿ2Áïm~ªÓÏcÓÒïá»[(ZôOË»Î0i9[Íû îxÁy+…öÞíˆyûj¤Kîíc@g­9'í9”ëè5˜õÅ·“Ì\_ªj?gý̪¾xêþ*ŒÚðÞÅH…oÏNªo\µÒï¹L_/Xö!Ü‘ÏV_µš»´ÒPŸÙ²O?U¡ûjÁ;¸5±v[\€B°³„7°ÖZJ?B“ÑÙ]²š&†O4"(ÕÖ8ÙDxb9^ªçAáhýE Jè¸h]•ذ`ƒuzÎÙª‡±”Å5|{2`Ùj…Š×cÜ-¯ô™`9r×G<â­5RiA[dzçɨ§"G0…Jþ~€}¨ñÐ róphÍqf¯Û‘#.¤»#®»†öVÑó†QÜê+é~@ù|ê8[^ù|wUÞAÞ_³Ø:¤2µ¥Ä €//8T­bPm¥Ó´®ÚWJDeî”(센ìÖdë€q¤+‹XL)½Áܽû@{¡X¹XÌŒRmõ¬Ž2¤œa/«´ÑëÄ[j}  ¦:{xDÕšSäb¯^T¿´æ¨òÀá \å£Þ‰†‹(ÙP~Ièú÷#T{ɧ€w¯õšam¾EJ^\‰ž àDDß•‰=­q?çPáüîŒ`Óc7*]Æ´ K®çþxWŸþXµ¿3KÆÞ··Îçž+S¹Äf8yŸ&`„;O¦°ü×ÙÓ ÄU7XEñ ÷Qäè)³þÅ¿’aç2½’‘”+M=cÚ"À.Oû){³ä\ªÐÍäή¥YnÉÏß;ÒûŒië$LÁSÔ¬ˆÒD ð¿ma»‹™cp¢4+ØÝ£ßà¸vgç\ïèD8÷Aů§ŒÎTf2*d^mÚLQÂy½ ëø…Y@ Ä>~÷Ìø3á>‰·9dön¡jsÕ)1¥%ÖpåLý)§5³ßý‡ SÛH ™ÿ‚OvAºç#è÷g̹LæP?ÊEü…g:Vا™6°TßÕ{× a:~[_zòŠzäÄ8ÖcÁ¨ys’8©öcxþuë®áî­<ÒÇ”GJ䋇ÈãéÅ=¦²Ç3 õ”VῸ¿8p±åƒ¬b£»dH Ž<è|ÏÙ ?«¹gAê{{[ƒ3×ø.8Ø.S¼‡è_‡›ýëmýV™ý^¸iÙï…[$û'E`‚”Ÿˆ8•YóOk ÎJv3¢wúþ2ÜX!7~rÀ¾x>«©#&Ê_º»Ðþµjr¾õ½uÚ¶ñ(ÔÒøyOCÂøùCòeòšÍòÛÒØßWóG;mè•ÎK0FûEï³d¦†+¨†c"Ù{¬½SØá7hy¸`3€£áGÆx4¯¢sÇÊC”H¿ú¥¤kªÓ/9e2Á/¾·U³òšÝ¨Ó6þ§Ø'ƒw7†=ìj†!?BþF¯ã6æ•z}œûä ^Ó1ò ®–ß›6gã}¯Ë³Ø> stream xœí\ÛrÜ6}Ÿ¯àÛJ[w›ÊƒãÈ^'¾EÒª*åʃ-ÉŠ¶$M"²ÞýúxA7Èæp4#jâªT›ÁÆéÓ°1þ#c9 áøròdßfgŸ'åålÿE-\ŸMþ˜¹ ÿ•°||™}è”,s¹3Ùá§ Ë+˜­fåg,/2Ëy®Šìðrò~ç`w*sm¥ÚÙ‹Òa”²(=‹Ò»(ý‹÷œš'„AƒÄ„W¤O¬ŸçNbñˆˆç ^øÄ9ˆ× Þ€xLŽEóοBxu~=üqâ¡ÚW“ÿß=¸o¢ôö€ Åm^ؘ¯È%f°®¿¬ÔXfø!J¯£t@¾,Ç€¼ñàác{¿|-ŽË¿®îãßGé废‘p|ñÄSü—$2sò±3òê59ïƒ2ꦜ«ÏÒwç\جWu,¼jæ:"±¥1˜‘Ð õÀG: Š“mÌ=‡M¶¦{n«%Ç0 ÌÆzA˜÷i”^%¦äW¹p=¦< q~⟂øj‹åQëÉ0›sµ²¿}½V/0Â,°hÌs»¼µKøB®ü”\î 9]E6yM޽Š¢ÿ%'{JŽEó~xø¼¸Wï¦Êšùò‘A£0(pE:–[>fžLÞw¹4mLëhs2ÂL(¸Ö5G䪯IXnÈh;ôÀè®Ò$Gcß‚xߟT*V™Üݰjɸ5m“k‹ þ‘EKñHËuiI°ÒiÕ8¬TQú)áÝâ‰"j–€|IŽ]‘5ŸIhÿG¾=öyUøSÛ £V»Å#þA,ã?ZUÉ£$z Ù;pŦ\lbm’ÚÄlÈMÀ9úòÅv™ùÆÆ2‡| ]ÉU¹\èçݣ¦Üã^{_­µ &¡û@Ù5^ð£h‹¶Ï<È&šô£{u°ÉõñX™äaÈŒ0O_ Zü%éë0²# µ(=Ùç<¦J“3ãÂ'A¾¼ø1‡ÿfÕ¢1«7!7¹åFìü¶Ë¼ ü~ý4J™—¤6†É`Xáù`-÷–•a^UZ¶¹ˆ†^CåÌJëQl¦:)%)X3)^ìª ºÒxÉû>À «ÝðñHêBâYÐØOåË•Ãs£æ0ö&óvÕøúí¿c1> ŠŸGé8,áŒùOzZXÑjA>é9*dn ÆÃW¦{ÔñJûÌŠ¢GõdΘ®6i9`jý§2šq…GÍø#exüö\«íÒáUNkŒêoغñM´‰fmóz€ÔÑÔ±ÂÔX…ûˆ g$sŒeŒ‹fUÀTIÆk$¦ ÓÚaÿIΫƒ‡…ƒê%fp¬ù4J¢4ÒM”®Ñ†±ðö½ÃÉÏžO é_åß%…v™TÕ_Dø?sÙõéäÓzçë•ðjKç¹'ªóõê€pÌ Bô³TiéX¥­¸sm!´•ÆæFh ÕÀÓ ­÷Lޤ±P&—¦«²Ö¹.pL礈j§gµ…ðäIm§îOUHì‚-Œ¯)ÆÑZ*½ìA]‘øº£¶dj,Vûøßöm´Þëj­ôXÌVJÝØÏ»jf,f+¯/» éÓò‚ôNc­7(ÐÝ„hþ…¨l/‰øß\K«Øáľ´2Ͼ]¿Ïâ*×|™ïFS¡­O¡n©±Üi—ó>ú-Q‹x¹qi•¯—`ÁôG^°>Å7}L–Ü—Üz–ÙÞûЖáwbÿ‘XîÊž´Æáyz«y—»ô¼í]:õË–´Ù¯ )rn"Dª™?5lAåb©Çad|+Žú¿ÿ­Dy¯ƒ-l¢kÊr)s§P² ã€{ò.7v”yÛäíûÁáºþ/¹ñeyEÆXŠ;›‡ â&;~Û‡à7àæ½š ÞsX6|Û2oKÁ2,b8!Ø6ì»)Z}?3Ü8ZÊ;íè6|‚ý«Dê¶Õ]€jÑYYµó$JÝÿÔ@H!s!·È•Ü}W$k#u—)dA·®ïG˜[‡Oê~_?,+xÕº?4|ðÑiðP‰ÚÛ{Í«ökØHQý°NŸ¯Õyít7Ã}™ô6›‹¡µ¾© GwsÏ“¾kiÌN«‰˜6Òº±i‡²y÷`»vF>–‘¼êjÁ¬Å?ÔYýs×O`™NšºWd;²îfJ§Ýl®BcÐí|CIÔæõlïCë5ŒR­|^µ^¹ÏœÒ6­W4íGxŠnùžÀ€Ø±ýÜÖÅp­Ò.hÓQ=%-úíÂS‹¼Bƒ>¸!)A/´AËE÷ûðF+‹ï%;ÌÈ¢€Ò09Õ@¡ØG¤ÚJsòö<*à mâ¯wÊ2+¹¨§•F7N]Š}<ÖæÎٲ뭄ÎqÏû=ŠWDôiætè\Ãi¼vÇåíþz“xšÝ=œIñůÉÒL£7Òý}úÌJ4©ˆy”XTŽÅk´‘'“ã ŸÃ9ˆ'èþ`œó(¿heŠÎ\–9Þ3:2ß%Â:KÑ9Áѯh¶;x0Ÿ¶i8P¢{„H)ûV5@'m’ÕÔ¾2«O¨ø ]öóJvö-+F¼ÁÒœVòiÿ†T]EÞ‘DŸÜ¡ÂÝðê¡°‚¢Y\~™ÝŒK)˜“A½¿q&Kœá¡½©Ëò8‡« ‹Ñ‚åUæŒÇ} ¸ïE»?! ÿMWsŸWQ [¬˜»GšÐ‰¶½š0<7EÌ«Óð^犲Êo8BÔL¡/(EÝnâ^tp¿‰÷Õ·“ð51áõ&¯ˆˆîÃ9ºDÿjNŽÁ4BÓïl\P„ ¯ O mÛ iGÒ)‡Ž È»âÞj±ŠÎëtµ¹qÿ.é¦mnm¤ÛžÎ]8+‰ ?á¡Äש,•Abíóõ¨ìÝû:†%ôìsë•â xåK¸x@e‘Ûä1"9©nr*ÃÂ"ú–*›i%–¡/, Mp`p1´}C'laÏGnaN¨Ï.JÎ))Í µRVu¬õ¶Y­<…\8ðöVŒEÁnF2 |Á ¶%ÒÕ][?ƒM^çsØ¢—ÓG–ç$RÈÏQtŽNµìH«ûAo^@uC M\h`køOÈŒ˜áÔc¥¢à©öl—"x'ª‡u“ñ]M€‰â' C¤LºIlD£²)ƒ<±cåèðv,ìK\ 5JUC(ü ÷@µJ™àë^SâˆÜ×°UC­jF»¼(Ò Þ ›-ôM‚ÀƒørLífuGïåNâí–/a^Ð~¸àõ­H!\½ƒJé—ÝÕÞGíþŠôgNÄÎ=/P­ôµ¶ð¥Q/¨ÑVT8λ_ñI±\ŠàSB-j™¦'¸Á·•ÐÑa¯•g…W*‘ТãÐ ªQ¬Déo1TŸ mì×¢^% @ YJµótûsˆú2N<½ûóäÿúosendstream endobj 44 0 obj 2650 endobj 48 0 obj <> stream xœí]ےܶ}߯˜·Ì¤44 ÞªTÉ’œ²ã‹"­Ëv*5»3»Þxµ»Ú‹lùCò½HÝr4ŠåØ–°ÆéÆéFóõ"M2±HÕ?]8}uôÑ‹zq~wÔ]^¼øÛP¸=?z}Ô$¹ú¯»À˧¯ŸË³lÑ&mµ8>;J“¶mÒºo5[TER·m¹¨³4©e…WGß-³•,g¢¨—i¸T¥Ùr½Z—Išf¢]~ºZIUU³\È yYUi®®f•¬]‰åóUž¤U[¬ÄjÞ­ÖõòAVÏä•&_ÞtOÉEZóâ5oéÙ÷N3n³;jöÍJÞT§e)/ò¦²Z^ÍrCW/W…*¶V7Y¯¨ê¶+ªwg<íßÈüýjµ…|J^óg÷­¤¢âo±s›îŠ‹îÙò9íò¢ë›š Ö õ®rDDn§»s¦.hözõÏãÏD!QÕRPŽ·R.ð³¬ÑA¼·©²ÒÔ92yRˆ¶^¾]əȊ´ä¿³þ@míøÈ%®NÛl8)çJbõT¿4?F·ë‹E@¶aóìñ[’L&TçP~®c“Hm±‰Kè÷/öƒU7ËÇ«u;ˆqYVbuØ£ûçåUY†ùVƒD\Íž¨kY®•˜¬µœ¨VKÑ ËÖÆ…YHÃËeU5Œ‰úùd’–­¸ùuk~eïxm~fÒ=üžK\z0¿_Ñxoá;¹Ò¯Å«¢س.Ô;åe] w}L[…þ4«°iq[eãÐÝ¿¡:ë/°D6ð*<’6YeÆÛ¾D’}ϧ§Q)aÑàaÍ[Ϧóôª°V!ü±écïBKû˜Öë׫u%WqY·léþ‹ÊPà+ùÚ‰hª¶ÁM-Ü)S]ÁˆqÐz-61ý`”Žr1CÄê²Æ~†`@ñl ‡ÅÍ AÑŽÍ0¢M]X`h°åÂÎ]\ÊX؉Lž%Ò6Ñ"£©¨¬ëîZtU×EÙȺÃùËñ¿†?¿8:þówýìu¦! OõÑâW5Ïö°Û05w¨æ“~¤êZ) u«†2Ëoú«Ma©8f\ó©G “ÝÊÅgJ•ö:áزñ9€ý.ô`ééÚÕqʺð‚•e™¦ð’™°¼z,j“¦Éµ`±!>ëlº©BÍömµÒbÜð~¡9bÚáõË€k[`ïðÛu&Ç_©hkøZLOÜÐ)•™—sK`Ýä©g×v‡æLXÏM2räè=î–x)WÖªj¡{„Lž°óÐÑëÒHͰdªÎ8åæO!7Qm^uPTôu xž™V JèªßÐE’”ùDž#!8ˆDeuXÏòÖB4™}«‘ÁÁ½6ÀBÄp… íO«V®±<6’:-¹r·ÍT1ˤ1¿†»A¼[ÃVÑœM“4¼ IÀ­Ûï©äß¹ÐB…—ãAÑa\&dØzV÷×Õ³J½2‰æºP—؃…¹˜RaJàwïîzÑ$”tÕj€=ÞÔµZ0ÅŠ*à¬Åpa»0ë‚=!¥ª)œ›ýL¦—;ëèš}óØdRc}[€ä§à;à4f`þkZ¼T°b¦åv 릩>F½]IóBd"®P yÓVÎÎÕa{Ò¶’#wEã~€9°”t‡ä­¿1s(„Þ}£"¯àd £¦vœê ²—ÎŽ¸ßtjc›8&!ÛÁ§ã—¥ÖЋa•àÓÎ$±v¢Á†blå¥^éf&UÛž…Nz‡kÆcö"´ÝA¯È°ç$QçT7€7¬ Õ`ÂçIVj4)F!´[¾ÜŠí® 1cý^jéwÝ¡§X_€›°Y‹• Æ2¶ÒC; WžB»¼QˆÂ"~qþ8HVÌ.‹–}Ô´fL{ LÌhøÛY<†}ÊÊ P×ç\t ˆEÉOÇä/Ú$O«Ãa66½’cù£l¨U3v¿»š=n‚éÇXphŽeS@þ” _üTHa°¦N¨ªKùv¿'PXžT:/7*Dv8ž EML›]²m/~iR¦®,EšÂì(#Ü=—Ñ´Æð‚Ç £2çI‹y ìç`– óÉ`V3@¡-’e`™žc´½ƒ&ZT,èGÒgnFº (±šqÊ„Á¦h´rcšx6š‰$m……fä ñÞ‡˜ÇJ‚5Ù = 6¯XK¦æ/ W¤•˜äz@X儯[Æ(Âê;ÁŒ¼1¹Y•…9‰öÔè‚CæÀœu ·QϤÏdHŒk› 0 \c†÷Ùrß÷Þ†*Þjá˜ît¡Û»FÁÃŽµ3DcîÑ^Ž%|¢œžÕ(O„ŒKo?øhLÑz]p59yð(2ËÌâPLñ˜‹eU›wX¦ZÔ3s‡)ÐK–›EnÊ5/÷eÚŠÍÆÍw¦²iËéZƒ‡ÜœÍ·÷0 0|4.?¼Z1Õ0Á~â§Ñ”ºÏ á^æ•#)î|ŽÈö°?H›D*M-…Qçí}l--á8":¸˜ÏF ¥Ý¨Îô"OM@WU%MÑ´úM¾X­ó¤¬³6WþY]|Lŧ«*MU§û–Xh˜ì¢*tK{º‡&h^×ë`ßʼn!#L'1e9!öˆfŽî/ g· \ Ó°g͉‰iۤʉCžAuJ¾mãþì&Ô¢g ;†™µù²ñ†é :+bÙÙvô–’J¤’%Ôã ÆŒÚwõ!Ít›ôü~À¥¥ö(æŸÇ1Ï{rPãÃ1 |ß(£ñÔ=©\çò¹!_œ_bÙðƒ”p@AXdãxûD‡k š FØ;ÒïÚ 3 épâFñ/%B )²…q´"¶ƒ1}ÒèiŽKÚÛϳõJû™Ùz'd™\eŽûCï:µARºk´ ã8à¤HÊÖD òð/5[iÖØ`¥˜xL$Vk!!›.ÄÚ¢E áÁ&B xæ{×J O˜`ı—ÄúcFœ*3J°«î57âØØõÑ¥òfb[<® q›¥|FÙ8MÓßÍHOž˜Ÿ¡…ÂðèÁ@O„ªpÉK÷Ô ö·`SÇñQ£KFï¹¢þo ›ƒ&›IZaVïºçÊäLö‹ÛâÈ”2¾‰)Lr(A/\ÖÍèÚxJ4l»}¹Å!G©–ìÁîmÔaÁ?ÑsŸ‘È›CÄj3(dð€ƒ‹fÿ“7%¤š¡³ÉŸöctóºMò<Ó\ãd¾†-ù$ "]—£"­j†£¼f:)Ã~#`|°°ÓcyÔœ‰¬°ZÀG°h9Ñ Œ(d2Uä #8j²àHÄ›!&·;÷Ì>Ó1/0x-°PØ 2ñnò á˜:¥·ŒêïÑ>¯<^sxwÕ»‚–‰­ Ò¤ÑgǤ8Hu ¦®LIIK“I¿‹×5YñÜtoJ;Sº5¥÷šr×ì¨xOÅ7TÜÂ"nìY»?QñôA- sõšŠ±þ¾‚]¿Eöà+*žÃv¿_Â&X/ßÂWfÏ8…]» »’ªBJ@C®ìwþïW4¼úœc÷#´¹|³\žñ›†ÒSSúÒ”^ºõœw¹‚ÃvGpÇèâ8F;X÷Þv ‹¸ë{J+îú¾|ô-Xw®Ý UÓ6S'–£‹¨®[_ÒD3"jw¿wQc0¢ÜÃ^ÁÆþ·òuoóƬµsس¡nÑèƒØ=0¨Ì‡ß»¼DëÞÂÛ>HÉpõï†äI†·¦ÈàùŠ›M†÷L°Rz ¯~3J‚ròú·!'âC““O~;r‚Åß$ä².߇üñOG1ÚcN4;[bô‡9ç?¨©©$8ùÔf?atT( `RÌvÝt>áh¶$Fà@g>‡h…€âø¾·ÛÖIYd·«ÄêM”ÜűD°ˆN6ˆìjȃ6qÅ'LÙäD‚à •XPË›H.œ€žŸÑÜ‹ZŠŽÎÄ^aψ;ËiÔqjYšT… o¹q{Óùqb©·ÿ¥¼š^ì—ãi•¬¹³E"MDF±§ÄîäÅ2°ékªþ8 žkØ ½0<ò¸¹ç#×è’1ExpÚ½›`&„Ƒ帬xж*l]x4~¦3Wušlz¸¥jÁŠI‚±ú&×Ýîð¼~j½44ºlºñ)ö|œhT E¹müt7¹^€ÞG †Ì‰šÍÕG=Ú¥lµ>êL¬c.Ý ›¨æ––åqñ#@­³ ßÊe­°£Q+G…õŽ™ú‡ü÷_|’9endstream endobj 49 0 obj 5155 endobj 53 0 obj <> stream xœí\isÛÈýÎ_ÁoS î­Jª¬]Ç%ïÚN$Ƶ[^WŠ"%®I”uøÈ¯Ïàš~<`HVŶ?´Á}¼îééáDZïjìb~2zº—Ž—£òñxïEMœ/GG™ÊHÏOÆ;SýaŒs/OÆÓ£‘ïåyæ§U¯Á8‰¼4Ïãqø^ªœŒ¶Æ“é¿GAàÅj<ýe4ýó»­Ý‰ï…q’øáÖÑd[¿Jƒ(×íÌÓ™&Ó@EéÖ©¡àõ‡‰%ó^_JG‡æáùd;ö|?P9’Wæý|nzKO2ÐÅdÛšBõe¨üTW´ã,¬§S>]ùA«ß§±ÂK!ŸÐÁÿ~W5™d[Ÿ'¹—f¡¯pÜO½¶Ôãzíe¯°bkCša,èÁb9[CÁ=„ sì¬Ãê²íœ¶­8\H`5y?}9RAè©<к4]ŒÞáX0èj%  -(à åPŒGÅØr8XKDÛ“¹D‰Rð¶ŒI3™Í™k½Ðïq%’\Í <âqH% sá\^ȼ‘IæáÒŃe¸¥–œ¡ÀñJir/ôÃFij]Mü§Àûêhp˜DÉÖvÙív*/Σñv^EçÀ@Á‰Õðk­,*Ôö›„[_'AêQÐ87@¶ra =ËÜPsY ÃÔÈ\ ]cÒ£|Ö öT–EØþXfql‚Š¸Ð¨£y¢ÏÐ>š7³’ë‘ÊÓ†ëz.èóêûRóüÌK•j4f;£ýÿPõúªÔ±¦ѱÊa>Ý }ãdµ3M²Æ{jîjø¼(ÚÚפ§aT̶¦N ue¨cCÍ uI¾]êÔPcC]oå‹3Bja¨'¤ç¿WT‡šCÝ×¾¡ÂŠŠ²´!‹o|!!ÇBþ¾%ô®Ï„ü™~PòÚö¥ûBî”d’åYO¿0ሒOèÔŸ4œHjMÒú¡1ùkM©¦+¶ ʈM¿ù“¿Mµn'Z¥U£jH¶!€”$® mA¾žË׳a lÇdÜq€!ò O"*q·_pªyª"ŒJ! #q<¶ÇÀ€ýþl¦=^nPÐÄ,K%ªÜË2ã~?É`C;L¢ã0ì´ö‰¶‰%x€Ú Œšà!?û&†Ý< äi@Е ÇnZÔÚKi]ÊzVO ‹Kǜ䱧3NŽym|—Q“‘*] i/MˆŸåÆgŽ'*Mõ&2­B®H‡GzãX|žeU À†ÿèrÍ£0<‘è«6ê4l\~¸³4¬©1¢0íÁ`·'¾',VU€kùùX;ú¨aÍKƒÓÚ£úU¤e³Cd`¨ÄP¡Ä÷îNßï~â{*LMÛ>q\Kq<ýBµtþ ÙßòííˆG…äk1Ê•«_玙C;|¦Ã›ÜFPØY15ÔÆÈSüÌ1$0Κ)SKÿ9„YÐQZsè{‘¨ì)ÅiàÐ)¡pª÷iÖœ¨È9i%rhç&qÒ‚™0O¼È/vvõ¶N0HÉàñ@‡%¯phÃÞAãfty-yãžæŒTvX½ûL‰%úò8¦çy«•;„Y#Ö‚6Ôì÷u˜jy!榻¶/»­2”í`™<~¬t?MmÆaáauPýOyMÁmÌŒ«ù:ÑÑ‘vy12âÀc•‹^Qk=büK°BƯ¢$['+cÇ»ÎTg_˜hA‚JÁì—<§äHUЦÀ(žÓò ”¦Ky•X¥^šg –õ¤) ú8ÉÀ°/ÖfmÏ\”¨N”båã2vŠB?nò!~Üc§ææÚ@Ó  x&•ÇÒt¶\Î G+~•%‘ê6¨Ý€!¹‚Z»—&_ÌCï&ƒSk f Å]€`;`‰?‡2 6:A—²jðÀp¶Ôs¥lˆbh½°¦›°jÒ %”ZÁk˜šk>ø^¹K~깡¦$~ý± KŒí´û‰ô·C¾xi ½b­ãYŠY Ï$©r.äJÈOB~r!ä!ÍñB^ ¹¤=ð™ÁN…¼¤S¿¢ .RÕ2-<âm‹êµ¡ÞtħŲ‰üU&ªùq~üAv@Ÿk§ü¬=ܶØ!¤“­üݾ d’¾ÐXvaâAšC]™·K†Cà€Ûy0>š›ï³ÚÀ΋‡µÐö²U™Ÿ¦½¾Ú™ cé…\çtFïˆÐï’h ³ {z.UV&ò²8"'<ßfexúW[æùøjy¼P9œPÿ3þµžÙà q>‚;û6œdºC•÷ÀÉžùJ/ä@CŽ;Î %‡!`û{BN…|Nqâ =9€G=SN„ü,䌶åÞŠ£ÒÅè jNÛB¿+:ĘÎì }:§W£•G,ÎÑ`AOJí 3m$#Û㦙ÂÙÌéÄÎÈÚpÞn†(ôWe2NW÷€<T9q³ìï„ΖNlFyÃ?[Ò§ôé9‚{¹s:šÓOòѸr2݉þ—j¸‰þ×CXçxÊœõv1 £2 ì€ ´äqô°#ä‹ï``XjÖ¡h˼¯$ñR?mð‹Œëâ€VÝïvµß+ú~kÔà•¡žYz7¨?€â½ò•ÏhÛÿW½r:Ùu!­TÇ4óRsÜÁ¶íú=ºBÙ >µ‚§ßÕêS«Xíªµ ¸­zëâÅ&Ñ.ÂýÊyŸ.ˆÇT÷ úƒ^’ª]=ß®V r¡Ø'ÙE3 ÓDï6ÉfÝ’Þ˜@k  zMqë—ï¶ „Áhën?Ûzפ20 ôÝŽIßIRŽVÍÑS<2X÷Ä·”ä!/˜±*AÌSÈ<]tê†ÍKz8Ó©Û/N1­Jz–!äy7Rêkj‰6ªwê^³¤#dÎ``¨ó‘tÊQŸUîW•c„^˜‚§$£ZwW¬ávÎ ü°ïȉ-÷Ú¥A¬Î^Çë,¹–vÎâ[" žª -";õÊÆêÍšƒí¢ÜM±; qÒS c•=šä±œl®L§Ê¹ucqÎ*ÔK”—Īçn½¨ÐS Üœ!tDwí\O.•µu’VèÂPVqST`©Ã Ón±G ´ /0&~1®ï¦à¸~Ó.ò©VÝ©¬é,Ä.(æ0ÉÍl^«Xæ…FÃ,$`\=Ón¼2‚Wîr{ृ0Ä'Úª²°xÂHë#W f`jXs)–>ûŠÍH"¦H?¤šÅ=#´uV‹sç;oQøùÀ‘–A0Kû+ =•ÚÕ¨eK€=çµ.Ëj-‹5rå…`4NúH*y+µsö1¯xh•èôm:+Æ6¯ÙÁ’è¾²°v„VÇáÒŠÀX5m]{¤r¹ŠÅ*?¦8ºæõ^:Ûg@UL•{aUÑ-û)QÊTqÎô¹õ\vhÐB@‚ßÀåøÍ}!Øjß=S«ÂËÏûH—/l ›¬ü6.×@ÊS ÆW¼‹W º}Ò\lº6¨%…X!A/J¹‚Ÿv…/( ĺY¼Øm³ËàF¶Ÿª»OM y!ÿ-Fíšõƒ‘ôX3V'q“:Ër¶*’ȼŒ^÷½rÐ ÅsáA[~A‰Gbƒ5.y¯²2;ubM·4¾ :Ckb:½~~íý;c¨­´a Ñ7›ân…Ø ±É!«¬‹‚^Ý42‰ìHoß“úúSԘݖ»rÇW.z6ÙÄ*™ø…û諈”¯$߸èô„·Š/¬Þ[WO[y¸ÿ’Üš’IŘfƒ,Ú %yÂP­œU¿jµÃÌ¥´ÑY>:#ì8¶ØA¾pÿ7¶xÐÔ&9‹R—´ÁŒ2 rœœ·W..²R‹6C[,|v,L( ùqÏD7ASyúÙÁ ªO;ÍŒ`F‹¯n½òcüþ:6õŠò€ <åGt_iÇ`‹ Jrî_P²¹îßÚÌ,Ûª ežR^Ü™ Ad¼” ž>”œ(êüº¾ ä±?@õN‘ýJÙÂOýxáý—‡a,3€ß¾¾þöXùJv}ÆÞ ²ÀR>ÓµÂ!”‚Ë&O)¿9· È;¦hÂñè^½¶TgݹHÌI~òp çå œÍË6zìþ̓ð‚ßDãÅÏ|©ÐÃçÛãµ`)zf½v­ûnøv¿õã\!…”ǼæÖ4vHÑÖ¿ ÅîUþço*„„ _wáòàyˆcʬûîƒD‰bS²óe—lÅ›,E»“hDü“<…2¿§qS”S·é¢©í s],Kîï}ó\çu‡Ü,œV ýžPVŸÑ¶²»Vøs‹Oåi@¿û yªÐÈ ÆušÞ øŠö`Yl¡1Qœx™œâÚ;õº  jÑsQoXµ„j)”gjsï{¸‰–qîóŒgù·¸r#à‘ ˆÿ÷:¯Y ý0€!j.woáYÍ;""ù9Y¥2Tj¨÷–(›ÍÝ·$~Z«»ÞÀiZKŒ7'ßÉ ànà—wáçÍï]ÐvMw³™†4üVYLP”K m˹´†½?î’ ü7” ¹±‹ ÿðSLñãÆ5™ó|:ú‡þû?"[Œendstream endobj 54 0 obj 3522 endobj 58 0 obj <> stream xœí\ûsܶþ]ÅýV]Ç¢AH7Ó«qÒ¸vØJÚ×Ó9=-G/˲R÷¯/Àv~<O:Ûí4ÎxÖ<}áÛÅ‚ïg"ËåL¸?=qp¾õð¥™|ØjÏ^~ß×'[ï·ª¬pÿ58}p>Ûݳ/bVgµžío‰¬®+aÚ^ó™V™©ërfò\Cža³`¬^À~Sýâ]õ{'yŸÞðÅ/³G²¸?yê'O½öm±<¥=Uxêͦu ÅŠ7OÆ“”aÓ=NI‚ ûÖ)pá6`CœC2iüï ×9‡3{Ш‹*ª¬RÚ‚â½Ã-ÛÕÞ»­þÙNëïŸCž$¹š´Röãߙ䯽…`=œÀ!ØÌ°×¾^¾¶Þ`¾Ìs¦Ø°Çš®ýåwaøb%Qy«ÞõöôP´ÝÂ&“'7ðó\ 1˜€ª2QKÓꃓ»ÈŠRkáVfc¦¼¨ùI• S«OÂ=”ÂX[é?¢‡¬){zAä `™dø€ì§·ô놵Íé©!6eUð×~ Ò2µÎ„Bn?ƒC7=˜\ÕVM\¥+§F†q¦åTîÙŽ±oÖNÞýkŸæ¹ SD¹ÑC6B01ß`w^XŠ:çå?õc:º%A¯ôô2%«²¥m›Ë íY¿–³Fae.­õÂäqç·Oœ'°ßbÖyétÉ‹­ð7«Yv§ÓuÅ8ÐöUØÿù²ßBy²µ²ÁN¨-d,'[9ë‹ðÊK.ƒBfL°h¹ÞþÆh=ƒÈ¹:—D>¢ö¬¢ÉÿÑu +¡»Q ]j>ù£Lµ•{zÀŠz`ý²y2¹9mÙ‘ZdZv7‘­Æ´jâøzä©Ûvˆ\kfMôZÂóãý¾ï^v³ Ôš)Þ£–,,ë=Š*Ç\±È¾KM€˜&9Œ§”§*Oí§~÷­+¸Y±p~nÉ’1FÃhAgÿØ&'ÂjH²¿$’%už³‘ç«bg’mŸŠZE¶$Ñ k¢(Tzè)¶º ’\3oúÀé¢{Áãû’AÄúaîo]Ö›AOܬˆõOàJÖ4•{àì·÷¨Ý(¢'OQnz«|-×)KðU(,Nƒ¯ÁÖ1üî·Çïïæ;*ÓÊAFÚª0jà¡p§d Áø‡µ§„ßé„öØá]«FËû—%m„'ýö]@Ô@HßÖ¢‰¼Î”ôWÚðr çt(¢‚ú|ѽŒÑN]"JBpc'icŽ™f/aL½BhÂð-®Z$]Yô,F6 &fsesa¨"Ö_»ekÎÑ›µØétµÏlïLÝ›>—mÍëí»*@ñà‘‰g¡•{§úÊÛÙ7Žý¥hÝSo†D1Aho¯P& {á;bz¸ à }*ò¨ ûWqt}8Gøþ6Œ}›PøÈGÆ3>5"ú×Y§A(θùk,|BqüÀHX°²Z.á²ã–,ŠÌì€ÇVEKkàyÒ'ŒÉLT÷R—%Ÿ,‹r/ádïÀÛn\²§ ËÇ#Öúp9îæ-æçØ€£î{(€Í»oJé\Ò Z±Ô²2Q@¹2ƒâHc,mZ7ÛiùÙ×à\¿‡€H9uç ¹Bn¸¥ ­àß*»ë"¥WlX&ÅÅõ qy &$Æù¤ˆÚîSÖN|ˆ“ qºˆ-çjŽŒò*`“Ýó…‘Ÿ@>.:P 3£ëÞ…ôÔd‘œ³€ù&²K å,°1"L3FYd¦Ò#™¾KÈ·dÂ8SX³ëް"¸^²:ƒÔç&–GCúl){ {éfX‹GsÉ]GF»Ö ’}nØT6ˆi²›Õk HÛ¦ƒ|iiA:Ë22t^ª”™6Eà¼P4ƒ¹ÙU”Ž‹<îiá_¢ÑÙÏ8‹ÚX7:(£†˜ìoÆSé ÔZ-„M± ´‘)Ü,®¯)%7¨x—èœG³K¥Ã]9Ý¡û[ˆøïï©c `¦‡.°x—e±›:ïI9^åÔ3%XËñ‚µŠ •¨²B·‚6j™ÎµQÎJ­Á–µå’}l¥Ý¼v´ÒYÕD%¾%«dtÅhÞþñÜÚ[%¤tÑz?ÆX‡¼“ºGY-v}w3êîùÜFuµl’ÿMS»Ó×n«±&^é*·ì¶Ú Ytž)/sçÔ©Û™mPÕž(Ú¾”0µîvˆ¶%o!´®éâ€Ñm; IÒa1×HõfQ©V2Ï­l-Ä‘¥á©vøø™~Cþ4—u&s öî¨%ôr‘Æ…n+Ò{gé±I~ˆ^bæ~1GûF_‡±²Œ <Ê{"?¢ ‘íÅSŽkÇ’BŽR‡žÓ³#‰·` uýÄÍá2‚1Žª,D÷8Êo˃É4¹•KÎ$†Óü°ø‘ó"Æ0ðÆ‚¾^ïø²÷Gø‚¢RèJ™r°×3 4¦ƒ;ËÏi±¯e+d,l6:]))–.<+\À§ïz$´Ôj†§ N¹„-EeDÆÖÃîªòêœepÈäY^…±¹ò7%q{/µ$CÀ†J(ãZŠ{¦äqXòïì:…C°c 1"Cu#.ɸöìSí T¦Á:ˆ¡DuüÔV` [â \"6„lŸ}RÈz£^µðÂz…I¡BïÉò-Кç T'ò ¬iÒ÷ã¢6Õ}~¹[ù: æ Ÿ]P>öÜ?< Ò½à#?ø·N=õïÕ{bµb¬^-Y¢‡kÐüÙÖàйÉ'GL Â-*ëz&º‡E”0=î ƒ¥á™ã=v~ÿ ž%ËñUƒµÊσ1ÓÙ9í>‘gàµÄ éwÖ«íïïQ_Œo¸*ü2kqçÑŽSýú:ËT= „ðûgÿȼƌé#ììrgÕE$Ü—¡HW¬G(Soáé1á’âd®<ÇeÍ ¾@­ÿ厌ѩ·>¯ömœ‰H¹†7‹ܼ“ç¸ëNô½Áf䱡€.Þûzpëá_DâòM|…o…»xþ)¯bµc¬†ék<‡3þZ÷¡I5Wƒð1.¤Šê‹Úò©eÈì4™²\f¦B Çø°ËüAbå FvølriΑW4%?R±« AÊ0•B *®<ɪÃjÞÀ?ÝõGÍN±Â3–¸R<:F§ô(kˆƒßdRàˆÜåJº8‚rßô#e“‘ö¤#MϦ±#ÍÄQÓްæñüº9«ëB¬’fç^Xßö›ãb%k$G2ƒ$ÅåÇj0Ð †£x²ñe: 2Am"§Ê Ù« ëtìÌ6LŠLi$.ŠUy@¿E5$ð˜u¬Ž,!¸¥—€\“ßôš,c¹JòhLÎðQ¡NU)~äšÌð5®õrŽÔ>Èèã´(²íÁ“íUï•Ð=zB:èŠ!¢Á¯O=EýQì<¼Ô=zY›‘S.›&¡á߈,aƒ)Ÿ9`Ðá ’è"íê4Øœ èçσ1Z‘¬úÅD‚# ’oa[Äüå_™ÊûàC3èã{K~ý’V aƒÿ+X.ˆ¯Õ °H6`á9ê…òÙ™Á—á¹ >§ܯ?ZG"i{Xžü¼²Ù€=¬¹êÓª¶ÛU‘àžL°‘u„¥Y@ÃGPhÎGûm^»9R+˜,DÅ5ŠOÝ0ì} Yë—±X€gNªÆ×Jš˜Ç8ÇmqýßÈù<‹9Ú"‰¼µÓ]’Æè°b¥ÏÜ€ª0,Ÿ¡ÂäX|³n93ªÍÆ¡ýE*èÊùSTÅ‘LkðàUí°b&j:’J‚’Ã_T`SdßœòmÀÏÖ=R߿ߍ¯‡>íJ§+Û§ŒŠÄÚzè> °" Ê&´ +§Ýó0âçœ ÄÐ{,X´ØÕSU9sSäÏ|®ª\·Al÷JÚ÷­—TeVi¡ÂbŸÑØwZŽ`¬Ö‘Åá¸JiM¸Be!ÊRŽ'*»&T—\KE"û…uÛe•¤nE×@maWÁ\2.Y‡¶/ŒN©¤EùÞ5iÓ5¯ŸM¨dJ4пÖ8XSÆ/̺i. ùiš ~äÀrÝXh”Æ•)•¾\ñ ó|¬d’”Ÿe=#Ïå¶@ù=W¸pº‚qÁ v¥¬oŠpXò ƒIWqMŠréeUËU×%÷Àêvpw$q°ªÅ5P_“·"[ȪWštѬA ¼ê¬¨ò^±³noéºbƇôÐ7:Óª4iÇ•¼˜áØ!ÀX‘mÓ¯¼`Õû†8¢Iºl|©_zÛv< >•pAÁ~Ú‚¯2S¥GëÁIŠ5XÄ1WU˜‘«¶©c’áÉFÝm@+jwŒ0Zd§9K1o|Ä‚6î‰;¸’:ÏIC]¸ðå·ƒqº—^ä .Óm|-³²o· ±ÝL­÷’þeÍ¡aÙ×ù&y2?ŵ=èöëðd+#·¥Ÿ£›>Ç<Žüs8¯)gçýw­«)ø| sâ> stream xœí\[wܶ~ׯطî¶YšAL›œãkÜ4qR[›“ôA—ÕZdÙ’l7ýõ@3 ?^VRÜ´'qŽ=‹ë`æ›Á`ˆ·‹<r‘»?8:ß»÷\/¶W{¾xñü‹–¸Üî½Ý3Yáþóœ>:_<Ø· …XÔY]-öOöò¬®M®›^Å¢R™®ër¡Eži[á|o¹XíÿsOˆ¬”‹ý¯ööÿÃrµU¦E%—¯V¶žJ/7‘ZXª(«*/–ÇàçÛZXZÕ˃Xø.Rg+;QÔjyMý°.Vk幨ªå…oTÈ\/_Éú?}m}aQš¢ÊW½\­K×— Sñ¥×¨<ÖU½²…ufyªø¼¿´ó^¾XY^ÕJ-DŠÕùq¹²kÓ¹0Ë—«µ*3SåjYÒx?®hƬÍævÕ0J5ç$[gÃiây³M‘OÇTó¸&òÙêû_îIi¥ÉTVö÷1I×h¹#”¨Ü‹"ÓÆÔËÊV‰R¯ÑÍqÆã"ømÕ¢¬[Iô͇–ØÐî®Cx±Z×ËÇn[u^‹åWTIðú›­“ÍX3&³l%竵T¶r)¸¤¾_•NÈr=gÑ‘|䂘JÝž6L­ò’wÛlU©¤eZl´åLpÒ²â²nÁÅÉL¢ÝBZá°[Wèvnn? ;®»Bâá°™+f+º”¬iûÏ+¡d&"ðÁžIcT‰½‚›ËæôŠx°u%U]¡‘U.ZåèÊÛ.²)¡0æ4pgÆ¿ivC ‰š•á¤UËFYgRÈ92…@—qéˆë·‡˜BY{&Äôí‚«zcœa°‘5=–ÍXÞ{^Ô a¨È<Öªc+e2eœyüÁm_žU¢ÒË3«ÈYYçV‘ «¼µªœî®+UeFYÖSMÞªbtÎhÍè+™U&—ryÆê°L:ñó°Š`Xéýéî:Íʪ®—õÀ ¾;mͱÝYH¯—â_[Ê2Yh6.«ø0^8øÌ•t"¨k7N­ó¼*Ývª¬E®XÕ3ê©%+'Daþ¬ÍU,ü„Ú°‰Œ2À—Ö“ÛðЋVaŠLYß«Þoí•”Öä=Ü+ÒZ„Q®¸ +j‘'dÃcÅ24ò^J#øÓnae5ßz ^îoâ‚ö̘û™¡?vþFÉŽRQò®R”iþÚ¤Ñüu¯»§ÕÌ‹ì¹n}dâf"îØ”aøf¢ÃLsYuÖ:ê8;л€Ã²fØîsgªciaRyÞ@æCÎóoö®ÙB߬Ѡ¾/ {Ù`'°ÛKèuN¸Ël†?¡ªUìàþ û6§ãk2²§Z­X Íì9³òÌe`¦]?Ä9²¾Ùß•û]YÈέ§Lá0{xRTe• Ú·kª{źPAPz"å\E¢˜ø7 U›ÂɿԻjí}GU9? L|Gò {.†+|OàB0„¨WT‘ñæ":”¯ÇÑ®U÷¸,Û¯¡WHêŽÏ#ÈýÚ–J· ÒnP8KæŸpû¡S,ó[‘c»»ßêÀIÑï‰Ä#r‰Ç^#näϺkÈilù©,¾´;ì%þ^¯šSãDÍ׉Úaí°ŠÌÄÚþÁ•µa¸r«3\â cÆ3n e_ÀºŒ‰éC†gÌô9@è-ÑC‹=?Gp`ä÷Q[o@Èf“£ ÇV*Ü€ª‘ :S’ŽB ¬e+7µ,uƒ”n¢¹ Ç7¥Ç0äv§F¦rHY؃ÁXBªPœåNO ½Ñü~OÚêTKƒ!#cÌ ñqwl=>4Õ8ʲY¥Ê'3† ¿ Èš»åX!aîŸy¤m¡;]ê8Í}Ÿ“Î/³†-2±^{]9Kƒ"°M›A¶%pÐn+w)z®b¡>cáá Ù-2j-zaY×xËp!ö ]“+TxØÅGWˆÂûØ8ò‹ƒ-Ÿ®:¡ûÏï\<$ÇB‘g&ÜØùö¤™)·¼u‘•ÚñA¤Î#µi)YùXHîblš7g$µºˆÔq¯'Êk©«¤÷ÐO[*jwêˆä¿‰ÜRÒ¤J'³¡Â‘çÜùšÈk"¯øŽŸ–çÒ³Ï3¶1;=^þ=®ìI¤ž'kÒ]ÎÈ.À^œõ¨Ñ½hç/ c¡ÍcÑ+" 3^Cn¹ !ã.§úe»{‡¸„CàcCÂI²fǃ[ÚÙŇ‘ãû‘úêW´‹§×SÌdä;"ßÀ°³;d<Òž'=n«å¿"¾ÿ2Ú“lÌö{LäÏs…˜˜ù J¿1³Ã̤&ÏLON ‰4-į̀âo¦úñ·fÒâ ;†Œýû½œj&‰¼Gdë2ã‚%…•²e^Ïl·hg_FJ|©¢¨ué±üŠÓU‘Õvê¤Â¡Ïº(dR-\ÿ”² BJ Ù°öï}J•X1ìíþÊUøhLҹ½mã$)‹ÄšAáòeŒH惓oB¿¥O¤˜›2RT:+Ú”‘o,§]Œ5/YDtèþ]²Æ°åSв~o?e ÂÒí ‚O&´²Ë¿0iÄ¡áýŒ ä²hm›SkvÓptö'ʺ‡CápV¦Ùôrø…[%dˆB»(:eS°(>ž ݳœ–$x8z1«3)bIÿ.Ó£ÝE¸íì]›“°ôs'bÐõU'¨­ ò“Ý,ÙUÎpd,Ùe/Y]ÆIvUuorƒàØI¾AÌÁ™ƒ·¡¯3”ZƒY:tSµ–E¦d­CŠmR±üEMš¯ît‡/jZ¡Á¹t-ò.Â]ºµ‰¿ÂÞSÊ&¨b‹¦ë ¡lž‰Ü†w©p @–F 3è:ðu¼ÍÊYèAåô,‡æ(7Ža„‹ ýV`,Å™ ”¨Š %÷ Ékó‚ƒ ã‹Ö+{Þñ÷"Œ‡g~0UXùîJz7õl`SŒÉÕɧè:oopâSüÕ“ãº,ç䪣ŲÎiÃÓË>¬j—ªº9ß„!,°‹!N^0†PËHè*ØïOÆš ô;»ãkU‰åb"àw äo`&š‡ æhÄlÚmD Ößæo= É3uâ»u“QœK’xoÂBÊˤ‘¡´ƒX¯ÉËvž%ðSš|v³¹ŒœLÑŰwŒl&Ó¶¶\è·:~Ý\°)¿YôEÝ ¿o"Í–±nî¸1ãvƃFFä’L˜ŒL¤•Çc±pÚLÇžâÀªÐåB¼Š5óJÛÞékIÆNŒØÔ²} —å.nFë×ÄN¦ØÓî¤0`÷§ë$Ýð<¶®nã,¡ÅnA±_PÜá)‘ßù<þ4€´›(í!Òš¤Ïaˆ|LäwD>ãÓ©òܱ­w¯«Ìº^0ªçmq‘“ƺ—(´–ÆNÈ¥RzSºôŸ ÏI É;*„„’Ä¡Ï#E¡ë‘z©o"õ´ ÀoŸƒz@ˆï»H=ëæctcÁü~Þ’mò¬ø¨WQ-?‹EóîÈÉæÛí’g>؆nÈ.” ;ßÝ™Ì_Ý%€‚ãö'dÅ"GæÎg2#SjÓG]¸ ]mHâœvïßa9ˆ0‚f L$?ƒs`!`–ßpÎç0„QBZªÔ£j;m>.H”ìƒz¿4qø("Õƒ!àú¿@åI0V7ÀâfÞ‹'>›äßúÇ "L)…S™š)ê¸ó …ßEîæ#ãÀ&ö/á7ül) âÄ“&ëw;5›^޽ÿJMý²ïì4œûç¨=`ÊÌTñ ¹©;&ÏãO\ ¤RŽïCçÚ9'€Y‡Â䋾¢°§ÎZ%Ÿ…ØÎ¬:½É%Æm³Œ Ÿ´'IEМ8L¦ÉþÝQ-lq©ë¦ÓƲ­U%²ªvæÎJxï€JÁ.øù5)|NgêcÉùyýO(LêšÄš[—íáPƒf*æÀ'‡†¾M˧½-D~°•â]íâÊѼÝSfªˆ®i‚:íW8Áí8?|77tQ„ÃÐÈkV–KGn¦11yàT*úÛÂonèi†É;vU|+¬Îìµ! Ç÷®Hè&¿W=éj,½æà¾ÐìݵvCj¬WV—‘é«\!~PÕ÷Ë#q…8»†à×ÿ0 >(üNjÇ ½JߢS56±ð!öQï™wnÁ=Ïßåõ‡Góf¶¥|a…剭c~åaò• -¬€cál:làµhWdFKðrÉ8[8œül3}îÂÍÕBŠðün¾`{›õøò>i‚cí£[ÍÊéâky›þP¸(ys©pû×Ó€(´o9òÅÒ‘§e(š3ißñG’lJl«ñý)Ø´bèÑ’ÖŒÊ.&qhÀ98S&‘sàãçòX‡Ú§·¤¶Ñ “{¾†t¢Õ|/‘§÷bdWS±…¼Ý‹`øl嘄ï:!—ÇРs^‚…ÁKø[xˆ¡q±6áKx¦X8¾–î~¤%H ¿d¿3“Û}š3 T'>}'c_ÏVM·ñ­j‚'A;š™Æ6và÷aèÖ-I\º&åsÃöM‘‰>™LvÆæ“[vy`ê ¾ ¤É³‡O|2¿ä)zb‰M€½bÁÒ 4'Q\æ>ë+î? ÀÔÙþägŠs¸Û{mªV} ½ lä'wöÀÄ-_>¬Ö—æÙ¼¦èdó {(1I^µ”ÒH—Wm{Va\b5.îuc{i+;k/aý÷Zúd_Á¸ð¥ÉªR ÏóØÃQÛ³Q¦ÍWnè…kX—¾òÂ',«J{OØ'/unÚtí¦:/Ý(½œŸÓ\»‹”† ôöÒ˜;¿s”¶-œ~” #öï±ì yAH}oxÑE©ëþÃhۃ2±.³ÂŒü~%J§mÝp4C†ÅgJ£Ö¢Ñ­%4èIZ`qV–¼yˆë<æäó†ðú&ÏXȬd·B†xóy û?nzÝ`Xíñç-\x9ùE¢½w“4æCCaðX'4³Ì˜ä¹æÆÏ6™ÉEjÇSNßÅ3Î+O\®ùñr´Üôr{¡âˆ{ÐQKN¦È«á—;ñ÷[ v}vî­”Ó9uÕ(³CŸ(½`„­õäaf ÁwF¶fMÖòå%½Ê8¡w$‰;(>˜Ï7tdè&]÷ÈÛ Onpé…ŸTëdô›}JÚ¸÷÷þjÿü†êendstream endobj 64 0 obj 4210 endobj 68 0 obj <> stream xœÍ\msܶþ®_q“/½ëD ‚ ài;cóâN&v•ëôCÒ²N–ÔÊ'Y/qÒ_ßH ð!ygɉâŒgM °»Ø—‹û°( !¥ûÓ'ï¾:jg·þñâèÛŽ¸9;øp`ŠÊýçpúäýâÅš: ±°…Õ‹õ»ƒ²°Ö”M;ªXhU4ÖÖ‹F”EC Þ,«õ¨OU†>ôÎ,Ö߬ÿü½=¬J) ¥–?YÔM¥–_j¨E þ¨M NõK þú¾ Ô«@Åï>Ô‹@}fÕ"5Ÿ?#»‰ [-/"yÉãH¾äe$O#¹ˆä6ø~í´•4Ø¿×?¢”âÈ?<”ñqè¡¶O†ïÉÖÒíF½ü“I-{‘|³:T…VJ›åÕŠv’ªYÞ¬ë¢,…´Ô¡,ªZëÒM­O_£AªÚÒĨ¡¶FмhCŠÊ*7­ÆõÖšö]nãÃ…ïÝe—W‡’ôèšõ¿‹Ÿ<÷ý+Y6|"ï©—*ʦjº9ûlÒ§°÷8’ÛnY¦âÝÚO8mcÓä a9Þ ŒåyÔ”uñhdö•{O–¤ƒý’kAÚ„fÁVÿeœû@z¾m; &YY··^ƒdÕ$Y2¶ëÍÁO¤¹}§ ×É ¿©µZžAŽcé´ *úŸsi Eò66€oxy™ WV…’¶Yþ¶M!TYð'‘]`PäÚI|ˆ5 MpVO¡öÝÂo݈8Œ0/⌯µ’ü¬W'.]ëLžÊ9doºöúâV­ÎÄí¿ Ôq Þ+röňÁK<}.[n7µêžé]Ô‡„ð¸ûÚ­bY´ŸªJÑñÊ›ag}_¶ö¯i[ù/qåûÕ°Öô ÀO)ìÿy7Ð]ÇÍÙíÅô!a2ù%É'0C}›^"KÏ>{ n®Û{Iìü’ý_s––­e‹¶[Ö`q ¹§W­­•º(+ÝÛÚ-ü›ÌI&´S¸úDÛ‘Ð’…ž&E¦šÍñúŠ »3…©8иó¦º1ºb¦úqú7öÍŽàŸixèÂÖûqei6jÂ~ ±¶÷w»)Œ7¼w­–”–âMÕkÉ@¾i0"›†ìuãMµªêBµ–ÚE‚n°h¢¥ØcßBãÞ;Ïì3‘Â:'ÜKD$Œû‹0õ©=š™†D÷‘ÂO¶L&Á.2y ­RxÍÆ( sf$Y}¡»ìð{ÓkåRò¯µ#±S¤¨h,ŒäVh­5¢QLÝë¶3ز† ©)aæ ‘™¥ëº1ÇžõQûøùc¸¼Ûlæ¥Õµâ»`üC–«Ibvv(¬s¹Øö2iïø`û$Õ&úŒ­ƒab±áU®e.Îì ‚&sœú‚¾WÍÙ‚“‰ SuQU61bk∦ñ´ì–Ý£Æb$ WCK$~IË•BkL뢮‚—gca˜k6x€Á"ŒòpæPÆbî0֧ɺ‰ÝÙ‘& ³@\2ñI€‡Î³.mÖ†•=0/8Ý.µÌ!T¡Ò,5ç]\è"+Ûk ÎƳÔÚ•TI„÷€¸­Íhtöl…๧rŽø&P¯õ/ðÝ#Ðî›5zŽx}É t+Ìï\Gò*’¡»º#°nïùÎ"‚>„Øáñ)ˆ |b¯0áM¢#gfg€íº÷(Ac}Ðáî±c=d ±ƒL,$„÷Ãü““€°‰!–1#K–ˆÜÈ­’5wËyö°Kþ:€ÌÜqÒ˜š´ÓY 댵FA¯æ‚7G0c¶&˜1F€FYQwGÞ%;‡Äà9N˜Yƒ/\¡*3Ï1’Ÿ~ì‹9Ñ/轕N@ŸÛÈêˆfC€‰±§ÝNþ|÷˹ sb¥¼ò;ˆ&"Üyc‹j›¢‘²W•$žh?Ú˜¤ Çd9ÖM‹¬S°û¥#ƒ˜½U†4dϘÅ#öa´Á”&Ú¸q¢÷ùëvp˜áŒÏ)ßÈvxÛð´Ã™©à„§Ôðøa0«|é-twÐý]Ï¹Ê Øöt“¼Kå/á‡gBŸ¤›Ã~ø“†AÏ~Õ¥³†ÍÓ’hõ©ݶ;Š‘v«]²F*V>Z•U>VVF¹ÊÇŸ–ruXZèF9ôüPµ-Kµ¬Wd]ØáØ«\sÌÒx•mêqUéÌEpeEÏZJVüõ1þýJ¶ÑµO¶È¦m\õBl° #,âL^ÑCK’¶Ë¯âÃ×4½ª,k'óþõË•,´q¹\7f­­sºqü‹8À {̧pÛÑuUŽCeRJjªœ¡uCžˆ3”X#Cɤ ¡¤ã(~.V‡¶04ŠhÍ~©h#º§¡u?¢Q&ŒâhËèÆ»iÑh»|¾Ägç¦)ø#ƒ-ëÒ.KÖ–Ó|ì…›Im í$wÐXèÒJá} ÍHI–¶Ô%›ÿi7c—¿âÁŽâ`§¬ÅY”¤Æ·¬Å×8 =J“ô¼qc“~µŸ ]_1zŸ„èJSåÃ¥ŽQÇ¬É £OÓµ…ý¼Œ}Û¢tCÚ¸è–Oj³üyÅ&ÃGùèÅgÚ(Íi]UQücd9~¾áPæ=H‘|„ÿ‰GØ8ßš«pýo̠휎èw'›ÌËSxÔgC³Å›PƒˆLSEºKùNÕ¼ø‡³:›IÓqùÆ,ÈÕV=$ŒB îÞ§.cPëÆdM´soÓG'…±Ù¸-®á"ž$«âø5—ªd¨ë¢Î2ÔTü3>\äJ›tEÓÅòÛ©j…Ì0bΦJÙÛY«Q@avß0™{»3@€È $…n“œÐyHìÚÙ䔤ä |ÔZyyÎláQĪgƬ¸ïì¹<ê²0úÁäÑ`ëz¤N£Ro¡O ÅöwÃ+›-ÎK4 Kx=ÉwQÉ>UPwÔ]A“»òªå[YæU(¼â“îæ\0·ÐÏÂÛNÅ68| 5íõ»‡«eÀ7o\ã­<ôïY%Ÿßë¬ÁX˜•!§Ù©úß1:–ôæÈ¬‰dàë>¥˜ ákw¿,„6à0—Hu‡Ûª©$<ßk_ ¶ø ?­’5âmšE”Ý`ÏÀni×>W³õèë x{Ú¡¢¬XômŠQq,FÞ@¼éãZ´ÐCÂL…‘°l‹±´¯#ù:’ß<6ð¸+÷Àx‘û/ÁÛ¾G+a(3õ¶sd¼‡Ld$Cõ~{L‰°nz¼€s˜=å},IOß,5A 9ùÖ¥xºÙ·÷eì8çš“¡/Žu&ªÒvRdc:À˜B]R‡²›X±øPë•.K7•lÉtSL Ó\çñ ÿX2VF™’0Ze+Ë4–‘k¨èo`ƒ£QF(S”V6;3;ïïž¼Šm_„4ã{þ Z´Ùu&¿Ó¶Ì*XRMîóßVÒR$i׎Uã>€þiZ×_¸ƒÝúuSB®Œ&ÞÒ4® E2\\èwe<ÞjpIÒ=ŒQöþøÐà²á x”jö¶|—âÎZ»él7¿ʬ£%ûac©ð¼˜}aï L“â½x¬ÉØüÀ“$3öa6qÛ–jxí}¤.íßdSßDòX"NZx~ jTXÆ€ûcjÓÛçÒÒ°4çJ5lÏ.QyÐ1œ,vÛ »èDþ)ÙE<0ªõ ¼•ƒCôôŠÀ_yÔÛ?,ÃÃ)Ê4£Ù 0[=2Ç~8ÇéŠxûâ´cÞ™ýª ;NG¿‹ÒG¤£‹D‚¨KËïjÀE‚Ý1‰k;Ô1hw÷M¾²‘û0q…¬°¬p¬`foU 6Q3€Æ_7`SŒÑ£Ã„iA»Z†UAdû„•w@2PˆÙMÓ"D½±€%†Ì<ߤ˜Î¥ŠQjBƒU®O„ÌøÐë Z¾ÜÙna‡Äå¢û°— BB^f,5ÛÝ×ß>ñÊG’²3€Z„yá$â:Øìª&ßlÄ•‰¼ÒruCª©#&1Xk/;TÀ²utÍf˜†â,48f¯ªî$x”a,HÈŒP[üú¿`ýg `vÔ?ŽŒEáG,tƒù:FÊŸ"‚õi@9Y Ë¦_crrÜ4Ï·ÎC ótbût,fÌôa3ÎØûÁ g~GãëõÁ?èÏÿ¡× endstream endobj 69 0 obj 3767 endobj 73 0 obj <> stream xœí[K“Û6¾ëWð¶R*C H¹mvÔl%eg¢=%[©‰¤yl4’âÑÄN~}| ›äGQšÄU.[Z`£Ño4ä¯Q+%þ¯“W6º}œ”ÃÑÕ×5ðævò뤈Sÿ¯ðâ!úrNÓ$r±Ë£ùÍ$‰+[QUQnbë\Y¥bSDó‡ÉÓhv‘&š~›é†À8³©™^è!@«Ò¹Ÿ“'6VÆÊé| ³îôÇñ”‚rétËàW nÜ3øØ€š–øßü?^“ðßLè÷?ü/ãÌ?kéâËÀíôU‹o¥U[= "Ì¢Am”‹–ˆkfððÚ’ bp §íᨘvqWP¡bÚŽÁ7Ð$ Hì‚+ EÏ$+ü+hq oN°‚±Â²÷T–õÄÅž¸‚êàT=öì÷Âü?3¸Ó„ê‘ÿÕÓ·™~ýAi>‚*\A]ˆÕ~?Ö÷Xœ NɾZ„»7¼ räâø½žÅÐe€¾méú)v%z€"ãÐêÙBE„=¤ , ̹†£‚ß[¸h ÁÇ1&·A𻃠N;G'ôg‚‚­(ͨýFù¾8lë‚à[©ÀÅæÁ)‹¹CèÙ¤cŠ×!|^èûò[féö¬B™X;‰ºƒº}„šÙÃQ1m3¦[Œ èÞ`(ALHñé Ç{ÈÑy³ŸÍôeKó‡krf™1{aࢠËE\e^2(ª©W'XfÔ}p­|ŠfÎ÷^ý¥Ö&v¥Ïpds´ÿ<ŠñÚ|c¬¬ÁG˜-” ï¯Ø§xɨª·`´Ñï‹+¥ÂZi&GgER²Êc«rMù„Ò¤Òy«E¥Yž'©Ä¤ÚG“õ¬UTò4¨ ]ò¤o Lr§3Û¢EÜfq’(²IµVªKåBïgƃ®4„%„©_P´Ë d’2ÍŠÖ„…ĤoJ$«ŒƒºÉ‹Š ,˜¸cš‚|Äô6<úÄàƒwµ8±©¥¢­C!!WÅmUp,ðbëR$o†¼H´€NÃü4Q$˜w­)íª"J¢ùròƒ\µ£Üš€Xj'Á øµÇM3G•ºò=ɤ†®yNŸ+¿” zÍDwPóXqÂ`£ð4AWP¸š­Eâ%ˆû¶ ™Üγ~ôr–’¿$Nù‘ǹɬóÇ7C ÷QÁö=¶r µ„ËV¦y–w¥¦°¯)ØB",+—I“ئq™˜iE .yô†G£™¶6vÎz’$aœ':ò3]æèÜ„$Ó8Zãˆ~LX®§˜&—xÓäyß;ó{Ç?ñ»ëª°›œ–Оog.¶m2¾zjÓóH ýÔ[˜¬N¸%®“/ÈuÝ Wþî9GWŸ{&µ…Bþ1Ëüºƒ!ŠSËââuïꌕǙ²ûáh&WVÎÛ¬µîTyKÞŽ¾púìý#ˆ(©C¬û(clă>‡yhC «î¸•ëç*+û8À±%D2¹·›H2È_CÝß2BGè]Ë\4–æ¤@OæÿŸÔ?«R¦Ì•¹£âMpÝs;Ï–ŠëΛe–a¬Ôf&¦[¨ÿá ël„òÑ Œ4±þ:PÝòªœcJz_ÔD²¦2,kÀÖý Q)¾ 5òm{cu‹+”Õ+ô>Îà¤ùÞï †úT#÷;Óð›a´v žîÁÓE€îÄæ_¶ |ä 7sqEàŽÞÒõ†uírüéo¤ï1 &#JkŽvkNiîâ)jçŒ_2²£°[.¥\=W½OyŒ/[×=gìå˜Ç‡’ijœñymëßtF(d¿ ,½Œ7…zðž¹÷Õ<¼;{£ìD¹HéÖ«EÚy•Ã$t’4Õ«zv‘ĹֺÐT œ*e´™fã„ï₨¨ªjO 9†O»¡X˜"Pñ°°-Ï Êænú϶¨Žtþ²„j#%·FÀMèEž“ŒgvúošIçX§•ïªF—gÖ Z þW5ÿ…ó~ƒˆ]1±•À¸ äî+r‰veÑ0övtöIŠÖÌ7ž¶SiµL˜z)àM ~€ÁinÒ¢¬TkE] ”…€WmÙÂB?Nyîër)“[ªÌç9úãLp#ɼ-íWø ü¾t»45qš«æt(¥¸«HמHH7)Ü$Bò(5°+…Ër¥u'ÉÔBç&o¨$UÏ* ¯ÄpÓ9qÕ!©°±±ŠIY%²ä‰ +­S«ÄXØ?x‹Ôë*hï#ïÀ1k¦Œ6ºÓ´c>ù—á;xróþÙ0ò:1…¼¯CÎ;'ìý52!ñé¿> ¢ö81âg¯GP¶Üp³æZކi¢Û¿pW%šQr7Mí~zÓ QYÞ„ 7„œ1ì¹€{‡T¶ÝneòMÙ.ý>Øg¨díÝ@tÄ>õ¢ž†ýSt¦x±ÞDG]¸õ÷]MÐ&Ú»š8 ä¡»‰Òé„ÿ²^‰°©ÆÄEù.h·«÷®kˆº¿ÜnS®*\Ù)2îÜÀN/ÂéX#[a+ˆ†A¬Ø¡DAE#eܳ"Ò î·Â ­„ÝéO†Û¯ÏáÔŸ¿¾ÚѨãªM» *ظª—V>÷{æm×Q8Ì›[U†ME˜c®aû8Oqû8 >1¯"ôVÜ7Åq!.Pp»»…À]Àd‹“ ž†Säh›]»Ào¡§/ö™Í ëžÒ¬nm×#« b¸oìM|°oüñ–.Røæj n½&Î,Â'ßw/˜Dò?ò•€Þ†Œò  %p¿˜… µáç/ ˜…óîüEÁ§ðEÁ§ü%ÁáO)ÎtÀ‘ `Ÿß[4Þè-Ãùæ ž_a~ŸÛÍù棽D×_~…YV¦ç·~Îoý|oý lEç·ŽI#ç.†wÎó ±7¾œO¾£¿?£·þendstream endobj 74 0 obj 2253 endobj 78 0 obj <> stream xœí[[w·~ç¯Ø·’=æzì ÉSå:©['qmúÁ§Éé¡DRRCIŒIYI}½`»ß$}©ÛØ~aæö˜‰’XÈ(±ÿ[ââfôøe]nGUsôòÛ†x{9úeTÆÊþ«8}qÍÌ@ ë<š­FI¬u™”õwåi\hEERF³›Ñ?Ær2Mâ\JYÊqli%D*Óq6Ю&S—†…G“©IËr,Xï–c™–Ž‹¥££Ÿ6I\–¹²ÝDg¦9Î-ÅøÞ’¦¯¯l](”½düÞÚj!Uæ5/>Ë̵.ÁJ=^ש¥ÇbSwÎÓÜNbšUžªr|ͺß:~»ýËS†ŒØÀgÇ2qÓ¯*ýÉ,Ñã9ërÁh>”Oúã˜Æ6šZ1¥¼©æOóBêñ¶ÎåaòÓì¯#%u,´0^6[¿âJáR\uÍPõà>S øÂ3¡ëѱakÚë° 52Ð~’•ªšŠÂº¬þñK!"í…\!;!——*.ò:êìšó¸¹4 I !ÓÂLÑR‘¡T–牲f,â$y>¾sßoµEŸ×3¥P:Å,w6nMsª›ïJ&þ û]e•ºÜw#tfg2žpíºÞ¶]-W7ÁŽb+¸¢s",BÇFq©¿Z4Œñe³12"!ï©õ–Èk«9•™äSX}Ž¿¢¿ÙB~¦å-Iìß&¢ˆEšdãsâ}åck^6ƒ GëLRªXm í]½ækg"'ÔÓ†m;í#(Ø>#×Þv’‘-¸´ÍžŽM±ƒ û¤ÐYÆ%žC5a=3ï©[XtRiîs‡ÈÆ esë•X¶*‚‘=äh¿rœ¤0ã ¸¥ò,w¾«ôé¾kú&¹¶)Óˆ, ƒv…}š*'JD–Z/¡¦V!sG-Ž1aW¤/ÆénbN7Eid¹™LejÅ*ˆ±„†´–æ"3 n;ŸÖ˜×^€ŽÓ¼<\ë×Ð{Ñõ=Ãs! oÇöa¢Rß4ÜëÑ&³m.‹S•ù~Ú5ÝÔ1c[ ž‹,°Ç–¢˜ÖÕºL&xà®Ýwf¼ v 1†½ÉupÎAóbÛ=˜ÕÚÈ.13Ga\É41±:N>=ØD2z°É€·^CQú>“Úc’JÁ²ú´Ûö>£ã4‘ÛvDö|¦jóµx€•¥q©Š °2Y3&H¢aàä€T àB§°ãá½çA¸1Š’Bu0Ý}›aÎ7Çá=Õœu‹P†Å‚Wû–m÷è6°GϬ»å©_¦á7‘ÅÕ5—Á™ÕNh®“´uBÈjàЂš… >µ°y™ƒ ǼŸ§†ÐÕ­ Cײkð.‡TÙ ò½°¸ÙÒSsû´ølÈ$QÞÞ»€j`²ã#ûk‚O›eH­éÀ)¸ )_L”‰«D‹~× PX׳N×ê;C¼»Úo”ŒE^¶~ÃÀq-I°3samDRö"Z_2 ^Iï ¯Ì—Ù¿ÌíS%îöis)ÑìùhöG‹’S•HsK­^UœfÊ;G-µl(Y->O쉥ºd4ßoµqÔ½£vŽŠuá¨+GÍõ´]~K0b ×ÊDm´™Œ{"wDnB""/ˆ¼"rNä[Øzù.á°mKÊÆÐÂ& ¬ ÍßÕ§éžA_;…<ñ"Jãç²Jbõ þ°W±ÈxóžÚùX4‚f#§y·wÞž¹­R\)]¡lf准±=çÀ<ŒM|Kä»Ð°,àl;8ëÀÜvSgß s%ºE˜ÍGÒÈ£®«NÛ‰§ÓÖ‡3祌$3“‹šlõaÜõ ¶Dá×D^ã)´SñjÛàND¤5œƒvºžA;(ÓWNubo©KG]‘n§ÀÐd¶þ¦ƒz¦Û¿Ñ¬çÃu!i7Éx`£p÷¬¿S\ƒažEqækÿ¥MÈ›˜µÞpfCÊÁšÞBrȇ€KáÌüFöïÉc¿@›†1ìò  @cáOŽ|çÒΪèøK±ö%ög± ø2àt1A'Ñý7WKÞD ÞMƒørLÄcÌ@×jÌ«S i_›°kf&[e^Ÿ^€Ï,óCµž{G­QöޱdU` ˆ óóµ TšPiÓ@Ú•ÚT":HÓWÇ+›åÉu•[h¥`«„ŠùK=iQˆ:ŸT ï'îüÊ=ÓÓÍ­§oSƒm‚Ø¡*Õà2h;ÉlV«ª°º /ëú«å²¹èL0T—s´`].1Átj!Õ† KŽdŠõò³U&/5ÝRÕž[+^W •µi­¼¦ÓQ¦Ì_Ni%+˜4gd0…Ï2¯8ÌDÀU0Z.Na¯ÛG Ž  ³Ÿ–*.ìƒ/¯¼Ú2w£aI˽|`*»ô¡þ"™u½BEÃu¹ú6i“·,»ä «K?‹¼â`£˜1O3Ò¯WeJáÕ« RÃÒNÍ£²<39¡6,å¿L7¬†Ã}Þs¡N‘ÝóínqÔÂ",1ùª‹õÆ’7åöö§ŽšSà Po@¿?9ê5ñƒ£ž;ê~¦›8޲ȚôXlÁìæ´(xŸóF<Ó˜wµF•¯­XÉùó±Ð÷ÀŸÊjÿï÷?{wÄ€F6x-ícšÞ9¬[¾ÜS">£©,Ý1„ ŧ¯Kô¸X‚ÊðJšõ»ƒù¢ÙGêrß´m;¥H÷ºÓPm×ñý…2ìtåý÷áœpEå’ìJƒ)ZGg š½xš]ÖXGHN÷ËÈ:Ÿ9┞hì~‹+*ø†¼€Ãv°• Ã7ï%T$†³6ÌÙñYÜÎw>ÿ܃òCQ¸Q9¼î÷«àIQÊ#”cxâ&¥}à¹?ý^¢EcÂiVe¼Yê1ì‹ãgz‰3Øq´2çei#” 8Î7=}§ão?+ÍGP…8ÕÅfûíPß# ’ƒ_Ÿ¿†²ŒÌ WDŽÆÎ-TˉšÇŠÅ¦¹‚êÆ|ùblÁèôþ¯\WPÌ甌Cé1rˆ4¡~>šs‡N°ï¯^|[¿ƒêÅVÁNr È#UðJ½8ßøÔU&qY„r‡û~ÈëWnAN˜¥õ»? ±ŸYÚ,ø¼c~ó·51ÿwLmáôÊå*uÌ;©Ã 4L1”ÜìÕ½lý2Yåy1Þ“%Ï,£÷+Dõß¾ôvòI} ˜{ÉGÏqyáŠr}]¸lCîœè] zD4_wà+zÉBn°ð´w(²âë<Þ~ñë÷÷¸!3¼õŸIéM.H&%÷îcn!8AŒ~pÔïKúG6A`R¿Q§YïÿWÎxâù¢µÚÓÙèïæÿÄœ¹%endstream endobj 79 0 obj 2745 endobj 83 0 obj <> stream xœÕ]Y›Ü4}ï_QoS5elÙ–íÙ¾/!Hzž`:½%C§»é%døõ#y‘ޤ£R¹º ð \Z®¤«{Ï]¤þy‘g…Xäúß©püþàóWÍâüö ÿ¼xõåX¸9?øù ÍJýOÿËÇïUÃnQˆ¬“‹Ã³ƒ<ëº6o‡ß‹…¬²¦ëêE“·‹Ã÷?,ÅjgRÑŠe¦ËeQT¢ZÖ‘ïÕjÝe­ê¢X.Vë¢È«L´ËjO=¶U»ì ,ÝrÑeE#»å£U!³®ª:Ý_—Õm[5ËÇê÷Læ(–÷º¨‰å™î kÊ.oò)t|£ûè QÖÎçÓ‰&ºÍZY×0òí8©¶[þwU4YWª™^ªšyVKÕÕ±ýý­éé¦ï¿”UÙ.¯ÌשU!à£Ã-µ–5X>雵­ZVÛì½.Õld_T¿7j¬±³¢+ûÁ†ªï ¯cãÊw°fXÿÊ’|™$ôùê߇ÿ<(¥"¦gÕÃÅD¶Ù]ß—¨óÎß³V¸uH’}jÿq mu^¤l¦”ý2*¾Q ¯íÛ‰ºn–?®ÆvU«9@ÏàóWE±èœCÒïȶÌ9œ“Cճ̚B Ź*Ŧ§¦´P¥²–2/]e–KÅWЬ©¤èjš¦Pt™šýR(üÅV8ê;-EÞèMpº>±?:µÔaRäWj[+ýµ«–çö÷Ë~˜²nKl}Áýˆ×~ƒJöÛÓ@ÿg–vû±¯ô_˜_.z+1°¢?‰fÔ;ÛÙ%K±k£ÊM­YÔT¸ë+”²–81Åku–ç… ˆª4•ª‚(;l£}5l†ìÚåwz ›¼+F¶ežUm51¾°í¥]´ªÿ*‹ºZ~´cÁG¸9|G ábÌï ¦_9 Ol·¯l¸ÜÙSä묤“¨iJz Ì Ì,` ýû-¥+É @Ì[ÊØ±\;ãºAÞ(±Ìm7¹í&w£i—Vµ>Òy3±¤ÌkœÞb%¥?ºF3Ëzâݼ8Æ #S‚•<iѡ˚¶lj8_ª¢:<ꤷ zN˜:2ý\zý5šö®lpÁ¹¤ºÐCê¡í¥_ÔâzéëÃ6Úù] TëÐI/“2›¬øœ²HH¦Ùë@8 ×–œ•9×…Ì¢¸ ç±I“)¾œÉÚîìKÁ(+‹Á±à¬þÅj]e²ÒKýœ~ývµ–ªX7]ä4ñãó½·„]ÛºP„€¥Ý0³xƒ"¡?IÎýÓ¨ÞJµHz‰ò¢E“dZ˜ PŽh|£A£@gW+…· \'ÂÄ$@{íŠêm„FWl ¼¡‡ªÅÀ öM{žìÙ·ÂÆêäq† A¥O׿,>q4À@•,,i@ï= °)›D…=ÈšÇ?£´ÛFpж•_l‚¾óaÚæºN€ áz+ $Ð !”2‚1S¢§v¬›àø1C_/|tTB]„ûhÏßG.)`®ìé{Çúb³œ2 `^°\G$Ñ'f~KÁå8ɲk´¤Ÿæ u¯WL,³Uw] œ™£æŽQÖ]^OèÞ݆J[Ae…Ò*³ÅIœUrìv]U"«›²Sm;tž4“DÓæW™óKÑ—7m3øhµ.uÇ•î-(Ù_Ÿ˜ÒW+™çUWJ Õ̼ÃB[Õ: K[€tßlŠíaÊnr¹8ÜM¨·èκªXYºM¨B Àtõ)­äX5GFƒyÆp™³ØŒsŠ®ËZa¶øéÀÚ5òÌmñ‘->±Åž}ôÍ`¡†aòpt窖±™§`˜©ÄYÃW.e^ÌBi±ÍÆ“.:u<*}Ò{4¢>þÇÛˆåB8J9!Àø=¤.h¿I§ w°ÜÒ"άùˆ©Å¬mHÏá*c ¦§U†Ÿê¸ÆH%¹Áë‡öüã‡íšžlÞìÞ¯ Á*ׄÔäŽÊõtnÀ`æÈ¹{£–«Ôz§ ùŽÀúpÔ2+J£-é9Š©a}žãˆeŒó2í­tL óÈfjÖ¸s¨©U›ª{°ÿ<<Ïb’›c3…çdee™þú<êv—2S«ÒD"7¡Æœ8¥SLp‚Eí&ŠA… «î›æXuV¾Uëacr¯S ¿ÀµZW²T¼Ùûã³R]ü±ÔsvyV4eÇûÙärmÞ‹ºÙbCG‘‹iL'r1¢…Aö[ÿhè3°)çD¶[½q›Ð ÇT ÌIâ¹ ¾KãÚac‹iDøà‚øÀ «uÀÃ-?Ï/1\Çd·\ ªáY3; uð¸B.Vg‹rŒ•+ë%7‰Íƒ—UHÅP·‡¾À Ës¹§÷‘qùMÉ»e¸ûØþþ6²£LuÀWîbžZO„ce°ÜYhöZgT&õ“B‡ç›\Qšƒã@LÿN—ãv¦ „ßß>°m¿La|(&£Zg3C˜B•`%p¸FG»¡uy@îˆÒpIûåÑPèì˜ÌÞ™ù qOñiyáôüÊ”¾!m_’1^8<ƒü?±Ì^m× :Ä>¢Àýý—Nfu%%ÌQÂh µ I¦Ë'w÷¤*ÛÞ\}o$Vú¾J%êÞ¨Õ÷UžXƒI nyc¾ÆHÓVq9@}Ù>Nø»#þÚë6š,~ìžT$Œ"Äœ¼Ôd׈ba‹w®e¹Aÿ9Að zp4b'ŠÖúÐIT‡çÃsÀ¾ù j:˜£ìÄ¢ÒƒUqÁ¼:s|ŸašFÔ-áæ]iÅÓt€™‚}o×ï/<¯$ñܘ¬jòŠô[q<ÛÍ7AÛ-‚²÷$T ©ò˜+}&/Ï §CÝd$–]DÞO}·ý‚4yMFù]vbÆpxÎÊÿJ‡8¡£=tG·K®¶ v¯ÛÂÕ Jæ '§[ú¢ ©9ë¢ £‰»s¶“à3€£?umkóÐ OæNÅ~æ¼0¢á«“”žˆZ̼Eáû²VYÕ”=1à½_‘ÌÝ|^ð£q`™´$LJщwâ`Òó·KÀŽp ,ræ£_©'ÃÜ«±Wlštäqzçgl¯¢«3Ú7#b-Tƒ†äãKÖ£Pzë9úRcUÖYÕ¢™Üœrí`æÇãG?»†Ž¾Qn]x†èäÏ2ax1š­Üvˆ¹ÉxÛ¼qvñaã\¾MÇ ƒq½ËÍÁì¤5﹈¦«¨MÖ§MR²Œi±f‚ ¾ØJ¾ôÌD\_ZÄÿ½}Î^ ÐYø´À×,XËw‚¿8q›æ]bµ©Ñ~ÙÃPä%óÜÇ0à‹ãEûX‰À'g‹ÖþÉpsõn/{1á5óz½°#ÁŸBq|eRö§2nÕš1rIpé¬Ã%êœW u0 žÚù{—s`¡e…è#‚ ÄP€¬ëá¯ÈÙVÎ{¡E›åÅ×&« 5›±ô‹KåQ̹>v‰ ó8ü×ìþ˜ OH“ïH Û¦.|Kºþz§ð6„¬!Òý–Æ“á:8 r *þ oó[äÆpD)ã1xô†¹Ý§H¿£¤Ç^…'·È¯“YNáúõ4êøOÄžöÉÆvûcYòX,Ô[ºf7ô+÷o ¬ú"5g¾kZ÷Š’“|/™ÂÆÉ0¸SIDûn7¾Â ú°™šA²žÆ[¹$ôµ¦FL[)~¥ _² =ääþªÝHöꄾ£ýnŠnöϼR(ÍááñWd}7¿3 dSļ’¼Œ‰’’‡MŽX.4ÿKeIôÉÁýÅ-ßÍ~ü€zXž{k¶yDo/ÉœëiCL^ÅòðüÓ(_šÒßMÉþ©Œ3SzgJ¦tjJ—¦tdJïYXš¿6£L)Ž´8 âÝå<›4p´Wœt„_‡¿6-ÑÑË(º Ë`±ÏØä÷|VcÀgû 0¦h‹*r½ ôþ´Oüîuë“l}ž"=•…ÊSPAI[Û0·ƒgßc”è’Ÿax2Ÿ)÷Øi!>Øí¯]ðÇ_ìUÎ-C×ômr/8ú;=ôØZç:Û|ë«[?9hKxÎüÙáÁ÷êßÿFðŸ–endstream endobj 84 0 obj 5294 endobj 88 0 obj <> stream xœí\ÉrÛF½ó+p ™ a`0ÀNåà=ŠåM¦Ê©r| $ŠRB-%e«ü{0Ý 87ÉN,ºÀY»ßt¿îùÙñ\_8^úW‡g{ÊO;³ÇÎÞ‹\¸w>wb7HÿÍpùðÌy<Ð}ßIÜ$rÇÏM’ØSÙ¨¾IW%Iè(ßs•npÖé:½Á¯ºOà™>ú³Øìvß~ÔŸöOø®”Ý÷ZtCÈîFzc¤FrŒôØHŒôÄH; Ç0òk ==`äê("J§óc_º"áÛ;ÊøIÐ=%qJâ%‰‡$þ »9°í!‰×pâ[Gp06‰çP-žXð‰ñ`¥]|üÔ b %™âjpÔùȵÇÄ+¸„i =Ãq›öÎÀÄBwKWÞ/–Þ÷}WÄÙú·Œsšcú"$£;`¶¢Ýÿ8·LlÅùÂùúü ì5…óm«ÁÝNá~LÛÑ%Ïà-Òp¢—ùxvY¸u!?r•‰îIO‡_H¥µWHŽ–‚0м@c;p½(‘)> I£Mè“£”¯•dZ²Núø éz*PéÎõÄA§¸ñ•ëKÁ[ÐÇÙôðJóë}+m+óÑ9‰Ç³¾Ê—‰V”LŸ&²;¦o¨©vD¡ëy¾> lš£™(£˜OxMâÅLÓàŠŽøÌâØSÖ-ßyä ¾Ð1ÍìÙöRËœÓÃ1Ô—†ŒÒrR£¨ ¨“³Œ„”n¤ Ô‡³¦‘J®Z 6J&- Nl8¸¬1¦év›nÜóDà52%O©©öp~âÊÀ xj‰Ù®dg3כш“BõˆÍoæîö€/Öbæ óL¡AF\_l0ÖÀ!±Ðz…†úèÒÃQ»Qì qzB)Í9Õ,ÒJ¤sÌ"m(²ʼnO …ûWøqB¹•ü(bÇûÜ|<6ãÔ!ªê×ò£‹Nõ¼ŸÑ.LûO½SQöf–c?›áaOw•BŹÎw¿›:âoZrmbº÷WaOÁÈ4ʾ‘^éo#ý`¤c#ùFú|*(jÑÇn½$cÕý‡‡7Ã`+a¬’qF$XØdlÓ Üà€ÄZÎŽ€WÆÖÀèÅ5\ú l0ÍøG¨r,³ó•ò~ñÌJ`× JÀˆoP©.*ˆ*áÇ¥>L,C*òR·Ö˜èÚð>eÖZ RÁ!5nç\ׇìmûO¼ƒ¤¨-’òG¤µ Á»©lg%ÌšWPd Àv[+v¦p„k(bDÁ`0Õð)¨™—²ç½¾vP2%Ö&öR`gü­Ä‰(^‘ýô£,…1܉á CH¢fze˜µÎs¦ùgA^Â:è­jŽã)Qh"Õ ­ñžÛžÖ‰òÂs ¦©œ×«P, µ|êÀ8æ-%/ž>?Ô'.ÑL”D8¥ ‚Äê2£†šY^Pxœz\Ï'q¶,'©UNØÆnø Í Œ)×-Ñ Æ¸##}3*­ÿδØ:ßæuVr’¯€k,×w²P¦jœŸÀ€qÅ6•8ìLp´<…S´)Ô¬^9Ä…%¶y°Sm¾Þ“æÜmùßVb_“ˆ‡Åp}WPGXµ8 ‚§Â^ßÃ9†³aã,it­a“,ÜàNÁÚ²ø–Äýy€Ìá1¡ç+ãâ©í á0XÛ;3ðïp ì…®á.p5x­xNâ|jjËÌ[Hà,~®@“ç½<–‰k…ò î°—¼#º²ÀÄ\¤?x ¸<Õ7€ãNàzQ8j@ÖczXS÷5˜dSSiØžò,Ö]%D¥^à­\¼`|GÜ>®ÑC‘iì öKök“41 áV®ŒóKöª×ÞæFÉ ÝlÑÐÖk·/ÙæVÛ´1tA›ÃòVÓ7ßåŠçjÖ_òP´y©ëvà K)nþüÉT,¥ýÕb1wªÊêtÍ%ÜJŠÅ1€iˆ\Lµ^‚á 6e;ë,>åWpŠK8ÂZ2‡ „ˆ=#•±6O»+Š5X—§¬¾cܺô¥¬h¹ j=ÛÙþN æqI ¿k ‚8ð±q1ï´^ëàÓŠcëŽÛÞŒµ¼¶Îf‹ ø+³Y«Ž¡Æ­¿?¯qè-ú»¿í†ŠµF9ë×6ðèz|^Ar!Éí‘IéåÕ›S¢–™Á„ô•WÌœ5m¶m~SÒÊjÅzk†‘„Ãëµz]R•ôÚ^õeÂ;Tå ÔÉ](xI½R x]ÑëüÍPÛæ5¼Ý¸c¶²e\K0x\Q²ì¾\Ê,ø-â+üï‘ÈnØÃK¾7¤3 |Öà·-j}}~ü£‘<#=4)›^ VFúT2@þp«'hÕêPóR Å;Â/½§K啽†š%}†@*k–Ÿ‚²]jõ‰_¿Åo àËrL·ë²Ø©eGõUZX¼!š‚Œ5Ò•vëpJûpOl§øžt»/X`s0‹ãÌ¿ Žß{¶’á1 ³ðEg{e”­¿ZÊò[³Vª cM­EÏîÕ©]ë[Z_Ó¡\ÕKïU¤;<[°ò½>|Ĉvô²¤ëÿVÈ|Ÿn ÛÎQRÀÈ vci^øZ„­~Ñ–¾JÙ-~,ÁR‚G?ÃA_Ø(Š%­nÙÖ–ª)Šnjs ™±{ìþ1oÿ›.HvõüS®Ùô z·Y†³ÝüvKÍmαåm ‹‹G‡m·÷l7&Íë¼ÿ6_ ½·:<Ê(0*Z\Œ¢Ò`Û/Ö|ả²ѵw_ƒ¾Ù—e^‘ø¶Å—il K¾'Á¬À|†&Åw{×µ¯ž‰¶JðŸ¢x6è¼ÓÿÔ¢fŠendstream endobj 89 0 obj 2519 endobj 93 0 obj <> stream xœí\Ks7¾ëWð¶äV4ž0˜™½ÙŽ“xKII[9${HJVbQŒEÅq~ýó@70’¶åMU6[•ÎhFãëôÛ$Ï 1Éí?1¿=zrZM®ïÚϓӯ{âíõÑoGu&íÿÚœžßNž›ŽE1i²FOίŽò¬iê¼ê¸­²ªiÊIUäYeÜM'³ó_ŽŠ"+ÅäüäèüŸ?MœK™UuÝL_ÏL»B¨jz33] Ù¨éG-ݯCÉRë\N7DRoØòÙì¸Éò\äbút&³\7JMŸÏŽE&êZ•Ó—Ô”õšÏŽ«éÅìØLY–µœ®ZÖRäÕ¨Õý´â¤ë»¤¯m‡nîF^±ÝÁÁ®!G6$ûúÖ´­2³#5i;8cxnió›}×\è^îV†˜ó&*.+dÙ†”eÓÜí ’ãfößó QgVoÎG?Mï‰Í|Û½*T3=íöM7µ›½Óv_«¼)ø”ž4Jö|R¿ÏJ« #¹ -Ù\×+šËì¶MGCY²…Y-íô³]ÇsZ'ÖO6"“îoÄûHZ3îµ€êæÍikËcbÃ+mÅ–W²N˜UÝ3“ºÔÓohí¯ØÆZe9î´ÅöRưYÒég˼5Ì”á!¹mY¹–Ž"{Ó¯ ÐzšAÙOf¢ªítžœJál ª³F*9¨ò³ciUæ%76Fi•ùªtѺÛY-eÇ›ÙWY[á4o|6.àÖ0…õv?¡ßМ_9«ÉØV¤91`äjÛP[‘Rµ˜Tº], ¦ÌÌ”°¥“ýb«a\«Ô‹±ÚìqÖw° fXenE{ñ©2«u®ðbØqd¬ÞÍsY*Õ¤äMoX­®ieÓf«ýt_uQÚöÖJè’vö ²öI=¼›Â.ËÛÖ‹,"3ƒÌ8ž!ËÏ. ¶ñžar3¼ð6KY#¿Î^‡ÛÚ(OKÜWk*âjm•,ìQ¯Zk¥{sEXeÕí~ÿ}|B¢cä»n•UUø¤UY•Æv–ÁÇW-LÙècÚl'l[þßPÍ[¶LfÌ‚†`‰™ff,ØÆÀÒØ6Xt“µåbTÈì•à\úae>ÜON›‰iÇáj-¸ªË<Ë•…«?CuœgZ˜ûFXInE¡„š–ñïMV.Ewse6dZ°ÖÇZÕSÍè’ÑÒtm²¢2WÏÓ™A7=ï˺¬í!yeÛ6JWÝ.™~FóÌ sÃfb§[‰Ö~å™(óVãÝ ¼ã„u|n;ê¼…٥ǪåQê¢eg¯d“û<¨ñ›Ž[.Ÿ£k%ëaô¶ÍÏSÆþ„FåK>côÏ3Öž3z×ʰî´>óÙ¿v3æùU‘ÍâßyßÇ‘¹jUO Sk=â^ygÛ­­¤D[ÛÊÈçǶ֭fÙ«^íwü¢ë(K‡*ÛL{Tv3‰igY<§Ÿµ;T×Zz:ômktDQHO'çõ#Õá-xOÞš·y ¹¡ó±‰èÄËñùÐJ‡R„:zÅÚ\¤ù\–¥8XI›îµvÔeG½Çh|n¿Fæö¼ß¿2<ì!µôëïœ7) ¯ÊAµ‰S/AcV•3Àéx¶Sêì/óMþ½p+Gið$Âú=¼©JÁœìã¬ÞµçÙ"ä‰ãCü†½üuƶ“IàF°¦„ܚȒHúú”Aý]¤eûK+À»;H«Ã³"+j‡g÷c5L€¡PrçŸÁdŒ£i6 ¯à¦l09b<¤Ìœ•&Ç‚óÜW·p¯“î§£Ž±>k8ÁÃôÁózyBýªSƒÁŒÖ sÖÅ5SîªfnƒÿÝ´Êöžuà»>äWæª/k/@±±†M(áÀ>BÉâ’Þ^Ñ@7(”ñk{™Cà¹ñì<ÄC¯!Ÿ@rd±ÇÚàÚRHá{Ó«¶[!±/‡} õ›%0Þ½t¬“°†3fG•4u$²àüá“äÙîV; ™U• TˆäÄ‚ÈD2WùÎ/—Y+h vˆW²™¡ÐÚqœ˜Å6p ±¸yMu¼õO| ¨cá±sè‡ñ†]xìÞv|š²Œœžt±Òûþ”YMZó™'œ0°Ð4S¢©¦ïgfÚ…j==ÏPÉʆ![CeS2–ÙœŒA.²O„®ùA!ÕkdѼ?ÄEÏ(3ƒMú„ ÓÇgx,¤Â*Â4³íÊ j;à:ÞÏøÎA([7pæ+ ÞAW‡9÷¦ªƒ†ŸLiéöy Úê1•ïÀ>Êõ'bGû[iTZÏ­a,²‚½¬ù¥<,j#Ï'€œ…s+)%^:J»_)9þ UvŠÙ6Œ‰Á~ûµ kÁFuȶNxñ¶ žØVËcGk”Ó…×XÒÐ5xEÑè3$—Îë µ,D¡W3ÉœŒý\ž¡0^&òC8–&Âpþe‰§>ø2Éœø>•ðì+Ó¶É«PK‚ ò5œ.éä10÷‘Í åº}Œ“}e’绸ٓ~ã_àB“¾ÿ0Œ_0RL^!±ªMäo²¯øšMÖíãëÛ­&/ñdû‹:û…ØyßÏÇ×ã’±g_ïÕî2Ô¢²ý;$]p•UìÕˆë½íÕH‹ ¶¿Rô9„ʬQ1°4:e~l†¬«ÿƳ¦È`ñJ `ÙøKdð36Q½59è\mùÓ?kg¹à³ãÈ‘óÝN°ó¥U-†m{©ì÷~ E¶¨h†Jj¤»~àßMˆ'nGÅç®ÑV·Æºž1dž¥gÃÇŸbì÷>±0¤Ï´‹šW‘~F‘4,XxI%«ˆ¯üI‡]­©rSÜáe{L5’輦+Ä6eÿjþ•ƒ´ŽºuÔÒƒ¹Ûkï]¯Gý¹;'œê¼…ä2…Pã>ÅHmÙÐÿÊ›÷v _¸^Úï5.\OBèuªA²”ü ’–÷} ¹`ßã1…«µÅ pÞÕ Xä›(Ç=¹5ÔÀEõËC˜§;Þ Á±ˆXwó5ñ…$‡Ýe͘ ÎÁ¦ Á¸öA,ŽáÜ™ XË{6Ül×6¼Ÿä'lcìwÂð¦âP%ÍaÛ'¬ ŒD¯‘bçÛz¶O—p–@ôè„¡—fûw>½ä÷y¯ÂF{u$@÷ßa·Þÿ«>Ée=ÁDKúvÁQUBžs¸|Ð’@ieÿŠ?’˜C¾IcŽáÞÃöù&ÎòÉ_~µ’±S1'$Lhö8ód¸Ý.H×Kø½Æ¨cü¢sŸG9“”8q]á딼?*zÇ%œ\eD¤¦‘žo‹*µ‡k>6)¾èiÖ~Ø?ùòm´//Î~0ÿü¤µ»yendstream endobj 94 0 obj 4132 endobj 98 0 obj <> stream xœí\[sܶ~ß_Á·îv* 7‚dò§Nꎚ¸òfòt2«]]Üêie%ùõxÈw-»öTqf|Ì=8Ïýü5a) ³Z`}5;<ΓóûYõ89þ¶îÎg¿ÎŠTÚÿª^_%/–f dI™–:YžÍXZ–Ëkª<Ñ*ÍË2KrÎSU$Ë«ÙOódq ™0ÿVó×L³\ªùÒA¯ô½l ¡«ÑõCB‡€Âý®‚A¼à*%E½­x)ç§¼kA1?ðOÿðà¯!…ă·€®œ¿…¶Â $†Ö+ç+HìmléIlؘX˜aÿZþ}f+Œ`fË?¢}óÙˆö2&˜¨hWð)–¡p×@†=>ŒÑñ§"¢øÊQÁEµù#ÒÚxðwHa)\Bb˜Â‡µ¨ïä]é/@˜/& §"Šçi‘Óßïá»a7…ýÖ+zð{ˆ‹MëÎv×åïá1ç.^qaC a¶ùñO–õ™hYÿjÁR™iÍìŠYšs¡,#ÜÃ[÷ÐL‘¥ŒqÃH#ãÜ ä™ ƒ¶~Ð)Dˆ^Yž§,—¹y‡uSAR°¼_ñ£’7}„Â…Q nþQXív¸×Üx„KÿºoÊ"”Š¢ž{0©Få\•Õ(SÀS2a÷§3?ž¼Â¥#E–rãø¸(Ó¼Pf¥g^VÈrþEMU2Þq•ù g)²ÅSg$wºé=ë¹Î÷‡>ˆ;æ}s1঱}­¡YÞC ÏJ {†u£Xk©ÐI^!l÷Và×-øuí  yáoBñNô 8,áxHpq~¸—+â9ñÌX>Ó+µŽ|¢ ‹0Ìõã Á=ƒ{åp;飷җZÒWìÙ«·ÍÁØc€÷MzÖÇA}È…ß[/÷Jg'xD¬Ãgàµjýkµ½äTeS-5?r(ßõ( ÏÊ8!ÕJ/‰´f—¢·]ë˜by…yCÖS“C boϤªYÃoÏŠ5XãàÇÆi²ãÂfä\(S´V¹@UÏ›…L™.Uå9ì ®…•¦NµÊXfYûû/n8©¤Z̼4ž¢Å$䫬ŸÔ0[Xäla-@-“.A %ÂC…«tA'»õèj-‚‹«/\ºRõÕ˜Ñí |3_Ð\ÐiÝÛ\ÃQTî)ÄÝT$”H…Ζ,7³ŸT͆¼ó…T]ÈY¯ýCLÕ£’B,`¢[,ß0gÉ‹cv~)œ èšbb—î%ýë^Cí Õ°É « ]dTSÍR’3›_ q?vÛ­´íÃÔb,«ûÔh#6Çn\ëÔ™np+p[k c©ºÕ¬cjŠÀ—¾îÒí¹.Ä5¢ NÓ©É µ¦µ¦ñ2•L¶š–Pñsa”°à6°©,-4³U»yhèÊÅõl«žb_³OZÔÚù—ðIËyÕarO³¤ÎUîE“”­{‰&š»$VC¦î¦J:Q„;¸8-ÁòØ!9»$^Éþ¾:Ždá­ÒeÖ5d¶0ÿc–Ö†êA`Ÿ‹Ô"P¡NªÓz=ÞT Ð]–TªJ¦Ë͈#[³9S¢¿‚ŠT('eRTål+IfZT®MÕ“yÿre[ø–®è—~#Éç§¡oƒÛT—•8”їߦ¦æŠ·—쟗‰™˜)d爲ÍU‹–jck…0Ä ,9WBͳçzqP¦…¡ÂkMbÊ$̶•ä°[Š…*æ3¿0dXZº2*7Ñk —J絿2èF4Ìæ`Çf'fz#ÅmEDd¬r/Ž6H‰mjV ^ךÆuE#Ó¼"g&Ï´u@£AÖJ[iXrL˜JŸÂ`z­dÑN_áü<¯éË’¶‰×NKßyéÖõó‚`S2•BµùÇ[‚~á¨ÐTDšÀ¬†™µœ<§cE:çu¥|Rhã:2oz‰2ÍŠ\J$ÙŠC!="Y÷6§òn Ñg…-‡Ÿd‘×jO:&¾¼ñ¦Ös¸Ö<»Ûªõ6 yšíè‘È`•¦}|ä=ÙkÔ²*H4µáÂ@1Fbtw÷—Ä’]€hü†9\2[g¯G'kß‚8(bº 3@– žéiËHD3Ú]ÒÞZÕ¥Ëeä™8±ëŧN°‹¶a¸þX£Þä„s’]2/RP’xN ¸Ú]º@{¦"¸;ôLÚ 57hNaÃhÞ24MÌ7ôšVaC(°+bkêdwåÕ‰Qis ¼M‚D·¬ OÍtJ Û[&=¥žS9*ãm׺«‡¸ûÕS@Û“ÁíœÞFm€ØqpF¨VP“/2<Ú åQà¬_m b'Ò0Úk޶ç1RY¾s5>÷´†Õäž]?R2õT¿Ó6Žz`Ðid઄ºÿaþU¸ýÆÏ0Á¬²\¢*¸sµé *.‰°D^Ô9=fB†ïLÝQ÷{æèÈûÈ1»»÷£_zð؃÷ jÆ,·zyj©S^d Xhçü]?꨽gìÛoËïœLíu kɈÿð>!ÚØØ{S»…ÄÜÃôFöÛ$ûiºñÚ”ÁÂjÇ.Sáv‡äÔ¶-NCK¢»=*åR'¶Uaþ"YLؘ ÍTÒ7õYÆ5ZäÝûX†C^ .r OyæKпŽz…¯ôµƒþf–™è®Ê¨Ó0FÝ3hÍR!5tß”-§÷6e¤®dFܲåØNoÄæpÇt(/t^Á8HN;ƒúוØ~oP²ávOpÇÏêgs!scÈ¥S—Ôt’Ñ­e¢>Ͳñ¶dÛ„lÄ­MÙMâ¾û)DܤƒƒáÕbÖö5df£½ª|'IÝ3âú}Î~NMžîX8çoxà++äà-¾ CN)áËS¯=ø|ŠBá.g ÁèÝBaêÑ'ÜïÀØ "íßäù¿éâ>‚a;™û ‹¦`{¨Ýêí´ÛßA6,ÁÁ‰nŒ%‰E÷, Nº‡ü†rsA‚æIÐ2bìÚÏ|êu«Äß ¹rP{nµ6DÍlƒ(7D©Z;4ÐÓ)­¡V_Aðš±!tÃ&~ˆü…[í½(4%ºäÔùsºØýà#°ØÞ±×ZÁao!î)d(¾8òanEÅNˆûÒÁ“£¤;†/¾¾ƒŒÃïzq±&FS£=ál6™ˆŸÄtïzàæÊ´¾ý¤8ž@ÖM¿ùÉx<¼ ØÅð?}LÈFðmŠÈ51hÁ^‹¼-ûän0v<¦ìàQwùÀŽ5¶ÄG81Á½ˆ­ »\"ÆóÐctÕîMÀîq=–n”GMýÆNÆ÷übG”o˜ÝXäÑË•·1Bì .rÌãîì>Þ‡*¦ÁaÈœBVá»8za³$ò$&?³†kÀwðêõ %}Fr!j¹Ë%­hò÷I}Ôçù["-øQ¿%‚{þ¢ÛØièáo‚à«0mÿÀoŒ|Œƒ]÷¡Ÿô[n~ßSèíìÙƒ`½/"YpÃôùë ÄÿþëñÞÀó×7¦8xøåröOóç¿ÂDQendstream endobj 99 0 obj 2985 endobj 103 0 obj <> stream xœí\Ûr·}çWlå%Ü”w4¸ÍÅqR%S¶äØNš®¸"»Rä’¢d“KšeÚß‘0ts°˜¥T)WbËMlh4}Cc~\”…‹ÒýõåÞ£Ãzq~»×5/ŸÀÍùÞ{M¡Ü]‡×—‹lGU.Ú¢­G/÷Ê¢m›²îG‹JuÛšE-D¡›ÅÑåÞ‹ýÅr¥JiÿÖy°´`ajÕ5J-ꢩùïWýï¢Uûw¾!pA Ã} ® <†­gÞŒ ´#|wô—=»@iøÅžýû÷î/íþ8úC°Þ¯ür>ñÐ_â=÷Ðß<ôÐ÷à}:dÅi–ÞÀõ^C>ÞäxŽ8€x06Û‰ §÷’sV1Ùµh£ÞY01¿~Êqæ,·D,Ä/Áº4F¸È¹Ò8Ê–•£©péý/<ô™‡þ:zBû7q„âˆÔÆûT“HjÔþ¯á Ï©LßE“6úŠS6“G@Þ™‡nt5i õÜýo‚E»õèPo£…tfŸi#G¡sFFuÓ´VDe!£íe¡LU•jÿ•k!µã¤oüj©Š²ju§=\'QI·¡UQiSkÍÆßÿå»FÝG̺µÊbÄdÃÛÕÖ–Á®§’eÍÉaX–v%µÐ­e„Çe%µ~@¸ áªkU¦q¾„ÃÕUÃ'»¦î85h,†Ë(·;oв²ugË.Ðe­ÆJ+Þ§pe?-[»QÚõŠOëW³½‚]õ¸g÷´"©e!«Ú:vG§ÖëQ+a%ä­Ý"K·TNlô°ÅkjÄ£ê-¡Lôd±½¡Õbβ…cv1~³]x™ 4Å.ƒ]øEÒr7P:ÔÉú –ÕMÕ6¬×…c’2|È€™Ã¯lpêˤ”¡ã½:q­V…tŽöÈà^†Teª·ßôÒR–…–Õ(-XÆÑ”um£‡Úu^i+kU««Þ$ö#”áÄIã·öt3ýÀXêÄFÕÆiÚ³g…j70²621%{ˆSÇ Òà¸!„·K;l]Ãè^÷º1Ä`šÄ ñ$wêƒC…Ôï\ô&w¢¹ט$xAç'ÑZô‚&ÚB•j´ß~kê´l„³kÚMUjË}ÛhH=•ÃN—¡3519Êþõ×1jl‚ÝÏ0å²ý:6T[Ì"SœY›„õ­ò*aR¢3¦»N]µ³lÙ£í3‚M`idÇ è[U)¼ÿÓ.ì¶óE£¢…1maª>G!—«²¨¤”´ƒYX ¡¥Þ7‰öz¹j‹ÆŽ"z~”ÚªoÛxìqÄF7~+· >°Ã”EÓTó9Ë­¥c±o½t­¦5Öï:(iÚ ßÇ£*[)œþó³Ü28ìiyѸCò´›°.+á<òÆÓÁ[o:XUZ5Á4oºÅHS¶õ9ò@j)­Çúí¾û£­U[öl°tÛírq¦ÄTÖ}N­ß.lÝ@g7‘2ÊJö›Í¼V²1gÈÓ ÌĨƒ'žZÔ騭AaˆªJèÞsxX±Láù^ŒcùØì8®ÝÙm-ÇG7øsï˜;~ƒŽ‰ÈŸ—²-¤P†k‰“„ þ©ãˆí]L0³AÜ òÔž/#{ÐW½‘Q–¤Öû¾ŒÄ‰ª4V¶×Сez{ ùñ!Ù™^ªW# «!b;uñÛÑ÷aþÕéŒwHõ€¬Îçï æ{è) È1¸†, Y–‚¥&Î d‰_Ó/à`ÙˆüN| iØÀqqœÎ[É/á`§`m¹4æûÞvÂ{ z(|ì¡CÐãP5 TRd~­r"9eL8pFíƺ½”¡4LÈÔ%lÝtr¢Zkç*3j±s¸öÝÅ/0¸«q’•`J*È2=éÃۺ̌’q¤Pް+d:¡9^ÓïØ>mS½²ê\ÆÀ?Muɪkn=ñ>:`‡m[ÒaSäøÁLÙùE›ßSÀË ìƒc¯Ã†îºÐÖ­âq–„ÔËë kcC4 ÞÆˆ óð%šÁ N2xÔß¹¡„u>+«®”‹¬-³ "ïßÃúÂxŠ·1ò,pRkFøŒVûÀüóq¨Èc> sŠxþjˆéN ÝØõôRûá³FlN†ä úh¿,JíUXàÓuSU¥éS%U6b?+àq˜²‚Ù©±ÑÝz3áÜéÒ~îü‘æÚx½v:c½†s[‚œdA5£Ít ]¾£´œX_N“´6^²m¥L$Ÿ­”Êô¶¶1‚pçPrî êTrÐOÅZû#k£¿QBü…Ôÿm\‡åÄjV+çrЬ.ª£]e³b‰Â³Åxá4íR9G™Aýš•g†ûá`vlŒ«»Ó»(«±-`UÈÕÞxWöØC—¯2{÷¶*Ä·‘ ¼õ½^{è—ù#aGù‚gÜAD>èmìê=¸°à‹€náÔx-P<ôW·YÇ;õÌù>KLuðu ¸˜¹Ü§˜Šâ¢Ã˜E:ìÕ]ôê`Û•·L8á˜d—Š«°°Dá›ò†èÁVAà3¸èÒN&t¥GÍú-Ù +C¸ƒ̰Pës¿ÞAzð6ˆxk3þE0#'ëÍ`Wn·£ãDFk]4¦2Ù‹Žˆ½({<·ªYÙk¤ÿN`ïRÁ¿¶Èž%ö25 Qb/Læø„/1eFR•ˆu€?&òU>·—-0d¹Ç ”Âe¿[ù­Û½€¯û¨^º›k_ ”Êxyb‚Œ×˜2œ¤½¢Ô^.ÃËú³”ã÷c"ô.d^ÁhÉ×/²ú»kˆ0ã"|{a%N˜3jñÍÎþ`gãf“F¡œŒ›¿³œ µ‰¼òÉu!M;ŠÌ1k”6Ô–t±(*«Èxº˜´Ç&wï@‘ÎÅ2ÊjE¿S§ëŒšb¾³rdIÖø„3qϨ3‹ H»zÅ×QÆv‰z{€GvHz}þº*T]k°3¸ÊbÐ{ºÚŸ_)MÓù`©ˆlvªˆ¬U’j¨‚qÎÓaÛZMëö+ª<†Œ¸–ñ‚MÂê!»g1­îí¡l mO\w…ÕºÚø ͵fc1xÕã ÚîpA¸¬åø¦°€µb ü –ÞÄéùëLÙÄôƒ°. Ç6¥©nÈéQaþé4C¥]ýKC%Ç•uÓë„aÃyLÏâc~TºTA#¦62 ÏáÏë8fùÛt*¶ë©ú9{ »ÂúÀ»§±šq<Ä`‡$xA;7‘¦NNxu•Ésó'>á¸Ä•–¶íÃ2wƼ§ 0NÞ’º¬ß¬²ßá˜á`\0[¶•Ñ|{ñIfËQ³Ò±R4;=iÉFÄ8sŠýG¶ ‚õzFJ4#ûY‚Š@Mà}LÌÜÚUž+¥„ëŠH*QÛ=,jÏùPW‘‡Á“ŽAyg6Û‡KŸÓ©ý9¦™·È‚:5°5ò×£F¬#†`&cœƒôïüòBÕÁ÷f°X=¨Ñï]¦P>)ˆýi6£µß.x…æÇŠÏ0C¬³':äF¯”VP)aÖœ ŽEŠîxRn0!fe*Û1G™w‚%öN˪ØQa®¿FQ«›ä) n0Œ,ªZZ •±Ó÷xˆ™¯°Þ}Û%QmÌ!÷Òhþ™¯—þujVK}M`"{˜;mÈ|³"&¥Þ@À› ,* v;3Ù‚J¤r@XŒëºí%ÿ™B+C_õ@ºàY¸G6s8ôðŠ”³wÉ!Ðk—ûwì‰L›f[Í3D Þ6üî9å/¢»¬àî8· «Um͵4ÃÅ·{®3+/½Rj]b£e¨Yž*ÔI–ôÈS¢hŠT vsñg DQjvéQG©eaÕ C¸œ2Ó´ÆŠoáf¦´ÔØÚ,]æ¨áMbh²ÛbZUX·ÏåÆMa¹íû|iõmcT³ÿ‡ÔùÉÒõPe¿=¦-¥á·ýˆ½¬˜kú¡²¡üPÖ?.„ª[w¹TMÝô‡Í!E_î2tßxÌSçS—²qÎa·ðª¤K¤|ª^(ûÃÜ/- ‘wðâB–öø6þé=zGLýJ¥hÏ–UYNÈU•;µ|6fЬ,’ŸÎ_9˜nÇB£ƒÃTýK¤ÿ§9¸´5ÝâˆoS?Ñû0æZbãƒ_g§ðAZ+õmˆwM–rH·¨'› O‰t ›§çStŸvñœzB¥~ŸxDàcY}ewHðl?¨ûýS-m#hçéoÌ_0WôuËA3K±qÿtê©’2Â%;lLœ3Êfègœ^„ËTt=¡¿9¹ÚŠìî}”€Ç^ðÔÓDKĉD"pWF®*ÎLëÀc_ ëëj¬£j (Äü ´‰G`­·¤©Óï˃w†]zˆÀe )÷¾`?Äê±g“Z}*ãþà}ä¡kM¿·êâYèÏa¹7 ˜|ßU&ãbvü\ˆ!àÏlàªküž×mãOcÌÿ:lîÑ}@}™å}ì眧¦øÑfdö,èkÁ3Þ+°g›¸vW”_C\6ñ=h•ïáã»øÂÄÿYe5¸ΰ5Jsc÷ÉÑÞßí¿ÿŒ endstream endobj 104 0 obj 4193 endobj 108 0 obj <> stream xœå][s$µ~÷¯˜·Ì¤Ñ-u«»C jwY`S KÀTAÖ×uÊÛc Tþ{¤¾è©?µfÆ^Ç$,Uzt×ѹ|çHûó,¹œeöÏ@^ì}øM5;½Ùk?Ͼù¼'®O÷~Þ«…²ÿ´8}x1{¾o*æù¬žíŸìe¢iê¬êZÍgºUÓ”³*ÏDe \ìÍg‹ýî幨ëÙþë½ý?ÿ8‹L¨RëLùäÒ”ªò¢™ÿ²hDU«ªœ¿3¿W¹,ªù±£®ËRdY.óÑÖQecz‘Uýý¿™Ñ©ÌN™Ñ—ª1CÛ?Úûqþ×ÅR‰²RÅüÊQ—ŽºvÔ­£>Yè,ëše“VZdÚ̲ovF“8[˜ÈUSÌoËŠÿò¶j­ø×£vVJfÃ[òÐÖ¥¦.KYˆ¬R•iÅ:w¿³öVÔËšŠ²`‡lIYc¸ìm¸OEÑðßi+úÊšÍùW×›«vWMÓ×’Ê2Rñ¯¨…["ßQY¶G¬Àh{-Úí5+&UYþË…e©Ìº–jàÜêÕêÖQçeaú2=XÖfËq ”í4Û¼Š'´ø—)V¸äû–.ÉLû¦7mç&ç/ ; Y릞½P†/³&ŸØUºÔóWDÒï«T5g•Cšï-Õê—NgåünQÚ³š ç©,¤ã`Ó}<†‹(¨Q6+l*#ø*»ÅK•5¢¨ªÙÒÈÖv—ß,–Zè¢4ݯœÔêGškÍDÙGúB•d§yí~'ipËÙµ]‡ª”FàÁÁæü«9±Mt|2%ÞƒÜòóíx°o˜w˜<IJs•*˺Ä]ÌhKÙFÓÉ–·ÌùÁ:w¿ã™ÆNP+d)ÊÒ©‹”aõÙV$˜”¡¶°”ø€V#)ýŽ¡”`c¼‚ëyˆñ$äâæ ¹Î:ÀëéÉO¦†œ<¡ÎÎñI;Smh(Œ .ÃMhIê)É!›£›K·'$Zzµ‘B’Ú0mÖÎR¯Å$êqȨV¢­àv11鋱B5¢Îê™JÙK2&´.¨YŠ«‚%jè†*õ¡ÊòþôôÃym-É?Ùÿ+;’-@Bdö}jÓ(Û—£iãšu+Ö|È€uQòóD•z‡ø¬ý sØ*4 È6œZÙ°™ Im';a©¶›Î$u}u2&“Úg3fr`ጎ >vPøÞöV•á ­I öGÝSrý·@Ç9ã(a®P—ÖJ`l,ày0åõÛB6B¶gßñ6²c8Æ$€cF82ØÙW¨ÑØ`¹ò\J% ÙTf1òJäEVF‡å„"ùЈ{®[+ÝzV‘†Uêj`•wpo câ±TÂt¹´>kc·Þ¦59vÜ|©Gò‡É¿¿LË?Ͻ5N¶sªÍr©ÌL¶(æß:÷õ¥£ö5sÔ—ŽúÚQ?€r¯õÊQ_>Þ8êG} Êѯ;êÜQ¹£~屮Qó@Aé¨þ×¢®,Š0ÔÁä¿R3´_oˆ<&ò–HVöœÈ3"W°VíÈKX ·p‡s–j¬ã¥¬#âðVôšÈ#Øï5_§ÎÏé³'@Öô¸î Àß?Aþ“šŸ‘ŒÞÁ…þą̊ÀöìàbFàåžö¤À>«°g¥u¡ ìy¢+‹­S¨ºÙ,XÇÌú‰ØtÀmÆŽàhmzDµ3.j#.nI.6“ÃNn`z•EÑòšÛ¥øURÔ:sXÅI€%'c P‹LõPõ$§œ×Ѿ À%ð¡úu½„HnÌÃ"w§Ü&z0`mZPÚ€Âü'Ä{Éímí Ý {NÇÙáÆ¾6Õ_¹6C6´ÿ-‰ù—DîsÒXN–U{·XÁßxRï¿g×#›®ªßžUÀ¦ ±7ßsÈ$&òAT)ÝØÄVjÞ‘!Y'1Î&NPøNæ*ëÆNNáHcñ6fQ¨†šŽÇ<ïу ûˆs;,1ÌàÁç>DC±,Ãqk.…™Iß’:¯ç‰{44¬£ ‹±ÛhÉ8ÚôþøÛu v`^$(¡²ZTeI±U2£vò×8X‹@™`å.öMpž=}¬Aç°y±U˜Á¶uVUàüÈP±N¨TÖóI ÏVµf aG’ðÿ)õØú‘#tØ"è1ó}$ŒUåƒéÝ™ÕB–ì̶Øk቗Žºj‚;os“þW!Õg ·ê ¯\Ú4-É'Ê€ †þ0@ŒaM? F•Nˆ|KäBjŒ} y{[Áá`¨î4Õµ¨ª1)*@Õ?f·ì§Ñ‹Öù§×,ÿ?³É7º)ŠˆMfŸ ¯œ¨ ñ+[ùW”‹Ê/8 á<v·AÒT(KYSfØyc£^Pb¹Ór"³SmÊüëT[¿KOAµ‘z"ÕF}¼ªí«eØú%¨BÃú˜ ÎÏÑ}6.Ù#Wñ9ßNËKÅqO¬˜ÞÁ1™½ œÃv“ÑG©¼ävŠ¥Smƒʲ¬PÑâ\rب ì'¼,RRÝ€B-E¦>›´ªò9ÎQ㪱¥ÐánX; kyÜv ÛPcaOÎË´K¤S¯Ã-Å Bƒ½‡ú™DÔ™pNà±()dû”Î^ЂÑìg=÷ë0›'`põ!ØÈÔk D©Y{`¨¦4u(¸ÿ)$ Ü=#ò‘_À#þ%‘FÚç¥ÐE+îôçS ³æydKyw¹#Ôæb Srb©7ÇIë’ãD[ä¦W™ï“>„B-"ú¦u!Œ$ï˜î8ÞÄQ†çrè1ùúç°Pëô²¯"ò~¬7ðIdF=[‡5<öSN3?'¼¦PjYŽsö­ÌO†ãqþ¶xGóHÑÑëOctú vˆx‡Lg^dº¤óO Q+˜5rèÝXFaõ‰ÌN¬k¶®¢õ†ìûn–÷³ï6/˘Þ÷“=c©†Zù–º³›WÛÌø†ÏC9Ì ¾‚–+3àƒÕžB†ä$“pç.´¾¶Uá¤;žð[8°#XOíbz`”TØçÑ“ ©eâÄgàÒ¹è×£rÇÊÂÈžºúáØWD~HäX£Êسî‚uª1ò’îÅàÎà|F¬ø™}ËË>•‘Y!›ß71u Yçï #M§Þ=$ÐÙÛzHáv—âA+–‹³†ãLbt·¡9‡D€·¥sip¤,tͦ‚/áv.f-š y1~@(¯ÇGr|ÕºõÙX‡»Ô¸5ÖYÓ­Z ®÷K´Zz×KPXûŽ`(ÿ~ÙÔÝâÞVfûÕνÜ=R7¨ùdÊ`2Cµ?¹¨î´OÙ‰M’pÚ:ù¨ÛHþS¸;cL­}î¨ïõ¹£~wÅrOE×c>¿ŽîÄJLRþ}˜ÎZÑÜa&&ÓþwPßâ8d2 |@$‹ýâ,Æ¡q¬z ŒìƒÞ }ïñruȰ}D¦±s߈k‚ÛU>ß ™xØ%Và~ÅQ'Ç7­5ZÔBWcWŠœ›¾À²³"Í·Ù–}¢¾õûrZ°§Âø’8—ã}qLòN ö9pÉ´ûá_ž"Øó3 gë¢{„Q’¶Æ^7ŸâÔ>E¦³SÉy mãðbùŽoêLD™X3ÕÜ»rX »&<žå†Ý¾øRe~²[)|a~Ÿ_ïÈñoìxËÛÖWýµF¨Œe¡y1›>’r´Mt‰’ùùk>B×Ã67GpP ŸýÔwA> ’!ÁI¢Ñ‡i@’@’dQ4¦&. ¹ ¢†… Ö‘g°‹mÀ›û#€lr“—Õ€…êæãMj´ý¤-ÝÍL’ßÃñá\<¼´X¡á”E¶w°Þ‘ØfÒ7kǤZƦ.ðvÁʲø5‘ß) ‹è³{óǧp91¾™d Œÿ?îFÿ»ÀÒ§Ba¤8É ;^§Ç&Þ $qò×MË5ÆÀMÖßîí”Îrø‰V-þ™£bÏì¢p”·Tàì0ºNµÀ¤ NÉK2†/ðòÝ7}o‹Ÿ²ÙX¢¶ÑSTdñ3ã¶ù°ÎùG*I~]„ùa€A‚¡åŒ®e%­Ë7.¾Ðb=‚ñ}ß&^NÀ×ÑR‰z ç(_+¯M tE~ƒ "GúéXù,ëç ©[Ï~TóÏ£ ]U-d•5 ¼±ÁQhöö«”ú}Ïþ+"ßÄÖd ŸÓòl´&M&dS¼÷$7ægòÝú—­‰ù$˜û—ðz—Ö>J—ð‚›wU.2ÅnÞN°•WŽ‚óp* .A)Cø ³Qú>{t'ÁßY†Ï¥žæ{¯Ã£6%Iüff¾ƒÁ½”Ý>EÆ_ÊÎuiŸÉþq.MCeÝ”µ}ÑŒ9/³¬{Ôxüµ^X›¶æŸdÿɬBÙ(!•´ç¦f“kûŽ®º.UmŽÍ@ͨòëE.rU5íÓ¹ÃÇ•ûxܵݱÓðó‹…íEe–凂 -êªî™f)Ý…3Ë&“eΚJGUŽjدß±äòPÿx‚C6À@ØñÇqµàvˆ19ïkÞ¡qüžι;õ (;B $çG©ÂÓ¿6ËŠâKÛx%ðë@ø’Sg¾v9|Ê)vÝiÈZÅwœØëâYÄ*HÝÙ»ç MRÂv½.PÒ¾Q=β]ðvmxÁ;„þζVÿÐá>”Œt)V•L¥Me±„/<ƒ'ìGOyLãð©7|˜Ö ßQèZ×›¿¼zÎk2HýRÇ ÝŠ96½~ëË>ƳMV Ô¹a¾«úÓœã÷¾uÜAÇŠ1Õè…ÞøsS3—¶Q6ÎF)AŒÍü.#¬áP¶ÈîÁQ…‘.ÎÝl y!¿<®^ÔU Þ€GW…PµŸDor$ú€sp`¡ƒtQ+tº7¼cB§ ÚhQªŠî õmyS¶ê=ýá% Y³§}cÌ”Iæ-¯Qó;Ú $ K"”€"ˆøÕ1üF?vÆžHw;Í‘èš4~î%|e#ù¤Öè±ø #Ó{L8~Y ’á„NS,Ã龞ÀÁÉ»·ôÉ{CÏS Äê/Gu£‰Õ ïÅá‰mw ?o“r¼M’üæ¯P> stream xœÕ]ksݶý®_q¿õÞN.M‚mšÛy¹qÇV2IúAo)•%ÅÒu’þö ì8 HKžÆMg‚\ ‹³g»à/«<+Ä*×ÿ˜ÂÑë½G/›ÕÙí^ÿóêåcáÍÙÞ/{mVêÿõ?ðòÑëÕ“}õ`Q¬º¬«Wû§{yÖumÞ ­«ºÊ𮓫¦È³jµÿzïÇu¶É³RÖu^ºÅmQdMQuë_7]Ö´e#×çêïM!ªf}bKo6[™åy!:õ£~¦”ÝzµMûÏý¿«¾”¹íK©ú*ËNudÿX½øãͶÌdSVë+[údSçùð DÑfyYµæÁuób£T”]µ¾Ýlþ—;ÀÐíRä¦ß}‘Õ=;Þ–¼Bß"½À¨­Ä]Ñ/íSôü| ë«°£ ¯7[QeyS6ëC8Ö/¶~C»jåÍ+ÊZÖëW›R5•woàNÚÐq½ÂWTõŒwÜVe $§Ú G|—öe°7¼æ¶éEŒ5ÿÛFKŒ(ê¬h#$x†±ÅkØÕSêÊA_¡.ä gª1Q:ãÆíÒذL,«{@rrˆ¦Ðᱳɭå¯â5uá R8òV¼_»:—ëoŠ&+*Ub£¹#Až—•௺‚Ëè©/ЦQØ×hÑØŠFd²–«­‚×^:>ßl«¬®ªºÅ;|l¿¨k>ªà‚Z,ÜÙ“S=Ô4?ù>Ò0tΗ-ûµ€õï/Ù:`I`ˆÂÇ]ðV*œ¿71põÞÞ/í[Z{³8wûê½µÞ¹‚7{Évà€MV O ýõ¾0tµA›îÉk#êþ¿ÔSÆöŽ 4–2<ÑIèMbŸ#Yh«92бëçÔBLLm…#¤É®ãÁ'; õ CeöÞ@‹úÅW£Ô”Y¥HJ¨oØÈ˜faêÉõG1ȪÊB‘žb¥’#j}¦ÕwÝUÕúÕóL´MS¬¿ÛlkeRaé#êããͶÓ}ÈÅú)ÕýÊ6°òu€·‰pooãXddY¸œk¸8³½bo&Qc5kï„/Û¯kw=¸äQ¥Ë¾Ó•’°ß…$ÜÝÇ8p?‡]'˜ø`=Ê^¼ã²jëÃÈvïíFêEê›b}Açí |eå…4‡ éŒ '¶ó„4žqðHÙž .™»Ú ýÁUÁ}ekAóËÀ$Ø}‘Å®»6ªø±UÀÅeR…à âc²R…pãЦ@ÐùˆVÊøª,Z]Èu9ÄÑ%Yé®9°D æÊƒ]:´V!í;²m=¬ö¨&µTãô6R¤9ö ‰¯8zsêntoÿh„ iZUÆ|âÆp´oë&Zu\ÖÃp ˼ˆt­ð²W Ýw0‚R—ÉÜ)6õ˜ÙÀ‰ÏÍ$kºßUw¨ªÇw)ÃJ½\¶=Uú…êû?ï°ö|oÿÏ?®¿VO©ÙphSR*”ÍWªŒ‹X8;ã˳•‘ð‚q‰Àkf”•笋Yøžá:Ë®R3 ìÇ¢*µ¼‰.SƪÔ?jWS-a!ôĉ€Ç`ü…Š»·¯ã>s¸¯a˜<ÍÐÌÓ&Õ ì§ã?€Žo¦z9­Ê¬-˜î“XûlO."6ÄÛEœÅ³j=~ŒéêŠÏ9Tp[ÿ»Ø/HÞÕjB¶4Þ%ƒt6KbÉnàxRkj˜†µŠ{ÖÃz³ãK‰Š.¨È1ÛÔ5ÃŽG^,3YZx˨jÔa]5]¦@‹@êÇõc )Œ-AbD–›ú3mÏc HSç"°¡~xo½Ý¶ÜÆŽF®¹"åIœ&‚{èà-}MÀ°€a‚|ë9ÏÙÌ2cLÑ:Ž}ÑÄ sR<;àÁŒ±—+µ1«¦jÕÛ¤“2²åKäg¹‚Îrë¬h;{$û”ˆ„ö<ú¯§¯™°m2CæÛÑ}Ùt¼‘¯6uÝïÈà,Xj©ÈkpÌŠ„ÍJM—8ÇÒ™`a“™±îpi¸!‡†’DnøAwh ²Ó863\D¥>®/ʻ꼘<Ñ!á3ƒy#gnÛ|¯¯×$AàúýËÆúL}¯þõ'7„¡È³ÖDÕ2 ŸµëÈ0|fKû¶´²¥¯mé…-ýÖp@DØÊS[z Ú£¾<·¥oÁ³_%¡µˆ’e5 mõ*]¯kMñ–ŠçT¼ â)器¢â%¯©ø+O¨ø¶pDÅسøëäÛ.Õõ;ØõoAËMÙ)“˜oàËp”ÆŽÚšë!Zƒÿ'1ýfRüNt ¡ve#ø@ÙÔÃY|eÝivcë°J½‹ã ¬{ »s_Œ%þ8Õ³ÛAH›6#ò°JuwÆn)¡ÉàTgŒŒ:Ê|;vB}Ch†5Æ+³Ï-z¸Ÿ±’¡°a¤UÇê4NjҚ½lBÍ®›‡êÕôT{T©Û^$Ï£GóÛd8íq‰<öÒè“l$–Î9ëèy®#/Á«f8V{±%8}Ç{F´²4x‰;ód,LCðd娠I¿Þz ¥‡x¨å™lx§¶qÇiª¶«zžÜ5e³È]èãÀ¹Jõ£ÃÌÊÒAÀ <†”Ô¶ô7ÐÞ©-]ØÒ¥-øïe¸­~| *ŽÝRV ‘¸:°ÎRl ó#¬¼>†u?uY0ÃzP 6œE–užÕêíø ˜hÕØØ–VMIâ@’ê\Û8¿gÅ$Ûy’F/pcïK’²~–êú™Ž9׺8 'ŽêVJ òŠ‚ëÛÆ ®—¹ÈÄ\/¼É¶“jSfZã2Wø'á¯íFÓž–ÿTŽ?©ñÊ®ÌD)´ÉLÖ¹zöÅFdu+ËVI‘)­èág™5U^ö«§ Yñd}´UdEÙt=H˜š'Ãkÿ»©©Õìr¡ýÊ>t×ÇÍ*äÖVµyˆýxA^Û‡ÆÇkcVÏIP¨Z-˜Ã$±=nÂJ«£J´EÈܘ n(<Å<ÄçÎÀ€}›,>£n=¢â·ÉòèpgŒ°øFX€Î]Bñ! PÜg Ëa„¥?«>„U÷Mù! PÞG6ëa„õ=–°ùØÜg Û{ð)õ»e±£”©|¤Ü"€ý¶—°ˆ «wXùîÞó²ût Çz‡}Ѹ™=qÐo\N«(në{Ju¡ƒÂ)žêÅP´(  ÂÖYÅnø±Î³nLDöl ÖŠ“`fÏ3“Ð[/XcF:X"ª&–|ÖGɉ®¡“UjI0qR¢ÆÁAP^ê],»5fÅ&†ÌoíË̈}µ½†31cÝm…«í¥ƒæµZÆ0 C"ð»)|BÐ ™g¸3ápý@06 Š|oé•É´¶,8¶óŠC˜=éXýFë„_æHG+ú^Šäm‰Ó’]JDœó-[÷gBßd¤5†þ>F§É¥ôážç¿£%›¸íbÄÏ m²â¨#ÙÞ{dÓÔ"«k{CHpé-1;~A¤Êc>ü•Œ¢.œñ5A@3å¼ógˆ,ˆÞò`û‚Nõbâac³ì¤M×ñðÜO }“‰Ï/76)Éb0EpÞ’w90¬}<Æ ö±Ä&¼EŽSjϽ=Æùç8[ŸRúÙŽJ›ÒApBhrV§=1 £5.˜³½73 EcSÒ0ú`YÄ}ræˆ7pë¹Èï¸@,8™\7îå¶’°À#‹q#ª€ñzƶõÂy4®’•F4 7y~0†ãDÓ™)Zš"’½¼ ±gÔÀ6L÷‰-År=çGJ,èKM §âSq—ǧZý,{èŽí§‡ýƃï«ëBŸÛr~‹ÜÃ8ÈÃF(`çÃ0ÕoónA:é©£OüÁ S CJ‚Ÿy‘~ì.ýôSq+I”tæ²ÓÂqq±@F·wFÒß3îëAîxÿ]'éÉ>0ïÊÌ|Œ­} qÂ%§cÀK›u¢‘‰¨ý@WëÌõäwL:z?;dÍy‘s/‚Üä=Ú-ìΡ/$`ÎyÅ"z­žñ .ýnƤëðMÄÉd‹œHlÚ°µêÇ6ÜŒÉâSœ›EçèÝ—ù=d¨nUGèžGk•µÔ4îmÞ„90–ÅI14Ǻtæô5…´ØÃ½¦¹´³×%‰a· Ãhúó*Æ?u`ñiñ×Uz:ñÍin†yû‘Uc%ôo99X‘ÎwÃKâE—Ut·/¢aí -è”…ïÞ…D³c‰•1Q=Ašô&9îÄÿIÝ)Œ½I…»óëö±Ð÷À#©]¿¸ÁKp˜“Ÿ°ÇÑ“ƒ¸äY×Ö PzÖ‡Tí¶å7\B;™ÞLC7üßnâãJÞèéÛ(˜eçEç øü ÞW‘‹Ê=s®ÿþ¦Ý/ÉbgüŽW&Îa9&Oí€~­“_»Á_‹‚—Õ„N“Á©6H›Z Â\¹ÆWæìyP:€ñ-†OóCxpè!´(±,âXŒ4÷øû„Ø=„vì×tô긓 ä@ü:$ürv…ùBSÑ$¾Ð”üN£ïà2ñ*ן .+*ä98Òü(l©±¥ŽýÕàs>²vðG½bÇÙ(ð“¥™6‰t6€—üv4üiü…ƒéËÓ£±$â™Ä»‰ïŒ¸e¶]ML`CÊ4µ—œ,¶è]ÉKáø· ¦‘OÝaý—ËèÔâƒRïhÛ½;Íù˜ÊìS•˜Ž²E˜iÂÚÂGY©Óã~”ÎÇ›ždyVàR™¸„w¤_…Ì*)ýzFEvü:Þ´,Ú ^P6™?Ç „×çÒ÷^PÐrìÄ#4ý¨äzÂ"iL˜yãO®]C¸[¨8‚=ûò >OͳèJ˜ÓS’€ÈØIv¿°­h¼U¨S¶)1’³O%ÄPÞ ­Þ»9ãÜ÷‰Ûb±__Ð%ç6q^"^l˜& 94ÓšÕΡ¸.²‘:ÈFa)3²h™êsÚvø³ >P)[ÑM®˜úÒvä> stream xœí][wÛ6~÷¯ÐÛJ{"AÌ[š&i¶¹ºÎ6{Ò>8²âx×Å—$í¯_€Ì€ü(H²û$izÚ)5¸Í|˜ HL?ŒD"ÕH¸?-1;ÞÚÞÉGç[ÕãÑΣ†8;Øú°U$©û«zÀéÙñè§]ÛPÊQ™”f´ûnK$eYˆ¼îUŽŒNò²ÌF¹InŽ·ÞŒ'S¥‘§ùørbK¥óñ…¥ÒÌ‘²‡{ž:šØŽdZjKMsË™gjü×Dæ‰Ô2&S)-ƒ.LJžóÄ·ž¹6BHcƧÕÃT‰œÏcAO÷ˆds¢^ßVC¥Y‘²IͩшM¦™V•¶WߊõÊš[†ÒNFé¦+Äzâ|F—Aƒœw°§PuÀÖ̦ÄȺ[¡ ÄEW¦N’§´Ž8 ëuÀØòßOþÜý×–R&©±pÙݷ踒(Ìt%Ë«Òr`bÁÌýü"kˆ•œ1Tè20’„x…8¤K?Aư5zÔΛ†šÓPŒ1¡i ùÊnD¦,ø ÷ádY¿¬‡ÏÁÂÛ—IŽäJ𢴺·÷R“™¦}¦•ÕaÛq*Ò0'´Ä;ÔhDä×üT¥‰VeÞ‘YV•çÖlå®ó©Ö™µpåÈuš©zˆ·À"‘•Úo&P¹¹È1‹ÄPÞÌTqNf…ð†éÀ±þY± è˜=b 8–õ‹VÐÙnn.‡dÑñ\X«O50 1X&ÅÊ&xÙÅUƒw`àN8+ÚzgÈh1iÏhÇþ£Ùš§d§ØPõ†LíßvV Û†ö—Ýÿn5ÿùdk÷ŸoÆ¿O¦išäEQÚ…·x:d®2ôO´bká²»o«î¡£\pN¯a&Éφ I×b·3#÷YKX›Àta`U}cDD‘~w`oçTíÂ,þíä ´ô"»–Õ8€’:Bðd¿_4˜´›L-&;2lÆb>¢!z³¯ÀÉÖÏ|Øè-vÈ[°ù?˜¤‰06ú㬠¨n&Ⴠ¡Ó?ó!Ø@aÁõ¬tGÜG¬tj½÷%Q§ ' Ûz[³o½{»„sÀ¼AtVa&Í“é3ë/¤Ô“iÙõ’F%¹ò$TQ7†ÞI˜Y¤£`ÿœ÷¡˜Eå…›ÑöNª|¢‹¤Lµ^N¦&1:³º»OþÃBUÛ§ÎP`DOŠ4­ûf NZ8 •mßÁŽ@¸`“e1f> `Ïû ÍùEµ§J­y·9Y"A0r›xKâU1ä IÅÃzM¸p»$dˆèúbœÍ`1ñÚaµMKµá^a„Æ‹a»“uõiRZ§«u9hY`ÑZf+ó3Ñâ#1ì‘IöJ¨——vÂÿ(O'R¹eza­î9ìÙ8]ŽÓÃûd%~ó¿c×€3(6Ã0©Ñî !ÕÜw½ï"¶Þ³¤!4W©t[=¯Ì•ѵ(æ¹€ð_øßïÒÃmÝv/*Ís’&ižYÛÕ± aœ6kt§ «è)LÒ;ò‹§ƒU·LfÌ‚v˜ifÆâÃp¤Ø礅,\/BVWŠ÷Ò › ÙĶö_ÿ¨ÂW,àÔö¦U!ñ‰Q4ül³2¯{Û…{•"Xƒ“ ²Ìx…rN/|ܺ€½°)!kØ!KÀ>N2§˜,0a^ìŒTÉ0û¹ž3k¸òÚ>I!$ˆ\ï¹ÐƒÇcÀ£°”ŠIŽ­y0eh'φN Ê‡ò»å'd¤´2ùXRðxêt@dŸÈþâ@òBc(þD¼ðˆk™kw'9Øh‡Èi=³õç \²$˲.l†½D¿iÅümÅfÚ­"8ëÙ÷6âÔ[²ä®öÌÚöN9²üBÓ1x‘wŽÁ³Ò؃»ø++ʬp±­U¾Ì„vŸÛ³.ø#Ý<²ÉÊ4Q©?u¿fFض/&*1E–ãמQã‰k‘ŠlY)”UÔÁD&2ÍKwâæ9Ïëaj+f¨•¢á¬ž¬ÛÏ´ ¦Îë©a¼Dâ/²:îwˆl`S½ðk W¿ž= VØ<¼òîúRbÙÐ2|a§Ö•f«^8IR}¤ºˆe¹©Zc (Ì3¸¦æ%”ÊÛå¢:{›ãeÛ©‡cQÈo„™^X¼‘õøWO=ðÔu)-ó¤è)ùËjöPq}CŠëè‹âIÒ× ›g_Ę=¦‰nùÊ'3xÕL°ë–Ø›²~àtÞCð°Î4`†À±ªu–A+€ŽK8ås¸h&ÁC( C(Ì9E bœúD\]@²U,@Š/ç“l`¦Å·pñ,x ´Ìf#å®ãޝvŠñ]hy(ÔŠø¦ v%E褸Ÿ=Õ9ˆú¡¸µwûÅŸÈ])Ú¿Ëb)~FùÆSÂSw=•{êÏPó8¡Kn6c’ŽîEÆXü§ô4ïZÕÁíÄgžzè)Ú»®¨ª "° õ=zfÑÛ£,~º„£á3äÃØh7 óLþ’ ðoOQ.D{•ÀC;ù«A[·|Ê4ò2àÀmàlvóŠ„–ô÷‹§ÐáñÑF'=5M2±Ü$z¬3ƒfMØàè€õ{óªF{–Ì2eÁd¾_QÓÔv3·gkèœÜ#(ÃOk(oÃwˆ7¯R¸{û‰ªv߯¤IÚÛWÛ½£ç &кžù2¦¾Û»Óq¿7´ÿɪ.ÚøÚjŒ 1RüÆÒâë²õ"ø å-Œ½›úˆFqÀÀ€S¯/£vu Ô9·F€”M!úUóªæ!ÔºŠi}ð; †…(æÛ•a‘I^öR{}µÄZdž"E¥½^úg Éj™rôpŸ€\ýùíQô€M&£ûM)ü”eô ŒÙóoL÷Ð0“Á¥0nó”*ÖBöZl¯³ŸÙ˯«¢"°áøí|ôüB}UòµT}·" wSºý´®‡¥iË€?b!ÏM"ÿÿR} œC†u>ÁßBá4ãkƒ@ê ¸”í=[’oûaè–¡rï¿C0E9lÿlj½Ïõ¢/ë7|Ç"|>UÇV‘w¬(|ô-ˆpõ\0öþ‘DDñùÃÛ)"õ…Dt­)Y+ïêš î\içΕ²y‡Òí-´©HŒRªPîv™HR)µªòø¼œLˤ°½ÈJÚRXiNž»í±Ð…ãö4.ªº27åøÞDš¤¬/FÚ®³ÂÝ@—Œ×4´}Sa„ä¥Ö*¨Ånúâ"W¾ôÎ’ª-¸Þ‡ûýZkGK•Ÿa¡v[Wã`·¡yõ0ÿ{P`¢¾il7zu‹·ö»®ú\ûêš½)»÷£Q-˜}Nz^XȬS0¬{i”]óçwâ#÷õgPl~(¾ް x*±Iû|$*\®¤¢ óC—ÜÁ{\I CÄúY÷ѺˆùÆé1ÆŠÉc%(/°¤$Ý›w÷â{kí_`ÇÅœjqçΰ7ÜUž£ƒî°î[0ƒöÊÒÏ©NSXÖ¤yHœ•±ë] öUíÆ«Þ$¦«D”ù¾ŽÅtixæ)ºˆ|¨CðìÀS„yêðáÏYôÇ/™á4ˆˆSMœTâ'•Ñ[høKvã¯%ñ·<½ÃSXcÍæ‹‡öê0$·‰mJUDº"Õ…8tb€ÛµM¿ùò2$v3gð™;BØ>ÀßGðŒø.ž “§8+€ú^ýfN˜ÄÇû •5wUŘEÌ Ãr¨:\{¨Ö#ãCìåP ï¨R¥2rÁ¹§ˆ€7m ðž:|GPRë\T\§øÀ%lE Këo‘¹ïšÏ`Eª×º–¸å…ÚàÀ-80ƘDÞž€Fn7T½zi¤¤%(ûzîøÔ’6ÂVvùmmW)îµË×m|'‚Ï Ð Ó†Ê²jlmâÕÍ0a"Çr®¡zɸÌs'½qÌ‚ºëžwŒ‹J»GëRE ;£¹*6¬èM¯„àÒª»KËÀaó)V]aÕ§HAíGV’»SÖ g ëÕ.CSžÁra+T£EH:±ð¼Ñ´ÞÒ2ççŒw[I¦æÑ•-.ÑJ{½ä°[g¾gúÇ.,5 ’œi‹7¢1k¨(*Ä?47Bô+Ögs²áºÐôÿÅhJ·µU‰+p÷¥êrºhíl\°P±ÿ1Æ’RàŒÄ§_¢t¸” îÍøxŸ_#Jñn_A\¾Û kG¶ÄuRR5èS8ÂÛl¯¾ïjz'v·^Ú?ÿQÿæ¿endstream endobj 119 0 obj 3825 endobj 123 0 obj <> stream xœí]Yo7~ׯ˜·-Œ5:è²%Y ûß—ìn²ŠäÇa·2v6X'APèáY,«¾*Rïgi’‰YªþÕÄáùÎWõìäz§û<;øf >œì¼ßi’\ýÓ}àôáùìÑRV̲Y›´Õly¼“&mÛ¤ußj6«Š¤nÛrVgiRËç;óÙîògY'OMù[3[>ÛYþùüu‘§"KŠbþP’IYçÅ| (úõ±¡žjf¨ç†zm¨A¹¿êÊP—†ú`¨Cý} DÅšaãgäuÿ{Öæó#"oˆœyKä,pFä;"¯a»¬· X}=½±1\ùô&dµ-ÿi˃”/¹²ò—?ídYÒ˜u¾Û]ˆD4uÍOw¥ld¢¨åh5%Û/“4ÍDË>ÎvEÝô=0é’ÈëZŠÛr½óæ^ëX¥©7ð¼JÒJJîЬ䈭äžê,o»eU£J“¼¬ª4Ÿ¯º¡æ"µ¾®»ªyÙäÃ\º‡ª.5u.ùQ$i×¼•3ó;kï‚~¿%’5ð–¾²KYc¸ì Íön·Mê¦(ÔB y\À9gTóç‚ ¬a }-©lI_s^µÀ&qJV|–¦¬·¼JÞÔòJމ¼ °ÿ²“w‘K¾–¹–«UÌç~:eÎúbü8³aKÍÆÅb Y~L»ŒÉÂ%dÍ)”Ü‚TÏY%‹TBieµÁ«¶™¿ÜÍ¥`¦m6ÿŠŠ>¥u¿We*†¦òª¬ø:Ò|õšÖÍÀ»²óô;}„% “¡×¢4˜ÔÛ™Ñl¡´×z8²dk¥Ðêk¿r+Uûʨ§a˜YU1•Æ‹63m{½ì¶š3LúiŽô ÃÙZy&0L¸¨k,§L˜è°Ï%)g.ʺV'jV¨Ñ·êHmçϨòO»P@ºñZjÉ:VJ¬@§x¾’‹m)4­Ü.QY‹1URåæÒ¿—‚×bë7ÖàeÆ«­áÀ˜ô~ ×ñ5m?¶À²5µK[õ\ýš$é+‘$<ßµ5ÌFn@½5sÃ\O0!Ï °6Ô‘eÀUidE­£¼Ð¶‘eJÜ‚¬¿CCjŒ¨qã‹×¸†ceSeÆÙ-0Î,C`Fß!‘§D® ý·‚Õ° ʪ{U›ryEÃÅ,(Ùð-†dTD¢Õ¶±µàw‹oå±×E5¨7šûõ–[1eÍ™âNÊ1ÁïbÆë¹K`µ€«±Ž™Ëð16,kØÛ ì-ä)‘Ék!UL«õîÕ'âÈWTºãÅ ´ƒ‡€… ‘ípæ2oð”æ¿ˆyƒkÈA¼oaÇQt»ÀÊ · F÷8êÆçR^°âÄP§ÄäXqZ?ÿôÁ«ût·ùÄ9-gŠ c¥ä;í{K ®`a¸‰ÀÄç<¬ŽÙ×ÛˆÂÁl8‚ma-c-…§e OÉÔf•_Âþ?)°²»O"±_tØÕ5…ü²õÁÖG3Þò†7’l$dþÒžôÇÙóŸèúÛò1[¥‰È9&ÈÝì¹R]’&ZÀ¨oÕ/Sv<Öh㉓0ûŒ—($a!úþÏ "HðÖPgÜÁ÷Qð‡U Ç"¨‘;å1Át 윑O!tìjMZ×dØÂ ’Ýi*Ž‚ ÕGƒ÷Ff2 ù BÙqH€õu@XÙr÷¥¼T¶øö P5#o ‘.êõ#5Åöhß+Hõ•é0¥Y¦pìc£+ [;SCR°3¶%f ¶»—rðím²!É{ƒô6…ä1|ÌRÍuÙ…Ï_Hý•T…½Rìg¥ìé*ÎÝ|½”äISfZJ>î– #osg¢º’ dÖ’®;3¿¨ª$Íp¾ÑCRáÑïM¨á€>j¶Â`Ž‚" D†5*8ÈAmCÕž$¨méB÷<6•ÓKÇš:-K$Ùg|ifÏLU3IÈà¶-¿~×…àÂæêO%lVà ÅK§þ¢2`¸\½¤¢X7k+ÉM±È®Y<0¶éqäô‚ËŠØ Æt­€ ŠYÁð‚µ•Í‚é6À€K8Äõ@JŽ(6€;Ò¬þ¾­´‡ØKm¢$*ÑÂW7\÷°8MæŠÙP« - ‹!¯B ß÷šâûÞLžâ—ß’Rù®W~eZr–Á°&“¶n`’qk|w·‡"vf0WPe3™4)já8͆€#tLªý誳ϱ aSï·(ŸØFç)ˆ$«Ép4ÉS¥ª(ÅËyZù¡Ø3d+áAáþYµØÑÖ-[‘Kyûu7S^ƒ¤p0q7QnP#ÔÂëí9 ûD.¡ò˜ÈDÊÝ’•räoŒx»`í¾„]„ò,Õ©–»†aÖþ„©¬Ûã¦x¯C•âx&WoŠ q”^£ƒO4U|D&ÞÈÓF.8[Ú)’Œ=nVÍòh{Ø¥Hš<Ú…ÅI±zÄ)ùŒ90û>ÊG|Là•A(•!*!ž£Æ¦ðÜV:åh’"ˆîO¤Ëì›Ä½_à‰Þ€ ƒ+ÖÀ&†¸iÇ쫵i, ¹”eCö&¼#ÅÒUžå`«BOØœ§à¢·—<ëÍåw4+Ôb4~MÈÛvêÅ)œ)Í o»PÎ ïÌ‚ûüR6üCºãÂ’q¦E2Ý—FÓºÐâÀ.#Sô³Í=,–‘ãyÜD`.ë7ªMpF¡gùtzG$Fè.†æ"± ê.=߈qu±ØpÙ{ü[…7Ðbò£¼•ØpaJHÒ˜üeÖÖ)ÁpN`!-¯*³”TämIÌh±¾ñ)×ywxì¿FÚ·&Àö­s¬9¢‘[¢Öv¡ÖìHCô/ë±D:Õñ…JÛÒÆA·Q9D®—$l^§Ób0õÊé Žok7_0z jD’±áõ”Û¶a/f“bûvóm¹º‰jVËzé èÔ1lÇ·šíË,#Ã’mY®ÊñÛ.àÔ°ÐÁEf­ÿ2áB«©ù‡5c æõ”Ç=·äŒ¬x¸ø@÷¤šËRŽ3ë}PÖÅUÀ$Ь¬%† ³cW¤÷AÃ'N™'BZ<üÄ!c–l]œrë\:j˜»ÏH2§™˜ ƆžÉîU›*(PxbëyJÆWÚ?>´5q]ðÐ/‚–ð~V(Ðo!p•”ƸW»å-í¿€Yà‰p.ÿ›ä…M›ÃýìýñZØ8× žXL+1ù¹‚SgÓaÕnÍCÒUi|)+&”3ªÝ{2"ÉêÞ&VîÓ¥;ù‹) è­_0Ê·“Yè·cƒæÈå¥scÊkÓo=ÐF5T$§H‹láÄH6_ oK+‚2\qÀ?º»2ê ßrÍšZZEâ>·\ŸùšÈ‰|fUÓw_÷èëS"YÇÁÛ³€Ü‡Õ–ữ*C´˜võÕä¯j2ŠìßóQcËßG‹UKŸ¤l ð»[¬‡;O&­fˆ“UšdYÙZ$äòJóF=+Ð?”¤ii¬’­Áè‚áè ×­L£P„=‚4B“ EbñŠá VÆ3yùÆæR~ý^éèàL¦èºÊsγ1 öBífïÑtxb”G¼ð Äý0àçÛ$ìuÓ4‡lzôσùó¿îŠ6™à4ê1ûˆ'Ò5½ÐºäÁ±–MÓl,m– –˜¹B”аŒQãÆÊ?ààÅÊ;£é!‰ø¤Çx«ï×ÉŽ-¿nkŠ™ZÞ ÷ómdYZw ÇþÍ#Ê]{ rܨÊl;0Ô(G¿R:&¥^f†ú·¡þÊ :º†Ÿ‹¦‹e诘üÏé2_§üå¥{æMFo±áð¥ïbõ2“«„ƒÂäXÞëQKJ¬S’^3'±£¿¯Eñ+-R(vÃÆ?Äu 9}Ç9ýG“ T`º> stream xœí\Ys7~ׯàÛ’[ædƒ¹Rµ‰#g²¯ÌT¶ÊÉEQ×)‹’²Þ_`tó ÁC—]§Êí tãëFw?õ ½PÿiˆÉåÑ7'Yo¶>*÷N~¬‰ëÙѧ£<ˆõåNO.{ßTÇ8ìA‘öFçGaPy˜U£F½TYQ$½, Ù]}è¿ ã ÉbÙÿÕPÿ6TÏPo õÚP?êØP¿êÄP?€v'ƒ4 ÿý¤ø"Ão–QfŠÙÑ™b®7ƒ8IÓ0îOì¿Rÿ΢X„Yÿr0ŒD†BprL –Džù‚FÜ0ø’“Ã(Ríò¸?¥§e‡`>úU!Õ(¦=e­ÚkãBo©jE²èÿ9(‚,—Ššë÷qRôå@BfŒb=NiHÆ k0†s2rn†AN§pÛ,L·Ïƒ( "&üýë²xKä¢Ù(¡ù@늈à “F=–œAÓÔÙ‚RVõâ§êaVD»ÃäÎ6ŠMCò1ÂÃÛÈú0» ÅU$ >ÿ6à–0r¤FHUÇTô/Â,ŽDÝ6N“Ô#Ÿ,·åc8«–›HQ ¨«7Y¦ &ÓÂÆadIÒS§±’Oµ\½EÖj›‡Sôš­Jk!µ€LñT1R(E "Žéî; 7{íœg¶§îèSNç½ìimºi~æêBâ œÁY-Ç»êšY‰LÄ ?R6P­ŠY"¸2ÎJ2)Ÿè†¶ÏÕ™Öþ±# 8ÎŒ¨’ȆàòÐJe׸9´Lÿ¾­fˆÃHMVªPÓV³–”Z¤ÞŒþc5e¼òÞèÍÑèïJŠ(²ÿX™°dOgñê†úÌÖP¥¸Ô«åf‚/ôºjZ; ¹&ò†È‘¸[È3ØàœÈqC ÍÓœ¸ºYLNàlKÈÎ6˜ÁÙ&¥&ÅʓȌέ`ÿ%ä`—3‡[º„à=Ç»{M[ZËÖ˨µ: õѨô_)_¤2Q6˜Á×ÅÖ[{Qš*ÐiwšÐkcD Û–}ošB·é ¡,£ÿjìÃÖÑÆ©¨PS Yã”LsÇS$‹âñK ®J楹ÑâQýíIЈ…ЈÐãG 309ñ©ò9T{vÚn¡‚cP9÷ @lâ1äa ÇÅ È›À‰oà*ð`æisb•ÞŠÇЀï¤5Ê—­X¬í¥Ï(0mYÀ)¼‹¿W³€´EnT–Š‹ßP›—`¼Ÿu zʽÞ8ÊûּΠ;TpóþôI ëVC& ì›<½6x°ã¾ÕF&á~ž½Þ؃޾í%È#”vúP%À€<†Oç¾nØ(¬áÄ„ÂÆp\<ñ;kg6W‘èͶuØð0¬U¶ Ïݳ±£KÐôE.Í{8ש°Ø3Á·™ÃŃ•5¸€ƒá“r囘-Û}Öí ζà݀¢µwÜwŒ–‰ óÎs<ˆƒ0-¤¬=w}Q¡; Å‚ ¡‹u™ŠE’<—(çdQv­˜Á òʹaÀÛPéÁWª•{YÑWµYÛñ"ß½Œ±Î˜rƒdVÉ/`ÀÊŠß¶c¤,*Co‹vT|\…”DDQÞè eQâ[ÁØ4J$N÷àÒaÔün@Ùn…$ª­ì¿Ó:›…EÄßÇ4VHc12¥¢ æ©âvHwßv á¸1 矡EŽ,øîÉ«)9LxêÃѤam`Ý~ï†Rç´hSDz×á6Z“ÇE£5LÑë‚"Ðdv¤[&êy’±0å*¼à`dG5ô3|ak<5PF-ÙÎVI£8K:BÙK(fºF!â ǼR!‘†×¼ˆ:X953£h¸+x·¸£Ò¦% ²4îëf~¼€O0ÕAºkõÂy1.6RYÿs­naPä)Èenz7 Xd½…Ú•f8öC?¼B-hZ;%†cj0;ÖˆR=ˆv¢«Ê ˜§¯‰ÄzÎ;ƒ›³ÛÚjt©›³tÓ¶(?7E¢©%aÄ©ñšZ'¡N¹r ¥¸)íë€eW‚Ú‚:Q±ÌK¨“8áùpÖM¹>;aVƒƒLJ. ü‘«f¤mãäí•ùxD…Ø)à Znðv×ä![[¯s~–¡,<;€x­x‹Îk”Kƒ06(ÇØdŠÆ@¬ H@¾bC’'Û ; –%„y}ä ³-e‚Áéz6™…WÌ…T‹•E‘è@M¢ŽRå|‰s]^3`{gl¾w牳«ª1À…/8t• åAAwCëµ€Á‘•yJ©Ü°¬Â¦¼ŽI XŠtÑåŸ"l¾0ªXJCuIŠ©X*jKØi¯$f{rlFìC·Ì–ݕٹ­}ÍPtàlWaŠÑ¤csía2X=“¹ìípVñ¹gg••(Àa¡ ß é•H°¦cGtQ.)DHŒÒ/¿AÕ`ëŽ+Óö{zl3jaìé4²ñWAVãݸ»0§§Ä=“Üý… ’4§š&r~&Б!W…òé]%mÎEÌ”\"¹qYíñ]Q#Ú¥Ú·ÎEËgÔA'×ãÀ¡¯±k¢Þ+q3ì:Ì×*jsCqêdK‘GíÀ–}ßÞXoPÀ‹ùvÕ >­,ê* Ôb«(²«nÓrP›àTÌËÎØßB>½î4>¢kgjw*µM}Ë嬉 þ–krRP”pV¨ò˜Y×Ý€vâpÁ„E|„v»].W>iÞ¡ZÞÜä5Ų  Ãš]>òØi¯†áºVK*R·Ò„Õ°lF¸u¥T6¸j ¦ÛÞ˜p_1Ï̺֦i +€‡ SïPýD{¶n!#€è€ „«,î¹ÜÚåì,}Òh¾GåSœê4Œ®÷-K ô>\Õ÷=èk×? ©t<Ïx‘,!Æ2¸,kwÇÓY(õµKùä)‘¬p—.bÎp ®ñ¼… 6—l’Y]M´›Ð(×J™Øý êG‰7æîÜ)|ŠkB¾ A:ÙSs »R§Ð§‚QN¡kü#ŠÙ{þ:$>ƒaëõæ„}4ìw£*th!#a`O³ˆ>¡G—FÚИ$lÿÞöß³)lšNu *Ë%£nkwŒìâúE+uâXýß>¨%ì€Jôíœ÷ƒ~8Žc“Ø}Ç×Êo|&Æû &#˜^Áz`0Ññ½&i„S¾oæ^µ6|·pòÃo½7À6ƒÍöÙÝÎ…“y~õU-ü^mùÖ_⢲±† õ­¡xû‡O;øDOävy?»°ƒ½Ÿ„2¤§6Ø|úéKxŠø‘Øèí¡fgÞkü'œ˜Xq8 A/ê?Wm@g˜"}‡(É5yeØ3ð‡ÛܯY/""“ÝQbÓMöKG Ñu ~–'ÿ'ÿK“õa§Uóô?èiÆ>óCzo¸ëþ,wl>¼?:ç½P?Š>iu‘" ra~ümŠ ›¶?kApBèç%N°†=†°«NÂ3Ù<+´@–쉒ìÁ#ù„X” «½×ãp—¼?ݸgŠøYIÚô›DTçCéý+i5}ƒ‹g°Sÿ|Äê¤;à ·N0€oAèûö5õa-©û†ofn`0ÞW7¸[Êžå4î!eÑ$'¬ŒE“§ðæîÙ'Rž”E»Ôp÷ÄŶ)|…a&tÑ@õÓ¹þð³6ô­ Æñèè_êÏ_“v*endstream endobj 129 0 obj 3353 endobj 133 0 obj <> stream xœí][o·~ׯ8@*>ëåeo ÀrœX…j»±Špò ›/‰-)’œ6ýõ%w—œ!ùqwÏ‘’nb ïárÉáÌ7éŸVe!䪴qúaçá7ÍêÍÍNÿxõÍ×#qýfç§¶Pö¿þ§O?¬öÌ‹B¬º¢«WG¯wÊ¢ëÚ²z«ZM×U«F”Ec|ØÙ]íý`ÞQ¥ÇüÖ®ŽwŽþòÊüºV¥…Ö»†,ªFéÝcO}ðÔùHÉÚ¾S—M!tÃ_gäë§þ³¼§Ó¡èÔî%‘ yNä‘·DÞ8RšO|ô×! ÙOßüýÏöoðâ±í‘§ƒq iY,3þ-óPjÑm0Å38ƒŸ‰|Gä)lû¶Å<`¯]Cò#‘W°‡Õljó­å©ï糞áØcšƒ$ò‘¬€ žùr®3Ü÷ûrƒ~q[ÜïV˪¸v±JZ÷5üpµ'düš’!g$c¦ÿ.98ªBZß¹]ÆÃuшZî¾Ý3LÒÀϹ§V†RU]—ò¬ÕUFp°P3®ÿÍÚ».r÷´ÿ4’l–’wFÌ™Ý9œÎq(”fúHø‡nJcòF]S¥ðÆ [™ò¬E«¢¬E¥Û¢Ô6kñÊš¼²¨¥”­4Z ¡eo`ásÁž—vö­éÑ~|]馨«&házou»Û2ºa´ù’è ÑÔc¡:­{ÎwEe¬±ÎÿºmÃz-­ÿjfר®l­ûÞ®{ÚzYõÈQU-dðøÒwrÊ^<'ÓvvÕi[c6Ý{µ®]wµV­ª_)A>ô]¿cOiÔ?RÏüµïvÙlØ/úçºnŒ0R߼ŃQRAó<˜£›f÷»½ñóº…>E&ùUIe\VŠ–û£nÔŠy ëázHrÙþ^éúÿå´š!°÷0bW¦ëÇ^6ÍÔõ2ý>æàⱃÙÖ€zÁc»ônÉkêŠý.¨+I(QÑÓš“¾Á£=eü‡¦[0B6.öÔ›k2_çƒK!<••s)èç€uãËŒÉÉò» ëqÊ`þ>ކ2^|ì˾…“ľ×&fœ $‘ÏØ¤¤…¸5µÅn 3x‰û;pÝ/êXbq'ügØ;ÿÝÀˆîC;¸£p× ©½“…PíG'^gž#gqà4B‹díÉ„‰/M­ï‹ eH¯/ö–ˆ-^ºc¸â7²yYo!ÌøÏÆŽ¨×Ò`Æ/{Âæ­ŒÅΆªÛ1ä0ö¢r^¨‹3ž’›õÂbVSv‚#ÿ’!_zäØy®Æ}I"Q dã•IJ4ó$2vYÛ"_ØPé¶¢‰àHdjQP„u{¼ö ç÷q×fñ -²¨u뤅…/a ¢ëºÐ]×£¾ç8…Þ&Ø!übÚH?cÀÚz˃º®hZU:xµñè™…¸!GdTA9(;6žl²ì{O) ê5ªî´÷klÁ” kTª\ö5&ÚÝçpF/½Nc»ŒÍn ‰÷ ¤îû¾úÔÂKû¿ƒAŠt]˜ÐÁIÑcÂ6\Ö¡iÝh©±Ït‹:¸Œ5°g–š‹9edgÓ9åˆ2*$"*…“DXµîBÛÄÆûƒûÔ¬—Ö¨öž<ÇÒ´»驉hj&ºÉ€Ð í•–|Ü·¼éàÆ6æ·Î‰LGaPc,›Rdz,$²â‡|#ò}¬né•59DaÌǸˆ¾+ v²~¸è¼ž1dI iÁ0€ £–I†Ð,YÈ6 ÑWfxÒ([–¿åJCàâF[Vv…’…Ö±3úJ 2'˜~g3øÅ'†Ù mf¡ï5J7²Ì˜Óe°ææ%6VÆC&Ì‹:ɰ ä=âÙôC> HŠœ Êèrg<äøâ0+ÑX _Ó*4ƒ>š¦5½dÆ G¥'3`(‹hûßÃeÐ6£´×cÓ`‹æÑÇ ¡¬Š¶³¡óÐnÓëu§Ý÷ëYŒ¢Q„u~V%UqZ(Që£ÍªJN­£ ëý° Æôzô÷°yµ–r±0ï$‡LXÇ‘è2gnsïɬŽ¢n,®NóFxÏ-Hy£ ’'ÞÄÒýv/Q%êß}˜Äœ*MÅnbp_l4lqÌ{j&^Ð6Ý„ì–s Qi>{ÂEÉuŤã ýÎìÒ‚-µ¬óûÿp¬©]_”GHXaÁŠ¡Ýš0M%—gë¿_¾EûÌ–•c ¯,ZB÷Éb?§ãñÐ4>u8*…~CÔU,&NÎ~‡iº/f}ƒ¼™ nqÎev܉`Fiõ8%–ÄcãÇ3€LÙe}L9Hm[Hí¥Ö¥UàAå6ïgb³Ù׿pnj‰k+ìÆpÕ¬P¸rÓè˜I”Þrç}j“%Å;óXb?&Kf]ìͲ„C0Z¢ª¡ˆÂaÊ”¦«8ŒyNéBuMhäqAÄ!˜:.½gþW6ç“!»ÌL•P¡4  Á†÷cl¢Š¢d»|œãt¹e÷’„K«Ãm ˆáÔ20o~N‹ãÝ…éb—Ö;„aNïö Û”Uïe„I¡p'´-›&SÌÇñÁbçÃlÝʨŽù'ñGYŒz ª¢fcÁ!+]™¨Ìûªðc³ùÄMªî¾ËwY¡dCYêÁŸ ÛÙ÷t§íZ@ó4³A¤0c¿Økæfw¾zä;_MËŒ;_FIºJ“ÇŠãÏp[ÃÖ_µ¿aýÕ¯·…0é~!'Rëd—uÊåŒÒ![äû"Ϻ” wø>÷ÇÈŒ¬,}nhn'pAUd$½ÎäðC-b2ÞØ$3±:¦û«¦[&BIÂ(Ýï n™3D¾#F|³Ù`?!†Ãa6X˜€ þjé&aG5ÃAv¨¤KÓÏ€JœÆÎzÄov ›8‹5͵å=÷¾.Ó<²ƒ»·ˆ¹Å[¹šÏ÷¸â( l}sv`¡ÙÍPá‰D'ðLÀ.1̾EþNªÛX¹ž^íºBÒžÅ#:€pIÖ€¥yºW—¥ådRT¨j+5¾Z,„+ç.€’œ æYIc0‚ݲ%N‚ã*w æÊc–;h7Éî×°4¸ëZ˜Ùb’ÄY”¦FàRsq!zUÈ΢ËBÕƒ°°Ò/Œi@Æ^™*Ë _ÇI5Œ&,¹ºÔÀÙ¶=\ÁU? ÝèÌFKÏžø±„›‡J©ª¦¨Ê‚3ÜDå`!=úœw:L«+]ˆR+÷Y¾£ÛÿC¾ÛxÜÒ3œ 6â×°bá(섵 ‘«#ÀÖ!r£æ“úsõ$H¡rZ„„8ð©¢”­Ò¿}U´÷}0æÜÒ[ÁpÇŒXUôNC «ñmÊüVën#­PøÌðŠŽ4®YÃM4bÖì=ñQÙœ+±(tžšƒ½K°2 e-vÍî¾apKêàú¬ÕЙ-ˆå…36xªHœvæ5·;6YœKahcI0\‚ߊָs¸øƒœÿB2±>:ܨÐm9,`W&w(nQ^1ÞSÀÃI $IG¡ß±+ÊjÊð¾óTÝ¿6udÄk ºo>ã9¨Á{jª¢îD]¢O#í¦Õ8Wܪ[Ÿk‡î–¡[j>zê½§è6›[¯Kÿð:7½û§äd÷pŠ»3l6cã'¤Ùijv- »ü€¼fw:°kØ7×ÀýPn4ÁApwÊmБöi}fKçÝŸ“@íè4ü O‚_W ¿gž"³z¡7Üyüóª“uÑÎ3þ ÈAvRžÝ€pÛù‚û}¨Á öû Ÿû°3öڗিëc ky&§E'Á¦e8÷ºãéôwz^*t†5ül +¡TÑU~.‘J0EaË| ¹}÷;Sð%³‹ÿœH7»ÜÕRÛ)ã½ûg´0ßÞ‹NÓ0ežÂB|¸Aºõ’×wàÙ0¼KÐŽ~½í®ÁM—ÝŠ¡Ë€0qüLävãɽž>µ;dQçà 4*¦„`š6ªãŠxÇK´Þ:¥—vÄîá×6QyöšNd G:MXÌY"2žûø5*#ýÕ©—i‹€4ömܳ÷µR&ä’ïSaÊ_EÃî“b¾Áæ \'ìhÍ®?k€/3c$¾Óê’¬_vu»ß§éeE©®¨«ŠÎz ›}:{+ÐdÎ5äÞÝdßMh-ÆÓpƒ»GÏõwe,Ÿ£®§­¥ˆùô‰ZIÁ1!ö þOŒ$¿tô´’8*šEŠD¸•ÔÉ?Œ¤'?5#™ˆ~C_«¦*Ùï/ö²õörß§ÊiíšQµþT]qö ½Ü£{)Ø2Fõµ,*I×®lc«ë²Ü`>اÒ nÊÁ X|ž1âæDg÷?q‚1Êžhh#ˆ¼‘oá5Š¥TÌž½¢Ã“lÃî)ð½™Ïy¤Ëq‘Kóæ·‹}eÀ¸§®ãä3ÞhؤV o*nY·Áú%.ûyÁË£2™%ËÂ^§€lXº‚¼ºô—U(ê {ÉËÁ¶®­ˆê‚Ò 3¥_ªS…êÚÿIFre¼ ŒŠ&àe` J¦³[šì a¶¼ ^DÊ6À\‰ ¯´ÁѶ¹óFò”ë9·•Œµ¨^€Q¨€ À;ã—5mmë§dWHì³­=| ô-Ü™Ã3WêŽK+Ï ‰‹Ó˾’ÃZXµg&±EMmû“¹mÍÜnx {Ò^Žè}„Üv¼õzlYªÃ)Cl Ä­m½†h ¡p€¨„'à×S@ѯg€¢ §ç"tä8uu!Z*öÿª>tkî£Fš®õÙHÁ ^ˆœÉñ¯H¸CÌtÖÚÞÙšrì‘xx= ”2wZŒ•V.(\BÀ\ÌèɆ–±„V…nüÑ>æ(0kQò·üSå€{¦ïAUI€ä`æ!»\*ie§oªªEJìýÖ²+¹£Uà0dÒW_˜V‚™9u-lJê™,/áIÆàÍÇŒñ-‘áñŸà8œw¢Pݳ£ØuÂ!6ž³•è‘Uéë®Ô9c‡È4è|éÒ=”Q©ɳ _‚A]˱ «g4ãUQküóGÙ’xa> stream xœí][sÇ~ׯط즬ñLÏ=o`À&&€\N §RBB±d$‘r~}ºçÒç랯§w ÇqŒËÕžíëés¿4?­Ò$S«ÔüGï÷¾|Y¯N¯÷ºÏ«—_§{?í5Inþé>`ûèýêá˜e«6i«ÕÁÛ½4iÛ&­ûY³UU$uÛ–«:K“Zwx¿÷z}°ÙϪ¤Î*µ~·ÑŸ3UÔëÛZéV^VUš¯Ï6zx–·ÅúÂþ|#?˘›ý2IÓLµë+öñÒ~<Úì‹å*­×׿ÿ»ÿ\ÊG »9×½t».Õdcݨõ¹ÚDŸ½ÀQÝÜ·ÒëÊoUXûÆ›f…ŸÞÉX8×Jo&3³·ëo6û*QMS”ë›5sճߌq±Ï¹>]!PXà¸;óÊ œ¹ÌÎïiîòs¸:e懶õ]Àé™5Y‘ôvqtEýB©ûåý7Û»©`5ð݈gŸú›–,Ì;¼¡ ŸÒ8H¢~·Ëù³™\ÊMIÅ ¶õî§ýñÛ~h÷‹Eâ}þ‰˜U¥†a†\¨`ï(DßЯ»ÿcÖ¸D‡Cµ¶š3°›sÂï}÷wüÝ!×s2â—eQjÑ}F ö7ƒSÐa¢^3œ~uÿ8õkgX¿q<# +$=<ó‚7i£mø†oz Ýç4›ebuýŸ¤‡ƒiEã§ž6 XÍ›Û_3š/¸(AŸ6ÇÇ^²¹è¸ÂÍ­ø+†â 4èé ¬L HøHCâ`©Y“ä… )r§AÐÇÝ{dÚi²P¥M<4FC‡e°7†ü ý¯5 ‡ýÎæW ÁQ ¬÷+ËîþDäȘ·a¼ùåeïió2„cÊR8óàü”ç=lÎÝØðNûåQm8<ðÈsÊpwÚ/äSØ(µ\¹HÑQê :•2I~jÑEO÷ôÂ…ƒ“§:ðÜ€ÀGÚßÂ[º¿/úšn‡ 8ž-sëðŽ.}è^Hó;‚’dóW‹ OlKô£—ŸÌňð0vì´/§Ò·Dh3Š5ÜÝoôšÎé¼p çµáçá””‡6âˆjI8Êg¤eª†XŸÿ”*’è"Ñ î‡Ø£¤±†B‰$>ž¹+_„›ßAh‚?ïĺÔv”¨kyÆ£Èõê…çñJ߸!δMJå…%ìCÛzo[' ˆýÚŽ:³­o?ST,¾§DÄÙiPêup ¥ŠQÉŒÀ' `‘ÙQ•+N§.ŠQóàö×[Úd,p„‘ç¿4>ÕʽÀ *´Ž‡â™{ÀYíIÀÂI¹µÍE×¾Œ‰•hÂç «ä9‚œ„«a:n,ù÷Éàgà/ z¦Í*ìÀ€Õ~Þ÷BœOþë/À}Q2ŧo,8„ë f=r;Ïëš`A^wNaÁi–;ÖîIgá(îÇ £ì¨"&®b0ŠªËF;ò@ÀúCºnVuãÎòñŸËÍ J‹•ÿr¯ÒϽWrC1öÁk;.é)¹û‚vx*MmÏiߨ@c$æU»ÒæÖ?6¹Wÿ¨ÚÊÖ?jìN“J)Õ(“dªÕò,+T±.ß3øž˪Ñ3-~¿,ê¤*k§Ç8{S4F…²müžvÎÆ¬®ÚõƒMV%Ú’j Ú¤lLjl׫7í:w—™N·‚h\ÒF©¼1ÐÓãòÒ俜Ɋ7>I‘ƒ­—* ßsépë³Hóæ>íÅ~=¶_Wý°:ïÀÎva;ܘ¯m·iãL1l®*újs(Ëõç[;ðŠN±‚EžàÞF`ÉÊGýjiïê–•qŠÖ0÷‹®]Tµ&ª§}dCUI&`²à›~\Y[‡ô6¥¹ªV!¸¬4WLðIê²1¦?Jzâ$=Úì™óÖá9LÖnÂ’:hò:Ü4íO°3Xx&/É\Ḧ@ê©3y‘T…ñ+˜k£™öc©—ejº–N/O4 ¥;ÎW}À!I}ésR¡òʤróÔf^ê K¿”©åº<×:mk€ÀÈÏÕ&*SzY×3¦N;ùÆ.B)v’yÊæ Žqœ—DÃ!MÈ >ÛœE³HµïR?æø´S2”ä±ü“)ئ•¼ú«¸YóÒ uUDäwNjºsOœlµ¯Ï`é.3[¦ï`#¦Yz˼ʄ…rRä,‡*»¦Eç *¦Ñ†ðË™Lõé{šwaîÂa·ÔóÆÉ¶7S¹š/£@L„£S=Ñc_<áT}#š¾½ê!ÄäµÚ‘1 ‘Bû#Îk"H8‰<2UÄ£T¾Ófæ…+‹<#ïèSO;÷v?MѨÐK¥\ÃÍä«’f%‡†¯©|}À@þ•ò¨œ ŽÃÕµ(gºm^ô\¤(“*/¤¦6ÂÓ8‘Fq|ÉcJ[܉í0Ã+\I5I#Ž÷À#$s’¬Ì€áhF Qˆ±:ŽŠ¥æi÷mèí6èëÄΔ@u$³D®l«µ­ÜòŠÊ~{À^yì­tøg“áGŒ;íäæ…ì—ˆo¦MÜ$w~µÅ; uØ;áIäÕB±ÚíH…NàÝÆÚ^ 1ø6ik)Í…< ¹Y‰5ÃcyÐ&§Õݹ8ú õBRÙÀÑ‚;¨e@vl6[Øù¯ó0ÆMr€÷¹·ô:t|‚?®:nïü¾»<›OßcÅ9Ó|–ðo윋Á×ó¾'-Û*ô¡²< ž—ZðNìxÙ•sѰäu)áâñ.‡cµ©•kA{žPS>EýƒÎ;†Àx¦L2ú&·˜‹‰¿´;/¯ü÷ê#¦åFŽËÇ£…¤¦éU×oKã°xx˜[翽Á)Ò¯›…ýÆM*Áèx—g·³o_¹ÑsêÚ”¥­ïòÅ«$uù é'9Ê’z^‘o©mþoóœ AjÔ yRÅyÁ+/›È¤©¤Yѯ©4ÐÉ^Jó@šcI켇U|ЋÏq§­måäžwÚJ3§7Íï ©!þÕ½Üi1½RV4óý'\® ÆßÈX·&aþï]à54p½ü‰¸H^†0â1¯tŠ–üo9*€W7tëWdÞhù’7q"iäQ·yZVR‚Åû w³¯Ø0¥ “‹X 8ô´¬gút–i4ߨ¯ _¢}€úGïwjÞœ„íƒííÏ;_¶~ÖµSûz—Ö쫚ø»ÎXV KE¹… n;„xµ_©;>øÄëèbšÖqôÞ!Ól—Gt`HÀ- è;yÖUÎÝÿM f‚ò¹]ô°OïäÑCò0ý6IU“äiâ:½ó®þù›a5[>ëßtÇ&ºòヽ¿è?ÿANé©endstream endobj 139 0 obj 4412 endobj 143 0 obj <> stream xœí\ÛrÜÆ}çWlå%Ü” Ï À~¢n‰9vlºR©8¼,)F$—Ör­8_Ÿ™0Ý38X`-J¶“X.U ;×îžž>Ý |¿…T áÿôÄùíÁ§_W‹«ÍAx¼øú÷ñöêàûƒºÐþ¿ð€Óç·‹''®£”‹¦hìâäò@MS‹ªU.¬)ª¦)•EåÜ.–'ÿt}´ˆ}´-„uŽ\£zqrqð÷Ão–Gº(+mŸGê$R‹H}©—‘zÚ=ã=‰Ô·‘ú}G)ë;+#«¢®Â8BÉÂNÞ·me£ß¹&ò"¯‰¼ rEäbªÁ‘["¯àxel wD>À¥oaƒMO*7Û?Nþx e¡œÄ^œüîñeöçH}ùžr”Ê«Ÿ‘#æËkȸ3ø”±èW+G3ã€þúÑ¡¬¥)T3"¼ È— äá ‘§DþYyø’LºX ï«аSœÂ§+>‚tb«Èð*ov[Óìt¡T½2N±La±5oz½tÔ Ó»_bûv‰Z‰váí_+zÚoè¡ø‚~f#=ÐtïÜÝà,_mJ6s>2ëzI]×ÔÀ©Pé9”;¶e#´kît ­L­ªNñS1ž»øN#u©U­ð²‹Älb¯ëHý{þH ë1è| ÉI¬7#éµ1"í§ LòjÀ!k9…˜'cl_ Ù^B€y»a𼞚‡ÎnF¹è÷bÀ®ý">¿XÆ]$#\¾˜![(ªòâ‹-I7U¹„$Z/Mt³p,†"ÜÉ"Ü¥^·Î°©å‘(¬´•cTáoŒ²ÂÚ¥ócº¸…5¶¨}DL¶á’Ží6¬ûF/¨ÉÓØoí¯)¡•—CO=øNM%„-;OLj׉šÞ8ªqÒ îl?æª]^Ùð>ýZJ]Ff8^¨$Ú_ëŒƶ§•˜¡pTžî¢Ò(Ï üÜ©žÃ nÙ^[©^íŸÆÖýˆµ©Ëº »¬lsxì=wýmвöþÍ3÷{aE£<š¤É7lˆ7Œf=ŸROóÝâ‚ÛSïu#jÏG7‚s‰âÛŠ¢´Ò#Õ0¡PM¤Ãä+6a×Õ&í¿;dãw›pð!X7¼R®ùSzú ëÉGI; /öÊ)ó0H];Ö·dóð®ïckï×\³Ç˺ÐBË7÷¬èŽblð"rˆ¸¶ÔØvUP9­½'h{DP1>ÕŒ–©À’ÝvÛâó\·2­´ÇuaK#›D›Nk™|ÛF„øViD!¥uþLÙ®Šk¨f£¨‘ŠÿkèÏ«¡F=‚‚V¤#M*\HªePsåœeƒ®³õ³”‡ ¸?’ˆ5JÒ¶¦y>’ñ¡ŠT ¨*"ããŸ>%ð! Bh"XÖ@yì7ê  P#‹ ´ôá•ÄCx~1†Z Ù08ÃŒc±·¸[†Í²ðÌhV¡qÚÑY‡‰Øä÷pšý¢™!î¢ëÂDíÄCp& &¡<8áÀéÜ [wÒ …Â0ÛYà- ÿƧ¾gÃzcֳџWãÛš66P‰Fú‡>*m›:$´ÁkÀÁ É½1¼õÞ¼÷ç(–PËt6zJÂù­Ìy8í…Ó(Ïw( —Á ÌtÅÍ(êUB²¢¶ÇñŠþŽ•øŸ‘d 2ÿsúBžáPgƒ¨É\n¥é¾;¨9gð2Úg—ùiðk¼Êµ0x…‰¡ óZ£†.N0#8Qɬrå-f¿¿ŽúB9”u}æ1ËÚÊ¡O™$²2#X§÷kæbÑ‚”ÞFá’æÂã[åaÂ4á„,Slv8Kš÷ ²½Oây™a¢lB%Êr}J81ÿθ/AæïsnwŠ$˸ß 4M®ò°‡9ƒ¸j"‘‚pêS¹Ë[&b@˜Wqí6ÌXï¹Ð²ñå4V›ô“L À-å ÜÂöÓfµû?ãrs8,2oóŽm¢„%2æ-Sí*U»>íd¤T¤J@U‘:žÊµ|ð’sÕ¯Õ?­ˆ¬‰”0Áâ]}0DAdIäD>¡nÞg#_À^ò-í.Pÿ(b×@èWñ‹;“uC¤€äˆØ™€ Øïgû¨´w•´·’ØSð¤4=ž'Cï~äÃT²«TÉ®3ÉŸc™X4î;»«ÞÝí#®zF5¦ä=®î¦­%®c^ZWœ$utóú @V"¡ìdà’]¨ðzÇE¢Sw´Ü§Á²²eåFìÚþz_û``¶gî€í…²:_id‘/´iA¹´Ð˜¾‚ >$Æñ ÿ¼ÅàŒøsírí%)X<3ŠQB/ÉnREÞ~ @ñ\¬á!n‚5Èqo«X=22”Ìë¡l‚z€jŒƒ´UðbÍ ´•ŒGžÊ^Éá6tÌÕ™;˺0>÷Kæj²4œ©³RÛüÔùõ±¥ J4ƒñõ67)ކú°7I¶‡ZnãO¯p*{Ó€yð‚cK¥d/z¨¦P2”ÉLá8Vú~Nod)I‰íñùxe&É3A±÷wØSxàÂþ±s¿x&'^~)º#ãß²Âï8Ñ>GyXû::µú‰€Çº iÝða™Í§Û)It"©Ž]¶ù[‚øLѰIgGL_q#vc¡ƒÉûôYfÈlà9 R‚µÓ,ß?ñy‚˜²°Ú€(`’5b¯½äA9ö&A«Úº*¹z¨Ršv˜rð襄;dg”÷?Fdî3hŠ?^¬äô 2Þ/A?yû9æÔµn ¸Àïç2ˆÄ Ìä»Þwðé»)8‹ fÙ 1àÄ ×8BÄ’'‚DJ/Š©—`Žç€¢7ÿ#E%ͯ@Iuøëå ÷³:gŒ£êbŒ†¿ï~ß'kPj”]þðÝî{Ô}=Þ;>Ãb óïYá;6¶¾ZrÅN \°1™ã¿aûÍoã¦QÓ·ñ}¸T¬«q¸¡®bŠ»BàÕS³û¾ ¨»2,³ÛPÁɽ¯É‘ÌÉ FÂ.ôl‹c´Û±Œò˘tq¢ [Æpp\Ô´³°ÑØÒ'2gI {(Á…»(, ²¬¼Õ Á:ô9"†ÄYÓ³âUâ®QÙ<2”™Çû¥Å{],“h0V<^Lùä¬(¤ó1¥e.&·?ð…ÜÞq%(2SÈ9ªˆNФ*~žëÏP'ö?ŽøkëÎ$9 gkø¦ù.X`ØüƒKdøA hб)ž˜“ï‚ÞýáA›ô­vf:ÆÞЇ ܱà‚K©2[Ukn’žíoW¸?¤³J‡žG.V@º1[§¥cwc×$ûú ÞÈ*VY4B‚zó@=êÛN%øíÇ…8ôÅ*,Ƀ9Æ  ›dd10Áñ¸ÿ ¬x ¡î¶#à´ß#)ô’ëÏØGŠ»ãÿý›…/áëGƒ‚ÕÌøOàFºEX,ŒþéÕ2")Ùô•œ»ñ»!ùÂú ¾Õ™!ņÀž÷zÄ*ÏÇ`y´<€°®-6Å5‡˜©ã7Lü¿ü"´ñ\6Ôo<[¥ÑµåWY‚‡û¬[,ë?9ø‹ûó3D¬endstream endobj 144 0 obj 3854 endobj 148 0 obj <> stream xœí\[wÓF~÷¯Ð9}¨Ô6bµºYœ¶ç@”ž´¤Á½p€ÇvJb‡ØÂ¯ïJ¶vf¥O7Ç8 Méð^ÍÎÎ}fwyg ד–HÿäÀè¼wï0¶Næ½lØ:|².Ozïz}×OÿË8<:·Ô‡žg%nYƒãžp“¤/â%VÏŠ7N’Њ=áÖà¼÷Ò8;^äÆ^$íSG(@±=Ñ¥ ?Œ"áÛž” +Œžú(±8¾+¢$ì{ÙXìÉ /ElûÊÕG}ŸO>pvW)$§iîìÄö꼈ʮ8#ðÒÙ íECÈ~:%\-GÆý׃ŸŸ}¡ùì…Ò ã@1y0VL}èìøêï~`?ÐÀ‰„X¢`¢’+‚H£`4éÖýDíDr®6%Õ°Ÿé×qèÙCš0%pL Kx-¢]…¡ëõ£â®¼ÄÏ7SZ>"ð€ÀÝ” Š|¯Äˆ ïŠDÆ­ÁdþÆ Îçô•!h­‘U‚ÖsŸÒ܇©JÇ"ñì}8õ˜¦2S­K÷*Sô„˜&$Pçé-æÂ™Þïîo}ŒƦ¾UË*[‹? £ÕWa ¹Ž-è÷™i.±rBq*æ+![©ÞúÑRº¹êú)ïÕxÜWK±Á™T<ô-±ìÛ™¸Ò“ç‡ÚíLµ+#?–™ü‚ˆäTöZnËíûIÀ'­ð…DZ0×4%aN¹ƒý0%Ÿp²ž½z~èÊÈkë…rhe«¾Œì=xXüySŽ"Ñâ˜ò÷Îz³³¢:iþ—µ˜Yc¸é s¯ØÍÁë ïJ9-[콦¡N–xDàÙRÎIÅJ,XA›^8ÈqM æ2´¯l6v«WckŒ!e̽r[•Zj°”WÔðyN烓*‘”±«l>×ò7ÌÉ07xZÄšMP, ÒôG©0[ö„öNh™†±Löæ¸L¤žH+åÓ–´r–?×ùgÛ8y’^]Õj”R²²Ó1Ýßš ì¯Ò0¢*å1ŸtFÞŠ嘽@r‰—nƒ¨Ïqe¶‹0¬ åªB¿ü“îè~0**AF'›0†áXy2é»Jâœ"47ši´ú%’¹BŸ¦ÎG¤4–oŽücôÃÍ2q©ñê…v¦—Ħ(ÂL­Õƒ¿ ±F¸}k°ß|ó2‹Bz®RôïuØð4jè>ø5ÐЇ¬TGýØþ¢|P”e¥Ô+¢e[ÊüZÊäõ(€² %a ,ª 'ÇÉëÁ¡†`ÌÒÐHC§â‡N!8!ð Vc8÷=oÁ¹œ0«_B¶Ö ¤¥~‰åF¶TÅ|ö»×÷7Kóß§Õœ6$BóN ;Æ`‹õê%Á½õvØNÛ8.׎DêOãF2¼Ò÷A®O :¿iˆ)P1™Â3¡0Å<&ðâ=‡Ÿ±Õ˜ÑaÝ¿„Îáj̼˜A[Å«]À¹CHú"ÓôæzjT*«N3§ý4Ä|m¨pûÆUyC”ú Ñ7#ʬNÙç¨D›òZHÓÓ$©"ßVµ’2}•%ªVÊó@5(•Yôƒ°9%dyÚ…N‹hYÌ"Ñ}†Ë[TI¨„…Mœb1\lî}ª­µo IkÚä%äMÉÞkhÒäu8æS{L8tCYÑšÙ¼2̉̕‘ðTSK‚ß5ô¸/ÈïFÆWyÔcS±eîå î¹¢#8÷‚uþ§*0=» ¶ÌšpCBzó¨ƒû%ˆðõùu¬¡×fîAa»;kçp³#ÈÄàÆV•8z !êÐ{­W¾˜³-Oþ«l©Ê5ØÂØq´Ì5*4yWóè =2xìIHbÙhà˜ÇŒ›-¢P@R»&-$¾îío/kÉÒ™–]@ Üê’çÜ*0éq‰5ýä(.^V?liHÄ r`¿T^l5 üUâg`ï­šx¼€lÁ<ž·ÞŸÁ,l÷ØõãºùN˜@Êæp F/®dõ :ÿ§æçzPá.¯'™pÓŒ™Øsâ®®Ù‡ð3Ü78ƒ4Œ7ÄíB[€1ËZ¯[S€ø3~°œÝ™²| ’~¯L°ò:_·X¿c7è¶Ëîr4Júk´ VG%ë÷ ²ƒ°ªí°º¦ÎAá`';¹\³©és–RÁ8åXÍ]ç˜ãÙN¸«—«c8 ˆ-Ï5ô¢ƒèŒ  ¼q“:ðSkНüNøŽzMŸÍ ØJpíÊHgá ‡É.›Op…•TVS¿j(¯«Ú$Çt"Õ%9ÆEý[¸oœJaŸÂæâ?ì]Þp¡>«'´*1=ƒ`cF¼©Úô®’ï­›«ä·«]·¼Ô¿ÌÚj/€ê~ʹv7Èzl^ØË7æä®ñj]Ť7œÞ7(ýàü~ ÚD­ßåHÛ)6 ÆV;6 .÷®ë¨ñµhtA™š/4fšG½hú ¢Ùh–„ýÊbèÒÞŸ1)½#vE¹ð¹¬f×È2ëÅÿSñÓ1äøÛŠ×xsí ¦z™æ¥ó& æ;™–ezýƒá²¤+«’¢pZo€îbšw@“¨É¡³=]Á`Îã°»…ÔµHd…‚5¶=°® »C£>Ÿ[㌩«EÕ&™ðÆäÚ¡O¸&s,È'|^ÂFqÓ w¶nHl­»t;ŒZ.{»É¶j£ià´—Q—é-œWvéªã…q¤À–ŽÈp©‰ó}Ôäo}™ë‰þs]øÂä mùèí ' Ä êm2°ÊaãnC£ãØ`P‘MÑ‘›èš÷òX˜b׺¤Û[ðÿÝ­®þQ]`ØÓOØáwéýrzâ_ôòÇ tk!úI/!ØóHº@ãÙ+ØÊ稶ñºÔ8Õ·ñ;ì¿sÀ^ŠæXÙ¿ÃñÁIÒ+bùö6M3$aÎO]ì_›h•½Ь4V²‚.P• ]Ô.M½ˆØy*(fc©‰ë®t0 Ly °aôZ½DoØh“c“mfð7®<;ÞjJTŒt£Ýüuû8CÇçŸ,"Ìšðb¥¨n”ÕÊx:'í@¯çhŒ$xV’•±ö”Œ@AôR²|¡&h–4.Ûº`àÆ§*x.›ð©)iÊr‰î z¿©?ÿLendstream endobj 149 0 obj 2432 endobj 153 0 obj <> stream xœí[[wܶ~ׯØÓ>”j#†¸$}ÚžcËvŽ[5®ííí8y´kYõJ«hWrÓ__€Ì€üH.mY®“Äyƒ03˜|Àþ0Kb!g‰û×§{_¿Ìfg›½²yöò›š¸>Ûûa/•û¯làôéÅìÑÜvTɬˆ 3›¿ÙKâ¢È“¬’*fFÇYQ¤³LˆXç³ùÅ^4ÛŸÿ{Ïþ)íŸG{óß¾¶-*‘–AGÏ-§™ÒÑ+OÍ<õÌS_{ê9à[zêPëN›4|×@ÎÕ@omÛs…j:;ò’kØÊ$yIäònˆÜÂ!®RÚnßÏÿdÍVÌ„ Ì–«–ÙL¦b%Ù^Grÿ ‰”2—Qìh%„–:2=í–¿ˆs+E¸y ‘hkòH0îFb®ó(e´ ÛE‹ÌÑÃ}aâBëÂÉ+â4Ïu=¶ßc“RDçlð ñŽÑ¬ç!õ\;Ò~—NÏ5µu½ŠLIîÌc%¨T%iɛĩ2ZU&²ðt9ø’ Xw5Z•ƒ{þï"&¿^„5@ô¢¯3?A×úëÉ¥àŽ¬õ»}6 ïù¾Ôk.­"ÎYóvßîïD ½­4Q»™g˜{1 ¤?ÎÊÍ›—.§´Ž•¶6_X‡ÊÙwÚ-kò¼2c¦TtkglR-Š@íµýu¦RïîBŒE)£‹¸öv·NgÂH«‰ÄnKOÍ,¥Rce=ºi”žJ=eX›Ý¶“sd'Æzrµ1ìwëÓ‹’QÉÄD;²(Ý8sþkmg«ù°§îÓš:]¹%.«´4N! λòµ¤Ï¬›`¥mr—(o G¾Á¬:­| ’r—å…¥òPê‘„1lÛ{êPK_Môl˦‘º°QøÞD‘|`o2¼…›¹¯(#b®Óžr€ES6û©Ó²-"pÄæ8L³¼Û²’¦%U®¢s™Õbm#wBýMy.}FãžbÓ¼%ÕÒØÄyãEF…f£.=«9´Mã•*¨~Ü—¶¬e%ìG:óþ«@³M\]û÷bùñ'f`Î`Zj{r{˳ÁúâH3yªùX'\.ùL<ù  í0YÚ+ã i£\}à{$3ç t㉧æÉx쩞:ôÔ%èqêñg0n3F…‚içœI>YF2˜a IÜ@øâ|¬Û%l}Á,l %¬ ,rJä;(ÁÃ-’ℾW‹þÃS/6ãõ­§zêð• Þð»ÀÁ`¶è.CÞÀ ‚ò‡Ï&ÎMØ!¡?KÙ“—`µþ†RÊp„/l€·NhV\ä·3È”ÄNóÅ•"îu 3@;s±š–ÅaÊ‘k¸|Êǵ}çXÇE( ×|#…Ò‚'rX•ˆÚKJ—h'†ûÍ 1劇`Œ¾èÀé¦ ¼÷q>;,عoánÄÝ6PÂNçcËðjã~Sþ3§üŸ¾ÅZ1<øOöœ ±ÚnÀ¾BýÚ7Räg§ :„é`"©Aa„\´#ÿ%ì~hÌ€klv^B^vÂ^Ã8ÉfÎ:Ñ…‹[ã(“š°šoBw§œ—&“`[xr¸­Ï&N”Põ+§V¡UnxæcÖ¡C ›,ëÏ3üÓÂæÛšfgûÀ‚#(91Æ:rzÂX@ò‚LÃNzˆÇ°y‘j0$:zíÜÁ°BÌ Ð`ß*ݯbMu*tV£ƒ2ÎLÑë2¥¨Æõ³œI]yŠuŠ©ÓŒ“jÒiç‰.O¨y^ËJQºüEW1¸Ä®õ+à0n†+¶Ux#†¤“18 ‡zSÈ,¯r/Òl,DÚØŒ2)¿^Œ;vÀ2IÒI;:“BfÍP82u7œ°Žµmo¤<¡¤÷-‘‰|D䑬[9Y7dwÂ2Nrûw=T€¼V@§‘©pøGjOH¨ŸàŽ5tÆÅH¬ðmKÌ4HK¤±Èe£Ak Û`\Œ§2ލ9tnì^2ÀmÚ÷ÃÍ-›”œ¯ûu´ ÔEÃÔ¸ÑsHÎayö˜ÈDö;bKk t³4y|0-}yÞgÄûȹa–Âî/Ä:å&F’oåÄËHÕW6IkÅdpïfØiYqX,ÊðE€:ÊòÒúkæí°ÆP_®Rsw×5Ø-yV ŽþUùæ£]½w5:³å¿u­:„½æI­S%µ²c+^tS!¥O\œ1Y7~€KàèF‚#ä~¡¬»ÓK± ]Ãи”S⤧#£‹gmgóI¿ù¨¥È”‹æ÷¸¯|"è¹Y…>=ëX¯õ>$°YéSI©Oà,°Þð^h»¯O‡‡ÑØ«¦÷D&Á•Ï莛R¢×%_ŒQ|¹áýQ©Í¤þ‡u‡Å×磈Ÿ­¥rû7 ôñA7™ì*Цµ¢ŽæÆVåœËë?À YZ>;Z_ÖîašÍöêè¥MSNiekÑB^7!èé÷¾ŠžJ=õ|•žúcIm™gÑ8 Õ4&@Q 4p‘‚&+ÀdMÏ9pŠ ßxì©-h#píÔS ÑÂ/sÉ /†Ÿ±*gyo!Œu yga=<ÄHIÊGþ¡:*'M (ŸC¹=¢”­ hv4X„øÎ ::73½+“`eɇ­¬ëù]Êûû0ÌH·ES…E`]‚Oàü­ÉÔ÷…ÕÃ…¡÷…î©G÷}¡kLÄN0»åºò¥|ªÊ8G¡ËÁcÊ;8ó÷•0¨ô‚µF¤˜,ÆÛ¹Ij‚æÎ7J—`/]xjø¸I\–Ìz.6¾×¹§þ»»$¤. ‰/jœ¿iïö@/}Ñ`‚hè^ìé]PB2A¯&¾1Ös¸–+"oà Yx?¼o ‰~u2òráùçÐÊzlýŸIAÐoï¤!½i`¸ºÊ<õ}˜m(M×-~¡q µ8ú#¤»ÒmK¥¤È¿zŠnC_KvºŒk«IÈÿ¯Ú„]Áq¼Ào[vÝâtüOM¹#:fÚ¼Ó*ùcìŒmlÒ ùÞÑêåƒlÌË® „O£-äDO;*ÒÑ7_€]C^üáÇ]=‡”A‘ìéOU÷‘þÙQ#¿4›’ÆT»…ÚÀªÝì¼,¦#Ù³ïqèÇ¿>ƒ K83ü|†Íw—y¶ß²HϾ§÷§÷]v†áòã,óñ/<p¥[ØÊºÝB†œ~î4]Û;½Ý `çúu"«¢uéG/Gé:aäYNý{1„fÞ P`kW• Gˆ8´žv/ŽF€‚ò¾¨ïðà‚?B JÐùº‚’S ãPÝÁ ”ý?j®ãkÞÁ>B•ë£öÓ¿_NËNËH/ôLæ_4Dçdœ/ðJX:ÄŒ‹A ß”7¤¿†­¿ƒ­¶®¡¾Ëø°Ê&Éò΋S–é¢î`©F§&zJÞœŸv©}‹³âÚÞßÁåâ’ GÆ‹S;î†5ºm«îóÈñ3j\3Þ$G1_Nì?ÏûýzÙ—~¤ÿ´õIJ†'ó½ößÿZ5†¢endstream endobj 154 0 obj 3038 endobj 158 0 obj <> stream xœí\Ksܸ¾ëWð)eQ¤«r°l'öz'ò¤*©Ý=Œ¥‘¥è¹zØ»ùõHÝ?gFÒh6µë=´ÁFèÐñÏI– ™dö/¶ötòåv«NþÒ7_¶~ÞªÒÜþ× pøð"ÙŸ˜‰y–Ôi]&“ã­,­ë*Ó-U‘”*Õu]$ZˆTUÉäbë‡ídg7Ϥù»Ú>0`Zè\m¿ò½î YÚ9e¦S¡4ŸÎÀÊÏ2ƒR VêMû]ÔùöŒÀC¯d¸G&N!¤ÀV»…`26í §Âœv—øqžþWÏár—ðœx¹w,ÍÒ?M¾Û2Ò—FúßoMþ¸„üß-!ÿâwù/-ÿ8ÊT--ÒO¿‹ôIEz æIΓ»1ö,n¼Î=«í{è{ýà¡ÌC/=$<ôS ¢*•õ2žîž‹àò„±õ©¼ã ¸Ö4¶Ä×1ýcÄ2á. Ž;Ç‚ßy†¿õЛǗë˜y?•\—ñz%+h„Ÿ<'Àñ>š°µC]‘; dáŽÞÃi%7dcä;Ilï=ôמ¶Ü&{×¥ôßÕ"Â\Â#ßAþ1ÜKÈ´ã1ˆ.»ž. ˆÆ×;öÊçØ¯c‡?…Ûa÷í|“%_ú’=Ì Ä½„ föÿ©ÀpÍë“3z àÙ³ómÈO_¯æ0Ð+M®öJ‰qþõ¬’Å'‰‚[ê4ºÑi þy£Û;ÂgŸ„´Ò2_þ`EWH'»·;yš•u4i– ÃR^íd©Ò„¢l01ƒyQ–™uyîûÔCæ°f¼¨ ·Ü˜Ñ;m†taõN™Á¼VÑ}þÒM©rNœ0 Ó4ÿÒ.–ËL÷>7¤´Pnó [ÿÜS½¢U¿íÔ©®Œ"µô^vD Ç̆sA>Od)ºÃf^]otÕëYÀ2Ðù\Ï]úÐC´ÙÛŠ”r47p«Ÿ¿äÒ!q ¶w<_ïÀW:Ú 8äQȶðÞϽÿØqvÓÛud £YSá[cÕ¼~œ³¨€ù$v‹]ÑÅJ1'9ÒÈÐJˆ—G€Ó¤Çà+‘äÎ{r2ÒšžòèÞC@“F%Œ#µòù"0æâL~¦`\†ðß±KÇï,¾ë!÷ûGÏ›O1—‚7Àž‡>¼½g˜÷ è\Ï™ |Í0[‡†.ëeÞãXSØþÑG{”•ª<*+©²J+Õ–•Ì *KK)e%·S çB(Ù$,àx¾³[§•¡"šw–ÈÌ;«²¼Çv+Uy*ÎÆ÷ s]Vesr¿ÐŸÍ°!­š·¨ý…Mœu[©ì[Â!sïìh™ÕRX\?o:0¯N‹ª2ï7í4Ã8·´,Šæ-áIœ1¸[1“F¯iêUs¬¢²yóØU´´R4BÑym0n"y©ò»ÝÈ9#ÇavZ>‘¯noOâ ã°‘Ûà 6“S‰&v µîÜ/Ój]®dš¥Ñ²É‘Q*óRf³öñvÊ(óßÄk0¸îè˜ëÑ^8×1hhj§¦Yû”$ÑKÜRÔ¥tof¾p(M ÆWÖE*Ê2Ù5aGUµLXÄ&ÄÀ¹lâ )t§Ežö:êxyÑÎÓRó^ÓÊ›\ ÛJ d7ð ²o€¼»ˆ=^ánŒ]MW±BÅÊÚ²«P²/±Ø¥ÜÏ $Æ]†Ó‚Ý6%Á4ÁÚ÷½·ïkÁLvpyô³Ö/ÛÏy&(RP&}‘|ð!È?\ˆ²QýŒX›å §`ÒH‚PÂã8^0*ᾃó,…²N’åæ>FŠnKe¨(/EÝ;T¥¢U¡û ·:?¨‹šI>À Í¡²V fÓØ¾‡pIõUTf~€Ó–)t©ý¸.g4Ù^Å{bÑäs™».Ñä–›%¦4=Ñ Tj[D°{ý"bÜçNX…m9öÈŠq1\‚îw¤8ç,‹:[¿uΡ;ù&8‡ À{f|XÚüÉC¨ZØoÙÏ}•Oé|ϲøãl޵߶Ê4q%ÈDßD£jëÒv:çþ¹‡Ê4¿bÓn‡ÃÞ w°û8x²ŸùNy®//K=îúØ~Ùí{«,„‹®IÇØX¦©I‡µö¹Ö¼¼¬ £³N¾ â26]B„e.!¼pI`F%¤€:U°¦2†26ãgñ¨ 5 Hµ Š9,mÚ/zlF  º2=ó^t·R‘æB…z“½ádA÷Wa.)‚þ/í5Vø•ýÆ/l›w—Åï¡ ¨½ €~ØøPõáÁY:3 NÇÀ÷P[†,iĽ… MîùCçûµÒö~jË¢Ñþë'èJŠ~‰©šÁFÍÌíRg^Íð“q‘(Õ=ÕÎìæç5Å<–º¢“Z1ž~ªwè)‡žóX«‡‚‡‘7$‹°Sôa0ì)bc£1YmÞ^c†¨m(Ð÷ÌyYQàådæªâ k©½‚Ü´º¬|˜MÑtèÐhþøÚýäš/ÞÏBØ9X‘ñבּy>@e‡¢X‹d»ô™˜Z©*é~³³í~y:/¯ôÀ×l”r™1š^½œaŽWþõ;û B„Äìç3!Q^3ˆ #l.öX…)Éh¶aøì1ßd7/²öÌíE»2;6ü§‡þ¶¾dÒÛÉÖ?ÌŸÿ¯Ä(endstream endobj 159 0 obj 3078 endobj 163 0 obj <> stream xœí\Ûr9}÷WôÛvoàrIuQûÄÅ3Ë, ,Ó±3ÆÝØl·‡¶aáëWª‹ò¨êTW·1L,𨥔”™JåMõÇ$Ž”žÄîo _ì¼4““õ^Õ'Ð|–Çqø¦Ó(NsÖtìö·’%\ØhK†¤Ý8“©é‘t¸p!`D7Ì÷“e‘*òî~T™´Ûh@•EyZmÉ·N|,àKlµÛ·ËW=¤E¤•. Îgi#X@‡#á(lÓIƒ“‘¤Ü†¯ô}-rzæçu2ã­Kœ tÉ9¦Ã`?0_ tXͬ˜" D`79ö€x²_ØÃ»{쌧h’gygá©Ó:V Ã‚v¸é-\ëJí`œHìkc;›‰òZêE%eÚ¶[^@ÁVÕôÆYù1 Tž{Ñ XqcÚŸeÆJËÀ ®Ëc5ݾٴAb{S&ˆ£B{Iùù»Ûg’™L£VlÉ ²»+.™ÐopH·x°wà ‹Û¤Ë‘ÞïNJ´2Q®[UñŽž8+v¶—•°búaf!•Æò‚ >œ7C¬-·—”™\)|Ös\fÙô‰t­î\—jú” !ªS/L ™ *m\?]Qʬ(9 ´!´ž×¢L2Ì뢿GSL_ÉÂ?Ô£í¥™r»ø®×¢´†.ÔOiRF¹5žÜÏY££˜]”kHÚx»¨§ÜÏ¢xL¿írŸÆ–ïI+€¦íP—G6\`¸ù‚«ÑI¬šƒÜñ©³Kÿâþ—în•.|£(tÄÆÀmp Ùn½¤–+°þ6•3`Íé¢H3gþ´ ´þ6ÃÕ1:âÖÒ³Wd}ÌÒ¼€yá €vUJt>×1ü؆@%zŒ(z˜—«Wè{³Ø„ÆxyÏÆY£±¶B匇Ö?òÐ…‡– ¤s7&Ýmap8€k?êÌC·Çt,¶ðJÀ .¼ðZÀu j!LŠÇI3Z<ñ«Wåg}·-”•£T¡Ò¨–ë¶ëÝË•€7t‡àP¼¢}_S(÷¶K£5ž j¬Æöý5ƒêÖú|×znP»ø‚%ËÄé)kŠf\2®7¯9o(Ý8±ø™:¢Ô<'ÀXôp}×£Q:ýþ©úã”®ÿ’‚Kº« %ç’â]øaKÉ‘õ‚ÈÐËŽV>B•5]=Ð „aE7}‡ç)@vE'¾•Ì·ÔÂ&*0Ÿê[q7û l¡Æ!±W.`]t-ԾƭÀ¢à®ïÆU`g¶k9ké³FÍÿãJ†¹EäûŸÈïÞ"ú¿!4 ž+ Þh¿Œ]Ô·»ÞwÙWÝ mA™¼Ób B¦™"úøÁ­n©QZ¬)ÃŽh+¨ÛaŽv¶òÒ/E¬¶ïðÂåvÆ%å\ pË‚²¿’]í [ÑaÙ*Ìÿ™‡w8ÜÚJÇ|Ë» ö¿½XÔ¾¦}ùyZSjÞÞ0l“i¦11e'N\±þCX(ýBfŒI3?LÔ:¬(Q¡Ã|NûŽÚóLŒŸHñðÑ¡9ÁÚ Qvr;“î••.},NBNçng™Š““ž]£ã¨c^ðr?fêæ?ïe0 w´JgÐźÆw¿A’¸îIYz {K U¯­UÊ ÞàfW§>ö,vÝ…:’ÈU (“c@#_’å]“ ‘_¯É¯²µS²ÉEH¶f B¬]Õô¸Ø! ëŒP(qYÃÞ0ªŽ8÷Ó?ÉÝÊC«fh»´m¨fè¢ióˆøCÿC›ecz‘쮣ÕKX; j¡ë±ðtdUÁ㻯¤²1·Ì”qgUgE„\E[²œÏ£KÚïo‚jÈë’D‰š%m’hÓfr•¥*™oü]÷JyBxE‰?Vº Ù>Î]žôàl²×yåifJgÎi È?Ð|Ó›.ç îo?záZGC ÞØ$V¸ ë¹@ž¢ðWÖæMá«V¢mš{MTN¬ÀbuV‘tª³{éÎ/ìhktÇQnQÚb²p¢Tª+‹¶§®"ª°(T½‰ØZƒ…3}喇´púÇÃØn~lÑØk¿È+]ì'rÍQ—Z9}ï›×0ò À·®¬p‡ô‘ŒtV‰]_ZÝ tíF•&)ã±Úb°Ô³vU­#sj༞0Ö¥‡«É—0a34O“¢]wÕß%ä<þf–Nzmsš+V.çûàà÷UåKáŽÎ4ã’OýFpà!`Ç‘'¾÷)%@C6“$-Obw,ËfÀ4¦¼D»ªÕ æ’|I-ZE Õh¶Ú6g¡¬ø‘5‡”ÎUËQëÀT&†£K™OßÍŠÈY{º–56“ëV ãÌìú(‰´RÖ”DÞ®2K¡ßf¼Ðd|Qw}Çhë­tÚÕr¨x“«Ðs° Hòø-MCo,« j͆ *T •B(òÌX<óêPƒ• Ϥ*RÊ_0Ãίß`9µPX‡Ú«s0Ùr¹öVuñ¬ÌÏ\ ²` f#àz²;ZObÿ V*z HÉœ.˜×ÃFŸÙÖ87ÖúSÅK¢¿Ë{õ]YªÇ yÝ¢ x7ËœåÞÍT²“*ûSÖÊÎ=´0‚ "SZ7­*ˆÒº¯ÄÖ’Œ*½%"j‰ºþ½XÂ0R)ƒ¼ñ=y¹VuŠÏdºŽ&Î2l•µ÷¨¥ .E7âi š.íà ÒO³KñëZ”-ݵò:ŠÛá[¬`sb#OviI콑ž¼vŠ$¡0ü¨ËŠJmñÍ 8 M%Œ¹/Ê ¾e÷ÛJ)Ü´z]°MI¡‡š“HÄcɈKÒÏx¨ôP졌ÆE8¸ 1s¼£‘ ]ð)”ðX´*3Ÿ ø»Õ.¶a ¦…™¥oŠiZÀø 2ms¦ïóñL’E-÷¾AžÁéú’<ûjM˜öçÐŽâ3÷ÀC?|Ï Éþ«·–mÒ¿Ÿ‹Íëÿ àÕ2¨Èb™-9£ Ì×t›¬0¶¦Ã>â0'(i¬#crx~@öÃ!œS:<ðï]Y ‚"û킚wVÓöY¨¤G pŸD+O¨PÞdïÁÞ®lâð*ñ/2Kpc¸s<×±ø_ÙÝÌl7ÉZ@ñ˜ øoÇ7pÞÉÏ,GžBqӜǂ%ÄŠçs^ÏÏ,~þfqÙĽ-K•OC^áTŒW8Ý'XªÄ(L  °þ×Úî–õÝ“f‰]÷Ä 7¤+ð­M 3øNQ¤@zÞx‰w½ñŽ£×)8ÀÓ´ß/.$°$x¦òa¦ËH«$Ûæ8 <ÚjÉ­¼<óBÄ*ÉâùªÀ e©Mz,‡Þ×úíÕ iý’®]ü‰³V¦yh"jÙ^x¹é> =úÑS»î°'¨>ý÷è‘>›¹…6¼-ZzvxŽ™ï!ˆh°3 }›{ÃÞ \Ó-¼ðá¼ ˆíç–ɾ°@²~zUÚk Kü±#D›¼¥.|Bº.K®Ã|i¤JŸXAÅDùò,ʳ£|pÓ5e9&ˆ ]÷„&|Ÿ6vgJMÓ%¿‘·5†AE]Z$1²¦Äè¶¥Éwm6Ë 1‘¥ô÷d¶ó6åÀÜúä6/=Æ-U¨–ä¶9/½ ¼ÈŠ–ßP »ß ñÔú>!˜ ô£‡Ä‰’g)OɈQø†ÿ©Ÿˆ lUjõ“¯wvE,'Ð5ô-/ædý=}ņ¯†‡£5C\„¡ÃH!&/#ÝââôÈn¼Æ“­£9ÛQëú‹VWwYêÁÉÀˆì<×¶)†<­ÚüJ¢ÂøO9pûf´ò©wµmHxláUX³`ð:©Ñ,wNy2té¶Ôåß¹úxFcûòŸ»EQ=žqë2(ðÛ¥  ï5X3Ë—Me­=CÒ¦­¤à‡^°*ï G¿ó!zþ±x›hú9-=õ.ÃGÕÇ' ’Ó$[ùš<1 †aðŒÏ·›€ÀÃNz—}›É/û”ÊÑÌÎÛéÀòÛ2Î,ñ߯âzâÖšf+O¨Œ‹üÖžPèœopÖ¸'DRÝ zÐ^KÓè!ú% Îí-ÎÊO{ß ÙoyêÝ)‰åì¨Üì@‘ F/yÕìyX¿Å?œ²'bÃA•3Ù7N½€AÜñí–…ƒ–ž@¹Éjcg.0É­Ý-•-?WÔÖp•±"¡Éžkz8ßû§ýû?þ…“endstream endobj 164 0 obj 3450 endobj 168 0 obj <> stream xœÝ[msã4þž_áá 35–üÎ3zP(ô3L¹½$í•K›^’ʯGrbíÊ~ÙIÓ\ïÃVÖËêÙí®Ô÷^à éú§$F×½OOSïrÑ+š½Óo×Äü²÷¾—ù¡þW4pztíÕ@!¼ÜÏoxÑ ü<Ï‚t5«ð’ÈOó<öRø©êpÝë{ƒáŸjL˜1ê[æ OzÃOÎÔ׃0¢þ+EúqFý#C åêCшÐïWCýd¨C½4ÔW`æ£Z›LôÔIú"J9ÛŒ¼[õyX2¤É)‘3"ωéù–ȉkØÎ0ƒì°˜ß+"—p˜é+Uëëá÷=!|ùÝUz"‘/óéýÿådY®Ú(¡©kƲ¡’‘HüT$RíN²Jå¯ʶE˜Gz©"Ñ ~'IöoMÏùà ÖßÒ3Óx?PmIž Íwª¥±TДŸ'†Ò³óyÏ‹/¡ ¬Ö‘S|‡q¦÷jzÝëOú}bKãKZÁ+&OE”÷¯2ò•—³†±¾o©uFärÎ:\ЬâÂÿj0‰¾äJ0Æþ%_ÑÌdÜ)‰Tï/cÓÞ*#eâa¢œûpÜ;ãl±©>Ò°Š(Ì­ì¥HJéÄaÿ¯Aî§Y¤xa̲QŒYêJ¬,é3FúŽVµ$ˆ6;}F£•zËÐZm­:y#Üf*ÌíÄ%š-ËÈY/šóªž,°Å˜@04[ö•+KÍ,ÔoÕ5Ž$·ˆsŽG¡2¡TgÞ¨2ÅT¥ê§›uj(6È_Š’µ;ˆâÌÏ‚ÈÓ3Dz‘UŒÑè¹È·T¬líÄèû’K±bjÚóݘž—lN#Mòvز±ÇÀ6µÉ<5+U/dž¾D~EIÆÕ‡Í‚¥‡-§ðF+£T%c€u/œñ[ÈK¹T¨UµÐÄDÈþÃ@æ¾’ƒ÷Æ…ˆe—F©Ù,Ë„šhFY®yÅ‚èâ{>[)¥ú_*e¹ÖÁú$?cäûÄI§†úÄNåjm⤠xpr£œ9‘×0d“M@hc {ƒn6Ì„bO¹RBÙUJ›s‘g“ wY„º€3tØæ63²T‚‘J´i*x8ÊÝgŽÚ‘g=®QëÚƒ™  r¤Ðç©+s/̺[Ò8\;h™f+<,÷¢Ô9ŒKGÊyÌÕTÄúpÈ™^‚ ‹çPIÔ°2íÀoÅ.ƒ¾ãÈT{9 -úSUýµ6yBä‘«zÁ:»Ò2õûz!ŸC¹òßAìçqªü·Ü6¤¦14TÚR£'LK«r¿#o9ÉÂkó²2M‹èƒ©‘Òuö—È’‘Û"j•E…Ê~ ,£Š}(O£¬Zò’“øg‡¸%O‡•ÄÚòÉ6TªÏKH¡›û†ÈWmµx•8â»g’6ùs,@ïUšæ˜úB—¹Ð¶¼ÏÔ„@3máZ–Å›¹.ª1ò†¾ÖvÌÂØC š,$2 ¾¬5%rmvÊý7Ä—¸°À8›ë;ϳÐßt%³$ˆq*ÃFy(ÿ¹Y§ÜÆ‹³ôfÛjªæJ×Qê',×»'eEÊ€sÞ83,-(£ò†CÎ<*ÛT½Ψ7ú'£0µð,ÌËY˜s¼¢b…Öa•´é¼¨É_²æõ7$&ë©Ð©,Š9R “†}Mª¼6gè8Û·Lj¥LÊ%æ€ý£Oâ~eÀh‰ÆÕüNǼ±)FAºpûÚ'@¡¥±hHN f±¬ë´dÛ¡Cee–LÝðê[Öqî8)9½ãž -ÆZÿ¦¹náº#®†«æƒãM•¤ú‘úÍ„ …Q_2sRoÒýbÐ1ù1~ °®–Ÿ Õɨ.J’ÏX¬Lår:¨BÌ”|•‡'a (¡‹LFÐ&÷þÜ„.ÂP±¡>_#C}iåc2ÒÐ¥ý/xþW6ŠÚD2©°Ü‚±p#crÆ‚ŒÁäq&gI?å<Ÿ±i<7Ô´Qn=2»ŠaIæ $Y’ÉROœÇ²¾÷0»Á¾ì0Û¼D™è6bŽ”3¬!MmÀœ}/K>w ŒQØõ»l—Â;õ½®ÇUnAéáÜÝ5šÐ jü¬1%Ö61±GH-Óg{Iw¹”D7†Ö#«þ¼ó²J‘%ºTl—Ž0ÏfYsÃ$yQúB:R(zµÃxº©4'4HSx_ˆóŠúXœ}[ ôw6U=N7¬ê0u‹lÞJ‚–'»ëmq²¾»ŸË7À.® 5q¹ƒz%Tº2Ô?íg®û’¸BÍꤋªuX¸4¹§¯ ·T®¼lúÁl@€œZbª×ù»ìŸ±Ø³Ì!H̽áZñ›WQ¬àE•u*|¢jûpñ(°¢#÷µK[:@=†H.a+Cò‘ǰõe†—n,Nk{~d­ÂÅ»f~.³U²–Bh™¿ ÈF‡5\Ûx©hÞ»vÂ`W;p«Øàñ‹£–ÖØ odèn‹´ñkÿëF ZÆ¿—ñû~1ÁWM ìã˜.½k«¤¿êÄÚ_W‹ÜŽûn•€½µÓ*pø8†Ã&·§4h‡6~WÓ¼6w߬´Kþ)Ü 6‚ÛÆ—µÿ‰9.åÉÅÖ/Çéê²…‘ü;p Ø;b—8‡}§tÚZ—ÓU;8hm²n¡·)äoŠízˇ®·Û!#Uçcl_KH:‚+¸~|7Äx0Øú$¦øøgÛŸm<•¨Š¥ß>Dµö‡O(ìç;£‰•ó°£ã"¼O?$¼±¿†¸°˜“)®œîÙÍŠL½éò\*ÞÄû‰®¡&yôíBMŒÀVò³H¶í[8ƒ'Coý¤ë阳2@‡â·6)†á|lNžžw½ønÕÆ$'»€œmf’·þåc/‡~$ò+¸Wçá·å}Þ{ˆ,N –N¤†¤|ô÷x„ðï®m+$W Ŷ:ž:õÍ‹µ{ךW{_/ W-}7`÷UÆ‘6ÛY‡¿ Æûp¡›!þù?1;gñ‰Š_k߬v‡ª1=ù¥ºüײ»y[ç¶=ýìðE¶îȶt»Ë¢'tË;G³p²ftNQÕö¦ˆêI©¡ì;#Âv·{énYÜwU÷”ÛÞ¸ÊüZ3Ý–gJ…QúGÕ:¿¶*ŽFÖ g¿6^¸v–<ûä·–Sºþ9D°¥+Ål,#†e#ø …Øânáéd‡÷ÇÈw;îºñ¡±÷ýÔߌ5ø8ÊÇŸCHø~Ç™Ûu©I?ž/j­k¿f“Óípölh]l—fkŽ£aïõó/“Ö¢endstream endobj 169 0 obj 2574 endobj 173 0 obj <> stream xœå[Ys¹~ׯ˜·pRË1€Á\~Û•¼®Mì(q˜£Ê»•¢IJb¬ƒ+Qr²¿>9Ð=3ßpHiµ%–º0@£/ôðÇ@EÚÊý5ÀâêèÕ‡,8¿;*‡ƒokàöüèÇ£<ŠÝ¿r@‹«à›-Ô:(¢" fgG**Š\eV¤6ÊŠ" 2­¢Œ&\M‚pöÏ#­£Ä³wG³ß~œ¼ ãH¥…µ“ÛpšDJiSHð&¤ÅÚØL4'iªâÉ…ÿ>÷Ðu8¥=⤘,ýØe8Íh(KÌda:.,Ml>Ÿ×KòX"ç™w´\~©6‹ÊzŸÎJT™¶ ñå,±ÿ¥Çzû~ ‹(Ëce*|¯k$4=˜ýΉî7$íXyi“TóFŠA8¥¥:"1®Œ’,.ÚƒnjȤb0x4DhÚƒ5›[ÛÉÂCLì]ÒEÜ|và’Á€Á9ƒ~Y#1’Ÿ)TŠ®#¥k@Ö0:_·à+³t˜[¶ÅUÓ°ôß×@ Ÿ5=ªã[(™[(Å9è¿!†ï' _€íb»gðšÁKoÆðbcø>z·^íÞO Ët $ÎÖq¾òkð²§ÿ¡CÂÖÓ3~‚î=t,jTÓB Ø@„¼Ïà!ä5Äû.ÃsÅ„Ÿ ½·€²ƒ4zêeóg ©ï<ôÊC§`Þ~Þ“ÇÚ'úàÙìX |ΰX„Z£Ø/ŠÓ†}(¶±Å¦« CE m¤,GýÂv¢~’¥¢®Ž>NL8UQªÓŒd…S%…Rv’…YTØ4wÔ¤6r;yš2ŽŽ¿æÜ% *6Ž›Z‰ï[™Riâì¯Á¿SžyrMs'žaâ×´²œ<îò«R ̯1&7Ž_J ´¶Æ1ŒÇu8-¢œ°hG§ÖÊ’èܨŸÝ`Ìm>IàBÀÇ„†òƒ<-Uí7z_¦Fë†÷j¶Ë~hGkÊÔ©‚Vâ{-߸P¹“¯G·sä6³r÷$Õ¦ÙFg:u†× •!ûrdG©"ñ–¹Q—¦m‰×$ªL CœÚXN½¸$,÷»uÂ.tÜÙÚ…;ϤÓÌ#Ä}%VHhØdxÝ ¯sÑÊo"wÿBsÈ\*º.M.¶IT(gd³%Y”ÔÀ…Ç,1œ–£™Jõ€åäXŒ+˵;ÚÐFµ¬ÎJ¿¯ó(OÓtòæQšÐ‰nöWI–û£4^0$Šx7ÕQrbL)NHõWy·öƒÆC € _B É)Ë´ÄCáÖoËâLfùç ^3¸bp‹Ê†E§6Ørm po$ÂNBâÉê2¦œ ª ÞæÁmSÑ¢µÉåŽ%7xA1aÕñlòÑšei©wŒ•®ÕTNèTI4×ÄÅ€<¾TÊH‹ bî]i?&¶T„šæ\ymྂÆ9d}%7óÔ,¡JÂÄ«*«eïÌiÁŒ­ »_ñ^T¿àq)yðÛ jñ2AÃÄp @pvéùhKl„÷ž†Uše¹ÕòLž°Öœoún½ìò“ËQÁ° :¦^šÃjL{#ŠÖDªÛ§4›‘gÉ£ B“QæQdîÛÔæ&"šƒiÝ—p3ê-593Ü`èC+ïVø¬ì”ea:ÚB–µ;ÊmÒ’e$í=ÙuMC†ŽCOìE*ä._Ã:½æ¥bs) oýÝL9³ÜDøœ{h.jE‰wË¥Œ¬ÆN(ÃþÅÖ¡“aN˜zïí誶£42qÂvDŸm¬¥“YµùéÛ­˜Û;µŸD•Â(¨f"÷¬­J$þ%TÕ'ž (šRÜéˇ{YxnKÜæP‡8Ì¡)µ„$¢…k—Y[ô¢;Œ[ȣضw¨JO#À=•t"e 8‘›J •uHD˜~¢Ñv(Ü»ì{zä˜úÇqØÑp›/÷P%méR K®¸ñ{eê"\˨s:<;*—ÝC»až*ÈÍw:¸(:ŽI!† ¤Ÿ“­DVå1”ç1»ÏúL(¶RLLSö¼‰®óá E GçÎ!¿8ibj°»=3€3„‹S“ÑEXö˜˜šG—±ŽFX™üVT¤eyÍOB5Ù¢ëüJ³…YPKä¥Ùг&Èdú¨Ò–âû“‡ZäÃ-ÏdtÇî&€‹xªðL}§Q ø |øG…wkT®H70519áÔ5¼ËpjöïÕ¥'k¹ë&¾}f§7z”FÙÚåÈ:z…uæ0TéTÆd¢$¥”°faîŸd©Ñ·Ò¦dã’§¡´“ß¡gìtØ=ˆ­Äk4÷‰UEJľ–R­‚y%>FÕ½e÷ö-Emhn>¿ñÐ 4šßƒ¯Ç`Þ;Юæy¿»´ZÓ:×6ª$Þ(@|׆/ ð ÎzlÙØ«ÆËV¾$ZÀ‹tIÔ½FxfýÍCÀõà ìÆÐ<ôµ‡¾¶p€¶_„ŠWpcq‹ñ šÃ.m·ÚÈû£ÜÜÑî¢Éòúƃ¾¡d ˽ßdýœh¦JõèqĦ–Ã8Jð÷+F;+#/ÄRá@q¨)׊(õÈ XDAmÄ¡vÏ>‡kàHªyà ú s7K449ᎃàN ¡ ­m•ôU™@X okŸÊÆš"kr• ´wV‘! Zî-BKK ‰‹¹ŽŒ1ETl½æÙ¿[ÏÕ-×¾½>ç~Åï¡]ÎÃ;s'¸3Ï—8«Õ™p 3Ø>Gy)oÝ‹„‘´W‘CíT;B[Æé˜À%æ¾îÐdß7Lü&ƒßHô_AT!/Uîd»œÖ ŸöÇ$’ ‘z\AÇ0úF^( ¤'ß‚ðÿ¶ô{`g$ eŠb,9|ˆEñÊ? ¸†ÜŠ€üÔH=É ƒ£î·ÿ«Âha ÈÒÎ ˆÞ4’íôô/=ÑÚÉG)½öPê¡@1û¡¥´Fùˆz¶P¢X=w{‹æYäáöfG&.R»íµgË^Gïè[Ùkը螒¾–-í¡×?k‹úòIlçv°lZÞVWâ®ù{/鿯‡ê¹cÂÿïwüé.§Üÿ:nѽ;«K‹Âÿá§µ£, ŸvñŠe ˆa %~ÝÝ`›’ó…¯R1"$A ž(™Ñ×Ãs8z–í/õ³ÒŸÃ4FB£9 ݃¦Í bÖÚ»è+X™wÝB¶7Ãó¶iýªÁx Ô¸üaVqúôÒËâ ­_¶nž=‹Ä—[ìa8á¹ d1_Ìõ~.2zÄ9´ýÕCíø¿o)„…)H^CöF,…/ц¸Å.{ª0¡ àvBÿ‡Pû$7iL{U¿œ|d¦€¸œ<<[02ù2–kð7-¦Ùš€¤ŒÿnÇ$uHn}HÇÿ*ò"Ãn ¥úc†÷öW•ð£ò3™ÂC,æb͉¹X1çcGã_ý°›‹ÖÙËpúQ G'ªéQ½T}‹æÓ!šý"ëÓ©FÕ&6ÿ6ÀóÚ6Ô9¹÷ÿmêÐo›¯á„ï|Åà)œû¨_㎽By3;úýýn*ºžendstream endobj 174 0 obj 2776 endobj 178 0 obj <> stream xœí\Ys·~ç¯Ø·pSâh€¹•ò%‘ŽËR¤u¹Rr*EqyżÌ%)K¿>Àè3ßÌì.EYtÉòC‹i4}bºç·I(= í¿Ø?Ûxü&›-6ÊáÉ›ïkàêhã·<ˆìåÂûg“§3ó R“"(ÒÉìp# Š"³ «š¤qE2ÉTdfÂÙÆ»Íi„iÇ›WÓ­$C¥ /¦f®Òq†ƒ3%iF›Çî÷=O·”2ŠÍ¹;nef(KôæÉÔС¢"6›ŸêGò‘ËÌ…y©‹t˜u~:,Qe*nˆ/gÁú§ë…¬úaZY…ºÂ÷¤Fb¦O¦ÿ™ýcÃü•èÉì‡ ó÷_ —£ÐqÙp3·¿ÌþúÎü¸ep¨ÀðóÀ€A’E%g;ÐEgL§åÓÕ àðrѾƒdéEREÔülÁ¹€÷t#tîaþŠÉÞÏ y×Þæ*hüzM~•­“MÎ}Ö40<—äi¡¦CµeÀ5åÐåæeìGÿF‘xH'¼p&à+_ ø__й;>¢«-èæ%—Ã2Ñ«Âßù´Ë}‘”Cò«Œ8è´sš}:$ç.²pE0ŠÌ|lÓÐ:ê+ ^Q`RŒz£™Ñxå–}K˜ðÂAôŠÌv¬d{¾nýiuäñ›b¢´ç˜ó¨å˜“" Ò¸rÌzº©Ö:×›…#¥bof=ãf~䋲t*ÆFÂ6ÌŽj8eܹ?®Š@ei±¹=Ui`ëîe™—¥cÔJ•ÖÎ=gý·Y/Ö¥óƒ$UÚ €›pmá"‹Š0·géðíÃ\g&hêuT¦RËÖzƒîƒ¡6Ü~fg¤¡ánéÞ…¨ º.ñê$,£ƒ!Jã§ž.„q½+ËëBE­¥Ù„M£Þ–Ãqš™Ð È{Vh̰Îúž|.Oþ2……‚åñå6¢:)¥.ŠMÔ§FÊfs#Tx Çnˈa× Kn„,ç2`N0އ?-÷¢tª4:I2ÜbÍb¥•—"Hò"ݼæAš¤qÚœFhC·ÀlqVë±ja'édKÅAžW|XU-¢ojñ§Q ^W*Å2éZ¤Åú\бS’”.>§fd¹óBãéa’Äe®c½•·ÔäA©†LïÀAv)79HÆrk’Ãm—z>–”MIÊ Øq Ù§Ô-¿ŽðÛ­»GÏi’ 4xìwÈêk“Ôœ'Àn‚0 µf¸áÞK3fF1Œ.‡emilíCÓ[$‘±/H1,=ïnßXow¥+n„g«¾M±TïV¥© g¹Ù·Q\è®W7L $7L]Zœíû©6éC ´ 9ÄÀ´ÐI–!ŸU Ìã¤d`3; B’Ò 0d#A\š™j,%Uƒòdí͸89’Egà!]yèF0Íéæ<#â¨íM\²¶±7ĸ›„vü;jµåi-NÍÔ›JÆ Y0XÁÄÐ}ƒ¹sÊó31Ÿ¦Æ6«8LÅüù÷2 L<¤wƒ¦¼ÿ^”Ïå\6Ï{NÏÂÊÀŒŠêîÕ†ÔWbt*†%ulZÏäBˆ}ª ‰¥-"£³Ì ÍJgf•–&(®&,ãÜÁVu·? ™w4T'r×ÝÒM¼¿qO¿Vö8õcêPï™ã½¡Û=iâÀZŸ¾‘7ìq½[ù¹A+££ƒ<Í º¹æt kir®‘aÆõ¨Ë¯¸ðd$òäârì$…nozÖefGpaʧÏVÙ 6Ty-õmÜÚçqˆ·–Wlï Œ‘X¨ýJjLŽë¨Çî¤&0(”útŽÄ2ä’š ;ü‰á©Þmuƒ¥…دŽ'—÷qÖ¢mÅFEmT,÷ï…÷`ÁN—…N³aë›Ú—+¥7Õ¾õ©Ð—fºÏ/#=°Ï–ÓǽôÙBf—WKÜ*éÑAZÀ}‚¼%Ø` çÇ– ]èáé=Ó@Ï”Ž„)„¹e.,Z™Äx9•{ïÚ—*×ÎàvšØ¼×î\SësA,u> sinµ(/˜Œ5?1»é£‘N,>-4Ò©Üë% uÞæV€ ²¦55ÑV³:ð*Õ¬ˆ@r)ØBOÏ<»g0DQêÙ=0Cb㺊f9#é]W¬ºÖR«“•'ÏÌ9¬bÁ»(¡5ˆƒò·”›hˆ•9]ŽUê@2Ñò»–Àf¸8­Ì¤»ï_»ÉDk uxïo¡uð•l‡y ´?Ú:"û, öõWQCØ7ìà î º’¨ù4¿Ìþ·T…‡¼¢ÝqÐŒ¼Ž}I^ê>#ó”ƒ"2–“±mïý­ÊUT¼ï/é{Kß¶B!Ã}š XQm﷛ѧCõ‚ Î )8¡Øàå.¼Ñµw0ò–méŠîù–æ+‘ ùõÈ…w¨;ƒ/JÀôa‰«’óþ»ƒ¤ãç;ÈŠÈÙ¿ôº#•\èØXã<ë‘‹9Ý7>€àœ 5PPø¬Ýð†r™×VÓŠ Ž÷bì1.è§äe,Š÷#åŽÃ°T½Ôç¶o¾mý@ÖxAÖð%' m4Ø'9¼Ôðë=µ :a Ýй§c«2ÑQØüQ).q¨ƒ,«ƒý*<ÙjÆàóeh—HÎ[‚ù› ùXAØjÂð‰’ÃËvûðvdè]¯¸1´è†¹×‡Š<§ õã”éûÞ*]±Ö¹Èû¨½‹œŸ“'ÄïH¬¦Èù÷.çÀP8´“±ÇÎé(ˆæ!Oéç„Þ/Ÿ’p•³Qö%Ó_ñÙÀ„µd¼äøþNGNâyâŸdÝçát>ÈsƒkJ`€“„£7ö+ÅàŒos¾_ì$%#‘hòYc‡@¢{;<%2²‚<à£ç Ä縉 IÁ¬å,¿HÎ÷;uû6󑃤¿ éúé½Ïø£p\,Í[¢S@ž0,(e,¹jXä×)ß­ÀPî®x”ËÕ†G¹ë…£Ñ:áhŸ2­ ®rqÅ9½ †Cʦ=îdMnêô7¼&²Õ÷þyµhï̃5‘­Åƒ/ì;äW‰ ^“1™'^¤¶$_w–1wü–‰¯ÆïSG/!ÎèèKÙ[ßÎzF®þ0¿0r…0t3öynÞ»Rmäq¹h™—3íþÞ£¾<™_Ð-¡~¥4ÛÖ°F–—WC¹òy¹³ú·º]&¿þ&z÷"z•õ̃,w·–¾bèèÚj0Û’d¥½ûSûÅʵ¿uW´ü†–v?ÕÑ×ñƒ5Ôd¦WCÍËáXz§ ÎÅëöxIû5­XñêXX¡²WÛæVkŠÛ4V%~jJ;µ'’ò8Ó$^\÷WECIë0ñ)å•È@m‹ͦE¡^ÊϨˆCÙVÎ$Hc1PvtWÀmúØŒ‚/èj;Ó4 íw Hƒ(L2Ò.E€RF´ µ™“»”Oµj2ûäÚØß wÂèã\ôÔ}Vm–‚튥ee¯KŒW ÍU˜ðDÊ'‚àZFûJ±¨PY`ìX¹wÔTÆR•ÛsŠöQˆ¥cåÙ %²"ý\oC¥f Ê™×(ôJéXI)`­(±í‡Ô0JÕSçEL4“…O²{i–úŽÈ -†’~NX¯<±[R½JsÈ*½.ßËîy¡q‡é]Þ]a%6NÔÕÅœ–—u/RÔ×6 ’=°]Û•ÆVFª~Ç,Ëces®¦ƒw­ú‡ÚtFieà 3Ö* ¿Ó.›}lôiùõÒâxÅ®¥Ü$ö ‘ïDÚí¯5~§Gè$ÂüṠ™qQ$6ð“ OCïËk•½­’s8œ°$í ÞÂéZ–Ýî|µŸ¿ˆUŠd{âa-qlpaÛk»Ç­-Ø:·ºH´Â£E«[Ãë[,ya?šG[„~œÚO†¹ˆ}¸qA¥ú©\ă-HkægÝØ‰çç¼ÃqTøeB'è('\RdÜT–äT­ª.V)ºì¬:QYŒÏdAàr½s×A/ãíÑï¦,â¾®VÖNO›¿†î‡ê´´·Ç~@ÃëO¹º¢²¥›/¸F©5•Éd«jô{;¾ó’yÏ >©‚ùÉAß{7´Ãú÷Õ¹Á'@a ÜÛò’QN¯Zãï¯nè„áœÏ}fR¹$÷õëãðM;çË1eÜ{:ÊoÄÐ9ú·Ø¢}‘ýfGÇíô~ÈcN~~ï ð±‡iÏ(ÿ$ÂrßÐà}0ÄሱÛ÷¿÷Eî´V¹Ùàn®G¿†Ñéyµ9÷˜Ì;ßRo†ØÕ5ñU±"÷<|¼oûwè²®%>ýÁbðAõ…§ùß…ò5½ƒ-§Ä%‰‰zIÌÑ[Ïà —CpðUà3 îQÀ ¶¸¹àÖë„.Á'ð*é¥azy; lž¿øä…%ËÓK]šœ¹¼ÎmœÌ2®…ƒ?S¢®(c8?ùËÖ}::Ú5Éá®ÆOd͓ޯò¦y´â&ÓÃ\Ð:èPý‰H…«O“ò©ðÕ¡˜Q®YßÊ¿„²Ä?«½f}õ(Þµ„¢¯ä—·®¢'Ã%²cvfÿ2ÿþ¤Jcàendstream endobj 179 0 obj 3875 endobj 183 0 obj <> stream xœí[m·þ®_±ßº[Të%—ûæ´lçl¸°‘ô¬œ O÷VètÊIg×ù!ý½%%-gÈ}VÔ‹ÏŽ‹Øþ0æ’CÎÃá33ÜÕ¯Q– eæo+œÝ VÑåb°jŽN_l„»ËÁ¯ƒ:ÍÍŸU—Ïn¢§#=Pˆ¨I›2] ²´iê¬ZkQ©ÒªiЍYZé7ƒ8JFÿ‘Öu4z5ýùmš ÞÆO“a®ÿŸ«ø; ’2ËÖ*ØæI•fª´*ØšÎôéy£-± ¹ÑFIÝœ¯D3º*D<¦3'$¦¤7â"²ª(RQ—¾U¢É[c8A\™†ƒÞ+H¾²)Ô0§¾Cèýøê• Û~þ¸äèñù`]ÿàwøæ”·Ð.l"N’ºüžð¿ù Àú¬E;•êô¥2Ñìö˺ýpï<@œ¦®•ÓàÚ=ð-½AÛ9Ó!V|i%ªƒO£ÉW·æ *„þþŽLùî`¦¿'ŸBeØ—1ì(?$}>Ü÷©-ö¹¬9ay4Âȱ_|Q€/¡ÉsZðâô=D'ucûƒ¸ðË/ŠðKZñ)‰/¾J„=`O¿(°8â€F ÷ 2\Èø­Èq<òcò%7ã ¢Â) >@ÐØ&ãû os~OáÝxQ:I)æ/À¨Ÿë`;Ci>ÎqPµëà< Œ,‘øì,S‘íö-“=_'öœ]3‘~ …¿Ìæ_(Ñû™0}þľ˜E¿[è|}ï]ºûŸÝpþÿŠ€ÍOWõìÇ*ëoàõî¯õ=Þ()˜»îù}î¹õ°; ÝvÚv «ùVEôÉ%M½ð%ϯ0å°ã~Øw<3°¼¥c\ê]ì/4¦]#'.‚níç虃Ñî,Û;þÇWãß@eŒ2/`öÓ5V’1ÖøžÄ_ Ù°¾'$þζ;÷µNÑ¡x^ú`;ò!Ïñ7­ÔF¯p¦Ýì;C´ïä w@#ùÌG G²ûž§,tqBQ¤{b^ÔcOêù€õ{BNFƒê¿ÿªŠQ«endstream endobj 184 0 obj 2383 endobj 188 0 obj <> stream xœå\Ysܸ~ׯ˜ÊËrRš ‚tž6ÎÖÆ©¤v³V^âÍÃHÉŠ­c-ÉG¶ö¿ànÉ™‘|¤bû¡M‚8î¯Ì/‹"å¢pâäòàÉOfq~{Ð>^üô}O¼=?øå Î¥ûÓ>àôÉåâGöÃf!tÞT‹£³ƒ"ošºhÚ×bQ©Ü4^˜Bç¥Z]¼Ìär%r¥uÕd‹åʨ*×Bf/ìSûm]™ìãRȼ…É.íû\©Z×ÙñrUå¦ev½,òZˆ¦®³7ËUaYWÙ£O‚!üã?¹Ç¢ÖR­o—«ÚŽlÿd7Aßu^V…6Ùºk`šŠÍw¼¦®¨‡ÓnÊnÎ}ãZUöœÑW]“¢†.Ûõ=‘wK‘kÙ¸uüëè/–ÕB,š€Õ¦Œx]ÕEÞ³úÈ.Øö/ª2{eû4¢T&Ûxja)©«ªÙŸ—«2/kcDöãRæEÕ(••¾¥° Û¶É ÿ¬@ýX^Ë\;!å6°Tyad·(û°´‹zã߯éáurMOß.W:/ Ë ÇC;#Tƒ¸At‹UUÍ[n¨åœ@»ŠpU¢±ß–Ê @Ù䥚wxL½ts/ʪŸ„ãÏ…§Nœ@E)ÞýiÏÝZ²¦·Ô4Y™{oe®”¹*›öÀ˜\¨Bs­ù"ì”JæR¹“ytjeã´}_ ­øThü+šÿò+§aY_)í¿TÛØ>°Õ²§}g²œsL<ìˆÒ>–Æo‡áÒ=yšôë&‰vé NȃÌûe“›Zy¡tß3ΰi¿"†¦ ¶¯6VYéÊå4Õj×4D·.­Êa\SöU2«êD¯eVƒ¨¸&ºäòBŒr§k˜|Uõ2â^¿q¥6ºt2Ú«^D…ÊžrYq£õcüõÀþÿ«ádá5œµµ{sôû—N´eQZµ®²•%sm¤Óå$¿*ïÅÐ’‘§¾é…§n=uã©7 ó%ÙöØ“k"oaƒo‰|Aä3"ŸC’õpBä+8ð[ø”}vGä|VÒ&”Ó¬>ùªYÍ–ùžÈkØöfŽg¯!ÏNÿW÷˜ñŒm·šÜíË/¹ÛD^Á¿ƒ«¼Ÿcë÷Áæp l`ÜÙœN²¤¤½Zt{ôœLÕW´¤rï=u-1µ\{Š!+¦ÆI·_yÝ}Ž€^hbÛÅ-†Xlxfá˜íàÎ÷…mø15èíŒ  aÖ×B™¯x‡ñ¼Y{(ȾgØ„yÖ!›V~[g ¢*ªÂݵÐÔ•dµYÆ]tìnøu¯­Ga8ç;‚ 3(æSa¶™Ý ³ -gÁêS¸vê÷ˆ°“}sôï­€Í7\ëöÔµ§Þ&o­h`¡RÙý¨ ïü[ÿþ…§žyê9 h_‡üî!œ9™áÌÿtκ¼Ìþ¿Â Ñ®ȸ^n{jã)bÿEÌUÇt;^U8åì5½¨ƒº$ѸG[öU¡ƒtv–r øùv–Ÿà \'M9ÑFÓAd˺‡,8Þîðï…‚ÁƒÒ lJ¶þÚ·$q½ž0,r𑨬$±4Ö˘‰ô†oíƒ]l娄öñ¾ZéìÇåªqÖ¬(û€_ÛVYÐ쌀³Œ8¢Ã,> š‘AC?ÈŒì0‘´Ã4fÀD $¬kƒ]h^9„qŒœ [±9bÔ±Þ¡mÀ1¿ŠwKí±E¶ôïG‚w¬Œ ßF¤Ô-ŽEaÄ÷fÀ–k8, ²ŠwdŸZØÏ,Œ´0Ž„ª,#Ì:ÄÅú í gVƒÌðøV²=nÁÌ ‡°%é"¦`èõ!\ ÓeOˆüÁ%*¥M øçP’ØzÙ>2…„¾í\•(ìì´Út›ž@ɹöúk©À«ñcá­—QèÒMfÖ-z-é5êIjg¹ÁúþÀO@«^Š*׺Ô >ƒLDzXóÞÆÞE«•h¶xv›8j°ÅtÛ•¨ÑÓèÀÙ‡ØI„î V€É›P–Léœ@ý±›ÐÀزNíjYï:3µõ!:³Ôäº(¹éQ…‹±¯:¤T•nâKœ9T; ø0rŠéÛ«5Ärþª7ŸÒç °æÁëßl k»JmxÚ‘%ήùèVdìÉ­½¶6Ÿi0±YÐIXI½VÓ‡²HOƽ²£Ðú›Bë‘ ßÁ¦C[VFñ©Åá6ÄÓxÇwÏÝ ‡å¸.ôö x¿‚ÆOýÓS¿oÉ—9šÁ- ~%?æ„Nú•âB‘o“‡î©V½§DžaT¿ÀÎë¹?ć<ÆŽ8/¤+ÚP=çŸíÀù5<¸ÌÖÝÃUŒ©¼Ø’­ùñmo¸Êa ef¹_ÌŽ¯áÚƒŒ·£¶X¤š[å°çvøåýyd ,îm½·j³ýäªÇJû30ûán0BÛ¾Á-ÃŽÈn?mlZÄ$-++kn\ú@ÕÝfÁZ„dCm%á¢ÏÉ)aŸSŸì\⺠ÙÀF¿’JŽ¸Ö ®ÎZú ß„qcbç‡H=‘î`£jkh´à)"ÿS•Ð9×ÎSï¡fÇ2[p•«Ò²…öˆì`TË…ã=7p?Øq…l¹@a3vÔ!‡Ç #RõkÞ/:Øã¦4óÀ]Æï±¼‰CÛÉ2ÛmÆcésßm—G¶žÍHY– ß`¬Æ¦b”vT5æÖ¾^¬«¹Øop|b Š?ЉdÓ~Ö¹>US·9‰ìöå¸3oYš¼–^ÕN†¦ÚXðð¬ùA¾Ã.þ4*´`6©¤â™ pb3[jµ>S ,ø;«’±!Ë\¡)à4[ú„B´ü²ÜÇà‘Õ§†‰“A‰a5È> c©t^Õ"FñøA¶XÍ|vª}ÅìL¢`©ú™±/ø¥ F/SeÚua Ï ².RsqZ*L1îc” ã”Kn ÅÃñÛÝ4fZÝtÎ`GjÙ¥ L¢wã¶È¡!жCm'=Ê$6lÂ(¯#|^ü‡¡/®›Ub?gpÌ­=Ÿ¶ºâ« O&—1\ =ø5iýÛÞ*É\6^l~^’º FàŠIUÖ…µÈgÕ#¥®š/2=£—u6^µô›ÚfC‡g„pX÷þ5vi‘›‰s×S*8Ä!ÅŠÏ`˜a$Žé¦ñ‚SvøØ1a©@öª5ö«-*pÀé¡1ÿ´É¥Ú' ˆAÖBA=æŒñÀͦ,g‘ÛÞžlZ¾ú¹ÃžÛGâVNu!Ôìç±n5‡SotÁ§3TÖy¬uŒƒ-ä%Å0#éý˜MdZ*fë8V¹R"ˆ¾=®k—Üå •TœVˆY À&a€v‡hø¾b1©ã&² -é Ê0ÇÄ _ Œíýù¬­5+˜*Kì9ûžÈ ÏñÑkëÒû"™ÐAóPn$B嘿Ö%BU]ñœ!ë½u”J3ÓJcž¤,仉²NÞŽr£‡ K:dDÛBЯ!1¹‹]Ü61Y?$3‰)ƒ‡†UqeÉ»P¤e ȇw©Ç¼œ15æTúOg‘*üÛ@îa+Éô嘗ÚUcéÜÊÆ [&¦R¯(àyb’±ó±SøºÛ_=æÑ2.c1)‡‹\rÂÔÓpµf÷ÔSt‹fOÑÆW0Èi'ÖŃÞÙ"’íÓ8³”ÓgþB³EL•f o(}±Ò ÿ»¤ßRÅ ×ìa¸ˆ’’qÎ2EæÈÁâU“3ÉáÙªI×ËM¼ ãeûH…%÷($²%,è“ñ•Ý3Ÿš¥ +÷z:WˆzJ©ÕÐ-üe[ß £²#ºùqL†|°Ä @_C7ùµ%&ñ'vûŸ7 €ƒû¦ÖàÇIBØ `¨Òøh\±ÄŽÚ¸9ýI]€Ã²ãFìCsý<¤Æ1 ‘Ì&aq )n^±«Æ:»é=b+$•w/ñÅSŒvJPG°+J½3Ó–(f×4õÞ†yÇJ!pà¾ó†éô¯’KŽ[ÄäpœÒøh¼òßÌÙ½GM6Ž‹ÓˆI@…0ŒáïÆ3¸ >zZΠðNy‰Nú§lÀÖÎ!9sÀe<Ò¿tÃ’\Â3ðEr‘7PΤů’oÇT2×Çuý‰}Ä—;LŸd‘ÚŒ—è —W `Çí$tÜpôFÛ™l?ZYx§…4ªµ3ŒOzÉÓ¤‚Ìok[qß5ÐþÞ–¶;Úh3ìÚYW*³½ª ~B,.8ó¸iÅü¤waP±ñàÔƒf´Ç?3ièI{Ï_ì6 Ãš*±_¦°½½{•¨ìàçúöíÓjüqnB<êm†æ'—æÁwG·ÿ Å=í)endstream endobj 189 0 obj 3450 endobj 193 0 obj <> stream xœíWIo1¾Ï¯ð bŒý¼9VªPÐÜ*¥ISiÕ4,êÇžÅ~Îx-4RûÅË÷öç×;™ÂÃg—Ûêå{K6÷U¿LÞ¿ÁnSÝU-“á§_ÀørKN:Qâ˜3¤»ª8s®åv`Ä(fÓÄ Îé¶Õ9ý^7À µVÐëš3+@YºŽhW7šq.À¡ERƒm?to¼,É£,ð@Zëu«ŠžÕÝç\øÚ ûçž„3©á’Þ× BŒ¡Ÿ”Új ›(ï¦n„ð«Îo{ „tŠ^õkV(¼8¨(ÛžËH‡&¶VÒU:Ÿög&÷û—oŸøH’Q$D°ÌØkØÿºÅíÁ|eÚ\TÜ¿Á¢zŒ€…ÐÕ2Pfr½WX2m… —à.Áu‚÷΃Iƒ(eŒ·þû<ʃ•!žc¼Aºã¶!‚Û<#Š Ä—( ÄÈ;ú5qm}(Æ­ôcZ]ÃW4§¬-Òë:éµ.žEÊ^Øcà³UÉmŠšÛDfÓµ –°ÄKê÷À0°†4¾m…š'¡æýMí¿¾­ºççô´–Œ§ý‘jy&õ”›ˆV±Ú×hÏùO¹ƒºØ%`ró‘[WÅr=V£ ­$?¹ÏB*¼ÁZœk‡]$QÝã>ŠŽv^§-”ìßj¯ºåZ/$ǾHP>‹úêmÑ;¨\S‘M¯óÄÿyvÐ8k§ñÖI‚)5õÙgˈöAD­ hyô Ñψ^DtÑCaw&5ÕL Q­x³&£/³^+ú:ëÝëcÖF—þÓÿù7%Q+áIݸðÂqëƒÉßT½Å7_[f@=åÍ‹¯»ô#ªm¡ïìÝ*ùn!Á|¢*pOÑ~§%×¶”•ÇHŠíoi܈ð¸ PàŸ8ndƒÒ#&?o÷ÙäÃ6KÙ9C±8y¨ð¿G߅ê4ÚüÝä!2qò°¾óø1#›> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 42 0 obj <> /Contents 43 0 R >> endobj 47 0 obj <> /Contents 48 0 R >> endobj 52 0 obj <> /Contents 53 0 R >> endobj 57 0 obj <> /Contents 58 0 R >> endobj 62 0 obj <> /Contents 63 0 R >> endobj 67 0 obj <> /Contents 68 0 R >> endobj 72 0 obj <> /Contents 73 0 R >> endobj 77 0 obj <> /Contents 78 0 R >> endobj 82 0 obj <> /Contents 83 0 R >> endobj 87 0 obj <> /Contents 88 0 R >> endobj 92 0 obj <> /Contents 93 0 R >> endobj 97 0 obj <> /Contents 98 0 R >> endobj 102 0 obj <> /Contents 103 0 R >> endobj 107 0 obj <> /Contents 108 0 R >> endobj 112 0 obj <> /Contents 113 0 R >> endobj 117 0 obj <> /Contents 118 0 R >> endobj 122 0 obj <> /Contents 123 0 R >> endobj 127 0 obj <> /Contents 128 0 R >> endobj 132 0 obj <> /Contents 133 0 R >> endobj 137 0 obj <> /Contents 138 0 R >> endobj 142 0 obj <> /Contents 143 0 R >> endobj 147 0 obj <> /Contents 148 0 R >> endobj 152 0 obj <> /Contents 153 0 R >> endobj 157 0 obj <> /Contents 158 0 R >> endobj 162 0 obj <> /Contents 163 0 R >> endobj 167 0 obj <> /Contents 168 0 R >> endobj 172 0 obj <> /Contents 173 0 R >> endobj 177 0 obj <> /Contents 178 0 R >> endobj 182 0 obj <> /Contents 183 0 R >> endobj 187 0 obj <> /Contents 188 0 R >> endobj 192 0 obj <> /Contents 193 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 26 0 R 35 0 R 42 0 R 47 0 R 52 0 R 57 0 R 62 0 R 67 0 R 72 0 R 77 0 R 82 0 R 87 0 R 92 0 R 97 0 R 102 0 R 107 0 R 112 0 R 117 0 R 122 0 R 127 0 R 132 0 R 137 0 R 142 0 R 147 0 R 152 0 R 157 0 R 162 0 R 167 0 R 172 0 R 177 0 R 182 0 R 187 0 R 192 0 R ] /Count 36 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 45 0 obj <> endobj 46 0 obj <> endobj 50 0 obj <> endobj 51 0 obj <> endobj 55 0 obj <> endobj 56 0 obj <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 65 0 obj <> endobj 66 0 obj <> endobj 70 0 obj <> endobj 71 0 obj <> endobj 75 0 obj <> endobj 76 0 obj <> endobj 80 0 obj <> endobj 81 0 obj <> endobj 85 0 obj <> endobj 86 0 obj <> endobj 90 0 obj <> endobj 91 0 obj <> endobj 95 0 obj <> endobj 96 0 obj <> endobj 100 0 obj <> endobj 101 0 obj <> endobj 105 0 obj <> endobj 106 0 obj <> endobj 110 0 obj <> endobj 111 0 obj <> endobj 115 0 obj <> endobj 116 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> endobj 125 0 obj <> endobj 126 0 obj <> endobj 130 0 obj <> endobj 131 0 obj <> endobj 135 0 obj <> endobj 136 0 obj <> endobj 140 0 obj <> endobj 141 0 obj <> endobj 145 0 obj <> endobj 146 0 obj <> endobj 150 0 obj <> endobj 151 0 obj <> endobj 155 0 obj <> endobj 156 0 obj <> endobj 160 0 obj <> endobj 161 0 obj <> endobj 165 0 obj <> endobj 166 0 obj <> endobj 170 0 obj <> endobj 171 0 obj <> endobj 175 0 obj <> endobj 176 0 obj <> endobj 180 0 obj <> endobj 181 0 obj <> endobj 185 0 obj <> endobj 186 0 obj <> endobj 190 0 obj <> endobj 191 0 obj <> endobj 195 0 obj <> endobj 196 0 obj <> endobj 197 0 obj <>stream xœmx XS׺öŽ!{o­b5¦&Ú&´ŽuÖjgÅ©ŠˆÈ "h„f’¦Â L2#ó ˆ³8Wë<×ÚÚž¶Ú¶÷¬Í]œûܼçœÿÿïÿ®Qâ=›ƒ÷n‰ qßê±-.lßöxÏ/Â÷'Fzí”I‚æ~ê´`á¢ÅK–.ûlùŠÏW:»ÄBâ—ØCl&\ˆEÄ b/±…XEÌ$܉­Äb±XJÌ&öÛ‰eÄ“ø‚øŒ˜Kì'vˉO /b18@¸ŸÞÄ.b±’X@ø» Wb,1Ž`Gb"ñ>1‰˜Lp‰)ø€˜Jð 1X‹L8àןb­fÃSÇžÂîw R 'Šó;¹‡¼Am¡Êéèš±c¿—0îû÷Ôïý5^3aÊ„ ýŽ>Ž·'®™˜1ñÒû3ÞïŸ4oÒ™IhrÉä?¹ÉSÆOàñy><+¯Œw‹÷5ïïLø òƒ®©ã¦š¦^á¯ç·ðŸ ü_B£#MÖÁün›üëÐTnsnâ)Ī8¥"ô¨§ÜÐÛÉMê¼#¡ÀÝââ§¹´ µÈ¨‹ l¡GöRÜ6°E¥Ü”JË(¼¼…<7q‡ƒ út1—³†½l¼CF]aè”ÍÜä'’èèˆf'š­IòËÈ ¯òK´Ô™Å¹QËV²k¾9Ê T£Hñʤe0ŸQÃB¤`¤QÇ‘IG‘CÌ)+‹‘2^<4]I6•$'¥eÒõ¢y¨Q0ÏPœfEPf5õdÓV¤¢jÒJµù H¢L¾1Å*©ó) JPƹ)$ªèx™ @r¶.WfRä(—˜{¬)¦ûÖ©‹pæy\Ä,°4VøýÀÊL»Àzön~É.a>áå ³Š]W ô¡, xhUÞi´†Z)若Òðuêd¥¹ÐÕ_ˆ˜nj…ý¡£VÊ7Wc4”S?\Üè¡Ø¹Mø”ÒgûÊôžIø{u÷N=ëøc6ó \ÂóÛëè%;ŸAê wŸ·Z´Á…¢¼ø|i™¼j*mU·ô¯ôõN ùH·54úàé"È>ÙYÒØ*¬­¶Õ6\¦GK%«c›Ì­vžÇCÉ2ò|F‰Ä¥V‚8Hȼ¼û›ØP)åÖü欺,¾ ]Q7ÓË”¿ d¡å|´í_öqjšw.«ÊF¹çÈóA°Z·õÁ÷à\þÏȩٷ, gœ{È“ta™4·" h35öŸ¶Q^yÊÂÃÈÖòál¸ãæ“róãN¶a ²z™²!öðçÌÞˆœ5ê5v°ÙFmÊUƒ»4³ƒÔby‡AêG¡è‘l”Äds¯Œ|‘jÑ€MôÈ< ÌTÉœ xÆF­ËQTßifsFú9ŤãðbœŽ6ØÝ Ím2ÛdÈ‚©C« 5•ÛRŠ“#£^¦šµ`£ù`£Zíœnǽ~K>w+ÚN´6•Ÿ§@¯¼3¢^ReÞQrÖRQX]As[ŽWZÛº¦A‡eƒh£Ð½æ½}©­Jš+¾[{ÄmØŸpôH\¸<(å @ûª šD°ËÃûy…©À*ì%!uÕoÙZ¯€…" ?]³¬ŽUÿ=,þž Ìaú`É|$DÓœ 'ÃÉoþ áÔÅ?£)"µ”÷òò|ôâÚ¶áhpug‚Hz6éN×Ïý×î í{e¦Õ±j_ÁÄ—løVǃsÎrJ(yfHtHJQŸˆ¹A¥fû)Bu;‚$*?+亭@$BK(qSTÑ@#j†Ê'hÊÃåpÌÅöòþ¢d9½^£ÓÊct‘€vö|'BÎÀƒGçz‰FÑ7ZÛÛ¸°ûìè“Êȳ™¥) èR5ÉJ4%ðÑ\£-1”޶sqASv(EY¸ÊÍ2êTF±ê7ô1œŠ<ùh–ëtKÒpy/Û¨eÙ*3xDC/ Î… W¿«©<ƒñ4¼XÕË8W@—Nfö0$&pHCB’&$¥Q™ç[⓸çˆ]ž[v‰4,jXqÆãÊŽ¿‰~í»úœ–—mÜü…ÄÜÀ¾òÃížßȦá?±pÖ¥³ G:…MáV©mm'-I©¢ º71ŽW&?| ã_Må* ˜xY&Pè&ÝAÑH(Å´Ú½ZAq•0µŒÜ¥.×i¸”j9V­ë4÷7ègÁ÷·¼ž·/ f€è1eÈÞŸªöT༠¼†‹­}Ö#±}7¸ôhmÚ)ä+Ù©V³:óüÖŽÜ <ÀáúÈKòv}sÆU^%Snêêcš$­þ€8(‡ ŠÚ \h$¼·R??¼ ‰n!zðŠžœh» ®‚j©e¹IT­Ìµ²N@’YI6üǰ3íC¢™ÈaÓ’óî¢A·×G¿Ã§÷B'èÿkä7žWDn_­læÐúä­áç}ôثޚx 9FzHîüÁ¡²ÐÆ}Ç~ÇCuo»ûéŽžÊ àX¬ùÆÞ³1$dMå–þ Y<àªQ¯§ØFBüÕ|íâÍËm¯Á[ð½ä‰ç…m_!V+úÐÜRT%#Ÿê ’q{5ó¸Eù]¯n&…>Ùßµ  qA+WôŽt×,ô îQGø +Áœ&Ö¸w®Ùµh"¤ÉA[c{E±.²LXmÖ•ºÊVVÕP³g—W”´(: %$s5íþÝ"Á&­ÆÕ.+åbÔ‡4¼)GæÑÊNxÚÔoC0ÉÔ[xöòj2J3kÀp¾°§úJSëUð%èVµGÕé]^·‡ò•ÊÈ)…:àJl¡ÀÊÝzý;ª\cTƒû4“àÀ}[k2ÕÂé×Μ·×Q‰ª¬ì8á;GÐ{º–Ãí²wÆ †ù ®åIIDŽE3Eú‚À\½)Õ˜QJià"ê°ª-ÎtTÆ‘ú 4¥V¥VHSbjXcH®¢./¯V§“¸w*•¥Kéã$·'ÎàŒÔáÃþËPüÛN(˜6r4¿ð·_X^²MÌ4^±± Ë èãI"d¤€^¿Sƒ“§·RÛŒ +¸DÃxª©çtm^Aª®HX¢*2X]m-­kN( ŒŒÛì+ú&3¯øõ^™`ÔH\‚×Ãc¹u“ëîD~ ·<®­ÊEÌ1†ÏëXG%b:H4Åçó£ £r£qr᢮KÜjÅ_Éär…!õX¤:$‚³¦+‘æ¢Í¢ŽOózðóÃ'5Ýg…í¥M \;ؽшxy|¯ì˜PÊ›[jó 2M™F`˰dæ6Ðim¬m®³¶€Ó >µJÕD#ýÈ4^³þ™aÐ?4«CÜ×EœÏÜë{«L–îÓ".Ì…?ò.×µô5Ö($%²P³?8Jï‹ õßxó·ÚR6ÜùïžvGŸñi\Ý»ê¯Û–ÖÒ¾Î_¶ÿ0ÊXþèC8·ë»† ø´ÁåòNaG`SâÝØ~Oþí5pÂaø°bžÂÌ›G¢ï‡y= OÖqÞ¯i>@07rÖÿà qžÀ”†4yõ‡ÿàXð¦þ—®¾®ÞþŠAܪ£°¾ú¡ë/&&ñ0l(Íj¹r5Ì, ˜º\;\>ü?áòöÿ… ƶ³v´ÁíØ^—ƒmÀ=FÂYÔsPª4¯Â¯è*“*˜¥åêÊÉg®A—;˜àmx‹Ç]?ähÉŒŒd}ff€.[›‹y ?>>'zúo×}MG¾ñ kI,‚€p©wX„¥R&L8ž\¥¾D'‘\7´ÄB™ s²Í83ÅU¼¡®6­lúãƒwNÇ6ïi!↴DyÔ :ëú.5„-«¾³Óϳ»1w,…oxÝ} ¥€>}L)Ô‚Íïr¯Ól°[©œû\µÅž{êb6LFW9p9âǼâq×åwÂIƒ‡ú$†È„þÑ¡†ù£zÑ ¯—Avœ×ÉjÂuzËöç¡|9Ù“nN7§X“óâÀa}N…ïŽrE+ç@îþ©î‡ÞaïÀµÚÛà"u„4HK¢Žï¦«È‰' 2¹*è@’Qn:dõÏ?„u;‰9èð§µ.îÝÚúF Ç€WNjél¡a.›-ƒ¹´#lÄnh|ë.I>¦óÙ˜Í 3¡>٬׉=û%¥iõ=HéYÆ ú:O®l8|y ½úl³P]”£ÐÅA°²öºvüNá-q¡¯±~Æ[ºÚÚbOŽ›(s¶%«ªG©LIWv«]tVê‹Ü”B{2!å xQqqRie\]}Ue}]\U$öçÔ -Ì® 8¿kòÝWPõûTî³ëm™§Õ*pðx„É.·A€âXdh„<«{÷A‡½÷ÄgƒŽ‹ Ô¹ –æ*·ûøoZæv:úãHî@ åT’Y6s±`vO³éÛ•)X¸©×OÿxáyñïŠ> ìçé Ýí×NvËBZ„áÅÑ%{q›¾ÿza›õ÷!xqˆ Åñ° ÷ŠŠ›°ok”QõEÚÊ“N³ÖÞÂ6jC†Ñ]Á‹èÀÿï»Çÿß» /Ênd^©ªxòÝ—pëKŒl&ö4/•ô×b ßPÜÇh™‚¼–nÖi( MU©´Qñ  †ŸèqO§h¶Š:•^¬þ4Ê¥Öœò¾ßÖQR[+ìéá8S9§Ë[‹Î FU’ù¥›M8:Ó0ÁI§ÀçZ­³ý´'m”KŽÊb—Ùüó$A¥¼ ¡«I\ǹJÐ:N5Y YU6è€)kÜ;LáÍ(»kÙ Ö¬L7¹“FÇ}¿Øò2Ç}ZF}ón ÁA3Ûðmw"<§[u@tº4Ý\dæÏ‚†âôbP€s‘©Åˆç`o%ÕžaÒõÍ„ãQ'?Û`ÔSòäy)y ˜,Ö68>à×ßÎ35éw$åRÌzÕw˜ÍÀÍ*ts¡ÚO4’@éé®võU[©Ä¬#ÙØY´ ÐcZvzvº1]¯3@*œlÐeJi%Œ¶À3h!ôâ××ÞýòZ_}¾ÀšSœ}R¾V‘™Ei@R’U* \ùŒ3•–-ÖD¦¸' $)öûlá‡m¬?‡à?zÙ dÌ<0d.yQˆ“£”QO %ñ`ª0…êÅI’(¿Àfà[Õ/oÒ·Ù­ôýQ=³Ã¨UF=5`m¢ÑM m|î×AÖÖÛÂêî‚>ðØNU£Y¾³¬¤Nè õ@”†XmÔRÃ_ ã}‹T™úM8ÇJª-ͬ-ßo’™dyò%È?§f‚B¨0çŸÈÁOmWRUùºJo8ýÌ7KòUy˜¾ó-æìíõü¶88Ç&[PÒ‰'º_ùeÝÆÜrüŠw´Âúå%Û†ë’Ge™³ðLÖT¨ÂD4ö«Ôn©¸2Z+å–£)hæ'œÃ ±n«bô6aáWOnûnëwpÆó©Ü™Ý_ò*MªÐqúâ^ÄÝw6µ^YßzضÛžÇ 7úºÅ•'VUW”WædæfšD™ùÀD×6UvÔFîî¦Ð¢]Š”Ãbšûc|¢*ìØ4·Þ€«½]g¯ s”%t8ø–v-þš"R“4±*ir C¥'zD9˜…»Zá„^{µÃl¬á]ö™0PF^H/Ó)HÒGi$hΛæÃŽÝO©V°Úg¢¬bûLh“Qx&ìY3GúøVÆ0ƒÍO‹ŠNÿž7ßb3K‡§òÌ™… 4d˜S°ƒ‹“xz£-hân+*zn¿Ih³ʬþ§äißÝ:´Ø¨µö+›û4ÜCÕTüÇ'6œß b@¥ÒK3SŒ^k Ú¨YªÂ%o‘ ùÐî½}³$ÿv®À†¬Ô»ör¶²`ãÐßJF€Ä’ˆ^4zó¿†ãmµ %MY+Z¦¤2 ¸#4šø˜ˆTƒF®NT•ó¥ÝòÀŠ-¦†, §#ʪ”¨þ„fò‰\$A:ý±tβRRM®%Ïdµ”‹žÂ‰? §C¶@®O;fc>ÉGgY¯^B7û‡mc>áYŒùö;­š‚$?ʳƒJ¹/ƒ*ÆJye+‹@ [(_h2¯ê)mtoe(¶. ìÓj=ìbg¥<²”¥à s©†žæ²>@_*‹r¡ x¤¤¸ëñCR+-ÉV— ¢à¸£WÖ¹{G»ïÊ/…Õî@ªrv£RúloY˜ÖSi÷íº&æ½ë¬¯^ÂÄïØpáðZ^jv 2*å Zi÷ByY9Àè΂ä@ÑH ñͱg?ÇÀpâúŸæìvñMz9tô·ž}rz-rDl¿.Ub:>õŠ Oa5Ýè'‰öß¼æÝj>y\¡O‡v ¯’µæ™²²ò„¦l²0_æRÂ¥Áûƒíƒ(ôºÎꀅ<7˜yóƒNú'‰‰Žˆ<SßXs¼¾1ºF‚ŸkTU T΂8Àí$’ÿc+ç6‰ªªž\ê~-oì’æÌóŠàP÷}bD†ÏjFcû÷µœ‰g@OikËà…vö6ï¿úkØ”MÏ »‰já|¬t\ZC£ ÔÁ{uÚMBcŽîÞòÙþŸàâ3ùuåVQEIcA‹}nLO©`î=`1û±$Œä^4§4¥çv™å@ ‰|G~äÜ$aèð'œÑ£ ;ÖM¾øhË(ýÊóÑTîkØÃPxùõ\K÷íi/\¯ÌA¬UÛ—ì°EþµXÈ}—øˆmÓæ=Ý9püã/ÿx6€&|/”ÏäÝ?PöÑ;Ú±Þ{ðÛ•݃ýÂÇx¯eƒ!}§ žìº~»'`«‡ô‡X˜š@Zºý¸P×Ã8Ô±þõoÙ—ãyÐÁrÁv{àÏGWáûN§¡Ëb8ñÐ{Ëf£éÈñÞ*H\è.é:' B+Û -ˆ¦áe8̉iÉÚdil°:ÐëýÂ÷.•\©¨•–/ªô·'W 5"ÇÄrÆ«z–“ ã¾y¯!üøoÊÆO ˆÿ½,ºB endstream endobj 198 0 obj 6518 endobj 199 0 obj <>stream xœYTT׺>sf"6‡"z* `‰=ŠPAQ 6EEADzf†¶©Ó胣¨X¬Än”hD,¹–$xñzo4¹ÉòöÜ·Þ>3ÀL²nÞ{Ë%ÃÙ³Ï.ÿÿ}ß_°¢lúQVVV¢ù±‰ñQñüï8+nx?n„uâ¸ß&FPÞSw,`…X£6û‡oÏ%ÛÁÁ2„²¶²J.®ž—µ}Ç^Wk<ÇŽg™äåååº%¥÷× QÛw»~@~Ù±{ï,×ùdvttÔV×íÑ)q;\÷m‹ØÆ¿¶:<:b—«_TtT\\ì>Wùž®“'Nœ4žü˜³%1Á50vw¬ëR×Û£Ãã7HQÔª¥>)»·® ôMݶ.h~\Äúe öD/Œß¾Ü/aÇ ÿ½Q+%î\µxß®€¤èðÕK’c¶¬ñðë:nü¨GO˜8irÆÜ)S§…~4}㠭צ™³Òf»Ï¡¨ñÔ(j=µŒZ@yQR£©P*˜ZHͤ&PnÔrÊšEM¤Þ§VPþÔ$êj%µˆšL¹S«¨ÅÔʃ ¡¨©”'µšZBM£ÆPk¨¥ÔGÔXj-HùRÓ©qÔ:*ˆšOÍ Þ£úS(+j 5ˆ²¦S6ÔÊŽ²§ÄTÅRC) å@ͦ)'j.åLÅRÃ(oÊ…Nm FP;(† !.&/-§nY͵ºÖof¿ï¬7Z?°am6Ùt – NÒîtý³ÐW¨þ"Ú+zùÞÂ÷nöŸÚÿ‚m”í×jº ¬ä6({гÁeC‡$yhça·Ç®Ã~µýYñ@ñ^qóP¯¡J #¹éépÏqŽcc—“‹ã4Ïi³“Ö©Å9ÒYíüßÃ|‡rê²Åå×áAï ÿyDÒˆ–/™õLK±vl ‹Øw®s]Ã\k¸7ƒ¸7HÏ%ê­`f 46[Ã6N'y@ש”•U2e ‹Cè”yF¦Z^Ç>4$ÎãŸ23ø'Î6Òµ Mf†\šÊ੸Qh\ ÜõpNo¥‚-0 "­9o–\Ãî‹Wa"­S)«*eÊd6’Æë8o¦SddÿõûÑtŠ”¼¡á‡ÆÒujòF9Òlú2¸  L/ÁY4PpNPM“qP W«·jƒ9à3¬á¤Ãì&Àb²­,3S-#‹d{©*+eª6€5W+¨2/‹­éTÓkÙ«4ž†Ÿãð\ð™Å !,#3TrkÜ’\¶[§·{=4´8ŠÁ,nšÄ›¿ ?«ŽmÂÝat²iU Žæk‡A9}\¯ÑÖhEâ³?nÔœvùéä“頻G1"Œ¢„æÛ÷£kM‡He£@I?^xcJàö¤¥Ërˆ4=ÂäÏõŽâÓÜNn€$üäêú $ÂÃÆŽÃNxèÀ±££þdû±®¦UŠÔRuެ 0GƬ ]’0ŸÌì7ïÁ¿Àúaô{Ö¾º”ßM-.—W¹T(Õ:V|ú¥P|·N®ÎbħÓå9)l/jì›!û ˜B>ýÀËQü ƒ^þI¼"¾_«QVVKËSyëöáè,Çã x"l´òÖ­¬2Z·o]¼uÁgâ`Œ©´ø•8Žfo΄à…w –˜=Î_Œa°Ö¼ÙZ#$3„÷¯…- Úô‹ÝqƒÒÀSØŽšäÇ“Oï;°m®\·€1n¼†‚ØóŸ"°Hæb 0ñÏãèdeNeq*sJ…ø ~%ØÎõ#$ÎÍÈ$žeÄgW"Ãux%0b‹³2¯?9ûen¾Û¿ïŽc»w@PÿîŸ0†ºÿe·ØüëéÌÑÓgº¹Mòîíó'?±Æ÷ãõðN6õûëíÏ·×¼9Š3¹î­d¼ÙW°”^ß¹âÄ<EÛ1dV܆¿ùlÞ<ùÕyFœ1ùµ°ÏEâÌU4Ž…w’·gº‘™î³fzºÏ!»>~òO¦Ïåz˜ßÑÜ ¬á(%°ö šèZª¢Rª"xh¶p3=pI‚Ox†òÊ—lœÑ‹Ø&áý5RhM©4£ÌHûIwõÖ\§kšÁíÉË¿˜Ží2l¥ñ ®H0’N5¡–½Â#ʵÚ>Dm¥á†¡¨ÇJ½Õdùx®T²áì²C‹Ñ"´lOXhøÆØ…(H4Cˆ]`0 Ãoµ:‘9|¨¢iDÚ•T–[ S0!KÖîñ#Fîïþ²÷„`û·7`ýâÉê ¦(­\V‰DUJ ¡‘°VÞ#í&m…õð˜;rAŽ@ê ¸Hã¯Àƒ™¿Bmy"Z«‘‘BˆuˆÖæ­õ„úüâ«1†üð=زâ'`;þ%â½6Îo5~W%ïúxN˜åíé1ûÁë×_wýÓ„ðÕýW¡t® >ýøŽg³ÚÆ’ uŸ€a‡_ÝÀå% ¸Ð¤ÉÑä(ò råìÖ]¾‰ShÖŒ_DzÝ«{×ß 7¨+¸f¢¨‡ÐAÈѯ®·ðäÈu€aL‘¡0RÄ_– á ·Ypða²*§ªBEXýáx¾?1lˆ“ƒ…}>iÓsS¹/ÁDɰ#öÂ^@>…`Ž0¼1<’PDßc»áã° 0=„à ®ß“kŽÈ„¼ß™4ñ48¼%KŽb¯ aÔ`ƒk‰U½!QÂ%Âhát:ýäý–CËÐj‘¯pÔqôf»~#!¤|Ãm‚n‰øÉíÐcs]ðà±±-¶ýûôøZcÇ9—xSM¡Ôè^ñkÉÁɬøÍj®ö²q¤µãiñìGjt«‡ûâÖÇ]?²½QÓ "ÉÕJI°‚P‹ÚN_:{²c¿>¯à0sAP1UmÔ Uf1]î§Œ@¢IKVû²AS…}õ;Üíi¡Ç%4ï ³8¸'N¯ÐC‰É¤Oɾ^&Ù4ÐÝ]¯~˜~o‹Z®!ÑR£3è¾[a††4(@á㎕þ~óWLe{µì^é‚ëÖò«>©ÙQ|‰lCTÚ-ŒN1Á§ŽGfY2³0ìBqœó¤S•ÒJžw °ៜDœÁ…ÀTºN¡ÎL7âôqЗÂ^‹F“ÝçbÞ…h^ëE<³ŒÌ|€»ÇŸL̼CÄE¥­ÊæUÐöÐâÓo¡ßÙƒg] úìC{•ò’‚rÞêš bõÔ*vÏþDÍn´Ç~8Côø|¡‡¿MÈÃ'¾#ðéØ°æ˜¯ ¶?‹°ð‡±`ÓyùXÇyK„þ«BüümF‘nC§jOžn<^y éEd+Bñë><a4'Nòº‹Àhž÷‡ã}Œºº~à­Ž…úôfî‰ÞÞÈ=Bc¸AœÊ”þÓE$1莄Œ½tt}Ží€Žg&ÈsóHîLŽ )-)**e>­øRÛˆPMþÇR’ŽIjók\Zþr¬¾BªÉÎÏ+ÈÏgw%¥Ê÷¡L$+NP‹ÄÉÚ˜½%»]ö¡ôÂh…è"×-ù¿)m‘ ¸µÀÐfkKxTVšá‘™ÙøÒS˜Z.­ª*7a#äϰñ”šB/Qž `g”줳pò¼£XË-à$’ǪW ‘ØÏÓ74`WÝî#)ì‘Ô#ò.ù5ùaùáŒÃéµñh¯H¬]°mêG OŸc”B±_…LCÒ¿>"°3Ès²RVň +•*«¶D^Lí ò.x~íщ¤¦èì®ýÚeÊ)jEqb%Yk_evmí°–kÇ»~³©ŒJ+VÊ*\*•‚líWBñ(’ƒd2bmVÙÁd±žÌÁh³ž¶$kg¾ôƒn£‰ö<ÊÁÉœ†…ŠÖV^½õqí5äÜŒ¼º¡7¯Z¦“}V #Aê颂Â÷­5eD¢ µ'{3º«TB6y¸£uU7ž&ºn–­Ú FÆžZÚúýñóÐmÑ73î‘®èO%e®ùUoJm Æ ;ï„, ˜¹Ö³'ò-ÕÃ>Ös¶ÍÖ$þEHªù³VW=¼š—û¬tãX<†^µg»l ‰ŠnSžÁpö¾Ý@{·‡¾úè3G52­47¿ OÎlIX˜º‰üÂO´±P±æ<®W¹~„~Vpœø÷>/\S, Õw㩱ï¿MÊn Óè0î™$K.KaW +˜ÚˆÆë$›Ue2=˜ ˜«·ÿ^ŸÒ 7ZÅ£¸å\•D¬Õ‡‡Õ»Œší?5¼n[Ó^¶1ñTÖWY÷2ë éGRkw¡"Ÿ…!ÌOD>mù…5$!!'ÑMI dUd;æ¨P¬ ÕßL¸ë¾½û¦mokÄ!6Z«š[¨IPÆÕ&Ô¦7¢FÑíöKO·‡-+5b¿\^éRY®ªcò5´Ñš” yn1ÑœbÙ‘fläñ¨àsL‚ [spGàˆ¸lñmszÈF'Éz­ÌšY°ÒèÞ¼½ÛÎ>n ç(ÞË-qxh®BÅÉQÜc,œ_E‹Ï.[¹,f‘ËÚ­ú‹,\g.`¯og_^ó׎Ï\¹Æôë¼ã¹‘ä–#I`ðú¯]Ìà¡»„~yëö+–[Ž›Ì@)abfŸã+™ƒtUŸã3‹¢fµ€Ö=Ÿyù ÓÔÙUåÄk@6`Z˜b*‡ê˜û0Ë8ê?g÷æŒÌRÄd²ÃvšÓx"щ˜5çÄÐiÖ$2Î/´@oÕá -ÖðÂfrÍê=ÖŒ÷;4)ôºI4ö„nÁms÷Àr’ž†Ù†‚^2=_ks ©—âä(6À|X$9w†ÔÔ q´ø¿Ìçs4OJnFã­°™TÇŽ‚ó´Ø`ÞËÁ¼×û`wg;Îl©ËÝjóŒ×&øp ½Õ[S%”GìÕdl(UËË“yµJ‘)Ò‰¹jHbò’ž¼pá”É~_½`àe×ï꥔2˘ВYØæÛ) ëîn°aúv Éëyž#d‡¿[´¬.š ¤ÿž8s݆çÖ2à ÔÉUYérY2ƒ=…‹Ûn#ÑOß<5Ö½¾ñ‚Hð²æž×ÔY¸¦Ÿùž—h?|2¨kéiX5î >ûÏuôsâ6úh_b­ÉM\¡1ª™Ðô–—Ž xŒ ÀBUœÌ3r,ûÍ0·yë[°"?øBÀ†ÄSsaÔ„»ûÚVµÆ¦¢¶‡ûQ-býõ3ÇNº4‹ÚÊâ»]ý–óü÷gn6$9ylg+.Ç–>‰‚“ôÍ —oÙ¹oÝ6&ñ®ƒ›Q8Ú*úcëHÙÝ3Lé\_ˆgt¡îõ ZܶàüçQ.0ðÍ[°ƒ!ãÞàÁ>«v-XÇ‚ß_$?>š9Ús–×ÙOß¾ýúI‰¹‘õÏõVÕ Þj ƒÚiqn`mp#úÒz‹®ãûf´|K¯0Äa+n¸ ŽaøJ.NðÌBèÝÌñµž7;h·æ&‘KH‘\ž·÷Û†ßCîÈíÄè;Øæ`„ZªF¢JµZ[ZX\PÆ.»˜·èWôkãÏ_Á â²¢bT*Òæ¨³ãZ±†`²V¶4';”ä²_a»£î|#Ã5f´”Ÿ‹ Pž([£Õ(•×2÷Àêè~D?íú—?Øì=#UKon’Bôˆ _æÎ5K<ž ‹é:S_'…Á‹ñ\a_«æâ¾­ªäëÐAÆ¿  ºIà’ÂüÂ|”ëœ^™US©,­×2ð>Ô zvâ È.}P0¼oæ™À=!:÷¹V./‰WKësRkJš Üé3ã¨FZk9JÞJßoà=ɶ˜Øˆí1Ÿ°ú̱#gÎD‰à…å-`G®ø5Ñ~8‰…*L§k ¾¨V®Í@ΙH!/LMÃûœ> «É¨²HKÊ9~\Þ3îm>k'ƒíÓÍ©ÇS:Ç dÂ,M޶R©®-bj Qð#f-$+i‰sUêZ–lîvyÓ»ÀN¯ö­;ÞLܾßJJTÅ%¨L¤‘ª¤9òü ƒÿúï¥yòÂ\”ç,Õäh4ee•JÞðéÍV 0–\ü,)ó€ÂÖØz¦ðƤç1Ö`ý(˜Ìào_KЭº;GÏj:QׂZPSʨÆ:´D4Yˆ–$ûÇlLŒŠJ 'ŒÜYŸÐ´û\ÊtËÔ ³‚$­Àqœ–—+GRr©ªDU‚Jpj*1kñ—ˆN‹f cÒž-?ÛsúÈfGq8ê%%Â뾸ÑýH¼™HæÀú£Ÿ{1íé˜À•±¡‘LrBf J娥m¹²ª”©þüVë$zðå2¿°äØñXœÁBq07â!­3õ:’qÙ¾ÑǪ%KPÔÞ¸]ùÉò\”!"„¬¾Òzî&>BtúPéâêbµU¢ªœòô¢í¥ 蔈|wsÓ¹ôYV†2O—ÏÏ;¸E‰0yg‹6éDü΄ì}h o®¤—† =¯8·$9+"/_QïT_H˜‰äe U.pxæðzY^n.’9£ü¢übòÏ©X¡Ì/GÄÓE¥%"£7¡YoÏy铽 O$EE%EE¤þþæóÃgÚ®ûnÞ—îkÖ¥Åle’Ò2RTDx¬U•”T¨™Æ¦‹ºkHôèfè’ͱÛÂSØ}Ò´ÂõH”šÝ[ñÝ×ÏÏž7oñ„U+ëOm`åey% $’æH¥™M©l[Ü…Ì‹¤0þðôÇ;Úç喙†êá[> á‰`Žh‘ý.6ÿe= ½hN®ÖsÌt'œ„pH/Áé4Q‰_‡hÓ]Ÿ6ÜýˆÞ­ögqŸK@A×›VKbñ)R%f +++)¯`Zö·j›‘èùÀÉ£ùz.9ÔÊ*”¹ÅäÙ99Ò̪ôãûØK;/¥]&—°ýæo`6ÞOÞß)Û·ÖØ¢ÿm˜qC?—¼¶ÈÃFü;F˜ŸÇõ²MoõOøÔ\!WR +¬Bt»¶ýHëÅÏ=B:¤+PçªrK ËH«Tku;j6Äo\Ãz/Ý1 a‘φAÿ7¿êݼç„lõÚÊ”†HZµuÕ5ÇO5ï'ÁòÞõų§­Xäã¿öü³86OŠk6&€¼í·p-<Õ )Ì+T |g£¼—•U—1ðW+¸mÍ&‘‡8õÌIhöÜ™îË ÏŒ1&`~‹&Äì{~ÓsåzRÌ ôÑ|QC˜ú‚³…‰øQç鯖öa/f>;væ¼ÉAÇÿŠdÄ/|£¶­Yëøªýꟶ¿xvk…·Ï¢ Y^KÚtݺþÔ(ÒUÆÐPõ§¡j'ÑŸ~-páŒ5 IQJq’Á ŒÝê=;tJF)¥Ò2YY^Q.jYfÊ©´Ooßn8w™íúâä·Þï „û™û¦Þ4ýeçåæ'zFqU’°);%=m×ö­û"‘h኎¯¿½~»ëNÛ¦é‡ÙÒTX¾×ØË´»dÅqÞ’Û¬ûÍÆh*p‚ÙàôçñG —ÉøîÈÈÆÝ¦ñ£§OG5Å¥3ü‹Y$f.ü}ÌT—Ö«M1ÓÿéÛ'›yçIPÓÁÃÇKu%ÊRT%ªÈQ¥¯ ßÌðBŸ°=/­P&CY(C¥¨)8UÐ"y nÙxµšÏTäeÉÅÌ®’½£&^ô>{ÿÎC'´õHÏ_¸Ê$Ü4œQVPVP‚œË‘²´¸¼¸Ä‰”%DµÊóÊ¥eøWXç䇦¬´ ©œQIaIùï”_./% Ey¹ù¢ÞèžeŠî¥½Ñ]¥)íã0Uÿ®ÅŠÓÁ5‚UUŸð‚IÂ¥ÈÈPÉt,ŒG[à#ØœªJªç¦Õ“Z¶¾žþ´¿ÞöÓôRÔÿZ?C endstream endobj 200 0 obj 6605 endobj 201 0 obj <>stream xœUV PSç¾!ÞË¥¥XÉÜW#šäU^§*âBEÁŠQƒJ”E‚"‚²Š‚"än€¬DÂ.h‹ìî+P7P@Á¥ ÖR»ùúþ›ùqæÝ`;νsçŸÿœ9ßwÎw.˜dBðxéÄôF-Ñ“)¨b2Jû˜àóxñ9e®‘Ú„êÐhÉç>Þ›fÍ™có÷̼ŋK” Y$nªêÉgÜ V¥‰Ô†«"¢—H\9oF, Ñ$hCwJ‚¶nUm5.“iTawµF­ÕFÆJ>w%q°·ŸgË}֩Õ1;%‚"vJdoUHŒ&h‡dut·Ñ?lAL—­ÞêªU­÷Üâ­Þ³1hm|¸d®ý<‡@‚°%>%܈/ k‹p'ì‰yÄBJ8« b ±€ ‰•„ áK¬#xÄdb aIˆåÄ Hb·x3ÑÍ[«6™f’Î7åoæçLZ8éI’rr7eOí¡ÊL7š‚éz9íJo¤/Ð/ÍV™ù£ ö" LKyhØÎØRØî]8É*¨âaS;Ð!Å k_Êcu(’ÑãHò7ê3C8‰Í©ñùœïÏšŒ"IT9ÈàD m@ÏI 6=z€µàõ «Ïùl;ú•É,äž<ÈB]Â7QzM­²Ðæ“Jîî¿Ny`þEŠÊÐú--ñÝ0 w«Z/UTÕ]¡©ðÅ]vììÖõ×]7^מּ t×ÙPïš‘"æÂˆðѱÈ¥Ô²oäA¼AÄäH­Çõa=Án ÄNøcì€CÄ‚lv{ÙÛs­å߈ªKò *iA|â2rˆÂVÉ{%1ÙÅ9퇛@/ûâ!&8*¥¾"Ù6LøœÜxvÛiusâU@æðûƒDpÌ…¸ËC¤@/ýªã™.3÷P¡¸¤—©xõ {èû-~Ë÷e¤L[N¼óí[5Ægë &avÓ";9ž X õ.­í~CÏà5<Ó÷t"³ýh xÙØ¢èXZit²sº#ëõ¡~à öa³ä®^NófÃU†6kë÷tÀ5xUóÓžkGÛ^¹4&ù·Ã(g˜g˜ò#B·OuA[QKCWgC? ÑÈ.c ¾ü¾@^SOX+æ2Ê!‘œlRËB“·G‡‹·m‰_ËmˆžƒŽ]9ÂÑ%Ð!)«c0\G>¥®¡S¤àb²¸™¤•<›š”µ?s/xÁWQn´@LJ<ŽèEL/;ÇHwݳ@Žï¶ŒÛd—óîº}1EBÁ艨°<µ• 8«òÕ*,;óî/û­ëºþb§(->O÷ñù7«‚õ-â#¦ÇM3’2öÃHºÜóýߣûåçZDíú›z$ø];U0ŠS<˜î&¿å +"âíëºQÜ]uAÜÐs¥±ËˆÖ?È2De0ÎÈäç.4µžhÚüÚªÀœ£Ü7㜱`[Kÿ é@ëñ³}¢ïx*f¼6ÜòPÄD¤*¹úüÄù1šö²öÍÅ^ñÕ jî@—öôWR‰?ôÇüŽÆÜäÐõîå Ç&ŒòQ Wç¬<¥n¢¦rdrïÎKž¯WoN8 ¯Äcœé1kžŸyŽ mns\®ˆõ”‰®¢“¤ª‰îemnó~â”Ê„ƒÙªAÖ¤#¥ÁÖ¤µGJÞ§–ŽS÷eì…}BE}øåÒšìC¢¢>Mê×É ŒÙ“W”y8óˆxå“£”ÚÁÝ/®yp¢ZÀmèHýI?²›P¤knÌ%ÝŒ–£¯ÒÇÐ…1ÿWŸú rö8s¤2+ á\lëö*eƒ¼È¬Á)Âgƒ2Hã›AÖ,¿–ùMüð8º.ÂohâU‘QÊmžñK€Æä¦¦—ù™9Ù'Ä ¨Ô¨ƒ‰Kcó®hÎGª._okèƒ+P‘R¹»<ætD©>^Þ1UÐçR¶³´yœ.¬©Ò7Öu}Ç95í-‹;±+? " %#5=nBzÆØ,[ظ!>«ã€’áNR ÃD´Û—‹¬|B«ÛŽeæd励¦œqáøçÐ% W]P>¼Z~åšH°%Ù›Kt:5Ìc÷¬”bì8ã÷¨]‹It€Â¢wV\‘MÈÎëçõŒ Á|¶ã<“r0 Ò€ŽJÎ)=Ù™Ùântéˆr…wÜ[>ÅâEž˜/zJåT-(((+k*<ôVí¶´ŒÔŒýâô„eüÞ•©B<-9hŸB³i{¸¼è%—ü^œn:šß ªÍ+Ìý¶N5M3UVîhψ#§DY™G¸‡Ærg’<æËçíVÞTw¢®ºD\^X}¬ãèëüc™Í…ïÅÄàÆQ‹²4 ÐÒqs™PXɺ¢wèüDçô7{Äš‘¶É ×2'’†‡~G¥ ÚƒrÉÙ^‚7ÎGɹúçâý¸”|C¡•Èÿö7®1<˜(N& ‡]­ù[jî¡aRp·¿èέÛVu‘ʽIéIbüî½­hðî^¶UEøíHQ‹]ñkÎÂÝ);/»ò„P‘\w2¦\›À)§w”§wâ•­úÕ° âÖ®çúI~±t¸t<¤þ™iÌë·ÔûVõp€÷f pmc‘Á—ù܃7,–Ù)°ð`]‰¼VSÞ‘ÐÏá»3מv¿jGR@3id!íä(Ù…é9ëïd»Û¨–ˆƒú«¸%Sµ?ú±Í>óD|öIû7]ð^ Æ?>›Å`b<‹¡Ðƒ/éEù`"6 [Ç%ɦݾg›xlwÝ÷ÌøGTqÇ厗-hÒÕ©Hx»ç üÍ»›´Õê3~%«Ï…ù.[±3=Bo9^]rª¦¹óÔ5 ïµnY»ÖoýA♛𠻟@¼PȆCÆn<¶[2ãcÔ^_’£,¢O²:£ÈNRƒŒ|8xØÜœ þjñJ endstream endobj 202 0 obj 2578 endobj 203 0 obj <>stream xœ]WXÉÖíº§E@alPЙ1‚‚Y‚ú\‘(Áœ@r$‰d3f‹5¢°¢$Á‹‚DÖôD\® ÏßUÑ]WÄp{^Íî÷ªgÔ}ïÿ>†é©®ª¾uî¹çÜQ:Z”H$à“ž¤ ¶qŠ †Æòf"~¨?Láxå-e:=Œò:Ò£ô´‘žÎñ¡’×F¼!œ)m‘(mWÎìø„ôDEdT’|ôâK­ÆŒû÷ÈyHú×;rçðÕŠÈ8¹¹H ‰Oˆ Kš&ŸMfÇÄ(Bå‘1é Q«åÁaaáa²%Á1á+宊EBB|Š|ôl+ùÄñã'Ø}±!É«å ƒãV˽äBüÿ3BQÔ?¼f¥Ç….óvŠ[î3;!|®sÄ<—ÄÈù®«£¸%)º'/òHY¹85&Ø3-6d´•|¬í¸ñ&Nš†W Ì€†|Wè1XkC†2–ÃÖÆòÛÁ¿g ”¨ŒÈ¿,¹FlIƒ-ƒ÷*ciìÈàœ?ci°aš€ ¯C/`Þ@mÀ7ãø>å@²JÊWrXG5 ÷ç‡ÑØ–Õ5z—x‡xYx˜ïÆÍY{Ó¥#Ä…;rvA§QíþcÏæ).¾ ‰JÉ`!ºBÞúǵ‚An"ih%;ª*ÉÏN±¤ófCmûõSÑžRüŒŒü%†þíXìä›07T*ix$&‘$ -ª¹6Ÿ/¹M0xÒ/Ø ±xŠÍpì…]{FÂd0yÙ\® ›2é.ñ ‹ëtóãŃGÊey§ÊòªÑtã}4¸8ïkVÞkÞVÀ;†¿Å‰ê Ö pü-Zu ¬•±8EŒ©:ñ`¾“ÆIÆ’»B*Àæ“uƒ5ÁHl£L$»¡‹yŠ.çÕWÖWž¸®¢»aWïž;Q‹n  ‰Í!g‚*—åÍ '~Ä€56äÀŽ‘ð•q‹—DĹʰƒ uÀ‘|¼Wííí$Ãn ÿ+v§ ¸û ~П㰃aìÚ‹)pû>Á,ðýÏeašë¼à:Üb©Ëw3—Ýéë»xç¡L½Q?›B~ôƒÉF'Én…\®å­ŒAN,Ç9LTËŠw"g…b;§¢Yg–Ê$MK+o+n›µ¡ó%§›Y¬Ëës?Ÿweåçæââ×ñÇ»Æ;wdß2B ±Öæí ˜ƒ5| §3X†`+x@ãi lS‘±b°«y-,h'0žÖ0Vãn­ÚÂàZždÑV@ÏpO#~? Õ“‰Êð)ܶÖõÕ15 ïO¨Á"o„-ñF¼$Ø’pÔô?´l¶`2mç-±%SX·×0 ¦üüè^ªK:!ËK=–Æ`VH^A?Âð«_Qi_¡häî"#§™Š¹ñíˆãßÀì&?Œ¼ “t^Œt¯˜cæ†üâsYÐ×Àâëêêâ×ÞÛÛØA`!Ôÿ #z`Àg_)\VÚ‹ö£–ÍÖWE¾|Mý¸±X{àY¯‡ƒ è¿íøT&ÃR&#(,q E+¤žZ_°íØÎ‹ì÷=\v÷隤LË3 âsS÷¥ìŽ ÕŸƒø½Ÿj˜…¦©23Gv o÷9.æˆ<‹*GÝrmuëŽþ€> î[Û[ÛÎ~B}˜ˆ÷sîxv8–@lÄÁ€G–x"¶r´ÀzXZ/ Êæq/èË4À¯CQ'ÉÚò Nc¨b@Ý)mºvµ½ì4˜?æwø^s-Á:ätä¾µª—#ËÌÄ@_žŽ§àÉ §cZfψ´LêyóAôBàõm¾”³ofNäüPš“·kgŽô£xÕ÷QYiˆµ /›3Óö¡Êä¼û±šn°™Tb< æ! æ PË)ËÁ\LŸ½Ñ`aÿ%äS/Ïé¹rñãʨïfEDMS3öïjý"¬O‡6Þ º ¼>ªŽ®%ß2RˆCH°C@¦ÑAIå7 ¬K:ºkjºܵ#_ #Ä›³¶¡íÈ- Žub%•ŸˆŽ{ñ‘Ð쥚fDa= 1‘tñcÉ.öbÉï7ƒýOx›áAc°!ž†§?Ç:0¤µ*ÿÚ%™“xŽo€Ë’¨¢šRlÎdçʼn­‹ºžñ„Òîõ=ÈÈ>?‘=ð>Lqw.9YZ-swòðo{ûþÜÏí²ÿ¢GÝz(mú¸¦ëeïÐ[ô9¼Ï³Ã«ÝáÇ‘h$rˆörót ¥áKÕ¨›„/O¢ûÐtùðÕÓì3LJ]4> {¸™ŽNI¢·¬DC°Þß ƒ_,ñ¸¹'ÊÔÆA*[ï}Þ$Þ»Þ»dì/k¶FWЂ eÔH€™fØP@¸—ÿž×âv3©›" ›êе#Åä§ÈòÓŽd^Ê€iKKzNoÌÙ˜l–:'2nNºtí¡­‡¶ž$Ó™=X÷ÿ¼Áž„%¯º¼ºüØÔŒîD58b—‹ƒÃr6@Eì©ò‚&©¤· ý¸:Ë—ýfwD#Em$š%„™mÄ’I¤‚ÛÝh¨o=ulcrž4'}Ï:Åâ'jÏCmée1¥‡+Z6…ÈA!Á`1 bïc‰EY™HxÈ€Z¾cnXå³mÚµ^¶+ ¥£ÈmOr~ SŽ3I¹™‡QùvX¹ý muwyÍ5ô+ºšÚu&ºÂ?ϱ*3 µ'êý=Äuèèö£kYÉÇ£k³¦(jS|ÆÚ5ëR·¡/!/„,Ì*dT Ö&’4¥?”à oGf{ȦÄäU¬¤!0rѪùfXò'ð„Î7 ¹T¿6îGYaú¡ŒÜp†3éÄÀ Þ`g’”<éxŒÂS`ùÛÛ§»/Ë$iGÇÐ/Rà¼{è‰Æ´J¹`N\ÆÜÕ¶ÿ‹™Úüª¬d÷é[ñº›wmBlDæ¡Fœ'”‘,l/¸I˜Ó&¬U‹±PmŒ2V‡lBB|a\èYÕ5ÀHh‘Úü~‡ÚÖÖGVû¶8ž$‚†g[8à˜¾²FÇËÞ§¾K«ŒGó†ø,Œo¾ îÑ6)±ˆl<þóTð!å4´ŸåÏ¿ŽÍJe˜)’Æ¢æ!?5î|Ó:k¯T“ÒV‚â>wÕ r»žrø©Ú¿ÆàR\¥B Gî>«·ÿá8j{Q7Pi|•1_IÎñR,ið v›[~K ïÈõĸÿm7ßk:~½Z*Iók8ù˜`„‡q|/ R>]†Uª^¡„À7mêLñ¥…ÔÔ–Iôƒ_IãÑ ©d¸/®a¾ª’þ: fˆ® pûÁŽIÜO ½‹øò?Ù4Ø3x9ü ^DãI[Ä÷¨õ·Lˆ,‹ESžzðVú„O佬ŸÚë!îpM({×±­¬¤çüúS!KM½|<"«îm—â¡LÑ3Hc~ÿ¤O"[¦•Izo×o¸e º“àaŽnñKC¥ñQëý»ŒÅYU»ó÷Í-+ϯAìårÅ<çH{Ù*ÂP`yâòðOÍKi)7‹wÇä…ZåJc]¨ÄH’&Üãh`9ìÍ€7\þ’C8Ú‰ “&‘¬]§\Ω0^* FÌáÓåùuˆý¹nÙûÀe.>в+;„~þ{<ðµá$Q‹HÇd>á6Yµ!&DV £i8÷œ\Ðê*¬%$))ÄÀb/|·å\fqziÄÅ9§þ&¡ÙaAÎÁ®Óæ68<óÌ‚ûaOÉæÃû¶0Ôî56˜–á(Ûý¡}§roVW]F èXúÑÖ /änÕ.›1Ó‘·g`óƒGõ-·dr\NU"h;q—OLÿTÌÅ{ûŽJ´MÃcóÏ…¤{DP¦C û%àuoÀ‡d³ˆ$“%µ úä›$µ·á¹c¹‡ö:ÈJ~?yüHÝ9SÐÓ…Mðà1ŽXÇíDÐM…TÒç¨XlgŠMûlÁ F÷õ€éSE‹C½›B2'ùýF]ø¢Åa>Þgj©«j•íÄœ¤¯£Î–[`€›[PûõçÚ„¬&á ò wOÀˆ"MA8p»±èÕ40@/QGUíÝÊÖ¢Ïä½õet¯lxèR=•ÔÓks<š(€%©8˜W$ËÍ>]\Êb³p÷龊ãµ;¤/øù\{íûK==C/vuW_º!3H*æ+ïô+f@W†÷Ýl==~POŸ¢þ/‹÷ endstream endobj 204 0 obj 4360 endobj 205 0 obj <>stream xœ]Wy\×öŸ3Œ¨(ÄA@ÍDQÄ Äº¡RË**Ê¢€l H$„°)j­ÂWjA°¢ ‹¸DDpE­â¾âÓgí³¶}¥?µgÒ‹í»Côõõ÷Gò™›;÷Üs¾ç{Î÷DBP‰ÄÂK¡LS¨ãVFNrU)£}¢”qkRâÎDa˜Dn Œ0ÌǪßgè2é”wÙ¿¢†h€QÕðÁ9ægšA°u0e(‘dì¬pS%e&ÇÅĪåvþAã&L˜ø×/ŽNNNò¨Ì;rwEJ\L¢Ü–<¤)”ª¤E¢z¶Ü¼­TÆ­”Ç(3“bSä‘ÑÑŠhñX`¤R/÷ŒSÆ%%©ÒävnãäS&OvœD¾¦,ŽKˆJM‘/‰LL‘{ËÅ0äóÕ‘ÄÊß6(вwÉL\¹ÈUí–¤ðq÷õHŽñLQÇ-Ÿ Œ Lˆ’Oœdï0ÙqÊ'S§MŸ1Ó‰¢FQ!”åNÙS6”/åA¦ü(Oj25†r¤l©%”5–ZJP ¨iÔx*ˆò¦¦S‹(Wjµ˜r£$Ô@Ê”D ¦Ì(sJJ ¡8Ê‚JYRV”5å@ð§Œ(gjÕ%™+Yk 1ˆ0h7œh˜gÄ¥iéat ½É,e~0žniœg¼ßø‚ñì"vU?Û~óû1‘™Œ4ñ4 7I6)0ÑõÙãËË\è>0ÓTjZ‡Mu«P%˜ƒ™0ŸD¨…±\9KÃ$f¼.Æ.Lï„÷ âò _‹ÁŒÃ~ ØC#m*h± u,9¶ShâpPïˆaí™û½MY´ÿŠÏœ/Pa®L%¥[÷äW"«“èèÎCÅ'ʪª¯Áï:sKSP¥°…ØHñ†à)2îm¢AÎ\ƒóeÝڳבÕéI¡ëó²·¤ò^ø[qëü±½¢°•[=öûfŠK@ÒÂYy·è’Œ  %ÀÆPx ßsÛª ŠÐt|m‹ò°¢9¼Ì MB.ñAÞ¾‘¸šˆ‚ËCêb£:Òï"àÐÏ÷ƒ5‹­¥ûê¤Äú$éÒ–^¬>ͼØt Õ£šœÒ´ÝY…9Ѭ©î.ªÔ¹ß7[@•ð'¶ìý“†Q 8ˆÐ©¼«÷|ºpžÆ vQ$[–Ÿ»È["ê`~äãfæÂ5° ã†J_u[ÀæÔU}®•ö´®»yÑu¤7/ka¥¯îŸ9vµËÍ:ºæëØÆà2Äâ‡Ì |‚k'f2_gD$ç&­ÍàS“S?"Ûkñu®ühðd¤=µ©aÉUÙi¼:N•ãX‚þGÀºÁ*DÐ…SÜhä¸ÂwN¨·r,šÆbÓgÎ` Ô??¹,k½R÷ zÎÂà÷ñY*6â. wÉL‰ä•K•äNŸhÍ-Þû’×›ŸR)L%—LÝgþl†J3„q$…zäÌR\¢n¬™‡¬f!§ð ÏBÜ‘+Õ`óNo®kk´§d«s®ýÈ lî‚©éqšÛ0ð<å Œj"ì€!’ß`œ0Æê iܶƒŨ˜x9êvÀƒä¶³dÀÃæX<õÍ8˜ Àºæž”a&ÑÁ=hbÇ·¿í>ðBs“o½ÛÙðiѱŒÊ¸Òôí(Z„*…Dñ+%î36Š‘4Á5‘®rO{Ÿ/Òò4TúWÝDV0 æá¼´ ¤^Gz-Ið —¡šÄ±ÜKIÓc57Àü.<ç?wÄ+0!©¸JR±YLÅMHãJ4;£ftjm«ª1FãW튜ÑleXÈB¯pÂß±,ü|aÑÀž«`Ø!Ã2&]±"9±þЦŽ"´{ÛA6¾âpST·»¼´¬º®¥¢5 Úµ¥ª]Y…¹(žÐ¸â#ˆïˆs`œ¡Ð Ëäð`dêdçì†Í6f±b xÂ5¿>—=ü^¦LXð’¿Ã ex÷Ë¥ùØ`NÒ'>|°‡w질µõ¾’‡_œ¼ÌŸ¹ñmã}¹>ê“r7“èŒÉ], ™1èGïôÜúù ê³éæ|Ï—áf¦ª·‡ëRhÆüK³bòœ5£ã|ùˆÏBTSES¤wm"᫈5á5±G ”æºP­=ß~¾áz€¾Q·FŽ9UìE*âò‡bÏt w T4Ø3íña‰¹ÑYi|Jlܺ@½é¿ Tmpƒ_™`Fã_±ÜSæk²ê­ÑÛŽë—{¬YíE{òË·‚Q–› ¾@[ÐB´02Ìuÿ;±âÄ—oû RdÙ°µyö@ØG eΤ¥ßu&Eò6}¦œïç¾  ñ,6¸;»§³¥ââUÙ:×ì¾Ë]‘•ôAȪªZ¾ „Æ£™¼œü\´ )(Ž¥7fžÜÔ‰`Éí¯aô:,¥ßáC£¹s¡.Øt®Í” ø]0èÐÝ|_w†SA8)‚p»/N|§øCuÑAý«›ƒÆÁ0°“ÃÒÙ„'‚·½œtãböåÜl>Ç+M!‹P¨üQ0Zr,úEâ£dKiÏ•ôÒjëøõâXåî/3dÙ%›‹ó÷³x³5+3Úˆ–׆L>ªnÝЉXø¨nr)úèÌ~ÂÁe{³÷ }Vu •Z™ôUG}züæü-yySx.x½’S„œ3sƈG‰ÇОLº=ÝÅxàg[³ó²Ñz«¥-Ê;õÕÛv”íû9;oÎJµgïŽÂ’íe|'´ÒS8Ü—_p&)¾÷¡|ȲØÕÄu6—zu{,’³Xèá VÖý~2ƒ¹Þž>ÐD“}Jb$žÿ¿h~àÒÿY@»ÐDc;/ëå^|¼KnýøJŸ;¿é¯×—3Íàq£óÞ$jæàŸQ4þ„Á)xý¡yöcýa¢¡n,9;ªwc†cðÊÉÈÊyyvoá—Û¾âß iâ’moâç«·¬F©V˜ýÑ&¼è>pú²ì¼¦î\ùSòŠåc€?þz0?CzÛ%Ê@u—U¶¹íE; vî¾ZËíå…{Q ªÍlPWÄ)ñ@ØÍòWa †2e‡÷]EìÙªõ iÙ©éøô\„Ü6øm´ Ý´*[™¨X½ù E¹¢6…HZÍ‘ '†]@mû/µoÍB_ ÏYiÚP¸yG.K<8©Æ)34|Þ°Ðä²úÖ¦úkÅü™Ý_mÛS¸«`{Uqá¶‚’]ú.Ó—œÑ‚­¨Z\Û{j¹pJ|üDiúû@}štÛÁÎÜþ§î»àÁÞ›šUs:6'C^.ï‚ÿýqë¶æØdÕT™““—µ5›wï> 8;k ¿$Σ VÇyÉþKeÙgBë¡(,ÙwAÙËÿ/®ÂfV ¬&èK3>‚_ÒAK5U'’¯#S› ¸?˜yvœ˜°œï€ºkDQr%µ/Š«(Jf÷úD‰ ‡p8}Ÿä5ØrJlK?gÖÏ‹‰^”›³Ä ¾ºt¾ jaIüsX 䟀1¼æp “ˆ‡ÑP£ç´òÈaÂñ¼žèÙ•:7B’^Ýr¿¯¤¯3ð^§ÔÏn¦Â0’ÏÀ‚Ì6†:#Aù)B;Ø×ž0-Ÿ0r,pjöFØ”Å&çÂÀç[nie§.|‚n±o¼¯Œ ËTËÊ: w•Ö¼Ùtè<:ög…³xàÚêc—Ùû|êº,¶é›—WÜåõTº$N3³Åøòô½ýƒƒ°ï8l?Á’Á_ K 3ÞeùÁåu¯Á‡¤ýØ„‘)Ïé`A2/äÜ‘}¥_•±Ò×ÍMUm§­¿Ÿu÷Ãcð@lQžÐ# N^“¹&$xÕŠTߥÖXòÔ¬žÞ¯Õ¶Ë´gëï¡è’ºqqkO†Hé뎦Ĉ…þË<–ÅÒ\Ѷ]ç—àNú¶£!~Ù´%‹æ¬<ÒúËß>ä?xÆKÒgh~dÓˆÛê>¿nƒ·u]þçhëpÇû70ø®ýÍ%Ùåw'ÀÁ@2m;?!5(ý ÏÄk¦®y½9×°÷ô9>74wuŽ*%0"!—Ãâ«ê¶¡í;xX ,!£GŒÿø…γyéO!Ê#Úž ®êýB“(j¡û M`L ¿0€¢þ»þË endstream endobj 206 0 obj 3663 endobj 207 0 obj <>stream xœuW XÇÖíqèET& :3‚QTÙTTPd ‹‚ à‚ÈŽÈ0n1‘Ò˜E”#ˆŠK¢ŠÅ%nQAEÀƒÈ@2*‰&˜w{Rã{¯zÈKþ¼÷ýß,]]ÝUuëÜsϽ%¡ŒQ‰ÄØ/>5'>;)6F¼³¬%¨AÂhé:œþ»›.ŸMîûÁ™H‘‰QÅ( saÕp¸>ò†QR‰$oÇÞyk2ò3“³•vá¡‹'LšdÿW“»»»rUþž(½ã³’Ò•ãH#'>uMFZ|zöLå<òvjjR¬2!5?#1K'‹ˆIOQÎOJMÊÈX“£´›7Aé*Ä{íê>™ çg%†úf'…ù­K^䟓›ñN^ÚªÅv”ö“m§89æéâê¶kê´éîÑ3f΢¨É” E…PÞ”;å@ÙRK©”5ƒr¤ÆR ©ùÔLj õ6JùRNÔ8*Œò£œ©ñÔ"ÊŸr¡ì¨p*€r¥&PÔ;”5‘ZLRS©IÔ*ˆò¢¦QöT$LÍ£¦Sƒ)cJB™Rf””J £†S攌J¢Ô[OYP¹”%5‚IYQs(kj5š &ΤŒ(9Yì ªC2^#Ù=hü ÀAŸ z%UH¤UFCŒ’v=§íè÷èoè2ã˜FÇ®b·²¿pN\ùà· N¬3¶5^a¼Ç¸Öø;ãŸ‡Ø Y5¤lÈý!Z“&‡LT¦CM“M/™þnæd–h–b¶Óì€Ù³³ š;´}˜b˜ç°‹Ãú‡/1gÌÍ«eJ™³,X¶A¶Ž› MH­cË$Z)ÌÕ¥ñoÒ„H5Ö3f:GTQ*aJ™DH‡5ü#¼†îgð.]M\ú&þ™iƒ54|¦âq!&ÐE› âtµî-2f„PÎã·õöØJ°§ðÑW¤L_¿:¯(|íÈßÉÍa÷mÚ÷ÁT‰Žî:ðÅáý{|Q E:³¢YåÂÂÖÄ2ó{mžOà£NKÙ™{dBÙ×úò'PËÊZÿqåÞÓ;µ±rü¯NAÉv\ç™æµ\.;£a‰Ùm‚}›äx·THWü6|{4¡!ð¨âðGl„½°G¯l`xû#x«D1)š¶2ÆqŽ‹Á0°¼¤úáÞ¹U^% :o²#kÁ˜‡bK¼×ਡ1ÍÀV x·Þ˜Ò^Wa)¾JK |80ò‘FøT#- FZ]æYœøf(NÕ ¥1ÿ&M#±z ÅåÔ°S …jóv-´kƒµ–2¡ÝÔL º°¿¦–“½>U]vñªÒÄ69œâdÂã'/=´BM¹uñ'âOF~áC¶ØÀhñ] Á½ˆŽn9”w8÷ó4‹âŠR׿fæ¥oYL^òÃOyØ ¹ìytlÃ,2óì=) V(ñ½”uÙë²Ò6¬@Áµ f­!„8@oíç|GoBÏ<[±Š…ïSâ÷¦ÀOuCS· §x<ÞÙ{co L€ñ½¯aø»öãIŠížü³+žØ]èáèÚ20¿ªÒ*ÄIT‹Ê…@x–™Äí…ÂfBËéø “ØvDÜÚ(g,Á3²ìÓ%0æAcåíórYáü‡,.bøÞ«3ñpñ±é™.ŽÁÁ Ìn<þAþ§#¡N+,¡†‡J!ÄàÌ8âÀdѱ,'ô¤¯FËšéV¼þ½Fk¤:+b†‹^Ëè‡ Zzœè1 #˜ê5ôkâèÕÙj¡O-©×Juħƒ«£ »¢ hVjxˆÿ¼[„6©Ûâ}e჌ç棗/@87‰Ù¹1©0=-$(ɃìîmGà B4À˜k—ó×STfîMÝÎà&ªHP\ƒâB·¥ìëK8‡™sá Y͈ë§¼Ý`Vx‡¦Å‘˜h÷de·¡ ÿñú < ž1Åea …¡ßvh È·_3¸¶IÎ<• %°žGï×Ö$=yq1kÜd2sðœÆÀx0él¦œ„Löüe ~(­8¸æ\î±-ÇŠ/r;šùOû®Ýz‚8õ-_·mh[ñ62= µpS-TDýwÁÑèoÒîÂEµ>€ü„ë„øû@¬Ö {‰ƒFêòym°ÎÃy@®°ZÓ^ÙxMÑ~·hC8x+`2N–»ó/ØborqÀ޶DŽüÀ¯Ÿ\¢+ ë÷h…QZiœ` hC.ä`#ã >ÁhßXóÂM˜ÃÂØï'â`8{«ø?¸ÿÁÉ6a3]v^W0ׇ•µ]Ž[pl¾5–;aš¨ŠçS·6¾Z£ÀÇY£†]jÉZèé“’ÍÜä!Ÿ¹ŠÎï«9yîôþ:t—ƒÑ3ÚðX9¾ô&MËÖF° "Øž¦¥3gF,uV èÌGj(RKz‰ø¿]¼0F­Eµ~Éej½cO‰™^²ûÂ:KYU‹¨ÃGE>Ä¢GûNåd-‡÷úäÛí\[´cËö (­JYêÁɪž-nÂé÷`¸(d’£Ýp†ÌòX˜B¦™ÍÊúnÆ,9êgG:a{ºUøœTÔD_Y{ÝAß©»Ãe°È{ÓÊœôu©+ó— x”T’ýeÞÞÍŸo=ÎMevÙµ-€¡H…î:Q{º~ïC92y™çñ½M³±%>ÛeÊ"ƒÎ\Qõ(þ‹iºµÅüä¶–ïOŒ‰ØÖfâ.­.Í7ë‡@3)ŸA—»Ù­‘â¾ÌowÂN"}yBŽ…P>7²²óÊ0Wÿ˜Ó·ä;]oǺÞû¥­¡²¥A.ËóÁéu£ ˆÜ#T‹‡>vqAÁ’Ú^ ÷fK¶}\\Џg Õ÷:SbŒ¦Ût2† Ñ1j |NBæ:k6«õ-î°åµ–1¨¦˜•_>“B¡ïdb©Dô%PmË…rz2ƒíôX)x&êËéÿ¼"šó-qбª¹¿’yÜ>¸Šþ•!5À ’{ýi%ƒ3q&mÈë:y™¤Kk@©AÌ^»Ø Þ!xpxté¡Dy‘‚*t™v9~l÷›#Ì…Ù0茓 » e¨JgS&Ù÷Ö’Í<Ç—°háûÉ™ùk’ V#Î'®®KQgÿ‹Us`8ÈÚÛÁ\.œˆ6qO p0ìÉVo?–Óo3ãõöJÒpdÀžlíóJpüMïHH(‚ÓüD öd±ioÒõˆïù´ SÚÌk»¡ÔàįHúÚ¶é´Õ:ýݽ‡pƒÕú6b~vຸDyVÆÆ´m‹¹'̧ßUU!îQíšHÅ:%æúoÆÆ…ù¤¬ÎL]†|9Yžýßî4U\º&ÿ$âpÖ%´•î¨ü”T àË£5› 2³“RW½…¸€øãM—«Žô”*4{¾Üy¤”û«²eŸs…ƒWèßàdá =ž½ä„ñç+í¾9ðCȧD¬µ1¸Ið†Fh4œGþ¥§i&¼Õ²±á<äÁ>ù`ŽC±9„Òö ÌÃ%8—ÑÏ ËÁ/§ûªõ?ÎO CüZÊ ´%©NÍÊùFóÍ“ñ~rüFìooœŠóo…Ñì³ð‹|brDÉS¯ÄôE>hùÚœ¬á!û÷ÂöR'\$¥[á%ƒø>ºËÊ‚O7¥·Zƒ¼—Ô$^àéöËç-ÉX§€£,ŒÃå¼v j ú{Õö»÷8ºB "øÙ2ØJ߃g=?<•4G“¦-!Ê_˜&¼€Yú„'Ou‘e’ÖÇRØ@Œ-qÂN;ßÂ/‰8^f\À6 f!pEàÒ°áþóKµ8‘ –· ç@–Ü"Fƒmˆ˜‘Ú»Da,;N¾x|uO™¤ûµ´»Ÿ·½žÏ¢ùës—mãâÙ{'Ï?U€ Œ§°åªà67̓M$ÖAçS&¹¨½«•^ÔEòXÊVb+º†©+¤ì]‹ÿîÕ¡_ÒÝ/…0²Šm¿(¹åº%Äè|²Q¼ñÍFú2u '–VA¥’œí…ª^)! ø£ôÕQ˃²í bs–=LT“¡äë~\ö|uã‚#~ˆó0z^7‰jùòÉ–ýQ÷ü¹ÂauäÄ {4RØ#ìàõ;4ºÈ…l*–äâQ\ HÔ-Ô¶I÷^©ðQ7¯Ììúö«ªî //øùÒ…V"äײ.ÇTÇT/þÒ9!¿”¸Ìø ÑÛæqæ£úOŽî©¨8ûÍá&Äu\–¼40Aá¸ÛM[é·O)¤Š5[ýùÚÛäg)k‚1ÂZ¾Ÿ•5-eНî=râc®š Ùš¼ ÍâÆ±²)°Ó¢)û¿OÌ„ÇÂ)(ãõEKÏb9ž®—c7±)ö8  °Í¼N¥½ó{É9Ö‚ŠßÍTm§e¯û£§:/Špœ´¬þ×÷¤J -Y½/å”ï½Ä>R©Œ{Ö V½‰S’¢áÎá¯ï[ÙŒVlmg{àIÊ5Ìö³;î>PVsîàeÄ©ê–ωÌO‹{W‘µ1õƒ b΀5Lh Üîðä±§<_·Û+XüHJ;°p±Ç™ d­?ŒîVc·D˜IàÏ ^×b"ôôcæLÉ}d¥ç×lÞ^4ÉsYMG±{ÏÂ’5åkO‡?Hí v۾쇉 txGEÆ®O[¥8Q4Ô‹˜²š7hWœk&?KYW™Û–•uÕ3Ûe$n%\¿þñ×ß î++½ka[Ïþïƒ o¨:ËŸª$çzà,1r+Ñ àf·` y Ÿø˜Àèù¹$Àx„‡|9»6°:ôfªJ<ûõ½ŒpÒb™Ï¢ŒÅñŠí0÷Ê‹Wè&:÷¹/GÄïjZ`ï0Ã=¨ùÍõÝ”~"xì±Hwd öÀïÏÇv¸ˆ{ÆÔ@n,WH8ÂW!Ke^ÕÐ ÅäÏRö«`- æe}­çžºkRWÛá.Xê0áÁ ¹ì×q± <¬°ÅON åO½À·Ç^s¯‘cv“ê£çûGEÏuînsù‡ Üh$ûUs=lú´0'·«×¯ œRUª‚›m’ê^š&˜ÊïÃÜ÷ó"g€ûÕgno)ëFÀ“Óaþ÷1·â®ÕÖÇcÑëoÝm8xùE1ž6i B+kŠš9xKHæÕ7}]ƒƒÜ¦F\{Þ{ýöcq9ìÝ(asR¹D }†â£Zú%0ˆHLò€ÄåF(Aâ„éâã×RH!^Øà–ÿGð„Çç!ŠŠp,ÝÎêå0]ƒ›Ø{̲+„r±Z ¬`TÆCT›˜tí21¥¨bÜÅ endstream endobj 208 0 obj 4877 endobj 18 0 obj <> endobj 30 0 obj <> endobj 32 0 obj <> endobj 9 0 obj <> endobj 39 0 obj <> endobj 11 0 obj <> endobj 17 0 obj <> endobj 29 0 obj <> endobj 31 0 obj <> endobj 8 0 obj <> endobj 38 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 209 0000000000 65535 f 0000133703 00000 n 0000171490 00000 n 0000133370 00000 n 0000127462 00000 n 0000000015 00000 n 0000003159 00000 n 0000133751 00000 n 0000170209 00000 n 0000167466 00000 n 0000170991 00000 n 0000168367 00000 n 0000133792 00000 n 0000133822 00000 n 0000127622 00000 n 0000003179 00000 n 0000006694 00000 n 0000168910 00000 n 0000166049 00000 n 0000133863 00000 n 0000133893 00000 n 0000127784 00000 n 0000006715 00000 n 0000007820 00000 n 0000133936 00000 n 0000133966 00000 n 0000127946 00000 n 0000007841 00000 n 0000011406 00000 n 0000169320 00000 n 0000166510 00000 n 0000169904 00000 n 0000167069 00000 n 0000134007 00000 n 0000134037 00000 n 0000128108 00000 n 0000011427 00000 n 0000014892 00000 n 0000170620 00000 n 0000167928 00000 n 0000134100 00000 n 0000134130 00000 n 0000128270 00000 n 0000014913 00000 n 0000017635 00000 n 0000134195 00000 n 0000134225 00000 n 0000128432 00000 n 0000017656 00000 n 0000022883 00000 n 0000134268 00000 n 0000134298 00000 n 0000128594 00000 n 0000022904 00000 n 0000026498 00000 n 0000134341 00000 n 0000134371 00000 n 0000128756 00000 n 0000026519 00000 n 0000030480 00000 n 0000134414 00000 n 0000134444 00000 n 0000128918 00000 n 0000030501 00000 n 0000034783 00000 n 0000134498 00000 n 0000134528 00000 n 0000129080 00000 n 0000034804 00000 n 0000038643 00000 n 0000134591 00000 n 0000134621 00000 n 0000129242 00000 n 0000038664 00000 n 0000040989 00000 n 0000134684 00000 n 0000134714 00000 n 0000129404 00000 n 0000041010 00000 n 0000043827 00000 n 0000134766 00000 n 0000134796 00000 n 0000129566 00000 n 0000043848 00000 n 0000049214 00000 n 0000134848 00000 n 0000134878 00000 n 0000129728 00000 n 0000049235 00000 n 0000051826 00000 n 0000134952 00000 n 0000134982 00000 n 0000129890 00000 n 0000051847 00000 n 0000056051 00000 n 0000135025 00000 n 0000135055 00000 n 0000130052 00000 n 0000056072 00000 n 0000059129 00000 n 0000135118 00000 n 0000135149 00000 n 0000130216 00000 n 0000059150 00000 n 0000063417 00000 n 0000135202 00000 n 0000135233 00000 n 0000130382 00000 n 0000063439 00000 n 0000067789 00000 n 0000135286 00000 n 0000135317 00000 n 0000130548 00000 n 0000067811 00000 n 0000073166 00000 n 0000135370 00000 n 0000135401 00000 n 0000130714 00000 n 0000073188 00000 n 0000077087 00000 n 0000135465 00000 n 0000135496 00000 n 0000130880 00000 n 0000077109 00000 n 0000081897 00000 n 0000135560 00000 n 0000135591 00000 n 0000131046 00000 n 0000081919 00000 n 0000085346 00000 n 0000135635 00000 n 0000135666 00000 n 0000131212 00000 n 0000085368 00000 n 0000090207 00000 n 0000135710 00000 n 0000135741 00000 n 0000131378 00000 n 0000090229 00000 n 0000094715 00000 n 0000135794 00000 n 0000135825 00000 n 0000131544 00000 n 0000094737 00000 n 0000098665 00000 n 0000135878 00000 n 0000135909 00000 n 0000131710 00000 n 0000098687 00000 n 0000101193 00000 n 0000135973 00000 n 0000136004 00000 n 0000131876 00000 n 0000101215 00000 n 0000104327 00000 n 0000136048 00000 n 0000136079 00000 n 0000132042 00000 n 0000104349 00000 n 0000107501 00000 n 0000136132 00000 n 0000136163 00000 n 0000132208 00000 n 0000107523 00000 n 0000111047 00000 n 0000136216 00000 n 0000136247 00000 n 0000132374 00000 n 0000111069 00000 n 0000113717 00000 n 0000136300 00000 n 0000136331 00000 n 0000132540 00000 n 0000113739 00000 n 0000116589 00000 n 0000136375 00000 n 0000136406 00000 n 0000132706 00000 n 0000116611 00000 n 0000120560 00000 n 0000136470 00000 n 0000136501 00000 n 0000132872 00000 n 0000120582 00000 n 0000123039 00000 n 0000136554 00000 n 0000136585 00000 n 0000133038 00000 n 0000123061 00000 n 0000126585 00000 n 0000136629 00000 n 0000136660 00000 n 0000133204 00000 n 0000126607 00000 n 0000127441 00000 n 0000136713 00000 n 0000136744 00000 n 0000136788 00000 n 0000143394 00000 n 0000143416 00000 n 0000150109 00000 n 0000150131 00000 n 0000152797 00000 n 0000152819 00000 n 0000157267 00000 n 0000157289 00000 n 0000161040 00000 n 0000161062 00000 n 0000166027 00000 n trailer << /Size 209 /Root 1 0 R /Info 2 0 R /ID [(Y¨¾> startxref 171693 %%EOF simh-3.8.1/DOCS/h316_doc.pdf0000644000175000017500000020062211143604363013375 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœÅ[[sݶ~ׯ8Ó—’š/ ûæ‹jçRÛ•ä¶§²dËIm˱¥´é¯/Øä‡CÊéLãŒÅûí Øóó®,*µ+í߸xtïDï®>¹îÝÉã©ñéêè磾¨í®ÛïwÎÌÄa§Ê¢lvgoŽÊbúªìÝ÷Êô—E³Óeeþ>{ô2{’ïëbPM×du¾¯Š¡QUŸUyS”­VY'}»\¥ê»ì4¯Š²”Ê~ô]ïm× º.»Í÷fɺê>{ç?Ÿ •›|¯Š¡kÚ&»†¡Ÿ\Û4u- ½y«‡*ûìRW0ÿõÔm&ÿã웣Fu7t»½ªÝ9\𖏽iô>7ßë¦o²G²Ökx1.kØ ßJ¾—ž- Þ‡9#/÷Nªj7˜¹A¥V“8ºÖü{hw]×½ˆ™sö““ ŸQ9Q}g¿üÞþ£Uö_gx™=´,©^ë*{–ï»Â0W¶Ùs»¥nhšìû|?e©J•ÈЯó²¨Û®+ëì±Ìz"ÎÌVºBW=Ì0öé8 oÚ°˜p,Ð}(c'n*vf7]bo@ø­!¬+Õh#ßÆÞ˜‘•én(ÿý‘®ê¡Öµhà’ý+ Ý׺5Þ¤´ºUÙ‡0ùʵjUFë\˜¡F_g¥ùk^é¢2p2(6-#ÞÞPöë¹·Ò¼¶a‘2àšŽ†C¯)¯çÒû›c9 ´>[Ú@ðFš×óë®íEØdÄ]pj1¡Ë!ÂâŸM³ì´î T «Æ2ÚL¨Qª1Æ¡1*ã4Ùrg]ÈÍ­lÏÈ¡5*5,Né+Yö÷JŒ‚³8ø á@AÆ“ÜúDTÃFaöRÈ—Tò²Ó[é}oôËØÝV¸³€BX ze“×H ýã(Yóÿ$½Á~´G—“¥ª;«Þ­ë¶ÚkÅÆ1W»¸ôñ³˜PÄË5¼}DŒ„æ+® ¾å4à­Œyd«à^Ø÷Êõ6]Ÿ òšµkV•r¾ÆCLÉ€›¯>í°îhìÓZÊðÁaT_-¬Ÿ‘6|ŸN7ôFTN…k£ßeíUø=Yàð&gzŠV‚Z0˜;؃ìüŸæ»ñZ >±ˆX+ÞÅP6qES›-ÿš«¡PUݦlà!²ýlo?d3«õC.0®¢ðd¥Iª++‹$Oá‹‘š'“Y"³ ¦sƒ$ƒÈbI†§&f‰…ÈÌSZˆ.’°„„A5SÞ„Öz®óî¯!j‡iÂÌ”†éð…;B`âµìh©O OëŠÎ?§‡Í—.ÄQ‹Ü¸²p(ÌÌõäÂà@Î醮èj_Qº©„{ß1Šh‹jQÚ­@êãš]ⱇגÊ⇠©®j›yŸǃSðòüè•Éz©Aó#X•(õD'áˆL*§û)~ë4°2i£×7Ô!M!{©ÈIÅÁÌpÿ tÅÌB1Pös(øM–Da^!Œ£}›WÊzjÆiÜW©ºdxÇzpV! §-|ç§Ï2‚ Tê³`ýÕàwf­Aé®2ßGª ªÉ!ÁÁ¹å¦œ‡æ²:TnÚ‹tk~?Ý ðCóó¦+£ÕË{³ŸhNÇ1T:•dt·Ì’"#ëTÙ&r:dãw‹þ¸³b¨äå)5»¦ÐM³î°~ûeÛjîi'×À5“¥@‹]Q®¦< žDˆŠ½Ø×™Ù!Û‹o¬ gJâ+éàiH»7J|cHOüý<݆Un)ÁñDí \Ú°MêŒmæü*tøE.×uÀÝ ŒL[hþDBu0 “ãV]ÑÕšhûênáŠKL¹ì/1“[¤àjåvr{²Ë6Wzªœ+é:»ÏRÔn¼¿Û£Õ9&°óKúqçb–˜.óy•p²©—(fN7¼D¥\ébŸVåVóï‘ ÷˜´~«ð 5ЂfâQ„É`Ã錊×µòzwAcOá±ì $ó HæÅȲji†V>–Ÿ¾SB7pç¤kn€ÄŽ}†û¡Ê5bÚ0¢†cPjPR±Vþó—)Ô\àn(5$)ÝÀ;¶cf  ²žHïý0ªÎ€gùþ`nAHÒ0_÷ºó _¦ð ûã2:³ÑÓ©%5Õ…µ@u©´#hˆ¶¬Êr¬Ã³@Sàtœ×à <¨~áÊ›¿ç Ø‹s0ÇŒõûÙ&Ù/´<ópk*ˆòÃROiÑ#ÈciIl/œýÉÚX°:Ç"ÒË¡›#´ °â¡ÃSjµ¹åÚåĦ[WOáNÀ›iÐvPIF´ØûP‚§ý°*pÃöH}E÷u‡GÃ;Õ™ßá}jVÒÄÅC€U^$;>ºìÿi•,{Û¦¥°)%6ôÇI7ïQMË…-0/¾¨kŸ+Ø¡ÿ˜ÔËôÅE@Ë{m‘ÎêX®UõmµÅ®”Üñçi£ÖÖ… E8pt‘C\>t{XS•£´CQ7‹j”ã³£¿˜?ÿÈèªendstream endobj 6 0 obj 3113 endobj 15 0 obj <> stream xœíœ[wã4Çßó)üh?XÕè®Wº@r(%©`ßèÄX<¶nD“vi<&b šˆ Ñd¡!%¯ò—mÕ»X•j³ß“-¬¡é ¢xÇ8Œªè p&ä!¢=°%²Å$ØÒ¬’ç±En-¤Q™dËqŒÅîQî:¡V¸ËIÙÛ…nnýj€Bë9FJI+Ô^ŽÇ`®*|Š+à¿>$¥J’-´/B—Ÿrj‰‚?:\0ª×û¯V¦ —AŽí#g±vž‡µ5äËi9sNÓ?-r.RlÉB÷ímVœº¹ %Bè8F)<÷i󱓔!Æ&¾éJo™ãR¦œ5©÷8»F™a[$„ “Ö6K§9>W*Œôqâ×3H¦²Ò/€›'§ñzDÃ]*:ê„Ta³2ÆLÔ=qŠc]œµ€EUzº_tÄ ³¾‚EK»ãG,†1H9&_!)ýé±bºæ‹ÎïIé àØËZW@Ý€­:•‚¢ŠU‰É@Ò æôÙàCü´v³î$¹wAݬ%;w±×Ç먈FË…nGî,bð$Šª7Ý”²±Ì½ï÷lMGÌ™÷Ø\;¿›ãh plçûÀ“‰ªUÇq ðôàÁpàYi¶*‹¼K‘<ư&À«ü¾ Çˆß I„%d$é6}R!¶Æm¸ r[8<ŸìæÍëd¹eÆøñ—öF ÃØÁË] E€ÜÜ|)=“t-½ Ò‹÷AVwRjf ÈH +2$ l2T*œM‡»1&N68 JÏ37Tkmg$díÃÔ9#;~ë2gDÙ-íøí®È«y†‹ÒëåW ¦3ÆĦ±gò,1èCLkf,eXw§/ñâêbŸ €¦Ð^ɾUZg8nRÍÄÄí?Ò*¹~7üê=çª~èŠ8t³zø³SX/ 0eð›ÙÖ›«üìÉünúnÔ¨Ù:‚ËÞ«OCþ€ÏœŠÿ@àóùì{üýðƒ5endstream endobj 16 0 obj 1442 endobj 20 0 obj <> stream xœÅZÛrÛ6}×Wð­T§¤ˆ+>6Nê4i›:J;O§ã‘äKcKŽm9Éßà»$W¢n±›ÎäÂb—»gÀ’Ÿ¢,e<ÊüŸLn£“<º¸ÃÑÉϸ»|˜TøÿŠŒ'7ÑOc'ÈXdS«£ñù K­5Y^®Ê"­Ü¿­Šr–¥¹›p3ˆ£áø¿c©âÑøí`üýi<&L§9Ó<¾ºyŒË<¾J‡„•ñý0ÉÓ,cZ;YÆSÉ ‹o† —i–‹<žùÁÜ‹a¡»a¢¼8·ñY1(x–Çs€S€K€h­È ¥u&ðÜÀ‰³ÐÉfÈœ°MÖ ¥6ñ,ç /~º„åg#ç*æ×±ñ±Ó’rc¤Â¦Ì‹ BáÄü:dyÊd¦âÏ¥”¶ÿ~í],”u@VnCVƒŒJ5ÀQÇÀܲXõ‰EØ'ÿŒpíd¹v$O§8r®b8Z`7Š1ò&òâCZNsx­£1Õ ²m1YçYÊ=‘OÝ£$,•Jiëã¢x–*fâ÷ÃD¸Q!³:qj¹‹¿vvgif$s à cÖïùbŠ0Ú™ž˜Ô Ï— 2s6.`îwhÎÁ^_{5L\öpƃ>?Šž¡÷µxäþìÕJ¦eïʤÔ¥Ož3GŸúw¦dPjšÚø™¨P(œ£¹3ßà”Ù!Ç„s`ó“­È–\ñŠw^ù(Žr ŒCç¦Òº@ Öo?Ñ…¤4<¯éþúÎ×UY—UT7aAT›Fe1ÍMQU¤Õ޾ !˺[–¶[ж½ÇuÈžFQG µ×$­ù·UN5­ùèPþYg㞦MIÆœ·*úá ¨w’$‹'yôFJ†ÑÇ¡Ì3¥¾¥êvÔ'AéÇ>wã„ØÃT4ÐsK²á)RÙs5ôuØ(Ùü=9áºÏ©tÑGqC%æŠT|Fh#ÉÛÄÛ€Þ<¯ÛÑšì½YÝçÕ+Ò•t0&äº rÂÇu w+ÌÓ¹ºñlפKæ}.AI×·B—IR¾ O§£-<%ƒ Œ@pÂ;'ã iû”ôÉ%9—ÞŠ"r)Fu£C)ï £`xkc2L¦Å]b½[€6<  ×¨í¾Ðn„ê":N!±œLfú±¹/6Øû¨¬Ü®x#æÞ‘s¯IسÓò]±3Rì‚{(˜"Œ»yI^_ÄhOÓ üHêí\;’ZER:¯æ×UÅmCnBq[Ÿ²÷¤‰´ásÒ£t€·áÐ )vA*ž‘НHmûŸËv$ªƒˆèKÒQ¥˜6ÖlÉ'‚›U0óÜRͨDò¢ñåh†îþý_í÷ʨ!ãÈ+”#¾êk-uÛEÄ»’Ãv‹¨~Ô-¥aF•Fµ“Ö|—Vü»”Ê\n=`m³U/¹Î°%U­¶Äꃡ…ÕøÎ‡êA£¾_õµ˜©U5µÊ±ªñaÜíêuM E÷ئ”í÷°~põãÕ=®Òìv‹ ºZõø©?-ÜVúÃÐ|ˆÜÓnÒ¯É"ÜÅ£^Ñ-Á9éG'úËJúýþ<Š ô”4—ü¨ ½(ão²¼AÊeÓAUPoqׇò‡~×ôtVß…wb.^¿ûÏ÷´T®¾ ¿£¯Aþ·ª(ÙXªÈ%RW˜aço Ûmæú AƒÈï]ªVë£mŸ¦ä IIsºdÐ%iu§íKº^"–¬âl˜°$'Ðo½Vp–¢7ÍÙ×ÀÉr—Zi{ú#cÔ¬‡¢ DÑ{&¢;_VœT©Q¢æäŸAèB¿jØž/ǃ?ÜŸÿnµ„Žendstream endobj 21 0 obj 2013 endobj 27 0 obj <> stream xœí\Ys·~ç¯Ø·ì¦´Ãs¹Êº+2e™Z'NɮԊËC¹¤¸¤éטÝÀ|³3³ËËeIzh‚ ïn4ú8 !G¡ù[g;»ûéèxµS öÿ^—Ç;w² 2ŠœžÌô‡BŒò OF³£0Èó,LËUÅH„r”Š0Hõ/ÏvÞŽ§“i„¡ùx4™ ‘« •ã½I„I.ã4Ÿk8‘ Óñ%M>¤Ñ‘£8IÂh|e–Ðã*ŸÐ„9Kþ™û‚Vx5™Ê@f™ŠÇÏÜÓs¥ÞYÊñãIdPSjüëdš›±PrÞO¦©†ÓXò;Ÿ(檅œ½'„­?8¢¹‡]¼Zºûô#gŠFÙÆó2`¤˜Eqf$c'Bð„Åä÷Ù?v¤Ê‚XäZf ­3x³kÚŒáXr[ªšôDÄj| ?ÓòVA˜ÆBc Åfú+6è*¢Ðˆ§2™È'Ö¡Óâ_”TkýK¬ê¼?J⤇±Sø—RsîÈ¿`Åsä ÓP‡¼>wûž¢íHRSJ5Ní cß1QÈV¥©lQ­¦2Òî3©µE%'GçŠ%U¢Ä0:f2Ñ¿™ýwGÿ˜e£Ù;³¿¾Ï4^‰ÑtYñ„V–dš™Õv÷ed«%RQÒÌ&µ?N¦Q§Z~²Ðc =›$aX.Á .†•(pXžÛÍqe¢%–Á<³Ûÿb¡= ½ˆ’l<£B ±¶,z©W[½Â:,¶bÛé D¦*¾Öy‘É©+ BÚJ * bYiàî~Ž„á*•ØrU•Ø’Xÿ¬µ3 µäª2›œLà Iª½C`|Lœkï6}Ö7F iŒ2-.¥OÆ2H2Ãõ׿›<ÊÓX‹º©Ý¿½ÂWogI™Ý­mS)m£:~§©Ð»Ô^랼ⳗ²ƒÈ¹§Þªœ¯H’¦½ŠD¡Šä6X9 W<|->§tÚª˜ÔÀ섳&’B„We ùëk¬Æù¡<þyiº9bátEÁ´ »ŒV$D‘Ðe.¨YV¤²œ«Ž­ ¯GqϨ˜Q–RލõÏ ãÜø‚ Ì¿Á» \’AsqŠÉ_L†µ+ŒÒñ;¨‹•èµ­JÐZ¸£#Lî«w¥mUuDá!Ê–˜RU6¤›ëTJáÇU'‰0°1Ö¸­PÀ´÷xccÐs Í,4²ÐS«š‘Œæý`¡7Ð[Ø ÿ¤Âƒ'åT‘Þª dF®¼ ð‚ ¸ÂœÀKˆÃÄá nqW8€³ KHÐÄ쮋7>_¿E(µ"ɻЖW QëÖ ©´#ËÒ½YBj›¿)ÖÍ)–Q¥³ïL'6¶¢ÜKŦõgÓJÙÊ÷ÖTMY襣LIh‚b›21pÕ%Æp,26zþ¥Kñ˜ ¿Â-ØgßÃQEàK`õêŒ>û& fë%q'þWXH¶…}ò'‰ PÞ¿¡t’ûŽ|ÀÒI€ñÉ´Å–?—ñ0‹YenY*Ñ7—æŒFÊ¥Qt¯ñí0é0°4“¥Øï<íbÝ3÷| ç2I²s@ÿãÖC8cu‹aýk©ezûÇ’Ã}i>ÝI|o¡å1 ñPÌHعŽy¨wp±K¸ãçÑA>†sÙÙ’zçõ%D’éÕ nqQÇÜa£¦Q£†CtÖÒïYÈ3ב®Ž9…vòu/Á¨ÐÏ:k¤’™°uá(­‹ÅQΩ¦ÏªÛÕݪhôT•~v'ðÕÞVâÊ}£ÃÉ∠­¸ï ÷“⦻…_á5×/ÕÝ*oUSV™t Ø÷Þ[÷Àbþ±;è²øqdïYùs²ó’·Ëöè7k»Í±Ý!ˆÍWP~§ñšN'çÊÑÛ¡h‰oaƒ‰Ó‚h`”a.Ì»4lˆÖ81þ ©”\Rtиa/ú"\ó{!*½á½t©Í<À@䌠‹iªƒY€ß0ÖΪїTö é…"ã¾l]×i{ãp¥Ã u8ãœþH{ç¯7”›}"ã)\–½ã%Ñ>¬±÷m9|-<ˆƒÆõí£4ˆE쑉í}¢I}{þNBE‘aï _X_A=?G¾ÿ5ƒ×:Emœø^»ÑËVnT·J°™?{xC–`¯Ø‹jíu„2{å=ÞUP³•s¯ÞÞÄÑÕh:¤¿Çé¹)L„}X¦rZk_\ …R‡m¨Õò¨Ê’ å^É~U*Cщ£Ñ_Ù·Û×'a¸ß–íëÑJÇ>WÖìåôJv­ÿQÖPrÛï*èÕ¤giвÔ8$TÝúu1 ÉÈWüŽ®i¾uW3dݬ*UEº±O ©Ó¼w¿Èt¨³Èk×Cýÿ( ã¤zÿ ¤½ó¢çSÉ<űçªzL‘²Îÿ„̰3ãÅ/.z<äéÈ4z´+û9`él.œ|æbsÏ€ÚÔ¡gø®ivK·kæ¡& *úýëÖªWÖ¬N`UæóÃ*(¡",눢z£ó—›6Aé£vèFoܪKå]°Þ‘¬ß´-º8{œxì„O¥³å]mŒªw¾#òZÖ}‡~ .¤Hl‡<aAWª,9 2)kG‰ß‹¬`¼Ç Íœ\-~ÉBùTe ðl2‡Xmx¸Rœ³Èv¾¹a9áêW%¸SÜ3’&¹jÇy y%ÃK^–3Ž¥Êèx”å¬Õ·.ë0¥x3BuHs\aW=Wu%ÈòIØwê:Ïžx•*²•zçQ¸@?\%(^-m™[èÌB‡NTYß´²_½·Ð×þ+ÀHtAtñ(ù ê¦T<6Àk?Š¿!”úBb‡0‘é³eù¸©žÊnÆ.!ÇpÂRŽC,[ì°Æ¹|AK÷p7áKÒÅœ8\BL‹ðeg¡äŸÜ …OîB°_o…0Ü̺€þï~ļþ>ùi…›ñ'ÖŒ”#HʶFÝ¥Ýt x*÷RwvÖ0œÂÅŽYHšÔ@J>s%$Í×$7$ë0Ö@lÛRݤèøüÉÆáŠÑú j6ÖVÜ €{²°ûÁÑûÈõSãÿXˆÿk@­‡õ;ÕMÜI‚›Jð³ ¼ŽÇpÝó T%yí0¹¯Çü^@n1EÒaÕia‡¸%?õ@œ•Ûz Ÿã)GðwWá4˜ÙlÂgÈÀ+È÷¸.vgŽvö;)Àw¤Ñ/€+yå0v;‡Í´#Ój{¤„Û1ß¶o]BÕ}ÑànÅ$§îºè?[ñã;œnâ˜{4Éi¬WhEÎ} ŒŸû cóËÊ¥óœó.†MGUª;RèÓ¼ß ‡±Rá´ñÎíd sl·mmÅ{P‡àƒs¬%ćáÞì]$^#óªëN¼wk7ú`ááéUÑ­TÅY]²î«aÓú;Ö+t«ÙÃQö ³©qô4(ìҸ΋Ç]‡<_8 .Üè%åí:ýVg€®…ù¹TCoœWãï,D·~wäJöœÆC¸;fë2t¦5Ø/ f âuW|t,¯$†¼Ô¸©óÌóÙÎÏúïÿMâŠendstream endobj 28 0 obj 3354 endobj 34 0 obj <> stream xœå\[sݶ~ׯ8oÕéX0q%á6‘§QÆ­ù¸i'ÉtdÝZÝ,YŽ’Òß[€`I~$xŽlKnm?¬q€%°»ØËïfãb–ù¿ ±¶ñx7Ÿ]o”ͳÝ?×ÄÕÑÆ»‚Iÿ§l ôþÙìé rf™5³ÅáFƬ-²¼âÊgF»ÿ[=Ë9gª˜-Î6~ܜͷd&ÜÿÕæ×Žd:—jó/ÚÔz¨•êI d ~®)aüóL–3®rúhºêVW^pÅ„³äV6Óôäv$g‘Üäq$÷"yɃHžB¾¿Dò"’W‘\¦¦C†ÝÀ9¼ä£O¿¶kø>ÿyñ݆+8w´XÖÓŸãd R¸VÏk«a¶åLP‘ã$|¨—Ú½£1ÖÐ]£P„,zÁ‚XÂ]ÁÕc«%BÛƒ|—)¾×$|Oá$Ï`k˜Yj»Ä Ó¾ï5sM †¢ï¨sL¦‚¯âzžA‰\B™br9$ƒ Ž£xw“aßE’$dA8ü’a¯#ù45,¹Llõdê”C” +„l‚2và—׺¶·Õ0È ×$Ï ’ªIîjœÔafI]%¡Z%¿Ãn ->áƒcÝSí7©M|g‡;dzˆLÜ*âÆI6öéhf4ïº|³©Ã]]ïë~ù—rü¬Ë@¸a97ÂÍÆÉ Wjæ(©ÉJãnkä9w&%Yf¬*ÕÐ4’®n®9Ë2nŒ[AÃ꼤¤ÈZ\ϼFX–˼îZv8™+OZEG¹e»©ç\Ù¡®þw© I°{ûžøÉJkámÓ?&ôºˆctÝd¹(6sÊô<ŒÓ$ÞÓg‡¾£2Ñœò½®„*¤¥ÌÈ07Eí¥î"ệ‘Ö–XI¸eJfÚ-Ò›œ¹ŸbáºDÞvŸ`¸Vô¹7ðad6K*† ½J³ÓÁR K¿K'}k}[*[üÅLÈÞæ!éç]óRmS êoߊgˆ×Múž† œ6J ûmÔgmßΚßó¢~¨4Ú´í—l¹ÊçìOpéÒ¥P5“táy$ÔIš,óË©"²R¬Ð…‹È‚IS±ˆ à«^$PKù6P1¯ºbdù‡ŸEÏøÈ5Ø {PI-)‹¼è ÇùM%lG8­¹ÇÙý@×È53Þ¤>›0•´b s~"Á|棴k–9õoIá‘=òdî('Ë&gã…b&7ÞBü$\óâ_m!»¬`¼ä%÷y$<=‘!9IúI€³’|Œƒd=I$€0ûò} s¡CHŽå,àCT˜Úü*PMé9è×ñD{PªXߟF³Óñ«‡©óéÚÀ…Ôý«`¨ŒÆsÀÚ{83Âì+ØÚƒ1µŸŒT÷æFÁù!“˜L²¦&…4)¯ß|Æ9î%¢fFvè4‰•T´7„ó4|p‡aè4yö¶šÍy3‘.GËMÈY?–õµÒÀ­æ!­$̨“!†2­+x ð?ßF,ádîùZÑž7~x^¸ü4’lô4à¸ü=‚…Õ1MßC+)íÛÐ@S„ ü¡Jªæ LÑ#šQcXD§n<ȃLä¡K2sðQ©ãï2’â*I0Ì2VĬ &³ìÜ2ãÁÔd6ãN©*Þ%±ªs—Dåyu‘DÌ·2f¸Éaó-Å´uÅ·kΙ­D¾% g¸¥·s̤͵Ÿ¦b–K×ñÂÃZ®@ÑÎ(JJHúóa﫦 SZ­aÖíá÷xè° fq&;®Ñ:¯g7ÇÆ—nz2s¥”ëÙüüõ\0Sd"ðÔÆZ¨øŸDû¤™NẦµÔAœvæv ½™SÈ®4•-áÁ(PáÔ%¼@r¸³55ØÎç[n°ãÂK¤ÕX&•ñ'/¡7'´©éB^‹&í Ç†îΊ¶JõåÒfE«wF虩‹Âu¯´­L.l­ÄªË¥W¢£)W­W~ å¢”Zœ•÷bÓ†‹ <èÀ]ßÃdNÔ¾K³Êc94GK£dà— ç„Ú,g"tf[+[.ä‰?ÍkQ©ºïëYJºÊZy#þ\­Ž_†ÆèÎ/Á!Ú{ãðì”xÒÀ Ë®3ñá^ÔMsļ‰v+øÛØJÒ€e[óiatì’³&.¶‡m—ø‰R$øK('sèášÞeÿ»„Œ]еµy éXý»q©‚%UËJÀxM´›A©­<³Ü§¤¼ð¦?¯l“ :¢ç`ÙÇÆVD†aó%€Y|XKp}cÇNWµo m¼…ê5zÏFlœäí0„W逥(±ìz§pÛÕƒs’¦•ÖL—°q•ƒzŽKàˆzÛ¨r^ ‹êéÚ÷ÄÉfïì¨ã±HWs_dð )¹aˆ²K‡ó¨;ÖŃœ´Î»¼ÉÒ¼㸽]Ô9¦;êÊ TzbïöæÛIçpS;*übut ÏɈ>*Vkº_ñº±ÆIß}è@±£ÀÃðÊ’U „æÚǶvœ9x.*V9Ýl9\d@KÈ «"…tÿšMßè2núuêÐÿĥܞC¶IÎüšb”0ÄgË¥§HöÂåjË>Ãzñµ(YÍ}X`‹eäÐÁü¼=†e2pïµ+¬s{ÎïÅÑGÐån—¾IÆcb¶¹(á“ÂØ‰Øƒ íÝðÄ7‘/Yg\Ñ-M ¨íà8§M.yoÏ'ý=Y‹¶»'?æÝ+ðë.è÷à—ñ>ç6Û9–ëßü#$9#À%>–Ã'iøÊºåú±Ži¦ 1Š)r~»Ó¸oEHòι@ ÎHÞI^ŸXLoî(°q[#x/9¨!fw ¥trîÜ?ck_AøpÌ®—pÈðU´‘\$?§m G³·0"Oš½«7ù¸.¿®ÓFW†ý2·Ï®YêT}d~åã=°EF¡ S5OÝØš¤W‘Ø­œ±„—füQƒ W~·ëË0qOzC\@³'¶þíà¥i/l±æ¥–†L^j©‚%Á¹·„1Þ°W¿Ê² ¼Rß{9Ïò Wwn Ôa Nu¨ƒ@ý©åÜÃ/&Ž®!âí‡*âo×':’~âDÑX~‰Ñ8Vɤ8GuÛŸa8GèÂÍ뉓»áCUE§2.Ÿë2§PšBE1´ÝvPò± y&“Ë™–9¾NÆ•ÌNãµòYW¤%WR W­œ»Ú+¿5É.÷Éx§HˆÝƒ0²4mcf3ÚÉ>¤°«±ónßcTÑ“œäÊTØíŽ!\¥ƒ˜tênŒ…$ï `0o ×Û³•Nœ˜ì@´¿¿×*Hˆôž2øGÙ¤P¢¸;uV—1išÕåÌÚ¼†êÍé+Ñ“3|¼ÏIÏj5¶ÉŽsMw$|¿C›Ã·)ö;•‘` ¨æ™Ö&…­ew%ßG鿘ã}Ýn$[BÅ™.6º–ûÆ_60ª¾CÑÏöñaTêý ¢©LÈ2•…ë"x®ÄwÕ´ 67-xŸ‚lÈÊÀ=§*”°—FøÚ^z $¯ @òuÞûâAòÒPÈiØeœööŽ}÷û—ÕÌ U¶ÜdL˜ð:H¬Ñ^j1XÀ:ïÉ |a룾•1yÐÊ´v5µQ•ùõ%$0á};ì B,³búë)ÄôN:·º~kµSGäzßB¾Ä§D3ôî U­ì¤ïŠR‡­K“ýwä¦ì2å¯ÉÖÍ€JšZ pÈm€‹öΈÙ+'üÀi%èÒçEhl]t \ÀhaIƳŒ®‹ÀnF½Áø-|zs¼óžN}þÒ;C÷®ªÚ”ª(×Z:Vy÷áݵ¸7ï%OL„LÞ n™ÒÁ]?Aƒ³[ÀGâ(u¸ƒ„’H«õî\q,Mb Àx¢Î^ Îz¨É”×.®þòÛtNøÓegL^Ò¿îÂ#ü {…<:FãˆD}ÓšïÝ>@°æ<–pØ{ØJ†¡o°´Dv‡á·fˆ*ð÷)ð7cÐg}Öø‚þâ)^ó‘˜Á·LR_Hºë·…îðD®ò• ü ‘wÖÖäïó ÑG7~,©}¤>Š«~VÅ`QáO$M}+J]3ˆþäEOvÿ7ÆŠ_ƒºû ™ÈŸ$¾++«æ ~ŠW_”íQý|"ü ì“ß™› ±ä{ —dFVqù&_¿C8&Š®=:þø¹µönÏ;D¥Rz²lj^,y[g)d9ÚyÉ—°/ÖHÒlp^°Š]%??öÉ]Ec Ïß»¿ÿ‡òoendstream endobj 35 0 obj 3465 endobj 39 0 obj <> stream xœí[moܸþî_¡oÝ-jE¤(Ро4×ú6¾xƒ¢¸+Šõ®ßz~‹í\œþþÞµg(=Z­\šúçÃ,9’3Ãy#õ.ÉR¥“ÌÿµÀâ|ëÙ›29¾Ùª›“7j€ëã­w[.Íý¿ºA‹óä› ÔyR¥•MfG[YZU.+WTUb ú]I©Tj\2;ßúa’L·óLÓo3Ù'0-ÊÜLfz ½ý3@»ïeiëi+§Lª+9 ¨Ô¨*SWÊþ›U¿ªòÉ-ƒ— ^1˜@„ ˆ°Ëà3_CÜC¯!x Z5QøÇì;RA$ÒÙ«-êùÍq½¨Í~û±)O3[Ñ~i|‘f™". ðrš¥¥Ò¦” 5æ…µY>9 ýóѾi޼¨&ËÐv6Ý.©©,ôätj¨1¯ !úî\gåä¸ãrIQI¥ì™óÐn×QMªT¦]}%p¨^ò¬¦UZ:þŠÞó†HÑr³f]¤Õ*K]ËF¡7‡A¯®tÙk[)(«âg…~‰³d³2U&:+Wcʹ€:}A°„§b†µb"¡i'UÝ]qÜîÍAï-è]è$@¬ËXКŸkÈ+È]{ 9ö ;2ÄsaÖ°|6·œ=©u5 f#\“¾c’À=‚·W€Í‘ÑPBVVÇ¥ÜNOE@/·謧„=󤄿%lõ¯UF¸I6öûr7­ö°Wx ×=Ä;¿fwOê(÷*Q:Š ]Þ‰  ãjqúZO·³Ôj­ž¤Ε2Ú¬k¯RGTTÍm[¥¹±“\`+ÛvÆyT Ñ®Š® è&l{°*ó*s¶pâGÎú^nlIqæ\ ¼êíZ¯ýÀJé¼ð4ª(ء謰Jo@C”³¿ØLn!ž„†D¦Iz?NĆ%=±(Ùüã´Á7FîÉS‘Sàn¼èiÐì_qTï§²½Z-ôÉ`ù ÄïW`Žþo9ÔîCFïÉÀÂGà'0#ðÜm²r±ßº±Ì*U3–×fç@_ì‰[žë°—c"Kî™È­ÌO:«Ïma%Â%dÍ\¶Ü%[-Ô³Ù/‰V¯ójòõW´ëÜo”l†ÑNÉ4è´Ö*­mª KZ4[’½8 ”Ä>SžT¬J,¥åtÞjV_ΗEA®Â¦ÖeEî¥m˜B‡¼×c¦ ·E…5U Ãú¹`òd»ÕèTtÒã5gáž…sïþh1d› ¼I¡SG]é ý«ÁuÒ«.¥1©«+;µÒÔ»® êQÉT—%Y¨ÒÞ6Y/W%Ûuò$¸&À&#:8l¥:̇ÇÓc¶ìÊË1\ ¢¡svÈRP0BFØá¥;(H ÙFç ‘Å'`¥ƺ•úzÀø§ÊçæZv·Š­”ù\.søÄ­ª4u¿÷[ÇO#ç¯Z°ì$˜'›—@ÓÄTïy­âì­p­*ÌÀÁX0îÊ«…À]@k‹­ †m¤8²x邘p?O ïúêÉBc½Ÿ¬²…ŸwÈF!Ÿùëx¶Þ.±FçóL5aÍv+b6!v1?t©»†Ô/Ìp޼XVǧc‰F1‡PÁ°ÚÞþ3JÐÿ¢X™¼¨v[\«Ácb:oŒe.¸Z 5(C®1uYAö*;µýÉõ m5yA#)V·•£\6 [©¡ãf|ËtÅâyGw2B£v‚qÜlqbE"4‹[vWƒÏW~™þó \ñ³{7)lsaàe€fÓ~ `ôî¼oÄÕ…¿h'@oô÷¨jîtxÀ©õ)ÌÓç0½ÿ³÷s˜²‹L]`ås3‘ÙÄ”_ÆîF¬µîð¾ö|Áà.?•_¨Hú¹Ùõö·^çÞBEêw¹õ¿à÷šËAç6öÅm®)pµ—pH7&SÖŽÇd"È9‹¯±KÎ;§µ®SøârÆ"²IÖo8Þæf M¬°f}!ðŽrµ.‡èÐÅE㕬|0í\¹Öû Ï­THø –¶à<‘TZ” ðôZ8ƒ:/ýÏS›e^)z*šÛT¹J,ÅöBH#I9³,Àž¢iKÑ©pžÏû1«µ^±ÉcÞÓ]î“Ô7]dVþ<âý-Ûï´Ù•™ü!²l‚àÿ3}‹w¾VEN÷l}ûµ²õq¸d®ì‹¤àyÕ!BÖ|Î~(7ùEYªÁü¢“#çÍMÂãL! Gìßî:¯qÖ-ÈÙ8áÉ˵ Ϙqƒñ¨›¢ã*΂뽚d'kÆUbL‡+"é‹>Õî9×ähƒgü V~Vìª+dCRT²(™ÂÁŸ†Š_`îGm2tA…Ê^ÁÖ˜*¾Úi‚$C‰’×Ph„ïlFc'´BAAÔÄWÝâïšÊNËæ²­JF·Šm³¢¯ÓTèk­¡‘6ëÄSuÿ.Sí1¤FˆJ àW•<¢ÛC¹Sa£Û€`"ë@Y‰L?À: eØ6¼â~;Ý6t¼7Ýg†‹Nú!˜õs'»Úˆ5oLØЇîŽ­l[ÙÜõªù|È”ÒXN1ײ«5n+4WlZé½åì\áEWª+k–§®P­^FƒÚª;®æ &ãj˜h¥éfÞnaìq—¤Y-Ä9YØÉ L|×%ô¶{fFoF»y¹0;Ø}Œß£25vνä/z9µIÎÇ/ùøqèy/€|F+À ÿ{sJ }žCð†Èâ•zd;þ¢Œ“YN¾Ö­4±±Ôp2²Ë3˜}âwV"._ÂaøUv q!CŰ_üat+’óQE³0®¨sUõû8§êËcì­(JmüE%b!Þ5f7ÖÉÑ×{â¥å¤Àa‚î;H K)ÐI­ç9Ýþc€â›Žµî• æ”82b¶€'ÐR0+ÐõÛ‘W=æýj”õNŒ-·†¨ÛÁa=;2 ·\·áW¿ºÜê÷צ·øð0Lè8~Ö‹7¦îˆ—ûßÖ;Æ<ŒÒñ¨þÝÞö äá-lýœ +鞌‰iô¥üÕ‚ v‰ÎÁ€e‡Éîô/6h}4§Ã(3&2,®áÍâ@Ç'÷ùKzTƒp”uÃ濤ÞØ:²MdO5vðQèÎgŒ°î§›þ¡¯<bâŸ>aÞüèÝãæ4¾1O1ßó#æp•´qEãékf §ùkfâÊÓçÌO_’Âøø“¿$•òéCÒ/¤/g[ßÓß2Ý(Êendstream endobj 40 0 obj 2531 endobj 44 0 obj <> stream xœí\[sܶ~ׯà[¹/M$t:±=N«$“8òj¦§ºYr+­6ÖÊ—þþÞ ’—»«Kd×qÆ9KàÁ¹Ÿƒü–äãInÿxàørçé^•œ]ï¸ÇÉÞßàýÙÎo;*ö÷€ÂÇ—É󙙨Æ3-“ÙÛ<ÓZåª~ÏYšßºLª\%³Ë7)ŸLóLrÎO3 Æ ^ >“©Î”AÁÒd2åRg¢öiÍ,X*}J°”ä9k0*†U:µ ®„ÎUÀnGS8±3K¥Ìð“)Ëd®9K¯,hÞót k2+¾¿ˆóOã&²ÏY{J0\ôùÒŒ6¹J?OX•iÁxº0Ïò¬”¬¢Y$çš§¿¦ä[ãŠ:p88*ýuÒŒ- u“É¿fß›ãf,Ñ­ã®x縥æY%퉛I³ï0–ÉìÇٟ߸udV1ÉÓóInîèá¡Ä@¢”2éñdZeyΤ4DôïçºF¯/&…„.0J²xý^ð¼?É=4Ôå"«”5y ùu™Á¥Ið[Ú–vS\Óµx0a¥Á•kGÛ²û½Í§8¼óÚoMß…mºŸË8Óý&˜’·nË+4@žÖcs.Ó›8ë}üŒ¤½ŒyÊ…¦Ÿ¿ˆà!ÄpH‰jYˆ‹<+TaXfvbtB‹”a_7]BHV ì©.á¾þâž óoîS¿˜E\r»bàIû³fJ³®…È 'ƒE&‹B*|l¹ç'ºJå™’t>ÿãÔˆ‡“[Ï9Ÿ'\gœ‰2=ŠŸxßc"žÔì,sÖ|؆Ûfëo{7¹oNÏ3ÂiV.¶Û+ßj¯‡%Á¶ÚL½Œ,×Â0"éÙ)Üà Äp[Òˆ;'c©;% Yâ&Ž%\såõ¼ËÖÞT˜ŸjËè¨á0NHÁ b'X;íu8ÔÙ)ÑlÈP 42«‘ë~¡Ä<~Û–$w IjŸ‹µ2YÃ0Âx"ê* {1`“8 8g±úÛ!ÇÙ"ÒêG]öë’ Û˜%Ýz@Fž^5†Ef9+½aIºV®kM°åÙÜz6¼ŽÍ³å¥q( sh%£³N Að©ý0U®Yú:@{f[…eÇb`RùõU˜5:ôùÈЫï4Rq@Ä… u}x]Cýt‹àpÇTQ£sfu¡­ÈJã¦ZCÚ@':m îT¥Ì­"°î”?Ð"@7Z( Ðq€Ît ÷àÙ1Àw f\ý’Om>iá÷hÁec’Gð<‚‡|ŸC¼§pÚµyÔð\Q_€ý!äu€öZ¤aÜðB墪ÞÑ\IbtŒ‡½ sÑŒ¸ZdŸ+׍:¡ô™2eD؈ÏE@üã±ñÑ’“»†ð4²ð<‚Æö0ò™­Õ–p52àÆ±ŒÆ,å•×ë„Ã÷D‘']®ú=Lþm ŒgØØõÈ-ñmT1 Ý ç>2C6KÄþ]Ï€^à6IN‹ûÒŸÆ\xTM]À%°ÆÂì×;Ú ::¨O%Q«q óHä)8ñx~}“„O÷,‡Ì:ñ%ý¼ðD€‘X·Îv‰ùLk@8é²;­…ÉÓøÅøãOk5¤ Á½¼†h‡T{­D ÓÚìutHñU[l£ðH¹0ꢋ.æÍd“G—§ü¦˜¾_¡6¿‰þ-EaXCà'·C2ÿü›Ìÿ¿Éü˜¢F˜#FÈ¿\¤Æ¹‘"oŒ†¡£ f‘ÇJI¬B1¾eÆ0ä0NÀë·Ýì§}x*MK˜ñéQ§{È´N‘‡¤Q\ÊGꢨS>ò)­Ó î‚Ì÷Ô0{?_±‹BñjÃ:™_ÔÉþH"Û )ZÝ®Œ¸ùy§22R^:+ åçë4¡ÎÊœ{?Žœ É»á|A{e3{•­æ’Ì^+{ÀUôî¦P‡ €ý¡†ÑŽ“9šý§Œ…o(\•¼Ah+]„µÖú„Œ‘± ! OÔUmœØLï/@h¦¬Y‹ÓWÈ:©mS9ì—‚Hézð ¤ÜùZ•œmk8Ø :ViäZíŽÂ|‰K2ƒµ]OÔC¸§Fˆ…È8ªµÕ =3]¶Ìx÷ÕìÈ¿Ï&SMª7/j!‘Z¥»qÐnœºYõ% ØU¡9ý o9>A“×lÎü5´¯U>Ï:%.Tr‡3Vÿ‘ÈSŽU‚—šŸx ƒq0ãy€vôS€žh¯‡o0çJ@xÌ¡—L‚‰ÏÐϾ„Ž8ŠƒHà‚Òû£f¤W\ã ðn›†M¼ƒiø,~à뾈à.“HŒ­#¶û£Üþmi¸’÷!ó–ü9߆g;¡_ÖŽþlÛá†ý"½H;F!îM²—Ä1žÒuǽ"ã ýïÕ‘îFª˜%] ö-kÑÿz‡B]ç+œ¯Ú2úD–ô Zÿh¬0‰ÖŠ&j·Ý|9SÑäóJÕ\D¬ŠíV-]S³d¸š•¶©&H§eÄd{Âëÿ˜È<·LÑãR!3¦4ÙÄHàwiÅÕ<-3±®µI•"4SM¹”–±“i#'ÙÔ¨g¶J¢vù0–·Qu ÐÛÅtd/áh ¿µTÝcg· ð³ÇJá? Yá-èºÿXéúÕXæ $v˜#Ûjc«剙Î& ©*6†t¢Åá¸}?æ#H g´rŸ:íô«6d‘¬ä‚m¸>”[.ÄEÌ¥$]’v“)õSÆL\:!ê½_€ó d>p}Ê‚ZÛÑÖbé†z]ýÝ-qš Yy²ÂØ:º:Í6ècwòqOÎ!mp4Ž`Ü,ÙJ5€»£m¨í1$nUîñŠ0TT@ù ì›íÇôAOÖFÓƒ¹——Ým¼<+ó’:x$[½Sµr±k{ûXçd¤ÏÔ6žrUR‘„]Þ8ÃÝ«ù¬H†ù&¥¥NYðqŠ[‡ßـʫ¡¼0ÙQv{lQ£ ò?º§öòÑw1c =\™‘n!"MÍC:+`Aëàî1mRêÁR+9>,ê¥Â‹<å\§uÉbärÑfj:LËú)¼†à²•R›—e¿øôü°d7Wø:;´ìßbTt˜=Ü}Å‚ë †û¸Šv¹s®râ Ëîö”¸·û&Êõ"8z㟓©²j^¦?O¦Òh޲Ò6/ëíÄ&)r’p‰Iû5ò #b7Ð{2Žô8[aø8cZ{R÷þ%ÌV$ð)"Èá´×p 1ö耡,ˆ2Û–¥V—Š´úên(D&_ã™¶Ïn D¸/±Û€?@Mƒ46Èh|k5€¨¿µ|•­›Ói3@:î«l3¸«zøæ}DâGd~¤ÍàÈúå¶“ür¶ó‹ùó;¢ÉØUendstream endobj 45 0 obj 3025 endobj 49 0 obj <> stream xœí\Ys7~ׯàÛ’[á×\»µ¾²vlÇŠ¬T­7ÞJ¢mÅ%‹ò•_¿ÀpÝÀ|Ãá0²¬r§Êmh}èžw#‘H5îOíìîç£×˪y´ÿï¸|½ón§H´û¯jàðÑÙèî(å¨LÊltðjG$eYˆ|…U޲Ôþ»LG¹In;œíü6~4‰N³Lèñ‚¹T&¨ñÎdZ&B(¡ÆÏ':YiÌøÞdªUä¹äã8šL¥´ÈL9>÷X/'ÓÔ¢’ªäüºÂU˜Ô.ÀÒi¡Ç'cGéÒŒ?M¦/È!éZÏl_“ˆ\çõÚ´ùø˜Àyš¬ÿÌh™§~™§v™¶-Oïzäÿ†PÏdÔ`­Õ€+B°Z‹Pï¿tsYè2\Â=§V@ƒTòaËÉÿ~ÚQZ%™)¬Ä[yK“Ícre25-[ø9‰uÈ Ã!_˜—–Ø ø*§ÍvyHk`V¢å’ÉÛk¸š“2É ‘I>×ÉŠÓ%ßmÀ1tW wð xÉÑÒ®ªõ=lŠ ZîÀ&nÈœ5‘R£ÁÎDÊ—T£t–fA%5vd&¼ÔŒè÷G„µEªCm²²h[·‚O\ÜUž[#–»)§Æ˜D¥ÙÈIÕjâ;Þ&ÖAÇ2µ…up‚r졹‡°mxB;üq25V‘œa #öŒ [¥úŒŸŒXÜOŒúL±ä{¶àùµ-¸´y\ɶ’¹£m, ’µ¨Sqzuã8–ŠjXôŒ÷I@XÒãÀúÝ×ÖL'E*¹ ÉÒ1º¬(_Ÿ‘]cDn9‡xݵ:Øÿku˜6ó’lÚ_~ßq«³®÷ÉÎÁߨ5dn>Ukz(bl³¬'ýNì 6f<>OdžH# äÈ,_på;\À]c/Îd«ã¶N /½±ÿl±)Ò2ò¡Ø12BµxêümèG”NŒ*ó†¦Ö»@0{ ;°uÕ²šY{–†64Ö×÷$¡[šy†Œ9V,l•9ÍE)Ç{zà!숰„tIÐhR"SY+ì9'âT*Û¬óŽø¦Ëæ¨¼päßÝWÚ‡ÎvóÉ*r®xñ|2ÕIšËRÛí{ð€Àleà 5– áˆU ]Ú€^#·ñ™µiY莇öÙ¯Ójì -ò4/›m$P 0ë{,ÒE,’1‰kÝEöfsÝeì½è³+OYDé‡Õr§ÚºëlÍÖªëEm¶ã2ÖÁ¯pK…ý=ŸM¦™7,·yèD! ãÊÞ䉒y#z[­.Õ]zÛ­Š2 Ï7 ·…JÊ´,¿¼Þzð9öŒÀ=®ãvæE"S‘‡ÚÉ‚ê©QÖ . CQlB4’À½˜ÈÔñ´ÄúÉÂa&±qà(‚”r¥?VûÀø#Ý^F0ëÄ–ÿÄ46²3­su¬ÖU¯T½L¡òš ö¯¿EÎA$YÑpÀ‰‚P21U,\[㙇Î<4¯¡*¸˜fÂéR·3péGxèÍ1‘ôxÁ9 ¯\6 ª bиÝW„±ÿPˆwýjõÐÁº¥r÷Pªƒ…5r‚e-Olñ”8k vÀ´õ»‚­lØ ì;‡eÃ.¼„,9‚È–œƒ]Ä,‰˜ðÔñ™‡î{èÁ”=츄ëœAÂïø¨—Ú#Høs8ÛÐ Jì#¿ÅŸA̵ß"›ÿp ª¡ejwÜ”À¢‚e+ò›ï¡äbE ‘aÑöxÆDü@§È—ÄøEÈOï¯Í!*ñyC…&Jü hB6÷I‡’mšÜ€Œ.áÄØË1fx ‡!› íÈcOÐ=`~›óÜ&FWûQ*% i’*LCˆBgp˜ÆŒX̾‚ÞBdŸálçÜ—€°ÁL”Þôa˜Áa½R…%‹è¤oË„u¨-‰9r7ä¥#e]gÊ”ÿÝôÉÆ pëb8… &ðëë´à.üú4˜˜ôÍj0ŽL·$÷mPÛsH¾+¸ãuÜ) _CsÙòkðáÑâ¤> ²ad!ú3ØK ÜY+>sÀE.àÄW°ïM†=¶žÂÖ4Gpõx{½þ-cØÛ`z]3½ÂŠd±ÏuÏ¿îc“ɆaŽ^#ËÕ-bùî~9’*H­*t”ZeR“”j•ZeÙ&’L)U¨qâ`-¥Q¦³Ý¸Ì¦Âb©ÖTV&ÚdN |oÉଆ S8kí±¤¬]Ö‹Òw·â6u`™ëR£ë­k„–cîhÜ,ê1µ¾œÔ½MAüîO¥3Ú.ñûO]Í·2™Üï3ðÎß]àu}÷Ë)ÃÇ^aŽ&©{ÕMSGÐ*3Bó9r6‹dév,áå±O¸qÄOIìñêr’»ÙdÁ²íÞÁ73œOuDkd¯zá6q¶ KG9„³à”:öðô{“ö>|Ÿ¨_¶º2ršl…øÙZã·œüvêYb9w«Ì5c‚dÁÕËFœ¥b×'©ÄÈÑÑ»4ÝIQˆL1ð=ÑÔcÐ/óðÐCý7pÀ[¶ßN"=Ž”‡\hboøÎÖ»f øÇ¦qÈu³'ýÎÝa{"5ÛÆQ DDfÈe¼Î#H.Ø !1#k#|Ø$S–v·Ë¨óJ´cöf:lour€Ì²–ƒr=ÙσÒAÊÅÉÄMT¤›»\sP´Ùé9Óîâ”M\ÓéÙþ‚Îì‰SAc43³œP}Ù!tþ3ˆ;N¡û'L0¹×Y1„.až%wú d® ¯zÖzY×Y…Ð>—óƒ 7³2,R Óï€Ò?®ïI:fIúŠ]éò¬zÅwŽõ¬T á] ÛÞ4{FPœ‚Üì†ÒpŸßMB¾VPØd]U’$0uõ¢²½LêÐfpWÔ±½¥‚iu¿Ÿië Ÿ¡Òœ†´%«ÂYÒY††ÐÙnð-ŸÔ:‚íîk1²¾Aº¿,MtiwÈ/«e–[ÿ‘¸Lá´Â=BØ×%lº°6ÛÔÒ«ž’Áƒw CÚٽœVRßt~â®…V•j¾qŽÆìU·ºÌS+-:ÉR±*Òl¿²ÃÓR«¼¾w–iVº«HÛµ”Z޵¹="KÙ²V“‘—™õP®ÕXt/'+ÌF"×qu,˕఻c3Üùá’<Ò_«r#ªÈfþ…†÷&LwÜ?!s²ç/…Ùž:î‡W—–6Š î/æÝ×¥ÇP‹q9V4l8ƒaÈL‡§õµîU¬Õá=n«Î¶¶Æ•}³A¢ Ÿ¸®tg‰Þ‹ FªZÙ\AÙ[×ýDT¢¹Ù!2Æá’f¨i2H½(Ði—ÇoPUíû²šVæXq .SÇ›ì­Þâ[°Ã'èÏjQ¹íæ&PÈéÇn²‘ó œuŪí&íÀ´Üd+ölÚ!´_QU¾®ìýz3Ѻ1£à“¹ÌÕpq_\ Þ®wD_†@•¿w‘}Ž/ ÈæÆ*^5ëõƒZRèbz\µ‹ãã^Eh•Y×—­+)-#™5T¿'ƒ}š[òaÍê-`  ~MðP²0ïºWVÙý ÏU~(Y¯yJÖ·9–ßld¢2!×È](qè­›ôö÷“¿#ç5èÔx_ñ}Ùç-˜«Ë¢ÕòÚõõ6ÛJuÝcŠw0÷éˆú” ;ïÁO80åÀÉY@®± SÐôô­C´šÑ¦Þ„±Ø&î°Â÷!{è‘Úx ¯‰û%_èézxom?ºÁ¤„6Tå&¹KäÞ§žÙ#N ò”Ër]q‚+»f·ÌݯI_˜øôÄA×Çôvÿ/5x·šø÷ |Jà=Ø·7ƒ°w½§/Î.v§Ø–Äë)h ²5܉6Œ¯ ôöóMÈÃÛ*Ã6gÖIIô’t½†Ü¥¯ÞÑg}Z÷C|\Åìô‡© oßW¢ðçDÖÅ~›Å¶Az‹Ç0àƒ[[t(¦ltч…¶ŽçV¯#:ÉóÖ‘)| ‘]ë®øÁÁÎ/öÏÿ›¥Ê=endstream endobj 50 0 obj 3242 endobj 54 0 obj <> stream xœí\ÙvÛ6}×Wð­ROÄ ¸åœ>¸uÒ¤{uOObykmI±äÄÍ× E̼([I×8cr0î € `èW¥ „ý©„£ËÁýgipºƒgŸn„«ÓÁ«AFö_ñ€ËG—ÁÇSPÊ ó$˜œ D˜ç™HK«2Hbó{©aj.Ã`4ùm e«`òÅ`òáÏÃÉh,“0•‰žŒžT:;)0R'‰ˆ†#mF¹ž;iΊŒÝ(Îy‘¥{}5Ç¡Rå¦ô85 i¬Xñ5*+”¨• û©Ô9«þr4V:i”nª*JQC™)¦ÊžÎId-XL­©ˆãM_ ¦µ÷gTŠÙZ™~ñ߬NY4<%]êRa¥…†P 7b•Œ¨¢|ø T„4þ2ùÌúø*raaÜŸd•¿ Ž‘P2ÔÖyã(ŒÓH§NºtÒñF25›2‰HCYă+ÎÄ•+uý-• 2† /¡xLâœÄ5‰«JT@ ÜÚö¾Æü¢ß»Ö:é'}í¤çµ¾lbPþ^i™†Y­×o`¯¯Hœ‘¸„ÅV$žCXÎa±9¬âܧÀv ›ÎD¯ÃØ VÅXh:·áNrâ³÷åÎì=ƒçvùݸ“ÁÃý“ýã#=ø`‡â˜9‡º§0¨Þ– ¿RØ}µÃìéCnéÃÈ;®fÛ{Ø…ç ‰Û=⎡kIeIŠº%Bxº™BþØ_WáÐz†Ñ³=öõÅuHâ—$xß§û ‰/F<ãµie WÛ9€Ù“êŽ1I‘ø¥“öè§õ½^AÜŽ¡†Å5´€'†ShaäOZs£¶›ÖJãçiÍ·pÁ bùBuu½¤h ‹áU¹k -àÂ쾂Ƙ ØíX¿gòÇ“>vÒ-ÏüýgA6š_’xŠy&£ç¹ïœDÞV¦Sg¬|HÝÒ/(kH!ù&íøôøïS8ƒøÌ¡ÈŒ]ÔPPÑþÖIjm_á2B,äÙP:"‚Ðçtÿ†d{û#WJÕKeR‡å‘ ØÙ¦šÙ=ƒÀa¢u -,} ˜û²Fn›ÅŽú¡5?ñåžèUcé7:µÏwxUÁä“ÿ—Ðë TÀköè^)`çWçÿÏô ^4ÞÕÞäV‡Ož‰ëŸ‰û D ïAjlÖÂsÎ6‡5ò>TÀ3'žñžÿ5T`ÆÐªÝ›²‘“i¢ø„éÕCxÓÇ©1Ýbn[Bœ¼ËÕ蕯}Ã3mE¼µ,©øI=ÝR=E¡Hòò¨§ºaâ]4Íû~ꤹ»±š¹gt7Õ¼ä*îiNéö†YoÜÞ°7ÓÆõ{uBZ ~wå@×X ªõÍ(ÓÌ8¿´÷`c$®Ð¢Û4G»¸ºÒ¢õ¬5GíSzc œ¹âƒTœ˜¼¯ Èð‚3ÅZ+Gá¯Î{9rà6o×à푓ΜDîŸÕ|§WL£™®wï?taí%ýgLÏ à…D/‹ß…¾,!Ëí(ãxìèo{Æ£‘Fä}06Iï–Ϧ 7ÿÇ%_:#Æ 4e云ýifÚfülY¤™¤©p4Öaœ a)eæ:ÉŠÖdfá(X¦Óä²è²öÈ®Õ"Rq«Þ”UÄE„+5sª¬üã‘ “L(USv‹ÂÜÄH><,JI•òZW¬Ð︵ö:Å — ‘Ä›¦kc„qè¤#Ó‰8´êÒ‘|©4ÔYGòÍ#ÛŽD[è‰CÜ”É2IXQ”«©Ì1à7¼ˆ£³ÎœƒÌCÓÍ«`Ôd seX6ÓwóžVúO¡udµˆñ\o,.3â>8·fÝÆ£S—ѱ—¤K@@úg£Š éJìvIZR!âŽ, =dÙ:ÇÃ2š”ÉYl‡ïdfÆê.ýš“Â5‰UÊ“ Ý—0c seæFÆQƒ‘T¡V™äݹ†¶X©iÝÃm˜±.H=ŽÄ”ºtŸ þXï xm¿’\Øaä`¼p¦®!Šlta˜×¤ðƸĬ´Ižq…ZBZQo¢Õ¦]Q'†5Z[<–aÙDB6óŽ>–MH3n+!ñ™ F*5“užZëc­µ™³`¼™¸l¾ÝVË}ykêæ­k”HeV4²iãmé%ŠØ ÞL=¦ËàbÞeÉ‚»4U(Fº9ÔMj e¶eÌ´R kR v¨¦™¦~IJsÆÄø ¶†6Ahª1áS!뵹ˉ­á—Ç1¾4úXˤÇÈüßÌ㪦årÛ+’è,:1%êúÈItôhóT¿®Øž„7Þ/Fq.Þ ¤£Þ{*žg’“˜‘ø9ñÑzîH¯S³ý@­î 5>å_@HØ6§â³NÏ!~'ê: àÉ;¾Ãé Å÷è(”H¹Cv!>ñd'Ã7>è½·5{H;y7†ÞE#mGïÛƒ”ò-°‡èHà§V[ú S†2>ñÞÕ¾…Îñ^áaiQ(2–W÷¸é°^'Þý¿Ë˜×-:Aô•ÕrsÃv!à ‹&ã¨íñ<»’6Ûé±Eà¶BømÃnè´HÅ®[ì¶uÖâÜðíºÉ.ú=åÈ"81e^+ÌàMø–=j}kÒMжîËÜæbêý„tÍä‘„‰ŽÓ¼£Ã,ŠØŽñÞfdÆM’TÜ3€¡Åž–{0-l£0;‰ÔpeiGlµ5k¸Æ1Ygí»@ÛÇ{p¬1 ñ®ïuM/¤¥¼™®õâÀž`¥"—p Ãçë Þü}Ôû}¬QèZ¥AÕ™8Gf"ú:Iù³´X1|ºŒ×1|‹ï]qV÷eÓ;eTSjÞGNªî·n ~¹»ƒïM—`mwMé«ÙÅ4’H5CjÉød›9þñ@$ò_P÷o»uNo‹’] »«™gs»ÊU“ËÚBw+žêý³¨­£·ºè$…þ#bl{êcNŒ(–5ÆZm£„Ý ü.,¯8ßõvq ÉI¨Ë“@Æ"&6æT{ð=v‹aëM Jœï…àØ¯¬m\žbTÔãß·EØå›wvúkDQÞ¤V÷i]_¼{y¸~Ýãcw§°ÉœJ¤b‡»Ì·Þ?°ó7ïÿáOÞNOÍÏŸ÷pendstream endobj 55 0 obj 2429 endobj 59 0 obj <> stream xœí\ßsܶ~¿¿‚o½ëX4‚™Nd[iXq¢œ§Óq:óé$«ÒI²%ÅIþú$]’€Ì®ˆ¼| dË-¦Àj_Ìþ=ÿ~¢²<. aLk~8yÉÙž2Ím­ÄÛqãv¬ÖcF'¿£“A£c¿†[û¶.áŠVpX9B¿WìÛAÇÙ<òûöÂSßÝϾÂ5^ÃV¦Wðu;‚$R®[xmö=o‹ð‹§þÕZTžèX(T…i”Jè¸Ð÷f÷g›=BƒÌJþ€ÃŽàll“ŽŠaD;ðšy2 Ù±F†5;ä.‘’¯àƒ:Ü&ðÜ•:;Z$ÝÍ=µw‡Z¼åûù”;àws¸•-#w±ë•øø^6ÇŒ°Ëÿøzƒ>`Ï/ü¹§îPq<`?!¡ö©u—ȇ oÊû>&’Bèn™cˆqн ~qŽÁÄK%w±ï©Ý;Tøxïq´: iˆqÀ»{+K8ù‚/z{p@âhþò æN8òaü¼‚N¡èÌÖÝýèlÃS¯¼=Eñ’ù£Ÿ[[ó~“-ê7¨,ìÉ#ØCàSgʺ„‚°æ d†#çpG¤oÚ ¦„ŸõvèKÜ]Ø«È0=JBΊÞz_ö4¿ÙYI?JµFmNWP|¶>ìI^AL…ï`¬Í[–®€Ž‘ÁS eôùÉSÿ[AýöBJ ½èUm­òjg¨¬ÃS"Y }²æñ8Ô©÷á¾´*¤Õ´yò«öL:½ïÍÒ8ÉK³^3>‹“D-1òbfŽÆŠYcdÓ,Ï—Üó…§ÌºÍiVšÝwmÆXµiÒ™4A™Æ´T¦£}œÊD[R)RκšÝÐüÉ‚†vU¬´PNúªàÌs½ YßÍÊXfó+~v¡™š~C¬jV lÕ¬ Ì‘£Xyóz ¨‹^[㬽EÞ)u¯ðn—!Ý&cpr°Ãº®Å<ôåç`;®öàé5xºôÔkO‘¶7z¤Â8ãj—õ Ç[¦µ+ãýï@!H&%2ùN |§pçPêmð` Ìµ•§þn¶£â\©¼`nñ÷ÆWå¹™ÖùZòº-Gíœò ¸l>Ä»¼CòžGlô%Ê8ÑRñ)X枯¡ÓÆþš¦aîz5ìêä¿mxâ]Y¯WÔzC$›–‘Œ x¬õNÑiIõ–ûÇkk–F_™0¬„4Æhš5^´f5ökT[;‘e,…œþ­†¤XfÊe]Ï:ñ¸â´‚LëÖÜF7(ñ²Ðl]¬Ãù€ŠÐG|=ÃvPãGá½e0$ïk"YnÞ L—6 ¶æ~JäCƒ À‹Þ„™ºX!Ó³%µ. 2ÉøÖ;žæ™3³Šdßec22EáLæ¦%£²ïÆ}Æ /•{Ï”&‘Ή¢óùRuÎç•a-«óù—6«1€HäZYn†qV&‰šf3—ÖuY—YWWe=¾§`t2@GÄM7ÍYšMw,]ê$É3£mz¤Ò­I¤IŸ=%£Ok‘²’sÞŸIëu¤l ;¶ 9I¥µ1G­Øók&à 1[¢æ%uk†Kߺê-¹(ʲªÒú9j)s¡·_gõâ•@Aeàf…Jò¸”t³ÂÆ™}c&#™æâ { $Ì‘'\RlbM¯q¿¤·¸aš'‚b–ÍúÂi/ GèF˜„,)¬QŽpCü˜[ÁÞ¨åˆ;KÚà¹X€:Ai k´'S>r‹"„àr@¹pRs>¹hž–¢Þø £tê<È9Zõ ¡åï¼Xuš¥TËa6‹.ùp,É n.V%h {æUÅVš‹Åv¡.»%·O²`·€ Ûxfóózï¸íØiPâaóÊN)!¤ò✣è†ÚOÏA?*>RºHuýÀlî”e°>ƒK1¸z…>~ÇUwŒõqyk›ëOŒ+Òá<ørðÙÂèZÙÝì#Ýa¢‚ÿS0vPÛ–§GØÀg¼ñï[ˆ60*Mü$Âw?€0<ëûò°Ö ]€<™en;àŠ-{ÄfÄñÇOœÁ^n¸„‚T‹ p3%Ìp6ÆE––Òª€-ЏÒl&;“rÑ’Ò!‘½¨ à0˜»…£šŽçP3ûö°Ü"fm\ª:)E;— ¤ #6Çs¸Vræ$$U3Šá/´²6¿ƒZu}ùd—¶ÚÌôRÇøfC1þC„x çäÞmÔï[O­ÛŽ~ãõ‰æóYPfnè/C³ác8\eÖ°•ݪx;ð;< ìñm}:)ŒÑù"@P“gp™ëÐâqé]­näÙiJô»ÕdÂÿðáKÂ*÷oÌ[¢Tv[ƒí3Ÿ± ãóü€µŽ€0…wb/Œs4æ µhü¸õ˜ƒ`¡QrA‡’½pNuF_àfE\HÆ8§A¨@ͤ.j0ß]f±NTæ^È ˜™=(Ðtž$=%+£ÊD(zåQm\$§±{eÚº"GÉÁ/Xbg ψÜ#²’ÙÎÜ—»ˆ“Rj7cÜf ’¹ñ,0¶ °q"·y½dÇÔ„z#[Q²¤C¹?x2oË]ð…­hÙ±@fØ´­‚sÂ[~ÅÈj#ÆÆÖ^Fn\Å®äBvaqF /¯Ë Jgz]#áTÄIá_3œ"°3€¦ð/ 5 5Y ’,àlOj'——E“²i](aŽkÆx“Õ‡ P2FiÂxø2ä ³a­Ux¨¿ë¡þÐPéz4¦f6£YCf¨)³sŠ·Ã÷‡” @è"®ðyצè#™ÁëyŒ ~úÂH|/£‡ e·”ðÕ¤À·1¡{tï“ÐStUå~Ò*^¨Ç8Y” ˆO/ü§¦Ò½õZS÷9Fü÷PÆÜl „Eþ/¬aöý¬a»Å©M62ªÌiki£I"]]J´ªIt¡…0ÛQ7šÙÆ–xÀÄŽéÛôõÐesáG;õF"ÌN!Êâ½ 0`‚J|%bð"çÆk-¹8õ¥1’BÐ üm:0-³lðf ‚¿}üع8Äè¯^OÖ|Mo¶HohLLLhǬ+¾å4tÔ}Ò †5…‹ÿìª4”¯šâÊ:ñÔŸã9áÀ½†$>«cQìv÷‹¿œŸè,ý »g³ñF@Éà›½·,žâ3| ‹ ÜןFøúÓŸçO#?2<èQw¡ÍñAýÞ|ò³ù÷?Æv%„endstream endobj 60 0 obj 2746 endobj 64 0 obj <> stream xœå\YsÜ6~ׯàÛÎle(<@Úµr¬ly×Ù8ŠR›ªÄ•’uXZ#K#+ö¯_€$Ð òã`F‡ÛqÚ Î>¿n`ü.©T‰°±¾±¹£“·×Ms²óÏŽ¸z»ñn£J3û_ÓÀéýóäÙ®¨²¤Në2Ù=Úi]WB·³Ê¤,Ìßë"ÑR¦y•ìžoü:I¦³L(ó÷|²mÈ´ÐY>ùÁSßu”*mO©¤Hµâƒ)ý(Ó¨r©ÓJóï‡íwYg“ "ˆLˆœy;°Ö"ψ<Œ c}÷ˆ|ëH{Ê×»ÿÚ0¼R†W/7vÿåÖÎrë· mê9‘ß¹Eäf¬ï·Dþ6…œyPé\AÞ^6³…ÇsÀðçžaß{jë¾XcûׇpÆ–8ÃIŒCl†Ô„˜‚—‰ó/ªžO~ d@,¾ƒΡŠ- «ækŒU±¾ØC`i,à°+H2)_°¡°yßÁÉX‡k0oÄ|HtÿñÔ.'ùµž`×çÜû{Èëý¯A.‘@B‚ÙòÔ3O½ˆèk SÌK¿!¡'ÑàB ÿÙSßyêWO O=ñTé©Ph{ˆÒ‰ªŠÈï„*ÆÆ8Cd²ˆ7".¿ZâZ®”B§2×Q]×Á(YÉh[Á‡_.hßS´4SÞk¨N¸¶† bVÉޡ/À¾Á©Zj|]€¯t¦cpºƒu! æ¹£i7Ì@q‘&šFcìôȧp2æmqÕñ'"w¡“xEäïз°¾Û±°º«ë»nçP š9ì3š‰Ž‹Tæ|¥¶O Ä:fEo€R = ”§çáGK»K9€­Iak°dpÆ5=ÊÀyìÑ'€L}ï‹>cPK `s'‰Á"§Û©:ïÝNåYÛÁÞN(ÒR–Úˆ$Îò´¨…°uÖyY5»©LhipºïY2šÏ1z“fZ0Z¶+šè<2P¬)üÀ„>Ÿª´¬„²a=Kk£|µå¦T”u=9e°a¯lsÕº°Zé{ì3š¼î-]Ufj›ÙšI´eÁöÑN ]—6sÕfÅ,—Ü)æ²J‹ÒJÍ Úý_S.ÊÒÀ‚R1äsè)†Bú„ ÆB¨²´§“u*t&ù }ê0gpÊQ ê9€hÈqKuHŸ1„³‹rË‹Ün/™u)›íÅ|È ÊÍ‚µß™_y8g5&U³-ŠÂR]ööò}ÕsknÅÎñˆUäÍmcÌhÑî£õiåÀjEW{èÄ¡zFëD}£Ê4f¾þ`¾…­Šbs=i'È„‹½3·ERç6À®’U~¦KF*V¦Û¯Wàkî)ºß¢©+Ù­‘3’Ý*âÚ¾`}Ï ½`×ér"K"‘¨"ó˜Çã>ݺzÜ—Íý‚H9–R>2óµ§Hõ]±û d¾†ªïkØkUKïÿé3 ÍÄ ²/xĨ¹_ñ\ý¤0²e¶|E–L—E“no.껸 V&6`¬pãcÜq?_i£q?Õ²‘ O;–lù3ŸµqWêTç.ìv,«ãY×¹}ägš³…4'^@%`¨ØÍ4mEîô9É9“Ò¹½)1²)äȰ-üa*íåº(¸ÄÙ¨V3ó¿Ç-+ûðÀÞ Z‚ÒaŽZ½¦¾'ØJJI=¶ç&È•ÛoŸìò-³Yµ<ŸFœzCŠ„ÅILN˜ð=¢¨„XÍ’¶/¦PÌÒÙ°ƒ˜ššÝÛ¸É~†ï1XrÏ8ãmˆ­äŠÎ쪠]¶i€@ÛQ³6Ò~*üyúÑ8ªøÒó‰ƒÕÜÌ£šøîßÃßñ>8}ñEýUl2ü^…Á†Ò8…3øÛ—GSHŽÿõ½;}fÞÔº$WÐÏXð÷} t` ¦«¼‘ÿlbšÉz´–§XŒUDpÍ]"x2¢”þ,„zs舣Ùf,û´à¨@éªeQàûê´,¤óª¦Ê"-󮪪¥!ä†ÕBz¸´Bî3OsÓîfAúÙýûIÁ3"_É®‡š]Ûµ‡;¯RQ+íÖ `t[ò4Ó™ . »Ü;{X»>ÅèŠ4•ä°ºUÖÿrÙàãþ…¯Ø­y×Ô/à ØØýW÷ “M ð­ÂâhUÒh¢Õ‚¢²õŒ•ZW¹´Ýº#WùÍŠ4'3pW'Ý0`Þ.QZ œáÆ3¯V@jw;„1£$;H6Øt§X(èáÊð‰ûŒó¡}˜¡`M`ª²Br…@é’tÑÚ¡‘ Ö.Ü\Lalß]%̽”ó›Jë´®u{M ¥‘R\ r½Q_ÃR+EKãå “³L…çDÌ Pö½”*¯¬ˆ~MGPúø6ú[“u*[øÇWlOÑG’Ñß<&pµcˆ˜¢û=ƒób„Ç’³Òå ´Ï•_?ž6fù"´a öý hÃz‡c5Ú¡Žúç8Å]‡‡)xM­Ù“½AF¹Ì6u;ÐÐYf»žL™ ÑÀAö÷ËHÜ¡¿ZëKØs®@,ÜA8‘sn‹"9™S‹Îèû†ÜÂÄ—¨˜)V?ñÍ6ìû¥3Ø¢EˆKØeìïýÕ0¿ÐCÙö’ E#!»Åþ`̹ x>G¾!¾Å/½`=ïuÄ‘±ÍP‰m ‡¬Ás^;êhu˜]Ø„Ãë9ŒàŒb‰yØ1û¥ÆÐcMŸk]ñÙ4­Aa @Ù[^9 QJmœOˆRVOr5›à×,Æ}(ý»Ãƒ §ò´%õ§AR¿ü×ýG0?e—WìÜ{0­ýwix†c8ì46Ùê?û·ÆfYÊ…º¤$,R‰ìÆfn^i=4ú0jõ×PSðÛã¯D7ï«#=µê’ÕÆû)FLwÑ!ð¿á!ÀzÕÌí"¸ ÜÞÝøÑüù?Uñuendstream endobj 65 0 obj 3094 endobj 69 0 obj <> stream xœå\ûsÛÆþ§¿”lMw8¼œ43Ž­¤éÄM"³ÓvœL‡i‰‘(ÉzÔqÿúÞáq»|À-%“8“¬{îîí}ûßÃ@ÈqhþÔÄÉftp”ŽOoGÅãñÑ×qs:z?Ê‚ÈüS<àôÉfüå\wbœy2ž¿…AžgaZŽ*ÆI¬ÿžÇãT„AªlFo'GÓ™ d–¦b²šêÇBªtr2¥AŠ$™\Ù‡7ÓYlÊ|²´ÇšŠâ$ £Éb:Bÿ…¿^¢Þå<‘ ÓÉ­ž¨üOó!جFnß_ÉZ­ªé³h²ž*Ó W“;zFï˹T’ñU±¡Ži‚œë´9VÑàŠÈæª Â4rF`«a—«el—‘³š'ÔV·¹7Ÿ|œŠ4*Œ'v€µÀH€±hÙÜŒLBÁç?˜þ4ÿÛHF2Uªõd¾Ôjá.UϤµ(ãB`k=§EP·[j°‚ÝØŒsla?N¡tpã1œy=Ifšª%‹É5 @¬Ã‚Æ›xOä=ç®UŠK(sô‚šÒ ð´¬ÿnü®K«3×$¶€S¸±wÔ‹4Xʪ[”ÄÉ$¤ÏKµ1üM³RmtËùÏ£™RYÉxlfˆ%½88’‘5MÚeãù·£ùŸÞê·³(”"PjòF“AœFjrh©¹¥Æ–ze©ï-u Ú}e©ï,ud©×–zfû‹¥Î+J&fè$4/åËf仲­È¦Zò†È ‘ "ïˆùÈ5l{»¹‚ËÙÀwp½lâ8Û ìÄLJ%lÁ S¡Ô–Ô†ié›÷’¨Ñl3ìžÂ(¬ÌÊy¶ÖI­3ûðMëõÓª¬TÚ„gé¯MO±€Þy\Â)Öp½ûUY£/Q®`ÕÙ¥¼ˆÜ—Ê:(oV/h&˜µ­Œoia¿ÖV+HT¬Ñ¡<‚v-¸bö¡ƒ­H7/)Â~M âÃl¨3zÏ.ÌÿNuÿ4ŒãŽ+›Ý‡ò¹À².RÕhJ}1*%Ûê% Ô‚S=@„u»o߬‰` ‰ƒ—;­22HñyøTä²ÖbÁovËÁ¬xšˆXM¾ÐókÔØñÚÅwŒŸ= ËOÇφ?Ag"Òÿº ­Y“C.2ÞF4ª¡ ˆ'1g0ßb¸À¼hë`íRkÂ@*E¶O¦©v*ÓòMTG ~o«¥ÞÝ#ssl’áé“Wy÷yl½Oèý5mËçf»qhŒ+“Žââ³m "¡_ÐAÙ6Ò¢,ͳþß »ûba’±yátx"¤€äìY ¦T¶J8ÚÂ$Ű½£"ȰbZxÜ÷St‚°ýf'“̓ý+ä}·öÜtžºÎŠ à3Ò§åâÒ¬¼]’<ìðÑÈ,±y˜ÉHû›IÄÜsgå¥Ï”‰5¼ìà- C™ÝlÙ·Â2;Z`»C6.в/ù°ØÛ%|˜æzƒªð6Û·Ìäü@ëdU¯b‡ë§#rH¯ôŸ“¹® ~Å{Ÿkšôû¦ýŸOùKx/¤‰èÉ”áÏ5Dš×.30û‚è½zWp\Ö€¹—pCx\ÛBg­I¢É…Åv ³æ°‰mˆD2  .ÂNàT^‡ž€i¿‚‹_ÒõZt{î5ã(Òš`ï¢iCŒ©¾°Æ¯¥eß›x |+PFW}3ŽõŒ Ûâ r‘Ù“.ã„Üo¤³2åQ䩨m9™Ô[Ê]Ѿ¨­'].WpÎ.ð`eI XÐŽœ¬Ã¼é€$Ï:äÈfÃ{,5)yܬûbòͶ]'îw»1m$‘ðX¯6€°‚‘Úä:ÛÃøßZº­2˜HU‘)â cô¬´U ÓânR6pó0¤#öݿ†³NƒbÌH6Îkv0_°1ª”,S±_ɺ¼F+Á…µQ^×|gÇ•éccÞ6¾2OC}-?ëáhâá#<@· ¥’aJë’‚íe~û„“ªîd&×mƒ†¥@Yµƒ],$º²Ô¥–l…tx‰º¯,u2dšô`X /O!xÁ¡Ò5‡`½ñv†ýžõ£&g0Œ°ðÊN îárð§p¶3ÈÔ^&YžÙ­ÆnI\ŠäÈÜZÕ Í$G­k¸4®M+Ôk`ýAŠ+Kq÷Æ®™QeVöp|¥ ä`Ý[jc©cÀ£È¯G¶þ Ô$|ê›Öp¶½žoìAá4ÖSPÓ!ÉuFOk©ÏÛÊT¼Þ%FMÎ …%6-Ì=¤¸œBkKýoøHØKÞ@Ò[oÛã«Ñ> \PU-¿pÖÝ_Ì@щÄí• ”êÝ`VwT=E±1_¤àK»”Xê«O³•c%Çpƒï ÙÊnlœÊUþn©—[l\Ù^¸4«)ÖÂØö’8š¸Üä®ÁÆez„NÄKË *![ps 5Úæü/ pti›Ø4ë&†ž4Ä"ù»`‘ÜZ‰ÞÐÉ,½v¶ÝÇlÁ¬½~ùúá$kpÌuPj#NwÔ¿wâۯ̪á[€ñíc“Y Q)â«Î =~˜3Þà4ŽÚz.¯Yœèl±£ãÌÑ×`íªÍ}°…'Þ^ùšÈDøÚ¾$’’t]qt¯9ó¦Í¼‚Dß(ú+©ñžÀÈ7–"XâÖÉî œ)eä9Üþ¶õj,VS,\5Óx œ±ÁU×p\¬'žzϱê#·E¤?”þ ò| æ½@1túžbÇ™¦SKB´Ïôv(Kø¸Â”‡]W;êç6yloàá6Øñz}8òöÖ~:ý*¾ðà Kò:ò6PÓfu·Ž¯öÉSü=¨Ü ä¯Yàå<ÜmõjÔ¾ªx:½9rˆ*ôØ'ôc¸oÌO¼ClÚFA¼ßbmÓÖûÝŽ}y°((L¾|kÀ’  @rtë*§S„xp·cçÞj¯h™Ê°ïð.à0ß"´û¯'•Ç#WvÃͧ@‘a{ ¹üBW; y«šµC[³Ö—–îþ ƒ3¦š4Ê3wü‰§ ÇÍNOœŒ~1²ùÝ·æ+\ñÁ@I`XWŒWÿÔE\s³`ÝÌh«ß¡Úå­Ôúºw *k¤©½?¿ãýJb·Tè%X_«FÝIßjö† :—. [ÜB㜃Õ`×{)ÞKŠ1ù38ÂÚ7Å"çD~Gä÷Dþ‡Èo`ÛCŸíÇžc‰½°&ÎG?è?ÿtŸJendstream endobj 70 0 obj 3108 endobj 74 0 obj <> stream xœå[[wܶ~ׯàéKv/M€ ÖO©’Óº§­][}iOO´º6’vc­d»=ùïxÁ ÀäJqܤó0K‚¸ 3ß|}Ÿ¹YáÿõÂúæàù“]Ü4³7¿í„wßؼôÿ5¸¼¾É~sä>”eVçµÎŽÎм®maÚ^E¦+÷»®2#D®lvtsð·E¶\•…t¿ÕẩyeJµ¸ Òi² m‚tÞÒ³« ]©Cjö ›}sfC#¿K{uÙOVÔÒuù÷£ß¸uJ·Î?¸ß_ø_Êÿ8ú2Zö«ÐÕ[°œ—Az¤W Ý˜Ú»‰‰w èæ@j>Ò<Œç—ýŠøô‰‰W$žÃoI<"ñ‰¯Iü‰/aÛoI|G»#q´M7úù!‚Õ»SÔoùó7u&ªè4ÔÉaÐÊäF·‡¡\®ÜvT•®ýt*Yä•°~ñÂ}kµY|\Š2×¢0‹›åʸ« µ8Y®tn¤nŠEn…¨­õ&_¸‡¦´Ú«7ÈëhˆðøÿXت=6á±Ó„u#»ÿÜšyß6—º¨Œ7 ßÀÔšÍ-‹Þ÷ÃÝR§­XhÛ·ªö»å¶e¡»Æ²v½mÛåÒ.îéén)òª¬k´#nG—"¯¥×´ûèèŸþÊ•7¥²2• #HÙì÷`„³¥¹±Êð¹œQË[8ƒfñ²DíGQÎhd{Óíû«„³çÐÉ&„×ëUÖ~æE!Ëh=§¤KjzGM£•5K¯;í¾ùØ”T¥käìçè´R[QK¶ÚSû¶Î¢ixÖtKÜCuå$fPü¦³&«ž¸@ar¡Š Oõ*•©ºÙ”¢Ö•Š÷;ôÅÌ n Ig4«HƒaXçÛd™+Y›hÚ£Ój;pÆäì´(JÃ-úÚé`ÜRWº›b¥d:œ7ã_ñ¯¼½¬:ƒYu^Å[M§I¡õâý²vç¦4?m»Á:|Sä~XO¿n?* ÑÜ»\´°h¬p@ÖSãBKˆ Ýi Ö1ÅÉ0¼ÃHË| ÿ!Œë/ak/áÀïàÓ5 üê Þ¨®×?o]³u¾‡X‡µÝÎ)í;¨´SØÙ/`“™ÎúýÝæ›ÿê63};‡cÙòîç´ÂúÝÀ!Øn`ƒ3Øwv§3؃lç oGî%¹×ÛàI·Aº ],´PËã ±ÈÆ\2!´Û.žÕ.}¾eЉ|à! #©±ÈB ‡jó¡5Ae„ذ—Ôô öuŽ–˜(‹w˜.ü’zm•]¸ü”}ÏB7‹ø¬C6­ä6PÎÅCUèËíHa¬æHhËCûŒâ®zLu›¾®«jn2Hã òž¦éèÛ¹í¡º›L aT„cÓ·¼\"œÄpe‹aJ÷@OÝ|js´×hl¿à¾·“UÓ¿u¶5‚…¤rb ïüëðžè™Ã ½HÄ,}n ‚È¥_ýÕ¬gTC Ñ{Ð9 ¸M•ýÆ`"ß5ÐÖ%0Ò“ ãGû@€¢ß»ÖuáýL´yÔ”øÍ;Ð%ÙÈ/-Ìœ†Ût-pCê˜BÁiØ šb•v:ƒëº‡:8ÙÏ Œ‘óÀˆá 8/­aS ü›Ð’ðP†Òk1)€É}SÒ3 ÐX/Q¼da>ˆm¥-_9ާhUÕñ€§úÒôQõ ô¨GóÁšae"S¦Ò8éÂJJ¸%#»ῨVÁДÍðMk–Ëð®)l`ûp6§š±¶‘=t3#ó^åÝAì98#8•FÝ@SÄ8öî1v0\L÷ ÉŸ±!±+eeÝöº¶=?êÙææ¯³iL^צAsJ—¹­TD† öÆ/‡å ô¶$Gż½~‹9:v_ù ­*S|ö„\,l %`¸ï»6ÝIjßþ¦^m/¶0C¿öÑèÔ s¤xIغ¶ÁA³w»K7µyš§¼e£‡ñ鮩35Yβ‡ ·o—FÚp•­àGX]ª¢uk`ZŸ8‹ýÜDì B.¿ ±(í•-0ÊœÃn=,+ïµ]:Š'¼ƒLçöÆŽä¶8Ý¥û½7OÙ Àfô–îüÿ¤/ƒôHI¨årtÁþ×Éo Ê V°¾K§ ¬á×ЂÄžîÞÓõ$ºI€‚YÆ—KéZÂfÌu]ðF¾z³+ÆIÚ1³ûÈ-G‰Ojz°÷;$ô•‘g=††ßôxM~ÖY`®+…«#Ì2HxôÈ«µ¼W™ ºÃÜËÁôþ ’£î„(C…Q#±ƒ®'¯=Ìá_Þ³éz#nàöÐØ ˆÓ޶œÎÞ 8u„CÙ÷làþ±¶9A¿ ŠG´«rmªÞnÆDøJ›[¥#ÀG¬ù) b~†‰0uÄïŸ44£þâa8ö)éÌ'0Prõ4VrÑ_/ãy25Ÿ#šÕ&\Œô†(¶Ë(h¯àÄ ÁP_‘8ÓàEgcu® ™ÖWDäò9ô<‡m|ó™ÞX¬ñÎa)m‡û0^õ€‹*:ƒÕNKŒõ†¿BY7;’8Jí±ýÈÜB‘Adœâ¥Ï©ûz¦%fŒ³ȶÁM&ÞuÍu¨Mo5¬¯ôgǬyϾÚËË)÷ÂŒeµäï]68x¬¾lêàõMMðqø€E” :A"âjЗ2"¯Èî;VHÆ0!ˆ‡4Ô¨lŽåžLEÕ8m[mGŽY×@ ÉŸ¾xêá›æ“n:§sS[ìµlZã¬/ƒþ…1ŒYñᇗ÷sºg3ˆ°WJ"U"ÖìþYgäÖS;»2ì¶ègÎx½l=›s¢;1BŽø—.mžFTvk5H‚<\˜Øx‹TUåUe#ÅÊU7ÁsP9ÚYxöÐ"k+p¶ˆñí ÛL·s›Ÿc æ"¼Éö¢ÒUPÏJfÑ&¦._=]¡³âà›á±6™p‘KÛÅ dµG® ¦. ÃÄaÑñô8Ôtàn¿=:ø³û÷=nwAendstream endobj 75 0 obj 3091 endobj 79 0 obj <> stream xœ+T0Ð34R0A#9—K?È\!½˜ ,¬äe¥srYèƒX™œ«àÔhh¨`©gi¦’Æe giia`1ÕPÁÌÈ·4U074Ð3*ÈåÒPÐ Éâr á BŒìendstream endobj 80 0 obj 105 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 38 0 obj <> /Contents 39 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 53 0 obj <> /Contents 54 0 R >> endobj 58 0 obj <> /Contents 59 0 R >> endobj 63 0 obj <> /Contents 64 0 R >> endobj 68 0 obj <> /Contents 69 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 78 0 obj <> /Contents 79 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 19 0 R 26 0 R 33 0 R 38 0 R 43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R ] /Count 14 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 66 0 obj <> endobj 67 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <> endobj 77 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 83 0 obj <>stream xœy XS×ÖöÁ“T‰a=--âP'¬‚8 ¢ 8¡(*( BA„0IH´@&@!8à€ˆC9Ô™:ë­R­ŠC/j[½¶µ~·ÚÞv~›û=ÿ> Ü>_ÿÿ|HrÎÙgï½Özßw­µu {PÒ™ )I±ÑIÂïѼ‡?¨?X´‡òü£Åƒ©ßm½‹{9 ^"ÔËqÏ !ÿvæ“úCk_Pô£Di%»f&$*’b7mÞî9,|ñrŸ#FÚîŒõóóó\¯èzâ9+:9vÓ6ÏÉÑq ‰ñÑÛ¶OöœIFÇÅÅnðܧHÜœìµqcôFáµeQqÑ[=ƒbãbvx›éã9n̘±£ÈÇ¸ÐØøõ)Éž! Û<x.ŽÞ”•ô7)ŠZ¼ P±mÊé W†ÎLŒ^µpÖ'1a³“6- JÞ¼x{ì’¹)KçíØœµl~ZüúåÃ|†ð9jèG£ÇŒ—5m¼ï„ˆ'®™dò[ë?9cŠ÷TŠE ¥VQ ©Y”õõ>A…Q³)j4åE-¢‚¨ÉÔêj15‡K}H-¡æRã(oj)5O…SÁ”/åC-£&PéåÔêcjµ‚ ¡fP©‘ÔJ*”šIM¢Þ£zRNT/ÊêMõ¡DT_ªÕŸr¦dT$ÅR(9åBM¡\)7jåN%P©ÊƒD­¦S›)†ZB‚K9’íÜr˜æp¥‡ïDkDYǵŽmââc´7]Cÿ*™!1I~“n—¾zoö{×{úö<çëôu¯Ý½=zWôþµÏ¢¾¢¾E}ê7¥Ÿ¦ß·ý·ô¿í<ĹÀù†,DvpÀ¸_ËÓå¯\¹Ô¸üèºÎõ k½ë®?¹ q pÛæöØ}{¡û?Nx×c˜ÇnÖA‹i]ì;8|ð׃b¼™hæëÆNbSø7}ø7ˆãS8ðo†ú&läÍò‡t^WY¥Ò)XN+rÕYÙu û¨#eºp•%\ñs` ]­1fg©•é öÅõËdàÍÁÎAëa2Ĉø“_ÁÞb»Wa mÖëª*Uº46†Æ+y³¯£*²Ž^xÜ‹Æs:RÄq´BIÞ0 ·FÐ5òF.ÙÒú"x‹¡Œ“ã4PpF¼‹&+ãÐf¾šsh©à“DpÌeöcYV•mP‘I²ˆ]úÊJ•^ÁÓ`à«ÅU¶i±ˆN·n°š½Lã ø9žÏÅŸÛÐi*2B¯6³–%‰±/¸DΜ9¨kv•=†Éüy€`‰0ª†mÄ/"é4ë¬f\mfGB9ÝÀM»MRÙé}ë'<>Efõ¾´;*“P¼£X‰Íútµuél,èè'³¯Ù”º`C6‘ÁA|Hvñœs•à·ð½äQÇ–Õ†")8b$vÃ~ ®­­µÇZ؃æÝ5¨RjPrU…E¹*fEÄüä™ddéÿ¢GmÐãÙý¨eZVv7½¤\]åQ¡3˜YÙ‰WÙݵa'#;‘©ÎU°]¨qn‚<&Áxò~®²à pr˜Ù$*²ÕF]å.eyºàÝn¦ñ(¼ÕâS‚w+«,Þí!xW@€à]˜ƒ³qFâtZöƒ 8®¶húÃðÃ[Äóm‘w»‚l:Ýîûß—€ûk˜*‡±ð›ØÐ‘`‡GÛ´±|JmSÃh<¿Åcá­x˜€Æ¬l!u)±6(o-o§±|Šž†ñø7±$8Ñ„[ýJs"òCn‡êëô«¶»Ï¿\wnâöú‘'Ðé³À/†3Ød[ìÍáh9DÃ$Ƀ+‘‹BB×~Ìbo\'‡ ð‘ÜDꆴ;önB›¤!KVÎb, ¯‡~„ ÎÂÂÇ ,ÒøLüëH:M—[YE‚Ê\­_Â?ˆ7ñ=ßHHp³²IdÙé%„Èp~[°Å;Xœ×“ìý"?SŽ?ðÆ}qÿwÀ þÝ?¡ ðþ/<€]ïø¯§þï›èïå5±ýÝÛçí¿°–÷“8xÇcížZç³ÍÀ7m€>®²l^Á¿•²àVÒ«î/>:@Ñi8Y•µà¾o>§G×}u–‘e{-é‘,{)àüí/2Ò{²¿÷T²ê“ö2Ý!ç`fkÓM˜%‚‡ “Ã,Ø#n¤«úŠJ¥žà±·ÍÃM4†Sñ(H*0TP¾4ˈ.Ä6Ò0ïãØ!h´6Ð µ!íîr"¾Ð…77Jlàöd@˜Ì̶ul ñ5¾X<„N·n¡š½$ ÊÕ¦nDm áZGqg8tœÃdú$^+_}záþyh.ZøIdDÔš„Ù(T:I‚= /î ƒnÝÜöiû™íÊšCLÓ’dv6â&}áô±Ö=\~áæœ¡$­ Ë4úì6®|é!;Ó‚Ó $@_Jº<GVŸIˆyâ­— \ÜiaæCüb„åÊÊÌ;D\ô¦ªAƒàZvâ-ô8½ï´Ç^T›³»N]ZX.xÝXA¼ž^Å~²'Ÿ ­@a M’þ >_pð¢é½ø„Àw>­«—™áGÂR,ùq8Þ¿x¤õ,‹å’9KÃç0ø=Ú†" Ý‚ŽW;QßPyqR2ɰKÙën3S#4ZN6ákÃ{逛?|ôÉÃF•I™WP˜¯fÖ'ÏN_‚¤AQG[X¨…[éÔ%?Ch .} hÅx»½õÄ/°¯]ºù«˜ZÔyÉ?“ïT«ìb @SG[p•úl¦vþ 1 œó÷œâ\;å*Ê/â«ä2Yæ1tÊߨšÛÙú”ã;¿Úy/»VS—y(½z+Ú, œþÙØRÐZ´›È>’>©“,¬ª¨"Ë1‡%2Sw=ù®ôúöî›–í§¢÷³qæý´Êc².±:¹:³ÕKoß¼ðôÉÍÈ…Z ÜÊÕ••åúö‰Ð¶Z¼ùgf”ñó-qF¸L†Ø|°†OÙ/€F(ëÕœly÷ŽÆu}ÑîiO[Ñ·¿#%’NUuy™µodÐ]¥ò k9ØõÎU¶ŸïòÈÖøÉÒbù^DÖí‚_EËN/\²0~®ÇŠ Üy.´õŒ—ÈÓ)—ÿ½õÆ¡KW»F&7ƒ‰üCN(üfH2 9UåÄ­@w¬Æ´Damj˜0¹#eèÿ^ñÚª+ŠÌr«M–ß·•¶D¶R0k«á¾§ä¾0ÑΡ¢@ëEðÒüù^â&;Eaäš4?/Ä©4öâÛ¶ŽÚ~GÔŽ^â.´ƒ³Ð€{3éŠàÁê*뀙0W~æ$é3g‹iÙÛöçjÝŸ’XIã °ŽtŒ®â³´¬Ã¶–‹m­½4ÄÞbÞi¤ÍS;ÌËl#^[ãËk8‡·Öî Ÿø«ÑrȲK]N¦N+TšLâ®Ý$Y¿¢ÇÍž=~\ÐW/xÕö=„"—Œ²ydvüv<ˆAôâ82Ý+‚î¬b²ÂOvÇ8çmÍ5iOÿ §‡ø¯\½÷Ì \$fµ~g¦Z•Æ`Éýy-É·‘ô—ožZjð®ØøA ø‰ø—g$45v¡éa³ó„Œ½x=ëgÂñçÿû@3ýœD§…~ ¦WØd _dQz+šÞ šdëé{v˜ÿ/šÔEžY¸qÎà×­m:?Ñå';þd.…ZúÞœÏ?Š ÎR,erò4”#UêsM%Z¡ x²ïî8’>¸°e;K¶+®| i4úOóeeç§±ü‡{×\½Éœvœ»`™phÙØrëÞg¿Ÿ:§Q·úŠ/´|Â:Ôº\¥!š7‹XšÊ]*k¤SÕ‚ñ„Æá)â‡ò)âÇ–C‡]¹å ’•8Ô°Gèÿè6B ˆ>‘·?xwjá¼Öý;)—а4¦ex$Á*·ÝŽ+« JÔþÓ4>ŠKÄPòÐZÓþÿrU¡œ[`!žD6Ò&â>½„§°MaœXGËwŸÁ`›`¥ñ`ì‚‚‹øŒ…·U—ˆl‘ÕÑd¾qxád.-{i+éÝm»˜C>‡Ñx¸8ØNUÜl#ríÏæî7Á´¦ oÁ|ű#Ix¶f¡¿è>Ê©¶´™:¹KÐ"㮞l¨;æÑx$v‹ï¶ÙAœžŸ¼^¿@æØ‘-Ѭ¬ß·ði,£¯¯>¿hý–+72)ç¶î[‡¢Ð6El„ôÏG#З ­h L²–8Ý#´,»Ãl—‹^¿¤e-³ÎÞˆ½ï½ß¼…þÐoäÜ7péÖY+Yú›üçÇþïûLö>lÊÓ·o¿nï$1?¤ö9ç° " "DÐ×å& RÞ D^DCÿF@Zkw÷ -ßÒ‹;±?HB§cÐ>QüÌNè½l °Vp;ì½)âÇ#”H­NOœ…{lÄï!oäuôý;Øq_´Ai@ÒJƒÁ¤-*),cgCÿø·èwô{ý¯_AŸ’²â¤•šr 9Œe®¿‰ ŒÌ•£ÌÍÉG…¥yìW¸ÿao¡¹÷Œ?÷)ÈC…(_šcÈ5uºƒÕÌ=pøz¢ŸÑ/[ÿ5·ŸT”Hp·EXø`9vÿLS\PœÜ•JUNŽ>W_Äj‹À=Ê;j”E(_h Lú2­IÏ´ƒè)²K uÈÝdÔ›Êóuå¬?i¿Ý›w••—#“;Ù­²@]T˜Ç`÷ub`ˆš²³Ô¹i2º2¤ÐqN³e< æÑ5Ö#ƒçái’îSc˜†ƒ…–°ªRh ûXé ó½ä³piQÙbž{fåÎÝ•:m­‰ Zܹ_HVéF`Ç6ê‘|;‘׉7qyi’AYƒÜkÁXZ×ånŸ»Xî•ÕöwÉ[™{à¼'ߟ½©>þSÖHŸ†Ìü’¼Òä®AšüMa[aAáR—iôyÀã•n˜Ç«TùyyHåŽ Š JÈ?· Á>"‘.Ö–J-Ñ„&Ι÷ãÒ,§ÚåÅŤŸ&å77œl¹:ð»é_z/_™¿IÍÈR ¥”°Ñ¤/-­00õçÍWôñõˆùë6F)ØÊŒ¢UHšžÓ‰XÙÝ×ÏæM™>}Þè¥Kj¯fÕeù¥$Uæ*•ÙYélKâ¹ìó¤å”üøôç;«oNmèlnpð­PJð.¤ r©r<`Œ] ;Ïöÿ«`Ày[‰´Šg&Ò8­ÃM¼Zû99Τ é~ï§­¶>m‚©{áéÞ÷`…ò7ä ¡k­³¥²ø8iƲ‰•••–W0Í{N™šôùµqCçÎðY¾ÿT«Ñå•#rrs•ÙU™ ;Ø [.d\$F8}ópÇ€öÖÆ¨v¬°œoôãþhYÐ äò×vÕÔàÇÁ`ÛõÈÎM¶pÿõ©P‚'äÉ ÍEUȈnWßÃ} ç›Gßõnúó1³Â7øm`´áòºS¦š]»Ž7í!)ïÞÕyS&,ž8gÅÙg‰l¾köY Öd)ã߯ç›åØ·ÃX”_¤Aî‘.+ÛUÆÀÇ|µøv‡tº$U§"­†¾–9§$6ÓÝu`Ã$ùp[zæ8BÌîë7&×’žIÌÅ ½[>stream xœUVi@SW~!¼ç4(ñaâ’¤¢¨ V¥(Je+*PÙT @(D–ê6á‚+*:€(PPE¤"U .¨ã¾ @Õ±¶N[[:¥=/½8û¶?p_î=çžóï~çˆ(SJ$Yûªµij½f}ÔL6Æ?Z«ù`£ZØ™ÁOñMøIâ4œô›‹!“žDùÿ0 £‘¦G'J¬xf 4YÂŽÑ”X$ÊØSê©KÎLÑlˆÓ«ì‚V„ØOŸ>ãÏ_œ]]]UÑ™ovT^êT͆$•-Y¤©µºäDu’~Ê“œÖj5ëU´™Éq©ª¨˜uŒ`¥U'¨|4ZMr².Meçi¯š=k–óLògörMbôÆTÕʨ¤T•ŸJHCµDE¼üßEQö~Iëß×Åx&«ý½R6ø¤ê5¾«–¤%Ee$ÚÙ«f::Íržýöœ¹ó\(j&åOyQŽTåMM¡)j5•r¦VR¾Ô4jD-¥ì©¹”BùQó(*”ZN™Q攈’P–Ôhj eEI©±GYSã(å@§L‰“ê™h¢è#Q¯É{&â·Ä»Å=¦óLu´ˆžCw1ÖÌb&‹©`Î3OGÈGıvì:öžÙh³³•fñf›Ì™3æŽæ»,¦Z¨-îŽ ù œð€` ß{DÄs†D;0Øéu"Íç0ŸÂÿÄÊø²Ÿ bðá8¨oªxÉç#×Èwü’œ‘++mÁV7]¾ÑVÑvVË[qí'×zc«Å˜š¬i¹ £€É—J‰!VcùÝ0Vô+ØóaªØpœOãvVå@H”“Ÿb'l© ¶¯ðz{-ž€0Çâ9?Ûƒ;,…ñýà~Fm˜$'¯éˆumè«|Þr[Ùz¿«®µ¡Óeš¢ô]ˆ ÌC*Éâ'0M:bÅo2i€ëOT žû:OàÃe¨+óoÖÝFr˜Ks…H¥ôH}{Þò]™©±žä±ÆKIsƒãZn‚Õ}0y¦f̤—`NJq”b»PŠÛÆlÙÓŒÑÙ[uõZË=ÐB´@¶Ì7’g‹G?s10ªÿˆ;XÁ¤«×¦„#v…º¡£íÛY¥„­/9œÌÖì+)*.¯i*mBu¨úÃ"ÝÞ¬‚Í(ð§ô ˆ¿ÝÀ^Ì×29<Ù†»Ú-tŠÀV`±'ˆ±|`ìw-?=S<úº$ÌYðUý‚—)2ðXîÇËK°‰[òÛëü•¡Þ~q‹këwDªžŸ¹¢<óŸõ¹.Vx‹©0FdAî궆F¦¢'§îõßyu,‡|z.|€—(p#st°ŸëçSi˜À|Ó²v–ÛS4Êuï†é殜Pl#éëˆ7þ[â NBþÌct±¼­³½³î)êF_è[£Ol¨Š>à‹X|eø80}<Ç ŽL{}BDÒæ˜¬4ejœæ£`âš—áÿ´à 4÷]8݇ä‡Ñþ¼’Ça²l[þ'(-CË¢"<Žý"#<ÁI/†^•@•J°uÈÒÍ!Ž2ß¡¥_u%G˜àŽÞU/ ôŠÅ&ÈÅ&÷ôw5•^º¦øÈ#{iÀ$—v‡Å­Væ¤ñ&wSÞf´ ©+Õ§Óë3ÏlëB0èî§0,ø™ô+|| ÷úðÅXân3;$á³[`ùè>å¶ÁY‚’+A ÎÀ î…ïS®4üwèk°ðdþT0+‚=L;L'}É÷ò&Ü®¢eØîØÊÍ7M½N±N­[BÑÊÓ1Ï“§È¤ýWÓ‹¶êÇ'¬Ó/Óî;”¡È>¸ý@Þ1ObvdåmG[Ñšêˆ3)§ô­[º ‹€êƒ±—cN½S¡œ^µúpö~tD^SWÖ¦¾ì¨MOØž—“»U)g¼ïK?›¯ä2'`ª Z<•öaÒé[Œ7~º#;7},_Õ¤½W[¾s_•âÈ«ìÜm8¹>kÿáÝw+» •~ÄHàÄP}a!)ñƒág<\eAš„ïdnSÞ–ÜÍÊÅø‡7[w[N_Eò†²ô ›r³vd+=ñËá©bOuÁ!2U<iu~/(eÅ*Eöùðš÷ÑR‘°´¸WFž‚!ôˆè Xˆá7¡ä+°Î/Gx r¨ÄÔlÆþȬ…)ÙÑ7ä {ë¯ÕwÜùž¶% Zˆã®ózûúŽ' ;KDèUÊÏ«`;iÅÓy G„e±XæŽ'nX–¾È†ØŸ .`=éw|Ëá & O ¡ÂÈbä‚­@a-¶¦vv™Á“\8hXÃáׯµô ^´Æ1IÂïSQ/¡ :ž«!ŽÃ4rZº ›G9©ñ»È–Å–wÝI_d¿¹Šæ¯Áõ³`éñT$“ ¤õxüê÷õx|ò ¥ÎN€ØÈÄÚ ßŸƒ”Ú6eycui®nÜË_I ßòÙœÍ`öÐSŸn¥™õ.®ë°¥; ,ºÓG¤ ó™0™tBÃá6¦¢¡ú(X<øò‹†êšÆæóÇ® «,P‹®akì4u&vû@㙢ï[Ún!¶½&142=\£Qjããõ˜ñÂÊwÂÞÃ2–O3J«’þ ‘D]oÄãÀfØ“@ÜæÎ¡Â¼cÛY)_¿¥,)z¼×*ß UçòGGäí§ñD&GŸ›…2HÃx° ¬_=®íº h:_q]CíúSaÇYé@Ϲ¦ëÇçÑ¥ó¼“C#±ë3×e-dc Oí)Ý_\ZSK†¶¥&)Ú- ÄY¹É¨30X~yÝPÉ-f^ò¾4veðÀ O 6§I—Ö`g´“àLßcp °öc :‡k e`:@Äî¼xÎÊ Jl;¨¤ÁŠ9z²¡´±kâmýÝçÇUœÚ¿/¿PIÆ–œt"Ó³v=Kadÿó3=Íå÷Ð-L|oaQ„z“6BÑv4œ{ƒa!˜ö–}Fèó¬É`#6˜ò-éM…h7û­ÏEL«æ½µT<'û!,a±ù#wõ¬§éN›âìåª^t‡ýÙïêäˆLu¨¢¸«`oQMÕí†ãèst,³0’Å“qw®6nµ£ÿ"Õq _¼¸Ú}_i¤Òea~Y ä—kTó_‚ì±ãü Kfì6Xù&Á4pJ&Ñã?ÆmS-€>4r$Eý Œîç endstream endobj 86 0 obj 3195 endobj 87 0 obj <>stream xœ]Ww\Ù–®ºªˆ m‰‚t7:‚ `À†§"QQŒ ’C+Ј€4&ÀìetTV(* CTPÁøD%Ì(â˜Þ¬ÐGÄpн8¿w«[Ý}ûGÓ]·îÉßùÎADéhQ"‘hˆGDLrD¢",ÄÎY.MàÍEü(-ÞBaåÀݕ؂òÊë5DÚÈ@çÔ¨¡I&¼Ä*‡@êPJ[$JÙŸ½@¯JPDE'ÊÇ-_ºÒfüø ÿ{2ÙÉÉIªúòFî±Q'·"?’#b”ñ±q‰³ä Èí˜E˜<*F½Q.ˆ­‰‰X/wSÄ(âã•Éòq läS&MšlGþLñQĆ&m”û…Äm”{Éÿÿã„¢¨)^ª¸0eøjŸñ‹]|]¢Ü6F/uOTøy$-ó\¿<&dQlè8¹ýÄI“§8L6Ý‘¢ì(j1åBùR®ÔLj"õ µ„r£&Qc©É”åGyPÖÔ2Ê“r ÆQË©…ÔTʆZAM£l)/j:åM9S(j¥KéQ"ʈB ¥Œ)JB £8j8eJ¤Hö)j4¥ ÎR‹ÜE§Dµ´´‚µ.iÖFÚ?éXèxéìÓâIâfZÎbtg&šyʱ¾ìF6“½ªËêZê6è¾ÖóÕ;¯÷«¾¹¾Tо«~þ¿ ƒPƒ^Ã1†Û ?eB¹Œù'¢Ç`« ©±¶ýËïcü–6â[°²`hˆ—òUÖ´Àú¼…ÛÓ8xS¼ŸÙˬŠ÷OÛ‘ùJ:š)Ü›½?U¢ºÃ%G¿ÏÍ+.¾ ’‚‘BÞôã LÚ@A 7•4¶ƒU䱇‘ôÜi¬ë¸U¶n‘ÿLNþf@ß³3Îþñ‹Ã¤’ÆG ñ$ÄðˆEç@®ÍçÃo\:Œpø ›#O³³Ä^Ø­w LÓßž—#Ãf´ÊU¿±.ÁwÞ_>Ú”W!Ë-+Ï­A×Ñ©ø\–8õEc%1y˜h…Vþ‡¬°»b·>LÁLpì̇Eã>â9²L,æz.¹YZ­t7wUWÿ宇2µ"]»B~"èÂÔ“3D[šáÞf8Èb±gÓÑ­kNzW9<Ïp.š~¥LÒ¼²êžâžy;ºx²²…Åz¼!÷ÃE·olÜ]]:ÿzÓÔÕ%3˜ˆ \ DPD*ÄÏ€b6óAb<›Æ2Üm [ŒgѰ{œƒ-c4©è[¸ 馀 ÛÁ4®ãw’ò}Š[>“&WÁ„? ÃD äâÀ?ødnwÛ¶š˜Z¿“k±ˆøЇakœ†·[“Êy‚á{˜•%ÃVt†½ï {r…u 0í‡?@ïJ½*ú´,wÓ‘”¬p!ÃIJ~]R÷_²Òþ”äŸö““JúÜâ&ebaø`Ž“F]’Iz.Gyœ[hîŽâ‹YÐSjÒâïææÐÑ××ÔIÒBñF÷Â"¾’¹Ìlô:ŒZw\ÚVõzêMµ÷'`ì‰ç¿´;0|Ýù¡\†¥tjpxÂ*†Öçm*ÛV°»dßeöÛ^.ëieímÞŠÔeΦCÉ"Y£üDÆGòSCf%$Ól@ÅE3Þ ]#Ç"b‹ª¾¹ëÖæþtÝ;ô==ÛÖÑÖþýÔ€Ž|»°kQç̊шÍÄÃ8òÈOÁ63­°6œÕGœ²{܆2¡f¤ 7‚±¨‡Tí:1Ô3ªiÐF]¥Í7ot”ÿ…@Œ@'æ¯%þ7ÝNbyo;ØÇ1sÄ×fãixªßl,Ö`v€1(‰Æ‡DãQ¡»“!‡ƒÅ4˜ýü Æ•ãc< HÀÍs:䮜y\=o~dô,µ "¿„|œˆŽŸy´sðV`pâ Áx°„0Eù–¦IZ|$È4-n*©úÚÞ5Œ¤óimíÓãG÷ïÍ—ÂhfGæn´-B~!±Î¬¤êinÀq¿¾'XùMÒá° FšJžðˆGFòçÀÓÞæxØxlŒgáÙ/°Œl«Î¿yEæÌ,ô_ëº"º¨6MŠÇÒ™!¹qçcë£o¥>#¨šñò>HdDÏU¢Â×uÅÙÚf•‡³g`ûë·~è©É‰ô‰&ÎBœ‰Bœ?’8$g“ø²Ô9Rs¼-“&âáB0ÇÆB¨}ü·¼·h‡N<–”ÅbºÖŽ:“Ÿ,ËOÉ˸’ ³VŽôV¦e§%™­ ß´0*îp¶JºåØ®c»ÎëôA¬÷ßÞàˆºÑõÜšŠšŠ’K¨uE7ºb×Ë#³Ӡ"¶¬¢ Y*ékGg7fú³_)•0ލx³‚ðW;aoâ©À¨·ÚÊJÒ’r¥Ùªƒ[Q4‹Ÿ©yµ«š"ËcJ×_‹ÜÐje¸/ Ê€B’ƒ¥`lB&äÂ< lL%<¤Bóè»§7øìBéû·Éö§ JCî“ò£Y˜vŠNÌÉ8Ž*öÀú=GíkžVÔÞD¿ ›Z¢Ï¯;˜ëØAs ƦèýžL=:±çÄVòþÄ–¬…ŠNW¦nÙ¼uÓî`ô¹ãä…IœÙÃLêÀÖT’2Àâp#É÷LzOhzBÒVÒµlÃsl8õqxrÏ+\iØwVV¨:–šÁ‚%­[lô »¢Dâ1x"Þ„7À7x¬~}¯òé5™$åÄxñ¯´üÌ{ôŠžiF@)7ÆÎëf߉ÿEOo ù½üä#'¥¯™­ûvìOGldƱ&\$S ÊIöÜ!Èi¤ÃÔÔ&ÀžˆÕ!J@ËNLBR¢F­0F›ÇäPû–†¨ÿÖ™g=àVNx4__㔲·›Þ¤T)‘ïH¿ÈIc—Ö?Ú-%„›…'}œ>×¾ ý‚´0³ä6/•aºH^SˆZF^m®ìyÕ6ÿ;©¦¤m$‹‡4“ N ¯ƒðœÃÏÕÓ`<.ŵP*L{òö9£cúã…pÔd­Ò)|õp¾ŠÄñ#i\´6Ä}qlÅ])¼!10Xÿž;0÷›Oݪ‘JR1L>&¹€í‚1Žïã@AÚç IÆÁ>¡…„„ ³©Mè3ÅçmCÓ[êLœÑM!“ðŒãÇ1!àÙ•¹öOàp†i¼~/cÍüÆ÷¨9®\0šItf0kCììÖÖ½òdîe0È©hIsPK@φèê)mijn(ºž²|óÕ)M §Ôf5 /ž\È;€Øä4ñc1X9)ÌãŠwG§Yha@kÞ5l…­æzcã )˜Ó‡¯ŸèDlë)U”*có–²”t„ÜÒüÒGÈU+—![˜q2žàw}ùÅ”&ó[èjióeV’‚”Òs6³0Æs –“4Î tYw²¦®¨àF¶´)«êð¡osŒü:ß?sÕ^®ûìÀÝbp§q7ñµÚÅØ•ÆÕø¾îkÌýá×ì ežæc*i¾õö°'÷Î×ܺx&>XŠÕ'ÿñÌ›0ïÝÆ”{ÀF¿é†ËËÏx o–¸Œ•4·3ÿºùuºù™˜ 7¯8w1¡Ã¸×0fÜOzuEÙâyÎy €År²Ð׸ÎfWkW7—5ý¯Õ~XMÖ  ¡@ ž€¥Û!lß„¥@žÙ7´%„*@5«s@€`ö‹”^Ãó›@ŠÀ}H1yf­é8ô™§Øa+¦-± UN! ÌEp^qø$6‡“jø‚•Ö.¶Ò zkáÀb!|`-‡C?ňÛiˆ«ÑcÀ# ¦°† à*ˆææ¢…ëƒ}C—¥LBØaã¼)g—×ytFYÓ@ÿÖ‚ Çºê‰ ±ÐÅ5£¬ý%»XIïÅme¡+ͼƒÖúxFUßß#Å£èL<ºw i ãÿü¤Ï¢Zg}/“ôÝ«=Õx× ôº±ÅLwåÊ0©2z[òb·Ãp&³ú@þá9åùµˆ½V¡ðu r”m×p Œ–'³þ©ù÷¡”›Ï{`'gº‰±TáɤH“0x°ö¦Á®}®!”€ö{Bkdç!²[VsƒCh¯A™Lèã•ùõˆý¡~Õ4Ç U®>Šòë{…ó[<ôå ‚5ÒÏïÞ‘=oìä7ØtMôö˜PY1ŒÃ…/ÉÉ­'…u$ …IHšoäv^È(V•F^^Xö䀄»„¸¥ÎF˜CØèøÜóKk=„?'Ê-ÿøö0jÆKl´$<50Hv ¡Ce9wjª¯¡FT¢:±–µÁ~ÜݺUsæ.ó^ÔÒý¨¡õ®ì³õ­¯À‡”¦ˆT†% †ä›T¨Ÿ·ã‡s%9Ç;ÊJþ>stream xœuW XS×¶>1œAdPÒ šD°Š “‚*27 ‚ à€È( SÁ:u£µ¶NµVÑV‹ ˆ³U‹–€A%ÐF¥W[í]'w‡{ß>¡·}}ß÷>ø’}ÎÞ{­ÿ_ë_k‹ƒQ„H$2 JÎ(HÎOKL~9ðV"~â(~’x=Êú—»¶˜œD(þd̉9#ƒª‰¬¿|\7…¢±„X$*Úq`avNqnZJj¾Ü.:r‰ýôéÍ8{zzÊWÿ÷‰Ü/9/-%K> ’3²s2“³ò½ä ñÛi‰ò”ŒâœÔO••'WÈ#“SÖg$äþm’ ˆ÷ г—†ú–d'ņ-ÌIŽ ÷[·&Â?7% /520?-*hýÚÅÁéÑ… 1ïe®^bg?mºÜa†µ£ÓLgW7÷Y³=<ãçxÍ%ˆ„5G„~„'áHØˈŸ˜C8SˆEDáEÌ$Þ%"‰@™˜JDA„-±˜&\ ;"š!Ü{"†xp'¦K1‹XJ„¾Älˆ%ˆ…„1š0$ÆF„ˆ0&L1aJŒ%Æf„„ï,aNX㉠„%1Ÿ°"&˜L#a@HñQ_½"[Q‚hï(ÛQŠQ»F½ËÄ!â:ƒ1k v¼ íÈÈïÈSS©JKO¢?¡o1³™o˜Ñ£?Ý8úwCGC…ájà ¿3ÔŽqã;¦wÌ¿\ÒŒ®[/6æŒÛŒ_™°&ãMæš„™l4Ùeò)a:ÅôcÓê±¶c—=?Îv\Ô¸f ̢̲Í›µÀI¾•Sié Q¯F ´™¬ãp&«B:ÊDëÄUBœ’ŸY!â³ ›}„²É7Ú­Í$‘…ög’ÿ º!›„/”,*¥Àž’&ü'(K¥}¯ÏW²è]²äH üuUéÖ•¥r>àÊvïdæÓ7üø0WÍÕì>üÕ±CuÊ´ØlV%¿¨+µÂ¬³Ûç |Úg!9Û‰7”|««|çhI×?¯v>»w.Q!EÿéãåtÈÕ©Þ±™¾+¤’³j›‘ßÍ;t‹Nö‹ùtxÍ~|yã·…5)MŠ_ŽA“òEÞƒr°†q=à=²ÙTÙìU ~ã´øŒ‹+ÊŸ:/¬öÝ#Ó¡õÃYñ†,”HL¡Ԁ¡D$!@{u†$àñr¸ËÐ5h áÑ•Ôüçj1Ÿj <:Pk3K£ÔaS”¡5%;œ©æ‚DÂq*Ø©‚R•Yz4a ßc*ªƒ»|¨á#y[[_ÑrÍ’S'¶:Ö2þÞÉÓWZr­…ɧ’OÇ~å]l¢4è> ¥D·p5[+ü2“Kä’Ê2Öæem]‚_ BÏXØ …ôEîÄÆÃyxçÃùûÒS,¹ÔÒ×ç¯ÏËܸ’c0–\oÒ%âÃ1:+_¸H¡{:#r>ïÓ…”4|ÏcÞ[G¯í‡Ö~1åkYdëbüŸÚìÁvð-̇`·7hºl»ûüª2G¦‹¼#»Af×”™°‰rq%¯P‚O…Ù±~hÁ´—ò[pXz #TjkÔqÁµ‰.H„¼d’&äßD0ù‡æê»¥’Ò€‡4*áØÁk^hœðØx‘—«SØc0“[’þI$4jļ4°P͇ëÉL®ˆ¥)8¥Ãs ÚD»fäõÕ¼T-ÖZb3\uJ7Ž×SÆÔo¬S“o1ÑkòUüJtI#ÖNã96 Üœ”ȳçæfD‡/L±á!‡Œê¦tø]]ôCÎ ¸W/C43Ú»)­4+3<4Í{÷®0áj aò¶âì²êÜ{£=<ü4%NŠ+BR\î·|{e±Oàu!º)¯cÀjD€Áé?w…d~‘é¡I8'z|hÉ](Q°?ßœƒÆ¡1asfº.êS0½Þ«Ö#ß AíàÖ-:ûLÌï ,×ûá¥Ò†´g^-öج©3pÊÌGóš ¶`Ô×T%N™ü€å)A\ ·òHö…Â[O”·0;ÚÙχnÜyÂ1ª;îÛ¸måÛðöÀ©øÛ*¯Ä ê{òŽ”.Dw›ôä[TºšâoâÀ?4b½†?€ š -fQ2²Fލþ†5êžê沞û@r0† f µROpù!ò³Æ_ŽÈÉËQ½Á_Ž2Jýù~¢F<`§(P R(„dR*C§(ͰË߆ù4Lùq CŠyöhŠìáþGLvó[0ì’‹Ú’þ´¤»-)âD€’:#«ŠÏ3kv5»Ö C'i4UëÅàˆ4CF†£ñ-ÕÏ28‰5.°¬ºDê>1ìÅÙ妢NW|]ÿù§\ù!i]´sãöŽñY?_æâØ©[ÞÇ/ï§G¤ù¬ v«D?i``HŒ¹ÍB1u»x°áô…3‡¹û LšÓ¦HÑ•áL Å[Ànˆ¡Z—yyÅ,s‘ȩ̀*(S‰±ø¿OY~²J7Êø™*Ý\*T:_Êža3}ÕØûu’ºA‡k>Js5©a$ÇÝu};ó”.Û±uûF.š[¾Ì›‘Ô½ÀZÜŠ²:aœ x“š~8‹wyÌÏÄÛÌ£%C·–ÖY¡ ΈA>îUþçce ñW×µq÷¸ïŽ7ÞcrhÎo󪂬õ«Š—rÉ\Úžü¯‹lùò£“Ì,j·]w˜rJîþÑSçÎ\:pSo‚7FE¡ì`ë<ðQãJíDÆí‹`ÎÊñOî¾VÆŸ·9*ó:øUÖa®nÂ¥ó'Ûï×f-Þ!ý£Øýó*D}¸Ö‰;q– göá:'T¹êêÍ¥Ò#%{r¹5ÌHµS+ÚÞ]°2ÿ½8)œ*øN(f  Á9C’·Xÿ·R}u‰Î2 _­ü"~æ «¨Ô#[m*£¢Ú¹§°ê«Î|{¹ÏÌæv )ÐÇ‹ÜW†õ¸xœØRUx´`ÿZ.žqO²“êóTQ *¨V™]ÐðTX¥µ±<Ë¢kž[Haé5Ÿ>MXTݸJº¬%«Ÿd ðW0G÷FΨ¾ Ö2xм8na~äòÙþËÉ! åÃ$4bÿÑS×ó«zÕ4ò“ù]ü´vÑSœ(ŸãÀq‡)hŠ_àœŠ©Í¼[Q±ã³ãÒNzã'Êß瘔M{êd€žÑ&‹±-¬€Ð Ñ}¼v^롫죴™³­Ÿ]×J³Kê` ü¢ÔÙ­á÷±å`c߇æb¶Ýì<Ñx—ƨ—¹2^FJ~Î;·&Ò’K*HÌXŸU¸bc7—‹>”z.ç›No¿ˆ‰ß´?¾&±9ðI ˆ¹~îAEcmSÃÉ»Ün0êž]5z¯i¼¤ÛõHúñ«–ï6ýÌ݇íÒqX^ªõ=ç ±^‘‰žj´™Ž¨]7Úqwöž )•ß+øev·vbE*â ÌùJÔLK.Ê£üÝ‚ÎÜ‘ò´‡ÎŽv»õkwSuG“TRä+‚²j'êéÄ¡&Ôô!zIIÉÒO8n{‰ÔÞ³í³òýó¼©þLkLC‚‡u¥O-¥Á—8eþ©µba‹J×á [‡­4”^Ì„bùê¹bpøÎÀ– I¤G_õæ°‚¯$gPÈNçä¼7‚BWIþ÷Áœë˜ ÉB±bõ¸üQù…KóK\ƒI9…rQ.©/·Zi…è©FR“PTvÓö~áhttüþ£©Ò”ã%u\ÃïvÄóÈîw'Xó`Ôs˜*åw$Œ`ÙõfÙè¦à6õ]ÊVç Ç' °uϩ׼Óï:'RG‚íOÄà€Ï›=œ‰£óÐéßÅÏì6;×ûõ<|Ó„ öÍsYe½à˜ nÑšÀfÄÎS¬OJ•æålÊܶ„yB}þ}}’cËŽ•­§¹Ô‚Òà-Ȱ´øãô a¹˹@FRäp/ü÷{­UWnHwÅË»Âäöï¨þ×bd¹ì-%¹ùi«ßã˜ä“­muÇöËÔû¾Þy|?óWÏ:ÑváŽÝŽB+uÃh-?LÚRp÷î¾Òƒéœ_°0ÿíºØQjåý šõþt$É“8ô4ô_„^„#,øÁ.R3‰Ì ’t `!ÚƒBQù‚‚ °,Ð rh¤þ£›9¿…¤Iy¸ˆ¨hÉãÛnµß>$EÄðóVmR°ð“ŸD?n±÷O(ˆˆ“f\M8Èùs+Ö­T0’¦‡ôß[Æ+}Ђ›¢Ò+zý|tŸ–4…iÍê²é ®ö¾àãþI.͉H’A SQ%«é‡BÿÞýËom©9„bül(d©@søŒš…‡“ðІ‚p å¯Ìæ_Â\ÝK'Ï´±¢®Çb؈°E Â!g¹ÜA!¯°¾µQ®`s9pãÀµ ¼Õ`Íü± >kP*û ,î@θ( Ä,˜Å”Ù4a=Â]%rCÞ®/ÃM’°Ì õ2˜\_Df€KsÊÍmÇ˵þ@œ(º19ù30À™öxS%„uŠ`O§˜? ,J„D”ˆýtþ)$2þG¶õ¢þ·âþ7¬ÍÍbš ØP¸|“Lwž¾øLFXFžÁÖDPwY´y!lÆ Aë_!jÑÜ׈[´±,ÓÕÈ’l ªÁ’1}ßüÿΚñFÔÿF Qø›7ÂÙ…•ڥءbìÚ4¼‰l£`“v“þzÑÅ+•¢óƒP7(†ãø€`.,òQƒÏÛk~Ué"IGZzX”KA.ÜùÃè~%8õ‹x/Œ\&L7ŠŠÑ™“©³M§â“^Ü ±~7"tºÏò†Þl÷¾E{²+׉þ!£Ûmóê L¹ãK416qCæjÙqˆ#á5²õ-e_å3¥èœǻ„¥¤˜yˆà¼9ÿäE|@!ΖCc¾žwNQy;C)\ކ^ Æ;kÄqÎ’dÙvXpõåkî6w>éË@ë€;û´5ÂÁ%> endobj 30 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 22 0 obj <> endobj 29 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 91 0000000000 65535 f 0000040497 00000 n 0000063795 00000 n 0000040337 00000 n 0000038071 00000 n 0000000015 00000 n 0000003198 00000 n 0000040545 00000 n 0000062939 00000 n 0000061070 00000 n 0000063314 00000 n 0000061508 00000 n 0000040586 00000 n 0000040616 00000 n 0000038231 00000 n 0000003218 00000 n 0000004732 00000 n 0000040657 00000 n 0000040687 00000 n 0000038393 00000 n 0000004753 00000 n 0000006838 00000 n 0000061995 00000 n 0000060088 00000 n 0000040719 00000 n 0000040749 00000 n 0000038555 00000 n 0000006859 00000 n 0000010285 00000 n 0000062572 00000 n 0000060643 00000 n 0000040801 00000 n 0000040831 00000 n 0000038717 00000 n 0000010306 00000 n 0000013843 00000 n 0000040885 00000 n 0000040915 00000 n 0000038879 00000 n 0000013864 00000 n 0000016467 00000 n 0000040978 00000 n 0000041008 00000 n 0000039041 00000 n 0000016488 00000 n 0000019585 00000 n 0000041060 00000 n 0000041090 00000 n 0000039203 00000 n 0000019606 00000 n 0000022920 00000 n 0000041142 00000 n 0000041172 00000 n 0000039365 00000 n 0000022941 00000 n 0000025442 00000 n 0000041235 00000 n 0000041265 00000 n 0000039527 00000 n 0000025463 00000 n 0000028281 00000 n 0000041319 00000 n 0000041349 00000 n 0000039689 00000 n 0000028302 00000 n 0000031468 00000 n 0000041403 00000 n 0000041433 00000 n 0000039851 00000 n 0000031489 00000 n 0000034669 00000 n 0000041487 00000 n 0000041517 00000 n 0000040013 00000 n 0000034690 00000 n 0000037853 00000 n 0000041560 00000 n 0000041590 00000 n 0000040175 00000 n 0000037874 00000 n 0000038051 00000 n 0000041642 00000 n 0000041672 00000 n 0000041704 00000 n 0000048203 00000 n 0000048224 00000 n 0000051505 00000 n 0000051526 00000 n 0000055249 00000 n 0000055270 00000 n 0000060067 00000 n trailer << /Size 91 /Root 1 0 R /Info 2 0 R /ID [(躮o¥¯Pc©Cö5)(躮o¥¯Pc©Cö5)] >> startxref 63996 %%EOF simh-3.8.1/DOCS/altairz80_doc.pdf0000644000175000017500000117456611143604346014555 0ustar vlmvlm%PDF-1.3 %Äåòåë§ó ÐÄÆ 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x­W]s¢X}÷WtåiRå0€(ºoDq¤VÁ2Ùlåáï¬r{qœì¯ßnÀÏÌTefIRe”‹}úôéîÃWø_Á胥kz_×ûе{`w:ÚÀÉàrø0T¤ ôòW¥xƒ®™–N?åGïOoñ›lÛÒðŠÑJ7pC§SÁWÏwtË„xƆ¦ƒñÞEÞlκH¸ü»¯ßBüܸvúbŠôö8ÝÁ –˜à)BÏÒº=èšZ±! }Së÷1;ËàO·… ,ߘ.TéözUºøj÷…Qgkjz«Êö˜(D|³['…p¯’gv‚õKŸE4tÝ˰*‚1dM°a¿wvÏï)Çß bUZøJU´˜ ñcÌCïã$?ˆ½¡ÛL n¯N椖x…EëµØóüR±}‘üyU@. ž2H¶Û5g ž,µ¥ÄN¦¬ ž'ò¥ IžA&Ò݆åERp‘ÿqÛú5ÑÕÕ>£¥ÓÕ¡×±‘–Ö¹¶‡Q×4#ܲDuÒÀ6‘lw‹5W+–ω_³,Uö’ËañsV0 Qº2o¬mè×`‡G2ŸÞ¥O·çP.âÿ^‡^‘e÷;•†N¥3¹áJaA€+X1É0óg™äËÚ°”ŒQ§«D>c5±ÂIþ[&Þ 8?r’DRŠ‚N+®š!«ß!Á_TV‰e±OpBš¥DÊÄy),Xò5Šñéiñ&ªo¹yº-ág …%/…Z_k¯=¸Æ»çÅJì œêª<%Ù·1xºÞeÄÚáòšoxÕ%¬²·ÊnÚ)äœÚ­ ‘ñ%½2*D3ˆæ«Þ©Û¢ 'È‹]õJÊrBƒÌÀŽRlMýµ¥¶/«Žm_³Yõ:*U‚yÕbiH»{M2FÚ¯Äæ¢¢¤ååNæU‡ã‰L€e&_XZ&ÕùLË3NõQ Í%C·ú×HiŒ& ñ•5½¡(hjŠ´ªëéªV r½`µpª‰Eè—ͨÁЯ¸ÅÊ«gǦ٠Yt]oíÿ¯ä†Ñ#Â.\J#ZæNˆ^á~ê„0¿çAä"a£c [o3B?^ÆÆÑ{á‚©½—øž?‘wæú±†RQB°Na¿æîaâ–!hÿ†±ø¤±aàÇ!¾mJcZá«ea|ÄôàEnœÐ‹H`ã0@ܤ{„Bô ïVð¨'Hp IÌB»~íØ“›”}":²7r)‚ÄõË‹‡Ã‡9Ѣǟ·kú™- † endstream endobj 5 0 obj 1368 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 612 792] >> endobj 6 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 10 0 obj << /Length 11 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream x…”MHaÇÿ³±Ñ—ÅÐÁ$T& RÓõ+S¶eÕL b}wg§™Ý-E"„è˜uŒ.VD‡ˆNá¡C§:D™u‰ £E^"¶ÿ;“»cT¾03¿yžÿû|½ÃURŽcE4`ÊλÉÞ˜vztLÛüU¨F\)Ãs:‰Ÿ©•Ïõkõ-iYj”±Öû6|«v™P4*wd>,y<àã’/ä<5g$©4Ù!7¸CÉNò-òÖlˆÇCœžTµS“3—q";È-E#+c> ëvÚ´Éï¥=íSÔ°ßÈ79 Ú¸òý@Û`Ó‹ŠmÌÜv×Ulõ5ÀÎ`ñPÅö=éÏGÙõÊËjöÃ)ÑkúP*}¯6ß~^/•~Ü.•~ÞaÖñÔ2 nÑײ0å%Ôìfüäý‹ƒž|U °À9Žlú¯7?ûÛ‰j`¨‘Ël7¸òâ"çtæœi×ÌNäµf]?¢uðh…ÖgM Zʲ4ßåi®ð„[é&LYÎÙ_Ûx {xOö¹$¼î̥߬S]œ%šØÖ§´èê&7ïgÌž>r=¯÷·g8`候ï 8rʶâ<©‰ÔØãñ“dÆWT'“ó<çeLß~.u"A®¥=9™ë—š]ÜÛ>31Ä3’¬X3ñßüÆ-$eÞ}ÔÜu,ÿ›gm‘g…6ï64$Ñ‹áÀEzL*LZ¥_ÐjÂÃä_•å]½XážÏy¸[Æ?…Xs åšþNÿ¢/ë ú]ýó|m¡¾â™sϚƫk_Wf–ÕȸA2¾¬)ˆo°Úz-diâôä•õáê2ö|mÙ£Éâj|5Ô¥ejÄ8ãÉ®e÷E²Å7áç[Ëö¯éQû|öIM%ײºxf)ú|6\ kÿ³«`Ò²«ðä.> stream xµÝ[s[×™½û{} \:UÛ4žÀ}—ØqGõo簭ήêÊZ¦c&:8”wïO¿A®9Æd HUñ']¸ä8^0çÏZóo«?¬þ¶ÚìVgë“õn½Þ­Î//V—§§'WÛÕíõêÿ]½]}ñåûÍêÕûÕúþ÷ûWûÿÃúd{¶¾ûuÿ¯>ŸÜ“./ÏNöÿËæÙ«7«_½Xž.ÿÍþŸÛýº>Û®^¼Y}ñõæd½Ú¬^|¿úìÛçßüfõË×^ÞÜþçný‹Õ‹¿¬~ýâ~° ¾{¤þqίŽ>Ðvõîûý8áâìäübu¾=9Ý϶vÛ“ÝnÿÓ]^í?Û?ßÿ“?îjùqÏÇS²ÿçý»Ýõ§}¶ü´›ùðÿÜyH>½{"·»g?7o~zýòûÛÕ×7¯¯ß?öqÆ öà'¸8Û¿”çûlÿ`ý1Æ‹vòøóÅ_n)~w!ñ—ëýßH÷Ü_nèK{¹¥/íå}i/ÏíK{i_Ú}i¯èK»ÛЗv·¥/íîî{Ôý­ÝÓ—vwa_Úý׫|r®èK{µ¡/íÕ–¾´W§ô¥½Ú¯¡àK{ua_Ú}i¯èK»Ù/]ïž|´JÛ¬·÷¯-ß޿¶ ~ÿÚ2ü~ !ŸûKûÒ^Ñ—v³¦/ífK_ÚÍ)}i7gô¥Ý\Ø—öÒ¾´Wô¥Ý®éK»ÝЗv{J_Úí}i·ö¥½‹ŸÜ—ávG_ÚÓ5}iO7ô¥==¥/íéG©¯òÓ%Žbø%Žbø%ŽRø}Ü+ÿZ-q›~‰£~‰£~‰£~‰£~‰£~‰£þ|‰£~‰£~‰£~‰£~‰£~‰£~‰£þb‰£~‰£~‰£~‰£~‰£~‰£~‰£þÒ¦Q—6º´iÔ¥M£.muiÓ¨K›Fílµ³iÔΦQ;›Fílµ³iÔΦQW6º²iÔ•M£®lueÓ¨+›F]Ñ4j/0É4j»¦iÔvMÓ¨}„,ƒÆíš¦QÛ5M£¶kšFm÷•'L£¶šFm74ÚnhµÝÐ4j»¹¼w?Ö Ï>]7¼÷1w“·ŸN¾÷1ÉÿÏõßoÞß¼{»úÍÍû½•ù?Ÿþ@Uí)„Ì*?•Fƒ¯ÒhðU ¾J£ÁWi4ø*_¥Ñà«4üT ¾J£ÁWi4ø*_¥Ñà«4|•F‚ŸJ£ÁWi4ø*_¥Ñà«4|•Fƒ¯ÒHðSiDø„ÈŸá"#|Bd„OˆŒð ‘ ¾J#Â'DFø„ÈŸá"#|Bd„OˆlðU>!2Â'DFø„ÈŸá"#|Bdƒ¯Òˆð ‘>!2Â'DFø„ÈŸá"|•F„OˆŒð ‘>!2Â'DFø(¥á£4|•F„ÒˆðQ>J#ÂGiDø(¥Ñà«4"|”F„ÒˆðQ>J#ÂGiDø(_¥ámU¥MoÓ¨*hz›FUi4ÓWiDx›FUiDÓÛ4ªJ#šÞ¦QUÍôUÞ¦QUÑô6ªÒˆ¦·iT•F2ýTž¦QSiDÓÓ4j*hzšFM¥ÑL_¥ái5•F4=M£¦Òˆ¦¿SÑçŸîÞ)gg§+§ŸN¾SõòÕ_ÿ|ûî§·ß}úCœ/_$Ûcó2ãùRk(üÅâÆ3üòEÂðK­ÁðË ÃçÆ¡GÞ¡OðΩe‹ð±l ¾–-ÂDzEøX¶ËácÙ"|,[„e‹ð±l ¾–-ÂDzEøX¶ËácÙ"|,[„ekðµl>–-ÂDzEøX¶ËácÙ"|,[ƒ¯e«ð£×PøaÙ*üè5~X¶ ?z …–-ÂDzUøÑk(ü°l~ô ?,[…½†ÂËácÙ*üè5~X¶ ?z …–­Â^Cá‡e‹ð±l~ô ?,[…½†ÂËVáG¯¡ðòEøX¶ ?z …–­Â^Cá‡e«ðòUøaÙ*ü°l>–­ÂËVá‡e«ðòUøaÙ*ü°l~X¶ËVá‡e«ðòUøaÙ*ü°l~X¶ ?,[„e«ð6Še«¦·iT,[5½M£bÙ¢écÙ*¼M£bÙªémËVMoÓ¨X¶húX¶ oÓ¨X¶jz›FŲUÓÛ4*–­™¾–­ÂÓ4ª–­šž¦QµlÕô4ªe‹¦e«ð4ªe«¦§iT-[5ýÞ²=‚¾øtQõβ=_ïOoÿßÇÖ?Á-Iï,Û#äß¼¼ýî痷ן>úùò¾=ö(O`Jž/…Ã/_á ¿J ¿ùÈ+üÏ}üfƒ¯ßŒðñ›>~3ÂÇoFøøÍ¿|…«÷ýEüf3}ýf„ߌðñ›>~3ÂÇoFøøÍ¿áã7|ýf„ߌðñ›>~3ÂÇoFøøÍ¿Ùàë7#|üf„ߌðñ›>~3ÂÇoFøøÍ_¿YáG£¤ðÃoVøÑ()üð›~4J ?üf„߬ð£QRøá7+üh”~øÍ ?%…~3ÂÇoVøÑ()üð›~4J ?üf…’¿áã7+üh”~øÍ ?%…~³ÂFIá‡ßŒðñ›~4J ?üf…’¿Yá‡ß¬ðÃoVøá7#|üf…~³Â¿Yá‡ß¬ðÃoVøá7+üð›>~³Â¿Yá‡ß¬ðÃoVøá7+üð›~øÍ¿Yám¿YMoÓ¨øÍjz›FÅoFÓÇoVx›FÅoVÓÛ4*~³šÞ¦Qñ›Ñôñ›Þ¦Qñ›Õô6Šß¬¦·iTüf3}ýf…§iTýf5=M£ê7«éiU¿M¿YáiU¿YMOÓ¨úÍjú½ß|ý~óùž|¾;ð›O6Ÿ. ïãÑcì/ÿŸÎžróáðO!ØVn6øÊÍ_¹Ùà+7|åf‚Ÿr³ÁWn6øÊÍ_¹Ùà+7|åfƒ¯ÜLðSn6øÊÍ_¹Ùà+7|åfƒ¯Ülð•› ¾r3ÁO¹Ùà+7|åfƒ¯Ülð•› ¾r³ÁWn&ø)7|åfƒ¯Ülð•› ¾r³ÁWn6øÊÍ?åf„O„ð‘›>uÂGnFøÔI¹Ùà+7#|ê$„ÜŒð©“>r3§NBøÈÍ_¹áS'!|äf„O„ð‘›>uÂGn6øÊÍŸ: á#7#|ê$„ÜŒð©“>r³ÁWnFøÔI¹áS'!|äf„ÜŒð‘›>r³ÁWnFøÈ͹á#7#|äf„܌𑛠¾r3ÂGnFøÈ͹á#7#|äf„Ülð•›Þ¦Q•›Ñô6ªÜŒ¦·iTåf3}åf„·iTåf4½M£*7£émU¹ÙL_¹ámU¹MoÓ¨ÊÍhz›FUn&ÓO¹ái5åf4=M£¦ÜŒ¦§iÔ”›Íô•›ž¦QSnFÓÓ4jÊÍhú;¹ù}ùé’ðv/7_l.åæí§³ïäæ#ì?\¯¾½¾½yùzõü‹ß­¾ÜßÍyõ§Ï¶ß>ÿÝŸ~1ôÙV[­O¶{Âþ×êî÷çó›Ýêòî+d½¿éô«7«_½Ø‹ß˳ÿçU¼‚呟}ñõæd½ÚßúûÕg¶žñõ öøgÝñúIðc'§ðÃ+Pø±“Søá(üØÉ)üð >^œÂ¯@áÇNNá‡W ðc'§ðÃ+@øx ?vr ?¼…;9…^œÂ¯áã(üØÉ)üð ~ìä~x ?vr ?¼„W ðc'§ðÃ+Pø±“Søá(üð ~x ?¼„W ðÃ+Pøá(üð ~x ?¼…^ÂÇ+Pøá(üð ~x ?¼…^¯áã(¼M£â¨ém¯@MoÓ¨xhúx oÓ¨xjz›FÅ+PÓÛ4*^š>^ÂÛ4*^šÞ¦Qñ Ôô6ŠW`¦¯W ð4ªW ¦§iT½5=M£ê éã(JÂg‡ðQ >›8„R`ðU >›8„R€ðÙÄ!|”„Ï&á£|•„Ï&ᣠ|6q¥á³‰Cø(_¥á³‰Cø(ŸMÂG)@ølâ>JÁW)@ølâ>JÂG)@ø(¥á£ |”ƒ¯R€ðQ >JÂG)@ø(¥á£|•„R€ðQ >JÂG)@ø(¥Àà« ¼M£ª émU¥MoÓ¨*fú*oÓ¨*hz›FU)@ÓÛ4ªJ™¾JÂÛ4ªJšÞ¦QU Ðô6ªR@¦ŸJÂÓ4j*hzšFM¥MOÓ¨©˜é« x¸góÂþG]ç¿YŸ¬Ï÷OÕÃÇ|öd×ù_åä‰=þcãÑÆÂ¡Ûq•“'>'O |t„ÎðÑ9 ¾:‡Â ´ÂCáÇZá‡Î¡ðc­ðCç@øè ?6Ð ?t…h…:‡Â ´Âá£s(üØ@+üÐ9~l ~è ?6Ð ?t„Ρðc­ðCçPø±Vø¡s(üØ@+üÐ9>:‡Â ´ÂCá‡Î¡ðCçPø¡s(üÐ9~èCá‡Î¡ðCçPø¡s(üÐ9~è ?t„ΡðCçPø¡s(üÐ9~è ?t…:ÂGçPx›FEçPÓÛ4*:‡šÞ¦QÑ9ÐôÑ9Þ¦QÑ9Ôô6ŠÎ¡¦·iTt4}t…·iTt5½M£¢s¨émÃL_CáiUCMOÓ¨êjzšFUç@ÓGçPxšFUçPÓ/ŽÂÇÁæÑ,Z÷:Çn÷1úìäü_ñ/ž£p¯s²ïtŽ÷7o~zýòÃõw«îÎnøîæý_ûx‡úÈ4"v»ý”G Gð5" ¾F„Á׈0ø?„ÏácD |ö #á³Eø_#á³EøŸ=(ÂLj@øìA>F„Á׈@øìA>FÂgŠð1">{P„að5">{P„ðÙƒ"|Œ„ÏácD|„ÏácD |Œ„ð1">FÂLj0ø#ácD |Œ„ð1">F„Á׈@ø#ácD |Œ„ð1" ¾FÂÛ4ªFšÞ¦Q5"Ðô6ªa¦¯ð6ª¦·iT4½M£jD˜ékD ¼M£jD émU#MoÓ¨dúiD 6ÂÇæ0øÚ ?öÏ ?l…ûg…6‡Âý³Â›ács(üØ?+ü°9~ìŸ~Ø ?öÏ ?l„Í¡ðcÿ¬ðÃæPø±Vøas(üØ?+ü°9>6‡Âý³Â›CáÇþYá‡Í¡ðcÿ¬ðÃæ@øØ ?öÏ ?l…6‡Â›Cá‡Í¡ðÃæPøas |l…6‡Â›Cá‡Í¡ðÃæPøas(ü°9>6‡Â›Cá‡Í¡ðÃæPøas(ü°9~Ø›Cám›CMoÓ¨Øjz›FÅæ@ÓÇæPx›FÅæPÓÛ4*6‡šÞ¦Q±9Ðô±9Þ¦Q±9Ôô6ŠÍ¡¦·iTl3}m…§iTm5=M£js¨éiU›M›CáiU›CM¿·9Ž 7§Ÿn\ì?VWëÓmŽGßÝâÐ9=Nþöå›__¯¾}÷ý‡Ÿ_Þ^úOp±Ä¤Ûc?ÆX—ëqÊ‘gé)ðË“M¿Ä¤ ¿Ä¤ ¿Ä¤ ¿Ä¤ ¿Ä¤ ¿Ä¤ ¿Ûl—ÃÌû~·Ä¤lú%&eø%&eø%&eø%&eø%&Uø«X{æÙS©>ÖÂÇÚCøX{kácí|­=…9©ÂkOáGNªðÃÚSø‘“*ü°ö>֞œTᇵ§ð#'Uøaí)üÈI~X{kOáGNªðÃÚSø‘“*ü°ö~ä¤ ?¬=„µ§ð#'Uøaí)üÈI~X{ ?rR…ÖÂÇÚSø‘“*ü°ö~X{ ?¬=…ÖžÂkOᇵ‡ð±ö~X{ ?¬=…ÖžÂkOᇵ§ðÃÚCøX{ ?¬=…ÖžÂkOᇵ§ðÃÚSøaí!|¬=…·iT¬=5½M£bí©émkMkOámkOMoÓ¨X{jz›FÅÚCÓÇÚSx›FÅÚSÓÛ4*ÖžšÞ¦Q±öÌôµöž¦QµöÔô4ªµ§¦§iT­=4}¬=…§iT­=5ýÞÚ;‚~ko¦«‹Íµw²ùtŸîîLGØ_þþ‹oV¼¾}óîíj{ò裵ÁË L‡?ɘuS`2ø L_Éà+0|&ƒ¯Àdð˜~ LŸÛŽùKü­zˆÂW`2ON&ƒ¯Àdð˜~ LŸÈá#0!|"#„À„ð‰Œ>“ÁW`BøDF á!|&„Od„𘠾Â'2BøLŸÈá#0!|"#„Àdð˜>‘ÂG`BøDF á!|&ƒ¯À„ð‰Œ>ÂG`BøL á#0!|&ƒ¯À„ð˜>ÂG`BøL á#0|&„À„ð˜>ÂG`BøLÉà+0!¼M£*0¡émU MoÓ¨ Lfú LoÓ¨ Lhz›FU`BÓÛ4ª“™¾ÂÛ4ªšÞ¦Q˜Ðô6ªÀD¦ŸÂÓ4j LhzšFM MOÓ¨)0™é+0!ü°ú¯—oÿzýÝêÍõ›w·ÿó‹g/þ²úõ‹ÕV[­O¶{Îþ×êî÷çóû¡/ï>Œ×ëÍêÕ›Õ¯^¬ö?Ëýs÷3mG¥r÷øÏ^¼Y}ñõædÿ¯W/¾_}öhûâ|‹1þ(U|1þ¨U?½ ã¯2ÆÍ ã/3ÆÝ ã¯3ÆíŠâ§^aüñÆø£`aüѰ0þ¨Xt,Œ?JÆ-‹â§faüѳ0þ(Z4-Œ?ªÆ] ã²EñÓ¶0þ¨[ô-Œ? Æ ãÊ…ñGç¢ø)]´.Œ?$`ƽ ã ˜ñGóÂøCVüt/Œ?ÊÆ.0ãú…ñ‡ Ìø£€aüá+~*Æ ããüê çW©aØóƒó«1hþ61Œoó«v1l~›_µaóÛüª}Œš?… ãÛüª• ›ßÉco.[Ê– K)syñqoq~òèƒfáK)sÿæîºòçϵ1«Çµ1‡W˜oÖ]­_^|\5=º9Æïjñ»ZGü®Ö ?×ímÖˆßÕ:âwµŽø]­#~WëˆßÕ:âwµnø¹|O½?·]­£ù»ZGü®Ö¿m3âwµŽøm› ¶ÍˆßÕ:â·mFü®Ö¿m3âwµŽøm› ¶ÍˆßÕ:â·mFü¶Íˆß¶ñÛ6#~ÛfÄoÛlø³mFü¶Íˆß¶ñÛ6#~ÛfÄoÛŒøm› ¶Íˆß¶ñÛ6#~ÛfÄoÛŒøm›¿m³á϶ñÛ6#~ÛfÄÇùU.óSûßKœ_åB?5ÿl›Íë»ÃùU®õcÏίrµ›çW¹ÞOÍ?Ûfóþ¼ÂùU.ùcÏίfÛŒžœ_Ͷ™Ìÿ mF|›_=h›Ñü6¿zÐ6£ùm~õ m6ó϶ñm~õ mFó߷͇ìÍÕcÛæÃòô¾mÞœž~ÜËžŸ<Áõ…÷móøþÀßþúŧ¾ÿû.þȃ8 þùàtü%øwüåƒÓñ—àŸñ/×kúþ¼\>8ÝüKðïøKðïøKðïøKðïøKðïø9[}~öp ÅÏ銟ã%?çK(~˜Püœ0¡ø9bñ{Æ„âç Å_‚öùp•c&Ôü9gBñsЄâç¤ ÄïQŒŸä߬Ÿ7ë$ÿŠŸä_ñc®*~’Źи5W?É¿âÇ\Uü$ÿŠsUñ“ü+~ÌUį¹ªøIþ?æªâ'ùWü˜«ŠŸä_ñc®"~ÍUÅOò¯ø1W?É¿âÇ\Uü$ÿŠsñk®*~’Źªø1W?æªâÇ\Uü˜«Šsñk®*~ÌUŹªø1W?æªâÇ\Uü˜«ˆ_sUñc®*~ÌUŹªø1W?æªâÇ\Eüš«Šó«O¡ò·Kœ_Õ\UÏ?ίj®¢ùk®*>ίj®ªùq~UsUÍó«š«hþš«Šó«š«j~œ_Õ\Uóãüªæª™š«Šoó«i®ªùm~5ÍU5¿Í¯¦¹Šæ¯¹ªø6¿šæªšÿ¸“Û'8_b1W¯ö‡:üïóÎOÎ?Ý,]ÌÕCø—¿ÿíï¾ýtüqõð1žV\5ü)®þW Š«†?ÅUßâªáOq•ðˆ«†?ÅUßâªáOqÕð§¸jøS\5ü)®þW ÿ¸jøS\5ü)®þW Š«†?ÅUßâ*á?W Š«†?ÅUßâªáOqÕð§¸jøS\%üâ*â7øGüŠ«ˆßàñ+®"~ƒį¸jøS\Eüÿˆ_qñü#~ÅUÄoðøW Š«ˆßàñ+®"~ƒį¸Šø þ¿âªáOqñü#~ÅUÄoðøW¿Á?âW\5ü)®"~ƒį¸ŠøW¿â*âW\EüŠ«ˆ_qÕ𧸊øW¿â*âW\EüŠ«ˆ_qñ+®þW¿â*âW\EüŠ«ˆ_qñ+®"~ÅUßâ*âãüjŠ«h~œ_MqÍó«)®šù§¸Šø8¿šâ*šçWS\EóãüjŠ«fþ)®">ί¦¸ŠæÇùÕWÑü8¿šâ*™ÿ¸Šø6¿z ®¢ùm~õ@\EóÛüê¸jæŸâ*âÛü긊æ¿WÙÛGŸÁùn¹º=ÛŠ«Ÿn–Þ‹«Gàû[®~³zùã¯o^½üpóîíêý»ï?üüòöz<ä³?¬þ¶ZŸl÷ÿÿý¯ÕÝïÏç7»ÕåÝ'òz½9–ÝËá·Pü|®*~zÅÏçªâ§Pü|®*~zÄo/ øù\UüôŠŸ^@ñÓ (~zÅO/ øé¿½€â§PüôŠŸ^@ñÓ (~zÅO/€øí?½€â§PüôŠŸ^@ñÓ (~zÄo/ øé?^«â§Püx­ŠŸ^@ñãµ"~{ÅO/ øñZ?½€âÇkUüôНñÛ (~zÅתøé?^«â§P|›_Í^ÀÌ¿]Ûüjöj~›_Í^@Íoó«Ù  ùÛ (¾Í¯f/ æ¿ëް·OpÞÒ \œ~œ›ŸŸì>=™_zCø/_xys»úêwß¶ Ø_©÷àñžÍKþuàf}²>¿¶<è³'»𪷋>½8ý¸IytÓpØ£\õvшßÛE#~oø½]4â÷ RßW*~wÚäý¹Yw§øÝi#~wÚˆß6âw§mø½‚½?7Ýi£ù»ÓFüî´¿;mÄïNñ»Ó6ü^AŠÞŸÛî´ÑüÝi#~wÚˆß6âw§øÝiþ4E¿;mį)‚øÝi#~MÄïNñkŠþ4E¿;mį)‚ø5E¿¦â×Aüš"ˆ_SÄð§)‚ø5E¿¦â×Aüš"ˆ_SñkŠþ4E¿¦â×Aüš"ˆ_SñkŠ ~MߦâãüªW¢ýã%ίz©šçW½‚Í?MóþÜáüªWªççW½‚TÍó«^AŠæŸ¦ˆy^áüªWªççW½‚TÍó«^Ajæ`Š÷çSñm~µŸ÷7Ÿ=?Û·ñh~›_m×6¿z`Š˜çgš"ˆo󫦚¸» 7Eå„{Säl»ù˜}~rõÀÜøïÝ|oŠSäW/ßß¼ZžlWúìì¯úÅcðð¢ÎÝ’¤ìÅšý£~,¿Î¯ª©ùq~UHÍó«ª@hþª@Šó«ª@j~œ_URóãüª*š¿*âãüª*šçWUÔü8¿ª dæŸ*âÛüjÞ4FÍoó«yÓ5¿Í¯¦ „æ¯ ¤ø6¿š*šÿN:ÂÞ>ú~ï‡rË¢]99~óð..ÿ” tH_\ Cúpv]Ýë@Ÿî].âÝótøhOà].¢ã/¢ã/¢ã/¢ã/"ãï6Ûq»!óþÙ-¢›:päý¿›œáOÎð§gøÓ#üœá÷vXæý?o‡…øÓ3ÏÏtà :p†?8ÂàÀ!~3Dį‡øÍ¿â7CDü:p†?8Äo†ˆøuà¿"â×Cüfˆˆ_Îð§‡øÍ¿â7CDü:pˆß ñëÀþtà¿"â×Cüfˆˆ_ñ›!"~8ßâ7CDü:pˆ_ñëÀ!~8ᇸuà :pˆ_ñëÀ!~8ᇸu࿜áOñëÀ!~8ᇸuà¿â×3üéÀ!>ί¦‡æÇùÕtàÐü8¿šœ™:pˆó«éÀ¡ùq~584?ί¦gæŸâãüj:ph~œ_MÍó«éÀ‘ù8pˆoó«šßæW84¿Í¯8pfþéÀ!¾Í¯8phþ{îýdÜùéÅÇ÷Ž:?Ùl>]M»wàŽÐÿ×ý°ö÷¢ûôGš’Ú‘‡{ ®’âWRCüJjˆ_IÍ𧤆ø•Ô¿’âWRCüJjˆ_I ñ+©þ”Ô¿’â÷Fmˆ_I ñ+©!~%5į¤føSRSü„|ŠIMñò)~$5ÅOȧø‘Ô¿’šâ'äSüHjŠŸOñ#©)~B>Ť†ø•Ô?!ŸâGRSü„|ŠIMñò)~$5ᤦø ù?’šâ'äSüHjŠŸOñ#©!~%5ÅOȧø‘Ô?’šâGRSüHjŠIMñ#©!~%5Ť¦ø‘Ô?’šâGRSüHjŠI ñ+©)~$5Ť¦ø‘Ô?’šâGRSüHjˆ_IMñq~UIMÍó«Jjj~œ_URCóWRS|œ_URSóãüª’ššçW•ÔÐü•ÔçW•ÔÔü8¿ª¤¦æÇùU%53ÿ”ÔßæWSRSóÛüjJjj~›_MI Í_IMñm~5%55ÿ¤v„½Ý}ºÜu£¶ó«Ó#’ÚöÓ鋤vH’ÚW7ïÿºúõ¸~ûÝõwË=ÛV¼¾}óîíêt½þüüó/ñìÅ_V¿~±úÃêoÿÔÍâO¥Üœö¯ÇÕéÇGS>Z_;Æo¼kø3ÞEüþõ@üÆ»ˆßxñï"~ã]Äo¼‹øw Æ»ˆßxñï"~ã]Äo¼‹øw¿ñ®áÏxñï"~ã]Äo¼‹øw¿ñ.â7Þ5üï"~ã]įžˆøw¿z"â7ÞEüꉆ?ã]Äo¼‹øÕ¿ñ.âWODüÆ»ˆ_=Ñðg¼‹øw¿z"â7ÞEüꉈßxñ«'þƒxñï"~õDÄo¼‹øÕßæWâ]3ÿŒwßæWâ]4ÿ}¼{È~|¼{NÞÇ»ç›sÏý5ȧwú³óõêî÷çûÞÇ»{úÇáñ?ïžï¯Lþôhw›¯†#?âSD»Û|5(~¾¿æºâç«AñóÕ øùjPü|5(~¾?Õâ·ÚPü|5(~ª ÅOµ¡ø©6?Õ†â§ÚPüTˆßjCñSm(~ª ÅOµ¡ø©6?Õ†â§Ú@üVŠŸjCñSm(~ª ÅOµ¡ø©6?Õâ·ÚPüTŠŸjCñSm(~ª ÅOµ¡ø©6¿Õ†â§ÚPüTŠŸjCñSm(~ª ÅOµø­6?Õ†â§ÚPüTŠó«+œ_Õ\7ÏϬ6ßæWÓ\WóÛüjšëj~›_ÍjÍßjCñm~5« 5ÿ]µq„½½ú×ʇՃòá¾ÚØoŽTgÿý ÚØÓÿAµñãí»?ß¾|óææíŸW¯_¾ýóO/ÿ|ý~õÇÿþùvõòíw«ožÿöwÿþÇY¨#?èS  ?”â'€RüPŠŸJñ@)~(Äo¥ø  ?”â'€RüPŠŸJñ@!þ{²>?[m?”â'€RüPŠŸJñ@)~(Äo¥ø  ?”â'€RüPŠŸJñ@!~(ÅO¥ø  ?”â'€RüPŠÊðg¥ø  ?•â'€RüTŠŸJñ#P!~(ÅO¥ø¨ÿ.€:Â~ºêââHuþTÔÅÁéAÿñå·_­~ÿòý«—¯WÏŸ?Éù>KÆt÷D>Ü£C¦g¯Þ¬~õâaP7Ï—Aü%cró/“ã/“ã/“ã/“ã/ãÏóeÌûójɘÜüKÆäøKÆäøKÆäøKÆäøKÆÄøûïø„Läóy³nÈ„ø ™¿!â7dBü†LˆßÉð{¾ŒùüÜl2¡ù2!~C&ÄoÈ„ø ™¿!“á÷|ôþÜ6dBó7dBü†Lˆß ñ2!~C&ÃïUzèýyÚ Íß ñ2!~C&ÄoÈ„ø ™ ¿Wé¡÷çYC&4C&ÄÏUzêùÉUzŠŸ«ô?Wé)~%)óúNI ñ+I!~%)į$…ø•¤¿’âW’2ü)I!~%)į$…ø•¤¿’âW’BüJR†¿ï_†$…ø8¿êù2èûëçW=_FÍó«ž/ƒæŸ’”yîp~ÕóeÔóƒó«ž/£æÇùUÏ—AóOIʼ?¯p~ÕóeÔóƒó«ž/£æÇùU¯Ò3ó?¤Èûs»¶ùÕ¼JO=?6¿šWé©ùm~5¯ÒCóOIʼ?76¿šWé©çç^’:|nN×Õ˜åŸý‡Ãj³Ûœ/sñéèý 1GÑßþxýêfoG½¿yóÓë—ÞÝ®¾¿~ùá§Ûë÷ã1Ÿýë‡Éìêº,?Ó³/¾ÞœìÇX½ø~õÙ£]©Ãû5îêºìù‹eOÀŸ® â×uAüº.ˆ_×ñëº ~]įëbøÓuQüdŠ×Eñ“(~\ÅOV øq]¿®‹â'+Pü¸.ŠŸ¬@ñãº(~²Åë‚øu]?YâÇuQüdŠ×Eñ“(~\Ä¯ë¢øÉ ?®‹â'+Pü¸.ŠŸ¬@ñ㺠~]ÅOV øq]?®‹âÇuQü¸.Š×Eñ㺠~]Åë¢øq]?®‹âÇuQü¸.Š×ñëº(~\Åë¢øq]?®‹âÇuQü¸.ˆ_×Eñq~U×EÍó«º.j~œ_ÕuAó×uQ|œ_ÕuQóãüª®‹šçWu]Ðüu]çWu]Ôü8¿ªë¢æÇùU]3ÿt]ßæWÓuQóÛüjº.j~›_M×Í_×Eñm~5]5ÿër„ýx×åPÞ¸¿#õnwpØæÅÉæ±¶Ë!üþ¬Í#ðo®ß¼»ýŸÕËW¯®ß¿_ý×íõË¿þøîæí‡÷«?}¶[ïÖ_üçn½z÷öõÿüéâÐæÙÌxtwpêç£õ”cü¾½¿ñ(â7EüÆ£ˆßxñ"~ãQßñ(â7EüÆ£ˆßxñ"~ãQÄoÄo<ŠøÕû¿ñ(âWï3ü"~ãQįއøG¿zâ7Eüê}„ÿ EüÆ£ˆ_½ñ"~õ>Äo<ŠøÕû Æ£ˆoó«ñ(šÿ>=d?>= ÷îãѫӃ#õ.N¶ŸžLÞÇ£GàÏß¾ÿpûÓ«7ïÞþƒpô‹}Jz±GŸ½øËê×/Vÿúµ›m>\÷s€k÷6u§?®ŠŸWÅχ«âçÃUñóáªøùpEü–ŠŸWÅO9 ø)?å€â§Pü”ŠŸrñ[(~ÊÅO9 ø)?å€â§Pü”ˆßr@ñS(~ÊÅO9 ø)?å€â§@ü–ŠŸr@ñS(~ÊÅO9 ø)?åâ·Pü”ŠŸr@ñS(~ÊÅO9 ø)¿å€â§Pü”ŠŸr@ñS(~ÊÅ·ùÕ,Ìüó>Šoó«ý­;Ζû$«ùm~µ]ÛüjºÓèùi9 ø6¿šå€šÿ®8Â>}½ùþ>WW‡©^>¶84§ïïxý«Û›ëïWïzóæå^ ~÷ýêåë׫7/ÿ²¿aà«^¾ýóõûÕ‡w«?\¯ÞÝÞüùæíþ–‚¿|ýáåÍí¼³à”½ù÷‘ mTþðû3]ó¡røä> ¿‹zÄï¢Þðç¢ñ»¨Gü.ê¿‹zÄï¢ñ»¨Gü.ê .ê¿‹zÄï¢ñ»¨Gü.ê¿‹zÄð,ê¿¥4âwQø]Ô#~õˆßE=â·”6ü¹¨Gü.ê¿¥4âß/êÙOµ¨ß_±{p#ëÝÓ,ê¡ùã×o¿»ùïÿ{õûÿùðÃ^úyÿêöæÇ«ïïVõïÞþýúöÃÍÛ?¯¾ùÕ¿¯¾¿y½,ñ|ýòfoíWøû}Àý¿}ŠuýX·ò)ÖÝY·2þX·2þX·2þX·2þX·2þX·2þX·*~Ö­Œ?Ö­Œ?Ö­Œ?Ö­Œ?Ö­Œ?Ö­Œ?Ö­ˆßu+ãu+ãu+ãu+ãu+ãu+ãu«âgÝÊøcÝÊøcÝÊøûuë1öé£eòÃÐô.ŒÞn67ò¸z¢uëô\·þæÝÏw¹óÝÞ­Súqõ_~ûÕê÷/ß¿ÚçÏÏŸï›Ù/j¿}þÍo:˳¿­Ö'Û½|»ÿµºûýùüãf·º¼û ^¯7‡?ææl‘w?ëêÅ›Õýa6Ïžì0›M„ÈcÏåS¬Ï‡ÉøCˆdü!D2þ"‘Œ?„HÅÉøCˆdü!D2þ"‘Œ?„HÆB¤âGˆdü!D2þ"‘Œ?ºSÆÝ)ã'ƒ:²àxŠïßfPŠŸ Jñ“A)~2(ÅO¥øÉ ?â7ƒRüdPŠŸ Jñ“A)~2(ÅO¥øÉ  fPŠŸ Jñ“A)~2(ÅO¥øÉ ?â7ƒRüdPŠŸ Jñï2¨#ìÓÓ†3«5œY2¨‹ƒ l}(óaðsgDn7‡ì?^¿º;.ùßn_þøÃÍ«ÿkõüí«“Õ·Ë1Êû(<ö‡:¼ÄÕ²aº{Ö.îñèé!ÿ†ÝÝ+Ԍߚ¿;&ÄïŽ ñ»cBü;&ÃÏñ±êý¹éŽ ÍßâwÇ„øÝ1!~wLˆß“áçøXõþÜvÇ„æïŽ ñ»cBü;&ÄïŽÉðs $õþ<íŽ ÍßâwÇ„øÝ1!~wLˆß“áçHêýyÖš¿;&Ä·@bÏϸã[ 1þ¸ã·ñ7¯ïlü¿?â·ñGü6þˆßÆñÛø#~ß?â·ñGü6þˆßÆñÛø#~Äoãoø³ñG|œ_åøXõý•«¥çW—8¿Êñ±êù™¿yîp~•ãcÙóƒó«ËæÇùUŽUóÏÆß¼?¯p~•ãcÙóƒó«ËæÇùUŽEó?hüÉû³·@bóÛüª·@bóÛüª·@bóÛüj;óþÜØüª·@RÏÿæ¾ñ?|nÎ} ¤Ãòúî|„ív{Ñ+1VË•›õãÏ=¤ßpŒþ»ý%Ñ¿¹þùÓËý‹%;ß—ûG~ˆ'(÷/–ÏÇ_²sÇ_>{ÉÎùìaüËõúÎÝpüå³Çñ—ìÜñ—ìÜñ—ìÜñ—ìÜñ—ìÜñ—ìœñw›-}ÿï–ìÜÍ¿d玿d玿d玿d玿d猵_ˆÈÏç«%;wó/Ù¹ã/Ù¹ã/Ù¹ã/Ù¹ãWþ4ëÃ)*~ÂsÅü©ø‘??á¹â'Å¡¦øIø?†â×PSü$|ŠCMñ“ð)~ 5ÅO§ø1Ô¿†šâ'áSüjŠŸ„Oñc¨)~>Å¡†ø5Ô? ŸâÇPSü$|ŠŸ„Oñc¨)~>ᡦø1Ô? ŸâÇPSüjŠCMñc¨)~ 5ᡦø1Ô?†šâÇPSüjŠCMñc¨!~ 5Å¡¦ø1Ô?†šâÇPSüjŠC ñk¨)>ίj¨©ùq~UCMÍó«jhþjŠó«jj~œ_ÕPSóãüª†š¿†šâãüª†ššçW5ÔÔü8¿ª¡f柆šâÛüjjj~›_MCMÍoó«íÚæWÓPCÏO 5Å·ùÕb¨™ýì ²ÅP»Ú1ÔÎ>ýæf÷èžÒj?ß|øaõ›¯>ÿú«Õ—ïÞ~¸}÷úõõí/ž½øËê×/VXýmµ>Ùî1û_«»ßŸÏ?nv«Ë»Oæõz³zõfõ««ýaÀ÷ÿÍþŸ›MK·«Ý³ô»GKkÇø]´ ~K7ߥâwÑ‚ø-Ý¿‹Äo醸]´ ~K7ߥâwÑ‚ø-Ý¿‹Äo醸]´ ~-†?K7Äo醸]´ ~K7Äo醸-Ý¿¥â·t3üYº!~K7Äo醸-Ý¿¥â·tCü–n†?K7Äo醸-Ý¿¥â·tCü–nˆßÒÍðg醸-Ý¿Ò8â·tCüJãˆßÒ ñ+þ,Ý¿¥âWGü–nˆó«ίféfžŸYº!>ίf醿ÇùÕ,ÝÐü8¿š¥™ÿA醸6¿zPº¡ùm~õ tCóÛüêAéfæß_µx²>?[m÷õ èG¶³tC|›_ÒípöÇ—n‡åÑ}évv~ññó¾YŸœ?¶t[èÏT_÷¥Ûžþq¥÷Ûw®ß¯Þ½]}{óæ§×/?\·úÍËÛï~~y{ýàAŸÍ‚íQ}Û:ë݇ülç‹ïWŸ=Eß¶Îz÷Èóöü^$©øYï*~Ö»ŠŸõ®âg½«øYï*~úZÄo_«øYï*~úZÅÏzWñÓ×*~Ö»ŠŸ¾ñÛ×*~Ö»ŠŸ¾Vñ³ÞUüôµŠŸõ®â§¯EüöµŠŸ¾Vñ³ÞUüôµŠŸ¾VñÓ×*~úZÅO_‹øík?}­â§¯UüôµŠŸ¾VñÓ×*~úZÄo_«øék?}­â§¯UüôµŠŸ¾VñÓ×"~ûZÅO_«øék?}­â§¯UüùÕǹÜSäK½HÍß¾Vñq~Õ‹$Õü8¿êE’j~œ_µ¯Eó·¯U|œ_µ¯Uóãüª}­šçWíkÍü³¯U|›_;VÍoó«Ùתùm~5ûZ4ûZÅ·ùÕvc󫥯=òÜœ=ú2ÆÐמoN?^îûÚ‹Õé#.U<èk÷ôØ×~øázõÇëWÞÝ®þíöå?ܼZ}uóþ¯«ço^þùzõ§ÏþøoÏÿô‹Õ×7¯¯W_¿»}óòÃ\>ÙíΑûI–ÃÙî(~¶;ŠŸíŽâg»£øÙî(~êzÄïvGñ³ÝQüÔõŠŸíŽâ§®Wülw?u=âw»£øÙî(~êzÅÏvGñS×+~¶;ŠŸºÞðçvGñ³ÝQüÔõŠŸíŽâ§®Wülw?u=âw»£øÙî(~¶;ж]m°Ÿd»³¿×Êöürs°ÝÙük›‡÷e9ýìçß|ûËç«Ýz·Žžzóîícîðˆ´y¶Õþçùxƒõè}Ì~϶Büžm…ø=Û ñ{¶â÷l+ÄïÙV†?϶Büžm…ø=Û ñ{¶â÷l+ÄïÙVˆß³­ žm¥øÍÈçófÝñ›!~s Äo„øÍ ¿—m ÷ç¦9š¿9â7Büæ@ˆßñ›~/Û@ïÏms 4s Äo„øÍ¿9â72ü^¶ÞŸ§ÍÐüÍ¿9â7Büæ@ˆßÈð{Ùzž5Bó7Bü\¶¡žŸ\¶¡ø¹lCñsÙ†âç² ÄïeŠŸË6?—m(~.ÛPü\¶¡ø¹lCñsÙâ÷² ÅÏeŠŸË6?—m(~.ÛPü\¶¡ø¹lñ§Çd¾/q~Õ³­Ôóƒó«žm¥æÇùU/Û@óOɼ?w8¿êeêùÁùU/ÛPóãüª—m ù§ÇdÞŸW8¿êeêùÁùU/ÛPóãüª—m˜ùxLäý¹]Ûüj^¶¡ž›_ÍË6Ôü6¿š—m ù§ÇdÞŸ½ÍžšßæWËeGf?{ôðåœûÛì]l϶ڜ<Úd:¤ßßfïýw¿¾ýûÍõÏŸ./],Ÿ={ÑëÈÃ<¼t±d玿|ö8þ’;þòÙãøKvÎø—Ë->ÉÎùìqü%;wü%;wü%;wü%;wü%;gü)šÏÏ]åOįü‰ø•?¿ò'âWþDüÊŸ†?åOįü‰ø•?¿ò'âWþDüÊŸˆ_ùÓð§ü©ø Ï?ò§â'ί*ªùq~UùSÍó«ÊŸfþ)*¾Í¯¦ü©æ·ùÕ”?Õü6¿šò'š¿ò§âÛüjÞ³[Íw»#ì§“?w§ o6'ÛOW3ùóþà6v?ß|øaõõó¯—{uùîí‡Ûw¯__ß>Á͹g¯´;8CúÑ^èáíÎ7½©Èâ·WBü~/#~{%Äï÷2â·W2üÙ+!~¿—¿½â÷{ñÛ+!~¿—¿½’áÏ^ ñû½Œøí•¿½â·WBüöJˆß^ ñÛ+þì•¿½â·WBüöJˆß^ ñÛ+!~{%ß½â·WBüöJˆß^ ñÛ+!~{%Äo¯dø³WBüöJˆ_/ñÛ+!~½hÄo¯„øõ¢ öJˆß^ ñëE#~{%įøí•¿^´áÏ^ ñÛ+!>ί®p~5{%ôüàüjöJdþ½âÛüêA¯„æ·ùÕƒ^ Íoó«½’™öJˆo󫽚ÿ¾W:d?¾W:,Gö«íåÙáY°n•Ù÷‡#aÿöÝí¾KúöÃËÛÕoÞÝÞüïÞ>é!IW½vÿØà¤«^‹ø½Öðçu°ŠŸýžâg¿§øÙï)~ö{ŠŸýžâg¿‡ø½Vñ³ßSüì÷?û=ÅÏ~Oñ³ßSüì÷¿¾‚âg¿§øÙï)~ö{Š_Añ³ßSüø ˆ__Añ³ßSüø ŠŸýžâÇWPüì÷?¾â×WPüì÷?¾‚âÇWPüø Š_Añã+(~|ᝠøñ?¾‚âÇWPüø Š_Añã+ ~}ů øñ?¾‚âÇWPüø Š_ñë+(>ίz¬šçW½VÍó«^‹æ¯¯ ø8¿êu°j~œ_õ:X5?ίz,š¿¾‚âãüª×Áªùq~U_AÍó«ú fþé+(¾Í¯¦¯ æ·ùÕôÔü6¿š¾š¿¾‚âÛüjú jþ;_áûìâÓ¯T½?e·Þ|Üëo¶Ovʺ8åÈÃ<úb×Ãc\æ!(ˆ¿|öì_`Äï!(ˆ¿|ö¸ù{Š™‚‚øËg{~.{ š¿‡  ~AAü‚‚ø=ñ{ŠáÏCP¿‡  ~AAü‚‚ø=ñ{ â÷߇  ~AAü‚‚ø=ñ+"~åOįüiøSþTü„çŠùSñž+~äOÅOx®ø‘?¿ò§â'B;d?ŒÐþðÿ3º endstream endobj 14 0 obj 17416 endobj 12 0 obj << /Type /Page /Parent 3 0 R /Resources 15 0 R /Contents 13 0 R /MediaBox [0 0 612 792] >> endobj 15 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 17 0 obj << /Length 18 0 R /Filter /FlateDecode >> stream xÅÝMs›ç•&à½~–ñ >X&rRNÓN,UOÕToT6¥f"JnJr¦ÿý<À¹Ï ZÀÔ¯šd‘r:}ñå ñ>_ÿ5ûûì¿f‹íìjþt¾Ï·³õæz¶Y­žî–³û›Ùÿœ½ýþÙûÅìÇ÷³ùáßïÿó§Ë«ùþ_‡ÿêëã?i³¹z:þ/‹'?ÞÍþøb¶ZÕÿfüçrüïWó«åìÅÝì÷^<ϳ¯f¿{þ—¿~;ûÛ/oïÿ×vþÕìÅ?fzqx°#¼ÿH¿ýã¬wg?ÐjöîÕøáúêéúz¶^>]g_„íòév;>»«Ínükýd|^ýÆOwVŸî:_’ñŸûOw¹Ÿb>Û'õÙ.VÇÿÛ¾’§ôªè'Mç ùìÝÝϾ7Û~½¸ž=¿½ûøæå‡Ûwo/ý€ùÖ=ø\¶»ë§óõøÖ=ü„òQŸ~9¿[Œ?È/Ç$äW[ÊŸOùô×ö[»µßÚýÖ.ÆÛÂþ‹~îóåá{ËøÕá{Ëøõá{Ëøñö,¿öû­ÝÑoíbN¿µ‹%ýÖ.Vô[»¸¢ßÚŵýÖnì·vG¿µË9ýÖ.ô[»\ÑoíòŠ~k—×ö[»ÿ«½ûÃp¹¥ßÚÕœ~kW ú­]­è·vuE¿µ«µýÖnì·vK¿µãWiù²ºZÐoíÕ’~k¯®è·öjm¿µû­ÝÚoíŽ~k× ú­]/é·v½Ì܆ë5ýÖ®¯í·v$iò‹Sq”úÅóºâ(ÆWÅøŠ£_qã+Žb|ÅQŒ¯8Jñ›Fmlµ±iÔÆ¦Q›Fmlµ±iÔÖ¦Q[›FmmµµiÔÖ¦Q[›Fmmµ³iÔΦQ;›Fílµ³iÔΦQ;šFâ°üòrNӨ圦Q#B–¿ü,ç4ZÎiµœÓ4j9JžðW·å‚¦QËM£–‹ó-WzJ~[OÇi‹År´[¬–Û©]$Ý‹ÕÓÅ—7XŒˆñþý/7÷¿ÜÞüëË?Àu½ß,Ï}”Gh¹®÷Æ×û ãëý†ñõ~Ãøz¿Qü¦Þo_ï7Œ¯÷ÆWúÍøJ¿_é7ã+ýf|¥ßŠßVúÍøJ¿_é7ã+ýf|¥ßŒ¯ô›ñÝŒyæ¯ ðgíÔŒ‰ønÆD|7c"¾›1ß͘ˆïfLÄw3¦á§fLÅ'þV|âoÅ'þV|âoÅ'þV|âoÄw3¦â+>ñ·â+>ñ·â+>ñ7â»Sñ‰¿Ÿø[ñ‰¿Ÿø[ñ‰¿ŸøñÝŒ©øÄߊOü­øÄߊOü­øÄߊOüønÆT|âoÅ'þV|š1ŸfLŧSñiÆT|š1ß͘ŠO3¦âÓŒ©ø4c*>͘ŠO3¦âÓŒ‰ønÆT|š1ŸfLŧSñiÆT|š1ŸfLÄw3¦âmÕ͘êémÕ͘êémÕ͘èé»Sñ6êfLõô6êfLõô6êfLôôÝŒ©x›Fu3¦zz›Fu3¦zz›Fu3¦yú©Sñ4šš1ÕÓÓ4jjÆTOOÓ¨©=}7c*ž¦QS3¦zúÑŒy†~´fÌíõ™fÌå—÷Jš1Oñoþòü,þ0ûööõÎþvsÿêÝýÝË·?ÞÌþüæÝÏ?ÿ÷ì›Û÷ÿœ={÷öÃý»7onî¿zrÙš´Ó%b‹)òÙ^ºIìâ6’s|G>ˆïÈñù ¾#ÄwäƒøŽ| ?E>ˆïÈñù ¾#ÄwäƒøŽ|ß‘á§Èñù ¾ß‘â» ñù ¾ ?E>ˆïÈñÝ€„øŽ|ß HˆïÈñÝ€dø)òA|G>ˆï$Äwäƒøn@B|G>ˆï$Â#Äwäƒøn@B|G>ˆï$Äwäƒøn@2üù ¾#Äwâ÷‘Ï)}yäsšWìwª_]­* YMËéÇüíòò ÜâŸ<Ø ¾.¾¥#ŸÐç˜ëÌþöòþåÝ͇›û÷’¦'¿eþéç´X¬O—ëýç5}ଡŒ„g1Þ&Jÿô«ö(úz#õÑ Ÿ};~l_wò•ñ‹Ó—óÔÇï¸ðÙWk©_$à³_÷kUüÌ,ÇßÖà³oåku¼5Ãg_íßâÙku5Rp¨¯äku5¢Føì×òµºy|ö|­^¥UîÙ¯òµz5~3Ï~%_«ûeÀðÙ¯åku»>ûN¾V×c­¥{öõR¾V×#»„Ï~%_«ëÁgßÈ×êzüŸ}'_«×cõ‡{öë¥|­^ê&|öµ|­^|ö|­^œÞ=ûf._«›†Âg_Ê×êfŒ¼Ág_Ë×êf4™ÀgßÊ×êfTòݳoçòµº_W Ÿ}\ç„ú¨IA}-_«ÛüÃgßÊ×êvŒwºgß-äku7ªà³¯äku7ºVà³_Ë×ên´ÀgßÂ×ê¡þÊž}¬?†¯Õå|¹à³_Á×êh·‡¯Õåü¾VG³=|­Ž²|­ŽµÊðµ:¶*Ë×êb´Á¸Ÿ÷q¬ó\áõ±jºëqŠüp'ûXt=Ôt/nä?_ÓüTZÍÊæÓšîw·w·§¸¥¨[eׇù1‹ºUv=óe{”¢n•]•^eW¥WÙé)»*½Ê®J¯²«Ò«ìªô*»*½Ê®J¯²+ÒSvUz•]•^eW¥WÙUéUvUz•]•^eW¥WÙé)»*½Ê®J¯²«Ò«ìªô*»*½Ê®J¯²«Ò«ìŠô”]•^eW¥WÙUéUvUz•]•^eW¥WÙUéUvEzÊ®J¯²«Ò«ìªô*»*½Ê®J¯²«Ò«ìŠô”]•^eW¥WÙUéUvUz•]•^eW¥WÙUéUvEzÊ®J¯²«Ò«ìªô*»*½Ê®J¯²«Ò«ìªô*»"=eW¥WÙUéUvUz•]•^eW¥WÙUéUv5z—]•^eW¥WÙUéUvUz•]•^eW¥WÙUéUvEzÊ®J¯²«Ò«ìªôQv=C_mŒÎF½}l+ÿšíÿýõñÛÙf?ÿ?åÏïf|1¬úߌÿÜŸ²]¯·Uv]<¥]}þpv¿=màŸ®fÛ]—c?ÚÝϾ7ûöåýO'+Óf/þñäO/fŸý¶½§ŸØb™• g>»Ç¨Žî‡ôFƒÆ¹/Þ£ðY©€ž¾oº(>+Ÿ• ŠÏJÅg¥‚â³RAñY©€ø¾é¢ø¬TP|V*(>7]Ÿ›.ŠÏMÅ禋âsÓñ½àSñYð©ø,øT||*> >ŸŸŠÏ‚OÄ÷‚OÅgÁ§â³àSñYð©ø,øT||*> >ß >ŸŸŠÏ‚OÅgÁ§â³àSñYð©ø,øD|/øT||*> >ŸŸŠÏ‚OÅgÁ§â³àñ½àSñYð©ø,øT||*Þ¦Q;›FõMóÅ™|*ž¦QÓMõô4šnº¨§§iÔtÓ=}/øT¯9uò«a°Õc-øü¹a°}]¢ï¶œßïù› -ON -‹Åü0çzæã^\/8§?|ÇÜŸÒk7©ÒkHNé5$§ô’Sz É)½†äž!9¥×œÒkHNé5$§ô’Sz É)½†äž!9¥×œÒkHNé5$§ô’Sz É)½†ä”^CrHÏœÒkHNé5$§ô’Sz É)½†ä”^CrJ¯!9¤gHNé5$§ô’Sz É)½†ä”^CrJ¯!9¥×Ò3$§ô’Sz É)½†ä”^CrJ¯!9¥×Ò3$§ô’Sz É)½†ä”^CrJ¯!9¥×œÒkHé’Sz É)½†ä”^CrJ¯!9¥×œÒkHNé5$‡ô É)½†ä”^CrJ¯!9¥×œÒkHNé5$gô’Sz É)½†ä”^CrJ¯!9¥×œÒkHNé5$‡ô É)½†ä”^CrJCrgèËËѧÕÕC9zs¼Ë˜å¡#ðkh/“;å÷÷&¯Où“›ÊÑ£2ýêöõÇûÃvÒÙ7¯oßnO>yñÙ—ÍÊåzØxˆO÷¯^\›>3‰·JBô$ôHOBoôN葞„éI葞„éI葞„éI葞„ÞèÐ#= =Ò“Ð#= =Ò“Ð#= =Ò“Ð#= ½Ñ;¡Gzz¤'¡Gzz¤'¡Gzz¤'¡7z'ôHOBô$ôHOBô$ôHOBô$ôHOBoôN葞„éI葞„éI葞„éI葞„ÞèÐ#= =Ò“Ð#= =Ò“Ð#= =Ò“Ð}J葞„éI葞„éI葞„éI葞„ÞèÐ#= =Ò“Ð#}ŸÐŸÒW»K#ôÓŒy¿Æn³Êõ°_­±»ú<üÓ5vÿtÝó?}÷ìÛ?üÛq‘Ýó›77?~xw?{öŸ/ß¾½yó`”ì«/Ïè{CЙOó1Búu6)>‚Ÿ AŠÏ† ÅgCâ³!ñ½!HñÙ¤ølR|6)>‚Ÿ AŠÏ† Ä÷† ÅgCâ³!HñÙ¤ølR|6)>‚ß‚Ÿ AŠÏ† ÅgCâ³!HñÙ¤ølB|oR|6)>‚Ÿ AŠÏ† ÅgC⳯ÚðÓ† ÅgC⳯ZñÙ¤øì«V|6)>ûªß‚Ÿ AŠÏ¾jÅ AgèÇ|ö-™›mîüjCÐÕcmüÿ— Ag>îÅÏi“ébQ‚”^‚”^‚”^‚”^‚”^‚”^‚ž AJ¯ AJ¯ AJ¯ AJ¯ AJ¯ AJ¯ AHφ ¥×† ¥×† ¥×† ¥×† ¥×üÒkþ@é5€ôÌ(½æ”^óJ¯ù¥×üÒkþ@é5 ôš?@zæ”^óJ¯ù¥×üÒkþ@é5 ôš?PzÍ =óJ¯ù¥×üÒkþ@é5 ôš?PzÍ =óJ¯ù¥×üÒkþ@é5 ôš?PzÍ(½æžù¥×üÒkþ@é5 ôš?PzÍ(½æ”^óHÏüÒkþ@é5 ôš?PzÍ(½æ”^óFïù¥×üÒkþ@é5 ôš?PzÍ(½æ”^óHÏüÒkþ@é5 ô1p†¾¼}Z]ÝÏl¯rQæWóëKçNñqû¾ß´šýûíË÷ÿývöüÅ×ëùõìÛ—÷?;¹}ÿÏÇ=X¥d|е èø)^\š>ÞX¬Ò¢ø´€ ¾O–+>- ŠO ˆâs²\ñ9Y®øœ,W|N–+>'ËßIŠÏ@’â3¤ø $)>IŠÏ@’â3„øHR|’Ÿ$Åg IñHR|’Ÿ$Ä÷@’â3¤ø $)>IŠÏ@’â3¤ø $!¾’Ÿ$Åg IñHR|’Ÿ$Åg ñ=¤ø $)>IŠÏ@’â3¤ø $)>I†Ÿ’Ÿ$Åg IñHR 1÷I»ùÉþôÑò½~¬¤ÁŸHZ=ÈzÉÉò3÷âÔ÷4Õî$¥×@’Òk Ié5¤ôHRz $)½’ž$¥×@’Òk Ié5¤ôHRz $)½’ž$¥×@’Òk Ié5¤ôHRz $)½’”^IHÏ@’Òk Ié5¤ôHRz $)½’”^IJ¯$¤g Ié5¤ôHRz $)½’”^IJ¯$¥×@Ò3¤ôHRz $)½’”^IJ¯$¥×@Ò3¤ôHRz $)½’”^IJ¯$¥×@’Òk éHRz $)½’”^IJ¯$¥×@’Òk Ié5„ô $)½’”^IJ¯$¥×@’Òk Ié5dôHRz $)½’”^IJ¯$¥×@’Òk Ié5„ô $)½’”^IJIgèËËѧÕÕC9ú:7K~µsýX'Ëw§|$;åîß½ys3.¡è“åã!àÉr¥'¡GÏž„ÞèÐ#= =Ò“Ð#= =Ò“Ð#= =Ò“Ð#= ½Ñ;¡Gzz¤'¡Gzz¤'¡Gzz¤'¡Gzz£wBô$ôHOBô$ôHOBô$ôHOBoôN葞„éI葞„éI葞„éI葞„ÞèÐ#= =Ò“Ð#= =Ò“Ð#= =Ò“Ð#= ½Ñ;¡Gzz¤'¡Gzz¤'¡Gzz¤'¡'ú”Ð#= =Ò“Ð#= =Ò“Ð#= =Ò“Ð#= ½Ñ;¡Gzz¤'¡Gú>¡?¥×óK·z[ÌçNX­Ÿ®>Ïxµ|=Òöþÿsdì»Û»Û/?ܾ{ûþÁG}2º›­Æ¿fû}üÇAnö³»óùâô³Z,æ£À°þõG~2ê¯f¿»xhì_W¬Î}á…¯©1Æ×ØãknŒñ58Æøšc|Ž)>³cŒ¯á1Æ×ôãk|Œñ5?Æø c|M)>#dŒ¯2Æ×ãkŠŒñ5FÆøš#c|•©_ujŧPÍøªT3¾JÕŒ¯Z5ã«XÍøªV3¾ÊÕŒ¯zµâS°f|U¬_%kÆWÍšñU´f|U­_ekÆWÝZñ)\3¾*׌¯Ò5ã«vÍø*^3¾ª×Œ¯òµâS¿f|°_lÆW ›ñUÃf|±_UlÆW[ñ©c3¾ ÙŒ¯J6ã«”Íøªe3¾ŠÙŒ¯j6㫜­øÔ³_mÆWE›ñUÒf|Õ´_EmÆWUñ]Öf|Õµ_…mÆWe›ñUÚf|Õ¶_ÅmÆWu[ñ)o3¾êÛŒ¯7ãG…ûœý%îñŽ3ìÍÉͨÅÕƒBóìXY¾¤Ð¼p;g?»wws÷ã»ÙÕï׿¿¾úó7Ïf/ßþ4{öìù×Ë«år¶ÿ/žßÞ}|s(y?xÏ­x¯sIfÿ4Ul×±±ä½Î)™sŸícÔ¼×¹%Ãü“a~®É(¿ÏÉ0?÷d˜Ÿƒ2ÌÏEæç¤ ósS†ù9*£ü¾*Ãüœ•a~îÊ0?‡e˜ŸË2ÌÏiæç¶Œòû¸ ós]†ù9/ÃüÜ—a~Ì0?f˜Ÿ3Êï3ÌÏ‘æçÊ ósf†ù¹3Ãüša~.Í ÿÛÌ×WgÛy„¿ÿ/ç¹5Þ?Çf˜Ÿk3ÌϹæçÞ ósþXù}q†ù99Ãü\@fþ8:sÎ^/„Ÿ™‚ìïŽ/«œ9^å_²ÏÔŽìïõ)Õ˜U#ÿ÷¿ÜÜÿr{ó¯KŸÿtMÑu½÷Œ/Йsñ›Û¿Þ{œ_ï=ί÷ç×{ó뽇ù›ùüéø³ÑùõÞãüzïq~_w~Ý_w~`w~]`w~`gþv±¤?ÿÛ:Âîž¿²sçWvîüÊÎ_Ù¹ó+;gþnaߟw•»ç¯ìÜù•;¿²sçWvîüÊΙ?~wÙîß@Õß?óÏÍßoóÏ•ßá¹ò;w8¿Úáüj‡ó«ίv8¿ÚÙüêØüi~>ÍŸÊ·ùÕˆŸiþ¼œÛüj9·ùÕrnó«åÔü‰~~6¿Z.l~µ\ü_zÓ.oþ«tŸUºŠÏ*]Åg•®â³JWñY¥‹ø^¥«ø¬ÒUüø+èþ"â³JWñY¥«ø¬ÒU|Vé*>«tß«tŸUºŠÏ*]Åg•®â³JWñY¥«øñk|ÏYg•.zú^¥«ø¬ÒU|Vé*>«tŸUºŠÏ*]Åg•.â{•®â³JWñY¥«ø¬ÒU|Vé*>«tŸUºŠÏ*]Ä÷*]Åg•®â³JWñY¥«ø¬ÒUü€áß¶Y¥«ž>«tß«tŸUºŠÏ*]Åg•®â³JWñY¥«ø¬Ò5ü´JWñY¥«øQôpï9£&· |Véª/NVé*>«tŸUºˆïUºŠÏ*]Åg•®â÷«tÏØ—×úNŸj}Ë«Ók±ûZßòÒE,å?\õr¨õ ÿÓk±gj}£ì÷êöõÇûÃþÜÙ7¯oß O^ücöe…¿}3þþ}éÌçyñ.˜Ó/áâ*Y§â“u*>Y'â;ëT|²NÅ'ëT|²NÅ'ëT|²NÅ'ëT|²NÄwÖ©ødŠOÖ©ødŠOÖ©ødŠOÖ‰øÎ:Ÿ¬SñÉ:Ÿ¬SñÉ:Ÿ¬SñÉ:Ÿ¬ñu*>Y§â“u*>Y§â“u*>Y§â“u*>Y'â;ëT|²NÅ'ëT|²NÅ'ëT|²NÅ'ë4ü”u*>Y§â“u*>Y§â“u*>Y§â“u*>Y'â;ëT|²NÅ'ëTü>ëƒ ŠÏ`ƒâ3Ø ø 6(>ƒ ŠÏ`ƒâ3Ø€ølP|ŸÁÅg°Añ)ö)>Å>ŧاøûßÅ>ŧاøûŸbŸâSìS|Š}ŠO±Oñ)ö!¾‹}ŠO±Oñ)ö)>Å>ŧاøûŸbâ»Ø§øûŸbŸâSìS|Š}ŠO±Oñ)ö)>Å>Äw±Oñ)ö)>Å>ŧاøûŸbŸâSìS|Š}ˆïbŸâSìS|Š}ŠO±Oñ)ö)>Å>ŧØgø©Ø§øûŸbŸâSìS|Š}ŠO±Oñ)ö)>Å>Äw±Oñ)ö)>Å>Åï‹}gìõgÌÆÕÚý¿¿ÿ9Þq†}}zÁvýy…¾‡öahâŒý‡Ÿ~yùöÇ›Ÿfßܾå½7c“ÙýÏï2ÐðüãÏ7÷_?¿ýß³ç·wߊӣ|~í戴ç™îé>bño:dpæó}ŒêßU2P~2P~2@þtÈ@ù}È@ù}È@ùu‰óìký1~>×}È@=2P~2@þtÈ@ù}È@ù}È@ù}È@ù}È@ù}È@ù}ÈùÓ!å÷!å÷!å÷!å÷!å÷!å÷!äO‡ ”߇ ”߇ ”߇ ”߇ ”߇ ”߇ ?2P~2P~2P~2P~2P~2P~20þ!X<ŠU~âT~2P~2P~2P~2P~2@þtÈ@ù}È@ù}È@ùûCgìõg6ä?Ìé–#‡²íqlø__~ÆàtµÉ8±zЧÔ-ãßÿrsÿËíÍ¿¦pï7^,8=”p]ï=ã 4>‰O?ÌÅáÆ¿Þ{œ_ï=ί÷ç×{ó뽇ù›:ìüzïq~½÷8¿²sç×`çWvîüÊÎ_Ù9ó·uØù•;¿²sçWvîüÊÎ_Ù¹ó+;gþnq8ÒîüÊÎ_Ù¹ó+;w~eçίìÜù•3üî²}zøåÚüýs1ïð\ùž+¿Ãsåwx®üÏ•ßá9ò÷Ý5òçsœŒ³~‡çêëÓá¹ò;w8¿Úáüj‡ó«ίv8¿ÚÙüêØüi~>—s›_-ç6¿ñ3ÍŸ—s›_-ç6¿ZÎm~µœš?ÑÏçÂæW˅ͯ–£¯ñl¶tyóçisãaHûjsrynlŠX\Þþyê.Ûñÿðͳç׳ç|6ûÛËû—w7‡v³ËnØ~´ñ“TÇ/Ï|ÄGè]ÌÇOÒþDžâ—ÊœI>ýzEùñ[¼|úÍŽòãw$øô‹ùšòão òéWôU»¾Ë§_ÓWíb´¯Ê§ßÒWíbd«ðé—súªíþèý¾wú+>;ýŸþŠÏNÅg§?â{§¿â³Ó_ñÙ靸ìôW|vú+>;ýŸþŠÏNÄ÷NÅg§¿âGÒÿ´ºÊNõôÙ靸ìôW|vú+>;ýß;ýŸþŠÏNÅg§¿â³Ó_ñÙ靸‘®Á÷œuvú£§ïþŠÏNÅg§¿â³Ó_ñÙ靸ìôW|vú#¾wú+~Ô<à«v“þêé³Ó_ñÙ靸ìôW|vú+>;ýß;ýŸþŠÏNÅg§¿â³Ó_ñ£Îßs¶Ù鯞>;ýß;ýŸþŠÏNÅg§¿â³Ó_ñÙ靸ìô7ü´Ó_ñÙ靸QÛvï9£õBV~Fç…¬ü,çÙ鯾öÙ靸ìôG|ïôW|vú+>;ý¿ßéÆ~´–ŽõêäÀö¡¥ãâ›§M‡–Ž3þ±¥ãÙ»·¯n_ÌJÿn^ß¾?ôw<¹¬¿£–‰=\U¶X¦æ4>ü“w³ßÿùxŸüâþŽs|jNŠOÍ ñ]sR|jNŠOÍIñ©9)>5'Å§æ¤øÔœŸšâ»æ¤øÔœŸš“âSsR|jNŠOÍIñ©9)>5'ÄwÍIñ©9)>5'Å§æ¤øÔœŸš“âSsR|jNˆïš“âSsR|jNŠOÍIñ©9)>5'Å§æ„ø®9)>5'Å§æ¤øÔœŸš“âSsR|jNŠOÍ ñ]sR|jNŠOÍIñ©9)>5'Å§æ¤øÔœŸšâ»æ¤øÔœŸš“âSsR|jNŠOÍIñ©9~ª9)>5'Å§æ¤øÔœŸš“âSsR|jNŠOÍ ñ]sR|jNŠOÍIñûšÓûòšÓiåä0F¼ÞåŽô±(s¨9}æš'îTWÍi—ÛÍGÿXsúîön“þpûîíû/?)3 úŽO©.Ê?äÅu¦Ó ÚØè]#ƒŠÏ ¯â3è«ø ú*>ƒ¾ŠÏ /â{ÐWñôU|}ŸA_ÅgÐWñôU|}ŸA_Ä÷ ¯âÇŠº}›“âW5ž¯ø ú*¾›.̆Ënº@|7]~jº@|7] ¾›.ßMˆï¦ ÄwÓâ»éñÝtaø©éñÝtønº@|7] ¾›.ßMˆï¦ ÄwӅ᧦ ÄwÓâ»éñÝtønº@|7] ¾›.ßM†Ÿš.ßMˆï¦ ÄwÓâ»éñÝtønº0üÔtønº@|7] ¾›.ßMˆï¦ ÄwÓâ»éÂðSÓâ»éñÝtønº@|7] ¾›.ßMˆï¦ ÃOMˆï¦ ÄwÓâ»éñÝtønº@|7]þØtønº@|7] ¾›.ßMˆï¦ ÄwÓâ»éÂðSÓâ»éñÝtøCÓÅ©½¾úò.…ñŽ3[\¯Õ¡°x:þiöâÕìw‹ë/· gìÛþûŸþ:{~ûöõ››Ùß½¼ÿiöìÝÝÏÇ|ïøoï>¾9´`|õ“¾9j9¢F}ŸàÅ-§ +‹}Õép[ù}ÔùÓQZå÷Qå÷Qå÷Qå÷Qå÷Qå÷QäOGi•ßG=”ßG=”ßGi•ßGi•?ºUäûçU¥UÏßGi‘?¥U~¥U~¥U~¥U~¥U~¥U~¥Eþt”Vù}”Vù}”Vù}”Vù}”Vù}”Vù}”ùÓQZå÷QZå÷QZå÷QZå÷QZå÷QZå÷QZäOGi•ßGi•ßGi•ßGi•ßGi•ßGi•ßGi‘?¥U~¥U~¥U~¥U~¥U>ί¦£´æùÕøûãñ(­z~›_Òªç·ùÕñ(­z~›_Ң矎Ò*ßæWu”ö̳¯×—NÃùå˜üg·Ÿæþ#’^|žþp–t$“ýÓ²É÷¿ÜÜÿr{ó¯K?Âé|çueçãjïø$>ý0/ÎøõÞãüÊÎ_ï=ίìÜùõÞÃüÍü­:¿²sç×{ó+;w~eçίìÜù•;¿²sæoË}mÁù•;¿²sçWvîüÊÎ_Ù¹ó+;gþnaߟw•»ç¯ìÜù•;¿²sçWvîüÊΙ¿˜Ï·Uœ5ÿûKoÐêï·‹y‡çêù;6¿:6ªç·ùÕ±ù=ÿÔü©|›_Uóç™g¿¼ùó´¹ñ°ybs}z¾~4^ÞþyêN‰œñ³ÙâÏf{yÿòîæp±~vÙÅúÓ6]9ó¡tº$¢ø\Q|.‰(>—DŸK"ŠÏ%Ä÷%Åç’ˆâsIDñ¹$¢ø\Q|.‰(>—DŸK"ˆïK"ŠÏ%Åç’ˆâsIDñ¹$¢øñkÑþŠ‹âsIñ}IDñ¹$¢ø\Q|.‰(>—DŸK"ŠÏ%Åç’âû’ˆâsIDñ# €ï9W¹$¢ž>—DŸK"ŠÏ%Åç’âû’ˆâsIDñ¹$¢ø\Q|.‰(>—D?â/øž³Î%ôô}IDñ¹$¢ø\Q|.‰(>—DŸK"ŠÏ%Ä÷%Å¢|ÕnrID=}.‰(>—DŸK"ŠÏ%Åç’âû’ˆâsIDñ¹$¢ø\Q|.‰(>—DŸK"ŠÏ%Ä÷%Åç’ˆâsIDñ¹$¢ø\Q|.‰(>—D ?]Q|.‰(~ŸÝßsFoÄ‚ò¹$¢¾8¹$¢ø\Q|.‰ ¾/‰(>—DŸK"Šß_9c?ZÏÅv¹ª]U«—DFÏÅòËbz.ÎøÇž‹gïÞ¾º}ýñþp>döÃÍëÛ÷‡ŒG8$’¢ÐøðµOìøé]Ü€qºªl±LQñ]R|ŠBŠOQHñ) )>E!ŧ(¤ø…Ÿ¢â»(¤ø…Ÿ¢âSR|ŠBŠOQHñ) )>E!ÄwQHñ) )>E!ŧ(¤ø…Ÿ¢âSR|ŠBˆï¢âSR|ŠBŠOQHñ) )>E!ŧ(„ø. )>E!ŧ(¤ø…Ÿ¢âSR|ŠBŠOQñ]R|ŠBŠOQHñ) )>E!ŧ(¤ø…Ÿ¢â»(¤ø…Ÿ¢âSR|ŠBŠOQHñ) ~* )>E!ŧ(¤ø…Ÿ¢âSR|ŠBŠOQñ]R|ŠBŠOQHñû¢Ðûò¢Ðiiã0ˆ»Ýæúú±j2~cY<]]Z*ÿá–* ms¾þè‹BßÝÞÝ~8T„Þ_úáÎMâ.7‡Âñø”>­s]\:ÇgTVñ•U|FeŸQYÅgTñ=*«øŒÊ*>£²ŠÏ¨¬â3*«øŒÊ*>£²ŠÏ¨,â{TVñ•U|FeŸQYÅgTVñÝaþ0œº" ?uE ¾»"ß]ˆï®ÄwWâ»+ñÝøîŠ0üÔøîŠ@|wE ¾»"ß]ˆï®ÄwWâ»+ÂðSWâ»+ñÝøîŠ@|wE ¾»"ß]ˆï®ÃO]ˆï®ÄwWâ»+ñÝøîŠ@|wE~êŠ@|wE ¾»"ß]ˆï®ÄwWâ»+ñÝaø©+ñÝøîŠ@|wE ¾»"ß]ˆï®ÄwW„á§®ÄwWâ»+ñÝøîŠ@|wE ¾»"ìŠ@|wE ¾»"ß]ˆï®ÄwWâ»+ñÝaø©+ñÝøîŠ@ü¡+âÔ^_yÁxÇ™-v«mµ,Ž£²›/·gì¿Ü½|}óÍíûÎþãwùë7ÿñÕìð‡ÿzöüãÏ?¿»ÿ0»};{þ—¿~ûÕ—OÌ®úºÁx”™=~šwJœö•,V}Sù}Ý@ù}Sù}ÝùÓuNå÷uNå÷uå÷uNå÷uNå÷uNå÷uNå÷uNäO×9•ß×9•ß×9•ß×9•ß×9•ß×9•ß×9‘?]çT~_çT~_çT~_çT~_çT~_çT~_çDþ8üS×9•ß×9•ß×9•ß×9•ß×9•ß×9•ß×9‘?]çT~_çT~_çT~_çT~_çT~_çT~_çDþtSù}Sù}Sù}Sù}Sù}Sù[úçï!SŸ¯¯1È–óQݧ¾Í¯Ž×9Í÷w9·ùÕñ:§z~›_¯s¢çŸ®s*ßæWuó̳¯/ލOÃÛåzï®?}_ËKðÓ‘À‘LôOÃõï¹¹ÿåöæ_—~„Ó1½ëzïYžý0‡Ûgüzïq~eçί÷çWvîüzïaþf>ßÿÙåüÊÎ_ï=ίìÜù•;¿²sçWvîüÊΙ¿],éÏÿ¶²s÷ü•;¿²sçWvîüÊÎ_Ù9ów ûþ¼«ìÜ=eçίìÜù•;¿²sçWvÎüÅ| Ïw9qÿͧÿáoÐêï·‹ùž£çŸÂsäOá9ò§ðùSxnü}cY…3ÈŸÂsäOá9ò§ðùSxŽü)ίº½‘=?ίº½‘=?ίº½Q=·72çWÝÞÈžçWÝÞÈžçWÝÞˆžjod¾Í¯–s›_Míìëcó«©½‘=¿Í¯¦öFõüÝÞÈ|›_ÚÏ=ûÃöÆ¿ÿŽ1è endstream endobj 18 0 obj 12227 endobj 16 0 obj << /Type /Page /Parent 3 0 R /Resources 19 0 R /Contents 17 0 R /MediaBox [0 0 612 792] >> endobj 19 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F3.0 20 0 R >> >> endobj 22 0 obj << /Length 23 0 R /Filter /FlateDecode >> stream x͘ÛnÛ8†ïõs除Η©Ó4F‘Ýv[`a ÐJt­FURN›>}‡’kÙ–ÄlÄ98üøÏüœê;|„ï@Cp‰MBBBðDZ#’Ã(ax:ôyK9ˆÀEz— O±VÍê8GÅûS\g¢„Åèâr²8ƒÙºâò|–ý„ɇOкäûWÀôU¢ŽÛ Ç#Žh1ñhÌÿA ¸œÎÞÓ‹×õAéûͰðÓº÷ZÊSº™H´%^­}‡~¬2¬Ü9>ãØÐ†”·”§”;§(ÿøtÿˆ endstream endobj 23 0 obj 1001 endobj 21 0 obj << /Type /Page /Parent 3 0 R /Resources 24 0 R /Contents 22 0 R /MediaBox [0 0 612 792] >> endobj 24 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 26 0 obj << /Length 27 0 R /Filter /FlateDecode >> stream xÍZÛrÚH}ç+úÑ®¬;âѳ-l9ÎÖU.! 0]ˆFØñ_îËþÏöÌ(\$Åe`HbWÛÂÝsúô}ü>ÀWÐ]°´¶æjš vÇŽi¶»d>CW=¦CÀ@Ÿ,À?ÐÚ†¥ññ«ÖöG”ÔéXm|¢7‚Þ݃iÊ÷à«ï75Ë€û®z[îçpáÆC¸ŽrŸf»Ú%Üÿ7÷â`[Á\ÓÛõØÝZE6¤s¸ÕàXmÛÛh›x64‚k´]ÑY.~Ø ´ÀüpAµt _M[Ã17h-Ó5íqNçí`{Ž·Y´ªÂÁÓ¢ŠFÉ Ó‹ÑØ»Á`4€>e_ —&y–FÉ`ö7YB L2é塇(h-á4Mgƒ³`Uà\†ÂÕÇZUñ֨™^\‡O~¡.hîGˆ6[¥™ŸÓ4|ÜïM/aØïµøI`ègü£ Óg|rÙ8Ìùª”'w-´Fc×ÇÇm®9?ÂਢqT2¸ewj ‡}"KU¯Q3½è¥ñj=ÉR·úG7WÃ’ƒI[Âæ“ÍʃÉ6µ²Y…“Å!óÃÃW Ë]L*ÙËOÓ‹Û4Ë—àå~ã¾×ºîCÈ=(Ø «3¡v4žBöœI fÁÊÔ´#ÒH}hI5%Üñó<"H/2žcö@ÖÃu3ðzÔ>o½ÂXËá]Êã*ƧøvÌ1c¨‰(é/áøp²Ô(…SÇØæéÝüÅH,ýäpçª7ršpòHD‚<Í ‡*Õ™s“E”DT§ËÓvo±#Rv}c$žÃGcîý|éÇà'!¼_GÔO04£§#%eV7ÝJû’ø,>îî0À/—_+gï_ ¼eö–u`÷8¼O×-{ÛMS/öËð‘þ»«AF ¿`ÛàFº[XøóÑ%¦5üV€»¼ÔìveþlwÝ íM>ññâ=a¸$aªVºÓáee/gP×y I†äpÏ­Ÿê´L/D¨¾íÔ€ìX¼ ”ézLWìp\Õ˜‰*~>e®^Ù‚pÊV6¶Gûi5zÔ“æ8ruˆ¯|‰¤w]>© iF[+’Šq\£»¢qSÇEK²Pt1î‚Ñ]yê²èä‰2¾ŒR†ãíË¡šª¤X˜o Í)ÈÇ}sâ_¢­]ÑÛ%õÁö©žÚFV·§ÞŠÖ;­ëõ¢ÅWÐÍÓà¿VË„ðŽ,ÓŒ¯Eãô W¦7ßÝy7W>på>ÇÝCßûS´ACþMžBHfëÌ#Á3íÉí`N·ŠvèæªiÈâÌ¡èr:˜¹ªhÁ\hÍla»¨”9i›½*´Ù¥ÉyÉóÃÉû>RrÚ–¸~Ã6)ö æYC¿8H›§'MàVúö×¶ïMøñ‘·Ú·îÃÍX™WÎÙ¼ EŸË«jDÝր̔z•´Í«^u¥ Qu2ü å·*xÇâáe ÏŠËÄÛ·Ò@¸Ù–]» òdß›&  HK¦[4€'¤¥jÖii#z›õNKUÑ"&kD£õIÀ¨£ª Ôh)§¥78ЃÜoÿ‘ù«% ùŽeT6ºƒÈgËçŒòŠ75aׄí6çÒ/@ùrVlcñÑææT<Ú^ñ4AÜ©:t§’I_¹^Ræâ¶¸GÁ< ÞÅ7¢Õ»xhCo]¯2¥.^£¥6#†i°ŽI’Ë f^|?õ¼>L|`bÚš²’æˆk)Ü“@Wµ› EŸ«¤ÕˆÖ-åtIÛì•´7ÒUJ\ÿ1Ñ=‰+]EÑ-W {ûMò-ÇeÏ#Haé_D½ÚLõ'xGµ¨ï؈VÌ5¢5»õÞO”s–ZïHHþœf_h²€À_ù3Ñü¥ 4 ¢5ï±{“«[œŒx·Ú›ÜÞyÊB[®ÎÚ(ú\¡]#ÚpZ·é'ÏQÕlÔ¬[Jäy£;ä+$}†Þ¡æ¹,‘.X}˜ãµ*Žø–Œð&OŒ¶3‚ßfŠBÜu*—Šd>§%Ið²[¯?üby ð endstream endobj 27 0 obj 1736 endobj 25 0 obj << /Type /Page /Parent 3 0 R /Resources 28 0 R /Contents 26 0 R /MediaBox [0 0 612 792] >> endobj 28 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F4.0 29 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 31 0 obj << /Length 32 0 R /Filter /FlateDecode >> stream xµZÛrÛH}÷W Rû0™’dñ¦Ë¾Ù§¢+öDšIÍV^ZdËâšd3¼ÄÑ~åÖîíA“2e‘5Žâ–SeF–„FãœоÐoô…¬ ¹ÃÁp2NÈhì8ƒ©M™¤O”ÐùUn‘ŸÓPÿË}|a8°Ý!ÿè?õ›—4»¼cù1].ÉqªÏàiãóÎеiÓù;k0$‹–kúi1›¿§‹¨aöÏÉð--ÿE×K­X#˜Wúþu¼içB#Rkl°Yaä¼yöÀn0ÂÄL&Ø;žâÇ;ƒÖß¹]ª¶ëZÕvñtmì‘Sí69«vûŸfùï3d‡dWK>;°#½^²ˆ ó¡dËëßúEFõŽ]¤æÁžaÀ³Æ0 îd!3Zø•%ôù§2 D!ºº;Ÿ“M*•™(Âäžòm^ȘDHÓ(ôñg•P®ÖÅ£ÈäÛ³ãøÓ6/Á±€V|¶OÓ@æ~¦¼Øç·¯·ÓÙ¸Oô¨ÃÑôhÛ×=ž$7ö=šmÉÚ2;ÉÖ¸¿©Izt¨@ Gå5èRä¡Oûp{£]ÆË›¾½GŒL۔݊¯Ê¬ù1 G<}ÿ3Ž\DZHJÑÇÛ«÷×=Z‡ß@bA«òžÂ„b«lK«LЇT…I‘W^NS]¿®ñ¿Wÿûü–òǰð7`¼uGðzêòIE!°Š¯XõÀ”㛸hÖñ=I6NèɶÛÿ‡HLºc‘B™H8­Àœ@ùe,“¢Š€ð}ŸTÐ9ÝýòŽÖ*‹EÀ^9RÝQÅ$2? X;Òr¤‚äÃtãhÔ–Ì‘ªCòÐë_¤æJd*Ã<ËgËpª¿¬•º/s*ú9àBh*dªýN^¦©ÊŒ5±¦' ’OT‡d>YeÄ@¹†€ª óW@dœœ`.în(þƒ¸—¹©£4ÁÕJ;ZãG ’O…P[²e1„´aþ ¡*é›_ܸÅoòE*Va[NüꀨÖkz[Šq;@`7ÛtÄ9¹qØ ùD°uH¶G»e ¶Ê0/ÃV;:L-/h $kèLN„‹>L]¢m¯ÿN®Ø:†`m›ã€BÊQÆXa\F:Ñ0–µ»d?Rl ¬ÑÓ]yÄ6…Uešã°ªBª±Hƒ00•¾õŸÂûYÖÄ=TmÑödW«0‡U»"r&e¼Bm ÕGdïþCN(fi|ÁZI\¯’ux_f’oXFvËöƧò„}*Ä:D[SãˆU¶yùt}ºøøáãíœÖ¸xcØ8N}×6ïø úTØtˆvž*:ÆNSe›—±‘ß ™p p œòò©:×W[Ô’UQ¼þ8ñ]ÒrÆubÑÔnÄ¥ŠWpµàÅ‘M \XÏÐÙ+Šó%Ûr=æöþ ^´«¶|ËnD7ú}Ín‹Ö¦yÒºíöçbËÉ‹1Zt,ràd«ËÁ^õ•ý휻³ÙaAØØIöìºÈ÷ ÄÚÕ DŸê$wˆF\¬+#Æ «lóòIF0 U€>ç™ ¡èeeZW?‹M&Q† ‚;/ÈF+s™»[ÞôTµ-}2 ۢѦ3Ž¡¶Í˶JׯNØhtª‚–ѧB§CôØ88•i^çâfy1ûÈqòœ>Ü6¯ª‚‰ ¤àéÉëÖþ³†Ç„u˜å}•YÎmZÎ7aþTº¦G‘Ócˆæ³¯pwÁá¦ëÝ>Êä£÷hTµáñÔªM-n´B5û©|íü@Ìæ†}ÑΈ,ˆ®HÔˆ>:°Ö`_ôí}‰ëÃ}¦Ê$8Vývð®,Ó.º1hóÙrNÓ<ô3E³$/²½FWF—2’±,ЮZè~<Ê£»nì„ü «Hhê#ðÜLB³¤øŠ5{?ˆì^ÊÄúÛC·Õ¶òQn×—­;•¢d‘Ñu„Öo¦’ÐÏ{ô¸ ÑÏZ)¡³Ä­*¹ó!_,·ºÛ¶*C¼ÒMÕ£ÌÖ%‡œ8-y:á!,týJ%ÑÖÔ&õ=­ÉžþæLÇbx=Žw¡VVЋq=‰µÐvME†h‰h¥h&ê‘€h¡±R›Íxз‹dK‘(ï7hßjåòXDJxÿÖ¼Ñ$ªÌ‚õ(Q»]ÀÒÚ܉âý„éC7QÕ…æ=òZŒØ®SÍMí“a‡û¸,+ퟎ_åTxó>ÚíTÜBÜ€KW^‹½zÕhäCåŒê­3Teš£4††»n¨óg aàx­qù-EŸ–#3^ÎXRø DÈØò Iì¿‚I~ű)ÁDÌ8H_À‰A–©¸­ûùìúº?Bþ 5ÇFËú‚¾‘9ÚŒìú‚Þ{÷XÑu Þ<¢KôÑyD‡hä]¢ß=-wdá¢;‹°GSbÏ2ÑO 6ÜÉ®m/)hO€ÿ&RàoðæY°Î†úV gžÏ25 &ÛZE‘z¬ftª2©nCüÝsÆãšû ¦ˆ\Ç¢Ú¾Ä;(æØ~xŨs•%ñg¸ðF4Áü-†çÔzdÿzÉañãżGö¯üßë;NáµYtäÁ'UÁå/S.xâÖ°±ÄbvkÈ~h dN&}«Ð/p ´@ÐÁƒ-’­…/é’1 ;LH`ì÷‘çÈDá…ìèºy¢Çúøµ ¼úú›û{==4‚“ik£~µ†BÈ ‘È,¹8‘•I²ËUÇLe{Þç·ÿ¾e¦ƒ endstream endobj 32 0 obj 2376 endobj 30 0 obj << /Type /Page /Parent 3 0 R /Resources 33 0 R /Contents 31 0 R /MediaBox [0 0 612 792] >> endobj 33 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F4.0 29 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 35 0 obj << /Length 36 0 R /Filter /FlateDecode >> stream x­YÛnÛH}×WÔÛØÀÛ$˜YÀ±¬á‘å‘5fá—–Ø’¸¡Ø »i÷÷}ó?{ªIF)/b‰J`Åê:}êÔ…_éwúJ^D¡ë¸‘ëFÔhÎ…O¹¤Ï”ч+íÑ\“kÿè9þƒëø¡Ë/ûÑÏÛ_q§á0tð×›¯é㔂 ¼ï>®ÜЧéš>|ò—<š.èìñvôºLHòFî9MÿE7SëØöÆléÇíô/’Z À­…AèôÔ÷¾„Èw¢Ñ…à ¼ú= °øÁp© 7ôÊpñÎáúƒ ŒÖwÜ^íÃt²uàÇ lß;èÛ{÷H>ˆÌiŠŸ4‘"Æ¿…1b¾’1E•ò³’ä?ÞŽi¦D;ìOï·ý±G Ú±>tk þO¬E6_ýX¨4]%šDªŪ˜¥¿à/mò$3À‹1rÎ{ï£àa|ÂÙWL¿~¼ëÜ»‰ÇèvúHQôóõíã}JÕfóJ׉þBs•™\¥)¢{IÌŠŠ “A&Ë•¡8Ož¥>6à>r…óïL€~Ø 8t¼#Þ½wdï]¼“N¿3Òž½nÞùêá#ïÝHÿ6鳤•xFú¬Ú˜De ) ½HèM¦ È(µÌ ©ÌfªÊ“e’‰”ÖHå$“4+ \[h¹(RJÊËt².RaTîÐgÙ …CkPx+Ö6uDšªzU3IËTÎ ­åZ寤“Ký7ëâL’xa?mtZ­%i±Þ¤xS c¿Éå×"A´6NÄ’Ò ¼£§³Ä‘Ç’²qCq#„«‡£§sTBhÇfÊG¡“¹ý¼¬Ft=~D´.’˜)†ÖI ×,£~xב— gÃËG2S$B]>‚=ÔÞ(¢aŸJ#û¥·äLulÊÚ|:‹åB©y:gӧ׆hÈñ•¦«:XÇwDÉoë­ o5“íq/òÉT*=ëê`¼ %¯•tØ®ò=|™Õ½ѲàúþV:ê¡’ÆÛéäòÝ f5—ì˜áõ˜¹ sE¡º¶2ÑW"5”,P(PzŸEšÄ¨ãsKBNÄÒ ¢áf ÝÈ,N²åé4+]ÄhÖ YÅåùJ¡aàó:7îm@,îA0„µ}ºU¸ß»Dþ€¡kÜ9‹µQ+ñs¢Y'Ê6ŸÌDܨŽH¢è6H[kcE£®Ûßé1B¾{ìz#)8“"êÌÆÅ›6<¿+#ÃËÁ@¼AWF"Û{ïù7÷79`èF—ýå{ªÄYší™ªi†‰â½¸mÄfÛ#—hÄ >È- =£çQ…¶}ZUi1}/’e‘‹2ÿNÏ*+Ζë;ŽÑÍh<ùó×_²,û{W—æöÑ•¦,Uœ6k’¾$©¢Ù«9~­°#Á¬ï¶ë^}¶/ïïn®»9ÞÊÎ^”7U Åf&²/¨ëõœ[l쮈C¯½·ßrj7,§Ÿ73Ï÷ÐÀ5T”ÝÑåFGÄ1fhM¹È–ëKçÛÕx4ßC²iÉ¥qÉ!×4“Ü«ÙWtå[{I0Wë5 ¨É´ÈÕšþ[;š¸Ÿð¢²[çrS^‹/°U° wU»}¿=ûóiÍ‹¼ªÊU»†²îÝrÌ÷8Çfž 5ú‰YöS…e…`£ƒ=XÀ]Ö^"ÿ‰µÊ½¥âÙJce‡©èìÓí's²"#`dùÁU;[ã;:ÛÀë7Ýkø¡y eñâµ Us?ôªÍùvõT'ýýø¾Ó¼?` ë rvÚVWI:ºVØú ì~ãÕo7—“RÊ;R·ÒØžºMP· âœwvûmwŒåóÔnÂì¾÷‘•Üì÷§`Ùßǃš#‘‰¥\s÷úG†õél4ÂTC¶¹¶‹Dv·âý—Â4³“ËQ¹×éȳÛÊKkÜ"a0ÓÙôãý\̳ŠÍÕy*EþýóZ> ºØò0ƒ„íÊ»~+-+D2UzкÊÄaÿÍL¼ümzy;™ŒGqô€©Ý ¬S~hU\«8Y$,;€6V/–ɱ-%ôY<ÌTÛµ™R¦#Ì#·•¬O0BÙò  ´¡aáÜá·QYª[k‘Þ±ÿ¬vD;SBc£z?îú\vÕ²j…ÔIÀ_CΠÌ$žq$x±Åþ÷ÿ_ô\I endstream endobj 36 0 obj 1934 endobj 34 0 obj << /Type /Page /Parent 3 0 R /Resources 37 0 R /Contents 35 0 R /MediaBox [0 0 612 792] >> endobj 37 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 39 0 obj << /Length 40 0 R /Filter /FlateDecode >> stream xÅZ[SâH~çWœ·Ñª“@Ø*¡tWÅ ÎNY¼4I#ÙÍ…ÉEtýžÓ ˜ÐqJ ­uª”‰Ì¹}ß¹2?á~‚nƒ©55[Ól°:mè´ZÍ®1‡¿ „ÓA¢ƒ“€&þ$þ­i˜}‰G'ïEIŽÙÄßè '€þZ­ü=øÓÀ÷·4Ó€I§#½©“9=\^_@ÏO™?ÙÚ1Lþ†áDö.˜4}^Õ­UdC4Gß5´Í¦ÕËh¶Ð6 ‚m4m½3;]ü²ù'Ý…Ü]SÏÝÅŸä®Ñnm¼mÞ¢oƒÛG¸¾~|7äs!•uØ‹t4¶": ÙÌç ¤ ×<ˆâ7¸f!{æSx ½¦GhÀôXè‚ãGÎ?0ùÏŒ‡ÎÛqc7êÍjéèoÐ(dËe§ÍýünlÅÖÔº›Ø®™TÄöf¼Gt –´Ptk´œ{Éÿ^Óê|^˜ ì‰d>K£VžïCœ…ø"]@À^ñ7$KÎ]X-CÛ„?û;3WæU…¶¥}ÄÜïÃûþøa¸k†Èzˆ»5zÖ•aÅâÜ x’`]ÀR–±¦‚ÑÂhP¥biʃeJïQCv×ÞÆa{)¢¢÷ãk@,Äë0 Oø«—¤¹­TÉšØ*’(·4…'j,ë˜ímËŠ7¯ 6IæbéŒøÐVE¦uÍmÓ’E´Ê‹|’bº`ªä6b:)bªcÀVoy(jìÝãåp¢†§uZ°UÄÈMd&ÃòµM׿á1¥Dìv)+aíÞÃd|;¾¹è])ò1WU™{¨Ò Ñ7…&I£%ĦéÅ HÒ8sR/ ©bkŽ2ÌΘ»ŠÜ×5œv>ðÿj¬ÖÿB×/àFw¥@‘ÈÁêBq8øTu½%%{)Æ0ËRä‡uZv/*qœ-S*–‘ãd±¢,Ó y”¡Þ'0¼¥‚è°ØÅ°°Ía)0×¥ìm4Bci‰nÝtÆ‘¡ìíp½ìÕó=†×º‹ñ»BjaE{w¦¸–aòm"õÉ+§ÜdÈ›:ágpËp§ÏÀN¾æLЧ§0~á1Î+á%½+7Bwpí¦D2´¶´ØìÌÃzäH´Œ--¿àáÞÑÎè9S© 7Š€«}—´ç„ÌÇbQß×–;"ÖÍŠ#;çÓ ÔeÑgj³ˆfÃ0¥›s_ 4MÕÉÞ}&ÃC¸N6ÍýÁ¦åÃ?~)÷±¿Ñªì³¢ñßëôé–AÇÂ{^@>‡üƒõS¬¸æ§­Õ‘έç .¬´ÙÊ@«‘M ¿4³US´ó5<Іë§eÐÊÚs⨙â6W)W‡3B V#[j5² µ‹«¯AͲèc¶u˜ª¨]¬ñ© vµ~ú¨µM©WõFº"Øj„+ƒ­F6ÁÆ|Ñ ñŒÒm,ÜiÐgçû­GºAU¹¬³0§Gù$$>õ*M²;i*_D.u*õXìý*Tj„+C¥Fv•RS…JYç×¢R×—”¡" W‡Š,»ŠJ©E)C¥¤óKQ±+mPäÊÅ•*Tj„+C¥Fv•R R…JYç×¢"O—? /vý¯B¥ÛŽ˜lY¶:PdÙÊå¼’»üu»¥+{ÞyK©ú"Æ[ÊÝÈÜ®h endstream endobj 40 0 obj 1849 endobj 38 0 obj << /Type /Page /Parent 3 0 R /Resources 41 0 R /Contents 39 0 R /MediaBox [0 0 612 792] >> endobj 41 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 44 0 obj << /Length 45 0 R /Filter /FlateDecode >> stream xµZÉrÛ8½ë+úWÅ 7QÔÜ,ÊN\ÛJ¤T’)Ϧ!‰c. /ùúée-fØrZB=4^÷k4Ðø_à˜8Fßð ÃÁÐ…¡m÷Gä¾C ü„°Cþ+BüÑ·ƒþäW§»ˆ4:}ü³&0žƒm×Ïà«…ÏÛ†cÁ<fßæ x7»¼úgqDù?žqóÿà|. ÛÓHog0Ò4‚lÜà:ý «o£mH‚gõ=gç Gø7è!‹7Nêé:f=]|¥éZ®³m¯žíåÏÝøocR…Ô푦ÛÚH«›Ðó•€ËŸ¥÷âc¥ÈÛŽ¶ ‰=ŽL«nKÒ&$nße)`0@–Æ/·'Ý’±g휱çòâ¢;6yÃV±½îÈä òeŠäçÕº„E,»#½ ç8^ðT/\Ï»OBzAÅæò‚мó³lu¬£xÓ]Sw³¯<ŽÐ@·ONjº =h )9-ò,-a¤"†â)*Õ(àö]U˜Àì+x°Èrp`Qx{Ò?éµ[gÔ,L9e€9jžôö—³ï_¿µ%QŦhFìfnÍ*2q¨A&£m^ WA„˜eú0_Ee— T©Î³øôüö,›ß/ʧNd¾èÎ1YëZ^“ã’Œz/Úñ pP¸E ehyº Àd¾O— ž³2 ¡¨ÂÍði&O«?F\†MC_©ú³ˆèí-•ÃÑ\9ÀAñô/…òŸ•L{àÊáÝLã³ë¿»ƒS,kÀíîÈdŠå°Ês‘–ñ `G‘dù Üé:>ZÈwâþõ{Œ'ù`Pâw§Pˆ×_ðİgaõw˜'²ue){—= ®gdŽšNôo®®n®»“MnÔÀsåu 4ùQfÒopŸ‹¢ >Ì’ëÃÚ£}¸É£e”1úº2 >íMnߥYY§'ŽÜaÓ‰8`IÖ%¦Ÿ`™cØàâ³Âü#à1ˆ+œ@µ^Çf£ç×þôJ®DþôÃØP`  y¦a*ùg)R‘hí# ׎îàiÂ4M%OøŸo|¦D¡ƒ·­îÑK©B‡-sEœ…P¬z Ë|WDIË\àO¿áŸ~¹1¯RJH‚g|*©ˆ eÆäFÓUr…ŒkÊj{–R=@ù²ŽÂ ®ÐBÚ2¡ÁïiõźGú÷ö†Aë¸`²Ï2(ÇÔ<2š«2Š£òfÓóó ª3(*T¬¤sßnÃLÕ˜iÉ’¶aڷ통@O†quOIKÆ,޳'R/QIßx†çþÕ=Öh55mYâZmm:®ƒDtG§<¬CŸE¿Ð¥N4¶û˜sq=ín¾$ÇÁ¤Ò8¼8ûÑ[R£ÁæZ£L öÙ¨“l ë*_g(¼Æ~°Gglo9ÊR‹uIÖÀØ‘µ9D:ûÜ–,[’…ØGÚc˜h”nÄî"ÜJ`àðxML˜eñÙ§¶©»Íš »õ&L…–*¨Ð«h¹:C®¥lõÇ\zÓ`³éMƒ=>ºÞܽs‘ÞÆ\zCìcéM}¨7K½ ¥séMƒÍ¥7 tCol y†ÂÏ¥7 6›Þ4ØþÑõæ¹;²6zó¹ô†ØÇÒ›úPo—z©ÍŸKol.½i zãcH=2Ÿpém¤b³éMƒ=9¶Þ,ác‚º\Úèm¤7Â>’ÞtЇzãð8éÍ2A›Fá„Io:l&½é zãcÈUOy*nËT±¹ô¦ÃÆ~€i&{Ýg ãDzܭÀ¶wºcÓ~ÄÒ`³±£ÁžeUbëŸúþÝgP³ã)ìL.»c×ì¨Ø|ì¨Øl:áÉ3¯²Rd;tÐ|°¡M½Ö·;Ô}§$IƒÎF’{VxºÊ«0ÇPògL1¤Áf£Gƒígx9K¾Ó5Ëq©az>6vTl>vTìIPÌì d;ù€s.v4Ølìh°ÏŸËœ¼×ž=*6=*vyxµå:Jƒuʱ‘™Ynýé…Ƚ¶6âMDÅðÇ(/+ìYÆ)žÉÂ:Ï–y`{µ¢‹ ”N{-.bª'Ìr¹ÊnÑÁnc6m½7SÁ%aÞÜn´([¦Á>ÆR6Ò ¢ƒttÉUJk°ÛÓ£V5=»S¼Sáu ,‡6ëý{l†÷§tó=µN©‘I½IÔ Ô—o°oŸ=ƒÔxíòs\’ÑçÙÊý¬‹Ïg['45Fdü!üÑâOƒ}×=‹¶eNmúþU"é?ﵜlû“x}Àt°·¦…½r0ñÒõð^4[† ¸)0¼9E¯`WÊ0v-å/ÿ¨JP endstream endobj 45 0 obj 1813 endobj 42 0 obj << /Type /Page /Parent 43 0 R /Resources 46 0 R /Contents 44 0 R /MediaBox [0 0 612 792] >> endobj 46 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 49 0 obj << /Length 50 0 R /Filter /FlateDecode >> stream xÍYÙnãF}×W\ôS Ä27IôL4€7%Ü–»%§‘FA™,JL¸¥Š´»ä'§hN)K\°lÙ°`n÷ž»œ»ðzGé’c ×0\MÆ4±íá©E‚ÓJèäBšäI2ôôpƒ1´C}ô¡ãÝ¿xÒdâ qÆx1¯È¶ËkðmázÛp,ZÅt23‡™´ è»åüíOtå,]ãˆV¿ÑÕJ+¶{°’ô|9N· Ó 4ˆ±3id m(+¸ÖÐuÏ™œâ3ÀÁ3ñR‰×1K¼ø ²ÆF w44%\ ~ú]ÌèrFó­f´œÑǽçrHgÕ÷íL]z1Û)|íºØ–Ý4=äOiñÀE¥4‹Øº'Q®ÓœSº ÷ò0Mz”å8-Y0è”æIÎ…(²¼GY#£% ž›ÒJ0÷)g> endobj 51 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 53 0 obj << /Length 54 0 R /Filter /FlateDecode >> stream xµYïoÛHýž¿‚[ u,ùw±»@Ú¤Û`»I6Q±è!8`"í¹H3®fd7÷×ßãHŽmÉÅÕ±6âÄV4äã#ùH}£¿ècêw;Ýq·;¦ÁhH£^¯3 )—ô7i:ý`Š-uý·ñÝNØïò—ëíæOÜi4êwðIpgô>¢^¯¼¯!®ïuû!E~ :] (šÒ«»Ë??ÑYê„Êÿ5è?tyÃ67æ“~þœþþƒ‚€ÌnŽö;ƒ! ÂNÆ…qØá^4Á×àLÒ_*ýí¥¿xeÃaÿÙÝ“Ê]8wwyMï/>ÞXòs 6|ÈI Óse©xRzFÿþî_}0Úå&}ûûýk²¦Ð‰%7—ô ÓôPªÀÖí…G×q­½º~‰«ÍcØÕ=ÇœÒÆQRz\s÷w‘â¹ÈEìdÞ¡h®,M¥p‚‹_ +§Eúúä0ÖíDo„g'Ûä^Í¥&AVeE*œLh‘›Y.2ÊÄ£´$¿ÇÒZµ”l³s•-“·™ØïqâíØvWèò*º¸½ýrOŽQyÒnŠ;àmiar‡ôˆsÀç…&¥”¼X8ª€zŽ›Œ¯Z •ЇTvŽ›7îÖˆ s¡“TæL á|‚ær¦,Ì£?.¾¾?ÿÔ9ŽÃ‰SKÖu®®[ŽÄ¾³v"‘ÌÙS†dÛØìí°ÛȈµ·wŸ/.nŽÇ”CZž²Ë¸TÊÝää¢Lʱà×>1å¸ z”OFä Ù–x.ãG¦b.Û!Û°?®“M›MnÓJl¼,NÏE á@™ÐOd2Ž‹¹}#3oÓ’}“aÝ>±4*¡¹šÍ¡DJn¾ 8‰ª¥¦‡Â>Áhå(5f–¼¬[ךÇh0úqN´È“=çTÝÃ2]*ZÚ1—eô…ý i+;ÆÐK?¨É7׷ѯ7¨—§‘Ì3¥Ezz+Erzeœý;WNâ-+]õó\8Ñ)Æ£†YlÉ;r+ƒ>;Côçò;‰$ÉÒºki¹òþxJpJOzlÃNJ¯xGFËÊŽDÆ*Iu‘= £¹VpQªtKI )XÇ ŽG&ì#r$Qq™«¾•+wÿʲè’{Zœ²í,èö÷¤qÕ6G)mN¶R”ãÝFŽV\<g<(%±€âÑ-Õ±  ê1ÚH îékq(ûuhŒø?}Jþ$v?!b¡[bD8ìÕñx´BÕpRŠ(q‚Y®N‰=bܤwaäðboŽþ†"òCÌ¡—%H´äÛŸ£!Ö.i ƒÞ˜g’É[Ù·[È–"-žÅ÷Ž)‡õ²ôûLβpµÞÂI á,œ¥ë/ÑRÙñ:”\å‹j 6£Ó'(VK—Wåå-!ÖÇØ_CŒsg-8¤÷¯[júÁ`ÂB|•¯¦@]ÀÀ”ZîD<÷Ï˨%L AS•ÊwÇ»Í"5*õ,ŠÎ>|òs@Øk‰å1;c.·&•ÿf¬Å—JÀ·H¦Ú§ŒÖ2vÊht¼rx {Èõª‘3ß ¶RnŒ;_䇓Fç_äj ¨gÒ¾¡§íððÀ*0;@,—¶!»ýëJëWÊqðÀŸ’6Ç›èc54šñV¬~aZh‘ÉßZŠYyÜÞ˜)½(ç©Ã(¯iš›Ì“•M ï=B“WB¦pø·ãÁðñû¦¼Sá<+¬>üf S¬Ch×ocºµ¬âáÚÇçy \™ü‘D[¢dÜlÉòû„ÆÄ*Ko‰Å"õ’‰9^.G )¡åjCOKMºWµ&UæÊ°~KQ^ËYH趪ÞÄ7æB_¬kMK¤ÝsƾBÃÁ’‹) ɼ„­¨òB¤Øœòš¯œ©aЭ$Hø¼êíw®¥Õ¦mûæXàðÍËçææt(zÍ[c ¼÷ÖÑ:™V † ¹T±<ô¼¦:(qòãÖ#öŸGS«TaUá3WzMUèuò “‡ÔK•Iíªuk P›b&c=±×î_eÆ:º=½†ÄYoÜ,šœ•\Še"6‰Ä§«¹‚XÛ¢'e"WéS[NŒB±\ ûýQ"ŠÙŒ·/‹"_`‡ÔÖÚ#ìC~yEÐïHЩšasøÓ’ûýQc#Z.éP¢Ë?/n¯¯Ž§:÷«°šŒ  Ã¿ òí]Piý¤± Ú†¼¶|Æùù ä)k¬ ×ZÊo=yøëÇÍ› endstream endobj 54 0 obj 1913 endobj 52 0 obj << /Type /Page /Parent 43 0 R /Resources 55 0 R /Contents 53 0 R /MediaBox [0 0 612 792] >> endobj 55 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 57 0 obj << /Length 58 0 R /Filter /FlateDecode >> stream xÅYmoÚVþί8ê§FJ\Ûlº) ÉÊÚ4m «¶eªnì x16õ5¡Ù´?¹jÿgϹ6`0›°¶D Ř{ÞŸsžãOôž>‘å‘c¦gšµÜ6¹Í¦Ñ±)•ôbzqª,ò™úWùø‚iØŽÉ?úÒÑê-Nr]ÇÀ'VßÐÉ€šÍü¼Ú¸¿i:6 &ôâÜ2L²h0¤çýÞÅ+:Ž2¦?yæ ~¥³Vlu0Kz¼g» ˦d W"ÚŽÑjSË6šP^ðlÃó`žãvðÓjÀÃGÚK¹½Ž•Û‹W¶×n7—æ6rscIÚä@Þ‡¾$5›N“4S”áƒaEÉ<ŒGÈÛÙˆ†‘)ºy®dFó0ÓŸ}øF½{vrýÝ7Cë÷¯†|¡$]}tÐxš ·«Þ´ ï¤QŽ”ù…²„d,n#I"ŠØ¥Ð|rs°rë“2dÃcM·’ ½·ûí¹-Úrô1 è½¥d*S‘…I¬(‰u ´‹9.õ8Ói²Ukμ¼<Õ¬j Ø,œÝØ()6 çÿ«]»ÕÙF´œÎ*¿‹r>½è>Õ®+Jgk»ªg³]aÉ‘ˆÈO&ja\¨­VÅήN.ûgûŸÏÆl9ŸIP:)ÍEsÍ£ˆešâïD*%FRí˜|-`&^Ù¶6À\'Ÿ½Ä\ÇpžjW‘|å³=}vž|«³iÿ“ǬõæÉ žwÔíõ_#üq–-eúTqÕlc'¹mo‰ÔE&³¸‹Þ ¿”*¤ÂÉÐ7M“Q*&PïÅ%…q&Ó¡žÔâ‹GaìâÉtú@AÞËCšC\›‹]£[*VܳZ›Ðr+Tè‹(z€ºïdšIŸÎ»GhÙy4MæH;î<|OÐíl8Ä•ÛD¤ü&Œ2hn{`åèU«Š%z<_K£ñt\ÃFíÍt6EWŒQÝ‹ãCRÉÂÂ÷Q˜O>ÍÂþgw«™ÂãÈ23–‰Nß]ç–àŽ8`°zTïT{¤‚ÚIEp1Òõ:‡ÚÅæ1úøX~¾9ÈßÏÝ>׿ÐÍ ®„!‘nD ×36dcdßÚ,Ðxñ/Œk2¢ÕÙL ö¨ÐÙožù1Pw†ÿÏR¤÷0Œ¤QOÉY¦îŠkà g\#+ƒ3RÆ ó±Èr?…“Y$2Ä9Õ¢‰x [I‘HGÈ_Ü–·ï$ GaŒ.‘ÄR½Ôy¼¼Ä_„'Œªõ8ÐÂè\ñ`*|¨‡ZVèÝP¼ûh6eİ[eùWnžkûSÉ{g S¤ð‹‹›ƒ~LfäÃDvŽô¹5Y`ÙŒ~k5Ï&·p)w¹b˜‘´Óéö0fyV"Q¦ÀÂi"*t/¢g& ®ŽO_÷6Œ_ :ç¾öYhØ|Ö]|dþ‚<«KЖ ý¡b>SW aê$0`½ÌÕ½®»€?=fPIñ#·NÕmÛÙT=o®ý8¥Bòs¨Ð-¾5€ü¶Ëšl\õO¦ÍÕv¤AÇo \¢‹µ ·õýQS7ÂN›GеxõÏN—WûƒëÚ°y‹”Ø`p”ù$[JF ZR¤ Fc•Í0(\ñÇ<Šé™ïý,©‰˜Ù¦]™ªô\·›á6Â%úÊ.„¬ùy/èA©žä¶MÏÝlÒ;n…òônTŒ×ÖŒç²]_ 1A_ß IJcdäS,Zx^‹F®gÛ°†õ‚ŽÕ¬Ì§q’1ÍþX;ï}Ë‹$ ž¶í²¸bµIÂv~Çb.r¶íð'¯©Š‰µt4¯©¶Íc ÖDQeÒž#ì5¡ÛM‡ùzž3Å„Þò# ˜' _>P_å¬SÜc— Wõ °€•"èãTøaöpXf¥ C)2 óмmª#Åøéƒ¨©®šJ]-ôbÂé]œ Ã_abÓ ÿã2ý=¥ØdØhŠÌ¬xµ“Êæ‰Þ¤H¨‚¯(ê½Ô ˜ï_Ö4¹ÚN§Òó8ä¬çA’ìVu3ÎÜå×Í’¶ÉBýKZæ:“‹ºÜÝîTžDm˜[W²sYk€üjQ¿ÿ3a²]¯²¨+ûáüòêâxðÍ×zóíŽÀ¹Ù r™kþèãY$o´V±êR z—(ò³A­æØTêÂÀCÌbǹ?ˆèÆä5+ ²Ýåóä’š¼¿,<¹;Y!ïcÏðàùü 0‡÷`w'u)Xe4xž«Æ,ñ…§Ñ÷—ƒ‡ÕІßóõþµ‹¦róœÿârÆ[7ì«o4†õûý®·RïýßéZÌg endstream endobj 58 0 obj 1951 endobj 56 0 obj << /Type /Page /Parent 43 0 R /Resources 59 0 R /Contents 57 0 R /MediaBox [0 0 612 792] >> endobj 59 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 61 0 obj << /Length 62 0 R /Filter /FlateDecode >> stream x½Y]sâØ}çWtò²v•W# $`â*Û°;T2¶×°™ª”_„tÅú`t…’ÊŸÿ¡œ¾W€¥Ê€*žñ0¶¤¾ýqºûtëýNßÈêPË4ÌŽivÈi»Ôn6®M™ ¯”Ї;i‘/ÉT¤LÃn™ü¥~õóöGHj·[®X ?¦Û5›ú|Ú¸¿i¶lÅôáWË0ɢф.†ƒ/Ÿé&ʽ0ûGǼ¤Ñ?©?RŠmóIï?§U}Õ¤t ·G¸-Ãqɱ&”ƒ:¶ÑéÀ¼V»‹/§LÞi/i{[–¶Ÿ¶C¶ÛܘÛÐæ>_ÈÜK/ ¨ógiØ£I”Îç+ BùBË0ŸÁ‹”gžÿ"YcÛ%)ü<ͤ¾hÙËÆq^ªÖ®iA¥¸QÆx• yE¡! ²À¡—[ò½¹ç‡ùêùÒØ:ï(”üÂ8h™Ý_Ö0@Ð?÷†½N>Ñoý‡/¿\çŸ>\K|GŸŠCŒÖ÷€¢ÚÜò¡E0†"§|&hÆáP4y¶¢<%h°ƒŠ ´Ù„K=Áh9íý`D"™ p€Á "²ÄËÃW­h•.”„¼H¦´B{lô‘}vÿ‘½6äóUãh9Ms_µû4ðœ§Ý÷ã&Ï=öF~Ç7pEäÏÒª±ý4Ëaš¤YŒgÆž¥‰º&à †9ßç)9¸6 # pÀJ)j²¢ÝÙ·‚ä“(H…¤$Íi@ˆï¡Ì)Ä_I~&¼ú°Q|7#tm…2r,Øÿl1"¥L]ÀÔ$G$d­3§¦H¸¨){;Zkˆ×Ð$óyšåR)3I£(]†É”1^LQh¼©$” ¨¦4þ1,ÒŽzýÛ?~ûebýû/“ÿ¼QšÑæR=îoÛ\ wŠ:ò3M$Það€„øù²žJÓî´pìNÃyêßôÎÎå½Bø Lf€î¹È®i¢j8#ƒë ŸÛ8¢½V2NÈN‹a **Ù×§Á¨¬U‡ÒÙ*Ho¬¥Å™­Zf!r¾l–ÎßµY'tí½®Ð5·fÿ½ÿtû0<Ú°‚}”ä³aòÙ°ÁÉhée ' gºÈ2$@,¤ô¦( Ϙº? &hGD©ÿ"‚«õoÏO¥¡s$\•Öeñä¤pÀj˜Bá“ñc™àw*Äö††µ ÷Xø^. ïT §óEƒ¢UêÍõO†ñ"Ru:ù2ÍTšxlœF”›,—[ƺx4˜­–ŽD6Ŷ8—ôz$¨ÁÏ£»ÇƒÇçKnk=ù®"ð?¹’¹ˆ%dù3ò$áûþè|<) lë apõ‡ËÐŽQœÈ1ZPŽA÷xÿ0T½a{õİ•ò®Ûi’¥µXûQ±~ÇppîrÂèL|[„àÚ`‘ðÐ{CŠA(ÂÙç§IeÑcÇ+òêò‹"æ;ݨáÑ WÑFÉfåùfÓ-aGÙÌØAhÑ‹U§fúû&át‘$¢&[Mî{;r«çãïþ6èßÎ-,KŸ³ÚÇ…b¨”E )LÐãý(/¢8 DMlÈrš\Æ*öŸÐ;j2RŸó#¥È^ÑLê4Ò=dß7£ÑÍÝgÌëÁãÏ^dŸ>^3í[JÇÎÏ%ôª°êCw,Ö ‰v¹‰,âÊ?N1–$4x$VmS”4Z¨žª™þùÈÖªµyvÜ ºœ ?œ„È öAœh%e€D­`ˆ‰É¾CO"Ï&¯—Õm¢}¶[d¹í®æN®aæOÇ‚ð•Y]ôØ­ìí+_‘rj‹-EÞ¶šÃDÝâ£tñ:ÃŒjfµíƒÑßvÐ 0âR¾š‡¾aóâ9æîG ë–Ý6°¨2¬¼~zÓÝãüè)Tµ±rÙCUÁºx r0>è6KѠЩiØc˜¦AÛaØöX ý1f™7uSM v°ŠÙS0YÄc­PNgù¦‹ªd€¢˜?âPJž@Žîi‡±S=­«f¸ªÐë¯Kѱ0?{'Æ6Ú¦}°ÛëÛÒÇ•ï}6·Ýê»6ªg›îA]ü Ø(¼ÚÅÍÝ]ÿ±&†Q%8K—ä­4Ô4oM™Õ¢px¾/æ`ç§Ÿ‚…Õ=ØõžýamgÐÛ¾?r¿ÇÆ ÃÞ&ž¶3/W~F¾í0òw9¸?_¶2°B¶6ówà媗/@"И÷òº¦øUìºþ8¢ÿ#|^¾µuXæ'2sžbŠnr8©¾kµ]TM×Õ=><[³özçÄЕEƒ Ø¢OåeÑȧ*ÑCÝù‡é$ÇFFœhÃ>´÷ F=-’bßS¼|”BV4€—ëËtaà”· ìwAì¿ FCêñ{šþwÛñ¼½î®xcÊý`ŒH”Ô§¤®ÚãâÝÔ^ë—âûŠ7_èAï±HªE‚N ^8 s€'!…—ù3ƒ2°Ðq´ºR&¦Y xèÔ¯Þ¨‡áŒWNºšbû½ôdM¹çâ%ÛžþsdyÈ{sítçJM 8Éœß,½0ç5 oM  r±D„Á:ã)˜…;¬¦iÒÖÌtg°øÊ¯†^¡”B¥Úä‚×!ÐV(¬cÊÀsé³Á`ÿSNÓTϽŒŠ &ŒÔóܺð« –½€½/àiñSÕäë ˳RÔ£ p‰­J^f„¿ÿÖ‰Qµ endstream endobj 62 0 obj 2056 endobj 60 0 obj << /Type /Page /Parent 43 0 R /Resources 63 0 R /Contents 61 0 R /MediaBox [0 0 612 792] >> endobj 63 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F6.1 65 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 67 0 obj << /Length 68 0 R /Filter /FlateDecode >> stream xÍœmsÛ6ÇßëS`t/®™qY‘zÎÜt&~ÈÕm\»±rM;yCK”Å‹$ª"Ç÷éo‹ðA²¬FîÍ\Uûïåb±øa¹õ—øMü%üè´¼Ö Õˆn¿'úí¶7 Ä:¿‹¥øá,õÅ8-ù¿t Ðò‚N ÿ‘?ú>ÿO°Ôïw<øß/ÄéH´Û¤ o·:-Äo}¯%|1šŠïn/¯~oæY¯ÿ´^‰ÑÅÅH:–Æ+í~Nù…üŽH¦0Âü½Ž×í‰nàµÁ9ˆÂ ð^§?„º ÁtÇñ oþ}…ãxƒA‹†x­ ·ëù¹»Å²Ä6ØÛ J°­B)žo"Qfùìæ‡+ñŸhÆÉR^ðÔ ©tèøø·L‡n×͆Ñ,NÅu!ø¸xÉÃR¬’u†³—Í"‘fár®'B:•%ò‡”@žÍ"˜µü¿4YD¢™L§ñ8çM¶šŠ»M&.Å$Yþ3{ÕxZº©©pÆÑiµa 3«gá—[xâ×d©<Š–“dF‘¬Á»ùDÜ=Š«ËÑ­€AÀ@?/“‡y4¹NÄ,yˆ ÞüƒUíø7JÄ]’d2†¯0›ý®èvð*°¶»:!Óxñ£³,ÏÄ$ý,Æ«EàÁ}ÁÆ“V€v¸`x“_°0‘”3¯ò´5l^Öpoئu–#K&Þ4Šæ©˜ÇŸ#q~}{*SÔç—ïÅC<Ÿ‹‡dýÙƒL“¹/ÇóÍæ>„ßÓx“Åó8‹£ôD¬æ›T„b=föû0 göÇÉb yµ¹›Çãï'É"Œ—Ò4EwÃyšå.yšlÖã(Å,EÇÇ›4Kñÿ`,§—×·V#­µ~ ‰½€ÈóÁÐõ|µNî×áýqsAq^­ãenˆ7·g——bÏ#˜…W7ôëÉf±‚xÞÅËp 4YÂ/ßÝ ` ü,Ê2pg,›øà•+ù‡‡àÝwǦi´¸›GÂÃK¡»2¶³èë‰xwýæœÜnΓpÒĈ/0òÊýi²^„™øôwv}õé•'.Îi(ù+0Mâ,YŸˆ¼9ûD´…6Lœs™ãwû{#Käp8Sî6ÓiÎŒ„õ7Íìq5aé@ÂÁˆÉÐ"`ª]ˆèkœAPfÑaF1„úÃʃ}è]Ð>Lp¿]Pù‚#EáGî€âüâ,Ngaç# =.‰It·¹¿Ç¡ž_«Ì ³ñì{üÃ`PºˆÑ;X°:?º" eÜáåÕ\Ì _ä\鼓³·Ž³H$°â™!¦¤D[¼ï#È1@Äd9ÐüVPXÎo>½Âx5oÿ¸=»¹ ¼Û§MXÚ‰Xo–"ÎN(ôMˆªR4=½wˆ¿»Tˆå[¶Þ@Jp%*7K`àz<€åɲ!ZfóG‘­Ãe ‰¬–*À†@(c-¿Äëd¹©Ø¤rÏgxÊ©AZ‰÷â_¸Ì—á"ò¢¯Ù€ÙéaòÂ÷;… >&Ús`« ¼ìDL“¯ ÎÉoÖkôy¯£1 ãæ;ö€2Ÿõd=%kô8ýôêVf4†Í ýûC`X äÛëkïâãHl–82Î2ƒõL “rl‰5“8…w¤æK¢IÅ`ºN£ðWhì@ŽÍlPY"cgþwž1q5ŒÀÄ@ø»“0Y…8[´;À¡êP~ 4´ÓSœJxËÁ JVÖ‚6™x>’f¨›yã&DN#1^G!€&M¹¸…$ÓÒ.V62)f1Ðo ¹u z!#±xw¶éʃ˜«M)Í‘²9 …¢ß;LFŠºÛ»œ \Ëæy‘àM ,5„kº Ç‘üÝêGˆÑÝÌ!XX½p{ƒÉ å !8—¯e¥õókÉGü‹I"šT\(ž2ÑÚì:¿ý¥%f“Ëö¦ô NÓ q®ùñíõû«7#ð­©¢ªô3üˆw}¹øä*—ã%Ô¹áêG\ò¼ëã¶å¹ $ „7Z¬²G¼oÌ¢|®w¼)(¿sõ{Ð/à¹Vwß#,pΚ|›ƒ0rpšÌçÉ¢S.×ùØ[Þ°åÑfÙ'hºô¯×ížïõ-_ô¡‹¿ïu}hT@èSÑëå¿ï ¼6¶0L ´2°ÛC–tÇ,-ŸÆÞ-ŸŠ¶'R¿Â†”oãOèAq˜`ã/ Âä·ЛÊãÐé`ØŒ8A`™zF¨Ú}lv±„Z>Ò#2¥E¦Œ@=»7fÜgvý®ð!PnJ]|Ív‰zmõêJ¢“G ã4h{m3]¯ëÁô¼Î e0ôZÐöóu¬°CH9¥ÌéXµ¥9ŠUÁ7ÙÚ¹XLª>n+7©n(» Ý. VURþÐVXŸ/=ˆAV¨ÚA»OL*e*˜ú›Rj€Z’Rgp÷uß.‘ªœ:`ˆÙÓzÐŃ–7Lñ´‘¡¶¦ÜØ®£õ¦èÆöˆIŽ-*´±Ì‹ªKi!c°5Z¸ZDžùÔê5­aÏ·Ûχ ¬©•ÂÖ(µk?ÀÆñvkœJÚÌ¿c«Û¡lc ü<´º¹<ÛAhÍïdƪ“˜ãF6t*Àv²sè ëÈ¥ZGîñ8lض·ËæuH&cƒÚï÷U?oþAÏÆY<5;†Â‚Ísé'w ðÊ¥¶ƒ·(ssÖPÆÈ³[§?Ü©€r‰[ƒ_l§žÀ>HyÆ>!HógÕ…²åVÈÚvoû£0+ZŠo¹[9¨ª7©søVX~lÌå›BW^ãH¡Ã7eMñÍqÍ䛲–WҚ÷ÂRfß\Z’5æ_¶mòlËݰeñl©àvÚTònÖ¡«Å–¡«ÅVµŽÜãqغ‚­ìLpsK>f=…‡N²sn[n]½9sÜúæÜ*q«×Û\[ÏW‰O§aÅ9Þa^¯¢u(›p·i4ÏàI >¬ÏI^ 7žp‹ÅT¾üŒeŠUƒ®Wh=«Ú –Ô9lSQµ‹´ ××n,tئ¬)¶±H‘ÁdCîrXè°¬q™Â¢JkÌ6Zl+0ÁšÅ64, ƒnÛì¸:‡m‹–5Y‰[/¡&+q+Øm¢uš \r{uº¯ý²uÈÕñ„÷Ū¶ì°Re¦)ŠA" Ò¢e²¢ëŠ©U¦(Æ¢Š‘1¦ ŠÖ¬éšI1eMUhlÍ¡Ycб¨„bd)ÆB‹bdËXý†gÅtÐb­~Åì¸:‡bf¨¡SWcÙã±Vël’Ú:$©mï(0mwðX´õÐ`ú§“ìßœ¦à–‹ˆÐ’ JÜê¶ö}²p *°Ý鸡z2MádÈÕÕŽgËsrÏ~AT´=,¼¾óg3zoÎù¯ñ}¿0ÛÀÑt'ߪàÚ(mþ*¸ÂùCyNXqË|jK QÁ•u®D¹±®ê“*Çä©Û€­i¸ZÖ\Y”#>YÖ\Y¨ájZÓÀÉ}S`r¬1\µµÊ)Ú}ò¬²4õ‡8ìgÃuàáÕm¸š:5¾@é,¸š:†«eOµFgÁÕÑI¸ZöŽ×N¯ð¼ã%ÀÜr‰á>º“¿õ‰” Ä­Î±áZâÓ$o’pº…øÜ£dNK†„©æ°Â/§ƒïȃÓ7} ¯µúôݾu¿ :|zåLò^}3ü"‚Â+ùj‘ÈV—"ë4ÁHcŒEÁLcL0j‚YÖ¨ñ Òt˜cYSc¡&˜i Æ"‹‡¦5& «Ÿª:ž©ªË´¥¶‡V¡ñwÁ Q5Á ‘ºŽa‰‡X!² f‹^Á%²ã ¼*윥+qëØ£ù³fŸ;»ùPVîñ^ƒ©kzüβÃ;DÖÕ²ŠE[YÅÂZV±¨’.ê$°ö­–U[­1«XXÃ*–g•¬‚ïîb«%¿ÎCªN+VÏðû}ૺ/¼Æ Õ™¨Üêt´aЦ¢uÎD°Ô™=Ó1‡ðV¹H{¦DŽgjçÑ—¬›Mó’&m•_´=iKù…ô'µ+Òö¤…Δ“5ÅîªK’¥\¤G©üÒ—dkò»`òKRNb²¥*Yç‚z ;¹È¼ •<®µJ!Yüíÿµ×½¢ endstream endobj 68 0 obj 4473 endobj 66 0 obj << /Type /Page /Parent 43 0 R /Resources 69 0 R /Contents 67 0 R /MediaBox [0 0 612 792] >> endobj 69 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 71 0 obj << /Length 72 0 R /Filter /FlateDecode >> stream xÍ[sÛF²Çßõ)æ1©ÊáàU/[%Kʱk¥UÖvö¤Ry¡)Èâ‰$zIj“ì§<éô §gº3Mˆf8†¢¿¹üÐÓsÁ¿Ì?Ì¿L17ãá`8çf2›šÙh48-ͦ2ÿcžÌ_η…YnͰþo»„0”ã¡ýSÿ¯ÿ ?‚¥Ùl<€ß'ËGóæ£P—  Ç¥ùøhþò}1šÂ|¼3ß|xwýÖœ=ì«ÍÏóá·æãÿšËµcÁ°½SûûŒÓ7*&f}On1&S3)#pJa^æsx¼ñìþLN îàÇÓaq:…ß§® @¦å`:6³aY?¸™MEiŠÙ`Fëb¼3S(Uúýt>Yó^‚·±5–|e ¥cVǸÀꀿmu”³Y\_¬þóç4©²8•e5¦¸~‡EP–¥(©Q9j§mäÎT('0åKé¸ jõ™hPçëÇÇêéFuâÑ ü( í@±{ «ÈÜÕ|8ÁN´_‡ÅçÈFöGhÕÛ* —Ú—‰à×ð&) pª¾=]çv !öZ/BÏ D0·fY<™5¬¹~BÖ°aGÖÊbn¾ßµ$o ª?²5cc#Iøµ¿Âçl!µ@½ÍÀ³Fm˜ ˜é\S[—EÇt®èHçú„+”¼íÑsH­wi¯‹×úPäó÷—ßóð¡~u)/ [GQ€_ñ‹bp~s¿)jÄÖd˜|òò¨«ùú²LNx5þ›ôIåŸ,§7_ñBµüÃú;áaàr³Þn¡åÝU›êiY™çÝêaµûCº™ UÓˆvœûyXGpNqŽð…œcÆbι~H޳qÎYsœcÖbÎQ¯vñ€³q.êÒòA95ÑqŽn;âœC[¾Û [¶Û{Ρ-,Üéã}÷Û,¾¸NÃ×iøRtèž{ŽH×|ËFÜ}qñ1jê¯Í.pª‡ìJx5í–]Xy‚]s<›ÛêÓóçÏÕ&ªÆ½ƒkyêT6LÝÁ±¦Õ:X^ýW%–jÄò"®4ã†^¨Ë‹²Ö±¼0O,/ œ WõØÑîtŠuwÏ\À5e:•XL§+¯“Ä’º~kaDp±~ŽÚúk# ¼ê!²^ÃC™wíònÍÐÆ[X}‚Yk;Y ~Q¯²Ql dMNE‰ ‹êÈ"¡Š,¹ŸG Ud‘(kEBY$A[ù k–`®É&«Ý=³áÓ©,c:•eyd™ÔõƒeÓf¾ëâ&jé¯|O= ¾^uŒ2¬;²O‹ÝòÞ|Ù¬—Õv»zúl~[íî͇ç/ÕæÃó'óË7~|sýî£}3@Ûüò°XVvnà—om•ŸÔ³£ù™9Ûš5¸ÁlQ.Pà @ÐÂtܰ³¸¤´AgŽ54æ¦ý¼0‚›³†Ù0/JàÈYì¿FpCk.ëïEYk7zR7´EY$’ -¨ùBƒ©—iá&Ý=Üd¹1]7zÐz*:¶¡+%ßå³2{IÒsH‡ÚÛ“Ì·Î%ÛÖ¡ƒ; =k&ž.>üíÍÍÍ 2bÖ{rL$TuÌNû&\ŸGÜÅ&ùzÙü„WóCG—fxŒl>xkÛõó’øwëù´^ïÌûæ$ˆ²è䤱èÄf…HJÛdsèÐ`¡²‹t»\ßÂË)‰ÀDÌ.ê¬õ’/ŒØõTéZ~I·Ø…Öˆ]$riZ#v‘P° mù>/<ìÂçÄœ¶Ëäs]Ä.WlHs®K2É=«¢C{î9"g>ïvYc„ˆÃ4±å–]óD^ìÇë"D¼vh^õ04KxUv .¬=šÝW¿›ÛçÇ/fQ'£ŠLŽ&yÃïêWí’ù“ÓÉ|/Ú-/Ô åEY̸ѤjÐò¢¬5-/ÌCËKÐVZ³vÉ|X×èG*´˜N…V^'¡%u=V33vyµôWGV3ÕƒÄØ¼éUDZxÇZ6'VÝ®vëÏ=&ùˆ™rãClÚcycyÑ^\Ñ]U\‘(  U\‘(kpEBW$Ù‡«I»Dþ„éT\1Š«¼NâJêú«ÓfòëòêÝÏg ÜjYoÎ>D^½öè0áUדX"ʺ|Xýga>ÃZs³z2oÛÕ2.·†‡“I»¼½×A¯…¥ú¾Ïº½~­½PEWäZs@Gè"¡Š.íE t‘ÄC(Ï{HmMl†£EÞžëTtEöXjK …¹½q]Ò^?ÐU ›+½.Ššúk‡ZÖ©¹ºÏk¥Üm…õ ‡fíº‰”O,µuù“]ùeãÔïÌåO°¢ââæœýtvu%bØ“ƒ“ö“Q»¤½×EdÞâ’ö^” !°N|yaD6gÍ¥y"×8Ùœ5|zkÙÐ%ƒöZ£ Œ„‚l’$I =stØ$’!›,7¦‹ÈFZ'í'L—$=k^‡ö(ñ%uœlèߞė5ö‰¯æŠ"›ø‚ ðç%d³ž3§¾¾»Ú„}ʩ̥EÀ}í˜,åVÇ™/pi#?ÁºûûÇÅæWSý^-aýýú 6L†²kÒæ¶°¤Åd÷?=<ëˆ'´ôpv–"ÿ-& <`ÂsB:•$ÚËNªì$*¿ÍÉû¦²s¯5b' v’d;ǧvƒXÍS¥¸±žqï®sì$`'×9v’YçØ©è;#e§´× ;Ër`ØBg«›»r~Фs»^Ú„zÍÏ„c"‚qûÅ_yÿR‘p«8 _ÿª©–ð °¹Y®¶Ì ÁâÚÕÓv·y^ÖÝV­6vfßÔ¨ØÆ*CuoÃÔœ×y’a—s= Ç·^L„+×uë(Ð =É„5Œ½yáHæz:Zso]zO2nõnþ SrkŽdþ¶ŽdÜ–‹ž¼=³ˆžÓ.\‘Ì.µ(`÷©,7¦s‘ å¶°Ž™ŽEÁ=k^‡öè9¤Ž¢À`oÉ80Ž6ýYŒ l¼Öa ¯ïö,Ë6û¯b¸C¶,K¸Õ9Ë>y–Ù}ʾ3ÏO·ÕL;Üšín±{†e¸«ÝÖì¿ÂJÜÕÓnmËåúv³}øfûg®ùàÝAtô\§tÐ#Q@€¿¢^ŽÐ#a½Fç®qL9kzd-‚Zc Эô¼5ܽɹÈ`Ál è¡gz{’„ß"˜¹rcºzô õÐwÌtô䳿uhžCê8ôÐÞèYcl”Yp14_±y¹úŽf‰ð ³?í¡gý'׎À%\ëôš%Ö=ôš>éÐ{Z¿*÷ é³.솭 î‘Nå‰<íBä:°kPå‰ÀšëåÞ.á‡$T¹G¢¬5â E°‡`¼`…–çr¦íÈÅt*¹˜N%W^'É%u=!×|Ö¿®Î...ßG/jȳÂ&üIØ”ðÿ?'øÇú¯„[ビxGZ.—ðÉnÿŠj0™ž³-4JÏSøáȉ Í¶éT`‘(`*\ `‘P‰ö‹„*°H”µFÀ"¡,’ ­|¨ ˆÆ‹éP‹éT`1 ¬¼NKêz¬Ó:}"C­^ kë¢1ûêû#nu Ã*£ÅûÕç{ÀÎÚbNš¬7Â÷Pj²9¼‡Â7µ²o!œhAcnE2Ò©$#QàW¸$#¡J2eÙC¡ U’‘(kHFB…d$A[y’ á‹K2ó•&Ó©$c:•dy$™Ôõƒdå°™ùºz÷¦óa£õ«²î‡)·:N{¹ {X}Ú,6¼ð+>«¹ ñ¶"éTr‘¨¶IŸ’r}Q‹„*¹H”e ‘‹„*¹H”µFä"¡B.’ ­,¹JØbц\\§‘‹ë4r):A®H×r%Ò]7g]sT{™ïJ¹Õqf]Šç ícOhk}Ò7;&¸e›nnyÆ-/ÚÇ-/Ô¸åEYÒ8ny¡Æ-/ÊZsÜòÂ<·¼d·àóª­¸Åt*·˜NåV^'¹%u=áV‘HvŇ ½v¢«§ún%Üêø°o[R1µnW›j ‡³3à*¡í¶éTp‘h/¸H¨‚‹DYÔ¸H¨‚‹DYk.*à"É>p“YúW4¸í%1j¹>»å\Äå,¹µ9‘„»X"X9K]¬Š(ËD–ëÇ(¡ûꤧúHª„[eÇ)y[qQ>2ÌM'ÚÆÝ•ë õ`Bxj}ŽŒ¹>°ÀG†Þš•°†{Ö½(ØWh 3¡^èAÅ­±^L¾¹Ç‹¬¨¨D¨¸-·ŒÞß-¸‘¡³J…Se™ÌÎG b¢<¨˜ÈÝÇfÇ"PeDTRÔPÁW¿ã•×ݧ°À«¸û¥òȯ|ÖF™p«sR%| +°Ö¥n«ÇOí–@ìÉÃÛnÅ»²ÿÌ€]+EÝN ò:•Y̘Î,ªÌ"Q`K¸Ì"¡Ê,᪶FÌ"¡Â,’ …<³Šd>bå™ÅDyfeD’YRÔ f5ÓW×pºì»øë#¯`ú™ÀJ¸U<<Κ‡2áSÀV}è­=,£Ú|µýÇ´ãVà" .Õ6) σ·S©$¡ .À„+.ªà"Q°®¸H¨€‹$h! ®N.J¤³$¸¸( ..Ê‚+'àŠD}׸™¿ºîZàT#Öêþ\È2áV×Yw¬>1]H¡•±ì:S¸ñm¸ŒþÉ•\{â.ÛšÛàËë4|ytk5îòB _^0®8¾¼P×áŠãË óøò´Çœ´_L”Çåñ•I|IQð5i&µ®Ï;çxÕG~%Üêš_X‚_›jqûÖŸO1hÔ­(F:•b$‚Þ­SŒ„*ÅHh®ÅH¨RŒDÁF¸#¡B1’ …<Åà  ýc¢<Ř(O±ŒHRLŠú@±i"ãu~Þõ‘Œ%¸ÕGŒ%ÜêcXc†}G1X½ëûö>³ù°Úî`gø1B0hË­àE:^$‚N­Ã‹„*¼H ®¼H¨Â‹DÁF¸ð"¡/’ …<¼ Y³^L”‡åá•IxIQ/à•H}Ýü³óÝ>å4‘ùê~«uÊ­ÎéÕ\ºõ¸þwµ¥#-à‹íÛ]õù/SÞüè‚–Ü ]¤SÑE"èÒ:ºH¨¢‹D1áJ ‹„*ºHl„+.*è" ZÈ£ ŽóÙ.&Ê£‹‰òèʈ$º¤¨èš5“_7ß^^]u}ÜX ž5B¯,˜O¸Õõ>ëTQÝÀ™7kóË7ï«W›íê—oë¯×[Њ›Ø‚î3±¹ã‰‚t[¨ $¼¨¶I9{nÌåì½ÐcKXÃ^ð‚v½kn%—zlqkn„e­¹ÉF/tØâ¶Ü /A[[ü9q_|¤¨¶˜LÀ§qêCåJ®ØÌ7HäD[ìvÀ>`kÞLzýð®ë˜—àU‘•pëà¯Ôç„D[Rñ¢Ó*8Žë{0ïìãò~ñô¹2?àéˆÇà4ã&·Ü½ŽB·Hç¹…É-!_l ”À ØÜo{X}WÏ-aÍq‹DÁF¸á =·¸5≂p%Â-*áIÐB6Üš×¹úáœJÁóêæ‘m ±¡)×D~í×a%`Ey{QE!¼ÜªWîXÄÊ{te}j¯·þ8Qä™{ y¡V›ü–®yÕäu~a•{KáFþʵ4\Ëç…Q•£5Wå¹[¢¥ å_pòvn‰ßkàm̽Ϣúw‚xqx;ød¢ñ gÁ÷LÑâ?þÆ„iÆ endstream endobj 72 0 obj 5499 endobj 70 0 obj << /Type /Page /Parent 43 0 R /Resources 73 0 R /Contents 71 0 R /MediaBox [0 0 612 792] >> endobj 73 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 75 0 obj << /Length 76 0 R /Filter /FlateDecode >> stream xÍ[wÛ6Çßý)ð¸=g«ŠÔÕû–:îiÎÖ¹XNÓúôE‘éD[]INš~ú`f@†²¢Šì>TMþ;˜ü¤Þ¨O*«~·Ów»c5 Õ¨×ëœçjS¨wj¥~¸Øfj¶U]ó¿í þÝNÞïêÌ}_þ'Xúø›ìl¶T?Þ¨^5ðïô½n?W7KõÃOY§«2us¯þ5yqõ³z¶ØMç›Ûq÷;uó?uyc+ ë+í~üBÙP­ïáËK ûÁP òNœƒVçñn¯?:‡gÐ÷ðŸçÝì|û 2Ì;þussãj4èd¹ÊF5Íx¯†Ðªü÷Ãq§§Í[ ^F÷Z²–ŽÙý »þ­»#ÂÞx9]e¥‡‡vÚu­”õÆ0lÊfè÷u«q3adý¬ã´To¤Ç!Kô€¼Ç‹¦l;¡)ÛNgž_æ†÷¶ ‡­ÓNƒl Ûé,µ—íöi¦Jq3%S6îuzn `m‡>„¨ü¼Ó…€Ì¬¬QdÎ6UϘ³MåwáÓšŠ"Üi*p(6¤&ó¿ÿ™!•gç~[õ€)wØyž{-ÕË{ÕæÔƒœL•í¦l+w@¡?#êb½\«CÕ™E3ð 7‚fö@¨3Èè׸;À ª×aóÙØò­Z[D©~h°þž$YN™Ëƒ‚›ÀBŒZ+BÏ2D°kM³x0ªX£8ak8°ky6Ö@¯·Æ#ÉZƒîl ú8ØXRþµý…÷¹‡ÐëŽb4÷Œan`GG ÌcÝo:GGMÇ:Š j”´íñ}ø:Ýï¾½&\=ˆ¡È¯¯/yq0YxPèÆ8úƒü ‹W¡W8tKÖ$˜|öô¬«úøÒLŽx•ÿ×o)‘~;Ü|ÃUóûïÌM6Åb¾œ¯¦›¯êâõ[µ+¶»­ï¤¨žEU¢\7«§¡ÉßHPŽá…”sŒ…”£(ä4‡¬”#kD9ÇZH9ŽiÊÈZ@¹  ýu™‰Ö˜r|ÙžK9´eƒÞ³¥ƒÞRmaãǘíÓß&áåê$x¹: ^‚Ý£ûtí€W?¯dÝ­€øU×Õ³‹0͈<¼"^ ›…öŸ¯íúq3+Ôl}s”õFaÆÐ›jëЋ>Š8ÈtÒZ“ÏÁ†ð\1:‰`Vñ,Ì %‚Y%/UæÐÄ %‚YQÒÌ Ó³’’[å/3“¤ÆqÚN×L¥_CG'ÌщKë|‚ùºvlIE~]˜8uæ.UàÕ|æñªß,¼Ýt¯M1½S÷óE±U÷›õR™‚\±ú<߬WzJÚQ“LJ‡õf·U_æ‹;5›nîTñ×Ãtµ¯Wj½úîìi5»j–ªsÂÁ zööå‹ßÔtu§ÞÍWwë/à°U»;_}PËÇÅnþ°àj×;Áø‹Ö4ë@;Ô™ø eZXdвP-‹SiвP-‹’Ö´,@Ë´•N{¦hEWÌRÕª‰'Ír‡VEmÆ*„"•J!–Uh‹¦ýI•X{E°¥½÷m51»æƒ ^'—7:.ƒQ~jÊ‚g-¤lÄ«†ç·Øƒe·ÅÙ:_íŠÍýt¶WUTÖwéé­äz½Å™•ÈyŽNY'2‹E`Bf Ef±ˆb>Í,ŠÌbQÒ3‹…³XR6Uù‹ *©C(•A¡®˜L ­JL ­JL S*ŸY®ª)á–“”°ÌÏ*Ìj~ZñªafazÌšn·Åòý¢Ø(šàêü뚺õX³[Ë{Œu"ÀX!+Œ…"ÀXDHŒ…"ÀX”´Æc¡0– ­tÒÕÕ Q 0«fU"ÀR*`®ª%U’®ÉϯÞM.ÃØ©s®Ñ¨9WÄ«¦ùe:Ðã×öãú‹Ú³Ýz³…yªšª»ùöÏ ‡ŽÎa´ÆW}©jãw/h±N„‹t¬K§5´wbÈBZ,Jb†Kr,¡Å¢¤5† h±m%¡5€Õ‘zh•* Z¥J‚VRåAËSµZcS3 ¶S=ø©NU®æKq¯ÆÍ–â°ó<`Ýï?|€|K'ZcØ’teŠUvùÀ[3FVéa»«¬Nb•Õ±Ê %VYQ’.Ä*+”XeEIkÄ*+L³ÊJêX[÷`•U‰¬²*‘U)•Ï*WÕVW«Z“×——MÓ Üj!­"^5œ^a÷y´zÜÍóÝWµ[«e1Ý>Båd÷VAëÙŸjûPwzíþ£íªõÓülí ¦½9d÷11 †wŠiXT½¬ ˜Æi•ÙÔaE¦qqÚì]µÂ€id 7uXQ„Bd ÷ÿXaÀ4´F5l+JZc¦ñzL«0Ái4/ÿ²U¯A?¨Ôó~/¿Õ¬*`ߤÙKíÛ‚ ´o‹ïÒÚŠ’6sx¶\¦¡_5•zíVt‘}¿ýìÕ5+½:ƒö é1¹yv<¶… LûÎnyïCÝÒû•cnÅÊ5'ÞÊs«á½Ú¥pÕña³þ<‡ódaËéN/rN߯wjö¸ÙÀ‚èâ«Z¬!M»3ÓI“Ú™°I-ö6ñ À M½Ê&645ÏX'òŒEµ*àÚ¬ØnaïDðx:hV ƒ7E,ŒÊÀX‹²|y@G(£è+cÚ¬ÇQÌ b‘5ÊÀk¥¡5ÊÀX ­9±Ì¾Å­1±¬µä¶Zë~éOùËÜ'=2øã±·¨«ó¦ Þ„ðÛÍÑ9ÔÒ:bµˆ£s¸UÚã{MëГË×1»J{5¹˜6ÆIÏáï:o™\,‹ÃÞ^ÿzù{0Ü…lL·î78VMM6Ž…IbØq«ivaz“Ìí×í®X*˜]~.¾=y¸`ìºñŒ©EÇ NµXgÁ…šJ²±\®1[³àò¬•aÊÖJÔxÖ\l͂˵æ³lÁe­!¸\[NÀ;¶hêèz†àêŸëÒ}=¸\YaÀyàru¸®@×på‘ÊX+ÀŽUÀÕüÖ‰,âVvð<²|{òз޼ó)º{bb:6†ÿÞ ÐÃÙ Kš6a ,³:‰eVdr~ƒÓ5F,³B‰eV„ ½ÁéYC–Y¡Ä2+JZ#–Y!Mñ‚Èlb™• ­4Ëà$ƒ½Xæèèþâ,st"ËÒ:Ÿe¾®%,ëU÷M~Ÿ\¼zÝxžU`Öü²dq«é, ûÐËÂf뇯ŠR±Ýf:ûs«Þ»/E±ŠÔÀ'Š$“kÊ`<ï3Ö‰0cQ-ÌX(ÂŒEIüP ¬ÏBf,JZc˜±P€Kê`6Ð5ý=3G'ÂÌщ0Kë|˜ùº¶À,²' `öú*&"'ŸRöªu§ê·è ˇûà ´ÇZØ™îQÖô‹æðG%alfÕrØ ^!Ú°ùÕÊõ¾°»Íüs¡žýçßêùüÃ|7]¨ëb[L7P4»¸xmÞ@úñù« µî·,\öaœ§ ‡Y –ͬ.€ÍްlfEÈñ4Õ,\Za9²†³O+Š`‰¬Ñxâ{ ‡ÖhöYo!g­¹³Ï œFó26ÛhPèïC& ç·›£ Ç7j/C{´|Y–×ø^{Qræéë\È¡5e3íÜQ«S¦lÖ¯V§tÆ» pîà NöÕoLÿNLá,âZ(q«iÊKá2æEšrµ[Cµ îsgæ6=ZÏÔ‘ãD¬71µ«'¦¬1Ç¢ẔPÄ‹j1ÇBs,JZc̱ÐËå|̱m¥1—›†p_ù &œôxpt"æ]€/ô1—ÖaŸ2æ|]K07¨Ö²n^\]>á½u݌ߣ¤L†q¿bÕ¡SoÕˆ¸Õ4ãÀ¥qð%ìÑPŸ§›ùúq«vó%lž]Ûmþ֌ʒOUš–ÂhN¡ Ã26Ö(ÃРכû,2,ÕØX Œ¬QÆÆ¢|Ð5ž–²0@ZsÂÛ½Q|Û‰ÀaFeÖš›±¡-Ž-e¶ÑtƇ‹%26¿Ý]€2¾QÌØ]€2ÿ^Ó:´Ç÷áë\”¡½šŒMcdo¡Þ ßµ|"Ê´óßàWbüªTØZ°\q«i”az¶ð!Ó£±'A™€—‡õ&û“ÃØ G/³á¨Ó1Ö‰ cQ-ÃX(2ŒEµ c¡È0%­1ÃXè¥cÓNì;–dXÎ@Û‡a®Nb˜«“&è<†º¶0¬ZZ{ûòâ ÙXrȺŒg²±aµ€‹ÁSgc·~y)—Âlìqõýl󸂪½ÔÙ®¡G¯…ItY„.+ªC—Jè²¢$l(ý²B ]V”´Fè²Â4º¬m¥Ñ‡‹í….G'¢ËщèJë|tùº– kTÝgööååõ³`´Ãl>U-ûgØ~UÒ¯,pFÜj:ýÂôÒ/`L·¼#®Ï úò rÁàÝ‹\¬ÉÅ¢Zr±P$‹’¬ar±P$‹’Ö˜\,ÈÅ’:rõ“¥~LâhÂÝst"¹H®´Î'—¯k ¹ÆÕf­ øU!W &Ž·ßgñÉ9›Çôf,aMaÀJg\èÐÙ‹_¬ùÅ¢Z~±Pä‹’Äa~±Pä‹’Ö˜_,øÅ’:~õLCÄjø>¿È/G'ò+­óùåëZ¯ójáëíäMð¬>yÞ^UèÕ‚¼+âVÓyö_˜wm?=ÅßÅ“ƳÊÇ©°Þñ¹ºX'¢‹E&b…š½½ªˆ.Çji×TÙ],ÑÅ"ÄMd—-£‹…ºX‚¶Ò“Æl¿š}Ïщèrt"ºÒ:]¾®-èªÖ»Þ5®v»Î«n5.øÂQpjö—Íöá±Ùp†ph¶Ùg;9–.stvÞÍ+§zkÿ<&».1„Û+1dHW•,!ñ®qb®*Ò•EI2]Y(Ò•EIkLW te ÚJÒ5‡¥]øØ ›ê©á‹c/1tuDWÖ! é¨lWGteÚ£Õ_AçÑ5Ðéûðí5±"šwÏÇáŠè»ð«K§Î µS-Ì cneóõ8oOÅ|ú²†¯ ,§ðò'œ> ;váMƒân'9OÍhiNíèú6æ‡zuï:¸§Ãê$‚YQi¢üåÌ %‚Yr"’ÑÁ¬P"˜%­Á¬0M0+A[i‚ÁIDÁx{šO0G'凹£“òCAç̷׎ü0ÏFú îäØ<ÁÀ© Á~þåu'^ ÕmºÕðWU¨û¼™íÇbñ`òCs˜ã»«'”å¤ÿõ0ß‹]¬ÙÅ¢’Xå/],ÙÅ¢$m˜],ÙÅ¢¤5f v±m¥ÙG EØ•ûàrD¸èSÏœw9"‡Zd‰“®„Èç•/b^‘¥FÒ­޼ aõêºñ„ Ü ¹ ºSã*âVÓ¸ÂôpùÖR}€¯\ýz'£Á787ú‹œ‹ø±Õ„º” Fq[†4”r±Îb 5•dc%$y½«×ÎzWÄ­Æ!f:ЃØ?±i’¯ç¯bik,éÒ#ê\©uÐÆî^°b+›¼ê&#t>½ª+ÇÙðÂsEаbQi£üåÁŠ…¬X‚’°Êàð¥ZX¹¢$¬\QV)‘«@ÔXõ«…­Ûga é䬯ژpEÜʺ‡~¥äHµùˆOð=àbs;}PƒNnR+*ÍÃÐW÷óð!ø 0¤^fé%]‡­‡uŠc81ÄÉ£Õá-¡Vñ&]<Ë4[·­0àYÃÉ£•¼±¿ÈN­0àZ£É£Yüí¶Á­QÒe…Ç*,pÍã˜m4˜=Î?Oð‰’cœ|¡ƒÍ Ê4ÉX'’ŒE‘¬Ë' E’±(’'‘5ʺX(’ŒEIkL2 $c ÚJ“ b/M2¢hj;@SQÐ!…è‘ú £¸ýñ‘ã8ÐSgzx´{Eù¹Ä—{Ó¹¤ËbîI|x±¥H'þ©ËYt9ZãD;qIÿl)rI¶vÞé9 Œ ÿ‚ö)vøQâ_Ð<èÙB´øæÿˆ„v¤ endstream endobj 76 0 obj 5232 endobj 74 0 obj << /Type /Page /Parent 43 0 R /Resources 77 0 R /Contents 75 0 R /MediaBox [0 0 612 792] >> endobj 77 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 80 0 obj << /Length 81 0 R /Filter /FlateDecode >> stream xÍKs7Çïü(ŸìCÆóä#‡­²$'«Jd9–’­¤|¡È¡Í5 ‡\¯÷ÓoãÑx #š6'{½ú«ÑÀ ÓèÁ€“ßÈß$“2MÒqšŽI5’QQ$“œìjò/²!//›ŒÌ’²ÿ53øƒ4ÉË”þÇþ¯Ô?ÁÒhT&ð›l0[“‹{R\?sÐi™“û5yùS–¤$#÷ òüîúæŸäÕj?]îþ§/Èý¿Éë{æ˜2L[êÞNén(‘íz¨š–I5$UžàŒÂ8OÆcè^9šÀÕ†`ѱ¿„÷·‚?§¾ÂOÚß|œòîæI:àÝ­’\9Ðm,¶Á.ذ¡Ûb(É×[†‘pY¾|ûò†üQïšåvC òy¹ÿH¦›Oõœ¬ëõv÷å©-‹ùQf|¼à'›UeOÖrA– Ù¬Is˜Íê¦ÙîÈ~Kدò$OÈ+2;4ûízù?pèâúö޼NÉÍ«Ë÷/è_/7³Õa¿†?\LgËÕr?Ý×d½/ËÙt{1xÚÜ×ÅêC™Ї>Å— Òlɼn–»zž{èȼ^L«}=z|\-Á¯å†üüúÍåÛ›äêÕ=Y@›/;^“õ¦Þ1Élº!594ÔÌÅíí=üEry{s"Ï+˜¢–ç kŒÚãj:«ÙE`ã¾ÚNçõÆù׫wÔe˜ô=l·{²ßMgŸpS2_6Ÿ’Ó¸Xå¥íâ»Ãf³Ü|à³Á55<ÁÜU¤šÐ¦]•Œ·f¹þ™î÷ÓÙG2o>‘ÙãºHàƒlpð¤×'48µ”•¬Á]ÝÔ{hë ›y£¬fFŘsÄê—hD„ùSÛrÄ7tiœ zÚZ´yTF÷fº®ÕuxBЉÃ4ð SV iÂ&Ç©,Ú¿‰5Y1¡ ¬”#úONžÕ1¯„9/iN¯¯Î€µ™UÁ ãÕ&ƾËpQÇŒ‡Ë7«²qF瑇JL(s(²q™@ò‹“/Ÿ$)›5¨¢+>¿Ð Xs1»ZޱĮóz¡=»†“Š¿9©ÙuI^—áj-`p¸|³+‡ÃUЈœ7|ò<Õ«È :8Tjn¡19TÜØ7šYã’•=³.·ëu½9nváõ¨dsã1¬€MruD?Áš1ƒ[:› q)´ÇYgÙ¢Îdf£ÕH5Ê!К aÛàböBø.… ˆ4H§8ƒ°Ô‰y »ĉ ò‰`™«ÊQ2¤+P£Ãã´²;ÜAh\Œjh/j­¹Š#mé`¤qNss™@M'u|ür1.~·‡ýÐtàí°i¯ãm픘¦ ѬÈDú¨ÀóêîÆâNøþAÄà5FÔ±‹vÿÇl"² ¬›ðû-'¿¤j:þèÖàé…¨n1‡ #A:@â€×–¾âöÁèðŠåÒÓ¦©×«zgz¯„&ˆ7‚;BŒŒ,»:‹x d”x–1x"1ÖÚÄCqâimâad‹,AlÏŠlÕ]·A¼ÎØra›“Pæ,4ŠQ® žÇ@fé¼ ÓtAùu&È4]¿@Mk…úêîîúç7ÖÔ? ËÆ¢ô  ›À‚ÙòLÜ‘%6¾=ËÚnåËâ¤5ó_ßÑ *ÍHº )P$ÂO ÆH:êËð0ýGaŒ¨SfÔ'fIÂ0)PF‚¤·Ø*šë Žo¥˜òX:/)4]~I M×+R”ÃVÊsq5t+Ï p­•ôܽ}gyöÝAáp+K%¤<Š,É»ä¨ ’E" ü¤@aŒ¨Sá¬>¤@!Lykeæ¨SfÔ'ƒ( “U`$H Xjt"…¥ó’BÓIá×™¤Ðt½"EÕ®a±§mV<žƒàZ‹ð Ðò컓ÂáV~ôúÈX„»˜dë#‡[âY{ˆÚl;xæG«ýü=¡ëí±tí á‹L™ i|ƒ*A'¾¡.È7ñpŸøù†ÂßP§ ¤>|CaŒo¨SfÔ'ƒo( ó U`$Ä7¨âvâ›­óñM×…øÐ|Óu½âÛ°]üé ßÀµßz 9Ü*M„Hª–LÅÄ(ˆÒdÞŠ^N–º(¤ˆÇR…Ô)ŸÔ'R…Ô)3ê“ ) ‚BªÀHp?ì’–Î M…_g‚BÓõ íâÊÅ›_ú²j¶ëIXÑv+;úª©€g]² © ÂÂ2æÍ*¤µ,4ƒ"(¬Øõi0‹¨AQ_QC•X©ŠÁbØ­[X:/,4]~ M×+XŒõ€ݦh¥ÔçX8w}L,nÍ #±€¼¡+PdŠxè ÆX:EõÉH,Pcê”õÉH,PN,PF‚‰EÙ­[X:/+4]~É M×+VŒØîÛN€g-NÐÈg~þìpË~fƒO䃤oÿüÙáìpÝ4 Ø9M÷î¶+ºm’íI}×Ä#*+¥×PäŠx˜¸†Bø «zØ‘'é ½bûˆî]8Bƒ1®¡N¶§Z摎9 Ã\CUŒky·Êqaé¼\ÓtA®ùu&×4]¯¸6qTVøk @Αs}D›Ã­ ÍáÕ#Û:þ¸Û~ØM×ÆÛ M=Ûowua» ²X½w©y·Ê±Ôùfó¯ñP|ò u^á…1¾¡Îkù†Â0ßPã[Ú±rlé¼|ÓtA¾ùu&ß4]¿øÖ.]^¾µæýYØÖ.ºôaÛà¤íÖi–wiǺ1ꂘ@š@„Â&PçjÄ c˜@× b…aL *‚‰|Ò­nlë|˜Ðu!Lt&t]Ÿ0‘§íRÐåíÛ?Û{ß΀ ê\Ó —[vtÔ®¼|Ò­j,u!THQ RA…Ôy#[ B #¨:¯A ) ¢Bªb¨€Qëòˆ)·t^Thº *ü:š®W¨ÈÚ• úBéùSŠpCan¨#A¸Á«è² Kç…›¦ Âͯ3á¦éú·v]NÉymMüs¬–òv£…•Üá–]XéL6ØÀ{š÷1]^Íé)Lp^ œÇ¤"Õíй"}€9ÐåùWŽº ÇPÄÃ:¤¡0Æ1Ô)بO ;˜¤¡0Æ1Ô)3ê“Á1†9†*0äXÖ­>œ[:/Ç4]c~É1M×+ŽÁ¦(;I»ºêÁc}‡v†vþÇú.·†Ö¾â3`Ì1Xc8?u{8|øp’·Êó¬[[ꂳŒùó1Æ8†:õÉà cC2£>Ca˜c¨#AŽ¥ Ø–ÎË1Mä˜_grLÓõŠce»*û«ùxÖZjöd·Z¯Šv&™‘÷|Í `¹Ã-zÚÛ)A–ºÊë0™i6Å"78N]ቛטU3ñ¸ ™n –(ÔAf„ÍÓp lUÑF™6@†uéÅÁ7qƒâ*K¡™nN!UÈôó´7;Ëë¹1z¶HPŒõ»€c€Ùˆè" aÂ’è¥OdÀKÇx¶°t¦ƒ}òª]&»zýÓ]ÈžµÈõëõ…åÙw¯§;ܲŸ»u×éV’¯Ä ¿ìôEöÎ*ÛZOO¦{,g»š®4ʼn›¼ž&‡vÐm½éx“5cgFÛç¢é‘Ìñ&uÞxĈ#)¤H1H~R‘ ' Jao ³zd!‚Ù6Èç“4ØÆ7(?fPÌeP¾s¢SKàMªxãx: ¹`ÕJV´DÞàüLÄ›&ÒñföÒ/Kè»&ÒñÆ-ÅñFû‡O¿Œ‰uì îðô¤]ŽºzýÇõåÓJe´àÛàÈÃßÛg„Ñ£Ë\¾õ¡Tæ2û!@çç…ú+&/ß.EéQ Ѐ: *âÙä6Áä–‘ÌgœÈ|d«m4ƒ Ø*˜±Ñ 4 °nÑ€:¯AD ƒ™ì‡ Ü?89ÑQR·2K¤¡Ž6E4h" f/="î¢Aéhà–âh Æ¾ ó¡£ötý“¼òï‚”Òvñq½œDüàRÚ¯pÌÃp¬•øô`Éæp«%t‡Wóz_ïÖË |«Àr¯–ÔøŽøž€ý纆#ù?ÃЩ­+}ÌS¬tU×HC]i(R SŸ`¢A`òÙ([•³QêxDÉVÞ…‘†-dž:hÏm‘†Â0ÒPÅݧ»Z£[ 28‚9Ž4KäFš&ò#Í#2‘¦‰úƒ4øî‚V9ýºä—*h'öi·²£ëé§«B9ÜšÃþô‘/O~6è:á^¤ ¹«¦nÅ”¢2Ôé…"É"_B†Â½Pç…x6(Ý‹Ñ+jé…Â0½P£W欩[ ™%rÓKùéå™ôÒDý¡×ØQŠºµîÒçÈÇÀ¯>æc·Žf×é Q¯¦{øB xŽ~Uý>÷Ïï~¿¸¹¾§«Ý÷/¬+|T–¹Šê#iQò‰/––¨“$ã‘6‰º Š ’éÆ0C¡N2àXZ¢Î ™aP,-Q¨“L7ˆKKÔy "ÉP¨H¦›Ãåª ’éþñûÅ„ÕÔañ-Fœ$k’Ì9I¦‹ ú p¶c%,‰^úDÉtQHæØžõûMÞ{ƒmP½|èpË.ªUsš°òl*‹Cò¼4 Rƒ‰ùS› 4&À …0H7CÃÀ6ÈÁ …0H× ƒÁ U`N—åÈU£}`V¯‹\ XìÁ÷eB‘Ð­ã˜æ(—:ëŠq:Ð þØŽi0¢%p!²Zô^VÙdä²ê­êt®ñ›‚4æ¸T¸Šg¯ŠHaûÚsƒ—¾V¹1i}®YWTLÕª|^z˜èÜœ¸sèmêô5-M9f‘iMsÎ=Ý~û?ŠøØb endstream endobj 81 0 obj 4258 endobj 78 0 obj << /Type /Page /Parent 79 0 R /Resources 82 0 R /Contents 80 0 R /MediaBox [0 0 612 792] >> endobj 82 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 84 0 obj << /Length 85 0 R /Filter /FlateDecode >> stream xÍÛw·‡ßõWìcsNÃp/¼=:¶ëÔ²Ki{zòÂ8TÂV‡RÒ´}~À`p[³²ˆô!ëøëp | ÌÞ~m¾k~mÚe3L'Óåtºlf‹y³èûɪkv›æoÍ]óÕˇ¶ùðÐL÷ÿ{ø@ÿ‡é¤¦êŸýúÒý‘"-Äþ¦=ùpÛ|}Õô½fèßñýtèš«Ûæ«oÚÉ´i›«ëæO—g篛7ëíîËéÍÕ?›Ó«}b.°ú¥ñ¿3Ĉ²»¿¦=t?1&³y3ë&=%G­°ì&Ë%íÞ°XÑ?³j‚kúãjÚ®æô÷±-jy7™ÍbÚíwœšo2 M»˜Ì(辯›9µ*þ~>Ÿ,:û÷ú7ÔaÐaì‘PazÊâIÇÐêÃAÿV‡£[,äÑx»¾Ý¸Jw•·×=J­ÔösÕØ¶†a¢vo%Z¢íWªóY¬_¨?ú­¹ÏJ‡³ÍeÃéÞ¤·ßïѽ·Ñ½—5׌Ž?5׉輧<Ži­`0¡µN}ª]¶ª¹f˜Mf-o-ñš†î0¡1‡®Õ­&Ó}ŸA{¹ÞeÚöê) 5óuÖXóÕ,Ö·.·ÿý<}«£ýõZ«Wê°½F·B×My[õ]¯Z-¥ÛSõ,̵”ö™úÕrØ7•ìW/ïoo7w‡ô­+jJK®])ÑÀ‡ÖÌÖr:Ó;]ætÏ!žöœŽjc©QN#Eþèlá~TƒÍŽ_pI#Äß :Qµ-íå~è—M@3bPuðýˆ±œÞÛ® ˆNeê~ Â͆Ådì°j êüF€ÞáÀ˜Öaèld»ªk@ÕÒ‚£–ö»tkq¦Áéxh—4§ÓÃ~0ŽÎ–j‡ýxG:¥õ4¦¥£_ EáÔAiÉ!>yùî\$¦»°3OÂÑ'Ÿ> OhÊÑ‘¬Úé_dR#g‡$=RÒ™¶eéì'M†2]ÜÆe¢û$d‚_ ebb,¸€R&& N¿ e"M) d‚ {!ÎŽ-ÎŒ-+îˆ9ýÕ\Û¨&éÉ¥Á¹œ#2œçÎÕ䈡 ¦½§ï_\žŠŽMPfj"’Õp¨%šé‰µÄ|©¦`^ÿ û9YÂr9KXÈœ0“–°`Á–£€YKX°` Ë%KX0k K¹6s[ûÕžØÍiÁ9Ê‚KZ‚qYK¤9ߌ«É3Z¼‹™Ä·§oÓö4A©U¨‰HVíüPOðÙÄ|®&B#<.ë @z°¬ÒžXò¸ä°6ko»%OÂóžåìම'†å8O.é Æe=‘æ|O0®*O„E4å‰ 9¹?†'‚U«ŽY˜U×=‰'h->ÊಞTôÀ’'À=°ä pÉ€ðÀ¼'@9;¸-î ZºŒšO.é Æe=‘æ|O0®&OÌÃÊD-ž Ô‚ùÄ«Wb=ôÜÕ‰HVrÙa:¸«˜Ð±§Âo´¬í £ã®`…%U«ÕGñ„_ÀzyñÕyóóæn³[?nïïšíÝõýîVoÓVóãúî_›Ÿšß7»úkÙ¨±êŠªxWpõÂÌè©Î?Êsಞ¤{f>°ä9pI-a>°ä9pÉ€ðÀ¼ç@9»¹-î¹Ö\ÐÓ›®®.é9Æe=—æ|Ï1®&Ï-ÂêŠöÜÛ¯Eß?ÂŒˆ’«Ðt‘¬Žo:}?Étw÷w_&mwòÉwÛµ^5TC;^uÙT_˜šƒ¶ÓƒÆ\˜²Ô|õ§ƒ™Z²CÛ™€º–l9 hFµÈ¶Cz¡ít@s¦¶³õIÏþ¬°„×z‘ Sº•g«d-Ùk@É1Û™öÓíÂ9a;ow3œŽgj✶ÓñÊ×›T¼èYõÐù€º…b–b¾=•3§¼èTþÈëI.í¨{"yU°ô‹dµáñwëÏŒnóS¶\Ö"ut1pLQÇF+™‚4#B4 6€%S€£0ñ€0À¼)@¹¬Ü7E7®„=\Ҍ˚"Íù¦`\U¦ ~/ïýy}vùie5ýû?–’áå'UÖ¡ßÃéÎ÷WoäÉ{ÿôˆò0¦%ÝçKƒ-rUÌ;…ZS7ÇÒzOt^ùÒ+þꮤ‹¿3pYOÒƒ%}©ËF+y‚Œk,Š–<®è €yO€rvp[ÜÓdñW/bLõ|&¸¤'—õDšó=Á¸º<VvÎÞž]½:{/d¾ü™TÖQ*¨î´m˜V°+øm‚³©+³R/I»\րЮXr¸äІ+–\.s €yW€r†p[ÌÃr\Xr)Wp.çŠ ç¹‚sU¹¢ +(oøóΪ8mæ©öÄmûÔgzúp?¥ ÄÂ¥ÇÑŸMj#iµÒÏ_B‰¥u¾ý°»¸¿~ln¶t«ÏN¸?W > ¯×•”a9®l¹œË,¤tzÞcAÛï¬Ì<@_ܵýuvÞcÁ‚Ë,— ˆ#vɺŒ‡3°;bgô˜Ú0W –\Òe,^ÖeiÎwãªrYVRÞ¼ªaÎC‰Õè²HZÇ¿Á§dµ¿—ñæ~ýÓf×ü{ûøKsÿñq{KÏíÿÔ˜ÿúõÙ»Ë1‚KÎiàæãJŸ¬àéQžÀ’àÀ%}d&k6½’àŠ!8€yÁrZs[|²6W*—㲂Ks¾àW•à†°D‚S}¿ÿ+;Ê.°Üù‹—"³g/GÒjÖܓԦÔE¯6’–³ÚR·h?|\ØüðÅS;nWäÀe¨è8€%Ç+:`Éqà’á8€yÇrfs[Üqý¸"÷ ¸¤ã—u\šóǸº¯ÞœUpv;„¢ W‘´Ž_R¬±^mÞ>®oš÷›‡Íz÷áZ–þ¸[ï¶ë§xødèÇUä-—•šFcRŒqTD–¤.é LÜ–¤.R˜—(·›n‹K­W‘—”ã²RKs¾ÔW•Ôf‘*ÛÙ[yñ1&m”Y0i ï=xöI[$­à©}tq{=1õ˜ÝÓMÚ"iE´6¶ÚVZŒ¶ã® à²N¤vf1 °ä4pIÁiKN— ˆ0ï4PÎdn‹9­_»r ¹”Ó8—sZ†óœÆ¹ªœ6TÛÞ½¨à­f-eV£Ó"iÉ+ݵԯÆUå-—ó„…ô`I{‚OX.9¬',Xð„å’',˜õ„¥œÜ÷Äb\U¾\ÒŒËz"Íùž`\Už wAÊ»–Ϋ¸ÂH‰Õ¨‰HZ|ý!΄Ï8õ‰¤å®0Þ®éZc³~xØÜþx3îRcaòÓ/ÆUâ-—•š–\ÐÙh%©±€fð i@jKRW”À¼Ô@¹¬Ü—Ú|\%¾\RjŒËJ-ÍùRc\]R «Tç²Ò}ŒåÜ"R¤ºü^–Ïž}9IKN}F+€t©z{è¾É kÖwtm‘î›  çµæãîþçÝúV^Ò8àm)ýl\ÞrY¿‰`i¿,ù \RGðÀ’ßÀ%bÒ0ï7PÎjn‹ûmW…ï—ôã²~Ks¾ßW•ß–aÁêüe ‚£Ä‚I[‚‹¤Uà"Yí6ëŸþlöù57Œ«Ë÷ಚ¤Çzfm °¤9pI+AsKš— ÍÌk”“›ÛâšëÆÕå{Á%5Ǹ¬æÒœ¯9ÆU¥¹UXúxqõòµ8Ác&G©¢« 0IKÞLqXkÿá“òÛtõRˆËŠBKχ–D.9®! €%Q€K„(æEÊéÁmqQLG»—ã²¢Hs¾(W“(öß̯ݾ8«à])*± 5K+ø†Çèß“]¿‹¥u±Ùm?þBoɼiÎî7tcÂúîçMsñt+½éÈò<¸¬Ùéá™,™ \RD0À’ÙÀ%Âlófå|涘ٺå¸ò¼äRfã\ÎlÎ3çê2[Xɺxÿ7|x¤›VYΊ¥uüÕ^,«ôÁ6ªd=4÷Í›fóÇæÃot×üú±yx\ï›ß>Š©nô± B­¾[Ž«Õ[.g8 éaž6œ †³\RHÆp,ÎrÉ€ÆpÌÎRÎkn‹n1®Vß .i8Æe —æ|Ã1®*õa-ëâû ÞšÐQb5ÎÝ"iÉ;JZâuóq%oËe5!‚%—x6ZI, bBKšWÔÀ¼&@¹¬Ü×}88ö&åpú4+•2èÕô«Áz@›3rˆ BG2/ýLA¾üŸ³ŸeÔ‘ŽôþË® ‹?<,¨ÒªÑ ‘´dágô‚îÉ.áÅKU¸›ý»ãšëÝým³ÿ’÷æî÷íîþnì×kKžY¬ªmÆÆ"}"®gM¦1ý`Úd<˜¹ÛÜFã&óêç -ç‰Ç ¨/[›Œ´cÜeh¸ÐûÝÅ„ûëLÆÃ™âÚŸuþáôØ]­j«Ïçú (8#³ýgvµôL»0Îì†ú8¬‹‡ÝMs¾ÕÇ&<.Þ±ÄÖ‡w\½?}û⼆åV£Ý"iI»6ç¡O™Gß(§Ö5Üà¬)¨G#:ºL›BƒæÌ­¯TtˆÆMáĈpÝÀöS 7h‡N) LaòÏØ†)@y¦àùSÐ׳ËsMt¦åL‹0ˆ9¢ÓÇ{™€|;0ˆ2®dÎC7¸É›1ߟ^~ýªŠ'ˆ;Ê.Ãå…œ”=÷ÝK±´d¹ç09´‰Ê°”¸¬é±¢B¸-}4rX’8wr––ä.rè¦Ü5(•û†o=zJ»ŠW†=µJ(*%å‚<9p¨9 a¡ä}øÿ.†w”Y †ã_ ¥UÁ«biO©=ýÛí*Qæã‘E–ËÙÌBN9n‹ÛÌ‚›Y.)SÞ±`Áf–K46³`Öf–Ò»™¶ÍSŠSUÀáPÜf JÛ,ù6cP=6›EÊ;—§Wª ®yÃh”]F‹¤%§:¦[Û·šný¿nI5Æ ±vôG]ÛÙªËö×ôªqX£—²¨ãžD߬Wí"Q£–—• §.·åI `Ibà’ÎÄ–$.˜—(½›i‰Qcs?©w5«“‡?%P\b JK,ùcP=›‡¥œË­¡C™Õ(°HZR`­ÕÚ¹«ªÅˆýÞ£¸¬9¸-O Kb—ÇÀ’À%B ób¥w3-ÚϲƒÒbH@¾T‘ÂBÎåKùœ×1&6óH '\D>{ '’ÖÓxapϬÀe½ÈÙÀmy^H}³W6rµs¥)ùŠjà’Ã^Xò¸d@x`Þ  ôn¦½Ð(ð¶Š{Ai/$ ß ªÇ ‹°†C‹13>†(±ç ‘´‚÷yž0ð÷þÓ+¹#W~¨ËMûA÷#špªr8+Íøsa@NzÚ`æ±ÆÅà4b`Í0µ¢m@.Ð\±œCŸ^QJÒ Þ]ˆ¿ìÄÀÙâ.g—G­§Š»Ó1Å]ÅÅÀ Ó"lM‚½L@¾T–a9„Äðêô› Ü@¹Õè†HZsùζT‘!üÈ’ûÌX;UJùX1j—U jB[Œ¨`I àt˜ÈHÆœ`I à’¡€y5€Ò;læ ¢õH «} ”nvÓœZ±±­õ±ÜW{¦öR™^Õ…±rœµ>b–GLûÁÜ|'c::±wèÙH&ûäaµ`á°ò_å~3©éÓ‚ 9T˜Xî?âeÁðØÃös5Mýªf¬jƒE~Ñ“ah,˜í ü7¹u(s‚±¡tûz½H'7Ô¿û8ÕýÎ endstream endobj 85 0 obj 4792 endobj 83 0 obj << /Type /Page /Parent 79 0 R /Resources 86 0 R /Contents 84 0 R /MediaBox [0 0 612 792] >> endobj 86 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 88 0 obj << /Length 89 0 R /Filter /FlateDecode >> stream xÍ]sÛ¶†ïõ+0¹j/ˆ)Q½èLâ¦ÓLc×V§ÓÙ¦X¢JRÉIýY`±ø"2®±¹]½Y.À`E®ÿf¿°¿Yœ³tMóé4gÙbγY´LXU°ßØŽ½8«cv[³©øSßÂ?˜FI:åÿ‰ÿõ\ÿ–‹4‚OâÉí–½Z±Ù 5ðwúÙ4MØjË^üGS³Õ=ûæúÍùOìåC³ÞTæÓoÙê¿ìõJ8¦ ó; ¿OÚ}£xÉÊ{h¡¾Å<²9Ë’hÎAò$Êsh^ºXÂÙBp?.§ñrŸw]A@æI4OÙbšˆ†Cø¢4eñ"ÊÀ¨ã=›CTéóù†‘΋™Ä˜“°a¤ó$†‘0Ì0Riré+“ai>ŒaŽÎË0Cd˜_g3ÌЊaíläõ×g—8Cÿk§þFpzËÚ^%§ç˜èF‹c·åþ3«?×M±eMµ¾ýP³›¢ùT;±;«þíD?…¶g‡„6ÒÑF"œß´‘°m¤ó’ˆÐFÂ>´‘ÎkÐFÂ0ÚH¥¦¯L´Á¡uÐöÌÑyÑfè‚hóël´º1¡mÞ‘•´]žÏœ¡´o­-Úõ¯¯ǾvbªÃ«Ó£ »ÑF[U¬›‚ݬwŠ;vvùâœÍ$êÜveÑú¨ßè ¢é‚T#NíÕHØG5Òy!DT#aÕHç5HT#a˜j¤Ò,ÓW&ÕbùÕ-~êÏ¢9:/Õ ]j~M5C7&ª-ÚY´ÕÕ˳×ÎÀ?ÓÀ³Ó~]ýÇqìk3­Ã+—i_ð,ÆDåÚ籕-æã·=ÎyjŠtAJHNÿ±Ž„}” ”³ÁñŽ(AÂ>JÎk(AÂ0%H¥½ÒW%²å°\»«óQÂÔ…(ÐY”0uc¢DÞNM­Æñ•xÖ¢Äé¿’ëðÊ¥„ßúkBØöÂóOs'ÓŽhí|0Ó¾Ø4¬Ù {hˆW:Ãu¤§²å°»Ò…8¦D8™ý»%ìá˜Òy±#9¦„=S:¯AÙÏJä˜Rizé+“c‹a)vxÐ:ëy9fè‚óëlŽºQq¬žZ]¯^^­œ]Å ¶;y;4‚ìT‡W#YGv pb[ïîXݬ«†m=}Õs€ËÃ2îJDšcÌ»5SÖúf nÍ”Á>¤õ$¤‘0Œ4Rié+iÙ°Œ{æè¼H3tA¤ùu6Ò Ý˜¶l§¥i—§'x6­Y‡W§'vb{kv¿Ù­ÄÖLB­Ü‹Ÿ}‘؇µlX¶=#]k$¹Ø©‘°k¤ƒa¬‘°k¤ó$¬‘0Œ5Ri˜é+k|ƒ?àé®ÌÑy±fè‚Xóël¬º1a-žv$¦þ¸A^Š{6B®u¹å>Ýõ¨ÌTos É_+]Ž1ÿö‡„}œ wZӉބ}œ × q‚„aNJÓA_™œH†å¯3Gçå„¡ r¯³9aèFÅ ÷îSS¿¾}}õòôûxÁaŒ .·N¿êòê°{^T뺀@ï7ƒ^;âc²S“ÖøðT– ˲+]eŽ1?ËHØÇ2ÒyÑC,#aËHç5H,#a˜e¤ÒÓW&˦³ìŽÎË2Cd™_g³ÌЋeíôÔÞz‰ãQ¦¦ºÜr7çÍ¿yÃ>mš÷ô(ý¶Ø–í'¼šÈr`í÷ÄE¬à{H•è`Ù1ºç¦†¯GÙö¢»Ï[v_VìüðÐl.«ò]µÞn7»wì¼Ümà}rvV|`ò3oà_î«ò¶¨kxå»/ÊO<¸"cþ¼ªÎ¢¯â"nÉaãÃ‹Æ ž<ºˆ’f¸WƒžêCÈ+µ+qÓ×UïLØè|*_W—¶¯é˜¼|Pf%]Nõ x梨"EŽðN‘ˆÜæ§qdɨ܉äŠgˆúR!š<ºâl{‘ÈbX$ DnFãéŠv΢™ž„4ÇP _¤¹Gð~â*éÆfE” ÖRQâ¦'+Ö ;ÒVë\ÚašÁ©€§9äéž1ƒdêÔ)²ŒVˆÀÈ £bß5ŒŽW£3†Ê"yÚQ—…çš)a‰…ƒ')FöbxmïZ®Ý*ÝC,Çôñ÷,àU #سt¸ÕñµüÀMË ðƒZ‡O"Wµ®ëb ù«A¯¿ñQJ½hqS.«P2¤ *˜0SXHÄü•ç0Ò)P¡Fî2ä9ŒD05¨Lc2i“Pʲ&÷,$Ò@A»Ê5 ¨Lk´g!‘×íYH(AeÚ¢= IЖ•ÙN .”YNÁ·òžÇ+S&øïƒ0ruòxeм ò‰,P9¢Q€ªú{õöçW?\\;›ò¯«vÆ-º¾¼rÜúê»–¼íVìn[z²Jú„u,Xµ}’•ä::²óAÈRAEÕ”~ýŠ, åL:8])]ˆTJ„Dᓯ4[p°)aˆTJ$ç9$?\k8F”0D*%òZ“}«„~R) Úò’ •H…4ƒxò0i¦=¤¥‚ÿ¾Œ€— \R”Nu”$›°%S{¦c'ðK£pÝQ–0ê²7mk"7£„ª7Q$W1\wÌ[š¬EŒJ +K:ꊬ‰µª.7ý’‹“ï–h‰DN\q”ÙÖÄEßÒÌ$¢-¹‚97T«€µT(;Ø29ẍ‘5Û3Kˆù?žº† endstream endobj 89 0 obj 4238 endobj 87 0 obj << /Type /Page /Parent 79 0 R /Resources 90 0 R /Contents 88 0 R /MediaBox [0 0 612 792] >> endobj 90 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 92 0 obj << /Length 93 0 R /Filter /FlateDecode >> stream xÍKwãÆF÷úXÚç$4ñàk9#ɱއ#y$;>ÞPe+‘c’Š=ùõ) ûë®~!Z&,ŒX7…b7ú¢Qh¿gße¿gù4«†ƒát8œf£É8›”å`Vd›eöC¶Ê¾:ÝæÙÝ66ÿÝÞÑÿa8(ªaýŸæ_ýÓþOŠ4™TúK~r÷”½½ÉÊR1ôÏ‚ørXÙÍSöÕ×ù`˜åÙÍCöÅõÅü›ìÍÇÝâqóÓtøevóŸìü¦IÌ®÷Ô~?U|GÅ0[?Ð/´»WƒÑ8ƒ’’£V˜ƒé”~^5™ÑF'Ôô?gÃ|6¦¿Ç¶¨AÆÅ`\e“aÑüðl2äE–O# Ú4ãC6¦VÅßÇÓAY‡7ˆÚMÝ*’é é5»£ÊUwÐ?ëî(&¿7Þ/ž–¶Ò‡G´sxìk¥¼œÒac›¡ªêVC3©6È«|ÀZªœÔÇ!ú€|P¬ eÚI…2ítâäÕüàÖ‡m¦[ÖN£|T·Ó‰wÔžÿ¹kÓLÁ(B3$¦|ZJÞ#:޼6ȧãAEC GT1 i@æ³G”gšªl™¦r»ðeM¥G8k*J(vH]?þïï9¤Š|æ¶UINÑãN5AQNK•E6g}ëP¶(”i¥×= ¦ÔŸ‘êtýô´\rP5“?r:v¨YÈ=4Ô!2½5ŽÔ ÚÏ©æÓfC<å#ÕÄÊi”Ö' @ôg:“ä9%Õìž@=¸µªQk •Y®Ì£Õ.M‚hzœ š:°½hE>­…¾?Ž$ºß‹5ªÔÁÄþÙl©ßÙtz£X…¡ßêÃh`ÆéƱî6ãtÓÓcB7JšSñð;\®îw7^'®’Æ'ä·ï¿ýñìòÚ“²p¦¨[ãÕÏ”˜¦\_}ð²RÇ®•MBÊ'/Ÿv…ç¯ZÊ‘¬ŠoÝœDºíÔøæ/œQkª<áóÀÛÅê·å}éÄ6óÓ@‚Ã|¿›i›æ<¹ÁYJn,˜/7=ø0»ÑÑ<¹éhZn,š/7 e= ÐÑ<¹yãØý¡\•*ä†Ý–\n*–ëN¬z¬¹©XêÌ1žªI¾þkÒYœ“œÅ9ÉY§ÒÓ¿Ããúᬪ&Ûµ³.:w%ÖCgE²ªºu–êÀ¨³ÂN<ÄYc:/츑³ '9Ë@4‚EgPr–ô,%´Œ¾Ò0 ä,%£ig0í,ƒXSÙ­æ’Q;kÏ¢Äg^ν¬Ž=ÏŠdÕñZ8‰ÜßÐòªšB¢\D«åN” ½ò(Ê PR7˜‹å( ò(È È>y•t¿©.Xë}&çbŒåÅ8Q^iΕ—ËõC^ã°æuzåÏxŽ-.Jª‡ó°HV‹Kuž#®Ír÷¼YÕs¯Ó«¯ü~Ü{ùèÔµ²è>P«ù8QY€hˆÊó-€¢²%%e•( Ê(( ˆŠ•žoåúö®ÞgRYŒ•Å8QYiÎU–ËõCY“°äÕ½²(©@Yó7§þœ¡©Àš Zu8š;‚æ‚Ö†¿Óùlÿʈx‰>’UÇÊRç(k»~ÞÜÑ]øõ†œ5D.¸“3­lu¬$ÑÚÊ¢s]L¶cœz¦NÔ !k  ¨-@zЧ/ŠÚ”Œm´Ä6•ÝB£Ñ½àѬ]¥žs’¶8'iKàmy\?´5 «^gg7ž Ž=Ó¢¤mEà‘µÉjÜm•^už£­9ͯ²°÷αšeo¸Û©d5šµ+ÑN’•h芲2 $+%õ¢çX”de d4-+¦ee«(»Åe5iW¢§EŠæòQ”ãDY¥9WV.×Y…5-:Ô‹®m–ŽbÓ…cÛ*̪k[…­újðµlEöisE8'Ú ]ÙVE[Jú¶(Ú P2lP°ë(»Åm5jW1N´ãD[¥9×V.×[ÍÂ"Ùê§ŽmEYõpnÉ*:¹²ë£½J­—m©îs&WÐUöÇãî׌VðgÛçOŸÖ›V+Y÷ÜAÚá GC—ÖÞ›áª7锢?ËþÂ^ER;sV¨ªaý( þ(ø ˆiÛ *3=•­‹-Šð#Ɖþbœè¯4çúËåúá¯|–´Î.¾îØ_uV=X,­Ž×péþs vÿøð°Ü,WTÙº]îþX.©(ÿÇ:{xü¸Üz½dÜ'±²]Y~N” ½(J PR;@Qb€’Ñ 1€‚Ä€ì“=àÑJbŒ%Æ8QbiΕ˜ËõDb4 üe]gþ"õc¸è‘‘pÖý"ˆXZWæë”¨ÿ<‰mháÖzó™–B<=-V÷mÌUªXšçÜÉÐ3„¢]e~N4 ½æ(š PÒ50@Ñ\€’Ñ`.€‚¹€ì3×°eežq¢¹'š+͹ær¹¾˜+Ríºôõã‹+,+õ Ø•çaZ‹+,wÝ.vw¿fŸ6ë»åvû¸ú%ûù‹ëïßÎ/nê&üùK¯oš€ [ÖìÁ‰´WcEJŠ(j P24PÐ=«èYä60ÎI㜤1s4æq=ÑX)ƒ]ûáüú¼ëÛŒ9¥\Jö`I«k™©NôfaÛßèðÛ.ë©/†¥HTÓvU|ÃIþ2Ð>Pò—’ÆÑþ2 ä/%£i0í/ƒì󽎣•¿'ú‹q¢¿Òœë/—뉿ÊHìûù•w²>úDŒÒ ÜÕýÒ®<’VÇ·ë”ü+H¶¸ëŒú2öüÁ«»ªq»¾áDy±`zÚáÝܚÃhEyJêò(Ê P2äPûëì»YÚ•ð9'ÊË‹GëÝÜw=èjðxžä\y¹ñz"¯*Rýê¼(­@^=˜xEÒêzâ¥:Ðx=?}:`Ê…·›Ù»£ªòUÑË^Ú,œ0œh-L¶@ÑZ€’žµŠÖ”Œk¬ĺÊnqk•íjöãDk1γ‘º k¥9×Z.×k…•¯ó³®'\UXbŠÍ޼Ì+¤ÕõʉXNͺԫe¶¼¤Ú½×›ÑZW}¤RÉ>y­X¶+ÙWàDq¢a,‹  (.@IÕ@\EqJFƒ¸ âbue·¸¸òv%ûŠq¢¸'Š+͹âr¹žˆkÖºÎ?¼ñŽõ£_*RV}œmEÒêz¶¥úÏ™m-7‹íòÐéVäFc•·«ÐN´ &[  h-@IÏÀZEkJFƒµ Öb]e·˜µÊY» =ç$kqN²–À9Öò¸žXkV¸ÈZßu­-J«ÚФÕõB/ÕimÑ}Æÿ.7·ëíòUn2–³vEzÃI 3 hQa”f ¤t´Â ()Ì@ÉhZaL+Ì V\v‹+lÒ®H_2NTãD…¥9Wa.×…Õoþô^ÞE/îúæüÇ®%F‰ëþQÆ<’–?÷zÁ«òOÔ›áéÜrÒ®n8Q ,˜¬€¢%3ÔPT d4¨   Vv‹«aÜ®^2NTãD5¤9W .×5„Å$RÃü²ó‚Ò$RPꃴ^G £vu木@4Pd5Õ(9˜¡€¢%£A 5±B°[\ U»:sÉ8Q ŒÕæ\5¸\OÔP?æÎ®ÿÝõ›ÕsJ¬³†HZA­¹õ´îCÙi} ¥Í=¨œè@4Rd7Ý(9šá€¢%£Á 7±F°[Ü E»RnÉ8Ñ ŒÝæ\7¸\OÜ0 ‹"ïø'›šÏ½”KYõQ ‘´rÒ ìúIG:º¦ô¹,öÙóh½‡ÄÞ þ+ïæ‘œæw›õvý°ËèNÔo/xwfú&”úÊŽózÇpÒ 'š«ùd &›  h.@I×À\EsJFƒ¹ æb›Ênqs [–s'š‹q¢¹Òœk.—뇹š/±y³šwgÞ†ïí?¶½êÌ{u¿f1–V~pI÷uìËéãzq¿Üd‘Ž”îž7Ë~"÷¡ÊaË".8Q\€hËâ(Š PR5@Q\€’Ñ .€‚¸€X]Ù-&®bÚ®ˆË9I\œ“Ä%pޏ<®/â +5ï.Þv\Á-†a=¤O¼ÅÒšú”lØXë/~çªNÉ_hýññv³ uŸwwŸ½¾<Ä[Å´]…Ùp’· D£Xô–%o(ií-JÞ2P2šö–ÓÞ2ˆµ•ÝâÞš´«0Œ½Å8Ñ[iÎõ–ËõÄ[yXFzwñÞŒX­Ëu ©·ì&ߪqèPó!WJ+˜mu__."iå?"òJ³­HN/¾BÄúj+Sµ¾º·«{N &  (,@IÅ@XEaJFƒ° Âb5e·¸°èƒÖ±GÚœ·aÑËâ¶RjÑŸaåW•³¬:¹’b»£HFR*RŸ¥Oßã{wù¦ë;`uZ}4T$­ƒ‹YÖ‡º¼¾I¡;ÐYá£._:£ÒŸ·'ošâ{1Šßõ@ÁX£–ጠã5Ì ŠÓ'BÍʉ¦>j G)N4õ¡ Añhú¡%£APh-(KvÓ‹UrŠÊ3Óö/£Å÷œ>ôì¶ãt”úq´šs5Å8¦)¿5͹²r9ÈÊÆëÄWe¸œgÞyñ½ ¬ú¨«HZEÇïÎŒ5•-¾?-¨ Ÿ-¶Ûå}‘¯Õ£ õXÁÛ›´Â2®°2±¨º~W%W8£05†¼¡hƒé¡h…уÂÍ(̉¦Èư[*šV@£0Í k››þ^4(ÌD3_@¦¶P¿ ¢"h…é¨h4ZÓUäñ*¼Ûh bþ*\y1Hï§yæVEÂOL@®¶\Ú*T¤nœV¯æþ—VŽ^q§uv²è•Cþ¬áÈ­‘´ºžaQJ~ñ †¢/ˆÞ7w ëO‰â_nê÷8ý²Y<ùm{q@}XJúʵx__àD}¢a-ë  ¨/@V3vËÑ@Q_€l »ÕDƒ¾ÚKÄ@_@T„¤¾òY¼ïè‹CI}q(©¯äè˃ú ¯*,bÍO;÷eÕGEÒêÚ_ªÿœ+ÄÍrqÿ#¬¿Ýbù,Q™÷,f8Éb¢Ñ-ZÌ€’Å dmc·¸Å (YÌ@6†Ýâ3`ÚbQÒ›Æ+ó®Å”¶ƒÒK@®Å\¨……®y祪a8 ëA%>’V×·Uÿ9Ûî›]Ö¼?àâ¢Íd«>F1ÙŠ,|È'‰z¼¯)p¢¦Ñð•5PÔ «»åh  ¨)@6†Ýr4PÐ!­)ªzí­Çç JkŠAiM% WS.ÔM#õ­î5EY…“­`1ü±¿Ì^DÒ*º¾aÉ©1Ôöóv·|j¾§ðU¥Þp’m1½æs¡¾ªÀ‰ªDCXV@QU€¬Rì–£*€¢ªÙvËQ@AU@T„´ªèçíWƒÒªbPZU ÈU• õBU‘²ÖÕ¼ó[‡ãHa+\,v|W…iu}aH-å¶î—õ›˜Ùí‚–ÁßgÊZ-…u‚¹U¤ŸWny:),p¢°Ñ@–…Fݧ4æÐ×Fªï§f¾ol\ª×:Pvivfw«F´.dÙhÉ:¼Aö «hQ‡Ï”ƒÒÂJ@®°\¨šD YWsZïßG¯ÅSbÁôªW‘´ž]½ÒjÕ…ÎU zœZ_}oý¼ËâO5D•î)ÀÓ›à#ÏÒø–•:Þé‚Ôb8ã-ŸUÌz‹Ó÷M4ã-'šö‹¦Çªò¡I Þh¼Å£é›kf—Ž·xn𖉦¼Åcéû‡^,=ÑⱨÑèþa>lS€g… Ïþ¨×ñªÖ×k´üHxg¯j~üD‰ËÍõ– õÁ[ÓhéêúæMןI((³@\o?øI}®I«kq©>tÄEå«Ýó6[?¨Ö‹¦ZÙ'+»¬”<°%ù0Vm÷F_­,p¢²)µÔ£Ï—Œ:]šh¢²X4YYEeÚ«,€úÚ0¦, *VRY³¦ØN©ê}Ò<–-TPZ›6eÄá„1^›í甸ԹÅÄó:J°œ &ãŒ'æiòä!Ó_5‘T>éÞ4 Ô›|—¡Oµ™M$Ý`jvÍ{@Ÿ¥ èu¹ú•Zá©]:ž7‘"»D´Ù gLÞÍéÀ9g˜8j‡ÎÁ£2Ó'F T¿û?CaAÁ endstream endobj 93 0 obj 4914 endobj 91 0 obj << /Type /Page /Parent 79 0 R /Resources 94 0 R /Contents 92 0 R /MediaBox [0 0 612 792] >> endobj 94 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 96 0 obj << /Length 97 0 R /Filter /FlateDecode >> stream xÍKwãÆF÷úX& sˆ_Ër<'–%‹Ìq&;ŽDÍЖH™¤’L~} èþº«_EHQ$ ÃÑM¡Øè¾hÀÙ/ÙY>ͪá`8§Ùh2Î&e9˜Ùn•ýšm²wçû<»ÝgÃæ¿û[ú? E5¬ÿÓüO?Ø¥H“I5 ¿äg·Ù‡EV–Š¡ėêÈÙ»¿äƒa–g‹ûìOóO—?fïËõîÓ៳ÅoÙÅ¢IÌ®÷Ô~?U|GEžmïéÚ]Œ«ÁhœŠAIÉQ+L‹ÁtJ?¯šÌè?£3j‚{ú×Ù0Ÿéï±-jq1WÙdX4?<›Œy‘å“Áˆ‚6ÍxŸ©Uñ÷ñtPÖá ¢vS É é-G•«ÃAÿ¬G1™øGãçåãÊ6Pº{ÔI;ÝãX+å唺m†ªª[ Í¤Ú ¯òk©rR÷C u‡¼WV…2í¤B™v:sòj~pën›©nËÚi”êv:ózíÅ¿mš)Eh¦³Dgʧå äm0¢~äµA>*jèQÅl0¤™Ìö(Î4UÙ„3Må—5•ᬩ(¡X—š¯ÿóÿéRE>sÛª$§èq§š ( §¥Ê¢ ›³îä:”m' eZém;Ô”Žg¤CoW›×tª3£fòGN}‡š…ÜCC"Ó[ÓáH ¢ãœj>m6ÄS>RQM¬œFi}ÒD¦3IžSRÍî Ôƒ[  µR™åJÁS3†1ãD10NCšsÅàrýÈNƒáŒáæÞ<õŒ ÄpùþÜËêÔ3†HV…?e¯™øÔÊ^ ´«…“˜úšIÁ3^:ª_v¿Ýe—×ï.ý;6ŸqfWYõLìÈ•S­­±æDm¢a*k  ¨-@zЧµPÔ d4h   - *Vz>SMÛi‹q¢¶'j+͹Úr¹žh+¬Ð]òg 'WVXëÁEÎ(Ì*z“™S+«9z޲jSeO«ÝúéÛj·|ÈÖ›Ãjwûm¹ùºÊžvÛ¯»åc‘ÕWÑ‹W-²ª)ƒÉ% ZdàD‘:*2€¢È%Õƒù@Qd€’Ñ 2€‚È€YIwKêr«ÞgrþÅ8QdŒE–æ\‘¹\?D6+6$²Âëë§6eL¾Î¯‚©Ä‰¯Ê"YM_+²lØÌ¾þ‡ õÜK=Gdç§ÝÛh5#'Š  \yFP ¤z 2€¢È%£Ad‘Q±Ò3²\ß²ÔûLŠŒq¢È'Š,͹"s¹~ˆlV˜®o>ý¼¸¸éØe”Xà²îKÏ‘¬Þ¦Â”;UÔº:j{;Š%õ œ(@B@Q €ô°J_ªÅ( b(ˆˆm*»…F£ºþhÖ®ôÌ9I œ“Ä pŽ<®b˜†&~álâÔsÊ«‡^ˆdUysœ¬K²·¤F³v•gÃI^0Ñ ”¼` äHÖJ^0P2šö‚Ó^0ˆµÝâ^˜´«<Ó*2s…$zq¢Òœë—ë‰ÂN×S…iX*„¢:uÍ9’•¯Ý¥í 2¾LÇ-ˆ¼ÅeeD7 œËžÝjy—-³ûõ­FÛm³Ã·UÖ,a\mþ¹Þm7mñ©àŒÈKm.| 'zŒ“=Pô ¤yà1€¢Ç%£Ác±ö²[Üc£v¥èãD1NôXšs=ærýðØ,¬àÜ|ìz½ %ÕÃÉM$«Ž 8êà9&»[}yþúuµkn =-©þ¼[=lo—‡år›®EkÚèRñc*µ+FÀÑ(¦ÔfäªÕ3zœèϲÊMT µ3g¡‘P@Qe€’Ñ 2€‚Ê€˜f° ¢2S•þQ}.lQŒæœ¨2/]¼ºËýôšJÏSž«27^?T–Ã"ÎÍÅÏ^_?õ…ZUeK«ãy™>~ŽÎv« =º §fÞ‘ŒZ«î¦¸…Y 0*ÛUž 'Z‹“­P´ ¤g`-€¢µ%£ÁZk9f-Z—ßÊZŒ­Å8ÏFêìk¥9×Z.×kQ·÷×0Ý\Ì?|ì| ­ö§aókÿ2÷Ô”±´º6—:†Ž¹6ÛÍ_–›ßWwYäX¾Ê]E»âøœè.@4¦ewÝ(i¸  è.@Éhp@Á]@޹kز8Î8Ñ]ŒÝ•æ\w¹\_Ü©‚…ËO>åÊ{Y Ë#iå¯6×Û,¿Œåôqýu} EL7«ýjI«˜²Çåín›-÷ûÕ#]DîÚLÃŽ]<[ÖóÁ‰*tTeE•JÊ*(ª P2TPP#*«èAÒ6Ó0ÎI*㜤2sTæq=QY)„Í/uØëð'×¥\Av_ØÏ#iù‹tç>Ue¿N),íïWUËoVeÞ/o[=·Ü\HžEÞ” *4Õ´]%ßp’Á tÌ`” f ¤s´Á (Ì@Éh8Èh‘´Á¼XõÈ·.c•üŠÞ¦ÐÊ`Œ Æ8Ñ`iÎ5˜ËõÄ`eXþšŸÿxñ±k}Q^¾>ÜøOèœü22’V×úRGйŒÜß~[Ý=Sé~™ý¶ýâÊ×\EVãvu{ÉâbÁÄ«HM‹¦‡©§ˆ  (.@GÅP›Ýâ⵫ÛWŒÅÅ8Q\iΗËõD\UXë…¸(¯@\ݯ½Ì#iùW‘¯ZdUÑ»0Ú,N0œhL6@Ñ €’cf(šP2¦43±>°[Ü e»ÚxÅ8Ñ ŒÍæ\3¸\_ÌÖ—úa†H©ûçþó*L˟ҼΠe»ÊsN4 '²Šf”Ë0@Ñ €’Ñ`€‚€XØ-n†¼]å¹bœhƉfHs®\®'f…åšùÇOþ½©“—j(­>N"iå¯^»ôF•çHN÷ËÍí÷ìn½[ݶ»ïÙ-½=m¹¹ksÕs¤à\åí Ά Æ‚É( PÒ90@Ñ`€’Ñ`0€‚Á€XoÙ-f°rÖ®àÌ9É`œ“ &pŽÁ<®'GÊ5]¯¼Ì)«> ,’Ö«ýõ6ÏÖ-åךëJóÓ’ö_Ñãÿ­YÖýTX®TÎÚU™ 'iË@4ˆEmPÒ–’¢ÑÚ2 ¤-%£im0­-ƒXYÙ-®­I»*sÉ8Q[Œµ•æ\m¹\O´U¿yÐ{åÒüÇ«_½sôÉ'^”V½I«ko©è™¿mÿ•íËÃó¾~™öÝzÿ{{{¥ï‘•“v¥fÉöbÁd{í(éØ  h/@Éh°@Á^@¬³ì·×¸]©¹dœh/ƉöJs®½\®/öŠ”®¯®üW¹ž^_aåfЃ{d“0-¿ÔŒË êÔ½¦ômöŽm3·i¾5ðïѪpÚõ´Ý>dχõÃúðÝ;½æY9jW7œè-Lö@Ñ[€’¦·ŠÞ”Œo¼ÄÚÊnqoUí á%ãDo1NôVšs½år=ñVýºÖÕoQ^}œvEÒò½õªBxIŸ²hs‹Ìp¢X0Ù E3JŽe˜ h@Éh0@Á @¬ì7CÑ®^2N4ãD3¤9× .×3Ì"e¤>˜ò ÌЃ[d‘´Þä™ú‰óúÀ°“Ó›‹ 'š¡ù ‰ &› h@ɱ 3Í( f(˜ˆm*»ÅÍ0lY`fœhƉfHs®\®fh¾SåϺÿ–FV †îW3ÇÒzõ«±ß¦Â¬ S©iÞÏXWj^ro¬î«R‘yØ²È NT ȲºŠê”” ÔPT d4¨   . VXv‹©«˜¶+2sNRç$u œ£.닺"eš>¨+,‡ôàÙÅ0L«ëwdÇrj^’ý*w¥KÌÅ´]‰Ùp’» D#Yt—%w(ií.Jî2P2šv—Óî2ˆ5–Ýâîš´+1ŒÝÅ8Ñ]iÎu—ËõÄ]y¤T³¸ºž_ÿÔõ꤂R ¦^Ý/hŽ¥å_“éÎ}ªÉꔂ›û‡íS¶¯KÍíå³.*ÛUSê²bÜ®Èl8Ñ\,˜l.€¢¹%]sÍ( æ(˜ ˆõ•Ýâæ¢¯ÿÆ s^:E ˆkK9F³’CÜYÎ HRk+¶;Šdl¥"uñ½EúŽjð]µùß>\~êzRXEI«ëkDukD5Ïzþò¸>¼ôfXÌS£XÉ[ 9*làŒ§ã 9ÌzŠÓ”6ÑŒ§œhêkrÌâDSOÐxŠGÓo2P2<…_ª=Åcé¯(z±ê±NQyfú$PFKÞ9}×m7Æé(õûÛjε㘭l<üÖ4ç:Ëåà,¯m•‘%HŸççWן½þ~êÛøex«µ­HZ]O°Ô1t¼u»}úží¿ï+zÿînyû‚5HÙæìÝù>oVà{ça‰…ßõ[!¹¾JÍ}©ñã CLC+‹&ô…hF_N4­/@6†ÝRÑ´¾}ñhfHÛÜôÐ÷¢A_&šù˜-µ…úÐAëKGE£ÑŠ<^—wAÌ]…+.éý4/¨T‘ð«,‚² ©_EªYŸçôÛÎuŽ4ôÒ:õ“ù­ô-Ú¹®ÂUGwôJ$èêëjCß};¬·¿íß«¦¬¾÷/XO®¬HZ]+KBg†u·º_>?².žWûæáz¾õ2ÕƒEX>Kå=N˜h`‹3 $0YÑØ-.0J3a·¸À ˜˜AT„´À¦ñ¢¼+0¥Æ ´À+0êƒÀFaikqÙõ'v©NÌlÝ¿Q7––o¯W-—Ì'‰·/p¢Ñ‘EP ;`í–#€¢ÙvË@A@T„´è÷h›žR4PZ J‹ ¹"p¡>ˆ`‹W]¿­ ¬‚iLîÄEÒò×Më)ùÉîÄ©ãçLckú$@ýœÚòÅO{D ÜùØÖ¤ëÞo¾9éK œ()@4xeI%ÈÊÄn9’(J a·I$DEHKŠ~ÞqI1(-)¥%•€\I¹P/$Vˆ=xª¶‡•˜|R.––?_9¹¦ÂѾyªö‰JËjcµ¬ ™ «È×KòÊ­ñ&UNT ²ªÒ¸¡}ÆúŠC³ýÔÌçqEõšŠªÂ.ÍÎìnÕXÖÇ×FK³ rLUE‹bvΠ´ª”VUrUåB}PÕ$¬ -æ‹÷7~ æä÷Þ(±`JÕƒ{o‘´ºv•:„Δꖾ€yX5žÚeôÊ¥úm»CÖnÁ±rP«gÓè–•êítÕ@b¡·w«‡ëŒµãV69ÖâÁô-8ÍXˉ¦­Å¢é‘ªâšÔ`-€ÆZ<š¾?evéX‹çk™hÊZ<–¾çÅÒ,‹nÁåÃ6õlQú‚Žúü›j}½ÒÉ„oÄ©æÇOd‘¸Ú\k¹P¬5”ƒæ´*Ó;)Ÿ\Z”W¥I«ki©#èH«™`ݯ7ô­’æ’Py‹ViÖÿöŸ)ɇ±B¶7ksÍè¨¹Šæ”t ÌP4 d4˜  ¾4Œ™ ˆŠ•4׬©cÓ#ô>i2Ënù+»M›úáp¢½@”8å/uŠ1œw ´ËÉ`2Îxbž-Ïî3ýÅRIåS+ÕËLŸ‡ (M¾ËP«ZÐ&’n05ÅæG@rz‡\ýJmòÔ.Ý›H‘]"ÚlP²îÞÍYÁ9ux;t:ÊLŸ%PEüå¿ñ~‰‘ endstream endobj 97 0 obj 4801 endobj 95 0 obj << /Type /Page /Parent 79 0 R /Resources 98 0 R /Contents 96 0 R /MediaBox [0 0 612 792] >> endobj 98 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 100 0 obj << /Length 101 0 R /Filter /FlateDecode >> stream xÍ[ÛrÛ6}×Wìc3Ó0xU¦“™Äq¦Ö¹Y™$}£eªf#‘*Iåö™ÉuA\B$9ŠíøÁ´u¼»ØËÁØü¯à? )„¾ç§¾ŸB”Ä7¥PçðJxpÒ˜7àw_ÍÿÀ÷hè³ݯî÷?¢¤$ =ü„Læ+x2ƒ àüNø!…Ù <#žf øåü³ßáñ²ÍŠúïÔ¿³átÖÖ fšö׎+¢ª®°W‡^CD½C/¤ÔKS\^˜Lñ_4A,ðÇ©O¦1~>ö„‰©‡ø´[8$‘G(Ä‹PhçÆÄèUùyœz¯ \ ‹—¤‚Á%3!ááÀï,4IÌh<ÏVyï {z0£é±ËK$H1mz7„!óšt÷ ‰§y*HXJKÈOX.Jù‰‹R~š ìê¼wÚO[ÍO‰˜Ÿ&FÖž~n÷qÓVI7M,ÉDÒÀ tD˜G†H{!–šÌ(:õ|,H¢`}F qÊUA'N¹jÂÃ\%*\s4–RçÅן“R”L‡¾ SDÝqPJž h°íN–äBTï'¥¼tÜ„J1ž# uR­Vyy¤š(jFþ ˜;èä,uIdâ)õ#^D»qÜ}‚Ù¤<ÎG\ª’E°JÙ¦!Aø1î$„ QzŠâ ¼jˆ[F8ëÒGÉ–4Q'ROlC%)#ôÝÒd&)i~CVòd“þcõÄ×¹pYÅ\ ®ÕÈaé` ',s}è: '\'q¢&„Sì8.O®cˆcqÊ»+À2yöþå©ÁÈŽm‚¹âèÛZenÞË×VñÄí™ÆÂÈ“Ã{®íÍ‹1òˆUôÏ¡MNöú©#›ØNûñèMô&°ý²Î!ƒE±Ì¡*¡½Ê¡™×y^ oU­Á<ç“Ý|Øupgð/U†½0“çDÊFG žÒϦé<'«ZôBšÁs\šä¹Ò$ÏI` ó—¥Ê¾_'Ò+{ÅsÜ2îÜ8åý¾øÔJ_:ÎE_:ÎE_Ü€¾ ÜÝ ¯nõÝoÎO_¹~Óô…VÝAú±ê–é‹Go@_MÞ¦ÉkÈê<3¸óízÀô²ªXŸº£…CÊŠq3ép.ÊR ,`'e) ‹²Hô+VÊR@e)Uš ,´S–‚ôDÕ?u/¹qÌßÔ…N[Çk8'ei8'eÙqCÊâîeEØI×[#Ñoš¯Ð¤-¾:yqfXuÓíÖˆUáí¶[0cÝ–Ä9©J‚vR•:©J‚¬ä"Nx•mNªÚ)MR•:¨JBvQU€wKìpZ¬ÀÚmi8'Ui8'UÙqCªâîUÅÛç[ï^Ÿ>»eªB«î`Ã5b‰o—ªxøT5¯«¦ÁÄ[äu^ÎsشŲh¿µ±×ø „h´ðúg¯FKâœì%A;ÙKì%A;ÙKì%AVi’½$ÐÁ^ÂeÙ-"nu…N+{i8'{i8'{ÙqCöâî{%ÛÇ[ïÎß<1rý¦-´ê²×ˆU·|¼Å£7 ¯EUÃÉËgðô…Daéo†}û'‹ Ž–Ù‘q_…òœ†µ[ç$, Bî7C t–‰r·¿J “°$È*M–:KBzWõOÒiØËFÓçñ<ÑtÆNœÇï PÆJÉ3bÅÉJÜè¶$Ée)\¯ÔS¥ÕS]«A¹Â:~£„DK˜ÇßÂЈ=—&îbZ¹0…ë×*¬3â*r¤×j½³1tê›Á`ÓP¢zMê‰'.\@.q¿:1*á~ÝÇïlú(MÅ‘"n±b,òž`®-;• ?.ÇÄÆ¬Fj|~:»¦xm~†9…:笳«¢¦Z´Ÿðvð¹(çËÍe~ Yë¬nÙX»1ÍêùUñ1‡{“Ãfè¶ß·‰áh\2sø¦ÙÇh¾.óÖûZ¬]0ª™àH ¶`Bq.pdÅÌ*¸¨‹òجùep^Äë•yµZW%ŽÉ<üñ5Ò'ü®UÁÍ.R9Ø«Gµm6¿‚ËætëÅ»Ôïáχ®z{LŠ+ÆÀ.ΆâK¶Æ^gþu‡!5wºH‚“–lèÒÐÅn­æëͯ(ް€G´Äá±ÜEÃе(Ú:;8!-¡ pßs¸ ¾^{JÕ L€ÃI.MY7[W+-D88¸{úv»¢»Dc_Ó7aÓ·]¶‹D€‹¬ü_jʰ1ÛK™Qײ(ô>’u¨õ Úb•×Õbq¨>KØbQs¦(妨€ÇÒ%’GÖ†ìœ.ޤ, Ç©"ké"4ñðˆÍ#ÙP¶Ð9aSY{Íi›™™Òž2&|N»Ë”‹ªj+º4ž] nv$ŠRëÇÁßW˜gÈ˦‚üs>ßàMÕ75®Óñw`/&ݬN-C¾XäsÜ ñWlšèÛe¥ƒ«óvS—¾#·³¿9ÂÂV0íˆið¶ôyê{ÇãYy k¬ßu‹[ÚU^â/Ë’ù±È`–/Yüðú­[B±Ú,³­gÄW°Z¯À´¾ù}?–õ8_=[ ¬gÛOo6³®iYÁl> endobj 102 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 104 0 obj << /Length 105 0 R /Filter /FlateDecode >> stream x­YkoâFýί¸w¥Ä ɶ•²Q¶ºIhˆºÝ*ª4˜¦1×coJe•ýC=ó0¶T8»â5wÎ}œ{ïá/ú…þ¢Îzm¯=l·‡ ú4èv½ŸRNŸ)¦wçªC¡¢¶ù§B|¡íù½¶þ3/—OqÒ`ÐóðN§.éÃu»ö3xôñùn»çÓÝ’Þ}ìxmêÐ݌ތ/¯~¢³(c"ý}Ø~KwÒŹXy°¶ôr;½Ý†ü.É–&ú=/èSà{]\^úÞpx½Á þ‚\0{!^²x{‹¯ßï®á¶,Ü/2§ÅÄ"%‰ÿÍÃ<ãô´ÆOaó,Œ³o4“)e NŠ-9ñÙŒ‡é—V ¾3•Õ—ò,Oã¾ËôwÞ¶ösänÝn½lUãõ¤ÄVB¹\²xJI*—IæÑ݂ǥg÷J’ªÓu‡Ú&r$XçÈÙÆ-Ñ4-`¾Þò}—©÷I6Ut>zw<´‘x½5Àzˆ*°HÎEÜ,ªvÞS& ¦šøÁvŒ¸2} Éé÷ìôõvt€vØ1P–,!ÅÓ¯<¥i*¾r:;Õm´ÜKN¨„-8ب†m*Ràܤ-²V%á Hc –ðïI-ä£2ÅÊ8(Í^† ê¸ G÷:7k…ŽŠ¦«Ñ»«ÂÑB¡è㙘ç)Ÿjo³0ä )憔%`ã <KZ9ÂKyÂ-53øW!sÕÌÕþG”B>Å= ƒ2 ÀͧEzˆ™¹Ô£P oߺ¾V‰ æùÁIÉó®­]Kðz¶bE¤V*ãKR ÅL„¤dž†\Ñý›ñõåÍØ»:;?"ðÍçÛŸ/?ÚgW£«ßÜ[÷o‰‹ˆÃ(ŸÂ÷2nÆsCÀØz¥ýxSõ€)@ß9C‰E+ÛnlÙé7=ú ÒwMLÄszúñâzüelûVÊÃ×oè4:¢Î€ ¥ˆ§ žl@^:?Ñøâö׋[ïv<"Ý©Öî5/èÚBFã,£<“KÜ5D°V¤òùœ#`Sš¬Èbò¼p€yB/xÔ‰ÒiûîÆþº¹^°oºf]=|ho™ÎYN¯?ѹ÷æÑç£ë›ñaÇWи¥Óî!µ¹Ð¯ãÃt¤â¢xg)èð˜#žÅpä:µËK "øŽ’³ìÑÔ ¾¯'­uí0E KÁZ–LY.ÜFwƒ:@·`Ñ2¦ ¼DòzOu<Ý%g§æ..cÐð%àË#š‰Te4Iuæ |YóÁS®ÂTLìp2ˆ22¦ÀëÙ‘®ðrCÒÁÞd3ë‘ÊtXªq "Æó8Dg™•ËÁÞýv[£¡wü¾E­áê¡è3p\NýÞþÑzÆR·ØwÊÙ¡f)–"KÙÞ¹ñŒµ^ÇUÑ3Öþ9x¿ª&ºö`o¨g¢ç=Kf6:,kO¶^¶ìXD´Å ÐÄi-ºUjÓñ„Å|º¶÷ÂÍí_ö»[[ˆ±‡†h³¤!;ƒöÖR°Æ¥†¬X‰¦LÁ… Ý”²6…É­Ñ]§3ì wÃ@¤m¡=ˆ^çTK `KË/ÙúŸÉ‘tª`5GôF§‡©<¬­4+-î«3lÖÁ „’Âbե󦌀/ývß[)š|Ü! ƒöÿ( R}ÛÔ¤*–ÏB9 »á7Ãø>z =Rs=_cÂgK –BD,aåšä Š ~ÇÈ85 vëĤ¡¯€åÓ>wC…ÙšêöþM-<,ËX¸ Ûë¦B=à iÎr[­XqôÊ`m(ˆÅf(&XŒ -±¯LDlÁ£övø²kÜý[¾TU+ìR±V¢Ö{UõÓÅ F²²>hêú¦Ô¢àœõ/ÍòÔŒ îjz„‹9”)e³eÉðØ,ÃlBÈ\Ï;yœ‰h=tÔp ¶Œ;m¥=ÍﶘPÖòýîÍ=½j ×õ½þ¸ÐNT™LlFˆe± ï’".>ø4Yñ­ê¥/6Ô9ÁË ³tS ‚­š6¤“H¥„Ë¢ƒ*3A‚—3§Sg4 =‡b{¾r²/Òª(­yÖŠèЬJÚšO{¾nMµDzé¶~>úàvr €xvE¯Š³f[>ÐùÕ%Oß>è»Û—;Fàõ÷voúðÍ5¯¡ r×шî±$‰°w›µ£XÛöE³c†Ó®ê÷¶d™;,PRÜV[jy&”Qä6P8¨+VÀðKE¬ËT3ÿ _”†LÀ6ª ÅQ_|Ðéofè1GŸöõËöœ¸ãm”Kb46ž¥rž²%$š9}bñ> endobj 106 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 108 0 obj << /Length 109 0 R /Filter /FlateDecode >> stream xÍYs7€ßù+PyÚÔnhÎÁ+[¥hå¬Ë’­XJíñFS”̵H*$U‰óë·1@«gDÓœI<ÛÓƒ¾zþ&~¿‰l"ÊA0 &b8‰qQô§¹Ø.Ä¿ÄZ¼:ßeb¾ƒêßÝþƒ~^ä?ÕúÁþOˆ4—}ø›¬7_‰ŸnEQ(þÌ/e.nWâÕë¬?™¸½¹ysõOqö¸Ÿ-·ÿ ¾·ÿ·Ub6°ÜSóý”ñ奨ÜÃÚ]ŒÊþp$†y¿€ä &y2Ã+ÇSøg؃&¸ox¼Bo™©ã…?åñæ£ÂnOîâz¶›ÏÅÕí_m&Í5ÜÉPí¤çµ©ÞÅãlýð<{XˆÝ—Ý~±ëÅânq'ö1߬ž– q}vsþúý‡+“IïÐLªÓ;‚c4g·'Ïîí'ØûlõûÚmî÷¿Ï Maç‹Ø¬Åw³§§þÝîówb¶®ÒzÞ-Är/î6&Ÿ†Í¯»mþñP”…ÎghzÛn¹ú»˜í÷³ù'{΄NÁìð«`8ù ðqq¿cþ¸Ùì—ëq~ýêªovÖðèà¼÷`0Ñ£ƒó>”dgÕX’­}·Ü}&-;߬ah­wbw¿y|Üü.󸇳¿ûñû޶A:Ȧ#±-ö£¼?*aŸòA&ÆÃ~–‹lÜfÓédÑ»£‘ýûѤ_ÈADLÒ7*’QŽŠD¤Ó;X:áø½r4Íüññn¶ZØ“ð b+õ­”£m†²”­Fš Ú +³>i©b,m‹ˆrN•‘ eÚI…"íôÕr&ýi˜ e;ùýéâ}“f’I;× l¦TgÊ&E¿ m0ìý6È&£~9±M™Oû¸ìd¦©äJõ(Î4UQ…SMäV ºñu,ìRãAëR7Ë?¿M—ʳ©ÛV\9í¸ƒ&ÈóÜi©"/Âæ”]J‡²í¡¾Q‡šÀùŒt¨óÍjµXÖ©ðÄ?2è;ù@þa.Ör ¦0%täª_ÔQj i«©XÊD^œ Æ'LŠìî`'Y²;p|IUŠÈq¯¦6’œe ÇA$=6T$Õ•½Hy6‘•ºHØst$8Ù^œa©º–à/!£É`èa½f˜ôºT.·R!ò•-†ÞËqùF²Êߺ-õÑõäÚâ+.RtêôõèªFN°Ÿ÷‹jÖu6ÿ¼Ø®f뵸^Ï÷ËÍÚÍ6¾KžQ;β\í8³ ¨glg즹~> årx‚¾zK½(œ~K®8¢÷›õÔ út#¡!Ç ¡Z¡!È ¡¤‚Ph²BC( …† #4Dê„6ˆÌéáX¡ŽZšs…ær]ZX“¯g_ø«Ÿ^haª kËa˜VëB ŸÈ‚×ý_p›OvSb-[±ÓÖ‚ŽÛÈZȱÖB¨ÖZ²ÖB(é´‚¬µJFCk!ÈX ‘kðÒq“iå8kQ޳Ã9Öò¸ŽXk)‡uÂZX0 ëÀš2’VË‘fê:Ó0k­—.$¡ËöˆÁª·«{Ê`²71˜á8ƒ¨Î`ä f ¤s´Á ÈÌ@ÉhÚ`LÌ uƒ/o42áXƒŽ5Xšs ær1Ø8,‰½yw{ñsë¯`gY °¬$#iµ]ÔWçÐQXt%ùf½_<,¶b¶]î?­ûåÜ«DËü5kɺu#§!Ç: ¡Z§!È: ¡¤…Ði²NC( † ã4Dêœ6lVæ/Ç:p¬ÓÒœë4—ëˆÓ&aqìí»³·^o?ùRòê¢Ñ"iµm4uëv¹yXÎÅÓóŸ>ãMl9Œ¹ 9ÖeÕº AÖe%íƒ.CuBÉhè2—!Rç²¢Y¡¿ ë2±.Ks®Ë\®+. ëb—oÞ½½ºm]faª u±I˜Vv°ÍlêÐCÊäd‘œì§0|5H<.×ð¥ï”Fgcu+LèÄ †k0„j † k0„’ÎAƒ!È ¡d44‚ŒÁ©3XÖ¬²_Ž5áXƒ¥9×`.׃MÃÙÕíŇ7^w?ùt  ¦c·ÿöÅzòW#iµ]#S§ÐŽ™oùŠÅv o[×mwðÅÝcÜž”ã§‘Äc%†P­Äd%†PR;(1Y‰!”Œ†C‘"5Ë§Í ý”ã$F9Nb çHÌãº!±>¯é¿˜xu{}ùkÛ“‰ƒÏ{n=µÄbieߟ<Î4,–“7 S/\C…L¾‚í7á/&ʾÜDd†ãDf :‘™’êÑ"3 '2%£i‘0-2ƒÔ‰lܬޟŽáX‘¥9Wd.בeam¬"ƒÄ""ËüQxâgøóHZm¯'c9}k‘A_n$2äX‘!T+2Y‘!”TŠ AVd%£¡ÈdD†HÈFÍŠü9áX‘ŽYšsEær]YXëˆÈ ü0L޺ȴ&‡¾K)ÇxœFRð8ÿ·ötåFCŽõBµCõBIó Çd=†P2z AÆcˆÔy¬lVàÏ ÇzŒp¬ÇÒœë1—ëˆÇòXy¬ +KH,2!+ÚöX$­¶=¦Naª<&ëûG_XBW=¦ ù²…jg<¦ç r¼ðªÅ\ý˜,…’ÖJA޵<¨ Öš„e-°ÖåÿçO¾>„Ä‚ùÖÅÿ[Š'ŸoEÒÊÛžoEròÊZÛçµØ/W xKûãv¶ýÒD^r 0ƒ¾ îrÆ—ÝÕðQò2œ‘—Cþ84Áô8ô‚iy™hF^N4%/ÙvKìê% yÑhZ^²1ìVMËË€é)—AT„ä”kR•äÆ%O])ÁcÚL¦m~ûr ¿;„'ÖS§N‚:Q†óN”v¡úÕ_š˜'Ì޽п7b"ÁQ²gÓ€ÜÙ¤»T§'âhÉž³¥{šR…½S®ŽRŸòÔ.U$ 9ío~²Ø´¿îv—æG†Q±ôEÍÛ¡¹08W'Òyüh4³H/ûåÿÞL² endstream endobj 109 0 obj 4584 endobj 107 0 obj << /Type /Page /Parent 79 0 R /Resources 110 0 R /Contents 108 0 R /MediaBox [0 0 612 792] >> endobj 110 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F1.0 8 0 R >> >> endobj 113 0 obj << /Length 114 0 R /Filter /FlateDecode >> stream xÍ]wÛ6†ïý+x¹=§UEêÓ{—¦n›³ùpmwÓöNuäD[ËJ%y³Ù_¿^CZÑŠlÏiûípˆ‡ƒÁú+û9û+ËçÙx8·Ãy6™M³Ùh48/²í2{›=dß>ßåÙí.–ÿînéŠñÐüSþè›ê¯di6è7ùÙí:ûî&XC¤ ÇEv³Î¾ý! ³<»¹ËþvýâÕOÙ³ûýbµý}>ü*»ùWvqS:V6WjqüBÅ$ÛÜÑV—˜Ž“i6)#rŽZa^ æsº½ñìœþ™œQÜÑ_χùù”~ûD 2-Óq6åg³É /²|6˜Ñ²ï²)µ*~?FƼ“ðeLO°%×lé˜Ý1ι;èOÓÅlöÆëÅzY5Pzx§Åðhj¥|4§aS5ÃxlZ ÍÄmó×R£™‡˜yÇ–M¹vbS®Î„_å ·¶[¯&ùÄ´ÓY0j/þ³oÓLµY„f:K ¦|>Œü6˜Ð8 Ú ŸOcšjQÅù`H2w²jDYs®©F¥9×T² ŸÖTv†{MEņÔõê¿ÿŸ!Uäç²­FÄ;ï¸ Š¢-5*Fõæ4ƒÜšªÚ‰L¹V:SFÔóÍz½|8dP94?r;Ô,Äšê™ý4Nx5ë¸ù,Ù`yÄV­œf©yh@D¿¦'Iž“SååIh'·„©ø“íTÒæ ž§Ü}g~¸_n׫‡Å}¶zØí÷÷‹ýjó}ÜnÞoëìn³Í.¯Þ¼|óãïÒñxìšì\ ¾aÞ È2¤³º|<ܼ¬Œ…೑à³Ö,ø×|ðašÛÁZ ÀÇÖ¾Fk„#|lËq ºOâŠá€{Æ;óÀþ6É3_§ñÌ×i¹F£Ðz:ò"ß^Ó Öd»yºn¸Ñ2ÕÚ£Ap%ïÕ³… îCê|¸±½†`Í8[‡N“e˜ kQÑåÕ‹0*Rb5ã:¼:JTdÖô¯z«E¼*E[6’9ųHNÑ¢kjBá8ºxøZtA ËN-‹.ˆhª†èbc6#5…0@W0Q¥k>º¬5‹.X ÐÅÖ€.ˆ,FêÖ€.ºØ–›ò³t\6ž§Ð%ÛÍÓèÂ2º<]I¸×´Žíá>¤ÎGû×€.c 8<_íe³JtÕ¤OC—qý ¼ªG%ºêéÈA÷aÙ¤îU×è*{¯]ÑLÙIvªÅظÌAêù7Z^N¡S1Q#Æ T1Q<6aì|S1Öh ƒPÁ$ìYc#Úª2¹n{^&1æéTŒy:ciĘÔõcÓHº,šMQ‚°ä ?444$#Çj Ìîf¯º^`r ”Ù|%Ì(wöq»¤ÿf‹‡wÙîv»\>dËw«ýfÛ&*Kö¬Åm/µŠÊ SqQ#Î TqQ#Î TqQÒp¡‚3HØVg¹Ý5¶×LâÌÓ©8ót*ÎÒ:‰3©ëÎf‘lY/pFŽÕpÖ}`ñjrðÀqòe܃gUì©Ù~°ñZ˯\$®MBºš”žSuFYÀ1…NåDdB_UB¨ò ";ûëë@„cªü‚(i ü‚Pá$USUŸÐh”›œ·Ëöû:_¾Nã—¢ü týà×¼L©ˆ"¨øæÖ©Ã1r¬Æ¯›_Ã]ÕSï_F¼MЇ_܃‚_ÇÞmnM!o`®²igsñp„@lržÎìó g92'¢i­‚Ì 59Q=dN¨Ì‰’Ö,Èœ0 2'©ðU}òA6Kföź’ $ÝúS™§SA–ÖII]O@K•»ô¿ãýä$«'£¯ž=¼:9Éê^uœ"›×Sd–÷i)y÷øpkj0v~8F‹Íl±Û-×Ü/±¶œ¢Ú¬-NEšgLG„*Ò JBHƒPEDIk@„ Ò ©@V}ò‘6i—ñŸx:ižNEZZ'‘&uý@Úy=Uöó/¯rœšgäV-2£ˆ#ðêÔ<‹xÕ1ϸûd`¶ÝÜoÞgï–ë êÉþž½þæ¯GÊ’íÌOˆeë°!c»— y²É¤]ÚßéhÓ‹nêÊ0Ã3¦³ B•e%é–A¨² ¢¤5° B…e¸f¨ÄÅ´fiÊ‹Z¤ý}ʲÀžWxÁý`w/}{ó$ˤ½~°,ÖeWϾû©YfܪÁ¬û¬Ì­ƒÓdÇ©½ —æañÅ~»xØÝ™\v·º§ôÕv³ÎöèƒùÛîón¿¤¿oÊ=¿üöUönµûóël·\f‹ûÝæ«³§½_Uß6õnùp6 {{õâæ"VBc©™±#æVÇ›¶Å϶{óý÷«?¶‹íç°áªlAí;KSõ²f·á—Óiür¢&~9¡Æ/'JÇòË 5~9QÒšå—¦ùå$Mü¢ÊZñËÓ©üòt*¿Ò:É/©ë ¿òHvì·ëço. ÆûÉùEžÕøÕ}†¬ˆ¸Õ5¿¸¿n7iÓìQÚó8¶‹Û?wÙËý's6·y  Õ¦4dË Ó­€ 4ˆ¡ 4ˆ’Ð TQÒ€¡4Hš€FÇ“µš§SæéT ¥uhR× ™mËàÅÌë~<«­¾qò€,âVÇåf÷¡š Èø+êÊã m—¶ÝµlÂäVƒNÅDƒPÅDIðcªƒ(i ƒPÁ$M£]µVót*Æ<б´NbLêú‚±zŽìm×o`E= ›'>&#æVç«gÇ>m¶&ÍO‹Ê2áOÇ?n?gw‹Û²Ú,¶IŽ4Žë³3¤¬oàÒ §sc ï˜Ùê1'*ç.ý¾1[zá„bÂW9‘ÀްÆ='tó­ÙÒ 'JZÄÐ"b¾-[²Ø2“¿²ŠF£ê±¢ˆ&ú ÙhžÈ#XÁ¸±E¡%üÈ–p‹ž%ÛLå×rJpIÀe}ê¢Ö¢Õ³aÝS‹œêcÜq+?¸Xì8ß*`ú/l*[G.ñ7s£ SQ# TQ…‡êS Äaªƒ¨²Q}*­a*ƒ„-¤6Œæ÷„y¢4Â<‘O'‰°„H"LŠú€°q=!V,<¡Ož#¿Â©Ù‡Ø+âV×é0îA±zü´]Ñ×/¨NŸÎ£-Ï©µg”ygÖVGÔfÁ1Œ_RÓ_ÐO!ç‹Ê  fc.ˆõÙÙé°H¤,dtÂi2Á%*ö±ÖlTa€4¶†¢¤5 B´¼FHsFQY>¦üpq£ù¢igTA̪/ò‘&nQ‘%Q"‡4¶Ô•Å««¿¤ˆ¿˜ÔOODšq5°G+âùÕƒ"þ˜[[—Âb‹ !ö6ö0HnQ¦¿Ý _oêU_VgN6Ä‚ˆŽuº€WÀÐl0ãù c!¯Ø˜]E:k¯€¡ò'ŠÆZc^9aÀ+¶fyåDIk–WN(xŶ¼9û$z^¹F3¼šF3ú2óE¯Ì–W¥è*Ò·T‡|—–¯ø›yå¸p´Šý‚¾l'LáÿúÛ«Wµ:¬“aäYƒ°ˆ[]a܇"»ÛÐ÷ÐQò‹fÄ{zorˇ‘QÅØ"û° Ü˜Ù” ¢ìCò`fÔ{“1M0èT‚AD”Ð ¡J0ˆ’̱‹Hw*Á­`*ƒ„=KlR6DxšO@0O”&˜'òá$!1TA0)êÁ¦õ> endobj 115 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R >> >> endobj 117 0 obj << /Length 118 0 R /Filter /FlateDecode >> stream xµ˜]wâ6†ïùs™œÓu$ÙÆ¦{ÚNã³²àd›Íî…&¸k,V6¡Û_ß‘l01ÎY .ø´Þ™Ñ«Ñ#‡ð¨ шMˆ ¦ÕK×µÀ'ˆá¢“P'@Ô3ãDc‘õÕ»â#ŽdY††¿ÐÆx—èzö|eø ¼9\\Qo g#·w í(õCñÙ&çàý ާ+–JûëÕB¬ |ŠMC3›`2MÇà° 6ÓlÓ3¬>Ì–`ºg¾åkâå2V|•ù2›dé24²tMm+€ýjY16Ž‹c7T)qì¼”P¤vèÈX‰ª‘{®7‚n˜|çŸ4ˆ'Á.Û#·÷HBƒ¡Ñºâ¹E š• _•EL³ìo&°òHgŒù|Îãè, "áÓtåËyâ’@„|™È_D"çZþ?s–¼l±L¡›ç¯1]Fðåìi™BñÕ—óóF=ûåSSÊÁ :æÐØvyV­U˜Î0æyÁºŒ2?ž`rüƒ€0UïÇã Á8ã àïþs ²™àhàÍLYV$æÀðÓ0~>Qì&úôuìÉ$ æ2.áǰÄÙÇ2ªIÙ”+ºðEº.º/Ƴð%P~lÔXͻ夌Xµu0LZ¶»¥ÉJû7\ÔuÊ4°9mÍÕ-[#FKʔ퇹rxâ<ýµ®N…Ã-ûM.annÎGû¦0^,ÁÆV|¼¥Ø*¤Þc“…UEèV\|;^‹É¬ZÒóØÙKYùiêg0ÁÖ1ò“p¬áÛ\²!·Ÿ}ºü®/¤d³™· ”Ì»ë¦Ø”`¹À¥Q7»]oH)K·*³{æ0’L—±i¾[%ì9½ÁðFîgçxAºñ׺)UÛжó­¸P»qûÎíÐí{ÎÕ:ˆe½Æá³Õ2×%lœ]»^;#ºîèôïz—J—¬u±3P­î7Üà&Bv”¬a£>Œ¶–¼¬8%%×fqÀà ®Üg„…Ð×ƶ”´ìhîÛBtØîw½¶ä°ò$ìµdª'Rk§ùÁ²\>xή†Žs¢Ò2c§!´o¼¶;ÌÉaèÜk%l8Â]T§E3Ü*,•¯œ¿<§ßuºpï Gî ¢åCu{§%t·Cô´´eYXRPÜäö;Ú‰¬c˜­²uêNXu7¥¦Î²¶h½Á]ßR»fÕ]”6I©ÀYýðßpv.¥¥ü¨VpíàsËS7%à65»nñ+Ç ¹jðÚ4¿;´¤ùª¡s(îF€ÇÙV*‡®Ddj¯I@ñÔJÜ|ÄãgX>%½Kî–2 "ìí¿YÆiIÒ”pëGsž ­ DÁ‹§´!™qäL¤Š`¾HñŠ”/EÆ3"x |uqÊOô…±ÃOž&üù˜úÂx*B{çö¢·/}ÌÀöch-<½ÊÿO06M49Ó•Nfÿâè~j2f„îìNŠèpš…?þ–<’wÖW¼Çp¼S—ô”UßK ÇÙÌ—~’g«dKñˆí—<É”šÔY%ûã‘ïƒàúØÒ;t-"ß0Š)ôT‚Jo‹ÊQr“-*¯{ï¥ÄTŒ±oò›Æòê¦Ïtl8E~ê~’Ò:)—3ÝÚAåŸù1“fÛ»¤š4Åäû[y´âƒ3éÉüœÌõ5™e2?º·I2g¦-Iï¡4r=ø½vÿ!;/¼EçÇÚ·ÙÜéAá7áü49ÛD2쫜‰Ù4ð¶DFçíû¶{Ó¾¼9¢3ÛÚY© zƒ¾ë †˜á|ü“¹( endstream endobj 118 0 obj 1398 endobj 116 0 obj << /Type /Page /Parent 112 0 R /Resources 119 0 R /Contents 117 0 R /MediaBox [0 0 612 792] >> endobj 119 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 121 0 obj << /Length 122 0 R /Filter /FlateDecode >> stream x½XQs¢H~÷WôcRµa™d¨TÝ•qÍ…ÚUw‘»”›ÜAbæ¢à2°Ùº_=€‚¯¢™Ä<`ç›oºûë¯ýßমéL×XvlÃÐ i×ÃǾ  Ћ?âtšº|ÕÿâJ¶mjx‡tÂ%\ø`å3x¥ø¼¡›ü%|¼$šü{8™¸Ã+è-²€§ß™~ þ?0ð‹Õ K¤—ã˜í@Ô†äÖ]S³º`QÍÀÍá)0ª1†ôLÛÁ—ÕÁ#¸!_(ùš¤ä‹Wj[@»zI×ÒôNI·?þ:õÜ?®| ŽmÃņ®?wÔ¯÷uÐ?ƒ4Œê„²:aíf8òAÿ[ €‰».BØøäzÇXxæ2Àx•IÒ%N @7 ,Í9ró͵YëÚðú•1}Úv]&5\‚‡`hnOÌÇÛÓ^ç%)Þ’Zò”s»’:'n I:‹RÈHó²Ìß`™äÌÇr Qy#Y,’'Ï!L–Ë ž ÜžüF–òð¸«Ì£¬xxòijð¼hÎE¥§ÃŠ´]hA§©)Ÿ?d·§ø¸‘ÜgO!¾_i&ËWî0HÃþ3:6p‚q,¢9–š·Ö¥:ç‚E&ž´ùª¯C¤¯CEe¡5J­~ÚëW—BÃÈNÕ ¾ü 0\åÀPÞ_„-¢ éÆ ËCº"Kó0ãI,Îø> endobj 123 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 125 0 obj << /Length 126 0 R /Filter /FlateDecode >> stream x½X]SÛF}ׯ¸ÍL­ì®¾ËL;¶QŠ,‚¥’&B–A ¶Œ´’_ß»+aÙ–&A±ó`cÄ={îçÙûðÔ¨Ä&ÄÃ2ÁÒ4Õa'ð–ðv\Pˆ ò§ˆñˆÊt"^ò«Aý+Z²,]Å¿P%^À(M+ŸÁw†ÏkDg.àí;ª Îá·À›œÀðžGiþÑ&o üÜP¬6,^£·1²92¬!L]5L0˜ªááÐ 6Sméé–ƒ/CAÌ_ÉJ¾:-ùâ;³ `&)é*QJºwr>½‚Àûèþ×yÂ×ùòS}¤NÞÝCӴʹˆV97t§ÏžÁï8SáéYj[L5ñŠ-޾÷õ,#ºþâì8µ{iÖ5·Zf„p¾Nª²¯­'•N^£¦VîI3Ýj3ày.®ÉÀú„‹¤Ã¹ QÁíÌÃ#¡¼QûUI_é¦bxÐ8d†^÷¹ªåH)#w†Õ•¤‹˜J ïq›ùWm–¾·ÇR~|_ÀìE–Ü+y¾û~êù¸ÐÂuÒ¸ó6¤j²[×%IÌÙÙ`IÙw‚«7áØ NÁÿg2’ˆd ñâú¥¾‹ endstream endobj 126 0 obj 1220 endobj 124 0 obj << /Type /Page /Parent 112 0 R /Resources 127 0 R /Contents 125 0 R /MediaBox [0 0 612 792] >> endobj 127 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 129 0 obj << /Length 130 0 R /Filter /FlateDecode >> stream xµXÛrÚH}×Wô£Sµ–gF÷lÕn–×Ä\Pœu\y…0Ú‰h„±÷?÷¶GŒ§ lWY%}¦»OwŸæ'|†Ÿ@mЉJlBl0,,MSY_!³§p Åð De:¯â­Sy‹–,KWñªsh{ iå3xeø¼FtÞÎ.¨J€‚7…“q· ­8÷£ì›M>€÷¸^q0iX Ž£¿ ÄH§è¡„0uÕ0Á`ª†‡Ã(ØLµmtO·| †`z ¿Pú«ÓÒ_¼2Ëf’Ò]C%Jéîåð+ô[ƒ[¸èöÜñŸp§}—:*¶;XšV…±ªÐn°F­Áù°¿©ˆüæ—ÝÓ-k×=›AûÖsÇp1rÝc}C y³ã›É0x‚6Ò·VÏkuGpÞ_û·çÎÝsh·ÆÝN Ry½o¦£mC*'7îhÜÅäÔ8íÀ¡ƒáe7”@Ë4¶á¤3¼¾uÿºô€:–a…~×CwÐQ+/ߘA‡îtxU à1ä0°tDâUÔ:%SФ± ! •j¯³®Ô¬k”ÖËhIëp¬éªIÕM³ò໦ËþçÿîS&“pmŸG܄Қ”ãš× U%G™(+¥Þ$½YÄá±BÂ.VB~÷Ø£Òe27ˆŠ7ÄÅÉ~b D]“ê¤DUDyvC qÊ"ßI[œúé–1Hiû¶@¨¯ÖZ¨ȇÂûEª,f–^úÔwûÃÑ-Œ»ß\”¬Y˜/³¤!ÙC-ÇÞ8Tå©×¸×£îÀsG×i ɶ¤ÚYKdTWîØ+uäàK¿] Ö,ÇÇ›¨ïèRJ¾÷À•u¶öq½r¼ÓÀ¨!‹ rP§¦c4¾à8øÕ"PH!ÂàŽêŸ¾ôê²üMÇpiÝð³òîn{í¨ƒ†hÊ4\Â×^¥ðÝ¦ë² +/›Ú˜á6ýØ\¶«ÝM@X/Ã%5Âk5ÈÎ&ð’éjXdéCæÏçB Ä~ò°DÉËáÆë2@Y€{Ú`Øk@à>ÀÌbtni0WˆeÃhعtQµæÏ ¡Q¢ˆü˜§ðq˜ßÇa†re"ÔK&Zf•N÷gyvnÓe¥FgaC'·èžzô_©«ä×T¢¦køó jxVÂl©DLL?æq3BÑÀÁ!Q¤F„Õ, fB¸ãÏW ‡é2Ža’Ëyˆïä¸þ<]frC°¢•žG­µÖ—˜•¦BnйET=ËØÏÓlý£JóÏÿô0‡‡ endstream endobj 130 0 obj 1358 endobj 128 0 obj << /Type /Page /Parent 112 0 R /Resources 131 0 R /Contents 129 0 R /MediaBox [0 0 612 792] >> endobj 131 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 133 0 obj << /Length 134 0 R /Filter /FlateDecode >> stream xµYoâHýŸOQÊ_ ÿæD€ì¢MBœÝ;v%šÄ;`3¶™löSžî¾Ð½jÛ±ÙÓG ±cª_¿zU]]ý…>ÒÒl2UEµUÕ&«Ó¦Ža(]BA¿OýH£yDªü‰æø‚ªè¦Ê—|ÔÊoa©Ó1üGkÌ×tåa$ïàSÇû†jêä¬éâZSTÒÈYÒùttû#õV±ë…ÿ´ÕwäüACGË óHß>޹ C¥`‰æC´MÅj“¥+À[WlÓ3;]\V,¿q¾”Ì×Â×+>y¾º­&ÓÕµ‘L×R4+GðmdVšÎÆ’KO¹¤,ëvÙòC: {7š»+”cýdj ?ø”z°¬²œgAQ°Œ_\°ïE´qØýãy:»«ö[Ä‘òV´m[ŠÑ5ÚdÈ‘×9yÛy´Pþò6ÇqØ(Œb›]ÅÔ²QÞÈÜpþì} 9…[Ÿ¼¸IñëF+cà/H†‘Ók° i¬×®¿ M¬7 "|×8,Dª,0õ¦j€zG"ˆÐwc [½’ç >Q1 ôlHˆgŒ‘ýryëg%¤JM8‘‘J8"±Ü®èÙóãèòPo¥‰©à-S·È2»‰‘½ÒXúw –Í‚åœçŽMrÌ<˜aÎ-ÿƒ¢çà%"w±ðb/ð¬©|"åôàa¢ÚzšÀATšÑ&ª*HDÁr’tòéLTÕ2µÇòÏ)Q¿ò|A_ƒÕv-"h^êùÚ[‰pGØ ^’„uÓW»²Ú;Ë'VIXÕòèò¿´~ƒ&ŽùM(–ÞŸb‘rWC´3[¶Dû lUEÀò‚åï$¯=–ûHÕ”õ$©BâN„Ä©Ò[¹!ÅA°Š.©Çîü9}- Å<¦…}N^IzM¨Æ7øµÓ³*{ØÖX•o²¿ï®Ån¡][?Î}KγH"Ãóç«íeæÑ‹·ZÑ#îðú¢&بÀ$²ÚËßI{,cqOP–+7ò朊„B’=ý4u†·J|{?ºNØÁ÷½i¿w“?‚bêq8×<%‡˜*_B¹êMGý|X )«)jªú½ÚÜtíùpŸéÑ’Ë3zBŒžÝ¯„ pÙz ‚eÑ…ÍQÝ¡ÔCpeL_&ÕÓïÃ&=‘Š×Î6²¦>cç‡"Š¹Ú­-Ê5Õꔽ½F±XìÎ$©‹\."5 ›ªÒ~n0ýI…Ô}lè|T"´ÄÆå$‹%bEåe°Y ?¦ òHüœn.šÔ‹"±~Ü¥K9Ǧ–âî³c­ÜŽ!ëYY•'—úKúav.P‡OgçîWѤ_fçÏ.îïðø¥I7³ó…6i‚Ûu“úø¯ÿÔ$gvº~Ô¤¾ãkgç[/¦]ývdЖë’Þ7€ S|pá›z°2Än‹šsÂC¤á;êOÆ|O¤u‰º­kñØêà/Ü«øeaJ7vyA¨iÒ†2¯M{]ËTɰˆVo¦ˆtÆ%õƒª Jº ‡nË6#Â6ˆÒ¢—ÇO9FÎxÂuå!üö#⢲qÀ¶ŒÈ”%ˆHkp…ÇOÝŒî~ÂzÂÊõ""M…¿örth™[Ad§ƒ½^ËÖ8"TxdçiºYAtÜ·ŒÈ2ÑßéhúéÎéýÊ‘zëÖ}Mt¤¡ù•räˆ?ãud8*êhè<Ücñ %üB%¬™9"]mï•”}ªŽÚ’£ŠŽ®Fwƒáä $ 0 ˆ ¿ï££¶Í}‰"Ù!dV²|4íî®eJÒÞ([78Ö¤ïjÎGè‡îÓÑÏÊ‚ÆÎXq†¿:Œ‘°Ä$ùˆ‹n@éI¬Õ¬£N£‚ŽrD™–dôs†L¡ûú÷^;%Ù’£×Êù(©j144d¼åÈÌ¢?óÚé`^ëmÛü?:º]Mz“OŒHë¼EÔÉrv†¨ž|Ô5Q%•Joæˆ «|]Ó»G¥è?u]ë8*èèf<¾ÏEÍ•”mä9»¤ìó‘®JŽ*:’ˆ2QïE”ÅZ‰£×5]µÓþÍÛu GÊp2O¦ ÿBËßF†L«™WUR½:Ò5#Ý»W º´bÞê0;ÎEpñú›!ª¥>ÒµG¥9»7Þ^ñþ©ÑoäÊ6Lƒr ¹Çk'ä#]—Utôžî¦Ã}`&øê23ùetÛG¶}KÕ‡®Û¼áÞ!ÈÖ1­{BškŠèýÊÃVlqáù-ì.>4 ƒÓã*˜Žh‰E“ÁmýìO4'Ñ8zÂ&ñÈB»ÝN_ð)§LœRÉ„”Ÿ<û´ÙV4NƒÓå¤c›¹EÓ]œMí1=݈¹‡^7zkÛ•!-…o±Ÿ>Ò™åc¼¶<,3 L)Z ƱùÒ÷?˜«´[À#ͽ¦oÅ:_ÉÏEÑc(ÜÏ›€ORhvnãø™Ð½;Rf…c )³6²”YÞ—ùÙEÚÖDóã ¾ IïÜGÑÍQ7¢û>}ÙŠ-öØEÄ"ž+8<{Æ1aîúh»Ù!æ³–3­i½Ò?«R¨P/µÊ/ŸeÆ¡÷ô$B4V_¸÷ær½Å;U¸„øÇ \€sÁ¨.èêIŸô>EC¸È4/¡‡Ö g»Y •1{''#4·Ü °Ü„ލã<²caíÄbP*øX1q8µÖô~G͇<ð>þ’5që endstream endobj 134 0 obj 2079 endobj 132 0 obj << /Type /Page /Parent 112 0 R /Resources 135 0 R /Contents 133 0 R /MediaBox [0 0 612 792] >> endobj 135 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F4.0 29 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 137 0 obj << /Length 138 0 R /Filter /FlateDecode >> stream xµZ]sâÊ}çWô£·*Æ ˆª[[e{½ •8xmnnÕ–_1˜YKvF2&¿òþ¤œ ƒ’¬Ø}óÑêé>}út‹Ÿô~R7 ¿ÓîN@ýဆ½^{ä‘‘ô%tqm»Zê¸ÿ6Ä:mÏïð?÷Òyõ', ‡~ït[aLWSêõòÏàÑÃç{ߣiL_»íuiº ³‡ñíßè2J…2߃Î'šþ ›©s¬2ÌWúõëøõêuI/pÂê¿ÝPßk÷à¢xí Àñüáÿú-„`ñ‹ç¥ü¼~7?/ù¼Þ W·•÷æU†YªtBkEdS½¢õR&òE é•4½/ÂPZ+-ýéнô¹MÿÔ©¤t)RËX› 埣™‘ây¥U’’²ŸZï c½û½.|Ž[»ÙJtJ©QOOÒÈ9Í6´i¸TÉ…zŽpoýzÖ¹Úd·¡t–Z…ÁÞ¥[.T@Ü_Þb´“í§¶Û\¸÷ø5ËGclcL°±ÀGÝ,€ï6×0Ë? ³È 5t2?Ü&Êzí´×“1Æ7«„K>;=Ä'sž³ö~*àç¦ü ÷y•…#B¥ÜÓ¦+.>^ˆŒ1ËÎçÊÔºne®90¡òÜsvÞ1 ª!§‡N¯¿“˜õÀê)¤$Ù• ÆPðrê ¤u½BP"ׄ…–å=W±D›8Øñ>º¾`Õx¢¾Ó§b‡ÓS$0„×Hl¹À˜I÷œ^” ©Œ™”PåW†`f¬#b7âDJ–¬ßLJٵ‡À©ù7P(T Xf,©:Iæ¹Ô¾ M»ÌEÛ„gù*XºÍ63™ŠÒsFRhYó9_GÝb¡R©9‰ÂÕóŠ_kó 'sˆ¯"¨ Í<£€«‹”#DÊ¡Žt"¥4]ª©·ÆôëwµP@ÑB!]ò5W)Ž'yÎmÓïå®ôþæò S³jV7 —l*‹6ÍÑëôx‡ðˆðÕÍápKfn[îòN41-n7Íî$vcSï®ÏïÆw4×øªkªÀFEÇ:iÊëà`,OtržÊWìœß Ç»é}C³’×Å݌ӱ2Ý8ëL3+þq?žÞ”cnÜÒ"+m´@%ça,“î “et þíî¬CCy÷Ѹ{Ídz8™·y†.®ÇãÒ]n€»Áµ³ñáèy¸Qw¢T—¦›OuiNu$°èÕ¢¡¡r1 ¹”¢6xÏ,¢µØXºÎ[Íùwˆ®ð›É×oqŸáôoo'5”ëžwÐVbñ Þ°|«Ê©s¦’dz;iÔj‰›wÄŽQPç]Á¨ôÜ»ü¾_Ѹ1<—KÁãvÈèm5çݨ~dÆægx"iTgú«zE#b¹ðIJ“3~5ž<`Â01Í4f!ƒéÉVÒ 'š€"B#ì²!½éùÃbAÒ|PaúTA­1}«çyƒ/ù­¦Êí *p£}SKa¯ï3_žB U¦›'«ÒëÊt¾vëmwÃÛ1Ú׿*w»Ài¨ ]¦zhŸæ±Ó§Â^ékw?« v¦`J`n)_ꉃ Ú§7{£É+MWXhH‡×™¾ÅÆåËÃßݸÿ0žà¾Í‹ÂmlüäÚVÏ2´Ìdz•Á«:³ØÖÅ"R!?ÇH†¯‡:‹æ9 2uE²¼aï`¡QÞ.|ÿí}´™~$´¯m‚.“íIè¢4Ý|kLçtQó3r¿ÓiàW:®ãÛ1£y‚€éSDé<^¸EªÐ¥BJU 5ˆÑN“­ð˂dzb Å=ëÖi½‚:ZßèÛñd( endstream endobj 138 0 obj 2251 endobj 136 0 obj << /Type /Page /Parent 112 0 R /Resources 139 0 R /Contents 137 0 R /MediaBox [0 0 612 792] >> endobj 139 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F4.0 29 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 141 0 obj << /Length 142 0 R /Filter /FlateDecode >> stream xµYíNÛHýŸ§¸+TJqìñg­–¢¢V*]ºP­´„NâÇv=No¹´gfì$Ø®„‰K¥‡pïÜsÏ9sgøA_éYº¦{ºî‘í:䚦6d”ôÅ48ãM8éòŸàtYºø’omÉu- ?1z“½¿&ÓTŸÁ+ÃçMÝbt½ Á¹¡édÐõŒúWŸ.>Òi”ûaö¯§Ðõwúp-¶ ,2=?ÕœÈd”ÌPá&…ci¶C6ÓL,(xLó<”g¹C|Ù=@0{f½¤êµ U/^-fsLU.@é©rÿÛ¤’ ‘-¹W’vl£GXs5òétLÉŸNÃ}¢±ßÈŸAÆ#ùœ¸¿H£€x2ËW~ôÚ±¿`¥]opÖ½»†ÈÀ‘«Üh]=²À®!²Â.òU’݇ññeš&Y.¹vv9ø›ðã)]~ùëê…¬s¥W¼ ²=ˆTØ[#çu\nˆÜ¹‚uÛk6®ù4Mƒx>Óåc>Óø$ S¤ þåÏ‹÷Ÿi ç ¥‘Æ/ä_eEŽí*þmP‡±Ÿ=ªlmazØJ¶ B´Ê…ËVÙëVíýAƒ%ÏH6HeámSÕu-S¹BO`ÅV*:z}œÇcúv}~ä‰w¶òõž³{Õµ òy¬ð¾2_¯¿'H¿ðóö!¬6÷ÇGîZ¶¯!tÙ¾|fð$tpqöJäǰõ_u²£þ9¸'úMýCèjÿ„Sè.ÆV©tDFçuYqi³íÀ§4ü)€–óÜ é-ÎŽð‡BJiÂåõaTXÆÁ%îI02É uÄ ”Ôø®Û`Â8ð ¡Šóœ¶;ÿäxâ9vÕjÃ…<óGÞQŽ¡]“ÏÙÇ¿u`ËvÏ ý !Ã…Z ¦^ô]ÅfáƒÚÔv´lÔ×qŸµóŒ“é¶°ù'ŽÎÚ–X·½¢r]7úíîèª%YµR$ Â|Ô‡E´ºÙì+¯¯7Ö0U…è6hß·oh?ï«´»Œáš§œyŠa,Òj:Øì½—þùIìªÌ“cPǰ’”Rn"Èòû­PôMˆoMù¸»ÿª¬øs]i„·Öh>é®J‹6î¼{K€‡zíT<¤Á÷±gÅÊ»Ž6%ýßP²ã endstream endobj 142 0 obj 1700 endobj 140 0 obj << /Type /Page /Parent 112 0 R /Resources 143 0 R /Contents 141 0 R /MediaBox [0 0 612 792] >> endobj 143 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F4.0 29 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 146 0 obj << /Length 147 0 R /Filter /FlateDecode >> stream xµYínÚJýÏSÌEÊ i“Åß6¨‰D)iû£R*¨zÛ’FÆ,a‹±©?Bõ)ï}¡;»6ØÁ®ƒK"fvΞ93³þ á'Èh‘,I²@7 0U•t(|ÚýP'Iü…þ@"Š&ñ‡¸t–½EK¦©üDn8Kx=UM¾ƒÏ ~_•4FKh_ÊDF3h ßx=7²YðÕ’N`ô#±°Ì0÷ô|?Z¹#\ƒ?Ã3†Ftt…¨¸8DÁRˆeaxšÙÁ‡Þ@fÏŒ’x59‰ŸU]Å’pu"5’pêPvG§ý9uÃx ç@ïWÔ‰²KÙ+a½ã[•Ì­ïj6;Ü´.¶±`zçç wk²¯k»K/mÐ;æÇáÉ¿AÑõ“F5•oœ&qž6Ú—¸q)xÔe3XÀÅQ¢&Cnƒ8ŠšÖyF”ãV!Î3Ç'Pg´c7ÚêT))‚ì ‘ð…­|uQHšˆ¤kê.Ä«€y Ò„˜~4ü˜‡zçøÁŽ·~G’òØÞt»´äÊ1Á¸u8ÉPmAïpuxB²Å)_Ç8Ý®`CôñÉáiÆÕÉP­]t¼<Ç¢— ×äÆÒwÝ$η&ߨu]!™jZ_2º-xHr=¡˜f!e Ù‰ø¥Òžúlð"ûœZVÌ ^3-3ô†0,.õÆ­€†±U¯(HÜSGÁÅ‹sÞzÎ\Š”än¾1@广Bï̃qKŸˆ4mT(×Å9;FFÄmˆg{ìZÑQîö†Á0’îŸ43-_ʶ éì)dyÓªr‰i¨Á4V´2Ó½ÕŠzSvß…wþšoÊ«ð-Ä+øÔ¾+;tlÞ¿Ç0}øDpøF uL ±QÙ¼qÅ =.&b¾×…/~Œ Å’·öƒ_×”áw"?xÀRèá`â…Íq@½a‰ßÄŸ"ã&ÔÅhx`‹ñåÑ’€…`ßÙ̵'.%u±a[Äh»œ$°×äölœVæ4 ÝÜ>ò¹I†ªC[NÆLIÆá¨#ƒ¬‹E<™ÜæQ´ê¶Û…ö Bâ·â]›ïêM²«íØ §7|kÛ²eà ªel„dPP(%bâ–I²˜ðkøjÐá/à Î =þJRñG¿^òWÿ”ôùø‚_‡goÝà§}O¤=òkÂÀ­½1Àµáÿï1 ø¨¡ÖÞ ÜÁ .ÅAÓJqJL÷±çÆ´IˆœtÞß?oR§ê¦‹„Ë4²ÄI§ÍÊ`•˜æ­S‰éº¦Äôà¡âƒI2¡`j<¯W+6&ksß!Ó—Ê0•˜æ0¡é?Å©Ó&db׿CÁ¸•N:“º÷`®]"ÔÙœ!gUkBqü g?þë7Ø endstream endobj 147 0 obj 1838 endobj 144 0 obj << /Type /Page /Parent 145 0 R /Resources 148 0 R /Contents 146 0 R /MediaBox [0 0 612 792] >> endobj 148 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 150 0 obj << /Length 151 0 R /Filter /FlateDecode >> stream xÍYmsÓFþî_±„~p:FèNï™*Ë Ñ`'V€‚›ã(&~Á– Lùñ}ödG~3NÅOwºxŸ}vo÷^>Ò }$á’©kº«ë.YŽMŽahž¤IB¯hHƒ© þ”tõ7íãtMš:Õõ(oâ—ÇÔðFTúªÇdÙÔã Ý”èñ¡Ðt_Rµ½8"ÿ&í]MÞ¸ú>ÅS+`ù³¤íå˜Å‚ “F—Ð0a›še“%5àÀ‚+5×…z¦ã¡XPp¹¥¾´©¯c’´ŒL]KÓ+™º¤J0›L’aJñ¤×¿æžEG'é§£ÉʘúÍ(“jL®þIö+w£ª¢¡3ÄÊãC@œ[DI'‚­ùcV¡#XéPcêk]䔄L‘·‚ìøòrš¤,P'€%Id a©Mä áyD>ÞQHtˆ=ââw‚(ROÊÁin2¦P Q÷HªO4º í$Î3I—$<BG¡ )Éãúªu÷»ûך­ýª¡D½=íkIP-sÝØ T(¨r¬ZC@Ó*77æ¢$z€h@8íä_× lI@-½¨d Â§zH¤:à ! øàÕ¡CR0ÁÌ}h1ÞŒfú¤~ =Ô´O4-Ô²€3j0ÐŒ1„R†¨Ê@I<Ö‡ñ j€d4X…% Ìãôá$øE;í°”Ô.f±@âæ?|Ñ (⢗5Td8&fÅHÅ03Šrþð忿˺¦Õ²€ZV‘Z ÜùÁœ°Å øA>Ã>ŸaР–M¤Ü’€:k‘“'“­€*Æ2Âèð?v§Ðãžc0=žà Ê<lp¥”gx§˜Ogî¡·~¹(0ðÝü ×1©Ë9À~½†{2@®ýþ†7a:Nì¨yqàJ7ã3_ô.Ód’çë[-=Šóœë¸µòXÏs‹ÌûóS±‡T±æP › ÜꧦbÏÙ@¶“©Xè›òœä|°k¹X蛤2ÒÝKÆB ­9&#ݽl,„[ˆt÷ұŜî^>²˜ÓÝKÈÂ0°ÇZÝ˰Ÿn•‘"8J|3#ój§¤…ƒ0ÜBœ»—’…¹XÞÊÛÝa'MÆdÜ3)/¥|CÖ¤,"KÌyÚ? ödÔO’ JGttÔîMû½z÷…Ò/ã«á{Ú«ž=úm’¤³Éð õRJ?$´>Ù£þh0è /h< Æ©Fñ—qr;òž[©%è¼ZVõÊÙrܳazuC_F3š&‰B3H†3zכܕ¯ùùɲPÇ‚Pl ÖV1A¦ë…ÝêÅUZ£Ónu6¬Ña·zu“Ô(èVABšh¯kôº[Mú³oünu:M5j ëÝì}í)½"Mÿ«$†lÓYgh!¥³1±úF£i‡©`òÁèÜh6¾è¥ {“Þ Á2pJ½)]ŽnnFŸ¦%0Û›#Ì…?€ÎÏ÷äsùð ‡IÂYð™£åÅaøYÙ“>}À¸„ÍŸfìÞÓå6¤z¼,ǑݪԨőߌÞD­g¿UJÒÝ5盃Uß’Šþ’Ôw‹Õï„ñYû€'בßzRÜ­†~pDG¨›m:éVÏ¢˜Þ6Ä}'Óº¼M‚»ªX¼38ë,ÚJ³êt«°q3ìîS»[==~юÈ3~#zÝÝ¿+Œ‚³a>+ÕMÞdl8»xÆ*df¼ÞЬðùõ6ÇÄÅšKÝËEÎÏgÛwÕæ?-ÌBR£°Ù –ÿ"¤ß鮢6‰³…®Ù\CB^Æžs›©ë~ð¼ÓöƒðûåH6§½"±ß¤FD/P…¯ýų:k¥à¸Ÿ7ˉRbá¹¶ÈcᢶN¨ùdrñ‡zGµÏJâÁØä¡ ¯üVLñ1eó†â£¨C/ýæYø”ºÕ?k­Úøm)göÒ(æà‡9W&S3w®°Åǧ8Ž ÂvLÏÃ?—È­|Ç”4q¾˜;Y…oˆ”õ¶u²ï¿?bW7­eêžJ¡à/>K¸­æ ¢ÖY“{Ïÿ(ÉÄ–â·9åËß7êy†æê†ÄÍîèržnåÃåPlYó¥l® ´—œh+ 7#¢! ÍÖm¾{[X^2´ÂWÙ¤< 2¤©¸h«ÀfâÚÖeˉ ¶Rw庌’ ìÍEâ _âs…Ò9Swwço¾ŸuѳÉÃÿÍ+CVô‡ÅÅL^Q\ ;ßKŒ‹˜µ;Ý¥ íÕÛb¾êç"ùŠ€„ò,~\¿-).zЇ7ßÚÉn×Éÿî㥠endstream endobj 151 0 obj 1742 endobj 149 0 obj << /Type /Page /Parent 145 0 R /Resources 152 0 R /Contents 150 0 R /MediaBox [0 0 612 792] >> endobj 152 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 155 0 obj << /Length 156 0 R /Filter /FlateDecode >> stream xÅYmoÛ6þ®_qûÖBR¤$6E¦k­²ÔJʼ Å€!h€í†6ýÿØ‘z¡ª©\ÓŠX±ãðø Í<«³Ý’_ÿyÿ寘\@ód£7fV–æÛáÓ†ÿÝ!Bc"ä¾A0?ÀÍ! 1óãáñh…—ð‚»™xÁÆq`"há Ÿx-\Àk“É| E²“ð ˜íÌc-y‡Ì†”ø¡à±1 ìÊ*©%äY!µ3áADƒ¨s BeÚ$9¬ešíð¾•&ýïIf¤eÑTe~áçÞiZM«w¹AZÇ›P!꥿uoŠë\}ú÷onXà6 .¿OŠšÒmR¼”Ðl³þHòkù+¼}vó¼xþÓÛ 7 pÁ1°,N ,ÛáC`µæ0mVVRnHDaB#¼ºhh‰ibUð,Z¡Í²¡ ±dhµæ&B óHk(7‡êåÍVɇõÕ+" [bž*?Ú³sCìôâ¤6¡ î7¡k ÞÄœs"Ÿ±fÅÃdóͱ)ñ/¢…4Ð,ìP¡Q/'«­=Ë¡û²Â‡U†ë†ƒ…Ôc#z9ǶöûJÞÀ«äU%GM‹Â·SQ7%¦j¹z®´È£m÷<˜šæ´hë›RstÑ~ó?ÂÔ1v endstream endobj 156 0 obj 1177 endobj 154 0 obj << /Type /Page /Parent 145 0 R /Resources 157 0 R /Contents 155 0 R /MediaBox [0 0 612 792] >> endobj 157 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F1.0 8 0 R >> >> endobj 159 0 obj << /Length 160 0 R /Filter /FlateDecode >> stream xÅYmoÛ6þî_qûÖ‰’…"˵YZm9iŠÀÃ,À†uC›þ쎢EÛR ¢e E'áñ>|îŽþ á+ð|汈±‚PA(¥7ðíO¸‡á:yáðôLÿ¼<á?0OøŒýÑÏvˆ3…¡ïáoøäé Ü” eõ7Ø ü{É|帞s‡òÞl–«Äÿ|ÿã¯oŸ#våß–zavb²ÔÝŽßnH*øï=´&”ï áI\¢ /ŠÐ=?œâL‚çŽþBÓßÐÈÊÝÐc“Ê]iÍwC²eæ©ô"&M?Ñh5š§÷pgÛôèm¬eÛÐ ÉŒGvФŒ3˜¥Ér…ý"ýïßãM²\BRäåºÈ®&ýö´ÅcZ„Ærr=?Yz ’š}g€4úDŸî’¾·£à7QÈqúû8/¡, YÄù‡ÊÅrSÁÿß<¼Íßþôxå?ð‘M æË4›A¯RxÃ]Uœy*ðñ8hsxV‘¼æ¬Þ¦0/Ö0϶›ÅpS·5`äZ9ÙÖQ¹´#Kì¥[Ó™@œÜÒ绹T…q¹¥‚`LnUæZ¹µ)‹ßÜ€2òéÂÔ Û%òˆJ©ø”¼–­Y¢_w7(DMÆ¥V„1vDÙªÌ5¨… =K³´LI¨×qR¦kƒï„’ .¹F{\š2r¯b™‰ñ´Ÿƒãð„Utª1nRˆÅôr Ò]ÕâëÍFw 7±‰3 ÃQpêL3'p„õF˜xE.ž+'ˆ'=®šÄ¦º”‡÷¹õ¥‡›¤&Ô07 dâÕL~WS-«‚ðîWG8âxqu¼Ñ©æƒHÑ œjµ=+—tñ‰Ý!Õ²t^:¢ÚTC\9x9ªMÛa&ݧ† )}Qª ¦Ìýô±²Ÿ‹jÖÞ©¶^~X8âšàã sMðvœ‰kÒ§„®Ty¢ØÌM½úøèFׄÀ—“¤¨wÖ×:öYÍß̉ºÝ;‘M!”ýòÁ¤÷\ Ï?ê¬Ï8<,YŸ kŠšl ã*Bj<”Wœ5ÙrÖ„âl8êíœÂΡØIÖ'|ÌöOšü?›>VöP­~¨Ûý…ñÀ«<|±@, r#ß! ¥¾gÉiÀëë–­Çßi£âæÛ »SmÄù/«J™¯¬ƒœ?¥Vk¯i£kZB½UM²ŒZ‹PµÞñk–V÷6˜âÒ£Ë`z¹+©…ÝípÔ5·×ð&Ú¬Q;gÓÆÊÞ‘6fi<ƒeóu±¬YnŠx=3£6~ü .ýÐ endstream endobj 160 0 obj 1233 endobj 158 0 obj << /Type /Page /Parent 145 0 R /Resources 161 0 R /Contents 159 0 R /MediaBox [0 0 612 792] >> endobj 161 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F1.0 8 0 R >> >> endobj 163 0 obj << /Length 164 0 R /Filter /FlateDecode >> stream xÅYaoÛ6ý®_qý–BR")(:U¦ca²ìÊJÓ¬m€!X€ ë†6ûÿØ‘’%9¢)¦\,9áݽ{|G^¾Á{ø4‚ø$"$.È ðg ¾ÿ×ð\$Ÿ‡;üâ³èa^ýÔ>âLR†>~C½»¯ð®„ ¨~ï ? !ƒò+\,¨O€BygÛtµ„øïÿ~ÿóûo9‡ò/P¥q¬X[n'´ $ü{¶&Dèsœù:‡(DÌ" /”3ÜCîÆ ýxeŒU¸Ü'^.è±NÊ8ƒ¹JÒÞ—êc¼ûo“4…d—Å:k=}1èèD@Z'jÌúBªOÕ­~ȯ2ýþö—so\6ì(ïb(Ôä8ýuœ—P®!YÆù¥‚r™náCœ]©·ðùìæuþúÕçs7„v©ÊæÇ+oàxK‚_ðP¯¦pÙÄ›©xi®ÃÝ&…RyÇœ7„çvdC6ü2Èzg&¡'å'œ€Ê íÉIøÅ ȯƒôÊ‚ï,ð#0Àé=£Y-}OÊ^±Ã· uDì¥ÒÐÀD„ÐÌß/W×Õbü\c¨C‚ó}²Ö<JV'Z$‰vbO‹ÌŠÁ Ö(=˜ÔWúN_õPÛDßn?¥”¥" ²Â`1t‚@dG`*1¬ÌaÅoÙ»ZP\ÛuËõJ¹6ÂRÞ_¦Ãk­pgD;a¥åšF@+–ÍÍ^’Â|º]¹Aa¶C¡…{´Zˆ»ÓÂfú®V «äNð¥dpŸdc¡ë‹}#†Ú@_í;j8ÞšNÔJ¸| _ÝlÉ(%Úº¨ø•a¤dú†Û™z,Ç¢| nÚû´H™=ö©°¶wP‹ôrYvÀ=b?H:¤òôùʤï™Û±>h·t µ¢l¬yDUeÕ¾yåˆÎ‰{²- Åùûª0L÷Ž?Ðjµwжº'¢ãÓÖê^c «ìÝsaMïiÈgV’±îáÕûdºWÙ;¨{WGÈ ¡ãB;]êbæžaTÓeqÃ,iGWSêÙc°;Zp¹÷£Íü{8jã¸8¢ØCÓ‹· Pð±$zjó‡ú2ßÁÑÖTÊHèHFSÖQµœípÜK¤&*%Uiæ°Rm|Eù@ܳ~ܧAF¨î@÷ ÀT"XÛÛÁ|æHó2Í/u÷³ˆ“R5žnÉ¿¼#Ȉ¨;ì˜Û-g7ùeÔ Ûç5RHš¾ sþ šmz¼uÃiFMÜ{yÌ,'òËí-kz2f5öZ5Üj‘~TóOs•©›,;r}q„2ÃôYV6s …Ý|Ó¾ìã/âl먯Ãpú×Á™uRwX`z²ÌVöö4£É¬š§%öÍâ$Q›~U7®RRÝÆß[@z}NšÚÛϖ̉SËíQO–ÚÊÞ“©Å†s¼Q.SË Ì§M­À~çM­ÀMŒ…Г¥¶²w µE¼U¥¹úâ¨Î˾:?ér•¢×b?ñr¨µ¿>YN+{Oå´úw®«¬F„Gdõýÿ&ýP endstream endobj 164 0 obj 1213 endobj 162 0 obj << /Type /Page /Parent 145 0 R /Resources 165 0 R /Contents 163 0 R /MediaBox [0 0 612 792] >> endobj 165 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F1.0 8 0 R >> >> endobj 167 0 obj << /Length 168 0 R /Filter /FlateDecode >> stream xÅYQOÛ0~ﯸ½Ä‚íØŽ;išBëЊ6…4…±ÁĆ´ilöÿµKÜÔ­J:b·}@”rß}ß—;ÛçG¸€G  8 ˆ"Dˆ$Daô<}‡+ø ǃg wÏ@Ê÷óþ 'Å«üè½ý#Eð/´w÷'9„¡ùþdøýpù'4 @!¿‡ƒùx:‚ø×ßo?ž¾(rùOÐy™˜ \ µÇáÍ@¡‚?÷ÈÐBH ‚!&‡*((…ôxÔÇ—è¡÷-ùBoĉÐÐéº)\Åiù £8=ÕÆs¸Œ' ý n®Ò£w7‡6Ëÿ‰M`©7âC2Ö“!¤ñTÃGx;’¤$‚+ ­ì=Ït2þ¬‡_uÏ‘ë t:„Y“qªo{»ÙÝ,sXÊÜ;NPæ5–ƒE–iTº”Pã$žÌõÛé2–WÂZ¦×Dî‡"!³á7µzn:Q” Ž.*jéå»ÒkðjEOð^Ù6è¥úÊxõvkè7è’  l7§uýu¢£¨t´ ¸¬?‡|­m/Öß|ivT²ÚkJ!Œ·–k—¸ ¿áœ¿ ŒHAÏYbx/U.YýV ªt´ ¸¬@×\gú’Y'™ŽÏn—í´WlÁÚìtÚ)v8…˜ªÃ-w=—K_ŸXÈåjë×Ò~ɹÖl\mj ÜvK“Éb>êÈRJÖöä)%"Z=G{1•RRdàÍÕ%Þv[çùì¼+WéšÂ¾\e¥¦ÈÑ6?¿¥J™P^]5x/»Šç¡žè\‡¯,ä:ëh DCS´È ¶H8[ ÞëEXlp:™10µ¦ñ3†‹e3ƒÉ endstream endobj 168 0 obj 982 endobj 166 0 obj << /Type /Page /Parent 145 0 R /Resources 169 0 R /Contents 167 0 R /MediaBox [0 0 612 792] >> endobj 169 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F1.0 8 0 R >> >> endobj 171 0 obj << /Length 172 0 R /Filter /FlateDecode >> stream xÅY]sÚ8}çWÜ}svÀ•äO˜Ù鸶ž‚!F4dã>Д¶ì’igÿý^ÙÆµ7%‹B„èèœ{t¥+… ø Ô“èÄ%Ä˱Á1 ½Í`=‡KXÁ+Cáf$ýÛÜà?™D^éG­ò{rSÇohãf oFölþÞ &±„W]ª  >6=ðn·³ÅúO—œø ¸HVv,‘Ç1ëŒ6Ü}B†%„mê– Ó ªà2Ýu‘žé´ñ²(Á§ùB•¯c³ŒŒ®£“FF÷ª„?LÉšžÛ†îƒÉV¡&\z‘1¿çEçD/Ã;¯?á¯!Ñ®šQó·ä칃¨ 'Ò3HI/¦PÐóŽ^Ñý½ˆ_ft: LÒØE©Ä‰Õ±ñ<»Ö`¦:6^uË >tCÞ òþ€ãuµ)ÑmË”³½¶QÌ»á”×Ñ0‚QF"ŒÎ¥ƒbÏ<~¦†§eât¨ðô'qÌѳ©IÝÚõúc~±&ÍÎ$±•g’´íæG¬,™·ó{0ÿç,{Ô¿Á Kå¢<öì@<_Í–s¹ˆéƒpì‡QwÛ;_©™hŒ•£ÿn¹œ­>vçÔÇŶ)3ëê ÝD[ÜΛ2Ý-ï›ÐÇûÕßM˜&Úüæa‹ßx‰¶ÙÌ—M™#æ>7_ÃuêDÑ1#®¬»÷²B÷¹Á¨ßÑ3\àkvJÐEÆëœ#—žÙ7äy™h_fxáÇßS!>.Ö©N’<êóeõ¹ "ÑÖ³Õ&Uc&åÁÕâa±…ë7ªô U=úŠô`õz‹5Ü.6ÛÅê³<4ÿŽ2À·»[x ¿?¸~íaÌ­ÔÇ2ç½P úЦ‘aÈQï9SŽzBót«ó\mФž’àªÄŒzmòÌ0^Œ‰‹e s[Ýù‡–ÓVÄЬgXd%?¦È@m€ö[I±ËL·¶ØÅÇBóaŒ|íGØJަñQŠÄ®D6çÝ û<Îx3(iy÷k©¹ÞV=ï{äeõ-/|ŒNk0K±Õð¶Ÿä̓PàþO^¦%yç^SÃÛÆ§m?ì=$RλFosÑ{„­†·cHìÿŠ·åFrÛ(æ˜ÞÎOx¿‰½øJ*è5gç55¼Ý'y¯"áMShŠGé ½ö:Ÿ»n¥æ”Pco0êóó¡ê‚O…üHnÍ[ÑÝ·–ã–yíâ_«×|Ý endstream endobj 172 0 obj 1447 endobj 170 0 obj << /Type /Page /Parent 145 0 R /Resources 173 0 R /Contents 171 0 R /MediaBox [0 0 612 792] >> endobj 173 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 175 0 obj << /Length 176 0 R /Filter /FlateDecode >> stream x­Xms¢Hþî¯èÚºªèÞÁl.[F1ñ.j¢xÑ]n«ˆŒ 0©üûëˆ%UÑ…|Àq„î§_ž~&¿à~h€"ð‚!¨ºº,ó B·àÃq+aÀþ¢> ð’"Ћ}UÏ–ø&]WxÜ+³%œ[ ËÉoð.áïeA‘ÀZÂqGäÁšCuÔí]Bs;^øÍj`ýÓbŽe/¦–>nG)6„_sD˜™Ð^Õ@•xÃ(oOÑx© Áüƒxa¯®€¤Ê \•* \ÀkdZãk¾5h›tE/IQ©÷œ—ºÞÈü;8ÔhZ2Ói¤©¡¡Ùló=“·Ì‰E×x) êõæ*¤¦+ûE¿µÌPWŽ;ˆ:gú¼Ûo›Ã7°5¹TÔJ1ê¾yË÷º£V·ß$ Ñ/Q¨wÈ]i¨UÁ\ï ÕcQ…¹· Ñé‹bâ{~ÝõÂ3sdÀÝ"˜=F°ŽˆËa+j°ö³Ïž '¼'Q\NfT¡ÐÇzžÀ…]%1#»ê<níꃃë>~ýÌÁ•]E¯9ârÉA wý{,»:~ÄA‹ñ±»ºöbø~þ_I>ïÆµµo‹ש&(FFoë´õàø÷ž<Ë|ÈÐ¾Æ ¨ûSSÕ 5äš$5‰ý¯0šŽ,³·©Þcªˆ ãò°.I­J›Ådê¾ØÒÀæ^/K2U†’ØfDm² 1y…4²šCäA°«C˜˥㻀ÏŠÑ®íëIåœ'¨! ëÒy³Tûý×§@7ò@G$»ž½øÚ;ðÊÉ¢eÄg \nÂ}l‚¼›E´°›ÅVH(4Ÿ<Ã}€…J¹ æAÿZ"†Ü®²ûÅÀ°)³Ie…ŠÌô]w$]…†BÑb*³YjW?ÿ1®ÞXø°j(¶ ¢¶M×ÃÁŰكö¸×›~ùýª¡HDUÆ+”´÷ ´½Ìb˜­Ã£é¸nH¢ÈóïsÁI¸ô|gôaÃ:ß!ÔQ’*ÊX:sôýœ9ÚÍœ/7ð ܽÀyw0‚:Ä¡7{„y,aìÐ[û%ú%ÉT*¾™§˜Œ–ÙMèÐòšLíê„›žtû–yaíZYù‘…n>7/ºý’Ò/ëúvúº˜œb&.Í>LNþÊ¢(bcg:n¤ÖôFÞ\åð&U)%\VM*ð¶é+¶é!ذ_+xšØ.]µQ q1Ó3 7(6´&Éyl" ‚YB;EÓ´ÝPÞ»–‰¤×ºDa$év›þ)rG_ޏ ½_•Ö¨ºœí¬QÍ~{opïpŸ! ÛÜwPÍ'¯¯l§Ê0Òƒßçù: GŽØPӎʆ°å<¢Òhö®¯ÌÍ('‚;'B&£Zc¸Þü•7¥Øøq½8y˜k˜9>¸‰\qèø^1å²ð\OøP ŒÎ£L¡lƒD—9O½IÇXºb–íê¿Ù0pƒgŸƒÿ°Åze× ü: Þ ðNC¯CÿŒ-~|ãËòV/–ŽÆ¾©Ü­ÃTQI‚^ [˜*4p&®1á1sã¦@ðØÁ2Éô#Œ’=¦*íZYàE•²¦jKqí}f<šØ•\ãS“éYàº9j5¯è žØµÙ:¦õ›®á–ÖÀ Gò Oš¬ ]‚KÀ³W¬`AžÈ‚%ûÓÊA²øDw̳b.¯7ÿ²1EK endstream endobj 176 0 obj 1364 endobj 174 0 obj << /Type /Page /Parent 145 0 R /Resources 177 0 R /Contents 175 0 R /MediaBox [0 0 612 792] >> endobj 177 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 180 0 obj << /Length 181 0 R /Filter /FlateDecode >> stream x­YkoÛVý®_1ØEwÀ&yIñ¡A›Äqc´†›ØH¢@@‘WÖ]óU^RŠ÷Wî—ý?{æ’%ÊíÊ© ²%ŠwæÌ™3þNèwMˉ'"? (ô®yFË%¿~oþodb%eþbò¼Àö^ûˆ¯|xàðáûrðZ8–x®g‡7÷„Ë7?€ôwvÙã;߬d½RrýLX£ ‘³%Zφ}2<÷˜>wŽ™ÎX‚ˆ¶g 9LJSL2®³GÊUR—xÕ6²¦E]æÔ,%ÞNIÌBçŸúŒæ±–)•…ù` ZÐÑÓqÇDáÃFOÌØÄïÁÄÛsá줟åÖ!"tÀ˜ ìÃ94o5µZ÷w[=Ûx79.ùžðnDÖÿ^Tö„Ú A›g–p$m`˜Ya ‘CžYÛ49,ÛëDh9ø.²}s]'€Ï„êÐpNÜÐÆ1D©À©uY[t·”°kÍÿi‰ìˆ³‘ Q^¦2Ó„ï¤m"S‹ËƲlï—xKåq­@¿–©µVÍ’á?!·Œa/C¸ßþl_¹õL¨Yå9®å£ˆH,•i˜VúQ72?#ÕP[¬Jø[kRE’µ)_wBç…@bùÓ[6ãÍëÛ«ÛT>? ¸¢‚yS0‰é$@?§§Ý€Ñä˜H'Á¾Ñ„‰3ºfé©Ê jt¡ôÝl1»5˜Ñoß^_ÜÜþöâl°w“–Ü›˜n`c¼‡O6–Ÿ0"×#¨ƒéHæ'-ÿ•-·ËÓ‘CŽ1“£Y0.âïŒzïWMÆ™¤bß1s‘•UõH)”ES—YÁ9³{UZ«•ÔÖÀËã<8d{0›N»N`ÈÅŸb®8½HI¥V÷ÅŽ =yDÂ{—l•Æ%%óëØãûÜB½ê¼9‘è±M+38ÐYËKÝPœeœµE«[È!*5uœ<ˆÍ™¾$ßrý¿õvQ÷}@L7Ø]V,ݼ“ËTÅ' ƒ@É»q[æ¨÷Vwr´£§¬4Võ®3»'ÏhÿŸæ†ÂÛÒ»/‘#boMË8¥<.gQ®L£¬I·À6Ö¦„_f±^®kÅäÚ¡ÏJ¥²4X?ÈÇy |Oà !L ÜãÄòË×Dh|É˵ï¡(× )F䪪éúÝõÍÇ«»w´.ý¾Ôriˆ«*S‰ñÕ¢_–²8‘é®3;4=Ÿ«½ÜÁóÌ< ÍÖj]. ’î㕤¬\ÃS¤ÅÈ™S™îó¨¶z\Ås•©æ‘Ö˜§–†*U-Wªl5à6˜Ï¥,(^Å*‹çÚî˜ÞÝîW,/Æ |.±Ý²¦WÑÞÆ2KÏ7!Ñ<ÝÛ9˜Õæ6¿ -µb]}§›WâÙ«OûƆ¾åÜúüËIí Ý~ÛáÜ·w½^[ d\«$É+‹QMÛb¡d–ÚUÕVE*¿XË&ÿûJTûû¡MM3+xú:%ÀX¹ýÀlð Å&¾—[  Î+SÔE•µÚÒ°7Û7‹¼®óvOŽn„Vþi6ÔÍÖ<Ë¢ÌÊûGc'f‘¹þ¬›v±°ÓÏÕŒïÈ^×ã™lpyø<)¸³é0D²¡³ oÊ43Âjì}0ßòyŒSßB·9Šü0Çà“ÿ¿}ºSÁíæbºF1a5þ J4X´TŽëšŸîûQ Ywö°}N/Ï ÙX¹n-™¶vr¯ÎQ·íuüÝkçÕ§woïn>žÿðñõÏï¯Þ—ל!ö¥¬>î4°0UlQê– ÏDé¬Á D›eçE™´¹DŠì¯670}õš…åÙu½?“’Í^³#¼ÂLc$0.Фë^ª¿q^¬õ@« æ1›°Èì_F€]°é‰- ë‰APzœúèæñ…e‰Î\Umk.±oYýºdD0Û0C33Î{›y¡Äœ2Â^4tì½å{&»é¯rH¢î³bòÁlñþÒÎЄ{ê㡼ǭƒæ<®“%ú'Ö‰!#La°F8^¯ØuDsÂ8îØ:nÛ>ímûIo÷ü´FóWË8SÿF :ÇJ6~`=ÙlÕŒ3Ðë¦å5µè‡Ó÷鶪ʺ8p4%ù‰Ï¨‰pÿ UÓTqÒâðÑö k5¹ÿÛ\êF­#Vß±þ™ýÄiºÈ¾}?•+Ø¡Žºn´Éª@*¡ËæÅºêþ¯S·Îª[r÷Á)ëÿjÊe¾é/¯ì§¢«¼¶hTŽg'|ŸŠ†a?ò’ãY ç™%#æ :5ƒ™™vbìö /ç¯nd¾“\ÀôæD‹7ÂÓÅúÈ-i—N=꺒 “šMgÇ©b¶›]©|y"x#S[÷N]_Üþ8Æ×&Z:l"1.¹8b,ôŸÝ­Üå½–qox-q¶a˜†©‚1q¾\¢÷<ÇË›ËËÁûÿHV—Ù endstream endobj 181 0 obj 2278 endobj 178 0 obj << /Type /Page /Parent 179 0 R /Resources 182 0 R /Contents 180 0 R /MediaBox [0 0 612 792] >> endobj 182 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 184 0 obj << /Length 185 0 R /Filter /FlateDecode >> stream x½YÛnÛÖ}×WÌcÈ4sœØõ…´1 è MnY<á-$eWýÊ¢ý¡®Ù›WQ),‹i‚ØŽE홵ffÍìÑú™¾á‘­kº§ë9 —–¥-M*}¤”Î/Jƒ‚’tù· ð]3mÿÈ_uÿÅI‹…­ác$ôîŽ,K=ƒï&ž·tÛ¤»„ί M'ƒîÖô݇ÛÕ ý/®ü¨øäéoèîÿôÃt¬;˜-½ÜŽ}ØLgk ìL¸¶æ¸ä˜šçÀ‚gjžxöb‰?Î ¬_ˆ—^ÛPxññš®¥àšš>Spï¯.o.;^ÆæøtË64ÓXxlb¶Ç(ýA÷"¨²‚n.Ï®.é"K«"‹cQлÌ/Â9Ýž¿§•Ÿç"¤*#ý· ý _¬7³ãÈ»%ƒl¹=;¿è:ÆWooOÇlz®æ˜¶¬r`¾Šýró\Dpšt…"£ 7IVì(é!¾B’ó²~Ô¶¾T¨»Ì¾ÛˆRP(ž¢@”ø)=©ÿ‹ð<ŒJùýú]”–•ð„çÛ´ýù×7´.²„ª ÎzŽªÍ÷x¼pY®Î]Éä¨Þ>ÉAýÀš¢ÚåâûÓ“I ·c° à²ù=;Òë«q:˺ÃvkT^ÓB<ùöt:kii $fg¨+ l›ßžnÁ^ššááCŸÎD‡…¯yI^©uœ\…a{Ø}‚7’7\«p¿¤Ó³È€_Ò¥ý馆¿0|ã “á¢D\láî­ ~þ0òZ endstream endobj 185 0 obj 2006 endobj 183 0 obj << /Type /Page /Parent 179 0 R /Resources 186 0 R /Contents 184 0 R /MediaBox [0 0 612 792] >> endobj 186 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 188 0 obj << /Length 189 0 R /Filter /FlateDecode >> stream xµX]s›8}çWÜÇf&QB|ô¡3›dÝd:n¶SO:íô…ŽÙÅÈOþæîÚ+!,‚]ÛØÎlttŽîǹü‚/ð h®MìÀ¶à¾>c$t Lápy]Qˆ+°Õ_ã6q\[~Ô­ ó/®äû.Áo¨ÏájŒ5¿Á«ƒ¿g¶ëÀd—#Jl 0™Â»¯wã[ø-¯£¬üØg0ù~Ÿ¨™…%Òî8îv —˜"CṄ{ÀÂps¨Bà @z®â‡[(ÁtG¾Ððåø¸Ü+^_κ±­†.µ 3;ØMÌÍÅu€qn)-qq­%aeÕÛ²2÷>ÁC×¢„ñ}at4¸´Q¯ŽÏÁuüF¾&Peó{“Ø\…±à¯r%0¾ÿìÚ×äêî3¤¬6Ö.1§£Ã†R î×t,Û ð´n˜×0E†"v0÷ɧ¦R0 O¬` `¬ÒæIõ¤Eô˜§Ép&z-’ † ã,.ÅBäYI†È±(êRäyZžYû•‹Í£“2rssn]Ž öA¸¹ºDhRÕÈÕuÏ`>Mb>Ú6yøxw„ÐWb6„°ÌB •‚µ€ñÍ×O6$eöœÊ#´ö(º›ÇÃ$ûbéÔkþ!3Í_÷glþîðÅeóß²ø¡Å£³môA[Wî4ÿUVÏàöæbt×G.ÃØDeþž°K„~!–ýìyšÌ’#74ͦW„›¦©ìô±ÆöZÒfAdý£hâ×~@—àã62‰Ðpm©ØÕ6KµŸM §úØ+B:‡úžŠõ,ÕP )×# ÞÓhˆ} ±7Ãt•e6L_ÒAêu©¬ÕÓ(»ñ(ªÙªÌê´td!6^ÇU(:Ž{â\F„¾Œ2—ë4/ðâì=e½ƒ “^K¤å¢[ˆÏG£®¢Ç2ôN-dèõ…Ô^QÕEŠöôêþ~" êÍ«3¶4yí(R=M{õats{CéP)SFaÿÇ¢6ýítF•º¨ˆšÝ»S…ú˜N•òÖ¯ó}Ý*Ësiç (Ò4ª:Âø`Ò¤N•VZsù¬¸±eÉÁo,«´{Ÿï†<I³!öÑkSîI …†À0ÀC©Dž"‹"«sHÄ¡¾§”Ü ‰ãàˆb&5]PrGùLT¯J‹uh»¦6ó:h&Ï…–*£/a= YŸÛBçYZ ?¯–8;FüýDz®_þe‘à0ܾ;;xWrŒ£^'lÚ9N‰]áñÞaX`LèHiAÞrɺC5Ë<ÁÈy*©; ØqJÀ ) j£MÝ2ItȈÔL V'¨‡ïiè¿Ö¢}Çö挤õb@”þ^ÐÔRßÿ¾Ú£ßÑ?nö:ßãH™ºÈ¹Å7ï÷ž¾¶¬º_1³Îú&©áíÌúò/è endstream endobj 189 0 obj 1263 endobj 187 0 obj << /Type /Page /Parent 179 0 R /Resources 190 0 R /Contents 188 0 R /MediaBox [0 0 612 792] >> endobj 190 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 192 0 obj << /Length 193 0 R /Filter /FlateDecode >> stream xÍ[[s¹~÷¯Ð#T…aàBí–:§¨:5±ÇñlOvfŒ7sùCÛ-©u›ñ%`6 UÄ$V«õu÷'©õ;{Ï~gAÆbßó3ßÏX2LÙ0мQÈê‚}bkö|ÜlÚ0Ÿÿm¦ð ¾Æ>þáßz¦ÿ š†Ã؃Ÿƒéн˜°(2ð5ùÈC6Y±ç—ç³€MæìÉõë7Wì|ÙæeýßÌÊ&ÿg/'Ü0­G:~œ¸ 8fÕf¨‡Hc/IYz^ÈB/Ë`zñp’¸`~ä|™˜o¿Ž¶ÂW>ß$Ó = ¦ø^¢-8Ι]åQ²(IÜ— \ú’@sÞëÑü¶j‹†Ukv]®6˼-fì*¯gÛ¼.:¨ÄF_ÁWôUù.4&‹‚},¦mU³«‹g—l\­ÛºZ.‹š5›»»ªn6¯65›Õå×¢9û \äí¢œ.Ø*¿g7ËÙ§r=]M ¿öùɬf³²ùòù©ø5±Ëª~:xð䚸“f0‰‰ï&ã/73pÙÇW/Þ½›x_½æ&°r•ßglüëó7¬lØ´ZÏËÛM ’ÍfÐ.òþ/\^\]ø(ÂgÊþ|ñåkЇßNc{¦®íz¼s†cÃŠÈøþJãê5,%à&ošÍ Äç´…‰_a=¸à7O4…aìN!oÛÆÚÊðbÎþDôòEøF«ÐT†w7M¹¾¾ï[3œìºjOcvbV´P#á]̼‡Fs™×ÉDÃT&^, ¥U.C×PŽ™¨Gùƒ3QfÈD}šU&â!Òë:¿ƒ˜g×ì5D:DÄøe¹,0ÀWù‰V+‹†îj!Ôe¢:ζi r“g7÷-Š˜ÇœÛ i²€ÉoÊìѰ0IO¸,úd­€ü>gË‹¼ÍÏx†ùš×eµŸ€·k6/‹å¬ß®Š6Ÿ8¦2c7÷˜¼(9cŽÅÊÀ5à2UwE·Q§±|±¿¹‡ü¾bõf½ÆÀ…„ƒãÊüSÕ£þ"5bäð\ûþeÄ‘cZ.«mó/=ud]©ï CM¼ÒÀƒ8:!ór SৈZ›yýçíX'NÃЋµQqìÄ‘ ýì–˨Z%8Œæƒ“ßÎÇ¿8f¥CÏ7ìÆt­ë­ÁÃyj×®(jŸ]×/Ç“w¿Ù†…ÁÈKµ¿ÒÌKãŸeW–½v}xûáúå…cW–ÙèRŽœÖ¶mðÜ¾ë³ H€‚úiæ.&˜f¸ì!lw`X¨‹‚/Î'çöÌ£$òBcC“Âá~DÎüä(bØôôÁe|õËõ‡7¶qqy‘—(ñ’àg.Ë0àËÒ‰±—c'ðãddY6„€‹F£ŸdA–î´ìÏÿýÚôQ{ÔÚ£Br cØØp#«ösø v¹qàævŽ)x¯”Øÿ‰D-uñ4í¨ Â‡Ñ£Á~[£ æ,à¹UJ‰Ìê*JS±5ÍN†–"6"JM":š0//Ó&aðw5‰ô%¥DòrA"±|s T=bPÍHe%%<­pÄ V¥_—Œ`ÒEñëèŠÁ›°W×Ê`†™Ÿ¸†Éˆ“bo°Õä@†ÅIv”2$R™ Dz$J¡†jÃÄ@®a‡¤öÐû¾æAƒ‘ÄÉŠøä@&r0*b䄺@Ì“À 9ŸÝrB_(ÑaÈÉ5µõÉ…ß-&—A®©!'WÕVG˵[Nè“>6ÄäZØêö³1TuRÞƒgA0òÝRصB¤ u0gR±ÎÑO|GÍuË7§b`’Ë©]“ޤaò¸¬ú›8 6 ÒJçg¶›Žd`ö æà˜“Å®›83MbÁO0ãÔÁ„aaq{ʇ]ÆjÚ5x=b]zÄ×ÏòîÕy*Zv€u€Åü[DÁŽ#ë8bDgѺηÇ81»Ü Œm=-ê±ËDƒá8²@ J¢‰ÎíÍ2"Jt"DXÅIÎ ;B„³AvHFhÐtGé’t‡äLÂ2"AõÈ4L­LRÒf’¥Í¨ZûµIÚCÊL⣕éÒFÊdis|&ƒ];‚]ɉ¹ê¸_ÑeM€”D€H†³(òQ tHV(IÉíV(i :DH)Ôåט1’oå…: 9âòÅBg0Û#á@b#u’ íT·KNéÃ$Mú:`‡ ‚äU¦i’WÙ¦r4¦”sl“EÆÜæ ’¤Ñ Âm2xÔÄzi–¯ÖAî” qî¡äÄlE0îW¨Ób4×þ@eU »?fÍqeÌÚó•±mÎWÊ©y©nÒÀÝØó•ŒyÏÀRŸÈû&…â¼€ÞŸÌqÁƒ˜ì ‹Ta+Åœù–æõPúQGˆ?ñ ˆÓú(î0Šëî!k’âžZQû(æ§›]rx’ãÌq‚»ÞÁM)žùÃ)ÿr›ßÃÿ——Æ)9º°ÿÔ¡Ò»#’‚ŽÂSÃhWÂß#'Ð(N^b’Ä3©‘[?‚$ñ mAÉ)ÌhA¡/Ž'…ÒñZž“ Ä'ˆ$?î¾#¥_âÔÝÅSyb†\o6¤q 9'‹}‡ƒÍë]Éãxuÿ p~Ùô°ÿL_à~À°îÖGæòýIO¿°õfu—[ò¶]_¼ÝËj} WÖÕIì`` %!¹.à;@!}ýu‰O ]À užH x Ç!–í!j†Ü^rÿ,jêUê¹"'S ••λ×kq,lobû³¸ûV¡‘Ù VÅ@CG°’Ü!°’œÎ¶úÏ»äGÜV’êÉÎX¡Éá(°r{ÁjÈý£Àš¤¤Ûw1HNþÁ𤩛u¡Å€w XLÞàoÅZ]ã¿)§uuW-!'_¼»†¾ƒ7ðzÚJߦW¬vÔ„4èt 5ÐoVðîe•ÏX>›ÕE7ùÐnUÓ º†–Åú¶]ÀÅ>Ö êÝ ¦ Þ"q²ö ^¸RÞ¼cµkPÓvŽUœÅ;û¨x'¹CñNr:Êõ'+ÞIp¼“Ôxà¾7‹­â<;I­Âi É`çB"qÈrh ÉH74=·Âõ±sŽWì¬{þ‘ƹ‘'м¿‰wf¡Sš†Ž©O;+§¨O¸8ÛUúLŸGPê¤OLãäMã””UJ9ÖIÚ¬†ÕžƒZ¼ÅTò1¨˜©ô¯Òeù·W¶Nªs¬{ÿ¯ endstream endobj 193 0 obj 3325 endobj 191 0 obj << /Type /Page /Parent 179 0 R /Resources 194 0 R /Contents 192 0 R /MediaBox [0 0 612 792] >> endobj 194 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 196 0 obj << /Length 197 0 R /Filter /FlateDecode >> stream xµXÛnÛF}×WÌc84/Eå¡€mEˆÐºIjµŠE®¬m(®²KÙÑ_ö“zfI‰¥¶ÌØ/Ú™9sæú>Ñ7ò"껎¹nDƒaHà pF>iAŸ)§ËãQbȵÿ&Á\Çï»ügo½©/qÒpØwðÄë%+ºžQ”ïàÓÇûÛ÷i¶¢Ë‰ç¸äÑlA¯î¦·ïé*+b©ÿŒÜ×4û›ÞͬbõÁ,éérú§õ¤°°öAHß  Pˆ|'Š`^8Âß O´—J{ð´ŸÖÞhXšë;n¯4×ójùOƒòÄÑCŸ‚hسHâè IšÞÞ]M)‚+éN®6Y\H•?W\å¹¾WZ‚O¶¤ÚŽ+¥M¦Kó•nT^h•eB“Ù¬×JôŠÓT¤4ßÒ;KAµL„óº÷Y§Ðez—“ÏsÎÅ·qxàù|ø¾Ï…òXmíÔÉ„~âñ =DYËWC¯ßöÕl)*vœòW¢r#Ma8>✦“[úòjšB/âDÐmlð•®U¬Ó/¯ñFJ1M¦×xi’©õz[`ÿƒ3½Û¶"DÀ°wëT1¯4x\ÊdIr¯`ëRæ`¡ˆ¿'jµÞ@e‡ˆ Çå\æ6ØBÖœm`3퉆-âBàn7ªG:+›eò^äôÙGŠÌ“l“ÊüžÆ·WTè87 ¡/¨´p-ôJÂ-M›ØFÜ_(½"…šŠØ|5”n4Ã0td¢…¿‚` £qºaê( N1•9jö Œp²ÂŒ6k@ÈûeQº<ÕòAégµ‚W hÁ úMÄ)Ýd*ùj½=Ž‹˜®æ}ֲ݀ä¹ÞKïDR(øÐ*°»øCh¹ØÒîr÷ÅÍtœ|­¢±ÈD\i5-_DèéýóZ8ºcýGT}—ÇóŒƒ!… ¦3Ȉ¦_Ô½LâÌ>[/·Æ^€µŒqšjaŒ0´Š·4ç€\€Êà;|³M2™ £H7yçɶ#ð=—ËëAŠH–"A4ÄPºŠ o ¸YÛlëÐç¥Èa ­•&‰Øî C/s(ÛxÄ“Gð1uaã/cÓOêÊ ô -+´(´”%Û=— ¹ÉŸt§K¹ç‡GMØï†“発ª×ý‚dÁ8­•1’¹ÔŠÛñ‡;BõEg•Û_WQ«ô¹%¾U¼ ¨Š‚ç¯á·.X1b6°¬ì;nÐý'z9/‚Qä¸~C°– Z¦hmPØ_.(ŽF^CTm (!6÷³÷Oú»‹°f,‡aÅ»ZüYXVv#kì°„„²?Är!L  #' !¸²3mm  ´,lÇQ™¯ºãG…c††PpªŽ¹cZÈûMÙ°ÑBrÚ^¢çØÝGÚ´£7J•µ¹æWYÈΡ}b^pÐUƒçÍp Wñ çE+h§aÙ±[Ías2 ™#ù?ÔiÛ] U!èi­¹7Ý‚g\؆Å,Õ&Ã(ŠÎÀþí) ^2 Ž:AVy‘X=ÚÖ\˜DË5—. ñ°rU¾åÀà ²æÞÈ-°÷À‚ÒÒ¼íFY _ÎutÈUªžbŽão§òñeðÕ®±›ø/^”Áå{(±­êµ€ÿòÓy-pêôs+@CqÞ œ<ú)°½<‘’}*Ø@¬ÓX·)™%œJÉÝ•7Ïcÿ—†äã7ûÊÖªj7ûQ¼ƒÀ²(bsúƒQù§P쬰U Z;Z V5íÔˆÀÑÕ{ÆÞö8qø _V«+°Z˜vLBHh×)l ¬QÎõôWJ± ?3Y4Š[ cÓžjKû†jyt«PÎP®P—ÝïcÜM0÷ƒjlúaÁ m㢈±—Ýr4üëÊÁ¨Ð–¥M-JV2Q?íöÝŠKv%Ô!9™ óm1A%«i×s¥x7И“þ·eÂ<´]‹zé=kõß •’AT)ÕŠ¬ÿÎàî‰PdËÃAåຆsÛG2ËÈ1öûÜߔɠâñnqiß9غ`Óª ZhµâŽÒå(¦‡8“hÈø‰u \Å÷ܽvlÁp×äÔ”tic•òé_øh endstream endobj 197 0 obj 1724 endobj 195 0 obj << /Type /Page /Parent 179 0 R /Resources 198 0 R /Contents 196 0 R /MediaBox [0 0 612 792] >> endobj 198 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 200 0 obj << /Length 201 0 R /Filter /FlateDecode >> stream x­XÛrÚH}ç+úq“yFwü°µ¶±ËyÀNR©Ú7YÂl„†Ì &ä+·jh{FÂ’…jË"‚²U èËéÓ§{øŸà;Ð|☂(„Èóœ© ’Á(àâFQHûT)~8®OÌþ5©_¢¥(ò¼CGé®—àyågðêâç=â»°ÜÀÅuPX®à·Å‡ù=\å:áò¯˜¼ƒåßp»´Õ†§·ûñ»ù!ˆfX»}'!pƒCb׉cLϦøFÁêùB™o–ùâÕæGeº®CFeºÔ­ý¿ ÊÓ‘ ^,’hºB„ÔkXèD½ü§(`Á7»<Ñ\}ÝVôi™^MF~´ Øáu>[L®f0ãê܈BK‘çL‚Úm·"ìI–± žé>‘̸O¤fcX¯Íõû_³ÔIÅÆy7êG‹ ³«i¨ƒW|HL𣋻3ê:´/2§Æ=êã'À²kÃn[~|fò™³ýFØ–­¢FÔou¹fM:UÍLAÓº ë™Ø=ål¢8t||•±Bq}€ö7* ðâ+¶ÛdùñÙ•mg]ÁÊÖº8nð/ÌYÆ“1ì96†€–IúM!)2 cÙD±T ©`‹ µ÷ƒê¨‡U„hŇUócLUóV&qýlÇÀ5 ¾[¡G|A »n>^ÌÁu\›B£õf À~GÛà :¥aQUj‰ >*uÐŽ÷d¼ó|x\Þ^‚á`E¼†ˆˆ"?•DÁ¬$߬A7ä¤F;Í7ÌÚ{? &”ºq”«,ãF^“2öÌS¦€i¾ËØeßæìV\j”«¥ôˆÊÕì×Í{Áч4­}Ôýÿt@KÑÇØ_!0O¶[”sä(ùq‹›Ä/×wwçêv«“¨çáð|ÝÞHÏV€PVüëNÚ1+nše8¾%±KFWQ˜¡l5zçeX”f›R,’¢âÞ@Ôñ¢i;…˜ó9FÝÈÆP{!c+^ Ùž.g+L¿HqA¶RñzéÏ×6¬Ú ”…ïM{¥³j-v9Nr†a˜¨$ò@àx²!¯pð‹½½Œ©Tò­ià+s³*_a‡D*Ù6)ÒƒÿƒÂå@Y¤¢Sô²;j-öí¨SÝ\ÆÐ|9¯ëfê ›IÝ‹Rß8*áhm;4ô½¶pàºÓ{ÿ<µnÖ.ëðëã&Þmº'‚çí‹M݉ ­Æà…(Ød¿÷Îó”(Þ4v¢ˆJ®ÔTTLÃ&SI¬HÎD´‘¥8?ªLðàUg21öÍfÑ€ödÜÐqH{BݨÝ äñlÙNLe§x´·ÛI ÂY•=]~Ž-òâ¡®¬Ööȹ?4ÄìWøg6»W?LØi¼çy OÔf¥d¸}6Ûe¹äà±bÍ‘`Õx¶Aýù8·#Ù|¸j¦Ö¨±}ú,Bã endstream endobj 201 0 obj 1194 endobj 199 0 obj << /Type /Page /Parent 179 0 R /Resources 202 0 R /Contents 200 0 R /MediaBox [0 0 612 792] >> endobj 202 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F5.0 47 0 R /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 204 0 obj << /Length 205 0 R /Filter /FlateDecode >> stream xµYÛRÛH}÷WôcRŠî—<-RPYr3›­Úâe,±veÉÑÈ8ìWî'íéÙ’-’ÅŽ ¶4};Ý}ºý•>ÑWrbòmËŽm;¦ )ò<+q©’ô;ôê\9”*²õ·Jñ€m¹¾Í_ú¥Óö_œE¾…wœQº 7·äyæ\]ÜïÙ¾K· zõÖ±lrèvF/Æ×7Wt–×"«þˆí—tû']ÞjÅÚƒYÒóåøO ò#*g°°úVRàZ”ƒb׊c˜çG ¾‚\0{¦½dì Cc/®ÚÞ82準=2æ:^+ÿy®|âèÈ%/ŽFÚ“8ºñ$—‹åjY•Ÿ:!³Å*uV‡Jl‚ç;Æ\Ù? öc·x^uU湬H­–˲ªi-‰éTNiòHWåZTSº±èJTµ<¡—£Mœ9¶=X7:º9žYŽ›°‚Æ%-¸æs÷‹þ]ËÔJËEëŒã Ý•yŽ <å‹nç’b;IíbôI;V[íy1ƒ. m+rm‡ßÒè #'I";âLœéìç»é鎷Â0}”5"8NçeUX£ ìA8 Aø>Å•QMÆwSÀrÚ({¸ç¸|x/ 8ÙÕjïŸüáAV™\!`„RØÁ û% {•³Í(Â:84CiÌ‘L¢H‘O×———§aÒd¥h"^Rª– ªç¢¦¬Hóç éJätþñ7J‘}mÊäò=½c;„FÝ ž"ùEVdÅ=]µÌõ‰bÊÄ„‚”J¥ÊJY„¤ÈTûŠÖKWŠT,Å$—\¡Õ:«Ó9Ÿ6‘õZÊ‚-@-©2 àš¼c@µ*êlªÄ:׬¡Èór ÖÉJ!'ê’p4yusÛ„Ú¶–yÎWóRÛ® }?j®T5- µ—-jžFÿÄæ2¼«¿Ì¥†*í—L¨Ç‚î^¬ç%ÍP~]elÙB wi °Íw/Ù>¥×ac´ÌÊŠ­ÕeÝO€´¼Ÿ\v´/g³¼SEª\h°ÓrQVT.e¥[4,7JÆ €#Pý›ÕˆØ$˳–•ÍC(šIQ™¨â49Œÿ°›}àýLgè´Ò7Ê‹Iù )ôßmJ顬f/õÍ`=M§·xäùT>dH´Mþ¿>²~÷„î¢Ñp˜‹ëñ;çìˆ:È”°s¾Ø–†9Nlï·nú§E¬HWÙýœ>Ê ø\p)¤·y¹\>ÒE¦þÂÍ b hwƒÕ–b±"èîA“ÿe¸ËœiÚ7ò8¦Üq?·!ÇsV·käÁ,µO8@ª#·2Zb†è6•Š]ê=i¢®CãÛÓÀé¢Ê¤*²^›n_þz~uöþ¸ØŽ:^ÅÙ–ƒ ®ÕRv†.¨Ç¨ÚiR{>E¶yqsö? ØšrÕÚzsñùúËåնƉe‡~@Ž‘ò][ÄWWôùìÆ ùîÅjÉÅÚ¿ycݽ(cßïÅu|,×ìÄÔ"ËO0+:ð£|6nf }X~CC§G_Á?Ü–ƒjw&ƒá|&\¯w:,ˆ%fPµYv¿2=•fY®[?Hãæ IzG`š©ϵu˜Ié ŸÍéATY þ)êZ¤s9»Dâ4™Ù– ¦—f¶Ô$`‡†Á-ÿ‚™àK4mhL„鯂XŒ±C»7ÚÎÝiº\¸tl`°Œqš‘¦uÀ–¸é™ !“äZ‹ì×hÝbtÝáwMŠÃC Dÿ|cÒæü›Ô7zÎî§¿ Ðæ$›=…6gÄ;&¡¯‡ÚÓ í€ýðÝ}Bh‚¦ß5sjžià?9_»@Œ‰êûvÇt86[;Ó;Ø|º±µít¤»GcÂ~òèÈ^G‘ŸŒë¢°·´Z¥„Ó…05šY3O0fÎb˺§yê0ÉÅRÕT¤6½0>/Q§çY:g½X%0\ñJ® ÷—çrS“ç¶¼êñ\RÆÍI÷+,x¾âçoèaQн,ÐÔÒ¡,z•ñ[«ÔDûÒG4ºœª¥L³Y–ÒºK4Y¦[åµÒÛ´4Ú·O̤¨W˜Õ"”®ïöbÀzÚŠvH«SëßÍü}½÷RÏ2w/®o.0šsƒf¼@‡æ¡\¤¼IáÕȦ;O›0 `Ý#S WðÓÓF¿trmv¿W:ÙO ¸:޹B0Í&ËxËIYÖªFÈéó¬& Ê)ØA!ñ.¯°}Ñ.ã¥ÆÉ@Þ ’Þò‚7\fâ7ؼ™8ÜYý¾©âS„¶.éKnËâ‘Þ#­Kø§àEÚCÆ«>%ÑVÀ/eY"Õٻݘ4ÎíäRëç“ÆÇ )ft¼ƒÈY‘ÓK eæÄo>&€ïÚUšüVËBo.§z‘¢ÞPi†?HSQ Bô 5“Õ±éøÍŽÚ×;j7Þ¤oÛåÏr-çÈ^7ê÷FÆ£V@–ó-»¥¢B‘ÆÊJu²íg[Ñ-€ö[ÆÉXÁ58ZnUÙäúƤ%×€œÞ"nG˜²êìOÿ—bùÌ endstream endobj 205 0 obj 1952 endobj 203 0 obj << /Type /Page /Parent 179 0 R /Resources 206 0 R /Contents 204 0 R /MediaBox [0 0 612 792] >> endobj 206 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F7.0 153 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 208 0 obj << /Length 209 0 R /Filter /FlateDecode >> stream xÕYÛrâF}ç+ú1[ec]¸æÍ6lâ$Ž7Àf«R[•¤(+$¢‘–õåòI9=3 .J•YäªÄ~„èéËéîÓ­?éú“Üuœ¶3pœuû=êû~{èQ.é¥ts¯\ 9ú_øÓö:ÿéK×ÕGHê÷;m|ã¶‚ÝÍÈ÷Í=xõp¿ït<š­èæ­ÛvÈ¥YDßL¿§Û¤qþÛÀyC³?h<ÓŠU‚ù¤—ŸÓ©?¨3 ,‚…Õ½N»Û£®×ö¡¼0ðÚƒÌëô‡øë¶à‚è…ö’±·ã{ñÊöz=gn˘;yz¤¿iœŠy"iže…*r±&¾, b¯^;îÛ·Ëv¥æY?ÒÀ÷{; ¬Ã~2:Œbuª„>¶uFê ï8ÃݱÖðÑøîýw0]ÓC9/Ûƒ8]\Q©dT&e9éo¸J*‹ŠÈe›è)•„ïVbb~šÈÏ2Q´Ï4—¤d"ƒB†oZçáç_Ôïö¡~k¦ŽHŸi–)ñ JM¡A.«5…¬^P›Öyö9eømZõ»Ôõ£Uo—<‹’{‡>Kn¥%].Ùï:u’Ç“ÉÓ„s Ï9¢R)±êŠŠ¥T’Øo@†¢n^"°ñªLDg)Çß^Ü è‚Ý»ÈÅŠò2M.¸¿¸Ü×ZïþàVh!‚ZK¥ ²´È³$‘9`:[Æ€¤1É pŠÍŸ“H”IѾ\A`èÁ¹¢ƒ’_ 5’§ãñÀÂTÊO[¿ ¹DÈeHEFK)BZg*f ÊU]l½¬.Ö îë{¶2k÷µ¸5”KüJî«‘|ÿ8‚÷PÉ?Q…C¼]­DªÊW/ìb¶kï÷öÕÐö  V#ug'¹¢g×SÉœ¿5’'£Ñíìîš0¤F¢q@•-v-æC/¡õ ôl׃£l×kTüJ ª‘üabõ! Yï©s Ú¤†[Ø8¤v’‡Täéìvö~Êõ«E©P·±*dŽ7"dX]ÎF®ãv_©Ö³èWVè_Ç“»§é_"ð§9—öçCâ×DÝûv°ù#ZóóÓ–‡eJEèÎÇÔò £Š^EÖÆó;Ç”b‚3z˜þèÞ’*×ë,/> endobj 210 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F2.0 9 0 R /F6.0 64 0 R /F6.1 65 0 R /F1.0 8 0 R >> >> endobj 213 0 obj << /Length 214 0 R /Filter /FlateDecode >> stream xµYÛrÚH}ç+úq·*&ºrÙ7 díÚµIªò6ÀÚèâh$_ööö“öôèŠÑV ÈN•‘ÑÓÓsºÏéöúD?Ècta Èí÷¨oÛÝ¡E±¤¯Òû±2i¥ÈÐÿÔ _0º–cð~tQÝÂR¿ïtñ?fgÐå‚l;{ŸÞ· Ç¢E@ï?˜]ƒLZlè—ùõÍüDxñ·ñ+-þ¢éB;Væ•^¿ŽÓ¼3¤hƒVKôœ®Û#×êÚpQXÝÁÛsúCü¸„`óÊýR¶_ÇÌö‹O«ï’Õ³³íöºF'Ûî?Õò¯‹dƒå!¢Ù³;/Iç[¶]£ÉòøfBÿÒÄSßi…Iù¾Œq"\«î±ëæØx+Ûʡqt¬,#V¥å tGÇêÐ2ǪÁòl2-F×LŠ5MD"(J‰­nó@u8ë^¨cXª]PÁòªÁò×Y¨¯±—ÈæH[^@ ]Fª]H•–[‡Tƒåùb´ø<¤æ‰HR…ʼõT‚ Œ1†U縚٠+×u¬Îû¨U§«Á2ò–ßV –¿Lg—wó)¢5}JbA2^FÊKžiÅ´–ËtË©ØFÑ¡õ¬A†° ·w“éåçßá@’Æ!(gCQ( ‹E{—I¾|>*èi'è:Ûà“Ùµo÷³´Ê4í®ÝµŽ®vÙ9vjöÝÒ>È»²?¹žÿaѸ"\n¼m‹Ä‹BÔÀ ©êÄ Öó L¦S@´Šöb‡Ò¡¨1ÓN(Rˆl,|&¬šCEê(Jv"¡•i)I>‰À åš@g8 {Æ î–ÏxK¶äzô¿ïºò‚ÔIw‰xk¹©Ÿ(€Iá6Ü`Ü R œñ'}sI;¾Zƒ³ï*YýõßÚqzh;/¾]ÌFã?¸ݦÁ5’ i¶ú®€råۂ㥓¥D8‡ùpþ¦aäÈ©ïöj:šìû³CY|•;çЋv§Ÿ3q…ÇÛùt¼¸›í;¤ä Çü—N+/2Ä4]¦¼½aæ×߸$ε#8¢¿Q~~\ŸÎ‰B…ãc ëÕ±Ãj¿ÊW•ÞßG1ྉRæØ{êùb)}`Gç´@ÅQºÝe·6À~½A.è—ÉÔ“D¬vx=‰ð8ŒÂ ùÄÔ&í$€i; ¸½pzØ¢/ð|ùN㽺§GÏ÷¹ž¬@Ì( pwD¡|ôŸ‹':e)û†ÚE©"[Q`'ØÆ£—ìÚ«9¦=<Èßqܧã(‹hw|wSèøSÑEÇÄí>™„LW=– 9'6 5ë¶i5Z?šÝr-_7m5;NóéŸã«Ñ-a»GØæ@''3w" ¥_ã¾– ×Óò~rœ4ÿïÁäfÄtQ´eðòÁ[KEüâà!Â[äÅiH¹v9zÞe X²”æ$V+¸jÊÕ³JC-»!@ò=eh`Eç‘m¹”?µ¯«‹:„Y”ÕöÓoˆÓLI$â÷"¡Ú ¸ÉQq4;NäôÄÕ²ŒΈUƒi¹–¦«rw*åÔNXc°Áô«­4pà q8i’QÌï;'8Á†– Øãí¼Ñ[D¨Ë· ¸ÈWÈ2±^C0Ë'ˆ}¾Àm $qq¨ëôœ¸…Œ ¢uêËSÓ²&YˆXnAÕ<J×=VéæC³šuVºl=ËÌÊúÑhk0 ¥ÛhšŽM_<¡žCš/.\´Ú¥<¬«Üâ€_9ÃnÈ%»až¦U.rÇn\W·{|ì"\áÀqÊK¡pUŽjõ§&‡ÜPä{áw¹¾ðÑžOúÜ{6·µ{²z{{÷ºËæ@YôUèRqÙÕ»5ÿE`)“GÉØÜE*¡ñÇÏÙkxÀcÚVNFk¥jµ×}'÷ºÂ£õÙÜ<´²ötyªª Ù|æˆÁXLÞXTãhoÄ`ׂX4BÕ\ð<Ùüé?™– endstream endobj 214 0 obj 1758 endobj 211 0 obj << /Type /Page /Parent 212 0 R /Resources 215 0 R /Contents 213 0 R /MediaBox [0 0 612 792] >> endobj 215 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F2.0 9 0 R /F6.0 64 0 R /F6.1 65 0 R /F1.0 8 0 R >> >> endobj 217 0 obj << /Length 218 0 R /Filter /FlateDecode >> stream xµXÛrâF}ç+ú1[å•uDÞ0¬Ë±Ùm\µoŒ@±.Þ™‘/ùŸüO>)g$¨*€„Ôsºçt÷iý¤oô“,\Ó0=Óô¨ÝíP×qŒžM‚Ó#%t=Í%™ùWÎñ€iØ®©?ù¥ÏÕ),u»®¬Ö<¦Ÿ§¸¿6îwL×&?¦ëß,Ã$‹ü€~™ÞÞ¡~¤X(~xæ'òÿ¢‘Ÿ« ë•_Çm^¨mRÀÃj‰Žk´;Ô¶ àÏ6<î¹Ý>íBè/þºVá/~µ¿vÇÙ¸Û*ÜŽn¾ÿNÿOØ,â´à³lIJ°y˜,¯(“<È" RQü³ÄU’i ^™àÑ8á„ÿâЊG#þÂ#I1{§'É#>W|ñ©u\›á;0Ç­ún1E,y§eøÂRa PS ,Úv…J\€MÏ"} |ñëPuÛäxnª³áÐ?ÕžFÏ{޶ÜÚa'}ܲâ5XM&ã ˆ0Bï(—’-¹¼"µâ’“Ž˜!)D˜WØØ0Î"¦Â4Ñû_^|Ŧ3Þ¥`1‰,I4]p žøx¬5n×ÑùºÅ€ P°ù¨¹ÌÎÓD‰4ЏMýUJ.L \°Ù;h°,RÆÇÚ CÛl_ˆ °|!24XžŽFwà”ó§uÜ@Á±å|A*¥g zNe¨)€]66¼lÌx¾®‡ð¡ç¹ÔÒõøL¹Ë— ß¾åÁýцò‰*â0ŽY²U¬,æeóªsĪƒ|¨b•÷®£cÕ`ugc¹êŠG×}Ë:,O†Ã¾ßG¸&šRC¦XÉ8°ª$UKË‚CºoCE ºvÙõ@ª²ë¨Ë,_ˆT –'e E¨xs¤ŽÕ);”êö:¢ÔÆòÙ)Õ`yê÷ýïS]¿S™DÝZ†Rq¶Ð´ú¸­¼NïB¥–/D«ËŽ&7ãéѽA÷ÄÓL×õ÷mÕwŽ¢ÚÛÜãµU™€œtæ]Y¹Õ¼QAOÛÁ¶[Èaüj9l™íRNØéf9FÛ°.wE}hÕhW  ŸU o§w 6òDá2…šš”\•'ºXÏhÀ²uGÝRL>ô[¢ÖœVLB­Ê—kˆÖÙ£uäöœA6qâo,ˆt4h¨\ à ‚ê,b/ß Y¶ƒ}£örU§‡Ž\½É\ ®q‡žXItL,°Û÷7´ÒG 4åâY¨ÚÊ5èÛsŒ:â¶»ñÒÜéŠôÅ3" ~zÆzÊEhMOùåF'|¤ýi@Ž­Ç¦bÈ-ûß×Q¸G+ºƒà|¤Çäpzz^Úš¹¦£?žl’˜Sñ?N«»Iâvº»DÓˆ¦·?t]œæ@0òüª´5þ4n—ŽOëDµ¢ãÓ±ª!¬\W+³ççT( ‚g˜Éæ^óˆÍ0hC=éÚb"Eš-WÅ©¶ßH†üf=÷0¥Ø|…Û¡ã%iò™¿éþ˜¨3ÕœNgoNcL“„Ï'JªÎé5Œ"]QæèΘ.·O Þ×Wòœ-Ÿ«4‹Pf`+1<¯¡Z±êtͲ¯WReÆÏÙW‘5ãûµ˜?µ-ÖE^ê<¼«Ê“¢jy'rŽîD¥ð®-w¢†ö:ÑaB1a¢“Zy·ŽxïÕ 5—½Z®—¥§âòN÷™qý ‡ƒˆÕ–¢9"òx£P‹ü"dË$•*œS¦ÂJå ¯& §™¸Á“3¹P+[ͳpðõú¾F€oÿ»ñ endstream endobj 218 0 obj 1354 endobj 216 0 obj << /Type /Page /Parent 212 0 R /Resources 219 0 R /Contents 217 0 R /MediaBox [0 0 612 792] >> endobj 219 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F2.0 9 0 R /F6.0 64 0 R /F6.1 65 0 R /F1.0 8 0 R >> >> endobj 221 0 obj << /Length 222 0 R /Filter /FlateDecode >> stream xµYÛrÚH}ç+úÑ©²eIHBäimÀj‹‰«¶ò"Kh£ ‘„ïÿìÿì'íii„(µ+v•±1ôôœ9sútóîé;i6ª¢Úªj“Ù³¨×í*}AÑõ ÕÈMIÍ¿SoPÝPù+êªú‘z=CÁ´ŽÒ킺Ýâ5xÔñú®jè´éúNSTÒh±¤‹ùxòn‚Ìñ“?mõ-þ¢Ñ"O¬ Ì+½~£y!S£x‰VKX†bZdêJÉ[WlÛ3z}|™@°|å~©Ø¯eûÅ#ï×°Ôb»º¢vŠíjFµþë lÝÓ9t'G¡%’4HâP„nLƵf][ÆÝp@NäÑ`0¿Ò ]'~bî‡ÛÀÉü8ª%Òy Ö2C+öˆGÞ£Ùµ÷´sÑ”Ç׋Ál:A_ßÑ]o6/4ˆ£,‰ƒ@$”n7›8ÉèÙIÉñ<áÑã }ˆŸÄ£‰Bœ$—ï:%7˜½šŒ?IÜ6 +.®×¼ÐoùÏL¸Š‡HçÝ€ZfßPlËÔÉ´Í}ÈèBÙ_'ßHdu5¾‹üFð²¼»Z¯àå‰éH LÄÁ÷ùðzºÄ b‘f(Z•Ì)íÔ‚w5ƒïØ_RôÔÈRGê‘õ<íÃÈÓ'‘<ùâùÌjÇĸ@ Oh±ÌóÓoäî¨ þB6Z:¡¼°â, ¶½n'UÀô%ÍD˜*ÇOAžÓ7øNvê2ËéWY’ŸÒ£“âžÅeø×ƒÀúIDCågN@C­×ÿA©H|‘ÖvQ á®ýMªqd?Ü"QVËi}ëp@““Žçh7´M‘šC+!K·L¹žaŒ£xö³5^UjÒUº®¿ÄëŸg³ödq•>‡oçú†q˜ýq KádÛDàØÏ+{Ç®©Z¯‰¶ò0‘Iw•€¶™øN†w:+1dZ½O†ì¥ ÆIèdü@ŽëŠ4õ£ÉÂk5MµX3ö˜[»?­¥-º`—'Tn E’œ A"7ØzŸÝ«fÃéœtŠÎU6ÿ««@øã8#T»¶®³¦k0û¨ÜxžÏEwÕO>ΣLò}KdÒmf0¼ZUd û†Úl¸"Ún•ª Ó¿ÕAÔœŒt 91;½=W1MCB#/„šañÕñªÝr%T4:u·E¥íÔ0QMäMpÖÐ''qB)NwËvÞ"¢šYsÜÒ‚æL/ÌX½$H–BôQKÁ«Í.z^ûîš\'¢Ç¼Œ,ýt+wi¬Râä}K2`iG2€„AŒQälÎ”Šº{ëš d}z4›MgÌÿ$áSEõEN/¹^§°›€ìàWðZeÇ«å“Ï8x‡^Ab(ÙFS¦ðo¿¶yâ}ýˆ;i¦AÖÐÊ‹ävTî©`ûSd†&ÐKgdçÊ|Ý!ðö<€€Ì·N}xj-¢)ô|4ú|˜ ñ­„tHZ{è2<íZ8mâ4w 8éR©èþü‚’#ˆ¹RA©T-])]íuä­!xz0ÀÜNTlDA C¸¹Vœ&å孄ý „“MpËP *t¥¹'Ãuš/rSèÙpx³¸b3&ÖÐÉÉ;p‹%µsÂÔ®a ÃXéº,‚íS ¡Õålý0“X=$~†~õ¬3FœõÓÏÁêK{Þ>±ô2tûÄj=_Ü,>ÏYË0=ئа•ÏÃüâxL®·O¯nið߀Woù""ô¯"WCè/£Ùít>`£°ƒ³þÈ2ÿ²o+½¥Ï9–q30N/õ^NñNÖû†ÐŒÛ.tûõ’|E(ïigðMqt/——èn¼Ò/£ánˉ˜Z÷W9„þU|l=žÝÕq„+›l7YÑmp;rªÑn` gÝ´yX´F’€§es„ñY„ñä‡uØïìʳG1µ6žú՗éÁœ@?¹ô6Ï äÿ7'À$ïÁóy+Jr!™i ZÉ›ìõŽæM?¬ñM9/€‹ªåTÊ8ÏÑ ÊÙø¹x‡Ê3Oäþt7Gh){[ûy"çÕ²«(†õeÞÈ#nRš!;þèIW'·„Oƒ*Æ¡Å{ÑnU[CãÕF«Ê˜Ûøœç`Æ7š?àn™W¥Çèû¥icƒþ¨øˆÏE‚5µ¿ÿåÃ%» endstream endobj 222 0 obj 1858 endobj 220 0 obj << /Type /Page /Parent 212 0 R /Resources 223 0 R /Contents 221 0 R /MediaBox [0 0 612 792] >> endobj 223 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F6.0 64 0 R /F6.1 65 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 225 0 obj << /Length 226 0 R /Filter /FlateDecode >> stream x­WÛnÛ8}÷WÌc8Šn¾í>%v³5P7il`±  Z¢m6’èŠTüe?iÏH²|i $…mÀ)R<3sæÌø}¡oäõ)t·ïº}êôºÔ gàS.éoÊèzh<Š ¹å×D8à:~èò§\ºÚOñ¦^/tðÄkE)ÝÎ(ª=}ìÜЧYJ×wžã’G³%]LÇ“t“X¡òûî%;҇Y lÿb¾éí÷„¯_ÔñI/aáþŠnètºÔñàà…¾ïôû0/ì ðé´à‚åí¥ÊÞЫìÅÈöúÝ 1·U™{{?›Îoè¶.uN£û)ùNÇoÃ#ÍBàx¾³‡ú.¯Ÿ ‚nƒ¢vúÝh8ûçá0 ï'˜Ñìe#ÿ ©´d5…€ÒmS7,ñ sÊ4Ò„}mâN|8œ^ù¡ïóºsÙz_à^wXèµuÈvp–Ã×"ÝÈœŒ´Ve«6År)ŠÄ’2°k~! «¯Z[g~Y»®Å  ‘~§¿çkÀñçãÏc†4ÎÖj¡,FüÌ/îtž ;¿$³U6Z¡sñ<ÓZ‘1#<‹Çº~ÿÔc³µlÂjŠÍFçÖ ENq®¾KÓ¦D,d"ãÝ.—ì:×Åj½[¢ñ’Du€Ý+¬ÑGÀq&ä½Î)òLgWòY+3ø*+$ JdøäÁœ¶*Ih!)Ê¥°2n“È€ { zìž óÀœQ‚"¦üÒXš(W«tv+z>§øc¡2Œ%Væ ~,õMı!…0èmFãɈÖRÄ€ oòÖº¥’ [“èl3ì„~Ð xI,tôÄþï27¼¢—g2¢q:6¢‚•Ù\' €Fb8¿Pî"aPF^oð  C[‰ˆ`äc°ø?¶1ÕqQOÃÜdCK©DYD aX«rš­åy¬è{?‰Ç1”ЬìÑH$É Ó“w&·eÐj¦ÁÖñí„‚êÉPy5U1³­ždf”}9ønï4´*Ù¯5¹ÍZÂÏš§%læx‚ÐØ•ו9“sdÖºHbN—\V¯DÊà4Lâóç?ð~Jæ¦l°\:Ãû mr½ÊEJEÆÜožsý›_ 1Ì/ËúÒ`#>úéæñ¯Ããg\–棼­`õ»W@ò»¥·V Fn<·Ww~Óðx¡ã9ýfm?¸ ó‹ v%}¨›ôý¤RN> endobj 227 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 229 0 obj << /Length 230 0 R /Filter /FlateDecode >> stream xµYÙrâF}ç+îcRek´"˜§Ø€3NŠqH¦*oBj2B"jÉËåòI9·µ±¥ÊòÈv•1BÜ­ÏÝŽþ¦/ô7#²uMéúˆwH®eic“2A_)¡i/IW¿ÒÇtÍ´uþQ—®Û·äº¶†OŒ¿£ÛYVy^MÜoé¶I«}¸34 Z…ôÃò~þ‰nâÜ‹²?Gú´ú‹f+eX+˜5½^}Y‘cQÂÃVÅÐÖœ!9¦fÁ8Dadj£ܳÝ1~œB¾Ò_*ýKñÊþÚC½t×ÔôAé®á´ú_Ê ¢]“ET$!ºŠ$Ý^â‹€¦Ñ&ʽ˜&i¶O3/Ò„–Å^d×Ëè™–Ñ®ˆÕÅÆ”A'Sl£ô¯ì¥cuÀ‡z3)L6±¸¾M½,€M»}‘‹Œd±‡q9=y’¼ €Ùëú”>ñMs>yY.®èÇA A¯F`¶[!Ài¹@¹5*c×¢p»eu?©¿¹ð5?Ý5ÁQÎê¯×\aÿ@³©ÇHû$T ÿÚ±å¡mŒ5dâж4Ø ±­!=uØtñØÕÝ›“² ‰Œã÷¯||C×=E’áhFkVl „[†É› ¨aÚUrÏCɦ2ûTòãÈ#ñôFG¦“‹‚qR«V[ñê$»P`ŠÚõÚ“€·Œ8 h­Ò+Ó©é!;¯ ź-Þ»„NlZ`ûà°ÎúÈ+è>§-Î0G¿r9\Ü̯ ?€¡_§†;~®l ãt¿¡ ’ßÈO“ö`ÃTeíÁ‹‡9N{–xkäøbú°$¾âå4A從ÞÝÝmë“ï:Ýœ$·a9ÕXцêóC©É#j•]ÁvªÒ6ÛbXm:»ýýg¸,J—±.6”gž2wE…aS˜fHh|²ÁU’i˜£¿ Ô¡‡D>Û¥²Ê¯Æ8ÖXÒÎ{áC”"~.‚žÌv1~——¼Ð&z åÑV-+dùB•a°›öYúaTéG®C†ƒ™@™5l¦¸ºæÓù cŽ-%útÈyka8C­>=[,Œÿ,ãSRz!¯Ù'q耮é|¥ÎGKqwuñ ïq„7¨,”IÂÁ-øÆ÷£ 4{Í šÒ@ž®#ÑxØÂƒžG´Ú¢“W>•hàÖË0°"ôŠ8ï£32 †C®‚(,ý¢OO­/@\½œÍ~–B|«C8d‹êržÒVxíS©Ú>Nº®TôåíËGЭ·D°ªT}¥D¿W/ˆžÌ§ úá`îv˜×d®WNÚ.‡kdpaÄFþ€» š+P#ºmSw.Z%òÑ‹éôfuƒˆ-XS/÷*Ü[\RX… %UÅjÄÕºŒUÏÐÎJjçX[]Æê\ô×E«¯Y”‹ ÁzsÐ$LÖq¬jó뜇积Z[#º`]½\ݬ~_r-˽¼¨a›H2‘h ®†¡è4ΟÄËÔÕ8ÿ•ŸE¿Sݺ$úÙâöa9CÀfÏ ÃúšËüËñ0ØS3 Ð"Vtï8»$zº¸ÿƒ£6WÃÏ>r/ü(Œ|n—A†9õŠ"Mhižæ˜›ÒäC†W˜¦‚z^ÆDÕÓ$bHâ÷™DXô{áñ‚èûÅDõ>AÊfÅ>oב®ƒö…j„™¦]ÑŸ½wÐVtÿl¬nEOçÜ>™¯âÿš2׬o=:Ð ¦ež­bŸêe2/2ìapŸî‡GøÁüÓm\tŽYÓ¶ªù碋b¾µý(`…”`o‰ú–E™TTEIÑ/ªÎÒ¥b:zE¾¶g¼Ân3Up‚¾Ýq%XÓ*5}RÚTÀ×"lÌÅ"ž½]”`gê0jŒoè–žpâ ’ÿ·¬@`Oªå 4+ÌÚnØÁ¬ƒG2‡uü4ÁÔç·pÿ1ŸY~[ië¾ÞÇFÏÇ=49æGÌÐ/.Òê—b‡ç/´ŽSÿáŠyŽê-À)ã4æ^R€Ô* < ‹KˆçØÊ&¹Šn-ºUc+¯Daê…iª†» ö´Ø[ƒ Ê{t}–›mùÖb9D|ÕÍŠäÎsÏßâvô)’4¹Ï<"%yOP—Œh‚Â(FSä…½}OOQ+–Í’ßæ{ÀIeͧžüÖ …—*X > endobj 231 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F6.0 64 0 R /F6.1 65 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 233 0 obj << /Length 234 0 R /Filter /FlateDecode >> stream x­TËnÛ0¼û+æ˜6£§%¹A ÔhS (P(‰²ØP¢BRuó÷]Im\9$m¨AŠÜYÎìnð?Eä1/õ¼q²D†lÀ|A‹‹Ìú(,¼ñ³]ðXy÷Ç%EJ’ˆÑV4¸Ê†Óš:zQ€¼ÁŵÏ<øÈ+œí6Û÷¸TŽKó5õΑÇ»|v ñÿñãçã_®³Ý»« d3èYêÖþc¶ÆÔÌNzÙSF+›^ÙPjÒ½7lßuÚLú¥–´Xo/çÈòlŽŽ¸PþÛ9´!E;aLß¹EiäR‡î„²·QÁ’¬râÀ#ü;ÞÞä+êAz¼-ñ™ÔÁ©± LdÓ)Ñ‚X¢êÉš£@fc½ù³Î endstream endobj 234 0 obj 610 endobj 232 0 obj << /Type /Page /Parent 212 0 R /Resources 235 0 R /Contents 233 0 R /MediaBox [0 0 612 792] >> endobj 235 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 237 0 obj << /Length 238 0 R /Filter /FlateDecode >> stream x½X]sÚV}çWìc3c˺úÆOÅ”6™ÆÁÁ$™é›,]@­ˆ$ þAý?ýI={%4ƒlR3cFöãÜݳgõ•>ÒWYº¦{ºî‘í:䚦Ö7(“ô…ºæ‚‚œtõÊü@× Kç?õÑu} K®ki¸#zÁ’î¦dšåwðnàû¦n4]ÒͯBÓIÐtF?=¾»Kƒ¸ð£ìOCÓ?i4UÕ†ÙÓù~¬ÓŽl›Ò2¬]8–f;dš‰à€‚ghž‡ô,·?»fgæKe¾ŽSæ‹wÎ×rô2]CÓ{eºÂ©ýŸå Ó®Á¦{ I˜®¤ÞçÑ==FÉ<–t—úYHÃt¹Z2çËuìQštõ_¥%ÊÔðΩ٦×>Éï¹Ï׫Uš´õsòÃP†ôôLoÓ-Çx¯Ñ[?+ä½éíNŸO\ÐÙåVat£°M[³t“-ªKn±`w?«ÿ… ´ ]Ö ¼¬Ð=÷MT®ƒf:†HkúQšž£YäX¦\õ-M•êÐý¾«»evŒ«BÄF=ãuw>5ÇuÛU#MÔQu)ÉÞqSl|_í»’ìj¹*¶Cˆ »my¼‘Ù&’Û:88/ÆÅµÍv5Oò» ¡i‘.åS&·ê¢ü9/ä’B™Gó¤¬ðAòÝ÷ÏI°Ðˆ¦ üêñnHŸÔµÞsjÖFà½CB] ÅÄý“Ýèa2¾¿"[¿«ëÉW~²ÊÖ nîÉÐ ÄvŸ‚îB Ž‘ÙSº.¨ØCàã^šð Ûböh„=Kã8Ý‚»(‘Û|ž¥ëÕm}¸ùà4gõ ppsú,Šbu{s£ÜåÚjÓ×›6mô ¢n›M&ã Ša”e|ª2Ïý¹Ìw㲺.‘$ÌOÌ!hÇPÎüu\h8cÓqì&L·3îL:Çç¤ÀVÐU(ÂêÈK)÷ñ_ØÔn endstream endobj 238 0 obj 1589 endobj 236 0 obj << /Type /Page /Parent 212 0 R /Resources 239 0 R /Contents 237 0 R /MediaBox [0 0 612 792] >> endobj 239 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F6.0 64 0 R /F6.1 65 0 R /F2.0 9 0 R /F1.0 8 0 R >> >> endobj 241 0 obj << /Length 242 0 R /Filter /FlateDecode >> stream x­UÛrÚ0|ç+ö±™I_Áy$)fZrÓÎôMØ"¨µ%"É¡üfûC=’SH(“&̃ëHÚ]íYÝã÷ˆr¤aæa˜#ô1H’à$†æø‰ã3¡4ýÏ”4! â4tÿt´ùK+ i@#Q¯lpZ IºzÇTŸ„iŒ¢Áñ‡(¡˜áÝä|üÃÚ2¡¿æáŠoØfa·Óë÷Iwo”õ¡fÄp³E? èc #ò8Ès¢—NèÉz$Áì•|ÑñM£Ž/½߸Ÿ¬éö:ºùíh°nP1Ë0ÓªsÜ\Ža4[b*$Ó+ÌDͱœs‰Éðvä*„áÖF‡`²òS[),ôÞ¦ÞnÔIDP›ÞÓCª¸eåœW0²ä~SOsfO¶ïÐxÔ¥æÌò KQטrDaš;MW–¨ ‰šË;;§%‹9‘rsö„?;ÙÆ_2é€I‡cÚjI°„tZKŒ®œª3¥Ñ’Zع§ÀJÛ²8êjÉ4ö0M\<Øïí¶ÅðŸ¶ Š—lõñí nãßåŠ,Š?u¾¾ä‹,Ncj»láÈÑáÌÚd6¶Yœo“øY*jùªm¿\ñ¬²óº±äeçqêÎVJ!ï`VÆò†ì{náüÅj£¨5õÍPhTÅk,¸6‚ʤ…±J³;JrYqO$Ù6‰Î±7d‰J‹~è:­ddm‚ØÈÅ­í4u}€Eͨ¥‰œK¡®fâJ”oš®þ×úYÚ%"½]"²GÕãõõƒ(H°‰å×Ý0]võž¬Ÿ­×§ f³~'Èäô ŸE#è…’æ­»=Þg[ùž')Éÿì:+HÂ÷“( cÜpV¢á8«Uùý™œN~©Èíb¡´‹ÉéÊËoDÓÖ£FŽ<Î2\_ÂÕ¹ì'ƒíÇÅÞ¢Õ eÈ<Š,áüêó“ѵCôXÝR‚/µ°älgvúÚø x¥ùÛd”~âžëßÒ<*Õ endstream endobj 242 0 obj 765 endobj 240 0 obj << /Type /Page /Parent 212 0 R /Resources 243 0 R /Contents 241 0 R /MediaBox [0 0 612 792] >> endobj 243 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 246 0 obj << /Length 247 0 R /Filter /FlateDecode >> stream x­VÛŽÛ6}÷WÌcð*¤$‹rŸzq‹Ý£Hc @  %*f#‰^в»Úÿé %Gò%€½° ¬d‰;> endobj 248 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /F1.0 8 0 R /F2.0 9 0 R >> >> endobj 3 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 2 0 R 12 0 R 16 0 R 21 0 R 25 0 R 30 0 R 34 0 R 38 0 R ] >> endobj 43 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 42 0 R 48 0 R 52 0 R 56 0 R 60 0 R 66 0 R 70 0 R 74 0 R ] >> endobj 79 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 78 0 R 83 0 R 87 0 R 91 0 R 95 0 R 99 0 R 103 0 R 107 0 R ] >> endobj 112 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 111 0 R 116 0 R 120 0 R 124 0 R 128 0 R 132 0 R 136 0 R 140 0 R ] >> endobj 145 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 144 0 R 149 0 R 154 0 R 158 0 R 162 0 R 166 0 R 170 0 R 174 0 R ] >> endobj 179 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 178 0 R 183 0 R 187 0 R 191 0 R 195 0 R 199 0 R 203 0 R 207 0 R ] >> endobj 212 0 obj << /Type /Pages /Parent 249 0 R /Count 8 /Kids [ 211 0 R 216 0 R 220 0 R 224 0 R 228 0 R 232 0 R 236 0 R 240 0 R ] >> endobj 245 0 obj << /Type /Pages /Parent 249 0 R /Count 1 /Kids [ 244 0 R ] >> endobj 249 0 obj << /Type /Pages /MediaBox [0 0 612 792] /Count 57 /Kids [ 3 0 R 43 0 R 79 0 R 112 0 R 145 0 R 179 0 R 212 0 R 245 0 R ] >> endobj 250 0 obj << /Type /Catalog /Pages 249 0 R >> endobj 251 0 obj << /Length 252 0 R /Length1 28796 /Filter /FlateDecode >> stream xŒ¼ |UÅÙ?>3g?wß—l÷æf¿@Br"9lˆìkÐH@vÊâQ‘U«*Ü@Ñ–°­u­¯¸£ÕJ[µRi‹ˆJîý}ç܀ؾÿ÷óÏÍ9sæ¬3Ï<ûóÌJ±’f"ãªk§-7JWàÌùvÕâ"?õücBèfB”)³̾ö€2çEBÔÓ„HWϾæÆYž×|#±72bΜ™Óf|ÿÃÌ;¹üU<ß‹Ÿp/Ó¬„ðW’¼9×Þ°´¬.èF=ï´^3ÿªi½OHC®¯¾vÚÒÊnq1êí¨G®›víÌ-·Üý0êGQÏ_0ÿúä‚kW¢JžZ°pæ‚ÿÜúS„¸ÞÁ9Šÿ³™t ŒË»Îð³é?†¾ò?‘H¸K!*шN,xÆFìÄAœÄEÐÆ‹þ<ÄK|ÄO$HB$L2p-“d‘l’ƒ/DQË%1’GòI)D­ˆ_ô4!%$Nº‘î¤)%e¤')'$A*I/Ò›ô!U¸·/éGªÉ%¤?©!@JI¦¹í ™b¾FRÇÏoɹ©ãü/Ù×ètVzëúf+y–|H‹h„´ÑÑæ³4D{’aèï÷èù.ÒI@Æ“Ô6ûÉ2ŒŠ¸'N§¾B;~EKí§·§vâúzò29‹üY¤hïHÜ?Ì$_ Ÿ“úÔC€Þ*À®Kýdù¿ïÐŽûÈýäwô–ÔY|ÕKnÇûªÑ«©Rç»Ä ÒQm/¹—¢rêªÔ\@2—¬eñÔ©ÏÃzò8ymŠÓq(à{5¹“l¦!áe=@ž Ije  éy|i™H®#KÈZ²“¼NÝt´tT:•º9ucëÁHL#sÉW´’Ž`OŠÖTÿÔÇÀˆäUô—ÿ:ÄËÅÒåÉšÔ#©1Æû©NŸ£/HåÒ=·¥MýXQ€»ýžH¦“;È ä5òOò/¶<µœ %ãðå?Ð,¡€ø,Ä–±e»é¤­]D¶’ÒJ’Cä0`ó'rŒ|N½4ƒ§Óé½ô_ÌÊf°·„‡…=Â{"Ÿ¼cÀ£ry’ì¾IÞ¢Þ_FGÓyt>ÝD¡ÇX û†}/ªââOb§T<–ü)52õð3L.#7‘å€íã¤ì!ÿCÞ'ÿ"ÿ&g¨“ö¡s裴…£ß0å²QlÛÈžd¿F ÷ /ˆ•â@ñjñMñci¥´N™¦$ÏmOÞ—üMòíÔþÔÛÀ;Þ_@jÑÛ€O’çÉ»xûGäSòWŽ?x?:…^‰¯\OWÓûéoèèÛôkô’˜¿\Ö ÆWç³…€Óíì>v?¾þ~GØÇìSöwö ¹B/¡IxThÚ…#¢S,{ˆ=ÅQâ1…‘)—.•ÆIOIÏH/J§äjy†¼@þR¹]Y¡þ±³¤óÏI’œ“lI¶wU`ÒM€Ä¯ÉcÀû=ƒ×ÑÿA‹‘Ó…0ÒB´»ŠÖÒ::‚N¢WЙôvºŠþŠn¦ÓÇèoÑô)€Vœ `ãØ46“­`«ØÝl~Ùkìv”DËBLˆ =…aÂárá:ôáa™°½WØ)¼%¼+œ¾NbÔb¶¸H¼I|PÜ!îß–.“®Åï1éy©Cz[:'“™–3åRyžü”üWEVz)£•5Ê{Ê¿Õ4“– åàþ…? f³Ì+.§'q:‹Šàd÷’8Æa¨âߤFHb\ìü:Úæc!Ñ× ±…v=D*éÈr™ à â1ÒJ?aÇÄß³KÈû´‘†ÄÂuÒë,Jž7ÚÀžc‡è@²‡U³‰l‹@èçô)ò9ð})¹Ÿ^M¯'ÏГ´/½•ö¦ËÉ{Ì/Œ£+Huê1&R£§Z@ng¤ù…nü×­"Ÿ¯’¿mâ-àOíd#FôYò}šüH¥Ô7àn¸Ñ4p™»€ïwÎõ@gËA!pkä·È*Czõ–û‹7‘Säò•t5ÜôDr®økño©Þ©î 0Py t7‡\ ŠùXru^»”®ƒ—”ƒªG“)d¹\ïÞTKjKêŽÔ©ùä <û#íF¤Û@íx¢š¼Šßzò]:¼ô¿ºöÿëDr¤Ø×4Hói9èᤴXÚ í”öH¿“Þ”{Ú+ÈÃÀè¿›uôà*ò6ùš|OUŒM'ööAÛ'“kX½p˜ ¢a²4[>>°«'×ã-·z[@χA§À'® ¿#G)£ôè*|_Å{êç©äz²#xmÙàÚ%äïè·öa7à{Þ´\«mú„|h§Ìvu_L'â]ß“Id¾Ð‹Œ¦»1û ýF’ÁÂï<ê$i.}Ï5‚Bí°UÒß(#Ý’#S}Ø\á0dL ç·AzeKhZá@?:‰Ž"•ɱhûT[è;f+d3S«„%ÉkÈäiŒ‰!.Vb oÔô¿¤º_ߪ>½+å=ËJ{tï/).*,ÈÏ‹åF#9ÙY™áP0à÷y=n—Óa·Y-º¦*²$ Œ’nCbµ‘–‚Ʊ 6thw^MÉihl‰àTí/ïi‰ðç¦áÒ/î4pç¬ÿ¸ÓHßi\¸“:#Õ¤º{·ÈX¤åÍÁ±H;2f2Žï«´œ4G˜ÇÌcŽ£Q<œ38ÒB#CZjÏY;¤qp÷nt·E4SïÞìÖ-8´à¨%[°›úSó€†ôÝ͈jC[±ÁCZB1<Š×ùC¦Íh=fòÁÑh}÷n-tÐU±é-$6°Å7o!ƒÌϴȃZó3‘¹-è YÙÝ­cí]íN2½1n›1íŠÉ-Â4¼cH‹+Žïn Üt<øs/wš¼êâ«ÂÚ!Á¹~óÚµ«"-c&_ôlF”¿¡¾ïÀ³,¿¶qm->}Fªn\_cwÖOn¡wâ“ÞÞ«tÿfƆð3ó"-Zl`lÎÚyšðÚ2öÆhk8lH#á!‘µã'Ç¢-5±úiƒ3w{ÉÚ±7¶…ŒHè—WºwÛít¥»Ûîè:°Ú.>˜  §¯™Gæíü¨nìÈRޢذuU-™CŸúðÝÌ>díU}0ø«§xªeFdn‹6¨q­³/?.Ò)ß‹¬ýŽb'¿ùå™i]gä|çw„_äxrÕZè´óÇ-ñxKI GeÆmìoÖ+»w[ÜÎzÅ8#(>2°Vß·àFù¯k7ÈtTZšÇLN×#dzF+1Jãõ-¬‘_Á¦¯ø&ð+Íç¯\x¼1LÞcªó¾µà¿Ãé÷ ™Ó·…úÿË3Ó×ëÆÅêÆL™²¶± këÆÿ¢–¾Î ¸áZ×Q‹gÐd!ƒá?b‚yHyÅ” · 2ÙÚ"æã_æu@JóÔ¶8‡¦÷õz4ÚE2ÿýL»¢^ôP{êÊ,~~¬«-}ã]íL·º¥ß/ê¿hu­P7‡ÕŸ²v­þ‹kµàek×ÖÆ"µk×NkO5OEœ±µØ¶cí‚!àBémO\—ÑR{W=º2‡öÚ22pwŒ®³Û «ÇM™|À ‹kõøÉ­Œ²Aëë»Cµ€:CK"Ô ¸‡Ñ¤¬´³ÃC$1)]“”„TYJ2á9Z@4(¨AŒ;ÏTwVtž®ÑYMjpì<‡]ϲ¨+êÊÇŽB蟋ç ‰üD"bj\ÃæH5ìEŽS5J4'q«‡i. =Š=a÷ºöoëñLdb;ÛØæzòjþņ“§O:O’šgµߢ 4VÀ*ž^½+óyÝ?›ùƒۮš¸¢cÍìK*cÉ1'迾‚ÉŽN¾œô'’O=< =F[¡-†Ù–aF°ê³Ùl}€ú”]ÑT'Á¿ÛÉ[EÐg³U{ÔK[y{Üóñöœì<þËæxú • &TøÝ>¯Â„!ã÷ÍœµæùM;Ö=›Óú»³Ÿ-ú}š–~˜Ì>ûö·ÉÓÉŸÒmY”<@Ÿ¤\K¬Ù«©YWÚi¶‘!o¡},º¾(yÓ˜-TCÖÙ‹ƒq€¾aÄñNcÄÉÓÔUE\UU=Ënàu=ŸêL`ý0écd †1ÁË—äÀ@úw–„¿£-÷™m9=âäHç™èyuMõ*©GüVçK=ËZAzõ»É{CÒ7?zy»™˜:!Ú¥è$rŸQ·T_­ï ;•Úû~íUMèª÷ׇ'æÌvÍñÏ ÏÎQ«X•ÜKëeƆÉC´ZÛí öšü’ö’í#ö'ù=í=›ËŒY°=Õaä»ý‰àvÕ–ã(u0‡šc;‘²ŽŽ‚¥Îõµ„¢ï¾hŽ”Ùâ&Þä“ñ&¾qÜ% ´<àw99–K\ÎÞ½¹²"»œ~Ey¯Þ½\΂VþþÒõ–¼ÿAòGì+Fû³£*Ò…Ô±yOrj²qßF¨ìÛé¯÷müjÀøk“ø{Ô5RöÂŒõcØÀ@# íjv3[ÀŠí´¸mªD¥vvå~U“(±j°ö'f”56‰ˆ9bDlE1¤¤; 7§Q®z§{ô§NáHC4ê’•Ê^y½+„‚ä‰‡Þ¾Ž²²ãblÃTÞk+ÓøQ›ÅŠ6dÑcêÞà¾ðŒ×ÅW‚G‚GBGÂê ŒA™ƒ²&†î·gªr8BŠäÞá¡â à Ð °šÌ å…8Q\Ü’±%sKÖÎÌYª›d9³"Y=³g­ÈÚõA–šÅGÆïõ%²˜ÓêÈâˆÌ8.@%\jÃ(‘vöh£VG;hÄr¬¥Vfå£gÝî‘´£~?”UJÂ9Ž£Î%,”}~Os¬kª®ÁPg¼é8˜^¼¡©Ú客®ŠxÄË’•êhuUñ6´:̰;«DÕY%©.”®ª¸ùW¿[fƒÆO6,ZF(ƒex(·öð"ü7Ôsܨ3ù0É€*“‰-+u¬OŸ>õ´©ãŠör÷vT& b@—ü^yåЂ8¢¬ˆÖs…Îmßü.Þwfýä9jòËU_þèì¥#*’g.õS)ùÓýTûÓîšI®œ9ïæÌ/_ÿú·WµMpztAzœF€^20NÅä#£|•ï5»9s]&Û.<-íðîJû¼? ©~/½Ûw€EuiÀãæØœV½æÖQ6jØÖÛ˜ÍFý픎O©‡y8€=Û3$  ïu·€ƒ©rœ·ÚZ¬«ßytyÎúœ­9»ržÏ‘rŽ)GGåѼpÜ4°„%¡’ó£q2=à} ']U¥ ]CÂÇ…W›NRN0E¨®+H<ù&}™ðSzû/²?«5‚oû±#±Ü¼Ôi[8fÒ’…c{Õå,\:yØÐY–dgƵ¿¿ñ­[g¿»lSò‹w^IþHïŒÎ¹nÅ‚y·ø>æN>yFc·;·^¾âšÕ/\ŸñÜ/$O}šxÅÁ€«ægF•5b«Ò¬!kÜ:Îzµõ¯Vù¤Ê¢_Ì‹lCm—ÛvØöÛ^¶i”©Ä*ÛI·ØbµÚlíô·FX½˜4³Š6ÁÆD(†­Ãv•C´NF÷ì#¢ˆH;¼GZ¯SŒ 3ÜNe«ò¼"(aG [Î ÙÒËèP“²7A¼Ž8Ý`w ÄzgC5¡»Ê„¡»Š3ZœÖápœGÞîÖK¬#¬oZ?µJ$¶o²¿’V¸*|1uQ¶¬ó)vË7ûö%O%wÑÂ3Âãç®ü>ù˦ß%-@:J.ÎUJÛÁœF±jX{»‡¸‡…´ýÚ¾Éý±]s»<î¨+æ¾Ó ¦Dm:ààv¹ÚÙ6Ão·yív›[÷rÿ›A…ÑtØÞ/l¿‰_6ˆí)†-G/Õ™ÎQQßîåègñúo™×ð ÞvúŒáu¹rœ¥NVê¬qŽr N~«“ËãpØE‡y$@ „sìí4j¸mKèsG5àIÜ~q€^ÚÅ,ÁñO×hàœi:MŽq“ð]C ÌkpM1nb¬‰­¿@ÕB «ôª('ÀQHŒ¼ËiкxÄä›nœvcãñ ìDç?º]9ýç®O¾‘"ôƬ©ó×oXµêê(û)ùÃ¥ÉSí½çED €“óàcÞÌÃF¿y–Eê*uSh‡´C}Ú¾ÓsÀ¾ÏuØÓázËcóI½\ƒ7ù÷²wœG¼Ê!ò©t;3"`]ˆÙRÆv‡-'Ze‰?Ý^£QC;¢¥4Ak§£ÚvQŠq‰¹9b)(Ÿß#n÷I é%ÙGGY©5œ<êå] oÎk´4«=ÝvINŽ”œª9=ݨT`òAÀ:È‚”€#R/¢i&):’§ôñƒêovÎÝÒòSòì[Nþ•–ücÇŸ:]6fäœãÇ,Çe½­ó–äé÷þ’cZ“Üh­>¼6 ·j(¶×¸U‚`–—çþ}ÞFa| 8ˆ7ëtÀ¢¤ŽZ彩\„b"pQeB6°Cí¨1:ZˆkØ“ðÚ"½ÔÚ‡ô–j¬óÈ<6S˜%ÍQgë_ Žá2ļF]ÓDE£4B/Ь‰bD’½’$«ºÎê¯óOXÂY =Ÿ ‚,‚­>gØe…I"ª5C¯fXrðˆÃfXCí,ÏÐr4Z¦5kL;Èòˆˆ;´ôþåÊ«ºŒÈθë醦`çÈ!3f°ÔT8 r)…Ž7¬U·¾´ªG0ŽBqVW¯z饴°GKh¶‰s­µ®Å2®®%œDH%[UQ?˜JRçvË"4Ø´›Ö€£Q?õ‚ô|òwÍûnL¾ÌúѪ’×_¦#’mÒÁskY¤ó»¯5‘¦öù¨Q³¤„α/-ùB<#ŠZÔ§ÉEÝ¢ù~wŽo”•ùvù˜Ïçåæ»=jÄ›O Ë(\ 7#lSWT¸ ¼ °lÓ, (wÁþíaôÝ£±Ç‚Í=6ôØÖCô(³òæFHÄSÕµ­këÞsÜy¨&@CÓ™8—O à5@#¾™¬Æ4|©æÖ¬*>ÒæEón×ûëqÓyéuZ@k·vÏUÓhy6ܲ©‚Š—¥(L‹òÞ½¸ +,ˆ ®hW¥ ¶‘ ÿí3«¦ÌŸºrC㋇'?OÚhÑ‹¿)¹lRÝðnoï¤îmñãŒ_—f]ñàÔÙÏÆ Ÿ[>ãp“MeâËÉßHÚ¤KOФÎÉ¥šµaäÀ+J¸.6-uBºvx˜|`Œ\©­ñ®ño%›åW´÷„÷,ß Z¾Vd-²{‹ý‹¤EÚJIU°õÊ–Ú…+¾Nþ˜üú 'çá÷G§GpÔ,7*Š@ò—fŠ3­RI *0Ô_ïŸã—ª½2Ve<(m´H9.Ž˜w¾Ã©† wq5 •¼_†§9J#Ñ20m—xè,s2è¡ëÚ"ÿ+^@BÞÏ&Ê)à7P@"(‹i4êö[Àñè~–µ¿ñ¶öÆî½g¸cúïÒ¢Ooé=tjuõ5ãúï•f¼˜<ñ?{ïØvU]IŽøâ¹J»{âvîÜ7Ëmç}¥ˆòñúj!ŒKT ÊL¾ìΑh™´ VÒ1î]Ë·Ädê6ÔŽØÊl Q‹Pn”-Ð'ëÅ}2ZOµ©0ÿ'iI ©¬* 4ÒâÅϤ%HàK¦oÉEÙÜkÎ}ÅŽuF„ éàÙä¡ï“Mß›íß„ö¯@û5²Ð¨Aûe)_‰¨eêóêgªXªnP™ª’t'4ô FÞ1V€iÅÂK™…Y~ÙýëACÚMÒY 6 ?Agõµp“p²³›Ñ¹…·îɳ÷rØN  FÀçjûd×eOT«‹­wª+¬wVdhr@ÎpÜE®¢`Q¸([j¹\¯M±Ìoo ÞÞgßç|Åö²óCç §]È”#œæŒœpUÞŽq¡þÌî²æædç®å¡NsNs%þîD‹i$4§ ÝYN$" Ë¹eÐÄB…ÛtêÐsô2]Ð9íE—mMóÄ.Úã®`çé“Ü )EßA{ AnžWw6Ź£¤‹ i%\FP³ò€pfTD œšj¼Ïéæ®¯J¡†-kHnÝûErç³î~†dE·äÇ9Ï4¿øù—Ï5Ä2¾ïlŸ²æ:ûÝÏ錩Ã>½÷5·žùWò§äOÃÍ1æ2£ÄÄÑÇ|M”tiz¾èÞ5[ ²$a0U†JjD~ËTÃ×¹†m´­Ñ&,°5ÛG×m°­E³¤¶Â(²‹~)žièò„›Â;>ê¦8LœÒâ€ÿ³çÑÊGú·‘±Á´(y´ó9é`çólÀµì¶NnƒÜÙƒ> dþZh+O$@m±|³4j¼‘ i´Ô,“¤©QZ ’Äf ̘ DeÂG”rŒœ/s*<‚šH®{žÎ…]]©1}ìM !¼¸Ëö.Z$ü±íx°ý=‡-½×«2u»u]˜ BhiˆÃê’¦j:ì›ýF\‘½Š" \Ñ¡ÎèºõE4~nYö‚†‹EUT¸úg´JCU†[1™»ùó¬¢Kká- q^L3ü´Î°‡wHÙ@Á»:hz-Ìö*ÕY­¾$ð}uZƒÙ«E,¶ÆåõVµš WeÈ ÉF¨@.Ô6ˆ›åmpƒvˆÊ ù)ñKñŒÍ+u¬­÷ØÊ#ùò%ú ÂJáAáAí!}§pPxMÐ_@rÌ9]¸D(°…°3h¼©¡ÞÄ9õe›ÛR#·§¾„[ÁR#–ÙüØY½5bÄâ®AKŽ´9BéÒH—¸Ã<›Ì²ë¾V»§-æÔÜCà#E£ÿŠëA`ÒDzOçQV›¼-y-Xvç"¶®óçnc-ß%‡˜tòxá“Òo‰D.1£Ž'"d9QE)ŒFïÓЗ{øëG:“œÁaÒh¿õ=‚/“~ûÓ00ZŠ ãä ±² a±jž+Š.4Zfß„éÛÃñX[Wi<‘Ùg±“EӾѡß麇eŠN-G±nbD+E¨fŽ8S›§/aKÅ'´ú^í ~FûQ÷o7h[õ—µ×ôÙQñí#ýûRü\ûZ·-Ñ–êw°»Ä;´»ô L™l™É扳µ9úbv£¨ fuâ`­NŸ¤NÒ&ëJP/µ'X_1¡õÓkì w¶Éš¦ûXX hJ—,€Ò5ɪ(å²ÝZEÕ‰€ÖhÕ–°ðÙK;pK5ì… ßáÔÃÉ,ªÀ 5¦èðØAã®{ ty‡héIç{P¿Uð¯ô3ºã+QÕ´ò´ë!&T.0x^#XEƬ +MQsì^)[σ8ˆ8 g—7¤YC`Üø„T®Êr•ª‡—c["+kg} 7x‚‰›HyW‘ñ[Op9çi¸^âÎê8«Ã!ggSgSu8GV'œÇ›ÐxŒ«©N“7ºì„.›À3怚:¶Ûá”0ü™<%N@@àjg]÷ÒCÈHQèsÉ“ÉO“Kþf@PøòÇZñöŸ–ñ 8µ¼'œÒèÿvMÕPE78 K8e¡4»ÍK£=ÊLHTÆA¼+Aä=yÅrù-3²Î–Ñ–F‹°ÀÒlaÛ,––÷*§vK“êíãÆ%´rÊ™çy‰ sXñ (&LtÒ ¨H5¦î®TUµª§U*GÜX:fhÀ 5’Æ‘Žý°©TÃ4¬p'Ò Ó¤jÞg©T›-•fÇ. ÷H¨ã°“¿P.‚X+Ü Õe›Úªä—„·ÔUôJÕ„ÐO¥þJتnv©-Âóª%m°V Xi`‡Ú1ÃVZž`¾S¼•8³ÉТ=l×åëüP|1ÙØcú€Ñsl:úÃõ5è;Èò’³ÆíUŽaŽIÊ<Ë<+Ön‹í³ÕtY•õ€ê×{ÙkíµEuj.¯Ýëð:{Ù{9.u,²ßè|W·,Õ–†g­ÖV‡VfÉšß«YöqöEööûíÛ%{ÄfõÚlV‡Õg øó=N/mônó2¯—D¢\œ¨`¤Ï…ÄæD(뽌Âmr‹Ü!A”mÕ‚ÄÊb,õ] µÜžé&ÜCÐÐå0ÙãÏf÷pÐp‘ÇßTŒž@ËMxÂðD…,sÁpª0ûçÿýýæ_h¼u^[ò×,å¬ê?½?¯zÔм='¤ƒ£^¿ýÉ3û¬|îšgê£[„‘y“¿!ðÎáð3þ ´Ó1.9àjÏÚWôr7F¼F¼/Ÿ)Í,ºA^j»¡è#ë1k½>Á>!·>6Ç:Ë=;:·hv·%Y+³6F­î—ÙÙ9 ^3CáĘÜ1±r_ˆ‰M¹M±Ûro‹ý%÷/19®—ØòróbU¶D¬N¯³ Λg›»ÑvSîÛÚÜíúÛS¹¨Œ69WŽ…ôÍŸ«äÆt¢ŠƒF(’˜¤óƒ[q?Èf""ÚaXaÈdÐŒî^ ¥œÃ G<ü3iQè6ä§t Åò¢®r"ß½D ~›BÄÆð:¥° Ü#§p›³uýÖ•¶CCÝßér$Ô›¼›}êMhp:¾Ç!šâ§âÇÓåÂøqÈ»4ó2•º\À##«?àq¤«ü[«§*àA³¯µºyíˆápWÙ"î*ÝÜüÜ—†ÝŠs¶*=È7ÓWtž?‚ùw)¾¾z_[en%à8Ì6(·6¶]:W7ãni“ÿBX³&¾éÀþÙ°RdŸ7àMÌâþá4Þºjý½—\–8ðÆUË¿}éÖ%yÔsë­· +íÖ‡¶¼µè®y>ùuòúi潫o“–áîÑoâ¿]ðûYÿzÝÖtUenU"¿tÖµ‡×-ûäj y—Ð%€†ØÕ±R­L,“Fk à}Ü )2•X>–˜Ñ¡ÁY).ç—v7tY¿)ñÐ1Qu öÑÈ{of˜ÈBjç³iþŠ€øn†Qáñðìà«<ÞÅ“¸ÝO :*¹ÝO?KŽïNŽ_<{ö§þ”Ü™‘‡v…ÈZ£¢*šâÑ.U/Õ”IÚDçFç&×fßÃþÎýþ}ŸËgd‹Íj…{FÉ÷hVKÄöW¬73Fg4f 2š3X$£,c[FG†˜AaGEBe¡Žâæaø"eÀô^0OšÞUÞÜ&Äó0(ÜõÔ«rÏigpŠó‘»Y<ëoYÖ¦Ee·ýí;-ófA~q¸Ï”kgoü­?—Lžýxcý´‡',;ƒþ¥RàãÑ?™ÚÛaR¹Ä]%p5{|¸o¢Cý“>?’$®ì.•6ÑìAq³´UUb‘KU®P7ªK¨"~¹˜ÈÃÈ¥ò$Œ#çJ¼Þ´ gz¤…v6ݰȰœa½aJÙ4„Ä:8r[Dº\l?!w¤Z }¹Ð,|&ƒâjÝ‹; z¤¸/ºŒRR.òEƒÆN74ăܳšÖ,OòðèÏzåÏZSG›ÓÔ©;öBU…áîˆæ‰È­#7%m 1Kçi:ÓfÓ¾ÿ–þô{ñ²&n(ð¶¯ì¬4e¸ãB\ŽX*,"@i1:XiÍm(9,Ï—­¡Jh' {ãCØÁŸœ®^ƒA~̨G¦ŽÁNÙ [Ãħ“|MùJ?aý^ûAÿÞ*½"½¦¿bý˜¼+åë×äsM{F|\zFÒzHl“é{­¯ŠZ1W*Õ#Ö‡Åû¤‡õ¬jºû{Tj·¡qmö(oâg8€‘åMÞÒ–¶?¶>nÌà5‹ ׎“-“n.²8L‘”±çE‹(EÚSem2 ŽöT¹q…@¬‘‹p@‡Û¤Ü¢{-H¶–¸Ì4¯ªj¢Åjí2MðÁ 7™hµ€<:YU© ML#‚Ô_ ¤–zD>l9l”r›Uk„‡. ÙÎcD84¢³!ìì ‡:‚çi¼à{þ3[™Š83]Ûý#.Ƙ.ƒä¼]’FÓ éÒByÑÄ‘Å|ñ˜HCg&£¥ŸR+¤ ý -InI¾ ×ð§ C—ðí98L`• ý©ãPªô78¤’?ðØðïŒþ–ÊŠ‚( Šˆœt)}aymÔ<‘®6! ã÷‰†9oºJ'Â<ô`ÃÄCv·~ˆðn™öЉØlý”¦%W;›Õ¦qp!­ :ä<Û,]€;¦)Èyæ8Q:&€$#Ó17Ý!ÜA KUÃ7‚D:"Ѧówž —#>–Nèü9þc:ŸcƒÏý¦ ðU?øýaô[£³xDìÔ^G•¨»KdY˜>•aiYt.©Û"… ”ÇŒ +ÙÀ 9‚ x‹/‚ÛrÔr²¬¼òž®n¶<#Þ#‘gäaÊJÌÏ;—ÇòjÍ´»2»#qIå9y-Y?d‰YYÝh1p–ÛLŒLˆ™®š¨‘áÄ.NDÛÙ {EÅjÓ»qôÀ5³Äe³ÄÝp‡ax-Ù= Ôb­ÈVŸcÝjeðO¤à¢0ìÈÙ JÐD#pñ.J*Š£Sô³˜˜¡Š¹º,´…ÐèšN6¤º‰×ŽóLF87â0Ïa´›ú鬧1¸µ4‹6ÕŸzZªzg\šÁÜå‰úDÿÄ`}æ÷Š\)ö³õóTf ëluž!÷+jºÕvL†VIñòÑðX,¢¢jxA6Ív3¡»bÃJf r(«& q䮜ì¬þb$ìÇt².âìÜUˆ”Ú«e–Eo‰£w9Şɿۯ5÷ÖåWžå£Þøé7¿JþúO¾ø9û¦|Üø{wÞrùüÒß½H 0'O¡ù;8Øñx&Ç› Fww½\¯×»Óز¨qVÓd7g³¾BÂÚ×— [‡û‡Ô4Ž'­’…ca·(v†BÛmH+6^Ïq'ª†²&W›äÉ{Øt&1¦v’ÎÊ@Ô¶‰ãŠm®ž‹4Úà–ª-½¾`PÿK*ßy'yb‹X0zåŠqy/9«ÆÔ}zn¿0̤ýä±ÑÔ!JéHcú’¬UYÌmµ-è¹ÒÖÜSŒPø¾„2ZÁ*ƒbƒ„ËõÞúü‰Åãõ¥W;κÎzÜýlþ~EÝàôñ× îvÊÚÐïÔ¶Xm–«­ÐîøºÛ¬pKó8ì5)À$»ËD’6‹5]•¤ QNózÏDš4_†)ú§bà ­9ŽB^ØõîàŸ É%Å–‚p3- ‡×÷¤=Á‚Ú TäEÝ¡² ܇g³qþã<éì4Ý \XužîòTŸ×€Ïm lƒ18&úR®¡ò¬BSMåzt—ˆk2ù–c®wnþìâYñ¹¥à[¤! ù¹T3%%xt÷ªpqâát€šU4ñºÞùÛ²ŽnNéóh¦Jÿ‡Ö'ÿõ×sw4ξgõœ™wÔöñeGý=cW>üìÞõïS ÿæs—>wp^õ{ì쎧yô×On{ø+èìõàë~ÒjÄ4Óë1Ît ëÏôª)’_Êc“]s\m×åö`2up f ïê^ŸÅߢ¨šÉKìÒh J7À 5ן›—ØÜd ‚§‚ìÛ æŠy üœõÜ»ÍGOù¨/¨I³}8ÿy:ŽÎtÕÒÖ)bJ'Ó€©`©¦‡ÒÀ”FŽP©,@l™ÒgVž¶eTVòDdÌ%µ×U$á íü|ëЫ×wÞËzî˜R9xÍÊÎoÐi0Ìû@ˆÏâç.9@4´¬Æ¥×Úh5k-ZÒ–¿Õ¤­Q[®mà IL† Å 3V.èD²„ù:S 3yï´h^B ©]ý2{•–c&yš©f òl!OÝ5“ 1 Š'Šû¨˜<÷Óp±à'3?;õXr Ýn¶ÑGÖ#üJ ôRö©Rs¨%¤€c¾…SûÏ6‰>yª“:ïÑÔ[Àœð°…×s]˜lNÐ^^åƒìjeóvcÌxUhÄñ ‡ÿù†“ 6³)A­Vž¤.–¥}ÚgâIñœø,TFª³ä[ŻćÅ-ÒCòCÊCªž#ºå¸—Jä¥D-µÕ‰u’};•€ÜY´ ߇O{ä9ˆõèä\k„¥Rµ*1’™H9( Í„òt™µææ.óÁÌ8@FvYéÜë€ÑŞÊÛÓ܆>ŸQÀ»öj«íJ‹äáN²Öœû"ók0ým’|¡Ÿ·“ßݷº8yKç•ôÓ5ÉgñéŸñtœ™ëas,Efk–Z°ÅéÛt‚ÇriN (­C§œ=™øˆ)sÿ…¦xXh¶Ø×•ϱ qúÍà÷…´ßRŒ§ð-ÈW«Oö[BBM±Álˆ:$88fEL±xœÖXÜ\¼µø y‡²ÝºWÞkm)>R|¬ØNŠK‹GãÂóÅŸËÅF83Qƒz³yQR¢¢Îâ±UW¸õgd‹ŠÓå*ÌÈÌ,(DüMv8 Ü.cJe£‹Î‰´³ZÃÎ(ÈÊĹù™´9€8·'ŽR®K¶RˆÞ¶9´^½ÐîBÜZh ÀV-¯0Qhô½$QZøVág…‚£0§°¹P …‘²ÂT¡X*ú[šu"¡“¤¥@5²bã¶g`‚£8Ï”øÐsÝlßäú€çBøŽÀšâ˜{ îä˜ ¦a¸…˜ÔÏüjÖuÌÚXVûØ‹+×Ê*ÓoNä‰ìš^ætOž î}zü„ ã§^1xsg=›úëÕC×mL2Vûð”nµ+ì<ü@n…X1ó“­FPñû“ô‰ö¾ábùK›s‡úû£ü{õe«´H]#¯P`ðÐà@òŠŠ·J 7f,@Ö¼= GîE¦WÚ€5ÓÄMã•Kvm®s쑹A‘6@¬#ç2áFÇÀ™ùܱ‚|Ó KÛûc×vnù'M$_ûæWÉï×ÒÈÆë®{àë®ÛÈrï¢òÚä+ßþ3ùû©§~ýÔSÛ¶<…¥²Y—¼FÜ„þ:a{=dôèãêaî„Pe«ò$2 ÃlÃ<ƒ3~Èиý~Þ&;£ü¡‚‚.¶Õý ÖÆ8o«»ŠívGÓia–ÿ´ÖGœÄô$dtü—½nN³áº ·×/²Áx¾²ã:73¹@àf˜ÿ‚Á¾ŽÊ¿w€²ä¹“× ûï™5ýö•WÍ^Á=#ùçdgòLò£Ú _ Úžy¤mÇc[’«z›}Ê(Ú$QÍNÇI³¤E’PêžlŸc_àFB¿Ãšceë­)+«±ŽÂÄÁv¶Ä(V`¸Àd½ˆhN­ 'Q /wou³©îåî]î#nÑí$ÜÕ\lXkF°¾fWÍš™V°¡__@è3 Œ¦Š zs¨Â:$M¤®%€<÷J3s»¼ž†DZÙ–]tÇéAWn¬Ÿté%ýÆ–Š›®\ù];“ÿDË€ÏNô±„½htÈ.9¦\Øf÷fï¦ÂJ4Å[ëeîC¶öW¢ŸÇÎÚÎäÊŶ ¶™¶,›Ü;rX•1#opÁìܫܫ¼+sïÈÓz ‘k-Ãm£µÑˆ¢æô¶VFy̰2O‘uÉ¥Eƒ¶BknnnLÉË5º]o]ê½Ñ·¸xQÉjߊ’‡|”ìÉݳ5Óõ»‚–<]ÒÒMDýF4–ð™9‰?ý æL…¿>ŸåÁ¬D~˜»êŒøîèn´¬-íF»eGË …TШ©­ƒ7›%nIK&ž×Š/mç‚õø­é—ëRͬzžr’ty‘+¹™úiAn¯hmt<­Ì sg'`b8šËŠ<6++ OE¬·¶È2:LõöþÓÎã´Ë»¡)ãÉM½Ñk"Úž.}E;×µåäa¢JD³yÝÈÀÁÕ6Ú+·6w³íþÜ—rßË•£¹V›(b¦EÚ^!Üri t¯Ai·f=7?í"΂ô#æÔÄÑTlÄ”ŒS1§©Í;=~ÜI©1¹`SÅS˜‡.ø ˜BþŠ€÷ XÌ£²w"Àý®#¿;¼×È1]œb`BØ€ÒíÓÑáT˜uuÞ V¶ñ8Ï‚Çü¤Á›Æ ··¹Né]Ñ2Ò„¿†t¶j^ê5CCz¡£»h{ê›}¶*«×ZÅ[­<^ýõnK•™tˆ”Ãú ó{øŒdx;téÙTéŒÞ®À3ŸWËuÆ2v_wÕµ½ó½¾aÉg/_öñç¿W”üÞ5uòü²Hf}¡~òéo?ꤥñ±Š2K#>¯«®ÿÄ×>wϺžýæøcÙ¾ÌYÃëVþêÈJrR_²{¥G Þ4Š#†©^ìèkn¯w(! ~ ¸=ˆm»6 š¢+V˜ƒ!ƒÀ¶@K@hDÑ?1 ðV¸Æ Úˆ¯€ϕՂTÃRBJéTð n¢…‚€{‚¯Æ»Õ»Ë+4z›½¼G¼§¼ñ:½|"©§ÝÒmç=Îu-½Á)ú™3b¼©¾æö;¢×ÎÓ¦ý‡(Ï™ÆSÙî²ß(Œu/W¯{8ØxPØ«¬¨Ìw±›:,…™…ÃÓo¹ì¦*‹vÛm4,KŽ¿=ž™ñqIŘ!= o{÷‰äÀçnð™q˜å'[ŒÀ$×l×FIÐä\ͪ]u¬Îu‚)¦]ç-~¢û¼pMÀ?Qàó!_ ÷º©)¤ÿ‡¦ !ÞÜ¥"¨ô"{¿T.ÖÒBæ‚êœÖÒÎ<ÌÃÆdv;Fö=<÷ê—ÑPÎØš¡ Khhë„éWîÜȶ%ƒÇföµè8íà…=A°ú\ò‘&’ŠÂ¥ …ïd¾SùÒ£m(MS-‚xîC"•‘„©êV ìQæÂZXÏ%Ý-¯X0m:uÊðà §Éâ%! ü³$H_Ë*¢¥™ÒÚ¬æ»,Z ùW•1¹ñ6 £9×ñ*·…èˆ|i"~2޵*î6‚™E ‹-Çœ !Ú uê5ú(3é¬Ì°ˆ ô,¦' ³ JZ³á°VA‚‹@CÖ—€[È{F”‰Ï P¾BfPÔ¬›:*WP±$üf(<‘Õ•Œ¸¦'ÀÈpÒýÉñ´ðÕ¾Ùî|F“€^ç_÷ñwïβÓ0Õ`ôL­¬Ðè Èêˆ?ꊤe?Ë]HöjÙºËj5ƒæ1K•P%†Ê›…Ͳé7–v» ´ˆwj]´f°è—¼ZH÷Y­1R$Jݵ"½ÐÚãúkµäRv©4T¦-!KÅ%Ò¸ô%ÖUdµ¸JB2—¾ÊúùH|_zñò÷/ÿZ<.Gfïqëä$fŸUÎ °~ÆÚøw -£oB,ÀÑÀÍšÎkÐfÓׯ™ôÝïØÒb`×5Äì0äÌ»ØU?‡Òe¾2)·Ï‡Ò¹QPu!Ž>¢+Ž~™QÎãèÿWl\NÇÆ‘u\—0B Q¾j‰Ì®%lÒ:ì{"ˆŒ¿t€†ÓÚ w…ÆÓ‘q¬‚€ÁçF)ßóŸÙ ‡D^pþÎ#ñE8`÷X [ r¶ÕÆSG΂ñ[ +?s ŒgxÚ±VŒ5Šób€ súÇ/ÿ§QA õÉêze?uì~ƒú’Ï$ÿµpl(kçÛO³g:± hëÏÂ×Èiw³±®HyUd›•ôú¾rʆ fa1(c-ÒGŠ0ô-t‘¢иҋöUjépe³å¬|VÑòÅ¥DOˆ}õAâHý÷¢z™>^¬×gˆ×êKé­úýâFå þ¾ø‰~N· ¢w€KP”èb^+j>¬rÒW©_­ï÷‹¯égD 9¯§ÚÜAÎ1޶ù@ä\%ðY] *bM$žâ€kÓ"WöwO¤ÌÄöc†ÃŸ— ²a™&ÉDÍ˧0Š¿#€Ë–"a–§Ä#üLFf·…À…ym«\¡q_„E9ʶÕv 3¥jsm+«€KùZÃÍýǰ xÜ_$3æMæ$ˆP»¦C”^˜ÁSgâMç³gÒG@ Óc¨J£ú^=¬æL;-Î#HCSÓB`GÓ jŽ,öÈî Ë“÷ÒIϽL‡'7Ó5ÉG?f1&$?¡yI­óm:,¹ʆÚáC‹qõÐÄw‘D=¼óA+b~"¾“ùNòããœ1|NÑf±ËNF<²èA†½Às×ùH.v”9 Çr‡ìú°Ä˜[Q+H³u7Þ€Þ¡X¼Fã­ò®×˜åÏ–ƒ–ƒk©é’ã–7×â ±\ ¬VäØñ3§«OÆrFØù–èáæÜwÇiSÓ`@Ž`Ö@0$½ ¥®¨Üwo&›pyÉ <°Æ&s°ôÝReqZ±ÙL¥·žTTÒ^½1q‰§a`9ލ/—Þ3½´çèäáºä¼õ‹2iÛŸèk Òľz%Ùíaåûôx€ÜÙzÕÝ÷Š©ŽêïÔÊÏ’—Þ¼ë«ó%÷ú"í]ÐB3/s:"JÿäH2ÈI~üñÇ›x©ëÊùbŒS¬ nƒäjlƒp¼åõ(ïcU©Nñz2ÛcØ*°ÀV€írl“º¶q(à™×¤WÐÌWÈFlÓ°Ý/M$ˆ#›ä*2ŸÇ;ïBù Î="ï$÷âx3®Õóûøu”Ãq­Žï“&¦RÊÝDAÙ‰s~|cÊU(' ßõÍ yü7ò«®öòv®áÇJY†k÷b‹me>[†væ ~7î³àÛJ+6»H°J5Ö5Çïò"Ö=Í¥1W÷+d¡U4Ä¥û¥É/*³”ûÕ±š¬=¡‡õ›ôW-×X>°†mÄÖÃ>Ù1Ùñ‰s°óŒ»À½ÐýWO³÷1_¶ïKÿ©ÀuAkpEhmXŸÍXšñzf¯Ì?f±¬O²Çf¿ž34ç`ôÊè?r§Ç¬yF~·üo ÒË Â:µÖ˜•áÑq’R¬ÐJäÙ‹˜iÅÐNJÜ]cÉWÂ&Cë.>`P|À¹Ӯé>pþ53FŒç#ÌïÄ_ª|˜>úý Ô‘Â )z~eôÿ^ ¯…~~õs¾öyz½ó‹W;ÿy­ó¾„¯s>˜ !Pǰ6í02kË^FF`ØQXËu ‹Uµ'`µíIX·¶«í^Ž•[vo`ž%»°¡³ØG°mÃ&Cx¶M±•í(Ý^³lõÇˤ:„g[ûV˜ç»ß_Þüœð –°­ÀégZ'ðÓÏ´ƒùíÏ´UôK—¥=ͲUM_V¼å9Âx¬#Ž®£Q(×cÛŠíyl2ô ù [ › <%<ÖZ›ƒ?‰9x…'1öoaKaÐú'Ñ—'É·]gD´êñ6ÍÊ?ÿ¸ùT†ð8žr`ïÄÖŒm¶·°Id>ö[±¥° 8ÂòÖØ˜ð˜ðh«3Ç9@~M–ccÂCÄ^—ƒ·onsš°y°Íá)78…ÈhlŒ´#H6†×Þ‹Çî% ·×µvïi‚°®M·—;qÿ:4z²ŸÜ†=5ëŽøýëÚ<~Þø;Z.ó¹›[Ëéƒ6g°|4 °”Pa¦pÇr°0öu|ù|á*”Y(§ 3à|åí4ÚÎòf|¯·×`èb\ ø±úrŽ0Xà«ïóÛµÚÓßYÔZTRŽ‚æ-ÁF¸U”ÖòœÈ!Á0¿ë1ðö­nuúÊ w 1r9¬´r‡cŒY"xóø6ÍV¾a€UnŽXrÐF (ó½!\׊ p C„L˜½9ÂÕ`>”µB¶Yîjç´dætî3Ÿú)>ß?ZýÛlöòŽšÐW[„{0÷˜ßÐVÐËLE¤ Œ—ãˆ'¯;…µ8Z‹Q[‹‘Z‹‘Z‹F­öa ®¬Á=¥ÂMd°„lÀ¶Ç­|­('_k^Qù!$ç!€é†B¸M³ó–[Ýó¶`›Õ^^sX¸žŒÂÆÐåÚÁòù‡„³+Ý“ÄXÐ t=ŒeÏÍ¡Á›ü|H ™L–ÝêËiƒ:Gd„»ØëìÈÿëìlbÛ8®<;’EZ,Z1ÁŠ:+Ó$m±´i× c8°–• ÕÁŒíTÝ ´SÉ)DÅh~äÀ@ÀŠ5Óƒ`ÔM´\Á e«0S5@€"5Ñ¢¨z(ʃ{ªƒäPôV¸ßRNšøÒ®ôæÍμofvf¹äÌîÎH%é?ê?IsËÌïVÿ®£?éèß·õý†n¶?ú¢[ÙÇõßIìýWuŸÖ·ô?V ÓÇ×¥õy'l…%Œ^gÿ‡èô·Ñ7ƒÑM]×—P”ý oPV¯ɽ‰u< w<ƒû³1ýý!£äFÿ½ý¡nð=`ômôºÁ˜Ñ7ô8ë f…oëßêU9Åõz™Y¸^ ¶Hü $j1èõ~ Ú{ù½fU¿¯¯«í˜¾Ä·ym)¾Óôß"=‡yòËÁˆÈöêw‚óOŒªÌÑVúAF©«®Y὆Š7”ñb^Ê[èJÇÒ©ôB—sSnÆ]p³¦HÚDåñùe¢Æ·˜ ÜÕœ=ˆ‡Tô¥ ;ãgÿÍ1Éqiu·j}EÜ’õ)܈õIìçÖ7¡/ª#ˆ&yä,r9OO¢¢_C^GÞ@Þ´!e|sÈ®&%ˆoe B” J%ˆ’%$çDÉEˆ"D¢h‰"D¢Q´„”·Q´D"‘‡È["‘‡ÈCä-‘‡ÈCä-áAx„g ƒð „á[‡ð!|KT!ªUˆª%ªUˆ*DÕUˆ*DÕˆ D¢b‰ D¢Q±D¢Q±ÄÿÜ4ú¼Sàþ˜á¶Ïn«Ïª{VÏ«u«ßT5«ßP V¿®.XýšÊX}FÅ­¦©­.+v“éÏr 8‚¼€¼‚\E‘ÛHÈúîàûr_{;ºûCG˜Šq‘É7-†Z!ÝÏü"W{{n÷lZìiõh7;¬ûìu”K‹zÎQgq?CøÁ°¾ }€|pçï€>àmýÔýl̹3æÜsÇœ·ÇœìfýŒÓm¯t®Êð€q Þ#ñÃfÉć¹2]Y¾÷˜ âO^yl«Ý^’Ý{H Y@. d?’BbˆA2ñ1°‚·£“ä*:Œ".’Qƒ<,¤¶†½Ýç,,}Ô§dîª ± îVH£êAâêƒ qÊ0ðµÌƒRÐ|¨®£s—è÷ÚêW¹ÅÞµ€;Ruçù ±u"H|b²}Îsô =ÞÑÇhpÙ?˜ïaöl`v£’A".Öcd#v·SPwÑø-½³S40‡°Þ˜ƒbV ix†zS¶x›ðË~×úlÅ)t;L¿õ©ù‰¹GyÿAÅrzüÅå–|`îÄd²Ù^³šú9ÆYd{Åžï‡ZGû¢o˜…Ø%ói9±eó3³Ç\IÕÃ_¦Ü—l¹àÖõuïQsΤM9u×Ìšïš“æ¨y>Fx`~`V¥˜j†.ýõe“'Áïp±À<£,ñió#㙄9è®Jýª'%kÎäÔªÔï ØÜ¿EýŽÅÈ=0ÏeêÎVo,ôy¨:š  EC;Bß „¶…‘ð&3ì ‡yÛ§›©sT˜¹5[^Rú8Ûzl—µ‡ÎÏ;[?C;øÛ] ÆÑš.†ÿh÷#ŽM2‡XãE5}Êõÿu,ZwzŸý¾¿):éøÓjúø¤ÿdrººÔÏ$§ýPþD¡æ8WfõõëŽ:^¨;÷%èâ°¬GPsÔÅËÜó㳋—gfÔÐà«C‡·|:÷§h‹9Îìl²ÿ•_ó»á,B'RŸ J¨3K;bô–0Ù;ÞÖtWe?Ø>º_Æ 3 ¢cmímMá©Ä*©J¦«¦ª¹‡´¼@ Y¯Ò`ïB—*'g7*oy†Ê–Eò{7x|Äf\7N’³vÀ÷AslpîÏÀo„qŒv›µÉK°­a\ñÒ”K{´sŸ“=ÙÚËRÏ"«öž ç‹=¥þ   endstream endobj 252 0 obj 21000 endobj 253 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 722 /Descent -212 /Flags 32 /FontBBox [-628 -376 2000 1011] /FontName /HKLJAC+Arial-BoldMT /ItalicAngle 0 /StemV 0 /Leading 33 /MaxWidth 2000 /XHeight 525 /FontFile2 251 0 R >> endobj 254 0 obj [ 278 0 0 0 0 0 0 0 333 333 0 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 0 0 0 0 0 0 722 722 722 722 667 611 778 722 278 0 722 611 833 722 778 667 0 722 667 611 722 667 944 0 667 611 0 0 0 0 0 0 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 0 389 556 333 611 556 778 556 556 500 ] endobj 9 0 obj << /Type /Font /Subtype /TrueType /BaseFont /HKLJAC+Arial-BoldMT /FontDescriptor 253 0 R /Widths 254 0 R /FirstChar 32 /LastChar 122 /Encoding /MacRomanEncoding >> endobj 255 0 obj << /Length 256 0 R /Length1 3308 /Filter /FlateDecode >> stream x½–\•ÕÇ?çœçyîu!]µFŒK†æ”)¢‘¥ ˆÆ¢ƒrXŽ@ôÊk¹‰šÈš‘:¥scÌ‘ËVÌÌÑ:c®Y¼lÓ+ÓeÛÜÊ¥3›cΙãeÄÝ÷|¸õG¯í¿½ºçý|ó=Ï÷<ç9@ˆC= ²+ªÊÃðã±tK»¢bŲà’âGä“€N …U•Dî[Ӣŵ¡óí|0ëD raù‚žº àĉ~]¥â^Q‡EÏýšÊªe+ýu*QôJÑý‹«+Ê‘‹«E‹îU•¯ û}êAÑWŠ\R^µð­ä ÃDo=%\]³,š…‡Eï}TxéÂpéÓ·‰þŽ gs*¹ì/ž4à)Zzp7b­^阃.”ª³èP›°A¥c=zÄ׎ì—–…t”™6‰éÃt∞‡ƒ¢…¡¯”ûlÜ.Q­Zëd,G—jF—Ž×ÓÕlÑ­j5ÇHqFK䔘]¨B¦Þ†RçºQÊR£ŸÀr@ Ju®ž=H£Å¹ˆ,wæáyDÐfªôi_)òT¯Œ^‰?á&ë,ÌG“ž/3Ý«Ž¨Ýê˜zWá5µOõ©Cn>¯<ëô¸]Ø­“pv‹ž„鯉ùóEOÁ™¿m!µÉ=¨¶ÈóÊÓ÷ ›ñ¨Ø7»ù2‹ Ó‚t#3ÇÜ"×Ó"–L·Aä}Rý"÷îT­XîÝ'µŸÙ­:iZܵŸº]­¡ê”—Œ)Nªí•b-θ=MýhÐ%rÞv›t›Ôc¨ÛªÔüš`¶[„ n¥2©rŸ'+’â^@‘Ú£Ó0mjûǵq_Ö§uœ7 ܳªGõzã½4Õáöj Auy“1Mõy™j¯7Å‹—j6H÷Ö5®Žâ~LÈöy®c´Â¸` ¢Ó¾¼ ’ýՒ๩éã>¥¾`…‘ÁµÁÎh´°ÄIrçFÜ«"&ÍqÒFžø_Îéã K‚Ê73/6ì̲<1—Hù³fI73/]ÊP³rŸ”èÈìA<åwµƒñÝÇ»38Ú}´{BÂÔ!i©CRCújLRß©þf_|ïù¥Þ;†B»:¬ûœT\†„l¿ùÝ 8¯q£'mÿ³&]—9qø°DoäÕ£ÚÛëV=ó³Uu;ô¥Úöuuíí¶?[GÙþsw_>õ¥üvT¼thã™OîÏRökl¯±ÛúQ«ìÁ9ýÛ?<î_ó‰‡ýÚ9„i—}Ùï¹B›Kc•´$idËsÃ÷žÄ ìb…¡±q< пܒ[ƒ÷°®ì8;+œ¦üWòy’|—ü y‚|›ü3yœü#ùò÷ä[ä1òM<€dÉö&FJž7h{Cd‡)¿£µ¼FùU²›<ˆÒëÊ/“Èýäo9÷ßP~‰ÜGv‘/{É_“Ï“¿"÷à—¸Mæµg@‹¾.ù­ÅÈ—ø9Ä‹§éՋç9ùÊyb³#Ì›µü…»dŸ›œ(vÊW9SâvÊ·Äm\3üœsàYÉèÊ—ÏÖþYyÿVˆfmV³™Úc#XÍŽð DÞÎ>O“ÛÈŸrÌ6Ü Þ§(?IûO˜ý Z¶ÆÆÛ*ç‘ÁVŽgçhðcöÛ‚‘“•1heï1ÛfÊ?$[ì{Œàza3-ß'¿Ç6Ñû]òQæ|„Þ‡qIâbL½É \Óï0f=¹×Jäºè9yÖµ´> endobj 258 0 obj [ 460 ] endobj 64 0 obj << /Type /Font /Subtype /TrueType /BaseFont /RVJBSC+Symbol /FontDescriptor 257 0 R /Widths 258 0 R /FirstChar 165 /LastChar 165 /Encoding /MacRomanEncoding >> endobj 259 0 obj << /Length 260 0 R /Length1 3356 /Filter /FlateDecode >> stream x½•tÖUÇß÷Þï÷y68~‰6‰M¸`Á6 & fNP “1œb0 DÆpM@˜h-Zf„äѧIHdÄ(CQ8RI ÁŒ–ÒŽŽ­÷}ïÑ?:õ_ÇçûÜ×÷óãÞû¹÷s¿÷^I¨„C^IYq90’–F–ëJ–/M]4­* |°=KËç—÷.O¦ÜB[Íü…+KÏ„%³·ŽúžóŠç6—•D`õ/, !éUóõ¥Ôû/([º"1 ¨×ROX¸¸¤÷âFêÛ©GÊŠW”'DÍ#ÔwQO]T\6ïø€Õý©¡Þ·|qÅÒ¶6Q¿H}@ù’yåEOg³nÈ1%$Ñføø_",@š,Í8ÆY­µë1ÓÑ€"s õf36˜L¬G3}Ï¢‡Y†#³ÝNÖiÅ ìà ;G©•"Ë^Ï÷ÜÅZuÖÚ>X†³ ¶«cžÁ6[gVc;çÓ7Èš—Qèö  9vŠ‚¯Øj¦¥Â>‰e&(²ãì”D‹Úà †‡1/"†®Ì^ˆa¼iaï ðœÅ0;sPcçp¤Ì ³×œ2ïØ©xÝ2­æX˜¯§”síæ°{m À^ê)ジ?Ÿz_dpü¾”šÍáQ³ó/à웑…­x‚ö­a>G‘åj‘é8r4áK|2\--9aåCÌþÔðî5uXyˆ¹¢Ïí5õÈqµa•9,ݯnws>Ò¹Aš)ÂZ4…1;ڞƒ¨²WXs΄5v'óÑ=¬³UfNGN0%œŠ a z23i|ÏäŠô /cªÙo3‘ìvšÝç&|Å^°I‘‰˜^2ͦ%2$’nêà T™†È0Œ6­‘s ’éÊlV1VU¯nÇÚ„³ƒS“c6ýö¹±¼; SÌHËüjjr45†‚X—•©ûÚÛ ƒ”pF,¼!æÒbAz¿³ÿËy6sð¤‚ÂÔ}&:a|¼Û ³ÇÓ8­ø÷f†›0>“é‚EiÛ– 4|Š;1ŠÞy‰"&!´†4žnÌBòÉÆ“C{tKë–žÖ-­4@k…Ki=ß¶%Úµåý%‘ŒŽ> ‘$¹Ýì£Fæõ‘˜ŸX˜è:%Dà “h_ŽØèÜ%0¦sbTtdçäËç²³s»çfÍbŒÖ“çfõÎ:+=mXš {™a¦WÔ:Ùí¹šg¶NqõéLÍÛ›LFµÉؤ½ÆýÖÔ¥òÝû¯õ1 ~x騯¦OÞ…ñYqÇ÷¦ß¥ë®ÖqÏNoÛýÑé„5ŸxÔž°Á1”sQHùFî)µaŽ¾Æ’Âb‘Œ<æ ыܻ»Þ {¼Ÿº÷Œ›tûƒ¦­,›³x¡Ï¯~mÏøóã¿üâþ±í¶Ò„hƒ3èðe+ÙˆðD»Ø&^[ÅÄÅñ_âññ²øOñ}ñâ{âßÅfñoâ%ñ¯µÃ»’›psr‡ú±xÙá‚俈çÅsâ;âŸÅ³âñâiñmñ÷âïÄߊ§Ä·ðMôa´·ÐqÞ”íMÊÞ|£,\¯Ø×©VoñZ±WœÉÌWOtcÄo»ï IÔz(b÷¸ÏÛœdÇ{Ë×Kæp„~wu•¥‹˜$v;‰‰b‚Å0Öà52”Ew5ß§ÕÞò7cÀ{Ð%³9ûÙ´Vò>|œe;KŒå KÆL®®1ƒ>•>•(ÿ× }þ ˜*Ä] endstream endobj 260 0 obj 1858 endobj 261 0 obj << /Type /FontDescriptor /Ascent 701 /CapHeight 623 /Descent -299 /Flags 4 /FontBBox [-167 -299 1063 827] /FontName /PWBKIQ+Symbol /ItalicAngle 0 /StemV 103 /MaxWidth 1042 /StemH 38 /XHeight 467 /FontFile2 259 0 R >> endobj 262 0 obj [ 549 ] endobj 263 0 obj << /Length 264 0 R /Filter /FlateDecode >> stream x]½nÄ „{žbËKq¶¢T)ºè$ùQœ<†µ…/h ¿}€8)ÅÌÌÃÊKÿÔ“O ß8ØLžã6¶#ΞDÛó6§ªÙÅD!3<ìkÂ¥§)€R@¾gdM¼ÃéÑ…ïŠöÊÙÓ §ÏËP•a‹ñ ¤ÐNùºg_Ì‚ +zî]ö}ÚÏ™úK|ì!7ÊDûSɇk4ÙÐŒB5V׫HîŸuãt$»V«:ÝÃ}Íÿ:-_¼U²snS÷P‹–žð¶ªby°Î7qÒp endstream endobj 264 0 obj 224 endobj 65 0 obj << /Type /Font /Subtype /TrueType /BaseFont /PWBKIQ+Symbol /FontDescriptor 261 0 R /Widths 262 0 R /FirstChar 33 /LastChar 33 /ToUnicode 263 0 R >> endobj 265 0 obj << /Length 266 0 R /Length1 44184 /Filter /FlateDecode >> stream xœ½€E¾?^UÝ“SOOìɱg'íl˜ÙĆ^–ŒÈeY…=9ó˜@ÖgVTP1ŸºDPä<ïÔSÏN½'úÓa:äôdg~ßê™ïÞ{ÿÿîNuUu˜îªoü|¿Õ‹0BHV!Igœ=où^V =/!„ù3Î[xظÀ õý©®Y¸|ÑÙÏLÿ!ÍR„K-½páÂÏcy„Œï"´N:kÁ¼3¿¿j¾¡»œpº³ CûùÚÐŽœuöŠ ^Ú?ÚípÍUK—1oA^@hãaØÿÌÙó.XΞ«Ê ´iÚsæ½à¶?V‚6\Å–/;w…úvÍ„î†&úxù/,rÓ:?´¡O»ú0üÒ¸¬”kÿc Ã*”*µF«ÓŒ&ÎÌ[¬6»Ã)¸Ü¯Ï†Â‘¨«ˆ'’©te¦ªº¦6›««ohlÑÜÒÚ&¡ö‘£F;nü„‰§L:ur甩ӦϘyÚ¬®Ùݧÿßüÿogš3÷ÿۙ짱×#7l}Ì|äC¨ó!>ÎÿöÁþüP¡@ÞëN/}Š_1Ú7Á~ñ¤â‰ÞDg£ß¢[ ¯ÿ=„$d‚}o"†~jF7 óÑ[hFá[è ¢{Ñ×(…ÑY…<2£•(îÅ8«½ õ¤™I²_¼%pó¾ ¥á*ÓÑÍÈ^+& Zho#^Ò gMG/2sÕ©BUá;¼}¡0݃›ÉÛìc@±‡pˆEùË W6î@Ft˜ñý¡P]8ΚzѯÐ%p«Ð]èeÜEZÈÓ…µpO³àV¢'Ћ8É"¶ñh*}ºíB{Ñ+è]ô ÆØ„+ð*ü~S†žÍ?[_˜_X†F£SQ'Z{½8ŠÛÉlf6ó(óÎÐç÷|píéèfˆ–L'3˜G‘µ Ùh>Œæ pO¡ЇX³¸ Kx5þ9e†ž¾d‘ Fp\íL8vŒé}èqô,z½×üÆ”ÁNâøtük|%¾߈ïÿÃá/‰‚¼Ë0Ì¥ìŸØ/óo´…Û Á÷º‘Pf¦óù2úž/S¸ ¿N’$Å`V?”ÏׯVþXx…Q ŽmA£à™'¡Óà®/D—£=èOpîËè/è ú'Œƒµ˜‡±à0žŠ§á_Á]<Š¿ÆCÄó×@–’­äM&ɼ̞Æ>6´=oËoÍ/) þPxIžß:øž˜´+ÏØøž?¢èsô=|‡ûá^Çá‰ð¼·Âõ?ÄGœÔä7äw¤À´0ë™X½5jþìü­ùm…laЃH@YømjšºàÚ—ÁhÞ‹†™ÙÔó6ú ;±Wáñx&ž…{ñYx^ŽûðÅøÕ‡ðv¼¿ßÇ_–(‰ Æ)IÎ —‘Èvò,y›`3™Åô1370Û™W™ÏXŽM±Uì$¶—½½HŒÒ®~é¨ãèÙCó‡núC¾2?*ÿ‹üÕùßçßÎ\О.|‚”¨ î± -‚{ü5ÐæjtÚôñ0ÜãGèSô%Ìùw0 Ö`ܱ_ž·¸ïIpç§á.¼~ÏÂK`üWáGðVü$Þ‡_À/â×ñøk‚áî+áwpÁ ²žávò …ßïɌȤ˜¦–iezáiÖ0WÁóÜÂ|À|ÂÖÆV³ÓØ•ìs Fq¦âfÅųŠç_(9e7Phñ÷żD~϶2KÑ&ÔIæ ò:iÆ¿&?áˆÿ¾ÍËt2¤ƒŒ@ï*?YU”AeX§ê¥—"·‘4s+2z´ø ‘Ùd5éE÷ã'ÑOdPÚyÌËd™Ël`¯g[ñ;h%|'"|µ£vÜ s÷êƒJ3³¡WT¨™£Š³‰¡°†ýTA˜×A¶€.ø3žáNb‡ÑA®CahsølÇþ(> 5°û™kÈò>ô-E7àßÃ3îAKÉ|ÌKðã/q'¾ƒ©F¿Á}0"h ¹…Èrzžþ/Ã6àÜŸ`n"d!b9½Iº`Ö_Å<©Ä¿:=]ûQ á}è%ò[T‡0{ C=„·0ãÐüûûaáJ¿‡Ñ¬é!…Ü 2bpfj‚¤€þ{@ž‚Ìä{| YŠã[™Ïñ}¤MF ˜sÉ|sþ{¶©…Û Ò¤CÙ¨FŠf…—ÍÂŒŠZ!¤<‹ýPq­3o0‡ ]…`~®Â˜ÿ]£3¤ÛÕÀKãÐ{ØŽçà)lLd …™èò8ûAÁõ8ˆ^+‡åwàf)p_A‡§…ÏQ>4t{5{%û+öÐO?Ô\®G·£g@›l½ƒq<Fót=‹AGT¡”ƒ§kE#A*‡}h&ÈÓ^’ Ñ9¨$ïèwh h¨‰0s༅h ôŸ êbôàÿ5è7£ûÑkäa²‘ ’«ÈÉyd1z½Ç<ÇHx&z“]Ë®DÓPMÁøæz˜%?œwMá ø¶8rƒôÏ—å¾,¼]xpè¸Þýpï×+G¢/•¨MÆGXVHíÓ¥¶Ö–æM õ¹lmMuU¦2J&â11 ‡‚¿Ïëq»§Ãn³Zx3g2ô:­F­R*X†`”Ó{X1k€™×=`NÂ÷Žp\tÀy¼ ç;f­9q¯›éí\ ÷÷¯ lš2ë„sÝAz…®.¸œK¢czûÇÀW_35qZ¾\Ù5k_ _ OBŸªø| £iOï’À€&<2|Vÿ’^˜Wÿšzap«Ë%í*ìG®Ñþé³ÂÁ6w¸kÞ(Ï+êŸzá6A Ã÷¤S[8sq`·M¥ŠÞpbe zqŸ\“§µ‰S,¦w? E€;™†gj Å‚ÔFLüta8kàL˜‘ÅšŽÞ~®‰öÃ#âE” ú¿G@áCÞ3¯Ô£Œrß#º“ÒÉ1RÀóÊõdr ‘ $¢ê€9…{l•Û¹tê¼A²8¼œ À†uÂØÎëjÊÀðƒt‚¯”Ð|h ¬š2«Ø ùî­HÊ$»H/ݳ¯¼Ç6ƒîYUÞsìôÞ0Pòv0"² ¨Åc&În}VÓ¶ÿ»÷Oœž8eö¬ÀèþÞÕNœ>¬UÜOÆ ö•j–ŽYŒ›@­7#ï¢<}ö±C 1K?ÀFáO)õ™ƒ*5P¥Üƒc¸ÞqŲK –xæ;i°ð =KÞ?­ôMÉÒo{`İö°ÛÓ÷3§ƒÈ!§Ïîï×Û7„Yÿ˜p`Loÿ¼Áªùáî߈ؿ|4ˆ¡âŒv_ísM<ÊY¸ è– ‘[Âøª)[$|Õ´Ù³vq®š>k+˜6½#»ºÒ`K‚¾b)è>³E©Äúí B,­0H«T@e'×FEûvb$¨'_ìLžÊnž4Ô|*w¤y7Ԍښ‡šé§ºªÖ4Gƒæà" 0ûŽJ ô °û€4þ²ð1ùX¡»ß&K¦·uŸèˆZ¥E¶¬pÁ`~ÚüŠùCó×f…y7¶#BönSƒ¥5H6ï¨R/Sõ“ä6ð¾ÅÅç8|ˆ‚§9|F¯™k†…Ç–žâx¾kŒ2 %^$W®€‚}=ïý~,na.÷~Ú#ÑX4O”j;-žÖîÇ­»w«Æ¢qbt|»TŸJ×u—,¹ñ;«GT<1™ Öð^¥{Â`aßÖPNÞDН¼ÙéÈMØé08¶(IÇôYn©E± Ú$Å7¥›"¸{"ê'u×·u7´ÖgÇe'fym·YÓmnå¥ÄÍY^ åxÉÞSn.ùAÒ)¥'UvçÒÝuÑîñ‘î ¹Öºìølç|´Ü ¤C»P&à Ñ#†šé'—r߉0!=øÑ©‰‰¹œ%\ªÕ[ФU[{¼RWW –‰Š©µ«QZT†ƒapüÝ>—Ë÷@HÑ,÷ˆ“ãxœöîàïð»\þ{‚¥Î§ä£\8¸îÍÿã)Ë^r ÿƒÃbqäS>'kr™ð_Ž×h?VËå›^åœ\¾«Égý4{ ÐÎ|¦˜èÄ©&ÒsmÂ"„/Œ<Þw)g„®í툰³Ô%lGówN>uÒ)'¨ÆOm$/nmøhÌ îÞZS“hדKQ%²‘€GþoðÆ=¸{»úiø²½rÓ |Ì#œb6óƒØ°-V³(1Hþ²sÄø–˜kf¶] tÀg\c*\£©É¥[ý-SŸ"ËÀ0½€*'>óJàþ!àšÃ‡à™ìõf:ß(s¸§ùLìáCÚsø0:H†Ž ¥æ±Ó½ñht¢èî—P°ŠS"ã%<6…ošGÂÓQ`Jv…:'O'I8>eÒ¸Š1Š'¸æ$0ç±"‰)Õ$“—^Š'è¦Mˆ‚ÒÚ>Ö7Ñ:z/˜ÁÂß‘>¾ÂßäŸ.Ô׃’¸$“lv»è(‚’H+ø ‡”6«ÃÎ:ì”NTŹœ³Ê-'Ÿê°ÃîÚšº\V>È$Ú:©£f5c§ÙâtZ8;«l˜º¬ï†öj<¢¡sòÖèþÛ«ìÞé°ð Qn¼êÚ{š¥Äêg&ž²™}l ¥·%J…àtù~ísÙ„ŒXwÅ´i©ÐWø‚¿|ÔüOzÄ?µ³S“Ë·Ùç6hü¹e«»FLw›±¹yÅÒ‡ÁUÙókeB:@¤’`ÿwà‘ÒÄç¬ á_¨‹æ:˜ðšch¡¦Âçœä&ápŽ Mâ„W"̈ú\¡½>c«g ¦z¾Þ$Tà땃øS‰åkUV´6Ô›R8Uh­¯$ÿxb”etg<âL‚:ÀÜ‘CC € ä âÀ‡oÌôAÐô…£±DÓÓØÑœm­@Mu Xª‚ÚÈJ¨qj¾µú le¡f'Pk©Që¡h«n¯@i(Ì*S6è °(lÈ¡@EêH–ɤD(ö¡Hš‘ž&Ýcô4·k  Ä€T$Ørð±IJJ5X¦"ᨲE2¡$ PPÊÉeëëê£EE§R2ÿáXrð–ÅKn¾yÉ’››Ï2å\úÁ§=bTéÌ*Ïhj-Tü·,Y| tKKù 懥·Þºté-·,¶bÅ4ø¼:Äòz­V©,móÜÒ[ný=hú¹+¦Mý Õ ƒò…™µSŠÅõ Ž(F‹–·+• Îa·ØZ-ŠIe“1‚„#xþ¼+@È ò¬ôL’;|˜•ÀºÔУf^Îò|}™ì‰ÍÊS†<&‘ô4?ÓyAuΜ9ç¨Þ¨>(áïÎÅO ëœf­þÏùÁÍ÷å_ÐàêBxB^ƒtþ(YYºß¸†h\ \,½g ¯tØ9…îW«…Û†;6ñJË»y7žT¾ã#ôŽÕÈ7<ì~­„¨Ê Ëç²Dݵ5;o'+ÿíý~{n¾4¤à~_Àã6߇Çýî¬ÓüNù~ÑÇDÅü7Üog¥Ë­.,y¾wýQtk,«¬Œßíœa‘^Ù–³#/9þÁvQ§!Uh­Fµ5ȧ1“VÁFQ:‰F¬Ñh$‘œpÄã¶zZâ_ipÓ`³EË9Í®ü×q‚Ùn{è!›Ìå¿ÌN“άÃ~ª+óu)³É®îúÌaàïçŸ{Þàøl¶Úf2 rÀ C¬£ƒma'!uKÕB·âsjë\7û€ÚԭѨ= nµ£Ûlvvsœw«TêªôX/Ô—‚²Ë`]¥¢ƒ?8̰†Ps…£\j.jsPv€jƒåÇ-Rò[ú`ùk¨Á+ä‡\AëÄŸ?]îÛ,“÷Èbf­Sy‘Ì¿I^Õ j%ç3èu´}&ßNÿƒü½nRùUDõ$¾iÑÙØ[tu¨ÜÈÐ;êÁA\bÜÍùwÜ¢fpåл5aAKC’h7Q±²|R—¤Gû€ÓD`©Ž?4ûA”™D/d æXËÑÈÊ .€{z¹ð1ƒó7 øf[Õ:ö]`<{öÁ÷ËR^[ÑááŒHCç”zZ|;¹¡éTúï?X8ùBq6ˆ¥³¥&ÆŽ Ó€5cðxM·æšóðšµêµš›ñmšûðCšh'~¿ yÄŸkŽà4ëñó;]+êÖ â­pSÝê§2 fÞ1â=[ž”—!Ð=¥qéëé¸MQ­ã¢heön3KKîÕYfAù׬¨`ÒÛ:Œ‚IBèxîÏÀŸ€"ÝÆmxwá;ÄoM«ã``|‡* ‡Q¬ðOd‡­ðϣƨ6’Ý…Àæøn«×˜¦g$ ßIá¸ÂcôCüÙjŸ‡G•8¦0„ÂÆ` ŸjQð …ÁÕ¾ìK;«#-F¡êîݲMž*)Q°¸À£¦8È>`S3-ŠÆ÷lRɉNÁ!Ø›`JÛëö¹ýnV+ĸ˜Y¥N¯Õkôj½J¯P2bÈ‘PÀâ’pR•PšÍH8l JØ-@!êSª$PP[«hŽ'à'y)*ÛO¸\‘·þH6³Ï"´Y}fG›™vŸo ~’$¨Ä¬3n Á…ÃØ¦EÌj7@ Æ Ç1>^×–ÖBa§5¯UÒ‹ü]r@ÅduøéYþ6¢åÌ­Z”œ…²Q(oémwa'Kš˜ö|.ÇÉ* L|‡L½¢W@ÀG€¶l>Ç|vé‚Û'\^émr@mâe•¾Qœ}zGB¨h{í¦Ž¤³¢qÜ5›Èû¯æ¿½ë’¹àõ-3Ï}s´º¾yæÊó_n áüþ}»ÎÿKKHˆàà>YÿCÔAîÐÕ[¶òj÷`áÉdV"µÆ-¹;ùN7«1í&!=Þ i8½ÞÄíÕ¨ íQ@ ‚÷ªKp¼Šw[wCœÜL=µ^ Ö=à!š‘ƒüEÒ¢Ef3^h÷YÖ»!<îÄ@CÕi–Ý9Y*Úé蘭õýág‡5ª«PZl.‰²cî¸mLÖã•\CKeYÈeÕ˜­Z`øétj9y‹ƒ­šI¥µA­¡vÄ9óðSŠ`Çn]RÁ‚Ù þî­ÞÚR€$i*j÷.à«ÿ’RîHvœé"ãêØêŠÕñû+îïÑoOh ¼ÖžÓ7$Øx8áKZc¾Š0Ød”Z _ð‡ìÿâ‡ìl…º<–— Œë»áöKN»>Å™ufçiÞ¾ñ¥ùä‘ìŽù·|zzÇsNÓ¹O,Ÿ¸~å'Üß=g}sÖªqp-3ž¾búÍ0a"òÀS`«z!×å5Éz=@|A…×ï±ÃÀÜéõîµ›l õJ¼Ñ¸×¬f†ýFë~‚ ¤ ÏàƒúVHZ$ ·¼Ê vd‚>»$—K&¬0.òzýÈäÃÀ¾ÝäÄÝ’ø !–µéAk½®_iBú&ÛGÀ¡fŽZP`H5s_É6• Ê5›`j‚9é@sß¿Ù\Þšª«úp0‡kÍe»¢\)‰£Z³9Œfè üÆãc¨q8F.óÏÓú©üixî<&vô%:zùïË2 Ï%eyô,¥u»ú/)¤skqwáÙ?–Œ9v„ºƒ¨žÁÎV+£0ÄÛ`„¥-(h‡³‘Á›’–Ê8;¢6 ™+Y–U[Y«ZdEuÂÒd™hé¶,±\h¹ÊredeGä=Ý{ü—‹+Ôª€RL‘@4¸ pFðÂà…çf–Wm íI¼­ÿX{PÏÏVƒÉw‹ÕoóÙ½sB(bÐGu¢WeHe ´I\•L(J£!R |rߎt Ãh܃ø¿$»¿Åªˆµh Δ-(Á%‰ª›xм YAzrÿ¡–*pÛ„ê=¸_Z”\²£AuÈPìüƒ"¡³}€Î³ì_É~€¬žh*d-œÉlâMŒRoЈ2Å&$°„ñï$µ`×E#jèL*ÒšütG 1ÀZUP”Œ:\“ÉD‚J·>YÕSÓ©¤‚’e ™Ê0J+ ~¨C]ÂÂ!DñÔ’è£Äƒ—žz߂կî}àì§ê:Úª6½uÉô§Ýlàã-È?-ˆ÷.[¾qÓ‚y³›‰åÜs>Ü|ó«¯~ôõ»®Z¼qAÈ$ð­5¿åÓàk;ïxüšË7­ì„7 yæmàKZµEÃPcH â+A”J†ìÕè †E6dµÙ ‚Dz‡Î¦G ‡É"Ölâ´,§×í^ÄäÁí`-AámÙ”>0 ljISá²Ç!óe'po‹ÔIúç‚eÄ8h I–ôõÌ¥C÷SyÂ0ùÇÔv#ïT²KE™16®þéy—ÙÉiyÄŸ‚ÿð©ì?DQ5^#âý}…¾Ò³.ÖkK¦OK. ‘uºVg¿óF|»úvÝ ±É;Òá{c;ÈÓÚÝúÝÉ—µNZ.Ä÷Iµ5 ÎVOØ7XøÛÖªpåîÂßÀñøa»Y]Q\ò·­‰ŠÐn@=£…/¶ÆBAjñÉ In‰Ç•Þ‹"Ó¢4„ñ_%.·sb ó‘«¥Í>ÙNìƒø¤« ´p¥Z4BÍI. ÐhF©‘&Ó©,…ªÒÕn¿Ùƪ}|@B+H¢JøU P¦~3È$· Š´:#¡jp6Ž;T½þÜ«@=¸§õ_± % ŸmÏä³mà0ЭTþ‚ -…j˜Ö0­)œV}›Í ‡ÛhŸöÙhÈÀã?]Ç´8HÁú²0êV)ëO€-'ÔËâ_ìß´iÿ/–œžhzëæ[ÞlŠîþÕŠ»7žwþFÇïV­úÝ£+W>J®®} ÷¦÷Þ»iîÙ\ã”ùý¯¼Ò?¿³éó¥îX2ÿ†òªe›7Ÿó˹h¹èºˆ¢Z Ñ:•šM¨’¨òáÈîˆR¤B2œ‚Âè„Â`ôÕdõ!(j쵩XÊFí1Swõ'üá$W* TM¥$=kNºæÿ T㔆³”ÖÕÏV¿QÍÎQ"H4êcº M&à.3i–çU¶šOÐz(—ÇP Q1V_Ç<ݾmÎi÷ö>½é—Of;ÅNÿÍU³]N³Þ«} ×Xsw.þÅ=÷,qnmüéÜgþ~ÉíC×­yô“­çuÞœi råÐYpí§‰w_¼aûµk·IRæYÆM˜ù€›¸Qµ¤1mµëÔ[‘’ßñk±Ø¾C§Ïq ¢ýEoBFËfl\Z†µÈqpEFXä‚™ßY?âTúZw q¡z¸¡€ÌPzN4K2ï3¾j|—ÛoüœûÆø#§r€¡òð¶*6À/iàMÿà~og±]2è²Æ×MvlO@ÊÒ}"Ñ`p;UùÃÃn ª$,ÉôhÃäÎz(¦œŽÕùOl€è”'ÜìKùªï³é-ÔGœÚÙFÀîÌèrÉ·E‰ÕMa+ÒhÍ ˆ 3Ô!Y@Ýém#†·kðÃH ¹˜zr3øÆ 5~r«r$EÃ5M¾ba0L… vÊJ÷À¡Cð‡„ÃÎCÅuQÀÖ)«ЧYpÑ‚Wa\Òó™kì:³:z„Ñ„³ÎNÆâ–,XòùN Tf»|îgC¾(ûú­ªÜÍ’Û1é——Ìcø3ñ¾gxÁSJúèc™FK2`7Ëq€²(ŠLÙ —[lõQÙófn¤öØ uòµ¿è'ÿe`£a\!ëj)ô´ò5ýwzfØxž8ÎÝ£ÁØÂ·ë`lé¸Òñ--¨ÖG;Ü•Qb% mçÅ0´Á‡vèÀÿahr¸d²¿8ºB[F{ôŸ¥±Í+å±ÅxàØØRùÉ|ÉB¾ªF{¥öQj|§ñ.317j7˜˜&¾,ü¸‘M\f:ÁÄç]&{tEJgßdJ™|ñ±lʇ»;Á.W©+ñÉ’¹B­®©Õ§‚®ZK·S¨¹—‚ç—ñMYÎQÕTßÌaNë0ÙÚ¨Blä{JAOŸ;`0{DÎËÇà·Ëè‡à¦ÙmŠaC™Ô§¤ð±¶¤:®—­ B%ÿ„ž"K ç,r# +Î÷;Ó÷^6òWÓ©rGå©§>ðâ…ù—¦§ZÚÓ!熔*®2%=jùF_|¦Ü­·<Ûœßæ§žƒÞ)ü03dÖÅ=»µÜ·ÍãËò4)DÃû² Š6hmƒ-M¡[§¼uf;èQޙݭ~R"<—1„9æ,uÀ‹[ê±QÒÖÕÕ´`vlë(ï £4BuÊøAk·¶…F4ÌljÔ(A«Œ¤]à±ÆÖ:ðFÇH»¶5Wi„c6nmOAâTFÒÙm‘´=’î„øô“øSHò}ކ´(¸n% 1C‡{†¨ãž&w˜ÒÚ`Ž(svf‰o”L®õÈŠij[G4«p&’ñdE2–“†²òV³•³²ÊŒXmÓ@ÚµÏE¦*Û\d¨ÑUàôIêÖ ìH s‘µÒ8ë³Æ ¼Mƒ÷ã˦Ÿ²PÁÊ2Í ùlO³×£À×;¥dÆÔÝšªOåÇ»NII iŸëš›êLÿ0%Q"‘ªÄ„¤µdiIvÃ:ÃFùЀ q³ÁÀ™½Z3ŽÓ]FQ¬Mˆb<á 'R€ K©¬• ~¯†¤‹Üe·Ïäív !sÈC»Æù‘•½ŸyÕýq·ßèwÈír¥ ŸÛe…8o6ûHðŽt$†9§*ÿ'*CÚñ¹?Ð-~ßÞN+ùç(زé†Ïüá5ù—Ë óU;?=Snã5䌡;ª‚•;ˆíò@>} ^(=õxâ‘䟴Ô½£U¬Kô'ï lˆnL>U^Y=7ù«ô:í:ëÕ‘uQõ n·R»œ[n^Î/·¨&&ÇG&&W5¦¦`S´-1"9Ú4–Sk2BÀtGÝ w&lJ$ÕrOFžË0cã£çVú«n ÜØP§Ô“%òÚ‰Z‘ÄØ«® ™p…±&óÆE{LTû¼¾êš»šØÕá¨Iï×gômúÉú¹úeNÄ—Kñtû“y½yŸùUó~ó7f¥Ù•UPFÃïßP¤vÂ…Eš ÆG_)»³GȨG Ô ƒ>41¸JF*Ë@Xy+K-_$Å[µ:‹˜Œ&¬é4ŽjÃiœâãiщi\„-eà’j—¾>}õôDÍåI¦¶ƒª$RJm ‚ü‘•ä•ÖÈ51ĨÎ/áîüã}—_Ôyß¼!9ùGŸ;¹eÔçç·á‡¦\ÐÚu×Õù×§§{ÇE·ÏÍÜ1gúÕó)öOêž%õ“¯¡®ÛÑ[â8+w¥ç¶Œ¶¶Úp‡`1•ÚhwÔ×Â> ž¼.¼=ÉGëuW³¾h²žaIRcÕ½AQt5¹ESµ¿:SÝVÍV kN˜„IršÛÐ~9¶Fmï"æ"‡×Šh1x2`Þ¢b2Z’Ñv!xà`…R7rÑv@TÉã´Óx ütQ×8½ŒÞË!âbÀ‰Ê{•¢NHö9dݹJ²ÐxÐÌ»E‹Íì Š™+nœ7SGÆ<˜Û¾ô‘N³·'§¾¼¸{θ9kk®ütÍ«¬¢Ïý.§{z{WÒŸ>uî˜Y7<™ÿrÎ\›ÝìÈœÞv{ä·§=r f®›ô.à½ó€÷¼ êôRð:íZÝUüZËZë5¶uþuþ൱þøº„ô\,÷€³½_ÒÜÛ$j‡—Ê[+Ž\./ò:Ô„¶sЏWñªÍ•&¿Ïn÷úê¤O£!>5‰ˆ&p*L1¹*S>ÀlC2JznÄê2&©…ÅTg`˜ m— Š€ ‹J kȼ $´6£É`Ò›t&V)FcÑŠh<Ê*!±‚'Ê`4¡Tâ€-\‰£¦d%ñþJêER ÍRаùƒ†iú €¢eI(sõcà5±WÎ ÂÏ.y¼rrÜûË+ϸ,ßL'b®^²«GˆŒŒ\3%ÿJ‰)f5Ì]2iñŠK¿›=’rEÿïçÜzjKWgj<ðÃ,˜ ÌGó’k®™r¥’1ëŒIž÷êB.öz’Z[&_ÝJ)“ЦœIˆWcu9’‹×•­¤Nª“¹œ·2–¦Ø#I$EÑ›ðo©Ôì"XÔ…#¢+˜´!‹èÔ!ÑäÁ_{ âi‡EZܩ٤yU³_óF¡É‰b%Jsi’h‡ÔPšš©– ÿ5ÿ uWê&,“ãþ0s‡h>/H4*¼zúÀì+I3ÙƒþÖ$ûûž7›UJP¿ÜŸL–w¼) !€„àc‰‰Ê µÇ¤X)ó"«|L1#‡gÕ”‡ŽÎ£3Ò'Ë0æ\Ú3t?–‘ij=“\Þ/ë±üv:)Eñ•ÿžòr~â\ºÍEKºÖÃÚGÄ®€yÊ¢o¥9½ lÒè“çÕݾ\(äuצMUþ*R•Ìf½iP$uT‘ð‚-i6{1…â\œÄ“Ѩ7 ‹B0Z!æE#:MGE”âR)b`­R R…¹ˆÜ7étor¿*[" ÷Ts€Ãˆ[Å­ç¾áXNÈÙE9‰:)²Rᇗg ÒîÚ IÍ`jýlüËšeøüœ0 QR ”™¡8 `±ÿ‡Y¸£λ˳`Òù˜;éÐýzø4”-y Úÿ0 ­Ýq%èŽf,J ×n ׯMæ˜ñú1‘™ºýÌÈýºû#O*wë5lØõ±°©‹(ëPãzÔØˆ¼u¹ Yµ¦\SWYS“©ôæ´jŒK[°Ïá•®Kø½t7‹u©»0I0Á¨‘p±°Z-$e5¾…••iÆÈÕMj?äñ -k–×î²b‘n@Ì ÜY¢Q{ïÀqS äȺžZÑ'ȶžb0^N‚üžƒò º§Ör€ÖQ@ÖI @¾8|* _ìˆØCöpY ¢‰’󡺥’”T‹#[ ½K*Èh8KµQ™Å2¶ d3½OÏ]óòu“×~u͋ר(Âíäͬ|íâ{¦ÔaôÑ)—Vœ* øgÅ[ó·æê:×o]{{?Vô/«¶B>ó^¿àðÎXºàºžón{íH ×:±Ãb°«d¾úðÕ2à«üŒ¤çï¶?–Ùf:ÃÝ!Yò\:oF΋½É ×z])Ha….ÈËÉÄk3™šZoªy$íâLm Ò–ìhkÙám.ú:e²äJ =^ò#’Qù:àšW$#ш79"G»: rÖÌ64ä²ÞáÂ{€‰‹©T2 º¢b2YôšGŒÐ‚SQë‹d}‘ÉãÏnìx¼ƒ¬ëø°ƒt ’=’{4ï ;*"‘õ„™L^%ÄDæ’e}íŽBðÛÔ‹= ØœiŒœ¤-ø * ÞüBI‰ˆJÚr’ì÷%bnH™=©uRRÇÏIìßž5üŠ4…d€ Åg($`²¶Ù%(2 Œž0Z up»pðgáÙ’ÑYaéÒÉ=¥]eÙÂ\>ô†,²óÈÒ7K‹eBÒË!`îÿ‘öd‹ŽüËI]Þ7LˆÈˆWþ¼½hK.°—÷ƒ,ÿœÏæüè)a+a}À°lOÆ×ª¨ÕWY«lmž6ߩнd•l=“½“}6‰’…N_''éx5‚_n{êÇãG^¡hé@öÓ”¯Ú +q6K1[Í^§_xQp"ªM"ä RGÔ<ru„À5–µ©¼Ô‹N6uä¥\è“ósN˜Ê“&Yž­“$6(ÉaÄ0ùíI™;ûéGd[VQ+ÒA¤:ð¿Ã¸‰è;é’ß0+-Y×’k˜u–~ëvµ†è¬:s;¹Sõ°êSîë'v%Ë-ävr;­lZ „sàd¿ç}§ÓëW™xŽ ø òÔé ÂT2˜ÛËeö#ÈêáZaZhô©Õ*ºCEw¬°NˆÝ¹ ¿URx`ÆQ)KƒÔ·– ò`ð€²•;)Ÿ@6Cœ(mv+”f… ä®I©²0Ž4æ” …í„O DÊzIj!R' ”`ÑŽ/ex24úI}¦“F–yüÀ]sŸXJÕžtË„©ãë»óOP‚% é³­C®ÛöŸv®£­üwcÇVø®›B–éT–‰] oƒqIΔzœig«=R›«­¯ìn_\Ú~~ð’ö~©¿ý6iCûãí{Ú_¬µ˜P]íèÚÓ²¬)”¬“mÏͬ~¶íÒ¾vµ;ä®^Z\}cöñôCuŸ…~LÿX§­‰Pu™ž“ÃèÙk‹<µ é€WHTQ¶ÒëÓ¤*ÓéõÕétUµ7Q ë‡è.#$©)j‡¼Œž"ÁÇÃôNÑ$úÅ*XN™ Q㇂íY©ŽmªF<òCÖ`0„‚Õ!6€«ÄDXLÄãBu(Ž–q’†z±µ­M­æDN@˜ê¢íÁ SS3ˆg=9²kvãáõI©³º·zy5á–êÎjfõ7`·×ït'q‹:É<* œYrßPæë˜°V'•ÂÃ%ǼR¢»†œÐìƒÅ E°–.Pé ©Ò.jYÑð±ì¦Ð*ý~¥¾#ä²Ð|HI¾f gO=B Ž4ÖŠ69ˆ•ìZ£øu1ÚyÂ’‡ õü®§rù„C3=}ÃŽ=Yl«Œø“ìv¡Paÿ6!’¥þÜVØÂ-tQA!;ªÿIh—2ÿÂæ2>T–â>©Ü"b ÏÜ@i<¿I–Ó²øÆ½´gu:Ýþ“îåk]Õé]L{î¹bûüç|Y.5C?ÁZ9ô“?#ñ›íßЃðâW`å•C«€?fÑR©œ¥u–¨!òÅ ÎÒû%ß(G}#"êÜÔÝ1i°Æ6µÏ ¡û.”ÅmÑë<Ø6r1ì&'É–ð1¸lÃhò*eµ—•UY™•F ¿E*å<¼^ú¸Ï?OKü -©PŧRe•ŸsÜÝ+†ˆb÷Áó4˜Ôô±÷ ŒAöÁ»uÞÀïz^óAGð¯6ŠbÞ˜Olë9Íó o—ïMô&,>üæ5Ìòa½¬l,©»íw;n1™x‹Wï—Í…:C$C¡¨èõgdGWS[i}uÞŒNAU§®eÕjëÕ¹m´mÙèÄ&§ßIœq«Ói³zÝ•EžOvÂ{gâ±d²"æ­,\-y¼<^¯+¦¥¯!€Í¬Ðüê•t¾(¬¦õù<^Â`^i¬€h¨'ŒMt“ÊL¬NÌdt:=kõj1ÖÐàõù¼õu>ÈVyûcscËbÇžŽ)b°Ö%“øœ)¶.öjlìè$I6¯ÏÅd~¢`Öãa a®¸P²[ ¬œóM¶¼bùÐòµ…µÏ”|×I”å5æÆLñ¯§ÖBÀÂÖ>'wÐîR±—ºQ²ÿ$;³ÍP§4¨I&›f¾ÜVŽ* •- ¹lÇt÷pÎ<‰m‡ñð°ÆÏO"ü%Xö}ðj£ðâÚZ\FqÿÍ!ä®ÞüSÜJ’ù?ÓrlŽ–÷Ó4þ…²e¶˜?÷"@çþ ¯7â†3ì+®¨ÁndIŠÑ¨OŒD@9]´Óm÷!Jp,ÐiµVJú fœ’Ü¥¤hÎ”šœš›Z–Z—ú0¥L¹* ¤Ÿ»éá~®e™eå k²`‹núÅ1ç®b@Ôæ ÇÁ¢ÉF]<ª` º*kVƒÔÇV³ Î-ç¢tÉ ÿq6ñÿ¹1œ$¨i.‹ð`ÿgr8IH…Y²t覢¼~‘NùY^@–Yþ¢e2–um.šú”ùcY,Óù&ð>µÙ³À7 • /˜þ(þ ý óGîGþ°ý° |ÎþWî¯üÛöwœŸsŸó*çâa}®“}Žÿ—鈅¹Ss“~3yHñf³þÏÊ?«Õ—“kתWé´ÝH6(ÔõÊzu­¦YßÄÕòµö&§:A’ú å£öŒs¬‚1=Ímå·Z¶ÚìO;w êGMq÷ñ÷Xîµm¶?î|XPŸf™bïqnän²Ü`¿Ãy› mmmŸà6XøJâ~åºÂEbê„ú×M~r²Uûï½²êcFjJöÉÂÈåM‘¶‰DœàšÅ£€Žz\™4ŠœPyäxР US¤šz=ˆZ{¦ˆsï ‰úÃ]Ô>æªÿg¾.:Ù4ó–\n3|ÑÅqÔSÆfä%ÑRœÀè“‘6°:>{ôý±5;›fæÄúž{'>|Yþ-¼?¿b8O¿´vÊeÑ—eú´ ZÏ€Ðåi³ÌÓ•¨ ¿ Í]“\]ÕŸ½%y[öÁŠû›+Õü’êŵDc’î˜5iM‹hBUG¶£n|ãĦžÈìèÌXOÕ´ÚÙÓê»Oo:3yfõ¢loýcU÷f7Õ?Uµ³vkv ~WÓŸ’ª UéëÚw6i«ÔZ=¼µZ] ©+©©ãéxc®¢-Ñ”nj›¼6ruìòä•WT­­ÛÙ»!¹¾ò¦ªÛêîC÷'ßH~Þø¯ª#Ù#uÿjòÔÕ76±ÙÚ*&%B˜ùÂ!+¼ŸÄ»xDÿÌ­º…K°V²0QGÀÞ5Ѹ#΄5Æ…)X‚Ò³‚„”äÌe¢ ¤ªR›RŠÔù¹¨(Œ—ƒºrBp9dÀbx034b~hÙÜVÁ‹>4\6Ùu@øÄ ý²y`üõ³Ç%‚œ®V Á Úh¦£4&Ü K£*Ï„ìÅd•TBÔù ÈÒ¢Ž®žÊÒ¢ÎÇÕÑÕSYZÔÑÕSYZ$­ÀøÃY,Ó£PAdÉ!{¬²Õ.SP|±R|ëE’£üK¹Ør“_.ŸV7kFs°i¬G™º¶ u‰GW[ÐbÕ˜Ž=| Ì žõýcìœn82¼ÃbÒ9BÞl†ÄL+·&Ž'|F>ÿ`þÛü÷ùÈ™ÃØèÆCöß§Q=¾{¼ìáYéÔ@°5it¶;·°úWÕŒ*ÙT=¡z¶kVõŠÀŠÔ¹ks÷%®~E|ËÿFàCñ­ô×¢à•êÑþ1Á RWúûS¿õßã$õ|à…àÁ¤Á·æi‰Ú»áÓåV^Xn%['9²#ŽË ‘ *CéTÂGu%¯4|™JÊöPÀ¼Uªqp]Ùñï&¡4Ù$<ˆ«zˆaù|ÏŽ•žurİ/„;C›B¯†¾ ±!j›Ì‡3€ëNh˜PÊ 8ìè9@_ÃÖ§¼P6=å,ÅegTÆ“e2;Áý_OàË<¼?Æ6·úõÝ. ‡·'õ9»4ÔÖlL¿(R+¬‚)ÂË@gÿÑê(Ê/ˆ2Ÿè%ÂâÐRÏø¹¿xôÎw®¼cöªk%j.¿ã‘eùï?9gÛ”‡.Ì¿H´ù Ã×s¿ž½1×zÇw´;öæ¦w.m˜~+ø†»À&±‚M2 ½/%ZjOqO®í©=߾ھƵÖ}Mãm#µãcÚ %‰‡Úù–ã ã{‡ ÖäÙjqÖA-H)Þ2Âå4)¬×kªÂLe–ƘÍ:ú>ެ9ÚQæÊ«cÙh°Í1uP5×Gçú–AZ™kŒ5*U‹aQj___ß<®ˆ £ïÜýÇa†IyÈÑð3¬k/ÆŸËè!ð9JAèbrz#]¯D_ÁD󤱼¼ú¤Ø2EòmV‘ßa"Ãû±r ™ì±ËÕ§ñËR,sCÑšãXqïWo®<¥wá#í§uüÃû—Óa…ìÙs×]OŒ]uëk§ŸþÆ£l«‡ÎÎÛ°zË=}õºy5Skýf7Ö?gý‹k«è®Ïh$úô[îZ:r‘Ïæ‚·^yÅ^곯¾n–õùuR‚g9<ƒg pB¼,GãeÁ^¾¼æÁý€°˜–/-ÛÉqfˆY‚ì^&8OÆÓëyÅÚô¨=ŸG©[Gq@XæÎZñÍå€×1~¸wO½úŽq̧/µÀÝ/­î&ëߣÏ6$£ïå¤Ãà<èùÂÿ#ù^ù½ûÿOްJìÖù¯toP*y'M²q6b«l6§àå‹°œÞNŽÃÛª7aÖÊ"Ì× Z×\ÄÞÆŠµ%Ì p5Öâq§ÈkEÞL¼`Ɇ‚».ƒ¹!&ËÏ¥ Âk ®“5s5Ë4+5ë ,TŸàû€ëSò|Š5j=æôü_Ü™á ´` äõ'@Ü%ŒRßä  0ÊO8l¶‚ò¢hk)zPêg†þþÀòÇ.‚`¼Qï+z.ö^6mí"ÙÃ-vä:rË7󟻀ì…3hé´ ¼ú™Sî:Cî‘q&ðg ¦ZÒ=IÜ%y`U¥³ âœìõ^ϽêýÑùcRû zÐKô²Kjä¸Z¬ä³yõ6{Þµ´Y¿\$HäÄ^ñUX’$Æ!ŸHzCI¤“ÚœËT˜®Þ_¦ï6NT*†xuzöx¯·Öéõ NoÐé°Šáƒ<HÓ— ˆMZx!gB bÐ*êQ‚· êuAôÒÄ*g§sÀù“¥UÒ9ˆ˜±Íµ=mclÐÞ/>uìÆ—¼ýê¶”œ›y­Üáƒ=°¢O‡cµŒfÐøC&SÆ4þMJÚÉ iÀtÿ›<¬,M4xª0…4ÛWž\šQ ¯ƒ×xÿ›^r߯ò]í«Á`uàF§Å`´8îÆ«•øÒMN+4œ¸¡¸•ØVM¯·iŠåQ;óʼnm:·à¯±›+È·EMrÚœÄîP(!7X]á²*Å€žh"Ä/*Oj¹7ÃPD·ºÊ-M[æZæ^æYæ½Ê¾Ú±O±Ïú™]ÓËõš{ù^ û ÁœsHvÉÁ:‰Ûáü^_EÜQGêìÕŽ1dŒ½ÝÑ…»í³W9t¼@ž·¿%g4š¹N¡sV޳X½«-xÖfÉ D–GŠp‘ÎȾȫEd}E$«ð+^)¢1iübÒ<­ùP󵦬º^¡Ñ(^½‚ ¸è!Vï\˜æ¯×%@äʼnàƒùIYˬ –õÙ¬°ŠÊ i«>§ d$ûœ¨; >‰DJl6;ƒä<Éç! ¨êcbÐEÿ‹hPŠ=,SÇ)à$'è(½GªyEÀ~ R"'HÙº¬°*•p$+Hb,+ˆ’©Â_1·beźŠ¯T|]¡®ØC.3Ð&¸Ã§Ù¥ |àT»äÊ™ì_Ë‹”fm'’˜›íÂ­Š€í)ø:+bà«Yœ–l~+ÞgÅV‘SÀk'+Ö)^Q°°âR0Ðhç§„ZâÐìWwP¾äPumn¨Ïå<$û¹}=`¯“û 8£$á`SŒÅ6ÃZLÊò[QåףР@¥N¸Þ‰h½ ìŸÀ?Kêüy0lj"˜f 0Íž «ˆËá²»À  FØÄWÉhÛ…$¤µ^•¹ÅΕöËà~OO¼ò8‡KΰÅRk)¿S¯,e™w®øêó+.ñSe‡¨{vÙ_úùÙ”;êh‡Ÿi; ïÒ.š†˜ÌÑטÿ*·e9Ú ¼¶ x­ƒÔJ7ñ9Ülç:$ýdûäÊö†)ú¹öžÊ) óôçØÏ©œ×pWåú†Bƒü`p0;Øñ<ÿ|ðùìóg¿n;ÔñOx á·\È ,\ƒù3ßæB°$^\ŒƒÙlÏCp= ñ£lMù`¡Æ5lmÂNZ‹È‹Aâõ#Å1+æÄÕbÉH°Õµj—zI¯³8+vt´54´Á‹++cÔ<çÛÚœˆ±B¯Wx½zHl¤B2›EPÖ\…Bá]#ÂRºŠ±…^ø&º_;¸n™—ñ £ö`QNr³_¹$L‚5&ÜaºÜ„Ú}¤NÀг5y§Ü'@ú‹4¡W¯%ñX$ìM„C!̅̈45aKµ˜ŒˆÉD@L¸‚í5;0‘òÆLŸô xMJtø»p˜]FS!oÆýæÞÂWTïdH:üh–c9+©ùuz<š9Mùm YÂ|)¬¯opV: Û«~í¬[l¸Õ°Ö ÍF½y¶ƒfF½Ñ¬Ÿ-1 u°ö¤“íd‹ñ'LÑ’e8J (Þ(ë–y³¼TÞ ËrÐ /pu‡Æ fP‰’ÁI}yàí>Uþ°Êºˆ;,.¼ºœk0é¾®,<¾Ì¯2¿‚ïÀKÓŸá‡õcweºBðx(l¡Xß Í™t_Ö}úÙܬI&UÿkC ­4[Î?AïA37ô$¶rËå.V ˆÛåÒgôOFž©–‹õE‘´f¥û–àÍ¡.Ïwƒx¶wêwxž îNî׿`ÛëÙ<~Ýöù¯ 2ʹü¨óÁ t{õ÷«·U?cÛYýòˆ·F|4ÂP iân%X”ŒÅcñRWÛ_62ÆF–ÁײÅX PÝiå ¾¶”™êb²Ù#Q÷2Ø—¥-–RÏvG¬@O¬,×lñd¬96%6/öxlOìHìTÌ 6ø7ÔÄtt|©îqÝÝ)FU~x@H>—WLêû˜Fg©rr‡«X2ÓÞ²3Œ`ƒÍ`‰ýÍŽå$™<Âôš¤úϱzP ÿó^—¡Ú^PBѰOÞ‚=8õ0¼Ÿcîþ£t¶[scõPPåáYè(€‹WˆÆ±Ëò2Y(Aå'ä £äYÏ¿ùð3§3fí”®®{£F‡ßd[¸}êã=Ëhdy9ý‹Ÿ¿vò-7Ýxxámn]ºú9»cí¸kF›`)k²Ë[ØU9$”ÿétLIO»dñL“K¬ u?ý/Ÿ•ò½´ Ü­˜I±Œ[ó}´ï$½€ÏÏëenŽ[æÂáÝÂýÅ1c4>t¡R.çcAw)ˆÙñæ%]°<1Y¢^×장žSðí(»òþÁÕA• ¬åH}³Zâ`^œÍ#|$‰O¨ÃfW¼aKv0œÕþÉàôiFðZíXsŸb¾8|9Ÿ¡«Ÿk¾š_£½Á¼JºÝt{ø¶È½áC‘ãû‹|Ïõ˜¥5ï cfa{× ß5PL@£ Œ7ÇûåÆpî0~^þÈop]æö°Y,Z²Öÿ?xïÞ#‚ Õ_¿’ùÇKÇ2ç^yŠø;MÎ_ü·¿}$Æ(²sœˆ¾YÎÎí™ Ú‡*õ ¥¯z?(z·ätätìÏE*ÑzK|íÑIE“JfDç]Q²Ä¾$p]ѽ tµSV¸=î˽×]SòEP«ƒ‰7€«(ø}Ç6ÇCy[‚OyŸÂ¹ ˆãìOˆ¦)ïWeÍl­3V¦7÷jtùÿé%̶´¡sG„oŒH‘`¥'†Å³±yG1'ãº0® TTÓèoˆÂÂ7>¨b!Iªí¬X™äod∹$'X8UÕD<ÛD7‡ (zëSPÉ/ÓÐSn •t{<üÒÛÏ,x}š>Ç=qüõÌyn~ý'²5ŸúÉ#Ah|ן~âäES=~gEëõ\~õuN0yô‡;ñ¾w¢?„ñÆ?|îâòÅå¥E@&+7„!Ý­8BI(”ç‡M¾x©q® ]¡·4†7Ž.Ç—Ë/ûo·Få‡%öíÌš;Ñ6Kà'¬ŒçYæ‘>ˆýºè“ØÙ¢ó±Ï u×—ÝXµ0¹°nµõ޲åu÷•uÕ=V¶©ngÙŽºCa›d ñ`Ù'Z­Á—X¸bD^Ôá¢6máÍ#bQSEŒm.ÖCÈG¼´ Ê£&“@·n£l7’šcñ$§ÁúêXWbcbG¢;¡9’8‘88—Ð$©ò«†4WÁÊ­(† bˆ›ÏÒ°JËÌ4fˆabP;> uÏç,+‡r¤ÿì Ã{•†ÊÊ,0}øgO•¨Šœ¢5ËÙÿÂës²§GoƒO•™·AøŒ qÞ!áD`ðà!ߥ΀…yËæLH¶¿N¸¥ÄwÏ[Ïž?ÿì[÷¼¾~ýÏ~¶~ýëÒñGŘqpzkå•¥àPóø%—·|uóýû9ËL|ðçol~ð7Ðf /܈¾ÐÀoRª¶ÏG% ÷ò«u«tùƒÒþ¤ÔÍ{%ÓSºê÷i÷ë_Ñ¿£?Ô NˆŽ1rÛ=ä™C^ÜY$ØžÊ95••Éšx™ú+ñá`ŽPaÅ*k.š“åbÉiýn%QŸ>ëã œªš²ÒRTwÓè&ƒ18•Ç1S<¡˜Ç°XtÄ‘š@åàî=>[•´Žâ ¤ÙA_hÒß8ä3â›§†œ`ÎêÀÑœî¢Ø[·Ý¦8‚!­^WÒÂÒ.¨ÏW»$ú$ZJNéú?ßµD<*„u0±Aªÿ¾ œjN<æ$þv9v <ÛFø´©›g/¸wΕX‚D2ŸÑ||åÝ«æ´$o r=ÜÑù™ãÇm˜Ò÷?ýWž½º*zKß'¹•aóö‹h>ø ‘ÁÉ®QÊãÚ€˜XXøN@ï¶:fy‡9©%,Gl „ùS º\ºæÂêúFa`ÙüúØEø…‘Ä›¨wçVâ¹5XnN•6Þ±†O 'ïË£ŠOøÙ·jï¾›¹ô+aD@¼ Æ*p44îKl_æN¹A<[{\©p,£ y¶½3\^þJ{ëâ]R—¼Ån›lØ`xܰ3ÿP¾6ß"ã‰|ôh­køgŸÓhâfõ‘›YœˆºÜ6ßfˆdðyŠN8åpÄbL&*>Ä÷ó_fG}¡l+’PhÃÛÌ}Ù× ì8ü’ŠgxæÁ°e-¼‘J§î¸+c%ÓNiü¬Yc§gþ!^ñúïÐó÷}%zÿÂë7VEDç¿ïZI/:Ÿ2Œ)“ZR¸¤~§ggBWŒ3b§ŠÑ``$ϧì>°?9Ú ëEÊxðÈœD$OÄóaX'ÆŒššŽTMM]*^•ªwšÅ…lI“Íf6ÅdÉŽÕ”jÆž·UرÇCwe1•/+›STVV\¯,‚‹þÂh}ʰdaÝQ–€Wâs×z´ Oçç{Ó!]qº².]UUY)™Ó.'3¤9‚=ÐrÚ¸ªÍG‹ gÔâ;XJ¬ËR]))šªIÍOÉ)“ FÁÁ• sÐ2c—Qr£ÆlÐl¤3FæÛ—õ8¤zˆ ‘ È{²òÔœŠfœHÒ ÐÄš÷d/`'0=Ù ä‰È!ç9äü”÷\€ p ·þd³ž7H´J±7†ì~mØ» †~.ÖÉá§£¸a™,Ly„e«“| î±;i¨” «Ìƒ,NÚc("ê©þ/ÄR_È—CûU|*Xp:럽ET”$ÔB*žëþæ“uH–œÆk˜n‘ ø ƒŽ/óE¢·Xið¸*_DWÑV敦3ó›3ß´Œü’WÒB]*ó—LgnÌä+DŸ:Œ>åAŸÊcs•Ôï ïÝ^0!–YÄ;‚[œEœ¢+Ï»Å鄾 "ãQ§øæ#Ù ‰ ü7cá7Žƒ›†Ž‚£Q0·ˆ¸ðÔÿ½¸×#àÍ:¤r¥Ñ>ÊÞ`mco´µ+ö6û8£«Ø2Ò²/ÔS©)á#¹4#~AþJýÊ|íH}mþ8ý¸üzmaÔXÑCOác:šÆŒÛå…rc·ŽºøT×›®Óp' a.‡K¡ÃærÙmqo€•`XÜÌJG8„ãE#E£Â:GTב¬««IÆGv(T¸èToëhnkSšãUI]¸¸ºª´ _Çõ壔4ëЕÇä`Ìh”õìyMV[ÊåH}¯ ú䯊 ÂÑ’bÚ/îBŒ“¯šX2ÚÜDb-Öt¤éD“Ü_þ¬:#©b,ôE2¸Ëf¨ 2œP¹@(ˆsè8¾ 2„‡ÚW~4ÇTèhLÅpæ"Ë]DKËòäA£5•iJ"\« ˜üU­-ð< L“H÷cªWk†P»CÙ…,€å¦þ¿0 Hßÿx÷ïÿuŽåª/Nصöú‚MÂr°7G{‹ž Wy^øaÚA¾bs IBp(uÇ2\¢óÇëohYkX1föÈñÂÏÆ¶ÉuÕ×´tPŸÛ6eDUåØ6Q|† Ô3ä3VŒëè—¾äоýÔí¤‡•éãõÁý¶7µÍ,(»ZÝ¡f®.JDŸ¼í|&Úy¿Gõ–î-ƒtLwÌ =aèÑõäåú.½´Pµáê¼-ô”Nº=ÒË÷Ir~dI†7…ÑcE»´{#0ÐêZq×pîV–l²¥#;3©Ü­ƒ9Фa,®µ¾CeqkÓ :~ˆŸfQH7Ü1Ü® ÂE£)<à šT‚ÑÝX³Œn€¸\¡²!AÚ§Ê;‰æ ÇSÍŸ4ÈámlO;´=?1׿‰Ãõ„òámÙ 3H:¸¥ŽðØ,Zx”öBsëÌ-÷†<4h3BGÀUq¯Ú~5 u±S7¬}Ñ9ëþÎùSf‹Æñ!Óÿqãe«—æsÅ1yÁšÎö²ðº‹û>Ë Ñ’Üy{Ûwûþ:°/Ö­h"à7A>Õˆ6‚Økü"¥ÁåÓøàýX~¿f~Kz_û[ý[fÝõúëœÒ"i‘æ:Ãu¦%Öœ‹Ü×ø Þ˜l!`×[bXíÁ‡K YäЇS®X½õÝŒ;€#™6ø€t’ç‚D§éœ³BõºÓºs:-"´éÍÃ0$ б†Á Ér¼Ø å? '$'ÁÔaDNø!Ï>ßçðØ<þCýgÀœéµ†aÑ©Åk'd³j|nö4 FÚ!òjöX¦p³ÙƒÄ`B¢§åŸ(àûô=ííjöyœþ&%n©óÀ^Û… “ ,›¡Îã4B þëä$ÍU%‰lpì5McæSˆgÿÂ]Ç^âîîØñ!ßs4sŽ;EH›s?ùÁïN=¶ýô)ÔMdÔÉ»r•Ò<Âd]ª¯ºn`æZ¯æ¨ÝõÖ•üöò›ªÍ?Õ5½«×ø^É»#>Ö}„P2»þ>y«¼KÖù€Ã’4,€GÖ‚¸O©Ì®ãC¦¥–x2;#Á‰]Òžöæ§!.±%cfSYŒoÖèY$]¤+ŽÙ ܬ«d¶hØ^0EØ„h µƒÅñÔGŒLõz$„ ÿJ–0¤ Ûuž“Ê÷”ZjhýQ%Œ·­QNµ>¢ÿ·{Ku.jFíg$é!±zn°þZ§"]Ÿ¸kÕ¿Z‘é{ñÃûT]§e…ìýú‘­'On}ø¤¼`ëì9+OÜ´?Óÿ|FG=Š_š´`‹®Ûtâ͛ޯÕ[¹›ylSaQšUèLÍL‡X‘ÑÂ,d²ëÏ!(V°Šy¢…öÄÔ„*Ì;—Ð%•}÷_˜„L]'©Ë'ÔŠpÃHõ¢rmðô ÂaýîßNƒ˜çœÙax¿Ûcó¹òÕIg1»©âUáK RêaìúÀrE’ž|bÜÄ»n“ÍHFm;ÂWR=õÝH«ù×\S^pòÁ‹‚nÈ[ÁY;3)Q9pÔ#½@•%xœ3ý¬ÿUYëiÎo‘\—°Nv]û®è®Qÿ§áçî×Zç~Û÷vÓû­vŸMý±õ+÷ç©¶ºÌnOÛdl¸½>oS¨u]|Kê°Ý<Ó}EÃu KÒ«îLßÛpoú)OÇtzDºÔPÌelc ø ›ÞkÍRPhªGÚmð5 E =v,¼‘´AoS¿OŽ’HÉ/‹±´~ÆèØ”0Á(dÀ(FLO¤Ë¼1…æIfD¥siŽ¢M/ëŠM1ó•Y=ÉðDì„kã„Â")­@UP- Ä ŒéÈwƒPe=þ MTÎB,×0ªÕÍ/rù›¼–ŽðQQ$®Vìúšó" vjcÇ4‚Ÿ ¦"##ÌÓâ 51WªK_ZuâOð9¹úß—ö¤Lù/ôÿùá¶©·É°Û'½q_#µ õO¬Z—.ô kÀ,kÄâ#íAÒ@sn,êÒ$í4ɶ{0­¶{ ɧëàÍÐIÏká¡D½ J š)ðäÌ\6a?Tí6E¤üƒ£à¢e¢JòuW˜ Ð'ß¡®jIÓ×0ížõ“Ó5ßÛÓ~Õ¼_¼úêƒW}ð'¶.}rÇ¥Ó2¯®½ääæÝrEÚêÆpÐh,i]QßXšowç%î¸èú-Š{lÁð³hÆÞêHMóêöÉÉd4µ¸ñ†5´ö|üVöQ•ì5¥ð|ˆ[*QzÒ´ßô’éצ³&íͶïÙ¶Ø~h{Åü¶Yç7P\•ݰý½Iñ4½Öl£q œ.6`);ÀŸPœáta¡>Í9ÓYb³g-\›?­x*+!1.޽ÂòùQ`IŽ@‚w@ú¨·Š–{hDg1<€‹Î¹ð"À? j –!…ªm †LfsÐa¦vGBÛ"T؈ʧ"€)¡·«¨*«Ü!§}ñyÁò ß™†UËg¼2ÊcE$’èÿ,ß¼[À¶QeÈ ¨{÷ýòâuQ+Å׊Múþ*)I…«½ÇÙxòV‚±ØbÒì÷I¥>„ºJ°9–¤Áb1âvU±jMÎ*VKbtÎDyaG´°0—pŸÝ¥Y‰ÉŸ—†»Á˜†ÛLD d~˜,<¡ËΨá\ô¸½t¸¸/’Ñ‹“5 ¢gý7âçÇÔ@~…äêÑ!Bu—›¢ ¸5Îsé<ê›W»¡;; ¿ˆŸ€#>Ë×3ès¤AF!ähy`Ju3êÂnvò”¿·ëøíÊeBDþòâÉoìÕð™XJܾ½mÖ*),*cý´%/¨›ªL™ê ùðÔAÂÞ;ùN×.·5EÍQKÔµEíQ¬ÞÒ¼Á5Æ}t­ó:Ïu‰=8é·K‰pr‚¶[ñÂÄÈaMZeëdá -—Éê4Ч…€ô‚8”ðý‘ÿÑÿOé¼ïŸ~§jY RδHE—ìV*—r„‰R(Ÿ£­˜ÈË » %‚›¿U`MâY¬Én¥lžƒAZÒTþÌÈ÷Ø ¸Ñ¸UؠƳ°“ÝŠ³  )0'q`N¶ÌIÿ¥6‡9…Rs„9‰œD³˜“#|øùJ´§Óxý^ˆÔ6ø“éSI:LGÝi«.m‰E£V«E·š¿Ÿ W¹O)f›J °&¥Àšk¢„‘‚H j¤•ùð:r˜ÿë?_°É I1:%Ñy')g ªª)Vmt>LOêÑlö¤ánáhOM=e½ £Sb·BÝÅψb\Aäø¾Èq1Ê—ÏŸÒ*Þú5¥H„M‘€K9ÃÊõ™/æÂ÷LöïSŸÌ%x þú8enœRñ9°*g粟ßèZ[š˜ßöSþSÇ/\¿ˆý<ñóÚ—R/µÙáG•=‚Îÿj"à%Q^"`'Qv"ÁÄÅžÆ`éN»Ò±t4‘®M¦éòÖt[º>J§•´¤Ø’æNmê¯Þm{¶ÏhÓ!‚˜Äb>ÀL¬fò¨]»àïM…ýò_Оüì¿“„ÅÜ`3I/".¤–“&¦Aý™õIá ,nò‰FÓîNÚ݈÷ٸ䒢VðÁ6€OmÜg•,Üf2?‚FðšÏÕ¤í¦fÓR’Ü|s—R€÷¼UY¹ ë&em¡ÉY€º"!“X‚'a‘‡áNu{+©`9˜%°œÈ1Þ!§cupáÏküeÈ^<-ô9Äb<–µ±ÕÜ2YP~»oÔ ,0ú˜tSßê"obߨ•ôjO”^º‰6^…½å`YÆ@<‚=²ÿ-×[qIöùp½/žO!*àï6ñÂj§Ë!Ûµ•³•f,æHyûYÔ0[6O‰±h•œ¬¶œÔh ÁûŸ &« c±èI/]R0PûåAþíëCa€"ü*¿›@2 ¶$/øiÙ\ä ˜^„–¤aD˜\RªÁµ³¦„õPßÀ ?Š€üª\+`½ˆ€£“FÖºü­#"&k00¥~|k]¾Ï—Ÿj¹uz^ÐjŒÖ¶”ò¿Ç«š2[G_¬• ]µãòÅ£&jdŸÇí“5GñÅW~;Ï岘dí„Q™Gš'÷CX—MxWV;ˆöT¸ ã)V¨7lYŒ¤ð´ÀGÉYâÞà#WÝyšÝ_ÞC/_{†[µín’"šFi®T,úB¸Ô·k¶©w‘¤„ï¢ æúègÍW´@¦¥‡DÖ6~"ºü]Igä¿â>ÌÍÚxÁ2ÈÊõÎ~¸­³Ìv›Wšþî~Ï føïX¾ÿ}¿Cà](Å ¦Plp°OÕ36üò\À{U)cÉ…­Þ÷vðÃZ‡¼ÀWÐü!s©ßâ —òßf7Ľ¼/Ñà˜¸—fÐ(®±Ñm˜Všÿîü‰?¯Ø½l%ÿ»þ'Ò{¸‘Wwª7ò9Y©P4ìÁ7R—CÖãO2(¸âßͼŸö†Òœw3 |an%QöXý|OvC}Ï îÕØ¡][/Ÿgoü‡Î_éïØ÷ý÷…œ8Ú±P TŠ?产û¶âËî~}ÿ¯4çލÇ›¯yƒ] bš7øŸA&lÍæ”}¨yCš‹¼ eg@±]üò7 úDßÙ ú)èmÐ1w!ÿ5èØv«çóì/-=¨–³ß`Û š ZúhèqPèzý]¿Dey jÐbPo6§ïm]r€Š@SA AoáYÆ"ÿ=~·9Ý3žž‰ÑóÑõïÑõéž¾ šz´tDïÇ º´ Dߣó黨öh6( ¢g¥çÜú¿‰\¢ß¥ïÑ=¿Šˆ=À>æ_Ê…šRð»•Úïh?н ê'è¯Ö_Ú(ך®0ךߴtY·Û˜Ý;¼vÇ£ŽŒ³Ã¹Ë5ͽØó}ïZßE~‡ÿlÞƒ? ]“,x&ìŠÜýVl{|Vâ‘ÂEßAË·KŽ•­.ÿ¼ÒTy¬JW5¡jmÕ'Õ–¤)ù^Íð|ºöþºkRßJ}P?ç4¼9ú̘îôý·Žmmz¦ Lù¸õPû5ãîï(ï8ÚñÉxÏø+ÆïPÛ›Ïãh£wƒ±–$+YùqíL@*%q€…ÈÑ É¦fÂåWLj¿¸¢m骛®[tÓäE·L½lÒtêÿâx2ìü‹¿ù(“qE-®¢g´t´ÑY/¿êd.Œü‚³i¬ýƒ,«Ì`*",Êb,-[!´¤Å°¶.…+„rVùj«Æ×`&©eu°q¯g#Ù(ÔÊh©kDD¸&ÄçQX keílë`ãtíb6Md—°Il2ÜÆLe—²iì2Üù v9b;Í‚já 6›ÍasÙ•p*ÓÃö±ýèÃlzÿQùhÏŒ:å²1"ëµÖva·×ly±®¹%)eË@{@'@6éšl‰Ì"ØjQé†í x˜½ ¢’C(9„’C(9„’fùãòóòs=…ÜÁ>xC®ý¬%(÷²~$oBxœ®}e6Ÿ—Í7 /GùÆl¾^^דި[ŒØçì3¤ý ϶½gü”ÚƒbcT£ØØ–+ÙÖ‹’HK@ÞŽ»ÚŽ»ÚŽ»ÚŽ»ú )ÇÕ·¡|Ê·¡|›(ßëºf¬,{©ìÆö»/[‚“Ü)_ŽÚ‹È³²ùLùòžÚÈ‘–ùò \zHwÈÓ±½A¤óD:E¤kÄÑ5b{©Ø^*¶›Åvsv›¾›ÛjÛvJåiòehMùRy‚ȧÊãÐÊ"+Oùdùb‘O’Ç‹ü”çáøDyÚlDž wˆý‹±ßŽý‹°Oùx¹£§=RÓ² ûópLbv™ÊÛq'í¨Ìv¼$*ÙÚ:%Jæ!]:’Å™\nǧ Ÿ¹ßPp G†ðƒø4ã/‹§‹T‘ñ¼¤IP3h hè(èM^nD•ëY HMÍiqJ|¯÷U‰_¨”!FǵbÒ}è9šÍ#Ò:ê™rXZ׎(-Fi› šZê’öõh]öΣs“ ) y 5 ÇA{@ÖŒG³„ˆ-1EÖ u—õ66ÖŠžEž_ æ–`­½å&¹ ¯©Œ=’qËe¸å2ÆÞ2 ï} ¥õx›ëñÞÖS ÁÛCÛÆ‘æìïiåƒø”áS‚O)>q|bøDñÒ_Æá<6Jð¹ŸõøÜ‡Ï:Ô†gOÅ‘ i^ýÒú5õê¯ßS¤^Xº ŸùÒ|ÅÄ|>Œè.§!Øâ4lP_ŠôY‘Þ$RE¤~%8ÇzvŽõøëÖ9Ö-s¬³æX'ϱv̱&çXðŠ¿Âú~…uc…õò ëÈ k}…µ®ÂZVamq½êLL?i«HkEiŸÙceÆ(=f@‹ç%ûbwE>ŠÐðžÈݱdÿ¡îÍV³4>©‰]©TKàò„Î.Œ½¨ÁØ ¾‹éy…R©M?O¯èGë«õUúR}‰>·W å›Áb0 0Ó l30B%£<:xà‚†NC©Fl;03rT¥@ $L=Ýny¢4ñ²VX]È&.ˆvq|°˜.½¢[›håÝ®‰lâôÖ¼îQèû§u7TLì6N=k/ç÷wb¯[Z{€³é³ð~*ún¨ÛÕµ+‡øl}(›wvÒwfíÕðõë;™ïææ¼fW“stGû¿Hæ‹ÂùíƒÄ=yƒ¶+èN ºšxÙ¬îg :»ki£¿ s"ÞÜeQ(|¥iä¸öƒÒ(Ê:g4uI ã¦Q¹©«7’;EQÞŸ˜”‰óX”ÎcÑaç…¥Qt^eêyaq^xÈy{ÇÆÆµï!QÏ+Î;ôœk‡žs­8çÚì9²¸q‰Üuô§YLœÓŸ÷>øœ°ú[ÿöœ¢yΠ׹¨uÐÎ×6ùA ¦³·mõ¸E‰qóãæw¯»yq^wׂhô kãoÓ¡h·\<ÁÂÅ”_µè;±¨½»-ÑÝ;A|uèñîÕtxB¢}/[=nú¬½«•Eí=” ãWµwöŽ¿ªüÙ!?woîçö–_õõ뾊.VN¿5^|oØo=K‡ÇÓo=K¿õ,ýÖxe¼ø-ÑêÑ, ¬µO‘÷JfðüP¬³ÕçXÖ$Zs:–wgè†!(†¹¢³Û’hí¶‚¨¡WµTµÐ!ô2:dC±={(ïÎt,„@ÙC;­,oÜuíø_±"»¡îþ¯éŠ+V^¹âJd+VŠÿ+W!§:c+ØŠ•ä7¹Å"æ·Fc›×îc´¼bEçJUhºb£__IÉÀ^ØZ…‹óƒ[£Ÿò‡£d‚(—[± Z\ê÷ø Hf*p|ue¶ cÎÿQ-–{ endstream endobj 266 0 obj 30602 endobj 267 0 obj << /Type /FontDescriptor /Ascent 833 /CapHeight 578 /Descent -300 /Flags 33 /FontBBox [-122 -680 622 1021] /FontName /JVZMDI+CourierNewPSMT /ItalicAngle 0 /StemV 0 /MaxWidth 634 /XHeight 431 /FontFile2 265 0 R >> endobj 268 0 obj [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 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 600 0 600 600 ] endobj 47 0 obj << /Type /Font /Subtype /TrueType /BaseFont /JVZMDI+CourierNewPSMT /FontDescriptor 267 0 R /Widths 268 0 R /FirstChar 32 /LastChar 211 /Encoding /MacRomanEncoding >> endobj 269 0 obj << /Length 270 0 R /Length1 4812 /Filter /FlateDecode >> stream x½W tÕþÿ™}%0d£(fÖ!1!»Ù„ð”L`7’D$„¨»Ð»$$Sµòð•úÀW|µÖÚÚ–É9@¥J«­räØƒZ->j|T¥Ú ÚŠdúÝIDñpz<==ÿÜûßÿý¸wfïÑpZG2íñä¡Ày#«±«S»§µb:ÖýDŽqËV,oïîÜê"r:06.¿ìêeŸú!Qúè<ÔÒoúx^S6ѰbÐSZÀ>.½t èñ-íW¥=KcA }Ïeqª§) oíj_µÂvÝ Z»<ÞÞ¾{Ó1Ð{…ýŠŽU”MÍ ß·âŠæûýš—Q€æOƒÇñ 'ÑAÁqv’䬥Œ,ù:“ÈzãÆ³P d[/Éã`Ñ+†4PQFyÈt5݉ά¦×i2¢|z›ËA÷Âït-¤ýTCòù”  i:µóLÏ/¢¢0ÝGOÀâjê€nýˆ|°Úm=Þz”Rô¥Qͤ¼l¡»¬ýô½OǬ­¨”ÖCãõ bg¶Dc(>Ûh¤Në&*G¼;i7½Îg[åtM¢2 Ñrj¡vºœn„ìq:D/3zM*Fœ*º„6Ó­t™ô;Vù|¯#Íj±Úèbdл[PÍAzþΙü$?cµz- ±ušF³ly7ÝKÒE%ô  ŸŽð¾DÖåI–bM´ŽC?]3¨–¾‹vº•ÜI¿¥ço j>Oá‹x¯ã?#Í’Ït¸Z×#Kú=0ù¯B¦Ûèvôðúàiú#:ŸÆºT)­“ŽÊ™²á˜ïì²ÖZ;¬'¬°#Nþ×. NºŠ6Ò&Ôs í¢§èìÕ't‚ÝìåQÈ¡—ó‡I™R¼ýAîò9º‡åΛè·–[·[ÏYÿDõ¹Ñû|š ˜†4ŸP”šÐëKíóqí êCïÑ«t˜NÐCSy/äkø'¼ŸŸçÏ%IòJ*bH7I÷öIÏK‡ä1rŽœ/—È‹åíòýò#Ž[MΕÎÛœ=®Ýšº®ØkÕZwX¿´ö!²{:‡ªQí|jÀ.£kì^'ÄYB>;é1À.dµ‡öâm>HoÑQœ¹/ч4»YèD1Oç2.Gž³9ÄÕ\Ëó¸ûs G8Îm¼š×ðÜ ¸—D½€>ÞÃÏñA~ÐÏ>—œ’[Ê’t)O2¤ZÀ%€¸Ô(µH­R»´ð°´Cê•R2Éår•˜ò>ùü²ü†ü/ÇG§ãŽß8žw¼êèwüÃñ™Óë,v^伨•åšîêr=äÚé¾Ôý+÷«”Dš¾ù4ÒÕÒvé Z#÷ìY"éÒìÄjÞÉ¡S?¥n¼“èÏPH\Nqn^îxýŸ¦ŽË›=ftÖ¨³Ïyæ™ÞŒÊðaéi·Ëé%&X¯Šif^Ìtäésæ­ÇÁˆƒ35°ªNÕ15a‡èMšË¾¥i j'5Ù«•QYÀ¯…uÍ<Òµ/œÁzkHjæ{=×^o³× Ö> ´pVKH39¦…ͪ®–D8 ø¹Ï@÷Ò~tc˜plÒìøê–, ¡6Çè¡°9ZÇ297o2ëæG¡lŸ/ð›<»Q_j’>ËÌ(2vTë#ˆð·šÈŸ6oÒ›6§ Z«ø÷"¦šRLÄÈ,4Gé!sÔ5ïd}M~µ où†Ð”r«â͉*ÓˆmFÓT| ¨ÚÜJ룓×#9‘„û`ÍzXpbmš™¦ÏÒ[m1ôœê#É1Ƙ°EMª‹$G£m"àïËZ3Ǧô*•Ïðe­ÄïÝ0ÈqŸÀYkö¿ \[²/,"éÕHÓÔí :r&¦æi”hœ†öá‰2ªl5ÓfÇÞó°¦3׫k‰c„Ò|t*'>Äqåz‘Ší—Š8ìñTj‹u*µ½œ3„]Ɇ<¨;{‘¨UbNªSïRëÕÏŠRpŽ¥<@o¤¤G“êá 1J}-¸XýSyºžnèWŸ ^¦îöÛ?íQèÜbï6†©? Þª>ܤþ¸Ü?PjûÛÞ`£Û ÀÜ¥n‚›UEø $Õ+JE„aê¥ðØæ¯UÁ¾ÛÈP‹üSÕÁ=êüàê¼ÁHÕ!Ûãœàn»¬O ¥2G­ æ¨3˨3B¨$©NæIuÒ`ð‰Evy%娅(ϱKÐP¢žƒlÔ‹–»—¸/rOuOv»óܹîqî÷Hϯg„g¸'Ýãñ¸<έgdÊzÓ(‚‘.¯@.üñfrØk/Þ¶ßo|'$öH8Zâwßä­gwUdUœQž9½*tš)f3c¡ÂÓ=9ævü晿ȉšÅÂʉ֚“Å%«ßãw¡>~W (h¿®·ù¾P4šUk^PIñ»áÖ­qÓÂwÁÀ] µæÅ¶*+ÔG Á4… „Ù)¶Ú¦Aµ ¼@-$Pë§ ­”úmµB­gcA8ÔS€ *Î"Úh«lt 4|0ób˜†z‚˜ •nr±wqºi;ʱ•–B¥S4Ò3± =¥mqðk±P\?(®·Å‹¾ Š?ñéZüßðšg}7«‹î¹®;Œ[oL7cÄÌÍ]¸Ø¯[ªi=×Ý#âv[ÚØ"p¼Ù¼Go™×é!­g‘m÷-q·/ÒC=ÔnˆôtÍ¡ä"c‘}YÞ¹aëÊð)±6ŒµrëibmÎVŠXl»oÅ ñ+,b…E¬ Æ;–¨>ܺ`ÖªÓ> endobj 272 0 obj [ 500 ] endobj 29 0 obj << /Type /Font /Subtype /TrueType /BaseFont /FZEMKZ+Times-Roman /FontDescriptor 271 0 R /Widths 272 0 R /FirstChar 208 /LastChar 208 /Encoding /MacRomanEncoding >> endobj 273 0 obj << /Length 274 0 R /Length1 43092 /Filter /FlateDecode >> stream x¬½|Åù7>³»×Û^ï½Hº¢;I§jI·î¶Œ ÆÆ ,ƒ Ý6½À€ ±é-“„’‚lÙF6œ@ qp~`'1%! NbLëîýÎÞ$C~yßÿçó?ygfgwgggžþ<3&”¢'„'Ò™ç/_+,6¢æ7ì8óÒ‹C?8÷æ©„ÐûQW­]}þ‰—>ð!š'Qõ®>ïŠUÞܮɄ˜B®½ü¬•ËW|zûrÜ»éj<ßr*´»é›8߆óØYç_|yý7lÏá|ηæÌåßxqó=„ÜþÎÎ_~ùZMX…gïX„óÐËÏ_ù‰é¢aœ_ŒóôÚ5],¼­ºç›q¾yí…+×¾»ù»³¹S"ÄnAÅû鉒ìF"§VjXíתx"îW5Ñ-ÑUnÓ1Êe‰™Xˆ•؈8ˆµ.â&â%>âÇY€ñ®0‰(‰‘8j8jpÔâ¨#I’"i’!õ$Kr¤u¤‰äI3i!­¤´“2 µ¤ )!݇„w/òÚ'¥•ãOÅoàÚ‡Ä^%„{ wï©ìIBV‘=´uìïò#¤½8n"7ÑõÔ-×ÞI~ˆüJr¹ £c"×b ²t)ù1zº‡ìG_O&÷âïsœYÈ/q}Oéd y,’ï¯Eݽ8‰^Ãù¹ áÈ!N^§%ácjá%—Òké¿øehÿ^´Pä^(Í& Éä;êté'‰œO®&·“‡¨‰FJ”öcôx÷ôÒ£¥—Ér\ÝB†éSüášÒÃxò$r¹ƒl£õB¿ðÊ蟋ח֔~9¾…|ïFÌÔËr¯÷“QJуUt ½†>H7Ó=ôÜKü"a–ðqiUé|,‡YqÚj-Ó1'½ä'dˆìÄóÄÝè{-àû¾ÉÀ]Êçùü©üÕü&þQþ a‰ð“b¾ø÷Ò¥GJÏ•Þ,½SA{f@n†ÌÁH/"KÉU˜¹ÛÉ÷ÐêÏÈ[äŸ4J§Ð è7é]ô{ô)úú}“9÷$ßÂßÉï¨ w ¿,š‹ß/?)M/õ–ŽáûΠד›Éäûä1@Ü6´v€Î¤'Ðé)´-®§·ÐÇé‹ôoœÀÆmçü:þJþ*þnþS!.\)üAqi±¯xgqg)Wº=¾¹ôWôÕÌk%=èééäl@ÆZr)¹}¾cþMôüFùï6|ÁSxç3äYŒËAò7ò)ÕP5R?Íá¯vã«–Ò‹é·èýôôOôCúGÑ“×ÂÍãVc>á^â^çþÈ/âÌ?ǿο.8„¹Âb@áãÂODaVv©óåþcOÞ7ú@‘+ÖûJª’·ä+Í,=]z±´¿ôw`n˜ßFæ§®&›5Ø©_÷ÓÞ'†€73ÑKO£×a¤×c¬¿C¿¿rž¦Ãø{»é/è^Œþ[ô }Ÿ~I¼\‚ˢǧq«¸«¸'¸ç¹¹"¯ã½|ãÙɯĘ^ÃßÄ?†oø=ÿþ3Á(X…„0IX)Ü!<)üLØ/|©˜©˜«¸LiV~K¹Q†B†?~t:—Gûíþë1âÛ¹_r`„Œgÿ?§·ÐÏÈËt yŸŽÊoÁßuä#àÑn*ýô=ÚJï p<·’ÜBw“ÍäþÇôMîzò-`=ù)å΢õôfÎjx;7Dþ ÈØ|ù7å=˜iÙÃï¡kÉ¿é?émä|K?g'«éïI½™N#çqu åÓ=€0ü’@§‚Þ®f´W¸›û+w7ý„Ìä–¿þ[t9ÙLëo{è©äiî€Ð"<(,õàî…œ’^Øü'ǹ_v·Ïæ+îönžLF¯kÉÅd*=‘PúÕ3½Ð~:0óôçIò$å‹xÛŒÒ.ùøˆËÒï&à·€¨ùQéÛä§ô àñ6ª%ß!$'ðG;¸ÆaÁ¯˜^âŠg}¥É« X"ÿ™EÞ¡·‚nÌ"oSy°t^)hÜSêE?o g‘ÅŠÉŠ¨ñrî<ò3Õfå{ÊNeƒ’*®T¬P,TÌQLU´*uŠ°Â­0)´Â'»Â^á§Â„owë» çßýÜÂßÏßʯáçò¾0éçîsîïÜ_¸ÿáöq»¹r×ÒAôòÒË¥ûK J]¥Ö’µX,~Z|±ø“âƒÅ»‹ß.×ûG_:öî±×m9ö(=:ºôëgôÕâ—à—”N)P: |³•î,uߢñq2 üú èꘗ`l—‚ÂIÜ,*’"ù”Œ`„ÞÄõä ÀØe¤Ÿœ¬\Dæc¾ÀÌë+P½´öqœñ˜+ 8@#~æä4ÂJ×€Ó¾D~\z„_Œ6¶È(ó8÷ ¿Oj@e.šCþL»É_ñ·l}o{Bù8ÞºSùCò©ò!þK´¸“ÜÊMW˜…,`~”[Co+Z<4í*²SxŸ,&D:aé’“/:iበæÏë™]èîêœÔÑÞÖÚœojlÈeë3éT²®¶&E#áP0à÷y=n—Óa·Y-fÑd4èuZZ¥T>}ùŠÁ'.>Í÷fÒƒtê™Ñ3ItÊ )%ßB¦Ê¯TNTɯ =ˆÏ!·†¶¤woøÖ°HÎèOéWDW,?mé ¿mL4§ðÞiƒÎ+¹ÆOѸeêÒ›&^õò¦»Î±›7l¸)4¸ùÄ¥žõ†Y ½½hÏrñýfàÕßÂTQWcÝgŸRþ¨•Ñ鬦ÿœÐ &:%zÖ†sú1!ž ƒdáá­´³tx¦‡6,Z ¼ÑÞåÓ|[ldÃÂ+†ÜRÈ}ü•Lz‹h.棩RÐ&Vb¤Ë×ä’|;+ÍY86œ”õ(:{PBO–Fñ!m,YÙF6œÙ†Qǯ—â©Á˜†³5Sû7ˆ¬CIq1Úð)Á´GGþv|ÍòJ2.“q‘Ç€ ÒåÕò`*5˜L2¸PMÅD¢Ýòys&}é0·'ºV !Ãð‘KñXoGc³Y½uX"gàdpàÄ¥åó9û•HÙTï ×Ï®ì®^±/fWªWÆï|·É:ˆ}PûgÖégu Rǹ¼²|}ÎIÑ9'ž²44}CTç,:î¬| (Æ ×*¥AëÔ¥¼—c çåå«€ÄÓN»'KõƒBÿ”2$¯V©Šr Íûg•Ó^m8\A”ÿÛCÃ¥Ãì)9¬òƒ©JGËÝœtÜùqÝÓoàç,¡áæ,:eÃíq×ç¥õñAMp2hˆå²5¾Õa\œ ûã ,¦±”©¸xéÞpohihpQ”¥Óu8{¸spÐ}P¼²Í¡-“œ¢]¼ÀtÆ]Tì<ÖÙÞ•u<ÌnÓÆÙëqRu|PŒšå²#¾Õmf=0Ëï¶Œ¥ƒ(’¯õ€u@ìü¿÷/Â?g|Ðw±S}ŒTú"Ó‡AZž±Kû½ËAKñ%ø§ˆ/^:¨”‡8…Ëã…¯CÿÑaü+7»x;8?…ÀÒÞëÊ? ÑÄZàTœ=)“Ž¢DäR(Å?Ô0  õ ãÚ¼Ñpïp©a瘮?ŽQõoèG1:xR’]M„¼ ý‰^<ÆãÞàJ6̈†flèß°|¸4pF4$F7ìä¼cÃÚéà'e$.íºÕ;8ã[½€Î³hHG¦l‰Ò›OÜ"Ñ›O:eéN‘ÐÍ‹–n…@?µJ/Cnꢥ”ñCþÈÞ cÙ´:"¬V0[€ŠL’JÕapI…p˜'Z¥â0ÏsJ8L‰[=ç*Wjžx¤sîhç<ñhç\q´“:G;ÙÑk2‡Íñ°9¼Z ÇBüîc’‚|IBÂn™îµÒž‡¤©#aI¯y׉ o(Ýú]Pê‰ÜìÜ‘>RiÈÅmÊh$ÑœoijtðüÂY³²cuǼy8Ê}~¢ô'è¯YèCarŸ¤m›©Wøg †ð³ÜéhYäNߎ×z¢.v®!nœ«ÕwäÎtYùmèýˆ8Rþ ðföî9ƒ¦WH-\È7Æ- oBÆí:WŠX bŠúîláujm)j6!ñ¨ü)â¤ð£,)ÿ®£v‡/áš›ó|‹E•¯ID#*¥Ýns:š[[š…ćo]ýäý¼uÕ“þ¶¯¹¿oRïéùå§Mêå>ÿãËÅ;ΧñüñWtÍyÅw}üšé'\ô£?>q5Ë02j0òôNâ)í–Dw¸àô¬ò\æáힸ‡ó€mu¸óø¦wPµF«ÓŒ&Ѽ‹{€{ûŽdÌ&ã‚QµÖ˜m¾ÖFmÒÌf›‰çmR¶1o¦ço' Zïü)76!ÂÍ™rgHóq“¸YäÅç¸X…î¤û1ÄâѾNŒ«xRÁˆôµ§R 9’ZׇÃpØ1Íáò<7™U•)oå^{­§Pè½›¥¯=“vÔ´'§*²_~·½>ÓÎ~INߩ˖-^ÐOÆ Nw-¾sÒÌæ+B×F¯]⺺h*63¶>öKíK:ÕíbrY;#¾‘«,1*ÆÄøÞÈÞèÞØÞ¸Ú€qê.äY.™:šï6ì5ðZª¤ü0=4Dyªèß¶)£1âætÛÄ™…†=5ef^ÎO8Iη.jÖ sswP2S¥ÖvAq S㎵JªôÔØ†¹Õ’Æó5‘j&çIåq9Ÿ}ÎqU«ÑÕÕœÚØeøô2Äö1Å@ŠGØöÍ= ⽪ÂȈ¹½Š£]íYPÈOŗȺ S©-J†øl`ÞܦóZ‘ˆM©Þ†]×G0 ñ0C1»=ÜL‹y6UÊÊLÈ—lJ•VŠŸÅÃÙÖc§pÉ‹:®?ÿ‚Ù §±!–ìZó‡k~þï™7³'Ð=çŒýô•ë§vιHŠLMÆ:k;·÷ñã‹oX 8½ ¸*aŽºÈs’N:Âý›~ææØK3² ù7È;nNÕáÒ8;Îv¯Ê\¡¸Â~yûº.F­1æIdfЗóq>_›Ê(é y£Ñ6Se05¸††ÔÌ6E0ÈñàP4.æ6—7Oj‡¹Ó·NšäÜìçñhÏó|!Ÿ×á¥;D[žè¨.Û—jÊš›²©‘¦ìHS*enÏ’Âhc¶%)³¥=»n¤ƒ•ê[—"}먃,F§¦L›œ†ÄlÜjðW®kíæÆÆQ¦a­•aäo=õ”ËþôÌß/Ÿ:)é¦k:ÏÚÜ7/snSkgý*U킆‹.ºkžÓh÷$;O[ÿÊO?˜Æ=Õýý•çï\Ö“ìHwÙZcßâÂ%!‹ŠoO¦;©ž[7yÙÉn•®3=}ò²}÷Í¿¥Lï>XFÐ?×'y®õoô?äç;}'»øV¹wº-nê—:šý`0C©Å仇lµå¼ÖÆòiÑ)™‚Ú£ð{)Ñì‹èš}=˜ž«}4äÏù¯òð:?µUE뢤J]Ía4ÈbµÙ˜ qJTUÔÔªJíŽÀl·Ëç÷s—Iz·Çæv{<~‡Ëïec½o€T.§S%yyÕO¹…°™¨éc°\û¸›%¬&£Þ=ß5èâÀNÄU[/iÕT¥R{NyDÌGÔ bq„9‡äXàXëplvìuhØ)çxŽ[@ j Å£ëÊd r‹üwH<Òǘ`Än´³˜bµ#£©N øi¿©>%\\´´»€¤elÜæK‰Œ†?w?f 3 Ï€¤iõü’MÏÎÞÂØ²|G¢Ùƒ!fS%½bÁ#á‚'¢3²«»‡´æòÝ¢]η˜@gǽ@õ>vŠwIQßæÜ'aÂ}fÛw3íÀÛ}x³OB[•G{ {@뗼Ѽ—%©T/Ȇ|´ƒ^HÍen?ž’7‡›©ÕLù;'w$=ÅFÉŒ¾N?žR|Ka<ö^{&Ó±°#S&ç Ÿä§{þ»¨–yÚ 2­0Âgð…drë¨ÚM|.€¡QSùûí®<ý‡wæ&ž®­zý‚9ÿÖhR¦&FO´@$“ †fôdžbQA¦wJzûLaYÍšškkøš]tqq§K–~åZå€r“RèGÆ)=iú,hu„™º$ øC(š‹òQö¼5hÈ › BÎ ú ¼ÁÚCóÍeÚ¼äX Pf”GA”=òŒëGG×õ÷›z…ärxµGp¥¨CÄ­ðB˜PÙ™ôÙáºë0ÈÔªa± WiÍ©aâ#,æpyØé® ûžÿb´øåëçu%£ 3ÒŠ]7_¿zÍ·C®ô$î"6ôÂä#±bñ7¿ÿdIã亮©ëeW]±a–Yjâ0~Z¦û0î‹A£™½ö)©[Ø3ƒ‹uKLK‚—™nî?׿Ct†n Ö›^Ö¾jzSw }H÷Iúˆn4­×˜Ü¦ž@OP'c`aó$sr&ÏkÌ®øfš1Œ} £ÅvjRŽ»¸¹ò;×ÊcºÙ0h8lP[Œlf øÆFvô}qô,¡bdÌÎÂÌÀÿZ*ƒ%KZyK¬©‘Y~d:€Y23úXÏúùçöÈÞs~O}ß½fz¦3éÊxÝgü~n³2tþÊ•ç_3¯û6î¹îöùÙл÷жÝІ'ÂM™.—qÍE Šs._zΩgm€#ŽÉ`÷•Š2½µ’»%©Ú˜Æ¨Ýö@ÑêtÃÜÿH fb3›‰ÎjÔb Ò,åèfZ0êÍf•v­f·†Óxìj¢Z«Ú­âUnDÕ1ÆÏ„)™= {¯fn£’§™aÄ3yÊ@€Ö â1à&c}ê&Å5/¹p4\æçÍa;­pöV¾£˜ZÒõíü•Åmö¦XªK&w6w÷ìØs,Ö]×YãÈáÛ`óL0ùÒMâðÚ¥.• Öjj•ÉÚššÔLùµêËk/K>P{GR¸Iqƒæéš§S‡‡4GG5êÞÚÞä¹)~¦Ú(9ÀGêhHxœ_o³—LÚ¡ÎU†ÐanDr؇)ã°ëˆ›º=~Áâö]1\V?6lç\¦^eJƒó²&Q&‹üqgBàØ]Œòç±tB™û^U©(~£­¾êE}pzÆù/ò8CŠý×ÁuVÕ¨Æ× MÂacyp%3œ7Ú~íZí€VÐzìs`݇àþÿ<À´fNJeªŸoiå £gat“žþÝcÑêèÒµlt»ý£o¾èN³Ñåà%&Â1xbê€>÷Jç¦$S Hµ¦V§¾‘Ú˜z%¥Œ¦èæWÓû |jâ›æ}î}ž“Ÿ¹µKÜ«¹³Í«ÜÑË“—¦o2Ó}CêÆôýÉ{Ò…ÙàÖ$é6s«[¢“¹©†Éâ,óÉÜR³!éBûfö„H*¾¼Abf‰›1ï6œî:.aNºãžx]"Ù,¶¹oµÉ¦4—Œ0ÆLÕÑt*ÅIvZUWÇôÕg³¥„䕬1‹AÒHzÉ  G¢±x¢¦¶ŽLËÒ$ Ù¥ÝL%& ^z<¥7(Ç]UáÓŒvM’÷T¯0aµìRðz<îJ-º‘J›S7Ì+iE³MÍîT]; yÜ6ÜiN»ó.Î’ŒK:BLpj³qÉ"æF¡EÂa-úáÞcæz%º8ÅA±C½’–xDO̳Ì#xžåþŸÇõ>“gˆ?‡†=Ìýc(³©­Œ§ W-íîlŸk„©‚²´n§ÇÌb¸¥,rÒzOñð8/pä^àHgž~SUFºPMܯφ _7•HÙ¦p¾IÂ07±nJGòì$ÏNòéˆåá05…ƒáeá‡Ã V,„çãô@X–,Íáaî’>•L«Sišó¨ML“ $òÈ$m®1à1™r‰anév½¤ózÌÀv‹® ß“ÛÅ-%À´YcÍ÷[ÖZ8‹§%çó©aîdIKs eîæ…ÏÈDƃQBJÍ…` ØÉb6H¡À,aâÌÍ…2à„Ë@Æ«žÁw„+jAo'¬ÌÁøk™ÁvEµ.›ÕnuXVAYÅÅ\ŒÏ¸r1Z«MÄhÔ˜ˆñ 榳ÖtŒÔèêb4nˆÄ ”ËÄãªÍ,É„ßVsÊ”\Å”n¤¸1[‘U6œÏÂÒÈLh¬ôRˆò`¾yùò¨'ÑtךÑÇz Òì‘?^± eÕšâ§ôŒkšjØTüÇž2@ì¹m꥞1ojS ÙÌ …þ»«®«åÄcïÅ}ùîË1v䟛…wàÉ~] _ÞJƒŒ@GΦ+jVÔÓz½Ì~qÍå­;Ý;üºl/ˆ³!´K2XkšyíÏyÎ[“Ò@î•Lʬ±`œo\f\c¼Ö¨4>Ëõ²˜+îú¡¸§ö´^ÈW.9ÍÁl±V‡Ö Öè—´bjd‚Șíóc\¶‡qTÍÔg›š[ZÈdýÄkÌ´A|8˜aV¯ƒ^¥jóèGã¾úÆfì[ÃÒreï> ^}(è±´¤‚Ãó|!>?¾,þpü@\ÅNYQˆ3¡”Kë¢i§S›|A_ÖÇ—|Ô×mÒPQÒ¬Õðšaz¤­—ÜÍõ$& R01&B-¹©eAK‹ÂÒ2L;h,ÇØ1x±Ys–Ín£å 7ßB-îÖ…e¹æ¡ ¡2 Š»E‚”Þ2à0 q‘ÀºÓ2 .³Á^ÙhSGúÖê['Ãèô‘u}xx{\Ò˜ ñ /–s½X¾<†=’Ì^â'ŒD#10óG î#’·…òU/ù[Ø­HÐvk »‰‰·YîSª·œAÃfë0ÿæ2P³®àD¨äªJ®¬ä ä’7„Ø#A–X‚ ¬C¡Ü‚¡Ò‚¡PnÁP(·`(È-Xp#Z0° ¬CÕ*UîX%í3‡Ë~"»¹*'Tù‡ÂÌ([¢¹9lnʗͰŒ“Èæ[z§c$ÿØ$Æ#B2÷àïc*@1²ÍlÏ´‹¡–ö ‰~Q|},p¼Än¯ˆÅ—‚ÒsnqßgC¡çoSÙ'â–È àÔVàTÝ* ÆzQ•aÇ\.LÖ:qU #©‚5Ã…T¸”klj"ã¸$#&† "7†?ÇYSÇÅÖñ‡& P98&‹E«oJzÂi—%–n²DÓM„ý,C`§`B/‡Xg'¦a|W§@€}m¥QJ3F&Æ‚MÙ¦þ¦ÝMB“SdÓ.z ã‚B¯§9AžåN†gH¨èy‘Ló,—tÞ`žˆ9^w~bûþ\ql`B«X3O,2¿"„H ·L®)0I¥*ÖFÑ 7Êì–s»œo)CpU˜•ïŒUî,çöËÇïƒuÓq¢hSÓ¥ $½ý{>&O¤_0Xý´ Híõm€£Ï‹o°Ê‰@ô+Dçß*QO¡ C“!sü2G'=EòŸbXUÿªáÕøoo©ß2¾•:X¯QUñ„òõzÁ:\úHòDùà ÃRó}ê'Ô; ;*Þ¥ßoç¸< 1ˆÅG8Tž´´¶µwLê”Ùædï_ ¨aÀÈÔ(ÉH„,ÉDªrXU\œÙõÔå}Ó˜§DhÍ·¤s$Ëf4ˆ™ÈJŽ|ô|!;?»,»&ûI¶”Uf³Ñ´e˜vm‹Ö˜xï0åa¥âÒ5‚ÆpúN—+Ë J·™ Øûzh ?ÄÓ]Ëd"©“SpY&ÒR-“‰º.©eBlE&J­»pîD[™EÉ ¹*œ;ªNÙ`˜JÉò*Ø. ØÌœc–Ý8õL¦im é”eŠ(Äœ9ÌÌZ…š«‰lÞνõÅu­?ûŠU×Ö:·™Å*U×¹ ø¼™1l‚˜™“weˆ²ç˜_AÒ@b¥žð=—– heÛY8Äæ 0VŸ¯ ^å âöL äËܹŒ*òŸÀx<3dƒ\Kfy c) ë¥å74]?醮Û#weÈÝÕôx÷+‘W ³ŸfõÉHGcOcoãå‘+•$«énÎÎÌΈ¼U‰_ášîõ¹oußÝðФ‡:ÕöðyÒKá7‡Ÿ†•šmajø†ðÞðþ‚¸uP*¤;ò–ˆTÛ‘ïŒtfŠÜŸ½#§ÈFž‹ìê|¶ë¬""Ì…ó²Ô Ç»¿K ?ݨÐwê»ôݼ”ËfÉ.L¦SÒ;99(YfŸÕAföà @ G¶™]öbÅaÆ‚¸Õ(Xƒž¶ú$ýs(2Â.éý_'ÚC˜e9ZEoâjëÊÔ;››l;Ö&¾í²×Èœ¯”ô>õDØ)SZÖéqxy~bŸ%£[JrÕXë˜Çó2ó0fç¤,‰d ‘fž”—ÖØÔ’'ˆÇA²h‹k9WÚŽØÂl<Ã’ÑZhÏ÷†Ï _¾5üPøÉð¯Ãï„?Ö›kY¿ÂáHW(‰™aÉ$–t… ÓqŠdKº¤3ó,™Ä’.iÎI8E2‰%…®ÎFAÊE& M´ô¼¶ô|Ò$&ÛÂ7|T2ð‚ð@xSXP…)ú<¸u,rú– lE‘ Í“?¾wÐzôÞDo“ ó3Vw!v«'í¢‘&Œ§M¦ME°†khM–ÊR’ElϲÊ\VÊ^•eëÍ û\/é.n¢ýMk›šø&(˜Û§„ Éú¥Öœ©èž¼ð“qã³ÞíëƒjˆX*ÈÆ©Âäœ*å\wáHÙ,³øÁœíÎã"nê+þûTêÖ·ÃâØï&ÑØyÍKÕx¡ˆ¼ÎǦq÷Vä0Y h‚“ÌQˆL`#Áˆ„Õy:øBW ªÞB »4ÄÆ‰å•¡bjc#;gCÆ‚`KcùV@C¹ rÚ ÛÔ:ò›áFÐ…ˆ„DÈ´žb<7Ã’4K²$V*;ÓsìEh_hr³óɈB„Í\„©¤¨zïa$dBÊÅ^¦F¡ÌÃÿ¿)U>TqÒTøbÎ*ãb’d ½½GÖ^×h+«xr•yÒÔâ¦ÕÛg´2`—tB÷š¹tðÏÿD÷Òvg­?Û®”é(þtÒ?‡Ø]÷ߺnurã4ÐÎû@;Ãäø2[E²WŽÛu¾â"a|‡IŒ:p£ FeÅúÒGKp½lÌÑð_7æ¸=õ å¸a§J¡qÀ ƒàd˜&§‘`…ÐlÒ¨4žhÙ´â·‹h YrɲVíË&‹&Çl,‘-S¶,@Í?@ŒEa.Œ lÎCúßm–d¦»Ø€kfãÙe,¥Ï±”ñ¤Ÿ³Bq»¡¢èËl‰bu$F1®üo¶—ˆX"^µé<Àë£Í ®ŽfwHB i`RÍ’Æ—÷5ò §ÎmwzÝ‚Çiw'q·`É1FCX’c¡•„%9ÉB ‰‰P—)'ºB.ɵץÚH6ænm¸µñaòpîþ†ûB~’ûqÃ_ /亻ĕ ç4Þˆîlx ñû ?j|³a£ö çÿ¸Þq¿Ýp Q1Æ{Lcl_&î•ùeÂ9ü !ŸòÔA àI«èÇ› ·C>ù_lÕÅ6¢©Ú${QuMN8T­Õ@êÈ5Ìl  ÌÑQÛ˜G¾{(ßQÎ '!çÞ‘|.·Íår;Iã¬FÚÂmîi”pC#»¡ÑåÄ NwC®ÑIsÒIÍÃ`NXO’ô7;eqr”<Ù–«rÏ'’›“¤»™t¸/±¹¹W{rŠknr¡«/¹Êuvò@­f®èadúª‹¼¶î+ôŸÉƒLôd"(s‘—q\9Q&Ï_QWÅfX†˜ÍH²2ë‘¢ì(gkïª×¡c¸H2êhN2¬uº]ˆát¹h²Î½ˆ:«£™ ºc¸4,y:šÝ‘¤+ïŒäÃa'W—L2,õdåË2 ±‚! ƒ›!O¨®®¿vmí@-_+MêÌ×zÒÿg<&øzšòAÙä(Y]ùLªJI—ÈHO9F”±¾5¦â”O&èå"³%¥Öá‹,²Èøä(cD!Ü¥ƒÏÀ5KB,)cB:OÏW”¤1••VKpû0 3Géª °%+¨#œ^®¢€›ôk¾}ô®2P¥;ªNíW¹³+Ši¦ã˜&´:؃ýÐ@Üd³”¾›Þ­x@s¿áÓÖ{í÷ºÕs°Òrºf޾[¬X¤9U¯î2×èxg ¯1ÂÅÞc®2¤SJg$-“6|Ö lm˜Ñ°¨áäÆ3ÎHkzfzIúúô™{ZkÙz9¤5¦ŒiUc<•J·gfD{2'G—¤VF/Š>šÞ‘þUÚtmúÛi®^ÍŒ\Ù4‹FƒÕIcS9®‘/áÈIãÿ"áŒkÄÕ¦à`f3×é¢ 2âYùðPSË¿³Öò—Ê—$iêIùàU™§2ÏgøŒ4µ9šÙŒéÏ0[^&-{íÒÙzƸêëÕ®Ö´»M¤d*Å¡ìå@ûÈ¿3TßÈòÝì=,—lh| º#ú‚)÷,Hõ§R| ¶ÔœúWž¡­ |»} ßgl+ŽÌŠ¢Ù¾!ELލš&]ˆb_7"GëTéá!X€a Ô˜kF޵”i#îÂuLŸd.ƒ^(×â 4654qÊO>Fs$ÍÞÖmògc$”c?˜#^pÈ(²Uþ¿jZ*Ä@;«&>®ðÈ\ã Qzç×5¬½û?Y»nrOý¼yîLwÏ%Ÿÿà×›{gœ>ùêË>¦¯‹_η訫½·ý 1؆8ëæíñÆÔtœ u-èéBÐÓôwÛk\±xÅ¡‡u!‘¥}Ùã†ØoðÑnå¿ÏÜí˜rƒ÷Æè†®»¦ha1=,8æ}¢Þ”×xt>¡ !åЀñ̨ïI ˆ3ž’5±3 W¯Ý뾯sGÓ+¦_6í3½ÙôA×çÞ£]Ffq•šð Ì‹xp’Ë+ØÛ\!À„—%® Í[N:š]`¹ò-Íæ\0R¨¹&Ì2ó3Ë2k232Êl†f~žà$íD(òp¬ð[yfé‚r\3MŒec‘\ÌîÛXëtç†0.…ä0€s¨¯“´u¦$-wÛe¶Ñ/­•¤M’ yfˆâQäû†ð4;ß§7)xÍuÛE]qXÁBaÚ£.1 pŸ¸@·Š(sÊ×”¼ò³™Oî„h/{|:!ç×0Ž@„ÿ2£ùº*Fà<9Té+Œ¬Ã¾]5ȶØ/ ã9Æ•Ë"q¼-/4tÉq/åå

9”ÕUX.÷ œì¬¸ïðÔá²?ßR@Øy%'­î©¡ÕTë3¨·A `BÙY8 ;“+@éxZÄñ-:ªn:´&&óuufηpkw™‚²¨C{ž~Öà’E}³–ÀPÄi$«=¯ù,Ç„wYXG˜ÌÑÖ?²øÌoeJ§^!ÅûÓâѩ߼íRnzG¦¾2±hô‡§Ý6mrÝ$nɘÏ>ÿwÀm·¡wîDœÿ{Ò3Td"bó4Þ@¹ÅÚ9K\m½Y\o}Æò2ýUä/T‹`³q™Ûi»Øbî°¨%Meů ñÉ_Ñû˜­a4Óùì8$6áÎ1»_¤_ ,ât›VrHPèìvÉ9A?üŠª©¡e£ŸÍjµT`‹ø$ §µ,M(CÞˆÕba§ˆ­Æi$b±¤ÑX¼yMÞƒ8X‹YÍÂD˜(<(‹*&¿hhl6Y Ök­­/X?±–¬ª¬•Z_ •56 Ć}åQ €¿aöP”EJ»!Ö¤<` `}Ì"È™¤Œƒ­o€Íc<„Ù#«vUÍŽ”ÞÜ*+u̺Ñׇ°I,y¨8eÍ E4J+Ë´*º\”[Eë² ¨µ§sÎ,žú‹âi³œ9TØ™KJ9ÉÞÅÖxÌÌÉ£âl,Ûþ x0àÁJWÒs¬+mç$.¥W[/±]žPS¢´ˆ„·°r/˃‚ì–ND–‚eùd¯e¯?@J„³d­a[Áš³-ã–ò§ZÙ–ÄÄ÷[ÿ`ûœûÔü…õˆí ÇѸ%D³‰—‹ÏçæYÇqu‚­ëM•× –ÃùQaeµ‰ˆ»£| 9»ô r+«œÌ ŠAVÕ¢\¶&+Æb ª !¯;—ƒM‹ëc¢yÕªÌsUÒÂl ,F…E¨èã{ûT®#_ºXm¡á’wÈÂQä¾gì‰xÈh=æì›I ˆñúvN¢Y ß·˜mmb‹vµ«R-kܑј3‡Ø~xV‹-a'‚¨ëÖÅÒòڼ߯áñ8öCt)}ŠØ OŸÚn µØ©Î¥¡v˜¬‘?#’€H5»"ݺ‘®‘¾‘>›E^W s2\2ÜìƒMs_{»Ù‚ä8 ­„ÛW2P®‰P»H_^ ä 4¶"gÊ7â*™ë+;sÀ8$ ŒoVfÃlÁ£œ!I°Dæ«È9&¨j ÍÅÓHÊg`·ñ’ò¬•ñæù†Y-™é²ú¶rŽ€=¦OȦA;kÎLñ>–ËÝeb{Z6}¡?òS,ʯ² Ýä%ÖU³Z›¬Ö‰˜¨TñQnõO?¸2ËðÌÌÐïÜiüaó4ÙéLÅžBcöÒ?q3FwñŸ(›ër̉ü"}·åº*ØXÓ¦†…ñnàßóÀ¿.~4?iŠ×¿RÏF¾0~*~øWæó¬òÍ®É_Œï‹ÞÏü%«º7söMºOÜïߨT[³ñ&ñ¦Ì-Ùû÷ŠªÆ%¦e™~ãjÓšŒ‘p‡¥þxÞ("±d5YmV—Õg !sȲ†l!{ÈÑg\$.ÎôeÏ1®WeÎÉwf_¤¯úüUö¥®wé~ÿÛÁ÷º¾ÈC5¡ÚP](J…ÒÙ\¶!Û˜mÊæ³Íú\M®6W—KæR¹t!Wh(4š ùB³¾ )h X@P0äÌ9K´!gÏ9ækækçëæëç¤Dø°˜ŸÔ”´ÃRgŠÕ/5-©¿¹þ¾z¥i¸tl[½ Ù]ØãÐX:&2ÉP±Ë$v Ñݸ¨?‹ØYe¥+TQÔxœw Èí÷&„ Ïý¨z¨AG°Yˆ /îI¦3™úãÕu9`"ÎUÛºº&ÄL²V*—«ÆJ&òªƒ°!Óÿì´¨n# L|€}-ÇÆÁë__¥NºÀìLÐh -ë‡~ÃÈ ÍØ(Íd»DLóÁ!TcÉιRÄŸµùýÙ`&“Ív¥[óÛk‰s†Þ¡†´bü)= ¸Ë"•HœÃaI °¯ÓäÛƒô0UÓçé0"—.Þ”݇üœÙ‚­ûÆõD„O[K°úþ5?ïÿ¬{¤daÉtù#©‘”Ç=:Úç:⨢ÑBfTçHyi[Ì#»†QdNbWÙÒ L4áWf‹ÛMl!J=æ¶Ì]iôµÉQË&&ß³Ë,L¹OÒ`™¹‰­5gµ’îWóÁš$»O®ÚмL"@¤Àg+ĉàf Ú°1žrƒÖ„09³ØCVŠÀ¿Ž·¼·UlÇïm'ø¾{AõX£°2T,–)Gk¼¾D­:Ñ—‹U'ÎV¸p™&Xñp#ôä®®Bχ³ ¿ÝÛÞQèù¶ÙNº¨ø\ç¤BÏó=›!󳃭ÐÇbWwNñLDä³ÐÏT;W,r)VNw øìz®‹pA†">³ëý÷]f_úmì"ƒ V“À– ™`–6=fy,ðÃÐ3oö…þjy?£^8'´Þ²>pKè–Œ²Ç2+034=³:pVèGÇBjGÀZàK~kÞGö™ÿügP©6¯ ^N.7¯bÿ=³JGVùW¯ô^â_¼!x¿÷.ÿ½ß îôn ¾ìÝ4ÁEbÎÏò®ÞâU‰Áp0<1Ø\ÜT1{×Ö™Í,“ŒÍ–©ÁAnSpocUCóOb{|$¹ jQ‡Ô™6¢N…ì=w 3[mÓë•KÁ®Ë…J '×H5z=·0¸# §¬ÌÉen鵨bB‘Õ?ååhñœÓ“×KHÜÙʺÉÇ?Œ(ÿõŸ/p‚w\þ Zë)[<ÈTÌÄõ Œ„TC³dBä¾®ì2}HO«¦A‹ù¸¦·j$yÙ£ÞÍW÷& Ù!A8‚fÂ'Ô`QÙb3CfK¨|î% H&`C™ ]ùšw‚i˾]XÀ¸!Š: £ƒAl7e^Ëp2)Q¸`;µHb»,ju°ìÆê0ÔoŶKr”bÅäÜ/bwhÌõÌÊ:uJ<ëˆx¶1%ïÝÓ‰*&*›!ˆ°uÀÇ/†:ï¬D‘uyÿ*þc]_V|‰aݺª$¬R †Œå“FúØbv©NŽ ”åÐÀ‹&‘ã åÈC91Ä0=pÁzÄ?²èóK¢,°íBÎva¹l>°¢€˜/îcqâ,‘ïC.߇\¾OƒBˆµ"·®A!Äîeg,ÀÏ vá™ÉŠBlI„%Q– ™ÂeØhä³;C[‘W?ù¿ä9Ả,a¬à`µ`1-ÂkªK8Øõ ×±ˆ9,å+Ž¥&3-+«2¥RY­²´ÔZ¦[Qî·eFù'7ŸšáhWOáÄŸÿ4“(ôü –üYt´¸·jÅÞÿ„’ÊHÓôHñO4`ÎG ²Êåbq7¿{̘IŸ¤#ÕØ’q#“㿎*ðê´¢FmaxŠdÒhˆ6¤ÍÁöè¶Ü»½j}.ï(V€2èûÆênܯ¯fr]ñ:‹åÂä/O`Bœrë¹L3G‹»¹×ä¾þÿµoZ¤PO‘·_Òf±.vQÎ6deÝcÈ1±sŠª \]yÂqrçèìâ0ëÜEÂ6¶ëó­Õ¾•ž½_‘(®‚o.†e˪ÎP¢ó˜·q˜6lù‚Câ•…íøüJ`ey!¤ðÝ/aÄ7¯‚9z?ûìrÃé-½'ÜÇ?NêH+Ì·ÍIÍÕSE‹£%ÚRHR…tWæ|ãÕF‚å½ê•¯„ÞPRmÁŽ+ÕH¾±@öþ²æ’­B××Ѻdk^oÑ2¤Ë"þ_„±ãì^–¦óÓ4®³Iì¶Ò"†ª:í@žæÃ‚΀q\²-¼,BY€²Ä"XéxSn˜[,YäØã *¤Ê1c[aGyö±*•ùËñѰê¬Ã‚;FàÄÍœRe! âØïIÁ7ìr%£ (ônb]eR‡œw˹]ÎÇ×-3¥ßÜ.þ­j¬±ýÖõ…›Ë&á<ÛD¤²‘SY4¨®‡¬‚$//(ïã†EùÁ¹»Nxô·TõQß•óלz{K ®Ýk?á»Ò ¯GÙ¬¾ê¬kNió6.éyvv®®îés®{×ÖPß3Lª÷$œ¢ÝýèÆâ)l>éWWM­ßîh %>N|œ<¦<*Å 1¶vŒ)c’…˜¤Õåå3 ^Iˆx%¬ÏUÿ/[1‘1=»*è" Ø2Ôš™"H1“&mmX«÷Z,Ùˆ 6̧¤Z RªèAeK»°7œ¶ ‚s>éx‰ds–7YzÍqÀñ‰£äP=ì òb~v›Ã:ój@™Šù8Á d/ç`¡`n_ÔVÝIi»É¡LC—CaþóÔ%Ca…&¬iIH Ò¤º6X1ú±==f/ºB5:}B_jtñ(ѰÉ%¤žêÕhŒãc\$ªˆb+Wlx¶ý%¦ò¸ z¢PZÝ^‡Þ ‚R,Ð o‹+ë0Q_lÃu*¬D<§t„#:å<™7Šî浃nA`/œ4žŒUFçµ J¡D.±6!$Üéß–'±o‹(^ˆÅæ#غœÑC o#}Ìꆣê•a,OÕ,®VË KÞ € 5‹ÙaîÁ¯¬Ñ©î¹/ lÄWUvK"8ã‹×¤A“~»«þÄoÜþô/œ˜;9šìêÛP<úÉÛhìãÅwð«£…Ù7ôt»,k¼¹}óò[=âÜîä´®SϼñÃwh0üÃò‰Â_*ø·VÊjuj£ÂÎ1RQ´Cb2¤ËÚ³¡PòÄ;IûÌ£¡c1–/êòIP1\”Ï\(x%+Ã>CT=M'9²˜¯£ +0[»^í°2쳪ÔÀ>ƒÚc[ .‘Ld ¼!Ün¨Šõ¤aúZ,y‚â|q™¸†Ôâ'bIT¿ Ç¿ÌÞ(£Ã,9Œ@F6Q6¤ËX&þm Å`yó EC1W´Æh‰ZâARcD3Á¦ VƯº¤NŸÔ¿êôÁ(ÕiÇ0HƾPØfÙ_a®ÚcW'àWÅ=•é2iγÉuVdMÜlGèŠÎ–@ggÜ¿ðÃâ´ö ˜#ãW´Œ^·Oaþò9†MXˆ­ûÕ^šhh(1=Ž]l¾§`¾Ï~ùIŒ>(i‡-ög¼¿ò ðñ”fûùÜy¶_)ßRî³ís¨üÈö‘û_ܧÊYŽÙþüÙ9BoHˆÔ–%hì5'ç贜ØÈò­ïcêý»¼(²óœí{Ê20WzÌÃY¦‘@9|c+rv;# ¿á²»¶‚úB4R…8gíò >¥J8ûØ3¡ïßzáóóumÚâ«÷ÓÂÞk~×4+úsö¾³Ïº/GO_pFƒ­#]ë‹O¥Ž_¥M=矰âÒ¥K–,•eÊ»1¤w‚4ѹRXåuzk¼­^áþåL¢¥ q?’þ?lÃBjµðµâª0®{~ÅÒÅ,åÌjŽ$(¸‚¼úKé =ÞÒU¾üS°¤-&MŸ™IsOBÒÜâXlemØO,+2MdE&]9/põ»\J—Ò´B£áV¨µ$•ûT1Lÿ*éYü)ö4#¤°»ØˆöËõE‘… ®Ÿ_‚ð¤Ä× 1bGYJ·TÉ< áV_byƒŒ¹‡pzÏVí®CJÐ~9D›Í6Â>P‰Í‚G«býWW5•ïr`\+ŒÒÛËRp”[IcL *ï{å¬|Æ+/,>Ph«³&®=cÕ\ÚÉ긊F†íì ÿdé¼õ[‚méì$•»+SÕ€ïÅEÂÏø±ÎèmR»±†kàTz§>li°L± ‡Ã/_^óyƒNôã!o6~þHðËð5_¦Žd>mÐÕ0Ú΢˜e9«fäg’—­óJY´Œè~¬ä…’™C«r‚ÈlÎ„ÅæðdýŽ´©.U‘+9ªÌ†ý:“1q¢#¹%[±Ÿ›—¨öaÍÓš4¯i¶aÜA Ôd5ó±§i¥:þ/±LâX04?´,´&´6¤x!DCîÆÙ««$ôƒ>ÌS%Š  ˜3ˆ~'"'äàžŠ[¬²Yåv£ÿ„á+Ë~å= “õH2’’ú’T¸.H3Á츀U¦ÿ¹¯¯Á› 9_" Ïà…gôß«­‰×Eµ1\‹Ã£/Ë^xEU¸j®,ä³Ð±(´‰Ì`"àgA®*ôDO}îÒXáO§Ÿ5í¶žßx~7ï¶Âã—\ò8;øù°öc¶ã¼G.f `Õ¼é4uîù-ufг×=öغ }”Ùß<ÀûïÛÈß$÷ öÏs(k”Êd…yÿþâU£“'k€Óé›û16 Š×¥GˆõdëÖK¬Ö Îõ™_d^ɾn}ÇùûÌþ†÷­i0ý(;hÝåÜ–Ù•ý¹õöWœjÁú€óîÌfëì?r>šQ­ÛßH6D66ÜnUŠÖTCGÃ2²ØzJdYƒê õ¯ ŸZyMÄvÑY^y%ò·È_£Ÿå´¶è¦(–2¹“ÂçÚÖ7¼}9÷:Ö[jHøAÛƒ‘ûrOÙvEwæ^³©a8Ã13 líÁz9“®ææm‹Oj¶˜ˆ±!H| ØÕÛzÔªB@Ìî¡ÔÔ< ŒZ°å»±PO®ž6ŸÎ–ZNjŽ ¢2žšž—[^–ÛèÝèÛèߨÔÙ$<îµù°#›EšH„'»ÆbŸ˜¹—­•ÇÞWl”ìÇfqÁ,ö ö¸ÊV6Ö´_ߺƒ™±Ûq5.f,,†íœ€Åäb}¨~sý`ýáz©?PÏÉ^È”æõ´¾>·Æñ0Ôa~³cÙA‡tlt< åX`áA’#Ñì48°îÃ!µ7;Ü͇m2ë#ëWµÏÌïÎú‹5ÿX9ÊGÒ›5—Æ—ùÙXe(aàØrf¦‘4Vâ5lXâÎÎ ‚Ùr‘pNòýŸÖ¾<®êJó¾÷JUªER©T’ª$•jS•–’T¥Í‹$»ž6 Ê@ Ë6‰‘‰ÕMyÃСÇÁ@kò5<Óm¥;!2A.±Mh;IÃÜÁC§)¥‡Ï@ÇmC,ÍÎ{e[˜îé}î¹÷¾[ç¼å®çžs.ù~§à"w›­Ir;·8·Ä”"Ñ+Î`c¬ÉÒ¢Å5KîØéfÆHz5æMNù¡§±•® \ù3Í%„yDóºCRwœ]¦cˆ°j±Ùãfws PƒýÏÑÿ~ªº‹|•þQwUep4öÛˆyT¶Œ:i[Ù¹_ú(’S B–ÝÆ:Kî!dËû¤ß`Ë cšöwê=hªÒ¿ãð+Mi Š®4ʰÏLGŽÙ?9þ–cKØyç-ê\l·çÀÛ¥¶Ø#…®Âé“\Da§È–$´|Í« úü@ïMÿ½öÜÎÜNÌtìCYñ7öÙ§a(ÃuW3˜ÁHªYè£Æ©f¬y›Ø´”*;^ 5 5fN¸AF€É™éc¤1‹æ4c¤YN댑†É=_gŒ4¤?œfŒ4Ló9ÍiÚ!úŒ‘æFY„ED5}º ôßáú¸@ÁݸS  ÅÊ6:1ÚåqR@d¦Ù>6“F>4 ‰²©|̤bxÞ".¨šQ+œp}£ÝÍ:¨Ø‡ÉwR ÕTRà¡ ‚‚€Š ‰‚)6Q Ö& *)8çÑ‹”ó„)YCA5ü­ÿ‚¡êéé·E›xMÜ0ÍdwÀCÑî׳¸%A/Kê»#6l„}ÄFÈû!dú²\©¥p!oJfçG&)£qdaµôçÉ—NNº1Ó(²ø´”ä¹1œ'Þ–VÍ>I#‰6ž¹ôÎH¹&Q~yö¨6ÂT‡¢c|¹ãË%_\b+dêçÖ2ÙÑò«åý”+Κ eËÂþÏ©‡>Ÿ-+´Ó§âuY@Œà ,/ÜÙ”ÚXm/í%+}ýò‚A~í+FKm¹îéYæÞÅiv’ò¸èV®VK2ÒcÆ'=OÖ?çÙWù\ýkžWês$¶œ. ²\Qõ[)oªq›w[ãïŽÆ]Þ]o¦ÑÒ”›Y”‰Ëq*c «Eˆ8`±Ü*ZÛ4­„ŽÎçå]ç<µ\«>•¬ –(ç]c]<²Å"ýLèob-ŸsNá+Ê’–„®ç©Ú9Y¯= û5²¬e%º»T:/­³CÏMÃcå>Ø©VT6ÔµI¦î2¿¥Îo¸Íbê6¶µ¶†BÅœ‚‘à™Òµ™tT»> 8ùÉÊm #ì#¢Äà(Ù+T +}Ø–½Ô=xé‹B(|ÅžjGXè\n 3p&|"<6Œ@X8ž€À~¦ß„A)-ñÓt§½ƒøºÂm»:¤‚ŽÉŽLÇLÇÉŽœ×9¢ðÅzì«u¨Kâ°õïêníï!'-G §pt¨+|mmk‡»'®‹/¸…Ñ.ÚÚÎü™NuHûäYÑ«!–}‚¦J“4’`„©ë ›Ÿ.ñhrô<0ä>"L݈Ö\ÉÁûÚNòôö Î È‹óë*Å/J¨ )¡‚%ô„%ô„ÿ!f…1Ul$âqš$Âq7IFûzqkôvõMÕ\Ý;Ä¿‘®Í%&êOY;‹2¶½Ôe>—'7¯"d •›+=ÂS™kr[K=’'·Ì£¸òÊ-H¯ØéÖYÍ"´SˆûžP ¥Ë±àk3B¦e·XJK¨iæš!"äi“"ym鯆沅8ŒÔüÆÆñ¨ä…£ ÷‚ùË?Zÿ‘M0Uø³K@þÈÙ:†õ•V·h–¦zü-ÏûŠúgúê%aKöó=²èÞäž›VÒ§áoT½ªÿæÙõ`Ì•Ô>Z_,¶áÊïÎÞ¥ <ø.wõxkÍÞUPÙN2r¡¦*¬WCF{¾UÖ‚¿P¯zVÞk}ëÈ~„=Ãi=„÷†Gã£jôŒ÷TàL홆S±S­Fc¹%,ïñ¿à?ZûVõnjÞrl–ûÂGÉ®b4«£=B¡#åjsUýùÆ“óWÕ\’‡Î ÷úiìÏ*Åœó8TFǹˆ{ª"~OÙÇîÛ=&—±Ù_ß|J®Rƒ1Ÿêð)Âg÷ù|3>ÔOò•-(ÿ\ ºEø#ôÉB0·®»òº¾©aÚE›múÞ¡¾uØyŒäŒvVCÔÅv’rÅ!©UrÆM5îßbÿÖj²:ŒùGî}¥/•¾]{¤á¨sÇšríØ¥:¢pë_Kµ®•j]!׺rªuÎZ˜/`û…*X‰òÕSNø®cÏïÿ7ÅTš¹©òcŸUÎi±K£¨uŽì•^o5šøª ÛgØ9[ŸuõLS›T¶ º_^%\¸‰Š¦6¯+év¥\ë]× ×œ+÷€Kr¹ÛÎî¥Ex3 ó}7 št:—.[ÅЎϧ׹Bªs¼ r¾Pµ¢¾©¸¬!Tª/ƒ$µ©A£5.V:¯Æi¢Õ–VGQ«®¥¨:ÉÃü WSWá© ×xPáê*pÝS©]?[áž©¥ª…·®icÖRõª¡€²ÒÙº¦Wµ §Ý¤%AŠ‘Úvœ¾ú0Í«j†î*2u ªO|›ßÏ~÷)©æË½èO”ÿ§Þ¯Ú9Ä5M>ðçk²UMhl¼eû+{%O¬ieþQ«jƒ;©ªaœ†õ0÷mµÒíÏú$é{FÉAó5TÖf±'ìòSö§ 15œg—sN^ó¥Ý’—ÜD“.´Wã5Ì:2P…°åå: } ÑÖBµ«?ÔZ˜_Æ3"ølg 'î„÷8Ý­Rm¾¤úóItk,s[t"c¹¹#BŒØ÷é$"ªŠ…á§ ™ÿˆ=ÜÄ»ëÎÛmÑ+œß`HbÅKù0Ô-­†±JËٚƂҴµ’{ ä\svY±ËùÁœ¥0(ì…2)6d üÀOuÚiöZH¯Í i KÝiðkÓ:Ò¦†ÂúæÌ˲Î;ê½J] ¯ôGžŸ·uÃÆQÊýãk—&š[ë—\²nÝì+gÇÞ±¡ïýÆúNô-ýòÏÕ6³ÃØæv”´]»'öpìO7þ²ñˆù°åHÓ1óûM§lŸD -’)Çd6-¨‰-ˆö×.‹æ’³Wu=™æ“}¾úÓ¹Á…bií2aŒŠ`UM[tY´ÿÞ¦GšN‹9é³ Å‘cUlæ¨-VjuÚ<.¯»,æh¿Ûz_ì×ÖŽæ[üÛöÓQŧ3U¥JK£Í" S•¿ÄæŽÉ>|û0™š!+mt`ŒÉ„ NŨ?â« kWéêôÀ ¥óõDR»Ì¿î§_ï‡ä”hªÖž¶˜ªE_»Îƒ0ެnmïTl(®­SûbÎX¬Qñ/4yû¶õèS ú’}²·OêSa¼Ü§.hë;²dI§±T-oh-½ÝŽú6ãWDWSo ?ÕšÁqÖ ™òS»´[x E¾úyLWz›oé¼m™·Î—zuàÆWß÷îÐ#ñ‚€#†™r¨9?º}õ—…ÚÚûtppíÖWûïê,òç×-²û†Éÿ^¡³´Ä^PQúîå·$nöVæåÇ}‰xmsMm}‰«¦¬ÌQ–¸øæ[.-¯ÈÇ¥æ» ¶¸ßð+œö“´;®ŠÒª7T"ü°‚òÍçø ­ëá®Åíl¨­•ÖÛfl2Õ\ÕŒ_†“!4IpØSY,œ´W2€Ý’õÎ)çAçŒó$ÆddRƸ3ÇI€IR›æ<3ºNîö°rùcd-¸¨Qô~ÐE 4©,ó®¤ÝQ$Ld%‹r·¿ÜZ^·îoînñÔtøšf3×8ÀýT‚z&‰'OØYë.ö÷”uFàŠ-ùÃÛ¥_ÒE,o°Æ¡S o*÷Æó'{ ìU;hUuÂó!åì¬m&„ø¥%¢YÒÜsA  £1m¹I XÞ±- :l˜²p>–"Ø·€ŒnvI¢%]Ò†-CŒ(4žX íÎB(´´4„òN(áB4ïÀ¡6Ø4Âìÿ ÈêÑy"²G€Á¥ŒY‘Iµø ï= ;¶{Ü>ñ´: kKÊ·û„DÆ–dÚ¦[NN·¡©“iÛ†²3kË\i¬=“ŠÒ:“ŒtñOS!'Ù:Ÿ$E61É®+´Ö†ŠtSN}D#ãN½¦pÚŸ|ÿ¬(m†‰™›¾x`ö‰#³Ox(*aËÓ5xZz!ßË«Vì1wÉ´1mqeÕ XM Ã?¡^„•UožpLËf¹@1†Ü²W) =Rô=çÛòQÇ[ÅGB¿“?p¼_|,dß)=,?\ô¨óÑÐÃa#ùkž'ÄŒc¦xNœt|„C²ÆÛÉú½º‡¦®¯€k¥2œß[ÖVÀâs|ºŸ E³¹~AÓÀœÎ’Þ…);ÆuV&¸ëpØ‹‡Å€c x— û6×É¡ÐbyAèbyYèë…ƒ%ß,ºßùºô²ü’ã‹^u¾Pü?C祹B'6ÎesÈvK•ra¨$Ü!µ„Roxµt«”HÊrf¨ÑŠq«x8xÖN¶«hƒK{+¾ê¿N‡€ÉÖT¦Lúø6—~D¹ÄÞ/ÿðì6ü9ÓÒ2»:×Ú iÚÐzŠÞìà‡6w5Oè4ÏÓ4^©[ÖI¡± é:ØŒžgBú¶jÖLHÙ‚Ï· }[­Ñ,H«B50UJ„"W9$¥P³uŠ";ÔªŠd3ÎS¾IµWVz

 ,f˱FŸkö9éÒÙi©Oד¨]l8ÓEsåÝè[з”H£jÕu–O-r±¥¼D~ÓrÌ"w[ûJ^/Y o‹ßYeGI¾-ÔzÞU8³×Jç*W‹°%h])V—ŒYÇJLže‰ý„„Ðö.ò¡H¡38áÒ cYh èàAœv),%f›µ‚ÊOöÐy6’ÀlôÕ,*±7Ý^Œ-?ȰڭIkʺÃ:a±š@ïïñ~$Šn‡°yÀ ¬dv—Ò¸Â>/}ÃÈ©µ¬³Ôyü$çØ¼=ƒ!>ôš±„4,ðAôÚ‰ýKë8¾9»æmG+Mبûg½-ë8½Þ³_Z~1äLÐ⢟ӱOç}aMyãZœžÍSŠb›ä×݆,Ä1›ŸI7àf(–n—+f_`ÃbeàµÅø¾{~,;Éç·pŽÑ^ Ķjûÿ.èü~˜‘!Ä?¾vÿ‡Y¬YŒàNI˜¹<]ÀïŒOœyßúµÙÛæb9sö ]¥¿¸á5q=%¤×<¸p/àQÀvÀQÀ÷¸6w ¸°ñ_—T€P tD®÷¢ì;€“ˆÓo¿ òÛ+%¸öO€ßiqÁýÌ"~yOÿx#`) ðŽË€Ç}€;Qî]àÀjÄO_Xxð×<ëÜŸÑD3þ])~#]%Çäc†e9rÎßmÆÃ¦¥¦o™þ`Þj¹ßz‘õó¼ä¿Pðbá¤c™ó½â‰Ò_¸Ž¹GËÚˬxÇó“ʽ/û¿|5ì _~:ŽäräãéöÎæC]Nåc<Û‡£€8 Øx `›Si³w*½¸½¹‹éªFËÜ8ð ù8WNM_¾ï½q@@r@÷nò8Â#œÂÛ+@¸° p‚r@â£ô‚væòQú²•Í]—Q–8ÌÔ?oèxŽÿNÇ÷èønߢãt¼ZÇÚ]~„êLwû‘X¢cz J£ê3nÒqHÇûtìeü§ô`ËDW­ò'¼¾åC|Éñ¸¢ •¯@‚Þ–r’é–+'™î ûè~Àt?Ýs9ˆO¦‡”Òf‡¯KU¶£ölÇGÛŽ{ÙŽW=¢ì ³`'rB;ÀˆTÀsHå-\y ]ÌŒòêÏÔÙ ´|€@äÌK)Ê/åatåU~ ¯Mz£¨iTƒ4ªA÷>£­ÃLë0hƯƒÖaÐ:Ì´Î¥åÊ´2êÝ«ü"ÝCèçÓþQoAW“Òò=¨I=x <„OéÆK:ˆp £Fuãj7Hv£D7¹[ä(ýJ}Wé”W£gó*HnWê/Öñ"%’nŸ€‚#Œ;è”j¤ª‘ªæTRUHU E‰!¬¥jàà*%Hi|D_ºÈÍõØ—ö‡ôHcsóóŠ_^….•Šø§ûú›Gº¬Jî§òŠj¥î»Ê±OwP)O75óÏÊÓËúõú®B¥T^Ç¼Š±»èO'p-p‘޽éÊnï>À»_At•+6¼m^• oÛ†WcÃw¶áõØÀÖ†aC°¡ÙPlx™6Ô#Ût>"÷Ê/¥«Zví—_'äÕU²Ï/íÊ9‘#ï2œ0È»” ´NÈòã“ì5ÆÃÆ”q‡1ÇkŠÃElʴÔ=%)'ƒ¯ÒðUûê}ý9öJ»ß°WÛëíýÆá®å›ñ‡åßIþœÂdÇ+ÆåFžO~a ¡ ÅÂõG8Á±I„S;ˆPû ]ÅðŠPå•<˜(œO9²ü¶¼Ž¹ùä£àr¥bUvT~œsíò[¸jÆ*``€Œe'—y\>‚ îq ÈGä›Ñ°¼ò›éÖo×ùMy5§_•_•_Á¿—ñï%ü{/´€áe~ª—ÄAù%1À‡üÀzÀà oçe<Û¤ü*Â(B0 ò/‹€Œ²(E,δ†ÂQ¥üW°Ø NÛäÛ øà4 mòfÀ­€-€Û8g=b›8gb¸€ÓàÞ¸pàfä¤ÀcŒy¤À#)ðH1x¤À#)æ‘’×#¶°@)ðIO |RÌ'…&>)ðI1Ÿ”ü5ľX >) §)ðIOŠù¤äAÄVVˆÏ0ø ËOˆ+ÁKAìZÀ(` @Ï3 >Ãà3 >ÃÌg|†Ág|†™Ï0øÀ`@Ï3 >ÃòÊ.LTÁi˜9%Á) N æ”§$8%Á)Éœ’à”§$8%™Sœ’à”§$sJ‚Sœ’à”dNIpJ≒à“d>qðiÀµ€QÀ€ž&qðˆƒGœyÄÁ#qðˆ38xÄÁ#qæ8xÄÁ#Î<¢àQÇ<¢à(xD™G<¢à(óˆ‚G<¢àeQðˆ‚G<¢Ì# Qðˆ‚G”ydÀãæ‘ xdÀ#Ã<2à‘ xd˜G<2à‘ óÈ€G<2à‘aðÈ€G<2ÄCþ+é1ù© ­ä ´–ÏÑj¾¶1‰6² memæ ´Œ~´´”N´˜ÚEÚG=ÚI5ÚK­"€ÖáG+ñ¡µTÊ׃æu 9&¾è â®?ÇÝ÷8‰{Ý…{Ž_;ìÇöàŽ;qç1Ü_î³÷[ûáî¸K?îÖ'ªîʇ?ƒŸÀFÀ@ °W*SÛ03ú0 ètb€j@øÐ–÷Šˆc<(ª]¥òó‘'=Ïáäð6/á°ŸÃvµt ïù¼ï ä¥ò†ò†ò– äµä='ÍŠ­ ò¾êÙš÷ÐÖ¼{¶æ}}k^bk^÷Ö¼®­y‹·æ-ØšEÜ'ýAêDÁ¿ãðaÿ+…â ?ãp†Ã«8ìäÐÇa¥Ô™Î0;ÿ8í_‚ç>•ö'ާý×=‘ö·z&=#Ix¥¤ýW!÷ïÓþ@×§ýðE$]—ö7u§ý=@]OûcÞÏý{ ØBðþÖ¿ÑûkÂ;å_ìý>好»ø’Õ»ÑñŽùë¼£Zöê!´Ç»Äÿ¤·AË©×rV™‹Ì{¥}j‹iâ™&FL1ÓDÄ4Qgš›&ªLp4á19s¹öÜü\[®%7ÎH ¹24© QëIà4Âå%9bî€#59nÇ\B³¦re,ÀGöËK0MX²[^8U¤,——vK˧^+–_ã›úd0¸W²\~åTN°[šr,ËWvG6¹–O¹—O ^~åš½ò’©ñÞå>üM¹Wpò`ïÐT˜£{qdÐÐT³Wo×ããˆ÷ëq”šZY¾×4·bjQdù”yàkkvKÒƒCHMÉß••köJs”uwù”£J¥’ä½ûrÂsw?04$J¶Ä!=]Z¸xYïW#œ9Ò{žŒ·„´4ñÆQ6ïO±‰mò¶˜¼A]Y>ˆÌ‰Ÿš&úLøZ¦Ë3õðòÁ5Ss<˜YŽï8胮«——ôõî“—Z³Ï=)ÇûHÙ6îžÄCž-‡ÆG9´M .'BTN„¾T. /¥rÕ„´r.˜Wnw¿¿¯w·V¦ŸËôÏ/39¿Ì$—™ÔË(|ÿL"K§h‘ðsÑ"¾÷óË4^ÿa™ê¯,sÞ'˜랟þwRÒ>±BÊìîØÒ7ì öF¦îÛrƒkjüŸoŸè2tÉ7¥„G®¹öÂWí•2Á±Þ©Ž`¯o÷ þéüëS[èòŠ`ïn±¥oåšÝ[Ô±Þô uE_ ÒÓÉëãëæ±ûv–Ýîøõ2›ºžˆÅ‰W’÷%^ëèr’x­#^ëˆWRM2¯¾©õ ¬Ù+º‡ `f<-[-¨õ#åþ¡îûú¥Ü:ü®­åû±ô\X#CS¶`÷T€ZGCWC]BçKùÈ.Ð/¹¶vøË÷Kë—ìÈ. v 4 þúzÿÿÿÛÌ›þÿ™’b³Nh³«ïÆÞóÿG"ôD›#›ð?r+hi‘Ú´y³pÆæM>Ü6R=R?Ò¯ŒTŽøåM›†(óy¬¬hÕCë+ yÒfVn×Þ~¨ÿ® ¼ EÀró&h‘ڱÇV’6m¾%nÅ þŠ¿ì SáläVxìû?ÊŸ€+ endstream endobj 274 0 obj 28627 endobj 275 0 obj << /Type /FontDescriptor /Ascent 833 /CapHeight 600 /Descent -300 /Flags 33 /FontBBox [-192 -710 702 1222] /FontName /BPCSUR+CourierNewPS-BoldMT /ItalicAngle 0 /StemV 0 /MaxWidth 600 /XHeight 450 /FontFile2 273 0 R >> endobj 276 0 obj [ 600 0 0 0 0 0 0 0 0 0 0 0 0 0 600 0 600 600 600 600 600 600 600 600 600 0 0 0 600 600 600 0 0 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 0 600 0 600 0 600 0 600 600 600 600 600 600 600 600 600 0 600 600 600 600 600 600 600 600 600 600 0 600 600 0 0 600 ] endobj 153 0 obj << /Type /Font /Subtype /TrueType /BaseFont /BPCSUR+CourierNewPS-BoldMT /FontDescriptor 275 0 R /Widths 276 0 R /FirstChar 32 /LastChar 122 /Encoding /MacRomanEncoding >> endobj 277 0 obj << /Length 278 0 R /Length1 31632 /Filter /FlateDecode >> stream x„¼ `TÕ½0~ιÛìsgßgîì3™$3ÉÌ$Lˆä†°LŒ„E@Ä’DD@)Q$hI­ÚÖºójØZ>õÕ*Oúj­ÚZ©—XžÚZIòý΀úÞÿûþ¹™{Î=w;çüöå\„B:Ô‹$/½vq©"+ åuø½¸tÝZi4óç³á~„„w—w­¸vÛéÇx„Ô«âö¯X½aùƒKg­AÈ(!´PµòªÅËþþó«²­JÀýU+¡ÁüWu wÂqdåµkט¨YÇ[áøñÕk–.î7®¡kJáx×wñ[øcz^úÁâk¯â.u4Ãñ/ฤkÍuk™Ùì$8> Çëºz®ê*|ôk=B«§A¡ ÃFÿtˆGG ”ÐÜñ¥ùí´0ˆE\/ R# ÒÂÝßÿÓ#2"™Y9a½Ù9¹àÈ<ʯ²ÿŸ;ßxƒ G•ã £Š*õŠ£J¬þ~3Q~^ænúÔ±¿Àï4üÎŒÎ;Ï]ƒÂ£«ÆN1´0SÊÁSîE{àigqz ¡èQTZÑÝh*z= ý߀_ƒq†Ñô8Šâ"¨ 90‡v¡wШ}ˆNAšÑŸ±žÓˆº`t…±O`ߌn;WiPú%:ŽWãÙ( õi¤§àÍ;dž`c'ÆÞ†£Ÿ¡qdl?šµ`Æâh3ú1ÌÛ*ô›±óÐßZ‚Ã7áO`:Ñ6Çö]ƒ&¢Ãè÷¸j³Ðîmõa´îz;ðÐØûc£çXŒ®‚'mA·C !RÎ4p{a6cèt)Z goDï` ®`ä±øØä±]Ðúú’¤È+ŒýH¡éhº=³ñ:þ†µ8†Ÿ‚í·ø îmè[3ºmD½ÐóGáÞ}è®ÀÄA0[€NœÛ‰÷D'q3nÇCøæ.3Z7f³}<6†JÐ|èáô¼ãÎÀ5ð&ĬeýìZ®räfá2ôStýúñg˜÷¿¡àØþB~H6Í{|ìCè‹ peº -@kÐ:tú9@õEôú/ü QÕo°/s¹³cwÁÜÆÐdè{ \=ž½ t ÂöŒÒ„%Å|)¾¯À;ñ½x¿ƒß!< ’nò)3À¼Æü‰­â¸±x’Q £yh%@à‡0ÛwÁxG/£W± ÇpŒè-¸ÿ+2‘Líaòù3s+³“=ÏÝ6zjô³ÑoÆú€’¦Þ͇Ù|fá¯Ø}HâUø:üŸÐó~rˆ10"fòL=3‡igngîf~Íü;ÛÃ>žËMçsO ‹G0ú۱汭0¨Ó˜TŠr¨ðg9`Ó5п.ØzÐMèfÔ‡~ørÚ‹ž‚q?^E¿Gï¡Ï¡ÏWÃÛ¯¬»ÿ¶]x~¿Œ_ÅÁ_Ñ„`Kï«# ¤‰¬ ·Âv79IÞ"g/³”ÙÌô¶›9¼Ã"–eǸJئq;¸Çø×„„0MX¢zýüðHÉHûÈŸGѨ{táè½£/Œ~<6wlô?ŠÊP9ôtôràà#°= ˜x½¼öJ_¿Äs€ñNl(¨Õá©x:l³ðe°µÁ6/€m1^‚W¶÷â-ø¼߉ïQ¶ûalà'ðØŽâã°ý¿?Ÿâ/ 1a›£$NÒ¤#m SI ¹¶d l]¤‡¬=F’cä-ÆÂD™2f1ÓÍìb~ɼȼÉ|Ͷ”M³µì\v{ ûû[ömö.À5r+¹ÝÜ‹¼‡Ïñmü*þ~þiþ ^à…Va‰p“ð¦0¦Š·ú7÷a€é·iþ |geד÷.œL· ·ÁŒñd³šùóÜr|–‘𻸹š¹fìa¦‰üƒYƒç’çqˆ p5ÌrtÃO‘¿säcÖ†çOp‚ý1>JÖ0 äðÔß±6öî B䨆lÂCäeææ–±_¡n7~ŸÛM~‹$ö± ÷ª·‘ûà¦'W“h>›ã¾AWü?Á­‡ùžDnÇ%Ì›ìnô!&ÿÏâ{kœÀ3ع’ðSÀqG° ãnÔ…ïA2~¿‡Æ3á™DÐ z\:Áñ›ŒµÓ>â±áVr–´1Ïò'™<ÆÀ%þmÄ Îî\øE? ¸›Ä§57ù®)tðûs£ÏRŽÍ½Íí<{ˆ)E—£ ê ¯¡ a›nC•è8ààí(CîG7õâeÀ÷gÿ$h¯Bi¬n逾mya'!à…‹àÕÿþÿàúÍø t–€²†P‚¥gî`3uÿÝÛ2ÔG?Ewñ‡¹ß¡ì@ˆ•Fw–ÿ ] 2ç?áýnT ý[€bK¡×pæn¸ã§£Ó Ûmè5LÐ&èó$ óVvpÞ{ÇVÁ¯5dâ«èê±ûPÀîò±[Æv Ec]V Ùcÿ]7vU¡m\;™Ë¥ØðØWñK þˆwßž†Þ~ÅNô)l¿„þOâžA}ì€wÖÝ1ö{ë ‚ZRô4º}ó6BÙÑKÉþ±&¦ $Ôûè²±ÇÆXƒVŽ­Îû,zDà€÷ô"?÷àîv9É@“ÈŽÓÐz·!yrÛ¹nÒ%µk ª«ò¹leE&]^Vš*I&â±h$ J¿Ïëq»œ»Õb6‰Fƒ^§Õ¨Uϱ Á¨´1ÜÔ) Ä:ØXxÚ´2z^ ‹¿ÓÐ9 ASÓ÷¯è}‹áÔ÷®”áÊåÿãJ¹x¥|ñJ,Jµ¨¶¬Tj K'¦„¥A¼à²ùP¿sJ¸]Vê³”z¿R×C=„¤FçÊ)Ò֭ìkìœRVŠ÷k5 ᆫ4e¥h¿F U-Ôá®ýØ1 +âh¬ÙOJCp‡§4¸Âp+<†‰6.^6ÐzÙüÆ)ž`°½¬t7, /@áÉÆ”r jP^3À7 Êk¤«`4h‡´¿t¨ïŽA-éLé–…—-¾bþ³žÑ8`JÁ{§ 86žv~{77Ìßöݳ¦¯ÑyµD/îëÛ& ì½lþwîõéÚÛá$ÚÔÙ×/¾àÔ<[‚w‘[Ûçà[á…SqtW…iKç*i@ž^Ù·ªãî@—op»åcc§»Qê›3?¨ó„ÛOñî·¢¾Ë7tÉ’ëûgÊJ÷‹¦â´î7Ç+:ýw+WÁ”Ï)5årZk¾üâ¼bÚ£ðôði©=™†1M »«& ¾¥`úá¯Ã]ËW¨:ûÄhaˆx€‹Ša©ïoàþüû-‹Ç[ø¨ø7DOR,¹ˆh 䯑n •()¡"4D¡“”ã|YéºA2î%(`úP+Ìíâöš4L~0HÁ»cPFKà` ÷²ùÅc -ñ@r:Õ>@:陡 glmôLï…3oï R¬Û€*vñß(Ú-+k°ýÿqúªâùæÙáæËÌ—û:Çq¶yÎ÷ŽŠç鄼Á¹ñ.Þ>ÀFøèô0 Þå ¢ôŸ‹6…¯îœ¤}°4Ìg<@kÄÃ(ü½bÁ…çуù:ú,6Ê+ø¿lPP+-Xj;§÷íš`pœ¼þÿn;KïRŠooó@Mj|TÅ1LüÞñ÷º§ëcšçw"Ísôõi¾w® ø^__SXjêëì[<8Ö»$,‰á¾cÌ|f~_W#p¬"øÇŽïð 4ÝÑCY‰kÉ š¼?Œo¿l¿ŒoŸ½`þ1!éö9óL:'·Óù" sæ÷W™yè1…D0Øtœ—£v¢€fí'øòµÉóÇ’ç1H#ÐÊaŒ\*ž{ÎÄà$Rãkð•È™¿ª©½TÈš– ï^‚­¨ÙuP‹`i ö¨`gmKK»üaº+Q~%%7[ræêªl¥Ãn²oƒ[.¯ßóòèÓŸ¹çÃþ>zfÿ=‹{öᙯË/™=úSàax˜3;ºO¶ÊÎNç^ç)'‹œ²“¬……ê-`cÔÄ÷‚îÀ(uÔÃÈ #¾ô€z¨)°ÑæÔ*aÀœþ;\>]6 FÙ”Ï7û{¬Ñå8N"øôøô¦jg‰Ã§EŠ=uµ&s› èoÃçñßR© Ððpw‡%š5Yív‡-˜ŸDòt è œÅ3‚–Ú+FIç»Fˆº£“Ù{è›m=ü$%¾ŠäOw—HþÅ7ðÁ0OÁýx¥¼Epj §÷’œS†‹îŒ~»=)Ô Ó…'^–² T  œ×¨ÖšÖšªý™a—iŸvŸáUîUǯï8Þqž’¾f¿vØlØÇº8Íew9|NAíÐ:µ¾œkªk»c§$8]„8Ü.‹×3.Âñ øØ¬‚…ÕB7ÔjÙª«ëUcõ “•u"çÞéÂ{\O»ˆë8“…‰»ó &:ÿ ¾SÖ#þƒË"ËËf kÄ‚l‘aPn$ÉR¯ÄtJ{%"¹žÁ_ë±,[Á³™ì$σ û>ù+QWà8‡1útm§;fë±k‡G:ºkëFº÷ó”aÝ©ÆÏ«ßPÔÑÝž:m2; dÌ…‹—ÚäºÓçÛ µÛDnÓK†—*2¸»§ hŒR˜ æÊçT¼®t¦ƒç"+«ªª™§?‚PÚýƒe{bQ×>ò^fÆ£_OÂKVÏkrcnô›(žŒïâæG¯ï>öÊ›ý+VüüðèÙ bE }6Ðù\€g%žy iÆNÐÔƒcCr­®P¯nÔ4i›CìjœLNHʹÎܹS¹¿k”ÃõêÍáåOFŽEŽ—¿Zþ~øýèË? }ÕMW%ñ  ’ÓOfpfÉf8ÑŽíƒxÏaŸœJç|ƒ¸á ¨O&žÁ+ÁM¦&ÿ)k[¤_@òà€ëq?´—õ–‘þ²½e¤ Ú/6ÃØɇ²FÎá½¹¡ÉU2é¨lyÞB,®,e9g.°œÓ”ãt wt|:ºOæ“î©î6ÒE.TUžöÇ4F–ÃÁH0dy.jˆÅ4À^ÒlÙì7B-¨/Áu9ŸY‚zå7bm‘á¤Jn†?…ÆzPw*e¡`RÔ®+¨°ZÚdWøO^á>±p˜Ò!…¬°²fÿÖ‡çM>¾©·ë®Ñ϶/M]nÓzG´dù}aw uï¥RËži7w>¸’±ýžU- îÞ]q䯛Ÿ÷•ª¸:^»{uKó_¢Þ¯¹rkËŠÍK>.½øjýANØõ؈õ²‘‘¸D‡m0]̨9³:­±:=Ëëô@W^Ù,¨¬‚ R1¬ÀëÀÓ¤ÇúgðOAªiñYÏa^­âyÇêtì3x:PŒ /—µjµ‘Á{˜§ â¿ËN\§˜wÇ:edŒ¼,`Áeøu×*0ª‚êG"•u…´X Û°8ÒSk*˜€™™ ÛÊSì&ñ%Z5ÀÓz:pGw¶…MaS0³P`æØ‘GF^$×ÿà‘Ñ>÷£Ñðò^fËù;ÈC#Ô…Þ,œß¾Ó öË ¿`±¹Ýµ3·™ß컃½Ó'äI>ØÆ´Ió‚×x×q¼ÛHŸ»Ïû0ó¸zoøTØˆÂØ(šÌ›Ý¡²ê ãôÊ&)h•V º=^Fp²´î9(IAËqà&NÆ"ìâù “ù8ž„DVK²¸W$¢+tÜ7Ÿ(ˆ}ºX½ØAçGAïÓÀx RUAjàü”ÓlS•§8˜0DŠÌFÖ÷àÒ#mÁ[ȉ®C™ ðÐùdí5ìó2×åã:Úq‚K±˜ç¾¨-óG`žqÌl¸tte;V?xë¼­—]·aãšò°;žnžuýþÝ;®}³ÜÌ'Äwß>xÍ‘ÞxõìJoJ æöo¾ñ÷5e1Rüœ°Øøé{ú¼\r½zæÃõ;ÑO¢<ÏàMÌFv£ýV[«Jðv%\<#-Rað#R ÇbFðUÞyЉ8ª¢4ê1L®La$›µnT"—¹¤³doÉ©¶ÄUœw8…,¢E²d,²¥ß²×"X\Éo•ó³FNk* »¦¬¢c¸¦ïÂ\Ò‚#‹2nE“)õFÕfŸ×ï%¼)ªEÕaà¢g  ÑÄ–`¯YZ‚B:ØQUEÑT(ãPضáo§šŠ)gŽTe1o³^œq̽[{øšHÿw¼¾â¦×w,~î.lüÇ5#¯›§6e§ÏÛ~û¦ØK>BºqQ> ózâ9*GSâpª®vWžÚfØôÒ8úª×rkù­ÜVžG]“=0“–  ‡qÔ9!þï䣵àTÝÑ™“õq3cÿ|Ž}ÙSÞ©¥ôÏ€?ë Ó8çŸxo”·c¬Î²%ñ5Ù¡^m¯®×ÝëÙíõeŸp>â~,zPwÈ}4öLüeÍËÚ?èíÒ`^OÜê¸]ïpGõQC3¾ߢ¿Õð2LD5¸<`Ó‹ðÂøÙUh¾š¬ˆ­Š¯ÌÞˆoН+½)»“ÝÉõ ½ª-¦-æÖöûÙ{Uw›î5?h4ö/ñɲGTŸh?Õ}bø$þIeRЫã5¨€'TrSTH玳ÊNt(Z9Ï•Q­Ü¢÷Õ«»«ûé/u8²ˆòržÈùÎüÞü©<›? 'à?%@"šŒCvô;‡+w1Î\¨¢~Na,çÏuuŠôØQP½2•ö‡LvVe‹¹0(æ‚o .µ–,AåfŒ!D¥Ÿ*æ){Ù”6Áî[ÍqòD:MgEþâsœ.þ‰›¶½ô’¿ŠŒGö¨ F£^ÔøÕÖ o3ZD·Éíñx>> ñhž3ósJ™*WÊÉb³+6»ýÅf‡Ò|À¦ò}¢%§7jááã c“8Ýßl7ÎÛ¬óý«Œ+Ä•þub/»ÍÐgÜ&n3o÷ßxÐø ¸Ëô ÿ˜ñ˜ø+÷1ÿkÆßˆ¿öýÆÿGãÛâgÆ3âÿׯˆ_û¾ö—ªÍ#& ùü~¯Ú ñ¨í^‡Ç®"‚Ge3Y=¶õ~£(‰~¯7d­¦.¦Ncà yU6¿•À÷‚@¸A|XÖ©D#c³ÛU*µÊ;ˆÿ)«pyÄ ›Iæ`‹ûÉç²A’ ­†³Æð˜tMõ1t¸Ü#ÃN7UL¨5D•8ØŸUe¤v›¡¨lë0”;SÛÀÖI9‘8ŒÅ¡ÿ½ß&nz©V¨…EAyYüÃ= ™…1€ –|5Îâ¢MK™F\K˜'FþûŠÐÄ%£mm®ì$ü^¿]è˜=òÉe…Ä>ú¿òVK<¢Q£3óöŠoî¿ý2.e˃¥‹°žDFþDeD"lôCüdÙ$g þíèvÿöì.÷ÏâûÜû⟸?œÖM@ã²TîÊ>y2û¶ûíøÛ [3H>>h\QUC‘ÆÊÑRþO›#—•ƒ¥°sùs•r8;/7%2%ºÝý~+ònöèÀFpT_)26Þã¶úí{–)¯lŒÌÈÍÃó] â÷“ˆÄš6¼ ÒYÓUÓ[³·Fåθ+[# îˆ?áJ³¸lI›Q¢‡ÆÝ…šgñoQ-†ø3•ºô\ <#À>`™§:ºæCLÙ'¥n#®–bh½$•êQTiªNMÓ ŒC1@êI¯U}:N8ýXp{\Âó±0úl,áŒeqZ¨Èâ°?–er¸"ËÄ=É,ÎpåYõ…²È_Éä³à>ñ^VÔ¡¢Ø`ÌàžžÔÓ}Q`B ¬E#法¢~°ƒÔš‘µSÙX”—‚©('Î,0îlZÜûþ‡#½Ù¶¨ÃŸ•%3~±ôÞÝ7Ü]T¸ë'—¾x|YëÚîÃÏÍ}qç¤ùrÈ?ùŠ[¯:Ö­ ÷0«,:#GoXþQê¶Ìºáqû7k<¯o¹k žN >¿pFàÕLäÉj§IšIî5îò?l|Ø|ÄxÔ¬Uù¡÷ Æßh[o¿“é³ÿŒ¹×½y†QëK|Ó °Ï¥U¢)âU‘;L<GƒLóé.áeð yÿ0]D,2õ‡wê÷è‰~IËi«šìƒh)®÷=mÂS‰˜Ü2  ºVrb£3à$N=œÓ£Ë–*þÇTGâüª§œn`O#à8÷QÝðçç€ QçU¼’ÍÃëÀÓÆìQÞ£.C:ìT.® kzpù^„\QJötw`KX™tb³š) ª<–¨2cŽP»ŸB®šým 0飇¶½»iÝðý[³!°|ôì3£Oë;‚ë~õ“%fÕ­å®;qdûè›ïŽ~Ùßý¸õðãÿ<~þ5<ç™iv‹'CuXB}†µaí !0r»Ö£õÝ&Þ#þ^äÖ‰ë¬ÛÄû-»l¯z^õ½)ªœ&³ÕçgÞæ¾ÝO*>àA )<ú`Øtƒž¸v;Ryk[̸¨–d̲™3Žýù¥*óô0¥ÆIuy°S¥0î S[˜  =:zt(î%\'=òJ#ï¦÷ó»C‹Ç¡@©qP¼•`¼§¾RÀò-Ñ.™×í7ÚĨ5æ7zçb· v>S`.öX\s/€O@3ÝÙĚm¢Àã0ï¸%8XÂÙ¹»—Ò@r=.yaß £×ÿqóÜ3¸rôßÏ.¸.Z¼ŽY½Y*ö>÷»ÑŸ{s‰7A¦… OQR³0D{æ<‹«ä:9¿Â{ƒ÷ÁÌÎ}™g2§òª¹®.¾Kجڬîå{…ªju$àñCÑ€' «d:%ª ÁP{TÌ m‚„xà=‡AñeÑ#©rT&Rwùˆ‹Ò Õ#>ϯקRïS©ø}uÔ†QhxÖGr«ò¬uåûJS²4ܺڽO­æ}ã™ÝšïåŸÉ#Q–¨ÀET€%†¢X¥1¢+²;wêÞÌSüŠJ–ØæÙ¹ŽÓ#°Ža`dŸƒT‡bTïÀ.!bA½3âðçHü[ hJ)ÇýÏØ¤TnpÄÂAê‹Î*Þøê,89)‰|ËÝ(=Ÿƒ,œ’µñ æËÛFß>ºnefR}âúo>ËdR’Ù“amƸ-[™¸Š##gÂåkGK½áÄhý‚¸CJOÚ4º/êå¥L÷ÍþDtô×´ÚÀû@yX`JóèÊpÉþDz”Ä(™£  l~ž¸EÀÏ+ÆÅa¹X°XB~ŸKŸÒèS`éÛ]þ¿ül=µ «u+ÖÀï‹€c|Šù xÛ€Ç)ð¹P‚à øÈPl¢@0–“pØduPE”±‹Ò©(Ÿ,ô<~øïm-úhǧü]¯‘J3#Ç3sbN½&”ù/}ØÝxÕ*ÛgÍkFó-3¢£sW]fg4Z!mdVë£o-j‡tQŒ o”¬xÐ òš BSA™2˜ œÈ»‚‹M˪T †œ9r<8V<¦`Øl‚IWA(*.U \,½ÕRw©zU§T̘ gT­ªN³H5¤:©bT,½L¥Ì¤jpì‡è½P•} 9/–º‚½ÁSA&l v™¡àÉ Yü'70Å"()P8`²‹ (¦ŽJ}7ÝGÿ÷”Ù€Qñ ±+SJÖ<3>S¥™ i¬˜sÁ ¦2ÑïÍ §ów+óTÄiÐ]!»g&j òà†ç d5›ÑõäFú̆üƪç5Çõªk6³å0aU¤\EzÉv¹Ÿì’êŽg7ü^ÿ‡J½Y‹ç¸Êh[ån´ï5ü¶R¥É‹§ ¨ýúÈM«ëÔ-ê;Ð+¹wЗ9£ZëÒfpždåÉrkã£øaòˆ|„Ñ L> >'ñ›6öú ’¢þ¦9«ûRï´gí¹\e&7ïBwëï­¼'§÷÷ÓÆ¿Öß8ņlbÈ &î´»WÏ5fP}Ž­ÑC mÁÐexÞ@ 1»ÝµÏY;qb"¿¤¦&™Œí‹;žçâ„SÕþ„5d2i¶—Ã]æÉY'ë[õ¤WÀ£>H¾–KÓF…– ÙZ6†~…–ýJ£_Á@ÿî)ÏâZ`Z‹±ë‚j{Aš9÷ÐГâ6¯@R¡f×x…–Ř¡Ž4ÌücøQ³ ¼ÄP8i9n’Apã[Ëlü€:‹AآùàßÈ–Õ§'—5°í)ê?ÖT9íú:d-T‚ñˆXECŽ9`( hކˆôhh?” ]ª½èÂã–õõ€{ùÿÎ[BJ©ªF4IJчmBÃbËð¹9oäÆš¬%?ZªLùÈsß!¤ÉåéÒ€Óz=NNò”Tð—¥ÓVδ&gG7¶ƒ ‰;±~c´ù{|(ä¤< ¤È²ÑÅ–ÕX\˜ð;ÂÀ×ìuMÖcŠ,Ìlö(ЄdXhXB–Có qòÒ'í’žŽI:Ä?’³†eUmä ?ÞÄCöjé’&àƒa) ABž FèÇ^“H¼a¨€ðV“Aò’œ¶ÿ)\jµFA‚,Y4»ƒ‹;Šz¯¢k)BüõçÕµœî Šp"Ü“O¿c\þ^4.l1ðºÑùW$t{opí7eçFmŠ µ|õ÷ª1ñ¸Uf5£ÀCgžm2IbF”EF–vÈÀʹCTQ Å\w'©†/‚¹”ÄRS.IZ¬¤¶`0ÂC!Rh¼pg£À×ÚHÈ•Xüó t ü|0í –hPÿH R½´ðaŠ*1>È÷(( Dp™¡Ê¢j³Ëœ„lÁÜ‚f˜¡æ5h•y£ùAHÿ~6¿†ÿ‰Í%œ í¸ÆÝÀÇ!2öøA¿¹ŽÀG+ðÌ@*Ù[ Õã…G9:â*€VE«oËFsÁl7Cfƒ ~®‚Úh ð˜“Å⇭"CZJ/º?(V¡jܵ1ŽCáÿ‰eŠIàÁ]Ì%cðÛ—"ç·xb-€X‘&^2Ñ7‘›y^` På›íì”ó¿ºpÄ<ÝXjQ_"hø/ÖƒÿB‡©´Ä—¨ ·@@<,ØJÀ;a#*AeFn|²»Ë͹§—S€O•c¹| œì)*?YΔ—bE¡ÂŠ8ÄŠB…CFƒBoã“"# »ËÊñ ƒÔDR…´©qŠë˜E½…±¢·ŠávE‡/úiIÁ\”¢àôF’¯3šˆ%àŒxaw•dqÔÎøà¿búœ ²èžÈ†üÒDaaʶ¹Cc¢§ vÈßï0Ñ ~?Åíñø¸âù+Æaü olVnäÑV¸9ðùþ?þº¢§>¹oå}Ó¶Îɶ’G¯ï €ŒžXˬ¦µæ=i˜ªÑ<Ô;ÿ¾fº^¬è[Z °O !òˆ“2¨¨2—ÛBØ|kìåðËeÌôÈceÄp”/0{Ea5†"‘ñäºÀuÒºÐúhÞ&Ý_«›¢GcÏ–El¼´ßÙ òþy4òtÙóeogþZ6V¦‡•EØMÌ €oEMyMfyäê´¦";^l xŒÁŠ&<Ì;C0lx¼Á°LJ£‘Hˆ`+˜v‘}D"BIòŹâ I«Ð)0ýM³@ž}ÞÜ þ±l¬Lø|^0cTfÐ90¿ªilÉ£àÓAÒ™‹UX®êª:YÅTåT N©”yP)8¥ Ùm NÙ”F›‚S¶ÝùÅÇõBÙ@´ã¶ŸØÑfw*Eñ)]Ä'(|¢°f> ù ˜÷¤STKwAå‚FŽÍ7à§¢§”±ŠŒ“b\Y…?ˆ–…ÓY\á‡]y¨4‹Â‘ŒT™ÅèBr¸(Á? Ne*’äzøÙÖB‚êéÀ“YCõìa±ÀždETÔ§RÁ ¦á¿øÿ ª·ãÊqç3±ÜJXÚ”ÏJz¿èÍÌ+h©¨Žø‹·Oì|ø)ììì[sþ‹WýâË{nÈÄFˆØ®û>rÖ=qý¦ÁØè·Íב»ñã[6ïŨwì/,|{™'»Ì÷”b#6-ƒŒ,¬ŽäR-¸…¨M5ƒ¸I>Y5¡ÊÍxØEÎE®EîEžÓsT2Tîծկ5¬3vù»]é®ÌvÕmÚmúm†­Æm©ÇÙdz¢YŸÕçôy_Ö—óå©k»Œ•üR ™,ƒÒ$RÇf\&韹KòÓôÓJæhçêç‰s“sS£ O6÷TÍqÎqÍq·W^‘½"wEþŠªÕF«MZ´ždX+ÕLLfjzÌ=–í‘û…ûÓ»2§‡/”¼’ª9[c½T5ÁƒÖÏÓø ˆÊmÆãžqYŸ ²d×<~ÿqøÊåœë+¤¼Öê VÎÒ•ؘZ) N;Úw¢‚ '¨ÇËþPB…4PƒÃ²˜6=o"ïÃò?ÓÓ¦÷M ÷¶ ìó§DšM ö”ãçËÿZ>Lr:äò7à€AåRyX-[þ,n‚¼„&%ÐB37:RÝ èzÎÑ$Èž‘žBZI8®£îÂñ tÑÜ4íÌ ~w$B­‹ÝàƒW<ðU‘Œ`IÄ´¥ê,J);µÀNÈÀ¡¦L—EZ]i*.s5’%Q30XUš§8HŒTÙ¥%`?à~µW—j—ëWˆKSÔ^…Ë,(R4`Öi,°c! ?*hÛ±âÚ¢Ùàˆñƒ÷@É•…LSÖOÀýEíÓXd;1øW·Ë¡F÷üë7ý¸º&>ú‹ŸÌ<õ/«7Lp¸ƒÅ©m{¯Ü|Ù¤ló¦å×Þ}Ù﫹:¤üö®wn]P¹¼Ôÿ¯kï˜s×ïò®@X3à¾t–_R¹L̇4F>@Š1òCvì!0òÁQC€Ø0„•Èw8ròäNÓ·Î,±]¥-)$T [ÂÎ fÀ‰v2È;¼È”4v·½ÄÎØ=¢ÇÀƒT0P¿O¸-_~ùeêÎP¼>ÙŠ8ìA6ð *¸8àð{ƒcÛd“]à%• ǧ³ãyªŠçíš0‚(@àb)hÚúö~£b˜ÑÛÚi¾ž òôȃ¢îõx>h£‰3¬¤j@¶^žûå´h>9޲qÑ5})¹rB9,>–Ó5œŽ›Õ+®úæ‡ìXaˆ‰«Ë#•«ÎȘ֖ùòZL¢ š>6Ìlgž†5z—0ÓÇs4¤:Å ['S©Í#”GUZ-Õ¨IEº,µo´f3iËÚé%püçCÔ… •s² ë¬rm¶ (¥š(w’n੟M–fr:Y ÕÉ>Ý›à”npìMÙO/‚4ÔÍNìTZÊN1êjKY”†¼È—Àv7ªéê#|3u§á€6¥††ÞK¥^ßov]4FÒ±hz`ÏÆK'z͇QÔÙj»–WÔàûJ[¦Ì0sëµ&×–U SÖÏl_ •Ö”WæÊæö'“S·Ž¾zËD« ¯pàŽZWigaš’;M”œÑṲ̀ìû˜Mã´ŸPÙ­6¤£)¬+(ÆãQ¶ŒŒ0uf‰]°txlèÅ WQì6QÏ BZOÔ$ÐH&¤AÃiz7­¦×Á‚©±·”; ò›£”{°Z-MàOQzV²Þ „x$Prê½ÔPúÄȺqñá³õÂ"¤Ä\ð§*(¾±PÔI`áV' ñ^ðèßÅþœ=é'ð*†FTL¯çÛ¬Ö€ÆI«0Z#ì©-É·ì´ »~`+J?RCJ ÒΠ¯/ZT©ôz ÆZë"g‡«uZßb8—ä¤ñìà^Y)/ 3rª@ƒ¾£ŠÂJŠœÒ<»¤<çá]êù–+í‹`MÍB·™û¼k„8Ût~;¹ƒß¦ëoõ=Lžr¶¼IÞ1¾+ž#ÿÍXÌð£ê‚ÑmW¿ üÚxVP±XÐo%Œú8,åwgT©›ÈTuK`™£^+ü·[¶»vY~¡þ…fPuX= ù7ò19¥;§±ªN  'ÒMK:wÔ¸¹³‰µ¢ŒÝFG`?Ð"ÛfÛÛû /mžßÑìpYYv (¼äi Ú`ޝð`ŠÂë*{ÂS0ÚñûfûNøç¬Ö^ëW‘Œj§ê}#ªdÎu© XÇ«ž4ØX´âS*›3š‹Æ ƒh ÌY6О¨a. þ†æ¢G Ò¬ˆ‘»¡¡XVÀ–ƒö€3•RþP>0+š, Ò(A‡œ0F?æâø »ÛVE‰WáÇoÓ† :¹¬ ‡ ‡$¨À¦O OñÈS<7~¤)iŠçÔÊ‘lPl`’¹$SA?Å{@yÖÅ¿ööv _Ô:!ÒÁ"3Í£©è‡¼Ùwñ²eÛÜZ°ýæþG>û¯#¼2² ?Ή®¥U³o!__»vézëö¿`üÎgXxíÉšù‘ òÍ _¶@òÛFî”"ªqꎖ)QÖ2™Êè2EÊ{ "là±ÊÄ**ß±æúSÙL Ô`¦-ãNþ8ägª!?S£ŠDý°Ê\þƒØsÀÌÓÌÌá!q¨îX½ õ¦@‰/‰¯Ð R2a¬ã„| ÖÆÑ{ CÈ#û’|ž¤JR·߆yJ˜PBVºñ¶¬U¨Qi‡n½{”ž2ÊJ‹ÄI_â^âDq¢Gž´CÚeÛc¦0StÓ\·2·ê¸Xœ.Ûìçû…=ª=êÝânÓ@™ZäO-*Y”"^•á_uWò ƒŒJ„ý{üÏCšª)uàT+ˆâLIÒlâ!A@Ä—Ü âw|u—¤±(ëIl6šÄ»ŒF¡Èz°³3§”55Ų®®XF*”R¶{ƒ¹~¦(¾"C†“Þà*=Îððõš¢Ä¥ò32PW‘´µP|Ôqš&µA ÓÔ€œ.¥ØYæhÜjEm±¨=áEqkÄ‹Ûÿ[Ÿè•ß1üi~F8&CU•HŠa¤ødC ‡lY~Ô4{ä½db²ëÀù‡»¯ž_“ó;²3X¹ìýœ™9òho¨4ILYBL«ÝþÜõSÊ&øóÁk-–ŠoMžFs//mbþúåD4µ3÷É[ÌöÖûb»ªHŠYHÖ•¬›MP _Î_¾Cbëª[®©¾>Öµp'äÔßâØêÜ™ï›tKãÎæÛZîqÜãÜÕ2Èã99_ͽÚ<´ðäÂS Ï.ô¸%[VÌ[« ¹ÇT3ªê<ÈÎTgx«áÛï)¨-«Z*9JµU3H$¨ „p -AÕÖí‰>}>ÊDñîÃóS½°f.•õôZóp$=d¨±EïQJ¸%×ÊÎþx]·8–©ÔÍ(¥¤3£ÕŠ­ƒX%[Ö¨ðfXã"êÕ6UžßÕ€™ Yçš¡I»p««Ö6þŠü¬ÅR3³P-œÒð‚ë2|Yi©qÖsLD®ö4‹ÉÈ1ƒ×dvföd˜Œ“Ê× ø¬ø¶L¾PÎôÎÁsèØô@ÛPùÍ!Þ¨´ÐK Bƒ@`s¢Ñ A¬ÜÎnIt%†'lÂ@¯„SÅT¾ÍT±O\/-Ì,”î…9çÒ[½Z]n¡aç½M¸IÑ)›*$;6Ú»ìo³ûR6Ñûì:ªØ•>ÚɯdË®:\™"­ ie0uÁÃÒ2˜R—/§”ðT(Ï)©´r”Ž‘¹zÁÂãx= bÍþí`¦Ñ<&ª‚‚¦©T†S=§ÅT7mPLýìOƒÃò@éJ*E(Œ|DED8Ls9@Ëèéõ *@Jz#ø~€œoxÉÀ±wèèûQhé¡V ’4 VpÅ6…ªBq›çÕ4Fò^ŸÃ‰¹X´²"[‘«`øúXK¬Sª' ïï¨;´ M´›`Ž»ú#¸3ÒéìœpR¤5Bdº‹PFPY™SÊ 5Ų,S,ÃQ¥”Ë]îœ3é·Ìé“~óŒp0üÁ):—ÎÒC)@êN°˜5ýàõ,Pžr !O ÙX—g®Ñéô.}Ä)§ Ðq°‘«jrýNÜêÄÎ.g?,ò?ëäœÂVÖ?Ðnƒôù eQì‚z C/.Å/:&!‘®÷€xýη,Š}g æi>÷…åùñ(N–LœXRR;ñ‡®Šúц†rZ𻽠¶r?¢'jKJ&ŽG¤¹o$â®mËï)•\ÆHøk0|û#à­ ÿ¸¨ÓCš‚µV…q-NÉuüN8 ¾T°*Ÿ¢MP)"&TÞR\7Pùóaªê¸_ž§‚Ÿ€,€¢Z‹UV¢ƒUHýï¥*/*y€‚Ö—¨ž*ÞK-nQpÏj¥xdÛàK’ãú]Q³ƒ®~/,¢T¨¸ái§Š¨¤ÓÁ7Šþúx+`Q{Žö;†gaÅ•º¦-åšÂÄvÐ/«ju`ÙÑêèttÁB¸½p¡ Kú…!œôóñ°5®¯·ø­~†^ƒpD¯TCKÙŸ˜ë×áVîÔuéúu{uguœî€ý;Èf„¢|} ~`¬Š–¥@ÿû§º»¿Ñ•›:ZWWî6œî,½á~ôMýÜ >ºŒüàT·àbø â3 +Íc~7®¯;ÚA'm튦î0)LÉÔ63Sœ2þ ”ÐÙHYF&¥\•ª¨nºpTŠWÑ9H¯jªŸZ¯\W¯ J½‚(õ3!]‚´Í¼pT¾RØTŠ€Ê?eÅ ™ú˜™)åö”r{ª(k)U‹”7Âñ›²–v¯ÚK ÇŸÊzw5QÎ+Ù›Õ&å&å&ê„)>CÊÐkàøÅâ3¤ú 8~WÖÒgHÊ3àø<à(ôE²»Ò•Ó([•¦Îi“é5é6ÜÒ¶¦m3¬ŸËO­pFKµàDäPê†aáXÝ{`ΟG†èu;(€tÿ»:Žêpà;õX¤À¶¡¼÷¢i#×ÂãáéZæ´ÍœSM Æ›$–Ò‚”R옔Җª®WŽê•£ú™0ŽO>*Ióažþ¡pY¥BI*_*g««ç ¾Pè*E ‚Ê?”³3g¶Ï'°~¡‹t/BÏ•P+RÆ|ò ˆ¢ôðé¡çQÓØÔ¿4ü2cg»ôäœPük÷ÈÞœp²ý¯v¦ì‡öNðe¤ô¸¿K*)é‡ä¨ó‡BÕITdmhfÒ?uFÈ”ô;áp*é—¥þP¸>éo‚Š<)ÜŸU?Çß6E•¬ž%’ ¢SçΣ€‰–ê4Zg9aj„Ešv‡Ã J‚ wIE²yÙX,OE&dªqWõ@5©¦möYóê#3gfµÎ"½³úg4KœEf]±Ús³:ç·’ƒnvâe·*ù •”„ñTFàk´¨½´ñª)‘Ó?ØÃÿ,˜+ú š‰úÒ8íSLQ”@kÒíõÑp,¢ ‚[Ò2D©áE=‘tÝ%ø!§dhL`hÙÅ=õÿÑ„<Êr’ñdyø,ˆàø–\X!þCáÛÖïÊ“,n]f.[™{“mÅš§wízMÕ%£µ–‰A‡†õÄçæ¯™Iˆ­¦i´bfAËK[ªò³Ë\Í£ë*݊쉱5E>_fŒ•,[´¾¹¹­æ¦Ñus%{ qˆaS+îë*—óÓ´©Ñæ+Ë¡11]m²¯´zÔ¶ Ê‰x&¶á+ï+ ^”S:0þþ¼,K.ò²¼Â˨ÁBÚ*”½Ae´‡)S(§ma_$©R˜Òx¦¶ÂTö¥{»·ë(¥ƒ}Q4P R <@å 9F)ÞŽ| ;ñ)ò)ð%ôI½9Iɇ¾*g *E6•Êú”$ò’àí²ºBVÓþVêé2U~!øE陈1R)¸K!<Ü$×Ç矋泌»p—(þ‰qfB9ðŽ—”Ýw}"ò•i;eE[®B©+¨(>ß¡) |›Já*…o¨ìÀþø6»Òd‡x¨Ûó9äS®ô) >å¤O(½X©ÐAåË£ô–d2ŸgJOÇë´§À*`Xï £Qd/¨n5y¹$¯ÊSÉ·Â*ô®|ž+c1]‘Þ•ï…£Í:3"Ñk ^P)†ë”LPyAŽêDÂÓ¹Áu{NAZ‘-´‘U.c壠äQÙ¨à︙ñ­^MÏÓëÜôfÅ)7kU0ΪƒÔGÐá@ðÃ𭲬_§+:ÓaDuFFÊK¨÷¼ÑÜkÃÙØáã¼ê—|ï¨yóÇI÷­l'ÛÅö³,Ï~®£ŽY·GGtýÇ4šE=©æÄìfø¼ä‚ý:ÿôýv:|oïW4~ßÑB±!ƒí óŸEn¦±ÈÊT~"~âùÎ!ÈH%ƒYS¨ ûÌð½!Kô5Q>f2Z%äÃn ÛÕPs P³èE {ØÙ´ ¹8ØÉ3uh$_®ÖŸZ6]O®ç7j66š×Û¯w^ïUAÞC1ãAíMüÀ[v¿¶hÐSSDqé›ëUUH-sp,C¶$¢“?¼fÝ›ßظbÓë³ó×LÞ³eñ¯žÊ<½{ÛÓ7žï}dÇ¿üðëêëvßôëÑ?ïý×swtRýT¶ÇQÀµ><Ži‰%F̦¸ÂvãÎWS¿«ÕẠȱ¡RäµPù¢è# (Jg@aÎÐ :Õ Xd`qÈ3Àl³÷lh‰¯‰oŽ3ñ„àÔ‹´îÕÞà“B4<ó}~KíbÊh¿c”„éãbpïõfÈé„8yè©‚º&E;3Á»ÿ Ú.ß•O‹Vs P’ü–M‚cL’':.rGˆ1ƒjb¬$•F™ÈÆ-¬ —àE%8êQ\Ñ…n ÇãR}ÌŸ‚4Ú“U1뤟9+ˆðe¬v"V í,â1˜å|y¾ŽL‘@ á^©_"HAû’NJœÔ™|ôbD¶¨¿ôœ†(§’±0Ü3Üa*ê)X´wá{q3AÉvl£l«èç¥Éo9B~‡Wá™×m¨þ?¥]{t[Å™Ÿ¹W/K¾–,ÙŽ%˾zF~ÈHI]9v¶‰ ‰ÅÈŽcÞ©Ú†lΖîæðpÚd !§”d9$YË2p —Í–RÉž¶ìY´lÛ%ÊžÂ&Þß7R íáŸ=+û÷}óº3£ÑÜ{g¾ùæ›5‹þM…öÂê:‡Ò¸âRÕ*ŸÓ¬Ç/5dæ…òäo¬ ‡–6TÜxim[¥@‘+ô¸Ú-&«œmŸ“~ƒþ²H·8Û_BQÑ_¢X_Å>%.¤+\HW¸ŠÙ¡\ y­$.¡'ŸiêÖEFSÈêÕÙ«ôü»z~ößk9ç•Fç]e¼6 ‚ï*©ä²cUët¸»kÁÁº1v‹Ó# O´S¿>eû5\WÜ#^kȤ«,*³×è¥ÊEÆL6N{«žß®¿G/郕Ʀ2¾µl‹A죢^Ð0$1\oµF#.S9M!¬—®…¢Ñ[NUÌð“x:tc…³»ÛvòdwÜv’ÌzÐŽzVä„aÉn¯Ñ, a¬/tån^¸ß¶7 7±Ø\ÑŒŽE Öè ÷h‡wïkÊky''ƒÿî3ðátø?|¶Øãáîð·ªw†Çù¸4.ŽÁò͘{WõxBZuflÔ6¸Íá_ø^õ›ÜrQ6œ%á}9ûÌû={ü{{•Rn ·G“ÑïT|'üý¼§ý“Ñåܹ¦Eeì¸TÆU^ ¶^•fÇ¡pëÒò+‹ËœÇKÊ\ª‹Û\üé<ÛW.Íg·C’dÑYC‚éËø+¬¦¶r¶µ¡Q]÷:ŤÑPPTK +½nçÜNÂØIÖ.h–A²66‰2V›–jÎËY£BžžñžÐ`h,${Bu!)ô÷Àòg*³:ˆ;鮉×þEZœ÷b°¡Ëÿéy' …çÇ6 æ®PjÃóÖŒH@±(Š…vT‘ PöR‘fÛ·ÅîòŒ[83+ÔÏÖxr”ÅP³/wy…ê±åŒj>&† “·0ÁÆr½›4Û 6ÆsP3-çKãç¶Ïó¿,‡.¦ ¤ÈÖ©9'ø„4!OXSvîví.ÙíÞç{Ä?QK´HšKRjÍRë¯ <ÞØ†•/Ò4Ê/÷8rʡȯ™$ /Ç´¹¯ìYÍin¨APXÚ)6l ÈóÁ»kš‚9¤ áhÀМX.ô”ÂÅŽL^P`¥¼°?€C¡IÂ;]sÂK$³6È6å(”ÁyÍ® iìG!ˆ—]æ=÷ mC:˜kúÅò§PÜË®Oa3*vçG/+Kct$¬-(¢]¬Ònï»nXµÑ£&øÚñ‘ wx (^¯û‰-Í›z/½[]½ÿž¥×Dómö\yòÒ/öÜÖR½¬¼¢fuß“;÷•™]|õƒ_ÛÐ|ãîå ›¶?ºÀšWŒw^Áü'RL÷3hõ_Ì>Â¥šóˆR±’jÉS‹ÜB×;„Ó!^dšÂÓs ŽÏ„ Žó0h‰‡šÃb [‹ `æ‡!|Ÿº“6<™3¼3kû9SW>Ÿœ p!ø‚’–òe7~Û…¤¿HÆá„C+ ƒ°Ôe-á…·ðµX‹¢â4tE”m)áz15Ћi‚^¼õ¨ MŒ ³CMÅûŽ/„\Áá(uýþ«šñ‹gº»gmBwg¦˜2@rŒ)¨@"·!É“’/Ý—¿Ïy¢ðDÑŒóC§q¢”ïrñöÜv%™›TþTŒ1vaq›© ‹.™)(9Àåºlmå:è‚r—P¥‹NCáãB¹°¿ äuf™áÐÂØ—[S[šÂž ìbÕéô‚sp2 —rÌ:Î8Î9 Ž÷a¬Ñ&i1 û½[˜ø$[6°1GÛ¶?b’0Çñúd€Ïf±lB6ÉH!/? Ë„ÂrU}”Vé±öÕS¨÷ÀfMË›oF˽+òCþ±¦šÎÊÔU/¨ÐýìÒ¯V]ü§®å[ú¢É>éoÑ­köCŠ~%aÜ~ç½¥ºl¿* ‰ù1m4^çOyVÞ•yʲò°9ÍAÝÈã ]v![ÖL‡ƒƒ†1XÃ@ÏK ö­ÜÃÂ’–W4XükGiýàcðÛ凨Z  éÛ‘ßÕR‹WcÅ(þ\ qŠ\ʶ"¼~ª"NA`·`¼3,Ý ·Éoê~¬ß¬ÓpÎø”éCóÝ–e– ¹ocý*˜ô|8ÿ%û[Ž[ n*|~Áââ;]Ãî­¥‡Ô Þu¾XàÉ…C–?UeGª¿‡\©­*ÙèG<Ž 7YJ«Å¹ULÿ;Ýïah›Ê…v¸H…%L€XËÆu]ׯ­Úpë¶þ¡uýw]7°­÷[ë¯Ù€””Ÿy/Nâø¦, œ|æ`ìë¬èü*7iŠÂnbæ$ª0«A-êØ"èGÙ¶*M8=c-NÅhƒ.Q»–]‡‘6⬠NÆŽAüÓr¥OÊs¬GþˆMÈï³³€r²9à}ážôó³òo§››#Ú xUàéòŠÈ1ŠH»Ü‘ãòo¥#¨ ¦ÜòÙtQ‰ˆy7ÝØ˜u,]–qLWVGÎ&Ìò»ìc@’ß•Ïb“¸jº¼&r>¡ €Ë÷BÕˆcÓÛù–$¦ÉoMF&Nȯ#þ—ò«h2ºìÕ´’A†¯È0ãÇTúó\6æ¹é¼üK ¡çp6 z8œtl@þG6 Œ“€ŽYAU h§ù°|õ<ˆë­ µÀ0èÐ„Ï üv¢òÓòmØ©§Êâ­Pþ€¼Gð§À]ð?‰pœÕ$ÿ~âYÿ~pŠ,¾þ"øÍòG^ÿà'þYÿòˆ¸n8ËÈCé2Õ–(C¼¨d¸öµM·>ÊqŠÎ¢Sàä¸-ÃÑ;Ó^¿øvN/pF Iw¢éw¢åv¢åv’@EÞq9ÍŽLšjyÒì@šH³­R'¡¼!êÙ 6ÀÈh÷!´;…§@g3€Ì¾º8@>ù.´cjµK¾-]®¢³Ý<Ý Eâ/Ê7¡©5ù&èšDÆ¿ö嘩#Þ4“—åVJÛ/ÒöOçäRhÿ´«4ÑêöDžÜÇî$È‚úXX 4:¹/¨U_×±m&¦å©£Ò¨<ªÕëꚸýäGxŠªxU³T¨É¯ïÉÌË‘iÏi]Ž–Ó‘£GåqY¦}ªq¹]NÊzÄ—Già½Ú°<ºÛrÀ’²ÌZÎXô)ìáŒáœá<öbê š¡ÃÐc4Œvcy?‡”ޤˠeÌ"Ûð®³À·E4÷Ë[ð5¨ v:´qáùF ‰_#‰f»á ”ÁgÎÀ}\Ÿé¬HgE¨¡V„2PŠéz€A€b¡kJ1 8œ @±yÅ„ç!. >>>©ÎHÿƒÚ@=@ ‹°sp¡×€^Ž«ËÆ÷€ÅŸ P¥8 ±pÕš­à)ÚÜÍwWp-OD4ô “þd0Yž<¨ðÊêÚýíÁöòöƒº¸?Œ—Çê0 Ö–×Ô©ØÃ§–«uãm“m'ÚN·é’mm£m2–ðf§ÓUuÁ}AâÏ¥®H½5q•4‰¯“Î26M²Z  :iT•Ž"ô(B²v èqÅQ\o¥xŠ£ð @/\gá’þ"^Æ?’^mO´à‘›&yÁõGDêŒkR„§@ωðvPJ ZùêlÚ“7S=@U $A@ÏNË›ðrØD9ƒªÀ 0 èäÍøÛ$o’ŽâïˆtÀÊ¢B•Áˆcö|“-a“rÑþ´  ºKи -¯Eù¼Eùçåû-J©œ%pÁ^A½š%¡<›PÚJEBAn `vE‘ 5å¿t a­À«üÙ«|êU>ñ*{•í^åj/]K¦¸®@P QœF´EÐ…šEU~®*›T¥^U ‚£Ðû"Z&h Q~áYkŒû¾È/à «H<«Pg$&ŸOÇê ¿”Ž­»˜Ž=öE:¶G}‰ÿCP¼ÒøçéÀœš(äŸñµ:òšåŸÀvûaøÏƒß ~ˆÅxü©tì>JÿS\ÿüO2Ÿ‰®û NÛ">3ç(üñìu?N‡· ÔýéðwQêcØ7N©I‡çº'ÞöÃtø°ñt*x[:V©&òi÷‰Diû0³¡š´eK„Iœß§òÕ™‹›Óaºª‰ €}Ý´Xˆjù÷³Qœšö‹/YÊü¢rX•.aAÁq°†¨¼‚ÁiJûïC.†gƒsêÇ^¤/Žã­é'Ô÷^Â÷ÛïïøÚôaõߎQs¥ÕÓá|^}Ãÿ¢ú¯¾1­Î†gLˆ8ž‘øsê9…´^ ߬õ‹Øƒ~Äâ§žˆU«ûý›Õ}AøÓê}á—¨l¾ñFDw…W¨m±Ãê*è“"Z‹¡0ͬ.÷[m@ð²¾vú°º(0CU©C‡ŸW+QâB¿¨Êõõ/HK 93¢…Ã8Po£ñZãUƨ±Zú¥F·±Àd7ÙLy¦\lœ9¬ŒÁB®©€6¯TÑð±À FÞ°­ N¸mx4b扛 O}˜ÏÂÉ0 “–V©u}#OÙaèzCcª¾ªuÆ8]jÖELÓ9ÅùÃ]𥤿ŸálCç Ÿ§ ûKpXv s^{ÿC˜õs¾ãþ‡0MnMÍö±Ö-žÔçëñ=Ì×nNéýŬèÎxqܾ"¿aUÓ7ØÓt…0jžW|ŠKS?j]ß™z¦´+!Ç|iWkj5°u ‡Z47“‰uuãwKÛ›¯£p~wS×WÉ ç7ˆdôƒQ²iæ£dÌǧE²6‘º©¯¹iÊB‰^æk)ºÏË"ÑÍ"úøvÊ«ƒ’Ie, ò He” ý!“™õÊÌr·ŠÌ¬¹Ldæ¦DSÁ Ê ƒtuNÕ‘`*X/¢íÑÇx£Ø÷Í»D9\”“É¢<“½ ›F2!Íøÿwö7þòàÓ½ooíkÆ9g=þæ~ 'õÀ·§Æ¶xy½‰ãÓLE¡hpü“wx•b(dÈW5uÂÇú0Úå™WC¡6(°гýðð) c ºø)0M!8‘´§ 5Q‰]ÂæB±™®[Y6Þ{S†¯ßœáÍë2<–ˆ`e!’ŽGÍ +Þ8Jô—À[À_z9"GDæ¨3}º†Ø¤ó##$£&2T5ÌI´É©¹‡‡ æ$ ‚ã A¾h^ø³ƇFš?‰ð!º eàÚì‡"ð(þ_]„}ï endstream endobj 278 0 obj 23477 endobj 279 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 670 /Descent -216 /Flags 32 /FontBBox [-568 -307 2000 1006] /FontName /JVNYUI+TimesNewRomanPSMT /ItalicAngle 0 /StemV 0 /Leading 42 /MaxWidth 2000 /XHeight 454 /FontFile2 277 0 R >> endobj 280 0 obj [ 250 0 0 0 0 0 0 0 0 0 0 0 0 0 250 0 500 500 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 722 667 667 722 611 556 0 0 333 0 722 611 889 722 722 556 0 667 556 0 0 722 0 0 0 0 0 0 0 0 0 0 444 0 0 0 444 333 500 0 278 0 0 278 778 500 500 0 0 333 389 278 500 ] endobj 20 0 obj << /Type /Font /Subtype /TrueType /BaseFont /JVNYUI+TimesNewRomanPSMT /FontDescriptor 279 0 R /Widths 280 0 R /FirstChar 32 /LastChar 117 /Encoding /MacRomanEncoding >> endobj 281 0 obj << /Length 282 0 R /Length1 41672 /Filter /FlateDecode >> stream x”}`TU¾÷9ç–é3wzÍd&“LÊH‡h.R” %@‘ M„ÐTD J3¢`»` 0”]‘eí¨Ï^Ñu±®QÖEldòýÎtßûÞ·_†¹÷ËÜ{Îù÷r„B̤™D:wÊüE7õŽáÌ+„PÇÔ%‹"„ÞýàO ‘Ç^:ÆÜι³ˆ})!Òw3.»êÒ•¯^6‰k6!«”™Ó§LûyÐôr®“ò™8á(1àø2gÏœ»èÊû~‰]Žã xæÊËæMòÿgå„ltáú‘¹S®œ/ >Áñ{8Ž\>eîôwýMWáøg~<ÞÂEßÿÇ`B6ùpÜ1Áôù9ïÔ>CÈzBLÆ9Šÿ3”5èÝPÂQ’uzƒÑd¶XmŠÝát¹=^Ÿ? e„3#ѬXvN<7/¿ QØ«wQqŸ¾%¥eå•Uýú“êsÎ%5ê€ó<äü †>¢ö‘u£F[?nü„2ñ}õÿßŋҷO"É‹'7þÿüTÚOüø¤Çˆ_ŒŒ[×—ø~Å÷©Y]_ñë|ϾÁSÛ»¿„l%Ûé,²G»¾“ÞëúˆôÂ/6’»È'ôVââ-͸ó>²€Ü-$EÚ5£ëW´ J®@DRKŽÒC,§O'_R]& ÄSêjí:‚»B$If’»É~ZFÏgQiRWm×QâÁ;®ÄSï"»È^|ÚÉŸÈÔ,èz¸ëñ“B2ýi#¯ÒCBªsEªã&a”òI®Ì#&Ï“×iŒ>ËæIf©¯¤JK»Þ".Ò‡Ô£µá—_Пصø,ž‡tG¬—[øh“¿’¿Ñ-¢#é8–Ïæ±û…D7öÁg™…ñ¾O?Ft/3³×„‡Ä'ÄßäŒÔ§]V`$Nî!÷‘g©=Ð…ô:úý;È&³{ØgÂíâ6ñ Ýôúb2—ÜDž ?Q­¤£èEt&]F×Ð[è]ô(}~۱lû^˜)4 ÏÃgŒ¸P¼^Z-Ý(•š:’ú¯ÔO]}»V“Q ‡hýFr?z¶¼FÞÇçò•¨‰Zñ‰Ð(­§Wãs-½‰>H·Òm´ oy~F¿¦?ÐéoŒà#³ ‹²,|bl»‚ÝÎîe¯áó:û–ý"x…,!!” ÕBƒ0­Z#lÀgð71 ¾&vaœûJ›¤ÍÒVé é°tB6ë®Óý+§ê,è<–"©µ©M©]©¶®¿7pÀ(d’j´~ >³ïM ¸äMjÆØh=—ŽÀÈL¦³i½#¹’ÞMÑÚþ=ˆQz—~6[XHksoVÆÎc#ñ¹˜MgMl»•µ±wد‚N0 6Á-ç Iaº°H¸JØ$´ ¯ Ÿ §„Óøt‰F1SÌãbB<_œ,.ï¿¿”&I/KŸËFy®¼Zn—ÿ©+׫«ÓÒ%uëu{uoéA!{ÈÓ¿— ôSa…0XØCnf%¢Ÿ½Ê^=O&Ó„ZJe[éZv mcÙÒ•rÖŸ^HNˆqŒõsl3;Åú µt8Cf³>égÊ.ñq@Õâ_H‡x}{O¾R6ÓkÙ÷²™ì‚ ­‚´ý«P,&„—ÉÂ'T'>@>ÔK;ØcB¨àOâ¹Òî%O Mô²‡AŠÓ¯_H‡\KûÒŸ…."° AEÂßÉõd{t€×’;è4q¹™”ÐeäKò(¸"_º\.ÝôE6KlaNÚF˜¸ ½«¢ÙT\d%M wËß³÷Ébòšh$Ç„'ÑúרSB­xBMg‚®!«IS× r•4A|ƒÎ GrÄO!Ý– }Å(öË!U&A¦íwï‡ Ô⌔3tQ q7>wBNˆ  Yàññb¯’6y,k'3$+…Ô!D|95šLìz”ÜÕ5ƒ\Þu+éy°¦kž¸•|NÖ“­tUêj2Ÿ„Á9Çèi{MÒÕ‹µ°÷Ù¶éøÅhçPùŸ§€™s¥¤E|—Œ!5]ëºÞuçAÂÞE.!ÃÈqôò;¼áá)I]Èvv 棿ŸQ]ueR#™ÙuI’Gt™¢KÇ­ô ô÷j2îZ$LOÍÂ8¬Ç(¨­Å?7¨ëÇPkÎ=§º¿ªÊŠ²Ò’¾}Š‹z÷*LäçåÆs²cYÑHf8# ø}^ÛåtØ›Õb6 z,‰£¤pplHc¤5ÞØ*Æc\ЋǦàÄ”ßhlàÔ?ÞÓá¿›‚K¸SÅ—þÛjúNõÌT‰T“ê^…‘Á±HëÑA±H;8jà›Å"­\«Á4Ø8Å"ƒ}3EZicdpë%3[7êUHwšŒc§{’F@ VolþNê=—jóî·“½]l Ä nõÇðS&‚·±U Zé*¼2Â{Â{•îßôØ`~¦qv¤Õ;/6³ev#Phi%£¯Šî Ô}]Ÿ’ÀàHËØ ±hkM0Ö0ePh§‹´Œ¾j·_øÿx¥WáNÅžØV[7`¶ü˜ŽAO_Ó ív }fd)oQlh« ŠšAK&ÄЧJ¾™^IZ¦Vøk øUë4`dV«a`c‹ÒŸGi«”£Ä"-?P@¬ãÛ?ž™Ò}FÎQ~$ü"§“3¤ÖJ§ôÀ­‰DkA'Ý@àmR‡±ÒЯÃrߨ®’KpÐÚþÔÚ±v1Ê6ž×ÀÑÂŽÐ=,Î8é‡Ð0×ÜÀ•,f!Q{Ôžƒ …Ò;V%ò‰ˆ‡p̸‹…Ýì ø,÷Åûàhü¼;+§TjïúYÍŠç—šd#TH‰$ɦï z½ 0¢ÓWm†f3`„T·ÅVj8F±šQÕb/¥~sÓc¾Ä…ÊÉDumgµÒ™HVwV“šjŸÎjl¨ÝQUÅ¿}Ši"áÊJÜB‰¶ÝÐ÷h¯û-vS¯Ó[ÞNw×—bƒô& ’LÚG]“—Q™Á ¢!ƒ·=í|:ô¼óùÐÏ2enb1H²@)Ä`Ò)A£Y§ø,6âµ:d»×ê\^«‡¹½V?sû,æC‚+hÌ\>KX¶û,™²=h4ƒ9Ä×`ñùr¼V—×ku³— E—c—Ûé^µÒjµXŒF ú|^/1º].»r®U'Ë;—øn·xo·äXU{ÕHëf+³.ŽonÇs1x{ìUpx;{`wdÛL_B9•LtWŽŸÙŸäã¥hÛîL£Ò‰¡´Wa»Fê¸F9²¦·ïlÿö‡!N&›¼ÎXY‰3Zu–ü[⎠QwTˆ9£‚3êŒÎ¿íùa©ïiÑøMãiÿñwŒßþòpêI½2~Ó¸ÔsãÓ~ÃSõÓÇ7Ò9éöÔþݘڸ15Ž>žÇjèÐ’@†võòRï§’¾¥æ, S±¸°¼¼hH´>ZW”,Ÿ-L-Z*\]XtuùšhsÑÍåJŸö®cO›ªÂ‘Hvia{×§ja$V:[_Yà1WD<ÅQq›+úG‰»"Z\ü’¹Âe6W›£¢·¯ÜÎÞ['Q©ƒ†ö³‡ImßíÍ|3ÑNËT#bÍ^¯K"í´b5öÅÙ¶ü7©%´ŸVâÖ;w•-Œs 6:ªŠãj¼9.ÄÛÙhÕVàñz33#‘Êʾ}óóñëÛTZ‰>}L&£±ÖU3y ÕÎ̪Aê½pž²\aÊ~z3‘i¥j«‘FJË¥õ’(ù«ž¿Qã…dmÇɦådRcÎ ø€;~ÿ9 äŸì 5'qþd5}·×þ%ÛÞª5ÖÞ‰5Ök4$÷)ö ¼J­0;³sb9Y9‚ìˆ[m“+¢e#iI6…ÎÞ#I±›òœÊ‘4©¨,Íí;’”ôíeOàrÂé*²õÁ-}Ì&ÂÛ’èÞ¤÷àÎD¢ Q°b ’HÒ¤TÖ›åVx¼¯=ž—•V”W€}ù ]<žk÷xÃÌí’unA–Ý.×Y^^VÏ¥©5Û®6îq—˜=oѸäÚ‹vϾoÒß~eú„µ…cgW}÷§Ù³®šqõìY7L¹åÍ6ûøÃë²nÔhbç¸?~Ù¡+êãÆÙj/y"4»ÉÑùK–3gö­õ~5ì•ó”µÉIËr:=–{^r‚]\ô]Ñõ’¼²Â„¨FäÂÑ©Æ þ 6SÛÙݪÍçwù|~_Ðmóú$Ùfb€ùof›U“ðû vÎÉãç3q¾7Û¼+Ç:Èî& È×>ìîÝYO–ÉüØciàœ¼¸tüDH唆͎Î>¨ß>#üÖhl›FãhZ’Nd’’HŸLÚ+¨(…Ù2‰WtgR»S¨ #/“öbS˜Û;“ǰ±Rs&õHØ(&G&qé°!wüOC"VФ³´¼¤/l|9–§YC%}9‚ZBéÿåÚ÷ojÙóôêU;iÕÀ†‰ç ÂWȺõôßèç÷ß kp¡?9¸a¢8ñ¾þúÌþŸ£]tÏM Ý}óÂ_ʆ_~¢7ßÿ!¿ð<=²èžu‹øÈ‹ ]Ǥ\à)“rzŽúÂR÷ÏïÒÞK‹V{-ú˜è7e<äa7]_ή­Œ²6môN‰2[õÌ&Âãáȶ¹Ÿò°ëÃ-Öb¼>Ä^Ž<—ËŽzÙþÀs.6«|¿‡ÍòN/aӋ踒IålHÉÄLVë9/ÈŠU™,ÌŽ0Ò«W¸Wo£‘=ž wÄã‰Dö{¹ŒÆ^ñ|…–æ‡û ¦àêŒØÅÎùÎ-N¡È©:™ó£Œõ>êkgÕÿÜð‚Hͨ¬Ì¿x‹…Z¶ô¹8¢£ºÙMwvSH’˃ã';’Ø>NjŽwÔtp&·B’ë¬Õk¬|§Tk€&É“ÿþGºOäÈÀi.²¼‚³eÍ-¯ðÊ:ΛTcʆs/¥w÷Âц7–þmåœOM=ïµû6=“úÕõò(=½ùª¹©ðâÁ“Ï:%£µ©½·^zóu£¶oŸ:õÎew­ýpÌ‚›Ï[ù—öÿu{jç„Ey‡–­¾hýaÕà™5Ã'_<(kxAg½küÆ¡ ‡¦ƒ')™•ž«ÙqÕM$Jß1"¬ˆÐ ”ÑÙ2· ô üÒ Ò§8m¬í­ŽL}§=gYjkÍ(äÕ˜k£DqèôŠÒNKv“ÍV=öª]·Ùz1!"“öûÖiî<ÅJ´¦š$3;^%²·Bé'_­xpÅU¹çÄÀ3©QéÏÔúÝ¿½ÞвéÀŸR™)Îàgß?]5ç±<…Œ %oq³@±o#›…‹­°aÛ…Õø¹ÍfÓ€ãm‹|«ÚŒFVo³fÂ$xÒÑÝFÎ¥ÿÖNgŒØKs!ksK dÝ ëä²8ëœÜ¥+N¬}-5Š~Jÿvpߦ–‰oüÖùÁw©Rˆ®£ª0•½qò‘Õê05ƒ4h3›ëde>îëëˆ(轋ž>÷þ5羿KÔ zj”%!JÄE]ÏȰÁ³QµHÕî,%~ÿ|Œ-7ôjOv‚z«;“Õ0I ¨ðrNÛó]Ÿb’tBk ²î IæÊº\P«Ú{óNz‹àš±ª÷ò¥çÌ»²ßÈa•Kõ]!n¿¹2Ï ©K o.°–­­¹ö¦aõë{ûyÿ öÑOÜ _©f’Ë ì½p¹¤“ —£—¿Hôò÷ó›¹MïKÝñý ä!f7k5§•O€À߀;#Ù¡FîΗ³õì.½ø¤H D–˜`¨™Ñ—ŒZë¼O„Fð[Xýš¤ðj×Ò Ç Å(«~Ž®œhø ˜%ž'<ÕôHK4‚Ü “ü¦ý´š®"iæhJ`\¸\ÅF†{¤¤¦†‹'î}&I2ÙeYW>,a¿µ xsìŸ-¯>wYæSç¿4™÷­´¬CßÂôùnZ2Ø‹Ïé”ë-í]'Ûìv øN5(  °K sõòÂa~5²âJŠm;; š™Ñëd*vÆ"™p…‹Þ:Ê·GIQoi ßA 6ØÍü…f‡ƒi/T 6; ô{>UM'«»ø9þì]x4g“‰ÕøVÕFñzçþ>þ6íejy©¿|@zF> {^ÿbH7ÔÜ`kcžf]êXê¼ÁqÐñyàóà‰€ùÓÓNTBJ†Vä?#Ѧñë±7[°QÑËòK¡€+ èCH } $X ¶Ý#íÔÞN}{xˆ66ÊÌÆ…Þ71ÚœÖé¶‚Dˆ_ÊlßSƒ„Ø<¶œ‰l?ˆ5¾~g𨹅àâ…kžêšŽÎ´“¤)ž#Š[ÛŒ=PIàÃ,hhÈqGãÀxQÄÅpÚ0†1 ëDÝé æÍyèîï·Þuõu÷Ò}ΟÿëÍS¼v ÏÀP2®ë ÑZIІnÔ™ü>•Ó±/D('Ö„4?f´ØÌ¶°Ñ˜ï‡Äp~HÊ·Ä,fŸÆD‡ÕGtðN¿jã·Ç‹¸H;ZÄ?ÄQUSEÒzéxNyÎQ¥Iôå_‹š'Y<–Á–Õq°}¼}IPí¹L™íšæYl¹ÊµÚÒâº!øˆÅ(Eîj›x]ÔQ¼êæáÝ*:pÉŠ|b«l6»E÷ªýl¦š‹VJh¦Å±prd^„E|œ–#ͺ…qM:Å)‰+q†Ÿ|š_‰oèåk§•»üoRîmtÜtV^¶Ó[»±È]^à‘K­“ M “ðvÑ9EÃhÚ’³Bˆ_iSL .µà·@jUœ5eÊ¥׫|KàåŒkËÜ8gùޝ)ár˜¶¯ž=k«-úÍSW¾4çÒi×mH}õγ]ôzß]kZ¯[ö€ë~vå5S¯[¹2²çù»¦M¾·wøO7JýøpËHr@ kÄðÄÕrÇóLóÝæmæÍÒa„åvQp€Î‰Yt’Ñ$èˆ ÿ’ "\$ ÂÌXUؤˆÝ¢‰(âò’Qlg—>-IF5#³ÔØ# påÄê|§i)#âªE§fÅJuÍÑ2ÝÔ1ÆÕâ*%La&àøSí7Žïåx`{¬ít6ÖßBjÂð$1Õʈ?@"Êp ¡$>ÌUUpJÅt Žï>bÞwTAν¥šJª„¬^U‚˜‘QÍÑtàÕeVMUææº*³¯2g…°ïUÅoH4 ðXFKìˆ7Ù;e›:W²ûn{î¹¶Tüˆ°÷ô°GR€±7vÎéqý•…œ—æ}„¢>4d5†ÝîƒKO“MÃ!‹•:C³ 4@ã3®û9Ÿp2ê<Þ଑ïÐä¯MÛ\•Ñ’±Éù˜ó/æwÌõ§ÏZ ÅR±i?d™þPœF·Ãé|ÉjsY.WÀ$ª“7Dµnánµ©nÚݨ§m"}“3$›áͳOVx\h½"*`ŸÆ&>J|Š¡±i6ñmˆ8Ò2b£AT•»¬{þ'vÉü#»œe˜$·+Á%ZG“ˆ&!ޝÑ÷NHÀ"Ñ„×ü•´ ×Üâä!@È=Ķt°âõrßuÙumÛ×_—·ífö~çÓ#WÞrˆêÝtò…NÚ¬´ÜxäÁ»w¬ñ°>™Z2)u꿞¿e×§` ðF-pç†ÜË td·äË´ÑL¤äJÉ «ð€-PŒA)+ì²Ôä(„´§„½ Wû^Mîy ÀÝvÜÑ·Ž*íÁ%¼ä#IŽË^sütNuòŠLtŒÌ¦é¦ég;¦Eé‡VéW‡ÞÑ¿å±ë"œrÓ\!×Ç4¡ÇOEµ :~!7‹Dù;oe…¡AúædŽJ>CO›aÕVª²'g¡¢¡^Ÿÿ½8ñ4·• …F.ê´JõÔx'{çy—{E/LS¹Þëá/õ¶³ì݉´©^ì8#÷ÒöZZÚ%¹áÆqƈK¼ªƒÿÇ 48O@—ò è"v¥Gê:+ eá·Ý¾Â¡sÆ ¨¿„ 88£­óŠ×Wþ-uü¾¾ÚþqgÅÈ›/\ððƒW/}\c]\[|îwMmLýôFKǵ(#XF·=»õðé“7´ßçŽÀ+Eµ>{ u@óUë ñéEäçÄbFEƒÙ²i>(#5U-°€M¿Ðð2ØŸÌ„ìæÑå0"ýFšàçYSuíÉŽ •SÜ*ã×âUvMaš4_F&‚¬‹•;S„=ëRÃËmû„ëþuƒøëöuSŽÔoín§ßÐçïåñç1 A?hÐKb¤˜‘4¶™I0Ü›ËIØc¬¾woG4,Kya‡%l0s5 'à$D%€„û윤 (hm>hÌ´C¯ü.Ý,d»ÍÜÞrkOtkìî&à´7ò;—2)ÑÁ“ÝžÉÓZC4'„7oÈqÍCá€v®ûýÜ ÆkO«YüFþZN^ü…|Ë{z¶=LƒwQM&¦[¢ùFœ‡*Ê<4ß3Ô34þ…ùëbÉPŒRkè2q‘¾É´À¼Ø²Ô{#i¡ëÄÕú¦•æÕ–›¼¯ØŸs:²À+»B‘ßE"E|×+½ÿ©ΘIØGÌhÆ–ÞôlK Ÿ1PC;›¡*‰…6þbDnlŠÙÚé-{ûú¶Â‰Æõ]Ù Ý=}Ä­º™{CŸ3®ÍI-^Æí„n3ÁQ•,â㊫›g4Y—\ÐDšèÙ@Ø{€ 4æì‰z"&üžyèìù—}ñÌ¡oæÌ]sSêÔûï§NÝrÉê93WÝp錵ý†n³bëöë–?&óåƒO¶\zG~ᑵ»¥‡Ö?KÇÎ\yýä©kVžîªÝ0òÑæëßÊå"×iœ&ËO¥½‡§M™P9v(S’¹6Ð<€jǨϮ¡Ô®y¡vŸ½0aÊ óhÑH«`µºH¥š1iQà]P®m V% ãGɾ ±dG_m`€yNˆ —£ÿ•æXÿ®gõ§Z )P»FÅÿ—·þñ]ÿö*¼éì‹ÔÒ~5v‘g|ìRá2ÏÜÀŒØÒÀ5áuÃw{¶¾ñ|9qžã¹ß³Ý#ôËŸ&³\®{c &_4"GòÂ#­“¹¢ ñîÑ7ëÒB¹7"s?­"&ÈdûUë†B.©Û¸ ¶Ÿ¡%»jgö ݲWK³¤e.yÏèÏÁK’ˆ¤ÀYÖÌÌsYYi.—·Ø#oâAu wã”[ŸÝôùÛ=˦Œ¹¦®œ–˜»÷4Õ=·¾ãê¥ÿ|ðÉØË,ºr×¶e×<@Ç(K/±ü½ùf߸9TÿÞ'T¹;õwÄë¾Lí~ê¡ôž½Gî]Ç….CU¡«QÿÉó·•°%$"ë L®…j*‹ˆàÀ¶!,‚±x@ßejâò>†rœ<«Šï>s„†£GO?† Kdz´g£ŠQ-ZhºÞt›é!Ó Â{4n¬01Ž3N7î1~fÔ™ŒV§®Z–%«hzh“ªE­+k‘uÕ¢±ÒÔO*kD©ø€­§IÕ}i.Þ6¥³SKƒpÇ…7’(/r!O4õ4ôL0ìhw8¬§Õ=A1MÍ'Ÿ‰ýQ݆zVÕ¼^hFXA’=“°‰8)°‰»˜*ï§u0±ëT7y‚>Y@/VS>`‹uÝù™j®yˆ¿(PÛ?_ »]ݱÊ2ê¦Ô=_xùtJ`lÅVz÷nÔP>»MÀ.  öˆ—?竹’LEä4G`ºQ”s 7£|±g$0P¿>…<žÕ%› ÄhhCL¸&½x,_±ßéJáþ.ÞÚydE¥ÙïúÜÌÖ‹°˜)| ÆûŒ¼>ú,©°눔î³üBßÕ2 è#Èv÷ù‹$z\]ÛÁ;ý?ô™Ò2þOìºL §»„—يԔݴ†VïN]šî7j¤E3âaT¬­T‹ÀÇA¶,°,È. L²9æ)V6áVndeA¿^'%×n'–| Ã\ءƢYÑêLcfuVV¤: “‹Ã—/öÎÎV.Ž (0;Ö“=Órg<^Š (X´-×lÜãvŽ!¤´“0ŒÄלE93¦½x‘»V¦ãÄOߣaOŸì•_±ðnß>ÿO/¿‹TÄõʬý(•í˜]Û¯â‘KúÍÚ¼á.ÏѾy´ñÁEk¼,u×óR£toKo“óQ+ø“:^Œ*O4šSf)±¶õ ŠÉ2ôüqc­Kó­žœ|7dÄóËåUsÆù2.ŠŽË7´aÜtßôœKó—–f,È^å[X—qctMÜoUê¬DÃM)£-·ØTgb&绀 $ÃÙ¶ýc&®>ÝFó,±ŸÖ’\v`oÑÙ6d“ÚÙõªM©;—d;¶Ø²‹•ùZz²Û÷·ÕTdã~‰±û‘,£eþ ãÓ{d£;áwC‘"p7»q Ž$¦ã ÉšäqøMÝv˜h¹%®Lµà¡·¢DH˽ŠrGY)C¡ˆœ¯C,‰d#"‹±¬ìlž‰ªph_ÀnEó.rã°TKúv#ËÊÄ<0ªa묇~X0þþª¬ÝÂùeã¬z"µýè7©kÞ~›Þö#•é%ö”üœzüŸÇR7¤~8vÚRú,U¦7.˜òÊÞ÷×»,)Ïuc+—5]°fŠÚ4[}høE3ß[±™Öl¹(yOç”u¶`î9uÔ²þ1šõÔ‡©ßü˜º[ëµ³>X¾àóúðäÇÔF#/¿¸ýåÔ±¿½Të§#n¸sàÊ—/]»iÀ†WT´©AÚN·Ò{)²'¬ÎõmÝÀÏšâÆ™“jWÜܦ”ë%m[¤+3ô3 ÊZaƒò¢ôœ|H9¡˜ôRÊÅë”™¦Vå_æYþe5ˆfÑ"Z”\J¢ˆ˜Œ^ÖéÌ€õ¨‹FžWãØ´ˆhDgvá`þ¬ÂúƒÍ.üÊ–$}XP1_5½ùkõBl?5Á@1©s„L× £ëP~ý‰(l€¨n§T5Õ™é>1 ÌÔÌ›î5[®kÖ1Ým¶wÞÕrM~XëøçuüJGñÕT@-ƒvð¸~O ßkJRmräˆõÈ‘5Rz¾Þj3¼5Œ’´6Ñ&èuû0DÉ·Úèî£ò¿2¨OAeŠG’H`%ÿÅ&|üDç=¼Oÿy׬P‰´ÿ×!ô`j›H7í»â¦¹|PNį+»æ…:÷X9ŸGðEqHl\ìÒØBÃJƒ<+°Xšo€æ“®7ɹƒàË-{2 §#\PŸO0™#—‰Ð-Ñûâ²™gñdDcÔnõËn$É2{YÏŸ8—]Ü—ÇæÄÍ!þ ³‘ßgæ”áæw™…áåZ(¯«Üüëø½8ó+¢ngD¼¹AˆçJ&úOâ1þôñ’*ˆs ®$}ŠGýy_˜¼¸¤ªÈÎˬ¨–ÓbÝHvý.>fe1훂ÆcP?}+4ŷ¾¼ðÒ«Öo~v]ê6zΊÊaÇ\wêC:÷âøÀ‰ýÆn\—Ú.íoØ7ýâGKr6ÏØÙØGm÷\Z;t^þo[tæÊ9CF_… (%—v})-ÞÈ oî™Êfg0˜®Ü½Òú÷•:™CÒ×2šnQF3Y™±Ü-=!iî«úŒ±>ßÿö,½üÏ‘ñúôå^[n}0õý-R_·ä4Í0KƒHAÓ6ŒÞçjQ$“Ô§éÓ®„mDFh¦`6hde0rš2 <›vm¹€X df(ÿ1ñý*Ôósñ…ÿøº ‘{bÝD×§õHåBP§—õ’^Ô‹²ßð1Ùd'¡2<.Ó#ÈAÁ¥+6>}(J=F{µ*Hjà5*œFQN„•‹Bs¢}»ãô¹ Ëûé/OL¼¶aÑ —ÞrtU 5)·<Ògpí—]¸=õŠ´ß1â’ÔkGK¥¶M黽¼Ïà¯ýâ§‚0hðAÈ>ÊD6ªnY ëõ:DÎèFCØDôˆR3G©n¬0,bŒX˜1` ÿñ˜qÎý#Úû_”&!?“<õ¤QÒÉã‰3ƒÖͩȻjihžŠŽº³Oß/$N¿-¬”öoOÕ<™²lç|cP\…>ÈMjBëÃz˜=Ý@î ÉXÀô´[5i’F#wˆ™Ôk¾‘£œs@úïlû‘éï2(fëD5ÅÙ¶o>>ý9kí¬ãíî·½“Ž”ÌÿïÿçP§º‚nÖ˜K/Ö;©CÈÎ&Q‡—å ‚# Âå)Þ°U@œÆ@i<7'Ö,z–Û¨¸y`¤[s{  MMùïÙ‚æ\š›©Qs þøÔn\€‘k•¤&EÑ#4òL¨8RÆ1—™øòÌHz †!HÍq%ÇÏŒës0 "ÇgɈÍÅÍ.gD‡£,)'JC&жˎMØ’l­ 4Î+Áºj‡}U†BRn4õH”ëôf!¼.…›U »0‚Í]Ÿz}Ë{©Ím»i݇›)½5¾#zÉÞy«_­\CÙ-מ8—ÕÌ~=|¸S–öw>Ê&þ:„íî¬Õèòç Œƒ@^ÙC1éƒñdòîÊs´¤òî’Òô¾WqzŸ—ŸÞÇ´òèC»3Âéc_@Û#v¢”F¤ Ò Ô £m=ê@Z‰X„ }Òã'ˆäˆàä"h9km,‘ H[ßöX<Ó“6 TmœIDÓ“Šï`zºÏ³.»šaÖ%š ê¦‡¢x:‡³c‰ý™ÃÜD®+º¾¦ v²MU¦³ò"¶X^kYk— ǵ™8õӀjÃ6ƒ!n4êã&žTá-ÓÞ \Bh@Zuó3ªÜ6%#NAù[³Ñ):iØDÚ2mÇ|Ó#W>êV%Ã{{zÒ¡$›Òö ÷‡¡];h¾V*ÄUgy:¢…ºãýwèæO:;ïpó×={”nñm]6páµÂ§ýí/Í>Æe l?i4§išRÃBVE•ÞÐ/×X&—Ï7ŽV ï º%Æ÷…÷¡ˆ¸E¦©ÇuúNöÅ7)Q“Üg(ÝHd®j¦ RP"zÇjg©6+ÿ‡ã}ª‡ÒÏMò3š¾H¦5šI£n4ð 0ê¿ ÞÔ;1o߆¶(ìxOþSßu*-'õV ê; ã `”¾Só8dvpN“lfÁ€",½Ád%z3šdQˤñì¯{5æUÀš_ôÔÚ¤«qætZæð01Oð*§šC‡”×_?Ä‹÷Põ®aŒôReê4™$k[AÛŠÚVÒ¶ÀÒjŒK-¦P›\'[ù6í5 WÚyÆ~V3¹Ì£@(bt”Ú´dµÂ4ÓÃFãçÏÔþ(ã6Ž80VãT I[1ڋПôc Oz$NÞ1è ìtg>ì¡?”´ó¿ ºœ0›ÞÅ‚zq‰yµù ¥y¨y¨MÈs,…Ö ÂEâË•Ö5½‰Iú*K¹u$. ý¨¯µœg5ÞÉî6é6é· éd³Y­ÅÇ3=bøÅ’ Þ<Ú6šªpÇõ|õÈ~«Uáxjt4;˜c?ÛŠìoŸ]R¬}T£Ù`Œ¨æå(ÛÜNZ© WX;œxÒ&Û|…¢ŽfÜÓ©Qj– NØÖÝv® ý¼Þ0YíƒjÔütÀ3Ç“ðÚ1 œÝ{>{jÞûšk´ù#ØAþžuÒÿDÌ]¿¡ªçBÞÑ|ôá­fH€ø”Çס¡©Ç[^A£PÓ˜€o¿³/*öøQ@¥©q;R¤ý¿ýpËu÷§"¾ü[™øéoWð“2Á+zÍNtQÚÞÐûÌ-/÷•å’ˆN¡«G@PÐDÆ :½(DÑFm”¦uü6m¤4/ÁQœØ¤dÄD#Ê5šæ›šM’I†š7ÿ¹Ðm߈šþþƒ}ÓíÐ9ÊzT:J34‹¦IÏg,-‡HŠˆ‰×é·¨MxRVÁ4 ‘Ê(`¡M¯©‚>´wH•^í›ûVé c¹¼×°oägcézNS¬JguáëäÇ'÷:f¤Á €nþ¼óŒÐífy€ÄÊí,j¿÷yíþt ([!.ºškªàÃN…ýÿ±ôÖM’—Ôº€º—+è EQ]&¯)(nóîµ>g¼^_E2TûHçH¯˜ M0ŒWê퓽“}ããƒ7zïbŠ?,ްÉàŽóŠznmpa m=8¡Ù¾Ñ¤€t† À¯ È] Åù¶8Ç¢¬á(->ü¡Ï?íú§}œˆœÿ®ªî¿SáñMî¦jÖ{…‚ôеÜ2•®¥å/Ó!O´¥ö>óZjÿÖhÆ»ÒàU_ßòjê]öKï;œzä£OR[ö¼@'þ9õSê5ZJƒ»©é¶Ôçi¿_ì}[PÁ½K-œnŸãbÕᮋ”‹\¢ÉŒ\ •x}Ü}%zG\’µkÕª§'UÍÒ"ŠŸ%Bÿ3jíqÿ»7ëÿ½2ÓÌï •&mpøÀôD4ëN x»¯6ˆE£vÀÚ, ü[k/»µá»Ô‹©µôêƒ÷'GôY™ºAÚouLß;÷@ª³óI®[>éz·%M;X_Dú´ƒyòt¦ºqr|sœù}nf a xf®LWL.zyñþRµ·_|„4Â;4ž”êcâ󤫅¥Ò:a´ë'Ÿ{?÷BR‚Hý%1)ÝêÛ;.æx ⥞ªøPßÐÐàÌÁ±áñqú öz÷ÄÐÄŒq™ã#ã³fI—ºçįŽßº9þ¡ï£¸ßä£ÈX¿µ+X¹ð–Z¬}._ÔO™àÉtyqŸ ~µV·ë­"öåEåÅ´NDJÂ5à røŒ‘\ùw>)§rœ-ï&o;§õŠx®øãšU÷ß÷Ð_ŸOÜÑJ¿ÈIþòÎ/¶Î}”þ~ê3üh椋¦ß—L¬©ºú¢CtÒïÓiûŸM=òÁžÔ'7%ï¥U˜™w[êÝnN½šÛßÚ²¡EP~=­F&+u”‡&f^ªŸ›‰·ôÚV§m³!ï4v׊q¹Ãƒ®Ú(†4àhïúl·#PŠý‰ÝY¹¥È ¶;#·Õ;ÚuÚ×ßÛO_ÇýÚuìùuu(€ë°Ð°ÈÓ¤ÐÜÐÕ֫l«ŒkmwX¶ÙÚm_Y¿´)°s"v›Ën·Ùmfƒk¤¥á7åYâÙ,[ÈÍ)·•Æå .š«Ï¹Ì4Û2Çz©kºï*ÓRËRÛ5Êâì…9«…Ó –ÛMʪìësnµl²mr‡Ó&‚Ú+wãCIBò±oŸ8UbÄÒëªà AÌñXz…sshŽätRMç­Â½ á°GÐ4]1°d:ÆwI„¹¼¨ÊMP‚—“mµ˜¤(¢ÑA,‚uBdš“…sM{ðDV¿Ú§+4iÁ=ͺRh„ÖÑF:3¿dZUg/þJ ¯F‹‡â$ŸæsÅmµ²z'U R~ /úDãàÐoµK0|P{ºÓc(Jý}ºƒ}ÉÚã 9䬴LÉÙ>ž žõLœä=£wZ–†j?4Ö6…Ί0Ób˜ÅGZyM­TœKHçw»¼ä8yÍ1——ñIO[&¿pͼÇÇÔMꟺlÔ¬×þpûC¿¬–öÛ¶ok} ª’¾?¡yéêßî{>õ¯»è»Êå7?oá Á3bÞ)‰Š‡¦Ï{vÚ¬WVXo¼yÅE#KJæäõß³dñk }Íu!%ÅðU÷C.ê°ª‹Eba 9‚¾X¢¥U wkN+¥OËÊŠx9¥{(åy¢š8Á=×58þA“Ž>ë 7Æ-|Âà‰ú½w5Pëå“—;'¿àÞCZåcZ¶E |•3•!¶¤‚’eûö_ÿ•nï°ûxœÑEÞWqÛq‚þE½èáÂÏû¹Tì¯"Ó/±=*}eÓ™ ³#ÓÝ&\qœiÛ@·mδpŽ?UCÜ\cɈ‡FB¢\@¢[Âí ú¡Ï'q{¾#î«"åö*G¹o(9ß>Ôq¾ooŸàïSîÔßiëHµD¡Â]*•šIƒÌÃÝc¥±æ‹ÜÓ¤iæ9îEÒ"óÕn›äæq B{6¦á8ãXój´ª*ˆø¤ˆè2DÜYÖ2´™±î’ƒ/fèƒ9Y½ ÔEøÞì°ó½:Ñ ×QC¡ÄEQD.éõa·Ïåvûfƒ!ìvtØ1.¢Ø] –F4˜õ>·dC!ah’$øPbm@“™Ïá@]Ž>àõ”:ŠDˆ[7¾*ŠˆFíDõûÛé;ÓÆA2à¯íD0¡3àïô]8xú /ÎØ=Án@raª}á¶Öjn+GòÙC÷,ŸÞ|›j¾Ñ ßo€lmç4á0òbÉ4äàdÁY èWXqf·Y•TÜĉbAáL„Ó(ƒ5H&É:JïO]ýü'ÙJ¬yöÍ#c¡^_ü%uùÔ˹:¯+õ"xµæŽÿÈŽuRßþëÆ6á)8³Éu‘éçÿö&‡9Ïý8Ù5:ÉO=&–ïÈwVÒ ¡R_i¨´ô³–9*œF‡“‡‘|ƒiÀZ Fª¶ÿ}LY½ 7ˆZÄ™o® W˜X\Ì×å™ ¬qG¹ØOßÏÄŸx~¬˜ÔO2M´ŽuÌ ÓÅÙú9¦YÖéŽÅâR=· ®p\á\-¶èZŒÅvýÓŽçÄõïŠïéß·¾ãøRüJÿ•õ G!ŒI̲3#t¨xøÖ¤ç[0ÛÏá²únÂdƬÅgDy)~ð•jå"c =È%I‚ˆÇ2|T“|aNå † P6N,!f¡ŠbÁ"N¸ ÌbÌN£‰Ê sŒNg$½°Š€zûˆYp™ÍdêșӅOôE˜ZúŒ˜1Y¥)“ŸŽ7!¶ï™Ü-~ÚU£Ü¦*uÊkŠ€Ià“Uc„ø]îÃQ.~žäT›ô}îïHv$h„Ëc°œfÓÛ5Òˆ”Ï™ÀŸÍÆé²Z¯‘gÏ.M¦GøZ<(Ê“w=Æ‚fÖšx¼Ú_E¹Ië VÁ09Ç~ß!YðÕÞ ‚ÙÁ*àþЮŽR3CUN˜¿¾«Ç[ítx¼È!D@ð`Ž©½á°e9ªLæŒè9”dD«MF1™^œszqŽC PO›Òû3MÄ!ìoˆKÌ»=#+{˜ÂÀ*Ræ/©qL¬Ï@šûfg'KœH­ÏŒöq§6°ÓìÏ©µ‹kêÆÓUµ§a¦^euáå²TîÖ_f:{/R.‚Øûr·ÃË“"_ªV¢opé½Ý>-òžÚ€˜‡¤®/0YÅ™t¦<ÓtL9ÉzA– ²`0ša»"FV_0¡ÀÉ€×)äžpya ÕEe³I¦0‡¨©ùU–ã!½µùTƒÙ0Z56#±ÙN÷¨Lz‹aôHLNå lŠ$3Èìκ™4I«à×ì#n{ÂòíµX»©JKYqkÔÒ;ÈBž9ÁL*.æ!1*¡‡z“¸°Ó 5¼JÁfx«,qÕ¦7Ìâþ®“˜ktR›ñ¡ÙŸTó› Z^„²9¶ÓÏ »î¿¨ý,í¬çËßÒhÝàó.¦¡Ï:Ÿfs…ÚÔeËn ;Nïî¼-m‹åÃvl…ÌB™×NdÑ!Õ†9ºÐóõ£ÞdèQqV3±Z¨)l†v Ë`t”u¦Kº‚jâ £L1çÃ(êÆ8r`yFú òì*B/‰Æû¾î‚Ó¯ŠÓ/6[Û„'¦ Û¾ý´nÆv´óÆÔ,æÃøéÈ5! ÊINt¦“Ÿ¥ð‚F+‚˜»‰Ÿ<©¿o&·Ÿá£µ=k8¥Ûõ-PèÃĸéM˜i0K7jã/ïoLã*75‹¶iïªQ½¢”ÐÉŠ€Eq©C–` >% 9:æ·ÈpÚ|Òp_r«mý÷—Ðhb³eQÚ–Zøþûô¦Ô¬r.ÞBI9Û%Ü¿W&SPnrp»ªrlq–GŽ}$*WÃ?›®z*¾ Jϱ •¾RüH¸Œ}Dgéôi_¥ÏИj8uòÔIÌÚ<§ª(m硞Tá6rÜ)gº ïû}tÊ-tꞟðêèéOÐtLïû[ +‚¦þ<Fвö€„¥ àˆï.—îS,A™â¶Ô¬ë®ãñäa]_‰!ñ\¬œYÁz©…‹¡Ào ä[ œqWû -HZ’³-³ ‹[,«óïöÜØfqçñÐ ÷àrc =ê  ‡ÈŸµ§œ³Œûõü8Ó›éK”V‰U…CÅ Çé—êg%–˜×`2è/–_öŠR+•¢ìRoߨË79^>ËYk¬ë± X—UÚlÝaýs9´õ Àé|;T òYäVmþ‡Uæ~0ýAÀܱÇ÷ú6b>5ÇýI5 ¹kƒs}C‚)Š2… 2„!͉"*ñmOxâÛt^#[䔂 ÇÑy 8©Î|Ä=C¹>[{ŽÓ~`v;»Hµæª|No$^ß—ª 34¿a‹wörß<Þ‡ŸS-aLç©:TŶTÑ*D¶Nªø½9¾¬¢ìgä×d–)×ÈL†‘‹ø•&Xeod6÷°1`-`«UëÈ}*ÏfE¢îH N ©‹jå XÝ™øüs¥8ŽÙëééÂÚ%¤³› Á¹×ÂÜ¡ç´ù¤)]ÉÌ]x,ý‚¦vp'^—{.œ|ŠÎÜ|ѪX“ά¨àãU¸I¨ž¶oöŽƒç/¼ lÎ3hÉàµË¯Êhõ]þú k¯S Þ¬ƒ!ï%GæMê;wÖÌã××ybÕ…+.tY-ìãå½Îihò5Ý8\2¬÷•'~[uN%ý8/¤äÕ]ÐxÑÈs®Ó3²4ÍóY|í‹fõ*™mÙR™4X’j2[3Yf&ª^Cç…ægnÈ”û9«=Õ˜Z3"Ô'-lIÏÅÙúË,3m—{.Ê|ßü÷ÿgÎo½ßúÿžñifW¦?"ÙŠ\ÅRM•FØê¤K¥2~UÌŠÛ*BÊC0ŒîÕäË~Ý„e¤T会Mbº²Ð¤Q©I+š€AÂ3ÝZV9 Ö‚¬œN|ª…øµˆcÔ´Ù!Ìà‚JÔ %Bc‡(¢?[h+=AÅLÌ1‰I¢¼Â„ka§Õ N`T#ª9þÔÁ‰~,ˆ…Ûª¸UN«þj ŠÂÖÅ_Aýáó+þྃtP'ƒj/Ð?=Dbá$„Z,§ý&MX¢Ä^^Òk™)˜Dž‹Õ9)¤gÐ^µ-ØyÉŽ&5õßÎa¥õ·,yò‘ÅKžDyÁëG®iaêûÔ;÷ÑMÏÔßxôåן;ªù u]_ Y:±ÛÏ/µ.·Q›‰òò ù¢#dÒùB"Vávëô¼ÿ:­ÿ:Dæ#·ƒ-Oi'޾õœ Ã\XÌûOjóþÏ7˜ifh s wŒsŒ·ÑÙ轇Ý#ÜmyXy8`Ö[üÆÙl–0[Zlžoi¶â8þNãS?«çqìØ9²ŸAåÿ'º.ÈQ4õFaä4Y­Ã <œ 5´iªI§i ?\Z÷;^K6afIq¶C%¬d”ŒvÀcÄ÷,§ñ:€hðuvf ¸N›#ÒÍiBåô#Ëß^<û­ë7íîŒ<¹xÉ#[¯¾òÕ÷¯ûí¡ÍTh5€YQÏçxå¥gŸûà•#\އ ƒÏÜÀÙÕ›IBnx I)i¨7MæHó ÓMz„XøJ\ÚHWGs(#Ä·¹Ž÷¥_]§bG?ŸÐGm`@h”kö„¦8榄®”¯tŸb§| þ£›Åë­óðè£à Ù6([0÷Fƒ!£Žìgó¥ zäÙ!pÆ‹ŒÑNp¸W…ë÷‘z^Ü@ºVNó ¹¥­(¨ dBÅîΉ—ò½:€«ÚLšé)Q²ujvAi¦Pxì¤1…ŽN3–уiõ”S¿—ŠÉDmçq¤paºio-¬ÉK »¨îlJ/ÔÊÃÜÕVQeÕÃbéd·KÕ"ž4Šè8©pñþÂïö}•D]½ÿKàôWÆ]«¦®ëü€2WŽ»aÙ6:ÎûPV°p^êXê%²cÿLºqõÀ™jrÒ $6Ã.õR‹v¨Í_ä/öc ,ÿ=æ{-Û,ú€%ÏÒê?äý|Dò™¥z‹`¶…ŒÔÍ.§(Èĸ+–u9UÑ›#bJà­L|ûT–ò½še–n Ô¯rFñ«0J·˜§9YœuH¡fOi¬£©/§}üž[jðªG4àWmþ?yÈç?H÷“(9…•Ú¹I|–PÝÁWªƒhÜ]„'ÐÉZ‘± óx :Y;IAÒØe[¢¦C[zœ²€›ñ%|¶ÔR<óàæ«jìÚ¼Ù¸~ɈIÁʾ£½öšp÷º¦9¥CÆ;î3i¼dÝéKÁç¥F ߀'øäyj£É$¹ M9®¦Á.ÙáÏ(4Å]…±*S¹k˜iˆkœn‚i¦éWãnkïXaî¹±ssGän(ÜR¨+–ç×1 ‰Î›?K75:5¿±°¹ðƒÜ¯¢ßžϵ{=²»ílË 9uš.Q"H[pMÒLaÉT¯ìµ¯ ÙŒƒ³Bf£Ç]’SbÌñù^÷RÅ«z½Í^±zV_¨Íjðj‚M³+5ÁæÕ_PA[Þè›´`ãwñº€Óê0ÎÑÞE6šC²2³Ÿ±½fûÄÖe3m5¶‘PuÏØ Å0ݳé±Õò éåAøy¹ÞæO.Šr‡PR›\Àaý“qÇOýŸÆ¾>ªêÞÿœ3wî6Û%³d’Ìd™IÈ Á ‘Ü`0…    Ve â†[w¨}O±­‚Ôgmm%$Ȧ>hiµE©Ø¢¶Z+íC[ ¯ÏR—&ùçN}þ?ÿÿ$¿sÎ=÷Üí¬¿ó[ÁùCG*ul BAdEŒT$Iütñ¦Ä®Ìl=SýŠ­žÚsWÝ|gÜÏW÷¼uâšWï{îÆ'½µù??|ø‰›×<ù“¯rvá™Ú…sê{îáMooäüÞÝÿZòÉ+×?åª~ußÞ—÷¿°Ÿövë  Eº޽]¿ôÚ¼H$;£Œ†¿‰=>Ef5Æu1äÀˆ |‡@‘[‹@a#cØ£ÆÔ |ŸÁ£¨aqQSˆ0U2ŒÐÁVú¯v*êk¨D"S2‹4TŒ5 J}B›¤ ˜"OB‰i’«S×=Ë£›£=ÑÁ¨ì¥iœZx‡øP§ ¹]èGÑ PÂŽÉQê –"Gî¨ÕgNƒ;x(–x8›V0 Íxj_AÄ @ÚXœÚSPã˜xJH”9:ýª_ËøUo’ûtŒKw‚5UØÃELâ¦hQ0!Ä&µPÕ‚àºík÷­~ºmûµWM¿¯ háߘûø÷úç‰ÇÖÝ4ãþ›ûŸÅ˜¼ …SÀû4vоÔC_Ðnl06=Æ>ã]ã„¡1#e,‡íMù¬#Æ a¦`>;`MMu­ÍÁ pUËÀH÷&e³Ò£ìSŽ(ê>å„"˜’VáHQ|Y\„D¾Þ /ˆ&S ŠˆPÎl8çÌlH8<@$þE4¨ÏMÓ¿\{;—<À¼½NÚpÑ2±rENês£Vîܾ}»ò—W^ù¼@É~þ&¦õÁ-°VÙ(¿9Ä^³'‚fâ§ŒrßávÇt·[S ×ì3îóƒa УÑzT­(Ø€Ô`XTʘæOyš=ívŸÙõÔ<ލ«Ü,xäÎÒü;¨ï!Ôé;`î }Á“G~RJtjTKLûP™ˆµ±‚5O¥}¾Ê•q£F­³t5 â×­@V·`UÓðkI¨wP ó‹Ð9®§ñ.¹†üÛ®,“ª³}TËCS”^}õÓ›öOy@éü|óϧ.¤ñоàúõâ ì$í±f«³Ô9†+àû÷IP@‡TaðÇœ—“ÀP>fKa˜‹\×™"¤¦Ã’Êz¢/TIT×Û‡ÀçGF©Ì°oCŽª€ÒªÖ“ÐêYæló:׵曮÷Uí •—«Y-£7¨cf_»¯CéPgkÆÍÊ î‡Ôß(¯«GÕ´ªŸê!‚î.EÔ;„ÞM°-õŒ#ëù÷Œ#ÿn¢Ã*Ä„”>¼0bì^ñ ì /‚ÆRþbÀ.MËý$h…€y2Ld°_d µcäÞÁÙrìË'£€û²'3l1Öå†>á:V±–ž°›Ðan°øFëPüH|0®¤õˆ? çâjÔgúü^E\âYq‰sy$¶å‘غ<¶å‘K·§ŒSÚ$¶å‘ØŽ?uÔ#±-Ÿ„æ?-}¡óðA0X¦™¥?¼â'âby|s¼'¾/®Ä¡i_•có$Œš:#ïô<ár†ài„ ¨9†¡ƒp9t>zDèËÜ´˜4»#Ç’e!©»˜šÎüA! èZù#0xòUƒ†©›T¸¬,èI0CùF&UJL§àmR+çi¹²a&^·åÚ·ç?6Ý2·W_5¹ëJö¡­—O­½¹¿KÜqÍÕ-¼ÜŸ×·ný íèc ~ÕøA÷˜f=fwQVBžifÂ;I¬ÏR;ô¯«‹u½Îj 5FGÇ'Zm¡¶èÄx§»Ó¸Ðšš½0~µûjc¡uuèêèÂøu¼ÀPݾK\ 1/ñ.u-r/2—zÍX‘¢1iD*’r÷“”Anæu4IÐÉi]§‡Ó'äûɵ„LP³#±Ï†ƒˆ:¨‘1ÍÒÒ ëœý.f ÊŸBä¤áfÁÑÜÒ²LRÒ‚Š—@(Éùq+g 2µŒ–¶qKš;»È ’Ë•oÀ@T˜ ƒÒ§ÛS’Y?ÂdK4Z¸ŒîÆeîË …V'*–Æì s )xgn‹Z¿ëoñèM¹çÝv÷®»£·ïöu½p"Wyÿê÷úþ弄û^~éåWñÒIK_žR)Ú0[|—Ù÷{­³¬s¬6KiN÷¤E*=Ì[^\[P[<¡xyzCZoŒ5&ÏŸìÐ/ñvÆ:“Kô«¼‹­«cW%÷¥y;þváoKŽFŽ–I¦£åJÎÊŒV-H®Ys¬?yþR<`y‚~€ˆ„®FABgþDÅ!“[¦mÎßYIËFLËîögدDm›²)qLsyÞÊ'µ¦Ä8f—Su›«xx”Ê0öÕ”ó!‚¹œ‘ósI0>E0?)gdI[wæRÞÓ$:3O¤@0çg ¼9“1æ_&—cgDc’†äµ<<4±BRšÆª Â¬Ú)zùºÇ¸òÎCK®}÷¦9ëGŸX}ýS?XÕµm`±ûù»/¸àÞÁßøüž¯5öîzüàÏ_zí¥ooòÀb×´¡ÅŠøû~ȉêø8Ñ&nðªÍ͉¶Ä†’Í%îºp]²¹¤5ÜšÙ;yyøòäü’î’Ãêk¡?«x?Œ[ÃD™7 –ÑÞ)â<ï±XüÞûVüýè‰?'ÿ%°g)Õ¯F@—cþ˜ SZ‡Ü Øùî€R"‰0 I$IŒÀ4§²$1" ‰ÈÅbJMˆÒêG“…ÄEdñfªèÀªàÿ¦²VÐ@#j*BI‡ÐäÓ$Õ\K—|‘ñÖþi+ö¥†5xص–ÔpI3Éá ´ÕáÕ]ôüÀñe¿]û‹[úK|}×[W_û}°¹õqÓø®møæ÷v®ë'îñðë/bha»óÚ%È~e« sKáåJr.Ž^¡¬RT#¨ºá  ƒÌ4ª6À¶FY:Ìâ,øßßŸÂø>±ƒgìï!¼.W£3ð Ù‹‰åŽÙÈAõ§…& qäÔƒ%¥ èÄÜW’ÅêµdjKŠ+ÁúùÑ¡>¼’,N8˜CWƒÖ}ðö-ã7_réø Æ])Q²­˜ÜøƒÊIÍóWöv꡼m¨‡‘®˜}“R)k4Î7Z+f•-*[cÜoÜVñDø©á?sùŒXa<6²møë1wú›Âªåf¼Sï4:ÍNO§·Ó·D_b,1—x–x—ø¶g·WH̸bؘŠ9f‡gavaÕªòUõÿ¶ù=ïU ÿ÷‘›?ô~¿òñª¾ì/²Q0´Œ´l(Q>”¨JÈ2TO² %dJÈ2”(ƦÕ4ÌÑ+3^S)Lg ψâBb•%†Sõ§͉öļÄÖÄ+ 5H%–%ÞM(©Äú„H<Ö)@ÏTo˜9„ËHÝÑ‚×XåXð€§/­£Ø&`ÎGt/-ÅE°#bHK©§‚â@Ód˜fA¥h„')òŠ„Ž×ÕÒå5’n+ñ\Z‡AÃňA˜¦+iº*!7 IùN€™Ý«UTãÒgŠUs¤þ,ç\$M ™ z@âÃ4T« å£JA‡Ÿ_»¯V4×v׊Z¢àW0ù̼›€´SËø ½%{õ銀œ„òõi9ƒÁ¯ˆYBjÄæÉeïmogçÉôèyÒƒ· ȾrZžžË­8Ãîñ…š?Z¾3VJwŠ€ã?Ï å®<«¤àlÐ YaË¥–ùÒIfTiIî> AI‡¥þò$+ƒYp}HU•†©æ¸^³Š ßrS´ÍUçn½ä°¡YO„"Ñ)+Ý•ÙJøÛ…c‰}Ÿ)8Ú¨t…i*Ûܸë¦5×Î|û…‡Û[ÆVkÆÍÏÏ öx»¯YÖ$oÛûЬÅ/ÜüÊïù9EW­\ÔzNy’á&cGØ(Þ$óéA‡;FŽhhž¿ kx .@ˆ€°"It5¯yíÒï·[žížà5\pÿ¸íßÛ>ùêöÑ]âþ¾ûΞtÁŒõwŠ"›¢}ÐH®ch“˜—ˆ¹ufê*WO©TPt×äÎÔ $-¹s´›³²`ƒI3¼/Ø``ÃY§SÁúûcJ–1JüÎ6JJëX³ ÐtXŽÞ´×V€Eï0VS! l´9™M2gÁž]‡>Û¸‚_!ë‹ëD˜Å úõÆuæ:¾NÜáºK»S¿Ûx„m4¾eþ˜m1Ÿg;µmæ¯Ø/Ì7Ùkæ_Ùûæçìcs8>ÇŒ³¨YÅÈi;1Ím‡¢unljëò”7xaôé ïô1¤Ñ&¹~º#åI¤–'d®p»½L5oç E8˜;˜c5§)êMâg 3b&˜… 5J {7¬tÍ.Õ Nʸ»“eºmÛŽïKž|ÆQ Vcxö…ÍË<þ†F/”ïû!n\ÿè(iNa¸6`3ëÈ%yñ´8<Ȇ˜:¥|Òé>çè3HñuÈ­ó§–þçÑ dÎþº{à%ÛÛ×—Í\-î”Ýýƒdpw¢„”â!»!ÂPå 䈅ÉvXº“Àê r,LSˆí- '°¼R*hËc3èâph ¡¾¨ŸWÊ8zaÊ>ЃR1O„Â%Xy´^?h–&$òZòûèÓh­HbFxµ2Ìç/ Þ¯Xå^‡È…ßIà¶'l#UZgAOÃú„½3UQ§¨^#¬&DÈ­0EõÀÞ…²zjEzÒSŒ½lF«Ösþ:6ZkÔÇù[]“T[›ª·yÎ L žº$paè*ØŠÿzèõFm•¾[ÝØú‡ú¹Qå V±*_¥¿*Pª‰Œeõ¡ëô;ô®‡¼?àOŠ'=›a;Ô=þ_‚öý{ã˜r,ð_¡ÕÏŒ"ÔÊôÊÐ’¡_††ò7iúJˆu Äñ@ÆOÛ9¿æòqo<ÿ×ízš©|èÕ”à> «¦'˜5sÁ™Ê…fgpipMðî 4ôFj§a¹=SÁ¤F/Õ6HPâÏÁ&m0ùHñDsCBZÇ^Å´ §ºk° ú&!à-Sì+Ì€?½?¨ÁTM0ÊÆŒíœñù#°Z¡ƒÐ“3ub뤒+°µ«…=ôú}òõB˜ËÉ žôZýÌŒœ´||>L9uû\¾]üÔo7ù2ó’ËÙ¼Â, Þ3Ítä±Ü|¾¤üÿÁ3üdø$F'¦ÂY^¶kðOÃlnü«5Qòã?Fßÿ‡"Šy&hëI͘½ª iñLRs€ðÐv62†\ÿ)¯-•’ÚzêfÀŽ>xh›F†û;ÚzJ!á=Jª¨èƒG¶ii'7„\2}¹›n´è îùêP¯6’îØËÆ 2~'º¹¼]“ס:b¦•4ù³‘Z.’{à<¼•‡0À·…‰ìßANâRY!'-nFŒÔ„ Ǥ:Œ«ÒÅÛžÝóÃfeÔwo}ÎŽ­ÛŸýá°70Å|÷hð€¸¦ãKÅŸ¿)Ö<ó¯WäZÀZôߘk,þ‡üZTà–“ ºàCŸ H¼í¥€°£ …®°Qà+´FóFÏ­ü~^ºXéÐ:<³ýñæFÏN±ËûKÏÿËÖ›®×ŒW}oY2CCà +¡` îzç@c…R©±W{*Q¿¨±rš<(h­ ½ ¥•@ÀgÒX±LVêMëö‚!¬¼3`謼ÎTæLµ´¨­˜í!šâ[ë-3 Tc­ ¥ƒäN[®vKÓ¬çÚþ´k­(kǼ=%¸FnXç~ì,X/¬?ÁŸ‘´t¦† dÛó ¹O“**PP‘Ê)?wBDÔyÁ¥ÂzBl¨íþxqÈ¿PH)†O–<·Ää1MPj‡ŽdtJ ½ÙWtH*&깤"ì|L}=ñŠ\•0“{ÛÀÃï}DÑðLßßâ÷¼ýfãÀ¢Š|:iä„QŸxûÍÏï*…¼ÉßÐG ù?ó}¤ØŒ\WQ"R=jØAúÂö¦ó}%Q“+|»0~LŠäfzè8}"ŽùéûꢆªÈ¬ÀV®Ål4HºjdEÌ䆢¾x¨ÒSé­ôñŽñö?ôT…ªÂ“£¡ŽpGÁâÐâðâ‚ÔÕ¾‚7Fn,¸ÝwwðÞнá»"Í'=ÏYÏ÷D>4ÿ+ò_¿õid°¨d¨GE¡¶T­Û .’8õú1ÁQˆ&ͧzhïAá.ì! ‡3!3‚8w z3›aª}^(¨Ð÷³"«HÔí-E»Dó3Ô…Ù%fÚžæóB{ahŸ°#ÀËØÄ$¦Æ™NmÁ4âHo»×5Ý;(õ¡&ôÕ@÷ØžL¯ÁÔˆÊë'½èDd¢7n}|4ç€+>*„@¾LÁü¶Ô¯ˆÁIê)§œÔ¥0é‘ÂS[óMóͳ°þsŒyÑô•ïV»YfdêLXXƒ°ú±g  Äï(ì£÷`®ÂºO¸’0])c×Â,•["ã†7M†û`·gàꟽ+KåÞß>°´¥bäšYu_ÿ¡UU‘¼*P¬Tõ?|í­kV‹«>ÿåÖ 3®ÂÜsýÊÏ·Ú>8ú•.B¼ÖQ5ú5¬¶Åêøx`®˜SfŸÄ0QeÔXJ7§ðóÄyú£Ýêä3ÅL}Ž1ÝZÊ/—ƒør_¥ßdÜÃo‡í§üc2\žåÃôœÑ ÿ‡þ×h´ì´ ê&X !‡írl§E£a pº3\@íEp2Ù,ª†j.ð1¬çÛ†\Ïs~ZFíXÝ곌U¸JûØ–œ2Ðû6CÙÅoûçû»ý'ün)ÿ‚ ¤jW1s-ç[o‡OÉAðݤ‘–X«JiÚ Ù…<'ÖW4… 5n?‘š¬?a£ø')lIÙºGyC^ ÂÓˆÇ$ñ ¬@Eu× S{:Õ%Ž~¶“j‘ªR„Ç-©“DkÜ;½©CãDÇvB—M&Ï!ô¬7Fg BmàI‹Âèé‰j6j¹£f3fTiA•x¼kö@»kaÿO—ݰ„ÿå—®>p]ÿ¥7ߥvn€×5¬f7ÙS'xU(»#hq¿.ƒ uÑÊ(V]íMeÒaLÄA̽©ôÓÉP0ãN4&3YW£ûéêÊÆìÓÕÕó`uQ°Å¹åŽ6äðÀc]ÿ$“‡J£ýt§æðš¨vøŠðx‚=äú1£°£†¸¬˜;nDhGY jø¼n˜ºúÓÆÙ“ÇfgM]Ùê ]<÷šË½e^jìeÃí™ ‘^0U,íwói£g^qŽe>ÒöÏ&^µê¬Ï¿±?Ó:æ+öõGù•wO󛾿ß¿R~ÿ­äwóo}øòa 6PœÅ ëÃ÷£“ÿÍn@"VƒÁîKgJƒþŒ/\šNû}O‡ ‹2•‘ÆPF­l¬zš+êÓpÒ®ºxøwá8””ùO×*äKõ@r‰² üPp‚9}µä’Gº®„ýÊ 'Š•_ªÚ?»ëR·vf5ˆ…ßÓïè}Ñ¢s,Ï#mßüìÜe·gêŸycöÜÑXÑ+Øx×tÔ‹Ç0 I¿¥ó°&ºOp5d6V©(.Ól‚ÇÝD¯Öô0±X_òþþ´÷ê§¿ünËú(áøQÊ”„ãù;ùÛ¿†S ü€ký‹P–aTºX‰à£Iÿ(&HHç¥gzW%$Eéâ¾õ-:Ù ×ÿˆ9h#ò1þ;»sÈ¢â¸v<,ÞÕÞ ‹W´WÂb¯¶7,¶j[Ãb“¶),ÖkëÃb­¶6,>×?ˆ¥úÒˆ˜£Ï‰¯îˆHX×@ ñ0WàS¿ëSá÷ îmò±&t°¦Û5áeÚ-Úzpïxxl¤Éïó6­¶c…uþk¹6Vo‚ûÕ&—k=t»ñp¦ LÙ±oKýœÜ$¢¯!ƒì¦Bö…V’!j8þA'jìÿ¬X•-ç'ÉåÒÁÄê¡w:Í#?MW_2¼¾ÎÅÿm(¥üüÕÿ¸£iú°ób—\|:…¶l’ë1Íý+YWoÙÓd]ÐODס¼yD;‡´Ca±OÛ=ZOXlѶ„ÅÚañ ía±\[‹ôE1CŸ‘¯«€×ãb‘§ÂT;^*ÍêâúSeŒä¨B5ìoò¢Æ*}±ñ£ ó]+ú *­’‘–ûY_``æý•Pe9Í R~³ƒ±ºNÕÔ òcâHÁ“šôéKn(}ñOS¹K†CèwC åTѸ †MŠÎ›q:Ec¿Ûõ?üÙ¯ÖÚcnÒïÕ…OçýG;®‰çÉ6ÿwµ§51G[ª ]CwÁwÂÂ×Ç’‹­õ¤HCN@4ÜK~"ztdáù‘N÷ Œ|ú¸¹ü(£Žƒ?Ô¼Kj|úœ3Û»å«ZYÎUK]ðsdÛ®²³¿ÕÞ×Ä6m¿&þ®óoëé¢Kÿ†Ãd‹@Fÿ-–o Ù@òÅÙ©ÖÍñ…¥i/ê¼bÞu2ê¨ÔSIàn¨š×|UíR‚<"Ö7~ò»­óMÿГ:2ûùÁ{¥YŠI2Q{MmA6ŒhÊÓò:müÀ4v®Å>ÛúÙ–s§üI-PQX4 ‹¿Á.UºX`ŠVÌ®sÏb³áuyø!k\ÅÌV~ÌV¢ìpÜ‚x]‡òÞ4f ”7°0ƒŽQv7]‹{,§ûȸ‹ÍÑSl™{Ö`?ž÷ ûEvàQ¤·(ï³'Õv5ŽÇu{Æê© îõ ú#¶ùßÃùË‘÷(âÙ8~ éN\72Ÿ6´ûXy@Eþ0Üç@¥ë§lŒÒ5øîu>àÜ{:âóm8F<°Ž¿Èîä/nÁyÄì›xî:Ê´æãɨ‹Ûq¾×U ï›Hâù*â P%~Ì5hµãxî¿pÍ À$ܳ°-ÅßÞ$º]3•ó•k”§A“[é~ÂýGUU+ÔN­Q{RÿOãÇ`¸5z³Þ5¾üGþÀ5žÀ1k¸õDð‰ÐšðäðŸ#wGþZ09jEÿ-¦ÆnŒ?ÿCáÊäää‹®(ž_ò`ê¯éÖôë¥Êz*ÆdÌÌ2G²¡ì¬ìòÊ7« {¼zmîÒáúYì¬ßŒøÚˆß¬¹åì=µŽê®»lôˆÑ½õwŒ=ÖðIÓˆsìñK›ç7±/³´{[&·¼3aÌ„;eŸ[Àþ+´@X€ë]Ãf¡\©#Ðx…Í÷LvÙìçÏèèȵ¬\¼`éÔ™² V’䝸-@ž wRqw}ÜÄ\ìe>Ü)€gqï0‹€ƒÅj‡}”B–dEð8PÂR, †2VÎ@XcYV ,|0³ÎÎb#ðž#ÙÙ¬–b ¸±1¬že ¬‘ƒ×äsØxf³6µ²‰ì<øÐ™Ì¦°óYû›Ê¦Ašl:»€]Èf°™ì"|ïÅl6ë`sØ%¬n«/eóÁ;ìeðäÅv°ú¢¥K/ïZÜ…OÙÍfºÀÿŒ§=çÆŽ„kXo®8µ$âÞq){—«¼/TPh9Ë•F½ÕÈ0p`+`/@aó\äÀBx  °°pº*B:›,l¨®bWQ/ü7·Tº¸–¬Ö\1v0p±Â@;``=`@•å(gàÀ^À €ÊlW¬÷Qx÷Xï=2ê[²´V.p;çÊþ‹;œxêNÜ:Å)Öè;»ÎÉ1Á‰+‡;q(SÛ›÷™¾Ú}-PZÂGBÆ-GÈÅÏე£¹7» X@@ÆÉ±]¡¾Šlí¦½.…Aûá…,5¸ÏÅ{}ÁÚS Šãè@)ñ7ñ‘sF|ÔçÖnj9_ü‘mì¸Äñ÷žxÝ"ŽP#llì¼8PÅü½‹¿wÄ;, Þf5€fÀ<À&À^Àq€&ÞFh‰?и!¥›Bü¡%ÞÂg½…0 ÞDêMñæà>ñÛÞú†ÚÝ2‘«É'R™|"–Ì'BÑÚ]â7½ŸCÊ¢¥Ñ£žu•¡;r•õfÎNírÅ{›Ã1Óû}é\jsËHq˜õÞä0ž|˜¥ÓóË*R¯#õ:ëllôÐËZ€´8xð:ìѽÎlÀt€.õâ1»Ä+½Ù ©–¨øµx5%Š_Êøeñ‚Œ_¿ñ¯cèŠâ…Þ’kñà<Ã5b q λÅOû*B©Á– Ø‹L!¬4ÚóëªØ+Êz¦B¸É³ìfá”èeÈø ¶Egö’”=0MA¶ñ¤lJÃP§}ðaR½ÿ¤(ÈÞv/Rdo¼) ²KW#EAvá¤(ÈΙ‡Ùö™H!Ø%ÝYQ™ªo¿Š§[â:ÔÒu¨¥ëPK×Á ÚuôÇ>Ū‘ßí­®F}ÇÎ «NuïáÝÏñî y÷Þ½ˆw¯åÝ·òî&Þ})ïÎñî"Þ]»mÞý,‹ªèæöö/6ØqÞ}€wÿ„wwñî,ïÎðî Þæõö.QÚ;£ÑDõµÐ ¥}çŒÇ쥨ÑRôùRÌ {¾”G6 ¥ËœÂ‰ŠËúª›ãµËZ&‹ý¸p?ša?{  ö£íÇMöãv„Í€y€}€ã€A€ŠÒeøŽõ2 ¬4æn¨òuŽãUàä !½âVùb5›ít$ö㯠¥¢îQЬœ5ÙµT¹Þ^2X"êY4Š9ÔáÒݷ㟾OþécF‹!îë±€¤Ä†|¼¾÷Óbø1ÞØ›}6ÕRÀb%Oñ–åÄcY—<ÍŠtʯcEâ)ĵ½E³pY 7;ýtÕŽÔ§EGS€~†ä±¢gSo¤w)¼7õržÚ‘:\tWêW5»tä<—…²ÞÔž´,º»hlê'dÑ[qâ;½©µíHÝ\4)uU‘<±È9qiŽì@êÂìœÔdܯµè²”Ý…{îH5]šjrJ¦kv¤FârN²/;¬H>´¼DÞð¢ú]°[;\{P› Ú1Z­6\+ÕRZ±–Ô"zH·t¿îÕM]#Y ¦GHS+G+~D•H'¡C0”"ÓfNÓ Bp]tÃ`=aW›h›1³ö]ÎÚ.K÷œœQ¾‹›Ìéq—Oà=¡6Ö6sBÏØ\Û.mðžú\[6ý’ÙÛ8¿¿¹=âÎ]œÍœ½‹RÖíɞйàQp¼ý¾$ÅU·ß×ÑÁâÑÕÍñæÐø`Ãy­_Ì—™ó[‰fÿFuêÏ÷<Ø6cvÏŠ;zj)1X FÉ·g¤ácœÿŸ˜Øº›ÿ7E³w»Æó¿O¼ò]ã[;:ÚvñY²öÿrè1ˆPNÇÂLåXZ/qÊ}Ç)—Áõ(WAÊpÎ(Ëe C–S8•ÛÖU1±u[”‰¥Y—,ÓKŸYæ@e2P&ÚÍÈ2¢ÝT¦g¼¼MQŠ” @^ÈŠd‘"^(‹È7ß&‹Ôä‹ÜuªÈ]òI.çmd pß‘¡2¾#(sªÿ_‰E@¹ê×qyçÄEåç—O\˜ßsÏê+ã=Ý—¥ÓÛ.ï éWvþe—_Iñ‚E=å‹Z{./oMo'¯ûÒéN:=®¼uëœ8sö¶N{Qkï8{ÜÄò­}“¦×ÕáYwzVÝô¯xÖtºl›¦·M’×}éYõtz=«žžUOÏšdO’Ïb²OŸ½Mg:@½”qÌ ¡¿ÎO–vLˆZËÇËÎ;®4¾6¹ØÊ“Ì“ëèñ–Oèñ¨_ŸÕrV ˜¢S~dò§âkÇ•&÷ð'ó§,dË'°Üªk»®eñ‰‹[ÿ.üµêZj 'ÌQÞWþPdb½ µkƒ¡ºjPÖ›AYߦiÈßڼơ> endobj 284 0 obj [ 278 0 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 0 278 469 556 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 0 334 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 1000 0 0 0 0 0 0 556 0 333 333 222 222 ] endobj 8 0 obj << /Type /Font /Subtype /TrueType /BaseFont /XSJSYY+ArialMT /FontDescriptor 283 0 R /Widths 284 0 R /FirstChar 32 /LastChar 213 /Encoding /MacRomanEncoding >> endobj 1 0 obj << /Title (altairz80_doc.doc) /Author (Peter Schorn) /Subject () /AAPL:Keywords [ () ] /Keywords () /Creator (Microsoft Word) /Producer (Mac OS X 10.5.4 Quartz PDFContext) /CreationDate (D:20080817080809Z00'00') /ModDate (D:20080817080809Z00'00') >> endobj xref 0 285 0000000000 65535 f 0000319879 00000 n 0000001484 00000 n 0000171609 00000 n 0000000022 00000 n 0000001464 00000 n 0000001588 00000 n 0000002613 00000 n 0000319705 00000 n 0000194492 00000 n 0000001698 00000 n 0000002593 00000 n 0000020163 00000 n 0000002649 00000 n 0000020141 00000 n 0000020270 00000 n 0000032694 00000 n 0000020369 00000 n 0000032672 00000 n 0000032801 00000 n 0000288761 00000 n 0000034011 00000 n 0000032913 00000 n 0000033990 00000 n 0000034118 00000 n 0000036062 00000 n 0000034229 00000 n 0000036041 00000 n 0000036169 00000 n 0000234973 00000 n 0000038766 00000 n 0000036293 00000 n 0000038745 00000 n 0000038873 00000 n 0000041028 00000 n 0000038997 00000 n 0000041007 00000 n 0000041135 00000 n 0000043192 00000 n 0000041246 00000 n 0000043171 00000 n 0000043299 00000 n 0000045320 00000 n 0000171733 00000 n 0000043410 00000 n 0000045299 00000 n 0000045428 00000 n 0000231274 00000 n 0000047510 00000 n 0000045552 00000 n 0000047489 00000 n 0000047618 00000 n 0000049752 00000 n 0000047742 00000 n 0000049731 00000 n 0000049860 00000 n 0000052019 00000 n 0000049971 00000 n 0000051998 00000 n 0000052127 00000 n 0000054391 00000 n 0000052238 00000 n 0000054370 00000 n 0000054499 00000 n 0000196862 00000 n 0000199591 00000 n 0000059193 00000 n 0000054623 00000 n 0000059172 00000 n 0000059301 00000 n 0000065021 00000 n 0000059425 00000 n 0000065000 00000 n 0000065129 00000 n 0000070557 00000 n 0000065228 00000 n 0000070536 00000 n 0000070665 00000 n 0000075119 00000 n 0000171859 00000 n 0000070764 00000 n 0000075098 00000 n 0000075227 00000 n 0000080240 00000 n 0000075351 00000 n 0000080219 00000 n 0000080348 00000 n 0000084782 00000 n 0000080447 00000 n 0000084761 00000 n 0000084890 00000 n 0000090025 00000 n 0000085014 00000 n 0000090004 00000 n 0000090133 00000 n 0000095130 00000 n 0000090232 00000 n 0000095109 00000 n 0000095238 00000 n 0000097777 00000 n 0000095337 00000 n 0000097755 00000 n 0000097887 00000 n 0000100022 00000 n 0000098012 00000 n 0000100000 00000 n 0000100133 00000 n 0000104942 00000 n 0000100258 00000 n 0000104920 00000 n 0000105053 00000 n 0000110317 00000 n 0000171987 00000 n 0000105166 00000 n 0000110295 00000 n 0000110429 00000 n 0000112027 00000 n 0000110529 00000 n 0000112005 00000 n 0000112139 00000 n 0000113588 00000 n 0000112264 00000 n 0000113566 00000 n 0000113700 00000 n 0000115145 00000 n 0000113825 00000 n 0000115123 00000 n 0000115257 00000 n 0000116840 00000 n 0000115382 00000 n 0000116818 00000 n 0000116952 00000 n 0000119256 00000 n 0000117077 00000 n 0000119234 00000 n 0000119368 00000 n 0000121857 00000 n 0000119506 00000 n 0000121835 00000 n 0000121969 00000 n 0000123907 00000 n 0000122107 00000 n 0000123885 00000 n 0000124019 00000 n 0000126095 00000 n 0000172122 00000 n 0000124157 00000 n 0000126073 00000 n 0000126207 00000 n 0000128174 00000 n 0000126332 00000 n 0000128152 00000 n 0000128286 00000 n 0000264458 00000 n 0000129702 00000 n 0000128425 00000 n 0000129680 00000 n 0000129814 00000 n 0000131274 00000 n 0000129941 00000 n 0000131252 00000 n 0000131386 00000 n 0000132826 00000 n 0000131513 00000 n 0000132804 00000 n 0000132938 00000 n 0000134146 00000 n 0000133065 00000 n 0000134125 00000 n 0000134258 00000 n 0000135932 00000 n 0000134385 00000 n 0000135910 00000 n 0000136044 00000 n 0000137647 00000 n 0000136183 00000 n 0000137625 00000 n 0000137759 00000 n 0000140262 00000 n 0000172257 00000 n 0000137884 00000 n 0000140240 00000 n 0000140374 00000 n 0000142592 00000 n 0000140486 00000 n 0000142570 00000 n 0000142704 00000 n 0000144206 00000 n 0000142843 00000 n 0000144184 00000 n 0000144318 00000 n 0000147882 00000 n 0000144457 00000 n 0000147860 00000 n 0000147994 00000 n 0000149930 00000 n 0000148106 00000 n 0000149908 00000 n 0000150042 00000 n 0000151475 00000 n 0000150181 00000 n 0000151453 00000 n 0000151587 00000 n 0000153778 00000 n 0000151726 00000 n 0000153756 00000 n 0000153890 00000 n 0000156216 00000 n 0000154016 00000 n 0000156194 00000 n 0000156328 00000 n 0000158324 00000 n 0000172392 00000 n 0000156466 00000 n 0000158302 00000 n 0000158436 00000 n 0000160028 00000 n 0000158574 00000 n 0000160006 00000 n 0000160140 00000 n 0000162236 00000 n 0000160278 00000 n 0000162214 00000 n 0000162348 00000 n 0000163792 00000 n 0000162486 00000 n 0000163770 00000 n 0000163904 00000 n 0000166044 00000 n 0000164016 00000 n 0000166022 00000 n 0000166156 00000 n 0000167003 00000 n 0000166294 00000 n 0000166982 00000 n 0000167115 00000 n 0000168916 00000 n 0000167227 00000 n 0000168894 00000 n 0000169028 00000 n 0000170030 00000 n 0000169166 00000 n 0000170009 00000 n 0000170142 00000 n 0000171385 00000 n 0000172527 00000 n 0000170254 00000 n 0000171363 00000 n 0000171497 00000 n 0000172606 00000 n 0000172746 00000 n 0000172799 00000 n 0000193892 00000 n 0000193915 00000 n 0000194157 00000 n 0000194671 00000 n 0000196580 00000 n 0000196602 00000 n 0000196837 00000 n 0000197037 00000 n 0000198987 00000 n 0000199009 00000 n 0000199243 00000 n 0000199268 00000 n 0000199570 00000 n 0000199755 00000 n 0000230450 00000 n 0000230473 00000 n 0000230703 00000 n 0000231456 00000 n 0000234685 00000 n 0000234707 00000 n 0000234948 00000 n 0000235153 00000 n 0000263873 00000 n 0000263896 00000 n 0000264131 00000 n 0000264646 00000 n 0000288216 00000 n 0000288239 00000 n 0000288486 00000 n 0000288946 00000 n 0000318866 00000 n 0000318889 00000 n 0000319126 00000 n trailer << /Size 285 /Root 250 0 R /Info 1 0 R /ID [ ] >> startxref 320144 %%EOF simh-3.8.1/DOCS/decodingh316.pdf0000644000175000017500000015650311042345315014252 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœí\]{Ô¸¾Ï¯ð]=…1–d}m¯Xh!û°í檰a2„”$3 ´}øÝ}ú{+ٖα}<OH2 7'²¾,½ïù’Æ¿$yÆx’ûÿA˜ŸV°S=Õ ÝŒØ{ºŒ˜¼ ‡Ú_Ī´xŸWk¦ oz’^Ĩ©–ü3.*ð¯Ï%´Ç郙DâO•ß"róÅìXFZi$‚Ho2o6Ú4… £x¿œ´ls®^EĹ>ORþ×siv‘æÈøÑ~!—´~U¼sÞ :²ÏÁ^Áª!ØPúmÅ#€ IÔSlŽ8yF†W ®A§ë©šdù‰¨H Šn0Ž«H'ò<Ë­¤²³o¨éoãæõ±—šé‹‰Ì´P¹m1QQ{{‡»á<veð<Á"fUX&šU§m¥^úŒÈ€-ÛdðËÖñ4K€­',&}@îgŸw+ }[Ò cÖð9CH…7újÑ®ïnÛãµiYZtJ(ÃFkÓa9faaÁ„ôb¬cë¡Ti̔ʹ âk^ {hcÑ7nù‚ëˆÆ‚—$Ôtkc|O¤|1-P| b˘իˆh×µºr<#ÙïÖæH€;¼læ+*¿E‚Iª¢ÞBbz_áöDªM³/vúkìé þ':¯FÃqȈ¶nŽÐOí÷”]Áûý¡«Â|•ŠÊÀä‚Q&tNK}ã.£éúÌ‹ýþF˜xÊ…F6àZ€ŽÛ˜›"ýß5ãWà5%Ð}È”÷†×qø°ÕB nÇN1/ûì^XLD@0`ˆ=W]ZÝ ¦|«Œ´[`_f~^.j”²aÂ:6¶8‘ÄZ‘Ä"#»Á€#hó´6»ˆ.Ç sx€£-DŸ !yG§>{-HiæFE‡UEd’ó@À]2V”ÿH/Ð ™ mF¿uš¤Z0D2ã1‰`Bá[=Æ£ ,Œg*åYŠ?bxFqî2µôqDGä4Él :²‘³(ñN»*ug k_’JV‰Ý:•Z/Žô¥FŽh¡û"G<«ûT¾ð‹Ó³,|‡ê­¤/}ž±uê¿:dpC&c ËWT€H âL ®½—êD¾sÓ9/ÚâRq~ÇJ‹8%l”mŸ-TË€h{Bù›´sqÈ$qa$Ún C þQJµ7ƒ”zF9§HDÞé%Y|s4*™ék˜ Â5µ­ÀωÕhÀ!qNW݃7¤\T@…ÐwD–‚a ³£`bzèÑ—~!cÏÐדÉTd…eÍ3‡uº¢›Î».¿•c®l<£ˆ@‰‰‰ä89A^§é ”Ð@0l®ÈyЦ¡Šîx¦£é!1o»Ú‰ª°xØ´…7í™>e›Ö„M%Pg\×=¶÷Ái¥ú³)ÕÚ!ž5R%òƒW‡?Œa2A"==”v#‡:{F_ö@ÇbõJøÒ¾X’²BÝ·*s -·µÔ‘/3o$âjÓg Ê ZQòhb™EG=’Éœ·À>’*=I•ûuçèï’luð6’J=^bmÛ7ฉѸT/A_¡#°ôq‰ßQö„È9 ç‰F½ÐL¿¥Ùë²mó&áŒoØ´‘§CYo‰‘É/™T Uàí ÊXƒ+ˆ¡Š¡ ²§BuI9ÿ$fÈÔP_:,dø¡Ð4ô0e^ÓÕqKX÷{µÄý¯PBá ïu ¿®Ç~ɦ„Èw ‚¸õ9€Û²/{Ü´Á÷y´»¹§ùÚY­™3¢â¦[Z‰Ÿ‡Þ­U(µÜ­ÌÝõ[‚¸qâ{@¼ìÔíQ¤]0ï5éîjÒÛ¯p£šYñÜÿ\¹h‹ýh½4Rõm¬wKµíX…¶¦F"(žZÝ<+o¢ì7ý«¨ÐÞô{°éS²‡G þâöžJe.„ :çU Pœòjª&è—ºáÎë®vhw±B´¼'ª MÎ ÖžÝÖ°#µ{`ûÆõÚVP‡µ¿‘ÊìÅk;„]…Ò8¬='±v3zm§mèïjw„¤/µçø…öjm¥/‚µ§¤.›À:¡Ø«µo Iã †ü²-£PtTµWkß”¶ÆÚ?öXÛCé–°öWÇD›cíwiB÷HúRPCè8òãÔ>Àów ž‚¸Â¿"mØ=`½Ó-½û9Ü FÑA::'1Šž"1:'¡ßWtïj7êÚBÚ}þ:ľãŠg¤8h¼7ÇÚͩƱpÚƒm‡À6&*i‚Mù¨å,~ü:\f&óßņQy&¤Ry`ðKÙL™*àKÙáûàþÁ!üd„¸Ø=îsQè‹Ó"VEß°Pä%yÔJ´j|Ó-ܧïû¤ü`2Ôü€>l?×ÜWcîÖw¾tg¿Ÿ§~ Üùx·\Íoü«ú•#sÕlB|õ—þ(ñù-þî»jX˜0 ³Ýâö=ã2Ój“Û÷;x¿^ë®×{ýÒ¹_ßL»¬u/õ..×·+t.×·oßaꪧ‡¾ËõQŒWêƒÁ¯\J¸nµ}Þ+˜7ú¸ß:~{zîåe½QæwÏïê)Ì…~‰Ç«(-ÛO½ö ïZ&K?’þ3ÔÊ„°¡ ¼]¡Ô¨‚ê¡ +ÌÉùÝjß4‡³ÿíÎ^#m§‘šŠè«±6ÍҴܘ]¸h°é¥ïëÊãϳƒ¿»ÿÿ"N ñendstream endobj 6 0 obj 3504 endobj 19 0 obj <> stream xœí\[s¹}÷¯˜Ç™,Ó´¤–ÔÚÜ –Ô.É&E(§öÁËÃà± Á Æ—%©Ê¿H~pÔéûÔ}zz<`XŒ_„îRŸó]e¿Ÿä™“¼ú …ã·ŸÛÉÙÕA]=yþ}[¸<;xPfªúWWðòñÛÉãC?Pè‰Ëœ™žä™sen›YŤôõÎé‰"+ÊÉáÛƒ£éd6W™¶ªøu”„SÓÿ¬íØk-J FWÅ ,~¦r·K|CóÎy‡‡>P¹É´3‡K‘+jCÅ×T¼€+°§°Ãw°ö÷°6ç×Píq69÷`–e³Ó½á=˜×t0¥+·ÁjQŽÕ‘bÊ'ó,~€}‹¿¸Çý—‹û­`ý%ã^í ÷÷M‹^ÜcàËÿ+ø…ýúpý%_ì ø÷ÿ£Iü}Åõ¾ÿјïqý)qýÅÂvqý;[5ýÃÇþWnÉ âþ× ë}Ãý[*žÀÚ \bAÅKØ÷v¸ìžrÇj@šÏýMŸ”¾¡í~îYq/ô3á5GU½ˆñ=?©o_SÚÐíCqøLš2û˘佂&°Ãm…ç] t0yžå¹¨Žõð¹!)Cëڭ΋‰É]›œõkþó`^ˆ"³Nz²¨¬ÖM}ó¿s4}<›^…ÈÂNofså~Íbz=›ËL+íjû"¯gÖϯ„¾¢®'T ]•™žÍŠÌ¾–·¯PååLfʸ¼º5á\úºãÙÜŸN›BX>é£f¯¢4¼–†­hÖ+6Ã5õ½œ ¿ZYÊpØÜÊdµk4ï;~„áÓVWԜƖaL5ÿMÝZ²C¨lG]N?ÌDž™"wYµÇ‰Nà˜Õ²•®©Ãƒú[I­ý¨PZÔ=M)-ßÉEÜÞ‚/_¡MZ‘)]±­¬à¢çÕ••¹¿ÇÏœ_IúæÔÌöïM¥23¹µº¹çzÿè"yû»¸ë%u8A•Ü+>´âw-mYkœp ‘DGá¢*,a‘°Ã˜ÄˆÀðô€ðÄ ÕrÎ_Äp.lå,Þ3ÍÄHÂÉEo§)âä®ú¨úþ‘”°cn3£…R ilŸ‹A}øÅ»²(K#ú ”j€ Ç-itæmÒ@†ß©¨ö[FÚx–wp-D56HPOÈqELb¤|IƒˆŸ+$)ý©g»gUŠÅ«zÍ\– »¼N“-Úc ÃhJÔRÐED&o›zñ®µÁ‹ØÎ*.ÙR ¼ìæOæJî¥ê|©ƒé(âº9µK*l_X-1ä[ñBKºa«xqû…¾ë¬”6Ó2/§?y³Äf¥õ7üŠ!”®áðe‚¥v'KÒE¬+›€æO4`ð<³Ú†‘~] p-´ou'p6˜Uß(`K#nk:L‹´AF¶?R±K±æÊÅ~˜y¨Iá„æh(ˆBžlP³-!™u …5®F‡!/ A[ÖË …Žg¶|ušýMóÊÒ¨pÁõý¶ö¸šx+Û[ßd’G“Ú/dMóhòY=™r^Æ^VªÕ •W·ê‹Úåy%Žª]+xû„ÚòûÊ•Æ÷Tþû*Yy,ÊkÃÜ‹z¯F[„A¥sŠ6‹‡°ÑÂéÌÿPùO Àöõµ$æµ[*R;6pžÔ0”ZÚÔΣҺ{g:³ÊÊ’‚Q ï„E bï>mmÍ¿5ÜR©…3@z ½Ø Â.#ÿ³ú¤&×a'UOÂvGwóz@¨üHR™máeWÔ zø°É¨V*B«Ê×!õä;ëúúiLX•—©3l­ÔylQì‹-xí¯Y/ ŸíW±•YôŠˆ\Ò €l8c9X C¿„µÿ7ÊÄ¿úE½’PnÀƒè t;Ǻԑë9P¶ïÓW—{†¾ó¸ÅÛŸÐwfÅgmDÊvƒþv4O iZ?`ç;šˆA†Òä]ß @ª¦‘“™ Eþ/Ø1ޏ•&A•C6\Øòc¢Ã¨ïÄ?ƒù !{áG[™Bâ ºÄtiï†1i­õ^3‰— LãÖ{à‡ ÊÄzü(>¹ž›d©ÛE½Âb7tñ êµbâ+@ašÈeÄÂ%c)9žAšôLàZ™Ø‰ÚiÔ’ñ&™«%ÜÔ5Ú2khÿ6ÆÚ„DXªcõVEÁQË0•5@“µÑ œªµ £ÊéS²b‡,€¾½ Ô†[ÆÎÓ(`oá>q÷u, ¹ÈdX"¡ ²— ^ɬ p%Iˆ)„bÃü˜lQšêKø¬‡çf+ ·r·Oéo!‹DFøõ† ìýЫâ¡K Ç¿BîFâd„ñ‰aT[l•,îp¢Ià`ØÎàˆR[¹ PºóÓÕ¸Õ.SJàžò{ˆÒ{”¸0Ä;àÄrú¼2¼ U:>éævÉnŸÍuÃYeø‰\D² œâYމSxg!it½ˆ§ˆÈfñËa Èh†Ä9šé}lfDÆ3m‘¡3nÇ¢¿•sºÔAÐ ­F$Ý¡ÿyæÕšÕ«:¶3µ/‘DÁÆ,tÕV¼\ï˜WÈ#fa{d{1AÊlΣ»ÇIïj›’Ȳ»|¥nô³Ì¼/䋽ƒÂ¡› ë&„cñ‚o}Ê”’f¯‘R7‚—‚–or’PYp÷7б®ô*2c’Ø2 ŸYq,ÑA¶h¢Ïwã‰$¨r‹äk*c³AÛ…;Œ{\%N|ßçÚÖÈ 3õlÌŠ=»R™Êp]?©óe$pËâΉ‚ÜWк[ðJ khÒ­D1ÛÉ¿"EÏÑL €½`5-o­PˆFvƒî›‰$Ж6ÓY©µyˆóomxÙYib.DzÙ(¶u i¼éóœñÇ ´A¢W÷£v;HÄä}MõÑr!¸!Ér¾¿åÉŠD.·—Çä23ÿÆÒê,M¨ÇsÊ€!0Ø5mþnôeðØTífõm­Ñ›ÉRÓÄȾFß–^L$O³€g5Ž-†í%çI‹hZýFa3]5³>™9W1 !JöÞ (˜XgïQÓl4€GÎ&ÒœVjgòˆngàv)”îh”#–¹øÄ¯K²æÊpb9Ì 0T|HÔû¡Ðy0ùø™ û7#4$G&¿ÙRG±§¤+zAí­Ê·µðÝ89.Ë2þE¡ï=É2iT“Á‰ìU“è¶:©Åéóc¿IWíxÎü‘?Ž)s)yå“XÉg=feJ°/céµ/9[ù…Í® ÿ¹Îê’,6γ×ÉA™&Ú7õ-“‡„=-ÓMm—1f[ ã cqÐ0#‹ÙˆwyÑȲ¨bp§&ö¢‚¤‚2ñ‚L¨–X<&¾Z(—Æ«ñ(”Wwôžû¶‡†Þöq¢5“Ìâ7C:Hzy8ïŠìú΄»ÞA9+Õ•ÎíÝá,üzüE‘ÜJòõ"ù!¡öV‚<Ђ¹#P8oè™°¯Nß©dˆŒTJÄOÔzcÔÛ‹Íqq›¼RE/¾û†…rV èø¼Œ£·Am Wÿe¼ö׋ÖŒ-@® A÷žI”d.&ysðù{ŸÐ‰±ISš2ZŒY ðû€ÒÎôÆ‘7Qê)|¶]v“¼ä%+!ÖC‹º Ä9ü¢c4EŽa´Ë¤ÔLÜØ_o÷²“Úû NÑ{[tì±`üÈQI¯¥q%÷ÌÙì?OC%{åóó "Ïãå¼[¿§«‚EQÐk7F½ö.õø[F³ž97ÒEäb ãæXÔ*1Þ #€ºãö쓦g¿uÀVâWþ z×¢’çX?âïÁ/^$ž€¸MáÓÿ8|XŽ‚”Q7V¸ ¤bW&²¡ËO«&*Úo@ïÜ=m:¢Ð¡Õ¸þ(£´>i{Ýw::<ø»ÿù?}!˜œendstream endobj 20 0 obj 3004 endobj 24 0 obj <> stream xœí[[sÛ¸~÷¯Ð[¥î !A€3}I§í^¦™nSwgw6}p,ßZÛRd{›ÌdöGô¼ç€ü(’²${v?ä7‚çûÎôûI$b9‰Š'œÞ½x“M.îŽÊæÉ›¯ja}qôþȈ¤øW6pùôfòÇc;1Ž'vãó£H义ò²3ž˜ÜþšëI‘§“㛣édvüïb‚žäe“›’ÉÆœ4ÏDjŠ9?ÙIóDè,Q{”^=bîãfÄy2xéו¤Lf!ñõv+`ñ»fkjr³íb?º×Lm뿎¿=Šc!íéÿõèø÷‡9ÿO‡›;æü?=úü™øi—çßZì1çï_“¿ÚÏñËt:÷û—dº“}Jú ‹2´›=3„=Š·o7Žü ´ÝxéÁK6®òb·ûÚ ®±ø5l½!ñÄûß_pÙ/u„DÆÐ}=ž/ž$ŒØ_ñ¨0¢–vFl%¸9ôAØÝ÷bûç'ÇF/bF³çÛë:`»§»?éÄK‹¦T(éŒÄõÑ=/àØ?ÐØ9pJâ | &~„+\‘x[ZH¥Bç±ÍÖuB?wmóšp#²ö’â³ÂûóBöDïöLö&mé[Ni«0a7:zwþêíö ~ÊcMxWNéPøh[{éÌK¤FËw$Þ“x§…p4:xø £ƒÃh+ºûþ88°KpUÇŒ)ÆÛï©FÑáHÁÂK/Q)茻3.¼tå¥Ó§×ËsJ£ŸÌ`¾o*æW¯òL1˜RÛtS-[³ ñ‰1ÒáêtÏV‡/½dH‡l6säÃ5›¸e Ñ}ÿF@L ÏÂcRx U;F÷9Ô=;ÿˆÏf¿c³ßJõŸ°k dûßïú¿QJ‹\V_}üc6Wö¼¤*‹‘]­¸e³ø‰ã$/×hw&…Nt’NW3%re[ù¤+;IgÆJ·ÔxO“le÷£SÖ˜ e’ÌðKþ,×ÏÖZÂeÏü^^ÖF;SLDi‰¥Zb#¢D©É¼j-Šg1-(ªʼ<€<)t2=žÍãHÄ&ͦ—þ)X#å]¥*ÏÏíüf67"²Lª›óa–‹(•™ä³¹jØú×^£¬ñ” ´ Ö}U®}ƒé—ÔJ[üa¦E–¤‘vÝ©6n~!Öìªë™IšÝ·ÔÍF²¥I˜(NíÐBFIk;~ä‰÷4©: ©5_‰‰ßøþs{>¶UkgPE#[é’Ö?oræ÷D³¯JÑ©%1WÕן–;þõ³1¯ÿ%zýà¥ÜRgð¡Kþ7”Í_õô‡û·/WÕBƤ‰ b5ªµû†‚Z'\”™Å]$ ­Î¥²¸’šCð/ÅÂqšià¿:£Ð•s8‚-±RGTÕ«3 \ Ÿð,B÷0•§Qž&æXìxD+±”ÐP\í'öqÝï0Ã"0„‚+Eéí§ ¯Bƒ¯ò)â(vˆÁÁÚ ÷vN'P <õ¸Í{Î~¥ DõМ.ɳ0³î÷1µvVÌ$v1DA’a'òû÷WnäŠïä~˜ºWÀ<–è¤;r‚] ûˆ{鶃 9¨ú$»‹¯O|ÿ äZòþ«Ž‘.Gd1Áõ³H¬#ÌfË’=«î)`ÂÀ]ûÐý. ùbÑÃÈOb% ŠBŒFKû5×5+j‘¨Ì±âbæQé#R_€ij…Â+´Ÿ¦gn¦Ô@W¤´7 5™HbÉQÃ,‰REŽrJ 5Þ4`­U©È¢…|&ÆÕß\5õ %°ƒ°­z°ýOZñ3Ìg»ÀmžÎV#@8ì”:H{?èé¨`R‡«%ÔÊðݵ,¡ œ„tíªWmÙw›ªš¤… ™ÂÏi¶ÓYÙ¯aq©éÿ¬¨³<“ñ€ìнégÖ»¤*R új»ÈóÍDH¥¦›Žú!D¼‹ZOo¤ñÕ|·h"É„Uà˜í^€²eƽ¹8vÍ|'Àá S phwAíåÑþxÕ/Oíñ?#ã`Î<*Š@½tAɇ+ãJ‘É]¨ñµu²Õ=N9@v€U{9Šè¬—x®AV7°Ä|l­`Tƒì˜GÓkaOÅQÒäz+ª3*7<¸…eª%Br;op;7Èßë'åý|B>S6?¥ÿ¨¶ãp¢Ê™£Hu×`êÈwD9X@S®9ÜÍ0[ux0vÀ8¿ýšðU]êÁŠï+ÞÉÊO ï°ÇÒú~SÃóÔ奧(·3—N.ûö×¶¥­ªˆ¥¥< h ûħL®´cl€>ðH«ÐÝ ¨ŽNÙ>œB®jåä-Vr&Æœáæ ì±µï®xŸ=2æ “ðö5faô=ŠxƒBM‰.£…’¾ð>¢PCýmšO¤UaÎh~Zþù[ñ9Yý•Lv]ÜvWG‡©m×ë.Ñ— ø+©V|׈šƒo³6•*ÞœUºY@[…|I&†„B]^Œ®¶²H×)8nüà«7qä¿ÀõKÆÖL¥€žšÍ#f â P$E©f°Y7§¯ìJ¨Ö#ðM¦lULô›;oÈ6:Д™URý­ÿF§7ñÃ!8±Ìî%‹ ý’õ—©v7™È«KÀL•#¶''4‰ÝLÛÃ7\Ò¦KÕø+°)¹ySΕ鋤c‡¬÷©Éæ§nxä•ö>S V¯ úsªMf˜8AäÇ‹Ëá‡2mÚâ+úwBHä¥Õ½8æCç³ðéà »Žp¨Ý³c°o„uEèÝöe”©Ýc×Щ;¼‘Ô2‰¼\ÿí§™Pr‚ ‰*ƒ¼ëÒ˜4ñ7—|#P8xÄE^XàÀ—0­º=6tZ{Á÷áTÒqT½yÞ¦{+P_©uÆü`« Rì†ß‹y@1ÁoáÝP£û£Ënù€À(!a“ðM3"µ¾{æŽOˆt`”q‚ìà .çw0åÇîK¤Mwn­oüKýóñÑßíÏÿmûœAendstream endobj 25 0 obj 2676 endobj 29 0 obj <> stream xœí]˖ܶÝÏW´W™Ž= ñ"€œ“…+~&9q&›<Š4’&ÑÖ¥<># Š$ÀnÜbÏ ¥Ä²ì…(ªX(Ô½UD£[ßíúNé]ŸÿŸ.<;ûÙ7~÷øû3º½ûæ³ñâåã³ïÎBgòt£¾~ðl÷ÉeRTj—ì\>:ë»CI¨v!¦¿F·ó}èâ°»|vö§óÝþB{í;oÏ/÷*9®ö¶‹Ö[“åÊ9×¹zäÃý…ëz¯¢=¿*—öéyn°ÊŸ¿`*Öc¯÷ªs>¤«çåæãêQ¹üòL™.ì.¿>»ü)rNËÎ}_¹Qy÷*èœqf8]î~;^úñéø©F~êýbóå^wfˆ½©&\=ýIù,É­R&G‹þÛàê§zŽý®˜*N]ÏžÖqRº_»â;Œ&KÊZ×i° uÐ_µžZ†þµè¿^™õ –÷^ÏsòB½©éWò‡õå¤ÕJ„Š';6šÖñQ2šî:릉¥$~°Èãl>X==3‡dœÞàBrª!xrÈìÉp«+>Íé£2ÑjÎu|îµ9~óÅ¿÷±ë­‡1éŸP0ª[ðh/k5`Éíd̤ËÊ2sä«Õ|šŸú¤S®®ê\»Zá¿ù¸†Ë¬Rä‹H¨2aˆ.&þ¹O+>Ø>grŠ“5!-îìïå‡RTC*™—S…l#®º[Mã¸`òªiì)/T4uÄÏú×sÄjûmTTX.eNíÜÌ:ί-O%.a†0˜óŸóc­²#².¦Ð]p˜±vù·Ô‘ı!Q¶Ósyÿ6ÛRƒwŽªâ|]åûãNƒòRõÉ’õµüºdëãÊØó¶á§ãè@L§ŒJ‚Ù/_¥Æ Cêž&>ó©×ÍÅpÒ®ÄWóÍ?ïGý~z'pi”O!“YùÞÞ¤‚­=ÿx‘jC¯{}þûùf.íC²7èó¯ÙðÐ+BzLÝÖ '®}Fž ¹’j› ]~Ê6ŒîýºüpŸž‘èŒÊå<¥ô„í]v2Ýý ©™Îg*ý8{îû¨Îÿ¸¿°É¨ÂúemvWBùáÞ›oçØÑr‰üËr»a\nÓ›üÂé|ßi›—{¢kœ!‰ÛìÜБç i‡,Öq²¢3 Pª5ôŽ™sG¹¡sVå™~^žœjC’ܤ¾c¾›Ê„O7㘞t¯ŠtqrGê^ÙEοhjUzXT—ÕÃ^Ö+ázŠ\ão‹³«ª:tóÄ‘óÃ+Âò<´om^ª†cë•:—Guà:GÚ¸ÎamªsYŒêTå:Gª Î “Ã2¸¾“¨ Ó¬ ˜=‡böŠ7)d6Ì<•ÒÈzŸv­z|Rnþ:åV?„Ø«òpÐ1ï ¢ ÂV<Ç/Þ—ßηc³¾y÷UAßÛ ³[‘Åç¹7NÙFDÌïp)!z³»¨hÿÿŠÞô*½Í7ÿ3;X%/ÏòõÝ•kü8T]e›ó¡æGÒÆüˆµ‰³ñ#Te~$UÀ¤ǰXg%~Äbš³çPÌÞAñ&ü˜hÊGüøeÊnf&âC&Á¨÷ç_ÍwSƒîBùÔ¥§Ä½Y-7†Ûvо³:¿H/it061º9ÿUú¢ÉWšì°àÁù=,8D<8[¨q¿9öeŽsš¦Î»šéJÙÞÕž7V1ø?÷1…Ë.¹¼zÀßóR^‡¸,™)L0UqÚkƒ¾~Û¢=¡„ÎÑžP›ŠQØVËÇüÌÇÍY?G¦Þ/®½‡ÒتìšþUTKW Êë…­)°¥Úý£N©yÕ³~;¶é>žÃh"¿äd Æ6IÜVU›ÄóÄ¡&qÒÆ$޵‰Ä³‘8Te'U@â¤ǰh$Çbš³çPÌÞAñ&$Þ«ñý4¹_”Œúªº9³‚_l‘è!æ-àu&wK$,PýKN×`]éŠ?(Oœ :U4m¾‡Ü?Yû²à¨”.Z©KNOþj¾»,Z&ÄÐy³a­šwß\}“ÚA$žã>Ú‰Äë*ÛñšÍcìRþjbsûúê·ˆf•7Wª4›ôìA誡kÓs!Ð34ÀôLÚ˜ž±6Ñs#z†ªLϤ èY˜ô–~0Eö‡ôŒÅ4+(fÏ¡˜½ƒâ-è9ñqFz®7Î/¬N8 ÕÆÃ½Övé×kr4åË¢wù³ÏÄxžyÒ÷ÎÂÝÓлmHÛƒK êxRd`щV˜Ÿ±sÛgC«wqZy„-h€¡EÚZX› •ÅZP•¡EªZ¤9,&Vé]¾PÕ¤qž“çXLÞañ&ÐÜ ­ªó©š¯Kò}ZîVèúæà-6 ùã¡ûGß;=¾=™žÎ$¤XýTCôTæÔ«´ñ)ü“/¹o/ñªî2VêÙ§‡{[‹”7q-åiNyl€RžµaÊ Ú9åI R«Rʳj;å¥Ia rÊc1Í ŠÙs(fï x“”w¦•ò¥r”jÒNøI>æ{j€—/áwÌ÷íZéÅÆ@«+\m_¬mÈÞdÇ}¬7—± †U ¨Ð5È@ÅÚÔ ª2PƒTaÒcX¼ T,¦YA1{Åì¯5Š'¬­MoY#H¿(§ùÜÏxÊOa©Ôýè>u d¬­]›Øì€Œ_Í2/g4ÀYæå,ÃÚ”e^È2¨ÊYæ…,&=†%µŒ¥Á?:LƒÅ›0¶öOÓTý»]ù¸õ.pЭ;䚊štÝ×/6ñoØÇ«>&>nÝ¿u÷ͽÊù•½J!ä+4ÀùJÚ8_±6åk£|…ªœ¯¤ òU˜ôW%dƒ±˜fÅì9³wP¼ ”›ÁÐl_ª¦¥úlþórwÙ¿ ÉðàÞ(¶ëh>-Xªî>)¦ÊÖSõ™Æ°¥¸ÒŽÔg“nø¬jÃo‰V·ŠV'£`´:­X›Ðê´BUF«Ð*Lz ‹•ÑŠÅ4+(fÏ¡˜½ƒâMÐÚ›Û µªbK´~ haUŠ`õvQ>V½É¦ŽXýw`Z|«°«ˆ´2"¡F¤•‰µ ‘V@$TeDZ‘¤ǰ‘XL³‚böŠÙ;(Þ‘&6wÔL¾?šýŽÍ6±¿Ñ¹lÊ‹çûsÙ?˜sÙ³uØý8Îe'Î\+cF.cЗ1#—1¬MeÌe ªr3B&=†EYéÈŠ ¦YA1{ÅìoRƆ0óÔûsÙïì¹l}HgïÏeßê\vãÊ™!ð#4ÀüHÚ˜±6ñc#~„ªÌ¤ øQ˜4‡EG§~ÄyVXLžc1y‡Å›ðcZ‹ŒϹìÅ ÿûsÙ?úsÙ‡ýÃ?—­cÕQ5IœF`LjÄY’¸ IœÄ€Ä±*‘8«¶I\šôï‰Ä±˜fÅì9³wP¼ ‰kõþPöûCÙo÷PöéªßýSÙ‰ V¾N#r†˜œI“3Ö&rÎbDÎP•É™T9 “Ãâé›á‚˜fÅì9³wP¼9ëèiçžÊþ"Õ®*ƒíäÏ#„ä‡8ùI'?Ö¦äÏb”üP•“ŸTAò “Ãb«<þIÓ¬ ˜=‡böŠ7IþàæäˆT†ÎÿÓ!RmWje B T+kP­T¨Ê@µP…Ia12P±˜fÅì9³wP|×C¤Ú«nAz/¿Yê0¶?Í_ «~­òzpä4ŽŸ2?7:9 [9Õf5'œ“Ð礑skSN!'¡*ç¤rR˜ômËÏÇ9ÅâMøÝÅnÜ=´§÷6·;qj_8å7ÔæÇ-«/³Îý}û5µµ'ÉY?o:Nl¢è½ê'Yu*]+0È#@ ÒÆ0ÀÚƒ,F0€ª R0&=†EUyÞ f,¦YA1{Åìo‚1ë»ãú5d©Ò, kü6ç]ÕŸŽ, áb*vÃßêøTÃ9j9JF4ÀÈQ2r°6!G ȪŒ% G˜ô–^FÓ¬ ˜=‡böŠ7AŽq älx¼œÓO‘©ZùðKÔËšÓQzĸ·ª‘Ú&Uͤ7Ä.øw÷t:mŒº„Z=oŒž|P]÷«”ÒË” 0¥ô2¥`m¢”^ ¨Ê”Ò ”"LšÃ¢¢H)‚8Ï ‹És,&ï°xJÑæ”rƒsêo‹RÀ§-ï¥üÐ3—äM*Å\Âjœ¡âgÐÌØqkCδ3gpV%Î`Õ6gH“ÃdÎÀbš³çPÌÞAñ&œ‘òCâŒæO°À.dܽqû%n€4r€ÜH¿Ç½å™Ã·ŽÜª˜Ïð=.÷äz¯Â*vƒŒ]h€±dìbmÂn° U»AÀ®0é1,^Æ.Ó¬ ˜=‡böŠ·À®ŠñæØ…å>þVØm¿z߯}ŸRìøÆé)”aœk‚Ù¯‚ÙË`†Ì^3Ö&0{ÌP•Áì0 “Ã2È`Æbš³çPÌÞAñ&`­´µæ½‚ÑáÇï§”â•î=œŽçÖ!“Zv¼7}}#4p³³P7ÞÌSÃ*x¼ÐƒwÁ‹µ ¼ƒ^¨Êàð “Ãâdðb1Í ŠÙs(fï xðúÖf^µ þIëÄ#ü¨ÖÑä³3ÛôÑËÃJgêÐ'Öbîòó'³÷É@Êõ„W ÎÖU'ã*èU‡ÜªÃ ¥ZW€½®*lKÿøXF,ÍãêÕ¡hôãGó›?k}ÅçQñ¶ŠÒ‹ù×*+†<áàþMÎnÌþÑ<îX©=j÷,óP¹gq«´çdÚƒ˜öœL{X›hÏ ´U™öœ@{¤ǰëfÉññAAL³‚böŠÙ;(Þ„ö\9œq;Þ[üLºL†º×Q¼wçÅ·ñÁi÷ *†[ðGëÜTÍ?Ç_¹k«W KÁÕSbkï j µ"½%5¶*Š«šZ«-Qó@ñJócªJÛf> stream xœµ\Isܸ¾÷¯à)éNÒ4öÅ9yœÔLRÙÆV*ÏI^Û’-)É\ò‹ò#4I¼â‘Ý Rãª)Ëðð½`iXËEÃâ¿¡põióì•mÞÝoÕÍ«oûÂ×w›/×Êøß¡—¯>5ß\„\6\µL5o7¬õÞ1¯xã|øÛëÆx}èðiófûínÏ[a¤pۛݞµ†«ÔöóN¶ŒI«³Ú¯»½j=—Lm?Ä¢ö,¯v¶õÊh¿m òÅN´Æ1!B¥l½•ÂoÍ=}!·÷ˆþC ¤½Ïô˜º^‘]»•8çýö6u-è[íÂJ~¼ø}do«?cöXÛ:ÙF\ücÃeþúÃæâo¶]á œ±Û÷»°Be¹, %9 Z-µ·Û;ÞjëBå-´¿Kƒ>¤æÃ¶¼6ŠÛ4^ÔásI?´?@qv)¡ýj¿†s‘Æ3‰»þkç[f„hÒÛDó±ëÈBó=½T\Èâ=Œ7f¹n§Ä°Å8GOׄû÷.0Û¨‚ËÙ5ľªŽ…ÊO»½k ³¼w0ìcj¿I³~ uŠs™ø;vK1N¤EGò0èš\êå[ºÖÄøâ:×}â+ê‰Ö„vÕ‹uxÎ&`\9¡5bÔÛÀЮ­Â4RÏ÷@ò/th¥E8É\Ç¡ò‰ßíÃÅAîyÌr’Ï=D$·ˆÏ—PÙoJñ$ ±6í¹ÙEÆ1&"÷÷àEF¾±’€¯¡¶Û`$ú*oǰ ¬“ Ž/B‹l<2PùŒ¥ŒjaT¿—_Ûþ ¤ŠÓ+ P2@)ØbZw$t®È³¿¤‹ä -¹$Ìß'˜%Ã2¡±’Ȩ$ÏøVI ’Sý Ou†ÈLÁ?l×{IÀ?çÆeÚ Úë-!ñ™nƤV4ižGú€ -%áX"Š.qm&s=ûÌ] JÎÃ!‘©Ü¡‰ ÙÂ&ŒŒef÷™€IU¨t.ÔCÏ÷äP×w»à)ycd©þÇ£ÀlÎÚíx†/@âÚw³'û˜ZïH‚à!x@æôHYW ô%6kæ -xúÎÌ©à¬*JØÖÓF'Àú@M@ºx‚–”fÐ!ƒ‡– 6L»è< .½yê6D%ŠR*.øb×cå=²T·”Pd– á— …Æ?B¤æ·ã‘ýÎHÕ‹¢ÌØO©E82RŽÙ"rP5f‡%cÈ&‡1sc;³ÄZk%)˜ìåGøŽàâ¡\SÀË¥-!’ùÿ$‡, ^ç#Ö>ó †55…ù“Ðp ç¬SÇE$ràÐ!ÙA²5D_§„bïÝz®s/.MBô¯±?Vã1’Â}_ò/63‰8xä¨WSy˜UÌ„À“ÛVä”oòPs ²v–JP1U®à¬r‡ƒ–( ÒÉ)§0¯h":øû8Ò# YIûæW™†ðHÈÿ%yl7Ô®~BPJfìD™*r±ä&"D_!EBu­uØ@æ†ÒÙþšíý5î -4 >ï¨(1|&0B}vG¼ L¢|@)pe¬-wBZÝ1Ûù•Öivà {}d¬3‚eDß%ÖNK¨¡ÀþàchŽáª“Ì·Â<-R)ydPþNÍIÃÚaÒû‰ˆˆ¿§!9åΞ’9dQG^þXá÷ò‘nù)={é-ÏØißziJ%Žè“>>-4ô(š ¼3Q&* u20Ô­•†iz":.LÅ»B¾ {›T{Ï7¤ÚÿúÙÖi“;.C k~B"¾y3/zSIç ¹Ò¹ÌpPIg:ç@Û Ð¶e^ˆgz»L)ä2‚ÏkÒtëQýD%}©L/™Ÿ}¤D¯p@ÁÆ4 Ú Px´[”WŽ‚í ¸Ñ%©^žÚ´3jj‡A%ÔÆOR:˜I™'NN:Aj¨ÔG·AXP2DŒ"æ´Qˆ7Ru¸Ë®ê–áN§v¹wzܳwúŒƒ™?bcî àŽ/ÄI•b5Ü¡àV>î Á*ÜAÓ,ÄP*îLÜDøBÜ¡¥ˆe¸³ëé;K- R;´ÏRTµÓëàÎ’¬Â£å<ÜÍ­ôLÜ)À§NëtÜ) 6¢ýtÜ!JpÇjp§¨™Ž‹Õ<ðE¾Þ"}‡*—ÚÙŒA¿ w@‰¸ópgÎ8˜ù#6ø8áé»*;›‘šÖwµ¸›Ux3¸ëâ ®Zk†—š/!Nƒçm/ LC±ÙÛ\áHê&?Õâ<ðÔ“¼£@! …tGïÊgGýRb ë8N½;úa7”ž“™$œ¯ì³+Ï^IÛG€Ü³V©þ Èö‘ÿ(> «ñL¤ÃFTÿ&už‹xñbŒ-ÂS©¤ ¥\+6•œµR4*‚C ØTÖZÕÝI˜EÁéÒ è/ ØBå…œ5Ò ©rÖ8..rÖøzάd½àMÿtÁ©XwÊ q'Æ®Ç8ã`æ9kfîàÎ.Äyuºw’Üóʸ“d± wr¬¥ªq”ÌRÜÁÁTé;Ž+黂R½¾ãTåR}GFS+ë;ÔNOÇ]6~‘%)ÕÙY¸ãËp§VÃ]Aé€;]ƒ;2½^pŠ(=]pºÔÎf¤é;T)Ç`<†»‹Ê Q•իᎦT…;ý¸Ó§án.7¾‚âlx:~‹K¾B"_—ÓÝúð/vx¦Æ• £¥™ m8´QÉ2òVRaßÿ±Nä'˜jâYdW’<ƒànù•¤¨Ñ d¬¶4E_ÌTï$ «yA5)zÚ 25ZL•ßÞ©ÖÈ{®JY½ñGEÈ^Ñ)«‰oH™¦?Ä‚gôã”súM¨+JDá ¦4Ì -½‚afdc‰d“ô-gæÔdˆ#©j„• —Ò¯¥g4‹¤ø]Ç&nB?gWÈ0eIíEϪÒ㜪¬»†&=ßãùã“=_U¡k2w5éšlQþ/ô@è'kx¾ô]LÕ³òKgwÉa~_!üùórøh~ú©Õ¿EÉV ÉÏJ\Gh¿„/°ÑöÈËšW;<(ºÆ2µæ»« r‚>ã.û½Š½‹<ä¾ÒÇA]—ú8ä—î #î2Åu†Þ_ÄI£ó@‰¾'{}ŽëŽõ#‡ü.þÂà 3~Ž›\&°0Ðù‚ÓÝøt? “>ž¥£iÖë™fÙ2äÄ™ÏL³l¥¯¾üd×…ÉÐ¥7×b¡iF7×rÆ4Ÿ""(Žý ˆ/7˜‹’ø š~üOæd¦¾’FP7šV¤Wæ#Q€YéñŸQûP¢?ž”vBB;±¼ Ÿ>¿„ßd9~e‹~{ ”`ÁBGgÏq££×Cÿ¢Á/ÓE.öH©æìÛµ®t¥þMÐ*í…2-ç•fÎýUÉ´ZföE©îb É4ùÈbék”šÐ¾ TÁ‘‘š¶ê+„ög]pÌçûrD©¿§Ï÷¡ZêëXp¾Buiº/Õ³“uÏ‘ü,irx–BûÄå,¥²¬Š¿>tŒY/MàLô5<e ¼m™6µ®ˆ"‹  /uEÔ¸²^mJØ WäpvßoþÖú5 endstream endobj 35 0 obj 3012 endobj 41 0 obj <> stream xœÍ[ݲ5¾ß§˜+Ü6ä¯óÃU –ba)°wàu8µŽVY>‘igÏΤ³Û³3“ÔÃM6™éI:_wÝ ï)”ndú×6.Þ¬î>õÍÕÇÕ¾»yúõ¡ñájõ~„IûÚ¾xÓ<Üá‹J5(g÷z%EŒAÆý jBÄŸ/ƒˆ®Ù½Y­›Íî—•²ÂãÏÇ«ÝçÏ×_l¤pZEë›­:hë×7[Ò«hñ•­` úõ‹õF ã‚Õë‹Í?Î*¿þu£ø€Ï^æ×^ææ‡ý[QšN–qíÇTp´—в"Z|ý:_çæ‹M'õ^î->¯vj~Ü}‹š2þ )%£p6ij÷jµþ;©…èQ)჆›ÑçÃÓ¾E{½°Áø°6Yþ¦)½Æ&€K“ùj·z²ò6í‘kÀ;œ™ÁÍré7ná‡ËÕëú­×€³h\ÄYå}ß*ç…×ÍVnV¦òmnÊN÷2w'èh—[ˆbÞ—TÌ8Q§êÆÙIY:©#IIÅ…¤ó“:ˆ2ˆâ›I%l3¢`3íªÎ…d;øû³Ò’ˆù|™ŠØoIÄP^±M²œa´§½×ݼcß"˜½Ó=JÆÉ·ÞuJ"ão»ÎwY3׬ÑQ·ðS÷YâÛ¬ð«Ná—' wZÍi[cÇißËãäÑà ä]K' ßØ£ÔþÈØOÄv½·7J‹¨¤-'ëµ>5k« Ñ2ú…ÌÚX…^«q  ‡¾ED`$YÀ²õLËÎöè³l3ײ¡šg¦ºq›Ç%×IÞ×Ýx!©·ÄôJr-¹X_Ò¹(% MªítƒËKE• ˆ:¼KnΞ]óAh0g¸mEu¸cƳìIó‡ÜäÝâGâI­¸æ}öçŽA\u°xËbñ·Ü›iÇ﹓JÀ6ÕC<´2˜±¾¨]­äiƒtß4q)Ú`B o.‘#Êl"°ßf7Ò¿(Ö?T1E›g˜ Ò=1ºˆÆÜæ©GïM[>,ÿ™ðc£Ç?Úý")D‰áƒÄe²Ä;_­ŠøG¹%¸ t”vÚð[<ÀPÕ²‰‡ a 'HDM0Ü%­Â”FÆõr3Ëì;t*V!©§ÌðnFÍ÷›­A|˜õÓ ‚¤>?C2ÓÝ÷8{iÔäÝgAÉ’Åë§0èÝŒô“'0Ú„nxYr¶N¦0É¢ÖÎÇ"‚¬Ûó-ÀŒ@ÖkÔS¬uŠš…ß³:NÁ¬îÆ5èH+ÉôH5©’A é*$uMWAºxI«KºÈøÞO€Ìþ¸štå' ;ÓaÒ5 w†mÎô•0wYÒ)…€»DöÍ<²ì¨ ÜHÚ“}¨ÁáîŒU¸w§¶:ì“ð÷Mn>û$ño€)Œ2gˆPO…o˜ŸMŠÖ ŽðÔEÅ} „ìñ!*:;%*\U£`¹¨èMŠŠÖ•Õå­ 5?©ÁÇE»˜²5þ©(‹Ì+3BöOšó?UþÉpãUþÉdÿt:©Îô“ÞGÇ©ÐÞêÏrªNtRW$i>B(+ŒÝ/¹d?W.Y°/ñ•׋MD iT˾VdY–:íè9×À||»è´kÃ¥ÎÖÊÙ61>mã_缿w„J;Ò"Ãw2æíRFcŒh¬vEABÄâ!Upïb¼&Ë{˜ÚwQ ŒþÇΔú¹ˆWuøÕ°½lñ>Ñ«Œµä'­MB•QBN«¶Ø źƒR}_~»—ÃÅ"Oq¬4˜â-Võˆ>õš $K/@¹Ê`IÆO“ÈiÁ2÷¥~cɼ=Ž õdž-HW%‘d*5dþDR}å¾5ž ŒM"]óÂI$¡F¾wÀ«ܱ’êpGHp»UEÒ€ÃUI#¸;³±ý1ë=qaœ¶6U`§Ö¾IxãïFÜîò)~/H´)*Qf>…¨Ã·Ò£¤¹Àg[ÜÞï^ºÏ½Š×¹°¡1”¢Ê¤^ì@—f‘…ò"‡µ]ԘǪ|Ùñª_Ö|ë|‰ùž cèÉ\¦Ð;ššd.FŽÿè’ˆÖ×=ù/Ѿ2ß:Ëû¬²Ã¼Ï§åM&~=d® ¨·X†§¸=ö¼7%.šò¾ÃG§±Íœ}-G65>×hÃRI“ƒ„fà蚈w¨ÈÚS6Ë6g}¦23-ÿ_ppCùWaSœ3«1ª;¬ùö]ŸleÍ©'X¨IæúîN)g„‚iF=&C»ÅY’¡¦ÌØ÷D£Eu˜àâ§ÊµD%`„^Ìl½Ú›­5e±#h¡cõAcß½ÍÑÆö¿"ޤÐÞGÙ[³©ãà‡gsGåœL—y+9|X'”ݵ°A8iç§~s¹ãÌË‚ÀIšÍÏ]c-‡£M¤„‘D)röˆ… c¼Ç8lr^€‹Óï€ô]Á`±á“1æâ7W°Âºé—•êhW¯V鲊B©‹Š¥+÷ âÑ€®uàn®Ÿɇy¿NRŸ3Dzô¸§g“.ÖÑ+˜­yö5 JvòMžeüWìøzyåûO%F‚@Êí±•ù‰ªÏ{ªâ‰[ì”›OH ;_Yš{ÊÑæ¬¶Ë¥Ä¹÷ <×9³„]ܲÿT%lR·ž{Ês¸ªÂçÈ )ÀLÀÝ”ÿ0´ÅÌnW•°Ýí‚U÷ ¦˜ÕØ{Pþxz‹áŽ?/™[E9‡–Ì öQáÉêÜ·…>endstream endobj 42 0 obj 2056 endobj 46 0 obj <> stream xœÕ[[¯Ô6~ß_‘§6iYcïUTU­Z`ß - —ri«Jü¤þÈŽ³›xrvödãPáÅëËxbŸ=Ÿ7 ™þ…Ç/7×ïûæÙ»M_ÝÜÿîPxûlóf„Nÿú Z~ü²¹½ÃJ5(g÷t#EŒAƾQ5!âÏh/ƒˆ®Ù½Ü´M·û}£Œðøóîf÷ÕÃöÇn«…‰ c{¿“Šʶwrñz·aµ¾½Ûm­^EÓBíƒó‡a`Á·7}µÃ™ÆâÜ¡éÀ‡Ÿw? ÒÚ”ö û9‡*ïžlÚ“†ä“”Q‚ß·><)v¬ýÐm%~ÇXí… Ú‡öV·5ê۪Έhð#Úk¬¶¤+§­5£kû¨í@h×ëb…×NÚ¡ÙÙÐ~±ŸUËpЬEUûõ–ZO‹é‚ÖäJÉuädª]×mRIÕ#šŒíß`sˆ )‘æ0™ñ™VíÛÝæÞÆ›„B×Xï|ZºÆ¥ßÒ·›§åàFÄ‹¨l†ö¼`›­Ò"ì÷@eК\”# H»c»fMD ãe®´TÔÐ>‘´‡CH[ŸVµt!1YÎËÛ²„„­e¼\ÉP– tê$ÕþG¬Ô'X9Ǻþ\rÖšS¼cXO;£lÊØZ´3¸ ³jr£lÁzÔNòÎÓö+Ùr‚,”N‡ÉîL€‹ Yr0à É)îà>¡ÔPIN„‰¤³”Òˆ3É]é„犿ò‡J`/;¶O$ífl?’Ô«Äm¬m2§“"Í}]B'®ˆpf<®É {**þhõÇ#c;ÈZp0‘-&]õJܱ¸*Â]îi×â.¯¼_‰;àÚã.Krütg €\åJàIÇ´ºî€íZ„;¸Œ–bÜeIn-î ît5Üéz¸ÓwúSàN×î†;]wzî&`c®·óqw$©Ç.ÁË€y3âjÜi¢31(ªâNÓʱèKpGDe\Ùw¬¤2ûNs÷l‘}§)ØVÙwG’zÜ“aÜØÓ>×­ì]‘XÅ­9Ÿë—¼@OÆU{2³U¤’¸Q¯9¨@Á¬›5TPɉ1q±›5¬LàòuïÀKiN`èZÞD²ŸwV÷ظGS0‹âIìë¼ãuaXºÈ•o;…K\lŸv[,Zk}û"ù²A"žÉð¿;päS°è FµÂ p'‹ª¨IÜA+䢸ïVBɱÃ:ƒnå±s¤iù±“yÏÜœg„U™IXeÅ¶ÏÆO>%ÇÍC)Ç÷b¯æ8¡pDZŸ‘gÇ6»ažâ‡ÀcÃuˆ¨`´Õâù`R<߸˜ÐHn‘ñ±4p¤ê1\Uc¸ªÇpµ’áßg†“p*ÏðÕ|•Õ"¬%qÓc}L«ÏsrZaÏÆ¿àöD˜¡çaV›Ú_Œj_°óg]ÉnäÊüÕ{íÒ¢<ÏËþžDõÇ À –ñ“s(E@í—¥^öb™ÂÍ1qp“O!p1amm@¦Hnvk@S Í/ìäþ6Z(i ïïIû:v;®}‘H®a—§œÇu¥98žIÓYÓ,úYàKª>gÉwÆE:ÖRF RŸ²ãO]´—îdéøKL^Ary’iVž‘KãrسL©|"™¦‘˜òˆêJ™’ˆ¼Jí4‡-­0xÞy¤]­d…ᮯ¢ N–´:hýÚ5ÁCD4)‰áË›H*ŠáLDÕŠá䨡e5­,!q¾¢ {¯Mҹ˕å¸s\¥(hí¸í.žËA·xn­Î‘tœ¥«´ö,„Špç«wþrÏrÜyŽÇE¸óÜnáÎgÜ­ ò^¹0 ¾=šV t²ÄÊ™{¼t˜+u%Å Ó¸½9ºÉ “᜹N¡•V|-sEã§´‚lŠÅ‘ÇÆ«Üz#~-}s;ã¢/3â—xÃ36|©ÞeŒÅñÃRðOã¨_½Ääöu^‚]àcœv;éáº$Ô.öpçLüõÏh™¼Wé{¾âƒ ¼|VWóÌ#ÒÁ4à5a´–&À<µiä³ßÑο $OÖgÝrʩף äaÿŒ!U¢ÌgòÖóAÁósXöQqíÓ×­zNËñ –lR¾ÂsÚ9úá²E%Í2ú©(Ó{Z­ª½§µ)ì Zö %j×»bkcžk/Ë!}~K¤›NYzô¯GêÙz³¯6ö”µ€«¦kåc@cg”‚0[/wØÜ –ÅX0íJcoöÍ8ù‹y§Iñ§\ü¤øDÖãÚÌáNŸP+€ö¼C'¡@.g€ž-»jïÈS*P7Mù+#“ç¹þ¥QÑÉȦñÊNF‚ºu³Ùõ©i2N|¥m‚{Fÿ¬³N^Ž:•gãú¾#YæAêÁ$1jÒ“èšMš?s%ÕõØÓX–=W7À.Kð_JÑå Ÿ¿g»rº<Ù¥òK3ù<Ý‚A Ob¡^–.&_@I=ÍÁ[)´ ‹Î|ÍžÙk)§—S®_³{›ÿuĵbendstream endobj 47 0 obj 2011 endobj 51 0 obj <> stream xœíZË®Û6Ýû+´j¥4f8$‡î’´Rh“zS$]äqó(zóF‹ù‰ö;ú‘Ê–8²iË¢„ä.z³MšcræpÎÌï*)@U2þëO/W7¸êŇUÛ]=¸³k¼±z·òBÇ¿¶ƒ·Ÿ^V·64 "9›ç+)Bð2´ƒPù@VNújs¹zXßn¤° `ýS³6Bye\ýKƒÂik]]5k%Pcpõ£ºQB[oTý´Y“\´\ý¦Y£‚©ß¦æ_MÒ*§z ÚÖØ´WÍÚ c$„úEcD04­~;‹ÐRh¥kÐ(,„ŠÂo×I7&5e¯;:Uj²YUã„ñÚù¡¨n¾LQݸâ’zuA?>¾¨½N9Ç™‹Ò„­¨Ds Ê¢ß-ŠÓÙ÷±ŸW©‰ýø@P?núñ1I–¯)ª\!%õÍ#¢ºN7º»ˆNÒˆ°¦Ç²L“tN<jß#5£¨u'« wyáNçäáNï³wIŸxîŽÚXs¸ÌÞ¶i=¹ÁC¸x½3,™Ó’=ïEÿòõ Œp6~Ú\{XßMž‘ÁÍDy xÕ€@çIAÌiLj½H½ï{OÎyãeúÂã$V¸È"€9êQ®¸Ùò™S>OJ v2U<ÉÑÝ¡ ¢=8óýÙ©Ùȸïþ o³€Ú雎În·Zú¸[¢–C^2€8–â¥à#f,Òþ-£&%i«®”šãfíÑVv®·07Q©`±4ïÎãõÇf­… º~Àã†ÞVÌloøº:×Ålý†ã½™ÅèË^ÓøïýÉù£G ƒø*xVÄAÙ„ éq²#î¶çÅJäŸâÂL°ˆ†w?IÈ=8qü3ŸúýŠ ºTt¦ Dð‘ 18¨„2ª=vÖ)˜ ™Åˆ2I:d·‰Di&„0§‰’-æ%&¢ æ–?3BÃìž÷y|f€†©éмï ,ÈøFŸÆ¤C?=ðh=ö¾¡q¤4GÉ%ûÂ'òG¨`è2Ïdm抹¿¢såo³Gn·›¸˜ÌnH˧v­~Ä·¤Ì2ÃÅ5±C…ôrçV Ô|†Ÿ›¤q{•К¢IÈÚG”ŽþòÄgÅÝ10Eú)+cÐ2#ÿ’ÚÇ’A ÅDi®Ž9¬ÎØ_e½Xv‹.Û<æÐ>?² ˜ ²N„`š\¹)ktScý0‰n*=Ÿ Ë‘+E…¬zr® öû•)î‚–æ(Ê0ª:ÒÔR(sBr‹V Aæbu¶Ð}1ÍåBœÉ…»üWé°Ë€Û2«øÜH¼›3_QEžÎ/Ùyz™åv6aÏ»ÂAõ¾WÓõ~üüÔ@oô¹«ÝóÅW$Ö@É?xKeôJEÞÄ3Á²aÕ¢¨ÐÛ¹žx ´o%«oš5m‡Z±·‹èŠí”P8ÑÕŸv&iàºÓáõ’’P!®ÖŽ–„ºLújŒ!ÚâBg@ƒ ‰Úð0WÅ2‘/®±#0·d»°³ûåõ ›+sLœÑ£¥Ë›³êALÒ HUPr}çáåZi=ˆ­Iª©;—ÅPð\XEÀK’f"“eì¨eÆlœ1÷’bt”¿ƒRí•÷nìNÇF_žD2÷p_¢r²ŸÐŒ ÐÚ¥çe1Êã^zë=-d~L•ž@üÑ3 :š¯~Åî’;„þÝ_é-Rع¢wkD)Ê&üRõg 6KÊèáÝšrÀ”ÆRŽ—¤÷Ò¬ïÒ‰È8¬aµ&]ë² bVyÖ/vô £äƒo¡:5¬Ï?qkÉ^µ–±5„Ö½„†©#H+ÀN*âåŸÕW²ò3õ•ñðs™ÀíŒðSø"O„/ç@™¥¸SÞö0nM$ÊPýÉ"¨›àn‡íñÈÑK:#€uV,Lç¢WÇe|j¥sÈ:_'|£„c§)›Ïf¸Î‚9-¥ît¼éá©ÒÒÅÀÁ-8øX± {QÃ4×™?NEÙ@V~æ¢9§”ñ]Ôø Ô³5|Yy˜Ñž‘ $ÍJCó’ŠÒPÌuŽoïtŠlÍ{ËkPŒ?KØÂñØCß“º“Šwf_N¥w†Ü+å_Ÿ¢w¿Pö}ìiaòM‰ep¿F~\8c1Mông`íµ$™ÑkèÅÂ8…m͹áÕI”?é¡`þ]Ô\j‡Å¨–£v˜Ní­!ï¯þã×õŠendstream endobj 52 0 obj 1783 endobj 56 0 obj <> stream xœåZ˲5Ýû+fž+R·^“EªPT(*Á»„E7—WòXR|IkìÑÃî±=3º•"8‹èJ£VK:}º[ÒÛF  ÿ†ÂóW«;\sý~ÕW7¾ÝÞ]¯Þ®¼Àðë+òòóWÍý-uTª!9Û—+)ºÎË®oTïèÏÎ4NúfûjõxýU+…Õ)³þ¡UB+…ÝúËv£xÐn}§Ý€0h:7Ô*o×_÷½À€Û è‹w÷ߢ]7©˜ukZpþçí÷¤º½~…ñÒvÛ«õ?íö·\{¥D'ÁíZçbÿnAøNJ]Û¿ˆíMë„öè<éêÂøßlWWN‡Å°ñ%uרð7­Õ»«ÕËùk Êï‡%¦áiJ–6V5…Âï&£ÚÒ©N¯u*ÊV‹NS1¯Ì>ÅTL+D ý%+_Åv—KŠë¦¸ñY¥0V®R:(%]¤*·å5 ÓËDYã÷J…vÚ%W™õ·±½Ûul?!Éz¶Ë• kÆ”’¥LªT¦àIK"¬Ž¨—©“‰_fû#¡©$mQw;<*MµÍöÁj{ëqnî¶'«m 5Ù^Ð@1ü]1mÖ*Gzi¯jÖ:á ±€Bç˜užØµ‡¬ŽYS³f!f·´ž´yër ò!Ôí†V¡Ãõ£óÔ…Øq ÀøÉÐÙDÀ&ê¼ÍRkaÇã#Òzº¼ÍýÕn$-£t”ag9¦ .¹Qèm-Ð+(4TBLk­°8‰iy¨ÎBm!jhG–œÎ„q¬$;+Œ3)Œƒ‡$—˜RÍdäNšÁ«»4mQ{ L )¦d-¦¢jo'FÏÈ=û€ÓÕt;¼8üáÅÆÚ½‰ZcŠêXiÆ,T“Pž¨ªŽ…¢¼z‘çY@n½›äW2cÀzŠÕ,ëY(.´Ðï’fÆúÓ¹TáW²fã<éò:©•ÅÿW©ö]tLÏÐ:c5éýKú ¥™¬ëX™‰âýY.ö¨×Uð”Óeß_+ÍöyI1µmòYñãÿÞvø­ûUÚ—«´riëûLé~1½¤Òuüô5›W>Kµ©Ó‡Ô¾[ ×wY^ÉÜ.Ç+@¶gÌ+…7çôþ‹–È9ÊìáäÔŠí÷zêT`³RÖìÓH4m"™c†¢0œ€óaJñ,uK7!™€âùȰ]—ç?§ué&§?#XGI \_ë5$éÞ(ã.Ò²uœô’O]ä¨[N> œ:+IÏ‚ºMPg_fŒuæ=dfjŠk/$Åvî=䑤þ=dqksé{ÈB£þÈôN¿‡LQo6üñšx9 wPwP wPwP wX wXwx¸ÃÃÊÎÂãäÏÂ+iî\5¾Ëâ…|w$i>î Qã¸#ˆ\»¤» -ÄÝR¾+DÕÂÝR¾Ëp·ï²-^ÈwG’ªà®ß%àÍå»]ÜׇšWÿιj@endstream endobj 57 0 obj 1765 endobj 61 0 obj <> stream xœÝZM5½Ï¯èL‡¯Ë_e£\²!P$”en I$¤•H‚„øIüHÊ3Óm{×Ó¶7»a÷â±»«Ëv½çW¶ßwœè¸ÿ ¯o6—ר½û¸9Tw×ß ÞmÞo,“þïP—_ßtW{z #;û·Μ³Ü¡³Ž~:Ý!·Ýþfórû¬ß)&¬P¸ýN3ŽàT\{ÙïÓR;Üþt¬k¶ßôÀ™Q€Û«PùõéQi¶](> t½@ûóþòOâÉ?c™u`È»ýo›í¿ýþ÷Ø{æ¸ÀcëËÔ¬f(Qظò«^5ÎU\õë"ö™²-õ…º"„˜uPs&шÕB¯µñ¶¾Ýo^lPù‘7¶VЫ¨:ãÓÄ|x³y[>¡BQí0Ÿô}roZ2@Ùí@2{t  ¨Pä½bNQqkâʱ ]¯ÄÔð~ô’™±ŸŒÑОu s•'Ÿ8ŠÔÒ´S'S’"öhÊ*18›²šŠ~ÖŽC÷Üé—Pìþ×þI™ûdÊ é–@f ³«1v‹ ƒ@Ï™ÈDf³˜ÉLñ§…LôÙ“Ù=ZRÐKÑ=֣ǚ“È1°ª@²€°@&†r€L$ï˜2Úžœòí*´gQ¹bÆöÄÒØ®Æö KÆRDbì”s¡uj)ãæ*ç»ç#”†„ØaŒg^ÂÈç¨ò¶Q+Oœ³L­ˆ;Œ§¸Ž¸1-²$î²–2l»Ž¸En¶f‰ÛûqGãsä2p —w^ì #<§u3 ÏÓ"R¨ñvNë<é-(ÌÐCÁNÙ|,Îй¡Uà–Ð9M'µ¦œÍi%id+Î&q£MG†XBÙÞË“æ1ë9*±“˜j%z ’³#ì@Ž4˰fè©ÂRH­—jЬÕiìD¾^Œï/G‰LPnÕ(O ©ôJ‡c3¥cÄ6ÈdªtA©Ð‰­:‹ÿ"¡#Û ÙLèDïW ÙNèÀŠî-:WޗЉ‚­Vè„h©:òv0–“µÌÍÖ ²þ>pqDÖ×SB'û$Y/ÞÕñlƒØ(E}:ÒõÓqÛ8m, nzÅ8ÛŠ¸…òrL3âFæD§É‘ð6©;!JåNÒ^ÇÜ&×^¦,r³CýVVÎÅuvðŸ~Ç™rHqõ«-…•4ŽËê|զд¶•LD×Êœ|,Jò´n\«X :Û)c™KD„°LSêR¦"¢v¨ŒÅЮ+U„+¡ˆÍ#¡s*¡HEè¸X¬"È¥ÈfÒ½¥""z?È=Û»iaòåüX¢"¢)Æì£Eq‡Sq÷pxâ5?¤e{›%,êîòZÄ¡³tºê³ÃtÔp°‚L¦GÙ•Ê·t~6W ÷1Ô³$¬h™­  £ò‚@`É™ÕÌ+Õ ‚Úý•³ÿ¹‚ ç>ûÙƒ¨üÀ ‘û=Á<ªÀP»¯¡r©b‘" :»z_#¬´µûY!P´¯aÎìk¤†5†Pø"‹éìnÎLŸ§šUÙË:®ð£=™¼øéœã eÈÒº-Ðó\!H©W¼q…ÆU')YI2iG¡ÁMƒ…³VEš¬X8£ ž}¿Î(pû<غ+ŸÁ¡Ëçl¨›ålÎcìÖ9´uT!ê×Ú„M7Kذ]†Í¶(·ªLØðq&lA¢BvþRøòkxöïÞQ·õåÏðÖ›ÐþÑpúpùý¯ñü/á?²ßÍ“Jíq­|bâJÙª³ËÃ}ªÙ¥ þÞ4åpÉE-òJ¹Uë^~«¾H.b °:¹x"xïƒ Ò¨ÆÕë.EeŽ®?å…‡@†§œ2Âe-ŸzÕ*Bú:_|Š:³:M^sŠcl¶JJÊ> stream xœÝ\ËrܸÝë+z•tO,š \ŽóšÉ£Rã(•…g²$Ë“±$[j3UùŠ|G>2¸ä!Ù­n—'‘†Hâ{î¹PVy¦ŠUþõ‹›“ç/íêú᤹¼zùû®q}òáÄe:ü4°}q³zqæ;*µòrÎÞœäY]»¼nnª•«ý¯µYÙÜ­ÎnN^­¿Üœ–YáŠÒ®W›ÂºïÎþà{kÛõV*+ å|߳˓õ6gÿ@Ùþn¶½ûÊ 8-2£Mm×ß®7E¦+Wý”«âºZÿÊßwuž—x±Øœš,·ª.×ßnu®ñmêÌJ'¢^K¯Ÿüý¼*l±ÞÊý+¹ÿ°9õëaªRÙõÇʳ¦u.¼—&¼¡›MÌÜlTQ‘Ùhÿ†n6Ö­Ÿµ÷+ãúGCó×??¯f9È.—¹BOï‚u…ìB«6ÆTAÖoÏN¾9±eØüjeœ+Vªªíª ¿{ݸ¿:yót*jí×±W)ÿ~?¼Ó"/³\¯N•Î\;.%K[J3ß”Y]úæÚÒûnˆÍJ§­KEõýs¹hÙýj¼µÎøfXŸv” ƒÿå‰*Ãtüog_üÌð¡þ¯ð¡~®ø0:ÿø¨*“Ùê@D "–÷—UÅ‹ls¯i\ç4¢ð¡G¢Ú­ëï—r?­\¬°ÙßO$Åûe¼?#©r…Å«(§'lIØóÏbKzUø…¤bHµ"Ç]_4@Þ€”µõ?0‚cY °ÜïEG`»5ðïê–@çî`«b‹ÌÚ½¶cƨ(/J9UɨèÜf•[U…ÎJ´)Êe¹·\Á¨TY•ò@«R1«±H¼Ÿ,_n¼Ö¶Hæ2©À‹ ×,LQO`L/`l? õ²v€ÇMmM5Ëáƒúid³}7iBe qåŒ:„Tb-ãT¦^.ü8JóDb6ÔZ> B–Ýÿüzjl×Oæ ¯Ÿ™-+Ó©Oa »>«G›WåhŽþ'Áx=ÃI69NÎdÆ3È‘àTZÖE~,8~«VÞÊ’8Pç™+ÕS Ðth$hž þLÑô¿„œ]bÁ#‚¤ò^[­Žåñ¡¼·y´XÐfve]e&]¦”=œs ÍÑ‚A{¼`Ð-L¢¹ƒ‚A; KEH°gKæ`×oe†ßoTf¬¬Ù¼ph.Zˆ*…½÷jaÙöËäUþ]µSë7ò¤ozÈx_q/BшTè%Þżٜº¬Ê­5Ðç…p(¼é]œ¿´þíÑí[eí¯£–U»¥¡àìÑmܸ+ª7ð1¢e„n5Fí cú)¯z½k×½Öí].c-UØ– .…76…®z£t)ݯp.±¹•÷å÷Ûbl»BÍ«®â\î‚F‚Á~úÉò Õ&<ИmUWe½þ³_©”®1Ôx.£‚«`ìÁ{&+ØP@–çEXÈÓ~%ƒ­‹v9{îR 9[Á Zú!ªö#âÉkyYæþêQ[Ιjƒ¤[i&xeì Ï^ÄÜÉEŠmБë8j#›»Yz(ì½XbNλ+hðó¨/‡ Õ(”àUµÐsžwëyhòA:ÈìïW‹ÒYººš±Ën«Û° Õ áL£–f»ºfšv öp=À߀G…ðŸQížpˆëN xýÏ"ìáEŒ„‚n´B qÎÍ?X³Jìꥪ»¸%úËæT{?÷M°ð(ÐÆÛbüJÌ®jS»à¾÷Ù óèR Æ+ÆuĤ…€ƒÙaÞñð¬0skµCPÕG­;–`¥›=€¥£¨Ú{#b ÁâsL>Æ€è7ÀA,Çã(þÀÈ{:J@!@œyp½ÞFÈÀ"0¡à+ࣥî• âXÔ¡ˆ Ô¸¨¾“µ#!(tÇ-{ 50¡£(þ\8`K)ä_»s~ß¡Áø`”F÷R(0Îw$ª¿‰Ç.™‚—¾û50©(s;@´ïÇtqž‹ºt«^¿¤OfÃÜ|L§ýÝß±™3UÎÊUlÝÆ¨ù×Q’ØÆ\s–À×þÛ¨ñGH%i3ñÜx®»h?²¬ /€¹ÜŽ&ÐÚÛ^ÞŒHh4¿…'{xHÔq?ï,~Íøf©¯eL¨¦ôøü‰²¿¥Yr;ÅNpgp)¼š7^€yYÜå!ÇækôŸd£qîdiQÅiºÈ]Þ5‰ŽºÅ‡èèSE‰ DS/x <ÍsuϦ9l‚PÅTJmH kñÑÜßß2»qEq?ŽÀ[k›h·˜a¸R¼·Šîçrhêšbh¬: h&;HDàñ¡Pùç¨Æmþ-v\è°ïÔ$ÔTþ eCI]«‰•xBy/rˆjÅà>6‹ZƪE€s•†Ätº»[=À(.¤«!ß ØÂJŠ` !,? ¬sŽ!?_ààªÏûaR¢­@ÝËám¡e!Y2ª* '{'»·–íñDd±”½ž»eZ 9ò›½B:Œûˆü/»J“W]’LDœÊ÷ XɼëeŠÜ/L{/Ì5‘šãÖ„-ÚÃH«Â˜ïÈð–ÐL- ÏlîhW¡¼4é.Òä°ôÔ.-`Q*OwCõ ¯ú/Q?埭}¯kÆL#Qa|À’sHý> é—ìUÒŸwçÖgÊžGÚRܾ¯‰ÀØð){}Îú-¡6ÍvDF\Tü‹•ËœÖ=SWŒðÜçЯ֔'ß—‚ŸDs{-‘¼áct½^²oãª÷€·kÞôBÂJÅb{Ònͤ@wc}­->°›ÓÖ¯rôR!KY“=°¹E‹@îÿU¸WêÃ]QC…^¯¤ i›ïâ;<„úq’?©ôJ•Y^ÂY§rð±aQ©Ì•ý÷†EV¹¼è\©Ê–ek »öíFûýÒEÄûÖÇpš²ð_ù­õ3Íy¸+èuŸ½Ž-¼$WùU¸¢/Û6šä­]ÇF•²f9ÿ9eQ–M²¤?Ï58/8q¼gñüì,8câuñØf¯ìD̰\°S „ÌÇÆ<={òq ÂI‚kpºbâ2S²b¡÷È·hÇ’~b†TìëòH††v”Ikþ=jŠ’$%M2þìeа¼8þˆQ<&¹nÓÝ\}2Pš¦ZLT$&ÂÁê l7œ@½im¦êcADêÐáøôPƳ$‘I¹ï‘`ùMa7~—š¤Ø;8P”Ræ(Ñ3ˆÜ%+õH÷r¢j)zù–ª˜ä²GQQùFIA®®[ˆÙ:+jM èŽ^Öƒš f$xaZhEü# +]'G ÃQ4íêt|1Ÿß-wy@%0áš,Ñ92·Ù4¹øþQ~$ŽÆ0· )Ð)cHƒ¦|ù5 {ƒx÷T°£|Ü›uŒ{ÛTmŒ{çÍÞRj(ÉŒ ’`3Á.qÈ/%I4¾G×D¾¬L壙xP0vêæ`.4Mz§Þšèî5|’[œÂíCê0Í–[عò;”èSâfÌ‚~O»ÔXÛ’w·38èt(s•wdØqï%¼Þ2æ¥ Y\ôÓ1ŠLšU¸g ä¸Ç¹!=[CêVðójT:…SRÇÝ_Iýz).‡ò4„Èf¡€hÀÜiä8 ó8gPdjSN%·„çbäßÊ· güQ×O¸>ã ­õ©z5Hb•1  ˜ò¹DðÛµø)Qóîû «3S= –¾ú*ˆGs"Ç„`Û®`8CNä% NåcSŽ[ˆ¤ÆF`Ô­ Àèo‚˜×ë¡’Åò–e»ƒ¡Ìè Õ¼gú J<ІF>Ï\_Rh-‡MÌãd¾|ôi£žðдžº2[Î2{•I‰ñ~dº(qý„Z½i‘×bÈeN$VÚ£PLà%Ïwr¤ DÛOà53 ãZ±æ>&›àÚÀbüYE·¢‡†È(¥,~×!¬1Åú]’´$jäe\ ”K¦åȉz˜£ÅòД=˜ölur8öý˜a–/xÏ:Q´•Ö;(M€²ðÀþ¦K VÉÙ%zÚa9NÄùw1LZÀ0Rñ™çÝÆ†ƒü]€ñÙ-x:&ù†£×KØÞ‘™•=g)ÃvAùùˆŸâ×µ€+áÀGªÌФX£5Û©ŒA„fÔ'N a°¤BšÊ;U3.lZÿë—™GóÔÅû; ŒÏN|$ß‘È}˜yh> stream xœíWKo1¾ï¯ðq-u¿Æn !$.¨(è!ô‰hšÒ¦”ŸÏìÃölêP$‰æP¯gìyø›oìoL ¥™ìipºnï=»¼o†iöþÍ4¸»l¾5A˜þo˜ ãÓ5{µÄ…J1ÜgyÑHcq*"~F`^[®›íGÞi¢oï@H¯¢mOÊ,+ÃwÜ ÐÎC{ST“ܸv[†WÜŠhQ¡=¯ª>r%…³ÊSùçªêªÌn¹6P®}8Y¾m^/›c †äÁ§üqˆ\¹BM¹º¢žä¬ámQx1ºŠ.Šœ 9Ư5èaJ‹( ¸çú»ÙºãZ¥i¿p%Àœ#bºŠœ¬+CQÍñY>¹Á€—ˆ¢H0PÜ.š“ýœInI¯“Q¡=%nªeö+Q^Û¢1æ±Aöº‚ –HWEºæ]Nzô4„!™BŒ&8߇Ø&Ë¿ð‹:¡z-Ï-4¼]߬‚}áŸeSõ¬]eù¶êÊ5×Z )U®o{,;(f:ºÈ\ÿP¿;o.I0Ö:¡Ã¬fˆçlHRgÍ;Í:eDSEHHÿ'¡’O,¦È…àÙKACÌÉ¥eù"Ë4ú?a YÅ Uä\Z! ~ÚUQ½¢ð+Dñ&WÂä´´DqÏæŸsÊ&JX¥L¤Ë®kŽÇ¥¨Ù¶dûN –ä‡Xÿ12"DH4£ bþ÷xC• œ“Lƒu4¢FÏP>ùmU_ƒ}® ÿ_òÏZLÞ’¸ÏÎÈX“†¸ÆJ%dp”k¦SÇLçó-Ý´t BÀ;ÍŽÔ!ˆï< é´×õMÑXzä¦j€ìú©Í;N*ÓfŠ#“PT?ñ¼Ë$P F|½ÀÄâ`aíî}â4¸}B C“gVÛZ»"`%ª¹ÄgÌ‘LU+‰ìNÓJ6Ýd£´ýÿR’²˜z¸õ"*“*hríXe[·ÕÂÌNLj„ÉT½š·e=qƒ:D#üá`½& %^UKnKÁ‹%c‚‹”Ý gÈRß¹TzkÚ®Rg»Šû¯„{ÙšcvØÜL«ÿרÜ@$ÍŽìéPã=××oŸþKÂç|ÊѾ×Pý±Òƒ G«öïŒÁƒJ·þ ö½¶Ž(ëWÀV³_zªŠÚ§ŒØÏÎO3îì­R¹hÜÔH·šÝ?èczílª{UIsóŒü9Î]q¯›µ× !åÍt­o‹DÊ™R(²’u]VCymå{KrRi/®hêì›,zd²\ŒˆÐqøPpÇÍO#¨sendstream endobj 72 0 obj 1004 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 23 0 obj <> /Contents 24 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 33 0 obj <> /Contents 34 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 45 0 obj <> /Contents 46 0 R >> endobj 50 0 obj <> /Contents 51 0 R >> endobj 55 0 obj <> /Contents 56 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 65 0 obj <> /Contents 66 0 R >> endobj 70 0 obj <> /Contents 71 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 23 0 R 28 0 R 33 0 R 40 0 R 45 0 R 50 0 R 55 0 R 60 0 R 65 0 R 70 0 R ] /Count 12 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 16 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 22 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 68 0 obj <> endobj 69 0 obj <> endobj 73 0 obj <> endobj 74 0 obj <> endobj 75 0 obj <>stream xœU•{TwÇ'„F«QƒD$É©PÝ**«¼Ñ* €>‚È“á)•ªÀDŠxxÉ[QyùÜU êŠ®õU•¥²«U»9Ý£ËÙÚÞ‰¿´»3â£þ“3soæÞソÏý]aiAðx<›¥:U©WíPÌóÖªcÖG«U‰)JÎ3—±ã13-{>š×÷L¤=±¶êåd4‰&Y›9ÁO÷¬¡~ äM%ø<^zIV—‘¤Š‹×ËBCÂçÌ™ûÁâæáá!Îxç‘û*“UqùgìCªR­ÕíTjôËä>ì¿ÕjÕyœ:CŸ,WÄÄ(c¸ÏÂje‚|•J­Òé´©r'g¹û‚nóØ÷uªÑ)Éò M²|­œ+C¾Z¯`£|ä ÂÖK³C£ô JŠKñ׫RV'¤©w†Ë âSb=áK~Ä*‘ð'܈ D±‘XH8¡D F¸k‰MÄÄ:b Á#$l÷K“( L¼M¼^‹Ï-öX\ãÏâGñ_Zæ’óÉ.ò” ¥§Š¨«VVvVÅV7­nà )Õ¬™Õµ<¦f‹«ñlæQ.¦$ö¢Ìs~Ýɽ>Öz¬Å8˜Wh'  :æûQ2¸ðaÓ!ÆVæäÔßàrÕhÿÅoäB“.2;ßp E€or®aø­¸¦¨UK¾ ¾äîª[*­eƒõëÁƀφY|æ9ü >ØPx•¡³™ÝêÊÎ-UhòJ_ä§ÀÐ\´¹:¢-¾=z0í>1zñð,Ì ñ Jíû'](¢×kNÝÚpAÖrµ£iD9©¥YE9…14«ý]ºQ˜5\J7¦Wì€Ü¶-\«žþ@cÁcO°âû³®Iû®·ý=¡aêâ‡Ø^š‚-Å׺¶øFÂ’2õ†-êp6cLÏ]=|õ\6Þ½ŽYÄ&YT+YÓE錳 Û˜ÏõUNmÄåúE£?’,E[ÂWFøjÜ-êÁÂ[?}Óߨß+e„âSÛü°Ð Ÿ‡©zîÁä`ñÙÛvÙa"[ ¶„}\ w U\ÞSru¢ÞÌ>m{\Opƒ7òDËÔQk¶°]›Mã©Oƒ5L»üA)–RiÊmI‘ˆQv F¥[d°×(Æ:êp[iuEUC[wM7:Z3+´_eíF 4G ‹K2XóLVà̶N Ñ?»¾»ûâ LA0‘ÏxµwRÇÌcâ1&™;ê_=Û,OtPɶ¯ŒÐ.B´ž° -4þû'ãÉ4‰—Påý%­G+‘¤ *hÎ;öÊVQ”Y˜œQ˜"ÍÕh»‚Js%‡)Ws‡“‘ G)i>Ëp.[––ÅüÈŠ‚SP!†õÔºÒÐyàòéÇh]Ò÷EŸˆk‰>€h|Âó9´]¨QF,î- ®Ô@{B”fwLVª,9^µ+ŒUÈHÆ)ÿðkÐ]5zñÌ(’EeÕyÇáSÛÜÂýèZƒÖ(¢¼ë¶eÀšç¯ÞPf©©6Ág®ÃL-(c )z6¤‹>d·­T†­öÅÈ…Æ÷— u×\½!Ýåm Úê$¢áˆØc­²Âr;Pù9»Q.R6)ϤµgœÏB°ï^3LƒO˜A[Ñ3|ÜAüu{¤¬˜åžpî6Lyä¨ìÃŒ AÎ`Nr°Ÿ.22 qq5;mGèç+±py@ªr»t»R‚6£ gbžjF’lEc×Ó*öêg$lׯ‹W—V¦K åûŽÔÓØžÊË*؇ö¢­­Q瓺ô}{† b¦ý5¦kI£lN˦£†2T+i;]×/O¦%ì+8¿W6~ýÔ±GÖBæ+˜'€Ózpž.zçá¬VR"ã喌؜¼ì½q2ÑXúª/cg œ¢=%é4,¤Jš³Ëö—¡J *),+*£EÆz˜rètã Ô™Ø^ÕßÔTÕÐH‹žuu7^ÿš5îêÒ5'´í¨ F´yò{ž2îân!a9Õ‹ê÷ÕdUf—f¢t¤ÍQg¤%jÔ9›YL5z˜&¯ƒBVn"ˆ„¿p2ÓMíÌL1î¡tØš„¥Tv´!I—ÈòÖ­AIÁvÎ>ÿ{·é‡öAÙ…û·:£ÑeEéJ¤TaeáTJƒóžàÅx æmžë!õ\‰EÓ4öKÌ_ãͶ‘ûRQú—näsŠ&ÀÈcÜÙañ¤N€#w•¨±#¹Š‹Û”~œgÈ7 lÉÆnõ·'–¶Hk_òsYh$ú¬²£‡ŠÊ‹«dCÐGþ vâ ÛàÉâý€Ã[N½#œ»¦¸÷7EÉ©÷—ýSö®g:mÀŠéàRûã»™KÃÑdsbÓŸ+Ùàõ²;ðçZfžt =?eH–] z ùÆ€4ËŸ|“ÔäË® K.ƒËû`ñü æ—ñìã>9Õ ~·†Fp. K(,†ï! ¢I¼ÂÉ8–dåí"Šû™ÏüjUœ6G /6 HF€¬ðB³`?#àŒ\y,e¦2Oý ~û8£àõäñ ¦bpâƒÏï†ÿ6 ½Ósî ’t6êãs öäï–yá—ï\÷zÎ\G’Žº´¸œü¬<ƒÌß.¾’Ö¢Jvñ„÷¹ù‡&…l”þÙö DQIAUl¯}ß¶æ)XòM0q»‚-3ÅÜsé!±;…õæs™sœõ"§ÔŽ‚=Ì¥bó%®¾žéàYO9?²rÒ$‚ø?ßlL endstream endobj 76 0 obj 2108 endobj 77 0 obj <>stream xœ}X XW¶®¶éêàFÙ€«ˆ1QTÜQqcQÁ $*¸à†,Š‚¡Ù¤7¶ËÒô¢Ð—(*ˆ¡n!îq¢Ä—ŒK1>'£É$§æ]òæÝjæ½oÞûü즪n{Ï9ÿÎZB9ô¢$‰bVRʶøØmâßc‰ðn/a°áPáÇŽ‘ ¦ü'né]ä,AÎRäì°çÝA.¤þp²/¨úQR‰$­x׬¤­ªmñ6n÷¾x™×È‘£ìwÆùúúz®W½}â9;69~ÃÏÉ;b’¶&ÆnÙ>ÍsYí¹!Aµuc²gTLLlŒøZDTBìfÏ ø„ø­[“vxŸåå9~ìØqÞäc|h|âú”dϤ-Iž =ÇnHIˆÚö?nR5sa€jKôòô¤˜¡³¶Æ®\4;.lÛ†äÅÁÛã—ÌMYºcsjBTDZâp¯‘žÞcÆŽ?sBöDŸI«&Oñ:=sEySC¨•Ô"j6åK¦"©0j5•C ¥>¢‚¨iÔXê*˜G-¡æRK© Ôp*œšOM¤¼¨ʇZF-¤–S!T 5™ZA…RïPŽ”3%¡úPý¨þ” ÅP5€ZK))WÊšA¹S©A”5˜ZM±”Iå@6º*™)¹Ðkj¯¤«¥ŸJŸ;¬r¸-ó‘5ÐCétú/òÁòLù/ŠÅéw¥Ž;œ6:]vNsþWïu½÷éݧ¼ÏŸ}§÷=ßϣ߅þÑýõÇ..³]b]L.2LÓ1À0àržòŒ«¿ë×£nžnܾwí¾È½HxÕGx…x!…—ÀÔf¨k’BŒ`UÞ¥kLÆÊ*­QÅápZ¥Ñee›u5ܽŽ?ñ*;K¼‚a5]­·dgéÔé,žˆëä6c0Œ‡“¼ÄëaÄIS^ÀÃd=^…±´Õd¬ªÔÓ¸8¯¬2¼ŽViÉ>&ñ±3ƒ;Rd ´JMÞ°ˆ·FÒ5fò††i:}†ÉÀÀ+ñN(8)ÛE“qh³PÍKZaøÂ)4¸ÎÆCe˜!Ûj³³ÍZb$‹øeª¬ÔšTÜ|ÌBµ¬ÊnKéôÎVsçi샟à)ðDöer:MKV˜tVζ%qö)¿•wö7»1÷ašà£ô=WÕpõøé:­Óª•7»Ûk Œ>Â[Êw—+˜{?­³4z|†¬º½ivTnC‰ Œâåvï{ÑÕ‡HçâÁH?˜siBȆԅ±ä<‡äOx7¦QØ$8+£"jC‘9 »ã?·¶¶Ú†VîSëîT©0«ÍmA¡FË.\<‹¬ìåw÷ ½×½ߎŠ(å˜éÅeº* £ÙÊ1ÏåÌy'Ë4fê4*î-j\š ˜@¾ƒÀ×y€WB0d“¬0wª-ÆÊ]ê²t1ºÝ8:Aco¼ …U²1º•U¶èö£+"@Œ.ãl†‘,f^ØãfÏÉàTؾx“l=s0PÄ®c—Nw£û6/ô%àþf(aü.3w¤ø÷À£—Ýl¼²ß~¨á4ƒ_ãqðZ6\DcV¶˜ý)ñv(ˆo[lo§qBЉ† øw™mëõÐàÔúñpŒd'MH"øÀ¿¢ÓŒšÊ*[ö”Êñ9üB¶Ap|%'1ÎÊ&f™KŸà"¼ÙR,Hl>8òR8+ÌRb—†á¾¸ÿ›á@À÷æè†ý àÖ;üãÑÔ÷‡Ož:tèä‡o^?yø+g{oxp¨ÝSërª„¦hèãÆd *áµÒÛ‡sXM¯¼½ø¨A„Ó²Ǵ⾯>§{—nžb™¬ñ/åÝ‘b²—Ò8 Þ(_?˜:”¬6mª×°d×a»#Ï춦+0[ wÁ¨„Ù°GVOW[L•jEo{T›h<§boH•}&E,@i¶oSOÃ\¼G†ãß“ÛsWjÏÝ{Âåá/ \k½ÜŽ1/‘¢1+×ÞMãKB‘ì=:½óÕÜ91±=aQ]ÞØh.uu¥ÃÈK¾&æ· ¥ÊU'훇æ¢E¯‰ŒZ4…*¦È±ôÅ}áÝ«Wö:ÃØWQ,ŠrI­Í-ÐêÙðË?"Avözs·äàôã+>{ZÂe”i+‘¢Êh!hvWëº*lg‰ƒÑ<<kqP zC ¤F‰.ˆµBt¡:œ\‘’gÑá$:¤äiHÉó‚úÔ¼ó‰÷‘úýü8qÌCpò~Žûù/ßÁBÐyå›{^c¦ù{ Ÿ~÷åËoÛéDò0‚—»!R‰NÔfÙøxZëHâÀ€acpìúÇPðxΧë-‹FŸW«ã¢7¦„ L¡i׳~WðÖ·.¾B¯P{Øî±Š.rÄB!G¯f¸Ø,’#׿Ëk´æ¬,½ø=üM© ëd—pž O3iª*L„5àGòåøaÇ:“&ïÎI+/L´Q䎓‚‚ݰ/öò  pƒ‰à)Pâ÷U@ñ¦ñP?~¹n/ISÃáM±S†o÷\ðY<4ЗWùhý¦+bؔӛ÷®CQh‹*>RÔ)ðÉ©0Ž Ktºô­³q¯ü††}ÿ:vØK†q¤¨4›ËK ‹ ÜèŸøýþ¨ûí&ô)6£R29˜sX›­¿H!ŒØÊQkròPAI.w÷?4LÔÙž‰ïá>ù¹¨å)rÌšr‹Ñøi5{ $Ÿ#ú;úuó?‚ÁaûqµYlçÊà»/ŠÀ™v"ã™0®é;T,ž‡gÊ»Ѐ™˜ä¨®"z¢ZF„,IŠŒcÓ’³‘J¡1«-åeƪRv×WW[®#ÅÝo­IKòÃáœ, “3aÂà{´Uo&S“Æ2†â#ì'Ò¬cdæç–ä£z¤ÏË×ä»ä4 AoʯpÇ^©ÍËÍEÚ(¿(¿˜üs/ÖóËIBQi‰Âhhâ]_>Í&×1&u»6 iÉ´f³¡Øld[.–› jdUïOýzٞ죨²Ö7Uך*Iœ½sº„Ǿ˾àJt‡+ÌPâc{ôÛyöV€3öŸBW ìd§u¸ËVÉa¯Ä™4ÄÀï²}tg05ÁŒ=ˆ´“¡Í°‡ð*TøJ zº¶ÓZ*‡á˜Mˆi0””U°Í{ZÊ›âÉ¥ñCæz}¾¯%’Ós‹I”r4uvUæ‘Ü›¾È8K¢äôÝàþ?X§Ý±œëܲ•—üí38E6ó„\eµ° Yеê+[ÎÜ}pò>²"k9×”[Zh@Šr£¹Üºq÷ºùó·­^Æù/Ü胰Bg=Æ}ÀñÕ½ï€zã÷dììðhßh¶4\¹¿¥¼f×î#ÇšöÂwëâ¼é>‹ç/?õx+—gFEú½¶"2柬RW•[BjŠZ­Uçå—jÉäùç²lc® ´XÌ–ÒC¾ÑVšlíVŒûz¡Y‰'vX ó õ( ­^ » ,Lªe×:~òT£–H"S-Û-òNk‰T“‘“HÆDB¸g‚´)™û·뚯 z6õÞÈ‘SýƇ‰ºÇ2Ïãc–Í4âÉô_}òí«¶˜³GYØMÖßûjéìù¡ËüýC¿l»yåü]7:0Ï_]ì07tšï‚+wÛ¯^|ÔãM¤Jôj†ÓÇ¥Bt(‹TÅHKÊJHÒÚHÿé‘~( ©JÕ­!¯(WÄ·6[u,ãók×öŸ<˵Ýð=‚w8ú`Ç3'aêƒû“Ÿß>Ûôgõç•ÉksT™›7DïˆCŠ9‹Û¾ýþâµöë­k'àJ5¨°l»¨àÓÁ]§áeLbR솺ÄÏ8 }üðÁãÇÆrj8Kîo‰‹«ÛÒyÿPcc¡XÑÛ`P >8ËP`((A˱´¸¬¸Ä¨R ÊòÊÔü¬pòa1”i *),) ÿÝóËt¥$;(¯ 7ß–í£M¢Pº£´’2K,LúÈUeÒ ˆrLäß4K+\ ÍÀTU% ‹Ž0ÒvõYY&­•Âðû=ÚD˜½=÷I­|j‰ø¬­¥?wä>wvæ{SÔ*ÎÜ endstream endobj 78 0 obj 5289 endobj 79 0 obj <>stream xœ‘ÏkAÇgÒhƒÆZ BÁÞ±BHio¶Ò „6˜ñ8»û6:™f&´[ú¸ÒK‚9 ¢^^¼{ðàAzè_à̓׬)‚³ ÞÞÌ÷}ßçý ¤X ”ÒéfÒ ”ÈÃjv‹f …la*=®eÇ—IýÕËkÙb™¤å©´\<ξÏeïndo®g¯gÇþú‚~¤Ÿvž<…¦ŠÝ3Xu¥Ã[mwVVîC€×«ÐD×Fˆ¹@Xßj<{´Yƒ¥ÚæÔP¢aÝ@ðêûp‚ýù¥‹ÒÝåÇÛ_¶¿-ÿ¸(õûóÁÉÖYí¼4óó^ïrz%½JÈoñëÛœ endstream endobj 80 0 obj 466 endobj 81 0 obj <>stream xœ}UiTW®¢éª…¶@mí.L4¢1n¸¡YÔEEA@ ¡éØ (M\“|`âÚŒ(* ˆ­ bp9qañ8J—ÉÌhШ!‰z«çaμ†,'rNwWõ»Ußûî½ßýM9:P4M MÖmHÎÔ&%øujûÒDQN‹£Ä1„ ¶›I:†Z|°×¹H‹ãÑÑÎõp×…œa”„¦³w˜ÓL뵚ÔLaŠeQ>~~ÿX™2kÖ,!Ñô[DJÎÐj Âxr³!YgLÓ'2ç ÉÓ:6IÐèLi©B‚Z¬¶¿¶2A—¼NPiuÚ´4ãaÂBá­7ßœâO~ÞzG«OÌÊ" ÂbÁÎÿO+E IFuZrtPJðzMFjH¦64ky˜.A˜‰R²Fò•c²cƒt‚4Aº]j•þÃÌ`"˜L/ëÀú°¬[ÁU8…j7[ *pÃ,´XoðÍø )Lbð§6½Ïf°ù•^ þL åMàÎãeÌc¨—º‰€ÊDb°x\âAð’Y¯ˆu|ùÛÃÊz®Z›:/W½·H¿%+¿°0$¬³ÑiI ™õ+ÖMlË)<)}‰X ù<1õk,GžîïcUïk0 ¼Þ¾X‰G1¦`cÚ Ä­½ú¼u_óÁeIUuIº€Ž¦•q„ÔoˆµdËÝ΋§y,¼ÚAVj™“ÍÆNÄç÷à·³nkÎ*e=­šÐ“áòcÐFpàlV©‚c:ûúš»ìàm™/al/ }I‹e°/4£OÑnt~ÛÙ-õš§Ó.ašpž<;â0¼à‘7øƒëÓ®ÕJ¬`r֪ׯBIhÝÁU[,•´rE½üÞ»µ_¢ZT“c1oܵagŠ½Òˆˆ&Üiñ–MÏ÷[±»èÄô;õgHÉÜû9VäÄ ©]a¿>Ù¾p|%=žPÏ€uW¶\ºØYý £îÙÒ®èKª ìH葸oO^“³ mŸ‹§ãi‘s±TéßÝMí}ü3ýÀÞÞëb%ÐÆ3ÿ½Ò\²£À¬xΦ¥f#nR\ü›Êðù“n÷‡‚ †ÞcÝl“ XJ¾³›¯ 0A?‚;þf‰®„uù+½ØD®JÆMIv ÊA…{Éê~Ww+ëºÛØx÷À¾—*`,»­ð#´-B‘ ú@NV÷‚h°áÁsÒä‡M&‡-0ÒKvGœHPXÙ“« ±Ç–Èñp?ìŽçà¹ÿÁŽ0òJ}é¥sÊ@6<:.xeêáÆ\Ç&”>Óžz9ç‘ÃÌG7A¦$8_ ¼ S|÷¹À7|V…†Åv<ýñô΀<f²o8ȱ»=>±Htà €™Ú…Ç‘"™¡ÂÍ~]éeiöÁüs90'j„¬·6ל›5ê=õÆpa·Ù¤Ø´ÿÃý'3Ÿ`ç/t ](i¨i¨)?‹ÚPwª5¨ ·ŽP›óö Ã\U¥E!ëë@'2 £¹ß}‚ŒÝAج$]ë Šy¥°‰/­§®T•çf•(̦O6£Tß0 ÔajN©ÖUƈC*´Ú¨~— iai& Ê 41†{4¯—,Û#Žæ1‘ Ìf¶'æ­ÏJçdÖxÍòô¥rì:í‘Ý”žÇ ;wj“ᄲ̴?§8™oƾØí1"¥HÁ¯áÉx#N‡×ñtXýôzíÝv¥,ûŸôC$'†öÒ÷ݤ’ŸãÈøSMêÂÿbfX¾«®Ø¹§Bñ”Ý\°mGâRò÷7+á 18¨&¹o·\%jë°¿40ßv 16½#!ma-ø Ih—…î$\INñ¸ÏãûàK¬ÆWâF¨Ä¾¤d¾pŸ0§!~väó°Þl±ÞS¬#Y™uQ\BH„¾æš~ Û¹°xÈõ`o¶½Ü e/b›ò ¡[í›ñbZ¢ú;„Qzø2ÜmAúŠ}L´¤aƒûÿÌÈì½dí| Nz#¼"`ŒWÙA~ý‘ c´±àðv;ÇÏ_‚Ýó gv·9Ô…¸óGMSþû›¶)³óRåFæLQË‘/Š-N®H#í\W}&»Y~}QÙÒÊɲ‘qg^ñûLdð<Ðó2ëì¤Ø 5†Š†¦Ã–‹fEó޺ݻŠJöŒ$tÉgs™m¡…µ-ŽÇ‰¯tÒm:rˆµ‘:¤ö¾$±|{:ùâf¾31øÕ“ج™k±ñR®E­½X‰¢–÷9lÁÙÊãíû¸Ó¬¶`ÝÖŒëÒ¶¼‡–q˜fáÏ¿ŒÔÈÒÁR$xv˜LÐC7ß‚öî(ÿ“õžÙR•5jI|Ü;ašú›Ûx4SˆÇö¾djÁïÉAqOs~Î?”²¾ëG­×FóÔ[xÌìcT’˜º%-æ¶‚'[X¿³t÷¡âêšÒFĵ×hß JÔ(·2¿²(És»»ÚßͶÕ|ÿPfq¿R ÌÚšÒÏwãóUÓâW¿£­¾ð±ý|(ÂÃÍa1ñçŸÉÁ1nÊØkMêV]¢òLÂiÆ^«>‚zäO@.¹iׇû7lÑêtCdËž,ª1—[jŽŸ@8 Ù'žþ•j18Ü)k"Çz (@#É|ˆVþƒÓùGL•)­áUCSÑBõÚ UÎ\„y„ÝÌÿlYcØ?Õ÷ Wïï_À$=óv[ªÎ‰WЮªâ« õíÈŠÊM‡â8É_kZ5o~ìò%‹âÛn}uêü5¥[æ±Îó,1Gpvï!à¼×ż÷¹¸RÔÿô-9  endstream endobj 82 0 obj 2405 endobj 83 0 obj <>stream xœuX \×ÖŸ23" j‚â..¨ È*eeU@ Ö­¶zÝjE©ˆ;­²¸–EpßY*Z‘€[µj¿3éï}w‚¯ïëï}ïG’¹¹™{æžsþçÿ?¥×‰Dú^qÉÙqY‰1ÑÂ7+Þ\ÄÖ.^Sÿœ¢É“ §|ö?3Dbd Wú™é6c>g04„ÜA”X$Êݺ×5-=/#1>!Ëb\pàÂñ'Zý{f²£££ÅÒ¼ýbá—™Ÿj1† ²ã’ÓÒSâR³fZ¸’»““c,â“óÒ2-¢ccãb…e!ÑÉqI‰É‰ééiÙã\Ç[ØÙÚNžD>ìüS–®Ì´ŠNʹ𱌋_™ñ·IŠ¢ø¸ä¥Æ,ò›êçšæï¶bY€{F¼GfB gVb×Ê󲓂½s’£?ÏMYºpç¸ñ&ZX˜dmc;yŒÝwÎö«g;LÙ>uZäôÝŽ3¾9+߉¢&Q#©0ÊŸr£)kÊ’  Ü©” 5ŠšOyP3)[j4HyR“©1TåE¥Pó({jLySÔx*„šBM R>ÔTj"µˆò¥æRVT(åG¹RÓ)–êOéS(JDRý(#j 5ˆLS %¥)95„Š¢,(Ž2¡r¨Y”)5”r¦†Qi”eN¹PŸQéHJF“ Szd°€ÚGuˆÆŠ¢E»ûí—Üïn¿?Å£ÅHüLÏ]o¹Þu‰X&Ù%QÐ"z.ÝJóL s˜5f7²õì“þIý û7÷ÿM_O¬~¸þ>ý¶ŒP=àÁhƒ5¿2´7\nxȰÑðƒ!™Í5Z`gô¥Q«?p÷@Í çA‘ƒ:Û Þ9ø†ñããÆ µ”ºH¤?q9¤`HùûCžùeF|Rj˜bQ‡Z .šÎúc ªÄZÚHcƒJ LÁÛ‹øTHãá4É{ïÒ¤H°‹>¦H~£Û Mß)8œOƒtIŒøopªR3„¬Ê—px´Ö ›ñV;ܵ¥IÓW/Ë-H@þD[ó¶³s˜ýëö}C'vÚwôàÞCûÎAÆh¨°­~~kB±qK›óØÖi*=ÛB JÔ–¦¨x !F áqJØ®„|¥q»ÚÕ~jS)ßnJº]:XuŽ•~(¯,®¿f†T1 Ö嬔¿_vúòC3ÔSw*îtè>wâb-­ÆäƒSNl8’{4çûƒb ’Wädä¦nXHnòÂO9Ø9ÌEtrÍ¡LbùPÖž¤x3”ðeÒʬ•™)k#–ä¢/–åÝÐÐ-†#|9‡ÇÚÄnØM5ÆÃØÞ0æ9¼Çå[œ¹çW± 8ßÉÆ:° ¤`|M¡– F Jx8í†z’Ñ|~=AÜt|˜Nh:.ìú3;,Â3åÒZìÞE0âAݱ{eÒ|‡ ^ÅGs½×fâÁÂφógÚÛø=#0ºõø™ì¯AµZÌ›BÇx]žbIn– 9ch8¥%sUjÆH³¬ïö_T¼L%Ö˜‘mØkÕ´v0¯–Œ’¡¢yC­JòäpY–’¥ըŚ <âRÁÁFÐx4+9Øžk¼%ÂúTŒjv»:ÿAú èÍËCÌN¤×‡®MÌOMñ÷Mt"Þ¶¼Á_ Œ¸q%/í¤üXÆÞäÝÁº+ø ‚÷ËÞ/u›J¼ŒS¹'p”¾\›Ù„X0ïà´‚Ûè‡ånI¾±îíÎŒô¬òá~½9ÆüfØÚÏï€0ðz‡Jù6ðj‡6ÑÙ§b¾Vs¨ã«šüªÄ§3ëÇ“m™DªažólŒƒÎ& KH5dyDÄ{¡´øpÚ…œ“Nn®g·6q;_ݸó±Ê;žS6¡M›7ó€”üm¥ˆW j9òÖ´Ö[{[âÈ×+µÞ ïÅß$˜>ØÄJ5¿—$h˜&Ãqx$¶Æ¹8È–©ÚÕÝ·7VƒÁ¾Är˜„—ËõÀîwl€}°ÛHr±Æ6–„i¼Àë=¹XË…Têžß£æ?S‹{Là > ä@6Öö•ãS´ú£9Ç߆9 ŒúeöÃ>³ÇãQòÿ÷O˜l㉠sÒ‹šU.íJlÀIs,›Œ%„0œŸŽYkÝÑkUr\Æà1š™\A¤16ø ކ·”¿Ê ŒÐ—g˜·ŠTbØM˜ËAIŸ.>P¹sÚ|PÖÌän_³eb#£æÈíÜ=[´|D7ÓǺg•°K)z¦†žWbâÌmòèkèâþªÓάF, ŸÑ†GÉðå)jš7׃]Âô4„Ïœn'ï£mJ(PŠz ¯]?B©¼­R»•P|±R;—6‚§d›sUÄûf‚:SiE³@±'Š= GkÏœ`¥ÍGùöú¶‹)غaËŒ–&…;±ÒŠ„fpj è9Ñ g‰•Ǽ-13›‘¾º½è„—96³ØyJ©ûùPyUÔÕWÐ}ôÓñêûl:ƒÜÖ-ÉN]™¼$oŠC‰…Yr÷®ÿ~c;•Þ5®-"jwq\Ñ¡lÙê½_íÝXÁÚÓÛ±aÓ|Nªrè“{oQç-ÈgôÜ—zU «9_ÖÔXžº`«ì“Ž=€ÙÅ¢–N"câR%S:‰„ vìØºübÙáU…hÛ'd*Ÿ+£]g}&ƒ“}D? z`¬VîñÊTúü€Pûº³"f²\ÊoöF‹¿‹*J’R:áð†£›^mƒÜòY§¡+Ïüx©Ó Œg5cKîÑáÅBþJ¢ '×—æÉ.ZŽ¢Ø)Q±ãdº:õ)*%S_Pó«•„¥5¡<ÇákŽ‘Cê5™8 sñ:V½D^ŸÚzYðüôÁÄ“Še/è0ëuá™È¹fFLsÇ„MYœñ-„þÖ^Ñþ3aõÒ ’„eY­ü„&Q)”8S`Ôt<ÊÍóg­Ržr¯¸xëŽã²fÍ7«7Øøµ…rÀO#È ±Í)ßbQ#Y;¬®-é¤5)zÓF>B:oÆ5ªyjx­òT Õ­æ÷p›Ár|'žE²í0ε«z™!‡yáiϯ™ç–š¡Øì˜ä•©9‘kÐ,|0á\ú_žÞr‘$~‹WQÔ‰˜:Ï'ñ FÝèçâêòÚª²{èê º?îþ¼v¨´ÍþpÒñ«fïÕ¾ö^ˆÕY_ ,kŠEÏy}Â5™¸K­I±ÆMÚÐD¯çÐ%”TVk¨à—ñ½NØN)—Ï6áK¦ã:FzÑ"ÈÝa^ô™;2ž™®Ç8Ü ú½­öXs­Lš;—Ña_C+Eð=AþÿhÌ9X¯Ô6;†æjZÇI‚æ½y.†‚ÂIäB-è‚(‚JˆäK$“hþlë#ôX:Üý£Å¸þÉ7Jpi?Úa*]Ư…nnKü—á9 xLúþÆ©óèû«ç=+™4Õj¶‡}ÄÁ¸ÛNréy<È5.ÔÖ (gÁÀÚê¢ÒrYIѽ•¬4õ¶“D46‚‰×JÃðÙWœ_˺ýè4[êT}]E|ØâŒT'"ïÃp5'=´$k¡¹kÀõ–«egž]×yÐÍŸkñ޽b~[7§íOïºþCE÷¥7—‡þvùR+Q™W¢+£+ðB“‘WR¬FÜš¨M®¬ŠÞVóí‰=¥¥ç:ڀ؎›³ƒ–‡ûÄËmâqÓ–x­Ç¶Ãød!m5«ï‘·©´Fð+¸÷Œ´!œÞ|mïñS;ØJÆãò4‹ÃHma»Éûpæ?!xãË Ê´#™Åð2<]+ÃS„¡0£+n(}ùmÆÕ*(êõè%ÇXX n7]±E"ýðª.jªÝ‚›‰5ï¾’“–*°pÙþ¤rÏ–„W¤­ó¼Ìzꦞ Îý£?ÖýlF3Z±9¶œí„'~#SÑ[Îo?¼ûPqÕ…ÃW«¨Žœš—û…¼Ȧó6Ü–ü•ËPúbGÎÜ¿yèªÂÜoó7Š[´qÛ@o-ÝZ¸½°ðÐþ¢¨•®/üêµÏУ_œ\{±wÏÖµÉ÷1}è2QˆÚº½=³ÎU3èÂîýMw5¹ˆî³Ðú#rþ5g&¶–uÑè§Ç÷>Xq¦„$¤íR¤gäÊØy@B«²²Ó6d¡xvÓkÅM¶ì¥uöa|;ïCÜAÜÎvVó‡ö‘üo?ÒJ¬¨oçp pçS »`Ó-âgȤ“:Òö£C´&’ÇôÙÚSûIt^Üô9:Àw¢sDUG6‰õžù…i%+Î?Hî ±¶|ó&€…õKüYhÌꔥòã&'÷bãZõâ Mäm*íª%¶-iW ýŸÜ(-j4ù/¤Y†h*ˆî’öa›@~ÿp¿£4•ïè¾ýßRt–a,–㡽Ö0¤±öð•Ÿä›±KÐÄ1È-©*hba¿œSÞö´qðó25äÆ‹Þ›÷뎹ï¡ù½úÂ_ÞGø|‰^ïgÜ#͉À-ÿ/}|R!~º°üƒ’HÎ……#qóÑ$þñEãø#ig´2˜ÎË`Š0fŒ HÐÊ77ÅoÀþáróOaˆ[~o9üƒÓæºó¹ôh½¾«QV)_"ô´>¥´B¿s€b‡A×.CŠú_°DÜ endstream endobj 84 0 obj 5222 endobj 13 0 obj <> endobj 15 0 obj <> endobj 37 0 obj <> endobj 85 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 14 0 obj <> endobj 36 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 86 0000000000 65535 f 0000033589 00000 n 0000054596 00000 n 0000033443 00000 n 0000031501 00000 n 0000000015 00000 n 0000003589 00000 n 0000033637 00000 n 0000053690 00000 n 0000051716 00000 n 0000054013 00000 n 0000052172 00000 n 0000052733 00000 n 0000050618 00000 n 0000053011 00000 n 0000051009 00000 n 0000033678 00000 n 0000033708 00000 n 0000031661 00000 n 0000003609 00000 n 0000006685 00000 n 0000033771 00000 n 0000033801 00000 n 0000031823 00000 n 0000006706 00000 n 0000009454 00000 n 0000033855 00000 n 0000033885 00000 n 0000031985 00000 n 0000009475 00000 n 0000013819 00000 n 0000033928 00000 n 0000033958 00000 n 0000032147 00000 n 0000013840 00000 n 0000016924 00000 n 0000053472 00000 n 0000051476 00000 n 0000033999 00000 n 0000034029 00000 n 0000032309 00000 n 0000016945 00000 n 0000019073 00000 n 0000034083 00000 n 0000034113 00000 n 0000032471 00000 n 0000019094 00000 n 0000021177 00000 n 0000034156 00000 n 0000034186 00000 n 0000032633 00000 n 0000021198 00000 n 0000023053 00000 n 0000034229 00000 n 0000034259 00000 n 0000032795 00000 n 0000023074 00000 n 0000024911 00000 n 0000034302 00000 n 0000034332 00000 n 0000032957 00000 n 0000024932 00000 n 0000026675 00000 n 0000034375 00000 n 0000034405 00000 n 0000033119 00000 n 0000026696 00000 n 0000030383 00000 n 0000034448 00000 n 0000034478 00000 n 0000033281 00000 n 0000030404 00000 n 0000031480 00000 n 0000034532 00000 n 0000034562 00000 n 0000034594 00000 n 0000036788 00000 n 0000036809 00000 n 0000042184 00000 n 0000042205 00000 n 0000042757 00000 n 0000042777 00000 n 0000045268 00000 n 0000045289 00000 n 0000050597 00000 n 0000051624 00000 n trailer << /Size 86 /Root 1 0 R /Info 2 0 R /ID [(߹ѹ²:"Æ 0WÞ#/)(߹ѹ²:"Æ 0WÞ#/)] >> startxref 54801 %%EOF simh-3.8.1/DOCS/bugfeature.pdf0000644000175000017500000014470110205461206014220 0ustar vlmvlm%PDF-1.4 %âãÏÓ 314 0 obj <> endobj xref 314 13 0000000016 00000 n 0000001031 00000 n 0000000556 00000 n 0000001284 00000 n 0000001533 00000 n 0000001907 00000 n 0000002485 00000 n 0000002521 00000 n 0000002743 00000 n 0000002820 00000 n 0000005663 00000 n 0000008333 00000 n 0000000850 00000 n trailer <<6c1b74c15a418e47b2d4377b6dfa4702>]>> startxref 0 %%EOF 316 0 obj<>stream xÚb```b``¾ÇÀÌÀÀ)ÏÀË€¼ ,`Èñ eVÁÖ? LÌ?Lâ½`s µ3´¼¨ÊDµØ)ze±SìÉå f¸‰ê¬b§H ûl±S|Cs›F€˜@>ˆcÁÀàÒÀÀÐa.ÐHQ†VÍ Ä`§¨0ðð)>`à^ ¹¾Ê‹‡á„ðƒ©<Ž;4€åezr4+Ð7KÁêæe0€½Æ` ÄŠ ýB@èIþ0°8Ë?€†3 endstream endobj 326 0 obj<>/W[1 1 1]/Type/XRef/Index[41 273]>>stream xÚbbRd`b``Ń3Î ƒÑøJ ÀÃW endstream endobj 315 0 obj<>>>/LastModified(D:20050125213255)/MarkInfo<>>> endobj 317 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 318 0 obj<> endobj 319 0 obj<> endobj 320 0 obj[/ICCBased 324 0 R] endobj 321 0 obj<> endobj 322 0 obj<> endobj 323 0 obj<>stream H‰”WksÛÆý®_±ß ¦ €Ä«É¤#Kq뤪='“ŽÜ+pE¢±(£üúž»‹'2ñxF†(â>Ï=çÞ·ŸØwß½½»ùpË\öý÷ïnoØÕÛ›{—¥ Ð?¦ÒâÊc>ÿ;>ß©«w›«·›Ë<¶yºr×u#¶I™~Z³Í‘^Ú(æ¹ôÿïømS1oå$±¶gž—Enè$^‚_6‡+ë]³[²÷‚×M%–LVìFnûYÖc‹Í›€Œã}? =2»½²®·Ï¢ wË öñžÝŠÇf·ËŠ~í‡ÍÕw”ҦץiÒðLö4?Ñ®|±OᆉïľëzîƒõN>²û¦,²/ ;´–Ì_Ûw¼²ýÅ6?¶Ö‚ÞZDÖ'ò· Ÿ²ÏVSny-¶ÌíyaÃ|ðyq.r6rW{yn”8.ì™êNMÞ—¼è­®æ­ö L­†ã£g‘¶zß¼zÛÖßY9nÈÏY…H?q)ÕJ\=8^ŸOgðé­µ™©ßs f-Ž ¬MˆàÖ¾FësáŸAÊ*ñNkß9YENè»øˆœ|(ØcX2›’¡2ß2YŠŠ×äߘõ;³^Ø"Ðñ¢$I h,z[½¨Z˜,g*;49¯eµdõ^0®Ts(ë ;4ªf‹Ð ¨ô¼–+ra÷>lÏñÖô@ø¬÷¼F”/¬¬äc™Ò.zŸ}ßWì‰7y?Ÿ#Óž©J$]ðõ·Œ# §ZàgÅxž›¨eIa¢£ì޲ú‚¹@"C7ö'W‚çlÏ«í‘WÂíù¨0{®X!k6St×Ô<‚¶x~ä/ŠŠðŒpjÉ««† ßw•P:ÌÞܺ2HL”ñë(`·ª`ŸnL*“(LúîŠì)KyQç/(oW\_“Ÿ…âT(à3ÉBtûF±1„ÙGáJÜß^Yâ·R*Á*ž –Êb›QiÑîÇPmÏ5Ýôœ0ê SgjÈVMkÑp2'뎼=o Añ›¨ÒLQ û’×{¯—sÝñ´Û_;Qü ® ø½\;TºQb»dªI÷€{jò2yNY_î9›ÄxR¤¬uùŸ²]CñR (ý™äýuâ*éBìRªNžÖ€ æiÛBi¹QôA×Q]?¯‹ŒlL"{Ê ^¤¾Ÿ£­µ  ¾Ççyþ²ì§~Y^è¬âD‹»?“\ E†0Ï7Eç׿7‰¤í;fõ~@÷Læ!bpšŒIÓ:êt0É ›e)«š›Ê$œ}L™íÞÂ$¶¶ÌF->ˆ "Þ:çD$ú Ñ6¹ÀéFü‘rÇôé8c_Ç÷‹×Æ ò'CóêDÌ×ñÊ CzËuhê’9%OþtŽ^|êþìšæþ¡˜³uäëg®R#K—6¾~Góâ×u bÇïü#4uBÚÃTóO4<Ê =C Þª…¦?,ˆ?ÝgµHi…¨À"²ÆBðJíÞÔt’ù ¤ÞS•Т~äçù%tw˜ÅÄ3¼Éòlðéö“¶ Y/uÚÝ4›7'Ñˆßø¡Ì±Ïïù³À° ³4—));‘d|$I¿Sµz:~M¥¨ayö»iÂÜO®¥'<…Æj&ý—¨ßÝßv1î¹2^ΣÈîíMÉ´‹kÛTÄ@…¬tFvðÐ0[ûý$FLwÂÔ´ÅD#ˆ>#ËX(™’…†QlMöˆW¬¾[G&C³åv–&¥¤Šñ¦ÞËê¯g™'üÊà àòb;µG"|‰ÓæOºq™õ³ß^£xn]é'ÏaÂ/f»¯±×Q©Áo<ûû7KvÄø‡èpM\Ž'Î*-YÉ…å¹>©”?¸ôÛ##Hâ¸+>ö 1 ìyVj®á·;r‡%B€yýÍÓÆêG/ñ nZëº!«Èk— {M]É<‡E¡G¤æ_œ„eù…2Ìê7ªOäR±³N¢Aš©¯AMoVˆ™%íUT øùŒãÀýÆÃÇîÂöð#°>/¶o.m úêÈ[2dÕƒqg`µK :ñ‰»\@àuz&·Š(wm•äF"°cVï‰i5ÿ<«³ñas3«À‰0°Ñep&ÀùªÈsn Y+_—Љc££ªådÖªÖã‹®Œ./z'PàZ^M²Ÿ$=ÅêF.{™0ô¿çsòb‹žã~ò£ÉU%ðsK=׺5òÿz5—¬¦Ö'iI–öÊ"˜¦ô@V=`Ç&5#ÐÞ ~µÓWï¸Ó¬¾˜xâı®³vIÕ#~¦è²éüb©Ä–ŠÒ¨¾¾ók¶š!£u7ŠÚòtŸx–Ó´ü[6¸``Ë»Ð]BÓþ¯bˆ@!lI[2 σò>Ð qPs ¨$Zõƒ8˜Ã€ú‹KSÀ¼>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 325 0 obj<> endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰¤WksÛÖý®_±Íô¾mE3Ñ£­»²#æC¦êh à’Dâ"xˆRšþ÷žÝ € %Q¶›Œ…‹÷½gÏ~¢££Ã§gäÐññÉÙ)ž^9äxÀÿS$.Exþ7<_ä'³ƒÃÙÌ!—fóƒžc;Žã✽>ÍÖü»YN®Ã×?p7Ëpc;žˆ4'×ÑÇñ¿§Ùêà_Öl©èôSgbýBQNª3´:}+UA% *4-ñÈïô\ë^‘Jt‰ÛÅ’Šh¥øm® *SêŒ,_žé²   0Jݪ¹ÎE­£8¦¾ö©óïÙ?žø04>ô\ÛLÇ4;ƒq™ÊS@cQµÁMÅ÷9%º õP›êã}Âï)Jò¢3µü¤M&\ÞF“ǚܩíºý Ç)„&j«‚7·>«¡_uI E…ìm¨bÿ‘ „lãC¿åØ%÷jÑâJì •‚ðšg×U?ê2#µ*cO „Ú°™Žcþ>ÃôFé¸ÑÉêÅ›ÁÇÊ›ŒŽV±U®nc\Ô1BÂ:‹¬Äá2vtzžu¸Š1D¬ äE˜wÉOBjE¯í¤ëÖ^ŠÊ&a¬¡IØ“n¹ãœ§œœ¼ÀÔXšéÛ˜cº²ŠóÙÁùGn‚Mc¸ucl¾ÃF8âoUÕM<{2ár¶¨3ûí9yÞ³òZ•!ž žŠLmgâ8FúOpçï~–)˜Šd¤D˜|Ê£UûYåÙ Ñ– £üŽÂ,ºW?º˜]Ù/Ù×ÿ*ûCÛ›8îp¯Ëƒ=.j—Q¥O¤{®=˜H—@úeª’“«3Zú¹ø¤ÓTçQ¡j_ß±öm™®‘9‚Óé´i°h." CÒñ¬BeY™¤ƒ Dq_{^¿Ý®4o8qŽ=tq–Öû…¯]F”U¬óu›K<Œa#Ä4¦7²«®dIbÖ…:(W*)Ðs’·H€EÀí­°ò£¤À?•uzc+÷bé_Kå3˜<œíq_ŒÚ“ÑÑžŒ>mÐgõL\9]Æ~@šPöp“–…`_ ÿºâv CF=ü#?M}”|?"V ¨á:V mu‚_5ߢÖ~ÄðÏ€•á¢Z床Ù&[homçŽgœñ´õ¥€)ÂÞ¨¹¾¥O@J˜Õ›XëNo`§ß~ŨiKŒèº-ó­NÓ½ ØÁ›Œÿú!¿ò®­UÔ;^E€Ø›H£øZ·é²K>ºî0ʾocé3ƒûeKætmy¬Tʬ7ÙCï8csnBuߺ‰XÕ~®=ž:ƒf"ØÈH(Sü¦L"Øúéç‹“‹Ë.}—=HÕw]æo]¶Ú¹î\wZ6·LvûýkùÛCn7/»6Ÿ³“ù79_ 41Ûø†Î~º9ýpyuŽž½ß-–ÙÛv‰<™ç;Aôl:)á&£´à`‚È@q+ÅŠPHÙ.(_j\3ÎSÑ ¢,(£6 7®OMcMlœFudUžp¢¿¬ËE<ÒsòÑU®e-p,ù˜ù<‚¸0:ðqÊOÑ_›rm¹g°W«Ý¸< ðer›‘ÄÚÌwØR0¦ÖQò‹uÓ™tÝœŒw‚ÅöþÀ»£i«9_)_h(˜äÊ9å(ÇËh±¼Fk¼ßO1ö5½4F ãþüô"‰°ÔìZ"½Mçjäµb¨ì!Ëë"è-ý8G„}1²KM­QXf²ÁÔ…Âæ§ÔIYz–vg¢±àK0ñ[~+A $PFåP°ôA¸®©ùL®(³äý¾(|eHaCÈóS'ßCC™læÈ#iè…=õÉm1+Ås€Ö\È›¨8[ßf Ð!ð ç—ÁÏ aºE¨J Ï‘ÔH/áËܘ ÚgÊç!ùÒÁØb·K¯…˜EÿútÊ¡…›p÷<È5-øpmÉ?Wim·â‹ã÷+ô ·ô wšýEO¾8:Òÿ—r¬væN€æ/H¢œþòá½yƒRÆæåæ³=Þ¦­l½FžÍÕÖm+—ö*¦®9´î`wc3ßnG¦¿‰Ì7&SܳŽó$õ¿_ÀÔž€öø9s/Ð`Üës°3½&^i…]ò°b¢'ˆuÌDG^ñæšâa^†?ìŸ#g0©Ç°N laøÙܪÀOžÁ Ï{¬_´à.Öáu§ fW€á7”U›½dSìÕª¶VÝDgl©/£è^,)­±]*Ï ½ŽŒ&(têY•_ÖÞ?°¸'áOØ.BH¡Ð/ø„ùÆ #@zUC,©ž¼D‡ìã-;ù¾úô£“ùQ®Ƙ¸­&r…}OœneO²'@ &Þëh½êa [?¸£5]1ÍY ÊvkìtD3*•#!™ŸÐŽ^-µMxÚ p§d÷e£á ¯”Ÿ "ªuæçÏfEôçœ?–âXt…žÙ£dF3+y­41²~g”¢tù)ÙØìî£i;A^/y¤ÈôX²ÌÆT0 æ‘5òY«¸©i]ó‘¦ßËhÏÜEpo:mø}púX,ý‚÷1L¤[ôgŽ.—NÀŒ‚Ø!~@Kÿ^½F7‚m¾‰¤â³ÞÙ’›µÜÁ©%{•£ÙÑã˜Ø¨¢¹*‚%¢¾F˜Û}³&zÃ>ª óÄ!ËX`%zwÈOn“çzg…Â5|,Œ^s I¾©¥—wÌ—˜Lƒ\›~¤[Ô.WDÖé\—WÄb™i.áÅ’X?(Dö÷[Äv0î7¹ó¾A@“:"˜N‚Õ w)GR>s“b6#—Õ‰Kêå%°W+Ú®¼ˆ`¬Ùµ þêü ñ¦‹B‚ˆ¶lB+Ù%$Ñ~žp'ÃÑÆ^¹Lï V¦å`ö‘(DžŽ7 ˆ¸±tÁl¸÷`ú5M[žÌ£ D•û³*FHãô(€)JL×U¡rµ­ku”…&EZ$'Ø– $Aa3ú6<éì1ñ`ãÐ6¸²«ÆÈ_ñªû»v´£‹ØtÓH|äÖ¸Ò­@Ã4MÜĉqihû“É´ÎÏÊ—v‘ŽCþq=å!òáòê={Ö•ö»±ªpêT’—²A>ƒÎµìVFö ó.múU—Ì ª¡‰Íç¾"áEÝG(ÅÇwÄX‰:Ú>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰œWkoÛFýn ÿa>,` +Ò|SZÛmÓ…»I¤¤âý@K#kÖ©pH«î¯ï¹3|KÔ"A‹˜zݹsï9çž{õžýøãÕÝõ»f³Ÿ~z{sÍ.®®6[I¼Aÿ1¹J/&ðþ/xÿI^¼]^\-—6sØrsa[¶m{x\1“žÝˆ-ô³¥dŽMÿ«eŽ–íªˆúÉqCá1 m?ß]|1Þ¤kö;/Þ.nØ$4¶±d…؉ô‰íñ2ŸøFö˜ðÝ$2$ÃgxïÀñO’Xlòßåo6%aëãMÇrü9r¹¹0Ødù¿ œßùÔ‹úÇ& Ë6¬ØrÉÙZ¬ÓË‚e«U9 Œœ‰”]ÒÃÄt '—,>eo®b}.•Àm+Ìé'²æžïPBkR"6N ¬¯ß¢?rOÙó5=NLÏ`/<—eóо|·¸~Ïð=ý%}KsxœK§™õqÍÅéÐÏ9…}ŵ6¼H AäMŒüeÁeÁÞß¼wðÂaù›zñ0QõUßbYš¼²ø1+ æâû‰«ÓS™Ñ÷tõØÒqº9†MŽÎì¨+_ T–~ôñÃͽ‡¬Þ"º šdJëó›{v@›ã‚MÌÈHxŒ7=+¸Ÿ²8¡¯ÒWÞ]2‰ BŠ ÔÝw³p:ØôNµƒÀ‘Р,gIŒ™¶Îò]Œch¤ǨжæÐÇŽã€kdmâ“MôFŽÝí³RHŽä°«‰[DAhº3ÏóL¹•»E'®×ÞGëÂŒiÐ'^¼ˆgaÄÝÅò™¯§'‹ã„•ºVÑzüÃÏs8%5qNféUþáJ1U«†üëä9³Â¨f"n‡FQ«â£’ït‹³¬h® Ù ÒzhV{‰Æ©žFk!Ÿ•æÈP¤°$Ÿo¯Ùc޲ÊÓ×÷jƒ7«R,÷l#rYTÍN‹.¶€PGsö3ÉÒ§iSÀ¾à»ÇU{ð¯ÛM4‰“}F—Út:nc´±>j1DE™c“$7"iqÐ3uÐìÈ0˜:T¿—œŒZ*5JkçϪ”±ìÜr^±¦ÎËÈ6½)L})õ¬Òs (‹÷”Ü&IÏka{*¹åÄÿíÅö{‡–¥]ˆ¤–0ç?{–mÊóíºæ†sšÑì¼°¹g„툜'N RõY¨Nù¾]†rEþ)'`Ýc‡ëž@/m&ð'q\ÐÚÈóéøösÆÊ,·øK;ÈC›hꉄwÕó¬ìz~•mà A²"’„}ŸgOØKi­…ÜW§¯©ö³íjboòl§á£5lS&š”zj륌à_]©eAëÒ:7ê˜ÇàØá±¬âªUÁÔm:¸m€´{룂?íg(à[6õ|Xô6[*“ŽÛ¯âàpd.-°‡M̨·–k>§ÙWõP%ÑÛÆÙøÛ(ž¸îÿ±çÖѨÉ~lu"ˆˆïzÚdܾÀ¢«kQ¹«5T--LX À]k™œ(EsFlHÏîîsˆ±âu9ÿZŠ\)°f¡ óº½L<¥ð)änuWPÙW,f|õ¬¦štV¥U¨^†‡-Wèo܆¨…g¼A…›ª•Çí°ÚíìL¢6™òÔò6¤‘mE5ÚëF.†×¤žtZÙ/wöoÒ¥ ¹ÊéÁƒÇt;[$|Õ9SƒÓ“ jŠ1Ù ¾]P£Ðò}µp õôo8lV endstream endobj 5 0 obj<> endobj 6 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 7 0 obj<>stream H‰œWÛrÛÈ}gUþ¡SIÕ‚YÂàÉå-‹TÖJQ!a;+9”ƒ€€Ëå~}ºg7Š”v#»D …9è>}útãìÞ½; '×S0àýûËég“…Ë/Ð?(—Ù€A‚×Äëåà2œE‘ ¢õ`lè†aØ-¾™NÑŽŽE%0ƒ>Å¿¢˜)àð#0À3|=Às&D›vU¼àe•dé~»'žAõÄáaûIóÌ'ìââÕ ï+ùŠ.‡ßÃg[7FâÖuò ìîÿ€÷ £ÿ 3ÙA€ÁLZ’-ó¢àËjq¶÷/óýÊð™[„«rØåtR¦ãÖé°€Q:ÌÓmÇ¡Ñù͉›n ׄw²eÜ£¨-–è„1:¾M=bx¯Ây¯ðØèÁTz8òÏ ûdŠÒù⟌¡FÉ"‘CáD¡î¦wãáØ×Ѐr>Ê.k]©>cJøžÛxÜ#”û²ârÆžñuRÁºÈ‡®¶ŽM¬¹ÄçÄx¡$îA{h¿EãlÉÓ”º1^>¨9äÛ ûl¾ˆgW÷¦iJ?AêÁ®­âg9f½F« Ì£+æžEŸlçŒR>y¬Š„Öq¢Ã5– ½@ï5q®€0<¿ŠicO‹æÃéQ¢pL…h¨ÚhYãq¦¥xÅ/*ZˆŸŸy\”#¬¶ÜkôÌ‘ä–éÐÓð¬][(·¦q F͵†:ÁDI•ÂÊ fö¯ééB\„Uð]‚¶ÃKÌ1:K¶SðX\L²öúé\DZäW-x ÷Úï‡8Û1¿§!Ó¬|Cè+ua†î™­ Ê8 ˜¸ØËd«"~.eñëA}G­†x ýI¾-•±+ҿƦ¥‡“• F€ ‘”åe%©ƒzDàW£i¯ŠVâYÅñùø$Çg¹}À¿°Kù9i·ÛT’^ Ò I<=„u<„Yb3À¯Ê;Ä7ôRÛsuß# !w ÃI8=Çñ6û ôs¯Íûá÷#üd(xñsqMIª©3‡E¦ã‰‹>§›£Et©€3Áb9¶}!òÞÿб˜„©o¾¼»Ÿ =[t±‘°¸=¯äè‹þÒžÃÇ‹ÏùÇé¢ÂÅ”ŠÖ}r÷ÜÏûÆù3zkBû©®ÂFz‚"¿ž2ToóÅô³ 4_zõm¾©b?åÛt%²ÙåÅ7]Æ?Çií3hx)y'Ê÷å-z¨ì¶cwàã@m›J°B‚þ®l êV궸<;6=ÝóDˆí·‰+Üâ~gr—…ˆÒÄÿK\– iJmPí.h:ÊÕý&¡VÉz/Y…²{Þ×ûµêŽÚŽ„ï«‚H'ˆ^di¾üvÞVíàÕàwuEû/. ¢”8T¥ê;¢7DÿåÃuÍa2»æ³››«9̯>Lú-â?PãÅí,‚Ÿ®¢cª¿ì¨¾ÃÅõ¦óëÏWê¡×·â¦Wàå3_ÕûÛ¯ f`뎜 Ê„ D=Ig¸£‰¾óÞÓ+ „Dºscã®ÒEcœšjá¾hˆµGo&Q˜Ó…9Qc…Ò“FWª‹žòªnÕ KHGÎúc²e®ìC_÷£i&N õó¤¢å¯$b[«YÁ¿+kÍ.^ÇèkÐ~ÀjA~»–n* ZÕê~¡\Ð?ã O÷#y%nSkÆÔÄÂ…;¨qÒ”v12èK]$åFšfrEèõ›Ú pg=Ìçw5Ÿiã{‘)bÛ´Ê­Gןáã0¼œÍ¢Ñüãåe¡79F†²¦j£;±–ûýµ\Åu,ËÐß°ÝãÁP ‹a_&½9t1'–òm/¢¨½îÀÓÙä†tØ”“7¢+壹Óvµ˜ÈÍ+ UC콡ažá/ë’þe6Ÿâ§Çzã .¢ÎTOJ¹¼Ýï/[ ÛñÌ7u¹^H¤ôqmK~åµ7‹ óô¨AÕXAÝú{ôŒà+Î ßF2¤w[9H«b»¬¶ÅQÙÀÿ/:T•îúʽôéâjòÒõË›¿“c¿ôÙ½¥¡˜v*#ÁÍñðñsÞGzÇÃO/$+Î-Þ:·8~.|ëÜËéoyaôBtÁb®/ ¤$ÎáÔÏÅ Ï«§Z'dÏ?è‘Îofg·Ÿÿ'ÀcS*Ù endstream endobj 8 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 9 0 obj<>stream H‰ŒWÙŽãÆ}( ¦‰Ã¢(.À{wgÒ˜ÁˆöéöC‰,IŒ)RáÒšÎ×çÜ*®I0¼´šjÞõÜsÏ}÷™ýøã»§ÛÇ;f±Ÿ~º¹»e“w·+‹E%Ð?¬Œ² g žÿ‚çÛrrNÞ…¡Å8 7“¹eZ–µdaÄè“í±ðHo…%ãýü~ Æme ?‹y–oxÍfá~bÜÔ«v²Là¿ê˜³C‘¯S¹/Y’Ñ7,išLÃÿL¬ÎM~¸kzöÒâä"žÙ–•ò¿µÌ"i2ö!)Êjƾ<|½eIÉŽy³(¯3<#S:p»3èÁygqÎMîA  ç[¿UR¿Û?g1\EyÏXRýP²RE;ÜU9f`7QÕ‡C^ʘU9[Kfã£|@zR™ZåìéÓoƒ¼‹i©cRƆ=½ùô)\ý{FÙ½_øËt:÷ Ê3ÍEœ "cb]ÖEœ¾á ?¶@x:ý=üÇd\¾ÐìÝÞMŒ¢ÎJ–£]k¹É©W0ñ$Êr]— UŽsY"£Ç¬O¦GM©0¾é9V×±&‘²(â#ú?ëßå}!T0¾¹X,‡¯"ÛRV%Ûb/ÙÜVîæ¶gZÞrÜMU&‹"/Ø‹ñáöþe:cÇ]í´9QUhMr. §A=ÓaäÙ{¶#ÄÍT.á*âî.Œ[fàôI¬tï€ó²*ê¨Jòl€§B ³qÍà$ù-AÈä:ÍóªRYbY‰$ÕY››…éJ¹#ÜÆ ’`m¯í ká=hŠÕLLkkVœG5YdoÆöuI ae²¯/7ØHE߀û² WïîÕÌä9ƤOâ ,Õ·÷áäþ‰Økuô[Rã-©HkP‰!--ß\*Jú5«id?‹jW«?rà ð˜·4¼núˆÃŠ'›çöyçc¿ÜUfzßãlz~^œµÖòKß1=”×»jÎ9kî<sâYDá\ǼB_Ñ<4u+JV€$’L‚z²@?÷lS§);OáK·Á!M˜^a<³¹BŠ®éòÀ9¼à-âÿ·²¢Å‘g€N Ún|&?%¹?To,NÊ?Ê÷@},%(]èp‡ñЈœC®¥ùÞ3=¿§IÁ«™L*@ŸU²$šByÊ®P×_[ÅYÖQ$K¢WZ—OÅši²QăàÀ*Û'Û]…QVŒ/ 9dø£dl¾ü…±»ûÛ¦Ã1z6")è±}ㆋð/þXuέÛ¶ÕRžÎ¹AùIQ Ç~óÅ‘a'¢µÞwõ!ÆDëõrZ¸³ &üôy?ÈÚî|)Ã²ê ©r7hÖíÂiËOe ›B¦”(;ÀÉF´%ÌY£6Žô¿,?i«^¸dn¼q3yTµ˜Î—Ƹ4GR$ƒ,O‰›Û€k/R†à*ûeå±Ôm< ªrYo6I”€EQã³Øó4®µù\šn+¥2£ŽmDT‘VQ 0wó±hŠè-š ×õ¶]%Cs Z a²g«J°¸.hâ® Xï Ó4áŠ[H«ªÙíÒÑ«è\º1PK‹ÌéH%áìã?QW;ý#°ò ŠB¢À2Éþ~‰——عY2gHÏq-8·Ô5fu/3k'¬Æ¸úÄm—9ŽOD»ÏFHð„NéÔ3JÔ5£ºU;A³_Sñ%}qL€—>âO;lJf{K§AೡgŒ¦‚,lŠÝE¥¦KcKƒ Wø˜¤„”1"ø©?êe¢nõ¼Wüó·j÷ÂãÚU˜§Mç c÷^gppKÈ’ô“¾©ña?å}±NßNôè|‡Wqž ”Ù¿°´:Ïè š@ªñtaúºTr˜hËMמŽã±‚V* ‹qÎTOdX£Žå‡BñlÙ^l+qÀcŒûNdTs™öéò;5C¶½Ün”y)“¶Ájzâï}Gø]]5Q0u“4!~TW¯gÕ:e•¿ii~T=ÒP’?¼]M}æ¶'µ\0U÷Tª6WSD·IÚ¼zWN“ÑÒî HC_QŒÊð¯Y¢°§ý |ŠãBª ƒ(q—¬“ ®gä“¡àtøóÆÙ“ä)“ߪ¦t€À…ˆÑ:jGë?û¡¢›éºÑŸØ6¹ò:WͲáÄòü63qo I¥° »55ø|„àˆÉ~Ò‚ßïÈyÏÌóÖô¨9‚jSêªÃkBF,O{¸çé-¾ÈÕÂÖNU%°Ÿ^~ÃIƒÓÀíIcMÃÚL•Ç>JÊÄÄ€3ìÙº±·á*„ÊÉ+¡£óâv^osSnF¹©•ФC ÚÊÄþVVÎð­÷§‰ò}Ầ½Pëè …ûWÄqw jæ[_Ú¦ã¨4‰Èoˆ’ÐïÕãtîÀPœË2#ÕP1E5¸‘4ò&O1d8'< ÀÅ_ÓÞej£'ÐI‘gI6{Ãçý¾Îd/¶û™×<½8½!›Áî—½y©.ÁŸ¹h¨¶cúÈåj•¿Ëe&m£KÐ eì€s¬/Ëö•ƒŸÙ>/ˆiœ¾Aè¡æ )œvÂéˆì%Åi½æ‚ÕåU›–惴¨m±ß“+íN€þˆ¡¡ qU¼kk£c¥‚)«B(FÈxÍŒ °—Hä­U„$òÓ<•VÓ;ˆ{È÷ÑÅmU›ðo£y—"•ßdT“€G¨èêÈ±ŠºÝ.âFö¹^ - §Ì3¥QÓWUƘÅd÷ÿº}`çdx§›ÝÓûŠdn¡¯ $+Hykg¤C_A|xŽ;fŸ÷V¿Ï^¯K_eI±ÕT·C*" êÎBí"‚@‚"˳b¹¡Rmm,íUnz‰B[e•º=©âÑe%5< è¾„>o®Xn÷ãÙw¸SÛd~P‹qm½­´•Qù¾~¹]…OìÙón–¿“œßжm`¡.´ ;‡lnµqùA/–‡¡é½Œdßôì~*TúÞéO9(´×½¬o{iºöé UV .ŸCž(2w PàSófì¸K¢ȾÂ9)1ß©«áCn· %•r,èLµé Ðu—ž©ª¢tÄ=)‘A/’’îÝ ŽË–1ùI&ÆVkZ•«*Rmú;ŽJŒ{+¡Û®=±¸Ó q¿oÇÅ뇠2@H'°Õ»£@DZ™å[iâ·.ž:˜‚sD;bTî»DÙs²í+”ýp9wèpÏ1ËâÝ©ÃRT2e©Ô£žâc$.¡seºÑDI°D¡ÀÏ ¿«ðJ#÷^ N\¶zît¡CLS¨Ô—Ù}¥h_z‡ª~¿8¼²•†™A;oÏúÐÏ,-¤ÙŸl4þe0ÕiÒcÚï&ìâ endstream endobj 10 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 11 0 obj<>stream H‰¤W]“›Ê}ß_1oA©ó°oÝ”ï®o¼7q¼e)ÉÃ:F^`(@++¿>§g ²])?¬„EÏéîӧϼzd¿üòêÃÝÃ=3Ù¯¿þvÇn^Ý­L×x@ÿX7Kñü¯x¾«o~[ß¼Z¯Mf±õöF7 Ó4=¶Ž}²¶>Ò[ëšY&ýý/¾­+fÙ2þ„& Ì¥â5›­óí½8²X²„Õ‡xÏ¢‚‰ÍK*5++±ÉxÎ6œl±þÒžæö§y!g-¥KÇ$7šxáU&Ä3OþÂØÇ‚#ænÇë&ûlÛ>köQ#c©ô¾®è–a¹aÊ8Íž³&*9ÛÑÔM•LlÙ¿\ÃbûHÓ¼<ºHßòÎke'VˆF¥ÐO Pø¿­,Sa:Fõ =ÇêÂ9n¨ð©€#€i‘¤»"*Pñ/<¦È›{á ¯¢¢&¸tÊJË´$ÌÀ°ìæúããJ·ÑßJJ}ÛóÓµ’›†ã¨WŸ¥Nëf¡/5@õ•ÿgýGû¶ß¿½”'ëv`¸ÞEB±H8;ŠêYU(BVQ¦Št÷øÏkX<Ãvº<6‡¦%U-³(-Ø^ áÝúæÝâø™÷VÇû¯Myˆ ?d®ï9F€Æ(æÎ…´'C*ôVß]Ëý6¼Ò`8¡ `ê#¯nYÉ«}TÖ·,KyÍRÅ ±©yõ5³LÔRɵ¶#g*cÐbΓºgˆ9èÅýVóXèešó[P7Ž5—¿å_ã}Tì8Ë1hu?GV¨žI1T/"t¢ª’cTqÉ­jîLG)·ÞÆATYn ¬Î£,Së‹æ4çEƒà,:M´ÚÂN‡ïI£ßÓœÖeó[Òön∅¨p„ÌõtTu£¯©‚ê\B}PŠÐ”»+°=ìfjaµk!,¯›€' ŸÆ>Nº:†ö¶?4HD[ÝÁRßBÊý©–s„ÏS-oeÐö§üÁ6Å#Nñ6VVMçInÒÁxJ.Uó"~З~¸÷h1ÿåeƺ«aí8š¬ûêáÃûiÕ>Û ;ûxÿˆ›]ÕTµáÊRTÔà‚}úÿµM¿NnY_ùˆ' QB^ä™aŸø`BS‰,]&-Å=;”¤°Kö Fgµ°4Ìò•õB§#/0•m1îøçö9ËÞ]P;åÊ;œ”ÏÏ#bîÅ o&/c*M½{q½ÄŠÚåªàÐyýaTÂÇ+f'š Ë{õéIÈBÂP<ß²ã>÷ä^ÒX·é íUŽÎ Yp¶žDHfT¹ýVJÒÇ•0cQ7³‹n9'û“êé8¾áø¦ï\UèðŠB÷õ¶—ÖÜ!¶ !ÏQ­·e‰q(šì¤|W#½þWÐ8W´í$`ÐT¯%ÈYj£ÎVÁ¼ &d|x{3 ˆ4Wæ¿5â„åakÜ\ÑT x ó« Ë9T?FðД®cx=e[¥M¸(`rLvEMÃ?û>mxê-{{¿"<·ýUÔm^j2Ý»66Kó´QÖÈeŸV€,Ç„„m†¤üø“Ôѱ[ä¨ßbšÁ:èb¸Ÿ´ñRjë/¹ÝõÖºfïù~3<þèªAf[He"â™y ‘÷3Ü3,àºû_ÏqÓ2Øçô„´ý€X^÷%X8ó´l£öbf*l¶Ÿ,ÜmÏÃvoýÏ“FÜø¸Â HYê‡øVU‚F:áÌ^xšµÐ¡Çý‚ÔÂC‡èkÚÐBŠÕC<€Äã#+¡û´ÛU£¦2°¿7¸ú€ÉS ¸Ž4˜žJ 8䜇ýk´D°9¸äk5­4%Ò4„ß, }lPÜ ŸHV›IÝÝ }í˜âЧF[Þùæs¼âìõÑ”}›cÎR¥öw÷ ]BS·»ÚìF£x–!ÆO(çšH ¤ê*:صçõ<Øéƒ9‹Ð†Á·e¯®uÚê¦Oï¥cò34ÜÎõ?igHÅ~ÍØÝßß2däx!×Ç ú¿opálz ‚Íæ|ÖÓ†oæÈ›h…ë Ã@>®Y`ºÃB·Ç„Œ¥£$Wƒ®e;`M“ 6$Îh¾"êWEmLDÁç3øîE\ ¦2XúR œQn›J§/ÍåDË(IhÌ›^÷³ƒ7 w>2HãâzghsGO»‚É#|ÇpÐ?t„ÿÝ#ìïjûÔaž…™¹:0ö5eÿ¡vº&üZŸÐ¹_kQÒeª‹LàûlvÓêüca/åXŒIôcáOÍ­²ª4 è,Æiuý‰JÁi¹VFË(óÿ&¾=­¤?E|Ó“ÄwFЬï³rZn§Ž]Ãu£%O a…­ûÉú®5%¬Ï)XT0¨Š.‰ä>ÓŠú¼˜G5-²?Þ©¥m„h”;ÂJûLJGÖ~õ¡¬¥gð~9ä%K·ŠTRœg„ô Ÿdoú endstream endobj 12 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 13 0 obj<>stream H‰¬W]sÛÖ}ׯؙ¾€­ã^|'q:²d§v¬D2Ig¬>€À¥ˆX´Ìþúž½Ah§®5ˆØ=»{öìÞwôÝw/n¯ßÞEßÿêæš.^\Ï,Š+<àªâüBPŠç?àùCuñj~ñb>·HÐ|yaÑ<Æ·æOüÕyEÂâÏÿð£7¦%µ•æJH|\úžeáíõ…A“ù¯ç¯oÙñŒØƒ9r6…ËòØ¥¾;òâ…¡)í0ôÙËÃw…5q 碛Ùõ M¦ŽÂùÔ¶…ß}~Kq¦¢’&žQÕQ½­hò¯ù»!xò „Z¶ ‘mB|Ûë%¤ª‹RÑ_ù„¬°‚ž‘OñÕsç¦ Â#¬<§f?]Ñõû÷ Lyó1Ý0»ÎK.Õ8ßŰÂOOeCŽô„)ÃÓdº_Ô[ôA]ÐÛŸïf¤Ê’¥¹G:<1>Ã('ÔMú™ebXÙ¿\:];à ¼v.u!¾FT%m¢z5×°nº‘ž‰`rˆäzZrÞkâ »‘å1OÃB<èI¸Ü"ApäI6-òW\²øÚg\ ëhC+»G|n‚‘œZ¶f¾ÝƒÐtéû«ßI6”’Ž7015¡ò­^tFëê'”Lñ£d4=ØRÜáÑ3HqèfÒœ¥é0º4þYÍ u:­œÀbšîYšËsKó—ÅñBMô–o©^¥ù#~+ä´¸£¨ÖÛ© jUl³„Š"šýxgއ=,’½°[09®k6b3ç9Ms´YÞÓ¼ç¶i Dzšùs…}1Í¥ðQ·á˜DWÚíaÂ|0Öѧt½]Ó/o $q‘/Ó‡mÕi‘_N¦ÌK¼·&êwcé˜FsÈ&HhºÞ”Q\§q”e;,²•WéGMά¶Ô½jMÙ”>6M‚`›Ú"—¦N×ê’žx^MÔGtF­ªZ%gêòYÁír„&’á˜X¥(Ðlåãç–iw›ï‹çE²,}ÐÑø5ç0¢E¦èºŸùªïoa«0]ÖNL¼ÀD,3Ôê^ª‹å)¶‘Mz p)µ±cL£¼Ñß“4‚Ývàäš.çìÙ.‡íydÀ‰o3ÛE;¹¯¨‹q&„BÅÈÆ:]§1M;)3µf*«¼ÖÙÅþÓ#µì¨Ø,ò¡8 ¡ ô>/â[lI•ñ*­U\oK…>z³-ÁÍò’›‹nicí˜Ó o*`P€ ó‹´.£rGQU©õ"‹Ôç L20=¹G¹Qeº˜(«€l¾J+ZfêSºH³´ÞÑ:Úõ v Ò.ÓÆÔ1Ä*}ÈÓeO¦®åµn春D÷#(›æHó&Ò.RÕîVg[¶©´<·E}ŒÒL3;S©¢ŒôqVlxMsBéTæA!„Õ¥’­õò™’Ó‘†Tí kΞT­ÒYVÚ«=õ)â \ê€fooÿAw7wÓƒ{û PÍ"¯ž $Íë< :º)}D=ü'f};ɲñãÍ;!°ßýÊç»-Ê"J¨RµÞÏÛšŽNø¦':H TŸËEe”?p.–e±¦›××u´Qtoð‰ vâwïÚ”E²u‡,v:3**Q{ߪî'\¶ÛÙõ]{æ;à’'#)<¤ùƒq¥YFñ¶,™é(ôÞ.ô|E„i” 8ûƒæ4ýÓÖê\ØJëŠVj—€áOz^¯¢ C[å¸*Ú” Ý”4LxFN?èf—èÚÆSTmã÷¤Š3ÅÓl_“¡˜;x±#€ x«¦'U©púTZ/П|4­¶å¦L+®Õâù †Óæ s´-Žî˜Ðº—RV ‘«G•©å‹KõÔíÞ?B¶ŽRhaš³o~‘ÉN?k$Š2÷ÁЩΠʊü¡ÅTd 6(… ]êÜ?E¬“£Óyx}¶0i)À rž(ÁÙb/ Ã*?àÃvy ~3P´Œ¢#@ˆ5W µLz ^(P-e‘–nü2û§·£ß°AP´­‹ý&ÅÃeŸÞ½õÇf4V¯:7F\ YbV]íB qÑõí4'ªÎqÆ= Ðøýì-Šû·¡ÍÑm÷ ¢ Ÿ¡¶ûL¸z¨Àf@°Ð¾Î%Ýâï*ÖÄDŸ…þ™ñÃJä·XK5mæboP7Ë9“‰8žTËo j[GP£²ŒvÜ¿s㬢 VR•ü}”BÃgŒáe¾åð±’8BœßvìácÂ0†¼x²9;€hG£.o<…*ÌÔ†“§†XAh°I”ÕÎR&²„ ^tÄfÝW¶©|ŸüƒÖO;'NÆZª´Ê²)y+ †UQ6õ€F÷Z"܃³Å^öd§yÍÎHˆ4)r¨(~u >Ñ%|ÃSt€M·OsÇX$*ë󖨏ø@ÁbÝùèÒor‡´ÛáD²£e %tU%—4ziÞušµ0À´h_] 9À¶19¦3»štM?<ƒè:~ Ä}b…qm3ì6±l]Tõþûg‰h›r_…¦éq²cÙ(°®`´n ÀÕ»KÇ‹V]àÞÝí›»9½$WǦÊ££êôÄìN^g‹Ó>ÊYy/w1ÉÎi68u íÞ€YàÚ¯É~Nwª}?ùfTì?/>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 15 0 obj<>stream H‰¤WmsÛÈ þî™þÌäfJµÃ]¾'7wµ-嬤²]Kµ{û"WšÔ‘T\õרå‹d‘ºt’|ðJ"±ÀàÁƒ··ðãog—Ó1ðÓOãK8{{97 ,ð úE˜ž1ˆñû_ðû§âìbqöv±0€Ábu62tÃ0X„`ê&³ñôBo- `ýý/~ZäøA7¸4¨NŒ;àâÑu  =Ÿ}Ö€þ½ÿ¦É\ív|çVÇuP F–FŸ¸©=Tß.ã²:É_Å`Ä4üæ_‹g“ÅÙdF¡´á±:¼c÷#÷ÕÑ$ÿ»œu|_ç¦ï»ä¬ÆLæXÆ;˜ÝÜÃ_Þ0—£C< ÿ® ±[›>“FMÝ5›àˆ0Zù  ÏÌ34Šú%KYÁrG‡|y*8=‘ñþÈTb”^odž«»^“šó.¦ xƒ9<­ ÑA}ÏÃDŒ$îäv\ÀP*Š2N’ÁÈÑ åϽޚߛ×ѹíû^ë­m¡·“¿‹|4¬ÆÇ_ɽՀi ~ƒ4ëõÑú^D síûÖžÑKxâwßà'× °ÅPyª°8Ýϱí,Æ}UWZ˜ˆ ‡%åäQ‹Åâò)Ì{•hGÌvgÌòƽ¼p»7fÛÔ¹{—­9þ†G“3ެ^Ô•a앆Õë‚ÅtKÃn]p¬vžPçßV˜›{0ÙÍœëžï±CØg°S.úQw¿uÓ¬ÄPw=Ô ›òЋº×íÝmÈ[«+9ø8ÌÑ}V%Yë Ê?Ñ>’eLî±uÃÔ—-FÁ,ÖÈ-a @~·5ñÛV¤¡€>iš•ð’å_ (ˆ:÷’ÅÜ •+æê¶áûu®ÊR¤:ÀM åZÀ§1E!=’…åêHËêAÌÜù'‚M.Šís°LvÐ2¼JPËV#néŽäÑÓ™EJ¦ÏÔEXCuÀ™\-ÅZ+Ê|–1>²€Û`äK÷Ñ,¯\ ÂP…íL£"ˆ"t­N–y°A,Öq"ö¼´c¶´6Ræö]ÔV¢ ×qú$Ý+²mŽ0W–¨~I¶a·$"ïB€Kúãç ‚t·˜>rÎ …Y-×qQÂ"šl­ §¶`VÊ¡ulŒ©ªá*@üG„ÛRD”o >N÷ k¬ñ&#V•xÆë{>€æðQ»½| ¨pM–×ɇü9Qáµ}Û#eð» ÛBÈ qL“A*Í ,·A‚PÐXÇ‚6UV9æzmEÊ—Õ‹”+A2ŠˆZ/oÿNÆ;GÊÒaÍÉK°ÃîIÙ;E¼Šèd€$§¢˶ºÂ°L„‡u@¾ÀSFe‘¥?÷õ:3¾™?<_Gÿ<67t⥓$ÂN¶£¢îºÄpt¼ÈVzj8H‹‘#£Mç¨`ÿ̆øõÎ\Í€«ïíÍôz±ßuG ËÃD:~ þâ>Mî®'¥ŠG õÏñD_ڦݿ œx̪·½!Û6¶M£5(Ö Ô¾ehÚ£þÀÀvL óánº˜TûÊâjrÄ÷êjGbŒýáÓÝUÄWÓ_®àâW|ýæлÄ3ƒÇ ÿnœÝ²NÝë·÷ö Ë2©–êÔÖðârp!—ƒkrIÕáŽ83= x|3™îó«› è¨C&Ø×H&#›ÙÿwÎN ÄVËŠí Æäºå¶I«ƒ¹žP,å?ÐÅì6$i8ýד!ü:!BáaWç÷ùÛùµÔ~¼e~{<=â¯[zYH̨¶´þVî–s¿Çšdß°uò*¾Dò/jÉE—¡1v,äÈŒƒ¨/´ª‡¥ìÞ^§›žÕN?’q•Îme¢ž‚¤;h&ëî±Jík;uJºJ¹Éá‹£9{ÅíUu?B1ï¸ö!SÉ\´Óyâ«@í±ÚŸöõ@ÞWC•îôtlf²>› MÓ¸,D²j¨†øŽû´LvÓâ­6yà+êÂŒ„‡Þ[ݪüä 6l~”A'KŽw+€nÕu‰mèÌ‘¿ã-WÙ D±R<ËíÍÑ`[fa–®â§m.P‘ìI UƎα¥ÈÛ4¥Q(þL ØdE/ã$.w”­’„MÊF•±WjêÀƒ—@I¼Rx æñ§Ú‰ U ]Å v?¨ÒÄÌáð–”¹1¬ ׫ ÀÆ30¥k×Ò¼…²Ô¢Ì´ÌdHƒÒ2ÎÆ‹.Ô,f”ÛdÀưn•¿á  +Tjä–¸Ò›)é)Ù”ÍL02@€ïo- endstream endobj 16 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 17 0 obj<>stream H‰´W]oÛÊ}7Ðÿ°Eó@"ÍO‰J‚ײ“¨“Ô¢oâÂX‰+‰ ÅUIÊ‚òë;3KrI™Vï-Z(Š<{æÌ÷åWööíåítvÍlöîÝÕõ”]\Nç6[pÿ±b™]8,ûàþº¸¸Š..£Èf‹V¦mÙ¶íÀõ’á¥ë±è€¯Eslüü ߢœ9.ÁÁÇÄfc;´&ðžË¢í…q'xlÊ,=²…”eQæ|·K²5K 6ˆþYáé#<Âq-g v|a,eVæ2MEÌG¶Jùš]ßüúêöQ¿?žÔïû.½ïX¡_½>ÿö‰%+7_›~¼c™Ç­Ãµ}ŽíàÛ¦ë[¶0Ó±2™ ÌwC®cŸ%%«(±E*—30~°ã~zõ0°›ÁsƒD½°›§ÀrCD³`Ç¢[¶•û 1ãÁÈÇ`±(Ų,à<^Ò¡(#PEÙm }"Ü`·)±xJ–•>äI)L ûCÄCൊ§:¸¤ƒ3²&~š4BÙ.O"+у<;*` *[4{„Uc6€]šIñt»Ú·lÝår)Š‚epZÎrÁ— Í‚êˆæ¸+lÑVv06J€?Ub´°Ys% Ø&ršÿÅÞ ¬±oŸ²WΖÉ*Yò2‘ãã¬HbÁÄjŽd:gb}d¹^`»Q”|‘&ņ$ÆHËaAdž±Ûùô+«Åp‚ZWÑaÖäMæßg)ê©\N)$Óüegfèiçƒ>GŒš!ë1¸Q¸Š–MŒz£š¤ìlG&'„y.ó¡†5C§J)ÛnyžÉ}¹Û—ú]¶£ùZ áNÌR90}CîŠv0øÝÊa6˜šI‹U©/ xB °‚j›•?'ñ]¥j8n! ˆ·òIÄ–:ü&º¸¹Åz¬k´S×èN VQ¨Šb]dÉÄB—“Ð @‡J-év{{+Iß¡oáŒÏønD`ð|:Ÿ12.¸‡¸Šóu2§X6^Ë´·Ž!aëäƒ,]Pd‘¤­ø\a:Ö•°'¬êØW`ÝZ­£ K?DC’³¶*Zw‚ KBäÁšÛ9”HªeÚ²¤àÇ¢)”ʈN´8¡j…úÜk8·ª¨¦‡Åò5{ÑÕÞájß³<ˆÌ³žöÏxZ%¸ÙÊp¸¬Î¡+hÝçX^@æ ¯ü Ì‘ñ¦ú¬?în¦_î®qÿyáç<ú%ºŸ3R¾íާÕÖly?„Ã&§IÚ˜—œ­r¹en"†ˆl> …( o¡-ª<hÙ­Öjå°ë7™ :9Æ;Õ!%: MöµÇÊúé÷ûŒŠ/‡£Sv-Šež@´ìðÞk ÙÏâE¸3²Ø-Æ×g´Çü+ö»]šà«~€*‘DpkMŸgpÉÓãOzj`ÂT%ïÐ=99 þçÓ5^ ¶JDCÄãkmšÏ¢æEÛº4±.‚›Ï›†7.#&‡¤ÜT) ÷TVRàd%'ø´ÙžùÿÇÿ?%Ÿ©É¯•ÐÀƒÚ jZYk ÃNmh YŠÞyõ·ÇOpãæ3 ðüO‘K4ªÒå¡¶T³b*²u¹ÁÒ†ŽXÊ-úY ¨ø?¨oéá µ$ƒÐ? ho;Þ|…ЬÙ9£‰½Æ¿ƒ]B¢<[¡ìÆXÛlµ˜. áÿÄ ¡k­×ƒŽs<Ëw»i÷Æ#0ÚG.Ê=\çö¼¹ÁŸ”Gªgë‚”Á«Œ=ñ<‘{è- fsÚK•¤käZÈ­(s”/çÖ‚I^G†Éw¨©NHtOáÖ;Æ>¥\Ð?SIÄÀ£@«ÐO'Å G>îÝÿêÌ ¥hÖÚÁª»:¾Lܰ ŒFm9L;‡X¡¾Á–ÇgáLù9é¥8OåÅ%lƒÀ‚ÌÿQ\¢þRRBµç´jƒ€ÍÌ ƒ‘£ïœO:]ŸŠ)»¯ÑZC )8‰*ý>¥—([taRÞ¤Ä>RãïCÒS•2íd™è赃ßSmªd±_Ó„µ!¢U–$*kbQ _À„ç-Áí¿‰S9ôšf‹ó'{Ÿ¨~–‡ åA¶7D-J‚£4ð–Yž>ÁQiRŸð›<Ë"†I [.Q™¡+Üyô0;¡ßÙ¹˜t,¿ž!$q%A#ôä¸PU–€•8è ¢}S½u®º™ÕI¿É0u(y¿g°Ãªž)­9¸;àÁÔåùx0åéÙîY“ªFLã—e¹çä9œŽ%®ºXh[ü×|Èâ–ÎöØ×õû „~€ùÐi͇ʳ=3¡g¬±«¨1v5›~B þÍôÆþöøí.ú:„ÙÿÞ°i*xÞ¦)¸0q˜‡Ž÷ðÅ IyÎXqÒýé:æÆ÷0¨yÃV)_[Äê"}#Bo©×Þ>Þ¿'B× ¡™ÚL÷*†³f* HÛFó÷Ç.ÆÇcÃóø©0Ä¢­É»Á3ò§ówCSarU"Æ©agÕ ¸o÷Ÿ£Ç÷Ÿ>  .Ò~W3õÎÕÍßÉ;#ûëû{îrž-7¸]aˆž0±ú™\ÍæÿÁç7iÒ@.’Ò=¾ïûœ—u¨h÷¬;Awݩ¹'†]vØÐ  cPçuíÕ«“”ÎĶ‘ÎHUFÍ&:=‰…Ãye7V j©ådEÝwcñ”,5bÕ#õVÙVõIC˜±ØÁÈ ÉÌ”PjN²œSµ„¡ú¹†kT7˜¨"Fxš%À6,„‰}¸uÂôã:=ÐSóé|¦±Ý[b×±FnÅ´ës0Æ2Yn4qí«t{¢êPÜÊ=0¬d•,UÏÅ’PårÝ­4hЀŽ+G㊴´çÝ8!ŽËy™Œ+Q*å®hŠnåáZ:éð‚yB«#rÔÈ—£ lG¶7ÖÖƒ6îE3[Ó±’ñYGqìвG¶ïüGy€®“r0V°µ„b‹ºú°+~zB¼!@Ýoq\H`ŒÏM™¥GÕý¤¤H? ¸q zÃFr:ªžDå¨i­•¶¿ÞÎa˜JSµÒN¯ï¾Ü>¸®[@ñE/ek³H²eÕÓ*8BsÆVØ,0.ËL0¹/™\A$æç…S˜®’¸²Ó™ ¯<¿âFpmjß 4 f¬H´¼bb´()’2‡é Œ¸Õí›F³Ãƒ±K¾/è!V'X'kèÍ:þ-À’…® endstream endobj 18 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 9>> endobj 19 0 obj[27 0 R] endobj 20 0 obj<>stream H‰ŒWao£Hýî_Ñö$û6&€1§ÕJ3qn7;ãË(¶fVš9­0´  ½t“(÷ëïU7`ð`ßj¤ÄaLÕëªW¯^ß~b?ýt»¾{X1›ýüóûÕÝÞmlI< LFÅÈa)žÿ‚ç9z¿Ýn·6sØv?²Ù6b¶eÛÎŒm_éû[É›~ÿmKæ¸:~6óí¥ضí²m>¯î?ÿ°þcóåã·‰ÅØƒb‘¨²˜B±gŠK5Ùþg4¥ðö¼Iäzer<˳ƒ  $ñhÌcöšª„­7wŸØÝêéqýÍu]yÃÂ"fû0Íxl1ŠfS„ibêXΜ>P$ý­ûíè~MµØâ-‘Ó”¨WƒÏ(pï°‹¥c¹Ž‹ÃÞ‰"Ê*™Š¢›€¾æH 2/°fsÒT¨ä£ýw€ÝÏŒ£P˜~öþ©N]Ÿ F<[ÅpØbá£Z¶½¸n~¥Bm¥nà@ofù†JÈñP°˜ïªÃ!-,d2Í«,T¢¼aAð7&ö,Ì2v,Å.;±ÄmY2×9–Ö,è$—L%¡b"Šª’¥Û•Nñ«#˜ÂN¼6o¸ÒDêråëXy* 'Soü&ÏAD#'ü„ZÓ‘í'ÿÞþ6Ò‡RèlŽëh´¾x^5¬2…¡`QipØ·›ÀE p©_Æ\Í=pȼM©ui:pNïšÔ:³.ÓÔ]XËïN7F}(NçŒæ|©’<ÛÜ#þçuæœF´žPÖœ¦ó¾Ä˜*¤èiNc~äEÌ‹(åÒÌ(bÌæA3¤ˆÒÃ¥[Xsó—4›»’‡ÏL”,› ƒZñuü¦%& _8ê ­4J*Ã+!uGÓ‰;FYÛ7s¶ Ú‘„eü–œN¸OÕM¥Ú&ü­=÷P'ÜYMÏöœQXD‚gT&ŒBCß"{c¯¢|fqš‰]ÛTC§i¬‡Pì$¨ÏQ4 É$õ¸¤W:·üe î#ŠÏ¤ =†´S ™¬ö{^²})rpZèPZ„1ŸLçã?+D¤u¿G¸Œh$òcÆ•YPò(ÍR•ò³›i,›ä£æ\-þç+Yª©n7 ¯#ÆRÉ.ÊÇ´x6ªqQ4*`ݘRT‡=Ž©yŠC<ÕÒYµ4ùËŽ4Ib.Db†'>ÏÙ·±þSS›*J(aÃî¶ÏM¨J™âEŽDŠ@ü•íy¨Ð|©×íG™ž¡>²U y3DN ÿ•¹s|>aÖåU%Zb¢äDÅR`´*꘹iCô°Fa%9ú…fˆ‚7¤Ì:§ B„N]ϲ›¡³(Áüò bÔZý=íëÒòò.oW»¿±Ã˜GòdÛK`+ö6÷âÊb4çéY(y­È÷]ô\ˆWX˜Ïy¡ä™@¡‘ôeÇFyµ•°õë‹!áÿ% ’ŽÙ‡r©vËÿã#ꊹl¾\Zù¥«á‚aŒ=FÕ¤ÊáÏ­™Qäx,"n´ýFÓê¡P¼,¸ÚöŽmb:]¢B0¬L0Õ¢L£0cZ˜ÅÄçÇ ñŒÀ ;U·KOô/ ¥’옅o´w®wfÑìœkfœj©Åâ”!í§RàG½°ÍfQ•wPq¡A-¬yƒ €B¿Œ<†Psèà rÆîÂ2¼2L%¾ry5LM¼³5Mˆ Ö¨V˜!´k'*Åž6¿;ÎúGãˆͼjL•¸ãØÕ)f¸‚M(›™&õ%‚r½@ëâk:®ƒõÐ…<ƒ$ÅŠJ`¤–JQ•xˆ³?‰]×Ùœ{-ì±EƒM±w*ôþ€%â8ôަ­ƒVÐkeӑΛ Ÿ`ÌiAQ;#R3þ¢ýhW0i#нðRvš=`+æ-ijû\)ÑøZüïËYGè¹Jz^ó\Äëp}cqÒml¾ fBé,ëÉ ñL!]W¼…o¹MEoN-Ê*"þÀ—¥‡DQÐ1JÒ,Áïô`ªCõ{Ý=)¸xÀ «$'#^äZŽÛ(@Î6‰8C&Gý°õ”î4N‹^R5™ú°–8óÓf»¹½oÙyƒ§ŽßŠs+Át"v”‰èóX†9 PYû™ßDR°&!$‹u8ï æQ>=ú§#®\šv'Û ec)Ì!šíß–¤«_Ø%³ÓÕ•P!²T )hWWᢀ·Ÿ6SÇf>6“>€~àÚæ•6¸Fæ™bcõ8ÚUθû‚ýê9ˆAü·z“ÓßuòõæîS£ “éRË2®wRVfl>¿ûý‹«÷›ùÛSKÎ;¾ýûhüS¶S…ºâ]­£F!õòØn0vòùòtÀ-½“ ËU¢Ó ÷…«$ûÂ!/ºôàª>4Áúñ+(ƒkxáBet·£ sÞZÑG” Ç6%ÔQ²OÕ#Uò¦C$-³zD'=ãèÃ2JÒÄYÝß)¬ °βãÒü³ë6ÄÂo4HPÞTeë˃¸ìï|Ë÷ç­ë,Ìì[î¶JÐyÏq·y¾•žþéx·Ox7S‘wEüÆ~Y¬Û}ÓŠ×À5óÐwîê}^oØ…?‚‰ä8R­V?Ò¢ªÕtЩEª †åHôÕá讈¥>ñÇúò§÷;2MfãÕýçÖl¾|$jãbOõ}pãMߤâ$<;6m/I®[sjƵoÇ~BüDšñ E)àÞ`—¾Ìqe™ }ÁFZ2×›YÞÂq®»H´í²Mþn d™Á /w¡³lÖ¿š+…±E|ÇBuÅ£#R]ÇeLFÅe¿~vRJæ7ÈE@¢Ôñ··P¦ÄB½Sêä”\¼…[mGû²o-(Ü~`×ÎÛ¸øNf]¤öÒ¦á]O?[8Ö|Þ‡xñ2óü® uåÌu=+@Ð[ÿ?™ãì endstream endobj 21 0 obj<> endobj 22 0 obj<> endobj 23 0 obj<> endobj 24 0 obj<> endobj 25 0 obj<> endobj 26 0 obj<> endobj 27 0 obj<>/Subtype/Link/A 26 0 R/StructParent 10>> endobj 28 0 obj<> endobj 29 0 obj<> endobj 30 0 obj<> endobj 31 0 obj<> endobj 32 0 obj<> endobj 33 0 obj<>stream xÚìX]o7ü+òž™æ7À€-Ÿ ŸlKtO‹}ØXŸyWX¯îâÕdWkøbkÆ~pm“v‘ÝUdÏÈ‹é·Fzg¼3âr4Þ }0>‰üød¬h?Ãf¸c]_Lèa³5AŒõ%˜`M‚¾3¶dô½q’¢ Á8ëœ Ñ8çà—Œó½7!Ã&1¡$™ØÃÆh¢½˜hKã6e——3úÀ+Ø|^)ÞÄl[L,Æ ð¶*ÅÞJ4ÉÂ1IC~òYÇš¶êƒÅx‚EKúèÐ^D~4ô¿ ¼TÐ^FðxûÏÀ+èh躸Í[N°p–ƒE23RàÄ™ÒÃ[ Ø)HÇÂT€BìÅhj#’†%C²˜¼§¼Œ$à Òz,$pнKhX4€§4GÁž4/Ñö:9 u22žÐÐT'¬¹¶:Éô G¤G#kÈA´ä €¢„$m‹ 9F§ Ǩ Ç¢ 'EDLŠlœ‰R 䂞X $O¬r U ‚Lê*ìHÖÉ ´œÁ«Fô™@Ò`Þë•€½ˆ³hÔGdC¾‚¬§¤^È ŠSÞéC’³ 4t-„”{ˆ\| `¿üÒ]}¸ºËÝöþÍîj; ›Í®;ÕƒÕ›‹îèvõþý«Õž0ퟯ¶ÃºÎÓ³v8òzøcw:|@fº‹ÍíP½‚ÎyòËœ.lÖ{al¶´Ó8°Öú6Ò&>ÇK³P]³Ä+Ä+ŽÖÓÒ¯¯p¨®Yâ%â$â$â$â$â$â$âxöýØ'®'nà>ñ#qýýýý"ý"ýç9Îsœç9Ïsžû÷\Çò¹ãz–ÏíøœñYúÛq׳\Ïr=7âûjx¼ó")ÓÒOšò/ä_È¿dOh‰Gþ%ü ùò/ä_È¿!ÿBþ…ü ù—‘ÿžqõÄcÞô<7K|æM˜7aÞ„yæM˜7aÞ„yæMƼ9â9â9â‘/!ïBÞ…¼ yO<ò.ä]<ñ-u*Ô©P§B u*Ô©„†GšÈI"G¤ˆ ‘ òCzÈÉ!7¤†ÌòBZÈŠYéeÏ({FÇÛA‹@³ ‡–Ú¤4©L 3·(Kž6zžÕÈÁÚb‹ ,60æT1—ݹiìt—ÝåðfW¯À×›í»Õ-î³êØ «ë›õÛŸTwú2r8†7é?Ã[„4Ôg›ë¸Â•‹±vWŸ\½z‰1}d´87ïëûwï½U`ÆëÊPZûkÃ*!J!j!é+QM–¡ªŠ¾µ„›v¡-ëjg÷»Û›5*ÊÝjÝýk}=l÷Ý–€î¼Ó}?ÛüÑ=¿ùOw¼]½Z ¥g½Ù ˜‡Ÿ¬¯÷˯P¥ŽoÞÞo‡îÅZ!†®ÎžâïHí mèÏ1GŽ9ry7lß¿ÙÞÜíÚv.ï=èî¶7¿›{vŸo7wG«»q…ãÍííæ¿Ãõ Ê¥†ô{›õiwÏiw"éìNìC&»÷Dää}Ýù“'ÿ·\ZžÄ½ý|Ù´<•{û]Êçr1³~à±…”¢´a,¨\ˆŠ|džXaåûÙJ  ±ä²tìíw)½ËÅ—j¯ðŽÛÛ¢#°¿JæËÙÞN*ÆËÅ]—‹‰åx¹`YŽöÀ|®:Çx`þ¤H‡Ï•ìúÒnŠñ«èìÙ?/º³_3MTÝù[€VWµÂ7Tø—¸ÿù‘ƒúFå,»£Ã;~üªº<Þ£*wß}m9'(eEN+ÑW2e?­ÈÉÏ@¨9¹õÂLvB½/“Ì@¨×eêg ÔÛ2–µ"Ç<!W„4¡T„8GQU”q–(«*ãUJ•eœ#ËVÈã]¶Bç³ò8G™­‡9Ò”ªÍ0G›RÅæˆSª:Ãu>¦ÒÓï h³]ƒ endstream endobj 34 0 obj<>stream xÚì˜]K\1†ÿÊ@ï=“™|BZ½¨¨TÔ^-^H»AÜ"[¨ÿ¾ïmÙ¥^tsZ(Ô‹³“Ì“ùJNŽ!Q Ñ$4.ÁðÄ(¡4´IÌÚ,Ö8­ˆC±JTCÛ$¦*!©¤$ÌKÀæ%—\1/E) ü”¤*çg©Î¶HÍl«ÔƶI3´Y¥‘›ƒ´Ê `œF XM ˜ÈÌX/JG©|±®¹´Í.Éd'¹€ì$äBÇI.ô˜äÂ5Läpê“EOUš‚žHÇý.4ªrØØr%¬‚Üœ=ø£ÕÂn1M¯b¡Onh!ân–¨ µŒƒ!Q,1q KÌTCÆ2CÞ@.¹0B¹ÂTS›ACAn¤"§®H‚Á%Wš QA¤u"… ¡P@1˜R¨œBƒi¸Bà:„C¢OH {wäHr9’@Ž$“ö@ ó¨‡ô,­|5½éç§Ÿõ“àQñÃݧåýíÍÝò×åÊ ™uP¯é¹ê…A½Á´ûPÚßO'׫¯ë‰c‡Ë«ûëõÍêîòáËr#wÛƒ«ÛÕýB÷Tžž«'ø"á‚¿cÇöêëëûõÖ»[cãïéËèËè_=–¶´nl±0¦fcj>¤¶ˆ’xó¥úxó­úÜa¼¹V3±Œ©Õ1µ6˜µÑlóÎKÊn¯ð-„uDžƒðŽHs±#âDꟃÈas¥#ÂDíƒhDh›0íˆ:Ñ«SçT§õêÔ9Õi½:uNuZ¯NSÖ«SçT§õêÔñê|yÅþ'£ä¢\u·‹òñ¢â·/vzpŽçè—T`},Àß/Ù¼qUÀ×k¿*xúqUðüÜU¡ÝJ6µâVÒê;Ùž'ï.OO0“ ›€­îm@Z¶iµ!­®4uûF3¤Öß¾ãñZ·/1»«ý3»•ÿ„Úm·î𑇾/òÐçEÚÇOZß©â@ endstream endobj 35 0 obj<>stream xÚìVÛNÛ@ý••ú\¼;{——JE„Aª>DyH‰A‘ÒY©Tþ¾g67; /E}áÁñììœ3·ÍxÉ(!–ðÒBI‡·Ê±Ú Òx tõ^è ð°ÎDa%쬲JØð&á4ø¬.x¼ð†×Væ·NØ’õ"¦uÑñ:¿‚ΕôÐ@«±† °o‡IÁ©CÌLÎB‚J+øqÈÈ¡ {p`6‡<˜ ƒ]YŘ­GÔÌ)lÏés¾ÌÞÀ—³ðåÁ˜‰5`ŽFs , J©X—„Ã\Ê‚·R@U‰–e¥¤A´B\@í7†5‘[,)r8Á˜Š‰ýÈis‚1«h97 "'€JPôÃÛ0Ðhž–:⇎ÂYеdÁ@°,X¡I6„€ ´ÄyÐŽ9‚ÐF1ì¬f(´ýÖ¬õ(7FfFÑ$%‹‹vX§#'Åuq•K7Å·Ù¸¬§“YY\Ý ÇÊ£#ÆWœ'ÕøñÉ~ù{×ÓR·Q>õµè«_ó‚·ÎÊÛªÍ'Õ¬ÿøP6ÂkožVÓªÈ)–Ïp¥ÿÚõ^a, ßäìòôÏù™ ^ˆÂ ø@c1Ü;9Õ(‰bt½nPJõ¥ªަMÀJÓ¶5lm[—R«Ô×0+ \3؆ùó;`öØ ¤Ù—@Œw;ðôœÛزµæÙÔ”ÌêŠRy°¼3 tÌäÁlÞñ¶ë©äôª?çËž.Oʪõ«›Ü½5=I”>“Ol®çÏÍ|TÏÏ1qfs|€dš9ËõGVü/síöÚ½ÀàÂÉëaõ¹ÙCÕ‡}S°–z{XåŒ8ÊBé,”ÉBÙ4ÅcþO£Ì‡\‚×´õ妣Óåh<™Ý÷juº©ÆßNù­^o h7ŽþÆÑK_€¦õ÷¾{ð}fñÿã ÍŽ»GÛ‡íâ#ÝUö·öâÇ­y¿YÇo_Ž?}ö)}:˜S7sÝÍÜt3ï0öÛ@— ä ß;¹-Ž8èU1q¨×pL2ŸãíFËž»ÿäÚÂòÚ¾=z÷DÒoáÝdzÿ^OæØ¸¬ÆeÑ«û?Z¥‚‡âÓlÜXq<ÇÓÉýlaWÜ<ŒnË“ò®ªË´ŸÖÇwó²^›oÐïžß=¿µç? /Çÿq endstream endobj 36 0 obj<> endobj 37 0 obj<> endobj 38 0 obj<> endobj 39 0 obj<>stream 16 Bug, Feature, or RotBob Supnik endstream endobj 40 0 obj<> endobj xref 0 314 0000000000 65535 f 0000008561 00000 n 0000008794 00000 n 0000011664 00000 n 0000011919 00000 n 0000014527 00000 n 0000014765 00000 n 0000015020 00000 n 0000017100 00000 n 0000017333 00000 n 0000020465 00000 n 0000020700 00000 n 0000023374 00000 n 0000023620 00000 n 0000026138 00000 n 0000026384 00000 n 0000029179 00000 n 0000029425 00000 n 0000031956 00000 n 0000032205 00000 n 0000032229 00000 n 0000034534 00000 n 0000034770 00000 n 0000035124 00000 n 0000035309 00000 n 0000035541 00000 n 0000036017 00000 n 0000036079 00000 n 0000036243 00000 n 0000036296 00000 n 0000036508 00000 n 0000036706 00000 n 0000036805 00000 n 0000036920 00000 n 0000038810 00000 n 0000039824 00000 n 0000040846 00000 n 0000040881 00000 n 0000040905 00000 n 0000041018 00000 n 0000045029 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/ctss_hardware.pdf0000644000175000017500000015230410345704031014717 0ustar vlmvlm%PDF-1.4 %âãÏÓ 490 0 obj <> endobj xref 490 15 0000000016 00000 n 0000001107 00000 n 0000000596 00000 n 0000001360 00000 n 0000001621 00000 n 0000001969 00000 n 0000002522 00000 n 0000002558 00000 n 0000002780 00000 n 0000002857 00000 n 0000004948 00000 n 0000005434 00000 n 0000005667 00000 n 0000008337 00000 n 0000000919 00000 n trailer <<7127dc2bd35e9243a737707f1e8f7b48>]>> startxref 0 %%EOF 492 0 obj<>stream xÚb```b``áe`a`àð`àe@+sLñæ½ðfšÁ9A¦-D`QCþ±íå@°¬V’%DVY¡«yIŠ_HO“ßÄ ®Ž—Œ8VX¹9в{œPZðªdûNƒl †Ðf B 6€%Ò€˜–…Åê?in [¯ÂÀÃ'h¡ÀÀ½@³¡ðO”CÓQ†&ˆodš–‚,f``\¤Uf+1€}Ï` Ä -ž@šhþM ÍÆÀ#wÝ Àuk5J endstream endobj 504 0 obj<>/W[1 1 1]/Type/XRef/Index[44 446]>>stream xÚbbR``b``Ń3Î ƒÑøÅ£ñ1´Ã¥qÐ endstream endobj 491 0 obj<>>>/LastModified(D:20051207200911)/MarkInfo<>>> endobj 493 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 0>> endobj 494 0 obj<> endobj 495 0 obj<> endobj 496 0 obj[/ICCBased 502 0 R] endobj 497 0 obj<> endobj 498 0 obj<> endobj 499 0 obj<>stream H‰¤WMsÛȽëWtù0!Ç3ƒï¬ã*IÖf•]gU%VíÁʇâD$À€’µ¿>Ý3AR)9òA et¿þzýúã |úôñëåõàðùóÅ—K8ûxyË!¯ð úU^œ Ðøý?ñû‡êìbzöq:å `:?›pÆ9`šƒ{øõ3½7­@púý'~šãÒštOQÈÒb2z}uæ]Nooá—ÌÌž3£`4ýïÙÕôìê+!êPŠ¥C!vQø‚`У „hܵ¾¢”³ NÓ„Ü}ó.Ê{¸Ý¬ ý8š„Üys=¯`Ô“®tYŒAÊÉ­ZO$çáÝFÿ™þ«‘ìEÄ-”^qÄ¢mÚ ‡âôÄ)·a½"É’„ ß:8_.¡œC½Pð¡Z«\ÏužÕ_õ4ywFÃ­ÑØd ˜a¦žT÷jY>Uf¦Œ~R3˜›reíŽ&¾+^E‰,7&W”Üd›%Üh…ŸÿR¹üõjâ¼MAš¦Îi¥kYÑø©6æI?é⬧Y™“¯ÍJµ†Á®ƒ8më@r¡«&œéB½ØH–úQ‘™åHx/P—$è"/W륪ÕO£IâaËeg>h­û¶“Æü.~o¾)r–-uý› vÿâÀç¥ó8ÛäjƆZ xocIÁ‚„ûÇ+<1@=vyL HkW0¸.jež²%Ôz¥Ì£hþkaêSÇéQäñÿ1úa±6<¬‡ßJ7˜ ]QÅ¢FÂe5†ûËŸŒ Øö‰Rk.d~J•µäü±À‘Ð5”OÊÌq0ª1ö¾¬«n¢D‡ÉoF*ŠB.#c¸×5rä ¶…@MÈÇAgæÕ°Èó Ûj“­¡Ìó©p°Ááæ’b«2šT]À² 9ÃðЧL&͘x4zÔ÷…úŽqUm6¶¡Él=büv0·†‡Ÿ4v÷Ç„QWvu±Ð˜5ÃØ+xÖÈó¬Î _dE¡°ÓèNJòÎI ý„Å¢£ý–ö[Ý’Ê+«2À_<öÝz¹®![V%dëµÊLåúãj{ÿIï>‰ø3äeQ›rYÙDëf¬,Ñ$RX¢‘’ùAÛƒnø(1ØIìœõrEYC®Léâ'—ÄÁzOœÁ}ÞÝK¹-N¶|Έ _*xÈŠ‡‰*²û¥k«Ë)ñçí-\‚¿³8A#(ž$G‹#Ž  2H|LQÂ)Ò`ÓÐoqÕ º—oÞA" XÂåë<½y:Ü> endobj 501 0 obj<> endobj 502 0 obj<>stream H‰œ–yTSwÇoÉž•°Ãc [€°5la‘QIBHØADED„ª•2ÖmtFOE.®c­Ö}êÒõ0êè8´׎8GNg¦Óïï÷9÷wïïÝß½÷ó '¥ªµÕ0 Ö ÏJŒÅb¤  2y­.-;!à’ÆK°ZÜ ü‹ž^i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= €x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±ZL®Ä„?â_øóyxg)Ë”z¥ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ßÞô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ;ÂA4ˆÉ 䀰ÈA9Ð=¨- t°lÃ`;»Á~pŒƒÁ ðGp| ®[`Lƒ‡`<¯ "A ˆ YA+äùCb(ЇR¡,¨*T2B-Ð ¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 N'çÎ)Î].ÂuæJ¸rî î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 503 0 obj<> endobj 1 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 1>> endobj 2 0 obj<>stream H‰œWÛr£H}÷WäÛ¢]«¸Óãq„%»§=ÑötØtÌDØó€QÉb‚_æë7³¸©]o8#$2Ofž<™uú ÎÎNo–×—Ààü|q¹„“Óå=ƒXâúg'|þ >–'‹ðä4 p×':3cÞÇ@·Ü |¥÷B œÑÿ¿ñSX7•=üç:F`ƒÇ|#ÀwM·'ÚC¸ð$6ÑËLw´$/ _CYD; å&*!ÏÒwØæ«dýåìÏðׯµÛz6}Nž¹g8®C.W'šŒV«BH©¬áG²¯y±‚8ʲ¼D—°«*+˜…5FíÖ(™½1ªsƒÛA(Ûë"ß*“2¯ŠXHà3¢od†ÞµX _b  -ÚîR!çD܇w Ñ–áý=Èd[¥Q)¤²«’‘ä3ÝÖ xz´ŠÔ©AÕ†û ´8ß½'Ù3\è3ÝÃO…€4£2A‡ ÊzïáT}ƒ{lõv”­sYmÉxñLwµJ™ÆÆGµíÆÌAæÌ&U¬ÓŒÖvy’•ýTÄ©ˆŠ}AœŽ_A[eËr0Á > OI gÖ'ûó¶& m&Ê= ˆY*Š^Ü|Oݺ^zgûôJÄ…Ø Dù¨gD©‹}ÐmÙ‰ÐM&R ÉÞCzX‰™«TÈø³Ævž\ÝP/îû“·ýyÐL9SŽŽú˵ê/+ þÒ¹¬šƒV’nzƒL Èl·nàåJ¤Tšw"¦N½•Ÿ ‹úyŒ•:3Ùy¯]»q›™Ìà>Û§¬íݸö€†¶Dn¼˜U“Ú)µ¨íÂÏø^ÏøQzgý ¼‰l°Îñ}”»0Ú 7øI¬×".“*$ÞJ¡ªÝY®+®Ý*O¶’˜“,)“(YҺƦ©^h )ÂÝ'cH–L'¨©J.íÀ;Œ%ìùmâ9‘¥(ðf%j³óÔ³¼ç¬Ý„ÂZÍR)^å(J$—d÷Y¨Ð.N EŠiQÍG-ß¶@ÓT-Fv$SZþ" ìýWcŒ–Ö8ÙhˆŠŽÇˆêž;Iu{‚ê{úTW>Øî[ʇšVuðÔ·¤=’'²Ë„4"àYM:!IÓJ¢Ô•8r’ м*‡„ÎTjä.ï©G’ XÞ\ýqÎáu“ÄÔjRJ¨v°Û¼x‡µÒÖA3­º¸aÙG5)DY±ª±(ŠjG+‰´ÙmHãLˆÊVr'ß‘^8#ùoêüf*C "þ¦r¿DÔŽõð]žh]'Ƀ ¶°š–0°&ј[¢©'Oy^Ò;Øaz8ÓWÅ î£!<Å;wØð^7 3Ž„æ\íFšeÀ·"/›LŽxñÆà:°}œË¬±?fÒŸh˜ãb ùðlw ’6tÒ‹€*HkMS¬`¹ÁxÞ@ùš#ƒ©ÌI×>G}‰¼eÝr'·òÓÞPÔ%çx…kxa;í‹ÈEÅiÜ&QîÕ>pH"Ýb†cmV§™îkQKê¦å ÑÍp²·ë‚5Fçyðq¾Ù¶K|³§ËÆÙDÝ~Xó¼X&‘ÏòqøNÙºÁlºüàr¢Üà`@ :Ӣͧ”ãy8ä…9DB×V^¾·e'¢fUO(TFYŽÏBÓPv›QáÑÁc¸þTºë¬O¦±(&æÙX~¬€¨ŸÎÏðTRßj-{ÑÈóëÅMd´ïh75•£3Ç}„:ú‹Í¡n¢Üã ÊÞ›³^’ÅiµR[ï ÁêÞ˜=ØWŽŠ€ØnñN<‹†×ûvÞO*¬GÝÏÝ–~AàvEò’¤â™°Uå~Õ%i8 È ?èÂëç%ÜTrŽ~/ Jq±­š“+˜ûà †Ë$Ås‚PË)d‰KžÀ%¦€2Ù ¹‰Šdæi˜-„]TÊ¡œ¹AS ßn¡fÏ$Uk²…XÑl¶7›=Ït[S£9«q³£MT¯ îñ;Q¨.ÿ ’5$ûäÍû–'Î{åx #X¾^×¹ê 5CdÑS*ãjƵñ©Ê‡—–ÉžÃQˆ=groºéFöÔgºvšiÖÓ‡-ÅS œéúÚôiÌ»9<ÔGCr,ÃqMkzS0GƶÑj@gM‹6lÓªW^]YÆ7y_j®ãôj§fšÂõéo‡"ü¨Ý]ÞÏá÷;¼,îïèòy÷—·s¸ûN—«ßç Êøq6Š~x5˜DoÚ†õð?ˆ¾;{E°üò6‡¯êz¯®Ó€G¶€)À,0ß4ùÿ‘nìóL®±ŽP‡WŸix·¤ëò7u½}›€=±bŒÀæmxîÇ`ïðL§z›dx'E†³öQûó\f†úJo>L€ß#FÁzáû&·?öË·;èKx7ŠdâÜ7†û¿e¡çÕíbÁÈ™p ­¾É¼ ¸¿º@ú_-Ôø£ Þ}½»®…üë·ëQxR= iÜr Ï4™3-iÃ*=²ÙÿèO˜òÖ¬W£[Á¨Ð\-ÿ!áUàÞx†Ãè\И¹ÚjÞ›Àõ¦ÁÃn·a•|þ ¯y•®uä™&/Èw´¼5ÆÂ°Få~|Œqæã45yðc‚þ#Àw†n endstream endobj 3 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 2>> endobj 4 0 obj<>stream H‰¬WÛrÛF}×WLå%`…„0ƒ»#»Ê–]»NÅ—ÅŠSåäa ID¸p1€¸Ê×§{W @líÊ‚H£çôåœ>sý‘ÜÜ\¸}ÿ–XäÕ«7ooÉÕõíE" à?"£üŠ’>ÿ|~Wo¶W×Û­E(Ùî¯6–iY–K¶Á'Û&Û3¾µ•„Zøû/øk[ÊT4øå¹fèß ÌÞdd›]Ë¢Q•9©J~’DòAª£ o Ïcòúú Ù§ü I’¯¶6ÇRÚžËÔ¹ ©‹ÆW†zÇq)¤T1b•"yµ&ðÙ Ï{éãš{‚1­.Ã`›&Ú†šÔ ÃPM‹ˆ+˜–ÍÖ$J/I-EI²"k"EeUÖQÕÃôÚÀÔÃÀ40Ý%ÆRàxÅI&²¢|$UA^“¨(! ~%þ+¢ºÒåèƒ:Z7Tp›°#¸-<%ÉɽmªXï¶Wï>`Ïû9 íŒú¬‹£à_tÒó|ÓŽêä\T6U§ÂºTœÉ\Ûôa´ô¨l¡ i"+ìÚ©L’TDL™ÂŒHhvÙ×ÊïP¥¢žºM¾01Ù—E¦ |»½»#;ÝÊÕ&0ŠPqyODV§ºx«?¶?M#ß4‘G=(‹ºJrèåù˜DǶ›0’DNtÓRz‡£)"&¦QÕo•Chz &ª£§5 ;qäé£ð&%•ÎÄ`Ú | :F*‹d“±ðE¡’ž’ü€§äBVpøoïF¢vƒÒ1”Fyû½„¹Î yUïŸD Ó~ÇÓê/`Žú]Åó'3ÞBÓ‘FèÞÖy%òÙQ¶çGùbÈ,7 p=º8ÈΠ۽¹“¬ f/P‡Ø&#4é?‰ŽX‰"Îq¿§êpßBÑõ½Å ½… /wê×7Aú[ò:!‹qT@Õz‚Åýº•;`kÆû¦÷Elä24ެ9׃N˜ë}&;(Ùýjã8ŸM˾Ž¢ö *¤í<9DŲŸú²£ˆ¹ »G2¿q(…0­–WÇRâïÃJ”òÅ\ýg4…8ža°Ø¼à9:ØíÔ…é¤X® ª½! kì¸ʆù®‹=¨™T½ì›0¬õ š®Iý®D¼ÂÊâÛµÜÓs)…ÏšÇ>'‹ÂÐSÊÆ9‰<~AàÙ ÿŸ9AÔ¯I‰ZÿS›œ Š]f„“?™ÒOküÛ)C‚çU`¨&¨Ló:Ûâ)Jü²M!Bí×u˜Êâ­‚‚î;¦ ÐýÅ)¦Kv¡ã2™>ij€*Ôv:ÇPŽÕu@sä꼫¢Ž„-¿¥ /aËÈa‘iZœç‰NvÐlYè°å]l"ú”S±ÁùùµC=;HjDzŒŸ?½'¿?X®ÇöVõˆ?¿¯Hû-¦;,¡ÚÞ¾þÇ Nù«ù q'1Ná·Mƒ¦e\×p9s¸”Œ|="okÜÅLC,Ït=ÚbS?êì•‚¤×O †~Ô`º;Ç¢gì;d&@Ëž›.-ƒ¯ð*¶˜¶G]ÚÑH-ï®:¤8‰’+‹SÔ‡£’šRL¸KÐæÞã½\"¤fÚYÕ¨»›Ü¿Ôio=:èØssJv;éïô5j´w©%¶éù=ÄvIáëCX<=óGIÀ¸7æé¡(“ê˜Á.˜—ƒée¶Ül×2}zË®-­”YÉìVŠmÃÕÅ£¾=Þ)jÊ“ \—®D’ã)ªã¸0³ÃÍfVÄÅNÕ¦l ³qЃ1* x»²à2rƒ9¿¤Øô*é.*ðýÌÑVˆÓ‘u¦K¡¨?wò¬ôO¶™….ŠLè.·yéþñÏ›¨³Ä¬Æ:üÊaã×RSZŠÿÔ"”iG8$u*qÂáàfcåþ­AºTwH¡® æû¢Ì ûy+´i"…ö‹±ð¢ê7急¿—k²««Än! Ÿq<Í ½Ù˜h)¾—$/(+šP€¼WÖl<Û–vÅ`ÂöVXf:)\¿+cÁˆ5Õæ<=Ýo—b˜>Ú=ÍsÔ{Ž»ï¡aR̯[»³ÔuóÆzá½"7/•tôólSŸ±_©ëƒð2/XÌÕžÞ¯½™ŠìØ8.¾§ó¨Ø>QlÛÁ‹È ŒzD’ÀØæ+ªÈ0ÔÎË™ñÁ­¶ÄQ– &ãPò,C×t¨“X¬5!8JZI2 ÔN,r[‡™(¾KÕ­±¹f…Ö­t ·¨¢]Ī؃_?p}Ðdµ±A¼`iƒ@ˆˆÄ'£­¯Y ëcÓÅëQŒZ Q˜ë}£Rç1¬8!D)ê·‹B"šژÄT±©§NbÌDµÖÕ¾tÐMÜ0'½7Pꋺž(5ÊâÖáF•~¿W¨T¯ÎàSûâR¬b«U±aZ8 ÄZ¯eÈ^ˆWÆKâü<¶{”Ô'lgRãó»Â6“ Â‹ú”Ó…Œë4Æ9R[8¹ŽÉƒØgdÌRÒž±€K”ôàMÙSFþ-ÀÂü endstream endobj 5 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 3>> endobj 6 0 obj<>stream H‰ÄW]›X}÷¯(åe°dèû\ØÍ*íÎDY©7‘ÚoÝó@cÚf‚Áºàxz”¿U 6 ÄÑ>¬ò`›õyêÔ©›/ðë¯7÷ËOwÀà·ßnï–0»Y>0ˆK|@ÿ Œó‡ŸÄç›rv»šÝ¬V 8¬^fÌaŒ3üƒù*ðë‘^[•À}þ¿V¸0æðÃ÷œÐÅ'dŒ Xíf–ëÀ§›Ï0_ý9û°š}¸§0ºÐø)´Kׯ)¹ëÛöCÏAçܘ3*Ú”ã]Bî ƒ€;ÿ¨Œ‡Õ6åêáÊײJvZ'å¾È×i¾ª€ ÿCYtœ”&cØ#Â;ž¢H1õ̪¶QǶѷÒÒ¼ø$„ÔÉvQ¼Móºð¢‹]k‰É6Z’Q»µjs‡»a’ñGë‹.þLâ îßÏ•µt>Uè©ö2ÿcõï&ùΜ ‘3'p›ø^Š,+Ž”u+.ò—tsÐQ•ù?Æê,¶yžt0x)&»ç^Zåo»×ÙE|Ã.s”Ë$Ù}´âm”çIï1p[¸ŒYÊgjq¤× “hhˆò5ìy¼]@†¥‡½Nó*Ñ‹ºZQyƒQ½D(‡ iB9Ðÿ  Šö ¬uú-)°Üê"/²bqVÄ_Gƒð¯ ‚KÇóûå¸í•CÇ0êR]çÒ Cü`R]ú\v>C.p2…D^X§åWL›ƒ’‚a‡Ý¨ÿàJÿïÈÀüÜÿ]ëÿ—uªiBš¿üBÏñâ›æ#ÚgÑëhá•a(á`uepƇ^”ò‡‹Ýî§±™±’â©4!‚s,ήŒÂ qš³.ŠßÛ(ù¡DÞõïôãz޼Éöc—­Bx²” Â=Í©ýÔð÷°M7[»Ü'5}|˜ÊÙ% ÔìâIN£çN³ &­š^¼= 5Ä_W27lÇÁm¨†²Ä©zšúu’,=&©¸žšNÇ›H§£{î‡C>Ü tTÀ|·]vgÙfÃ’D˜-Sžö“¨×Hàø¢^#VE䪑V’2Öé3v8ÍÍR ~Á—ã¹íYéÜ–Ö>Câùür¾¡ü6b£6ìÚöù²³>ï“z/áš3»9ÂÍEÕÜ.g²€ ¯ÝÂÇz%>Gñ×.´t»»,4±À¡ììô±€PÇgµ™„|¢%$X™¬Ú‡ÍÖX6% &¨×·çy™N³y°Ø9yö Ï ˜éĺÝ.ï`W¬g´ýþ0ª(l1-ÆCpÝ€ ø5ºFí« xµÉ o]Æt Ä¡lv¾s¦qÒŠ “HÛTV [Aå«›§'uÈè: ÂÞ àR>ƒ BñøF&õ³É#ï«.kEm†ÜVfr˜ÓŠCÆ›|q‚À㊠kC±Ú¢:¤ØÞbµËJ\ŠX»1×ÖYL4ÒÆ\Oj8Œ¶>øIÂ’¡Dþå¤~§+œ@”j ?HWR…Z.4µH‚wêÝISŸç×"¨ý¬ý®´Õ§*Á¬“ê q 8z]BñrVñ~hüƒ4¨ët“V%lÒoæ6 2=àµWµœoßuÇP¯H¯©*Ý%ä÷5‰4®”cJÔRcôèi>ªÅkW¿Ž2 H#ós9½]İÖâm`y¹ô.50•“4À%Ý)áÊží÷5J/œÿú¦¬é×zB ñ‰ÒECœ‹s"ÞF#ÞF“æDtNA|/Te†™ê?|÷Yµ…æéhhgÑP|t…a|Óc#†åÃ5Dl¼ˆP™Ë´#bSëÒ°Óyõ )sû9ÂôO_‡8‰a‡Z‡¢TפÎê÷q¬V#ٙ뤉tkŽm-^ D´ßG4C¸£8FÄ {AJË’hoF‚„³&eŸ½ŽnI1,’ƨ¬.˜:¸Âü Èðþ½BMÖN<ÏñW^#(E+(o((ÅðjžNÊ儵`Z"‹aæ¿k"0Š'ü¿-ýNvIz÷|㟶ý…‚ý_¼ß,øÑj/¼ñ>¡Çã Á'˜;Ù'9LùWðÀGð ¢q>Ù‚oIàÃCu|’ÿ|R\ ŸàátRßÝÛãBŽyñB³dýSVx¥)ãý=ÍŠât‘:Ÿ îÑ´\Ú/BÊé´¦ø»=zähVxÛ" j>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 4>> endobj 8 0 obj<>stream H‰¬WÛnÛF}×W죈ôîry ’<ÄIo€Ñ"Ðvh‰²ØP¤JRQܯïÌ^x±HJ‚+¦D‰sÎÜÎÌÞüAÞ¾½¹»ýõ#¡äýûoÉìæö %« nà©VùŒ‘îÿ ÷ŸªÙ‡åìf¹¤„‘åfFÉr¿Zñ§ËŠ0Š×ñVI—&àâ{N$H@C'¢”r²ÜÍæÄZþ=û´œ}ºCЖ3Dz@6uàÉ@ÂÁ;!æK?ò`À¤}á¸'ܸiõm ޵~`„Ìq#˜tÂpB4NxÃ^øãÁ·B",·‰rbN¤‰÷û¸Lò:{&1Éâ:)É®X')6HFaxƒ 9n/ti ¬k0ÊŒÑ9nÓÕ–¤9©eNŽq%ÐZk»­Ù9›9Là´ZV«¤ªŠR>]”éSšÇâ0‰ã@eÀq•®Èã!ÍÖˆ@m*ƒÁ™„žæ™æOä1+VßÀ7i´JÊ´¥Å'>:>×&F Ÿ€Ð²d¡jŸÃŸë]éK!ë{SÇ —{d“þHÖ*{ˆ8„¼ U6Ph Â)šPñ݉ë˜9y,êG¸ÌÖ|Kªt ><ÌUOT‡rCt,g¬äÄ`É÷±E„/‹ëS ¼´m™÷uú×P uAêB€,Z¦~Ã4”1g>¤«-,îQtN—[¨ãÎ Nâ|-«ý }(¡ª2c»—IJö¬(°Ç´&U]&ñNWØœtì²¶ç$í MÒ6®ÉíòËr¨Àñ-ÿ±ÐuS‘=´×~û % H/¸ÚÊL—Þý<ÉWY!ÅšgáçªNvmõËöæËó2©dSX_—¿ÐUʹ㇨kTa”à|]Xþ¼Ž±íááCÙ0~àÜ—ßë/¥ÝÖu»1Ö +<‰ ç$¤wBÎ]Aï>0оYÖíhøØYÁ„\âïÁ²Í¥ýîáäÜg0*›ïý ùƒæz÷Ë_òCrÌaƩС¾ã†L°>{CèA>¼Ãg ¯Ïw¸¿ÃL'Á/"áFÜ ü“˜x!Ž†ï†£¹v%fœ†{ r8äœŸÒøŒÿ&hèxéQ¨H=Ìa…£èT’ÄeÄàâA’ÂSbwÍÉÎ"×,‘jO;-ï2Rw(?‰–iwœ+*’˜*â7PÚ(„ 82lÒ,›bá²àúø7в®ÂYJ·ì½Œ¼,‰,\O¿;bžÔ[)ÉV·ÞGÉ—…„ >Ìó€fž0!¸ˆBxX|8gÞ(¶×1ˆ.bÀ£Àñép^™>,·' BáP1ÿu1à—©*‡c†€,C^É€_9ɸçcŸúlr’q÷»)‹Î×ú„[Ò.ÞïSX/å¡%+žàĘZ|ÞªLô¢U&êj¿¯`¥’{ìËæãa׆‡˜’`zCçÃÚ×èƒ Äp;8Ÿ®É»¥•¸{hHŸIëþ¨—Ã"8ä¥a—P°pÚËa9»ÔK†ì©0^RbÉÁcNVÊOºÐoôØÑwÇ=§p>Žï33 áWCèìZôsêfÐ}†â6­Í¯pžÂ»ç¤ÍÀ‹À! ;FÜ+¼Ÿ€?§kÞubß_\὇?·'x8ÝÁ©74;ñ®ð~þÜ~hàaYöÃŽ÷þÞ{ãðç¶@ ¹ã,lÄ%øÐÏ­{–cB¯·«ûuyØ‘ð ükŸÂ ¹8 endstream endobj 9 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 5>> endobj 10 0 obj<>stream H‰¤WkoÛFýî_1È—•‘™¾³mÄvÛt×Ý &° 8ý@‘c‹ E ÒZ÷×÷Ì I=L2v+‘Lyî=sï9÷ñöùî»·7—¯%?üðáê’\¼½¼¥$•x ~ˆLË Fr<ÿ ÏäŇøâmSÂH|AIœâ¯â½úÓXFÕûêQM×&ðæ{vä’€†vD)å$Þ^,È2þýâ:¾¸¾QN@XäÄ‘EmœdLûÃGî(§çüȳiŸj!Ûm².žV¤ÙÂ|±I2ò{«\‹^o…ÊàB6$!2/ Aj‘VuFv¢&M¤_W$/']u’{vèzêºn¤¼Èd+È}UoX’úQàpªo«{ÊÕ´˜ÍÜ(Šôù¬n·+²nœÊ%I“²¬²dWÜrå–1;Œz·Õ£(É}]mÿª­S!í©ðòÑðòø,¤~`{pëÎ&Í™Iš;äÌ‹F=xŽPêZ¸¶c;&dWˆÇ”C÷µ×p"Å='š½†÷"î1üÜWäs}í"JÎm›nˆÜ&E!j› ¸(‰K 5ÎÙèj‘í±>Íš‚È+y/É>o6:Û‡£çÒmß³ºÃšûRÔ¹$ËåW¹ŽÓá7Ì m* só®sOd»ÛUuC“:‡´:ª/ýEd—Ë`/Ùâö–´rù[üË1gõµ4ù!5ƒJ}˜•ZxÛ]‘ßç"#ûäéÝTšüW%߉BN¼(T4öÙlþƒSÃìyþÇL‡ží¹ÔÓÔºƒm‹#«‹M…Bdª„ê¾'ø‚q|Ñÿ ?äËâûŸÿ§__–DGn]8Šn QÀì(¤žsЍ«g=¦çˆb¼>߬È~#j1 #z1 /´Ÿú§(^ÊQç?<ò?¼k²‘²Ý®Q{§p0úb ®k.õG|VÿÍ éb¦†\_Iz¢EÎ$‹±£s˜Rܸ›áиÛ<É"‚´ +›ºBþFz7ólöw¨IRf¨_rW•¸;f¢z8®áäŒÈ©œ?O„ï—S;òiø¹>šÉ÷0øð`Ê fFÝ”è’K¤±¹B± |¨B71óÒWrþœÈQŽæIÌç¶þ +/F<ŸQ3]n”›ÉHü•5ߊ²AÚRó-øøc…r{ ÕA,f¹@:h 8eˆpX ó²˜<¶™Fù¡J‘6¹šjl5±£'D…N0b¶ÜÉÔ’«­dý„¡îÓÕ'kD;ÜŒ¸Üh ÛÊn, häþC’7Yލe#êºÝ5o†]ÊÈÏ26Np¥É.YçEÞ¨'GA“í¶-Q°g„Î;’t"®ô¼ÝµÞ1õªluÞ‹¢]™)PÖ`ðDLÇ̯@äÚtPü«Eý˜u"÷T{EK¥nÈ/ÐA ¢·}R$uºY‘õ†&á;]išŽkôÞ?Ay =Ùµõ®BQ%û®htÎ JAÝËaáȧ+Rd‡}EªkQˆÇ¤lþ ü²I¦«æÍ´W" …5Ë:Õ¯(òR`»ÊÈ}]muH.—Á"^²Åí­©ZÛø}F»“3Ù8ûx (ºóÙp®×ó]™ü¶(UæuÀLaßzç(NÇ÷HQm ·ü¡¬ê¥.D6#Éñu¢ €úÌ ±AqÎkdn†='Ò˜׳Q˜ß­,t=Ù£ $2OI…IÅ$ôêK’éìë¼öäx¦ ÷Àv”US³‚4c]V'¹^HZq)º[ aÀ8úP'Û#*ÛK+XLÇc|šUl)ÁºÞsÁþ)À,V›J endstream endobj 11 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 6>> endobj 12 0 obj<>stream H‰¤WÛnãF}×W4ò²R`qº›d“œ8LìIà$ƒ,Ævq(²eõ.E*¼ŒÇùúTuón’‘σ.Õ©Ë9§ŠoþM®¯ß|¼¹»%”¼{÷Ãí Y½¹¹§$*à üGŠ(]1¢àûŸàûÇbõÃnõf·£„‘Ýaµ¥¥Ô È."øÖ¦ðõþnWFñõOø´Ë ã:¼× âQß à·œìN«ÏëÝQÏs)Ée¢dL²”$Y´ÙŠu˜3ò°þæ&WçD~ýæa³ù}÷soð™‹ð,°CØxµ&eFÎyöEÅ’”ê$“gr2Þ‡ÑÿÉ!ËIù|¸Íî+ü=wð÷[`Ë,æA ãDÇ0£Ræ…EUAT%U,ñM!óC˜lœ6èt‹Š:•>’_$á"‘›OXçûû›»;ŸO* ‘>ìV>â4º ±fBƒ P ¦aF>³lHÂïç¢òɨ¦›5•pwAx–kšíuoT©ÂDý ½Ý?“B¦1V®J[ìÉS–ÃŒ$LÂþ5_³ýÚš]Ûò q¾X³³P³Û–ìMØÒÖ4€c¹#Ÿd“²(ÂG`à ¦ûÚJ¸°@@ŽX¬D,Tâtà &G~¸fzºˆSSD”¥…*J="R”9Þ3¾'(„Žëòbpj¹Â5\ÿ¼6z¹‚)o¼µzL  FÀ‡5ràaCöY•Æaþ ²ê)zL½my Id\r£Ÿ’Rå‘„%Idˆ%¤’|øíc+ñZ”ÄáÔ¢|X?±¾:dlÏó¾#ê@ÊÌDåµg˜€ƒüNàï%9‡qܯ]³¿©ûŠô¬Ã”Í|Ë ê.®æ÷8p‡¬M)B] âñ^I9×£Èmo™rþåX75{ÁõÔ¾ÝÆAåУÖ\Ñ;°Ýe¦ÅI…ƒL«Ó^Ž{Ôs%P¤Ût+¿"OG¡MU M:eq•dÀKÇ×¾-s‰iføÐFâïc¨ÁÃó9ÌeZâ)³(KôÚh çRâ:ˆº »60³™°œ6EL r¹ÙÝßE–e5½Ú%T÷ÎâÜ¥Íîé>äÚr ËèX{Íœ^|KøÀ¨à9à=§Ñ1ÏR°ç¢^E2„žÕ$‡ä~ÌGrθ´­à¤‡e§4Ž“yž™ùeÈù¶c>Û:À ùõ,aêñQ–´À'Ò: ºòZWfZlÌ­5Ù u¹¿"ЗžKF&¶rk‡Ý‘p_ô-ÌE¦Wfš–på),¦Ò0ËaËÝ|×kÄË}¢EâìyBÁÆyvÆ{³Ý8kíµ{Y>Ii*Öû‡¤?ÐÀùzIãŒ#¶Òîíj¿h0ëúËÕ¨/Úod¯fH’¥ÀŒªñ¬Å¯´Çs`ç0¾|“0:¶« é„€MA™mÖÚ{ÍÓ±£¹Ø(6¶6•Þnøï9îá›D¥­>q|*=WeOFMÔ¥ÃRb Œq–‡°/Ú!6šªeØ‹ášÂÌÀ ûQr'MË7éꨃ\!æ1ü"uïø·ßœöpÒýìî`¢â7ªy™°¸ßõ“D°šëtb3±ÌXQ˜Ç ¬c¯=…p°bwy³±¥[øïV›C]d¶0´»ÓóïFK"0‡„ÓÇš¥e›¬n‚‚Ôø“g‚¶þ‡eH\¹©ƒôPÚáLhµ7q“OÓlJX|¤P_Û|;¨·³M|íÉgœìÌó—g3ºÙÙËÙ€•ºœ áXžÃÌeóy "€ú7Ü¡tM€oaRÅN!»“È/ \h§o¶ oðº4ï!žySÈ?*Ù:û¬¸ Ö†G0Á¼®Ýâ*Xhy^Ëyï2ŽÖÅ<Ö¡8-Ê•Zð/`Ô²ýAn p ÓÇ¥““dã°|Î<^ãl¶æiJšÍ+œ7çDj+}>ˬÒOCðÿÖ×ßCÂðÐSÞ\r|f•é3œœZ§ JàÓæ7^ “Õ ¿#ËúÖ𾾸LJx.#©¾@ñ‡ν¤ò)yn\½wµOS¾Ý«ZS× Þf¸Ê…c(ÜÛ¶x}eI’=A€ý³Ne3µ¨ê'¯@1?kõ)ÕÞ|,ÉKÝÍoÕ w´·F¨3Åc³±‘ù“¼8ÜþଡJÍ bùEEXí¬»òé5ﮜ×`ÁßpÊþ'îÊá°á”õQwMߊw¤[ã³ Ó–þ":¥–€Þxϵ÷–±wd#PªuÇ:s“áe&Î|Ï¢¼®°à¯Ùßׯ¿ÜÚÚî<Ðe¶Í<ÛrDWüñ£ð(0 s™o37°|èßÁØ#˜Lä×yœËì›9pàqŽ jpœ1Îþ;2íà/Plxdk¸lPÜŠJ<°\€iÓË0A´ÔT&faö: endstream endobj 13 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 7>> endobj 14 0 obj<>stream H‰œWËrÛÊÝ«*ÿ0;“7"ŒÁ²ËU–äºQ%·,ÞÊÂʆâÄ @c@1Ê"ßžÓ3xR ÇvÉ Ä™~>}úíoìýû·÷7w·Ìf>\ßÞ°‹·76K^Ð_¦Òâ‚3‰÷¿âý“º¸^^¼].mÆÙr}a³eŠo-ôÕ¥bܦÿÿM¯*Æ}þ |+öXhGVl۶Öۋ›/ÿyñiyñ鞌öŽðÖ‘‘¡…má¤Ëµ=<:>=¶ľ8ø:[nËË+«ù"œe¢bÁŠ•k¦ö+%¾ïEQ³,©–n’*IkQ)6ÿÇòÏæþ˜ 8ÜòÉÆ2»˜%•`j#׵Ș,ê’­d­tÚ·Pˆ¬ l¾ÿ>¸rýd°†#¿ÿõn‰èÚ¯ÿ®mñ˜-¸Å½8Žõ±µyÆöE-s–LùÎG&«D*ä³È¬S9t&sØëUÞ‚Ðòá”w¶2î™Ê8]a¼I¾k…¨Ÿ)ý]@ªý®F€û¾—x@rŸD!ª„Ò»zaj'R¹–é rS´T(V"/Iºyü ±Û£ŒâY¦‚=ÎÖe¥½7Nñ.®ÁhŸéÛ‹™øW²Ýåâ’]ùôñsg^ nðº è.ŠË ]S߯3í8\IØç‡/ ˜=Î-4êýM¼¬Ê¤ÊØ6Ùídñ¤ù$(ý»j“É[+#TP~Š‘–é~ äj j`Ý,X.UMF`¼wÞt ¬Ð£ž0¨üSR<íw„¢>݇ (rY–I•–E!ÒZ„—÷³ðB@®mûçá埗ßfˆÇ|Ê‚Gà`ÐùaèÛLÍþ -¤^u:²Çß(––Êh×ú}9¸FãXç7é£*nu‰‘Aüð2"’uUnY}(Y¬r¡._•aÐôÍ…£ú–H=Dß&×/(¦®®Ú%)=§%áR¿*ñcˆrí®F~ß*(á3¢”eÑÀ‘ÜÆûM2`¬>N>ÕE]Ia`‹¯,¸^28ô# !¹“ÔO‚.ÐÈT ¬öГH ƒ…íÙ—Ìæ>úõæË㜞9ž‘9ú@”`sû8@íg`Ú£½v”ÁÇÙ*I¿Q¾DׄyRëlä"©rpʦ<(Ãë›ÛW<ÑӴ϶ÍÁ:ò9ÉÅ|áÍPiðùG”3“=w<1«^X ¾ÿp›S€×¬e©AL‘ÿ*&ªNûæ´C§E"¸‡£á@ãSîékÙz&ñزcßîR¦pw×÷èQ<¡ï…ž‘#V:fOn[íhËdV¼©1¤0TUMñîkÄ#ññ_ÛKrùM7©»¨d’&ÚUe]¦enœÝ+¡ν‚ZdyQÛ£ÔØ‰)´Ìra®ƒŸµÐèÚ&™ÀW»_Oѽÿ sí¨&úÔa½Gb W²8=s"Ëqì6÷34eݶvßIš ~ŠfÝ8r˜osH,ÎÏK¬ð Ó¾ªúk#^Ae56nxÌ£0Œ¨MÂÛå{ÅvI%ëk"Í-›G–ø-<€9g÷ ÷me‘äJWRFsÖ »ÉöþÏ Rsñw;Œý†ª_ˆÈÓH¿a–fªÊ“*õ8šúÙ0HaMÏm§¦Éã¼OÊdä]KEÝhaó¼a÷>ô^0ÆQð£á‘•Ztii#<0Úç÷t' àÐPÓ%;èÛÏZ Ÿ†Ll¹1¾YJòCòb†t«én¼e`ëòün  G!C¤YºYÔ°ud¹Çú„ôCRCcw¯¨åë >/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 8>> endobj 16 0 obj<>stream H‰”Wïs›Hý®¿¢+Ÿä*‰ÌO¶vSËÑ•ou*ÑÞ—ø>ŒÑÈâ‚@(^ï_=É€Wœ’M¿îׯßô¼ÿ¿þúþóâö|øp}³€ÉûÅ7Qì?(¢tB!ÆçÿÀçOÅäz5y¿Z °ÚLæÄ#„Pü>û-ã°z¶¯­  Ä~þ?­r Ì…Ã_z¡E/ÄW¬v“éb«s•&®”‚¸€27º4k(ö&Šu’¼x·%DúP˜®Vÿ©¡eƒLýÐB3âI*-æz2-·”’Ê r³Çˆ`%Ú¾ï^÷íëU$÷:%^ ê·‹ã “–°Ér÷j ,Z`ûâœSK sêQ†¡‹vXS¶qÊx‡©»‚61–§'ᘠG…G¤$ô$ýÔü…U7ôX6¯j`Â%ÎÞ ƒýËÚpc¨’88Vû—úÒ¿=[©wD¨ìGxÒã°L²gXdi™gÉ è‹ë/Eà9)(“—k¹äs¯ûÕàEFy}¿ºO!N<¬Ý)% jÄ-¦Ø)ûè]ªËC®“wð¨4ö©FÛƒGS>“BaY(_öqa÷`˜†w]”ï`ñåÏjözeJNÔs3™–ü0f‡ÍÆälòlÙO“o0~œ>¡¬®e-žV›O=ŸÕʯ$û<{Êõ"”û“IM®Kk]jØ¢-Ø\íýí‚Ü]ÌS­Ÿ–[]‘ƒïîâT'…‹ßGàÊŸÖlF™cã²:SQ=#¦>|㨨À ö¤;)ÿ`¼ªxS endstream endobj 17 0 obj<>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 9>> endobj 18 0 obj<>stream H‰¤W[sÛ6~ׯÀø¥ÔŒHà=ͤãØÙŽ7Óm&R§ñ>@$$¡æEKVÔ_¿ç¤n&Õ&{,‰Îõ;ßùpû‰¼}{ûËýãqÉ»wïîÉäö~î’TÁü!*-'”Hxþ3<_«ÉûÅäv±p %‹ÕÄv×u)¼O ¾eYìðØBêâëŸðiQÊ´9x 'ñIäÆNGY“/Öýb>'MÍÓgEš e[,§¡%¦ÔªIµ"é†×¤¨>ˆ¢À%Óÿ.þ=A·4@·Ìuâ$@ÙÄZ¶«•¨IZ•ª-DF–{ÂÉZ¾ˆ’´JÔ! ™.þ˜ØÝ1›:ÔO’DŸ–eZ‹B” †#áOÕðœžnH# ¦:ûð?4¢ƒð1;~ÐQµÍ¶mÀ×rÒ!+QfŠpµ/ÓM]•U«ÈcžùâÁÔiEcŠfíÎîY”iUlsѰà“B(Å×c%ðè’>—ÕŽì6tSiã½YªŨã².V’‰.gÝ,ÒÊ«•lKLE×l°í¶1wâ¯&±>Òc”¼XŒ†ð†äg¡1¦ë9aÔ×QpÕñ"ê=ñè!¦c$á!-],ê:”.íN×j×Yü«,ÚB'ña1ùð þ8´‚« §±vrŽh—&ðÆs"×õ4¨­‹ÒYšL9EÕª­H¡ìDŽء~×§È닱áºC{]Bž«ÊÔQ®Nûv(»;}Ú—/V.KAÖtBf¹ OÏs]Ù©Y¼›3mäiê˜I(,˜iåX‘Xè5ø¡.ÒX'¼+ʼnÆ|PæxðE_ûðÀñɧJ)¹„T瀀œ7²*É(§ëýXþ·§$±"éÕô‚aÃç8sýs±×ûˆ}ÄYò´ÉÔŽãÈz\—@Q°‘ë ¨âÃF«MíÞì}‡v$ðÅR ¯ à·´ÂÔ †ÁEø²z¹ôr›W;ûÄÅ~mèGxÁ &º%˜Íx½× #!rYÈš¢H¸$E• g¬†á•æid zA„Œ½ÓêV Aª2Ç]¡d¹´4û­À…åÎ`%Ytä'˜`G©HûçŸm/"»ªÍ3˜TQ A/MפPÍÌÌ)Pž§OŸÕ ˜K6¤ÿkeì^Ë »(…ÒàvRTe^fd€?Ìf„¸ú[ee-¤‡y©\5Úš‚|†V.søï‰­Ë$mcíœNR¤µ@r³oïþ3ÔÌƃ·¬`çÙ- ´€ ¤$ÊT;¯ªZÛÖ{Br ³Ãn¿„«{âùab-D^¶Ks‰Ûz,ÑwL²KA¢PÊ®Nr|…¨¼ã‚ Fœø1Óí| Qä·’—j'j("H!Q©7cî“oÏË| GŠäy%/êþ­]h6ÿÀ”ù¡ ½ŒâK‰¡By aæ'ç…º3L+|9,tF­„âåG)‚g™Ô ÅÒ5P…h~ÝÛtxq_¨©N{ ]ØáEwù¶åãå44²Nª\ZW"VבîˆôпŽôá-?Âð¯½x E¤G´›áÜCŠ¥ÈÉ¿F]ŽìÿkÉxQKŸFñõd†·¢I&BûC¦C!Iö[‰­Gö*²€é_DýYÔóBÇ'qׂèЂŸAÙF,Nž¦£¾¿ƒB=Æœ ¤pk¼–»F¡`yÞ˜7v¢¹ÌÌØ1#Iœ´¥„í@07Hq âvj–œ2k ò%òLÊýíÑ›8‘ï.FigRµÛmU78£Í®"7ÇɼK̽¼ôialžÑjV·…ê.¾ÛºZ×¼(Ð>RC®i¡A¹‡_;1~Ùjæ¢l4Áê¬7ûe-Qg4;! (ìmg ‹à‰±´xqW°Å³P+sqŠB˜\=ø¡«Louü¦œ\ÌŒïÞ\â&ZPñŒTõ€š‚IÕ}Í×ËÅ]-›>!ìrV“ïd³Ñh°9ÍP],²ÏsþïóÒÿÖ¨÷|?Ó3Þp~¥Z‰º7÷ºÊÊø{üõþ“>ofäV5@ÒÏâP©!aÔÀP‘—msZXÒpÜþød%k0¾«ê K7.;iô÷”S[_úÚZŽ(Ѩ‚•UP³¿vg÷B¨mUf ×)zšß?|E%[€­ÙI!t/á˨Ø Çå<Ó‡tp}èÔf’/Ļݙ¹ÙÚä‡]˜*=Hª©j9,˜.Y®P[7Ò þÅU(pXßfP ð iwàùc13Éá‰ÖŸ¦£;€ «›1ºd€r²àúf×4É¥|í„& îBš5ü§²Ýì•L᪡k޵¦ ûën©ñØ8ÌäÀÈ´ãÊ <àmÍKù'(°¡äWr“Wk™¢báù ¾h ìuŽ˜ÊôzÊDÓgÓÁ…0Ýkèè°¯mÏPæ5m]ήÍHìGºÌঘe]¡0¸I7mù¬n°ç@U­`E@ÄÌõcrN ¶¶ô:ÀŽÑÏ0¬Æ™Ù @¿oÌš V… e[,Q×®ÎÛMA®¸_ÌçQXeض¡ ÂÎDÚû8‹–A¤'4 ³|½3Å+ÐÿÕ—“žÑ»û J}¡Ÿ\Gþ…¥¯ïZ¸&tnÃ^<ס`ôÏ‹,e£Þzo‚wdÊ`7[Ç›ÏfíŽ(m F3ä¸3ˆ™‘A½[¼¡8ÁO‡à¿å5ô:ZW×s€×âîÖ÷íBGÀèSÃ#uÀ<ßb>/Font<>/ProcSet[/PDF/Text]/ExtGState<>>>/StructParents 10>> endobj 20 0 obj[25 0 R 26 0 R 22 0 R] endobj 21 0 obj<>stream H‰œWÛrÛF}WÕþÃ=ÏnnæïnßÞ1‹½zõúî–]Ìoï-I¼ ?&£üÂf Þ¿ÁûGyñzu1_­,f³Õæbf™–e9l1úæxlµ§S+Él‹>ÿÂÓªd¶£¬áÃ_˜KVh.õÉìâÁXmÛ&Û™¼œ-Œ1‹Ë:cQ‘We‘¦¢dIÎnW÷÷¬rWä±d\²:O*æº+rmyž‹”½1ûpwÏxþ»üïê× ‹ÍlÓö–Ë%[Ý]Ÿðß])v¼¬ÂŽK6EÉ.Wê°lÇîâ²m ÌvÍÐ[PDñ…Áá Nìˤ¬Ø‰²;kÙÝY˧£¡éûíÁ*)rxùi+råÀñ”×Z¨ûsá·×µñ%’ÉŠ—Rt<×Àö—tpæâ!Àc/rØølì“jË~¿ýå ùýŸŸ/¯”›¤”Û%"Ú°jÂ%g¡LÛé„S¥¯qlWåå,0xFN’QUCǨ™DÀTe]eN9Ú˜¸)JS™¬×Rü¯¹ö UGÑò¢ÌxªÂ×1+¿B3\’…ƳÀ·”6Ë`F^QPûmm¢Ë™gHµÀIÚ'mÐE«óØY8øöÛ÷Ê=|¹3•#?­.~zGÝsì(»í¨AÇXêåñ¨'|wI=á.©'ŒSVI«'j5¾ÁñM´¥ç«þ¸.Wá’R¶KE%3а:VX廚*ŸÁâD¢X¹ˆ*H6ê÷"çëT°5š“Ð*ªa_Yn×öRá|õÃßo®È'^ñXUÉw¬ˆ¢ºlA¤Þ´åobul3ðñMýÓ"jÊ+žäŠY”{hŸ<溂ˆ(Krî®—, ÎpF’ÇID6ù¥mTE)‰¡nìŵ¼ºF Tm’T\'â7æêçõ.;Ò„N¨c™Û¶üVDO@.|.µ›ÿþöî Vu™Kæúkºå˜ÙŘ{ô¶yÕ‘#UÝ$V ô× ÌŽÓðžï4NQûU,›$ñ[×Z²ëÚy5YdÝé¶cúAןaëÆv_©†F‰d ä‰U[‹1³Í€[w„r^þSìÆzE@ã;¢ô¼BG¿ÿø }'›Óý®æt—¡Ã‹€º't'úó~Çóζw¦EuÁǹð\ÓQÒâ(,òfá,Í…‚gÌg2æw8 í RS—Ø “¦®ÌÏj”À@;"+duÅÞ²=Ç ºø1¹ãÆÙaƒO"Š­Hwjâ»Ò•àÙæH!µjHòz0†{ÄæØíü€Ér“¼Òáa’kŠM“gÌ–˜zi:ÑËN Â 9‹–ß .‘[±ÑãÃ÷O¼bñPL(-ÍÐÉvÐò‹” ñ´âDž>óÇ$lã`™ãM CYÔe$”Û‹4¥O„W¤uã |ʬXK¸hI»O|^C|Þ¢u  KŠ”ŠŒ}’?¦Š ïAÝì®F‘»_Á6%¹·%?²9ðöž§Õ_¢¼bwEÎÓ˜}Jâ2yÄ# ÿï10ÇzFýѨ‚^q; øºX³ŸEY!Ò+FOw<‡+ö¡xg(8•ø´œš5N²ß8é!gï ô0V¶üYhµÉ‘`­¬ø¤; ÿq©Ã£z|ž“ËÀDÞ¢QV»]š¾QQ–M’úC«öú8§4²gÍá$ä˜þ1Ì’æã€ušÖY’“ÊR£W4êbzîig=sÙfU£F¡ò +‘ÿÂFBDÂÁnì» ž»jÄê(j¥ ŸÐÜDÓ¡ù á#4Ý—x¦”š=’¤Iu89GÂSÔú’¨æÙKš Èà92\ž!×SõÅÖÂt!dÕfe¼çuÊÞ' ©¦å\Ƴ ¢âŒl@VÝ®—Oª %ÏeT"!ÈkG‰r¨ vOj‰ôÁ z á#VñP0ƒð†€ †¦úNÉ/-ˆfN k9O•¦+Èî¾$\™âMN®Žë5fuʡச Aíƒ „ÐûÇÔš¥ÂDj0Ð’Š˜9N–¦1Ý8ÓFîò2Úb{Œ èD£¸Û<ãú'q@ó—ÄKã)ã´^+Ëâ"ª•\Pj»É/–¤÷Žøã}RU²WôñªÓ¦Z|ÙÆÔÍð‡òþÙq¨ú$«é¥Ê\£Ú8ÛÔà'äÞóGoAmÄzßùG(ÊDMç²ÎÙÛ×÷ÿ°êSE’ÅC‚¾YEÐPÎ.´®- )g\J‘a¬•Êý©xÂ Ê å\9NVÈqz£QèI §vE;&‹‰©Ðh’eï(î`_DTWj:­Šg®mº‹qÖ¬ˆkðÛ”˜Ç^S“V-TÛL;ËþŸ¨hèu€°Dú'×R#—UY·"o•¿—3ßh}“ÏéÆðÀeåa’ñGÜŒÂ{CtÍTÆd&– 4¸}6P[¨ô7Á Kê³À '54œdbÛú*v-(3ߦD¡bü{ÒæÑù†%F ‡ÊðïœÔ'-IÔÆ ŠT¨7?¦ì7«Øëü€ªdvý6?î¡Zÿ-Å4:½‡‚v|q²µ1´[®ó™’ÕýDÉÝ£†å¸æT%U \_%O¡iWìbbûˆ?À+Ô‘Dà”T³-»‰î8qôÊKòišo¤ŒC¨Ò.¾X­§ì@W—³Ð ݯðwÑ¡é„ã)Ó¤J×O¡1°¼³îµ(ÅóåÒÀþwÌU_ñuƒÔ¾Jó?#?QZDO}OF±¬~€Éõœ|¸¹IR½k@ž†¾ó]Û¬‚©»$ü{Á×–Y,U[Ž€Sñá[ºÉ>‰µÚ Ôºðr§u× YhÑ&×­³öÔ: Éø­áÚžoú#?Nfqñ K-£‹ñ…YsSÉëÙóO’G_ŽØ[­ºÅ^¢å|5†JNsÆh$\Ÿ«aÐz¢î`LFùT9‰ :®o/Ê‘mUí®çóý~oî”;4¢Eiv«…mÖ›†ŽŠlž&ë’—‡yTIin«,í»Û^â›t¡E%³š]S׿—Õ°‹5‹ÂÁ‰vèau9_¯sÂû…~Û\_Q/®Ñ{K Zí=‹sµr¬¯Õj çŽ ÕoS¾Gì~­ÖI%¡Ÿ@©Eù8ßÅ›ù0ùÝY?4}Õu6Ôåé>œ®ƒc­/R´ð¿Þ„Î4•MË®7xî±/ŒÄÓG¤öc*¢§¶ihe@ãì T®5Ÿ¨ƒûm=ÓcT'lœÑžôzFú(á$žs]jûü<§¥û¢ï“6d#Ûžв:âÅ^⼿×βmìAIþ/À66<9 endstream endobj 22 0 obj<>/Subtype/Link/A 27 0 R/StructParent 13>> endobj 23 0 obj<> endobj 24 0 obj<> endobj 25 0 obj<>/Subtype/Link/A 23 0 R/StructParent 11>> endobj 26 0 obj<>/Subtype/Link/A 24 0 R/StructParent 12>> endobj 27 0 obj<> endobj 28 0 obj<> endobj 29 0 obj<> endobj 30 0 obj<> endobj 31 0 obj<> endobj 32 0 obj<>stream xÚìš[o¹…ÿ <ÇÝUd7›À€/1ìØY –÷i ‡Y{à(+k„ñ(YÿûN-id]ÜXÇAò°É.’uNUwÍ8¥Ð†ÔkcH}ðÜzHYí`! Á‹«-!º§Ðµ!¦¡„ÎBÌq‡XJ:Mõ܇. ©W¿ÓT÷Ði¨­÷³LÙë¯o5_ClC_ÿÚ>ô¦6ëÃuÕºlÕOj&È>Ö~½¥MôY·\ƒƒÚ\B¯¿Ný¬[]N¡BõÚt^Ö¹²ðrí /—!ä:UƒÚr_L}á-šuÙ¶úi«C ­ ÚäŠlš?¸Ú¢¾L=ª¯¥\ƒ–Ž:ÏЫ赔ί#ç¤Åä²ÜÉEx6_„§ƒ†"¼^Î(ÂË1Aç,<¹,u\xEþ*Â+rnÝz)êké6%q¦5[¡›œ3X¬#ZÕ´N¥s¨¾Ûí¿:ÏZm$Šb“LJXêHÖD²éÌC×Vc!wrŸY=\c&d9SB–7t!dCB´ ™‡ŠcBJ5rIÕXÈe¨ÆÚveì­.äi«1©Êä©bâμºDu´x_;]Ô­z//EíÐå¾X÷ãƒ.ä9s!§º–(I¾·(ä®zCë•NÄZr'·U•^SMX¥7Uo%k …œû:]ȃÕ!ËÁ„^J=irIuDÈ¥Ô‘XiÐÜŸ~jÞ~>[5‡ÛÍù»íÛÍjõf½Þ6/kpµáMóädùéÓß–g5Êjÿ`¹Yîìj¼íü¼ú}ûrõYèÍ›õÉj7k¨6j™—‹$qª'èDÛÑö´™v -c«ˆ[£uZðzðzðzðzðzðzð2x¼ ^/ƒ—ÁËàeð2x¼¼¼¼¼¼¼–û-÷[î·Ó}ÖkY¯e½–õŒõŒõ <ÏÀ3ðÒ4Ö™ïÌwæGæEÆã8á'ÂO„Ÿ?~"üDø‰ð óá!ÂC„‡"ñ‘À#~½¯¯¼êäU'¯:qëÄ­·NÜ:qëÄ­·NÜ:yÕÉ«Ž?|òyÌÉcNóv²g}ò˜“Çœ<æä1'9ỷO'>øtâÓ‰O'>øtâÓ‰O'>øtâÓ‰O'>øtâÓ‰O'>=NüƒG|ú6ômèÛз¡oC߆¾ }ú6ômèÛз¡oC߆¾ }ú6ômèÛз¡oC߆¾ }ú5ôkè×Я¡_C¿†~ ýú5ôkè×Я¡_C¿†~ ýú5ôkè×Я¡_C¿†~ ýú5ôkè×Я¡_C¿F~3ò›‘ߌüfä7#¿ùÍòÄ7xä7Ü‹wqîä[bÅÐŒFl4bÀˆ#Œ0bÀl²ç Ä€F 1`Ä€F °u”‹pÑ-²AµˆÍ"Y‹`Ñ+òC­8Íâj” 1£~šƒ0*¨9lWï¶»×ğכ˽+ï›ÏWË÷ǧþTsNãâÊ˜Š¾qÕ+cªêƵG¸óŸm˜¦Ð` ˆ0TØdØt˜BJ¬• t†IŒµÄÕad­wGÝíåõùöäøTïÚgËÓæ—Ó÷«Íew½ÛŸmÇížÿº×ÝnŽ[­Ïé>ݬϞ,Ϧž­ONÖÿZ½®B¢é·Ñêz÷’Éæ¹]%±yîWùkžÇ‡ cŒ-¾{µ@†Id˜D†¹·z Ã$ž²‰ —xÊ&ž²‰§lâ)›xÊ^«:ޏì€éÓ±ŸÊümʘŽ0a:æoR¦È?ꔛꔣÅBåÎBEúÿ®T䀥ÊÕRåhñ?^«è?Š•?R¬-¾ªZ!\¬ª–xsõB¸ábíô:\þUÍÑ⮲…ÓÌ®nøÔì9øz¿Ö ý’V©|v_‡„M_À¿~ü×7Íë_ÿƈo>£Œ©…Ò¸  ¥Wz¡æûôQ_ô7Œ›»nðâ—²¤Àiž\”0ÓO‡#—@u¾ÐŒË áMºÄÚcó(ôœ)ôSÁxY?íÿn^-?×J¥ROWïÖ›åöx}º;áåœý›OÖ'ëÍ¢} ÆÆGœO¸½Í?_¹é|vÛùZo æj6|×óõ­³Î7ªþúùÚ[Îg¹ž/q¾ìßñ|¡s,›cìsŒã ãE Ý‘¹<Þërëfmh–}žÓ×[ÓàâÛ¸'{ßÝ\ÙÁ•áo¿x._,î7/î_› òœ\çä‚<'\5NsŒ»æÑÂÆ‚o/[¼xr?^¿ÿ|}Þ¢Ÿ^óŽvé» `?£ðêxû6‡9g*³Hkwøòf¼ÝveMK©kó®«ÿÝ¢-¾‹²kéî¥ã¾ó†{Ž8K÷'©=ë~–uže=‹u›E»ÏŠlŸÚ³bÛg·ÏâÒgq鳸ôy\"ÒƒéG¬*îA%Çèÿè*î«  þV ©´¼éæÅƒäp»Ül_èÑqº ±ÐîôÿlÃö;˜¿¼ÈxÓ}{þ˜~+Žv‡ì6€ÿôÞï nÎC±›óPìæ<»9Ånï¡8øŸcœ¿Òøß P’ŒÂ endstream endobj 33 0 obj<>stream xÚ”–ÍŠU1„_¥ß঺ó×0ÌÆå€ˆ>+7Šàû/¬ºŠ¸ .Ü$ç;tUN'˜3F`®àsG«wµù7ùß‘Éö‘gòQIneÔ&·*&È­É(âV¬!nÇZâN¬wcOqû’Û#N‘ÛˆsÉ팛ävÅ=äöŒ†¸½ÅñÙ€@.p,‘|Š!´ÙÐz£ r²§Hy ŸbZÅžr¨#X9§`e«WÏQzÖãµ5ÒZ(Gž+T:šoýÑæhŠ[zA#S¨£ðš¸%ÖeäVÌ<‡äº”uH/jž` 64E'’¬‹ÒK3¦D¯È’j|œ¥Eõ "‚y*ÝfäÍÃÈ‹ÒqÂÈ=4!#ï¥FÞ91]Õ#Ÿç¢ù–Fù0V§FØiŠÍõF pÜ c+îèUKL6´( Z©)°Ùx¦{¢ª_6î3ݨÉ]–«æQºŒ¼ØËdäEé2ysJÂÆÌÈG0ÿ:Ú ÉÈGÚ0VÝ)˜‘¯T§1ÕÊ«¹)]r7Ì‘’[xPºäã IWü$´#“«œOIê°¡)ê²!Cªcrõ¡}6ŸYLÄ”òljGådäE%^^oqþúñõû㓾¸Ÿ_þ|ã–ÓÐëënưhXtZtYô´èeÑÛ¢E[VÂò2-/Óò2-/Óò2-/Óò2-/Óò2-/Óò²,/Ëò²,/Ëò²,/Ëò²,/Ëò²,/Ëòò]+ëÿOÞÓ˧/Þ|ø:p[¦xZÂ2–‹°l„å#,#a9 ËJX^¦åezߣåeZ^¾»l8UNSEàT8UNSEàT8UVUE`UXUVUE`UXUVUE`UXUVUE`ݵaݵaݵaݵaݵaݵaݵaݵaݵaݵaœmmœmmœmmœmmœmÙß &´O endstream endobj 34 0 obj<>stream xÚìWËŠUA ü•þoçÕÄ•(*ºŠA® ÿÞªVDWcí…HrªrÒ©Ü&Ç3[ožÕðç9Ú¦7›ÝÕlÑßÍ~Ážø/kaô½Å -;ýDúȶéVI¶ZôWA·1áƒ;þ°6}oËèG[E?Qýj›å ”Çúêë‘0P`Ÿ Ænfî§<3à|Z3g²é0P™Ï€±ž‰ã%Ác<š%+›Èœ, I­XÛDæbqh„ &\Ènk(™‡—ÁtÞ& €'pö5Ôºƒ]ÝxœIð¢Toj-Nóú†aì^Áà±úõàùi°5‹‹3ƒ'¡1Y7Å*iðÅAùYî ÁoÎì4øâ3"IBYAeØÀ?º8Rà<ô` ‘axÁp‡se`‹êŒ€YÅ2¾ ñÑyò…L:‹d^8+™™<Àf=¤{1 &S•}2‚¶SÔ€1™-ÍŒ£Ù0ÐìÀ'j…a0#Þ!Èœ¢››Ë3 ëåÉåÅ—¯Ÿï>]^ñ×Ûë˛ߑm3r{ûl Øp(àTÀ¥€‡ž x)à-€]QÐ]QÐ]QÐ]QÐ]QÐCQ0J<ÿ:ë6l ذCÀN»ìV´„S”3E:S´3ESô3E@StEA—~{Š‚®(节¸•¿åŠ…Ð;…7ÏdÞ<¼.óy}˼}xKåá2'oÊ<;¼!óüðJæÅá¥ÌËà ™w楋óòòòüîû—o×Ë›ëÝ×ëÓû÷î¯Ø¯õÿOŧ/%,%,%,õ×2€©ìúk/ -UÚ¹–*TÚ¹•ÊUÚ¹”ÊTÚ¹“ª«4;wRn™wî¤\2O SfâÁ%£”%£”%£”%£”%£”%£”%£”%£”%£”%£”%£”O¿R>ýJùô+åÓ¯”O¿R>ý~0aLX¨ endstream endobj 35 0 obj<>stream xÚÜX]k\7ý+úíj>ô!ö)Ø´¦é›Éƒ!¡/m Á…æß÷y¯k{ïÊèCKw4çH3göjvÍ=åd^þÌk|jIŒ=IçóH*KNê%iã¨É2GKf=Yå,yJM®[òеÒSÉãH…Xø–Q1Jªä­šj'‡¥¦ð¦5ú•Ôµa¬©w>·4œÏ=½ëH’ Jr‡¥I!à"K¢B‹cRi)ˆ’Çi•áÒ‚¸=L¸cWk`. iGë`®*˜€¹VZÀÜ„0·BKaÞhswZÀÜ-`F ˜G½O”fnŠeQ Å1bS$d8&È’’Tyf¤MÕèÓ0é\êI* ¨e6’CåGQ7ÃÌŽP<ƒ¹¨cæ‚ß½ýôáã§;$øÇü®^¤üBnìQ"%à«_ øzÀ·|kÀ·|ûáÍ5ßG°¼ ã{ìwùÓí‡/ÏacÂò¬/`×|ÖvzsÍ7ã)AYí+O4æ´,ˆˆÊ‘Y":KDh‰(-©¥GœGäÔ§š¾£f^©©2q¶ƒ“Uñ jã¿2ÒS‚Þ—?ÑšíÅ2¼ˆØ[#bkDlˆ­±-òJ¶È;yÓájvcGùÞ5?–ÒV›ÂïÓ^>Õ³D³«<³xö2:üþñŸíùé/Ý]ÿ‹ûÅÃëíj¶—'ùhÇ|ôãXÎç£úY¢ÙÍŸYüÆòñRáI @/ ^@½€z ôèäI/ðõ¾#¢EH¸ˆr‘N"Úá‚G‡SNo/'Wš<îpÊüu¼š¼œvVî}Aðüþ—¶>fD_‰,…5¢ð¼õùÅñ$3VV™Q¸ºƒ«KœM\ÙÁµ%n–€ûn© :âl7–¸:q;%çË’ã·jË%ç;%g¶Üøi]èXË) ‹ƶóÕüâÿì¾;Ç&ö&Þ&Æ–Ü-Y[Ì»÷!º8·Ñü9c±XV‹uµØV‹ýÜâ·sÿ+À­( endstream endobj 36 0 obj<>stream xÚìUM‹1ý+žÝNU*I–…UÇœâ¡uz‡µGšôßûÒ댳 ê xðTIåÕ«—¼tGU)j"-™ª"beD#6C¬$ŒåHRDdD¡(1R,µ±åêR¦$È¥B© K2ÊÜp•r.*¸ÌTZ-r€Ë‘ R4+YE¿œ «å!Ï #C_ˆ­ƒAi®Ä, BæÒú3±šÁëZ"qlí‹b€ ö bˆç¦R ˜SÐE>'ìS ˜S³9·sB?ÎÌ8.Ìæb`60[³¥vr`F–«€º%h[;L0NU°½óóîu·ê¿ï¿ÎÝzî§ùjÜ ãL1Ÿ…îzøv˜?f; ÿü)îF ·Ý›å¦´Ñº[]Ý-¾o—©å2!~h¨t òïþ´öÐ=ížýf7nIàSu÷Ò[RX……ŸuŒºWûés{ZrȸÏMöÙÉ>?Ùg(ûeŸ¥ìó”}¦²ÏUñ¹*ÎoÔçªø\Ÿ«âsU|®ŠÏUñ¹*>W£ÏÕès5z\=¾ï¦ÝŒ_ÿËýfèVÓõÇ{ÏIÀkòlÜœÌÚÛry»ÛŽw¸ný¥ÿ4<nöÓ°¬/óË›y˜Žð_Õÿ;ÿÝÎ?Ðî? endstream endobj 37 0 obj<> endobj 38 0 obj<> endobj 39 0 obj<> endobj 40 0 obj<> endobj 41 0 obj<> endobj 42 0 obj<>stream 8 CTSS HardwareSiCortex endstream endobj 43 0 obj<> endobj xref 0 490 0000000000 65535 f 0000008565 00000 n 0000008810 00000 n 0000010985 00000 n 0000011230 00000 n 0000013437 00000 n 0000013682 00000 n 0000015710 00000 n 0000015955 00000 n 0000017887 00000 n 0000018133 00000 n 0000020412 00000 n 0000020659 00000 n 0000022665 00000 n 0000022912 00000 n 0000025409 00000 n 0000025656 00000 n 0000027529 00000 n 0000027776 00000 n 0000030096 00000 n 0000030346 00000 n 0000030384 00000 n 0000033076 00000 n 0000033234 00000 n 0000033311 00000 n 0000033368 00000 n 0000033532 00000 n 0000033696 00000 n 0000033768 00000 n 0000033821 00000 n 0000033953 00000 n 0000034090 00000 n 0000034188 00000 n 0000036539 00000 n 0000037404 00000 n 0000038317 00000 n 0000039413 00000 n 0000040037 00000 n 0000040072 00000 n 0000040096 00000 n 0000040156 00000 n 0000040282 00000 n 0000040348 00000 n 0000044343 00000 n 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f 0000000000 65535 f trailer <> startxref 116 %%EOF simh-3.8.1/DOCS/simh_magtape.pdf0000644000175000017500000010372010572160244014526 0ustar vlmvlm%PDF-1.4 %Çì¢ 5 0 obj <> stream xœí]sÛ6ìÝ¿BO›¼Æ?D‘ÊžÖk·v×ÝÖÌoéœØqÒ$vš8»õ®·ß>R P‚b;NÚô¶ä&ÄA@–q‘0÷ïãËÁ÷:™ß ªáäà縞> L&Ý_5€áãËäùØ– WYY$ã“ËÊÒ°²šæ‰)íÏR%š©LäÉørp˜þ1É,W27éëáˆeZh“é¯n˜©Lúj8â´(iÒ`HaLF®TQ¦ó!Ë ç¥1éj8²l””á f0j6™(˜ÒéA½]ž Œ€È®‡#a™P…ôEÁÓ›̵v£&+.‹tQ‘‰’iËÏ”,¥vÜøy<«f$+ÒåpTX˜ ƒhxäy8Û¬Fȵ•Ù4 «½rtš—yÞ¥«§°Ý¢Ã!ìZÂLäNjޱ ç<±†Ó«î¢dYQkûùp”gˆ\»CªŒ9¾Ò#“¡Îr#¹v†áQoaþj˜genAËRqQ®Í@ŽHáÌÃZ$÷$çç+]…Ñv¿JœÈù6Hú½Åþ¨È;gÔ[hJùÙ2`zör…¦Ñ ¤Ô½‹0{nB@ óÞèN³ÛKcD×6Z^NÓOÛ×ëFÖ1#uNZÚ6ᚈû`G·a¢u”»Ö7}wk7ßçwHbaI"=©YîIá*"$œ "/…°¾_¯hüËvä™k=´ìkM6¯5;Á£ëMUM±'–Ž#8pàI¶0¸ß€¢zÂqÃræÒg€0Ú Ô‚“ ð[¸ÑP(‚œ%åLe:ï?‘CàtÀ&˜ üÌU±#‡×ÎHí.;¸Ž—éfnÄØ—PîSÖéÎV÷¸:ÝÎꞬri®£øô¤MüãWתxÕ1µÏ¿xIrˆœá|kÿ+ÊýÿrzJÊÝ4~yp ¯cHr Rã )Ï¢ï‚ÔS'T¢~wYpè¯à?Tó‰GCüЊjw÷>Ö•åá©B”ÂbºÚ¿yÅ$ï<د¨„ ©;ýV"*¶uíwãWùÄYWXSîC[®"̸Na1GÀý|#^]“P@”Pܧ™£ OBªœÑm=Ô ´­!3׿vÅb¡žÊÿ-±UóN.ʞï(!wJt­vÆŒªÕwt(C·Âa¢Jd·Æ(ãª~·–‰j|ü«uÉýSø.xŠú^Á(Ü¢×êßþô`ý Þø"@)ØbÝu¸„ è„D¸pF.và-A¶.ø b|¹³QVR R4Hvs’ìŠ ™Åm#¶3¸OŽÞC'îŒk@‘¾ˆÖxÐá¯IáÒ"G¸Ým®÷³ÆÝ…K€[Zãên[ê3ÁK’ ì¼-šÒ³ð‰ñiIœŸ¡B7ô¦¶kø ¤ç^mª€ ß  x}àz¹¸Ï„n1´îšºü¤³™Ü ýBfUðuYúEkF&ÑIé$˜­V ZêšÈÕµÉRó8Ùª[N6•àÒ§ht~‰?Ÿ ² 9ÕK:¥N…ˆöÈ,î]êO° ÚÏì¬)¹(ð ‡Eß ‰&ñ7ÕfSxðŸ°'ϼ¡üwü$£‘>JÞú´Èa½ïÁ`BͯëQѽ^ä7कêWáÆS@$?áËÉ=j-kÙÛxJ(îîŽQNEèeñÙ ­Qù¥¢>¶§GDЯºm^S"‚Sýa’>êžD2E‡¼×¤ÝP‹›pˆª·¸EW}ËÀkd·å&£F[ûõTË9à]W_õõù Ç”4Q²£Kº²vôÝGÓ TÆ—dî|óAîò@TDÅž²\Dg™2J„Ù®¨>ñzåCÔ|éJª Î;Šv¼œnÌáWÐÚݬsÒ¬‘ÑÑZÆÞ¼w¾DEþaZ4¸ßýÌ=ÏÇå°ú"þíà_å¬`_endstream endobj 6 0 obj 2124 endobj 17 0 obj <> stream xœÝ[ێܸ}ï¯Ð[¦·VI‰r.‚ÄA6‹u&ȃ“‡ž™žKì¹ìLÏ&^ìWägò{!["ëH:jµ&nLj ´Ô$‹Å:‡UÅÒwI–æE’ù¿¡q~»øêm•\=-v“·¿mW‹ï6UþÏî¶Ïo“_ŸºŽ¹JÜ8§—‹,­k›Õ»—ybk÷ßÚ$UV§¥MNoïN’åªÐ™I+=ÒüÙr•§¹¶Z¬þ«fUä´Y„)üÓdù·Óß/r•š29ýÃâô§ïNÎd¤ÒÜJs#Í'i&Ò¼“&¬FKóõÔo«¶Y?n™iQW'?ÒÙ©dçÒ¼üÖ¯ü•›?Š–¢jüÖ5ª™Ü<.á*!¬üj g/ËõÁÊæ}‘æå’·ÉuZìÔé^ýÄ‹uûÇe––E^çÆo˜I³*¯µWG‘eÜîoäi"O·Ë*ÕVå•WWx¿ J×k÷ÓÊâCèµYê´Ö®Çwï/pÔðú¯‡R¹§7Ë<5•uB)˜R; áË"Ueyë £>7CeUám#ŒzßoðaÖÒØV…‰ôCÁûfVÇñ{ï7òôûefeáZ›TM¯0<|ŽãߺõëúY†ìåþ!îŒj¼bô ôý7?¡ô,vÔ±ˆp&èã “6¶¥ºGʧ§f„·HvCØ ì¬G‚`Îß²ãòË-¥¯Ö¢‹ÊàC9†¦ï›7±÷zäpØ‘vé΀º¤Í9°h|;Æ×Á‚Oîœo]sŠŽ¯;·ª¹Š®@æ ž± ·tAc¼ìå0‘\”‚í] £ÈðJ°Û#õv#€Ô…ý:žFlÊ{3w«èXWÌ—êà6¼óÕ W€hÇWch¥Çc7Lºû¡[“á¢s@-PØÊìdͶÐ@ˆa¦3 ,6˜mþ|Äg rö1²óÉà—w jM+¥jAŠÁáÜ›©ŠãŸÑ‰OøÌ•³e ïSlؘ§ Š B©‡qÄà@ì²+hÿKGA¥Î¢õxøO®X”­ž”g àH=ƒ­Z3<„zf= JŽv”µ9Ùû°ØMŸì=§êƒýµ¬üȺ8f–¤/nhúâÒTÒÌ¥ù«Nò!/2å|bŒ¡såÿr~v‡È÷ÒœÌNðnwÒ„ìÁZš7ô·OtŠ çq¿¼/KEdÿ+5ÞI ¹¤Ý6¤IM“F ‰7nÏ \ȶ4×3x›içÝ9¾Ž?x¦ƒ8°S ú†*÷‡)=ßÕÐôäÕTK¯©Âºúæú‚<£o^ ´ä×ø*ÐyEA )ÁWT¹·Ò|¦V¾¥ÝΨ8 •Œ1OÇöçoðA Âàî—)wRÒPRÒwÔÑr^×è’‘¸ŸK“2©Š‚„ѯúnÈ÷°ƒYu}ÐòK^î'º¶$ q˜ûÔëNϨ5¦IuYí4I€ÇÃñiד8þ¨ld"Ÿ:? :æA? A#IúBHêœÎ6 l´ç¥ †æiÚÝÉsžFáÆOͧëÖÜ€ßC;ˆPMŠR/ÑÇ×1ðX3Œƒ‡ ï¦äo–ej\h=öSŠ|6ß3§øii²Ì/ã7§‹o•öW‰e¢ÊL'uVîþ³û7«“ÇÍâòKÊœ^RæµM­N´ÒNÏþŽÒ-òôï ǵ.®ÒVì¥yŽŒ<¡Øa tTç¸j5@£_fŒm$¹0– ï/è¨49ñ nòA*¸Ð½Y"?}ožXݱ£yx¿¦œÞÉ|MßÍW§Z‚µ-1|úÅÐg}Pòï[v„RQPžo¾‘Ï!G0×jpN2Ÿ$Ó_ ³ŸÒ¸mºfqÿ Þðnî]zΩD&ý\“jì¶h I ÁÄàn¼c3ï¹ÃÓ‰»÷»t˜+ìANÕU>‹2št¾JuVìÜ”óH¿}˜ œ{Pú£Þ÷ˆXSœÖ‹ !ó0ø$qWhüíâ?à¼endstream endobj 18 0 obj 2996 endobj 26 0 obj <> stream xœí]Ér$·½ó+úØA–j_®ápŒ/’GŒðÁÒ¡9Mr3CR3M[ú ùgü{®@&€ –î&GnòRѨB¡²ò垨_Wq”¤«¸û—ï?_|÷®Zݽè^½û«8ørñëEeÝ_ÿ?~ÿyõ溽0IVí<×wqÔ4uÜôƒíoq½ªâ:jÊÕõç‹®?o®ê¨Œ«ªXo7WEWI“¯¿lÒ(+›8[Ü\µežTëhs•FEVdåzÅ«(¯³ª^¿UGw›«¤/ò‚Ÿ¹WãèV·tøû¦‰â2­R~\»ˆz»É£&o]?ÐøŽŸ*ïÿØŸY穾R9̈"î·‹ÚÑá/›$*긥ɳš‰]tÙÏ”êéË¢»§º|xöFlx«&z Ùø-ßñSå=í‡+*1\ðŸè!>uGUÕÎr»ùùúoi™FuÚqáõ®å>µºß{Æüø<æW6~IÄžŽŸAëºGϼ¥ÃOêL6ÕƒºˆÁ†ÙkBod sѨ)óDñPÖÞh«Æ÷4¾¥ñÕ&­ê(ŽÓŽŽW’WIÕ1Z÷·êVõGOöºåÌOêè‘Ø•1ñ½âlÆîô#ÃÈþgw“æMͯzDxbã´¨:sëÀ€¶¶¹@Bæ°¨¦J“õOtFK£«w¹c¯? ñ=dÀ÷Jö|€¬ÆÖB\y‹àÇæÿ`œY֩ƪ{ÿxE”å•ÞÍoÅÆÙb,±{ÑüåZ…˜¯»×Ò-ê»wi,tÉUZgQV'-CçQ™«üow¿ºŽ‹ºVW(í“´`Èò\>Ñ»öõ—iÒ$—áAö‚’ÿ_ê•Ý"žæd`'[‰±S‰§,¡Ø-Ðz(ƒç1£>!žžûÊhèT>+1Ý dJ]ÿ€Ä÷0œ–‰©û“¨©Ië0Õ 5Ñ\C¬yýѵϛÍUµ(‰ëõ÷›2*ÚgÊ××íª[³®nñ•Ð* ¡0z0}ñýæ*‹ò&Íù>;åƒy8êß›ö†ô<½#Ý-&3›–aÿ˦=Ê겑tîD§°ìŠÌ4Â*1L´BJà`® Ã&Ï0ªÛ©>šÜÓÏÀ 6v8GÀéžœ“a KÉ=ž+èG[H“siÀ¾U¬RL`hà¥'Ë»ˆÊ£8/$¤frêÖ4èõ±‰æPÛɱUŒÁM Enž‰îi[(™Ò†3ôÆ/…Óiâq 1äåM± æ˜ßÁY¡Ó²:%uÄìF¡” Wã®OЍ‡5ð L$—"ÇCNÉ šôÎŒƒô -¡äÊ**Zb¤–ƒ)@²QçW—óð,’ö;ÊHDv [Á÷ä¡Q=1ÐrŠ.\•Û"@b“’ ÀESb•.u,ŸÅ4ó4³±‡Â>ÏíTy’d ÔƒÊäÁ° m Ï} É‘5U2IrôЫZ×#NGæ rØ(ëœiÚxŒ£a¡Yà;5N„z¢CÔP%ÈÄPu«aõ4.ÿ¼auÃ(5ëBàÿÖ«8-Αõo)²~Ðð‡ß² ëjÌßñ¦?äÅQ;…DÞLU2zÑ+šÌ$ío[™x®IâÔÂÌ@ƒ™z Ƥòê½<3[ ƒÿs³ÿh'¯¢º(Kñ©SÆ hLˆïÿ8h“ºm&š‹v⪆ééÜ¿P@H=+žâÌ,â‡çTfvdŸ€ †éÍ0ÖaºØ Ø ÔîäF)Sa¾lÍf›ãàŒ:a‡Ô-g8:cxåflFýx‰„ÃÔæŸÜÖ#¦%ÃjÁGð¼9W'n÷€A·ˆçQä0¬)“0ÇK=@æRæˆa‡?lЍÊʸ8pÜl¾'‰}^¯')¨Ç44E•}ð  Ôʳ‚¤{„-†=”ÿ¯çSMèwÖÏ¿˜F=Å¥}¬tD£Ei^q—a‹ú–‘ xù&$o Õ2¡‚b ù[ïJZ`TEÿóÆ2MËK½)7ŽãgJ–@€†d›hdÅó=äwSþÑ’e}`[3½¨Zg-[¥*Ãt@÷ Æd—§K:9þ€ùÊšÙùaäÔ´aâÚ8i ’qÝ NÄØ wÌ‚¢P“š^°âüðpµ; Mkvå‡åM}éá1 `§Bã" ›éŠ©™fìiêL3?,-š— Iž\ž(6å³;8}¬10$ˆÒâ…r°3tÇÖ>­[i—ÈsfüÿˆQÏCÜuomêÄXN]k™dá…*p@ ¨´2äå$Ãs‘S —Ç:øÛY”¥ÊÄ9b…ÁD§ éÇwå^¢~‘©½¡üzVÖû­_‚` _Ïû`Œ‰à£…—"€Ö?ö¢a¾+HèØõtâUÊs]ëH%E9˜Y“sD±õ=8À™"˜9AùJ¹²‰`H™Í-É-º;6ÿlv7x†.Š긋sJ€iª˜šl­Ž‚Rõã!˦¦ë¾L“9s,-aÊ€+A'ÍA¶x˜rHÝá6œÃ md•(€x&~LÉÖ]tÊ ƒÅŽ `§¹Í0/$K ”q¥k.Öî,ÉU*âm"ÿíºI2Š‚úÇ̬úXA¢SM­)àñ¯@«ž1™ϦÏW?‘ô©JÁ'-ûgËñ`QžeîlRŸž nZ«ÅYJ Ëñ²F•ã-®)@:¦?5Et"›u‡ìl‡&W‰òöUÿáNž8 `í:»ŽH R/USB±ZjůÙª¼EƒU(ç‚@ÞY¯šÉ³¾™jЀŸÛ­wA Æ´è zªø•Šf8”'@˜¿[¯‰Š$;}(Ûvž×¸÷ʂٳìjW´Ù™ç¯pוå_ÞÚéÊ&³í4Óú\[TQJÊÀíºAÄ ƒè ÇÇgö Ú^TKÇê‡3M‡˜!‚vLG¼!¼‰¶¿¬ëŒ£¤¬Š¢ô3ew¨® eý^E§ °Ë¬·Êdˆ•×Áñ±&v^ªU°˜VQâÝi°RF¨¥y`¯ ‘/ot‡ØãT½çN­²V0ƒÜÅÂXâÝŽdðÄçv N?U6hLqš¯[ð5dƒSÏm ‰ŒA“¥QBq‚pìèÙ 2oA6ȸ|d0_s†=¥bä–ç‚TsAÞjð)à;åÔݘ“vNä±\Ⱦ;eq¤ddŠ5ìPRéUe…–6$䳃üòõ¸âÿ:Ñ«"õÙŒVZ‰1òì¼P ?ÊC ºRË (kQ?uêo¦o%†äTÜAÃ…LiV“$|hOxÑËä•LÇ`È¢_0zˆ5q”f9L+YŠÆ•TZ¬_IMŽ)móLÿ(šíE¤[ëÞ §jfvE¼Tȯ“¬üûìTœQM KÙ°õT¿ì\¡­¶õ&X\ ¦2j€õï©Ë[©­ƒ655ÛléÉ Ábi*‘R?NÇoÇä”ÍÎä 22ØaûpqVD¥ ’L{f0¦±x€NhQÚ>Ý!ZMah Zg§h0žœeIvìãÊ Á² ‘HÐÍM 1M ,Èy!§JoÍçÒv‡¬Ó~oF*Rr ¤a@:h§)²)]i!?`2j~UŸ«šÉð´| wY2±¤`‚&9†'Gf§Wcê¹,‘[Læ%jÐ&íøîüYz Y¤ƒîŠëf´²¨ˆçï?ùªš3&Öy+”%‘·2Ú_ãuØiRfy0<&l>4!Týj»WÒ2Z{°ˆº†¢ë™Îíº†—iÀeIÖÙ¢Œ Ìñ1wAFÞ+zºexºŸƒîç ûiƒî¶Ê„™_ZÎsëÚ“íãÝÒŒBu_j±Í( ¨v¨IÇu`ïay%¼ñžÙŒ¦Ò›Òø±¼…ñÛÚmONÀ÷f ´#í„2Ñco·‡-·UQ™•ÚsàÆAãÀ¡½ƒt Ö»O‚vý¸Ðf·erE>2ä]’AŒƒ¸Â80·jÔ¢„‹#B4Íìþ2*Ò}Y>YÐhgù:ZYJØØ,(”u$™l #‚ëtŸ9¬üf)’ÐF ¶œÏ2º4ªhoK º…iªcï ¸ íìøaL\q POZ‚÷@mã¶]×MB†‘@ˆÛ]o<*›'K«ðÓ°+ÚðàkŸžaž×–ÏÙ?q×Z!ô½‚SQ«Jà!n—æ¡ÕÝÀç •:†t¤½o¢§ÛǶaÚ#ë 13úøÞ­Ð’2 ^®ºÄ®›€>6gí ¶í- 3aåW1{_ÁÞ=jùmxx‰ñ[bÇ;xª•OaÕ–%1'„ë·.Nñý#a¾ëÞX/åò*j’L²Ë”O!¡».îl˜Ñc:*‹+0,%oš)¾ÚÊ0ø¾€díb¯‰>Açówl)ëbDsaÎßA:éUÉ—dAîù;H#ÜÛówàœGÜ¥èXßA:@1 óüi“™¿˜°Ú„‡|tËR6’5ìÌoå›?HpÜoZ_¥)«r­…X™²“Y›œü”£u5X;ᦆ™©–±U SÃnF¸Vn&gò„sùwQ¼§{tù40‡&»fZ㵡Ò;Z8HÐK펵iýCÂqƒ- SÍLYg‰¦Ð¬Ý -hÖwÄsD¯\ DÌ ƒË©ñpÄüf„#B ÍÖò†>$áósuon©÷*szÙhŠ)u’ûß%YªRqtØ_tàÙŸÏÈ%±¸Šî$fÝs?]ËÃ|•îcøúÊðµ°éˆúˆÀa§)<Å Æ@ÆýC_ád!MÏgŒ(1ߘi”çuèôD¨¾f'Ìé ýz³Ä@»«Üu¼ÊÕþ’êÞ2¤ÇòrL è¥Î5?=ƒÌ[s×ö"¾Æ–%Û8Óû ¤â:ý'êy©S: Áv´ÏGÁµ lèï?ñ^7v8XVà×j¬Qü´¡5Ãûx›…ÝêHÞÄ´‹Ì²BÖ3"_"HÇ$Ó”Ê|­Åº~ÔÞÀñß=eZLóAoxÜ-Ð8}/ë„E‰–þËõÅßÛÿÿ³kNendstream endobj 27 0 obj 4046 endobj 31 0 obj <> stream xœí]ݖܶ ¾ß§˜»îôxU‰”Hª¹ëINN›8Îæô"§ãñÚµ½»ñîæç5ú2}½òP3öØži&¾ˆV H”4øqQWXÔî_º¸z{ö‡gzq}æo/žý9^¼»>ûñÌTÒýçoàë«·‹?]ZFQ/l?—/ÎêªïMÝ{b³0½ý³ïº6nœË·g?œÿwyQWÆÔ1ç‹å¿.ÿfÙ›&²7¦ªeÛZæËµmûÝò¢­„­>ß,/ºªÖMßž_¹úNµ>„Ûï–¢’ª¯åù«eSuÚØ{Ë Qu²“êü×e_ÕJhaGÍ77û nÞ£¡PÛ zXàK]µFj“änŒÂ¬¨ÁľY¶UßÚ–ç×þÊ´©cGÒ ÙUgRw‰î¾ mk;ï͸/G'”éÚíTòň7pùàç,º÷yô[¸ÄºôS5õppó6K†RFèó'xªÎoD×Vê’³ü¼lêJµu 2“à‰ ™Wäô˜m(“§¯qÓ¤ þÅò¢±ü]§ùíòÂTªÖºÃ¢ ž¬ÔVì¾—Ø ˆk±Ú8\$ \4²2A ¨éo¯i†œoçË/ƒž½™2?¢ƒó"úÙÕcnzM×ùæŠäßdzì©mô—Àµ…ú ~—FûÐß×¥)ÿJÊ6]Ž…9”^¤`xÑ´VßÁ¼»DÏÚÎue:¥¬y­JcÄ 6¦pµ™ÚÑt[ÄÆPSKm{M™™¹ Ëžq³A M’îBsËë±ÃŒL¿E -ûÆ1”ˆ!ôðÇßžÄÀi¬[d?¥±!„_ÑÃÓš€t0È;£ 錾ÊóC ý8«ª™pK½ƒÈ01¬ý¸°ªb\%=k —@ÇÞ0ÁÖÓ•géÏá.‰Z¸`7rsGÇ&»ÃmI43i²*ÛÝœ¦+$¸Óï›ì~¤§˜¡ —Žךlú|lÔ˜C½$*…ØÑòBqî¯Ù“^¼g ÀÈf{ùþûa<Èl$FÉ7ÇqXþ¦UVÙl/¹ ®J^gtž‰KÒ·ÞàEgáLUׂƒ4ÈY ²6 ZoØÅwN®%dfY mÑ xB&0,Z¦dgdp˜âÐÉâwM»^Ïg3 ÆS ¦÷XAõŒ¿N‚èÈÉòP·e28ÑŠŠ @þV VaÕ©¦ê $²ñÔ<p„¬Í^3)‰'ç6[¥T´E¦Dù^w°jÐØõ”3ò$å V_Ð'Z|ó0n„`lXÂå¿ÏÂ_?»üýçÿ°Êm›F:…‘’‹÷š”¬v9|ˆV\‘ ±ÿÅ:щäh ZE¡õþ&‡zËÞuÊiâ«Ë³oÏtëÎ@Ô¢³öY4µjÊý]÷‹w›³[­dt?P!:ý…‚Fkì$ãuÀÌT&4yã ’ÓÉÔß±m&ò¨,– ‰ÝSA],çr>³÷ô>å>„RÝY‡RD,E£’XèCóm„qfG‡è·9t ¼A©„’e\!.çc”8õÙc”ùE¬'<¹ƒÃ§¾†pô=\2øÜúzºaj×Vsc¡Ø{;™™1J²I4ždqI47”|Kš|Ùý޲+çCîWÆ$VBˆá{¿Ï ¦w6-PÀ5µŒA(‹¿Bû¥’ ö~ @`“ý¨ÕÇÈ ÀGÚQG]Ò’Ïn6?*ÈoZŒžt­ÌϬ¸ª6vS®¬eìÂëÈ7ÖHº‘¢v:hÝ¥Ou%;¥BŽ·º×MÛ㦷p9hƒ‚’6 ‰fáX;L¼"YÐ%j`ÁµË¯ùïÂâ$·spw…ª›¨ž4Ê%ie[);½ÃýåË`Öh­lÆFlïìÚ±–kŒ;ýµ—uÓy¶2èªé,p’ŽÞ7X?k<@V ÒÚ ¸[Ô’iCvŒæò<º­‡6­ÆL¹óØg@1;Í‹&ì\Û|@*¥Ïôˆ¤A†h;Šã¤x“ßMïµÍQVЦw«¢ŸÎ½ìû³‰“sWÏâ—üwª 48(4´!fm#z‹-òeXÕª7ŒÈ«±ÛÛ(äà¡Ø6zÈ˹b'´¶uû»‹auã¥ó˜?[Ðc²u^raawíÄ 3ìüž5˱) ³!‡A—„> ÁV¢~+ÑZ¡2î"$ÞMlÓBA‹N„æƒu¡KnKܺÓ%nm7@<·¦-p[²)q›¦)q›¦+pÛ}—)pKÙŠÄ- n£Jj—F×…±[ÙË÷ 9[]Ú &Š0ÚßD6åÉÞh,9X…'{µ³ä WžìÇ’ÃÔYòVÛç9€Q›ª6`< ñG딫\àZå«u¾¢“Ò‹qPvMÑ‘DúMAžä/¾ñÑ1‹ßEñ›NV6xvMDÿÊõÓK#ÜnÊfÆÚØIÀÕ­Ýa©^aâ-š²Ðª7~/ž.SÛtñÊ)E6Â?‰3• Ùa#’8rÃ<nõÔ;a£š@„MâX¥‹uºøu©ªÎ†C¼JÈùæûµíOÔµtx½­gÕAb]„Á×{ÔÒ\fÑïüE+*¶iBµ¦&È· øÇ°+¨1Ö?…Ç ¦jÛÚnžPjFÓ4ÝÜ4U[˜¥tnšÏsËU¾Zç«Áä&q-°J€+7¤AÓˆ©ÇŽ(5Ý•o9[-ü{1G;©ÄóÉ`g#~°s4Í\¶G6½Z Ƀ‹۩àdr‹3¬ò=¾CÚ?>€‡ÃÞ§zu]Ï=×¢ôØÐã¹#¤à¸=Ðc¹â`¸Ðã¹# á¸=Ðc¹^a¸Ð Ü,Ðã¹=ÐcÇNh‡5ZäÑZÑ€^ì¬Â“½Ú d§WžìW ;Íðd?už¼ ×T*?vÌ}¼Ñ"O'U)íódo–ÔΓ½^YrPOöšaÉaê,y/i¿ÉÙò;÷"‡]im|{gxºs59‰Ùí ñ“HNÜ™û¾Æ'Cò3:ÁˆÓËmXÊÓÉÑ^@Ïqž!%90sìú´ðÈO“ ~jXÙI1+]‹¬d;°’玆ãö°’åNø†á°’çŽð‡ãö°’åNèˆá°2p³°’çö°’;a+Öh‘G´ðtmúªZì­Â’ƒÚy²×+KŠãÉ^3,9L%ïVJ÷ýAãJÛsûŸLJcÇ?=™ÜáÉdñ˜@Á>X:=Ý;#œžîí ),Ó+÷eŽ’½ÿ‘Œt“‰be•’îwƒ¼sôöÚ8bŽ•©!&¾î¦ð̲!ƽëÔñ¼Në^ðö¹¥¬ò’ðeÖ rš{ê˜M„wù!žíÌÓYߢ€§Ùžæ¹#r+=å¹°+=-pGÜWz:Ës'XXz:¹Y<Ís{<ÍŽ@åÌÓYaœrS #Fxº@vVáÉ^í²Ó+OöŠ+fx²Ÿ:OÞ ž–ðmé{áéüÄvðí›m*;݉øñ›ô?1?îšæÏú\WÊÎÓ1ŸNdâDöô.ߎG€ï·æ˜ñþÿÏIl²Ãñ!ÿÏpk3ð̷ľù¥o‰#7‡ Ü1òc'Ø4ó±°õ7h1}°_ {«°ä vžìõÊ’ƒâx²× KSgÉ{AŒ5À¥C@ŒŸãŒÕ-ûÓ«€; Æ£9•Œͪ•.äåÙ[…%µód¯W–Ç“½fXr˜:KÞK^®!]~à.ýzôð mÒwLáuëƒÃ)…ŸRø)…ŸRø4…k5ó*ŠoQHál!…óÜ1Y”^Eá¹S.)½ŠRàŽ©¦ô* Ï2QéU”ÈͦpžÛ§pvì”Çf^E±áŸycZˆ™ÃúR - ®Á É\ÿÁsøÎ½çð2Û¹w,¶óàXlç‘ÌuüŽïÜûßy ³{·d;nÉvÉ\çÁkCç¬×ò2Û¹wjVòàÔlç‘Ìu>C~ïr€ ×5ZØDw+ü—kš=§è¨ˆ)ªy%á^QåÉR‹·¹{T ÑïɚĨ&Û# E¡jã BòÓÅ–Éââ¡Ø–ÓÄ|a.$TÛÚ®L Ó#YE›+ñÊ—Á–wJÕ!\Å"ª½+G– ™A1=R¤Aea¢1žÓ²¾j 5¯è2‡wxÖI„®Øœj©Áü©b‚Teó]Mƒ õ:ÏBõTQacÔ`\Z5hy¶ÈàkTT&VßÐK㊺!W4YD–ì ¹ŒKzZØ+YNuå…¶‰½Ãô+ï)µ ­EW*~C•×›¯GNÇ£êO’…;±ÛÐÅùžÛX>_“06ÖÊî•5_ÙØ]~ãp¼PM{N×T\‘wQ jû1«ëºÅOÐ*Ý^‘øb騈ï ëhEÍ¡uK˜÷Mwž*$ãl…jyÒÙ\PÛ†Š¸¨%™Áè …š¢:œë¼àKwT²v®x8|ÉÏ”?NÙìi¾êæ“à¤@ö¨÷Ø;ýŠBL^G!÷Õ•Ö¨‚xêô›å…Ýïö’žvè½V¬å×Á8Ï-H*tÚ™`ÖŒ;¦Á¥_o`MþÇ®´Nk¡»R" *G ´X^V» ¯aå>§rç/òràÖ‘IQS²S²˜5Ý)ä?ñŽÒ#‚3éÑǘL³æ~Ì®…ÁkÔÝFÃÆxWÒREå…LûTÚTm·-z€ŠV‘Öׄ®@+d±k ¦“G!>·°§ùñ×q¥óQC”ǹ0(-5H€ïÕ…`T@ÓÐù$rjœ“Ï#ðIî¼8Ÿ÷tÑhFab¢Ê}5QŠ7ðkÖÝaWÍ—Wd çbbE¶Ì:µ­!që4õo± 묭‘+ZßP=Ò^Íwî wiT{ÀÚ+ú ƒXÕ7³¹±´„f Uáƒ6Ðâ!KÁ£›™P’H5ßûAEuˆýƒc¢+¦$;‰*Ѱ!›¶l¾æ:€õóz:¬‹Óô` êez 3ªÿÑ‘Q9$Øô°Áþïwá‰Ã·gÿ¹ÀÞiendstream endobj 32 0 obj 4694 endobj 36 0 obj <> stream xœ­ZËrÛF¼ó+x 27x-G§ÊNœ8•ŠÍªìTŠ)9±DÊ–äÄ¿냳xíô½i[:Ú÷ºgzgñn©8YFõoÿp~½øîE±¼¼]4ÅË?tï/ï¥J럦ŸÏ¯—߯MÇ8^šqÖ‹HUUUMe¼,+óo¥—ETª*_®¯¯‚§áÊLšZçÁ!\iq•ïÃD¥y™%Á2\%J§º*‚¿ÂXé¢4Õ{iy×Õ§y°“Ò+ÛTž¤ûe˜©*3 ± s)Pº±ÜH/ë¶ÞU¥ó,.‚Ga¡²2-J‹­ê-têÆŠŠÄ³‚7l K;Õ:\ÅÆ®eQ?‡«L%e•n:ÕVFãçºì¶U×ï¥~‹Mëñ­û•ÔÞHýÎö‡A×íÎ H‚—¡VEšG:ˆ›^y™Ýc3À#é¶ ÿXÿ´HòLÅYfà³Þ´ÜÙÀ¬ËÈX Í+¸‘¶-®Œv©gR{eGû_›Â,ŽÓ ‡œÖ‡Vퟶ3“R£ÃJ?†•±^”ÔvJŠREQR›kÕÛk§ªlm&T¤l)ëîfX' x~jºWQêM%£¨¥ð€¼$S]±–8>ŒúÆf]e™ª-Ìâ dz&/ú÷; õ¼kwÞþmmäm[î¥Ô0kÇ@r5‡<‡š=ÞÆþ¤mÚp,‹U%=Ç6Â̤6ä–K˜ñ¡Áhâv¶í|+Ú0û9݈©ç– õ­ Ȫj ÌfýwŒîé¿§6y†²ÌÓ¾A¼Ï5³º±Òæý9,îÞ°˜žr7äsMH€;†™=åé?¡aÁ°þ‰ÆÓ-F$ÛTêG§^áu¸*Uå:ÅAeÛ0Ý!xöI°Ù2€Ç¹E*tž†ïG[;'ÿ…‘ÙNU$q.ã–´Ö$T5䃥àh.Vþ‰2“öüõijÞ(Ê%xü5\¥*«Rè½e~d†Éõ8%¹÷Û"=½$„<þa jvmäñƒ œ;/û:{û N†¢ ȶ-Àï;{îyS”v²·êÎÄ,ƒbé†Ï”n[¶ êO„‚-æS¯3päéœ,vÌÿ9>ývš?) Çç›Í˜•A7UxP¸&Rxmž­T-”Ž- …=tM4Ìì˜>”)/¤%sr0ºô{AÎ)Ú…‹ˆO­¼Ê+\€ì¤-Œ£ÃÝÀÖüxwâ ÚÖª@Æ aÒ܉q)êzy¢Ì@K! ä¡Ö(\¸£J¼ð1Ú>>“Çþe(/‡Ï±™ õÓÔ¦Ðòy”{‚>îÏ}r‰²pÞ#ÕÁy³NŸDjÕ÷- u®2ÝspK{˜ó,vOœ1ØÞ9Þp´&>pN¿;b–FVŸšíœàøõd±F±êS³E[›C¹¬„ØY 48“n-Ä7ãþ ĉÖô¥ž7<&AÓáj•1z5Xì•­ƒd@Â-;ÀN%“Òa¨ÇÃ=e}ÇŽƒîà Ò×õÎ#—ÝÇó!?:ªÎ?ÌìZ©š«"¯z~ú²($à•ÏÇÇüº…OØöƒ=cþÅC»é3äžÑò_ëàù[8Ù뤲=—‰{€Üû%lkgàä¬XÝŒ9aJϤà+xëNz#ÜÌöÛ1Zœq©˜e[kÛÍ S ®qÅb2zA ¼f`Œ²“öšèG“«s ºOœVE ýn—½ŽM+«c¤ätŒy‹Áá6æ¤ÁÈ9ûóòY2ÕÜ0©¨sÇ}÷fgb Ç:;ñc"€Þ¹"bÞI°’ŒÞ0.Ñl)»í€þ_!;Pœú`„•ÊÂâI—C X3Xßéó½p¢™Ä£!Æ6K=aŒh“¬ZU6ð$˜z§g¬lCЕNT$þÊ©>Ÿ\Ñfúì:{aâ»Q"mxØ%J«‰^¬ | Ž3nFÆø0À$>57‡ån¡—¤df= ¥º§‘\žRA_—sÂk6û?V0t’Àæ[ÌÑ®HLn?.mo)ÛÓýI,:ÞÏôcÛ¢ŽçeÚe£ÆáÉT¬ÿ^´ÿ=_¬¿}übu>ÀBGD3† 'Ío‘”Çžœbرõôð¹ÔK§3Û²±Q¹'fdâ(ë,fþ|Ó˜åe›RM²Â¦bŒ ÄX?††ŠI\ÉIydi®åƒ«Mšµn©-=*€èó6¸gþäfxí"ªWÈc3û<áŒmÎ*äj„ËDžŸ°üI¹ï$„ 3úb^6…‰£Ù| ž¯ÛCl¤b÷~„æ¡éG:¿R!QYÖA³ØGX¸Gð¶÷:ìݽí6X¯_ãùÕ¹Žé°P?<À¶6¦X¸³4Äï˜3¹ãDú"• ©tÐÁƒëZLpý•/43_p3\U:L¡LæRÁBô$´¤ø ²Ð¼Í=ëD28Ãô¢!Ýѵ-+ÍìV`ËZðó£YIÃ.mÎ?ÙÑã„ah@¯V±-ÚŠÈ ~æv­Ä¾ò Âvu$DeÀ”©çë½é„¨“£!MaNßÍ8Þs×NjÌ+“‰Áž[’{îzHî/“ŸÂ4QY¢“à÷p•ª,tÌ@û0Ñ%œ›lËÇòQ P奎â²NÝôIK¹X¥º”‡ú‰`I3_c<vò¦ÀãæÛ¬#®E`Ϥô—Ú8Yé^À¥Qæé%T&©Pç©cŸ‡o£ZÇ«ÖR1þd½øÍüþ®EÞendstream endobj 37 0 obj 2069 endobj 4 0 obj <> /Contents 5 0 R >> endobj 16 0 obj <> /Contents 17 0 R >> endobj 25 0 obj <> /Contents 26 0 R >> endobj 30 0 obj <> /Contents 31 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 16 0 R 25 0 R 30 0 R 35 0 R ] /Count 5 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 14 0 obj <> endobj 15 0 obj <> endobj 23 0 obj <> endobj 24 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <>stream xœ}W TWº®ºª¥l«8†ˆÜTĉ .(ÔV¶QQ¡µ7¶bën…Æ5Š b(ECDfâ .33êä)Ï—@2“¿Þ\æw«hfÎyïpèÓU}ë.ßÿ-I'B"‘È"2³w+Rw‹ßC_‰ð'aœ3‡b…¿þ7%G, ËQê&áÜœ97—c¿}Ãþ1N»ƒrá,‘䔉ÈÌRîVlß±×oâºÕ냂‚w¦Ìž=Û/Y9ð‹_dêÅö ¿ øË¾Ô´Ì¬ôÔŒ½sý"ðè´4ÅV¿íiʬ{ü’RRRSÄÇâ“ÒRwùE)ÒYY™ûü&FúM$#(JG}/[ «—¡aºa7‡Ç ïu5¸þÓ-Â-Êm»[‘ÛÏ#fŒ89¢w¤~äsw_÷õî§Ü¿å$¼)¼ãx!›—ÀœVhhq†Á*DÖ™Œ–­QÉ¢u¤R£ËWUéêØÇ}Ùáâ•*_¼¢a Y«7«òuê\…¡ O†b[…Z^Òóa6Ìr†&ÏHä/E4~N«RUiëXÈdz›,­IÉ.#¡J¨•ÖuUÆ‹F\ϙ̵¯PËÞ ÑtôÍ‚Ò/†Œ È-aÒYÙ#¤mÿðŠÏâ=Àƒ‡S­^ô˜+L—/$•öQul#z•HæØgµ²àEZMx.­1‡Myž7W­–Ñ—O|Ü`nöý„³êNäœÞgÙͥ˧ ”jü ONd­}¹¬ŒäÓ%_N[¹}ÿм‰<”À¼‹¼Ý,ìÜäIMñõ±œ FÞhÌÛPðºw¯¾©ýØz´Ž³ÈªÔUmq‰FËlHX¾'t ô7p~ÜNßu&ÅW°ôƒÜ2ƒ®Æ÷°±ÊÊÒÍ?Pôƒ:]ÕA†nÎÓi”¬íìÉ0 *ñáGñp/#dâã_‚É£ÆRƒd: ‚Bèt»0ü…'ÈWá§úò\2¸o¤¶ý ^ÒÉ Ãyg¸.DÈ‘ÇûÈîÙÞa$Œ ø/4†MvùÛó9ïMœ9Çßæ³ÞžÏ~fmÏïæ¡——úcõm­ ´l…‘^´JP =òk:šÜÔ¹úB8>®ë$¼K·#÷wÀõñí¦?¶1tþÔnj°Ö´j-‰2¡WÞótŽ?0wN`À|¼êÓg?2v¶Á<<Ëÿ3àøc‚É\{ÙjÙŽ¾Úuø ³Ï¬­eaffŸ³/Êɶ˜éO8ŒzûàÊÒÏÀ5ä4jᆬ¨x¢nÈ{/ »0pâ¼GÝÝêúÑ~VXÌÃ$^"…9w¥¸>ïüŽïæ¶á3 E#‘ç¯þàû¸]m4kÌ}aqŽÝºkqöJÜÜûù—ñ.Ö7o½ãÞq]qG'Ë2Äu|‰K0Š—+¨A& ž=àãÙŒŸ.È“A™i6’|_¶\Ȇ÷¨®9¯iŸ|r/[LŸ61˜P‰¶á¹+09!aˆ¢¾"?¿Ütï_X|š¹ªç¸2Nv£T¥7©ÊØ4C”1•“MY¿˜ £…ô½ tHþLÖšûe…âÈ~n®úž‡T;;ý[å÷·xÑŸcc8%ÿDRiÒÔ6UÕ1àEÕi«æk1-‘?¤Rt;\ $sjLas-lß"Qˆù*Qˆ}ÙØ•dIç ë Œ¬ÓW©òô"¯?G®èjàÈixõÌ‹& C&Rá Ы Û•÷ÉÚjSuÍ!S.‹¢àw$ÝÜN—O\ö=ÎÕ:¹×¨+/6ˆ°˜cXrkØßË6gp¸¸ÌfÉþ‹_óðW,€˜ŽÂJx)§ŸÝÛ¼þÜb_ä‚dˆz.×ÏÝkc‘œŠ^».šAðïÙo…"Û¹‹µMÍ ç-ç8^†'™ØAÑ݃ !égh~–¼» “3|á!‹09»ºÞÚˆ˜Kì˜ÃìéC·X€«Tƒ€Ã7T®A]Sc°£½îÿB»·Õ¾ \f†´+ÆA(Œ¶ipÿehj󢫅HA.ßq.ñÈjNFG.NX¶«.㌒=“{F×¥»©;­;:¯v7·WFWÇ/K ›±¤ù c¤è¨ÃZ3v¸ebª°³ðuŽQ[ÃÐ%£ÉÊVS­Û®åÞÃz•¾¸ùäÂþÆ´ãì®c©Õ«ŒÓªôeÙ<×>Ë¡ÚÚ±­7Ïw=þ"ý·• =þ@™Q{Ø×b4c®Tÿ‘¢Çc T1tõA ^ÁŽX¿qÙ0;…2›V°ãÐ}Ö-v¦‹¼o²ÎÜA"˜ÈjË»×Þä|Z¸ã8:N DG"åЯ—µDì:Ï—~›´7vCtü‚ËÚê 1EÁ•²êMyyz1U]9ôÖ…Ž“ ¿Jxó=—ΚµÕê‚¢âB“¼gIîN•t¡…zÈt¸ç€~' œÇÇúVTÀ´!ÅŽ^¡°!6ó?v ÛLa:™(|'?¨Ó*ÙÕ.Âaæi+Â-œ!&3`ú¯ìòþ¸5©×‹Þ+,÷|ìˆI:G!¸aÍ 9C I_^µfUúRß [ùk,Üv$lþuÞõõ¾wçLÇM›ià% rHv†×ž0Gp“¶ ¡`c÷÷I€^I÷“(^IÿàHù¡ƒxæõ¹I 1¯À§§è 4b½è>ˆ€¥ò+— -‘f‘ô?“WÑVx5†'‘D[á#˜^Ò6’îs¬åéXë8‰¡©àì`Âõ>k¼cD·Ý'…ùì%êK4ØÁð>ëÿS¢"DòàÍ{tòp›.ÎÝ\a¦ç©CÞZ¨'FñÁ¶eùʵ̡½ž;$S›4Õe¥¥ÌÓgŒ9Ù·ŸïÜÄFRqGÒ [p˜žÆÒ׿^ÿæáíÓ·¾b.»,]/¶*Q‰íw~úëgWõꋃDóh‡U¸]› î0ÍÁ| Ï!C3aªÔHÒOêì0á­Kœh#Ñ8䉯‚§ôŠ­h5ZCŽØÚHâù¦¢¸ ’~íÈGˆE“¸“ M’.s„¿¨Õš¡Íbg ,hÙÚü!†ƒ –Æ"Gšá–Q1pUkëüªû ¯À@Òü­KçO5ù6žSleу®! K›øû¥Û '0šMçv¦²´uð‰šÈÛ›¯}˜¼s߯&ûê®qI\†R‘ û÷> ÜyÜѶÃ,»! vQ$­ê³®sЪû5I·G¶ÝQtúˆw=0F¿Cî‹ÖîŠÜÈBÔïå?=™ó^àÜÙ“&Î{ÞÓó§g¶>E?éà€8È@ Y§³µöJÅ Ô`3 Ð21˜k,b0´½A@ øË#QyIQIWà“g9xÔb¬¨¯fà}¨•ö¯$ãUqè{ßA< ¯ER®´8 à9L 9#çpD iL…¦>F8ãæ˜Ê ¿t˹»u÷Ï^9Ùx¡®•kå•Ç [¬ÑÜrÙTŠ[ž¾%[¡ØŸ„¡ÝY¿§1ãŠò>w×ÞfIà¶ÚG‚ G±q tœÚGmV›ÊMå\Þ­Ò> b‡ôýCZ%¦¿³ùÅCXÉoÃíL%xñòrêÖÕ¯¿|õRF„¹?¢þ챯§?Ÿ´rMfÂ6&g*SÊ4UjsµÁXSÁ¹s÷³ûœìÑ7«¢s2CBYtí‘ÆQtœ0î1iµ729 ]¹Cìb„SP!ï›y…eåEœžÓé‹‹¼‹‹J ¹bNW©7€€6z#mÒpZ®¨´¨ ÿy—éE®œ«,­(—Ù€†ÞC˜ÍçØz"Ï䥥奥8¥¿¿súRû­±/ÿ X¿ñ@úVfÿ|%§–©«ÔÕ¦òòÃULCã5ëMNöävÂò2S’”ì>õ’Mœ,÷P%éÝßÅÌ  ]»¦þâfVWYX®çdjZ­:œß˜Ë¶g]U]ÃñM½}þÓýÍ_Í?ßïcxø‹è‚'ï,xÂ|93yH|ƃα Æ\s¼žn˜™$Êéó–n¦à$/Gy$&ãߥ'Å2 -6[§LZå(¬Ï\RX¢çŠ|UiªÍ••G*˜ß?ÿÐ' §öµ¸Ù4Õ3Mð™ˆ{xÃ<ð–ÀU&OIÏLÝÞþ k&/;séRÚ™TV ×ñýŒmÛ2ì÷Ï67§MOÔ •ra:ʯ,®,.ç| œ±¢ÌPVîͶÃPhPW¢_a£7àseE%gòáÊKÊ‹ñ¿w‘AW7Éá·†"±dp¡EÌ…oåVÜ·ä‹ÌøçtJiÐ`âVNÆ÷¶J+ÜÄú2ÕÔh X_qؾôùù&­•âÐ{C”ç°¹‘ûë…éõ8ëëÉO‡ó®Ÿº¹ñn#âÔ‚šO endstream endobj 41 0 obj 3526 endobj 42 0 obj <>stream xœQMkSAI¢Õ ®lñ.,~tQ¤íJ*!´ÁPÄ“÷îK.Lf†™ ö nÝ8‚6*´BBwÜ—”B‡RTŸA®-ÈÅR­2ò¤•Kà¥Î`J‘„Ç)š hA;"çb ä``…ò˜×@*•㬲ù\+Æêˆ"¥ºÚy—Z2¢c÷ÕëE~(|åë( óX™ét<ÂÈÿƒyAÊÇc_ùô2rFŠ"úF)ciÞÂØ‘,Ý[`q l&ÑÍu«­,烦ÆÈbÎÕóª¿þäÊ<éy¡²¨‹#:è,—ÿGaŒÕàcœyVç¼±òøòÛj|áCørÊ/?‡ÉƒÍݽ'›ÓÝÙl:½˜íý~þhõjûôfy«¼ÍØ5|Êã endstream endobj 43 0 obj 429 endobj 44 0 obj <>stream xœU’kPW†Ï&$îÔH-iªñ’ìtœŽV¬‚UZÆH%T@ n¢áRáÄHÁ‚á2VD Q)(¼r§ÔAmíhgZû㬳þèÆKΙÙ9{޳߻ïó}pb Ã\¤J2C©ÓÄÈ×íVš”t¥ãÔ•ZŽQ+XÔJ6¤5Ôo/9+í³Edž<§Ó+8fÔñªx-l Ë25ø$k³S5*µŽXºfíZ×w'nžžž„"û­Bø*Ó4ª$âf“¡$“µ‰Ê$ÝV‡¹M’šBEfkÕi„<6VëøL&'• „DCj´Úä bµÏÂ}÷uÌÃ=P“¨HO#öÊ“Ò"X©J'å©Ä.œ)ô? ÀKŠIÖ*SÓtšt9Ànà ö€ {Á>ü€ x ,bÀ' F07¬‘µ˜åÏzÂödG±­NQN ÔæL]ƒ*x\]ïB´/áw¡]”E@³_Z8óÜaÔÍáßE¬¦{w/·u©)>T”#ÞAÿ͈sÏXe8«„Ó²¾-Ûwie¡"~WÕMŽ3uL7C¹Î`Óv4øMõ¢¿3³*¡Q­Ùß§´çæ`è½”2ID †ÆàzQvVÝ})kÞ‡w¿»ÜßÜZÓ^3€{p=÷Eù@\Õ7;j™¶^wݰµC|ä¼:ø,,)3)^[ÎÙÑ„M}Hu >ƒžòovD~O³àfœfÏlGNO§ÏÏ ˆ.X…×2¯¨Û5ÖÈš ˆë¿L^ ÷ÚsÐO»_¬”„¨$÷²Í ÖÞj±‰»î tÝ‚ø[¬£Xå´F0ÏBím~RÆ pNu^é¿ÕþügÈñ¤SDT”>%F”™«×kq~WÝ<Ç‹ËH ‚©žÐM!¯ ãvî÷ƒ¸÷ÛÜPíÍV&ám[ûOo ‘tm˜Áæ_GBz±ÖÔ[Ú [„O·LÑàs…t¿(Â?ŽæB/Ò¸ï|\‡¦'g"|>Ù†ƒP%‘©¤ßÙ÷Àb¨8a× šŸýâc—¿Ì/),Î;£‡Ì,xÛ™x| ’¾›ƒëè ‡­9 #VQ݃¥úãßò`ŒL ðÅù–Úûÿ5Ýeø iG2{À£%ü ª’r”Z &X…ß“ö~ꘫ©Ôi¡0v$=×Nf-µ˜˜òpþ“ò‚²CË`v™£O͈ÖG3-m±•L† ±;÷¨®$æà[ï…#­y6ƒÇŸÛØ Ú~šÃŸØ[Tß¼ ÖŸ:SWÛÔØZÑñV]l!3ß2¹Útã”ëmì3,&–;· ­âxpIzÇ—{XÊãn{É-Î/Ƀù°öÄëÛŒ'ªE5dáQÆT˜~¸²Æh(5”‰g)6ç×¥2 2ëQ@=†ª™‚Ü7¤Ðú‰Ì[ú*fïhÞx˜ãž ?šÊü±) cêOÛ8| t¾_m^¢nýá¤Át¼Bü3µ€7½\ýê7ôÂWg›®‹øÑ¹ÁœWÅ(·iì¦Í>fS}WÅE°â)¹¦úÐh0ŠGQY „°BxGrécZ¼yÍÎÍsMgË«««ºÍ!>tYWTâÀr,»@‘•ç](¤—åÊóÃÈÐøD ·ö‡?îè.¯ê«4W\èÄ -PœMí­4²3¢ã†2fá4@>`£l#ăšº­§­­uâ&sëɾò?ªNzÌ8ƒÝY×HY˜ü¹³ïÙΖòxü 2µ™z endstream endobj 45 0 obj 1364 endobj 46 0 obj <>stream xœU”PSWÇï#ÉË ÏW¤Ñäi …þ^+ºN%±b‹e·)Ä$š,D H~T©\Ô‚(Ž D¡"Ц"¶ê,"?:»•N×Ú»[º»´ÔµœÇ\º³70íÎþóæ½sÞýÞ{>ç{ƒä>ˆa˜ùqK®ÁaÎÐGÅØ-™ÞP¤¤b¤E>Òb&¶©º)§b1z©nlö“a?ù…Eì§ÐÇçCÁSHÆ0ùå5[ìYÎl³ÑäÃ^Ù•ù¿ÈÊèèhñ ç/q«!Çl´‰¡ô%×`±gY 6Çq ýÛb1gˆF‹3Ë”#ê33 ™Þe¯ê-†ý¢Îl1geÙsŰ-áâª+VFÑǪfërÄd½-G|Iôžÿÿ"¡@[¢=3ËmÌÙå0'ÇÅ[ô"BIhÚ‰bÑËH‡’QÚV£WPZƒÂ‘%¢Ä žÒAr´ •¢&‚y›÷Ñú4ËV˺e“òEòJù-ùçò'ŠrÅ1hñ—»¥(˜ksöƒ¿1ˆ÷ôKíÂt;ýUò£÷½9QED²üö"‡àþ÷ëïÜÔÄ(Rûª©¡³HMBØ }­íŠõšénÁ”Õ ßý x Õ¹E5H%AÂÈ͘çÃ÷ÄÅħŽÿøá'C3xÀž€O`Ý7T$À[À„tLòŽ»z˜„P@‹×êˆÌxÚRŸ«©Ï¯+¹YRòcmE5EžÙ—™—`´UÕ8Õ…§Ÿ>|‘þΞ ¾_'Â:ü)þ¸¶£µ£õÝnÜ‹GLž­nÛ³0³¦ø$nà.µºn¨ù‰Aü^NE*ç?µ»Á ° ?-ÔB$YáA¼pU€Mì@óvÆÅå‡4åù؉‹ð¶êMüæë8Sr·–Áþ²SË:¶vÞÁÅ·ózMWö]N¯MÄÜ´jæVH«äT?^y Ÿ+;WÈñOÎVg›ŸÁ¦b{AáÁ7󎼎9øJŠc¾˜½1MÂF¡Ñ-&_²k=úo[ŸlT+ß:ns}œFgÉÁÂRM~1ƺ¢äâ…¢3e7Öâô3†Æ,Ž÷ìo¹žß¥º‹o5Ýèáø|l?^|æ ‘,ÙV÷¬ÏHßúš­±ãjƒëvº«º½ªòXíÉ`z\Çy©Ý]içYðõ…%sÁ·ÚÏ–œò›‡ÐÑjUD endstream endobj 47 0 obj 1621 endobj 48 0 obj <>stream xœuW \×ÖŸ23*‹’4A° Š€€»€²Ò€ˆ ‚("»,";¬[mõB] •º(‚ÖTв(î ´hY Ÿ¶ÚžIoüúÝ }ï}ý½÷ý’ÌÜ™¹çä,ÿó?g”Î0J ŒðŠŒOL 㯬9S7n7^˜†ÿ˜¡Î§ä‡žë!]!ÒÕ)'ÞgÈyކ+9Š ™ùÜ6$e%ÇFǤšY®ð´š:Õúßw¦Ï™3Çl]Ö?Ÿ˜¹G¦ÄF'šM"‹ôÈø I ‘‰©óÌÜÈîøøØp³èø¬¤˜³°ˆˆÈ^, ,>2ÎlQl|lRÒ†t3K7+3{;»éÓÈÁÞ76a]ZŠÙ²°Ä3¹™dtZ|XòßnRå._˜•îãš½!Â7)r‰ûÆ(?äèE)1þ©±Ë¼ÒÖ/_œ·"#>ì“Ì„uû,­¦šYO³±›>Éþk‡MŽ3 fÎ »wEM£Ì©`j åNÙPÔ*Êò l©¥Ô"ÊŽú˜ò§<©éÔ$jåEM¦–S‹)jåM9RVTõ 5ƒšBRrj&µ’ò¡\)k*ˆò¥Ü¨ÙK §FPº”€Ò£ô)j4eH1”˜Š¥>¢B) eDePÆÔjeJ-¤ÆQ$c”%%rêL„^38¬fX¯p¾ð ðwG¤sK4Bd/J¡E´]@·1nLsˆµdýÙ|ö6ÛÏþ1|üð¥Ã÷¿9¼sDʈŠÝ#çŒÜ3òÞÈA] Ý`ÝͺÛtËu¿×½¯'ÓsÓëÕw×ßi0Â`Á>ƒ×£G•j¥õçhÙè¥pJŸkD 5S,èR a¡:Abó! R` ­¯¶E%ÜÁÙ ¸DØ y‚7ˆÞѸ@ º4.ú úÝDðu‡çР ÏDúÜ—8Q¡þˆÈŒáJ$øc56á¬Eö4xhJãfoŠÊÌAc?C¹ùY»ÙÌ¡­‡¾8ŠN Š‚£ËŽ8z°rÕúcx³J¸¥m1ņ­íÎ=ðU·±øb+Q(>­)éjFÜöûÖÞ‡Õár)þ³›3cú¼oLr Jp]-_T2ÄŒÔvκ]pªOÈÅÁ[ÉW6ŸÎ¨ˆ®—W¸"O°Å:Ø; ˜9Œî|Êfѹ³Ö†¹#ÖvùÆ×:ž·^ZçZ(ÓW?F%j÷bÁ%·O)äbŒ Œ‘”ê,ap̯6aɇ%4„k@ÄGO»£0ìTA§ÊWe,æ:@A· +GÎW³â÷窊¯Þ4AÊðF›s¬˜{xêìµÇ&¨1£6òLäÙ ƒÄÐzZ…I ¼˜«¨bûñ̲ŒoP8ŠÈߘ‘œ™¸=lò½Ø ÌetróÑ¢ùhêþ¸hóY\ZjZJÂæ5ˆ%Amœ~›€[B¨1u…Ë4~¨Ñ-àœÛp8=’½Æ¡°ëƒÆ>!çÎIðd{sìŽÝ•æ`“ÞÃXìøO•å9K^ÜpÆFØ`©“­;ˆÁðf‡JÆ+éX^ÂÉ;À¹Ø°¬®’äåpÛ¸fãctLã²rÞµqöX€çÉÄõØ£ ` 'š.KÅ9‹38› “ Üœ‡Góõ–Îs°õ} ú ÷és©¾:j(?)9©R¨6!z4*Z3šS‰&ñ)PÒœžF)zOk-á¦t]ãQt¥ÏX|úN”ô@}iE}J3bÁ´@ìh÷0 ËÜýã|"ˆ:qdË%?ß™‹G㑾sí–vÜêRjl¯fpl\ìr…°I‚º>¯Ë9Û;ïªñoÒ4‚±xÁó 0t»›.!K]í…Кc.eœÜ~r×U6¿Y²oðöýÄ*î{ÎØ‰vîÚI0w$UÁ *U*î€JÈUgIp$6Ç68g9C”²óDÃmYç£Z!ÉÂgXÓðzé°ÿëb9v7''lkAJÓ ¼Þ‘“ŒŸ‚»§ô«¸q*a¿œ¡A"B¤cb>C«>˜J¸{°€‰?MÁ¾Xîb…'ÊþOHÿÊl;·DT|Y½Ðƒ·_ð;¹ÈK§c©0ç^s¶5”Ý\µï+´ëˆ´…Éܽ9/±Î«CÈì=<[5!Ý\H3DSP ô3]•Äû(cqe ÏI<'gГ#õ*XqKÙ‘ã{oå±Ï˜Üüíy›Ñ ´.n•+®|Ix©'¶Âh¾Èˆ’Š>¸H´<åìˆF^ÿbÓÚ>ØJÕ›Ó—ìúÕóÖ#8ן/Ÿ2§:F%È:2oå–¦£È±ÁÁë]×DM—n:ðù•¬½ë5/…ñ¤àÆô4½í­±8.›wÄó`âQT9¶®æTó£s‰Ëó¥ÿàR,hí&¼/l%õý!¡›p>Ïø'NlÍ)–Ë.LFQìó+å×?^¸&õ“`)œd´ìßóÔk¨R˜;h,~¾@Xt;Ý]>]&ævy£5_‡%ŒQ)sl{ÙÎÁ] ›ynþŠ §¯t›€áül!ÅýZ¼˜é~M#|r[iÆñô¢õ(”a)%¸Mmã¦4 žÐî#Iœgã‰îž?hÌè€s MÅÅù{Ê¥­Ìæ/7íú±Ñ[ +e€{}H&~fƒO±à‘BdgkJºiu‚Î,ó¾ûxÛaX§\¬‚×JO_i*n¿dXXuãù$òŽ–sðûÚe¯’e°x•HÜÿsJu”¿ ŠHOKÌX½ÙÍG+ŽÄT'}÷ÙÙ¼Ë$ y^E¡á ž=Ñ D}è‡âÚsõçO5¡ûh`ÙCËø“ú1âv‡cqå7L7Õ¿¶)À:oˆqyˆÔ º_p#Õ”àg*u‚ nÖŒ„f25¼€g<¼SÛ‚x¿ ›ºa7a‡L.݈+™ñe³eŽ‹Ã.Ü—rÌl%ãØ´ì—öú-õRq¦+œøX=N‘V’v¾K 2ÙÙ+¿D(/[êÎîܳ«±/ê«~©õÓ™mÞMk¬¦ø–À÷wµ©¶)4-s`ûS]žXønñæ…”¦Ky@k£/€*#XÍ•ˆ¦ÑØRã„Í8'²¹¦DôÏ-¼9·H‚&ðí‹ÇáZúixàJѯ4^¯À‹ÌhœŒ“IÓJU¨¥Å‚g*m”ꉌ¦€±r_‚‡¯-:#.Ï®D×Y®À†ÜÇ–¿ÙÂBpa/`’”+/owÅl´fYh¬'’ñécz²ÆÚŒ,li°&Ö½ ßr¶¿ilEZñþ5÷Ášü߬ =ÚÆ×êÑÆÙµV÷A‘6ßÕ’Þ¹õ ´Ã4ñÓe2¸Ë¨<°ÄEž#MIÚ’°3í¡÷=¨ªè@ì“ê A²4Ťç,Þ†Gäd}·É79>y²âLë‡K~{ØXzí¶to@YÊ5tåŸØGZxJІmÙÉ©±ñë> F¬w䩯ë•åýE2åþûˋX-[óæv’\-€¯%0’| 5#D0ŒÆœ;4@ƒv¼üS#q"‚+óïl]†cp‡½|¾ ±?6‘5 n¸ûàbÑKÆÂj0Æ«Eƒ´vlûk„ì$þk,®×ൂ?ýéúÝæ{g#½¤øƒ¿¼{.b1Ég^¬¸jå–î,¿vÌy Õ×ÈYqýcæïεn¸J‹œkZ¢zòˆ×û^hLl3éi«®à<ã=–º­Lò‹A“p‰D54Søü}¦øÃ}(8ê#ð!c¶M4ýx.×/šLã™d9ž,-hXBfï_h˜Å½‚ùšWÕAÅ‚*ábƒE“·47)fÉÜÙsú{Ôž%åÕËoi{*„ÍdÏdl,ÇÞOGØþ>ö~ƒÙë´XÃ|ŽêÁI æì_bàöT¨Â1’7`|¼LG`/ïÉ`Ì.§•Ø¢žðvDØ!;9`"Æ=ÝR¾­(lr§ X‚Ã!܇ãp ‡V —|m’/ž\Õ_,è{/ì{'±¸“Å E›2BvÛ[Ï^î•.o:lÿQ•Ð$Á[Ý`«¶¨ß úÞ a‘±x§Å{×Ñ!¨€Ê!”«ƒ$‹Ñ’ĨàÕ>©Öˆx‡·ua1XÃi|¹ Mû9ä4ªÁ¯Ü ±N:/k§4­^=ÍÊ&ä5@@íË—2m+R“·دÂ~._¢ÉWªƒ–2ñX‘ƒÇe³r~úÐ"áZ ¯ö|©€…e]Æâ(n ôIò¢?[•ƒÇ¬1ïnŸ©A?²?{6YKʼnÖ.‹BŽDÞs’‰kð(·È ;¬«˜õµE¥ç¤%E‡TU²âÄ{N"ÐX¦VÞ,yˆîŒÅ%¾­[¡³ìÕ3g®6TF¯INt"o,®•ˆkü–­M 4uó»ÕzãÔ…ç·´ôqÕín΀ûªO¢NÜú®²ïÊ›kcþqíJé·S®‡U…UöBÓ‘W\Ä’äÈÍ¡;ÝX%ýUÝÞŠý¥¥5ß—5"¶ëŽ¿Ë²õ«äÑ2Û@l9k­×6l7–‹ç'¢ºËkTMäg,n„ ÜFÉ;FܸŠÞuó@ù™=l³dÇú4ŸÄˆí`·Ñ»UÌ>ÑÖ”>œvÃZ% , /K°:$ßЕy"ñûÁ†Ð™öËl§†Ôýú¹ŒLþ…Q‡âÎy¶Æ ’‰bÒ‹>0ˆi˜YAšûÃ²Ó ?˜€þÜ6lŠ-\œðÔ/¥J:¯f÷±oŽŸ¿tì:b;jW/ÊJˆøT–²%þ Ÿ]<^¡¤ùì#·ªQÈ­âl%y9iQ(}º'c?Þ2&»0soòF‘+wLcéüÒüÂÝ……GF…¨t[áç¯åcÊ>=¹å8b\lh—d´ð«NNNÔEÔÎì¬ç÷æÜZo?Ñø‹l¸Ú)ÁÉ4$Ã}ÑP ú:À¶OÀÍ#9K"@Ö £4F¢§ôÅú3‡ˆõ/ïx›ìç3Õ9ä|W:‰Åþ¥…J6^Xñc|‰…Å›w0Ìl^áqAá›ÖÉÊ!Xu|¢Ôņõª5—šÉÏXü¬žè¶`ÄÏêèÿ¤ qÑ#£ÿÎ#Z ïvt—ôv.õC 1ráÔl`]Z0…œGd˜ýŽËòÓµ{ƒ€{‚ ÉìÄ 1´¼À0Âë‡Ø‚+ÑÑ’$7‘.dÏCö¿¢ò/nâfóâï…GüäÍqËÿÃT©¥\ ?1ÈKéŽÝ#;öèê>+ÐÕ£¨ÿ­XÒì endstream endobj 49 0 obj 4364 endobj 13 0 obj <> endobj 20 0 obj <> endobj 50 0 obj <> endobj 22 0 obj <> endobj 9 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 19 0 obj <> endobj 21 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 2 0 obj <>endobj xref 0 51 0000000000 65535 f 0000017310 00000 n 0000033419 00000 n 0000017214 00000 n 0000016406 00000 n 0000000015 00000 n 0000002209 00000 n 0000017358 00000 n 0000032672 00000 n 0000030944 00000 n 0000032929 00000 n 0000031308 00000 n 0000031833 00000 n 0000029622 00000 n 0000017399 00000 n 0000017429 00000 n 0000016566 00000 n 0000002229 00000 n 0000005297 00000 n 0000032208 00000 n 0000030045 00000 n 0000032424 00000 n 0000030585 00000 n 0000017481 00000 n 0000017511 00000 n 0000016728 00000 n 0000005318 00000 n 0000009436 00000 n 0000017576 00000 n 0000017606 00000 n 0000016890 00000 n 0000009457 00000 n 0000014223 00000 n 0000017649 00000 n 0000017679 00000 n 0000017052 00000 n 0000014244 00000 n 0000016385 00000 n 0000017722 00000 n 0000017752 00000 n 0000017784 00000 n 0000021396 00000 n 0000021417 00000 n 0000021932 00000 n 0000021952 00000 n 0000023402 00000 n 0000023423 00000 n 0000025130 00000 n 0000025151 00000 n 0000029601 00000 n 0000030496 00000 n trailer << /Size 51 /Root 1 0 R /Info 2 0 R /ID [(bŠê×<Ò¦Ã7±\rà–¥)(bŠê×<Ò¦Ã7±\rà–¥)] >> startxref 33624 %%EOF simh-3.8.1/makefile0000644000175000017500000003220611111157514012341 0ustar vlmvlm# # CC Command # ifeq ($(WIN32),) #Unix Environments ifneq (,$(findstring solaris,$(OSTYPE))) OS_CCDEFS = -lm -lsocket -lnsl -lrt -lpthread -D_GNU_SOURCE else ifneq (,$(findstring darwin,$(OSTYPE))) OS_CCDEFS = -D_GNU_SOURCE else OS_CCDEFS = -lrt -lm -D_GNU_SOURCE endif endif CC = gcc -std=c99 -U__STRICT_ANSI__ -g $(OS_CCDEFS) -I . ifeq ($(USE_NETWORK),) else NETWORK_OPT = -DUSE_NETWORK -isystem /usr/local/include /usr/local/lib/libpcap.a endif else #Win32 Environments LDFLAGS = -lm -lwsock32 -lwinmm CC = gcc -std=c99 -U__STRICT_ANSI__ -O2 -I. EXE = .exe ifeq ($(USE_NETWORK),) else NETWORK_OPT = -DUSE_NETWORK -lwpcap -lpacket endif endif # # Common Libraries # BIN = BIN/ SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \ sim_tmxr.c sim_ether.c sim_tape.c # # Emulator source files and compile time options # PDP1D = PDP1 PDP1 = ${PDP1D}/pdp1_lp.c ${PDP1D}/pdp1_cpu.c ${PDP1D}/pdp1_stddev.c \ ${PDP1D}/pdp1_sys.c ${PDP1D}/pdp1_dt.c ${PDP1D}/pdp1_drm.c \ ${PDP1D}/pdp1_clk.c ${PDP1D}/pdp1_dcs.c PDP1_OPT = -I ${PDP1D} NOVAD = NOVA NOVA = ${NOVAD}/nova_sys.c ${NOVAD}/nova_cpu.c ${NOVAD}/nova_dkp.c \ ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c ${NOVAD}/nova_mta.c \ ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c ${NOVAD}/nova_clk.c \ ${NOVAD}/nova_tt.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c NOVA_OPT = -I ${NOVAD} ECLIPSE = ${NOVAD}/eclipse_cpu.c ${NOVAD}/eclipse_tt.c ${NOVAD}/nova_sys.c \ ${NOVAD}/nova_dkp.c ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c \ ${NOVAD}/nova_mta.c ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c \ ${NOVAD}/nova_clk.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE -DUSE_INT64 PDP18BD = PDP18B PDP18B = ${PDP18BD}/pdp18b_dt.c ${PDP18BD}/pdp18b_drm.c ${PDP18BD}/pdp18b_cpu.c \ ${PDP18BD}/pdp18b_lp.c ${PDP18BD}/pdp18b_mt.c ${PDP18BD}/pdp18b_rf.c \ ${PDP18BD}/pdp18b_rp.c ${PDP18BD}/pdp18b_stddev.c ${PDP18BD}/pdp18b_sys.c \ ${PDP18BD}/pdp18b_rb.c ${PDP18BD}/pdp18b_tt1.c ${PDP18BD}/pdp18b_fpp.c PDP4_OPT = -DPDP4 -I ${PDP18BD} PDP7_OPT = -DPDP7 -I ${PDP18BD} PDP9_OPT = -DPDP9 -I ${PDP18BD} PDP15_OPT = -DPDP15 -I ${PDP18BD} PDP11D = PDP11 PDP11 = ${PDP11D}/pdp11_fp.c ${PDP11D}/pdp11_cpu.c ${PDP11D}/pdp11_dz.c \ ${PDP11D}/pdp11_cis.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_rx.c \ ${PDP11D}/pdp11_stddev.c ${PDP11D}/pdp11_sys.c ${PDP11D}/pdp11_tc.c \ ${PDP11D}/pdp11_tm.c ${PDP11D}/pdp11_ts.c ${PDP11D}/pdp11_io.c \ ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_tq.c ${PDP11D}/pdp11_pclk.c \ ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_hk.c \ ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_vh.c \ ${PDP11D}/pdp11_rh.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_cpumod.c \ ${PDP11D}/pdp11_cr.c ${PDP11D}/pdp11_rf.c ${PDP11D}/pdp11_dl.c \ ${PDP11D}/pdp11_ta.c ${PDP11D}/pdp11_rc.c ${PDP11D}/pdp11_kg.c \ ${PDP11D}/pdp11_ke.c ${PDP11D}/pdp11_dc.c ${PDP11D}/pdp11_io_lib.c PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} VAXD = VAX VAX = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c ${VAXD}/vax_io.c \ ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ ${VAXD}/vax_mmu.c ${VAXD}/vax_stddev.c ${VAXD}/vax_sysdev.c \ ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c ${VAXD}/vax_syslist.c \ ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_vh.c \ ${PDP11D}/pdp11_cr.c ${PDP11D}/pdp11_io_lib.c VAX_OPT = -DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} VAX780 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ ${VAXD}/vax780_stddev.c ${VAXD}/vax780_sbi.c \ ${VAXD}/vax780_mem.c ${VAXD}/vax780_uba.c ${VAXD}/vax780_mba.c \ ${VAXD}/vax780_fload.c ${VAXD}/vax780_syslist.c \ ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_hk.c \ ${PDP11D}/pdp11_io_lib.c VAX780_OPT = -DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} PDP10D = PDP10 PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ ${PDP10D}/pdp10_ksio.c ${PDP10D}/pdp10_lp20.c ${PDP10D}/pdp10_mdfp.c \ ${PDP10D}/pdp10_pag.c ${PDP10D}/pdp10_rp.c ${PDP10D}/pdp10_sys.c \ ${PDP10D}/pdp10_tim.c ${PDP10D}/pdp10_tu.c ${PDP10D}/pdp10_xtnd.c \ ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_xu.c \ ${PDP11D}/pdp11_cr.c PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} PDP8D = PDP8 PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ ${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \ ${PDP8D}/pdp8_pt.c ${PDP8D}/pdp8_rf.c ${PDP8D}/pdp8_rk.c \ ${PDP8D}/pdp8_rx.c ${PDP8D}/pdp8_sys.c ${PDP8D}/pdp8_tt.c \ ${PDP8D}/pdp8_ttx.c ${PDP8D}/pdp8_rl.c ${PDP8D}/pdp8_tsc.c \ ${PDP8D}/pdp8_td.c ${PDP8D}/pdp8_ct.c ${PDP8D}/pdp8_fpp.c PDP8_OPT = -I ${PDP8D} H316D = H316 H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ ${H316D}/h316_dp.c H316_OPT = -I ${H316D} HP2100D = HP2100 HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq.c \ ${HP2100D}/hp2100_dr.c ${HP2100D}/hp2100_lps.c ${HP2100D}/hp2100_ms.c \ ${HP2100D}/hp2100_mt.c ${HP2100D}/hp2100_mux.c ${HP2100D}/hp2100_cpu.c \ ${HP2100D}/hp2100_fp.c ${HP2100D}/hp2100_sys.c ${HP2100D}/hp2100_lpt.c \ ${HP2100D}/hp2100_ipl.c ${HP2100D}/hp2100_ds.c ${HP2100D}/hp2100_cpu0.c \ ${HP2100D}/hp2100_cpu1.c ${HP2100D}/hp2100_cpu2.c ${HP2100D}/hp2100_cpu3.c \ ${HP2100D}/hp2100_cpu4.c ${HP2100D}/hp2100_cpu5.c ${HP2100D}/hp2100_cpu6.c \ ${HP2100D}/hp2100_cpu7.c ${HP2100D}/hp2100_fp1.c ${HP2100D}/hp2100_baci.c \ ${HP2100D}/hp2100_mpx.c ${HP2100D}/hp2100_pif.c HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} I1401D = I1401 I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \ ${I1401D}/i1401_cd.c ${I1401D}/i1401_mt.c ${I1401D}/i1401_dp.c \ ${I1401D}/i1401_sys.c I1401_OPT = -I ${I1401D} I1620D = I1620 I1620 = ${I1620D}/i1620_cd.c ${I1620D}/i1620_dp.c ${I1620D}/i1620_pt.c \ ${I1620D}/i1620_tty.c ${I1620D}/i1620_cpu.c ${I1620D}/i1620_lp.c \ ${I1620D}/i1620_fp.c ${I1620D}/i1620_sys.c I1620_OPT = -I ${I1620D} I7094D = I7094 I7094 = ${I7094D}/i7094_cpu.c ${I7094D}/i7094_cpu1.c ${I7094D}/i7094_io.c \ ${I7094D}/i7094_cd.c ${I7094D}/i7094_clk.c ${I7094D}/i7094_com.c \ ${I7094D}/i7094_drm.c ${I7094D}/i7094_dsk.c ${I7094D}/i7094_sys.c \ ${I7094D}/i7094_lp.c ${I7094D}/i7094_mt.c ${I7094D}/i7094_binloader.c I7094_OPT = -DUSE_INT64 -I ${I7094D} IBM1130D = Ibm1130 IBM1130 = ${IBM1130D}/ibm1130_cpu.c ${IBM1130D}/ibm1130_cr.c \ ${IBM1130D}/ibm1130_disk.c ${IBM1130D}/ibm1130_stddev.c \ ${IBM1130D}/ibm1130_sys.c ${IBM1130D}/ibm1130_gdu.c \ ${IBM1130D}/ibm1130_gui.c ${IBM1130D}/ibm1130_prt.c \ ${IBM1130D}/ibm1130_fmt.c ${IBM1130D}/ibm1130_ptrp.c \ ${IBM1130D}/ibm1130_plot.c ${IBM1130D}/ibm1130_sca.c \ ${IBM1130D}/ibm1130_t2741.c IBM1130_OPT = -I ${IBM1130D} ID16D = Interdata ID16 = ${ID16D}/id16_cpu.c ${ID16D}/id16_sys.c ${ID16D}/id_dp.c \ ${ID16D}/id_fd.c ${ID16D}/id_fp.c ${ID16D}/id_idc.c ${ID16D}/id_io.c \ ${ID16D}/id_lp.c ${ID16D}/id_mt.c ${ID16D}/id_pas.c ${ID16D}/id_pt.c \ ${ID16D}/id_tt.c ${ID16D}/id_uvc.c ${ID16D}/id16_dboot.c ${ID16D}/id_ttp.c ID16_OPT = -I ${ID16D} ID32D = Interdata ID32 = ${ID32D}/id32_cpu.c ${ID32D}/id32_sys.c ${ID32D}/id_dp.c \ ${ID32D}/id_fd.c ${ID32D}/id_fp.c ${ID32D}/id_idc.c ${ID32D}/id_io.c \ ${ID32D}/id_lp.c ${ID32D}/id_mt.c ${ID32D}/id_pas.c ${ID32D}/id_pt.c \ ${ID32D}/id_tt.c ${ID32D}/id_uvc.c ${ID32D}/id32_dboot.c ${ID32D}/id_ttp.c ID32_OPT = -I ${ID32D} S3D = S3 S3 = ${S3D}/s3_cd.c ${S3D}/s3_cpu.c ${S3D}/s3_disk.c ${S3D}/s3_lp.c \ ${S3D}/s3_pkb.c ${S3D}/s3_sys.c S3_OPT = -I ${S3D} ALTAIRD = ALTAIR ALTAIR = ${ALTAIRD}/altair_sio.c ${ALTAIRD}/altair_cpu.c ${ALTAIRD}/altair_dsk.c \ ${ALTAIRD}/altair_sys.c ALTAIR_OPT = -I ${ALTAIRD} ALTAIRZ80D = AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ ${ALTAIRZ80D}/altairz80_dsk.c ${ALTAIRZ80D}/disasm.c \ ${ALTAIRZ80D}/altairz80_sio.c ${ALTAIRZ80D}/altairz80_sys.c \ ${ALTAIRZ80D}/altairz80_hdsk.c ${ALTAIRZ80D}/altairz80_net.c \ ${ALTAIRZ80D}/flashwriter2.c ${ALTAIRZ80D}/i86_decode.c \ ${ALTAIRZ80D}/i86_ops.c ${ALTAIRZ80D}/i86_prim_ops.c \ ${ALTAIRZ80D}/i8272.c ${ALTAIRZ80D}/insnsa.c ${ALTAIRZ80D}/insnsd.c \ ${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \ ${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c ${ALTAIRZ80D}/s100_disk3.c\ ${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \ ${ALTAIRZ80D}/s100_mdsad.c ${ALTAIRZ80D}/s100_selchan.c \ ${ALTAIRZ80D}/s100_ss1.c ${ALTAIRZ80D}/s100_64fdc.c \ ${ALTAIRZ80D}/s100_scp300f.c ${ALTAIRZ80D}/sim_imd.c \ ${ALTAIRZ80D}/wd179x.c ${ALTAIRZ80D}/s100_hdc1001.c \ ${ALTAIRZ80D}/s100_if3.c ${ALTAIRZ80D}/s100_adcs6.c ALTAIRZ80_OPT = -I ${ALTAIRZ80D} GRID = GRI GRI = ${GRID}/gri_cpu.c ${GRID}/gri_stddev.c ${GRID}/gri_sys.c GRI_OPT = -I ${GRID} LGPD = LGP LGP = ${LGPD}/lgp_cpu.c ${LGPD}/lgp_stddev.c ${LGPD}/lgp_sys.c LGP_OPT = -I ${LGPD} SDSD = SDS SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ ${SDSD}/sds_lp.c ${SDSD}/sds_mt.c ${SDSD}/sds_mux.c ${SDSD}/sds_rad.c \ ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c SDS_OPT = -I ${SDSD} # # Build everything # ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ vax vax780 nova eclipse hp2100 i1401 i1620 s3 \ altair altairz80 gri i1620 i7094 ibm1130 id16 \ id32 sds lgp h316 all : ${ALL} clean : ifeq ($(WIN32),) ${RM} ${BIN}* else if exist BIN\*.exe del /q BIN\*.exe endif # # Individual builds # pdp1 : ${BIN}pdp1${EXE} ${BIN}pdp1${EXE} : ${PDP1} ${SIM} ${CC} ${PDP1} ${SIM} ${PDP1_OPT} -o $@ ${LDFLAGS} pdp4 : ${BIN}pdp4${EXE} ${BIN}pdp4${EXE} : ${PDP18B} ${SIM} ${CC} ${PDP18B} ${SIM} ${PDP4_OPT} -o $@ ${LDFLAGS} pdp7 : ${BIN}pdp7${EXE} ${BIN}pdp7${EXE} : ${PDP18B} ${SIM} ${CC} ${PDP18B} ${SIM} ${PDP7_OPT} -o $@ ${LDFLAGS} pdp8 : ${BIN}pdp8${EXE} ${BIN}pdp8${EXE} : ${PDP8} ${SIM} ${CC} ${PDP8} ${SIM} ${PDP8_OPT} -o $@ ${LDFLAGS} pdp9 : ${BIN}pdp9${EXE} ${BIN}pdp9${EXE} : ${PDP18B} ${SIM} ${CC} ${PDP18B} ${SIM} ${PDP9_OPT} -o $@ ${LDFLAGS} pdp15 : ${BIN}pdp15${EXE} ${BIN}pdp15${EXE} : ${PDP18B} ${SIM} ${CC} ${PDP18B} ${SIM} ${PDP15_OPT} -o $@ ${LDFLAGS} pdp10 : ${BIN}pdp10${EXE} ${BIN}pdp10${EXE} : ${PDP10} ${SIM} ${CC} ${PDP10} ${SIM} ${PDP10_OPT} -o $@ ${LDFLAGS} pdp11 : ${BIN}pdp11${EXE} ${BIN}pdp11${EXE} : ${PDP11} ${SIM} ${CC} ${PDP11} ${SIM} ${PDP11_OPT} -o $@ ${LDFLAGS} vax : ${BIN}vax${EXE} ${BIN}vax${EXE} : ${VAX} ${SIM} ${CC} ${VAX} ${SIM} ${VAX_OPT} -o $@ ${LDFLAGS} vax780 : ${BIN}vax780${EXE} ${BIN}vax780${EXE} : ${VAX780} ${SIM} ${CC} ${VAX780} ${SIM} ${VAX780_OPT} -o $@ ${LDFLAGS} nova : ${BIN}nova${EXE} ${BIN}nova${EXE} : ${NOVA} ${SIM} ${CC} ${NOVA} ${SIM} ${NOVA_OPT} -o $@ ${LDFLAGS} eclipse : ${BIN}eclipse${EXE} ${BIN}eclipse${EXE} : ${ECLIPSE} ${SIM} ${CC} ${ECLIPSE} ${SIM} ${ECLIPSE_OPT} -o $@ ${LDFLAGS} h316 : ${BIN}h316${EXE} ${BIN}h316${EXE} : ${H316} ${SIM} ${CC} ${H316} ${SIM} ${H316_OPT} -o $@ ${LDFLAGS} hp2100 : ${BIN}hp2100${EXE} ${BIN}hp2100${EXE} : ${HP2100} ${SIM} ${CC} ${HP2100} ${SIM} ${HP2100_OPT} -o $@ ${LDFLAGS} i1401 : ${BIN}i1401${EXE} ${BIN}i1401${EXE} : ${I1401} ${SIM} ${CC} ${I1401} ${SIM} ${I1401_OPT} -o $@ ${LDFLAGS} i1620 : ${BIN}i1620${EXE} ${BIN}i1620${EXE} : ${I1620} ${SIM} ${CC} ${I1620} ${SIM} ${I1620_OPT} -o $@ ${LDFLAGS} i7094 : ${BIN}i7094${EXE} ${BIN}i7094${EXE} : ${I7094} ${SIM} ${CC} ${I7094} ${SIM} ${I7094_OPT} -o $@ ${LDFLAGS} ibm1130 : ${BIN}ibm1130${EXE} ${BIN}ibm1130${EXE} : ${IBM1130} ${CC} ${IBM1130} ${SIM} ${IBM1130_OPT} -o $@ ${LDFLAGS} s3 : ${BIN}s3${EXE} ${BIN}s3${EXE} : ${S3} ${SIM} ${CC} ${S3} ${SIM} ${S3_OPT} -o $@ ${LDFLAGS} altair : ${BIN}altair${EXE} ${BIN}altair${EXE} : ${ALTAIR} ${SIM} ${CC} ${ALTAIR} ${SIM} ${ALTAIR_OPT} -o $@ ${LDFLAGS} altairz80 : ${BIN}altairz80${EXE} ${BIN}altairz80${EXE} : ${ALTAIRZ80} ${SIM} ${CC} ${ALTAIRZ80} ${SIM} ${ALTAIRZ80_OPT} -o $@ ${LDFLAGS} gri : ${BIN}gri${EXE} ${BIN}gri${EXE} : ${GRI} ${SIM} ${CC} ${GRI} ${SIM} ${GRI_OPT} -o $@ ${LDFLAGS} lgp : ${BIN}lgp${EXE} ${BIN}lgp${EXE} : ${LGP} ${SIM} ${CC} ${LGP} ${SIM} ${LGP_OPT} -o $@ ${LDFLAGS} id16 : ${BIN}id16${EXE} ${BIN}id16${EXE} : ${ID16} ${SIM} ${CC} ${ID16} ${SIM} ${ID16_OPT} -o $@ ${LDFLAGS} id32 : ${BIN}id32${EXE} ${BIN}id32${EXE} : ${ID32} ${SIM} ${CC} ${ID32} ${SIM} ${ID32_OPT} -o $@ ${LDFLAGS} sds : ${BIN}sds${EXE} ${BIN}sds${EXE} : ${SDS} ${SIM} ${CC} ${SDS} ${SIM} ${SDS_OPT} -o $@ ${LDFLAGS} simh-3.8.1/sim_fio.h0000644000175000017500000000417311111655430012441 0ustar vlmvlm/* sim_fio.h: simulator file I/O library headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 15-May-06 RMS Added sim_fsize_name 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jan-04 RMS Split out from SCP */ #ifndef _SIM_FIO_H_ #define _SIM_FIO_H_ 0 #define FLIP_SIZE (1 << 16) /* flip buf size */ #define fxread(a,b,c,d) sim_fread (a, b, c, d) #define fxwrite(a,b,c,d) sim_fwrite (a, b, c, d) int32 sim_finit (void); FILE *sim_fopen (const char *file, const char *mode); int sim_fseek (FILE *st, t_addr offset, int whence); size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr); size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr); uint32 sim_fsize (FILE *fptr); uint32 sim_fsize_name (char *fname); #endif simh-3.8.1/sim_tape.c0000644000175000017500000011017311111340766012611 0ustar vlmvlm/* sim_tape.c: simulator tape support library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Ultimately, this will be a place to hide processing of various tape formats, as well as OS-specific direct hardware access. 08-Jun-08 JDB Fixed signed/unsigned warning in sim_tape_set_fmt 23-Jan-07 JDB Fixed backspace over gap at BOT 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell) 15-Dec-06 RMS Added support for small capacity tapes 30-Aug-06 JDB Added erase gap support 14-Feb-06 RMS Added variable tape capacity 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf 17-Dec-05 RMS Added write support for Paul Pierce 7b format 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-May-05 RMS Added support for Pierce 7b format 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan) RMS Fixed incorrect error codes (found by Dave Bryan) 05-Jan-04 RMS Revised for file I/O library 25-Apr-03 RMS Added extended file support 28-Mar-03 RMS Added E11 and TPC format support Public routines: sim_tape_attach attach tape unit sim_tape_detach detach tape unit sim_tape_rdrecf read tape record forward sim_tape_rdrecr read tape record reverse sim_tape_wrrecf write tape record forward sim_tape_sprecf space tape record forward sim_tape_sprecr space tape record reverse sim_tape_wrtmk write tape mark sim_tape_wreom erase remainder of tape sim_tape_wrgap write erase gap sim_tape_rewind rewind sim_tape_reset reset unit sim_tape_bot TRUE if at beginning of tape sim_tape_eot TRUE if at or beyond end of tape sim_tape_wrp TRUE if write protected sim_tape_set_fmt set tape format sim_tape_show_fmt show tape format sim_tape_set_capac set tape capacity sim_tape_show_capac show tape capacity */ #include "sim_defs.h" #include "sim_tape.h" struct sim_tape_fmt { char *name; /* name */ int32 uflags; /* unit flags */ t_addr bot; /* bot test */ }; static struct sim_tape_fmt fmts[MTUF_N_FMT] = { { "SIMH", 0, sizeof (t_mtrlnt) - 1 }, { "E11", 0, sizeof (t_mtrlnt) - 1 }, { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 }, { "P7B", 0, 0 }, /* { "TPF", UNIT_RO, 0 }, */ { NULL, 0, 0 } }; extern int32 sim_switches; t_stat sim_tape_ioerr (UNIT *uptr); t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map); t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map); /* Attach tape unit */ t_stat sim_tape_attach (UNIT *uptr, char *cptr) { uint32 objc; char gbuf[CBUFSIZE]; t_stat r; if (sim_switches & SWMASK ('F')) { /* format spec? */ cptr = get_glyph (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) return SCPE_ARG; } r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; switch (MT_GET_FMT (uptr)) { /* case on format */ case MTUF_F_TPC: /* TPC */ objc = sim_tape_tpc_map (uptr, NULL); /* get # objects */ if (objc == 0) { /* tape empty? */ sim_tape_detach (uptr); return SCPE_FMT; /* yes, complain */ } uptr->filebuf = calloc (objc + 1, sizeof (t_addr)); if (uptr->filebuf == NULL) { /* map allocated? */ sim_tape_detach (uptr); return SCPE_MEM; /* no, complain */ } uptr->hwmark = objc + 1; /* save map size */ sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf); /* fill map */ break; default: break; } sim_tape_rewind (uptr); return SCPE_OK; } /* Detach tape unit */ t_stat sim_tape_detach (UNIT *uptr) { uint32 f = MT_GET_FMT (uptr); t_stat r; r = detach_unit (uptr); /* detach unit */ if (r != SCPE_OK) return r; switch (f) { /* case on format */ case MTUF_F_TPC: /* TPC */ if (uptr->filebuf) /* free map */ free (uptr->filebuf); uptr->filebuf = NULL; uptr->hwmark = 0; break; default: break; } sim_tape_rewind (uptr); return SCPE_OK; } /* Read record length forward (internal routine) Inputs: uptr = pointer to tape unit bc = pointer to returned record length Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set tape mark updated data record updated, sim_fread will read record forward See notes at "sim_tape_wrgap" regarding erase gap implementation. */ t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) { uint8 c; t_bool all_eof; uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; t_tpclnt tpcbc; MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */ switch (f) { /* switch on fmt */ case MTUF_F_STD: case MTUF_F_E11: do { sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ sbc = MTR_L (*bc); /* save rec lnt */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ return sim_tape_ioerr (uptr); } if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */ MT_SET_PNU (uptr); /* pos not upd */ return MTSE_EOM; } uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */ if (*bc == MTR_TMK) /* tape mark? */ return MTSE_TMK; if (*bc == MTR_FHGAP) { /* half gap? */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ } else if (*bc != MTR_GAP) uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */ ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); } while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP)); break; case MTUF_F_TPC: sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ return sim_tape_ioerr (uptr); } if (feof (uptr->fileref)) { /* eof? */ MT_SET_PNU (uptr); /* pos not upd */ return MTSE_EOM; } uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ if (tpcbc == TPC_TMK) /* tape mark? */ return MTSE_TMK; uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ break; case MTUF_F_P7B: for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ sim_fread (&c, sizeof (uint8), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ return sim_tape_ioerr (uptr); } if (feof (uptr->fileref)) { /* eof? */ if (sbc == 0) /* no data? eom */ return MTSE_EOM; break; /* treat like eor */ } if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ break; if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; } *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ uptr->pos = uptr->pos + sbc; /* spc over record */ if (all_eof) /* tape mark? */ return MTSE_TMK; break; default: return MTSE_FMT; } return MTSE_OK; } /* Read record length reverse (internal routine) Inputs: uptr = pointer to tape unit bc = pointer to returned record length Outputs: status = operation status exit condition position unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated data record updated, sim_fread will read record forward See notes at "sim_tape_wrgap" regarding erase gap implementation. */ t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) { uint8 c; t_bool all_eof; uint32 f = MT_GET_FMT (uptr); t_addr ppos; t_mtrlnt sbc; t_tpclnt tpcbc; MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (sim_tape_bot (uptr)) /* at BOT? */ return MTSE_BOT; switch (f) { /* switch on fmt */ case MTUF_F_STD: case MTUF_F_E11: do { sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET); sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ sbc = MTR_L (*bc); if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); if (feof (uptr->fileref)) /* eof? */ return MTSE_EOM; uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */ if (*bc == MTR_EOM) /* eom? */ return MTSE_EOM; if (*bc == MTR_TMK) /* tape mark? */ return MTSE_TMK; if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ } else if (*bc != MTR_GAP) { uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */ ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET); } else if (sim_tape_bot (uptr)) /* backed into BOT? */ return MTSE_BOT; } while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP)); break; case MTUF_F_TPC: ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); if (feof (uptr->fileref)) /* eof? */ return MTSE_EOM; uptr->pos = ppos; /* spc over record */ if (*bc == MTR_TMK) /* tape mark? */ return MTSE_TMK; sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); break; case MTUF_F_P7B: for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET); sim_fread (&c, sizeof (uint8), 1, uptr->fileref); if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); if (feof (uptr->fileref)) /* eof? */ return MTSE_EOM; if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; if (c & P7B_SOR) /* start of record? */ break; } uptr->pos = uptr->pos - sbc; /* update position */ *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ if (all_eof) /* tape mark? */ return MTSE_TMK; break; default: return MTSE_FMT; } return MTSE_OK; } /* Read record forward Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = pointer to returned record length max = maximum record size Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set invalid record unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, tbc, rbc; t_addr opos; t_stat st; opos = uptr->pos; /* old position */ if (st = sim_tape_rdlntf (uptr, &tbc)) /* read rec lnt */ return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) { /* rec out of range? */ MT_SET_PNU (uptr); uptr->pos = opos; return MTSE_INVRL; } i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); uptr->pos = opos; return sim_tape_ioerr (uptr); } for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; if (f == MTUF_F_P7B) /* p7b? strip SOR */ buf[0] = buf[0] & P7B_DPAR; return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } /* Read record reverse Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = pointer to returned record length max = maximum record size Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged end of file unchanged end of medium updated invalid record unchanged tape mark updated data record updated data record error updated */ t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, rbc, tbc; t_stat st; if (st = sim_tape_rdlntr (uptr, &tbc)) /* read rec lnt */ return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) /* rec out of range? */ return MTSE_INVRL; i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; if (f == MTUF_F_P7B) /* p7b? strip SOR */ buf[0] = buf[0] & P7B_DPAR; return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } /* Write record forward Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = record length Outputs: status = operation status exit condition position unit unattached unchanged write protect unchanged write error unchanged, PNU set data record updated */ t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc) { uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; MT_CLR_PNU (uptr); sbc = MTR_L (bc); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; if (sbc == 0) /* nothing to do? */ return MTSE_OK; sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ switch (f) { /* case on format */ case MTUF_F_STD: /* standard */ sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */ case MTUF_F_E11: /* E11 */ sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */ break; case MTUF_F_P7B: /* Pierce 7B */ buf[0] = buf[0] | P7B_SOR; /* mark start of rec */ sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } uptr->pos = uptr->pos + sbc; /* move tape */ break; } return MTSE_OK; } /* Write metadata forward (internal routine) */ t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat) { MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */ return MTSE_OK; } /* Write tape mark */ t_stat sim_tape_wrtmk (UNIT *uptr) { if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ uint8 buf = P7B_EOF; /* eof mark */ return sim_tape_wrrecf (uptr, &buf, 1); /* write char */ } return sim_tape_wrdata (uptr, MTR_TMK); } /* Write end of medium */ t_stat sim_tape_wreom (UNIT *uptr) { if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ return MTSE_FMT; return sim_tape_wrdata (uptr, MTR_EOM); } /* Write erase gap Inputs: uptr = pointer to tape unit gaplen = length of gap in tenths of an inch bpi = current recording density in bytes per inch Outputs: status = operation status exit condition position ------------------ ------------------ unit unattached unchanged unsupported format unchanged write protected unchanged read error unchanged, PNU set write error unchanged, PNU set gap written updated An erase gap is represented in the tape image file by a special metadata value. This value is chosen so that it is still recognizable even if it has been "cut in half" by a subsequent data overwrite that does not end on a metadatum-sized boundary. In addition, a range of metadata values are reserved for detection in the reverse direction. Erase gaps are supported only in SIMH tape format. This implementation supports erasing gaps in the middle of a populated tape image and will always produce a valid image. It also produces valid images when overwriting gaps with data records, with one exception: a data write that leaves only two bytes of gap remaining will produce an invalid tape. This limitation is deemed acceptable, as it is analogous to the existing limitation that data records cannot overwrite other data records without producing an invalid tape. Because SIMH tape images do not carry physical parameters (e.g., recording density), overwriting a tape image file containing gap metadata is problematic if the density setting is not the same as that used during recording. There is no way to establish a gap of a certain length unequivocally in an image file, so this implementation establishes a gap of a certain number of bytes that reflect the desired gap length at the bpi used during writing. To write an erase gap, the implementation uses one of two approaches, depending on whether or not the current tape position is at EOM. Erasing at EOM presents no special difficulties; gap metadata markers are written for the prescribed number of bytes. If the tape is not at EOM, then erasing must take into account the existing record structure to ensure that a valid tape image is maintained. The general approach is to erase for the nominal number of bytes but to increase that length, if necessary, to ensure that a partially overwritten data record at the end of the gap can be altered to maintain validity. Because the smallest legal tape record requires space for two metadata markers plus two data bytes, an erasure that would leave less than that is increased to consume the entire record. Otherwise, the final record is truncated appropriately. When reading in either direction, gap metadata markers are ignored (skipped) until a record length header, EOF marker, EOM marker, or physical EOF is encountered. Thus, tape images containing gap metadata are transparent to the calling simulator. The permissibility of data record lengths that are not multiples of the metadatum size presents a difficulty when reading. If such an "odd length" record is written over a gap, half of a metadata marker will exist immediately after the trailing record length. This condition is detected when reading forward by the appearance of a "reversed" marker. The value appears reversed because the value is made up of half of one marker and half of the next. This is handled by seeking forward two bytes to resync (the stipulation above that the overwrite cannot leave only two bytes of gap means that at least one "whole" metadata marker will follow). Reading in reverse presents a more complex problem, because half of the marker is from the preceding trailing record length marker and therefore could be any of a range of values. However, that range is restricted by the SIMH tape specification requirement that record length metadata values must have bits 30:24 set to zero. This allows unambiguous detection of the condition. The value chosen for gap metadata and the values reserved for "half-gap" detection are: 0xFFFFFFFE - primary gap value 0xFFFEFFFF - reserved (indicates half-gap in forward reads) 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) */ t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi) { t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; t_addr gap_pos = uptr->pos; uint32 file_size, marker_count; uint32 format = MT_GET_FMT (uptr); uint32 gap_alloc = 0; /* gap allocated from tape */ int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */ const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (format != MTUF_F_STD) /* not SIMH fmt? */ return MTSE_FMT; if (sim_tape_wrp (uptr)) /* write protected? */ return MTSE_WRP; file_size = sim_fsize (uptr->fileref); /* get file size */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ /* Read tape records and allocate to gap until amount required is consumed. Read next metadatum from tape: - EOF or EOM: allocate remainder of bytes needed. - TMK or GAP: allocate sizeof(metadatum) bytes. - Reverse GAP: allocate sizeof(metadatum) / 2 bytes. - Data record: see below. Loop until bytes needed = 0. */ do { sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */ if (ferror (uptr->fileref)) { /* read error? */ uptr->pos = gap_pos; /* restore original position */ MT_SET_PNU (uptr); /* position not updated */ return sim_tape_ioerr (uptr); /* translate error */ } else uptr->pos = uptr->pos + meta_size; /* move tape over datum */ if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ gap_needed = 0; } else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */ gap_alloc = gap_alloc + meta_size; /* allocate marker space */ gap_needed = gap_needed - meta_size; /* reduce requirement */ } else if (meta == MTR_FHGAP) { /* half gap? */ uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ } else if (uptr->pos + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */ gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */ gap_needed = 0; /* allocate remainder */ } /* Allocate a data record: - Determine record size in bytes (including metadata) - If record size - bytes needed < smallest allowed record size, allocate entire record to gap, else allocate needed amount and truncate data record to reflect remainder. */ else { /* data record */ sbc = MTR_L (meta); /* get record data length */ rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */ if (rec_size < gap_needed + min_rec_size) { /* rec too small? */ uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */ gap_alloc = gap_alloc + rec_size; /* allocate record */ gap_needed = gap_needed - rec_size; /* reduce requirement */ } else { /* record size OK */ uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */ new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */ st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */ st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ gap_needed = 0; } } } while (gap_needed > 0); uptr->pos = gap_pos; /* reposition to gap start */ if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } uptr->pos = uptr->pos - meta_size / 2; /* realign position */ gap_alloc = gap_alloc - 2; /* decrease gap to write */ } marker_count = gap_alloc / meta_size; /* count of gap markers */ do { st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } } while (--marker_count > 0); return MTSE_OK; } /* Space record forward */ t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc) { t_stat st; st = sim_tape_rdlntf (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } /* Space record reverse */ t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc) { t_stat st; if (MT_TST_PNU (uptr)) { MT_CLR_PNU (uptr); *bc = 0; return MTSE_OK; } st = sim_tape_rdlntr (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } /* Rewind tape */ t_stat sim_tape_rewind (UNIT *uptr) { uptr->pos = 0; MT_CLR_PNU (uptr); return MTSE_OK; } /* Reset tape */ t_stat sim_tape_reset (UNIT *uptr) { MT_CLR_PNU (uptr); return SCPE_OK; } /* Test for BOT */ t_bool sim_tape_bot (UNIT *uptr) { uint32 f = MT_GET_FMT (uptr); return (uptr->pos <= fmts[f].bot)? TRUE: FALSE; } /* Test for end of tape */ t_bool sim_tape_eot (UNIT *uptr) { return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE; } /* Test for write protect */ t_bool sim_tape_wrp (UNIT *uptr) { return (uptr->flags & MTUF_WRP)? TRUE: FALSE; } /* Process I/O error */ t_stat sim_tape_ioerr (UNIT *uptr) { perror ("Magtape library I/O error"); clearerr (uptr->fileref); return MTSE_IOERR; } /* Set tape format */ t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 f; if (uptr == NULL) return SCPE_IERR; if (cptr == NULL) return SCPE_ARG; for (f = 0; f < MTUF_N_FMT; f++) { if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { uptr->flags = (uptr->flags & ~MTUF_FMT) | (f << MTUF_V_FMT) | fmts[f].uflags; return SCPE_OK; } } return SCPE_ARG; } /* Show tape format */ t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 f = MT_GET_FMT (uptr); if (fmts[f].name) fprintf (st, "%s format", fmts[f].name); else fprintf (st, "invalid format"); return SCPE_OK; } /* Map a TPC format tape image */ uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map) { t_addr tpos; t_tpclnt bc; uint32 i, objc; if ((uptr == NULL) || (uptr->fileref == NULL)) return 0; for (objc = 0, tpos = 0;; ) { sim_fseek (uptr->fileref, tpos, SEEK_SET); i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref); if (i == 0) break; if (map) map[objc] = tpos; objc++; tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt); } if (map) map[objc] = tpos; return objc; } /* Find the preceding record in a TPC file */ t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map) { uint32 lo, hi, p; if (map == NULL) return 0; lo = 0; hi = uptr->hwmark - 1; do { p = (lo + hi) >> 1; if (uptr->pos == map[p]) return ((p == 0)? map[p]: map[p - 1]); else if (uptr->pos < map[p]) hi = p - 1; else lo = p + 1; } while (lo <= hi); return ((p == 0)? map[p]: map[p - 1]); } /* Set tape capacity */ t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc) { extern uint32 sim_taddr_64; t_addr cap; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); if (r != SCPE_OK) return SCPE_ARG; uptr->capac = cap * ((t_addr) 1000000); return SCPE_OK; } /* Show tape capacity */ t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc) { if (uptr->capac) { if (uptr->capac >= (t_addr) 1000000) fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); else if (uptr->capac >= (t_addr) 1000) fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); else fprintf (st, "capacity=%dB", (uint32) uptr->capac); } else fprintf (st, "unlimited capacity"); return SCPE_OK; } simh-3.8.1/Interdata/0000755000175000017500000000000011112563464012557 5ustar vlmvlmsimh-3.8.1/Interdata/id_mt.c0000644000175000017500000005627711112026546014033 0ustar vlmvlm/* id_mt.c: Interdata magnetic tape simulator Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt M46-494 dual density 9-track magtape controller 16-Feb-06 RMS Added tape capacity checking 18-Mar-05 RMS Added attached test to detach routine 07-Dec-04 RMS Added read-only file support 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library 20-Feb-03 RMS Fixed read to stop selch on error Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. */ #include "id_defs.h" #include "sim_tape.h" #define UST u3 /* unit status */ #define UCMD u4 /* unit command */ #define MT_MAXFR (1 << 24) /* max transfer */ /* Command - in UCMD */ #define MTC_SPCR 0x11 /* backspace */ #define MTC_SKFR 0x13 /* space file rev */ #define MTC_CLR 0x20 /* clear */ #define MTC_RD 0x21 /* read */ #define MTC_WR 0x22 /* write */ #define MTC_SKFF 0x23 /* space file fwd */ #define MTC_WEOF 0x30 /* write eof */ #define MTC_REW 0x38 /* rewind */ #define MTC_MASK 0x3F #define MTC_STOP1 0x40 /* stop, set EOM */ #define MTC_STOP2 0x80 /* stop, set NMTN */ /* Status byte, * = in UST */ #define STA_ERR 0x80 /* error */ #define STA_EOF 0x40 /* end of file */ #define STA_EOT 0x20 /* *end of tape */ #define STA_NMTN 0x10 /* *no motion */ #define STA_UFLGS (STA_EOT|STA_NMTN) /* unit flags */ #define STA_MASK (STA_ERR|STA_EOF|STA_BSY|STA_EOM) #define SET_EX (STA_ERR|STA_EOF|STA_NMTN) extern uint32 int_req[INTSZ], int_enb[INTSZ]; uint8 mtxb[MT_MAXFR]; /* xfer buffer */ uint32 mt_bptr = 0; /* pointer */ uint32 mt_blnt = 0; /* length */ uint32 mt_sta = 0; /* status byte */ uint32 mt_db = 0; /* data buffer */ uint32 mt_xfr = 0; /* data xfr in prog */ uint32 mt_arm[MT_NUMDR] = { 0 }; /* intr armed */ int32 mt_wtime = 10; /* byte latency */ int32 mt_rtime = 1000; /* record latency */ int32 mt_stopioe = 1; /* stop on error */ uint8 mt_tplte[] = { 0, o_MT0, o_MT0*2, o_MT0*3, TPL_END }; static const uint8 bad_cmd[64] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }; DEVICE mt_dev; uint32 mt (uint32 dev, uint32 op, uint32 dat); t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_detach (UNIT *uptr); t_stat mt_boot (int32 unitno, DEVICE *dptr); t_stat mt_map_err (UNIT *uptr, t_stat st); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ DIB mt_dib = { d_MT, 0, v_MT, mt_tplte, &mt, NULL }; UNIT mt_unit[] = { { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } }; REG mt_reg[] = { { HRDATA (STA, mt_sta, 8) }, { HRDATA (BUF, mt_db, 8) }, { BRDATA (DBUF, mtxb, 16, 8, MT_MAXFR) }, { HRDATA (DBPTR, mt_bptr, 16) }, { HRDATA (DBLNT, mt_blnt, 17), REG_RO }, { FLDATA (XFR, mt_xfr, 0) }, { GRDATA (IREQ, int_req[l_MT], 16, MT_NUMDR, i_MT) }, { GRDATA (IENB, int_enb[l_MT], 16, MT_NUMDR, i_MT) }, { BRDATA (IARM, mt_arm, 16, 1, MT_NUMDR) }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { DRDATA (WTIME, mt_wtime, 24), PV_LEFT + REG_NZ }, { DRDATA (RTIME, mt_rtime, 24), PV_LEFT + REG_NZ }, { URDATA (UST, mt_unit[0].UST, 16, 8, 0, MT_NUMDR, 0) }, { URDATA (CMD, mt_unit[0].UCMD, 16, 8, 0, MT_NUMDR, 0) }, { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT | REG_RO) }, { HRDATA (DEVNO, mt_dib.dno, 8), REG_HRO }, { HRDATA (SELCH, mt_dib.sch, 1), REG_HRO }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", &set_sch, &show_sch, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 16, 8, NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, &mt_dib, DEV_DISABLE }; /* Magtape: IO routine */ uint32 mt (uint32 dev, uint32 op, uint32 dat) { uint32 i, f, t; uint32 u = (dev - mt_dib.dno) / o_MT0; UNIT *uptr = mt_dev.units + u; switch (op) { /* case IO op */ case IO_ADR: /* select */ sch_adr (mt_dib.sch, dev); /* inform sel ch */ return BY; /* byte only */ case IO_RD: /* read data */ if (mt_xfr) /* xfr? set busy */ mt_sta = mt_sta | STA_BSY; return mt_db; /* return data */ case IO_WD: /* write data */ if (mt_xfr) { /* transfer? */ mt_sta = mt_sta | STA_BSY; /* set busy */ if ((uptr->UCMD & (MTC_STOP1 | MTC_STOP2)) && ((uptr->UCMD & MTC_MASK) == MTC_WR)) /* while stopping? */ mt_sta = mt_sta | STA_ERR; /* write overrun */ } mt_db = dat & DMASK8; /* store data */ break; case IO_SS: /* status */ mt_sta = mt_sta & STA_MASK; /* ctrl status */ if (uptr->flags & UNIT_ATT) /* attached? */ t = mt_sta | (uptr->UST & STA_UFLGS); /* yes, unit status */ else t = mt_sta | STA_DU; /* no, dev unavail */ if (t & SET_EX) /* test for ex */ t = t | STA_EX; return t; case IO_OC: /* command */ mt_arm[u] = int_chg (v_MT + u, dat, mt_arm[u]); f = dat & MTC_MASK; /* get cmd */ if (f == MTC_CLR) { /* clear? */ mt_reset (&mt_dev); /* reset world */ break; } if (((uptr->flags & UNIT_ATT) == 0) || /* ignore if unatt */ bad_cmd[f] || /* or bad cmd */ (((f == MTC_WR) || (f == MTC_WEOF)) && /* or write */ sim_tape_wrp (uptr))) /* and protected */ break; for (i = 0; i < MT_NUMDR; i++) { /* check other drvs */ if (sim_is_active (&mt_unit[i]) && /* active? */ (mt_unit[i].UCMD != MTC_REW)) { /* not rewind? */ sim_cancel (&mt_unit[i]); /* stop */ mt_unit[i].UCMD = 0; } } if (sim_is_active (uptr) && /* unit active? */ !(uptr->UCMD & (MTC_STOP1 | MTC_STOP2))) /* not stopping? */ break; /* ignore */ if ((f == MTC_WR) || (f == MTC_REW)) /* write, rew: bsy=0 */ mt_sta = 0; else mt_sta = STA_BSY; /* bsy=1,nmtn,eom,err=0 */ mt_bptr = mt_blnt = 0; /* not yet started */ if ((f == MTC_RD) || (f == MTC_WR)) /* data xfr? */ mt_xfr = 1; /* set xfr flag */ else mt_xfr = 0; uptr->UCMD = f; /* save cmd */ uptr->UST = 0; /* clr tape stat */ sim_activate (uptr, mt_rtime); /* start op */ break; } return 0; } /* Unit service A given operation can generate up to three interrupts - EOF generates an interrupt when set (read, space, wreof) BUSY will still be set, EOM and NMTN will be clear - After operation complete + delay, EOM generates an interrupt BUSY will be clear, EOM will be set, NMTN will be clear - After a further delay, NMTN generates an interrupt BUSY will be clear, EOM and NMTN will be set Rewind generates an interrupt when NMTN sets */ t_stat mt_svc (UNIT *uptr) { uint32 i; int32 u = uptr - mt_dev.units; uint32 dev = mt_dib.dno + (u * o_MT0); t_mtrlnt tbc; t_bool passed_eot; t_stat st, r = SCPE_OK; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ uptr->UCMD = 0; /* clr cmd */ uptr->UST = 0; /* set status */ mt_xfr = 0; /* clr op flags */ mt_sta = STA_ERR | STA_EOM; /* set status */ if (mt_arm[u]) /* interrupt */ SET_INT (v_MT + u); return IORETURN (mt_stopioe, SCPE_UNATT); } if (uptr->UCMD & MTC_STOP2) { /* stop, gen NMTN? */ uptr->UCMD = 0; /* clr cmd */ uptr->UST = uptr->UST | STA_NMTN; /* set nmtn */ mt_xfr = 0; /* clr xfr */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); return SCPE_OK; } if (uptr->UCMD & MTC_STOP1) { /* stop, gen EOM? */ uptr->UCMD = uptr->UCMD | MTC_STOP2; /* clr cmd */ mt_sta = (mt_sta & ~STA_BSY) | STA_EOM; /* clr busy, set eom */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); sim_activate (uptr, mt_rtime); /* schedule */ return SCPE_OK; } passed_eot = sim_tape_eot (uptr); /* passed EOT? */ switch (uptr->UCMD) { /* case on function */ case MTC_REW: /* rewind */ sim_tape_rewind (uptr); /* reposition */ uptr->UCMD = 0; /* clr cmd */ uptr->UST = STA_NMTN | STA_EOT; /* update status */ mt_sta = mt_sta & ~STA_BSY; /* don't set EOM */ if (mt_arm[u]) /* interrupt */ SET_INT (v_MT + u); return SCPE_OK; /* For read, busy = 1 => buffer empty For write, busy = 1 => buffer full For read, data transfers continue for the full length of the record, or the maximum size of the transfer buffer For write, data transfers continue until a write is attempted and the buffer is empty */ case MTC_RD: /* read */ if (mt_blnt == 0) { /* first time? */ st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ if (st == MTSE_RECE) /* rec in err? */ mt_sta = mt_sta | STA_ERR; else if (st != SCPE_OK) { /* other error? */ r = mt_map_err (uptr, st); /* map error */ if (sch_actv (mt_dib.sch, dev)) /* if sch, stop */ sch_stop (mt_dib.sch); break; } mt_blnt = tbc; /* set buf lnt */ } if (sch_actv (mt_dib.sch, dev)) { /* sch active? */ i = sch_wrmem (mt_dib.sch, mtxb, mt_blnt); /* store rec in mem */ if (sch_actv (mt_dib.sch, dev)) /* sch still active? */ sch_stop (mt_dib.sch); /* stop chan, long rd */ else if (i < mt_blnt) /* process entire rec? */ mt_sta = mt_sta | STA_ERR; /* no, overrun error */ } else if (mt_bptr < mt_blnt) { /* no, if !eor */ if (!(mt_sta & STA_BSY)) /* busy still clr? */ mt_sta = mt_sta | STA_ERR; /* read overrun */ mt_db = mtxb[mt_bptr++]; /* get next byte */ mt_sta = mt_sta & ~STA_BSY; /* !busy = buf full */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); sim_activate (uptr, mt_wtime); /* reschedule */ return SCPE_OK; } break; /* record done */ case MTC_WR: /* write */ if (sch_actv (mt_dib.sch, dev)) { /* sch active? */ mt_bptr = sch_rdmem (mt_dib.sch, mtxb, MT_MAXFR); /* get rec */ if (sch_actv (mt_dib.sch, dev)) /* not done? */ sch_stop (mt_dib.sch); /* stop chan */ } else if (mt_sta & STA_BSY) { /* no, if !eor */ if (mt_bptr < MT_MAXFR) /* if room */ mtxb[mt_bptr++] = mt_db; /* store in buf */ mt_sta = mt_sta & ~STA_BSY; /* !busy = buf emp */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); sim_activate (uptr, mt_wtime); /* reschedule */ return SCPE_OK; } if (mt_bptr) { /* any chars? */ if (st = sim_tape_wrrecf (uptr, mtxb, mt_bptr)) /* write, err? */ r = mt_map_err (uptr, st); /* map error */ } break; /* record done */ case MTC_WEOF: /* write eof */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mt_map_err (uptr, st); /* map error */ mt_sta = mt_sta | STA_EOF; /* set eof */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); break; case MTC_SKFF: /* skip file fwd */ while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; if (st == MTSE_TMK) { /* stopped by tmk? */ mt_sta = mt_sta | STA_EOF; /* set eof */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); } else r = mt_map_err (uptr, st); /* map error */ break; case MTC_SKFR: /* skip file rev */ while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; if (st == MTSE_TMK) { /* stopped by tmk? */ mt_sta = mt_sta | STA_EOF; /* set eof */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); } else r = mt_map_err (uptr, st); /* map error */ break; case MTC_SPCR: /* backspace */ if (st = sim_tape_sprecr (uptr, &tbc)) /* skip rec rev, err? */ r = mt_map_err (uptr, st); /* map error */ break; } /* end case */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ uptr->UST = uptr->UST | STA_EOT; uptr->UCMD = uptr->UCMD | MTC_STOP1; /* set stop stage 1 */ sim_activate (uptr, mt_rtime); /* schedule */ return r; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { int32 u = uptr - mt_dev.units; switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ mt_sta = mt_sta | STA_ERR; case MTSE_OK: /* no error */ return SCPE_IERR; case MTSE_TMK: /* end of file */ mt_sta = mt_sta | STA_EOF; /* set eof */ if (mt_arm[u]) /* set intr */ SET_INT (v_MT + u); break; case MTSE_IOERR: /* IO error */ mt_sta = mt_sta | STA_ERR; /* set err */ if (mt_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ mt_sta = mt_sta | STA_ERR; return SCPE_MTRLNT; case MTSE_WRP: /* write protect */ case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ mt_sta = mt_sta | STA_ERR; /* set err */ break; case MTSE_BOT: /* reverse into BOT */ uptr->UST = uptr->UST | STA_EOT; /* set err */ break; } /* end switch */ return SCPE_OK; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { uint32 u; UNIT *uptr; mt_bptr = mt_blnt = 0; /* clr buf */ mt_sta = STA_BSY; /* clr flags */ mt_xfr = 0; /* clr controls */ for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ CLR_INT (v_MT + u); /* clear int */ CLR_ENB (v_MT + u); /* disable int */ mt_arm[u] = 0; /* disarm int */ uptr = mt_dev.units + u; sim_tape_reset (uptr); /* clear pos flag */ sim_cancel (uptr); /* cancel activity */ uptr->UST = (uptr->UST & STA_UFLGS) | STA_NMTN; /* init status */ uptr->UCMD = 0; /* init cmd */ } return SCPE_OK; } /* Attach routine */ t_stat mt_attach (UNIT *uptr, char *cptr) { int32 u = uptr - mt_dev.units; t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->UST = STA_EOT; if (mt_arm[u]) SET_INT (v_MT + u); return r; } /* Detach routine */ t_stat mt_detach (UNIT* uptr) { int32 u = uptr - mt_dev.units; t_stat r; if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; r = sim_tape_detach (uptr); if (r != SCPE_OK) return r; if (mt_arm[u]) SET_INT (v_MT + u); uptr->UST = 0; return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 0x50 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) static uint8 boot_rom[] = { 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */ 0x43, 0x00, 0x00, 0x80 /* BR 80 */ }; t_stat mt_boot (int32 unitno, DEVICE *dptr) { extern uint32 PC, dec_flgs; extern uint16 decrom[]; extern DIB sch_dib; uint32 sch_dev; if (decrom[0xD5] & dec_flgs) /* AL defined? */ return SCPE_NOFNC; sim_tape_rewind (&mt_unit[unitno]); /* rewind */ sch_dev = sch_dib.dno + mt_dib.sch; /* sch dev # */ IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */ IOWriteB (AL_DEV, mt_dib.dno + (unitno * o_MT0)); /* set dev no for unit */ IOWriteB (AL_IOC, 0xA1); /* set dev cmd */ IOWriteB (AL_SCH, sch_dev); /* set dev no for chan */ PC = BOOT_START; return SCPE_OK; } simh-3.8.1/Interdata/id32_dboot.c0000644000175000017500000002011511110121260014630 0ustar vlmvlm/* id32_dboot.c: Interdata 32b simulator disk bootstrap Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 17-Jul-06 RMS Fixed transcription errors (found by Davis Johnson) 17-Feb-03 RMS Fixed for UNIX bootstrap, upper platter bootstrap */ #include "id_defs.h" #define DBOOT_BEG 0x1000 #define DBOOT_START 0x100E #define DBOOT_LEN (sizeof (dboot_rom) / sizeof (uint8)) /* Transcribed from 32b Bootstrap Loader, 03-074N81R03A13 */ static uint8 dboot_rom[] = { 0xca, 0xf0, 0x00, 0x30, 0xc5, 0xf0, 0x00, 0x3a, 0x02, 0x8e, 0x26, 0xf7, 0x03, 0x0e, 0xe6, 0xd0, 0x0f, 0x30, 0xd1, 0xe0, 0x00, 0x78, 0xd0, 0xed, 0x03, 0x40, 0xd3, 0xf0, 0x00, 0x7e, 0xc4, 0xf0, 0x00, 0x0f, 0x41, 0xed, 0x00, 0xd0, 0xd2, 0xfd, 0x03, 0x25, 0xd3, 0xf0, 0x00, 0x7f, 0x10, 0xf4, 0x41, 0xed, 0x00, 0xd0, 0xd2, 0xfd, 0x03, 0x26, 0xd3, 0xf0, 0x00, 0x7f, 0xc4, 0xf0, 0x00, 0x0f, 0x41, 0xed, 0x00, 0xd0, 0xd2, 0xfd, 0x03, 0x27, 0xd3, 0x20, 0x00, 0x7d, 0xd3, 0x30, 0x00, 0x7c, 0xd3, 0x40, 0x00, 0x7a, 0x24, 0x50, 0xd3, 0xf0, 0x00, 0x7b, 0xcb, 0xf0, 0x00, 0x33, 0x23, 0x23, 0x11, 0xf1, 0x08, 0x5f, 0xe6, 0x7d, 0x03, 0x50, 0xe6, 0x8d, 0x04, 0x4f, 0x07, 0xcc, 0x41, 0xed, 0x01, 0xfc, 0xd1, 0xed, 0x03, 0x5c, 0xd0, 0xed, 0x03, 0x48, 0x58, 0xcd, 0x03, 0x58, 0x43, 0x3d, 0x01, 0x9c, 0xe6, 0x7d, 0x03, 0x50, 0x41, 0xed, 0x01, 0xfc, 0xe6, 0xed, 0x03, 0x54, 0x24, 0x15, 0xf8, 0xf0, 0x4f, 0x53, 0x33, 0x32, 0xd3, 0x7e, 0x00, 0x24, 0xc3, 0x70, 0x00, 0x10, 0x23, 0x3e, 0xc3, 0x70, 0x00, 0xe0, 0x21, 0x3b, 0x55, 0xfe, 0x00, 0x00, 0x21, 0x38, 0x58, 0x6e, 0x00, 0x08, 0x10, 0x68, 0x55, 0x6d, 0x03, 0x24, 0x43, 0x3d, 0x01, 0xb2, 0xca, 0xe0, 0x00, 0x30, 0x27, 0x11, 0x42, 0x3d, 0x01, 0x66, 0x58, 0xcd, 0x03, 0x50, 0x42, 0x3d, 0x01, 0x52, 0x48, 0x10, 0x00, 0x7e, 0x42, 0x3d, 0x02, 0xf0, 0x58, 0xcd, 0x03, 0x48, 0x43, 0x3d, 0x02, 0xf0, 0x58, 0x8d, 0x03, 0x4c, 0x23, 0x07, 0x58, 0xce, 0x00, 0x0c, 0x58, 0x8e, 0x00, 0x10, 0x0b, 0x8c, 0x26, 0xc1, 0x11, 0x88, 0x08, 0x18, 0xe6, 0xf0, 0x11, 0x18, 0x58, 0x0f, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, 0x42, 0x3d, 0x03, 0x08, 0x26, 0xf4, 0x26, 0x14, 0xc5, 0xf0, 0x12, 0x78, 0x20, 0x8c, 0x08, 0xd8, 0xcb, 0xd0, 0x01, 0xe8, 0x03, 0x08, 0x27, 0x81, 0x07, 0x77, 0x41, 0xed, 0x01, 0xfc, 0xd1, 0xed, 0x03, 0x40, 0xd0, 0xe0, 0x00, 0x78, 0x43, 0x00, 0x00, 0x60, 0xde, 0x2d, 0x03, 0x28, 0x08, 0x0c, 0x4d, 0x0d, 0x45, 0x00, 0x03, 0x30, 0x08, 0x91, 0x4d, 0x0d, 0x45, 0x00, 0x03, 0x38, 0x08, 0xa1, 0x08, 0xb0, 0x08, 0x55, 0x42, 0x2d, 0x02, 0x4a, 0xde, 0x3d, 0x03, 0x28, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x42, 0x1d, 0x02, 0xf4, 0xc3, 0xf0, 0x00, 0x10, 0x20, 0x35, 0x11, 0xa5, 0x06, 0xba, 0x98, 0x49, 0xde, 0x4d, 0x03, 0x2b, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x42, 0x7d, 0x02, 0xf8, 0x20, 0x83, 0x41, 0x6d, 0x02, 0x96, 0x22, 0x0b, 0x9d, 0x4f, 0xc3, 0xf0, 0x00, 0x19, 0x42, 0x3d, 0x02, 0xfc, 0xde, 0x4d, 0x03, 0x2c, 0x9d, 0x3f, 0x22, 0x21, 0x98, 0x49, 0xde, 0x4d, 0x03, 0x2e, 0x9d, 0x3f, 0x22, 0x21, 0xde, 0x4d, 0x03, 0x2d, 0x9d, 0x3f, 0x22, 0x21, 0x98, 0x4a, 0xde, 0x4d, 0x03, 0x2f, 0x9d, 0x3f, 0x22, 0x21, 0xde, 0x4d, 0x03, 0x2b, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x20, 0x81, 0xc3, 0xf0, 0x00, 0x53, 0x42, 0x3d, 0x03, 0x00, 0x08, 0xfa, 0x11, 0xfa, 0x06, 0xf9, 0xe6, 0x6d, 0x02, 0x54, 0x34, 0x77, 0x9a, 0x27, 0x34, 0x77, 0x98, 0x27, 0x34, 0x88, 0x9a, 0x28, 0x34, 0x88, 0x98, 0x28, 0x08, 0x55, 0x21, 0x24, 0x98, 0x49, 0x9a, 0x3b, 0x23, 0x03, 0x9a, 0x3b, 0x98, 0x3f, 0xde, 0x3d, 0x03, 0x2a, 0xde, 0x2d, 0x03, 0x29, 0x9d, 0x2f, 0x20, 0x81, 0xde, 0x2d, 0x03, 0x28, 0x9b, 0x20, 0x99, 0x21, 0x34, 0x00, 0x06, 0x01, 0xde, 0x2d, 0x03, 0x28, 0x9d, 0x3f, 0x22, 0x21, 0x42, 0x1d, 0x03, 0x04, 0xc3, 0xf0, 0x00, 0x10, 0x03, 0x3e, 0x0b, 0x07, 0x26, 0x04, 0xc4, 0x00, 0xff, 0x00, 0x0a, 0x70, 0x26, 0x91, 0x07, 0xaa, 0x07, 0xbb, 0x03, 0x06, 0x24, 0x11, 0x23, 0x0c, 0x24, 0x12, 0x23, 0x0a, 0x24, 0x13, 0x23, 0x08, 0x24, 0x14, 0x23, 0x06, 0x24, 0x15, 0x23, 0x04, 0x24, 0x16, 0x23, 0x02, 0x24, 0x17, 0x24, 0x01, 0xde, 0x0d, 0x03, 0x28, 0x9a, 0x01, 0xde, 0x0d, 0x03, 0x28, 0xd1, 0xed, 0x03, 0x40, 0xd0, 0xe0, 0x00, 0x78, 0x11, 0x0f, 0x95, 0x10, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x48, 0x30, 0xc1, 0xc2, 0xc8, 0xc4, 0xd0, 0xe0, 0x00, 0x30, 0x01, 0x90, 0x01, 0x40, 0x04, 0xc0, 0x00, 0x18, 0x00, 0x14, 0x00, 0x40, 0x00, 0x40, 0x00 }; /* Lower memory setup 78 = binary input device address 79 = binary device input command 7A = disk device number 7B = device code 7C = disk controller address 7D = selector channel address 7E:7F = operating system extension (user specified) */ struct dboot_id { char *name; uint32 sw; uint32 cap; uint32 dtype; uint32 offset; uint32 adder; }; static struct dboot_id dboot_tab[] = { { "DP", 0, 2, 0x31, o_DP0, 0 }, { "DP", SWMASK ('F'), 9, 0x32, o_DP0, o_DPF }, { "DP", 0, 9, 0x33, o_DP0, 0 }, { "DM", 0, 64, 0x35, o_ID0, 0 }, { "DM", 0, 244, 0x36, o_ID0, 0 }, { NULL } }; t_stat id_dboot (int32 u, DEVICE *dptr) { extern DIB ttp_dib, sch_dib; extern uint32 PC; extern int32 sim_switches; uint32 i, typ, ctlno, off, add, cap, sch_dev; UNIT *uptr; DIB *ddib = (DIB *) dptr->ctxt; /* get disk DIB */ ctlno = ddib->dno; /* get ctrl devno */ sch_dev = sch_dib.dno + ddib->sch; /* sch dev # */ uptr = dptr->units + u; /* get capacity */ cap = uptr->capac >> 20; for (i = typ = 0; dboot_tab[i].name != NULL; i++) { if ((strcmp (dboot_tab[i].name, dptr->name) == 0) && ((dboot_tab[i].sw == 0) || (dboot_tab[i].sw & sim_switches)) && (dboot_tab[i].cap == cap)) { typ = dboot_tab[i].dtype; off = dboot_tab[i].offset; add = dboot_tab[i].adder; break; } } if (typ == 0) return SCPE_NOFNC; IOWriteBlk (DBOOT_BEG, DBOOT_LEN, dboot_rom); /* copy boot */ IOWriteB (AL_DEV, ttp_dib.dno); /* bin input dev */ IOWriteB (AL_IOC, 0xa3); IOWriteB (AL_DSKU, ctlno + ((u + 1) * off) + add); /* disk dev addr */ IOWriteB (AL_DSKT, typ); /* disk type */ IOWriteB (AL_DSKC, ctlno); /* disk ctl addr */ IOWriteB (AL_SCH, sch_dev); PC = DBOOT_START; return SCPE_OK; } simh-3.8.1/Interdata/id_dp.c0000644000175000017500000006301111112026546013776 0ustar vlmvlm/* id_dp.c: Interdata 2.5MB/10MB cartridge disk simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dp M46-421 2.5MB/10MB cartridge disk 18-Mar-05 RMS Added attached test to detach routine 25-Jan-04 RMS Revised for device debug support 25-Apr-03 RMS Revised for extended file support 16-Feb-03 RMS Fixed read to test transfer ok before selch operation */ #include "id_defs.h" #include #define DP_NUMBY 256 /* bytes/sector */ #define DP_NUMSC 24 /* sectors/track */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_M_DTYPE 0x1 #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define CYL u3 /* current cylinder */ #define STD u4 /* drive status */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* Controller status */ #define STC_OVR 0x80 /* overrun */ #define STC_ACF 0x40 /* addr cmp fail */ #define STC_DEF 0x20 /* def track NI */ #define STC_CYO 0x10 /* cylinder ovflo */ #define STC_IDL 0x02 /* ctrl idle */ #define STC_DTE 0x01 /* xfer error */ #define SETC_EX (STC_OVR|STC_ACF|STC_DEF|STC_CYO) #define STC_MASK (STC_OVR|STC_ACF|STC_DEF|STC_CYO|STA_BSY|STC_IDL|STC_DTE) /* Controller command */ #define CMC_MASK 0xF #define CMC_CLR 0x8 /* reset */ #define CMC_RD 0x1 /* read */ #define CMC_WR 0x2 /* write */ #define CMC_RCHK 0x3 /* read check */ #define CMC_RFMT 0x5 /* read fmt NI */ #define CMC_WFMT 0x6 /* write fmt NI */ /* Drive status, ^ = dynamic, * = in unit status */ #define STD_WRP 0x80 /* ^write prot */ #define STD_WCK 0x40 /* write check NI */ #define STD_ILA 0x20 /* *illegal addr */ #define STD_ILK 0x10 /* ^addr interlock */ #define STD_MOV 0x08 /* *heads in motion */ #define STD_INC 0x02 /* seek incomplete NI */ #define STD_NRDY 0x01 /* ^not ready */ #define STD_UST (STD_ILA | STD_MOV) /* set from unit */ #define SETD_EX (STD_WCK | STD_ILA | STD_ILK) /* set examine */ /* Drive command */ #define CMD_SK 0x02 /* seek */ #define CMD_RST 0x01 /* restore */ /* Head/sector register */ #define HS_SMASK 0x1F /* sector mask */ #define HS_V_SRF 5 /* surface */ #define HS_HMASK 0x20 /* head mask */ #define HS_MASK (HS_HMASK | HS_SMASK) #define GET_SEC(x) ((x) & HS_SMASK) #define GET_SRF(x) (((x) & HS_HMASK) >> HS_V_SRF) #define GET_SA(p,cy,sf,sc,t) (((((((p)*drv_tab[t].cyl)+(cy))*drv_tab[t].surf)+(sf))* \ DP_NUMSC)+(sc)) #define GET_ROTATE(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DP_NUMSC))) /* This controller supports two different disk drive types: type #sectors/ #surfaces/ #cylinders/ surface cylinder drive 2315 24 2 203 5440 24 4 408 In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE AND MUST HAVE THE SAME SECTORS/TRACK. */ #define TYPE_2315 0 #define CYL_2315 203 #define SURF_2315 2 #define SIZE_2315 (DP_NUMSC * SURF_2315 * CYL_2315 * DP_NUMBY) #define TYPE_5440 1 #define CYL_5440 408 #define SURF_5440 2 #define SIZE_5440 (2 * DP_NUMSC * SURF_5440 * CYL_5440 * DP_NUMBY) struct drvtyp { int32 cyl; /* cylinders */ uint32 surf; /* surfaces */ uint32 size; /* #blocks */ }; static struct drvtyp drv_tab[] = { { CYL_2315, SURF_2315, SIZE_2315 }, { CYL_5440, SURF_5440, SIZE_5440 }, { 0 } }; extern uint32 int_req[INTSZ], int_enb[INTSZ]; extern FILE *sim_deb; uint8 dpxb[DP_NUMBY]; /* xfer buffer */ uint32 dp_bptr = 0; /* buffer ptr */ uint32 dp_db = 0; /* ctrl buffer */ uint32 dp_cyl = 0; /* drive buffer */ uint32 dp_sta = 0; /* ctrl status */ uint32 dp_cmd = 0; /* ctrl command */ uint32 dp_plat = 0; /* platter */ uint32 dp_hdsc = 0; /* head/sector */ uint32 dp_svun = 0; /* most recent unit */ uint32 dp_1st = 0; /* first byte */ uint32 dpd_arm[DP_NUMDR] = { 0 }; /* drives armed */ int32 dp_stime = 100; /* seek latency */ int32 dp_rtime = 100; /* rotate latency */ int32 dp_wtime = 1; /* word time */ uint8 dp_tplte[(2 * DP_NUMDR) + 2]; /* fix/rmv + ctrl + end */ DEVICE dp_dev; uint32 dp (uint32 dev, uint32 op, uint32 dat); void dp_ini (t_bool dtpl); t_stat dp_svc (UNIT *uptr); t_stat dp_reset (DEVICE *dptr); t_stat dp_attach (UNIT *uptr, char *cptr); t_stat dp_detach (UNIT *uptr); t_stat dp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dp_rds (UNIT *uptr); t_stat dp_wds (UNIT *uptr); t_bool dp_dter (UNIT *uptr, uint32 first); void dp_done (uint32 flg); extern t_stat id_dboot (int32 u, DEVICE *dptr); /* DP data structures dp_dev DP device descriptor dp_unit DP unit list dp_reg DP register list dp_mod DP modifier list */ DIB dp_dib = { d_DPC, 0, v_DPC, dp_tplte, &dp, &dp_ini }; UNIT dp_unit[] = { { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) } }; REG dp_reg[] = { { HRDATA (CMD, dp_cmd, 3) }, { HRDATA (STA, dp_sta, 8) }, { HRDATA (BUF, dp_db, 8) }, { HRDATA (PLAT, dp_plat, 1) }, { HRDATA (HDSC, dp_hdsc, 6) }, { HRDATA (CYL, dp_cyl, 9) }, { HRDATA (SVUN, dp_svun, 8), REG_HIDDEN }, { BRDATA (DBUF, dpxb, 16, 8, DP_NUMBY) }, { HRDATA (DBPTR, dp_bptr, 9), REG_RO }, { FLDATA (FIRST, dp_1st, 0) }, { GRDATA (IREQ, int_req[l_DPC], 16, DP_NUMDR + 1, i_DPC) }, { GRDATA (IENB, int_enb[l_DPC], 16, DP_NUMDR + 1, i_DPC) }, { BRDATA (IARM, dpd_arm, 16, 1, DP_NUMDR) }, { DRDATA (RTIME, dp_rtime, 0), PV_LEFT | REG_NZ }, { DRDATA (STIME, dp_stime, 0), PV_LEFT | REG_NZ }, { DRDATA (WTIME, dp_wtime, 0), PV_LEFT | REG_NZ }, { URDATA (UCYL, dp_unit[0].CYL, 16, 9, 0, DP_NUMDR, REG_RO) }, { URDATA (UST, dp_unit[0].STD, 16, 8, 0, DP_NUMDR, REG_RO) }, { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0, DP_NUMDR, PV_LEFT | REG_HRO) }, { HRDATA (DEVNO, dp_dib.dno, 8), REG_HRO }, { HRDATA (SELCH, dp_dib.sch, 2), REG_HRO }, { NULL } }; MTAB dp_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_2315 << UNIT_V_DTYPE) + UNIT_ATT, "2315", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_5440 << UNIT_V_DTYPE) + UNIT_ATT, "5440", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_2315 << UNIT_V_DTYPE), "2315", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_5440 << UNIT_V_DTYPE), "5440", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_2315 << UNIT_V_DTYPE), NULL, "2315", &dp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_5440 << UNIT_V_DTYPE), NULL, "5440", &dp_set_size }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", &set_sch, &show_sch, NULL }, { 0 } }; DEVICE dp_dev = { "DP", dp_unit, dp_reg, dp_mod, DP_NUMDR, 16, 24, 1, 16, 8, NULL, NULL, &dp_reset, &id_dboot, &dp_attach, &dp_detach, &dp_dib, DEV_DISABLE | DEV_DEBUG }; /* Controller: IO routine */ uint32 dpc (uint32 dev, uint32 op, uint32 dat) { uint32 f, t, u; UNIT *uptr; static uint8 good_cmd[8] = { 0, 1, 1, 1, 0, 0, 0, 0 }; switch (op) { /* case IO op */ case IO_ADR: /* select */ sch_adr (dp_dib.sch, dev); /* inform sel ch */ return BY; /* byte only */ case IO_RD: /* read data */ if (dp_sta & STC_IDL) /* if idle */ return GET_ROTATE (dp_rtime); /* return sector */ else dp_sta = dp_sta | STA_BSY; /* xfr? set busy */ return dp_db; /* return data */ case IO_WD: /* write data */ if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, ">>DPC WD = %02X, STA = %02X\n", dat, dp_sta); if (dp_sta & STC_IDL) /* idle? hdsc */ dp_hdsc = dat & HS_MASK; else { /* data xfer */ dp_sta = dp_sta | STA_BSY; /* set busy */ dp_db = dat & 0xFF; /* store data */ } break; case IO_SS: /* status */ t = dp_sta & STC_MASK; /* get status */ if (t & SETC_EX) /* test for EX */ t = t | STA_EX; return t; case IO_OC: /* command */ if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, ">>DPC OC = %02X, STA = %02X\n", dat, dp_sta); f = dat & CMC_MASK; /* get cmd */ if (f & CMC_CLR) { /* clear? */ dp_reset (&dp_dev); /* reset world */ break; } u = (dp_svun - dp_dib.dno - o_DP0) / o_DP0; /* get unit */ uptr = dp_dev.units + u; /* ignore if busy */ if (!(dp_sta & STC_IDL) || sim_is_active (uptr)) break; dp_cmd = f; /* save cmd */ if (dp_cmd == CMC_WR) /* write: bsy=0 else */ dp_sta = 0; else dp_sta = STA_BSY; /* bsy=1,idl,err=0 */ dp_1st = 1; /* xfr not started */ dp_bptr = 0; /* buffer empty */ if (dp_svun & o_DPF) /* upper platter? */ dp_plat = 1; else dp_plat = 0; /* no, lower */ if (good_cmd[f]) /* legal? sched */ sim_activate (uptr, dp_rtime); break; } return 0; } /* Drives: IO routine */ uint32 dp (uint32 dev, uint32 op, uint32 dat) { int32 diff; uint32 t, u; UNIT *uptr; if (dev == dp_dib.dno) /* controller? */ return dpc (dev, op, dat); u = (dev - dp_dib.dno - o_DP0) / o_DP0; /* get unit num */ uptr = dp_dev.units + u; /* get unit ptr */ switch (op) { /* case IO op */ case IO_ADR: /* select */ if (dp_sta & STC_IDL) /* idle? save unit */ dp_svun = dev; return BY; /* byte only */ case IO_WD: /* write data */ if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, ">>DP%d WD = %02X, STA = %02X\n", u, dat, dp_sta); if (GET_DTYPE (uptr->flags) == TYPE_2315) /* 2.5MB drive? */ dp_cyl = dat & 0xFF; /* cyl is 8b */ else dp_cyl = ((dp_cyl << 8) | dat) & DMASK16; /* insert byte */ break; case IO_SS: /* status */ if (uptr->flags & UNIT_ATT) t = /* onl? */ ((uptr->flags & UNIT_WPRT)? STD_WRP: 0) | ((dp_sta & STC_IDL)? 0: STD_ILK) | (uptr->STD & STD_UST); else t = STD_MOV | STD_NRDY; /* off = X'09' */ if (t & SETD_EX) /* test for ex */ t = t | STA_EX; return t; case IO_OC: /* command */ if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, ">>DP%d OC = %02X, STA = %02X\n", u, dat, dp_sta); dpd_arm[u] = int_chg (v_DPC + u + 1, dat, dpd_arm[u]); if (dat & CMD_SK) /* seek? get cyl */ t = dp_cyl; else if (dat & CMD_RST) /* rest? cyl 0 */ t = 0; else break; /* no action */ diff = t - uptr->CYL; if (diff < 0) /* ABS cyl diff */ diff = -diff; else if (diff == 0) /* must be nz */ diff = 1; uptr->STD = STD_MOV; /* stat = moving */ uptr->CYL = t; /* put on cyl */ sim_activate (uptr, diff * dp_stime); /* schedule */ break; } return 0; } /* Unit service If seek done, on cylinder; if read check, signal completion; else, do read or write */ t_stat dp_svc (UNIT *uptr) { uint32 u = uptr - dp_dev.units; /* get unit number */ int32 cyl = uptr->CYL; /* get cylinder */ uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ uint32 t; t_stat r; if (uptr->STD & STD_MOV) { /* seek? */ uptr->STD = 0; /* clr seek in prog */ if ((uptr->flags & UNIT_ATT) == 0) /* offl? hangs */ return SCPE_OK; if (cyl >= drv_tab[dtype].cyl) { /* bad cylinder? */ uptr->STD = STD_ILA; /* error */ uptr->CYL = drv_tab[dtype].cyl - 1; /* put at edge */ } if (dpd_arm[u]) /* req intr */ SET_INT (v_DPC + u + 1); return SCPE_OK; } switch (dp_cmd & 0x7) { /* case on func */ case CMC_RCHK: /* read check */ dp_dter (uptr, 1); /* check xfr err */ break; case CMC_RD: /* read */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ if (dp_dter (uptr, dp_1st)) /* check xfr err */ return SCPE_OK; if (r = dp_rds (uptr)) /* read sec, err? */ return r; dp_1st = 0; t = sch_wrmem (dp_dib.sch, dpxb, DP_NUMBY); /* write to memory */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ sim_activate (uptr, dp_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } dp_sta = dp_sta | STC_DTE; /* can't work */ break; case CMC_WR: /* write */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ if (dp_dter (uptr, dp_1st)) /* check xfr err */ return SCPE_OK; dp_bptr = sch_rdmem (dp_dib.sch, dpxb, DP_NUMBY); /* read from mem */ dp_db = dpxb[dp_bptr - 1]; /* last byte */ if (r = dp_wds (uptr)) /* write sec, err? */ return r; dp_1st = 0; if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ sim_activate (uptr, dp_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } dp_sta = dp_sta | STC_DTE; /* can't work */ break; } /* end case func */ dp_done (0); /* done */ return SCPE_OK; } /* Read data sector */ t_stat dp_rds (UNIT *uptr) { uint32 i; i = fxread (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); for ( ; i < DP_NUMBY; i++) /* fill with 0's */ dpxb[i] = 0; if (ferror (uptr->fileref)) { /* error? */ perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (STC_DTE); return SCPE_IOERR; } return SCPE_OK; } /* Write data sector */ t_stat dp_wds (UNIT *uptr) { for ( ; dp_bptr < DP_NUMBY; dp_bptr++) dpxb[dp_bptr] = dp_db; /* fill with last */ fxwrite (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (STC_DTE); return SCPE_IOERR; } return SCPE_OK; } /* Data transfer error test routine */ t_bool dp_dter (UNIT *uptr, uint32 first) { uint32 hd, sc, sa; uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ ((uptr->flags & UNIT_WPRT) && (dp_cmd == CMC_WR))) { dp_done (STC_DTE); /* error, done */ return TRUE; } hd = GET_SRF (dp_hdsc); /* get head */ sc = GET_SEC (dp_hdsc); /* get sector */ if (dp_cyl != (uint32) uptr->CYL) { /* wrong cylinder? */ if (dp_cyl == 0) uptr->CYL = 0; else { dp_done (STC_ACF); /* error, done */ return TRUE; } } if (sc >= DP_NUMSC) { /* bad sector? */ dp_done (STC_OVR); /* error, done */ return TRUE; } if (!first && (sc == 0) && (hd == 0)) { /* cyl overflow? */ dp_done (STC_CYO); /* error, done */ return TRUE; } sa = GET_SA (dp_plat, uptr->CYL, hd, sc, dtype); /* curr disk addr */ fseek (uptr->fileref, sa * DP_NUMBY, SEEK_SET); if ((sc + 1) < DP_NUMSC) /* end of track? */ dp_hdsc = dp_hdsc + 1; else dp_hdsc = (dp_hdsc ^ HS_HMASK) & HS_HMASK; /* sec 0, nxt srf */ return FALSE; } /* Data transfer done routine */ void dp_done (uint32 flg) { dp_sta = (dp_sta | STC_IDL | flg) & ~STA_BSY; /* set flag, idle */ SET_INT (v_DPC); /* unmaskable intr */ if (flg) /* if err, stop ch */ sch_stop (dp_dib.sch); return; } /* Reset routine */ t_stat dp_reset (DEVICE *dptr) { uint32 u; UNIT *uptr; dp_cmd = 0; /* clear cmd */ dp_sta = STA_BSY | STC_IDL; /* idle, busy */ dp_1st = 0; /* clear flag */ dp_svun = dp_db = 0; /* clear unit, buf */ dp_plat = 0; dp_hdsc = 0; /* clear addr */ CLR_INT (v_DPC); /* clear ctrl int */ SET_ENB (v_DPC); /* always enabled */ for (u = 0; u < DP_NUMDR; u++) { /* loop thru units */ uptr = dp_dev.units + u; uptr->CYL = uptr->STD = 0; CLR_INT (v_DPC + u + 1); /* clear intr */ CLR_ENB (v_DPC + u + 1); /* clear enable */ dpd_arm[u] = 0; /* clear arm */ sim_cancel (uptr); /* cancel activity */ } return SCPE_OK; } /* Attach routine (with optional autosizing) */ t_stat dp_attach (UNIT *uptr, char *cptr) { uint32 i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; uptr->CYL = 0; if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; if ((p = ftell (uptr->fileref)) == 0) return SCPE_OK; for (i = 0; drv_tab[i].surf != 0; i++) { if (p <= drv_tab[i].size) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Detach routine (generates an interrupt) */ t_stat dp_detach (UNIT *uptr) { uint32 u = uptr - dp_dev.units; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (dpd_arm[u]) /* if arm, intr */ SET_INT (v_DPC + u + 1); return detach_unit (uptr); } /* Set size command validation routine */ t_stat dp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[GET_DTYPE (val)].size; return SCPE_OK; } /* Create device number (T) or interrupt (F) template */ void dp_ini (t_bool dtpl) { int32 u, j, dev; dp_tplte[0] = 0; /* controller */ for (u = 0, j = 1; u < DP_NUMDR; u++) { /* loop thru units */ dev = (u + 1) * o_DP0; /* drive dev # */ dp_tplte[j++] = dev; if (dtpl && (GET_DTYPE (dp_unit[u].flags) == TYPE_5440)) dp_tplte[j++] = dev + o_DPF; /* if fixed */ } dp_tplte[j] = TPL_END; /* end marker */ return; } simh-3.8.1/Interdata/id32_cpu.c0000644000175000017500000033544511112027352014340 0ustar vlmvlm/* id32_cpu.c: Interdata 32b CPU simulator Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu Interdata 32b CPU 28-Apr-07 RMS Removed clock initialization 27-Oct-06 RMS Added idle support Removed separate PASLA clock 09-Mar-06 RMS Added 8 register bank support for 8/32 06-Feb-06 RMS Fixed bug in DH (found by Mark Hittinger) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Mar-05 RMS Fixed bug in initial memory allocation RMS Fixed bug in show history routine (from Mark Hittinger) RMS Revised examine/deposit to do words rather than bytes 18-Feb-05 RMS Fixed branches to mask new PC (from Greg Johnson) 06-Nov-04 RMS Added =n to SHOW HISTORY 25-Jan-04 RMS Revised for device debug support 31-Dec-03 RMS Fixed bug in cpu_set_hist 22-Sep-03 RMS Added additional instruction decode types Added instruction history The register state for an Interdata 32b CPU is: REG[0:F][2]<0:31> general register sets F[0:7]<0:31> single precision floating point registers D[0:7]<0:63> double precision floating point registers PSW<0:63> processor status word, including STAT<0:11> status flags CC<0:3> condition codes PC<0:31> program counter int_req[n]<0:31> interrupt requests int_enb[n]<0:31> interrupt enables The Interdata 32b systems have seven instruction formats: register to register, short format, register and memory (three formats), and register and immediate (two formats). The formats are: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | R2 | register-register +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | N | short format +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-memory 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (absolute 14b) | 0| 0| address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-memory 2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (relative) | 1| address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-memory 3 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (double index) | 0| 1| 0| 0| RX2 | address hi | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | address lo | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-immediate 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | immediate | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-immediate 2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | immediate hi | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | immediate lo | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ For register-memory 1 and register-immediate 1 and 2 an instructions, an effective address is calculated as follows: effective addr = address + RX (if RX > 0) For register-memory 2, an effective address is calculated as follows: effective addr = address + PC + RX (if RX > 0) For register-memory 3, an effective address is calculated as follows: effective addr = address + RX (if RX > 0) + RX2 (if RX2 > 0) Register-memory instructions can access an address space of 16M bytes. This routine is the instruction decode routine for the Interdata CPU. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered wait state and no I/O outstanding invalid instruction I/O error in I/O simulator 2. Interrupts. Each device has an interrupt armed flag, an interrupt request flag, and an interrupt enabled flag. To facilitate evaluation, all interrupt requests are kept in int_req, and all enables in int_enb. Interrupt armed flags are local to devices. If external interrupts are enabled in the PSW, and a request is pending, an interrupt occurs. 3. Non-existent memory. On the Interdata 32b, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: id_defs.h add device interrupt definitions id32_sys.c add sim_devices table entry */ #include "id_defs.h" #include #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] = oPC #define VAMASK VAMASK32 #define NRSETS 8 /* up to 8 reg sets */ #define PSW_MASK PSW_x32 #define ABORT(val) longjmp (save_env, (val)) #define MPRO (-1) #define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ #define UNIT_V_DPFP (UNIT_V_UF + 1) #define UNIT_V_832 (UNIT_V_UF + 2) #define UNIT_V_8RS (UNIT_V_UF + 3) #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define UNIT_DPFP (1 << UNIT_V_DPFP) #define UNIT_832 (1 << UNIT_V_832) #define UNIT_8RS (1 << UNIT_V_8RS) #define UNIT_TYPE (UNIT_DPFP | UNIT_832) #define HIST_PC 0x40000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { uint32 pc; uint32 ir1; uint32 ir2; uint32 ir3; uint32 r1; uint32 ea; uint32 opnd; } InstHistory; #define PSW_GETREG(x) (((x) >> PSW_V_REG) & psw_reg_mask) #define SEXT32(x) (((x) & SIGN32)? ((int32) ((x) | ~0x7FFFFFFF)): \ ((int32) ((x) & 0x7FFFFFFF))) #define SEXT16(x) (((x) & SIGN16)? ((int32) ((x) | ~0x7FFF)): \ ((int32) ((x) & 0x7FFF))) #define SEXT15(x) (((x) & 0x4000)? ((int32) ((x) | ~0x3FFF)): \ ((int32) ((x) & 0x3FFF))) #define CC_GL_16(x) if ((x) & SIGN16) \ cc = CC_L; \ else if (x) \ cc = CC_G; \ else cc = 0 #define CC_GL_32(x) if ((x) & SIGN32) \ cc = CC_L; \ else if (x) \ cc = CC_G; \ else cc = 0 #define BUILD_PSW(x) (((PSW & ~CC_MASK) | (x)) & PSW_MASK) #define NEG(x) ((~(x) + 1) & DMASK32) #define ABS(x) (((x) & SIGN32)? NEG (x): (x)) #define DNEG(x,y) y = NEG (y); \ x = (~(x) + (y == 0)) & DMASK32 /* Logging */ #define LOG_CPU_I 0x0001 /* intr/exception */ #define LOG_CPU_C 0x0002 /* context change */ uint32 GREG[16 * NRSETS] = { 0 }; /* general registers */ uint32 *M = NULL; /* memory */ uint32 *R = &GREG[0]; /* working reg set */ uint32 F[8] = { 0 }; /* sp fp registers */ dpr_t D[8] = { 0 }; /* dp fp registers */ uint32 PSW = 0; /* processor status word */ uint32 PC = 0; /* program counter */ uint32 oPC = 0; /* PC at inst start */ uint32 SR = 0; /* switch register */ uint32 DR = 0; /* display register */ uint32 DRX = 0; /* display extension */ uint32 drmod = 0; /* mode */ uint32 srpos = 0; /* switch register pos */ uint32 drpos = 0; /* display register pos */ uint32 mac_reg[MAC_LNT] = { 0 }; /* mac registers */ uint32 mac_sta = 0; /* mac status */ uint32 int_req[INTSZ] = { 0 }; /* interrupt requests */ uint32 int_enb[INTSZ] = { 0 }; /* interrupt enables */ uint32 qevent = 0; /* events */ uint32 stop_inst = 0; /* stop on ill inst */ uint32 stop_wait = 0; /* stop on wait */ uint32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ uint32 dec_flgs = 0; /* decode flags */ uint32 fp_in_hwre = 0; /* ucode vs hwre fp */ uint32 pawidth = PAWIDTH32; /* addr mask */ uint32 hst_p = 0; /* history pointer */ uint32 hst_lnt = 0; /* history length */ uint32 psw_reg_mask = 1; /* PSW reg mask */ InstHistory *hst = NULL; /* instruction history */ jmp_buf save_env; /* abort handler */ struct BlockIO blk_io; /* block I/O status */ uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout) = { NULL }; extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern t_bool sim_idle_enab; extern FILE *sim_deb; uint32 ReadB (uint32 loc, uint32 rel); uint32 ReadH (uint32 loc, uint32 rel); void WriteB (uint32 loc, uint32 val, uint32 rel); void WriteH (uint32 loc, uint32 val, uint32 rel); uint32 RelocT (uint32 va, uint32 base, uint32 rel, uint32 *pa); uint32 int_auto (uint32 dev, uint32 cc); uint32 addtoq (uint32 ea, uint32 val, uint32 flg); uint32 remfmq (uint32 ea, uint32 r1, uint32 flg); uint32 exception (uint32 loc, uint32 cc, uint32 flg); uint32 newPSW (uint32 val); uint32 testsysq (uint32 cc); uint32 display (uint32 dev, uint32 op, uint32 dat); 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_consint (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); void set_r_display (uint32 *rbase); extern t_bool devtab_init (void); extern void int_eval (void); extern uint32 int_getdev (void); extern void sch_cycle (uint32 ch); extern t_bool sch_blk (uint32 dev); extern uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_fix32 (uint32 op, uint32 r1, uint32 r2); extern uint32 f_flt32 (uint32 op, uint32 r1, uint32 r2); /* Instruction decoding table */ const uint16 decrom[256] = { 0, /* 00 */ OP_RR, /* BALR */ OP_RR, /* BTCR */ OP_RR, /* BFCR */ OP_RR, /* NR */ OP_RR, /* CLR */ OP_RR, /* OR */ OP_RR, /* XR */ OP_RR, /* LR */ OP_RR, /* CR */ OP_RR, /* AR */ OP_RR, /* SR */ OP_RR, /* MHR */ OP_RR, /* DHR */ 0, 0, /* 0E:0F */ OP_NO, /* SRLS */ OP_NO, /* SLLS */ OP_RR, /* CHVR */ 0, 0, 0, 0, 0, /* 13:17 */ OP_RR | OP_PRV, /* LPSWR */ 0, 0, 0, /* 19:1B */ OP_RR, /* MR */ OP_RR, /* DR */ 0, 0, /* 1E:1F */ OP_NO, /* BTBS */ OP_NO, /* BTFS */ OP_NO, /* BFBS */ OP_NO, /* BFFS */ OP_NO, /* LIS */ OP_NO, /* LCS */ OP_NO, /* AIS */ OP_NO, /* SIS */ OP_NO, /* LER */ OP_NO, /* CER */ OP_NO, /* AER */ OP_NO, /* SER */ OP_NO, /* MER */ OP_NO, /* DER */ OP_NO, /* FXR */ OP_NO, /* FLR */ 0, /* MPBSR - 8/32C */ 0, /* 31 */ 0, /* PBR - 8/32C */ 0, /* 33 */ OP_RR, /* EXHR */ 0, 0, 0, /* 35:37 */ OP_NO | OP_DPF, /* LDR */ OP_NO | OP_DPF, /* CDR */ OP_NO | OP_DPF, /* ADR */ OP_NO | OP_DPF, /* SDR */ OP_NO | OP_DPF, /* MDR */ OP_NO | OP_DPF, /* DDR */ OP_NO | OP_DPF, /* FXDR */ OP_NO | OP_DPF, /* FLDR */ OP_RX, /* STH */ OP_RX, /* BAL */ OP_RX, /* BTC */ OP_RX, /* BFC */ OP_RXH, /* NH */ OP_RXH, /* CLH */ OP_RXH, /* OH */ OP_RXH, /* XH */ OP_RXH, /* LH */ OP_RXH, /* CH */ OP_RXH, /* AH */ OP_RXH, /* SH */ OP_RXH, /* MH */ OP_RXH, /* DH */ 0, 0, /* 4E:4F */ OP_RX, /* ST */ OP_RXF, /* AM */ 0, 0, /* 52:53 */ OP_RXF, /* N */ OP_RXF, /* CL */ OP_RXF, /* O */ OP_RXF, /* X */ OP_RXF, /* L */ OP_RXF, /* C */ OP_RXF, /* A */ OP_RXF, /* S */ OP_RXF, /* M */ OP_RXF, /* D */ OP_RXH, /* CRC12 */ OP_RXH, /* CRC16 */ OP_RX, /* STE */ OP_RXH, /* AHM */ 0, /* PB - 8/32C */ OP_RX, /* LRA */ OP_RX, /* ATL */ OP_RX, /* ABL */ OP_RX, /* RTL */ OP_RX, /* RBL */ OP_RX, /* LE */ OP_RX, /* CE */ OP_RX, /* AE */ OP_RX, /* SE */ OP_RX, /* ME */ OP_RX, /* DE */ 0, 0, /* 6E:6F */ OP_RX | OP_DPF, /* STD */ OP_RX, /* SME */ OP_RX, /* LME */ OP_RXH, /* LHL */ OP_RX, /* TBT */ OP_RX, /* SBT */ OP_RX, /* RBT */ OP_RX, /* CBT */ OP_RX | OP_DPF, /* LD */ OP_RX | OP_DPF, /* CD */ OP_RX | OP_DPF, /* AD */ OP_RX | OP_DPF, /* SD */ OP_RX | OP_DPF, /* MD */ OP_RX | OP_DPF, /* DD */ OP_RX | OP_DPF, /* STMD */ OP_RX | OP_DPF, /* LMD */ 0, 0, 0, 0, 0, 0, 0, 0, /* 80:8F */ 0, 0, 0, 0, 0, 0, 0, 0, OP_NO, /* SRHLS */ OP_NO, /* SLHLS */ OP_NO, /* STBR */ OP_RR, /* LDBR */ OP_RR, /* EXBR */ OP_NO | OP_PRV, /* EPSR */ OP_RR | OP_PRV, /* WBR */ OP_RR | OP_PRV, /* RBR */ OP_RR | OP_PRV, /* WHR */ OP_RR | OP_PRV, /* RHR */ OP_RR | OP_PRV, /* WDR */ OP_RR | OP_PRV, /* RDR */ 0, /* 9C */ OP_RR | OP_PRV, /* SSR */ OP_RR | OP_PRV, /* OCR */ 0, /* 9F */ 0, 0, 0, 0, 0, 0, 0, 0, /* A0:AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0:BF */ 0, 0, 0, 0, 0, 0, 0, 0, OP_RX, /* BXH */ OP_RX, /* BXLE */ OP_RXF | OP_PRV, /* LPSW */ OP_RI1, /* THI */ OP_RI1, /* NHI */ OP_RI1, /* CLHI */ OP_RI1, /* OHI */ OP_RI1, /* XHI */ OP_RI1, /* LHI */ OP_RI1, /* CHI */ OP_RI1, /* AHI */ OP_RI1, /* SHI */ OP_RI1, /* SRHL */ OP_RI1, /* SLHL */ OP_RI1, /* SRHA */ OP_RI1, /* SLHA */ OP_RX, /* STM */ OP_RX, /* LM */ OP_RX, /* STB */ OP_RXB, /* LDB */ OP_RXB, /* CLB */ OP_RX | OP_PRV, /* AL */ OP_RXF | OP_PRV, /* WB */ OP_RXF | OP_PRV, /* RB */ OP_RX | OP_PRV, /* WH */ OP_RX | OP_PRV, /* RH */ OP_RX | OP_PRV, /* WD */ OP_RX | OP_PRV, /* RD */ 0, /* DC */ OP_RX | OP_PRV, /* SS */ OP_RX | OP_PRV, /* OC */ 0, /* DF */ OP_RXH, /* TS */ OP_RX, /* SVC */ OP_RI1 | OP_PRV, /* SINT */ OP_RXH | OP_PRV, /* SCP */ 0, 0, /* E4:E5 */ OP_RX, /* LA */ OP_RXF, /* TLATE */ 0, 0, /* E8:E9 */ OP_RI1, /* RRL */ OP_RI1, /* RLL */ OP_RI1, /* SRL */ OP_RI1, /* SLL */ OP_RI1, /* SRA */ OP_RI1, /* SLA */ 0, 0, 0, /* F0:F2 */ OP_RI2, /* TI */ OP_RI2, /* NI */ OP_RI2, /* CLI */ OP_RI2, /* OI */ OP_RI2, /* XI */ OP_RI2, /* LI */ OP_RI2, /* CI */ OP_RI2, /* AI */ OP_RI2, /* SI */ 0, 0, 0, 0 /* FC:FF */ }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ DIB cpu_dib = { d_DS, -1, v_DS, NULL, &display }; UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK, MAXMEMSIZE32) }; REG cpu_reg[] = { { HRDATA (PC, PC, 20) }, { HRDATA (OPC, oPC, 20), REG_HRO }, { HRDATA (R0, GREG[0], 32) }, { HRDATA (R1, GREG[1], 32) }, { HRDATA (R2, GREG[2], 32) }, { HRDATA (R3, GREG[3], 32) }, { HRDATA (R4, GREG[4], 32) }, { HRDATA (R5, GREG[5], 32) }, { HRDATA (R6, GREG[6], 32) }, { HRDATA (R7, GREG[7], 32) }, { HRDATA (R8, GREG[8], 32) }, { HRDATA (R9, GREG[9], 32) }, { HRDATA (R10, GREG[10], 32) }, { HRDATA (R11, GREG[11], 32) }, { HRDATA (R12, GREG[12], 32) }, { HRDATA (R13, GREG[13], 32) }, { HRDATA (R14, GREG[14], 32) }, { HRDATA (R15, GREG[15], 32) }, { HRDATA (FR0, F[0], 32) }, { HRDATA (FR2, F[1], 32) }, { HRDATA (FR4, F[2], 32) }, { HRDATA (FR6, F[3], 32) }, { HRDATA (FR8, F[4], 32) }, { HRDATA (FR10, F[5], 32) }, { HRDATA (FR12, F[6], 32) }, { HRDATA (FR14, F[7], 32) }, { HRDATA (D0H, D[0].h, 32) }, { HRDATA (D0L, D[0].l, 32) }, { HRDATA (D2H, D[1].h, 32) }, { HRDATA (D2L, D[1].l, 32) }, { HRDATA (D4H, D[2].h, 32) }, { HRDATA (D4L, D[2].l, 32) }, { HRDATA (D6H, D[3].h, 32) }, { HRDATA (D6L, D[3].l, 32) }, { HRDATA (D8H, D[4].h, 32) }, { HRDATA (D8L, D[4].l, 32) }, { HRDATA (D10H, D[5].h, 32) }, { HRDATA (D10L, D[5].l, 32) }, { HRDATA (D12L, D[6].l, 32) }, { HRDATA (D12H, D[6].h, 32) }, { HRDATA (D14H, D[7].h, 32) }, { HRDATA (D14L, D[7].l, 32) }, { HRDATA (PSW, PSW, 16) }, { HRDATA (CC, PSW, 4) }, { HRDATA (SR, SR, 32) }, { HRDATA (DR, DR, 32) }, { HRDATA (DRX, DRX, 8) }, { FLDATA (DRMOD, drmod, 0) }, { FLDATA (SRPOS, srpos, 0) }, { HRDATA (DRPOS, drpos, 3) }, { BRDATA (IRQ, int_req, 16, 32, INTSZ) }, { BRDATA (IEN, int_enb, 16, 32, INTSZ) }, { BRDATA (MACREG, mac_reg, 16, 32, MAC_LNT) }, { HRDATA (MACSTA, mac_sta, 5) }, { HRDATA (QEVENT, qevent, 4), REG_HRO }, { FLDATA (STOP_INST, stop_inst, 0) }, { FLDATA (STOP_WAIT, stop_wait, 0) }, { BRDATA (PCQ, pcq, 16, 20, PCQ_SIZE), REG_RO+REG_CIRC }, { HRDATA (PCQP, pcq_p, 6), REG_HRO }, { HRDATA (WRU, sim_int_char, 8) }, { HRDATA (BLKIOD, blk_io.dfl, 16), REG_HRO }, { HRDATA (BLKIOC, blk_io.cur, 20), REG_HRO }, { HRDATA (BLKIOE, blk_io.end, 20), REG_HRO }, { BRDATA (GREG, GREG, 16, 32, 16 * NRSETS) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_8RS|UNIT_TYPE, 0, NULL, "732", NULL }, { UNIT_DPFP, UNIT_DPFP, NULL, "DPFP", NULL }, { UNIT_TYPE, 0, "7/32, single precision fp", "732", NULL }, { UNIT_TYPE, UNIT_DPFP, "7/32, double precision fp", NULL, NULL }, { UNIT_8RS|UNIT_TYPE, UNIT_8RS|UNIT_DPFP|UNIT_832, NULL, "832", NULL }, { UNIT_8RS, 0, NULL, "2RS", NULL }, { UNIT_8RS|UNIT_TYPE, UNIT_8RS|UNIT_DPFP|UNIT_832, "832, 8 register sets", NULL, NULL }, { UNIT_8RS|UNIT_TYPE, UNIT_DPFP|UNIT_832, "832, 2 register sets", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, { UNIT_MSIZE, 1048756, NULL, "1M", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "CONSINT", &cpu_set_consint, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEBTAB cpu_deb[] = { { "INTEXC", LOG_CPU_I }, { "CONTEXT", LOG_CPU_C }, { NULL, 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 16, 20, 2, 16, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, &cpu_dib, DEV_DEBUG, 0, cpu_deb, NULL, NULL }; t_stat sim_instr (void) { volatile uint32 cc; /* set before setjmp */ t_stat reason; /* set after setjmp */ int abortval; /* Restore register state */ if (devtab_init ()) /* check conflicts */ return SCPE_STOP; if (cpu_unit.flags & (UNIT_DPFP | UNIT_832)) { fp_in_hwre = 1; /* fp in hwre */ dec_flgs = 0; /* all instr ok */ } else { fp_in_hwre = 0; /* fp in ucode */ dec_flgs = OP_DPF; /* sp only */ } if (cpu_unit.flags & UNIT_8RS) /* 8 register sets */ psw_reg_mask = 7; else psw_reg_mask = 1; /* 2 register sets */ int_eval (); /* eval interrupts */ cc = newPSW (PSW & PSW_MASK); /* split PSW, eval wait */ reason = 0; /* Abort handling If an abort occurs in memory protection, the relocation routine executes a longjmp to this area OUTSIDE the main simulation loop. Memory protection errors are the only sources of aborts in the Interdata 32b systems. All referenced variables must be globals, and all sim_instr scoped automatic variables must be volatile or set after the call on setjmp. */ abortval = setjmp (save_env); /* set abort hdlr */ if (abortval != 0) { /* mem mgt abort? */ qevent = qevent | EV_MAC; /* set MAC intr */ if (cpu_unit.flags & UNIT_832) /* 832? restore PC */ PC = oPC; } /* Event handling */ while (reason == 0) { /* loop until halted */ uint32 dev, drom, opnd, inc, lim, bufa; uint32 op, r1, r1p1, r2, rx2, ea; uint32 mpy, mpc, dvr; uint32 i, rslt, rlo, t; uint32 ir1, ir2, ir3, ityp; int32 sr, st; if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; int_eval (); } if (qevent) { /* any events? */ if (qevent & EV_MAC) { /* MAC interrupt? */ qevent = 0; /* clr all events */ cc = exception (MPRPSW, cc, 0); /* take exception */ int_eval (); /* re-eval intr */ continue; } if (qevent & EV_BLK) { /* block I/O in prog? */ dev = blk_io.dfl & DEV_MAX; /* get device */ cc = dev_tab[dev] (dev, IO_SS, 0) & 0xF; /* sense status */ if (cc == STA_BSY) { /* just busy? */ sim_interval = 0; /* force I/O event */ continue; } else if (cc == 0) { /* ready, no err? */ if (blk_io.dfl & BL_RD) { /* read? */ t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ if ((t == 0) && (blk_io.dfl & BL_LZ)) continue; blk_io.dfl = blk_io.dfl & ~BL_LZ; /* non-zero seen */ WriteB (blk_io.cur, t, VW); /* write mem */ } else { /* write */ t = ReadB (blk_io.cur, VR); /* read mem */ dev_tab[dev] (dev, IO_WD, t); /* put byte */ } if (blk_io.cur != blk_io.end) { /* more to do? */ blk_io.cur = (blk_io.cur + 1) & VAMASK; /* incr addr */ continue; } } qevent = qevent & ~EV_BLK; /* clr blk I/O flag */ int_eval (); /* re-eval intr */ continue; } if ((qevent & EV_INT) && (PSW & PSW_EXI)) { /* interrupt? */ dev = int_getdev (); /* get int dev */ cc = int_auto (dev, cc); /* do auto intr */ int_eval (); /* re-eval intr */ continue; } if (PSW & PSW_WAIT) { /* wait state? */ if (sim_idle_enab) /* idling enabled? */ sim_idle (TMR_LFC, TRUE); else sim_interval = sim_interval - 1; /* no, count cycle */ continue; } qevent = 0; /* no events */ } /* Instruction fetch and decode */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } sim_interval = sim_interval - 1; ir1 = ReadH (oPC = PC, VE); /* fetch instr */ op = (ir1 >> 8) & 0xFF; /* extract op,R1,R2 */ r1 = (ir1 >> 4) & 0xF; r2 = ir1 & 0xF; drom = decrom[op]; /* get decode flags */ ityp = drom & OP_MASK; /* instruction type */ if ((drom == 0) || (drom & dec_flgs)) { /* not in model? */ if (stop_inst) /* stop or */ reason = STOP_RSRV; else cc = exception (ILOPSW, cc, 0); /* exception */ continue; } if ((drom & OP_PRV) && (PSW & PSW_PRO)) { /* priv & protected? */ cc = exception (ILOPSW, cc, 0); /* exception */ continue; } switch (ityp) { /* decode instruction */ case OP_NO: /* no operand */ opnd = r2; /* assume short */ PC = (PC + 2) & VAMASK; /* increment PC */ break; case OP_RR: /* reg-reg */ opnd = R[r2]; /* ea/operand is R2 */ PC = (PC + 2) & VAMASK; /* increment PC */ break; case OP_RI1: /* reg-imm 1 */ ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch immed */ opnd = SEXT16 (ir2); /* sign extend */ if (r2) /* index calculation */ opnd = (opnd + R[r2]) & DMASK32; PC = (PC + 4) & VAMASK; /* increment PC */ break; case OP_RI2: /* reg-imm 2 */ ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch imm hi */ ir3 = ReadH ((PC + 4) & VAMASK, VE); /* fetch imm lo */ opnd = (ir2 << 16) | ir3; /* 32b immediate */ if (r2) /* index calculation */ opnd = (opnd + R[r2]) & DMASK32; PC = (PC + 6) & VAMASK; /* increment PC */ break; case OP_RX: case OP_RXB: case OP_RXH: case OP_RXF: /* reg-mem */ ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch addr */ if ((ir2 & 0xC000) == 0) { /* displacement? */ PC = (PC + 4) & VAMASK; /* increment PC */ ea = ir2; /* abs 14b displ */ } else if (ir2 & 0x8000) { /* relative? */ PC = (PC + 4) & VAMASK; /* increment PC */ ea = PC + SEXT15 (ir2); /* add to incr PC */ } else { /* absolute */ rx2 = (ir2 >> 8) & 0xF; /* get second index */ ea = (ir2 & 0xFF) << 16; /* shift to place */ ir3 = ReadH ((PC + 4) & VAMASK, VE); /* fetch addr lo */ ea = ea | ir3; /* finish addr */ if (rx2) /* index calc 2 */ ea = ea + R[rx2]; PC = (PC + 6) & VAMASK; /* increment PC */ } if (r2) /* index calculation */ ea = ea + R[r2]; ea = ea & VAMASK; if (ityp == OP_RXF) /* get fw operand? */ opnd = ReadF (ea, VR); else if (ityp == OP_RXH) { /* get hw operand? */ t = ReadH (ea, VR); /* read halfword */ opnd = SEXT16 (t); /* sign extend */ } else if (ityp == OP_RXB) /* get byte opnd? */ opnd = ReadB (ea, VR); else opnd = ea; /* just address */ break; default: return SCPE_IERR; } if (hst_lnt) { /* instruction history? */ hst[hst_p].pc = oPC | HIST_PC; /* save decode state */ hst[hst_p].ir1 = ir1; hst[hst_p].ir2 = ir2; hst[hst_p].ir3 = ir3; hst[hst_p].r1 = R[r1]; hst[hst_p].ea = ea; hst[hst_p].opnd = opnd; hst_p = hst_p + 1; if (hst_p >= hst_lnt) hst_p = 0; } if (qevent & EV_MAC) /* MAC abort on fetch? */ continue; switch (op) { /* case on opcode */ /* Load/store instructions */ case 0x08: /* LR - RR */ case 0x24: /* LIS - NO */ case 0x48: /* LH - RXH */ case 0x58: /* L - RXF */ case 0xC8: /* LHI - RI1 */ case 0xF8: /* LI - RI2 */ R[r1] = opnd; /* load operand */ CC_GL_32 (R[r1]); /* set G,L */ break; case 0x73: /* LHL - RXH */ R[r1] = opnd & DMASK16; /* get op, zero ext */ CC_GL_32 (R[r1]); /* set G, L */ break; case 0x25: /* LCS - NO */ R[r1] = NEG (opnd); /* load complement */ CC_GL_32 (R[r1]); /* set G,L */ break; case 0xE6: /* LA - RX */ R[r1] = ea; /* load addr */ break; case 0x63: /* LRA - RX */ cc = RelocT (R[r1] & VAMASK, ea, VR, &R[r1]); /* test reloc */ break; case 0x40: /* STH - RX */ WriteH (ea, R[r1], VW); /* store register */ break; case 0x50: /* ST - RX */ WriteF (ea, R[r1], VW); /* store register */ break; case 0xD1: /* LM - RX */ for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ R[r1] = ReadF (ea, VR); /* load register */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0xD0: /* STM - RX */ for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ WriteF (ea, R[r1], VW); /* store register */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0xE0: /* TS - RXH */ CC_GL_16 (opnd); /* set cc's */ WriteH (ea, opnd | SIGN16, VW); /* set MSB */ break; case 0x93: /* LDBR - RR */ case 0xD3: /* LDB - RXB */ R[r1] = opnd & DMASK8; /* load byte */ break; case 0x92: /* STBR - NO */ R[r2] = (R[r2] & ~DMASK8) | (R[r1] & DMASK8); /* store byte */ break; case 0xD2: /* STB - RX */ WriteB (ea, R[r1], VW); /* store byte */ break; case 0x34: /* EXHR - RR */ R[r1] = ((opnd >> 16) & DMASK16) | ((opnd & DMASK16) << 16); break; case 0x94: /* EXBR - RR */ R[r1] = (R[r1] & ~DMASK16) | ((opnd >> 8) & DMASK8) | ((opnd & DMASK8) << 8); break; /* Control instructions */ case 0x01: /* BALR - RR */ case 0x41: /* BAL - RX */ PCQ_ENTRY; /* save old PC */ R[r1] = PC; /* save cur PC */ PC = opnd & VAMASK; /* branch */ break; case 0x02: /* BTCR - RR */ case 0x42: /* BTC - RX */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = opnd & VAMASK; } break; case 0x20: /* BTBS - NO */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = (oPC - r2 - r2) & VAMASK; } break; case 0x21: /* BTFS - NO */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = (oPC + r2 + r2) & VAMASK; } break; case 0x03: /* BFCR - RR */ case 0x43: /* BFC - RX */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = opnd & VAMASK; } break; case 0x22: /* BFBS - NO */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = (oPC - r2 - r2) & VAMASK; } break; case 0x23: /* BFFS - NO */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = (oPC + r2 + r2) & VAMASK; } break; case 0xC0: /* BXH - RX */ inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ R[r1] = (R[r1] + inc) & DMASK32; /* R1 = R1 + inc */ if (R[r1] > lim) { /* if R1 > lim */ PCQ_ENTRY; /* branch */ PC = opnd & VAMASK; } break; case 0xC1: /* BXLE - RX */ inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ R[r1] = (R[r1] + inc) & DMASK32; /* R1 = R1 + inc */ if (R[r1] <= lim) { /* if R1 <= lim */ PCQ_ENTRY; /* branch */ PC = opnd & VAMASK; } break; /* Logical instructions */ case 0x04: /* NR - RR */ case 0x44: /* NH - RXH */ case 0x54: /* N - RXF */ case 0xC4: /* NHI - RI1 */ case 0xF4: /* NI - RI2 */ R[r1] = R[r1] & opnd; /* result */ CC_GL_32 (R[r1]); /* set G,L */ break; case 0x06: /* OR - RR */ case 0x46: /* OH - RXH */ case 0x56: /* O - RXF */ case 0xC6: /* OHI - RI1 */ case 0xF6: /* OI - RI2 */ R[r1] = R[r1] | opnd; /* result */ CC_GL_32 (R[r1]); /* set G,L */ break; case 0x07: /* XR - RR */ case 0x47: /* XH - RXH */ case 0x57: /* X - RXF */ case 0xC7: /* XHI - RI1 */ case 0xF7: /* XI - RI2 */ R[r1] = R[r1] ^ opnd; /* result */ CC_GL_32 (R[r1]); /* set G,L */ break; case 0xC3: /* THI - RI1 */ case 0xF3: /* TI - RI2 */ rslt = R[r1] & opnd; /* result */ CC_GL_32 (rslt); /* set G, L */ break; case 0x05: /* CLR - RR */ case 0x45: /* CLH - RXH */ case 0x55: /* CL - RXF */ case 0xC5: /* CLHI - RI1 */ case 0xF5: /* CI - RI2 */ rslt = (R[r1] - opnd) & DMASK32; /* result */ CC_GL_32 (rslt); /* set G,L */ if (R[r1] < opnd) /* set C if borrow */ cc = cc | CC_C; if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN32) cc = cc | CC_V; break; case 0xD4: /* CLB - RXB */ t = R[r1] & DMASK8; rslt = (t - opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L 16b */ if (t < opnd) /* set C if borrow */ cc = cc | CC_C; break; case 0x12: /* CHVR - RR */ t = cc & CC_C; /* save C */ R[r1] = (SEXT16 (opnd & DMASK16)) & DMASK32; /* result */ CC_GL_32 (R[r1]); /* set G, L */ if (R[r1] != opnd) /* wont fit? set V */ cc = cc | CC_V; cc = cc | t; /* restore C */ break; /* Shift instructions */ case 0xCC: /* SRHL - RI1 */ opnd = opnd & 0xF; /* shift count */ case 0x90: /* SRHLS - NO */ rslt = (R[r1] & DMASK16) >> opnd; /* result */ CC_GL_16 (rslt); /* set G,L 16b */ if (opnd && (((R[r1] & DMASK16) >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = (R[r1] & ~DMASK16) | rslt; /* store result */ break; case 0xCD: /* SLHL - RI1 */ opnd = opnd & 0xF; /* shift count */ case 0x91: /* SLHLS - NO */ rslt = R[r1] << opnd; /* result */ CC_GL_16 (rslt & DMASK16); /* set G,L 16b */ if (opnd && (rslt & 0x10000)) /* set C if shft out */ cc = cc | CC_C; R[r1] = (R[r1] & ~DMASK16) | (rslt & DMASK16); /* store result */ break; case 0xCE: /* SRHA - RI1 */ opnd = opnd & 0xF; /* shift count */ rslt = (SEXT16 (R[r1]) >> opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L 16b */ if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = (R[r1] & ~DMASK16) | rslt; /* store result */ break; case 0xCF: /* SLHA - RI1 */ opnd = opnd & 0xF; /* shift count */ rslt = R[r1] << opnd; /* raw result */ R[r1] = (R[r1] & ~MMASK16) | (rslt & MMASK16); CC_GL_16 (R[r1] & DMASK16); /* set G,L 16b */ if (opnd && (rslt & SIGN16)) /* set C if shft out */ cc = cc | CC_C; break; case 0xEC: /* SRL - RI1 */ opnd = opnd & 0x1F; /* shift count */ case 0x10: /* SRLS - NO */ rslt = R[r1] >> opnd; /* result */ CC_GL_32 (rslt); /* set G,L */ if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = rslt; /* store result */ break; case 0xED: /* SLL - RI1 */ opnd = opnd & 0x1F; /* shift count */ case 0x11: /* SLLS - NO */ rslt = (R[r1] << opnd) & DMASK32; /* result */ CC_GL_32 (rslt); /* set G,L */ if (opnd && ((R[r1] << (opnd - 1)) & SIGN32)) cc = cc | CC_C; R[r1] = rslt; /* store result */ break; case 0xEE: /* SRA - RI1 */ opnd = opnd & 0x1F; /* shift count */ rslt = (SEXT32 (R[r1]) >> opnd) & DMASK32; /* result */ CC_GL_32 (rslt); /* set G,L */ if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = rslt; /* store result */ break; case 0xEF: /* SLA - RI1 */ opnd = opnd & 0x1F; /* shift count */ rslt = (R[r1] << opnd) & DMASK32; /* raw result */ R[r1] = (R[r1] & SIGN32) | (rslt & MMASK32); /* arith result */ CC_GL_32 (R[r1]); /* set G,L */ if (opnd && (rslt & SIGN32)) /* set C if shft out */ cc = cc | CC_C; break; case 0xEA: /* RRL - RI1 */ opnd = opnd & 0x1F; /* shift count */ if (opnd) /* if cnt > 0 */ R[r1] = (R[r1] >> opnd) | ((R[r1] << (32 - opnd)) & DMASK32); CC_GL_32 (R[r1]); /* set G,L */ break; case 0xEB: /* RLL - RI1 */ opnd = opnd & 0x1F; /* shift count */ if (opnd) R[r1] = ((R[r1] << opnd) & DMASK32) | (R[r1] >> (32 - opnd)); CC_GL_32 (R[r1]); /* set G,L */ break; /* Bit instructions */ case 0x74: /* TBT - RX */ t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ opnd = ReadH (ea, VR); /* read HW */ if (opnd & t) /* test bit */ cc = CC_G; else cc = 0; break; case 0x75: /* SBT - RX */ t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ opnd = ReadH (ea, VR); /* read HW */ WriteH (ea, opnd | t, VW); /* set bit, rewr */ if (opnd & t) /* test bit */ cc = CC_G; else cc = 0; break; case 0x76: /* RBT - RX */ t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ opnd = ReadH (ea, VR); /* read HW */ WriteH (ea, opnd & ~t, VW); /* clr bit, rewr */ if (opnd & t) /* test bit */ cc = CC_G; else cc = 0; break; case 0x77: /* CBT - RX */ t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ opnd = ReadH (ea, VR); /* read HW */ WriteH (ea, opnd ^ t, VW); /* com bit, rewr */ if (opnd & t) /* test bit */ cc = CC_G; else cc = 0; break; /* Arithmetic instructions */ case 0x0A: /* AR - RR */ case 0x26: /* AIS - NO */ case 0x4A: /* AH - RXH */ case 0x5A: /* A - RXF */ case 0xCA: /* AHI - RI1 */ case 0xFA: /* AI - RI2 */ rslt = (R[r1] + opnd) & DMASK32; /* result */ CC_GL_32 (rslt); /* set G,L */ if (rslt < opnd) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN32) cc = cc | CC_V; R[r1] = rslt; break; case 0x51: /* AM - RXF */ rslt = (R[r1] + opnd) & DMASK32; /* result */ WriteF (ea, rslt, VW); /* write result */ CC_GL_32 (rslt); /* set G,L */ if (rslt < opnd) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN32) cc = cc | CC_V; break; case 0x61: /* AHM - RXH */ rslt = (R[r1] + opnd) & DMASK16; /* result */ WriteH (ea, rslt, VW); /* write result */ CC_GL_16 (rslt); /* set G,L 16b */ if (rslt < (opnd & DMASK16)) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; break; case 0x0B: /* SR - RR */ case 0x27: /* SIS - NO */ case 0x4B: /* SH - RXH */ case 0x5B: /* S - RXF */ case 0xCB: /* SHI - RI1 */ case 0xFB: /* SI - RI2 */ rslt = (R[r1] - opnd) & DMASK32; /* result */ CC_GL_32 (rslt); /* set G,L */ if (R[r1] < opnd) /* set C if borrow */ cc = cc | CC_C; if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN32) cc = cc | CC_V; R[r1] = rslt; break; case 0x09: /* CR - RR */ case 0x49: /* CH - RXH */ case 0x59: /* C - RXF */ case 0xC9: /* CHI - RI1 */ case 0xF9: /* CI - RI2 */ if (R[r1] == opnd) cc = 0; /* =? */ else if ((R[r1] ^ opnd) & SIGN32) /* unlike signs? */ cc = (R[r1] & SIGN32)? (CC_C | CC_L): CC_G; else cc = (R[r1] > opnd)? CC_G: (CC_C | CC_L); /* like signs */ if (((R[r1] ^ opnd) & (~opnd ^ (R[r1] - opnd))) & SIGN32) cc = cc | CC_V; break; case 0x0C: /* MHR - RR */ case 0x4C: /* MH - RXH */ R[r1] = (SEXT16 (R[r1]) * SEXT16 (opnd)) & DMASK32; /* multiply */ break; case 0x1C: /* MR - RR */ case 0x5C: /* M - RXF */ r1p1 = (r1 + 1) & 0xF; mpc = ABS (opnd); /* |mpcnd| */ mpy = ABS (R[r1p1]); /* |mplyr| */ rslt = rlo = 0; /* clr result */ for (i = 0; i < 32; i++) { /* develop 32b */ t = 0; /* no cout */ if (mpy & 1) { /* cond add */ rslt = (rslt + mpc) & DMASK32; if (rslt < mpc) t = SIGN32; } rlo = (rlo >> 1) | ((rslt & 1) << 31); /* shift result */ rslt = (rslt >> 1) | t; mpy = mpy >> 1; /* shift mpylr */ } if ((opnd ^ R[r1p1]) & SIGN32) { DNEG (rslt, rlo); } R[r1] = rslt; /* store result */ R[r1p1] = rlo; break; case 0x0D: /* DHR - RR */ case 0x4D: /* DH - RXH */ opnd = opnd & DMASK16; /* force HW opnd */ if ((opnd == 0) || /* div by zero? */ ((R[r1] == 0x80000000) && (opnd == 0xFFFF))) { if (PSW & PSW_AFI) /* div fault enabled? */ cc = exception (AFIPSW, cc, 0); /* exception */ break; } r1p1 = (r1 + 1) & 0xF; st = SEXT32 (R[r1]) / SEXT16 (opnd); /* quotient */ sr = SEXT32 (R[r1]) % SEXT16 (opnd); /* remainder */ if ((st < 0x8000) && (st >= -0x8000)) { /* if quo fits */ R[r1] = sr & DMASK32; /* store remainder */ R[r1p1] = st & DMASK32; /* store quotient */ } else if (PSW & PSW_AFI) /* div fault enabled? */ cc = exception (AFIPSW, cc, 0); /* exception */ break; case 0x1D: /* DR - RR */ case 0x5D: /* D - RXF */ r1p1 = (r1 + 1) & 0xF; rslt = R[r1]; /* get dividend */ rlo = R[r1p1]; if (R[r1] & SIGN32) { DNEG (rslt, rlo); } /* |divd| */ dvr = ABS (opnd); /* |divr| */ if (rslt < dvr) { /* will div work? */ uint32 quos = R[r1] ^ opnd; /* expected sign */ for (i = t = 0; i < 32; i++) { /* 32 iterations */ rslt = ((rslt << 1) & DMASK32) | /* shift divd */ ((rlo >> 31) & 1); rlo = (rlo << 1) & DMASK32; t = (t << 1) & DMASK32; /* shift quo */ if (rslt >= dvr) { /* subtract work? */ rslt = rslt - dvr; /* divd -= divr */ t = t | 1; /* set quo bit */ } } if (quos & SIGN32) /* res -? neg quo */ t = NEG (t); if (R[r1] & SIGN32) /* adj rem sign */ rslt = NEG (rslt); if (t && ((t ^ quos) & SIGN32)) { /* res sign wrong? */ if (PSW & PSW_AFI) /* if enabled, */ cc = exception (AFIPSW, cc, 0); /* exception */ break; } R[r1] = rslt; /* store rem */ R[r1p1] = t; /* store quo */ } else if (PSW & PSW_AFI) /* div fault enabled? */ cc = exception (AFIPSW, cc, 0); /* exception */ break; /* Floating point instructions */ case 0x28: /* LER - NO */ case 0x38: /* LDR - NO */ case 0x68: /* LE - RX */ case 0x78: /* LD - RX */ cc = f_l (op, r1, r2, ea); /* load */ if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ cc = exception (AFIPSW, cc, 1); break; case 0x29: /* CER - NO */ case 0x39: /* CDR - NO */ case 0x69: /* CE - RX */ case 0x79: /* CD - RX */ cc = f_c (op, r1, r2, ea); /* compare */ break; case 0x2A: /* AER - NO */ case 0x2B: /* SER - NO */ case 0x3A: /* ADR - NO */ case 0x3B: /* SDR - NO */ case 0x6A: /* AE - RX */ case 0x6B: /* SE - RX */ case 0x7A: /* AD - RX */ case 0x7B: /* SD - RX */ cc = f_as (op, r1, r2, ea); /* add/sub */ if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ cc = exception (AFIPSW, cc, 1); break; case 0x2C: /* MER - NO */ case 0x3C: /* MDR - NO */ case 0x6C: /* ME - RX */ case 0x7C: /* MD - RX */ cc = f_m (op, r1, r2, ea); /* multiply */ if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ cc = exception (AFIPSW, cc, 1); break; case 0x2D: /* DER - NO */ case 0x3D: /* DDR - NO */ case 0x6D: /* DE - RX */ case 0x7D: /* DD - RX */ cc = f_d (op, r1, r2, ea); /* perform divide */ if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ cc = exception (AFIPSW, cc, 1); break; case 0x2E: /* FXR - NO */ case 0x3E: /* FXDR - NO */ cc = f_fix32 (op, r1, r2); /* cvt to integer */ break; case 0x2F: /* FLR - NO */ case 0x3F: /* FLDR - NO */ cc = f_flt32 (op, r1, r2); /* cvt to floating */ break; case 0x60: /* STE - RX */ t = ReadFReg (r1); /* get sp reg */ WriteF (ea, t, VW); /* write */ break; case 0x70: /* STD - RX */ WriteF (ea, D[r1 >> 1].h, VW); /* write hi */ WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, VW); /* write lo */ break; case 0x71: /* STME - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ t = ReadFReg (r1); /* get sp reg */ WriteF (ea, t, VW); /* write */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0x72: /* LME - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ t = ReadF (ea, VR); /* get value */ WriteFReg (r1, t); /* write reg */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0x7E: /* STMD - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ WriteF (ea, D[r1 >> 1].h, VW); /* write register */ WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, VW); ea = (ea + 8) & VAMASK; /* incr mem addr */ } break; case 0x7F: /* LMD - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ D[r1 >> 1].h = ReadF (ea, VR); /* load register */ D[r1 >> 1].l = ReadF ((ea + 4) & VAMASK, VR); ea = (ea + 8) & VAMASK; /* incr mem addr */ } break; /* Miscellaneous */ case 0xE1: /* SVC - RX */ PCQ_ENTRY; /* effective branch */ t = BUILD_PSW (cc); /* save PSW */ cc = newPSW (ReadF (SVNPS32, P)); /* get new PSW */ R[13] = ea & 0xFFFFFF; /* parameter */ R[14] = t; /* old PSW */ R[15] = PC; /* old PC */ PC = ReadH (SVNPC + r1 + r1, P); /* new PC */ if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, ">>SVC: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", pcq[pcq_p], t, PC, PSW); break; case 0xE2: /* SINT - RI1 */ dev = opnd & DEV_MAX; /* get dev */ cc = int_auto (dev, cc); /* auto int */ int_eval (); break; case 0xE3: /* SCP - RXH */ opnd = opnd & DMASK16; /* zero ext operand */ if (opnd & CCW32_B1) /* point to buf */ t = ea + CCB32_B1C; else t = ea + CCB32_B0C; sr = ReadH (t & VAMASK, VR); /* get count */ sr = SEXT16 (sr); /* sign extend */ if (sr <= 0) { /* <= 0? */ bufa = ReadF ((t + 2) & VAMASK, VR); /* get buf end */ if (opnd & CCW32_WR) /* write? */ R[r1] = ReadB ((bufa + sr) & VAMASK, VR); /* R1 gets mem */ else WriteB ((bufa + sr) & VAMASK, R[r1], VW); /* read, R1 to mem */ sr = sr + 1; /* inc count */ CC_GL_32 (sr & DMASK32); /* set cc's */ WriteH (t & VAMASK, sr, VW); /* rewrite */ if ((sr > 0) && !(opnd & CCW32_FST)) /* buf switch? */ WriteH (ea, opnd ^ CCW32_B1, VW); /* flip CCW bit */ } /* end if */ else cc = CC_V; break; case 0x18: /* LPSWR - RR */ PCQ_ENTRY; /* effective branch */ PC = R[(r2 + 1) & 0xF] & VAMASK; /* new PC (old reg set) */ if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, ">>LPSWR: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", pcq[pcq_p], BUILD_PSW (cc), PC, opnd); cc = newPSW (opnd); /* new PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0xC2: /* LPSW - RXF */ PCQ_ENTRY; /* effective branch */ PC = ReadF ((ea + 4) & VAMASK, VR) & VAMASK; /* new PC */ if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, ">>LPSW: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", pcq[pcq_p], BUILD_PSW (cc), PC, opnd); cc = newPSW (opnd); /* new PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0x95: /* EPSR - NO */ R[r1] = BUILD_PSW (cc); /* save PSW */ cc = newPSW (R[r2]); /* load new PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0x64: /* ATL - RX */ case 0x65: /* ABL - RX */ cc = addtoq (ea, R[r1], op & 1); /* add to q */ break; case 0x66: /* RTL - RX */ case 0x67: /* RBL - RX */ cc = remfmq (ea, r1, op & 1); /* rem from q */ break; case 0x5E: /* CRC12 - RXH */ opnd = opnd & DMASK16; /* zero ext opnd */ t = (R[r1] & 0x3F) ^ opnd; for (i = 0; i < 6; i++) { if (t & 1) t = (t >> 1) ^ 0x0F01; else t = t >> 1; } WriteH (ea, t, VW); break; case 0x5F: /* CRC16 - RXH */ opnd = opnd & DMASK16; /* zero ext opnd */ t = (R[r1] & 0xFF) ^ opnd; for (i = 0; i < 8; i++) { if (t & 1) t = (t >> 1) ^ 0xA001; else t = t >> 1; } WriteH (ea, t, VW); break; case 0xE7: /* TLATE - RXF */ t = (opnd + ((R[r1] & DMASK8) << 1)) & VAMASK; /* table entry */ rslt = ReadH (t, VR); /* get entry */ if (rslt & SIGN16) /* direct xlate? */ R[r1] = rslt & DMASK8; else { PCQ_ENTRY; /* branch */ PC = rslt << 1; } break; /* I/O instructions */ case 0xDE: /* OC - RX */ opnd = ReadB (ea, VR); /* fetch operand */ case 0x9E: /* OCR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { dev_tab[dev] (dev, IO_ADR, 0); /* select */ dev_tab[dev] (dev, IO_OC, opnd & DMASK8); /* send command */ cc = 0; } else cc = CC_V; int_eval (); /* re-eval intr */ break; case 0xDA: /* WD - RX */ opnd = ReadB (ea, VR); /* fetch operand */ case 0x9A: /* WDR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { dev_tab[dev] (dev, IO_ADR, 0); /* select */ dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send data */ cc = 0; } else cc = CC_V; int_eval (); /* re-eval intr */ break; case 0xD8: /* WH - RX */ opnd = ReadH (ea, VR); /* fetch operand */ case 0x98: /* WHR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { if (dev_tab[dev] (dev, IO_ADR, 0)) /* select; hw ok? */ dev_tab[dev] (dev, IO_WH, opnd & DMASK16); /* send data */ else { /* byte only */ dev_tab[dev] (dev, IO_WD, (opnd >> 8) & DMASK8); /* hi */ dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send lo byte */ } cc = 0; } else cc = CC_V; int_eval (); /* re-eval intr */ break; case 0x9B: /* RDR - RR */ case 0xDB: /* RD - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ dev_tab[dev] (dev, IO_ADR, 0); /* select */ t = dev_tab[dev] (dev, IO_RD, 0); /* get data */ cc = 0; } else { /* no */ t = 0; cc = CC_V; } if (OP_TYPE (op) != OP_RR) /* RX or RR? */ WriteB (ea, t, VW); else R[r2] = t & DMASK8; int_eval (); /* re-eval intr */ break; case 0x99: /* RHR - RR */ case 0xD9: /* RH - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (dev_tab[dev] (dev, IO_ADR, 0)) /* select, hw ok? */ t = dev_tab[dev] (dev, IO_RH, 0); /* get data */ else { /* byte only */ rslt = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ t = (rslt << 8) | t; /* merge */ } cc = 0; } else { /* no */ t = 0; cc = CC_V; } if (OP_TYPE (op) != OP_RR) /* RX or RR? */ WriteH (ea, t, VW); else R[r2] = t & DMASK16; int_eval (); /* re-eval intr */ break; case 0x9D: /* SSR - RR */ case 0xDD: /* SS - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ dev_tab[dev] (dev, IO_ADR, 0); /* select */ t = dev_tab[dev] (dev, IO_SS, 0); /* get status */ } else t = STA_EX; /* no */ if (OP_TYPE (op) != OP_RR) /* RX or RR? */ WriteB (ea, t, VW); else R[r2] = t & DMASK8; cc = t & 0xF; int_eval (); /* re-eval intr */ break; /* Block I/O instructions On a real Interdata system, the block I/O instructions can't be interrupted or stopped. To model this behavior, while allowing the instructions to go back through fetch for I/O processing and WRU testing, the simulator implements a 'block I/O in progress' flag and status block. If a block I/O is in progress, normal interrupts and fetches are suppressed until the block I/O is done. */ case 0x96: /* WBR - RR */ case 0xD6: /* WB - RXF */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (OP_TYPE (op) != OP_RR) lim = ReadF ((ea + 4) & VAMASK, VR); else lim = R[(r2 + 1) & 0xF]; if (opnd > lim) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ blk_io.dfl = dev; /* set status block */ blk_io.cur = opnd; blk_io.end = lim; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; case 0x97: /* RBR - RR */ case 0xD7: /* RB - RXF */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (OP_TYPE (op) != OP_RR) lim = ReadF ((ea + 4) & VAMASK, VR); else lim = R[(r2 + 1) & 0xF]; if (opnd > lim) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ blk_io.dfl = dev | BL_RD; /* set status block */ blk_io.cur = opnd; blk_io.end = lim; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; case 0xD5: /* AL - RX */ dev = ReadB (AL_DEV, P); /* get device */ t = ReadB (AL_IOC, P); /* get command */ if (DEV_ACC (dev)) { /* dev exist? */ if (AL_BUF > ea) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ dev_tab[dev] (dev, IO_OC, t); /* start dev */ blk_io.dfl = dev | BL_RD | BL_LZ; /* set status block */ blk_io.cur = AL_BUF; blk_io.end = ea; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; } /* end switch */ } /* end while */ /* Simulation halted */ PSW = BUILD_PSW (cc); PC = PC & VAMASK; set_r_display (R); pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Load new PSW */ uint32 newPSW (uint32 val) { uint32 rs = PSW_GETREG (val); /* register set */ R = &GREG[rs * 16]; /* set register set */ PSW = val & PSW_MASK; /* store PSW */ int_eval (); /* update intreq */ if (PSW & PSW_WAIT) /* wait state? */ qevent = qevent | EV_WAIT; else qevent = qevent & ~EV_WAIT; if (PSW & PSW_EXI) /* enable/disable */ SET_ENB (v_DS); else CLR_ENB (v_DS); /* console intr */ return PSW & CC_MASK; } /* Exception handler - 7/32 always uses register set 0 */ uint32 exception (uint32 loc, uint32 cc, uint32 flg) { int32 oldPSW = BUILD_PSW (cc); /* save old PSW */ int32 oldPC = PC; /* save old PC */ cc = newPSW (ReadF (loc, P)); /* new PSW */ PC = ReadF (loc + 4, P) & VAMASK; /* new PC */ if (cpu_unit.flags & UNIT_832) { /* 8/32? */ R[14] = oldPSW; /* PSW to new 14 */ R[15] = oldPC; /* PC to new 15 */ } else { GREG[14] = oldPSW; /* 7/32, PSW to set 0 14 */ GREG[15] = oldPC; /* PC to set 0 15 */ } if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, ">>Exc %X: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", loc, oldPC, oldPSW, PC, PSW | cc | flg); return cc | flg; /* return CC */ } /* Test for queue interrupts - system queue addresses are physical */ uint32 testsysq (uint32 cc) { int32 qb = ReadF (SQP, P); /* get sys q addr */ int32 usd = ReadH (qb + Q32_USD, P); /* get use count */ if (usd) { /* entries? */ cc = exception (SQTPSW, cc, 0); /* take sysq exc */ if (cpu_unit.flags & UNIT_832) /* R13 = sys q addr */ R[13] = qb; else GREG[13] = qb; } return cc; } /* Add to queue */ uint32 addtoq (uint32 ea, uint32 val, uint32 flg) { uint32 slt, usd, wra, t; t = ReadF (ea, VR); /* slots/used */ slt = (t >> 16) & DMASK16; /* # slots */ usd = t & DMASK16; /* # used */ if (usd >= slt) /* list full? */ return CC_V; usd = (usd + 1) & DMASK16; /* inc # used */ WriteH (ea + Q32_USD, usd, VW); /* rewrite */ if (flg) { /* ABL? */ wra = ReadH ((ea + Q32_BOT) & VAMASK, VR); /* get bottom */ t = wra + 1; /* adv bottom */ if (t >= slt) /* wrap if necc */ t = 0; WriteH ((ea + Q32_BOT) & VAMASK, t, VW); /* rewrite bottom */ } else { wra = ReadH ((ea + Q32_TOP) & VAMASK, VR); /* ATL, get top */ if (wra == 0) wra = (slt - 1) & DMASK16; /* wrap if necc */ else wra = wra - 1; /* dec top */ WriteH ((ea + Q32_TOP) & VAMASK, wra, VW); /* rewrite top */ } WriteF ((ea + Q32_BASE + (wra * Q32_SLNT)) & VAMASK, val, VW); /* write slot */ return 0; } /* Remove from queue */ uint32 remfmq (uint32 ea, uint32 r1, uint32 flg) { uint32 slt, usd, rda, t; t = ReadF (ea, VR); /* get slots/used */ slt = (t >> 16) & DMASK16; /* # slots */ usd = t & DMASK16; /* # used */ if (usd == 0) /* empty? */ return CC_V; usd = usd - 1; /* dec used */ WriteH (ea + Q32_USD, usd, VW); /* rewrite */ if (flg) { /* RBL? */ rda = ReadH ((ea + Q32_BOT) & VAMASK, VR); /* get bottom */ if (rda == 0) /* wrap if necc */ rda = (slt - 1) & DMASK16; else rda = rda - 1; /* dec bottom */ WriteH ((ea + Q32_BOT) & VAMASK, rda, VW); /* rewrite bottom */ } else { rda = ReadH ((ea + Q32_TOP) & VAMASK, VR); /* RTL, get top */ t = rda + 1; /* adv top */ if (t >= slt) /* wrap if necc */ t = 0; WriteH ((ea + Q32_TOP) & VAMASK, t, VW); /* rewrite top */ } R[r1] = ReadF ((ea + Q32_BASE + (rda * Q32_SLNT)) & VAMASK, VR); /* read slot */ if (usd) return CC_G; else return 0; } /* Automatic interrupt processing */ uint32 int_auto (uint32 dev, uint32 cc) { uint32 addr, vec, by, ccw, ccwa, ccwb; uint32 i, hw, tblad, tblen, bufe, st, t; int32 bufc; uint32 oldPSW = BUILD_PSW (cc); vec = ReadH (INTSVT + dev + dev, P); /* get vector */ newPSW (0x2800); /* new PSW */ R[0] = oldPSW; /* save old PSW */ R[1] = PC; /* save PC */ R[2] = dev; /* set dev # */ if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, ">>Int %X: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", dev, PC, oldPSW, vec, 0x2800); if (DEV_ACC (dev)) { /* dev exist? */ hw = dev_tab[dev] (dev, IO_ADR, 0); /* select, get hw */ R[3] = st = dev_tab[dev] (dev, IO_SS, 0); /* sense status */ } else { hw = 0; R[3] = CC_V; } if ((vec & 1) == 0) { /* immed int? */ PC = vec; /* new PC */ return PSW & CC_MASK; /* exit */ } R[4] = ccwa = vec & ~1; /* save CCW addr */ ccw = ReadH (ccwa, VR); /* read CCW */ if ((ccw & CCW32_EXE) == 0) { /* exec clr? */ PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ return 0; /* CC = 0 */ } if (!DEV_ACC (dev) || (st & CCW32_STA (ccw))) { /* bad status? */ PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ return CC_L; /* CC = L */ } if (ccw & CCW32_FST) { /* fast mode? */ t = ReadH (ccwa + CCB32_B0C, VR); /* get count */ bufc = SEXT16 (t); /* sign ext */ if (bufc <= 0) { /* still valid? */ bufe = ReadF (ccwa + CCB32_B0E, VR); /* get end addr */ addr = (bufe + bufc) & VAMASK; if (hw) { /* halfword? */ if (ccw & CCW32_WR) { /* write? */ t = ReadH (addr, VR); /* get hw */ dev_tab[dev] (dev, IO_WH, t); /* send to dev */ } else { /* read */ t = dev_tab[dev] (dev, IO_RH, 0); /* get hw */ WriteH (addr, t, VW); /* write to mem */ } bufc = bufc + 2; /* adv buf cnt */ } else { /* byte */ if (ccw & CCW32_WR) { /* write? */ t = ReadB (addr, VR); /* get byte */ dev_tab[dev] (dev, IO_WD, t); /* send to dev */ } else { /* read */ t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ WriteB (addr, t, VW); /* write to mem */ } bufc = bufc + 1; /* adv buf cnt */ } WriteH (ccwa + CCB32_B0C, bufc, VW); /* rewrite cnt */ if (bufc > 0) { PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ return CC_G; /* CC = G */ } } /* end if bufc <= 0 */ } /* end fast */ else { /* slow mode */ if (ccw & CCW32_B1) /* which buf? */ ccwb = ccwa + CCB32_B1C; else ccwb = ccwa + CCB32_B0C; t = ReadH (ccwb, VR); /* get count */ bufc = SEXT16 (t); /* sign ext */ if (bufc <= 0) { /* still valid? */ bufe = ReadF (ccwb + 2, VR); /* get end addr */ addr = (bufe + bufc) & VAMASK; if (ccw & CCW32_WR) { /* write? */ by = ReadB (addr, VR); /* byte fm mem */ if (ccw & CCW32_TL) { /* translate? */ tblad = ReadF (ccwa + CCB32_TAB, VR); /* get tbl addr */ tblen = (tblad + (by << 1)) & VAMASK; /* tbl entry addr */ t = ReadH (tblen, VR); /* get tbl entry */ if ((t & SIGN16) == 0) { /* special xlate? */ PC = t << 1; /* change PC */ R[3] = by; /* untrans char */ return 0; /* CC = 0 */ } by = t & DMASK8; /* replace */ } dev_tab[dev] (dev, IO_WD, by); /* write to dev */ } else { /* read */ by = dev_tab[dev] (dev, IO_RD, 0); /* get from dev */ if (ccw & CCW32_TL) { /* translate? */ tblad = ReadF (ccwa + CCB32_TAB, VR); /* get tbl addr */ tblen = (tblad + (by << 1)) & VAMASK; /* tbl entry addr */ t = ReadH (tblen, VR); /* get tbl entry */ if ((t & SIGN16) == 0) { /* special xlate? */ PC = t << 1; /* change PC */ R[3] = by; /* untrans char */ return 0; /* CC = 0 */ } WriteB (addr, t, VW); /* wr trans */ } else WriteB (addr, by, VW); /* wr orig */ } t = ReadH (ccwa + CCB32_CHK, VR); /* get check wd */ t = t ^ by; /* start LRC */ if (ccw & CCW32_CRC) { /* CRC? */ for (i = 0; i < 8; i++) { if (t & 1) t = (t >> 1) ^ 0xA001; else t = t >> 1; } } WriteH (ccwa + CCB32_CHK, t, VW); /* rewrite chk wd */ bufc = bufc + 1; /* adv buf cnt */ WriteH (ccwb, bufc, VW); /* rewrite cnt */ if (bufc > 0) { /* cnt pos? */ ccw = ccw ^ CCW32_B1; /* flip buf */ WriteH (ccwa, ccw, VW); /* rewrite */ PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ return CC_G; /* CC = G */ } } /* end if bufc */ } /* end slow */ PC = R[1]; /* restore PC */ return newPSW (R[0]); /* restore PSW, CC */ } /* Display register device */ uint32 display (uint32 dev, uint32 op, uint32 dat) { int t; switch (op) { case IO_ADR: /* select */ if (!drmod) /* norm mode? clr */ drpos = srpos = 0; return BY; /* byte only */ case IO_OC: /* command */ op = op & 0xC0; if (op == 0x40) { /* x40 = inc */ drmod = 1; drpos = srpos = 0; /* init cntrs */ } else if (op == 0x80) /* x80 = norm */ drmod = 0; break; case IO_WD: /* write */ if (drpos < 4) DR = (DR & ~(DMASK8 << (drpos * 8))) | (dat << (drpos * 8)); else if (drpos == 4) DRX = dat; drpos = (drpos + 1) & 0x7; break; case IO_RD: /* read */ t = (SR >> (srpos * 8)) & DMASK8; srpos = srpos ^ 1; return t; case IO_SS: /* status */ return 0x80; } return 0; } /* Relocation and protection */ uint32 Reloc (uint32 va, uint32 rel) { uint32 seg, off, mapr, lim; seg = VA_GETSEG (va); /* get seg num */ off = VA_GETOFF (va); /* get offset */ mapr = mac_reg[seg]; /* get seg reg */ lim = GET_SRL (mapr); /* get limit */ if (off >= lim) { /* limit viol? */ mac_sta = MACS_L; /* set status */ ABORT (MPRO); /* abort */ } if ((mapr & SR_PRS) == 0) { /* not present? */ mac_sta = MACS_NP; /* set status */ ABORT (MPRO); /* abort */ } if ((rel == VE) && (mapr & SR_EXP)) { /* exec, prot? */ mac_sta = MACS_EX; /* set status */ qevent = qevent | EV_MAC; /* req intr */ } if ((rel == VW) && (mapr & (SR_WPI | SR_WRP))) { /* write, prot? */ if (mapr & SR_WRP) { /* write abort? */ mac_sta = MACS_WP; /* set status */ ABORT (MPRO); /* abort */ } else { /* write intr */ mac_sta = MACS_WI; /* set status */ qevent = qevent | EV_MAC; /* req intr */ } } return (off + (mapr & SRF_MASK)) & PAMASK32; /* relocate */ } uint32 RelocT (uint32 va, uint32 base, uint32 rel, uint32 *pa) { uint32 seg, off, mapr, lim; seg = VA_GETSEG (va); /* get seg num */ off = VA_GETOFF (va); /* get offset */ mapr = ReadF ((base + (seg << 2)) & VAMASK, rel); /* get seg reg */ lim = GET_SRL (mapr); /* get limit */ if (off >= lim) /* limit viol? */ return CC_C; if ((mapr & SR_PRS) == 0) /* not present? */ return CC_V; *pa = off + (mapr & SRF_MASK); /* translate */ if (mapr & (SR_WRP | SR_WPI)) /* write prot? */ return CC_G; if (mapr & SR_EXP) /* exec prot? */ return CC_L; return 0; /* ok */ } /* Memory interface routines ReadB read byte (processor) ReadH read halfword (processor) ReadF read fullword (processor) WriteB write byte (processor) WriteH write halfword (processor) WriteF write fullword (processor) IOReadB read byte (IO) IOWriteB write byte (IO) IOReadH read halfword (IO) IOWriteH write halfword (IO) */ uint32 ReadB (uint32 loc, uint32 rel) { uint32 val; uint32 sc = (3 - (loc & 3)) << 3; if ((PSW & PSW_REL) == 0) { /* reloc off? */ if ((loc & ~03) == MAC_STA) { /* MAC status? */ val = mac_sta; /* read it */ qevent = qevent & ~EV_MAC; /* clr MAC intr */ } else val = M[loc >> 2]; /* get mem word */ } else if (rel == 0) /* phys ref? */ val = M[loc >> 2]; else { uint32 pa = Reloc (loc, rel); /* relocate */ val = M[pa >> 2]; } return (val >> sc) & DMASK8; } uint32 ReadH (uint32 loc, uint32 rel) { uint32 val; if ((PSW & PSW_REL) == 0) { /* reloc off? */ if ((loc & ~03) == MAC_STA) { /* MAC status? */ val = mac_sta; /* read it */ qevent = qevent & ~EV_MAC; /* clr MAC intr */ } else val = M[loc >> 2]; /* get mem word */ } else if (rel == 0) /* phys ref? */ val = M[loc >> 2]; else { uint32 pa = Reloc (loc, rel); /* relocate */ val = M[pa >> 2]; } return (val >> ((loc & 2)? 0: 16)) & DMASK16; } uint32 ReadF (uint32 loc, uint32 rel) { uint32 val; if ((PSW & PSW_REL) == 0) { /* reloc off? */ if ((loc & ~03) == MAC_STA) { /* MAC status? */ val = mac_sta; /* read it */ qevent = qevent & ~EV_MAC; /* clr MAC intr */ } else val = M[loc >> 2]; /* get mem word */ } else if (rel == 0) /* phys ref? */ val = M[loc >> 2]; else { uint32 pa = Reloc (loc, rel); /* relocate */ val = M[pa >> 2]; } return val; } void WriteB (uint32 loc, uint32 val, uint32 rel) { uint32 pa = loc; uint32 sc = (3 - (loc & 3)) << 3; val = val & DMASK8; if ((PSW & PSW_REL) == 0) { /* reloc off? */ uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ if (idx <= MAC_LNT) { if (idx < MAC_LNT) mac_reg[idx] = ((mac_reg[idx] & ~(DMASK8 << sc)) | (val << sc)) & SR_MASK; else { mac_sta = 0; qevent = qevent & ~EV_MAC; } } } else if (rel != 0) /* !phys? relocate */ pa = Reloc (loc, rel); if (MEM_ADDR_OK (pa)) M[pa >> 2] = (M[pa >> 2] & ~(DMASK8 << sc)) | (val << sc); return; } void WriteH (uint32 loc, uint32 val, uint32 rel) { uint32 pa = loc; val = val & DMASK16; if ((PSW & PSW_REL) == 0) { /* reloc off? */ uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ if (idx <= MAC_LNT) { if (idx < MAC_LNT) mac_reg[idx] = ((loc & 2)? ((mac_reg[idx] & ~DMASK16) | val): ((mac_reg[idx] & DMASK16) | (val << 16))) & SR_MASK; else { mac_sta = 0; qevent = qevent & ~EV_MAC; } } } else if (rel != 0) /* !phys? relocate */ pa = Reloc (loc, rel); if (MEM_ADDR_OK (pa)) M[pa >> 2] = (loc & 2)? ((M[pa >> 2] & ~DMASK16) | val): ((M[pa >> 2] & DMASK16) | (val << 16)); return; } void WriteF (uint32 loc, uint32 val, uint32 rel) { uint32 pa = loc; val = val & DMASK32; if (loc & 2) { WriteH (loc & VAMASK, (val >> 16) & DMASK16, rel); WriteH ((loc + 2) & VAMASK, val & DMASK16, rel); return; } if ((PSW & PSW_REL) == 0) { /* reloc off? */ uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ if (idx <= MAC_LNT) { if (idx < MAC_LNT) mac_reg[idx] = val & SR_MASK; else { mac_sta = 0; qevent = qevent & ~EV_MAC; } } } else if (rel != 0) /* !phys? relocate */ pa = Reloc (loc, rel); if (MEM_ADDR_OK (pa)) M[pa >> 2] = val & DMASK32; return; } uint32 IOReadB (uint32 loc) { uint32 sc = (3 - (loc & 3)) << 3; return (M[loc >> 2] >> sc) & DMASK8; } uint32 IOReadH (uint32 loc) { return (M[loc >> 2] >> ((loc & 2)? 0: 16)) & DMASK16; } void IOWriteB (uint32 loc, uint32 val) { uint32 sc = (3 - (loc & 3)) << 3; val = val & DMASK8; M[loc >> 2] = (M[loc >> 2] & ~(DMASK8 << sc)) | (val << sc); return; } void IOWriteH (uint32 loc, uint32 val) { uint32 sc = (loc & 2)? 0: 16; val = val & DMASK16; M[loc >> 2] = (M[loc >> 2] & ~(DMASK16 << sc)) | (val << sc); return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { qevent = 0; /* no events */ mac_sta = 0; /* clear MAC */ newPSW (0); /* PSW = 0 */ set_r_display (R); DR = 0; /* clear display */ drmod = 0; blk_io.dfl = blk_io.cur = blk_io.end = 0; /* no block I/O */ sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init bkpts */ if (M == NULL) M = (uint32 *) calloc (MAXMEMSIZE32 >> 2, sizeof (uint32)); if (M == NULL) return SCPE_MEM; pcq_r = find_reg ("PCQ", NULL, dptr); /* init PCQ */ if (pcq_r) pcq_r->qptr = 0; else return SCPE_IERR; return SCPE_OK; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { if ((sw & SWMASK ('V')) && (PSW & PSW_REL)) { int32 cc = RelocT (addr, MAC_BASE, P, &addr); if (cc & (CC_C | CC_V)) return SCPE_NXM; } if (addr >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = IOReadH (addr); return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if ((sw & SWMASK ('V')) && (PSW & PSW_REL)) { int32 cc = RelocT (addr, MAC_BASE, P, &addr); if (cc & (CC_C | CC_V)) return SCPE_NXM; } if (addr >= MEMSIZE) return SCPE_NXM; IOWriteH (addr, val); return SCPE_OK; } /* Change memory size */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 mc = 0; uint32 i; if ((val <= 0) || (val > MAXMEMSIZE32) || ((val & 0xFFFF) != 0)) return SCPE_ARG; for (i = val; i < MEMSIZE; i = i + 4) mc = mc | M[i >> 2]; if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) return SCPE_OK; MEMSIZE = val; for (i = MEMSIZE; i < MAXMEMSIZE32; i = i + 4) M[i >> 2] = 0; return SCPE_OK; } /* Set current R pointers for SCP */ void set_r_display (uint32 *rbase) { extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); REG *rptr; int32 i; rptr = find_reg ("R0", NULL, &cpu_dev); if (rptr == NULL) return; for (i = 0; i < 16; i++, rptr++) rptr->loc = (void *) (rbase + i); return; } /* Set console interrupt */ t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc) { if (PSW & PSW_EXI) SET_INT (v_DS); return SCPE_OK; } /* Set history */ t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 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 = (uint32) 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 op, k, di, lnt; char *cptr = (char *) desc; t_value sim_eval[3]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 r1 operand 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? */ fprintf (st, "%06X %08X %08X ", h->pc & VAMASK32, h->r1, h->opnd); op = (h->ir1 >> 8) & 0xFF; if (OP_TYPE (op) >= OP_RX) fprintf (st, "%06X ", h->ea); else fprintf (st, " "); sim_eval[0] = h->ir1; sim_eval[1] = h->ir2; sim_eval[2] = h->ir3; if ((fprint_sym (st, h->pc & VAMASK32, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %04X", h->ir1); fputc ('\n', st); /* end line */ } /* end if instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/Interdata/id_uvc.c0000644000175000017500000003367611112026546014206 0ustar vlmvlm/* id_uvc.c: Interdata universal clock Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. pic precision incremental clock lfc line frequency clock 18-Jun-07 RMS Added UNIT_IDLE flag 18-Oct-06 RMS Changed LFC to be free running, export tmr_poll 23-Jul-05 RMS Fixed {} error in OC 01-Mar-03 RMS Added SET/SHOW LFC FREQ support Changed precision clock algorithm for V7 UNIX */ #include "id_defs.h" #include /* Device definitions */ #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diag mode */ #define UNIT_DIAG (1 << UNIT_V_DIAG) #define STA_OVF 0x08 /* PIC overflow */ #define CMD_STRT 0x20 /* start */ #define PIC_V_RATE 12 /* rate */ #define PIC_M_RATE 0xF #define PIC_RATE (PIC_M_RATE << PIC_V_RATE) #define PIC_CTR 0x0FFF /* PIC counters */ #define GET_RATE(x) (((x) >> PIC_V_RATE) & PIC_M_RATE) #define GET_CTR(x) ((x) & PIC_CTR) #define PIC_TPS 1000 extern uint32 int_req[INTSZ], int_enb[INTSZ]; int32 pic_db = 0; /* output buf */ int32 pic_ric = 0; /* reset count */ int32 pic_cic = 0; /* current count */ uint32 pic_save = 0; /* saved time */ uint32 pic_ovf = 0; /* overflow */ uint32 pic_rdp = 0; uint32 pic_wdp = 0; uint32 pic_cnti = 0; /* instr/timer */ uint32 pic_arm = 0; /* int arm */ uint32 pic_decr = 1; /* decrement */ uint16 pic_time[4] = { 1, 10, 100, 1000 }; /* delays */ uint16 pic_usec[4] = { 1, 10, 100, 1000 }; /* usec per tick */ static int32 pic_map[16] = { /* map rate to delay */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; DEVICE pic_dev; uint32 pic (uint32 dev, uint32 op, uint32 dat); t_stat pic_svc (UNIT *uptr); t_stat pic_reset (DEVICE *dptr); void pic_sched (t_bool strt); uint32 pic_rd_cic (void); int32 lfc_tps = 120; /* ticks per */ int32 lfc_poll = 8000; uint32 lfc_arm = 0; /* int arm */ DEVICE lfc_dev; uint32 lfc (uint32 dev, uint32 op, uint32 dat); t_stat lfc_svc (UNIT *uptr); t_stat lfc_reset (DEVICE *dptr); t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); /* PIC data structures pic_dev PIC device descriptor pic_unit PIC unit descriptor pic_reg PIC register list */ DIB pic_dib = { d_PIC, -1, v_PIC, NULL, &pic, NULL }; UNIT pic_unit = { UDATA (&pic_svc, UNIT_IDLE, 0), 1000 }; REG pic_reg[] = { { HRDATA (BUF, pic_db, 16) }, { HRDATA (RIC, pic_ric, 16) }, { HRDATA (CIC, pic_cic, 12) }, { FLDATA (RDP, pic_rdp, 0) }, { FLDATA (WDP, pic_wdp, 0) }, { FLDATA (OVF, pic_ovf, 0) }, { FLDATA (IREQ, int_req[l_PIC], i_PIC) }, { FLDATA (IENB, int_enb[l_PIC], i_PIC) }, { FLDATA (IARM, pic_arm, 0) }, { BRDATA (TIME, pic_time, 10, 16, 4), REG_NZ + PV_LEFT }, { DRDATA (SAVE, pic_save, 32), REG_HRO + PV_LEFT }, { DRDATA (DECR, pic_decr, 16), REG_HRO + PV_LEFT }, { FLDATA (MODE, pic_cnti, 0), REG_HRO }, { HRDATA (DEVNO, pic_dib.dno, 8), REG_HRO }, { NULL } }; MTAB pic_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, NULL, "NORMAL", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE pic_dev = { "PIC", &pic_unit, pic_reg, pic_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &pic_reset, NULL, NULL, NULL, &pic_dib, DEV_DISABLE }; /* LFC data structures lfc_dev LFC device descriptor lfc_unit LFC unit descriptor lfc_reg LFC register list */ DIB lfc_dib = { d_LFC, -1, v_LFC, NULL, &lfc, NULL }; UNIT lfc_unit = { UDATA (&lfc_svc, UNIT_IDLE, 0), 8333 }; REG lfc_reg[] = { { FLDATA (IREQ, int_req[l_LFC], i_LFC) }, { FLDATA (IENB, int_enb[l_LFC], i_LFC) }, { FLDATA (IARM, lfc_arm, 0) }, { DRDATA (TIME, lfc_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, lfc_tps, 8), PV_LEFT + REG_HRO }, { HRDATA (DEVNO, lfc_dib.dno, 8), REG_HRO }, { NULL } }; MTAB lfc_mod[] = { { MTAB_XTD|MTAB_VDV, 100, NULL, "50HZ", &lfc_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 120, NULL, "60HZ", &lfc_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &lfc_show_freq, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE lfc_dev = { "LFC", &lfc_unit, lfc_reg, lfc_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &lfc_reset, NULL, NULL, NULL, &lfc_dib, DEV_DISABLE }; /* Precision clock: IO routine */ uint32 pic (uint32 dev, uint32 op, uint32 dat) { int32 t; switch (op) { /* case IO op */ case IO_ADR: /* select */ return HW; /* HW capable */ case IO_RH: /* read halfword */ pic_rdp = 0; /* clr ptr */ return pic_rd_cic (); case IO_RD: /* read */ t = pic_rd_cic (); /* get cic */ if (pic_rdp) /* 2nd? get lo */ t = t & DMASK8; else t = (t >> 8) & DMASK8; /* 1st? get hi */ pic_rdp = pic_rdp ^ 1; /* flip byte ptr */ return t; case IO_WH: /* write halfword */ pic_wdp = 0; /* clr ptr */ pic_db = dat; break; case IO_WD: /* write */ if (pic_wdp) pic_db = (pic_db & 0xFF00) | dat; else pic_db = (pic_db & 0xFF) | (dat << 8); pic_wdp = pic_wdp ^ 1; /* flip byte ptr */ break; case IO_SS: /* sense status */ if (pic_ovf) { /* overflow? */ pic_ovf = 0; /* clear flag */ CLR_INT (v_PIC); /* clear intr */ return STA_OVF; } return 0; case IO_OC: /* output cmd */ pic_arm = int_chg (v_PIC, dat, pic_arm); /* upd int ctrl */ if (dat & CMD_STRT) { /* start? */ pic_ric = pic_db; /* new ric */ pic_cic = GET_CTR (pic_ric); /* new cic */ pic_ovf = 0; /* clear flag */ sim_cancel (&pic_unit); /* stop clock */ pic_rdp = pic_wdp = 0; /* init ptrs */ if (pic_ric & PIC_RATE) /* any rate? */ pic_sched (TRUE); } /* end if start */ break; } /* end case */ return 0; } /* Unit service */ t_stat pic_svc (UNIT *uptr) { t_bool rate_chg = FALSE; if (pic_cnti) /* one shot? */ pic_cic = 0; pic_cic = pic_cic - pic_decr; /* decrement */ if (pic_cic <= 0) { /* overflow? */ if (pic_wdp) /* broken wr? set flag */ pic_ovf = 1; if (pic_arm) /* if armed, intr */ SET_INT (v_PIC); if (GET_RATE (pic_ric) != GET_RATE (pic_db)) /* rate change? */ rate_chg = TRUE; pic_ric = pic_db; /* new ric */ pic_cic = GET_CTR (pic_ric); /* new cic */ if ((pic_ric & PIC_RATE) == 0) return SCPE_OK; } pic_sched (rate_chg); return SCPE_OK; } /* Schedule next interval If eff rate < 1ms, or diagnostic mode, count instructions If eff rate = 1ms, and not diagnostic mode, use timer */ void pic_sched (t_bool strt) { int32 r, t, intv, intv_usec; pic_save = sim_grtime (); /* save start */ r = pic_map[GET_RATE (pic_ric)]; /* get mapped rate */ intv = pic_cic? pic_cic: 1; /* get cntr */ intv_usec = intv * pic_usec[r]; /* cvt to usec */ if (!(pic_unit.flags & UNIT_DIAG) && /* not diag? */ ((intv_usec % 1000) == 0)) { /* 1ms multiple? */ pic_cnti = 0; /* clr mode */ pic_decr = pic_usec[3 - r]; /* set decrement */ if (strt) /* init or */ t = sim_rtcn_init (pic_time[3], TMR_PIC); else t = sim_rtcn_calb (PIC_TPS, TMR_PIC); /* calibrate */ } else { pic_cnti = 1; /* set mode */ pic_decr = 1; /* decr = 1 */ t = pic_time[r] * intv; /* interval */ if (t == 1) /* for diagn */ t++; } sim_activate (&pic_unit, t); /* activate */ return; } /* Read (interpolated) current interval */ uint32 pic_rd_cic (void) { if (sim_is_active (&pic_unit) && pic_cnti) { /* running, one shot? */ uint32 delta = sim_grtime () - pic_save; /* interval */ uint32 tm = pic_time[pic_map[GET_RATE (pic_ric)]]; /* ticks/intv */ delta = delta / tm; /* ticks elapsed */ if (delta >= ((uint32) pic_cic)) /* cap value */ return 0; return pic_cic - delta; } return pic_cic; } /* Reset routine */ t_stat pic_reset (DEVICE *dptr) { sim_cancel (&pic_unit); /* cancel unit */ pic_ric = pic_cic = 0; pic_db = 0; pic_ovf = 0; /* clear state */ pic_cnti = 0; pic_decr = 1; pic_rdp = pic_wdp = 0; CLR_INT (v_PIC); /* clear int */ CLR_ENB (v_PIC); /* disable int */ pic_arm = 0; /* disarm int */ return SCPE_OK; } /* Line clock: IO routine */ uint32 lfc (uint32 dev, uint32 op, uint32 dat) { switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_OC: /* command */ lfc_arm = int_chg (v_LFC, dat, lfc_arm); /* upd int ctrl */ break; } return 0; } /* Unit service */ t_stat lfc_svc (UNIT *uptr) { lfc_poll = sim_rtcn_calb (lfc_tps, TMR_LFC); /* calibrate */ sim_activate (uptr, lfc_poll); /* reactivate */ if (lfc_arm) { /* armed? */ SET_INT (v_LFC); /* req intr */ } return SCPE_OK; } /* Reset routine */ t_stat lfc_reset (DEVICE *dptr) { lfc_poll = sim_rtcn_init (lfc_unit.wait, TMR_LFC); sim_activate_abs (&lfc_unit, lfc_poll); /* init clock */ CLR_INT (v_LFC); /* clear int */ CLR_ENB (v_LFC); /* disable int */ lfc_arm = 0; /* disarm int */ return SCPE_OK; } /* Set frequency */ t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 100) && (val != 120)) return SCPE_IERR; lfc_tps = val; return SCPE_OK; } /* Show frequency */ t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (lfc_tps == 100)? "50Hz": "60Hz"); return SCPE_OK; } simh-3.8.1/Interdata/id16_cpu.c0000644000175000017500000026665211112026546014351 0ustar vlmvlm/* id16_cpu.c: Interdata 16b CPU simulator Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu Interdata 16b CPU 28-Apr-07 RMS Removed clock initialization 27-Oct-06 RMS Added idle support Removed separate PASLA clock 06-Feb-06 RMS Fixed bug in DH (found by Mark Hittinger) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 25-Aug-05 RMS Fixed DH integer overflow cases 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Mar-05 RMS Fixed bug in show history routine (from Mark Hittinger) Revised examine/deposit to do words rather than bytes 07-Nov-04 RMS Added instruction history 22-Sep-03 RMS Added additional instruction decode types 07-Feb-03 RMS Fixed bug in SETM, SETMR (found by Mark Pizzolato) The register state for the Interdata 16b CPU is: R[0:F]<0:15> general registers F[0:7]<0:31> single precision floating point registers D[0:7]<0:63> double precision floating point registers PSW<0:31> processor status word, including STAT<0:11> status flags CC<0:3> condition codes PC<0:15> program counter int_req[8]<0:31> interrupt requests int_enb[8]<0:31> interrupt enables The Interdata 16b systems have four instruction formats: register to register, short format, register to memory, and register to storage. The formats are: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | R2 | register-register +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | N | short format +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-memory +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op | R1 | RX | register-storage +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ For register-memory and register-storage instructions, an effective address is calculated as follows: effective addr = address + RX (if RX > 0) Register-memory instructions can access an address space of 64K bytes. The Interdata 16b product line had many different models, with varying instruction sets: instruction group model = 3 4 5 70 80 716 816 816E base group (61) y y y y y y y y AL, LM, STM (3) - y y y y y y y single prec fp (13) - y y y y y y y model 5 group (36) - - y y y y y y double prec fp (17) - - - - - - y y memory extension (4) - - - - - - - y This allows the most common CPU options to be covered by just five model selections: I3, I4, I5/70/80/716, I816, and I816E. Variations within a model (e.g., 816 with no floating point or just single precision floating point) are not implemented. The I3 kept its general registers in memory; this is not simulated. Single precision (only) floating point was implemented in microcode, did not have a guard digit, and kept the floating point registers in memory. Double precision floating point was implemented in hardware, provided a guard digit for single precision (but not double), and kept the floating point registers in hardware. This routine is the instruction decode routine for the Interdata CPU. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered wait state and no I/O outstanding invalid instruction I/O error in I/O simulator 2. Interrupts. Each device has an interrupt armed flag, an interrupt request flag, and an interrupt enabled flag. To facilitate evaluation, all interrupt requests are kept in int_req, and all enables in int_enb. Interrupt armed flags are local to devices. If external interrupts are enabled in the PSW, and a request is pending, an interrupt occurs. 3. Non-existent memory. On the Interdata 16b, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: id_defs.h add device interrupt definitions id16_sys.c add sim_devices table entry */ #include "id_defs.h" #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] = oPC #define VAMASK VAMASK16 #define VA_S1 0x8000 /* S0/S1 flag */ #define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ #define UNIT_V_ID4 (UNIT_V_UF + 1) #define UNIT_V_716 (UNIT_V_UF + 2) #define UNIT_V_816 (UNIT_V_UF + 3) #define UNIT_V_816E (UNIT_V_UF + 4) #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define UNIT_ID4 (1 << UNIT_V_ID4) #define UNIT_716 (1 << UNIT_V_716) #define UNIT_816 (1 << UNIT_V_816) #define UNIT_816E (1 << UNIT_V_816E) #define UNIT_TYPE (UNIT_ID4 | UNIT_716 | UNIT_816 | UNIT_816E) #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { uint16 vld; uint16 pc; uint16 ir1; uint16 ir2; uint16 r1; uint16 ea; uint16 opnd; } InstHistory; #define PSW_GETMAP(x) (((x) >> PSW_V_MAP) & PSW_M_MAP) #define SEXT16(x) (((x) & SIGN16)? ((int32) ((x) | 0xFFFF8000)): \ ((int32) ((x) & 0x7FFF))) #define CC_GL_16(x) if ((x) & SIGN16) \ cc = CC_L; \ else if (x) \ cc = CC_G; \ else cc = 0 #define CC_GL_32(x) if ((x) & SIGN32) \ cc = CC_L; \ else if (x) \ cc = CC_G; \ else cc = 0 #define BUILD_PSW(x) (((PSW & ~CC_MASK) | (x)) & psw_mask) #define CPU_x16 (cpu_unit.flags & (UNIT_716 | UNIT_816 | UNIT_816E)) uint32 GREG[16] = { 0 }; /* general registers */ uint16 *M = NULL; /* memory */ uint32 *R = &GREG[0]; /* register set ptr */ uint32 F[8] = { 0 }; /* sp fp registers */ dpr_t D[8] = { 0 }; /* dp fp registers */ uint32 PSW = 0; /* processor status word */ uint32 psw_mask = PSW_x16; /* PSW mask */ uint32 PC = 0; /* program counter */ uint32 SR = 0; /* switch register */ uint32 DR = 0; /* display register */ uint32 DRX = 0; /* display extension */ uint32 drmod = 0; /* mode */ uint32 srpos = 0; /* switch register pos */ uint32 drpos = 0; /* display register pos */ uint32 s0_rel = 0; /* S0 relocation */ uint32 s1_rel = 0; /* S1 relocation */ uint32 int_req[INTSZ] = { 0 }; /* interrupt requests */ uint32 int_enb[INTSZ] = { 0 }; /* interrupt enables */ int32 blkiop = -1; /* block I/O in prog */ uint32 qevent = 0; /* events */ uint32 stop_inst = 0; /* stop on ill inst */ uint32 stop_wait = 0; /* stop on wait */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ uint32 dec_flgs = 0; /* decode flags */ uint32 fp_in_hwre = 0; /* ucode/hwre fp */ uint32 pawidth = PAWIDTH16; /* phys addr mask */ uint32 hst_p = 0; /* history pointer */ uint32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ struct BlockIO blk_io; /* block I/O status */ uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout) = { NULL }; extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern t_bool sim_idle_enab; uint32 ReadB (uint32 loc); uint32 ReadH (uint32 loc); void WriteB (uint32 loc, uint32 val); void WriteH (uint32 loc, uint32 val); uint32 int_auto (uint32 dev, uint32 cc); uint32 addtoq (uint32 ea, uint32 val, uint32 flg); uint32 remfmq (uint32 ea, uint32 r1, uint32 flg); uint32 newPSW (uint32 val); uint32 swap_psw (uint32 loc, uint32 cc); uint32 testsysq (uint32); uint32 display (uint32 dev, uint32 op, uint32 dat); 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_model (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_bool devtab_init (void); extern void int_eval (void); extern uint32 int_getdev (void); extern t_bool sch_blk (uint32 dev); extern uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea); extern uint32 f_fix (uint32 op, uint32 r1, uint32 r2); extern uint32 f_flt (uint32 op, uint32 r1, uint32 r2); /* Instruction decoding table - flags are first implementation */ const uint16 decrom[256] = { 0, /* 00 */ OP_RR, /* BALR */ OP_RR, /* BTCR */ OP_RR, /* BFCR */ OP_RR, /* NHR */ OP_RR, /* CLHR */ OP_RR, /* OHR */ OP_RR, /* XHR */ OP_RR, /* LHR */ OP_RR | OP_716, /* CHR */ OP_RR, /* AHR */ OP_RR, /* SHR */ OP_RR, /* MHR */ OP_RR, /* DHR */ OP_RR, /* ACHR */ OP_RR, /* SCHR */ 0, 0, 0, /* 10:12 */ OP_RR | OP_816E | OP_PRV, /* SETMR */ 0, 0, 0, 0, /* 14:1F */ 0, 0, 0, 0, 0, 0, 0, 0, OP_NO | OP_716, /* BTBS */ OP_NO | OP_716, /* BTFS */ OP_NO | OP_716, /* BFBS */ OP_NO | OP_716, /* BFFS */ OP_NO | OP_716, /* LIS */ OP_NO | OP_716, /* LCS */ OP_NO | OP_716, /* AIS */ OP_NO | OP_716, /* SIS */ OP_NO | OP_ID4, /* LER */ OP_NO | OP_ID4, /* CER */ OP_NO | OP_ID4, /* AER */ OP_NO | OP_ID4, /* SER */ OP_NO | OP_ID4, /* MER */ OP_NO | OP_ID4, /* DER */ OP_NO | OP_816, /* FXR */ OP_NO | OP_816, /* FLR */ 0, 0, 0, /* 30:32 */ OP_NO | OP_816E | OP_PRV, /* LPSR */ 0, 0, 0, 0, /* 34:37 */ OP_NO | OP_816 | OP_DPF, /* LDR */ OP_NO | OP_816 | OP_DPF, /* CDR */ OP_NO | OP_816 | OP_DPF, /* ADR */ OP_NO | OP_816 | OP_DPF, /* SDR */ OP_NO | OP_816 | OP_DPF, /* MDR */ OP_NO | OP_816 | OP_DPF, /* DDR */ OP_NO | OP_816 | OP_DPF, /* FXDR */ OP_NO | OP_816 | OP_DPF, /* FLDR */ OP_RX, /* STH */ OP_RX, /* BAL */ OP_RX, /* BTC */ OP_RX, /* BFC */ OP_RXH, /* NH */ OP_RXH, /* CLH */ OP_RXH, /* OH */ OP_RXH, /* XH */ OP_RXH, /* LH */ OP_RXH | OP_716, /* CH */ OP_RXH, /* AH */ OP_RXH, /* SH */ OP_RXH, /* MH */ OP_RXH, /* DH */ OP_RXH, /* ACH */ OP_RXH, /* SCH */ 0, 0, 0, /* 50:52 */ OP_RXH | OP_816E | OP_PRV, /* SETM */ 0, 0, 0, 0, /* 54:5F */ 0, 0, 0, 0, 0, 0, 0, 0, OP_RX | OP_ID4, /* STE */ OP_RXH | OP_716, /* AHM */ 0, 0, /* 62:63 */ OP_RX | OP_716, /* ATL */ OP_RX | OP_716, /* ABL */ OP_RX | OP_716, /* RTL */ OP_RX | OP_716, /* RBL */ OP_RX | OP_ID4, /* LE */ OP_RX | OP_ID4, /* CE */ OP_RX | OP_ID4, /* AE */ OP_RX | OP_ID4, /* SE */ OP_RX | OP_ID4, /* ME */ OP_RX | OP_ID4, /* DE */ 0, 0, /* 6E:6F */ OP_RX | OP_816 | OP_DPF, /* STD */ OP_RX | OP_816, /* SME */ OP_RX | OP_816, /* LME */ OP_RXH | OP_816E | OP_PRV, /* LPS */ 0, 0, 0, 0, /* 74:7F */ OP_RX | OP_816 | OP_DPF, /* LD */ OP_RX | OP_816 | OP_DPF, /* CD */ OP_RX | OP_816 | OP_DPF, /* AD */ OP_RX | OP_816 | OP_DPF, /* SD */ OP_RX | OP_816 | OP_DPF, /* MD */ OP_RX | OP_816 | OP_DPF, /* DD */ OP_RX | OP_816 | OP_DPF, /* STMD */ OP_RX | OP_816 | OP_DPF, /* LMD */ 0, 0, 0, 0, 0, 0, 0, 0, /* 80:8F */ 0, 0, 0, 0, 0, 0, 0, 0, OP_NO | OP_716, /* SRLS */ OP_NO | OP_716, /* SLLS */ OP_NO, /* STBR */ OP_RR, /* LDBR */ OP_RR | OP_716, /* EXBR */ OP_NO | OP_716 | OP_PRV, /* EPSR */ OP_RR | OP_PRV, /* WBR */ OP_RR | OP_PRV, /* RBR */ OP_RR | OP_716 | OP_PRV, /* WHR */ OP_RR | OP_716 | OP_PRV, /* RHR */ OP_RR | OP_PRV, /* WDR */ OP_RR | OP_PRV, /* RDR */ OP_RR | OP_716, /* MHUR */ OP_RR | OP_PRV, /* SSR */ OP_RR | OP_PRV, /* OCR */ OP_RR | OP_PRV, /* AIR */ 0, 0, 0, 0, 0, 0, 0, 0, /* A0:AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0:BF */ 0, 0, 0, 0, 0, 0, 0, 0, OP_RX, /* BXH */ OP_RX, /* BXLE */ OP_RX | OP_PRV, /* LPSW */ OP_RS | OP_716, /* THI */ OP_RS, /* NHI */ OP_RS, /* CLHI */ OP_RS, /* OHI */ OP_RS, /* XHI */ OP_RS, /* LHI */ OP_RS | OP_716, /* CHI */ OP_RS, /* AHI */ OP_RS, /* SHI */ OP_RS, /* SRHL */ OP_RS, /* SLHL */ OP_RS, /* SRHA */ OP_RS, /* SLHA */ OP_RX | OP_ID4, /* STM */ OP_RX | OP_ID4, /* LM */ OP_RX, /* STB */ OP_RXB, /* LDB */ OP_RXB | OP_716, /* CLB */ OP_RX | OP_ID4 | OP_PRV, /* AL */ OP_RXH | OP_PRV, /* WB */ OP_RXH | OP_PRV, /* RB */ OP_RX | OP_716 | OP_PRV, /* WH */ OP_RX | OP_716 | OP_PRV, /* RH */ OP_RX | OP_PRV, /* WD */ OP_RX | OP_PRV, /* RD */ OP_RXH | OP_716, /* MHU */ OP_RX | OP_PRV, /* SS */ OP_RX | OP_PRV, /* OC */ OP_RX | OP_PRV, /* AI */ 0, /* E0 */ OP_RX | OP_716, /* SVC */ OP_RS | OP_716 | OP_PRV, /* SINT */ 0, 0, 0, 0, 0, 0, 0, /* E3:E9 */ OP_RS | OP_716, /* RRL */ OP_RS | OP_716, /* RLL */ OP_RS | OP_716, /* SRL */ OP_RS | OP_716, /* SLL */ OP_RS | OP_716, /* SRA */ OP_RS | OP_716, /* SLA */ 0, 0, 0, 0, 0, 0, 0, 0, /* F0:FF */ 0, 0, 0, 0, 0, 0, 0, 0 }; /* 8/16E relocation constants for S0 and S1, indexed by PSW<8:11> */ static uint32 s0_rel_const[16] = { /* addr 0-7FFF */ 0x00000, 0x00000, 0x00000, 0x00000, /* 0 = no reloc */ 0x00000, 0x00000, 0x00000, 0x08000, /* 8000 = rel to S1 */ 0x08000, 0x08000, 0x08000, 0x08000, 0x08000, 0x08000, 0x08000, 0x00000 }; static uint32 s1_rel_const[16] = { /* addr 8000-FFFF */ 0x00000, 0x08000, 0x10000, 0x18000, /* reloc const must */ 0x20000, 0x28000, 0x30000, 0xFFF8000, /* "sub" base addr */ 0x00000, 0x08000, 0x10000, 0x18000, 0x20000, 0x28000, 0x30000, 0x00000 }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ DIB cpu_dib = { d_DS, -1, v_DS, NULL, &display, NULL }; UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_716, MAXMEMSIZE16) }; REG cpu_reg[] = { { HRDATA (PC, PC, 16) }, { HRDATA (R0, GREG[0], 16) }, { HRDATA (R1, GREG[1], 16) }, { HRDATA (R2, GREG[2], 16) }, { HRDATA (R3, GREG[3], 16) }, { HRDATA (R4, GREG[4], 16) }, { HRDATA (R5, GREG[5], 16) }, { HRDATA (R6, GREG[6], 16) }, { HRDATA (R7, GREG[7], 16) }, { HRDATA (R8, GREG[8], 16) }, { HRDATA (R9, GREG[9], 16) }, { HRDATA (R10, GREG[10], 16) }, { HRDATA (R11, GREG[11], 16) }, { HRDATA (R12, GREG[12], 16) }, { HRDATA (R13, GREG[13], 16) }, { HRDATA (R14, GREG[14], 16) }, { HRDATA (R15, GREG[15], 16) }, { HRDATA (FR0, F[0], 32) }, { HRDATA (FR2, F[1], 32) }, { HRDATA (FR4, F[2], 32) }, { HRDATA (FR6, F[3], 32) }, { HRDATA (FR8, F[4], 32) }, { HRDATA (FR10, F[5], 32) }, { HRDATA (FR12, F[6], 32) }, { HRDATA (FR14, F[7], 32) }, { HRDATA (D0H, D[0].h, 32) }, { HRDATA (D0L, D[0].l, 32) }, { HRDATA (D2H, D[1].h, 32) }, { HRDATA (D2L, D[1].l, 32) }, { HRDATA (D4H, D[2].h, 32) }, { HRDATA (D4L, D[2].l, 32) }, { HRDATA (D6H, D[3].h, 32) }, { HRDATA (D6L, D[3].l, 32) }, { HRDATA (D8H, D[4].h, 32) }, { HRDATA (D8L, D[4].l, 32) }, { HRDATA (D10H, D[5].h, 32) }, { HRDATA (D10L, D[5].l, 32) }, { HRDATA (D12L, D[6].l, 32) }, { HRDATA (D12H, D[6].h, 32) }, { HRDATA (D14H, D[7].h, 32) }, { HRDATA (D14L, D[7].l, 32) }, { HRDATA (PSW, PSW, 16) }, { HRDATA (CC, PSW, 4) }, { HRDATA (SR, SR, 16) }, { HRDATA (DR, DR, 32) }, { HRDATA (DRX, DRX, 8) }, { FLDATA (DRMOD, drmod, 0) }, { FLDATA (SRPOS, srpos, 0) }, { HRDATA (DRPOS, drpos, 3) }, { BRDATA (IRQ, int_req, 16, 32, 8) }, { BRDATA (IEN, int_enb, 16, 32, 8) }, { HRDATA (QEVENT, qevent, 4), REG_HRO }, { FLDATA (STOP_INST, stop_inst, 0) }, { FLDATA (STOP_WAIT, stop_inst, 0) }, { BRDATA (PCQ, pcq, 16, 16, PCQ_SIZE), REG_RO+REG_CIRC }, { HRDATA (PCQP, pcq_p, 6), REG_HRO }, { HRDATA (WRU, sim_int_char, 8) }, { HRDATA (BLKIOD, blk_io.dfl, 16), REG_HRO }, { HRDATA (BLKIOC, blk_io.cur, 16), REG_HRO }, { HRDATA (BLKIOE, blk_io.end, 16), REG_HRO }, { NULL } }; MTAB cpu_mod[] = { { UNIT_TYPE, 0, "I3", "I3", &cpu_set_model }, { UNIT_TYPE, UNIT_ID4, "I4", "I4", &cpu_set_model }, { UNIT_TYPE, UNIT_716, "7/16", "716", &cpu_set_model }, { UNIT_TYPE, UNIT_816, "8/16", "816", &cpu_set_model }, { UNIT_TYPE, UNIT_816E, "8/16E", "816E", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "CONSINT", &cpu_set_consint, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 16, 18, 2, 16, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, &cpu_dib, 0 }; t_stat sim_instr (void) { uint32 cc; t_stat reason; /* Restore register state */ if (devtab_init ()) /* check conflicts */ return SCPE_STOP; pawidth = PAWIDTH16; /* default width */ if (cpu_unit.flags & UNIT_816E) { /* 8/16E? */ dec_flgs = 0; /* all instr ok */ fp_in_hwre = 1; /* fp in hwre */ pawidth = PAWIDTH16E; /* 18b phys addr */ psw_mask = PSW_816E; /* mem ext bits */ } else if (cpu_unit.flags & UNIT_816) { /* 8/16? */ dec_flgs = OP_816E; fp_in_hwre = 1; pawidth = PAWIDTH16; psw_mask = PSW_x16; } else if (cpu_unit.flags & UNIT_716) { /* I5, 70, 80, 7/16? */ dec_flgs = OP_816 | OP_816E; fp_in_hwre = 0; pawidth = PAWIDTH16; psw_mask = PSW_x16; } else if (cpu_unit.flags & UNIT_ID4) { /* I4? */ dec_flgs = OP_716 | OP_816 | OP_816E; fp_in_hwre = 0; pawidth = PAWIDTH16; psw_mask = PSW_ID4; } else { dec_flgs = OP_ID4 | OP_716 | OP_816 | OP_816E; /* I3 */ fp_in_hwre = 0; pawidth = PAWIDTH16; psw_mask = PSW_ID4; } int_eval (); /* eval interrupts */ cc = newPSW (PSW & psw_mask); /* split PSW, eval wait */ reason = 0; /* Process events */ while (reason == 0) { /* loop until halted */ uint32 dev, drom, inc, lim, opnd; uint32 op, r1, r1p1, r2, ea, oPC; uint32 rslt, t, map; uint32 ir1, ir2, ityp; int32 sr, st; if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; int_eval (); } if (qevent) { /* any events? */ if (qevent & EV_BLK) { /* block I/O in prog? */ dev = blk_io.dfl & DEV_MAX; /* get device */ cc = dev_tab[dev] (dev, IO_SS, 0) & 0xF; /* sense status */ if (cc == STA_BSY) { /* just busy? */ sim_interval = 0; /* force I/O event */ continue; } else if (cc == 0) { /* ready? */ if (blk_io.dfl & BL_RD) { /* read? */ t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ if ((t == 0) && (blk_io.dfl & BL_LZ)) continue; blk_io.dfl = blk_io.dfl & ~BL_LZ; /* non-zero seen */ WriteB (blk_io.cur, t); /* write mem */ } else { /* write */ t = ReadB (blk_io.cur); /* read mem */ dev_tab[dev] (dev, IO_WD, t); /* put byte */ } if (blk_io.cur != blk_io.end) { /* more to do? */ blk_io.cur = (blk_io.cur + 1) & VAMASK; /* incr addr */ continue; } } qevent = qevent & ~EV_BLK; /* clr block I/O flg */ int_eval (); /* re-eval intr */ continue; } if ((qevent & EV_INT) && (PSW & PSW_EXI)) { /* interrupt? */ if (PSW & PSW_AIO) { /* auto enabled? */ dev = int_getdev (); /* get int dev */ cc = int_auto (dev, cc); /* do auto intr */ int_eval (); /* re-eval intr */ } else cc = swap_psw (EXIPSW, cc); /* old type, swap */ continue; } if (PSW & PSW_WAIT) { /* wait state? */ if (sim_idle_enab) /* idling enabled? */ sim_idle (TMR_LFC, TRUE); else sim_interval = sim_interval - 1; /* no, count cycle */ continue; } qevent = 0; /* no events */ } /* end if event */ /* Fetch and decode instruction */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } sim_interval = sim_interval - 1; ir1 = ReadH (oPC = PC); /* fetch instr */ op = (ir1 >> 8) & 0xFF; /* isolate op, R1, R2 */ r1 = (ir1 >> 4) & 0xF; r2 = ir1 & 0xF; drom = decrom[op]; ityp = drom & OP_MASK; if ((drom == 0) || (drom & dec_flgs)) { /* not in model? */ if (stop_inst) /* stop or */ reason = STOP_RSRV; else cc = swap_psw (ILOPSW, cc); /* swap PSW */ continue; } if ((drom & OP_PRV) && (PSW & PSW_PRO)) { /* priv & protected? */ cc = swap_psw (ILOPSW, cc); /* swap PSW */ continue; } switch (ityp) { /* decode instruction */ case OP_NO: /* no operand */ opnd = r2; /* assume short */ break; case OP_RR: /* reg-reg */ opnd = R[r2]; /* operand is R2 */ break; case OP_RS: /* reg-storage */ case OP_RX: /* reg-mem */ PC = (PC + 2) & VAMASK; /* increment PC */ ir2 = ea = ReadH (PC); /* fetch address */ if (r2) /* index calculation */ ea = (ir2 + R[r2]) & VAMASK; opnd = ea; /* operand is ea */ break; case OP_RXB: /* reg-mem byte */ PC = (PC + 2) & VAMASK; /* increment PC */ ir2 = ea = ReadH (PC); /* fetch address */ if (r2) /* index calculation */ ea = (ir2 + R[r2]) & VAMASK; opnd = ReadB (ea); /* fetch operand */ break; case OP_RXH: /* reg-mem halfword */ PC = (PC + 2) & VAMASK; /* increment PC */ ir2 = ea = ReadH (PC); /* fetch address */ if (r2) /* index calculation */ ea = (ir2 + R[r2]) & VAMASK; opnd = ReadH (ea); /* fetch operand */ break; default: return SCPE_IERR; } if (hst_lnt) { /* instruction history? */ hst[hst_p].vld = 1; hst[hst_p].pc = oPC; hst[hst_p].ir1 = ir1; hst[hst_p].ir2 = ir2; hst[hst_p].r1 = R[r1]; hst[hst_p].ea = ea; hst[hst_p].opnd = opnd; hst_p = hst_p + 1; if (hst_p >= hst_lnt) hst_p = 0; } PC = (PC + 2) & VAMASK; /* increment PC */ switch (op) { /* case on opcode */ /* Load/store instructions */ case 0x08: /* LHR - RR */ case 0x24: /* LIS - NO */ case 0x48: /* LH - RXH */ case 0xC8: /* LHI - RS */ R[r1] = opnd; /* load operand */ CC_GL_16 (R[r1]); /* set G,L */ break; case 0x25: /* LCS - NO */ R[r1] = (~opnd + 1) & DMASK16; /* load complement */ CC_GL_16 (R[r1]); /* set G,L */ break; case 0x40: /* STH - RX */ WriteH (ea, R[r1]); /* store register */ break; case 0xD1: /* LM - RX */ for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ R[r1] = ReadH (ea); /* load register */ ea = (ea + 2) & VAMASK; /* incr mem addr */ } break; case 0xD0: /* STM - RX */ for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ WriteH (ea, R[r1]); /* store register */ ea = (ea + 2) & VAMASK; /* incr mem addr */ } break; case 0x93: /* LDBR - RR */ case 0xD3: /* LDB - RXB */ R[r1] = opnd & DMASK8; /* load byte */ break; case 0x92: /* STBR - NO */ R[r2] = (R[r2] & ~DMASK8) | (R[r1] & DMASK8); /* store byte */ break; case 0xD2: /* STB - RX */ WriteB (ea, R[r1] & DMASK8); /* store byte */ break; case 0x94: /* EXBR - RR */ R[r1] = (opnd >> 8) | ((opnd & DMASK8) << 8); break; /* Control instructions */ case 0x01: /* BALR - RR */ case 0x41: /* BAL - RX */ PCQ_ENTRY; /* save old PC */ R[r1] = PC; /* save cur PC */ PC = opnd; /* branch */ break; case 0x02: /* BTCR - RR */ case 0x42: /* BTC - RX */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = opnd; } break; case 0x20: /* BTBS - NO */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = (oPC - r2 - r2) & VAMASK; } break; case 0x21: /* BTFS - NO */ if (cc & r1) { /* test CC's */ PCQ_ENTRY; /* branch if true */ PC = (oPC + r2 + r2) & VAMASK; } break; case 0x03: /* BFCR - RR */ case 0x43: /* BFC - RX */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = opnd; } break; case 0x22: /* BFBS - NO */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = (oPC - r2 - r2) & VAMASK; } break; case 0x23: /* BFFS - NO */ if ((cc & r1) == 0) { /* test CC's */ PCQ_ENTRY; /* branch if false */ PC = (oPC + r2 + r2) & VAMASK; } break; case 0xC0: /* BXH - RX */ inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ R[r1] = (R[r1] + inc) & DMASK16; /* R1 = R1 + inc */ if (R[r1] > lim) { /* if R1 > lim */ PCQ_ENTRY; /* branch */ PC = opnd; } break; case 0xC1: /* BXLE - RX */ inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ R[r1] = (R[r1] + inc) & DMASK16; /* R1 = R1 + inc */ if (R[r1] <= lim) { /* if R1 <= lim */ PCQ_ENTRY; /* branch */ PC = opnd; } break; /* Logical instructions */ case 0x04: /* NHR - RR */ case 0x44: /* NH - RXH */ case 0xC4: /* NHI - RS */ R[r1] = R[r1] & opnd; /* result */ CC_GL_16 (R[r1]); /* set G,L */ break; case 0x06: /* OHR - RR */ case 0x46: /* OH - RXH */ case 0xC6: /* OHI - RS */ R[r1] = R[r1] | opnd; /* result */ CC_GL_16 (R[r1]); /* set G,L */ break; case 0x07: /* XHR - RR */ case 0x47: /* XH - RXH */ case 0xC7: /* XHI - RS */ R[r1] = R[r1] ^ opnd; /* result */ CC_GL_16 (R[r1]); /* set G,L */ break; case 0xC3: /* THI - RS */ rslt = R[r1] & opnd; /* result */ CC_GL_16 (rslt); /* set G, L */ break; case 0x05: /* CLHR - RR */ case 0x45: /* CLH - RXH */ case 0xC5: /* CLHI - RS */ rslt = (R[r1] - opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (R[r1] < opnd) /* set C if borrow */ cc = cc | CC_C; if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; break; case 0xD4: /* CLB - RXB */ t = R[r1] & DMASK8; rslt = (t - opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (t < opnd) /* set C if borrow */ cc = cc | CC_C; break; /* Shift instructions */ case 0xCC: /* SRHL - RS */ opnd = opnd & 0xF; /* shift count */ case 0x90: /* SRLS - NO */ rslt = R[r1] >> opnd; /* result */ CC_GL_16 (rslt); /* set G,L */ if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = rslt; /* store result */ break; case 0xCD: /* SLHL - RS */ opnd = opnd & 0xF; /* shift count */ case 0x91: /* SLLS - NO */ rslt = R[r1] << opnd; /* raw result */ R[r1] = rslt & DMASK16; /* masked result */ CC_GL_16 (R[r1]); /* set G,L */ if (opnd && (rslt & 0x10000)) /* set C if shft out */ cc = cc | CC_C; break; case 0xCE: /* SRHA - RS */ opnd = opnd & 0xF; /* shift count */ rslt = (SEXT16 (R[r1]) >> opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = rslt; /* store result */ break; case 0xCF: /* SLHA - RS */ opnd = opnd & 0xF; /* shift count */ rslt = R[r1] << opnd; /* raw result */ R[r1] = (R[r1] & SIGN16) | (rslt & MMASK16); /* arith result */ CC_GL_16 (R[r1]); /* set G,L */ if (opnd && (rslt & SIGN16)) /* set C if shft out */ cc = cc | CC_C; break; case 0xEA: /* RRL - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ if (opnd) /* result */ rslt = (t >> opnd) | (t << (32 - opnd)); else rslt = t; /* no shift */ CC_GL_32 (rslt); /* set G,L 32b */ R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0xEB: /* RLL - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ if (opnd) /* result */ rslt = (t << opnd) | (t >> (32 - opnd)); else rslt = t; /* no shift */ CC_GL_32 (rslt); /* set G,L 32b */ R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0xEC: /* SRL - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ rslt = t >> opnd; /* result */ CC_GL_32 (rslt); /* set G,L 32b */ if (opnd && ((t >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0xED: /* SLL - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ rslt = t << opnd; /* result */ CC_GL_32 (rslt); /* set G,L 32b */ if (opnd && ((t << (opnd - 1)) & SIGN32)) cc = cc | CC_C; R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0xEE: /* SRA - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ rslt = ((int32) t) >> opnd; /* signed result */ CC_GL_32 (rslt); /* set G,L 32b */ if (opnd && ((t >> (opnd - 1)) & 1)) cc = cc | CC_C; R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0xEF: /* SLA - RS */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ opnd = opnd & 0x1F; /* shift count */ t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ rslt = (t & SIGN32) | ((t << opnd) & MMASK32); /* signed result */ CC_GL_32 (rslt); /* set G,L 32b */ if (opnd && ((t << opnd) & SIGN32)) cc = cc | CC_C; R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; /* Arithmetic instructions */ case 0x0A: /* AHR - RR */ case 0x26: /* AIS - NO */ case 0x4A: /* AH - RXH */ case 0xCA: /* AHI - RS */ rslt = (R[r1] + opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (rslt < opnd) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; R[r1] = rslt; break; case 0x61: /* AHM - RXH */ rslt = (R[r1] + opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (rslt < opnd) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; WriteH (ea, rslt); /* store in memory */ break; case 0x0B: /* SHR - RR */ case 0x27: /* SIS - NO */ case 0x4B: /* SH - RXH */ case 0xCB: /* SHI - RS */ rslt = (R[r1] - opnd) & DMASK16; /* result */ CC_GL_16 (rslt); /* set G,L */ if (R[r1] < opnd) /* set C if borrow */ cc = cc | CC_C; if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; R[r1] = rslt; break; case 0x09: /* CHR - RR */ case 0x49: /* CH - RXH */ case 0xC9: /* CHI - RS */ sr = SEXT16 (R[r1]); /* sign ext */ st = SEXT16 (opnd); if (sr < st) /* < sets C, L */ cc = CC_C | CC_L; else if (sr > st) /* > sets G */ cc = CC_G; else cc = 0; if (((R[r1] ^ opnd) & (~opnd ^ (sr - st))) & SIGN16) cc = cc | CC_V; break; case 0x0C: /* MHR - RR */ case 0x4C: /* MH - RXH */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ rslt = SEXT16 (R[r1p1]) * SEXT16 (opnd); /* multiply */ R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0x9C: /* MHUR - RR */ case 0xDC: /* MHU - RXH */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ rslt = R[r1p1] * opnd; /* multiply, unsigned */ R[r1] = (rslt >> 16) & DMASK16; /* hi result */ R[r1p1] = rslt & DMASK16; /* lo result */ break; case 0x0D: /* DHR - RR */ case 0x4D: /* DH - RXH */ r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ if ((opnd == 0) || ((R[r1] == 0x8000) && (R[r1p1] == 0) && (opnd == 0xFFFF))) { if (PSW & PSW_AFI) /* div fault enabled? */ cc = swap_psw (AFIPSW, cc); /* swap PSW */ break; } sr = (R[r1] << 16) | R[r1p1]; /* signed 32b divd */ st = sr / SEXT16 (opnd); /* signed quotient */ sr = sr % SEXT16 (opnd); /* remainder */ if ((st < 0x8000) && (st >= -0x8000)) { /* if quo fits */ R[r1] = sr & DMASK16; /* store remainder */ R[r1p1] = st & DMASK16; /* store quotient */ } else if (PSW & PSW_AFI) /* div fault enabled? */ cc = swap_psw (AFIPSW, cc); /* swap PSW */ break; case 0x0E: /* ACHR - RR */ case 0x4E: /* ACH - RXH */ t = R[r1] + opnd + ((cc & CC_C) != 0); /* raw result */ rslt = t & DMASK16; /* masked result */ CC_GL_16 (rslt); /* set G,L */ if (t > DMASK16) /* set C if carry */ cc = cc | CC_C; if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; R[r1] = rslt; /* store result */ break; case 0x0F: /* SCHR - RR */ case 0x4F: /* SCH - RXH */ t = R[r1] - opnd - ((cc & CC_C) != 0); /* raw result */ rslt = t & DMASK16; /* masked result */ CC_GL_16 (rslt); /* set G,L */ if (t > DMASK16) /* set C if borrow */ cc = cc | CC_C; if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; R[r1] = rslt; /* store result */ break; /* Floating point instructions */ case 0x28: /* LER - NO */ case 0x38: /* LDR - NO */ case 0x68: /* LE - RX */ case 0x78: /* LD - RX */ cc = f_l (op, r1, r2, ea); /* load */ if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ cc = swap_psw (FPFPSW, cc); break; case 0x29: /* CER - NO */ case 0x39: /* CDR - NO */ case 0x69: /* CE - RX */ case 0x79: /* CD - RX */ cc = f_c (op, r1, r2, ea); /* compare */ break; case 0x2A: /* AER - NO */ case 0x2B: /* SER - NO */ case 0x3A: /* ADR - NO */ case 0x3B: /* SDR - NO */ case 0x6A: /* AE - RX */ case 0x6B: /* SE - RX */ case 0x7A: /* AD - RX */ case 0x7B: /* SD - RX */ cc = f_as (op, r1, r2, ea); /* add/sub */ if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ cc = swap_psw (FPFPSW, cc); break; case 0x2C: /* MER - NO */ case 0x3C: /* MDR - NO */ case 0x6C: /* ME - RX */ case 0x7C: /* MD - RX */ cc = f_m (op, r1, r2, ea); /* multiply */ if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ cc = swap_psw (FPFPSW, cc); break; case 0x2D: /* DER - NO */ case 0x3D: /* DDR - NO */ case 0x6D: /* DE - RX */ case 0x7D: /* DD - RX */ cc = f_d (op, r1, r2, ea); /* perform divide */ if ((cc & CC_V) && ((cc & CC_C) || /* V set, x/16 or */ ((PSW & PSW_FPF) && CPU_x16))) /* V & C set? */ cc = swap_psw (FPFPSW, cc); break; case 0x2E: /* FXR - NO */ case 0x3E: /* FXDR - NO */ cc = f_fix (op, r1, r2); /* cvt to integer */ break; case 0x2F: /* FLR - NO */ case 0x3F: /* FLDR - NO */ cc = f_flt (op, r1, r2); /* cvt to floating */ break; case 0x60: /* STE - RX */ t = ReadFReg (r1); /* get fp reg */ WriteF (ea, t, P); /* write */ break; case 0x70: /* STD - RX */ WriteF (ea, D[r1 >> 1].h, P); /* write hi */ WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, P); /* write lo */ break; case 0x71: /* STME - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ t = ReadFReg (r1); /* get fp reg */ WriteF (ea, t, P); /* write */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0x72: /* LME - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ t = ReadF (ea, P); /* get value */ WriteFReg (r1, t); /* write reg */ ea = (ea + 4) & VAMASK; /* incr mem addr */ } break; case 0x7E: /* STMD - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ WriteF (ea, D[r1 >> 1].h, P); /* write register */ WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, P); ea = (ea + 8) & VAMASK; /* incr mem addr */ } break; case 0x7F: /* LMD - RX */ for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ D[r1 >> 1].h = ReadF (ea, P); /* load register */ D[r1 >> 1].l = ReadF ((ea + 4) & VAMASK, P); ea = (ea + 8) & VAMASK; /* incr mem addr */ } break; /* Miscellaneous */ case 0xE1: /* SVC - RX */ PCQ_ENTRY; /* save PC */ WriteH (SVCAP, ea); /* save opnd */ WriteH (SVOPS, BUILD_PSW (cc)); /* save PS */ WriteH (SVOPC, PC); /* save PC */ PC = ReadH (SVNPC + r1 + r1); /* new PC */ cc = newPSW (ReadH (SVNPS)); /* new PS */ break; case 0xE2: /* SINT - RS */ dev = opnd & DEV_MAX; /* get dev */ cc = int_auto (dev, cc); /* auto intr */ int_eval (); /* re-eval intr */ break; case 0xC2: /* LPSW - RX */ PCQ_ENTRY; /* effective branch */ PC = ReadH ((ea + 2) & VAMASK); /* read PC */ cc = newPSW (ReadH (ea)); /* read PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0x95: /* EPSR - NO */ R[r1] = BUILD_PSW (cc); /* save PSW */ case 0x33: /* LPSR - NO */ cc = newPSW (R[r2]); /* load new PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0x73: /* LPS - RXH */ cc = newPSW (opnd); /* load new PSW */ if (PSW & PSW_SQI) /* test for q */ cc = testsysq (cc); break; case 0x64: /* ATL - RX */ case 0x65: /* ABL - RX */ cc = addtoq (ea, R[r1], op & 1); /* add to q */ break; case 0x66: /* RTL - RX */ case 0x67: /* RBL - RX */ cc = remfmq (ea, r1, op & 1); /* remove from q */ break; case 0x13: /* SETMR - RR */ case 0x53: /* SETM - RXH */ t = BUILD_PSW (cc); /* old PSW */ map = PSW_GETMAP (opnd); /* get new map */ switch (map) { /* case on map */ case 0x7: map = 0; /* use 1:1 map */ R[r1] = R[r1] ^ SIGN16; /* flip sign */ break; case 0x8: case 0x9: case 0xA: case 0xB: case 0xC: case 0xD: case 0xE: if (R[r1] & SIGN16) /* S1? clr map<0> */ map = map & ~0x8; else { map = 0; /* else 1:1 map */ R[r1] = R[r1] | SIGN16; /* set sign */ } break; default: break; } t = (t & ~PSW_MAP) | (map << PSW_V_MAP); /* insert map */ newPSW (t); /* load new PSW */ CC_GL_16 (R[r1]); /* set G,L */ break; /* I/O instructions */ case 0xDE: /* OC - RX */ opnd = ReadB (ea); /* fetch operand */ case 0x9E: /* OCR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { dev_tab[dev] (dev, IO_ADR, 0); /* select */ dev_tab[dev] (dev, IO_OC, opnd & DMASK8); /* send command */ int_eval (); /* re-eval intr */ cc = 0; } else cc = CC_V; break; case 0xDA: /* WD - RX */ opnd = ReadB (ea); /* fetch operand */ case 0x9A: /* WDR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { dev_tab[dev] (dev, IO_ADR, 0); /* select */ dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send data */ int_eval (); /* re-eval intr */ cc = 0; } else cc = CC_V; break; case 0xD8: /* WH - RX */ opnd = ReadH (ea); /* fetch operand */ case 0x98: /* WHR - RR */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { if (dev_tab[dev] (dev, IO_ADR, 0)) /* select; hw ok? */ dev_tab[dev] (dev, IO_WH, opnd); /* send data */ else { /* byte only */ dev_tab[dev] (dev, IO_WD, opnd >> 8); /* send hi byte */ dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send lo byte */ } int_eval (); /* re-eval intr */ cc = 0; } else cc = CC_V; break; case 0x9B: /* RDR - RR */ case 0xDB: /* RD - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ dev_tab[dev] (dev, IO_ADR, 0); /* select */ t = dev_tab[dev] (dev, IO_RD, 0); /* get data */ cc = 0; } else { /* no */ t = 0; /* read zero */ cc = CC_V; /* set V */ } if (OP_TYPE (op) != OP_RR) /* RX or RR? */ WriteB (ea, t); else R[r2] = t & DMASK8; int_eval (); /* re-eval intr */ break; case 0x99: /* RHR - RR */ case 0xD9: /* RH - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (dev_tab[dev] (dev, IO_ADR, 0)) /* select, hw ok? */ t = dev_tab[dev] (dev, IO_RH, 0); /* get data */ else { /* byte only */ rslt = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ t = (rslt << 8) | t; /* merge */ } cc = 0; } else { /* no */ t = 0; /* read zero */ cc = CC_V; /* set V */ } if (OP_TYPE (op) != OP_RR) /* RX or RR? */ WriteH (ea, t); else R[r2] = t; int_eval (); /* re-eval intr */ break; case 0x9F: /* AIR - RR */ case 0xDF: /* AI - RX */ R[r1] = int_getdev (); /* get int dev */ /* fall through */ case 0x9D: /* SSR - RR */ case 0xDD: /* SS - RX */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ dev_tab[dev] (dev, IO_ADR, 0); /* select */ t = dev_tab[dev] (dev, IO_SS, 0); /* get status */ } else t = STA_EX; /* no */ if (OP_TYPE (op) != OP_RR) /* RR or RX? */ WriteB (ea, t); else R[r2] = t & DMASK8; cc = t & 0xF; int_eval (); /* re-eval intr */ break; /* Block I/O instructions On a real Interdata system, the block I/O instructions can't be interrupted or stopped. To model this behavior, while allowing the instructions to go back through fetch for I/O processing and WRU testing, the simulator implements a 'block I/O in progress' flag and status block. If a block I/O is in progress, normal interrupts and fetches are suppressed until the block I/O is done. */ case 0x96: /* WBR - RR */ case 0xD6: /* WB - RXH */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (OP_TYPE (op) != OP_RR) lim = ReadH ((ea + 2) & VAMASK); else lim = R[(r2 + 1) & 0xF]; if (opnd > lim) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ blk_io.dfl = dev; /* set status block */ blk_io.cur = opnd; blk_io.end = lim; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; case 0x97: /* RBR - RR */ case 0xD7: /* RB - RXH */ dev = R[r1] & DEV_MAX; if (DEV_ACC (dev)) { /* dev exist? */ if (OP_TYPE (op) != OP_RR) lim = ReadH ((ea + 2) & VAMASK); else lim = R[(r2 + 1) & 0xF]; if (opnd > lim) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ blk_io.dfl = dev | BL_RD; /* set status block */ blk_io.cur = opnd; blk_io.end = lim; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; case 0xD5: /* AL - RX */ dev = ReadB (AL_DEV); /* get device */ t = ReadB (AL_IOC); /* get command */ if (DEV_ACC (dev)) { /* dev exist? */ if (AL_BUF > ea) /* start > end? */ cc = 0; else { /* no, start I/O */ dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ dev_tab[dev] (dev, IO_OC, t); /* start dev */ blk_io.dfl = dev | BL_RD | BL_LZ; /* set status block */ blk_io.cur = AL_BUF; blk_io.end = ea; qevent = qevent | EV_BLK; /* I/O in prog */ } } else cc = CC_V; /* nx dev */ break; } /* end switch */ } /* end while */ /* Simulation halted */ PSW = BUILD_PSW (cc); PC = PC & VAMASK; pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Load new PSW and memory map */ uint32 newPSW (uint32 val) { PSW = val & psw_mask; /* store PSW */ int_eval (); /* update intreq */ if (PSW & PSW_WAIT) /* wait state? */ qevent = qevent | EV_WAIT; else qevent = qevent & ~EV_WAIT; if (cpu_unit.flags & UNIT_816E) { /* mapping enabled? */ uint32 map = PSW_GETMAP (PSW); /* get new map */ s0_rel = s0_rel_const[map]; /* set relocation */ s1_rel = s1_rel_const[map]; /* constants */ } else s0_rel = s1_rel = 0; /* no relocation */ if (PSW & PSW_AIO) /* PSW<4> controls */ SET_ENB (v_DS); else CLR_ENB (v_DS); /* DS interrupts */ return PSW & CC_MASK; } /* Swap PSW */ uint32 swap_psw (uint32 loc, uint32 cc) { WriteH (loc, BUILD_PSW (cc)); /* write PSW, PC */ WriteH (loc + 2, PC); cc = newPSW (ReadH (loc + 4)); /* read PSW, PC */ PC = ReadH (loc + 6); if (PSW & PSW_SQI) /* sys q int enb? */ cc = testsysq (cc); return cc; /* return CC */ } /* Test for queue interrupts */ uint32 testsysq (uint32 cc) { int32 qb = ReadH (SQP); /* get sys q addr */ int32 usd = ReadB (qb + Q16_USD); /* get use count */ if (usd) { /* any entries? */ WriteH (SQIPSW, BUILD_PSW (cc)); /* swap PSW */ WriteH (SQIPSW + 2, PC); cc = newPSW (ReadH (SQIPSW + 4)); PC = ReadH (SQIPSW + 6); } return cc; } /* Add to head of queue */ uint32 addtoq (uint32 ea, uint32 val, uint32 flg) { uint32 slt, usd, wra, t; t = ReadH (ea); /* slots/used */ slt = (t >> 8) & DMASK8; /* # slots */ usd = t & DMASK8; /* # used */ if (usd >= slt) /* list full? */ return CC_V; usd = usd + 1; /* inc # used */ WriteB (ea + Q16_USD, usd); /* rewrite */ if (flg) { /* ABL? */ wra = ReadB ((ea + Q16_BOT) & VAMASK); /* get bottom */ t = wra + 1; /* adv bottom */ if (t >= slt) /* wrap if necc */ t = 0; WriteB ((ea + Q16_BOT) & VAMASK, t); /* rewrite bottom */ } else { /* ATL */ wra = ReadB ((ea + Q16_TOP) & VAMASK); /* get top */ if (wra == 0) /* wrap if necc */ wra = (slt - 1) & DMASK8; else wra = wra - 1; /* dec top */ WriteB ((ea + Q16_TOP) & VAMASK, wra); /* rewrite top */ } WriteH ((ea + Q16_BASE + (wra * Q16_SLNT)) & VAMASK, val); /* write slot */ return 0; } uint32 remfmq (uint32 ea, uint32 r1, uint32 flg) { uint32 slt, usd, rda, t; t = ReadH (ea); /* get slots/used */ slt = (t >> 8) & DMASK8; /* # slots */ usd = t & DMASK8; /* # used */ if (usd == 0) /* empty? */ return CC_V; usd = usd - 1; /* dec used */ WriteB (ea + Q16_USD, usd); /* rewrite */ if (flg) { /* RBL? */ rda = ReadB ((ea + Q16_BOT) & VAMASK); /* get bottom */ if (rda == 0) /* wrap if necc */ rda = (slt - 1) & DMASK8; else rda = rda - 1; /* dec bottom */ WriteB ((ea + Q16_BOT) & VAMASK, rda); /* rewrite bottom */ } else { rda = ReadB ((ea + Q16_TOP) & VAMASK); /* RTL, get top */ t = rda + 1; /* adv top */ if (t >= slt) /* wrap if necc */ t = 0; WriteB ((ea + Q16_TOP) & VAMASK, t); /* rewrite top */ } R[r1] = ReadH ((ea + Q16_BASE + (rda * Q16_SLNT)) & VAMASK); /* read slot */ if (usd) /* set cc's */ return CC_G; else return 0; } /* Automatic interrupt processing */ #define CCW16_ERR(x) (((x)|CCW16_INIT|CCW16_NOP|CCW16_Q) & \ ~(CCW16_CHN|CCW16_CON|CCW16_HI)) uint32 int_auto (uint32 dev, uint32 cc) { int32 ba, ea, by, vec, ccw, bpi, fnc, trm, st, i, t; t_bool sysqe = FALSE; t_bool rpt = FALSE; do { vec = ReadH (INTSVT + dev + dev); /* get vector */ if ((vec & 1) == 0) { /* immed int? */ WriteH (vec, BUILD_PSW (cc)); /* write PSW, PC */ WriteH ((vec + 2) & VAMASK, PC); cc = newPSW (ReadH ((vec + 4) & VAMASK)); /* read PSW */ PC = (vec + 6) & VAMASK; /* set new PC */ return cc; } vec = vec & ~1; /* get CCW addr */ ccw = ReadH (vec); /* read CCW */ if (DEV_ACC (dev)) /* select dev */ dev_tab[dev] (dev, IO_ADR, 0); if (ccw & CCW16_NOP) /* NOP? exit */ break; if (ccw & CCW16_INIT) { /* init set? */ ccw = ccw & ~CCW16_INIT; /* clr init */ WriteH (vec, ccw); /* rewrite */ if (ccw & CCW16_OC) { /* OC set? */ if (DEV_ACC (dev)) { /* dev exist? */ by = ReadB ((vec + CCB16_IOC) & VAMASK);/* read OC byte */ dev_tab[dev] (dev, IO_OC, by); /* send to dev */ } break; /* and exit */ } } fnc = CCW16_FNC (ccw); /* get func */ st = 0; /* default status */ if (fnc == CCW16_DMT) { /* DMT */ ba = ReadH ((vec + CCB16_STR) & VAMASK); /* get cnt wd */ ba = (ba - 1) & DMASK16; /* decr */ WriteH ((vec + CCB16_STR) & VAMASK, ba); /* rewrite */ if (ba) /* nz? exit */ break; } /* end if dmt */ else if (fnc != CCW16_NUL) { /* rd or wr? */ if (DEV_ACC (dev)) /* dev exist? */ st = dev_tab[dev] (dev, IO_SS, 0); /* sense status */ else st = CC_V; /* else timeout */ if (st & 0xF) { /* error? */ ccw = CCW16_ERR (ccw); /* neuter CCW */ WriteH (vec, ccw); /* rewrite CCW */ } else { /* ok, do xfer */ bpi = CCW16_BPI (ccw); /* get bytes/int */ if (bpi == 0) /* max 16B */ bpi = 16; ba = ReadH ((vec + CCB16_STR) & VAMASK); /* get start */ for (i = 0; i < bpi; i++) { /* do # bytes */ if (fnc == CCW16_RD) { /* chan read? */ by = dev_tab[dev] (dev, IO_RD, 0); /* read byte */ WriteB (ba, by); /* store */ } else { /* chan write */ by = ReadB (ba); /* fetch */ dev_tab[dev] (dev, IO_WD, by); /* write byte */ } ba = (ba + 1) & VAMASK; /* incr addr */ } WriteH ((vec + CCB16_STR) & VAMASK, ba); /* rewrite */ ea = ReadH ((vec + CCB16_END) & VAMASK); /* get end */ trm = ReadB ((vec + CCB16_TRM) & VAMASK); /* get term chr */ if ((ba <= ea) && /* not at end? */ (((ccw & CCW16_TRM) == 0) || /* not term chr? */ (by != trm))) /* exit */ break; ccw = ccw | CCW16_NOP; /* nop CCW */ WriteH (vec, ccw); /* rewrite CCW */ } /* end else sta */ } /* end if r/w */ /* Termination phase */ t = (dev << 8) | (st & DMASK8); /* form dev/sta */ WriteH ((vec + CCB16_DEV) & VAMASK, t); /* write dev/sta */ if (ccw & CCW16_Q) { /* q request? */ t = ReadH (SQP); /* get sys q addr */ if (addtoq (t, vec, ccw & CCW16_HI)) { /* add to sys q */ WriteH (SQOP, vec); /* write to ovflo */ return swap_psw (SQVPSW, cc); /* take exception */ } else sysqe = TRUE; /* made an entry */ } if (ccw & CCW16_CHN) { /* chain */ t = ReadH ((vec + CCB16_CHN) & VAMASK); /* get chain wd */ WriteH (INTSVT + dev + dev, t); /* wr int svc tab */ if (ccw & CCW16_CON) /* cont? */ rpt = TRUE; } } while (rpt); /* Common exit */ if (sysqe && (PSW & PSW_SQI)) /* sys q ent & enb? */ return swap_psw (SQIPSW, cc); /* take sys q int */ return cc; } /* Display register device */ uint32 display (uint32 dev, uint32 op, uint32 dat) { int t; switch (op) { case IO_ADR: /* select */ if (!drmod) /* norm mode? clr */ drpos = srpos = 0; return BY; /* byte only */ case IO_OC: /* command */ op = op & 0xC0; if (op == 0x40) { /* x40 = inc */ drmod = 1; drpos = srpos = 0; /* init cntrs */ } else if (op == 0x80) /* x80 = norm */ drmod = 0; break; case IO_WD: /* write */ if (drpos < 4) DR = (DR & ~(DMASK8 << (drpos * 8))) | (dat << (drpos * 8)); else if (drpos == 4) DRX = dat; drpos = (drpos + 1) & ((cpu_unit.flags & (UNIT_716 | UNIT_816))? 7: 3); break; case IO_RD: /* read */ t = (SR >> (srpos * 8)) & DMASK8; srpos = srpos ^ 1; return t; case IO_SS: /* status */ return 0x80; } return 0; } /* Memory interface routines ReadB read byte (processor) ReadH read halfword (processor) ReadF read fullword (processor) WriteB write byte (processor) WriteH write halfword (processor) WriteF write fullword (processor) IOReadB read byte (IO) IOWriteB write byte (IO) IOReadH read halfword (IO) IOWriteH write halfword (IO) */ uint32 ReadB (uint32 loc) { uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; return ((M[pa >> 1] >> ((pa & 1)? 0: 8)) & DMASK8); } uint32 ReadH (uint32 loc) { uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; return M[pa >> 1]; } uint32 ReadF (uint32 loc, uint32 rel) { uint32 pa, pa1; uint32 loc1 = (loc + 2) & VAMASK; loc = loc & VAMASK; /* FP doesn't mask */ if (rel) { pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; pa1 = (loc1 + ((loc1 & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; } else { pa = loc; pa1 = loc1; } return (((uint32) M[pa >> 1]) << 16) | ((uint32) M[pa1 >> 1]); } void WriteB (uint32 loc, uint32 val) { uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; val = val & DMASK8; if (MEM_ADDR_OK (pa)) M[pa >> 1] = ((pa & 1)? ((M[pa >> 1] & ~DMASK8) | val): ((M[pa >> 1] & DMASK8) | (val << 8))); return; } void WriteH (uint32 loc, uint32 val) { uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; if (MEM_ADDR_OK (pa)) M[pa >> 1] = val & DMASK16; return; } void WriteF (uint32 loc, uint32 val, uint32 rel) { uint32 pa, pa1; uint32 loc1 = (loc + 2) & VAMASK; loc = loc & VAMASK; /* FP doesn't mask */ if (rel) { pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; pa1 = (loc1 + ((loc1 & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; } else { pa = loc; pa1 = loc1; } if (MEM_ADDR_OK (pa)) M[pa >> 1] = (val >> 16) & DMASK16; if (MEM_ADDR_OK (pa1)) M[pa1 >> 1] = val & DMASK16; return; } uint32 IOReadB (uint32 loc) { return ((M[loc >> 1] >> ((loc & 1)? 0: 8)) & DMASK8); } void IOWriteB (uint32 loc, uint32 val) { val = val & DMASK8; M[loc >> 1] = ((loc & 1)? ((M[loc >> 1] & ~DMASK8) | val): ((M[loc >> 1] & DMASK8) | (val << 8))); return; } uint32 IOReadH (uint32 loc) { return (M[loc >> 1] & DMASK16); } void IOWriteH (uint32 loc, uint32 val) { M[loc >> 1] = val & DMASK16; return; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { qevent = 0; /* no events */ newPSW (0); /* PSW = 0 */ DR = 0; /* clr display */ drmod = 0; blk_io.dfl = blk_io.cur = blk_io.end = 0; /* no block IO */ sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init bkpts */ if (M == NULL) M = (uint16 *) calloc (MAXMEMSIZE16E >> 1, sizeof (uint16)); if (M == NULL) return SCPE_MEM; pcq_r = find_reg ("PCQ", NULL, dptr); /* init PCQ */ if (pcq_r) pcq_r->qptr = 0; else return SCPE_IERR; return SCPE_OK; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { if (sw & SWMASK ('V')) { if (addr > VAMASK) return SCPE_NXM; addr = (addr + ((addr & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; } if (addr >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = IOReadH (addr); return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if (sw & SWMASK ('V')) { if (addr > VAMASK) return SCPE_NXM; addr = (addr + ((addr & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; } if (addr >= MEMSIZE) return SCPE_NXM; IOWriteH (addr, val); 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 & 0xFFF) != 0) || (((uint32) val) > ((uptr->flags & UNIT_816E)? MAXMEMSIZE16E: MAXMEMSIZE16))) return SCPE_ARG; for (i = val; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1]; if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) return SCPE_OK; MEMSIZE = val; for (i = MEMSIZE; i < MAXMEMSIZE16E; i = i + 2) M[i >> 1] = 0; return SCPE_OK; } /* Change CPU model */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i; if (!(val & UNIT_816E) && (MEMSIZE > MAXMEMSIZE16)) { MEMSIZE = MAXMEMSIZE16; for (i = MEMSIZE; i < MAXMEMSIZE16E; i = i + 2) M[i >> 1] = 0; printf ("Reducing memory to 64KB\n"); } return SCPE_OK; } /* Set console interrupt */ t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc) { if ((uptr->flags & (UNIT_716 | UNIT_816 | UNIT_816E)) == 0) return SCPE_NOFNC; if (PSW & PSW_AIO) SET_INT (v_DS); return SCPE_OK; } /* Set history */ t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i, lnt; t_stat r; if (cptr == NULL) { for (i = 0; i < hst_lnt; i++) hst[i].vld = 0; hst_p = 0; return SCPE_OK; } lnt = (uint32) 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 op, k, di, lnt; char *cptr = (char *) desc; t_value sim_eval[2]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 r1 opnd ea IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(di++) % hst_lnt]; /* entry pointer */ if (h->vld) { /* instruction? */ fprintf (st, "%04X %04X %04X ", h->pc, h->r1, h->opnd); op = (h->ir1 >> 8) & 0xFF; if (OP_TYPE (op) >= OP_RX) fprintf (st, "%04X ", h->ea); else fprintf (st, " "); sim_eval[0] = h->ir1; sim_eval[1] = h->ir2; if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %04X", h->ir1); fputc ('\n', st); /* end line */ } /* end if instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/Interdata/id_pt.c0000644000175000017500000003757011112026546014031 0ustar vlmvlm/* id_pt.c: Interdata paper tape reader Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. pt paper tape reader and punch 25-Apr-03 RMS Revised for extended file support 10-Apr-03 RMS Fixed type problem in ptr service (from Mark Pizzolato) */ #include "id_defs.h" #include /* Device definitions */ #define PTR 0 /* unit subscripts */ #define PTP 1 #define STA_OVR 0x80 /* overrun */ #define STA_NMTN 0x10 /* no motion */ #define STA_MASK (STA_BSY | STA_OVR | STA_DU) /* static bits */ #define SET_EX (STA_OVR | STA_NMTN) /* set EX */ #define CMD_V_RUN 4 /* run/stop */ #define CMD_V_SLEW 2 /* slew/step */ #define CMD_V_RD 0 /* read/write */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; uint32 pt_run = 0, pt_slew = 0; /* ptr modes */ uint32 pt_rd = 1, pt_chp = 0; /* pt state */ uint32 pt_arm = 0; /* int arm */ uint32 pt_sta = STA_BSY; /* status */ uint32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ DEVICE pt_dev; uint32 pt (uint32 dev, uint32 op, uint32 dat); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat pt_boot (int32 unitno, DEVICE *dptr); t_stat pt_reset (DEVICE *dptr); /* PT data structures pt_dev PT device descriptor pt_unit PT unit descriptors pt_reg PT register list */ DIB pt_dib = { d_PT, -1, v_PT, NULL, &pt, NULL }; UNIT pt_unit[] = { { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }, { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } }; REG pt_reg[] = { { HRDATA (STA, pt_sta, 8) }, { HRDATA (RBUF, pt_unit[PTR].buf, 8) }, { DRDATA (RPOS, pt_unit[PTR].pos, T_ADDR_W), PV_LEFT }, { DRDATA (RTIME, pt_unit[PTR].wait, 24), PV_LEFT }, { FLDATA (RSTOP_IOE, ptr_stopioe, 0) }, { HRDATA (PBUF, pt_unit[PTP].buf, 8) }, { DRDATA (PPOS, pt_unit[PTP].pos, T_ADDR_W), PV_LEFT }, { DRDATA (PTIME, pt_unit[PTP].wait, 24), PV_LEFT }, { FLDATA (PSTOP_IOE, ptp_stopioe, 0) }, { FLDATA (IREQ, int_req[l_PT], i_PT) }, { FLDATA (IENB, int_enb[l_PT], i_PT) }, { FLDATA (IARM, pt_arm, 0) }, { FLDATA (RD, pt_rd, 0) }, { FLDATA (RUN, pt_run, 0) }, { FLDATA (SLEW, pt_slew, 0) }, { FLDATA (CHP, pt_chp, 0) }, { HRDATA (DEVNO, pt_dib.dno, 8), REG_HRO }, { NULL } }; MTAB pt_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "devno", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE pt_dev = { "PT", pt_unit, pt_reg, pt_mod, 2, 10, 31, 1, 16, 8, NULL, NULL, &pt_reset, &pt_boot, NULL, NULL, &pt_dib, DEV_DISABLE }; /* Paper tape: IO routine */ uint32 pt (uint32 dev, uint32 op, uint32 dat) { uint32 t, old_rd, old_run; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_OC: /* command */ old_rd = pt_rd; /* save curr rw */ old_run = pt_run; /* save curr run */ pt_arm = int_chg (v_PT, dat, pt_arm); /* upd int ctrl */ pt_rd = io_2b (dat, CMD_V_RD, pt_rd); /* upd read/wr */ if (old_rd != pt_rd) { /* rw change? */ pt_sta = pt_sta & ~STA_OVR; /* clr overrun */ if (sim_is_active (&pt_unit[pt_rd? PTR: PTP])) { pt_sta = pt_sta | STA_BSY; /* busy = 1 */ CLR_INT (v_PT); /* clear int */ } else { /* not active */ pt_sta = pt_sta & ~STA_BSY; /* busy = 0 */ if (pt_arm) /* no, set int */ SET_INT (v_PT); } } if (pt_rd) { /* reader? */ pt_run = io_2b (dat, CMD_V_RUN, pt_run); /* upd run/stop */ pt_slew = io_2b (dat, CMD_V_SLEW, pt_slew); /* upd slew/inc */ if (pt_run) { /* run set? */ if (old_run == 0) { /* run 0 -> 1? */ sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); pt_sta = pt_sta & ~STA_DU; /* clear eof */ } } else sim_cancel (&pt_unit[PTR]); /* clr, stop rdr */ } else pt_sta = pt_sta & ~STA_DU; /* punch, clr eof */ break; case IO_RD: /* read */ if (pt_run && !pt_slew) { /* incremental? */ sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); pt_sta = pt_sta & ~STA_DU; /* clr eof */ } pt_chp = 0; /* clr char pend */ if (pt_rd) /* set busy */ pt_sta = pt_sta | STA_BSY; return (pt_unit[PTR].buf & 0xFF); /* return char */ case IO_WD: /* write */ pt_unit[PTP].buf = dat & DMASK8; /* save char */ if (!pt_rd) /* set busy */ pt_sta = pt_sta | STA_BSY; sim_activate (&pt_unit[PTP], pt_unit[PTP].wait); break; case IO_SS: /* status */ t = pt_sta & STA_MASK; /* get status */ if (pt_rd && !pt_run && !sim_is_active (&pt_unit[PTR])) t = t | STA_NMTN; /* stopped? */ if ((pt_unit[pt_rd? PTR: PTP].flags & UNIT_ATT) == 0) t = t | STA_DU; /* offline? */ if (t & SET_EX) /* test for EX */ t = t | STA_EX; return t; } return 0; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); if (pt_rd) { /* read mode? */ pt_sta = pt_sta & ~STA_BSY; /* clear busy */ if (pt_arm) /* if armed, intr */ SET_INT (v_PT); if (pt_chp) /* overrun? */ pt_sta = pt_sta | STA_OVR; } pt_chp = 1; /* char pending */ if ((temp = getc (uptr->fileref)) == EOF) { /* error? */ if (feof (uptr->fileref)) { /* eof? */ pt_sta = pt_sta | STA_DU; /* set DU */ if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } uptr->buf = temp & DMASK8; /* store char */ uptr->pos = uptr->pos + 1; /* incr pos */ if (pt_slew) /* slew? continue */ sim_activate (uptr, uptr->wait); return SCPE_OK; } t_stat ptp_svc (UNIT *uptr) { if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (!pt_rd) { /* write mode? */ pt_sta = pt_sta & ~STA_BSY; /* clear busy */ if (pt_arm) /* if armed, intr */ SET_INT (v_PT); } if (putc (uptr->buf, uptr -> fileref) == EOF) { /* write char */ perror ("PTP I/O error"); clearerr (uptr -> fileref); return SCPE_IOERR; } uptr -> pos = uptr -> pos + 1; /* incr pos */ return SCPE_OK; } /* Reset routine */ t_stat pt_reset (DEVICE *dptr) { sim_cancel (&pt_unit[PTR]); /* deactivate units */ sim_cancel (&pt_unit[PTP]); pt_rd = 1; /* read */ pt_chp = pt_run = pt_slew = 0; /* stop, inc, disarm */ pt_sta = STA_BSY; /* buf empty */ CLR_INT (v_PT); /* clear int */ CLR_ENB (v_PT); /* disable int */ pt_arm = 0; /* disarm int */ return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 0x50 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) #define BOOT3_START 0x3E #define BOOT3_LEN (sizeof (boot_rom) / sizeof (uint8)) static uint8 boot_rom[] = { 0xD5, 0x00, 0x00, 0xCF, /* ST AL CF */ 0x43, 0x00, 0x00, 0x80 /* BR 80 */ }; static uint8 boot3_rom[] = { 0xC8, 0x20, 0x00, 0x80, /* ST LHI 2,80 */ 0xC8, 0x30, 0x00, 0x01, /* LHI 3,1 */ 0xC8, 0x40, 0x00, 0xCF, /* LHI 4,CF */ 0xD3, 0xA0, 0x00, 0x78, /* LB A,78 */ 0xDE, 0xA0, 0x00, 0x79, /* OC A,79 */ 0x9D, 0xAE, /* LP SSR A,E */ 0x42, 0xF0, 0x00, 0x52, /* BTC F,LP */ 0x9B, 0xAE, /* RDR A,E */ 0x08, 0xEE, /* LHR E,E */ 0x43, 0x30, 0x00, 0x52, /* BZ LP */ 0x43, 0x00, 0x00, 0x6C, /* BR STO */ 0x9D, 0xAE, /* LP1 SSR A,E */ 0x42, 0xF0, 0x00, 0x64, /* BTC F,LP1 */ 0x9B, 0xAE, /* RDR A,E */ 0xD2, 0xE2, 0x00, 0x00, /* STO STB E,0(2) */ 0xC1, 0x20, 0x00, 0x64, /* BXLE 2,LP1 */ 0x43, 0x00, 0x00, 0x80 /* BR 80 */ }; t_stat pt_boot (int32 unitno, DEVICE *dptr) { extern uint32 PC, dec_flgs; extern uint16 decrom[]; if (decrom[0xD5] & dec_flgs) /* AL defined? */ IOWriteBlk (BOOT3_START, BOOT3_LEN, boot3_rom); /* no, 50 seq */ else IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy AL boot */ IOWriteB (AL_DEV, pt_dib.dno); /* set dev no */ IOWriteB (AL_IOC, 0x99); /* set dev cmd */ IOWriteB (AL_SCH, 0); /* clr sch dev no */ PC = BOOT_START; return SCPE_OK; } /* Dump routine */ #define LOAD_START 0x80 #define LOAD_LO 0x8A #define LOAD_HI 0x8E #define LOAD_CS 0x93 #define LOAD_LEN (sizeof (load_rom) / sizeof (uint8)) #define LOAD_LDR 50 static uint8 load_rom[] = { 0x24, 0x21, /* BOOT LIS R2,1 */ 0x23, 0x03, /* BS BOOT */ 0x00, 0x00, /* 32b psw pointer */ 0x00, 0x00, /* 32b reg pointer */ 0xC8, 0x10, /* ST LHI R1,lo */ 0x00, 0x00, 0xC8, 0x30, /* LHI R3,hi */ 0x00, 0x00, 0xC8, 0x60, /* LHI R3,cs */ 0x00, 0x00, 0xD3, 0x40, /* LB R4,X'78' */ 0x00, 0x78, 0xDE, 0x40, /* OC R4,X'79' */ 0x00, 0x79, 0x9D, 0x45, /* LDR SSR R4,R5 */ 0x20, 0x91, /* BTBS 9,.-2 */ 0x9B, 0x45, /* RDR R4,R5 */ 0x08, 0x55, /* L(H)R R5,R5 */ 0x22, 0x34, /* BZS LDR */ 0xD2, 0x51, /* LOOP STB R5,0(R1) */ 0x00, 0x00, 0x07, 0x65, /* X(H)R R6,R5 */ 0x9A, 0x26, /* WDR R2,R6 */ 0x9D, 0x45, /* SSR R4,R5 */ 0x20, 0x91, /* BTBS 9,.-2 */ 0x9B, 0x45, /* RDR R4,R5 */ 0xC1, 0x10, /* BXLE R1,LOOP */ 0x00, 0xA6, 0x24, 0x78, /* LIS R7,8 */ 0x91, 0x7C, /* SLLS R7,12 */ 0x95, 0x57, /* EPSR R5,R7 */ 0x22, 0x03 /* BS .-6 */ }; t_stat pt_dump (FILE *of, char *cptr, char *fnam) { uint32 i, lo, hi, cs; char *tptr; extern DEVICE cpu_dev; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; tptr = get_range (NULL, cptr, &lo, &hi, cpu_dev.aradix, 0xFFFF, 0); if ((tptr == NULL) || (lo < INTSVT)) return SCPE_ARG; if (*tptr != 0) return SCPE_2MARG; for (i = lo, cs = 0; i <= hi; i++) cs = cs ^ IOReadB (i); IOWriteBlk (LOAD_START, LOAD_LEN, load_rom); IOWriteB (LOAD_LO, (lo >> 8) & 0xFF); IOWriteB (LOAD_LO + 1, lo & 0xFF); IOWriteB (LOAD_HI, (hi >> 8) & 0xFF); IOWriteB (LOAD_HI + 1, hi & 0xFF); IOWriteB (LOAD_CS, cs & 0xFF); for (i = 0; i < LOAD_LDR; i++) fputc (0, of); for (i = LOAD_START; i < (LOAD_START + LOAD_LEN); i++) fputc (IOReadB (i), of); for (i = 0; i < LOAD_LDR; i++) fputc (0, of); for (i = lo; i <= hi; i++) fputc (IOReadB (i), of); for (i = 0; i < LOAD_LDR; i++) fputc (0, of); return SCPE_OK; } simh-3.8.1/Interdata/id16_dboot.c0000644000175000017500000002121111107706754014657 0ustar vlmvlm/* id16_dboot.c: Interdata 16b simulator disk bootstrap Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 17-Jul-06 RMS Fixed transcription error */ #include "id_defs.h" #define DBOOT_BEG 0x1000 #define DBOOT_START 0x100e #define DBOOT_LEN (sizeof (dboot_rom) / sizeof (uint8)) /* Boot ROM: transcription of OS/16 MT2 ALO Direct Access Loader */ static uint8 dboot_rom[] = { 0xca, 0xf0, 0x00, 0x30, 0xc5, 0xf0, 0x00, 0x3a, 0x02, 0x8e, 0x26, 0xf7, 0x03, 0x0e, 0xd1, 0xc0, 0x00, 0x78, 0xd0, 0xc0, 0x13, 0xf6, 0x07, 0xdd, 0xc8, 0x10, 0x10, 0x00, 0xd3, 0xf0, 0x00, 0x7e, 0xc4, 0xf0, 0x00, 0x0f, 0x01, 0xe1, 0xd2, 0xf0, 0x12, 0xe2, 0xd3, 0xf0, 0x00, 0x7f, 0x90, 0xf4, 0x01, 0xe1, 0xd2, 0xf0, 0x12, 0xe3, 0xd3, 0xf0, 0x00, 0x7f, 0xc4, 0xf0, 0x00, 0x0f, 0x01, 0xe1, 0xd2, 0xf0, 0x12, 0xe4, 0xd3, 0x20, 0x00, 0x7d, 0xd3, 0x30, 0x00, 0x7c, 0xd3, 0x40, 0x00, 0x7a, 0xd3, 0x50, 0x00, 0x7b, 0xc8, 0x70, 0x12, 0xf6, 0xc8, 0x80, 0x13, 0xf5, 0x07, 0xaa, 0x07, 0xcc, 0x41, 0xe0, 0x11, 0x88, 0x48, 0xa0, 0x12, 0xfe, 0x48, 0xc0, 0x13, 0x00, 0x43, 0x00, 0x10, 0x98, 0xc8, 0x70, 0x12, 0xf6, 0x41, 0xe0, 0x11, 0x88, 0xc8, 0xe0, 0x12, 0xfa, 0x24, 0x15, 0xd3, 0x0e, 0x00, 0x24, 0xc3, 0x00, 0x00, 0x10, 0x21, 0x3f, 0xca, 0xe0, 0x00, 0x30, 0x27, 0x11, 0x20, 0x38, 0x48, 0xa0, 0x12, 0xf6, 0x48, 0xc0, 0x12, 0xf8, 0x42, 0x30, 0x10, 0x70, 0x08, 0xaa, 0x20, 0x33, 0x43, 0x00, 0x12, 0xb2, 0x90, 0x05, 0x42, 0x30, 0x10, 0x88, 0xc8, 0x60, 0x4f, 0x53, 0x45, 0x63, 0x00, 0x00, 0x20, 0x36, 0xc8, 0x60, 0x31, 0x36, 0x45, 0x6e, 0x00, 0x02, 0x20, 0x3b, 0x48, 0x6e, 0x00, 0x08, 0x45, 0x60, 0x12, 0xe2, 0x20, 0x35, 0xd3, 0x6e, 0x00, 0x0a, 0xd4, 0x60, 0x12, 0xe4, 0x20, 0x3a, 0x08, 0x0e, 0x07, 0x66, 0xca, 0x60, 0x20, 0x00, 0x23, 0x36, 0x40, 0x06, 0x00, 0x00, 0x45, 0x06, 0x00, 0x00, 0x22, 0x37, 0x48, 0xae, 0x00, 0x0c, 0x48, 0xce, 0x00, 0x0e, 0x48, 0x0e, 0x00, 0x10, 0x48, 0x1e, 0x00, 0x12, 0x0b, 0x1c, 0x0f, 0x0a, 0x07, 0xff, 0x26, 0x11, 0x0e, 0x0f, 0xed, 0x00, 0x00, 0x08, 0xcb, 0x60, 0x02, 0xbe, 0x08, 0x00, 0x23, 0x34, 0x08, 0x86, 0x08, 0x16, 0x23, 0x04, 0x05, 0x16, 0x22, 0x84, 0x08, 0x81, 0x07, 0x77, 0x27, 0x81, 0xc8, 0xd1, 0xee, 0xc0, 0xc8, 0xf0, 0x11, 0x40, 0x48, 0x0f, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x26, 0xf2, 0x26, 0x12, 0xc5, 0xf0, 0x13, 0xfe, 0x20, 0x88, 0x0a, 0xed, 0x40, 0xed, 0x12, 0xf4, 0x43, 0x0d, 0x11, 0x40, 0x41, 0xed, 0x11, 0x88, 0xd1, 0xed, 0x13, 0xf6, 0xd0, 0xe0, 0x00, 0x78, 0xd1, 0xed, 0x13, 0xfa, 0xd0, 0xe0, 0x00, 0x7c, 0x48, 0x10, 0x00, 0x62, 0x48, 0x6d, 0x12, 0xf4, 0xd1, 0xa6, 0x00, 0x00, 0xd0, 0xa1, 0x00, 0x30, 0xd1, 0xe6, 0x00, 0x0c, 0xd0, 0xe1, 0x00, 0x28, 0x43, 0x00, 0x00, 0x60, 0x07, 0x00, 0x07, 0xbb, 0x0b, 0xcf, 0x0f, 0xab, 0x21, 0x13, 0x26, 0x01, 0x22, 0x04, 0x0a, 0xcf, 0x0e, 0xab, 0x08, 0xac, 0x08, 0xc0, 0x03, 0x0e, 0xde, 0x2d, 0x12, 0x1e, 0xc5, 0x50, 0x00, 0x33, 0x42, 0x2d, 0x11, 0xec, 0xde, 0x3d, 0x12, 0x1e, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x42, 0x1d, 0x12, 0xb8, 0xc3, 0xf0, 0x00, 0x10, 0x20, 0x35, 0xd0, 0xad, 0x13, 0xea, 0xc8, 0xf0, 0x00, 0x30, 0x41, 0xed, 0x11, 0x70, 0x08, 0x9c, 0x08, 0xba, 0x48, 0xad, 0x13, 0xea, 0x48, 0xcd, 0x13, 0xee, 0xd1, 0xed, 0x13, 0xf2, 0xc5, 0xb0, 0x00, 0x18, 0x21, 0x82, 0x26, 0xb8, 0x98, 0x49, 0xde, 0x4d, 0x12, 0xe7, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x42, 0x7d, 0x12, 0xb8, 0x20, 0x83, 0x98, 0x27, 0x98, 0x28, 0x98, 0x49, 0x9a, 0x3b, 0x41, 0x6d, 0x12, 0x7a, 0x22, 0x0f, 0x9d, 0x4f, 0xc3, 0xf0, 0x00, 0x19, 0x42, 0x3d, 0x12, 0xb8, 0xd0, 0xad, 0x13, 0xea, 0xc8, 0xf5, 0xff, 0xcc, 0x0a, 0xff, 0x48, 0xff, 0x12, 0xec, 0x41, 0xed, 0x11, 0x70, 0x08, 0x9c, 0x08, 0xca, 0x07, 0xaa, 0xc8, 0xf5, 0xff, 0xcc, 0xd3, 0xff, 0x12, 0xe8, 0x41, 0xed, 0x11, 0x70, 0x40, 0xcd, 0x12, 0xf2, 0x08, 0xba, 0x48, 0xad, 0x13, 0xea, 0x48, 0xcd, 0x13, 0xee, 0xd1, 0xed, 0x13, 0xf2, 0xde, 0x4d, 0x12, 0x6e, 0x9d, 0x3f, 0x22, 0x21, 0x98, 0x49, 0xde, 0x4d, 0x12, 0xd0, 0x9d, 0x3f, 0x22, 0x21, 0xde, 0x4d, 0x12, 0xa2, 0x9d, 0x3f, 0x22, 0x21, 0xd8, 0x4d, 0x12, 0xf2, 0xde, 0x4d, 0x12, 0xd1, 0x9d, 0x3f, 0x22, 0x21, 0xde, 0x4d, 0x12, 0xe7, 0x9d, 0x3f, 0x22, 0x21, 0x9d, 0x4f, 0x20, 0x81, 0xc3, 0xf0, 0x00, 0x53, 0x42, 0x3d, 0x12, 0xb8, 0x48, 0xfd, 0x12, 0xf2, 0x91, 0xfa, 0x06, 0xf9, 0xc8, 0x6d, 0x12, 0x2c, 0x98, 0x27, 0x98, 0x28, 0x9a, 0x3b, 0x98, 0x3f, 0xde, 0x3d, 0x12, 0xe6, 0xde, 0x2d, 0x11, 0xaf, 0x9d, 0x2f, 0x20, 0x81, 0xde, 0x2d, 0x12, 0x1e, 0x99, 0x20, 0xde, 0x2d, 0x12, 0x1e, 0x9d, 0x3f, 0x22, 0x21, 0x42, 0x1d, 0x12, 0xbc, 0xc3, 0xf0, 0x00, 0x10, 0x03, 0x3e, 0x0b, 0x07, 0x26, 0x02, 0xc4, 0x00, 0xff, 0x00, 0x0a, 0x70, 0x26, 0x91, 0x07, 0xbb, 0x40, 0xbd, 0x12, 0xf2, 0x03, 0x06, 0x24, 0xf1, 0x24, 0x10, 0x23, 0x04, 0x08, 0x14, 0x23, 0x02, 0x08, 0x13, 0x24, 0x01, 0xde, 0x0d, 0x10, 0xdc, 0x9a, 0x0f, 0x9a, 0x01, 0xde, 0x0d, 0x12, 0xe5, 0xd1, 0xed, 0x13, 0xf6, 0xd0, 0xe0, 0x00, 0x78, 0xd1, 0xed, 0x13, 0xfa, 0xd0, 0xe0, 0x00, 0x7c, 0x91, 0x0f, 0x95, 0x10, 0x22, 0x01, 0x00, 0x00, 0x00, 0x80, 0xc1, 0xc2, 0x14, 0x40, 0x40, 0x00, 0x01, 0x90, 0x01, 0x40, 0x04, 0xc0, 0x00, 0x00, 0x00, 0x00 }; /* Lower memory setup 78 = binary input device address 79 = binary device input command 7A = disk device number 7B = device code 7C = disk controller address 7D = selector channel address 7E:7F = operating system extension (user specified) */ struct dboot_id { char *name; uint32 sw; uint32 cap; uint32 dtype; uint32 offset; uint32 adder; }; static struct dboot_id dboot_tab[] = { { "DP", 0, 2, 0x31, o_DP0, 0 }, { "DP", SWMASK ('F'), 9, 0x32, o_DP0, o_DPF }, { "DP", 0, 9, 0x33, o_DP0, 0 }, { "DM", 0, 64, 0x35, o_ID0, 0 }, { "DM", 0, 244, 0x36, o_ID0, 0 }, { NULL } }; t_stat id_dboot (int32 u, DEVICE *dptr) { extern DIB pt_dib, sch_dib; extern uint32 PC; uint32 i, typ, ctlno, off, add, cap, sch_dev; UNIT *uptr; DIB *ddib = (DIB *) dptr->ctxt; /* get disk DIB */ ctlno = ddib->dno; /* get ctrl devno */ sch_dev = sch_dib.dno + ddib->sch; /* sch dev # */ uptr = dptr->units + u; /* get capacity */ cap = uptr->capac >> 20; for (i = typ = 0; dboot_tab[i].name != NULL; i++) { if ((strcmp (dboot_tab[i].name, dptr->name) == 0) && (dboot_tab[i].cap == cap)) { typ = dboot_tab[i].dtype; off = dboot_tab[i].offset; add = dboot_tab[i].adder; break; } } if (typ == 0) return SCPE_NOFNC; IOWriteBlk (DBOOT_BEG, DBOOT_LEN, dboot_rom); /* copy boot */ IOWriteB (AL_DEV, pt_dib.dno); /* bin input dev */ IOWriteB (AL_IOC, 0x99); IOWriteB (AL_DSKU, ctlno + ((u + 1) * off) + add); /* disk param */ IOWriteB (AL_DSKT, typ); IOWriteB (AL_DSKC, ctlno); IOWriteB (AL_SCH, sch_dev); PC = DBOOT_START; return SCPE_OK; } simh-3.8.1/Interdata/id_defs.h0000644000175000017500000006011010515443310014314 0ustar vlmvlm/* id_defs.h: Interdata 16b/32b simulator definitions Copyright (c) 2000-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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. The author gratefully acknowledges the help of Carl Friend and Al Kossow, who provided key documents about the Interdata product line. 09-Mar-06 RMS Increased register sets to architectural limit 25-Jan-04 RMS Removed local logging support 22-Sep-03 RMS Added additional instruction decode types 21-Jun-03 RMS Changed subroutine argument for ARM compiler conflict 25-Apr-03 RMS Revised for extended file support 28-Feb-03 RMS Changed magtape device default to 0x85 */ #ifndef _ID_DEFS_H_ #define _ID_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_RSRV 1 /* undef instr */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_WAIT 4 /* wait */ #define STOP_VFU 5 /* runaway VFU */ /* Memory */ #define PAWIDTH16 16 #define PAWIDTH16E 18 #define PAWIDTH32 20 #define MAXMEMSIZE16 (1u << PAWIDTH16) /* max mem size, 16b */ #define MAXMEMSIZE16E (1u << PAWIDTH16E) /* max mem size, 16b E */ #define MAXMEMSIZE32 (1u << PAWIDTH32) /* max mem size, 32b */ #define PAMASK16 (MAXMEMSIZE16 - 1) /* phys mem mask */ #define PAMASK16E (MAXMEMSIZE16E - 1) #define PAMASK32 (MAXMEMSIZE32 - 1) #define MEMSIZE (cpu_unit.capac) /* act memory size */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* Single precision floating point registers */ #if defined (IFP_IN_MEM) #define ReadFReg(r) (fp_in_hwre? \ F[(r) >> 1]: ReadF (((r) << 1) & ~3, P)) #define WriteFReg(r,v) if (fp_in_hwre) F[(r) >> 1] = (v); \ else WriteF (((r) << 1) & ~3, (v), P) #else #define ReadFReg(r) (F[(r) >> 1]) #define WriteFReg(r,v) F[(r) >> 1] = (v) #endif /* Double precision floating point registers */ typedef struct { uint32 h; /* high 32b */ uint32 l; /* low 32b */ } dpr_t; /* Architectural constants */ #define VAMASK16 (0xFFFF) /* 16b virt addr */ #define VAMASK32 (0x000FFFFF) /* 32b virt addr */ #define SIGN8 0x80 /* 8b sign bit */ #define DMASK8 0xFF /* 8b data mask */ #define MMASK8 0x7F /* 8b magnitude mask */ #define SIGN16 0x8000 /* 16b sign bit */ #define DMASK16 0xFFFF /* 16b data mask */ #define MMASK16 0x7FFF /* 16b magnitude mask */ #define SIGN32 0x80000000 /* 32b sign bit */ #define DMASK32 0xFFFFFFFF /* 32b data mask */ #define MMASK32 0x7FFFFFFF /* 32b magn mask */ #define CC_C 0x8 /* carry */ #define CC_V 0x4 /* overflow */ #define CC_G 0x2 /* greater than */ #define CC_L 0x1 /* less than */ #define CC_MASK (CC_C | CC_V | CC_G | CC_L) #define PSW_WAIT 0x8000 /* wait */ #define PSW_EXI 0x4000 /* ext intr enable */ #define PSW_MCI 0x2000 /* machine check enable */ #define PSW_AFI 0x1000 /* arith fault enb */ #define PSW_AIO 0x0800 /* auto I/O int enable */ #define PSW_FPF 0x0400 /* flt fault enb, 16b */ #define PSW_REL 0x0400 /* reloc enb, 32b */ #define PSW_SQI 0x0200 /* sys q int enable */ #define PSW_PRO 0x0100 /* protect mode */ #define PSW_V_MAP 4 /* mem map, 16b */ #define PSW_M_MAP 0xF #define PSW_MAP (PSW_M_MAP << PSW_V_MAP) #define PSW_V_REG 4 /* reg set, 32b */ #define PSW_M_REG 0xF #define PSW_ID4 0xF40F /* I3, I4 PSW */ #define PSW_x16 0xFF0F /* 7/16, 8/16 PSW */ #define PSW_816E 0xFFFF /* 8/16E PSW */ #define PSW_x32 0xFFFF /* 7/32, 8/32 PSW */ #define MCKOPSW 0x20 /* mchk old PSW, 32b */ #define FPFPSW 0x28 /* flt fault PSW, 16b */ #define ILOPSW 0x30 /* ill op PSW */ #define MCKPSW 0x38 /* mach chk PSW */ #define EXIPSW 0x40 /* ext intr PSW, 16b */ #define AFIPSW 0x48 /* arith flt PSW */ #define SQP 0x80 /* system queue ptr */ #define SQIPSW 0x82 /* sys q int PSW, 16b */ #define SQOP 0x8A /* sys q ovf ptr, 16b */ #define SQVPSW 0x8C /* sys q ovf PSW, 16b */ #define SQTPSW 0x88 /* sys q int PSW, 32b */ #define MPRPSW 0x90 /* mprot int PSW, 32b */ #define SVCAP 0x94 /* svc arg ptr, 16b */ #define SVOPS 0x96 /* svc old PS, 16b */ #define SVOPC 0x98 /* svc old PC, 16b */ #define SVNPS32 0x98 /* svc new PS, 32b */ #define SVNPS 0x9A /* svc new PS, 16b */ #define SVNPC 0x9C /* svc new PC */ #define INTSVT 0xD0 /* int service table */ #define AL_DEV 0x78 /* autoload: dev */ #define AL_IOC 0x79 /* command */ #define AL_DSKU 0x7A /* disk unit */ #define AL_DSKT 0x7B /* disk type */ #define AL_DSKC 0x7C /* disk ctrl */ #define AL_SCH 0x7D /* sel chan */ #define AL_EXT 0x7E /* OS extension */ #define AL_BUF 0x80 /* buffer start */ #define Q16_SLT 0 /* list: # slots */ #define Q16_USD 1 /* # in use */ #define Q16_TOP 2 /* current top */ #define Q16_BOT 3 /* next bottom */ #define Q16_BASE 4 /* base of q */ #define Q16_SLNT 2 /* slot length */ #define Q32_SLT 0 /* list: # slots */ #define Q32_USD 2 /* # in use */ #define Q32_TOP 4 /* current top */ #define Q32_BOT 6 /* next bottom */ #define Q32_BASE 8 /* base of q */ #define Q32_SLNT 4 /* slot length */ /* CPU event flags */ #define EV_MAC 0x01 /* MAC interrupt */ #define EV_BLK 0x02 /* block I/O in prog */ #define EV_INT 0x04 /* interrupt pending */ #define EV_WAIT 0x08 /* wait state pending */ /* Block I/O state */ struct BlockIO { uint32 dfl; /* devno, flags */ uint32 cur; /* current addr */ uint32 end; /* end addr */ }; #define BL_RD 0x8000 /* block read */ #define BL_LZ 0x4000 /* skip 0's */ /* Instruction decode ROM, for all, 16b, 32b */ #define OP_UNDEF 0x0000 /* undefined */ #define OP_NO 0x0001 /* all: short or fp rr */ #define OP_RR 0x0002 /* all: reg-reg */ #define OP_RS 0x0003 /* 16b: reg-storage */ #define OP_RI1 0x0003 /* 32b: reg-imm 16b */ #define OP_RX 0x0004 /* all: reg-mem */ #define OP_RXB 0x0005 /* all: reg-mem, rd BY */ #define OP_RXH 0x0006 /* all: reg-mem, rd HW */ #define OP_RXF 0x0007 /* 32b: reg-mem, rd FW */ #define OP_RI2 0x0008 /* 32b: reg-imm 32b */ #define OP_MASK 0x000F #define OP_ID4 0x0010 /* 16b: ID4 */ #define OP_716 0x0020 /* 16b: 7/16 */ #define OP_816 0x0040 /* 16b: 8/16 */ #define OP_816E 0x0080 /* 16b: 8/16E */ #define OP_DPF 0x4000 /* all: hwre FP */ #define OP_PRV 0x8000 /* all: privileged */ #define OP_TYPE(x) (decrom[(x)] & OP_MASK) #define OP_DPFP(x) (decrom[(x)] & OP_DPF) /* Device information block */ typedef struct { uint32 dno; /* device number */ int32 sch; /* sch */ uint32 irq; /* interrupt */ uint8 *tplte; /* template */ uint32 (*iot)(uint32 d, uint32 o, uint32 dat); void (*ini)(t_bool f); } DIB; #define TPL_END 0xFF /* template end */ /* Device select return codes */ #define BY 0 /* 8b only */ #define HW 1 /* 8b/16b */ /* I/O operations */ #define IO_ADR 0x0 /* address select */ #define IO_RD 0x1 /* read byte */ #define IO_RH 0x2 /* read halfword */ #define IO_WD 0x3 /* write byte */ #define IO_WH 0x4 /* write halfword */ #define IO_OC 0x5 /* output command */ #define IO_SS 0x6 /* sense status */ /* Device command byte */ #define CMD_V_INT 6 /* interrupt control */ #define CMD_M_INT 0x3 #define CMD_IENB 1 /* enable */ #define CMD_IDIS 2 /* disable */ #define CMD_IDSA 3 /* disarm */ #define CMD_GETINT(x) (((x) >> CMD_V_INT) & CMD_M_INT) /* Device status byte */ #define STA_BSY 0x8 /* busy */ #define STA_EX 0x4 /* examine status */ #define STA_EOM 0x2 /* end of medium */ #define STA_DU 0x1 /* device unavailable */ /* Default device numbers */ #define DEV_LOW 0x01 /* lowest intr dev */ #define DEV_MAX 0xFF /* highest intr dev */ #define DEVNO (DEV_MAX + 1) /* number of devices */ #define d_DS 0x01 /* display, switches */ #define d_TT 0x02 /* teletype */ #define d_PT 0x03 /* reader */ #define d_CD 0x04 /* card reader */ #define d_TTP 0x10 /* PAS as console */ #define d_PAS 0x10 /* first PAS */ #define o_PASX 0x01 /* offset to xmt */ #define d_LPT 0x62 /* line printer */ #define d_PIC 0x6C /* interval timer */ #define d_LFC 0x6D /* line freq clk */ #define d_MT 0x85 /* magtape */ #define o_MT0 0x10 #define d_DPC 0xB6 /* disk controller */ #define o_DP0 0x10 #define o_DPF 0x01 /* offset to fixed */ #define d_FD 0xC1 /* floppy disk */ #define d_SCH 0xF0 /* selector chan */ #define d_IDC 0xFB /* MSM disk ctrl */ #define o_ID0 0x01 /* Interrupts To make interrupt flags independent of device numbers, each device is assigned an interrupt flag in one of four interrupt words word 0 DMA devices word 1 programmed I/O devices word 2-3 PAS devices Devices are identified by a level and a bit within a level. Priorities run low to high in the array, right to left within words */ #define INTSZ 4 /* interrupt words */ #define SCH_NUMCH 4 /* #channels */ #define ID_NUMDR 4 /* # MSM drives */ #define DP_NUMDR 4 /* # DPC drives */ #define MT_NUMDR 4 /* # MT drives */ /* Word 0, DMA devices */ #define i_SCH 0 /* highest priority */ #define i_IDC (i_SCH + SCH_NUMCH) /* MSM disk ctrl */ #define i_DPC (i_IDC + ID_NUMDR + 1) /* cartridge disk ctrl */ #define i_MT (i_DPC + DP_NUMDR + 1) /* magtape */ #define l_SCH 0 #define l_IDC 0 #define l_DPC 0 #define l_MT 0 #define v_SCH (l_SCH * 32) + i_SCH #define v_IDC (l_IDC * 32) + i_IDC #define v_DPC (l_DPC * 32) + i_DPC #define v_MT (l_MT * 32) + i_MT /* Word 1, programmed I/O devices */ #define i_PIC 0 /* precision clock */ #define i_LFC 1 /* line clock */ #define i_FD 2 /* floppy disk */ #define i_CD 3 /* card reader */ #define i_LPT 4 /* line printer */ #define i_PT 5 /* paper tape */ #define i_TT 6 /* teletype */ #define i_DS 7 /* display */ #define i_TTP 10 /* PAS console */ #define l_PIC 1 #define l_LFC 1 #define l_FD 1 #define l_CD 1 #define l_LPT 1 #define l_PT 1 #define l_TT 1 #define l_DS 1 #define l_TTP 1 #define v_PIC (l_PIC * 32) + i_PIC #define v_LFC (l_LFC * 32) + i_LFC #define v_FD (l_FD * 32) + i_FD #define v_CD (l_CD * 32) + i_CD #define v_LPT (l_LPT * 32) + i_LPT #define v_PT (l_PT * 32) + i_PT #define v_TT (l_TT * 32) + i_TT #define v_DS (l_DS * 32) + i_DS #define v_TTP (l_TTP * 32) + i_TTP /* Word 2-3, PAS devices */ #define i_PAS 0 #define l_PAS 2 #define v_PAS (l_PAS * 32) + i_PAS #define v_PASX (v_PAS + 1) /* offset to xmt */ /* I/O macros */ #define SET_INT(v) int_req[(v) >> 5] = int_req[(v) >> 5] | (1u << ((v) & 0x1F)) #define CLR_INT(v) int_req[(v) >> 5] = int_req[(v) >> 5] & ~(1u << ((v) & 0x1F)) #define SET_ENB(v) int_enb[(v) >> 5] = int_enb[(v) >> 5] | (1u << ((v) & 0x1F)) #define CLR_ENB(v) int_enb[(v) >> 5] = int_enb[(v) >> 5] & ~(1u << ((v) & 0x1F)) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* Device accessible macro */ #define DEV_ACC(d) (dev_tab[d] && !sch_blk (d)) /* Automatic I/O channel programs, 16b */ #define CCB16_CHN -4 /* chain */ #define CCB16_DEV -2 /* dev no */ #define CCB16_STS -1 /* status */ #define CCB16_CCW 0 /* cmd wd */ #define CCB16_STR 2 /* start */ #define CCB16_END 4 /* end */ #define CCB16_IOC 6 /* OC byte */ #define CCB16_TRM 7 /* term byte */ #define CCW16_INIT 0x8000 /* init */ #define CCW16_NOP 0x4000 /* nop */ #define CCW16_V_FNC 12 /* function */ #define CCW16_M_FNC 0x3 #define CCW16_FNC(x) (((x) >> CCW16_V_FNC) & CCW16_M_FNC) #define CCW16_RD 0 /* read */ #define CCW16_WR 1 /* write */ #define CCW16_DMT 2 /* dec mem */ #define CCW16_NUL 3 /* null */ #define CCW16_TRM 0x0400 /* term char */ #define CCW16_Q 0x0200 /* queue */ #define CCW16_HI 0x0100 /* queue hi */ #define CCW16_OC 0x0080 /* OC */ #define CCW16_CHN 0x0020 /* chain */ #define CCW16_CON 0x0010 /* continue */ #define CCW16_V_BPI 0 /* bytes per int */ #define CCW16_M_BPI 0xF #define CCW16_BPI(x) (((x) >> CCW16_V_BPI) & CCW16_M_BPI) /* Automatic I/O channel programs, 32b */ #define CCB32_CCW 0 /* cmd wd */ #define CCB32_B0C 2 /* buf 0 cnt */ #define CCB32_B0E 4 /* buf 0 end */ #define CCB32_CHK 8 /* check word */ #define CCB32_B1C 10 /* buf 1 cnt */ #define CCB32_B1E 12 /* buf 1 end */ #define CCB32_TAB 16 /* trans table */ #define CCB32_SUB 20 /* subroutine */ #define CCW32_V_STA 8 /* status */ #define CCW32_M_STA 0xFF #define CCW32_STA(x) (((x) >> CCW32_V_STA) & CCW32_M_STA) #define CCW32_EXE 0x80 /* execute */ #define CCW32_CRC 0x10 #define CCW32_B1 0x08 /* buffer 1 */ #define CCW32_WR 0x04 /* write */ #define CCW32_TL 0x02 /* translate */ #define CCW32_FST 0x01 /* fast mode */ /* MAC, 32b */ #define P 0 /* physical */ #define VE 1 /* virtual inst */ #define VR 2 /* virtual read */ #define VW 3 /* virtual write */ #define MAC_BASE 0x300 /* MAC base */ #define MAC_STA 0x340 /* MAC status */ #define MAC_LNT 16 #define VA_V_OFF 0 /* offset */ #define VA_M_OFF 0xFFFF #define VA_GETOFF(x) (((x) >> VA_V_OFF) & VA_M_OFF) #define VA_V_SEG 16 /* segment */ #define VA_M_SEG 0xF #define VA_GETSEG(x) (((x) >> VA_V_SEG) & VA_M_SEG) #define SRF_MASK 0x000FFF00 /* base mask */ #define SRL_MASK 0x0FF00000 /* limit mask */ #define GET_SRL(x) ((((x) & SRL_MASK) >> 12) + 0x100) #define SR_EXP 0x80 /* execute prot */ #define SR_WPI 0x40 /* wr prot int */ #define SR_WRP 0x20 /* wr prot */ #define SR_PRS 0x10 /* present */ #define SR_MASK (SRF_MASK|SRL_MASK|SR_EXP|SR_WPI|SR_WRP|SR_PRS) #define MACS_L 0x10 /* limit viol */ #define MACS_NP 0x08 /* not present */ #define MACS_WP 0x04 /* write prot */ #define MACS_WI 0x02 /* write int */ #define MACS_EX 0x01 /* exec prot */ /* Miscellaneous */ #define TMR_LFC 0 /* LFC = timer 0 */ #define TMR_PIC 1 /* PIC = timer 1 */ #define LPT_WIDTH 132 #define VFU_LNT 132 #define MIN(x,y) (((x) < (y))? (x): (y)) #define MAX(x,y) (((x) > (y))? (x): (y)) /* Function prototypes */ int32 int_chg (uint32 irq, int32 dat, int32 armdis); int32 io_2b (int32 val, int32 pos, int32 old); uint32 IOReadB (uint32 loc); void IOWriteB (uint32 loc, uint32 val); uint32 IOReadH (uint32 loc); void IOWriteH (uint32 loc, uint32 val); uint32 ReadF (uint32 loc, uint32 rel); void WriteF (uint32 loc, uint32 val, uint32 rel); uint32 IOReadBlk (uint32 loc, uint32 cnt, uint8 *buf); uint32 IOWriteBlk (uint32 loc, uint32 cnt, uint8 *buf); void sch_adr (uint32 ch, uint32 dev); t_bool sch_actv (uint32 sch, uint32 devno); void sch_stop (uint32 sch); uint32 sch_wrmem (uint32 sch, uint8 *buf, uint32 cnt); uint32 sch_rdmem (uint32 sch, uint8 *buf, uint32 cnt); t_stat set_sch (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_sch (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/Interdata/id32_sys.c0000644000175000017500000006575311112027352014371 0ustar vlmvlm/* id32_sys.c: Interdata 32b simulator interface Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices 25-Jan-07 RMS Fixed conflict between -h (hex) and -h (halfword) 18-Oct-06 RMS Re-ordered device list 02-Jul-04 RMS Fixed missing type in declaration 15-Jul-03 RMS Fixed signed/unsigned bug in get_imm 27-Feb-03 RMS Added relative addressing support 23-Dec-01 RMS Cloned from ID4 sources */ #include "id_defs.h" #include #define MSK_SBF 0x0100 #define SEXT15(x) (((x) & 0x4000)? ((x) | ~0x3FFF): ((x) & 0x3FFF)) extern DEVICE cpu_dev; extern DEVICE sch_dev; extern DEVICE pt_dev; extern DEVICE tt_dev, ttp_dev; extern DEVICE pas_dev, pasl_dev; extern DEVICE lpt_dev; extern DEVICE pic_dev, lfc_dev; extern DEVICE dp_dev, idc_dev; extern DEVICE fd_dev, mt_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint32 *M; t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val); t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val); extern t_stat lp_load (FILE *fileref, char *cptr, char *fnam); extern t_stat pt_dump (FILE *of, char *cptr, char *fnam); /* 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[] = "Interdata 32b"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 6; DEVICE *sim_devices[] = { &cpu_dev, &sch_dev, &pic_dev, &lfc_dev, &pt_dev, &tt_dev, &ttp_dev, &pas_dev, &pasl_dev, &lpt_dev, &dp_dev, &idc_dev, &fd_dev, &mt_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Reserved instruction", "HALT instruction", "Breakpoint", "Wait state", "Runaway VFU" }; /* Binary loader -- load carriage control tape Binary dump -- paper tape dump */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { if (flag) return pt_dump (fileref, cptr, fnam); return lp_load (fileref, cptr, fnam); } /* Symbol tables */ #define I_V_FL 16 /* class bits */ #define I_M_FL 0xF /* class mask */ #define I_V_MR 0x0 /* mask-register */ #define I_V_RR 0x1 /* register-register */ #define I_V_R 0x2 /* register */ #define I_V_MX 0x3 /* mask-memory */ #define I_V_RX 0x4 /* register-memory */ #define I_V_X 0x5 /* memory */ #define I_V_FF 0x6 /* float reg-reg */ #define I_V_FX 0x7 /* float reg-mem */ #define I_V_SI 0x8 /* short immed */ #define I_V_SB 0x9 /* short branch */ #define I_V_SX 0xA /* short ext branch */ #define I_V_RI 0xB /* halfword imm */ #define I_V_RF 0xC /* fullword imm */ #define I_MR (I_V_MR << I_V_FL) #define I_RR (I_V_RR << I_V_FL) #define I_R (I_V_R << I_V_FL) #define I_MX (I_V_MX << I_V_FL) #define I_RX (I_V_RX << I_V_FL) #define I_X (I_V_X << I_V_FL) #define I_FF (I_V_FF << I_V_FL) #define I_FX (I_V_FX << I_V_FL) #define I_SI (I_V_SI << I_V_FL) #define I_SB (I_V_SB << I_V_FL) #define I_SX (I_V_SX << I_V_FL) #define I_RI (I_V_RI << I_V_FL) #define I_RF (I_V_RF << I_V_FL) #define R_X 0 /* no R1 */ #define R_M 1 /* R1 mask */ #define R_R 2 /* R1 int reg */ #define R_F 3 /* R1 flt reg */ static const int32 masks[] = { 0xFF00, 0xFF00, 0xFFF0, 0xFF00, 0xFF00, 0xFFF0, 0xFF00, 0xFF00, 0xFF00, 0xFE00, 0xFEF0, 0xFF00, 0xFF00 }; static const uint32 r1_type[] = { R_M, R_R, R_X, R_M, R_R, R_X, R_F, R_F, R_R, R_M, R_X, R_R, R_R }; static const uint32 r2_type[] = { R_X, R_R, R_R, R_X, R_X, R_X, R_F, R_X, R_M, R_X, R_X, R_X, R_X }; static const char *opcode[] = { "BER", "BNER","BZR", "BNZR", "BPR", "BNPR","BLR", "BNLR", "BMR", "BNMR","BOR", "BNOR", "BCR", "BNCR","BR", "BES", "BNES","BZS", "BNZS", "BPS", "BNPS","BLS", "BNLS", "BMS", "BNMS","BOS", "BNOS", "BCS", "BNCS","BS", "BE", "BNE", "BZ", "BNZ", "BP", "BNP", "BL", "BNL", "BM", "BNM", "BO", "BNO", "BC", "BNC", "B", "BALR","BTCR","BFCR", "NR", "CLR", "OR", "XR", "LR", "CHR", "AR", "SR", "MHR", "DHR", "SRLS","SLLS","CHVR", "LPSWR", "MR", "DR", "BTBS","BTFS","BFBS","BFFS", "LIS", "LCS", "AIS", "SIS", "LER", "CER", "AER", "SER", "MER", "DER", "FXR", "FLR", "MPBSR", "PBR", "EXHR", "LDR", "CDR", "ADR", "SDR", "MDR", "DDR", "FXDR","FLDR", "STH", "BAL", "BTC", "BFC", "NH", "CLH", "OH", "XH", "LH", "CH", "AH", "SH", "MH", "DH", "ST", "AM", "N", "CL", "O", "X", "L", "C", "A", "S", "M", "D", "CRC12","CRC16", "STE", "AHM", "PB", "LRA", "ATL", "ABL", "RTL", "RBL", "LE", "CE", "AE", "SE", "ME", "DE", "STD", "STME","LME", "LHL", "TBT", "SBT", "RBT", "CBT", "LD", "CD", "AD", "SD", "MD", "DD", "STMD","LMD", "SRHLS","SLHLS","STBR","LBR", "EXBR","EPSR","WBR", "RBR", "WHR", "RHR", "WDR", "RDR", "SSR", "OCR", "BXH", "BXLE","LPSW","THI", "NHI", "CLHI","OHI", "XHI", "LHI", "CHI", "AHI", "SHI", "SRHL","SLHL","SRHA","SLHA", "STM", "LM", "STB", "LB", "CLB", "AL", "WB", "RB", "WH", "RH", "WD", "RD", "SS", "OC", "TS", "SVC", "SINT","SCP", "LA", "TLATE", "RRL", "RLL", "SRL", "SLL", "SRA", "SLA", "TI", "NI", "CLI", "OI", "XI", "LI", "CI", "AI", "SI", NULL }; static const uint32 opc_val[] = { 0x0330+I_R, 0x0230+I_R, 0x0330+I_R, 0x0230+I_R, 0x0220+I_R, 0x0320+I_R, 0x0280+I_R, 0x0380+I_R, 0x0210+I_R, 0x0310+I_R, 0x0240+I_R, 0x0340+I_R, 0x0280+I_R, 0x0380+I_R, 0x0300+I_R, 0x2230+I_SX, 0x2030+I_SX, 0x2230+I_SX, 0x2030+I_SX, 0x2020+I_SX, 0x2220+I_SX, 0x2080+I_SX, 0x2280+I_SX, 0x2010+I_SX, 0x2210+I_SX, 0x2040+I_SX, 0x2240+I_SX, 0x2080+I_SX, 0x2280+I_SX, 0x2200+I_SX, 0x4330+I_X, 0x4230+I_X, 0x4330+I_X, 0x4230+I_X, 0x4220+I_X, 0x4320+I_X, 0x4280+I_X, 0x4380+I_X, 0x4210+I_X, 0x4310+I_X, 0x4240+I_X, 0x4340+I_X, 0x4280+I_X, 0x4380+I_X, 0x4300+I_X, 0x0100+I_RR, 0x0200+I_MR, 0x0300+I_MR, 0x0400+I_RR, 0x0500+I_RR, 0x0600+I_RR, 0x0700+I_RR, 0x0800+I_RR, 0x0900+I_RR, 0x0A00+I_RR, 0x0B00+I_RR, 0x0C00+I_RR, 0x0D00+I_RR, 0x1000+I_SI, 0x1100+I_SI, 0x1200+I_RR, 0x1800+I_RR, 0x1C00+I_RR, 0x1D00+I_RR, 0x2000+I_SB, 0x2100+I_SB, 0x2200+I_SB, 0x2300+I_SB, 0x2400+I_SI, 0x2500+I_SI, 0x2600+I_SI, 0x2700+I_SI, 0x2800+I_FF, 0x2900+I_FF, 0x2A00+I_FF, 0x2B00+I_FF, 0x2C00+I_FF, 0x2D00+I_FF, 0x2E00+I_RR, 0x2F00+I_RR, 0x3000+I_RR, 0x3200+I_RR, 0x3400+I_RR, 0x3800+I_FF, 0x3900+I_FF, 0x3A00+I_FF, 0x3B00+I_FF, 0x3C00+I_FF, 0x3D00+I_FF, 0x3E00+I_RR, 0x3F00+I_RR, 0x4000+I_RX, 0x4100+I_RX, 0x4200+I_MX, 0x4300+I_MX, 0x4400+I_RX, 0x4500+I_RX, 0x4600+I_RX, 0x4700+I_RX, 0x4800+I_RX, 0x4900+I_RX, 0x4A00+I_RX, 0x4B00+I_RX, 0x4C00+I_RX, 0x4D00+I_RX, 0x5000+I_RX, 0x5100+I_RX, 0x5400+I_RX, 0x5500+I_RX, 0x5600+I_RX, 0x5700+I_RX, 0x5800+I_RX, 0x5900+I_RX, 0x5A00+I_RX, 0x5B00+I_RX, 0x5C00+I_RX, 0x5D00+I_RX, 0x5E00+I_RX, 0x5F00+I_RX, 0x6000+I_RX, 0x6100+I_RX, 0x6200+I_RX, 0x6300+I_RX, 0x6400+I_RX, 0x6500+I_RX, 0x6600+I_RX, 0x6700+I_RX, 0x6800+I_FX, 0x6900+I_FX, 0x6A00+I_FX, 0x6B00+I_FX, 0x6C00+I_FX, 0x6D00+I_FX, 0x7000+I_FX, 0x7100+I_FX, 0x7200+I_FX, 0x7300+I_RX, 0x7400+I_RX, 0x7500+I_RX, 0x7600+I_RX, 0x7700+I_RX, 0x7800+I_FX, 0x7900+I_FX, 0x7A00+I_FX, 0x7B00+I_FX, 0x7C00+I_FX, 0x7D00+I_FX, 0x7E00+I_FX, 0x7F00+I_FX, 0x9000+I_SI, 0x9100+I_SI, 0x9200+I_RR, 0x9300+I_RR, 0x9400+I_RR, 0x9500+I_RR, 0x9600+I_RR, 0x9700+I_RR, 0x9800+I_RR, 0x9900+I_RR, 0x9A00+I_RR, 0x9B00+I_RR, 0x9D00+I_RR, 0x9E00+I_RR, 0xC000+I_RX, 0xC100+I_RX, 0xC200+I_RX, 0xC300+I_RI, 0xC400+I_RI, 0xC500+I_RI, 0xC600+I_RI, 0xC700+I_RI, 0xC800+I_RI, 0xC900+I_RI, 0xCA00+I_RI, 0xCB00+I_RI, 0xCC00+I_RI, 0xCD00+I_RI, 0xCE00+I_RI, 0xCF00+I_RI, 0xD000+I_RX, 0xD100+I_RX, 0xD200+I_RX, 0xD300+I_RX, 0xD400+I_RX, 0xD500+I_X, 0xD600+I_RX, 0xD700+I_RX, 0xD800+I_RX, 0xD900+I_RX, 0xDA00+I_RX, 0xDB00+I_RX, 0xDD00+I_RX, 0xDE00+I_RX, 0xE000+I_RX, 0xE100+I_RX, 0xE200+I_RI, 0xE300+I_RX, 0xE600+I_RX, 0xE700+I_RX, 0xEA00+I_RI, 0xEB00+I_RI, 0xEC00+I_RI, 0xED00+I_RI, 0xEE00+I_RI, 0xEF00+I_RI, 0xF300+I_RF, 0xF400+I_RF, 0xF500+I_RF, 0xF600+I_RF, 0xF700+I_RF, 0xF800+I_RF, 0xF900+I_RF, 0xFA00+I_RF, 0xFB00+I_RF, 0xFFFF }; /* Print an RX specifier */ t_stat fprint_addr (FILE *of, t_addr addr, uint32 rx, uint32 ea1, uint32 ea2) { uint32 rx2; if ((ea1 & 0xC000) == 0) { /* RX1 */ fprintf (of, "%-X", ea1); if (rx) fprintf (of, "(R%d)", rx); return -3; } if (ea1 & 0x8000) { /* RX2 */ ea1 = addr + 4 + SEXT15 (ea1); fprintf (of, "%-X", ea1 & VAMASK32); if (rx) fprintf (of, "(R%d)", rx); return -3; } rx2 = (ea1 >> 8) & 0xF; /* RX3 */ fprintf (of, "%-X", ((ea1 << 16) | ea2) & VAMASK32); if (rx && !rx2) fprintf (of, "(R%d)", rx); if (rx2) fprintf (of, "(R%d,R%d)", rx, rx2); return -5; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 bflag, c1, c2, rdx; t_stat r; DEVICE *dptr; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) /* 8b dev? */ bflag = 1; else bflag = 0; /* assume 16b */ if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if (sw & SWMASK ('A')) { /* ASCII char? */ if (bflag) c1 = val[0] & 0x7F; else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0x7F; /* get byte */ fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); return 0; } if (sw & SWMASK ('B')) { /* byte? */ if (bflag) c1 = val[0] & 0xFF; else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0xFF; /* get byte */ fprint_val (of, c1, rdx, 8, PV_RZRO); return 0; } if (bflag) /* 16b only */ return SCPE_ARG; if (sw & SWMASK ('C')) { /* string? */ c1 = (val[0] >> 8) & 0x7F; c2 = val[0] & 0x7F; fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); return -1; } if (sw & SWMASK ('W')) { /* halfword? */ fprint_val (of, val[0], rdx, 16, PV_RZRO); return -1; } if (sw & SWMASK ('M')) { /* inst format? */ r = fprint_sym_m (of, addr, val); /* decode inst */ if (r <= 0) return r; } fprint_val (of, (val[0] << 16) | val[1], rdx, 32, PV_RZRO); return -3; } /* Symbolic decode for -m Inputs: of = output stream addr = current PC *val = values to decode cf = true if parsing for CPU Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val) { uint32 i, j, inst, r1, r2, ea1, ea2; inst = val[0]; ea1 = val[1]; ea2 = val[2]; for (i = 0; opc_val[i] != 0xFFFF; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & 0xFFFF) == (inst & masks[j])) { /* match? */ r1 = (inst >> 4) & 0xF; r2 = inst & 0xF; fprintf (of, "%s ", opcode[i]); /* print opcode */ switch (j) { /* case on class */ case I_V_MR: /* mask-register */ fprintf (of, "%-X,R%d", r1, r2); return -1; case I_V_RR: /* register-register */ case I_V_FF: /* floating-floating */ fprintf (of, "R%d,R%d", r1, r2); return -1; case I_V_SI: /* short immediate */ fprintf (of, "R%d,%-X", r1, r2); return -1; case I_V_SB: /* short branch */ fprintf (of, "%-X,", r1); case I_V_SX: /* ext short branch */ fprintf (of, "%-X", ((inst & MSK_SBF)? (addr + r2 + r2): (addr - r2 - r2))); return -1; case I_V_R: /* register */ fprintf (of, "R%d", r2); return -1; case I_V_RI: /* reg-immed */ fprintf (of, "R%d,%-X", r1, ea1); if (r2) fprintf (of, "(R%d)", r2); return -3; case I_V_RF: /* reg-full imm */ fprintf (of, "R%d,%-X", r1, (ea1 << 16) | ea2); if (r2) fprintf (of, "(R%d)", r2); return -5; case I_V_MX: /* mask-memory */ fprintf (of, "%-X,", r1); return fprint_addr (of, addr, r2, ea1, ea2); case I_V_RX: /* register-memory */ case I_V_FX: /* floating-memory */ fprintf (of, "R%d,", r1); case I_V_X: /* memory */ return fprint_addr (of, addr, r2, ea1, ea2); } /* end case */ return SCPE_IERR; } /* end if */ } /* end for */ return SCPE_ARG; /* no match */ } /* Register number Inputs: *cptr = pointer to input string **optr = pointer to pointer to next char rtype = mask, integer, or float Outputs: rnum = output register number, -1 if error */ int32 get_reg (char *cptr, char **optr, int32 rtype) { int32 reg; if ((*cptr == 'R') || (*cptr == 'r')) { /* R? */ cptr++; /* skip */ if (rtype == R_M) /* cant be mask */ return -1; } if ((*cptr >= '0') && (*cptr <= '9')) { reg = *cptr++ - '0'; if ((*cptr >= '0') && (*cptr <= '9')) reg = (reg * 10) + (*cptr - '0'); else --cptr; if (reg > 0xF) return -1; } else if ((*cptr >= 'a') && (*cptr <= 'f')) reg = (*cptr - 'a') + 10; else if ((*cptr >= 'A') && (*cptr <= 'F')) reg = (*cptr - 'A') + 10; else return -1; if ((rtype == R_F) && (reg & 1)) return -1; *optr = cptr + 1; return reg; } /* Immediate Inputs: *cptr = pointer to input string *imm = pointer to output value *inst = pointer to instruction max = max value Outputs: sta = status */ t_stat get_imm (char *cptr, uint32 *imm, uint32 *inst, uint32 max) { char *tptr; int32 idx; errno = 0; *imm = strtoul (cptr, &tptr, 16); /* get immed */ if (errno || (*imm > max) || (cptr == tptr)) return SCPE_ARG; if (*tptr == '(') { /* index? */ if ((idx = get_reg (tptr + 1, &tptr, R_R)) < 0) return SCPE_ARG; if (*tptr++ != ')') return SCPE_ARG; *inst = *inst | idx; } if (*tptr != 0) return SCPE_ARG; return SCPE_OK; } /* Address Inputs: *cptr = pointer to input string **tptr = pointer to moved pointer *ea = effective address addr = base address Outputs: status = SCPE_OK if ok, else error code */ t_stat get_addr (char *cptr, char **tptr, t_addr *ea, t_addr addr) { int32 sign = 1; if (*cptr == '.') { /* relative? */ cptr++; *ea = addr; if (*cptr == '+') /* .+? */ cptr++; else if (*cptr == '-') { /* .-? */ sign = -1; cptr++; } else return SCPE_OK; } else *ea = 0; errno = 0; *ea = *ea + (sign * ((int32) strtoul (cptr, tptr, 16))); if (errno || (cptr == *tptr)) return SCPE_ARG; return SCPE_OK; } /* Symbolic input */ t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 bflag, by, rdx, num; t_stat r; DEVICE *dptr; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) /* 8b dev? */ bflag = 1; else bflag = 0; /* assume 16b */ if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; if (bflag) val[0] = (t_value) cptr[0]; else val[0] = (addr & 1)? (val[0] & ~0xFF) | ((t_value) cptr[0]): (val[0] & 0xFF) | (((t_value) cptr[0]) << 8); return 0; } if (sw & SWMASK ('B')) { /* byte? */ by = get_uint (cptr, rdx, DMASK8, &r); /* get byte */ if (r != SCPE_OK) return SCPE_ARG; if (bflag) val[0] = by; else val[0] = (addr & 1)? (val[0] & ~0xFF) | by: (val[0] & 0xFF) | (by << 8); return 0; } if (bflag) /* 16b only */ return SCPE_ARG; if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII chars? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = ((t_value) cptr[0] << 8) | (t_value) cptr[1]; return -1; } if (sw & SWMASK ('W')) { /* halfword? */ val[0] = (int32) get_uint (cptr, rdx, DMASK16, &r); /* get number */ if (r != SCPE_OK) return r; return -1; } r = parse_sym_m (cptr, addr, val); /* try to parse inst */ if (r <= 0) return r; num = (int32) get_uint (cptr, rdx, DMASK32, &r); /* get number */ if (r != SCPE_OK) return r; val[0] = (num >> 16) & DMASK16; val[1] = num & DMASK16; return -3; } /* Symbolic input for -m Inputs: *cptr = pointer to input string addr = current PC *val = pointer to output values cf = true if parsing for CPU Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val) { uint32 i, j, df, db, t, inst, vp; int32 st, r1, r2, rx2; t_stat r; char *tptr, gbuf[CBUFSIZE]; vp = 0; 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; inst = opc_val[i] & 0xFFFF; /* get value */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if (r1_type[j]) { /* any R1 field? */ cptr = get_glyph (cptr, gbuf, ','); /* get R1 field */ if ((r1 = get_reg (gbuf, &tptr, r1_type[j])) < 0) return SCPE_ARG; if (*tptr != 0) return SCPE_ARG; inst = inst | (r1 << 4); /* or in R1 */ } cptr = get_glyph (cptr, gbuf, 0); /* get operand */ if (*cptr) /* should be end */ return SCPE_ARG; switch (j) { /* case on class */ case I_V_FF: case I_V_SI: /* flt-flt, sh imm */ case I_V_MR: case I_V_RR: /* mask/reg-register */ case I_V_R: /* register */ if ((r2 = get_reg (gbuf, &tptr, r2_type[j])) < 0) return SCPE_ARG; if (*tptr != 0) return SCPE_ARG; inst = inst | r2; /* or in R2 */ break; case I_V_FX: /* float-memory */ case I_V_MX: case I_V_RX: /* mask/reg-memory */ case I_V_X: /* memory */ r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ if (r != SCPE_OK) /* error? */ return SCPE_ARG; rx2 = 0; /* assume no 2nd */ if (*tptr == '(') { /* index? */ if ((r2 = get_reg (tptr + 1, &tptr, R_R)) < 0) return SCPE_ARG; inst = inst | r2; /* or in R2 */ if (*tptr == ',') { /* 2nd index? */ if ((rx2 = get_reg (tptr + 1, &tptr, R_R)) < 0) return SCPE_ARG; } if (*tptr++ != ')') /* all done? */ return SCPE_ARG; } if (*tptr != 0) return SCPE_ARG; val[0] = inst; /* store inst */ if (rx2 == 0) { /* no 2nd? */ if (t < 0x4000) { /* RX1? */ val[1] = t; /* store ea */ return -3; } st = (t - (addr + 4)); /* displ */ if ((st <= 0x3FFF) && (st >= -0x4000)) { /* RX2? CPU only */ t = (st & 0x7FFF) | 0x8000; val[1] = t; /* store displ */ return -3; } } t = (t & VAMASK32) | 0x40000000 | (rx2 << 24); val[1] = (t >> 16) & DMASK16; val[2] = t & DMASK16; return -5; case I_V_RI: /* 16b immediate */ r = get_imm (gbuf, &t, &inst, DMASK16); /* process imm */ if (r != SCPE_OK) return r; val[0] = inst; val[1] = t; return -3; case I_V_RF: r = get_imm (gbuf, &t, &inst, DMASK32); /* process imm */ if (r != SCPE_OK) return r; val[0] = inst; val[1] = (t >> 16) & DMASK16; val[2] = t & DMASK16; return -5; case I_V_SB: case I_V_SX: /* short branches */ r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ if ((r != SCPE_OK) || (t & 1) || *tptr) /* error if odd */ return SCPE_ARG; st = t; /* signed version */ db = (addr - t) & 0x1F; /* back displ */ df = (t - addr) & 0x1F; /* fwd displ */ if ((t == ((addr - db) & VAMASK16)) && /* back work and */ ((j == I_V_SX) || !(inst & MSK_SBF))) /* ext or back br? */ inst = inst | (db >> 1); /* or in back displ */ else if ((t == ((addr + df) & VAMASK16)) && /* fwd work and */ ((j == I_V_SX) || (inst & MSK_SBF))) /* ext or fwd br? */ inst = inst | (df >> 1) | MSK_SBF; /* or in fwd displ */ else return SCPE_ARG; } /* end case */ val[0] = inst; return -1; } simh-3.8.1/Interdata/id_pas.c0000644000175000017500000005540311112026546014164 0ustar vlmvlm/* id_pas.c: Interdata programmable async line adapter simulator Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. pas Programmable asynchronous line adapter(s) 19-Nov-08 RMS Revised for common TMXR show routines 18-Jun-07 RMS Added UNIT_IDLE flag 18-Oct-06 RMS Synced PASLA to clock 22-Nov-05 RMS Revised for new terminal processing routines 29-Jun-05 RMS Added SET PASLn DISCONNECT 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS 05-Jan-04 RMS Revised for tmxr library changes 09-May-03 RMS Added network device flag This module implements up to 32 individual serial interfaces, representing either individual PASLA modules or combinations of the 2-line and 8-line multiplexors, which are functionally very similar. These interfaces are mapped to Telnet based connections as the lines of a terminal multiplexor. The connection polling mechanism and the character input polling for all lines are done through a single polling job. */ #include "id_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #define PAS_LINES 32 #define UNIT_V_MDM (TTUF_V_UF + 0) /* modem control */ #define UNIT_MDM (1 << UNIT_V_MDM) #define PASL_WAIT 500 /* Status byte */ #define STA_OVR 0x80 /* overrun RO */ #define STA_PF 0x40 /* parity err RONI */ #define STA_NCL2S 0x40 /* not clr to snd XO */ #define STA_FR 0x20 /* framing err RO */ #define STA_RCR 0x10 /* rv chan rcv NI */ #define STA_CROF 0x02 /* carrier off RO */ #define STA_RING 0x01 /* ring RO */ #define STA_RCV (STA_OVR|STA_PF|STA_FR|STA_RCR|STA_CROF|STA_RING) #define SET_EX (STA_OVR|STA_PF|STA_FR) #define STA_XMT (STA_BSY) /* Command bytes 1,0 */ #define CMD_DTR (0x20 << 8) /* DTR */ #define CMD_ECHO (0x10 << 8) /* echoplex */ #define CMD_RCT (0x08 << 8) /* RCT/DTB NI */ #define CMD_XMTB (0x04 << 8) /* xmt break NI */ #define CMD_WRT (0x02 << 8) /* write/read */ #define CMD_V_CLK 6 /* baud rate */ #define CMD_M_CLK 0x3 #define CMD_V_DB 4 /* data bits */ #define CMD_M_DB 0x3 #define CMD_STOP 0x80 /* stop bit */ #define CMD_V_PAR 1 /* parity */ #define CMD_M_PAR 0x3 #define GET_PAR(x) (((x) >> CMD_V_PAR) & CMD_M_PAR) #define PAR_NONE 0 #define PAR_RAW 1 #define PAR_ODD 2 #define PAR_EVEN 3 #define CMD_TYP 0x01 /* command type */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; extern int32 lfc_poll; uint8 pas_sta[PAS_LINES]; /* status */ uint16 pas_cmd[PAS_LINES]; /* command */ uint8 pas_rbuf[PAS_LINES]; /* rcv buf */ uint8 pas_xbuf[PAS_LINES]; /* xmt buf */ uint8 pas_rarm[PAS_LINES]; /* rcvr int armed */ uint8 pas_xarm[PAS_LINES]; /* xmt int armed */ uint8 pas_rchp[PAS_LINES]; /* rcvr chr pend */ uint8 pas_tplte[PAS_LINES * 2 + 1]; /* template */ TMLN pas_ldsc[PAS_LINES] = { 0 }; /* line descriptors */ TMXR pas_desc = { 8, 0, 0, pas_ldsc }; /* mux descriptor */ #define PAS_ENAB pas_desc.lines uint32 pas (uint32 dev, uint32 op, uint32 dat); void pas_ini (t_bool dtpl); t_stat pasi_svc (UNIT *uptr); t_stat paso_svc (UNIT *uptr); t_stat pas_reset (DEVICE *dptr); t_stat pas_attach (UNIT *uptr, char *cptr); t_stat pas_detach (UNIT *uptr); int32 pas_par (int32 cmd, int32 c); t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); void pas_reset_ln (int32 i); /* PAS data structures pas_dev PAS device descriptor pas_unit PAS unit descriptor pas_reg PAS register list pas_mod PAS modifiers list */ DIB pas_dib = { d_PAS, -1, v_PAS, pas_tplte, &pas, &pas_ini }; UNIT pas_unit = { UDATA (&pasi_svc, UNIT_ATTABLE|UNIT_IDLE, 0), 0 }; REG pas_reg[] = { { BRDATA (STA, pas_sta, 16, 8, PAS_LINES) }, { BRDATA (CMD, pas_cmd, 16, 16, PAS_LINES) }, { BRDATA (RBUF, pas_rbuf, 16, 8, PAS_LINES) }, { BRDATA (XBUF, pas_xbuf, 16, 8, PAS_LINES) }, { BRDATA (IREQ, &int_req[l_PAS], 16, 32, PAS_LINES / 16) }, { BRDATA (IENB, &int_enb[l_PAS], 16, 32, PAS_LINES / 16) }, { BRDATA (RARM, pas_rarm, 16, 1, PAS_LINES) }, { BRDATA (XARM, pas_xarm, 16, 1, PAS_LINES) }, { BRDATA (RCHP, pas_rchp, 16, 1, PAS_LINES) }, { HRDATA (DEVNO, pas_dib.dno, 8), REG_HRO }, { NULL } }; MTAB pas_mod[] = { { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &pas_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &pas_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &pas_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &pas_desc }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &pas_vlines, &tmxr_show_lines, (void *) &pas_desc }, { 0 } }; DEVICE pas_dev = { "PAS", &pas_unit, pas_reg, pas_mod, 1, 10, 31, 1, 16, 8, &tmxr_ex, &tmxr_dep, &pas_reset, NULL, &pas_attach, &pas_detach, &pas_dib, DEV_NET | DEV_DISABLE }; /* PASL data structures pasl_dev PASL device descriptor pasl_unit PASL unit descriptor pasl_reg PASL register list pasl_mod PASL modifiers list */ UNIT pasl_unit[] = { { UDATA (&paso_svc, 0, 0), PASL_WAIT }, /* all but 8 dis */ { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, 0, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT } }; MTAB pasl_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { UNIT_MDM, 0, "no dataset", "NODATASET", NULL }, { UNIT_MDM, UNIT_MDM, "dataset", "DATASET", NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &pas_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &pas_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &pas_desc }, { 0 } }; REG pasl_reg[] = { { URDATA (TIME, pasl_unit[0].wait, 16, 24, 0, PAS_LINES, REG_NZ + PV_LEFT) }, { NULL } }; DEVICE pasl_dev = { "PASL", pasl_unit, pasl_reg, pasl_mod, PAS_LINES, 10, 31, 1, 16, 8, NULL, NULL, &pas_reset, NULL, NULL, NULL, NULL, 0 }; /* PAS: IO routine */ uint32 pas (uint32 dev, uint32 op, uint32 dat) { int32 ln = (dev - pas_dib.dno) >> 1; int32 xmt = (dev - pas_dib.dno) & 1; int32 t, old_cmd; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_RD: /* read */ pas_rchp[ln] = 0; /* clr chr pend */ pas_sta[ln] = pas_sta[ln] & ~STA_OVR; /* clr overrun */ return pas_rbuf[ln]; /* return buf */ case IO_WD: /* write */ pas_xbuf[ln] = dat & 0xFF; /* store char */ pas_sta[ln] = pas_sta[ln] | STA_BSY; /* set busy */ sim_activate (&pasl_unit[ln], pasl_unit[ln].wait); break; case IO_SS: /* status */ if (xmt) { /* xmt side? */ if (pas_ldsc[ln].conn == 0) /* not conn? */ t = STA_NCL2S | STA_BSY; /* busy, not clr */ else t = pas_sta[ln] & STA_XMT; /* else just busy */ } else { t = pas_sta[ln] & STA_RCV; /* get static */ if (!pas_rchp[ln]) /* no char? busy */ t = t | STA_BSY; if (pas_ldsc[ln].conn == 0) /* not connected? */ t = t | STA_BSY | STA_EX; /* = !dsr */ if (t & SET_EX) /* test for ex */ t = t | STA_EX; } return t; case IO_OC: /* command */ old_cmd = pas_cmd[ln]; /* old cmd */ if (dat & CMD_TYP) { /* type 1? */ pas_cmd[ln] = (pas_cmd[ln] & 0xFF) | (dat << 8); if (pas_cmd[ln] & CMD_WRT) /* write? */ pas_xarm[ln] = int_chg (v_PASX + ln + ln, dat, pas_xarm[ln]); else pas_rarm[ln] = int_chg (v_PAS + ln + ln, dat, pas_rarm[ln]); } else pas_cmd[ln] = (pas_cmd[ln] & ~0xFF) | dat; if (pasl_unit[ln].flags & UNIT_MDM) { /* modem ctrl? */ if ((pas_cmd[ln] & CMD_DTR) && (pas_sta[ln] & STA_RING)) pas_sta[ln] = pas_sta[ln] & ~(STA_CROF | STA_RING); if (old_cmd & ~pas_cmd[ln] & CMD_DTR) { tmxr_linemsg (&pas_ldsc[ln], "\r\nLine hangup\r\n"); tmxr_reset_ln (&pas_ldsc[ln]); /* reset line */ pas_sta[ln] = pas_sta[ln] | STA_CROF; /* no carrier */ if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); } } break; } return 0; } /* Unit service - receive side Poll all active lines for input Poll for new connections */ t_stat pasi_svc (UNIT *uptr) { int32 ln, c, out; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; sim_activate (uptr, lfc_poll); /* continue poll */ ln = tmxr_poll_conn (&pas_desc); /* look for connect */ if (ln >= 0) { /* got one? */ if ((pasl_unit[ln].flags & UNIT_MDM) && /* modem control */ ((pas_cmd[ln] & CMD_DTR) == 0)) /* & !dtr? */ pas_sta[ln] = pas_sta[ln] | STA_RING | STA_CROF; /* set ring, no cd */ else pas_sta[ln] = pas_sta[ln] & ~STA_CROF; /* just answer */ if (pas_rarm[ln]) /* interrupt */ SET_INT (v_PAS + ln + ln); pas_ldsc[ln].rcve = 1; /* rcv enabled */ } tmxr_poll_rx (&pas_desc); /* poll for input */ for (ln = 0; ln < PAS_ENAB; ln++) { /* loop thru lines */ if (pas_ldsc[ln].conn) { /* connected? */ if (c = tmxr_getc_ln (&pas_ldsc[ln])) { /* any char? */ pas_sta[ln] = pas_sta[ln] & ~(STA_FR | STA_PF); if (pas_rchp[ln]) pas_sta[ln] = pas_sta[ln] | STA_OVR; if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); if (c & SCPE_BREAK) { /* break? */ pas_sta[ln] = pas_sta[ln] | STA_FR; /* framing error */ pas_rbuf[ln] = 0; /* no character */ } else { /* normal */ out = c & 0x7F; /* echo is 7b */ c = sim_tt_inpcvt (c, TT_GET_MODE (pasl_unit[ln].flags)); if (TT_GET_MODE (pasl_unit[ln].flags) != TT_MODE_8B) c = pas_par (pas_cmd[ln], c); /* apply parity */ pas_rbuf[ln] = c; /* save char */ pas_rchp[ln] = 1; /* char pending */ if ((pas_cmd[ln] & CMD_ECHO) && pas_ldsc[ln].xmte) { TMLN *lp = &pas_ldsc[ln]; /* get line */ out = sim_tt_outcvt (out, TT_GET_MODE (pasl_unit[ln].flags)); if (out >= 0) /* output char */ tmxr_putc_ln (lp, out); tmxr_poll_tx (&pas_desc); /* poll xmt */ } } /* end else normal */ } /* end if char */ } /* end if conn */ else if ((pas_sta[ln] & STA_CROF) == 0) { /* not conn, was conn? */ pas_sta[ln] = pas_sta[ln] | STA_CROF; /* no carrier */ if (pas_rarm[ln]) /* intr */ SET_INT (v_PAS + ln + ln); } } /* end for */ return SCPE_OK; } /* Unit service - transmit side */ t_stat paso_svc (UNIT *uptr) { int32 c; uint32 ln = uptr - pasl_unit; /* line # */ if (pas_ldsc[ln].conn) { /* connected? */ if (pas_ldsc[ln].xmte) { /* xmt enabled? */ TMLN *lp = &pas_ldsc[ln]; /* get line */ if (TT_GET_MODE (pasl_unit[ln].flags) == TT_MODE_8B) c = pas_par (pas_cmd[ln], pas_xbuf[ln]); /* apply parity */ else c = sim_tt_outcvt (pas_xbuf[ln], TT_GET_MODE (pasl_unit[ln].flags)); if (c >= 0) { tmxr_putc_ln (lp, c); /* output char */ } tmxr_poll_tx (&pas_desc); /* poll xmt */ } else { /* buf full */ tmxr_poll_tx (&pas_desc); /* poll xmt */ sim_activate (uptr, pasl_unit[ln].wait); /* wait */ return SCPE_OK; } } pas_sta[ln] = pas_sta[ln] & ~STA_BSY; /* not busy */ if (pas_xarm[ln]) /* set intr */ SET_INT (v_PASX + ln + ln); return SCPE_OK; } int32 pas_par (int32 cmd, int32 c) { int32 pf = GET_PAR (cmd); static const uint8 odd_par[] = { 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 00 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 10 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 20 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 30 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 40 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 50 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 60 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 70 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 80 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 90 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* A0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* B0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* C0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* D0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* E0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* F0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80 }; switch (pf) { /* case on parity */ case PAR_ODD: return (odd_par[c & 0x7F]) | (c & 0x7F); case PAR_EVEN: return (odd_par[c & 0x7F] ^ 0x80) | (c & 0x7F); case PAR_NONE: case PAR_RAW: break; } return c & 0xFF; } /* Reset routine */ t_stat pas_reset (DEVICE *dptr) { int32 i; if (dptr->flags & DEV_DIS) { /* disabled? */ pas_dev.flags = pas_dev.flags | DEV_DIS; /* disable lines */ pasl_dev.flags = pasl_dev.flags | DEV_DIS; } else { pas_dev.flags = pas_dev.flags & ~DEV_DIS; /* enable lines */ pasl_dev.flags = pasl_dev.flags & ~DEV_DIS; } if (pas_unit.flags & UNIT_ATT) /* master att? */ sim_activate_abs (&pas_unit, lfc_poll); /* cosched with clock */ else sim_cancel (&pas_unit); /* else stop */ for (i = 0; i < PAS_LINES; i++) pas_reset_ln (i); return SCPE_OK; } /* Attach master unit */ t_stat pas_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&pas_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_activate_abs (uptr, 100); /* quick poll */ return SCPE_OK; } /* Detach master unit */ t_stat pas_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&pas_desc, uptr); /* detach */ for (i = 0; i < PAS_LINES; i++) /* disable rcv */ pas_ldsc[i].rcve = 0; sim_cancel (uptr); /* stop poll */ return r; } /* Change number of lines */ t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, PAS_LINES, &r); if ((r != SCPE_OK) || (newln == PAS_ENAB)) return r; if (newln == 0) return SCPE_ARG; if (newln < PAS_ENAB) { for (i = newln, t = 0; i < PAS_ENAB; i++) t = t | pas_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < PAS_ENAB; i++) { if (pas_ldsc[i].conn) { tmxr_linemsg (&pas_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&pas_ldsc[i]); /* reset line */ } pasl_unit[i].flags = pasl_unit[i].flags | UNIT_DIS; pas_reset_ln (i); } } else { for (i = PAS_ENAB; i < newln; i++) { pasl_unit[i].flags = pasl_unit[i].flags & ~UNIT_DIS; pas_reset_ln (i); } } PAS_ENAB = newln; return SCPE_OK; } /* Reset an individual line */ void pas_reset_ln (int32 i) { CLR_INT (v_PAS + i + i); /* clear int */ CLR_ENB (v_PAS + i + i); CLR_INT (v_PASX + i + i); /* disable int */ CLR_ENB (v_PASX + i + i); pas_rarm[i] = pas_xarm[i] = 0; /* disarm int */ pas_rbuf[i] = pas_xbuf[i] = 0; /* clear state */ pas_cmd[i] = 0; pas_rchp[i] = 0; pas_sta[i] = 0; if (pas_ldsc[i].conn == 0) /* clear carrier */ pas_sta[i] = pas_sta[i] | STA_CROF; sim_cancel (&pasl_unit[i]); return; } /* Init template */ void pas_ini (t_bool dtpl) { int32 i, j; for (i = j = 0; i < PAS_ENAB; i++) { pas_tplte[j] = j; pas_tplte[j + 1] = j + o_PASX; j = j + 2; } pas_tplte[j] = TPL_END; return; } simh-3.8.1/Interdata/id_idc.c0000644000175000017500000010424211112026546014134 0ustar vlmvlm/* id_idc.c: Interdata MSM/IDC disk controller simulator Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. idc MSM/IDC disk controller 03-Apr-06 RMS Fixed WD/WH handling (found by Davis Johnson) 30-Mar-06 RMS Fixed bug, nop command should be ignored (found by Davis Johnson) 25-Apr-03 RMS Revised for extended file support 16-Feb-03 RMS Fixed read to test transfer ok before selch operation Note: define flag ID_IDC to enable the extra functions of the intelligent disk controller */ #include "id_defs.h" #define IDC_NUMBY 256 /* bytes/sector */ #define IDC_NUMSC 64 /* sectors/track */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_M_DTYPE 0x7 #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define CYL u3 /* current cylinder */ #define HD u4 /* current head */ #define STD buf /* drive status */ #define FNC wait /* function */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define IDC_DRVMASK ((1 << ID_NUMDR) - 1) /* drive bit mask */ #define IDC_DIRMASK (IDC_DRVMASK << (i_IDC + 1)) /* drive irq mask */ /* Controller status */ #define STC_WRP 0x80 /* write protected */ #define STC_ACF 0x40 /* addr cmp fail */ #define STC_DEF 0x20 /* def track NI */ #define STC_CYO 0x10 /* cylinder ovflo */ #define STC_IDL 0x02 /* ctrl idle */ #define STC_DTE 0x01 /* xfer error */ #define SETC_EX (STC_WRP|STC_ACF|STC_DEF|STC_CYO) #define STC_MASK (STC_WRP|STC_ACF|STC_DEF|STC_CYO|STA_BSY|STC_IDL|STC_DTE) /* Controller command */ #define CMC_MASK 0x3F #define CMC_CLR 0x08 /* reset */ #define CMC_RD 0x01 /* read */ #define CMC_WR 0x02 /* write */ #define CMC_RCHK 0x03 /* read check */ #define CMC_FCHK 0x04 /* format check NI */ #define CMC_RFMT 0x05 /* read fmt NI */ #define CMC_WFMT 0x06 /* write fmt NI */ #define CMC_WFTK 0x07 /* write fmt track NI */ /* IDC only functions */ #define CMC_RRAM 0x10 /* read RAM */ #define CMC_WRAM 0x11 /* write RAM */ #define CMC_EXP0 0x12 /* read page 0 NI */ #define CMC_RUNC 0x21 /* read uncorr */ #define CMC_STST 0x30 /* self test */ #define CMC_WLNG 0x32 /* write long NI */ #define CMC_LAMP 0x37 /* lamp test */ #define CMC_DRV 0x100 /* drive func */ #define CMC_DRV1 0x200 /* drive func, part 2 */ /* Drive status, ^ = dynamic, * = in unit status */ #define STD_WRP 0x80 /* ^write prot */ /* 0x40 /* unused */ #define STD_ACH 0x20 /* alt chan busy NI */ #define STD_UNS 0x10 /* *unsafe */ #define STD_NRDY 0x08 /* ^not ready */ #define STD_SKI 0x02 /* *seek incomplete */ #define STD_OFFL 0x01 /* ^offline */ #define STD_UST (STD_UNS | STD_SKI) /* set from unit */ #define SETD_EX (STD_WRP | STD_UNS) /* set examine */ /* Drive command */ #define CMDF_SHD 0x20 /* set head */ #define CMDF_SCY 0x10 /* set cylinder */ #define CMD_SK 0x02 /* seek */ #define CMD_RST 0x01 /* restore */ #define CMDX_MASK 0x30 /* ext cmd bits */ #define CMDX_RLS 0x80 /* release */ #define CMDX_CLF 0x40 /* clear fault */ #define CMDX_SVP 0x08 /* servo + */ #define CMDX_SVM 0x04 /* servo - */ #define CMDX_DSP 0x02 /* strobe + */ #define CMDX_DSM 0x01 /* strobe - */ /* Geometry masks */ #define CY_MASK 0xFFF /* cylinder */ #define HD_MASK 0x1F /* head mask */ #define SC_MASK 0x3F /* sector mask */ #define HCYL_V_HD 10 /* head/cyl word */ #define HCYL_V_CYL 0 #define GET_SA(cy,sf,sc,t) \ (((((cy)*drv_tab[t].surf)+(sf))*IDC_NUMSC)+(sc)) /* The MSM (IDC) controller supports (two) six different disk drive types: type #sectors/ #surfaces/ #cylinders/ surface cylinder drive MCCDD16 64 1 823 IDC MCCDD48 64 3 823 IDC MCCDD80 64 5 823 IDC MSM80 64 5 823 MSM MSM300 64 19 823 MSM MSM330F 64 16 1024 IDC In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE AND MUST HAVE THE SAME SECTORS/TRACK. */ #define TYPE_MCCDD16 0 #define SURF_MCCDD16 1 #define CYL_MCCDD16 823 #define SIZE_MCCDD16 (IDC_NUMSC * SURF_MCCDD16 * CYL_MCCDD16 * IDC_NUMBY) #define TYPE_MCCDD48 1 #define SURF_MCCDD48 3 #define CYL_MCCDD48 823 #define SIZE_MCCDD48 (IDC_NUMSC * SURF_MCCDD48 * CYL_MCCDD48 * IDC_NUMBY) #define TYPE_MCCDD80 2 #define SURF_MCCDD80 5 #define CYL_MCCDD80 823 #define SIZE_MCCDD80 (IDC_NUMSC * SURF_MCCDD80 * CYL_MCCDD80 * IDC_NUMBY) #define TYPE_MSM80 3 #define SURF_MSM80 5 #define CYL_MSM80 823 #define SIZE_MSM80 (IDC_NUMSC * SURF_MSM80 * CYL_MSM80 * IDC_NUMBY) #define TYPE_MSM300 4 #define SURF_MSM300 19 #define CYL_MSM300 823 #define SIZE_MSM300 (IDC_NUMSC * SURF_MSM300 * CYL_MSM300 * IDC_NUMBY) #define TYPE_MSM330F 5 #define SURF_MSM330F 16 #define CYL_MSM330F 1024 #define SIZE_MSM330F (IDC_NUMSC * SURF_MSM330F * CYL_MSM330F * IDC_NUMBY) struct drvtyp { uint32 surf; /* surfaces */ uint32 cyl; /* cylinders */ uint32 size; /* #blocks */ uint32 msmf; /* MSM drive */ }; static struct drvtyp drv_tab[] = { { SURF_MCCDD16, CYL_MCCDD16, SIZE_MCCDD16, 0 }, { SURF_MCCDD48, CYL_MCCDD48, SIZE_MCCDD48, 0 }, { SURF_MCCDD80, CYL_MCCDD80, SIZE_MCCDD80, 0 }, { SURF_MSM80, CYL_MSM80, SIZE_MSM80, 1 }, { SURF_MSM300, CYL_MSM300, SIZE_MSM300, 1 }, { SURF_MSM330F, CYL_MSM330F, SIZE_MSM330F, 0 }, { 0 } }; extern uint32 int_req[INTSZ], int_enb[INTSZ]; uint8 idcxb[IDC_NUMBY * 3]; /* xfer buffer */ uint32 idc_bptr = 0; /* buffer ptr */ uint32 idc_wdptr = 0; /* ctrl write data ptr */ uint32 idc_db = 0; /* ctrl buffer */ uint32 idc_sta = 0; /* ctrl status */ uint32 idc_sec = 0; /* sector */ uint32 idc_hcyl = 0; /* head/cyl */ uint32 idc_svun = 0; /* most recent unit */ uint32 idc_1st = 0; /* first byte */ uint32 idc_arm = 0; /* ctrl armed */ uint32 idd_db = 0; /* drive buffer */ uint32 idd_wdptr = 0; /* drive write data ptr */ uint32 idd_arm[ID_NUMDR] = { 0 }; /* drives armed */ uint16 idd_dcy[ID_NUMDR] = { 0 }; /* desired cyl */ uint32 idd_sirq = 0; /* drive saved irq */ int32 idc_stime = 100; /* seek latency */ int32 idc_rtime = 100; /* rotate latency */ int32 idc_ctime = 5; /* command latency */ uint8 idc_tplte[] = { 0, 1, 2, 3, 4, TPL_END }; /* ctrl + drive */ DEVICE idc_dev; uint32 id (uint32 dev, uint32 op, uint32 dat); t_stat idc_svc (UNIT *uptr); t_stat idc_reset (DEVICE *dptr); t_stat idc_attach (UNIT *uptr, char *cptr); t_stat idc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); void idc_wd_byte (uint32 dat); t_stat idc_rds (UNIT *uptr); t_stat idc_wds (UNIT *uptr); t_bool idc_dter (UNIT *uptr, uint32 first); void idc_done (uint32 flg); extern t_stat id_dboot (int32 u, DEVICE *dptr); /* DP data structures idc_dev DP device descriptor idc_unit DP unit list idc_reg DP register list idc_mod DP modifier list */ DIB idc_dib = { d_IDC, 0, v_IDC, idc_tplte, &id, NULL }; UNIT idc_unit[] = { { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) } }; REG idc_reg[] = { { HRDATA (STA, idc_sta, 8) }, { HRDATA (BUF, idc_db, 8) }, { HRDATA (SEC, idc_sec, 8) }, { HRDATA (HCYL, idc_hcyl, 16) }, { HRDATA (BUF, idd_db, 8) }, { HRDATA (SVUN, idc_svun, 2), REG_HIDDEN }, { BRDATA (DBUF, idcxb, 16, 8, IDC_NUMBY * 3) }, { HRDATA (DBPTR, idc_bptr, 10), REG_RO }, { FLDATA (FIRST, idc_1st, 0) }, { HRDATA (CWDPTR, idc_wdptr, 2) }, { HRDATA (DWDPTR, idc_wdptr, 1) }, { GRDATA (IREQ, int_req[l_IDC], 16, ID_NUMDR + 1, i_IDC) }, { GRDATA (IENB, int_enb[l_IDC], 16, ID_NUMDR + 1, i_IDC) }, { GRDATA (SIREQ, idd_sirq, 16, ID_NUMDR, i_IDC + 1), REG_RO }, { FLDATA (ICARM, idc_arm, 0) }, { BRDATA (IDARM, idd_arm, 16, 1, ID_NUMDR) }, { DRDATA (RTIME, idc_rtime, 24), PV_LEFT | REG_NZ }, { DRDATA (STIME, idc_stime, 24), PV_LEFT | REG_NZ }, { DRDATA (CTIME, idc_ctime, 24), PV_LEFT | REG_NZ }, { BRDATA (CYL, idd_dcy, 16, 16, ID_NUMDR) }, { URDATA (UCYL, idc_unit[0].CYL, 16, 12, 0, ID_NUMDR, REG_RO) }, { URDATA (UHD, idc_unit[0].HD, 16, 5, 0, ID_NUMDR, REG_RO) }, { URDATA (UFNC, idc_unit[0].FNC, 16, 10, 0, ID_NUMDR, REG_HRO) }, { URDATA (UST, idc_unit[0].STD, 16, 8, 0, ID_NUMDR, REG_RO) }, { URDATA (CAPAC, idc_unit[0].capac, 10, T_ADDR_W, 0, ID_NUMDR, PV_LEFT | REG_HRO) }, { HRDATA (DEVNO, idc_dib.dno, 8), REG_HRO }, { HRDATA (SELCH, idc_dib.sch, 2), REG_HRO }, { BRDATA (TPLTE, idc_tplte, 16, 8, ID_NUMDR + 1), REG_HRO }, { NULL } }; MTAB idc_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD16 << UNIT_V_DTYPE) + UNIT_ATT, "MCCDD16", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD48 << UNIT_V_DTYPE) + UNIT_ATT, "MCCDD48", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD80 << UNIT_V_DTYPE) + UNIT_ATT, "MCCDD80", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM330F << UNIT_V_DTYPE) + UNIT_ATT, "MSM330F", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD16 << UNIT_V_DTYPE), "MCCDD16", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD48 << UNIT_V_DTYPE), "MCCDD48", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD80 << UNIT_V_DTYPE), "MCCDD80", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM330F << UNIT_V_DTYPE), "MSM330F", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD16 << UNIT_V_DTYPE), NULL, "MCCDD16", &idc_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD48 << UNIT_V_DTYPE), NULL, "MCCDD48", &idc_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD80 << UNIT_V_DTYPE), NULL, "MCCDD80", &idc_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM330F << UNIT_V_DTYPE), NULL, "MSM330F", &idc_set_size }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM80 << UNIT_V_DTYPE) + UNIT_ATT, "MSM80", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM300 << UNIT_V_DTYPE) + UNIT_ATT, "MSM300", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM80 << UNIT_V_DTYPE), "MSM80", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM300 << UNIT_V_DTYPE), "MSM300", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM80 << UNIT_V_DTYPE), NULL, "MSM80", &idc_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM300 << UNIT_V_DTYPE), NULL, "MSM300", &idc_set_size }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", &set_sch, &show_sch, NULL }, { 0 } }; DEVICE idc_dev = { "DM", idc_unit, idc_reg, idc_mod, ID_NUMDR, 16, 29, 1, 16, 8, NULL, NULL, &idc_reset, &id_dboot, &idc_attach, NULL, &idc_dib, DEV_DISABLE }; /* Controller: IO routine */ uint32 idc (uint32 dev, uint32 op, uint32 dat) { uint32 f, t; UNIT *uptr; switch (op) { /* case IO op */ case IO_ADR: /* select */ sch_adr (idc_dib.sch, dev); /* inform sel ch */ return HW; /* halfwords */ case IO_RD: /* read data */ case IO_RH: /* read halfword */ return 0; /* return data */ case IO_WD: /* write data */ idc_wd_byte (dat); /* one byte only */ break; case IO_WH: /* write halfword */ idc_wd_byte (dat >> 8); /* high byte */ idc_wd_byte (dat); /* low byte */ break; case IO_SS: /* status */ t = idc_sta & STC_MASK; /* get status */ if (t & SETC_EX) /* test for EX */ t = t | STA_EX; return t; case IO_OC: /* command */ idc_arm = int_chg (v_IDC, dat, idc_arm); /* upd int ctrl */ idc_wdptr = 0; /* init ptr */ f = dat & CMC_MASK; /* get cmd */ uptr = idc_dev.units + idc_svun; /* get unit */ if (f & CMC_CLR) { /* clear? */ idc_reset (&idc_dev); /* reset world */ break; } if ((f == 0) || /* if nop, */ (f == CMC_EXP0) || /* expg, */ !(idc_sta & STC_IDL) || /* !idle, */ sim_is_active (uptr)) break; /* unit busy, ignore */ idc_sta = STA_BSY; /* bsy=1,idl,err=0 */ idc_1st = 1; /* xfr not started */ idc_bptr = 0; /* buffer empty */ uptr->FNC = f; /* save cmd */ sim_activate (uptr, idc_rtime); /* schedule */ idd_sirq = int_req[l_IDC] & IDC_DIRMASK; /* save drv ints */ int_req[l_IDC] = int_req[l_IDC] & ~IDC_DIRMASK; /* clr drv ints */ break; } return 0; } /* Process WD/WH data */ void idc_wd_byte (uint32 dat) { dat = dat & 0xFF; switch (idc_wdptr) { case 0: /* byte 0 = sector */ idc_sec = dat; idc_wdptr++; break; case 1: /* byte 1 = high hd/cyl */ idc_hcyl = (idc_hcyl & 0xFF) | (dat << 8); idc_wdptr++; break; case 2: /* byte 2 = low hd/cyl */ idc_hcyl = (idc_hcyl & 0xFF00) | dat; idc_wdptr = 0; break; } return; } /* Drives: IO routine */ uint32 id (uint32 dev, uint32 op, uint32 dat) { uint32 t, u, f; UNIT *uptr; if (dev == idc_dib.dno) /* controller? */ return idc (dev, op, dat); u = (dev - idc_dib.dno - o_ID0) / o_ID0; /* get unit num */ uptr = idc_dev.units + u; /* get unit ptr */ switch (op) { /* case IO op */ case IO_ADR: /* select */ if (idc_sta & STC_IDL) /* idle? save unit */ idc_svun = u; return BY; /* byte only */ case IO_RD: /* read data */ case IO_RH: return 0; case IO_WD: /* write data */ if (idd_wdptr & 1) /* low byte? */ idd_db = (idd_db & 0xFF00) | dat; else idd_db = (idd_db & 0xFF) | (dat << 8); /* no, high */ idd_wdptr = idd_wdptr ^ 1; /* other byte */ break; case IO_SS: /* status */ if (uptr->flags & UNIT_ATT) t = ((uptr->flags & UNIT_WPRT)? STD_WRP: 0) | (sim_is_active (uptr)? STD_NRDY: 0) | (uptr->STD & STD_UST); else t = STD_NRDY | STD_OFFL; /* off = X'09' */ if (t & SETD_EX) /* test for ex */ t = t | STA_EX; return t; case IO_OC: /* command */ idd_arm[u] = int_chg (v_IDC + u + 1, dat, idd_arm[u]); idd_wdptr = 0; /* init ptr */ if (idd_arm[u] == 0) /* disarmed? */ idd_sirq &= ~(1 << (v_IDC + u + 1)); /* clr saved req */ f = dat & CMC_MASK; /* get cmd */ if ((f == 0) || /* if nop, */ (f == CMDX_MASK) || /* 0x30, */ !(idc_sta & STC_IDL) || /* !idle, */ sim_is_active (uptr)) /* unit busy, ignore */ break; uptr->FNC = f | CMC_DRV; /* save cmd */ idc_sta = idc_sta & ~STC_IDL; /* clr idle */ sim_activate (uptr, idc_ctime); /* schedule */ break; } return 0; } /* Unit service If drive command, process; if an interrupt is needed (positioning command), schedule second part If data transfer command, process; must use selector channel */ t_stat idc_svc (UNIT *uptr) { int32 diff; uint32 f, u = uptr - idc_dev.units; /* get unit number */ uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ uint32 t; t_stat r; if (uptr->FNC & CMC_DRV) { /* drive cmd? */ f = uptr->FNC & CMC_MASK; /* get cmd */ if (uptr->FNC & CMC_DRV1) { /* part 2? */ if (idd_arm[u]) { /* drv int armed? */ if (idc_sta & STC_IDL) /* ctrl idle? */ SET_INT (v_IDC + u + 1); /* req intr */ else idd_sirq |= (1 << (v_IDC + u + 1)); /* def intr */ } if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; if (((f & CMDX_MASK) == 0) && /* seek? */ (f & (CMD_SK | CMD_RST))) { if (idd_dcy[u] >= drv_tab[dtype].cyl) /* bad cylinder? */ uptr->STD = uptr->STD | STD_SKI; /* error */ uptr->CYL = idd_dcy[u]; /* put on cyl */ } } /* end if p2 */ else { /* part 1 */ idc_sta = idc_sta | STC_IDL; /* set idle */ uptr->FNC = uptr->FNC | CMC_DRV1; /* set part 2 */ if (f >= CMDX_MASK) { /* extended? */ if (f & CMDX_CLF) /* clr fault? */ uptr->STD = uptr->STD & ~STD_UNS; /* clr unsafe */ if (f & (CMDX_RLS | CMDX_SVP | CMDX_SVM)) /* intr expected? */ sim_activate (uptr, idc_ctime); } else if (f >= CMDF_SCY) { /* tag? */ if (f & CMDF_SHD) uptr->HD = idd_db & HD_MASK; else if (f & CMDF_SCY) { if (idd_db >= drv_tab[dtype].cyl) /* bad cylinder? */ uptr->STD = uptr->STD | STD_SKI; /* set seek inc */ idd_dcy[u] = idd_db & CY_MASK; } } else if (f & (CMD_SK | CMD_RST)) { /* seek? */ if (f == CMD_RST) /* restore? */ idd_dcy[u] = 0; if (idd_dcy[u] >= drv_tab[dtype].cyl) { /* bad cylinder? */ uptr->STD = uptr->STD | STD_SKI; /* set seek inc */ idd_dcy[u] = uptr->CYL; /* no motion */ sim_activate (uptr, 0); /* finish asap */ } else { /* cylinder ok */ uptr->STD = uptr->STD & ~STD_SKI; /* clr seek inc */ diff = idd_dcy[u] - uptr->CYL; if (diff < 0) /* ABS cyl diff */ diff = -diff; else if (diff == 0) /* must be nz */ diff = 1; sim_activate (uptr, diff * idc_stime); } } } /* end else p1 */ return SCPE_OK; /* end if drv */ } switch (uptr->FNC & CMC_MASK) { /* case on func */ case CMC_RCHK: /* read check */ idc_dter (uptr, 1); /* check xfr err */ break; #if defined (ID_IDC) case CMC_RUNC: /* read uncorr */ #endif case CMC_RD: /* read */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ if (idc_dter (uptr, idc_1st)) /* dte? done */ return SCPE_OK; if (r = idc_rds (uptr)) /* read sec, err? */ return r; idc_1st = 0; t = sch_wrmem (idc_dib.sch, idcxb, IDC_NUMBY); /* write mem */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ sim_activate (uptr, idc_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } idc_sta = idc_sta | STC_DTE; /* cant work */ break; case CMC_WR: /* write */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ if (idc_dter (uptr, idc_1st)) /* dte? done */ return SCPE_OK; idc_bptr = sch_rdmem (idc_dib.sch, idcxb, IDC_NUMBY); /* read mem */ idc_db = idcxb[idc_bptr - 1]; /* last byte */ if (r = idc_wds (uptr)) /* write sec, err? */ return r; idc_1st = 0; if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ sim_activate (uptr, idc_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } idc_sta = idc_sta | STC_DTE; /* cant work */ break; case CMC_FCHK: case CMC_RFMT: case CMC_WFMT: case CMC_WFTK: idc_dter (uptr, 1); idc_sta = idc_sta | STC_WRP; break; #if defined (ID_IDC) case CMC_RRAM: /* read RAM */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ sch_wrmem (idc_dib.sch, idcxb, IDC_NUMBY * 3); if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ sim_activate (uptr, idc_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } idc_sta = idc_sta | STC_DTE; /* cant work */ break; case CMC_WRAM: /* write RAM */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ sch_rdmem (idc_dib.sch, idcxb, IDC_NUMBY * 3); /* read from mem */ if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ sim_activate (uptr, idc_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } idc_sta = idc_sta | STC_DTE; /* cant work */ break; case CMC_STST: case CMC_LAMP: /* tests */ break; #endif default: idc_sta = idc_sta | STC_DTE; break; } idc_done (0); /* done */ return SCPE_OK; } /* Read data sector */ t_stat idc_rds (UNIT *uptr) { uint32 i; i = fxread (idcxb, sizeof (uint8), IDC_NUMBY, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("IDC I/O error"); clearerr (uptr->fileref); idc_done (STC_DTE); return SCPE_IOERR; } for ( ; i < IDC_NUMBY; i++) /* fill with 0's */ idcxb[i] = 0; return SCPE_OK; } /* Write data sector */ t_bool idc_wds (UNIT *uptr) { for ( ; idc_bptr < IDC_NUMBY; idc_bptr++) idcxb[idc_bptr] = idc_db; /* fill with last */ fxwrite (idcxb, sizeof (uint8), IDC_NUMBY, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("IDC I/O error"); clearerr (uptr->fileref); idc_done (STC_DTE); return SCPE_IOERR; } return FALSE; } /* Data transfer error test routine */ t_bool idc_dter (UNIT *uptr, uint32 first) { uint32 cy; uint32 hd, sc, sa; uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ idc_done (STC_DTE); /* error, done */ return TRUE; } if ((uptr->flags & UNIT_WPRT) && (uptr->FNC == CMC_WR)) { idc_done (STC_WRP); /* error, done */ return TRUE; } cy = uptr->CYL; /* get cylinder */ hd = uptr->HD; /* get head */ sc = idc_sec & SC_MASK; /* get sector */ if (cy >= drv_tab[dtype].cyl) { /* bad cylinder? */ uptr->STD = uptr->STD | STD_SKI; /* error */ idc_done (STC_DTE); /* error, done */ return TRUE; } if (hd >= drv_tab[dtype].surf) { /* bad head? */ if (first) { /* 1st xfer? */ uptr->STD = uptr->STD | STD_UNS; /* drive unsafe */ idc_done (STC_ACF); } else idc_done (STC_CYO); /* no, cyl ovf */ return TRUE; } sa = GET_SA (cy, hd, sc, dtype); /* curr disk addr */ fseek (uptr->fileref, sa * IDC_NUMBY, SEEK_SET); /* seek to pos */ idc_sec = (idc_sec + 1) & SC_MASK; /* incr disk addr */ if (idc_sec == 0) uptr->HD = uptr->HD + 1; return FALSE; } /* Data transfer done routine */ void idc_done (uint32 flg) { idc_sta = (idc_sta | STC_IDL | flg) & ~STA_BSY; /* set flag, idle */ if (idc_arm) /* if armed, intr */ SET_INT (v_IDC); int_req[l_IDC] = int_req[l_IDC] | idd_sirq; /* restore drv ints */ idd_sirq = 0; /* clear saved */ if (flg) /* if err, stop sch */ sch_stop (idc_dib.sch); return; } /* Reset routine */ t_stat idc_reset (DEVICE *dptr) { uint32 u; UNIT *uptr; idc_sta = STC_IDL | STA_BSY; /* idle, busy */ idc_wdptr = 0; idd_wdptr = 0; idc_1st = 0; /* clear flag */ idc_svun = idc_db = 0; /* clear unit, buf */ idc_sec = 0; /* clear addr */ idc_hcyl = 0; CLR_INT (v_IDC); /* clear ctrl int */ CLR_ENB (v_IDC); /* clear ctrl enb */ idc_arm = 0; /* clear ctrl arm */ idd_sirq = 0; for (u = 0; u < ID_NUMDR; u++) { /* loop thru units */ uptr = idc_dev.units + u; uptr->CYL = uptr->STD = 0; uptr->HD = uptr->FNC = 0; idd_dcy[u] = 0; CLR_INT (v_IDC + u + 1); /* clear intr */ CLR_ENB (v_IDC + u + 1); /* clear enable */ idd_arm[u] = 0; /* clear arm */ sim_cancel (uptr); /* cancel activity */ } return SCPE_OK; } /* Attach routine (with optional autosizing) */ t_stat idc_attach (UNIT *uptr, char *cptr) { uint32 i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; uptr->CYL = 0; if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; if ((p = ftell (uptr->fileref)) == 0) return SCPE_OK; for (i = 0; drv_tab[i].surf != 0; i++) { if (p <= drv_tab[i].size) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Set size command validation routine */ t_stat idc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[GET_DTYPE (val)].size; return SCPE_OK; } simh-3.8.1/Interdata/id16_sys.c0000644000175000017500000005500011112026546014357 0ustar vlmvlm/* id16_sys.c: Interdata 16b simulator interface Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices 18-Oct-06 RMS Re-ordered device list 26-Mar-04 RMS Fixed warning with -std=c99 27-Feb-03 RMS Added relative addressing support */ #include "id_defs.h" #include #define MSK_SBF 0x0100 extern DEVICE cpu_dev; extern DEVICE sch_dev; extern DEVICE pt_dev; extern DEVICE tt_dev, ttp_dev; extern DEVICE pas_dev, pasl_dev; extern DEVICE lpt_dev; extern DEVICE pic_dev, lfc_dev; extern DEVICE dp_dev, idc_dev; extern DEVICE fd_dev, mt_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint16 *M; t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val); t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val); extern t_stat lp_load (FILE *fileref, char *cptr, char *fnam); extern t_stat pt_dump (FILE *of, char *cptr, char *fnam); /* 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[] = "Interdata 16b"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 2; DEVICE *sim_devices[] = { &cpu_dev, &sch_dev, &pic_dev, &lfc_dev, &pt_dev, &tt_dev, &ttp_dev, &pas_dev, &pasl_dev, &lpt_dev, &dp_dev, &idc_dev, &fd_dev, &mt_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Reserved instruction", "HALT instruction", "Breakpoint", "Wait state", "Runaway VFU" }; /* Binary loader -- load carriage control tape Binary dump -- paper tape dump */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { if (flag) return pt_dump (fileref, cptr, fnam); return lp_load (fileref, cptr, fnam); } /* Symbol tables */ #define I_V_FL 16 /* class bits */ #define I_M_FL 0xF /* class mask */ #define I_V_MR 0x0 /* mask-register */ #define I_V_RR 0x1 /* register-register */ #define I_V_R 0x2 /* register */ #define I_V_MX 0x3 /* mask-memory */ #define I_V_RX 0x4 /* register-memory */ #define I_V_X 0x5 /* memory */ #define I_V_FF 0x6 /* float reg-reg */ #define I_V_FX 0x7 /* float reg-mem */ #define I_V_SI 0x8 /* short immed */ #define I_V_SB 0x9 /* short branch */ #define I_V_SX 0xA /* short ext branch */ #define I_MR (I_V_MR << I_V_FL) #define I_RR (I_V_RR << I_V_FL) #define I_R (I_V_R << I_V_FL) #define I_MX (I_V_MX << I_V_FL) #define I_RX (I_V_RX << I_V_FL) #define I_X (I_V_X << I_V_FL) #define I_FF (I_V_FF << I_V_FL) #define I_FX (I_V_FX << I_V_FL) #define I_SI (I_V_SI << I_V_FL) #define I_SB (I_V_SB << I_V_FL) #define I_SX (I_V_SX << I_V_FL) #define R_X 0 /* no reg */ #define R_M 1 /* reg mask */ #define R_R 2 /* reg int reg */ #define R_F 3 /* reg flt reg */ static const int32 masks[] = { 0xFF00, 0xFF00, 0xFFF0, 0xFF00, 0xFF00, 0xFFF0, 0xFF00, 0xFF00, 0xFF00, 0xFE00, 0xFEF0 }; static const uint32 r1_type[] = { R_M, R_R, R_X, R_M, R_R, R_X, R_F, R_F, R_R, R_M, R_X }; static const uint32 r2_type[] = { R_X, R_R, R_R, R_X, R_X, R_X, R_F, R_X, R_M, R_X, R_X }; static const char *opcode[] = { "BER", "BNER","BZR", "BNZR", "BPR", "BNPR","BLR", "BNLR", "BMR", "BNMR","BOR", "BNOR", "BCR", "BNCR","BR", "BES", "BNES","BZS", "BNZS", "BPS", "BNPS","BLS", "BNLS", "BMS", "BNMS","BOS", "BNOS", "BCS", "BNCS","BS", "BE", "BNE", "BZ", "BNZ", "BP", "BNP", "BL", "BNL", "BM", "BNM", "BO", "BNO", "BC", "BNC", "B", "BALR","BTCR","BFCR", "NHR", "CLHR","OHR", "XHR", "LHR", "CHR", "AHR", "SHR", "MHR", "DHR", "ACHR","SCHR", "SETMR", "BTBS","BTFS","BFBS","BFFS", "LIS", "LCS", "AIS", "SIS", "LER", "CER", "AER", "SER", "MER", "DER", "FXR", "FLR", "LPSR", "LDR", "CDR", "ADR", "SDR", "MDR", "DDR", "FXDR","FLDR", "STH", "BAL", "BTC", "BFC", "NH", "CLH", "OH", "XH", "LH", "CH", "AH", "SH", "MH", "DH", "ACH", "SCH", "SETM", "STE", "AHM", "ATL", "ABL", "RTL", "RBL", "LE", "CE", "AE", "SE", "ME", "DE", "STD", "STME","LME", "LPS", "LD", "CD", "AD", "SD", "MD", "DD", "STMD","LMD", "SRLS","SLLS","STBR","LBR", "EXBR","EPSR","WBR", "RBR", "WHR", "RHR", "WDR", "RDR", "MHUR","SSR", "OCR", "AIR", "BXH", "BXLE","LPSW","THI", "NHI", "CLHI","OHI", "XHI", "LHI", "CHI", "AHI", "SHI", "SRHL","SLHL","SRHA","SLHA", "STM", "LM", "STB", "LB", "CLB", "AL", "WB", "RB", "WH", "RH", "WD", "RD", "MHU", "SS", "OC", "AI", "SVC", "SINT", "RRL", "RLL", "SRL", "SLL", "SRA", "SLA", NULL }; static const uint32 opc_val[] = { 0x0330+I_R, 0x0230+I_R, 0x0330+I_R, 0x0230+I_R, 0x0220+I_R, 0x0320+I_R, 0x0280+I_R, 0x0380+I_R, 0x0210+I_R, 0x0310+I_R, 0x0240+I_R, 0x0340+I_R, 0x0280+I_R, 0x0380+I_R, 0x0300+I_R, 0x2230+I_SX, 0x2030+I_SX, 0x2230+I_SX, 0x2030+I_SX, 0x2020+I_SX, 0x2220+I_SX, 0x2080+I_SX, 0x2280+I_SX, 0x2010+I_SX, 0x2210+I_SX, 0x2040+I_SX, 0x2240+I_SX, 0x2080+I_SX, 0x2280+I_SX, 0x2200+I_SX, 0x4330+I_X, 0x4230+I_X, 0x4330+I_X, 0x4230+I_X, 0x4220+I_X, 0x4320+I_X, 0x4280+I_X, 0x4380+I_X, 0x4210+I_X, 0x4310+I_X, 0x4240+I_X, 0x4340+I_X, 0x4280+I_X, 0x4380+I_X, 0x4300+I_X, 0x0100+I_RR, 0x0200+I_MR, 0x0300+I_MR, 0x0400+I_RR, 0x0500+I_RR, 0x0600+I_RR, 0x0700+I_RR, 0x0800+I_RR, 0x0900+I_RR, 0x0A00+I_RR, 0x0B00+I_RR, 0x0C00+I_RR, 0x0D00+I_RR, 0x0E00+I_RR, 0x0F00+I_RR, 0x1300+I_RR, 0x2000+I_SB, 0x2100+I_SB, 0x2200+I_SB, 0x2300+I_SB, 0x2400+I_SI, 0x2500+I_SI, 0x2600+I_SI, 0x2700+I_SI, 0x2800+I_FF, 0x2900+I_FF, 0x2A00+I_FF, 0x2B00+I_FF, 0x2C00+I_FF, 0x2D00+I_FF, 0x2E00+I_RR, 0x2F00+I_RR, 0x3300+I_R, 0x3800+I_FF, 0x3900+I_FF, 0x3A00+I_FF, 0x3B00+I_FF, 0x3C00+I_FF, 0x3D00+I_FF, 0x3E00+I_RR, 0x3F00+I_RR, 0x4000+I_RX, 0x4100+I_RX, 0x4200+I_MX, 0x4300+I_MX, 0x4400+I_RX, 0x4500+I_RX, 0x4600+I_RX, 0x4700+I_RX, 0x4800+I_RX, 0x4900+I_RX, 0x4A00+I_RX, 0x4B00+I_RX, 0x4C00+I_RX, 0x4D00+I_RX, 0x4E00+I_RX, 0x4F00+I_RX, 0x5300+I_RX, 0x6000+I_RX, 0x6100+I_RX, 0x6400+I_RX, 0x6500+I_RX, 0x6600+I_RX, 0x6700+I_RX, 0x6800+I_FX, 0x6900+I_FX, 0x6A00+I_FX, 0x6B00+I_FX, 0x6C00+I_FX, 0x6D00+I_FX, 0x7000+I_FX, 0x7100+I_FX, 0x7200+I_FX, 0x7300+I_X, 0x7800+I_FX, 0x7900+I_FX, 0x7A00+I_FX, 0x7B00+I_FX, 0x7C00+I_FX, 0x7D00+I_FX, 0x7E00+I_FX, 0x7F00+I_FX, 0x9000+I_SI, 0x9100+I_SI, 0x9200+I_RR, 0x9300+I_RR, 0x9400+I_RR, 0x9500+I_RR, 0x9600+I_RR, 0x9700+I_RR, 0x9800+I_RR, 0x9900+I_RR, 0x9A00+I_RR, 0x9B00+I_RR, 0x9C00+I_RR, 0x9D00+I_RR, 0x9E00+I_RR, 0x9F00+I_RR, 0xC000+I_RX, 0xC100+I_RX, 0xC200+I_X, 0xC300+I_RX, 0xC400+I_RX, 0xC500+I_RX, 0xC600+I_RX, 0xC700+I_RX, 0xC800+I_RX, 0xC900+I_RX, 0xCA00+I_RX, 0xCB00+I_RX, 0xCC00+I_RX, 0xCD00+I_RX, 0xCE00+I_RX, 0xCF00+I_RX, 0xD000+I_RX, 0xD100+I_RX, 0xD200+I_RX, 0xD300+I_RX, 0xD400+I_RX, 0xD500+I_X, 0xD600+I_RX, 0xD700+I_RX, 0xD800+I_RX, 0xD900+I_RX, 0xDA00+I_RX, 0xDB00+I_RX, 0xDC00+I_RX, 0xDD00+I_RX, 0xDE00+I_RX, 0xDF00+I_RX, 0xE100+I_RX, 0xE200+I_RX, 0xEA00+I_RX, 0xEB00+I_RX, 0xEC00+I_RX, 0xED00+I_RX, 0xEE00+I_RX, 0xEF00+I_RX, 0xFFFF }; /* Symbolic decode Inputs: *of = output stream addr = current PC *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 bflag, c1, c2, rdx; t_stat r; DEVICE *dptr; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) /* 8b dev? */ bflag = 1; else bflag = 0; /* assume 16b */ if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if (sw & SWMASK ('A')) { /* ASCII char? */ if (bflag) c1 = val[0] & 0x7F; else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0x7F; /* get byte */ fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); return 0; } if (sw & SWMASK ('B')) { /* byte? */ if (bflag) c1 = val[0] & 0xFF; else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0xFF; /* get byte */ fprint_val (of, c1, rdx, 8, PV_RZRO); return 0; } if (bflag) /* 16b only */ return SCPE_ARG; if (sw & SWMASK ('C')) { /* string? */ c1 = (val[0] >> 8) & 0x7F; c2 = val[0] & 0x7F; fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); return -1; } if (sw & SWMASK ('F')) { /* fullword? */ fprint_val (of, (val[0] << 16) | val[1], rdx, 32, PV_RZRO); return -3; } if (sw & SWMASK ('M')) { /* inst format? */ r = fprint_sym_m (of, addr, val); /* decode inst */ if (r <= 0) return r; } fprint_val (of, val[0], rdx, 16, PV_RZRO); return -1; } /* Symbolic decode for -m Inputs: of = output stream addr = current PC *val = values to decode Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val) { uint32 i, j, inst, r1, r2, ea, vp; vp = 0; inst = val[0]; /* first 16b */ ea = val[1]; /* second 16b */ for (i = 0; opcode[i] != NULL; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & 0xFFFF) == (inst & masks[j])) { /* match? */ r1 = (inst >> 4) & 0xF; r2 = inst & 0xF; fprintf (of, "%s ", opcode[i]); /* print opcode */ switch (j) { /* case on class */ case I_V_MR: /* mask-register */ fprintf (of, "%-X,R%d", r1, r2); return -1; case I_V_RR: /* register-register */ case I_V_FF: /* floating-floating */ fprintf (of, "R%d,R%d", r1, r2); return -1; case I_V_SI: /* short immediate */ fprintf (of, "R%d,%-X", r1, r2); return -1; case I_V_SB: /* short branch */ fprintf (of, "%-X,", r1); case I_V_SX: /* ext short branch */ fprintf (of, "%-X", ((inst & MSK_SBF)? (addr + r2 + r2): (addr - r2 - r2))); return -1; case I_V_R: /* register */ fprintf (of, "R%d", r2); return -1; case I_V_MX: /* mask-memory */ fprintf (of, "%-X,%-X", r1, ea); break; case I_V_RX: /* register-memory */ case I_V_FX: /* floating-memory */ fprintf (of, "R%d,%-X", r1, ea); break; case I_V_X: /* memory */ fprintf (of, "%-X", ea); break; } /* end case */ if (r2) fprintf (of, "(R%d)", r2); return -3; } /* end if */ } /* end for */ return SCPE_ARG; /* no match */ } /* Register number Inputs: *cptr = pointer to input string **optr = pointer to pointer to next char rtype = mask, integer, or float Outputs: rnum = output register number, -1 if error */ int32 get_reg (char *cptr, char **optr, int32 rtype) { int32 reg; if ((*cptr == 'R') || (*cptr == 'r')) { /* R? */ cptr++; /* skip */ if (rtype == R_M) /* cant be mask */ return -1; } if ((*cptr >= '0') && (*cptr <= '9')) { reg = *cptr++ - '0'; if ((*cptr >= '0') && (*cptr <= '9')) reg = (reg * 10) + (*cptr - '0'); else --cptr; if (reg > 0xF) return -1; } else if ((*cptr >= 'a') && (*cptr <= 'f')) reg = (*cptr - 'a') + 10; else if ((*cptr >= 'A') && (*cptr <= 'F')) reg = (*cptr - 'A') + 10; else return -1; if ((rtype == R_F) && (reg & 1)) return -1; *optr = cptr + 1; return reg; } /* Address Inputs: *cptr = pointer to input string **tptr = pointer to moved pointer *ea = effective address addr = base address Outputs: status = SCPE_OK if ok, else error code */ t_stat get_addr (char *cptr, char **tptr, t_addr *ea, t_addr addr) { int32 sign = 1; if (*cptr == '.') { /* relative? */ cptr++; *ea = addr; if (*cptr == '+') /* .+? */ cptr++; else if (*cptr == '-') { /* .-? */ sign = -1; cptr++; } else return SCPE_OK; } else *ea = 0; errno = 0; *ea = *ea + (sign * ((int32) strtoul (cptr, tptr, 16))); if (errno || (cptr == *tptr)) return SCPE_ARG; return SCPE_OK; } /* Symbolic input */ t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 bflag, by, rdx, num; t_stat r; DEVICE *dptr; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) /* 8b dev? */ bflag = 1; else bflag = 0; /* assume 16b */ if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; if (bflag) val[0] = (t_value) cptr[0]; else val[0] = (addr & 1)? (val[0] & ~0xFF) | ((t_value) cptr[0]): (val[0] & 0xFF) | (((t_value) cptr[0]) << 8); return 0; } if (sw & SWMASK ('B')) { /* byte? */ by = get_uint (cptr, rdx, DMASK8, &r); /* get byte */ if (r != SCPE_OK) return SCPE_ARG; if (bflag) val[0] = by; else val[0] = (addr & 1)? (val[0] & ~0xFF) | by: (val[0] & 0xFF) | (by << 8); return 0; } if (bflag) /* 16b only */ return SCPE_ARG; if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII chars? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = ((t_value) cptr[0] << 8) | (t_value) cptr[1]; return -1; } if (sw & SWMASK ('F')) { num = (int32) get_uint (cptr, rdx, DMASK32, &r); /* get number */ if (r != SCPE_OK) return r; val[0] = (num >> 16) & DMASK16; val[1] = num & DMASK16; return -3; } r = parse_sym_m (cptr, addr, val); /* try to parse inst */ if (r <= 0) return r; val[0] = (int32) get_uint (cptr, rdx, DMASK16, &r); /* get number */ if (r != SCPE_OK) return r; return -1; } /* Symbolic input for -m Inputs: *cptr = pointer to input string addr = current PC *val = pointer to output values cf = true if parsing for CPU Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val) { uint32 i, j, t, df, db, inst; int32 st, r1, r2; t_stat r; char *tptr, gbuf[CBUFSIZE]; 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; inst = opc_val[i] & 0xFFFF; /* get value */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if (r1_type[j]) { /* any R1 field? */ cptr = get_glyph (cptr, gbuf, ','); /* get R1 field */ if ((r1 = get_reg (gbuf, &tptr, r1_type[j])) < 0) return SCPE_ARG; if (*tptr != 0) /* all done? */ return SCPE_ARG; inst = inst | (r1 << 4); /* or in R1 */ } cptr = get_glyph (cptr, gbuf, 0); /* get operand */ if (*cptr) /* should be end */ return SCPE_ARG; switch (j) { /* case on class */ case I_V_FF: case I_V_SI: /* flt-flt, sh imm */ case I_V_MR: case I_V_RR: /* mask/reg-reg */ case I_V_R: /* register */ if ((r2 = get_reg (gbuf, &tptr, r2_type[j])) < 0) return SCPE_ARG; if (*tptr != 0) /* all done? */ return SCPE_ARG; inst = inst | r2; /* or in R2 */ break; case I_V_FX: /* float-memory */ case I_V_MX: case I_V_RX: /* mask/reg-mem */ case I_V_X: /* memory */ r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ if ((r != SCPE_OK) || (t > PAMASK16)) return SCPE_ARG; if (*tptr == '(') { /* index? */ if ((r2 = get_reg (tptr + 1, &tptr, R_R)) < 0) return SCPE_ARG; if (*tptr++ != ')') return SCPE_ARG; inst = inst | r2; /* or in R2 */ } if (*tptr != 0) return SCPE_ARG; val[0] = inst; val[1] = t; return -3; case I_V_SB: case I_V_SX: /* short branches */ r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ if ((r != SCPE_OK) || (t & 1) || *tptr) /* error if odd */ return SCPE_ARG; st = t; /* signed version */ db = (addr - t) & 0x1F; /* back displ */ df = (t - addr) & 0x1F; /* fwd displ */ if ((t == ((addr - db) & VAMASK16)) && /* back work and */ ((j == I_V_SX) || !(inst & MSK_SBF))) /* ext or back br? */ inst = inst | (db >> 1); /* or in back displ */ else if ((t == ((addr + df) & VAMASK16)) && /* fwd work and */ ((j == I_V_SX) || (inst & MSK_SBF))) /* ext or fwd br? */ inst = inst | (df >> 1) | MSK_SBF; /* or in fwd displ */ else return SCPE_ARG; break; } /* end case */ val[0] = inst; return -1; } simh-3.8.1/Interdata/id_io.c0000644000175000017500000005424311112563464014016 0ustar vlmvlm/* id_io.c: Interdata CPU-independent I/O routines Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Mar-06 RMS Fixed bug, GO preserves EXA and SSTA (found by Davis Johnson) 21-Jun-03 RMS Changed subroutine argument for ARM compiler conflict Interdata I/O devices are defined by a device information block: dno base device number sch selector channel, -1 if none irq interrupt request flag tplte device number template, NULL if one device number iot I/O processing routine ini initialization routine Interdata I/O uses the following interconnected tables: dev_tab[dev] Indexed by device number, points to the I/O instruction processing routine for the device. sch_tab[dev] Indexed by device number, if non-zero, the number + 1 of the selector channel used by the device. int_req[level] Indexed by interrupt level, device interrupt flags. int_enb[level] Indexed by interrupt level, device interrupt enable flags. int_tab[idx] Indexed by ((interrupt level * 32) + interrupt number), maps bit positions in int_req to device numbers. */ #include "id_defs.h" /* Selector channel */ #define SCHC_EXA 0x40 /* read ext addr */ #define SCHC_RD 0x20 /* read */ #define SCHC_GO 0x10 /* go */ #define SCHC_STOP 0x08 /* stop */ #define SCHC_SSTA 0x04 /* sel ch status */ #define SCHC_EXM 0x03 /* ext mem */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; extern uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout); extern uint32 pawidth; extern UNIT cpu_unit; extern FILE *sim_log; extern DEVICE *sim_devices[]; uint32 sch_max = 2; /* sch count */ uint32 sch_sa[SCH_NUMCH] = { 0 }; /* start addr */ uint32 sch_ea[SCH_NUMCH] = { 0 }; /* end addr */ uint8 sch_sdv[SCH_NUMCH] = { 0 }; /* device */ uint8 sch_cmd[SCH_NUMCH] = { 0 }; /* command */ uint8 sch_rdp[SCH_NUMCH] = { 0 }; /* read ptr */ uint8 sch_wdc[SCH_NUMCH] = { 0 }; /* write ctr */ uint32 sch_tab[DEVNO] = { 0 }; /* dev to sch map */ uint32 int_tab[INTSZ * 32] = { 0 }; /* int to dev map */ uint8 sch_tplte[SCH_NUMCH + 1]; /* dnum template */ uint32 sch (uint32 dev, uint32 op, uint32 dat); void sch_ini (t_bool dtpl); t_stat sch_reset (DEVICE *dptr); t_stat sch_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sch_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat sch_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc); /* Selector channel data structures sch_dev channel device descriptor sch_unit channel unit descriptor sch_mod channel modifiers list sch_reg channel register list */ DIB sch_dib = { d_SCH, -1, v_SCH, sch_tplte, &sch, &sch_ini }; UNIT sch_unit = { UDATA (NULL, 0, 0) }; REG sch_reg[] = { { HRDATA (CHAN, sch_max, 3), REG_HRO }, { BRDATA (SA, sch_sa, 16, 20, SCH_NUMCH) }, { BRDATA (EA, sch_ea, 16, 20, SCH_NUMCH) }, { BRDATA (CMD, sch_cmd, 16, 8, SCH_NUMCH) }, { BRDATA (DEV, sch_sdv, 16, 8, SCH_NUMCH) }, { BRDATA (RDP, sch_rdp, 16, 2, SCH_NUMCH) }, { BRDATA (WDC, sch_wdc, 16, 3, SCH_NUMCH) }, { GRDATA (IREQ, int_req[l_SCH], 16, SCH_NUMCH, i_SCH) }, { GRDATA (IENB, int_enb[l_SCH], 16, SCH_NUMCH, i_SCH) }, { HRDATA (DEVNO, sch_dib.dno, 8), REG_HRO }, { NULL } }; MTAB sch_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "channels", "CHANNELS", &sch_set_nchan, &sch_show_nchan, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "0", NULL, NULL, &sch_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "1", NULL, NULL, &sch_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 2, "2", NULL, NULL, &sch_show_reg, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 3, "3", NULL, NULL, &sch_show_reg, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, &sch_dib }, { 0 } }; DEVICE sch_dev = { "SELCH", &sch_unit, sch_reg, sch_mod, 1, 16, 8, 1, 16, 8, NULL, NULL, &sch_reset, NULL, NULL, NULL, &sch_dib, 0 }; /* (Extended) selector channel There are really three different selector channels: - 16b selector channel (max 4B of data) - 18b selector channel (max 4B of data) - 20b selector channel (max 6B of data) The algorithm for loading the start and end addresses is taken from the maintenance manual for the Extended Selector Channel. */ #define SCH_EXR(ch) ((sch_cmd[ch] & SCHC_EXA) && (pawidth == PAWIDTH32)) uint32 sch (uint32 dev, uint32 op, uint32 dat) { uint32 t, bank, sdv, ch = dev - sch_dib.dno; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_RD: /* read data */ t = (sch_sa[ch] >> (sch_rdp[ch] * 8)) & DMASK8; /* get sa byte */ if (sch_rdp[ch] == 0) sch_rdp[ch] = /* wrap? */ SCH_EXR (ch)? 2: 1; else sch_rdp[ch] = sch_rdp[ch] - 1; /* dec byte ptr */ return t; case IO_WD: /* write data */ if (pawidth != PAWIDTH32) { /* 16b? max 4 */ if (sch_wdc[ch] >= 4) /* stop at 4 */ break; sch_sa[ch] = ((sch_sa[ch] << 8) | /* ripple ea to sa */ (sch_ea[ch] >> 8)) & DMASK16; sch_ea[ch] = ((sch_ea[ch] << 8) | /* ripple ea low */ dat) & DMASK16; /* insert byte */ } else { /* 32b? max 6 */ if (sch_wdc[ch] >= 6) /* stop at 6 */ break; if (sch_wdc[ch] != 5) { /* if not last */ sch_sa[ch] = ((sch_sa[ch] << 8) | /* ripple ea<15:8> to sa */ ((sch_ea[ch] >> 8) & DMASK8)) & PAMASK32; sch_ea[ch] = /* ripple ea<7:0> */ (((sch_ea[ch] & DMASK8) << 8) | dat) & PAMASK32; } else sch_ea[ch] = ((sch_ea[ch] << 8) | dat) & PAMASK32; } sch_wdc[ch] = sch_wdc[ch] + 1; /* adv sequencer */ break; case IO_SS: /* status */ if (sch_cmd[ch] & SCHC_GO) /* test busy */ return STA_BSY; if (sch_cmd[ch] & SCHC_SSTA) /* test sch sta */ return 0; else { sdv = sch_sdv[ch]; /* get dev */ if (dev_tab[sdv] == 0) /* not there? */ return CC_V; dev_tab[sdv] (sdv, IO_ADR, 0); /* select dev */ t = dev_tab[sdv] (sdv, IO_SS, 0); /* get status */ return t & ~STA_BSY; /* clr busy */ } case IO_OC: /* command */ bank = 0; /* assume no bank */ if (pawidth != PAWIDTH32) { /* 16b/18b proc? */ dat = dat & ~(SCHC_EXA | SCHC_SSTA); /* clr ext func */ if (pawidth == PAWIDTH16E) /* 18b proc? */ bank = (dat & SCHC_EXM) << 16; } if (dat & SCHC_STOP) { /* stop? */ sch_cmd[ch] = dat & (SCHC_EXA | SCHC_SSTA); /* clr go */ CLR_INT (v_SCH + ch); /* clr intr */ sch_rdp[ch] = SCH_EXR (ch)? 2: 1; /* init sequencers */ sch_wdc[ch] = 0; } else if (dat & SCHC_GO) { /* go? */ sch_cmd[ch] = dat & (SCHC_EXA | SCHC_SSTA| SCHC_GO | SCHC_RD); if (sch_wdc[ch] <= 4) { /* 4 bytes? */ sch_sa[ch] = (sch_sa[ch] & PAMASK16) | bank; /* 16b addr */ sch_ea[ch] = (sch_ea[ch] & PAMASK16) | bank; } sch_sa[ch] = sch_sa[ch] & ~1; if (sch_ea[ch] <= sch_sa[ch]) /* wrap? */ sch_ea[ch] = sch_ea[ch] | /* modify end addr */ ((pawidth == PAWIDTH32)? PAMASK32: PAMASK16); } break; } return 0; } /* CPU call to test if channel blocks access to device */ t_bool sch_blk (uint32 dev) { uint32 ch = sch_tab[dev] - 1; if ((ch < sch_max) && (sch_cmd[ch] & SCHC_GO)) return TRUE; return FALSE; } /* Device call to 'remember' last dev on channel */ void sch_adr (uint32 ch, uint32 dev) { if (ch < sch_max) sch_sdv[ch] = dev; return; } /* Device call to see if selector channel is active for device */ t_bool sch_actv (uint32 ch, uint32 dev) { if ((ch < sch_max) && /* chan valid, */ (sch_cmd[ch] & SCHC_GO) && /* on, and */ (sch_sdv[ch] == dev)) /* set for dev? */ return TRUE; return FALSE; /* no */ } /* Device call to read a block of memory */ uint32 sch_rdmem (uint32 ch, uint8 *buf, uint32 cnt) { uint32 addr, end, xfr, inc; if ((ch >= sch_max) || ((sch_cmd[ch] & SCHC_GO) == 0)) return 0; addr = sch_sa[ch]; /* start */ end = sch_ea[ch]; /* end */ xfr = MIN (cnt, end - addr + 1); /* sch xfr cnt */ inc = IOReadBlk (addr, xfr, buf); /* read mem */ if ((addr + inc) > end) { /* end? */ SET_INT (v_SCH + ch); /* interrupt */ sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ sch_sa[ch] = sch_sa[ch] + inc - 1; /* end addr */ } else sch_sa[ch] = sch_sa[ch] + inc; /* next addr */ return inc; } /* Device call to write a block of memory */ uint32 sch_wrmem (uint32 ch, uint8 *buf, uint32 cnt) { uint32 addr, end, xfr, inc; if ((ch >= sch_max) || ((sch_cmd[ch] & SCHC_GO) == 0)) return 0; addr = sch_sa[ch]; /* start */ end = sch_ea[ch]; /* end */ xfr = MIN (cnt, end - addr + 1); /* sch xfr cnt */ inc = IOWriteBlk (addr, xfr, buf); /* write mem */ if ((addr + inc) > end) { /* end? */ SET_INT (v_SCH + ch); /* interrupt */ sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ sch_sa[ch] = sch_sa[ch] + inc - 1; /* end addr */ } else sch_sa[ch] = sch_sa[ch] + inc; /* next addr */ return inc; } /* Device call to stop a selector channel */ void sch_stop (uint32 ch) { if (ch < sch_max) { SET_INT (v_SCH + ch); /* interrupt */ sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ } return; } /* Reset */ void sch_reset_ch (uint32 rst_lim) { uint32 ch; for (ch = 0; ch < SCH_NUMCH; ch++) { if (ch >= rst_lim) { CLR_INT (v_SCH + ch); SET_ENB (v_SCH + ch); sch_sa[ch] = sch_ea[ch] = 0; sch_cmd[ch] = sch_sdv[ch] = 0; sch_wdc[ch] = 0; sch_rdp[ch] = 1; } } return; } t_stat sch_reset (DEVICE *dptr) { sch_reset_ch (0); /* reset all chan */ return SCPE_OK; } /* Set number of channels */ t_stat sch_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 i, newmax; t_stat r; if (cptr == NULL) return SCPE_ARG; newmax = get_uint (cptr, 10, SCH_NUMCH, &r); /* get new max */ if ((r != SCPE_OK) || (newmax == sch_max)) /* err or no chg? */ return r; if (newmax == 0) /* must be > 0 */ return SCPE_ARG; if (newmax < sch_max) { /* reducing? */ for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && (dibp->sch >= (int32) newmax)) { /* dev using chan? */ printf ("Device %02X uses channel %d\n", dibp->dno, dibp->sch); if (sim_log) fprintf (sim_log, "Device %02X uses channel %d\n", dibp->dno, dibp->sch); return SCPE_OK; } } } sch_max = newmax; /* set new max */ sch_reset_ch (sch_max); /* reset chan */ return SCPE_OK; } /* Show number of channels */ t_stat sch_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "channels=%d", sch_max); return SCPE_OK; } /* Show channel registers */ t_stat sch_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc) { if (val < 0) return SCPE_IERR; if (val >= (int32) sch_max) fprintf (st, "Channel %d disabled\n", val); else { fprintf (st, "SA: %05X\n", sch_sa[val]); fprintf (st, "EA: %05X\n", sch_ea[val]); fprintf (st, "CMD: %02X\n", sch_cmd[val]); fprintf (st, "DEV: %02X\n", sch_sdv[val]); fprintf (st, "RDP: %X\n", sch_rdp[val]); fprintf (st, "WDC: %X\n", sch_wdc[val]); } return SCPE_OK; } /* Initialize template */ void sch_ini (t_bool dtpl) { uint32 i; for (i = 0; i < sch_max; i++) sch_tplte[i] = i; sch_tplte[sch_max] = TPL_END; return; } /* Evaluate interrupt */ void int_eval (void) { int i; extern uint32 qevent; for (i = 0; i < INTSZ; i++) { if (int_req[i] & int_enb[i]) { qevent = qevent | EV_INT; return; } } qevent = qevent & ~EV_INT; return; } /* Return interrupting device */ uint32 int_getdev (void) { int32 i, j, t; uint32 r; for (i = t = 0; i < INTSZ; i++) { /* loop thru array */ if (r = int_req[i] & int_enb[i]) { /* find nz int wd */ for (j = 0; j < 32; t++, j++) { if (r & (1u << j)) { int_req[i] = int_req[i] & ~(1u << j); /* clr request */ return int_tab[t]; } } } else t = t + 32; } return 0; } /* Update device interrupt status */ int32 int_chg (uint32 irq, int32 dat, int32 armdis) { int32 t = CMD_GETINT (dat); /* get int ctrl */ if (t == CMD_IENB) { /* enable? */ SET_ENB (irq); return 1; } else if (t == CMD_IDIS) { /* disable? */ CLR_ENB (irq); return 1; } if (t == CMD_IDSA) { /* disarm? */ CLR_ENB (irq); CLR_INT (irq); return 0; } return armdis; } /* Process a 2b field and return unchanged, set, clear, complement */ int32 io_2b (int32 val, int32 pos, int32 old) { int32 t = (val >> pos) & 3; if (t == 0) return old; if (t == 1) return 1; if (t == 2) return 0; return old ^1; } /* Block transfer routines */ uint32 IOReadBlk (uint32 loc, uint32 cnt, uint8 *buf) { uint32 i; if (!MEM_ADDR_OK (loc) || (cnt == 0)) return 0; if (!MEM_ADDR_OK (loc + cnt - 1)) cnt = MEMSIZE - loc; for (i = 0; i < cnt; i++) buf[i] = IOReadB (loc + i); return cnt; } uint32 IOWriteBlk (uint32 loc, uint32 cnt, uint8 *buf) { uint32 i; if (!MEM_ADDR_OK (loc) || (cnt == 0)) return 0; if (!MEM_ADDR_OK (loc + cnt - 1)) cnt = MEMSIZE - loc; for (i = 0; i < cnt; i++) IOWriteB (loc + i, buf[i]); return cnt; } /* Change selector channel for a device */ t_stat set_sch (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newch; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if ((dibp == NULL) || (dibp->sch < 0)) return SCPE_IERR; newch = get_uint (cptr, 16, sch_max - 1, &r); /* get new */ if (r != SCPE_OK) return r; dibp->sch = newch; /* store */ return SCPE_OK; } /* Show selector channel for a device */ t_stat show_sch (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if ((dibp == NULL) || (dibp->sch < 0)) return SCPE_IERR; fprintf (st, "selch=%X", dibp->sch); return SCPE_OK; } /* Change device number for a device */ t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newdev; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newdev = get_uint (cptr, 16, DEV_MAX, &r); /* get new */ if ((r != SCPE_OK) || (newdev == dibp->dno)) return r; if (newdev == 0) /* must be > 0 */ return SCPE_ARG; dibp->dno = newdev; /* store */ return SCPE_OK; } /* Show device number for a device */ t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if ((dibp == NULL) || (dibp->dno == 0)) return SCPE_IERR; fprintf (st, "devno=%02X", dibp->dno); return SCPE_OK; } /* Init device tables */ t_bool devtab_init (void) { DEVICE *dptr; DIB *dibp; uint32 i, j, dno, dmsk, doff, t, dmap[DEVNO / 32]; uint8 *tplte, dflt_tplte[] = { 0, TPL_END }; /* Clear tables, device map */ for (i = 0; i < DEVNO; i++) { dev_tab[i] = NULL; sch_tab[i] = 0; } for (i = 0; i < (INTSZ * 32); i++) int_tab[i] = 0; for (i = 0; i < (DEVNO / 32); i++) dmap[i] = 0; /* Test each device for conflict; add to map; init tables */ for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((dibp == NULL) || (dptr->flags & DEV_DIS)) /* exist, enabled? */ continue; dno = dibp->dno; /* get device num */ if (dibp->ini) /* gen dno template */ dibp->ini (TRUE); tplte = dibp->tplte; /* get template */ if (tplte == NULL) /* none? use default */ tplte = dflt_tplte; for ( ; *tplte != TPL_END; tplte++) { /* loop thru template */ t = (dno + *tplte) & DEV_MAX; /* loop thru template */ dmsk = 1u << (t & 0x1F); /* bit to test */ doff = t / 32; /* word to test */ if (dmap[doff] & dmsk) { /* in use? */ printf ("Device number conflict, devno = %02X\n", t); if (sim_log) fprintf (sim_log, "Device number conflict, devno = %02X\n", t); return TRUE; } dmap[doff] = dmap[doff] | dmsk; if (dibp->sch >= 0) sch_tab[t] = dibp->sch + 1; dev_tab[t] = dibp->iot; } if (dibp->ini) /* gen int template */ dibp->ini (FALSE); tplte = dibp->tplte; /* get template */ if (tplte == NULL) /* none? use default */ tplte = dflt_tplte; for (j = dibp->irq; *tplte != TPL_END; j++, tplte++) int_tab[j] = (dno + *tplte) & DEV_MAX; } /* end for i */ return FALSE; } simh-3.8.1/Interdata/id_lp.c0000644000175000017500000003022711112026546014011 0ustar vlmvlm/* id_lp.c: Interdata line printer Copyright (c) 2001-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt M46-206 line printer 27-May-08 RMS Fixed bug in printing test (from Davis Johnson) 19-Jan-07 RMS Added UNIT_TEXT flag 25-Apr-03 RMS Revised for extended file support */ #include "id_defs.h" #include /* Device definitions */ #define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ #define UNIT_UC (1 << UNIT_V_UC) #define SPC_BASE 0x40 /* spacing base */ #define VFU_BASE 0x78 /* VFU base */ #define VFU_WIDTH 0x8 /* VFU width */ #define LF 0xA #define VT 0xB #define VT_VFU 4 /* VFU chan for VT */ #define FF 0xC #define FF_VFU 8 /* VFU chan for FF */ #define CR 0xD #define VFUP(ch,val) ((val) & (1 << (ch))) /* VFU chan test */ /* Status byte, * = dynamic */ #define STA_PAPE 0x40 /* *paper empty */ #define STA_MASK (STA_BSY) /* static status */ uint32 lpt_sta = STA_BSY; /* status */ char lpxb[LPT_WIDTH + 1]; /* line buffer */ uint32 lpt_bptr = 0; /* buf ptr */ uint32 lpt_spnd = 0; /* space pending */ uint32 lpt_vfup = 0; /* VFU ptr */ uint32 lpt_vful = 1; /* VFU lnt */ uint8 lpt_vfut[VFU_LNT] = { 0xFF }; /* VFU tape */ uint32 lpt_arm = 0; /* int armed */ int32 lpt_ctime = 10; /* char time */ int32 lpt_stime = 1000; /* space time */ int32 lpt_stopioe = 0; /* stop on err */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; DEVICE lpt_dev; uint32 lpt (uint32 dev, uint32 op, uint32 dat); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); t_stat lpt_bufout (UNIT *uptr); t_stat lpt_vfu (UNIT *uptr, int32 ch); t_stat lpt_spc (UNIT *uptr, int32 cnt); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptors lpt_reg LPT register list */ DIB lpt_dib = { d_LPT, -1, v_LPT, NULL, &lpt, NULL }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_UC+UNIT_TEXT, 0) }; REG lpt_reg[] = { { HRDATA (STA, lpt_sta, 8) }, { HRDATA (BUF, lpt_unit.buf, 7) }, { BRDATA (DBUF, lpxb, 16, 7, LPT_WIDTH) }, { HRDATA (DBPTR, lpt_bptr, 8) }, { HRDATA (VFUP, lpt_vfup, 8) }, { HRDATA (VFUL, lpt_vful, 8) }, { BRDATA (VFUT, lpt_vfut, 16, 8, VFU_LNT) }, { FLDATA (IREQ, int_req[l_LPT], i_LPT) }, { FLDATA (IENB, int_enb[l_LPT], i_LPT) }, { FLDATA (IARM, lpt_arm, 0) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT }, { DRDATA (STIME, lpt_stime, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { HRDATA (DEVNO, lpt_dib.dno, 8), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { UNIT_UC, 0, "lower case", "LC", NULL }, { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 16, 7, NULL, NULL, &lpt_reset, NULL, &lpt_attach, NULL, &lpt_dib, DEV_DISABLE }; /* Line printer: IO routine */ uint32 lpt (uint32 dev, uint32 op, uint32 dat) { int32 t; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_OC: /* command */ lpt_arm = int_chg (v_LPT, dat, lpt_arm); /* upd int ctrl */ break; case IO_WD: /* write */ t = lpt_unit.buf = dat & 0x7F; /* mask char */ lpt_sta = STA_BSY; /* set busy */ if (lpt_spnd || ((t >= LF) && (t <= CR))) /* space op? */ sim_activate (&lpt_unit, lpt_stime); else sim_activate (&lpt_unit, lpt_ctime); /* normal char */ break; case IO_SS: /* status */ t = lpt_sta & STA_MASK; /* status byte */ if ((lpt_unit.flags & UNIT_ATT) == 0) /* test paper out */ t = t | STA_EX | STA_PAPE | STA_BSY; return t; } return 0; } /* Unit service */ t_stat lpt_svc (UNIT *uptr) { int32 t; t_stat r = SCPE_OK; lpt_sta = 0; /* clear busy */ if (lpt_arm) /* armed? intr */ SET_INT (v_LPT); if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); t = uptr->buf; /* get character */ if (lpt_spnd || ((t >= LF) && (t < CR))) { /* spc pend or spc op? */ lpt_spnd = 0; if (lpt_bufout (uptr) != SCPE_OK) /* print */ return SCPE_IOERR; if ((t == 1) || (t == LF)) /* single space */ lpt_spc (uptr, 1); else if (t == VT) /* VT->VFU */ r = lpt_vfu (uptr, VT_VFU - 1); else if (t == 0xC) /* FF->VFU */ r = lpt_vfu (uptr, FF_VFU - 1); else if ((t >= SPC_BASE) && (t < VFU_BASE)) lpt_spc (uptr, t - SPC_BASE); /* space */ else if ((t >= VFU_BASE) && (t < VFU_BASE + VFU_WIDTH)) r = lpt_vfu (uptr, t - VFU_BASE); /* VFU */ else fputs ("\r", uptr->fileref); /* overprint */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (lpt_unit.fileref)) { perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } } else if (t == CR) { /* CR? */ lpt_spnd = 1; /* set spc pend */ return lpt_bufout (uptr); /* print line */ } else if (t >= 0x20) { /* printable? */ if ((uptr->flags & UNIT_UC) && islower (t)) /* UC only? */ t = toupper (t); if (lpt_bptr < LPT_WIDTH) lpxb[lpt_bptr++] = t; } return r; } /* Printing and spacing routines */ t_stat lpt_bufout (UNIT *uptr) { int32 i; t_stat r = SCPE_OK; if (lpt_bptr == 0) return SCPE_OK; /* any char in buf? */ for (i = LPT_WIDTH - 1; (i >= 0) && (lpxb[i] == ' '); i--) lpxb[i] = 0; /* backscan line */ if (lpxb[0]) { /* any char left? */ fputs (lpxb, uptr->fileref); /* write line */ lpt_unit.pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { perror ("LPT I/O error"); clearerr (uptr->fileref); r = SCPE_IOERR; } } lpt_bptr = 0; /* reset buffer */ for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' '; lpxb[LPT_WIDTH] = 0; return r; } t_stat lpt_vfu (UNIT *uptr, int32 ch) { uint32 i, j; if ((ch == (FF_VFU - 1)) && VFUP (ch, lpt_vfut[0])) { /* top of form? */ fputs ("\n\f", uptr->fileref); /* nl + ff */ lpt_vfup = 0; /* top of page */ return SCPE_OK; } for (i = 1; i < lpt_vful + 1; i++) { /* sweep thru cct */ lpt_vfup = (lpt_vfup + 1) % lpt_vful; /* adv pointer */ if (VFUP (ch, lpt_vfut[lpt_vfup])) { /* chan punched? */ for (j = 0; j < i; j++) fputc ('\n', uptr->fileref); return SCPE_OK; } } return STOP_VFU; /* runaway channel */ } t_stat lpt_spc (UNIT *uptr, int32 cnt) { int32 i; if (cnt == 0) fputc ('\r', uptr->fileref); else { for (i = 0; i < cnt; i++) fputc ('\n', uptr->fileref); lpt_vfup = (lpt_vfup + cnt) % lpt_vful; } return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { int32 i; sim_cancel (&lpt_unit); /* deactivate */ lpt_sta = 0; /* clr busy */ lpt_bptr = 0; /* clr buf ptr */ for (i = 0; i < LPT_WIDTH; i++) /* clr buf */ lpxb[i] = ' '; lpxb[LPT_WIDTH] = 0; CLR_INT (v_LPT); /* clearr int */ CLR_ENB (v_LPT); /* disable int */ lpt_arm = 0; /* disarm int */ return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { lpt_vfup = 0; /* top of form */ return attach_unit (uptr, cptr); } /* Carriage control load routine */ t_stat lp_load (FILE *fileref, char *cptr, char *fnam) { int32 col, ptr, mask, vfubuf[VFU_LNT]; uint32 rpt; t_stat r; char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; if (*cptr != 0) return SCPE_ARG; ptr = 0; for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ mask = 0; if (*cptr == '(') { /* repeat count? */ cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ rpt = get_uint (gbuf, 10, VFU_LNT, &r); /* repeat count */ if (r != SCPE_OK) return SCPE_FMT; } else rpt = 1; while (*cptr != 0) { /* get col no's */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ col = get_uint (gbuf, 10, 7, &r); /* column number */ if (r != SCPE_OK) return SCPE_FMT; mask = mask | (1 << col); /* set bit */ } for ( ; rpt > 0; rpt--) { /* store vals */ if (ptr >= VFU_LNT) return SCPE_FMT; vfubuf[ptr++] = mask; } } if (ptr == 0) return SCPE_FMT; lpt_vful = ptr; lpt_vfup = 0; for (rpt = 0; rpt < lpt_vful; rpt++) lpt_vfut[rpt] = vfubuf[rpt]; return SCPE_OK; } simh-3.8.1/Interdata/id_tt.c0000644000175000017500000002651711110076056014033 0ustar vlmvlm/* id_tt.c: Interdata teletype Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tt console 18-Jun-07 RMS Added UNIT_IDLE flag to console input 18-Oct-06 RMS Sync keyboard to LFC clock 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 22-Nov-05 RMS Revised for new terminal processing routines 29-Dec-03 RMS Added support for console backpressure 25-Apr-03 RMS Revised for extended file support 11-Jan-03 RMS Added TTP support 22-Dec-02 RMS Added break support */ #include "id_defs.h" #include /* Device definitions */ #define TTI 0 #define TTO 1 #define STA_OVR 0x80 /* overrun */ #define STA_BRK 0x20 /* break */ #define STA_MASK (STA_OVR | STA_BRK | STA_BSY) /* status mask */ #define SET_EX (STA_OVR | STA_BRK) /* set EX */ #define CMD_V_FDPX 4 /* full/half duplex */ #define CMD_V_RD 2 /* read/write */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; extern int32 lfc_poll; uint32 tt_sta = STA_BSY; /* status */ uint32 tt_fdpx = 1; /* tt mode */ uint32 tt_rd = 1, tt_chp = 0; /* tt state */ uint32 tt_arm = 0; /* int arm */ uint32 tt (uint32 dev, uint32 op, uint32 dat); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tt_reset (DEVICE *dptr); t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc); /* TT data structures tt_dev TT device descriptor tt_unit TT unit descriptors tt_reg TT register list tt_mod TT modifiers list */ DIB tt_dib = { d_TT, -1, v_TT, NULL, &tt, NULL }; UNIT tt_unit[] = { { UDATA (&tti_svc, TT_MODE_KSR|UNIT_IDLE, 0), 0 }, { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT } }; REG tt_reg[] = { { HRDATA (STA, tt_sta, 8) }, { HRDATA (KBUF, tt_unit[TTI].buf, 8) }, { DRDATA (KPOS, tt_unit[TTI].pos, T_ADDR_W), PV_LEFT }, { DRDATA (KTIME, tt_unit[TTI].wait, 24), PV_LEFT }, { HRDATA (TBUF, tt_unit[TTO].buf, 8) }, { DRDATA (TPOS, tt_unit[TTO].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TTIME, tt_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, { FLDATA (IREQ, int_req[l_TT], i_TT) }, { FLDATA (IENB, int_enb[l_TT], i_TT) }, { FLDATA (IARM, tt_arm, 0) }, { FLDATA (RD, tt_rd, 0) }, { FLDATA (FDPX, tt_fdpx, 0) }, { FLDATA (CHP, tt_chp, 0) }, { HRDATA (DEVNO, tt_dib.dno, 8), REG_HRO }, { NULL } }; MTAB tt_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tt_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tt_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tt_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tt_set_mode }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ENABLED", &tt_set_enbdis, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, DEV_DIS, NULL, "DISABLED", &tt_set_enbdis, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "BREAK", &tt_set_break, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, &tt_dib }, { 0 } }; DEVICE tt_dev = { "TT", tt_unit, tt_reg, tt_mod, 2, 10, 31, 1, 16, 8, NULL, NULL, &tt_reset, NULL, NULL, NULL, &tt_dib, 0 }; /* Terminal: IO routine */ uint32 tt (uint32 dev, uint32 op, uint32 dat) { uint32 old_rd, t; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_OC: /* command */ old_rd = tt_rd; tt_arm = int_chg (v_TT, dat, tt_arm); /* upd int ctrl */ tt_fdpx = io_2b (dat, CMD_V_FDPX, tt_fdpx); /* upd full/half */ tt_rd = io_2b (dat, CMD_V_RD, tt_rd); /* upd rd/write */ if (tt_rd != old_rd) { /* rw change? */ if (tt_rd? tt_chp: !sim_is_active (&tt_unit[TTO])) { tt_sta = 0; /* busy = 0 */ if (tt_arm) /* req intr */ SET_INT (v_TT); } else { tt_sta = STA_BSY; /* busy = 1 */ CLR_INT (v_TT); /* clr int */ } } else tt_sta = tt_sta & ~STA_OVR; /* clr ovflo */ break; case IO_RD: /* read */ tt_chp = 0; /* clear pend */ if (tt_rd) tt_sta = (tt_sta | STA_BSY) & ~STA_OVR; return (tt_unit[TTI].buf & 0xFF); case IO_WD: /* write */ tt_unit[TTO].buf = dat & 0xFF; /* save char */ if (!tt_rd) /* set busy */ tt_sta = tt_sta | STA_BSY; sim_activate (&tt_unit[TTO], tt_unit[TTO].wait); break; case IO_SS: /* status */ t = tt_sta & STA_MASK; /* get status */ if (t & SET_EX) /* test for EX */ t = t | STA_EX; return t; } return 0; } /* Unit service routines */ t_stat tti_svc (UNIT *uptr) { int32 out, temp; sim_activate (uptr, KBD_WAIT (uptr->wait, lfc_poll)); /* continue poll */ tt_sta = tt_sta & ~STA_BRK; /* clear break */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return temp; if (tt_rd) { /* read mode? */ tt_sta = tt_sta & ~STA_BSY; /* clear busy */ if (tt_arm) /* if armed, intr */ SET_INT (v_TT); if (tt_chp) /* got char? overrun */ tt_sta = tt_sta | STA_OVR; } tt_chp = 1; /* char pending */ out = temp & 0x7F; /* echo is 7B */ if (temp & SCPE_BREAK) { /* break? */ tt_sta = tt_sta | STA_BRK; /* set status */ uptr->buf = 0; /* no character */ } else uptr->buf = sim_tt_inpcvt (temp, TT_GET_MODE (uptr->flags) | TTUF_KSR); uptr->pos = uptr->pos + 1; /* incr count */ if (!tt_fdpx) { /* half duplex? */ out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (out >= 0) { /* valid echo? */ sim_putchar (out); /* write char */ tt_unit[TTO].pos = tt_unit[TTO].pos + 1; } } return SCPE_OK; } t_stat tto_svc (UNIT *uptr) { int32 ch; t_stat r; ch = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (ch >= 0) { if ((r = sim_putchar_s (ch)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); } } if (!tt_rd) { /* write mode? */ tt_sta = tt_sta & ~STA_BSY; /* clear busy */ if (tt_arm) /* if armed, intr */ SET_INT (v_TT); } uptr->pos = uptr->pos + 1; /* incr count */ return SCPE_OK; } /* Reset routine */ t_stat tt_reset (DEVICE *dptr) { if (dptr->flags & DEV_DIS) /* dis? cancel poll */ sim_cancel (&tt_unit[TTI]); else sim_activate_abs (&tt_unit[TTI], KBD_WAIT (tt_unit[TTI].wait, lfc_poll)); sim_cancel (&tt_unit[TTO]); /* cancel output */ tt_rd = tt_fdpx = 1; /* read, full duplex */ tt_chp = 0; /* no char */ tt_sta = STA_BSY; /* buffer empty */ CLR_INT (v_TT); /* clear int */ CLR_ENB (v_TT); /* disable int */ tt_arm = 0; /* disarm int */ return SCPE_OK; } /* Make mode flags uniform */ t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { tt_unit[TTO].flags = (tt_unit[TTO].flags & ~TT_MODE) | val; if (val == TT_MODE_7P) val = TT_MODE_7B; tt_unit[TTI].flags = (tt_unit[TTI].flags & ~TT_MODE) | val; return SCPE_OK; } /* Set input break */ t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc) { if (tt_dev.flags & DEV_DIS) return SCPE_NOFNC; tt_sta = tt_sta | STA_BRK; if (tt_rd) { /* read mode? */ tt_sta = tt_sta & ~STA_BSY; /* clear busy */ if (tt_arm) /* if armed, intr */ SET_INT (v_TT); } sim_cancel (&tt_unit[TTI]); /* restart TT poll */ sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* so brk is seen */ return SCPE_OK; } /* Set enabled/disabled */ t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc) { extern DEVICE ttp_dev; extern t_stat ttp_reset (DEVICE *dptr); tt_dev.flags = (tt_dev.flags & ~DEV_DIS) | val; ttp_dev.flags = (ttp_dev.flags & ~DEV_DIS) | (val ^ DEV_DIS); tt_reset (&tt_dev); ttp_reset (&ttp_dev); return SCPE_OK; } simh-3.8.1/Interdata/id_fd.c0000644000175000017500000005564211112026546013777 0ustar vlmvlm/* id_fd.c: Interdata floppy disk simulator Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. fd M46-630 floppy disk A diskette consists of 77 tracks, each with 26 sectors of 128B. The Interdata floppy uses a logical record numbering scheme from 1 to 2002. Physical tracks are numbered 0-76, physical sectors 1-26. To allow for deleted data handling, a directory is appended to the end of the image, one byte per LRN. Zero (the default) is a normal record, non-zero a deleted record. */ #include "id_defs.h" #define FD_NUMTR 77 /* tracks/disk */ #define FD_NUMSC 26 /* sectors/track */ #define FD_NUMBY 128 /* bytes/sector */ #define FD_NUMLRN (FD_NUMTR * FD_NUMSC) /* LRNs/disk */ #define FD_SIZE (FD_NUMLRN * FD_NUMBY) /* bytes/disk */ #define FD_NUMDR 4 /* drives/controller */ #define UNIT_V_WLK (UNIT_V_UF) /* write locked */ #define UNIT_WLK (1u << UNIT_V_UF) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define LRN u3 /* last LRN */ #define FNC u4 /* last function */ #define GET_DA(x) (((x) - 1) * FD_NUMBY) #define GET_TRK(x) (((x) - 1) / FD_NUMSC) #define GET_SEC(x) ((((x) - 1) % FD_NUMSC) + 1) #define LRN_BOOT 5 /* boot block LRN */ /* Command byte */ #define CMD_V_UNIT 4 /* unit */ #define CMD_M_UNIT 0x3 #define GET_UNIT(x) (((x) >> CMD_V_UNIT) & CMD_M_UNIT) #define CMD_V_FNC 0 /* function */ #define CMD_M_FNC 0xF #define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) #define FNC_RD 0x1 /* read */ #define FNC_WR 0x2 /* write */ #define FNC_RDID 0x3 /* read ID */ #define FNC_RSTA 0x4 /* read status */ #define FNC_DEL 0x5 /* write deleted */ #define FNC_BOOT 0x6 /* boot */ #define FNC_STOP 0x7 /* stop */ #define FNC_RESET 0x8 /* reset */ #define FNC_FMT 0x9 /* format NI */ #define FNC_STOPPING 0x10 /* stopping */ /* Status byte, * = dynamic */ #define STA_WRP 0x80 /* *write prot */ #define STA_DEF 0x40 /* def track NI */ #define STA_DEL 0x20 /* del record */ #define STA_ERR 0x10 /* error */ #define STA_IDL 0x02 /* idle */ #define STA_OFL 0x01 /* fault */ #define STA_MASK (STA_DEF|STA_DEL|STA_ERR|STA_BSY|STA_IDL) #define SET_EX (STA_ERR) /* set EX */ /* Extended status, 6 bytes, * = dynamic */ #define ES_SIZE 6 #define ES0_HCRC 0x80 /* ID CRC NI */ #define ES0_DCRC 0x40 /* data CRC NI */ #define ES0_LRN 0x20 /* illegal LRN */ #define ES0_WRP 0x10 /* *write prot */ #define ES0_ERR 0x08 /* error */ #define ES0_DEF 0x04 /* def trk NI */ #define ES0_DEL 0x02 /* del rec NI */ #define ES0_FLT 0x01 /* fault */ #define ES1_TK0 0x80 /* track 0 */ #define ES1_NRDY 0x40 /* not ready */ #define ES1_NOAM 0x20 /* no addr mk NI */ #define ES1_CMD 0x10 /* illegal cmd */ #define ES1_SKE 0x08 /* seek err NI */ #define ES1_UNS 0x04 /* unsafe NI */ #define ES1_UNIT 0x03 /* unit # */ /* Processing options for commands */ #define C_RD 0x1 /* cmd reads disk */ #define C_WD 0x2 /* cmd writes disk */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; uint32 fd_sta = 0; /* status */ uint32 fd_cmd = 0; /* command */ uint32 fd_db = 0; /* data buffer */ uint32 fd_bptr = 0; /* buffer pointer */ uint8 fdxb[FD_NUMBY] = { 0 }; /* sector buffer */ uint8 fd_es[FD_NUMDR][ES_SIZE] = { 0 }; /* ext status */ uint32 fd_lrn = 0; /* log rec # */ uint32 fd_wdv = 0; /* wd valid */ uint32 fd_stopioe = 1; /* stop on error */ uint32 fd_arm = 0; /* intr arm */ int32 fd_ctime = 100; /* command time */ int32 fd_stime = 10; /* seek, per LRN */ int32 fd_xtime = 1; /* tr set time */ static uint32 ctab[16] = { 0, C_RD, C_WD, 0, /* 0, rd, wr, 0 */ 0, C_WD, C_RD, 0, /* 0, del, boot, 0 */ 0, 0, 0, 0, 0, 0, 0, 0 }; DEVICE fd_dev; uint32 fd (uint32 dev, uint32 op, uint32 dat); t_stat fd_svc (UNIT *uptr); t_stat fd_reset (DEVICE *dptr); t_stat fd_clr (DEVICE *dptr); t_stat fd_boot (int32 unitno, DEVICE *dptr); t_bool fd_dte (UNIT *uptr, t_bool wr); uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt); void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1); void sched_seek (UNIT *uptr, int32 newlrn); /* FD data structures fd_dev FD device descriptor fd_unit FD unit list fd_reg FD register list fd_mod FD modifier list */ DIB fd_dib = { d_FD, -1, v_FD, NULL, &fd, NULL }; UNIT fd_unit[] = { { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) } }; REG fd_reg[] = { { HRDATA (CMD, fd_cmd, 8) }, { HRDATA (STA, fd_sta, 8) }, { HRDATA (BUF, fd_db, 8) }, { HRDATA (LRN, fd_lrn, 16) }, { BRDATA (ESTA, fd_es, 16, 8, ES_SIZE * FD_NUMDR) }, { BRDATA (DBUF, fdxb, 16, 8, FD_NUMBY) }, { HRDATA (DBPTR, fd_bptr, 8) }, { FLDATA (WDV, fd_wdv, 0) }, { FLDATA (IREQ, int_req[l_FD], i_FD) }, { FLDATA (IENB, int_enb[l_FD], i_FD) }, { FLDATA (IARM, fd_arm, 0) }, { DRDATA (CTIME, fd_ctime, 24), PV_LEFT }, { DRDATA (STIME, fd_stime, 24), PV_LEFT }, { DRDATA (XTIME, fd_xtime, 24), PV_LEFT }, { FLDATA (STOP_IOE, fd_stopioe, 0) }, { URDATA (ULRN, fd_unit[0].LRN, 16, 16, 0, FD_NUMDR, REG_HRO) }, { URDATA (UFNC, fd_unit[0].FNC, 16, 8, 0, FD_NUMDR, REG_HRO) }, { HRDATA (DEVNO, fd_dib.dno, 8), REG_HRO }, { NULL } }; MTAB fd_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE fd_dev = { "FD", fd_unit, fd_reg, fd_mod, FD_NUMDR, 16, 20, 1, 16, 8, NULL, NULL, &fd_reset, &fd_boot, NULL, NULL, &fd_dib, DEV_DISABLE }; /* Floppy disk: IO routine */ uint32 fd (uint32 dev, uint32 op, uint32 dat) { int32 u, t, fnc; UNIT *uptr; fnc = GET_FNC (fd_cmd); /* get fnc */ u = GET_UNIT (fd_cmd); /* get unit */ uptr = fd_dev.units + u; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_RD: /* read */ if (fd_sta & (STA_IDL | STA_BSY)) /* idle, busy? */ return fd_db; if (fd_bptr < FD_NUMBY) /* get byte */ fd_db = fdxb[fd_bptr++]; if (fd_bptr >= FD_NUMBY) { /* buf end? */ if (ctab[fnc] & C_RD) { /* disk read? */ sched_seek (uptr, uptr->LRN + 1); /* sched read */ fd_sta = fd_sta | STA_BSY; /* set busy */ } else fd_bptr = 0; /* just wrap */ } if ((ctab[fnc] & C_RD) && fd_arm) /* if rd & arm, */ SET_INT (v_FD); /* interrupt */ return fd_db; /* return buf */ case IO_WD: /* write */ if (fd_sta & STA_IDL) { /* idle? */ fd_lrn = ((fd_lrn << 8) | dat) & DMASK16; /* insert byte */ fd_wdv = 1; break; } if (fd_bptr < FD_NUMBY) /* if room, */ fdxb[fd_bptr++] = fd_db = dat; /* store byte */ if (fd_bptr >= FD_NUMBY) { /* buf end? */ if (ctab[fnc] & C_WD) { /* disk write? */ sched_seek (uptr, uptr->LRN + 1); /* sched write */ fd_sta = fd_sta | STA_BSY; /* set busy */ } else fd_bptr = 0; /* just wrap */ } if ((ctab[fnc] & C_WD) && fd_arm) /* if wr & arm, */ SET_INT (v_FD); /* interrupt */ break; case IO_SS: /* status */ t = fd_sta & STA_MASK; /* get status */ if ((uptr->flags & UNIT_ATT) == 0) t = t | STA_DU; if (t & SET_EX) /* test for ex */ t = t | STA_EX; return t; case IO_OC: /* command */ fd_arm = int_chg (v_FD, dat, fd_arm); /* upd int ctrl */ fnc = GET_FNC (dat); /* new fnc */ fd_cmd = dat; /* save cmd */ u = GET_UNIT (dat); /* get unit */ uptr = fd_dev.units + u; if (fnc == FNC_STOP) { /* stop? */ uptr->FNC = uptr->FNC | FNC_STOPPING; /* flag stop */ if (sim_is_active (uptr)) /* busy? cont */ break; if (ctab[GET_FNC (uptr->FNC)] & C_WD) { /* write? */ sched_seek (uptr, uptr->LRN + 1); /* sched write */ fd_sta = fd_sta | STA_BSY; /* set busy */ } else fd_done (u, 0, 0, 0); /* nrml done */ break; } else if (fd_sta & STA_IDL) { /* must be idle */ if (fnc != FNC_RSTA) { /* !rd status */ fd_sta = STA_BSY; /* busy, !idle */ fd_es[u][0] = 0; fd_es[u][1] = u; /* init ext sta */ } else fd_sta = (fd_sta & ~STA_IDL) | STA_BSY; if (fnc == FNC_BOOT) /* boot? fixed sec */ t = LRN_BOOT; else if (fd_wdv) /* valid data? use */ t = fd_lrn; else t = uptr->LRN; /* use prev */ fd_wdv = 0; /* data invalid */ fd_bptr = 0; /* init buffer */ uptr->FNC = fnc; /* save function */ uptr->LRN = t; /* save LRN */ if (ctab[fnc] & C_RD) /* seek now? */ sched_seek (uptr, t); else sim_activate (uptr, fd_ctime); /* start cmd */ } break; } return 0; } /* Unit service; the action to be taken depends on command */ t_stat fd_svc (UNIT *uptr) { uint32 i, u, tk, sc, crc, fnc, da; uint8 *fbuf = uptr->filebuf; u = uptr - fd_dev.units; /* get unit number */ fnc = GET_FNC (uptr->FNC); /* get function */ switch (fnc) { /* case on function */ case FNC_RESET: /* reset */ fd_clr (&fd_dev); /* clear device */ fd_done (u, 0, 0, 0); /* set idle */ return SCPE_OK; case FNC_STOP: /* stop */ fd_done (u, 0, 0, 0); /* set idle */ return SCPE_OK; case FNC_BOOT: /* boot, buf empty */ case FNC_RD: /* read, buf empty */ if (uptr->FNC & FNC_STOPPING) /* stopped? */ break; if (fd_dte (uptr, FALSE)) /* xfr error? */ return SCPE_OK; da = GET_DA (uptr->LRN); /* get disk addr */ for (i = 0; i < FD_NUMBY; i++) /* read sector */ fdxb[i] = fbuf[da + i]; if (fbuf[FD_SIZE + uptr->LRN - 1]) { /* deleted? set err */ fd_sta = fd_sta | STA_DEL; fd_es[u][0] = fd_es[u][0] | ES0_DEL; } fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */ fd_es[u][3] = GET_TRK (uptr->LRN); fd_bptr = 0; /* init buf */ uptr->LRN = uptr->LRN + 1; /* next block */ break; case FNC_WR: case FNC_DEL: /* write block */ if (fd_dte (uptr, TRUE)) /* xfr error? */ return SCPE_OK; if (fd_bptr) { /* any transfer? */ da = GET_DA (uptr->LRN); /* get disk addr */ for (i = fd_bptr; i < FD_NUMBY; i++) /* pad sector */ fdxb[i] = fd_db; for (i = 0; i < FD_NUMBY; i++) /* write sector */ fbuf[da + i] = fdxb[i]; /* then dir */ fbuf[FD_SIZE + uptr->LRN - 1] = ((fnc == FNC_DEL)? 1: 0); uptr->hwmark = uptr->capac; /* rewrite all */ fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */ fd_es[u][3] = GET_TRK (uptr->LRN); fd_bptr = 0; /* init buf */ uptr->LRN = uptr->LRN + 1; /* next block */ } break; case FNC_RSTA: /* read status */ if (uptr->flags & UNIT_WPRT) /* wr protected? */ fd_es[u][0] = fd_es[u][0] | ES0_WRP; if (GET_TRK (uptr->LRN) == 0) /* on track 0? */ fd_es[u][1] = fd_es[u][1] | ES1_TK0; if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ fd_es[u][0] = fd_es[u][0] | ES0_FLT; /* set err */ fd_es[u][1] = fd_es[u][1] | ES1_NRDY; } for (i = 0; i < ES_SIZE; i++) /* copy to buf */ fdxb[i] = fd_es[u][i]; for (i = ES_SIZE; i < FD_NUMBY; i++) fdxb[i] = 0; break; case FNC_RDID: /* read ID */ if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY); return SCPE_OK; } for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr buf */ tk = GET_TRK (uptr->LRN); /* get track */ sc = GET_SEC (uptr->LRN); /* get sector */ fdxb[0] = tk & 0xFF; /* store track */ fdxb[2] = sc & 0xFF; /* store sector */ crc = fd_crc (0xFFFF, 0xFE00, 8); /* CRC addr mark */ crc = fd_crc (crc, tk << 8, 16); /* CRC track */ crc = fd_crc (crc, sc << 8, 16); /* CRC sector */ fdxb[4] = (crc >> 8) & 0xFF; /* store CRC */ fdxb[5] = crc & 0xFF; break; case FNC_FMT: /* format */ default: fd_done (u, STA_ERR, ES0_ERR, ES1_CMD); /* ill cmd */ uptr->LRN = 1; /* on track 0 */ return SCPE_OK; } if (uptr->FNC & FNC_STOPPING) { /* stopping? */ uptr->FNC = FNC_STOP; /* fnc = STOP */ sim_activate (uptr, fd_ctime); /* schedule */ } fd_sta = fd_sta & ~STA_BSY; /* clear busy */ if (fd_arm) /* if armed, int */ SET_INT (v_FD); return SCPE_OK; } /* Schedule seek */ void sched_seek (UNIT *uptr, int32 newlrn) { int32 diff = newlrn - uptr->LRN; /* LRN diff */ if (diff < 0) /* ABS */ diff = -diff; if (diff < 10) diff = 10; /* MIN 10 */ sim_activate (uptr, diff * fd_stime); /* schedule */ return; } /* Command complete */ void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1) { fd_sta = (fd_sta | STA_IDL | nsta) & ~STA_BSY; /* set idle */ if (fd_arm) /* if armed, int */ SET_INT (v_FD); fd_es[u][0] = fd_es[u][0] | nes0; /* set ext state */ fd_es[u][1] = fd_es[u][1] | nes1; return; } /* Test for data transfer error */ t_bool fd_dte (UNIT *uptr, t_bool wr) { uint32 u = uptr - fd_dev.units; if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY); return TRUE; } if (wr && (uptr->flags & UNIT_WPRT)) { /* wr protected? */ fd_done (u, STA_ERR, ES0_ERR | ES0_WRP, 0); return TRUE; } if ((uptr->LRN == 0) || (uptr->LRN > FD_NUMLRN)) { /* bad LRN? */ fd_done (u, STA_ERR, ES0_ERR | ES0_LRN, 0); return TRUE; } return FALSE; } /* Header CRC calculation */ uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt) { uint32 i, wrk; for (i = 0; i < cnt; i++) { wrk = crc ^ dat; crc = (crc << 1) & DMASK16; if (wrk & SIGN16) crc = ((crc ^ 0x1020) + 1) & DMASK16; dat = (dat << 1) & DMASK16; } return crc; } /* Reset routine */ t_stat fd_clr (DEVICE *dptr) { int32 i, j; UNIT *uptr; fd_sta = STA_IDL; /* idle */ fd_cmd = 0; /* clear state */ fd_db = 0; fd_bptr = 0; fd_lrn = 1; fd_wdv = 0; for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr xfr buf */ for (i = 0; i < FD_NUMDR; i++) { /* loop thru units */ for (j = 0; j < ES_SIZE; j++) fd_es[i][j] = 0; /* clr ext sta */ fd_es[i][2] = 1; /* sector 1 */ uptr = fd_dev.units + i; sim_cancel (uptr); /* stop drive */ uptr->LRN = 1; /* clear state */ uptr->FNC = 0; } return SCPE_OK; } t_stat fd_reset (DEVICE *dptr) { CLR_INT (v_FD); /* clear int */ CLR_ENB (v_FD); /* disable int */ fd_arm = 0; /* disarm int */ return fd_clr (dptr);; } /* Bootstrap routine */ #define BOOT_START 0x50 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) static uint8 boot_rom[] = { 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */ 0x43, 0x00, 0x00, 0x80 /* BR 80 */ }; t_stat fd_boot (int32 unitno, DEVICE *dptr) { extern uint32 PC, dec_flgs; extern uint16 decrom[]; if (decrom[0xD5] & dec_flgs) /* AL defined? */ return SCPE_NOFNC; IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */ IOWriteB (AL_DEV, fd_dib.dno); /* set dev no */ IOWriteB (AL_IOC, 0x86 + (unitno << CMD_V_UNIT)); /* set dev cmd, unit num */ IOWriteB (AL_SCH, 0); /* clr sch dev no */ PC = BOOT_START; return SCPE_OK; } simh-3.8.1/Interdata/id_diag.txt0000644000175000017500000004267711017062274014715 0ustar vlmvlmInterdata Diagnostics Summary 816E CPU diagnostic, part 1 passed 16b n/a 816E CPU diagnostic, part 2 partial 16b n/a Series 16 CPU diagnostic, part 1 passed 16b n/a 16b memory diagnostic, part 1 passed 16b n/a 16b memory diagnostic, part 2 passed 16b n/a 816e extended memory diagnostic passed 16b n/a Series 16 selector channel diagnostic passed 16b n/a 32b CPU diagnostic, part 1 n/a passed 32b 32b CPU diagnostic, part 2 n/a passed 32b 32b CPU diagnostic, part 3 n/a passed 32b 32b memory diagnostic, part 1 n/a passed 32b 32b memory diagnostic, part 2 n/a passed 32b 32b memory diagnostic, part 3 n/a passed 32b 32b memory diagnostic 6a, part 1 n/a passed 32b 32b memory diagnostic 6a, part 2 n/a passed 32b 32b MAC diagnostic, part 1 n/a passed 32b 32b MAC diagnostic, part 2 n/a passed 32b Common line printer diagnostic passed 16b passed 32b Common magtape diagnostic passed 16b passed 32b Common 2.5/10MB disk diagnostic passed 16b passed 32b 32b MSM disk diagnostic passed 32b Common floppy disk diagnostic passed 16b passed 32b Common clock diagnostic passed 16b passed 32b Not tested: - 16b floating point - 32b double precision floating point - IDC - PASLA ------------------------------------------------------------------- Operating Instructions 816E CPU diagnostic, part 1 sim> set cpu 816e sim> att -e pt0 diag.bin sim> br c2 sim> boot pt0 Breakpoint: PC: 00C2 (EXBR R8,R6) sim> run 100 MODEL 8/16E PROCESSOR TEST PART 1 06-211R00 CPU * 8D ENTER 0 OR 1 1 NO ERROR CPU * --- 816E CPU diagnostic, part 2 sim> set cpu 816e sim> d tt ttime 1000 ; timing dependency sim> att -e pt0 diag.bin sim> br c2 sim> boot pt0 Breakpoint: PC: 00C2 (EXBR R8,R6) sim> run 2d0 MODEL 8/16E PROCESSOR TEST PART 2 06-212R00 CPU * 8D SUBTEST * (type subtest number) Subtests 0, 1, 2, 5, 7, 8, 9 run correctly Subtest 3, 4 cannot be run (initialization, power fail) Subtest 6 cannot be run (hexadecimal display) --- Series 16 CPU diagnostic, part 1 (Central error routine is at 21F4) sim> set cpu 816e sim> att -e pt0 diag.bin ; diagnostic sim> br c0 sim> boot pt0 Breakpoint, PC: 00C0 (8800) sim> d 234a 0202 ; patch to use sim> d 234c a4a8 ; TTY as console sim> d 17a b 1e4 sim> run 100 SERIES SIXTEEN PROCESSOR TEST PART 1 06-242F01R00 CPU * 2D ENTER 0 OR 1 1 1234567890 NO ERROR 000A 0000 CPU * --- 16b memory diagnostic, part 1 sim> att -e pt0 diag.bin ; diagnostic sim> br c2 sim> boot pt0 Breakpoint, PC: 00C2 (EPSR R7,R6) sim> run 100 02-340 PART 1 06-162F01R01 NO ERRORS --- 16b memory diagnostic, part 2 sim> att -e pt0 diag.bin ; diagnostic sim> br c2 sim> boot pt0 Breakpoint, PC: 00C2 (EPSR R7,R6) sim> run 1000 02-340 PART 2 06-162F02R01 NO ERRORS --- 816e extended memory diagnostic, parts 1 and 2 sim> set cpu 816e sim> set cpu 256k sim> att -e pt0 diag.bin ; diagnostic sim> br b4 sim> boot pt0 Breakpoint, PC: 00B4 (LPSW R0,B8) sim> run 1000 8/16 E EXTENDED MEMORY TEST PART 1 06-221R00 NO ERROR * (CR to repeat part 1) 8/16 E EXTENDED MEMORY TEST PART 1 06-221R00 NO ERROR * (LF to go on to part 2) Breakpoint, PC: 00B4 (LPSW R0,B8) sim> run 100 8/16 E EXTENDED MEMORY TEST PART 2 06-221R00 PROGRAM DETECTED MAXIMUM MEMORY 3FFFE *TEST ; standard tests *RUN SUBTEST 0 NO ERROR SUBTEST 1 NO ERROR SUBTEST 2 NO ERROR SUBTEST 3 NO ERROR SUBTEST 4 NO ERROR SUBTEST 6 NO ERROR SUBTEST 7 NO ERROR SUBTEST 8 NO ERROR END OF TEST * --- Series 16 selector channel diagnostic sim> set cpu 816e sim> set cpu 256k sim> att -e pt0 diag.bin ; diagnostic sim> att mt0 foo.tap ; magtape to test sim> br c0 sim> boot pt0 Breakpoint, PC: 00C0 (LPSW R0,C8) sim> d 2e68 2 ; console is TTY sim> run A00 S16 SELCH TEST 06-222 R01 TOP OF MEMORY 3 FFFF * IODEV1 C5 ; magtape * DEV1 2 * RUN ; bank 0 by default TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR * MEMMOD 1 {2,3} ; repeat for banks 1,2,3 * RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR * --- 32b CPU diagnostic, part 1 sim> att -e mt0 mmd_r07.tap sim> d -b 7f 7 ; file 8 on MMD R07 tape sim> boot mt0 S32PT1 06-154 R03 CPU *7X NO ERROR 000A 0000 * --- 32b CPU diagnostic, part 2 sim> set tt 7b ; test is parity sensitive sim> att -e mt0 mmd_r07.tap sim> d -b 7f 8 ; file 9 on MMD R07 tape sim> boot mt0 S32PT2R02 CPU * 7X SUBTEST * (type subtest number) Subtests 1, 3, 4, 5, 9 run correctly Subtest 2 cannot be run (7/32 with halfword mode only) Subtest 6 cannot be run (hexadecimal display) Subtests 7,8 cannot be run (initialization, power fail) --- 32b CPU diagnostic, part 3 sim> att -e mt0 mmd_r07.tap sim> d -b 7f 9 ; file 10 on MMD R07 tape sim> boot mt0 S32PT3 R01 CPU * 8X ; 7X denotes 7/32 with halfword mode MAC RESPONSE AT 000300 SUBTEST * Subtests 1, 2, 3 run correctly Subtest 4 cannot be run (parity option) --- 32b memory diagnostic, part 1 sim> att -e mt0 mmd_r07.tap sim> d -b 7f 17 ; file 24 on MMD R07 tape sim> br 2000 sim> boot mt0 Breakpoint, PC: 02000 (B 2060) sim> d -w 2010 0202 ; console is TTY sim> c S32MT1 06-156F01R04 MAC PRESENT ? (Y OR N) * Y 01 02 03 04 05 06 NO ERROR * --- 32b memory diagnostic, part 2 sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 18 ; file 25 on MMD R07 tape sim> br a00 sim> boot mt0 Breakpoint, PC: 00A00 (B A60) sim> d -w a10 0202 ; console is TTY sim> c S32MT2 06-156F02R04 AVAILABLE MEMORY 000000 - 0FFFFF SUBTEST * 0 ; all standard tests 01 TEST STILL RUNNING ; repeated multiple times : NO ERROR 02 TEST STILL RUNNING ; repeated multiple times : NO ERROR 03 TEST STILL RUNNING ; repeated multiple times : NO ERROR 04 TEST STILL RUNNING ; repeated multiple times : NO ERROR 05 TEST STILL RUNNING ; repeated multiple times : NO ERROR 06 TEST STILL RUNNING ; repeated multiple times : NO ERROR 07 TEST STILL RUNNING ; repeated multiple times : NO ERROR SUBTEST * --- 32b memory diagnostic, part 3 sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 19 ; file 26 on MMD R07 tape sim> br a00 sim> boot mt0 Breakpoint, PC: 00A00 (B A60) sim> d -w a10 0202 ; console is TTY sim> c S32MT3 06-156F03R04 AVAILABLE MEMORY 000000 - 0FFFFF * TEST STILL RUNNING ; repeated multiple times : NO ERROR * --- 32b memory diagnostic, 6a, part 1 sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 15 ; file 22 on MMD R07 tape sim> boot mt0 32 BIT S6A MEMORY TEST 06-157F01R01 AVAILABLE MEMORY 0000-3FFF MAC ADDRESS = 300 TYPE= 3 ; any value, 0-4 SUBTEST * 0 01 NO ERROR 02 NO ERROR 03 NO ERROR 04 NO ERROR 05 NO ERROR 06 NO ERROR 07 NO ERROR 08 NO ERROR SUBTEST * --- 32b memory diagnostic, 6a, part 2 sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 16 ; file 23 on MMD R07 tape sim> boot mt0 32 BIT S6A MEMORY TEST 06-157F02R01 AVAILABLE MEMORY 0000f-FFFFF TYPE= 2 ; any value, 0-4 SUBTEST * 0 01 NO ERROR 02 NO ERROR 03 NO ERROR 04 NO ERROR 05 NO ERROR 06 NO ERROR 07 NO ERROR 08 NO ERROR SUBTEST * --- 32b MAC diagnostic, part 1 sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 24 ; file 37 on MMD R07 tape sim> boot mt0 MACT 06-160F01R03 AVAILABLE MEMORY 00000- FFFFF * RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR TEST 08 NO ERROR TEST 09 NO ERROR TEST 0B NO ERROR * --- sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 25 ; file 38 on MMD R07 tape sim> br ffd0 ; start != load point sim> boot mt0 Breakpoint, PC: 0FFD0 (B 1093E) sim> run 10010 MACT 06-160F02R03 * RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR TEST 08 NO ERROR * --- Common line printer diagnostic sim> att -e pt0 diag.bin sim> br c2 sim> boot pt0 Breakpoint: PC: 00C2 (EXBR R8,R6) sim> run a00 ; 32b sim> run a04 ; 16b COMMON LINE PRINTER TEST 06-170R02 *TEST 0,1,2,3 *RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR END OF TEST *INTRPT 1 *RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR END OF TEST * --- Common magtape diagnostic sim> att -e pt0 diag.bin sim> att mt foo.tap sim> br c4 sim> boot pt0 Breakpoint, PC: 00C4 (EXBR R8,R6) sim> run a00 ; 32b sim> run a04 ; 16b COMMON MAGNETIC TAPE TEST PROGRAM 06-172R02 *TEST 0,1,2,3,4,5 *MODE 0 ; prog i/o and selch *RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR END OF TEST * --- Common 2.5/10MB disk diagnostic sim> att -e pt0 diag.bin sim> br c2 sim> boot pt0 Breakpoint, PC: 00C2 (EXBR R8,R6) sim> set dp0 5440 sim> set dp1 5440 sim> att dp0 test0.dsk sim> att dp1 test1.dsk sim> run a00 ; 32b sim> run a04 ; 16b COMMON DISC TEST 06-173R01F01 *FILE 2 ; FILE 1 to test fixed platter *LOCYL 0 *HICYL 197 *TIMCON 1C0 *TEST 0,1,2,3,4,6,7,8,9,A,C ; test 5 requires format capability ; test B requires manual intervention *RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR TEST 08 NO ERROR TEST 09 NO ERROR TEST 0A NO ERROR TEST 0C NO ERROR END OF TEST * --- 32b MSM disk diagnostic sim> att -e mt0 c:\temp\mmd_r07.tap sim> d -b 7f 45 ; file 70 on MMD R07 tape sim> br a00 sim> boot mt0 Breakpoint, PC: 00A00 (B A5E) sim> d -h a10 0202 ; patch for TTY console sim> att dm0 foo.dsk sim> att dm1 foo1.dsk sim> c MSM DISC TEST 06-200F02R04 (32-BIT) *LOCYL 0 *HICYL 336 ; tests 8,9,A will run a very long ; time, use 40 to shorten test *DRIVE 0 *PACTYP 0 *TIMVAL 14D *XFILE 1 *TEST 0,1,2,3,4,6,7,8,9,A,C ; test 5 requires format capability ; test B requires manual intervention *RUN TEST 00 TEST 01 TEST 02 TEST 03 TEST 04 TEST 06 TEST 07 TEST 08 TEST 09 TEST 0A TEST 0C --- Common floppy disk diagnostic sim> att -e pt0 diag.bin sim> att fd0 foo0.flp sim> att fd1 foo1.flp sim> br b8 sim> boot pt0 Breakpoint, PC: 000B8 (BS B2) sim> d 2a72 bal r15,320a ; patch for multidrive test sim> run a00 ; 32b sim> run a04 ; 16b COMMON FLOPPY DISC TEST 06-198R00 UNPROTECT DISKETTE *DRIVE AB *RUN DRIVE A UNDER TEST TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR DRIVE B UNDER TEST TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR END OF TEST *TEST 9 ; test 8 requires formatting *RUN TEST 09 NO ERROR END OF TEST * --- Common clock diagnostic sim> att -e pt0 diag.bin sim> br c4 sim> boot pt0 Breakpoint, PC: 00C4 (EXBR R8,R6) sim> d -w e28 4300 ; R09 patches sim> d -w e2a 10f4 sim> id -w 10f4:110a 10f4: 4840 10f6: 188a 10f8: 4850 10fa: 188c 10fc: de40 10fe: 1eaf 1100: de50 1102: 1eaf 1104: 4810 1106: 0a24 1108: 4300 1110: 0e2c sim> d 1b9c bs 1ba6 sim> d -w 1102 1eaf sim> run a00 ; 32b sim> run a04 ; 16b COMMON UNIVERSAL CLOCK MODULE TEST 06-133R05 *TIMVAL 1A4 ; simulator is a fast CPU *RUN TEST 00 NO ERROR TEST 01 NO ERROR TEST 02 NO ERROR TEST 03 NO ERROR TEST 04 NO ERROR TEST 05 NO ERROR TEST 06 NO ERROR TEST 07 NO ERROR END OF TEST * ------------------------------------------------------------------- Bugs Found and Fixed During Simulator Debug 1. CPU16: instruction decoding interpreting CPU models incorrectly 2. CPU16: SINT should not be conditional on device existing 3. CPU16: immediate interrupts do not do a PSW swap, new PC is block+6 4. CPU16: SLA, SLHA setting C incorrectly 5. CPU16: diagnostic requires 816E extended memory to run 6. CPU16: CCW16_OC defined incorrectly 7. CPU16, CPU32: autoload not fetching or outputing OC 8. CPU16, CPU32: block I/O completion is off by 1 9. CPU16, CPU32: ESPR broken, EPSR rx,rx should copy PSW to rx 10. CPU16, CPU32: PCQ displays in octal instead of hexadecimal 11. CPU16, CPU32: SH and variations overflow calculation wrong 12. CPU16, CPU32: SCH overflow calculation wrong 13. CPU16, CPU32: CH and CLH overflow calculation wrong 14. CPU16, CPU32: CH or'ing into CC's instead of loading 15. CPU16, CPU32: RD, RH, SS, AI store some data on non-existent device 16. CPU16, CPU32: console interrupt not implemented 17. CPU16, CPU32: SRHL(s) setting C incorrectly 18. CPU16, CPU32: WDR, OCR not masking register data to 8b 19. CPU32: WH not masking data to 8b or 16b as required 20. CPU32: 32b register sets ordered incorrectly in memory 21. CPU32: wrong slot length in queue instructions 22. CPU32: display device missing its interrupt declaration 23. CPU32: LPSW(R) must load PC before changing PSW 24. CPU32: SLL setting C incorrectly 25. CPU32: bit instructions use halfword memory access and offsets 26. CPU32: CRC sign-extending rather than zero-extending operands 27. CPU32: SCP incrementing counts before, not after, transfer 28. CPU32: CHVR not implemented 29. CPU32: M(R) algorithm wrong 30. CPU32: M(R) using wrong register as first operand 31. CPU32: memory accesses were fullword rather than halfword aligned 32. CPU32: D(R) overflow calculation incorrect 33. CPU32: on 7/32, exceptions use register set 0, regardless of new PSW 34. CPU32: system queue PSW location misdefined 35. CPU32: autodriver channel not shifting bytes left before use as translation table index 36. CPU32: MAC, LRA using wrong value for limit test 37. CPU32: LRA using wrong value for segment base 38. CPU32: MAC registers are accessible only if protection is off 39. CPU32: MAC status clears only on write, not read 40. CPU32: MAC write protect abort and interrupts implemented incorrectly 41. CPU32: ex/dep -v test used & instead of && 42. CPU32: fetch tests for MAC abort at end of fetch, not per halfword 43. FP: unpack and pack detecting RR format incorrectly 44. FP: need separate microcode/hardware algorithms for add/sub denormalization 45. FP: multiply and divide have 'early out' detection of overflow/underflow 46. FP: compare less than not setting C 47. FP: fix overflow not setting V 48. FP: fix shift needed to be hex digits not binary digits 49. IO: interrupt evaluation routine never sets an interrupt 50. SELCH: transfer count calculation off by 1 51. SELCH: device data structure set up incorrectly (reset routine) 52. SELCH: stop clears pending interrupts 53. SELCH: register load algorithm incorrect for 6 byte loads 54. PT, LPT, FD: OR'ing status mask instead of AND'ing 55. PT, TT: SET_INT on status change not conditioned on interrupt armed 56. TT: input char converted to UC incorrectly 57. TT: need SET TT BREAK to run CPU test part 2 58. LPT: not clearing spacing done 59. MT: WREOF not setting EOF status 60. MT: CMD register pointer to wrong place 61. MT: write record byte count taken from wrong variable 62. MT: overrun processing incorrect for selector channel mode 63. PIC, LFC: write data and overflow detection incorrect 64. PIC, LFC: interpolation algorithm for cic read incorrect 65. PIC, LFC: ric reloaded from output buffer on count overflow 66. PIC, LFC: added diagnostic mode, revised use of count vs timer 67. DP: track increment algorithm incorrect 68. DP, IDC: incorrectly setting overrun for less than full sector reads 69. DP: should interrupt on detach (offline) 70. FD: high water mark not updated on write 71. FD: deleted data not implemented, required for diagnostic 72. FD: header CRC not implemented, required for diagnostic 73. FD: function code not stored for service routine 74. FD: LRN to track and sector conversions incorrect 75. FD: reset status incorrect (should be not busy, LRN = 1) 76. FD: extended status track 0 calculation wrong 77. FD: reset does not clear interrupts, requires delay 78. FD: read/write sequencing incorrect 79. FD: command without write data uses implicit LRN 80. FD: extended status is per drive not per controller 81. FD: command start clears only extended status bytes 0,1 82. FD: IDLE sets after BUSY drops and generates a separate interrupt 83. SYS16, SYS32: WH mistyped as WD in symbol table 84. SYS32: MHR, DHR misdefined 85. PAS: busy set instead of cleared initially 86. IDC: busy set instead of cleared initially 87. IDC, DP: busy not cleared at transfer command complete 88. IDC: busy is not cleared at drive command complete 89. IDC: for MSM compatibility, must absorb WH of head/cylinder 90. IDC: drive command 0x30 is an instant NOP 91. IDC: set cylinder with invalid cylinder sets SKI 92. IDC: read with invalid head sets ACF, not DTE 93. DP, IDC: write with cylinder overflow advanced selch pointer 94. MT: read error must stop selector channel (if active) 95. IDC: xx000000 to controller or drive are NOP's, not invalid commands 96. IDC: WD/WH use standard Interdata write pointers 97. SELCH: GO preserves EXA and SSTA 98. CPU: DH overflow checking broken simh-3.8.1/Interdata/id_ttp.c0000644000175000017500000002565511110076456014221 0ustar vlmvlm/* id_ttp.c: Interdata PASLA console interface Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ttp console (on PAS) 18-Jun-07 RMS Added UNIT_IDLE flag to console input 18-Oct-06 RMS Sync keyboard to LFC clock 22-Nov-05 RMS Revised for new terminal processing routines 29-Dec-03 RMS Added support for console backpressure 25-Apr-03 RMS Revised for extended file support */ #include "id_defs.h" #include #define TTI 0 #define TTO 1 /* Status byte */ #define STA_OVR 0x80 /* overrun RO */ #define STA_PF 0x40 /* parity err RO */ #define STA_FR 0x20 /* framing err RO */ #define STA_RCV (STA_OVR|STA_PF|STA_FR) #define SET_EX (STA_OVR|STA_PF|STA_FR) #define STA_XMT (STA_BSY) /* Command bytes 1,0 */ #define CMD_ECHO (0x10 << 8) /* echoplex */ #define CMD_WRT (0x02 << 8) /* write/read */ #define CMD_TYP 0x01 /* command type */ extern uint32 int_req[INTSZ], int_enb[INTSZ]; extern int32 pas_par (int32 cmd, int32 c); extern int32 lfc_poll; uint32 ttp_sta = 0; /* status */ uint32 ttp_cmd = 0; /* command */ uint32 ttp_kchp = 0; /* rcvr chr pend */ uint32 ttp_karm = 0; /* rcvr int armed */ uint32 ttp_tarm = 0; /* xmt int armed */ uint8 ttp_tplte[] = { 0, 1, TPL_END }; uint32 ttp (uint32 dev, uint32 op, uint32 dat); t_stat ttpi_svc (UNIT *uptr); t_stat ttpo_svc (UNIT *uptr); t_stat ttp_reset (DEVICE *dptr); t_stat ttp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ttp_set_break (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ttp_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc); /* TTP data structures */ DIB ttp_dib = { d_TTP, -1, v_TTP, ttp_tplte, &ttp, NULL }; UNIT ttp_unit[] = { { UDATA (&ttpi_svc, UNIT_IDLE, 0), 0 }, { UDATA (&ttpo_svc, 0, 0), SERIAL_OUT_WAIT } }; REG ttp_reg[] = { { HRDATA (CMD, ttp_cmd, 16) }, { HRDATA (KBUF, ttp_unit[TTI].buf, 8) }, { DRDATA (KPOS, ttp_unit[TTI].pos, T_ADDR_W), PV_LEFT }, { DRDATA (KTIME, ttp_unit[TTI].wait, 24), REG_NZ + PV_LEFT + REG_HRO }, { FLDATA (KIREQ, int_req[l_TTP], i_TTP) }, { FLDATA (KIENB, int_enb[l_TTP], i_TTP) }, { FLDATA (KARM, ttp_karm, 0) }, { FLDATA (CHP, ttp_kchp, 0) }, { HRDATA (TBUF, ttp_unit[TTO].buf, 8) }, { DRDATA (TPOS, ttp_unit[TTO].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TTIME, ttp_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, { FLDATA (TIREQ, int_req[l_TTP], i_TTP + 1) }, { FLDATA (TIENB, int_enb[l_TTP], i_TTP + 1) }, { FLDATA (TARM, ttp_tarm, 0) }, { HRDATA (DEVNO, ttp_dib.dno, 8), REG_HRO }, { NULL } }; MTAB ttp_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", &ttp_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &ttp_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &ttp_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &ttp_set_mode }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ENABLED", &ttp_set_enbdis, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, DEV_DIS, NULL, "DISABLED", &ttp_set_enbdis, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "BREAK", &ttp_set_break, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE ttp_dev = { "TTP", ttp_unit, ttp_reg, ttp_mod, 2, 10, 31, 1, 16, 8, NULL, NULL, &ttp_reset, NULL, NULL, NULL, &ttp_dib, DEV_DIS }; /* Terminal: I/O routine */ uint32 ttp (uint32 dev, uint32 op, uint32 dat) { int32 xmt = dev & 1; int32 t, old_cmd; switch (op) { /* case IO op */ case IO_ADR: /* select */ return BY; /* byte only */ case IO_RD: /* read */ ttp_kchp = 0; /* clr chr pend */ ttp_sta = ttp_sta & ~STA_OVR; /* clr overrun */ return ttp_unit[TTI].buf; /* return buf */ case IO_WD: /* write */ ttp_unit[TTO].buf = dat & 0xFF; /* store char */ ttp_sta = ttp_sta | STA_BSY; /* set busy */ sim_activate (&ttp_unit[TTO], ttp_unit[TTO].wait); break; case IO_SS: /* status */ if (xmt) t = ttp_sta & STA_XMT; /* xmt? just busy */ else { /* rcv */ t = ttp_sta & STA_RCV; /* get static */ if (!ttp_kchp) /* no char? busy */ t = t | STA_BSY; if (t & SET_EX) /* test for ex */ t = t | STA_EX; } return t; case IO_OC: /* command */ old_cmd = ttp_cmd; /* old cmd */ if (dat & CMD_TYP) { /* type 1? */ ttp_cmd = (ttp_cmd & 0xFF) | (dat << 8); if (ttp_cmd & CMD_WRT) /* write? */ ttp_tarm = int_chg (v_TTP + 1, dat, ttp_tarm); else ttp_karm = int_chg (v_TTP, dat, ttp_karm); } else ttp_cmd = (ttp_cmd & ~0xFF) | dat; break; } return 0; } /* Unit service */ t_stat ttpi_svc (UNIT *uptr) { int32 c, out; sim_activate (uptr, KBD_WAIT (uptr->wait, lfc_poll)); /* continue poll */ ttp_sta = ttp_sta & ~STA_FR; /* clear break */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; ttp_sta = ttp_sta & ~STA_PF; /* clear parity err */ if (ttp_kchp) /* overrun? */ ttp_sta = ttp_sta | STA_OVR; if (ttp_karm) SET_INT (v_TTP); if (c & SCPE_BREAK) { /* break? */ ttp_sta = ttp_sta | STA_FR; /* framing error */ uptr->buf = 0; /* no character */ } else { out = c & 0x7F; /* echo is 7b */ c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); if (TT_GET_MODE (uptr->flags) != TT_MODE_8B) /* not 8b mode? */ c = pas_par (ttp_cmd, c); /* apply parity */ uptr->buf = c; /* save char */ uptr->pos = uptr->pos + 1; /* incr count */ ttp_kchp = 1; /* char pending */ if (ttp_cmd & CMD_ECHO) { out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags)); if (c >= 0) sim_putchar (out); ttp_unit[TTO].pos = ttp_unit[TTO].pos + 1; } } return SCPE_OK; } t_stat ttpo_svc (UNIT *uptr) { int32 c; t_stat r; if (TT_GET_MODE (uptr->flags) == TT_MODE_8B) /* 8b? */ c = pas_par (ttp_cmd, uptr->buf); /* apply parity */ else c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags)); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); } } ttp_sta = ttp_sta & ~STA_BSY; /* not busy */ if (ttp_tarm) /* set intr */ SET_INT (v_TTP + 1); uptr->pos = uptr->pos + 1; /* incr count */ return SCPE_OK; } /* Reset routine */ t_stat ttp_reset (DEVICE *dptr) { if (dptr->flags & DEV_DIS) sim_cancel (&ttp_unit[TTI]); else sim_activate_abs (&ttp_unit[TTI], KBD_WAIT (ttp_unit[TTI].wait, lfc_poll)); sim_cancel (&ttp_unit[TTO]); CLR_INT (v_TTP); /* clear int */ CLR_ENB (v_TTP); CLR_INT (v_TTP + 1); /* disable int */ CLR_ENB (v_TTP + 1); ttp_karm = ttp_tarm = 0; /* disarm int */ ttp_cmd = 0; ttp_sta = 0; ttp_kchp = 0; return SCPE_OK; } /* Make mode flags uniform */ t_stat ttp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { ttp_unit[TTO].flags = (ttp_unit[TTO].flags & ~TT_MODE) | val; if (val == TT_MODE_7P) val = TT_MODE_7B; ttp_unit[TTI].flags = (ttp_unit[TTI].flags & ~TT_MODE) | val; return SCPE_OK; } /* Set input break */ t_stat ttp_set_break (UNIT *uptr, int32 val, char *cptr, void *desc) { if (ttp_dev.flags & DEV_DIS) return SCPE_NOFNC; ttp_sta = ttp_sta | STA_FR; if (ttp_karm) /* if armed, intr */ SET_INT (v_TTP); sim_cancel (&ttp_unit[TTI]); /* restart TT poll */ sim_activate (&ttp_unit[TTI], ttp_unit[TTI].wait); return SCPE_OK; } /* Set enabled/disabled */ t_stat ttp_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc) { extern DEVICE tt_dev; extern t_stat tt_reset (DEVICE *dptr); ttp_dev.flags = (ttp_dev.flags & ~DEV_DIS) | val; tt_dev.flags = (tt_dev.flags & ~DEV_DIS) | (val ^ DEV_DIS); ttp_reset (&ttp_dev); tt_reset (&tt_dev); return SCPE_OK; } simh-3.8.1/Interdata/id_fp.c0000644000175000017500000005362411110106152013777 0ustar vlmvlm/* id_fp.c: Interdata floating point instructions Copyright (c) 2000-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. The Interdata uses IBM 360 floating point format: 0 7 8 15 23 31 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |S| exponent | fraction | :single +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | fraction low | :double +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ where S = 0 for plus, 1 for minus exponent = 16**n, in excess 64 code fraction = .hhhhhh, seen as 6-14 hexadecimal digits Numbers can be normalized or unnormalized but are always normalized when loaded. Interdata has 8 floating point registers, F0, F2, ... , FE. In floating point instructions, the low order bit of the register number is ignored. On floating point overflow, the exponent and fraction are set to 1's. On floating point underflow, the exponent and fraction are set to 0's. Interdata has both 32b only and 32b/64b floating point implementations. In 32b only implementations, add and subtract are truncated, but multiply and divide are rounded, and the floating point registers are kept in memory. In 64b implementations, all single precision precision operations are rounded, double precision operations are truncated, and the floating point registers are kept in separate hardware arrays. */ #include "id_defs.h" struct ufp { /* unpacked fp */ int32 sign; /* sign */ int32 exp; /* unbiased exp */ uint32 h; /* fr high */ uint32 l; /* fr low */ }; #define FP_V_SIGN 31 /* sign */ #define FP_M_SIGN 0x1 #define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN) #define FP_V_EXP 24 /* exponent */ #define FP_M_EXP 0x7F #define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP) #define FP_V_FRH 0 /* fraction */ #define FP_M_FRH 0xFFFFFF #define FP_GETFRH(x) (((x) >> FP_V_FRH) & FP_M_FRH) #define FP_GETFRL(x) (x) #define FP_BIAS 0x40 /* exp bias */ #define FP_CARRY (1 << FP_V_EXP ) /* carry out */ #define FP_NORM (0xF << (FP_V_EXP - 4)) /* normalized */ #define FP_ROUND 0x80000000 /* Double precision fraction add/subtract/compare */ #define FR_ADD(d,s) d.l = (d.l + s.l) & DMASK32; \ d.h = (d.h + s.h + (d.l < s.l)) & DMASK32 #define FR_SUB(d,s) d.h = (d.h - s.h - (d.l < s.l)) & DMASK32; \ d.l = (d.l - s.l) & DMASK32 #define FR_GE(s1,s2) ((s1.h > s2.h) || \ ((s1.h == s2.h) && (s1.l >= s2.l))) /* Variable and constant shifts; for constants, 0 < k < 32 */ #define FR_RSH_V(v,s) if ((s) < 32) { \ v.l = ((v.l >> (s)) | \ (v.h << (32 - (s)))) & DMASK32; \ v.h = (v.h >> (s)) & DMASK32; \ } \ else { \ v.l = v.h >> ((s) - 32); \ v.h = 0; \ } #define FR_RSH_K(v,s) v.l = ((v.l >> (s)) | \ (v.h << (32 - (s)))) & DMASK32; \ v.h = (v.h >> (s)) & DMASK32 #define FR_LSH_K(v,s) v.h = ((v.h << (s)) | \ (v.l >> (32 - (s)))) & DMASK32; \ v.l = (v.l << (s)) & DMASK32 #define Q_RND(op) (OP_DPFP (op) == 0) #define Q_RND_AS(op) ((OP_DPFP (op) == 0) && fp_in_hwre) extern uint32 *R; extern uint32 F[8]; extern dpr_t D[8]; extern uint16 decrom[]; extern uint32 fp_in_hwre; extern uint32 ReadF (uint32 loc, uint32 rel); extern void WriteF (uint32 loc, uint32 dat, uint32 rel); void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea); void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1); void NormUFP (struct ufp *fop); uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd); uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1); /* Floating point load */ uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea) { struct ufp fop2; ReadFP2 (&fop2, op, r2, ea); /* get op, normalize */ return StoreFPR (&fop2, op, r1, 0); /* store, chk unflo */ } /* Floating point compare */ uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea) { struct ufp fop1, fop2; ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ UnpackFPR (&fop1, op, r1); /* get op1, norm */ if (fop1.sign ^ fop2.sign) /* signs differ? */ return (fop2.sign? CC_G: (CC_C | CC_L)); if (fop1.exp != fop2.exp) /* exps differ? */ return (((fop1.exp > fop2.exp) ^ fop1.sign)? CC_G: (CC_C | CC_L)); if (fop1.h != fop2.h) /* hi fracs differ? */ return (((fop1.h > fop2.h) ^ fop1.sign)? CC_G: (CC_C | CC_L)); if (OP_DPFP (op) && (fop1.l != fop2.l)) /* dp: low fracs diff? */ return (((fop1.l > fop2.l) ^ fop1.sign)? CC_G: (CC_C | CC_L)); return 0; } /* Floating to integer conversion */ uint32 f_fix (uint32 op, uint32 r1, uint32 r2) /* 16b */ { struct ufp res; uint32 cc; UnpackFPR (&res, op, r2); /* get op2, norm */ if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */ R[r1] = 0; return 0; } if ((res.exp > 0x44) || /* result too big? */ ((res.exp == 0x44) && (res.h >= 0x00800000))) { res.h = MMASK16; cc = CC_V; } else { res.h = res.h >> ((0x46 - res.exp) * 4); /* right align frac */ cc = 0; } if (res.sign) { R[r1] = ((res.h ^ DMASK16) + 1) & DMASK16; /* negate result */ return cc | CC_L; } R[r1] = res.h & DMASK16; return cc | CC_G; } uint32 f_fix32 (uint32 op, uint32 r1, uint32 r2) /* 32b */ { struct ufp res; uint32 cc; UnpackFPR (&res, op, r2); /* get op2, norm */ if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */ R[r1] = 0; return 0; } if ((res.exp > 0x48) || /* result too big? */ ((res.exp == 0x48) && (res.h >= 0x00800000))) { res.h = MMASK32; cc = CC_V; } else { FR_LSH_K (res, 8); /* get all in 32b */ res.h = res.h >> ((0x48 - res.exp) * 4); /* right align frac */ cc = 0; } if (res.sign) { R[r1] = (res.h ^ DMASK32) + 1; /* negate result */ return cc | CC_L; } R[r1] = res.h; return cc | CC_G; } /* Integer to floating conversion */ uint32 f_flt (uint32 op, uint32 r1, uint32 r2) /* 16b */ { struct ufp res = { 0, 0x44, 0, 0 }; /* +, 16**4 */ uint32 cc; if (R[r2] == 0) /* zero arg? */ cc = 0; else if (R[r2] & SIGN16) { /* neg arg? */ res.sign = FP_M_SIGN; /* set sign */ res.h = ((~R[r2] + 1) & DMASK16) << 8; /* get magnitude */ cc = CC_L; } else { res.h = R[r2] << 8; /* pos nz arg */ cc = CC_G; } NormUFP (&res); /* normalize */ StoreFPR (&res, op, r1, 0); /* store result */ return cc; } uint32 f_flt32 (uint32 op, uint32 r1, uint32 r2) /* 32b */ { struct ufp res = { 0, 0x48, 0, 0 }; /* +, 16**8 */ uint32 cc, t; t = R[r2]; /* int op */ if (t) { /* nonzero arg? */ if (t & SIGN32) { /* neg arg? */ res.sign = FP_M_SIGN; /* set sign */ t = (~t + 1) & DMASK32; /* get magnitude */ cc = CC_L; } else cc = CC_G; /* pos nz arg */ res.h = t >> 8; /* hi frac */ res.l = t << 24; /* lo frac */ } else cc = 0; /* zero arg */ NormUFP (&res); /* normalize */ StoreFPR (&res, op, r1, 0); /* store result */ return cc; } /* Floating point add/subtract */ uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea) { struct ufp fop1, fop2, t; int32 ediff; ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ UnpackFPR (&fop1, op, r1); /* get op1, norm */ if (op & 1) /* if sub, inv sign2 */ fop2.sign = fop2.sign ^ 1; if (fop1.h == 0) /* if op1 = 0, res = op2 */ fop1 = fop2; else if (fop2.h != 0) { /* if op2 = 0, no add */ if ((fop1.exp < fop2.exp) || /* |op1| < |op2|? */ ((fop1.exp == fop2.exp) && ((fop1.h < fop2.h) || ((fop1.h == fop2.h) && (fop1.l < fop2.l))))) { t = fop2; /* swap operands */ fop2 = fop1; fop1 = t; } ediff = fop1.exp - fop2.exp; /* exp difference */ if (OP_DPFP (op) || fp_in_hwre) { /* dbl prec or hwre? */ if (ediff >= 14) /* diff too big? */ fop2.h = fop2.l = 0; else if (ediff) { /* any difference? */ FR_RSH_V (fop2, ediff * 4); /* shift frac */ } } else { /* sgl prec ucode */ if (ediff >= 6) /* diff too big? */ fop2.h = 0; else if (ediff) /* any difference? */ fop2.h = fop2.h >> (ediff * 4); /* shift frac */ } if (fop1.sign ^ fop2.sign) { /* eff subtract */ FR_SUB (fop1, fop2); /* sub fractions */ NormUFP (&fop1); /* normalize result */ } else { FR_ADD (fop1, fop2); /* add fractions */ if (fop1.h & FP_CARRY) { /* carry out? */ FR_RSH_K (fop1, 4); /* renormalize */ fop1.exp = fop1.exp + 1; /* incr exp */ } } } /* end if fop2 */ return StoreFPR (&fop1, op, r1, Q_RND_AS (op)); /* store result */ } /* Floating point multiply Notes: - Exponent overflow/underflow is tested right after the exponent add, without regard to potential changes due to normalization - Exponent underflow is tested right after normalization, without regard to changes due to rounding - Single precision hardware multiply may generate up to 48b - Double precision multiply generates 56b with no guard bits */ int32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea) { struct ufp fop1, fop2; struct ufp res = { 0, 0, 0, 0 }; uint32 i; ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ UnpackFPR (&fop1, op, r1); /* get op1, norm */ if (fop1.h && fop2.h) { /* if both != 0 */ res.sign = fop1.sign ^ fop2.sign; /* sign = diff */ res.exp = fop1.exp + fop2.exp - FP_BIAS; /* exp = sum */ if ((res.exp < 0) || (res.exp > FP_M_EXP)) /* ovf/undf? */ return StoreFPX (&res, op, r1); /* early out */ if ((fop1.l | fop2.l) == 0) { /* 24b x 24b? */ for (i = 0; i < 24; i++) { /* 24 iterations */ if (fop2.h & 1) /* add hi only */ res.h = res.h + fop1.h; FR_RSH_K (res, 1); /* shift dp res */ fop2.h = fop2.h >> 1; } } else { /* some low 0's */ if (fop2.l != 0) { /* 56b x 56b? */ for (i = 0; i < 32; i++) { /* do low 32b */ if (fop2.l & 1) { FR_ADD (res, fop1); } FR_RSH_K (res, 1); fop2.l = fop2.l >> 1; } } for (i = 0; i < 24; i++) { /* do hi 24b */ if (fop2.h & 1) { FR_ADD (res, fop1); } FR_RSH_K (res, 1); fop2.h = fop2.h >> 1; } } NormUFP (&res); /* normalize */ if (res.exp < 0) /* underflow? */ return StoreFPX (&res, op, r1); /* early out */ } return StoreFPR (&res, op, r1, Q_RND (op)); /* store */ } /* Floating point divide - see overflow/underflow notes for multiply */ int32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea) { struct ufp fop1, fop2; struct ufp quo = { 0, 0, 0, 0 }; int32 i; ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ UnpackFPR (&fop1, op, r1); /* get op1, norm */ if (fop2.h == 0) /* div by zero? */ return CC_C | CC_V; if (fop1.h) { /* dvd != 0? */ quo.sign = fop1.sign ^ fop2.sign; /* sign = diff */ quo.exp = fop1.exp - fop2.exp + FP_BIAS; /* exp = diff */ if ((quo.exp < 0) || (quo.exp > FP_M_EXP)) /* ovf/undf? */ return StoreFPX (&quo, op, r1); /* early out */ if (!FR_GE (fop1, fop2)) { FR_LSH_K (fop1, 4); /* ensure success */ } else { /* exp off by 1 */ quo.exp = quo.exp + 1; /* incr exponent */ if (quo.exp > FP_M_EXP) /* overflow? */ return StoreFPX (&quo, op, r1); /* early out */ } for (i = 0; i < (OP_DPFP (op)? 14: 6); i++) { /* 6/14 hex digits */ FR_LSH_K (quo, 4); /* shift quotient */ while (FR_GE (fop1, fop2)) { /* while sub works */ FR_SUB (fop1, fop2); /* decrement */ quo.l = quo.l + 1; /* add quo bit */ } FR_LSH_K (fop1, 4); /* shift divd */ } if (!OP_DPFP (op)) { /* single? */ quo.h = quo.l; /* move quotient */ if (fop1.h >= (fop2.h << 3)) quo.l = FP_ROUND; else quo.l = 0; } /* don't need to normalize */ } /* end if fop1.h */ return StoreFPR (&quo, op, r1, Q_RND (op)); /* store result */ } /* Utility routines */ /* Unpack floating point number */ void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1) { uint32 hi; if (OP_DPFP (op)) { /* double prec? */ hi = D[r1 >> 1].h; /* get hi */ fop->l = FP_GETFRL (D[r1 >> 1].l); /* get lo */ } else { hi = ReadFReg (r1); /* single prec */ fop->l = 0; /* lo is zero */ } fop->h = FP_GETFRH (hi); /* get hi frac */ if (fop->h || fop->l) { /* non-zero? */ fop->sign = FP_GETSIGN (hi); /* get sign */ fop->exp = FP_GETEXP (hi); /* get exp */ NormUFP (fop); /* normalize */ } else fop->sign = fop->exp = 0; /* clean zero */ return; } /* Read memory operand */ void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea) { uint32 hi; if (OP_TYPE (op) > OP_RR) { /* mem ref? */ hi = ReadF (ea, VR); /* get hi */ if (OP_DPFP (op)) /* dp? get lo */ fop->l = ReadF (ea + 4, VR); else fop->l = 0; /* sp, lo = 0 */ } else { if (OP_DPFP (op)) { /* RR */ hi = D[r2 >> 1].h; /* dp? get dp reg */ fop->l = D[r2 >> 1].l; } else { hi = ReadFReg (r2); /* get sp reg */ fop->l = 0; } } fop->h = FP_GETFRH (hi); /* get hi frac */ if (fop->h || fop->l) { /* non-zero? */ fop->sign = FP_GETSIGN (hi); /* get sign */ fop->exp = FP_GETEXP (hi); /* get exp */ NormUFP (fop); /* normalize */ } else fop->sign = fop->exp = 0; /* clean zero */ return; } /* Normalize unpacked floating point number */ void NormUFP (struct ufp *fop) { if ((fop->h & FP_M_FRH) || fop->l) { /* any fraction? */ while ((fop->h & FP_NORM) == 0) { /* until norm */ fop->h = (fop->h << 4) | ((fop->l >> 28) & 0xF); fop->l = fop->l << 4; fop->exp = fop->exp - 1; } } else fop->sign = fop->exp = 0; /* clean 0 */ return; } /* Round fp number, store, generate condition codes */ uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd) { uint32 hi, cc; if (rnd && (fop->l & FP_ROUND)) { /* round? */ fop->h = fop->h + 1; /* add 1 to frac */ if (fop->h & FP_CARRY) { /* carry out? */ fop->h = fop->h >> 4; /* renormalize */ fop->exp = fop->exp + 1; /* incr exp */ } } if (fop->h == 0) { /* result 0? */ hi = fop->l = 0; /* store clean 0 */ cc = 0; } else if (fop->exp < 0) { /* underflow? */ hi = fop->l = 0; /* store clean 0 */ cc = CC_V; } else if (fop->exp > FP_M_EXP) { /* overflow? */ hi = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF; fop->l = 0xFFFFFFFF; cc = (CC_V | ((fop->sign)? CC_L: CC_G)); } else { hi = ((fop->sign & FP_M_SIGN) << FP_V_SIGN) | /* pack result */ ((fop->exp & FP_M_EXP) << FP_V_EXP) | ((fop->h & FP_M_FRH) << FP_V_FRH); cc = (fop->sign)? CC_L: CC_G; /* set cc's */ } if (OP_DPFP (op)) { /* double precision? */ D[r1 >> 1].h = hi; D[r1 >> 1].l = fop->l; } else { WriteFReg (r1, hi); } return cc; } /* Generate exception result */ uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1) { uint32 cc = CC_V; if (fop->exp < 0) /* undf? clean 0 */ fop->h = fop->l = 0; else { fop->h = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF; /* overflow */ fop->l = 0xFFFFFFFF; cc = cc | ((fop->sign)? CC_L: CC_G); } if (OP_DPFP (op)) { /* double precision? */ D[r1 >> 1].h = fop->h; D[r1 >> 1].l = fop->l; } else { WriteFReg (r1, fop->h); } return cc; } simh-3.8.1/ALTAIR/0000755000175000017500000000000011004504526011612 5ustar vlmvlmsimh-3.8.1/ALTAIR/altair_sys.c0000644000175000017500000002724410303752072014143 0ustar vlmvlm/* altair_sys.c: MITS Altair system interface Copyright (c) 1997-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. */ #include #include "altair_defs.h" extern DEVICE cpu_dev; extern DEVICE dsk_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern DEVICE sio_dev; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE lpt_dev; extern unsigned char M[]; extern int32 saved_PC; /* SCP data structures sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax number of words needed 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[] = "Altair 8800"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 4; DEVICE *sim_devices[] = { &cpu_dev, &sio_dev, &ptr_dev, &ptp_dev, &dsk_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unknown I/O Instruction", "HALT instruction", "Breakpoint", "Invalid Opcode" }; static const char *opcode[] = { "NOP", "LXI B", "STAX B", "INX B", /* 000-003 */ "INR B", "DCR B", "MVI B", "RLC", /* 004-007 */ "???", "DAD B", "LDAX B", "DCX B", /* 010-013 */ "INR C", "DCR C", "MVI C", "RRC", /* 014-017 */ "???", "LXI D", "STAX D", "INX D", /* 020-023 */ "INR D", "DCR D", "MVI D", "RAL", /* 024-027 */ "???", "DAD D", "LDAX D", "DCX D", /* 030-033 */ "INR E", "DCR E", "MVI E", "RAR", /* 034-037 */ "???", "LXI H", "SHLD", "INX H", /* 040-043 */ "INR H", "DCR H", "MVI H", "DAA", /* 044-047 */ "???", "DAD H", "LHLD", "DCX H", /* 050-053 */ "INR L", "DCR L", "MVI L", "CMA", /* 054-057 */ "???", "LXI SP", "STA", "INX SP", /* 060-063 */ "INR M", "DCR M", "MVI M", "STC", /* 064-067 */ "???", "DAD SP", "LDA", "DCX SP", /* 070-073 */ "INR A", "DCR A", "MVI A", "CMC", /* 074-077 */ "MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", /* 100-103 */ "MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 104-107 */ "MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", /* 110-113 */ "MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 114-117 */ "MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", /* 120-123 */ "MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 124-127 */ "MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", /* 130-133 */ "MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 134-137 */ "MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", /* 140-143 */ "MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 144-147 */ "MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", /* 150-153 */ "MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 154-157 */ "MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", /* 160-163 */ "MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 164-167 */ "MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", /* 170-173 */ "MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 174-177 */ "ADD B", "ADD C", "ADD D", "ADD E", /* 200-203 */ "ADD H", "ADD L", "ADD M", "ADD A", /* 204-207 */ "ADC B", "ADC C", "ADC D", "ADC E", /* 210-213 */ "ADC H", "ADC L", "ADC M", "ADC A", /* 214-217 */ "SUB B", "SUB C", "SUB D", "SUB E", /* 220-223 */ "SUB H", "SUB L", "SUB M", "SUB A", /* 224-227 */ "SBB B", "SBB C", "SBB D", "SBB E", /* 230-233 */ "SBB H", "SBB L", "SBB M", "SBB A", /* 234-237 */ "ANA B", "ANA C", "ANA D", "ANA E", /* 240-243 */ "ANA H", "ANA L", "ANA M", "ANA A", /* 244-247 */ "XRA B", "XRA C", "XRA D", "XRA E", /* 250-253 */ "XRA H", "XRA L", "XRA M", "XRA A", /* 254-257 */ "ORA B", "ORA C", "ORA D", "ORA E", /* 260-263 */ "ORA H", "ORA L", "ORA M", "ORA A", /* 264-267 */ "CMP B", "CMP C", "CMP D", "CMP E", /* 270-273 */ "CMP H", "CMP L", "CMP M", "CMP A", /* 274-277 */ "RNZ", "POP B", "JNZ", "JMP", /* 300-303 */ "CNZ", "PUSH B", "ADI", "RST 0", /* 304-307 */ "RZ", "RET", "JZ", "???", /* 310-313 */ "CZ", "CALL", "ACI", "RST 1", /* 314-317 */ "RNC", "POP D", "JNC", "OUT", /* 320-323 */ "CNC", "PUSH D", "SUI", "RST 2", /* 324-327 */ "RC", "???", "JC", "IN", /* 330-333 */ "CC", "???", "SBI", "RST 3", /* 334-337 */ "RPO", "POP H", "JPO", "XTHL", /* 340-343 */ "CPO", "PUSH H", "ANI", "RST 4", /* 344-347 */ "RPE", "PCHL", "JPE", "XCHG", /* 350-353 */ "CPE", "???", "XRI", "RST 5", /* 354-357 */ "RP", "POP PSW", "JP", "DI", /* 360-363 */ "CP", "PUSH PSW", "ORI", "RST 6", /* 364-367 */ "RM", "SPHL", "JM", "EI", /* 370-373 */ "CM", "???", "CPI", "RST 7", /* 374-377 */ }; int32 oplen[256] = { 1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, 0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1, 1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 }; /* This is the binary loader. The input file is considered to be a string of literal bytes with no format special format. The load starts at the current value of the PC. */ int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 i, addr = 0, cnt = 0; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; addr = saved_PC; while ((i = getc (fileref)) != EOF) { M[addr] = i; addr++; cnt++; } /* end while */ printf ("%d Bytes loaded.\n", cnt); return (SCPE_OK); } /* Symbolic output Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: status = error code */ int32 fprint_sym (FILE *of, int32 addr, uint32 *val, UNIT *uptr, int32 sw) { int32 cflag, c1, c2, inst, adr; cflag = (uptr == NULL) || (uptr == &cpu_unit); c1 = (val[0] >> 8) & 0177; c2 = val[0] & 0177; if (sw & SWMASK ('A')) { fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (sw & SWMASK ('C')) { fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; inst = val[0]; fprintf (of, "%s", opcode[inst]); if (oplen[inst] == 2) { if (strchr(opcode[inst], ' ') != NULL) fprintf (of, ","); else fprintf (of, " "); fprintf (of, "%o", val[1]); } if (oplen[inst] == 3) { adr = val[1] & 0xFF; adr |= (val[2] << 8) & 0xff00; if (strchr(opcode[inst], ' ') != NULL) fprintf (of, ","); else fprintf (of, " "); fprintf (of, "%o", adr); } return -(oplen[inst] - 1); } /* 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 */ int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, uint32 *val, int32 sw) { int32 cflag, i = 0, j, r; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = (uint32) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; return SCPE_OK; } /* An instruction: get opcode (all characters until null, comma, or numeric (including spaces). */ while (1) { if (*cptr == ',' || *cptr == '\0' || isdigit(*cptr)) break; gbuf[i] = toupper(*cptr); cptr++; i++; } /* Allow for RST which has numeric as part of opcode */ if (toupper(gbuf[0]) == 'R' && toupper(gbuf[1]) == 'S' && toupper(gbuf[2]) == 'T') { gbuf[i] = toupper(*cptr); cptr++; i++; } /* Allow for 'MOV' which is only opcode that has comma in it. */ if (toupper(gbuf[0]) == 'M' && toupper(gbuf[1]) == 'O' && toupper(gbuf[2]) == 'V') { gbuf[i] = toupper(*cptr); cptr++; i++; gbuf[i] = toupper(*cptr); cptr++; i++; } /* kill trailing spaces if any */ gbuf[i] = '\0'; for (j = i - 1; gbuf[j] == ' '; j--) { gbuf[j] = '\0'; } /* find opcode in table */ for (j = 0; j < 256; j++) { if (strcmp(gbuf, opcode[j]) == 0) break; } if (j > 255) /* not found */ return SCPE_ARG; val[0] = j; /* store opcode */ if (oplen[j] < 2) /* if 1-byter we are done */ return SCPE_OK; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); /* get address */ sscanf(gbuf, "%o", &r); if (oplen[j] == 2) { val[1] = r & 0xFF; return (-1); } val[1] = r & 0xFF; val[2] = (r >> 8) & 0xFF; return (-2); } simh-3.8.1/ALTAIR/altair_cpu.c0000644000175000017500000011054310306717256014117 0ustar vlmvlm/* altair_cpu.c: MITS Altair Intel 8080 CPU simulator Copyright (c) 1997-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. cpu 8080 CPU 08-Oct-02 RMS Tied off spurious compiler warnings The register state for the 8080 CPU is: A<0:7> Accumulator BC<0:15> BC Register Pair DE<0:15> DE Register Pair HL<0:15> HL Register Pair C carry flag Z zero flag S Sign bit AC Aux carry P Parity bit PC<0:15> program counter SP<0:15> Stack Pointer The 8080 is an 8-bit CPU, which uses 16-bit registers to address up to 64KB of memory. The 78 basic instructions come in 1, 2, and 3-byte flavors. This routine is the instruction decode routine for the 8080. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction I/O error in I/O simulator Invalid OP code (if ITRAP is set on CPU) 2. Interrupts. There are 8 possible levels of interrupt, and in effect they do a hardware CALL instruction to one of 8 possible low memory addresses. 3. Non-existent memory. On the 8080, reads to non-existent memory return 0377, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against actual memory size. 4. Adding I/O devices. These modules must be modified: altair_cpu.c add I/O service routines to dev_table altair_sys.c add pointer to data structures in sim_devices */ #include #include "altair_defs.h" #define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ #define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) #define UNIT_V_CHIP (UNIT_V_UF+1) /* 8080 or Z80 */ #define UNIT_CHIP (1 << UNIT_V_CHIP) #define UNIT_V_MSIZE (UNIT_V_UF+2) /* Memory Size */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) unsigned char M[MAXMEMSIZE]; /* memory */ int32 A = 0; /* accumulator */ int32 BC = 0; /* BC register pair */ int32 DE = 0; /* DE register pair */ int32 HL = 0; /* HL register pair */ int32 SP = 0; /* Stack pointer */ int32 C = 0; /* carry flag */ int32 Z = 0; /* Zero flag */ int32 AC = 0; /* Aux carry */ int32 S = 0; /* sign flag */ int32 P = 0; /* parity flag */ int32 saved_PC = 0; /* program counter */ int32 SR = 0; /* switch register */ int32 INTE = 0; /* Interrupt Enable */ int32 int_req = 0; /* Interrupt request */ int32 chip = 0; /* 0 = 8080 chip, 1 = z80 chip */ int32 PCX; /* External view of PC */ extern int32 sim_int_char; extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ /* function prototypes */ 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); void setarith(int32 reg); void setlogical(int32 reg); void setinc(int32 reg); int32 getreg(int32 reg); void putreg(int32 reg, int32 val); int32 getpair(int32 reg); int32 getpush(int32 reg); void putpush(int32 reg, int32 data); void putpair(int32 reg, int32 val); void parity(int32 reg); int32 cond(int32 con); extern int32 sio0s(int32 io, int32 data); extern int32 sio0d(int32 io, int32 data); extern int32 sio1s(int32 io, int32 data); extern int32 sio1d(int32 io, int32 data); extern int32 dsk10(int32 io, int32 data); extern int32 dsk11(int32 io, int32 data); extern int32 dsk12(int32 io, int32 data); int32 nulldev(int32 io, int32 data); /* This is the I/O configuration table. There are 255 possible device addresses, if a device is plugged to a port it's routine address is here, 'nulldev' means no device is available */ struct idev { int32 (*routine)(); }; struct idev dev_table[256] = { {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 000 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 004 */ {&dsk10}, {&dsk11}, {&dsk12}, {&nulldev}, /* 010 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 014 */ {&sio0s}, {&sio0d}, {&sio1s}, {&sio1d}, /* 020 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 024 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 030 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 034 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 040 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 044 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 050 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 054 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 060 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 064 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 070 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 074 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 100 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 104 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 110 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 114 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 120 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 124 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 130 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 134 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 140 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 144 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 150 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 154 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 160 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 164 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 170 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 174 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 200 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 204 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 210 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 214 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 220 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 224 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 230 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 234 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 240 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 244 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 250 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 254 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 260 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 264 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 270 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 274 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 300 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 304 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 310 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 314 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 320 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 324 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 330 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 334 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 340 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 344 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 350 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 354 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 360 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 364 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 370 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev} /* 374 */ }; /* Altair MITS standard BOOT EPROM, fits in upper 256K of memory */ int32 bootrom[256] = { 0041, 0000, 0114, 0021, 0030, 0377, 0016, 0346, 0032, 0167, 0023, 0043, 0015, 0302, 0010, 0377, 0303, 0000, 0114, 0000, 0000, 0000, 0000, 0000, 0363, 0061, 0142, 0115, 0257, 0323, 0010, 0076, /* 46000 */ 0004, 0323, 0011, 0303, 0031, 0114, 0333, 0010, /* 46010 */ 0346, 0002, 0302, 0016, 0114, 0076, 0002, 0323, /* 46020 */ 0011, 0333, 0010, 0346, 0100, 0302, 0016, 0114, 0021, 0000, 0000, 0006, 0000, 0333, 0010, 0346, 0004, 0302, 0045, 0114, 0076, 0020, 0365, 0325, 0305, 0325, 0021, 0206, 0200, 0041, 0324, 0114, 0333, 0011, 0037, 0332, 0070, 0114, 0346, 0037, 0270, 0302, 0070, 0114, 0333, 0010, 0267, 0372, 0104, 0114, 0333, 0012, 0167, 0043, 0035, 0312, 0132, 0114, 0035, 0333, 0012, 0167, 0043, 0302, 0104, 0114, 0341, 0021, 0327, 0114, 0001, 0200, 0000, 0032, 0167, 0276, 0302, 0301, 0114, 0200, 0107, 0023, 0043, 0015, 0302, 0141, 0114, 0032, 0376, 0377, 0302, 0170, 0114, 0023, 0032, 0270, 0301, 0353, 0302, 0265, 0114, 0361, 0361, 0052, 0325, 0114, 0325, 0021, 0000, 0377, 0315, 0316, 0114, 0321, 0332, 0276, 0114, 0315, 0316, 0114, 0322, 0256, 0114, 0004, 0004, 0170, 0376, 0040, 0332, 0054, 0114, 0006, 0001, 0312, 0054, 0114, 0333, 0010, 0346, 0002, 0302, 0240, 0114, 0076, 0001, 0323, 0011, 0303, 0043, 0114, 0076, 0200, 0323, 0010, 0303, 0000, 0000, 0321, 0361, 0075, 0302, 0056, 0114, 0076, 0103, 0001, 0076, 0117, 0001, 0076, 0115, 0107, 0076, 0200, 0323, 0010, 0170, 0323, 0001, 0303, 0311, 0114, 0172, 0274, 0300, 0173, 0275, 0311, 0204, 0000, 0114, 0044, 0026, 0126, 0026, 0000, 0000, 0000, 0000, 0000 }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, 16) }, { ORDATA (A, A, 8) }, { ORDATA (BC, BC, 16) }, { ORDATA (DE, DE, 16) }, { ORDATA (HL, HL, 16) }, { ORDATA (SP, SP, 16) }, { FLDATA (C, C, 16) }, { FLDATA (Z, Z, 16) }, { FLDATA (AC, AC, 16) }, { FLDATA (S, S, 16) }, { FLDATA (P, P, 16) }, { FLDATA (INTE, INTE, 16) }, { ORDATA (SR, SR, 16) }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_CHIP, UNIT_CHIP, "Z80", "Z80", NULL }, { UNIT_CHIP, 0, "8080", "8080", NULL }, { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL }, { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 16, 1, 8, 8, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; int32 sim_instr (void) { extern int32 sim_interval; int32 PC, IR, OP, DAR, reason, hi, lo, carry, i; PC = saved_PC & ADDRMASK; /* load local PC */ C = C & 0200000; reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } if (int_req > 0) { /* interrupt? */ /* 8080 interrupts not implemented yet. None were used, on a standard Altair 8800. All I/O is programmed. */ } /* end interrupt */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } if (PC == 0177400) { /* BOOT PROM address */ for (i = 0; i < 250; i++) { M[i + 0177400] = bootrom[i] & 0xFF; } } PCX = PC; IR = OP = M[PC]; /* fetch instruction */ PC = (PC + 1) & ADDRMASK; /* increment PC */ sim_interval--; if (OP == 0166) { /* HLT Instruction*/ reason = STOP_HALT; PC--; continue; } /* Handle below all operations which refer to registers or register pairs. After that, a large switch statement takes care of all other opcodes */ if ((OP & 0xC0) == 0x40) { /* MOV */ DAR = getreg(OP & 0x07); putreg((OP >> 3) & 0x07, DAR); continue; } if ((OP & 0xC7) == 0x06) { /* MVI */ putreg((OP >> 3) & 0x07, M[PC]); PC++; continue; } if ((OP & 0xCF) == 0x01) { /* LXI */ DAR = M[PC] & 0x00ff; PC++; DAR = DAR | (M[PC] <<8) & 0xFF00;; putpair((OP >> 4) & 0x03, DAR); PC++; continue; } if ((OP & 0xEF) == 0x0A) { /* LDAX */ DAR = getpair((OP >> 4) & 0x03); putreg(7, M[DAR]); continue; } if ((OP & 0xEF) == 0x02) { /* STAX */ DAR = getpair((OP >> 4) & 0x03); M[DAR] = getreg(7); continue; } if ((OP & 0xF8) == 0xB8) { /* CMP */ DAR = A & 0xFF; DAR -= getreg(OP & 0x07); setarith(DAR); continue; } if ((OP & 0xC7) == 0xC2) { /* JMP */ if (cond((OP >> 3) & 0x07) == 1) { lo = M[PC]; PC++; hi = M[PC]; PC++; PC = (hi << 8) + lo; } else { PC += 2; } continue; } if ((OP & 0xC7) == 0xC4) { /* CALL */ if (cond((OP >> 3) & 0x07) == 1) { lo = M[PC]; PC++; hi = M[PC]; PC++; SP--; M[SP] = (PC >> 8) & 0xff; SP--; M[SP] = PC & 0xff; PC = (hi << 8) + lo; } else { PC += 2; } continue; } if ((OP & 0xC7) == 0xC0) { /* RET */ if (cond((OP >> 3) & 0x07) == 1) { PC = M[SP]; SP++; PC |= (M[SP] << 8) & 0xff00; SP++; } continue; } if ((OP & 0xC7) == 0xC7) { /* RST */ SP--; M[SP] = (PC >> 8) & 0xff; SP--; M[SP] = PC & 0xff; PC = OP & 0x38; continue; } if ((OP & 0xCF) == 0xC5) { /* PUSH */ DAR = getpush((OP >> 4) & 0x03); SP--; M[SP] = (DAR >> 8) & 0xff; SP--; M[SP] = DAR & 0xff; continue; } if ((OP & 0xCF) == 0xC1) { /*POP */ DAR = M[SP]; SP++; DAR |= M[SP] << 8; SP++; putpush((OP >> 4) & 0x03, DAR); continue; } if ((OP & 0xF8) == 0x80) { /* ADD */ A += getreg(OP & 0x07); setarith(A); A = A & 0xFF; continue; } if ((OP & 0xF8) == 0x88) { /* ADC */ carry = 0; if (C) carry = 1; A += getreg(OP & 0x07); A += carry; setarith(A); A = A & 0xFF; continue; } if ((OP & 0xF8) == 0x90) { /* SUB */ A -= getreg(OP & 0x07); setarith(A); A = A & 0xFF; continue; } if ((OP & 0xF8) == 0x98) { /* SBB */ carry = 0; if (C) carry = 1; A -= (getreg(OP & 0x07)) + carry ; setarith(A); A = A & 0xFF; continue; } if ((OP & 0xC7) == 0x04) { /* INR */ DAR = getreg((OP >> 3) & 0x07); DAR++; setinc(DAR); DAR = DAR & 0xFF; putreg((OP >> 3) & 0x07, DAR); continue; } if ((OP & 0xC7) == 0x05) { /* DCR */ DAR = getreg((OP >> 3) & 0x07); DAR--; setinc(DAR); DAR = DAR & 0xFF; putreg((OP >> 3) & 0x07, DAR); continue; } if ((OP & 0xCF) == 0x03) { /* INX */ DAR = getpair((OP >> 4) & 0x03); DAR++; DAR = DAR & 0xFFFF; putpair((OP >> 4) & 0x03, DAR); continue; } if ((OP & 0xCF) == 0x0B) { /* DCX */ DAR = getpair((OP >> 4) & 0x03); DAR--; DAR = DAR & 0xFFFF; putpair((OP >> 4) & 0x03, DAR); continue; } if ((OP & 0xCF) == 0x09) { /* DAD */ HL += getpair((OP >> 4) & 0x03); C = 0; if (HL & 0x10000) C = 0200000; HL = HL & 0xFFFF; continue; } if ((OP & 0xF8) == 0xA0) { /* ANA */ A &= getreg(OP & 0x07); C = 0; setlogical(A); A &= 0xFF; continue; } if ((OP & 0xF8) == 0xA8) { /* XRA */ A ^= getreg(OP & 0x07); C = 0; setlogical(A); A &= 0xFF; continue; } if ((OP & 0xF8) == 0xB0) { /* ORA */ A |= getreg(OP & 0x07); C = 0; setlogical(A); A &= 0xFF; continue; } /* The Big Instruction Decode Switch */ switch (IR) { /* Logical instructions */ case 0376: { /* CPI */ DAR = A & 0xFF; DAR -= M[PC]; PC++; setarith(DAR); break; } case 0346: { /* ANI */ A &= M[PC]; PC++; C = AC = 0; setlogical(A); A &= 0xFF; break; } case 0356: { /* XRI */ A ^= M[PC]; PC++; C = AC = 0; setlogical(A); A &= 0xFF; break; } case 0366: { /* ORI */ A |= M[PC]; PC++; C = AC = 0; setlogical(A); A &= 0xFF; break; } /* Jump instructions */ case 0303: { /* JMP */ lo = M[PC]; PC++; hi = M[PC]; PC++; PC = (hi << 8) + lo; break; } case 0351: { /* PCHL */ PC = HL; break; } case 0315: { /* CALL */ lo = M[PC]; PC++; hi = M[PC]; PC++; SP--; M[SP] = (PC >> 8) & 0xff; SP--; M[SP] = PC & 0xff; PC = (hi << 8) + lo; break; } case 0311: { /* RET */ PC = M[SP]; SP++; PC |= (M[SP] << 8) & 0xff00; SP++; break; } /* Data Transfer Group */ case 062: { /* STA */ lo = M[PC]; PC++; hi = M[PC]; PC++; DAR = (hi << 8) + lo; M[DAR] = A; break; } case 072: { /* LDA */ lo = M[PC]; PC++; hi = M[PC]; PC++; DAR = (hi << 8) + lo; A = M[DAR]; break; } case 042: { /* SHLD */ lo = M[PC]; PC++; hi = M[PC]; PC++; DAR = (hi << 8) + lo; M[DAR] = HL; DAR++; M[DAR] = (HL >>8) & 0x00ff; break; } case 052: { /* LHLD */ lo = M[PC]; PC++; hi = M[PC]; PC++; DAR = (hi << 8) + lo; HL = M[DAR]; DAR++; HL = HL | (M[DAR] <<8); break; } case 0353: { /* XCHG */ DAR = HL; HL = DE; DE = DAR; break; } /* Arithmetic Group */ case 0306: { /* ADI */ A += M[PC]; PC++; setarith(A); A = A & 0xFF; break; } case 0316: { /* ACI */ carry = 0; if (C) carry = 1; A += M[PC]; A += carry; PC++; setarith(A); A = A & 0xFF; break; } case 0326: { /* SUI */ A -= M[PC]; PC++; setarith(A); A = A & 0xFF; break; } case 0336: { /* SBI */ carry = 0; if (C) carry = 1; A -= (M[PC] + carry); PC++; setarith(A); A = A & 0xFF; break; } case 047: { /* DAA */ DAR = A & 0x0F; if (DAR > 9 || AC > 0) { DAR += 6; A &= 0xF0; A |= DAR & 0x0F; if (DAR & 0x10) AC = 0200000; else AC = 0; } DAR = (A >> 4) & 0x0F; if (DAR > 9 || AC > 0) { DAR += 6; if (AC) DAR++; A &= 0x0F; A |= (DAR << 4); } if ((DAR << 4) & 0x100) C = 0200000; else C = 0; if (A & 0x80) { S = 0200000; } else { S = 0; } if ((A & 0xff) == 0) Z = 0200000; else Z = 0; parity(A); A = A & 0xFF; break; } case 07: { /* RLC */ C = 0; C = (A << 9) & 0200000; A = (A << 1) & 0xFF; if (C) A |= 0x01; break; } case 017: { /* RRC */ C = 0; if ((A & 0x01) == 1) C |= 0200000; A = (A >> 1) & 0xFF; if (C) A |= 0x80; break; } case 027: { /* RAL */ DAR = C; C = 0; C = (A << 9) & 0200000; A = (A << 1) & 0xFF; if (DAR) A |= 1; else A &= 0xFE; break; } case 037: { /* RAR */ DAR = C; C = 0; if ((A & 0x01) == 1) C |= 0200000; A = (A >> 1) & 0xFF; if (DAR) A |= 0x80; else A &= 0x7F; break; } case 057: { /* CMA */ A = ~ A; A &= 0xFF; break; } case 077: { /* CMC */ C = ~ C; C &= 0200000; break; } case 067: { /* STC */ C = 0200000; break; } /* Stack, I/O & Machine Control Group */ case 0: { /* NOP */ break; } case 0343: { /* XTHL */ lo = M[SP]; hi = M[SP + 1]; M[SP] = HL & 0xFF; M[SP + 1] = (HL >> 8) & 0xFF; HL = (hi << 8) + lo; break; } case 0371: { /* SPHL */ SP = HL; break; } case 0373: { /* EI */ INTE = 0200000; break; } case 0363: { /* DI */ INTE = 0; break; } case 0333: { /* IN */ DAR = M[PC] & 0xFF; PC++; if (DAR == 0xFF) { A = (SR >> 8) & 0xFF; } else { A = dev_table[DAR].routine(0, 0); } break; } case 0323: { /* OUT */ DAR = M[PC] & 0xFF; PC++; dev_table[DAR].routine(1, A); break; } default: { if (cpu_unit.flags & UNIT_OPSTOP) { reason = STOP_OPCODE; PC--; } break; } } } /* Simulation halted */ saved_PC = PC; return reason; } /* Test an 8080 flag condition and return 1 if true, 0 if false */ int32 cond(int32 con) { switch (con) { case 0: if (Z == 0) return (1); break; case 1: if (Z != 0) return (1); break; case 2: if (C == 0) return (1); break; case 3: if (C != 0) return (1); break; case 4: if (P == 0) return (1); break; case 5: if (P != 0) return (1); break; case 6: if (S == 0) return (1); break; case 7: if (S != 0) return (1); break; default: break; } return (0); } /* Set the arry, ign, ero and

arity flags following an arithmetic operation on 'reg'. */ void setarith(int32 reg) { int32 bc = 0; if (reg & 0x100) C = 0200000; else C = 0; if (reg & 0x80) { bc++; S = 0200000; } else { S = 0; } if ((reg & 0xff) == 0) Z = 0200000; else Z = 0; AC = 0; if (cpu_unit.flags & UNIT_CHIP) { P = 0; /* parity is zero for *all* arith ops on Z80 */ } else { parity(reg); } } /* Set the arry, ign, ero amd

arity flags following a logical (bitwise) operation on 'reg'. */ void setlogical(int32 reg) { C = 0; if (reg & 0x80) { S = 0200000; } else { S = 0; } if ((reg & 0xff) == 0) Z = 0200000; else Z = 0; AC = 0; parity(reg); } /* Set the Parity (P) flag based on parity of 'reg', i.e., number of bits on even: P=0200000, else P=0 */ void parity(int32 reg) { int32 bc = 0; if (reg & 0x01) bc++; if (reg & 0x02) bc++; if (reg & 0x04) bc++; if (reg & 0x08) bc++; if (reg & 0x10) bc++; if (reg & 0x20) bc++; if (reg & 0x40) bc++; if (reg & 0x80) bc++; P = ~(bc << 16); P &= 0200000; } /* Set the ign, ero amd

arity flags following an INR/DCR operation on 'reg'. */ void setinc(int32 reg) { int32 bc = 0; if (reg & 0x80) { bc++; S = 0200000; } else { S = 0; } if ((reg & 0xff) == 0) Z = 0200000; else Z = 0; if (cpu_unit.flags & UNIT_CHIP) { P = 0; /* parity is zero for *all* arith ops on Z80 */ } else { parity(reg); } } /* Get an 8080 register and return it */ int32 getreg(int32 reg) { switch (reg) { case 0: return ((BC >>8) & 0x00ff); case 1: return (BC & 0x00FF); case 2: return ((DE >>8) & 0x00ff); case 3: return (DE & 0x00ff); case 4: return ((HL >>8) & 0x00ff); case 5: return (HL & 0x00ff); case 6: return (M[HL]); case 7: return (A); default: break; } return 0; } /* Put a value into an 8080 register from memory */ void putreg(int32 reg, int32 val) { switch (reg) { case 0: BC = BC & 0x00FF; BC = BC | (val <<8); break; case 1: BC = BC & 0xFF00; BC = BC | val; break; case 2: DE = DE & 0x00FF; DE = DE | (val <<8); break; case 3: DE = DE & 0xFF00; DE = DE | val; break; case 4: HL = HL & 0x00FF; HL = HL | (val <<8); break; case 5: HL = HL & 0xFF00; HL = HL | val; break; case 6: M[HL] = val & 0xff; break; case 7: A = val & 0xff; default: break; } } /* Return the value of a selected register pair */ int32 getpair(int32 reg) { switch (reg) { case 0: return (BC); case 1: return (DE); case 2: return (HL); case 3: return (SP); default: break; } return 0; } /* Return the value of a selected register pair, in PUSH format where 3 means A& flags, not SP */ int32 getpush(int32 reg) { int32 stat; switch (reg) { case 0: return (BC); case 1: return (DE); case 2: return (HL); case 3: stat = A << 8; if (S) stat |= 0x80; if (Z) stat |= 0x40; if (AC) stat |= 0x10; if (P) stat |= 0x04; stat |= 0x02; if (C) stat |= 0x01; return (stat); default: break; } return 0; } /* Place data into the indicated register pair, in PUSH format where 3 means A& flags, not SP */ void putpush(int32 reg, int32 data) { switch (reg) { case 0: BC = data; break; case 1: DE = data; break; case 2: HL = data; break; case 3: A = (data >> 8) & 0xff; S = Z = AC = P = C = 0; if (data & 0x80) S = 0200000; if (data & 0x40) Z = 0200000; if (data & 0x10) AC = 0200000; if (data & 0x04) P = 0200000; if (data & 0x01) C = 0200000; break; default: break; } } /* Put a value into an 8080 register pair */ void putpair(int32 reg, int32 val) { switch (reg) { case 0: BC = val; break; case 1: DE = val; break; case 2: HL = val; break; case 3: SP = val; break; default: break; } } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { C = 0; Z = 0; saved_PC = 0; int_req = 0; 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] & 0377; 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 & 0377; return SCPE_OK; } 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; } int32 nulldev(int32 flag, int32 data) { if (flag == 0) return (0377); return 0; } simh-3.8.1/ALTAIR/altair_defs.h0000644000175000017500000000370610302350344014243 0ustar vlmvlm/* altair_defs.h: MITS Altair simulator definitions Copyright (c) 1997-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. */ #include "sim_defs.h" /* simulator defns */ /* Memory */ #define MAXMEMSIZE 65536 /* max memory size */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_OPCODE 4 simh-3.8.1/ALTAIR/altair.txt0000644000175000017500000001676306311622302013642 0ustar vlmvlmAltair 8800 Simulator ===================== 1. Background. The MITS (Micro Instrumentation and Telemetry Systems) Altair 8800 was announced on the January 1975 cover of Popular Electronics, which boasted you could buy and build this powerful computer kit for only $397. The kit consisted at that time of only the parts to build a case, power supply, card cage (18 slots), CPU card, and memory card with 256 *bytes* of memory. Still, thousands were ordered within the first few months after the announcement, starting the personal computer revolution as we know it today. Many laugh at the small size of the that first kit, noting there were no peripherals and the 256 byte memory size. But the computer was an open system, and by 1977 MITS and many other small startups had added many expansion cards to make the Altair quite a respectable little computer. The "Altair Bus" that made this possible was soon called the S-100 Bus, later adopted as an industry standard, and eventually became the IEE-696 Bus. 2. Hardware We are simulating a fairly "loaded" Altair 8800 from about 1977, with the following configuration: device simulates name(s) CPU Altair 8800 with Intel 8080 CPU board, 62KB of RAM, 2K of EPROM with start boot ROM. 2SIO MITS 88-2SIO Dual Serial Interface Board. Port 1 is assumed to be connected to a serial "glass TTY" that is your terminal running the Simulator. PTR Paper Tape Reader attached to port 2 of the 2SIO board. PTP Paper Tape Punch attached to port 2 of the 2SIO board. This also doubles as a printer port. DSK MITS 88-DISK Floppy Disk controller with up to eight drives. 2.1 CPU We have 2 CPU options that were not present on the original machine but are useful in the simulator. We also allow you to select memory sizes, but be aware that some sample software requires the full 64K (i.e. CP/M) and the MITS Disk Basic and Altair DOS require about a minimum of 24K. SET CPU 8080 Simulates the 8080 CPU (normal) SET CPU Z80 Simulates the later Z80 CPU [At the present time this is not fully implemented and is not to be trusted with real Z80 software] SET CPU ITRAP Causes the simulator to halt if an invalid 8080 Opcode is detected. SET CPU NOITRAP Does not stop on an invalid Opcode. This is how the real 8080 works. SET CPU 4K SET CPU 8K SET CPU 12K SET CPU 16K ...... SET CPU 64K All these set various CPU memory configurations. The 2K EPROM at the high end of memory is always present and will always boot. The BOOT EPROM card starts at address 177400. Jumping to this address will always boot drive 0 of the floppy controller. If no valid bootable software is present there the machine crashes. This is historically accurate behavior. The real 8080, on receiving a HLT (Halt) instruction, freezes the processor and only an interrupt or CPU hardware reset will restore it. The simulator is alot nicer, it will halt but send you back to the simulator command line. CPU Registers include the following: name size comments PC 16 The Program Counter A 8 The accumulator BC 16 The BC register pair. Register B is the high 8 bits, C is the lower 8 bits DE 16 The DE register pair. D is the top 8 bits, E is the bottom. HL 16 The HL register pair. H is top, L is bottom. C 1 Carry flag. Z 1 Zero Flag. AC 1 Auxillary Carry flag. P 1 Parity flag. S 1 Sign flag. SR 16 The front panel switches. BREAK 16 Breakpoint address (377777 to disable). WRU 8 The interrupt character. This starts as 005 (ctrl-E) but some Altair software uses this keystroke so best to change this to something exotic such as 035 (which is Ctl-]). 2.2 The Serial I/O Card (2SIO) This simple programmed I/O device provides 2 serial ports to the outside world, which could be hardware jumpered to support RS-232 plugs or a TTY current loop interface. The standard I/O addresses assigned by MITS was 20-21 (octal) for the first port, and 22-23 (octal) for the second. We follow this standard in the Simulator. The simulator directs I/O to/from the first port to the screen. The second port reads from an attachable "tape reader" file on input, and writes to an attachable "punch file" on output. These files are considered a simple stream of 8-bit bytes. 2.3 The 88-DISK controller. The MITS 88-DISK is a simple programmed I/O interface to the MITS 8-inch floppy drive, which was basically a Pertec FD-400 with a power supply and buffer board builtin. The controller supports neither interrupts nor DMA, so floppy access required the sustained attention of the CPU. The standard I/O addresses were 10, 11, and 12 (octal), and we follow the standard. Details on controlling this hardware are in the altair_dsk.c source file. 3. Sample Software Running an Altair in 1977 you would be running either MITS Disk Extended BASIC, or the brand new and sexy CP/M Operating System from Digital Research. Or possibly, you ordered Altair DOS back when it was promised in 1975, and are still waiting for it to be delivered in early 1977. We have samples of all three for you to check out. We can't go into the details of how they work, but we'll give you a few hints. 3.1 CP/M Version 2.2 This version is my own port of the standard CP/M to the Altair. There were some "official" versions but I don't have them. None were endorsed or sold by MITS to my knowledge, however. To boot CP/M: sim> attach dsk0 altcpm.dsk sim> go 177400 62K CP/M VERSION 2.2 (ALTAIR 8800) A>DIR CP/M feels like DOS, sort of. DIR will work. I have included all the standard CP/M utilities, plus a few common public-domain ones. I also include the sources to the customized BIOS and some other small programs. TYPE will print an ASCII file. DUMP will dump a binary one. LS is a better DIR than DIR. ASM will assemble .ASM files to Hex, LOAD will "load" them to binary format (.COM). ED is a simple editor, #A command will bring the source file to the buffer, T command will "type" lines, L will move lines, E exits the editor. 20L20T will move down 20 lines, and type 20. Very DECish. DDT is the debugger, SUBMIT is a batch-type command processor. A sample batch file that will assemble and write out the bootable CP/M image (on drive A) is "SYSGEN.SUB". To run it, type "SUBMIT SYSGEN". 3.2 MITS Disk Extended BASIC Version 4.1 This was the commonly used software for serious users of the Altair computer. It is a powerful (but slow) BASIC with some extended commands to allow it to access and manage the disk. There was no operating system it ran under. To boot: sim> attach dsk0 mbasic.dsk sim> go 177400 MEMORY SIZE? [return] LINEPRINTER? C [return] HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) NUMBER OF FILES? 3 [return] NUMBER OF RANDOM FILES? 2 [return] 44297 BYTES FREE ALTAIR BASIC REV. 4.1 [DISK EXTENDED VERSION] COPYRIGHT 1977 BY MITS INC. OK mount 0 OK files 3.3 Altair DOS Version 1.0 This was long promised but not delivered until it was almost irrelevant. A short attempted tour will reveal it to be a dog, far inferior to CP/M. To boot: sim> attach dsk0 altdos.dsk sim> go 177400 MEMORY SIZE? 64 [return] INTERRUPTS? N [return] HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) HOW MANY DISK FILES? 3 [return] HOW MANY RANDOM FILES? 2 [return] 056769 BYTES AVAILABLE DOS MONITOR VER 1.0 COPYRIGHT 1977 BY MITS INC .mnt 0 .dir 0 simh-3.8.1/ALTAIR/altair_sio.c0000644000175000017500000001710010303752072014105 0ustar vlmvlm/* altair_sio: MITS Altair serial I/O card Copyright (c) 1997-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. These functions support a simulated MITS 2SIO interface card. The card had two physical I/O ports which could be connected to any serial I/O device that would connect to a current loop, RS232, or TTY interface. Available baud rates were jumper selectable for each port from 110 to 9600. All I/O is via programmed I/O. Each each has a status port and a data port. A write to the status port can select some options for the device (0x03 will reset the port). A read of the status port gets the port status: +---+---+---+---+---+---+---+---+ | X X X X X X O I | +---+---+---+---+---+---+---+---+ I - A 1 in this bit position means a character has been received on the data port and is ready to be read. O - A 1 in this bit means the port is ready to receive a character on the data port and transmit it out over the serial line. A read to the data port gets the buffered character, a write to the data port writes the character to the device. */ #include #include "altair_defs.h" #define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ #define UNIT_ANSI (1 << UNIT_V_ANSI) t_stat sio_svc (UNIT *uptr); t_stat sio_reset (DEVICE *dptr); t_stat ptr_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ /* 2SIO Standard I/O Data Structures */ UNIT sio_unit = { UDATA (&sio_svc, 0, 0), KBD_POLL_WAIT }; REG sio_reg[] = { { ORDATA (DATA, sio_unit.buf, 8) }, { ORDATA (STAT, sio_unit.u3, 8) }, { NULL } }; MTAB sio_mod[] = { { UNIT_ANSI, 0, "TTY", "TTY", NULL }, { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, { 0 } }; DEVICE sio_dev = { "2SIO", &sio_unit, sio_reg, sio_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &sio_reset, NULL, NULL, NULL }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ + UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG ptr_reg[] = { { ORDATA (DATA, ptr_unit.buf, 8) }, { ORDATA (STAT, ptr_unit.u3, 8) }, { ORDATA (POS, ptr_unit.pos, T_ADDR_W) }, { NULL } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, NULL, NULL, NULL }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ + UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG ptp_reg[] = { { ORDATA (DATA, ptp_unit.buf, 8) }, { ORDATA (STAT, ptp_unit.u3, 8) }, { ORDATA (POS, ptp_unit.pos, T_ADDR_W) }, { NULL } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL }; /* Service routines to handle simulator functions */ /* service routine - actually gets char & places in buffer */ int32 sio_svc (UNIT *uptr) { int32 temp; sim_activate (&sio_unit, sio_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ sio_unit.buf = temp & 0377; /* Save char */ sio_unit.u3 |= 0x01; /* Set status */ /* Do any special character handling here */ sio_unit.pos++; return SCPE_OK; } int32 ptr_svc (UNIT *uptr) { return SCPE_OK; } int32 ptp_svc (UNIT *uptr) { return SCPE_OK; } /* Reset routine */ int32 sio_reset (DEVICE *dptr) { sio_unit.buf = 0; /* Data */ sio_unit.u3 = 0x02; /* Status */ sim_activate (&sio_unit, sio_unit.wait); /* activate unit */ return SCPE_OK; } int32 ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; ptr_unit.u3 = 0x02; sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } int32 ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; ptp_unit.u3 = 0x02; sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. Each function is passed an 'io' flag, where 0 means a read from the port, and 1 means a write to the port. On input, the actual input is passed as the return value, on output, 'data' is written to the device. */ int32 sio0s(int32 io, int32 data) { if (io == 0) { return (sio_unit.u3); } else { if (data == 0x03) { /* reset port! */ sio_unit.u3 = 0x02; sio_unit.buf = 0; sio_unit.pos = 0; } return (0); } } int32 sio0d(int32 io, int32 data) { if (io == 0) { sio_unit.u3 = sio_unit.u3 & 0xFE; return (sio_unit.buf); } else { sim_putchar(data); } return 0; } /* Port 2 controls the PTR/PTP devices */ int32 sio1s(int32 io, int32 data) { if (io == 0) { if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return 0x02; if (ptr_unit.u3 != 0) /* No more data? */ return 0x02; return (0x03); /* ready to read/write */ } else { if (data == 0x03) { ptr_unit.u3 = 0; ptr_unit.buf = 0; ptr_unit.pos = 0; ptp_unit.u3 = 0; ptp_unit.buf = 0; ptp_unit.pos = 0; } return (0); } } int32 sio1d(int32 io, int32 data) { int32 temp; UNIT *uptr; if (io == 0) { if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return 0; if (ptr_unit.u3 != 0) return 0; uptr = ptr_dev.units; if ((temp = getc(uptr -> fileref)) == EOF) { /* end of file? */ ptr_unit.u3 = 0x01; return 0; } ptr_unit.pos++; return (temp & 0xFF); } else { uptr = ptp_dev.units; putc(data, uptr -> fileref); ptp_unit.pos++; } return 0; } simh-3.8.1/ALTAIR/altair_dsk.c0000644000175000017500000003201010303752072014071 0ustar vlmvlm/* altair_dsk.c: MITS Altair 88-DISK Simulator Copyright (c) 1997-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. The 88_DISK is a 8-inch floppy controller which can control up to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. Each diskette has physically 77 tracks of 32 137-byte sectors each. The controller is interfaced to the CPU by use of 3 I/O addreses, standardly, these are device numbers 10, 11, and 12 (octal). Address Mode Function ------- ---- -------- 10 Out Selects and enables Controller and Drive 10 In Indicates status of Drive and Controller 11 Out Controls Disk Function 11 In Indicates current sector position of disk 12 Out Write data 12 In Read data Drive Select Out (Device 10 OUT): +---+---+---+---+---+---+---+---+ | C | X | X | X | Device | +---+---+---+---+---+---+---+---+ C = If this bit is 1, the disk controller selected by 'device' is cleared. If the bit is zero, 'device' is selected as the device being controlled by subsequent I/O operations. X = not used Device = value zero thru 15, selects drive to be controlled. Drive Status In (Device 10 IN): +---+---+---+---+---+---+---+---+ | R | Z | I | X | X | H | M | W | +---+---+---+---+---+---+---+---+ W - When 0, write circuit ready to write another byte. M - When 0, head movement is allowed H - When 0, indicates head is loaded for read/write X - not used (will be 0) I - When 0, indicates interrupts enabled (not used this simulator) Z - When 0, indicates head is on track 0 R - When 0, indicates that read circuit has new byte to read Drive Control (Device 11 OUT): +---+---+---+---+---+---+---+---+ | W | C | D | E | U | H | O | I | +---+---+---+---+---+---+---+---+ I - When 1, steps head IN one track O - When 1, steps head OUT out track H - When 1, loads head to drive surface U - When 1, unloads head E - Enables interrupts (ignored this simulator) D - Disables interrupts (ignored this simulator) C - When 1 lowers head current (ignored this simulator) W - When 1, starts Write Enable sequence: W bit on device 10 (see above) will go 1 and data will be read from port 12 until 137 bytes have been read by the controller from that port. The W bit will go off then, and the sector data will be written to disk. Before you do this, you must have stepped the track to the desired number, and waited until the right sector number is presented on device 11 IN, then set this bit. Sector Position (Device 11 IN): As the sectors pass by the read head, they are counted and the number of the current one is available in this register. +---+---+---+---+---+---+---+---+ | X | X | Sector Number | T | +---+---+---+---+---+---+---+---+ X = Not used Sector number = binary of the sector number currently under the head, 0-31. T = Sector True, is a 1 when the sector is positioned to read or write. */ #include #include "altair_defs.h" #define UNIT_V_ENABLE (UNIT_V_UF + 0) /* Write Enable */ #define UNIT_ENABLE (1 << UNIT_V_ENABLE) #define DSK_SECTSIZE 137 #define DSK_SECT 32 #define DSK_TRACSIZE 4384 #define DSK_SURF 1 #define DSK_CYL 77 #define DSK_SIZE (DSK_SECT * DSK_SURF * DSK_CYL * DSK_SECTSIZE) t_stat dsk_svc (UNIT *uptr); t_stat dsk_reset (DEVICE *dptr); void writebuf(); extern int32 PCX; /* Global data on status */ int32 cur_disk = 8; /* Currently selected drive */ int32 cur_track[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; int32 cur_sect[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; int32 cur_byte[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; int32 cur_flags[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; char dskbuf[137]; /* Data Buffer */ int32 dirty = 0; /* 1 when buffer has unwritten data in it */ UNIT *dptr; /* fileref to write dirty buffer to */ int32 dsk_rwait = 100; /* rotate latency */ /* 88DSK Standard I/O Data Structures */ UNIT dsk_unit[] = { { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) } }; REG dsk_reg[] = { { ORDATA (DISK, cur_disk, 4) }, { NULL } }; DEVICE dsk_dev = { "DSK", dsk_unit, dsk_reg, NULL, 8, 10, 31, 1, 8, 8, NULL, NULL, &dsk_reset, NULL, NULL, NULL }; /* Service routines to handle simlulator functions */ /* service routine - actually gets char & places in buffer */ t_stat dsk_svc (UNIT *uptr) { return SCPE_OK; } /* Reset routine */ t_stat dsk_reset (DEVICE *dptr) { cur_disk = 0; return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. Each function is passed an 'io' flag, where 0 means a read from the port, and 1 means a write to the port. On input, the actual input is passed as the return value, on output, 'data' is written to the device. */ /* Disk Controller Status/Select */ /* IMPORTANT: The status flags read by port 8 IN instruction are INVERTED, that is, 0 is true and 1 is false. To handle this, the simulator keeps it's own status flags as 0=false, 1=true; and returns the COMPLEMENT of the status flags when read. This makes setting/testing of the flag bits more logical, yet meets the simulation requirement that they are reversed in hardware. */ int32 dsk10(int32 io, int32 data) { if (io == 0) { /* IN: return flags */ return ((~cur_flags[cur_disk]) & 0xFF); /* Return the COMPLEMENT! */ } /* OUT: Controller set/reset/enable/disable */ if (dirty == 1) writebuf(); /*printf("\n[%o] OUT 10: %x", PCX, data);*/ cur_disk = data & 0x0F; if (data & 0x80) { cur_flags[cur_disk] = 0; /* Disable drive */ cur_sect[cur_disk = 0377]; cur_byte[cur_disk = 0377]; return (0); } cur_flags[cur_disk] = 0x1A; /* Enable: head move true */ cur_sect[cur_disk] = 0377; /* reset internal counters */ cur_byte[cur_disk] = 0377; if (cur_track[cur_disk] == 0) cur_flags[cur_disk] |= 0x40; /* track 0 if there */ return (0); } /* Disk Drive Status/Functions */ int32 dsk11(int32 io, int32 data) { int32 stat; if (io == 0) { /* Read sector position */ /*printf("\n[%o] IN 11", PCX);*/ if (dirty == 1) writebuf(); if (cur_flags[cur_disk] & 0x04) { /* head loaded? */ cur_sect[cur_disk]++; if (cur_sect[cur_disk] > 31) cur_sect[cur_disk] = 0; cur_byte[cur_disk] = 0377; stat = cur_sect[cur_disk] << 1; stat &= 0x3E; /* return 'sector true' bit = 0 (true) */ stat |= 0xC0; /* set on 'unused' bits */ return (stat); } else { return (0); /* head not loaded - return 0 */ } } /* Drive functions */ if (cur_disk > 7) return (0); /* no drive selected - can do nothin */ /*printf("\n[%o] OUT 11: %x", PCX, data);*/ if (data & 0x01) { /* Step head in */ cur_track[cur_disk]++; if (cur_track[cur_disk] > 76 ) cur_track[cur_disk] = 76; if (dirty == 1) writebuf(); cur_sect[cur_disk] = 0377; cur_byte[cur_disk] = 0377; } if (data & 0x02) { /* Step head out */ cur_track[cur_disk]--; if (cur_track[cur_disk] < 0) { cur_track[cur_disk] = 0; cur_flags[cur_disk] |= 0x40; /* track 0 if there */ } if (dirty == 1) writebuf(); cur_sect[cur_disk] = 0377; cur_byte[cur_disk] = 0377; } if (dirty == 1) writebuf(); if (data & 0x04) { /* Head load */ cur_flags[cur_disk] |= 0x04; /* turn on head loaded bit */ cur_flags[cur_disk] |= 0x80; /* turn on 'read data available */ } if (data & 0x08) { /* Head Unload */ cur_flags[cur_disk] &= 0xFB; /* off on 'head loaded' */ cur_flags[cur_disk] &= 0x7F; /* off on 'read data avail */ cur_sect[cur_disk] = 0377; cur_byte[cur_disk] = 0377; } /* Interrupts & head current are ignored */ if (data & 0x80) { /* write sequence start */ cur_byte[cur_disk] = 0; cur_flags[cur_disk] |= 0x01; /* enter new write data on */ } return 0; } /* Disk Data In/Out*/ int32 dsk12(int32 io, int32 data) { static int32 rtn, i; static long pos; UNIT *uptr; uptr = dsk_dev.units + cur_disk; if (io == 0) { if ((i = cur_byte[cur_disk]) < 138) { /* just get from buffer */ cur_byte[cur_disk]++; return (dskbuf[i] & 0xFF); } /* physically read the sector */ /*printf("\n[%o] IN 12 (READ) T%d S%d", PCX, cur_track[cur_disk], cur_sect[cur_disk]);*/ pos = DSK_TRACSIZE * cur_track[cur_disk]; pos += DSK_SECTSIZE * cur_sect[cur_disk]; rtn = fseek(uptr -> fileref, pos, 0); rtn = fread(dskbuf, 137, 1, uptr -> fileref); cur_byte[cur_disk] = 1; return (dskbuf[0] & 0xFF); } else { if (cur_byte[cur_disk] > 136) { i = cur_byte[cur_disk]; dskbuf[i] = data & 0xFF; writebuf(); return (0); } i = cur_byte[cur_disk]; dirty = 1; dptr = uptr; dskbuf[i] = data & 0xFF; cur_byte[cur_disk]++; return (0); } } void writebuf() { long pos; int32 rtn, i; i = cur_byte[cur_disk]; /* null-fill rest of sector if any */ while (i < 138) { dskbuf[i] = 0; i++; } /*printf("\n[%o] OUT 12 (WRITE) T%d S%d", PCX, cur_track[cur_disk], cur_sect[cur_disk]); i = getch(); */ pos = DSK_TRACSIZE * cur_track[cur_disk]; /* calc file pos */ pos += DSK_SECTSIZE * cur_sect[cur_disk]; rtn = fseek(dptr -> fileref, pos, 0); rtn = fwrite(dskbuf, 137, 1, dptr -> fileref); cur_flags[cur_disk] &= 0xFE; /* ENWD off */ cur_byte[cur_disk] = 0377; dirty = 0; return; } simh-3.8.1/I1620/0000755000175000017500000000000011112032560011331 5ustar vlmvlmsimh-3.8.1/I1620/i1620_sys.c0000644000175000017500000005414111112032560013141 0ustar vlmvlm/* i1620_sys.c: IBM 1620 simulator interface Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include "i1620_defs.h" #include #define LINE_LNT 50 extern DEVICE cpu_dev, tty_dev; extern DEVICE ptr_dev, ptp_dev; extern DEVICE lpt_dev; extern DEVICE cdr_dev, cdp_dev; extern DEVICE dp_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint8 M[MAXMEMSIZE]; extern char cdr_to_alp[128], alp_to_cdp[256]; /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "IBM 1620"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = LINE_LNT; DEVICE *sim_devices[] = { &cpu_dev, &tty_dev, &ptr_dev, &ptp_dev, &cdr_dev, &cdp_dev, &lpt_dev, &dp_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "HALT instruction", "Breakpoint", "Invalid instruction", "Invalid digit", "Invalid character", "Invalid indicator", "Invalid digit in P address", "Invalid P address", "P address exceeds indirect address limit", "Invalid digit in Q address", "Invalid Q address", "Q address exceeds indirect address limit", "Invalid IO device", "Invalid return register", "Invalid IO function", "Instruction address must be even", "Invalid select code", "Index instruction with no band selected", "P address must be odd", "DCF address must be even", "Invalid disk drive", "Invalid disk sector address", "Invalid disk sector count", "Invalid disk buffer address", "Disk address compare error", "Disk write check error", "Disk cylinder overflow error", "Disk wrong length record error", "Invalid CCT", "Field exceeds memory", "Record exceeds memory", "No card in reader", "Overflow check", "Exponent check", "Write address function disabled", "Floating point mantissa too long", "Floating point mantissa lengths unequal", "Floating point exponent flag missing", "Floating point divide by zero" }; /* Binary loader -- load carriage control tape A carriage control tape consists of entries of the form (repeat count) column number,column number,column number,... The CCT entries are stored in cct[0:lnt-1], cctlnt contains the number of entries */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; t_stat r; extern int32 cct_lnt, cct_ptr, cct[CCT_LNT]; char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; ptr = 0; for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ mask = 0; if (*cptr == '(') { /* repeat count? */ cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ if (r != SCPE_OK) return SCPE_FMT; } else rpt = 1; while (*cptr != 0) { /* get col no's */ cptr = get_glyph (cptr, gbuf, ','); /* get next field */ col = get_uint (gbuf, 10, 12, &r); /* column number */ if (r != SCPE_OK) return SCPE_FMT; mask = mask | (1 << col); /* set bit */ } for ( ; rpt > 0; rpt--) { /* store vals */ if (ptr >= CCT_LNT) return SCPE_FMT; cctbuf[ptr++] = mask; } } if (ptr == 0) return SCPE_FMT; cct_lnt = ptr; cct_ptr = 0; for (rpt = 0; rpt < cct_lnt; rpt++) cct[rpt] = cctbuf[rpt]; return SCPE_OK; } /* Symbol table */ struct opc { char *str; /* mnemonic */ uint32 opv; /* opcode & flags */ uint32 qv; /* q field */ }; #define I_V_FL 16 /* flags */ #define I_M_QX 0x01 /* Q indexable */ #define I_M_QM 0x02 /* Q immediate */ #define I_M_QNP 0x00 /* Q no print */ #define I_M_QCP 0x04 /* Q cond print */ #define I_M_QP 0x08 /* Q print */ #define I_M_PCP 0x00 /* P cond print */ #define I_M_PP 0x10 /* P print */ #define I_GETQF(x) (((x) >> I_V_FL) & 0x03) #define I_GETQP(x) (((x) >> I_V_FL) & 0x0C) #define I_GETPP(x) (((x) >> I_V_FL) & 0x10) #define I_2 ((I_M_PP | I_M_QP | I_M_QX) << I_V_FL) #define I_2M ((I_M_PP | I_M_QP | I_M_QM) << I_V_FL) #define I_2X ((I_M_PP | I_M_QP | I_M_QX | I_M_QM) << I_V_FL) #define I_2S ((I_M_PP | I_M_QP) << I_V_FL) #define I_1 ((I_M_PP | I_M_QCP) << I_V_FL) #define I_1E ((I_M_PP | I_M_QNP) << I_V_FL) #define I_0 ((I_M_PCP | I_M_QCP) << I_V_FL) #define I_0E ((I_M_PCP | I_M_QNP) << I_V_FL) struct opc opcode[] = { { "RNTY", 36+I_1E, 100 }, { "RATY", 37+I_1E, 100 }, { "WNTY", 38+I_1E, 100 }, { "WATY", 39+I_1E, 100 }, { "DNTY", 35+I_1E, 100 }, { "SPTY", 34+I_0E, 101 }, { "RCTY", 34+I_0E, 102 }, { "BKTY", 34+I_0E, 103 }, { "IXTY", 34+I_0E, 104 }, { "TBTY", 34+I_0E, 108 }, { "RNPT", 36+I_1E, 300 }, { "RAPT", 37+I_1E, 300 }, { "WNPT", 38+I_1E, 200 }, { "WAPT", 39+I_1E, 200 }, { "DNPT", 35+I_1E, 200 }, { "RNCD", 36+I_1E, 500 }, { "RACD", 37+I_1E, 500 }, { "WNCD", 38+I_1E, 400 }, { "WACD", 39+I_1E, 400 }, { "DNCD", 35+I_1E, 400 }, { "PRN", 38+I_1E, 900 }, { "PRNS", 38+I_1E, 901 }, { "PRA", 39+I_1E, 900 }, { "PRAS", 39+I_1E, 901 }, { "PRD", 35+I_1E, 900 }, { "PRDS", 35+I_1E, 901 }, { "SK", 34+I_1E, 701 }, { "RDGN", 36+I_1E, 700 }, { "CDGN", 36+I_1E, 701 }, { "RDN", 36+I_1E, 702 }, { "CDN", 36+I_1E, 703 }, { "RTGN", 36+I_1E, 704 }, { "CTGN", 36+I_1E, 705 }, { "RTN", 36+I_1E, 706 }, { "CTN", 36+I_1E, 707 }, { "WDGN", 38+I_1E, 700 }, { "WDN", 38+I_1E, 702 }, { "WTGN", 38+I_1E, 704 }, { "WTN", 38+I_1E, 706 }, { "RBPT", 37+I_1E, 3300 }, { "WBPT", 39+I_1E, 3200 }, { "BC1", 46+I_1E, 100 }, { "BNC1", 47+I_1E, 100 }, { "BC2", 46+I_1E, 200 }, { "BNC2", 47+I_1E, 200 }, { "BC3", 46+I_1E, 300 }, { "BNC3", 47+I_1E, 300 }, { "BC4", 46+I_1E, 400 }, { "BNC4", 47+I_1E, 400 }, { "BLC", 46+I_1E, 900 }, { "BNLC", 47+I_1E, 900 }, { "BH", 46+I_1E, 1100 }, { "BNH", 47+I_1E, 1100 }, { "BP", 46+I_1E, 1100 }, { "BNP", 47+I_1E, 1100 }, { "BE", 46+I_1E, 1200 }, { "BNE", 47+I_1E, 1200 }, { "BZ", 46+I_1E, 1200 }, { "BNZ", 47+I_1E, 1200 }, { "BNL", 46+I_1E, 1300 }, { "BL", 47+I_1E, 1300 }, { "BNN", 46+I_1E, 1300 }, { "BN", 47+I_1E, 1300 }, { "BV", 46+I_1E, 1400 }, { "BNV", 47+I_1E, 1400 }, { "BXV", 46+I_1E, 1500 }, { "BNXV", 47+I_1E, 1500 }, { "BA", 46+I_1E, 1900 }, { "BNA", 47+I_1E, 1900 }, { "BNBS", 46+I_1E, 3000 }, { "BEBS", 47+I_1E, 3000 }, { "BBAS", 46+I_1E, 3100 }, { "BANS", 47+I_1E, 3100 }, { "BBBS", 46+I_1E, 3200 }, { "BBNS", 47+I_1E, 3200 }, { "BCH9", 46+I_1E, 3300 }, { "BCOV", 46+I_1E, 3400 }, { "BSNX", 60+I_1E, 0 }, { "BSBA", 60+I_1E, 1 }, { "BSBB", 60+I_1E, 2 }, { "BSNI", 60+I_1E, 8 }, { "BSIA", 60+I_1E, 9 }, { "FADD", 1+I_2, 0 }, { "FSUB", 2+I_2, 0 }, { "FMUL", 3+I_2, 0 }, { "FSL", 5+I_2, 0 }, { "TFL", 6+I_2, 0 }, { "BTFL", 7+I_2, 0 }, { "FSR", 8+I_2, 0 }, { "FDIV", 9+I_2, 0 }, { "BTAM", 10+I_2M, 0 }, { "AM", 11+I_2M, 0 }, { "SM", 12+I_2M, 0 }, { "MM", 13+I_2M, 0 }, { "CM", 14+I_2M, 0 }, { "TDM", 15+I_2S, 0 }, { "TFM", 16+I_2M, 0 }, { "BTM", 17+I_2M, 0 }, { "LDM", 18+I_2M, 0 }, { "DM", 19+I_2M, 0 }, { "BTA", 20+I_2, 0 }, { "A", 21+I_2, 0 }, { "S", 22+I_2, 0 }, { "M", 23+I_2, 0 }, { "C", 24+I_2, 0 }, { "TD", 25+I_2, 0 }, { "TF", 26+I_2, 0 }, { "BT", 27+I_2, 0 }, { "LD", 28+I_2, 0 }, { "D", 29+I_2, 0 }, { "TRNM", 30+I_2, 0 }, { "TR", 31+I_2, 0 }, { "SF", 32+I_1, 0 }, { "CF", 33+I_1, 0 }, { "K", 34+I_2S, 0 }, { "DN", 35+I_2S, 0 }, { "RN", 36+I_2S, 0 }, { "RA", 37+I_2S, 0 }, { "WN", 38+I_2S, 0 }, { "WA", 39+I_2S, 0 }, { "NOP", 41+I_0, 0 }, { "BB", 42+I_0, 0 }, { "BD", 43+I_2, 0 }, { "BNF", 44+I_2, 0 }, { "BNR", 45+I_2, 0 }, { "BI", 46+I_2S, 0 }, { "BNI", 47+I_2S, 0 }, { "H", 48+I_0, 0 }, { "B", 49+I_1, 0 }, { "BNG", 55+I_2, 0 }, { "BS", 60+I_2S, 0 }, { "BX", 61+I_2, 0 }, { "BXM", 62+I_2X, 0 }, { "BCX", 63+I_2, 0 }, { "BCXM", 64+I_2X, 0 }, { "BLX", 65+I_2, 0 }, { "BLXM", 66+I_2X, 0 }, { "BSX", 67+I_2, 0 }, { "MA", 70+I_2, 0 }, { "MF", 71+I_2, 0 }, { "TNS", 72+I_2, 0 }, { "TNF", 73+I_2, 0 }, { "BBT", 90+I_2, 0 }, { "BMK", 91+I_2, 0 }, { "ORF", 92+I_2, 0 }, { "ANDF", 93+I_2, 0 }, { "CPFL", 94+I_2, 0 }, { "EORF", 95+I_2, 0 }, { "OTD", 96+I_2, 0 }, { "DTO", 97+I_2, 0 }, { NULL, 0, 0 } }; /* Print an address from five characters */ void fprint_addr (FILE *of, int32 spc, t_value *dig, t_bool flg) { int32 i, idx; fputc (spc, of); /* spacer */ if (dig[ADDR_LEN - 1] & FLAG) { /* signed? */ fputc ('-', of); /* print minus */ dig[ADDR_LEN - 1] = dig[ADDR_LEN - 1] & ~FLAG; } for (i = 0; i < ADDR_LEN; i++) /* print digits */ fprintf (of, "%X", dig[i] & DIGIT); if ((cpu_unit.flags & IF_IDX) && flg) { /* indexing? */ for (i = idx = 0; i < ADDR_LEN - 2; i++) { /* get index reg */ if (dig[ADDR_LEN - 2 - i] & FLAG) idx = idx | (1 << i); dig[ADDR_LEN - 2 - i] = dig[ADDR_LEN - 2 - i] & ~FLAG; } if (idx) /* print */ fprintf (of, "(%d)", idx); } return; } /* Symbolic decode Inputs: *of = output stream addr = current address *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra words retired */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 pmp, qmp, i, c, d, any; uint32 op, qv, opfl; if (uptr == NULL) uptr = &cpu_unit; if (sw & SWMASK ('C')) { /* character? */ if (uptr->flags & UNIT_BCD) { if (addr & 1) /* must be even */ return SCPE_ARG; c = ((val[0] & DIGIT) << 4) | (val[1] & DIGIT); if (alp_to_cdp[c] > 0) fprintf (of, "%c", alp_to_cdp[c]); else fprintf (of, "<%02x>", c); return -1; } else fprintf (of, FMTASC (val[0] & 0177)); return SCPE_OK; } if ((uptr->flags & UNIT_BCD) == 0) /* CPU or disk? */ return SCPE_ARG; if (sw & SWMASK ('D')) { /* dump? */ for (i = d = 0; i < LINE_LNT; i++) d = d | val[i]; if (d & FLAG) { /* any flags? */ for (i = 0; i < LINE_LNT; i++) /* print flags */ fprintf (of, (val[i] & FLAG)? "_": " "); fprintf (of, "\n\t"); } for (i = 0; i < LINE_LNT; i++) /* print digits */ fprintf (of, "%X", val[i] & DIGIT) ; return -(i - 1); } if (sw & SWMASK ('S')) { /* string? */ if (addr & 1) /* must be even */ return SCPE_ARG; for (i = 0; i < LINE_LNT; i = i + 2) { c = ((val[i] & DIGIT) << 4) | (val[i + 1] & DIGIT); if (alp_to_cdp[c] < 0) break; fprintf (of, "%c", alp_to_cdp[c]); } if (i == 0) { fprintf (of, "<%02X>", c); return -1; } return -(i - 1); } if ((sw & SWMASK ('M')) == 0) return SCPE_ARG; if (addr & 1) /* must be even */ return SCPE_ARG; op = ((val[0] & DIGIT) * 10) + (val[1] & DIGIT); /* get opcode */ for (i = qv = pmp = qmp = 0; i < ADDR_LEN; i++) { /* test addr */ if (val[I_P + i]) pmp = 1; if (val[I_Q + i]) qmp = 1; qv = (qv * 10) + (val[I_Q + i] & DIGIT); } if ((val[0] | val[1]) & FLAG) /* flags force */ pmp = qmp = 1; for (i = 0; opcode[i].str != NULL; i++) { /* find opcode */ opfl = opcode[i].opv & 0xFF0000; if ((op == (opcode[i].opv & 0xFF)) && ((qv == opcode[i].qv) || ((opfl != I_1E) && (opfl != I_0E)))) break; } if (opcode[i].str == NULL) return SCPE_ARG; if (I_GETQP (opfl) == I_M_QNP) /* Q no print? */ qmp = 0; fprintf (of, opcode[i].str); /* print opcode */ if (I_GETPP (opfl) == I_M_PP) /* P required? */ fprint_addr (of, ' ', &val[I_P], I_M_QX); else if ((I_GETPP (opfl) == I_M_PCP) && (pmp || qmp)) /* P opt & needed? */ fprint_addr (of, ' ', &val[I_P], 0); if (I_GETQP (opfl) == I_M_QP) { /* Q required? */ fprint_addr (of, ',', &val[I_Q], I_GETQF (opfl)); if (I_GETQF (opfl) & I_M_QM) /* immediate? */ val[I_Q] = val[I_Q] & ~FLAG; /* clr hi Q flag */ } else if ((I_GETQP (opfl) == I_M_QCP) && (pmp || qmp)) /* Q opt & needed? */ fprint_addr (of, ',', &val[I_Q], 0); for (i = any = 0; i < INST_LEN; i++) { /* print rem flags */ if (val[i] & FLAG) { if (!any) fputc (',', of); any = 1; fprintf (of, "%d", i); } } return -(INST_LEN - 1); } /* parse_addr - get sign + address + index */ t_stat parse_addr (char *cptr, t_value *val, int32 flg) { int32 i, sign = 0, addr, index; static int32 idx_tst[ADDR_LEN] = { 0, 4, 2, 1, 0 }; char *tptr; if (*cptr == '+') /* +? skip */ cptr++; else if (*cptr == '-') { /* -? skip, flag */ sign = 1; cptr++; } errno = 0; /* get address */ addr = strtoul (cptr, &tptr, 16); if (errno || (cptr == tptr) || (addr > 0xFFFFF)) /* err or too big? */ return SCPE_ARG; if ((cpu_unit.flags & IF_IDX) && (flg & I_M_QX) && /* index allowed? */ (*tptr == '(')) { /* index specified */ errno = 0; index = strtoul (cptr = tptr + 1, &tptr, 10); /* get index */ if (errno || (cptr == tptr) || (index > 7)) /* err or too big? */ return SCPE_ARG; if (*tptr++ != ')') return SCPE_ARG; } else index = 0; if (*tptr != 0) /* all done? */ return SCPE_ARG; for (i = ADDR_LEN - 1; i >= 0; i--) { /* cvt addr to dig */ val[i] = (addr & 0xF) | ((index & idx_tst[i])? FLAG: 0); addr = addr >> 4; } if (sign) /* set sign */ val[ADDR_LEN - 1] = val[ADDR_LEN - 1] | FLAG; if (flg & I_M_QM) /* set immediate */ val[0] = val[0] | FLAG; return SCPE_OK; } /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 i, qv, opfl, last; char t, la, *fptr, gbuf[CBUFSIZE]; while (isspace (*cptr)) /* absorb spaces */ cptr++; if ((sw & SWMASK ('C')) || ((*cptr == '\'') && cptr++)) { /* character? */ if ((t = *cptr & 0x7F) == 0) /* get char */ return SCPE_ARG; if (uptr->flags & UNIT_BCD) { /* BCD? */ if (addr & 1) return SCPE_ARG; t = cdr_to_alp[t]; /* convert */ if (t < 0) /* invalid? */ return SCPE_ARG; val[0] = (t >> 4) & DIGIT; /* store */ val[1] = t & DIGIT; return -1; } else val[0] = t; /* store ASCII */ return SCPE_OK; } if ((uptr->flags & UNIT_BCD) == 0) /* CPU or disk? */ return SCPE_ARG; if ((sw & SWMASK ('S')) || ((*cptr == '"') && cptr++)) { /* string? */ if (addr & 1) /* must be even */ return SCPE_ARG; for (i = 0; (i < sim_emax) && (*cptr != 0); i = i + 2) { t = *cptr++ & 0x7F; /* get character */ t = cdr_to_alp[t]; /* convert */ if (t < 0) /* invalid? */ return SCPE_ARG; val[i] = (t >> 4) & DIGIT; /* store */ val[i + 1] = t & DIGIT; } if (i == 0) /* final check */ return SCPE_ARG; return -(i - 1); } if (addr & 1) /* even addr? */ return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ for (i = 0; opcode[i].str != NULL; i++) { /* look it up */ if (strcmp (gbuf, opcode[i].str) == 0) break; } if (opcode[i].str == NULL) /* successful? */ return SCPE_ARG; opfl = opcode[i].opv & 0xFF0000; /* get flags */ val[0] = (opcode[i].opv & 0xFF) / 10; /* store opcode */ val[1] = (opcode[i].opv & 0xFF) % 10; qv = opcode[i].qv; for (i = ADDR_LEN - 1; i >= 0; i--) { /* set P,Q fields */ val[I_P + i] = 0; val[I_Q + i] = qv % 10; qv = qv /10; } cptr = get_glyph (cptr, gbuf, ','); /* get P field */ if (gbuf[0]) { /* any? */ if (parse_addr (gbuf, &val[I_P], (I_GETPP (opfl)? I_M_QX: 0))) return SCPE_ARG; } else if (I_GETPP (opfl) == I_M_PP) return SCPE_ARG; if (I_GETQP (opfl) != I_M_QNP) { /* Q field allowed? */ cptr = get_glyph (cptr, gbuf, ','); /* get Q field */ if (gbuf[0]) { /* any? */ if (parse_addr (gbuf, &val[I_Q], I_GETQF (opfl))) return SCPE_ARG; } else if (I_GETQP (opfl) == I_M_QP) return SCPE_ARG; } cptr = get_glyph (cptr, fptr = gbuf, ' '); /* get flag field */ last = -1; /* none yet */ while (t = *fptr++) { /* loop through */ if ((t < '0') || (t > '9')) /* must be digit */ return SCPE_ARG; t = t - '0'; /* convert */ if (t == 1) { /* ambiguous? */ la = *fptr++; /* get next */ if (la == '0') /* 10? */ t = 10; else if ((la == '1') && (*fptr == 0)) /* 11 & end field? */ t = 11; else --fptr; /* dont lookahead */ } if (t <= last) /* in order? */ return SCPE_ARG; val[t] = val[t] | FLAG; /* set flag */ last = t; /* continue */ } if (*cptr != 0) return SCPE_ARG; return -(INST_LEN - 1); } simh-3.8.1/I1620/i1620_tty.c0000644000175000017500000003454611107552374013170 0ustar vlmvlm/* i1620_tty.c: IBM 1620 typewriter Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tty console typewriter 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility 22-Dec-02 RMS Added break test */ #include "i1620_defs.h" #define TTO_COLMAX 80 int32 tto_col = 0; extern uint8 M[MAXMEMSIZE]; extern uint8 ind[NUM_IND]; extern UNIT cpu_unit; extern uint32 io_stop; void tti_unlock (void); t_stat tti_rnum (int8 *c); t_stat tti_ralp (int8 *c); t_stat tti_read (int8 *c); t_stat tto_num (uint32 pa, uint32 len); t_stat tto_write (uint32 c); t_stat tty_svc (UNIT *uptr); t_stat tty_reset (DEVICE *dptr); /* TTY data structures tty_dev TTY device descriptor tty_unit TTY unit descriptor tty_reg TTY register list */ UNIT tty_unit = { UDATA (&tty_svc, 0, 0), KBD_POLL_WAIT }; REG tty_reg[] = { { DRDATA (COL, tto_col, 7) }, { DRDATA (TIME, tty_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; DEVICE tty_dev = { "TTY", &tty_unit, tty_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &tty_reset, NULL, NULL, NULL }; /* Data tables */ /* Keyboard to numeric */ const char *tti_to_num = "0123456789|=@:;}"; /* Keyboard to alphameric (digit pair) - translates LC to UC */ const int8 tti_to_alp[128] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x02, -1, 0x33, 0x13, 0x24, 0x10, 0x34, /* !"#$%&' */ 0x24, 0x04, 0x14, 0x10, 0x23, 0x20, 0x03, 0x21, /* ()*+,-./ */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 01234567 */ 0x78, 0x79, -1, -1, -1, 0x33, -1, -1, /* 89:;<=>? */ 0x34, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* HIJKLMNO */ 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* PQRSTUVW */ 0x67, 0x68, 0x69, -1, -1, -1, -1, -1, /* XYZ[\]^_ */ -1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* hijklmno */ 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* pqrstuvw */ 0x67, 0x68, 0x69, -1, -1, 0x0F, -1, -1 /* xyz{|}~ */ }; /* Numeric (digit) to typewriter */ const char num_to_tto[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '|', '=', '@', ':', ';', '}' }; /* Alphameric (digit pair) to typewriter */ const char alp_to_tto[256] = { ' ', -1, '?', '.', ')', -1, -1, -1, /* 00 */ -1, -1, -1, -1, -1, -1, -1, -1, '+', -1, '!', '$', '*', ' ', -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, '0', '=', '@', ':', -1, -1, /* 30 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ 'H', 'I', -1, -1, -1, -1, -1, -1, '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ 'Q', 'R', -1, -1, -1, -1, -1, -1, -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ 'Y', 'Z', -1, -1, -1, -1, -1, -1, '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ '8', '9', -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ -1, -1, -1, -1, -1, -1, -1, -1 }; /* Terminal IO - On input, parity errors cannot occur. - On input, release-start does NOT cause a record mark to be stored. - On output, invalid characters type an invalid character and set WRCHK. If IO stop is set, the system halts at the end of the operation. */ t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1) { t_addr i; uint8 d; int8 ttc; t_stat r, sta; sta = SCPE_OK; switch (op) { /* case on op */ case OP_K: /* control */ switch (f1) { /* case on control */ case 1: /* space */ tto_write (' '); break; case 2: /* return */ tto_write ('\r'); break; case 3: /* backspace */ if ((cpu_unit.flags & IF_MII) == 0) return STOP_INVFNC; tto_write ('\b'); break; case 4: /* index */ if ((cpu_unit.flags & IF_MII) == 0) return STOP_INVFNC; tto_write ('\n'); break; case 8: /* tab */ tto_write ('\t'); break; default: return STOP_INVFNC; } return SCPE_OK; case OP_RN: /* read numeric */ tti_unlock (); /* unlock keyboard */ for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ r = tti_rnum (&ttc); /* read char */ if (r != SCPE_OK) /* error? */ return r; if (ttc == 0x7F) /* end record? */ return SCPE_OK; M[pa] = ttc & (FLAG | DIGIT); /* store char */ PP (pa); /* incr mem addr */ } break; case OP_RA: /* read alphameric */ tti_unlock (); for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ r = tti_ralp (&ttc); /* read char */ if (r != SCPE_OK) /* error? */ return r; if (ttc == 0x7F) /* end record? */ return SCPE_OK; M[pa] = (M[pa] & FLAG) | (ttc & DIGIT); /* store 2 digits */ M[pa - 1] = (M[pa - 1] & FLAG) | ((ttc >> 4) & DIGIT); pa = ADDR_A (pa, 2); /* incr mem addr */ } break; case OP_DN: return tto_num (pa, 20000 - (pa % 20000)); /* dump numeric */ case OP_WN: return tto_num (pa, 0); /* type numeric */ case OP_WA: for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ d = M[pa] & DIGIT; /* get digit */ if ((d & 0xA) == REC_MARK) /* 8-2 char? done */ return sta; d = ((M[pa - 1] & DIGIT) << 4) | d; /* get digit pair */ ttc = alp_to_tto[d]; /* translate */ if (ttc < 0) { /* bad char? */ ind[IN_WRCHK] = 1; /* set write check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; } tto_write (ttc & 0x7F); /* write */ pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return STOP_RWRAP; } /* Read numerically - cannot generate parity errors */ t_stat tti_rnum (int8 *c) { int8 raw, flg = 0; char *cp; t_stat r; *c = -1; /* no char yet */ do { r = tti_read (&raw); /* get char */ if (r != SCPE_OK) /* error? */ return r; if (raw == '\r') /* return? mark */ *c = 0x7F; else if ((raw == '~') || (raw == '`')) /* flag? mark */ flg = FLAG; else if (cp = strchr (tti_to_num, raw)) /* legal? */ *c = ((int8) (cp - tti_to_num)) | flg; /* assemble char */ else raw = 007; /* beep! */ tto_write (raw); /* echo */ } while (*c == -1); return SCPE_OK; } /* Read alphamerically - cannot generate parity errors */ t_stat tti_ralp (int8 *c) { int8 raw; t_stat r; *c = -1; /* no char yet */ do { r = tti_read (&raw); /* get char */ if (r != SCPE_OK) /* error? */ return r; if (raw == '\r') /* return? mark */ *c = 0x7F; else if (tti_to_alp[raw] >= 0) /* legal char? */ *c = tti_to_alp[raw]; /* xlate */ else raw = 007; /* beep! */ tto_write (raw); /* echo */ } while (*c == -1); return SCPE_OK; } /* Read from keyboard */ t_stat tti_read (int8 *c) { int32 t; do { t = sim_poll_kbd (); /* get character */ } while ((t == SCPE_OK) || (t & SCPE_BREAK)); /* ignore break */ if (t < SCPE_KFLAG) /* error? */ return t; *c = t & 0177; /* store character */ return SCPE_OK; } /* Write numerically - cannot generate parity errors */ t_stat tto_num (uint32 pa, uint32 len) { t_stat r; uint8 d; uint32 i, end; end = pa + len; for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ d = M[pa]; /* get char */ if (len? (pa >= end): /* dump: end reached? */ ((d & REC_MARK) == REC_MARK)) /* write: rec mark? */ return SCPE_OK; /* end operation */ if (d & FLAG) /* flag? */ tto_write ('`'); r = tto_write (num_to_tto[d & DIGIT]); /* write */ if (r != SCPE_OK) /* error? */ return r; PP (pa); /* incr mem addr */ } return STOP_RWRAP; } /* Write, maintaining position */ t_stat tto_write (uint32 c) { int32 rpt; if (c == '\t') { /* tab? */ rpt = 8 - (tto_col % 8); /* distance to next */ tto_col = tto_col + rpt; /* tab over */ while (rpt-- > 0) /* use spaces */ sim_putchar (' '); return SCPE_OK; } if (c == '\r') { /* return? */ sim_putchar ('\r'); /* crlf */ sim_putchar ('\n'); tto_col = 0; /* clear colcnt */ return SCPE_OK; } if ((c == '\n') || (c == 007)) { /* non-spacing? */ sim_putchar (c); return SCPE_OK; } if (c == '\b') /* backspace? */ tto_col = tto_col? tto_col - 1: 0; else tto_col++; /* normal */ if (tto_col > TTO_COLMAX) { /* line wrap? */ sim_putchar ('\r'); sim_putchar ('\n'); tto_col = 0; } sim_putchar (c); return SCPE_OK; } /* Unit service - polls for WRU */ t_stat tty_svc (UNIT *uptr) { int32 temp; sim_activate (&tty_unit, tty_unit.wait); /* continue poll */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return temp; return SCPE_OK; } /* Reset routine */ t_stat tty_reset (DEVICE *dptr) { sim_activate (&tty_unit, tty_unit.wait); /* activate poll */ tto_col = 0; return SCPE_OK; } /* TTI unlock - signals that we are ready for keyboard input */ void tti_unlock (void) { tto_write ('>'); return; } simh-3.8.1/I1620/i1620_defs.h0000644000175000017500000003143111111657322013256 0ustar vlmvlm/* i1620_defs.h: IBM 1620 simulator definitions Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This simulator is based on the 1620 simulator written by Geoff Kuenning. I am grateful to Al Kossow, the Computer History Museum, and the IBM Corporate Archives for their help in gathering documentation about the IBM 1620. 18-Oct-02 RMS Fixed bug in ADDR_S macro (found by Hans Pufal) */ #ifndef _I1620_DEFS_H_ #define _I1620_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_HALT 1 /* HALT */ #define STOP_IBKPT 2 /* breakpoint */ #define STOP_INVINS 3 /* invalid instruction */ #define STOP_INVDIG 4 /* invalid digit */ #define STOP_INVCHR 5 /* invalid char */ #define STOP_INVIND 6 /* invalid indicator */ #define STOP_INVPDG 7 /* invalid P addr digit */ #define STOP_INVPAD 8 /* invalid P addr */ #define STOP_INVPIA 9 /* invalid P indir addr */ #define STOP_INVQDG 10 /* invalid Q addr digits */ #define STOP_INVQAD 11 /* invalid Q addr */ #define STOP_INVQIA 12 /* invalid Q indir addr */ #define STOP_INVIO 13 /* invalid IO address */ #define STOP_INVRTN 14 /* invalid return */ #define STOP_INVFNC 15 /* invalid function */ #define STOP_INVIAD 16 /* invalid instr addr */ #define STOP_INVSEL 17 /* invalid select */ #define STOP_INVIDX 18 /* invalid index instr */ #define STOP_INVEAD 19 /* invalid even addr */ #define STOP_INVDCF 20 /* invalid DCF addr */ #define STOP_INVDRV 21 /* invalid disk drive */ #define STOP_INVDSC 22 /* invalid disk sector */ #define STOP_INVDCN 23 /* invalid disk count */ #define STOP_INVDBA 24 /* invalid disk buf addr */ #define STOP_DACERR 25 /* disk addr comp err */ #define STOP_DWCERR 26 /* disk wr check err */ #define STOP_CYOERR 27 /* cylinder ovflo err */ #define STOP_WRLERR 28 /* wrong rec lnt err */ #define STOP_CCT 29 /* runaway CCT */ #define STOP_FWRAP 30 /* field wrap */ #define STOP_RWRAP 31 /* record wrap */ #define STOP_NOCD 32 /* no card in reader */ #define STOP_OVERFL 33 /* overflow */ #define STOP_EXPCHK 34 /* exponent error */ #define STOP_WRADIS 35 /* write addr disabled */ #define STOP_FPLNT 36 /* invalid fp length */ #define STOP_FPUNL 37 /* fp lengths unequal */ #define STOP_FPMF 38 /* no flag on exp */ #define STOP_FPDVZ 39 /* divide by zero */ /* Memory */ #define MAXMEMSIZE 60000 /* max mem size */ #define MEMSIZE (cpu_unit.capac) /* act memory size */ /* Processor parameters */ #define INST_LEN 12 /* inst length */ #define ADDR_LEN 5 /* addr length */ #define MUL_TABLE 100 /* multiply table */ #define MUL_TABLE_LEN 200 #define ADD_TABLE 300 /* add table */ #define ADD_TABLE_LEN 100 #define IDX_A 300 /* index A base */ #define IDX_B 340 /* index B base */ #define PROD_AREA 80 /* product area */ #define PROD_AREA_LEN 20 /* product area */ #define PROD_AREA_END (PROD_AREA + PROD_AREA_LEN) /* Branch indicator codes */ #define NUM_IND 100 /* number of indicators */ #define IN_SW1 1 /* sense switch 1 */ #define IN_SW2 2 /* sense switch 2 */ #define IN_SW3 3 /* sense switch 3 */ #define IN_SW4 4 /* sense switch 4 */ #define IN_RDCHK 6 /* read check (I/O error) */ #define IN_WRCHK 7 /* write check (I/O error) */ #define IN_LAST 9 /* last card was just read */ #define IN_HP 11 /* high or positive result */ #define IN_EZ 12 /* equal or zero result */ #define IN_HPEZ 13 /* high/positive or equal/zero */ #define IN_OVF 14 /* overflow */ #define IN_EXPCHK 15 /* floating exponent check */ #define IN_MBREVEN 16 /* even parity check */ #define IN_MBRODD 17 /* odd parity check */ #define IN_ANYCHK 19 /* any of read, write, even/odd */ #define IN_PRCHK 25 /* printer check */ #define IN_IXN 30 /* IX neither */ #define IN_IXA 31 /* IX A band */ #define IN_IXB 32 /* IX B band */ #define IN_PRCH9 33 /* printer chan 9 */ #define IN_PRCH12 34 /* printer chan 12 */ #define IN_PRBSY 35 /* printer busy */ #define IN_DACH 36 /* disk addr/data check */ #define IN_DWLR 37 /* disk rec length */ #define IN_DCYO 38 /* disk cyl overflow */ #define IN_DERR 39 /* disk any error */ /* I/O channel codes */ #define NUM_IO 100 /* number of IO chan */ #define IO_TTY 1 /* console typewriter */ #define IO_PTP 2 /* paper-tape punch */ #define IO_PTR 3 /* paper-tape reader */ #define IO_CDP 4 /* card punch */ #define IO_CDR 5 /* card reader */ #define IO_DSK 7 /* disk */ #define IO_LPT 9 /* line printer */ #define IO_BTP 32 /* binary ptp */ #define IO_BTR 33 /* binary ptr */ #define LPT_WIDTH 120 /* line print width */ #define CCT_LNT 132 /* car ctrl length */ #define CRETIOE(f,c) return ((f)? (c): SCPE_OK) /* Memory representation: flag + BCD digit per byte */ #define FLAG 0x10 #define DIGIT 0x0F #define REC_MARK 0xA #define NUM_BLANK 0xC #define GRP_MARK 0xF #define BAD_DIGIT(x) ((x) > 9) /* Instruction format */ #define I_OP 0 /* opcode */ #define I_P 2 /* P start */ #define I_PL 6 /* P end */ #define I_Q 7 /* Q start */ #define I_QL 11 /* Q end */ #define I_IO 8 /* IO select */ #define I_BR 8 /* indicator select */ #define I_CTL 10 /* control select */ #define I_SEL 11 /* BS select */ #define ADDR_A(x,a) ((((x) + (a)) >= MEMSIZE)? ((x) + (a) - MEMSIZE): ((x) + (a))) #define ADDR_S(x,a) (((x) < (a))? ((x) - (a) + MEMSIZE): ((x) - (a))) #define PP(x) x = ADDR_A(x,1) #define MM(x) x = ADDR_S(x,1) /* CPU options, stored in cpu_unit.flags */ /* Decoding flags must be part of the same definition set */ #define UNIT_SCP ((1 << UNIT_V_UF) - 1) /* mask of SCP flags */ #define IF_MII (1 << (UNIT_V_UF + 0)) /* model 2 */ #define IF_DIV (1 << (UNIT_V_UF + 1)) /* automatic divide */ #define IF_IA (1 << (UNIT_V_UF + 2)) /* indirect addressing */ #define IF_EDT (1 << (UNIT_V_UF + 3)) /* edit */ #define IF_FP (1 << (UNIT_V_UF + 4)) /* floating point */ #define IF_BIN (1 << (UNIT_V_UF + 5)) /* binary */ #define IF_IDX (1 << (UNIT_V_UF + 6)) /* indexing */ #define IF_VPA (1 << (UNIT_V_UF + 7)) /* valid P addr */ #define IF_VQA (1 << (UNIT_V_UF + 8)) /* valid Q addr */ #define IF_4QA (1 << (UNIT_V_UF + 9)) /* 4 char Q addr */ #define IF_NQX (1 << (UNIT_V_UF + 10)) /* no Q indexing */ #define IF_IMM (1 << (UNIT_V_UF + 11)) /* immediate */ #define UNIT_BCD (1 << (UNIT_V_UF + 12)) /* BCD coded */ #define UNIT_MSIZE (1 << (UNIT_V_UF + 13)) /* fake flag */ #define ALLOPT (IF_DIV + IF_IA + IF_EDT + IF_FP + IF_BIN + IF_IDX) #define MI_OPT (IF_DIV + IF_IA + IF_EDT + IF_FP) #define MI_STD (IF_DIV + IF_IA + IF_EDT) #define MII_OPT (ALLOPT) #define MII_STD (IF_DIV + IF_IA + IF_EDT + IF_BIN + IF_IDX) /* Add status codes */ #define ADD_NOCRY 0 /* no carry out */ #define ADD_CARRY 1 /* carry out */ #define ADD_SIGNC 2 /* sign change */ /* Opcodes */ enum opcodes { OP_FADD = 1, OP_FSUB, OP_FMUL, /* 00 - 09 */ OP_FSL = 5, OP_TFL, OP_BTFL, OP_FSR, OP_FDIV, OP_BTAM = 10, OP_AM, OP_SM, OP_MM, OP_CM, /* 10 - 19 */ OP_TDM, OP_TFM, OP_BTM, OP_LDM, OP_DM, OP_BTA = 20, OP_A, OP_S, OP_M, OP_C, /* 20 - 29 */ OP_TD, OP_TF, OP_BT, OP_LD, OP_D, OP_TRNM = 30, OP_TR, OP_SF, OP_CF, OP_K, /* 30 - 39 */ OP_DN, OP_RN, OP_RA, OP_WN, OP_WA, OP_NOP = 41, OP_BB, OP_BD, OP_BNF, /* 40 - 49 */ OP_BNR, OP_BI, OP_BNI, OP_H, OP_B, OP_BNG = 55, OP_BS = 60, OP_BX, OP_BXM, OP_BCX, OP_BCXM, /* 60 - 69 */ OP_BLX, OP_BLXM, OP_BSX, OP_MA = 70, OP_MF, OP_TNS, OP_TNF, /* 70 - 79 */ /* 80 - 89 */ OP_BBT = 90, OP_BMK, OP_ORF, OP_ANDF, OP_CPLF, /* 90 - 99 */ OP_EORF, OP_OTD, OP_DTO }; #endif simh-3.8.1/I1620/i1620_fp.c0000644000175000017500000004157411112032560012736 0ustar vlmvlm/* i1620_fp.c: IBM 1620 floating point simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. The IBM 1620 uses a variable length floating point format, with a fixed two digit decimal exponent and a variable length decimal mantissa: _ S_S M.......MEE where S represents flag bits if the mantissa or exponent are negative. 31-May-2008 RMS Fixed add_field call (found by Peter Schorn) */ #include "i1620_defs.h" #define FP_LMAX 100 /* max fp mant lnt */ #define FP_EMAX 99 /* max fp exponent */ /* Unpacked floating point operand */ typedef struct { int32 sign; /* 0 => +, 1 => - */ int32 exp; /* binary exponent */ uint32 lnt; /* mantissa length */ uint32 addr; /* mantissa addr */ uint32 zero; /* 0 => nz, 1 => zero */ } FPA; extern uint8 M[MAXMEMSIZE]; /* main memory */ extern uint8 ind[NUM_IND]; /* indicators */ extern UNIT cpu_unit; t_stat fp_scan_mant (uint32 ad, uint32 *lnt, uint32 *zro); t_stat fp_zero (FPA *fp); extern t_stat xmt_field (uint32 d, uint32 s, uint32 skp); extern t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta); extern t_stat mul_field (uint32 d, uint32 s); extern t_stat xmt_divd (uint32 d, uint32 s); extern t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez); /* Unpack and validate a floating point argument */ t_stat fp_unpack (uint32 ad, FPA *fp) { uint8 d0, d1, esign; esign = M[ad] & FLAG; /* get exp sign */ d0 = M[ad] & DIGIT; /* get exp lo digit */ MM (ad); if ((M[ad] & FLAG) == 0) /* no flag on hi exp? */ return STOP_FPMF; d1 = M[ad] & DIGIT; /* get exp hi digit */ MM (ad); fp->addr = ad; /* save mant addr */ if (BAD_DIGIT (d1) || BAD_DIGIT (d0)) /* exp bad dig? */ return STOP_INVDIG; fp->exp = ((d1 * 10) + d0) * (esign? -1: 1); /* convert exponent */ fp->sign = (M[ad] & FLAG)? 1: 0; /* get mantissa sign */ return fp_scan_mant (fp->addr, &(fp->lnt), &(fp->zero)); } /* Unpack and validate source and destination arguments */ t_stat fp_unpack_two (uint32 dad, uint32 sad, FPA *dfp, FPA *sfp) { t_stat r; if ((r = fp_unpack (dad, dfp)) != SCPE_OK) /* unpack dst */ return r; if ((r = fp_unpack (sad, sfp)) != SCPE_OK) /* unpack src */ return r; if (sfp->lnt != dfp->lnt) /* lnts must be equal */ return STOP_FPUNL; return SCPE_OK; } /* Pack floating point result */ t_stat fp_pack (FPA *fp) { int32 e; uint32 i, mad; e = (fp->exp >= 0)? fp->exp: -fp->exp; /* get |exp| */ if (e > FP_EMAX) { /* too big? */ ind[IN_EXPCHK] = 1; /* set indicator */ if (fp->exp < 0) /* underflow? */ return fp_zero (fp); mad = fp->addr; for (i = 0; i < fp->lnt; i++) { /* mant = 99...99 */ M[mad] = (M[mad] & FLAG) | 9; MM (mad); } e = FP_EMAX; /* cap at max */ } M[ADDR_A (fp->addr, 1)] = (e / 10) | FLAG; /* high exp digit */ M[ADDR_A (fp->addr, 2)] = (e % 10) | /* low exp digit */ ((fp->exp < 0)? FLAG: 0); return SCPE_OK; } /* Shift mantissa right n positions */ void fp_rsh (FPA *fp, uint32 n) { uint32 i, sad, dad; if (n == 0) /* zero? done */ return; sad = ADDR_S (fp->addr, n); /* src = addr - n */ dad = fp->addr; /* dst = n */ for (i = 0; i < fp->lnt; i++) { /* move digits */ if (i >= (fp->lnt - n)) M[dad] = M[dad] & FLAG; else M[dad] = (M[dad] & FLAG) | (M[sad] & DIGIT); MM (dad); MM (sad); } return; } /* Shift mantissa left 1 position */ void fp_lsh_1 (FPA *fp) { uint32 i, mad, nxt; mad = ADDR_S (fp->addr, fp->lnt - 1); /* hi order digit */ for (i = 0; i < (fp->lnt - 1); i++) { /* move lnt-1 digits */ nxt = ADDR_A (mad, 1); M[mad] = (M[mad] & FLAG) | (M[nxt] & DIGIT); mad = nxt; } M[mad] = M[mad] & FLAG; /* clear last digit */ return; } /* Clear floating point number */ t_stat fp_zero (FPA *fp) { uint32 i, mad = fp->addr; for (i = 0; i < fp->lnt; i++) { /* clear mantissa */ M[mad] = (i? M[mad] & FLAG: 0); /* clear sign bit */ MM (mad); } M[ADDR_A (fp->addr, 1)] = FLAG + 9; /* exp = -99 */ M[ADDR_A (fp->addr, 2)] = FLAG + 9; /* exp = -99 */ ind[IN_EZ] = 1; /* result = 0 */ ind[IN_HP] = 0; return SCPE_OK; } /* Scan floating point mantissa for length and (optionally) zero */ t_stat fp_scan_mant (uint32 ad, uint32 *lnt, uint32 *zro) { uint8 d, l, z; z = 1; /* assume zero */ for (l = 1; l <= FP_LMAX; l++) { /* scan to get length */ d = M[ad] & DIGIT; /* get mant digit */ if (d) z = 0; /* non-zero? */ if ((l != 1) && (M[ad] & FLAG)) { /* flag past first dig? */ *lnt = l; /* set returns */ if (zro) *zro = z; return SCPE_OK; } MM (ad); } return STOP_FPLNT; /* too long */ } /* Copy floating point mantissa */ void fp_copy_mant (uint32 d, uint32 s, uint32 l) { uint32 i; if (ind[IN_HP]) /* clr/set sign */ M[d] = M[d] & ~FLAG; else M[d] = M[d] | FLAG; for (i = 0; i < l; i++) { /* copy src */ M[d] = (M[d] & FLAG) | (M[s] & DIGIT); /* preserve flags */ MM (d); MM (s); } return; } /* Compare floating point mantissa */ int32 fp_comp_mant (uint32 d, uint32 s, uint32 l) { uint8 i, dd, sd; d = ADDR_S (d, l - 1); /* start of mantissa */ s = ADDR_S (s, l - 1); for (i = 0; i < l; i++) { /* compare dst:src */ dd = M[d] & DIGIT; /* get dst digit */ sd = M[s] & DIGIT; /* get src digit */ if (dd > sd) /* >? done */ return 1; if (dd < sd) /* = ((int32) dfp.lnt))) { /* src = 0, or too small? */ if (dfp.zero) /* res = dst, zero? */ return fp_zero (&dfp); ind[IN_EZ] = 0; /* res nz, set EZ, HP */ ind[IN_HP] = (dfp.sign == 0); return SCPE_OK; } if (dfp.zero || (dif <= -((int32) dfp.lnt))) { /* dst = 0, or too small? */ if (sfp.zero) /* res = src, zero? */ return fp_zero (&dfp); r = xmt_field (d, s, 3); /* copy src to dst */ ind[IN_EZ] = 0; /* res nz, set EZ, HP */ ind[IN_HP] = (dfp.sign == 0); return r; } if (dif > 0) { /* dst exp > src exp? */ sad = sfp.addr; /* save src in save area */ for (i = 0; i < sfp.lnt; i++) { sav_src[i] = M[sad]; MM (sad); } fp_rsh (&sfp, dif); /* denormalize src */ } else if (dif < 0) { /* dst exp < src exp? */ dfp.exp = sfp.exp; /* res exp = src exp */ fp_rsh (&dfp, -dif); /* denormalize dst */ } r = add_field (dfp.addr, sfp.addr, sub, TRUE, 0, &sta); /* add mant, set EZ, HP */ if (dif > 0) { /* src denormalized? */ sad = sfp.addr; /* restore src from */ for (i = 0; i < sfp.lnt; i++) { /* save area */ M[sad] = sav_src[i]; MM (sad); } } if (r != SCPE_OK) /* add error? */ return r; hi = ADDR_S (dfp.addr, dfp.lnt - 1); /* addr of hi digit */ if (sta == ADD_CARRY) { /* carry out? */ fp_rsh (&dfp, 1); /* shift mantissa */ M[hi] = FLAG + 1; /* high order 1 */ dfp.exp = dfp.exp + 1; ind[IN_EZ] = 0; /* not zero */ ind[IN_HP] = (dfp.sign == 0); /* set HP */ } else if (ind[IN_EZ]) /* result zero? */ return fp_zero (&dfp); else { while ((M[hi] & DIGIT) == 0) { /* until normalized */ fp_lsh_1 (&dfp); /* left shift */ dfp.exp = dfp.exp - 1; /* decr exponent */ } } return fp_pack (&dfp); /* pack and exit */ } /* Floating point multiply */ t_stat fp_mul (uint32 d, uint32 s) { FPA sfp, dfp; uint32 pad; t_stat r; r = fp_unpack_two (d, s, &dfp, &sfp); /* unpack operands */ if (r != SCPE_OK) /* error? */ return r; if (sfp.zero || dfp.zero) /* either zero? */ return fp_zero (&dfp); r = mul_field (dfp.addr, sfp.addr); /* mul, set EZ, HP */ if (r != SCPE_OK) return r; if (M[ADDR_S (PROD_AREA_END, 2 * dfp.lnt)] & DIGIT) { /* hi prod dig set? */ pad = ADDR_S (PROD_AREA_END - 1, dfp.lnt); /* no normalization */ dfp.exp = dfp.exp + sfp.exp; /* res exp = sum */ } else { pad = ADDR_S (PROD_AREA_END, dfp.lnt); /* 'normalize' 1 */ dfp.exp = dfp.exp + sfp.exp - 1; /* res exp = sum - 1 */ } fp_copy_mant (dfp.addr, pad, dfp.lnt); /* copy prod to mant */ return fp_pack (&dfp); /* pack and exit */ } /* Floating point divide */ t_stat fp_div (uint32 d, uint32 s) { FPA sfp, dfp; uint32 i, pad, a100ml, a99ml; int32 ez; t_stat r; r = fp_unpack_two (d, s, &dfp, &sfp); /* unpack operands */ if (r != SCPE_OK) /* error? */ return r; if (sfp.zero) { /* divide by zero? */ ind[IN_OVF] = 1; /* dead jim */ return SCPE_OK; } if (dfp.zero) /* divide into zero? */ return fp_zero (&dfp); for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */ M[PROD_AREA + i] = 0; a100ml = ADDR_S (PROD_AREA_END, dfp.lnt); /* 100 - lnt */ a99ml = ADDR_S (PROD_AREA_END - 1, dfp.lnt); /* 99 - lnt */ if (fp_comp_mant (dfp.addr, sfp.addr, dfp.lnt) >= 0) { /* |Mdst| >= |Msrc|? */ pad = a100ml; dfp.exp = dfp.exp - sfp.exp + 1; /* res exp = diff + 1 */ } else { pad = a99ml; dfp.exp = dfp.exp - sfp.exp; /* res exp = diff */ } r = xmt_divd (pad, dfp.addr); /* xmt dividend */ if (r != SCPE_OK) /* error? */ return r; r = div_field (a100ml, sfp.addr, &ez); /* divide fractions */ if (r != SCPE_OK) /* error? */ return r; if (ez) /* result zero? */ return fp_zero (&dfp); ind[IN_HP] = ((dfp.sign ^ sfp.sign) == 0); /* set res sign */ ind[IN_EZ] = 0; /* not zero */ fp_copy_mant (dfp.addr, a99ml, dfp.lnt); /* copy result */ return fp_pack (&dfp); } /* Floating shift right */ t_stat fp_fsr (uint32 d, uint32 s) { uint32 cnt; uint8 t; if (d == s) /* no move? */ return SCPE_OK; cnt = 0; M[d] = (M[d] & FLAG) | (M[s] & DIGIT); /* move 1st wo flag */ do { MM (d); /* decr ptrs */ MM (s); t = M[d] = M[s] & (FLAG | DIGIT); /* copy others */ if (cnt++ > MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((t & FLAG) == 0); /* until src flag */ cnt = 0; do { MM (d); /* decr pointer */ t = M[d]; /* save old val */ M[d] = 0; /* zero field */ if (cnt++ > MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((t & FLAG) == 0); /* until dst flag */ return SCPE_OK; } /* Floating shift left - note that dst is addr of high order digit */ t_stat fp_fsl (uint32 d, uint32 s) { uint32 i, lnt; uint8 sign; t_stat r; if (d == s) return SCPE_OK; sign = M[s] & FLAG; /* get src sign */ r = fp_scan_mant (s, &lnt, NULL); /* get src length */ if (r != SCPE_OK) /* error? */ return r; s = ADDR_S (s, lnt - 1); /* hi order src */ M[d] = M[s] & (FLAG | DIGIT); /* move 1st w flag */ M[s] = M[s] & ~FLAG; /* clr flag from src */ for (i = 1; i < lnt; i++) { /* move src to dst */ PP (d); /* incr ptrs */ PP (s); M[d] = M[s] & DIGIT; /* move just digit */ } PP (d); /* incr pointer */ while ((M[d] & FLAG) == 0) { /* until flag */ M[d] = 0; /* clear field */ PP (d); } if (sign) /* -? zero under sign */ M[d] = FLAG; return SCPE_OK; } simh-3.8.1/I1620/i1620_dp.c0000644000175000017500000005243511107555446012753 0ustar vlmvlm/* i1620_dp.c: IBM 1311 disk simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dp 1311 disk pack The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track. Each sector contains 105 characters of information: 5c sector address 100c sector data By default, a sector's address field will be '00000', which is interpreted to mean the implied sector number that would be in place if the disk pack had been formatted with sequential sector numbers. 18-Oct-02 RMS Fixed bug in error testing (found by Hans Pufal) */ #include "i1620_defs.h" #define DP_NUMDR 4 /* #drives */ #define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */ #define UNIT_WAE (1 << UNIT_V_WAE) /* Disk format */ #define DP_ADDR 5 /* address */ #define DP_DATA 100 /* data */ #define DP_NUMCH (DP_ADDR + DP_DATA) #define DP_NUMSC 20 /* #sectors */ #define DP_NUMSF 10 /* #surfaces */ #define DP_NUMCY 100 /* #cylinders */ #define DP_TOTSC (DP_NUMCY * DP_NUMSF * DP_NUMSC) #define DP_SIZE (DP_TOTSC * DP_NUMCH) /* Disk control field */ #define DCF_DRV 0 /* drive select */ #define DCF_SEC 1 /* sector addr */ #define DCF_SEC_LEN 5 #define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */ #define DCF_CNT_LEN 3 #define DCF_ADR (DCF_CNT + DCF_CNT_LEN) /* buffer address */ #define DCF_ADR_LEN 5 #define DCF_LEN (DCF_ADR + DCF_ADR_LEN) /* Functions */ #define FNC_SEEK 1 /* seek */ #define FNC_SEC 0 /* sectors */ #define FNC_WCH 1 /* write check */ #define FNC_NRL 2 /* no rec lnt chk */ #define FNC_TRK 4 /* tracks */ #define FNC_WRI 8 /* write offset */ #define CYL u3 /* current cylinder */ extern uint8 M[MAXMEMSIZE]; /* memory */ extern uint8 ind[NUM_IND]; extern UNIT cpu_unit; int32 dp_stop = 1; /* disk err stop */ uint32 dp_ba = 0; /* buffer addr */ t_stat dp_reset (DEVICE *dptr); t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc); t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc); t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr); t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr); int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd); t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd); t_bool dp_zeroad (uint8 *ap); int32 dp_cvt_ad (uint8 *ap); int32 dp_trkop (int32 drv, int32 sec); int32 dp_cvt_bcd (uint32 ad, int32 len); void dp_fill (UNIT *uptr, uint32 da, int32 cnt); t_stat dp_tstgm (uint32 c, int32 qnr); /* DP data structures dp_dev DP device descriptor dp_unit DP unit list dp_reg DP register list dp_mod DP modifier list */ UNIT dp_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } }; REG dp_reg[] = { { FLDATA (ADCHK, ind[IN_DACH], 0) }, { FLDATA (WLRC, ind[IN_DWLR], 0) }, { FLDATA (CYLO, ind[IN_DCYO], 0) }, { FLDATA (ERR, ind[IN_DERR], 0) }, { FLDATA (DPSTOP, dp_stop, 0) }, { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0, DP_NUMDR, PV_LEFT + REG_RO) }, { NULL } }; MTAB dp_mod[] = { { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL }, { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, { 0 } }; DEVICE dp_dev = { "DP", dp_unit, dp_reg, dp_mod, DP_NUMDR, 10, 21, 1, 16, 5, NULL, NULL, &dp_reset, NULL, NULL, NULL }; /* Disk IO routine */ t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1) { int32 drv, sa, sec, psec, cnt, qwc, qnr, t; UNIT *uptr; t_stat r; if (pa & 1) /* dcf must be even */ return STOP_INVDCF; ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */ ind[IN_DERR] = ind[IN_DCYO] = 0; sa = ADDR_A (pa, DCF_SEC); /* ptr to sector */ if (((dp_unit[0].flags & UNIT_DIS) == 0) && /* only drive 0? */ (dp_unit[1].flags & UNIT_DIS) && (dp_unit[2].flags & UNIT_DIS) && (dp_unit[3].flags & UNIT_DIS)) drv = 0; /* ignore drv select */ else drv = (((M[pa] & 1)? M[pa]: M[sa]) & 0xE) >> 1; /* drive # */ if (drv >= DP_NUMDR) /* invalid? */ return STOP_INVDRV; uptr = dp_dev.units + drv; /* get unit ptr */ if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_DERR] = 1; /* no, error */ CRETIOE (dp_stop, SCPE_UNATT); } sec = dp_cvt_bcd (sa, DCF_SEC_LEN); /* cvt sector */ if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */ return STOP_INVDSC; if (op == OP_K) { /* seek? */ if (f1 != FNC_SEEK) /* really? */ return STOP_INVFNC; uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */ DP_NUMCY; return SCPE_OK; /* done! */ } cnt = dp_cvt_bcd (ADDR_A (pa, DCF_CNT), DCF_CNT_LEN); /* get count */ t = dp_cvt_bcd (ADDR_A (pa, DCF_ADR), DCF_ADR_LEN); /* get address */ if ((t < 0) || (t & 1)) /* bad address? */ return STOP_INVDBA; dp_ba = t; /* save addr */ if (f1 >= FNC_WRI) /* invalid func? */ return STOP_INVFNC; if (op == OP_RN) /* read? set wch */ qwc = f1 & FNC_WCH; else if (op == OP_WN) { /* write? */ if (op & FNC_WCH) /* cant check */ return STOP_INVFNC; f1 = f1 + FNC_WRI; /* offset fnc */ } else return STOP_INVFNC; /* not R or W */ qnr = f1 & FNC_NRL; /* no rec check? */ switch (f1 & ~(FNC_WCH | FNC_NRL)) { /* case on function */ case FNC_SEC: /* read sectors */ if (cnt <= 0) /* bad count? */ return STOP_INVDCN; psec = dp_fndsec (uptr, sec, TRUE); /* find sector */ if (psec < 0) /* error? */ CRETIOE (dp_stop, STOP_DACERR); do { /* loop on count */ if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read sector */ break; sec++; psec++; /* next sector */ } while ((--cnt > 0) && ((r = dp_nexsec (uptr, sec, psec, TRUE)) == SCPE_OK)); break; /* done, clean up */ case FNC_TRK: /* read track */ psec = dp_trkop (drv, sec); /* start of track */ for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */ if (r = dp_rdadr (uptr, psec, qnr, qwc)) /* read addr */ break; /* error? */ if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read data */ break; /* error? */ psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); } break; /* done, clean up */ case FNC_SEC + FNC_WRI: /* write */ if (cnt <= 0) /* bad count? */ return STOP_INVDCN; psec = dp_fndsec (uptr, sec, FALSE); /* find sector */ if (psec < 0) /* error? */ CRETIOE (dp_stop, STOP_DACERR); do { /* loop on count */ if (r = dp_tstgm (M[dp_ba], qnr)) /* start with gm? */ break; if (r = dp_wrsec (uptr, psec, qnr)) /* write data */ break; sec++; psec++; /* next sector */ } while ((--cnt > 0) && ((r = dp_nexsec (uptr, sec, psec, FALSE)) == SCPE_OK)); break; /* done, clean up */ case FNC_TRK + FNC_WRI: /* write track */ if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */ return STOP_WRADIS; psec = dp_trkop (drv, sec); /* start of track */ for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */ if (r = dp_tstgm (M[dp_ba], qnr)) /* start with gm? */ break; if (r = dp_wradr (uptr, psec, qnr)) /* write addr */ break; if (r = dp_wrsec (uptr, psec, qnr)) /* write data */ break; psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); } break; /* done, clean up */ default: /* unknown */ return STOP_INVFNC; } if ((r == SCPE_OK) && !qnr) { /* eor check? */ if ((M[dp_ba] & DIGIT) != GRP_MARK) { /* GM at end? */ ind[IN_DWLR] = ind[IN_DERR] = 1; /* no, error */ r = STOP_WRLERR; } } if ((r != SCPE_OK) && /* error? */ (dp_stop || !ind[IN_DERR])) /* iochk or stop? */ return r; return SCPE_OK; /* continue */ } /* Read or compare address with memory */ t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc) { int32 i; uint8 ad; int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ t_bool zad = dp_zeroad (ap); /* zero address */ static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */ 10000, 1000, 100, 10, 1 } ; for (i = 0; i < DP_ADDR; i++) { /* copy/check addr */ if (zad) { /* addr zero? */ ad = sec / dec_tab[i]; /* get addr digit */ sec = sec % dec_tab[i]; /* get remainder */ } else ad = *ap; /* addr digit */ if (qwc) { /* write check? */ if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */ return STOP_WRLERR; /* yes, error */ if (!zad && (M[dp_ba] != ad)) { /* digits equal? */ ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ return STOP_DWCERR; } } else M[dp_ba] = ad & (FLAG | DIGIT); /* store digit */ if (dp_tstgm (*ap, qnr)) /* grp mrk on disk? */ return STOP_WRLERR; ap++; PP (dp_ba); /* adv ptrs */ } return SCPE_OK; } /* Read or compare data with memory */ t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc) { int32 i; int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */ for (i = 0; i < DP_DATA; i++) { /* copy data */ if (qwc) { /* write check? */ if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */ return STOP_WRLERR; /* yes, error */ if (M[dp_ba] != *ap) { /* dig+flags equal? */ ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ return STOP_DWCERR; } } else M[dp_ba] = *ap & (FLAG | DIGIT); /* flag + digit */ if (dp_tstgm (*ap, qnr)) /* grp mrk on disk? */ return STOP_WRLERR; ap++; PP (dp_ba); /* adv ptrs */ } return SCPE_OK; } /* Write address to disk */ t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr) { int32 i; uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ for (i = 0; i < DP_ADDR; i++) { /* copy address */ *ap = M[dp_ba] & (FLAG | DIGIT); /* flag + digit */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */ dp_fill (uptr, da + 1, DP_NUMCH - i - 1); /* fill addr+data */ return STOP_WRLERR; /* error */ } da++; ap++; PP (dp_ba); /* adv ptrs */ } return SCPE_OK; } /* Write data to disk */ t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr) { int32 i; uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ for (i = 0; i < DP_DATA; i++) { /* copy data */ *ap = M[dp_ba] & (FLAG | DIGIT); /* get character */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */ dp_fill (uptr, da + 1, DP_DATA - i - 1); /* fill data */ return STOP_WRLERR; /* error */ } da++; ap++; PP (dp_ba); /* adv ptrs */ } return SCPE_OK; } /* Find sector */ int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd) { int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk; int32 da = psec * DP_NUMCH; /* char number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ int32 dskad, i; if (dp_zeroad (ap)) /* addr zero? ok */ return psec; dskad = dp_cvt_ad (ap); /* cvt addr */ if (dskad == sec) { /* match? */ if (rd || ((*ap & FLAG) == 0)) /* read or !wprot? */ return psec; ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ return -1; } psec = psec - (psec % DP_NUMSC); /* sector 0 */ for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */ da = psec * DP_NUMCH; /* char number */ ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */ if (dp_zeroad (ap)) /* no implicit match */ continue; dskad = dp_cvt_ad (ap); /* cvt addr */ if (dskad == sec) { /* match? */ if (rd || ((*ap & FLAG) == 0)) /* read or !wprot? */ return psec; ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ return -1; } } ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ return -1; } /* Find next sector - must be sequential, cannot cross cylinder boundary */ t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd) { int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ int32 da = psec * DP_NUMCH; /* word number */ uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ int32 dskad; if (ctrk) { /* not trk zero? */ if (dp_zeroad (ap)) /* addr zero? ok */ return SCPE_OK; dskad = dp_cvt_ad (ap); /* cvt addr */ if ((dskad == sec) && /* match? */ (rd || ((*ap & FLAG) == 0))) /* read or !wprot? */ return SCPE_OK; ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ return STOP_DACERR; } ind[IN_DCYO] = ind[IN_DERR] = 1; /* cyl overflow */ return STOP_CYOERR; } /* Test for zero address */ t_bool dp_zeroad (uint8 *ap) { int32 i; for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ if (*ap & DIGIT) /* nonzero? lose */ return FALSE; } return TRUE; /* all zeroes */ } /* Test for group mark when enabled */ t_stat dp_tstgm (uint32 c, int32 qnr) { if (!qnr && ((c & DIGIT) == GRP_MARK)) { /* premature GM? */ ind[IN_DWLR] = ind[IN_DERR] = 1; /* error */ return STOP_WRLERR; } return SCPE_OK; } /* Convert disk address to binary - invalid char force bad address */ int32 dp_cvt_ad (uint8 *ap) { int32 i, r; uint8 c; for (i = r = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ c = *ap & DIGIT; /* get digit */ if (BAD_DIGIT (c)) /* bad digit? */ return -1; r = (r * 10) + c; /* bcd to binary */ } return r; } /* Track operation setup */ int32 dp_trkop (int32 drv, int32 sec) { int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF; return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) + (ctrk * DP_NUMSC)); } /* Convert DCF BCD field to binary */ int32 dp_cvt_bcd (uint32 ad, int32 len) { uint8 c; int32 r; for (r = 0; len > 0; len--) { /* loop thru char */ c = M[ad] & DIGIT; /* get digit */ if (BAD_DIGIT (c)) /* invalid? */ return -1; r = (r * 10) + c; /* cvt to bin */ PP (ad); /* next digit */ } return r; } /* Fill sector buffer with zero */ void dp_fill (UNIT *uptr, uint32 da, int32 cnt) { while (cnt-- > 0) { /* fill with zeroes*/ *(((uint8 *) uptr->filebuf) + da) = 0; if (da >= uptr->hwmark) uptr->hwmark = da + 1; da++; } return; } /* Reset routine */ t_stat dp_reset (DEVICE *dptr) { int32 i; for (i = 0; i < DP_NUMDR; i++) /* reset cylinder */ dp_unit[i].CYL = 0; ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */ ind[IN_DERR] = ind[IN_DCYO] = 0; return SCPE_OK; } simh-3.8.1/I1620/i1620_pt.c0000644000175000017500000004564211112032560012754 0ustar vlmvlm/* i1620_pt.c: IBM 1621/1624 paper tape reader/punch simulator Copyright (c) 2002-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr 1621 paper tape reader ptp 1624 paper tape punch 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility 25-Apr-03 RMS Revised for extended file support */ #include "i1620_defs.h" #define PT_EL 0x80 /* end record */ #define PT_X 0x40 /* X */ #define PT_O 0x20 /* O */ #define PT_C 0x10 /* C */ #define PT_FD 0x7F /* deleted */ extern uint8 M[MAXMEMSIZE]; extern uint8 ind[NUM_IND]; extern UNIT cpu_unit; extern uint32 io_stop; t_stat ptr_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); t_stat ptr_read (uint8 *c, t_bool ignfeed); t_stat ptp_reset (DEVICE *dptr); t_stat ptp_write (uint32 c); t_stat ptp_num (uint32 pa, uint32 len); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_reg PTR register list */ UNIT ptr_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) }; REG ptr_reg[] = { { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, NULL, NULL }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_reg PTP register list */ UNIT ptp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; REG ptp_reg[] = { { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL }; /* Data tables */ /* Paper tape reader odd parity chart: 1 = bad, 0 = ok */ const int8 bad_par[128] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 00 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 10 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 20 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 30 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 40 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 50 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 60 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 /* 70 */ }; /* Paper tape read (7b) to numeric (one digit) */ const int8 ptr_to_num[128] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* - */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* C */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* O */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* OC */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* X */ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x10, 0x1E, 0x1F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* XC */ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x10, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* XO */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* XOC */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F }; /* Paper tape read (7b) to alphameric (two digits) Codes XO82, 82, XO842, 842 do not have consistent translations */ const int8 ptr_to_alp[128] = { 0x00, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* - */ 0x78, 0x79, -1, 0x33, 0x34, 0x70, -1, 0x0F, 0x00, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* C */ 0x78, 0x79, -1, 0x33, 0x34, 0x70, -1, 0x0F, 0x70, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* O */ 0x68, 0x69, 0x0A, 0x23, 0x24, 0x60, 0x0E, 0x0F, 0x70, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* OC */ 0x68, 0x69, 0x0A, 0x23, 0x24, 0x60, 0x0E, 0x0F, 0x20, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* X */ 0x58, 0x59, 0x5A, 0x13, 0x14, 0x50, 0x5E, 0x5F, 0x20, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* XC */ 0x58, 0x59, 0x5A, 0x13, 0x14, 0x50, 0x5E, 0x5F, 0x10, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* XO */ 0x48, 0x49, -1, 0x03, 0x04, 0x40, -1, 0x7F, 0x10, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* XOC */ 0x48, 0x49, -1, 0x03, 0x04, 0x40, -1, 0x7F }; /* Numeric (flag + digit) to paper tape punch */ const int8 num_to_ptp[32] = { 0x20, 0x01, 0x02, 0x13, 0x04, 0x15, 0x16, 0x07, /* 0 */ 0x08, 0x19, 0x2A, 0x3B, 0x1C, 0x0D, 0x3E, 0x3F, 0x40, 0x51, 0x52, 0x43, 0x54, 0x45, 0x46, 0x57, /* F + 0 */ 0x58, 0x49, 0x4A, 0x5B, 0x4C, 0x5D, 0x5E, 0x4F }; /* Alphameric (two digits) to paper tape punch */ const int8 alp_to_ptp[256] = { 0x10, -1, 0x7A, 0x6B, 0x7C, -1, -1, 0x7F, /* 00 */ -1, -1, 0x2A, -1, -1, -1, -1, 0x1F, 0x70, -1, 0x4A, 0x5B, 0x4C, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, 0x40, 0x31, 0x2A, 0x3B, 0x2C, -1, -1, -1, /* 20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x1A, 0x0B, 0x1C, 0x0D, 0x0E, -1, /* 30 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x61, 0x62, 0x73, 0x64, 0x75, 0x76, 0x67, /* 40 */ 0x68, 0x79, -1, -1, -1, -1, -1, -1, 0x40, 0x51, 0x52, 0x43, 0x54, 0x45, 0x46, 0x57, /* 50 */ 0x58, 0x49, 0x4A, -1, -1, -1, -1, 0x4F, -1, 0x31, 0x32, 0x23, 0x34, 0x25, 0x26, 0x37, /* 60 */ 0x38, 0x29, -1, -1, -1, -1, -1, -1, 0x20, 0x01, 0x02, 0x13, 0x04, 0x15, 0x16, 0x07, /* 70 */ 0x08, 0x19, 0x7A, -1, -1, -1, -1, 0x7F, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ -1, -1, -1, -1, -1, -1, -1, -1 }; /* Paper tape reader IO routine - Hard errors halt the operation and the system. - Parity errors place an invalid character in memory and set RDCHK, but the read continues until end of record. If IO stop is set, the system then halts. */ t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1) { uint32 i; int8 mc; uint8 ptc; t_stat r, sta; sta = SCPE_OK; switch (op) { /* case on op */ case OP_RN: /* read numeric */ for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ r = ptr_read (&ptc, TRUE); /* read frame */ if (r != SCPE_OK) /* error? */ return r; if (ptc & PT_EL) { /* end record? */ M[pa] = REC_MARK; /* store rec mark */ return sta; /* done */ } if (bad_par[ptc]) { /* bad parity? */ ind[IN_RDCHK] = 1; /* set read check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; M[pa] = 0; /* store zero */ } else M[pa] = ptr_to_num[ptc]; /* translate, store */ PP (pa); /* incr mem addr */ } break; case OP_RA: /* read alphameric */ for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ r = ptr_read (&ptc, TRUE); /* read frame */ if (r != SCPE_OK) /* error? */ return r; if (ptc & PT_EL) { /* end record? */ M[pa] = REC_MARK; /* store rec mark */ M[pa - 1] = 0; return sta; /* done */ } mc = ptr_to_alp[ptc]; /* translate */ if (bad_par[ptc] || (mc < 0)) { /* bad par or char? */ ind[IN_RDCHK] = 1; /* set read check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; mc = 0; /* store blank */ } M[pa] = (M[pa] & FLAG) | (mc & DIGIT); /* store 2 digits */ M[pa - 1] = (M[pa - 1] & FLAG) | ((mc >> 4) & DIGIT); pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return STOP_RWRAP; } /* Binary paper tape reader IO routine - see above for error handling */ t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1) { uint32 i; uint8 ptc; t_stat r, sta; if ((cpu_unit.flags & IF_BIN) == 0) return STOP_INVIO; sta = SCPE_OK; switch (op) { /* case on op */ case OP_RA: /* read alphameric */ for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ r = ptr_read (&ptc, FALSE); /* read frame */ if (r != SCPE_OK) /* error? */ return r; if (ptc & PT_EL) { /* end record? */ M[pa] = REC_MARK; /* store rec mark */ M[pa - 1] = 0; return sta; /* done */ } if (bad_par[ptc]) { /* bad parity? */ ind[IN_RDCHK] = 1; /* set read check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; } M[pa] = (M[pa] & FLAG) | (ptc & 07); /* store 2 digits */ M[pa - 1] = (M[pa - 1] & FLAG) | (((ptc >> 5) & 06) | ((ptc >> 3) & 1)); pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return STOP_RWRAP; } /* Read ptr frame - all errors are 'hard' errors and halt the system */ t_stat ptr_read (uint8 *c, t_bool ignfeed) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_RDCHK] = 1; /* no, error */ return SCPE_UNATT; } do { if ((temp = getc (ptr_unit.fileref)) == EOF) { /* read char */ ind[IN_RDCHK] = 1; /* err, rd chk */ if (feof (ptr_unit.fileref)) printf ("PTR end of file\n"); else perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } *c = temp & 0377; /* save char */ ptr_unit.pos = ptr_unit.pos + 1; /* incr file addr */ } while (ignfeed && (*c == PT_FD)); /* until not feed */ return SCPE_OK; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { return SCPE_OK; } /* Bootstrap routine */ const static uint8 boot_rom[] = { 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* NOP */ 3, 6, 0, 0, 0, 3, 1, 0, 0, 3, 0, 0, /* RNPT 31 */ 2, 5, 0, 0, 0, 7, 1, 0, 0, 0, 0, 0, /* TD 71,loc */ 3, 6, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, /* RNPT loc1 */ 2, 6, 0, 0, 0, 6, 6, 0, 0, 0, 3, 5, /* TF 66,35 */ 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* TDM loc2,loc3 */ 4, 9, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0 /* BR 12 */ }; #define BOOT_START 0 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } /* Paper tape punch IO routine - Hard errors halt the operation and the system. - Parity errors stop the operation and set WRCHK. If IO stop is set, the system then halts. */ t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1) { uint32 i; int8 ptc; uint8 z, d; t_stat r; switch (op) { /* decode op */ case OP_DN: return ptp_num (pa, 20000 - (pa % 20000)); /* dump numeric */ case OP_WN: return ptp_num (pa, 0); /* punch numeric */ case OP_WA: for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ d = M[pa] & DIGIT; /* get digit */ z = M[pa - 1] & DIGIT; /* get zone */ if ((d & REC_MARK) == REC_MARK) /* 8-2 char? */ return ptp_write (PT_EL); /* end record */ ptc = alp_to_ptp[(z << 4) | d]; /* translate pair */ if (ptc < 0) { /* bad char? */ ind[IN_WRCHK] = 1; /* write check */ CRETIOE (io_stop, STOP_INVCHR); } r = ptp_write (ptc); /* write char */ if (r != SCPE_OK) /* error? */ return r; pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return STOP_RWRAP; } /* Binary paper tape punch IO routine - see above for error handling */ t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1) { uint32 i; uint8 ptc, z, d; t_stat r; if ((cpu_unit.flags & IF_BIN) == 0) return STOP_INVIO; switch (op) { /* decode op */ case OP_WA: for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ d = M[pa] & DIGIT; /* get digit */ z = M[pa - 1] & DIGIT; /* get zone */ if ((d & REC_MARK) == REC_MARK) /* 8-2 char? */ return ptp_write (PT_EL); /* end record */ ptc = ((z & 06) << 5) | ((z & 01) << 3) | (d & 07); if (bad_par[ptc]) /* set parity */ ptc = ptc | PT_C; r = ptp_write (ptc); /* write char */ if (r != SCPE_OK) /* error? */ return r; pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return STOP_RWRAP; } /* Punch tape numeric - cannot generate parity errors */ t_stat ptp_num (uint32 pa, uint32 len) { t_stat r; uint8 d; uint32 i, end; end = pa + len; for (i = 0; i < MEMSIZE; i++) { /* stop runaway */ d = M[pa] & (FLAG | DIGIT); /* get char */ if (len? (pa >= end): /* dump: end reached? */ ((d & REC_MARK) == REC_MARK)) /* write: rec mark? */ return ptp_write (PT_EL); /* end record */ r = ptp_write (num_to_ptp[d]); /* write */ if (r != SCPE_OK) /* error? */ return r; PP (pa); /* incr mem addr */ } return STOP_RWRAP; } /* Write ptp frame - all errors are hard errors */ t_stat ptp_write (uint32 c) { if ((ptp_unit.flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_WRCHK] = 1; /* no, error */ return SCPE_UNATT; } if (putc (c, ptp_unit.fileref) == EOF) { /* write char */ ind[IN_WRCHK] = 1; /* error? */ perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ptp_unit.pos + 1; /* count char */ return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { return SCPE_OK; } simh-3.8.1/I1620/i1620_cpu.c0000644000175000017500000024652611112032560013124 0ustar vlmvlm/* i1620_cpu.c: IBM 1620 CPU simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This CPU module incorporates code and comments from the 1620 simulator by Geoff Kuenning, with his permission. 28-May-06 RMS Fixed bug in cpu history (found by Peter Schorn) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Nov-04 RMS Added instruction history 26-Mar-04 RMS Fixed warnings with -std=c99 02-Nov-03 RMS Fixed bug in branch digit (found by Dave Babcock) 21-Aug-03 RMS Fixed bug in immediate index add (found by Michael Short) 25-Apr-03 RMS Changed t_addr to uint32 throughout 18-Oct-02 RMS Fixed bugs in invalid result testing (found by Hans Pufal) The simulated register state for the IBM 1620 is: 1620 sim comment IR1 [PC] program counter IR2 instruction register 2 (subroutine return address) OR1 [QAR] Q address OR2 [PAR] P address PR1 manual save address ind[0:99] indicators Additional internal registers OR3, PR2, and PR3 are not simulated. The IBM 1620 is a fixed instruction length, variable data length, decimal data system. Memory consists of 20000 - 60000 BCD digits, each containing four bits of data and a flag. There are no general registers; all instructions are memory to memory. The 1620 uses a fixed, 12 digit instruction format: oo ppppp qqqqq where oo = opcode ppppp = P (usually destination) address qqqqq = Q (usually source) address Immediate instructions use the qqqqq field as the second operand. The 1620 Model 1 uses table lookups for add and multiply; for that reason, it was nicknamed CADET (Can't Add, Doesn't Even Try). The Model 2 does adds in hardware and uses the add table memory for index registers. This routine is the instruction decode routine for the IBM 1620. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered illegal addresses or instruction formats I/O error in I/O simulator 2. Interrupts. The 1620 has no interrupt structure. 3. Non-existent memory. On the 1620, all memory references are modulo the memory size. 4. Adding I/O devices. These modules must be modified: i1620_cpu.c add iodisp table entry i1620_sys.c add sim_devices table entry */ #include "i1620_defs.h" #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] = saved_PC #define HIST_PC 0x40000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { uint16 vld; uint16 pc; uint8 inst[INST_LEN]; } InstHistory; uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */ uint32 saved_PC = 0; /* saved PC */ uint32 IR2 = 1; /* inst reg 2 */ uint32 PAR = 0; /* P address */ uint32 QAR = 0; /* Q address */ uint32 PR1 = 1; /* proc reg 1 */ uint32 iae = 1; /* ind addr enb */ uint32 idxe = 0; /* index enable */ uint32 idxb = 0; /* index band */ uint32 io_stop = 1; /* I/O stop */ uint32 ar_stop = 1; /* arith stop */ int32 ind_max = 16; /* iadr nest limit */ 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; /* instruction history */ uint8 ind[NUM_IND] = { 0 }; /* indicators */ extern int32 sim_int_char; extern int32 sim_interval; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern FILE *sim_log; 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_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); int32 get_2d (uint32 ad); t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *addr); t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val); t_stat get_idx (uint32 aidx); t_stat xmt_field (uint32 d, uint32 s, uint32 skp); t_stat xmt_record (uint32 d, uint32 s, t_bool cpy); t_stat xmt_index (uint32 d, uint32 s); t_stat xmt_divd (uint32 d, uint32 s); t_stat xmt_tns (uint32 d, uint32 s); t_stat xmt_tnf (uint32 d, uint32 s); t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta); uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry); t_stat mul_field (uint32 mpc, uint32 mpy); t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last); t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez); t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop); t_stat oct_to_dec (uint32 tbl, uint32 s); t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez); t_stat or_field (uint32 d, uint32 s); t_stat and_field (uint32 d, uint32 s); t_stat xor_field (uint32 d, uint32 s); t_stat com_field (uint32 d, uint32 s); void upd_ind (void); extern t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1); extern t_stat fp_add (uint32 d, uint32 s, t_bool sub); extern t_stat fp_mul (uint32 d, uint32 s); extern t_stat fp_div (uint32 d, uint32 s); extern t_stat fp_fsl (uint32 d, uint32 s); extern t_stat fp_fsr (uint32 d, uint32 s); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifier list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BCD+MI_STD, MAXMEMSIZE) }; REG cpu_reg[] = { { DRDATA (PC, saved_PC, 16), PV_LEFT }, { DRDATA (IR2, IR2, 16), PV_LEFT }, { DRDATA (PR1, PR1, 16), PV_LEFT }, { DRDATA (PAR, PAR, 16), PV_LEFT + REG_RO }, { DRDATA (QAR, QAR, 16), PV_LEFT + REG_RO }, { FLDATA (SW1, ind[IN_SW1], 0) }, { FLDATA (SW2, ind[IN_SW2], 0) }, { FLDATA (SW3, ind[IN_SW3], 0) }, { FLDATA (SW4, ind[IN_SW4], 0) }, { FLDATA (HP, ind[IN_HP], 0) }, { FLDATA (EZ, ind[IN_EZ], 0) }, { FLDATA (OVF, ind[IN_OVF], 0) }, { FLDATA (EXPCHK, ind[IN_EXPCHK], 0) }, { FLDATA (RDCHK, ind[IN_RDCHK], 0) }, { FLDATA (WRCHK, ind[IN_WRCHK], 0) }, { FLDATA (ARSTOP, ar_stop, 0) }, { FLDATA (IOSTOP, io_stop, 0) }, { BRDATA (IND, ind, 10, 1, NUM_IND) }, { FLDATA (IAE, iae, 0) }, { FLDATA (IDXE, idxe, 0) }, { FLDATA (IDXB, idxb, 0) }, { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, { BRDATA (PCQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { IF_IA, IF_IA, "IA", "IA", &cpu_set_opt1 }, { IF_IA, 0, "no IA", "NOIA", &cpu_set_opt1 }, { IF_EDT, IF_EDT, "EDT", "EDT", &cpu_set_opt1 }, { IF_EDT, 0, "no EDT", "NOEDT", &cpu_set_opt1 }, { IF_DIV, IF_DIV, "DIV", "DIV", &cpu_set_opt1 }, { IF_DIV, 0, "no DIV", "NODIV", &cpu_set_opt1 }, { IF_FP, IF_FP, "FP", "FP", NULL }, { IF_FP, 0, "no FP", "NOFP", NULL }, { IF_BIN, IF_BIN, "BIN", "BIN", &cpu_set_opt2 }, { IF_BIN, 0, "no BIN", "NOBIN", &cpu_set_opt2 }, { IF_IDX, IF_IDX, "IDX", "IDX", &cpu_set_opt2 }, { IF_IDX, 0, "no IDX", "NOIDX", &cpu_set_opt2 }, { IF_MII, IF_MII, "Model 2", "MOD2", &cpu_set_model }, { IF_MII, 0, "Model 1", "MOD1", &cpu_set_model }, { UNIT_MSIZE, 20000, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 40000, NULL, "40K", &cpu_set_size }, { UNIT_MSIZE, 60000, NULL, "60K", &cpu_set_size }, { UNIT_MSIZE, 0, NULL, "SAVE", &cpu_set_save }, { UNIT_MSIZE, 0, NULL, "TABLE", &cpu_set_table }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 10, 18, 1, 16, 5, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; /* Instruction table */ const int32 op_table[100] = { 0, /* 0 */ IF_FP + IF_VPA + IF_VQA, /* FADD */ IF_FP + IF_VPA + IF_VQA, /* FSUB */ IF_FP + IF_VPA + IF_VQA, /* FMUL */ 0, IF_FP + IF_VPA + IF_VQA, /* FSL */ IF_FP + IF_MII + IF_VPA + IF_VQA, /* TFL */ IF_FP + IF_MII + IF_VPA + IF_VQA, /* BTFL */ IF_FP + IF_VPA + IF_VQA, /* FSR */ IF_FP + IF_VPA + IF_VQA, /* FDV */ IF_MII + IF_VPA + IF_IMM, /* 10: BTAM */ IF_VPA + IF_IMM, /* AM */ IF_VPA + IF_IMM, /* SM */ IF_VPA + IF_IMM, /* MM */ IF_VPA + IF_IMM, /* CM */ IF_VPA + IF_IMM, /* TDM */ IF_VPA + IF_IMM, /* TFM */ IF_VPA + IF_IMM, /* BTM */ IF_DIV + IF_VPA + IF_IMM, /* LDM */ IF_DIV + IF_VPA + IF_IMM, /* DM */ IF_MII + IF_VPA + IF_VQA, /* 20: BTA */ IF_VPA + IF_VQA, /* A */ IF_VPA + IF_VQA, /* S */ IF_VPA + IF_VQA, /* M */ IF_VPA + IF_VQA, /* C */ IF_VPA + IF_VQA, /* TD */ IF_VPA + IF_VQA, /* TF */ IF_VPA + IF_VQA, /* BT */ IF_DIV + IF_VPA + IF_VQA, /* LD */ IF_DIV + IF_VPA + IF_VQA, /* D */ IF_MII + IF_VPA + IF_VQA, /* 30: TRNM */ IF_VPA + IF_VQA, /* TR */ IF_VPA, /* SF */ IF_VPA, /* CF */ IF_VPA, /* K */ IF_VPA, /* DN */ IF_VPA, /* RN */ IF_VPA, /* RA */ IF_VPA, /* WN */ IF_VPA, /* WA */ 0, /* 40 */ 0, /* NOP */ 0, /* BB */ IF_VPA + IF_VQA, /* BD */ IF_VPA + IF_VQA, /* BNF */ IF_VPA + IF_VQA, /* BNR */ IF_VPA, /* BI */ IF_VPA, /* BNI */ 0, /* H */ IF_VPA, /* B */ 0, /* 50 */ 0, 0, 0, 0, IF_VPA + IF_VQA, /* BNG - disk sys */ 0, 0, 0, 0, IF_MII + IF_VPA, /* 60: BS */ IF_IDX + IF_VPA + IF_NQX, /* BX */ IF_IDX + IF_VPA + IF_IMM, /* BXM */ IF_IDX + IF_VPA + IF_NQX, /* BCX */ IF_IDX + IF_VPA + IF_IMM, /* BCXM */ IF_IDX + IF_VPA + IF_NQX, /* BLX */ IF_IDX + IF_VPA + IF_IMM, /* BLXM */ IF_IDX + IF_VPA + IF_NQX, /* BSX */ 0, 0, IF_IDX + IF_VPA + IF_VQA, /* 70: MA */ IF_EDT + IF_VPA + IF_VQA, /* MF */ IF_EDT + IF_VPA + IF_VQA, /* MF */ IF_EDT + IF_VPA + IF_VQA, /* TNF */ 0, 0, 0, 0, 0, 0, 0, /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, IF_BIN + IF_VPA + IF_4QA, /* 90: BBT */ IF_BIN + IF_VPA + IF_4QA, /* BMK */ IF_BIN + IF_VPA + IF_VQA, /* ORF */ IF_BIN + IF_VPA + IF_VQA, /* ANDF */ IF_BIN + IF_VPA + IF_VQA, /* CPLF */ IF_BIN + IF_VPA + IF_VQA, /* EORF */ IF_BIN + IF_VPA + IF_VQA, /* OTD */ IF_BIN + IF_VPA + IF_VQA, /* DTO */ 0, 0 }; /* IO dispatch table */ t_stat (*iodisp[NUM_IO])(uint32 op, uint32 pa, uint32 f0, uint32 f1) = { NULL, &tty, &ptp, &ptr, &cdp, /* 00 - 09 */ &cdr, NULL, &dp, NULL, &lpt, NULL, NULL, NULL, NULL, NULL, /* 10 - 19 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 20 - 29 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, &btp, &btr, NULL, /* 30 - 39 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 40 - 49 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 50 - 59 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 60 - 69 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 70 - 79 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 80 - 89 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 90 - 99 */ NULL, NULL, NULL, NULL, NULL }; /* Indicator table: -1 = illegal, +1 = resets when tested */ const int32 ind_table[NUM_IND] = { -1, 0, 0, 0, 0, -1, 1, 1, -1, 1, /* 00 - 09 */ -1, 0, 0, 0, 1, 1, 1, 1, -1, 0, /* 10 - 19 */ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, /* 20 - 29 */ 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, /* 30 - 39 */ -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, /* 40 - 49 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 - 59 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 - 69 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 90 - 99 */ }; /* Add table for 1620 Model 1 */ const uint8 std_add_table[ADD_TABLE_LEN] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; /* Add table for 1620 Model 2 ("hardware add") */ const uint8 sum_table[20] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 }; /* Multiply table */ const uint8 std_mul_table[MUL_TABLE_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 2, 0, 4, 0, 6, 0, 8, 0, 0, 0, 3, 0, 6, 0, 9, 0, 2, 1, 0, 0, 4, 0, 8, 0, 2, 1, 6, 1, 0, 0, 5, 0, 0, 1, 5, 1, 0, 2, 0, 0, 6, 0, 2, 1, 8, 1, 4, 2, 0, 0, 7, 0, 4, 1, 1, 2, 8, 2, 0, 0, 8, 0, 6, 1, 4, 2, 2, 3, 0, 0, 9, 0, 8, 1, 7, 2, 6, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 0, 1, 2, 1, 4, 1, 6, 1, 8, 1, 5, 1, 8, 1, 1, 2, 4, 2, 7, 2, 0, 2, 4, 2, 8, 2, 2, 3, 6, 3, 5, 2, 0, 3, 5, 3, 0, 4, 5, 4, 0, 3, 6, 3, 2, 4, 8, 4, 4, 5, 5, 3, 2, 4, 9, 4, 6, 5, 3, 6, 0, 4, 8, 4, 6, 5, 4, 6, 2, 7, 5, 4, 4, 5, 3, 6, 2, 7, 1, 8 }; #define BRANCH(x) PCQ_ENTRY; PC = (x) #define GET_IDXADDR(x) ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1)) t_stat sim_instr (void) { uint32 PC, pla, qla, f0, f1; int32 i, t, idx, flags, sta, dev, op; t_stat reason; /* Restore saved state */ PC = saved_PC; if ((cpu_unit.flags & IF_IA) == 0) iae = 0; if ((cpu_unit.flags & IF_IDX) == 0) idxe = idxb = 0; upd_ind (); /* update indicators */ reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ saved_PC = PC; /* commit prev instr */ 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; } sim_interval = sim_interval - 1; /* Instruction fetch and address decode */ if (PC & 1) { /* PC odd? */ reason = STOP_INVIAD; /* stop */ break; } op = get_2d (PC); /* get opcode */ if (op < 0) { /* invalid? */ reason = STOP_INVINS; break; } flags = op_table[op]; /* get op, flags */ if ((flags & ALLOPT) && /* need option? */ !(flags & ALLOPT & cpu_unit.flags)) { /* any set? */ reason = STOP_INVINS; /* no, error */ break; } pla = ADDR_A (PC, I_PL); /* P last addr */ qla = ADDR_A (PC, I_QL); /* Q last addr */ if (flags & IF_VPA) { /* need P? */ reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */ if (reason != SCPE_OK) /* stop if error */ break; } if (flags & (IF_VQA | IF_4QA | IF_NQX)) { /* need Q? */ reason = get_addr (qla, /* get Q addr */ ((flags & IF_4QA)? 4: 5), /* 4 or 5 digits */ ((flags & IF_NQX)? FALSE: TRUE), /* not or indexed */ &QAR); if (reason != SCPE_OK) { /* stop if invalid */ reason = reason + (STOP_INVQDG - STOP_INVPDG); break; } } else if (flags & IF_IMM) /* immediate? */ QAR = qla; if (hst_lnt) { /* history enabled? */ hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].vld = 1; hst[hst_p].pc = PC; for (i = 0; i < INST_LEN; i++) hst[hst_p].inst[i] = M[(PC + i) % MEMSIZE]; } PC = PC + INST_LEN; /* advance PC */ switch (op) { /* case on op */ /* Transmit digit - P,Q are valid */ case OP_TD: case OP_TDM: M[PAR] = M[QAR] & (FLAG | DIGIT); /* move dig, flag */ break; /* Transmit field - P,Q are valid */ case OP_TF: case OP_TFM: reason = xmt_field (PAR, QAR, 1); /* xmit field */ break; /* Transmit record - P,Q are valid */ case OP_TR: reason = xmt_record (PAR, QAR, TRUE); /* xmit record */ break; /* Transmit record no record mark - P,Q are valid */ case OP_TRNM: reason = xmt_record (PAR, QAR, FALSE); /* xmit record but */ break; /* not rec mark */ /* Set flag - P is valid */ case OP_SF: M[PAR] = M[PAR] | FLAG; /* set flag on P */ break; /* Clear flag - P is valid */ case OP_CF: M[PAR] = M[PAR] & ~FLAG; /* clear flag on P */ break; /* Branch - P is valid */ case OP_B: BRANCH (PAR); /* branch to P */ break; /* Branch and transmit - P,Q are valid */ case OP_BT: case OP_BTM: reason = xmt_field (ADDR_S (PAR, 1), QAR, 1); /* xmit field to P-1 */ IR2 = PC; /* save PC */ BRANCH (PAR); /* branch to P */ break; /* Branch and transmit floating - P,Q are valid */ case OP_BTFL: reason = xmt_field (ADDR_S (PAR, 1), QAR, 3); /* skip 3 flags */ IR2 = PC; /* save PC */ BRANCH (PAR); /* branch to P */ break; /* Branch and transmit address - P,Q are valid */ case OP_BTA: case OP_BTAM: reason = xmt_field (ADDR_S (PAR, 1), QAR, 4); /* skip 4 flags */ IR2 = PC; /* save PC */ BRANCH (PAR); /* branch to P */ break; /* Branch back */ case OP_BB: if (PR1 != 1) { /* PR1 valid? */ BRANCH (PR1); /* return to PR1 */ PR1 = 1; /* invalidate */ } else if (IR2 != 1) { /* IR2 valid? */ BRANCH (IR2); /* return to IR2 */ IR2 = 1; /* invalidate */ } else reason = STOP_INVRTN; /* MAR check */ break; /* Branch on digit (not zero) - P,Q are valid */ case OP_BD: if ((M[QAR] & DIGIT) != 0) { /* digit != 0? */ BRANCH (PAR); /* branch */ } break; /* Branch no flag - P,Q are valid */ case OP_BNF: if ((M[QAR] & FLAG) == 0) { /* flag == 0? */ BRANCH (PAR); /* branch */ } break; /* Branch no record mark (8-2 not set) - P,Q are valid */ case OP_BNR: if ((M[QAR] & REC_MARK) != REC_MARK) { /* not rec mark? */ BRANCH (PAR); /* branch */ } break; /* Branch no group mark - P,Q are valid */ case OP_BNG: if ((M[QAR] & DIGIT) != GRP_MARK) { /* not grp mark? */ BRANCH (PAR); /* branch */ } break; /* Branch (no) indicator - P is valid */ case OP_BI: case OP_BNI: upd_ind (); /* update indicators */ t = get_2d (ADDR_A (saved_PC, I_BR)); /* get ind number */ if ((t < 0) || (ind_table[t] < 0)) { /* not valid? */ reason = STOP_INVIND; /* stop */ break; } if ((ind[t] != 0) ^ (op == OP_BNI)) { /* ind value correct? */ BRANCH (PAR); /* branch */ } if (ind_table[t] > 0) /* reset if needed */ ind[t] = 0; break; /* Add/subtract/compare - P,Q are valid */ case OP_A: case OP_AM: reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */ if (sta == ADD_CARRY) /* cout => ovflo */ ind[IN_OVF] = 1; if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; break; case OP_S: case OP_SM: reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */ if (sta == ADD_CARRY) /* cout => ovflo */ ind[IN_OVF] = 1; if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; break; case OP_C: case OP_CM: reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */ if (sta == ADD_CARRY) /* cout => ovflo */ ind[IN_OVF] = 1; if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; break; /* Multiply - P,Q are valid */ case OP_M: case OP_MM: reason = mul_field (PAR, QAR); /* multiply */ break; /* IO instructions - P is valid */ case OP_RA: case OP_WA: if ((PAR & 1) == 0) { /* P even? */ reason = STOP_INVEAD; /* stop */ break; } case OP_K: case OP_DN: case OP_RN: case OP_WN: dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */ f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */ f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT; if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */ reason = STOP_INVIO; /* stop */ else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */ break; /* Divide special feature instructions */ case OP_LD: case OP_LDM: for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */ M[PROD_AREA + i] = 0; t = M[QAR] & FLAG; /* save Q sign */ reason = xmt_divd (PAR, QAR); /* xmit dividend */ M[PROD_AREA + PROD_AREA_LEN - 1] |= t; /* set sign */ break; /* Divide - P,Q are valid */ case OP_D: case OP_DM: reason = div_field (PAR, QAR, &t); /* divide */ ind[IN_EZ] = t; /* set indicator */ if ((reason == STOP_OVERFL) && !ar_stop) /* ovflo stop? */ reason = SCPE_OK; /* no */ break; /* Edit special feature instructions */ /* Move flag - P,Q are valid */ case OP_MF: M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG); /* copy Q flag */ M[QAR] = M[QAR] & ~FLAG; /* clr Q flag */ break; /* Transmit numeric strip - P,Q are valid, P is source */ case OP_TNS: if ((PAR & 1) == 0) { /* P must be odd */ reason = STOP_INVEAD; break; } reason = xmt_tns (QAR, PAR); /* xmit and strip */ break; /* Transmit numeric fill - P,Q are valid */ case OP_TNF: if ((PAR & 1) == 0) { /* P must be odd */ reason = STOP_INVEAD; break; } reason = xmt_tnf (PAR, QAR); /* xmit and strip */ break; /* Index special feature instructions */ /* Move address - P,Q are valid */ case OP_MA: for (i = 0; i < ADDR_LEN; i++) { /* move 5 digits */ M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT); MM (PAR); MM (QAR); } break; /* Branch load index - P,Q are valid, Q not indexed */ case OP_BLX: case OP_BLXM: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } xmt_index (GET_IDXADDR (idx), QAR); /* copy Q to idx */ BRANCH (PAR); /* branch to P */ break; /* Branch store index - P,Q are valid, Q not indexed */ case OP_BSX: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } xmt_index (QAR, GET_IDXADDR (idx)); /* copy idx to Q */ BRANCH (PAR); /* branch to P */ break; /* Branch and modify index - P,Q are valid, Q not indexed */ case OP_BX: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta); if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; BRANCH (PAR); /* branch to P */ break; case OP_BXM: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta); if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; BRANCH (PAR); /* branch to P */ break; /* Branch conditionally and modify index - P,Q are valid, Q not indexed */ case OP_BCX: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta); if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */ BRANCH (PAR); /* branch */ } break; case OP_BCXM: idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ if (idx < 0) { /* disabled? */ reason = STOP_INVIDX; /* stop */ break; } reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta); if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */ BRANCH (PAR); /* branch */ } break; /* Branch and select - P is valid */ case OP_BS: t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT; /* get select */ switch (t) { /* case on select */ case 0: idxe = idxb = 0; /* indexing off */ break; case 1: idxe = 1; idxb = 0; /* index band A */ break; case 2: idxe = idxb = 1; /* index band B */ break; case 8: iae = 0; /* indirect off */ break; case 9: iae = 1; /* indirect on */ break; default: reason = STOP_INVSEL; /* undefined */ break; } BRANCH (PAR); break; /* Binary special feature instructions */ /* Branch on bit - P,Q are valid, Q is 4d address */ case OP_BBT: t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */ if (t & M[QAR] & DIGIT) { /* match to mem? */ BRANCH (PAR); /* branch */ } break; /* Branch on mask - P,Q are valid, Q is 4d address */ case OP_BMK: t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */ if (((t ^ M[QAR]) & /* match to mem? */ ((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) { BRANCH (PAR); /* branch */ } break; /* Or - P,Q are valid */ case OP_ORF: reason = or_field (PAR, QAR); /* OR fields */ break; /* AND - P,Q are valid */ case OP_ANDF: reason = and_field (PAR, QAR); /* AND fields */ break; /* Exclusive or - P,Q are valid */ case OP_EORF: reason = xor_field (PAR, QAR); /* XOR fields */ break; /* Complement - P,Q are valid */ case OP_CPLF: reason = com_field (PAR, QAR); /* COM field */ break; /* Octal to decimal - P,Q are valid */ case OP_OTD: reason = oct_to_dec (PAR, QAR); /* convert */ break; /* Decimal to octal - P,Q are valid */ case OP_DTO: reason = dec_to_oct (PAR, QAR, &t); /* convert */ ind[IN_EZ] = t; /* set indicator */ if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; break; /* Floating point special feature instructions */ case OP_FADD: reason = fp_add (PAR, QAR, FALSE); /* add */ if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; break; case OP_FSUB: reason = fp_add (PAR, QAR, TRUE); /* subtract */ if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; break; case OP_FMUL: reason = fp_mul (PAR, QAR); /* multiply */ if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; break; case OP_FDIV: reason = fp_div (PAR, QAR); /* divide */ if (ar_stop && ind[IN_OVF]) reason = STOP_FPDVZ; if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; break; case OP_FSL: reason = fp_fsl (PAR, QAR); /* shift left */ break; case OP_FSR: reason = fp_fsr (PAR, QAR); /* shift right */ break; /* Halt */ case OP_H: saved_PC = PC; /* commit inst */ reason = STOP_HALT; /* stop */ break; /* NOP */ case OP_NOP: break; /* Invalid instruction code */ default: reason = STOP_INVINS; /* stop */ break; } /* end switch */ } /* end while */ /* Simulation halted */ pcq_r->qptr = pcq_p; /* update pc q ptr */ upd_ind (); return reason; } /* Utility routines */ /* Get 2 digit field Inputs: ad = address of high digit Outputs: val = field converted to binary -1 if bad digit */ int32 get_2d (uint32 ad) { int32 d, d1; d = M[ad] & DIGIT; /* get 1st digit */ d1 = M[ADDR_A (ad, 1)] & DIGIT; /* get 2nd digit */ if (BAD_DIGIT (d) || BAD_DIGIT (d1)) /* bad? error */ return -1; return ((d * 10) + d1); /* cvt to binary */ } /* Get address routine Inputs: alast = address of low digit lnt = length indexok = TRUE if indexing allowed &addr = pointer to address output Output: return = error status (in terms of P address) addr = address converted to binary Notes: - If indexing produces a negative result, the effective address is the 10's complement of the result - An address that exceeds memory produces a MAR check stop */ t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta) { uint8 indir; int32 cnt, idx, idxa, idxv, addr; if (iae) /* init indirect */ indir = FLAG; else indir = 0; cnt = 0; /* count depth */ do { indir = indir & M[alast]; /* get indirect */ if (cvt_addr (alast, lnt, FALSE, &addr)) /* cvt addr to bin */ return STOP_INVPDG; /* bad? */ idx = get_idx (ADDR_S (alast, 1)); /* get index reg num */ if (indexok && (idx > 0)) { /* indexable? */ idxa = GET_IDXADDR (idx); /* get idx reg addr */ if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv)) /* cvt idx reg */ return STOP_INVPDG; addr = addr + idxv; /* add in index */ if (addr < 0) /* -? 10's comp */ addr = addr + 100000; } if (addr >= (int32) MEMSIZE) /* invalid addr? */ return STOP_INVPAD; alast = addr; /* new address */ lnt = ADDR_LEN; /* std len */ } while (indir && (cnt++ < ind_max)); if (cnt > ind_max) /* indir too deep? */ return STOP_INVPIA; *reta = addr; /* return address */ return SCPE_OK; } /* Convert address to binary Inputs: alast = address of low digit lnt = length signok = TRUE if signed val = address of output Outputs: status = 0 if ok, != 0 if error */ t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val) { int32 sign = 0, addr = 0, t; if (signok && (M[alast] & FLAG)) /* signed? */ sign = 1; alast = alast - lnt; /* find start */ do { PP (alast); /* incr mem addr */ t = M[alast] & DIGIT; /* get digit */ if (BAD_DIGIT (t)) /* bad? error */ return STOP_INVDIG; addr = (addr * 10) + t; /* cvt to bin */ } while (--lnt > 0); if (sign) /* minus? */ *val = -addr; else *val = addr; return SCPE_OK; } /* Get index register number Inputs: aidx = address of low digit Outputs: index = >0 if indexed =0 if not indexed <0 if indexing disabled */ t_stat get_idx (uint32 aidx) { int32 i, idx; if (idxe == 0) /* indexing off? */ return -1; for (i = idx = 0; i < 3; i++) { /* 3 flags worth */ if (M[aidx] & FLAG) /* test flag */ idx = idx | (1 << i); MM (aidx); /* next digit */ } return idx; } /* Update indicators routine */ void upd_ind (void) { ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ]; /* HPEZ = HP | EZ */ ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO]; ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] | /* ANYCHK = all chks */ ind[IN_MBREVEN] | ind[IN_MBRODD] | ind[IN_PRCHK] | ind[IN_DACH]; ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0; /* clr index indics */ if (!idxe) /* off? */ ind[IN_IXN] = 1; else if (!idxb) /* on, band A? */ ind[IN_IXA] = 1; else ind[IN_IXB] = 1; /* no, band B */ return; } /* Transmit routines */ /* Transmit field from 's' to 'd' - ignore first 'skp' flags */ t_stat xmt_field (uint32 d, uint32 s, uint32 skp) { uint32 cnt = 0; uint8 t; do { t = M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ MM (d); /* decr mem addrs */ MM (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (((t & FLAG) == 0) || (cnt <= skp)); /* until flag */ return SCPE_OK; } /* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */ t_stat xmt_record (uint32 d, uint32 s, t_bool cpy) { uint32 cnt = 0; while ((M[s] & REC_MARK) != REC_MARK) { /* until rec mark */ M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ PP (d); /* incr mem addrs */ PP (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } if (cpy) /* copy rec mark */ M[d] = M[s] & (FLAG | DIGIT); return SCPE_OK; } /* Transmit index from 's' to 'd' - fixed five character field */ t_stat xmt_index (uint32 d, uint32 s) { int32 i; M[d] = M[s] & (FLAG | DIGIT); /* preserve sign */ MM (d); MM (s); /* decr mem addrs */ for (i = 0; i < ADDR_LEN - 2; i++) { /* copy 3 digits */ M[d] = M[s] & DIGIT; /* without flags */ MM (d); /* decr mem addrs */ MM (s); } M[d] = (M[s] & DIGIT) | FLAG; /* set flag on last */ return SCPE_OK; } /* Transmit dividend from 'd' to 's' - clear flag on first digit */ t_stat xmt_divd (uint32 d, uint32 s) { uint32 cnt = 0; M[d] = M[s] & DIGIT; /* first w/o flag */ do { MM (d); /* decr mem addrs */ MM (s); M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((M[d] & FLAG) == 0); /* until src flag */ return SCPE_OK; } /* Transmit numeric strip from 's' to 'd' - s is odd */ t_stat xmt_tns (uint32 d, uint32 s) { uint32 cnt = 0; uint8 t, z; t = M[s] & DIGIT; /* get units */ z = M[s - 1] & DIGIT; /* get zone */ if ((z == 1) || (z == 5) || ((z == 2) && (t == 0))) /* 1x, 5x, 20? */ M[d] = t | FLAG; /* set flag */ else M[d] = t; /* else clear flag */ do { MM (d); /* decr mem addrs */ s = ADDR_S (s, 2); t = M[d] & FLAG; /* save dst flag */ M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ if (cnt >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; cnt = cnt + 2; } while (t == 0); /* until dst flag */ M[d] = M[d] | FLAG; /* set flag at end */ return SCPE_OK; } /* Transmit numeric fill from 's' to 'd' - d is odd */ t_stat xmt_tnf (uint32 d, uint32 s) { uint32 cnt = 0; uint8 t; t = M[s]; /* get 1st digit */ M[d] = t & DIGIT; /* store */ M[d - 1] = (t & FLAG)? 5: 7; /* set sign from flag */ do { MM (s); /* decr mem addr */ d = ADDR_S (d, 2); t = M[s]; /* get src digit */ M[d] = t & DIGIT; /* move to dst, no flag */ M[d - 1] = 7; /* set zone */ if (cnt >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; cnt = cnt + 2; } while ((t & FLAG) == 0); /* until src flag */ return SCPE_OK; } /* Add routine Inputs: d = destination field low (P) s = source field low (Q) sub = TRUE if subtracting sto = TRUE if storing skp = number of source field flags, beyond sign, to ignore Output: return = status sta = ADD_NOCRY: no carry out, no sign change ADD_SCHNG: sign change ADD_CARRY: carry out Reference Manual: "When the sum is zero, the sign of the P field is retained." */ t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta) { uint32 cry, src, dst, res, comp, dp, dsv; uint32 src_f = 0, cnt = 0, dst_f; *sta = ADD_NOCRY; /* assume no cry */ dsv = d; /* save dst */ comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0); /* set compl flag */ cry = 0; /* clr carry */ ind[IN_HP] = ((M[d] & FLAG) == 0); /* set sign from res */ ind[IN_EZ] = 1; /* assume zero */ dst = M[d] & DIGIT; /* 1st digits */ src = M[s] & DIGIT; if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */ return STOP_INVDIG; if (comp) /* complement? */ src = 10 - src; res = add_one_digit (dst, src, &cry); /* add */ if (sto) /* store */ M[d] = (M[d] & FLAG) | res; MM (d); MM (s); /* decr mem addrs */ do { dst = M[d] & DIGIT; /* get dst digit */ dst_f = M[d] & FLAG; /* get dst flag */ if (src_f) /* src done? src = 0 */ src = 0; else { src = M[s] & DIGIT; /* get src digit */ if (cnt >= skp) /* get src flag */ src_f = M[s] & FLAG; MM (s); /* decr src addr */ } if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */ return STOP_INVDIG; if (comp) /* complement? */ src = 9 - src; res = add_one_digit (dst, src, &cry); /* add */ if (sto) /* store */ M[d] = dst_f | res; MM (d); /* decr dst addr */ if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (dst_f == 0); /* until dst done */ if (!src_f) /* !src done? ovf */ ind[IN_OVF] = 1; if (comp && !cry && !ind[IN_EZ]) { /* recomp needed? */ ind[IN_HP] = ind[IN_HP] ^ 1; /* flip indicator */ if (sto) { /* storing? */ for (cry = 1, dp = dsv; dp != d; ) { /* rescan */ dst = M[dp] & DIGIT; /* get dst digit */ res = add_one_digit (9 - dst, 0, &cry); /* "add" */ M[dp] = (M[dp] & FLAG) | res; /* store */ MM (dp); /* decr dst addr */ } M[dsv] = M[dsv] ^ FLAG; /* compl sign */ } *sta = ADD_SIGNC; /* sign changed */ return SCPE_OK; } /* end if recomp */ if (ind[IN_EZ]) /* res = 0? clr HP */ ind[IN_HP] = 0; if (!comp && cry) /* set status */ *sta = ADD_CARRY; return SCPE_OK; } /* Add one digit via table (Model 1) or "hardware" (Model 2) */ uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry) { uint32 res; if (*cry) src = src + 1; /* cry in? incr src */ if (src >= 10) { /* src > 10? */ src = src - 10; /* src -= 10 */ *cry = 1; /* carry out */ } else *cry = 0; /* else no carry */ if (cpu_unit.flags & IF_MII) /* Model 2? */ res = sum_table[dst + src]; /* "hardware" */ else res = M[ADD_TABLE + (dst * 10) + src]; /* table lookup */ if (res & FLAG) /* carry out? */ *cry = 1; if (res & DIGIT) /* nz? clr ind */ ind[IN_EZ] = 0; return res & DIGIT; } /* Multiply routine Inputs: mpc = multiplicand address mpy = multiplier address Outputs: return = status Reference manual: "A zero product may have a negative or positive sign, depending on the signs of the fields at the P and Q addresses." */ t_stat mul_field (uint32 mpc, uint32 mpy) { int32 i; uint32 pro; /* prod pointer */ uint32 mpyd, mpyf; /* mpy digit, flag */ uint32 cnt = 0; /* counter */ uint8 sign; /* final sign */ t_stat r; PR1 = 1; /* step on PR1 */ for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */ M[PROD_AREA + i] = 0; sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG); /* get final sign */ ind[IN_HP] = (sign == 0); /* set indicators */ ind[IN_EZ] = 1; pro = PROD_AREA + PROD_AREA_LEN - 1; /* product ptr */ /* Loop on multiplier (mpy) and product (pro) digits */ do { mpyd = M[mpy] & DIGIT; /* multiplier digit */ mpyf = (M[mpy] & FLAG) && (cnt != 0); /* last digit flag */ if (BAD_DIGIT (mpyd)) /* bad? */ return STOP_INVDIG; r = mul_one_digit (mpyd, mpc, pro, mpyf); /* prod += mpc*mpy_dig */ if (r != SCPE_OK) /* error? */ return r; MM (mpy); /* decr mpyr, prod addrs */ MM (pro); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((mpyf == 0) || (cnt <= 1)); /* until mpyr flag */ if (ind[IN_EZ]) /* res = 0? clr HP */ ind[IN_HP] = 0; M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set final sign */ return SCPE_OK; } /* Multiply step Inputs: mpyd = multiplier digit (tested valid) mpcp = multiplicand low address prop = product low address last = last iteration flag (set flag on high product) Outputs: prod += multiplicand * multiplier_digit return = status The multiply table address is constructed as follows: - double the multiplier digit - use the 10's digit of the doubled result, + 1, as the 100's digit of the table address - use the multiplicand digit as the 10's digit of the table address - use the unit digit of the doubled result as the unit digit of the table address EZ indicator is cleared if a non-zero digit is ever generated */ t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last) { uint32 mpta, mptb; /* mult table */ uint32 mptd; /* mult table digit */ uint32 mpcd, mpcf; /* mpc digit, flag */ uint32 prwp; /* prod working ptr */ uint32 prod; /* product digit */ uint32 cry; /* carry */ uint32 mpcc, cryc; /* counters */ mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2): /* set mpy table 100's, */ (((mpyd - 5) * 2) + 100)); /* 1's digits */ /* Inner loop on multiplicand (mpcp) and product (prop) digits */ mpcc = 0; /* multiplicand ctr */ do { prwp = prop; /* product working ptr */ mpcd = M[mpcp] & DIGIT; /* multiplicand digit */ mpcf = M[mpcp] & FLAG; /* multiplicand flag */ if (BAD_DIGIT (mpcd)) /* bad? */ return STOP_INVDIG; mpta = mptb + (mpcd * 10); /* mpy table 10's digit */ cry = 0; /* init carry */ mptd = M[mpta] & DIGIT; /* mpy table digit */ if (BAD_DIGIT (mptd)) /* bad? */ return STOP_INVDIG; prod = M[prwp] & DIGIT; /* product digit */ if (BAD_DIGIT (prod)) /* bad? */ return STOP_INVDIG; M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */ MM (prwp); /* decr working ptr */ mptd = M[mpta + 1] & DIGIT; /* mpy table digit */ if (BAD_DIGIT (mptd)) /* bad? */ return STOP_INVDIG; prod = M[prwp] & DIGIT; /* product digit */ if (BAD_DIGIT (prod)) /* bad? */ return STOP_INVDIG; M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */ cryc = 0; /* (stop runaway) */ while (cry) { /* propagate carry */ MM (prwp); /* decr working ptr */ prod = M[prwp] & DIGIT; /* product digit */ if (BAD_DIGIT (prod)) /* bad? */ return STOP_INVDIG; M[prwp] = add_one_digit (prod, 0, &cry); /* add cry */ if (cryc++ > MEMSIZE) return STOP_FWRAP; } MM (mpcp); /* decr mpc, prod ptrs */ MM (prop); if (mpcc++ > MEMSIZE) return STOP_FWRAP; } while ((mpcf == 0) || (mpcc <= 1)); /* until mpcf flag */ if (last) /* flag high product */ M[prop] = M[prop] | FLAG; return SCPE_OK; } /* Divide routine - comments from Geoff Kuenning's 1620 simulator The destination of the divide is given by: 100 - <# digits in quotient> Which is more easily calculated as: 100 - <# digits in divisor> - <# digits in dividend> The quotient goes into 99 minus the divisor length. The remainder goes into 99. The load dividend instruction (above) should have specified a P address of 99 minus the size of the divisor. Note that this all implies that "dest" points to the *leftmost* digit of the dividend. After the division, the assumed decimal point will be as many positions to the left as there are digits in the divisor. In other words, a 4-digit divisor will produce 4 (assumed) decimal places. There are other ways to do these things. In particular, the load-dividend instruction doesn't have to specify the above formula; if it's done differently, then you don't have to get decimal places. This is not well-explained in the books I have. How to divide on a 1620: The dividend is the field at 99: 90 = _1234567890 The divisor is somewhere else in memory: _03 The divide operation specifies the left-most digit of the dividend as the place to begin trial subtractions: DM 90,3 The loop works as follows: 1. Call the left-most digit of the dividend "current_dividend". Call the location current_dividend - "quotient_digit". 2. Clear the flag at current_dividend, and set one at quotient_digit. 88 = _001234567890, q_d = 88, c_d = 90 [Not actually done; divisor length controls subtract.] 3. Subtract the divisor from the field at current-dividend, using normal 1620 rules, except that signs are ignored. Continue these subtractions until either 10 subtractions have been done, or you get a negative result: 88 = _00_2234567890, q_d = 88, c_d = 90 4. If 10 subtractions have been done, set the overflow indicator and abort. Otherwise, add the divisor back to correct for the oversubtraction: 88 = _001234567890, q_d = 88, c_d = 90 5. Store the (net) number of subtractions in quotient_digit: 88 = _001234567890, q_d = 88, c_d = 90 6. If this is not the first pass, clear the flag at quotient_digit. Increment quotient_digit and current_dividend, and set a flag at the new quotient_digit: 88 = _0_01234567890, q_d = 89, c_d = 91 [If first pass, set a flag at quotient digit.] 7. If current_dividend is not 100, repeat steps 3 through 7. 8. Set flags at 99 and quotient_digit - 1 according to the rules of algebra: the quotient's sign is the exclusive-or of the signs of the divisor and dividend, and the remainder has the sign of the dividend: 10 / 3 = 3 remainder 1 10 / -3 = -3 remainder 1 -10 / 3 = -3 remainder -1 -10 / -3 = 3 remainder -1 This preserves the relationship dd = q * dv + r. Our example continues as follows for steps 3 through 7: 3. 88 = _0_00_334567890, q_d = 89, c_d = 91 4. 88 = _0_00034567890 5. 88 = _0_40034567890 6. 88 = _04_0034567890, q_d = 90, c_d = 92 3. 88 = _04_00_34567890 4. 88 = _04_0004567890 5. 88 = _04_1004567890 6. 88 = _041_004567890, q_d = 91, c_d = 93 3. 88 = _041_00_2567890 4. 88 = _041_001567890 5. 88 = _041_101567890 6. 88 = _0411_01567890, q_d = 92, c_d = 94 3. 88 = _0411_00_367890 4. 88 = _0411_00067890 5. 88 = _0411_50067890 6. 88 = _04115_0067890, q_d = 93, c_d = 95 3. 88 = _04115_00_37890 4. 88 = _04115_0007890 5. 88 = _04115_2007890 6. 88 = _041152_007890, q_d = 94, c_d = 96 3. 88 = _041152_00_2890 4. 88 = _041152_001890 5. 88 = _041152_201890 6. 88 = _0411522_01890, q_d = 95, c_d = 97 3. 88 = _0411522_00_390 4. 88 = _0411522_00090 5. 88 = _0411522_60090 6. 88 = _04115226_0090, q_d = 96, c_d = 98 3. 88 = _04115226_00_30 4. 88 = _04115226_0000 5. 88 = _04115226_3000 6. 88 = _041152263_000, q_d = 97, c_d = 99 3. 88 = _041152263_00_3 4. 88 = _041152263_000 5. 88 = _041152263_000 6. 88 = _0411522630_00, q_d = 98, c_d = 100 In the actual code below, we elide several of these steps in various ways for convenience and efficiency. Note that the EZ indicator is NOT valid for divide, because it is cleared by any non-zero result in an intermediate add. The code maintains its own EZ indicator for the quotient. */ t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez) { uint32 quop, quod, quos; /* quo ptr, dig, sign */ uint32 dvds; /* dvd sign */ t_bool first = TRUE; /* first pass */ t_stat r; dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG; /* dividend sign */ quos = dvds ^ (M[dvr] & FLAG); /* quotient sign */ ind[IN_HP] = (quos == 0); /* set indicators */ *ez = 1; /* Loop on current dividend, high order digit at dvd */ do { r = div_one_digit (dvd, dvr, 10, &quod, &quop); /* dev quo digit */ if (r != SCPE_OK) /* error? */ return r; /* Store quotient digit and advance current dividend pointer */ if (first) { /* first pass? */ if (quod >= 10) { /* overflow? */ ind[IN_OVF] = 1; /* set indicator */ return STOP_OVERFL; /* stop */ } M[quop] = FLAG | quod; /* set flag on quo */ first = FALSE; } else M[quop] = quod; /* store quo digit */ if (quod) /* if nz, clr ind */ *ez = 0; PP (dvd); /* incr dvd ptr */ } while (dvd != (PROD_AREA + PROD_AREA_LEN)); /* until end prod */ /* Division done. Set signs of quo, rem, set flag on high order remainder */ if (*ez) /* res = 0? clr HP */ ind[IN_HP] = 0; M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds; /* remainder sign */ M[quop] = M[quop] | quos; /* quotient sign */ PP (quop); /* high remainder */ M[quop] = M[quop] | FLAG; /* set flag */ return SCPE_OK; } /* Divide step Inputs: dvd = current dividend address (high digit) dvr = divisor address (low digit) max = max number of iterations before overflow &quod = address to store quotient digit &quop = address to store quotient pointer (can be NULL) Outputs: return = status Divide step calculates a quotient digit by repeatedly subtracting the divisor from the current dividend. The divisor's length controls the subtraction; dividend flags are ignored. */ t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop) { uint32 dvrp, dvrd, dvrf; /* dvr ptr, dig, flag */ uint32 dvdp, dvdd; /* dvd ptr, dig */ uint32 qd, cry; /* quo dig, carry */ uint32 cnt; for (qd = 0; qd < max; qd++) { /* devel quo dig */ dvrp = dvr; /* divisor ptr */ dvdp = dvd; /* dividend ptr */ cnt = 0; cry = 1; /* carry in = 1 */ do { /* sub dvr fm dvd */ dvdd = M[dvdp] & DIGIT; /* dividend digit */ if (BAD_DIGIT (dvdd)) /* bad? */ return STOP_INVDIG; dvrd = M[dvrp] & DIGIT; /* divisor digit */ dvrf = M[dvrp] & FLAG; /* divisor flag */ if (BAD_DIGIT (dvrd)) /* bad? */ return STOP_INVDIG; M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */ MM (dvdp); /* decr ptrs */ MM (dvrp); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */ if (!cry) { /* !cry = borrow */ dvdd = M[dvdp] & DIGIT; /* borrow digit */ if (BAD_DIGIT (dvdd)) /* bad? */ return STOP_INVDIG; M[dvdp] = add_one_digit (dvdd, 9, &cry); /* sub */ } if (!cry) /* !cry = negative */ break; } /* Add back the divisor to correct for the negative result */ dvrp = dvr; /* divisor ptr */ dvdp = dvd; /* dividend ptr */ cnt = 0; cry = 0; /* carry in = 0 */ do { dvdd = M[dvdp] & DIGIT; /* dividend digit */ dvrd = M[dvrp] & DIGIT; /* divisor digit */ dvrf = M[dvrp] & FLAG; /* divisor flag */ M[dvdp] = add_one_digit (dvdd, dvrd, &cry); /* add */ MM (dvdp); /* decr ptrs */ MM (dvrp); cnt++; } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */ if (cry) { /* carry out? */ dvdd = M[dvdp] & DIGIT; /* borrow digit */ M[dvdp] = add_one_digit (dvdd, 0, &cry); /* add */ } if (quop != NULL) /* set quo addr */ *quop = dvdp; *quod = qd; /* set quo digit */ return SCPE_OK; } /* Logical operation routines (and, or, xor, complement) Inputs: d = destination address s = source address Output: return = status Destination flags are preserved; EZ reflects the result. COM does not obey normal field length restrictions. */ t_stat or_field (uint32 d, uint32 s) { uint32 cnt = 0; int32 t; ind[IN_EZ] = 1; /* assume result zero */ do { t = M[s]; /* get src */ M[d] = (M[d] & FLAG) | ((M[d] | t) & 07); /* OR src to dst */ if (M[d] & DIGIT) /* nz dig? clr ind */ ind[IN_EZ] = 0; MM (d); /* decr pointers */ MM (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ return SCPE_OK; } t_stat and_field (uint32 d, uint32 s) { uint32 cnt = 0; int32 t; ind[IN_EZ] = 1; /* assume result zero */ do { t = M[s]; /* get src */ M[d] = (M[d] & FLAG) | ((M[d] & t) & 07); /* AND src to dst */ if (M[d] & DIGIT) /* nz dig? clr ind */ ind[IN_EZ] = 0; MM (d); /* decr pointers */ MM (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ return SCPE_OK; } t_stat xor_field (uint32 d, uint32 s) { uint32 cnt = 0; int32 t; ind[IN_EZ] = 1; /* assume result zero */ do { t = M[s]; /* get src */ M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07); /* XOR src to dst */ if (M[d] & DIGIT) /* nz dig? clr ind */ ind[IN_EZ] = 0; MM (d); /* decr pointers */ MM (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ return SCPE_OK; } t_stat com_field (uint32 d, uint32 s) { uint32 cnt = 0; int32 t; ind[IN_EZ] = 1; /* assume result zero */ do { t = M[s]; /* get src */ M[d] = (t & FLAG) | ((t ^ 07) & 07); /* comp src to dst */ if (M[d] & DIGIT) /* nz dig? clr ind */ ind[IN_EZ] = 0; MM (d); /* decr pointers */ MM (s); if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while ((t & FLAG) == 0); /* until src flag */ return SCPE_OK; } /* Octal to decimal Inputs: tbl = conversion table address (low digit) s = source address Outputs: product area = converted source result = status OTD is a cousin of multiply. The octal digits in the source are multiplied by successive values in the conversion table, and the results are accumulated in the product area. Although the manual does not say, this code assumes that EZ and HP are affected. */ t_stat oct_to_dec (uint32 tbl, uint32 s) { uint32 cnt = 0, tblc; uint32 i, sd, sf, tf, sign; t_stat r; for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */ M[PROD_AREA + i] = 0; sign = M[s] & FLAG; /* save sign */ ind[IN_EZ] = 1; /* set indicators */ ind[IN_HP] = (sign == 0); do { sd = M[s] & DIGIT; /* src digit */ sf = M[s] & FLAG; /* src flag */ r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf); if (r != SCPE_OK) /* err? */ return r; MM (s); /* decr src addr */ MM (tbl); /* skip 1st tbl dig */ tblc = 0; /* count */ do { tf = M[tbl] & FLAG; /* get next */ MM (tbl); /* decr ptr */ if (tblc++ > MEMSIZE) return STOP_FWRAP; } while (tf == 0); /* until flag */ if (cnt++ >= MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } while (sf == 0); if (ind[IN_EZ]) /* res = 0? clr HP */ ind[IN_HP] = 0; M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set sign */ return SCPE_OK; } /* Decimal to octal Inputs: d = destination address tbl = conversion table address (low digit of highest power) &ez = address of soft EZ indicator product area = field to convert Outputs: return = status DTO is a cousin to divide. The number in the product area is repeatedly divided by successive values in the conversion table, and the quotient digits are stored in the destination. Although the manual does not say, this code assumes that EZ and HP are affected. */ t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez) { uint32 sign, octd, t; t_bool first = TRUE; uint32 ctr = 0; t_stat r; sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG; /* input sign */ *ez = 1; /* set indicators */ ind[IN_HP] = (sign == 0); for ( ;; ) { r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1, /* divide */ tbl, 8, &octd, NULL); if (r != SCPE_OK) /* error? */ return r; if (first) { /* first pass? */ if (octd >= 8) { /* overflow? */ ind[IN_OVF] = 1; /* set indicator */ return SCPE_OK; /* stop */ } M[d] = FLAG | octd; /* set flag on quo */ first = FALSE; } else M[d] = octd; /* store quo digit */ if (octd) /* if nz, clr ind */ *ez = 0; PP (tbl); /* incr tbl addr */ if ((M[tbl] & REC_MARK) == REC_MARK) /* record mark? */ break; PP (tbl); /* skip flag */ if ((M[tbl] & REC_MARK) == REC_MARK) /* record mark? */ break; do { /* look for F, rec mk */ PP (tbl); t = M[tbl]; } while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK)); MM (tbl); /* step back one */ PP (d); /* incr quo addr */ if (ctr++ > MEMSIZE) /* (stop runaway) */ return STOP_FWRAP; } if (*ez) /* res = 0? clr HP */ ind[IN_HP] = 0; M[d] = M[d] | sign; /* set result sign */ return SCPE_OK; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int32 i; static t_bool one_time = TRUE; PR1 = IR2 = 1; /* invalidate PR1,IR2 */ ind[0] = 0; for (i = IN_SW4 + 1; i < NUM_IND; i++) /* init indicators */ ind[i] = 0; if (cpu_unit.flags & IF_IA) /* indirect enabled? */ iae = 1; else iae = 0; idxe = idxb = 0; /* indexing off */ pcq_r = find_reg ("PCQ", NULL, dptr); /* init old PC queue */ if (pcq_r) pcq_r->qptr = 0; else return SCPE_IERR; sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init breakpoints */ upd_ind (); /* update indicators */ if (one_time) /* set default tables */ cpu_set_table (&cpu_unit, 1, NULL, NULL); one_time = FALSE; 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] & (FLAG | DIGIT); 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 & (FLAG | DIGIT); return SCPE_OK; } /* Memory size change */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; uint32 i; if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 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; } /* Model change */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val) cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) | IF_DIV | IF_IA | IF_EDT; else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT); return SCPE_OK; } /* Set/clear Model 1 option */ t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cpu_unit.flags & IF_MII) { printf ("Feature is standard on 1620 Model 2\n"); if (sim_log) fprintf (sim_log, "Feature is standard on 1620 Model 2\n"); return SCPE_NOFNC; } return SCPE_OK; } /* Set/clear Model 2 option */ t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc) { if (!(cpu_unit.flags & IF_MII)) { printf ("Feature is not available on 1620 Model 1\n"); if (sim_log) fprintf (sim_log, "Feature is not available on 1620 Model 1\n"); return SCPE_NOFNC; } return SCPE_OK; } /* Front panel save */ t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc) { if (saved_PC & 1) return SCPE_NOFNC; PR1 = saved_PC; return SCPE_OK; } /* Set standard add/multiply tables */ t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; for (i = 0; i < MUL_TABLE_LEN; i++) /* set mul table */ M[MUL_TABLE + i] = std_mul_table[i]; if (((cpu_unit.flags & IF_MII) == 0) || val) { /* set add table */ for (i = 0; i < ADD_TABLE_LEN; i++) M[ADD_TABLE + i] = std_add_table[i]; } return SCPE_OK; } /* 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].vld = 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 i, k, di, lnt; char *cptr = (char *) desc; t_value sim_eval[INST_LEN]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(++di) % hst_lnt]; /* entry pointer */ if (h->vld) { /* instruction? */ fprintf (st, "%05d ", h->pc); for (i = 0; i < INST_LEN; i++) sim_eval[i] = h->inst[i]; if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { fprintf (st, "(undefined)"); for (i = 0; i < INST_LEN; i++) fprintf (st, "%02X", h->inst[i]); } fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/I1620/i1620_lp.c0000644000175000017500000003203311107553644012751 0ustar vlmvlm/* i1620_lp.c: IBM 1443 line printer simulator Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt 1443 line printer 19-Jan-07 RMS Added UNIT_TEXT flag 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility 29-Dec-03 RMS Fixed bug in scheduling 25-Apr-03 RMS Revised for extended file support */ #include "i1620_defs.h" #define LPT_BSIZE 197 /* buffer size */ #define K_IMM 0x10 /* control now */ #define K_LIN 0x20 /* spc lines */ #define K_CH10 0x40 /* chan 10 */ #define K_LCNT 0x03 /* line count */ #define K_CHAN 0x0F /* channel */ extern uint8 M[MAXMEMSIZE]; extern uint8 ind[NUM_IND]; extern UNIT cpu_unit; extern uint32 io_stop; uint32 cct[CCT_LNT] = { 03 }; /* car ctrl tape */ int32 cct_lnt = 66, cct_ptr = 0; /* cct len, ptr */ int32 lpt_bptr = 0; /* lpt buf ptr */ char lpt_buf[LPT_BSIZE + 1]; /* lpt buf */ int32 lpt_savctrl = 0; /* saved spc ctrl */ t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); void lpt_buf_init (void); t_stat lpt_num (uint32 pa, uint32 len, uint32 f1); t_stat lpt_print (void); t_stat lpt_space (int32 lines, int32 lflag); #define CHP(ch,val) ((val) & (1 << (ch))) /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 50) }; REG lpt_reg[] = { { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE + 1) }, { DRDATA (BPTR, lpt_bptr, 8) }, { HRDATA (PCTL, lpt_savctrl, 8) }, { FLDATA (PRCHK, ind[IN_PRCHK], 0) }, { FLDATA (PRCH9, ind[IN_PRCH9], 0) }, { FLDATA (PRCH12, ind[IN_PRCH12], 0) }, { FLDATA (PRBSY, ind[IN_PRBSY], 0) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, { DRDATA (CCTP, cct_ptr, 8), PV_LEFT }, { DRDATA (CCTL, cct_lnt, 8), REG_RO + PV_LEFT }, { NULL } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &lpt_reset, NULL, &lpt_attach, NULL }; /* Data tables */ /* Numeric (flag plus digit) to lineprinter (ASCII) */ const char num_to_lpt[32] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '|', ' ', '@', ':', ' ', 'G', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'W', ' ', '*', ' ', -1, 'X' }; /* Alphameric (digit pair) to lineprinter (ASCII) */ const char alp_to_lpt[256] = { ' ', -1, '?', '.', ')', -1, -1, -1, /* 00 */ -1, -1, -1, -1, -1, -1, -1, -1, '+', -1, '!', '$', '*', ' ', -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, '0', '=', '@', ':', -1, -1, /* 30 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ 'H', 'I', -1, -1, -1, -1, -1, -1, '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ 'Q', 'R', -1, -1, -1, -1, -1, -1, -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ 'Y', 'Z', -1, -1, -1, -1, -1, -1, '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ '8', '9', -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ -1, -1, -1, -1, -1, -1, -1, -1 }; /* Line printer IO routine - Hard errors halt the system. - Invalid characters print a blank, set the WRCHK and PRCHK flags, and halt the system if IO stop is set. */ t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1) { int8 lpc; uint8 z, d; t_stat r, sta; sta = SCPE_OK; sim_cancel (&lpt_unit); /* "stall" until */ ind[IN_PRBSY] = 0; /* printer free */ switch (op) { /* decode op */ case OP_K: /* control */ lpt_savctrl = (f0 << 4) | f1; /* form ctrl */ if (lpt_savctrl & K_IMM) /* immediate? */ return lpt_print (); break; case OP_DN: return lpt_num (pa, 20000 - (pa % 20000), f1); /* dump numeric */ case OP_WN: return lpt_num (pa, 0, f1); /* write numeric */ case OP_WA: for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */ d = M[pa] & DIGIT; /* get digit */ z = M[pa - 1] & DIGIT; /* get zone */ if ((d & REC_MARK) == REC_MARK) /* 8-2 char? */ break; lpc = alp_to_lpt[(z << 4) | d]; /* translate pair */ if (lpc < 0) { /* bad char? */ ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */ if (io_stop) /* set return status */ sta = STOP_INVCHR; } lpt_buf[lpt_bptr] = lpc & 0x7F; /* fill buffer */ pa = ADDR_A (pa, 2); /* incr mem addr */ } if ((f1 & 1) == 0) { ; /* print now? */ r = lpt_print (); /* print line */ if (r != SCPE_OK) return r; } return sta; default: /* invalid function */ return STOP_INVFNC; } return SCPE_OK; } /* Print numeric */ t_stat lpt_num (uint32 pa, uint32 len, uint32 f1) { uint32 end; uint8 d; int8 lpc; t_stat r, sta; sta = SCPE_OK; end = pa + len; for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */ d = M[pa]; /* get digit */ if (len? (pa >= end): /* end reached? */ ((d & REC_MARK) == REC_MARK)) break; lpc = num_to_lpt[d]; /* translate */ if (lpc < 0) { /* bad char? */ ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */ if (io_stop) /* set return status */ sta = STOP_INVCHR; } lpt_buf[lpt_bptr++] = lpc & 0x7F; /* fill buffer */ PP (pa); /* incr mem addr */ } if ((f1 & 1) == 0) { /* print now? */ r = lpt_print (); /* print line */ if (r != SCPE_OK) return r; } return sta; } /* Print and space */ t_stat lpt_print (void) { int32 i, chan, ctrl = lpt_savctrl; if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */ ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ return SCPE_UNATT; } ind[IN_PRBSY] = 1; /* print busy */ sim_activate (&lpt_unit, lpt_unit.wait); /* start timer */ for (i = LPT_WIDTH; i <= LPT_BSIZE; i++) /* clear unprintable */ lpt_buf[i] = ' '; while ((lpt_bptr > 0) && (lpt_buf[lpt_bptr - 1] == ' ')) lpt_buf[--lpt_bptr] = 0; /* trim buffer */ if (lpt_bptr) { /* any line? */ fputs (lpt_buf, lpt_unit.fileref); /* print */ lpt_unit.pos = ftell (lpt_unit.fileref); /* update pos */ lpt_buf_init (); /* reinit buf */ if (ferror (lpt_unit.fileref)) { /* error? */ ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ perror ("LPT I/O error"); clearerr (lpt_unit.fileref); return SCPE_IOERR; } } lpt_savctrl = 0x61; /* reset ctrl */ if ((ctrl & K_LIN) == ((ctrl & K_IMM)? 0: K_LIN)) /* space lines? */ return lpt_space (ctrl & K_LCNT, FALSE); chan = lpt_savctrl & K_CHAN; /* basic chan */ if (lpt_savctrl & K_CH10) { /* chan 10-12? */ if (chan == 0) chan = 10; else if (chan == 3) chan = 11; else if (chan == 4) chan = 12; else chan = 0; } if ((chan == 0) || (chan > 12)) return STOP_INVFNC; for (i = 1; i < cct_lnt + 1; i++) { /* sweep thru cct */ if (CHP (chan, cct[(cct_ptr + i) % cct_lnt])) return lpt_space (i, TRUE); } return STOP_CCT; /* runaway channel */ } /* Space routine - space or skip n lines Inputs: count = number of lines to space or skip sflag = skip (TRUE) or space (FALSE) */ t_stat lpt_space (int32 count, int32 sflag) { int32 i; cct_ptr = (cct_ptr + count) % cct_lnt; /* adv cct, mod lnt */ if (sflag && CHP (0, cct[cct_ptr])) /* skip, top of form? */ fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ else { for (i = 0; i < count; i++) /* count lines */ fputc ('\n', lpt_unit.fileref); } lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ ind[IN_PRCH9] = CHP (9, cct[cct_ptr]) != 0; /* set indicators */ ind[IN_PRCH12] = CHP (12, cct[cct_ptr]) != 0; if (ferror (lpt_unit.fileref)) { /* error? */ ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ perror ("LPT I/O error"); clearerr (lpt_unit.fileref); return SCPE_IOERR; } return SCPE_OK; } /* Unit service - clear printer busy */ t_stat lpt_svc (UNIT *uptr) { ind[IN_PRBSY] = 0; return SCPE_OK; } /* Initialize lpt buffer */ void lpt_buf_init (void) { int32 i; lpt_bptr = 0; for (i = 0; i < LPT_WIDTH + 1; i++) lpt_buf[i] = 0; return; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { lpt_buf_init (); /* clear buffer */ cct_ptr = 0; /* clear cct ptr */ lpt_savctrl = 0x61; /* clear cct action */ ind[IN_PRCHK] = ind[IN_PRBSY] = 0; /* clear indicators */ ind[IN_PRCH9] = ind[IN_PRCH12] = 0; return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { lpt_reset (&lpt_dev); return attach_unit (uptr, cptr); } simh-3.8.1/I1620/i1620_cd.c0000644000175000017500000004127111107431300012710 0ustar vlmvlm/* i1620_cd.c: IBM 1622 card reader/punch Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cdr 1622 card reader cdp 1622 card punch 19-Jan-07 RMS Set UNIT_TEXT flag 13-Jul-06 RMS Fixed card reader fgets call (from Tom McBride) Fixed card reader boot sequence (from Tom McBride) 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility 25-Apr-03 RMS Revised for extended file support Cards are represented as ASCII text streams terminated by newlines. This allows cards to be created and edited as normal files. */ #include "i1620_defs.h" #define CD_LEN 80 extern uint8 M[MAXMEMSIZE]; extern uint8 ind[NUM_IND]; extern UNIT cpu_unit; extern int32 io_stop; char cdr_buf[CD_LEN + 2]; char cdp_buf[CD_LEN + 2]; t_stat cdr_reset (DEVICE *dptr); t_stat cdr_attach (UNIT *uptr, char *cptr); t_stat cdr_boot (int32 unitno, DEVICE *dptr); t_stat cdr_read (void); t_stat cdp_reset (DEVICE *dptr); t_stat cdp_write (uint32 len); t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump); /* Card reader data structures cdr_dev CDR descriptor cdr_unit CDR unit descriptor cdr_reg CDR register list */ UNIT cdr_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0) }; REG cdr_reg[] = { { FLDATA (LAST, ind[IN_LAST], 0) }, { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE cdr_dev = { "CDR", &cdr_unit, cdr_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cdr_reset, &cdr_boot, &cdr_attach, NULL }; /* CDP data structures cdp_dev CDP device descriptor cdp_unit CDP unit descriptor cdp_reg CDP register list */ UNIT cdp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG cdp_reg[] = { { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE cdp_dev = { "CDP", &cdp_unit, cdp_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cdp_reset, NULL, NULL, NULL }; /* Data tables. The card reader presents unusual problems. - Unique codes needed for 11-2-8 (uses !) and 12-7-8 (uses ") . - Can punch both 11 (-) and 11-0 (uses ]). On input, the nul and nl generated by C are converted to spaces; tabs and line feeds are also converted to spaces. /* Card reader (ASCII) to numeric (one digit) */ const char cdr_to_num[128] = { 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */ -1, 0x00, 0x00, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x1A, 0x0F, 0x0B, 0x1B, 0x0C, 0x00, 0x0C, /* !"#$%&' */ 0x0C, 0x0C, 0x1C, 0x00, 0x0B, 0x10, 0x1B, 0x01, /* ()*+,-./ */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */ 0x08, 0x09, 0x00, 0x1E, 0x1E, 0x0B, 0x0E, 0x1A, /* 89:;<=>? */ 0x0C, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* @ABCDEFG */ 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* HIJKLMNO */ 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* PQRSTUVW */ 0x07, 0x08, 0x09, 0x00, 0x0E, 0x10, 0x0A, 0x1F, /* XYZ[\]^_ */ -1, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* `abcdefg */ 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* hijklmno */ 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* pqrstuvw */ 0x07, 0x08, 0x09, 0x0F, 0x0A, 0x1F, 0x00, -1 /* xyz{|}~ */ }; /* Numeric (flag + digit) to card punch (ASCII) */ const char num_to_cdp[32] = { '0', '1', '2', '3', '4', '5', '6', '7', /* 0 */ '8', '9', '|', ',', ' ', '"', ' ', '"', ']', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* F + 0 */ 'Q', 'R', '!', '$', -1, -1, -1, '"' }; /* Card reader (ASCII) to alphameric (two digits) 11-2-8 (!) reads as 5A 11-7-8 (_) reads as 5F 12-2-8 (?) reads inconsistently (here 02) 12-6-8 (<) reads inconsistently (here 5E) 12-7-8 (}) reads as 5F */ const char cdr_to_alp[128] = { 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */ -1, 0x00, 0x00, -1, -1, 0x00, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x5A, 0x0F, 0x33, 0x13, 0x24, 0x10, 0x34, /* !"#$%&' */ 0x24, 0x04, 0x14, 0x10, 0x23, 0x20, 0x03, 0x21, /* ()*+,-./ */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 01234567 */ 0x78, 0x79, 0x70, 0x5E, 0x5E, 0x33, 0x0E, 0x02, /* 89:;<=>? */ 0x34, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* HIJKLMNO */ 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* PQRSTUVW */ 0x67, 0x68, 0x69, 0x40, 0x0E, 0x50, 0x0A, 0x5F, /* XYZ[\]^_ */ 0x50, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* hijklmno */ 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* pqrstuvw */ 0x67, 0x68, 0x69, 0x0F, 0x0A, 0x5F, 0x60, -1 /* xyz{|}~ */ }; /* Alphameric (two digits) to card punch (ASCII). Oddities: 02 -> 12-2-8 (?), symmetric 07 -> 12-7-8 (}), reads as 5F 12 -> 11-2-8 (!), reads as 5A 15 -> 11,0 (`), reads as 50 22 -> 0-2-8 (|), reads as 0A 32 -> 2-8 (^), reads as 0A 5B -> 11-3-8 (=), reads as 13 6A -> 0-2-8 (|), reads as 0A 6B -> 0-3-8 (,), reads as 23 AA -> 0-2-8 (|), reads as 0A There is no way to punch 0-5-8 (~), 0-6-8 (\), 11-5-8 (]), 11-6-8 (;), 11-7-8 (_), 12-5-8 ([), or 12-6-8 (<) */ const char alp_to_cdp[256] = { ' ', -1, '?', '.', ')', -1, -1, '}', /* 00 */ -1, -1, '\'', -1, -1, -1, -1, '"', '+', -1, '!', '$', '*', ']', -1, -1, /* 10 */ -1, -1, -1, -1, -1, -1, -1, -1, '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, '^', '=', '@', ':', ' ', -1, /* 30 */ -1, -1, '|', -1, -1, -1, -1, '"', -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ 'H', 'I', -1, -1, -1, -1, -1, -1, '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ 'Q', 'R', '?', '=', -1, -1, -1, '}', -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ 'Y', 'Z', '|', ',', -1, -1, -1, -1, '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ '8', '9', -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ -1, -1, '|', -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ -1, -1, -1, -1, -1, -1, -1, -1 }; /* Card reader IO routine - Hard errors stop the operation and halt the system. - Invalid characters place a blank in memory and set RDCHK. If IO stop is set, the system halts at the end of the operation. */ t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1) { int32 i; int8 cdc; t_stat r, sta; sta = SCPE_OK; /* assume ok */ switch (op) { /* case on op */ case OP_RN: /* read numeric */ r = cdr_read (); /* fill reader buf */ if (r != SCPE_OK) /* error? */ return r; for (i = 0; i < CD_LEN; i++) { /* transfer to mem */ cdc = cdr_to_num[cdr_buf[i]]; /* translate */ if (cdc < 0) { /* invalid? */ ind[IN_RDCHK] = 1; /* set read check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; cdc = 0; } M[pa] = cdc; /* store digit */ PP (pa); /* incr mem addr */ } break; case OP_RA: /* read alphameric */ r = cdr_read (); /* fill reader buf */ if (r != SCPE_OK) /* error? */ return r; for (i = 0; i < CD_LEN; i++) { /* transfer to mem */ cdc = cdr_to_alp[cdr_buf[i]]; /* translate */ if (cdc < 0) { /* invalid? */ ind[IN_RDCHK] = 1; /* set read check */ if (io_stop) /* set return status */ sta = STOP_INVCHR; cdc = 0; }; M[pa] = (M[pa] & FLAG) | (cdc & DIGIT); /* store 2 digits */ M[pa - 1] = (M[pa - 1] & FLAG) | ((cdc >> 4) & DIGIT); pa = ADDR_A (pa, 2); /* incr mem addr */ } break; default: /* invalid function */ return STOP_INVFNC; } return sta; } /* Fill card reader buffer - all errors are hard errors */ t_stat cdr_read (void) { int32 i; ind[IN_LAST] = 0; /* clear last card */ if ((cdr_unit.flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_RDCHK] = 1; /* no, error */ return SCPE_UNATT; } for (i = 0; i < CD_LEN + 2; i++) /* clear buffer */ cdr_buf[i] = ' '; fgets (cdr_buf, CD_LEN + 2, cdr_unit.fileref); /* read card */ if (feof (cdr_unit.fileref)) /* eof? */ return STOP_NOCD; if (ferror (cdr_unit.fileref)) { /* error? */ ind[IN_RDCHK] = 1; /* set read check */ perror ("CDR I/O error"); clearerr (cdr_unit.fileref); return SCPE_IOERR; } cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ getc (cdr_unit.fileref); /* see if more */ if (feof (cdr_unit.fileref)) /* eof? set last */ ind[IN_LAST] = 1; fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); /* "backspace" */ return SCPE_OK; } /* Card reader attach */ t_stat cdr_attach (UNIT *uptr, char *cptr) { ind[IN_LAST] = 0; /* clear last card */ return attach_unit (uptr, cptr); } /* Card reader reset */ t_stat cdr_reset (DEVICE *dptr) { ind[IN_LAST] = 0; /* clear last card */ return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 0 t_stat cdr_boot (int32 unitno, DEVICE *dptr) { t_stat r; int32 old_io_stop; extern int32 saved_PC; old_io_stop = io_stop; io_stop = 1; r = cdr (OP_RN, 0, 0, 0); /* read card @ 0 */ io_stop = old_io_stop; if (r != SCPE_OK) /* error? */ return r; saved_PC = BOOT_START; return SCPE_OK; } /* Card punch IO routine - Hard errors stop the operation and halt the system. - Invalid characters stop the operation and set WRCHK. If IO stop is set, the system halts. */ t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1) { int32 i; int8 cdc; uint8 z, d; switch (op) { /* decode op */ case OP_DN: return cdp_num (pa, 20000 - (pa % 20000), TRUE); /* dump numeric */ case OP_WN: return cdp_num (pa, CD_LEN, FALSE); /* write numeric */ case OP_WA: for (i = 0; i < CD_LEN; i++) { /* one card */ d = M[pa] & DIGIT; /* get digit pair */ z = M[pa - 1] & DIGIT; cdc = alp_to_cdp[(z << 4) | d]; /* translate */ if (cdc < 0) { /* bad char? */ ind[IN_WRCHK] = 1; /* set write check */ CRETIOE (io_stop, STOP_INVCHR); } cdp_buf[i] = cdc; /* store in buf */ pa = ADDR_A (pa, 2); /* incr mem addr */ } return cdp_write (CD_LEN); /* punch buffer */ default: /* invalid function */ break; } return STOP_INVFNC; } /* Punch card numeric */ t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump) { int32 i, ncd, len; uint8 d; int8 cdc; t_stat r; ncd = ndig / CD_LEN; /* number of cards */ while (ncd-- >= 0) { /* until done */ len = (ncd >= 0)? CD_LEN: (ndig % CD_LEN); /* card length */ if (len == 0) break; for (i = 0; i < len; i++) { /* one card */ d = M[pa] & (FLAG | DIGIT); /* get char */ if (dump && (d == FLAG)) cdc = '-'; /* dump? F+0 is diff */ else cdc = num_to_cdp[d]; /* translate */ if (cdc < 0) { /* bad char? */ ind[IN_WRCHK] = 1; /* set write check */ CRETIOE (io_stop, STOP_INVCHR); /* stop */ } cdp_buf[i] = cdc; /* store in buf */ PP (pa); /* incr mem addr */ } r = cdp_write (len); /* punch card */ if (r != SCPE_OK) /* error? */ return r; } return SCPE_OK; } /* Write punch card buffer - all errors are hard errors */ t_stat cdp_write (uint32 len) { if ((cdp_unit.flags & UNIT_ATT) == 0) { /* attached? */ ind[IN_WRCHK] = 1; /* no, error */ return SCPE_UNATT; } while ((len > 0) && (cdp_buf[len - 1] == ' ')) /* trim spaces */ --len; cdp_buf[len] = '\n'; /* newline, null */ cdp_buf[len + 1] = 0; fputs (cdp_buf, cdp_unit.fileref); /* write card */ cdp_unit.pos = ftell (cdp_unit.fileref); /* count char */ if (ferror (cdp_unit.fileref)) { /* error? */ ind[IN_WRCHK] = 1; perror ("CDP I/O error"); clearerr (cdp_unit.fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset card punch */ t_stat cdp_reset (DEVICE *dptr) { return SCPE_OK; } simh-3.8.1/sim_ether.h0000644000175000017500000002341011004401144012756 0ustar vlmvlm/* sim_ether.h: OS-dependent network information ------------------------------------------------------------------------------ Copyright (c) 2002-2005, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ Modification history: 30-Nov-05 DTH Added CRC length to packet and more field comments 04-Feb-04 DTH Added debugging information 14-Jan-04 MP Generalized BSD support issues 05-Jan-04 DTH Added eth_mac_scan 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq 23-Dec-03 DTH Added status to packet 01-Dec-03 DTH Added reflections, tweaked decnet fix items 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. 05-Jun-03 DTH Added used to struct eth_packet 01-Feb-03 MP Changed some uint8 strings to char* to reflect usage 22-Oct-02 DTH Added all_multicast and promiscuous support 21-Oct-02 DTH Corrected copyright again 16-Oct-02 DTH Fixed copyright 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 15-Aug-02 DTH Started XQ simulation ------------------------------------------------------------------------------ */ #ifndef _SIM_ETHER_H #define _SIM_ETHER_H #include "sim_defs.h" /* make common BSD code a bit easier to read in this file */ /* OS/X seems to define and compile using one of these BSD types */ #if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) #define xBSD 1 #endif #if !defined(__FreeBSD__) && !defined(_WIN32) && !defined(VMS) #define USE_SETNONBLOCK 1 #endif #if defined(__sun__) && defined(__i386__) #define USE_READER_THREAD 1 #endif /* make common winpcap code a bit easier to read in this file */ #if defined(_WIN32) || defined(VMS) #define PCAP_READ_TIMEOUT -1 #else #define PCAP_READ_TIMEOUT 1 #endif /* set related values to have correct relationships */ #if defined (USE_READER_THREAD) #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK #endif #undef PCAP_READ_TIMEOUT #define PCAP_READ_TIMEOUT 15 #if !defined (xBSD) && !defined(_WIN32) && !defined(VMS) #define MUST_DO_SELECT #endif #endif /* USE_BPF is defined to let this code leverage the libpcap/OS kernel provided BPF packet filtering. This generally will enhance performance. It may not be available in some environments and/or it may not work correctly, so undefining this will still provide working code here. */ #define USE_BPF 1 #if defined (USE_READER_THREAD) #include #endif /* structure declarations */ #define ETH_PROMISC 1 /* promiscuous mode = true */ #define ETH_TIMEOUT -1 /* read timeout in milliseconds (immediate) */ #define ETH_FILTER_MAX 20 /* maximum address filters */ #define ETH_DEV_NAME_MAX 256 /* maximum device name size */ #define ETH_DEV_DESC_MAX 256 /* maximum device description size */ #define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ #define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ #define ETH_MAX_DEVICE 10 /* maximum ethernet devices */ #define ETH_CRC_SIZE 4 /* ethernet CRC size */ #define ETH_FRAME_SIZE 1518 /* ethernet maximum frame size */ #define DECNET_SELF_FRAME(dnet_mac, msg) \ ((memcmp(dnet_mac, msg , 6) == 0) && \ (memcmp(dnet_mac, msg+6, 6) == 0)) struct eth_packet { uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ int len; /* packet length without CRC */ int used; /* bytes processed (used in packet chaining) */ int status; /* transmit/receive status */ int crc_len; /* packet length with CRC */ }; struct eth_item { int type; /* receive (0=setup, 1=loopback, 2=normal) */ struct eth_packet packet; }; struct eth_queue { int max; int count; int head; int tail; int loss; int high; struct eth_item* item; }; struct eth_list { int num; char name[ETH_DEV_NAME_MAX]; char desc[ETH_DEV_DESC_MAX]; }; typedef int ETH_BOOL; typedef unsigned char ETH_MAC[6]; typedef struct eth_packet ETH_PACK; typedef void (*ETH_PCALLBACK)(int status); typedef struct eth_list ETH_LIST; typedef struct eth_queue ETH_QUE; typedef struct eth_item ETH_ITEM; struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ ETH_PACK* write_packet; /* write packet */ ETH_MAC filter_address[ETH_FILTER_MAX]; /* filtering addresses */ int addr_count; /* count of filtering addresses */ ETH_BOOL promiscuous; /* promiscuous mode flag */ ETH_BOOL all_multicast; /* receive all multicast messages */ int32 decnet_self_sent; /* loopback packets sent but not seen */ ETH_MAC decnet_addr; /* decnet address of interface */ DEVICE* dptr; /* device ethernet is attached to */ uint32 dbit; /* debugging bit */ int reflections; /* packet reflections on interface */ int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ #if defined (USE_READER_THREAD) ETH_QUE read_queue; pthread_mutex_t lock; pthread_t reader_thread; /* Reader Thread Id */ #endif }; typedef struct eth_device ETH_DEV; /* prototype declarations*/ t_stat eth_open (ETH_DEV* dev, char* name, /* open ethernet interface */ DEVICE* dptr, uint32 dbit); t_stat eth_close (ETH_DEV* dev); /* close ethernet interface */ t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, /* write sychronous packet; */ ETH_PCALLBACK routine); /* callback when done */ t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ ETH_PCALLBACK routine); /* callback when done*/ t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incoming packets */ ETH_MAC* addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous); int eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ void eth_setcrc (ETH_DEV* dev, int need_crc); /* enable/disable CRC mode */ void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet */ t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices */ int32 val, void* desc); void eth_mac_fmt (ETH_MAC* add, char* buffer); /* format ethernet mac address */ t_stat eth_mac_scan (ETH_MAC* mac, char* strmac); /* scan string for mac, put in mac */ t_stat ethq_init (ETH_QUE* que, int max); /* initialize FIFO queue */ void ethq_clear (ETH_QUE* que); /* clear FIFO queue */ void ethq_remove (ETH_QUE* que); /* remove item from FIFO queue */ void ethq_insert (ETH_QUE* que, int32 type, /* insert item into FIFO queue */ ETH_PACK* packet, int32 status); #endif /* _SIM_ETHER_H */ simh-3.8.1/I7094/0000755000175000017500000000000011112035160011343 5ustar vlmvlmsimh-3.8.1/I7094/i7094_defs.h0000644000175000017500000005343511111660154013311 0ustar vlmvlm/* i7094_defs.h: IBM 7094 simulator definitions Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This simulator incorporates prior work by Paul Pierce, Dave Pitts, and Rob Storey. Tom Van Vleck, Stan Dunten, Jerry Saltzer, and other CTSS veterans helped to reconstruct the CTSS hardware RPQ's. Dave Pitts gets special thanks for patiently coaching me through IBSYS debug. */ #ifndef _I7094_DEFS_H_ #define _I7094_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_HALT 1 /* halted */ #define STOP_IBKPT 2 /* breakpoint */ #define STOP_ILLEG 3 /* illegal instr */ #define STOP_DIVCHK 4 /* divide check */ #define STOP_XEC 5 /* XCT loop */ #define STOP_ASTOP 6 /* address stop */ #define STOP_NXCHN 7 /* nx channel */ #define STOP_7909 8 /* ill inst to 7909 */ #define STOP_NT7909 9 /* ill inst to !7909 */ #define STOP_NXDEV 10 /* nx device */ #define STOP_ILLCHI 11 /* illegal channel op */ #define STOP_WRP 12 /* write protect */ #define STOP_ILLIOP 13 /* illegal I/O op */ #define STOP_INVFMT 14 /* invalid disk format */ #define STOP_NOIFREE 15 /* 7750: no buf for inp */ #define STOP_NOOFREE 16 /* 7750: no buf for out */ #define STOP_INVLIN 17 /* 7750: invalid line# */ #define STOP_INVMSG 18 /* 7750: invalid message */ #define STOP_CHBKPT 19 /* channel breakpoint */ /* Simulator error codes */ #define ERR_STALL 40 /* stall */ #define ERR_ENDRC 41 /* end rec */ #define ERR_NRCF 42 /* no record found */ /* Instruction history - flags in left half of pc entry */ #define HIST_PC 0x04000000 /* CPU */ #define HIST_V_CH 28 /* chan + 1 */ #define HIST_M_CH 0xF #define HIST_CH(x) (((x) >> HIST_V_CH) & HIST_M_CH) typedef struct { uint32 pc; uint32 ea; uint32 rpt; t_uint64 ir; t_uint64 ac; t_uint64 mq; t_uint64 si; t_uint64 opnd; } InstHistory; /* Architectural constants */ #define A704_SIZE 14 /* addr width, 704 mode */ #define ASIZE 15 /* inst addr width */ #define PASIZE 16 /* phys addr width */ #define STDMEMSIZE (1u << ASIZE) /* standard memory */ #define MAXMEMSIZE (1u << PASIZE) /* maximum memory */ #define A704_MASK ((1u << A704_SIZE) - 1) #define PAMASK ((1u << PASIZE) - 1) #define MEMSIZE (cpu_unit.capac) #define BCORE_V (ASIZE) /* (CTSS) A/B core sel */ #define BCORE_BASE (1u << BCORE_V) /* (CTSS) B core base */ /* Traps */ #define TRAP_STD_SAV 000000 /* trap save location */ #define TRAP_TRA_PC 000001 /* trap PC: transfer */ #define TRAP_STR_PC 000002 /* trap PC: STR */ #define TRAP_FP_PC 000010 /* trap PC: flt point */ #define TRAP_PROT_SAV 000032 /* protection trap save */ #define TRAP_PROT_PC 000033 /* protection trap PC */ #define TRAP_704_SAV 040000 /* 704 compat trap */ #define TRAP_SEL_PC 040001 /* 704 trap PC: select */ #define TRAP_CPY_PC 040002 /* 704 trap PC: copy */ #define TRAP_F_MQ 000001 /* MQ error */ #define TRAP_F_AC 000002 /* AC error */ #define TRAP_F_OVF 000004 /* overflow */ #define TRAP_F_SGL 000010 /* single precision */ #define TRAP_F_DVC 000020 /* fake: divide check */ #define TRAP_F_ODD 000040 /* odd address */ #define TRAP_F_BDATA 020000 /* (CTSS) data B core */ #define TRAP_F_BINST 040000 /* (CTSS) inst B core */ /* Integer */ #define DMASK 0777777777777 /* data mask */ #define SIGN 0400000000000 /* sign */ #define MMASK 0377777777777 /* magnitude mask */ #define LMASK 0777777000000 /* left mask */ #define RMASK 0000000777777 /* right mask */ #define PMASK 0700000000000 /* prefix */ #define XMASK 0077777000000 /* decrement */ #define TMASK 0000000700000 /* tag */ #define AMASK 0000000077777 /* address */ #define SCMASK 0000000000377 /* shift count mask */ #define B1 0200000000000 /* bit 1 */ #define B9 0000400000000 /* bit 9 */ /* Accumulator is actually 38b wide */ #define AC_S 02000000000000 /* sign */ #define AC_Q 01000000000000 /* Q */ #define AC_P 00400000000000 /* P */ #define AC_MMASK 01777777777777 /* Q+P+magnitude */ /* Floating point */ #define FP_N_FR 27 /* fraction bits */ #define FP_FMASK ((1u << FP_N_FR) - 1) #define FP_N_DFR 54 /* double fraction bits */ #define FP_DFMASK ((((t_uint64) 1) << FP_N_DFR) - 1) #define FP_FNORM (((t_uint64) 1u) << (FP_N_DFR - 1)) /* normalized bit */ #define FP_FCRY (((t_uint64) 1u) << FP_N_DFR) /* fraction carry */ #define FP_BIAS 0200 /* exponent bias */ #define FP_V_CH (FP_N_FR) /* exponent */ #define FP_M_CH 0377 /* SR char mask */ #define FP_M_ACCH 01777 /* AC char mask incl Q,P */ /* Instruction format */ #define INST_T_DEC 0300000000000 /* if nz, takes decr */ #define INST_T_CXR1 0000000100000 /* if nz, update XR1 */ #define INST_V_OPD 33 /* decrement opcode */ #define INST_M_OPD 07 #define INST_V_DEC 18 /* decrement */ #define INST_M_DEC 077777 #define INST_V_OPC 24 /* normal opcode */ #define INST_M_OPC 0777 #define INST_V_IND 22 /* indirect */ #define INST_IND (3 << INST_V_IND) #define INST_V_CCNT 18 /* convert count */ #define INST_M_CCNT 0377 #define INST_V_VCNT 18 /* vlm/vdh count */ #define INST_M_VCNT 077 #define INST_V_TAG 15 /* index */ #define INST_M_TAG 07 #define INST_V_ADDR 0 #define INST_M_ADDR 077777 #define GET_OPD(x) ((uint32) (((x) >> INST_V_OPD) & INST_M_OPD)) #define GET_DEC(x) ((uint32) (((x) >> INST_V_DEC) & INST_M_DEC)) #define GET_OPC(x) (((uint32) (((x) >> INST_V_OPC) & INST_M_OPC)) | \ (((x) & SIGN)? 01000: 0)) #define TST_IND(x) (((x) & INST_IND) == INST_IND) #define GET_CCNT(x) ((uint32) (((x) >> INST_V_CCNT) & INST_M_CCNT)) #define GET_VCNT(x) ((uint32) (((x) >> INST_V_VCNT) & INST_M_VCNT)) #define GET_TAG(x) ((uint32) (((x) >> INST_V_TAG) & INST_M_TAG)) /* Instruction decode flags */ #define I_4X 0x01 /* 7040, 7044 */ #define I_9X 0x02 /* 7090, 7094, CTSS */ #define I_94 0x04 /* 7094, CTSS */ #define I_CT 0x08 /* CTSS */ #define I_MODEL 0x0F /* option mask */ #define I_X 0x10 /* indexed */ #define I_N 0x20 /* indirect */ #define I_R 0x40 /* read */ #define I_D 0x80 /* double read */ #define I_XN (I_X|I_N) #define I_XNR (I_X|I_N|I_R) #define I_XND (I_X|I_N|I_D) /* Memory protection (CTSS) */ #define VA_V_OFF 0 /* offset in block */ #define VA_N_OFF 8 /* width of offset */ #define VA_M_OFF ((1u << VA_N_OFF) - 1) #define VA_OFF (VA_M_OFF << VA_V_OFF) #define VA_V_BLK (VA_N_OFF) /* block */ #define VA_N_BLK (ASIZE - VA_N_OFF) /* width of block */ #define VA_M_BLK ((1u << VA_N_BLK) - 1) #define VA_BLK (VA_M_BLK << VA_V_BLK) /* Unsigned operations */ #define NEG(x) (~(x) + 1) #define BIT_TST(w,b) (((w) >> (b)) & 1) /* Device information block */ typedef struct { t_stat (*chsel)(uint32 ch, uint32 sel, uint32 u); t_stat (*write)(uint32 ch, t_uint64 val, uint32 flags); } DIB; /* BCD digits */ #define BCD_MASK 017 #define BCD_ZERO 012 #define BCD_ONE 001 #define BCD_TWO 002 #define BCD_AT 014 /* Channels */ #define NUM_CHAN 8 /* # channels */ #define CH_A 0 /* channel A */ #define CH_B 1 #define CH_C 2 #define CH_D 3 #define CH_E 4 #define CH_F 5 #define CH_G 6 #define CH_H 7 #define REQ_CH(x) (1u << (x)) /* All channel commands */ #define CHI_IND 0000000400000 /* ch inst indirect */ /* Channel selects - all channels */ #define CHSL_RDS 0001 /* data selects */ #define CHSL_WRS 0002 #define CHSL_SNS 0003 #define CHSL_CTL 0004 #define CHSL_FMT 0005 #define CHSL_WEF 0010 /* non-data selects */ #define CHSL_WBT 0011 /* 704X only */ #define CHSL_BSR 0012 #define CHSL_BSF 0013 #define CHSL_REW 0014 #define CHSL_RUN 0015 #define CHSL_SDN 0016 #define CHSL_2ND 0020 /* second state */ #define CHSL_3RD 0040 /* etc */ #define CHSL_4TH 0060 #define CHSL_5TH 0100 #define CHSL_NDS 0010 /* non-data sel flag */ #define CHSL_NUM 16 /* Channel commands - 7607/7289 - S12'19 */ #define CH6I_NST 0000000200000 /* ch inst no store */ #define CH6_IOCD 000 #define CH6_TCH 002 #define CH6_IORP 004 #define CH6_IORT 006 #define CH6_IOCP 010 #define CH6_IOCT 012 #define CH6_IOSP 014 #define CH6_IOST 016 #define CH6_OPMASK 016 /* without nostore */ #define TCH_LIMIT 5 /* TCH autoresolve limit */ /* Channel data flags - 7607 */ #define CH6DF_EOR 1 /* end of record */ #define CH6DF_VLD 2 /* input valid */ /* Channel commands - 7909 - S123'19 */ #define CH9_WTR 000 #define CH9_XMT 001 #define CH9_TCH 004 #define CH9_LIPT 005 #define CH9_CTL 010 #define CH9_CTLR 011 #define CH9_CTLW 012 #define CH9_SNS 013 #define CH9_LAR 014 #define CH9_SAR 015 #define CH9_TWT 016 #define CH9_CPYP 020 #define CH9_CPYD 024 #define CH9_TCM 025 #define CH9_LIP 031 #define CH9_TDC 032 #define CH9_LCC 033 #define CH9_SMS 034 #define CH9_ICC 035 #define CH9_ICCA 037 /* ignores bit <3> */ #define CH9_OPMASK 037 /* Channel data flags - 7909 */ #define CH9DF_STOP 1 /* stop */ #define CH9DF_VLD 2 /* input valid */ /* Extended parts of the command come from the decrement, stored in ch_wc */ #define CH9D_V_MASK 0 /* condition mask */ #define CH9D_M_MASK 077 #define CH9D_V_COND 12 /* condition select */ #define CH9D_M_COND 07 #define CH9D_MASK(x) (((x) >> CH9D_V_MASK) & CH9D_M_MASK) #define CH9D_COND(x) (((x) >> CH9D_V_COND) & CH9D_M_COND) #define CH9D_NST 020000 /* no store */ #define CH9D_B11 000100 /* Or from the effective address, stored in ch_ca */ #define CH9A_V_LCC 0 /* counter */ #define CH9A_M_LCC 077 #define CH9A_V_SMS 0 /* system mask */ #define CH9A_M_SMS 0177 #define CH9A_LCC(x) (((x) >> CH9A_V_LCC) & CH9A_M_LCC) #define CH9A_SMS(x) (((x) >> CH9A_V_SMS) & CH9A_M_SMS) /* Channel states - common */ #define CHXS_IDLE 0 /* idle */ #define CHXS_DSX 1 /* executing */ /* Channel states - 7607/7289 */ #define CH6S_PNDS 2 /* polling NDS */ #define CH6S_PDS 3 /* polling DS */ #define CH6S_NDS 4 /* nds, executing */ #define CH6S_DSW 5 /* ds, chan wait */ /* Channel traps - 7909 has only CMD (== TWT) */ #define CHTR_V_CME 0 /* cmd/eof enable */ #define CHTR_V_CLK 17 /* clock */ #define CHTR_V_TRC 18 /* tape check */ #define CHTR_V_TWT (CHTR_V_CME) #define CHTR_CLK_SAV 006 /* clock */ #define CHTR_CHA_SAV 012 /* start of chan block */ #define CHTR_F_CMD 1 /* CMD flag (in decr) */ #define CHTR_F_TRC 2 /* TRC flag (in decr) */ #define CHTR_F_EOF 4 /* EOF flag (in decr) */ /* Channel interrupts - 7909 only */ #define CHINT_CHA_SAV 042 /* start of chan block */ /* Channel interrupt conditions - 7909 only */ #define CHINT_ADPC 001 /* adapter check */ #define CHINT_ATN2 002 /* attention 2 - ni */ #define CHINT_ATN1 004 /* attention 1 */ #define CHINT_UEND 010 /* unusual end */ #define CHINT_SEQC 020 /* sequence check */ #define CHINT_IOC 040 /* IO check */ /* Channel SMS flags - 7909 only */ #define CHSMS_SEL2 0001 /* select 2nd - ni */ #define CHSMS_IATN2 0002 /* inhibit atn2 - ni */ #define CHSMS_IATN1 0004 /* inhibit atn1 */ #define CHSMS_IUEND 0010 /* inhibit uend */ #define CHSMS_BCD 0020 /* BCD conversion - ni */ #define CHSMS_RBCK 0040 /* read backwards - ni */ #define CHSMS_ENCI 0100 /* enable noncon - ni */ /* Channel flags (7607 in right half, 7909 in left half) */ #define CHF_CMD 00000000001 /* cmd done */ #define CHF_TWT (CHF_CMD) #define CHF_TRC 00000000002 /* tape check */ #define CHF_EOF 00000000004 /* end of file */ #define CHF_BOT 00000000010 /* beginning of tape */ #define CHF_EOT 00000000020 /* end of tape */ #define CHF_LDW 00000000040 /* LCH waiting */ #define CHF_EOR 00000000100 /* end of record */ #define CHF_IRQ 00001000000 /* intr request */ #define CHF_INT 00002000000 /* intr in prog */ #define CHF_WRS 00004000000 /* write */ #define CHF_RDS 00010000000 /* read */ #define CHF_PWR 00020000000 /* prepare to write */ #define CHF_PRD 00040000000 /* prepare to read */ #define CHF_V_COND 24 /* cond register */ #define CHF_M_COND 077 #define CHF_ADPC (CHINT_ADPC << CHF_V_COND) /* adapter check */ #define CHF_ATN2 (CHINT_ATN2 << CHF_V_COND) /* attention 2 */ #define CHF_ATN1 (CHINT_ATN1 << CHF_V_COND) /* attention 1 */ #define CHF_UEND (CHINT_UEND << CHF_V_COND) /* unusual end */ #define CHF_SEQC (CHINT_SEQC << CHF_V_COND) /* sequence check */ #define CHF_IOC (CHINT_IOC << CHF_V_COND) /* IO check */ #define CHF_V_LCC 30 /* loop ctrl counter */ #define CHF_M_LCC 077 #define CHF_CLR_7909 07775000177 /* 7909 clear flags */ #define CHF_SDC_7909 07776000000 /* 7909 SDC flags */ /* Channel characteristics (in dev.flags) */ #define DEV_7909 (1u << (DEV_V_UF + 0)) #define DEV_7289 (1u << (DEV_V_UF + 1)) #define DEV_CDLP (1u << (DEV_V_UF + 2)) #define DEV_7750 (1u << (DEV_V_UF + 3)) #define DEV_7631 (1u << (DEV_V_UF + 4)) /* Unit addresses - 7607/7289 only */ #define U_V_CH 9 /* channel number */ #define U_M_CH 077 #define U_V_UNIT 0 #define U_M_UNIT 0777 #define GET_U_CH(x) (((((uint32) (x)) >> U_V_CH) & U_M_CH) - 1) #define GET_U_UNIT(x) ((((uint32) (x)) >> U_V_UNIT) & U_M_UNIT) #define U_MTBCD 0201 /* BCD tape */ #define U_MTBIN 0221 /* binary tape */ #define U_CDR 0321 /* card reader */ #define U_CDP 0341 /* card punch */ #define U_LPBCD 0361 /* BCD print */ #define U_LPBIN 0362 /* binary print */ #define U_DRM 0330 /* 7320A drum */ #define MT_NUMDR 10 /* CTSS Chronolog clock */ #define CHRONO_CH (CH_A) /* channel A */ #define CHRONO_UNIT (7) /* unit 7 */ /* Interval timer */ #define CLK_CTR 05 /* counter */ #define CLK_TPS 60 /* 60Hz */ #define TMR_CLK 0 /* use timer 0 */ #define TMR_COM 1 /* 7750 timer */ /* Function prototypes and macros */ #define ReadP(p) M[p] #define WriteP(p,d) M[p] = d void cpu_ent_hist (uint32 pc, uint32 ea, t_uint64 ir, t_uint64 opnd); t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat ch6_end_nds (uint32 ch); uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags); t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 flags); t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 flags); t_stat ch6_req_wr (uint32 ch, uint32 unit); t_bool ch6_qconn (uint32 ch, uint32 unit); t_stat ch9_req_rd (uint32 ch, t_uint64 val); void ch9_set_atn (uint32 ch); void ch9_set_ioc (uint32 ch); void ch9_set_end (uint32 ch, uint32 ireq); t_bool ch9_qconn (uint32 ch); void ch_set_map (void); t_bool ch_qidle (void); #endif simh-3.8.1/I7094/i7094_cd.c0000644000175000017500000004661411112033436012750 0ustar vlmvlm/* i7094_cd.c: IBM 711/721 card reader/punch Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cdr 711 card reader cdp 721 card punch 19-Jan-07 RMS Added UNIT_TEXT 13-Jul-06 RMS Fixed problem with 80 column full cards Cards are represented as ASCII text streams terminated by newlines. This allows cards to be created and edited as normal files. Two formats are supported: column binary each character represents 6b of a 12b column text each character represents all 12b of a column Internally, the 7094 works only with column binary and is limited to 72 columns of data. Each row of the card is represented by 72b of data (two 36b words). A complete card image consists of 12 rows (24 36b words). */ #include "i7094_defs.h" #define CD_BINLNT 24 /* bin buf length */ #define CD_CHRLNT 80 /* char buf length */ #define CDS_INIT 0 /* card in motion */ #define CDS_DATA 1 /* data transfer */ #define CDS_END 2 /* card complete */ #define UNIT_V_CBN (UNIT_V_UF + 0) /* column binary file */ #define UNIT_V_PCA (UNIT_V_UF + 1) /* A vs H punch flag */ #define UNIT_CBN (1 << UNIT_V_CBN) #define UNIT_PCA (1 << UNIT_V_PCA) uint32 cdr_sta = 0; /* state */ uint32 cdr_bptr = 0; /* buffer ptr */ uint32 cdr_tstart = 27500; /* timing */ uint32 cdr_tstop = 27500; uint32 cdr_tleft = 150; uint32 cdr_tright = 4000; t_uint64 cdr_bbuf[CD_BINLNT]; /* col binary buf */ uint32 cdp_sta = 0; /* state */ uint32 cdp_bptr = 0; /* buffer ptr */ uint32 cdp_tstart = 35000; /* timing */ uint32 cdp_tstop = 35000; uint32 cdp_tleft = 150; uint32 cdp_tright = 15500; t_uint64 cdp_chob = 0; uint32 cdp_chob_v = 0; t_uint64 cdp_bbuf[CD_BINLNT]; /* col binary buf */ t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat cdr_reset (DEVICE *dptr); t_stat cdr_svc (UNIT *uptr); t_stat cdr_boot (int32 unitno, DEVICE *dptr); t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 flags); t_stat cdp_reset (DEVICE *dptr); t_stat cdp_svc (UNIT *uptr); t_stat cdp_card_end (UNIT *uptr); t_stat cd_attach (UNIT *uptr, char *cptr); t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); char colbin_to_bcd (uint32 cb); extern uint32 sim_switches; extern uint32 PC; extern uint32 ind_ioc; extern char bcd_to_ascii_a[64]; extern char bcd_to_ascii_h[64]; extern uint32 bcd_to_colbin[64]; extern char ascii_to_bcd[128]; extern t_uint64 bit_masks[36]; extern uint32 col_masks[12]; /* Card reader data structures cdr_dev CDR descriptor cdr_unit CDR unit descriptor cdr_reg CDR register list */ DIB cdr_dib = { &cdr_chsel, NULL }; UNIT cdr_unit = { UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0) }; REG cdr_reg[] = { { ORDATA (STATE, cdr_sta, 2) }, { DRDATA (BPTR, cdr_bptr, 5), PV_LEFT }, { BRDATA (BUF, cdr_bbuf, 8, 36, CD_BINLNT) }, { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TSTART, cdr_tstart, 24), PV_LEFT + REG_NZ }, { DRDATA (TSTOP, cdr_tstop, 24), PV_LEFT + REG_NZ }, { DRDATA (TLEFT, cdr_tleft, 24), PV_LEFT + REG_NZ }, { DRDATA (TRIGHT, cdr_tright, 24), PV_LEFT + REG_NZ }, { NULL } }; MTAB cdr_mod[] = { { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode }, { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode }, { 0 } }; DEVICE cdr_dev = { "CDR", &cdr_unit, cdr_reg, cdr_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &cdr_reset, &cdr_boot, &cd_attach, NULL, &cdr_dib, DEV_DISABLE }; /* CDP data structures cdp_dev CDP device descriptor cdp_unit CDP unit descriptor cdp_reg CDP register list */ DIB cdp_dib = { &cdp_chsel, &cdp_chwr }; UNIT cdp_unit = { UDATA (&cdp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; REG cdp_reg[] = { { ORDATA (STATE, cdp_sta, 2) }, { ORDATA (CHOB, cdp_chob, 36) }, { FLDATA (CHOBV, cdp_chob_v, 0) }, { DRDATA (BPTR, cdp_bptr, 5), PV_LEFT }, { BRDATA (BUF, cdp_bbuf, 8, 36, CD_BINLNT) }, { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TSTART, cdp_tstart, 24), PV_LEFT + REG_NZ }, { DRDATA (TSTOP, cdp_tstop, 24), PV_LEFT + REG_NZ }, { DRDATA (TLEFT, cdp_tleft, 24), PV_LEFT + REG_NZ }, { DRDATA (TRIGHT, cdp_tright, 24), PV_LEFT + REG_NZ }, { NULL } }; MTAB cdp_mod[] = { { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode }, { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode }, { UNIT_PCA, UNIT_PCA, "business set", "BUSINESS", NULL }, { UNIT_PCA, 0, "Fortran set", "FORTRAN", NULL }, { 0 } }; DEVICE cdp_dev = { "CDP", &cdp_unit, cdp_reg, cdp_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &cdp_reset, NULL, &cd_attach, NULL, &cdp_dib, DEV_DISABLE }; /* Card reader select */ t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit) { if (sel & CHSL_NDS) /* nds? nop */ return ch6_end_nds (ch); switch (sel) { /* case on data sel */ case CHSL_RDS: /* read */ if ((cdr_unit.flags & UNIT_ATT) == 0) /* not attached? */ return SCPE_UNATT; if (sim_is_active (&cdr_unit)) /* busy? */ return ERR_STALL; cdr_sta = CDS_INIT; /* initial state */ sim_activate (&cdr_unit, cdp_tstart); /* start reader */ break; default: /* other */ return STOP_ILLIOP; /* not allowed */ } return SCPE_OK; } /* Unit timeout */ t_stat cdr_svc (UNIT *uptr) { uint32 i, col, row, bufw, colbin; char cdr_cbuf[(2 * CD_CHRLNT) + 2]; t_uint64 dat = 0; if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return SCPE_UNATT; switch (cdr_sta) { /* case on state */ case CDS_INIT: /* initial state */ for (i = 0; i < CD_BINLNT; i++) /* clear bin buf */ cdr_bbuf[i] = 0; for (i = 0; i < ((2 * CD_CHRLNT) + 2); i++) /* clear char buf */ cdr_cbuf[i] = ' '; cdr_sta = CDS_DATA; /* data state */ cdr_bptr = 0; /* init buf ptr */ fgets (cdr_cbuf, (uptr->flags & UNIT_CBN)? (2 * CD_CHRLNT) + 2: CD_CHRLNT + 2, uptr->fileref); /* read card */ if (feof (uptr->fileref)) /* eof? */ return ch6_err_disc (CH_A, U_CDR, CHF_EOF); /* set EOF, disc */ if (ferror (uptr->fileref)) { /* error? */ perror ("CDR I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; /* stop */ } uptr->pos = ftell (uptr->fileref); /* update position */ for (i = 0; i < (2 * CD_CHRLNT); i++) /* convert to BCD */ cdr_cbuf[i] = ascii_to_bcd[cdr_cbuf[i] & 0177] & 077; for (col = 0; col < 72; col++) { /* process 72 columns */ if (uptr->flags & UNIT_CBN) /* column binary? */ colbin = (((uint32) cdr_cbuf[2 * col]) << 6) | ((uint32) cdr_cbuf[(2 * col) + 1]); /* 2 chars -> col bin */ else colbin = bcd_to_colbin[cdr_cbuf[col]]; /* cvt to col binary */ dat = bit_masks[35 - (col % 36)]; /* mask for column */ for (row = 0; row < 12; row++) { /* rows 9..0, 11, 12 */ bufw = (row * 2) + (col / 36); /* index to buffer */ if (colbin & col_masks[row]) /* row bit set? */ cdr_bbuf[bufw] |= dat; } } case CDS_DATA: /* data state */ dat = cdr_bbuf[cdr_bptr++]; /* get next word */ if (cdr_bptr >= CD_BINLNT) { /* last word? */ cdr_sta = CDS_END; /* end state */ ch6_req_rd (CH_A, U_CDR, dat, CH6DF_EOR); /* req chan, dat, EOR */ sim_activate (uptr, cdr_tstop); } else { ch6_req_rd (CH_A, U_CDR, dat, 0); /* req chan, dat */ sim_activate (uptr, (cdr_bptr & 1)? cdr_tleft: cdr_tright); } break; case CDS_END: /* end state */ if (ch6_qconn (CH_A, U_CDR)) { /* if cdr still conn */ cdr_sta = CDS_INIT; /* return to init */ sim_activate (uptr, 1); /* next card */ } break; } return SCPE_OK; } /* Card reader reset */ t_stat cdr_reset (DEVICE *dptr) { uint32 i; for (i = 0; i < CD_BINLNT; i++) /* clear buffer */ cdr_bbuf[i] = 0; cdr_sta = 0; /* clear state */ cdr_bptr = 0; /* clear buf ptr */ sim_cancel (&cdr_unit); /* stop reader */ return SCPE_OK; } /* Card reader bootstrap */ #define BOOT_START 01000 #define BOOT_SIZE (sizeof (boot_rom) / sizeof (t_uint64)) static const t_uint64 boot_rom[] = { 00762000001000 + U_CDR, /* RDSA CDR */ 00544000000000 + BOOT_START + 4, /* LCHA *+3 */ 00544000000000, /* LCHA 0 */ 00021000000001, /* TTR 1 */ 05000030000000, /* IOCT 3,,0 */ }; t_stat cdr_boot (int32 unitno, DEVICE *dptr) { uint32 i; extern t_uint64 *M; for (i = 0; i < BOOT_SIZE; i++) WriteP (BOOT_START + i, boot_rom[i]); PC = BOOT_START; return SCPE_OK; } /* Reader/punch attach */ t_stat cd_attach (UNIT *uptr, char *cptr) { t_stat r; r = attach_unit (uptr, cptr); if (r != SCPE_OK) /* attach */ return r; if (sim_switches & SWMASK ('T')) /* text? */ uptr->flags = uptr->flags & ~UNIT_CBN; else if (sim_switches & SWMASK ('C')) /* column binary? */ uptr->flags = uptr->flags | UNIT_CBN; else if (match_ext (cptr, "TXT")) /* .txt? */ uptr->flags = uptr->flags & ~UNIT_CBN; else if (match_ext (cptr, "CBN")) /* .cbn? */ uptr->flags = uptr->flags | UNIT_CBN; return SCPE_OK; } /* Reader/punch set mode - valid only if not attached */ t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { return (uptr->flags & UNIT_ATT)? SCPE_NOFNC: SCPE_OK; } /* Card punch select */ t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit) { if (sel & CHSL_NDS) /* nds? nop */ return ch6_end_nds (ch); switch (sel) { /* case on cmd */ case CHSL_WRS: /* write */ if ((cdp_unit.flags & UNIT_ATT) == 0) /* not attached? */ return SCPE_UNATT; if (sim_is_active (&cdp_unit)) /* busy? */ return ERR_STALL; cdp_sta = CDS_INIT; /* initial state */ sim_activate (&cdp_unit, cdp_tstart); /* start punch */ break; default: /* other */ return STOP_ILLIOP; /* not allowed */ } return SCPE_OK; } /* Channel write routine - write word to buffer, write card when full */ t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 eorfl) { cdp_chob = val & DMASK; /* store data */ cdp_chob_v = 1; /* buffer valid */ if (cdp_sta == CDS_DATA) { cdp_bbuf[cdp_bptr++] = cdp_chob; /* store data */ if ((cdp_bptr >= CD_BINLNT) || eorfl) { /* end card or end rec? */ ch6_set_flags (CH_A, U_CDP, CHF_EOR); /* set eor */ return cdp_card_end (&cdp_unit); /* write card */ } return SCPE_OK; } return SCPE_IERR; } /* Unit timeout */ t_stat cdp_svc (UNIT *uptr) { uint32 i; switch (cdp_sta) { /* case on state */ case CDS_INIT: /* initial state */ for (i = 0; i < CD_BINLNT; i++) /* clear bin buffer */ cdp_bbuf[i] = 0; cdp_sta = CDS_DATA; /* data state */ cdp_bptr = 0; /* init pointer */ ch6_req_wr (CH_A, U_CDP); /* request channel */ cdp_chob = 0; /* clr, inval buffer */ cdp_chob_v = 0; sim_activate (uptr, cdp_tleft); /* go again */ break; case CDS_DATA: /* data state */ if (!ch6_qconn (CH_A, U_CDP)) /* chan disconnect? */ return cdp_card_end (uptr); /* write card */ if (cdp_chob_v) /* valid? clear */ cdp_chob_v = 0; else ind_ioc = 1; /* no, io check */ ch6_req_wr (CH_A, U_CDP); /* req channel */ sim_activate (uptr, (cdp_bptr & 1)? cdp_tleft: cdp_tright); break; case CDS_END: /* end state */ if (ch6_qconn (CH_A, U_CDP)) { /* if cdp still conn */ cdp_sta = CDS_INIT; /* return to init */ sim_activate (uptr, 1); /* next card */ } break; } return SCPE_OK; } /* Card end - write card image to file, transition to end state */ t_stat cdp_card_end (UNIT *uptr) { uint32 i, col, row, bufw, colbin; char *pch, bcd, cdp_cbuf[(2 * CD_CHRLNT) + 2]; t_uint64 dat; if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return SCPE_UNATT; if (uptr->flags & UNIT_PCA) pch = bcd_to_ascii_a; else pch = bcd_to_ascii_h; for (col = 0; col < ((2 * CD_CHRLNT) + 1); col++) cdp_cbuf[col] = ' '; /* clear char buf */ for (col = 0; col < 72; col++) { /* process 72 columns */ colbin = 0; dat = bit_masks[35 - (col % 36)]; /* mask for column */ for (row = 0; row < 12; row++) { /* proc 12 rows */ bufw = (row * 2) + (col / 36); /* index to buffer */ if (cdp_bbuf[bufw] & dat) colbin |= col_masks[row]; } if (cdp_unit.flags & UNIT_CBN) { /* column binary? */ cdp_cbuf[2 * col] = pch[(colbin >> 6) & 077]; cdp_cbuf[(2 * col) + 1] = pch[colbin & 077]; } else { /* text */ bcd = colbin_to_bcd (colbin); /* column bin -> BCD */ cdp_cbuf[col] = pch[bcd]; /* -> ASCII */ } } for (i = ((2 * CD_CHRLNT) + 1); (i > 0) && (cdp_cbuf[i - 1] == ' '); --i) ; /* trim spaces */ cdp_cbuf[i++] = '\n'; /* append nl */ cdp_cbuf[i++] = 0; /* append nul */ fputs (cdp_cbuf, uptr->fileref); /* write card */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("CDP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } cdp_sta = CDS_END; /* end state */ sim_cancel (uptr); /* cancel current */ sim_activate (uptr, cdp_tstop); /* long timer */ return SCPE_OK; } /* Card punch reset */ t_stat cdp_reset (DEVICE *dptr) { uint32 i; for (i = 0; i < 24; i++) /* clear buffer */ cdp_bbuf[i] = 0; cdp_sta = 0; /* clear state */ cdp_bptr = 0; /* clear buf ptr */ cdp_chob = 0; cdp_chob_v = 0; sim_cancel (&cdp_unit); /* stop punch */ return SCPE_OK; } /* Column binary to BCD This is based on documentation in the IBM 1620 manual and may not be accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit pattern, and the appropriate bits are set. (Double punches inclusive OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected; since the 7094 only reads column binary, double punches are ignored. Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. The for loop works right to left, so the table is reversed. */ static const char row_val[12] = { 011, 010, 007, 006, 005, 004, 003, 002, 001, 020, 040, 060 }; char colbin_to_bcd (uint32 cb) { uint32 i; char bcd; for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */ if (cb & (1 << i)) bcd |= row_val[i]; } return bcd; } simh-3.8.1/I7094/i7094_binloader.c0000644000175000017500000001365711112033436014322 0ustar vlmvlm/* i7094_binloader.c: IBM 7094 simulator interface Copyright (c) 2008, David G. Pitts 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 AUTHOR 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 author. */ /*********************************************************************** * * binloader.h - IBM 7090 emulator binary loader header. * * Changes: * 10/20/03 DGP Original. * 12/28/04 DGP Changed for new object formats. * ***********************************************************************/ #define IBSYSSYM '$' /* Marks end of object file */ #define WORDPERREC 5 /* Object words per record */ #define LOADADDR 0200 /* Default load address */ #define OBJRECLEN 80 /* Object record length */ #define CHARWORD 12 /* Characters per word */ /* ** Object tags */ #define IDT_TAG '0' /* 0SSSSSS0LLLLL */ #define ABSENTRY_TAG '1' /* 10000000AAAAA */ #define RELENTRY_TAG '2' /* 20000000RRRRR */ #define ABSEXTRN_TAG '3' /* 3SSSSSS0AAAAA */ #define RELEXTRN_TAG '4' /* 4SSSSSS0RRRRR */ #define ABSGLOBAL_TAG '5' /* 5SSSSSS0AAAAA */ #define RELGLOBAL_TAG '6' /* 6SSSSSS0RRRRR */ #define ABSORG_TAG '7' /* 70000000AAAAA */ #define RELORG_TAG '8' /* 80000000RRRRR */ #define ABSDATA_TAG '9' /* 9AAAAAAAAAAAA */ #define RELADDR_TAG 'A' /* AAAAAAAARRRRR */ #define RELDECR_TAG 'B' /* BARRRRRAAAAAA */ #define RELBOTH_TAG 'C' /* CARRRRRARRRRR */ #define BSS_TAG 'D' /* D0000000PPPPP */ #define ABSXFER_TAG 'E' /* E0000000RRRRR */ #define RELXFER_TAG 'F' /* F0000000RRRRR */ #define EVEN_TAG 'G' /* G0000000RRRRR */ #define FAPCOMMON_TAG 'H' /* H0000000AAAAA */ /* Where: * SSSSSS - Symbol * LLLLLL - Length of module * AAAAAA - Absolute field * RRRRRR - Relocatable field * PPPPPP - PC offset field */ /*********************************************************************** * * binloader.c - IBM 7090 emulator binary loader routines for ASM7090 * and LNK7090 object files. * * Changes: * 10/20/03 DGP Original. * 12/28/04 DGP Changed for new object formats. * 02/14/05 DGP Detect IBSYSSYM for EOF. * 06/09/06 DGP Make simh callable. * ***********************************************************************/ #include "i7094_defs.h" extern t_uint64 *M; extern uint32 PC; t_stat binloader (FILE *fd, char *file, int loadpt) { #ifdef DEBUGLOADER FILE *lfd; #endif int transfer = FALSE; int loadaddr = LOADADDR; int curraddr = LOADADDR; char inbuf[OBJRECLEN+2]; #ifdef DEBUGLOADER lfd = fopen ("load.log", "w"); fprintf (lfd, "binloader: file = '%s', loadpt = %d\n", file, loadpt); #endif if (loadpt > 0) { loadaddr = loadpt; curraddr = loadpt; } while (fgets (inbuf, sizeof(inbuf), fd)) { char *op = inbuf; int i; if (*op == IBSYSSYM) /* End of object marker */ break; for (i = 0; i < WORDPERREC; i++) { char otag; char item[16]; t_uint64 ldata; otag = *op++; if (otag == ' ') break; strncpy (item, op, CHARWORD); item[CHARWORD] = '\0'; #ifdef WIN32 sscanf (item, "%I64o", &ldata); #else sscanf (item, "%llo", &ldata); #endif #ifdef DEBUGLOADER fprintf (lfd, "loadaddr = %05o, curraddr = %05o\n", loadaddr, curraddr); fprintf (lfd, " otag = %c, item = %s\n", otag, item); fprintf (lfd, " ldata = %12.12o\n", ldata); #endif switch (otag) { case IDT_TAG: break; case ABSORG_TAG: curraddr = loadaddr = (int32) ldata & AMASK; break; case RELORG_TAG: curraddr = (int32) (ldata + loadaddr) & AMASK; break; case BSS_TAG: curraddr = (int32) (curraddr + ldata) & AMASK; break; case RELBOTH_TAG: ldata = ldata + loadaddr + (loadaddr << INST_V_DEC); goto STORE; case RELDECR_TAG: ldata = ldata + (loadaddr << INST_V_DEC); goto STORE; case RELADDR_TAG: ldata = ldata + loadaddr; case ABSDATA_TAG: STORE: #ifdef DEBUGLOADER fprintf (lfd, " M[%05o] = %12.12o\n", curraddr, ldata); #endif M[curraddr] = ldata & DMASK; curraddr++; break; case ABSXFER_TAG: transfer = TRUE; case ABSENTRY_TAG: PC = (uint32) ldata & AMASK; #ifdef DEBUGLOADER fprintf (lfd, " PC = %05o\n", PC); #endif if (transfer) goto GOSTART; break; case RELXFER_TAG: transfer = TRUE; case RELENTRY_TAG: ldata = (ldata + loadaddr) & AMASK; PC = (uint32) ldata & AMASK; #ifdef DEBUGLOADER fprintf (lfd, " PC = %05o\n", PC); #endif if (transfer) goto GOSTART; break; default: ; } op += CHARWORD; } } GOSTART: #ifdef DEBUGLOADER fclose (lfd); #endif return SCPE_OK; } simh-3.8.1/I7094/i7094_com.c0000644000175000017500000013126011112033436013130 0ustar vlmvlm/* i7094_com.c: IBM 7094 7750 communications interface simulator Copyright (c) 2005-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. com 7750 controller coml 7750 lines 19-Nov-2008 RMS Revised for common TMXR show routines This module implements an abstract simulator for the IBM 7750 communications computer as used by the CTSS system. The 7750 supports up to 112 lines; the simulator supports 33. The 7750 can handle both high-speed lines, in 6b and 12b mode, and normal terminals, in 12b mode only; the simulator supports only terminals. The 7750 can handle many different kinds of terminals; the simulator supports only a limited subset. Input is asynchronous. The 7750 sets ATN1 to signal availability of input. When the 7094 issues a CTLRN, the 7750 gathers available input characters into a message. The message has a 12b sequence number, followed by 12b line number/character pairs, followed by end-of-medium (03777). Input characters can either be control characters (bit 02000 set) or data characters. Data characters are 1's complemented and are 8b wide: 7 data bits and 1 parity bit (which may be 0). Output is synchronous. When the 7094 issues a CTLWN, the 7750 interprets the channel output as a message. The message has a 12b line number, followed by a 12b character count, followed by characters, followed by end-of-medium. If bit 02000 of the line number is set, the characters are 12b wide. If bit 01000 is set, the message is a control message. 12b characters consist of 7 data bits, 1 parity bit, and 1 start bit. Data characters are 1's complemented. Data character 03777 is special and causes the 7750 to repeat the previous bit for the number of bit times specified in the next character. This is used to generate delays for positioning characters. The 7750 supports flow control for output. To help the 7094 account for usage of 7750 buffer memory, the 7750 sends 'character output completion' messages for every 'n' characters output on a line, where n <= 31. Note that the simulator console is mapped in as line n+1. */ #include "i7094_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #define COM_MLINES 32 /* mux lines */ #define COM_TLINES (COM_MLINES + 1) /* total lines */ #define COM_BUFSIZ 120 /* max chan transfer */ #define COM_PKTSIZ 16384 /* character buffer */ #define UNIT_V_2741 (TTUF_V_UF + 0) /* 2741 - ni */ #define UNIT_V_K35 (TTUF_V_UF + 1) /* KSR-35 */ #define UNIT_2741 (1 << UNIT_V_2741) #define UNIT_K35 (1 << UNIT_V_K35) #define CONN u3 /* line is connected */ #define NEEDID u4 /* need to send ID */ #define COM_INIT_POLL 8000 /* polling interval */ #define COMC_WAIT 2 /* channel delay time */ #define COML_WAIT 1000 /* char delay time */ #define COM_LBASE 4 /* start of lines */ /* Input threads */ #define COM_PLU 0 /* multiplexor poll */ #define COM_CIU 1 /* console input */ #define COM_CHU 2 /* channel transfer */ #define COM_SNS 3 /* sense transfer */ /* Communications input */ #define COMI_VALIDL 02000 /* valid line flag */ #define COMI_PARITY 00200 /* parity bit */ #define COMI_DIALUP 02001 /* dialup */ #define COMI_ENDID 02002 /* end ID */ #define COMI_INTR 02003 /* interrupt */ #define COMI_QUIT 02004 /* quit */ #define COMI_HANGUP 02005 /* hangup */ #define COMI_EOM 03777 /* end of medium */ #define COMI_COMP(x) ((uint16) (03000 + ((x) & COMI_CMAX))) #define COMI_K35 1 /* KSR-35 ID */ #define COMI_K37 7 /* KSR-37 ID */ #define COMI_2741 8 /* 2741 ID */ #define COMI_CMAX 31 /* max chars returned */ #define COMI_BMAX 50 /* buffer max, words */ #define COMI_12BMAX ((3 * COMI_BMAX) - 1) /* last 12b char */ /* Communications output */ #define COMO_LIN12B 0200000000000 /* line is 12b */ #define COMO_LINCTL 0100000000000 /* control msg */ #define COMO_GETLN(x) (((uint32) ((x) >> 24)) & 0777) #define COMO_CTLRST 0000077770000 /* control reset */ #define COMO_BITRPT 03777 /* bit repeat */ #define COMO_EOM12B 07777 /* end of medium */ #define COMO_BMAX 94 /* buffer max, words */ #define COMO_12BMAX ((3 * COMO_BMAX) - 1) /* Status word (60b) */ #define COMS_PCHK 004000000000000000000 /* prog check */ #define COMS_DCHK 002000000000000000000 /* data check */ #define COMS_EXCC 001000000000000000000 /* exc cond */ #define COMS_MLNT 000040000000000000000 /* message length check */ #define COMS_CHNH 000020000000000000000 /* channel hold */ #define COMS_CHNQ 000010000000000000000 /* channel queue full */ #define COMS_ITMO 000000100000000000000 /* interface timeout */ #define COMS_DATR 000000004000000000000 /* data message ready */ #define COMS_INBF 000000002000000000000 /* input buffer free */ #define COMS_SVCR 000000001000000000000 /* service message ready */ #define COMS_PALL 000000000000000000000 #define COMS_DALL 000000000000000000000 #define COMS_EALL 000000000000000000000 #define COMS_DYN 000000007000000000000 /* Report variables */ #define COMR_FQ 1 /* free queue */ #define COMR_IQ 2 /* input queue */ #define COMR_OQ 4 /* output queue */ /* List heads and entries */ typedef struct { uint16 head; uint16 tail; } LISTHD; typedef struct { uint16 next; uint16 data; } LISTENT; /* The 7750 character buffer is maintained as linked lists. The lists are: free free list inpq input queue outq[ln] output queue for line ln The input queue has two entries for each character; the first is the line number, the second the character. The output queues have only one entry for each character. Links are done as subscripts in array com_pkt. This allows the list headers and the queues themselves to be saved and restored. */ uint32 com_ch = CH_E; /* saved channel */ uint32 com_enab = 0; /* 7750 enabled */ uint32 com_msgn = 0; /* next input msg num */ uint32 com_sta = 0; /* 7750 state */ uint32 com_stop = 0; /* channel stop */ uint32 com_quit = 0; /* quit code */ uint32 com_intr = 0; /* interrupt code */ uint32 com_bptr = 0; /* buffer pointer */ uint32 com_blim = 0; /* buffer count */ uint32 com_tps = 50; /* polls/second */ uint32 com_not_ret[COM_TLINES] = { 0 }; /* chars not returned */ t_uint64 com_sns = 0; /* sense word */ t_uint64 com_chob = 0; /* chan output buf */ uint32 com_chob_v = 0; /* valid flag */ t_uint64 com_buf[COM_BUFSIZ]; /* channel buffer */ LISTHD com_free; /* free list */ LISTHD com_inpq; /* input queue */ LISTHD com_outq[COM_TLINES]; /* output queue */ LISTENT com_pkt[COM_PKTSIZ]; /* character packets */ TMLN com_ldsc[COM_MLINES] = { 0 }; /* line descriptors */ TMXR com_desc = { COM_MLINES, 0, 0, com_ldsc }; /* mux descriptor */ /* Even parity truth table */ static const uint8 com_epar[128] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }; extern uint32 ch_req; t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat com_chwr (uint32 ch, t_uint64 val, uint32 flags); t_stat comi_svc (UNIT *uptr); t_stat comc_svc (UNIT *uptr); t_stat como_svc (UNIT *uptr); t_stat coms_svc (UNIT *uptr); t_stat comti_svc (UNIT *uptr); t_stat comto_svc (UNIT *uptr); t_stat com_reset (DEVICE *dptr); t_stat com_attach (UNIT *uptr, char *cptr); t_stat com_detach (UNIT *uptr); t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc); void com_reset_ln (uint32 i); uint16 com_gethd_free (LISTHD *lh); uint16 com_gethd (LISTHD *lh); t_bool com_new_puttl (LISTHD *lh, uint16 val); void com_puttl (LISTHD *lh, uint16 ent); t_bool com_inp_msg (uint32 ln, uint16 msg); void com_skip_outc (uint32 ln); t_stat com_test_atn (uint32 ch); t_uint64 com_getob (uint32 ch); t_bool com_qdone (uint32 ch); void com_end (uint32 ch, uint32 fl, uint32 st); t_stat com_send_id (uint32 ln); t_stat com_send_ccmp (uint32 ln); t_stat com_queue_in (uint32 ln, uint32 ch); uint32 com_queue_out (uint32 ln, uint32 *c1); void com_set_sns (t_uint64 stat); /* COM data structures com_dev COM device descriptor com_unit COM unit descriptor com_reg COM register list com_mod COM modifiers list */ DIB com_dib = { &com_chsel, &com_chwr }; UNIT com_unit[] = { { UDATA (&comi_svc, UNIT_ATTABLE, 0), COM_INIT_POLL }, { UDATA (&comti_svc, UNIT_DIS, 0), KBD_POLL_WAIT }, { UDATA (&comc_svc, UNIT_DIS, 0), COMC_WAIT }, { UDATA (&coms_svc, UNIT_DIS, 0), COMC_WAIT } }; REG com_reg[] = { { FLDATA (ENABLE, com_enab, 0) }, { ORDATA (STATE, com_sta, 6) }, { ORDATA (MSGNUM, com_msgn, 12) }, { ORDATA (SNS, com_sns, 60) }, { ORDATA (CHOB, com_chob, 36) }, { FLDATA (CHOBV, com_chob_v, 0) }, { FLDATA (STOP, com_stop, 0) }, { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) }, { DRDATA (BPTR, com_bptr, 7), REG_RO }, { DRDATA (BLIM, com_blim, 7), REG_RO }, { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT }, { BRDATA (FREEQ, &com_free, 10, 16, 2) }, { BRDATA (INPQ, &com_inpq, 10, 16, 2) }, { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) }, { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) }, { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (CHAN, com_ch, 3), REG_HRO }, { NULL } }; MTAB com_mod[] = { { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &com_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &com_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &com_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL, NULL, &com_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INQ", NULL, NULL, &com_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL, NULL, &com_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL, NULL, &com_show_ctrl, 0 }, { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "OUTQ", NULL, NULL, &com_show_outq, 0 }, { 0 } }; DEVICE com_dev = { "COM", com_unit, com_reg, com_mod, 3, 10, 31, 1, 16, 8, &tmxr_ex, &tmxr_dep, &com_reset, NULL, &com_attach, &com_detach, &com_dib, DEV_NET | DEV_DIS }; /* COML data structures coml_dev COML device descriptor coml_unit COML unit descriptor coml_reg COML register list coml_mod COML modifiers list */ UNIT coml_unit[] = { { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&como_svc, 0, 0), COML_WAIT }, { UDATA (&comto_svc, 0, 0), COML_WAIT }, }; MTAB coml_mod[] = { { UNIT_K35+UNIT_2741, 0 , "KSR-37", "KSR-37", NULL }, { UNIT_K35+UNIT_2741, UNIT_K35 , "KSR-35", "KSR-35", NULL }, // { UNIT_K35+UNIT_2741, UNIT_2741, "2741", "2741", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &com_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, (void*) &com_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &com_desc }, { 0 } }; REG coml_reg[] = { { URDATA (TIME, coml_unit[0].wait, 10, 24, 0, COM_TLINES, REG_NZ + PV_LEFT) }, { NULL } }; DEVICE coml_dev = { "COML", coml_unit, coml_reg, coml_mod, COM_TLINES, 10, 31, 1, 16, 8, NULL, NULL, &com_reset, NULL, NULL, NULL, NULL, DEV_DIS }; /* COM: channel select */ t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit) { com_ch = ch; /* save channel */ if (sim_is_active (&com_unit[COM_CHU]) || /* not idle? */ sim_is_active (&com_unit[COM_SNS])) { com_end (ch, CHINT_SEQC, 0); /* end, seq check */ return SCPE_OK; } switch (sel) { /* case on select */ case CHSL_RDS: /* read */ case CHSL_WRS: /* write */ com_sns = 0; /* clear status */ sim_activate (&com_unit[COM_CHU], com_unit[COM_CHU].wait); break; case CHSL_SNS: /* sense */ sim_activate (&com_unit[COM_SNS], com_unit[COM_SNS].wait); break; case CHSL_CTL: /* control */ default: /* other */ return STOP_ILLIOP; } com_stop = 0; /* clear stop */ com_sta = sel; /* set initial state */ return SCPE_OK; } /* Channel write, from 7909 channel program */ t_stat com_chwr (uint32 ch, t_uint64 val, uint32 stopf) { if (stopf) com_stop = 1; else { com_chob = val; /* store data */ com_chob_v = 1; /* set valid */ } return SCPE_OK; } /* Unit service - SNS */ t_stat coms_svc (UNIT *uptr) { t_uint64 dat; switch (com_sta) { /* case on state */ case CHSL_SNS: /* prepare data */ com_sns &= ~COMS_DYN; /* clear dynamic flags */ if (com_free.head) /* free space? */ com_set_sns (COMS_INBF); if (com_inpq.head) /* pending input? */ com_set_sns (COMS_DATR); com_buf[0] = (com_sns >> 24) & DMASK; /* buffer is 2 words */ com_buf[1] = (com_sns << 12) & DMASK; com_bptr = 0; com_blim = 2; com_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ break; case CHSL_SNS|CHSL_2ND: /* second state */ if (com_bptr >= com_blim) { /* end of buffer? */ ch9_set_end (com_ch, 0); /* set end */ ch_req |= REQ_CH (com_ch); /* request channel */ com_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ sim_activate (uptr, 10 * uptr->wait); /* longer wait */ return SCPE_OK; } dat = com_buf[com_bptr++]; /* get word */ if (!com_stop) /* send wd to chan */ ch9_req_rd (com_ch, dat); break; case CHSL_SNS|CHSL_3RD: /* 3rd state */ if (com_qdone (com_ch)) /* done? exit */ return SCPE_OK; com_sta = CHSL_SNS; /* repeat sequence */ break; } sim_activate (uptr, uptr->wait); /* sched next */ return SCPE_OK; } /* Unit service - channel program */ t_stat comc_svc (UNIT *uptr) { uint32 i, j, k, ccnt, ln, uln, ent; uint16 chr; t_uint64 dat; switch (com_sta) { /* case on state */ case CHSL_RDS: /* read start */ for (i = 0; i < COM_BUFSIZ; i++) /* clear chan buf */ com_buf[i] = 0; com_buf[0] = com_msgn; /* 1st char is msg num */ com_msgn = (com_msgn + 1) & 03777; /* incr msg num */ for (i = 1, j = 0; i < COMI_12BMAX; i++) { /* fill buffer */ ent = com_gethd_free (&com_inpq); /* get next entry */ if (ent == 0) /* q empty, done */ break; if ((i % 3) == 0) /* next word? */ j++; com_buf[j] = (com_buf[j] << 12) | /* pack data */ ((t_uint64) (com_pkt[ent].data & 07777)); } for (k = i % 3; k < 3; k++) { /* fill with EOM */ if (k == 0) /* next word? */ j++; com_buf[j] = (com_buf[j] << 12) | COMI_EOM; } com_bptr = 0; /* init buf ptr */ com_blim = j + 1; /* save buf size */ com_sta = CHSL_RDS|CHSL_2ND; /* next state */ break; case CHSL_RDS|CHSL_2ND: /* read xmit word */ if (com_bptr >= com_blim) /* transfer done? */ com_end (com_ch, 0, CHSL_RDS|CHSL_3RD); /* end, next state */ else { /* more to do */ dat = com_buf[com_bptr++]; /* get word */ if (!com_stop) /* give to channel */ ch9_req_rd (com_ch, dat); } break; case CHSL_RDS|CHSL_3RD: /* read end */ if (com_qdone (com_ch)) /* done? */ return com_test_atn (com_ch); /* test atn, exit */ com_sta = CHSL_RDS; /* repeat sequence */ break; case CHSL_WRS: /* write start */ for (i = 0; i < COM_BUFSIZ; i++) /* clear chan buf */ com_buf[i] = 0; com_bptr = 0; /* init buf ptr */ com_sta = CHSL_WRS|CHSL_2ND; /* next state */ ch_req |= REQ_CH (com_ch); /* request channel */ com_chob = 0; /* clr, inval buf */ com_chob_v = 0; break; case CHSL_WRS|CHSL_2ND: /* write first word */ dat = com_getob (com_ch); /* get word? */ if (dat == 0777777777777) { /* turn on? */ com_enab = 1; /* enable 7750 */ com_msgn = 0; /* init message # */ com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ } else if (dat & COMO_LINCTL) { /* control message? */ ln = COMO_GETLN (dat); /* line number */ if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ return STOP_INVLIN; if (dat & COMO_CTLRST) /* char must be 0 */ return STOP_INVMSG; if (ln >= COM_LBASE) com_reset_ln (ln - COM_LBASE); com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ } else { /* data message */ ccnt = (((uint32) dat >> 12) & 07777) + 1; /* char count plus EOM */ if (dat & COMO_LIN12B) /* 12b? double */ ccnt = ccnt << 1; com_blim = (ccnt + 6 + 5) / 6; /* buffer limit */ if ((com_blim == 1) || (com_blim >= COMO_BMAX)) return STOP_INVMSG; com_buf[com_bptr++] = dat; /* store word */ com_sta = CHSL_WRS|CHSL_3RD; /* next state */ ch_req |= REQ_CH (com_ch); /* request channel */ } break; case CHSL_WRS|CHSL_3RD: /* other words */ dat = com_getob (com_ch); /* get word */ com_buf[com_bptr++] = dat; /* store word */ if (com_bptr >= com_blim) { /* transfer done? */ ln = COMO_GETLN (com_buf[0]); /* line number */ if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ return STOP_INVLIN; if ((com_buf[0] & COMO_LIN12B) && /* 12b message? */ (ln >= COM_LBASE)) { uln = ln - COM_LBASE; /* unit number */ for (i = 2, j = 0; i < COMO_12BMAX; i++) { /* unpack 12b char */ if ((i % 3) == 0) j++; chr = (uint16) (com_buf[j] >> ((2 - (i % 3)) * 12)) & 07777; if (chr == COMO_EOM12B) /* EOM? */ break; if (!com_new_puttl (&com_outq[uln], chr)) return STOP_NOOFREE; /* append to outq */ } sim_activate (&coml_unit[uln], coml_unit[uln].wait); } com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ } else if (!com_stop) /* request channel */ ch_req |= REQ_CH (com_ch); break; case CHSL_WRS|CHSL_4TH: /* buffer done */ if (com_qdone (com_ch)) /* done? */ return com_test_atn (com_ch); /* test atn, exit */ com_sta = CHSL_WRS; /* repeat sequence */ break; default: return SCPE_IERR; } sim_activate (uptr, uptr->wait); return SCPE_OK; } /* Unit service - console receive - always running, even if device is not */ t_stat comti_svc (UNIT *uptr) { int32 c; t_stat r; sim_activate (uptr, uptr->wait); /* continue poll */ c = sim_poll_kbd (); /* get character */ if (c && !(c & (SCPE_BREAK|SCPE_KFLAG))) /* error? */ return c; if (!com_enab || (c & SCPE_BREAK)) /* !enab, break? done */ return SCPE_OK; if (coml_unit[COM_MLINES].NEEDID) /* ID needed? */ return com_send_id (COM_MLINES); if ((c & SCPE_KFLAG) && ((c = c & 0177) != 0)) { /* char input? */ if (r = com_queue_in (COM_MLINES, c)) return r; if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) sim_putchar (c); if (c == '\r') sim_putchar ('\n'); } return com_test_atn (com_ch); /* set ATN if input */ } /* Unit service - receive side Poll all active lines for input Poll for new connections */ t_stat comi_svc (UNIT *uptr) { int32 c, ln, t; t_stat r; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; t = sim_rtcn_calb (com_tps, TMR_COM); /* calibrate */ sim_activate (uptr, t); /* continue poll */ if (!com_enab) /* not enabled? exit */ return SCPE_OK; ln = tmxr_poll_conn (&com_desc); /* look for connect */ if (ln >= 0) { /* got one? */ com_ldsc[ln].rcve = 1; /* rcv enabled */ coml_unit[ln].CONN = 1; /* flag connected */ coml_unit[ln].NEEDID = 1; /* need ID */ } tmxr_poll_rx (&com_desc); /* poll for input */ for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ if (com_ldsc[ln].conn) { /* connected? */ if (coml_unit[ln].NEEDID) return com_send_id (ln); c = tmxr_getc_ln (&com_ldsc[ln]); /* get char */ if (c) { /* any char? */ c = c & 0177; /* mask to 7b */ if (r = com_queue_in (ln, c)) /* queue char, err? */ return r; if (com_ldsc[ln].xmte) { /* output enabled? */ if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) /* echo char */ tmxr_putc_ln (&com_ldsc[ln], c); if (c == '\r') /* add LF after CR */ tmxr_putc_ln (&com_ldsc[ln], '\n'); tmxr_poll_tx (&com_desc); /* poll xmt */ } /* end if enabled */ } /* end if char */ } /* end if conn */ else if (coml_unit[ln].CONN) { /* not conn, was conn? */ coml_unit[ln].CONN = 0; /* clear connected */ coml_unit[ln].NEEDID = 0; /* clear need id */ if (!com_inp_msg (ln, COMI_HANGUP)) /* hangup message */ return STOP_NOIFREE; } } /* end for */ return com_test_atn (com_ch); /* set ATN if input */ } /* Unit service - console transmit */ t_stat comto_svc (UNIT *uptr) { uint32 c, c1; if (com_outq[COM_MLINES].head == 0) /* no more characters? */ return com_send_ccmp (COM_MLINES); /* free any remaining */ c = com_queue_out (COM_MLINES, &c1); /* get character, cvt */ if (c) /* printable? output */ sim_putchar (c); if (c1) /* second char? output */ sim_putchar (c1); sim_activate (uptr, uptr->wait); /* next char */ if (com_not_ret[COM_MLINES] >= COMI_CMAX) /* completion needed? */ return com_send_ccmp (COM_MLINES); /* generate msg */ return SCPE_OK; } /* Unit service - transmit side */ t_stat como_svc (UNIT *uptr) { uint32 c, c1; int32 ln = uptr - coml_unit; /* line # */ if (com_outq[ln].head == 0) /* no more characters? */ return com_send_ccmp (ln); /* free any remaining */ if (com_ldsc[ln].conn) { /* connected? */ if (com_ldsc[ln].xmte) { /* output enabled? */ c = com_queue_out (ln, &c1); /* get character, cvt */ if (c) /* printable? output */ tmxr_putc_ln (&com_ldsc[ln], c); if (c1) /* print second */ tmxr_putc_ln (&com_ldsc[ln], c1); } /* end if */ tmxr_poll_tx (&com_desc); /* poll xmt */ sim_activate (uptr, uptr->wait); /* next char */ if (com_not_ret[ln] >= COMI_CMAX) /* completion needed? */ return com_send_ccmp (ln); /* generate msg */ } /* end if conn */ return SCPE_OK; } /* Send ID sequence on input */ t_stat com_send_id (uint32 ln) { com_inp_msg (ln, COMI_DIALUP); /* input message: */ if (coml_unit[ln].flags & UNIT_K35) /* dialup, ID, endID */ com_inp_msg (ln, COMI_K35); else com_inp_msg (ln, COMI_K37); com_inp_msg (ln, 0); com_inp_msg (ln, 0); com_inp_msg (ln, 0); com_inp_msg (ln, 0); com_inp_msg (ln, (uint16) (ln + COM_LBASE)); if (!com_inp_msg (ln, COMI_ENDID)) /* make sure there */ return STOP_NOIFREE; /* was room for msg */ coml_unit[ln].NEEDID = 0; return SCPE_OK; } /* Translate and queue input character */ t_stat com_queue_in (uint32 ln, uint32 c) { uint16 out; if (c == com_intr) out = COMI_INTR; else if (c == com_quit) out = COMI_QUIT; else { if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ if (islower (c)) /* convert LC to UC */ c = toupper (c); } else c |= (com_epar[c]? COMI_PARITY: 0); /* add even parity */ out = (~c) & 0377; /* 1's complement */ } if (!com_inp_msg (ln, out)) /* input message */ return STOP_NOIFREE; return SCPE_OK; } /* Retrieve and translate output character */ uint32 com_queue_out (uint32 ln, uint32 *c1) { uint32 c, ent, raw; *c1 = 0; /* assume non-printing */ ent = com_gethd_free (&com_outq[ln]); /* get character */ if (ent == 0) /* nothing? */ return 0; raw = com_pkt[ent].data; /* get 12b character */ com_not_ret[ln]++; if (raw == COMO_BITRPT) { /* insert delay? */ com_skip_outc (ln); return 0; } c = (~raw >> 1) & 0177; /* remove start, parity */ if (c >= 040) { /* printable? */ if (c == 0177) /* DEL? ignore */ return 0; if ((coml_unit[ln].flags & UNIT_K35) && islower (c)) /* KSR-35 LC? */ c = toupper (c); /* cvt to UC */ return c; } switch (c) { case '\t': case '\f': case '\b': case '\a': /* valid ctrls */ return c; case '\r': /* carriage return? */ if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */ *c1 = '\n'; /* lf after cr */ return c; case '\n': /* line feed? */ if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ *c1 = '\n'; /* lf after cr */ return '\r'; } return c; /* print lf */ case 022: /* DC2 */ if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ com_skip_outc (ln); /* skip next */ return '\n'; /* print lf */ } break; case 023: /* DC3 */ if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */ com_skip_outc (ln); /* skip next */ break; } return 0; /* ignore others */ } /* Generate completion message, if needed */ t_stat com_send_ccmp (uint32 ln) { uint32 t; if (t = com_not_ret[ln]) { /* chars not returned? */ if (t > COMI_CMAX) /* limit to max */ t = COMI_CMAX; com_not_ret[ln] -= t; /* keep count */ if (!com_inp_msg (ln, COMI_COMP (t))) /* gen completion msg */ return STOP_NOIFREE; } return SCPE_OK; } /* Skip next char in output queue */ void com_skip_outc (uint32 ln) { if (com_gethd_free (&com_outq[ln])) /* count it */ com_not_ret[ln]++; return; } /* Read and validate output buffer */ t_uint64 com_getob (uint32 ch) { if (com_chob_v) /* valid? clear */ com_chob_v = 0; else if (!com_stop) { /* not stopped? */ ch9_set_ioc (com_ch); /* IO check */ com_set_sns (COMS_ITMO); /* set sense bit */ } return com_chob; } /* Set attention if input pending */ t_stat com_test_atn (uint32 ch) { if (com_inpq.head) ch9_set_atn (ch); return SCPE_OK; } /* Test for done */ t_bool com_qdone (uint32 ch) { if (com_stop || !ch9_qconn (ch)) { /* stop or err disc? */ com_sta = 0; /* ctrl is idle */ return TRUE; } return FALSE; } /* Channel end */ void com_end (uint32 ch, uint32 fl, uint32 st) { ch9_set_end (ch, fl); /* set end */ ch_req |= REQ_CH (ch); com_sta = st; /* next state */ return; } /* List routines - remove from head and free */ uint16 com_gethd_free (LISTHD *lh) { uint16 ent; if ((ent = com_gethd (lh)) != 0) com_puttl (&com_free, ent); return ent; } /* Get free entry and insert at tail */ t_bool com_new_puttl (LISTHD *lh, uint16 val) { uint16 ent; if ((ent = com_gethd (&com_free)) != 0) { com_pkt[ent].data = val; com_puttl (lh, ent); return TRUE; } return FALSE; } /* Remove from head */ uint16 com_gethd (LISTHD *lh) { uint16 ent; if ((ent = lh->head) != 0) { lh->head = com_pkt[ent].next; if (lh->head == 0) lh->tail = 0; } else lh->tail = 0; return ent; } /* Insert at tail */ void com_puttl (LISTHD *lh, uint16 ent) { if (lh->tail == 0) lh->head = ent; else com_pkt[lh->tail].next = ent; com_pkt[ent].next = 0; lh->tail = ent; return; } /* Insert line and message into input queue */ t_bool com_inp_msg (uint32 ln, uint16 msg) { uint16 ent1, ent2; if ((ent1 = com_gethd (&com_free)) != 0) { /* pkt avail? */ if ((ent2 = com_gethd (&com_free)) != 0) { /* 2nd pkt avail? */ com_pkt[ent1].data = (ln + COM_LBASE) | COMI_VALIDL; /* 1st pkt = line# */ com_pkt[ent2].data = msg; /* 2nd pkt = char */ com_puttl (&com_inpq, ent1); /* queue pkts */ com_puttl (&com_inpq, ent2); return TRUE; } com_puttl (&com_free, ent1); /* free 1st */ } return FALSE; /* failed */ } /* Set flag in sense */ void com_set_sns (t_uint64 stat) { com_sns |= stat; com_sns &= ~(COMS_PCHK|COMS_DCHK|COMS_EXCC); if (com_sns & COMS_PALL) com_sns |= COMS_PCHK; if (com_sns & COMS_DALL) com_sns |= COMS_DCHK; if (com_sns & COMS_EALL) com_sns |= COMS_EXCC; return; } /* Reset routine */ t_stat com_reset (DEVICE *dptr) { uint32 i; if (dptr->flags & DEV_DIS) { /* disabled? */ com_dev.flags = com_dev.flags | DEV_DIS; /* disable lines */ coml_dev.flags = coml_dev.flags | DEV_DIS; } else { com_dev.flags = com_dev.flags & ~DEV_DIS; /* enable lines */ coml_dev.flags = coml_dev.flags & ~DEV_DIS; } sim_activate (&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */ sim_cancel (&com_unit[COM_PLU]); sim_cancel (&com_unit[COM_CHU]); if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */ int32 t = sim_rtcn_init (com_unit[COM_PLU].wait, TMR_COM); sim_activate (&com_unit[COM_PLU], t); } com_enab = 0; com_sns = 0; com_msgn = 0; com_sta = 0; com_chob = 0; com_chob_v = 0; com_stop = 0; com_bptr = 0; com_blim = 0; for (i = 0; i < COM_BUFSIZ; i++) com_buf[i] = 0; com_inpq.head = 0; /* init queues */ com_inpq.tail = 0; for (i = 0; i < COM_TLINES; i++) { com_outq[i].head = 0; com_outq[i].tail = 0; com_reset_ln (i); } com_pkt[0].next = 0; /* init free list */ for (i = 1; i < COM_PKTSIZ; i++) { com_pkt[i].next = i + 1; com_pkt[i].data = 0; } com_pkt[COM_PKTSIZ - 1].next = 0; /* end of free list */ com_free.head = 1; com_free.tail = COM_PKTSIZ - 1; coml_unit[COM_MLINES].CONN = 1; /* console always conn */ coml_unit[COM_MLINES].NEEDID = 1; return SCPE_OK; } /* Attach master unit */ t_stat com_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&com_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_rtcn_init (uptr->wait, TMR_COM); sim_activate (uptr, 100); /* quick poll */ return SCPE_OK; } /* Detach master unit */ t_stat com_detach (UNIT *uptr) { uint32 i; t_stat r; r = tmxr_detach (&com_desc, uptr); /* detach */ for (i = 0; i < COM_MLINES; i++) /* disable rcv */ com_ldsc[i].rcve = 0; sim_cancel (uptr); /* stop poll */ return r; } /* Reset an individual line */ void com_reset_ln (uint32 ln) { while (com_gethd_free (&com_outq[ln])) ; com_not_ret[ln] = 0; sim_cancel (&coml_unit[ln]); if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0)) coml_unit[ln].CONN = 0; return; } /* Special show commands */ uint32 com_show_qsumm (FILE *st, LISTHD *lh, char *name) { uint32 i, next; next = lh->head; for (i = 0; i < COM_PKTSIZ; i++) { if (next == 0) { if (i == 0) fprintf (st, "%s is empty\n", name); else if (i == 1) fprintf (st, "%s has 1 entry\n", name); else fprintf (st, "%s had %d entries\n", name, i); return i; } next = com_pkt[next].next; } fprintf (st, "%s is corrupt\n", name); return 0; } void com_show_char (FILE *st, uint32 ch) { uint32 c; fprintf (st, "%03o", ch); c = (~ch) & 0177; if (((ch & 07400) == 0) && (c >= 040) && (c != 0177)) fprintf (st, "[%c]", c); return; } t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc) { com_show_qsumm (st, &com_free, "Free queue"); return SCPE_OK; } t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 entc, ln, i, next; if (entc = com_show_qsumm (st, &com_inpq, "Input queue")) { for (i = 0, next = com_inpq.head; next != 0; i++, next = com_pkt[next].next) { if ((i % 4) == 0) fprintf (st, "%d:\t", i); ln = com_pkt[next].data; next = com_pkt[next].next; if (next == 0) { fprintf (st, "Line number without data\n"); return SCPE_OK; } fprintf (st, "%d/", ln); com_show_char (st, com_pkt[next].data); fputc ((((i % 4) == 3)? '\n': '\t'), st); } if (i % 4) fputc ('\n', st); } return SCPE_OK; } t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 entc, ln, i, next; char name[20]; ln = uptr - com_dev.units; sprintf (name, "Output queue %d", ln); if (entc = com_show_qsumm (st, &com_outq[ln], name)) { for (i = 0, next = com_outq[ln].head; next != 0; i++, next = com_pkt[next].next) { if ((i % 8) == 0) fprintf (st, "%d:\t", i); com_show_char (st, com_pkt[next].data >> 1); fputc ((((i % 8) == 7)? '\n': '\t'), st); } if (i % 8) fputc ('\n', st); } return SCPE_OK; } t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 i; for (i = 0; i < COM_TLINES; i++) com_show_outq (st, com_dev.units + i, 1, desc); return SCPE_OK; } t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) { if (!com_enab) fprintf (st, "Controller is not initialized\n"); if (val & COMR_FQ) com_show_freeq (st, uptr, 1, desc); if (val & COMR_IQ) com_show_inq (st, uptr, 1, desc); if (val & COMR_OQ) com_show_aoutq (st, uptr, 1, desc); return SCPE_OK; } simh-3.8.1/I7094/i7094_lp.c0000644000175000017500000003750011107560226012775 0ustar vlmvlm/* i7094_lp.c: IBM 716 line printer simulator Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt 716 line printer 19-Jan-07 RMS Added UNIT_TEXT flag Internally, the 7094 works only with column binary and is limited to 72 columns of data. Each row of the printed line is represented by 72b of data (two 36b words). A complete print line consists of 12 rows (24 36b words). The printer can also echo part of what it prints, namely, the digit rows plus the 8+3 and 8+4 combinations. This was intended for verification of check printing. Echoed data is interspersed with output data in the following order: output row 9 to row 1 echo row "8+4" output row 0 echo row "8+3" output row 11 echo row 9 output row 12 echo row 8 to row 1 */ #include "i7094_defs.h" #define UNIT_V_CONS (UNIT_V_UF + 0) /* print to console */ #define UNIT_CONS (1u << UNIT_V_CONS) #define UNIT_V_BZ (UNIT_V_UF + 1) #define UNIT_V_48 (UNIT_V_UF + 2) #define UNIT_BZ (1 << UNIT_V_BZ) #define UNIT_48 (1 << UNIT_V_48) #define GET_PCHAIN(x) (((x) >> UNIT_V_BZ) & (UNIT_BZ|UNIT_48)) #define LPT_BINLNT 24 /* bin buffer length */ #define LPT_ECHLNT 22 /* echo buffer length */ #define LPT_CHRLNT 80 /* char buffer length */ #define LPS_INIT 0 /* init state */ #define LPS_DATA 1 /* print data state */ #define ECS_DATA 2 /* echo data state */ #define LPS_END 3 /* end state */ #define LPB_9ROW 0 /* bin buf: 9 row */ #define LPB_8ROW 2 /* 8 row */ #define LPB_4ROW 10 /* 4 row */ #define LPB_3ROW 12 /* 3 row */ #define LPB_1ROW 16 /* 1 row */ #define LPB_12ROW 22 /* 12 row */ #define ECB_84ROW 0 /* echo buf: 8-4 row */ #define ECB_83ROW 2 /* 8-3 row */ #define ECB_9ROW 4 /* 9 row */ #define ECHO_F 0100 /* echo map: flag */ #define ECHO_MASK 0037 /* mask */ #define CMD_BIN 1 /* cmd: bcd/bin */ #define CMD_ECHO 2 /* cmd: wrs/rds */ uint32 lpt_sta = 0; /* state */ uint32 lpt_bptr = 0; /* buffer ptr */ uint32 lpt_cmd = 0; /* modes */ uint32 lpt_tstart = 27500; /* timing */ uint32 lpt_tstop = 27500; uint32 lpt_tleft = 150; uint32 lpt_tright = 4000; t_uint64 lpt_chob = 0; uint32 lpt_chob_v = 0; t_uint64 lpt_bbuf[LPT_BINLNT]; /* binary buffer */ t_uint64 lpt_ebuf[LPT_ECHLNT]; /* echo buffer */ /* Echo ordering map */ static const uint8 echo_map[LPT_BINLNT + LPT_ECHLNT] = { 0, 1, 2, 3, 4, 5, 6, 7, /* write 9 to 1 */ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0+ECHO_F, 1+ECHO_F, /* echo 8+4 */ 18, 19, /* write 0 */ 2+ECHO_F, 3+ECHO_F, /* echo 8+3 */ 20, 21, /* write 11 */ 4+ECHO_F, 5+ECHO_F, /* echo 9 */ 22, 23, /* write 12 */ 6+ECHO_F, 7+ECHO_F, 8+ECHO_F, 9+ECHO_F, /* echo 8 to 1 */ 10+ECHO_F, 11+ECHO_F, 12+ECHO_F, 13+ECHO_F, 14+ECHO_F, 15+ECHO_F, 16+ECHO_F, 17+ECHO_F, 18+ECHO_F, 19+ECHO_F, 20+ECHO_F, 21+ECHO_F }; extern uint32 ind_ioc; extern t_uint64 bit_masks[36]; extern uint32 col_masks[12]; extern char bcd_to_ascii_a[64]; extern char bcd_to_ascii_h[64]; extern char bcd_to_pca[64]; extern char bcd_to_pch[64]; char *pch_table[4] = { bcd_to_ascii_h, bcd_to_ascii_a, bcd_to_pch, bcd_to_pca, }; t_stat lpt_reset (DEVICE *dptr); t_stat lpt_svc (UNIT *uptr); t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 flags); t_stat lpt_end_line (UNIT *uptr); extern char colbin_to_bcd (uint32 colbin); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { &lpt_chsel, &lpt_chwr }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_CONS+UNIT_TEXT, 0) }; REG lpt_reg[] = { { ORDATA (STATE, lpt_sta, 2) }, { ORDATA (CMD, lpt_cmd, 2) }, { ORDATA (CHOB, lpt_chob, 36) }, { FLDATA (CHOBV, lpt_chob_v, 0) }, { DRDATA (BPTR, lpt_bptr, 6), PV_LEFT }, { BRDATA (BUF, lpt_bbuf, 8, 36, LPT_BINLNT) }, { BRDATA (EBUF, lpt_ebuf, 8, 36, LPT_ECHLNT) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TSTART, lpt_tstart, 24), PV_LEFT + REG_NZ }, { DRDATA (TSTOP, lpt_tstop, 24), PV_LEFT + REG_NZ }, { DRDATA (TLEFT, lpt_tleft, 24), PV_LEFT + REG_NZ }, { DRDATA (TRIGHT, lpt_tright, 24), PV_LEFT + REG_NZ }, { NULL } }; MTAB lpt_mod[] = { { UNIT_CONS, UNIT_CONS, "default to console", "DEFAULT" }, { UNIT_CONS, 0 , "no default device", "NODEFAULT" }, { UNIT_48, UNIT_48, "48 character chain", "48" }, { UNIT_48, 0, "64 character chain", "64" }, { UNIT_BZ, UNIT_BZ, "business set", "BUSINESS" }, { UNIT_BZ, 0, "Fortran set", "FORTRAN" }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &lpt_reset, NULL, NULL, NULL, &lpt_dib, DEV_DISABLE }; /* Channel select routine */ t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit) { if (sel & CHSL_NDS) /* nds? nop */ return ch6_end_nds (ch); switch (sel) { /* case on cmd */ case CHSL_RDS: /* read */ case CHSL_WRS: /* write */ if (!(lpt_unit.flags & (UNIT_ATT|UNIT_CONS))) /* not attached? */ return SCPE_UNATT; if (sim_is_active (&lpt_unit)) /* busy? */ return ERR_STALL; lpt_cmd = ((unit & 02)? CMD_BIN: 0) | /* save modes */ ((sel == CHSL_RDS)? CMD_ECHO: 0); lpt_sta = LPS_INIT; /* initial state */ sim_activate (&lpt_unit, lpt_tstart); /* start reader */ break; default: /* other */ return STOP_ILLIOP; } return SCPE_OK; } /* Channel write routine - Normal mode is processed here - Echo mode is processed in the service routine (like a read) */ t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) { uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ lpt_chob = val & DMASK; /* store data */ lpt_chob_v = 1; /* set valid */ if (lpt_sta == ECS_DATA) return SCPE_OK; if (lpt_sta == LPS_DATA) { lpt_bbuf[lpt_bptr++] = lpt_chob; /* store data */ if (eorfl || /* end record, or */ ((lpt_cmd & CMD_BIN)? /* last word in buffer? */ (lpt_bptr > (LPB_1ROW + 1)): /* (binary mode) */ (lpt_bptr > (LPB_12ROW + 1)))) { /* (normal mode) */ ch6_set_flags (CH_A, u, CHF_EOR); /* set eor */ return lpt_end_line (&lpt_unit); } return SCPE_OK; } return SCPE_IERR; } /* Unit timeout */ t_stat lpt_svc (UNIT *uptr) { uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ uint32 i, map; switch (lpt_sta) { /* case on state */ case LPS_INIT: /* initial state */ for (i = 0; i < LPT_BINLNT; i++) /* clear data buffer */ lpt_bbuf[i] = 0; for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buffer */ lpt_ebuf[i] = 0; if (lpt_cmd & CMD_BIN) /* set buffer ptr */ lpt_bptr = LPB_1ROW; else lpt_bptr = LPB_9ROW; if (lpt_cmd & CMD_ECHO) /* set data state */ lpt_sta = ECS_DATA; else lpt_sta = LPS_DATA; ch6_req_wr (CH_A, u); /* request channel */ lpt_chob = 0; /* clr, inval buffer */ lpt_chob_v = 0; sim_activate (uptr, lpt_tleft); /* go again */ break; case LPS_DATA: /* print data state */ if (!ch6_qconn (CH_A, u)) /* disconnect? */ return lpt_end_line (uptr); /* line is done */ if (lpt_chob_v) /* valid? clear */ lpt_chob_v = 0; else ind_ioc = 1; /* no, io check */ ch6_req_wr (CH_A, u); /* request chan again */ sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); break; case ECS_DATA: /* echo data state */ map = echo_map[lpt_bptr++]; /* map column */ if (map == ECHO_F) { /* first echo? */ lpt_ebuf[ECB_84ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_4ROW]; lpt_ebuf[ECB_84ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_4ROW + 1]; lpt_ebuf[ECB_83ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_3ROW]; lpt_ebuf[ECB_83ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_3ROW + 1]; for (i = 0; i < 18; i++) /* copy rows 9.. 1 */ lpt_ebuf[ECB_9ROW + i] = lpt_bbuf[LPB_9ROW + i]; } if (map & ECHO_F) { /* echo cycle */ ch6_req_rd (CH_A, u, lpt_ebuf[map & ECHO_MASK], 0); if (lpt_bptr >= (LPT_BINLNT + LPT_ECHLNT)) return lpt_end_line (uptr); /* done? */ sim_activate (uptr, lpt_tleft); /* short timer */ } else { /* print cycle */ if (lpt_chob_v) /* valid? clear */ lpt_chob_v = 0; else ind_ioc = 1; /* no, io check */ lpt_bbuf[map] = lpt_chob; /* store in buffer */ sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); } if (!(echo_map[lpt_bptr] & ECHO_F)) /* print word next? */ ch6_req_wr (CH_A, u); /* req channel */ break; case LPS_END: /* end state */ if (ch6_qconn (CH_A, u)) { /* lpt still conn? */ lpt_sta = LPS_INIT; /* initial state */ sim_activate (uptr, 1); /* next line */ } break; } return SCPE_OK; } /* End line routine */ t_stat lpt_end_line (UNIT *uptr) { uint32 i, col, row, bufw, colbin; char *pch, bcd, lpt_cbuf[LPT_CHRLNT + 1]; t_uint64 dat; pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */ for (col = 0; col < (LPT_CHRLNT + 1); col++) /* clear ascii buf */ lpt_cbuf[col] = ' '; for (col = 0; col < 72; col++) { /* proc 72 columns */ colbin = 0; dat = bit_masks[35 - (col % 36)]; /* mask for column */ for (row = 0; row < 12; row++) { /* proc 12 rows */ bufw = (row * 2) + (col / 36); /* index to buffer */ if (lpt_bbuf[bufw] & dat) colbin |= col_masks[row]; } bcd = colbin_to_bcd (colbin); /* column bin -> BCD */ lpt_cbuf[col] = pch[bcd]; /* -> ASCII */ } for (i = LPT_CHRLNT; (i > 0) && (lpt_cbuf[i - 1] == ' '); --i) ; /* trim spaces */ lpt_cbuf[i] = 0; /* append nul */ if (uptr->flags & UNIT_ATT) { /* file? */ fputs (lpt_cbuf, uptr->fileref); /* write line */ fputc ('\n', uptr->fileref); /* append nl */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } } else if (uptr->flags & UNIT_CONS) { /* print to console? */ for (i = 0; lpt_cbuf[i] != 0; i++) sim_putchar (lpt_cbuf[i]); sim_putchar ('\r'); sim_putchar ('\n'); } else return SCPE_UNATT; /* otherwise error */ lpt_sta = LPS_END; /* end line state */ sim_cancel (uptr); /* cancel current */ sim_activate (uptr, lpt_tstop); /* long timer */ return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { uint32 i; for (i = 0; i < LPT_BINLNT; i++) /* clear bin buf */ lpt_bbuf[i] = 0; for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buf */ lpt_ebuf[i] = 0; lpt_sta = 0; /* clear state */ lpt_cmd = 0; /* clear modes */ lpt_bptr = 0; /* clear buf ptr */ lpt_chob = 0; lpt_chob_v = 0; sim_cancel (&lpt_unit); /* stop printer */ return SCPE_OK; } simh-3.8.1/I7094/i7094_drm.c0000644000175000017500000002730611107560226013147 0ustar vlmvlm/* i7094_drm.c: 7289/7320A drum simulator Copyright (c) 2005-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. drm 7289/7320A "fast" drum Very little is known about this device; the behavior simulated here is what is used by CTSS. - The drum channel/controller behaves like a hybrid of the 7607 and the 7909. It responds to SCD (like the 7909), gets its address from the channel program (like the 7909), but responds to IOCD/IOCP (like the 7607) and sets channel flags (like the 7607). - The drum channel supports at least 2 drums. The maximum is 8 or less. Physical drums are numbered from 0. - Each drum has a capacity of 192K 36b words. This is divided into 6 "logical" drum of 32KW each. Each "logical" drum has 16 2048W "sectors". Logical drums are numbered from 1. - The drum's behavior if a sector boundary is crossed in mid-transfer is unknown. CTSS never does this. - The drum's behavior with record operations is unknown. CTSS only uses IOCD and IOCP. - The drum's error indicators are unknown. CTSS regards bits <0:2,13> of the returned SCD data as errors, as well as the normal 7607 trap flags. - The drum's rotational speed is unknown. Assumptions in this simulator: - Transfers may not cross a sector boundary. An attempt to do so sets the EOF flag and causes an immediate disconnect. - The hardware never sets end of record. For speed, the entire drum is buffered in memory. */ #include "i7094_defs.h" #include #define DRM_NUMDR 8 /* drums/controller */ /* Drum geometry */ #define DRM_NUMWDS 2048 /* words/sector */ #define DRM_SCMASK (DRM_NUMWDS - 1) /* sector mask */ #define DRM_NUMSC 16 /* sectors/log drum */ #define DRM_NUMWDL (DRM_NUMWDS * DRM_NUMSC) /* words/log drum */ #define DRM_NUMLD 6 /* log drums/phys drum */ #define DRM_SIZE (DRM_NUMLD * DRM_NUMWDL) /* words/phys drum */ #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DRM_NUMWDS))) /* Drum address from channel */ #define DRM_V_PHY 30 /* physical drum sel */ #define DRM_M_PHY 07 #define DRM_V_LOG 18 /* logical drum sel */ #define DRM_M_LOG 07 #define DRM_V_WDA 0 /* word address */ #define DRM_M_WDA (DRM_NUMWDL - 1) #define DRM_GETPHY(x) (((uint32) ((x) >> DRM_V_PHY)) & DRM_M_PHY) #define DRM_GETLOG(x) ((((uint32) (x)) >> DRM_V_LOG) & DRM_M_LOG) #define DRM_GETWDA(x) ((((uint32) (x)) >> DRM_V_WDA) & DRM_M_WDA) #define DRM_GETDA(x) (((DRM_GETLOG(x) - 1) * DRM_NUMWDL) + DRM_GETWDA(x)) /* Drum controller states */ #define DRM_IDLE 0 #define DRM_1ST 1 #define DRM_DATA 2 #define DRM_EOS 3 uint32 drm_ch = CH_G; /* drum channel */ uint32 drm_da = 0; /* drum address */ uint32 drm_sta = 0; /* state */ uint32 drm_op = 0; /* operation */ t_uint64 drm_chob = 0; /* output buf */ uint32 drm_chob_v = 0; /* valid */ int32 drm_time = 10; /* inter-word time */ extern uint32 ind_ioc; t_stat drm_svc (UNIT *uptr); t_stat drm_reset (DEVICE *dptr); t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags); t_bool drm_da_incr (void); /* DRM data structures drm_dev DRM device descriptor drm_unit DRM unit descriptor drm_reg DRM register list */ DIB drm_dib = { &drm_chsel, &drm_chwr }; UNIT drm_unit[] = { { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) } }; REG drm_reg[] = { { ORDATA (STATE, drm_sta, 2) }, { ORDATA (DA, drm_da, 18) }, { FLDATA (OP, drm_op, 0) }, { ORDATA (CHOB, drm_chob, 36) }, { FLDATA (CHOBV, drm_chob_v, 0) }, { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, { DRDATA (CHAN, drm_ch, 3), REG_HRO }, { NULL } }; MTAB drm_mtab[] = { { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &ch_show_chan }, { 0 } }; DEVICE drm_dev = { "DRM", drm_unit, drm_reg, drm_mtab, DRM_NUMDR, 8, 18, 1, 8, 36, NULL, NULL, &drm_reset, NULL, NULL, NULL, &drm_dib, DEV_DIS }; /* Channel select routine */ t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit) { drm_ch = ch; /* save channel */ if (sel & CHSL_NDS) /* nds? nop */ return ch6_end_nds (ch); switch (sel) { /* case on cmd */ case CHSL_RDS: /* read */ case CHSL_WRS: /* write */ if (drm_sta != DRM_IDLE) /* busy? */ return ERR_STALL; drm_sta = DRM_1ST; /* initial state */ if (sel == CHSL_WRS) /* set read/write */ drm_op = 1; else drm_op = 0; /* LCHx sends addr */ break; /* wait for addr */ default: /* other */ return STOP_ILLIOP; } return SCPE_OK; } /* Channel write routine */ t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags) { uint32 u, l; int32 cp, dp; if (drm_sta == DRM_1ST) { u = DRM_GETPHY (val); /* get unit */ l = DRM_GETLOG (val); /* get logical address */ if ((u >= DRM_NUMDR) || /* invalid unit? */ (drm_unit[u].flags & UNIT_DIS) || /* disabled unit? */ (l == 0) || (l > DRM_NUMLD)) { /* invalid log drum? */ ch6_err_disc (ch, U_DRM, CHF_TRC); /* disconnect */ drm_sta = DRM_IDLE; return SCPE_OK; } drm_da = DRM_GETDA (val); /* get drum addr */ cp = GET_POS (drm_time); /* current pos in sec */ dp = (drm_da & DRM_SCMASK) - cp; /* delta to desired pos */ if (dp <= 0) /* if neg, add rev */ dp = dp + DRM_NUMWDS; sim_activate (&drm_unit[u], dp * drm_time); /* schedule */ if (drm_op) /* if write, get word */ ch6_req_wr (ch, U_DRM); drm_sta = DRM_DATA; drm_chob = 0; /* clr, inval buffer */ drm_chob_v = 0; } else { drm_chob = val & DMASK; drm_chob_v = 1; } return SCPE_OK; } /* Unit service - this code assumes the entire drum is buffered */ t_stat drm_svc (UNIT *uptr) { t_uint64 *fbuf = (t_uint64 *) uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? */ ch6_err_disc (drm_ch, U_DRM, CHF_TRC); /* set TRC, disc */ drm_sta = DRM_IDLE; /* drum is idle */ return SCPE_UNATT; } if (drm_da >= DRM_SIZE) { /* nx logical drum? */ ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */ drm_sta = DRM_IDLE; /* drum is idle */ return SCPE_OK; } switch (drm_sta) { /* case on state */ case DRM_DATA: /* data */ if (drm_op) { /* write? */ if (drm_chob_v) /* valid? clear */ drm_chob_v = 0; else if (ch6_qconn (drm_ch, U_DRM)) /* no, chan conn? */ ind_ioc = 1; /* io check */ fbuf[drm_da] = drm_chob; /* get data */ if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; if (!drm_da_incr ()) ch6_req_wr (drm_ch, U_DRM); } else{ /* read */ ch6_req_rd (drm_ch, U_DRM, fbuf[drm_da], 0); /* send word to channel */ drm_da_incr (); } sim_activate (uptr, drm_time); /* next word */ break; case DRM_EOS: /* end sector */ if (ch6_qconn (drm_ch, U_DRM)) /* drum still conn? */ ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */ drm_sta = DRM_IDLE; /* drum is idle */ break; } /* end case */ return SCPE_OK; } /* Increment drum address - return true, set new state if end of sector */ t_bool drm_da_incr (void) { drm_da = drm_da + 1; if (drm_da & DRM_SCMASK) return FALSE; drm_sta = DRM_EOS; return TRUE; } /* Reset routine */ t_stat drm_reset (DEVICE *dptr) { uint32 i; drm_da = 0; drm_op = 0; drm_sta = DRM_IDLE; drm_chob = 0; drm_chob_v = 0; for (i = 0; i < dptr->numunits; i++) sim_cancel (dptr->units + i); return SCPE_OK; } simh-3.8.1/I7094/i7094_mt.c0000644000175000017500000010574411112035160012776 0ustar vlmvlm/* i7094_mt.c: IBM 7094 magnetic tape simulator Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt magtape simulator */ #include "i7094_defs.h" #include "sim_tape.h" #define UST u3 /* unit state */ #define UCH u4 /* channel number */ #define MTUF_V_LDN (MTUF_V_UF + 0) #define MTUF_LDN (1 << MTUF_V_LDN) #define MT_MAXFR ((1 << 18) + 2) #define QCHRONO(c,u) ((cpu_model & I_CT) && \ ((c) == CHRONO_CH) && ((u) == CHRONO_UNIT)) uint8 *mtxb[NUM_CHAN] = { NULL }; /* xfer buffer */ uint32 mt_unit[NUM_CHAN]; /* unit */ uint32 mt_bptr[NUM_CHAN]; uint32 mt_blnt[NUM_CHAN]; t_uint64 mt_chob[NUM_CHAN]; uint32 mt_chob_v[NUM_CHAN]; uint32 mt_tshort = 2; uint32 mt_twef = 25000; /* 50 msec */ uint32 mt_tstart = 29000; /* 58 msec */ uint32 mt_tstop = 10000; /* 20 msec */ uint32 mt_tword = 50; /* 125 usec */ static const uint8 odd_par[64] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }; static const char *tape_stat[] = { "OK", "TMK", "UNATT", "IOERR", "INVRECLNT", "FMT", "BOT", "EOM", "RECERR", "WRPROT" }; extern uint32 PC; extern uint32 cpu_model; extern uint32 ind_ioc; extern FILE *sim_deb; extern char *sel_name[]; t_stat mt_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 flags); t_stat mt_rec_end (UNIT *uptr); t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_boot (int32 unitno, DEVICE *dptr); t_stat mt_map_err (UNIT *uptr, t_stat st); extern uint32 chrono_rd (uint8 *buf, uint32 bufsiz); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ DIB mt_dib = { &mt_chsel, &mt_chwr }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTUF_LDN, 0, "high density", "HIGH", NULL }, { MTUF_LDN, MTUF_LDN, "low density", "LOW", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { 0 } }; UNIT mta_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mta_reg[] = { { ORDATA (UNIT, mt_unit[0], 5) }, { ORDATA (CHOB, mt_chob[0], 36) }, { FLDATA (CHOBV, mt_chob_v[0], 0) }, { DRDATA (BPTR, mt_bptr[0], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[0], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mta_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mta_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mtb_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mtb_reg[] = { { ORDATA (UNIT, mt_unit[1], 5) }, { ORDATA (CHOB, mt_chob[1], 36) }, { FLDATA (CHOBV, mt_chob_v[1], 0) }, { DRDATA (BPTR, mt_bptr[1], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[1], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mtb_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mtb_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mtc_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mtc_reg[] = { { ORDATA (UNIT, mt_unit[2], 5) }, { ORDATA (CHOB, mt_chob[2], 36) }, { FLDATA (CHOBV, mt_chob_v[2], 0) }, { DRDATA (BPTR, mt_bptr[2], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[2], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mtc_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mtc_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mtd_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mtd_reg[] = { { ORDATA (UNIT, mt_unit[3], 5) }, { ORDATA (CHOB, mt_chob[3], 36) }, { FLDATA (CHOBV, mt_chob_v[3], 0) }, { DRDATA (BPTR, mt_bptr[3], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[3], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mtd_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mtd_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mte_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mte_reg[] = { { ORDATA (UNIT, mt_unit[4], 5) }, { ORDATA (CHOB, mt_chob[4], 36) }, { FLDATA (CHOBV, mt_chob_v[4], 0) }, { DRDATA (BPTR, mt_bptr[4], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[4], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mte_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mte_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mtf_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mtf_reg[] = { { ORDATA (UNIT, mt_unit[5], 5) }, { ORDATA (CHOB, mt_chob[5], 36) }, { FLDATA (CHOBV, mt_chob_v[5], 0) }, { DRDATA (BPTR, mt_bptr[5], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[5], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mtf_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mtf_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mtg_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mtg_reg[] = { { ORDATA (UNIT, mt_unit[6], 5) }, { ORDATA (CHOB, mt_chob[6], 36) }, { FLDATA (CHOBV, mt_chob_v[6], 0) }, { DRDATA (BPTR, mt_bptr[6], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[6], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mtg_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mtg_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; UNIT mth_unit[] = { { UDATA (NULL, UNIT_DIS, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } }; REG mth_reg[] = { { ORDATA (UNIT, mt_unit[7], 5) }, { ORDATA (CHOB, mt_chob[7], 36) }, { FLDATA (CHOBV, mt_chob_v[7], 0) }, { DRDATA (BPTR, mt_bptr[7], 16), PV_LEFT }, { DRDATA (BLNT, mt_blnt[7], 16), PV_LEFT }, { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, { URDATA (UST, mth_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, { URDATA (POS, mth_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR + 1, PV_LEFT | REG_RO) }, { NULL } }; DEVICE mt_dev[NUM_CHAN] = { { "MTA", mta_unit, mta_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DEBUG }, { "MTB", mtb_unit, mtb_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTC", mtc_unit, mtc_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTD", mtd_unit, mtd_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTE", mte_unit, mte_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTF", mtf_unit, mtf_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTG", mtg_unit, mtg_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG }, { "MTH", mth_unit, mth_reg, mt_mod, MT_NUMDR + 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &sim_tape_detach, &mt_dib, DEV_DIS|DEV_DEBUG } }; /* Select controller Inputs: ch = channel cmd = select command unit = unit Outputs: status = SCPE_OK if ok STOP_STALL if busy error code if error */ static const int mt_must_att[CHSL_NUM] = { 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 }; static const int mt_will_wrt[CHSL_NUM] = { 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }; t_stat mt_chsel (uint32 ch, uint32 cmd, uint32 unit) { UNIT *uptr; uint32 u = unit & 017; if ((ch >= NUM_CHAN) || (cmd == 0) || (cmd >= CHSL_NUM)) return SCPE_IERR; /* invalid arg? */ if (mt_dev[ch].flags & DEV_DIS) /* disabled? */ return STOP_NXDEV; if ((u == 0) || (u > MT_NUMDR)) /* valid unit? */ return STOP_NXDEV; uptr = mt_dev[ch].units + u; /* get unit ptr */ if (uptr->flags & UNIT_DIS) /* disabled? */ return STOP_NXDEV; if (mt_unit[ch] || sim_is_active (uptr)) /* ctrl or unit busy? */ return ERR_STALL; /* stall */ if (QCHRONO (ch, u)) { /* Chronolog clock? */ if (cmd != CHSL_RDS) /* only reads */ return STOP_ILLIOP; sim_activate (uptr, mt_tword); /* responds quickly */ } else { /* real tape */ if (!(uptr->flags & UNIT_ATT) && mt_must_att[cmd]) /* unit unatt? */ return SCPE_UNATT; if (sim_tape_wrp (uptr) && mt_will_wrt[cmd]) /* unit wrp && write? */ return STOP_WRP; if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d %s, pos = %d\n", mt_dev[ch].name, u, sel_name[cmd], uptr->pos); switch (cmd) { /* case on cmd */ case CHSL_RDS: case CHSL_WRS: case CHSL_BSR: case CHSL_BSF: /* rd, wr, backspace */ sim_activate (uptr, mt_tstart); /* schedule op */ break; case CHSL_WEF: /* write eof? */ sim_activate (uptr, mt_twef); /* schedule op */ break; case CHSL_RUN: sim_activate (uptr, mt_tshort); /* schedule quick event */ break; case CHSL_REW: case CHSL_SDN: /* rew, rew/unl, set det */ sim_activate (uptr, mt_tshort); /* schedule quick event */ break; default: return SCPE_IERR; } /* end switch */ } /* end else */ uptr->UST = cmd; /* set cmd */ mt_unit[ch] = unit & 0777; /* save unit */ return SCPE_OK; } /* Channel write routine */ t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) { int32 k, u; uint8 by, *xb; UNIT *uptr; if (ch >= NUM_CHAN) /* invalid chan? */ return SCPE_IERR; xb = mtxb[ch]; /* get xfer buf */ u = mt_unit[ch] & 017; if ((xb == NULL) || (u > MT_NUMDR)) /* invalid args? */ return SCPE_IERR; uptr = mt_dev[ch].units + u; /* get unit */ mt_chob[ch] = val & DMASK; /* save word from chan */ mt_chob_v[ch] = 1; /* set valid */ if (uptr->UST == (CHSL_WRS|CHSL_2ND)) { /* data write? */ for (k = 30; /* proc 6 bytes */ (k >= 0) && (mt_bptr[ch] < MT_MAXFR); k = k - 6) { by = (uint8) ((val >> k) & 077); /* get byte */ if ((mt_unit[ch] & 020) == 0) { /* BCD? */ if (by == 0) /* cvt bin 0 */ by = BCD_ZERO; else if (by & 020) /* invert zones */ by = by ^ 040; if (!odd_par[by]) /* even parity */ by = by | 0100; } else if (odd_par[by]) /* bin, odd par */ by = by | 0100; xb[mt_bptr[ch]++] = by; /* put in buffer */ } if (eorfl) return mt_rec_end (uptr); /* EOR? write rec */ return SCPE_OK; } return SCPE_IERR; } /* Unit timeout */ t_stat mt_svc (UNIT *uptr) { uint32 i, u, ch = uptr->UCH; /* get channel number */ uint8 by, *xb = mtxb[ch]; /* get xfer buffer */ t_uint64 dat; t_mtrlnt bc; t_stat r; if (xb == NULL) /* valid buffer? */ return SCPE_IERR; u = uptr - mt_dev[ch].units; switch (uptr->UST) { /* case on state */ case CHSL_RDS: /* read start */ if (QCHRONO (ch, mt_unit[ch] & 017)) /* Chronolog clock? */ bc = chrono_rd (xb, MT_MAXFR); /* read clock */ else { /* real tape */ r = sim_tape_rdrecf (uptr, xb, &bc, MT_MAXFR); /* read record */ if (r = mt_map_err (uptr, r)) /* map status */ return r; if (mt_unit[ch] == 0) /* disconnected? */ return SCPE_OK; } /* end else Chrono */ if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ mt_unit[ch] = 0; /* clr ctrl busy */ return SCPE_OK; } for (i = bc; i < (bc + 6); i++) /* extra 0's */ xb[i] = 0; mt_bptr[ch] = 0; /* set ptr, lnt */ mt_blnt[ch] = bc; uptr->UST = CHSL_RDS|CHSL_2ND; /* next state */ sim_activate (uptr, mt_tword); break; case CHSL_RDS|CHSL_2ND: /* read word */ for (i = 0, dat = 0; i < 6; i++) { /* proc 6 bytes */ by = xb[mt_bptr[ch]++] & 077; /* get next byte */ if ((mt_unit[ch] & 020) == 0) { /* BCD? */ if (by == BCD_ZERO) /* cvt BCD 0 */ by = 0; else if (by & 020) /* invert zones */ by = by ^ 040; } dat = (dat << 6) | ((t_uint64) by); } if (mt_bptr[ch] >= mt_blnt[ch]) { /* end of record? */ ch6_req_rd (ch, mt_unit[ch], dat, CH6DF_EOR); uptr->UST = CHSL_RDS|CHSL_3RD; /* next state */ sim_activate (uptr, mt_tstop); /* long timing */ } else { ch6_req_rd (ch, mt_unit[ch], dat, 0); /* send to channel */ sim_activate (uptr, mt_tword); /* next word */ } break; case CHSL_RDS|CHSL_3RD: /* end record */ if (ch6_qconn (ch, mt_unit[ch])) { /* ch still conn? */ uptr->UST = CHSL_RDS; /* initial state */ sim_activate (uptr, mt_tshort); /* sched next record */ } else mt_unit[ch] = 0; /* clr ctrl busy */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d RDS complete, pos = %d, %s\n", mt_dev[ch].name, u, uptr->pos, mt_unit[ch]? "continuing": "disconnecting"); return SCPE_OK; case CHSL_WRS: /* write start */ if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ mt_unit[ch] = 0; /* clr ctrl busy */ return SCPE_OK; /* (writes blank tape) */ } mt_bptr[ch] = 0; /* init buffer */ uptr->UST = CHSL_WRS|CHSL_2ND; /* next state */ ch6_req_wr (ch, mt_unit[ch]); /* request channel */ mt_chob[ch] = 0; /* clr, inval buffer */ mt_chob_v[ch] = 0; sim_activate (uptr, mt_tword); /* wait for word */ break; case CHSL_WRS|CHSL_2ND: /* write word */ if (!ch6_qconn (ch, mt_unit[ch])) /* disconnected? */ return mt_rec_end (uptr); /* write record */ if (mt_chob_v[ch]) /* valid? clear */ mt_chob_v[ch] = 0; else ind_ioc = 1; /* no, io check */ ch6_req_wr (ch, mt_unit[ch]); /* request channel */ sim_activate (uptr, mt_tword); /* next word */ break; case CHSL_WRS|CHSL_3RD: /* write stop */ if (ch6_qconn (ch, mt_unit[ch])) { /* chan active? */ uptr->UST = CHSL_WRS; /* initial state */ sim_activate (uptr, mt_tshort); /* sched next record */ } else mt_unit[ch] = 0; /* clr ctrl busy */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d WRS complete, pos = %d, %s\n", mt_dev[ch].name, u, uptr->pos, mt_unit[ch]? "continuing": "disconnecting"); return SCPE_OK; case CHSL_BSR: /* backspace rec */ r = sim_tape_sprecr (uptr, &bc); /* space backwards */ mt_unit[ch] = 0; /* clr ctrl busy */ ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d BSR complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); if (r == MTSE_TMK) /* allow tape mark */ return SCPE_OK; return mt_map_err (uptr, r); case CHSL_BSF: /* backspace file */ while ((r = sim_tape_sprecr (uptr, &bc)) == MTSE_OK) ; mt_unit[ch] = 0; /* clr ctrl busy */ ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d BSF complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); if (r == MTSE_TMK) /* allow tape mark */ return SCPE_OK; return mt_map_err (uptr, r); /* map others */ case CHSL_WEF: /* write eof */ r = sim_tape_wrtmk (uptr); /* write tape mark */ mt_unit[ch] = 0; /* clr ctrl busy */ ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d WEF complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); return mt_map_err (uptr, r); case CHSL_REW: case CHSL_RUN: /* rewind, unload */ uptr->UST = uptr->UST | CHSL_2ND; /* set 2nd state */ sim_activate (uptr, mt_tstart); /* reactivate */ mt_unit[ch] = 0; /* clr ctrl busy */ ch6_end_nds (ch); /* disconnect */ return SCPE_OK; case CHSL_REW | CHSL_2ND: sim_tape_rewind (uptr); if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d REW complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); return SCPE_OK; case CHSL_RUN | CHSL_2ND: sim_tape_detach (uptr); if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d RUN complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); return SCPE_OK; case CHSL_SDN: if (mt_unit[ch] & 020) /* set density flag */ uptr->flags = uptr-> flags & ~MTUF_LDN; else uptr->flags = uptr->flags | MTUF_LDN; mt_unit[ch] = 0; /* clr ctrl busy */ ch6_end_nds (ch); /* disconnect */ if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d SDN complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); return SCPE_OK; default: return SCPE_IERR; } return SCPE_OK; } /* End record routine */ t_stat mt_rec_end (UNIT *uptr) { uint32 ch = uptr->UCH; uint8 *xb = mtxb[ch]; t_stat r; if (mt_bptr[ch]) { /* any data? */ if (xb == NULL) return SCPE_IERR; r = sim_tape_wrrecf (uptr, xb, mt_bptr[ch]); /* write record */ if (r = mt_map_err (uptr, r)) /* map error */ return r; } uptr->UST = CHSL_WRS|CHSL_3RD; /* next state */ sim_cancel (uptr); /* cancel current */ sim_activate (uptr, mt_tstop); /* long timing */ return SCPE_OK; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { uint32 ch = uptr->UCH; uint32 u = mt_unit[ch]; uint32 up = uptr - mt_dev[ch].units; if ((st != MTSE_OK) && DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, ">>%s%d status = %s, pos = %d\n", mt_dev[ch].name, up, tape_stat[st], uptr->pos); switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ ch6_err_disc (ch, u, CHF_TRC); mt_unit[ch] = 0; /* disconnect */ return SCPE_IERR; case MTSE_IOERR: /* IO error */ ch6_err_disc (ch, u, CHF_TRC); mt_unit[ch] = 0; /* disconnect */ return SCPE_IOERR; case MTSE_INVRL: /* invalid rec lnt */ ch6_err_disc (ch, u, CHF_TRC); mt_unit[ch] = 0; /* disconnect */ return SCPE_MTRLNT; case MTSE_WRP: /* write protect */ ch6_err_disc (ch, u, 0); mt_unit[ch] = 0; /* disconnect */ return STOP_WRP; case MTSE_EOM: /* end of medium */ case MTSE_TMK: /* tape mark */ ch6_err_disc (ch, u, CHF_EOF); mt_unit[ch] = 0; /* disconnect */ break; case MTSE_RECE: /* record in error */ ch6_set_flags (ch, u, CHF_TRC); break; case MTSE_BOT: /* reverse into BOT */ ch6_set_flags (ch, u, CHF_BOT); break; case MTSE_OK: /* no error */ break; } return SCPE_OK; } /* Magtape reset */ t_stat mt_reset (DEVICE *dptr) { uint32 ch = dptr - &mt_dev[0]; uint32 j; REG *rptr; UNIT *uptr; if (mtxb[ch] == NULL) mtxb[ch] = (uint8 *) calloc (MT_MAXFR + 6, sizeof (uint8)); if (mtxb[ch] == NULL) /* allocate buffer */ return SCPE_MEM; rptr = find_reg ("BUF", NULL, dptr); /* update reg ptr */ if (rptr == NULL) return SCPE_IERR; rptr->loc = (void *) mtxb[ch]; mt_unit[ch] = 0; /* clear busy */ mt_bptr[ch] = 0; /* clear buf ptrs */ mt_blnt[ch] = 0; mt_chob[ch] = 0; mt_chob_v[ch] = 0; for (j = 1; j <= MT_NUMDR; j++) { /* for all units */ uptr = dptr->units + j; uptr->UST = 0; /* clear state */ uptr->UCH = ch; sim_cancel (uptr); /* stop activity */ } /* end for */ return SCPE_OK; /* done */ } /* Magtape attach */ t_stat mt_attach (UNIT *uptr, char *cptr) { uptr->flags = uptr->flags & ~MTUF_LDN; /* start as hi den */ return sim_tape_attach (uptr, cptr); } /* Magtape boot */ #define BOOT_START 01000 static const t_uint64 boot_rom[5] = { 0076200000000 + U_MTBIN - 1, /* RDS MT_binary */ 0054000000000 + BOOT_START + 4, /* RCHA *+3 */ 0054400000000, /* LCHA 0 */ 0002100000001, /* TTR 1 */ 0500003000000, /* IOCT 0,,3 */ }; t_stat mt_boot (int32 unitno, DEVICE *dptr) { uint32 i, chan; extern t_uint64 *M; chan = dptr - &mt_dev[0] + 1; WriteP (BOOT_START, boot_rom[0] + unitno + (chan << 9)); for (i = 1; i < 5; i++) WriteP (BOOT_START + i, boot_rom[i]); PC = BOOT_START; return SCPE_OK; } simh-3.8.1/I7094/i7094_io.c0000644000175000017500000021573411111661606012777 0ustar vlmvlm/* i7094_io.c: IBM 7094 I/O subsystem (channels) Copyright (c) 2003-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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. chana..chanh I/O channels Notes on channels and CTSS. - CTSS B-core is supported by the addition of a 16th bit to the current address field of the channel command. Both the channel location counter and the channel current address register are widened to 16b. Thus, channel programs can run in B-core, and channel transfers can access B-core. CTSS assumes that a channel command which starts a transfer in B-core will not access A-core; the 16th bit does not increment. - The channel start commands (RCHx and LCHx) incorporate the A-core/B-core select as part of effective address generation. CTSS does not relocate RCHx and LCHx target addresses; because the relocation indicator is always zero, it's impossible to tell whether the protection indicator affects address generation. - The CTSS protection RPQ does not cover channel operations. Thus, CTSS must inspect and vet all channel programs initiated by user mode programs, notably the background processor FMS. CTSS inspects in-progress 7607 channel programs to make sure than either the nostore bit or the B-core bit is set; thus, SCHx must store all 16b of the current address. */ #include "i7094_defs.h" #define CHAMASK ((cpu_model & I_CT)? PAMASK: AMASK) /* chan addr mask */ #define CHAINC(x) (((x) & ~AMASK) | (((x) + 1) & AMASK)) typedef struct { char *name; uint32 flags; } DEV_CHAR; uint32 ch_sta[NUM_CHAN]; /* channel state */ uint32 ch_dso[NUM_CHAN]; /* data select op */ uint32 ch_dsu[NUM_CHAN]; /* data select unit */ uint32 ch_ndso[NUM_CHAN]; /* non-data select op */ uint32 ch_ndsu[NUM_CHAN]; /* non-data select unit */ uint32 ch_flags[NUM_CHAN]; /* flags */ uint32 ch_clc[NUM_CHAN]; /* chan loc ctr */ uint32 ch_op[NUM_CHAN]; /* channel op */ uint32 ch_wc[NUM_CHAN]; /* word count */ uint32 ch_ca[NUM_CHAN]; /* core address */ uint32 ch_lcc[NUM_CHAN]; /* control cntr (7909) */ uint32 ch_cnd[NUM_CHAN]; /* cond reg (7909) */ uint32 ch_sms[NUM_CHAN]; /* cond mask reg (7909) */ t_uint64 ch_ar[NUM_CHAN]; /* assembly register */ uint32 ch_idf[NUM_CHAN]; /* channel input data flags */ DEVICE *ch2dev[NUM_CHAN] = { NULL }; uint32 ch_tpoll = 5; /* channel poll */ extern t_uint64 *M; extern uint32 cpu_model, data_base; extern uint32 hst_ch; extern uint32 ch_req; extern uint32 chtr_inht, chtr_inhi, chtr_enab; extern uint32 ind_ioc; extern uint32 chtr_clk; extern DEVICE cdr_dev, cdp_dev; extern DEVICE lpt_dev; extern DEVICE mt_dev[NUM_CHAN]; extern DEVICE drm_dev; extern DEVICE dsk_dev; extern DEVICE com_dev; extern int32 sim_brk_summ; t_stat ch_reset (DEVICE *dptr); t_stat ch6_svc (UNIT *uptr); t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); DEVICE *ch_find_dev (uint32 ch, uint32 unit); t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta); t_bool ch6_rd_putw (uint32 ch); t_stat ch6_wr_getw (uint32 ch, t_bool eorz); t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld); t_stat ch6_ioxt (uint32 ch); void ch6_iosp_cclr (uint32 ch); t_stat ch9_new_cmd (uint32 ch); t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir); t_stat ch9_sel (uint32 ch, uint32 sel); t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl); t_stat ch9_rd_putw (uint32 ch); t_stat ch9_wr_getw (uint32 ch); void ch9_eval_int (uint32 ch, uint32 iflags); DEVICE *ch_map_flags (uint32 ch, int32 fl); extern CTAB *sim_vm_cmd; extern t_stat ch_bkpt (uint32 ch, uint32 clc); const uint32 col_masks[12] = { /* row 9,8,..,0,11,12 */ 00001, 00002, 00004, 00010, 00020, 00040, 00100, 00200, 00400, 01000, 02000, 04000 }; const t_uint64 bit_masks[36] = { 0000000000001, 0000000000002, 0000000000004, 0000000000010, 0000000000020, 0000000000040, 0000000000100, 0000000000200, 0000000000400, 0000000001000, 0000000002000, 0000000004000, 0000000010000, 0000000020000, 0000000040000, 0000000100000, 0000000200000, 0000000400000, 0000001000000, 0000002000000, 0000004000000, 0000010000000, 0000020000000, 0000040000000, 0000100000000, 0000200000000, 0000400000000, 0001000000000, 0002000000000, 0004000000000, 0010000000000, 0020000000000, 0040000000000, 0100000000000, 0200000000000, 0400000000000 }; const DEV_CHAR dev_table[] = { { "729", 0 }, { "TAPE", 0 }, { "7289", DEV_7289 }, { "DRUM", DEV_7289 }, { "7631", DEV_7909|DEV_7631 }, { "FILE", DEV_7909|DEV_7631 }, { "7750", DEV_7909|DEV_7750 }, { "COMM", DEV_7909|DEV_7750 }, { NULL }, }; const char *sel_name[] = { "UNK", "RDS", "WRS", "SNS", "CTL", "FMT", "UNK", "UNK", "WEF", "WBT", "BSR", "BSF", "REW", "RUN", "SDN", "UNK" }; /* Channel data structures */ UNIT ch_unit[NUM_CHAN] = { { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) }, { UDATA (&ch6_svc, 0, 0) } }; MTAB ch_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL, NULL, &ch_show_type, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "ENABLED", &ch_set_enable, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "DISABLED", &ch_set_disable, NULL, NULL }, { 0 } }; REG cha_reg[] = { { ORDATA (STA, ch_sta[CH_A], 8) }, { ORDATA (DSC, ch_dso[CH_A], 4) }, { ORDATA (DSU, ch_dsu[CH_A], 9) }, { ORDATA (NDSC, ch_ndso[CH_A], 4) }, { ORDATA (NDSU, ch_ndsu[CH_A], 9) }, { ORDATA (FLAGS, ch_flags[CH_A], 30) }, { ORDATA (IDF, ch_idf[CH_A], 2) }, { ORDATA (OP, ch_op[CH_A], 5) }, { ORDATA (CLC, ch_clc[CH_A], 16) }, { ORDATA (WC, ch_wc[CH_A], 15) }, { ORDATA (CA, ch_ca[CH_A], 16) }, { ORDATA (AR, ch_ar[CH_A], 36) }, { ORDATA (CND, ch_cnd[CH_A], 6), REG_HRO }, { ORDATA (LCC, ch_lcc[CH_A], 6), REG_HRO }, { ORDATA (SMS, ch_sms[CH_A], 7), REG_HRO }, { 0 } }; REG chb_reg[] = { { ORDATA (STATE, ch_sta[CH_B], 8) }, { ORDATA (DSC, ch_dso[CH_B], 4) }, { ORDATA (DSU, ch_dsu[CH_B], 9) }, { ORDATA (NDSC, ch_ndso[CH_B], 4) }, { ORDATA (NDSU, ch_ndsu[CH_B], 9) }, { ORDATA (FLAGS, ch_flags[CH_B], 30) }, { ORDATA (IDF, ch_idf[CH_B], 2) }, { ORDATA (OP, ch_op[CH_B], 5) }, { ORDATA (CLC, ch_clc[CH_B], 16) }, { ORDATA (WC, ch_wc[CH_B], 15) }, { ORDATA (CA, ch_ca[CH_B], 16) }, { ORDATA (AR, ch_ar[CH_B], 36) }, { ORDATA (CND, ch_cnd[CH_B], 6) }, { ORDATA (LCC, ch_lcc[CH_B], 6) }, { ORDATA (SMS, ch_sms[CH_B], 7) }, { 0 } }; REG chc_reg[] = { { ORDATA (STATE, ch_sta[CH_C], 8) }, { ORDATA (DSC, ch_dso[CH_C], 4) }, { ORDATA (DSU, ch_dsu[CH_C], 9) }, { ORDATA (NDSC, ch_ndso[CH_C], 4) }, { ORDATA (NDSU, ch_ndsu[CH_C], 9) }, { ORDATA (FLAGS, ch_flags[CH_C], 30) }, { ORDATA (IDF, ch_idf[CH_C], 2) }, { ORDATA (OP, ch_op[CH_C], 5) }, { ORDATA (CLC, ch_clc[CH_C], 16) }, { ORDATA (WC, ch_wc[CH_C], 15) }, { ORDATA (CA, ch_ca[CH_C], 16) }, { ORDATA (AR, ch_ar[CH_C], 36) }, { ORDATA (CND, ch_cnd[CH_C], 6) }, { ORDATA (LCC, ch_lcc[CH_C], 6) }, { ORDATA (SMS, ch_sms[CH_C], 7) }, { 0 } }; REG chd_reg[] = { { ORDATA (STATE, ch_sta[CH_D], 8) }, { ORDATA (DSC, ch_dso[CH_D], 4) }, { ORDATA (DSU, ch_dsu[CH_D], 9) }, { ORDATA (NDSC, ch_ndso[CH_D], 4) }, { ORDATA (NDSU, ch_ndsu[CH_D], 9) }, { ORDATA (FLAGS, ch_flags[CH_D], 30) }, { ORDATA (IDF, ch_idf[CH_D], 2) }, { ORDATA (OP, ch_op[CH_D], 5) }, { ORDATA (CLC, ch_clc[CH_D], 16) }, { ORDATA (WC, ch_wc[CH_D], 15) }, { ORDATA (CA, ch_ca[CH_D], 16) }, { ORDATA (AR, ch_ar[CH_D], 36) }, { ORDATA (CND, ch_cnd[CH_D], 6) }, { ORDATA (LCC, ch_lcc[CH_D], 6) }, { ORDATA (SMS, ch_sms[CH_D], 7) }, { 0 } }; REG che_reg[] = { { ORDATA (STATE, ch_sta[CH_E], 8) }, { ORDATA (DSC, ch_dso[CH_E], 4) }, { ORDATA (DSU, ch_dsu[CH_E], 9) }, { ORDATA (NDSC, ch_ndso[CH_E], 4) }, { ORDATA (NDSU, ch_ndsu[CH_E], 9) }, { ORDATA (FLAGS, ch_flags[CH_E], 30) }, { ORDATA (IDF, ch_idf[CH_E], 2) }, { ORDATA (OP, ch_op[CH_E], 5) }, { ORDATA (CLC, ch_clc[CH_E], 16) }, { ORDATA (WC, ch_wc[CH_E], 15) }, { ORDATA (CA, ch_ca[CH_E], 16) }, { ORDATA (AR, ch_ar[CH_E], 36) }, { ORDATA (CND, ch_cnd[CH_E], 6) }, { ORDATA (LCC, ch_lcc[CH_E], 6) }, { ORDATA (SMS, ch_sms[CH_E], 7) }, { 0 } }; REG chf_reg[] = { { ORDATA (STATE, ch_sta[CH_F], 8) }, { ORDATA (DSC, ch_dso[CH_F], 4) }, { ORDATA (DSU, ch_dsu[CH_F], 9) }, { ORDATA (NDSC, ch_ndso[CH_F], 4) }, { ORDATA (NDSU, ch_ndsu[CH_F], 9) }, { ORDATA (FLAGS, ch_flags[CH_F], 30) }, { ORDATA (IDF, ch_idf[CH_F], 2) }, { ORDATA (OP, ch_op[CH_F], 5) }, { ORDATA (CLC, ch_clc[CH_F], 16) }, { ORDATA (WC, ch_wc[CH_F], 15) }, { ORDATA (CA, ch_ca[CH_F], 16) }, { ORDATA (AR, ch_ar[CH_F], 36) }, { ORDATA (CND, ch_cnd[CH_F], 6) }, { ORDATA (LCC, ch_lcc[CH_F], 6) }, { ORDATA (SMS, ch_sms[CH_F], 7) }, { 0 } }; REG chg_reg[] = { { ORDATA (STATE, ch_sta[CH_G], 8) }, { ORDATA (DSC, ch_dso[CH_G], 4) }, { ORDATA (DSU, ch_dsu[CH_G], 9) }, { ORDATA (NDSC, ch_ndso[CH_G], 4) }, { ORDATA (NDSU, ch_ndsu[CH_G], 9) }, { ORDATA (FLAGS, ch_flags[CH_G], 30) }, { ORDATA (IDF, ch_idf[CH_G], 2) }, { ORDATA (OP, ch_op[CH_G], 5) }, { ORDATA (CLC, ch_clc[CH_G], 16) }, { ORDATA (WC, ch_wc[CH_G], 15) }, { ORDATA (CA, ch_ca[CH_G], 16) }, { ORDATA (AR, ch_ar[CH_G], 36) }, { ORDATA (CND, ch_cnd[CH_G], 6) }, { ORDATA (LCC, ch_lcc[CH_G], 6) }, { ORDATA (SMS, ch_sms[CH_G], 7) }, { 0 } }; REG chh_reg[] = { { ORDATA (STATE, ch_sta[CH_H], 8) }, { ORDATA (DSC, ch_dso[CH_H], 4) }, { ORDATA (DSU, ch_dsu[CH_H], 9) }, { ORDATA (NDSC, ch_ndso[CH_H], 4) }, { ORDATA (NDSU, ch_ndsu[CH_H],9) }, { ORDATA (FLAGS, ch_flags[CH_H], 30) }, { ORDATA (IDF, ch_idf[CH_H], 2) }, { ORDATA (OP, ch_op[CH_H], 5) }, { ORDATA (CLC, ch_clc[CH_H], 16) }, { ORDATA (WC, ch_wc[CH_H], 15) }, { ORDATA (CA, ch_ca[CH_H], 16) }, { ORDATA (AR, ch_ar[CH_H], 36) }, { ORDATA (CND, ch_cnd[CH_H], 6) }, { ORDATA (LCC, ch_lcc[CH_H], 6) }, { ORDATA (SMS, ch_sms[CH_H], 7) }, { 0 } }; DEVICE ch_dev[NUM_CHAN] = { { "CHANA", &ch_unit[CH_A], cha_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, 0 }, { "CHANB", &ch_unit[CH_B], chb_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHANC", &ch_unit[CH_C], chc_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHAND", &ch_unit[CH_D], chd_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHANE", &ch_unit[CH_E], che_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHANF", &ch_unit[CH_F], chf_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHANG", &ch_unit[CH_G], chg_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }, { "CHANH", &ch_unit[CH_H], chh_reg, ch_mod, 1, 8, 8, 1, 8, 8, NULL, NULL, &ch_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS } }; /* 7607 channel overview Channel variables: ch_sta channel state ch_dso, ch_dsu operation and unit for current data select ch_ndso, ch_ndsu operation and unit for current non-data select ch_clc current location counter ch_ca memory addres ch_wc word count ch_op channel opcode (bits ) ch_flags channel flags States of a channel IDLE - channel is not in operation RDS, WDS: -> DSW if device is idle, schedule device device timeout drives next transition -> stall if device is busy repeat until device is idle other I/O: -> NDS if device is idle, schedule device device timeout drives next transition -> stall if device is busy repeat until device is idle chan reset: -> IDLE PDS (PNDS) - channel is polling device to start data (non-data) select chan timeout: -> DSW (NDS) if device is idle device timeout drives next transition -> no change if device is busy, schedule channel chan reset: -> IDLE DSW - channel is waiting for channel start command dev timeout: -> IDLE if no stacked non-data select -> PNDS if stacked non-data select channel timeout drives next transition start chan: -> DSX if chan program transfers data device timeout drives next transition -> IDLE if channel disconnects, no stacked NDS -> PNDS if channel disconnects, stacked NDS channel timeout drives next transition chan reset: -> IDLE DSX - channel is executing data select dev timeout: -> DSX if transfer not complete, reschedule device device timeout drives next transition -> DSW if channel command completes, CHF_LDW set -> IDLE if transfer complete, no stacked NDS, or if channel command completes, CHF_LDW clear -> PNDS if channel disconnects, stacked NDS channel timeout drives next transition start chan: -> DSX with CHF_LDW, CPU stall chan reset: -> IDLE NDS - channel is executing non-data select dev timeout: -> IDLE if transfer complete, no stacked DS -> PDS if channel disconnects, stacked DS channel timeout drives next transition chan reset: -> IDLE The channel has two interfaces to a device. The select routine: dev_select (uint32 ch, uint32 sel, uint32 unit) Returns can include device errors and ERR_STALL. If ERR_STALL, the device is busy. For I/O instructions, ERR_STALL stalls execution of the instruction until the device is not busy. For stacked command polls, ERR_STALL causes the poll to be repeated after a delay. The device write routine is used to place output data in the device write buffer. Channel transfers are driven by the channel. When a device needs to read or write data, it sets a channel request in ch_req. The channel process transfers the data and updates channel control parameters accordingly. Note that the channel may disconnect; in this case, the transfer completes 'correctly' from the point of view of the device. The channel transfer commands (IOxT) require the channel to 'hold' a new channel command in anticipation of the current transfer. If the channel is currently executing (CH6S_DSX) and a channel start is issued by the CPU, a 'start pending' flag is set and the CPU is stalled. When the channel reaches the end of an IOxT command, it checks the 'start pending' flag. If the flag is set, the channel sets itself to waiting and then requeues itself for one cycle later. The CPU tries the channel start, sees that the channel is waiting, and issues the new channel command. state op device channel IDLE RDS,WDS start I/O ->DSW DSW LCHx (timed wait) ->DSX DSX -- timeout, req svc (timed wait) transfer word timeout, req svc (timed wait) LCHx, stalls : timeout, EOR/EOC IOxT: ->DSW, resched DSW LCHx (timed wait) ->DSX, etc 7909 channel overview Channel variables: ch_sta channel state ch_clc current location counter ch_ca memory addres ch_wc word count ch_op channel opcode (bits ) ch_sms status mask ch_cond interrupt conditions ch_lcc control counter ch_flags channel flags States of a channel IDLE - channel is not in operation RDCx, SDCx, interrupt -> DSX DSX - channel is executing data select TWT, WTR -> IDLE The 7909 is more capable than the 7607 but also simpler in some ways. It has many more instructions, built in counters and status checking, and interrupts. But it has only two states and no concept of records. The 7909 read process is driven by the device: channel CTLR/SNS: send select device: schedule timeout device timeout: device to AR, request channel channel: AR to memory device timeout: device to AR, request channel channel: AR to memory : device timeout: set end, request channel channel: disconnect on CPYD, send STOP The 7909 write process is also driven by the device: channel CTL/CTLW: send select device: schedule timeout, request channel channel: memory to output buffer device timeout: output buffer to device, request channel channel: memory to output buffer device timeout: output buffer to device, request channel : channel: memory to output buffer device timeout: output buffer to device, set end, request channel channel: disconnect on CPYD, send STOP For both reads and writes, devices must implement an 'interblock' or 'interrecord' state that is long enough for the channel to see the end, disconnect, and send a stop signal. */ /* Data select - called by RDS or WDS instructions - 7607/7289 only - Channel is from address and has been corrected - Channel must be an enabled 7607 - If data select already in use, stall CPU - If non-data select is a write end-of-file, stall CPU - If channel is busy, stack command - Otherwise, start IO, set channel to waiting */ t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit) { t_stat r; if (ch >= NUM_CHAN) /* invalid arg? */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_7909) /* 7909? stop */ return STOP_7909; if (ch_dso[ch]) /* DS in use? */ return ERR_STALL; if (ch_ndso[ch] == CHSL_WEF) /* NDS = WEF? */ return ERR_STALL; if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */ r = ch6_sel (ch, ds, unit, CH6S_DSW); /* select device */ if (r != SCPE_OK) return r; } ch_dso[ch] = ds; /* set command, unit */ ch_dsu[ch] = unit; ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_CMD); /* clear flags */ ch_idf[ch] = 0; return SCPE_OK; } /* Non-data select - called by BSR, BSF, WEF, REW, RUN, SDS instructions - 7607 only - Channel is from address and has been corrected - Channel must be an enabled 7607 - If non-data select already in use, stall CPU - If data select is card or printer, stall CPU - If channel is busy, stack command - Otherwise, start IO, set channel to waiting */ t_stat ch_op_nds (uint32 ch, uint32 nds, uint32 unit) { DEVICE *dptr; t_stat r; if (ch >= NUM_CHAN) /* invalid arg? */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_7909) /* 7909? stop */ return STOP_7909; if (ch_ndso[ch]) /* NDS in use? */ return ERR_STALL; if (ch_dso[ch] && (dptr = ch_find_dev (ch, ch_dsu[ch])) /* DS, cd or lpt? */ && (dptr->flags & DEV_CDLP)) return ERR_STALL; if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */ r = ch6_sel (ch, nds, unit, CH6S_NDS); /* select device */ if (r != SCPE_OK) return r; } ch_ndso[ch] = nds; /* set command, unit */ ch_ndsu[ch] = unit; return SCPE_OK; } /* End of data select - called from channel - 7607/7289 only - If executing, set command trap flag - Set channel idle - If stacked nds, set up immediate channel timeout */ t_stat ch6_end_ds (uint32 ch) { if (ch >= NUM_CHAN) /* invalid arg? */ return STOP_NXCHN; ch_dso[ch] = ch_dsu[ch] = 0; /* no data select */ if (ch_ndso[ch]) { /* stacked non-data sel? */ sim_activate (ch_dev[ch].units, 0); /* immediate poll */ ch_sta[ch] = CH6S_PNDS; /* state = polling */ } else ch_sta[ch] = CHXS_IDLE; /* else state = idle */ return SCPE_OK; } /* End of non-data select - called from I/O device completion - 7607/7289 only - Set channel idle - If stacked ds, set up immediate channel timeout */ t_stat ch6_end_nds (uint32 ch) { if (ch >= NUM_CHAN) /* invalid arg? */ return STOP_NXCHN; ch_ndso[ch] = ch_ndsu[ch] = 0; /* no non-data select */ if (ch_dso[ch]) { /* stacked data sel? */ sim_activate (ch_dev[ch].units, 0); /* immediate poll */ ch_sta[ch] = CH6S_PDS; /* state = polling */ } else ch_sta[ch] = CHXS_IDLE; /* else state = idle */ return SCPE_OK; } /* Send select to device - 7607/7289 only */ t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta) { DEVICE *dptr; DIB *dibp; t_stat r; if (ch >= NUM_CHAN) /* invalid arg? */ return STOP_NXCHN; dptr = ch_find_dev (ch, unit); /* find device */ if (dptr == NULL) /* invalid device? */ return STOP_NXDEV; dibp = (DIB *) dptr->ctxt; r = dibp->chsel (ch, sel, unit); /* select device */ if (r == SCPE_OK) /* set status */ ch_sta[ch] = sta; return r; } /* Channel unit service - called to start stacked command - 7607 only */ t_stat ch6_svc (UNIT *uptr) { uint32 ch = uptr - &ch_unit[0]; /* get channel */ t_stat r; if (ch >= NUM_CHAN) /* invalid chan? */ return SCPE_IERR; switch (ch_sta[ch]) { /* case on state */ case CH6S_PDS: /* polling for ds */ r = ch6_sel (ch, ch_dso[ch], ch_dsu[ch], CH6S_DSW); break; case CH6S_PNDS: /* polling for nds */ r = ch6_sel (ch, ch_ndso[ch], ch_ndsu[ch], CH6S_NDS); break; default: return SCPE_OK; } if (r == ERR_STALL) { /* stalled? */ sim_activate (uptr, ch_tpoll); /* continue poll */ return SCPE_OK; } return r; } /* Map channel and unit number to device - all channels */ DEVICE *ch_find_dev (uint32 ch, uint32 unit) { if (ch >= NUM_CHAN) /* invalid arg? */ return NULL; if (ch_dev[ch].flags & (DEV_7909|DEV_7289)) return ch2dev[ch]; unit = unit & 0777; if (((unit >= U_MTBCD) && (unit <= (U_MTBCD + MT_NUMDR))) || ((unit >= U_MTBIN) && (unit <= (U_MTBIN + MT_NUMDR)))) return ch2dev[ch]; if (ch != 0) return NULL; if (unit == U_CDR) return &cdr_dev; if (unit == U_CDP) return &cdp_dev; if ((unit == U_LPBCD) || (unit == U_LPBIN)) return &lpt_dev; return NULL; } /* Start channel - channel is from opcode 7607: channel should have a data select operation pending (DSW state) 7909: channel should be idle (IDLE state) */ t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset) { t_uint64 ir; t_stat r; clc = clc | data_base; /* add A/B select */ if (ch >= NUM_CHAN) /* invalid argument? */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_7909) { /* 7909? */ if (ch_sta[ch] != CHXS_IDLE) /* must be idle */ return ERR_STALL; if (reset) { /* RDCx? */ ch_cnd[ch] = 0; /* clear conditions */ ch_clc[ch] = clc; /* set clc */ } else { /* SDCx */ if (BIT_TST (chtr_enab, CHTR_V_TWT + ch) && /* pending trap? */ (ch_flags[ch] & CHF_TWT)) return ERR_STALL; ch_clc[ch] = ch_ca[ch] & CHAMASK; /* finish WTR, TWT */ } ch_flags[ch] &= ~CHF_CLR_7909; /* clear flags, not IP */ ch_idf[ch] = 0; ch_sta[ch] = CHXS_DSX; /* set state */ return ch9_new_cmd (ch); /* start executing */ } /* 7607, 7289 */ if (reset) { /* reset? */ if (ch_sta[ch] == CHXS_DSX) ch_sta[ch] = CH6S_DSW; ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_TRC|CHF_CMD); ch_idf[ch] = 0; } switch (ch_sta[ch]) { /* case on chan state */ case CHXS_IDLE: /* idle */ ind_ioc = 1; /* IO check */ ir = ReadP (clc); /* get chan word */ ch_clc[ch] = CHAINC (clc); /* incr chan pc */ ch_wc[ch] = GET_DEC (ir); /* get word cnt */ ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ ch_op[ch] = (GET_OPD (ir) << 1) | /* get opcode */ ((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */ break; case CH6S_PNDS: /* NDS polling */ case CH6S_PDS: /* DS polling */ case CH6S_NDS: /* NDS executing */ return ERR_STALL; /* wait it out */ case CH6S_DSW: /* expecting command */ ch_sta[ch] = CHXS_DSX; /* update state */ if (ch_dev[ch].flags & DEV_7289) { /* drum channel? */ ir = ReadP (clc); /* read addr */ ch_clc[ch] = CHAINC (clc); /* incr chan pc */ if (r = ch9_wr (ch, ir, 0)) /* write to dev */ return r; } else ch_clc[ch] = clc; /* set clc */ return ch6_new_cmd (ch, TRUE); /* start channel */ case CHXS_DSX: /* executing */ ch_flags[ch] = ch_flags[ch] | CHF_LDW; /* flag pending LCH */ return ERR_STALL; /* stall */ } return SCPE_OK; } /* Store channel 7607/7289 stores op,ca,nostore,clc 7909 stores clc,,ca */ t_stat ch_op_store (uint32 ch, t_uint64 *dat) { if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN; if (ch_dev[ch].flags & DEV_7909) *dat = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_ADDR); else *dat = (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_DEC) | (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_ADDR) | (((t_uint64) (ch_op[ch] & 1)) << 16) | (((t_uint64) (ch_op[ch] & 016)) << 32); return SCPE_OK; } /* Store channel diagnostic 7607 is undefined 7289 stores IOC+??? 7909 stores 7909 lcc+flags */ t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat) { if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN; if (ch_flags[ch] & DEV_7289) *dat = ind_ioc? SIGN: 0; else if (ch_flags[ch] & DEV_7909) *dat = (((t_uint64) (ch_lcc[ch] & CHF_M_LCC)) << CHF_V_LCC) | (ch_flags[ch] & CHF_SDC_7909); else *dat = 0; return SCPE_OK; } /* Reset data channel 7607 responds to RDC 7909 responds to RIC */ t_stat ch_op_reset (uint32 ch, t_bool ch7909) { DEVICE *dptr; if (ch >= NUM_CHAN) /* invalid argument? */ return STOP_NXCHN; if (ch_dev[ch].flags & DEV_DIS) /* disabled? ok */ return SCPE_OK; if (ch_dev[ch].flags & DEV_7909) { /* 7909? */ if (!ch7909) /* wrong reset is NOP */ return SCPE_OK; dptr = ch2dev[ch]; /* get device */ } else { /* 7607, 7289 */ if (ch7909) /* wrong reset is err */ return STOP_NT7909; dptr = ch_find_dev (ch, ch_ndsu[ch]); /* find device */ } ch_reset (&ch_dev[ch]); /* reset channel */ if (dptr && dptr->reset) /* reset device */ dptr->reset (dptr); return SCPE_OK; } /* Channel process - called from main CPU loop. If the channel is unable to get a valid command, it will reschedule itself for the next cycle. The read process is basically synchronous with the device timeout routine. The device requests the channel and supplies the word to be stored in memory. In the next time slot, the channel stores the word in memory. */ t_stat ch_proc (uint32 ch) { t_stat r; if (ch >= NUM_CHAN) /* bad channel? */ return SCPE_IERR; ch_req &= ~REQ_CH (ch); /* clear request */ if (ch_dev[ch].flags & DEV_DIS) /* disabled? */ return SCPE_IERR; if (ch_dev[ch].flags & DEV_7909) { /* 7909 */ t_uint64 sr; uint32 csel, sc, tval, mask, ta; t_bool xfr; if (ch_flags[ch] & CHF_IRQ) { /* interrupt? */ ta = CHINT_CHA_SAV + (ch << 1); /* save location */ if (ch_sta[ch] == CHXS_IDLE) /* waiting? */ sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | ((t_uint64) ch_clc[ch] & CHAMASK); /* save CLC */ else sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | ((t_uint64) CHAINC (ch_clc[ch])); /* no, save CLC+1 */ ch_sta[ch] = CHXS_DSX; /* set running */ ch_flags[ch] = (ch_flags[ch] | CHF_INT) & /* set intr state */ ~(CHF_IRQ|CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); /* clr flags */ WriteP (ta, sr); /* write ca,,clc */ sr = ReadP (ta + 1); /* get chan cmd */ return ch9_exec_cmd (ch, sr); /* exec cmd */ } switch (ch_op[ch] & CH9_OPMASK) { /* switch on op */ case CH9_TWT: /* transfer of TWT */ case CH9_WTR: /* transfer of WTR */ case CH9_TCH: /* transfer */ ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ break; case CH9_TDC: /* decr & transfer */ if (ch_lcc[ch] != 0) { /* counter != 0? */ ch_lcc[ch]--; /* decr counter */ ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ } break; case CH9_TCM: /* transfer on cond */ csel = CH9D_COND (ch_wc[ch]); mask = CH9D_MASK (ch_wc[ch]); if (csel == 7) /* C = 7? mask mbz */ xfr = (mask == 0); else { /* C = 0..6 */ if (csel == 0) /* C = 0? test cond */ tval = ch_cnd[ch]; else tval = (uint32) (ch_ar[ch] >> (6 * (6 - csel))) & 077; if (ch_wc[ch] & CH9D_B11) xfr = ((tval & mask) == mask); else xfr = (tval == mask); } if (xfr) /* change CLC */ ch_clc[ch] = ch_ca[ch] & CHAMASK; break; case CH9_LIP: /* leave interrupt */ ta = CHINT_CHA_SAV + (ch << 1); /* save location */ ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */ ch_cnd[ch] = 0; /* clear channel cond */ ch_clc[ch] = (uint32) ReadP (ta) & CHAMASK; break; case CH9_LIPT: /* leave intr, transfer */ ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */ ch_cnd[ch] = 0; /* clear channel cond */ ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ break; case CH9_LAR: /* load assembly reg */ ch_ar[ch] = ReadP (ch_ca[ch]); break; case CH9_SAR: /* store assembly reg */ WriteP (ch_ca[ch], ch_ar[ch]); break; case CH9_SMS: /* load SMS reg */ ch_sms[ch] = CH9A_SMS (ch_ca[ch]); /* from eff addr */ if (!(ch_sms[ch] & CHSMS_IATN1) && /* atn inhbit off */ (ch_flags[ch] & CHF_ATN1)) /* and atn pending? */ ch9_eval_int (ch, 0); /* force int eval */ break; case CH9_LCC: /* load control cntr */ ch_lcc[ch] = CH9A_LCC (ch_ca[ch]); /* from eff addr */ break; case CH9_ICC: /* insert control cntr */ case CH9_ICCA: csel = CH9D_COND (ch_wc[ch]); /* get C */ if (csel == 0) ch_ar[ch] = /* C = 0? read SMS */ (ch_ar[ch] & 0777777770000) | ((t_uint64) ch_sms[ch]); else if (csel < 7) { /* else read cond cntr */ sc = 6 * (6 - csel); ch_ar[ch] = (ch_ar[ch] & ~(((t_uint64) 077) << sc)) | (((t_uint64) ch_lcc[ch]) << sc); } break; case CH9_XMT: /* transmit */ if (ch_wc[ch] == 0) break; sr = ReadP (ch_clc[ch]); /* next word */ WriteP (ch_ca[ch], sr); ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr pointers */ ch_ca[ch] = CHAINC (ch_ca[ch]); ch_wc[ch] = ch_wc[ch] - 1; /* decr count */ ch_req |= REQ_CH (ch); /* go again */ return SCPE_OK; case CH9_SNS: /* sense */ if (r = ch9_sel (ch, CHSL_SNS)) /* send sense to dev */ return r; ch_flags[ch] |= CHF_PRD; /* prepare to read */ break; /* next command */ case CH9_CTL: case CH9_CTLR: case CH9_CTLW: /* control */ if (((ch_wc[ch] & CH9D_NST) == 0) && /* N = 0 and */ !(ch_flags[ch] & CHF_EOR)) { /* end not set? */ sr = ReadP (ch_ca[ch]); ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ return ch9_wr (ch, sr, 0); /* write ctrl wd */ } ch_flags[ch] &= ~CHF_EOR; /* clear end */ if (ch_op[ch] == CH9_CTLR) { /* CTLR? */ if (r = ch9_sel (ch, CHSL_RDS)) /* send read sel */ return r; ch_flags[ch] |= CHF_PRD; /* prep to read */ ch_idf[ch] = 0; } else if (ch_op[ch] == CH9_CTLW) { /* CTLW? */ if (r = ch9_sel (ch, CHSL_WRS)) /* end write sel */ return r; ch_flags[ch] |= CHF_PWR; /* prep to write */ } break; case CH9_CPYD: /* copy & disc */ if ((ch_wc[ch] == 0) || (ch_flags[ch] & CHF_EOR)) { /* wc == 0 or EOR? */ if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) { ch_flags[ch] &= ~(CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); if (r = ch9_wr (ch, 0, CH9DF_STOP)) /* send stop */ return r; } if (ch_flags[ch] & CHF_EOR) { /* EOR? */ ch_flags[ch] &= ~CHF_EOR; /* clear flag */ break; /* new command */ } return SCPE_OK; /* wait for end */ } if (ch_flags[ch] & CHF_RDS) /* read? */ return ch9_rd_putw (ch); return ch9_wr_getw (ch); /* no, write */ case CH9_CPYP: /* anything to do? */ if (ch_wc[ch] == 0) /* (new, wc = 0) next */ break; if (ch_flags[ch] & CHF_EOR) /* end? */ ch_flags[ch] &= ~CHF_EOR; /* ignore */ else if (ch_flags[ch] & CHF_RDS) /* read? */ ch9_rd_putw (ch); else if (r = ch9_wr_getw (ch)) /* no, write */ return r; if (ch_wc[ch] == 0) /* done? get next */ break; return SCPE_OK; /* more to do */ default: return STOP_ILLIOP; } return ch9_new_cmd (ch); /* next command */ } else if (ch_flags[ch] & CHF_RDS) { /* 7607 read? */ if (ch_sta[ch] != CHXS_DSX) /* chan exec? no, disc */ return ch6_end_ds (ch); switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */ case CH6_TCH: /* transfer */ ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */ return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ case CH6_IOCD: /* IOCD */ if (ch_wc[ch]) { /* wc > 0? */ if (ch6_rd_putw (ch)) /* store; more? cont */ return SCPE_OK; } return ch6_end_ds (ch); /* no, disconnect */ case CH6_IOCP: /* IOCP */ if (ch_wc[ch]) { /* wc > 0? */ if (ch6_rd_putw (ch)) /* store; more? cont */ return SCPE_OK; } return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ case CH6_IOCT: /* IOCT */ if (ch_wc[ch]) { /* wc > 0? */ if (ch6_rd_putw (ch)) /* store; more? cont */ return SCPE_OK; } return ch6_ioxt (ch); /* unstall or disc */ case CH6_IOSP: /* IOSP */ if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_new_cmd (ch, FALSE); /* get next cmd */ } if (ch_wc[ch]) { /* wc > 0? */ if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR)) return SCPE_OK; /* yes, store; more? */ ch6_iosp_cclr (ch); /* cond clear eor */ } return ch6_new_cmd (ch, FALSE); /* next cmd */ case CH6_IOST: /* IOST */ if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_ioxt (ch); /* get next cmd */ } if (ch_wc[ch]) { /* wc > 0? */ if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR)) return SCPE_OK; /* yes, store; more? */ ch6_iosp_cclr (ch); /* cond clear eor */ } return ch6_ioxt (ch); /* unstall or disc */ case CH6_IORP: /* IORP */ if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_new_cmd (ch, FALSE); /* get next cmd */ } ch6_rd_putw (ch); /* store wd; ignore wc */ if (ch_flags[ch] & CHF_EOR) { /* EOR? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_new_cmd (ch, FALSE); /* get next cmd */ } return SCPE_OK; /* done */ case CH6_IORT: /* IORT */ if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_ioxt (ch); /* get next cmd */ } ch6_rd_putw (ch); /* store wd; ignore wc */ if (ch_flags[ch] & CHF_EOR) { /* EOR? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ return ch6_ioxt (ch); /* unstall or disc */ } return SCPE_OK; /* done */ default: return SCPE_IERR; } /* end case */ } /* end if read */ else { /* 7607 write */ if (ch_sta[ch] != CHXS_DSX) /* chan exec? no, disc */ return ch6_end_ds (ch); switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */ case CH6_TCH: /* transfer */ ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */ return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ case CH6_IOCD: /* IOCD */ if (ch_wc[ch]) { /* wc > 0? */ if (r = ch6_wr_getw (ch, TRUE)) /* send wd to dev; err? */ return r; if (ch_wc[ch]) /* more to do? */ return SCPE_OK; } return ch6_end_ds (ch); /* disconnect */ case CH6_IOCP: /* IOCP */ case CH6_IOSP: /* IOSP */ if (ch_wc[ch]) { /* wc > 0? */ if (r = ch6_wr_getw (ch, FALSE)) /* send wd to dev; err? */ return r; if (ch_wc[ch]) /* more to do? */ return SCPE_OK; } return ch6_new_cmd (ch, FALSE); /* get next cmd */ case CH6_IOCT: /* IOCT */ case CH6_IOST: /* IOST */ if (ch_wc[ch]) { /* wc > 0? */ if (r = ch6_wr_getw (ch, FALSE)) /* send wd to dev; err? */ return r; if (ch_wc[ch]) /* more to do? */ return SCPE_OK; } return ch6_ioxt (ch); /* get next cmd */ case CH6_IORP: /* IORP */ if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */ if (r = ch6_wr_getw (ch, TRUE)) /* send wd to dev; err? */ return r; if (ch_wc[ch]) /* more to do? */ return SCPE_OK; } ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */ return ch6_new_cmd (ch, FALSE); /* get next cmd */ case CH6_IORT: /* IORT */ if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */ if (r = ch6_wr_getw (ch, TRUE)) /* send wd to dev; err? */ return r; if (ch_wc[ch]) /* more to do? */ return SCPE_OK; } ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */ return ch6_ioxt (ch); /* unstall or disc */ default: return SCPE_IERR; } /* end switch */ } /* end else write */ } /* 7607 channel support routines */ /* 7607 channel input routine - put one word to memory */ t_bool ch6_rd_putw (uint32 ch) { if (ch_idf[ch] & CH6DF_EOR) /* eor from dev? */ ch_flags[ch] |= CHF_EOR; else ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* set/clr chan eor */ ch_idf[ch] = 0; /* clear eor, valid */ if (ch_wc[ch]) { /* wc > 0? */ if ((ch_op[ch] & 1) == 0) { /* do store? */ WriteP (ch_ca[ch], ch_ar[ch]); ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ } ch_wc[ch] = ch_wc[ch] - 1; } return (ch_wc[ch]? TRUE: FALSE); } /* 7607 channel output routine - get one word from memory */ t_stat ch6_wr_getw (uint32 ch, t_bool eorz) { DEVICE *dptr; DIB *dibp; uint32 eorfl; ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clr eor */ if (ch_wc[ch]) { ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */ ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ ch_wc[ch] = ch_wc[ch] - 1; } else ch_ar[ch] = 0; if (eorz && (ch_wc[ch] == 0)) /* eor on wc = 0? */ eorfl = 1; else eorfl = 0; dptr = ch_find_dev (ch, ch_dsu[ch]); /* find device */ if (dptr && /* valid device? */ (dibp = (DIB *) dptr->ctxt) && /* with DIB? */ dibp->write) /* and write routine? */ return dibp->write (ch, ch_ar[ch], eorfl); return SCPE_IERR; /* huh? */ } /* 7607 channel new command - on channel load, check for disconnects The protocol for new commands is as follows: - If IOCD 0,,0, disconnect immediately - If IOCT 0,,0 or IOST 0,,0 and loaded by RCHA, disconnect immediately - If an effective NOP (TCH, IOCx 0,,0, IOSx 0,,0), force a channel cycle to retire the channel comand as quickly as possible. - If an IORx and EOR is set, force a channel cycle to retire the channel command as quickly as possible. */ t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld) { t_uint64 ir; uint32 op, t; ir = ReadP (t = ch_clc[ch]); /* read cmd */ ch_wc[ch] = GET_DEC (ir); /* get word cnt */ ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ op = GET_OPD (ir) << 1; /* get opcode */ ch_op[ch] = op | ((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */ if ((ir & CHI_IND) && (ch_wc[ch] || /* indirect? */ ((op != CH6_IOCP) && (op != CH6_IOSP)))) { /* wc >0, or !IOxP? */ t_uint64 sr = ReadP (ch_ca[ch] & AMASK); /* read indirect */ ch_ca[ch] = ((uint32) sr) & ((cpu_model & I_CT)? PAMASK: AMASK); } if (hst_ch) cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0); ch_clc[ch] = (ch_clc[ch] + 1) & AMASK; /* incr chan pc */ switch (op) { /* case on opcode */ case CH6_IOCD: /* IOCD */ if (ch_wc[ch] == 0) /* wc 0? end now */ ch6_end_ds (ch); break; case CH6_IOST: /* IOST */ if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ ch_req |= REQ_CH (ch); case CH6_IOCT: /* IOCT */ if (ch_wc[ch] == 0) { /* wc 0? */ if (ch_ld) /* load? end now */ ch6_end_ds (ch); else ch_req |= REQ_CH (ch); /* else immed ch req */ } break; case CH6_IOSP: /* IOSP */ if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ ch_req |= REQ_CH (ch); case CH6_IOCP: /* IOCP */ if (ch_wc[ch] == 0) /* wc 0? immed ch req */ ch_req |= REQ_CH (ch); break; case CH6_IORT: /* IORT */ case CH6_IORP: /* IORP */ if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ ch_req |= REQ_CH (ch); break; case CH6_TCH: /* TCH */ ch_req |= REQ_CH (ch); /* immed ch req */ break; default: /* all others */ break; } /* end case */ if (sim_brk_summ && sim_brk_test (t, SWMASK ('E'))) return ch_bkpt (ch, t); return SCPE_OK; } /* 7607 channel IOxT: if LCH stall, set state back to DSW; else disconnect and trap */ t_stat ch6_ioxt (uint32 ch) { if (ch_flags[ch] & CHF_LDW) { /* LCH cmd pending? */ ch_flags[ch] &= ~CHF_LDW; /* clr pending flag */ ch_sta[ch] = CH6S_DSW; /* unstall pending LCH */ } else { ch_flags[ch] |= CHF_CMD; /* set cmd trap flag */ ch6_end_ds (ch); /* disconnect */ } return SCPE_OK; } /* 7607 conditionally clear EOR on IOSx completion */ void ch6_iosp_cclr (uint32 ch) { uint32 i, op; if (ch_wc[ch] == 0) { /* wc = 0? */ uint32 ccnt = 5; /* allow 5 for CPU */ for (i = 0; i < NUM_CHAN; i++) { /* test channels */ if (ch_sta[ch] != CHXS_DSX) /* idle? skip */ continue; op = ch_op[ch] & ~1; /* get op */ ccnt++; /* 1 per active ch */ if ((op == CH6_IOCP) || (op == CH6_IORP) || /* 1 per proceed */ (op == CH6_IOSP)) ccnt++; } if (ccnt <= 11) /* <= 11? ok */ return; } ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear eor */ return; } /* 7607 external interface routines */ /* Input - store word, request channel input service */ t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 fl) { if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ if (ch_idf[ch] & CH6DF_VLD) /* overrun? */ ind_ioc = 1; ch_idf[ch] = CH6DF_VLD; /* set ar valid */ if (fl) /* set eor if requested */ ch_idf[ch] |= CH6DF_EOR; ch_req |= REQ_CH (ch); /* request chan */ ch_flags[ch] |= CHF_RDS; ch_ar[ch] = val & DMASK; /* save data */ } return SCPE_OK; } /* Disconnect on error */ t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 fl) { if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ ch_flags[ch] |= fl; /* set flag */ return ch6_end_ds (ch); /* disconnect */ } return SCPE_OK; } /* Output - request channel output service */ t_bool ch6_req_wr (uint32 ch, uint32 unit) { if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ ch_req |= REQ_CH (ch); ch_flags[ch] &= ~CHF_RDS; } return SCPE_OK; } /* Set/read channel flags */ uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags) { if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ ch_flags[ch] = ch_flags[ch] | flags; return ch_flags[ch]; } return 0; } /* Channel connected to unit? */ t_bool ch6_qconn (uint32 ch, uint32 unit) { if ((ch < NUM_CHAN) && /* valid chan */ (ch_dsu[ch] == unit)) /* for right unit? */ return TRUE; return FALSE; } /* 7909 channel support routines */ /* 7909 channel input routine - put one word to memory */ t_stat ch9_rd_putw (uint32 ch) { ch_idf[ch] = 0; /* invalidate */ if (ch_wc[ch]) { /* wc > 0? */ WriteP (ch_ca[ch], ch_ar[ch]); ch_ca[ch] = CHAINC (ch_ca[ch]); ch_wc[ch] = ch_wc[ch] - 1; } return SCPE_OK; } /* 7909 channel output routine - get one word from memory */ t_stat ch9_wr_getw (uint32 ch) { if (ch_wc[ch]) { ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */ ch_ca[ch] = CHAINC (ch_ca[ch]); ch_wc[ch] = ch_wc[ch] - 1; } else ch_ar[ch] = 0; return ch9_wr (ch, ch_ar[ch], 0); /* write to device */ } /* 7909 send select to device */ t_stat ch9_sel (uint32 ch, uint32 sel) { DEVICE *dptr = ch2dev[ch]; DIB *dibp; if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp && dibp->chsel) return dibp->chsel (ch, sel, 0); return SCPE_IERR; } /* 7909 send word to device */ t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl) { DEVICE *dptr = ch2dev[ch]; DIB *dibp; if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp && dibp->write) return dibp->write (ch, dat, fl); return SCPE_IERR; } /* 7909 channel new command */ t_stat ch9_new_cmd (uint32 ch) { t_uint64 ir; uint32 t; t_stat r; ir = ReadP (t = ch_clc[ch]); /* read cmd */ r = ch9_exec_cmd (ch, ir); /* exec cmd */ if (ch_sta[ch] != CHXS_IDLE) /* chan running? */ ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr chan pc */ if ((r == SCPE_OK) && sim_brk_summ && sim_brk_test (t, SWMASK ('E'))) return ch_bkpt (ch, t); return r; } t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir) { uint32 op; ch_wc[ch] = GET_DEC (ir); /* get word cnt */ ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ op = (GET_OPD (ir) << 2); /* get opcode */ ch_op[ch] = op | ((((uint32) ir) & 0200000)? 1: 0) | /* plus bit<19> */ (((op & 010) && (ch_wc[ch] & 040000))? 2: 0); /* plus bit 3 if used */ if (ir & CHI_IND) { /* indirect? */ t_uint64 sr = ReadP (ch_ca[ch] & CHAMASK); /* read indirect */ ch_ca[ch] = ((uint32) sr) & CHAMASK; /* get address */ } if (hst_ch) cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0); switch (ch_op[ch]) { /* check initial cond */ case CH9_LAR: /* misc processing */ case CH9_SAR: case CH9_ICC: case CH9_ICCA: case CH9_XMT: case CH9_LCC: case CH9_SMS: if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) ch9_eval_int (ch, CHINT_SEQC); /* not during data */ /* fall through */ case CH9_TCM: /* jumps */ case CH9_TCH: case CH9_TDC: case CH9_LIPT: case CH9_LIP: ch_req |= REQ_CH (ch); /* process in chan */ break; case CH9_CTL: /* control */ case CH9_CTLR: case CH9_CTLW: if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) ch9_eval_int (ch, CHINT_SEQC); /* not during data */ ch_flags[ch] &= ~CHF_EOR; if (ch_wc[ch] & CH9D_NST) /* N set? proc in chan */ ch_req |= REQ_CH (ch); else return ch9_sel (ch, CHSL_CTL); /* sel, dev sets ch_req! */ break; case CH9_SNS: /* sense */ if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) ch9_eval_int (ch, CHINT_SEQC); ch_flags[ch] &= ~CHF_EOR; ch_req |= REQ_CH (ch); /* process in chan */ break; case CH9_CPYD: /* data transfers */ case CH9_CPYP: if ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) == 0) ch9_eval_int (ch, CHINT_SEQC); /* not unless data */ if (ch_flags[ch] & CHF_PRD) ch_flags[ch] |= CHF_RDS; else if (ch_flags[ch] & CHF_PWR) ch_flags[ch] |= CHF_WRS; ch_flags[ch] &= ~(CHF_EOR|CHF_PRD|CHF_PWR); if ((ch_op[ch] == CH9_CPYP) && (ch_wc[ch] == 0)) ch_req |= REQ_CH (ch); /* CPYP x,,0? */ break; /* dev sets ch_req! */ case CH9_WTR: /* wait */ ch_sta[ch] = CHXS_IDLE; /* stop */ break; case CH9_TWT: /* trap and wait */ ch_sta[ch] = CHXS_IDLE; /* stop */ ch_flags[ch] |= CHF_TWT; /* set trap */ break; default: return STOP_ILLIOP; } return SCPE_OK; } /* 7909 external interface routines */ /* Input - store word, request channel input service */ t_stat ch9_req_rd (uint32 ch, t_uint64 val) { if (ch < NUM_CHAN) { /* valid chan? */ if (ch_idf[ch] & CH9DF_VLD) /* prev still valid? io chk */ ch9_set_ioc (ch); ch_idf[ch] = CH9DF_VLD; /* set ar valid */ ch_req |= REQ_CH (ch); /* request chan */ ch_ar[ch] = val & DMASK; /* save data */ } return SCPE_OK; } /* Set attention */ void ch9_set_atn (uint32 ch) { if (ch < NUM_CHAN) ch9_eval_int (ch, CHINT_ATN1); return; } /* Set IO check - UEND will occur at end - not recognized in int mode */ void ch9_set_ioc (uint32 ch) { if ((ch < NUM_CHAN) && !(ch_flags[ch] & CHF_INT)) { ind_ioc = 1; /* IO check */ ch_flags[ch] |= CHF_IOC; /* ch IOC for end */ } return; } /* Set end */ void ch9_set_end (uint32 ch, uint32 iflags) { if (ch < NUM_CHAN) { /* valid chan? */ ch_flags[ch] |= CHF_EOR; ch9_eval_int (ch, iflags); } return; } /* Test connected */ t_bool ch9_qconn (uint32 ch) { if ((ch < NUM_CHAN) && (ch_sta[ch] == CHXS_DSX)) return TRUE; return FALSE; } /* Evaluate interrupts - Interrupt requests set flags in the channel flags word - If an interrupt is not in progress, interrupt requests are evaluated - If an interrupt request is found, the interruptable flags are transferred to the channel condition register and cleared in the channel flags This provides an effective stage of buffering for interrupt requests that are not immediately serviced */ void ch9_eval_int (uint32 ch, uint32 iflags) { uint32 ireq; ch_flags[ch] |= (iflags << CHF_V_COND); /* or into chan flags */ if ((ch_flags[ch] & CHF_INT) == 0) { /* int not in prog? */ ireq = ((ch_flags[ch] >> CHF_V_COND) & CHF_M_COND) & ~(((ch_sms[ch] & CHSMS_IUEND)? CHINT_UEND: 0) | ((ch_sms[ch] & CHSMS_IATN1)? CHINT_ATN1: 0) | ((ch_sms[ch] & CHSMS_IATN2)? CHINT_ATN2: 0) | ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))? CHINT_SEQC: 0)); if (ireq) { /* int pending? */ ch_cnd[ch] = ireq; /* set cond reg */ ch_flags[ch] &= ~(ireq << CHF_V_COND); /* clear chan flags */ ch_flags[ch] |= CHF_IRQ; /* set int req */ ch_req |= REQ_CH (ch); /* request channel */ } } return; } /* Test for all channels idle */ t_bool ch_qidle (void) { uint32 i; for (i = 0; i < NUM_CHAN; i++) { if (ch_sta[i] != CHXS_IDLE) return FALSE; } return TRUE; } /* Evaluate/execute channel traps */ uint32 chtr_eval (uint32 *decr) { uint32 i, cme; if (!chtr_inht && !chtr_inhi && chtr_enab) { if (BIT_TST (chtr_enab, CHTR_V_CLK) && chtr_clk) { /* clock trap? */ if (decr) { /* exec? */ chtr_clk = 0; /* clr flag */ *decr = 0; } return CHTR_CLK_SAV; } for (i = 0; i < NUM_CHAN; i++) { /* loop thru chan */ cme = BIT_TST (chtr_enab, CHTR_V_CME + i); /* cmd/eof enab? */ if (cme && (ch_flags[i] & CHF_CMD)) { /* cmd enab and set? */ if (decr) { /* exec? */ ch_flags[i] &= ~CHF_CMD; /* clr flag */ *decr = CHTR_F_CMD; } return (CHTR_CHA_SAV + (i << 1)); } if (cme && (ch_flags[i] & CHF_EOF)) { /* eof enab and set? */ if (decr) { /* exec? */ ch_flags[i] &= ~CHF_EOF; /* clr flag */ *decr = CHTR_F_EOF; } return (CHTR_CHA_SAV + (i << 1)); } if (BIT_TST (chtr_enab, CHTR_V_TRC + i) && /* trc enab? */ (ch_flags[i] & CHF_TRC)) { /* trc flag? */ if (decr) { /* exec? */ ch_flags[i] &= ~CHF_TRC; /* clr flag */ *decr = CHTR_F_TRC; } return (CHTR_CHA_SAV + (i << 1)); } /* end if BIT_TST */ } /* end for */ } /* end if !chtr_inht */ if (decr) *decr = 0; return 0; } /* Channel reset */ t_stat ch_reset (DEVICE *dptr) { uint32 ch = dptr - &ch_dev[0]; /* get channel */ if (ch == CH_A) /* channel A fixed */ ch2dev[ch] = &mt_dev[0]; ch_sta[ch] = 0; ch_flags[ch] = 0; ch_idf[ch] = 0; ch_dso[ch] = 0; ch_dsu[ch] = 0; ch_ndso[ch] = 0; ch_ndsu[ch] = 0; ch_op[ch] = 0; ch_clc[ch] = 0; ch_wc[ch] = 0; ch_ca[ch] = 0; ch_ar[ch] = 0; ch_sms[ch] = 0; ch_cnd[ch] = 0; ch_lcc[ch] = 0; sim_cancel (&ch_unit[ch]); return SCPE_OK; } /* Show channel type */ t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; if (dptr->flags & DEV_7909) fputs ("7909", st); else if (dptr->flags & DEV_7289) fputs ("7289", st); else fputs ("7607", st); return SCPE_OK; } /* Enable channel, assign device */ t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr, *dptr1; char gbuf[CBUFSIZE]; uint32 i, ch; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; ch = dptr - &ch_dev[0]; if ((ch == 0) || !(dptr->flags & DEV_DIS)) return SCPE_ARG; if (cptr == NULL) cptr = "TAPE"; get_glyph (cptr, gbuf, 0); for (i = 0; dev_table[i].name; i++) { if (strcmp (dev_table[i].name, gbuf) == 0) { dptr1 = ch_map_flags (ch, dev_table[i].flags); if (!dptr1 || !(dptr1->flags & DEV_DIS)) return SCPE_ARG; dptr->flags &= ~(DEV_DIS|DEV_7909|DEV_7289|DEV_7750|DEV_7631); dptr->flags |= dev_table[i].flags; dptr1->flags &= ~DEV_DIS; ch2dev[ch] = dptr1; return reset_all (0); } } return SCPE_ARG; } /* Map device flags to device pointer */ DEVICE *ch_map_flags (uint32 ch, int32 fl) { if (fl & DEV_7289) return &drm_dev; if (!(fl & DEV_7909)) return &mt_dev[ch]; if (fl & DEV_7631) return &dsk_dev; if (fl & DEV_7750) return &com_dev; return NULL; } /* Set up channel map */ void ch_set_map (void) { uint32 i; for (i = 0; i < NUM_CHAN; i++) { if (ch_dev[i].flags & DEV_DIS) ch2dev[i] = NULL; else ch2dev[i] = ch_map_flags (i, ch_dev[i].flags); } return; } /* Disable channel, deassign device */ t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr, *dptr1; UNIT *uptr1; uint32 i, ch; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; ch = dptr - &ch_dev[0]; if ((ch == 0) || (dptr->flags & DEV_DIS) || (cptr != NULL)) return SCPE_ARG; dptr1 = ch2dev[ch]; if (dptr1 == NULL) return SCPE_IERR; if (dptr1->units) { for (i = 0; i < dptr1->numunits; i++) { uptr1 = dptr1->units + i; if (dptr1->detach) dptr1->detach (uptr1); else detach_unit (uptr1); } } dptr->flags &= ~(DEV_7909|DEV_7289); dptr->flags |= DEV_DIS; dptr1->flags |= DEV_DIS; return reset_all (0); } /* Show channel that device is on (tapes, 7289, 7909 only) */ t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; uint32 i; dptr = find_dev_from_unit (uptr); if (dptr) { for (i = 0; i < NUM_CHAN; i++) { if (ch2dev[i] == dptr) { fprintf (st, "channel %c", 'A' + i); return SCPE_OK; } } } fprintf (st, "not assigned to channel"); return SCPE_OK; } simh-3.8.1/I7094/i7094_sys.c0000644000175000017500000006227211112035160013172 0ustar vlmvlm/* i7094_sys.c: IBM 7094 simulator interface Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 29-Oct-06 RMS Added additional expanded core instructions 08-Jun-06 RMS Added Dave Pitts' binary loader */ #include "i7094_defs.h" #include #include "i7094_dat.h" extern DEVICE cpu_dev; extern DEVICE ch_dev[NUM_CHAN]; extern DEVICE mt_dev[NUM_CHAN]; extern DEVICE drm_dev; extern DEVICE dsk_dev; extern DEVICE com_dev, coml_dev; extern DEVICE cdr_dev, cdp_dev; extern DEVICE lpt_dev; extern DEVICE clk_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; uint32 cvt_code_to_ascii (uint32 c, int32 sw); uint32 cvt_ascii_to_code (uint32 c, int32 sw); /* 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[] = "IBM 7094"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &clk_dev, &ch_dev[0], &ch_dev[1], &ch_dev[2], &ch_dev[3], &ch_dev[4], &ch_dev[5], &ch_dev[6], &ch_dev[7], &mt_dev[0], &mt_dev[1], &mt_dev[2], &mt_dev[3], &mt_dev[4], &mt_dev[5], &mt_dev[6], &mt_dev[7], &cdr_dev, &cdp_dev, &lpt_dev, &dsk_dev, &drm_dev, &com_dev, &coml_dev, NULL }; char ch_bkpt_msg[] = "Channel A breakpoint, CLC: xxxxxx"; const char *sim_stop_messages[] = { "Unknown error", "HALT instruction", "Breakpoint", "Undefined instruction", "Divide check", "Nested XEC limit exceeded", "Address stop", "Non-existent channel", "Illegal instruction for 7909 channel", "Illegal instruction for non-7909 channel", "Non-existent device", "Undefined channel instruction", "Write to protected device", "Illegal instruction for device", "Invalid 7631 track format", "7750 buffer pool empty on input", "7750 buffer pool empty on output", "7750 invalid line number", "7750 invalid message", ch_bkpt_msg }; /* Modify channel breakpoint message */ t_stat ch_bkpt (uint32 ch, uint32 clc) { ch_bkpt_msg[8] = 'A' + ch; sprintf (&ch_bkpt_msg[27], "%06o", clc); return STOP_CHBKPT; } /* Binary loader, not implemented */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { extern t_stat binloader (FILE *fd, char *file, int loadpt); if (flag == 0) return binloader (fileref, cptr, 0); return SCPE_NOFNC; } /* Symbol tables */ #define I_V_FL 39 /* inst class */ #define I_M_FL 017 /* class mask */ #define I_NOP 0000000000000000 /* no operand */ #define I_MXR 0010000000000000 /* addr(tag) */ #define I_MXN 0020000000000000 /* *addr(tag) */ #define I_MXV 0030000000000000 /* var mul/div */ #define I_MXC 0040000000000000 /* convert */ #define I_DNP 0050000000000000 /* decr, no oper */ #define I_DEC 0060000000000000 /* decrement */ #define I_SNS 0070000000000000 /* sense */ #define I_IMM 0100000000000000 /* 18b immediate */ #define I_TAG 0110000000000000 /* tag only */ #define I_IOX 0120000000000000 /* IO channel */ #define I_TCH 0130000000000000 /* transfer channel */ #define I_I9N 0140000000000000 /* 7909 with nostore */ #define I_I9S 0150000000000000 /* 7909 */ #define IFAKE_7607 0001000000000000 /* fake op extensions */ #define IFAKE_7909 0002000000000000 #define DFAKE (DMASK|IFAKE_7607|IFAKE_7909) #define I_N_NOP 000 #define I_N_MXR 001 #define I_N_MXN 002 #define I_N_MXV 003 #define I_N_MXC 004 #define I_N_DNP 005 #define I_N_DEC 006 #define I_N_SNS 007 #define I_N_IMM 010 #define I_N_TAG 011 #define I_N_IOX 012 #define I_N_TCH 013 #define I_N_I9N 014 #define I_N_I9S 015 #define INST_P_XIT 0 /* exit */ #define INST_P_SKP 1 /* do not print */ #define INST_P_PRA 2 /* print always */ #define INST_P_PNZ 3 /* print if nz */ #define INST_P_PNT 4 /* print if nz, term */ static const t_uint64 masks[14] = { 03777700000000, 03777700000000, 03777700000000, 03777700000000, 03777400000000, 03700000000000, 03700000000000, 03777700077777, 03777700000000, 03777700000000, 03700000200000, 03700000200000, 03760000200000, 03740000200000 }; static const uint32 fld_max[14][3] = { /* addr,tag,decr limit */ { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, INST_M_TAG, INST_M_VCNT }, { INST_M_ADDR, INST_M_TAG, INST_M_CCNT }, { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, { 0, INST_M_TAG, 0 }, { RMASK, 0, 0 }, { INST_M_ADDR, INST_M_TAG, 0 }, { INST_M_ADDR, 1, INST_M_DEC }, { INST_M_ADDR, 1, 0 }, { INST_M_ADDR, 1, 0 }, { INST_M_ADDR, 1, 0 } }; static const uint32 fld_fmt[14][3] = { /* addr,tag,decr print */ { INST_P_PNT, INST_P_PNT, INST_P_XIT }, /* nop: all optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxr: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxn: tag optional */ { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* mxv: tag optional */ { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* cvt: tag optional */ { INST_P_PNT, INST_P_PNT, INST_P_PNT }, /* dnp: all optional */ { INST_P_PRA, INST_P_PRA, INST_P_PRA }, /* dec: print all */ { INST_P_SKP, INST_P_PNT, INST_P_XIT }, /* sns: skip addr, tag opt */ { INST_P_PRA, INST_P_XIT, INST_P_XIT }, /* immediate: addr only */ { INST_P_PNZ, INST_P_PRA, INST_P_XIT }, /* tag: addr optional */ { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* iox: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* tch: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* i9n: tag optional */ { INST_P_PRA, INST_P_PNT, INST_P_XIT } /* i9s: tag optional */ }; static const t_uint64 ind_test[14] = { 0, 0, INST_IND, 0, 0, 0, 0, 0, 0, 0, CHI_IND, CHI_IND, CHI_IND, CHI_IND }; static const char *opcode[] = { "TXI", "TIX", "TXH", "STR", "TNX", "TXL", "HTR", "TRA", "TTR", "CLM", "LBT", "CHS", "SSP", "ENK", "IOT", "COM", "ETM", "RND", "FRN", "DCT", "RCT", "LMTM", "SLF", "SLN1", "SLN2", "SLN3", "SLN4", "SWT1", "SWT2", "SWT3", "SWT4", "SWT5", "SWT6", "BTTA", "BTTB", "BTTC", "BTTD", "BTTE", "BTTF", "BTTG", "BTTH", "RICA", "RICB", "RICC", "RICD", "RICE", "RICF", "RICG", "RICH", "RDCA", "RDCB", "RDCC", "RDCD", "RDCE", "RDCF", "RDCG", "RDCH", "TRCA", "TRCC", "TRCE", "TRCG", "TEFA", "TEFC", "TEFE", "TEFG", "TLQ", "IIA", "TIO", "OAI", "PAI", "TIF", "IIR", "RFT", "SIR", "RNT", "RIR", "TCOA", "TCOB", "TCOC", "TCOD", "TCOE", "TCOF", "TCOG", "TCOH", "TSX", "TZE", "CVR", "TPL", "XCA", "TOV", "TQO", "TQP", "MPY", "VLM", "VLM1", "DVH", "DVP", "VDH", "VDP", "VDH2", "VDP2", "FDH", "FDP", "FMP", "DFMP", "FAD", "DFAD", "FSB", "DFSB", "FAM", "DFAM", "FSM", "DFSM", "ANS", "ERA", "CAS", "ACL", "ADD", "ADM", "SUB", "SBM", "HPR", "IIS", "LDI", "OSI", "DLD", "OFT", "RIS", "ONT", "CLA", "CLS", "ZET", "XEC", "LXA", "LAC", "RCHA", "RCHC", "RCHE", "RCHG", "LCHA", "LCHC", "LCHE", "LCHG", "RSCA", "RSCC", "RSCE", "RSCG", "STCA", "STCC", "STCE", "STCG", "LDQ", "ENB", "STZ", "STO", "SLW", "STI", "STA", "STD", "STT", "STP", "SXA", "SCA", "SCHA", "SCHC", "SCHE", "SCHG", "SCDA", "SCDC", "SCDE", "SCDG", "PAX", "PAC", "PXA", "PCA", "PSE", "NOP", "RDS", "LLS", "BSR", "LRS", "WRS", "ALS", "WEF", "ARS", "REW", "AXT", "SDN", "CLM", "PBT", "EFTM", "SSM", "LFTM", "ESTM", "ECTM", "LTM", "LSNM", "EMTM", "SLT1", "SLT2", "SLT3", "SLT4", "ETTA", "ETTB", "ETTC", "ETTD", "ETTE", "ETTF", "ETTG", "ETTH", "ESNT", "TRCB", "TRCD", "TRCF", "TRCH", "TEFB", "TEFD", "TEFF", "TEFH", "RIA", "PIA", "IIL", "LFT", "SIL", "LNT", "RIL", "TCNA", "TCNB", "TCNC", "TCND", "TCNE", "TCNF", "TCNG", "TCNH", "TNZ", "CVR", "TMI", "XCL", "TNO", "CRQ", "MPR", "DFDH", "DFDP", "UFM", "DUFM", "UFA", "DUFA", "UFS", "DUFS", "UAM", "DUAM", "USM", "DUSM", "ANA", "LAS", "CAL", "ORA", "NZT", "LXD", "LXC", "RCHB", "RCHD", "RCHF", "RCHH", "LCHB", "LCHD", "LCHF", "LCHH", "RSCB", "RSCD", "RSCF", "RSCH", "STCB", "STCD", "STCF", "STCH", "STQ", "ORS", "DST", "SLQ", "STL", "SXD", "SCD", "SCHB", "SCHD", "SCHF", "SCHH", "SCDB", "SCDD", "SCDF", "SCDH", "PDX", "PDC", "PXD", "PCD", "MSE", "LGL", "BSF", "LGR", "RQL", "RUN", "AXC", "TIA", "TIB", "LRI", "LPI", "SEA", "SEB", "IFT", "EFT", "IOCD", "IOCDN", "TCH", "IORP", "IORPN", "IORT", "IORTN", "IOCP", "IOCPN", "IOCT", "IOCTN", "IOSP", "IOSPN", "IOST", "IOSTN", "WTR", "XMT", "TCH", "LIPT", "CTL", "CTLN", "CTLR", "CTLRN", "CTLW", "CTLWN", "SNS", "LAR", "SAR", "TWT", "CPYP", "CPYD", "TCM", "LIP", "TDC", "LCC", "SMS", "ICC", NULL }; static const t_uint64 opc_v[] = { 0100000000000+I_DEC, 0200000000000+I_DEC, 0300000000000+I_DEC, 0500000000000+I_DNP, 0600000000000+I_DEC, 0700000000000+I_DEC, 0000000000000+I_MXN, 0002000000000+I_MXN, 0002100000000+I_MXN, 0076000000000+I_SNS, 0076000000001+I_SNS, 0076000000002+I_SNS, 0076000000003+I_SNS, 0076000000004+I_SNS, 0076000000005+I_SNS, 0076000000006+I_SNS, 0076000000007+I_SNS, 0076000000010+I_SNS, 0076000000011+I_SNS, 0076000000012+I_SNS, 0076000000014+I_SNS, 0076000000016+I_SNS, 0076000000140+I_SNS, 0076000000141+I_SNS, 0076000000142+I_SNS, 0076000000143+I_SNS, 0076000000144+I_SNS, 0076000000161+I_SNS, 0076000000162+I_SNS, 0076000000163+I_SNS, 0076000000164+I_SNS, 0076000000165+I_SNS, 0076000000166+I_SNS, 0076000001000+I_SNS, 0076000002000+I_SNS, 0076000003000+I_SNS, 0076000004000+I_SNS, 0076000005000+I_SNS, 0076000006000+I_SNS, 0076000007000+I_SNS, 0076000010000+I_SNS, 0076000001350+I_SNS, 0076000002350+I_SNS, 0076000003350+I_SNS, 0076000004350+I_SNS, 0076000005350+I_SNS, 0076000006350+I_SNS, 0076000007350+I_SNS, 0076000010350+I_SNS, 0076000001352+I_SNS, 0076000002352+I_SNS, 0076000003352+I_SNS, 0076000004352+I_SNS, 0076000005352+I_SNS, 0076000006352+I_SNS, 0076000007352+I_SNS, 0076000010352+I_SNS, 0002200000000+I_MXN, 0002400000000+I_MXN, 0002600000000+I_MXN, 0002700000000+I_MXN, 0003000000000+I_MXN, 0003100000000+I_MXN, 0003200000000+I_MXN, 0003300000000+I_MXN, 0004000000000+I_MXN, 0004100000000+I_NOP, 0004200000000+I_MXR, 0004300000000+I_NOP, 0004400000000+I_NOP, 0004600000000+I_MXR, 0005100000000+I_IMM, 0005400000000+I_IMM, 0005500000000+I_IMM, 0005600000000+I_IMM, 0005700000000+I_IMM, 0006000000000+I_MXN, 0006100000000+I_MXN, 0006200000000+I_MXN, 0006300000000+I_MXN, 0006400000000+I_MXN, 0006500000000+I_MXN, 0006600000000+I_MXN, 0006700000000+I_MXN, 0007400000000+I_MXR, 0010000000000+I_MXN, 0011400000000+I_MXC, 0012000000000+I_MXN, 0013100000000+I_NOP, 0014000000000+I_MXN, 0016100000000+I_MXN, 0016200000000+I_MXN, 0020000000000+I_MXN, 0020400000000+I_MXV, 0020500000000+I_MXV, 0022000000000+I_MXN, 0022100000000+I_MXN, 0022400000000+I_MXV, 0022500000000+I_MXV, 0022600000000+I_MXV, 0022700000000+I_MXV, 0024000000000+I_MXN, 0024100000000+I_MXN, 0026000000000+I_MXN, 0026100000000+I_MXN, 0030000000000+I_MXN, 0030100000000+I_MXN, 0030200000000+I_MXN, 0030300000000+I_MXN, 0030400000000+I_MXN, 0030500000000+I_MXN, 0030600000000+I_MXN, 0030700000000+I_MXN, 0032000000000+I_MXN, 0032200000000+I_MXN, 0034000000000+I_MXN, 0036100000000+I_MXN, 0040000000000+I_MXN, 0040100000000+I_MXN, 0040200000000+I_MXN, 0440000000000+I_MXN, 0042000000000+I_NOP, 0044000000000+I_MXN, 0044100000000+I_MXN, 0044200000000+I_MXN, 0044300000000+I_MXN, 0044400000000+I_MXN, 0044500000000+I_MXN, 0044600000000+I_MXN, 0050000000000+I_MXN, 0050200000000+I_MXN, 0052000000000+I_MXN, 0052200000000+I_MXN, 0053400000000+I_MXR, 0053500000000+I_MXR, 0054000000000+I_MXN, 0054100000000+I_MXN, 0054200000000+I_MXN, 0054300000000+I_MXN, 0054400000000+I_MXN, 0054500000000+I_MXN, 0054600000000+I_MXN, 0054700000000+I_MXN, 0054000000000+I_MXN, 0054100000000+I_MXN, 0054200000000+I_MXN, 0054300000000+I_MXN, 0054400000000+I_MXN, 0054500000000+I_MXN, 0054600000000+I_MXN, 0054700000000+I_MXN, 0056000000000+I_MXN, 0056400000000+I_MXN, 0060000000000+I_MXN, 0060100000000+I_MXN, 0060200000000+I_MXN, 0060400000000+I_MXN, 0062100000000+I_MXN, 0062200000000+I_MXN, 0062500000000+I_MXN, 0063000000000+I_MXN, 0063400000000+I_MXR, 0063600000000+I_MXR, 0064000000000+I_MXN, 0064000000000+I_MXN, 0064200000000+I_MXN, 0064300000000+I_MXN, 0064400000000+I_MXN, 0064500000000+I_MXN, 0064600000000+I_MXN, 0064700000000+I_MXN, 0073400000000+I_TAG, 0073700000000+I_TAG, 0075400000000+I_TAG, 0075600000000+I_TAG, 0076000000000+I_MXR, 0076100000000+I_NOP, 0076200000000+I_MXR, 0076300000000+I_MXR, 0076400000000+I_MXR, 0076500000000+I_MXR, 0076600000000+I_MXR, 0076700000000+I_MXR, 0077000000000+I_MXR, 0077100000000+I_MXR, 0077200000000+I_MXR, 0077400000000+I_MXR, 0077600000000+I_MXR, 0476000000000+I_SNS, 0476000000001+I_SNS, 0476000000002+I_SNS, 0476000000003+I_SNS, 0476000000004+I_SNS, 0476000000005+I_SNS, 0476000000006+I_SNS, 0476000000007+I_SNS, 0476000000010+I_SNS, 0476000000016+I_SNS, 0476000000141+I_SNS, 0476000000142+I_SNS, 0476000000143+I_SNS, 0476000000144+I_SNS, 0476000001000+I_SNS, 0476000002000+I_SNS, 0476000003000+I_SNS, 0476000004000+I_SNS, 0476000005000+I_SNS, 0476000006000+I_SNS, 0476000007000+I_SNS, 0476000010000+I_SNS, 0402100000000+I_MXN, 0402200000000+I_MXN, 0402400000000+I_MXN, 0402600000000+I_MXN, 0402700000000+I_MXN, 0403000000000+I_MXN, 0403100000000+I_MXN, 0403200000000+I_MXN, 0403300000000+I_MXN, 0404200000000+I_NOP, 0404600000000+I_NOP, 0405100000000+I_IMM, 0405400000000+I_IMM, 0405500000000+I_IMM, 0405600000000+I_IMM, 0405700000000+I_IMM, 0406000000000+I_MXN, 0406100000000+I_MXN, 0406200000000+I_MXN, 0406300000000+I_MXN, 0406400000000+I_MXN, 0406500000000+I_MXN, 0406600000000+I_MXN, 0406700000000+I_MXN, 0410000000000+I_MXN, 0411400000000+I_MXC, 0412000000000+I_MXN, 0413000000000+I_NOP, 0414000000000+I_MXN, 0415400000000+I_MXC, 0420000000000+I_MXN, 0424000000000+I_MXN, 0424100000000+I_MXN, 0426000000000+I_MXN, 0426100000000+I_MXN, 0430000000000+I_MXN, 0430100000000+I_MXN, 0430200000000+I_MXN, 0430300000000+I_MXN, 0430400000000+I_MXN, 0430500000000+I_MXN, 0430600000000+I_MXN, 0430700000000+I_MXN, 0432000000000+I_MXN, 0434000000000+I_MXN, 0450000000000+I_MXN, 0450100000000+I_MXN, 0452000000000+I_MXN, 0453400000000+I_MXR, 0453500000000+I_MXR, 0454000000000+I_MXN, 0454100000000+I_MXN, 0454200000000+I_MXN, 0454300000000+I_MXN, 0454400000000+I_MXN, 0454500000000+I_MXN, 0454600000000+I_MXN, 0454700000000+I_MXN, 0454000000000+I_MXN, 0454100000000+I_MXN, 0454200000000+I_MXN, 0454300000000+I_MXN, 0454400000000+I_MXN, 0454500000000+I_MXN, 0454600000000+I_MXN, 0454700000000+I_MXN, 0460000000000+I_MXN, 0460200000000+I_MXN, 0460300000000+I_MXN, 0462000000000+I_MXN, 0462500000000+I_MXN, 0463400000000+I_MXR, 0463600000000+I_MXR, 0464000000000+I_MXN, 0464000000000+I_MXN, 0464200000000+I_MXN, 0464300000000+I_MXN, 0464400000000+I_MXN, 0464500000000+I_MXN, 0464600000000+I_MXN, 0464700000000+I_MXN, 0473400000000+I_TAG, 0473700000000+I_TAG, 0475400000000+I_TAG, 0475600000000+I_TAG, 0476000000000+I_MXR, 0476300000000+I_MXR, 0476400000000+I_MXR, 0476500000000+I_MXR, 0477300000000+I_MXR, 0477200000000+I_MXR, 0477400000000+I_MXR, 0010100000000+I_MXN, 0410100000000+I_MXN, 0056200000000+I_MXN, 0456400000000+I_MXN, 0476100000041+I_SNS, 0476100000042+I_SNS, 0476100000043+I_SNS, 0476100000044+I_SNS, 01000000000000+I_IOX, 01000000200000+I_IOX, 01100000000000+I_TCH, 01200000000000+I_IOX, 01200000200000+I_IOX, 01300000000000+I_IOX, 01300000200000+I_IOX, 01400000000000+I_IOX, 01400000200000+I_IOX, 01500000000000+I_IOX, 01500000200000+I_IOX, 01600000000000+I_IOX, 01600000200000+I_IOX, 01700000000000+I_IOX, 01700000200000+I_IOX, 02000000000000+I_TCH, 02000000200000+I_IOX, 02100000000000+I_TCH, 02100000200000+I_TCH, 02200000000000+I_I9N, 02220000000000+I_TCH, 02200000200000+I_I9N, 02220000200000+I_TCH, 02240000000000+I_I9N, 02260000000000+I_TCH, 02240000200000+I_I9N, 02300000000000+I_I9S, 02300000200000+I_I9S, 02340000000000+I_I9S, 02400000000000+I_IOX, 02500000000000+I_IOX, 02500000200000+I_IOX, 02600000200000+I_I9S, 02640000000000+I_I9S, 02640000200000+I_I9S, 02700000000000+I_I9S, 02700000200000+I_IOX, 0 }; /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: return = status code */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { uint32 i, j, k, l, fmt, c, fld[3]; DEVICE *dptr; t_uint64 inst; inst = val[0]; if (uptr == NULL) uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; if (sw & SWMASK ('C')) { /* character? */ c = (uint32) (inst & 077); fprintf (of, "%c", cvt_code_to_ascii (c, sw)); return SCPE_OK; } if (sw & SWMASK ('S')) { /* string? */ for (i = 36; i > 0; i = i - 6) { c = (uint32) ((inst >> (i - 6)) & 077); fprintf (of, "%c", cvt_code_to_ascii (c, sw)); } return SCPE_OK; } if (!(sw & (SWMASK ('M')|SWMASK ('I')|SWMASK ('N'))) || /* M, N or I? */ (dptr->dwidth != 36)) return SCPE_ARG; /* Instruction decode */ fld[0] = ((uint32) inst & 0777777); fld[1] = GET_TAG (inst); /* get 3 fields */ fld[2] = GET_DEC (inst); if (sw & SWMASK ('I')) /* decode as 7607? */ inst |= IFAKE_7607; if (sw & SWMASK ('N')) /* decode as 7909? */ inst |= IFAKE_7909; for (i = 0; opc_v[i] > 0; i++) { /* loop thru ops */ j = (int32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ if ((opc_v[i] & DFAKE) == (inst & masks[j])) { /* match? */ if (inst & ind_test[j]) /* indirect? */ fprintf (of, "%s*", opcode[i]); else fprintf (of, "%s", opcode[i]); /* opcode */ for (k = 0; k < 3; k++) fld[k] = fld[k] & fld_max[j][k]; for (k = 0; k < 3; k++) { /* loop thru fields */ fmt = fld_fmt[j][k]; /* get format */ if (fmt == INST_P_XIT) return SCPE_OK; switch (fmt) { /* case on format */ case INST_P_PNT: /* print nz, else term */ for (l = k, c = 0; l < 3; l++) c |= fld[k]; if (c == 0) return SCPE_OK; case INST_P_PNZ: /* print non-zero */ fputc (k? ',': ' ', of); if (fld[k]) fprintf (of, "%-o", fld[k]); break; case INST_P_PRA: /* print always */ fputc (k? ',': ' ', of); fprintf (of, "%-o", fld[k]); break; case INST_P_SKP: /* skip */ break; } /* end switch */ } /* end for k */ return SCPE_OK; /* done */ } /* end if */ } /* end for i */ return SCPE_ARG; } /* Convert character to code to ASCII -b BCD -a business-chain */ uint32 cvt_code_to_ascii (uint32 c, int32 sw) { if (sw & SWMASK ('B')) { if (sw & SWMASK ('A')) return bcd_to_ascii_a[c]; else return bcd_to_ascii_h[c]; } else if (sw & SWMASK ('A')) return nine_to_ascii_a[c]; else return nine_to_ascii_h[c]; } /* 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) { uint32 i, j, c; t_uint64 fld[3]; t_bool ind; t_stat r; char gbuf[CBUFSIZE]; while (isspace (*cptr)) cptr++; if ((sw & SWMASK ('C')) || ((*cptr == '\'') && cptr++)) { /* character? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cvt_ascii_to_code (cptr[0] & 0177, sw); return SCPE_OK; } if ((sw & SWMASK ('S')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; for (i = 0; i < 6; i++) { c = cptr[0] & 0177; if (c) val[0] = (val[0] << 6) | ((t_value) cvt_ascii_to_code (c, sw)); else { val[0] = val[0] << (6 * (6 - i)); break; } } return SCPE_OK; } cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ j = strlen (gbuf); /* get length */ if (gbuf[j - 1] == '*') { /* indirect? */ ind = TRUE; gbuf[j - 1] = 0; } else ind = FALSE; for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; if (opcode[i] == NULL) return SCPE_ARG; j = (uint32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ val[0] = opc_v[i] & DMASK; if (ind) { if (ind_test[j]) val[0] |= ind_test[j]; else return SCPE_ARG; } for (i = 0; i < 3; i++) /* clear inputs */ fld[i] = 0; for (i = 0; (i < 3) && *cptr; i++) { /* parse inputs */ if (i < 2) /* get glyph */ cptr = get_glyph (cptr, gbuf, ','); else cptr = get_glyph (cptr, gbuf, 0); if (gbuf[0]) { /* anything? */ fld[i] = get_uint (gbuf, 8, fld_max[j][i], &r); if ((r != SCPE_OK) || (fld_max[j][i] == 0)) return SCPE_ARG; } } if (*cptr != 0) /* junk at end? */ return SCPE_ARG; val[0] = val[0] | fld[0] | (fld[1] << INST_V_TAG) | (fld[2] << INST_V_DEC); return SCPE_OK; } /* Convert ASCII to character code -b BCD */ uint32 cvt_ascii_to_code (uint32 c, int32 sw) { if (sw & SWMASK ('B')) return ascii_to_bcd[c]; else return ascii_to_nine[c]; } simh-3.8.1/I7094/i7094_cpu.c0000644000175000017500000027157511112035160013153 0ustar vlmvlm/* i7094_cpu.c: IBM 7094 CPU simulator Copyright (c) 2003-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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu 7094 central processor 28-Apr-07 RMS Removed clock initialization 29-Oct-06 RMS Added additional expanded core instructions 17-Oct-06 RMS Fixed the fix in halt IO wait loop 16-Jun-06 RMS Fixed bug in halt IO wait loop The register state for the 7094 is: AC accumulator MQ multiplier-quotient register SI storage indicators KEYS<0:35> front panel keys (switches) IC<0:14> instruction counter (called PC here) XR<0:14>[8] index registers (XR[0] is always 0) SSW<0:5> sense switches SLT<0:3> sense lights OVF AC overflow MQO MQ overflow DVC divide check IOC I/O check TTRAP transfer trap mode CTRAP copy trap mode (for 709 compatibility) FTRAP floating trap mode (off is 704 compatibility) STRAP select trap mode STORN storage nullifcation mode MULTI multi-tag mode (7090 compatibility) CTSS required a set of special features: memory extension (to 65K), protection, and relocation. Additional state: USER user mode INST_BASE instruction memory select (A vs B core) DATA_BASE data memory select (A vs B core) IND_RELOC<0:6> relocation value (block number) IND_START<0:6> start address block IND_LIMIT<0:6> limit address block The 7094 had five instruction formats: memory reference, memory reference with count, convert, decrement, and immediate. 00000000011 11 1111 112 222222222333333 S12345678901 23 4567 890 123456789012345 +------------+--+----+---+---------------+ | opcode |ND|0000|tag| address | memory reference +------------+--+----+---+---------------+ 00000000011 111111 112 222222222333333 S12345678901 234567 890 123456789012345 +------------+------+---+---------------+ | opcode | count|tag| address | memory reference +------------+------+---+---------------+ with count 000000000 11111111 11 2 222222222333333 S123456789 01234567 89 0 123456789012345 +----------+--------+--+-+---------------+ | opcode | count |00|X| address | convert +----------+--------+--+-+---------------+ 00 000000011111111 112 222222222333333 S12 345678901234567 890 123456789012345 +---+---------------+---+---------------+ |opc| decrement |tag| address | decrement +---+---------------+---+---------------+ 00000000011 111111 112222222222333333 S12345678901 234567 890123456789012345 +------------+------+------------------+ | opcode |000000| immediate | immediate +------------+------+------------------+ This routine is the instruction decode routine for the 7094. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until a stop condition occurs. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction illegal instruction illegal I/O operation for device illegal I/O operation for channel breakpoint encountered nested XEC's exceeding limit divide check I/O error in I/O simulator 2. Data channel traps. The 7094 is a channel-based system. Channels can generate traps for errors and status conditions. Channel trap state: ch_flags[0..7] flags for channels A..H chtr_enab channel trap enables chtr_inht channel trap inhibit due to trap (cleared by RCT) chtr_inhi channel trap inhibit due to XEC, ENAB, RCT, RDS, or WDS (cleared after one instruction) Channel traps are summarized in variable chtr_pend. 3. Arithmetic. The 7094 uses signed magnitude arithmetic for integer and floating point calculations, and 2's complement arithmetic for indexing calculations. 4. Adding I/O devices. These modules must be modified: i7094_defs.h add device definitions i7094_io.c add device address mapping i7094_sys.c add sim_devices table entry */ #include "i7094_defs.h" #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 | inst_base) #define HIST_MIN 64 #define HIST_MAX (2 << 18) #define HIST_CH_C 1 /* include channel */ #define HIST_CH_I 2 /* include IO */ #define HALT_IO_LIMIT ((2 << 18) + 1) /* max wait to stop */ t_uint64 *M = NULL; /* memory */ t_uint64 AC = 0; /* AC */ t_uint64 MQ = 0; /* MQ */ t_uint64 SI = 0; /* indicators */ t_uint64 KEYS = 0; /* storage keys */ uint32 PC = 0; /* PC (IC) */ uint32 oldPC = 0; /* prior PC */ uint32 XR[8] = { 0 }; /* index registers */ uint32 SSW = 0; /* sense switches */ uint32 SLT = 0; /* sense lights */ uint32 ch_req = 0; /* channel requests */ uint32 chtr_pend = 0; /* chan trap pending */ uint32 chtr_inht = 0; /* chan trap inhibit trap */ uint32 chtr_inhi = 0; /* chan trap inhibit inst */ uint32 chtr_enab = 0; /* chan trap enables */ uint32 mode_ttrap = 0; /* transfer trap mode */ uint32 mode_ctrap = 0; /* copy trap mode */ uint32 mode_strap = 0; /* select trap mode */ uint32 mode_ftrap = 0; /* floating trap mode */ uint32 mode_storn = 0; /* storage nullification */ uint32 mode_multi = 0; /* multi-index mode */ uint32 ind_ovf = 0; /* overflow */ uint32 ind_mqo = 0; /* MQ overflow */ uint32 ind_dvc = 0; /* divide check */ uint32 ind_ioc = 0; /* IO check */ uint32 cpu_model = I_9X|I_94; /* CPU type */ uint32 mode_user = 0; /* (CTSS) user mode */ uint32 ind_reloc = 0; /* (CTSS) relocation */ uint32 ind_start = 0; /* (CTSS) prot start */ uint32 ind_limit = 0; /* (CTSS) prot limit */ uint32 inst_base = 0; /* (CTSS) inst A/B sel */ uint32 data_base = 0; /* (CTSS) data A/B sel */ uint32 xec_max = 16; /* XEC chain limit */ uint32 ht_pend = 0; /* HTR pending */ uint32 ht_addr = 0; /* HTR address */ uint32 stop_illop = 1; /* stop on ill op */ uint32 cpu_astop = 0; /* address stop */ static uint32 eamask = AMASK; /* (dynamic) addr mask */ 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 */ uint32 hst_ch = 0; /* channel history */ InstHistory *hst = NULL; /* instruction history */ extern uint32 ch_sta[NUM_CHAN]; extern uint32 ch_flags[NUM_CHAN]; extern DEVICE mt_dev[NUM_CHAN]; extern DEVICE ch_dev[NUM_CHAN]; extern FILE *sim_deb; extern int32 sim_int_char; extern int32 sim_interval; extern int32 sim_switches; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ /* Forward and external declarations */ 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_model (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_bool ReadI (uint32 va, t_uint64 *dat); t_bool Read (uint32 va, t_uint64 *dat); t_bool Write (uint32 va, t_uint64 dat); void WriteTA (uint32 pa, uint32 addr); void WriteTAD (uint32 pa, uint32 addr, uint32 decr); void TrapXfr (uint32 newpc); t_bool fp_trap (uint32 spill); t_bool prot_trap (uint32 decr); t_bool sel_trap (uint32 va); t_bool cpy_trap (uint32 va); uint32 get_xri (uint32 tag); uint32 get_xrx (uint32 tag); void put_xr (uint32 tag, uint32 dat); t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd); extern uint32 chtr_eval (uint32 *decr); extern void op_add (t_uint64 sr); extern void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc); extern t_bool op_div (t_uint64 sr, uint32 sc); extern uint32 op_fad (t_uint64 sr, t_bool norm); extern uint32 op_fmp (t_uint64 sr, t_bool norm); extern uint32 op_fdv (t_uint64); extern uint32 op_dfad (t_uint64 shi, t_uint64 slo, t_bool norm); extern uint32 op_dfmp (t_uint64 shi, t_uint64 slo, t_bool norm); extern uint32 op_dfdv (t_uint64 shi, t_uint64 slo); extern void op_als (uint32 ea); extern void op_ars (uint32 ea); extern void op_lls (uint32 ea); extern void op_lrs (uint32 ea); extern void op_lgl (uint32 ea); extern void op_lgr (uint32 ea); extern t_stat op_pse (uint32 ea); extern t_stat op_mse (uint32 ea); extern t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit); extern t_stat ch_op_nds (uint32 ch, uint32 ds, uint32 unit); extern t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset); extern t_stat ch_op_store (uint32 ch, t_uint64 *dat); extern t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat); extern t_stat ch_proc (uint32 ch); /* 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, STDMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, PC, ASIZE) }, { ORDATA (AC, AC, 38) }, { ORDATA (MQ, MQ, 36) }, { ORDATA (SI, SI, 36) }, { ORDATA (KEYS, KEYS, 36) }, { ORDATA (XR1, XR[1], 15) }, { ORDATA (XR2, XR[2], 15) }, { ORDATA (XR3, XR[3], 15) }, { ORDATA (XR4, XR[4], 15) }, { ORDATA (XR5, XR[5], 15) }, { ORDATA (XR6, XR[6], 15) }, { ORDATA (XR7, XR[7], 15) }, { FLDATA (SS1, SSW, 5) }, { FLDATA (SS2, SSW, 4) }, { FLDATA (SS3, SSW, 3) }, { FLDATA (SS4, SSW, 2) }, { FLDATA (SS5, SSW, 1) }, { FLDATA (SS6, SSW, 0) }, { FLDATA (SL1, SLT, 3) }, { FLDATA (SL2, SLT, 2) }, { FLDATA (SL3, SLT, 1) }, { FLDATA (SL4, SLT, 0) }, { FLDATA (OVF, ind_ovf, 0) }, { FLDATA (MQO, ind_mqo, 0) }, { FLDATA (DVC, ind_dvc, 0) }, { FLDATA (IOC, ind_ioc, 0) }, { FLDATA (TTRAP, mode_ttrap, 0) }, { FLDATA (CTRAP, mode_ctrap, 0) }, { FLDATA (STRAP, mode_strap, 0) }, { FLDATA (FTRAP, mode_ftrap, 0) }, { FLDATA (STORN, mode_storn, 0) }, { FLDATA (MULTI, mode_multi, 0) }, { ORDATA (CHREQ, ch_req, NUM_CHAN) }, { FLDATA (CHTR_PEND, chtr_pend, 0) }, { FLDATA (CHTR_INHT, chtr_inht, 0) }, { FLDATA (CHTR_INHI, chtr_inhi, 0) }, { ORDATA (CHTR_ENAB, chtr_enab, 30) }, { FLDATA (USERM, mode_user, 0) }, { FLDATA (IMEM, inst_base, BCORE_V) }, { FLDATA (DMEM, data_base, BCORE_V) }, { GRDATA (RELOC, ind_reloc, 8, VA_N_BLK, VA_V_BLK) }, { GRDATA (START, ind_start, 8, VA_N_BLK, VA_V_BLK) }, { GRDATA (LIMIT, ind_limit, 8, VA_N_BLK, VA_V_BLK) }, { ORDATA (OLDPC, oldPC, ASIZE), REG_RO }, { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { FLDATA (HTPEND, ht_pend, 0) }, { ORDATA (HTADDR, ht_addr, ASIZE) }, { DRDATA (XECMAX, xec_max, 8), PV_LEFT + REG_NZ }, { ORDATA (WRU, sim_int_char, 8) }, { FLDATA (STOP_ILL, stop_illop, 0) }, { ORDATA (MODEL, cpu_model, 4), REG_HRO }, { NULL } }; MTAB cpu_mod[] = { { MTAB_XTD | MTAB_VDV, I_9X|I_94|I_CT, "MODEL", "CTSS", &cpu_set_model, &cpu_show_model, NULL }, { MTAB_XTD | MTAB_VDV, I_9X|I_94, NULL, "7094", &cpu_set_model, NULL, NULL }, { MTAB_XTD | MTAB_VDV, I_9X, NULL, "7090", &cpu_set_model, NULL, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, PASIZE, 1, 8, 36, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, DEV_DEBUG }; /* Instruction decode table */ const uint8 op_flags[1024] = { I_XN , 0 , 0 , 0 , /* +000 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , I_XN|I_9X , I_XN , 0 , /* +020 */ I_XN , 0 , I_XN , I_XN , I_XN , I_XN , I_XN , I_XN , 0 , 0 , 0 , 0 , I_XN|I_9X , I_9X , I_XN|I_9X , I_9X , /* +040 */ I_9X , 0 , I_XN|I_9X , 0 , 0 , I_9X , 0 , 0 , I_9X , I_9X , I_9X , I_9X , I_XN , I_XN , I_XN , I_XN , /* +060 */ I_XN , I_XN , I_XN , I_XN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , I_XN|I_CT , 0 , 0 , /* +100 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , I_9X , I_9X , I_9X , I_XN , 0 , 0 , 0 , /* +120 */ 0 , 0 , 0 , 0 , 0 , I_9X , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , 0 , 0 , 0 , /* +140 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN|I_9X , I_XN|I_9X , 0 , /* +160 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , 0 , 0 , /* +200 */ I_XNR , I_XNR , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, I_XNR , 0 , 0 , /* +220 */ I_XNR|I_9X, I_XNR , I_XNR|I_9X, I_XNR , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, I_XNR , 0 , 0 , /* +240 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XND|I_94, 0 , 0 , /* +260 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* +300 */ I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , I_XNR|I_9X, 0 , /* +320 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , 0 , 0 , /* +340 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , 0 , /* +360 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XNR|I_9X, I_XNR , 0 , /* +400 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* +420 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_94, /* +440 */ I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , 0 , 0 , 0 , /* +460 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , I_XNR , 0 , /* +500 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , I_XNR , 0 , /* +520 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_R , I_R , 0 , 0 , I_XN , I_XN , I_XN , I_XN , /* +540 */ I_XN , I_XN , I_XN , I_XN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , I_XNR|I_CT, 0 , /* +560 */ I_XNR , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , I_XN , I_XN , 0 , /* +600 */ I_XN|I_9X , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XNR , 0 , /* +620 */ 0 , I_XNR|I_9X, 0 , 0 , I_XNR|I_9X, 0 , 0 , 0 , I_R , 0 , I_R|I_94 , 0 , I_XN , I_XN , I_XN , I_XN , /* +640 */ I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* +660 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , 0 , 0 , 0 , /* +700 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* +720 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* +740 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_94 , 0 , I_X , 0 , I_X , I_X , /* +760 */ I_X , I_X , I_X , I_X , I_X , I_X , I_X , 0 , 0 , 0 , 0 , 0 , I_XN , 0 , 0 , 0 , /* -000 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , I_XN|I_9X , I_XN , 0 , /* -020 */ I_XN , 0 , I_XN , I_XN , I_XN , I_XN , I_XN , I_XN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -040 */ 0 , 0 , I_9X , 0 , 0 , I_9X , 0 , 0 , I_9X , I_9X , I_9X , I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , /* -060 */ I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , I_XN|I_CT , 0 , 0 , /* -100 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , I_9X , I_9X , I_9X , I_XN , 0 , 0 , 0 , /* -120 */ 0 , 0 , 0 , 0 , I_9X , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN|I_9X , 0 , 0 , 0 , /* -140 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , I_9X , I_9X , I_9X , 0 , 0 , 0 , 0 , /* -160 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , 0 , 0 , /* -200 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -220 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XND|I_94, I_XND|I_94, 0 , 0 , /* -240 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XND|I_94, 0 , 0 , /* -260 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* -300 */ I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , 0 , 0 , /* -320 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , 0 , 0 , 0 , /* -340 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -360 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , 0 , 0 , /* -400 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -420 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -440 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -460 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR , I_XNR , 0 , 0 , /* -500 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , 0 , 0 , /* -520 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_R , I_R , 0 , 0 , I_XN , I_XN , I_XN , I_XN , /* -540 */ I_XN , I_XN , I_XNR , I_XN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -560 */ I_XNR|I_CT, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XN , 0 , I_XNR|I_9X, I_XN|I_94 , /* -600 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_XNR|I_9X, 0 , 0 , 0 , /* -620 */ 0 , I_XNR , 0 , 0 , 0 , 0 , 0 , 0 , I_R , 0 , I_R|I_94 , 0 , I_XN , I_XN , I_XN , I_XN , /* -640 */ I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -660 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_9X , 0 , 0 , 0 , /* -700 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -720 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* -740 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , I_94 , 0 , I_X , I_X|I_CT , 0 , I_X , /* -760 */ 0 , I_X , 0 , 0 , 0 , 0 , I_X , I_X , I_9X , 0 , 0 , 0 }; /* Instruction execution routine */ t_stat sim_instr (void) { t_stat reason = SCPE_OK; t_uint64 IR, SR, t, t1, t2, sr1; uint32 op, fl, tag, tagi, addr, ea; uint32 ch, dec, xr, xec_cnt, trp; uint32 i, j, sc, s1, s2, spill; t_bool tracing; /* Restore register state */ ch_set_map (); /* set dispatch map */ if (!(cpu_model & (I_94|I_CT))) /* ~7094? MTM always on */ mode_multi = 1; eamask = mode_storn? A704_MASK: AMASK; /* set eff addr mask */ inst_base = inst_base & ~AMASK; /* A/B sel is 1b */ data_base = data_base & ~AMASK; ind_reloc = ind_reloc & VA_BLK; /* canonical form */ ind_start = ind_start & VA_BLK; ind_limit = (ind_limit & VA_BLK) | VA_OFF; chtr_pend = chtr_eval (NULL); /* eval chan traps */ tracing = ((hst_lnt != 0) || DEBUG_PRS (cpu_dev)); if (ht_pend) { /* HTR pending? */ oldPC = (PC - 1) & AMASK; ht_pend = 0; /* clear flag */ PCQ_ENTRY; if (mode_ttrap) { /* trap? */ WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ TrapXfr (TRAP_TRA_PC); /* trap */ } else PC = ht_addr; /* branch */ } /* Main instruction fetch/decode loop */ while (reason == SCPE_OK) { /* loop until error */ if (cpu_astop) { /* debug stop? */ cpu_astop = 0; reason = SCPE_STOP; break; } if (sim_interval <= 0) { /* intv cnt expired? */ if (reason = sim_process_event ()) /* process events */ break; chtr_pend = chtr_eval (NULL); /* eval chan traps */ } for (i = 0; ch_req && (i < NUM_CHAN); i++) { /* loop thru channels */ if (ch_req & REQ_CH (i)) { /* channel request? */ if (reason = ch_proc (i)) break; } chtr_pend = chtr_eval (NULL); if (reason) /* error? */ break; } if (chtr_pend) { /* channel trap? */ addr = chtr_eval (&trp); /* get trap info, clr */ chtr_inht = 1; /* inhibit traps */ chtr_pend = 0; /* no trap pending */ WriteTAD (addr, PC, trp); /* wr trap addr,flag */ IR = ReadP (addr + 1); /* get trap instr */ oldPC = PC; /* save current PC */ } else { if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } if (chtr_inhi) { /* 1 cycle inhibit? */ chtr_inhi = 0; /* clear */ chtr_pend = chtr_eval (NULL); /* re-evaluate */ } oldPC = PC; /* save current PC */ PC = (PC + 1) & eamask; /* increment PC */ if (!ReadI (oldPC, &IR)) /* get inst; trap? */ continue; } sim_interval = sim_interval - 1; xec_cnt = 0; /* clear XEC cntr */ XEC: tag = GET_TAG (IR); /* get tag */ addr = (uint32) IR & eamask; /* get base addr */ /* Decrement format instructions */ if (IR & INST_T_DEC) { /* decrement type? */ op = GET_OPD (IR); /* get opcode */ dec = GET_DEC (IR); /* get decrement */ xr = get_xrx (tag); /* get xr, upd MTM */ if (tracing) { /* trace or history? */ if (hst_lnt) /* history enabled? */ cpu_ent_hist (oldPC|HIST_PC, xr, IR, 0); if (DEBUG_PRS (cpu_dev)) cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, xr, IR, AC, MQ, SI, 0); } switch (op) { case 01: /* TXI */ put_xr (tag, xr + dec); /* xr += decr */ PCQ_ENTRY; if (mode_ttrap) { /* trap? */ WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ TrapXfr (TRAP_TRA_PC); /* trap */ } else PC = addr; /* branch */ break; case 02: /* TIX */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (xr > dec) { /* if xr > decr */ put_xr (tag, xr - dec); /* xr -= decr */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = addr; /* branch */ } break; case 03: /* TXH */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (xr > dec) { /* if xr > decr */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = addr; /* branch */ } break; case 05: /* STR */ WriteTA (TRAP_STD_SAV, PC); /* save inst+1 */ PCQ_ENTRY; PC = TRAP_STR_PC; /* branch to 2 */ break; case 06: /* TNX */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (xr > dec) /* if xr > decr */ put_xr (tag, xr - dec); else { /* xr -= decr */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = addr; /* branch */ } break; case 07: /* TXL */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (xr <= dec) { /* if xr <= decr */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = addr; /* branch */ } break; } } /* end if */ /* Normal format instructions */ else { op = GET_OPC (IR); /* get opcode */ fl = op_flags[op]; /* get flags */ if (fl & I_MODEL & ~cpu_model) { /* invalid for model? */ if (stop_illop) /* possible stop */ reason = STOP_ILLEG; continue; } if (tag && (fl & I_X)) /* tag and indexable? */ ea = (addr - get_xri (tag)) & eamask; /* do indexing */ else ea = addr; if (TST_IND (IR) && (fl & I_N)) { /* indirect? */ if (!ReadI (ea, &SR)) /* get ind; trap? */ continue; addr = (uint32) SR & eamask; /* get address */ tagi = GET_TAG (SR); /* get tag */ if (tagi) /* tag? */ ea = (addr - get_xri (tagi)) & eamask; /* do indexing */ else ea = addr; } if ((fl & I_R) && !Read (ea, &SR)) /* read opnd; trap? */ continue; else if (fl & I_D) { /* double prec? */ if ((ea & 1) && fp_trap (TRAP_F_ODD)) continue; if (!Read (ea, &SR)) /* SR gets high */ continue; if (!Read (ea | 1, &sr1)) /* "sr1" gets low */ continue; } if (tracing) { /* tracing or history? */ if (hst_lnt) /* history enabled? */ cpu_ent_hist (oldPC|HIST_PC, ea, IR, SR); if (DEBUG_PRS (cpu_dev)) cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, ea, IR, AC, MQ, SI, SR); } switch (op) { /* case on opcode */ /* Positive instructions */ case 00000: /* HTR */ case 01000: /* also -HTR */ if (prot_trap (0)) /* user mode? */ break; ht_pend = 1; /* transfer pending */ ht_addr = ea; /* save address */ reason = STOP_HALT; /* halt if I/O done */ break; case 00020: /* TRA */ case 01020: /* also -TRA */ PCQ_ENTRY; if (mode_ttrap) { /* trap? */ WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ TrapXfr (TRAP_TRA_PC); /* trap */ } else PC = ea; /* branch */ break; case 00021: /* TTR */ PCQ_ENTRY; PC = ea; /* branch, no trap */ break; case 00040: /* TLQ */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); s1 = (AC & AC_S)? 1: 0; /* get AC, MQ sign, */ s2 = (MQ & SIGN)? 1: 0; /* magnitude */ t1 = AC & AC_MMASK; t2 = MQ & MMASK; /* signs differ? */ if ((s1 != s2)? s2: /* y, br if MQ- */ ((t1 != t2) && (s2 ^ (t1 > t2)))) { /* n, br if sgn-^AC>MQ */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00041: /* IIA */ SI = SI ^ (AC & DMASK); break; case 00042: /* TIO */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((SI & AC) == (AC & DMASK)) { /* if ind on */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00043: /* OAI */ SI = SI | (AC & DMASK); break; case 00044: /* PAI */ SI = AC & DMASK; break; case 00046: /* TIF */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((SI & AC) == 0) { /* if ind off */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00051: /* IIR */ SI = SI ^ (IR & RMASK); break; case 00054: /* RFT */ t = IR & RMASK; if ((SI & t) == 0) /* if ind off, skip */ PC = (PC + 1) & eamask; break; case 00055: /* SIR */ SI = SI | (IR & RMASK); break; case 00056: /* RNT */ t = IR & RMASK; if ((SI & t) == t) /* if ind on, skip */ PC = (PC + 1) & eamask; break; case 00057: /* RIR */ SI = SI & ~(IR & RMASK); break; case 00074: /* TSX */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (tag) /* save -inst loc */ put_xr (tag, ~oldPC + 1); PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ break; case 00100: /* TZE */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((AC & AC_MMASK) == 0) { /* if AC Q,P,1-35 = 0 */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00101: /* (CTSS) TIA */ if (prot_trap (0)) /* not user mode? */ break; PCQ_ENTRY; PC = ea; inst_base = 0; break; case 00114: case 00115: case 00116: case 00117: /* CVR */ sc = GET_CCNT (IR); SR = ea; while (sc) { ea = (uint32) ((AC & 077) + SR) & eamask; if (!Read (ea, &SR)) break; AC = (AC & AC_S) | ((AC >> 6) & 0017777777777) | (SR & 0770000000000); sc--; } if ((sc == 0) && (IR & INST_T_CXR1)) put_xr (1, (uint32) SR); break; case 00120: /* TPL */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((AC & AC_S) == 0) { /* if AC + */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00131: /* XCA */ t = MQ; MQ = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); AC = (t & MMASK) | ((t & SIGN)? AC_S: 0); break; case 00140: /* TOV */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (ind_ovf) { /* if overflow */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ ind_ovf = 0; /* clear overflow */ } break; case 00161: /* TQO */ if (!mode_ftrap) { /* only in 704 mode */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (ind_mqo) { /* if MQ overflow */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ ind_mqo = 0; /* clear overflow */ } } break; case 00162: /* TQP */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((MQ & SIGN) == 0) { /* if MQ + */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00200: /* MPY */ op_mpy (0, SR, 043); break; case 00204: /* VLM */ case 00205: /* for diagnostic */ sc = GET_VCNT (IR); op_mpy (0, SR, sc); break; case 00220: /* DVH */ if (op_div (SR, 043)) { ind_dvc = 1; if (!prot_trap (0)) reason = STOP_DIVCHK; } break; case 00221: /* DVP */ if (op_div (SR, 043)) ind_dvc = 1; break; case 00224: /* VDH */ case 00226: /* for diagnostic */ sc = GET_VCNT (IR); if (op_div (SR, sc)) { ind_dvc = 1; if (!prot_trap (0)) reason = STOP_DIVCHK; } break; case 00225: /* VDP */ case 00227: /* for diagnostic */ sc = GET_VCNT (IR); if (op_div (SR, sc)) ind_dvc = 1; break; case 00240: /* FDH */ spill = op_fdv (SR); if (spill == TRAP_F_DVC) { ind_dvc = 1; if (!prot_trap (0)) reason = STOP_DIVCHK; } else if (spill) fp_trap (spill); break; case 00241: /* FDP */ spill = op_fdv (SR); if (spill == TRAP_F_DVC) ind_dvc = 1; else if (spill) fp_trap (spill); break; case 00260: /* FMP */ spill = op_fmp (SR, 1); /* MQ * SR */ if (spill) fp_trap (spill); break; case 00261: /* DFMP */ spill = op_dfmp (SR, sr1, 1); if (spill) fp_trap (spill); break; case 00300: /* FAD */ spill = op_fad (SR, 1); if (spill) fp_trap (spill); break; case 00301: /* DFAD */ spill = op_dfad (SR, sr1, 1); if (spill) fp_trap (spill); break; case 00302: /* FSB */ spill = op_fad (SR ^ SIGN, 1); if (spill) fp_trap (spill); break; case 00303: /* DFSB */ spill = op_dfad (SR ^ SIGN, sr1, 1); if (spill) fp_trap (spill); break; case 00304: /* FAM */ spill = op_fad (SR & ~SIGN, 1); if (spill) fp_trap (spill); break; case 00305: /* DFAM */ spill = op_dfad (SR & ~SIGN, sr1, 1); if (spill) fp_trap (spill); break; case 00306: /* FSM */ spill = op_fad (SR | SIGN, 1); if (spill) fp_trap (spill); break; case 00307: /* DFSM */ spill = op_dfad (SR | SIGN, sr1, 1); if (spill) fp_trap (spill); break; case 00320: /* ANS */ SR = AC & SR; Write (ea, SR); break; case 00322: /* ERA */ AC = (AC ^ SR) & DMASK; /* AC S,Q cleared */ break; case 00340: /* CAS */ s1 = (AC & AC_S)? 1: 0; /* get AC, MQ signs, */ s2 = (SR & SIGN)? 1: 0; t1 = AC & AC_MMASK; /* magnitudes */ t2 = SR & MMASK; if (s1 ^ s2) { /* diff signs? */ if (s1) /* AC < mem? skip 2 */ PC = (PC + 2) & eamask; } else if (t1 == t2) /* equal? skip 1 */ PC = (PC + 1) & eamask; else if ((t1 < t2) ^ s1) /* AC < mem, AC +, or */ PC = (PC + 2) & eamask; /* AC > mem, AC -? */ break; case 00361: /* ACL */ t = (AC + SR) & DMASK; /* AC P,1-35 + SR */ if (t < SR) /* end around carry */ t = (t + 1) & DMASK; AC = (AC & (AC_S | AC_Q)) | t; /* preserve AC S,Q */ break; case 00400: /* ADD */ op_add (SR); break; case 00401: /* ADM */ op_add (SR & MMASK); break; case 00402: /* SUB */ op_add (SR ^ SIGN); break; case 00420: /* HPR */ if (prot_trap (0)) /* user mode? */ break; reason = STOP_HALT; /* halt if I/O done */ break; case 00440: /* IIS */ SI = SI ^ SR; break; case 00441: /* LDI */ SI = SR; break; case 00442: /* OSI */ SI = SI | SR; break; case 00443: /* DLD */ AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); /* normal load */ if (!Read (ea | 1, &SR)) /* second load */ break; MQ = SR; if (ea & 1) /* trap after exec */ fp_trap (TRAP_F_ODD); break; case 00444: /* OFT */ if ((SI & SR) == 0) /* skip if ind off */ PC = (PC + 1) & eamask; break; case 00445: /* RIS */ SI = SI & ~SR; break; case 00446: /* ONT */ if ((SI & SR) == SR) /* skip if ind on */ PC = (PC + 1) & eamask; break; case 00460: /* LDA (704) */ cpy_trap (PC); break; case 00500: /* CLA */ AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); break; case 00502: /* CLS */ AC = (SR & MMASK) | ((SR & SIGN)? 0: AC_S); break; case 00520: /* ZET */ if ((SR & MMASK) == 0) /* skip if M 1-35 = 0 */ PC = (PC + 1) & eamask; break; case 00522: /* XEC */ if (xec_cnt++ >= xec_max) { /* xec chain limit? */ reason = STOP_XEC; /* stop */ break; } IR = SR; /* operand is new inst */ chtr_inhi = 1; /* delay traps */ chtr_pend = 0; /* no trap now */ goto XEC; /* start over */ case 00534: /* LXA */ if (tag) /* M addr -> xr */ put_xr (tag, (uint32) SR); break; case 00535: /* LAC */ if (tag) /* -M addr -> xr */ put_xr (tag, NEG ((uint32) SR)); break; case 00560: /* LDQ */ MQ = SR; break; case 00562: /* (CTSS) LRI */ if (prot_trap (0)) /* user mode? */ break; ind_reloc = ((uint32) SR) & VA_BLK; break; case 00564: /* ENB */ if (prot_trap (0)) /* user mode? */ break; chtr_enab = (uint32) SR; /* set enables */ chtr_inht = 0; /* clear inhibit */ chtr_inhi = 1; /* 1 cycle delay */ chtr_pend = 0; /* no traps now */ break; case 00600: /* STZ */ Write (ea, 0); break; case 00601: /* STO */ SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); Write (ea, SR); break; case 00602: /* SLW */ Write (ea, AC & DMASK); break; case 00604: /* STI */ Write (ea, SI); break; case 00621: /* STA */ SR = (SR & ~AMASK) | (AC & AMASK); Write (ea, SR); break; case 00622: /* STD */ SR = (SR & ~XMASK) | (AC & XMASK); Write (ea, SR); break; case 00625: /* STT */ SR = (SR & ~TMASK) | (AC & TMASK); Write (ea, SR); break; case 00630: /* STP */ SR = (SR & ~PMASK) | (AC & PMASK); Write (ea, SR); break; case 00634: /* SXA */ SR = (SR & ~AMASK) | /* xr -> M addr */ ((t_uint64) get_xrx (tag)); Write (ea, SR); break; case 00636: /* SCA */ SR = (SR & ~AMASK) | /* -xr -> M addr */ ((t_uint64) (NEG (get_xrx (tag)) & AMASK)); Write (ea, SR); break; case 00700: /* CPY (704) */ cpy_trap (PC); break; case 00734: /* PAX */ if (tag) /* AC addr -> xr */ put_xr (tag, (uint32) AC); break; case 00737: /* PAC */ if (tag) /* -AC addr -> xr */ put_xr (tag, NEG ((uint32) AC)); break; case 00754: /* PXA */ AC = get_xrx (tag); /* xr -> AC */ break; case 00756: /* PCA */ AC = NEG (get_xrx (tag)) & AMASK; /* -xr -> AC */ break; case 00760: /* PSE */ if (prot_trap (0)) /* user mode? */ break; reason = op_pse (ea); break; case 00761: /* NOP */ break; case 00763: /* LLS */ op_lls (ea); break; case 00765: /* LRS */ op_lrs (ea); break; case 00767: /* ALS */ op_als (ea); break; case 00771: /* ARS */ op_ars (ea); break; case 00774: /* AXT */ if (tag) /* IR addr -> xr */ put_xr (tag, addr); break; /* Negative instructions */ case 01021: /* ESNT */ mode_storn = 1; /* enter nullification */ PCQ_ENTRY; PC = ea; /* branch, no trap */ break; case 01042: /* RIA */ SI = SI & ~AC; break; case 01046: /* PIA */ AC = SI; break; case 01051: /* IIL */ SI = SI ^ ((IR & RMASK) << 18); break; case 01054: /* LFT */ t = (IR & RMASK) << 18; if ((SI & t) == 0) /* if ind off, skip */ PC = (PC + 1) & eamask; break; case 01055: /* SIL */ SI = SI | ((IR & RMASK) << 18); break; case 01056: /* LNT */ t = (IR & RMASK) << 18; if ((SI & t) == t) /* if ind on, skip */ PC = (PC + 1) & eamask; break; case 01057: /* RIL */ SI = SI & ~((IR & RMASK) << 18); break; case 01100: /* TNZ */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((AC & AC_MMASK) != 0) { /* if AC != 0 */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 01101: /* (CTSS) TIB */ if (prot_trap (0)) /* user mode? */ break; PCQ_ENTRY; PC = ea; mode_user = 1; inst_base = BCORE_BASE; break; case 01114: case 01115: case 01116: case 01117: /* CAQ */ sc = GET_CCNT (IR); SR = ea; while (sc) { ea = (uint32) ((MQ >> 30) + SR) & eamask; if (!Read (ea, &SR)) break; MQ = ((MQ << 6) & DMASK) | (MQ >> 30); AC = (AC & AC_S) | ((AC + SR) & AC_MMASK); sc--; } if ((sc == 0) && (IR & INST_T_CXR1)) put_xr (1, (uint32) SR); break; case 01120: /* TMI */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if ((AC & AC_S) != 0) { /* if AC - */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 01130: /* XCL */ t = MQ; MQ = AC & DMASK; AC = t; break; case 01140: /* TNO */ if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (!ind_ovf) { /* if no overflow */ PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } ind_ovf = 0; /* clear overflow */ break; case 01154: case 01155: case 01156: case 01157: /* CRQ */ sc = GET_CCNT (IR); SR = ea; while (sc) { ea = (uint32) ((MQ >> 30) + SR) & eamask; if (!Read (ea, &SR)) break; MQ = ((MQ << 6) & DMASK) | (SR >> 30); sc--; } if ((sc == 0) && (IR & INST_T_CXR1)) put_xr (1, (uint32) SR); break; case 01200: /* MPR */ op_mpy (0, SR, 043); if (MQ & B1) AC = (AC & AC_S) | ((AC + 1) & AC_MMASK); break; case 01240: /* DFDH */ spill = op_dfdv (SR, sr1); if (spill == TRAP_F_DVC) { ind_dvc = 1; if (!prot_trap (0)) reason = STOP_DIVCHK; } else if (spill) fp_trap (spill); break; case 01241: /* DFDP */ spill = op_dfdv (SR, sr1); if (spill == TRAP_F_DVC) ind_dvc = 1; else if (spill) fp_trap (spill); break; case 01260: /* UFM */ spill = op_fmp (SR, 0); if (spill) fp_trap (spill); break; case 01261: /* DUFM */ spill = op_dfmp (SR, sr1, 0); if (spill) fp_trap (spill); break; case 01300: /* UFA */ spill = op_fad (SR, 0); if (spill) fp_trap (spill); break; case 01301: /* DUFA */ spill = op_dfad (SR, sr1, 0); if (spill) fp_trap (spill); break; case 01302: /* UFS */ spill = op_fad (SR ^ SIGN, 0); if (spill) fp_trap (spill); break; case 01303: /* DUFS */ spill = op_dfad (SR ^ SIGN, sr1, 0); if (spill) fp_trap (spill); break; case 01304: /* UAM */ spill = op_fad (SR & ~SIGN, 0); if (spill) fp_trap (spill); break; case 01305: /* DUAM */ spill = op_dfad (SR & ~SIGN, sr1, 0); if (spill) fp_trap (spill); break; case 01306: /* USM */ spill = op_fad (SR | SIGN, 0); if (spill) fp_trap (spill); break; case 01307: /* DUSM */ spill = op_dfad (SR | SIGN, sr1, 0); if (spill) fp_trap (spill); break; case 01320: /* ANA */ AC = AC & SR; break; case 01340: /* LAS */ t = AC & AC_MMASK; /* AC Q,P,1-35 */ if (t < SR) PC = (PC + 2) & eamask; else if (t == SR) PC = (PC + 1) & eamask; break; case 01400: /* SBM */ op_add (SR | SIGN); break; case 01500: /* CAL */ AC = SR; break; case 01501: /* ORA */ AC = AC | SR; break; case 01520: /* NZT */ if ((SR & MMASK) != 0) PC = (PC + 1) & eamask; break; case 01534: /* LXD */ if (tag) /* M decr -> xr */ put_xr (tag, GET_DEC (SR)); break; case 01535: /* LDC */ if (tag) /* -M decr -> xr */ put_xr (tag, NEG (GET_DEC (SR))); break; case 01564: /* (CTSS) LPI */ if (prot_trap (0)) /* user mode? */ break; ind_start = ((uint32) SR) & VA_BLK; ind_limit = (GET_DEC (SR) & VA_BLK) | VA_OFF; break; case 01600: /* STQ */ Write (ea, MQ); break; case 01602: /* ORS */ SR = SR | (AC & DMASK); Write (ea, SR); break; case 01603: /* DST */ SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); if (!Write (ea, SR)) break; Write ((ea + 1) & eamask, MQ); break; case 01620: /* SLQ */ SR = (SR & RMASK) | (MQ & LMASK); Write (ea, SR); break; case 01625: /* STL */ SR = (SR & ~AMASK) | PC; Write (ea, SR); break; case 01634: /* SXD */ SR = (SR & ~XMASK) | /* xr -> M decr */ (((t_uint64) get_xrx (tag)) << INST_V_DEC); Write (ea, SR); break; case 01636: /* SCD */ SR = (SR & ~XMASK) | /* -xr -> M decr */ (((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC); Write (ea, SR); break; case 01700: /* CAD (704) */ cpy_trap (PC); break; case 01734: /* PDX */ if (tag) /* AC decr -> xr */ put_xr (tag, GET_DEC (AC)); break; case 01737: /* PDC */ if (tag) /* -AC decr -> xr */ put_xr (tag, NEG (GET_DEC (AC))); break; case 01754: /* PXD */ AC = ((t_uint64) get_xrx (tag)) << INST_V_DEC; break; /* xr -> AC decr */ case 01756: /* PCD */ AC = ((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC; break; /* -xr -> AC decr */ case 01760: /* MSE */ if (prot_trap (0)) /* user mode? */ break; reason = op_mse (ea); break; case 01761: /* (CTSS) ext core */ if (prot_trap (0)) /* user mode? */ break; if (ea == 041) /* SEA? */ data_base = 0; else if (ea == 042) /* SEB? */ data_base = BCORE_BASE; else if (ea == 043) { /* IFT? */ if (inst_base == 0) PC = (PC + 1) & eamask; } else if (ea == 044) { /* EFT? */ if (data_base == 0) PC = (PC + 1) & eamask; } else if (stop_illop) reason = STOP_ILLEG; break; case 01763: /* LGL */ op_lgl (ea); break; case 01765: /* LGR */ op_lgr (ea); break; case 01773: /* RQL */ sc = (ea & SCMASK) % 36; if (sc) MQ = ((MQ << sc) | (MQ >> (36 - sc))) & DMASK; break; case 01774: /* AXC */ if (tag) /* -IR addr -> xr */ put_xr (tag, NEG (addr)); break; /* IO instructions */ case 00022: case 00024: case 00026: /* TRCx */ case 01022: case 01024: case 01026: if (prot_trap (0)) /* user mode? */ break; ch = ((op & 077) - 00022) | ((op >> 9) & 01); if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && (ch_flags[ch] & CHF_TRC)) { PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; chtr_pend = chtr_eval (NULL); /* eval chan traps */ } break; case 00027: case 01027: if (prot_trap (0)) /* user mode? */ break; ch = 6 + ((op >> 9) & 01); if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && (ch_flags[ch] & CHF_TRC)) { PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; chtr_pend = chtr_eval (NULL); /* eval chan traps */ } break; case 00030: case 00031: case 00032: case 00033: /* TEFx */ case 01030: case 01031: case 01032: case 01033: if (prot_trap (0)) /* user mode? */ break; ch = ((op & 03) << 1) | ((op >> 9) & 01); if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (!BIT_TST (chtr_enab, CHTR_V_CME + ch) && (ch_flags[ch] & CHF_EOF)) { PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOF; chtr_pend = chtr_eval (NULL); /* eval chan traps */ } break; case 00060: case 00061: case 00062: case 00063: /* TCOx */ case 00064: case 00065: case 00066: case 00067: if (prot_trap (0)) /* user mode? */ break; ch = op & 07; if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (ch_sta[ch] != CHXS_IDLE) { PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 01060: case 01061: case 01062: case 01063: /* TCNx */ case 01064: case 01065: case 01066: case 01067: if (prot_trap (0)) /* user mode? */ break; ch = op & 07; if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); if (ch_sta[ch] == CHXS_IDLE) { PCQ_ENTRY; if (mode_ttrap) /* trap? */ TrapXfr (TRAP_TRA_PC); else PC = ea; /* branch */ } break; case 00540: case 00541: case 00542: case 00543: /* RCHx */ case 01540: case 01541: case 01542: case 01543: if (prot_trap (0)) /* user mode? */ break; ch = ((op & 03) << 1) | ((op >> 9) & 01); reason = ch_op_start (ch, ea, TRUE); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00544: case 00545: case 00546: case 00547: /* LCHx */ case 01544: case 01545: case 01546: case 01547: if (prot_trap (0)) /* user mode? */ break; ch = ((op & 03) << 1) | ((op >> 9) & 01); reason = ch_op_start (ch, ea, FALSE); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00640: case 00641: case 00642: case 00643: /* SCHx */ case 01640: case 01641: case 01642: case 01643: if (prot_trap (0)) /* user mode? */ break; ch = ((op & 03) << 1) | ((op >> 9) & 01); if ((reason = ch_op_store (ch, &SR)) == SCPE_OK) Write (ea, SR); break; case 00644: case 00645: case 00646: case 00647: /* SCDx */ case 01644: case 01645: case 01646: case 01647: ch = ((op & 03) << 1) | ((op >> 9) & 01); if ((reason = ch_op_store_diag (ch, &SR)) == SCPE_OK) Write (ea, SR); break; case 00762: /* RDS */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_ds (ch, CHSL_RDS, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00764: /* BSR */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_BSR, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00766: /* WRS */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_ds (ch, CHSL_WRS, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00770: /* WEF */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_WEF, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00772: /* REW */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_REW, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 01764: /* BSF */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_BSF, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 01772: /* RUN */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_RUN, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; case 00776: /* SDN */ if (prot_trap (0) || sel_trap (PC)) break; ch = GET_U_CH (IR); reason = ch_op_nds (ch, CHSL_SDN, GET_U_UNIT (ea)); chtr_pend = chtr_eval (NULL); /* eval chan traps */ break; default: if (stop_illop) reason = STOP_ILLEG; break; } } /* end else */ if (reason) { /* reason code? */ if (reason == ERR_STALL) { /* stall? */ PC = oldPC; /* back up PC */ reason = 0; } else if (reason == STOP_HALT) { /* halt? wait for IO */ t_stat r; for (i = 0; (i < HALT_IO_LIMIT) && !ch_qidle (); i++) { sim_interval = 0; if (r = sim_process_event ()) /* process events */ return r; chtr_pend = chtr_eval (NULL); /* eval chan traps */ while (ch_req) { /* until no ch req */ for (j = 0; j < NUM_CHAN; j++) { /* loop thru channels */ if (ch_req & REQ_CH (j)) { /* channel request? */ if (r = ch_proc (j)) return r; } chtr_pend = chtr_eval (NULL); } } /* end while ch_req */ } /* end for wait */ if (chtr_pend) /* trap? cancel HALT */ reason = 0; } /* end if HALT */ } /* end if reason */ } /* end while */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Get index register for indexing */ uint32 get_xri (uint32 tag) { tag = tag & INST_M_TAG; if (tag) { if (mode_multi) { uint32 r = 0; if (tag & 1) r = r | XR[1]; if (tag & 2) r = r | XR[2]; if (tag & 4) r = r | XR[4]; return r & eamask; } return XR[tag] & eamask; } return 0; } /* Get index register for instruction execution Instructions which are 'executing directly' on index registers rewrite the index register value. In multi-tag mode, this causes all registers involved in the OR function to receive the OR'd value. */ uint32 get_xrx (uint32 tag) { tag = tag & INST_M_TAG; if (tag) { if (mode_multi) { uint32 r = 0; if (tag & 1) r = r | XR[1]; if (tag & 2) r = r | XR[2]; if (tag & 4) r = r | XR[4]; put_xr (tag, r); return r & eamask; } return XR[tag] & eamask; } return 0; } /* Store index register */ void put_xr (uint32 tag, uint32 dat) { tag = tag & INST_M_TAG; dat = dat & eamask; if (tag) { if (mode_multi) { if (tag & 1) XR[1] = dat; if (tag & 2) XR[2] = dat; if (tag & 4) XR[4] = dat; } else XR[tag] = dat; } return; } /* Floating point trap */ t_bool fp_trap (uint32 spill) { if (mode_ftrap) { WriteTAD (TRAP_STD_SAV, PC, spill); PCQ_ENTRY; PC = TRAP_FP_PC; return TRUE; } else { if (spill & TRAP_F_AC) ind_ovf = 1; if (spill & TRAP_F_MQ) ind_mqo = 1; } return FALSE; } /* (CTSS) Protection trap */ t_bool prot_trap (uint32 decr) { if (mode_user) { WriteTAD (TRAP_PROT_SAV, PC, decr); PCQ_ENTRY; PC = TRAP_PROT_PC; return TRUE; } return FALSE; } /* Store trap address and decrement, with A/B select flags; clear A/B, user mode */ void WriteTAD (uint32 pa, uint32 addr, uint32 decr) { t_uint64 mem; if (inst_base) decr |= TRAP_F_BINST; if (data_base) decr |= TRAP_F_BDATA; mem = ReadP (pa) & ~(XMASK | AMASK); mem |= (((t_uint64) (decr & AMASK)) << INST_V_DEC) | ((t_uint64) (addr & AMASK)); WriteP (pa, mem); mode_ctrap = 0; mode_strap = 0; mode_storn = 0; mode_user = 0; inst_base = 0; data_base = 0; return; } /* Copy trap */ t_bool cpy_trap (uint32 va) { if (mode_ctrap) { WriteTA (TRAP_704_SAV, va); PCQ_ENTRY; TrapXfr (TRAP_CPY_PC); return TRUE; } return FALSE; } /* Select trap */ t_bool sel_trap (uint32 va) { if (mode_strap) { WriteTA (TRAP_704_SAV, va); PCQ_ENTRY; TrapXfr (TRAP_SEL_PC); return TRUE; } return FALSE; } /* Store trap address - do not alter state yet (might be TRA) */ void WriteTA (uint32 pa, uint32 dat) { t_uint64 mem; mem = ReadP (pa) & ~AMASK; mem |= (dat & AMASK); WriteP (pa, mem); return; } /* Set trap PC - second half of address-only trap */ void TrapXfr (uint32 newpc) { PC = newpc; mode_ctrap = 0; mode_strap = 0; mode_storn = 0; mode_user = 0; inst_base = 0; data_base = 0; return; } /* Read instruction and indirect */ t_bool ReadI (uint32 va, t_uint64 *val) { if (mode_user) { va = (va + ind_reloc) & AMASK; if ((va < ind_start) || (va > ind_limit)) { prot_trap (0); return FALSE; } } *val = M[va | inst_base]; return TRUE; } /* Read */ t_bool Read (uint32 va, t_uint64 *val) { if (mode_user) { va = (va + ind_reloc) & AMASK; if ((va < ind_start) || (va > ind_limit)) { prot_trap (0); return FALSE; } } *val = M[va | data_base]; return TRUE; } /* Write */ t_bool Write (uint32 va, t_uint64 dat) { if (mode_user) { va = (va + ind_reloc) & AMASK; if ((va < ind_start) || (va > ind_limit)) { prot_trap (0); return FALSE; } } M[va | data_base] = dat; return TRUE; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { ind_ovf = 0; ind_mqo = 0; ind_dvc = 0; ind_ioc = 0; ind_reloc = 0; ind_start = 0; ind_limit = 0; mode_ttrap = 0; mode_ctrap = 0; mode_strap = 0; mode_ftrap = 1; mode_storn = 0; if (cpu_model & (I_94|I_CT)) mode_multi = 0; else mode_multi = 1; mode_user = 0; inst_base = 0; data_base = 0; ch_req = 0; chtr_pend = chtr_enab = 0; chtr_inht = chtr_inhi = 0; ht_pend = 0; SLT = 0; XR[0] = 0; if (M == NULL) M = (t_uint64 *) calloc (MAXMEMSIZE, sizeof (t_uint64)); if (M == NULL) return SCPE_MEM; 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 ea, UNIT *uptr, int32 sw) { if (vptr == NULL) return SCPE_ARG; if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) return SCPE_NXM; if ((sw & SWMASK ('B')) || ((sw & SWMASK ('V')) && mode_user && inst_base)) ea = ea | BCORE_BASE; *vptr = M[ea] & DMASK; return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) { if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) return SCPE_NXM; if (sw & SWMASK ('B')) ea = ea | BCORE_BASE; M[ea] = val & DMASK; return SCPE_OK; } /* Set model */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) { UNIT *chuptr = mt_dev[CHRONO_CH].units + CHRONO_UNIT; extern DEVICE clk_dev; cpu_model = val; if (val & I_CT) { uptr->capac = MAXMEMSIZE; detach_unit (uptr); chuptr->flags &= ~UNIT_ATTABLE; clk_dev.flags &= ~DEV_DIS; } else { uptr->capac = STDMEMSIZE; chuptr->flags |= UNIT_ATTABLE; } if (!(cpu_model & I_94)) mode_multi = 1; return SCPE_OK; } /* Show CTSS */ t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) { if (cpu_model & I_CT) fputs ("CTSS", st); else if (cpu_model & I_94) fputs ("7094", st); else fputs ("7090", st); return SCPE_OK; } /* Insert history entry */ static uint32 inst_io_tab[32] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 0000 - 0377 */ 0, 0, 0, 0x000000FF, 0, 0, 0, 0x45540000, /* 0400 - 0777 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 1000 - 1400 */ 0, 0, 0, 0x000000FF, 0, 0, 0, 0 /* 1400 - 1777 */ }; void cpu_ent_hist (uint32 pc, uint32 ea, t_uint64 ir, t_uint64 opnd) { int32 prv_p; if (pc & HIST_PC) { if ((pc == hst[hst_p].pc) && (ir == hst[hst_p].ir)) { /* repeat last? */ hst[hst_p].rpt++; return; } prv_p = hst_p? hst_p - 1: hst_lnt - 1; if ((pc == hst[prv_p].pc) && (ir == hst[prv_p].ir)) { /* 2 line loop? */ hst[prv_p].rpt++; return; } if (hst_ch & HIST_CH_I) { /* IO only? */ uint32 op = GET_OPC (ir); /* get opcode */ if ((ir & INST_T_DEC) || !(inst_io_tab[op / 32] & (1u << (op & 037)))) return; } } hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = pc; hst[hst_p].ir = ir; hst[hst_p].ac = AC; hst[hst_p].mq = MQ; hst[hst_p].si = SI; hst[hst_p].ea = ea; hst[hst_p].opnd = opnd; hst[hst_p].rpt = 0; return; } /* 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 = hst_ch = 0; hst = NULL; } if (lnt) { hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); if (hst == NULL) return SCPE_MEM; hst_lnt = lnt; if (sim_switches & SWMASK ('I')) hst_ch = HIST_CH_I|HIST_CH_C; else if (sim_switches & SWMASK ('C')) hst_ch = HIST_CH_C; else hst_ch = 0; } return SCPE_OK; } /* Print one instruction */ t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd) { int32 ch; t_value sim_eval; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); sim_eval = ir; if (pc & HIST_PC) { /* instruction? */ fputs ("CPU ", st); fprintf (st, "%05o ", pc & AMASK); if (rpt == 0) fprintf (st, " "); else if (rpt < 1000000) fprintf (st, "%6d ", rpt); else fprintf (st, "%5dM ", rpt / 1000000); fprint_val (st, ac, 8, 38, PV_RZRO); fputc (' ', st); fprint_val (st, mq, 8, 36, PV_RZRO); fputc (' ', st); fprint_val (st, si, 8, 36, PV_RZRO); fputc (' ', st); if (ir & INST_T_DEC) fprintf (st, " "); else fprintf (st, "%05o ", ea); if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M')) > 0) { fputs ("(undefined) ", st); fprint_val (st, ir, 8, 36, PV_RZRO); } else if (!(ir & INST_T_DEC) && (op_flags[GET_OPC (ir)] & I_R)) { fputs (" [", st); fprint_val (st, opnd, 8, 36, PV_RZRO); fputc (']', st); } fputc ('\n', st); /* end line */ } /* end if instruction */ else if (ch = HIST_CH (pc)) { /* channel? */ fprintf (st, "CH%c ", 'A' + ch - 1); fprintf (st, "%05o ", pc & AMASK); fputs (" ", st); fprintf (st, "%05o ", ea & AMASK); if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, (ch_dev[ch - 1].flags & DEV_7909)? SWMASK ('N'): SWMASK ('I')) > 0) { fputs ("(undefined) ", st); fprint_val (st, ir, 8, 36, PV_RZRO); } fputc ('\n', st); /* end line */ } /* end else channel */ return SCPE_OK; } /* Show history */ t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 k, di, lnt; char *cptr = (char *) desc; t_stat r; InstHistory *h; if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 repeat AC MQ SI EA IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(++di) % hst_lnt]; /* entry pointer */ cpu_fprint_one_inst (st, h->pc, h->rpt, h->ea, h->ir, h->ac, h->mq, h->si, h->opnd); } /* end for */ return SCPE_OK; } simh-3.8.1/I7094/i7094_dsk.c0000644000175000017500000014433411112035160013135 0ustar vlmvlm/* i7094_dsk.c: 7631 file control (disk/drum) simulator Copyright (c) 2005-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dsk 7631 file control The 7631 is a controller for multiple serial bit stream devices such as disks or drums. It supports the 1301 fixed disk 1302 fixed disk 2302 fixed disk 7320 drum The 7631 supports variable record formatting, user-specified record numbering, and other complex features. Each track has home address 1: the track number, 4 BCD digits (implicit) home address 2: user-specified track identifier, 6 BCD chars records 1..n: variably formatted records, each consisting of record address: user-specified record identifier, 4 BCD digits and 2 BCD characters record data: 36b words To deal with this, the simulator provides a container of 500 (7320/1301) or 1000 (1302/2302) words per track. Each track starts with home address 2 and then contains a variable number of records. Each record has a two-word header followed by data: word 0: record length without header word 1: record address word 2: start of data word 2+n-1: end of data word 2+n+2: start of next record A record length of 0 indicates end of valid data on the track. Orders to the 7631 are 10 BCD digits (60b), consisting of two words: word 0: op-op-access-module-d1-d2 word 1: d3-d4-d5-d6-x-x Depending on the opcode, d1:d6 can be a track number plus home address 2, or a record number. Status from the 7631 is also 10 BCD digits (60b), with 36b in the first word, and 24b (plus 12b of zeroes) in the second. Because modules can have two access arms that seek independently, each module m is represented by two units: unit m for access 0 and unit m+10 for access 1. This requires tricky bookkeeping to be sure that the service routine is using the 'right' unit. Limitations of the simulation: - HA2 and record address must be exactly 6 characters (one word) - Record lengths must be exact multiples of 6 characters - Seek timing is fixed rather than based on seek length */ /* Definitions */ #include "i7094_defs.h" #include #define DSK_NUMDR 10 /* modules/controller */ #define DSK_SNS (2 * DSK_NUMDR) /* dummy unit for sense */ /* Drive geometry */ #define DSK_WDSPT_7320 500 /* words/track */ #define DSK_WDSPT_1301 500 #define DSK_WDSPT_1302 1000 #define DSK_WDSPT_2302 1000 #define DSK_TRKPC_7320 400 /* tracks/cylinder */ #define DSK_TRKPC_1301 40 #define DSK_TRKPC_1302 40 #define DSK_TRKPC_2302 40 #define DSK_CYLPA_7320 1 /* cylinders/access */ #define DSK_CYLPA_1301 250 #define DSK_CYLPA_1302 250 #define DSK_CYLPA_2302 250 #define DSK_TRKPA_7320 (DSK_TRKPC_7320*DSK_CYLPA_7320) /* tracks/access */ #define DSK_TRKPA_1301 (DSK_TRKPC_1301*DSK_CYLPA_1301) #define DSK_TRKPA_1302 (DSK_TRKPC_1302*DSK_CYLPA_1302) #define DSK_TRKPA_2302 (DSK_TRKPC_2302*DSK_CYLPA_2302) #define DSK_ACCPM_7320 1 /* access/module */ #define DSK_ACCPM_1301 1 #define DSK_ACCPM_1302 2 #define DSK_ACCPM_2302 2 #define DSK_FMCPT_7320 2868 /* format chars/track */ #define DSK_FMCPT_1301 2868 #define DSK_FMCPT_1302 5942 #define DSK_FMCPT_2302 5942 #define SIZE_7320 (DSK_WDSPT_7320*DSK_TRKPA_7320*DSK_ACCPM_7320) #define SIZE_1301 (DSK_WDSPT_1301*DSK_TRKPA_1301*DSK_ACCPM_1301) #define SIZE_1302 (DSK_WDSPT_1302*DSK_TRKPA_1302*DSK_ACCPM_1302) #define SIZE_2302 (DSK_WDSPT_2302*DSK_TRKPA_2302*DSK_ACCPM_2302) #define DSK_BUFSIZ (DSK_WDSPT_2302) #define DSK_DA(a,t,d) (((((a) * dsk_tab[d].trkpa) + (t)) * dsk_tab[d].wdspt) *\ sizeof (t_uint64)) /* Unit flags */ #define UNIT_V_INOP0 (UNIT_V_UF + 0) /* acc 0 inoperative */ #define UNIT_V_INOP1 (UNIT_V_UF + 1) /* acc 1 inoperative */ #define UNIT_V_FMTE (UNIT_V_UF + 2) /* format enabled */ #define UNIT_V_TYPE (UNIT_V_UF + 3) /* drive type */ #define UNIT_M_TYPE 03 #define UNIT_INOP0 (1 << UNIT_V_INOP0) #define UNIT_INOP1 (1 << UNIT_V_INOP1) #define UNIT_FMTE (1 << UNIT_V_FMTE) #define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE) #define TYPE_7320 (0 << UNIT_V_TYPE) #define TYPE_1301 (1 << UNIT_V_TYPE) #define TYPE_1302 (2 << UNIT_V_TYPE) #define TYPE_2302 (3 << UNIT_V_TYPE) #define GET_DTYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE) #define TRK u3 /* track */ #define SKF u4 /* seek in progress */ /* Track/record structure */ #define THA2 0 /* home address 2 */ #define HA2_MASK 0777700000000 /* two chars checked */ #define T1STREC 1 /* start of records */ #define RLNT 0 /* record length */ #define RADDR 1 /* record address */ #define RDATA 2 /* start of data */ #define REC_MASK 0171717177777 /* 4 digits, 2 chars */ /* Command word (60b) - 10 BCD digits */ #define OP1 0 /* opcode */ #define OP2 1 #define ACC 2 /* access */ #define MOD 3 /* module */ #define T1 4 /* track */ #define T2 5 #define T3 6 #define T4 7 /* Disk states */ #define DSK_IDLE 0 /* Status word (60b) */ #define DSKS_PCHK 004000000000000000000 /* prog check */ #define DSKS_DCHK 002000000000000000000 /* data check */ #define DSKS_EXCC 001000000000000000000 /* exc cond */ #define DSKS_INVS 000200000000000000000 /* invalid seq */ #define DSKS_INVC 000040000000000000000 /* invalid opcode */ #define DSKS_FMTC 000020000000000000000 /* format check */ #define DSKS_NRCF 000010000000000000000 /* no record found */ #define DSKS_INVA 000002000000000000000 /* invalid address */ #define DSKS_RSPC 000000400000000000000 /* response check */ #define DSKS_CMPC 000000200000000000000 /* compare check */ #define DSKS_PARC 000000100000000000000 /* parity check */ #define DSKS_ACCI 000000020000000000000 /* access inoperative */ #define DSKS_ACCN 000000004000000000000 /* access not ready */ #define DSKS_DSKE 000000002000000000000 /* disk error */ #define DSKS_FILE 000000001000000000000 /* file error */ #define DSKS_6B 000000000040000000000 /* six bit mode */ #define DSKS_ATN0 000000000002000000000 /* attention start */ #define DSKS_PALL 000777000000000000000 #define DSKS_DALL 000000740000000000000 #define DSKS_EALL 000000037000000000000 #define DSKS_ALLERR 007777777000000000000 /* Commands - opcode 0 */ #define DSKC_NOP 0x00 #define DSKC_RLS 0x04 #define DSKC_8B 0x08 #define DSKC_6B 0x09 /* Commands - opcode 8 */ #define DSKC_SEEK 0x0 /* seek */ #define DSKC_SREC 0x2 /* single record */ #define DSKC_WFMT 0x3 /* write format */ #define DSKC_TNOA 0x4 /* track no addr */ #define DSKC_CYL 0x5 /* cyl no addr */ #define DSKC_WCHK 0x6 /* write check */ #define DSKC_ACCI 0x7 /* set acc inoperative */ #define DSKC_TWIA 0x8 /* track with addr */ #define DSKC_THA 0x9 /* track home addr */ /* CTSS record structure */ #define CTSS_HA2 0676767676767 /* =HXXXXXX */ #define CTSS_RLNT 435 /* data record */ #define CTSS_D1LNT 31 /* padding */ #define CTSS_D2LNT 14 #define CTSS_D3LNT 16 #define CTSS_DLLNT 1 #define CTSS_RA1 2 #define CTSS_RA2 8 /* Data and declarations */ typedef struct { char *name; uint32 accpm; /* acc/module: 1 or 2 */ uint32 wdspt; /* wds/track: 500 or 1000 */ uint32 trkpc; /* trks/cyl: 1 or 40 */ uint32 trkpa; /* trks/acc: 400 or 10000 */ uint32 fchpt; /* format ch/track */ uint32 size; } DISK_TYPE; const DISK_TYPE dsk_tab[4] = { { "7320", DSK_ACCPM_7320, DSK_WDSPT_7320, DSK_TRKPC_7320, DSK_TRKPA_7320, DSK_FMCPT_7320, SIZE_7320 }, { "1301", DSK_ACCPM_1301, DSK_WDSPT_1301, DSK_TRKPC_1301, DSK_TRKPA_1301, DSK_FMCPT_1301, SIZE_1301 }, { "1302", DSK_ACCPM_1302, DSK_WDSPT_1302, DSK_TRKPC_1302, DSK_TRKPA_1302, DSK_FMCPT_1302, SIZE_1302 }, { "2302", DSK_ACCPM_2302, DSK_WDSPT_2302, DSK_TRKPC_2302, DSK_TRKPA_2302, DSK_FMCPT_2302, SIZE_2302 } }; /* 7320/1301 format track characters */ uint8 fmt_thdr_7320[] = { 4, 4, 4, /* gap 1 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ }; uint8 fmt_rhdr_7320[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ 1, 1, 1, 1, 0 /* record ovhd */ }; /* 1302/2302 format track characters */ uint8 fmt_thdr_1302[] = { 4, 4, 4, 4, 4, 4, /* gap 1 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ }; uint8 fmt_rhdr_1302[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ 1, 1, 1, 1, 1, 1, 1, 0 /* record ovhd */ }; /* CTSS 7320/1301 track format table */ uint32 ctss_fmt_7320[] = { CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 }; /* CTSS 1302/2302 track format table */ uint32 ctss_fmt_1302[] = { CTSS_RLNT, CTSS_D1LNT, CTSS_D2LNT, CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 }; uint32 dsk_ch = CH_C; /* disk channel */ uint32 dsk_acc = 0; /* access */ uint32 dsk_mod = 0; /* module */ uint32 dsk_sta = 0; /* disk state */ uint32 dsk_mode = 0; /* I/O mode */ uint32 dsk_wchk = 0; /* write check flag */ uint32 dsk_ctime = 10; /* command time */ uint32 dsk_stime = 1000; /* seek time */ uint32 dsk_rtime = 100; /* rotational latency */ uint32 dsk_wtime = 2; /* word time */ uint32 dsk_gtime = 5; /* gap time */ uint32 dsk_rbase = 0; /* record tracking */ uint32 dsk_rptr = 0; uint32 dsk_rlim = 0; uint32 dsk_stop = 0; uint32 dsk_fmt_cntr = 0; /* format counter */ t_uint64 dsk_rec = 0; /* rec/home addr (36b) */ t_uint64 dsk_sns = 0; /* sense data (60b) */ t_uint64 dsk_cmd = 0; /* BCD command (60b) */ t_uint64 dsk_chob = 0; /* chan output buffer */ uint32 dsk_chob_v = 0; /* valid */ t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */ extern uint32 ch_req; t_stat dsk_svc (UNIT *uptr); t_stat dsk_svc_sns (UNIT *uptr); t_stat dsk_reset (DEVICE *dptr); t_stat dsk_attach (UNIT *uptr, char *cptr); t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit); t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags); t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd); t_stat dsk_uend (uint32 ch, t_uint64 stat); t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res); t_uint64 dsk_acc_atn (uint32 u); t_stat dsk_init_trk (UNIT *udptr, uint32 trk); t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp); t_stat dsk_wr_trk (UNIT *uptr, uint32 trk); t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc); t_bool dsk_qdone (uint32 ch); t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc); /* DSK data structures dsk_dev DSK device descriptor dsk_unit DSK unit descriptor dsk_reg DSK register list */ DIB dsk_dib = { &dsk_chsel, &dsk_chwr }; UNIT dsk_unit[] = { { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ TYPE_7320, SIZE_7320) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_DIS+TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_DIS+TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_DIS+TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_DIS+TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_DIS+TYPE_2302, SIZE_2302) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc, UNIT_DIS, 0) }, { UDATA (&dsk_svc_sns, UNIT_DIS, 0) } }; REG dsk_reg[] = { { ORDATA (STATE, dsk_sta, 6) }, { ORDATA (ACCESS, dsk_acc, 1) }, { ORDATA (MODULE, dsk_mod, 4) }, { ORDATA (RECORD, dsk_rec, 36) }, { ORDATA (MODE, dsk_mode, 4) }, { ORDATA (SENSE, dsk_sns, 60) }, { ORDATA (BCDCMD, dsk_cmd, 60) }, { ORDATA (CHOB, dsk_chob, 36) }, { FLDATA (CHOBV, dsk_chob_v, 0) }, { FLDATA (STOP, dsk_stop, 0) }, { DRDATA (FCNTR, dsk_fmt_cntr, 13) }, { BRDATA (BUF, dsk_buf, 8, 36, DSK_BUFSIZ) }, { DRDATA (RBASE, dsk_rbase, 10), REG_RO }, { DRDATA (RPTR, dsk_rptr, 10), REG_RO }, { DRDATA (RLIM, dsk_rlim, 10), REG_RO }, { DRDATA (CHAN, dsk_ch, 3), REG_HRO }, { DRDATA (STIME, dsk_stime, 24), REG_NZ + PV_LEFT }, { DRDATA (RTIME, dsk_rtime, 24), REG_NZ + PV_LEFT }, { DRDATA (WTIME, dsk_wtime, 24), REG_NZ + PV_LEFT }, { DRDATA (GTIME, dsk_gtime, 24), REG_NZ + PV_LEFT }, { DRDATA (CTIME, dsk_ctime, 24), REG_NZ + PV_LEFT }, { URDATA (TRACK, dsk_unit[0].TRK, 10, 14, 0, 2 * DSK_NUMDR, PV_LEFT) }, { URDATA (SEEKF, dsk_unit[0].SKF, 10, 1, 0, 2 * DSK_NUMDR, PV_LEFT | REG_HRO) }, { URDATA (CAPAC, dsk_unit[0].capac, 10, T_ADDR_W, 0, DSK_NUMDR, PV_LEFT | REG_HRO) }, { NULL } }; MTAB dsk_mtab[] = { { UNIT_INOP0 + UNIT_INOP1, 0, "operational", "OPERATIONAL" }, { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP0, "access 0 inoperative", NULL }, { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP1, "access 1 inoperative", NULL }, { UNIT_FMTE, UNIT_FMTE, "formating enabled", "FORMAT" }, { UNIT_FMTE, 0, "formating disabled", "NOFORMAT" }, { UNIT_TYPE, TYPE_7320, "7320", "7320", &dsk_set_size }, { UNIT_TYPE, TYPE_1301, "1301", "1301", &dsk_set_size }, { UNIT_TYPE, TYPE_1302, "1302", "1302", &dsk_set_size }, { UNIT_TYPE, TYPE_2302, "2302", "2302", &dsk_set_size }, { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &ch_show_chan, NULL }, { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "FORMAT", NULL, NULL, &dsk_show_format, NULL }, { 0 } }; DEVICE dsk_dev = { "DSK", dsk_unit, dsk_reg, dsk_mtab, (DSK_NUMDR * 2) + 1, 10, 24, 1, 8, 36, NULL, NULL, &dsk_reset, NULL, &dsk_attach, NULL, &dsk_dib, DEV_DIS }; /* Disk channel select, from 7909 channel program */ t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit) { uint32 u; dsk_ch = ch; if (dsk_sta != DSK_IDLE) /* not idle? seq check */ dsk_uend (ch, DSKS_INVS); switch (sel) { case CHSL_CTL: /* control */ ch_req |= REQ_CH (ch); /* request channel */ break; case CHSL_SNS: /* sense */ if (sim_is_active (&dsk_unit[DSK_SNS])) /* already sensing? */ return dsk_uend (ch, DSKS_INVS); /* sequence error */ sim_activate (&dsk_unit[DSK_SNS], dsk_ctime); /* set timer */ dsk_stop = 0; break; case CHSL_RDS: /* read */ if (dsk_mode == DSKC_WFMT) /* write format? */ return dsk_uend (ch, DSKS_INVS); /* sequence error */ case CHSL_WRS: /* write */ if (dsk_mode == 0) /* no mode? seq check */ dsk_uend (ch, DSKS_INVS); if (dsk_mode == DSKC_WFMT) /* format? fake sel */ sel = CHSL_FMT; u = (dsk_acc * DSK_NUMDR) + dsk_mod; /* access unit number */ if (sim_is_active (&dsk_unit[u])) /* access in use? */ return dsk_uend (ch, DSKS_ACCN); /* access not ready */ sim_activate (&dsk_unit[u], dsk_rtime); /* rotational time */ break; default: /* other */ return STOP_ILLIOP; } dsk_sta = sel; /* set new state */ return SCPE_OK; } /* Disk channel write, from 7909 channel program */ t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf) { if (stopf) /* stop? */ dsk_stop = 1; else { val = val & DMASK; switch (dsk_sta) { /* case on state */ case CHSL_CTL: /* control */ dsk_cmd = val << 24; if (val & 0100000000000) { /* need 2nd word? */ ch_req |= REQ_CH (ch); /* req ch for 2nd */ dsk_sta = CHSL_CTL|CHSL_2ND; /* next state */ return SCPE_OK; } return dsk_new_cmd (ch, dsk_cmd); /* no, do cmd */ case CHSL_CTL|CHSL_2ND: /* 2nd control */ dsk_cmd |= (val >> 12); return dsk_new_cmd (ch, dsk_cmd); /* do cmd */ default: dsk_chob = val; /* store data */ dsk_chob_v = 1; /* set valid */ } } return SCPE_OK; } /* New command - end of CTL sequence */ t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd) { uint32 i, d, a, m, u, trk, dtyp, bcd[8]; ch_req |= REQ_CH (ch); /* req ch for end */ ch9_set_end (ch, 0); /* set end flag */ dsk_sta = DSK_IDLE; /* ctrl is idle */ for (i = 0; i < 8; i++) { /* get chars from cmd */ d = (uint32) (cmd >> (6 * (9 - i))) & BCD_MASK; if (d == BCD_ZERO) d = 0; else if (d == 0) /* BCD zero cvt */ d = BCD_ZERO; bcd[i] = d; } if (bcd[OP1] == 0) { /* cmd = 0x? */ switch (bcd[OP2]) { /* case on x */ case DSKC_NOP: /* nop */ case DSKC_RLS: /* release */ break; case DSKC_8B: /* 8b mode */ dsk_sns &= ~DSKS_6B; break; case DSKC_6B: /* 6b mode */ dsk_sns |= DSKS_6B; break; default: /* unknown */ return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ } /* end case op2 */ return SCPE_OK; } /* end if */ else if (bcd[OP1] == 8) { /* cmd = 8x? */ a = bcd[ACC]; /* get access, */ m = bcd[MOD]; /* module */ u = (a * DSK_NUMDR) + m; /* unit for access */ if ((m > DSK_NUMDR) || /* invalid module? */ (dsk_unit[m].flags & UNIT_DIS)) /* disabled module? */ return dsk_uend (ch, DSKS_ACCI); dtyp = GET_DTYPE (dsk_unit[m].flags); /* get drive type */ if ((a >= dsk_tab[dtyp].accpm) || /* invalid access? */ (dsk_unit[m].flags & (UNIT_INOP0 << a))) /* access inop? */ return dsk_uend (ch, DSKS_ACCI); if ((bcd[T1] > 9) || (bcd[T2] > 9) || (bcd[T3] > 9) || (bcd[T4] > 9)) trk = dsk_tab[dtyp].trkpa + 1; /* bad track */ else trk = (((((bcd[T1] * 10) + bcd[T2]) * 10) + bcd[T3]) * 10) + bcd[T4]; if (bcd[OP2] == DSKC_WCHK) { /* write check */ if (dsk_mode == 0) /* no prior operation? */ return dsk_uend (ch, DSKS_INVS); bcd[OP2] = dsk_mode; /* use prior mode */ dsk_wchk = 1; /* set write check */ } else dsk_wchk = 0; dsk_sns &= ~(DSKS_ALLERR | dsk_acc_atn (u)); /* clear err, atn */ dsk_stop = 0; /* clear stop */ switch (bcd[OP2]) { case DSKC_SEEK: /* seek */ if ((trk >= dsk_tab[dtyp].trkpa) && /* inv track? */ ((dtyp == TYPE_7320) || /* drum or not CE? */ (bcd[T1] > 9) || (bcd[T2] != BCD_AT) || (bcd[T3] > 9) || (bcd[T4] > 9))) return dsk_uend (ch, DSKS_INVA); if (sim_is_active (&dsk_unit[u])) /* selected acc busy? */ return dsk_uend (ch, DSKS_ACCN); dsk_unit[u].SKF = 1; /* set seeking flag */ dsk_unit[u].TRK = trk; /* sel acc on cyl */ sim_activate (&dsk_unit[u], dsk_stime); /* seek */ dsk_mode = 0; /* clear I/O mode */ return SCPE_OK; case DSKC_ACCI: /* access inoperative */ dsk_unit[m].flags |= (UNIT_INOP0 << a); /* set correct flag */ dsk_mode = 0; /* clear I/O mode */ return SCPE_OK; case DSKC_SREC: /* single record */ break; /* no verification */ case DSKC_WFMT: /* format */ if (!(dsk_unit[m].flags & UNIT_FMTE)) /* format enabled? */ return dsk_uend (ch, DSKS_FMTC); /* no, error */ case DSKC_TNOA: /* track no addr */ case DSKC_CYL: /* cyl no addr */ case DSKC_TWIA: /* track with addr */ case DSKC_THA: /* track home addr */ if (trk != (uint32) dsk_unit[u].TRK) /* on track? */ return dsk_uend (ch, DSKS_NRCF); break; default: return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ } dsk_acc = a; /* save access */ dsk_mod = m; /* save module */ dsk_rec = cmd & DMASK; /* save rec/home addr */ dsk_mode = bcd[OP2]; /* save mode */ return SCPE_OK; } return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ } /* Sense unit service */ t_stat dsk_svc_sns (UNIT *uptr) { t_uint64 dat; switch (dsk_sta) { /* case on state */ case CHSL_SNS: /* prepare data */ dsk_buf[0] = (dsk_sns >> 24) & DMASK; /* buffer is 2 words */ dsk_buf[1] = (dsk_sns << 12) & DMASK; dsk_rptr = 0; dsk_rlim = 2; dsk_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ break; case CHSL_SNS|CHSL_2ND: /* second state */ if (dsk_rptr >= dsk_rlim) { /* end of buffer? */ ch9_set_end (dsk_ch, 0); /* set end */ ch_req |= REQ_CH (dsk_ch); /* request channel */ dsk_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ sim_activate (uptr, dsk_ctime); /* longer wait */ return SCPE_OK; } dat = dsk_buf[dsk_rptr++]; /* get word */ if (!dsk_stop) /* send wd to chan */ ch9_req_rd (dsk_ch, dat); break; case CHSL_SNS|CHSL_3RD: /* 3rd state */ if (dsk_qdone (dsk_ch)) /* done? exit */ return SCPE_OK; dsk_sta = CHSL_SNS; /* repeat sequence */ break; } sim_activate (uptr, dsk_wtime); /* sched next */ return SCPE_OK; } /* Seek, read, write unit service */ t_stat dsk_svc (UNIT *uaptr) { uint32 i, dtyp, trk; uint8 fc, *format; t_uint64 rdat; UNIT *udptr; t_stat r; if (uaptr->SKF) { /* seeking? */ uint32 u = uaptr - dsk_dev.units; /* get unit */ uaptr->SKF = 0; /* seek done */ dsk_sns |= dsk_acc_atn (u); /* set atn bit */ ch9_set_atn (dsk_ch); /* set atn flag */ return SCPE_OK; } udptr = dsk_dev.units + dsk_mod; /* data unit */ if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */ return dsk_uend (dsk_ch, DSKS_ACCI); /* error */ if ((udptr->flags & UNIT_ATT) == 0) { /* not attached? */ dsk_uend (dsk_ch, DSKS_ACCI); /* error */ return SCPE_UNATT; } dtyp = GET_DTYPE (udptr->flags); /* get data drive type */ trk = uaptr->TRK; /* get access track */ switch (dsk_sta) { /* case on state */ case CHSL_RDS: /* read start */ if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ } dsk_sta = CHSL_RDS|CHSL_2ND; /* next state */ break; case CHSL_RDS|CHSL_2ND: /* read data transmit */ if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ if (r != ERR_ENDRC) /* error? */ return r; dsk_sta = CHSL_RDS|CHSL_3RD; /* next state */ sim_activate (uaptr, dsk_gtime); /* gap time */ return SCPE_OK; } rdat = dsk_buf[dsk_rptr++]; /* get word */ if (dsk_rptr == T1STREC) /* if THA, skip after HA */ dsk_rptr++; if (!dsk_stop) /* give to channel */ ch9_req_rd (dsk_ch, rdat); break; case CHSL_RDS|CHSL_3RD: /* read end rec/trk */ if (dsk_qdone (dsk_ch)) /* done? exit */ return SCPE_OK; dsk_sta = CHSL_RDS; /* repeat sequence */ break; case CHSL_WRS: /* write start */ if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ } ch_req |= REQ_CH (dsk_ch); /* first request */ dsk_sta = CHSL_WRS|CHSL_2ND; /* next state */ dsk_chob = 0; /* clr, inval buffer */ dsk_chob_v = 0; break; case CHSL_WRS|CHSL_2ND: /* write data transmit */ if (dsk_chob_v) /* valid? clear */ dsk_chob_v = 0; else if (!dsk_stop) /* no, no stop? io chk */ ch9_set_ioc (dsk_ch); if (dsk_wchk) { /* write check? */ if (dsk_buf[dsk_rptr++] != dsk_chob) /* data mismatch? */ return dsk_uend (dsk_ch, DSKS_CMPC); /* error */ } else dsk_buf[dsk_rptr++] = dsk_chob; /* write, store word */ if (dsk_rptr == T1STREC) /* if THA, skip after HA */ dsk_rptr++; if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ if (r != ERR_ENDRC) /* error? */ return r; dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */ sim_activate (uaptr, dsk_gtime); /* gap time */ return SCPE_OK; } if (!dsk_stop) /* more to do */ ch_req |= REQ_CH (dsk_ch); break; case CHSL_WRS|CHSL_3RD: /* write done */ if (!dsk_wchk) { /* if write */ if (r = dsk_wr_trk (udptr, trk)) /* write track; err? */ return r; } if (dsk_qdone (dsk_ch)) /* done? exit */ return SCPE_OK; dsk_sta = CHSL_WRS; /* repeat sequence */ break; /* Formatting takes place in five stages 1. Clear the track buffer, request the first word from the channel 2. Match characters against the fixed overhead (HA1, HA2, and gaps) 3. Match characters against the per-record overhead (RA and gaps) 4. Count the characters defining the record length 5. See if the next character is end or gap; if gap, return to stage 3 This formating routine is not exact. It checks whether the format will fit in the container, not whether the format would fit on a real 7320, 1301, 1302, or 2302. */ case CHSL_FMT: /* initialization */ for (i = 0; i < DSK_BUFSIZ; i++) /* clear track buf */ dsk_buf[i] = 0; dsk_rbase = T1STREC; /* init record ptr */ dsk_rptr = 0; /* init format ptr */ dsk_fmt_cntr = 0; /* init counter */ ch_req |= REQ_CH (dsk_ch); /* request channel */ dsk_sta = CHSL_FMT|CHSL_2ND; /* next state */ dsk_chob = 0; /* clr, inval buffer */ dsk_chob_v = 0; break; case CHSL_FMT|CHSL_2ND: /* match track header */ if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) format = fmt_thdr_7320; else format = fmt_thdr_1302; if (!dsk_get_fmtc (dtyp, &fc)) /* get fmt char; err? */ return SCPE_OK; if (fc != format[dsk_rptr++]) /* mismatch? */ return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ if (format[dsk_rptr] == 0) { /* end format? */ dsk_sta = CHSL_FMT|CHSL_3RD; /* next state */ dsk_rptr = 0; /* reset format ptr */ } break; case CHSL_FMT|CHSL_3RD: /* match record header */ if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) format = fmt_rhdr_7320; else format = fmt_rhdr_1302; if (!dsk_get_fmtc (dtyp, &fc)) /* get fmt char; err? */ return SCPE_OK; if (fc != format[dsk_rptr++]) /* mismatch? */ return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ if (format[dsk_rptr] == 0) { /* end format? */ dsk_sta = CHSL_FMT|CHSL_4TH; /* next state */ dsk_rlim = 0; /* reset record ctr */ } break; case CHSL_FMT|CHSL_4TH: /* count record size */ if (!dsk_get_fmtc (dtyp, &fc)) /* get fmt char; err? */ return SCPE_OK; if (fc == BCD_ONE) /* more record? */ dsk_rlim++; else { uint32 rsiz = dsk_rlim / 6; /* rec size words */ if ((fc != BCD_TWO) || /* improper end? */ (rsiz == 0) || /* smaller than min? */ ((dsk_rlim % 6) != 0) || /* not multiple of 6? */ ((dsk_rbase + rsiz + RDATA) >= dsk_tab[dtyp].wdspt)) return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ dsk_buf[dsk_rbase + RLNT] = rsiz; /* record rec lnt */ dsk_rbase = dsk_rbase + rsiz + RDATA; /* new rec start */ dsk_sta = CHSL_FMT|CHSL_5TH; /* next state */ } break; case CHSL_FMT|CHSL_5TH: /* record or track end */ if (!dsk_get_fmtc (dtyp, &fc)) /* get fmt char; err? */ return SCPE_OK; if (fc == BCD_TWO) { /* back to record header? */ dsk_rptr = 2; /* already done 2 chars */ dsk_sta = CHSL_FMT|CHSL_3RD; /* record header state */ } else if (fc != BCD_ONE) /* format check */ dsk_uend (dsk_ch, DSKS_FMTC); else { if (!dsk_wchk) { /* actual write? */ trk = trk - (trk % dsk_tab[dtyp].trkpc); /* cyl start */ for (i = 0; i < dsk_tab[dtyp].trkpc; i++) { /* do all tracks */ if (r = dsk_wr_trk (udptr, trk + i)) /* wr track; err? */ return r; } } ch9_set_end (dsk_ch, 0); /* set end */ ch_req |= REQ_CH (dsk_ch); /* request channel */ dsk_sta = DSK_IDLE; /* disk is idle */ return SCPE_OK; /* done */ } break; default: return SCPE_IERR; } sim_activate (uaptr, dsk_wtime); return SCPE_OK; } /* Initialize data transfer Inputs: udptr = pointer to data unit trk = track to read Outputs: dsk_buf contains track specified by trk dsk_rbase, dsk_rptr, dsk_rlim are initialized Errors: SCPE_IOERR = I/O error (fatal, uend) ERR_NRCF = no record found (HA2 or record number mismatch, uend) STOP_INVFMT = invalid format (fatal, uend) */ t_stat dsk_init_trk (UNIT *udptr, uint32 trk) { uint32 k, da, dtyp, rlnt; dtyp = GET_DTYPE (udptr->flags); /* get drive type */ da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */ sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */ k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); if (ferror (udptr->fileref)) { /* error? */ perror ("DSK I/O error"); clearerr (udptr->fileref); dsk_uend (dsk_ch, DSKS_DSKE); return SCPE_IOERR; } for ( ; k < dsk_tab[dtyp].wdspt; k++) /* zero fill */ dsk_buf[k] = 0; dsk_rbase = T1STREC; /* record base */ rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ if ((rlnt == 0) || (dsk_rlim >= dsk_tab[dtyp].wdspt)) { /* invalid record? */ dsk_uend (dsk_ch, DSKS_FMTC); return STOP_INVFMT; } if (dsk_mode != DSKC_SREC) { /* not single record? */ if (dsk_mode == DSKC_THA) /* trk home addr? */ dsk_rptr = 0; else { if (((dsk_rec << 24) ^ dsk_buf[THA2]) & HA2_MASK) { dsk_uend (dsk_ch, DSKS_NRCF); /* invalid HA2 */ return ERR_NRCF; } if (dsk_mode == DSKC_TWIA) /* track with addr? */ dsk_rptr = dsk_rbase + RADDR; /* start at addr */ else dsk_rptr = dsk_rbase + RDATA; /* else, at data */ } return SCPE_OK; } while (rlnt != 0) { /* until end track */ dsk_rptr = dsk_rbase + RDATA; if (((dsk_rec ^ dsk_buf[dsk_rbase + RADDR]) & REC_MASK) == 0) return SCPE_OK; /* rec found? done */ dsk_rbase = dsk_rlim; /* next record */ rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ dsk_rlim = dsk_rbase + rlnt + RDATA; /* limit */ if (dsk_rlim >= dsk_tab[dtyp].wdspt) { /* invalid format? */ dsk_uend (dsk_ch, DSKS_FMTC); return STOP_INVFMT; } } dsk_uend (dsk_ch, DSKS_NRCF); /* not found */ return ERR_NRCF; } /* Check end of transfer Inputs: uptr = pointer to access unit dtyp = drive type Outputs: ERR_ENDRC = end of record/track/cylinder, end sent, ch req if required SCPE_OK = more to do, dsk_rbase, dsk_rptr, dsk_rlim may be updated STOP_INVFMT = invalid format (fatal, uend sent) */ t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp) { uint32 rlnt; if (dsk_rptr < dsk_rlim) /* record done? */ return SCPE_OK; if (dsk_stop || !ch9_qconn (dsk_ch) || /* stop or err disc or */ (dsk_mode == DSKC_SREC)) { /* single record? */ ch9_set_end (dsk_ch, 0); /* set end */ ch_req |= REQ_CH (dsk_ch); /* request channel */ return ERR_ENDRC; } dsk_rbase = dsk_rlim; /* next record */ rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ if ((dsk_rbase >= dsk_tab[dtyp].wdspt) || /* invalid format? */ (dsk_rlim >= dsk_tab[dtyp].wdspt)) { dsk_uend (dsk_ch, DSKS_FMTC); return STOP_INVFMT; } if (rlnt) { /* more on track? */ if ((dsk_mode == DSKC_THA) || (dsk_mode == DSKC_TWIA)) dsk_rptr = dsk_rbase + RADDR; /* start with addr */ else dsk_rptr = dsk_rbase + RDATA; /* or data */ return SCPE_OK; } if (dsk_mode == DSKC_CYL) { /* cylinder mode? */ uaptr->TRK = (uaptr->TRK + 1) % dsk_tab[dtyp].trkpa; /* incr track */ if (uaptr->TRK % dsk_tab[dtyp].trkpc) /* not cyl end? */ return ERR_ENDRC; /* don't set end */ } ch9_set_end (dsk_ch, 0); /* set end */ ch_req |= REQ_CH (dsk_ch); /* request channel */ return ERR_ENDRC; } /* Write track back to file */ t_stat dsk_wr_trk (UNIT *udptr, uint32 trk) { uint32 dtyp = GET_DTYPE (udptr->flags); uint32 da = DSK_DA (dsk_acc, trk, dtyp); sim_fseek (udptr->fileref, da, SEEK_SET); sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); if (ferror (udptr->fileref)) { perror ("DSK I/O error"); clearerr (udptr->fileref); dsk_uend (dsk_ch, DSKS_DSKE); return SCPE_IOERR; } return SCPE_OK; } /* Synthesize right attention bit from (access * 10 + module) */ t_uint64 dsk_acc_atn (uint32 u) { uint32 g, b; g = u / 4; /* bit group */ b = u % 4; /* bit within group */ return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0))); } /* Get next format character */ t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc) { uint32 cc = dsk_fmt_cntr % 6; if (cc == 0) { /* start of word? */ if (dsk_chob_v) /* valid? clear */ dsk_chob_v = 0; else if (!dsk_stop) /* no, no stop? io chk */ ch9_set_ioc (dsk_ch); } *fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */ if ((cc == 5) && !dsk_stop) /* end of word? */ ch_req |= REQ_CH (dsk_ch); if (dsk_fmt_cntr++ >= dsk_tab[dtyp].fchpt) { /* track overflow? */ dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ return FALSE; } return TRUE; } /* Unusual end (set status and stop) */ t_stat dsk_uend (uint32 ch, t_uint64 stat) { dsk_sns |= stat; dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC); if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK; if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK; if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC; ch9_set_end (ch, CHINT_UEND); ch_req |= REQ_CH (ch); dsk_sta = DSK_IDLE; return SCPE_OK; } /* Test for done */ t_bool dsk_qdone (uint32 ch) { if (dsk_stop || !ch9_qconn (ch)) { /* stop or err disc? */ dsk_sta = DSK_IDLE; /* disk is idle */ return TRUE; } return FALSE; } /* Reset */ t_stat dsk_reset (DEVICE *dptr) { uint32 i; UNIT *uptr; dsk_acc = 0; dsk_mod = 0; dsk_rec = 0; dsk_mode = 0; dsk_wchk = 0; dsk_sns = 0; dsk_cmd = 0; dsk_sta = DSK_IDLE; dsk_rbase = 0; dsk_rptr = 0; dsk_rlim = 0; dsk_stop = 0; dsk_fmt_cntr = 0; dsk_chob = 0; dsk_chob_v = 0; for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0; for (i = 0; i <= (2 * DSK_NUMDR); i++) { uptr = dsk_dev.units + i; sim_cancel (uptr); uptr->TRK = 0; uptr->SKF = 0; } return SCPE_OK; } /* Attach routine, test formating */ t_stat dsk_attach (UNIT *uptr, char *cptr) { uint32 dtyp = GET_DTYPE (uptr->flags); t_stat r; uptr->capac = dsk_tab[dtyp].size; r = attach_unit (uptr, cptr); if (r != SCPE_OK) return r; uptr->TRK = 0; uptr->SKF = 0; uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1); return dsk_show_format (stdout, uptr, 0, NULL); } /* Set disk size */ t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 dtyp = GET_DTYPE (val); uint32 u = uptr - dsk_dev.units; UNIT *u1; if (u & 1) return SCPE_ARG; u1 = dsk_dev.units + u + 1; if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT)) return SCPE_ALATT; if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS; else { u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE; u1->capac = dsk_tab[dtyp].size; } uptr->capac = dsk_tab[dtyp].size; return SCPE_OK; } /* Show format */ t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 a, t, k, u, tlim, dtyp, da; uint32 rptr, rlnt, rlim, rec, ctptr, *format; uint32 minrsz = DSK_BUFSIZ; uint32 maxrsz = 0; uint32 minrno = DSK_BUFSIZ; uint32 maxrno = 0; t_bool ctss; t_uint64 dbuf[DSK_BUFSIZ]; DEVICE *dptr; if (uptr == NULL) return SCPE_IERR; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; dptr = find_dev_from_unit (uptr); u = uptr - dptr->units; if (dptr == NULL) return SCPE_IERR; dtyp = GET_DTYPE (uptr->flags); if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) format = ctss_fmt_7320; else format = ctss_fmt_1302; for (a = 0, ctss = TRUE; a < dsk_tab[dtyp].accpm; a++) { if (val) tlim = dsk_tab[dtyp].trkpa; else tlim = 1; for (t = 0; t < tlim; t++) { da = DSK_DA (a, t, dtyp); /* get disk address */ sim_fseek (uptr->fileref, da, SEEK_SET); /* read track */ k = sim_fread (dbuf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, uptr->fileref); if (ferror (uptr->fileref)) /* error? */ return SCPE_IOERR; for ( ; k < dsk_tab[dtyp].wdspt; k++) dbuf[k] = 0; rptr = T1STREC; rlnt = (uint32) dbuf[rptr + RLNT]; if (dbuf[THA2] != CTSS_HA2) ctss = FALSE; if (rlnt == 0) { if (a || t) fprintf (st, "Unformatted track, unit = %d, access = %d, track = %d\n", u, a, t); else fprintf (st, "Unit %d is unformatted\n", u); return SCPE_OK; } for (rec = 0, ctptr = 0; rlnt != 0; rec++) { if ((format[ctptr] == 0) || format[ctptr++] != rlnt) ctss = FALSE; rlim = rptr + rlnt + RDATA; if (rlim >= dsk_tab[dtyp].wdspt) { fprintf (st, "Invalid record length %d, unit = %d, access = %d, track = %d, record = %d\n", rlnt, u, a, t, rec); return SCPE_OK; } if (rlnt > maxrsz) maxrsz = rlnt; if (rlnt < minrsz) minrsz = rlnt; rptr = rlim; rlnt = (uint32) dbuf[rptr + RLNT]; } if (format[ctptr] != 0) ctss = FALSE; if (rec > maxrno) maxrno = rec; if (rec < minrno) minrno = rec; } } if (val == 0) return SCPE_OK; if (ctss) fprintf (st, "CTSS format\n"); else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, "Valid fixed format, records/track = %d, record size = %d\n", minrno, minrsz); else if (minrsz == maxrsz) fprintf (st, "Valid variable format, records/track = %d-%d, record size = %d\n", minrno, maxrno, minrsz); else if (minrno == maxrno) fprintf (st, "Valid variable format, records/track = %d, record sizes = %d-%d\n", minrno, minrsz, maxrsz); else fprintf (st, "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n", minrno, maxrno, minrsz, maxrsz); return SCPE_OK; } simh-3.8.1/I7094/i7094_clk.c0000644000175000017500000000737711112033436013136 0ustar vlmvlm/* i7094_clk.c: IBM 7094 clock Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. clk RPQ F89349 interval timer Chronolog calendar clock */ #include "i7094_defs.h" #include uint32 chtr_clk = 0; extern t_uint64 *M; t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); uint8 bcd_2d (uint32 n, uint8 *b2); /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit clk_reg CLK register list */ UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; REG clk_reg[] = { { FLDATA (TRAP, chtr_clk, 0) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, NULL, DEV_DISABLE+DEV_DIS }; /* Clock unit service */ t_stat clk_svc (UNIT *uptr) { t_uint64 ctr; if ((clk_dev.flags & DEV_DIS) == 0) { /* clock enabled? */ ctr = ReadP (CLK_CTR); ctr = (ctr + 1) & DMASK; /* increment */ WriteP (CLK_CTR, ctr); if ((ctr & MMASK) == 0) /* overflow? req trap */ chtr_clk = 1; sim_activate (uptr, sim_rtcn_calb (CLK_TPS, TMR_CLK)); /* reactivate unit */ } return SCPE_OK; } /* Chronolog clock */ uint32 chrono_rd (uint8 *buf, uint32 bufsiz) { time_t curtim; t_uint64 ctr; struct tm *tptr; if (bufsiz < 12) return 0; curtim = time (NULL); /* get time */ tptr = localtime (&curtim); /* decompose */ if (tptr == NULL) /* error? */ return 0; buf[0] = bcd_2d (tptr->tm_mon + 1, buf + 1); buf[2] = bcd_2d (tptr->tm_mday, buf + 3); buf[4] = bcd_2d (tptr->tm_hour, buf + 5); buf[6] = bcd_2d (tptr->tm_min, buf + 7); buf[8] = bcd_2d (tptr->tm_sec, buf + 9); ctr = ReadP (CLK_CTR); buf[10] = bcd_2d ((uint32) (ctr % 60), buf + 11); return 12; } /* Convert number (0-99) to BCD */ uint8 bcd_2d (uint32 n, uint8 *b2) { uint8 d1, d2; d1 = n / 10; d2 = n % 10; if (d1 == 0) d1 = BCD_ZERO; if (d2 == 0) d2 = BCD_ZERO; if (b2 != NULL) *b2 = d2; return d1; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { chtr_clk = 0; if (clk_dev.flags & DEV_DIS) sim_cancel (&clk_unit); else sim_activate (&clk_unit, sim_rtcn_init (clk_unit.wait, TMR_CLK)); return SCPE_OK; } simh-3.8.1/I7094/i7094_bug_history.txt0000644000175000017500000000675511017062170015320 0ustar vlmvlmBugs Found and Fixed During Simulator Debug 1. CPU: MPY tested sign of AC instead of sign of MQ. 2. CPU: VLM, VDP, VDH need alternate opcode decode points for large counts. 3. CPU: STL not in decode table. 4. CPU: Partial stores lacked initial read in decode table. 5. CPU: PXD, PCD needed (t_uint64) cast. 6. CPU: SXD, SCD needed (t_uint64) cast. 7. CPU: SBM at wrong case offset. 8. CPU: All transfers used IR<21:35> instead of calculated effective address. 9. CPU: HPR, TRA need alternate negative opcodes. 10. CPU: STT missing final write to memory. 11. IO: Channel output process model incorrect, extensive revision required. 12. IO: Channel connect test should not test channel state, only connection. 13. IO: Channel opcode with nostore doesn't increment channel address. 14. CDR: Card reader missing activate at end of state 2. 15. SYS: Zero operand instructions printed with trailing space. 16. CPU: Convert class count bit field start definition incorrect. 17. CPU: CAQ cut and paste error from CVR. 18. CPU: Shift count is 8b wide, not 9b. 19. SYS: Mnemonic is LDQ not LMQ. 20. SYS: RQL opcode incorrect in symbolic decode table. 21. CPU: Multi-tag mode stores OR'd value of tags on any index read except normal effective address. 22. CPU: Floating point trap does not write location 0 if trap suppressed. 23. SYS: TRA instructions should have symbolic class MXN not MXR. 24. CPU: Floating add instructions test for zero result only if normalization enabled. 25. CPU: Floating add with unlike signs and equal magnitudes, result sign is sign of SR rather than sign of AC. 26. CPU: Floating multiply does not test spill prior to normalization step. 27. CPU: Channel activity proceeds under HALT. 28. MT: Any read error should stop the channel and the tape controller. 29. MT: EOR write flag cleared before testing. 39. IO: EOR write flag set after data sent to device. 40. IO: EOR write flag incorrect set on IOCP, IOCT, IOSP, IOST. 41. MT: End of file errors not masked on backspace operations. 42. CPU: LCHx, RCHx miscoded in decode table. 43. IO: Only 7607 error completions (IOxT without new command) set trap. 44. CPU: 7067 channel trap flags misdefined with extraneous decrement shift. 45. CPU: pcq array misdeclared as uint32 instead of uint16. 46. IO: SDC and SCD tested chan_flags instead of chan_dev.flags. 47. SYS: 7907 opcodes defined incorrectly. 48. SYS: TCH not decoding bit<19>. 49. IO: CTL not clearing EOR after device end. 50. DSK: Format code not incrementing track in writing all tracks in cylinder. 51. DSK: THA mode overwrites record structure of first record. 52. DSK: Write doesn't set channel request for initial word. 53. DSK: Saved record number was command digits 3..8 instead of 5..10. 54. CLK: Compute 60th's from location 5, so Chrono clock is in sync with interval clock. 55. CPU: Enable Chronolog clock if CTSS. 56. CPU: Read/write protection error not setting protection trap. 57. CPU: SCHx not setting protection trap in user mode. 58: CPU: Protection trap overwriting wrong location. 59. CPU: Stop message reporting physical, not virtual, PC. 60. IO: Test for "request another cycle" in channel processor was inverted. 61. IO: Valid bit handling incorrect across multiple transfers. 62. IO: BSR doesn't set EOF indicator. 63. IO: 7607 channel modeled incorrectly, could stall. 64. IO: All 7607 "effective NOP" conditions must be tested when a new command is decoded (wc == 0 for IOCx and IOSx, EOR set for IOSx and IORx). simh-3.8.1/I7094/i7094_cpu1.c0000644000175000017500000010714711111661606013236 0ustar vlmvlm/* i7094_cpu1.c: IBM 7094 CPU complex instructions Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #include "i7094_defs.h" #define FP_HIFRAC(x) ((uint32) ((x) >> FP_N_FR) & FP_FMASK) #define FP_LOFRAC(x) ((uint32) (x) & FP_FMASK) #define FP_PACK38(s,e,f) (((s)? AC_S: 0) | ((t_uint64) (f)) | \ (((t_uint64) ((e) & FP_M_ACCH)) << FP_V_CH)) #define FP_PACK36(s,e,f) (((s)? SIGN: 0) | ((t_uint64) (f)) | \ (((t_uint64) ((e) & FP_M_CH)) << FP_V_CH)) extern t_uint64 AC, MQ, SI, KEYS; extern uint32 PC; extern uint32 SLT, SSW; extern uint32 cpu_model, stop_illop; extern uint32 ind_ovf, ind_dvc, ind_ioc, ind_mqo; extern uint32 mode_ttrap, mode_strap, mode_ctrap, mode_ftrap; extern uint32 mode_storn, mode_multi; extern uint32 chtr_pend, chtr_inht, chtr_inhi; extern uint32 ch_flags[NUM_CHAN]; typedef struct { /* unpacked fp */ uint32 s; /* sign: 0 +, 1 - */ int32 ch; /* exponent */ t_uint64 fr; /* fraction (54b) */ } UFP; uint32 op_frnd (void); t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem); void fp_norm (UFP *op); void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op); uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch); extern t_bool fp_trap (uint32 spill); extern t_bool sel_trap (uint32 va); extern t_stat ch_op_reset (uint32 ch, t_bool ch7909); /* Integer add Sherman: "As the result of an addition or subtraction, if the C(AC) is zero, the sign of AC is unchanged." */ void op_add (t_uint64 op) { t_uint64 mac = AC & AC_MMASK; /* get magnitudes */ t_uint64 mop = op & MMASK; AC = AC & AC_S; /* isolate AC sign */ if ((AC? 1: 0) ^ ((op & SIGN)? 1: 0)) { /* signs diff? sub */ if (mac >= mop) /* AC >= MQ */ AC = AC | (mac - mop); else AC = (AC ^ AC_S) | (mop - mac); /* <, sign change */ } else { AC = AC | ((mac + mop) & AC_MMASK); /* signs same, add */ if ((AC ^ mac) & AC_P) /* P change? overflow */ ind_ovf = 1; } return; } /* Multiply */ void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc) { uint32 sign; if (sc == 0) /* sc = 0? nop */ return; sign = ((MQ & SIGN)? 1: 0) ^ ((sr & SIGN)? 1: 0); /* result sign */ ac = ac & AC_MMASK; /* clear AC sign */ sr = sr & MMASK; /* mpy magnitude */ MQ = MQ & MMASK; /* MQ magnitude */ if (sr && MQ) { /* mpy != 0? */ while (sc--) { /* for sc */ if (MQ & 1) /* MQ35? AC += mpy */ ac = (ac + sr) & AC_MMASK; MQ = (MQ >> 1) | ((ac & 1) << 34); /* AC'MQ >> 1 */ ac = ac >> 1; } } else ac = MQ = 0; /* result = 0 */ if (sign) { /* negative? */ ac = ac | AC_S; /* insert signs */ MQ = MQ | SIGN; } AC = ac; /* update AC */ return; } /* Divide */ t_bool op_div (t_uint64 sr, uint32 sc) { uint32 signa, signm; if (sc == 0) /* sc = 0? nop */ return FALSE; signa = (AC & AC_S)? 1: 0; /* get signs */ signm = (sr & SIGN)? 1: 0; sr = sr & MMASK; /* get dvr magn */ if ((AC & AC_MMASK) >= sr) /* |AC| >= |sr|? */ return TRUE; AC = AC & AC_MMASK; /* AC, MQ magn */ MQ = MQ & MMASK; while (sc--) { /* for sc */ AC = ((AC << 1) & AC_MMASK) | (MQ >> 34); /* AC'MQ << 1 */ MQ = (MQ << 1) & MMASK; if (AC >= sr) { /* AC >= dvr? */ AC = AC - sr; /* AC -= dvr */ MQ = MQ | 1; /* set quo bit */ } } if (signa ^ signm) /* quo neg? */ MQ = MQ | SIGN; if (signa) /* rem neg? */ AC = AC | AC_S; return FALSE; /* div ok */ } /* Shifts */ void op_als (uint32 addr) { uint32 sc = addr & SCMASK; if ((sc >= 35)? /* shift >= 35? */ ((AC & MMASK) != 0): /* test all bits for ovf */ (((AC & MMASK) >> (35 - sc)) != 0)) /* test only 35-sc bits */ ind_ovf = 1; if (sc >= 37) /* sc >= 37? result 0 */ AC = AC & AC_S; else AC = (AC & AC_S) | ((AC << sc) & AC_MMASK); /* shift, save sign */ return; } void op_ars (uint32 addr) { uint32 sc = addr & SCMASK; if (sc >= 37) /* sc >= 37? result 0 */ AC = AC & AC_S; else AC = (AC & AC_S) | ((AC & AC_MMASK) >> sc); /* shift, save sign */ return; } void op_lls (uint32 addr) { uint32 sc; /* get sc */ AC = AC & AC_MMASK; /* clear AC sign */ for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ AC = ((AC << 1) & AC_MMASK) | ((MQ >> 34) & 1); /* AC'MQ << 1 */ MQ = (MQ & SIGN) | ((MQ << 1) & MMASK); /* preserve MQ sign */ if (AC & AC_P) /* if P, overflow */ ind_ovf = 1; } if (MQ & SIGN) /* set ACS from MQS */ AC = AC | AC_S; return; } void op_lrs (uint32 addr) { uint32 sc = addr & SCMASK; t_uint64 mac; MQ = MQ & MMASK; /* get MQ magnitude */ if (sc != 0) { mac = AC & AC_MMASK; /* get AC magnitude, */ AC = AC & AC_S; /* sign */ if (sc < 35) { /* sc [1,34]? */ MQ = ((MQ >> sc) | (mac << (35 - sc))) & MMASK; /* MQ has AC'MQ */ AC = AC | (mac >> sc); /* AC has AC only */ } else if (sc < 37) { /* sc [35:36]? */ MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ AC = AC | (mac >> sc); /* AC has */ } else if (sc < 72) /* sc [37:71]? */ MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ else MQ = 0; /* >72? MQ = 0 */ } if (AC & AC_S) /* set MQS from ACS */ MQ = MQ | SIGN; return; } void op_lgl (uint32 addr) { uint32 sc; /* get sc */ for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ AC = (AC & AC_S) | ((AC << 1) & AC_MMASK) | /* AC'MQ << 1 */ ((MQ >> 35) & 1); /* preserve AC sign */ MQ = (MQ << 1) & DMASK; if (AC & AC_P) /* if P, overflow */ ind_ovf = 1; } return; } void op_lgr (uint32 addr) { uint32 sc = addr & SCMASK; t_uint64 mac; if (sc != 0) { mac = AC & AC_MMASK; /* get AC magnitude, */ AC = AC & AC_S; /* sign */ if (sc < 36) { /* sc [1,35]? */ MQ = ((MQ >> sc) | (mac << (36 - sc))) & DMASK; /* MQ has AC'MQ */ AC = AC | (mac >> sc); /* AC has AC only */ } else if (sc == 36) { /* sc [36]? */ MQ = mac & DMASK; /* MQ = AC */ AC = AC | (mac >> 36); /* AC = AC */ } else if (sc < 73) /* sc [37, 72]? */ MQ = (mac >> (sc - 36)) & DMASK; /* MQ has AC only */ else MQ = 0; /* >72, AC,MQ = 0 */ } return; } /* Plus sense - undefined operations are NOPs */ t_stat op_pse (uint32 addr) { uint32 ch, spill; switch (addr) { case 00000: /* CLM */ if (cpu_model & I_9X) /* 709X only */ AC = AC & AC_S; break; case 00001: /* LBT */ if ((AC & 1) != 0) PC = (PC + 1) & AMASK; break; case 00002: /* CHS */ AC = AC ^ AC_S; break; case 00003: /* SSP */ AC = AC & ~AC_S; break; case 00004: /* ENK */ MQ = KEYS; break; case 00005: /* IOT */ if (ind_ioc) ind_ioc = 0; else PC = (PC + 1) & AMASK; break; case 00006: /* COM */ AC = AC ^ AC_MMASK; break; case 00007: /* ETM */ if (cpu_model & I_9X) /* 709X only */ mode_ttrap = 1; break; case 00010: /* RND */ if ((cpu_model & I_9X) && (MQ & B1)) /* 709X only, MQ1 set? */ op_add ((t_uint64) 1); /* incr AC */ break; case 00011: /* FRN */ if (cpu_model & I_9X) { /* 709X only */ spill = op_frnd (); if (spill) fp_trap (spill); } break; case 00012: /* DCT */ if (ind_dvc) ind_dvc = 0; else PC = (PC + 1) & AMASK; break; case 00014: /* RCT */ chtr_inhi = 1; /* 1 cycle delay */ chtr_inht = 0; /* clr inhibit trap */ chtr_pend = 0; /* no trap now */ break; case 00016: /* LMTM */ if (cpu_model & I_94) /* 709X only */ mode_multi = 0; break; case 00140: /* SLF */ if (cpu_model & I_9X) /* 709X only */ SLT = 0; break; case 00141: case 00142: case 00143: case 00144: /* SLN */ if (cpu_model & I_9X) /* 709X only */ SLT = SLT | (1u << (00144 - addr)); break; case 00161: case 00162: case 00163: /* SWT */ case 00164: case 00165: case 00166: if ((SSW & (1u << (00166 - addr))) != 0) PC = (PC + 1) & AMASK; break; case 01000: case 02000: case 03000: case 04000: /* BTT */ case 05000: case 06000: case 07000: case 10000: if (cpu_model & I_9X) { /* 709X only */ if (sel_trap (PC)) /* sel trap? */ break; ch = GET_U_CH (addr); /* get channel */ if (ch_flags[ch] & CHF_BOT) /* BOT? */ ch_flags[ch] &= ~CHF_BOT; /* clear */ else PC = (PC + 1) & AMASK; /* else skip */ } break; case 001350: case 002350: case 003350: case 004350: /* RICx */ case 005350: case 006350: case 007350: case 010350: ch = GET_U_CH (addr); /* get channel */ return ch_op_reset (ch, 1); case 001352: case 002352: case 003352: case 004352: /* RDCx */ case 005352: case 006352: case 007352: case 010352: ch = GET_U_CH (addr); /* get channel */ return ch_op_reset (ch, 0); } /* end case */ return SCPE_OK; } /* Minus sense */ t_stat op_mse (uint32 addr) { uint32 t, ch; switch (addr) { case 00000: /* CLM */ if (cpu_model & I_9X) /* 709X only */ AC = AC & AC_S; break; case 00001: /* PBT */ if ((AC & AC_P) != 0) PC = (PC + 1) & AMASK; break; case 00002: /* EFTM */ if (cpu_model & I_9X) { /* 709X only */ mode_ftrap = 1; ind_mqo = 0; /* clears MQ ovf */ } break; case 00003: /* SSM */ if (cpu_model & I_9X) /* 709X only */ AC = AC | AC_S; break; case 00004: /* LFTM */ if (cpu_model & I_9X) /* 709X only */ mode_ftrap = 0; break; case 00005: /* ESTM */ if (cpu_model & I_9X) /* 709X only */ mode_strap = 1; break; case 00006: /* ECTM */ if (cpu_model & I_9X) /* 709X only */ mode_ctrap = 1; break; case 00007: /* LTM */ if (cpu_model & I_9X) /* 709X only */ mode_ttrap = 0; break; case 00010: /* LSNM */ if (cpu_model & I_9X) /* 709X only */ mode_storn = 0; break; case 00012: /* RTT (704) */ if (cpu_model & I_9X) /* 709X only */ sel_trap (PC); break; case 00016: /* EMTM */ mode_multi = 1; break; case 00140: /* SLF */ if (cpu_model & I_9X) /* 709X only */ SLT = 0; break; case 00141: case 00142: case 00143: case 00144: /* SLT */ if (cpu_model & I_9X) { /* 709X only */ t = SLT & (1u << (00144 - addr)); SLT = SLT & ~t; if (t != 0) PC = (PC + 1) & AMASK; } break; case 00161: case 00162: case 00163: /* SWT */ case 00164: case 00165: case 00166: if ((cpu_model & I_9X) && /* 709X only */ ((SSW & (1u << (00166 - addr))) != 0)) PC = (PC + 1) & AMASK; break; case 001000: case 002000: case 003000: case 004000: /* ETT */ case 005000: case 006000: case 007000: case 010000: if (sel_trap (PC)) /* sel trap? */ break; ch = GET_U_CH (addr); /* get channel */ if (ch_flags[ch] & CHF_EOT) /* EOT? */ ch_flags[ch] = ch_flags[ch] & ~CHF_EOT; /* clear */ else PC = (PC + 1) & AMASK; /* else skip */ break; } return SCPE_OK; } /* Floating add Notes: - AC enter into the initial exponent comparison. If either is set, the numbers are always swapped. AC

gets OR'd into AC during the swap, and AC are cleared afterwards - The early end test is actually > 077 if AC <= SR and > 100 if AC > SR. However, any shift >= 54 will produce a zero fraction, so the difference can be ignored */ uint32 op_fad (t_uint64 sr, t_bool norm) { UFP op1, op2, t; int32 mqch, diff; MQ = 0; /* clear MQ */ fp_unpack (AC, 0, 1, &op1); /* unpack AC */ fp_unpack (sr, 0, 0, &op2); /* unpack sr */ if (op1.ch > op2.ch) { /* AC exp > SR exp? */ if (AC & AC_P) /* AC P or's with S */ op1.s = 1; t = op1; /* swap operands */ op1 = op2; op2 = t; op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ } diff = op2.ch - op1.ch; /* exp diff */ if (diff) { /* any shift? */ if ((diff < 0) || (diff > 077)) /* diff > 63? */ op1.fr = 0; else op1.fr = op1.fr >> diff; /* no, denormalize */ } if (op1.s ^ op2.s) { /* subtract? */ if (op1.fr >= op2.fr) { /* op1 > op2? */ op2.fr = op1.fr - op2.fr; /* op1 - op2 */ op2.s = op1.s; /* op2 sign is result */ } else op2.fr = op2.fr - op1.fr; /* else op2 - op1 */ } else { op2.fr = op2.fr + op1.fr; /* op2 + op1 */ if (op2.fr & FP_FCRY) { /* carry? */ op2.fr = op2.fr >> 1; /* renormalize */ op2.ch++; /* incr exp */ } } if (norm) { /* normalize? */ if (op2.fr) { /* non-zero frac? */ fp_norm (&op2); mqch = op2.ch - FP_N_FR; } else op2.ch = mqch = 0; /* else true zero */ } else mqch = op2.ch - FP_N_FR; return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ } /* Floating multiply */ uint32 op_fmp (t_uint64 sr, t_bool norm) { UFP op1, op2; int32 mqch; uint32 f1h, f2h; fp_unpack (MQ, 0, 0, &op1); /* unpack MQ */ fp_unpack (sr, 0, 0, &op2); /* unpack sr */ op1.s = op1.s ^ op2.s; /* result sign */ if ((op2.ch == 0) && (op2.fr == 0)) { /* sr a normal 0? */ AC = op1.s? AC_S: 0; /* result is 0 */ MQ = op1.s? SIGN: 0; return 0; } f1h = FP_HIFRAC (op1.fr); /* get hi fracs */ f2h = FP_HIFRAC (op2.fr); op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* f1h * f2h */ op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ if (norm) { /* normalize? */ if (!(op1.fr & FP_FNORM)) { /* not normalized? */ op1.fr = op1.fr << 1; /* shift frac left 1 */ op1.ch--; /* decr exp */ } if (FP_HIFRAC (op1.fr)) /* hi result non-zero? */ mqch = op1.ch - FP_N_FR; /* set MQ exp */ else op1.ch = mqch = 0; /* clear AC, MQ exp */ } else mqch = op1.ch - FP_N_FR; /* set MQ exp */ return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ } /* Floating divide */ uint32 op_fdv (t_uint64 sr) { UFP op1, op2; int32 mqch; uint32 spill, quos; t_uint64 rem; fp_unpack (AC, 0, 1, &op1); /* unpack AC */ fp_unpack (sr, 0, 0, &op2); /* unpack sr */ quos = op1.s ^ op2.s; /* quotient sign */ if (op1.fr >= (2 * op2.fr)) { /* |AC| >= 2*|sr|? */ MQ = quos? SIGN: 0; /* MQ = sign only */ return TRAP_F_DVC; /* divide check */ } if (op1.fr == 0) { /* |AC| == 0? */ MQ = quos? SIGN: 0; /* MQ = sign only */ AC = 0; /* AC = +0 */ return 0; /* done */ } op1.ch = op1.ch & FP_M_CH; /* remove AC */ if (op1.fr >= op2.fr) { /* |AC| >= |sr|? */ op1.fr = op1.fr >> 1; /* denorm AC */ op1.ch++; } op1.fr = fp_fracdiv (op1.fr, op2.fr, &rem); /* fraction divide */ op1.fr = op1.fr | (rem << FP_N_FR); /* rem'quo */ mqch = op1.ch - op2.ch + FP_BIAS; /* quotient exp */ op1.ch = op1.ch - FP_N_FR; /* remainder exp */ spill = fp_pack (&op1, quos, mqch); /* pack up */ return (spill? (spill | TRAP_F_SGL): 0); /* if spill, set SGL */ } /* Double floating add Notes: - AC enter into the initial exponent comparison. If either is set, the numbers are always swapped. AC

gets OR'd into AC during the swap, and AC are cleared afterwards - For most cases, SI ends up with the high order part of the larger number - The 'early end' cases (smaller number is shifted away) must be tracked exactly for SI impacts. The early end cases are: (a) AC > SR, diff > 0100, and AC normalized (b) AC <= SR, diff > 077, and SR normalized In case (a), SI is unchanged. In case (b), SI ends up with the SR sign and characteristic but the MQ (!) fraction */ uint32 op_dfad (t_uint64 sr, t_uint64 sr1, t_bool norm) { UFP op1, op2, t; int32 mqch, diff; fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ if (op1.ch > op2.ch) { /* AC exp > SR exp? */ if (((op1.ch - op2.ch) > 0100) && (AC & B9)) ; /* early out */ else SI = FP_PACK36 (op1.s, op1.ch, FP_HIFRAC (op1.fr)); if (AC & AC_P) /* AC P or's with S */ op1.s = 1; t = op1; /* swap operands */ op1 = op2; op2 = t; op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ } else { /* AC <= SR */ if (((op2.ch - op1.ch) > 077) && (sr & B9)) /* early out */ SI = FP_PACK36 (op2.s, op2.ch, FP_LOFRAC (MQ)); else SI = FP_PACK36 (op2.s, op2.ch, FP_HIFRAC (op2.fr)); } diff = op2.ch - op1.ch; /* exp diff */ if (diff) { /* any shift? */ if ((diff < 0) || (diff > 077)) /* diff > 63? */ op1.fr = 0; else op1.fr = op1.fr >> diff; /* no, denormalize */ } if (op1.s ^ op2.s) { /* subtract? */ if (op1.fr >= op2.fr) { /* op1 > op2? */ op2.fr = op1.fr - op2.fr; /* op1 - op2 */ op2.s = op1.s; /* op2 sign is result */ } else op2.fr = op2.fr - op1.fr; /* op2 - op1 */ } else { op2.fr = op2.fr + op1.fr; /* op2 + op1 */ if (op2.fr & FP_FCRY) { /* carry? */ op2.fr = op2.fr >> 1; /* renormalize */ op2.ch++; /* incr exp */ } } if (norm) { /* normalize? */ if (op2.fr) { /* non-zero frac? */ fp_norm (&op2); mqch = op2.ch - FP_N_FR; } else op2.ch = mqch = 0; /* else true zero */ } else mqch = op2.ch - FP_N_FR; return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ } /* Double floating multiply Notes (notation is A+B' * C+D', where ' denotes 2^-27): - The instruction returns 0 if A and C are both zero, because B*D is never done as part of the algorithm - For most cases, SI ends up with B*C, with a zero sign and exponent - For the A+B' both zero 'early end' case SI ends up with A or C, depending on whether the operation is normalized or not */ uint32 op_dfmp (t_uint64 sr, t_uint64 sr1, t_bool norm) { UFP op1, op2; int32 mqch; uint32 f1h, f2h, f1l, f2l; t_uint64 tx; fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ op1.s = op1.s ^ op2.s; /* result sign */ f1h = FP_HIFRAC (op1.fr); /* A */ f1l = FP_LOFRAC (op1.fr); /* B */ f2h = FP_HIFRAC (op2.fr); /* C */ f2l = FP_LOFRAC (op2.fr); /* D */ if (((op1.ch == 0) && (op1.fr == 0)) || /* AC'MQ normal 0? */ ((op2.ch == 0) && (op2.fr == 0)) || /* sr'sr1 normal 0? */ ((f1h == 0) && (f2h == 0))) { /* both hi frac zero? */ AC = op1.s? AC_S: 0; /* result is 0 */ MQ = op1.s? SIGN: 0; SI = sr; /* SI has C */ return 0; } op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ if (op1.fr) { /* A'B != 0? */ op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* A * C */ tx = ((t_uint64) f1h) * ((t_uint64) f2l); /* A * D */ op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ tx = ((t_uint64) f1l) * ((t_uint64) f2h); /* B * C */ op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ SI = tx >> FP_N_FR; /* SI keeps B * C */ } else { if (norm) /* early out */ SI = sr; else SI = FP_PACK36 (op2.s, op2.ch, 0); } if (norm) { /* normalize? */ if (!(op1.fr & FP_FNORM)) { /* not normalized? */ op1.fr = op1.fr << 1; /* shift frac left 1 */ op1.ch--; /* decr exp */ } if (FP_HIFRAC (op1.fr)) { /* non-zero? */ mqch = op1.ch - FP_N_FR; /* set MQ exp */ } else op1.ch = mqch = 0; /* clear AC, MQ exp */ } else mqch = op1.ch - FP_N_FR; /* set MQ exp */ return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ } /* Double floating divide Notes: - This is a Taylor series expansion (where ' denotes >> 27): (A+B') * (C+D')^-1 = (A+B') * C^-1 - (A+B') * D'* C^-2 +... to two terms, which can be rewritten as terms Q1, Q2: Q1 = (A+B')/C Q2' = (R - Q1*D)'/C - Tracking the sign of Q2' is complicated: Q1 has the sign of the quotient, s_AC ^ s_SR D has the sign of the divisor, s_SR R has the sign of the dividend, s_AC Q1*D sign is s_AC ^ s_SR ^ s^SR = s^AC Therefore, R and Q1*D have the same sign, s_AC Q2' sign is s^AC ^ s_SR, which is the sign of the quotient - For first divide check, SI is 0 - For other cases, including second divide check, SI ends up with Q1 - R-Q1*D is only calculated to the high 27b; using the full 54b throws off the result - The second divide must check for divd >= divr, otherwise an extra bit of quotient would be devloped, throwing off the result - A late ECO added full post-normalization; single precision divide does no normalization */ uint32 op_dfdv (t_uint64 sr, t_uint64 sr1) { UFP op1, op2; int32 mqch; uint32 csign, ac_s; t_uint64 f1h, f2h, tr, tq1, tq1d, trmq1d, tq2; fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ fp_unpack (sr, 0, 0, &op2); /* unpack sr only */ ac_s = op1.s; /* save AC sign */ op1.s = op1.s ^ op2.s; /* sign of result */ f1h = FP_HIFRAC (op1.fr); f2h = FP_HIFRAC (op2.fr); if (f1h >= (2 * f2h)) { /* |A| >= 2*|C|? */ SI = 0; /* clear SI */ return TRAP_F_DVC; /* divide check */ } if (f1h == 0) { /* |AC| == 0? */ SI = MQ = op1.s? SIGN: 0; /* MQ, SI = sign only */ AC = op1.s? AC_S: 0; /* AC = sign only */ return 0; /* done */ } op1.ch = op1.ch & FP_M_CH; /* remove AC */ if (f1h >= f2h) { /* |A| >= |C|? */ op1.fr = op1.fr >> 1; /* denorm AC */ op1.ch++; } op1.ch = op1.ch - op2.ch + FP_BIAS; /* exp of quotient */ tq1 = fp_fracdiv (op1.fr, op2.fr, &tr); /* |A+B| / |C| */ tr = tr << FP_N_FR; /* R << 27 */ tq1d = (tq1 * ((t_uint64) FP_LOFRAC (sr1))) & /* Q1 * D */ ~((t_uint64) FP_FMASK); /* top 27 bits */ csign = (tr < tq1d); /* correction sign */ if (csign) /* |R|<|Q1*D|? compl */ trmq1d = tq1d - tr; else trmq1d = tr - tq1d; /* no, subtr ok */ SI = FP_PACK36 (op1.s, op1.ch, tq1); /* SI has Q1 */ if (trmq1d >= (2 * op2.fr)) { /* |R-Q1*D| >= 2*|C|? */ AC = FP_PACK38 (csign ^ ac_s, 0, FP_HIFRAC (trmq1d)); /* AC has R-Q1*D */ MQ = (csign ^ ac_s)? SIGN: 0; /* MQ = sign only */ return TRAP_F_DVC; /* divide check */ } tq2 = fp_fracdiv (trmq1d, op2.fr, NULL); /* |R-Q1*D| / |C| */ if (trmq1d >= op2.fr) /* can only gen 27b quo */ tq2 &= ~((t_uint64) 1); op1.fr = tq1 << FP_N_FR; /* shift Q1 into place */ if (csign) /* sub or add Q2 */ op1.fr = op1.fr - tq2; else op1.fr = op1.fr + tq2; fp_norm (&op1); /* normalize */ if (op1.fr) /* non-zero? */ mqch = op1.ch - FP_N_FR; else op1.ch = mqch = 0; /* clear AC, MQ exp */ return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ } /* Floating round */ uint32 op_frnd (void) { UFP op; uint32 spill; spill = 0; /* no error */ if (MQ & B9) { /* MQ9 set? */ fp_unpack (AC, 0, 1, &op); /* unpack AC */ op.fr = op.fr + ((t_uint64) (1 << FP_N_FR)); /* round up */ if (op.fr & FP_FCRY) { /* carry out? */ op.fr = op.fr >> 1; /* renormalize */ op.ch++; /* incr exp */ if (op.ch == (FP_M_CH + 1)) /* ovf with QP = 0? */ spill = TRAP_F_OVF | TRAP_F_AC; } AC = FP_PACK38 (op.s, op.ch, FP_HIFRAC (op.fr)); /* pack AC */ } return spill; } /* Fraction divide - 54/27'0 yielding quotient and remainder */ t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem) { dvr = dvr >> FP_N_FR; if (rem) *rem = dvd % dvr; return (dvd / dvr); } /* Floating point normalize */ void fp_norm (UFP *op) { op->fr = op->fr & FP_DFMASK; /* mask fraction */ if (op->fr == 0) /* zero? */ return; while ((op->fr & FP_FNORM) == 0) { /* until norm */ op->fr = op->fr << 1; /* lsh 1 */ op->ch--; /* decr exp */ } return; } /* Floating point unpack */ void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op) { if (q_ac) { /* AC? */ op->s = (h & AC_S)? 1: 0; /* get sign */ op->ch = (uint32) ((h >> FP_V_CH) & FP_M_ACCH); /* get exp */ } else { op->s = (h & SIGN)? 1: 0; /* no, mem */ op->ch = (uint32) ((h >> FP_V_CH) & FP_M_CH); } op->fr = (((t_uint64) FP_LOFRAC (h)) << FP_N_FR) | /* get frac hi */ ((t_uint64) FP_LOFRAC (l)); /* get frac lo */ return; } /* Floating point pack */ uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch) { uint32 spill; AC = FP_PACK38 (op->s, op->ch, FP_HIFRAC (op->fr)); /* pack AC */ MQ = FP_PACK36 (mqs, mqch, FP_LOFRAC (op->fr)); /* pack MQ */ if (op->ch > FP_M_CH) /* check AC exp */ spill = TRAP_F_OVF | TRAP_F_AC; else if (op->ch < 0) spill = TRAP_F_AC; else spill = 0; if (mqch > FP_M_CH) /* check MQ exp */ spill |= (TRAP_F_OVF | TRAP_F_MQ); else if (mqch < 0) spill |= TRAP_F_MQ; return spill; } simh-3.8.1/I7094/i7094_dat.h0000644000175000017500000001432211111660154013130 0ustar vlmvlm/* i7094_dat.h: IBM 7094 data conversion tables Copyright (c) 2003-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ /* Nine-code to ASCII conversion */ const char nine_to_ascii_a[64] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '^', '#', '@', ':', '>', '{', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '%', '~', '\\', '"', }; const char nine_to_ascii_h[64] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '^', '=', '\'', ':', '>', '{', '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '(', '~', '\\', '"', }; /* ASCII to nine-code conversion */ const char ascii_to_nine[128] = { 060, 060, 060, 060, 060, 060, 060, 060, /* 000 - 037 */ 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 060, 052, 077, 013, 053, 074, 020, 014, /* 040 - 077 */ 074, 034, 054, 020, 073, 040, 033, 061, 000, 001, 002, 003, 004, 005, 006, 007, 010, 011, 015, 056, 036, 013, 016, 032, 014, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */ 030, 031, 041, 042, 043, 044, 045, 046, 047, 050, 051, 062, 063, 064, 065, 066, 067, 070, 071, 035, 076, 055, 012, 057, 060, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */ 030, 031, 041, 042, 043, 044, 045, 046, 047, 050, 051, 062, 063, 064, 065, 066, 067, 070, 071, 017, 072, 037, 075, 060 }; /* ASCII to BCD conversion */ const char ascii_to_bcd[128] = { 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 052, 037, 013, 053, 074, 060, 014, /* 040 - 077 */ 034, 074, 054, 060, 033, 040, 073, 021, 020, 001, 002, 003, 004, 005, 006, 007, 010, 011, 015, 056, 076, 013, 016, 072, 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 075, 036, 055, 012, 057, 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 017, 032, 077, 035, 000 }; /* BCD to ASCII conversion */ const char bcd_to_ascii_a[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '^', '#', '@', ':', '>', '{', '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '%', '~', '\\', '"', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}' }; const char bcd_to_ascii_h[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '^', '=', '\'', ':', '>', '{', '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '|', ',', '(', '~', '\\', '"', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '!', '$', '*', ']', ';', '_', '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '?', '.', ')', '[', '<', '}' }; /* BCD to ASCII 48 character print chains */ const char bcd_to_pca[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '#', '@', ' ', ' ', ' ', '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ',', '%', ' ', ' ', ' ', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; const char bcd_to_pch[64] = { ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '=', '\'', ' ', ' ', ' ', '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ',', '(', ' ', ' ', ' ', '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; /* BCD to column binary conversion */ const uint32 bcd_to_colbin[64] = { 00000, 00400, 00200, 00100, 00040, 00020, 00010, 00004, 00002, 00001, 00202, 00102, 00042, 00022, 00012, 00006, 01000, 01400, 01200, 01100, 01040, 01020, 01010, 01004, 01002, 01001, 01202, 01102, 01042, 01022, 01012, 01006, 02000, 02400, 02200, 02100, 02040, 02020, 02010, 02004, 02002, 02001, 02202, 02102, 02042, 02022, 02012, 02006, 04000, 04400, 04200, 04100, 04040, 04020, 04010, 04004, 04002, 04001, 04202, 04102, 04042, 04022, 04012, 04006 }; simh-3.8.1/sim_rev.h0000644000175000017500000032775111143602004012464 0ustar vlmvlm/* sim_rev.h: simulator revisions and current rev level Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #ifndef _SIM_REV_H_ #define _SIM_REV_H_ 0 #define SIM_MAJOR 3 #define SIM_MINOR 8 #define SIM_PATCH 1 #define SIM_DELTA 0 /* V3.8 revision history patch date module(s) and fix(es) 1 08-Feb-09 scp.c: - revised RESTORE unit logic for consistency - "detach_all" ignores error status returns if shutting down (from Dave Bryan) - DO cmd missing params now default to null string (from Dave Bryan) - DO cmd sub_args now allows "\\" to specify literal backslash (from Dave Bryan) - decommitted MTAB_VAL - fixed implementation of MTAB_NC - fixed warnings in help printouts sim_tape.c: - fixed signed/unsigned warning in sim_tape_set_fmt (from Dave Bryan) sim_tmxr.c, sim_tmxr.h: - added line connection order to tmxr_poll_conn, added tmxr_set_lnorder and tmxr_show_lnorder (from Dave Bryan) - print device and line to which connection was made (from Dave Bryan) - added three new standardized SHOW routines all terminal multiplexers: - revised for new common SHOW routines in TMXR library - rewrote set size routines not to use MTAB_VAL hp2100_cpu.c (from Dave Bryan): - VIS and IOP are now mutually exclusive on 1000-F - Removed A/B shadow register variables - Moved hp_setdev, hp_showdev to hp2100_sys.c - Moved non-existent memory checks to WritePW - Fixed mp_dms_jmp to accept lower bound, check write protection - Corrected DMS violation register set conditions - Refefined ABORT to pass address, moved def to hp2100_cpu.h - Combined dms and dms_io routines - JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort - Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA - Rewrote device I/O to model backplane signals - EDT no longer passes DMA channel - Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE - Breakpoints on interrupt trap cells now work hp2100_cpu0.c (from Dave Bryan): - .FLUN and self-tests for VIS and SIGNAL are NOP if not present - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Added "user microcode" dispatcher for unclaimed instructions hp2100_cpu1.c (from Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Moved option-present tests to UIG dispatchers - Call "user microcode" dispatcher for unclaimed UIG instructions hp2100_cpu2.c (from Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Updated mp_dms_jmp calling sequence - Fixed DJP, SJP, and UJP jump target validation - RVA/B conditionally updates dms_vr before returning value hp2100_cpu3.c (from Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Updated mp_dms_jmp calling sequence hp2100_cpu4.c, hp2100_cpu7.c (from Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) hp2100_cpu5.c (from Dave Bryan): - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Redefined ABORT to pass address, moved def to hp2100_cpu.h - Rewrote device I/O to model backplane signals hp2100_cpu6.c (from Dave Bryan): - Corrected .SIP debug formatting - Moved microcode function prototypes to hp2100_cpu1.h - Removed option-present tests (now in UIG dispatchers) - Rewrote device I/O to model backplane signals hp2100 all peripherals (from Dave Bryan): - Rewrote device I/O to model backplane signals hp2100_baci.c (from Dave Bryan): - Fixed STC,C losing interrupt request on BREAK - Changed Telnet poll to connect immediately after reset or attach - Added REG_FIT to register variables < 32-bit size - Moved fmt_char() function to hp2100_sys.c hp2100_dp.c, hp2100_dq.c (from Dave Bryan): - Added REG_FIT to register variables < 32-bit size hp2100_dr.c (from Dave Bryan): - Revised drc_boot to use ibl_copy hp2100_fp1.c (from Dave Bryan): - Quieted bogus gcc warning in fp_exec hp2100_ipl.c (from Dave Bryan): - Changed socket poll to connect immediately after reset or attach - Revised EDT handler to refine completion delay conditions - Revised ipl_boot to use ibl_copy hp2100_lpt.c (from Dave Bryan): - Changed CTIME register width to match documentation hp2100_mpx.c (from Dave Bryan): - Implemented 12792C eight-channel terminal multiplexer hp2100_ms.c (from Dave Bryan): - Revised to use AR instead of saved_AR in boot hp2100_mt.c (from Dave Bryan): - Fixed missing flag after CLR command - Moved write enable and format commands from MTD to MTC hp2100_mux.c (from Dave Bryan): - SHOW MUX CONN/STAT with SET MUX DIAG is no longer disallowed - Changed Telnet poll to connect immediately after reset or attach - Added LINEORDER support - Added BREAK deferral to allow RTE break-mode to work hp2100_pif.c (from Dave Bryan): - Implemented 12620A/12936A Privileged Interrupt Fences hp2100_sys.c (from Dave Bryan): - Fixed IAK instruction dual-use mnemonic display - Moved hp_setdev, hp_showdev from hp2100_cpu.c - Changed sim_load to use WritePW instead of direct M[] access - Added PIF device - Moved fmt_char() function from hp2100_baci.c - Added MPX device hp2100_cpu.h (from Dave Bryan): - Rearranged declarations with hp2100_cpu.c and hp2100_defs.h - Added mp_control to CPU state externals hp2100_cpu1.h (from Dave Bryan): - Moved microcode function prototypes here hp2100_defs.h (from Dave Bryan): - Added POLL_FIRST to indicate immediate connection attempt - Rearranged declarations with hp2100_cpu.h - Added PIF device - Declared fmt_char() function - Added MPX device i1401_cpu.c: - fixed bug in ZA and ZS (from Bob Abeles) - fixed tape indicator implementation (from Bob Abeles) - added missing magtape modifier A (from Van Snyder) i1401_mt.c: - added -n (no rewind) option to BOOT (from Van Snyder) - fixed bug to mask input to 6b on read (from Bob Abeles) lgp_stddev.c: - changed encode character from # to !, due to overlap pdp11_cpu.c: - fixed failure to clear cpu_bme on RESET (found by Walter Mueller) pdp11_dz.c: - added MTAB_NC modifier on SET LOG command (found by Walter Mueller) pdp11_io.c, vax_io.c, vax780_uba.c: - revised to use PDP-11 I/O library pdp11_io_lib.c: - created common library for Unibus/Qbus support routines pdp11_cis.c, vax_cis.c: - fixed bug in ASHP left overflow calc (Word/NibbleLShift) - fixed bug in DIVx (LntDstr calculation) sds_lp.c: - fixed loss of carriage control position on space op vax_stddev.c, vax780_stddev.c - modified to resync TODR on any clock reset 0 15-Jun-08 scp.c: - fixed bug in local/global register search (found by Mark Pizzolato) - fixed bug in restore of RO units (from Mark Pizzolato) - added SET/SHO/NO BR with default argument (from Dave Bryan) sim_tmxr.c - worked around Telnet negotiation problem with QCTerm (from Dave Bryan) gri_defs.h, gri_cpu.c, gri_sys.c: - added GRI-99 support hp2100_baci.c (from Dave Bryan): - Implemented 12966A Buffered Asynchronous Communications Interface simulator hp2100_cpu.c (from Dave Bryan): - Memory ex/dep and bkpt type default to current map mode - Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA - Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, mp_mevff clear on interrupt with I/O instruction in trap cell - Removed DBI support from 1000-M (was temporary for RTE-6/VM) - Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags - Enabled SIGNAL instructions, SIG debug flag - Fixed single stepping through interrupts hp2100_cpu0.c (from Dave Bryan and Holger Veit): - Removed and implemented "cpu_rte_vma" and "cpu_rte_os" - Removed and implemented "cpu_vis" and "cpu_signal" - Removed and implemented "cpu_rte_ema" hp2100_cpu1.c (from Dave Bryan): - Added fprint_ops, fprint_regs for debug printouts - Enabled DIAG as NOP on 1000 F-Series - Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64 hp2100_cpu3.c (from Dave Bryan): - Fixed unsigned divide bug in .DDI - Fixed unsigned multiply bug in .DMP - Added implementation of DBI self-test hp2100_cpu4.c (from Dave Bryan): - Fixed B register return bug in /CMRT hp2100_cpu5.c (from Holger Veit): - Implemented RTE-6/VM Virtual Memory Area firmware - Implemented RTE-IV Extended Memory Area firmware hp2100_cpu6.c (from Dave Bryan): - Implemented RTE-6/VM OS accelerator firmware hp2100_cpu7.c (from Holger Veit): - Implemented Vector Instruction Set and SIGNAL/1000 firmware hp2100_ds.c (from Dave Bryan): - Corrected and verified ioCRS action - Corrected DPTR register definition from FLDATA to DRDATA hp2100_fp.c (from Mark Pizzolato) - Corrected fp_unpack mantissa high-word return hp2100_fp1.c (from Dave Bryan): - Reworked "complement" to avoid inlining bug in gcc-4.x - Fixed uninitialized return in fp_accum when setting hp2100_mux.c (from Dave Bryan): - Sync mux poll with console poll for idle compatibility hp2100_stddev.c (from Dave Bryan): - Fixed PTR trailing null counter for tape re-read - Added IPTICK register to CLK to display CPU instr/tick - Corrected and verified ioCRS actions - Changed TTY console poll to 10 msec. real time - Synchronized CLK with TTY if set for 10 msec. - Added UNIT_IDLE to TTY and CLK - Removed redundant control char handling definitions - Changed TTY output wait from 100 to 200 for MSU BASIC hp2100_sys.c (from Dave Bryan): - Added BACI device - Added RTE OS/VMA/EMA mnemonics - Changed fprint_sym to handle step with irq pending hp2100_cpu.h (from Dave Bryan): - Added calc_defer() prototype - Added extern sim_deb, cpu_dev, DEB flags for debug printouts - Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl, ReadIO, WriteIO for RTE-6/VM microcode support hp2100_cpu1.h (from Dave Bryan): - Corrected OP_AFF to OP_AAFF for SIGNAL/1000 - Removed unused operand patterns - Added fprint_ops, fprint_regs for debug printouts - Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC hp2100_defs.h (from Dave Bryan): - Added BACI device - Added 16/32-bit unsigned-to-signed conversions - Changed TMR_MUX to TMR_POLL for idle support - Added POLLMODE, sync_poll() declaration - Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks - Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks nova_defs.h (from Bruce Ray): - added support for third-party 64KW memory nova_clk.c (from Bruce Ray): - renamed to RTC, to match DG literature nova_cpu.c (from Bruce Ray): - added support for third-party 64KW memory - added Nova 3 "secret" instructions - added CPU history support nova_dkp.c (from Bruce Ray): - renamed to DKP, to match DG literature - fixed numerous bugs in both documented and undocumented behavior - changed bootstrap code to DG official sequence nova_dsk.c (from Bruce Ray): - renamed to DSK, to match DG literature - added support for undocumented behavior - changed bootstrap code to DG official sequence nova_mta.c (from Bruce Ray): - renamed to MTA, to match DG literature - changed bootstrap code to DG official sequence nova_plt.c, nova_pt.c (from Bruce Ray): - added 7B/8B support nova_sys.c (from Bruce Ray): - fixed mistaken instruction mnemonics pdp11_cpu.c, pdp11_io.c, pdp11_rh.c: - fixed DMA memory address limit test (found by John Dundas) - fixed MMR0 treatment in RESET (found by Walter Mueller) pdp11_cpumod.h, pdp11_cpumod.c: - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (found by Walter Mueller) - added support to set default state of KDJ11B,E clock control register pdp11_dc.c: - added support for DC11 pdp11_defs.h: - added KE, KG, RC, DC support - renamed DL11 devices pdp11_dl.c: - renamed devices to DLI/DLO, to match DC11 - added modem control pdp11_io.c: - added autoconfigure support for DC11 pdp11_ke.c: - added support for KE11A pdp11_kg.c (from John Dundas): - added support for KG11A pdp11_rc.c (from John Dundas): - added support for RC11 pdp11_sys.c: - modified to allow -A, -B use with 8b devices - added KE, KG, RC, DC support - renamed DL11 devices vax_cmode.c, vax_io.c, vax780_uba.c: - fixed declarations (from Mark Pizzolato) /* V3.7 revision history 3 02-Sep-07 scp.c: - fixed bug in SET THROTTLE command pdp10_cpu.c: - fixed non-portable usage in SHOW HISTORY routine pdp11_ta.c: - forward op at BOT skips initial file gap pdp8_ct.c: - forward op at BOT skips initial file gap - fixed handling of BEOT vax_cpu.c: - fixed bug in read access g-format indexed specifiers 2 12-Jul-07 sim_ether.c (from Dave Hittner): - fixed non-ethernet device removal loop (from Naoki Hamada) - added dynamic loading of wpcap.dll; - corrected exceed max index bug in ethX lookup - corrected failure to look up ethernet device names in the registry on Windows XP x64 sim_timer.c: - fixed idle timer event selection algorithm h316_lp.c: - fixed loss of last print line (from Theo Engel) h316_mt.c: - fixed bug in write without stop (from Theo Engel) h316_stddev.c: - fixed bug in clock increment (from Theo Engel) i1401_cpu.c: - added recognition of overlapped operation modifiers - remove restriction on load-mode binary tape operations i1401_mt.c: - fixed read tape mark operation (found by Van Snyder) - remove restriction on load-mode binary tape operations pdp1_cpu.c: - fixed typo in SBS clear (from Norm Lastovica) pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: - CS1 DVA is in the device, not the MBA pdp8_ct.c: - fixed typo (from Norm Lastovica) vax_cpu.c: - revised idle detector 1 14-May-07 scp.c: - modified sim_instr invocation to call sim_rtcn_init_all - fixed bug in get_sim_opt (reported by Don North) - fixed bug in RESTORE with changed memory size - added global 'RESTORE in progress' flag - fixed breakpoint actions in DO command file processing (from Dave Bryan) all CPU's with clocks: - removed clock initialization (now done in SCP) hp2100_cpu.c (from Dave Bryan): - EDT passes input flag and DMA channel in dat parameter hp2100_ipl.c (from Dave Bryan): - IPLI EDT delays DMA completion interrupt for TSB hp2100_mux.c (from Dave Bryan): - corrected "mux_sta" size from 16 to 21 elements - fixed "muxc_reset" to clear lines 16-20 - fixed control card OTx to set current channel number - fixed to set "muxl_ibuf" in response to a transmit interrupt - changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits - fixed to set "mux_rchp" when a line break is received - fixed incorrect "odd_par" table values - reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity - rixed mux reset (ioCRS) to clear port parameters - fixed to use PUT_DCH instead of PUT_CCH for data channel status - added DIAG/TERM modifiers to implement diagnostic mode pdp11_cpumod.c: - changed memory size routine to work with RESTORE pdp11_hk.c: - NOP and DCLR (at least) do not check drive type - MR2 and MR3 only updated on NOP pdp10_tu.c, pdp11_tu.c: - TMK sets FCE only on read (found by Naoki Hamada) pdp11_xu.c: - added missing FC_RMAL command - cleared multicast on write vax_moddefs.h, vax_cpu1.c: - separated PxBR and SBR mbz checks vax780_defs.h - separated PxBR and SBR mbz checks - modified mbz checks to reflect 780 microcode patches (found by Naoki Hamada) vax_mmu.c: - added address masking to all SBR-based memory reads 0 30-Jan-07 scp.c: - implemented throttle commands - added -e to control error processing in DO command files (from Dave Bryan) sim_console.c: - fixed handling of non-printable characters in KSR mode sim_tape.c: - fixed bug in reverse operations for P7B-format tapes - fixed bug in reverse operations across erase gaps sim_timer.c: - added throttle support - added idle support (based on work by Mark Pizzolato) gri_stddev.c, h316_stddev.c, pdp18b_tt1.c - fixed handling of non-printable characters in KSR mode hp2100_cpu.c, hp2100_cpu0.c, hp2100_cpu1.c, hp2100_cpu2.c, hp2100_cpu3.c, hp2100_cpu4.c (from Dave Bryan): - reorganized CPU modules for easier addition of new instructions - added Double Integer instructions, 1000-F CPU, 2114 and 2115 CPUs, 12K and 24K memory sizes, 12607B and 12578A DMA controllers, and 21xx binary loader protection - fixed DMS self-test instruction execution on 1000-M - fixed indirect interrupt holdoff logic hp2100_ds.c: - fixed REQUEST STATUS to clear status-1 (from Dave Bryan) hp2100_fp1.c: - Added Floating Point Processor (from Dave Bryan) hp2100_lps.c: - fixed diag-mode CLC response i7094_cpu.c: - fixed new bug in halt IO wait loop - added IFT, EFT expanded core test instructions id16_cpu.c, id32_cpu.c: - removed separate multiplexor clock - added idle support id_pas.c: - synced multiplexor poll to real-time clock id_tt.c, id_ttp.c: - fixed handling of non-printable characters in KSR mode - synced keyboard poll to real-time clock id_uvc.c: - changed line-time clock to be free-running pdp1_cpu.c: - added 16-channel sequence break system (API) support - added PDP-1D support pdp1_clk.c: - first release pdp1_dcs.c: - first release pdp1_stddev.c: - separated TTI, TTO for API support pdp1_sys.c: - added PDP-1D, 16-channel SBS, clock, DCS support - fixed bugs in character input, block loader pdp10_cpu.c: - added idle support pdp10_defs.h, pdp10_sys.c: - added CR support pdp10_fe.c, pdp10_tim.c: - synced keyboard poll to real-time clock pdp11_cr.c: - revised for PDP-10 compatibility (CD11 only) pdp11_cpu.c: - added idle support - fixed bug in ASH -32 C value pdp11_rf.c: - fixed unit mask (found by John Dundas) pdp11_stddev.c, vax_stddev.c, vax780_stddev.c: - synced keyboard poll to real-time clock - added clock coscheduling support pdp11_ta.c: - first release pdp11_vh.c: - synced service poll to real-time clock - changed device to be off by default pdp11_dz.c, pdp11_xq.c, pdp11_xu.c: - synced service poll to real-time clock pdp11_sys.c: - fixed operand order in EIS instructions (found by W.F.J. Mueller) - added TA11 support pdp18b_cpu.c: - fixed incorrect value of PC on instruction fetch mem mmgt error - fixed PDP-15 handling of mem mmgt traps (sets API 3) - fixed PDP-15 handling of CAL API 4 (sets only if 0-3 inactive) - fixed PDP-15 CAF to clear memory management mode register - fixed boundary test in KT15/XVM (reported by Andrew Warkentin) - added XVM RDCLK instruction - added idle support and infinite loop detection pdp18b_rf.c: - fixed bug, DSCD does not clear function register pdp18b_stddev.c: - added PDP-15 program-selectable duplex handling instruction - fixed PDP-15 handling of reader out-of-tape - fixed handling of non-printable characters in KSR mode - added XVM RDCLK instruction - changed real-time clock to be free running - synced keyboard poll to real-time clock pdp18b_tt1.c - fixed handling of non-printable characters in KSR mode pdp18b_sys.c: - added XVM RDCLK instruction pdp8_cpu.c: - fixed SC value after DVI overflow (found by Don North) - added idle support and infinite loop detection pdp8_ct.c: - first release pdp8_clk.c: - changed real-time clock to be free running pdp8_sys.c: - added TA8E support - added ability to disambiguate overlapping IOT definitions pdp8_tt.c: - fixed handling of non-printable characters in KSR mode - synced keyboard poll to real-time clock vax_cpu.c, vax_cpu1.c: - added idle support vax_syscm.c: - fixed operand order in EIS instructions (found by W.F.J. Mueller) /* V3.6 revision history 1 25-Jul-06 sim_console.c: - implemented SET/SHOW PCHAR all DECtapes: - fixed conflict in ATTACH switches hp2100_ms.c (from Dave Bryan): - added CAPACITY as alternate for REEL - fixed EOT test for unlimited reel size i1620_cd.c (from Tom McBride): - fixed card reader fgets call - fixed card reader boot sequence i7094_cd.c: - fixed problem with 80 column full cards i7094_cpu.c: - fixed bug in halt IO wait loop i7094_sys.c: - added binary loader (courtesy of Dave Pitt) pdp1_cpu.c: - fixed bugs in MUS and DIV pdp11_cis.c: - added interrupt tests to character instructions - added 11/44 stack probe test to MOVCx (only) pdp11_dl.c: - first release pdp11_rf.c: - first release pdp11_stddev.c: - added UC support to TTI, TTO pdp18b_cpu.c: - fixed RESET to clear AC, L, and MQ pdp18b_dt.c: - fixed checksum calculation bug for Type 550 pdp18b_fpp.c: - fixed bugs in left shift, multiply pdp18b_stddev.c: - fixed Baudot letters/figures inversion for PDP-4 - fixed letters/figures tracking for PDP-4 - fixed PDP-4/PDP-7 default terminal to be local echo pdp18b_sys.c: - added Fiodec, Baudot display - generalized LOAD to handle HRI, RIM, and BIN files pdp8_ttx.c: - fixed bug in DETACH routine 0 15-May-06 scp.c: - revised save file format to save options, unit capacity sim_tape.c, sim_tape.h: - added support for finite reel size - fixed bug in P7B write record most magtapes: - added support for finite reel size h316_cpu.c: fixed bugs in LLL, LRL (found by Theo Engel) h316_lp.c: fixed bug in blanks backscanning (found by Theo Engel) h316_stddev.c: fixed bugs in punch state handling (found by Theo Engel) i1401_cpu.c: fixed bug in divide (reported by Van Snyder) i16_cpu.c: fixed bug in DH (found by Mark Hittinger) i32_cpu.c: - fixed bug in DH (found by Mark Hittinger) - added support for 8 register banks in 8/32 i7094: first release id_io.c: fixed bug, GO preserves EXA and SSTA (found by Davis Johnson) id_idc.c: - fixed WD/WH handling (found by Davis Johnson) - fixed bug, nop command should be ignored (found by Davis Johnson) nova_cpu.c: fixed bug in DIVS (found by Mark Hittinger) pdp11_cis.c: (all reported by John Dundas) - fixed bug in decode table - fixed bug in ASHP - fixed bug in write decimal string with mmgt enabled - fixed bug in 0-length strings in multiply/divide pdp11_cpu.c: fixed order of operand fetching in XOR for SDSD models pdp11_cr.c: added CR11/CD11 support pdp11_tc.c: - fixed READ to set extended data bits in TCST (found by Alan Frisbie) vax780_fload.c: added FLOAD command vax780_sbi.c: fixed writes to ACCS vax780_stddev.c: revised timer logic for EVKAE (reported by Tim Stark) vax_cis.c: (all reported by Tim Stark) - fixed MOVTC, MOVTUC to preserve cc's through page faults - fixed MOVTUC to stop on translated == escape - fixed CVTPL to set registers before destination reg write - fixed CVTPL to set correct cc bit on overflow - fixed EDITPC to preserve cc's through page faults - fixed EDITPC EO$BLANK_ZERO count, cc test - fixed EDITPC EO$INSERT to insert fill instead of blank - fixed EDITPC EO$LOAD_PLUS/MINUS to skip character vax_cpu.c: - added KESU capability to virtual examine - fixed bugs in virtual examine - rewrote CPU history function for improved usability (bugs below reported by Tim Stark) - fixed fault cleanup to clear PSL - fixed ADAWI r-mode to preserve dst<31:16> - fixed ACBD/G to test correct operand - fixed access checking on modify-class specifiers - fixed branch address calculation in CPU history - fixed bug in reported VA on faulting cross-page write vax_cpu1.c: (all reported by Tim Stark) - added access check on system PTE for 11/780 - added mbz check in LDPCTX for 11/780 vax_cmode.c: (all reported by Tim Stark) - fixed omission of SXT - fixed order of operand fetching in XOR vax_fpa.c: (all reported by Tim Stark) - fixed POLYD, POLYG to clear R4, R5 - fixed POLYD, POLYG to set R3 correctly - fixed POLYD, POLYG to not exit prematurely if arg = 0 - fixed POLYD, POLYG to do full 64b multiply - fixed POLYF, POLYD, POLYG to remove truncation on add - fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b - fixed fp add routine to test for zero via fraction to support "denormal" argument from POLYF, POLYD, POLYG - fixed bug in 32b floating multiply routine - fixed bug in 64b extended modulus routine vax_mmu.c: - added access check on system PTE for 11/780 vax_octa.c: (all reported by Tim Stark) - fixed MNEGH to test negated sign, clear C - fixed carry propagation in qp_inc, qp_neg, qp_add - fixed pack routines to test for zero via fraction - fixed ACBH to set cc's on result - fixed POLYH to set R3 correctly - fixed POLYH to not exit prematurely if arg = 0 - fixed POLYH to mask mul reslt to 127b - fixed fp add routine to test for zero via fraction to support "denormal" argument from POLYH - fixed EMODH to concatenate 15b of 16b extension - fixed bug in reported VA on faulting cross-page write /* V3.5 revision history patch date module(s) and fix(es) 2 07-Jan-06 scp.c: - added breakpoint spaces - added REG_FIT support sim_console.c: added ASCII character processing routines sim_tape.c, sim_tape.h: - added write support for P7B format - fixed bug in write forward (found by Dave Bryan) h316_stddev.c, hp2100_stddev.c, hp2100_mux.c, id_tt.c, id_ttp.c, id_pas.c, pdp8_tt.c, pdp8_ttx.c, pdp11_stddev.c, pdp11_dz.c, pdp18b_stddev.c, pdp18b_tt1.c, vax_stddev, gri_stddev.c: - revised to support new character handling routines pdp10_rp.c: fixed DCLR not to clear disk address pdp11_hk.c: fixed overlapped seek interaction with NOP, etc pdp11_rh.c: added enable/disable routine pdp11_rq.c, pdp11_tm.c, pdp11_tq.c, pdp11_ts.c - widened address display to 64b when USE_ADDR64 pdp11_rp.c: - fixed DCLR not to clear disk address - fixed device enable/disable logic to include Massbus adapter - widened address display to 64b when USE_ADDR64 pdp11_tu.c: - fixed device enable/disable logic to include Massbus adapter - widened address display to 64b when USE_ADDR64 - changed default adapter to TM03 (for VMS) pdp8_df.c, pdp8_dt.c, pdp8_rf.c: - fixed unaligned access bug (found by Doug Carman) pdp8_rl.c: fixed IOT 61 decoding bug (found by David Gesswein) vax_cpu.c: - fixed breakpoint detection when USE_ADDR64 option is active - fixed CVTfi to trap on integer overflow if PSW set 1 15-Oct-05 All CPU's, other sources: fixed declaration inconsistencies (from Sterling Garwood) i1401_cpu.c: added control for old/new character encodings i1401_cd.c, i1401_lpt.c, i1401_tty.c: - changed character encodings to be consistent with 7094 - changed column binary format to be consistent with 7094 - added choice of business or Fortran set for output encoding i1401_sys.c: changed WM character to ` under new encodings i1620_cd.c, i1620_lpt.c, i1620_tty.c: - changed character encodings to be consistent with 7094 pdp10_cpu.c: changed MOVNI to eliminate gcc warning pdp11_io.c: fixed bug in autoconfiguration (missing XU) vax_io.c: fixed bug in autoconfiguration (missing XU) vax_fpa.c: fixed bug in 32b structure definitions (from Jason Stevens) 0 1-Sep-05 Note: most source modules have been edited to improve readability and to fix declaration and cast problems in C++ all instruction histories: fixed reversed arguments to calloc scp.c: revised to trim trailing spaces on file inputs sim_sock.c: fixed SIGPIPE error on Unix sim_ether.c: added Windows user-defined adapter names (from Timothe Litt) sim_tape.c: fixed misallocation of TPC map array sim_tmxr.c: added support for SET DISCONNECT hp2100_mux.c: added SET MUXLn DISCONNECT i1401_cpu.c: - fixed SSB-SSG clearing on RESET (reported by Ralph Reinke) - removed error stops in MCE i1401_cd.c: fixed read, punch to ignore modifier on 1, 4 char inst (reported by Van Snyder) id_pas.c: - fixed bug in SHOW CONN/STATS - added SET PASLn DISCONNECT pdp10_ksio.c: revised for new autoconfiguration interface pdp11_cpu.c: replaced WAIT clock queue check with API call pdp11_cpumod.c: added additional 11/60 registers pdp11_io.c: revised autoconfiguration algorithm and interface pdp11_dz.c: revised for new autoconfiguration interface pdp11_vh.c: - revised for new autoconfiguration interface - fixed bug in vector display routine pdp11_xu.c: fixed runt packet processing (found by Tim Chapman) pdp18b_cpu.c, pdp18b_sys.c: - removed spurious AAS instruction pdp18b_tt1.c: - fixed bug in SHOW CONN/STATS - fixed bug in SET LOG/NOLOG - added SET TTOXn DISCONNECT pdp8_ttx.c: - fixed bug in SHOW CONN/STATS - fixed bug in SET LOG/NOLOG - added SET TTOXn DISCONNECT sds_mux.c: - fixed bug in SHOW CONN/STATS - added SET MUXLn DISCONNECT vaxmod_defs.h: added QDSS support vax_io.c: revised autoconfiguration algorithm and interface /* V3.4 revision history 0 01-May-04 scp.c: - fixed ASSERT code - revised syntax for SET DEBUG (from Dave Bryan) - revised interpretation of fprint_sym, fparse_sym returns - moved DETACH sanity tests into detach_unit sim_sock.h and sim_sock.c: - added test for WSAEINPROGRESS (from Tim Riker) many: revised detach routines to test for attached state hp2100_cpu.c: reorganized CPU options (from Dave Bryan) hp2100_cpu1.c: reorganized EIG routines (from Dave Bryan) hp2100_fp1.c: added FFP support (from Dave Bryan) id16_cpu.c: - fixed bug in show history routine (from Mark Hittinger) - revised examine/deposit to do words rather than bytes id32_cpu.c: - fixed bug in initial memory allocation - fixed bug in show history routine (from Mark Hittinger) - revised examine/deposit to do words rather than bytes id16_sys.c, id32_sys: - revised examine/deposit to do words rather than bytes pdp10_tu.c: - fixed bug, ERASE and WREOF should not clear done (reported by Rich Alderson) - fixed error reporting pdp11_tu.c: fixed error reporting /* V3.3 revision history 2 08-Mar-05 scp.c: added ASSERT command (from Dave Bryan) h316_defs.h: fixed IORETURN macro h316_mt.c: fixed error reporting from OCP (found by Philipp Hachtmann) h316_stddev.c: fixed bug in OCP '0001 (found by Philipp Hachtmann) hp2100_cpu.c: split out EAU and MAC instructions hp2100_cpu1.c: (from Dave Bryan) - fixed missing MPCK on JRS target - removed EXECUTE instruction (is NOP in actual microcode) hp2100_fp: (from Dave Bryan) - fixed missing negative overflow renorm in StoreFP i1401_lp.c: fixed bug in write_line (reported by Van Snyder) id32_cpu.c: fixed branches to mask new PC (from Greg Johnson) pdp11_cpu.c: fixed bugs in RESET for 11/70 (reported by Tim Chapman) pdp11_cpumod.c: - fixed bug in SHOW MODEL (from Sergey Okhapkin) - made SYSID variable for 11/70 (from Tim Chapman) - added MBRK write case for 11/70 (from Tim Chapman) pdp11_rq: added RA60, RA71, RA81 disks pdp11_ry: fixed bug in boot code (reported by Graham Toal) vax_cpu.c: fixed initial state of cpu_extmem 1 05-Jan-05 h316_cpu.c: fixed bug in DIV h316_stddev.c: - fixed bug in SKS '104 (reported by Philipp Hachtmann) - fixed bug in SKS '504 - adder reader/punch ASCII file support - added Teletype reader/punch support h316_dp.c: fixed bug in skip on !seeking h316_mt.c: fixed bug in DMA/DMC support h316_lp.c: fixed bug in DMA/DMC support hp2100_cpu.c: - fixed DMA reset to clear alternate CTL flop (from Dave Bryan) - fixed DMA reset to not clear control words (from Dave Bryan) - fixed SBS, CBS, TBS to do virtual reads - separated A/B from M[0/1], for DMA IO (from Dave Bryan) - added SET CPU 21MX-M, 21MX-E (from Dave Brian) - disabled TIMER/EXECUTE/DIAG instructions for 21MX-M (from Dave Bryan) - added post-processor to maintain T/M consistency (from Dave Bryan) hp2100_ds.c: first release hp2100_lps.c (all changes from Dave Bryan) - added restart when set online, etc. - fixed col count for non-printing chars hp2100_lpt.c (all changes from Dave Bryan) - added restart when set online, etc. hp2100_sys.c (all changes from Dave Bryan): - added STOP_OFFLINE, STOP_PWROFF messages i1401_sys.c: added address argument support (from Van Snyder) id_mt.c: added read-only file support lgp_cpu.c, lgp_sys.c: modified VM pointer setup pdp11_cpu.c: fixed WAIT to work in all modes (from John Dundas) pdp11_tm.c, pdp11_ts.c: added read-only file support sds_mt.c: added read-only file support 0 23-Nov-04 scp.c: - added reset_all_p (powerup) - fixed comma-separated SET options (from Dave Bryan) - changed ONLINE/OFFLINE to ENABLED/DISABLED (from Dave Bryan) - modified to flush device buffers on stop (from Dave Bryan) - changed HELP to suppress duplicate command displays sim_console.c: - moved SET/SHOW DEBUG under CONSOLE hierarchy hp2100_cpu.c: (all fixes by Dave Bryan) - moved MP into its own device; added MP option jumpers - modified DMA to allow disabling - modified SET CPU 2100/2116 to truncate memory > 32K - added -F switch to SET CPU to force memory truncation - fixed S-register behavior on 2116 - fixed LIx/MIx behavior for DMA on 2116 and 2100 - fixed LIx/MIx behavior for empty I/O card slots - modified WRU to be REG_HRO - added BRK and DEL to save console settings - fixed use of "unsigned int16" in cpu_reset hp2100_dp.c: (all fixes by Dave Bryan) - fixed enable/disable from either device - fixed ANY ERROR status for 12557A interface - fixed unattached drive status for 12557A interface - status cmd without prior STC DC now completes (12557A) - OTA/OTB CC on 13210A interface also does CLC CC - fixed RAR model - fixed seek check on 13210 if sector out of range hp2100_dq.c: (all fixes by Dave Bryan) - fixed enable/disable from either device - shortened xtime from 5 to 3 (drive avg 156KW/second) - fixed not ready/any error status - fixed RAR model hp2100_dr.c: (all fixes by Dave Bryan) - fixed enable/disable from either device - fixed sector return in status word - provided protected tracks and "Writing Enabled" status bit - fixed DMA last word write, incomplete sector fill value - added "parity error" status return on writes for 12606 - added track origin test for 12606 - added SCP test for 12606 - fixed 12610 SFC operation - added "Sector Flag" status bit - added "Read Inhibit" status bit for 12606 - fixed current-sector determination - added TRACKPROT modifier hp2100_ipl.c, hp2100_ms.c: (all fixes by Dave Bryan) - fixed enable/disable from either device hp2100_lps.c: (all fixes by Dave Bryan) - added SET OFFLINE/ONLINE, POWEROFF/POWERON - fixed status returns for error conditions - fixed handling of non-printing characters - fixed handling of characters after column 80 - improved timing model accuracy for RTE - added fast/realistic timing - added debug printouts hp2100_lpt.c: (all fixes by Dave Bryan) - added SET OFFLINE/ONLINE, POWEROFF/POWERON - fixed status returns for error conditions - fixed TOF handling so form remains on line 0 hp2100_stddev.c (all fixes by Dave Bryan) - added paper tape loop mode, DIAG/READER modifiers to PTR - added PV_LEFT to PTR TRLLIM register - modified CLK to permit disable hp2100_sys.c: (all fixes by Dave Bryan) - added memory protect device - fixed display of CCA/CCB/CCE instructions i1401_cpu.c: added =n to SHOW HISTORY id16_cpu.c: added instruction history id32_cpu.c: added =n to SHOW HISTORY pdp10_defs.h: revised Unibus DMA API's pdp10_ksio.c: revised Unibus DMA API's pdp10_lp20.c: revised Unibus DMA API's pdp10_rp.c: replicated register state per drive pdp10_tu.c: - fixed to set FCE on short record - fixed to return bit<15> in drive type - fixed format specification, 1:0 are don't cares - implemented write check - TMK is cleared by new motion command, not DCLR - DONE is set on data transfers, ATA on non data transfers pdp11_defs.h: - revised Unibus/Qbus DMA API's - added CPU type and options flags pdp11_cpumod.h, pdp11_cpumod.c: - new routines for setting CPU type and options pdp11_io.c: revised Unibus/Qbus DMA API's all PDP-11 DMA peripherals: - revised Unibus/Qbus DMA API's pdp11_hk.c: CS2 OR must be zero for M+ pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: - split Massbus adapter from controllers - replicated RP register state per drive - added TM02/TM03 with TE16/TU45/TU77 drives pdp11_rq.c, pdp11_tq.c: - provided different default timing for PDP-11, VAX - revised to report CPU bus type in stage 1 - revised to report controller type reflecting bus type - added -L switch (LBNs) to RAUSER size specification pdp15_cpu.c: added =n to SHOW HISTORY pdp15_fpp.c: - fixed URFST to mask low 9b of fraction - fixed exception PC setting pdp8_cpu.c: added =n to SHOW HISTORY vax_defs.h: - added octaword, compatibility mode support vax_moddefs.h: - revised Unibus/Qbus DMA API's vax_cpu.c: - moved processor-specific code to vax_sysdev.c - added =n to SHOW HISTORY vax_cpu1.c: - moved processor-specific IPR's to vax_sysdev.c - moved emulation trap to vax_cis.c - added support for compatibility mode vax_cis.c: new full VAX CIS instruction emulator vax_octa.c: new full VAX octaword and h_floating instruction emulator vax_cmode.c: new full VAX compatibility mode instruction emulator vax_io.c: - revised Unibus/Qbus DMA API's vax_io.c, vax_stddev.c, vax_sysdev.c: - integrated powerup into RESET (with -p) vax_sys.c: - fixed bugs in parsing indirect displacement modes - fixed bugs in displaying and parsing character data vax_syscm.c: added display and parse for compatibility mode vax_syslist.c: - split from vax_sys.c - removed PTR, PTP /* V3.2 revision history 3 03-Sep-04 scp.c: - added ECHO command (from Dave Bryan) - qualified RESTORE detach with SIM_SW_REST sim_console: added OS/2 EMX fixes (from Holger Veit) sim_sock.h: added missing definition for OS/2 (from Holger Veit) hp2100_cpu.c: changed error stops to report PC not PC + 1 (from Dave Bryan) hp2100_dp.c: functional and timing fixes (from Dave Bryan) - controller sets ATN for all commands except read status - controller resumes polling for ATN interrupts after read status - check status on unattached drive set busy and not ready - check status tests wrong unit for write protect status - drive on line sets ATN, will set FLG if polling hp2100_dr.c: fixed CLC to stop operation (from Dave Bryan) hp2100_ms.c: functional and timing fixes (from Dave Bryan) - fixed erroneous execution of rejected command - fixed erroneous execution of select-only command - fixed erroneous execution of clear command - fixed odd byte handling for read - fixed spurious odd byte status on 13183A EOF - modified handling of end of medium - added detailed timing, with fast and realistic modes - added reel sizes to simulate end of tape - added debug printouts hp2100_mt.c: modified handling of end of medium (from Dave Bryan) hp2100_stddev.c: added tab to control char set (from Dave Bryan) pdp11_rq.c: VAX controllers luns start at 0 (from Andreas Cejna) vax_cpu.c: fixed bug in EMODD/G, second word of quad dst not probed 2 17-Jul-04 scp.c: fixed problem ATTACHing to read only files (found by John Dundas) sim_console.c: revised Windows console code (from Dave Bryan) sim_fio.c: fixed problem in big-endian read (reported by Scott Bailey) gri_cpu.c: updated MSR, EAO functions hp_stddev.c: generalized handling of control char echoing (from Dave Bryan) vax_sys.c: fixed bad block initialization routine 1 10-Jul-04 scp.c: added SET/SHOW CONSOLE subhierarchy hp2100_cpu.c: fixes and added features (from Dave Bryan) - SBT increments B after store - DMS console map must check dms_enb - SFS x,C and SFC x,C work - MP violation clears automatically on interrupt - SFS/SFC 5 is not gated by protection enabled - DMS enable does not disable mem prot checks - DMS status inconsistent at simulator halt - Examine/deposit are checking wrong addresses - Physical addresses are 20b not 15b - Revised DMS to use memory rather than internal format - Added instruction printout to HALT message - Added M and T internal registers - Added N, S, and U breakpoints Revised IBL facility to conform to microcode Added DMA EDT I/O pseudo-opcode Separated DMA SRQ (service request) from FLG all HP2100 peripherals: - revised to make SFS x,C and SFC x,C work - revised to separate SRQ from FLG all HP2100 IBL bootable peripherals: - revised boot ROMs to use IBL facility - revised SR values to preserve SR<5:3> hp2100_lps.c, hp2100_lpt.c: fixed timing hp2100_dp.c: fixed interpretation of SR<0> hp2100_dr.c: revised boot code to use IBL algorithm hp2100_mt.c, hp2100_ms.c: fixed spurious timing error after CLC (found by Dave Bryan) hp2100_stddev.c: - fixed input behavior during typeout for RTE-IV - suppressed nulls on TTY output for RTE-IV hp2100_sys.c: added SFS x,C and SFC x,C to print/parse routines pdp10_fe.c, pdp11_stddev.c, pdp18b_stddev.c, pdp8_tt.c, vax_stddev.c: - removed SET TTI CTRL-C option pdp11_tq.c: - fixed bug in reporting write protect (reported by Lyle Bickley) - fixed TK70 model number and media ID (found by Robert Schaffrath) pdp11_vh.c: added DHQ11 support (from John Dundas) pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (from John Dundas) pdp11_sys.c, vax_sys.c: added DHQ11 support (from John Dundas) vax_cpu.c: fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) 0 04-Apr-04 scp.c: - added sim_vm_parse_addr and sim_vm_fprint_addr - added REG_VMAD - moved console logging to SCP - changed sim_fsize to use descriptor rather than name - added global device/unit show modifiers - added device debug support (Dave Hittner) - moved device and unit flags, updated save format sim_ether.c: - further generalizations (Dave Hittner, Mark Pizzolato) sim_tmxr.h, sim_tmxr.c: - added tmxr_linemsg - changed TMXR definition to support variable number of lines sim_libraries: - new console library (sim_console.h, sim_console.c) - new file I/O library (sim_fio.h, sim_fio.c) - new timer library (sim_timer.h, sim_timer.c) all terminal multiplexors: revised for tmxr library changes all DECtapes: - added STOP_EOR to enable end-of-reel stop - revised for device debug support all variable-sized devices: revised for sim_fsize change eclipse_cpu.c, nova_cpu.c: fixed device enable/disable support (found by Bruce Ray) nova_defs.h, nova_sys.c, nova_qty.c: - added QTY and ALM support (Bruce Ray) id32_cpu.c, id_dp.c: revised for device debug support lgp: added LGP-30 [LGP-21] simulator pdp1_sys.c: fixed bug in LOAD (found by Mark Crispin) pdp10_mdfp.c: - fixed bug in floating unpack - fixed bug in FIXR (found by Philip Stone, fixed by Chris Smith) pdp11_dz.c: added per-line logging pdp11_rk.c: - added formatting support - added address increment inhibit support - added transfer overrun detection pdp11_hk.c, pdp11_rp.c: revised for device debug support pdp11_rq.c: fixed bug in interrupt control (found by Tom Evans) pdp11_ry.c: added VAX support pdp11_tm.c, pdp11_tq.c, pdp11_ts.c: revised for device debug support pdp11_xu.c: replaced stub with real implementation (Dave Hittner) pdp18b_cpu.c: - fixed bug in XVM g_mode implementation - fixed bug in PDP-15 indexed address calculation - fixed bug in PDP-15 autoindexed address calculation pdp18b_fpp.c: fixed bugs in instruction decode pdp18b_stddev.c: - fixed clock response to CAF - fixed bug in hardware read-in mode bootstrap pdp18b_sys.c: fixed XVM instruction decoding errors pdp18b_tt1.c: added support for 1-16 additional terminals vax_moddef.h, vax_cpu.c, vax_sysdev.c: - added extended physical memory support (Mark Pizzolato) - added RXV21 support vax_cpu1.c: - added PC read fault in EXTxV - fixed PC write fault in INSV /* V3.1 revision history 0 29-Dec-03 sim_defs.h, scp.c: added output stall status all console emulators: added output stall support sim_ether.c (Dave Hittner, Mark Pizzolato, Anders Ahgren): - added Alpha/VMS support - added FreeBSD, Mac OS/X support - added TUN/TAP support - added DECnet duplicate address detection all memory buffered devices (fixed head disks, floppy disks): - cleaned up buffer copy code all DECtapes: - fixed reverse checksum in read all - added DECtape off reel message - simplified timing eclipse_cpu.c (Charles Owen): - added floating point support - added programmable interval timer support - bug fixes h316_cpu.c: - added instruction history - added DMA/DMC support - added device ENABLE/DISABLE support - change default to HSA option included h316_dp.c: added moving head disk support h316_fhd.c: added fixed head disk support h316_mt.c: added magtape support h316_sys.c: added new device support nova_dkp.c (Charles Owen): - fixed bug in flag clear sequence - added diagnostic mode support for disk sizing ` nova_mt.c (Charles Owen): - fixed bug, space operations return record count - fixed bug, reset doesn't cancel rewind nova_sys.c: added floating point, timer support (from Charles Owen) i1620_cpu.c: fixed bug in branch digit (found by Dave Babcock) pdp1_drm.c: - added parallel drum support - fixed bug in serial drum instructin decoding pdp1_sys.c: added parallel drum support, mnemonics pdp11_cpu.c: - added autoconfiguration controls - added support for 18b-only Qbus devices - cleaned up addressing/bus definitions pdp11_rk.c, pdp11_ry.c, pdp11_tm.c, pdp11_hk.c: - added Q18 attribute pdp11_io.c: - added autoconfiguration controls - fixed bug in I/O configuration (found by Dave Hittner) pdp11_rq.c: - revised MB->LBN conversion for greater accuracy - fixed bug with multiple RAUSER drives pdp11_tc.c: changed to be off by default (base config is Qbus) pdp11_xq.c (Dave Hittner, Mark Pizzolato): - fixed second controller interrupts - fixed bugs in multicast and promiscuous setup pdp18b_cpu.c: - added instruction history - fixed PDP-4,-7,-9 autoincrement bug - change PDP-7,-9 default to API option included pdp8_defs.h, pdp8_sys.c: - added DECtape off reel message - added support for TSC8-75 (ETOS) option - added support for TD8E controller pdp8_cpu.c: added instruction history pdp8_rx.c: - fixed bug in RX28 read status (found by Charles Dickman) - fixed double density write pdp8_td.c: added TD8E controller pdp8_tsc.c: added TSC8-75 option vax_cpu.c: - revised instruction history for dynamic sizing - added autoconfiguration controls vax_io.c: - added autoconfiguration controls - fixed bug in I/O configuration (found by Dave Hittner) id16_cpu.c: revised instruction decoding id32_cpu.c: - revised instruction decoding - added instruction history /* V3.0 revision history 2 15-Sep-03 scp.c: - fixed end-of-file problem in dep, idep - fixed error on trailing spaces in dep, idep pdp1_stddev.c - fixed system hang if continue after PTR error - added PTR start/stop functionality - added address switch functionality to PTR BOOT pdp1_sys.c: added multibank capability to LOAD pdp18b_cpu.c: - fixed priorities in PDP-15 API (PI between 3 and 4) - fixed sign handling in PDP-15 unsigned mul/div - fixed bug in CAF, must clear API subsystem i1401_mt.c: - fixed tape read end-of-record handling based on real 1401 - added diagnostic read (space forward) i1620_cpu.c - fixed bug in immediate index add (found by Michael Short) 1 27-Jul-03 pdp1_cpu.c: updated to detect indefinite I/O wait pdp1_drm.c: fixed incorrect logical, missing activate, break pdp1_lp.c: - fixed bugs in instruction decoding, overprinting - updated to detect indefinite I/O wait pdp1_stddev.c: - changed RIM loader to be "hardware" - updated to detect indefinite I/O wait pdp1_sys.c: added block loader format support to LOAD pdp10_rp.c: fixed bug in read header pdp11_rq: fixed bug in user disk size (found by Chaskiel M Grundman) pdp18b_cpu.c: - added FP15 support - added XVM support - added EAE support to the PDP-4 - added PDP-15 "re-entrancy ECO" - fixed memory protect/skip interaction - fixed CAF to only reset peripherals pdp18b_fpp.c: added FP15 pdp18b_lp.c: fixed bug in Type 62 overprinting pdp18b_rf.c: fixed bug in set size routine pdp18b_stddev.c: - increased PTP TIME for PDP-15 operating systems - added hardware RIM loader for PDP-7, PDP-9, PDP-15 pdp18b_sys.c: added FP15, KT15, XVM instructions pdp8b_df.c, pdp8_rf.c: fixed bug in set size routine hp2100_dr.c: - fixed drum sizes - fixed variable capacity interaction with SAVE/RESTORE i1401_cpu.c: revised fetch to model hardware more closely ibm1130: fixed bugs found by APL 1130 nova_dsk.c: fixed bug in set size routine altairz80: fixed bug in real-time clock on Windows host 0 15-Jun-03 scp.c: - added ASSIGN/DEASSIGN - changed RESTORE to detach files - added u5, u6 unit fields - added USE_ADDR64 support - changed some structure fields to unsigned scp_tty.c: added extended file seek sim_sock.c: fixed calling sequence in stubs sim_tape.c: - added E11 and TPC format support - added extended file support sim_tmxr.c: fixed bug in SHOW CONNECTIONS all magtapes: - added multiformat support - added extended file support i1401_cpu.c: - fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS - fixed MCE bug, BS off by 1 if zero suppress - fixed chaining bug, D lost if return to SCP - fixed H branch, branch occurs after continue - added check for invalid 8 character MCW, LCA i1401_mt.c: fixed load-mode end of record response nova_dsk.c: fixed variable size interaction with restore pdp1_dt.c: fixed variable size interaction with restore pdp10_rp.c: fixed ordering bug in attach pdp11_cpu.c: - fixed bug in MMR1 update (found by Tim Stark) - fixed bug in memory size table pdp11_lp.c, pdp11_rq.c: added extended file support pdp11_rl.c, pdp11_rp.c, pdp11_ry.c: fixed ordering bug in attach pdp11_tc.c: fixed variable size interaction with restore pdp11_xq.c: - corrected interrupts on IE state transition (code by Tom Evans) - added interrupt clear on soft reset (first noted by Bob Supnik) - removed interrupt when setting XL or RL (multiple people) - added SET/SHOW XQ STATS - added SHOW XQ FILTERS - added ability to split received packet into multiple buffers - added explicit runt & giant packet processing vax_fpa.c: - fixed integer overflow bug in CVTfi - fixed multiple bugs in EMODf vax_io.c: optimized byte and word DMA routines vax_sysdev.c: - added calibrated delay to ROM reads (from Mark Pizzolato) - fixed calibration problems in interval timer (from Mark Pizzolato) pdp1_dt.c: fixed variable size interaction with restore pdp18b_dt.c: fixed variable size interaction with restore pdp18b_mt.c: fixed bug in MTTR pdp18b_rf.c: fixed variable size interaction with restore pdp8_df.c, pdp8_rf.c: fixed variable size interaction with restore pdp8_dt.c: fixed variable size interaction with restore pdp8_mt.c: fixed bug in SKTR hp2100_dp.c,hp2100_dq.c: - fixed bug in read status (13210A controller) - fixed bug in seek completion id_pt.c: fixed type declaration (found by Mark Pizzolato) gri_cpu.c: fixed bug in SC queue pointer management /* V2.10 revision history 4 03-Mar-03 scp.c - added .ini startup file capability - added multiple breakpoint actions - added multiple switch evaluation points - fixed bug in multiword deposits to file sim_tape.c: magtape simulation library h316_stddev.c: added set line frequency command hp2100_mt.c, hp2100_ms.c: revised to use magtape library i1401_mt.c: revised to use magtape library id_dp.c, id_idc.c: fixed cylinder overflow on writes id_mt.c: - fixed error handling to stop selector channel - revised to use magtape library id16_sys.c, id32_sys.c: added relative addressing support id_uvc.c: - added set frequency command to line frequency clock - improved calibration algorithm for precision clock nova_clk.c: added set line frequency command nova_dsk.c: fixed autosizing algorithm nova_mt.c: revised to use magtape library pdp10_tu.c: revised to use magtape library pdp11_cpu.c: fixed bug in MMR1 update (found by Tim Stark) pdp11_stddev.c - added set line frequency command - added set ctrl-c command pdp11_rq.c: - fixed ordering problem in queue process - fixed bug in vector calculation for VAXen - added user defined drive support pdp11_ry.c: fixed autosizing algorithm pdp11_tm.c, pdp11_ts.c: revised to use magtape library pdp11_tq.c: - fixed ordering problem in queue process - fixed overly restrictive test for bad modifiers - fixed bug in vector calculation for VAXen - added variable controller, user defined drive support - revised to use magtape library pdp18b_cpu.c: fixed three EAE bugs (found by Hans Pufal) pdp18b_mt.c: - fixed bugs in BOT error handling, interrupt handling - revised to use magtape library pdp18b_rf.c: - removed 22nd bit from disk address - fixed autosizing algorithm pdp18b_stddev.c: - added set line frequency command - added set ctrl-c command pdp18b_sys.c: fixed FMTASC printouts (found by Hans Pufal) pdp8_clk.c: added set line frequency command pdp8_df.c, pdp8_rf.c, pdp8_rx.c: fixed autosizing algorithm pdp8_mt.c: - fixed bug in BOT error handling - revised to use magtape library pdp8_tt.c: added set ctrl-c command sds_cpu.c: added set line frequency command sds_mt.c: revised to use magtape library vax_stddev.c: added set ctrl-c command 3 06-Feb-03 scp.c: - added dynamic extension of the breakpoint table - added breakpoint actions hp2100_cpu.c: fixed last cycle bug in DMA output (found by Mike Gemeny) hp2100_ipl.c: individual links are full duplex (found by Mike Gemeny) pdp11_cpu.c: changed R, SP to track PSW respectively pdp18b_defs.h, pdp18b_sys.c: added RB09 fixed head disk, LP09 printer pdp18b_rf.c: - fixed IOT decoding (found by Hans Pufal) - fixed address overrun logic - added variable number of platters and autosizing pdp18b_rf.c: - fixed IOT decoding - fixed bug in command initiation pdp18b_rb.c: new RB09 fixed head disk pdp18b_lp.c: new LP09 line printer pdp8_df.c: added variable number of platters and autosizing pdp8_rf.c: added variable number of platters and autosizing nova_dsk.c: added variable number of platters and autosizing id16_cpu.c: fixed bug in SETM, SETMR (found by Mark Pizzolato) 2 15-Jan-03 scp.c: - added dynamic memory size flag and RESTORE support - added EValuate command - added get_ipaddr routine - added ! (OS command) feature (from Mark Pizzolato) - added BREAK support to sim_poll_kbd (from Mark Pizzolato) sim_tmxr.c: - fixed bugs in IAC+IAC handling (from Mark Pizzolato) - added IAC+BRK handling (from Mark Pizzolato) sim_sock.c: - added use count for Windows start/stop - added sim_connect_sock pdp1_defs.h, pdp1_cpu.c, pdp1_sys.c, pdp1_drm.c: added Type 24 serial drum pdp18_defs.h: added PDP-4 drum support hp2100_cpu.c: added 21MX IOP support hp2100_ipl.c: added HP interprocessor link support pdp11_tq.c: fixed bug in transfer end packet length pdp11_xq.c: - added VMScluster support (thanks to Mark Pizzolato) - added major performance enhancements (thanks to Mark Pizzolato) - added local packet processing - added system id broadcast pdp11_stddev.c: changed default to 7b (for early UNIX) vax_cpu.c, vax_io.c, vax_stddev.c, vax_sysdev.c: added console halt capability (from Mark Pizzolato) all terminals and multiplexors: added BREAK support 1 21-Nov-02 pdp1_stddev.c: changed typewriter to half duplex (found by Derek Peschel) pdp10_tu.c: - fixed bug in bootstrap (reported by Michael Thompson) - fixed bug in read (reported by Harris Newman) 0 15-Nov-02 SCP and libraries scp.c: - added Telnet console support - removed VT emulation support - added support for statically buffered devices - added HELP - fixed bugs in set_logon, ssh_break (found by David Hittner) - added VMS file optimization (from Robert Alan Byer) - added quiet mode, DO with parameters, GUI interface, extensible commands (from Brian Knittel) - added DEVICE context and flags - added central device enable/disable support - modified SAVE/GET to save and restore flags - modified boot routine calling sequence scp_tty.c: - removed VT emulation support - added sim_os_sleep, renamed sim_poll_kbd, sim_putchar sim_tmxr.c: - modified for Telnet console support - fixed bug in binary (8b) support sim_sock.c: modified for Telnet console support sim_ether.c: new library for Ethernet (from David Hittner) all magtapes: - added support for end of medium - cleaned up BOT handling all DECtapes: added support for RT11 image file format most terminals and multiplexors: - added support for 7b vs 8b character processing PDP-1 pdp1_cpu.c, pdp1_sys.c, pdp1_dt.c: added PDP-1 DECtape support PDP-8 pdp8_cpu.c, all peripherals: - added variable device number support - added new device enabled/disable support pdp8_rx.c: added RX28/RX02 support PDP-11 pdp11_defs.h, pdp11_io.c, pdp11_sys.c, all peripherals: - added variable vector support - added new device enable/disable support - added autoconfiguration support all bootstraps: modified to support variable addresses dec_mscp.h, pdp11_tq.c: added TK50 support pdp11_rq.c: - added multicontroller support - fixed bug in HBE error log packet - fixed bug in ATP processing pdp11_ry.c: added RX211/RX02 support pdp11_hk.c: added RK611/RK06/RK07 support pdp11_tq.c: added TMSCP support pdp11_xq.c: added DEQNA/DELQA support (from David Hittner) pdp11_pclk.c: added KW11P support pdp11_ts.c: - fixed bug in CTL decoding - fixed bug in extended status XS0_MOT pdp11_stddev.c: removed paper tape to its own module PDP-18b pdp18b_cpu.c, all peripherals: - added variable device number support - added new device enabled/disabled support VAX dec_dz.h: fixed bug in number of boards calculation vax_moddefs.h, vax_io.c, vax_sys.c, all peripherals: - added variable vector support - added new device enable/disable support - added autoconfiguration support vax_sys.c: - generalized examine/deposit - added TMSCP, multiple RQDX3, DEQNA/DELQA support vax_stddev.c: removed paper tape, now uses PDP-11 version vax_sysdev.c: - allowed NVR to be attached to file - removed unused variables (found by David Hittner) PDP-10 pdp10_defs.h, pdp10_ksio.c, all peripherals: - added variable vector support - added new device enable/disable support pdp10_defs.h, pdp10_ksio.c: added support for standard PDP-11 peripherals, added RX211 support pdp10_pt.c: rewritten to reference common implementation Nova, Eclipse: nova_cpu.c, eclipse_cpu.c, all peripherals: - added new device enable/disable support HP2100 hp2100_cpu: - fixed bugs in the EAU, 21MX, DMS, and IOP instructions - fixed bugs in the memory protect and DMS functions - created new options to enable/disable EAU, MPR, DMS - added new device enable/disable support hp2100_fp.c: - recoded to conform to 21MX microcode algorithms hp2100_stddev.c: - fixed bugs in TTY reset, OTA, time base generator - revised BOOT support to conform to RBL loader - added clock calibration hp2100_dp.c: - changed default to 13210A - added BOOT support hp2100_dq.c: - finished incomplete functions, fixed head switching - added BOOT support hp2100_ms.c: - fixed bugs found by diagnostics - added 13183 support - added BOOT support hp2100_mt.c: - fixed bugs found by diagnostics - disabled by default hp2100_lpt.c: implemented 12845A controller hp2100_lps.c: - renamed 12653A controller - added diagnostic mode for MPR, DCPC diagnostics - disabled by default IBM 1620: first release /* V2.9 revision history 11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark without word mark (found by Van Snyder) i1401_dp.c: reworked address generation and checking vax_cpu.c: added infinite loop detection and halt to boot ROM option (from Mark Pizzolato) vax_fpa.c: changed function names to prevent conflict with C math library pdp11_cpu.c: fixed bug in MMR0 update logic (from John Dundas) pdp18b_stddev.c: added "ASCII mode" for reader and punch (from Hans Pufal) gri_*.c: added GRI-909 simulator scp.c: added DO echo, DO exit (from Brian Knittel) scp_tty.c: added Windows priority hacking (from Mark Pizzolato) 10 15-Jun-02 scp.c: fixed error checking on calls to fxread/fxwrite (found by Norm Lastovic) scp_tty.c, sim_vt.h, sim_vt.c: added VTxxx emulation support for Windows (from Fischer Franz) sim_sock.c: added OS/2 support (from Holger Veit) pdp11_cpu.c: fixed bugs (from John Dundas) - added special case for PS<15:12> = 1111 to MFPI - removed special case from MTPI - added masking of relocation adds i1401_cpu.c: - added multiply/divide - fixed bugs (found by Van Snyder) o 5 and 7 character H, 7 character doesn't branch o 8 character NOP o 1401-like memory dump i1401_dp.c: added 1311 disk 9 04-May-02 pdp11_rq: fixed bug in polling routine 8 03-May-02 scp.c: - changed LOG/NOLOG to SET LOG/NOLOG - added SHOW LOG - added SET VT/NOVT and SHOW VT for VT emulation sim_sock.h: changed VMS stropt.h include to ioctl.h vax_cpu.c - added TODR powerup routine to set date, time on boot - fixed exception flows to clear trap request - fixed register logging in autoincrement indexed vax_stddev.c: added TODR powerup routine vax_cpu1.c: fixed exception flows to clear trap request 7 30-Apr-02 scp.c: fixed bug in clock calibration when (real) clock jumps forward due too far (found by Jonathan Engdahl) pdp11_cpu.c: fixed bugs, added features (from John Dundas and Wolfgang Helbig) - added HTRAP and BPOK to maintenance register - added trap on kernel HALT if MAINT set - fixed red zone trap, clear odd address and nxm traps - fixed RTS SP, don't increment restored SP - fixed TSTSET, write dst | 1 rather than prev R0 | 1 - fixed DIV, set N=0,Z=1 on div by zero (J11, 11/70) - fixed DIV, set set N=Z=0 on overfow (J11, 11/70) - fixed ASH, ASHC, count = -32 used implementation- dependent 32 bit right shift - fixed illegal instruction test to detect 000010 - fixed write-only page test pdp11_rp.c: fixed SHOW ADDRESS command vaxmod_defs.h: fixed DZ vector base and number of lines dec_dz.h: - fixed interrupt acknowledge routines - fixed SHOW ADDRESS command all magtape routines: added test for badly formed record length (suggested by Jonathan Engdahl) 6 18-Apr-02 vax_cpu.c: fixed CASEL condition codes vax_cpu1.c: fixed vfield pos > 31 test to be unsigned vax_fpu.c: fixed EDIV overflow test for 0 quotient 5 14-Apr-02 vax_cpu1.c: - fixed interrupt, prv_mode set to 0 (found by Tim Stark) - fixed PROBEx to mask mode to 2b (found by Kevin Handy) 4 1-Apr-02 pdp11_rq.c: fixed bug, reset cleared write protect status pdp11_ts.c: fixed bug in residual frame count after space 3 15-Mar-02 pdp11_defs.h: changed default model to KDJ11A (11/73) pdp11_rq.c: adjusted delays for M+ timing bugs hp2100_cpu.c, pdp10_cpu.c, pdp11_cpu.c: tweaked abort code for ANSI setjmp/longjmp compliance hp2100_cpu.c, hp2100_fp.c, hp2100_stddev.c, hp2100_sys.c: revised to allocate memory dynamically 2 01-Mar-02 pdp11_cpu.c: - fixed bugs in CPU registers - fixed double operand evaluation order for M+ pdp11_rq.c: added delays to initialization for RSX11M+ prior to V4.5 1 20-Feb-02 scp.c: fixed bug in clock calibration when (real) time runs backwards pdp11_rq.c: fixed bug in host timeout logic pdp11_ts.c: fixed bug in message header logic pdp18b_defs.h, pdp18b_dt.c, pdp18b_sys.c: added PDP-7 DECtape support hp2100_cpu.c: - added floating point and DMS - fixed bugs in DIV, ASL, ASR, LBT, SBT, CBT, CMW hp2100_sys.c: added floating point, DMS hp2100_fp.c: added floating point ibm1130: added Brian Knittel's IBM 1130 simulator 0 30-Jan-02 scp.c: - generalized timer package for multiple timers - added circular register arrays - fixed bugs, line spacing in modifier display - added -e switch to attach - moved device enable/disable to simulators scp_tty.c: VAX specific fix (from Robert Alan Byer) sim_tmxr.c, sim_tmxr.h: - added tmxr_fstats, tmxr_dscln - renamed tmxr_fstatus to tmxr_fconns sim_sock.c, sim_sock.h: added VMS support (from Robert Alan Byer) pdp_dz.h, pdp18b_tt1.c, nova_tt1.c: - added SET DISCONNECT - added SHOW STATISTICS pdp8_defs.h: fixed bug in interrupt enable initialization pdp8_ttx.c: rewrote as unified multiplexor pdp11_cpu.c: fixed calc_MMR1 macro (found by Robert Alan Byer) pdp11_stddev.c: fixed bugs in KW11L (found by John Dundas) pdp11_rp.c: fixed bug in 18b mode boot pdp11 bootable I/O devices: fixed register setup at boot exit (found by Doug Carman) hp2100_cpu.c: - fixed DMA register tables (found by Bill McDermith) - fixed SZx,SLx,RSS bug (found by Bill McDermith) - fixed flop restore logic (found by Bill McDermith) hp2100_mt.c: fixed bug on write of last character hp2100_dq,dr,ms,mux.c: added new disk, magtape, and terminal multiplexor controllers i1401_cd.c, i1401_mt.c: new zero footprint bootstraps (from Van Snyder) i1401_sys.c: fixed symbolic display of H, NOP with no trailing word mark (found by Van Snyder) most CPUs: - replaced OLDPC with PC queue - implemented device enable/disable locally V2.8 revision history 5 25-Dec-01 scp.c: fixed bug in DO command (found by John Dundas) pdp10_cpu.c: - moved trap-in-progress to separate variable - cleaned up declarations - cleaned up volatile state for GNU C longjmp pdp11_cpu.c: cleaned up declarations pdp11_rq.c: added RA-class disks 4 17-Dec-01 pdp11_rq.c: added delayed processing of packets 3 16-Dec-01 pdp8_cpu.c: - mode A EAE instructions didn't clear GTF - ASR shift count > 24 mis-set GTF - effective shift count == 32 didn't work 2 07-Dec-01 scp.c: added breakpoint package all CPU's: revised to use new breakpoint package 1 05-Dec-01 scp.c: fixed bug in universal register name logic 0 30-Nov-01 Reorganized simh source and documentation tree scp: Added DO command, universal registers, extended SET/SHOW logic pdp11: overhauled PDP-11 for DMA map support, shared sources with VAX, dynamic buffer allocation 18b pdp: overhauled interrupt structure pdp8: added RL8A pdp10: fixed two ITS-related bugs (found by Dave Conroy) V2.7 revision history patch date module(s) and fix(es) 15 23-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: fixed bugs error interrupt handling pdp10_defs.h, pdp10_ksio.c, pdp10_fe.c, pdp10_fe.c, pdp10_rp.c, pdp10_tu.c: reworked I/O page interface to use symbolic base addresses and lengths 14 20-Oct-01 dec_dz.h, sim_tmxr_h, sim_tmxr.c: fixed bug in Telnet state handling (found by Thord Nilson), removed tmxr_getchar, added tmxr_rqln and tmxr_tqln 13 18-Oct-01 pdp11_tm.c: added stub diagnostic register clock for RSTS/E (found by Thord Nilson) 12 15-Oct-01 pdp11_defs.h, pdp11_cpu.c, pdp11_tc.c, pdp11_ts.c, pdp11_rp.c: added operations logging 11 8-Oct-01 scp.c: added sim_rev.h include and version print pdp11_cpu.c: fixed bug in interrupt acknowledge, multiple outstanding interrupts caused the lowest rather than the highest to be acknowledged 10 7-Oct-01 pdp11_stddev.c: added monitor bits (CSR<7>) for full KW11L compatibility, needed for RSTS/E autoconfiguration 9 6-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: rewrote interrupt logic from RH11/RH70 schematics, to mimic hardware quirks dec_dz.c: fixed bug in carrier detect logic, carrier detect was being cleared on next modem poll 8 4-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: undid edit of 28-Sep-01; real problem was level-sensitive nature of CS1_SC, but CS1_SC can only trigger an interrupt if DONE is set 7 2-Oct-01 pdp11_rp.c, pdp10_rp.c: CS1_SC is evaluated as a level- sensitive, rather than an edge-sensitive, input to interrupt request 6 30-Sep-01 pdp11_rp.c, pdp10_rp.c: separated out CS1<5:0> to per- drive registers pdp10_tu.c: based on above, cleaned up handling of non-existent formatters, fixed non-data transfer commands clearing DONE 5 28-Sep-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: controller should interrupt if ATA or SC sets when IE is set, was interrupting only if DON = 1 as well 4 27-Sep-01 pdp11_ts.c: - NXM errors should return TC4 or TC5; were returning TC3 - extended features is part of XS2; was returned in XS3 - extended characteristics (fifth) word needed for RSTS/E pdp11_tc.c: stop, stop all do cause an interrupt dec_dz.h: scanner should find a ready output line, even if there are no connections; needed for RSTS/E autoconfigure scp.c: - added routine sim_qcount for 1130 - added "simulator exit" detach routine for 1130 sim_defs.h: added header for sim_qcount 3 20-Sep-01 pdp11_ts.c: boot code binary was incorrect 2 19-Sep-01 pdp18b_cpu.c: EAE should interpret initial count of 00 as 100 scp.c: modified Macintosh support 1 17-Sep-01 pdp8_ttx.c: new module for PDP-8 multi-terminal support pdp18b_tt1.c: modified to use sim_tmxr library nova_tt1.c: modified to use sim_tmxr library dec_dz.h: added autodisconnect support scp.c: removed old multiconsole support sim_tmxr.c: modified calling sequence for sim_putchar_ln sim_sock.c: added Macintosh sockets support */ #endif simh-3.8.1/PDP8/0000755000175000017500000000000011111665260011353 5ustar vlmvlmsimh-3.8.1/PDP8/pdp8_ttx.c0000644000175000017500000003562611111101314013266 0ustar vlmvlm/* pdp8_ttx.c: PDP-8 additional terminals simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ttix,ttox PT08/KL8JA terminal input/output 19-Nov-08 RMS Revised for common TMXR show routines 07-Jun-06 RMS Added UNIT_IDLE flag 06-Jul-06 RMS Fixed bug in DETACH routine 22-Nov-05 RMS Revised for new terminal processing routines 29-Jun-05 RMS Added SET TTOXn DISCONNECT Fixed bug in SET LOG/NOLOG 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS 05-Jan-04 RMS Revised for tmxr library changes 09-May-03 RMS Added network device flag 25-Apr-03 RMS Revised for extended file support 22-Dec-02 RMS Added break support 02-Nov-02 RMS Added 7B/8B support 04-Oct-02 RMS Added DIB, device number support 22-Aug-02 RMS Updated for changes to sim_tmxr.c 06-Jan-02 RMS Added device enable/disable support 30-Dec-01 RMS Complete rebuild 30-Nov-01 RMS Added extended SET/SHOW support This module implements four individual serial interfaces similar in function to the console. These interfaces are mapped to Telnet based connections as though they were the four lines of a terminal multiplexor. The connection polling mechanism is superimposed onto the keyboard of the first interface. */ #include "pdp8_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #define TTX_LINES 4 #define TTX_MASK (TTX_LINES - 1) #define TTX_GETLN(x) (((x) >> 4) & TTX_MASK) extern int32 int_req, int_enable, dev_done, stop_inst; uint8 ttix_buf[TTX_LINES] = { 0 }; /* input buffers */ uint8 ttox_buf[TTX_LINES] = { 0 }; /* output buffers */ int32 ttx_tps = 100; /* polls per second */ TMLN ttx_ldsc[TTX_LINES] = { 0 }; /* line descriptors */ TMXR ttx_desc = { TTX_LINES, 0, 0, ttx_ldsc }; /* mux descriptor */ DEVICE ttix_dev, ttox_dev; int32 ttix (int32 IR, int32 AC); int32 ttox (int32 IR, int32 AC); t_stat ttix_svc (UNIT *uptr); t_stat ttix_reset (DEVICE *dptr); t_stat ttox_svc (UNIT *uptr); t_stat ttox_reset (DEVICE *dptr); t_stat ttx_attach (UNIT *uptr, char *cptr); t_stat ttx_detach (UNIT *uptr); void ttx_enbdis (int32 dis); /* TTIx data structures ttix_dev TTIx device descriptor ttix_unit TTIx unit descriptor ttix_reg TTIx register list ttix_mod TTIx modifiers list */ DIB ttix_dib = { DEV_KJ8, 8, { &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox } }; UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG ttix_reg[] = { { BRDATA (BUF, ttix_buf, 8, 8, TTX_LINES) }, { GRDATA (DONE, dev_done, 8, TTX_LINES, INT_V_TTI1) }, { GRDATA (ENABLE, int_enable, 8, TTX_LINES, INT_V_TTI1) }, { GRDATA (INT, int_req, 8, TTX_LINES, INT_V_TTI1) }, { DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, ttx_tps, 10), REG_NZ + PV_LEFT }, { ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO }, { NULL } }; MTAB ttix_mod[] = { { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE ttix_dev = { "TTIX", &ttix_unit, ttix_reg, ttix_mod, 1, 10, 31, 1, 8, 8, &tmxr_ex, &tmxr_dep, &ttix_reset, NULL, &ttx_attach, &ttx_detach, &ttix_dib, DEV_NET | DEV_DISABLE }; /* TTOx data structures ttox_dev TTOx device descriptor ttox_unit TTOx unit descriptor ttox_reg TTOx register list */ UNIT ttox_unit[] = { { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT } }; REG ttox_reg[] = { { BRDATA (BUF, ttox_buf, 8, 8, TTX_LINES) }, { GRDATA (DONE, dev_done, 8, TTX_LINES, INT_V_TTO1) }, { GRDATA (ENABLE, int_enable, 8, TTX_LINES, INT_V_TTO1) }, { GRDATA (INT, int_req, 8, TTX_LINES, INT_V_TTO1) }, { URDATA (TIME, ttox_unit[0].wait, 10, 24, 0, TTX_LINES, PV_LEFT) }, { NULL } }; MTAB ttox_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &ttx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &ttx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &ttx_desc }, { 0 } }; DEVICE ttox_dev = { "TTOX", ttox_unit, ttox_reg, ttox_mod, 4, 10, 31, 1, 8, 8, NULL, NULL, &ttox_reset, NULL, NULL, NULL, NULL, DEV_DISABLE }; /* Terminal input: IOT routine */ int32 ttix (int32 inst, int32 AC) { int32 pulse = inst & 07; /* IOT pulse */ int32 ln = TTX_GETLN (inst); /* line # */ int32 itti = (INT_TTI1 << ln); /* rx intr */ int32 itto = (INT_TTO1 << ln); /* tx intr */ switch (pulse) { /* case IR<9:11> */ case 0: /* KCF */ dev_done = dev_done & ~itti; /* clear flag */ int_req = int_req & ~itti; break; case 1: /* KSF */ return (dev_done & itti)? IOT_SKP + AC: AC; case 2: /* KCC */ dev_done = dev_done & ~itti; /* clear flag */ int_req = int_req & ~itti; return 0; /* clear AC */ case 4: /* KRS */ return (AC | ttix_buf[ln]); /* return buf */ case 5: /* KIE */ if (AC & 1) int_enable = int_enable | (itti + itto); else int_enable = int_enable & ~(itti + itto); int_req = INT_UPDATE; /* update intr */ break; case 6: /* KRB */ dev_done = dev_done & ~itti; /* clear flag */ int_req = int_req & ~itti; return ttix_buf[ln]; /* return buf */ default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ return AC; } /* Unit service */ t_stat ttix_svc (UNIT *uptr) { int32 ln, c, temp; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; temp = sim_rtcn_calb (ttx_tps, TMR_TTX); /* calibrate */ sim_activate (uptr, temp); /* continue poll */ ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ if (ln >= 0) /* got one? rcv enb*/ ttx_ldsc[ln].rcve = 1; tmxr_poll_rx (&ttx_desc); /* poll for input */ for (ln = 0; ln < TTX_LINES; ln++) { /* loop thru lines */ if (ttx_ldsc[ln].conn) { /* connected? */ if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */ if (temp & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags)); ttix_buf[ln] = c; dev_done = dev_done | (INT_TTI1 << ln); int_req = INT_UPDATE; } } } return SCPE_OK; } /* Reset routine */ t_stat ttix_reset (DEVICE *dptr) { int32 t, ln, itto; ttx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ if (ttix_unit.flags & UNIT_ATT) { /* if attached, */ if (!sim_is_active (&ttix_unit)) { t = sim_rtcn_init (ttix_unit.wait, TMR_TTX); sim_activate (&ttix_unit, t); /* activate */ } } else sim_cancel (&ttix_unit); /* else stop */ for (ln = 0; ln < TTX_LINES; ln++) { /* for all lines */ ttix_buf[ln] = 0; /* clear buf, */ itto = (INT_TTI1 << ln); /* interrupt */ dev_done = dev_done & ~itto; /* clr done, int */ int_req = int_req & ~itto; int_enable = int_enable | itto; /* set enable */ } return SCPE_OK; } /* Terminal output: IOT routine */ int32 ttox (int32 inst, int32 AC) { int32 pulse = inst & 07; /* pulse */ int32 ln = TTX_GETLN (inst); /* line # */ int32 itti = (INT_TTI1 << ln); /* rx intr */ int32 itto = (INT_TTO1 << ln); /* tx intr */ switch (pulse) { /* case IR<9:11> */ case 0: /* TLF */ dev_done = dev_done | itto; /* set flag */ int_req = INT_UPDATE; /* update intr */ break; case 1: /* TSF */ return (dev_done & itto)? IOT_SKP + AC: AC; case 2: /* TCF */ dev_done = dev_done & ~itto; /* clear flag */ int_req = int_req & ~itto; /* clear intr */ break; case 5: /* SPI */ return (int_req & (itti | itto))? IOT_SKP + AC: AC; case 6: /* TLS */ dev_done = dev_done & ~itto; /* clear flag */ int_req = int_req & ~itto; /* clear int req */ case 4: /* TPC */ sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate */ ttox_buf[ln] = AC & 0377; /* load buffer */ break; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ return AC; } /* Unit service */ t_stat ttox_svc (UNIT *uptr) { int32 c, ln = uptr - ttox_unit; /* line # */ if (ttx_ldsc[ln].conn) { /* connected? */ if (ttx_ldsc[ln].xmte) { /* tx enabled? */ TMLN *lp = &ttx_ldsc[ln]; /* get line */ c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags)); if (c >= 0) /* output char */ tmxr_putc_ln (lp, c); tmxr_poll_tx (&ttx_desc); /* poll xmt */ } else { tmxr_poll_tx (&ttx_desc); /* poll xmt */ sim_activate (uptr, ttox_unit[ln].wait); /* wait */ return SCPE_OK; } } dev_done = dev_done | (INT_TTO1 << ln); /* set done */ int_req = INT_UPDATE; /* update intr */ return SCPE_OK; } /* Reset routine */ t_stat ttox_reset (DEVICE *dptr) { int32 ln, itto; ttx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ for (ln = 0; ln < TTX_LINES; ln++) { /* for all lines */ ttox_buf[ln] = 0; /* clear buf */ itto = (INT_TTO1 << ln); /* interrupt */ dev_done = dev_done & ~itto; /* clr done, int */ int_req = int_req & ~itto; int_enable = int_enable | itto; /* set enable */ sim_cancel (&ttox_unit[ln]); /* deactivate */ } return SCPE_OK; } /* Attach master unit */ t_stat ttx_attach (UNIT *uptr, char *cptr) { int32 t; t_stat r; r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; t = sim_rtcn_init (ttix_unit.wait, TMR_TTX); /* init calib */ sim_activate (uptr, t); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat ttx_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&ttx_desc, uptr); /* detach */ for (i = 0; i < TTX_LINES; i++) /* all lines, */ ttx_ldsc[i].rcve = 0; /* disable rcv */ sim_cancel (uptr); /* stop poll */ return r; } /* Enable/disable device */ void ttx_enbdis (int32 dis) { if (dis) { ttix_dev.flags = ttix_dev.flags | DEV_DIS; ttox_dev.flags = ttox_dev.flags | DEV_DIS; } else { ttix_dev.flags = ttix_dev.flags & ~DEV_DIS; ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; } return; } simh-3.8.1/PDP8/pdp8_pt.c0000644000175000017500000002346611107355106013110 0ustar vlmvlm/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr,ptp PC8E paper tape reader/punch 25-Apr-03 RMS Revised for extended file support 04-Oct-02 RMS Added DIBs 30-May-02 RMS Widened POS to 32b 30-Nov-01 RMS Added read only unit support 30-Mar-98 RMS Added RIM loader as PTR bootstrap */ #include "pdp8_defs.h" extern int32 int_req, int_enable, dev_done, stop_inst; int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ int32 ptr (int32 IR, int32 AC); int32 ptp (int32 IR, int32 AC); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_reg PTR register list */ DIB ptr_dib = { DEV_PTR, 1, { &ptr } }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 8) }, { FLDATA (DONE, dev_done, INT_V_PTR) }, { FLDATA (ENABLE, int_enable, INT_V_PTR) }, { FLDATA (INT, int_req, INT_V_PTR) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB ptr_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, NULL, NULL, &ptr_dib, 0 }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_reg PTP register list */ DIB ptp_dib = { DEV_PTP, 1, { &ptp } }; 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, dev_done, INT_V_PTP) }, { FLDATA (ENABLE, int_enable, INT_V_PTP) }, { FLDATA (INT, int_req, INT_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[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, &ptp_dib, 0 }; /* Paper tape reader: IOT routine */ int32 ptr (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* RPE */ int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* RSF */ return (dev_done & INT_PTR)? IOT_SKP + AC: AC; case 6: /* RFC!RRB */ sim_activate (&ptr_unit, ptr_unit.wait); case 2: /* RRB */ dev_done = dev_done & ~INT_PTR; /* clear flag */ int_req = int_req & ~INT_PTR; /* clear int req */ return (AC | ptr_unit.buf); /* or data to AC */ case 4: /* RFC */ sim_activate (&ptr_unit, ptr_unit.wait); dev_done = dev_done & ~INT_PTR; /* clear flag */ int_req = int_req & ~INT_PTR; /* clear int req */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); if ((temp = getc (ptr_unit.fileref)) == EOF) { if (feof (ptr_unit.fileref)) { if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } dev_done = dev_done | INT_PTR; /* set done */ int_req = INT_UPDATE; /* update interrupts */ ptr_unit.buf = temp & 0377; ptr_unit.pos = ptr_unit.pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; dev_done = dev_done & ~INT_PTR; /* clear done, int */ int_req = int_req & ~INT_PTR; int_enable = int_enable | INT_PTR; /* set enable */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Paper tape punch: IOT routine */ int32 ptp (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* PCE */ int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* PSF */ return (dev_done & INT_PTP)? IOT_SKP + AC: AC; case 2: /* PCF */ dev_done = dev_done & ~INT_PTP; /* clear flag */ int_req = int_req & ~INT_PTP; /* clear int req */ return AC; case 6: /* PLS */ dev_done = dev_done & ~INT_PTP; /* clear flag */ int_req = int_req & ~INT_PTP; /* clear int req */ case 4: /* PPC */ ptp_unit.buf = AC & 0377; /* load punch buf */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { dev_done = dev_done | INT_PTP; /* set done */ int_req = INT_UPDATE; /* update interrupts */ if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ptp_unit.pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; dev_done = dev_done & ~INT_PTP; /* clear done, int */ int_req = int_req & ~INT_PTP; int_enable = int_enable | INT_PTP; /* set enable */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 07756 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 06014, /* 7756, RFC */ 06011, /* 7757, LOOP, RSF */ 05357, /* JMP .-1 */ 06016, /* RFC RRB */ 07106, /* CLL RTL*/ 07006, /* RTL */ 07510, /* SPA*/ 05374, /* JMP 7774 */ 07006, /* RTL */ 06011, /* RSF */ 05367, /* JMP .-1 */ 06016, /* RFC RRB */ 07420, /* SNL */ 03776, /* DCA I 7776 */ 03376, /* 7774, DCA 7776 */ 05357, /* JMP 7757 */ 00000, /* 7776, 0 */ 05301 /* 7777, JMP 7701 */ }; t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 M[]; if (ptr_dib.dev != DEV_PTR) /* only std devno */ return STOP_NOTSTD; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_sys.c0000644000175000017500000011403211107350614013270 0ustar vlmvlm/* pdp8_sys.c: PDP-8 simulator interface Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 24-Jun-08 RMS Fixed bug in new rim loader (found by Don North) 24-May-08 RMS Fixed signed/unsigned declaration inconsistency 03-Sep-07 RMS Added FPP8 support Rewrote rim and binary loaders 15-Dec-06 RMS Added TA8E support, IOT disambiguation 30-Oct-06 RMS Added infinite loop stop 18-Oct-06 RMS Re-ordered device list 17-Oct-03 RMS Added TSC8-75, TD8E support, DECtape off reel message 25-Apr-03 RMS Revised for extended file support 30-Dec-01 RMS Revised for new TTX 26-Nov-01 RMS Added RL8A support 17-Sep-01 RMS Removed multiconsole support 16-Sep-01 RMS Added TSS/8 packed char support, added KL8A support 27-May-01 RMS Added multiconsole support 18-Mar-01 RMS Added DF32 support 14-Mar-01 RMS Added extension detection of RIM binary tapes 15-Feb-01 RMS Added DECtape support 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface 10-Apr-98 RMS Added RIM loader support 17-Feb-97 RMS Fixed bug in handling of bin loader fields */ #include "pdp8_defs.h" #include extern DEVICE cpu_dev; extern UNIT cpu_unit; extern DEVICE tsc_dev; extern DEVICE ptr_dev, ptp_dev; extern DEVICE tti_dev, tto_dev; extern DEVICE clk_dev, lpt_dev; extern DEVICE rk_dev, rl_dev; extern DEVICE rx_dev; extern DEVICE df_dev, rf_dev; extern DEVICE dt_dev, td_dev; extern DEVICE mt_dev, ct_dev; extern DEVICE ttix_dev, ttox_dev; extern REG cpu_reg[]; extern uint16 M[]; extern int32 sim_switches; t_stat fprint_sym_fpp (FILE *of, t_value *val); t_stat parse_sym_fpp (char *cptr, t_value *val); char *parse_field (char *cptr, uint32 max, uint32 *val, uint32 c); char *parse_fpp_xr (char *cptr, uint32 *xr, t_bool inc); int32 test_fpp_addr (uint32 ad, uint32 max); /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_consoles array of pointers to consoles (if more than one) sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "PDP-8"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 4; DEVICE *sim_devices[] = { &cpu_dev, &tsc_dev, &clk_dev, &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, &ttix_dev, &ttox_dev, &lpt_dev, &rk_dev, &rl_dev, &rx_dev, &df_dev, &rf_dev, &dt_dev, &td_dev, &mt_dev, &ct_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unimplemented instruction", "HALT instruction", "Breakpoint", "Non-standard device number", "DECtape off reel", "Infinite loop" }; /* Ambiguous device list - these devices have overlapped IOT codes */ DEVICE *amb_dev[] = { &rl_dev, &ct_dev, &td_dev, NULL }; #define AMB_RL (1 << 12) #define AMB_CT (2 << 12) #define AMB_TD (3 << 12) /* RIM loader format consists of alternating pairs of addresses and 12-bit words. It can only operate in field 0 and is not checksummed. */ t_stat sim_load_rim (FILE *fi) { int32 origin, hi, lo, wd; origin = 0200; do { /* skip leader */ if ((hi = getc (fi)) == EOF) return SCPE_FMT; } while ((hi == 0) || (hi >= 0200)); do { /* data block */ if ((lo = getc (fi)) == EOF) return SCPE_FMT; wd = (hi << 6) | lo; if (wd > 07777) origin = wd & 07777; else M[origin++ & 07777] = wd; if ((hi = getc (fi)) == EOF) return SCPE_FMT; } while (hi < 0200); /* until trailer */ return SCPE_OK; } /* BIN loader format consists of a string of 12-bit words (made up from 7-bit characters) between leader and trailer (200). The last word on tape is the checksum. A word with the "link" bit set is a new origin; a character > 0200 indicates a change of field. */ int32 sim_bin_getc (FILE *fi, uint32 *newf) { int32 c, rubout; rubout = 0; /* clear toggle */ while ((c = getc (fi)) != EOF) { /* read char */ if (rubout) /* toggle set? */ rubout = 0; /* clr, skip */ else if (c == 0377) /* rubout? */ rubout = 1; /* set, skip */ else if (c > 0200) /* channel 8 set? */ *newf = (c & 070) << 9; /* change field */ else return c; /* otherwise ok */ } return EOF; } t_stat sim_load_bin (FILE *fi) { int32 hi, lo, wd, csum, t; uint32 field, newf, origin; do { /* skip leader */ if ((hi = sim_bin_getc (fi, &newf)) == EOF) return SCPE_FMT; } while ((hi == 0) || (hi >= 0200)); csum = origin = field = newf = 0; /* init */ for (;;) { /* data blocks */ if ((lo = sim_bin_getc (fi, &newf)) == EOF) /* low char */ return SCPE_FMT; wd = (hi << 6) | lo; /* form word */ t = hi; /* save for csum */ if ((hi = sim_bin_getc (fi, &newf)) == EOF) /* next char */ return SCPE_FMT; if (hi == 0200) { /* end of tape? */ if ((csum - wd) & 07777) /* valid csum? */ return SCPE_CSUM; return SCPE_OK; } csum = csum + t + lo; /* add to csum */ if (wd > 07777) /* chan 7 set? */ origin = wd & 07777; /* new origin */ else { /* no, data */ if ((field | origin) >= MEMSIZE) return SCPE_NXM; M[field | origin] = wd; origin = (origin + 1) & 07777; } field = newf; /* update field */ } return SCPE_IERR; } /* Binary loader Two loader formats are supported: RIM loader (-r) and BIN (-b) loader. */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; if ((sim_switches & SWMASK ('R')) || /* RIM format? */ (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) return sim_load_rim (fileref); else return sim_load_bin (fileref); /* no, BIN */ } /* Symbol tables */ #define I_V_FL 18 /* flag start */ #define I_M_FL 07 /* flag mask */ #define I_V_NPN 0 /* no operand */ #define I_V_FLD 1 /* field change */ #define I_V_MRF 2 /* mem ref */ #define I_V_IOT 3 /* general IOT */ #define I_V_OP1 4 /* operate 1 */ #define I_V_OP2 5 /* operate 2 */ #define I_V_OP3 6 /* operate 3 */ #define I_V_IOA 7 /* ambiguous IOT */ #define I_NPN (I_V_NPN << I_V_FL) #define I_FLD (I_V_FLD << I_V_FL) #define I_MRF (I_V_MRF << I_V_FL) #define I_IOT (I_V_IOT << I_V_FL) #define I_OP1 (I_V_OP1 << I_V_FL) #define I_OP2 (I_V_OP2 << I_V_FL) #define I_OP3 (I_V_OP3 << I_V_FL) #define I_IOA (I_V_IOA << I_V_FL) static const int32 masks[] = { 07777, 07707, 07000, 07000, 07416, 07571, 017457, 077777, }; /* Ambiguous device mnemonics must precede default mnemonics */ static const char *opcode[] = { "SKON", "ION", "IOF", "SRQ", /* std IOTs */ "GTF", "RTF", "SGT", "CAF", "RPE", "RSF", "RRB", "RFC", "RFC RRB", /* reader/punch */ "PCE", "PSF", "PCF", "PPC", "PLS", "KCF", "KSF", "KCC", "KRS", "KIE", "KRB", /* console */ "TLF", "TSF", "TCF", "TPC", "SPI", "TLS", "SBE", "SPL", "CAL", /* power fail */ "CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK", /* clock */ "CINT", "RDF", "RIF", "RIB", /* mem mmgt */ "RMF", "SINT", "CUF", "SUF", "RLDC", "RLSD", "RLMA", "RLCA", /* RL - ambiguous */ "RLCB", "RLSA", "RLWC", "RRER", "RRWC", "RRCA", "RRCB", "RRSA", "RRSI", "RLSE", "KCLR", "KSDR", "KSEN", "KSBF", /* CT - ambiguous */ "KLSA", "KSAF", "KGOA", "KRSB", "SDSS", "SDST", "SDSQ", /* TD - ambiguous */ "SDLC", "SDLD", "SDRC", "SDRD", "ADCL", "ADLM", "ADST", "ADRB", /* A/D */ "ADSK", "ADSE", "ADLE", "ADRS", "DCMA", "DMAR", "DMAW", /* DF/RF */ "DCIM", "DSAC", "DIML", "DIMA", "DCEA", "DEAL", "DEAC", "DFSE", "DFSC", "DISK", "DMAC", "DCXA", "DXAL", "DXAC", "PSKF", "PCLF", "PSKE", /* LPT */ "PSTB", "PSIE", "PCLF PSTB", "PCIE", "LWCR", "CWCR", "LCAR", /* MT */ "CCAR", "LCMR", "LFGR", "LDBR", "RWCR", "CLT", "RCAR", "RMSR", "RCMR", "RFSR", "RDBR", "SKEF", "SKCB", "SKJD", "SKTR", "CLF", "DSKP", "DCLR", "DLAG", /* RK */ "DLCA", "DRST", "DLDC", "DMAN", "LCD", "XDR", "STR", /* RX */ "SER", "SDN", "INTR", "INIT", "DTRA", "DTCA", "DTXA", "DTLA", /* DT */ "DTSF", "DTRB", "DTLB", "ETDS", "ESKP", "ECTF", "ECDF", /* TSC75 */ "ERTB", "ESME", "ERIOT", "ETEN", "FFST", "FPINT", "FPICL", "FPCOM", /* FPP8 */ "FPHLT", "FPST", "FPRST", "FPIST", "FMODE", "FMRB", "FMRP", "FMDO", "FPEP", "CDF", "CIF", "CIF CDF", "AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT", "NOP", "NOP2", "NOP3", "SWAB", "SWBA", "STL", "GLK", "STA", "LAS", "CIA", "BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR", "SKP", "SNL", "SZL", "SZA", "SNA", "SZA SNL", "SNA SZL", "SMA", "SPA", "SMA SNL", "SPA SZL", "SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL", "SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", "SCA", "SCA SCL", "SCA MUY", "SCA DVI", "SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR", "ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", "SCA", "DAD", "DST", "SWBA", "DPSZ", "DPIC", "DCIM", "SAM", "CLA", "CLL", "CMA", "CML", "IAC", /* encode only */ "CLA", "OAS", "HLT", "CLA", "MQA", "MQL", NULL, NULL, NULL, NULL, /* decode only */ NULL }; static const int32 opc_val[] = { 06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN, 06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN, 06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN, 06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN, 06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN, 06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN, 06101+I_NPN, 06102+I_NPN, 06103+I_NPN, 06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN, 06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN, 06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN, 06600+I_IOA+AMB_RL, 06601+I_IOA+AMB_RL, 06602+I_IOA+AMB_RL, 06603+I_IOA+AMB_RL, 06604+I_IOA+AMB_RL, 06605+I_IOA+AMB_RL, 06607+I_IOA+AMB_RL, 06610+I_IOA+AMB_RL, 06611+I_IOA+AMB_RL, 06612+I_IOA+AMB_RL, 06613+I_IOA+AMB_RL, 06614+I_IOA+AMB_RL, 06615+I_IOA+AMB_RL, 06617+I_IOA+AMB_RL, 06700+I_IOA+AMB_CT, 06701+I_IOA+AMB_CT, 06702+I_IOA+AMB_CT, 06703+I_IOA+AMB_CT, 06704+I_IOA+AMB_CT, 06705+I_IOA+AMB_CT, 06706+I_IOA+AMB_CT, 06707+I_IOA+AMB_CT, 06771+I_IOA+AMB_TD, 06772+I_IOA+AMB_TD, 06773+I_IOA+AMB_TD, 06774+I_IOA+AMB_TD, 06775+I_IOA+AMB_TD, 06776+I_IOA+AMB_TD, 06777+I_IOA+AMB_TD, 06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN, /* AD */ 06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN, 06601+I_NPN, 06603+I_NPN, 06605+I_NPN, /* DF/RF */ 06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN, 06611+I_NPN, 06615+I_NPN, 06616+I_NPN, 06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN, 06641+I_NPN, 06643+I_NPN, 06645+I_NPN, 06661+I_NPN, 06662+I_NPN, 06663+I_NPN, /* LPT */ 06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN, 06701+I_NPN, 06702+I_NPN, 06703+I_NPN, /* MT */ 06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN, 06711+I_NPN, 06712+I_NPN, 06713+I_NPN, 06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN, 06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN, 06741+I_NPN, 06742+I_NPN, 06743+I_NPN, /* RK */ 06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN, 06751+I_NPN, 06752+I_NPN, 06753+I_NPN, /* RX */ 06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN, 06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN, /* DT */ 06771+I_NPN, 06772+I_NPN, 06774+I_NPN, 06360+I_NPN, 06361+I_NPN, 06362+I_NPN, 06363+I_NPN, /* TSC */ 06364+I_NPN, 06365+I_NPN, 06366+I_NPN, 06367+I_NPN, 06550+I_NPN, 06551+I_NPN, 06552+I_NPN, 06553+I_NPN, /* FPP8 */ 06554+I_NPN, 06555+I_NPN, 06556+I_NPN, 06557+I_NPN, 06561+I_NPN, 06563+I_NPN, 06564+I_NPN, 06565+I_NPN, 06567+I_NPN, 06201+I_FLD, 06202+I_FLD, 06203+I_FLD, 00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF, 04000+I_MRF, 05000+I_MRF, 06000+I_IOT, 07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN, 07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN, 07002+I_OP1, 07004+I_OP1, 07006+I_OP1, 07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1, 07410+I_OP2, 07420+I_OP2, 07430+I_OP2, 07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2, 07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2, 07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2, 07403+I_OP3, 07405+I_OP3, 07407+I_OP3, 07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3, 07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3, 07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3, 017403+I_OP3, 017405+I_OP3, 0174017+I_OP3, 017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3, 017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3, 017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3, 07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1, 07600+I_OP2, 07404+I_OP2, 07402+I_OP2, 07601+I_OP3, 07501+I_OP3, 07421+I_OP3, 07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3, -1 }; /* Symbol tables for FPP-8 */ #define F_V_FL 18 /* flag start */ #define F_M_FL 017 /* flag mask */ #define F_V_NOP12 0 /* no opnd 12b */ #define F_V_NOP9 1 /* no opnd 9b */ #define F_V_AD15 2 /* 15b dir addr */ #define F_V_AD15X 3 /* 15b dir addr indx */ #define F_V_IMMX 4 /* 12b immm indx */ #define F_V_X 5 /* index */ #define F_V_MRI 6 /* mem ref ind */ #define F_V_MR1D 7 /* mem ref dir 1 word */ #define F_V_MR2D 8 /* mem ref dir 2 word */ #define F_V_LEMU 9 /* LEA/IMUL */ #define F_V_LEMUI 10 /* LEAI/IMULI */ #define F_V_LTR 11 /* LTR */ #define F_V_MRD 12 /* mem ref direct (enc) */ #define F_NOP12 (F_V_NOP12 << F_V_FL) #define F_NOP9 (F_V_NOP9 << F_V_FL) #define F_AD15 (F_V_AD15 << F_V_FL) #define F_AD15X (F_V_AD15X << F_V_FL) #define F_IMMX (F_V_IMMX << F_V_FL) #define F_X (F_V_X << F_V_FL) #define F_MRI (F_V_MRI << F_V_FL) #define F_MR1D (F_V_MR1D << F_V_FL) #define F_MR2D (F_V_MR2D << F_V_FL) #define F_LEMU (F_V_LEMU << F_V_FL) #define F_LEMUI (F_V_LEMUI << F_V_FL) #define F_LTR (F_V_LTR << F_V_FL) #define F_MRD (F_V_MRD << F_V_FL) static const uint32 fmasks[] = { 07777, 07770, 07770, 07600, 07770, 07770, 07600, 07600, 07600, 017600, 017600, 07670, 07777 }; /* Memory references are encode dir / decode 1D / decode 2D / indirect */ static const char *fopcode[] = { "FEXIT", "FPAUSE", "FCLA", "FNEG", "FNORM", "STARTF", "STARTD", "JAC", "ALN", "ATX", "XTA", "FNOP", "STARTE", "LDX", "ADDX", "FLDA", "FLDA", "FLDA", "FLDAI", "JEQ", "JGE", "JLE", "JA", "JNE", "JLT", "JGT", "JAL", "SETX", "SETB", "JSA", "JSR", "FADD", "FADD", "FADD", "FADDI", "JNX", "FSUB", "FSUB", "FSUB", "FSUBI", "TRAP3", "FDIV", "FDIV", "FDIV", "FDIVI", "TRAP4", "FMUL", "FMUL", "FMUL", "FMULI", "LTREQ", "LTRGE", "LTRLE", "LTRA", "LTRNE", "LTRLT", "LTRGT", "LTRAL", "FADDM", "FADDM", "FADDM", "FADDMI", "IMUL", "LEA", "FSTA", "FSTA", "FSTA", "FSTAI", "IMULI", "LEAI", "FMULM", "FMULM", "FMULM", "FMULMI", NULL }; static const int32 fop_val[] = { 00000+F_NOP12, 00001+F_NOP12, 00002+F_NOP12, 00003+F_NOP12, 00004+F_NOP12, 00005+F_NOP12, 00006+F_NOP12, 00007+F_NOP12, 00010+F_X, 00020+F_X, 00030+F_X, 00040+F_NOP9, 00050+F_NOP9, 00100+F_IMMX, 00110+F_IMMX, 00000+F_MRD, 00200+F_MR1D, 00400+F_MR2D, 00600+F_MRI, 01000+F_AD15, 01010+F_AD15, 01020+F_AD15, 01030+F_AD15, 01040+F_AD15, 01050+F_AD15, 01060+F_AD15, 01070+F_AD15, 01100+F_AD15, 01110+F_AD15, 01120+F_AD15, 01130+F_AD15, 01000+F_MRD, 01200+F_MR1D, 01400+F_MR2D, 01600+F_MRI, 02000+F_AD15X, 02000+F_MRD, 02200+F_MR1D, 02400+F_MR2D, 02600+F_MRI, 03000+F_AD15, 03000+F_MRD, 03200+F_MR1D, 03400+F_MR2D, 03600+F_MRI, 04000+F_AD15, 04000+F_MRD, 04200+F_MR1D, 04400+F_MR2D, 04600+F_MRI, 05000+F_LTR, 05010+F_LTR, 05020+F_LTR, 05030+F_LTR, 05040+F_LTR, 05050+F_LTR, 05060+F_LTR, 05070+F_LTR, 05000+F_MRD, 05200+F_MR1D, 05400+F_MR2D, 05600+F_MRI, 016000+F_LEMU, 006000+F_LEMU, 06000+F_MRD, 06200+F_MR1D, 06400+F_MR2D, 06600+F_MRI, 017000+F_LEMUI, 007000+F_LEMUI, 07000+F_MRD, 07200+F_MR1D, 07400+F_MR2D, 07600+F_MRI, -1 }; /* Operate decode Inputs: *of = output stream inst = mask bits class = instruction class code sp = space needed? Outputs: status = space needed */ int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) { int32 i, j; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((j == class) && (opc_val[i] & inst)) { /* same class? */ inst = inst & ~opc_val[i]; /* mask bit set? */ fprintf (of, (sp? " %s": "%s"), opcode[i]); sp = 1; } } return sp; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to data *uptr = pointer to unit sw = switches Outputs: return = status code */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) #define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100) #define TSSTOASC(x) ((x) + 040) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, sp, inst, disp, opc; extern int32 emode; t_stat r; cflag = (uptr == NULL) || (uptr == &cpu_unit); inst = val[0]; if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (sw & SWMASK ('C')) { /* characters? */ fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); fprintf (of, "%c", SIXTOASC (inst & 077)); return SCPE_OK; } if (sw & SWMASK ('T')) { /* TSS8 packed? */ fprintf (of, "%c", TSSTOASC ((inst >> 6) & 077)); fprintf (of, "%c", TSSTOASC (inst & 077)); return SCPE_OK; } if ((sw & SWMASK ('F')) && /* FPP8? */ ((r = fprint_sym_fpp (of, val)) != SCPE_ARG)) return r; if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ opc = (inst >> 9) & 07; /* get major opcode */ if (opc == 07) /* operate? */ inst = inst | ((emode & 1) << 12); /* include EAE mode */ if (opc == 06) { /* IOT? */ DEVICE *dptr; DIB *dibp; uint32 dno = (inst >> 3) & 077; for (i = 0; (dptr = amb_dev[i]) != NULL; i++) { /* check amb devices */ if ((dptr->ctxt == NULL) || /* no DIB or */ (dptr->flags & DEV_DIS)) continue; /* disabled? skip */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((dno >= dibp->dev) || /* IOT for this dev? */ (dno < (dibp->dev + dibp->num))) { inst = inst | ((i + 1) << 12); /* disambiguate */ break; /* done */ } } } for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & 077777) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: case I_V_IOA: /* no operands */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_FLD: /* field change */ fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07); break; case I_V_MRF: /* mem ref */ disp = inst & 0177; /* displacement */ fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " ")); if (inst & 0200) { /* current page? */ if (cflag) fprintf (of, "%-o", (addr & 07600) | disp); else fprintf (of, "C %-o", disp); } else fprintf (of, "%-o", disp); /* page zero */ break; case I_V_IOT: /* IOT */ fprintf (of, "%s %-o", opcode[i], inst & 0777); break; case I_V_OP1: /* operate group 1 */ sp = fprint_opr (of, inst & 0361, j, 0); if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); break; case I_V_OP2: /* operate group 2 */ if (opcode[i]) fprintf (of, "%s", opcode[i]); /* skips */ fprint_opr (of, inst & 0206, j, opcode[i] != NULL); break; case I_V_OP3: /* operate group 3 */ sp = fprint_opr (of, inst & 0320, j, 0); if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); break; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* 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) { uint32 cflag, d, i, j, k; t_stat r; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0] | 0200; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) cptr[0] & 077) << 6) | ((t_value) cptr[1] & 077); return SCPE_OK; } if ((sw & SWMASK ('T')) || ((*cptr == '"') && cptr++)) { /* TSS8 string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) (cptr[0] - 040) & 077) << 6) | ((t_value) (cptr[1] - 040) & 077); return SCPE_OK; } if ((r = parse_sym_fpp (cptr, val)) != SCPE_ARG) /* FPP8 inst? */ return r; /* Instruction parse */ 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] & 07777; /* get value */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ switch (j) { /* case on class */ case I_V_IOT: /* IOT */ if ((cptr = parse_field (cptr, 0777, &d, 0)) == NULL) return SCPE_ARG; /* get dev+pulse */ val[0] = val[0] | d; break; case I_V_FLD: /* field */ 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] >> I_V_FL) & I_M_FL; if (k != j) return SCPE_ARG; val[0] = val[0] | (opc_val[i] & 07777); } else { d = get_uint (gbuf, 8, 07, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | (d << 3); break; } } break; case I_V_MRF: /* mem ref */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if (strcmp (gbuf, "I") == 0) { /* indirect? */ val[0] = val[0] | 0400; cptr = get_glyph (cptr, gbuf, 0); } if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) { if ((cptr = parse_field (cptr, 0177, &d, 0)) == NULL) return SCPE_ARG; val[0] = val[0] | d | (k? 0200: 0); } else { d = get_uint (gbuf, 8, 07777, &r); if (r != SCPE_OK) return SCPE_ARG; if (d <= 0177) val[0] = val[0] | d; else if (cflag && (((addr ^ d) & 07600) == 0)) val[0] = val[0] | (d & 0177) | 0200; else return SCPE_ARG; } break; case I_V_OP1: case I_V_OP2: case I_V_OP3: /* operates */ case I_V_NPN: case I_V_IOA: 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++) ; k = opc_val[i] & 07777; if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0)) return SCPE_ARG; val[0] = val[0] | k; } break; } /* end case */ if (*cptr != 0) return SCPE_ARG; /* junk at end? */ return SCPE_OK; } /* FPP8 instruction decode */ t_stat fprint_sym_fpp (FILE *of, t_value *val) { uint32 wd1, wd2, xr4b, xr3b, ad15; uint32 i, j; extern uint32 fpp_bra, fpp_cmd; wd1 = (uint32) val[0] | ((fpp_cmd & 04000) << 1); wd2 = (uint32) val[1]; xr4b = (wd1 >> 3) & 017; xr3b = wd1 & 07; ad15 = (xr3b << 12) | wd2; for (i = 0; fop_val[i] >= 0; i++) { /* loop thru ops */ j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ if ((fop_val[i] & 017777) == (wd1 & fmasks[j])) { /* match? */ switch (j) { /* case on class */ case F_V_NOP12: case F_V_NOP9: case F_V_LTR: /* no operands */ fprintf (of, "%s", fopcode[i]); break; case F_V_X: /* index */ fprintf (of, "%s %o", fopcode[i], xr3b); break; case F_V_IMMX: /* index imm */ fprintf (of, "%s %-o,%o", fopcode[i], wd2, xr3b); return -1; /* extra word */ case F_V_AD15: /* 15b address */ fprintf (of, "%s %-o", fopcode[i], ad15); return -1; /* extra word */ case F_V_AD15X: /* 15b addr, indx */ fprintf (of, "%s %-o", fopcode[i], ad15); if (xr4b >= 010) fprintf (of, ",%o+", xr4b & 7); else fprintf (of, ",%o", xr4b); return -1; /* extra word */ case F_V_MR1D: /* 1 word direct */ ad15 = (fpp_bra + (3 * (wd1 & 0177))) & ADDRMASK; fprintf (of, "%s %-o", fopcode[i], ad15); break; case F_V_LEMU: case F_V_MR2D: /* 2 word direct */ fprintf (of, "%s %-o", fopcode[i], ad15); if (xr4b >= 010) fprintf (of, ",%o+", xr4b & 7); else if (xr4b != 0) fprintf (of, ",%o", xr4b); return -1; /* extra word */ case F_V_LEMUI: case F_V_MRI: /* indirect */ ad15 = (fpp_bra + (3 * xr3b)) & ADDRMASK; fprintf (of, "%s %-o", fopcode[i], ad15); if (xr4b >= 010) fprintf (of, ",%o+", xr4b & 7); else if (xr4b != 0) fprintf (of, ",%o", xr4b); break; case F_V_MRD: /* encode only */ return SCPE_IERR; } return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* FPP8 instruction parse */ t_stat parse_sym_fpp (char *cptr, t_value *val) { uint32 i, j, ad, xr; int32 broff, nwd; char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ for (i = 0; (fopcode[i] != NULL) && (strcmp (fopcode[i], gbuf) != 0) ; i++) ; if (fopcode[i] == NULL) return SCPE_ARG; val[0] = fop_val[i] & 07777; /* get value */ j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ xr = 0; nwd = 0; switch (j) { /* case on class */ case F_V_NOP12: case F_V_NOP9: case F_V_LTR: /* no operands */ break; case F_V_X: /* 3b XR */ if ((cptr = parse_field (cptr, 07, &xr, 0)) == NULL) return SCPE_ARG; val[0] |= xr; break; case F_V_IMMX: /* 12b, XR */ if ((cptr = parse_field (cptr, 07777, &ad, ',')) == NULL) return SCPE_ARG; if ((*cptr == 0) || ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) return SCPE_ARG; val[0] |= xr; val[++nwd] = ad; break; case F_V_AD15: /* 15b addr */ if ((cptr = parse_field (cptr, 077777, &ad, 0)) == NULL) return SCPE_ARG; val[0] |= (ad >> 12) & 07; val[++nwd] = ad & 07777; break; case F_V_AD15X: /* 15b addr, idx */ if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) return SCPE_ARG; if ((*cptr == 0) || ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) return SCPE_ARG; val[0] |= ((xr << 3) | ((ad >> 12) & 07)); val[++nwd] = ad & 07777; break; case F_V_LEMUI: case F_V_MRI: /* indirect */ if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) return SCPE_ARG; if ((*cptr != 0) && ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) return SCPE_ARG; if ((broff = test_fpp_addr (ad, 07)) < 0) return SCPE_ARG; val[0] |= ((xr << 3) | broff); break; case F_V_MRD: /* direct */ if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) return SCPE_ARG; if (((broff = test_fpp_addr (ad, 0177)) < 0) || (*cptr != 0)) { if ((*cptr != 0) && ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) return SCPE_ARG; val[0] |= (00400 | (xr << 3) | ((ad >> 12) & 07)); val[++nwd] = ad & 07777; } else val[0] |= (00200 | broff); break; case F_V_LEMU: if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) return SCPE_ARG; if ((*cptr != 0) && ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) return SCPE_ARG; val[0] |= ((xr << 3) | ((ad >> 12) & 07)); val[++nwd] = ad & 07777; break; case F_V_MR1D: case F_V_MR2D: return SCPE_IERR; } /* end case */ if (*cptr != 0) return SCPE_ARG; /* junk at end? */ return -nwd; } /* Parse field */ char *parse_field (char *cptr, uint32 max, uint32 *val, uint32 c) { char gbuf[CBUFSIZE]; t_stat r; cptr = get_glyph (cptr, gbuf, c); /* get field */ *val = get_uint (gbuf, 8, max, &r); if (r != SCPE_OK) return NULL; return cptr; } /* Parse index register */ char *parse_fpp_xr (char *cptr, uint32 *xr, t_bool inc) { char gbuf[CBUFSIZE]; uint32 len; t_stat r; cptr = get_glyph (cptr, gbuf, 0); /* get field */ len = strlen (gbuf); if (gbuf[len - 1] == '+') { if (!inc) return NULL; gbuf[len - 1] = 0; *xr = 010; } else *xr = 0; *xr += get_uint (gbuf, 8, 7, &r); if (r != SCPE_OK) return NULL; return cptr; } /* Test address in range of base register */ int32 test_fpp_addr (uint32 ad, uint32 max) { uint32 off; extern uint32 fpp_bra; off = ad - fpp_bra; if (((off % 3) != 0) || (off > (max * 3))) return -1; return ((int32) off / 3); } simh-3.8.1/PDP8/pdp8_mt.c0000644000175000017500000006556511107355106013113 0ustar vlmvlm/* pdp8_mt.c: PDP-8 magnetic tape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt TM8E/TU10 magtape 16-Feb-06 RMS Added tape capacity checking 16-Aug-05 RMS Fixed C++ declaration and cast problems 18-Mar-05 RMS Added attached test to detach routine 25-Apr-03 RMS Revised for extended file support 29-Mar-03 RMS Added multiformat support 04-Mar-03 RMS Fixed bug in SKTR 01-Mar-03 RMS Fixed interrupt handling Revised for magtape library 30-Oct-02 RMS Revised BOT handling, added error record handling 04-Oct-02 RMS Added DIBs, device number support 30-Aug-02 RMS Revamped error handling 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length test 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed UST, POS, FLG to arrays 25-Apr-01 RMS Added device enable/disable support 04-Oct-98 RMS V2.4 magtape format 22-Jan-97 RMS V2.3 magtape format 01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. */ #include "pdp8_defs.h" #include "sim_tape.h" #define MT_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ #define MT_MAXFR (1 << 16) /* max record lnt */ #define WC_SIZE (1 << 12) /* max word count */ #define WC_MASK (WC_SIZE - 1) /* Command/unit - mt_cu */ #define CU_V_UNIT 9 /* unit */ #define CU_M_UNIT 07 #define CU_PARITY 00400 /* parity select */ #define CU_IEE 00200 /* error int enable */ #define CU_IED 00100 /* done int enable */ #define CU_V_EMA 3 /* ext mem address */ #define CU_M_EMA 07 #define CU_EMA (CU_M_EMA << CU_V_EMA) #define CU_DTY 00002 /* drive type */ #define CU_UNPAK 00001 /* 6b vs 8b mode */ #define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) #define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA)) /* Function - mt_fn */ #define FN_V_FNC 9 /* function */ #define FN_M_FNC 07 #define FN_UNLOAD 00 #define FN_REWIND 01 #define FN_READ 02 #define FN_CMPARE 03 #define FN_WRITE 04 #define FN_WREOF 05 #define FN_SPACEF 06 #define FN_SPACER 07 #define FN_ERASE 00400 /* erase */ #define FN_CRC 00200 /* read CRC */ #define FN_GO 00100 /* go */ #define FN_INC 00040 /* incr mode */ #define FN_RMASK 07700 /* readable bits */ #define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC) /* Status - stored in mt_sta or (*) uptr->USTAT */ #define STA_ERR (04000 << 12) /* error */ #define STA_REW (02000 << 12) /* *rewinding */ #define STA_BOT (01000 << 12) /* *start of tape */ #define STA_REM (00400 << 12) /* *offline */ #define STA_PAR (00200 << 12) /* parity error */ #define STA_EOF (00100 << 12) /* *end of file */ #define STA_RLE (00040 << 12) /* rec lnt error */ #define STA_DLT (00020 << 12) /* data late */ #define STA_EOT (00010 << 12) /* *end of tape */ #define STA_WLK (00004 << 12) /* *write locked */ #define STA_CPE (00002 << 12) /* compare error */ #define STA_ILL (00001 << 12) /* illegal */ #define STA_9TK 00040 /* 9 track */ /* #define STA_BAD 00020 /* bad tape?? */ #define STA_INC 00010 /* increment error */ #define STA_LAT 00004 /* lateral par error */ #define STA_CRC 00002 /* CRC error */ #define STA_LON 00001 /* long par error */ #define STA_CLR (FN_RMASK | 00020) /* always clear */ #define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \ STA_EOT | STA_WLK) /* kept in USTAT */ extern uint16 M[]; extern int32 int_req, stop_inst; extern UNIT cpu_unit; int32 mt_cu = 0; /* command/unit */ int32 mt_fn = 0; /* function */ int32 mt_ca = 0; /* current address */ int32 mt_wc = 0; /* word count */ int32 mt_sta = 0; /* status register */ int32 mt_db = 0; /* data buffer */ int32 mt_done = 0; /* mag tape flag */ int32 mt_time = 10; /* record latency */ int32 mt_stopioe = 1; /* stop on error */ uint8 *mtxb = NULL; /* transfer buffer */ DEVICE mt_dev; int32 mt70 (int32 IR, int32 AC); int32 mt71 (int32 IR, int32 AC); int32 mt72 (int32 IR, int32 AC); t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_detach (UNIT *uptr); int32 mt_updcsta (UNIT *uptr); int32 mt_ixma (int32 xma); t_stat mt_map_err (UNIT *uptr, t_stat st); t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); UNIT *mt_busy (void); void mt_set_done (void); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ DIB mt_dib = { DEV_MT, 3, { &mt70, &mt71, &mt72 } }; UNIT mt_unit[] = { { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } }; REG mt_reg[] = { { ORDATA (CMD, mt_cu, 12) }, { ORDATA (FNC, mt_fn, 12) }, { ORDATA (CA, mt_ca, 12) }, { ORDATA (WC, mt_wc, 12) }, { ORDATA (DB, mt_db, 12) }, { GRDATA (STA, mt_sta, 8, 12, 12) }, { ORDATA (STA2, mt_sta, 6) }, { FLDATA (DONE, mt_done, 0) }, { FLDATA (INT, int_req, INT_V_MT) }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { DRDATA (TIME, mt_time, 24), PV_LEFT }, { URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) }, { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT | REG_RO) }, { FLDATA (DEVNUM, mt_dib.dev, 6), REG_HRO }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mt_vlock }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mt_vlock }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach, &mt_dib, DEV_DISABLE }; /* IOT routines */ int32 mt70 (int32 IR, int32 AC) { int32 f; UNIT *uptr; uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ switch (IR & 07) { /* decode IR<9:11> */ case 1: /* LWCR */ mt_wc = AC; /* load word count */ return 0; case 2: /* CWCR */ mt_wc = 0; /* clear word count */ return AC; case 3: /* LCAR */ mt_ca = AC; /* load mem address */ return 0; case 4: /* CCAR */ mt_ca = 0; /* clear mem address */ return AC; case 5: /* LCMR */ if (mt_busy ()) /* busy? illegal op */ mt_sta = mt_sta | STA_ILL | STA_ERR; mt_cu = AC; /* load command reg */ mt_updcsta (mt_dev.units + GET_UNIT (mt_cu)); return 0; case 6: /* LFGR */ if (mt_busy ()) /* busy? illegal op */ mt_sta = mt_sta | STA_ILL | STA_ERR; mt_fn = AC; /* load function */ if ((mt_fn & FN_GO) == 0) { /* go set? */ mt_updcsta (uptr); /* update status */ return 0; } f = GET_FNC (mt_fn); /* get function */ if (((uptr->flags & UNIT_ATT) == 0) || sim_is_active (uptr) || (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) || (((f == FN_SPACER) || (f == FN_REWIND)) && sim_tape_bot (uptr))) { mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal op error */ mt_set_done (); /* set done */ mt_updcsta (uptr); /* update status */ return 0; } uptr->USTAT = uptr->USTAT & STA_WLK; /* clear status */ if (f == FN_UNLOAD) { /* unload? */ detach_unit (uptr); /* set offline */ uptr->USTAT = STA_REW | STA_REM; /* rewinding, off */ mt_set_done (); /* set done */ } else if (f == FN_REWIND) { /* rewind */ uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */ mt_set_done (); /* set done */ } else mt_done = 0; /* clear done */ mt_updcsta (uptr); /* update status */ sim_activate (uptr, mt_time); /* start io */ return 0; case 7: /* LDBR */ if (mt_busy ()) /* busy? illegal op */ mt_sta = mt_sta | STA_ILL | STA_ERR; mt_db = AC; /* load buffer */ mt_set_done (); /* set done */ mt_updcsta (uptr); /* update status */ return 0; } /* end switch */ return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } int32 mt71 (int32 IR, int32 AC) { UNIT *uptr; uptr = mt_dev.units + GET_UNIT (mt_cu); switch (IR & 07) { /* decode IR<9:11> */ case 1: /* RWCR */ return mt_wc; /* read word count */ case 2: /* CLT */ mt_reset (&mt_dev); /* reset everything */ return AC; case 3: /* RCAR */ return mt_ca; /* read mem address */ case 4: /* RMSR */ return ((mt_updcsta (uptr) >> 12) & 07777); /* read status */ case 5: /* RCMR */ return mt_cu; /* read command */ case 6: /* RFSR */ return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK)) & 07777); /* read function */ case 7: /* RDBR */ return mt_db; /* read data buffer */ } return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } int32 mt72 (int32 IR, int32 AC) { UNIT *uptr; uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ switch (IR & 07) { /* decode IR<9:11> */ case 1: /* SKEF */ return (mt_sta & STA_ERR)? IOT_SKP + AC: AC; case 2: /* SKCB */ return (!mt_busy ())? IOT_SKP + AC: AC; case 3: /* SKJD */ return mt_done? IOT_SKP + AC: AC; case 4: /* SKTR */ return (!sim_is_active (uptr) && (uptr->flags & UNIT_ATT))? IOT_SKP + AC: AC; case 5: /* CLF */ if (!sim_is_active (uptr)) mt_reset (&mt_dev); /* if TUR, zap */ else { /* just ctrl zap */ mt_sta = 0; /* clear status */ mt_done = 0; /* clear done */ mt_updcsta (uptr); /* update status */ } return AC; } /* end switch */ return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt */ t_stat mt_svc (UNIT *uptr) { int32 f, i, p, u, wc, xma; t_mtrlnt tbc, cbc; t_bool passed_eot; uint16 c, c1, c2; t_stat st, r = SCPE_OK; u = (int32) (uptr - mt_dev.units); /* get unit number */ f = GET_FNC (mt_fn); /* get command */ xma = GET_EMA (mt_cu) + mt_ca; /* get mem addr */ wc = WC_SIZE - mt_wc; /* get wc */ if (uptr->USTAT & STA_REW) { /* rewind? */ sim_tape_rewind (uptr); /* update position */ if (uptr->flags & UNIT_ATT) /* still on line? */ uptr->USTAT = (uptr->USTAT & STA_WLK) | STA_BOT; else uptr->USTAT = STA_REM; if (u == GET_UNIT (mt_cu)) { /* selected? */ mt_set_done (); /* set done */ mt_updcsta (uptr); /* update status */ } return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ uptr->USTAT = STA_REM; /* unit off line */ mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ mt_set_done (); /* set done */ mt_updcsta (uptr); /* update status */ return IORETURN (mt_stopioe, SCPE_UNATT); } passed_eot = sim_tape_eot (uptr); /* passed eot? */ switch (f) { /* case on function */ case FN_READ: /* read */ case FN_CMPARE: /* read/compare */ st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ if (st == MTSE_RECE) /* rec in err? */ mt_sta = mt_sta | STA_PAR | STA_ERR; else if (st != MTSE_OK) { /* other error? */ r = mt_map_err (uptr, st); /* map error */ mt_sta = mt_sta | STA_RLE | STA_ERR; /* err, eof/eom, tmk */ break; } cbc = (mt_cu & CU_UNPAK)? wc: wc * 2; /* expected bc */ if (tbc != cbc) /* wrong size? */ mt_sta = mt_sta | STA_RLE | STA_ERR; if (tbc < cbc) { /* record small? */ cbc = tbc; /* use smaller */ wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2; } for (i = p = 0; i < wc; i++) { /* copy buffer */ xma = mt_ixma (xma); /* increment xma */ mt_wc = (mt_wc + 1) & 07777; /* incr word cnt */ if (mt_cu & CU_UNPAK) c = mtxb[p++]; else { c1 = mtxb[p++] & 077; c2 = mtxb[p++] & 077; c = (c1 << 6) | c2; } if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; else if ((f == FN_CMPARE) && (M[xma] != c)) { mt_sta = mt_sta | STA_CPE | STA_ERR; break; } } break; case FN_WRITE: /* write */ tbc = (mt_cu & CU_UNPAK)? wc: wc * 2; for (i = p = 0; i < wc; i++) { /* copy buf to tape */ xma = mt_ixma (xma); /* incr mem addr */ if (mt_cu & CU_UNPAK) mtxb[p++] = M[xma] & 0377; else { mtxb[p++] = (M[xma] >> 6) & 077; mtxb[p++] = M[xma] & 077; } } if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) { /* write rec, err? */ r = mt_map_err (uptr, st); /* map error */ xma = GET_EMA (mt_cu) + mt_ca; /* restore xma */ } else mt_wc = 0; /* ok, clear wc */ break; case FN_WREOF: if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mt_map_err (uptr, st); /* map error */ break; case FN_SPACEF: /* space forward */ do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* stop */ } } while ((mt_wc != 0) && (passed_eot || !sim_tape_eot (uptr))); break; case FN_SPACER: /* space reverse */ do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* stop */ } } while (mt_wc != 0); break; } /* end case */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ uptr->USTAT = uptr->USTAT | STA_EOT; mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA); mt_ca = xma & 07777; /* update mem addr */ mt_set_done (); /* set done */ mt_updcsta (uptr); /* update status */ return r; } /* Update controller status */ int32 mt_updcsta (UNIT *uptr) { mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN); if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) || (mt_done && (mt_cu & CU_IED))) int_req = int_req | INT_MT; else int_req = int_req & ~INT_MT; return mt_sta; } /* Test if controller busy */ UNIT *mt_busy (void) { int32 u; UNIT *uptr; for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ uptr = mt_dev.units + u; if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0)) return uptr; } return NULL; } /* Increment extended memory address */ int32 mt_ixma (int32 xma) /* incr extended ma */ { int32 v; v = ((xma + 1) & 07777) | (xma & 070000); /* wrapped incr */ if (mt_fn & FN_INC) { /* increment mode? */ if (xma == 077777) /* at limit? error */ mt_sta = mt_sta | STA_INC | STA_ERR; else v = xma + 1; /* else 15b incr */ } return v; } /* Set done */ void mt_set_done (void) { mt_done = 1; /* set done */ mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC); /* clear func<4:6> */ return; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* unattached */ mt_sta = mt_sta | STA_ILL | STA_ERR; case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_TMK: /* end of file */ uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */ mt_sta = mt_sta | STA_ERR; break; case MTSE_IOERR: /* IO error */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ if (mt_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ break; case MTSE_BOT: /* reverse into BOT */ uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ mt_sta = mt_sta | STA_ERR; break; case MTSE_WRP: /* write protect */ mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ break; } return SCPE_OK; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { int32 u; UNIT *uptr; mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0; int_req = int_req & ~INT_MT; /* clear interrupt */ for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ uptr = mt_dev.units + u; sim_cancel (uptr); /* cancel activity */ sim_tape_reset (uptr); /* reset tape */ if (uptr->flags & UNIT_ATT) uptr->USTAT = (sim_tape_bot (uptr)? STA_BOT: 0) | (sim_tape_wrp (uptr)? STA_WLK: 0); else uptr->USTAT = STA_REM; } if (mtxb == NULL) mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); if (mtxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat mt_attach (UNIT *uptr, char *cptr) { t_stat r; int32 u = uptr - mt_dev.units; /* get unit number */ r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->USTAT = STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); return r; } /* Detach routine */ t_stat mt_detach (UNIT* uptr) { int32 u = uptr - mt_dev.units; /* get unit number */ if (!(uptr->flags & UNIT_ATT)) /* check for attached */ return SCPE_OK; if (!sim_is_active (uptr)) uptr->USTAT = STA_REM; if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); return sim_tape_detach (uptr); } /* Write lock/enable routine */ t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 u = uptr - mt_dev.units; /* get unit number */ if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) uptr->USTAT = uptr->USTAT | STA_WLK; else uptr->USTAT = uptr->USTAT & ~STA_WLK; if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); return SCPE_OK; } simh-3.8.1/PDP8/pdp8_tsc.c0000644000175000017500000001305011107355106013242 0ustar vlmvlm/* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75) Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module is based on Bernhard Baehr's PDP-8/E simulator PDP-8/E Simulator Source Code Copyright ) 2001-2003 Bernhard Baehr TSC8iots.c - IOTs for the TSC8-75 Board plugin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA tsc TSC8-75 option board */ #include "pdp8_defs.h" extern int32 int_req; extern int32 SF; extern int32 tsc_ir; /* "ERIOT" */ extern int32 tsc_pc; /* "ERTB" */ extern int32 tsc_cdf; /* "ECDF" */ extern int32 tsc_enb; /* enable */ #define UNIT_V_SN699 (UNIT_V_UF + 0) /* SN 699 or above */ #define UNIT_SN699 (1 << UNIT_V_SN699) DEVICE tsc_dev; int32 tsc (int32 IR, int32 AC); t_stat tsc_reset (DEVICE *dptr); /* TSC data structures tsc_dev TSC device descriptor tsc_unit TSC unit descriptor tsc_reg TSC register list */ DIB tsc_dib = { DEV_TSC, 1, { &tsc } }; UNIT tsc_unit = { UDATA (NULL, UNIT_SN699, 0) }; REG tsc_reg[] = { { ORDATA (IR, tsc_ir, 12) }, { ORDATA (PC, tsc_pc, 12) }, { FLDATA (CDF, tsc_cdf, 0) }, { FLDATA (ENB, tsc_enb, 0) }, { FLDATA (INT, int_req, INT_V_TSC) }, { NULL } }; MTAB tsc_mod[] = { { UNIT_SN699, UNIT_SN699, "ESME", "ESME", NULL }, { UNIT_SN699, 0, "no ESME", "NOESME", NULL }, { 0 } }; DEVICE tsc_dev = { "TSC", &tsc_unit, tsc_reg, tsc_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tsc_reset, NULL, NULL, NULL, &tsc_dib, DEV_DISABLE | DEV_DIS }; /* IOT routine */ int32 tsc (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* ETDS */ tsc_enb = 0; /* disable int req */ int_req = int_req & ~INT_TSC; /* clear flag */ break; case 1: /* ESKP */ return (int_req & INT_TSC)? IOT_SKP + AC: AC; /* skip on int req */ case 2: /* ECTF */ int_req = int_req & ~INT_TSC; /* clear int req */ break; case 3: /* ECDF */ AC = AC | ((tsc_ir >> 3) & 07); /* read "ERIOT"<6:8> */ if (tsc_cdf) /* if cdf, skip */ AC = AC | IOT_SKP; tsc_cdf = 0; break; case 4: /* ERTB */ return tsc_pc; case 5: /* ESME */ if (tsc_unit.flags & UNIT_SN699) { /* enabled? */ if (tsc_cdf && ((tsc_ir & 070) >> 3) == (SF & 07)) { AC = AC | IOT_SKP; tsc_cdf = 0; } } break; case 6: /* ERIOT */ return tsc_ir; case 7: /* ETEN */ tsc_enb = 1; break; } /* end switch */ return AC; } /* Reset routine */ t_stat tsc_reset (DEVICE *dptr) { tsc_ir = 0; tsc_pc = 0; tsc_cdf = 0; tsc_enb = 0; int_req = int_req & ~INT_TSC; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_tt.c0000644000175000017500000002345611107355106013113 0ustar vlmvlm/* pdp8_tt.c: PDP-8 console terminal simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti,tto KL8E terminal input/output 18-Jun-07 RMS Added UNIT_IDLE flag to console input 18-Oct-06 RMS Synced keyboard to clock 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 22-Nov-05 RMS Revised for new terminal processing routines 28-May-04 RMS Removed SET TTI CTRL-C 29-Dec-03 RMS Added console output backpressure support 25-Apr-03 RMS Revised for extended file support 02-Mar-02 RMS Added SET TTI CTRL-C 22-Dec-02 RMS Added break support 01-Nov-02 RMS Added 7B/8B support 04-Oct-02 RMS Added DIBs, device number support 30-May-02 RMS Widened POS to 32b 07-Sep-01 RMS Moved function prototypes */ #include "pdp8_defs.h" #include extern int32 int_req, int_enable, dev_done, stop_inst; extern int32 tmxr_poll; int32 tti (int32 IR, int32 AC); int32 tto (int32 IR, int32 AC); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list tti_mod TTI modifiers list */ DIB tti_dib = { DEV_TTI, 1, { &tti } }; UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), 0 }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 8) }, { FLDATA (DONE, dev_done, INT_V_TTI) }, { FLDATA (ENABLE, int_enable, INT_V_TTI) }, { FLDATA (INT, int_req, INT_V_TTI) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tti_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { DEV_TTO, 1, { &tto } }; UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 8) }, { FLDATA (DONE, dev_done, INT_V_TTO) }, { FLDATA (ENABLE, int_enable, INT_V_TTO) }, { FLDATA (INT, int_req, INT_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* Terminal input: IOT routine */ int32 tti (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* KCF */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; return AC; case 1: /* KSF */ return (dev_done & INT_TTI)? IOT_SKP + AC: AC; case 2: /* KCC */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; return 0; /* clear AC */ case 4: /* KRS */ return (AC | tti_unit.buf); /* return buffer */ case 5: /* KIE */ if (AC & 1) int_enable = int_enable | (INT_TTI+INT_TTO); else int_enable = int_enable & ~(INT_TTI+INT_TTO); int_req = INT_UPDATE; /* update interrupts */ return AC; case 6: /* KRB */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; return (tti_unit.buf); /* return buffer */ default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) /* break? */ uptr->buf = 0; else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); uptr->pos = uptr->pos + 1; dev_done = dev_done | INT_TTI; /* set done */ int_req = INT_UPDATE; /* update interrupts */ return SCPE_OK; } /* Reset routine */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; dev_done = dev_done & ~INT_TTI; /* clear done, int */ int_req = int_req & ~INT_TTI; int_enable = int_enable | INT_TTI; /* set enable */ sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll)); return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* TLF */ dev_done = dev_done | INT_TTO; /* set flag */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* TSF */ return (dev_done & INT_TTO)? IOT_SKP + AC: AC; case 2: /* TCF */ dev_done = dev_done & ~INT_TTO; /* clear flag */ int_req = int_req & ~INT_TTO; /* clear int req */ return AC; case 5: /* SPI */ return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; case 6: /* TLS */ dev_done = dev_done & ~INT_TTO; /* clear flag */ int_req = int_req & ~INT_TTO; /* clear int req */ case 4: /* TPC */ sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ tto_unit.buf = AC; /* load buffer */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* if !stall, report */ } } dev_done = dev_done | INT_TTO; /* set done */ int_req = INT_UPDATE; /* update interrupts */ uptr->pos = uptr->pos + 1; return SCPE_OK; } /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; dev_done = dev_done & ~INT_TTO; /* clear done, int */ int_req = int_req & ~INT_TTO; int_enable = int_enable | INT_TTO; /* set enable */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_rl.c0000644000175000017500000007011111107355106013067 0ustar vlmvlm/* pdp8_rl.c: RL8A cartridge disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rl RL8A cartridge disk 25-Oct-05 RMS Fixed IOT 61 decode bug (found by David Gesswein) 16-Aug-05 RMS Fixed C++ declaration and cast problems 04-Jan-04 RMS Changed attach routine to use sim_fsize 25-Apr-03 RMS Revised for extended file support 04-Oct-02 RMS Added DIB, device number support 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Cloned from RL11 The RL8A is a four drive cartridge disk subsystem. An RL01 drive consists of 256 cylinders, each with 2 surfaces containing 40 sectors of 256 bytes. An RL02 drive has 512 cylinders. The RL8A controller has several serious complications. - Seeking is relative to the current disk address; this requires keeping accurate track of the current cylinder. - The RL8A will not switch heads or cross cylinders during transfers. - The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it packs 2 12b words into 3 bytes, creating a 170 "word" sector with one wasted byte. Multi-sector transfers in 12b mode don't work. */ #include "pdp8_defs.h" /* Constants */ #define RL_NUMBY 256 /* 8b bytes/sector */ #define RL_NUMSC 40 /* sectors/surface */ #define RL_NUMSF 2 /* surfaces/cylinder */ #define RL_NUMCY 256 /* cylinders/drive */ #define RL_NUMDR 4 /* drives/controller */ #define RL_MAXFR (1 << 12) /* max transfer */ #define RL01_SIZE (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY) /* words/drive */ #define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ #define RL_BBMAP 014 /* sector for bblk map */ #define RL_BBID 0123 /* ID for bblk map */ /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write lock */ #define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ #define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ #define UNIT_DUMMY (1u << UNIT_V_DUMMY) #define UNIT_WLK (1u << UNIT_V_WLK) #define UNIT_RL02 (1u << UNIT_V_RL02) #define UNIT_AUTO (1u << UNIT_V_AUTO) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* Parameters in the unit descriptor */ #define TRK u3 /* current cylinder */ #define STAT u4 /* status */ /* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ #define RLDS_LOAD 0 /* no cartridge */ #define RLDS_LOCK 5 /* lock on */ #define RLDS_BHO 0000010 /* brushes home NI */ #define RLDS_HDO 0000020 /* heads out NI */ #define RLDS_CVO 0000040 /* cover open NI */ #define RLDS_HD 0000100 /* head select ^ */ #define RLDS_RL02 0000200 /* RL02 */ #define RLDS_DSE 0000400 /* drv sel err NI */ #define RLDS_VCK 0001000 /* vol check * */ #define RLDS_WGE 0002000 /* wr gate err * */ #define RLDS_SPE 0004000 /* spin err * */ #define RLDS_STO 0010000 /* seek time out NI */ #define RLDS_WLK 0020000 /* wr locked */ #define RLDS_HCE 0040000 /* hd curr err NI */ #define RLDS_WDE 0100000 /* wr data err NI */ #define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ #define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ #define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ RLDS_VCK+RLDS_DSE) /* errors bits */ /* RLCSA, seek = offset/rw = address (also uptr->TRK) */ #define RLCSA_DIR 04000 /* direction */ #define RLCSA_HD 02000 /* head select */ #define RLCSA_CYL 00777 /* cyl offset */ #define GET_CYL(x) ((x) & RLCSA_CYL) #define GET_TRK(x) ((((x) & RLCSA_CYL) * RL_NUMSF) + \ (((x) & RLCSA_HD)? 1: 0)) #define GET_DA(x) ((GET_TRK(x) * RL_NUMSC) + rlsa) /* RLCSB, function/unit select */ #define RLCSB_V_FUNC 0 /* function */ #define RLCSB_M_FUNC 07 #define RLCSB_MNT 0 #define RLCSB_CLRD 1 #define RLCSB_GSTA 2 #define RLCSB_SEEK 3 #define RLCSB_RHDR 4 #define RLCSB_WRITE 5 #define RLCSB_READ 6 #define RLCSB_RNOHDR 7 #define RLCSB_V_MEX 3 /* memory extension */ #define RLCSB_M_MEX 07 #define RLCSB_V_DRIVE 6 /* drive */ #define RLCSB_M_DRIVE 03 #define RLCSB_V_IE 8 /* int enable */ #define RLCSB_IE (1u << RLCSB_V_IE) #define RLCSB_8B 01000 /* 12b/8b */ #define RCLS_MNT 02000 /* maint NI */ #define RLCSB_RW 0001777 /* read/write */ #define GET_FUNC(x) (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC) #define GET_MEX(x) (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX) #define GET_DRIVE(x) (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE) /* RLSA, disk sector */ #define RLSA_V_SECT 6 /* sector */ #define RLSA_M_SECT 077 #define GET_SECT(x) (((x) >> RLSA_V_SECT) & RLSA_M_SECT) /* RLER, error register */ #define RLER_DRDY 00001 /* drive ready */ #define RLER_DRE 00002 /* drive error */ #define RLER_HDE 01000 /* header error */ #define RLER_INCMP 02000 /* incomplete */ #define RLER_ICRC 04000 /* CRC error */ #define RLER_MASK 07003 /* RLSI, silo register, used only in read header */ #define RLSI_V_TRK 6 /* track */ extern uint16 M[]; extern int32 int_req; extern UNIT cpu_unit; uint8 *rlxb = NULL; /* xfer buffer */ int32 rlcsa = 0; /* control/status A */ int32 rlcsb = 0; /* control/status B */ int32 rlma = 0; /* memory address */ int32 rlwc = 0; /* word count */ int32 rlsa = 0; /* sector address */ int32 rler = 0; /* error register */ int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0; /* silo queue */ int32 rl_lft = 0; /* silo left/right */ int32 rl_done = 0; /* done flag */ int32 rl_erf = 0; /* error flag */ int32 rl_swait = 10; /* seek wait */ int32 rl_rwait = 10; /* rotate wait */ int32 rl_stopioe = 1; /* stop on error */ DEVICE rl_dev; int32 rl60 (int32 IR, int32 AC); int32 rl61 (int32 IR, int32 AC); t_stat rl_svc (UNIT *uptr); t_stat rl_reset (DEVICE *dptr); void rl_set_done (int32 error); t_stat rl_boot (int32 unitno, DEVICE *dptr); t_stat rl_attach (UNIT *uptr, char *cptr); t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); /* RL8A data structures rl_dev RL device descriptor rl_unit RL unit list rl_reg RL register list rl_mod RL modifier list */ DIB rl_dib = { DEV_RL, 2, { &rl60, &rl61 } }; UNIT rl_unit[] = { { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE, RL01_SIZE) } }; REG rl_reg[] = { { ORDATA (RLCSA, rlcsa, 12) }, { ORDATA (RLCSB, rlcsb, 12) }, { ORDATA (RLMA, rlma, 12) }, { ORDATA (RLWC, rlwc, 12) }, { ORDATA (RLSA, rlsa, 6) }, { ORDATA (RLER, rler, 12) }, { ORDATA (RLSI, rlsi, 16) }, { ORDATA (RLSI1, rlsi1, 16) }, { ORDATA (RLSI2, rlsi2, 16) }, { FLDATA (RLSIL, rl_lft, 0) }, { FLDATA (INT, int_req, INT_V_RL) }, { FLDATA (DONE, rl_done, INT_V_RL) }, { FLDATA (IE, rlcsb, RLCSB_V_IE) }, { FLDATA (ERR, rl_erf, 0) }, { DRDATA (STIME, rl_swait, 24), PV_LEFT }, { DRDATA (RTIME, rl_rwait, 24), PV_LEFT }, { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0, RL_NUMDR, PV_LEFT + REG_HRO) }, { FLDATA (STOP_IOE, rl_stopioe, 0) }, { ORDATA (DEVNUM, rl_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rl_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE rl_dev = { "RL", rl_unit, rl_reg, rl_mod, RL_NUMDR, 8, 24, 1, 8, 8, NULL, NULL, &rl_reset, &rl_boot, &rl_attach, NULL, &rl_dib, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 rl60 (int32 IR, int32 AC) { int32 curr, offs, newc, maxc; UNIT *uptr; switch (IR & 07) { /* case IR<9:11> */ case 0: /* RLDC */ rl_reset (&rl_dev); /* reset device */ break; case 1: /* RLSD */ if (rl_done) /* skip if done */ AC = IOT_SKP; else AC = 0; rl_done = 0; /* clear done */ int_req = int_req & ~INT_RL; /* clear intr */ return AC; case 2: /* RLMA */ rlma = AC; break; case 3: /* RLCA */ rlcsa = AC; break; case 4: /* RLCB */ rlcsb = AC; rl_done = 0; /* clear done */ rler = rl_erf = 0; /* clear errors */ int_req = int_req & ~INT_RL; /* clear intr */ rl_lft = 0; /* clear silo ptr */ uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ switch (GET_FUNC (rlcsb)) { /* case on func */ case RLCSB_CLRD: /* clear drive */ uptr->STAT = uptr->STAT & ~RLDS_ERR; /* clear errors */ case RLCSB_MNT: /* mnt */ rl_set_done (0); break; case RLCSB_SEEK: /* seek */ curr = GET_CYL (uptr->TRK); /* current cylinder */ offs = GET_CYL (rlcsa); /* offset */ if (rlcsa & RLCSA_DIR) { /* in or out? */ newc = curr + offs; /* out */ maxc = (uptr->flags & UNIT_RL02)? RL_NUMCY * 2: RL_NUMCY; if (newc >= maxc) newc = maxc - 1; } else { newc = curr - offs; /* in */ if (newc < 0) newc = 0; } uptr->TRK = newc | (rlcsa & RLCSA_HD); sim_activate (uptr, rl_swait * abs (newc - curr)); break; default: /* data transfer */ sim_activate (uptr, rl_swait); /* activate unit */ break; } /* end switch func */ break; case 5: /* RLSA */ rlsa = GET_SECT (AC); break; case 6: /* spare */ return 0; case 7: /* RLWC */ rlwc = AC; break; } /* end switch pulse */ return 0; /* clear AC */ } int32 rl61 (int32 IR, int32 AC) { int32 dat; UNIT *uptr; switch (IR & 07) { /* case IR<9:11> */ case 0: /* RRER */ uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ if (!sim_is_active (uptr) && /* update drdy */ (uptr->flags & UNIT_ATT)) rler = rler | RLER_DRDY; else rler = rler & ~RLER_DRDY; dat = rler & RLER_MASK; break; case 1: /* RRWC */ dat = rlwc; break; case 2: /* RRCA */ dat = rlcsa; break; case 3: /* RRCB */ dat = rlcsb; break; case 4: /* RRSA */ dat = (rlsa << RLSA_V_SECT) & 07777; break; case 5: /* RRSI */ if (rl_lft) { /* silo left? */ dat = (rlsi >> 8) & 0377; /* get left 8b */ rlsi = rlsi1; /* ripple */ rlsi1 = rlsi2; } else dat = rlsi & 0377; /* get right 8b */ rl_lft = rl_lft ^ 1; /* change side */ break; case 6: /* spare */ return AC; case 7: /* RLSE */ if (rl_erf) /* skip if err */ dat = IOT_SKP | AC; else dat = AC; rl_erf = 0; break; } /* end switch pulse */ return dat; } /* Service unit timeout If seek in progress, complete seek command Else complete data transfer command The unit control block contains the function and cylinder for the current command. */ t_stat rl_svc (UNIT *uptr) { int32 err, wc, maxc; int32 i, j, func, da, bc, wbc; uint32 ma; func = GET_FUNC (rlcsb); /* get function */ if (func == RLCSB_GSTA) { /* get status? */ rlsi = uptr->STAT | ((uptr->TRK & RLCSA_HD)? RLDS_HD: 0) | ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); if (uptr->flags & UNIT_RL02) rlsi = rlsi | RLDS_RL02; if (uptr->flags & UNIT_WPRT) rlsi = rlsi | RLDS_WLK; rlsi2 = rlsi1 = rlsi; rl_set_done (0); /* done */ return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ rl_set_done (RLER_INCMP); /* flag error */ return IORETURN (rl_stopioe, SCPE_UNATT); } if ((func == RLCSB_WRITE) && (uptr->flags & UNIT_WPRT)) { uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ rl_set_done (RLER_DRE); /* flag error */ return SCPE_OK; } if (func == RLCSB_SEEK) { /* seek? */ rl_set_done (0); /* done */ return SCPE_OK; } if (func == RLCSB_RHDR) { /* read header? */ rlsi = (GET_TRK (uptr->TRK) << RLSI_V_TRK) | rlsa; rlsi1 = rlsi2 = 0; rl_set_done (0); /* done */ return SCPE_OK; } if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr->TRK) != GET_CYL (rlcsa))) || (rlsa >= RL_NUMSC)) { /* bad cyl or sector? */ rl_set_done (RLER_HDE | RLER_INCMP); /* flag error */ return SCPE_OK; } ma = (GET_MEX (rlcsb) << 12) | rlma; /* get mem addr */ da = GET_DA (rlcsa) * RL_NUMBY; /* get disk addr */ wc = 010000 - rlwc; /* get true wc */ if (rlcsb & RLCSB_8B) { /* 8b mode? */ bc = wc; /* bytes to xfr */ maxc = (RL_NUMSC - rlsa) * RL_NUMBY; /* max transfer */ if (bc > maxc) /* trk ovrun? limit */ wc = bc = maxc; } else { bc = ((wc * 3) + 1) / 2; /* 12b mode */ if (bc > RL_NUMBY) { /* > 1 sector */ bc = RL_NUMBY; /* cap xfer */ wc = (RL_NUMBY * 2) / 3; } } err = fseek (uptr->fileref, da, SEEK_SET); if ((func >= RLCSB_READ) && (err == 0) && /* read (no hdr)? */ MEM_ADDR_OK (ma)) { /* valid bank? */ i = fxread (rlxb, sizeof (int8), bc, uptr->fileref); err = ferror (uptr->fileref); for ( ; i < bc; i++) /* fill buffer */ rlxb[i] = 0; for (i = j = 0; i < wc; i++) { /* store buffer */ if (rlcsb & RLCSB_8B) /* 8b mode? */ M[ma] = rlxb[i] & 0377; /* store */ else if (i & 1) { /* odd wd 12b? */ M[ma] = ((rlxb[j + 1] >> 4) & 017) | (((uint16) rlxb[j + 2]) << 4); j = j + 3; } else M[ma] = rlxb[j] | /* even wd 12b */ ((((uint16) rlxb[j + 1]) & 017) << 8); ma = (ma & 070000) + ((ma + 1) & 07777); } /* end for */ } /* end if wr */ if ((func == RLCSB_WRITE) && (err == 0)) { /* write? */ for (i = j = 0; i < wc; i++) { /* fetch buffer */ if (rlcsb & RLCSB_8B) /* 8b mode? */ rlxb[i] = M[ma] & 0377; /* fetch */ else if (i & 1) { /* odd wd 12b? */ rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4); rlxb[j + 2] = ((M[ma] >> 4) & 0377); j = j + 3; } else { /* even wd 12b */ rlxb[j] = M[ma] & 0377; rlxb[j + 1] = (M[ma] >> 8) & 017; } ma = (ma & 070000) + ((ma + 1) & 07777); } /* end for */ wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1); /* clr to */ for (i = bc; i < wbc; i++) /* end of blk */ rlxb[i] = 0; fxwrite (rlxb, sizeof (int8), wbc, uptr->fileref); err = ferror (uptr->fileref); } /* end write */ rlwc = (rlwc + wc) & 07777; /* final word count */ if (rlwc != 0) /* completed? */ rler = rler | RLER_INCMP; rlma = (rlma + wc) & 07777; /* final word addr */ rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY); rl_set_done (0); if (err != 0) { /* error? */ perror ("RL I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Set done and possibly errors */ void rl_set_done (int32 status) { rl_done = 1; rler = rler | status; if (rler) rl_erf = 1; if (rlcsb & RLCSB_IE) int_req = int_req | INT_RL; else int_req = int_req & ~INT_RL; return; } /* Device reset Note that the RL8A does NOT recalibrate its drives on RESET */ t_stat rl_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rlcsa = rlcsb = rlsa = rler = 0; rlma = rlwc = 0; rlsi = rlsi1 = rlsi2 = 0; rl_lft = 0; rl_done = 0; rl_erf = 0; int_req = int_req & ~INT_RL; for (i = 0; i < RL_NUMDR; i++) { uptr = rl_dev.units + i; sim_cancel (uptr); uptr->STAT = 0; } if (rlxb == NULL) rlxb = (uint8 *) calloc (RL_MAXFR, sizeof (uint8)); if (rlxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat rl_attach (UNIT *uptr, char *cptr) { uint32 p; t_stat r; uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; uptr->TRK = 0; /* cyl 0 */ uptr->STAT = RLDS_VCK; /* new volume */ if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ if (uptr->flags & UNIT_RO) return SCPE_OK; return rl_set_bad (uptr, 0, NULL, NULL); } if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return r; if (p > (RL01_SIZE * sizeof (int16))) { uptr->flags = uptr->flags | UNIT_RL02; uptr->capac = RL02_SIZE; } else { uptr->flags = uptr->flags & ~UNIT_RL02; uptr->capac = RL01_SIZE; } return SCPE_OK; } /* Set size routine */ t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; return SCPE_OK; } /* Factory bad block table creation routine This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP): words 0 magic number = 0123 (RL_BBID) words 1-n block numbers : words n+1 end of table = 0 Inputs: uptr = pointer to unit val = ignored Outputs: sta = status code */ t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i, da = RL_BBMAP * RL_NUMBY; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; if (uptr->flags & UNIT_RO) return SCPE_RO; if (!get_yn ("Create bad block table? [N]", FALSE)) return SCPE_OK; if (fseek (uptr->fileref, da, SEEK_SET)) return SCPE_IOERR; rlxb[0] = RL_BBID; for (i = 1; i < RL_NUMBY; i++) rlxb[i] = 0; fxwrite (rlxb, sizeof (uint8), RL_NUMBY, uptr->fileref); if (ferror (uptr->fileref)) return SCPE_IOERR; return SCPE_OK; } /* Bootstrap */ #define BOOT_START 1 /* start */ #define BOOT_UNIT 02006 /* unit number */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 06600, /* BT, RLDC ; reset */ 07201, /* 02, CLA IAC ; clr drv = 1 */ 04027, /* 03, JMS GO ; do io */ 01004, /* 04, TAD 4 ; rd hdr fnc */ 04027, /* 05, JMS GO ; do io */ 06615, /* 06, RRSI ; rd hdr lo */ 07002, /* 07, BSW ; swap */ 07012, /* 10, RTR ; lo cyl to L */ 06615, /* 11, RRSI ; rd hdr hi */ 00025, /* 12, AND 25 ; mask = 377 */ 07004, /* 13, RTL ; get cyl */ 06603, /* 14, RLCA ; set addr */ 07325, /* 15, CLA STL IAC RAL ; seek = 3 */ 04027, /* 16, JMS GO ; do io */ 07332, /* 17, CLA STL RTR ; dir in = 2000 */ 06605, /* 20, RLSA ; sector */ 01026, /* 21, TAD (-200) ; one sector */ 06607, /* 22, RLWC ; word cnt */ 07327, /* 23, CLA STL IAC RTL ; read = 6*/ 04027, /* 24, JMS GO ; do io */ 00377, /* 25, JMP 377 ; start */ 07600, /* 26, -200 ; word cnt */ 00000, /* GO, 0 ; subr */ 06604, /* 30, RLCB ; load fnc */ 06601, /* 31, RLSD ; wait */ 05031, /* 32, JMP .-1 ; */ 06617, /* 33, RLSE ; error? */ 05427, /* 34, JMP I GO ; no, ok */ 05001 /* 35, JMP BT ; restart */ }; t_stat rl_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; if (unitno) /* only unit 0 */ return SCPE_ARG; if (rl_dib.dev != DEV_RL) /* only std devno */ return STOP_NOTSTD; rl_unit[unitno].TRK = 0; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_rx.c0000644000175000017500000010350311107355106013105 0ustar vlmvlm/* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rx RX8E/RX01, RX28/RX02 floppy disk 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) 04-Jan-04 RMS Changed sim_fsize calling sequence 05-Nov-03 RMS Fixed bug in RX28 read status (found by Charles Dickman) 26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 03-Mar-03 RMS Fixed autosizing 08-Oct-02 RMS Added DIB, device number support Fixed reset to work with disabled device 15-Sep-02 RMS Added RX28/RX02 support 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted FLG to array 17-Jul-01 RMS Fixed warning from VC++ 6 26-Apr-01 RMS Added device enable/disable support 13-Apr-01 RMS Revised for register arrays 14-Apr-99 RMS Changed t_addr to unsigned 15-Aug-96 RMS Fixed bug in LCD An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. An RX02 diskette consists of 77 tracks, each with 26 sectors of 128B (single density) or 256B (double density). Tracks are numbered 0-76, sectors 1-26. The RX8E (RX28) can store data in 8b mode or 12b mode. In 8b mode, the controller reads or writes 128 bytes (128B or 256B) per sector. In 12b mode, it reads or writes 64 (64 or 128) 12b words per sector. The 12b words are bit packed into the first 96 (192) bytes of the sector; the last 32 (64) bytes are zeroed on writes. */ #include "pdp8_defs.h" #define RX_NUMTR 77 /* tracks/disk */ #define RX_M_TRACK 0377 #define RX_NUMSC 26 /* sectors/track */ #define RX_M_SECTOR 0177 /* cf Jones!! */ #define RX_NUMBY 128 /* bytes/sector */ #define RX2_NUMBY 256 #define RX_NUMWD (RX_NUMBY / 2) /* words/sector */ #define RX2_NUMWD (RX2_NUMBY / 2) #define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ #define RX2_SIZE (RX_NUMTR * RX_NUMSC * RX2_NUMBY) #define RX_NUMDR 2 /* drives/controller */ #define RX_M_NUMDR 01 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ #define UNIT_WLK (1u << UNIT_V_WLK) #define UNIT_DEN (1u << UNIT_V_DEN) #define UNIT_AUTO (1u << UNIT_V_AUTO) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define IDLE 0 /* idle state */ #define CMD8 1 /* 8b cmd, ho next */ #define RWDS 2 /* rw, sect next */ #define RWDT 3 /* rw, track next */ #define RWXFR 4 /* rw, transfer */ #define FILL 5 /* fill buffer */ #define EMPTY 6 /* empty buffer */ #define SDCNF 7 /* set dens, conf next */ #define SDXFR 8 /* set dens, transfer */ #define CMD_COMPLETE 9 /* set done next */ #define INIT_COMPLETE 10 /* init compl next */ #define RXCS_V_FUNC 1 /* function */ #define RXCS_M_FUNC 7 #define RXCS_FILL 0 /* fill buffer */ #define RXCS_EMPTY 1 /* empty buffer */ #define RXCS_WRITE 2 /* write sector */ #define RXCS_READ 3 /* read sector */ #define RXCS_SDEN 4 /* set density (RX28) */ #define RXCS_RXES 5 /* read status */ #define RXCS_WRDEL 6 /* write del data */ #define RXCS_ECODE 7 /* read error code */ #define RXCS_DRV 0020 /* drive */ #define RXCS_MODE 0100 /* mode */ #define RXCS_MAINT 0200 /* maintenance */ #define RXCS_DEN 0400 /* density (RX28) */ #define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC) #define RXES_CRC 0001 /* CRC error NI */ #define RXES_ID 0004 /* init done */ #define RXES_RX02 0010 /* RX02 (RX28) */ #define RXES_DERR 0020 /* density err (RX28) */ #define RXES_DEN 0040 /* density (RX28) */ #define RXES_DD 0100 /* deleted data */ #define RXES_DRDY 0200 /* drive ready */ #define TRACK u3 /* current track */ #define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr) #define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b extern int32 int_req, int_enable, dev_done; int32 rx_28 = 0; /* controller type */ int32 rx_tr = 0; /* xfer ready flag */ int32 rx_err = 0; /* error flag */ int32 rx_csr = 0; /* control/status */ int32 rx_dbr = 0; /* data buffer */ int32 rx_esr = 0; /* error status */ int32 rx_ecode = 0; /* error code */ int32 rx_track = 0; /* desired track */ int32 rx_sector = 0; /* desired sector */ int32 rx_state = IDLE; /* controller state */ int32 rx_cwait = 100; /* command time */ int32 rx_swait = 10; /* seek, per track */ int32 rx_xwait = 1; /* tr set time */ int32 rx_stopioe = 0; /* stop on error */ uint8 rx_buf[RX2_NUMBY] = { 0 }; /* sector buffer */ int32 rx_bptr = 0; /* buffer pointer */ DEVICE rx_dev; int32 rx (int32 IR, int32 AC); t_stat rx_svc (UNIT *uptr); t_stat rx_reset (DEVICE *dptr); t_stat rx_boot (int32 unitno, DEVICE *dptr); t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rx_attach (UNIT *uptr, char *cptr); void rx_cmd (void); void rx_done (int32 esr_flags, int32 new_ecode); t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); /* RX8E data structures rx_dev RX device descriptor rx_unit RX unit list rx_reg RX register list rx_mod RX modifier list */ DIB rx_dib = { DEV_RX, 1, { &rx } }; UNIT rx_unit[] = { { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ UNIT_ROABLE, RX_SIZE) }, { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ UNIT_ROABLE, RX_SIZE) } }; REG rx_reg[] = { { ORDATA (RXCS, rx_csr, 12) }, { ORDATA (RXDB, rx_dbr, 12) }, { ORDATA (RXES, rx_esr, 12) }, { ORDATA (RXERR, rx_ecode, 8) }, { ORDATA (RXTA, rx_track, 8) }, { ORDATA (RXSA, rx_sector, 8) }, { DRDATA (STAPTR, rx_state, 4), REG_RO }, { DRDATA (BUFPTR, rx_bptr, 8) }, { FLDATA (TR, rx_tr, 0) }, { FLDATA (ERR, rx_err, 0) }, { FLDATA (DONE, dev_done, INT_V_RX) }, { FLDATA (ENABLE, int_enable, INT_V_RX) }, { FLDATA (INT, int_req, INT_V_RX) }, { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, { DRDATA (STIME, rx_swait, 24), PV_LEFT }, { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, { FLDATA (STOP_IOE, rx_stopioe, 0) }, { BRDATA (SBUF, rx_buf, 8, 8, RX2_NUMBY) }, { FLDATA (RX28, rx_28, 0), REG_HRO }, { URDATA (CAPAC, rx_unit[0].capac, 10, T_ADDR_W, 0, RX_NUMDR, REG_HRO | PV_LEFT) }, { ORDATA (DEVNUM, rx_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rx_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "RX28", &rx_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "RX8E", &rx_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &rx_showtype, NULL }, { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &rx_set_size }, { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &rx_set_size }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE rx_dev = { "RX", rx_unit, rx_reg, rx_mod, RX_NUMDR, 8, 20, 1, 8, 8, NULL, NULL, &rx_reset, &rx_boot, &rx_attach, NULL, &rx_dib, DEV_DISABLE }; /* IOT routine */ int32 rx (int32 IR, int32 AC) { int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ switch (IR & 07) { /* decode IR<9:11> */ case 0: /* unused */ break; case 1: /* LCD */ if (rx_state != IDLE) /* ignore if busy */ return AC; dev_done = dev_done & ~INT_RX; /* clear done, int */ int_req = int_req & ~INT_RX; rx_tr = rx_err = 0; /* clear flags */ rx_bptr = 0; /* clear buf pointer */ if (rx_28 && (AC & RXCS_MODE)) { /* RX28 8b mode? */ rx_dbr = rx_csr = AC & 0377; /* save 8b */ rx_tr = 1; /* xfer is ready */ rx_state = CMD8; /* wait for part 2 */ } else { rx_dbr = rx_csr = AC; /* save new command */ rx_cmd (); /* issue command */ } return 0; /* clear AC */ case 2: /* XDR */ switch (rx_state & 017) { /* case on state */ case EMPTY: /* emptying buffer */ sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ return READ_RXDBR; /* return data reg */ case CMD8: /* waiting for cmd */ rx_dbr = AC & 0377; rx_csr = (rx_csr & 0377) | ((AC & 017) << 8); rx_cmd (); break; case RWDS:case RWDT:case FILL:case SDCNF: /* waiting for data */ rx_dbr = AC; /* save data */ sim_activate (&rx_unit[drv], rx_xwait); /* schedule */ break; default: /* default */ return READ_RXDBR; /* return data reg */ } break; case 3: /* STR */ if (rx_tr != 0) { rx_tr = 0; return IOT_SKP + AC; } break; case 4: /* SER */ if (rx_err != 0) { rx_err = 0; return IOT_SKP + AC; } break; case 5: /* SDN */ if ((dev_done & INT_RX) != 0) { dev_done = dev_done & ~INT_RX; int_req = int_req & ~INT_RX; return IOT_SKP + AC; } break; case 6: /* INTR */ if (AC & 1) int_enable = int_enable | INT_RX; else int_enable = int_enable & ~INT_RX; int_req = INT_UPDATE; break; case 7: /* INIT */ rx_reset (&rx_dev); /* reset device */ break; } /* end case pulse */ return AC; } void rx_cmd (void) { int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ switch (RXCS_GETFNC (rx_csr)) { /* decode command */ case RXCS_FILL: rx_state = FILL; /* state = fill */ rx_tr = 1; /* xfer is ready */ rx_esr = rx_esr & RXES_ID; /* clear errors */ break; case RXCS_EMPTY: rx_state = EMPTY; /* state = empty */ rx_esr = rx_esr & RXES_ID; /* clear errors */ sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ break; case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: rx_state = RWDS; /* state = get sector */ rx_tr = 1; /* xfer is ready */ rx_esr = rx_esr & RXES_ID; /* clear errors */ break; case RXCS_SDEN: if (rx_28) { /* RX28? */ rx_state = SDCNF; /* state = get conf */ rx_tr = 1; /* xfer is ready */ rx_esr = rx_esr & RXES_ID; /* clear errors */ break; } /* else fall thru */ default: rx_state = CMD_COMPLETE; /* state = cmd compl */ sim_activate (&rx_unit[drv], rx_cwait); /* sched done */ break; } /* end switch func */ return; } /* Unit service; the action to be taken depends on the transfer state: IDLE Should never get here RWDS Save sector, set TR, set RWDT RWDT Save track, set RWXFR RWXFR Read/write buffer FILL copy dbr to rx_buf[rx_bptr], advance ptr if rx_bptr > max, finish command, else set tr EMPTY if rx_bptr > max, finish command, else copy rx_buf[rx_bptr] to dbr, advance ptr, set tr CMD_COMPLETE copy requested data to dbr, finish command INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command For RWDT and CMD_COMPLETE, the input argument is the selected drive; otherwise, it is drive 0. */ t_stat rx_svc (UNIT *uptr) { int32 i, func, byptr, bps, wps; int8 *fbuf = uptr->filebuf; uint32 da; #define PTR12(x) (((x) + (x) + (x)) >> 1) if (rx_28 && (uptr->flags & UNIT_DEN)) /* RX28 and double density? */ bps = RX2_NUMBY; /* double bytes/sector */ else bps = RX_NUMBY; /* RX8E, normal count */ wps = bps / 2; func = RXCS_GETFNC (rx_csr); /* get function */ switch (rx_state) { /* case on state */ case IDLE: /* idle */ return SCPE_IERR; case EMPTY: /* empty buffer */ if (rx_csr & RXCS_MODE) { /* 8b xfer? */ if (rx_bptr >= bps) { /* done? */ rx_done (0, 0); /* set done */ break; /* and exit */ } rx_dbr = rx_buf[rx_bptr]; /* else get data */ } else { byptr = PTR12 (rx_bptr); /* 12b xfer */ if (rx_bptr >= wps) { /* done? */ rx_done (0, 0); /* set done */ break; /* and exit */ } rx_dbr = (rx_bptr & 1)? /* get data */ ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]: (rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017); } rx_bptr = rx_bptr + 1; rx_tr = 1; break; case FILL: /* fill buffer */ if (rx_csr & RXCS_MODE) { /* 8b xfer? */ rx_buf[rx_bptr] = rx_dbr; /* fill buffer */ rx_bptr = rx_bptr + 1; if (rx_bptr < bps) /* if more, set xfer */ rx_tr = 1; else rx_done (0, 0); /* else done */ } else { byptr = PTR12 (rx_bptr); /* 12b xfer */ if (rx_bptr & 1) { /* odd or even? */ rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017); rx_buf[byptr + 1] = rx_dbr & 0377; } else { rx_buf[byptr] = (rx_dbr >> 4) & 0377; rx_buf[byptr + 1] = (rx_dbr & 017) << 4; } rx_bptr = rx_bptr + 1; if (rx_bptr < wps) /* if more, set xfer */ rx_tr = 1; else { for (i = PTR12 (wps); i < bps; i++) rx_buf[i] = 0; /* else fill sector */ rx_done (0, 0); /* set done */ } } break; case RWDS: /* wait for sector */ rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ rx_tr = 1; /* set xfer ready */ rx_state = RWDT; /* advance state */ return SCPE_OK; case RWDT: /* wait for track */ rx_track = rx_dbr & RX_M_TRACK; /* save track */ rx_state = RWXFR; sim_activate (uptr, /* sched done */ rx_swait * abs (rx_track - uptr->TRACK)); return SCPE_OK; case RWXFR: /* transfer */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ rx_done (0, 0110); /* done, error */ return IORETURN (rx_stopioe, SCPE_UNATT); } if (rx_track >= RX_NUMTR) { /* bad track? */ rx_done (0, 0040); /* done, error */ break; } uptr->TRACK = rx_track; /* now on track */ if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ rx_done (0, 0070); /* done, error */ break; } if (rx_28 && /* RX28? */ (((uptr->flags & UNIT_DEN) != 0) ^ ((rx_csr & RXCS_DEN) != 0))) { /* densities agree? */ rx_done (RXES_DERR, 0240); /* no, error */ break; } da = CALC_DA (rx_track, rx_sector, bps); /* get disk address */ if (func == RXCS_WRDEL) /* del data? */ rx_esr = rx_esr | RXES_DD; if (func == RXCS_READ) { /* read? */ for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i]; } else { /* write */ if (uptr->flags & UNIT_WPRT) { /* locked? */ rx_done (0, 0100); /* done, error */ break; } for (i = 0; i < bps; i++) fbuf[da + i] = rx_buf[i]; da = da + bps; if (da > uptr->hwmark) uptr->hwmark = da; } rx_done (0, 0); /* done */ break; case SDCNF: /* confirm set density */ if ((rx_dbr & 0377) != 0111) { /* confirmed? */ rx_done (0, 0250); /* no, error */ break; } rx_state = SDXFR; /* next state */ sim_activate (uptr, rx_cwait * 100); /* schedule operation */ break; case SDXFR: /* erase disk */ for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0; uptr->hwmark = uptr->capac; if (rx_csr & RXCS_DEN) uptr->flags = uptr->flags | UNIT_DEN; else uptr->flags = uptr->flags & ~UNIT_DEN; rx_done (0, 0); break; case CMD_COMPLETE: /* command complete */ if (func == RXCS_ECODE) { /* read ecode? */ rx_dbr = rx_ecode; /* set dbr */ rx_done (0, -1); /* don't update */ } else if (rx_28) { /* no, read sta; RX28? */ rx_esr = rx_esr & ~RXES_DERR; /* assume dens match */ if (((uptr->flags & UNIT_DEN) != 0) ^ /* densities mismatch? */ ((rx_csr & RXCS_DEN) != 0)) rx_done (RXES_DERR, 0240); /* yes, error */ else rx_done (0, 0); /* no, ok */ } else rx_done (0, 0); /* RX8E status */ break; case INIT_COMPLETE: /* init complete */ rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ rx_done (RXES_ID, 0010); /* init done, error */ break; } da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ for (i = 0; i < bps; i++) /* read sector */ rx_buf[i] = fbuf[da + i]; rx_done (RXES_ID, 0); /* set done */ if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; break; } /* end case state */ return SCPE_OK; } /* Command complete. Set done and put final value in interface register, return to IDLE state. */ void rx_done (int32 esr_flags, int32 new_ecode) { int32 drv = (rx_csr & RXCS_DRV)? 1: 0; rx_state = IDLE; /* now idle */ dev_done = dev_done | INT_RX; /* set done */ int_req = INT_UPDATE; /* update ints */ rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN); if (rx_28) /* RX28? */ rx_esr = rx_esr | RXES_RX02; if (rx_unit[drv].flags & UNIT_ATT) { /* update drv rdy */ rx_esr = rx_esr | RXES_DRDY; if (rx_unit[drv].flags & UNIT_DEN) /* update density */ rx_esr = rx_esr | RXES_DEN; } if (new_ecode > 0) /* test for error */ rx_err = 1; if (new_ecode < 0) /* don't update? */ return; rx_ecode = new_ecode; /* update ecode */ rx_dbr = rx_esr; /* update RXDB */ return; } /* Reset routine. The RX is one of the few devices that schedules an I/O transfer as part of its initialization */ t_stat rx_reset (DEVICE *dptr) { rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */ rx_esr = rx_ecode = 0; /* clear error */ rx_tr = rx_err = 0; /* clear flags */ rx_track = rx_sector = 0; /* clear address */ rx_state = IDLE; /* ctrl idle */ dev_done = dev_done & ~INT_RX; /* clear done, int */ int_req = int_req & ~INT_RX; int_enable = int_enable & ~INT_RX; sim_cancel (&rx_unit[1]); /* cancel drive 1 */ if (dptr->flags & DEV_DIS) /* disabled? */ sim_cancel (&rx_unit[0]); else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ rx_state = INIT_COMPLETE; /* yes, sched init */ sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); } else rx_done (rx_esr | RXES_ID, 0010); /* no, error */ return SCPE_OK; } /* Attach routine */ t_stat rx_attach (UNIT *uptr, char *cptr) { uint32 sz; if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { if (sz > RX_SIZE) uptr->flags = uptr->flags | UNIT_DEN; else uptr->flags = uptr->flags & ~UNIT_DEN; } uptr->capac = (uptr->flags & UNIT_DEN)? RX2_SIZE: RX_SIZE; return attach_unit (uptr, cptr); } /* Set size routine */ t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; if ((rx_28 == 0) && val) /* not on RX8E */ return SCPE_NOFNC; uptr->capac = val? RX2_SIZE: RX_SIZE; return SCPE_OK; } /* Set controller type */ t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; if (val == rx_28) return SCPE_OK; for (i = 0; i < RX_NUMDR; i++) { if (rx_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } for (i = 0; i < RX_NUMDR; i++) { if (val) rx_unit[i].flags = rx_unit[i].flags | UNIT_DEN | UNIT_AUTO; else rx_unit[i].flags = rx_unit[i].flags & ~(UNIT_DEN | UNIT_AUTO); rx_unit[i].capac = val? RX2_SIZE: RX_SIZE; } rx_28 = val; return SCPE_OK; } /* Show controller type */ t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) { if (rx_28) fprintf (st, "RX28"); else fprintf (st, "RX8E"); return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 022 #define BOOT_ENTRY 022 #define BOOT_INST 060 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) #define BOOT2_START 020 #define BOOT2_ENTRY 033 #define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 06755, /* 22, SDN */ 05022, /* 23, JMP .-1 */ 07126, /* 24, CLL CML RTL ; read command + */ 01060, /* 25, TAD UNIT ; unit no */ 06751, /* 26, LCD ; load read+unit */ 07201, /* 27, CLA IAC ; AC = 1 */ 04053, /* 30, JMS LOAD ; load sector */ 04053, /* 31, JMS LOAD ; load track */ 07104, /* 32, CLL RAL ; AC = 2 */ 06755, /* 33, SDN */ 05054, /* 34, JMP LOAD+1 */ 06754, /* 35, SER */ 07450, /* 36, SNA ; more to do? */ 07610, /* 37, CLA SKP ; error */ 05046, /* 40, JMP 46 ; go empty */ 07402, /* 41-45, HALT ; error */ 07402, 07402, 07402, 07402, 06751, /* 46, LCD ; load empty */ 04053, /* 47, JMS LOAD ; get data */ 03002, /* 50, DCA 2 ; store */ 02050, /* 51, ISZ 50 ; incr store */ 05047, /* 52, JMP 47 ; loop until done */ 00000, /* LOAD, 0 */ 06753, /* 54, STR */ 05033, /* 55, JMP 33 */ 06752, /* 56, XDR */ 05453, /* 57, JMP I LOAD */ 07024, /* UNIT, CML RAL ; for unit 1 */ 06030 /* 61, KCC */ }; static const uint16 boot2_rom[] = { 01061, /* READ, TAD UNIT ; next unit+den */ 01046, /* 21, TAD CON360 ; add in 360 */ 00060, /* 22, AND CON420 ; mask to 420 */ 03061, /* 23, DCA UNIT ; 400,420,0,20... */ 07327, /* 24, STL CLA IAC RTL ; AC = 6 = read */ 01061, /* 25, TAD UNIT ; +unit+den */ 06751, /* 26, LCD ; load cmd */ 07201, /* 27, CLA IAC; ; AC = 1 = trksec */ 04053, /* 30, JMS LOAD ; load trk */ 04053, /* 31, JMS LOAD ; load sec */ 07004, /* CN7004, RAL ; AC = 2 = empty */ 06755, /* START, SDN ; done? */ 05054, /* 34, JMP LOAD+1 ; check xfr */ 06754, /* 35, SER ; error? */ 07450, /* 36, SNA ; AC=0 on start */ 05020, /* 37, JMP RD ; try next den,un */ 01061, /* 40, TAD UNIT ; +unit+den */ 06751, /* 41, LCD ; load cmd */ 01061, /* 42, TAD UNIT ; set 60 for sec boot */ 00046, /* 43, AND CON360 ; only density */ 01032, /* 44, TAD CN7004 ; magic */ 03060, /* 45, DCA 60 */ 00360, /* CON360, 360 ; NOP */ 04053, /* 47, JMS LOAD ; get data */ 03002, /* 50, DCA 2 ; store */ 02050, /* 51, ISZ .-1 ; incr store */ 05047, /* 52, JMP .-3 ; loop until done */ 00000, /* LOAD, 0 */ 06753, /* 54, STR ; xfr ready? */ 05033, /* 55, JMP 33 ; no, chk done */ 06752, /* 56, XDR ; get word */ 05453, /* 57, JMP I 53 ; return */ 00420, /* CON420, 420 ; toggle */ 00020 /* UNIT, 20 ; unit+density */ }; t_stat rx_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 M[]; if (rx_dib.dev != DEV_RX) /* only std devno */ return STOP_NOTSTD; if (rx_28) { for (i = 0; i < BOOT2_LEN; i++) M[BOOT2_START + i] = boot2_rom[i]; saved_PC = BOOT2_ENTRY; } else { for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; M[BOOT_INST] = unitno? 07024: 07004; saved_PC = BOOT_ENTRY; } return SCPE_OK; } simh-3.8.1/PDP8/pdp8_rk.c0000644000175000017500000004351111107355106013072 0ustar vlmvlm/* pdp8_rk.c: RK8E cartridge disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rk RK8E/RK05 cartridge disk 25-Apr-03 RMS Revised for extended file support 04-Oct-02 RMS Added DIB, device number support 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted FLG to array, made register names consistent 25-Apr-01 RMS Added device enable/disable support 29-Jun-96 RMS Added unit enable/disable support */ #include "pdp8_defs.h" /* Constants */ #define RK_NUMSC 16 /* sectors/surface */ #define RK_NUMSF 2 /* surfaces/cylinder */ #define RK_NUMCY 203 /* cylinders/drive */ #define RK_NUMWD 256 /* words/sector */ #define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */ #define RK_NUMDR 4 /* drives/controller */ #define RK_M_NUMDR 03 /* Flags in the unit flags word */ #define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ #define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ #define UNIT_HWLK (1 << UNIT_V_HWLK) #define UNIT_SWLK (1 << UNIT_V_SWLK) #define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write protect */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ #define FUNC u4 /* function */ /* Status register */ #define RKS_DONE 04000 /* transfer done */ #define RKS_HMOV 02000 /* heads moving */ #define RKS_SKFL 00400 /* drive seek fail */ #define RKS_NRDY 00200 /* drive not ready */ #define RKS_BUSY 00100 /* control busy error */ #define RKS_TMO 00040 /* timeout error */ #define RKS_WLK 00020 /* write lock error */ #define RKS_CRC 00010 /* CRC error */ #define RKS_DLT 00004 /* data late error */ #define RKS_STAT 00002 /* drive status error */ #define RKS_CYL 00001 /* cyl address error */ #define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL) /* Command register */ #define RKC_M_FUNC 07 /* function */ #define RKC_READ 0 #define RKC_RALL 1 #define RKC_WLK 2 #define RKC_SEEK 3 #define RKC_WRITE 4 #define RKC_WALL 5 #define RKC_V_FUNC 9 #define RKC_IE 00400 /* interrupt enable */ #define RKC_SKDN 00200 /* set done on seek done */ #define RKC_HALF 00100 /* 128W sector */ #define RKC_MEX 00070 /* memory extension */ #define RKC_V_MEX 3 #define RKC_M_DRV 03 /* drive select */ #define RKC_V_DRV 1 #define RKC_CYHI 00001 /* high cylinder addr */ #define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC) #define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV) #define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX)) /* Disk address */ #define RKD_V_SECT 0 /* sector */ #define RKD_M_SECT 017 #define RKD_V_SUR 4 /* surface */ #define RKD_M_SUR 01 #define RKD_V_CYL 5 /* cylinder */ #define RKD_M_CYL 0177 #define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \ (((y) >> RKD_V_CYL) & RKD_M_CYL)) #define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y) /* Reset commands */ #define RKX_CLS 0 /* clear status */ #define RKX_CLC 1 /* clear control */ #define RKX_CLD 2 /* clear drive */ #define RKX_CLSA 3 /* clear status alt */ #define RK_INT_UPDATE if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \ ((rk_cmd & RKC_IE) != 0)) \ int_req = int_req | INT_RK; \ else int_req = int_req & ~INT_RK #define RK_MIN 10 #define MAX(x,y) (((x) > (y))? (x): (y)) extern uint16 M[]; extern int32 int_req, stop_inst; extern UNIT cpu_unit; int32 rk_busy = 0; /* controller busy */ int32 rk_sta = 0; /* status register */ int32 rk_cmd = 0; /* command register */ int32 rk_da = 0; /* disk address */ int32 rk_ma = 0; /* memory address */ int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */ int32 rk_stopioe = 1; /* stop on error */ DEVICE rk_dev; int32 rk (int32 IR, int32 AC); t_stat rk_svc (UNIT *uptr); t_stat rk_reset (DEVICE *dptr); t_stat rk_boot (int32 unitno, DEVICE *dptr); void rk_go (int32 function, int32 cylinder); /* RK-8E data structures rk_dev RK device descriptor rk_unit RK unit list rk_reg RK register list rk_mod RK modifiers list */ DIB rk_dib = { DEV_RK, 1, { &rk } }; UNIT rk_unit[] = { { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) } }; REG rk_reg[] = { { ORDATA (RKSTA, rk_sta, 12) }, { ORDATA (RKCMD, rk_cmd, 12) }, { ORDATA (RKDA, rk_da, 12) }, { ORDATA (RKMA, rk_ma, 12) }, { FLDATA (BUSY, rk_busy, 0) }, { FLDATA (INT, int_req, INT_V_RK) }, { DRDATA (STIME, rk_swait, 24), PV_LEFT }, { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, { FLDATA (STOP_IOE, rk_stopioe, 0) }, { ORDATA (DEVNUM, rk_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rk_mod[] = { { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE rk_dev = { "RK", rk_unit, rk_reg, rk_mod, RK_NUMDR, 8, 24, 1, 8, 12, NULL, NULL, &rk_reset, &rk_boot, NULL, NULL, &rk_dib, DEV_DISABLE }; /* IOT routine */ int32 rk (int32 IR, int32 AC) { int32 i; UNIT *uptr; switch (IR & 07) { /* decode IR<9:11> */ case 0: /* unused */ return (stop_inst << IOT_V_REASON) + AC; case 1: /* DSKP */ return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */ IOT_SKP + AC: AC; case 2: /* DCLR */ rk_sta = 0; /* clear status */ switch (AC & 03) { /* decode AC<10:11> */ case RKX_CLS: /* clear status */ if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; case RKX_CLSA: /* clear status alt */ break; case RKX_CLC: /* clear control */ rk_cmd = rk_busy = 0; /* clear registers */ rk_ma = rk_da = 0; for (i = 0; i < RK_NUMDR; i++) sim_cancel (&rk_unit[i]); break; case RKX_CLD: /* reset drive */ if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; else rk_go (RKC_SEEK, 0); /* seek to 0 */ break; } /* end switch AC */ break; case 3: /* DLAG */ if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; else { rk_da = AC; /* load disk addr */ rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da)); } break; case 4: /* DLCA */ if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; else rk_ma = AC; /* load curr addr */ break; case 5: /* DRST */ uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */ if ((uptr->flags & UNIT_ATT) == 0) rk_sta = rk_sta | RKS_NRDY; if (sim_is_active (uptr)) rk_sta = rk_sta | RKS_HMOV; return rk_sta; case 6: /* DLDC */ if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; else { rk_cmd = AC; /* load command */ rk_sta = 0; /* clear status */ } break; case 7: /* DMAN */ break; } /* end case pulse */ RK_INT_UPDATE; /* update int req */ return 0; /* clear AC */ } /* Initiate new function Called with function, cylinder, to allow recalibrate as well as load and go to be processed by this routine. Assumes that the controller is idle, and that updating of interrupt request will be done by the caller. */ void rk_go (int32 func, int32 cyl) { int32 t; UNIT *uptr; if (func == RKC_RALL) /* all? use standard */ func = RKC_READ; if (func == RKC_WALL) func = RKC_WRITE; uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; return; } if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */ rk_sta = rk_sta | RKS_DONE | RKS_STAT; return; } if ((func == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ return; } if (func == RKC_WLK) { /* write lock? */ uptr->flags = uptr->flags | UNIT_SWLK; rk_sta = rk_sta | RKS_DONE; return; } t = abs (cyl - uptr->CYL) * rk_swait; /* seek time */ if (func == RKC_SEEK) { /* seek? */ sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */ rk_sta = rk_sta | RKS_DONE; /* set done */ } else { sim_activate (uptr, t + rk_rwait); /* schedule */ rk_busy = 1; /* set busy */ } uptr->FUNC = func; /* save func */ uptr->CYL = cyl; /* put on cylinder */ return; } /* Unit service If seek, complete seek command Else complete data transfer command The unit control block contains the function and cylinder address for the current command. Note that memory addresses wrap around in the current field. */ static uint16 fill[RK_NUMWD/2] = { 0 }; t_stat rk_svc (UNIT *uptr) { int32 err, wc, wc1, awc, swc, pa, da; UNIT *seluptr; if (uptr->FUNC == RKC_SEEK) { /* seek? */ seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */ if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) { rk_sta = rk_sta | RKS_DONE; RK_INT_UPDATE; } return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* not att? abort */ rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; rk_busy = 0; RK_INT_UPDATE; return IORETURN (rk_stopioe, SCPE_UNATT); } if ((uptr->FUNC == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ rk_busy = 0; RK_INT_UPDATE; return SCPE_OK; } pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */ da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */ swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */ if ((wc1 = ((rk_ma + wc) - 010000)) > 0) /* if wrap, limit */ wc = wc - wc1; err = fseek (uptr->fileref, da, SEEK_SET); /* locate sector */ if ((uptr->FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */ awc = fxread (&M[pa], sizeof (int16), wc, uptr->fileref); for ( ; awc < wc; awc++) /* fill if eof */ M[pa + awc] = 0; err = ferror (uptr->fileref); if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ pa = pa & 070000; /* wrap phys addr */ awc = fxread (&M[pa], sizeof (int16), wc1, uptr->fileref); for ( ; awc < wc1; awc++) /* fill if eof */ M[pa + awc] = 0; err = ferror (uptr->fileref); } } if ((uptr->FUNC == RKC_WRITE) && (err == 0)) { /* write? */ fxwrite (&M[pa], sizeof (int16), wc, uptr->fileref); err = ferror (uptr->fileref); if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ pa = pa & 070000; /* wrap phys addr */ fxwrite (&M[pa], sizeof (int16), wc1, uptr->fileref); err = ferror (uptr->fileref); } if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */ fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr->fileref); err = ferror (uptr->fileref); } } rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */ rk_sta = rk_sta | RKS_DONE; /* set done */ rk_busy = 0; RK_INT_UPDATE; if (err != 0) { perror ("RK I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat rk_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0; int_req = int_req & ~INT_RK; /* clear interrupt */ for (i = 0; i < RK_NUMDR; i++) { /* stop all units */ uptr = rk_dev.units + i; sim_cancel (uptr); uptr->flags = uptr->flags & ~UNIT_SWLK; uptr->CYL = uptr->FUNC = 0; } return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 023 #define BOOT_UNIT 032 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 06007, /* 23, CAF */ 06744, /* 24, DLCA ; addr = 0 */ 01032, /* 25, TAD UNIT ; unit no */ 06746, /* 26, DLDC ; command, unit */ 06743, /* 27, DLAG ; disk addr, go */ 01032, /* 30, TAD UNIT ; unit no, for OS */ 05031, /* 31, JMP . */ 00000 /* UNIT, 0 ; in bits <9:10> */ }; t_stat rk_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; if (rk_dib.dev != DEV_RK) /* only std devno */ return STOP_NOTSTD; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_dt.c0000644000175000017500000016110511107355106013065 0ustar vlmvlm/* pdp8_dt.c: PDP-8 DECtape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dt TC08/TU56 DECtape 23-Jun-06 RMS Fixed switch conflict in ATTACH 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) 16-Aug-05 RMS Fixed C++ declaration and cast problems 25-Jan-04 RMS Revised for device debug support 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR 18-Oct-03 RMS Fixed bugs in read all, tightened timing 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed sizing interaction with save/restore 17-Oct-02 RMS Fixed bug in end of reel logic 04-Oct-02 RMS Added DIB, device number support 12-Sep-02 RMS Added support for 16b format 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays 29-Aug-01 RMS Added casts to PDP-18b packup routine 17-Jul-01 RMS Moved function prototype 11-May-01 RMS Fixed bug in reset 25-Apr-01 RMS Added device enable/disable support 18-Apr-01 RMS Changed to rewind tape before boot 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor 15-Mar-01 RMS Added 129th word to PDP-8 format PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the TD8E formatter) is: reverse end zone 8192 reverse end zone codes ~ 10 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 8192 forward end zone codes ~ 10 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of read all and write all. Read all assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write all writes only the data words and dumps the non-data words in the bit bucket. */ #include "pdp8_defs.h" #define DT_NUMDR 8 /* #drives */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define DT_WC 07754 /* word count */ #define DT_CA 07755 /* current addr */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_BLKWD 1 /* blk no word in h/t */ #define DT_CSMWD 4 /* checksum word in h/t */ #define DT_HTWRD 5 /* header/trailer words */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word size in lines */ #define D18_BSIZE 384 /* block size in 12b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 129 /* block size in 12b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_FILSIZ (D8_CAPAC * sizeof (int16)) /* This controller */ #define DT_CAPAC D8_CAPAC /* default */ #define DT_WSIZE D8_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) #define DT_QREZ(u) (((u)->pos) < DT_EZLIN) #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) /* Status register A */ #define DTA_V_UNIT 9 /* unit select */ #define DTA_M_UNIT 07 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) #define DTA_V_MOT 7 /* motion */ #define DTA_M_MOT 03 #define DTA_V_MODE 6 /* mode */ #define DTA_V_FNC 3 /* function */ #define DTA_M_FNC 07 #define FNC_MOVE 00 /* move */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_RALL 03 /* read all */ #define FNC_WRIT 04 /* write */ #define FNC_WALL 05 /* write all */ #define FNC_WMRK 06 /* write timing */ #define DTA_V_ENB 2 /* int enable */ #define DTA_V_CERF 1 /* clr error flag */ #define DTA_V_CDTF 0 /* clr DECtape flag */ #define DTA_FWDRV (1u << (DTA_V_MOT + 1)) #define DTA_STSTP (1u << DTA_V_MOT) #define DTA_MODE (1u << DTA_V_MODE) #define DTA_ENB (1u << DTA_V_ENB) #define DTA_CERF (1u << DTA_V_CERF) #define DTA_CDTF (1u << DTA_V_CDTF) #define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) #define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) /* Status register B */ #define DTB_V_ERF 11 /* error flag */ #define DTB_V_MRK 10 /* mark trk err */ #define DTB_V_END 9 /* end zone err */ #define DTB_V_SEL 8 /* select err */ #define DTB_V_PAR 7 /* parity err */ #define DTB_V_TIM 6 /* timing err */ #define DTB_V_MEX 3 /* memory extension */ #define DTB_M_MEX 07 #define DTB_MEX (DTB_M_MEX << DTB_V_MEX) #define DTB_V_DTF 0 /* DECtape flag */ #define DTB_ERF (1u << DTB_V_ERF) #define DTB_MRK (1u << DTB_V_MRK) #define DTB_END (1u << DTB_V_END) #define DTB_SEL (1u << DTB_V_SEL) #define DTB_PAR (1u << DTB_V_PAR) #define DTB_TIM (1u << DTB_V_TIM) #define DTB_DTF (1u << DTB_V_DTF) #define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ DTB_PAR | DTB_TIM) #define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) /* DECtape state */ #define DTS_V_MOT 3 /* motion */ #define DTS_M_MOT 07 #define DTS_STOP 0 /* stopped */ #define DTS_DECF 2 /* decel, fwd */ #define DTS_DECR 3 /* decel, rev */ #define DTS_ACCF 4 /* accel, fwd */ #define DTS_ACCR 5 /* accel, rev */ #define DTS_ATSF 6 /* @speed, fwd */ #define DTS_ATSR 7 /* @speed, rev */ #define DTS_DIR 01 /* dir mask */ #define DTS_V_FNC 0 /* function */ #define DTS_M_FNC 07 #define DTS_OFR 7 /* "off reel" */ #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) #define DTS_V_2ND 6 /* next state */ #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ ((DTS_STA (y, z)) << DTS_V_2ND) #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ ((DTS_STA (y, z)) << DTS_V_3RD) #define DTS_NXTSTA(x) (x >> DTS_V_2ND) /* Operation substates */ #define DTO_WCO 1 /* wc overflow */ #define DTO_SOB 2 /* start of block */ /* Logging */ #define LOG_MS 001 /* move, search */ #define LOG_RW 002 /* read, write */ #define LOG_BL 004 /* block # lblk */ #define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ int_req = int_req | INT_DTA; \ else int_req = int_req & ~INT_DTA; #define ABS(x) (((x) < 0)? (-(x)): (x)) extern uint16 M[]; extern int32 int_req; extern UNIT cpu_unit; extern int32 sim_switches; extern FILE *sim_deb; int32 dtsa = 0; /* status A */ int32 dtsb = 0; /* status B */ int32 dt_ltime = 12; /* interline time */ int32 dt_dctime = 40000; /* decel time */ int32 dt_substate = 0; int32 dt_logblk = 0; int32 dt_stopoffr = 0; DEVICE dt_dev; int32 dt76 (int32 IR, int32 AC); int32 dt77 (int32 IR, int32 AC); t_stat dt_svc (UNIT *uptr); t_stat dt_reset (DEVICE *dptr); t_stat dt_attach (UNIT *uptr, char *cptr); t_stat dt_detach (UNIT *uptr); t_stat dt_boot (int32 unitno, DEVICE *dptr); void dt_deselect (int32 oldf); void dt_newsa (int32 newf); void dt_newfnc (UNIT *uptr, int32 newsta); t_bool dt_setpos (UNIT *uptr); void dt_schedez (UNIT *uptr, int32 dir); void dt_seterr (UNIT *uptr, int32 e); int32 dt_comobv (int32 val); int32 dt_csum (UNIT *uptr, int32 blk); int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); extern int32 sim_is_running; /* DT data structures dt_dev DT device descriptor dt_unit DT unit list dt_reg DT register list dt_mod DT modifier list */ DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } }; UNIT dt_unit[] = { { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } }; REG dt_reg[] = { { ORDATA (DTSA, dtsa, 12) }, { ORDATA (DTSB, dtsb, 12) }, { FLDATA (INT, int_req, INT_V_DTA) }, { FLDATA (ENB, dtsa, DTA_V_ENB) }, { FLDATA (DTF, dtsb, DTB_V_DTF) }, { FLDATA (ERF, dtsb, DTB_V_ERF) }, { ORDATA (WC, M[DT_WC], 12), REG_FIT }, { ORDATA (CA, M[DT_CA], 12), REG_FIT }, { DRDATA (LTIME, dt_ltime, 24), REG_NZ | PV_LEFT }, { DRDATA (DCTIME, dt_dctime, 24), REG_NZ | PV_LEFT }, { ORDATA (SUBSTATE, dt_substate, 2) }, { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO) }, { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, DT_NUMDR, REG_HRO) }, { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO }, { NULL } }; MTAB dt_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEBTAB dt_deb[] = { { "MOTION", LOG_MS }, { "DATA", LOG_RW }, { "BLOCK", LOG_BL }, { NULL, 0 } }; DEVICE dt_dev = { "DT", dt_unit, dt_reg, dt_mod, DT_NUMDR, 8, 24, 1, 8, 12, NULL, NULL, &dt_reset, &dt_boot, &dt_attach, &dt_detach, &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, dt_deb, NULL, NULL }; /* IOT routines */ int32 dt76 (int32 IR, int32 AC) { int32 pulse = IR & 07; int32 old_dtsa = dtsa, fnc; UNIT *uptr; if (pulse & 01) AC = AC | dtsa; /* DTRA */ if (pulse & 06) { /* select */ if (pulse & 02) /* DTCA */ dtsa = 0; if (pulse & 04) { /* DTXA */ if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; dtsa = dtsa ^ (AC & DTA_RW); AC = 0; /* clr AC */ } if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ fnc = DTA_GETFNC (dtsa); /* get fnc */ if (((uptr->flags) & UNIT_DIS) || /* disabled? */ (fnc >= FNC_WMRK) || /* write mark? */ ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) dt_seterr (uptr, DTB_SEL); /* select err */ else dt_newsa (dtsa); DT_UPDINT; } return AC; } int32 dt77 (int32 IR, int32 AC) { int32 pulse = IR & 07; if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ AC = IOT_SKP | AC; if (pulse & 02) /* DTRB */ AC = AC | dtsb; if (pulse & 04) { /* DTLB */ dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); AC = AC & ~07777; /* clear AC */ } return AC; } /* Unit deselect */ void dt_deselect (int32 oldf) { int32 old_unit = DTA_GETUNIT (oldf); UNIT *uptr = dt_dev.units + old_unit; int32 old_mot = DTS_GETMOT (uptr->STATE); if (old_mot >= DTS_ATSF) /* at speed? */ dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); else if (old_mot >= DTS_ACCF) /* accelerating? */ DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); return; } /* Command register change 1. If change in motion, stop to start - schedule acceleration - set function as next state 2. If change in motion, start to stop - if not already decelerating (could be reversing), schedule deceleration 3. If change in direction, - if not decelerating, schedule deceleration - set accelerating (other dir) as next state - set function as next next state 4. If not accelerating or at speed, - schedule acceleration - set function as next state 5. If not yet at speed, - set function as next state 6. If at speed, - set function as current state, schedule function */ void dt_newsa (int32 newf) { int32 new_unit, prev_mot, new_fnc; int32 prev_mving, new_mving, prev_dir, new_dir; UNIT *uptr; new_unit = DTA_GETUNIT (newf); /* new, old units */ uptr = dt_dev.units + new_unit; if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ dt_seterr (uptr, DTB_SEL); /* no, error */ return; } prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ prev_mving = prev_mot != DTS_STOP; /* previous moving? */ prev_dir = prev_mot & DTS_DIR; /* previous dir? */ new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ new_fnc = DTA_GETFNC (newf); /* new function? */ if ((prev_mving | new_mving) == 0) /* stop to stop */ return; if (new_mving & ~prev_mving) { /* start? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mving & ~new_mving) { /* stop? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ return; } if (prev_dir ^ new_dir) { /* dir chg? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ return; } if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* cancel cur */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mot < DTS_ATSF) { /* not at speed? */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ return; } /* Schedule new DECtape function This routine is only called if - the selected unit is attached - the selected unit is at speed (forward or backward) This routine - updates the selected unit's position - updates the selected unit's state - schedules the new operation */ void dt_newfnc (UNIT *uptr, int32 newsta) { int32 fnc, dir, blk, unum, relpos, newpos; uint32 oldpos; oldpos = uptr->pos; /* save old pos */ if (dt_setpos (uptr)) /* update pos */ return; uptr->STATE = newsta; /* update state */ fnc = DTS_GETFNC (uptr->STATE); /* set variables */ dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; unum = (int32) (uptr - dt_dev.units); if (oldpos == uptr->pos) /* bump pos */ uptr->pos = uptr->pos + (dir? -1: 1); blk = DT_LIN2BL (uptr->pos, uptr); if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ dt_seterr (uptr, DTB_END); /* set ez flag, stop */ return; } sim_cancel (uptr); /* cancel cur op */ dt_substate = DTO_SOB; /* substate = block start */ switch (fnc) { /* case function */ case DTS_OFR: /* off reel */ if (dir) /* rev? < start */ newpos = -1000; else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ break; case FNC_MOVE: /* move */ dt_schedez (uptr, dir); /* sched end zone */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", unum, (dir? "backward": "forward")); return; /* done */ case FNC_SRCH: /* search */ if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; else newpos = DT_BLK2LN ((DT_QREZ (uptr)? 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s]\n", unum, (dir? "backward": "forward")); break; case FNC_WRIT: /* write */ case FNC_READ: /* read */ case FNC_RALL: /* read all */ case FNC_WALL: /* write all */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dt_seterr (uptr, DTB_SEL); return; } if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); break; default: dt_seterr (uptr, DTB_SEL); /* bad state */ return; } sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/dt_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool dt_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 mot = DTS_GETMOT (uptr->STATE); int32 unum, delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (mot & ~DTS_DIR) { /* case on motion */ case DTS_STOP: /* stop */ delta = 0; break; case DTS_DECF: /* slowing */ ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case DTS_ACCF: /* accelerating */ ulin = ut / (uint32) dt_ltime; udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; delta = (ulin * ulin) / (2 * udelt); break; case DTS_ATSF: /* at speed */ delta = ut / (uint32) dt_ltime; break; } if (mot & DTS_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel? */ uptr->STATE = uptr->pos = 0; unum = (int32) (uptr - dt_dev.units); if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ dt_seterr (uptr, DTB_SEL); /* error */ return TRUE; } return FALSE; } /* Unit service Unit must be attached, detach cancels operation */ t_stat dt_svc (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; int32 fnc = DTS_GETFNC (uptr->STATE); int16 *fbuf = (int16 *) uptr->filebuf; int32 unum = uptr - dt_dev.units; int32 blk, wrd, ma, relpos, dat; uint32 ba; /* Motion cases Decelerating - if next state != stopped, must be accel reverse Accelerating - next state must be @speed, schedule function At speed - do functional processing */ switch (mot) { case DTS_DECF: case DTS_DECR: /* decelerating */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ if (uptr->STATE) /* not stopped? */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */ return SCPE_OK; case DTS_ACCF: case DTS_ACCR: /* accelerating */ dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ return SCPE_OK; case DTS_ATSF: case DTS_ATSR: /* at speed */ break; /* check function */ default: /* other */ dt_seterr (uptr, DTB_SEL); /* state error */ return SCPE_OK; } /* Functional cases Move - must be at end zone Search - transfer block number, schedule next block Off reel - detach unit (it must be deselected) */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); if (DT_QEZ (uptr)) { /* in end zone? */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; } blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ switch (fnc) { /* at speed, check fnc */ case FNC_MOVE: /* move */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; case FNC_SRCH: /* search */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if (MEM_ADDR_OK (ma)) /* store block # */ M[ma] = blk & 07777; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTS_OFR: /* off reel */ detach_unit (uptr); /* must be deselected */ uptr->STATE = uptr->pos = 0; /* no visible action */ break; /* Read has four subcases Start of block, not wc ovf - check that DTF is clear, otherwise normal Normal - increment MA, WC, copy word from tape to memory if read dir != write dir, bits must be scrambled if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - if end of block reached, timing error, otherwise, continue to next word */ case FNC_READ: /* read */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start of block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal read */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dat = fbuf[ba]; /* get tape word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dat; if (M[DT_WC] == 0) /* wc ovf? */ dt_substate = DTO_WCO; case DTO_WCO: /* wc ovf, not sob */ if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* next block */ if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ dt_seterr (uptr, DTB_TIM); /* timing error */ else sim_activate (uptr, DT_WSIZE * dt_ltime); break; } break; /* Write has four subcases Start of block, not wc ovf - check that DTF is clear, set block direction Normal - increment MA, WC, copy word from memory to tape if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - copy 0 to tape if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - schedule end zone */ case FNC_WRIT: /* write */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal write */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; case DTO_WCO: /* wc ovflo */ ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dat = dt_substate? 0: M[ma]; /* get word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); fbuf[ba] = dat; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } break; /* Read all has two subcases Not word count overflow - increment MA, WC, copy word from tape to memory Word count overflow - schedule end zone */ case FNC_RALL: switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; dat = fbuf[ba]; /* get tape word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); } else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dat; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; /* Write all has two subcases Not word count overflow - increment MA, WC, copy word from memory to tape Word count overflow - schedule end zone */ case FNC_WALL: switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dat = M[ma]; /* get mem word */ if (dir) dat = dt_comobv (dat); wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; fbuf[ba] = dat; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; } /* /* ignore hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; default: dt_seterr (uptr, DTB_SEL); /* impossible state */ break; } DT_UPDINT; /* update interrupts */ return SCPE_OK; } /* Reading the header is complicated, because 18b words are being parsed out 12b at a time. The sequence of word numbers is directionally sensitive Forward Reverse Word Word Content Word Word Content (abs) (rel) (abs) (rel) 137 8 fwd csm'00 6 6 rev csm'00 138 9 0000 5 5 0000 139 10 0000 4 4 0000 140 11 0000 3 3 0000 141 12 00'lo rev blk 2 2 00'lo fwd blk 142 13 hi rev blk 1 1 hi fwd blk 143 14 0000 0 0 0000 0 0 0000 143 14 0000 1 1 0000 142 13 0000 2 2 hi fwd blk 141 12 hi rev blk 3 3 lo fwd blk'00 140 11 lo rev blk'00 4 4 0000 139 10 0000 5 5 0000 138 9 0000 6 6 0000 137 8 0000 7 7 rev csm 136 7 00'fwd csm */ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) { if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); if (dir) { /* reverse */ switch (relpos / DT_WSIZE) { case 6: /* rev csm */ return 077; case 2: /* lo fwd blk */ return dt_comobv ((blk & 077) << 6); case 1: /* hi fwd blk */ return dt_comobv (blk >> 6); case 12: /* hi rev blk */ return (blk >> 6) & 07777; case 11: /* lo rev blk */ return ((blk & 077) << 6); case 7: /* fwd csum */ return (dt_comobv (dt_csum (uptr, blk)) << 6); default: /* others */ return 07777; } } else { /* forward */ switch (relpos / DT_WSIZE) { case 8: /* fwd csum */ return (dt_csum (uptr, blk) << 6); case 12: /* lo rev blk */ return dt_comobv ((blk & 077) << 6); case 13: /* hi rev blk */ return dt_comobv (blk >> 6); case 2: /* hi fwd blk */ return ((blk >> 6) & 07777); case 3: /* lo fwd blk */ return ((blk & 077) << 6); case 7: /* rev csum */ return 077; default: /* others */ break; } } return 0; } /* Utility routines */ /* Set error flag */ void dt_seterr (UNIT *uptr, int32 e) { int32 mot = DTS_GETMOT (uptr->STATE); dtsa = dtsa & ~DTA_STSTP; /* clear go */ dtsb = dtsb | DTB_ERF | e; /* set error flag */ if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ sim_cancel (uptr); /* cancel activity */ if (dt_setpos (uptr)) /* update position */ return; sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ } DT_UPDINT; return; } /* Schedule end zone */ void dt_schedez (UNIT *uptr, int32 dir) { int32 newpos; if (dir) /* rev? rev ez */ newpos = DT_EZLIN - DT_WSIZE; else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Complement obverse routine */ int32 dt_comobv (int32 dat) { dat = dat ^ 07777; /* compl obverse */ dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | ((dat & 070) << 3) | ((dat & 07) << 9); return dat; } /* Checksum routine */ int32 dt_csum (UNIT *uptr, int32 blk) { int16 *fbuf = (int16 *) uptr->filebuf; int32 ba = blk * DTU_BSIZE (uptr); int32 i, csum, wrd; csum = 077; /* init csum */ for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i] ^ 07777; /* get ~word */ csum = csum ^ (wrd >> 6) ^ wrd; } return (csum & 077); } /* Reset routine */ t_stat dt_reset (DEVICE *dptr) { int32 i, prev_mot; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ uptr = dt_dev.units + i; if (sim_is_running) { /* CAF? */ prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ if (dt_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } dtsa = dtsb = 0; /* clear status */ DT_UPDINT; /* reset interrupt */ return SCPE_OK; } /* Bootstrap routine This is actually the 4K disk monitor bootstrap, which also works with OS/8. The reverse is not true - the OS/8 bootstrap doesn't work with the disk monitor. */ #define BOOT_START 0200 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 07600, /* 200, CLA CLL */ 01216, /* TAD MVB ; move back */ 04210, /* JMS DO ; action */ 01217, /* TAD K7577 ; addr */ 03620, /* DCA I CA */ 01222, /* TAD RDF ; read fwd */ 04210, /* JMS DO ; action */ 05600, /* JMP I 200 ; enter boot */ 00000, /* DO, 0 */ 06766, /* DTCA!DTXA ; start tape */ 03621, /* DCA I WC ; clear wc */ 06771, /* DTSF ; wait */ 05213, /* JMP .-1 */ 05610, /* JMP I DO */ 00600, /* MVB, 0600 */ 07577, /* K7577, 7757 */ 07755, /* CA, 7755 */ 07754, /* WC, 7754 */ 00220 /* RF, 0220 */ }; t_stat dt_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; if (unitno) /* only unit 0 */ return SCPE_ARG; if (dt_dib.dev != DEV_DTA) /* only std devno */ return STOP_NOTSTD; dt_unit[unitno].pos = DT_EZLIN; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 16b or 18b, read 16b or 18b format and convert to 12b in buffer If 12b, read data into buffer */ t_stat dt_attach (UNIT *uptr, char *cptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k; int32 u = uptr - dt_dev.units; t_stat r; uint32 ba, sz; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) return r; /* fail? */ if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; if (sim_switches & SWMASK ('F')) /* att 18b? */ uptr->flags = uptr->flags & ~UNIT_8FMT; else if (sim_switches & SWMASK ('S')) /* att 16b? */ uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ (sz = sim_fsize (uptr->fileref))) { if (sz == D11_FILSIZ) uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (sz > D8_FILSIZ) uptr->flags = uptr->flags & ~UNIT_8FMT; } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (uint16)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint16 *) uptr->filebuf; /* file buffer */ printf ("%s%d: ", sim_dname (&dt_dev), u); if (uptr->flags & UNIT_8FMT) printf ("12b format"); else if (uptr->flags & UNIT_11FMT) printf ("16b format"); else printf ("18b/36b format"); printf (", buffering file in memory\n"); if (uptr->flags & UNIT_8FMT) /* 12b? */ uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), uptr->capac, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ if (uptr->flags & UNIT_11FMT) { k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; } else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ fbuf[ba] = (pdp18b[k] >> 6) & 07777; fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | ((pdp18b[k + 1] >> 12) & 077); fbuf[ba + 2] = pdp18b[k + 1] & 07777; ba = ba + 3; } /* end blk loop */ } /* end file loop */ uptr->hwmark = ba; } /* end else */ uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ return SCPE_OK; } /* Detach routine Cancel in progress operation If 12b, write buffer to file If 16b or 18b, convert 12b buffer to 16b or 18b and write to file Deallocate buffer */ t_stat dt_detach (UNIT* uptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k; int32 u = uptr - dt_dev.units; uint32 ba; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (sim_is_active (uptr)) { sim_cancel (uptr); if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; DT_UPDINT; } uptr->STATE = uptr->pos = 0; } fbuf = (uint16 *) uptr->filebuf; /* file buffer */ if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) /* PDP8? */ fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ uptr->hwmark, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ for (k = 0; k < D18_NBSIZE; k = k + 2) { pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | ((uint32) (fbuf[ba + 1] >> 6) & 077); pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | ((uint32) (fbuf[ba + 2] & 07777)); ba = ba + 3; } /* end loop blk */ if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i]; fxwrite (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); } else fxwrite (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); } /* end loop buf */ } /* end else */ if (ferror (uptr->fileref)) perror ("I/O error"); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ return detach_unit (uptr); } simh-3.8.1/PDP8/pdp8_df.c0000644000175000017500000003520111107355106013044 0ustar vlmvlm/* pdp8_df.c: DF32 fixed head disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. df DF32 fixed head disk 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) 04-Jan-04 RMS Changed sim_fsize calling sequence 26-Oct-03 RMS Cleaned up buffer copy code 26-Jul-03 RMS Fixed bug in set size routine 14-Mar-03 RMS Fixed variable platter interaction with save/restore 03-Mar-03 RMS Fixed autosizing 02-Feb-03 RMS Added variable platter and autosizing support 04-Oct-02 RMS Added DIBs, device number support 28-Nov-01 RMS Added RL8A support 25-Apr-01 RMS Added device enable/disable support The DF32 is a head-per-track disk. It uses the three cycle data break facility. To minimize overhead, the entire DF32 is buffered in memory. Two timing parameters are provided: df_time Interword timing, must be non-zero df_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, DMA occurs in a burst */ #include "pdp8_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT 03 #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define DF_NUMWD 2048 /* words/track */ #define DF_NUMTR 16 /* tracks/disk */ #define DF_DKSIZE (DF_NUMTR * DF_NUMWD) /* words/disk */ #define DF_NUMDK 4 /* disks/controller */ #define DF_WC 07750 /* word count */ #define DF_MA 07751 /* mem address */ #define DF_WMASK (DF_NUMWD - 1) /* word mask */ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ #define DF_READ 2 /* read */ #define DF_WRITE 4 /* write */ /* Status register */ #define DFS_PCA 04000 /* photocell status */ #define DFS_DEX 03700 /* disk addr extension */ #define DFS_MEX 00070 /* mem addr extension */ #define DFS_DRL 00004 /* data late error */ #define DFS_WLS 00002 /* write lock error */ #define DFS_NXD 00002 /* non-existent disk */ #define DFS_PER 00001 /* parity error */ #define DFS_ERR (DFS_DRL | DFS_WLS | DFS_PER) #define DFS_V_DEX 6 #define DFS_V_MEX 3 #define GET_MEX(x) (((x) & DFS_MEX) << (12 - DFS_V_MEX)) #define GET_DEX(x) (((x) & DFS_DEX) << (12 - DFS_V_DEX)) #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DF_NUMWD))) #define UPDATE_PCELL if (GET_POS (df_time) < 6) df_sta = df_sta | DFS_PCA; \ else df_sta = df_sta & ~DFS_PCA extern uint16 M[]; extern int32 int_req, stop_inst; extern UNIT cpu_unit; int32 df_sta = 0; /* status register */ int32 df_da = 0; /* disk address */ int32 df_done = 0; /* done flag */ int32 df_wlk = 0; /* write lock */ int32 df_time = 10; /* inter-word time */ int32 df_burst = 1; /* burst mode flag */ int32 df_stopioe = 1; /* stop on error */ DEVICE df_dev; int32 df60 (int32 IR, int32 AC); int32 df61 (int32 IR, int32 AC); int32 df62 (int32 IR, int32 AC); t_stat df_svc (UNIT *uptr); t_stat pcell_svc (UNIT *uptr); t_stat df_reset (DEVICE *dptr); t_stat df_boot (int32 unitno, DEVICE *dptr); t_stat df_attach (UNIT *uptr, char *cptr); t_stat df_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* DF32 data structures df_dev RF device descriptor df_unit RF unit descriptor pcell_unit photocell timing unit (orphan) df_reg RF register list */ DIB df_dib = { DEV_DF, 3, { &df60, &df61, &df62 } }; UNIT df_unit = { UDATA (&df_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DF_DKSIZE) }; REG df_reg[] = { { ORDATA (STA, df_sta, 12) }, { ORDATA (DA, df_da, 12) }, { ORDATA (WC, M[DF_WC], 12), REG_FIT }, { ORDATA (MA, M[DF_MA], 12), REG_FIT }, { FLDATA (DONE, df_done, 0) }, { FLDATA (INT, int_req, INT_V_DF) }, { ORDATA (WLS, df_wlk, 8) }, { DRDATA (TIME, df_time, 24), REG_NZ + PV_LEFT }, { FLDATA (BURST, df_burst, 0) }, { FLDATA (STOP_IOE, df_stopioe, 0) }, { DRDATA (CAPAC, df_unit.capac, 18), REG_HRO }, { ORDATA (DEVNUM, df_dib.dev, 6), REG_HRO }, { NULL } }; MTAB df_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &df_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &df_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &df_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &df_set_size }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE df_dev = { "DF", &df_unit, df_reg, df_mod, 1, 8, 17, 1, 8, 12, NULL, NULL, &df_reset, &df_boot, &df_attach, NULL, &df_dib, DEV_DISABLE }; /* IOT routines */ int32 df60 (int32 IR, int32 AC) { int32 t; int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ if (pulse & 1) { /* DCMA */ df_da = 0; /* clear disk addr */ df_done = 0; /* clear done */ df_sta = df_sta & ~DFS_ERR; /* clear errors */ int_req = int_req & ~INT_DF; /* clear int req */ } if (pulse & 6) { /* DMAR, DMAW */ df_da = df_da | AC; /* disk addr |= AC */ df_unit.FUNC = pulse & ~1; /* save function */ t = (df_da & DF_WMASK) - GET_POS (df_time); /* delta to new loc */ if (t < 0) /* wrap around? */ t = t + DF_NUMWD; sim_activate (&df_unit, t * df_time); /* schedule op */ AC = 0; /* clear AC */ } return AC; } /* Based on the hardware implementation. DEAL and DEAC work as follows: 6615 pulse 1 = clear df_sta pulse 4 = df_sta = df_sta | AC AC = AC | old_df_sta 6616 pulse 2 = clear AC, skip if address confirmed pulse 4 = df_sta = df_sta | AC = 0 (nop) AC = AC | old_df_sta */ int32 df61 (int32 IR, int32 AC) { int32 old_df_sta = df_sta; int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ if (pulse & 1) /* DCEA */ df_sta = df_sta & ~(DFS_DEX | DFS_MEX); /* clear dex, mex */ if (pulse & 2) /* DSAC */ AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0; if (pulse & 4) { df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX)); /* DEAL */ AC = AC | old_df_sta; /* DEAC */ } return AC; } int32 df62 (int32 IR, int32 AC) { int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ if (pulse & 1) { /* DFSE */ if ((df_sta & DFS_ERR) == 0) AC = AC | IOT_SKP; } if (pulse & 2) { /* DFSC */ if (pulse & 4) /* for DMAC */ AC = AC & ~07777; else if (df_done) AC = AC | IOT_SKP; } if (pulse & 4) /* DMAC */ AC = AC | df_da; return AC; } /* Unit service Note that for reads and writes, memory addresses wrap around in the current field. This code assumes the entire disk is buffered. */ t_stat df_svc (UNIT *uptr) { int32 pa, t, mex; uint32 da; int16 *fbuf = uptr->filebuf; UPDATE_PCELL; /* update photocell */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ df_done = 1; int_req = int_req | INT_DF; /* update int req */ return IORETURN (df_stopioe, SCPE_UNATT); } mex = GET_MEX (df_sta); da = GET_DEX (df_sta) | df_da; /* form disk addr */ do { if (da >= uptr->capac) { /* nx disk addr? */ df_sta = df_sta | DFS_NXD; break; } M[DF_WC] = (M[DF_WC] + 1) & 07777; /* incr word count */ M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */ pa = mex | M[DF_MA]; /* add extension */ if (uptr->FUNC == DF_READ) { /* read? */ if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da]; /* if !nxm, read wd */ } else { /* write */ t = (da >> 14) & 07; /* check wr lock */ if ((df_wlk >> t) & 1) /* locked? set err */ df_sta = df_sta | DFS_WLS; else { /* not locked */ fbuf[da] = M[pa]; /* write word */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; } } da = (da + 1) & 0377777; /* incr disk addr */ } while ((M[DF_WC] != 0) && (df_burst != 0)); /* brk if wc, no brst */ if ((M[DF_WC] != 0) && ((df_sta & DFS_ERR) == 0)) /* more to do? */ sim_activate (&df_unit, df_time); /* sched next */ else { if (uptr->FUNC != DF_READ) da = (da - 1) & 0377777; df_done = 1; /* done */ int_req = int_req | INT_DF; /* update int req */ } df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX); df_da = da & 07777; /* separate disk addr */ return SCPE_OK; } /* Reset routine */ t_stat df_reset (DEVICE *dptr) { df_sta = df_da = 0; df_done = 1; int_req = int_req & ~INT_DF; /* clear interrupt */ sim_cancel (&df_unit); return SCPE_OK; } /* Bootstrap routine */ #define OS8_START 07750 #define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) #define DM4_START 00200 #define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) static const uint16 os8_rom[] = { 07600, /* 7750, CLA CLL ; also word count */ 06603, /* 7751, DMAR ; also address */ 06622, /* 7752, DFSC ; done? */ 05352, /* 7753, JMP .-1 ; no */ 05752 /* 7754, JMP @.-2 ; enter boot */ }; static const uint16 dm4_rom[] = { 00200, 07600, /* 0200, CLA CLL */ 00201, 06603, /* 0201, DMAR ; read */ 00202, 06622, /* 0202, DFSC ; done? */ 00203, 05202, /* 0203, JMP .-1 ; no */ 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ 07750, 07576, /* 7750, 7576 ; word count */ 07751, 07576 /* 7751, 7576 ; address */ }; t_stat df_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 sim_switches, saved_PC; if (sim_switches & SWMASK ('D')) { for (i = 0; i < DM4_LEN; i = i + 2) M[dm4_rom[i]] = dm4_rom[i + 1]; saved_PC = DM4_START; } else { for (i = 0; i < OS8_LEN; i++) M[OS8_START + i] = os8_rom[i]; saved_PC = OS8_START; } return SCPE_OK; } /* Attach routine */ t_stat df_attach (UNIT *uptr, char *cptr) { uint32 p, sz; uint32 ds_bytes = DF_DKSIZE * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= DF_NUMDK) p = DF_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * DF_DKSIZE; return attach_unit (uptr, cptr); } /* Change disk size */ t_stat df_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETP (val) * DF_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_clk.c0000644000175000017500000001477411107355106013240 0ustar vlmvlm/* pdp8_clk.c: PDP-8 real-time clock simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. clk real time clock 18-Jun-07 RMS Added UNIT_IDLE flag 01-Mar-03 RMS Aded SET/SHOW CLK FREQ support 04-Oct-02 RMS Added DIB, device number support 30-Dec-01 RMS Removed for generalized timers 05-Sep-01 RMS Added terminal multiplexor support 17-Jul-01 RMS Moved function prototype 05-Mar-01 RMS Added clock calibration support Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks */ #include "pdp8_defs.h" extern int32 int_req, int_enable, dev_done, stop_inst; int32 clk_tps = 60; /* ticks/second */ int32 tmxr_poll = 16000; /* term mux poll */ int32 clk (int32 IR, int32 AC); t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_reg CLK register list */ DIB clk_dib = { DEV_CLK, 1, { &clk } }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 }; REG clk_reg[] = { { FLDATA (DONE, dev_done, INT_V_CLK) }, { FLDATA (ENABLE, int_enable, INT_V_CLK) }, { FLDATA (INT, int_req, INT_V_CLK) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &clk_show_freq, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, 0 }; /* IOT routine IOT's 6131-6133 are the PDP-8/E clock IOT's 6135-6137 are the PDP-8/A clock */ int32 clk (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 1: /* CLEI */ int_enable = int_enable | INT_CLK; /* enable clk ints */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 2: /* CLDI */ int_enable = int_enable & ~INT_CLK; /* disable clk ints */ int_req = int_req & ~INT_CLK; /* update interrupts */ return AC; case 3: /* CLSC */ if (dev_done & INT_CLK) { /* flag set? */ dev_done = dev_done & ~INT_CLK; /* clear flag */ int_req = int_req & ~INT_CLK; /* clear int req */ return IOT_SKP + AC; } return AC; case 5: /* CLLE */ if (AC & 1) /* test AC<11> */ int_enable = int_enable | INT_CLK; else int_enable = int_enable & ~INT_CLK; int_req = INT_UPDATE; /* update interrupts */ return AC; case 6: /* CLCL */ dev_done = dev_done & ~INT_CLK; /* clear flag */ int_req = int_req & ~INT_CLK; /* clear int req */ return AC; case 7: /* CLSK */ return (dev_done & INT_CLK)? IOT_SKP + AC: AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat clk_svc (UNIT *uptr) { int32 t; dev_done = dev_done | INT_CLK; /* set done */ int_req = INT_UPDATE; /* update interrupts */ t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, t); /* reactivate unit */ tmxr_poll = t; /* set mux poll */ return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { int32 t; dev_done = dev_done & ~INT_CLK; /* clear done, int */ int_req = int_req & ~INT_CLK; int_enable = int_enable & ~INT_CLK; /* clear enable */ t = sim_rtcn_init (clk_unit.wait, TMR_CLK); sim_activate_abs (&clk_unit, t); /* activate unit */ tmxr_poll = t; return SCPE_OK; } /* Set frequency */ t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; clk_tps = val; return SCPE_OK; } /* Show frequency */ t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); return SCPE_OK; } simh-3.8.1/PDP8/pdp8_td.c0000644000175000017500000011613211107355106013065 0ustar vlmvlm/* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones' PDP8 simulator but tracks the hardware implementation more closely. td TD8E/TU56 DECtape 23-Jun-06 RMS Fixed switch conflict in ATTACH 16-Aug-05 RMS Fixed C++ declaration and cast problems 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 18/36b DECtape file is read in, it is converted to 12b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the TD8E formatter) is: reverse end zone 8192 reverse end zone codes ~ 10 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 8192 forward end zone codes ~ 10 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of non-data words. Read assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write modifies only the data words and dumps the non-data words in the bit bucket. */ #include "pdp8_defs.h" #define DT_NUMDR 2 /* #drives */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */ #define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word sizein lines */ #define D18_BSIZE 384 /* block size in 12b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 129 /* block size in 12b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_FILSIZ (D8_CAPAC * sizeof (int16)) /* This controller */ #define DT_CAPAC D8_CAPAC /* default */ #define DT_WSIZE D8_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) /* Command register */ #define TDC_UNIT 04000 /* unit select */ #define TDC_FWDRV 02000 /* fwd/rev */ #define TDC_STPGO 01000 /* stop/go */ #define TDC_RW 00400 /* read/write */ #define TDC_MASK 07400 /* implemented */ #define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0) /* Status register */ #define TDS_WLO 00200 /* write lock */ #define TDS_TME 00100 /* timing/sel err */ /* Mark track register and codes */ #define MTK_MASK 077 #define MTK_REV_END 055 /* rev end zone */ #define MTK_INTER 025 /* interblock */ #define MTK_FWD_BLK 026 /* fwd block */ #define MTK_REV_GRD 032 /* reverse guard */ #define MTK_FWD_PRE 010 /* lock, etc */ #define MTK_DATA 070 /* data */ #define MTK_REV_PRE 073 /* lock, etc */ #define MTK_FWD_GRD 051 /* fwd guard */ #define MTK_REV_BLK 045 /* rev block */ #define MTK_FWD_END 022 /* fwd end zone */ /* DECtape state */ #define STA_STOP 0 /* stopped */ #define STA_DEC 2 /* decelerating */ #define STA_ACC 4 /* accelerating */ #define STA_UTS 6 /* up to speed */ #define STA_DIR 1 /* fwd/rev */ #define ABS(x) (((x) < 0)? (-(x)): (x)) #define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1) /* State and declarations */ int32 td_cmd = 0; /* command */ int32 td_dat = 0; /* data */ int32 td_mtk = 0; /* mark track */ int32 td_slf = 0; /* single line flag */ int32 td_qlf = 0; /* quad line flag */ int32 td_tme = 0; /* timing error flag */ int32 td_csum = 0; /* save check sum */ int32 td_qlctr = 0; /* quad line ctr */ int32 td_ltime = 20; /* interline time */ int32 td_dctime = 40000; /* decel time */ int32 td_stopoffr = 0; static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */ DEVICE td_dev; int32 td77 (int32 IR, int32 AC); t_stat td_svc (UNIT *uptr); t_stat td_reset (DEVICE *dptr); t_stat td_attach (UNIT *uptr, char *cptr); t_stat td_detach (UNIT *uptr); t_stat td_boot (int32 unitno, DEVICE *dptr); t_bool td_newsa (int32 newf); t_bool td_setpos (UNIT *uptr); int32 td_header (UNIT *uptr, int32 blk, int32 line); int32 td_trailer (UNIT *uptr, int32 blk, int32 line); int32 td_read (UNIT *uptr, int32 blk, int32 line); void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb); int32 td_set_mtk (int32 code, int32 u, int32 k); t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc); extern uint16 M[]; extern int32 sim_switches; extern int32 sim_is_running; /* TD data structures td_dev DT device descriptor td_unit DT unit list td_reg DT register list td_mod DT modifier list */ DIB td_dib = { DEV_TD8E, 1, { &td77 } }; UNIT td_unit[] = { { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } }; REG td_reg[] = { { GRDATA (TDCMD, td_cmd, 8, 4, 8) }, { ORDATA (TDDAT, td_dat, 12) }, { ORDATA (TDMTK, td_mtk, 6) }, { FLDATA (TDSLF, td_slf, 0) }, { FLDATA (TDQLF, td_qlf, 0) }, { FLDATA (TDTME, td_tme, 0) }, { ORDATA (TDQL, td_qlctr, 2) }, { ORDATA (TDCSUM, td_csum, 6), REG_RO }, { DRDATA (LTIME, td_ltime, 31), REG_NZ | PV_LEFT }, { DRDATA (DCTIME, td_dctime, 31), REG_NZ | PV_LEFT }, { URDATA (POS, td_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (STATT, td_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO) }, { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0, DT_NUMDR, REG_HRO) }, { FLDATA (STOP_OFFR, td_stopoffr, 0) }, { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO }, { NULL } }; MTAB td_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos }, { 0 } }; DEVICE td_dev = { "TD", td_unit, td_reg, td_mod, DT_NUMDR, 8, 24, 1, 8, 12, NULL, NULL, &td_reset, &td_boot, &td_attach, &td_detach, &td_dib, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 td77 (int32 IR, int32 AC) { int32 pulse = IR & 07; int32 u = TDC_GETUNIT (td_cmd); /* get unit */ int32 diff, t; switch (pulse) { case 01: /* SDSS */ if (td_slf) return AC | IOT_SKP; break; case 02: /* SDST */ if (td_tme) return AC | IOT_SKP; break; case 03: /* SDSQ */ if (td_qlf) return AC | IOT_SKP; break; case 04: /* SDLC */ td_tme = 0; /* clear tim err */ diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */ td_cmd = AC & TDC_MASK; /* update cmd */ if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */ if (td_newsa (td_cmd)) /* new command */ return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON); } break; case 05: /* SDLD */ td_slf = 0; /* clear flags */ td_qlf = 0; td_qlctr = 0; td_dat = AC; /* load data reg */ break; case 06: /* SDRC */ td_slf = 0; /* clear flags */ td_qlf = 0; td_qlctr = 0; t = td_cmd | td_mtk; /* form status */ if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */ t = t | TDS_TME; if (td_unit[u].flags & UNIT_WPRT) /* write locked? */ t = t | TDS_WLO; return t; /* return status */ case 07: /* SDRD */ td_slf = 0; /* clear flags */ td_qlf = 0; td_qlctr = 0; return td_dat; /* return data */ } return AC; } /* Command register change (start/stop, forward/reverse, new unit) 1. If change in motion, stop to start - schedule up to speed - set function as next state 2. If change in motion, start to stop, or change in direction - schedule stop */ t_bool td_newsa (int32 newf) { int32 prev_mving, new_mving, prev_dir, new_dir; UNIT *uptr; uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */ if ((uptr->flags & UNIT_ATT) == 0) /* new unit attached? */ return FALSE; new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */ prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */ new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */ prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */ td_mtk = 0; /* mark trk reg cleared */ if (!prev_mving && !new_mving) /* stop from stop? */ return FALSE; if (new_mving && !prev_mving) { /* start from stop? */ if (td_setpos (uptr)) /* update pos */ return TRUE; sim_cancel (uptr); /* stop current */ sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */ uptr->STATE = STA_ACC | new_dir; /* set status */ td_slf = td_qlf = td_qlctr = 0; /* clear state */ return FALSE; } if ((prev_mving && !new_mving) || /* stop from moving? */ (prev_dir != new_dir)) { /* dir chg while moving? */ if (uptr->STATE >= STA_ACC) { /* not stopping? */ if (td_setpos (uptr)) /* update pos */ return TRUE; sim_cancel (uptr); /* stop current */ sim_activate (uptr, td_dctime); /* schedule decel */ uptr->STATE = STA_DEC | prev_dir; /* set status */ td_slf = td_qlf = td_qlctr = 0; /* clear state */ } return FALSE; } return FALSE; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/td_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool td_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (uptr->STATE & ~STA_DIR) { /* case on motion */ case STA_STOP: /* stop */ delta = 0; break; case STA_DEC: /* slowing */ ulin = ut / (uint32) td_ltime; udelt = td_dctime / td_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case STA_ACC: /* accelerating */ ulin = ut / (uint32) td_ltime; udelt = (td_dctime - (td_dctime >> 2)) / td_ltime; delta = (ulin * ulin) / (2 * udelt); break; case STA_UTS: /* at speed */ delta = ut / (uint32) td_ltime; break; } if (uptr->STATE & STA_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel */ sim_cancel (uptr); /* no timing pulses */ return TRUE; } return FALSE; } /* Unit service - unit is either changing speed, or it is up to speed */ t_stat td_svc (UNIT *uptr) { int32 mot = uptr->STATE & ~STA_DIR; int32 dir = uptr->STATE & STA_DIR; int32 unum = uptr - td_dev.units; int32 su = TDC_GETUNIT (td_cmd); int32 mtkb, datb; /* Motion cases Decelerating - if go, next state must be accel as specified by td_cmd Accelerating - next state must be up to speed, fall through Up to speed - process line */ if (mot == STA_STOP) /* stopped? done */ return SCPE_OK; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ uptr->STATE = uptr->pos = 0; /* also done */ return SCPE_UNATT; } switch (mot) { /* case on motion */ case STA_DEC: /* deceleration */ if (td_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (td_stopoffr, STOP_DTOFF); if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */ uptr->STATE = 0; /* stop */ else { /* selected and go */ uptr->STATE = STA_ACC | /* accelerating */ ((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */ sim_activate (uptr, td_dctime - (td_dctime >> 2)); } return SCPE_OK; case STA_ACC: /* accelerating */ if (td_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (td_stopoffr, STOP_DTOFF); uptr->STATE = STA_UTS | dir; /* set up to speed */ break; case STA_UTS: /* up to speed */ if (dir) /* adjust position */ uptr->pos = uptr->pos - 1; else uptr->pos = uptr->pos + 1; uptr->LASTT = sim_grtime (); /* save time */ if (((int32) uptr->pos < 0) || /* off reel? */ (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) { detach_unit (uptr); return IORETURN (td_stopoffr, STOP_DTOFF); } break; /* check function */ } /* At speed - process the current line Once the TD8E is running at speed, it operates line by line. If reading, the current mark track bit is shifted into the mark track register, and the current data nibble (3b) is shifted into the data register. If writing, the current mark track bit is shifted into the mark track register, the top nibble from the data register is written to tape, and the data register is shifted up. The complexity here comes from synthesizing the mark track, based on tape position, and the header data. */ sim_activate (uptr, td_ltime); /* sched next line */ if (unum != su) /* not sel? done */ return SCPE_OK; td_slf = 1; /* set single */ td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */ if (td_qlctr == 0) { /* lines mod 4? */ if (td_qlf) { /* quad line set? */ td_tme = 1; /* timing error */ td_cmd = td_cmd & ~TDC_RW; /* clear write */ } else td_qlf = 1; /* no, set quad */ } datb = 0; /* assume no data */ if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */ mtkb = MTK_BIT (MTK_REV_END, uptr->pos); else if (uptr->pos < DT_EZLIN) /* rev buffer? */ mtkb = MTK_BIT (MTK_INTER, uptr->pos); else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ if (lineno < DT_HTLIN) { /* header? */ if ((td_cmd & TDC_RW) == 0) /* read? */ datb = td_header (uptr, blkno, lineno); /* get nibble */ } else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */ if (td_cmd & TDC_RW) /* write? */ td_write (uptr, blkno, /* write data nibble */ lineno - DT_HTLIN, /* data rel line num */ (td_dat >> 9) & 07); else datb = td_read (uptr, blkno, /* no, read */ lineno - DT_HTLIN); } else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */ datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */ (DTU_LPERB (uptr) - DT_HTLIN)); mtkb = tdb_mtk[unum][lineno]; } else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN)) mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */ else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */ if (dir) { /* reverse? */ mtkb = mtkb ^ 01; /* complement mark bit, */ datb = datb ^ 07; /* data bits */ } td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */ td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */ return SCPE_OK; } /* Header read - reads out 18b words in 3b increments word lines contents 0 0-5 0 1 6-11 block number 2 12-17 0 3 18-23 0 4 24-29 reverse checksum (0777777) */ int32 td_header (UNIT *uptr, int32 blk, int32 line) { int32 nibp; switch (line) { case 8: case 9: case 10: case 11: /* block num */ nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC)); return (blk >> nibp) & 07; case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */ return 07; /* 777777 */ default: return 0; } } /* Trailer read - reads out 18b words in 3b increments Checksum is stored to avoid double calculation word lines contents 0 0-5 forward checksum (lines 0-1, rest 0) 1 6-11 0 2 12-17 0 3 18-23 reverse block mark 4 24-29 0 Note that the reverse block mark (when read forward) appears as the complement obverse (3b nibbles swapped end for end and complemented). */ int32 td_trailer (UNIT *uptr, int32 blk, int32 line) { int32 nibp, i, ba; int16 *fbuf= (int16 *) uptr->filebuf; switch (line) { case 0: td_csum = 07777; /* init csum */ ba = blk * DTU_BSIZE (uptr); for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */ td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777; td_csum = ((td_csum >> 6) ^ td_csum) & 077; return (td_csum >> 3) & 07; case 1: return (td_csum & 07); case 18: case 19: case 20: case 21: nibp = 3 * (line % DT_LPERMC); return ((blk >> nibp) & 07) ^ 07; default: return 0; } } /* Data read - convert block number/data line # to offset in data array */ int32 td_read (UNIT *uptr, int32 blk, int32 line) { int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ ba = ba + (line / DT_WSIZE); /* block addr */ return (fbuf[ba] >> nibp) & 07; /* get data nibble */ } /* Data write - convert block number/data line # to offset in data array */ void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat) { int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ ba = ba + (line / DT_WSIZE); /* block addr */ fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */ if (ba >= uptr->hwmark) /* upd length */ uptr->hwmark = ba + 1; return; } /* Reset routine */ t_stat td_reset (DEVICE *dptr) { int32 i; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ uptr = td_dev.units + i; if (sim_is_running) { /* CAF? */ if (uptr->STATE >= STA_ACC) { /* accel or uts? */ if (td_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, td_dctime); /* sched decel */ uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } td_slf = td_qlf = td_qlctr = 0; /* clear state */ td_cmd = td_dat = td_mtk = 0; td_csum = 0; return SCPE_OK; } /* Bootstrap routine - OS/8 only 1) Read reverse until reverse end zone (mark track is complement obverse) 2) Read forward until mark track code 031. This is a composite code from the last 4b of the forward block number and the first two bits of the reverse guard (01 -0110 01- 1010). There are 16 lines before the first data word. 3) Store data words from 7354 to end of page. This includes header and trailer words. 4) Continue at location 7400. */ #define BOOT_START 07300 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 01312, /* ST, TAD L4MT ;=2000, reverse */ 04312, /* JMS L4MT ; rev lk for 022 */ 04312, /* JMS L4MT ; fwd lk for 031 */ 06773, /* DAT, SDSQ ; wait for 12b */ 05303, /* JMP .-1 */ 06777, /* SDRD ; read word */ 03726, /* DCA I BUF ; store */ 02326, /* ISZ BUF ; incr ptr */ 05303, /* JMP DAT ; if not 0, cont */ 05732, /* JMP I SCB ; jump to boot */ 02000, /* L4MT,2000 ; overwritten */ 01300, /* TAD ST ; =1312, go */ 06774, /* SDLC ; new command */ 06771, /* MTK, SDSS ; wait for mark */ 05315, /* JMP .-1 */ 06776, /* SDRC ; get mark code */ 00331, /* AND K77 ; mask to 6b */ 01327, /* CMP, TAD MCD ; got target code? */ 07640, /* SZA CLA ; skip if yes */ 05315, /* JMP MTK ; wait for mark */ 02321, /* ISZ CMP ; next target */ 05712, /* JMP I L4MT ; exit */ 07354, /* BUF, 7354 ; loading point */ 07756, /* MCD, -22 ; target 1 */ 07747, /* -31 ; target 2 */ 00077, /* 77 ; mask */ 07400 /* SCB, 7400 ; secondary boot */ }; t_stat td_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; if (unitno) return SCPE_ARG; /* only unit 0 */ if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */ td_unit[unitno].pos = DT_EZLIN; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 16b or 18b, read 16b or 18b format and convert to 12b in buffer If 12b, read data into buffer Set up mark track bit array */ t_stat td_attach (UNIT *uptr, char *cptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k, mtkpb; int32 u = uptr - td_dev.units; t_stat r; uint32 ba, sz; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) /* fail? */ return r; if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; if (sim_switches & SWMASK ('F')) /* att 18b? */ uptr->flags = uptr->flags & ~UNIT_8FMT; else if (sim_switches & SWMASK ('S')) /* att 16b? */ uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ (sz = sim_fsize (uptr->fileref))) { if (sz == D11_FILSIZ) uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (sz > D8_FILSIZ) uptr->flags = uptr->flags & ~UNIT_8FMT; } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (int16)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint16 *) uptr->filebuf; /* file buffer */ printf ("%s%d: ", sim_dname (&td_dev), u); if (uptr->flags & UNIT_8FMT) printf ("12b format"); else if (uptr->flags & UNIT_11FMT) printf ("16b format"); else printf ("18b/36b format"); printf (", buffering file in memory\n"); if (uptr->flags & UNIT_8FMT) /* 12b? */ uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), uptr->capac, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ if (uptr->flags & UNIT_11FMT) { k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; } else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ fbuf[ba] = (pdp18b[k] >> 6) & 07777; fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | ((pdp18b[k + 1] >> 12) & 077); fbuf[ba + 2] = pdp18b[k + 1] & 07777; ba = ba + 3; } /* end blk loop */ } /* end file loop */ uptr->hwmark = ba; } /* end else */ uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ uptr->STATE = STA_STOP; /* stopped */ mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */ k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */ k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */ k = td_set_mtk (MTK_REV_GRD, u, k); for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k); for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k); for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k); k = td_set_mtk (MTK_FWD_GRD, u, k); k = td_set_mtk (MTK_REV_BLK, u, k); k = td_set_mtk (MTK_INTER, u, k); return SCPE_OK; } /* Detach routine If 12b, write buffer to file If 16b or 18b, convert 12b buffer to 16b or 18b and write to file Deallocate buffer */ t_stat td_detach (UNIT* uptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k; int32 u = uptr - td_dev.units; uint32 ba; if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; fbuf = (uint16 *) uptr->filebuf; /* file buffer */ if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u); rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) /* PDP8? */ fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ uptr->hwmark, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ for (k = 0; k < D18_NBSIZE; k = k + 2) { pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | ((uint32) (fbuf[ba + 1] >> 6) & 077); pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | ((uint32) (fbuf[ba + 2] & 07777)); ba = ba + 3; } /* end loop blk */ if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i]; fxwrite (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); } else fxwrite (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); } /* end loop buf */ } /* end else */ if (ferror (uptr->fileref)) perror ("I/O error"); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ uptr->pos = uptr->STATE = 0; sim_cancel (uptr); /* no more pulses */ return detach_unit (uptr); } /* Set mark track code into bit array */ int32 td_set_mtk (int32 code, int32 u, int32 k) { int32 i; for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1; return k; } /* Show position */ t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc) { if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; if (uptr->pos < DT_EZLIN) /* rev end zone? */ fprintf (st, "Reverse end zone\n"); else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ fprintf (st, "Block %d, line %d, ", blkno, lineno); if (lineno < DT_HTLIN) /* header? */ fprintf (st, "header cell %d, nibble %d\n", lineno / DT_LPERMC, lineno % DT_LPERMC); else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */ fprintf (st, "data word %d, nibble %d\n", (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE); else fprintf (st, "trailer cell %d, nibble %d\n", (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC, (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC); } else fprintf (st, "Forward end zone\n"); /* fwd end zone */ return SCPE_OK; } simh-3.8.1/PDP8/pdp8_cpu.c0000644000175000017500000020671311111665260013252 0ustar vlmvlm/* pdp8_cpu.c: PDP-8 CPU simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu central processor 28-Apr-07 RMS Removed clock initialization 30-Oct-06 RMS Added idle and infinite loop detection 30-Sep-06 RMS Fixed SC value after DVI overflow (found by Don North) 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 06-Nov-04 RMS Added =n to SHOW HISTORY 31-Dec-03 RMS Fixed bug in set_cpu_hist 13-Oct-03 RMS Added instruction history Added TSC8-75 support (from Bernhard Baehr) 12-Mar-03 RMS Added logical name support 04-Oct-02 RMS Revamped device dispatching, added device number support 06-Jan-02 RMS Added device enable/disable routines 30-Dec-01 RMS Added old PC queue 16-Dec-01 RMS Fixed bugs in EAE 07-Dec-01 RMS Revised to use new breakpoint package 30-Nov-01 RMS Added RL8A, extended SET/SHOW support 16-Sep-01 RMS Fixed bug in reset routine, added KL8A support 10-Aug-01 RMS Removed register from declarations 17-Jul-01 RMS Moved function prototype 07-Jun-01 RMS Fixed bug in JMS to non-existent memory 25-Apr-01 RMS Added device enable/disable support 18-Mar-01 RMS Added DF32 support 05-Mar-01 RMS Added clock calibration support 15-Feb-01 RMS Added DECtape support 14-Apr-99 RMS Changed t_addr to unsigned The register state for the PDP-8 is: AC<0:11> accumulator MQ<0:11> multiplier-quotient L link flag PC<0:11> program counter IF<0:2> instruction field IB<0:2> instruction buffer DF<0:2> data field UF user flag UB user buffer SF<0:6> interrupt save field The PDP-8 has three instruction formats: memory reference, I/O transfer, and operate. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | op |in|zr| page offset | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+ <0:2> mnemonic action 000 AND AC = AC & M[MA] 001 TAD L'AC = AC + M[MA] 010 DCA M[MA] = AC, AC = 0 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 100 JMS M[MA] = PC, PC = MA + 1 101 JMP PC = MA <3:4> mode action 00 page zero MA = IF'0'IR<5:11> 01 current page MA = IF'PC<0:4>'IR<5:11> 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. Memory reference instructions can access an address space of 32K words. The address space is divided into eight 4K word fields; each field is divided into thirty-two 128 word pages. An instruction can directly address, via its 7b offset, locations 0-127 on page zero or on the current page. All 32k words can be accessed via indirect addressing and the instruction and data field registers. If an indirect address is in locations 0010-0017 of any field, the indirect address is incremented and rewritten to memory before use. The I/O transfer format is as follows: 0 1 2 3 4 5 6 7 8 9 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | op | device | pulse | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+ The IO transfer instruction sends the the specified pulse to the specified I/O device. The I/O device may take data from the AC, return data to the AC, initiate or cancel operations, or skip on status. The operate format is as follows: +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 0| | | | | | | | | operate group 1 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | +--- increment AC 3 | | | | | | +--- rotate 1 or 2 4 | | | | | +--- rotate left 4 | | | | +--- rotate right 4 | | | +--- complement L 2 | | +--- complement AC 2 | +--- clear L 1 +-- clear AC 1 +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 1| | | | | | | | 0| operate group 2 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | +--- halt 3 | | | | | +--- or switch register 3 | | | | +--- reverse skip sense 1 | | | +--- skip on L != 0 1 | | +--- skip on AC == 0 1 | +--- skip on AC < 0 1 +-- clear AC 2 +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 1| | | | | | | | 1| operate group 3 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | \______/ | | | | | | | +--|-----+--- EAE command 3 | | +--- AC -> MQ, 0 -> AC 2 | +--- MQ v AC --> AC 2 +-- clear AC 1 The operate instruction can be microprogrammed to perform operations on the AC, MQ, and link. This routine is the instruction decode routine for the PDP-8. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered unimplemented instruction and stop_inst flag set I/O error in I/O simulator 2. Interrupts. Interrupts are maintained by three parallel variables: dev_done device done flags int_enable interrupt enable flags int_req interrupt requests In addition, int_req contains the interrupt enable flag, the CIF not pending flag, and the ION not pending flag. If all three of these flags are set, and at least one interrupt request is set, then an interrupt occurs. 3. Non-existent memory. On the PDP-8, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes outside the current field (indirect writes) need be checked against actual memory size. 3. Adding I/O devices. These modules must be modified: pdp8_defs.h add device number and interrupt definitions pdp8_sys.c add sim_devices table entry */ #include "pdp8_defs.h" #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] = MA #define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ #define UNIT_NOEAE (1 << UNIT_V_NOEAE) #define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define OP_KSF 06031 /* for idle */ #define HIST_PC 0x40000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { int32 pc; int32 ea; int16 ir; int16 opnd; int16 lac; int16 mq; } InstHistory; uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */ int32 saved_LAC = 0; /* saved L'AC */ int32 saved_MQ = 0; /* saved MQ */ int32 saved_PC = 0; /* saved IF'PC */ int32 saved_DF = 0; /* saved Data Field */ int32 IB = 0; /* Instruction Buffer */ int32 SF = 0; /* Save Field */ int32 emode = 0; /* EAE mode */ int32 gtf = 0; /* EAE gtf flag */ int32 SC = 0; /* EAE shift count */ int32 UB = 0; /* User mode Buffer */ int32 UF = 0; /* User mode Flag */ int32 OSR = 0; /* Switch Register */ int32 tsc_ir = 0; /* TSC8-75 IR */ int32 tsc_pc = 0; /* TSC8-75 PC */ int32 tsc_cdf = 0; /* TSC8-75 CDF flag */ int32 tsc_enb = 0; /* TSC8-75 enabled */ int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ int32 dev_done = 0; /* dev done flags */ int32 int_enable = INT_INIT_ENABLE; /* intr enables */ int32 int_req = 0; /* intr requests */ int32 stop_inst = 0; /* trap on ill inst */ int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */ int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern DEVICE *sim_devices[]; extern FILE *sim_log; extern t_bool sim_idle_enab; 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_bool build_dev_tab (void); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifier list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, 15) }, { ORDATA (AC, saved_LAC, 12) }, { FLDATA (L, saved_LAC, 12) }, { ORDATA (MQ, saved_MQ, 12) }, { ORDATA (SR, OSR, 12) }, { GRDATA (IF, saved_PC, 8, 3, 12) }, { GRDATA (DF, saved_DF, 8, 3, 12) }, { GRDATA (IB, IB, 8, 3, 12) }, { ORDATA (SF, SF, 7) }, { FLDATA (UB, UB, 0) }, { FLDATA (UF, UF, 0) }, { ORDATA (SC, SC, 5) }, { FLDATA (GTF, gtf, 0) }, { FLDATA (EMODE, emode, 0) }, { FLDATA (ION, int_req, INT_V_ION) }, { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, { FLDATA (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING) }, { FLDATA (PWR_INT, int_req, INT_V_PWR) }, { FLDATA (UF_INT, int_req, INT_V_UF) }, { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, { ORDATA (DONE, dev_done, INT_V_DIRECT), REG_RO }, { ORDATA (ENABLE, int_enable, INT_V_DIRECT), REG_RO }, { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { FLDATA (STOP_INST, stop_inst, 0) }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 15, 1, 8, 12, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, 0 }; t_stat sim_instr (void) { int32 IR, MB, IF, DF, LAC, MQ; uint32 PC, MA; int32 device, pulse, temp, iot_data; t_stat reason; /* Restore register state */ if (build_dev_tab ()) return SCPE_STOP; /* build dev_tab */ PC = saved_PC & 007777; /* load local copies */ IF = saved_PC & 070000; DF = saved_DF & 070000; LAC = saved_LAC & 017777; MQ = saved_MQ & 07777; int_req = INT_UPDATE; reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } if (int_req > INT_PENDING) { /* interrupt? */ int_req = int_req & ~INT_ION; /* interrupts off */ SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ IF = IB = DF = UF = UB = 0; /* clear mem ext */ PCQ_ENTRY; /* save old PC */ M[0] = PC; /* save PC in 0 */ PC = 1; /* fetch next from 1 */ } MA = IF | PC; /* form PC */ if (sim_brk_summ && sim_brk_test (MA, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } IR = M[MA]; /* fetch instruction */ PC = (PC + 1) & 07777; /* increment PC */ int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ sim_interval = sim_interval - 1; /* Instruction decoding. The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) are decoded together. This produces 32 decode points, four per major opcode. For IOT, the extra decode points are not useful; for OPR, only the group flag (IR<3>) is used. AND, TAD, ISZ, DCA calculate a full 15b effective address. JMS, JMP calculate a 12b field-relative effective address. Autoindex calculations always occur within the same field as the instruction fetch. The field must exist; otherwise, the instruction fetched would be 0000, and indirect addressing could not occur. Note that MA contains IF'PC. */ if (hst_lnt) { /* history enabled? */ int32 ea; hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */ hst[hst_p].ir = IR; hst[hst_p].lac = LAC; hst[hst_p].mq = MQ; if (IR < 06000) { /* mem ref? */ if (IR & 0200) ea = (MA & 077600) | (IR & 0177); else ea = IF | (IR & 0177); /* direct addr */ if (IR & 0400) { /* indirect? */ if (IR < 04000) { /* mem operand? */ if ((ea & 07770) != 00010) ea = DF | M[ea]; else ea = DF | ((M[ea] + 1) & 07777); } else { /* no, jms/jmp */ if ((ea & 07770) != 00010) ea = IB | M[ea]; else ea = IB | ((M[ea] + 1) & 07777); } } hst[hst_p].ea = ea; /* save eff addr */ hst[hst_p].opnd = M[ea]; /* save operand */ } } switch ((IR >> 7) & 037) { /* decode IR<0:4> */ /* Opcode 0, AND */ case 000: /* AND, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ LAC = LAC & (M[MA] | 010000); break; case 001: /* AND, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ LAC = LAC & (M[MA] | 010000); break; case 002: /* AND, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = LAC & (M[MA] | 010000); break; case 003: /* AND, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = LAC & (M[MA] | 010000); break; /* Opcode 1, TAD */ case 004: /* TAD, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ LAC = (LAC + M[MA]) & 017777; break; case 005: /* TAD, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ LAC = (LAC + M[MA]) & 017777; break; case 006: /* TAD, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = (LAC + M[MA]) & 017777; break; case 007: /* TAD, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = (LAC + M[MA]) & 017777; break; /* Opcode 2, ISZ */ case 010: /* ISZ, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ if (MB == 0) PC = (PC + 1) & 07777; break; case 011: /* ISZ, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ if (MB == 0) PC = (PC + 1) & 07777; break; case 012: /* ISZ, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MB = (M[MA] + 1) & 07777; if (MEM_ADDR_OK (MA)) M[MA] = MB; if (MB == 0) PC = (PC + 1) & 07777; break; case 013: /* ISZ, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MB = (M[MA] + 1) & 07777; if (MEM_ADDR_OK (MA)) M[MA] = MB; if (MB == 0) PC = (PC + 1) & 07777; break; /* Opcode 3, DCA */ case 014: /* DCA, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 015: /* DCA, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 016: /* DCA, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 017: /* DCA, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; LAC = LAC & 010000; break; /* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75: (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF flag is cleared. The address of the JMS instruction is loaded into the ERTB register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF, clearing the interrupt inhibit flag, storing of the return address in the first word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed as usual. */ case 020: /* JMS, dir, zero */ PCQ_ENTRY; MA = IR & 0177; /* dir addr, page zero */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 021: /* JMS, dir, curr */ PCQ_ENTRY; MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 022: /* JMS, indir, zero */ PCQ_ENTRY; MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 023: /* JMS, indir, curr */ PCQ_ENTRY; MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; /* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75: (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF flag is cleared. The address of the JMP instruction is loaded into the ERTB register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual (including the setting of IF, UF and clearing the interrupt inhibit flag). */ case 024: /* JMP, dir, zero */ PCQ_ENTRY; MA = IR & 0177; /* dir addr, page zero */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; /* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */ case 025: /* JMP, dir, curr */ PCQ_ENTRY; MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } if (sim_idle_enab && /* idling enabled? */ (IF == IB)) { /* to same bank? */ if (MA == ((PC - 2) & 07777)) { /* 1) JMP *-1? */ if (!(int_req & (INT_ION|INT_TTI)) && /* iof, TTI flag off? */ (M[IB|((PC - 2) & 07777)] == OP_KSF)) /* next is KSF? */ sim_idle (TMR_CLK, FALSE); /* we're idle */ } /* end JMP *-1 */ else if (MA == ((PC - 1) & 07777)) { /* 2) JMP *? */ if (!(int_req & INT_ION)) /* iof? */ reason = STOP_LOOP; /* then infinite loop */ else if (!(int_req & INT_ALL)) /* ion, not intr? */ sim_idle (TMR_CLK, FALSE); /* we're idle */ } /* end JMP */ } /* end idle enabled */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; case 026: /* JMP, indir, zero */ PCQ_ENTRY; MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; case 027: /* JMP, indir, curr */ PCQ_ENTRY; MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; /* Opcode 7, OPR group 1 */ case 034:case 035: /* OPR, group 1 */ switch ((IR >> 4) & 017) { /* decode IR<4:7> */ case 0: /* nop */ break; case 1: /* CML */ LAC = LAC ^ 010000; break; case 2: /* CMA */ LAC = LAC ^ 07777; break; case 3: /* CMA CML */ LAC = LAC ^ 017777; break; case 4: /* CLL */ LAC = LAC & 07777; break; case 5: /* CLL CML = STL */ LAC = LAC | 010000; break; case 6: /* CLL CMA */ LAC = (LAC ^ 07777) & 07777; break; case 7: /* CLL CMA CML */ LAC = (LAC ^ 07777) | 010000; break; case 010: /* CLA */ LAC = LAC & 010000; break; case 011: /* CLA CML */ LAC = (LAC & 010000) ^ 010000; break; case 012: /* CLA CMA = STA */ LAC = LAC | 07777; break; case 013: /* CLA CMA CML */ LAC = (LAC | 07777) ^ 010000; break; case 014: /* CLA CLL */ LAC = 0; break; case 015: /* CLA CLL CML */ LAC = 010000; break; case 016: /* CLA CLL CMA */ LAC = 07777; break; case 017: /* CLA CLL CMA CML */ LAC = 017777; break; } /* end switch opers */ if (IR & 01) /* IAC */ LAC = (LAC + 1) & 017777; switch ((IR >> 1) & 07) { /* decode IR<8:10> */ case 0: /* nop */ break; case 1: /* BSW */ LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); break; case 2: /* RAL */ LAC = ((LAC << 1) | (LAC >> 12)) & 017777; break; case 3: /* RTL */ LAC = ((LAC << 2) | (LAC >> 11)) & 017777; break; case 4: /* RAR */ LAC = ((LAC >> 1) | (LAC << 12)) & 017777; break; case 5: /* RTR */ LAC = ((LAC >> 2) | (LAC << 11)) & 017777; break; case 6: /* RAL RAR - undef */ LAC = LAC & (IR | 010000); /* uses AND path */ break; case 7: /* RTL RTR - undef */ LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); break; /* uses address path */ } /* end switch shifts */ break; /* end group 1 */ /* OPR group 2. From Bernhard Baehr's description of the TSC8-75: (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with HLT and OSR: Additional to raising a user mode interrupt, the current OPR opcode is moved to the ERIOT register and the ECDF flag is cleared. */ case 036:case 037: /* OPR, groups 2, 3 */ if ((IR & 01) == 0) { /* group 2 */ switch ((IR >> 3) & 017) { /* decode IR<6:8> */ case 0: /* nop */ break; case 1: /* SKP */ PC = (PC + 1) & 07777; break; case 2: /* SNL */ if (LAC >= 010000) PC = (PC + 1) & 07777; break; case 3: /* SZL */ if (LAC < 010000) PC = (PC + 1) & 07777; break; case 4: /* SZA */ if ((LAC & 07777) == 0) PC = (PC + 1) & 07777; break; case 5: /* SNA */ if ((LAC & 07777) != 0) PC = (PC + 1) & 07777; break; case 6: /* SZA | SNL */ if ((LAC == 0) || (LAC >= 010000)) PC = (PC + 1) & 07777; break; case 7: /* SNA & SZL */ if ((LAC != 0) && (LAC < 010000)) PC = (PC + 1) & 07777; break; case 010: /* SMA */ if ((LAC & 04000) != 0) PC = (PC + 1) & 07777; break; case 011: /* SPA */ if ((LAC & 04000) == 0) PC = (PC + 1) & 07777; break; case 012: /* SMA | SNL */ if (LAC >= 04000) PC = (PC + 1) & 07777; break; case 013: /* SPA & SZL */ if (LAC < 04000) PC = (PC + 1) & 07777; break; case 014: /* SMA | SZA */ if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) PC = (PC + 1) & 07777; break; case 015: /* SPA & SNA */ if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) PC = (PC + 1) & 07777; break; case 016: /* SMA | SZA | SNL */ if ((LAC >= 04000) || (LAC == 0)) PC = (PC + 1) & 07777; break; case 017: /* SPA & SNA & SZL */ if ((LAC < 04000) && (LAC != 0)) PC = (PC + 1) & 07777; break; } /* end switch skips */ if (IR & 0200) /* CLA */ LAC = LAC & 010000; if ((IR & 06) && UF) { /* user mode? */ int_req = int_req | INT_UF; /* request intr */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } else { if (IR & 04) /* OSR */ LAC = LAC | OSR; if (IR & 02) /* HLT */ reason = STOP_HALT; } break; } /* end if group 2 */ /* OPR group 3 standard MQA!MQL exchanges AC and MQ, as follows: temp = MQ; MQ = LAC & 07777; LAC = LAC & 010000 | temp; */ temp = MQ; /* group 3 */ if (IR & 0200) /* CLA */ LAC = LAC & 010000; if (IR & 0020) { /* MQL */ MQ = LAC & 07777; LAC = LAC & 010000; } if (IR & 0100) /* MQA */ LAC = LAC | temp; if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { reason = stop_inst; /* EAE not present */ break; } /* OPR group 3 EAE The EAE operates in two modes: Mode A, PDP-8/I compatible Mode B, extended capability Mode B provides eight additional subfunctions; in addition, some of the Mode A functions operate differently in Mode B. The mode switch instructions are decoded explicitly and cannot be microprogrammed with other EAE functions (SWAB performs an MQL as part of standard group 3 decoding). If mode switching is decoded, all other EAE timing is suppressed. */ if (IR == 07431) { /* SWAB */ emode = 1; /* set mode flag */ break; } if (IR == 07447) { /* SWBA */ emode = gtf = 0; /* clear mode, gtf */ break; } /* If not switching modes, the EAE operation is determined by the mode and IR<6,8:10>: <6:10> mode A mode B comments 0x000 NOP NOP 0x001 SCL ACS 0x010 MUY MUY if mode B, next = address 0x011 DVI DVI if mode B, next = address 0x100 NMI NMI if mode B, clear AC if result = 4000'0000 0x101 SHL SHL if mode A, extra shift 0x110 ASR ASR if mode A, extra shift 0x111 LSR LSR if mode A, extra shift 1x000 SCA SCA 1x001 SCA + SCL DAD 1x010 SCA + MUY DST 1x011 SCA + DVI SWBA NOP if not detected earlier 1x100 SCA + NMI DPSZ 1x101 SCA + SHL DPIC must be combined with MQA!MQL 1x110 SCA + ASR DCM must be combined with MQA!MQL 1x111 SCA + LSR SAM EAE instructions which fetch memory operands use the CPU's DEFER state to read the first word; if the address operand is in locations x0010 - x0017, it is autoincremented. */ if (emode == 0) /* mode A? clr gtf */ gtf = 0; switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ case 020: /* mode A, B: SCA */ LAC = LAC | SC; break; case 000: /* mode A, B: NOP */ break; case 021: /* mode B: DAD */ if (emode) { MA = IF | PC; if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MQ = MQ + M[MA]; MA = DF | ((MA + 1) & 07777); LAC = (LAC & 07777) + M[MA] + (MQ >> 12); MQ = MQ & 07777; PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 001: /* mode B: ACS */ if (emode) { SC = LAC & 037; LAC = LAC & 010000; } else { /* mode A: SCL */ SC = (~M[IF | PC]) & 037; PC = (PC + 1) & 07777; } break; case 022: /* mode B: DST */ if (emode) { MA = IF | PC; if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = MQ & 07777; MA = DF | ((MA + 1) & 07777); if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 002: /* MUY */ MA = IF | PC; if (emode) { /* mode B: defer */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ } temp = (MQ * M[MA]) + (LAC & 07777); LAC = (temp >> 12) & 07777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = 014; /* 12 shifts */ break; case 023: /* mode B: SWBA */ if (emode) break; LAC = LAC | SC; /* mode A: SCA then */ case 003: /* DVI */ MA = IF | PC; if (emode) { /* mode B: defer */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ } if ((LAC & 07777) >= M[MA]) { /* overflow? */ LAC = LAC | 010000; /* set link */ MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ SC = 0; /* no shifts */ } else { temp = ((LAC & 07777) << 12) | MQ; MQ = temp / M[MA]; LAC = temp % M[MA]; SC = 015; /* 13 shifts */ } PC = (PC + 1) & 07777; break; case 024: /* mode B: DPSZ */ if (emode) { if (((LAC | MQ) & 07777) == 0) PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 004: /* NMI */ temp = (LAC << 12) | MQ; /* preserve link */ for (SC = 0; ((temp & 017777777) != 0) && (temp & 040000000) == ((temp << 1) & 040000000); SC++) temp = temp << 1; LAC = (temp >> 12) & 017777; MQ = temp & 07777; if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) LAC = LAC & 010000; /* clr if 4000'0000 */ break; case 025: /* mode B: DPIC */ if (emode) { temp = (LAC + 1) & 07777; /* SWP already done! */ LAC = MQ + (temp == 0); MQ = temp; break; } LAC = LAC | SC; /* mode A: SCA then */ case 5: /* SHL */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ if (SC > 25) /* >25? result = 0 */ temp = 0; else temp = ((LAC << 12) | MQ) << SC; /* <=25? shift LAC:MQ */ LAC = (temp >> 12) & 017777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; case 026: /* mode B: DCM */ if (emode) { temp = (-LAC) & 07777; /* SWP already done! */ LAC = (MQ ^ 07777) + (temp == 0); MQ = temp; break; } LAC = LAC | SC; /* mode A: SCA then */ case 6: /* ASR */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ if (LAC & 04000) temp = temp | ~037777777; if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; if (SC > 25) temp = (LAC & 04000)? -1: 0; else temp = temp >> SC; LAC = (temp >> 12) & 017777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; case 027: /* mode B: SAM */ if (emode) { temp = LAC & 07777; LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); break; } LAC = LAC | SC; /* mode A: SCA then */ case 7: /* LSR */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ temp = ((LAC & 07777) << 12) | MQ; /* clear link */ if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; if (SC > 24) /* >24? result = 0 */ temp = 0; else temp = temp >> SC; /* <=24? shift AC:MQ */ LAC = (temp >> 12) & 07777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; } /* end switch */ break; /* end case 7 */ /* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75: (In user mode) Additional to raising a user mode interrupt, the current IOT opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1), the ECDF flag is set, otherwise it is cleared. */ case 030:case 031:case 032:case 033: /* IOT */ if (UF) { /* privileged? */ int_req = int_req | INT_UF; /* request intr */ tsc_ir = IR; /* save instruction */ if ((IR & 07707) == 06201) /* set/clear flag */ tsc_cdf = 1; else tsc_cdf = 0; break; } device = (IR >> 3) & 077; /* device = IR<3:8> */ pulse = IR & 07; /* pulse = IR<9:11> */ iot_data = LAC & 07777; /* AC unchanged */ switch (device) { /* decode IR<3:8> */ case 000: /* CPU control */ switch (pulse) { /* decode IR<9:11> */ case 0: /* SKON */ if (int_req & INT_ION) PC = (PC + 1) & 07777; int_req = int_req & ~INT_ION; break; case 1: /* ION */ int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; break; case 2: /* IOF */ int_req = int_req & ~INT_ION; break; case 3: /* SRQ */ if (int_req & INT_ALL) PC = (PC + 1) & 07777; break; case 4: /* GTF */ LAC = (LAC & 010000) | ((LAC & 010000) >> 1) | (gtf << 10) | (((int_req & INT_ALL) != 0) << 9) | (((int_req & INT_ION) != 0) << 7) | SF; break; case 5: /* RTF */ gtf = ((LAC & 02000) >> 10); UB = (LAC & 0100) >> 6; IB = (LAC & 0070) << 9; DF = (LAC & 0007) << 12; LAC = ((LAC & 04000) << 1) | iot_data; int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; break; case 6: /* SGT */ if (gtf) PC = (PC + 1) & 07777; break; case 7: /* CAF */ gtf = 0; emode = 0; int_req = int_req & INT_NO_CIF_PENDING; dev_done = 0; int_enable = INT_INIT_ENABLE; LAC = 0; reset_all (1); /* reset all dev */ break; } /* end switch pulse */ break; /* end case 0 */ case 020:case 021:case 022:case 023: case 024:case 025:case 026:case 027: /* memory extension */ switch (pulse) { /* decode IR<9:11> */ case 1: /* CDF */ DF = (IR & 0070) << 9; break; case 2: /* CIF */ IB = (IR & 0070) << 9; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 3: /* CDF CIF */ DF = IB = (IR & 0070) << 9; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 4: switch (device & 07) { /* decode IR<6:8> */ case 0: /* CINT */ int_req = int_req & ~INT_UF; break; case 1: /* RDF */ LAC = LAC | (DF >> 9); break; case 2: /* RIF */ LAC = LAC | (IF >> 9); break; case 3: /* RIB */ LAC = LAC | SF; break; case 4: /* RMF */ UB = (SF & 0100) >> 6; IB = (SF & 0070) << 9; DF = (SF & 0007) << 12; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 5: /* SINT */ if (int_req & INT_UF) PC = (PC + 1) & 07777; break; case 6: /* CUF */ UB = 0; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 7: /* SUF */ UB = 1; int_req = int_req & ~INT_NO_CIF_PENDING; break; } /* end switch device */ break; default: reason = stop_inst; break; } /* end switch pulse */ break; /* end case 20-27 */ case 010: /* power fail */ switch (pulse) { /* decode IR<9:11> */ case 1: /* SBE */ break; case 2: /* SPL */ if (int_req & INT_PWR) PC = (PC + 1) & 07777; break; case 3: /* CAL */ int_req = int_req & ~INT_PWR; break; default: reason = stop_inst; break; } /* end switch pulse */ break; /* end case 10 */ default: /* I/O device */ if (dev_tab[device]) { /* dev present? */ iot_data = dev_tab[device] (IR, iot_data); LAC = (LAC & 010000) | (iot_data & 07777); if (iot_data & IOT_SKP) PC = (PC + 1) & 07777; if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; } else reason = stop_inst; /* stop on flag */ break; } /* end switch device */ break; /* end case IOT */ } /* end switch opcode */ } /* end while */ /* Simulation halted */ saved_PC = IF | (PC & 07777); /* save copies */ saved_DF = DF & 070000; saved_LAC = LAC & 017777; saved_MQ = MQ & 07777; pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* end sim_instr */ /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; saved_DF = IB = saved_PC & 070000; UF = UB = gtf = emode = 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] & 07777; 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 & 07777; return SCPE_OK; } /* Memory size change */ 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 device number for a device */ t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newdev; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ if ((r != SCPE_OK) || (newdev == dibp->dev)) return r; dibp->dev = newdev; /* store */ return SCPE_OK; } /* Show device number for a device */ t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "devno=%02o", dibp->dev); if (dibp->num > 1) fprintf (st, "-%2o", dibp->dev + dibp->num - 1); return SCPE_OK; } /* CPU device handler - should never get here! */ int32 bad_dev (int32 IR, int32 AC) { return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ } /* Build device dispatch table */ t_bool build_dev_tab (void) { DEVICE *dptr; DIB *dibp; uint32 i, j; static const uint8 std_dev[] = { 000, 010, 020, 021, 022, 023, 024, 025, 026, 027 }; for (i = 0; i < DEV_MAX; i++) /* clr table */ dev_tab[i] = NULL; for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ dev_tab[std_dev[i]] = &bad_dev; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ for (j = 0; j < dibp->num; j++) { /* loop thru disp */ if (dibp->dsp[j]) { /* any dispatch? */ if (dev_tab[dibp->dev + j]) { /* already filled? */ printf ("%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); if (sim_log) fprintf (sim_log, "%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); return TRUE; } dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ } /* end if dsp */ } /* end for j */ } /* end if enb */ } /* end for i */ return FALSE; } /* 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 l, 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) /* enabled? */ return SCPE_NOFNC; 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 L AC MQ 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? */ l = (h->lac >> 12) & 1; /* link */ fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq); if (h->ir < 06000) fprintf (st, "%05o ", h->ea); else fprintf (st, " "); sim_eval = h->ir; if ((fprint_sym (st, h->pc & ADDRMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %04o", h->ir); if (h->ir < 04000) fprintf (st, " [%04o]", h->opnd); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } simh-3.8.1/PDP8/pdp8_ct.c0000644000175000017500000006737111107355106013076 0ustar vlmvlm/* pdp8_ct.c: PDP-8 cassette tape simulator Copyright (c) 2006-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ct TA8E/TU60 cassette tape 13-Aug-07 RMS Fixed handling of BEOT 06-Aug-07 RMS Foward op at BOT skips initial file gap 30-May-2007 RMS Fixed typo (from Norm Lastovica) Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. Cassette format differs in one very significant way: it has file gaps rather than file marks. If the controller spaces or reads into a file gap and then reverses direction, the file gap is not seen again. This is in contrast to magnetic tapes, where the file mark is a character sequence and is seen again if direction is reversed. In addition, cassettes have an initial file gap which is automatically skipped on forward operations from beginning of tape. Note that the read and write sequences for the cassette are asymmetric: Read: KLSA /SELECT READ KGOA /INIT READ, CLEAR DF KGOA /READ 1ST CHAR, CLEAR DF DCA CHAR : KGOA /READ LAST CHAR, CLEAR DF DCA CHAR KLSA /SELECT CRC MODE KGOA /READ 1ST CRC KGOA /READ 2ND CRC Write: KLSA /SELECT WRITE TAD CHAR /1ST CHAR KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF : TAD CHAR /LAST CHAR KGOA /CHAR TO BUF, CLEAR DF KLSA /SELECT CRC MODE KGOA /WRITE CRC, CLEAR DF */ #include "pdp8_defs.h" #include "sim_tape.h" #define CT_NUMDR 2 /* #drives */ #define FNC u3 /* unit function */ #define UST u4 /* unit status */ #define CT_MAXFR (CT_SIZE) /* max record lnt */ #define CT_SIZE 93000 /* chars/tape */ /* Status Register A */ #define SRA_ENAB 0200 /* enable */ #define SRA_V_UNIT 6 /* unit */ #define SRA_M_UNIT (CT_NUMDR - 1) #define SRA_V_FNC 3 /* function */ #define SRA_M_FNC 07 #define SRA_READ 00 #define SRA_REW 01 #define SRA_WRITE 02 #define SRA_SRF 03 #define SRA_WFG 04 #define SRA_SRB 05 #define SRA_CRC 06 #define SRA_SFF 07 #define SRA_2ND 010 #define SRA_IE 0001 /* int enable */ #define GET_UNIT(x) (((x) >> SRA_V_UNIT) & SRA_M_UNIT) #define GET_FNC(x) (((x) >> SRA_V_FNC) & SRA_M_FNC) /* Function code flags */ #define OP_WRI 01 /* op is a write */ #define OP_REV 02 /* op is rev motion */ #define OP_FWD 04 /* op is fwd motion */ /* Unit status flags */ #define UST_REV (OP_REV) /* last op was rev */ #define UST_GAP 01 /* last op hit gap */ /* Status Register B, ^ = computed on the fly */ #define SRB_WLE 0400 /* "write lock err" */ #define SRB_CRC 0200 /* CRC error */ #define SRB_TIM 0100 /* timing error */ #define SRB_BEOT 0040 /* ^BOT/EOT */ #define SRB_EOF 0020 /* end of file */ #define SRB_EMP 0010 /* ^drive empty */ #define SRB_REW 0004 /* rewinding */ #define SRB_WLK 0002 /* ^write locked */ #define SRB_RDY 0001 /* ^ready */ #define SRB_ALLERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP) #define SRB_XFRERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF) extern int32 int_req, stop_inst; extern UNIT cpu_unit; extern FILE *sim_deb; uint32 ct_sra = 0; /* status reg A */ uint32 ct_srb = 0; /* status reg B */ uint32 ct_db = 0; /* data buffer */ uint32 ct_df = 0; /* data flag */ uint32 ct_write = 0; /* TU60 write flag */ uint32 ct_bptr = 0; /* buf ptr */ uint32 ct_blnt = 0; /* buf length */ int32 ct_stime = 1000; /* start time */ int32 ct_ctime = 100; /* char latency */ uint32 ct_stopioe = 1; /* stop on error */ uint8 *ct_xb = NULL; /* transfer buffer */ static uint8 ct_fnc_tab[SRA_M_FNC + 1] = { OP_FWD, 0 , OP_WRI|OP_FWD, OP_REV, OP_WRI|OP_FWD, OP_REV, 0, OP_FWD }; DEVICE ct_dev; int32 ct70 (int32 IR, int32 AC); t_stat ct_svc (UNIT *uptr); t_stat ct_reset (DEVICE *dptr); t_stat ct_attach (UNIT *uptr, char *cptr); t_stat ct_detach (UNIT *uptr); t_stat ct_boot (int32 unitno, DEVICE *dptr); uint32 ct_updsta (UNIT *uptr); int32 ct_go_start (int32 AC); int32 ct_go_cont (UNIT *uptr, int32 AC); t_stat ct_map_err (UNIT *uptr, t_stat st); UNIT *ct_busy (void); void ct_set_df (t_bool timchk); t_bool ct_read_char (void); uint32 ct_crc (uint8 *buf, uint32 cnt); /* CT data structures ct_dev CT device descriptor ct_unit CT unit list ct_reg CT register list ct_mod CT modifier list */ DIB ct_dib = { DEV_CT, 1, { &ct70 } }; UNIT ct_unit[] = { { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, }; REG ct_reg[] = { { ORDATA (CTSRA, ct_sra, 8) }, { ORDATA (CTSRB, ct_srb, 8) }, { ORDATA (CTDB, ct_db, 8) }, { FLDATA (CTDF, ct_df, 0) }, { FLDATA (RDY, ct_srb, 0) }, { FLDATA (WLE, ct_srb, 8) }, { FLDATA (WRITE, ct_write, 0) }, { FLDATA (INT, int_req, INT_V_CT) }, { DRDATA (BPTR, ct_bptr, 17) }, { DRDATA (BLNT, ct_blnt, 17) }, { DRDATA (STIME, ct_stime, 24), PV_LEFT + REG_NZ }, { DRDATA (CTIME, ct_ctime, 24), PV_LEFT + REG_NZ }, { FLDATA (STOP_IOE, ct_stopioe, 0) }, { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, 0), REG_HRO }, { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, 0), REG_HRO }, { URDATA (POS, ct_unit[0].pos, 10, T_ADDR_W, 0, CT_NUMDR, PV_LEFT | REG_RO) }, { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO }, { NULL } }; MTAB ct_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, NULL, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE ct_dev = { "CT", ct_unit, ct_reg, ct_mod, CT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &ct_reset, &ct_boot, &ct_attach, &ct_detach, &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG }; /* IOT routines */ int32 ct70 (int32 IR, int32 AC) { int32 srb; UNIT *uptr; srb = ct_updsta (NULL); /* update status */ switch (IR & 07) { /* decode IR<9:11> */ case 0: /* KCLR */ ct_reset (&ct_dev); /* reset the world */ break; case 1: /* KSDR */ if (ct_df) AC |= IOT_SKP; break; case 2: /* KSEN */ if (srb & SRB_ALLERR) AC |= IOT_SKP; break; case 3: /* KSBF */ if ((srb & SRB_RDY) && !(srb & SRB_EMP)) AC |= IOT_SKP; break; case 4: /* KLSA */ ct_sra = AC & 0377; ct_updsta (NULL); return ct_sra ^ 0377; case 5: /* KSAF */ if (ct_df || (srb & (SRB_ALLERR|SRB_RDY))) AC |= IOT_SKP; break; case 6: /* KGOA */ ct_df = 0; /* clear data flag */ if (uptr = ct_busy ()) /* op in progress? */ AC = ct_go_cont (uptr, AC); /* yes */ else AC = ct_go_start (AC); /* no, start */ ct_updsta (NULL); break; case 7: /* KSRB */ return srb & 0377; } /* end switch */ return AC; } /* Start a new operation - cassette is not busy */ int32 ct_go_start (int32 AC) { UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra); uint32 fnc = GET_FNC (ct_sra); uint32 flg = ct_fnc_tab[fnc]; uint32 old_ust = uptr->UST; if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, ">>CT start: op=%o, old_sta = %o, pos=%d\n", fnc, uptr->UST, uptr->pos); if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) { /* enabled, att? */ ct_srb &= ~(SRB_XFRERR|SRB_REW); /* clear err, rew */ if (flg & OP_WRI) { /* write-type op? */ if (sim_tape_wrp (uptr)) { /* locked? */ ct_srb |= SRB_WLE; /* set flag, abort */ return AC; } ct_write = 1; /* set TU60 wr flag */ ct_db = AC & 0377; } else { ct_write = 0; ct_db = 0; } ct_srb &= ~SRB_BEOT; /* tape in motion */ if (fnc == SRA_REW) /* rew? set flag */ ct_srb |= SRB_REW; if ((fnc != SRA_REW) && !(flg & OP_WRI)) { /* read cmd? */ t_mtrlnt t; t_stat st; uptr->UST = flg & UST_REV; /* save direction */ if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */ if (st != MTSE_TMK) /* not there? */ sim_tape_rewind (uptr); /* restore tap pos */ else old_ust = 0; /* defang next */ } if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */ if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n", fnc, uptr->UST, uptr->pos); if (uptr->UST) /* skip file gap */ sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR); else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); } } else uptr->UST = 0; ct_bptr = 0; /* init buffer */ ct_blnt = 0; uptr->FNC = fnc; /* save function */ sim_activate (uptr, ct_stime); /* schedule op */ } if ((fnc == SRA_READ) || (fnc == SRA_CRC)) /* read or CRC? */ return 0; /* get "char" */ return AC; } /* Continue an in-progress operation - cassette is in motion */ int32 ct_go_cont (UNIT *uptr, int32 AC) { int32 fnc = GET_FNC (ct_sra); switch (fnc) { /* case on function */ case SRA_READ: /* read */ return ct_db; /* return data */ case SRA_WRITE: /* write */ ct_db = AC & 0377; /* save data */ break; case SRA_CRC: /* CRC */ if ((uptr->FNC & SRA_M_FNC) != SRA_CRC) /* if not CRC */ uptr->FNC = SRA_CRC; /* start CRC seq */ if (!ct_write) /* read? AC <- buf */ return ct_db; break; default: break; } return AC; } /* Unit service */ t_stat ct_svc (UNIT *uptr) { uint32 i, crc; uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC]; t_mtrlnt tbc; t_stat st, r; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ ct_updsta (uptr); /* update status */ return (ct_stopioe? SCPE_UNATT: SCPE_OK); } if (((flgs & OP_REV) && sim_tape_bot (uptr)) || /* rev at BOT or */ ((flgs & OP_FWD) && sim_tape_eot (uptr))) { /* fwd at EOT? */ ct_srb |= SRB_BEOT; /* error */ ct_updsta (uptr); /* op done */ return SCPE_OK; } r = SCPE_OK; switch (uptr->FNC) { /* case on function */ case SRA_READ: /* read start */ st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */ if (st == MTSE_RECE) /* rec in err? */ ct_srb |= SRB_CRC; else if (st != MTSE_OK) { /* other error? */ r = ct_map_err (uptr, st); /* map error */ break; } crc = ct_crc (ct_xb, ct_blnt); /* calculate CRC */ ct_xb[ct_blnt++] = (crc >> 8) & 0377; /* append to buffer */ ct_xb[ct_blnt++] = crc & 0377; uptr->FNC |= SRA_2ND; /* next state */ sim_activate (uptr, ct_ctime); /* sched next char */ return SCPE_OK; case SRA_READ|SRA_2ND: /* read char */ if (!ct_read_char ()) /* read, overrun? */ break; ct_set_df (TRUE); /* set data flag */ sim_activate (uptr, ct_ctime); /* sched next char */ return SCPE_OK; case SRA_WRITE: /* write start */ for (i = 0; i < CT_MAXFR; i++) /* clear buffer */ ct_xb[i] = 0; uptr->FNC |= SRA_2ND; /* next state */ sim_activate (uptr, ct_ctime); /* sched next char */ return SCPE_OK; case SRA_WRITE|SRA_2ND: /* write char */ if ((ct_bptr < CT_MAXFR) && /* room in buf? */ ((uptr->pos + ct_bptr) < uptr->capac)) /* room on tape? */ ct_xb[ct_bptr++] = ct_db; /* store char */ ct_set_df (TRUE); /* set data flag */ sim_activate (uptr, ct_ctime); /* sched next char */ return SCPE_OK; case SRA_CRC: /* CRC */ if (ct_write) { /* write? */ if (st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)) /* write, err? */ r = ct_map_err (uptr, st); /* map error */ break; /* write done */ } ct_read_char (); /* get second CRC */ ct_set_df (FALSE); /* set df */ uptr->FNC |= SRA_2ND; /* next state */ sim_activate (uptr, ct_ctime); return SCPE_OK; case SRA_CRC|SRA_2ND: /* second read CRC */ if (ct_bptr != ct_blnt) { /* partial read? */ crc = ct_crc (ct_xb, ct_bptr); /* actual CRC */ if (crc != 0) /* must be zero */ ct_srb |= SRB_CRC; } break; /* read done */ case SRA_WFG: /* write file gap */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = ct_map_err (uptr, st); /* map error */ break; case SRA_REW: /* rewind */ sim_tape_rewind (uptr); ct_srb |= SRB_BEOT; /* set BOT */ break; case SRA_SRB: /* space rev blk */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ r = ct_map_err (uptr, st); /* map error */ break; case SRA_SRF: /* space rev file */ while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; r = ct_map_err (uptr, st); /* map error */ break; case SRA_SFF: /* space fwd file */ while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; r = ct_map_err (uptr, st); /* map error */ break; default: /* never get here! */ return SCPE_IERR; } /* end case */ ct_updsta (uptr); /* update status */ if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n", uptr->FNC, ct_sra, ct_srb, uptr->pos); return r; } /* Update controller status */ uint32 ct_updsta (UNIT *uptr) { int32 srb; if (uptr == NULL) { /* unit specified? */ uptr = ct_busy (); /* use busy unit */ if ((uptr == NULL) && (ct_sra & SRA_ENAB)) /* none busy? */ uptr = ct_dev.units + GET_UNIT (ct_sra); /* use sel unit */ } else if (ct_srb & SRB_EOF) /* save gap */ uptr->UST |= UST_GAP; if (uptr) { /* any unit? */ ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY); /* clear dyn flags */ if ((uptr->flags & UNIT_ATT) == 0) /* unattached? */ ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */ if (!sim_is_active (uptr)) { /* not busy? */ ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW; /* ready, ~rew */ } if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW)) /* locked or rew? */ ct_srb |= SRB_WLK; /* set locked */ } if (ct_sra & SRA_ENAB) /* can TA see TU60? */ srb = ct_srb; else srb = 0; /* no */ if ((ct_sra & SRA_IE) && /* int enabled? */ (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))) /* any flag? */ int_req |= INT_CT; /* set int req */ else int_req &= ~INT_CT; /* no, clr int req */ return srb; } /* Set data flag */ void ct_set_df (t_bool timchk) { if (ct_df && timchk) /* flag still set? */ ct_srb |= SRB_TIM; ct_df = 1; /* set data flag */ if (ct_sra & SRA_IE) /* if ie, int req */ int_req |= INT_CT; return; } /* Read character */ t_bool ct_read_char (void) { if (ct_bptr < ct_blnt) { /* more chars? */ ct_db = ct_xb[ct_bptr++]; return TRUE; } ct_db = 0; ct_srb |= SRB_CRC; /* overrun */ return FALSE; } /* Test if controller busy */ UNIT *ct_busy (void) { uint32 u; UNIT *uptr; for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ uptr = ct_dev.units + u; if (sim_is_active (uptr)) return uptr; } return NULL; } /* Calculate CRC on buffer */ uint32 ct_crc (uint8 *buf, uint32 cnt) { uint32 crc, i, j; crc = 0; for (i = 0; i < cnt; i++) { crc = crc ^ (((uint32) buf[i]) << 8); for (j = 0; j < 8; j++) { if (crc & 1) crc = (crc >> 1) ^ 0xA001; else crc = crc >> 1; } } return crc; } /* Map error status */ t_stat ct_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* unattached */ ct_srb |= SRB_CRC; case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_TMK: /* end of file */ ct_srb |= SRB_EOF; break; case MTSE_IOERR: /* IO error */ ct_srb |= SRB_CRC; /* set crc err */ if (ct_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ ct_srb |= SRB_CRC; /* set crc err */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ ct_srb |= SRB_CRC; /* set crc err */ break; case MTSE_BOT: /* reverse into BOT */ ct_srb |= SRB_BEOT; /* set BOT */ break; case MTSE_WRP: /* write protect */ ct_srb |= SRB_WLE; /* set wlk err */ break; } return SCPE_OK; } /* Reset routine */ t_stat ct_reset (DEVICE *dptr) { uint32 u; UNIT *uptr; ct_sra = 0; ct_srb = 0; ct_df = 0; ct_db = 0; ct_write = 0; ct_bptr = 0; ct_blnt = 0; int_req = int_req & ~INT_CT; /* clear interrupt */ for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ uptr = ct_dev.units + u; sim_cancel (uptr); /* cancel activity */ sim_tape_reset (uptr); /* reset tape */ } if (ct_xb == NULL) ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8)); if (ct_xb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat ct_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; ct_updsta (NULL); uptr->UST = 0; return r; } /* Detach routine */ t_stat ct_detach (UNIT* uptr) { t_stat r; if (!(uptr->flags & UNIT_ATT)) /* check attached */ return SCPE_OK; r = sim_tape_detach (uptr); ct_updsta (NULL); uptr->UST = 0; return r; } /* Bootstrap routine */ #define BOOT_START 04000 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 01237, /* BOOT, TAD M50 /change CRC to REW */ 01206, /* CRCCHK, TAD L260 /crc op */ 06704, /* KLSA /load op */ 06706, /* KGOA /start */ 06703, /* KSBF /ready? */ 05204, /* RDCOD, JMP .-1 /loop */ 07264, /* L260, CML STA RAL /L = 1, AC = halt */ 06702, /* KSEN /error? */ 07610, /* SKP CLA /halt on any error */ 03211, /* DCA . /except REW or FFG */ 03636, /* DCA I PTR /TAD I PTR mustn't change L */ 01205, /* TAD RDCOD /read op */ 06704, /* KLSA /load op */ 06706, /* KGOA /start */ 06701, /* LOOP, KSDF /data ready? */ 05216, /* JMP .-1 /loop */ 07002, /* BSW /to upper 6b */ 07430, /* SZL /second byte? */ 01636, /* TAD I PTR /yes */ 07022, /* CML BSW /swap back */ 03636, /* DCA I PTR /store in mem */ 07420, /* SNL /done with both bytes? */ 02236, /* ISZ PTR /yes, bump mem ptr */ 02235, /* ISZ KNT /done with record? */ 05215, /* JMP LOOP /next byte */ 07346, /* STA CLL RTL */ 07002, /* BSW /AC = 7757 */ 03235, /* STA KNT /now read 200 byte record */ 05201, /* JMP CRCCHK /go check CRC */ 07737, /* KNT, 7737 /1's compl of byte count */ 03557, /* PTR, 3557 /load point */ 07730, /* M50, 7730 /CLA SPA SZL */ }; t_stat ct_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 M[]; if ((ct_dib.dev != DEV_CT) || unitno) /* only std devno */ return STOP_NOTSTD; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_lp.c0000644000175000017500000001431211107355106013066 0ustar vlmvlm/* pdp8_lp.c: PDP-8 line printer simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt LP8E line printer 19-Jan-07 RMS Added UNIT_TEXT 25-Apr-03 RMS Revised for extended file support 04-Oct-02 RMS Added DIB, enable/disable, device number support 30-May-02 RMS Widened POS to 32b */ #include "pdp8_defs.h" extern int32 int_req, int_enable, dev_done, stop_inst; int32 lpt_err = 0; /* error flag */ int32 lpt_stopioe = 0; /* stop on error */ DEVICE lpt_dev; int32 lpt (int32 IR, int32 AC); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); t_stat lpt_detach (UNIT *uptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { DEV_LPT, 1, { &lpt } }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lpt_reg[] = { { ORDATA (BUF, lpt_unit.buf, 8) }, { FLDATA (ERR, lpt_err, 0) }, { FLDATA (DONE, dev_done, INT_V_LPT) }, { FLDATA (ENABLE, int_enable, INT_V_LPT) }, { FLDATA (INT, int_req, INT_V_LPT) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { ORDATA (DEVNUM, lpt_dib.dev, 6), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lpt_reset, NULL, &lpt_attach, &lpt_detach, &lpt_dib, DEV_DISABLE }; /* IOT routine */ int32 lpt (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 1: /* PSKF */ return (dev_done & INT_LPT)? IOT_SKP + AC: AC; case 2: /* PCLF */ dev_done = dev_done & ~INT_LPT; /* clear flag */ int_req = int_req & ~INT_LPT; /* clear int req */ return AC; case 3: /* PSKE */ return (lpt_err)? IOT_SKP + AC: AC; case 6: /* PCLF!PSTB */ dev_done = dev_done & ~INT_LPT; /* clear flag */ int_req = int_req & ~INT_LPT; /* clear int req */ case 4: /* PSTB */ lpt_unit.buf = AC & 0177; /* load buffer */ if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || (lpt_unit.buf == 012)) { sim_activate (&lpt_unit, lpt_unit.wait); return AC; } return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC; case 5: /* PSIE */ int_enable = int_enable | INT_LPT; /* set enable */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 7: /* PCIE */ int_enable = int_enable & ~INT_LPT; /* clear enable */ int_req = int_req & ~INT_LPT; /* clear int req */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat lpt_svc (UNIT *uptr) { dev_done = dev_done | INT_LPT; /* set done */ int_req = INT_UPDATE; /* update interrupts */ if ((uptr->flags & UNIT_ATT) == 0) { lpt_err = 1; return IORETURN (lpt_stopioe, SCPE_UNATT); } fputc (uptr->buf, uptr->fileref); /* print char */ uptr->pos = ftell (uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { lpt_unit.buf = 0; dev_done = dev_done & ~INT_LPT; /* clear done, int */ int_req = int_req & ~INT_LPT; int_enable = int_enable | INT_LPT; /* set enable */ lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; return reason; } /* Detach routine */ t_stat lpt_detach (UNIT *uptr) { lpt_err = 1; return detach_unit (uptr); } simh-3.8.1/PDP8/pdp8_defs.h0000644000175000017500000002416511111665052013407 0ustar vlmvlm/* pdp8_defs.h: PDP-8 simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Aug-07 RMS Added FPP8 support 13-Dec-06 RMS Added TA8E support 30-Oct-06 RMS Added infinite loop stop 13-Oct-03 RMS Added TSC8-75 support 04-Oct-02 RMS Added variable device number support 20-Jan-02 RMS Fixed bug in TTx interrupt enable initialization 25-Nov-01 RMS Added RL8A support 16-Sep-01 RMS Added multiple KL support 18-Mar-01 RMS Added DF32 support 15-Feb-01 RMS Added DECtape support 14-Apr-99 RMS Changed t_addr to unsigned 19-Mar-95 RMS Added dynamic memory size 02-May-94 RMS Added non-existent memory handling The author gratefully acknowledges the help of Max Burnet, Richie Lary, and Bill Haygood in resolving questions about the PDP-8 */ #ifndef _PDP8_DEFS_H_ #define _PDP8_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_NOTSTD 4 /* non-std devno */ #define STOP_DTOFF 5 /* DECtape off reel */ #define STOP_LOOP 6 /* infinite loop */ /* Memory */ #define MAXMEMSIZE 32768 /* max memory size */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* IOT subroutine return codes */ #define IOT_V_SKP 12 /* skip */ #define IOT_V_REASON 13 /* reason */ #define IOT_SKP (1 << IOT_V_SKP) #define IOT_REASON (1 << IOT_V_REASON) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* Timers */ #define TMR_CLK 0 /* timer 0 = clock */ #define TMR_TTX 1 /* timer 1 = TTx */ /* Device information block */ #define DEV_MAXBLK 8 /* max dev block */ #define DEV_MAX 64 /* total devices */ typedef struct { uint32 dev; /* base dev number */ uint32 num; /* number of slots */ int32 (*dsp[DEV_MAXBLK])(int32 IR, int32 dat); } DIB; /* Standard device numbers */ #define DEV_PTR 001 /* paper tape reader */ #define DEV_PTP 002 /* paper tape punch */ #define DEV_TTI 003 /* console input */ #define DEV_TTO 004 /* console output */ #define DEV_CLK 013 /* clock */ #define DEV_TSC 036 #define DEV_KJ8 040 /* extra terminals */ #define DEV_FPP 055 /* floating point */ #define DEV_DF 060 /* DF32 */ #define DEV_RF 060 /* RF08 */ #define DEV_RL 060 /* RL8A */ #define DEV_LPT 066 /* line printer */ #define DEV_MT 070 /* TM8E */ #define DEV_CT 070 /* TA8E */ #define DEV_RK 074 /* RK8E */ #define DEV_RX 075 /* RX8E/RX28 */ #define DEV_DTA 076 /* TC08 */ #define DEV_TD8E 077 /* TD8E */ /* Interrupt flags The interrupt flags consist of three groups: 1. Devices with individual interrupt enables. These record their interrupt requests in device_done and their enables in device_enable, and must occupy the low bit positions. 2. Devices without interrupt enables. These record their interrupt requests directly in int_req, and must occupy the middle bit positions. 3. Overhead. These exist only in int_req and must occupy the high bit positions. Because the PDP-8 does not have priority interrupts, the order of devices within groups does not matter. Note: all extra KL input and output interrupts must be assigned to contiguous bits. */ #define INT_V_START 0 /* enable start */ #define INT_V_LPT (INT_V_START+0) /* line printer */ #define INT_V_PTP (INT_V_START+1) /* tape punch */ #define INT_V_PTR (INT_V_START+2) /* tape reader */ #define INT_V_TTO (INT_V_START+3) /* terminal */ #define INT_V_TTI (INT_V_START+4) /* keyboard */ #define INT_V_CLK (INT_V_START+5) /* clock */ #define INT_V_TTO1 (INT_V_START+6) /* tto1 */ #define INT_V_TTO2 (INT_V_START+7) /* tto2 */ #define INT_V_TTO3 (INT_V_START+8) /* tto3 */ #define INT_V_TTO4 (INT_V_START+9) /* tto4 */ #define INT_V_TTI1 (INT_V_START+10) /* tti1 */ #define INT_V_TTI2 (INT_V_START+11) /* tti2 */ #define INT_V_TTI3 (INT_V_START+12) /* tti3 */ #define INT_V_TTI4 (INT_V_START+13) /* tti4 */ #define INT_V_DIRECT (INT_V_START+14) /* direct start */ #define INT_V_RX (INT_V_DIRECT+0) /* RX8E */ #define INT_V_RK (INT_V_DIRECT+1) /* RK8E */ #define INT_V_RF (INT_V_DIRECT+2) /* RF08 */ #define INT_V_DF (INT_V_DIRECT+3) /* DF32 */ #define INT_V_MT (INT_V_DIRECT+4) /* TM8E */ #define INT_V_DTA (INT_V_DIRECT+5) /* TC08 */ #define INT_V_RL (INT_V_DIRECT+6) /* RL8A */ #define INT_V_CT (INT_V_DIRECT+7) /* TA8E int */ #define INT_V_PWR (INT_V_DIRECT+8) /* power int */ #define INT_V_UF (INT_V_DIRECT+9) /* user int */ #define INT_V_TSC (INT_V_DIRECT+10) /* TSC8-75 int */ #define INT_V_FPP (INT_V_DIRECT+11) /* FPP8 */ #define INT_V_OVHD (INT_V_DIRECT+12) /* overhead start */ #define INT_V_NO_ION_PENDING (INT_V_OVHD+0) /* ion pending */ #define INT_V_NO_CIF_PENDING (INT_V_OVHD+1) /* cif pending */ #define INT_V_ION (INT_V_OVHD+2) /* interrupts on */ #define INT_LPT (1 << INT_V_LPT) #define INT_PTP (1 << INT_V_PTP) #define INT_PTR (1 << INT_V_PTR) #define INT_TTO (1 << INT_V_TTO) #define INT_TTI (1 << INT_V_TTI) #define INT_CLK (1 << INT_V_CLK) #define INT_TTO1 (1 << INT_V_TTO1) #define INT_TTO2 (1 << INT_V_TTO2) #define INT_TTO3 (1 << INT_V_TTO3) #define INT_TTO4 (1 << INT_V_TTO4) #define INT_TTI1 (1 << INT_V_TTI1) #define INT_TTI2 (1 << INT_V_TTI2) #define INT_TTI3 (1 << INT_V_TTI3) #define INT_TTI4 (1 << INT_V_TTI4) #define INT_RX (1 << INT_V_RX) #define INT_RK (1 << INT_V_RK) #define INT_RF (1 << INT_V_RF) #define INT_DF (1 << INT_V_DF) #define INT_MT (1 << INT_V_MT) #define INT_DTA (1 << INT_V_DTA) #define INT_RL (1 << INT_V_RL) #define INT_CT (1 << INT_V_CT) #define INT_PWR (1 << INT_V_PWR) #define INT_UF (1 << INT_V_UF) #define INT_TSC (1 << INT_V_TSC) #define INT_FPP (1 << INT_V_FPP) #define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) #define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING) #define INT_ION (1 << INT_V_ION) #define INT_DEV_ENABLE ((1 << INT_V_DIRECT) - 1) /* devices w/enables */ #define INT_ALL ((1 << INT_V_OVHD) - 1) /* all interrupts */ #define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT) | \ (INT_TTI1+INT_TTI2+INT_TTI3+INT_TTI4) | \ (INT_TTO1+INT_TTO2+INT_TTO3+INT_TTO4) #define INT_PENDING (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING) #define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable)) /* Function prototypes */ t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/PDP8/pdp8_rf.c0000644000175000017500000004264111107355106013070 0ustar vlmvlm/* pdp8_rf.c: RF08 fixed head disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rf RF08 fixed head disk 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) 04-Jan-04 RMS Changed sim_fsize calling sequence 26-Oct-03 RMS Cleaned up buffer copy code 26-Jul-03 RMS Fixed bug in set size routine 14-Mar-03 RMS Fixed variable platter interaction with save/restore 03-Mar-03 RMS Fixed autosizing 02-Feb-03 RMS Added variable platter and autosizing support 04-Oct-02 RMS Added DIB, device number support 28-Nov-01 RMS Added RL8A support 25-Apr-01 RMS Added device enable/disable support 19-Mar-01 RMS Added disk monitor bootstrap, fixed IOT decoding 15-Feb-01 RMS Fixed 3 cycle data break sequence 14-Apr-99 RMS Changed t_addr to unsigned 30-Mar-98 RMS Fixed bug in RF bootstrap The RF08 is a head-per-track disk. It uses the three cycle data break facility. To minimize overhead, the entire RF08 is buffered in memory. Two timing parameters are provided: rf_time Interword timing, must be non-zero rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, DMA occurs in a burst */ #include "pdp8_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT 03 #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define RF_NUMWD 2048 /* words/track */ #define RF_NUMTR 128 /* tracks/disk */ #define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ #define RF_NUMDK 4 /* disks/controller */ #define RF_WC 07750 /* word count */ #define RF_MA 07751 /* mem address */ #define RF_WMASK (RF_NUMWD - 1) /* word mask */ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ #define RF_READ 2 /* read */ #define RF_WRITE 4 /* write */ /* Status register */ #define RFS_PCA 04000 /* photocell status */ #define RFS_DRE 02000 /* data req enable */ #define RFS_WLS 01000 /* write lock status */ #define RFS_EIE 00400 /* error int enable */ #define RFS_PIE 00200 /* photocell int enb */ #define RFS_CIE 00100 /* done int enable */ #define RFS_MEX 00070 /* memory extension */ #define RFS_DRL 00004 /* data late error */ #define RFS_NXD 00002 /* non-existent disk */ #define RFS_PER 00001 /* parity error */ #define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER) #define RFS_V_MEX 3 #define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX)) #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) RF_NUMWD))) #define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \ else rf_sta = rf_sta & ~RFS_PCA #define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \ ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \ ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \ int_req = int_req | INT_RF; \ else int_req = int_req & ~INT_RF extern uint16 M[]; extern int32 int_req, stop_inst; extern UNIT cpu_unit; int32 rf_sta = 0; /* status register */ int32 rf_da = 0; /* disk address */ int32 rf_done = 0; /* done flag */ int32 rf_wlk = 0; /* write lock */ int32 rf_time = 10; /* inter-word time */ int32 rf_burst = 1; /* burst mode flag */ int32 rf_stopioe = 1; /* stop on error */ DEVICE rf_dev; int32 rf60 (int32 IR, int32 AC); int32 rf61 (int32 IR, int32 AC); int32 rf62 (int32 IR, int32 AC); int32 rf64 (int32 IR, int32 AC); t_stat rf_svc (UNIT *uptr); t_stat pcell_svc (UNIT *uptr); t_stat rf_reset (DEVICE *dptr); t_stat rf_boot (int32 unitno, DEVICE *dptr); t_stat rf_attach (UNIT *uptr, char *cptr); t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* RF08 data structures rf_dev RF device descriptor rf_unit RF unit descriptor pcell_unit photocell timing unit (orphan) rf_reg RF register list */ DIB rf_dib = { DEV_RF, 5, { &rf60, &rf61, &rf62, NULL, &rf64 } }; UNIT rf_unit = { UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE) }; UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) }; REG rf_reg[] = { { ORDATA (STA, rf_sta, 12) }, { ORDATA (DA, rf_da, 20) }, { ORDATA (WC, M[RF_WC], 12), REG_FIT }, { ORDATA (MA, M[RF_MA], 12), REG_FIT }, { FLDATA (DONE, rf_done, 0) }, { FLDATA (INT, int_req, INT_V_RF) }, { ORDATA (WLK, rf_wlk, 32) }, { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT }, { FLDATA (BURST, rf_burst, 0) }, { FLDATA (STOP_IOE, rf_stopioe, 0) }, { DRDATA (CAPAC, rf_unit.capac, 21), REG_HRO }, { ORDATA (DEVNUM, rf_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rf_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEVICE rf_dev = { "RF", &rf_unit, rf_reg, rf_mod, 1, 8, 20, 1, 8, 12, NULL, NULL, &rf_reset, &rf_boot, &rf_attach, NULL, &rf_dib, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 rf60 (int32 IR, int32 AC) { int32 t; int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ if (pulse & 1) { /* DCMA */ rf_da = rf_da & ~07777; /* clear DAR<8:19> */ rf_done = 0; /* clear done */ rf_sta = rf_sta & ~RFS_ERR; /* clear errors */ RF_INT_UPDATE; /* update int req */ } if (pulse & 6) { /* DMAR, DMAW */ rf_da = rf_da | AC; /* DAR<8:19> |= AC */ rf_unit.FUNC = pulse & ~1; /* save function */ t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ if (t < 0) /* wrap around? */ t = t + RF_NUMWD; sim_activate (&rf_unit, t * rf_time); /* schedule op */ AC = 0; /* clear AC */ } return AC; } int32 rf61 (int32 IR, int32 AC) { int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ switch (pulse) { /* decode IR<9:11> */ case 1: /* DCIM */ rf_sta = rf_sta & 07007; /* clear STA<3:8> */ int_req = int_req & ~INT_RF; /* clear int req */ sim_cancel (&pcell_unit); /* cancel photocell */ return AC; case 2: /* DSAC */ return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP: 0; case 5: /* DIML */ rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */ if (rf_sta & RFS_PIE) /* photocell int? */ sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) * rf_time); else sim_cancel (&pcell_unit); RF_INT_UPDATE; /* update int req */ return 0; /* clear AC */ case 6: /* DIMA */ return rf_sta; /* AC <- STA<0:11> */ } return AC; } int32 rf62 (int32 IR, int32 AC) { int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ if (pulse & 1) { /* DFSE */ if (rf_sta & RFS_ERR) AC = AC | IOT_SKP; } if (pulse & 2) { /* DFSC */ if (pulse & 4) /* for DMAC */ AC = AC & ~07777; else if (rf_done) AC = AC | IOT_SKP; } if (pulse & 4) /* DMAC */ AC = AC | (rf_da & 07777); return AC; } int32 rf64 (int32 IR, int32 AC) { int32 pulse = IR & 07; UPDATE_PCELL; /* update photocell */ switch (pulse) { /* decode IR<9:11> */ case 1: /* DCXA */ rf_da = rf_da & 07777; /* clear DAR<0:7> */ break; case 3: /* DXAL */ rf_da = rf_da & 07777; /* clear DAR<0:7> */ case 2: /* DXAL w/o clear */ rf_da = rf_da | ((AC & 0377) << 12); /* DAR<0:7> |= AC */ AC = 0; /* clear AC */ break; case 5: /* DXAC */ AC = 0; /* clear AC */ case 4: /* DXAC w/o clear */ AC = AC | ((rf_da >> 12) & 0377); /* AC |= DAR<0:7> */ break; default: AC = (stop_inst << IOT_V_REASON) + AC; break; } /* end switch */ if ((uint32) rf_da >= rf_unit.capac) rf_sta = rf_sta | RFS_NXD; else rf_sta = rf_sta & ~RFS_NXD; RF_INT_UPDATE; return AC; } /* Unit service Note that for reads and writes, memory addresses wrap around in the current field. This code assumes the entire disk is buffered. */ t_stat rf_svc (UNIT *uptr) { int32 pa, t, mex; int16 *fbuf = uptr->filebuf; UPDATE_PCELL; /* update photocell */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ rf_sta = rf_sta | RFS_NXD; rf_done = 1; RF_INT_UPDATE; /* update int req */ return IORETURN (rf_stopioe, SCPE_UNATT); } mex = GET_MEX (rf_sta); do { if ((uint32) rf_da >= rf_unit.capac) { /* disk overflow? */ rf_sta = rf_sta | RFS_NXD; break; } M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */ M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ pa = mex | M[RF_MA]; /* add extension */ if (uptr->FUNC == RF_READ) { /* read? */ if (MEM_ADDR_OK (pa)) /* if !nxm */ M[pa] = fbuf[rf_da]; /* read word */ } else { /* write */ t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07); if ((rf_wlk >> t) & 1) /* write locked? */ rf_sta = rf_sta | RFS_WLS; else { /* not locked */ fbuf[rf_da] = M[pa]; /* write word */ if (((uint32) rf_da) >= uptr->hwmark) uptr->hwmark = rf_da + 1; } } rf_da = (rf_da + 1) & 03777777; /* incr disk addr */ } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ sim_activate (&rf_unit, rf_time); /* sched next */ else { rf_done = 1; /* done */ RF_INT_UPDATE; /* update int req */ } return SCPE_OK; } /* Photocell unit service */ t_stat pcell_svc (UNIT *uptr) { rf_sta = rf_sta | RFS_PCA; /* set photocell */ if (rf_sta & RFS_PIE) { /* int enable? */ sim_activate (&pcell_unit, RF_NUMWD * rf_time); int_req = int_req | INT_RF; } return SCPE_OK; } /* Reset routine */ t_stat rf_reset (DEVICE *dptr) { rf_sta = rf_da = 0; rf_done = 1; int_req = int_req & ~INT_RF; /* clear interrupt */ sim_cancel (&rf_unit); sim_cancel (&pcell_unit); return SCPE_OK; } /* Bootstrap routine */ #define OS8_START 07750 #define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) #define DM4_START 00200 #define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) static const uint16 os8_rom[] = { 07600, /* 7750, CLA CLL ; also word count */ 06603, /* 7751, DMAR ; also address */ 06622, /* 7752, DFSC ; done? */ 05352, /* 7753, JMP .-1 ; no */ 05752 /* 7754, JMP @.-2 ; enter boot */ }; static const uint16 dm4_rom[] = { 00200, 07600, /* 0200, CLA CLL */ 00201, 06603, /* 0201, DMAR ; read */ 00202, 06622, /* 0202, DFSC ; done? */ 00203, 05202, /* 0203, JMP .-1 ; no */ 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ 07750, 07576, /* 7750, 7576 ; word count */ 07751, 07576 /* 7751, 7576 ; address */ }; t_stat rf_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 sim_switches, saved_PC; if (rf_dib.dev != DEV_RF) /* only std devno */ return STOP_NOTSTD; if (sim_switches & SWMASK ('D')) { for (i = 0; i < DM4_LEN; i = i + 2) M[dm4_rom[i]] = dm4_rom[i + 1]; saved_PC = DM4_START; } else { for (i = 0; i < OS8_LEN; i++) M[OS8_START + i] = os8_rom[i]; saved_PC = OS8_START; } return SCPE_OK; } /* Attach routine */ t_stat rf_attach (UNIT *uptr, char *cptr) { uint32 sz, p; uint32 ds_bytes = RF_DKSIZE * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= RF_NUMDK) p = RF_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; return attach_unit (uptr, cptr); } /* Change disk size */ t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETP (val) * RF_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return SCPE_OK; } simh-3.8.1/PDP8/pdp8_fpp.c0000644000175000017500000013647411107355106013256 0ustar vlmvlm/* pdp8_fpp.c: PDP-8 floating point processor (FPP8A) Copyright (c) 2007-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. fpp FPP8A floating point processor Floating point formats: 00 01 02 03 04 05 06 07 08 09 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | S| hi integer | : double precision +--+--+--+--+--+--+--+--+--+--+--+--+ | lo integer | +--+--+--+--+--+--+--+--+--+--+--+--+ 00 01 02 03 04 05 06 07 08 09 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | S| exponent | : floating point +--+--+--+--+--+--+--+--+--+--+--+--+ | S| hi fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ | lo fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ 00 01 02 03 04 05 06 07 08 09 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | S| exponent | : extended precision +--+--+--+--+--+--+--+--+--+--+--+--+ | S| hi fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ | next fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ | next fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ | next fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ | lo fraction | +--+--+--+--+--+--+--+--+--+--+--+--+ Exponents are 2's complement, as are fractions. Normalized numbers have the form: 0.0...0 0. 1. 1.1...0 Note that 1.0...0 is normalized but considered illegal, since it cannot be represented as a positive number. When a result is normalized, 1.0...0 is converted to 1.1...0 with exp+1. */ #include "pdp8_defs.h" extern int32 int_req; extern int32 sim_switches; extern int32 sim_interval; extern uint16 M[]; extern int32 stop_inst; extern UNIT cpu_unit; #define SEXT12(x) (((x) & 04000)? (x) | ~07777: (x) & 03777) /* Index registers are in memory */ #define fpp_read_xr(xr) fpp_read (fpp_xra + xr) #define fpp_write_xr(xr,d) fpp_write (fpp_xra +xr, d) /* Command register */ #define FPC_DP 04000 /* integer double */ #define FPC_UNFX 02000 /* exit on fl undf */ #define FPC_FIXF 01000 /* lock mem field */ #define FPC_IE 00400 /* int enable */ #define FPC_V_FAST 4 /* startup bits */ #define FPC_M_FAST 017 #define FPC_LOCK 00010 /* lockout */ #define FPC_V_APTF 0 #define FPC_M_APTF 07 /* apta field */ #define FPC_STA (FPC_DP|FPC_LOCK) #define FPC_GETFAST(x) (((x) >> FPC_V_FAST) & FPC_M_FAST) #define FPC_GETAPTF(x) (((x) >> FPC_V_APTF) & FPC_M_APTF) /* Status register */ #define FPS_DP (FPC_DP) /* integer double */ #define FPS_TRPX 02000 /* trap exit */ #define FPS_HLTX 01000 /* halt exit */ #define FPS_DVZX 00400 /* div zero exit */ #define FPS_IOVX 00200 /* int ovf exit */ #define FPS_FOVX 00100 /* flt ovf exit */ #define FPS_UNF 00040 /* underflow */ #define FPS_UNFX 00020 /* undf exit */ #define FPS_XXXM 00010 /* FADDM/FMULM */ #define FPS_LOCK (FPC_LOCK) /* lockout */ #define FPS_EP 00004 /* ext prec */ #define FPS_PAUSE 00002 /* paused */ #define FPS_RUN 00001 /* running */ /* Floating point number: 3-6 words */ #define FPN_FRSIGN 04000 #define FPN_NFR_FP 2 /* std precision */ #define FPN_NFR_EP 5 /* ext precision */ #define EXACT (uint32)((fpp_sta & FPS_EP)? FPN_NFR_EP: FPN_NFR_FP) #define EXTEND ((uint32) FPN_NFR_EP) typedef struct { int32 exp; uint32 fr[FPN_NFR_EP]; } FPN; uint32 fpp_apta; /* APT pointer */ uint32 fpp_aptsvf; /* APT saved field */ uint32 fpp_opa; /* operand pointer */ uint32 fpp_fpc; /* FP PC */ uint32 fpp_bra; /* base reg pointer */ uint32 fpp_xra; /* indx reg pointer */ uint32 fpp_cmd; /* command */ uint32 fpp_sta; /* status */ uint32 fpp_flag; /* flag */ FPN fpp_ac; /* FAC */ static FPN fpp_zero = { 0, { 0, 0, 0, 0, 0 } }; static FPN fpp_one = { 1, { 02000, 0, 0, 0, 0 } }; DEVICE fpp_dev; int32 fpp55 (int32 IR, int32 AC); int32 fpp56 (int32 IR, int32 AC); void fpp_load_apt (uint32 apta); void fpp_dump_apt (uint32 apta, uint32 sta); uint32 fpp_1wd_dir (uint32 ir); uint32 fpp_2wd_dir (uint32 ir); uint32 fpp_indir (uint32 ir); uint32 fpp_ad15 (uint32 hi); uint32 fpp_adxr (uint32 ir, uint32 base_ad); t_bool fpp_add (FPN *a, FPN *b, uint32 sub); t_bool fpp_mul (FPN *a, FPN *b); t_bool fpp_div (FPN *a, FPN *b); t_bool fpp_imul (FPN *a, FPN *b); uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b); void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b); void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b); t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b); uint32 fpp_fr_neg (uint32 *a, uint32 cnt); int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt); int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt); uint32 fpp_fr_abs (uint32 *a, uint32 *b, uint32 cnt); void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt); void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt); void fpp_fr_lsh12 (uint32 *a, uint32 cnt); void fpp_fr_lsh1 (uint32 *a, uint32 cnt); void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt); void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt); t_bool fpp_cond_met (uint32 cond); t_bool fpp_norm (FPN *a, uint32 cnt); void fpp_round (FPN *a); t_bool fpp_test_xp (FPN *a); void fpp_copy (FPN *a, FPN *b); void fpp_zcopy (FPN *a, FPN *b); void fpp_read_op (uint32 ea, FPN *a); void fpp_write_op (uint32 ea, FPN *a); uint32 fpp_read (uint32 ea); void fpp_write (uint32 ea, uint32 val); uint32 apt_read (uint32 ea); void apt_write (uint32 ea, uint32 val); t_stat fpp_svc (UNIT *uptr); t_stat fpp_reset (DEVICE *dptr); /* FPP data structures fpp_dev FPP device descriptor fpp_unit FPP unit descriptor fpp_reg FPP register list */ DIB fpp_dib = { DEV_FPP, 2, { &fpp55, &fpp56 } }; UNIT fpp_unit = { UDATA (&fpp_svc, 0, 0) }; REG fpp_reg[] = { { ORDATA (FPACE, fpp_ac.exp, 12) }, { ORDATA (FPAC0, fpp_ac.fr[0], 12) }, { ORDATA (FPAC1, fpp_ac.fr[1], 12) }, { ORDATA (FPAC2, fpp_ac.fr[2], 12) }, { ORDATA (FPAC3, fpp_ac.fr[3], 12) }, { ORDATA (FPAC4, fpp_ac.fr[4], 12) }, { ORDATA (CMD, fpp_cmd, 12) }, { ORDATA (STA, fpp_sta, 12) }, { ORDATA (APTA, fpp_apta, 15) }, { GRDATA (APTSVF, fpp_aptsvf, 8, 3, 12) }, { ORDATA (FPC, fpp_fpc, 15) }, { ORDATA (BRA, fpp_bra, 15) }, { ORDATA (XRA, fpp_xra, 15) }, { ORDATA (OPA, fpp_opa, 15) }, { FLDATA (FLAG, fpp_flag, 0) }, { NULL } }; DEVICE fpp_dev = { "FPP", &fpp_unit, fpp_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &fpp_reset, NULL, NULL, NULL, &fpp_dib, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 fpp55 (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 1: /* FPINT */ return (fpp_flag? IOT_SKP | AC: AC); /* skip on flag */ case 2: /* FPICL */ fpp_reset (&fpp_dev); /* reset device */ break; case 3: /* FPCOM */ if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ fpp_cmd = AC; /* load cmd */ fpp_sta = (fpp_sta & ~FPC_STA) | /* copy flags */ (fpp_cmd & FPC_STA); /* to status */ } break; case 4: /* FPHLT */ if (fpp_sta & FPS_RUN) { /* running? */ if (fpp_sta & FPS_PAUSE) /* paused? */ fpp_fpc = (fpp_fpc - 1) & ADDRMASK; /* decr FPC */ sim_cancel (&fpp_unit); /* stop execution */ fpp_dump_apt (fpp_apta, FPS_HLTX); /* dump APT */ } else sim_activate (&fpp_unit, 0); /* single step */ break; case 5: /* FPST */ if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ fpp_apta = (FPC_GETAPTF (fpp_cmd) << 12) | AC; fpp_load_apt (fpp_apta); /* load APT */ sim_activate (&fpp_unit, 0); /* start unit */ return IOT_SKP | AC; } if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == (FPS_RUN|FPS_PAUSE)) { fpp_sta &= ~FPS_PAUSE; /* continue */ sim_activate (&fpp_unit, 0); /* start unit */ return (IOT_SKP | AC); } break; case 6: /* FPRST */ return fpp_sta; case 7: /* FPIST */ if (fpp_flag) { /* if flag set */ uint32 old_sta = fpp_sta; fpp_flag = 0; /* clr flag, status */ fpp_sta = 0; int_req &= ~INT_FPP; /* clr int req */ return IOT_SKP | old_sta; /* ret old status */ } break; default: return (stop_inst << IOT_V_REASON) | AC; } /* end switch */ return AC; } int32 fpp56 (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 7: /* FPEP */ if ((AC & 04000) && !(fpp_sta & FPS_RUN)) /* if AC0, not run, */ fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; /* set ep */ break; default: return (stop_inst << IOT_V_REASON) | AC; } /* end switch */ return AC; } /* Service routine */ t_stat fpp_svc (UNIT *uptr) { FPN x; uint32 ir, op, op2, op3, ad, ea, wd; uint32 i; fpp_ac.exp = SEXT12 (fpp_ac.exp); /* sext AC exp */ do { /* repeat */ ir = fpp_read (fpp_fpc); /* get instr */ fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FP PC */ op = (ir >> 7) & 037; /* get op+mode */ op2 = (ir >> 3) & 017; /* get subop */ op3 = ir & 07; /* get field/xr */ fpp_sta &= ~FPS_XXXM; /* not mem op */ switch (op) { /* case on op+mode */ case 000: /* operates */ switch (op2) { /* case on subop */ case 000: /* no-operands */ switch (op3) { /* case on subsubop */ case 0: /* FEXIT */ fpp_dump_apt (fpp_apta, 0); break; case 1: /* FPAUSE */ fpp_sta |= FPS_PAUSE; break; case 2: /* FCLA */ fpp_copy (&fpp_ac, &fpp_zero); /* clear FAC */ break; case 3: /* FNEG */ fpp_fr_neg (fpp_ac.fr, EXACT); /* do exact length */ break; case 4: /* FNORM */ if (!(fpp_sta & FPS_DP)) { /* fp or ep only */ fpp_copy (&x, &fpp_ac); /* copy AC */ fpp_norm (&x, EXACT); /* do exact length */ if (!fpp_test_xp (&x)) /* no trap? */ fpp_copy (&fpp_ac, &x); /* copy back */ } break; case 5: /* STARTF */ if (fpp_sta & FPS_EP) { /* if ep, */ fpp_copy (&x, &fpp_ac); /* copy AC */ fpp_round (&x); /* round */ if (!fpp_test_xp (&x)) /* no trap? */ fpp_copy (&fpp_ac, &x); /* copy back */ } fpp_sta &= ~(FPS_DP|FPS_EP); break; case 6: /* STARTD */ fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; break; case 7: /* JAC */ fpp_fpc = ((fpp_ac.fr[0] & 07) << 12) | fpp_ac.fr[1]; break; } break; case 001: /* ALN */ if (op3 != 0) /* if xr, */ wd = fpp_read_xr (op3); /* use val */ else wd = 027; /* else 23 */ if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ int32 t = wd - fpp_ac.exp; /* alignment */ fpp_ac.exp = SEXT12 (wd); /* new exp */ wd = t & 07777; } if (wd & 04000) /* left? */ fpp_fr_lshn (fpp_ac.fr, 04000 - wd, EXACT); else fpp_fr_algn (fpp_ac.fr, wd, EXACT); break; case 002: /* ATX */ if (fpp_sta & FPS_DP) /* dp? */ fpp_write_xr (op3, fpp_ac.fr[1]); /* xr<-FAC<12:23> */ else { fpp_copy (&x, &fpp_ac); /* copy AC */ wd = (fpp_ac.exp - 027) & 07777; /* shift amt */ if (wd & 04000) /* left? */ fpp_fr_lshn (x.fr, 04000 - wd, EXACT); else fpp_fr_algn (x.fr, wd, EXACT); fpp_write_xr (op3, x.fr[1]); /* xr<-val<12:23> */ } break; case 003: /* XTA */ for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) x.fr[i] = 0; /* clear FOP2-4 */ x.fr[1] = fpp_read_xr (op3); /* get XR value */ x.fr[0] = (x.fr[1] & 04000)? 07777: 0; x.exp = 027; /* standard exp */ if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ fpp_norm (&x, EXACT); /* normalize */ if (fpp_test_xp (&x)) /* exception? */ break; } fpp_copy (&fpp_ac, &x); /* result to AC */ break; case 004: /* NOP */ break; case 005: /* STARTE */ if (!(fpp_sta & FPS_EP)) { fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) fpp_ac.fr[i] = 0; /* clear FAC2-4 */ } break; case 010: /* LDX */ wd = fpp_ad15 (0); /* load XR immed */ fpp_write_xr (op3, wd); break; case 011: /* ADDX */ wd = fpp_ad15 (0); wd = wd + fpp_read_xr (op3); /* add to XR immed */ fpp_write_xr (op3, wd); /* trims to 12b */ break; default: return stop_inst; } /* end case subop */ break; case 001: /* FLDA */ ea = fpp_1wd_dir (ir); fpp_read_op (ea, &fpp_ac); break; case 002: ea = fpp_2wd_dir (ir); fpp_read_op (ea, &fpp_ac); break; case 003: ea = fpp_indir (ir); fpp_read_op (ea, &fpp_ac); break; case 004: /* jumps and sets */ ad = fpp_ad15 (op3); /* get 15b address */ switch (op2) { /* case on subop */ case 000: case 001: case 002: case 003: /* cond jump */ case 004: case 005: case 006: case 007: if (fpp_cond_met (op2)) /* br if cond */ fpp_fpc = ad; break; case 010: /* SETX */ fpp_xra = ad; break; case 011: /* SETB */ fpp_bra = ad; break; case 012: /* JSA */ fpp_write (ad, 01030 + (fpp_fpc >> 12)); /* save return */ fpp_write (ad + 1, fpp_fpc); /* trims to 12b */ fpp_fpc = (ad + 2) & ADDRMASK; break; case 013: /* JSR */ fpp_write (fpp_bra + 1, 01030 + (fpp_fpc >> 12)); fpp_write (fpp_bra + 2, fpp_fpc); /* trims to 12b */ fpp_fpc = ad; break; default: return stop_inst; } /* end case subop */ break; case 005: /* FADD */ ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 0); break; case 006: ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 0); break; case 007: ea = fpp_indir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 0); break; case 010: /* JNX */ ad = fpp_ad15 (op3); /* get 15b addr */ wd = fpp_read_xr (op2 & 07); /* read xr */ if (ir & 00100) { /* inc? */ wd = (wd + 1) & 07777; fpp_write_xr (op2 & 07, wd); /* ++xr */ } if (wd != 0) /* xr != 0? */ fpp_fpc = ad; /* jump */ break; case 011: /* FSUB */ ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 1); break; case 012: ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 1); break; case 013: ea = fpp_indir (ir); fpp_read_op (ea, &x); fpp_add (&fpp_ac, &x, 1); break; case 014: /* TRAP3 */ case 020: /* TRAP4 */ fpp_opa = fpp_ad15 (op3); fpp_dump_apt (fpp_apta, FPS_TRPX); break; case 015: /* FDIV */ ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); fpp_div (&fpp_ac, &x); break; case 016: ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); fpp_div (&fpp_ac, &x); break; case 017: ea = fpp_indir (ir); fpp_read_op (ea, &x); fpp_div (&fpp_ac, &x); break; case 021: /* FMUL */ ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); fpp_mul (&fpp_ac, &x); break; case 022: ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); fpp_mul (&fpp_ac, &x); break; case 023: ea = fpp_indir (ir); fpp_read_op (ea, &x); fpp_mul (&fpp_ac, &x); break; case 024: /* LTR */ fpp_copy (&fpp_ac, (fpp_cond_met (op2 & 07)? &fpp_one: &fpp_zero)); break; case 025: /* FADDM */ fpp_sta |= FPS_XXXM; ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; case 026: fpp_sta |= FPS_XXXM; ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; case 027: fpp_sta |= FPS_XXXM; ea = fpp_indir (ir); fpp_read_op (ea, &x); if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; case 030: /* IMUL/LEA */ ea = fpp_2wd_dir (ir); /* 2-word direct */ if (fpp_sta & FPS_DP) { /* dp? */ fpp_read_op (ea, &x); /* IMUL */ fpp_imul (&fpp_ac, &x); } else { /* LEA */ fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ fpp_ac.fr[0] = (ea >> 12) & 07; fpp_ac.fr[1] = ea & 07777; } break; case 031: /* FSTA */ ea = fpp_1wd_dir (ir); fpp_write_op (ea, &fpp_ac); break; case 032: ea = fpp_2wd_dir (ir); fpp_write_op (ea, &fpp_ac); break; case 033: ea = fpp_indir (ir); fpp_write_op (ea, &fpp_ac); break; case 034: /* IMULI/LEAI */ ea = fpp_indir (ir); /* 1-word indir */ if (fpp_sta & FPS_DP) { /* dp? */ fpp_read_op (ea, &x); /* IMUL */ fpp_imul (&fpp_ac, &x); } else { /* LEA */ fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ fpp_ac.fr[0] = (ea >> 12) & 07; fpp_ac.fr[1] = ea & 07777; } break; case 035: /* FMULM */ fpp_sta |= FPS_XXXM; ea = fpp_1wd_dir (ir); fpp_read_op (ea, &x); if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; case 036: fpp_sta |= FPS_XXXM; ea = fpp_2wd_dir (ir); fpp_read_op (ea, &x); if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; case 037: fpp_sta |= FPS_XXXM; ea = fpp_indir (ir); fpp_read_op (ea, &x); if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ fpp_write_op (ea, &x); /* store result */ break; } /* end sw op+mode */ if (sim_interval) sim_interval = sim_interval - 1; } while ((sim_interval > 0) && ((fpp_sta & (FPS_RUN|FPS_PAUSE|FPS_LOCK)) == (FPS_RUN|FPS_LOCK))); if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == FPS_RUN) sim_activate (uptr, 1); fpp_ac.exp &= 07777; /* mask AC exp */ return SCPE_OK; } /* Address decoding routines */ uint32 fpp_1wd_dir (uint32 ir) { uint32 ad; ad = fpp_bra + ((ir & 0177) * 3); /* base + 3*7b off */ if (fpp_sta & FPS_DP) /* dp? skip exp */ ad = ad + 1; return ad & ADDRMASK; } uint32 fpp_2wd_dir (uint32 ir) { uint32 ad; ad = fpp_ad15 (ir); /* get 15b addr */ return fpp_adxr (ir, ad); /* do indexing */ } uint32 fpp_indir (uint32 ir) { uint32 ad, iad, wd1, wd2; ad = fpp_bra + ((ir & 07) * 3); /* base + 3*3b off */ iad = fpp_adxr (ir, ad); /* do indexing */ wd1 = fpp_read (iad + 1); /* read wds 2,3 */ wd2 = fpp_read (iad + 2); return ((wd1 & 07) << 12) | wd2; /* return addr */ } uint32 fpp_ad15 (uint32 hi) { uint32 ad; ad = ((hi & 07) << 12) | fpp_read (fpp_fpc); /* 15b addr */ fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FPC */ return ad; /* return addr */ } uint32 fpp_adxr (uint32 ir, uint32 base_ad) { uint32 xr, wd; xr = (ir >> 3) & 07; wd = fpp_read_xr (xr); /* get xr */ if (ir & 0100) { /* increment? */ wd = (wd + 1) & 07777; /* inc, rewrite */ fpp_write_xr (xr, wd); } if (xr != 0) { /* indexed? */ if (fpp_sta & FPS_EP) wd = wd * 6; /* scale by len */ else if (fpp_sta & FPS_DP) wd = wd * 2; else wd = wd * 3; return (base_ad + wd) & ADDRMASK; /* return index */ } else return base_ad & ADDRMASK; /* return addr */ } /* Computation routines */ /* Fraction/floating add - return true if overflow */ t_bool fpp_add (FPN *a, FPN *b, uint32 sub) { FPN x, y, z; uint32 ediff, c; fpp_zcopy (&x, a); /* copy opnds */ fpp_zcopy (&y, b); if (sub) /* subtract? */ fpp_fr_neg (y.fr, EXACT); /* neg B, exact */ if (fpp_sta & FPS_DP) { /* dp? */ fpp_fr_add (z.fr, x.fr, y.fr); /* z = a + b */ if ((~x.fr[0] ^ y.fr[0]) & (x.fr[0] ^ z.fr[0]) & FPN_FRSIGN) { fpp_dump_apt (fpp_apta, FPS_IOVX); /* int ovf? */ return TRUE; } } else { /* fp or ep */ if (fpp_fr_test (b->fr, 0, EXACT) == 0) /* B == 0? */ z = x; /* result is A */ else if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* A == 0? */ z = y; /* result is B */ else { /* fp or ep */ if (x.exp < y.exp) { /* |a| < |b|? */ z = x; /* exchange ops */ x = y; y = z; } ediff = x.exp - y.exp; /* exp diff */ z.exp = x.exp; /* result exp */ if (ediff <= (fpp_sta & FPS_EP)? 59: 24) { /* any add? */ if (ediff != 0) /* any align? */ fpp_fr_algn (y.fr, ediff, EXTEND); /* align, 60b */ c = fpp_fr_add (z.fr, x.fr, y.fr); /* add fractions */ if ((((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) == 0) && /* same signs? */ (c || /* carry out? */ ((~x.fr[0] & z.fr[0] & FPN_FRSIGN)))) { /* + to - change? */ fpp_fr_rsh1 (z.fr, c << 11, EXTEND); /* rsh, insert cout */ z.exp = z.exp + 1; /* incr exp */ } /* end same signs */ } /* end in range */ } /* end ops != 0 */ if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ fpp_round (&z); /* round */ if (fpp_test_xp (&z)) /* ovf, unf? */ return TRUE; } /* end else */ fpp_copy (a, &z); /* result is z */ return FALSE; } /* Fraction/floating multiply - return true if overflow */ t_bool fpp_mul (FPN *a, FPN *b) { FPN x, y, z; fpp_zcopy (&x, a); /* copy opnds */ fpp_zcopy (&y, b); if (fpp_sta & FPS_DP) /* dp? */ fpp_fr_mul (z.fr, x.fr, y.fr); /* mult frac */ else { /* fp or ep */ z.exp = x.exp + y.exp; /* add exp */ fpp_fr_mul (z.fr, x.fr, y.fr); /* mult frac */ if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ fpp_round (&z); /* round */ if (fpp_test_xp (&z)) /* ovf, unf? */ return TRUE; } fpp_copy (a, &z); /* result is z */ return FALSE; } /* Fraction/floating divide - return true if div by zero or overflow */ t_bool fpp_div (FPN *a, FPN *b) { FPN x, y, z; if (fpp_fr_test (b->fr, 0, EXACT) == 0) { /* divisor 0? */ fpp_dump_apt (fpp_apta, FPS_DVZX); /* error */ return TRUE; } if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* dividend 0? */ return FALSE; /* quotient is 0 */ fpp_zcopy (&x, a); /* copy opnds */ fpp_zcopy (&y, b); if (fpp_sta & FPS_DP) { /* dp? */ if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ fpp_dump_apt (fpp_apta, FPS_IOVX); /* error */ return TRUE; } } else { /* fp or ep */ fpp_norm (&y, EXACT); /* norm divisor */ if (fpp_fr_test (x.fr, 04000, EXACT) == 0) { /* divd 1.000...? */ x.fr[0] = 06000; /* fix */ x.exp = x.exp + 1; } z.exp = x.exp - y.exp; /* calc exp */ if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ uint32 cin = (a->fr[0] ^ b->fr[0]) & FPN_FRSIGN; fpp_fr_rsh1 (z.fr, cin, EXTEND); /* rsh, insert sign */ z.exp = z.exp + 1; /* incr exp */ } if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ fpp_round (&z); /* round */ if (fpp_test_xp (&z)) /* ovf, unf? */ return TRUE; } fpp_copy (a, &z); /* result is z */ return FALSE; } /* Integer multiply - returns true if overflow */ t_bool fpp_imul (FPN *a, FPN *b) { uint32 sext; FPN x, y, z; fpp_zcopy (&x, a); /* copy args */ fpp_zcopy (&y, b); fpp_fr_mul (z.fr, x.fr, y.fr); /* mult fracs */ sext = (z.fr[2] & FPN_FRSIGN)? 07777: 0; if (((z.fr[0] | z.fr[1] | sext) != 0) && /* hi 25b == 0 */ ((z.fr[0] & z.fr[1] & sext) != 07777)) { /* or 777777774? */ fpp_dump_apt (fpp_apta, FPS_IOVX); return TRUE; } a->fr[0] = z.fr[2]; /* low 24b */ a->fr[1] = z.fr[3]; return FALSE; } /* Auxiliary floating point routines */ t_bool fpp_cond_met (uint32 cond) { switch (cond) { case 0: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0); case 1: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) >= 0); case 2: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) <= 0); case 3: return 1; case 4: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) != 0); case 5: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) < 0); case 6: return (fpp_fr_test (fpp_ac.fr, 0, EXACT) > 0); case 7: return (fpp_ac.exp > 027); } return 0; } /* Normalization - returns TRUE if rounding possible, FALSE if exact */ t_bool fpp_norm (FPN *a, uint32 cnt) { if (fpp_fr_test (a->fr, 0, cnt) == 0) { /* zero? */ a->exp = 0; /* clean exp */ return FALSE; /* don't round */ } while (((a->fr[0] == 0) && !(a->fr[1] & 04000)) || /* lead 13b same? */ ((a->fr[0] = 07777) && (a->fr[1] & 04000))) { fpp_fr_lsh12 (a->fr, cnt); /* move word */ a->exp = a->exp - 12; } while (((a->fr[0] ^ (a->fr[0] << 1)) & FPN_FRSIGN) == 0) { /* until norm */ fpp_fr_lsh1 (a->fr, cnt); /* shift 1b */ a->exp = a->exp - 1; } if (fpp_fr_test (a->fr, 04000, EXACT) == 0) { /* 4000...0000? */ a->fr[0] = 06000; /* chg to 6000... */ a->exp = a->exp + 1; /* with exp+1 */ return FALSE; /* don't round */ } return TRUE; } /* Exact fp number copy */ void fpp_copy (FPN *a, FPN *b) { uint32 i; if (!(fpp_sta & FPS_DP)) a->exp = b->exp; for (i = 0; i < EXACT; i++) a->fr[i] = b->fr[i]; return; } /* Zero extended fp number copy (60b) */ void fpp_zcopy (FPN *a, FPN *b) { uint32 i; a->exp = b->exp; for (i = 0; i < FPN_NFR_EP; i++) { if ((i < FPN_NFR_FP) || (fpp_sta & FPS_EP)) a->fr[i] = b->fr[i]; else a->fr[i] = 0; } return; } /* Test exp for overflow or underflow, returns TRUE on trap */ t_bool fpp_test_xp (FPN *a) { if (a->exp > 2047) { /* overflow? */ fpp_dump_apt (fpp_apta, FPS_FOVX); /* trap */ return TRUE; } if (a->exp < -2048) { /* underflow? */ fpp_sta |= FPS_UNF; /* set flag */ if (fpp_sta & FPS_UNFX) { /* trap? */ fpp_dump_apt (fpp_apta, FPS_UNFX); return TRUE; } fpp_copy (a, &fpp_zero); /* flush to 0 */ } return FALSE; } /* Round dp/fp value */ void fpp_round (FPN *a) { int32 i; uint32 cin, afr0_sign; if (fpp_sta & FPS_EP) /* ep? */ return; /* don't round */ afr0_sign = a->fr[0] & FPN_FRSIGN; /* save input sign */ cin = afr0_sign? 03777: 04000; for (i = FPN_NFR_FP; i >= 0; i--) { /* 3 words */ a->fr[i] = a->fr[i] + cin; /* add in carry */ cin = (a->fr[i] >> 12) & 1; a->fr[i] = a->fr[i] & 07777; } if (!(fpp_sta & FPS_DP) && /* fp? */ (afr0_sign ^ (a->fr[0] & FPN_FRSIGN))) { /* sign change? */ fpp_fr_rsh1 (a->fr, afr0_sign, EXACT); /* rsh, insert sign */ a->exp = a->exp + 1; } return; } /* N-precision integer routines */ /* Fraction add/sub - always carried out to 60b */ uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b) { uint32 i, cin; for (i = FPN_NFR_EP, cin = 0; i > 0; i--) { c[i - 1] = a[i - 1] + b[i - 1] + cin; cin = (c[i - 1] >> 12) & 1; c[i - 1] = c[i - 1] & 07777; } return cin; } void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b) { uint32 i, cin; for (i = FPN_NFR_EP, cin = 0; i > 0; i--) { c[i - 1] = a[i - 1] - b[i - 1] - cin; cin = (c[i - 1] >> 12) & 1; c[i - 1] = c[i - 1] & 07777; } return; } /* Fraction multiply - always develop 60b, multiply is either 24b*24b or 60b*60b This is a signed multiply. The shift in for signed multiply is technically ALU_N XOR ALU_V. This can be simplified as follows: a-sign c-sign result-sign cout overflow N XOR V = shift in 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 1 0 0 1 1 0 0 1 0 0 1 0 1 0 0 1 1 1 0 1 1 1 1 1 1 1 0 1 If a-sign == c-sign, shift-in = a-sign If a-sign != c-sign, shift-in = result-sign */ void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b) { uint32 i, cnt, lo, c_old, cin; fpp_fr_fill (c, 0, EXTEND); /* clr answer */ if (fpp_sta & FPS_EP) /* ep? */ lo = FPN_NFR_EP - 1; /* test <59> */ else lo = FPN_NFR_FP - 1; /* sp, test <23> */ cnt = (lo + 1) * 12; /* # iterations */ for (i = 0; i < cnt; i++) { /* loop thru mpcd */ c_old = c[0]; if (b[lo] & 1) /* mpcd bit set? */ fpp_fr_add (c, a, c); /* add mpyr */ cin = (((a[0] ^ c_old) & FPN_FRSIGN)? c[0]: a[0]) & FPN_FRSIGN; fpp_fr_rsh1 (c, cin, EXTEND); /* shift answer */ fpp_fr_rsh1 (b, 0, EXACT); /* shift mpcd */ } if (a[0] & FPN_FRSIGN) /* mpyr negative? */ fpp_fr_sub (c, c, a); /* adjust result */ return; } /* Fraction divide */ t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b) { uint32 i, old_c, lo, cnt, sign; fpp_fr_fill (c, 0, EXTEND); /* clr answer */ sign = (a[0] ^ b[0]) & FPN_FRSIGN; /* sign of result */ if (a[0] & FPN_FRSIGN) /* |a| */ fpp_fr_neg (a, EXACT); if (b[0] & FPN_FRSIGN); /* |b| */ fpp_fr_neg (b, EXACT); if (fpp_sta & FPS_EP) /* ep? 5 words */ lo = FPN_NFR_EP - 1; else lo = FPN_NFR_FP; /* fp, dp? 3 words */ cnt = (lo + 1) * 12; for (i = 0; i < cnt; i++) { /* loop */ fpp_fr_lsh1 (c, EXTEND); /* shift quotient */ if (fpp_fr_cmp (a, b, EXTEND) >= 0) { /* sub work? */ fpp_fr_sub (a, a, b); /* divd - divr */ if (a[0] & FPN_FRSIGN) /* sign flip? */ return TRUE; /* no, overflow */ c[lo] |= 1; /* set quo bit */ } fpp_fr_lsh1 (a, EXTEND); /* shift dividend */ } old_c = c[0]; /* save hi quo */ if (sign) /* expect neg ans? */ fpp_fr_neg (c, EXTEND); /* -quo */ if (old_c & FPN_FRSIGN) /* sign set before */ return TRUE; /* neg? */ return FALSE; } /* Negate - 24b or 60b */ uint32 fpp_fr_neg (uint32 *a, uint32 cnt) { uint32 i, cin; for (i = cnt, cin = 1; i > 0; i--) { a[i - 1] = (~a[i - 1] + cin) & 07777; cin = (a[i - 1] == 0); } return cin; } /* Test (compare to x'0...0) - 24b or 60b */ int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt) { uint32 i; if (a[0] != v0) return (a[0] & FPN_FRSIGN)? -1: +1; for (i = 1; i < cnt; i++) { if (a[i] != 0) return (a[0] & FPN_FRSIGN)? -1: +1; } return 0; } /* Fraction compare - 24b or 60b */ int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt) { uint32 i; if ((a[0] ^ b[0]) & FPN_FRSIGN) return (b[0] & FPN_FRSIGN)? +1: -1; for (i = 0; i < cnt; i++) { if (a[i] > b[i]) return (b[0] & FPN_FRSIGN)? +1: -1; if (a[i] < b[i]) return (b[0] & FPN_FRSIGN)? -1: +1; } return 0; } /* Fraction fill */ void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt) { uint32 i; for (i = 0; i < cnt; i++) a[i] = v; return; } /* Left shift n (unsigned) */ void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt) { uint32 i; if (sc >= (cnt * 12)) { /* out of range? */ fpp_fr_fill (a, 0, cnt); return; } while (sc >= 12) { /* word shift? */ fpp_fr_lsh12 (a, cnt); sc = sc - 12; } if (sc == 0) /* any more? */ return; for (i = 1; i < cnt; i++) /* bit shift */ a[i - 1] = ((a[i - 1] << sc) | (a[i] >> (12 - sc))) & 07777; a[cnt - 1] = (a[cnt - 1] << sc) & 07777; return; } /* Left shift 12b (unsigned) */ void fpp_fr_lsh12 (uint32 *a, uint32 cnt) { uint32 i; for (i = 1; i < cnt; i++) a[i - 1] = a[i]; a[cnt - 1] = 0; return; } /* Left shift 1b (unsigned) */ void fpp_fr_lsh1 (uint32 *a, uint32 cnt) { uint32 i; for (i = 1; i < cnt; i++) a[i - 1] = ((a[i - 1] << 1) | (a[i] >> 11)) & 07777; a[cnt - 1] = (a[cnt - 1] << 1) & 07777; return; } /* Right shift 1b, with shift in */ void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt) { uint32 i; for (i = cnt - 1; i > 0; i--) a[i] = ((a[i] >> 1) | (a[i - 1] << 11)) & 07777; a[0] = (a[0] >> 1) | sign; return; } /* Right shift n (signed) */ void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt) { uint32 i, sign; sign = (a[0] & FPN_FRSIGN)? 07777: 0; if (sc >= (cnt * 12)) { /* out of range? */ fpp_fr_fill (a, sign, cnt); return; } while (sc >= 12) { for (i = cnt - 1; i > 0; i++) a[i] = a[i - 1]; a[0] = sign; sc = sc - 12; } if (sc == 0) return; for (i = cnt - 1; i > 0; i--) a[i] = ((a[i] >> sc) | (a[i - 1] << (12 - sc))) & 07777; a[0] = ((a[0] >> sc) | (sign << (12 - sc))) & 07777; return; } /* Read/write routines */ void fpp_read_op (uint32 ea, FPN *a) { uint32 i; fpp_opa = ea; if (!(fpp_sta & FPS_DP)) { a->exp = fpp_read (ea++); a->exp = SEXT12 (a->exp); } for (i = 0; i < EXACT; i++) a->fr[i] = fpp_read (ea + i); return; } void fpp_write_op (uint32 ea, FPN *a) { uint32 i; fpp_opa = ea; if (!(fpp_sta & FPS_DP)) fpp_write (ea++, a->exp); for (i = 0; i < EXACT; i++) fpp_write (ea + i, a->fr[i]); return; } uint32 fpp_read (uint32 ea) { ea = ea & ADDRMASK; if (fpp_cmd & FPC_FIXF) ea = fpp_aptsvf | (ea & 07777); return M[ea]; } void fpp_write (uint32 ea, uint32 val) { ea = ea & ADDRMASK; if (fpp_cmd & FPC_FIXF) ea = fpp_aptsvf | (ea & 07777); if (MEM_ADDR_OK (ea)) M[ea] = val & 07777; return; } uint32 apt_read (uint32 ea) { ea = ea & ADDRMASK; return M[ea]; } void apt_write (uint32 ea, uint32 val) { ea = ea & ADDRMASK; if (MEM_ADDR_OK (ea)) M[ea] = val & 07777; return; } /* Utility routines */ void fpp_load_apt (uint32 ad) { uint32 wd0, i; wd0 = apt_read (ad++); fpp_fpc = ((wd0 & 07) << 12) | apt_read (ad++); if (FPC_GETFAST (fpp_cmd) != 017) { fpp_xra = ((wd0 & 00070) << 9) | apt_read (ad++); fpp_bra = ((wd0 & 00700) << 6) | apt_read (ad++); ad++; fpp_ac.exp = apt_read (ad++); for (i = 0; i < EXACT; i++) fpp_ac.fr[i] = apt_read (ad++); } fpp_aptsvf = (ad - 1) & 070000; fpp_sta |= FPS_RUN; return; } void fpp_dump_apt (uint32 ad, uint32 sta) { uint32 wd0, i; wd0 = (fpp_fpc >> 12) & 07; if (FPC_GETFAST (fpp_cmd) != 017) wd0 = wd0 | ((fpp_opa >> 3) & 07000) | ((fpp_bra >> 6) & 00700) | ((fpp_xra >> 9) & 00070); apt_write (ad++, wd0); apt_write (ad++, fpp_fpc); if (FPC_GETFAST (fpp_cmd) != 017) { apt_write (ad++, fpp_xra); apt_write (ad++, fpp_bra); apt_write (ad++, fpp_opa); apt_write (ad++, fpp_ac.exp); for (i = 0; i < EXACT; i++) apt_write (ad++, fpp_ac.fr[i]); } fpp_sta = (fpp_sta | sta) & ~FPS_RUN; fpp_flag = 1; if (fpp_cmd & FPC_IE) int_req |= INT_FPP; return; } /* Reset routine */ t_stat fpp_reset (DEVICE *dptr) { sim_cancel (&fpp_unit); fpp_sta = 0; fpp_cmd = 0; fpp_flag = 0; int_req &= ~INT_FPP; if (sim_switches & SWMASK ('P')) { fpp_apta = 0; fpp_aptsvf = 0; fpp_fpc = 0; fpp_bra = 0; fpp_xra = 0; fpp_opa = 0; fpp_ac = fpp_zero; } return SCPE_OK; } simh-3.8.1/build_mingw_ether.bat0000644000175000017500000000110410032574376015023 0ustar vlmvlm@echo off rem 12-Nov-02 rms Ethernet support rem Compile all of SIMH using MINGW make and gcc environment rem Individual simulator sources are in .\simulator_name rem Individual simulator executables are to .\bin 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;%path% if not exist BIN mkdir BIN gcc -v 1>NUL 2>NUL if ERRORLEVEL 1 echo "MinGW Environment Unavailable" mingw32-make WIN32=1 USE_NETWORK=1 -f makefile %1 %2 %3 %4 simh-3.8.1/VAX/0000755000175000017500000000000011147615611011302 5ustar vlmvlmsimh-3.8.1/VAX/vax_cpu1.c0000644000175000017500000020367411112312544013177 0ustar vlmvlm/* vax_cpu1.c: VAX complex instructions Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 28-May-08 RMS Inlined physical memory routines 29-Apr-07 RMS Separated base register access checks for 11/780 10-May-06 RMS Added access check on system PTE for 11/780 Added mbz check in LDPCTX for 11/780 22-Sep-06 RMS Fixed declarations (from Sterling Garwood) 30-Sep-04 RMS Added conditionals for full VAX Moved emulation to vax_cis.c Moved model-specific IPRs to system module 27-Jan-04 RMS Added device logging support Fixed EXTxV, INSV double register PC reference fault 30-Apr-02 RMS Fixed interrupt/exception handler to clear traps 17-Apr-02 RMS Fixed pos > 31 test in bit fields (should be unsigned) 14-Apr-02 RMS Fixed prv_mode handling for interrupts (found by Tim Stark) Fixed PROBEx to mask mode to 2b (found by Kevin Handy) This module contains the instruction simulators for Field instructions: - BBS, BBC, BBSSI, BBCCI - BBSC, BBCC, BBCS, BBSS - EXTV, EXTZV, CMPV, CMPZV - FFS, FFC, INSV Call/return and push/pop instructions: - CALLS, CALLG, RET - PUSHR, POPR Queue instructions: - INSQUE, REMQUE - INSQHI, INSQTI, REMQHI, REMQTI String instructions: - MOVC3, MOVC5, CMPC3, CMPC5 - LOCC, SKPC, SCANC, SPANC Operating system interface instructions: - CHMK, CHME, CHMS, CHMU - PROBER, PROBEW, REI - MTPR, MFPR - LDPCTX, SVPCTX - (interrupt and exception routine) */ #include "vax_defs.h" static const uint8 rcnt[128] = { 0, 4, 4, 8, 4, 8, 8,12, 4, 8, 8,12, 8,12,12,16, /* 00 - 0F */ 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 10 - 1F */ 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 20 - 2F */ 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 30 - 3F */ 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 40 - 4F */ 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 50 - 5F */ 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 60 - 6F */ 12,16,16,20,16,20,20,24,16,20,20,24,20,24,24,28 /* 70 - 7F */ }; int32 last_chm = 0; extern uint32 *M; extern const uint32 byte_mask[33]; extern int32 R[16]; extern int32 STK[5]; extern int32 PSL; extern int32 SCBB, PCBB, SBR, SLR; extern int32 P0BR, P0LR, P1BR, P1LR; extern int32 ASTLVL, SISR, mapen; extern int32 pme; extern int32 trpirq; extern int32 p1, p2; extern int32 fault_PC; extern int32 pcq[PCQ_SIZE]; extern int32 pcq_p; extern int32 in_ie; extern int32 sim_interval; extern int32 ibcnt, ppc; extern FILE *sim_deb; extern DEVICE cpu_dev; extern int32 Test (uint32 va, int32 acc, int32 *status); extern void set_map_reg (void); extern void zap_tb (int stb); extern void zap_tb_ent (uint32 va); extern t_bool chk_tb_ent (uint32 va); extern int32 ReadIPR (int32 rg); extern void WriteIPR (int32 rg, int32 val); extern t_bool BadCmPSL (int32 newpsl); extern int32 cpu_psl_ipl_idle (int32 newpsl); extern jmp_buf save_env; /* Branch on bit and no modify Branch on bit and modify opnd[0] = position (pos.rl) opnd[1] = register number/memory flag opnd[2] = memory address, if memory Returns bit to be tested */ int32 op_bb_n (int32 *opnd, int32 acc) { int32 pos = opnd[0]; int32 rn = opnd[1]; int32 ea, by; if (rn >= 0) { /* register? */ if (((uint32) pos) > 31) /* pos > 31? fault */ RSVD_OPND_FAULT; return (R[rn] >> pos) & 1; /* get bit */ } ea = opnd[2] + (pos >> 3); /* base byte addr */ pos = pos & 07; /* pos in byte */ by = Read (ea, L_BYTE, RA); /* read byte */ return ((by >> pos) & 1); /* get bit */ } int32 op_bb_x (int32 *opnd, int32 newb, int32 acc) { int32 pos = opnd[0]; int32 rn = opnd[1]; int32 ea, by, bit; if (rn >= 0) { /* register? */ if (((uint32) pos) > 31) /* pos > 31? fault */ RSVD_OPND_FAULT; bit = (R[rn] >> pos) & 1; /* get bit */ R[rn] = newb? (R[rn] | (1u << pos)): (R[rn] & ~(1u << pos)); return bit; } ea = opnd[2] + (pos >> 3); /* base byte addr */ pos = pos & 07; /* pos in byte */ by = Read (ea, L_BYTE, WA); /* read byte */ bit = (by >> pos) & 1; /* get bit */ by = newb? (by | (1u << pos)): (by & ~(1u << pos)); /* change bit */ Write (ea, by, L_BYTE, WA); /* rewrite byte */ return bit; } /* Extract field opnd[0] = position (pos.rl) opnd[1] = size (size.rb) opnd[2] = register number/memory flag opnd[3] = register content/memory address If the field is in a register, rn + 1 is in vfldrp1 */ int32 op_extv (int32 *opnd, int32 vfldrp1, int32 acc) { int32 pos = opnd[0]; int32 size = opnd[1]; int32 rn = opnd[2]; uint32 wd = opnd[3]; int32 ba, wd1 = 0; if (size == 0) /* size 0? field = 0 */ return 0; if (size > 32) /* size > 32? fault */ RSVD_OPND_FAULT; if (rn >= 0) { /* register? */ if (((uint32) pos) > 31) /* pos > 31? fault */ RSVD_OPND_FAULT; if (((pos + size) > 32) && (rn >= nSP)) /* span 2 reg, PC? */ RSVD_ADDR_FAULT; /* fault */ if (pos) wd = (wd >> pos) | (((uint32) vfldrp1) << (32 - pos)); } else { ba = wd + (pos >> 3); /* base byte addr */ pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ ba = ba & ~03; /* lw align base */ wd = Read (ba, L_LONG, RA); /* read field */ if ((size + pos) > 32) wd1 = Read (ba + 4, L_LONG, RA); if (pos) wd = (wd >> pos) | (((uint32) wd1) << (32 - pos)); } return wd & byte_mask[size]; } /* Insert field opnd[0] = field (src.rl) opnd[1] = position (pos.rl) opnd[2] = size (size.rb) opnd[3] = register number/memory flag opnd[4] = register content/memory address If the field is in a register, rn + 1 is in vfldrp1 */ void op_insv (int32 *opnd, int32 vfldrp1, int32 acc) { uint32 ins = opnd[0]; int32 pos = opnd[1]; int32 size = opnd[2]; int32 rn = opnd[3]; int32 val, mask, ba, wd, wd1; if (size == 0) /* size = 0? done */ return; if (size > 32) /* size > 32? fault */ RSVD_OPND_FAULT; if (rn >= 0) { /* in registers? */ if (((uint32) pos) > 31) /* pos > 31? fault */ RSVD_OPND_FAULT; if ((pos + size) > 32) { /* span two reg? */ if (rn >= nSP) /* if PC, fault */ RSVD_ADDR_FAULT; mask = byte_mask[pos + size - 32]; /* insert fragment */ val = ins >> (32 - pos); R[rn + 1] = (vfldrp1 & ~mask) | (val & mask); } mask = byte_mask[size] << pos; /* insert field */ val = ins << pos; R[rn] = (R[rn] & ~mask) | (val & mask); } else { ba = opnd[4] + (pos >> 3); /* base byte addr */ pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ ba = ba & ~03; /* lw align base */ wd = Read (ba, L_LONG, WA); /* read field */ if ((size + pos) > 32) { /* field span lw? */ wd1 = Read (ba + 4, L_LONG, WA); /* read 2nd lw */ mask = byte_mask[pos + size - 32]; /* insert fragment */ val = ins >> (32 - pos); Write (ba + 4, (wd1 & ~mask) | (val & mask), L_LONG, WA); } mask = byte_mask[size] << pos; /* insert field */ val = ins << pos; Write (ba, (wd & ~mask) | (val & mask), L_LONG, WA); } return; } /* Find first */ int32 op_ffs (uint32 wd, int32 size) { int32 i; for (i = 0; wd; i++, wd = wd >> 1) { if (wd & 1) return i; } return size; } #define CALL_DV 0x8000 /* DV set */ #define CALL_IV 0x4000 /* IV set */ #define CALL_MBZ 0x3000 /* MBZ */ #define CALL_MASK 0x0FFF /* mask */ #define CALL_V_SPA 30 /* SPA */ #define CALL_M_SPA 03 #define CALL_V_S 29 /* S flag */ #define CALL_S (1 << CALL_V_S) #define CALL_V_MASK 16 #define CALL_PUSH(n) if ((mask >> (n)) & 1) { \ tsp = tsp - 4; \ Write (tsp, R[n], L_LONG, WA); \ } #define CALL_GETSPA(x) (((x) >> CALL_V_SPA) & CALL_M_SPA) #define RET_POP(n) if ((spamask >> (n + CALL_V_MASK)) & 1) { \ R[n] = Read (tsp, L_LONG, RA); \ tsp = tsp + 4; \ } #define PUSHR_PUSH(n) CALL_PUSH(n) #define POPR_POP(n) if ((mask >> (n)) & 1) { \ R[n] = Read (SP, L_LONG, RA); \ SP = SP + 4; \ } /* CALLG, CALLS opnd[0] = argument (arg.rx) opnd[1] = procedure address (adr.ab) flg = CALLG (0), CALLS (1) acc = access mask These instructions implement a generalized procedure call and return facility. The principal data structure involved is the stack frame. CALLS and CALLG build a stack frame in the following format: +---------------------------------------------------------------+ | condition handler (initially 0) | +---+-+-+-----------------------+--------------------+----------+ |SPA|S|0| entry mask<11:0> | saved PSW<15:5> | 0 0 0 0 0| +---+-+-+-----------------------+--------------------+----------+ | saved AP | +---------------------------------------------------------------+ | saved FP | +---------------------------------------------------------------+ | saved PC | +---------------------------------------------------------------+ | saved R0 (...) | +---------------------------------------------------------------+ . . . (according to entry mask<11:0>) . . . +---------------------------------------------------------------+ | saved R11 (...) | +---------------+-----------------------------------------------+ | #args (CALLS) | (0-3 bytes needed to align stack) | +---------------+-----------------------------------------------+ | | 0 0 0 (CALLS) | +---------------+-----------------------------------------------+ RET expects to find this structure based at the frame pointer (FP). For CALLG and CALLS, the entry mask specifies the new settings of DV and IV, and also which registers are to be saved on entry: 15 14 13 12 11 0 +--+--+-----+----------------------------------+ |DV|IV| MBZ | register mask | +--+--+-----+----------------------------------+ CALLG/CALLS operation: read the procedure entry mask make sure that the stack frame will be accessible if CALLS, push the number of arguments onto the stack align the stack to the next lower longword boundary push the registers specified by the procedure entry mask push PC, AP, FP, saved SPA/S0/mask/PSW, condition handler update PC, SP, FP, AP update PSW traps, clear condition codes */ int32 op_call (int32 *opnd, t_bool gs, int32 acc) { int32 addr = opnd[1]; int32 mask, stklen, tsp, wd; mask = Read (addr, L_WORD, RA); /* get proc mask */ if (mask & CALL_MBZ) /* test mbz */ RSVD_OPND_FAULT; stklen = rcnt[mask & 077] + rcnt[(mask >> 6) & 077] + (gs? 24: 20); Read (SP - stklen, L_BYTE, WA); /* wchk stk */ if (gs) { Write (SP - 4, opnd[0], L_LONG, WA); /* if S, push #arg */ SP = SP - 4; /* stack is valid */ } tsp = SP & ~CALL_M_SPA; /* lw align stack */ CALL_PUSH (11); /* check mask bits, */ CALL_PUSH (10); /* push sel reg */ CALL_PUSH (9); CALL_PUSH (8); CALL_PUSH (7); CALL_PUSH (6); CALL_PUSH (5); CALL_PUSH (4); CALL_PUSH (3); CALL_PUSH (2); CALL_PUSH (1); CALL_PUSH (0); Write (tsp - 4, PC, L_LONG, WA); /* push PC */ Write (tsp - 8, FP, L_LONG, WA); /* push AP */ Write (tsp - 12, AP, L_LONG, WA); /* push FP */ wd = ((SP & CALL_M_SPA) << CALL_V_SPA) | (gs << CALL_V_S) | ((mask & CALL_MASK) << CALL_V_MASK) | (PSL & 0xFFE0); Write (tsp - 16, wd, L_LONG, WA); /* push spa/s/mask/psw */ Write (tsp - 20, 0, L_LONG, WA); /* push cond hdlr */ if (gs) /* update AP */ AP = SP; else AP = opnd[0]; SP = FP = tsp - 20; /* update FP, SP */ PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV)) | /* update PSW */ ((mask & CALL_DV)? PSW_DV: 0) | ((mask & CALL_IV)? PSW_IV: 0); JUMP (addr + 2); /* new PC */ return 0; /* new cc's */ } int32 op_ret (int32 acc) { int32 spamask, stklen, newpc, nargs; int32 tsp = FP; spamask = Read (tsp + 4, L_LONG, RA); /* spa/s/mask/psw */ if (spamask & PSW_MBZ) /* test mbz */ RSVD_OPND_FAULT; stklen = rcnt[(spamask >> CALL_V_MASK) & 077] + rcnt[(spamask >> (CALL_V_MASK + 6)) & 077] + ((spamask & CALL_S)? 23: 19); Read (tsp + stklen, L_BYTE, RA); /* rchk stk end */ AP = Read (tsp + 8, L_LONG, RA); /* restore AP */ FP = Read (tsp + 12, L_LONG, RA); /* restore FP */ newpc = Read (tsp + 16, L_LONG, RA); /* get new PC */ tsp = tsp + 20; /* update stk ptr */ RET_POP (0); /* chk mask bits, */ RET_POP (1); /* pop sel regs */ RET_POP (2); RET_POP (3); RET_POP (4); RET_POP (5); RET_POP (6); RET_POP (7); RET_POP (8); RET_POP (9); RET_POP (10); RET_POP (11); SP = tsp + CALL_GETSPA (spamask); /* dealign stack */ if (spamask & CALL_S) { /* CALLS? */ nargs = Read (SP, L_LONG, RA); /* read #args */ SP = SP + 4 + ((nargs & BMASK) << 2); /* pop arg list */ } PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV | PSW_T)) | /* reset PSW */ (spamask & (PSW_DV | PSW_FU | PSW_IV | PSW_T)); JUMP (newpc); /* set new PC */ return spamask & (CC_MASK); /* return cc's */ } /* PUSHR and POPR */ void op_pushr (int32 *opnd, int32 acc) { int32 mask = opnd[0] & 0x7FFF; int32 stklen, tsp; if (mask == 0) return; stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + ((mask & 0x4000)? 4: 0); Read (SP - stklen, L_BYTE, WA); /* wchk stk end */ tsp = SP; /* temp stk ptr */ PUSHR_PUSH (14); /* check mask bits, */ PUSHR_PUSH (13); /* push sel reg */ PUSHR_PUSH (12); PUSHR_PUSH (11); PUSHR_PUSH (10); PUSHR_PUSH (9); PUSHR_PUSH (8); PUSHR_PUSH (7); PUSHR_PUSH (6); PUSHR_PUSH (5); PUSHR_PUSH (4); PUSHR_PUSH (3); PUSHR_PUSH (2); PUSHR_PUSH (1); PUSHR_PUSH (0); SP = tsp; /* update stk ptr */ return; } void op_popr (int32 *opnd, int32 acc) { int32 mask = opnd[0] & 0x7FFF; int32 stklen; if (mask == 0) return; stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + ((mask & 0x4000)? 4: 0); Read (SP + stklen - 1, L_BYTE, RA); /* rchk stk end */ POPR_POP (0); /* check mask bits, */ POPR_POP (1); /* pop sel regs */ POPR_POP (2); POPR_POP (3); POPR_POP (4); POPR_POP (5); POPR_POP (6); POPR_POP (7); POPR_POP (8); POPR_POP (9); POPR_POP (10); POPR_POP (11); POPR_POP (12); POPR_POP (13); if (mask & 0x4000) /* if pop SP, no inc */ SP = Read (SP, L_LONG, RA); return; } /* INSQUE opnd[0] = entry address (ent.ab) opnd[1] = predecessor address (pred.ab) Condition codes returned to caller on comparison of (ent):(ent+4). All writes must be checked before any writes are done. Pictorially: BEFORE AFTER P: S P: E W P+4: (n/a) P+4: (n/a) E: --- E: S W E+4: --- E+4: P W S: (n/a) S: (n/a) S+4: P S+4: E W s+4 must be tested with a read modify rather than a probe, as it might be misaligned. */ int32 op_insque (int32 *opnd, int32 acc) { int32 p = opnd[1]; int32 e = opnd[0]; int32 s, cc; s = Read (p, L_LONG, WA); /* s <- (p), wchk */ Read (s + 4, L_LONG, WA); /* wchk s+4 */ Read (e + 4, L_LONG, WA); /* wchk e+4 */ Write (e, s, L_LONG, WA); /* (e) <- s */ Write (e + 4, p, L_LONG, WA); /* (e+4) <- p */ Write (s + 4, e, L_LONG, WA); /* (s+4) <- ent */ Write (p, e, L_LONG, WA); /* (p) <- e */ CC_CMP_L (s, p); /* set cc's */ return cc; } /* REMQUE opnd[0] = entry address (ent.ab) opnd[1:2] = destination address (dst.wl) Condition codes returned to caller based on (ent):(ent+4). All writes must be checked before any writes are done. Pictorially: BEFORE AFTER P: E P: S W P+4: (n/a) P+4: (n/a) E: S W E: S E+4: P W E+4: P S: (n/a) S: (n/a) S+4: E W S+4: P */ int32 op_remque (int32 *opnd, int32 acc) { int32 e = opnd[0]; int32 s, p, cc; s = Read (e, L_LONG, RA); /* s <- (e) */ p = Read (e + 4, L_LONG, RA); /* p <- (e+4) */ CC_CMP_L (s, p); /* set cc's */ if (e != p) { /* queue !empty? */ Read (s + 4, L_LONG, WA); /* wchk (s+4) */ if (opnd[1] < 0) /* wchk dest */ Read (opnd[2], L_LONG, WA); Write (p, s, L_LONG, WA); /* (p) <- s */ Write (s + 4, p, L_LONG, WA); /* (s+4) <- p */ } else cc = cc | CC_V; /* else set v */ if (opnd[1] >= 0) /* store result */ R[opnd[1]] = e; else Write (opnd[2], e, L_LONG, WA); return cc; } /* Interlocked insert instructions opnd[0] = entry (ent.ab) opnd[1] = header (hdr.aq) Pictorially: BEFORE AFTER INSQHI AFTER INSQTI H: A-H H: D-H W H: A-H W for interlock H+4: C-H H+4: C-H H+4: D-H W A: B-A A: B-A A: B-A A+4: H-A A+4: D-A W A+4: H-A B: C-B B: C-B B: C-B B+4: A-B B+4: A-B B+4: A-B C: H-C C: H-C C: D-C W C+4: B-C C+4: B-C C+4: B-C D: --- D: A-D W D: H-D W D+4: --- D+4: H-D W D+4: C-D W Note that the queue header, the entry to be inserted, and all the intermediate entries that are "touched" in any way must be QUADWORD aligned. In addition, the header and the entry must not be equal. */ int32 op_insqhi (int32 *opnd, int32 acc) { int32 h = opnd[1]; int32 d = opnd[0]; int32 a, t; if ((h == d) || ((h | d) & 07)) /* h, d quad align? */ RSVD_OPND_FAULT; Read (d, L_BYTE, WA); /* wchk ent */ a = Read (h, L_LONG, WA); /* a <- (h), wchk */ if (a & 06) /* chk quad align */ RSVD_OPND_FAULT; if (a & 01) /* busy, cc = 0001 */ return CC_C; Write (h, a | 1, L_LONG, WA); /* get interlock */ a = a + h; /* abs addr of a */ if (Test (a, WA, &t) < 0) /* wtst a, rls if err */ Write (h, a - h, L_LONG, WA); Write (a + 4, d - a, L_LONG, WA); /* (a+4) <- d-a, flt ok */ Write (d, a - d, L_LONG, WA); /* (d) <- a-d */ Write (d + 4, h - d, L_LONG, WA); /* (d+4) <- h-d */ Write (h, d - h, L_LONG, WA); /* (h) <- d-h, rls int */ return (a == h)? CC_Z: 0; /* Z = 1 if a = h */ } int32 op_insqti (int32 *opnd, int32 acc) { int32 h = opnd[1]; int32 d = opnd[0]; int32 a, c, t; if ((h == d) || ((h | d) & 07)) /* h, d quad align? */ RSVD_OPND_FAULT; Read (d, L_BYTE, WA); /* wchk ent */ a = Read (h, L_LONG, WA); /* a <- (h), wchk */ if (a == 0) /* if empty, ins hd */ return op_insqhi (opnd, acc); if (a & 06) /* chk quad align */ RSVD_OPND_FAULT; if (a & 01) /* busy, cc = 0001 */ return CC_C; Write (h, a | 1, L_LONG, WA); /* acquire interlock */ c = Read (h + 4, L_LONG, RA) + h; /* c <- (h+4) + h */ if (c & 07) { /* c quad aligned? */ Write (h, a, L_LONG, WA); /* release interlock */ RSVD_OPND_FAULT; /* fault */ } if (Test (c, WA, &t) < 0) /* wtst c, rls if err */ Write (h, a, L_LONG, WA); Write (c, d - c, L_LONG, WA); /* (c) <- d-c, flt ok */ Write (d, h - d, L_LONG, WA); /* (d) <- h-d */ Write (d + 4, c - d, L_LONG, WA); /* (d+4) <- c-d */ Write (h + 4, d - h, L_LONG, WA); /* (h+4) <- d-h */ Write (h, a, L_LONG, WA); /* release interlock */ return 0; /* q >= 2 entries */ } /* Interlocked remove instructions opnd[0] = header (hdr.aq) opnd[1:2] = destination address (dst.al) Pictorially: BEFORE AFTER REMQHI AFTER REMQTI H: A-H H: B-H W H: A-H W for interlock H+4: C-H H+4: C-H H+4: B-H W A: B-A A: B-A R A: B-A A+4: H-A A+4: H-A A+4: H-A B: C-B B: C-B B: H-B W B+4: A-B B+4: H-B W B+4: A-B C: H-C C: H-C C: H-C C+4: B-C C+4: B-C C+4: B-C R Note that the queue header and all the entries that are "touched" in any way must be QUADWORD aligned. In addition, the header and the destination must not be equal. */ int32 op_remqhi (int32 *opnd, int32 acc) { int32 h = opnd[0]; int32 ar, a, b, t; if (h & 07) /* h quad aligned? */ RSVD_OPND_FAULT; if (opnd[1] < 0) { /* mem destination? */ if (h == opnd[2]) /* hdr = dst? */ RSVD_OPND_FAULT; Read (opnd[2], L_LONG, WA); /* wchk dst */ } ar = Read (h, L_LONG, WA); /* ar <- (h) */ if (ar & 06) /* a quad aligned? */ RSVD_OPND_FAULT; if (ar & 01) /* busy, cc = 0011 */ return CC_V | CC_C; a = ar + h; /* abs addr of a */ if (ar) { /* queue not empty? */ Write (h, ar | 1, L_LONG, WA); /* acquire interlock */ if (Test (a, RA, &t) < 0) /* read tst a */ Write (h, ar, L_LONG, WA); /* release if error */ b = Read (a, L_LONG, RA) + a; /* b <- (a)+a, flt ok */ if (b & 07) { /* b quad aligned? */ Write (h, ar, L_LONG, WA); /* release interlock */ RSVD_OPND_FAULT; /* fault */ } if (Test (b, WA, &t) < 0) /* write test b */ Write (h, ar, L_LONG, WA); /* release if err */ Write (b + 4, h - b, L_LONG, WA); /* (b+4) <- h-b, flt ok */ Write (h, b - h, L_LONG, WA); /* (h) <- b-h, rls int */ } if (opnd[1] >= 0) /* store result */ R[opnd[1]] = a; else Write (opnd[2], a, L_LONG, WA); if (ar == 0) /* empty, cc = 0110 */ return CC_Z | CC_V; return (b == h)? CC_Z: 0; /* if b = h, q empty */ } int32 op_remqti (int32 *opnd, int32 acc) { int32 h = opnd[0]; int32 ar, b, c, t; if (h & 07) /* h quad aligned? */ RSVD_OPND_FAULT; if (opnd[1] < 0) { /* mem destination? */ if (h == opnd[2]) /* hdr = dst? */ RSVD_OPND_FAULT; Read (opnd[2], L_LONG, WA); /* wchk dst */ } ar = Read (h, L_LONG, WA); /* a <- (h) */ if (ar & 06) /* a quad aligned? */ RSVD_OPND_FAULT; if (ar & 01) /* busy, cc = 0011 */ return CC_V | CC_C; if (ar) { /* queue not empty */ Write (h, ar | 1, L_LONG, WA); /* acquire interlock */ c = Read (h + 4, L_LONG, RA); /* c <- (h+4) */ if (ar == c) { /* single entry? */ Write (h, ar, L_LONG, WA); /* release interlock */ return op_remqhi (opnd, acc); /* treat as remqhi */ } if (c & 07) { /* c quad aligned? */ Write (h, ar, L_LONG, WA); /* release interlock */ RSVD_OPND_FAULT; /* fault */ } c = c + h; /* abs addr of c */ if (Test (c + 4, RA, &t) < 0) /* read test c+4 */ Write (h, ar, L_LONG, WA); /* release if error */ b = Read (c + 4, L_LONG, RA) + c; /* b <- (c+4)+c, flt ok */ if (b & 07) { /* b quad aligned? */ Write (h, ar, L_LONG, WA); /* release interlock */ RSVD_OPND_FAULT; /* fault */ } if (Test (b, WA, &t) < 0) /* write test b */ Write (h, ar, L_LONG, WA); /* release if error */ Write (b, h - b, L_LONG, WA); /* (b) <- h-b */ Write (h + 4, b - h, L_LONG, WA); /* (h+4) <- b-h */ Write (h, ar, L_LONG, WA); /* release interlock */ } else c = h; /* empty, result = h */ if (opnd[1] >= 0) /* store result */ R[opnd[1]] = c; else Write (opnd[2], c, L_LONG, WA); if (ar == 0) /* empty, cc = 0110 */ return CC_Z | CC_V; return 0; /* q can't be empty */ } /* String instructions */ #define MVC_FRWD 0 /* movc state codes */ #define MVC_BACK 1 #define MVC_FILL 3 /* must be 3 */ #define MVC_M_STATE 3 #define MVC_V_CC 2 /* MOVC3, MOVC5 if PSL = 0 and MOVC3, opnd[0] = length opnd[1] = source address opnd[2] = dest address if PSL = 0 and MOVC5, opnd[0] = source length opnd[1] = source address opnd[2] = fill opnd[3] = dest length opnd[4] = dest address if PSL = 1, R0 = delta-PC/fill/initial move length R1 = current source address R2 = current move length R3 = current dest address R4 = dstlen - srclen (loop count if fill state) R5 = cc/state */ int32 op_movc (int32 *opnd, int32 movc5, int32 acc) { int32 i, cc, fill, wd; int32 j, lnt, mlnt[3]; static const int32 looplnt[3] = { L_BYTE, L_LONG, L_BYTE }; if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get fill */ R[2] = R[2] & STR_LNMASK; /* mask lengths */ if (R[4] > 0) R[4] = R[4] & STR_LNMASK; } else { R[1] = opnd[1]; /* src addr */ if (movc5) { /* MOVC5? */ R[2] = (opnd[0] < opnd[3])? opnd[0]: opnd[3]; R[3] = opnd[4]; /* dst addr */ R[4] = opnd[3] - opnd[0]; /* dstlen - srclen */ fill = opnd[2]; /* set fill */ CC_CMP_W (opnd[0], opnd[3]); /* set cc's */ } else { R[2] = opnd[0]; /* mvlen = srclen */ R[3] = opnd[2]; /* dst addr */ R[4] = fill = 0; /* no fill */ cc = CC_Z; /* set cc's */ } R[0] = STR_PACK (fill, R[2]); /* initial mvlen */ if (R[2]) { /* any move? */ if (((uint32) R[1]) < ((uint32) R[3])) { R[1] = R[1] + R[2]; /* backward, adjust */ R[3] = R[3] + R[2]; /* addr to end */ R[5] = MVC_BACK; /* set state */ } else R[5] = MVC_FRWD; /* fwd, set state */ } else R[5] = MVC_FILL; /* fill, set state */ R[5] = R[5] | (cc << MVC_V_CC); /* pack with state */ PSL = PSL | PSL_FPD; /* set FPD */ } /* At this point, R0 = delta PC'fill'initial move length R1 = current src addr R2 = current move length R3 = current dst addr R4 = dst length - src length R5 = cc'state */ switch (R[5] & MVC_M_STATE) { /* case on state */ case MVC_FRWD: /* move forward */ mlnt[0] = (4 - R[3]) & 3; /* length to align */ if (mlnt[0] > R[2]) /* cant exceed total */ mlnt[0] = R[2]; mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ for (i = 0; i < 3; i++) { /* head, align, tail */ lnt = looplnt[i]; /* length for loop */ for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { wd = Read (R[1], lnt, RA); /* read src */ Write (R[3], wd, lnt, WA); /* write dst */ R[1] = R[1] + lnt; /* inc src addr */ R[3] = R[3] + lnt; /* inc dst addr */ R[2] = R[2] - lnt; /* dec move lnt */ } } goto FILL; /* check for fill */ case MVC_BACK: /* move backward */ mlnt[0] = R[3] & 03; /* length to align */ if (mlnt[0] > R[2]) /* cant exceed total */ mlnt[0] = R[2]; mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ for (i = 0; i < 3; i++) { /* head, align, tail */ lnt = looplnt[i]; /* length for loop */ for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { wd = Read (R[1] - lnt, lnt, RA); /* read src */ Write (R[3] - lnt, wd, lnt, WA); /* write dst */ R[1] = R[1] - lnt; /* dec src addr */ R[3] = R[3] - lnt; /* dec dst addr */ R[2] = R[2] - lnt; /* dec move lnt */ } } R[1] = R[1] + (R[0] & STR_LNMASK); /* final src addr */ R[3] = R[3] + (R[0] & STR_LNMASK); /* final dst addr */ case MVC_FILL: /* fill */ FILL: if (R[4] <= 0) /* any fill? */ break; R[5] = R[5] | MVC_FILL; /* set state */ mlnt[0] = (4 - R[3]) & 3; /* length to align */ if (mlnt[0] > R[4]) /* cant exceed total */ mlnt[0] = R[4]; mlnt[1] = (R[4] - mlnt[0]) & ~03; /* aligned length */ mlnt[2] = R[4] - mlnt[0] - mlnt[1]; /* tail */ for (i = 0; i < 3; i++) { /* head, align, tail */ lnt = looplnt[i]; /* length for loop */ fill = fill & BMASK; /* fill for loop */ if (lnt == L_LONG) fill = (((uint32) fill) << 24) | (fill << 16) | (fill << 8) | fill; for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { Write (R[3], fill, lnt, WA); /* write fill */ R[3] = R[3] + lnt; /* inc dst addr */ R[4] = R[4] - lnt; /* dec fill lnt */ } } break; default: /* bad state */ RSVD_OPND_FAULT; /* you lose */ } PSL = PSL & ~PSL_FPD; /* clear FPD */ cc = (R[5] >> MVC_V_CC) & CC_MASK; /* get cc's */ R[0] = NEG (R[4]); /* set R0 */ R[2] = R[4] = R[5] = 0; /* clear reg */ return cc; } /* CMPC3, CMPC5 if PSL = 0 and CMPC3, opnd[0] = length opnd[1] = source1 address opnd[2] = source2 address if PSL = 0 and CMPC5, opnd[0] = source1 length opnd[1] = source1 address opnd[2] = fill opnd[3] = source2 length opnd[4] = source2 address if PSL = 1, R0 = delta-PC/fill/source1 length R1 = source1 address R2 = source2 length R3 = source2 address */ int32 op_cmpc (int32 *opnd, int32 cmpc5, int32 acc) { int32 cc, s1, s2, fill; if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get fill */ } else { R[1] = opnd[1]; /* src1len */ if (cmpc5) { /* CMPC5? */ R[2] = opnd[3]; /* get src2 opnds */ R[3] = opnd[4]; fill = opnd[2]; } else { R[2] = opnd[0]; /* src2len = src1len */ R[3] = opnd[2]; fill = 0; } R[0] = STR_PACK (fill, opnd[0]); /* src1len + FPD data */ PSL = PSL | PSL_FPD; } R[2] = R[2] & STR_LNMASK; /* mask src2len */ for (s1 = s2 = 0; ((R[0] | R[2]) & STR_LNMASK) != 0; sim_interval--) { if (R[0] & STR_LNMASK) /* src1? read */ s1 = Read (R[1], L_BYTE, RA); else s1 = fill; /* no, use fill */ if (R[2]) /* src2? read */ s2 = Read (R[3], L_BYTE, RA); else s2 = fill; /* no, use fill */ if (s1 != s2) /* src1 = src2? */ break; if (R[0] & STR_LNMASK) { /* if src1, decr */ R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); R[1] = R[1] + 1; } if (R[2]) { /* if src2, decr */ R[2] = (R[2] - 1) & STR_LNMASK; R[3] = R[3] + 1; } } PSL = PSL & ~PSL_FPD; /* clear FPD */ CC_CMP_B (s1, s2); /* set cc's */ R[0] = R[0] & STR_LNMASK; /* clear packup */ return cc; } /* LOCC, SKPC if PSL = 0, opnd[0] = match character opnd[1] = source length opnd[2] = source address if PSL = 1, R0 = delta-PC/match/source length R1 = source address */ int32 op_locskp (int32 *opnd, int32 skpc, int32 acc) { int32 c, match; if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ match = STR_GETCHR (R[0]); /* get match char */ } else { match = opnd[0]; /* get operands */ R[0] = STR_PACK (match, opnd[1]); /* src len + FPD data */ R[1] = opnd[2]; /* src addr */ PSL = PSL | PSL_FPD; } for ( ; (R[0] & STR_LNMASK) != 0; sim_interval-- ) { /* loop thru string */ c = Read (R[1], L_BYTE, RA); /* get src byte */ if ((c == match) ^ skpc) /* match & locc? */ break; R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); R[1] = R[1] + 1; /* incr src1adr */ } PSL = PSL & ~PSL_FPD; /* clear FPD */ R[0] = R[0] & STR_LNMASK; /* clear packup */ return (R[0]? 0: CC_Z); /* set cc's */ } /* SCANC, SPANC if PSL = 0, opnd[0] = source length opnd[1] = source address opnd[2] = table address opnd[3] = mask if PSL = 1, R0 = delta-PC/char/source length R1 = source address R3 = table address */ int32 op_scnspn (int32 *opnd, int32 spanc, int32 acc) { int32 c, t, mask; if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ mask = STR_GETCHR (R[0]); /* get mask */ } else { R[1] = opnd[1]; /* src addr */ R[3] = opnd[2]; /* tblad */ mask = opnd[3]; /* mask */ R[0] = STR_PACK (mask, opnd[0]); /* srclen + FPD data */ PSL = PSL | PSL_FPD; } for ( ; (R[0] & STR_LNMASK) != 0; sim_interval-- ) { /* loop thru string */ c = Read (R[1], L_BYTE, RA); /* get byte */ t = Read (R[3] + c, L_BYTE, RA); /* get table ent */ if (((t & mask) != 0) ^ spanc) /* test vs instr */ break; R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); R[1] = R[1] + 1; } PSL = PSL & ~PSL_FPD; R[0] = R[0] & STR_LNMASK; /* clear packup */ R[2] = 0; return (R[0]? 0: CC_Z); } /* Operating system interfaces */ /* Interrupt or exception vec = SCB vector cc = condition codes ipl = new IPL if interrupt ei = -1: severe exception 0: normal exception 1: interrupt */ int32 intexc (int32 vec, int32 cc, int32 ipl, int ei) { int32 oldpsl = PSL | cc; int32 oldcur = PSL_GETCUR (oldpsl); int32 oldsp = SP; int32 newpsl; int32 newpc; int32 acc; in_ie = 1; /* flag int/exc */ CLR_TRAPS; /* clear traps */ newpc = ReadLP ((SCBB + vec) & PAMASK); /* read new PC */ if (ei < 0) /* severe? on istk */ newpc = newpc | 1; if (newpc & 2) /* bad flags? */ ABORT (STOP_ILLVEC); if (oldpsl & PSL_IS) /* on int stk? */ newpsl = PSL_IS; else { STK[oldcur] = SP; /* no, save cur stk */ if (newpc & 1) { /* to int stk? */ newpsl = PSL_IS; /* flag */ SP = IS; /* new stack */ } else { newpsl = 0; /* to ker stk */ SP = KSP; /* new stack */ } } if (ei > 0) /* if int, new IPL */ PSL = cpu_psl_ipl_idle (newpsl | (ipl << PSL_V_IPL)); else PSL = cpu_psl_ipl_idle (newpsl | /* exc, old IPL/1F */ ((newpc & 1)? PSL_IPL1F: (oldpsl & PSL_IPL)) | (oldcur << PSL_V_PRV)); if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, ">>IEX: PC=%08x, PSL=%08x, SP=%08x, VEC=%08x, nPSL=%08x, nSP=%08x\n", PC, oldpsl, oldsp, vec, PSL, SP); acc = ACC_MASK (KERN); /* new mode is kernel */ Write (SP - 4, oldpsl, L_LONG, WA); /* push old PSL */ Write (SP - 8, PC, L_LONG, WA); /* push old PC */ SP = SP - 8; /* update stk ptr */ JUMP (newpc & ~3); /* change PC */ in_ie = 0; /* out of flows */ return 0; } /* CHMK, CHME, CHMS, CHMU opnd[0] = operand */ int32 op_chm (int32 *opnd, int32 cc, int32 opc) { int32 mode = opc & PSL_M_MODE; int32 cur = PSL_GETCUR (PSL); int32 tsp, newpc, acc, sta; if (PSL & PSL_IS) ABORT (STOP_CHMFI); newpc = ReadLP ((SCBB + SCB_CHMK + (mode << 2)) & PAMASK); if (cur < mode) /* only inward */ mode = cur; STK[cur] = SP; /* save stack */ tsp = STK[mode]; /* get new stk */ acc = ACC_MASK (mode); /* set new mode */ if (Test (p2 = tsp - 1, WA, &sta) < 0) { /* probe stk */ p1 = MM_WRITE | (sta & MM_EMASK); ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); } if (Test (p2 = tsp - 12, WA, &sta) < 0) { p1 = MM_WRITE | (sta & MM_EMASK); ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); } Write (tsp - 12, SXTW (opnd[0]), L_LONG, WA); /* push argument */ Write (tsp - 8, PC, L_LONG, WA); /* push PC */ Write (tsp - 4, PSL | cc, L_LONG, WA); /* push PSL */ SP = tsp - 12; /* set new stk */ PSL = (mode << PSL_V_CUR) | (PSL & PSL_IPL) | /* set new PSL */ (cur << PSL_V_PRV); /* IPL unchanged */ last_chm = fault_PC; JUMP (newpc & ~03); /* set new PC */ return 0; /* cc = 0 */ } /* REI - return from exception or interrupt The lengthiest part of the REI instruction is the validity checking of the PSL popped off the stack. The new PSL is checked against the following eight rules: let tmp = new PSL popped off the stack let PSL = current PSL Rule SRM formulation Comment ---- --------------- ------- 1 tmp<25:24> GEQ PSL<25:24> tmp GEQ PSL 2 tmp<26> LEQ PSL<26> tmp LEQ PSL 3 tmp<26> = 1 => tmp<25:24> = 0 tmp = 1 => tmp = ker 4 tmp<26> = 1 => tmp<20:16> > 0 tmp = 1 => tmp > 0 5 tmp<20:16> > 0 => tmp<25:24> = 0 tmp > 0 => tmp = ker 6 tmp<25:24> LEQ tmp<23:22> tmp LEQ tmp 7 tmp<20:16> LEQ PSL<20:16> tmp LEQ PSL 8 tmp<31,29:28,21,15:8> = 0 tmp = 0 9 tmp<31> = 1 => tmp = 3, tmp = 3>, tmp = 0 */ int32 op_rei (int32 acc) { int32 newpc = Read (SP, L_LONG, RA); int32 newpsl = Read (SP + 4, L_LONG, RA); int32 newcur = PSL_GETCUR (newpsl); int32 oldcur = PSL_GETCUR (PSL); int32 newipl, i; if ((newpsl & PSL_MBZ) || /* rule 8 */ (newcur < oldcur)) /* rule 1 */ RSVD_OPND_FAULT; if (newcur) { /* to esu, skip 2,4,7 */ if ((newpsl & (PSL_IS | PSL_IPL)) || /* rules 3,5 */ (newcur > PSL_GETPRV (newpsl))) /* rule 6 */ RSVD_OPND_FAULT; /* end rei to esu */ } else { /* to k, skip 3,5,6 */ newipl = PSL_GETIPL (newpsl); /* get new ipl */ if ((newpsl & PSL_IS) && /* setting IS? */ (((PSL & PSL_IS) == 0) || (newipl == 0))) /* test rules 2,4 */ RSVD_OPND_FAULT; /* else skip 2,4 */ if (newipl > PSL_GETIPL (PSL)) /* test rule 7 */ RSVD_OPND_FAULT; } /* end if kernel */ if (newpsl & PSL_CM) { /* setting cmode? */ if (BadCmPSL (newpsl)) /* validate PSL */ RSVD_OPND_FAULT; for (i = 0; i < 7; i++) /* mask R0-R6, PC */ R[i] = R[i] & WMASK; newpc = newpc & WMASK; } SP = SP + 8; /* pop stack */ if (PSL & PSL_IS) /* save stack */ IS = SP; else STK[oldcur] = SP; if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) fprintf (sim_deb, ">>REI: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", PC, PSL, SP - 8, newpc, newpsl, ((newpsl & IS)? IS: STK[newcur])); PSL = cpu_psl_ipl_idle ((PSL & PSL_TP) | (newpsl & ~CC_MASK)); /* set PSL */ if (PSL & PSL_IS) /* set new stack */ SP = IS; else { SP = STK[newcur]; /* if ~IS, chk AST */ if (newcur >= ASTLVL) { if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) fprintf (sim_deb, ">>REI: AST delivered\n"); SISR = SISR | SISR_2; } } JUMP (newpc); /* set new PC */ return newpsl & CC_MASK; /* set new cc */ } /* LDCPTX - load process context */ void op_ldpctx (int32 acc) { int32 newpc, newpsl, pcbpa, t; if (PSL & PSL_CUR) /* must be kernel */ RSVD_INST_FAULT; pcbpa = PCBB & PAMASK; /* phys address */ KSP = ReadLP (pcbpa); /* restore stk ptrs */ ESP = ReadLP (pcbpa + 4); SSP = ReadLP (pcbpa + 8); USP = ReadLP (pcbpa + 12); R[0] = ReadLP (pcbpa + 16); /* restore registers */ R[1] = ReadLP (pcbpa + 20); R[2] = ReadLP (pcbpa + 24); R[3] = ReadLP (pcbpa + 28); R[4] = ReadLP (pcbpa + 32); R[5] = ReadLP (pcbpa + 36); R[6] = ReadLP (pcbpa + 40); R[7] = ReadLP (pcbpa + 44); R[8] = ReadLP (pcbpa + 48); R[9] = ReadLP (pcbpa + 52); R[10] = ReadLP (pcbpa + 56); R[11] = ReadLP (pcbpa + 60); R[12] = ReadLP (pcbpa + 64); R[13] = ReadLP (pcbpa + 68); newpc = ReadLP (pcbpa + 72); /* get PC, PSL */ newpsl = ReadLP (pcbpa + 76); t = ReadLP (pcbpa + 80); ML_PXBR_TEST (t); /* validate P0BR */ P0BR = t & BR_MASK; /* restore P0BR */ t = ReadLP (pcbpa + 84); LP_MBZ84_TEST (t); /* test mbz */ ML_LR_TEST (t & LR_MASK); /* validate P0LR */ P0LR = t & LR_MASK; /* restore P0LR */ t = (t >> 24) & AST_MASK; LP_AST_TEST (t); /* validate AST */ ASTLVL = t; /* restore AST */ t = ReadLP (pcbpa + 88); ML_PXBR_TEST (t + 0x800000); /* validate P1BR */ P1BR = t & BR_MASK; /* restore P1BR */ t = ReadLP (pcbpa + 92); LP_MBZ92_TEST (t); /* test MBZ */ ML_LR_TEST (t & LR_MASK); /* validate P1LR */ P1LR = t & LR_MASK; /* restore P1LR */ pme = (t >> 31) & 1; /* restore PME */ zap_tb (0); /* clear process TB */ set_map_reg (); if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) fprintf (sim_deb, ">>LDP: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", PC, PSL, SP, newpc, newpsl, KSP); if (PSL & PSL_IS) /* if istk, */ IS = SP; PSL = PSL & ~PSL_IS; /* switch to kstk */ SP = KSP - 8; Write (SP, newpc, L_LONG, WA); /* push PC, PSL */ Write (SP + 4, newpsl, L_LONG, WA); return; } /* SVPCTX - save processor context */ void op_svpctx (int32 acc) { int32 savpc, savpsl, pcbpa; if (PSL & PSL_CUR) /* must be kernel */ RSVD_INST_FAULT; savpc = Read (SP, L_LONG, RA); /* pop PC, PSL */ savpsl = Read (SP + 4, L_LONG, RA); if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) fprintf (sim_deb, ">>SVP: PC=%08x, PSL=%08x, SP=%08x, oPC=%08x, oPSL=%08x\n", PC, PSL, SP, savpc, savpsl); if (PSL & PSL_IS) /* int stack? */ SP = SP + 8; else { KSP = SP + 8; /* pop kernel stack */ SP = IS; /* switch to int stk */ if ((PSL & PSL_IPL) == 0) /* make IPL > 0 */ PSL = PSL | PSL_IPL1; PSL = PSL | PSL_IS; /* set PSL */ } pcbpa = PCBB & PAMASK; WriteLP (pcbpa, KSP); /* save stk ptrs */ WriteLP (pcbpa + 4, ESP); WriteLP (pcbpa + 8, SSP); WriteLP (pcbpa + 12, USP); WriteLP (pcbpa + 16, R[0]); /* save registers */ WriteLP (pcbpa + 20, R[1]); WriteLP (pcbpa + 24, R[2]); WriteLP (pcbpa + 28, R[3]); WriteLP (pcbpa + 32, R[4]); WriteLP (pcbpa + 36, R[5]); WriteLP (pcbpa + 40, R[6]); WriteLP (pcbpa + 44, R[7]); WriteLP (pcbpa + 48, R[8]); WriteLP (pcbpa + 52, R[9]); WriteLP (pcbpa + 56, R[10]); WriteLP (pcbpa + 60, R[11]); WriteLP (pcbpa + 64, R[12]); WriteLP (pcbpa + 68, R[13]); WriteLP (pcbpa + 72, savpc); /* save PC, PSL */ WriteLP (pcbpa + 76, savpsl); return; } /* PROBER and PROBEW opnd[0] = mode opnd[1] = length opnd[2] = base address */ int32 op_probe (int32 *opnd, int32 rw) { int32 mode = opnd[0] & PSL_M_MODE; /* mask mode */ int32 length = opnd[1]; int32 ba = opnd[2]; int32 prv = PSL_GETPRV (PSL); int32 acc, sta, sta1; if (prv > mode) /* maximize mode */ mode = prv; acc = ACC_MASK (mode) << (rw? TLB_V_WACC: 0); /* set acc mask */ Test (ba, acc, &sta); /* probe */ switch (sta) { /* case on status */ case PR_PTNV: /* pte TNV */ p1 = MM_PARAM (rw, PR_PTNV); p2 = ba; ABORT (ABORT_TNV); /* force TNV */ case PR_TNV: case PR_OK: /* TNV or ok */ break; /* continue */ default: /* other */ return CC_Z; /* lose */ } Test (ba + length - 1, acc, &sta1); /* probe end addr */ switch (sta1) { /* case on status */ case PR_PTNV: /* pte TNV */ p1 = MM_PARAM (rw, PR_PTNV); p2 = ba + length - 1; ABORT (ABORT_TNV); /* force TNV */ case PR_TNV: case PR_OK: /* TNV or ok */ break; /* win */ default: /* other */ return CC_Z; /* lose */ } return 0; } /* MTPR - move to processor register opnd[0] = data opnd[1] = register number */ int32 op_mtpr (int32 *opnd) { int32 val = opnd[0]; int32 prn = opnd[1]; int32 cc; if (PSL & PSL_CUR) /* must be kernel */ RSVD_INST_FAULT; if (prn > 63) /* reg# > 63? fault */ RSVD_OPND_FAULT; CC_IIZZ_L (val); /* set cc's */ switch (prn) { /* case on reg # */ case MT_KSP: /* KSP */ if (PSL & PSL_IS) /* on IS? store KSP */ KSP = val; else SP = val; /* else store SP */ break; case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ STK[prn] = val; /* store stack */ break; case MT_IS: /* IS */ if (PSL & PSL_IS) /* on IS? store SP */ SP = val; else IS = val; /* else store IS */ break; case MT_P0BR: /* P0BR */ ML_PXBR_TEST (val); /* validate */ P0BR = val & BR_MASK; /* lw aligned */ zap_tb (0); /* clr proc TLB */ set_map_reg (); break; case MT_P0LR: /* P0LR */ ML_LR_TEST (val & LR_MASK); /* validate */ P0LR = val & LR_MASK; zap_tb (0); /* clr proc TLB */ set_map_reg (); break; case MT_P1BR: /* P1BR */ ML_PXBR_TEST (val + 0x800000); /* validate */ P1BR = val & BR_MASK; /* lw aligned */ zap_tb (0); /* clr proc TLB */ set_map_reg (); break; case MT_P1LR: /* P1LR */ ML_LR_TEST (val & LR_MASK); /* validate */ P1LR = val & LR_MASK; zap_tb (0); /* clr proc TLB */ set_map_reg (); break; case MT_SBR: /* SBR */ ML_SBR_TEST (val); /* validate */ SBR = val & BR_MASK; /* lw aligned */ zap_tb (1); /* clr entire TLB */ set_map_reg (); break; case MT_SLR: /* SLR */ ML_LR_TEST (val & LR_MASK); /* validate */ SLR = val & LR_MASK; zap_tb (1); /* clr entire TLB */ set_map_reg (); break; case MT_SCBB: /* SCBB */ ML_PA_TEST (val); /* validate */ SCBB = val & BR_MASK; /* lw aligned */ break; case MT_PCBB: /* PCBB */ ML_PA_TEST (val); /* validate */ PCBB = val & BR_MASK; /* lw aligned */ break; case MT_IPL: /* IPL */ PSL = (PSL & ~PSL_IPL) | ((val & PSL_M_IPL) << PSL_V_IPL); break; case MT_ASTLVL: /* ASTLVL */ if (val > AST_MAX) /* > 4? fault */ RSVD_OPND_FAULT; ASTLVL = val; break; case MT_SIRR: /* SIRR */ if ((val > 0xF) || (val == 0)) RSVD_OPND_FAULT; SISR = SISR | (1 << val); /* set bit in SISR */ break; case MT_SISR: /* SISR */ SISR = val & SISR_MASK; break; case MT_MAPEN: /* MAPEN */ mapen = val & 1; case MT_TBIA: /* TBIA */ zap_tb (1); /* clr entire TLB */ break; case MT_TBIS: /* TBIS */ zap_tb_ent (val); break; case MT_TBCHK: /* TBCHK */ if (chk_tb_ent (val)) cc = cc | CC_V; break; case MT_PME: /* PME */ pme = val & 1; break; default: WriteIPR (prn, val); /* others */ break; } return cc; } int32 op_mfpr (int32 *opnd) { int32 prn = opnd[0]; int32 val; if (PSL & PSL_CUR) /* must be kernel */ RSVD_INST_FAULT; if (prn > 63) /* reg# > 63? fault */ RSVD_OPND_FAULT; switch (prn) { /* case on reg# */ case MT_KSP: /* KSP */ val = (PSL & PSL_IS)? KSP: SP; /* return KSP or SP */ break; case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ val = STK[prn]; /* return stk ptr */ break; case MT_IS: /* IS */ val = (PSL & PSL_IS)? SP: IS; /* return SP or IS */ break; case MT_P0BR: /* P0BR */ val = P0BR; break; case MT_P0LR: /* P0LR */ val = P0LR; break; case MT_P1BR: /* P1BR */ val = P1BR; break; case MT_P1LR: /* P1LR */ val = P1LR; break; case MT_SBR: /* SBR */ val = SBR; break; case MT_SLR: /* SLR */ val = SLR; break; case MT_SCBB: /* SCBB */ val = SCBB; break; case MT_PCBB: /* PCBB */ val = PCBB; break; case MT_IPL: /* IPL */ val = PSL_GETIPL (PSL); break; case MT_ASTLVL: /* ASTLVL */ val = ASTLVL; break; case MT_SISR: /* SISR */ val = SISR & SISR_MASK; break; case MT_MAPEN: /* MAPEN */ val = mapen & 1; break; case MT_PME: val = pme & 1; break; case MT_SIRR: case MT_TBIA: case MT_TBIS: case MT_TBCHK: RSVD_OPND_FAULT; /* write only */ default: /* others */ val = ReadIPR (prn); /* read from SSC */ break; } return val; } simh-3.8.1/VAX/vax_stddev.c0000644000175000017500000003060411112314464013612 0ustar vlmvlm/* vax_stddev.c: VAX 3900 standard I/O devices Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti terminal input tto terminal output clk 100Hz and TODR clock 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 17-Oct-06 RMS Synced keyboard poll to real-time clock for idling 22-Nov-05 RMS Revised for new terminal processing routines 09-Sep-04 RMS Integrated powerup into RESET (with -p) 28-May-04 RMS Removed SET TTI CTRL-C 29-Dec-03 RMS Added console backpressure support 25-Apr-03 RMS Revised for extended file support 02-Mar-02 RMS Added SET TTI CTRL-C 22-Dec-02 RMS Added console halt capability 01-Nov-02 RMS Added 7B/8B capability to terminal 12-Sep-02 RMS Removed paper tape, added variable vector support 30-May-02 RMS Widened POS to 32b 30-Apr-02 RMS Automatically set TODR to VMS-correct value during boot */ #include "vax_defs.h" #include #define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ #define TTICSR_RW (CSR_IE) #define TTIBUF_ERR 0x8000 /* error */ #define TTIBUF_OVR 0x4000 /* overrun */ #define TTIBUF_FRM 0x2000 /* framing error */ #define TTIBUF_RBR 0x0400 /* receive break */ #define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ #define TTOCSR_RW (CSR_IE) #define CLKCSR_IMP (CSR_IE) /* real-time clock */ #define CLKCSR_RW (CSR_IE) #define CLK_DELAY 5000 /* 100 Hz */ #define TMXR_MULT 1 /* 100 Hz */ extern int32 int_req[IPL_HLVL]; extern int32 hlt_pin; extern int32 sim_switches; int32 tti_csr = 0; /* control/status */ int32 tto_csr = 0; /* control/status */ int32 clk_csr = 0; /* control/status */ int32 clk_tps = 100; /* ticks/second */ int32 todr_reg = 0; /* TODR register */ int32 todr_blow = 1; /* TODR battery low */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat clk_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr); t_stat todr_resync (void); extern int32 sysd_hlt_enb (void); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list */ DIB tti_dib = { 0, 0, NULL, NULL, 1, IVCL (TTI), SCB_TTI, { NULL } }; UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_8B, 0), 0 }; REG tti_reg[] = { { HRDATA (BUF, tti_unit.buf, 16) }, { HRDATA (CSR, tti_csr, 16) }, { FLDATA (INT, int_req[IPL_TTI], INT_V_TTI) }, { FLDATA (DONE, tti_csr, CSR_V_DONE) }, { FLDATA (IE, tti_csr, CSR_V_IE) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tti_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 16, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { 0, 0, NULL, NULL, 1, IVCL (TTO), SCB_TTO, { NULL } }; UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { HRDATA (BUF, tto_unit.buf, 8) }, { HRDATA (CSR, tto_csr, 16) }, { FLDATA (INT, int_req[IPL_TTO], INT_V_TTO) }, { FLDATA (DONE, tto_csr, CSR_V_DONE) }, { FLDATA (IE, tto_csr, CSR_V_IE) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 16, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_reg CLK register list */ DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; REG clk_reg[] = { { HRDATA (CSR, clk_csr, 16) }, { FLDATA (INT, int_req[IPL_CLK], INT_V_CLK) }, { FLDATA (IE, clk_csr, CSR_V_IE) }, { DRDATA (TODR, todr_reg, 32), PV_LEFT }, { FLDATA (BLOW, todr_blow, 0) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO }, { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, 0 }; /* Clock and terminal MxPR routines iccs_rd/wr interval timer todr_rd/wr time of year clock rxcs_rd/wr input control/status rxdb_rd input buffer txcs_rd/wr output control/status txdb_wr output buffer */ int32 iccs_rd (void) { return (clk_csr & CLKCSR_IMP); } int32 todr_rd (void) { return todr_reg; } int32 rxcs_rd (void) { return (tti_csr & TTICSR_IMP); } int32 rxdb_rd (void) { int32 t = tti_unit.buf; /* char + error */ tti_csr = tti_csr & ~CSR_DONE; /* clr done */ tti_unit.buf = tti_unit.buf & 0377; /* clr errors */ CLR_INT (TTI); return t; } int32 txcs_rd (void) { return (tto_csr & TTOCSR_IMP); } void iccs_wr (int32 data) { if ((data & CSR_IE) == 0) CLR_INT (CLK); clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); return; } void todr_wr (int32 data) { todr_reg = data; if (data) todr_blow = 0; return; } void rxcs_wr (int32 data) { if ((data & CSR_IE) == 0) CLR_INT (TTI); else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (TTI); tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); return; } void txcs_wr (int32 data) { if ((data & CSR_IE) == 0) CLR_INT (TTO); else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (TTO); tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); return; } void txdb_wr (int32 data) { tto_unit.buf = data & 0377; tto_csr = tto_csr & ~CSR_DONE; CLR_INT (TTO); sim_activate (&tto_unit, tto_unit.wait); return; } /* Terminal input routines tti_svc process event (character ready) tti_reset process reset */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) { /* break? */ if (sysd_hlt_enb ()) /* if enabled, halt */ hlt_pin = 1; tti_unit.buf = TTIBUF_ERR | TTIBUF_FRM | TTIBUF_RBR; } else tti_unit.buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); uptr->pos = uptr->pos + 1; tti_csr = tti_csr | CSR_DONE; if (tti_csr & CSR_IE) SET_INT (TTI); return SCPE_OK; } t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; tti_csr = 0; CLR_INT (TTI); sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); return SCPE_OK; } /* Terminal output routines tto_svc process event (character typed) tto_reset process reset */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = sim_tt_outcvt (tto_unit.buf, TT_GET_MODE (uptr->flags)); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } } tto_csr = tto_csr | CSR_DONE; if (tto_csr & CSR_IE) SET_INT (TTO); uptr->pos = uptr->pos + 1; return SCPE_OK; } t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; tto_csr = CSR_DONE; CLR_INT (TTO); sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } /* Clock routines clk_svc process event (clock tick) clk_reset process reset todr_powerup powerup for TODR (get date from system) */ t_stat clk_svc (UNIT *uptr) { int32 t; if (clk_csr & CSR_IE) SET_INT (CLK); t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, t); /* reactivate unit */ tmr_poll = t; /* set tmr poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */ if (!todr_blow) /* incr TODR */ todr_reg = todr_reg + 1; return SCPE_OK; } /* Clock coscheduling routine */ int32 clk_cosched (int32 wait) { int32 t; t = sim_is_active (&clk_unit); return (t? t - 1: wait); } /* TODR resync routine */ t_stat todr_resync (void) { uint32 base; time_t curr; struct tm *ctm; curr = time (NULL); /* get curr time */ if (curr == (time_t) -1) /* error? */ return SCPE_NOFNC; ctm = localtime (&curr); /* decompose */ if (ctm == NULL) /* error? */ return SCPE_NOFNC; base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ ctm->tm_hour) * 60) + ctm->tm_min) * 60) + ctm->tm_sec; todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ todr_blow = 0; return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { int32 t; todr_resync (); /* resync clock */ clk_csr = 0; CLR_INT (CLK); t = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init timer */ sim_activate_abs (&clk_unit, t); /* activate unit */ tmr_poll = t; /* set tmr poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */ return SCPE_OK; } simh-3.8.1/VAX/vax_io.c0000644000175000017500000006363611111054414012736 0ustar vlmvlm/* vax_io.c: VAX 3900 Qbus IO simulator Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. qba Qbus adapter 28-May-08 RMS Inlined physical memory routines 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato) 03-Dec-05 RMS Added SHOW QBA VIRT and ex/dep via map 05-Oct-05 RMS Fixed bug in autoconfiguration (missing XU) 25-Jul-05 RMS Revised autoconfiguration algorithm and interface 30-Sep-04 RMS Revised Qbus interface Moved mem_err, crd_err interrupts here from vax_cpu.c 09-Sep-04 RMS Integrated powerup into RESET (with -p) 05-Sep-04 RMS Added CRD interrupt handling 28-May-04 RMS Revised I/O dispatching (from John Dundas) 21-Mar-04 RMS Added RXV21 support 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner) 29-Oct-03 RMS Fixed WriteX declaration (found by Mark Pizzolato) 19-Apr-03 RMS Added optimized byte and word DMA routines 12-Mar-03 RMS Added logical name support 22-Dec-02 RMS Added console halt support 12-Oct-02 RMS Added autoconfigure support Added SHOW IO space routine 29-Sep-02 RMS Added dynamic table support 07-Sep-02 RMS Added TMSCP and variable vector support */ #include "vax_defs.h" /* CQBIC system configuration register */ #define CQSCR_POK 0x00008000 /* power ok RO1 */ #define CQSCR_BHL 0x00004000 /* BHALT enb */ #define CQSCR_AUX 0x00000400 /* aux mode RONI */ #define CQSCR_DBO 0x0000000C /* offset NI */ #define CQSCR_RW (CQSCR_BHL | CQSCR_DBO) #define CQSCR_MASK (CQSCR_RW | CQSCR_POK | CQSCR_AUX) /* CQBIC DMA system error register - W1C */ #define CQDSER_BHL 0x00008000 /* BHALT NI */ #define CQDSER_DCN 0x00004000 /* DC ~OK NI */ #define CQDSER_MNX 0x00000080 /* master NXM */ #define CQDSER_MPE 0x00000020 /* master par NI */ #define CQDSER_SME 0x00000010 /* slv mem err NI */ #define CQDSER_LST 0x00000008 /* lost err */ #define CQDSER_TMO 0x00000004 /* no grant NI */ #define CQDSER_SNX 0x00000001 /* slave NXM */ #define CQDSER_ERR (CQDSER_MNX | CQDSER_MPE | CQDSER_TMO | CQDSER_SNX) #define CQDSER_MASK 0x0000C0BD /* CQBIC master error address register */ #define CQMEAR_MASK 0x00001FFF /* Qbus page */ /* CQBIC slave error address register */ #define CQSEAR_MASK 0x000FFFFF /* mem page */ /* CQBIC map base register */ #define CQMBR_MASK 0x1FFF8000 /* 32KB aligned */ /* CQBIC IPC register */ #define CQIPC_QME 0x00008000 /* Qbus read NXM W1C */ #define CQIPC_INV 0x00004000 /* CAM inval NIWO */ #define CQIPC_AHLT 0x00000100 /* aux halt NI */ #define CQIPC_DBIE 0x00000040 /* dbell int enb NI */ #define CQIPC_LME 0x00000020 /* local mem enb */ #define CQIPC_DB 0x00000001 /* doorbell req NI */ #define CQIPC_W1C CQIPC_QME #define CQIPC_RW (CQIPC_AHLT | CQIPC_DBIE | CQIPC_LME | CQIPC_DB) #define CQIPC_MASK (CQIPC_RW | CQIPC_QME ) /* CQBIC map entry */ #define CQMAP_VLD 0x80000000 /* valid */ #define CQMAP_PAG 0x000FFFFF /* mem page */ int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */ int32 cq_scr = 0; /* SCR */ int32 cq_dser = 0; /* DSER */ int32 cq_mear = 0; /* MEAR */ int32 cq_sear = 0; /* SEAR */ int32 cq_mbr = 0; /* MBR */ int32 cq_ipc = 0; /* IPC */ int32 autcon_enb = 1; /* autoconfig enable */ extern uint32 *M; extern UNIT cpu_unit; extern int32 PSL, SISR, trpirq, mem_err, crd_err, hlt_pin; extern int32 p1; extern int32 ssc_bto; extern jmp_buf save_env; extern int32 sim_switches; extern DEVICE *sim_devices[]; extern FILE *sim_log; t_stat dbl_rd (int32 *data, int32 addr, int32 access); t_stat dbl_wr (int32 data, int32 addr, int32 access); int32 eval_int (void); void cq_merr (int32 pa); void cq_serr (int32 pa); t_stat qba_reset (DEVICE *dptr); t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); t_bool qba_map_addr (uint32 qa, uint32 *ma); t_bool qba_map_addr_c (uint32 qa, uint32 *ma); t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc); /* Qbus adapter data structures qba_dev QBA device descriptor qba_unit QBA units qba_reg QBA register list */ DIB qba_dib = { IOBA_DBL, IOLN_DBL, &dbl_rd, &dbl_wr, 0 }; UNIT qba_unit = { UDATA (NULL, 0, 0) }; REG qba_reg[] = { { HRDATA (SCR, cq_scr, 16) }, { HRDATA (DSER, cq_dser, 8) }, { HRDATA (MEAR, cq_mear, 13) }, { HRDATA (SEAR, cq_sear, 20) }, { HRDATA (MBR, cq_mbr, 29) }, { HRDATA (IPC, cq_ipc, 16) }, { HRDATA (IPL17, int_req[3], 32), REG_RO }, { HRDATA (IPL16, int_req[2], 32), REG_RO }, { HRDATA (IPL15, int_req[1], 32), REG_RO }, { HRDATA (IPL14, int_req[0], 32), REG_RO }, { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, { NULL } }; MTAB qba_mod[] = { { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, NULL, &show_iospace }, { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", &set_autocon, &show_autocon }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", &set_autocon, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, NULL, &qba_show_virt }, { 0 } }; DEVICE qba_dev = { "QBA", &qba_unit, qba_reg, qba_mod, 1, 16, CQMAWIDTH, 2, 16, 16, &qba_ex, &qba_dep, &qba_reset, NULL, NULL, NULL, &qba_dib, DEV_QBUS }; /* IO page dispatches */ t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); /* Interrupt request to interrupt action map */ int32 (*int_ack[IPL_HLVL][32])(); /* int ack routines */ /* Interrupt request to vector map */ int32 int_vec[IPL_HLVL][32]; /* int req to vector */ /* The KA65x handles errors in I/O space as follows - read: set DSER<7>, latch addr in MEAR, machine check - write: set DSER<7>, latch addr in MEAR, MEMERR interrupt */ int32 ReadQb (uint32 pa) { int32 idx, val; idx = (pa & IOPAGEMASK) >> 1; if (iodispR[idx]) { iodispR[idx] (&val, pa, READ); return val; } cq_merr (pa); MACH_CHECK (MCHK_READ); return 0; } void WriteQb (uint32 pa, int32 val, int32 mode) { int32 idx; idx = (pa & IOPAGEMASK) >> 1; if (iodispW[idx]) { iodispW[idx] (val, pa, mode); return; } cq_merr (pa); mem_err = 1; return; } /* ReadIO - read I/O space Inputs: pa = physical address lnt = length (BWLQ) Output: longword of data */ int32 ReadIO (uint32 pa, int32 lnt) { int32 iod; iod = ReadQb (pa); /* wd from Qbus */ if (lnt < L_LONG) /* bw? position */ iod = iod << ((pa & 2)? 16: 0); else iod = (ReadQb (pa + 2) << 16) | iod; /* lw, get 2nd wd */ SET_IRQL; return iod; } /* WriteIO - write I/O space Inputs: pa = physical address val = data to write, right justified in 32b longword lnt = length (BWLQ) Outputs: none */ void WriteIO (uint32 pa, int32 val, int32 lnt) { if (lnt == L_BYTE) WriteQb (pa, val, WRITEB); else if (lnt == L_WORD) WriteQb (pa, val, WRITE); else { WriteQb (pa, val & 0xFFFF, WRITE); WriteQb (pa + 2, (val >> 16) & 0xFFFF, WRITE); } SET_IRQL; return; } /* Find highest priority outstanding interrupt */ int32 eval_int (void) { int32 ipl = PSL_GETIPL (PSL); int32 i, t; static const int32 sw_int_mask[IPL_SMAX] = { 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */ 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */ 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */ 0xE000, 0xC000, 0x8000 /* C - E */ }; if (hlt_pin) /* hlt pin int */ return IPL_HLTPIN; if ((ipl < IPL_MEMERR) && mem_err) /* mem err int */ return IPL_MEMERR; if ((ipl < IPL_CRDERR) && crd_err) /* crd err int */ return IPL_CRDERR; for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ if (i <= ipl) /* at ipl? no int */ return 0; if (int_req[i - IPL_HMIN]) /* req != 0? int */ return i; } if (ipl >= IPL_SMAX) /* ipl >= sw max? */ return 0; if ((t = SISR & sw_int_mask[ipl]) == 0) /* eligible req */ return 0; for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */ if ((t >> i) & 1) /* req != 0? int */ return i; } return 0; } /* Return vector for highest priority hardware interrupt at IPL lvl */ int32 get_vector (int32 lvl) { int32 i; int32 l = lvl - IPL_HMIN; if (lvl == IPL_MEMERR) { /* mem error? */ mem_err = 0; return SCB_MEMERR; } if (lvl == IPL_CRDERR) { /* CRD error? */ crd_err = 0; return SCB_CRDERR; } if (lvl > IPL_HMAX) { /* error req lvl? */ ABORT (STOP_UIPL); /* unknown intr */ } for (i = 0; int_req[l] && (i < 32); i++) { if ((int_req[l] >> i) & 1) { int_req[l] = int_req[l] & ~(1u << i); if (int_ack[l][i]) return int_ack[l][i](); return int_vec[l][i]; } } return 0; } /* CQBIC registers SCR system configuration register DSER DMA system error register (W1C) MEAR master error address register (RO) SEAR slave error address register (RO) MBR map base register IPC inter-processor communication register */ int32 cqbic_rd (int32 pa) { int32 rg = (pa - CQBICBASE) >> 2; switch (rg) { case 0: /* SCR */ return (cq_scr | CQSCR_POK) & CQSCR_MASK; case 1: /* DSER */ return cq_dser & CQDSER_MASK; case 2: /* MEAR */ return cq_mear & CQMEAR_MASK; case 3: /* SEAR */ return cq_sear & CQSEAR_MASK; case 4: /* MBR */ return cq_mbr & CQMBR_MASK; } return 0; } void cqbic_wr (int32 pa, int32 val, int32 lnt) { int32 nval, rg = (pa - CQBICBASE) >> 2; if (lnt < L_LONG) { int32 sc = (pa & 3) << 3; int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; int32 t = cqbic_rd (pa); nval = ((val & mask) << sc) | (t & ~(mask << sc)); val = val << sc; } else nval = val; switch (rg) { case 0: /* SCR */ cq_scr = ((cq_scr & ~CQSCR_RW) | (nval & CQSCR_RW)) & CQSCR_MASK; break; case 1: /* DSER */ cq_dser = (cq_dser & ~val) & CQDSER_MASK; if (val & CQDSER_SME) cq_ipc = cq_ipc & ~CQIPC_QME; break; case 2: case 3: cq_merr (pa); /* MEAR, SEAR */ MACH_CHECK (MCHK_WRITE); break; case 4: /* MBR */ cq_mbr = nval & CQMBR_MASK; break; } return; } /* IPC can be read as local register or as Qbus I/O Because of the W1C */ int32 cqipc_rd (int32 pa) { return cq_ipc & CQIPC_MASK; /* IPC */ } void cqipc_wr (int32 pa, int32 val, int32 lnt) { int32 nval = val; if (lnt < L_LONG) { int32 sc = (pa & 3) << 3; nval = val << sc; } cq_ipc = cq_ipc & ~(nval & CQIPC_W1C); /* W1C */ if ((pa & 3) == 0) /* low byte only */ cq_ipc = ((cq_ipc & ~CQIPC_RW) | (val & CQIPC_RW)) & CQIPC_MASK; return; } /* I/O page routines */ t_stat dbl_rd (int32 *data, int32 addr, int32 access) { *data = cq_ipc & CQIPC_MASK; return SCPE_OK; } t_stat dbl_wr (int32 data, int32 addr, int32 access) { cqipc_wr (addr, data, (access == WRITEB)? L_BYTE: L_WORD); return SCPE_OK; } /* CQBIC map read and write (reflects to main memory) Read error: set DSER<0>, latch slave address, machine check Write error: set DSER<0>, latch slave address, memory error interrupt */ int32 cqmap_rd (int32 pa) { int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ if (ADDR_IS_MEM (ma)) return M[ma >> 2]; cq_serr (ma); /* set err */ MACH_CHECK (MCHK_READ); /* mcheck */ return 0; } void cqmap_wr (int32 pa, int32 val, int32 lnt) { int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ if (ADDR_IS_MEM (ma)) { if (lnt < L_LONG) { int32 sc = (pa & 3) << 3; int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; int32 t = M[ma >> 2]; val = ((val & mask) << sc) | (t & ~(mask << sc)); } M[ma >> 2] = val; } else { cq_serr (ma); /* error */ mem_err = 1; } return; } /* CQBIC Qbus memory read and write (reflects to main memory) May give master or slave error, depending on where the failure occurs */ int32 cqmem_rd (int32 pa) { int32 qa = pa & CQMAMASK; /* Qbus addr */ uint32 ma; if (qba_map_addr (qa, &ma)) /* map addr */ return M[ma >> 2]; MACH_CHECK (MCHK_READ); /* err? mcheck */ return 0; } void cqmem_wr (int32 pa, int32 val, int32 lnt) { int32 qa = pa & CQMAMASK; /* Qbus addr */ uint32 ma; if (qba_map_addr (qa, &ma)) { /* map addr */ if (lnt < L_LONG) { int32 sc = (pa & 3) << 3; int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; int32 t = M[ma >> 2]; val = ((val & mask) << sc) | (t & ~(mask << sc)); } M[ma >> 2] = val; } else mem_err = 1; return; } /* Map an address via the translation map */ t_bool qba_map_addr (uint32 qa, uint32 *ma) { int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ if (ADDR_IS_MEM (qmma)) { /* legit? */ int32 qmap = M[qmma >> 2]; /* get map */ if (qmap & CQMAP_VLD) { /* valid? */ *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); if (ADDR_IS_MEM (*ma)) /* legit addr */ return TRUE; cq_serr (*ma); /* slave nxm */ return FALSE; } cq_merr (qa); /* master nxm */ return FALSE; } cq_serr (0); /* inv mem */ return FALSE; } /* Map an address via the translation map - console version (no status changes) */ t_bool qba_map_addr_c (uint32 qa, uint32 *ma) { int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ if (ADDR_IS_MEM (qmma)) { /* legit? */ int32 qmap = M[qmma >> 2]; /* get map */ if (qmap & CQMAP_VLD) { /* valid? */ *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); return TRUE; /* legit addr */ } } return FALSE; } /* Set master error */ void cq_merr (int32 pa) { if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST; cq_dser = cq_dser | CQDSER_MNX; /* master nxm */ cq_mear = (pa >> VA_V_VPN) & CQMEAR_MASK; /* page addr */ return; } /* Set slave error */ void cq_serr (int32 pa) { if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST; cq_dser = cq_dser | CQDSER_SNX; /* slave nxm */ cq_sear = (pa >> VA_V_VPN) & CQSEAR_MASK; return; } /* Reset I/O bus */ void ioreset_wr (int32 data) { reset_all (5); /* from qba on... */ return; } /* Powerup CQBIC */ t_stat qba_powerup (void) { cq_mbr = 0; cq_scr = CQSCR_POK; return SCPE_OK; } /* Reset CQBIC */ t_stat qba_reset (DEVICE *dptr) { int32 i; if (sim_switches & SWMASK ('P')) qba_powerup (); cq_scr = (cq_scr & CQSCR_BHL) | CQSCR_POK; cq_dser = cq_mear = cq_sear = cq_ipc = 0; for (i = 0; i < IPL_HLVL; i++) int_req[i] = 0; return SCPE_OK; } /* Qbus I/O buffer routines, aligned access Map_ReadB - fetch byte buffer from memory Map_ReadW - fetch word buffer from memory Map_WriteB - store byte buffer into memory Map_WriteW - store word buffer into memory */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) { int32 i; uint32 ma, dat; if ((ba | bc) & 03) { /* check alignment */ for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } *buf = ReadB (ma); ma = ma + 1; } } else { for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } dat = ReadL (ma); /* get lw */ *buf++ = dat & BMASK; /* low 8b */ *buf++ = (dat >> 8) & BMASK; /* next 8b */ *buf++ = (dat >> 16) & BMASK; /* next 8b */ *buf = (dat >> 24) & BMASK; ma = ma + 4; } } return 0; } int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) { int32 i; uint32 ma,dat; ba = ba & ~01; bc = bc & ~01; if ((ba | bc) & 03) { /* check alignment */ for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } *buf = ReadW (ma); ma = ma + 2; } } else { for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } dat = ReadL (ma); /* get lw */ *buf++ = dat & WMASK; /* low 16b */ *buf = (dat >> 16) & WMASK; /* high 16b */ ma = ma + 4; } } return 0; } int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) { int32 i; uint32 ma, dat; if ((ba | bc) & 03) { /* check alignment */ for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } WriteB (ma, *buf); ma = ma + 1; } } else { for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } dat = (uint32) *buf++; /* get low 8b */ dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */ dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */ dat = dat | (((uint32) *buf) << 24); /* merge hi 8b */ WriteL (ma, dat); /* store lw */ ma = ma + 4; } } return 0; } int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) { int32 i; uint32 ma, dat; ba = ba & ~01; bc = bc & ~01; if ((ba | bc) & 03) { /* check alignment */ for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } WriteW (ma, *buf); ma = ma + 2; } } else { for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ if ((ma & VA_M_OFF) == 0) { /* need map? */ if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ return (bc - i); } dat = (uint32) *buf++; /* get low 16b */ dat = dat | (((uint32) *buf) << 16); /* merge hi 16b */ WriteL (ma, dat); /* store lw */ ma = ma + 4; } } return 0; } /* Memory examine via map (word only) */ t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { uint32 qa = (uint32) exta, pa; if ((vptr == NULL) || (qa >= CQMSIZE)) return SCPE_ARG; if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) { *vptr = (uint32) ReadW (pa); return SCPE_OK; } return SCPE_NXM; } /* Memory deposit via map (word only) */ t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { uint32 qa = (uint32) exta, pa; if (qa >= CQMSIZE) return SCPE_ARG; if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) { WriteW (pa, (int32) val); return SCPE_OK; } return SCPE_NXM; } /* Build dib_tab from device list */ t_stat build_dib_tab (void) { int32 i; DEVICE *dptr; DIB *dibp; t_stat r; init_ubus_tab (); /* init bus tables */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ if (r = build_ubus_tab (dptr, dibp)) /* add to bus tab */ return r; } /* end if enabled */ } /* end for */ return SCPE_OK; } /* Show QBA virtual address */ t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) { t_stat r; char *cptr = (char *) desc; uint32 qa, pa; if (cptr) { qa = (uint32) get_uint (cptr, 16, CQMSIZE - 1, &r); if (r == SCPE_OK) { if (qba_map_addr_c (qa, &pa)) fprintf (of, "Qbus %-X = physical %-X\n", qa, pa); else fprintf (of, "Qbus %-X: invalid mapping\n", qa); return SCPE_OK; } } fprintf (of, "Invalid argument\n"); return SCPE_OK; } simh-3.8.1/VAX/vax780_bug_history.txt0000644000175000017500000000665511017061700015520 0ustar vlmvlmBugs Found And Fixed During Simulator Debug 1. RP: drive clear does not clear RPDA. 2. TU: default formatter must be TM03. 3. SBI: drive 'letter' is actually a 1-based number. 4. MBA: drive register reads return SR<31:16> as high word. 5. UBA: DMA addresses must be masked to Unibus width (18b). 6. HK: thread used multiple times if SEEK is followed by NOP or DCLR. 7. HK: only DCLR clears ATA. 8. MEM: MS780E size declaration off-by-1. 9. MEM: MS780E array start off by >> 4. 10. MEM: CSR-C register write logic incorrect. 11. CIS: CMPP3/CMPP4 using wrong arguments to ReadDstr. 12. CPU, OCTA: CVTfi with integer overflow not setting trap if PSW = 1. 13. STDDEV: read of ICR was missing the call parameter. 14. ACBD/G: testing wrong operand register to get limit sign. 15. CPU: faults not clearing PSL. 16. ADAWI: register mode implemented incorrectly. 17. MOVTC: condition codes not preserved through page fault. 18. MOVTUC: condition codes not preserved through page fault. 19. MOVTUC: escape tested against untranslated rather than translated character. 20. CVTPT: condition code and decimal overflow calculation incorrect. 21. CVTPS: condition code and decimal overflow calculation incorrect. 22. CVTPL: if destination is register, result is stored after register updates. 23. CVTPL: integer overflow set rather than . 24. all decimal string: 11/780 does not validate characters in decimal strings. 25. EDITPC: condition codes not preserved through page fault. 26. EDITPC EO$INSERT: inserts sign instead of fill. 27. EDITPC EO$BLANK_ZERO: address off by one. 28. EDITPC EO$BLANK_ZERO: not testing for set. 29. EDITPC EO$LOAD_PLUS: not skipping character if test fails. 30. EDITPC EO$LOAD_MINUS: not skipping character if test fails. 31. Compatibility mode: SXT not implemented. 32. Compatibility mode: XOR operands fetched in wrong order. 33. MNEGH: condition codes set from original sign. 34. MNEGH: not cleared. 35. H_floating quad precision integer routines (add, inc, neg): carry propagation incorrect. 36. H_floating packup routines: test for zero used exponent not fraction. 37. MULH: carries out of floating accumulator lost. 38. DIVH: stores wrong operand as result. 39. POLYF/D/G: truncation after add not needed. 40. POLYF/D/G: early SRM requires truncation to 31b/63b, not 32b/64b. 41. POLYF/D/G/H: exits too early if argument is zero. 42. POLYD/G/H: calculates address of residual pointer result incorrectly. 43. POLYD/G: performs single precision rather than double precision multiply. 44. POLYH: fails to truncate intermediate result from 128b to 127b. 45. POLYF/D/G/H: internal add routine must test fraction rather than exponent to detect zero, POLYx can create "denormalized" intermediate result. 46. EMODH: concatenate 16b of extension operand instead of 15b. 47. Specifier flows: modify flows testing for read access rather than write access. 48. Quad/octa writes: wrong address reported on faulting cross-page writes. 49. Memory management: 11/780 implements access control test on first level PTE's. 50. LDPCTX: 11/780 implements mbz tests on PCB fields. 51. LDPCTX/MTPR: 11/780 validity checks PCBB, SCBB, SBR, SLR, P0BR, P0LR, P1BR, P1LR. 52. TMR: tmr_inc not updated in standard (100Hz) mode. 53. MTPR SBR/PCBB/SCBB: 11/780 only checks that bits<1:0> == 0. 54. MTPR xLR: 11/780 excludes bits<31:24> from mbz test. 55. MTPR PxBR: 11/780 only checks bits<31,1:0> == 1,00. simh-3.8.1/VAX/vax780_stddev.c0000644000175000017500000010762111112260236014052 0ustar vlmvlm/* vax780_stddev.c: VAX 11/780 standard I/O devices Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti console input tto console output rx console floppy todr TODR clock tmr interval timer 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 29-Oct-06 RMS Added clock coscheduler function Synced keyboard to clock for idling 11-May-06 RMS Revised timer logic for EVKAE 22-Nov-05 RMS Revised for new terminal processing routines 10-Mar-05 RMS Fixed bug in timer schedule routine (from Mark Hittinger) 08-Sep-04 RMS Cloned from vax_stddev.c, vax_sysdev.c, and pdp11_rx.c The console floppy protocol is based on the description in the 1982 VAX Architecture Reference Manual: TXDB<11:8> = 0 -> normal console output TXDB<11:8> = 1 -> data output to floppy TXDB<11:8> = 3 -> read communications region TXDB<11:8> = 9 -> command output to floppy TXDB<11:8> = F -> flag output (e.g., reboot) RXDB<11:8> = 0 -> normal terminal input RXDB<11:8> = 1 -> data input from floppy RXDB<11:8> = 3 -> communications region data RXDB<11:8> = 2 -> status input from floppy RXDB<11:8> = 9 -> "command" input from floppy (protocol error) */ #include "vax_defs.h" #include /* Terminal definitions */ #define RXCS_RD (CSR_DONE + CSR_IE) /* terminal input */ #define RXCS_WR (CSR_IE) #define RXDB_ERR 0x8000 /* error */ #define RXDB_OVR 0x4000 /* overrun */ #define RXDB_FRM 0x2000 /* framing error */ #define TXCS_RD (CSR_DONE + CSR_IE) /* terminal output */ #define TXCS_WR (CSR_IE) #define TXDB_V_SEL 8 /* unit select */ #define TXDB_M_SEL 0xF #define TXDB_FDAT 0x1 /* floppy data */ #define TXDB_COMM 0x3 /* console mem read */ #define TXDB_FCMD 0x9 /* floppy cmd */ #define TXDB_MISC 0xF /* console misc */ #define COMM_LNT 0200 /* comm region lnt */ #define COMM_MASK (COMM_LNT - 1) /* comm region mask */ #define COMM_GH 0144 /* GH flag */ #define COMM_WRMS 0145 /* warm start */ #define COMM_CLDS 0146 /* cold start */ #define COMM_APTL 0147 /* APT load */ #define COMM_LAST 0150 /* last position */ #define COMM_AUTO 0151 /* auto restart */ #define COMM_PCSV 0152 /* PCS version */ #define COMM_WCSV 0153 /* WCS version */ #define COMM_WCSS 0154 /* WCS secondary */ #define COMM_FPLV 0155 /* FPLA version */ #define COMM_DATA 0x300 /* comm data return */ #define MISC_MASK 0xFF /* console data mask */ #define MISC_SWDN 0x1 /* software done */ #define MISC_BOOT 0x2 /* reboot */ #define MISC_CLWS 0x3 /* clear warm start */ #define MISC_CLCS 0x4 /* clear cold start */ #define TXDB_SEL (TXDB_M_SEL << TXDB_V_SEL) /* non-terminal */ #define TXDB_GETSEL(x) (((x) >> TXDB_V_SEL) & TXDB_M_SEL) /* Clock definitions */ #define TMR_CSR_ERR 0x80000000 /* error W1C */ #define TMR_CSR_DON 0x00000080 /* done W1C */ #define TMR_CSR_IE 0x00000040 /* int enb RW */ #define TMR_CSR_SGL 0x00000020 /* single WO */ #define TMR_CSR_XFR 0x00000010 /* xfer WO */ #define TMR_CSR_RUN 0x00000001 /* run RW */ #define TMR_CSR_RD (TMR_CSR_W1C | TMR_CSR_WR) #define TMR_CSR_W1C (TMR_CSR_ERR | TMR_CSR_DON) #define TMR_CSR_WR (TMR_CSR_IE | TMR_CSR_RUN) #define TMR_INC 10000 /* usec/interval */ #define CLK_DELAY 5000 /* 100 Hz */ #define TMXR_MULT 1 /* 100 Hz */ /* Floppy definitions */ #define FL_NUMTR 77 /* tracks/disk */ #define FL_M_TRACK 0377 #define FL_NUMSC 26 /* sectors/track */ #define FL_M_SECTOR 0177 #define FL_NUMBY 128 /* bytes/sector */ #define FL_SIZE (FL_NUMTR * FL_NUMSC * FL_NUMBY) /* bytes/disk */ #define UNIT_V_WLK (UNIT_V_UF) /* write locked */ #define UNIT_WLK (1u << UNIT_V_UF) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define FL_IDLE 0 /* idle state */ #define FL_RWDS 1 /* rw, sect next */ #define FL_RWDT 2 /* rw, track next */ #define FL_READ 3 /* read */ #define FL_READ1 4 #define FL_WRITE 5 /* write */ #define FL_WRITE1 6 #define FL_FILL 7 /* fill buffer */ #define FL_EMPTY 8 /* empty buffer */ #define FL_READSTA 9 /* read status */ #define FL_DONE 10 /* cmd done */ #define FL_V_FNC 0 /* floppy function */ #define FL_M_FNC 0xFF #define FL_FNCRD 0x0 /* read */ #define FL_FNCWR 0x1 /* write */ #define FL_FNCRS 0x2 /* read status */ #define FL_FNCWD 0x3 /* write del data */ #define FL_FNCCA 0x4 /* cancel */ #define FL_CDATA 0x100 /* returned data */ #define FL_CDONE 0x200 /* completion code */ #define FL_STACRC 0x001 /* status bits */ #define FL_STAPAR 0x002 #define FL_STAINC 0x004 #define FL_STADDA 0x040 #define FL_STAERR 0x080 #define FL_CPROT 0x905 /* protocol error */ #define FL_GETFNC(x) (((x) >> FL_V_FNC) & FL_M_FNC) #define TRACK u3 /* current track */ #define CALC_DA(t,s) (((t) * FL_NUMSC) + ((s) - 1)) * FL_NUMBY int32 tti_csr = 0; /* control/status */ int32 tti_buf = 0; /* buffer */ int32 tti_int = 0; /* interrupt */ int32 tto_csr = 0; /* control/status */ int32 tto_buf = 0; /* buffer */ int32 tto_int = 0; /* interrupt */ int32 tmr_iccs = 0; /* interval timer csr */ uint32 tmr_icr = 0; /* curr interval */ uint32 tmr_nicr = 0; /* next interval */ uint32 tmr_inc = 0; /* timer increment */ int32 tmr_sav = 0; /* timer save */ int32 tmr_int = 0; /* interrupt */ int32 tmr_use_100hz = 1; /* use 100Hz for timer */ int32 clk_tps = 100; /* ticks/second */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 todr_reg = 0; /* TODR register */ int32 fl_fnc = 0; /* function */ int32 fl_esr = 0; /* error status */ int32 fl_ecode = 0; /* error code */ int32 fl_track = 0; /* desired track */ int32 fl_sector = 0; /* desired sector */ int32 fl_state = FL_IDLE; /* controller state */ int32 fl_stopioe = 1; /* stop on error */ int32 fl_swait = 100; /* seek, per track */ int32 fl_cwait = 50; /* command time */ int32 fl_xwait = 20; /* tr set time */ uint8 fl_buf[FL_NUMBY] = { 0 }; /* sector buffer */ int32 fl_bptr = 0; /* buffer pointer */ uint8 comm_region[COMM_LNT] = { 0 }; /* comm region */ extern int32 sim_switches; extern jmp_buf save_env; t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat clk_svc (UNIT *uptr); t_stat tmr_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr); t_stat fl_svc (UNIT *uptr); t_stat fl_reset (DEVICE *dptr); int32 icr_rd (t_bool interp); void tmr_incr (uint32 inc); void tmr_sched (void); t_stat todr_resync (void); t_stat fl_wr_txdb (int32 data); t_bool fl_test_xfr (UNIT *uptr, t_bool wr); void fl_protocol_error (void); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list */ UNIT tti_unit = { UDATA (&tti_svc, TT_MODE_8B, 0), 0 }; REG tti_reg[] = { { HRDATA (RXDB, tti_buf, 16) }, { HRDATA (RXCS, tti_csr, 16) }, { FLDATA (INT, tti_int, 0) }, { FLDATA (DONE, tti_csr, CSR_V_DONE) }, { FLDATA (IE, tti_csr, CSR_V_IE) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tti_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 16, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, NULL, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { HRDATA (TXDB, tto_buf, 16) }, { HRDATA (TXCS, tto_csr, 16) }, { FLDATA (INT, tto_int, 0) }, { FLDATA (DONE, tto_csr, CSR_V_DONE) }, { FLDATA (IE, tto_csr, CSR_V_IE) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT + REG_NZ }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 16, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, NULL, 0 }; /* TODR and TMR data structures */ UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ REG clk_reg[] = { { DRDATA (TODR, todr_reg, 32), PV_LEFT }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), REG_HIDDEN + REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { "TODR", &clk_unit, clk_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, NULL, 0 }; UNIT tmr_unit = { UDATA (&tmr_svc, 0, 0) }; /* timer */ REG tmr_reg[] = { { HRDATA (ICCS, tmr_iccs, 32) }, { HRDATA (ICR, tmr_icr, 32) }, { HRDATA (NICR, tmr_nicr, 32) }, { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN }, { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN }, { FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN }, { FLDATA (INT, tmr_int, 0) }, { NULL } }; DEVICE tmr_dev = { "TMR", &tmr_unit, tmr_reg, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, &tmr_reset, NULL, NULL, NULL, NULL, 0 }; /* RX01 data structures fl_dev RX device descriptor fl_unit RX unit list fl_reg RX register list fl_mod RX modifier list */ UNIT fl_unit = { UDATA (&fl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, FL_SIZE) }; REG fl_reg[] = { { HRDATA (FNC, fl_fnc, 8) }, { HRDATA (ES, fl_esr, 8) }, { HRDATA (ECODE, fl_ecode, 8) }, { HRDATA (TA, fl_track, 8) }, { HRDATA (SA, fl_sector, 8) }, { DRDATA (STATE, fl_state, 4), REG_RO }, { DRDATA (BPTR, fl_bptr, 7) }, { DRDATA (CTIME, fl_cwait, 24), PV_LEFT }, { DRDATA (STIME, fl_swait, 24), PV_LEFT }, { DRDATA (XTIME, fl_xwait, 24), PV_LEFT }, { FLDATA (STOP_IOE, fl_stopioe, 0) }, { BRDATA (DBUF, fl_buf, 16, 8, FL_NUMBY) }, { BRDATA (COMM, comm_region, 16, 8, COMM_LNT) }, { NULL } }; MTAB fl_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { 0 } }; DEVICE fl_dev = { "RX", &fl_unit, fl_reg, fl_mod, 1, DEV_RDX, 20, 1, DEV_RDX, 8, NULL, NULL, &fl_reset, NULL, NULL, NULL, NULL, 0 }; /* Terminal MxPR routines rxcs_rd/wr input control/status rxdb_rd input buffer txcs_rd/wr output control/status txdb_wr output buffer */ int32 rxcs_rd (void) { return (tti_csr & RXCS_RD); } void rxcs_wr (int32 data) { if ((data & CSR_IE) == 0) tto_int = 0; else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) tti_int = 1; tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); return; } int32 rxdb_rd (void) { int32 t = tti_buf; /* char + error */ tti_csr = tti_csr & ~CSR_DONE; /* clr done */ tti_buf = tti_buf & BMASK; /* clr errors */ tti_int = 0; return t; } int32 txcs_rd (void) { return (tto_csr & TXCS_RD); } void txcs_wr (int32 data) { if ((data & CSR_IE) == 0) tto_int = 0; else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) tto_int = 1; tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR); return; } void txdb_wr (int32 data) { tto_buf = data & WMASK; /* save data */ tto_csr = tto_csr & ~CSR_DONE; /* clear flag */ tto_int = 0; /* clear int */ if (tto_buf & TXDB_SEL) /* floppy? */ fl_wr_txdb (tto_buf); else sim_activate (&tto_unit, tto_unit.wait); /* no, console */ return; } /* Terminal input service (poll for character) */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) /* break? */ tti_buf = RXDB_ERR | RXDB_FRM; else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); uptr->pos = uptr->pos + 1; tti_csr = tti_csr | CSR_DONE; if (tti_csr & CSR_IE) tti_int = 1; return SCPE_OK; } /* Terminal input reset */ t_stat tti_reset (DEVICE *dptr) { tti_buf = 0; tti_csr = 0; tti_int = 0; sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); return SCPE_OK; } /* Terminal output service (output character) */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; if ((tto_buf & TXDB_SEL) == 0) { /* for console? */ c = sim_tt_outcvt (tto_buf, TT_GET_MODE (uptr->flags)); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } } uptr->pos = uptr->pos + 1; } tto_csr = tto_csr | CSR_DONE; if (tto_csr & CSR_IE) tto_int = 1; return SCPE_OK; } /* Terminal output reset */ t_stat tto_reset (DEVICE *dptr) { tto_buf = 0; tto_csr = CSR_DONE; tto_int = 0; sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } /* Programmable timer The architected VAX timer, which increments at 1Mhz, cannot be accurately simulated due to the overhead that would be required for 1M clock events per second. Instead, a hidden calibrated 100Hz timer is run (because that's what VMS expects), and a hack is used for the interval timer. When the timer is started, the timer interval is inspected. if the interval is >= 10msec, then the 100Hz timer drives the next interval if the interval is < 10mec, then count instructions If the interval register is read, then its value between events is interpolated using the current instruction count versus the count when the most recent event started, the result is scaled to the calibrated system clock, unless the interval being timed is less than a calibrated system clock tick (or the calibrated clock is running very slowly) at which time the result will be the elapsed instruction count. */ int32 iccs_rd (void) { return tmr_iccs & TMR_CSR_RD; } void iccs_wr (int32 val) { if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ sim_cancel (&tmr_unit); /* cancel timer */ tmr_use_100hz = 0; if (tmr_iccs & TMR_CSR_RUN) /* run 1 -> 0? */ tmr_icr = icr_rd (TRUE); /* update itr */ } tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */ tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */ (val & TMR_CSR_WR); if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr; /* xfr set? */ if (val & TMR_CSR_RUN) { /* run? */ if (val & TMR_CSR_XFR) /* new tir? */ sim_cancel (&tmr_unit); /* stop prev */ if (!sim_is_active (&tmr_unit)) /* not running? */ tmr_sched (); /* activate */ } else if (val & TMR_CSR_SGL) { /* single step? */ tmr_incr (1); /* incr tmr */ if (tmr_icr == 0) /* if ovflo, */ tmr_icr = tmr_nicr; /* reload tir */ } if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ (TMR_CSR_DON | TMR_CSR_IE)) tmr_int = 0; return; } int32 icr_rd (t_bool interp) { uint32 delta; if (interp || (tmr_iccs & TMR_CSR_RUN)) { /* interp, running? */ delta = sim_grtime () - tmr_sav; /* delta inst */ if (tmr_use_100hz && (tmr_poll > TMR_INC)) /* scale large int */ delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll); if (delta >= tmr_inc) delta = tmr_inc - 1; return tmr_icr + delta; } return tmr_icr; } int32 nicr_rd () { return tmr_nicr; } void nicr_wr (int32 val) { tmr_nicr = val; } /* 100Hz base clock unit service */ t_stat clk_svc (UNIT *uptr) { tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ todr_reg = todr_reg + 1; /* incr TODR */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ tmr_incr (TMR_INC); /* do timer service */ return SCPE_OK; } /* Interval timer unit service */ t_stat tmr_svc (UNIT *uptr) { tmr_incr (tmr_inc); /* incr timer */ return SCPE_OK; } /* Timer increment */ void tmr_incr (uint32 inc) { uint32 new_icr = (tmr_icr + inc) & LMASK; /* add incr */ if (new_icr < tmr_icr) { /* ovflo? */ tmr_icr = 0; /* now 0 */ if (tmr_iccs & TMR_CSR_DON) /* done? set err */ tmr_iccs = tmr_iccs | TMR_CSR_ERR; else tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */ if (tmr_iccs & TMR_CSR_RUN) { /* run? */ tmr_icr = tmr_nicr; /* reload */ tmr_sched (); /* reactivate */ } if (tmr_iccs & TMR_CSR_IE) /* ie? set int req */ tmr_int = 1; else tmr_int = 0; } else { tmr_icr = new_icr; /* no, update icr */ if (tmr_iccs & TMR_CSR_RUN) /* still running? */ tmr_sched (); /* reactivate */ } return; } /* Timer scheduling */ void tmr_sched (void) { tmr_sav = sim_grtime (); /* save intvl base */ tmr_inc = (~tmr_icr + 1); /* inc = interval */ if (tmr_inc == 0) tmr_inc = 1; if (tmr_inc < TMR_INC) { /* 100Hz multiple? */ sim_activate (&tmr_unit, tmr_inc); /* schedule timer */ tmr_use_100hz = 0; } else tmr_use_100hz = 1; /* let clk handle */ return; } /* Clock coscheduling routine */ int32 clk_cosched (int32 wait) { int32 t; t = sim_is_active (&clk_unit); return (t? t - 1: wait); } /* 100Hz clock reset */ t_stat clk_reset (DEVICE *dptr) { tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ return SCPE_OK; } /* Interval timer reset */ t_stat tmr_reset (DEVICE *dptr) { tmr_iccs = 0; tmr_icr = 0; tmr_nicr = 0; tmr_int = 0; tmr_use_100hz = 1; sim_cancel (&tmr_unit); /* cancel timer */ todr_resync (); /* resync TODR */ return SCPE_OK; } /* TODR routines */ int32 todr_rd (void) { return todr_reg; } void todr_wr (int32 data) { todr_reg = data; return; } t_stat todr_resync (void) { uint32 base; time_t curr; struct tm *ctm; curr = time (NULL); /* get curr time */ if (curr == (time_t) -1) /* error? */ return SCPE_NOFNC; ctm = localtime (&curr); /* decompose */ if (ctm == NULL) /* error? */ return SCPE_NOFNC; base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ ctm->tm_hour) * 60) + ctm->tm_min) * 60) + ctm->tm_sec; todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ return SCPE_OK; } /* Console write, txdb<11:8> != 0 (console unit) */ t_stat fl_wr_txdb (int32 data) { int32 sel = TXDB_GETSEL (data); /* get selection */ if (sel == TXDB_FCMD) { /* floppy command? */ fl_fnc = FL_GETFNC (data); /* get function */ if (fl_state != FL_IDLE) /* cmd in prog? */ switch (fl_fnc) { case FL_FNCCA: /* cancel? */ sim_cancel (&fl_unit); /* stop op */ fl_state = FL_DONE; break; default: /* all others */ fl_protocol_error (); return SCPE_OK; } else switch (fl_fnc) { /* idle, case */ case FL_FNCRS: /* read status */ fl_state = FL_READSTA; break; case FL_FNCCA: /* cancel, nop */ fl_state = FL_DONE; break; case FL_FNCRD: case FL_FNCWR: /* data xfer */ case FL_FNCWD: fl_esr = 0; /* clear errors */ fl_ecode = 0; fl_bptr = 0; /* init buffer */ fl_state = FL_RWDS; /* sector next */ break; default: /* all others */ fl_protocol_error (); return SCPE_OK; } sim_activate (&fl_unit, fl_cwait); /* sched command */ } /* end command */ else if (sel == TXDB_FDAT) { /* floppy data? */ switch (fl_state) { /* data */ case FL_RWDS: /* expecting sector */ fl_sector = data & FL_M_SECTOR; fl_state = FL_RWDT; break; case FL_RWDT: /* expecting track */ fl_track = data & FL_M_TRACK; if (fl_fnc == FL_FNCRD) fl_state = FL_READ; else fl_state = FL_FILL; break; case FL_FILL: /* expecting wr data */ fl_buf[fl_bptr++] = data & BMASK; if (fl_bptr >= FL_NUMBY) fl_state = FL_WRITE; break; default: fl_protocol_error (); return SCPE_OK; } sim_activate (&fl_unit, fl_xwait); /* schedule xfer */ } /* end else data */ else { sim_activate (&tto_unit, tto_unit.wait); /* set up timeout */ if (sel == TXDB_COMM) { /* read comm region? */ data = data & COMM_MASK; /* byte to select */ tti_buf = comm_region[data] | COMM_DATA; tti_csr = tti_csr | CSR_DONE; /* set input flag */ if (tti_csr & CSR_IE) tti_int = 1; } else if (sel == TXDB_MISC) { /* misc function? */ switch (data & MISC_MASK) { /* case on function */ case MISC_CLWS: comm_region[COMM_WRMS] = 0; case MISC_CLCS: comm_region[COMM_CLDS] = 0; break; case MISC_SWDN: ABORT (STOP_SWDN); break; case MISC_BOOT: ABORT (STOP_BOOT); break; } } } return SCPE_OK; } /* Unit service; the action to be taken depends on the transfer state: FL_IDLE Should never get here FL_RWDS Set TXCS (driver sends sector, sets FL_RWDT) FL_RWDT Set TXCS (driver sends track, sets FL_READ/FL_FILL) FL_READ Set TXCS, schedule FL_READ1 FL_READ1 Read sector, schedule FL_EMPTY FL_EMPTY Copy data to RXDB, set RXCS if fl_bptr >= max, schedule completion, else continue FL_FILL Set TXCS (driver sends next byte, sets FL_WRITE) FL_WRITE Set TXCS, schedule FL_WRITE1 FL_WRITE1 Write sector, schedule FL_DONE FL_DONE Copy requested data to TXDB, set FL_IDLE */ t_stat fl_svc (UNIT *uptr) { int32 i, t; uint32 da; int8 *fbuf = uptr->filebuf; switch (fl_state) { /* case on state */ case FL_IDLE: /* idle */ return SCPE_IERR; /* done */ case FL_READ: case FL_WRITE: /* read, write */ fl_state = fl_state + 1; /* set next state */ t = abs (fl_track - uptr->TRACK); /* # tracks to seek */ if (t == 0) /* minimum 1 */ t = 1; sim_activate (uptr, fl_swait * t); /* schedule seek */ /* fall thru, set flag */ case FL_RWDS: case FL_RWDT: case FL_FILL: /* rwds, rwdt, fill */ tto_csr = tto_csr | CSR_DONE; /* set output done */ if (tto_csr & CSR_IE) tto_int = 1; break; case FL_READ1: /* read, seek done */ if (fl_test_xfr (uptr, FALSE)) { /* transfer ok? */ da = CALC_DA (fl_track, fl_sector); /* get disk address */ for (i = 0; i < FL_NUMBY; i++) /* copy sector to buf */ fl_buf[i] = fbuf[da + i]; tti_buf = fl_esr | FL_CDONE; /* completion code */ tti_csr = tti_csr | CSR_DONE; /* set input flag */ if (tti_csr & CSR_IE) tti_int = 1; fl_state = FL_EMPTY; /* go empty */ } else fl_state = FL_DONE; /* error? cmd done */ sim_activate (uptr, fl_xwait); /* schedule next */ break; case FL_EMPTY: /* empty buffer */ if ((tti_csr & CSR_DONE) == 0) { /* prev data taken? */ tti_buf = FL_CDATA | fl_buf[fl_bptr++]; /* get next byte */ tti_csr = tti_csr | CSR_DONE; /* set input flag */ if (tti_csr & CSR_IE) tti_int = 1; if (fl_bptr >= FL_NUMBY) { /* buffer empty? */ fl_state = FL_IDLE; /* cmd done */ break; } } sim_activate (uptr, fl_xwait); /* schedule next */ break; case FL_WRITE1: /* write, seek done */ if (fl_test_xfr (uptr, TRUE)) { /* transfer ok? */ da = CALC_DA (fl_track, fl_sector); /* get disk address */ for (i = 0; i < FL_NUMBY; i++) /* copy buf to sector */ fbuf[da + i] = fl_buf[i]; da = da + FL_NUMBY; if (da > uptr->hwmark) /* update hwmark */ uptr->hwmark = da; } if (fl_fnc == FL_FNCWD) /* wrdel? set status */ fl_esr |= FL_STADDA; fl_state = FL_DONE; /* command done */ sim_activate (uptr, fl_xwait); /* schedule */ break; case FL_DONE: /* command done */ if (tti_csr & CSR_DONE) /* input buf empty? */ sim_activate (uptr, fl_xwait); /* no, wait */ else { /* yes */ tti_buf = fl_esr | FL_CDONE; /* completion code */ tti_csr = tti_csr | CSR_DONE; /* set input flag */ if (tti_csr & CSR_IE) tti_int = 1; fl_state = FL_IDLE; /* floppy idle */ } break; case FL_READSTA: /* read status */ if ((tti_csr & CSR_DONE) == 0) { /* input buf empty? */ tti_buf = fl_ecode; /* return err code */ tti_csr = tti_csr | CSR_DONE; /* set input flag */ if (tti_csr & CSR_IE) tti_int = 1; fl_state = FL_DONE; /* command done */ } sim_activate (uptr, fl_xwait); break; } return SCPE_OK; } /* Test for data transfer okay */ t_bool fl_test_xfr (UNIT *uptr, t_bool wr) { if ((uptr->flags & UNIT_BUF) == 0) /* not buffered? */ fl_ecode = 0110; else if (fl_track >= FL_NUMTR) /* bad track? */ fl_ecode = 0040; /* done, error */ else if ((fl_sector == 0) || (fl_sector > FL_NUMSC)) /* bad sect? */ fl_ecode = 0070; /* done, error */ else if (wr && (uptr->flags & UNIT_WPRT)) /* write and locked? */ fl_ecode = 0100; /* done, error */ else { uptr->TRACK = fl_track; /* now on track */ return TRUE; } fl_esr = fl_esr | FL_STAERR; /* set error */ return FALSE; } /* Set protocol error */ void fl_protocol_error (void) { if ((tto_csr & CSR_DONE) == 0) { /* output busy? */ tto_csr = tto_csr | CSR_DONE; /* set done */ if (tto_csr & CSR_IE) tto_int = 1; } if ((tti_csr & CSR_DONE) == 0) { /* input idle? */ tti_csr = tti_csr | CSR_DONE; /* set done */ if (tti_csr & CSR_IE) tti_int = 1; } tti_buf = FL_CPROT; /* status */ fl_state = FL_IDLE; /* floppy idle */ return; } /* Reset */ t_stat fl_reset (DEVICE *dptr) { uint32 i; fl_esr = FL_STAINC; fl_ecode = 0; /* clear error */ fl_sector = 0; /* clear addr */ fl_track = 0; fl_state = FL_IDLE; /* ctrl idle */ fl_bptr = 0; sim_cancel (&fl_unit); /* cancel drive */ fl_unit.TRACK = 0; for (i = 0; i < COMM_LNT; i++) comm_region[i] = 0; comm_region[COMM_FPLV] = VER_FPLA; comm_region[COMM_PCSV] = VER_PCS; comm_region[COMM_WCSV] = VER_WCSP; comm_region[COMM_WCSS] = VER_WCSS; comm_region[COMM_GH] = 1; return SCPE_OK; } simh-3.8.1/VAX/vax_cpu.c0000644000175000017500000036306411112312310013105 0ustar vlmvlm/* vax_cpu.c: VAX CPU Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu VAX central processor 21-May-08 RMS Removed inline support 28-May-08 RMS Inlined instruction prefetch, physical memory routines 13-Aug-07 RMS Fixed bug in read access g-format indexed specifiers 28-Apr-07 RMS Removed clock initialization 29-Oct-06 RMS Added idle support 22-May-06 RMS Fixed format error in CPU history (found by Peter Schorn) 10-May-06 RMS Added -kesu switches for virtual addressing modes Fixed bugs in examine virtual Rewrote history function for greater usability Fixed bug in reported VA on faulting cross-page write 02-May-06 RMS Fixed fault cleanup to clear PSL Fixed ADAWI r-mode to preserve dst<31:16> Fixed ACBD/G to test correct operand Fixed access checking on modify-class specifiers Fixed branch displacements in history buffer (all reported by Tim Stark) 17-Nov-05 RMS Fixed CVTfi with integer overflow to trap if PSW set 13-Nov-05 RMS Fixed breakpoint test with 64b addresses 25-Oct-05 RMS Removed cpu_extmem 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 13-Jan-05 RMS Fixed initial state of cpu_extmem 06-Nov-04 RMS Added =n to SHOW HISTORY 30-Sep-04 RMS Added octaword specifier decodes and instructions Moved model-specific routines to system module 02-Sep-04 RMS Fixed bug in EMODD/G, second word of quad dst not probed 28-Jun-04 RMS Fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) 18-Apr-04 RMS Added octaword macros 25-Jan-04 RMS Removed local debug logging support RMS,MP Added extended physical memory support 31-Dec-03 RMS Fixed bug in set_cpu_hist 21-Dec-03 RMS Added autoconfiguration controls 29-Oct-03 RMS Fixed WriteB declaration (found by Mark Pizzolato) 23-Sep-03 RMS Revised instruction history for dynamic sizing 17-May-03 RMS Fixed operand order in EMODx 23-Apr-03 RMS Revised for 32b/64b t_addr 05-Jan-02 RMS Added memory size restore support 25-Dec-02 RMS Added instruction history (from Mark Pizzolato) 29-Sep-02 RMS Revised to build dib_tab dynamically 14-Jul-02 RMS Added halt to console, infinite loop detection (from Mark Pizzolato) 02-May-02 RMS Fixed bug in indexed autoincrement register logging 30-Apr-02 RMS Added TODR powerup routine 18-Apr-02 RMS Cleanup ambiguous signed left shifts 15-Apr-02 RMS Fixed bug in CASEL condition codes The register state for the VAX is: R[0:15] general registers PSL<31:0> processor status longword TP<30> trace pending FPD<27> first part done IS<26> interrupt stack CM<25:24> current mode PM<23:22> previous mode IPL<20:16> interrupt priority level PSW<15:0> non-privileged processor status word DV<7> decimal overflow trap enable FU<6> floating underflow fault enable IV<5> integer overflow trap enable T<4> trace trap enable CC<3:0> condition codes SCBB system control block base PCBB process control block base SBR system page table base SLR system page table length P0BR process region 0 page table base P0LR process region 0 page table length P1BR process region 1 page table base P1LR process region 1 page table length SIRR/SISR software interrupt request/summary register ASTLVL AST level register The VAX has a variable length instruction format with up to six operands: opcode byte operand 1 specifier : operand n specifier Each operand specifier is a byte consisting of an addressing mode, a register, and possibly 1-8 bytes of extension: number name extension mnemonic operation 0-3 short literal - #n op <- specifier 4 index - [Rn] index by Rn 5 register - Rn op <- Rn 6 register def - (Rn) op <- M[Rn] 7 autodecrement - -(Rn) Rn <- Rn - length op <- M[Rn] 8 autoincrement - (Rn)+ op <- M[Rn] Rn <- Rn + length 9 auto deferred - @(Rn)+ op <- M[M[Rn]] Rn <- Rn + 4 A byte displ byte d d(Rn) op <- M[Rn + sxt.d] B byte displ def byte d @d(Rn) op <- M[M[Rn + sxt.d]] C word displ word d d(Rn) op <- M[Rn + sxt.d] D word displ def word d @d(Rn) op <- M[M[Rn + sxt.d]] E long displ long d d(Rn) op <- M[Rn + d] F long displ def long d @d(Rn) op <- M[M[Rn + d]] When the general register is the PC, certain modes are forbidden, and others have special interpretations: 4F index fault 5F register fault 6F register def fault 7F autodecrement fault 8F immediate 1-8B #imm op <- imm 9 absolute 4B @#imm op <- M[imm] A byte relative byte d d(Rn) op <- M[PC + sxt.d] B byte rel def byte d @d(Rn) op <- M[M[PC + sxt.d]] C word relative word d d(Rn) op <- M[PC + sxt.d] D word rel def word d @d(Rn) op <- M[M[PC + sxt.d]] E long relative long d d(Rn) op <- M[PC + d] F long rel def long d @d(Rn) op <- M[M[PC + d]] This routine is the instruction decode routine for the VAX. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until an enabled exception is encountered. General notes: 1. Traps and interrupts. Variable trpirq microencodes the outstanding trap request (if any) and the level of the highest outstanding interrupt (if any). 2. Interrupt requests are maintained in the int_req array, one word per interrupt level, one bit per device. 3. Adding I/O devices. These modules must be modified: vax_defs.h add device address and interrupt definitions vax_sys.c add sim_devices table entry */ /* Definitions */ #include "vax_defs.h" #define OP_MEM -1 #define UNIT_V_CONH (UNIT_V_UF + 0) /* halt to console */ #define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy */ #define UNIT_CONH (1u << UNIT_V_CONH) #define UNIT_MSIZE (1u << UNIT_V_MSIZE) #define GET_CUR acc = ACC_MASK (PSL_GETCUR (PSL)) #define OPND_SIZE 16 #define INST_SIZE 52 #define op0 opnd[0] #define op1 opnd[1] #define op2 opnd[2] #define op3 opnd[3] #define op4 opnd[4] #define op5 opnd[5] #define op6 opnd[6] #define op7 opnd[7] #define op8 opnd[8] #define CHECK_FOR_PC if (rn == nPC) \ RSVD_ADDR_FAULT #define CHECK_FOR_SP if (rn >= nSP) \ RSVD_ADDR_FAULT #define CHECK_FOR_AP if (rn >= nAP) \ RSVD_ADDR_FAULT #define WRITE_B(r) if (spec > (GRN | nPC)) \ Write (va, r, L_BYTE, WA); \ else R[rn] = (R[rn] & ~BMASK) | ((r) & BMASK) #define WRITE_W(r) if (spec > (GRN | nPC)) \ Write (va, r, L_WORD, WA); \ else R[rn] = (R[rn] & ~WMASK) | ((r) & WMASK) #define WRITE_L(r) if (spec > (GRN | nPC)) \ Write (va, r, L_LONG, WA); \ else R[rn] = (r) #define WRITE_Q(rl,rh) if (spec > (GRN | nPC)) { \ if ((Test (va + 7, WA, &mstat) >= 0) || \ (Test (va, WA, &mstat) < 0)) \ Write (va, rl, L_LONG, WA); \ Write (va + 4, rh, L_LONG, WA); \ } \ else { \ if (rn >= nSP) \ RSVD_ADDR_FAULT; \ R[rn] = rl; \ R[rn + 1] = rh; \ } #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { int32 iPC; int32 PSL; int32 opc; uint8 inst[INST_SIZE]; int32 opnd[OPND_SIZE]; } InstHistory; uint32 *M = NULL; /* memory */ int32 R[16]; /* registers */ int32 STK[5]; /* stack pointers */ int32 PSL; /* PSL */ int32 SCBB = 0; /* SCB base */ int32 PCBB = 0; /* PCB base */ int32 P0BR = 0; /* P0 mem mgt */ int32 P0LR = 0; int32 P1BR = 0; /* P1 mem mgt */ int32 P1LR = 0; int32 SBR = 0; /* S0 mem mgt */ int32 SLR = 0; int32 SISR; /* swre int req */ int32 ASTLVL; /* AST level */ int32 mapen; /* map enable */ int32 pme; /* perf mon enable */ int32 trpirq; /* trap/intr req */ int32 in_ie = 0; /* in exc, int */ int32 recq[6]; /* recovery queue */ int32 recqptr; /* recq pointer */ int32 hlt_pin = 0; /* HLT pin intr */ int32 mem_err = 0; int32 crd_err = 0; int32 p1 = 0, p2 = 0; /* fault parameters */ int32 fault_PC; /* fault PC */ int32 pcq_p = 0; /* PC queue ptr */ int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ int32 badabo = 0; int32 cpu_astop = 0; int32 mchk_va, mchk_ref; /* mem ref param */ int32 ibufl, ibufh; /* prefetch buf */ int32 ibcnt, ppc; /* prefetch ctl */ uint32 cpu_idle_ipl_mask = 0x8; /* idle if on IPL 3 */ uint32 cpu_idle_type = 1; /* default to VMS */ int32 cpu_idle_wait = 1000; /* for these cycles */ jmp_buf save_env; REG *pcq_r = NULL; /* PC queue reg ptr */ int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ InstHistory *hst = NULL; /* instruction history */ const uint32 byte_mask[33] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF }; const uint32 byte_sign[33] = { 0x00000000, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; const uint32 align[4] = { 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, 0x000000FF }; /* External and forward references */ extern int32 sim_interval; extern int32 sim_int_char; extern int32 sim_switches; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern t_bool sim_idle_enab; extern t_stat build_dib_tab (void); extern UNIT rom_unit, nvr_unit; extern int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg); extern int32 op_emul (int32 mpy, int32 mpc, int32 *rh); extern int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg); extern int32 op_bb_n (int32 *opnd, int32 acc); extern int32 op_bb_x (int32 *opnd, int32 newb, int32 acc); extern int32 op_extv (int32 *opnd, int32 vfldrp1, int32 acc); extern int32 op_ffs (uint32 fld, int32 size); extern void op_insv (int32 *opnd, int32 vfldrp1, int32 acc); extern int32 op_call (int32 *opnd, t_bool gs, int32 acc); extern int32 op_ret (int32 acc); extern int32 op_insque (int32 *opnd, int32 acc); extern int32 op_remque (int32 *opnd, int32 acc); extern int32 op_insqhi (int32 *opnd, int32 acc); extern int32 op_insqti (int32 *opnd, int32 acc); extern int32 op_remqhi (int32 *opnd, int32 acc); extern int32 op_remqti (int32 *opnd, int32 acc); extern void op_pushr (int32 *opnd, int32 acc); extern void op_popr (int32 *opnd, int32 acc); extern int32 op_movc (int32 *opnd, int32 opc, int32 acc); extern int32 op_cmpc (int32 *opnd, int32 opc, int32 acc); extern int32 op_locskp (int32 *opnd, int32 opc, int32 acc); extern int32 op_scnspn (int32 *opnd, int32 opc, int32 acc); extern int32 op_chm (int32 *opnd, int32 cc, int32 opc); extern int32 op_rei (int32 acc); extern void op_ldpctx (int32 acc); extern void op_svpctx (int32 acc); extern int32 op_probe (int32 *opnd, int32 opc); extern int32 op_mtpr (int32 *opnd); extern int32 op_mfpr (int32 *opnd); extern int32 op_movfd (int32 val); extern int32 op_movg (int32 val); extern int32 op_mnegfd (int32 val); extern int32 op_mnegg (int32 val); extern int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2); extern int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2); extern int32 op_cvtifdg (int32 val, int32 *rh, int32 opc); extern int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc); extern int32 op_cvtdf (int32 *opnd); extern int32 op_cvtgf (int32 *opnd); extern int32 op_cvtfg (int32 *opnd, int32 *rh); extern int32 op_cvtgh (int32 *opnd, int32 *hflt); extern int32 op_addf (int32 *opnd, t_bool sub); extern int32 op_addd (int32 *opnd, int32 *rh, t_bool sub); extern int32 op_addg (int32 *opnd, int32 *rh, t_bool sub); extern int32 op_mulf (int32 *opnd); extern int32 op_muld (int32 *opnd, int32 *rh); extern int32 op_mulg (int32 *opnd, int32 *rh); extern int32 op_divf (int32 *opnd); extern int32 op_divd (int32 *opnd, int32 *rh); extern int32 op_divg (int32 *opnd, int32 *rh); extern int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg); extern int32 op_emodd (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); extern int32 op_emodg (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); extern void op_polyf (int32 *opnd, int32 acc); extern void op_polyd (int32 *opnd, int32 acc); extern void op_polyg (int32 *opnd, int32 acc); extern int32 op_cmode (int32 cc); extern int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc); extern int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va); extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); extern int32 Test (uint32 va, int32 acc, int32 *status); extern int32 BadCmPSL (int32 newpsl); extern int32 eval_int (void); extern int32 get_vector (int32 lvl); extern void set_map_reg (void); extern void rom_wr_B (int32 pa, int32 val); extern int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta); extern const uint16 drom[NUM_INST][MAX_SPEC + 1]; extern t_stat cpu_boot (int32 unitno, DEVICE *dptr); extern int32 con_halt (int32 code, int32 cc); t_stat cpu_reset (DEVICE *dptr); t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); int32 cpu_get_vsw (int32 sw); SIM_INLINE int32 get_istr (int32 lnt, int32 acc); int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc); t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line); int32 cpu_psl_ipl_idle (int32 newpsl); t_stat cpu_idle_svc (UNIT *uptr); /* 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 (&cpu_idle_svc, UNIT_FIX|UNIT_BINK, INITMEMSIZE) }; REG cpu_reg[] = { { HRDATA (PC, R[nPC], 32) }, { HRDATA (R0, R[0], 32) }, { HRDATA (R1, R[1], 32) }, { HRDATA (R2, R[2], 32) }, { HRDATA (R3, R[3], 32) }, { HRDATA (R4, R[4], 32) }, { HRDATA (R5, R[5], 32) }, { HRDATA (R6, R[6], 32) }, { HRDATA (R7, R[7], 32) }, { HRDATA (R8, R[8], 32) }, { HRDATA (R9, R[9], 32) }, { HRDATA (R10, R[10], 32) }, { HRDATA (R11, R[11], 32) }, { HRDATA (R12, R[12], 32) }, { HRDATA (R13, R[13], 32) }, { HRDATA (R14, R[14], 32) }, { HRDATA (AP, R[nAP], 32) }, { HRDATA (FP, R[nFP], 32) }, { HRDATA (SP, R[nSP], 32) }, { HRDATA (PSL, PSL, 32) }, { HRDATA (CC, PSL, 4) }, { HRDATA (KSP, KSP, 32) }, { HRDATA (ESP, ESP, 32) }, { HRDATA (SSP, SSP, 32) }, { HRDATA (USP, USP, 32) }, { HRDATA (IS, IS, 32) }, { HRDATA (SCBB, SCBB, 32) }, { HRDATA (PCBB, PCBB, 32) }, { HRDATA (P0BR, P0BR, 32) }, { HRDATA (P0LR, P0LR, 22) }, { HRDATA (P1BR, P1BR, 32) }, { HRDATA (P1LR, P1LR, 22) }, { HRDATA (SBR, SBR, 32) }, { HRDATA (SLR, SLR, 22) }, { HRDATA (SISR, SISR, 16) }, { HRDATA (ASTLVL, ASTLVL, 4) }, { FLDATA (MAPEN, mapen, 0) }, { FLDATA (PME, pme, 0) }, { HRDATA (TRPIRQ, trpirq, 8) }, { FLDATA (CRDERR, crd_err, 0) }, { FLDATA (MEMERR, mem_err, 0) }, { FLDATA (HLTPIN, hlt_pin, 0) }, { HRDATA (IDLE_IPL, cpu_idle_ipl_mask, 16), REG_HIDDEN }, { DRDATA (IDLE_TYPE, cpu_idle_type, 4), REG_HRO }, { DRDATA (IDLE_WAIT, cpu_idle_wait, 16), REG_HIDDEN }, { BRDATA (PCQ, pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC }, { HRDATA (PCQP, pcq_p, 6), REG_HRO }, { HRDATA (BADABO, badabo, 32), REG_HRO }, { HRDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL }, { UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, (1u << 23), NULL, "8M", &cpu_set_size }, { UNIT_MSIZE, (1u << 24), NULL, "16M", &cpu_set_size }, { UNIT_MSIZE, (1u << 25), NULL, "32M", &cpu_set_size }, { UNIT_MSIZE, (1u << 25) + (1u << 24), NULL, "48M", &cpu_set_size }, { UNIT_MSIZE, (1u << 26), NULL, "64M", &cpu_set_size }, { UNIT_MSIZE, (1u << 27), NULL, "128M", &cpu_set_size }, #if !defined (VAX_780) { UNIT_MSIZE, (1u << 28), NULL, "256M", &cpu_set_size }, { UNIT_MSIZE, (1u << 29), NULL, "512M", &cpu_set_size }, #endif { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, NULL, &cpu_show_virt }, { 0 } }; DEBTAB cpu_deb[] = { { "INTEXC", LOG_CPU_I }, { "REI", LOG_CPU_R }, { "CONTEXT", LOG_CPU_P }, { NULL, 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 16, 32, 1, 16, 8, &cpu_ex, &cpu_dep, &cpu_reset, &cpu_boot, NULL, NULL, NULL, DEV_DYNM | DEV_DEBUG, 0, cpu_deb, &cpu_set_size, NULL }; t_stat sim_instr (void) { volatile int32 opc, cc; /* used by setjmp */ int32 acc; /* set by setjmp */ int abortval; t_stat r; if ((r = build_dib_tab ()) != SCPE_OK) /* build, chk dib_tab */ return r; if ((PSL & PSL_MBZ) || /* validate PSL */ ((PSL & PSL_CM) && BadCmPSL (PSL)) || /* validate PSL */ ((PSL_GETCUR (PSL) != KERN) && /* esu => is, ipl = 0 */ (PSL & (PSL_IS|PSL_IPL))) || ((PSL & PSL_IS) && ((PSL & PSL_IPL) == 0))) /* is => ipl > 0 */ return SCPE_STOP; cc = PSL & CC_MASK; /* split PSL */ PSL = PSL & ~CC_MASK; in_ie = 0; /* not in exc */ set_map_reg (); /* set map reg */ GET_CUR; /* set access mask */ SET_IRQL; /* eval interrupts */ FLUSH_ISTR; /* clear prefetch */ abortval = setjmp (save_env); /* set abort hdlr */ if (abortval > 0) { /* sim stop? */ PSL = PSL | cc; /* put PSL together */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return abortval; /* return to SCP */ } else if (abortval < 0) { /* mm or rsrv or int */ int32 i, delta; if ((PSL & PSL_FPD) == 0) { /* FPD? no recovery */ for (i = 0; i < recqptr; i++) { /* unwind inst */ int32 rrn, rlnt; rrn = RQ_GETRN (recq[i]); /* recover reg # */ rlnt = DR_LNT (RQ_GETLNT (recq[i])); /* recovery lnt */ if (recq[i] & RQ_DIR) R[rrn] = R[rrn] - rlnt; else R[rrn] = R[rrn] + rlnt; } } PSL = PSL & ~PSL_TP; /* clear */ recqptr = 0; /* clear queue */ delta = PC - fault_PC; /* save delta PC */ SETPC (fault_PC); /* restore PC */ switch (-abortval) { /* case on abort code */ case SCB_RESIN: /* rsrv inst fault */ case SCB_RESAD: /* rsrv addr fault */ case SCB_RESOP: /* rsrv opnd fault */ if (in_ie) /* in exc? panic */ ABORT (STOP_INIE); cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ GET_CUR; /* PSL changed */ break; case SCB_CMODE: /* comp mode fault */ case SCB_ARITH: /* arithmetic fault */ if (in_ie) /* in exc? panic */ ABORT (STOP_INIE); cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ GET_CUR; in_ie = 1; Write (SP - 4, p1, L_LONG, WA); /* write arith param */ SP = SP - 4; in_ie = 0; break; case SCB_ACV: /* ACV fault */ case SCB_TNV: /* TNV fault */ if (in_ie) { /* in exception? */ if (PSL & PSL_IS) /* on is? panic */ ABORT (STOP_INIE); cc = intexc (SCB_KSNV, cc, 0, IE_SVE); /* ksnv */ GET_CUR; } else { cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ GET_CUR; in_ie = 1; Write (SP - 8, p1, L_LONG, WA); /* write mm params */ Write (SP - 4, p2, L_LONG, WA); SP = SP - 8; in_ie = 0; } break; case SCB_MCHK: /* machine check */ if (in_ie) /* in exc? panic */ ABORT (STOP_INIE); cc = machine_check (p1, opc, cc, delta); /* system specific */ in_ie = 0; GET_CUR; /* PSL changed */ break; case 1: /* interrupt */ break; /* just proceed */ default: /* other */ badabo = abortval; /* save code */ ABORT (STOP_UNKABO); /* panic */ } /* end case */ } /* end else */ /* Main instruction loop */ for ( ;; ) { int32 spec, disp, rn, index, numspec; int32 vfldrp1, brdisp, flg, mstat; int32 i, j, r, rh, temp; uint32 va, iad; int32 opnd[OPND_SIZE]; /* operand queue */ if (cpu_astop) { cpu_astop = 0; ABORT (SCPE_STOP); } fault_PC = PC; recqptr = 0; /* clr recovery q */ if (sim_interval <= 0) { /* chk clock queue */ temp = sim_process_event (); if (temp) ABORT (temp); SET_IRQL; /* update interrupts */ } /* Test for non-instruction dispatches, in SRM order - trap or interrupt (trpirq != 0) - PSL set If any of these conditions are met, re-dispatch; otherwise, set PSL from PSL. */ if (trpirq) { /* trap or interrupt? */ if (temp = GET_TRAP (trpirq)) { /* trap? */ cc = intexc (SCB_ARITH, cc, 0, IE_EXC); /* take, clear trap */ GET_CUR; /* set cur mode */ in_ie = 1; Write (SP - 4, temp, L_LONG, WA); /* write parameter */ SP = SP - 4; in_ie = 0; } else if (temp = GET_IRQL (trpirq)) { /* interrupt? */ int32 vec; if (temp == IPL_HLTPIN) { /* console halt? */ hlt_pin = 0; /* clear intr */ trpirq = 0; /* clear everything */ cc = con_halt (CON_HLTPIN, cc); /* invoke firmware */ continue; /* continue */ } else if (temp >= IPL_HMIN) /* hardware req? */ vec = get_vector (temp); /* get vector */ else if (temp > IPL_SMAX) ABORT (STOP_UIPL); else { vec = SCB_IPLSOFT + (temp << 2); SISR = SISR & ~(1u << temp); } if (vec) /* take intr */ cc = intexc (vec, cc, temp, IE_INT); GET_CUR; /* set cur mode */ } else trpirq = 0; /* clear everything */ SET_IRQL; /* eval interrupts */ continue; } if (PSL & (PSL_CM|PSL_TP|PSW_T)) { /* PSL event? */ if (PSL & PSL_TP) { /* trace trap? */ PSL = PSL & ~PSL_TP; /* clear */ cc = intexc (SCB_TP, cc, 0, IE_EXC); /* take trap */ GET_CUR; /* set cur mode */ continue; } if (PSL & PSW_T) /* if T, set TP */ PSL = PSL | PSL_TP; if (PSL & PSL_CM) { /* compat mode? */ cc = op_cmode (cc); /* exec instr */ continue; /* skip fetch */ } } /* end PSL event */ if (sim_brk_summ && sim_brk_test ((uint32) PC, SWMASK ('E'))) { /* breakpoint? */ ABORT (STOP_IBKPT); /* stop simulation */ } sim_interval = sim_interval - 1; /* count instr */ GET_ISTR (opc, L_BYTE); /* get opcode */ if (opc == 0xFD) { /* 2 byte op? */ GET_ISTR (opc, L_BYTE); /* get second byte */ opc = opc | 0x100; /* flag */ } numspec = drom[opc][0]; /* get # specs */ if (PSL & PSL_FPD) { if ((numspec & DR_F) == 0) RSVD_INST_FAULT; } else { numspec = numspec & DR_NSPMASK; /* get # specifiers */ /* Specifier flows. Operands are parsed and placed into queue opnd. r.bwl opnd[j] = value of operand r.q opnd[j:j+1] = value of operand r.o opnd[j:j+3] = value of operand a.bwlqo opnd[j] = address of operand m.bwl opnd[j] = value of operand m.q opnd[j:j+1] = value of operand m.o opnd[j:j+3] = value of operand w.bwlqo opnd[j] = register/memory flag opnd[j+1] = memory address For the last memory specifier, the specifier is in spec, the register number is in rn, and the effective address is in va. Modify specifiers (always last) can test spec > reg+PC, as short literal are illegal for modifiers specifiers, and final index specifiers are always illegal. */ for (i = 1, j = 0; i <= numspec; i++) { /* loop thru specs */ disp = drom[opc][i]; /* get dispatch */ if (disp >= BB) { GET_ISTR (brdisp, DR_LNT (disp & 1)); break; } GET_ISTR (spec, L_BYTE); /* get spec byte */ rn = spec & RGMASK; /* get reg # */ disp = (spec & ~RGMASK) | disp; /* merge w dispatch */ switch (disp) { /* dispatch spec */ /* Short literal - only read access permitted */ case SH0|RB: case SH0|RW: case SH0|RL: case SH1|RB: case SH1|RW: case SH1|RL: case SH2|RB: case SH2|RW: case SH2|RL: case SH3|RB: case SH3|RW: case SH3|RL: opnd[j++] = spec; break; case SH0|RQ: case SH1|RQ: case SH2|RQ: case SH3|RQ: opnd[j++] = spec; opnd[j++] = 0; break; case SH0|RO: case SH1|RO: case SH2|RO: case SH3|RO: opnd[j++] = spec; opnd[j++] = 0; opnd[j++] = 0; opnd[j++] = 0; break; case SH0|RF: case SH1|RF: case SH2|RF: case SH3|RF: opnd[j++] = (spec << 4) | 0x4000; break; case SH0|RD: case SH1|RD: case SH2|RD: case SH3|RD: opnd[j++] = (spec << 4) | 0x4000; opnd[j++] = 0; break; case SH0|RG: case SH1|RG: case SH2|RG: case SH3|RG: opnd[j++] = (spec << 1) | 0x4000; opnd[j++] = 0; break; case SH0|RH: case SH1|RH: case SH2|RH: case SH3|RH: opnd[j++] = ((spec & 0x7) << 29) | (0x4000 | ((spec >> 3) & 0x7)); opnd[j++] = 0; opnd[j++] = 0; opnd[j++] = 0; break; /* Register */ case GRN|RB: case GRN|MB: CHECK_FOR_PC; opnd[j++] = R[rn] & BMASK; break; case GRN|RW: case GRN|MW: CHECK_FOR_PC; opnd[j++] = R[rn] & WMASK; break; case GRN|VB: vfldrp1 = R[(rn + 1) & RGMASK]; case GRN|WB: case GRN|WW: case GRN|WL: case GRN|WQ: case GRN|WO: opnd[j++] = rn; case GRN|RL: case GRN|RF: case GRN|ML: CHECK_FOR_PC; opnd[j++] = R[rn]; break; case GRN|RQ: case GRN|RD: case GRN|RG: case GRN|MQ: CHECK_FOR_SP; opnd[j++] = R[rn]; opnd[j++] = R[rn + 1]; break; case GRN|RO: case GRN|RH: case GRN|MO: CHECK_FOR_AP; opnd[j++] = R[rn]; opnd[j++] = R[rn + 1]; opnd[j++] = R[rn + 2]; opnd[j++] = R[rn + 3]; break; /* Register deferred, autodecrement */ case RGD|VB: case RGD|WB: case RGD|WW: case RGD|WL: case RGD|WQ: case RGD|WO: opnd[j++] = OP_MEM; case RGD|AB: case RGD|AW: case RGD|AL: case RGD|AQ: case RGD|AO: CHECK_FOR_PC; va = opnd[j++] = R[rn]; break; case ADC|VB: case ADC|WB: case ADC|WW: case ADC|WL: case ADC|WQ: case ADC|WO: opnd[j++] = OP_MEM; case ADC|AB: case ADC|AW: case ADC|AL: case ADC|AQ: case ADC|AO: CHECK_FOR_PC; va = opnd[j++] = R[rn] = R[rn] - DR_LNT (disp); recq[recqptr++] = RQ_REC (disp, rn); break; case ADC|RB: case ADC|RW: case ADC|RL: case ADC|RF: R[rn] = R[rn] - (DR_LNT (disp)); recq[recqptr++] = RQ_REC (disp, rn); case RGD|RB: case RGD|RW: case RGD|RL: case RGD|RF: CHECK_FOR_PC; opnd[j++] = Read (va = R[rn], DR_LNT (disp), RA); break; case ADC|RQ: case ADC|RD: case ADC|RG: R[rn] = R[rn] - 8; recq[recqptr++] = RQ_REC (disp, rn); case RGD|RQ: case RGD|RD: case RGD|RG: CHECK_FOR_PC; opnd[j++] = Read (va = R[rn], L_LONG, RA); opnd[j++] = Read (R[rn] + 4, L_LONG, RA); break; case ADC|RO: case ADC|RH: R[rn] = R[rn] - 16; recq[recqptr++] = RQ_REC (disp, rn); case RGD|RO: case RGD|RH: CHECK_FOR_PC; j = ReadOcta (va = R[rn], opnd, j, RA); break; case ADC|MB: case ADC|MW: case ADC|ML: R[rn] = R[rn] - (DR_LNT (disp)); recq[recqptr++] = RQ_REC (disp, rn); case RGD|MB: case RGD|MW: case RGD|ML: CHECK_FOR_PC; opnd[j++] = Read (va = R[rn], DR_LNT (disp), WA); break; case ADC|MQ: R[rn] = R[rn] - 8; recq[recqptr++] = RQ_REC (disp, rn); case RGD|MQ: CHECK_FOR_PC; opnd[j++] = Read (va = R[rn], L_LONG, WA); opnd[j++] = Read (R[rn] + 4, L_LONG, WA); break; case ADC|MO: R[rn] = R[rn] - 16; recq[recqptr++] = RQ_REC (disp, rn); case RGD|MO: CHECK_FOR_PC; j = ReadOcta (va = R[rn], opnd, j, WA); break; /* Autoincrement */ case AIN|VB: case AIN|WB: case AIN|WW: case AIN|WL: case AIN|WQ: case AIN|WO: /* CHECK_FOR_PC; */ opnd[j++] = OP_MEM; case AIN|AB: case AIN|AW: case AIN|AL: case AIN|AQ: case AIN|AO: va = opnd[j++] = R[rn]; if (rn == nPC) { if (DR_LNT (disp) >= L_QUAD) { GET_ISTR (temp, L_LONG); GET_ISTR (temp, L_LONG); if (DR_LNT (disp) == L_OCTA) { GET_ISTR (temp, L_LONG); GET_ISTR (temp, L_LONG); } } else GET_ISTR (temp, DR_LNT (disp)); } else { R[rn] = R[rn] + DR_LNT (disp); recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|RB: case AIN|RW: case AIN|RL: case AIN|RF: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], DR_LNT (disp)); } else { opnd[j++] = Read (R[rn], DR_LNT (disp), RA); R[rn] = R[rn] + DR_LNT (disp); recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|RQ: case AIN|RD: case AIN|RG: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); } else { opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); R[rn] = R[rn] + 8; recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|RO: case AIN|RH: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); } else { j = ReadOcta (va, opnd, j, RA); R[rn] = R[rn] + 16; recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|MB: case AIN|MW: case AIN|ML: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], DR_LNT (disp)); } else { opnd[j++] = Read (R[rn], DR_LNT (disp), WA); R[rn] = R[rn] + DR_LNT (disp); recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|MQ: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); } else { opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); R[rn] = R[rn] + 8; recq[recqptr++] = RQ_REC (disp, rn); } break; case AIN|MO: va = R[rn]; if (rn == nPC) { GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); GET_ISTR (opnd[j++], L_LONG); } else { j = ReadOcta (va, opnd, j, WA); R[rn] = R[rn] + 16; recq[recqptr++] = RQ_REC (disp, rn); } break; /* Autoincrement deferred */ case AID|VB: case AID|WB: case AID|WW: case AID|WL: case AID|WQ: case AID|WO: opnd[j++] = OP_MEM; case AID|AB: case AID|AW: case AID|AL: case AID|AQ: case AID|AO: if (rn == nPC) { GET_ISTR (va = opnd[j++], L_LONG); } else { va = opnd[j++] = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } break; case AID|RB: case AID|RW: case AID|RL: case AID|RF: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } opnd[j++] = Read (va, DR_LNT (disp), RA); break; case AID|RQ: case AID|RD: case AID|RG: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case AID|RO: case AID|RH: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } j = ReadOcta (va, opnd, j, RA); break; case AID|MB: case AID|MW: case AID|ML: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } opnd[j++] = Read (va, DR_LNT (disp), WA); break; case AID|MQ: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case AID|MO: if (rn == nPC) { GET_ISTR (va, L_LONG); } else { va = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } j = ReadOcta (va, opnd, j, WA); break; /* Byte displacement */ case BDP|VB: case BDP|WB: case BDP|WW: case BDP|WL: case BDP|WQ: case BDP|WO: opnd[j++] = OP_MEM; case BDP|AB: case BDP|AW: case BDP|AL: case BDP|AQ: case BDP|AO: GET_ISTR (temp, L_BYTE); va = opnd[j++] = R[rn] + SXTB (temp); break; case BDP|RB: case BDP|RW: case BDP|RL: case BDP|RF: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); opnd[j++] = Read (va, DR_LNT (disp), RA); break; case BDP|RQ: case BDP|RD: case BDP|RG: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case BDP|RO: case BDP|RH: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); j = ReadOcta (va, opnd, j, RA); break; case BDP|MB: case BDP|MW: case BDP|ML: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); opnd[j++] = Read (va, DR_LNT (disp), WA); break; case BDP|MQ: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case BDP|MO: GET_ISTR (temp, L_BYTE); va = R[rn] + SXTB (temp); j = ReadOcta (va, opnd, j, WA); break; /* Byte displacement deferred */ case BDD|VB: case BDD|WB: case BDD|WW: case BDD|WL: case BDD|WQ: case BDD|WO: opnd[j++] = OP_MEM; case BDD|AB: case BDD|AW: case BDD|AL: case BDD|AQ: case BDD|AO: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = opnd[j++] = Read (iad, L_LONG, RA); break; case BDD|RB: case BDD|RW: case BDD|RL: case BDD|RF: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), RA); break; case BDD|RQ: case BDD|RD: case BDD|RG: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case BDD|RO: case BDD|RH: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, RA); break; case BDD|MB: case BDD|MW: case BDD|ML: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), WA); break; case BDD|MQ: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case BDD|MO: GET_ISTR (temp, L_BYTE); iad = R[rn] + SXTB (temp); va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, WA); break; /* Word displacement */ case WDP|VB: case WDP|WB: case WDP|WW: case WDP|WL: case WDP|WQ: case WDP|WO: opnd[j++] = OP_MEM; case WDP|AB: case WDP|AW: case WDP|AL: case WDP|AQ: case WDP|AO: GET_ISTR (temp, L_WORD); va = opnd[j++] = R[rn] + SXTW (temp); break; case WDP|RB: case WDP|RW: case WDP|RL: case WDP|RF: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); opnd[j++] = Read (va, DR_LNT (disp), RA); break; case WDP|RQ: case WDP|RD: case WDP|RG: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case WDP|RO: case WDP|RH: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); j = ReadOcta (va, opnd, j, RA); break; case WDP|MB: case WDP|MW: case WDP|ML: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); opnd[j++] = Read (va, DR_LNT (disp), WA); break; case WDP|MQ: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case WDP|MO: GET_ISTR (temp, L_WORD); va = R[rn] + SXTW (temp); j = ReadOcta (va, opnd, j, WA); break; /* Word displacement deferred */ case WDD|VB: case WDD|WB: case WDD|WW: case WDD|WL: case WDD|WQ: case WDD|WO: opnd[j++] = OP_MEM; case WDD|AB: case WDD|AW: case WDD|AL: case WDD|AQ: case WDD|AO: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = opnd[j++] = Read (iad, L_LONG, RA); break; case WDD|RB: case WDD|RW: case WDD|RL: case WDD|RF: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), RA); break; case WDD|RQ: case WDD|RD: case WDD|RG: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case WDD|RO: case WDD|RH: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, RA); break; case WDD|MB: case WDD|MW: case WDD|ML: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), WA); break; case WDD|MQ: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case WDD|MO: GET_ISTR (temp, L_WORD); iad = R[rn] + SXTW (temp); va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, WA); break; /* Longword displacement */ case LDP|VB: case LDP|WB: case LDP|WW: case LDP|WL: case LDP|WQ: case LDP|WO: opnd[j++] = OP_MEM; case LDP|AB: case LDP|AW: case LDP|AL: case LDP|AQ: case LDP|AO: GET_ISTR (temp, L_LONG); va = opnd[j++] = R[rn] + temp; break; case LDP|RB: case LDP|RW: case LDP|RL: case LDP|RF: GET_ISTR (temp, L_LONG); va = R[rn] + temp; opnd[j++] = Read (va, DR_LNT (disp), RA); break; case LDP|RQ: case LDP|RD: case LDP|RG: GET_ISTR (temp, L_LONG); va = R[rn] + temp; opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case LDP|RO: case LDP|RH: GET_ISTR (temp, L_LONG); va = R[rn] + temp; j = ReadOcta (va, opnd, j, RA); break; case LDP|MB: case LDP|MW: case LDP|ML: GET_ISTR (temp, L_LONG); va = R[rn] + temp; opnd[j++] = Read (va, DR_LNT (disp), WA); break; case LDP|MQ: GET_ISTR (temp, L_LONG); va = R[rn] + temp; opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case LDP|MO: GET_ISTR (temp, L_LONG); va = R[rn] + temp; j = ReadOcta (va, opnd, j, WA); break; /* Longword displacement deferred */ case LDD|VB: case LDD|WB: case LDD|WW: case LDD|WL: case LDD|WQ: case LDD|WO: opnd[j++] = OP_MEM; case LDD|AB: case LDD|AW: case LDD|AL: case LDD|AQ: case LDD|AO: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = opnd[j++] = Read (iad, L_LONG, RA); break; case LDD|RB: case LDD|RW: case LDD|RL: case LDD|RF: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), RA); break; case LDD|RQ: case LDD|RD: case LDD|RG: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, RA); opnd[j++] = Read (va + 4, L_LONG, RA); break; case LDD|RO: case LDD|RH: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, RA); break; case LDD|MB: case LDD|MW: case LDD|ML: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, DR_LNT (disp), WA); break; case LDD|MQ: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); opnd[j++] = Read (va, L_LONG, WA); opnd[j++] = Read (va + 4, L_LONG, WA); break; case LDD|MO: GET_ISTR (temp, L_LONG); iad = R[rn] + temp; va = Read (iad, L_LONG, RA); j = ReadOcta (va, opnd, j, WA); break; /* Index */ case IDX|VB: case IDX|WB: case IDX|WW: case IDX|WL: case IDX|WQ: case IDX|WO: case IDX|AB: case IDX|AW: case IDX|AL: case IDX|AQ: case IDX|AO: case IDX|MB: case IDX|MW: case IDX|ML: case IDX|MQ: case IDX|MO: case IDX|RB: case IDX|RW: case IDX|RL: case IDX|RQ: case IDX|RO: case IDX|RF: case IDX|RD: case IDX|RG: case IDX|RH: CHECK_FOR_PC; index = R[rn] << (disp & DR_LNMASK); GET_ISTR (spec, L_BYTE); rn = spec & RGMASK; switch (spec & ~RGMASK) { case ADC: R[rn] = R[rn] - DR_LNT (disp); recq[recqptr++] = RQ_REC (ADC | (disp & DR_LNMASK), rn); case RGD: CHECK_FOR_PC; index = index + R[rn]; break; case AIN: CHECK_FOR_PC; index = index + R[rn]; R[rn] = R[rn] + DR_LNT (disp); recq[recqptr++] = RQ_REC (AIN | (disp & DR_LNMASK), rn); break; case AID: if (rn == nPC) { GET_ISTR (temp, L_LONG); } else { temp = Read (R[rn], L_LONG, RA); R[rn] = R[rn] + 4; recq[recqptr++] = RQ_REC (AID|RL, rn); } index = temp + index; break; case BDP: GET_ISTR (temp, L_BYTE); index = index + R[rn] + SXTB (temp); break; case BDD: GET_ISTR (temp, L_BYTE); index = index + Read (R[rn] + SXTB (temp), L_LONG, RA); break; case WDP: GET_ISTR (temp, L_WORD); index = index + R[rn] + SXTW (temp); break; case WDD: GET_ISTR (temp, L_WORD); index = index + Read (R[rn] + SXTW (temp), L_LONG, RA); break; case LDP: GET_ISTR (temp, L_LONG); index = index + R[rn] + temp; break; case LDD: GET_ISTR (temp, L_LONG); index = index + Read (R[rn] + temp, L_LONG, RA); break; default: RSVD_ADDR_FAULT; /* end case idxspec */ } switch (disp & (DR_ACMASK|DR_SPFLAG|DR_LNMASK)) { /* case acc+lnt */ case VB: case WB: case WW: case WL: case WQ: case WO: opnd[j++] = OP_MEM; case AB: case AW: case AL: case AQ: case AO: va = opnd[j++] = index; break; case RB: case RW: case RL: case RF: opnd[j++] = Read (va = index, DR_LNT (disp), RA); break; case RQ: case RD: case RG: opnd[j++] = Read (va = index, L_LONG, RA); opnd[j++] = Read (index + 4, L_LONG, RA); break; case RO: case RH: j = ReadOcta (va = index, opnd, j, RA); break; case MB: case MW: case ML: opnd[j++] = Read (va = index, DR_LNT (disp), WA); break; case MQ: opnd[j++] = Read (va = index, L_LONG, WA); opnd[j++] = Read (index + 4, L_LONG, WA); break; case MO: j = ReadOcta (va = index, opnd, j, WA); break; default: /* all others */ RSVD_ADDR_FAULT; /* fault */ break; } /* end case access/lnt */ break; /* end index */ default: /* all others */ RSVD_ADDR_FAULT; /* fault */ break; } /* end case spec */ } /* end for */ } /* end if not FPD */ /* Optionally record instruction history */ if (hst_lnt) { int32 lim; t_value wd; hst[hst_p].iPC = fault_PC; hst[hst_p].PSL = PSL | cc; hst[hst_p].opc = opc; for (i = 0; i < j; i++) hst[hst_p].opnd[i] = opnd[i]; lim = PC - fault_PC; if ((uint32) lim > INST_SIZE) lim = INST_SIZE; for (i = 0; i < lim; i++) { if ((cpu_ex (&wd, fault_PC + i, &cpu_unit, SWMASK ('V'))) == SCPE_OK) hst[hst_p].inst[i] = (uint8) wd; else { hst[hst_p].inst[0] = hst[hst_p].inst[1] = 0xFF; break; } } hst_p = hst_p + 1; if (hst_p >= hst_lnt) hst_p = 0; } /* Dispatch to instructions */ switch (opc) { /* Single operand instructions with dest, write only - CLRx dst.wx spec = reg/memory flag rn = register number va = virtual address */ case CLRB: WRITE_B (0); /* store result */ CC_ZZ1P; /* set cc's */ break; case CLRW: WRITE_W (0); /* store result */ CC_ZZ1P; /* set cc's */ break; case CLRL: WRITE_L (0); /* store result */ CC_ZZ1P; /* set cc's */ break; case CLRQ: WRITE_Q (0, 0); /* store result */ CC_ZZ1P; /* set cc's */ break; /* Single operand instructions with source, read only - TSTx src.rx opnd[0] = source */ case TSTB: CC_IIZZ_B (op0); /* set cc's */ break; case TSTW: CC_IIZZ_W (op0); /* set cc's */ break; case TSTL: CC_IIZZ_L (op0); /* set cc's */ break; /* Single operand instructions with source, read/write - op src.mx opnd[0] = operand spec = reg/mem flag rn = register number va = operand address */ case INCB: r = (op0 + 1) & BMASK; /* calc result */ WRITE_B (r); /* store result */ CC_ADD_B (r, 1, op0); /* set cc's */ break; case INCW: r = (op0 + 1) & WMASK; /* calc result */ WRITE_W (r); /* store result */ CC_ADD_W (r, 1, op0); /* set cc's */ break; case INCL: r = (op0 + 1) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_ADD_L (r, 1, op0); /* set cc's */ break; case DECB: r = (op0 - 1) & BMASK; /* calc result */ WRITE_B (r); /* store result */ CC_SUB_B (r, 1, op0); /* set cc's */ break; case DECW: r = (op0 - 1) & WMASK; /* calc result */ WRITE_W (r); /* store result */ CC_SUB_W (r, 1, op0); /* set cc's */ break; case DECL: r = (op0 - 1) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_SUB_L (r, 1, op0); /* set cc's */ break; /* Push instructions - PUSHL src.rl or PUSHAx src.ax opnd[0] = source */ case PUSHL: case PUSHAB: case PUSHAW: case PUSHAL: case PUSHAQ: Write (SP - 4, op0, L_LONG, WA); /* push operand */ SP = SP - 4; /* decr stack ptr */ CC_IIZP_L (op0); /* set cc's */ break; /* Moves, converts, and ADAWI - op src.rx, dst.wx opnd[0] = source spec = reg/mem flag rn = register number va = operand address */ case MOVB: WRITE_B (op0); /* result */ CC_IIZP_B (op0); /* set cc's */ break; case MOVW: case MOVZBW: WRITE_W (op0); /* result */ CC_IIZP_W (op0); /* set cc's */ break; case MOVL: case MOVZBL: case MOVZWL: case MOVAB: case MOVAW: case MOVAL: case MOVAQ: WRITE_L (op0); /* result */ CC_IIZP_L (op0); /* set cc's */ break; case MCOMB: r = op0 ^ BMASK; /* compl opnd */ WRITE_B (r); /* store result */ CC_IIZP_B (r); /* set cc's */ break; case MCOMW: r = op0 ^ WMASK; /* compl opnd */ WRITE_W (r); /* store result */ CC_IIZP_W (r); /* set cc's */ break; case MCOML: r = op0 ^ LMASK; /* compl opnd */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ break; case MNEGB: r = (-op0) & BMASK; /* negate opnd */ WRITE_B (r); /* store result */ CC_SUB_B (r, op0, 0); /* set cc's */ break; case MNEGW: r = (-op0) & WMASK; /* negate opnd */ WRITE_W (r); /* store result */ CC_SUB_W (r, op0, 0); /* set cc's */ break; case MNEGL: r = (-op0) & LMASK; /* negate opnd */ WRITE_L (r); /* store result */ CC_SUB_L (r, op0, 0); /* set cc's */ break; case CVTBW: r = SXTBW (op0); /* ext sign */ WRITE_W (r); /* store result */ CC_IIZZ_W (r); /* set cc's */ break; case CVTBL: r = SXTB (op0); /* ext sign */ WRITE_L (r); /* store result */ CC_IIZZ_L (r); /* set cc's */ break; case CVTWL: r = SXTW (op0); /* ext sign */ WRITE_L (r); /* store result */ CC_IIZZ_L (r); /* set cc's */ break; case CVTLB: r = op0 & BMASK; /* set result */ WRITE_B (r); /* store result */ CC_IIZZ_B (r); /* initial cc's */ if ((op0 > 127) || (op0 < -128)) { V_INTOV; } break; case CVTLW: r = op0 & WMASK; /* set result */ WRITE_W (r); /* store result */ CC_IIZZ_W (r); /* initial cc's */ if ((op0 > 32767) || (op0 < -32768)) { V_INTOV; } break; case CVTWB: r = op0 & BMASK; /* set result */ WRITE_B (r); /* store result */ CC_IIZZ_B (r); /* initial cc's */ temp = SXTW (op0); /* cvt op to long */ if ((temp > 127) || (temp < -128)) { V_INTOV; } break; case ADAWI: if (op1 >= 0) temp = R[op1] & WMASK; /* reg? ADDW2 */ else { if (op2 & 1) /* mem? chk align */ RSVD_OPND_FAULT; temp = Read (op2, L_WORD, WA); /* ok, ADDW2 */ } r = (op0 + temp) & WMASK; WRITE_W (r); CC_ADD_W (r, op0, temp); /* set cc's */ break; /* Integer operates, 2 operand, read only - op src1.rx, src2.rx opnd[0] = source1 opnd[1] = source2 */ case CMPB: CC_CMP_B (op0, op1); /* set cc's */ break; case CMPW: CC_CMP_W (op0, op1); /* set cc's */ break; case CMPL: CC_CMP_L (op0, op1); /* set cc's */ break; case BITB: r = op1 & op0; /* calc result */ CC_IIZP_B (r); /* set cc's */ break; case BITW: r = op1 & op0; /* calc result */ CC_IIZP_W (r); /* set cc's */ break; case BITL: r = op1 & op0; /* calc result */ CC_IIZP_L (r); /* set cc's */ break; /* Integer operates, 2 operand read/write, and 3 operand, also MOVQ op2 src.rx, dst.mx op3 src.rx, src.rx, dst.wx opnd[0] = source1 opnd[1] = source2 spec = register/memory flag rn = register number va = memory address */ case ADDB2: case ADDB3: r = (op1 + op0) & BMASK; /* calc result */ WRITE_B (r); /* store result */ CC_ADD_B (r, op0, op1); /* set cc's */ break; case ADDW2: case ADDW3: r = (op1 + op0) & WMASK; /* calc result */ WRITE_W (r); /* store result */ CC_ADD_W (r, op0, op1); /* set cc's */ break; case ADWC: r = (op1 + op0 + (cc & CC_C)) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_ADD_L (r, op0, op1); /* set cc's */ if ((r == op1) && op0) /* special case */ cc = cc | CC_C; break; case ADDL2: case ADDL3: r = (op1 + op0) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_ADD_L (r, op0, op1); /* set cc's */ break; case SUBB2: case SUBB3: r = (op1 - op0) & BMASK; /* calc result */ WRITE_B (r); /* store result */ CC_SUB_B (r, op0, op1); /* set cc's */ break; case SUBW2: case SUBW3: r = (op1 - op0) & WMASK; /* calc result */ WRITE_W (r); /* store result */ CC_SUB_W (r, op0, op1); /* set cc's */ break; case SBWC: r = (op1 - op0 - (cc & CC_C)) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_SUB_L (r, op0, op1); /* set cc's */ if ((op0 == op1) && r) /* special case */ cc = cc | CC_C; break; case SUBL2: case SUBL3: r = (op1 - op0) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_SUB_L (r, op0, op1); /* set cc's */ break; case MULB2: case MULB3: temp = SXTB (op0) * SXTB (op1); /* multiply */ r = temp & BMASK; /* mask to result */ WRITE_B (r); /* store result */ CC_IIZZ_B (r); /* set cc's */ if ((temp > 127) || (temp < -128)) { V_INTOV; } break; case MULW2: case MULW3: temp = SXTW (op0) * SXTW (op1); /* multiply */ r = temp & WMASK; /* mask to result */ WRITE_W (r); /* store result */ CC_IIZZ_W (r); /* set cc's */ if ((temp > 32767) || (temp < -32768)) { V_INTOV; } break; case MULL2: case MULL3: r = op_emul (op0, op1, &rh); /* get 64b result */ WRITE_L (r); /* store result */ CC_IIZZ_L (r); /* set cc's */ if (rh != ((r & LSIGN)? -1: 0)) { /* chk overflow */ V_INTOV; } break; case DIVB2: case DIVB3: if (op0 == 0) { /* div by zero? */ r = op1; temp = CC_V; SET_TRAP (TRAP_DIVZRO); } else if ((op0 == BMASK) && (op1 == BSIGN)) { /* overflow? */ r = op1; temp = CC_V; INTOV; } else { r = SXTB (op1) / SXTB (op0); /* ok, divide */ temp = 0; } r = r & BMASK; /* mask to result */ WRITE_B (r); /* write result */ CC_IIZZ_B (r); /* set cc's */ cc = cc | temp; /* error? set V */ break; case DIVW2: case DIVW3: if (op0 == 0) { /* div by zero? */ r = op1; temp = CC_V; SET_TRAP (TRAP_DIVZRO); } else if ((op0 == WMASK) && (op1 == WSIGN)) { /* overflow? */ r = op1; temp = CC_V; INTOV; } else { r = SXTW (op1) / SXTW (op0); /* ok, divide */ temp = 0; } r = r & WMASK; /* mask to result */ WRITE_W (r); /* write result */ CC_IIZZ_W (r); /* set cc's */ cc = cc | temp; /* error? set V */ break; case DIVL2: case DIVL3: if (op0 == 0) { /* div by zero? */ r = op1; temp = CC_V; SET_TRAP (TRAP_DIVZRO); } else if ((op0 == LMASK) && (op1 == LSIGN)) { /* overflow? */ r = op1; temp = CC_V; INTOV; } else { r = op1 / op0; /* ok, divide */ temp = 0; } r = r & LMASK; /* mask to result */ WRITE_L (r); /* write result */ CC_IIZZ_L (r); /* set cc's */ cc = cc | temp; /* error? set V */ break; case BISB2: case BISB3: r = op1 | op0; /* calc result */ WRITE_B (r); /* store result */ CC_IIZP_B (r); /* set cc's */ break; case BISW2: case BISW3: r = op1 | op0; /* calc result */ WRITE_W (r); /* store result */ CC_IIZP_W (r); /* set cc's */ break; case BISL2: case BISL3: r = op1 | op0; /* calc result */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ break; case BICB2: case BICB3: r = op1 & ~op0; /* calc result */ WRITE_B (r); /* store result */ CC_IIZP_B (r); /* set cc's */ break; case BICW2: case BICW3: r = op1 & ~op0; /* calc result */ WRITE_W (r); /* store result */ CC_IIZP_W (r); /* set cc's */ break; case BICL2: case BICL3: r = op1 & ~op0; /* calc result */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ break; case XORB2: case XORB3: r = op1 ^ op0; /* calc result */ WRITE_B (r); /* store result */ CC_IIZP_B (r); /* set cc's */ break; case XORW2: case XORW3: r = op1 ^ op0; /* calc result */ WRITE_W (r); /* store result */ CC_IIZP_W (r); /* set cc's */ break; case XORL2: case XORL3: r = op1 ^ op0; /* calc result */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ break; /* MOVQ - movq src.rq, dst.wq opnd[0:1] = source spec = register/memory flag rn = register number va = memory address */ case MOVQ: WRITE_Q (op0, op1); /* store result */ CC_IIZP_Q (op0, op1); break; /* Shifts - op shf.rb,src.rl,dst.wl opnd[0] = shift count opnd[1] = source spec = register/memory flag rn = register number va = memory address */ case ROTL: j = op0 % 32; /* reduce sc, mod 32 */ if (j) r = ((((uint32) op1) << j) | (((uint32) op1) >> (32 - j))) & LMASK; else r = op1; WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ break; case ASHL: if (op0 & BSIGN) { /* right shift? */ temp = 0x100 - op0; /* get |shift| */ if (temp > 31) /* sc > 31? */ r = (op1 & LSIGN)? LMASK: 0; else r = op1 >> temp; /* shift */ WRITE_L (r); /* store result */ CC_IIZZ_L (r); /* set cc's */ break; } else { if (op0 > 31) /* sc > 31? */ r = temp = 0; else { r = (((uint32) op1) << op0) & LMASK; /* shift */ temp = r >> op0; /* shift back */ } WRITE_L (r); /* store result */ CC_IIZZ_L (r); /* set cc's */ if (op1 != temp) { /* bits lost? */ V_INTOV; } } break; case ASHQ: r = op_ashq (opnd, &rh, &flg); /* do qw shift */ WRITE_Q (r, rh); /* store results */ CC_IIZZ_Q (r, rh); /* set cc's */ if (flg) { /* if ovflo, set */ V_INTOV; } break; /* EMUL - emul mplr.rl,mpcn.rl,add.rl,dst.wq op0 = multiplier op1 = multiplicand op2 = adder op3:op4 = destination (.wq) */ case EMUL: r = op_emul (op0, op1, &rh); /* calc 64b result */ r = r + op2; /* add 32b value */ rh = rh + (((uint32) r) < ((uint32) op2)) - /* into 64b result */ ((op2 & LSIGN)? 1: 0); WRITE_Q (r, rh); /* write result */ CC_IIZZ_Q (r, rh); /* set cc's */ break; /* EDIV - ediv dvr.rl,dvd.rq,quo.wl,rem.wl op0 = divisor (.rl) op1:op2 = dividend (.rq) op3:op4 = quotient address (.wl) op5:op6 = remainder address (.wl) */ case EDIV: if (op5 < 0) /* wtest remainder */ Read (op6, L_LONG, WA); if (op0 == 0) { /* divide by zero? */ flg = CC_V; /* set V */ r = opnd[1]; /* quo = low divd */ rh = 0; /* rem = 0 */ SET_TRAP (TRAP_DIVZRO); /* set trap */ } else { r = op_ediv (opnd, &rh, &flg); /* extended divide */ if (flg) { /* if ovf+IV, set trap */ INTOV; } } if (op3 >= 0) /* store quotient */ R[op3] = r; else Write (op4, r, L_LONG, WA); if (op5 >= 0) /* store remainder */ R[op5] = rh; else Write (op6, rh, L_LONG, WA); CC_IIZZ_L (r); /* set cc's */ cc = cc | flg; /* set V if required */ break; /* Control instructions */ /* Simple branches and subroutine calls */ case BRB: BRANCHB (brdisp); /* branch */ if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) ABORT (STOP_LOOP); break; case BRW: BRANCHW (brdisp); /* branch */ if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) ABORT (STOP_LOOP); break; case BSBB: Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ SP = SP - 4; /* decr stk ptr */ BRANCHB (brdisp); /* branch */ break; case BSBW: Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ SP = SP - 4; /* decr stk ptr */ BRANCHW (brdisp); /* branch */ break; case BGEQ: if (!(cc & CC_N)) /* br if N = 0 */ BRANCHB (brdisp); break; case BLSS: if (cc & CC_N) /* br if N = 1 */ BRANCHB (brdisp); break; case BNEQ: if (!(cc & CC_Z)) /* br if Z = 0 */ BRANCHB (brdisp); break; case BEQL: if (cc & CC_Z) /* br if Z = 1 */ BRANCHB (brdisp); break; case BVC: if (!(cc & CC_V)) /* br if V = 0 */ BRANCHB (brdisp); break; case BVS: if (cc & CC_V) /* br if V = 1 */ BRANCHB (brdisp); break; case BGEQU: if (!(cc & CC_C)) /* br if C = 0 */ BRANCHB (brdisp); break; case BLSSU: if (cc & CC_C) /* br if C = 1 */ BRANCHB (brdisp); break; case BGTR: if (!(cc & (CC_N | CC_Z))) /* br if N | Z = 0 */ BRANCHB (brdisp); break; case BLEQ: if (cc & (CC_N | CC_Z)) /* br if N | Z = 1 */ BRANCHB (brdisp); break; case BGTRU: if (!(cc & (CC_C | CC_Z))) /* br if C | Z = 0 */ BRANCHB (brdisp); break; case BLEQU: if (cc & (CC_C | CC_Z)) /* br if C | Z = 1 */ BRANCHB (brdisp); break; /* Simple jumps and subroutine calls - op addr.ab opnd[0] = address */ case JSB: Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ SP = SP - 4; /* decr stk ptr */ case JMP: JUMP (op0); /* jump */ break; case RSB: temp = Read (SP, L_LONG, RA); /* get top of stk */ SP = SP + 4; /* incr stk ptr */ JUMP (temp); break; /* SOB instructions - op idx.ml,disp.bb opnd[0] = index spec = register/memory flag rn = register number va = memory address */ case SOBGEQ: r = op0 - 1; /* decr index */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ V_SUB_L (r, 1, op0); /* test for ovflo */ if (r >= 0) /* if >= 0, branch */ BRANCHB (brdisp); break; case SOBGTR: r = op0 - 1; /* decr index */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ V_SUB_L (r, 1, op0); /* test for ovflo */ if (r > 0) /* if >= 0, branch */ BRANCHB (brdisp); break; /* AOB instructions - op limit.rl,idx.ml,disp.bb opnd[0] = limit opnd[1] = index spec = register/memory flag rn = register number va = memory address */ case AOBLSS: r = op1 + 1; /* incr index */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ V_ADD_L (r, 1, op1); /* test for ovflo */ if (r < op0) /* if < lim, branch */ BRANCHB (brdisp); break; case AOBLEQ: r = op1 + 1; /* incr index */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ V_ADD_L (r, 1, op1); /* test for ovflo */ if (r <= op0) /* if < lim, branch */ BRANCHB (brdisp); break; /* ACB instructions - op limit.rx,add.rx,index.mx,disp.bw opnd[0] = limit opnd[1] = adder opnd[2] = index spec = register/memory flag rn = register number va = memory address */ case ACBB: r = (op2 + op1) & BMASK; /* calc result */ WRITE_B (r); /* store result */ CC_IIZP_B (r); /* set cc's */ V_ADD_B (r, op1, op2); /* test for ovflo */ if ((op1 & BSIGN)? (SXTB (r) >= SXTB (op0)): (SXTB (r) <= SXTB (op0))) BRANCHW (brdisp); break; case ACBW: r = (op2 + op1) & WMASK; /* calc result */ WRITE_W (r); /* store result */ CC_IIZP_W (r); /* set cc's */ V_ADD_W (r, op1, op2); /* test for ovflo */ if ((op1 & WSIGN)? (SXTW (r) >= SXTW (op0)): (SXTW (r) <= SXTW (op0))) BRANCHW (brdisp); break; case ACBL: r = (op2 + op1) & LMASK; /* calc result */ WRITE_L (r); /* store result */ CC_IIZP_L (r); /* set cc's */ V_ADD_L (r, op1, op2); /* test for ovflo */ if ((op1 & LSIGN)? (r >= op0): (r <= op0)) BRANCHW (brdisp); break; /* CASE instructions - casex sel.rx,base.rx,lim.rx opnd[0] = selector opnd[1] = base opnd[2] = limit */ case CASEB: r = (op0 - op1) & BMASK; /* sel - base */ CC_CMP_B (r, op2); /* r:limit, set cc's */ if (r > op2) /* r > limit (unsgnd)? */ JUMP (PC + ((op2 + 1) * 2)); else { temp = Read (PC + (r * 2), L_WORD, RA); BRANCHW (temp); } break; case CASEW: r = (op0 - op1) & WMASK; /* sel - base */ CC_CMP_W (r, op2); /* r:limit, set cc's */ if (r > op2) /* r > limit (unsgnd)? */ JUMP (PC + ((op2 + 1) * 2)); else { temp = Read (PC + (r * 2), L_WORD, RA); BRANCHW (temp); } break; case CASEL: r = (op0 - op1) & LMASK; /* sel - base */ CC_CMP_L (r, op2); /* r:limit, set cc's */ if (((uint32) r) > ((uint32) op2)) /* r > limit (unsgnd)? */ JUMP (PC + ((op2 + 1) * 2)); else { temp = Read (PC + (r * 2), L_WORD, RA); BRANCHW (temp); } break; /* Branch on bit instructions - bbxy pos.rl,op.wb,disp.bb opnd[0] = position opnd[1] = register number/memory flag opnd[2] = memory address, if memory */ case BBS: if (op_bb_n (opnd, acc)) /* br if bit set */ BRANCHB (brdisp); break; case BBC: if (!op_bb_n (opnd, acc)) /* br if bit clr */ BRANCHB (brdisp); break; case BBSS: case BBSSI: if (op_bb_x (opnd, 1, acc)) /* br if set, set */ BRANCHB (brdisp); break; case BBCC: case BBCCI: if (!op_bb_x (opnd, 0, acc)) /* br if clr, clr*/ BRANCHB (brdisp); break; case BBSC: if (op_bb_x (opnd, 0, acc)) /* br if clr, set */ BRANCHB (brdisp); break; case BBCS: if (!op_bb_x (opnd, 1, acc)) /* br if set, clr */ BRANCHB (brdisp); break; case BLBS: if (op0 & 1) /* br if bit set */ BRANCHB (brdisp); break; case BLBC: if ((op0 & 1) == 0) /* br if bit clear */ BRANCHB (brdisp); break; /* Extract field instructions - ext?v pos.rl,size.rb,base.wb,dst.wl opnd[0] = position opnd[1] = size opnd[2] = register number/memory flag opnd[3] = register content/memory address spec = register/memory flag rn = register number va = memory address */ case EXTV: r = op_extv (opnd, vfldrp1, acc); /* get field */ if (r & byte_sign[op1]) r = r | ~byte_mask[op1]; WRITE_L (r); /* store field */ CC_IIZP_L (r); /* set cc's */ break; case EXTZV: r = op_extv (opnd, vfldrp1, acc); /* get field */ WRITE_L (r); /* store field */ CC_IIZP_L (r); /* set cc's */ break; /* Compare field instructions - cmp?v pos.rl,size.rb,base.wb,src2.rl opnd[0] = position opnd[1] = size opnd[2] = register number/memory flag opnd[3] = register content/memory address opnd[4] = source2 */ case CMPV: r = op_extv (opnd, vfldrp1, acc); /* get field */ if (r & byte_sign[op1]) r = r | ~byte_mask[op1]; CC_CMP_L (r, op4); /* set cc's */ break; case CMPZV: r = op_extv (opnd, vfldrp1, acc); /* get field */ CC_CMP_L (r, op4); /* set cc's */ break; /* Find first field instructions - ff? pos.rl,size.rb,base.wb,dst.wl opnd[0] = position opnd[1] = size opnd[2] = register number/memory flag opnd[3] = register content/memory address spec = register/memory flag rn = register number va = memory address */ case FFS: r = op_extv (opnd, vfldrp1, acc); /* get field */ temp = op_ffs (r, op1); /* find first 1 */ WRITE_L (op0 + temp); /* store result */ cc = r? 0: CC_Z; /* set cc's */ break; case FFC: r = op_extv (opnd, vfldrp1, acc); /* get field */ r = r ^ byte_mask[op1]; /* invert bits */ temp = op_ffs (r, op1); /* find first 1 */ WRITE_L (op0 + temp); /* store result */ cc = r? 0: CC_Z; /* set cc's */ break; /* Insert field instruction - insv src.rl,pos.rb,size.rl,base.wb opnd[0] = source opnd[1] = position opnd[2] = size opnd[3] = register number/memory flag opnd[4] = register content/memory address */ case INSV: op_insv (opnd, vfldrp1, acc); /* insert field */ break; /* Call and return - call? arg.rx,proc.ab opnd[0] = argument opnd[1] = procedure address */ case CALLS: cc = op_call (opnd, TRUE, acc); break; case CALLG: cc = op_call (opnd, FALSE, acc); break; case RET: cc = op_ret (acc); break; /* Miscellaneous instructions */ case HALT: if (PSL & PSL_CUR) /* not kern? rsvd inst */ RSVD_INST_FAULT; else if (cpu_unit.flags & UNIT_CONH) /* halt to console? */ cc = con_halt (CON_HLTINS, cc); /* enter firmware */ else { ABORT (STOP_HALT); /* halt to simulator */ } case NOP: break; case BPT: SETPC (fault_PC); cc = intexc (SCB_BPT, cc, 0, IE_EXC); GET_CUR; break; case XFC: SETPC (fault_PC); cc = intexc (SCB_XFC, cc, 0, IE_EXC); GET_CUR; break; case BISPSW: if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT; PSL = PSL | (opnd[0] & ~CC_MASK); cc = cc | (opnd[0] & CC_MASK); break; case BICPSW: if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT; PSL = PSL & ~opnd[0]; cc = cc & ~opnd[0]; break; case MOVPSL: r = PSL | cc; WRITE_L (r); break; case PUSHR: op_pushr (opnd, acc); break; case POPR: op_popr (opnd, acc); break; case INDEX: if ((op0 < op1) || (op0 > op2)) SET_TRAP (TRAP_SUBSCR); r = (op0 + op4) * op3; WRITE_L (r); CC_IIZZ_L (r); break; /* Queue and interlocked queue */ case INSQUE: cc = op_insque (opnd, acc); break; case REMQUE: cc = op_remque (opnd, acc); break; case INSQHI: cc = op_insqhi (opnd, acc); break; case INSQTI: cc = op_insqti (opnd, acc); break; case REMQHI: cc = op_remqhi (opnd, acc); break; case REMQTI: cc = op_remqti (opnd, acc); break; /* String instructions */ case MOVC3: case MOVC5: cc = op_movc (opnd, opc & 4, acc); break; case CMPC3: case CMPC5: cc = op_cmpc (opnd, opc & 4, acc); break; case LOCC: case SKPC: cc = op_locskp (opnd, opc & 1, acc); break; case SCANC: case SPANC: cc = op_scnspn (opnd, opc & 1, acc); break; /* Floating point instructions */ case TSTF: case TSTD: r = op_movfd (op0); CC_IIZZ_FP (r); break; case TSTG: r = op_movg (op0); CC_IIZZ_FP (r); break; case MOVF: r = op_movfd (op0); WRITE_L (r); CC_IIZP_FP (r); break; case MOVD: if ((r = op_movfd (op0)) == 0) op1 = 0; WRITE_Q (r, op1); CC_IIZP_FP (r); break; case MOVG: if ((r = op_movg (op0)) == 0) op1 = 0; WRITE_Q (r, op1); CC_IIZP_FP (r); break; case MNEGF: r = op_mnegfd (op0); WRITE_L (r); CC_IIZZ_FP (r); break; case MNEGD: if ((r = op_mnegfd (op0)) == 0) op1 = 0; WRITE_Q (r, op1); CC_IIZZ_FP (r); break; case MNEGG: if ((r = op_mnegg (op0)) == 0) op1 = 0; WRITE_Q (r, op1); CC_IIZZ_FP (r); break; case CMPF: cc = op_cmpfd (op0, 0, op1, 0); break; case CMPD: cc = op_cmpfd (op0, op1, op2, op3); break; case CMPG: cc = op_cmpg (op0, op1, op2, op3); break; case CVTBF: r = op_cvtifdg (SXTB (op0), NULL, opc); WRITE_L (r); CC_IIZZ_FP (r); break; case CVTWF: r = op_cvtifdg (SXTW (op0), NULL, opc); WRITE_L (r); CC_IIZZ_FP (r); break; case CVTLF: r = op_cvtifdg (op0, NULL, opc); WRITE_L (r); CC_IIZZ_FP (r); break; case CVTBD: case CVTBG: r = op_cvtifdg (SXTB (op0), &rh, opc); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case CVTWD: case CVTWG: r = op_cvtifdg (SXTW (op0), &rh, opc); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case CVTLD: case CVTLG: r = op_cvtifdg (op0, &rh, opc); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case CVTFB: case CVTDB: case CVTGB: r = op_cvtfdgi (opnd, &flg, opc) & BMASK; WRITE_B (r); CC_IIZZ_B (r); if (flg) { V_INTOV; } break; case CVTFW: case CVTDW: case CVTGW: r = op_cvtfdgi (opnd, &flg, opc) & WMASK; WRITE_W (r); CC_IIZZ_W (r); if (flg) { V_INTOV; } break; case CVTFL: case CVTDL: case CVTGL: case CVTRFL: case CVTRDL: case CVTRGL: r = op_cvtfdgi (opnd, &flg, opc) & LMASK; WRITE_L (r); CC_IIZZ_L (r); if (flg) { V_INTOV; } break; case CVTFD: r = op_movfd (op0); WRITE_Q (r, 0); CC_IIZZ_FP (r); break; case CVTDF: r = op_cvtdf (opnd); WRITE_L (r); CC_IIZZ_FP (r); break; case CVTFG: r = op_cvtfg (opnd, &rh); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case CVTGF: r = op_cvtgf (opnd); WRITE_L (r); CC_IIZZ_FP (r); break; case ADDF2: case ADDF3: r = op_addf (opnd, FALSE); WRITE_L (r); CC_IIZZ_FP (r); break; case ADDD2: case ADDD3: r = op_addd (opnd, &rh, FALSE); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case ADDG2: case ADDG3: r = op_addg (opnd, &rh, FALSE); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case SUBF2: case SUBF3: r = op_addf (opnd, TRUE); WRITE_L (r); CC_IIZZ_FP (r); break; case SUBD2: case SUBD3: r = op_addd (opnd, &rh, TRUE); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case SUBG2: case SUBG3: r = op_addg (opnd, &rh, TRUE); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case MULF2: case MULF3: r = op_mulf (opnd); WRITE_L (r); CC_IIZZ_FP (r); break; case MULD2: case MULD3: r = op_muld (opnd, &rh); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case MULG2: case MULG3: r = op_mulg (opnd, &rh); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case DIVF2: case DIVF3: r = op_divf (opnd); WRITE_L (r); CC_IIZZ_FP (r); break; case DIVD2: case DIVD3: r = op_divd (opnd, &rh); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case DIVG2: case DIVG3: r = op_divg (opnd, &rh); WRITE_Q (r, rh); CC_IIZZ_FP (r); break; case ACBF: r = op_addf (opnd + 1, FALSE); /* add + index */ temp = op_cmpfd (r, 0, op0, 0); /* result : limit */ WRITE_L (r); /* write result */ CC_IIZP_FP (r); /* set cc's */ if ((temp & CC_Z) || ((op1 & FPSIGN)? /* test br cond */ !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); break; case ACBD: r = op_addd (opnd + 2, &rh, FALSE); temp = op_cmpfd (r, rh, op0, op1); WRITE_Q (r, rh); CC_IIZP_FP (r); if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); break; case ACBG: r = op_addg (opnd + 2, &rh, FALSE); temp = op_cmpg (r, rh, op0, op1); WRITE_Q (r, rh); CC_IIZP_FP (r); if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); break; /* EMODF op0 = multiplier op1 = extension op2 = multiplicand op3:op4 = integer destination (int.wl) op5:op6 = floating destination (flt.wl) */ case EMODF: r = op_emodf (opnd, &temp, &flg); if (op5 < 0) Read (op6, L_LONG, WA); if (op3 >= 0) R[op3] = temp; else Write (op4, temp, L_LONG, WA); WRITE_L (r); CC_IIZZ_FP (r); if (flg) { V_INTOV; } break; /* EMODD, EMODG op0:op1 = multiplier op2 = extension op3:op4 = multiplicand op5:op6 = integer destination (int.wl) op7:op8 = floating destination (flt.wq) */ case EMODD: r = op_emodd (opnd, &rh, &temp, &flg); if (op7 < 0) { Read (op8, L_BYTE, WA); Read ((op8 + 7) & LMASK, L_BYTE, WA); } if (op5 >= 0) R[op5] = temp; else Write (op6, temp, L_LONG, WA); WRITE_Q (r, rh); CC_IIZZ_FP (r); if (flg) { V_INTOV; } break; case EMODG: r = op_emodg (opnd, &rh, &temp, &flg); if (op7 < 0) { Read (op8, L_BYTE, WA); Read ((op8 + 7) & LMASK, L_BYTE, WA); } if (op5 >= 0) R[op5] = temp; else Write (op6, temp, L_LONG, WA); WRITE_Q (r, rh); CC_IIZZ_FP (r); if (flg) { V_INTOV; } break; /* POLY */ case POLYF: op_polyf (opnd, acc); CC_IIZZ_FP (R[0]); break; case POLYD: op_polyd (opnd, acc); CC_IIZZ_FP (R[0]); break; case POLYG: op_polyg (opnd, acc); CC_IIZZ_FP (R[0]); break; /* Operating system instructions */ case CHMK: case CHME: case CHMS: case CHMU: cc = op_chm (opnd, cc, opc); /* CHMx */ GET_CUR; /* update cur mode */ SET_IRQL; /* update intreq */ break; case REI: cc = op_rei (acc); /* REI */ GET_CUR; /* update cur mode */ SET_IRQL; /* update intreq */ break; case LDPCTX: op_ldpctx (acc); break; case SVPCTX: op_svpctx (acc); break; case PROBER: case PROBEW: cc = (cc & CC_C) | op_probe (opnd, opc & 1); break; case MTPR: cc = (cc & CC_C) | op_mtpr (opnd); SET_IRQL; /* update intreq */ break; case MFPR: r = op_mfpr (opnd); WRITE_L (r); CC_IIZP_L (r); break; /* CIS or emulated instructions */ case CVTPL: case MOVP: case CMPP3: case CMPP4: case CVTLP: case CVTPS: case CVTSP: case CVTTP: case CVTPT: case ADDP4: case ADDP6: case SUBP4: case SUBP6: case MULP: case DIVP: case ASHP: case CRC: case MOVTC: case MOVTUC: case MATCHC: case EDITPC: cc = op_cis (opnd, cc, opc, acc); break; /* Octaword or reserved instructions */ case PUSHAO: case MOVAO: case CLRO: case MOVO: case TSTH: case MOVH: case MNEGH: case CMPH: case CVTBH: case CVTWH: case CVTLH: case CVTHB: case CVTHW: case CVTHL: case CVTRHL: case CVTFH: case CVTDH: case CVTGH: case CVTHF: case CVTHD: case CVTHG: case ADDH2: case ADDH3: case SUBH2: case SUBH3: case MULH2: case MULH3: case DIVH2: case DIVH3: case ACBH: case POLYH: case EMODH: cc = op_octa (opnd, cc, opc, acc, spec, va); if (cc & LSIGN) { /* ACBH branch? */ BRANCHW (brdisp); cc = cc & CC_MASK; /* mask off flag */ } break; default: RSVD_INST_FAULT; break; } /* end case op */ } /* end for */ ABORT (STOP_UNKNOWN); } /* end sim_instr */ /* Prefetch buffer routine Prefetch buffer state ibufl, ibufh = the prefetch buffer ibcnt = number of bytes available (0, 4, 8) ppc = physical PC The get_istr routines fetches the indicated number of bytes from the prefetch buffer. Although it is complicated, it is faster than calling Read on every byte of the instruction stream. If the prefetch buffer has enough bytes, the required bytes are extracted from the prefetch buffer and returned. If it does not have enough bytes, enough prefetch words are fetched until there are. A longword is only prefetched if data is needed from it, so any translation errors are real. */ SIM_INLINE int32 get_istr (int32 lnt, int32 acc) { int32 bo = PC & 3; int32 sc, val, t; while ((bo + lnt) > ibcnt) { /* until enuf bytes */ if ((ppc < 0) || (VA_GETOFF (ppc) == 0)) { /* PPC inv, xpg? */ ppc = Test ((PC + ibcnt) & ~03, RD, &t); /* xlate PC */ if (ppc < 0) Read ((PC + ibcnt) & ~03, L_LONG, RA); } if (ibcnt == 0) /* fill low */ ibufl = ReadLP (ppc); else ibufh = ReadLP (ppc); /* or high */ ppc = ppc + 4; /* incr phys PC */ ibcnt = ibcnt + 4; /* incr ibuf cnt */ } PC = PC + lnt; /* incr PC */ if (lnt == L_BYTE) /* byte? */ val = (ibufl >> (bo << 3)) & BMASK; else if (lnt == L_WORD) { /* word? */ if (bo == 3) val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); else val = (ibufl >> (bo << 3)) & WMASK; } else if (bo) { /* unaligned lw? */ sc = bo << 3; val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); } else val = ibufl; /* aligned lw */ if ((bo + lnt) >= 4) { /* retire ibufl? */ ibufl = ibufh; ibcnt = ibcnt - 4; } return val; } /* Read octaword specifier */ int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc) { opnd[j++] = Read (va, L_LONG, acc); opnd[j++] = Read (va + 4, L_LONG, acc); opnd[j++] = Read (va + 8, L_LONG, acc); opnd[j++] = Read (va + 12, L_LONG, acc); return j; } /* Check new PSL IPL for idle start Checked only on exception or REI, not on MTPR #IPL, to allow for local locking within the idle loop */ int32 cpu_psl_ipl_idle (int32 newpsl) { if (((newpsl ^ PSL) & PSL_IPL) != 0) { sim_cancel (&cpu_unit); if (sim_idle_enab && ((newpsl & PSL_CUR) == 0)) { uint32 newipl = PSL_GETIPL (newpsl); if (cpu_idle_ipl_mask & (1u << newipl)) sim_activate (&cpu_unit, cpu_idle_wait); } } return newpsl; } /* Idle timer has expired with no PSL change */ t_stat cpu_idle_svc (UNIT *uptr) { if (sim_idle_enab) sim_idle (TMR_CLK, FALSE); return SCPE_OK; } /* Reset */ t_stat cpu_reset (DEVICE *dptr) { hlt_pin = 0; mem_err = 0; crd_err = 0; PSL = PSL_IS | PSL_IPL1F; SISR = 0; ASTLVL = 4; mapen = 0; if (M == NULL) M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); if (M == NULL) return SCPE_MEM; 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 build_dib_tab (); } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { int32 st; uint32 addr = (uint32) exta; if (vptr == NULL) return SCPE_ARG; if (sw & SWMASK ('V')) { int32 acc = cpu_get_vsw (sw); addr = Test (addr, acc, &st); } else addr = addr & PAMASK; if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || ADDR_IS_ROM (addr) || ADDR_IS_NVR (addr)) { *vptr = (uint32) ReadB (addr); return SCPE_OK; } return SCPE_NXM; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { int32 st; uint32 addr = (uint32) exta; if (sw & SWMASK ('V')) { int32 acc = cpu_get_vsw (sw); addr = Test (addr, acc, &st); } else addr = addr & PAMASK; if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || ADDR_IS_NVR (addr)) { WriteB (addr, (int32) val); return SCPE_OK; } if (ADDR_IS_ROM (addr)) { rom_wr_B (addr, (int32) val); return SCPE_OK; } return SCPE_NXM; } /* Memory allocation */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; uint32 i, clim; uint32 *nM = NULL; if ((val <= 0) || (val > MAXMEMSIZE_X)) return SCPE_ARG; for (i = val; i < MEMSIZE; i = i + 4) mc = mc | M[i >> 2]; if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) return SCPE_OK; nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); if (nM == NULL) return SCPE_MEM; clim = (uint32) ((((uint32) val) < MEMSIZE)? val: MEMSIZE); for (i = 0; i < clim; i = i + 4) nM[i >> 2] = M[i >> 2]; free (M); M = nM; MEMSIZE = val; return SCPE_OK; } /* Virtual address translation */ t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) { t_stat r; char *cptr = (char *) desc; uint32 va, pa; int32 st; static const char *mm_str[] = { "Access control violation", "Length violation", "Process PTE access control violation", "Process PTE length violation", "Translation not valid", "Internal error", "Process PTE translation not valid" }; if (cptr) { va = (uint32) get_uint (cptr, 16, 0xFFFFFFFF, &r); if (r == SCPE_OK) { int32 acc = cpu_get_vsw (sim_switches); pa = Test (va, acc, &st); if (st == PR_OK) fprintf (of, "Virtual %-X = physical %-X\n", va, pa); else fprintf (of, "Virtual %-X: %s\n", va, mm_str[st]); return SCPE_OK; } } fprintf (of, "Invalid argument\n"); return SCPE_OK; } /* Get access mode for examine, deposit, show virtual */ int32 cpu_get_vsw (int32 sw) { int32 md; set_map_reg (); /* update dyn reg */ if (sw & SWMASK ('K')) md = KERN; else if (sw & SWMASK ('E')) md = EXEC; else if (sw & SWMASK ('S')) md = SUPV; else if (sw & SWMASK ('U')) md = USER; else md = PSL_GETCUR (PSL); return ACC_MASK (md); } /* 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].iPC = 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 i, k, di, lnt, numspec; char *cptr = (char *) desc; t_stat r; InstHistory *h; extern const char *opcode[]; extern t_value *sim_eval; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 PSL IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(di++) % hst_lnt]; /* entry pointer */ if (h->iPC == 0) /* filled in? */ continue; fprintf(st, "%08X %08X| ", h->iPC, h->PSL); /* PC, PSL */ numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ if (opcode[h->opc] == NULL) /* undefined? */ fprintf (st, "%03X (undefined)", h->opc); else if (h->PSL & PSL_FPD) /* FPD set? */ fprintf (st, "%s FPD set", opcode[h->opc]); else { /* normal */ for (i = 0; i < INST_SIZE; i++) sim_eval[i] = h->inst[i]; if ((fprint_sym (st, h->iPC, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "%03X (undefined)", h->opc); if ((numspec > 1) || ((numspec == 1) && (drom[h->opc][1] < BB))) { if (cpu_show_opnd (st, h, 0)) { /* operands; more? */ if (cpu_show_opnd (st, h, 1)) { /* 2nd line; more? */ cpu_show_opnd (st, h, 2); /* octa, 3rd/4th */ cpu_show_opnd (st, h, 3); } } } } /* end else */ fputc ('\n', st); /* end line */ } /* end for */ return SCPE_OK; } t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line) { int32 numspec, i, j, disp; t_bool more; numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ fputs ("\n ", st); /* space */ for (i = 1, j = 0, more = FALSE; i <= numspec; i++) { /* loop thru specs */ disp = drom[h->opc][i]; /* specifier type */ if (disp == RG) /* fix specials */ disp = RQ; else if (disp >= BB) break; /* ignore branches */ else switch (disp & (DR_LNMASK|DR_ACMASK)) { case RB: case RW: case RL: /* read */ case AB: case AW: case AL: case AQ: case AO: /* address */ case MB: case MW: case ML: /* modify */ if (line == 0) fprintf (st, " %08X", h->opnd[j]); else fputs (" ", st); j = j + 1; break; case RQ: case MQ: /* read, modify quad */ if (line <= 1) fprintf (st, " %08X", h->opnd[j + line]); else fputs (" ", st); if (line == 0) more = TRUE; j = j + 2; break; case RO: case MO: /* read, modify octa */ fprintf (st, " %08X", h->opnd[j + line]); more = TRUE; j = j + 4; break; case WB: case WW: case WL: case WQ: case WO: /* write */ if (line == 0) fprintf (st, " %08X", h->opnd[j + 1]); else fputs (" ", st); j = j + 2; break; } /* end case */ } /* end for */ return more; } struct os_idle { char *name; uint32 mask; }; static struct os_idle os_tab[] = { { "VMS", 0x8 }, { "NETBSD", 0x2 }, { "ULTRIX", 0x2 }, { "OPENBSD", 0x1 }, { "32V", 0x1 }, { NULL, 0 } }; /* Set and show idle */ t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i; if (cptr != NULL) { for (i = 0; os_tab[i].name != NULL; i++) { if (strcmp (os_tab[i].name, cptr) == 0) { cpu_idle_type = i + 1; cpu_idle_ipl_mask = os_tab[i].mask; return sim_set_idle (uptr, val, cptr, desc); } } return SCPE_ARG; } return sim_set_idle (uptr, val, cptr, desc); } t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) { if (sim_idle_enab && (cpu_idle_type != 0)) fprintf (st, "idle enabled=%s", os_tab[cpu_idle_type - 1].name); else fprintf (st, "idle disabled"); return SCPE_OK; } simh-3.8.1/VAX/vax_fpa.c0000644000175000017500000015676411112313066013104 0ustar vlmvlm/* vax_fpa.c - VAX f_, d_, g_floating instructions Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 28-May-08 RMS Inlined physical memory routines 16-May-06 RMS Fixed bug in 32b floating multiply routine Fixed bug in 64b extended modulus routine 03-May-06 RMS Fixed POLYD, POLYG to clear R4, R5 Fixed POLYD, POLYG to set R3 correctly Fixed POLYD, POLYG to not exit prematurely if arg = 0 Fixed POLYD, POLYG to do full 64b multiply Fixed POLYF, POLYD, POLYG to remove truncation on add Fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b Fixed fp add routine to test for zero via fraction to support "denormal" argument from POLYF, POLYD, POLYG (all reported by Tim Stark) 27-Sep-05 RMS Fixed bug in 32b structure definitions (from Jason Stevens) 30-Sep-04 RMS Comment and formating changes based on vax_octa.c 18-Apr-04 RMS Moved format definitions to vax_defs.h 19-Jun-03 RMS Simplified add algorithm 16-May-03 RMS Fixed bug in floating to integer convert overflow Fixed multiple bugs in EMODx Integrated 32b only code 05-Jul-02 RMS Changed internal routine names for C library conflict 17-Apr-02 RMS Fixed bug in EDIV zero quotient This module contains the instruction simulators for - 64 bit arithmetic (ASHQ, EMUL, EDIV) - single precision floating point - double precision floating point, D and G format */ #include "vax_defs.h" #include extern int32 R[16]; extern int32 PSL; extern int32 p1; extern jmp_buf save_env; #if defined (USE_INT64) #define M64 0xFFFFFFFFFFFFFFFF /* 64b */ #define FD_FRACW (0xFFFF & ~(FD_EXP | FPSIGN)) #define FD_FRACL (FD_FRACW | 0xFFFF0000) /* f/d fraction */ #define G_FRACW (0xFFFF & ~(G_EXP | FPSIGN)) #define G_FRACL (G_FRACW | 0xFFFF0000) /* g fraction */ #define UNSCRAM(h,l) (((((t_uint64) (h)) << 48) & 0xFFFF000000000000) | \ ((((t_uint64) (h)) << 16) & 0x0000FFFF00000000) | \ ((((t_uint64) (l)) << 16) & 0x00000000FFFF0000) | \ ((((t_uint64) (l)) >> 16) & 0x000000000000FFFF)) #define CONCAT(h,l) ((((t_uint64) (h)) << 32) | ((uint32) (l))) typedef struct { int32 sign; int32 exp; t_uint64 frac; } UFP; #define UF_NM 0x8000000000000000 /* normalized */ #define UF_FRND 0x0000008000000000 /* F round */ #define UF_DRND 0x0000000000000080 /* D round */ #define UF_GRND 0x0000000000000400 /* G round */ #define UF_V_NM 63 #define UF_V_FDHI 40 #define UF_V_FDLO (UF_V_FDHI - 32) #define UF_V_GHI 43 #define UF_V_GLO (UF_V_GHI - 32) #define UF_GETFDHI(x) (int32) ((((x) >> (16 + UF_V_FDHI)) & FD_FRACW) | \ (((x) >> (UF_V_FDHI - 16)) & ~0xFFFF)) #define UF_GETFDLO(x) (int32) ((((x) >> (16 + UF_V_FDLO)) & 0xFFFF) | \ (((x) << (16 - UF_V_FDLO)) & ~0xFFFF)) #define UF_GETGHI(x) (int32) ((((x) >> (16 + UF_V_GHI)) & G_FRACW) | \ (((x) >> (UF_V_GHI - 16)) & ~0xFFFF)) #define UF_GETGLO(x) (int32) ((((x) >> (16 + UF_V_GLO)) & 0xFFFF) | \ (((x) << (16 - UF_V_GLO)) & ~0xFFFF)) void unpackf (int32 hi, UFP *a); void unpackd (int32 hi, int32 lo, UFP *a); void unpackg (int32 hi, int32 lo, UFP *a); void norm (UFP *a); int32 rpackfd (UFP *a, int32 *rh); int32 rpackg (UFP *a, int32 *rh); void vax_fadd (UFP *a, UFP *b); void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); void vax_fdiv (UFP *b, UFP *a, int32 prec, int32 bias); void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); /* Quadword arithmetic shift opnd[0] = shift count (cnt.rb) opnd[1:2] = source (src.rq) opnd[3:4] = destination (dst.wq) */ int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg) { t_int64 src, r; int32 sc = opnd[0]; src = CONCAT (opnd[2], opnd[1]); /* build src */ if (sc & BSIGN) { /* right shift? */ *flg = 0; /* no ovflo */ sc = 0x100 - sc; /* |shift| */ if (sc > 63) /* sc > 63? */ r = (opnd[2] & LSIGN)? -1: 0; else r = src >> sc; } else { if (sc > 63) { /* left shift */ r = 0; /* sc > 63? */ *flg = (src != 0); /* ovflo test */ } else { r = src << sc; /* do shift */ *flg = (src != (r >> sc)); /* ovflo test */ } } *rh = (int32) ((r >> 32) & LMASK); /* hi result */ return ((int32) (r & LMASK)); /* lo result */ } /* Extended multiply subroutine */ int32 op_emul (int32 mpy, int32 mpc, int32 *rh) { t_int64 lmpy = mpy; t_int64 lmpc = mpc; lmpy = lmpy * lmpc; *rh = (int32) ((lmpy >> 32) & LMASK); return ((int32) (lmpy & LMASK)); } /* Extended divide opnd[0] = divisor (non-zero) opnd[1:2] = dividend */ int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg) { t_int64 ldvd, ldvr; int32 quo, rem; *flg = CC_V; /* assume error */ *rh = 0; ldvr = ((opnd[0] & LSIGN)? -opnd[0]: opnd[0]) & LMASK; /* |divisor| */ ldvd = CONCAT (opnd[2], opnd[1]); /* 64b dividend */ if (opnd[2] & LSIGN) /* |dividend| */ ldvd = -ldvd; if (((ldvd >> 32) & LMASK) >= ldvr) /* divide work? */ return opnd[1]; quo = (int32) (ldvd / ldvr); /* do divide */ rem = (int32) (ldvd % ldvr); if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ quo = -quo; /* negate */ if (quo && ((quo & LSIGN) == 0)) /* right sign? */ return opnd[1]; } else if (quo & LSIGN) return opnd[1]; if (opnd[2] & LSIGN) /* sign of rem */ rem = -rem; *flg = 0; /* no overflow */ *rh = rem & LMASK; /* set rem */ return (quo & LMASK); /* return quo */ } /* Compare floating */ int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2) { t_uint64 n1, n2; if ((h1 & FD_EXP) == 0) { if (h1 & FPSIGN) RSVD_OPND_FAULT; h1 = l1 = 0; } if ((h2 & FD_EXP) == 0) { if (h2 & FPSIGN) RSVD_OPND_FAULT; h2 = l2 = 0; } if ((h1 ^ h2) & FPSIGN) return ((h1 & FPSIGN)? CC_N: 0); n1 = UNSCRAM (h1, l1); n2 = UNSCRAM (h2, l2); if (n1 == n2) return CC_Z; return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); } int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2) { t_uint64 n1, n2; if ((h1 & G_EXP) == 0) { if (h1 & FPSIGN) RSVD_OPND_FAULT; h1 = l1 = 0; } if ((h2 & G_EXP) == 0) { if (h2 & FPSIGN) RSVD_OPND_FAULT; h2 = l2 = 0; } if ((h1 ^ h2) & FPSIGN) return ((h1 & FPSIGN)? CC_N: 0); n1 = UNSCRAM (h1, l1); n2 = UNSCRAM (h2, l2); if (n1 == n2) return CC_Z; return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); } /* Integer to floating convert */ int32 op_cvtifdg (int32 val, int32 *rh, int32 opc) { UFP a; if (val == 0) { if (rh) *rh = 0; return 0; } if (val < 0) { a.sign = FPSIGN; val = - val; } else a.sign = 0; a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); a.frac = ((t_uint64) val) << (UF_V_NM - 31); norm (&a); if (opc & 0x100) return rpackg (&a, rh); return rpackfd (&a, rh); } /* Floating to integer convert */ int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc) { UFP a; int32 lnt = opc & 03; int32 ubexp; static t_uint64 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; *flg = 0; if (opc & 0x100) { unpackg (opnd[0], opnd[1], &a); ubexp = a.exp - G_BIAS; } else { if (opc & 0x20) unpackd (opnd[0], opnd[1], &a); else unpackf (opnd[0], &a); ubexp = a.exp - FD_BIAS; } if ((a.exp == 0) || (ubexp < 0)) return 0; if (ubexp <= UF_V_NM) { a.frac = a.frac >> (UF_V_NM - ubexp); /* leave rnd bit */ if ((opc & 03) == 03) /* if CVTR, round */ a.frac = a.frac + 1; a.frac = a.frac >> 1; /* now justified */ if (a.frac > (maxv[lnt] + (a.sign? 1: 0))) *flg = CC_V; } else { *flg = CC_V; /* set overflow */ if (ubexp > (UF_V_NM + 32)) return 0; a.frac = a.frac << (ubexp - UF_V_NM - 1); /* no rnd bit */ } return ((int32) ((a.sign? (a.frac ^ LMASK) + 1: a.frac) & LMASK)); } /* Extended modularize One of three floating point instructions dropped from the architecture, EMOD presents two sets of complications. First, it requires an extended fraction multiply, with precise (and unusual) truncation conditions. Second, it has two write operands, a dubious distinction it shares with EDIV. */ int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg) { UFP a, b; unpackf (opnd[0], &a); /* unpack operands */ unpackf (opnd[2], &b); a.frac = a.frac | (((t_uint64) opnd[1]) << 32); /* extend src1 */ vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ return rpackfd (&a, NULL); /* return frac */ } int32 op_emodd (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) { UFP a, b; unpackd (opnd[0], opnd[1], &a); /* unpack operands */ unpackd (opnd[3], opnd[4], &b); a.frac = a.frac | opnd[2]; /* extend src1 */ vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ return rpackfd (&a, flo); /* return frac */ } int32 op_emodg (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) { UFP a, b; unpackg (opnd[0], opnd[1], &a); /* unpack operands */ unpackg (opnd[3], opnd[4], &b); a.frac = a.frac | (opnd[2] >> 5); /* extend src1 */ vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ return rpackg (&a, flo); /* return frac */ } /* Unpacked floating point routines */ void vax_fadd (UFP *a, UFP *b) { int32 ediff; UFP t; if (a->frac == 0) { /* s1 = 0? */ *a = *b; return; } if (b->frac == 0) /* s2 = 0? */ return; if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ ((a->exp == b->exp) && (a->frac < b->frac))) { t = *a; *a = *b; *b = t; } ediff = a->exp - b->exp; /* exp diff */ if (a->sign ^ b->sign) { /* eff sub? */ if (ediff) { /* exp diff? */ if (ediff > 63) /* retain sticky */ b->frac = M64; else b->frac = ((-((t_int64) b->frac) >> ediff) | /* denormalize */ (M64 << (64 - ediff))); /* preserve sign */ a->frac = a->frac + b->frac; /* add frac */ } else a->frac = a->frac - b->frac; /* sub frac */ norm (a); /* normalize */ } else { if (ediff > 63) /* add */ b->frac = 0; else if (ediff) /* denormalize */ b->frac = b->frac >> ediff; a->frac = a->frac + b->frac; /* add frac */ if (a->frac < b->frac) { /* chk for carry */ a->frac = UF_NM | (a->frac >> 1); /* shift in carry */ a->exp = a->exp + 1; /* skip norm */ } } return; } /* Floating multiply - 64b * 64b with cross products */ void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) { t_uint64 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; t_uint64 mask = (((t_uint64) mhi) << 32) | ((t_uint64) mlo); if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ a->frac = a->sign = a->exp = 0; /* result is zero */ return; } a->sign = a->sign ^ b->sign; /* sign of result */ a->exp = a->exp + b->exp - bias; /* add exponents */ ah = (a->frac >> 32) & LMASK; /* split operands */ bh = (b->frac >> 32) & LMASK; /* into 32b chunks */ rhi = ah * bh; /* high result */ if (qd) { /* 64b needed? */ al = a->frac & LMASK; bl = b->frac & LMASK; rmid1 = ah * bl; rmid2 = al * bh; rlo = al * bl; rhi = rhi + ((rmid1 >> 32) & LMASK) + ((rmid2 >> 32) & LMASK); rmid1 = rlo + (rmid1 << 32); /* add mid1 to lo */ if (rmid1 < rlo) /* carry? incr hi */ rhi = rhi + 1; rmid2 = rmid1 + (rmid2 << 32); /* add mid2 to lo */ if (rmid2 < rmid1) /* carry? incr hi */ rhi = rhi + 1; } a->frac = rhi & ~mask; norm (a); /* normalize */ return; } /* Floating modulus - there are three cases exp <= bias - integer is 0, fraction is input, no overflow bias < exp <= bias+64 - separate integer and fraction, integer overflow may occur bias+64 < exp - result is integer, fraction is 0 integer overflow */ void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) { if (a->exp <= bias) /* 0 or <1? int = 0 */ *intgr = *flg = 0; else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ *intgr = (int32) (a->frac >> (64 - (a->exp - bias))); if ((a->exp > (bias + 32)) || /* test ovflo */ ((a->exp == (bias + 32)) && (((uint32) *intgr) > (a->sign? 0x80000000: 0x7FFFFFFF)))) *flg = CC_V; else *flg = 0; if (a->sign) /* -? comp int */ *intgr = -*intgr; if (a->exp == (bias + 64)) /* special case 64 */ a->frac = 0; else a->frac = a->frac << (a->exp - bias); a->exp = bias; } else { *intgr = 0; /* out of range */ a->frac = a->sign = a->exp = 0; /* result 0 */ *flg = CC_V; /* overflow */ } norm (a); /* normalize */ return; } /* Floating divide Needs to develop at least one rounding bit. Since the first divide step can fail, caller should specify 2 more bits than the precision of the fraction. */ void vax_fdiv (UFP *a, UFP *b, int32 prec, int32 bias) { int32 i; t_uint64 quo = 0; if (a->exp == 0) /* divr = 0? */ FLT_DZRO_FAULT; if (b->exp == 0) /* divd = 0? */ return; b->sign = b->sign ^ a->sign; /* result sign */ b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ a->frac = a->frac >> 1; /* allow 1 bit left */ b->frac = b->frac >> 1; for (i = 0; (i < prec) && b->frac; i++) { /* divide loop */ quo = quo << 1; /* shift quo */ if (b->frac >= a->frac) { /* div step ok? */ b->frac = b->frac - a->frac; /* subtract */ quo = quo + 1; /* quo bit = 1 */ } b->frac = b->frac << 1; /* shift divd */ } b->frac = quo << (UF_V_NM - i + 1); /* shift quo */ norm (b); /* normalize */ return; } /* Support routines */ void unpackf (int32 hi, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = FD_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac = 0; /* else 0 */ return; } hi = (((hi & FD_FRACW) | FD_HB) << 16) | ((hi >> 16) & 0xFFFF); r->frac = ((t_uint64) hi) << (32 + UF_V_FDLO); return; } void unpackd (int32 hi, int32 lo, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = FD_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac = 0; /* else 0 */ return; } hi = (hi & FD_FRACL) | FD_HB; /* canonical form */ r->frac = UNSCRAM (hi, lo) << UF_V_FDLO; /* guard bits */ return; } void unpackg (int32 hi, int32 lo, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = G_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac = 0; /* else 0 */ return; } hi = (hi & G_FRACL) | G_HB; /* canonical form */ r->frac = UNSCRAM (hi, lo) << UF_V_GLO; /* guard bits */ return; } void norm (UFP *r) { int32 i; static t_uint64 normmask[5] = { 0xc000000000000000, 0xf000000000000000, 0xff00000000000000, 0xffff000000000000, 0xffffffff00000000 }; static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; if (r->frac == 0) { /* if fraction = 0 */ r->sign = r->exp = 0; /* result is 0 */ return; } while ((r->frac & UF_NM) == 0) { /* normalized? */ for (i = 0; i < 5; i++) { /* find first 1 */ if (r->frac & normmask[i]) break; } r->frac = r->frac << normtab[i]; /* shift frac */ r->exp = r->exp - normtab[i]; /* decr exp */ } return; } int32 rpackfd (UFP *r, int32 *rh) { if (rh) /* assume 0 */ *rh = 0; if (r->frac == 0) /* result 0? */ return 0; r->frac = r->frac + (rh? UF_DRND: UF_FRND); /* round */ if ((r->frac & UF_NM) == 0) { /* carry out? */ r->frac = r->frac >> 1; /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } if (rh) /* get low */ *rh = UF_GETFDLO (r->frac); return r->sign | (r->exp << FD_V_EXP) | UF_GETFDHI (r->frac); } int32 rpackg (UFP *r, int32 *rh) { *rh = 0; /* assume 0 */ if (r->frac == 0) /* result 0? */ return 0; r->frac = r->frac + UF_GRND; /* round */ if ((r->frac & UF_NM) == 0) { /* carry out? */ r->frac = r->frac >> 1; /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } *rh = UF_GETGLO (r->frac); /* get low */ return r->sign | (r->exp << G_V_EXP) | UF_GETGHI (r->frac); } #else /* 32b code */ #define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) typedef struct { uint32 lo; uint32 hi; } UDP; typedef struct { int32 sign; int32 exp; UDP frac; } UFP; #define UF_NM_H 0x80000000 /* normalized */ #define UF_FRND_H 0x00000080 /* F round */ #define UF_FRND_L 0x00000000 #define UF_DRND_H 0x00000000 /* D round */ #define UF_DRND_L 0x00000080 #define UF_GRND_H 0x00000000 /* G round */ #define UF_GRND_L 0x00000400 #define UF_V_NM 63 void unpackf (uint32 hi, UFP *a); void unpackd (uint32 hi, uint32 lo, UFP *a); void unpackg (uint32 hi, uint32 lo, UFP *a); void norm (UFP *a); int32 rpackfd (UFP *a, int32 *rh); int32 rpackg (UFP *a, int32 *rh); void vax_fadd (UFP *a, UFP *b); void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); void vax_fdiv (UFP *b, UFP *a, int32 prec, int32 bias); void dp_add (UDP *a, UDP *b); void dp_inc (UDP *a); void dp_sub (UDP *a, UDP *b); void dp_imul (uint32 a, uint32 b, UDP *r); void dp_lsh (UDP *a, uint32 sc); void dp_rsh (UDP *a, uint32 sc); void dp_rsh_s (UDP *a, uint32 sc, uint32 neg); void dp_neg (UDP *a); int32 dp_cmp (UDP *a, UDP *b); /* Quadword arithmetic shift opnd[0] = shift count (cnt.rb) opnd[1:2] = source (src.rq) opnd[3:4] = destination (dst.wq) */ int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg) { UDP r, sr; uint32 sc = opnd[0]; r.lo = opnd[1]; /* get source */ r.hi = opnd[2]; *flg = 0; /* assume no ovflo */ if (sc & BSIGN) /* right shift? */ dp_rsh_s (&r, 0x100 - sc, r.hi & LSIGN); /* signed right */ else { dp_lsh (&r, sc); /* left shift */ sr = r; /* copy result */ dp_rsh_s (&sr, sc, sr.hi & LSIGN); /* signed right */ if ((sr.hi != ((uint32) opnd[2])) || /* reshift != orig? */ (sr.lo != ((uint32) opnd[1]))) *flg = 1; /* overflow */ } *rh = r.hi; /* hi result */ return r.lo; /* lo result */ } /* Extended multiply subroutine */ int32 op_emul (int32 mpy, int32 mpc, int32 *rh) { UDP r; int32 sign = mpy ^ mpc; /* sign of result */ if (mpy & LSIGN) /* abs value */ mpy = -mpy; if (mpc & LSIGN) mpc = -mpc; dp_imul (mpy & LMASK, mpc & LMASK, &r); /* 32b * 32b -> 64b */ if (sign & LSIGN) /* negative result? */ dp_neg (&r); *rh = r.hi; return r.lo; } /* Extended divide opnd[0] = divisor (non-zero) opnd[1:2] = dividend */ int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg) { UDP dvd; uint32 i, dvr, quo; dvr = opnd[0]; /* get divisor */ dvd.lo = opnd[1]; /* get dividend */ dvd.hi = opnd[2]; *flg = CC_V; /* assume error */ *rh = 0; if (dvd.hi & LSIGN) /* |dividend| */ dp_neg (&dvd); if (dvr & LSIGN) /* |divisor| */ dvr = NEG (dvr); if (dvd.hi >= dvr) /* divide work? */ return opnd[1]; for (i = quo = 0; i < 32; i++) { /* 32 iterations */ quo = quo << 1; /* shift quotient */ dp_lsh (&dvd, 1); /* shift dividend */ if (dvd.hi >= dvr) { /* step work? */ dvd.hi = (dvd.hi - dvr) & LMASK; /* subtract dvr */ quo = quo + 1; } } if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ quo = NEG (quo); /* negate */ if (quo && ((quo & LSIGN) == 0)) /* right sign? */ return opnd[1]; } else if (quo & LSIGN) return opnd[1]; if (opnd[2] & LSIGN) /* sign of rem */ *rh = NEG (dvd.hi); else *rh = dvd.hi; *flg = 0; /* no overflow */ return quo; /* return quo */ } /* Compare floating */ int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2) { UFP a, b; int32 r; unpackd (h1, l1, &a); unpackd (h2, l2, &b); if (a.sign != b.sign) return (a.sign? CC_N: 0); r = a.exp - b.exp; if (r == 0) r = dp_cmp (&a.frac, &b.frac); if (r < 0) return (a.sign? 0: CC_N); if (r > 0) return (a.sign? CC_N: 0); return CC_Z; } int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2) { UFP a, b; int32 r; unpackg (h1, l1, &a); unpackg (h2, l2, &b); if (a.sign != b.sign) return (a.sign? CC_N: 0); r = a.exp - b.exp; if (r == 0) r = dp_cmp (&a.frac, &b.frac); if (r < 0) return (a.sign? 0: CC_N); if (r > 0) return (a.sign? CC_N: 0); return CC_Z; } /* Integer to floating convert */ int32 op_cvtifdg (int32 val, int32 *rh, int32 opc) { UFP a; if (val == 0) { /* zero? */ if (rh) *rh = 0; /* return true 0 */ return 0; } if (val < 0) { /* negative? */ a.sign = FPSIGN; /* sign = - */ val = -val; } else a.sign = 0; /* else sign = + */ a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); /* initial exp */ a.frac.hi = val & LMASK; /* fraction */ a.frac.lo = 0; norm (&a); /* normalize */ if (opc & 0x100) /* pack and return */ return rpackg (&a, rh); return rpackfd (&a, rh); } /* Floating to integer convert */ int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc) { UFP a; int32 lnt = opc & 03; int32 ubexp; static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; *flg = 0; if (opc & 0x100) { /* G? */ unpackg (opnd[0], opnd[1], &a); /* unpack */ ubexp = a.exp - G_BIAS; /* unbiased exp */ } else { if (opc & 0x20) /* F or D */ unpackd (opnd[0], opnd[1], &a); else unpackf (opnd[0], &a); /* unpack */ ubexp = a.exp - FD_BIAS; /* unbiased exp */ } if ((a.exp == 0) || (ubexp < 0)) /* true zero or frac? */ return 0; if (ubexp <= UF_V_NM) { /* exp in range? */ dp_rsh (&a.frac, UF_V_NM - ubexp); /* leave rnd bit */ if (lnt == 03) /* if CVTR, round */ dp_inc (&a.frac); dp_rsh (&a.frac, 1); /* now justified */ if ((a.frac.hi != 0) || (a.frac.lo > (maxv[lnt] + (a.sign? 1: 0)))) *flg = CC_V; } else { *flg = CC_V; /* always ovflo */ if (ubexp > (UF_V_NM + 32)) /* in ext range? */ return 0; dp_lsh (&a.frac, ubexp - UF_V_NM - 1); /* no rnd bit */ } return (a.sign? NEG (a.frac.lo): a.frac.lo); /* return lo frac */ } /* Extended modularize One of three floating point instructions dropped from the architecture, EMOD presents two sets of complications. First, it requires an extended fraction multiply, with precise (and unusual) truncation conditions. Second, it has two write operands, a dubious distinction it shares with EDIV. */ int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg) { UFP a, b; unpackf (opnd[0], &a); /* unpack operands */ unpackf (opnd[2], &b); a.frac.hi = a.frac.hi | opnd[1]; /* extend src1 */ vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ return rpackfd (&a, NULL); /* return frac */ } int32 op_emodd (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) { UFP a, b; unpackd (opnd[0], opnd[1], &a); /* unpack operands */ unpackd (opnd[3], opnd[4], &b); a.frac.lo = a.frac.lo | opnd[2]; /* extend src1 */ vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ return rpackfd (&a, flo); /* return frac */ } int32 op_emodg (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) { UFP a, b; unpackg (opnd[0], opnd[1], &a); /* unpack operands */ unpackg (opnd[3], opnd[4], &b); a.frac.lo = a.frac.lo | (opnd[2] >> 5); /* extend src1 */ vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ return rpackg (&a, flo); /* return frac */ } /* Unpacked floating point routines */ /* Floating add */ void vax_fadd (UFP *a, UFP *b) { int32 ediff; UFP t; if ((a->frac.hi == 0) && (a->frac.lo == 0)) { /* s1 = 0? */ *a = *b; return; } if ((b->frac.hi == 0) && (b->frac.lo == 0)) /* s2 = 0? */ return; if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ ((a->exp == b->exp) && (dp_cmp (&a->frac, &b->frac) < 0))) { t = *a; *a = *b; *b = t; } ediff = a->exp - b->exp; /* exp diff */ if (a->sign ^ b->sign) { /* eff sub? */ if (ediff) { /* exp diff? */ dp_neg (&b->frac); /* negate fraction */ dp_rsh_s (&b->frac, ediff, 1); /* signed right */ dp_add (&a->frac, &b->frac); /* "add" frac */ } else dp_sub (&a->frac, &b->frac); /* a >= b */ norm (a); /* normalize */ } else { if (ediff) /* add, denormalize */ dp_rsh (&b->frac, ediff); dp_add (&a->frac, &b->frac); /* add frac */ if (dp_cmp (&a->frac, &b->frac) < 0) { /* chk for carry */ dp_rsh (&a->frac, 1); /* renormalize */ a->frac.hi = a->frac.hi | UF_NM_H; /* add norm bit */ a->exp = a->exp + 1; /* skip norm */ } } return; } /* Floating multiply - 64b * 64b with cross products */ void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) { UDP rhi, rlo, rmid1, rmid2; if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ a->frac.hi = a->frac.lo = 0; /* result is zero */ a->sign = a->exp = 0; return; } a->sign = a->sign ^ b->sign; /* sign of result */ a->exp = a->exp + b->exp - bias; /* add exponents */ dp_imul (a->frac.hi, b->frac.hi, &rhi); /* high result */ if (qd) { /* 64b needed? */ dp_imul (a->frac.hi, b->frac.lo, &rmid1); /* cross products */ dp_imul (a->frac.lo, b->frac.hi, &rmid2); dp_imul (a->frac.lo, b->frac.lo, &rlo); /* low result */ rhi.lo = (rhi.lo + rmid1.hi) & LMASK; /* add hi cross */ if (rhi.lo < rmid1.hi) /* to low high res */ rhi.hi = (rhi.hi + 1) & LMASK; rhi.lo = (rhi.lo + rmid2.hi) & LMASK; if (rhi.lo < rmid2.hi) rhi.hi = (rhi.hi + 1) & LMASK; rlo.hi = (rlo.hi + rmid1.lo) & LMASK; /* add mid1 to low res */ if (rlo.hi < rmid1.lo) /* carry? incr high res */ dp_inc (&rhi); rlo.hi = (rlo.hi + rmid2.lo) & LMASK; /* add mid2 to low res */ if (rlo.hi < rmid2.lo) /* carry? incr high res */ dp_inc (&rhi); } a->frac.hi = rhi.hi & ~mhi; /* mask fraction */ a->frac.lo = rhi.lo & ~mlo; norm (a); /* normalize */ return; } /* Floating modulus - there are three cases exp <= bias - integer is 0, fraction is input, no overflow bias < exp <= bias+64 - separate integer and fraction, integer overflow may occur bias+64 < exp - result is integer, fraction is 0 integer overflow */ void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) { UDP ifr; if (a->exp <= bias) /* 0 or <1? int = 0 */ *intgr = *flg = 0; else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ ifr = a->frac; dp_rsh (&ifr, 64 - (a->exp - bias)); /* separate integer */ if ((a->exp > (bias + 32)) || /* test ovflo */ ((a->exp == (bias + 32)) && (ifr.lo > (a->sign? 0x80000000: 0x7FFFFFFF)))) *flg = CC_V; else *flg = 0; *intgr = ifr.lo; if (a->sign) /* -? comp int */ *intgr = -*intgr; dp_lsh (&a->frac, a->exp - bias); /* excise integer */ a->exp = bias; } else { *intgr = 0; /* out of range */ a->frac.hi = a->frac.lo = a->sign = a->exp = 0; /* result 0 */ *flg = CC_V; /* overflow */ } norm (a); /* normalize */ return; } /* Floating divide Needs to develop at least one rounding bit. Since the first divide step can fail, caller should specify 2 more bits than the precision of the fraction. */ void vax_fdiv (UFP *a, UFP *b, int32 prec, int32 bias) { int32 i; UDP quo = { 0, 0 }; if (a->exp == 0) /* divr = 0? */ FLT_DZRO_FAULT; if (b->exp == 0) /* divd = 0? */ return; b->sign = b->sign ^ a->sign; /* result sign */ b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ dp_rsh (&a->frac, 1); /* allow 1 bit left */ dp_rsh (&b->frac, 1); for (i = 0; i < prec; i++) { /* divide loop */ dp_lsh (&quo, 1); /* shift quo */ if (dp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ dp_sub (&b->frac, &a->frac); /* subtract */ quo.lo = quo.lo + 1; /* quo bit = 1 */ } dp_lsh (&b->frac, 1); /* shift divd */ } dp_lsh (&quo, UF_V_NM - prec + 1); /* put in position */ b->frac = quo; norm (b); /* normalize */ return; } /* Double precision integer routines */ int32 dp_cmp (UDP *a, UDP *b) { if (a->hi < b->hi) /* compare hi */ return -1; if (a->hi > b->hi) return +1; if (a->lo < b->lo) /* hi =, compare lo */ return -1; if (a->lo > b->lo) return +1; return 0; /* hi, lo equal */ } void dp_add (UDP *a, UDP *b) { a->lo = (a->lo + b->lo) & LMASK; /* add lo */ if (a->lo < b->lo) /* carry? */ a->hi = a->hi + 1; a->hi = (a->hi + b->hi) & LMASK; /* add hi */ return; } void dp_inc (UDP *a) { a->lo = (a->lo + 1) & LMASK; /* inc lo */ if (a->lo == 0) /* carry? inc hi */ a->hi = (a->hi + 1) & LMASK; return; } void dp_sub (UDP *a, UDP *b) { if (a->lo < b->lo) /* borrow? decr hi */ a->hi = a->hi - 1; a->lo = (a->lo - b->lo) & LMASK; /* sub lo */ a->hi = (a->hi - b->hi) & LMASK; /* sub hi */ return; } void dp_lsh (UDP *r, uint32 sc) { if (sc > 63) /* > 63? result 0 */ r->hi = r->lo = 0; else if (sc > 31) { /* [32,63]? */ r->hi = (r->lo << (sc - 32)) & LMASK; r->lo = 0; } else if (sc != 0) { r->hi = ((r->hi << sc) | (r->lo >> (32 - sc))) & LMASK; r->lo = (r->lo << sc) & LMASK; } return; } void dp_rsh (UDP *r, uint32 sc) { if (sc > 63) /* > 63? result 0 */ r->hi = r->lo = 0; else if (sc > 31) { /* [32,63]? */ r->lo = (r->hi >> (sc - 32)) & LMASK; r->hi = 0; } else if (sc != 0) { r->lo = ((r->lo >> sc) | (r->hi << (32 - sc))) & LMASK; r->hi = (r->hi >> sc) & LMASK; } return; } void dp_rsh_s (UDP *r, uint32 sc, uint32 neg) { dp_rsh (r, sc); /* do unsigned right */ if (neg && sc) { /* negative? */ if (sc > 63) /* > 63? result -1 */ r->hi = r->lo = LMASK; else { UDP ones = { LMASK, LMASK }; dp_lsh (&ones, 64 - sc); /* shift ones */ r->hi = r->hi | ones.hi; /* or into result */ r->lo = r->lo | ones.lo; } } return; } void dp_imul (uint32 a, uint32 b, UDP *r) { uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; if ((a == 0) || (b == 0)) { /* zero argument? */ r->hi = r->lo = 0; /* result is zero */ return; } ah = (a >> 16) & WMASK; /* split operands */ bh = (b >> 16) & WMASK; /* into 16b chunks */ al = a & WMASK; bl = b & WMASK; rhi = ah * bh; /* high result */ rmid1 = ah * bl; rmid2 = al * bh; rlo = al * bl; rhi = rhi + ((rmid1 >> 16) & WMASK) + ((rmid2 >> 16) & WMASK); rmid1 = (rlo + (rmid1 << 16)) & LMASK; /* add mid1 to lo */ if (rmid1 < rlo) /* carry? incr hi */ rhi = rhi + 1; rmid2 = (rmid1 + (rmid2 << 16)) & LMASK; /* add mid2 to to */ if (rmid2 < rmid1) /* carry? incr hi */ rhi = rhi + 1; r->hi = rhi & LMASK; /* mask result */ r->lo = rmid2; return; } void dp_neg (UDP *r) { r->lo = NEG (r->lo); r->hi = (~r->hi + (r->lo == 0)) & LMASK; return; } /* Support routines */ void unpackf (uint32 hi, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = FD_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.hi = r->frac.lo = 0; /* else 0 */ return; } r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); r->frac.lo = 0; dp_lsh (&r->frac, FD_GUARD); return; } void unpackd (uint32 hi, uint32 lo, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = FD_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.hi = r->frac.lo = 0; /* else 0 */ return; } r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); r->frac.lo = WORDSWAP (lo); dp_lsh (&r->frac, FD_GUARD); return; } void unpackg (uint32 hi, uint32 lo, UFP *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = G_GETEXP (hi); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.hi = r->frac.lo = 0; /* else 0 */ return; } r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); r->frac.lo = WORDSWAP (lo); dp_lsh (&r->frac, G_GUARD); return; } void norm (UFP *r) { int32 i; static uint32 normmask[5] = { 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff }; static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; if ((r->frac.hi == 0) && (r->frac.lo == 0)) { /* if fraction = 0 */ r->sign = r->exp = 0; /* result is 0 */ return; } while ((r->frac.hi & UF_NM_H) == 0) { /* normalized? */ for (i = 0; i < 5; i++) { /* find first 1 */ if (r->frac.hi & normmask[i]) break; } dp_lsh (&r->frac, normtab[i]); /* shift frac */ r->exp = r->exp - normtab[i]; /* decr exp */ } return; } int32 rpackfd (UFP *r, int32 *rh) { static UDP f_round = { UF_FRND_L, UF_FRND_H }; static UDP d_round = { UF_DRND_L, UF_DRND_H }; if (rh) /* assume 0 */ *rh = 0; if ((r->frac.hi == 0) && (r->frac.lo == 0)) /* result 0? */ return 0; if (rh) /* round */ dp_add (&r->frac, &d_round); else dp_add (&r->frac, &f_round); if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ dp_rsh (&r->frac, 1); /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } dp_rsh (&r->frac, FD_GUARD); /* remove guard */ if (rh) /* get low */ *rh = WORDSWAP (r->frac.lo); return r->sign | (r->exp << FD_V_EXP) | (WORDSWAP (r->frac.hi) & ~(FD_HB | FPSIGN | FD_EXP)); } int32 rpackg (UFP *r, int32 *rh) { static UDP g_round = { UF_GRND_L, UF_GRND_H }; *rh = 0; /* assume 0 */ if ((r->frac.hi == 0) && (r->frac.lo == 0)) /* result 0? */ return 0; dp_add (&r->frac, &g_round); /* round */ if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ dp_rsh (&r->frac, 1); /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } dp_rsh (&r->frac, G_GUARD); /* remove guard */ *rh = WORDSWAP (r->frac.lo); /* get low */ return r->sign | (r->exp << G_V_EXP) | (WORDSWAP (r->frac.hi) & ~(G_HB | FPSIGN | G_EXP)); } #endif /* Floating point instructions */ /* Move/test/move negated floating Note that only the high 32b is processed. If the high 32b is not zero, it is unchanged. */ int32 op_movfd (int32 val) { if (val & FD_EXP) return val; if (val & FPSIGN) RSVD_OPND_FAULT; return 0; } int32 op_mnegfd (int32 val) { if (val & FD_EXP) return (val ^ FPSIGN); if (val & FPSIGN) RSVD_OPND_FAULT; return 0; } int32 op_movg (int32 val) { if (val & G_EXP) return val; if (val & FPSIGN) RSVD_OPND_FAULT; return 0; } int32 op_mnegg (int32 val) { if (val & G_EXP) return (val ^ FPSIGN); if (val & FPSIGN) RSVD_OPND_FAULT; return 0; } /* Floating to floating convert - F to D is essentially done with MOVFD */ int32 op_cvtdf (int32 *opnd) { UFP a; unpackd (opnd[0], opnd[1], &a); return rpackfd (&a, NULL); } int32 op_cvtfg (int32 *opnd, int32 *rh) { UFP a; unpackf (opnd[0], &a); a.exp = a.exp - FD_BIAS + G_BIAS; return rpackg (&a, rh); } int32 op_cvtgf (int32 *opnd) { UFP a; unpackg (opnd[0], opnd[1], &a); a.exp = a.exp - G_BIAS + FD_BIAS; return rpackfd (&a, NULL); } /* Floating add and subtract */ int32 op_addf (int32 *opnd, t_bool sub) { UFP a, b; unpackf (opnd[0], &a); /* F format */ unpackf (opnd[1], &b); if (sub) /* sub? -s1 */ a.sign = a.sign ^ FPSIGN; vax_fadd (&a, &b); /* add fractions */ return rpackfd (&a, NULL); } int32 op_addd (int32 *opnd, int32 *rh, t_bool sub) { UFP a, b; unpackd (opnd[0], opnd[1], &a); unpackd (opnd[2], opnd[3], &b); if (sub) /* sub? -s1 */ a.sign = a.sign ^ FPSIGN; vax_fadd (&a, &b); /* add fractions */ return rpackfd (&a, rh); } int32 op_addg (int32 *opnd, int32 *rh, t_bool sub) { UFP a, b; unpackg (opnd[0], opnd[1], &a); unpackg (opnd[2], opnd[3], &b); if (sub) /* sub? -s1 */ a.sign = a.sign ^ FPSIGN; vax_fadd (&a, &b); /* add fractions */ return rpackg (&a, rh); /* round and pack */ } /* Floating multiply */ int32 op_mulf (int32 *opnd) { UFP a, b; unpackf (opnd[0], &a); /* F format */ unpackf (opnd[1], &b); vax_fmul (&a, &b, 0, FD_BIAS, 0, 0); /* do multiply */ return rpackfd (&a, NULL); /* round and pack */ } int32 op_muld (int32 *opnd, int32 *rh) { UFP a, b; unpackd (opnd[0], opnd[1], &a); /* D format */ unpackd (opnd[2], opnd[3], &b); vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* do multiply */ return rpackfd (&a, rh); /* round and pack */ } int32 op_mulg (int32 *opnd, int32 *rh) { UFP a, b; unpackg (opnd[0], opnd[1], &a); /* G format */ unpackg (opnd[2], opnd[3], &b); vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* do multiply */ return rpackg (&a, rh); /* round and pack */ } /* Floating divide */ int32 op_divf (int32 *opnd) { UFP a, b; unpackf (opnd[0], &a); /* F format */ unpackf (opnd[1], &b); vax_fdiv (&a, &b, 26, FD_BIAS); /* do divide */ return rpackfd (&b, NULL); /* round and pack */ } int32 op_divd (int32 *opnd, int32 *rh) { UFP a, b; unpackd (opnd[0], opnd[1], &a); /* D format */ unpackd (opnd[2], opnd[3], &b); vax_fdiv (&a, &b, 58, FD_BIAS); /* do divide */ return rpackfd (&b, rh); /* round and pack */ } int32 op_divg (int32 *opnd, int32 *rh) { UFP a, b; unpackg (opnd[0], opnd[1], &a); /* G format */ unpackg (opnd[2], opnd[3], &b); vax_fdiv (&a, &b, 55, G_BIAS); /* do divide */ return rpackg (&b, rh); /* round and pack */ } /* Polynomial evaluation The most mis-implemented instruction in the VAX (probably here too). POLY requires a precise combination of masking versus normalizing to achieve the desired answer. In particular, the multiply step is masked prior to normalization. In addition, negative small fractions must not be treated as 0 during denorm. */ void op_polyf (int32 *opnd, int32 acc) { UFP r, a, c; int32 deg = opnd[1]; int32 ptr = opnd[2]; int32 i, wd, res; if (deg > 31) /* degree > 31? fault */ RSVD_OPND_FAULT; unpackf (opnd[0], &a); /* unpack arg */ wd = Read (ptr, L_LONG, RD); /* get C0 */ ptr = ptr + 4; unpackf (wd, &r); /* unpack C0 */ res = rpackfd (&r, NULL); /* first result */ for (i = 0; i < deg; i++) { /* loop */ unpackf (res, &r); /* unpack result */ vax_fmul (&r, &a, 0, FD_BIAS, 1, LMASK); /* r = r * arg, mask */ wd = Read (ptr, L_LONG, RD); /* get Cnext */ ptr = ptr + 4; unpackf (wd, &c); /* unpack Cnext */ vax_fadd (&r, &c); /* r = r + Cnext */ res = rpackfd (&r, NULL); /* round and pack */ } R[0] = res; R[1] = R[2] = 0; R[3] = ptr; return; } void op_polyd (int32 *opnd, int32 acc) { UFP r, a, c; int32 deg = opnd[2]; int32 ptr = opnd[3]; int32 i, wd, wd1, res, resh; if (deg > 31) /* degree > 31? fault */ RSVD_OPND_FAULT; unpackd (opnd[0], opnd[1], &a); /* unpack arg */ wd = Read (ptr, L_LONG, RD); /* get C0 */ wd1 = Read (ptr + 4, L_LONG, RD); ptr = ptr + 8; unpackd (wd, wd1, &r); /* unpack C0 */ res = rpackfd (&r, &resh); /* first result */ for (i = 0; i < deg; i++) { /* loop */ unpackd (res, resh, &r); /* unpack result */ vax_fmul (&r, &a, 1, FD_BIAS, 0, 1); /* r = r * arg, mask */ wd = Read (ptr, L_LONG, RD); /* get Cnext */ wd1 = Read (ptr + 4, L_LONG, RD); ptr = ptr + 8; unpackd (wd, wd1, &c); /* unpack Cnext */ vax_fadd (&r, &c); /* r = r + Cnext */ res = rpackfd (&r, &resh); /* round and pack */ } R[0] = res; R[1] = resh; R[2] = 0; R[3] = ptr; R[4] = 0; R[5] = 0; return; } void op_polyg (int32 *opnd, int32 acc) { UFP r, a, c; int32 deg = opnd[2]; int32 ptr = opnd[3]; int32 i, wd, wd1, res, resh; if (deg > 31) /* degree > 31? fault */ RSVD_OPND_FAULT; unpackg (opnd[0], opnd[1], &a); /* unpack arg */ wd = Read (ptr, L_LONG, RD); /* get C0 */ wd1 = Read (ptr + 4, L_LONG, RD); ptr = ptr + 8; unpackg (wd, wd1, &r); /* unpack C0 */ res = rpackg (&r, &resh); /* first result */ for (i = 0; i < deg; i++) { /* loop */ unpackg (res, resh, &r); /* unpack result */ vax_fmul (&r, &a, 1, G_BIAS, 0, 1); /* r = r * arg */ wd = Read (ptr, L_LONG, RD); /* get Cnext */ wd1 = Read (ptr + 4, L_LONG, RD); ptr = ptr + 8; unpackg (wd, wd1, &c); /* unpack Cnext */ vax_fadd (&r, &c); /* r = r + Cnext */ res = rpackg (&r, &resh); /* round and pack */ } R[0] = res; R[1] = resh; R[2] = 0; R[3] = ptr; R[4] = 0; R[5] = 0; return; } simh-3.8.1/VAX/vax780_fload.c0000644000175000017500000002244011110566220013641 0ustar vlmvlm/* vax780_fload.c: VAX780 FLOAD command Copyright (c) 2006-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This code is based on the CP/M RT11 utility, which bears the following copyrights: copyright (c) 1980, William C. Colley, III Rev. 1.2 -- Craig Davenport - Incitec Ltd (Feb 1984) P O Box 140 Morningside, Qld 4170, Australia. -- Modified for Digital Research C compiler under CP/M-86 -- Assebmbly language routines added for BIOS calls etc. Thanks to Phil Budne for the original adaptation of RT11 to SimH. 28-May-08 RMS Inlined physical memory routines */ #include "vax_defs.h" #include #define BLK_SIZE 256 /* RT11 block size */ /* Floppy disk parameters */ #define BPT 26 /* blocks/track */ #define NTRACKS 77 #define SECTOR_SKEW 2 #define TRACK_SKEW 6 #define TRACK_OFFSET 1 /* track 0 unused */ /* RT11 directory segment (2 blocks = 512 16b words) */ #define DS_TOTAL 0 /* segments available */ #define DS_MAX 31 /* segment max */ #define DS_NEXT 1 /* zero for last segment */ #define DS_HIGHEST 2 /* only in 1st segment */ #define DS_EXTRA 3 /* extra bytes/entry */ #define DS_FIRST 4 /* first block */ #define DS_ENTRIES 5 /* start of entries */ #define DS_SIZE (2 * BLK_SIZE) /* segment size, words */ /* RT11 directory entry offsets */ #define DE_STATUS 0 /* status (odd byte) */ #define TENTAT 001 /* tentative */ #define EMPTY 002 #define PERM 004 #define ENDSEG 010 /* end of segment */ #define DE_NAME 1 /* file name */ #define DE_FLNT 4 /* file length */ #define DE_SIZE 7 /* entry size in words */ #define DE_GET_STAT(x) (((x) >> 8) & 0377) extern UNIT cpu_unit; extern UNIT fl_unit; t_bool rtfile_parse (char *pntr, uint16 *file_name); uint32 rtfile_lookup (uint16 *file_name, uint32 *start); uint32 rtfile_ator50 (uint32 ascii); t_bool rtfile_read (uint32 block, uint32 count, uint16 *buffer); uint32 rtfile_find (uint32 block, uint32 sector); /* FLOAD file_name {file_origin} */ t_stat vax780_fload (int flag, char *cptr) { char gbuf[CBUFSIZE]; uint16 file_name[3], blkbuf[BLK_SIZE]; t_stat r; uint32 i, j, start, size, origin; if ((fl_unit.flags & UNIT_ATT) == 0) /* floppy attached? */ return SCPE_UNATT; if (*cptr == 0) return SCPE_2FARG; cptr = get_glyph (cptr, gbuf, 0); /* get file name */ if (!rtfile_parse (gbuf, file_name)) /* legal file name? */ return SCPE_ARG; if ((size = rtfile_lookup (file_name, &start)) == 0) /* file on floppy? */ return SCPE_ARG; if (*cptr) { /* origin? */ origin = (uint32) get_uint (cptr, 16, MEMSIZE, &r); if ((r != SCPE_OK) || (origin & 1)) /* must be even */ return SCPE_ARG; } else origin = 512; /* no, use default */ for (i = 0; i < size; i++) { /* loop thru blocks */ if (!rtfile_read (start + i, 1, blkbuf)) /* read block */ return SCPE_FMT; for (j = 0; j < BLK_SIZE; j++) { /* loop thru words */ if (ADDR_IS_MEM (origin)) WriteW (origin, blkbuf[j]); else return SCPE_NXM; origin = origin + 2; } } return SCPE_OK; } /* Parse an RT11 file name and convert it to radix-50 */ t_bool rtfile_parse (char *pntr, uint16 *file_name) { char c; uint16 d; uint32 i, j; file_name[0] = file_name[1] = file_name[2] = 0; /* zero file name */ for (i = 0; i < 2; i++) { /* 6 characters */ for (j = 0; j < 3; j++) { c = *pntr; if ((c == '.') || (c == 0)) /* fill if . or end */ d = 0; else { if ((d = rtfile_ator50 (c)) == 0) return FALSE; pntr++; } file_name[i] = (file_name[i] * 050) + d; /* merge into name */ } } if (file_name[0] == 0) /* no name? lose */ return FALSE; while ((c = *pntr++) != '.') { /* scan for . */ if (c == 0) /* end? done */ return TRUE; } for (i = 0; i < 3; i++) { /* 3 characters */ c = *pntr; if (c == 0) /* fill if end */ d = 0; else { if ((d = rtfile_ator50 (c)) == 0) return FALSE; pntr++; } file_name[2] = (file_name[2] * 050) + d; /* merge into ext */ } return TRUE; } /* ASCII to radix-50 conversion */ uint32 rtfile_ator50 (uint32 ascii) { static char *r50 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; char *fptr; ascii = toupper (ascii); if ((fptr = strchr (r50, toupper (ascii))) != NULL) return ((uint32) (fptr - r50)); else return 0; } /* Lookup an RT11 file name in the directory */ uint32 rtfile_lookup (uint16 *file_name, uint32 *start) { uint16 dirseg[DS_SIZE]; uint32 segnum, dirent; for (segnum = 1; (segnum != 0) && (segnum <= DS_MAX); /* loop thru segments */ segnum = dirseg[DS_NEXT]) { if (!rtfile_read ((segnum * 2) + 4, 2, dirseg)) /* read segment */ return 0; /* error? */ *start = dirseg[DS_FIRST]; /* init file start */ for (dirent = DS_ENTRIES; /* loop thru entries */ (dirent < DS_SIZE) && (DE_GET_STAT (dirseg[dirent + DE_STATUS]) != ENDSEG); dirent += DE_SIZE + (dirseg[DS_EXTRA] / 2)) { if ((DE_GET_STAT (dirseg[dirent + DE_STATUS]) == PERM) && (dirseg[dirent + DE_NAME + 0] == file_name[0]) && (dirseg[dirent + DE_NAME + 1] == file_name[1]) && (dirseg[dirent + DE_NAME + 2] == file_name[2])) return dirseg[dirent + DE_FLNT]; *start += dirseg[dirent + DE_FLNT]; /* incr file start */ } } return 0; } /* Read blocks */ t_stat rtfile_read (uint32 block, uint32 count, uint16 *buffer) { uint32 i, j; uint32 pos; uint8 *fbuf = fl_unit.filebuf; for (; count > 0; count--, block++) { for (i = 0; i < 4; i++) { /* 4 sectors/block */ pos = rtfile_find (block, i); /* position */ if ((pos + 128) >= (uint32) fl_unit.capac) /* off end of disk? */ return FALSE; for (j = 0; j < 128; j = j + 2) /* copy 128 bytes */ *buffer++ = (((uint16) fbuf[pos + j + 1]) << 8) | ((uint16) fbuf[pos + j]); } } return TRUE; } /* Map an RT-11 block number to a physical byte number */ uint32 rtfile_find (uint32 block, uint32 sector) { uint32 ls, lt, pt, ps; uint32 off, bb; /* get logical block, track & sector */ bb = (block * 4) + sector; lt = bb / BPT; ls = bb % BPT; /* logic from 4.3BSD rx.c * calculate phys track & sector * 2:1 skew, 6 sector skew for each track */ pt = lt + TRACK_OFFSET; ps = ((ls * SECTOR_SKEW) + (ls / (BPT / SECTOR_SKEW)) + (TRACK_SKEW * lt)) % BPT; /* byte offset in logical disk */ off = (pt * BPT + ps) * 128; return off; } simh-3.8.1/VAX/vax780_sbi.c0000644000175000017500000006105711110567462013351 0ustar vlmvlm/* vax780_sbi.c: VAX 11/780 SBI Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module contains the VAX 11/780 system-specific registers and devices. sbi bus controller 31-May-2008 RMS Fixed machine_check calling sequence (found by Peter Schorn) 03-May-2006 RMS Fixed writes to ACCS 28-May-08 RMS Inlined physical memory routines */ #include "vax_defs.h" /* 11/780 specific IPRs */ /* Writeable control store */ #define WCSA_RW 0xFFFF /* writeable */ #define WCSA_ADDR 0x1FFF /* addr */ #define WCSA_CTR 0x6000 /* counter */ #define WCSA_CTR_INC 0x2000 /* increment */ #define WCSA_CTR_MAX 0x6000 /* max value */ #define WCSD_RD_VAL 0xFF /* fixed read val */ #define WCSD_WR 0xFFFFFFFF /* write */ #define MBRK_RW 0x1FFF /* microbreak */ /* System registers */ #define SBIFS_RD (0x031F0000|SBI_FAULTS) /* SBI faults */ #define SBIFS_WR 0x03140000 #define SBIFS_W1C 0x00080000 #define SBISC_RD 0xFFFF0000 /* SBI silo comp */ #define SBISC_WR 0x7FFF0000 #define SBISC_LOCK 0x80000000 /* lock */ #define SBIMT_RD 0xFFFFFF00 /* SBI maint */ #define SBIMT_WR 0xFFFFF900 #define SBIER_CRDIE 0x00008000 /* SBI error, CRD IE */ #define SBIER_CRD 0x00004000 /* CRD */ #define SBIER_RDS 0x00002000 /* RDS */ #define SBIER_TMO 0x00001000 /* timeout */ #define SBIER_STA 0x00000C00 /* timeout status (0) */ #define SBIER_CNF 0x00000100 /* error confirm */ #define SBIER_IBRDS 0x00000080 #define SBIER_IBTMO 0x00000040 #define SBIER_IBSTA 0x00000030 #define SBIER_IBCNF 0x00000008 #define SBIER_MULT 0x00000004 /* multiple errors */ #define SBIER_FREE 0x00000002 /* SBI free */ #define SBIER_RD 0x0000FDFE #define SBIER_WR 0x00008000 #define SBIER_W1C 0x000070C0 #define SBIER_TMOW1C (SBIER_TMO|SBIER_STA|SBIER_CNF|SBIER_MULT) #define SBIER_IBTW1C (SBIER_IBTMO|SBIER_STA|SBIER_IBCNF) #define SBITMO_V_MODE 30 /* mode */ #define SBITMO_VIRT 0x20000000 /* physical */ #define SBIQC_MBZ 0xC0000007 /* MBZ */ /* VAX-11/780 boot device definitions */ struct boot_dev { char *name; int32 code; int32 let; }; uint32 wcs_addr = 0; uint32 wcs_data = 0; uint32 wcs_mbrk = 0; uint32 nexus_req[NEXUS_HLVL]; /* nexus int req */ uint32 sbi_fs = 0; /* SBI fault status */ uint32 sbi_sc = 0; /* SBI silo comparator */ uint32 sbi_mt = 0; /* SBI maintenance */ uint32 sbi_er = 0; /* SBI error status */ uint32 sbi_tmo = 0; /* SBI timeout addr */ static t_stat (*nexusR[NEXUS_NUM])(int32 *dat, int32 ad, int32 md); static t_stat (*nexusW[NEXUS_NUM])(int32 dat, int32 ad, int32 md); static struct boot_dev boot_tab[] = { { "RP", BOOT_MB, 0 }, { "HK", BOOT_HK, 0 }, { "RL", BOOT_RL, 0 }, { "RQ", BOOT_UDA, 1 << 24 }, { "TQ", BOOT_TK, 1 << 24 }, { NULL } }; extern int32 R[16]; extern int32 PSL; extern int32 ASTLVL, SISR; extern int32 mapen, pme, trpirq; extern int32 in_ie; extern int32 mchk_va, mchk_ref; extern int32 crd_err, mem_err, hlt_pin; extern int32 tmr_int, tti_int, tto_int; extern jmp_buf save_env; extern int32 p1; extern int32 sim_switches; extern DEVICE *sim_devices[]; extern FILE *sim_log; extern CTAB *sim_vm_cmd; t_stat sbi_reset (DEVICE *dptr); void sbi_set_tmo (int32 pa); void uba_eval_int (void); t_stat vax780_boot (int32 flag, char *ptr); extern t_stat vax780_fload (int flag, char *cptr); extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); extern int32 iccs_rd (void); extern int32 nicr_rd (void); extern int32 icr_rd (t_bool interp); extern int32 todr_rd (void); extern int32 rxcs_rd (void); extern int32 rxdb_rd (void); extern int32 txcs_rd (void); extern void iccs_wr (int32 dat); extern void nicr_wr (int32 dat); extern void todr_wr (int32 dat); extern void rxcs_wr (int32 dat); extern void txcs_wr (int32 dat); extern void txdb_wr (int32 dat); extern void init_mbus_tab (void); extern void init_ubus_tab (void); extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp); extern t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); /* SBI data structures sbi_dev SBI device descriptor sbi_unit SBI unit sbi_reg SBI register list */ UNIT sbi_unit = { UDATA (NULL, 0, 0) }; REG sbi_reg[] = { { HRDATA (NREQ14, nexus_req[0], 16) }, { HRDATA (NREQ15, nexus_req[1], 16) }, { HRDATA (NREQ16, nexus_req[2], 16) }, { HRDATA (NREQ17, nexus_req[3], 16) }, { HRDATA (WCSA, wcs_addr, 16) }, { HRDATA (WCSD, wcs_data, 32) }, { HRDATA (MBRK, wcs_mbrk, 13) }, { HRDATA (SBIFS, sbi_fs, 32) }, { HRDATA (SBISC, sbi_sc, 32) }, { HRDATA (SBIMT, sbi_mt, 32) }, { HRDATA (SBIER, sbi_er, 32) }, { HRDATA (SBITMO, sbi_tmo, 32) }, { NULL } }; DEVICE sbi_dev = { "SBI", &sbi_unit, sbi_reg, NULL, 1, 16, 16, 1, 16, 8, NULL, NULL, &sbi_reset, NULL, NULL, NULL, NULL, 0 }; /* Special boot command, overrides regular boot */ CTAB vax780_cmd[] = { { "BOOT", &vax780_boot, RU_BOOT, "bo{ot} {/R5:flg} boot device\n" }, { "FLOAD", &vax780_fload, 0, "fl{oad} {} load file from console floppy\n" }, { NULL } }; /* The VAX 11/780 has three sources of interrupts - internal device interrupts (CPU, console, clock) - nexus interupts (e.g., memory controller, MBA, UBA) - external device interrupts (Unibus) Internal devices vector to fixed SCB locations. Nexus interrupts vector to an SCB location based on this formula: SCB_NEXUS + ((IPL - 0x14) * 0x40) + (TR# * 0x4) External device interrupts do not vector directly. Instead, the interrupt handler for a given UBA IPL reads a vector register that contains the Unibus vector for that IPL. /* Find highest priority vectorable interrupt */ int32 eval_int (void) { int32 ipl = PSL_GETIPL (PSL); int32 i, t; static const int32 sw_int_mask[IPL_SMAX] = { 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */ 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */ 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */ 0xE000, 0xC000, 0x8000 /* C - E */ }; if (hlt_pin) /* hlt pin int */ return IPL_HLTPIN; if ((ipl < IPL_MEMERR) && mem_err) /* mem err int */ return IPL_MEMERR; if ((ipl < IPL_CRDERR) && crd_err) /* crd err int */ return IPL_CRDERR; if ((ipl < IPL_CLKINT) && tmr_int) /* clock int */ return IPL_CLKINT; uba_eval_int (); /* update UBA */ for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ if (i <= ipl) /* at ipl? no int */ return 0; if (nexus_req[i - IPL_HMIN]) /* req != 0? int */ return i; } if ((ipl < IPL_TTINT) && (tti_int || tto_int)) /* console int */ return IPL_TTINT; if (ipl >= IPL_SMAX) /* ipl >= sw max? */ return 0; if ((t = SISR & sw_int_mask[ipl]) == 0) return 0; /* eligible req */ for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */ if ((t >> i) & 1) /* req != 0? int */ return i; } return 0; } /* Return vector for highest priority hardware interrupt at IPL lvl */ int32 get_vector (int32 lvl) { int32 i, l; if (lvl == IPL_MEMERR) { /* mem error? */ mem_err = 0; return SCB_MEMERR; } if (lvl == IPL_CRDERR) { /* CRD error? */ crd_err = 0; return SCB_CRDERR; } if (lvl == IPL_CLKINT) { /* clock? */ tmr_int = 0; /* clear req */ return SCB_INTTIM; /* return vector */ } if (lvl > IPL_HMAX) { /* error req lvl? */ ABORT (STOP_UIPL); /* unknown intr */ } if ((lvl <= IPL_HMAX) && (lvl >= IPL_HMIN)) { /* nexus? */ l = lvl - IPL_HMIN; for (i = 0; nexus_req[l] && (i < NEXUS_NUM); i++) { if ((nexus_req[l] >> i) & 1) { nexus_req[l] = nexus_req[l] & ~(1u << i); return SCB_NEXUS + (l << 6) + (i << 2); /* return vector */ } } } if (lvl == IPL_TTINT) { /* console? */ if (tti_int) { /* input? */ tti_int = 0; /* clear req */ return SCB_TTI; /* return vector */ } if (tto_int) { /* output? */ tto_int = 0; /* clear req */ return SCB_TTO; /* return vector */ } } return 0; } /* Read 780-specific IPR's */ int32 ReadIPR (int32 rg) { int32 val; switch (rg) { case MT_ICCS: /* ICCS */ val = iccs_rd (); break; case MT_NICR: /* NICR */ val = nicr_rd (); break; case MT_ICR: /* ICR */ val = icr_rd (FALSE); break; case MT_TODR: /* TODR */ val = todr_rd (); break; case MT_ACCS: /* ACCS (not impl) */ val = 0; break; case MT_WCSA: /* WCSA */ val = wcs_addr & WCSA_RW; break; case MT_WCSD: /* WCSD */ val = WCSD_RD_VAL; break; case MT_RXCS: /* RXCS */ val = rxcs_rd (); break; case MT_RXDB: /* RXDB */ val = rxdb_rd (); break; case MT_TXCS: /* TXCS */ val = txcs_rd (); break; case MT_SBIFS: /* SBIFS */ val = sbi_fs & SBIFS_RD; break; case MT_SBIS: /* SBIS */ val = 0; break; case MT_SBISC: /* SBISC */ val = sbi_sc & SBISC_RD; break; case MT_SBIMT: /* SBIMT */ val = sbi_mt & SBIMT_RD; break; case MT_SBIER: /* SBIER */ val = sbi_er & SBIER_RD; break; case MT_SBITA: /* SBITA */ val = sbi_tmo; break; case MT_MBRK: /* MBRK */ val = wcs_mbrk & MBRK_RW; break; case MT_SID: /* SID */ val = VAX780_SID | VAX780_ECO | VAX780_PLANT | VAX780_SN; break; default: RSVD_OPND_FAULT; } return val; } /* Write 780-specific IPR's */ void WriteIPR (int32 rg, int32 val) { switch (rg) { case MT_ICCS: /* ICCS */ iccs_wr (val); break; case MT_NICR: /* NICR */ nicr_wr (val); break; case MT_TODR: /* TODR */ todr_wr (val); break; case MT_ACCS: /* ACCS (not impl) */ break; case MT_WCSA: /* WCSA */ wcs_addr = val & WCSA_RW; break; case MT_WCSD: /* WCSD */ wcs_data = val & WCSD_WR; wcs_addr = (wcs_addr & ~WCSA_CTR) | ((wcs_addr + WCSA_CTR_INC) & WCSA_CTR); if ((wcs_addr & WCSA_CTR) == WCSA_CTR_MAX) wcs_addr = (wcs_addr & ~WCSA_ADDR) | ((wcs_addr + 1) & WCSA_ADDR); break; case MT_RXCS: /* RXCS */ rxcs_wr (val); break; case MT_TXCS: /* TXCS */ txcs_wr (val); break; case MT_TXDB: /* TXDB */ txdb_wr (val); break; case MT_SBIFS: /* SBIFS */ sbi_fs = (sbi_fs & ~SBIFS_WR) | (val & SBIFS_WR); sbi_fs = sbi_fs & ~(val & SBIFS_W1C); break; case MT_SBISC: /* SBISC */ sbi_sc = (sbi_sc & ~(SBISC_LOCK|SBISC_WR)) | (val & SBISC_WR); break; case MT_SBIMT: /* SBIMT */ sbi_mt = (sbi_mt & ~SBIMT_WR) | (val & SBIMT_WR); break; case MT_SBIER: /* SBIER */ sbi_er = (sbi_er & ~SBIER_WR) | (val & SBIER_WR); sbi_er = sbi_er & ~(val & SBIER_W1C); if (val & SBIER_TMO) sbi_er = sbi_er & ~SBIER_TMOW1C; if (val & SBIER_IBTMO) sbi_er = sbi_er & ~SBIER_IBTW1C; if ((sbi_er & SBIER_CRDIE) && (sbi_er & SBIER_CRD)) crd_err = 1; else crd_err = 0; break; case MT_SBIQC: /* SBIQC */ if (val & SBIQC_MBZ) { RSVD_OPND_FAULT; } WriteLP (val, 0); WriteLP (val + 4, 0); break; case MT_MBRK: /* MBRK */ wcs_mbrk = val & MBRK_RW; break; default: RSVD_OPND_FAULT; } return; } /* ReadReg - read register space Inputs: pa = physical address lnt = length (BWLQ) Output: longword of data */ int32 ReadReg (int32 pa, int32 lnt) { int32 nexus, val; if (ADDR_IS_REG (pa)) { /* reg space? */ nexus = NEXUS_GETNEX (pa); /* get nexus */ if (nexusR[nexus] && /* valid? */ (nexusR[nexus] (&val, pa, lnt) == SCPE_OK)) { SET_IRQL; return val; } } sbi_set_tmo (pa); /* timeout */ MACH_CHECK (MCHK_RD_F); /* machine check */ return 0; } /* WriteReg - write register space Inputs: pa = physical address val = data to write, right justified in 32b longword lnt = length (BWLQ) Outputs: none */ void WriteReg (int32 pa, int32 val, int32 lnt) { int32 nexus; if (ADDR_IS_REG (pa)) { /* reg space? */ nexus = NEXUS_GETNEX (pa); /* get nexus */ if (nexusW[nexus] && /* valid? */ (nexusW[nexus] (val, pa, lnt) == SCPE_OK)) { SET_IRQL; return; } } sbi_set_tmo (pa); /* timeout */ mem_err = 1; /* interrupt */ eval_int (); return; } /* Set SBI timeout - machine checks only on reads */ void sbi_set_tmo (int32 pa) { if ((sbi_er & SBIER_TMO) == 0) { /* not yet set? */ sbi_tmo = pa >> 2; /* save addr */ if (mchk_ref == REF_V) /* virt? add mode */ sbi_tmo |= SBITMO_VIRT | (PSL_GETCUR (PSL) << SBITMO_V_MODE); sbi_er |= SBIER_TMO; /* set tmo flag */ } else sbi_er |= SBIER_MULT; /* yes, multiple */ return; } /* Set SBI error confirmation - always machine checks */ void sbi_set_errcnf (void) { if (sbi_er & SBIER_CNF) sbi_er |= SBIER_MULT; else sbi_er |= SBIER_CNF; MACH_CHECK (MCHK_RD_F); return; } /* Machine check Error status word format <2:0> = ASTLVL <3> = PME <6:4> = arith trap code Rest will be zero */ int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta) { int32 acc, err; err = (GET_TRAP (trpirq) << 4) | (pme << 3) | ASTLVL; /* error word */ cc = intexc (SCB_MCHK, cc, 0, IE_SVE); /* take exception */ acc = ACC_MASK (KERN); /* in kernel mode */ in_ie = 1; SP = SP - 44; /* push 11 words */ Write (SP, 40, L_LONG, WA); /* # bytes */ Write (SP + 4, p1, L_LONG, WA); /* mcheck type */ Write (SP + 8, err, L_LONG, WA); /* CPU error status */ Write (SP + 12, 0, L_LONG, WA); /* uPC */ Write (SP + 16, mchk_va, L_LONG, WA); /* VA */ Write (SP + 20, 0, L_LONG, WA); /* D register */ Write (SP + 24, mapen, L_LONG, WA); /* TB status 1 */ Write (SP + 28, 0, L_LONG, WA); /* TB status 2 */ Write (SP + 32, sbi_tmo, L_LONG, WA); /* SBI timeout addr */ Write (SP + 36, 0, L_LONG, WA); /* cache status */ Write (SP + 40, sbi_er, L_LONG, WA); /* SBI error */ in_ie = 0; sbi_er = sbi_er & ~SBIER_TMOW1C; /* clr SBIER etc */ return cc; } /* Console entry */ int32 con_halt (int32 code, int32 cc) { ABORT (STOP_HALT); return cc; } /* Special boot command - linked into SCP by initial reset Syntax: BOOT {/R5:val} Sets up R0-R5, calls SCP boot processor with effective BOOT CPU */ t_stat vax780_boot (int32 flag, char *ptr) { char gbuf[CBUFSIZE]; char *slptr, *regptr; int32 i, r5v, unitno; DEVICE *dptr; UNIT *uptr; DIB *dibp; t_stat r; regptr = get_glyph (ptr, gbuf, 0); /* get glyph */ if (slptr = strchr (gbuf, '/')) { /* found slash? */ regptr = strchr (ptr, '/'); /* locate orig */ *slptr = 0; /* zero in string */ } dptr = find_unit (gbuf, &uptr); /* find device */ if ((dptr == NULL) || (uptr == NULL)) return SCPE_ARG; dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp == NULL) return SCPE_ARG; unitno = (int32) (uptr - dptr->units); r5v = 0; if ((strncmp (regptr, "/R5:", 4) == 0) || (strncmp (regptr, "/R5=", 4) == 0) || (strncmp (regptr, "/r5:", 4) == 0) || (strncmp (regptr, "/r5=", 4) == 0)) { r5v = (int32) get_uint (regptr + 4, 16, LMASK, &r); if (r != SCPE_OK) return r; } else if (*regptr != 0) return SCPE_ARG; for (i = 0; boot_tab[i].name != NULL; i++) { if (strcmp (dptr->name, boot_tab[i].name) == 0) { R[0] = boot_tab[i].code; if (dptr->flags & DEV_MBUS) { R[1] = dibp->ba + TR_MBA0; R[2] = unitno; } else { R[1] = TR_UBA; R[2] = boot_tab[i].let | (dibp->ba & UBADDRMASK); } R[3] = unitno; R[4] = 0; R[5] = r5v; return run_cmd (flag, "CPU"); } } return SCPE_NOFNC; } /* Bootstrap - finish up bootstrap process */ t_stat cpu_boot (int32 unitno, DEVICE *dptr) { t_stat r; printf ("Loading boot code from vmb.exe\n"); if (sim_log) fprintf (sim_log, "Loading boot code from vmb.exe\n"); r = load_cmd (0, "-O vmb.exe 200"); if (r != SCPE_OK) return r; SP = PC = 512; return SCPE_OK; } /* SBI reset */ t_stat sbi_reset (DEVICE *dptr) { wcs_addr = 0; wcs_data = 0; wcs_mbrk = 0; sbi_fs = 0; sbi_sc = 0; sbi_mt = 0; sbi_er = 0; sbi_tmo = 0; sim_vm_cmd = vax780_cmd; return SCPE_OK; } /* Show nexus */ t_stat show_nexus (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "nexus=%d", val); return SCPE_OK; } /* Init nexus tables */ void init_nexus_tab (void) { uint32 i; for (i = 0; i < NEXUS_NUM; i++) { nexusR[i] = NULL; nexusW[i] = NULL; } return; } /* Build nexus tables Inputs: dptr = pointer to device dibp = pointer to DIB Outputs: status */ t_stat build_nexus_tab (DEVICE *dptr, DIB *dibp) { uint32 idx; if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; idx = dibp->ba; if (idx >= NEXUS_NUM) return SCPE_IERR; if ((nexusR[idx] && dibp->rd && /* conflict? */ (nexusR[idx] != dibp->rd)) || (nexusW[idx] && dibp->wr && (nexusW[idx] != dibp->wr))) { printf ("Nexus %s conflict at %d\n", sim_dname (dptr), dibp->ba); if (sim_log) fprintf (sim_log, "Nexus %s conflict at %d\n", sim_dname (dptr), dibp->ba); return SCPE_STOP; } if (dibp->rd) /* set rd dispatch */ nexusR[idx] = dibp->rd; if (dibp->wr) /* set wr dispatch */ nexusW[idx] = dibp->wr; return SCPE_OK; } /* Build dib_tab from device list */ t_stat build_dib_tab (void) { uint32 i; DEVICE *dptr; DIB *dibp; t_stat r; init_nexus_tab (); init_ubus_tab (); init_mbus_tab (); for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ if (dptr->flags & DEV_NEXUS) { /* Nexus? */ if (r = build_nexus_tab (dptr, dibp)) /* add to dispatch table */ return r; } else if (dptr->flags & DEV_MBUS) { /* Massbus? */ if (r = build_mbus_tab (dptr, dibp)) return r; } else { /* no, Unibus device */ if (r = build_ubus_tab (dptr, dibp)) /* add to dispatch tab */ return r; } /* end else */ } /* end if enabled */ } /* end for */ return SCPE_OK; } simh-3.8.1/VAX/vaxmod_defs.h0000644000175000017500000005176311111615352013757 0ustar vlmvlm/* vaxmod_defs.h: VAX model-specific definitions file Copyright (c) 1998-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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 29-Apr-07 RMS Separated checks for PxBR and SBR 17-May-06 RMS Added CR11/CD11 support 10-May-06 RMS Added NOP'd reserved operand checking macros 05-Oct-05 RMS Added XU definitions for autoconfigure 15-Jun-05 RMS Added QDSS support 12-Sep-04 RMS Removed map_address prototype 16-Jun-04 RMS Added DHQ11 support 21-Mar-04 RMS Added RXV21 support 25-Jan-04 RMS Removed local debug logging support RMS,MP Added "KA655X" support 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility 22-Dec-02 RMS Added BDR halt enable definition 11-Nov-02 RMS Added log bits for XQ 10-Oct-02 RMS Added DEQNA/DELQA, multiple RQ, autoconfigure support 29-Sep-02 RMS Revamped bus support macros 06-Sep-02 RMS Added TMSCP support 14-Jul-02 RMS Added additional console halt codes 28-Apr-02 RMS Fixed DZV vector base and number of lines This file covers the KA65x ("Mayfair") series of CVAX-based Qbus systems. The simulator defines an extended physical memory variant of the KA655, called the KA655X. It has a maximum memory size of 512MB instead of 64MB. System memory map 0000 0000 - 03FF FFFF main memory (KA655) 0400 0000 - 0FFF FFFF reserved (KA655), main memory (KA655X) 1000 0000 - 13FF FFFF cache diagnostic space (KA655), main memory (KA655X) 1400 0000 - 1FFF FFFF reserved (KA655), main memory (KA655X) 2000 0000 - 2000 1FFF Qbus I/O page 2000 2000 - 2003 FFFF reserved 2004 0000 - 2005 FFFF ROM space, halt protected 2006 0000 - 2007 FFFF ROM space, halt unprotected 2008 0000 - 201F FFFF Local register space 2020 0000 - 2FFF FFFF reserved 3000 0000 - 303F FFFF Qbus memory space 3400 0000 - 3FFF FFFF reserved */ #ifdef FULL_VAX /* subset VAX */ #undef FULL_VAX #endif #ifndef _VAXMOD_DEFS_H_ #define _VAXMOD_DEFS_H_ 1 /* Microcode constructs */ #define CVAX_SID (10 << 24) /* system ID */ #define CVAX_UREV 6 /* ucode revision */ #define CON_HLTPIN 0x0200 /* external CPU halt */ #define CON_PWRUP 0x0300 /* powerup code */ #define CON_HLTINS 0x0600 /* HALT instruction */ #define CON_BADPSL 0x4000 /* invalid PSL flag */ #define CON_MAPON 0x8000 /* mapping on flag */ #define MCHK_TBM_P0 0x05 /* PPTE in P0 */ #define MCHK_TBM_P1 0x06 /* PPTE in P1 */ #define MCHK_M0_P0 0x07 /* PPTE in P0 */ #define MCHK_M0_P1 0x08 /* PPTE in P1 */ #define MCHK_INTIPL 0x09 /* invalid ireq */ #define MCHK_READ 0x80 /* read check */ #define MCHK_WRITE 0x82 /* write check */ /* Machine specific IPRs */ #define MT_CADR 37 #define MT_MSER 39 #define MT_CONPC 42 #define MT_CONPSL 43 #define MT_IORESET 55 /* Memory system error register */ #define MSER_HM 0x80 /* hit/miss */ #define MSER_CPE 0x40 /* CDAL par err */ #define MSER_CPM 0x20 /* CDAL mchk */ /* Cache disable register */ #define CADR_RW 0xF3 #define CADR_MBO 0x0C /* Memory */ #define MAXMEMWIDTH 26 /* max mem, std KA655 */ #define MAXMEMSIZE (1 << MAXMEMWIDTH) /* max mem size */ #define MAXMEMWIDTH_X 29 /* max mem, KA655X */ #define MAXMEMSIZE_X (1 << MAXMEMWIDTH_X) #define INITMEMSIZE (1 << 24) /* initial memory size */ #define MEMSIZE (cpu_unit.capac) #define ADDR_IS_MEM(x) (((uint32) (x)) < MEMSIZE) /* Cache diagnostic space */ #define CDAAWIDTH 16 /* cache dat addr width */ #define CDASIZE (1u << CDAAWIDTH) /* cache dat length */ #define CDAMASK (CDASIZE - 1) /* cache dat mask */ #define CTGAWIDTH 10 /* cache tag addr width */ #define CTGSIZE (1u << CTGAWIDTH) /* cache tag length */ #define CTGMASK (CTGSIZE - 1) /* cache tag mask */ #define CDGSIZE (CDASIZE * CTGSIZE) /* diag addr length */ #define CDGBASE 0x10000000 /* diag addr base */ #define CDG_GETROW(x) (((x) & CDAMASK) >> 2) #define CDG_GETTAG(x) (((x) >> CDAAWIDTH) & CTGMASK) #define CTG_V (1u << (CTGAWIDTH + 0)) /* tag valid */ #define CTG_WP (1u << (CTGAWIDTH + 1)) /* wrong parity */ #define ADDR_IS_CDG(x) ((((uint32) (x)) >= CDGBASE) && \ (((uint32) (x)) < (CDGBASE + CDGSIZE))) /* Qbus I/O registers */ #define IOPAGEAWIDTH 13 /* IO addr width */ #define IOPAGESIZE (1u << IOPAGEAWIDTH) /* IO page length */ #define IOPAGEMASK (IOPAGESIZE - 1) /* IO addr mask */ #define IOPAGEBASE 0x20000000 /* IO page base */ #define ADDR_IS_IO(x) ((((uint32) (x)) >= IOPAGEBASE) && \ (((uint32) (x)) < (IOPAGEBASE + IOPAGESIZE))) /* Read only memory - appears twice */ #define ROMAWIDTH 17 /* ROM addr width */ #define ROMSIZE (1u << ROMAWIDTH) /* ROM length */ #define ROMAMASK (ROMSIZE - 1) /* ROM addr mask */ #define ROMBASE 0x20040000 /* ROM base */ #define ADDR_IS_ROM(x) ((((uint32) (x)) >= ROMBASE) && \ (((uint32) (x)) < (ROMBASE + ROMSIZE + ROMSIZE))) /* Local register space */ #define REGAWIDTH 19 /* REG addr width */ #define REGSIZE (1u << REGAWIDTH) /* REG length */ #define REGBASE 0x20080000 /* REG addr base */ /* KA655 board registers */ #define KAAWIDTH 3 /* KA reg width */ #define KASIZE (1u << KAAWIDTH) /* KA reg length */ #define KABASE (REGBASE + 0x4000) /* KA650 addr base */ /* CQBIC registers */ #define CQBICSIZE (5 << 2) /* 5 registers */ #define CQBICBASE (REGBASE) /* CQBIC addr base */ #define CQMAPASIZE 15 /* map addr width */ #define CQMAPSIZE (1u << CQMAPASIZE) /* map length */ #define CQMAPAMASK (CQMAPSIZE - 1) /* map addr mask */ #define CQMAPBASE (REGBASE + 0x8000) /* map addr base */ #define CQIPCSIZE 2 /* 2 bytes only */ #define CQIPCBASE (REGBASE + 0x1F40) /* ipc reg addr */ /* CMCTL registers */ /* #define CMCTLSIZE (18 << 2) /* 18 registers */ #define CMCTLSIZE (19 << 2) /* KA655X extra reg */ #define CMCTLBASE (REGBASE + 0x100) /* CMCTL addr base */ /* SSC registers */ #define SSCSIZE 0x150 /* SSC size */ #define SSCBASE 0x20140000 /* SSC base */ /* Non-volatile RAM - 1KB long */ #define NVRAWIDTH 10 /* NVR addr width */ #define NVRSIZE (1u << NVRAWIDTH) /* NVR length */ #define NVRAMASK (NVRSIZE - 1) /* NVR addr mask */ #define NVRBASE 0x20140400 /* NVR base */ #define ADDR_IS_NVR(x) ((((uint32) (x)) >= NVRBASE) && \ (((uint32) (x)) < (NVRBASE + NVRSIZE))) /* CQBIC Qbus memory space (seen from CVAX) */ #define CQMAWIDTH 22 /* Qmem addr width */ #define CQMSIZE (1u << CQMAWIDTH) /* Qmem length */ #define CQMAMASK (CQMSIZE - 1) /* Qmem addr mask */ #define CQMBASE 0x30000000 /* Qmem base */ /* Machine specific reserved operand tests (all NOPs) */ #define ML_PA_TEST(r) #define ML_LR_TEST(r) #define ML_SBR_TEST(r) #define ML_PXBR_TEST(r) #define LP_AST_TEST(r) #define LP_MBZ84_TEST(r) #define LP_MBZ92_TEST(r) /* Qbus I/O modes */ #define READ 0 /* PDP-11 compatibility */ #define WRITE (L_WORD) #define WRITEB (L_BYTE) /* Common CSI flags */ #define CSR_V_GO 0 /* go */ #define CSR_V_IE 6 /* interrupt enable */ #define CSR_V_DONE 7 /* done */ #define CSR_V_BUSY 11 /* busy */ #define CSR_V_ERR 15 /* error */ #define CSR_GO (1u << CSR_V_GO) #define CSR_IE (1u << CSR_V_IE) #define CSR_DONE (1u << CSR_V_DONE) #define CSR_BUSY (1u << CSR_V_BUSY) #define CSR_ERR (1u << CSR_V_ERR) /* Timers */ #define TMR_CLK 0 /* 100Hz clock */ /* I/O system definitions */ #define DZ_MUXES 4 /* max # of DZV muxes */ #define DZ_LINES 4 /* lines per DZV mux */ #define VH_MUXES 4 /* max # of DHQ muxes */ #define DLX_LINES 16 /* max # of KL11/DL11's */ #define DCX_LINES 16 /* max # of DC11's */ #define MT_MAXFR (1 << 16) /* magtape max rec */ #define AUTO_LNT 34 /* autoconfig ranks */ #define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ #define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ #define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus, mem <= 256KB */ #define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ #define DEV_UBUS (1u << DEV_V_UBUS) #define DEV_QBUS (1u << DEV_V_QBUS) #define DEV_Q18 (1u << DEV_V_Q18) #define DEV_FLTA (1u << DEV_V_FLTA) #define UNIBUS FALSE /* 22b only */ #define DEV_RDX 16 /* default device radix */ /* Device information block */ #define VEC_DEVMAX 4 /* max device vec */ typedef struct { uint32 ba; /* base addr */ uint32 lnt; /* length */ t_stat (*rd)(int32 *dat, int32 ad, int32 md); t_stat (*wr)(int32 dat, int32 ad, int32 md); int32 vnum; /* vectors: number */ int32 vloc; /* locator */ int32 vec; /* value */ int32 (*ack[VEC_DEVMAX])(void); /* ack routine */ } DIB; /* I/O page layout - RQB,RQC,RQD float based on number of DZ's */ #define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ #define IOLN_DZ 010 #define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) #define IOLN_RQB 004 #define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) #define IOLN_RQC 004 #define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) #define IOLN_RQD 004 #define IOBA_VH (IOPAGEBASE + 000440) /* DHQ11 */ #define IOLN_VH 020 #define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */ #define IOLN_RQ 004 #define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ #define IOLN_TS 004 #define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ #define IOLN_RL 012 #define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ #define IOLN_XQ 020 #define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ #define IOLN_XQB 020 #define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ #define IOLN_TQ 004 #define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ #define IOLN_XU 010 #define IOBA_RP (IOPAGEBASE + 016700) /* RP/RM */ #define IOLN_RP 054 #define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ #define IOLN_CR 010 #define IOBA_RX (IOPAGEBASE + 017170) /* RXV11 */ #define IOLN_RX 004 #define IOBA_RY (IOPAGEBASE + 017170) /* RXV21 */ #define IOLN_RY 004 #define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ #define IOLN_QDSS 002 #define IOBA_DBL (IOPAGEBASE + 017500) /* doorbell */ #define IOLN_DBL 002 #define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ #define IOLN_LPT 004 #define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ #define IOLN_PTR 004 #define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ #define IOLN_PTP 004 /* The KA65x maintains 4 separate hardware IPL levels, IPL 17 to IPL 14 Within each IPL, priority is right to left */ /* IPL 17 */ /* IPL 16 */ #define INT_V_CLK 0 /* clock */ /* IPL 15 */ #define INT_V_RQ 0 /* RQDX3 */ #define INT_V_RL 1 /* RLV12/RL02 */ #define INT_V_DZRX 2 /* DZ11 */ #define INT_V_DZTX 3 #define INT_V_RP 4 /* RP,RM drives */ #define INT_V_TS 5 /* TS11/TSV05 */ #define INT_V_TQ 6 /* TMSCP */ #define INT_V_XQ 7 /* DEQNA/DELQA */ #define INT_V_RY 8 /* RXV21 */ /* IPL 14 */ #define INT_V_TTI 0 /* console */ #define INT_V_TTO 1 #define INT_V_PTR 2 /* PC11 */ #define INT_V_PTP 3 #define INT_V_LPT 4 /* LP11 */ #define INT_V_CSI 5 /* SSC cons UART */ #define INT_V_CSO 6 #define INT_V_TMR0 7 /* SSC timers */ #define INT_V_TMR1 8 #define INT_V_VHRX 9 /* DHQ11 */ #define INT_V_VHTX 10 #define INT_V_QDSS 11 /* QDSS */ #define INT_V_CR 12 #define INT_CLK (1u << INT_V_CLK) #define INT_RQ (1u << INT_V_RQ) #define INT_RL (1u << INT_V_RL) #define INT_DZRX (1u << INT_V_DZRX) #define INT_DZTX (1u << INT_V_DZTX) #define INT_RP (1u << INT_V_RP) #define INT_TS (1u << INT_V_TS) #define INT_TQ (1u << INT_V_TQ) #define INT_XQ (1u << INT_V_XQ) #define INT_RY (1u << INT_V_RY) #define INT_TTI (1u << INT_V_TTI) #define INT_TTO (1u << INT_V_TTO) #define INT_PTR (1u << INT_V_PTR) #define INT_PTP (1u << INT_V_PTP) #define INT_LPT (1u << INT_V_LPT) #define INT_CSI (1u << INT_V_CSI) #define INT_CSO (1u << INT_V_CSO) #define INT_TMR0 (1u << INT_V_TMR0) #define INT_TMR1 (1u << INT_V_TMR1) #define INT_VHRX (1u << INT_V_VHRX) #define INT_VHTX (1u << INT_V_VHTX) #define INT_QDSS (1u << INT_V_QDSS) #define INT_CR (1u << INT_V_CR) #define IPL_CLK (0x16 - IPL_HMIN) /* relative IPL */ #define IPL_RQ (0x15 - IPL_HMIN) #define IPL_RL (0x15 - IPL_HMIN) #define IPL_DZRX (0x15 - IPL_HMIN) #define IPL_DZTX (0x15 - IPL_HMIN) #define IPL_RP (0x15 - IPL_HMIN) #define IPL_TS (0x15 - IPL_HMIN) #define IPL_TQ (0x15 - IPL_HMIN) #define IPL_XQ (0x15 - IPL_HMIN) #define IPL_RY (0x15 - IPL_HMIN) #define IPL_TTI (0x14 - IPL_HMIN) #define IPL_TTO (0x14 - IPL_HMIN) #define IPL_PTR (0x14 - IPL_HMIN) #define IPL_PTP (0x14 - IPL_HMIN) #define IPL_LPT (0x14 - IPL_HMIN) #define IPL_CSI (0x14 - IPL_HMIN) #define IPL_CSO (0x14 - IPL_HMIN) #define IPL_TMR0 (0x14 - IPL_HMIN) #define IPL_TMR1 (0x14 - IPL_HMIN) #define IPL_VHRX (0x14 - IPL_HMIN) #define IPL_VHTX (0x14 - IPL_HMIN) #define IPL_QDSS (0x14 - IPL_HMIN) #define IPL_CR (0x14 - IPL_HMIN) #define IPL_HMAX 0x17 /* highest hwre level */ #define IPL_HMIN 0x14 /* lowest hwre level */ #define IPL_HLVL (IPL_HMAX - IPL_HMIN + 1) /* # hardware levels */ #define IPL_SMAX 0xF /* highest swre level */ /* Device vectors */ #define VEC_Q 0x200 /* Qbus vector offset */ #define VEC_PTR (VEC_Q + 0070) #define VEC_PTP (VEC_Q + 0074) #define VEC_XQ (VEC_Q + 0120) #define VEC_XU (VEC_Q + 0120) #define VEC_RQ (VEC_Q + 0154) #define VEC_RL (VEC_Q + 0160) #define VEC_LPT (VEC_Q + 0200) #define VEC_TS (VEC_Q + 0224) #define VEC_CR (VEC_Q + 0230) #define VEC_RP (VEC_Q + 0254) #define VEC_TQ (VEC_Q + 0260) #define VEC_RX (VEC_Q + 0264) #define VEC_RY (VEC_Q + 0264) #define VEC_DZRX (VEC_Q + 0300) #define VEC_DZTX (VEC_Q + 0304) #define VEC_VHRX (VEC_Q + 0310) #define VEC_VHTX (VEC_Q + 0314) /* Interrupt macros */ #define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) #define IREQ(dv) int_req[IPL_##dv] #define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) #define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ /* Logging */ #define LOG_CPU_I 0x1 /* intexc */ #define LOG_CPU_R 0x2 /* REI */ #define LOG_CPU_P 0x4 /* context */ /* Function prototypes for virtual memory interface */ int32 Read (uint32 va, int32 lnt, int32 acc); void Write (uint32 va, int32 val, int32 lnt, int32 acc); /* Function prototypes for physical memory interface (inlined) */ SIM_INLINE int32 ReadB (uint32 pa); SIM_INLINE int32 ReadW (uint32 pa); SIM_INLINE int32 ReadL (uint32 pa); SIM_INLINE int32 ReadLP (uint32 pa); SIM_INLINE void WriteB (uint32 pa, int32 val); SIM_INLINE void WriteW (uint32 pa, int32 val); SIM_INLINE void WriteL (uint32 pa, int32 val); void WriteLP (uint32 pa, int32 val); /* Function prototypes for I/O */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); int32 clk_cosched (int32 wait); #include "pdp11_io_lib.h" #endif simh-3.8.1/VAX/vax_octa.c0000644000175000017500000012651511112313610013246 0ustar vlmvlm/* vax_octa.c - VAX octaword and h_floating instructions Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module simulates the VAX h_floating instruction set. 28-May-08 RMS Inlined physical memory routines 10-May-06 RMS Fixed bug in reported VA on faulting cross-page write 03-May-06 RMS Fixed MNEGH to test negated sign, clear C Fixed carry propagation in qp_inc, qp_neg, qp_add Fixed pack routines to test for zero via fraction Fixed ACBH to set cc's on result Fixed POLYH to set R3 correctly Fixed POLYH to not exit prematurely if arg = 0 Fixed POLYH to mask mul reslt to 127b Fixed fp add routine to test for zero via fraction to support "denormal" argument from POLYH Fixed EMODH to concatenate 15b of 16b extension (all reported by Tim Stark) 15-Jul-04 RMS Cloned from 32b VAX floating point implementation */ #include "vax_defs.h" #if defined (FULL_VAX) extern int32 R[16]; extern int32 PSL; extern int32 trpirq; extern int32 p1; extern jmp_buf save_env; extern int32 Test (uint32 va, int32 acc, int32 *status); #define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) typedef struct { uint32 f0; /* low */ uint32 f1; uint32 f2; uint32 f3; /* high */ } UQP; typedef struct { int32 sign; int32 exp; UQP frac; } UFPH; #define UH_NM_H 0x80000000 /* normalized */ #define UH_FRND 0x00000080 /* F round */ #define UH_DRND 0x00000080 /* D round */ #define UH_GRND 0x00000400 /* G round */ #define UH_HRND 0x00004000 /* H round */ #define UH_V_NM 127 int32 op_tsth (int32 val); int32 op_cmph (int32 *hf1, int32 *hf2); int32 op_cvtih (int32 val, int32 *hf); int32 op_cvthi (int32 *hf, int32 *flg, int32 opc); int32 op_cvtfdh (int32 vl, int32 vh, int32 *hf); int32 op_cvtgh (int32 vl, int32 vh, int32 *hf); int32 op_cvthfd (int32 *hf, int32 *vh); int32 op_cvthg (int32 *hf, int32 *vh); int32 op_addh (int32 *opnd, int32 *hf, t_bool sub); int32 op_mulh (int32 *opnd, int32 *hf); int32 op_divh (int32 *opnd, int32 *hf); int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg); void op_polyh (int32 *opnd, int32 acc); void h_write_b (int32 spec, int32 va, int32 val, int32 acc); void h_write_w (int32 spec, int32 va, int32 val, int32 acc); void h_write_l (int32 spec, int32 va, int32 val, int32 acc); void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc); void h_write_o (int32 spec, int32 va, int32 *val, int32 acc); void vax_hadd (UFPH *a, UFPH *b); void vax_hmul (UFPH *a, UFPH *b, uint32 mlo); void vax_hmod (UFPH *a, int32 *intgr, int32 *flg); void vax_hdiv (UFPH *a, UFPH *b); uint32 qp_add (UQP *a, UQP *b); uint32 qp_sub (UQP *a, UQP *b); void qp_inc (UQP *a); void qp_lsh (UQP *a, uint32 sc); void qp_rsh (UQP *a, uint32 sc); void qp_rsh_s (UQP *a, uint32 sc, uint32 neg); void qp_neg (UQP *a); int32 qp_cmp (UQP *a, UQP *b); void h_unpackfd (int32 hi, int32 lo, UFPH *a); void h_unpackg (int32 hi, int32 lo, UFPH *a); void h_unpackh (int32 *hflt, UFPH *a); void h_normh (UFPH *a); int32 h_rpackfd (UFPH *a, int32 *rl); int32 h_rpackg (UFPH *a, int32 *rl); int32 h_rpackh (UFPH *a, int32 *hflt); static int32 z_octa[4] = { 0, 0, 0, 0 }; /* Octaword instructions */ int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) { int32 r, rh, temp, flg; int32 r_octa[4]; switch (opc) { /* PUSHAO opnd[0] = src.ao */ case PUSHAO: Write (SP - 4, opnd[0], L_LONG, WA); /* push operand */ SP = SP - 4; /* decr stack ptr */ CC_IIZP_L (opnd[0]); /* set cc's */ break; /* MOVAO opnd[0] = src.ro opnd[1:2] = dst.wl spec = last specifier va = address if last specifier is memory */ case MOVAO: h_write_l (spec, va, opnd[0], acc); /* write operand */ CC_IIZP_L (opnd[0]); /* set cc's */ break; /* CLRO opnd[0:1] = dst.wl spec = last specifier va = address if last specifier is memory */ case CLRO: h_write_o (spec, va, z_octa, acc); /* write 0's */ CC_ZZ1P; /* set cc's */ break; /* TSTH opnd[0:3] = src.rh */ case TSTH: r = op_tsth (opnd[0]); /* test for 0 */ CC_IIZZ_FP (r); /* set cc's */ break; /* MOVO, MOVH, MNEGH opnd[0:3] = src.ro opnd[4:5] = dst.wo spec = last specifier va = address if last specifier is memory */ case MOVO: h_write_o (spec, va, opnd, acc); /* write src */ CC_IIZP_O (opnd[0], opnd[1], opnd[2], opnd[3]); /* set cc's */ break; case MOVH: if (r = op_tsth (opnd[0])) { /* test for 0 */ h_write_o (spec, va, opnd, acc); /* nz, write result */ CC_IIZP_FP (r); /* set cc's */ } else { /* zero */ h_write_o (spec, va, z_octa, acc); /* write 0 */ cc = (cc & CC_C) | CC_Z; /* set cc's */ } break; case MNEGH: if (r = op_tsth (opnd[0])) { /* test for 0 */ opnd[0] = opnd[0] ^ FPSIGN; /* nz, invert sign */ h_write_o (spec, va, opnd, acc); /* write result */ CC_IIZZ_FP (opnd[0]); /* set cc's */ } else { /* zero */ h_write_o (spec, va, z_octa, acc); /* write 0 */ cc = CC_Z; /* set cc's */ } break; /* CMPH opnd[0:3] = src1.rh opnd[4:7] = src2.rh */ case CMPH: cc = op_cmph (opnd + 0, opnd + 4); /* set cc's */ break; /* CVTBH, CVTWH, CVTLH opnd[0] = src.rx opnd[1:2] = dst.wh spec = last specifier va = address if last specifier is memory */ case CVTBH: r = op_cvtih (SXTB (opnd[0]), r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write reslt */ CC_IIZZ_FP (r); /* set cc's */ break; case CVTWH: r = op_cvtih (SXTW (opnd[0]), r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case CVTLH: r = op_cvtih (opnd[0], r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; /* CVTHB, CVTHW, CVTHL, CVTRHL opnd[0:3] = src.rh opnd[4:5] = dst.wx spec = last specifier va = address if last specifier is memory */ case CVTHB: r = op_cvthi (opnd, &flg, opc) & BMASK; /* convert */ h_write_b (spec, va, r, acc); /* write result */ CC_IIZZ_B (r); /* set cc's */ if (flg) { V_INTOV; } break; case CVTHW: r = op_cvthi (opnd, &flg, opc) & WMASK; /* convert */ h_write_w (spec, va, r, acc); /* write result */ CC_IIZZ_W (r); /* set cc's */ if (flg) { V_INTOV; } break; case CVTHL: case CVTRHL: r = op_cvthi (opnd, &flg, opc) & LMASK; /* convert */ h_write_l (spec, va, r, acc); /* write result */ CC_IIZZ_L (r); /* set cc's */ if (flg) { V_INTOV; } break; /* CVTFH opnd[0] = src.rf opnd[1:2] = dst.wh spec = last specifier va = address if last specifier is memory */ case CVTFH: r = op_cvtfdh (opnd[0], 0, r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; /* CVTDH, CVTGH opnd[0:1] = src.rx opnd[2:3] = dst.wh spec = last specifier va = address if last specifier is memory */ case CVTDH: r = op_cvtfdh (opnd[0], opnd[1], r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case CVTGH: r = op_cvtgh (opnd[0], opnd[1], r_octa); /* convert */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; /* CVTHF, CVTHD, CVTHG opnd[0:3] = src.rh opnd[4:5] = dst.wx spec = last specifier va = address if last specifier is memory */ case CVTHF: r = op_cvthfd (opnd, NULL); /* convert */ h_write_l (spec, va, r, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case CVTHD: r = op_cvthfd (opnd, &rh); /* convert */ h_write_q (spec, va, r, rh, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case CVTHG: r = op_cvthg (opnd, &rh); /* convert */ h_write_q (spec, va, r, rh, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; /* ADDH2, SUBH2, MULH2, DIVH2 op[0:3] = src.rh op[4:7] = dst.mh spec = last specifier va = address if last specifier is memory ADDH3, SUBH3, MULH3, DIVH3 op[0:3] = src1.rh op[4:7] = src2.rh op[8:9] = dst.wh spec = last specifier va = address if last specifier is memory */ case ADDH2: case ADDH3: r = op_addh (opnd, r_octa, FALSE); /* add */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case SUBH2: case SUBH3: r = op_addh (opnd, r_octa, TRUE); /* subtract */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case MULH2: case MULH3: r = op_mulh (opnd, r_octa); /* multiply */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; case DIVH2: case DIVH3: r = op_divh (opnd, r_octa); /* divide */ h_write_o (spec, va, r_octa, acc); /* write result */ CC_IIZZ_FP (r); /* set cc's */ break; /* ACBH opnd[0:3] = limit.rh opnd[4:7] = add.rh opnd[8:11] = index.mh spec = last specifier va = last va brdest = branch destination */ case ACBH: r = op_addh (opnd + 4, r_octa, FALSE); /* add + index */ CC_IIZP_FP (r); /* set cc's */ temp = op_cmph (r_octa, opnd); /* result : limit */ h_write_o (spec, va, r_octa, acc); /* write 2nd */ if ((temp & CC_Z) || ((opnd[4] & FPSIGN)? /* test br cond */ !(temp & CC_N): (temp & CC_N))) cc = cc | LSIGN; /* hack for branch */ break; /* POLYH opnd[0:3] = arg.rh opnd[4] = deg.rb opnd[5] = table.ah */ case POLYH: op_polyh (opnd, acc); /* eval polynomial */ CC_IIZZ_FP (R[0]); /* set cc's */ break; /* EMODH opnd[0:3] = multiplier opnd[4] = extension opnd[5:8] = multiplicand opnd[9:10] = integer destination (int.wl) opnd[11:12] = floating destination (flt.wh) spec = last specifier va = address if last specifier is memory */ case EMODH: r = op_emodh (opnd, r_octa, &temp, &flg); /* extended mod */ if (opnd[11] < 0) { /* 2nd memory? */ Read (opnd[12], L_BYTE, WA); /* prove write */ Read ((opnd[12] + 15) & LMASK, L_BYTE, WA); } if (opnd[9] >= 0) /* store 1st */ R[opnd[9]] = temp; else Write (opnd[10], temp, L_LONG, WA); h_write_o (spec, va, r_octa, acc); /* write 2nd */ CC_IIZZ_FP (r); /* set cc's */ if (flg) { V_INTOV; } break; default: RSVD_INST_FAULT; } return cc; } /* Test h_floating Note that only the high 32b is processed. If the high 32b is not zero, the rest of the fraction is unchanged. */ int32 op_tsth (int32 val) { if (val & H_EXP) /* non-zero? */ return val; if (val & FPSIGN) /* reserved? */ RSVD_OPND_FAULT; return 0; /* clean 0 */ } /* Compare h_floating */ int32 op_cmph (int32 *hf1, int32 *hf2) { UFPH a, b; int32 r; h_unpackh (hf1, &a); /* unpack op1 */ h_unpackh (hf2, &b); /* unpack op2 */ if (a.sign != b.sign) /* opp signs? */ return (a.sign? CC_N: 0); if (a.exp != b.exp) /* cmp exp */ r = a.exp - b.exp; else r = qp_cmp (&a.frac, &b.frac); /* if =, cmp frac */ if (r < 0) /* !=, maybe set N */ return (a.sign? 0: CC_N); if (r > 0) return (a.sign? CC_N: 0); return CC_Z; /* =, set Z */ } /* Integer to h_floating convert */ int32 op_cvtih (int32 val, int32 *hf) { UFPH a; if (val == 0) { /* zero? */ hf[0] = hf[1] = hf[2] = hf[3] = 0; /* result is 0 */ return 0; } if (val < 0) { /* negative? */ a.sign = FPSIGN; /* sign = - */ val = -val; } else a.sign = 0; /* else sign = + */ a.exp = 32 + H_BIAS; /* initial exp */ a.frac.f3 = val & LMASK; /* fraction hi */ a.frac.f2 = a.frac.f1 = a.frac.f0 = 0; h_normh (&a); /* normalize */ return h_rpackh (&a, hf); /* round and pack */ } /* H_floating to integer convert */ int32 op_cvthi (int32 *hf, int32 *flg, int32 opc) { UFPH a; int32 lnt = opc & 03; int32 ubexp; static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; *flg = 0; /* clear ovflo */ h_unpackh (hf, &a); /* unpack */ ubexp = a.exp - H_BIAS; /* unbiased exp */ if ((a.exp == 0) || (ubexp < 0)) /* true zero or frac? */ return 0; if (ubexp <= UH_V_NM) { /* exp in range? */ qp_rsh (&a.frac, UH_V_NM - ubexp); /* leave rnd bit */ if (lnt == 03) /* if CVTR, round */ qp_inc (&a.frac); qp_rsh (&a.frac, 1); /* now justified */ if (a.frac.f3 || a.frac.f2 || a.frac.f1 || (a.frac.f0 > (maxv[lnt] + (a.sign? 1: 0)))) *flg = CC_V; } else { *flg = CC_V; /* always ovflo */ if (ubexp > (UH_V_NM + 32)) /* in ext range? */ return 0; qp_lsh (&a.frac, ubexp - UH_V_NM - 1); /* no rnd bit */ } return (a.sign? NEG (a.frac.f0): a.frac.f0); /* return lo frac */ } /* Floating to floating convert - F/D to H, G to H, H to F/D, H to G */ int32 op_cvtfdh (int32 vl, int32 vh, int32 *hflt) { UFPH a; h_unpackfd (vl, vh, &a); /* unpack f/d */ a.exp = a.exp - FD_BIAS + H_BIAS; /* if nz, adjust exp */ return h_rpackh (&a, hflt); /* round and pack */ } int32 op_cvtgh (int32 vl, int32 vh, int32 *hflt) { UFPH a; h_unpackg (vl, vh, &a); /* unpack g */ a.exp = a.exp - G_BIAS + H_BIAS; /* if nz, adjust exp */ return h_rpackh (&a, hflt); /* round and pack */ } int32 op_cvthfd (int32 *hflt, int32 *rh) { UFPH a; h_unpackh (hflt, &a); /* unpack h */ a.exp = a.exp - H_BIAS + FD_BIAS; /* if nz, adjust exp */ return h_rpackfd (&a, rh); /* round and pack */ } int32 op_cvthg (int32 *hflt, int32 *rh) { UFPH a; h_unpackh (hflt, &a); /* unpack h */ a.exp = a.exp - H_BIAS + G_BIAS; /* if nz, adjust exp */ return h_rpackg (&a, rh); /* round and pack */ } /* Floating add and subtract */ int32 op_addh (int32 *opnd, int32 *hflt, t_bool sub) { UFPH a, b; h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ h_unpackh (&opnd[4], &b); if (sub) /* sub? -s1 */ a.sign = a.sign ^ FPSIGN; vax_hadd (&a, &b); /* do add */ return h_rpackh (&a, hflt); /* round and pack */ } /* Floating multiply */ int32 op_mulh (int32 *opnd, int32 *hflt) { UFPH a, b; h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ h_unpackh (&opnd[4], &b); vax_hmul (&a, &b, 0); /* do multiply */ return h_rpackh (&a, hflt); /* round and pack */ } /* Floating divide */ int32 op_divh (int32 *opnd, int32 *hflt) { UFPH a, b; h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ h_unpackh (&opnd[4], &b); vax_hdiv (&a, &b); /* do divide */ return h_rpackh (&b, hflt); /* round and pack */ } /* Polynomial evaluation The most mis-implemented instruction in the VAX (probably here too). POLY requires a precise combination of masking versus normalizing to achieve the desired answer. In particular, both the multiply and add steps are masked prior to normalization. In addition, negative small fractions must not be treated as 0 during denorm. */ void op_polyh (int32 *opnd, int32 acc) { UFPH r, a, c; int32 deg = opnd[4]; int32 ptr = opnd[5]; int32 i, wd[4], res[4]; if (deg > 31) /* deg > 31? fault */ RSVD_OPND_FAULT; h_unpackh (&opnd[0], &a); /* unpack arg */ wd[0] = Read (ptr, L_LONG, RD); /* get C0 */ wd[1] = Read (ptr + 4, L_LONG, RD); wd[2] = Read (ptr + 8, L_LONG, RD); wd[3] = Read (ptr + 12, L_LONG, RD); ptr = ptr + 16; /* adv ptr */ h_unpackh (wd, &r); /* unpack C0 */ h_rpackh (&r, res); /* first result */ for (i = 0; i < deg; i++) { /* loop */ h_unpackh (res, &r); /* unpack result */ vax_hmul (&r, &a, 1); /* r = r * arg */ wd[0] = Read (ptr, L_LONG, RD); /* get Cn */ wd[1] = Read (ptr + 4, L_LONG, RD); wd[2] = Read (ptr + 8, L_LONG, RD); wd[3] = Read (ptr + 12, L_LONG, RD); ptr = ptr + 16; h_unpackh (wd, &c); /* unpack Cnext */ vax_hadd (&r, &c); /* r = r + Cnext */ h_rpackh (&r, res); /* round and pack */ } R[0] = res[0]; /* result */ R[1] = res[1]; R[2] = res[2]; R[3] = res[3]; R[4] = 0; R[5] = ptr; return; } /* Extended modularize EMOD presents two sets of complications. First, it requires an extended fraction multiply, with precise (and unusual) truncation conditions. Second, it has two write operands, a dubious distinction it shares with EDIV. */ int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg) { UFPH a, b; h_unpackh (&opnd[0], &a); /* unpack operands */ h_unpackh (&opnd[5], &b); a.frac.f0 = a.frac.f0 | (opnd[4] >> 1); /* extend src1 */ vax_hmul (&a, &b, 0); /* multiply */ vax_hmod (&a, intgr, flg); /* sep int & frac */ return h_rpackh (&a, hflt); /* round and pack frac */ } /* Unpacked floating point routines */ /* Floating add */ void vax_hadd (UFPH *a, UFPH *b) { int32 ediff; UFPH t; if ((a->frac.f3 == 0) && (a->frac.f2 == 0) && /* s1 = 0? */ (a->frac.f1 == 0) && (a->frac.f0 == 0)) { *a = *b; /* result is s2 */ return; } if ((b->frac.f3 == 0) && (b->frac.f2 == 0) && /* s2 = 0? */ (b->frac.f1 == 0) && (b->frac.f0 == 0)) return; if ((a->exp < b->exp) || /* |s1| < |s2|? */ ((a->exp == b->exp) && (qp_cmp (&a->frac, &b->frac) < 0))) { t = *a; /* swap */ *a = *b; *b = t; } ediff = a->exp - b->exp; /* exp diff */ if (a->sign ^ b->sign) { /* eff sub? */ qp_neg (&b->frac); /* negate fraction */ if (ediff) /* denormalize */ qp_rsh_s (&b->frac, ediff, 1); qp_add (&a->frac, &b->frac); /* "add" frac */ h_normh (a); /* normalize */ } else { if (ediff) /* add, denormalize */ qp_rsh (&b->frac, ediff); if (qp_add (&a->frac, &b->frac)) { /* add frac, carry? */ qp_rsh (&a->frac, 1); /* renormalize */ a->frac.f3 = a->frac.f3 | UH_NM_H; /* add norm bit */ a->exp = a->exp + 1; /* incr exp */ } } return; } /* Floating multiply - 128b * 128b */ void vax_hmul (UFPH *a, UFPH *b, uint32 mlo) { int32 i, c; UQP accum = { 0, 0, 0, 0 }; if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ a->frac.f0 = a->frac.f1 = 0; /* result is zero */ a->frac.f2 = a->frac.f3 = 0; a->sign = a->exp = 0; return; } a->sign = a->sign ^ b->sign; /* sign of result */ a->exp = a->exp + b->exp - H_BIAS; /* add exponents */ for (i = 0; i < 128; i++) { /* quad precision */ if (a->frac.f0 & 1) /* mplr low? add */ c = qp_add (&accum, &b->frac); else c = 0; qp_rsh (&accum, 1); /* shift result */ if (c) /* add carry out */ accum.f3 = accum.f3 | UH_NM_H; qp_rsh (&a->frac, 1); /* shift mplr */ } a->frac = accum; /* result */ a->frac.f0 = a->frac.f0 & ~mlo; /* mask low frac */ h_normh (a); /* normalize */ return; } /* Floating modulus - there are three cases exp <= bias - integer is 0, fraction is input, no overflow bias < exp <= bias+128 - separate integer and fraction, integer overflow may occur bias+128 < exp - result is integer, fraction is 0 integer overflow */ void vax_hmod (UFPH *a, int32 *intgr, int32 *flg) { UQP ifr; if (a->exp <= H_BIAS) /* 0 or <1? int = 0 */ *intgr = *flg = 0; else if (a->exp <= (H_BIAS + 128)) { /* in range? */ ifr = a->frac; qp_rsh (&ifr, 128 - (a->exp - H_BIAS)); /* separate integer */ if ((a->exp > (H_BIAS + 32)) || /* test ovflo */ ((a->exp == (H_BIAS + 32)) && (ifr.f0 > (a->sign? 0x80000000: 0x7FFFFFFF)))) *flg = CC_V; else *flg = 0; *intgr = ifr.f0; if (a->sign) /* -? comp int */ *intgr = -*intgr; qp_lsh (&a->frac, a->exp - H_BIAS); /* excise integer */ a->exp = H_BIAS; } else { *intgr = 0; /* out of range */ a->frac.f0 = a->frac.f1 = 0; /* result 0 */ a->frac.f2 = a->frac.f3 = 0; a->sign = a->exp = 0; *flg = CC_V; /* overflow */ } h_normh (a); /* normalize */ return; } /* Floating divide Carried out to 128 bits, although fewer are required */ void vax_hdiv (UFPH *a, UFPH *b) { int32 i; UQP quo = { 0, 0, 0, 0 }; if (a->exp == 0) /* divr = 0? */ FLT_DZRO_FAULT; if (b->exp == 0) /* divd = 0? */ return; b->sign = b->sign ^ a->sign; /* result sign */ b->exp = b->exp - a->exp + H_BIAS + 1; /* unbiased exp */ qp_rsh (&a->frac, 1); /* allow 1 bit left */ qp_rsh (&b->frac, 1); for (i = 0; i < 128; i++) { /* divide loop */ qp_lsh (&quo, 1); /* shift quo */ if (qp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ qp_sub (&b->frac, &a->frac); /* subtract */ quo.f0 = quo.f0 + 1; /* quo bit = 1 */ } qp_lsh (&b->frac, 1); /* shift divd */ } b->frac = quo; h_normh (b); /* normalize */ return; } /* Quad precision integer routines */ int32 qp_cmp (UQP *a, UQP *b) { if (a->f3 < b->f3) /* compare hi */ return -1; if (a->f3 > b->f3) return +1; if (a->f2 < b->f2) /* hi =, compare mid1 */ return -1; if (a->f2 > b->f2) return +1; if (a->f1 < b->f1) /* mid1 =, compare mid2 */ return -1; if (a->f1 > b->f1) return +1; if (a->f0 < b->f0) /* mid2 =, compare lo */ return -1; if (a->f0 > b->f0) return +1; return 0; /* all equal */ } uint32 qp_add (UQP *a, UQP *b) { uint32 cry1, cry2, cry3, cry4; a->f0 = (a->f0 + b->f0) & LMASK; /* add lo */ cry1 = (a->f0 < b->f0); /* carry? */ a->f1 = (a->f1 + b->f1 + cry1) & LMASK; /* add mid2 */ cry2 = (a->f1 < b->f1) || (cry1 && (a->f1 == b->f1)); /* carry? */ a->f2 = (a->f2 + b->f2 + cry2) & LMASK; /* add mid1 */ cry3 = (a->f2 < b->f2) || (cry2 && (a->f2 == b->f2)); /* carry? */ a->f3 = (a->f3 + b->f3 + cry3) & LMASK; /* add hi */ cry4 = (a->f3 < b->f3) || (cry3 && (a->f3 == b->f3)); /* carry? */ return cry4; /* return carry out */ } void qp_inc (UQP *a) { a->f0 = (a->f0 + 1) & LMASK; /* inc lo */ if (a->f0 == 0) { /* propagate carry */ a->f1 = (a->f1 + 1) & LMASK; if (a->f1 == 0) { a->f2 = (a->f2 + 1) & LMASK; if (a->f2 == 0) { a->f3 = (a->f3 + 1) & LMASK; } } } return; } uint32 qp_sub (UQP *a, UQP *b) { uint32 brw1, brw2, brw3, brw4; brw1 = (a->f0 < b->f0); /* borrow? */ a->f0 = (a->f0 - b->f0) & LMASK; /* sub lo */ brw2 = (a->f1 < b->f1) || (brw1 && (a->f1 == b->f1)); /* borrow? */ a->f1 = (a->f1 - b->f1 - brw1) & LMASK; /* sub mid1 */ brw3 = (a->f2 < b->f2) || (brw2 && (a->f2 == b->f2)); /* borrow? */ a->f2 = (a->f2 - b->f2 - brw2) & LMASK; /* sub mid2 */ brw4 = (a->f3 < b->f3) || (brw3 && (a->f3 == b->f3)); /* borrow? */ a->f3 = (a->f3 - b->f3 - brw3) & LMASK; /* sub high */ return brw4; } void qp_neg (UQP *a) { uint32 cryin; cryin = 1; a->f0 = (~a->f0 + cryin) & LMASK; if (a->f0 != 0) cryin = 0; a->f1 = (~a->f1 + cryin) & LMASK; if (a->f1 != 0) cryin = 0; a->f2 = (~a->f2 + cryin) & LMASK; if (a->f2 != 0) cryin = 0; a->f3 = (~a->f3 + cryin) & LMASK; return; } void qp_lsh (UQP *r, uint32 sc) { if (sc >= 128) /* > 127? result 0 */ r->f3 = r->f2 = r->f1 = r->f0 = 0; else if (sc >= 96) { /* [96,127]? */ r->f3 = (r->f0 << (sc - 96)) & LMASK; r->f2 = r->f1 = r->f0 = 0; } else if (sc > 64) { /* [65,95]? */ r->f3 = ((r->f1 << (sc - 64)) | (r->f0 >> (96 - sc))) & LMASK; r->f2 = (r->f0 << (sc - 64)) & LMASK; r->f1 = r->f0 = 0; } else if (sc == 64) { /* [64]? */ r->f3 = r->f1; r->f2 = r->f0; r->f1 = r->f0 = 0; } else if (sc > 32) { /* [33,63]? */ r->f3 = ((r->f2 << (sc - 32)) | (r->f1 >> (64 - sc))) & LMASK; r->f2 = ((r->f1 << (sc - 32)) | (r->f0 >> (64 - sc))) & LMASK; r->f1 = (r->f0 << (sc - 32)) & LMASK; r->f0 = 0; } else if (sc == 32) { /* [32]? */ r->f3 = r->f2; r->f2 = r->f1; r->f1 = r->f0; r->f0 = 0; } else if (sc != 0) { /* [31,1]? */ r->f3 = ((r->f3 << sc) | (r->f2 >> (32 - sc))) & LMASK; r->f2 = ((r->f2 << sc) | (r->f1 >> (32 - sc))) & LMASK; r->f1 = ((r->f1 << sc) | (r->f0 >> (32 - sc))) & LMASK; r->f0 = (r->f0 << sc) & LMASK; } return; } void qp_rsh (UQP *r, uint32 sc) { if (sc >= 128) /* > 127? result 0 */ r->f3 = r->f2 = r->f1 = r->f0 = 0; else if (sc >= 96) { /* [96,127]? */ r->f0 = (r->f3 >> (sc - 96)) & LMASK; r->f1 = r->f2 = r->f3 = 0; } else if (sc > 64) { /* [65,95]? */ r->f0 = ((r->f2 >> (sc - 64)) | (r->f3 << (96 - sc))) & LMASK; r->f1 = (r->f3 >> (sc - 64)) & LMASK; r->f2 = r->f3 = 0; } else if (sc == 64) { /* [64]? */ r->f0 = r->f2; r->f1 = r->f3; r->f2 = r->f3 = 0; } else if (sc > 32) { /* [33,63]? */ r->f0 = ((r->f1 >> (sc - 32)) | (r->f2 << (64 - sc))) & LMASK; r->f1 = ((r->f2 >> (sc - 32)) | (r->f3 << (64 - sc))) & LMASK; r->f2 = (r->f3 >> (sc - 32)) & LMASK; r->f3 = 0; } else if (sc == 32) { /* [32]? */ r->f0 = r->f1; r->f1 = r->f2; r->f2 = r->f3; r->f3 = 0; } else if (sc != 0) { /* [31,1]? */ r->f0 = ((r->f0 >> sc) | (r->f1 << (32 - sc))) & LMASK; r->f1 = ((r->f1 >> sc) | (r->f2 << (32 - sc))) & LMASK; r->f2 = ((r->f2 >> sc) | (r->f3 << (32 - sc))) & LMASK; r->f3 = (r->f3 >> sc) & LMASK; } return; } void qp_rsh_s (UQP *r, uint32 sc, uint32 neg) { qp_rsh (r, sc); /* do unsigned right */ if (neg && sc) { /* negative? */ if (sc >= 128) r->f0 = r->f1 = r->f2 = r->f3 = LMASK; /* > 127? result -1 */ else { UQP ones = { LMASK, LMASK, LMASK, LMASK }; qp_lsh (&ones, 128 - sc); /* shift ones */ r->f0 = r->f0 | ones.f0; /* or into result */ r->f1 = r->f1 | ones.f1; r->f2 = r->f2 | ones.f2; r->f3 = r->f3 | ones.f3; } } return; } /* Support routines */ void h_unpackfd (int32 hi, int32 lo, UFPH *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = FD_GETEXP (hi); /* get exponent */ r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.f2 = r->frac.f3 = 0; /* else 0 */ return; } r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); r->frac.f2 = WORDSWAP (lo); qp_lsh (&r->frac, FD_GUARD); return; } void h_unpackg (int32 hi, int32 lo, UFPH *r) { r->sign = hi & FPSIGN; /* get sign */ r->exp = G_GETEXP (hi); /* get exponent */ r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.f2 = r->frac.f3 = 0; /* else 0 */ return; } r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); r->frac.f2 = WORDSWAP (lo); qp_lsh (&r->frac, G_GUARD); return; } void h_unpackh (int32 *hflt, UFPH *r) { r->sign = hflt[0] & FPSIGN; /* get sign */ r->exp = H_GETEXP (hflt[0]); /* get exponent */ if (r->exp == 0) { /* exp = 0? */ if (r->sign) /* if -, rsvd op */ RSVD_OPND_FAULT; r->frac.f0 = r->frac.f1 = 0; /* else 0 */ r->frac.f2 = r->frac.f3 = 0; return; } r->frac.f3 = WORDSWAP ((hflt[0] & ~(FPSIGN | H_EXP)) | H_HB); r->frac.f2 = WORDSWAP (hflt[1]); r->frac.f1 = WORDSWAP (hflt[2]); r->frac.f0 = WORDSWAP (hflt[3]); qp_lsh (&r->frac, H_GUARD); return; } void h_normh (UFPH *r) { int32 i; static uint32 normmask[5] = { 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff }; static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; if ((r->frac.f0 == 0) && (r->frac.f1 == 0) && (r->frac.f2 == 0) && (r->frac.f3 == 0)) { /* if fraction = 0 */ r->sign = r->exp = 0; /* result is 0 */ return; } while ((r->frac.f3 & UH_NM_H) == 0) { /* normalized? */ for (i = 0; i < 5; i++) { /* find first 1 */ if (r->frac.f3 & normmask[i]) break; } qp_lsh (&r->frac, normtab[i]); /* shift frac */ r->exp = r->exp - normtab[i]; /* decr exp */ } return; } int32 h_rpackfd (UFPH *r, int32 *rh) { static UQP f_round = { 0, 0, 0, UH_FRND }; static UQP d_round = { 0, 0, UH_DRND, 0 }; if (rh) /* assume 0 */ *rh = 0; if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) /* frac = 0? done */ return 0; qp_add (&r->frac, rh? &d_round: &f_round); if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ qp_rsh (&r->frac, 1); /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } qp_rsh (&r->frac, FD_GUARD); /* remove guard */ if (rh) *rh = WORDSWAP (r->frac.f2); return r->sign | (r->exp << FD_V_EXP) | (WORDSWAP (r->frac.f3) & ~(FD_HB | FPSIGN | FD_EXP)); } int32 h_rpackg (UFPH *r, int32 *rh) { static UQP g_round = { 0, 0, UH_GRND, 0 }; *rh = 0; /* assume 0 */ if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) /* frac = 0? done */ return 0; qp_add (&r->frac, &g_round); /* round */ if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ qp_rsh (&r->frac, 1); /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } qp_rsh (&r->frac, G_GUARD); /* remove guard */ *rh = WORDSWAP (r->frac.f2); /* get low */ return r->sign | (r->exp << G_V_EXP) | (WORDSWAP (r->frac.f3) & ~(G_HB | FPSIGN | G_EXP)); } int32 h_rpackh (UFPH *r, int32 *hflt) { static UQP h_round = { UH_HRND, 0, 0, 0 }; hflt[0] = hflt[1] = hflt[2] = hflt[3] = 0; /* assume 0 */ if ((r->frac.f3 == 0) && (r->frac.f2 == 0) && /* frac = 0? done */ (r->frac.f1 == 0) && (r->frac.f0 == 0)) return 0; if (qp_add (&r->frac, &h_round)) { /* round, carry out? */ qp_rsh (&r->frac, 1); /* renormalize */ r->exp = r->exp + 1; } if (r->exp > (int32) H_M_EXP) /* ovflo? fault */ FLT_OVFL_FAULT; if (r->exp <= 0) { /* underflow? */ if (PSL & PSW_FU) /* fault if fu */ FLT_UNFL_FAULT; return 0; /* else 0 */ } qp_rsh (&r->frac, H_GUARD); /* remove guard */ hflt[0] = r->sign | (r->exp << H_V_EXP) | (WORDSWAP (r->frac.f3) & ~(H_HB | FPSIGN | H_EXP)); hflt[1] = WORDSWAP (r->frac.f2); hflt[2] = WORDSWAP (r->frac.f1); hflt[3] = WORDSWAP (r->frac.f0); return hflt[0]; } void h_write_b (int32 spec, int32 va, int32 val, int32 acc) { int32 rn; if (spec > (GRN | nPC)) Write (va, val, L_BYTE, WA); else { rn = spec & 0xF; R[rn] = (R[rn] & ~BMASK) | val; } return; } void h_write_w (int32 spec, int32 va, int32 val, int32 acc) { int32 rn; if (spec > (GRN | nPC)) Write (va, val, L_WORD, WA); else { rn = spec & 0xF; R[rn] = (R[rn] & ~WMASK) | val; } return; } void h_write_l (int32 spec, int32 va, int32 val, int32 acc) { if (spec > (GRN | nPC)) Write (va, val, L_LONG, WA); else R[spec & 0xF] = val; return; } void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc) { int32 rn, mstat; if (spec > (GRN | nPC)) { if ((Test (va + 7, WA, &mstat) >= 0) || (Test (va, WA, &mstat) < 0)) Write (va, vl, L_LONG, WA); Write (va + 4, vh, L_LONG, WA); } else { rn = spec & 0xF; if (rn >= nSP) RSVD_ADDR_FAULT; R[rn] = vl; R[rn + 1] = vh; } return; } void h_write_o (int32 spec, int32 va, int32 *val, int32 acc) { int32 rn, mstat; if (spec > (GRN | nPC)) { if ((Test (va + 15, WA, &mstat) >= 0) || (Test (va, WA, &mstat) < 0)) Write (va, val[0], L_LONG, WA); Write (va + 4, val[1], L_LONG, WA); Write (va + 8, val[2], L_LONG, WA); Write (va + 12, val[3], L_LONG, WA); } else { rn = spec & 0xF; if (rn >= nAP) RSVD_ADDR_FAULT; R[rn] = val[0]; R[rn + 1] = val[1]; R[rn + 2] = val[2]; R[rn + 3] = val[3]; } return; } #else extern jmp_buf save_env; int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) { RSVD_INST_FAULT; return cc; } #endif simh-3.8.1/VAX/vax780_mem.c0000644000175000017500000002322311110567120013332 0ustar vlmvlm/* vax780_mem.c: VAX 11/780 memory controllers Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module contains the VAX 11/780 system-specific registers and devices. mctl0, mctl1 MS780C/E memory controllers */ #include "vax_defs.h" /* Memory controller register A */ #define MCRA_OF 0x0 #define MCRA_SUMM 0x00100000 /* err summ (MS780E) */ #define MCRA_C_SIZE 0x00007E00 /* array size - fixed */ #define MCRA_V_SIZE 9 #define MCRA_ILVE 0x00000100 /* interleave wr enab */ #define MCRA_TYPE 0x000000F8 /* type */ #define MCRA_C_TYPE 0x00000010 /* 16k uninterleaved */ #define MCRA_E_TYPE 0x0000006A /* 256k upper + lower */ #define MCRA_ILV 0x00000007 /* interleave */ #define MCRA_RD (0x00107FFF|SBI_FAULTS) #define MCRA_WR 0x00000100 /* Memory controller register B */ #define MCRB_OF 0x1 #define MCRB_FP 0xF0000000 /* file pointers */ #define MCRB_V_SA 15 /* start addr */ #define MCRB_M_SA 0x1FFF #define MCRB_SA (MCRB_M_SA << MCRB_V_SA) #define MCRB_SAE 0x00004000 /* start addr wr enab */ #define MCRB_INIT 0x00003000 /* init state */ #define MCRB_REF 0x00000400 /* refresh */ #define MCRB_ECC 0x000003FF /* ECC for diags */ #define MCRB_RD 0xFFFFF7FF #define MCRB_WR 0x000043FF /* Memory controller register C,D */ #define MCRC_OF 0x2 #define MCRD_OF 0x3 #define MCRC_DCRD 0x40000000 /* disable CRD */ #define MCRC_HER 0x20000000 /* high error rate */ #define MCRC_ERL 0x10000000 /* log error */ #define MCRC_C_ER 0x0FFFFFFF /* MS780C error */ #define MCRC_E_PE1 0x00080000 /* MS780E par ctl 1 */ #define MCRC_E_PE0 0x00040000 /* MS780E par ctl 0 */ #define MCRC_E_CRD 0x00000200 /* MS780E CRD */ #define MCRC_E_PEW 0x00000100 /* MS780E par err wr */ #define MCRC_E_USEQ 0x00000080 /* MS780E seq err */ #define MCRC_C_RD 0x7FFFFFFF #define MCRC_E_RD 0x700C0380 #define MCRC_WR 0x40000000 #define MCRC_C_W1C 0x30000000 #define MCRC_E_W1C 0x300C0380 #define MCRROM_OF 0x400 uint32 mcr_a[MCTL_NUM]; uint32 mcr_b[MCTL_NUM]; uint32 mcr_c[MCTL_NUM]; uint32 mcr_d[MCTL_NUM]; uint32 rom_lw[MCTL_NUM][ROMSIZE >> 2]; extern UNIT cpu_unit; t_stat mctl_reset (DEVICE *dptr); t_stat mctl_rdreg (int32 *val, int32 pa, int32 mode); t_stat mctl_wrreg (int32 val, int32 pa, int32 mode); /* MCTLx data structures mctlx_dev MCTLx device descriptor mctlx_unit MCTLx unit mctlx_reg MCTLx register list */ DIB mctl0_dib[] = { TR_MCTL0, 0, &mctl_rdreg, &mctl_wrreg, 0 }; UNIT mctl0_unit = { UDATA (NULL, 0, 0) }; REG mctl0_reg[] = { { HRDATA (CRA, mcr_a[0], 32) }, { HRDATA (CRB, mcr_b[0], 32) }, { HRDATA (CRC, mcr_c[0], 32) }, { HRDATA (CRD, mcr_d[0], 32) }, { BRDATA (ROM, rom_lw[0], 16, 32, ROMSIZE >> 2) }, { NULL } }; MTAB mctl0_mod[] = { { MTAB_XTD|MTAB_VDV, TR_MCTL0, "NEXUS", NULL, NULL, &show_nexus }, { 0 } }; DIB mctl1_dib[] = { TR_MCTL1, 0, &mctl_rdreg, &mctl_wrreg, 0 }; UNIT mctl1_unit = { UDATA (NULL, 0, 0) }; MTAB mctl1_mod[] = { { MTAB_XTD|MTAB_VDV, TR_MCTL1, "NEXUS", NULL, NULL, &show_nexus }, { 0 } }; REG mctl1_reg[] = { { HRDATA (CRA, mcr_a[1], 32) }, { HRDATA (CRB, mcr_b[1], 32) }, { HRDATA (CRC, mcr_c[1], 32) }, { HRDATA (CRD, mcr_d[1], 32) }, { BRDATA (ROM, rom_lw[1], 16, 32, ROMSIZE >> 2) }, { NULL } }; DEVICE mctl_dev[] = { { "MCTL0", &mctl0_unit, mctl0_reg, mctl0_mod, 1, 16, 16, 1, 16, 8, NULL, NULL, &mctl_reset, NULL, NULL, NULL, &mctl0_dib, DEV_NEXUS }, { "MCTL1", &mctl1_unit, mctl1_reg, mctl1_mod, 1, 16, 16, 1, 16, 8, NULL, NULL, &mctl_reset, NULL, NULL, NULL, &mctl1_dib, DEV_NEXUS } }; /* Memory controller register read */ t_stat mctl_rdreg (int32 *val, int32 pa, int32 lnt) { int32 mctl, ofs; t_bool extmem = MEMSIZE > MAXMEMSIZE; if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>MCTL: invalid adapter read mask, pa = %X, lnt = %d\r\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ ofs = NEXUS_GETOFS (pa); /* get offset */ if (ofs >= MCRROM_OF) { /* ROM? */ *val = rom_lw[mctl][ofs - MCRROM_OF]; /* get lw */ return SCPE_OK; } switch (ofs) { case MCRA_OF: /* CR A */ *val = mcr_a[mctl] & MCRA_RD; break; case MCRB_OF: /* CR B */ *val = (mcr_b[mctl] & MCRB_RD) | MCRB_INIT; break; case MCRC_OF: /* CR C */ *val = mcr_c[mctl] & (extmem? MCRC_E_RD: MCRC_C_RD); break; case MCRD_OF: /* CR D */ if (!extmem) /* MS780E only */ return SCPE_NXM; *val = mcr_d[mctl] & MCRC_E_RD; break; default: return SCPE_NXM; } return SCPE_OK; } /* Memory controller register write */ t_stat mctl_wrreg (int32 val, int32 pa, int32 lnt) { int32 mctl, ofs, mask; t_bool extmem = MEMSIZE > MAXMEMSIZE; if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>MCTL: invalid adapter write mask, pa = %X, lnt = %d\r\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ ofs = NEXUS_GETOFS (pa); /* get offset */ switch (ofs) { case MCRA_OF: /* CR A */ mask = MCRA_WR | ((val & MCRA_ILVE)? MCRA_ILV: 0); mcr_a[mctl] = (mcr_a[mctl] & ~mask) | (val & mask); break; case MCRB_OF: /* CR B */ mask = MCRB_WR | ((val & MCRB_SAE)? MCRB_SA: 0); mcr_b[mctl] = (mcr_b[mctl] & ~mask) | (val & mask); break; case MCRC_OF: /* CR C */ mcr_c[mctl] = ((mcr_c[mctl] & ~MCRC_WR) | (val & MCRC_WR)) & ~(val & (extmem? MCRC_E_W1C: MCRC_C_W1C)); break; case MCRD_OF: /* CR D */ if (!extmem) /* MS780E only */ return SCPE_NXM; mcr_d[mctl] = ((mcr_d[mctl] & ~MCRC_WR) | (val & MCRC_WR)) & ~(val & MCRC_E_W1C); break; default: return SCPE_NXM; } return SCPE_OK; } /* Used by CPU and loader */ void rom_wr_B (int32 pa, int32 val) { uint32 mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ uint32 ofs = NEXUS_GETOFS (pa) - MCRROM_OF; /* get offset */ int32 sc = (pa & 3) << 3; rom_lw[mctl][ofs] = ((val & 0xFF) << sc) | (rom_lw[mctl][ofs] & ~(0xFF << sc)); return; } /* MEMCTL reset */ t_stat mctl_reset (DEVICE *dptr) { int32 i, amb; t_bool extmem = MEMSIZE > MAXMEMSIZE; amb = (int32) (MEMSIZE / 2) >> 20; /* array size MB */ for (i = 0; i < MCTL_NUM; i++) { /* init for MS780C */ if (extmem) { /* extended memory? */ mcr_a[i] = ((amb - 1) << MCRA_V_SIZE) | MCRA_E_TYPE; mcr_b[i] = MCRB_INIT | ((i * amb) << (MCRB_V_SA + 4)); } else { mcr_a[i] = MCRA_C_SIZE | MCRA_C_TYPE; mcr_b[i] = MCRB_INIT | (i << 21); } mcr_c[i] = 0; mcr_d[i] = 0; } return SCPE_OK; } simh-3.8.1/VAX/vax_sysdev.c0000644000175000017500000014266311112314642013645 0ustar vlmvlm/* vax_sysdev.c: VAX 3900 system-specific logic Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module contains the CVAX chip and VAX 3900 system-specific registers and devices. rom bootstrap ROM (no registers) nvr non-volatile ROM (no registers) csi console storage input cso console storage output sysd system devices (SSC miscellany) 25-Oct-05 RMS Automated CMCTL extended memory 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Mar-05 RMS Fixed bug in timer schedule routine (from Mark Hittinger) 30-Sep-04 RMS Moved CADR, MSER, CONPC, CONPSL, machine_check, cpu_boot, con_halt here from vax_cpu.c Moved model-specific IPR's here from vax_cpu1.c 09-Sep-04 RMS Integrated powerup into RESET (with -p) Added model-specific registers and routines from CPU 23-Jan-04 MP Added extended physical memory support (Mark Pizzolato) 07-Jun-03 MP Added calibrated delay to ROM reads (Mark Pizzolato) Fixed calibration problems interval timer (Mark Pizzolato) 12-May-03 RMS Fixed compilation warnings from VC.Net 23-Apr-03 RMS Revised for 32b/64b t_addr 19-Aug-02 RMS Removed unused variables (found by David Hittner) Allowed NVR to be attached to file 30-May-02 RMS Widened POS to 32b 28-Feb-02 RMS Fixed bug, missing end of table (found by Lars Brinkhoff) */ #include "vax_defs.h" #define UNIT_V_NODELAY (UNIT_V_UF + 0) /* ROM access equal to RAM access */ #define UNIT_NODELAY (1u << UNIT_V_NODELAY) /* Console storage control/status */ #define CSICSR_IMP (CSR_DONE + CSR_IE) /* console input */ #define CSICSR_RW (CSR_IE) #define CSOCSR_IMP (CSR_DONE + CSR_IE) /* console output */ #define CSOCSR_RW (CSR_IE) /* CMCTL configuration registers */ #define CMCNF_VLD 0x80000000 /* addr valid */ #define CMCNF_BA 0x1FF00000 /* base addr */ #define CMCNF_LOCK 0x00000040 /* lock NI */ #define CMCNF_SRQ 0x00000020 /* sig req WO */ #define CMCNF_SIG 0x0000001F /* signature */ #define CMCNF_RW (CMCNF_VLD | CMCNF_BA) /* read/write */ #define CMCNF_MASK (CMCNF_RW | CMCNF_SIG) #define MEM_BANK (1 << 22) /* bank size 4MB */ #define MEM_SIG (0x17) /* ECC, 4 x 4MB */ /* CMCTL error register */ #define CMERR_RDS 0x80000000 /* uncorr err NI */ #define CMERR_FRQ 0x40000000 /* 2nd RDS NI */ #define CMERR_CRD 0x20000000 /* CRD err NI */ #define CMERR_PAG 0x1FFFFC00 /* page addr NI */ #define CMERR_DMA 0x00000100 /* DMA err NI */ #define CMERR_BUS 0x00000080 /* bus err NI */ #define CMERR_SYN 0x0000007F /* syndrome NI */ #define CMERR_W1C (CMERR_RDS | CMERR_FRQ | CMERR_CRD | \ CMERR_DMA | CMERR_BUS) /* CMCTL control/status register */ #define CMCSR_PMI 0x00002000 /* PMI speed NI */ #define CMCSR_CRD 0x00001000 /* enb CRD int NI */ #define CMCSR_FRF 0x00000800 /* force ref WONI */ #define CMCSR_DET 0x00000400 /* dis err NI */ #define CMCSR_FDT 0x00000200 /* fast diag NI */ #define CMCSR_DCM 0x00000080 /* diag mode NI */ #define CMCSR_SYN 0x0000007F /* syndrome NI */ #define CMCSR_MASK (CMCSR_PMI | CMCSR_CRD | CMCSR_DET | \ CMCSR_FDT | CMCSR_DCM | CMCSR_SYN) /* KA655 boot/diagnostic register */ #define BDR_BRKENB 0x00000080 /* break enable */ /* KA655 cache control register */ #define CACR_DRO 0x00FFFF00 /* diag bits RO */ #define CACR_V_DPAR 24 /* data parity */ #define CACR_FIXED 0x00000040 /* fixed bits */ #define CACR_CPE 0x00000020 /* parity err W1C */ #define CACR_CEN 0x00000010 /* enable */ #define CACR_DPE 0x00000004 /* disable par NI */ #define CACR_WWP 0x00000002 /* write wrong par NI */ #define CACR_DIAG 0x00000001 /* diag mode */ #define CACR_W1C (CACR_CPE) #define CACR_RW (CACR_CEN | CACR_DPE | CACR_WWP | CACR_DIAG) /* SSC base register */ #define SSCBASE_MBO 0x20000000 /* must be one */ #define SSCBASE_RW 0x1FFFFC00 /* base address */ /* SSC configuration register */ #define SSCCNF_BLO 0x80000000 /* batt low W1C */ #define SSCCNF_IVD 0x08000000 /* int dsbl NI */ #define SSCCNF_IPL 0x03000000 /* int IPL NI */ #define SSCCNF_ROM 0x00F70000 /* ROM param NI */ #define SSCCNF_CTLP 0x00008000 /* ctrl P enb */ #define SSCCNF_BAUD 0x00007700 /* baud rates NI */ #define SSCCNF_ADS 0x00000077 /* addr strb NI */ #define SSCCNF_W1C SSCCNF_BLO #define SSCCNF_RW 0x0BF7F777 /* SSC timeout register */ #define SSCBTO_BTO 0x80000000 /* timeout W1C */ #define SSCBTO_RWT 0x40000000 /* read/write W1C */ #define SSCBTO_INTV 0x00FFFFFF /* interval NI */ #define SSCBTO_W1C (SSCBTO_BTO | SSCBTO_RWT) #define SSCBTO_RW SSCBTO_INTV /* SSC output port */ #define SSCOTP_MASK 0x0000000F /* output port */ /* SSC timer control/status */ #define TMR_CSR_ERR 0x80000000 /* error W1C */ #define TMR_CSR_DON 0x00000080 /* done W1C */ #define TMR_CSR_IE 0x00000040 /* int enb */ #define TMR_CSR_SGL 0x00000020 /* single WO */ #define TMR_CSR_XFR 0x00000010 /* xfer WO */ #define TMR_CSR_STP 0x00000004 /* stop */ #define TMR_CSR_RUN 0x00000001 /* run */ #define TMR_CSR_W1C (TMR_CSR_ERR | TMR_CSR_DON) #define TMR_CSR_RW (TMR_CSR_IE | TMR_CSR_STP | TMR_CSR_RUN) /* SSC timer intervals */ #define TMR_INC 10000 /* usec/interval */ /* SSC timer vector */ #define TMR_VEC_MASK 0x000003FC /* vector */ /* SSC address strobes */ #define SSCADS_MASK 0x3FFFFFFC /* match or mask */ extern int32 R[16]; extern int32 STK[5]; extern int32 PSL; extern int32 SISR; extern int32 mapen; extern int32 pcq[PCQ_SIZE]; extern int32 pcq_p; extern int32 ibcnt, ppc; extern int32 in_ie; extern int32 mchk_va, mchk_ref; extern int32 fault_PC; extern int32 int_req[IPL_HLVL]; extern UNIT cpu_unit; extern UNIT clk_unit; extern jmp_buf save_env; extern int32 p1; extern int32 sim_switches; extern int32 MSER; extern int32 tmr_poll; uint32 *rom = NULL; /* boot ROM */ uint32 *nvr = NULL; /* non-volatile mem */ int32 CADR = 0; /* cache disable reg */ int32 MSER = 0; /* mem sys error reg */ int32 conpc, conpsl; /* console reg */ int32 csi_csr = 0; /* control/status */ int32 cso_csr = 0; /* control/status */ int32 cmctl_reg[CMCTLSIZE >> 2] = { 0 }; /* CMCTL reg */ int32 ka_cacr = 0; /* KA655 cache ctl */ int32 ka_bdr = BDR_BRKENB; /* KA655 boot diag */ int32 ssc_base = SSCBASE; /* SSC base */ int32 ssc_cnf = 0; /* SSC conf */ int32 ssc_bto = 0; /* SSC timeout */ int32 ssc_otp = 0; /* SSC output port */ int32 tmr_csr[2] = { 0 }; /* SSC timers */ uint32 tmr_tir[2] = { 0 }; /* curr interval */ uint32 tmr_tnir[2] = { 0 }; /* next interval */ int32 tmr_tivr[2] = { 0 }; /* vector */ uint32 tmr_inc[2] = { 0 }; /* tir increment */ uint32 tmr_sav[2] = { 0 }; /* saved inst cnt */ int32 ssc_adsm[2] = { 0 }; /* addr strobes */ int32 ssc_adsk[2] = { 0 }; int32 cdg_dat[CDASIZE >> 2]; /* cache data */ static uint32 rom_delay = 0; t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); t_stat rom_reset (DEVICE *dptr); t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); t_stat nvr_reset (DEVICE *dptr); t_stat nvr_attach (UNIT *uptr, char *cptr); t_stat nvr_detach (UNIT *uptr); t_stat csi_reset (DEVICE *dptr); t_stat cso_reset (DEVICE *dptr); t_stat cso_svc (UNIT *uptr); t_stat tmr_svc (UNIT *uptr); t_stat sysd_reset (DEVICE *dptr); int32 rom_rd (int32 pa); int32 nvr_rd (int32 pa); void nvr_wr (int32 pa, int32 val, int32 lnt); int32 csrs_rd (void); int32 csrd_rd (void); int32 csts_rd (void); void csrs_wr (int32 dat); void csts_wr (int32 dat); void cstd_wr (int32 dat); int32 cmctl_rd (int32 pa); void cmctl_wr (int32 pa, int32 val, int32 lnt); int32 ka_rd (int32 pa); void ka_wr (int32 pa, int32 val, int32 lnt); int32 cdg_rd (int32 pa); void cdg_wr (int32 pa, int32 val, int32 lnt); int32 ssc_rd (int32 pa); void ssc_wr (int32 pa, int32 val, int32 lnt); int32 tmr_tir_rd (int32 tmr, t_bool interp); void tmr_csr_wr (int32 tmr, int32 val); void tmr_sched (int32 tmr); void tmr_incr (int32 tmr, uint32 inc); int32 tmr0_inta (void); int32 tmr1_inta (void); int32 parity (int32 val, int32 odd); t_stat sysd_powerup (void); extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); extern int32 cqmap_rd (int32 pa); extern void cqmap_wr (int32 pa, int32 val, int32 lnt); extern int32 cqipc_rd (int32 pa); extern void cqipc_wr (int32 pa, int32 val, int32 lnt); extern int32 cqbic_rd (int32 pa); extern void cqbic_wr (int32 pa, int32 val, int32 lnt); extern int32 cqmem_rd (int32 pa); extern void cqmem_wr (int32 pa, int32 val, int32 lnt); extern int32 iccs_rd (void); extern int32 todr_rd (void); extern int32 rxcs_rd (void); extern int32 rxdb_rd (void); extern int32 txcs_rd (void); extern void iccs_wr (int32 dat); extern void todr_wr (int32 dat); extern void rxcs_wr (int32 dat); extern void txcs_wr (int32 dat); extern void txdb_wr (int32 dat); extern void ioreset_wr (int32 dat); extern uint32 sim_os_msec(); /* ROM data structures rom_dev ROM device descriptor rom_unit ROM units rom_reg ROM register list */ UNIT rom_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, ROMSIZE) }; REG rom_reg[] = { { NULL } }; MTAB rom_mod[] = { { UNIT_NODELAY, UNIT_NODELAY, "fast access", "NODELAY", NULL }, { UNIT_NODELAY, 0, "1usec calibrated access", "DELAY", NULL }, { 0 } }; DEVICE rom_dev = { "ROM", &rom_unit, rom_reg, rom_mod, 1, 16, ROMAWIDTH, 4, 16, 32, &rom_ex, &rom_dep, &rom_reset, NULL, NULL, NULL, NULL, 0 }; /* NVR data structures nvr_dev NVR device descriptor nvr_unit NVR units nvr_reg NVR register list */ UNIT nvr_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, NVRSIZE) }; REG nvr_reg[] = { { NULL } }; DEVICE nvr_dev = { "NVR", &nvr_unit, nvr_reg, NULL, 1, 16, NVRAWIDTH, 4, 16, 32, &nvr_ex, &nvr_dep, &nvr_reset, NULL, &nvr_attach, &nvr_detach, NULL, 0 }; /* CSI data structures csi_dev CSI device descriptor csi_unit CSI unit descriptor csi_reg CSI register list */ DIB csi_dib = { 0, 0, NULL, NULL, 1, IVCL (CSI), SCB_CSI, { NULL } }; UNIT csi_unit = { UDATA (NULL, 0, 0), KBD_POLL_WAIT }; REG csi_reg[] = { { ORDATA (BUF, csi_unit.buf, 8) }, { ORDATA (CSR, csi_csr, 16) }, { FLDATA (INT, int_req[IPL_CSI], INT_V_CSI) }, { FLDATA (DONE, csi_csr, CSR_V_DONE) }, { FLDATA (IE, csi_csr, CSR_V_IE) }, { DRDATA (POS, csi_unit.pos, 32), PV_LEFT }, { DRDATA (TIME, csi_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB csi_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, { 0 } }; DEVICE csi_dev = { "CSI", &csi_unit, csi_reg, csi_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &csi_reset, NULL, NULL, NULL, &csi_dib, 0 }; /* CSO data structures cso_dev CSO device descriptor cso_unit CSO unit descriptor cso_reg CSO register list */ DIB cso_dib = { 0, 0, NULL, NULL, 1, IVCL (CSO), SCB_CSO, { NULL } }; UNIT cso_unit = { UDATA (&cso_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG cso_reg[] = { { ORDATA (BUF, cso_unit.buf, 8) }, { ORDATA (CSR, cso_csr, 16) }, { FLDATA (INT, int_req[IPL_CSO], INT_V_CSO) }, { FLDATA (DONE, cso_csr, CSR_V_DONE) }, { FLDATA (IE, cso_csr, CSR_V_IE) }, { DRDATA (POS, cso_unit.pos, 32), PV_LEFT }, { DRDATA (TIME, cso_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB cso_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, { 0 } }; DEVICE cso_dev = { "CSO", &cso_unit, cso_reg, cso_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &cso_reset, NULL, NULL, NULL, &cso_dib, 0 }; /* SYSD data structures sysd_dev SYSD device descriptor sysd_unit SYSD units sysd_reg SYSD register list */ DIB sysd_dib[] = { 0, 0, NULL, NULL, 2, IVCL (TMR0), 0, { &tmr0_inta, &tmr1_inta } }; UNIT sysd_unit[] = { { UDATA (&tmr_svc, 0, 0) }, { UDATA (&tmr_svc, 0, 0) } }; REG sysd_reg[] = { { HRDATA (CADR, CADR, 8) }, { HRDATA (MSER, MSER, 8) }, { HRDATA (CONPC, conpc, 32) }, { HRDATA (CONPSL, conpsl, 32) }, { BRDATA (CMCSR, cmctl_reg, 16, 32, CMCTLSIZE >> 2) }, { HRDATA (CACR, ka_cacr, 8) }, { HRDATA (BDR, ka_bdr, 8) }, { HRDATA (BASE, ssc_base, 29) }, { HRDATA (CNF, ssc_cnf, 32) }, { HRDATA (BTO, ssc_bto, 32) }, { HRDATA (OTP, ssc_otp, 4) }, { HRDATA (TCSR0, tmr_csr[0], 32) }, { HRDATA (TIR0, tmr_tir[0], 32) }, { HRDATA (TNIR0, tmr_tnir[0], 32) }, { HRDATA (TIVEC0, tmr_tivr[0], 9) }, { HRDATA (TINC0, tmr_inc[0], 32) }, { HRDATA (TSAV0, tmr_sav[0], 32) }, { HRDATA (TCSR1, tmr_csr[1], 32) }, { HRDATA (TIR1, tmr_tir[1], 32) }, { HRDATA (TNIR1, tmr_tnir[1], 32) }, { HRDATA (TIVEC1, tmr_tivr[1], 9) }, { HRDATA (TINC1, tmr_inc[1], 32) }, { HRDATA (TSAV1, tmr_sav[1], 32) }, { HRDATA (ADSM0, ssc_adsm[0], 32) }, { HRDATA (ADSK0, ssc_adsk[0], 32) }, { HRDATA (ADSM1, ssc_adsm[1], 32) }, { HRDATA (ADSK1, ssc_adsk[1], 32) }, { BRDATA (CDGDAT, cdg_dat, 16, 32, CDASIZE >> 2) }, { NULL } }; DEVICE sysd_dev = { "SYSD", sysd_unit, sysd_reg, NULL, 2, 16, 16, 1, 16, 8, NULL, NULL, &sysd_reset, NULL, NULL, NULL, &sysd_dib, 0 }; /* ROM: read only memory - stored in a buffered file Register space access routines see ROM twice ROM access has been 'regulated' to about 1Mhz to avoid issues with testing the interval timers in self-test. Specifically, the VAX boot ROM (ka655.bin) contains code which presumes that the VAX runs at a particular slower speed when code is running from ROM (which is not cached). These assumptions are built into instruction based timing loops. As the host platform gets much faster than the original VAX, the assumptions embedded in these code loops are no longer valid. Code has been added to the ROM implementation to limit CPU speed to about 500K instructions per second. This heads off any future issues with the embedded timing loops. */ int32 rom_swapb(int32 val) { return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); } int32 rom_read_delay (int32 val) { uint32 i, l = rom_delay; int32 loopval = 0; if (rom_unit.flags & UNIT_NODELAY) return val; /* Calibrate the loop delay factor when first used. Do this 4 times to and use the largest value computed. */ if (rom_delay == 0) { uint32 ts, te, c = 10000, samples = 0; while (1) { c = c * 2; te = sim_os_msec(); while (te == (ts = sim_os_msec ())); /* align on ms tick */ /* This is merely a busy wait with some "work" that won't get optimized away by a good compiler. loopval always is zero. To avoid smart compilers, the loopval variable is referenced in the function arguments so that the function expression is not loop invariant. It also must be referenced by subsequent code or to avoid the whole computation being eliminated. */ for (i = 0; i < c; i++) loopval |= (loopval + ts) ^ rom_swapb (rom_swapb (loopval + ts)); te = sim_os_msec (); if ((te - ts) < 50) /* sample big enough? */ continue; if (rom_delay < (loopval + (c / (te - ts) / 1000) + 1)) rom_delay = loopval + (c / (te - ts) / 1000) + 1; if (++samples >= 4) break; c = c / 2; } if (rom_delay < 5) rom_delay = 5; } for (i = 0; i < l; i++) loopval |= (loopval + val) ^ rom_swapb (rom_swapb (loopval + val)); return val + loopval; } int32 rom_rd (int32 pa) { int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; return rom_read_delay (rom[rg]); } void rom_wr_B (int32 pa, int32 val) { int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; int32 sc = (pa & 3) << 3; rom[rg] = ((val & 0xFF) << sc) | (rom[rg] & ~(0xFF << sc)); return; } /* ROM examine */ t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; if ((vptr == NULL) || (addr & 03)) return SCPE_ARG; if (addr >= ROMSIZE) return SCPE_NXM; *vptr = rom[addr >> 2]; return SCPE_OK; } /* ROM deposit */ t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; if (addr & 03) return SCPE_ARG; if (addr >= ROMSIZE) return SCPE_NXM; rom[addr >> 2] = (uint32) val; return SCPE_OK; } /* ROM reset */ t_stat rom_reset (DEVICE *dptr) { if (rom == NULL) rom = (uint32 *) calloc (ROMSIZE >> 2, sizeof (uint32)); if (rom == NULL) return SCPE_MEM; return SCPE_OK; } /* NVR: non-volatile RAM - stored in a buffered file */ int32 nvr_rd (int32 pa) { int32 rg = (pa - NVRBASE) >> 2; return nvr[rg]; } void nvr_wr (int32 pa, int32 val, int32 lnt) { int32 rg = (pa - NVRBASE) >> 2; if (lnt < L_LONG) { /* byte or word? */ int32 sc = (pa & 3) << 3; /* merge */ int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; nvr[rg] = ((val & mask) << sc) | (nvr[rg] & ~(mask << sc)); } else nvr[rg] = val; return; } /* NVR examine */ t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; if ((vptr == NULL) || (addr & 03)) return SCPE_ARG; if (addr >= NVRSIZE) return SCPE_NXM; *vptr = nvr[addr >> 2]; return SCPE_OK; } /* NVR deposit */ t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; if (addr & 03) return SCPE_ARG; if (addr >= NVRSIZE) return SCPE_NXM; nvr[addr >> 2] = (uint32) val; return SCPE_OK; } /* NVR reset */ t_stat nvr_reset (DEVICE *dptr) { if (nvr == NULL) { nvr = (uint32 *) calloc (NVRSIZE >> 2, sizeof (uint32)); nvr_unit.filebuf = nvr; ssc_cnf = ssc_cnf | SSCCNF_BLO; } if (nvr == NULL) return SCPE_MEM; return SCPE_OK; } /* NVR attach */ t_stat nvr_attach (UNIT *uptr, char *cptr) { t_stat r; uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); r = attach_unit (uptr, cptr); if (r != SCPE_OK) uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); else { uptr->hwmark = (uint32) uptr->capac; ssc_cnf = ssc_cnf & ~SSCCNF_BLO; } return r; } /* NVR detach */ t_stat nvr_detach (UNIT *uptr) { t_stat r; r = detach_unit (uptr); if ((uptr->flags & UNIT_ATT) == 0) uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); return r; } /* CSI: console storage input */ int32 csrs_rd (void) { return (csi_csr & CSICSR_IMP); } int32 csrd_rd (void) { csi_csr = csi_csr & ~CSR_DONE; CLR_INT (CSI); return (csi_unit.buf & 0377); } void csrs_wr (int32 data) { if ((data & CSR_IE) == 0) CLR_INT (CSI); else if ((csi_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (CSI); csi_csr = (csi_csr & ~CSICSR_RW) | (data & CSICSR_RW); return; } t_stat csi_reset (DEVICE *dptr) { csi_unit.buf = 0; csi_csr = 0; CLR_INT (CSI); return SCPE_OK; } /* CSO: console storage output */ int32 csts_rd (void) { return (cso_csr & CSOCSR_IMP); } void csts_wr (int32 data) { if ((data & CSR_IE) == 0) CLR_INT (CSO); else if ((cso_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (CSO); cso_csr = (cso_csr & ~CSOCSR_RW) | (data & CSOCSR_RW); return; } void cstd_wr (int32 data) { cso_unit.buf = data & 0377; cso_csr = cso_csr & ~CSR_DONE; CLR_INT (CSO); sim_activate (&cso_unit, cso_unit.wait); return; } t_stat cso_svc (UNIT *uptr) { cso_csr = cso_csr | CSR_DONE; if (cso_csr & CSR_IE) SET_INT (CSO); if ((cso_unit.flags & UNIT_ATT) == 0) return SCPE_OK; if (putc (cso_unit.buf, cso_unit.fileref) == EOF) { perror ("CSO I/O error"); clearerr (cso_unit.fileref); return SCPE_IOERR; } cso_unit.pos = cso_unit.pos + 1; return SCPE_OK; } t_stat cso_reset (DEVICE *dptr) { cso_unit.buf = 0; cso_csr = CSR_DONE; CLR_INT (CSO); sim_cancel (&cso_unit); /* deactivate unit */ return SCPE_OK; } /* SYSD: SSC access mechanisms and devices - IPR space read/write routines - register space read/write routines - SSC local register read/write routines - SSC console storage UART - SSC timers - CMCTL local register read/write routines */ /* Read/write IPR register space These routines implement the SSC's response to IPR's which are sent off the CPU chip for processing. */ int32 ReadIPR (int32 rg) { int32 val; switch (rg) { case MT_ICCS: /* ICCS */ val = iccs_rd (); break; case MT_CSRS: /* CSRS */ val = csrs_rd (); break; case MT_CSRD: /* CSRD */ val = csrd_rd (); break; case MT_CSTS: /* CSTS */ val = csts_rd (); break; case MT_CSTD: /* CSTD */ val = 0; break; case MT_RXCS: /* RXCS */ val = rxcs_rd (); break; case MT_RXDB: /* RXDB */ val = rxdb_rd (); break; case MT_TXCS: /* TXCS */ val = txcs_rd (); break; case MT_TXDB: /* TXDB */ val = 0; break; case MT_TODR: /* TODR */ val = todr_rd (); break; case MT_CADR: /* CADR */ val = CADR & 0xFF; break; case MT_MSER: /* MSER */ val = MSER & 0xFF; break; case MT_CONPC: /* console PC */ val = conpc; break; case MT_CONPSL: /* console PSL */ val = conpsl; break; case MT_SID: /* SID */ val = CVAX_SID | CVAX_UREV; break; default: ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ val = 0; break; } return val; } void WriteIPR (int32 rg, int32 val) { switch (rg) { case MT_ICCS: /* ICCS */ iccs_wr (val); break; case MT_TODR: /* TODR */ todr_wr (val); break; case MT_CSRS: /* CSRS */ csrs_wr (val); break; case MT_CSRD: /* CSRD */ break; case MT_CSTS: /* CSTS */ csts_wr (val); break; case MT_CSTD: /* CSTD */ cstd_wr (val); break; case MT_RXCS: /* RXCS */ rxcs_wr (val); break; case MT_RXDB: /* RXDB */ break; case MT_TXCS: /* TXCS */ txcs_wr (val); break; case MT_TXDB: /* TXDB */ txdb_wr (val); break; case MT_CADR: /* CADR */ CADR = (val & CADR_RW) | CADR_MBO; break; case MT_MSER: /* MSER */ MSER = MSER & MSER_HM; break; case MT_IORESET: /* IORESET */ ioreset_wr (val); break; case MT_SID: case MT_CONPC: case MT_CONPSL: /* halt reg */ RSVD_OPND_FAULT; default: ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ break; } return; } /* Read/write I/O register space These routines are the 'catch all' for address space map. Any address that doesn't explicitly belong to memory, I/O, or ROM is given to these routines for processing. */ struct reglink { /* register linkage */ uint32 low; /* low addr */ uint32 high; /* high addr */ t_stat (*read)(int32 pa); /* read routine */ void (*write)(int32 pa, int32 val, int32 lnt); /* write routine */ }; struct reglink regtable[] = { { CQMAPBASE, CQMAPBASE+CQMAPSIZE, &cqmap_rd, &cqmap_wr }, { ROMBASE, ROMBASE+ROMSIZE+ROMSIZE, &rom_rd, NULL }, { NVRBASE, NVRBASE+NVRSIZE, &nvr_rd, &nvr_wr }, { CMCTLBASE, CMCTLBASE+CMCTLSIZE, &cmctl_rd, &cmctl_wr }, { SSCBASE, SSCBASE+SSCSIZE, &ssc_rd, &ssc_wr }, { KABASE, KABASE+KASIZE, &ka_rd, &ka_wr }, { CQBICBASE, CQBICBASE+CQBICSIZE, &cqbic_rd, &cqbic_wr }, { CQIPCBASE, CQIPCBASE+CQIPCSIZE, &cqipc_rd, &cqipc_wr }, { CQMBASE, CQMBASE+CQMSIZE, &cqmem_rd, &cqmem_wr }, { CDGBASE, CDGBASE+CDGSIZE, &cdg_rd, &cdg_wr }, { 0, 0, NULL, NULL } }; /* ReadReg - read register space Inputs: pa = physical address lnt = length (BWLQ) - ignored Output: longword of data */ int32 ReadReg (uint32 pa, int32 lnt) { struct reglink *p; for (p = ®table[0]; p->low != 0; p++) { if ((pa >= p->low) && (pa < p->high) && p->read) return p->read (pa); } ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; MACH_CHECK (MCHK_READ); return 0; } /* WriteReg - write register space Inputs: pa = physical address val = data to write, right justified in 32b longword lnt = length (BWLQ) Outputs: none */ void WriteReg (uint32 pa, int32 val, int32 lnt) { struct reglink *p; for (p = ®table[0]; p->low != 0; p++) { if ((pa >= p->low) && (pa < p->high) && p->write) { p->write (pa, val, lnt); return; } } ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; MACH_CHECK (MCHK_WRITE); return; } /* CMCTL registers CMCTL00 - 15 configure memory banks 00 - 15. Note that they are here merely to entertain the firmware; the actual configuration of memory is unaffected by the settings here. CMCTL16 - error status register CMCTL17 - control/diagnostic status register The CMCTL registers are cleared at power up. */ int32 cmctl_rd (int32 pa) { int32 rg = (pa - CMCTLBASE) >> 2; switch (rg) { default: /* config reg */ return cmctl_reg[rg] & CMCNF_MASK; case 16: /* err status */ return cmctl_reg[rg]; case 17: /* csr */ return cmctl_reg[rg] & CMCSR_MASK; case 18: /* KA655X ext mem */ if (MEMSIZE > MAXMEMSIZE) /* more than 128MB? */ return ((int32) MEMSIZE); MACH_CHECK (MCHK_READ); } return 0; } void cmctl_wr (int32 pa, int32 val, int32 lnt) { int32 i, rg = (pa - CMCTLBASE) >> 2; if (lnt < L_LONG) { /* LW write only */ int32 sc = (pa & 3) << 3; /* shift data to */ val = val << sc; /* proper location */ } switch (rg) { default: /* config reg */ if (val & CMCNF_SRQ) { /* sig request? */ int32 rg_g = rg & ~3; /* group of 4 */ for (i = rg_g; i < (rg_g + 4); i++) { cmctl_reg[i] = cmctl_reg[i] & ~CMCNF_SIG; if (ADDR_IS_MEM (i * MEM_BANK)) cmctl_reg[i] = cmctl_reg[i] | MEM_SIG; } } cmctl_reg[rg] = (cmctl_reg[rg] & ~CMCNF_RW) | (val & CMCNF_RW); break; case 16: /* err status */ cmctl_reg[rg] = cmctl_reg[rg] & ~(val & CMERR_W1C); break; case 17: /* csr */ cmctl_reg[rg] = val & CMCSR_MASK; break; case 18: MACH_CHECK (MCHK_WRITE); } return; } /* KA655 registers */ int32 ka_rd (int32 pa) { int32 rg = (pa - KABASE) >> 2; switch (rg) { case 0: /* CACR */ return ka_cacr; case 1: /* BDR */ return ka_bdr; } return 0; } void ka_wr (int32 pa, int32 val, int32 lnt) { int32 rg = (pa - KABASE) >> 2; if ((rg == 0) && ((pa & 3) == 0)) { /* lo byte only */ ka_cacr = (ka_cacr & ~(val & CACR_W1C)) | CACR_FIXED; ka_cacr = (ka_cacr & ~CACR_RW) | (val & CACR_RW); } return; } int32 sysd_hlt_enb (void) { return ka_bdr & BDR_BRKENB; } /* Cache diagnostic space */ int32 cdg_rd (int32 pa) { int32 t, row = CDG_GETROW (pa); t = cdg_dat[row]; ka_cacr = ka_cacr & ~CACR_DRO; /* clear diag */ ka_cacr = ka_cacr | (parity ((t >> 24) & 0xFF, 1) << (CACR_V_DPAR + 3)) | (parity ((t >> 16) & 0xFF, 0) << (CACR_V_DPAR + 2)) | (parity ((t >> 8) & 0xFF, 1) << (CACR_V_DPAR + 1)) | (parity (t & 0xFF, 0) << CACR_V_DPAR); return t; } void cdg_wr (int32 pa, int32 val, int32 lnt) { int32 row = CDG_GETROW (pa); if (lnt < L_LONG) { /* byte or word? */ int32 sc = (pa & 3) << 3; /* merge */ int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; int32 t = cdg_dat[row]; val = ((val & mask) << sc) | (t & ~(mask << sc)); } cdg_dat[row] = val; /* store data */ return; } int32 parity (int32 val, int32 odd) { for ( ; val != 0; val = val >> 1) { if (val & 1) odd = odd ^ 1; } return odd; } /* SSC registers - byte/word merges done in WriteReg */ int32 ssc_rd (int32 pa) { int32 rg = (pa - SSCBASE) >> 2; switch (rg) { case 0x00: /* base reg */ return ssc_base; case 0x04: /* conf reg */ return ssc_cnf; case 0x08: /* bus timeout */ return ssc_bto; case 0x0C: /* output port */ return ssc_otp & SSCOTP_MASK; case 0x1B: /* TODR */ return todr_rd (); case 0x1C: /* CSRS */ return csrs_rd (); case 0x1D: /* CSRD */ return csrd_rd (); case 0x1E: /* CSTS */ return csts_rd (); case 0x20: /* RXCS */ return rxcs_rd (); case 0x21: /* RXDB */ return rxdb_rd (); case 0x22: /* TXCS */ return txcs_rd (); case 0x40: /* T0CSR */ return tmr_csr[0]; case 0x41: /* T0INT */ return tmr_tir_rd (0, FALSE); case 0x42: /* T0NI */ return tmr_tnir[0]; case 0x43: /* T0VEC */ return tmr_tivr[0]; case 0x44: /* T1CSR */ return tmr_csr[1]; case 0x45: /* T1INT */ return tmr_tir_rd (1, FALSE); case 0x46: /* T1NI */ return tmr_tnir[1]; case 0x47: /* T1VEC */ return tmr_tivr[1]; case 0x4C: /* ADS0M */ return ssc_adsm[0]; case 0x4D: /* ADS0K */ return ssc_adsk[0]; case 0x50: /* ADS1M */ return ssc_adsm[1]; case 0x51: /* ADS1K */ return ssc_adsk[1]; } return 0; } void ssc_wr (int32 pa, int32 val, int32 lnt) { int32 rg = (pa - SSCBASE) >> 2; if (lnt < L_LONG) { /* byte or word? */ int32 sc = (pa & 3) << 3; /* merge */ int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; int32 t = ssc_rd (pa); val = ((val & mask) << sc) | (t & ~(mask << sc)); } switch (rg) { case 0x00: /* base reg */ ssc_base = (val & SSCBASE_RW) | SSCBASE_MBO; break; case 0x04: /* conf reg */ ssc_cnf = ssc_cnf & ~(val & SSCCNF_W1C); ssc_cnf = (ssc_cnf & ~SSCCNF_RW) | (val & SSCCNF_RW); break; case 0x08: /* bus timeout */ ssc_bto = ssc_bto & ~(val & SSCBTO_W1C); ssc_bto = (ssc_bto & ~SSCBTO_RW) | (val & SSCBTO_RW); break; case 0x0C: /* output port */ ssc_otp = val & SSCOTP_MASK; break; case 0x1B: /* TODR */ todr_wr (val); break; case 0x1C: /* CSRS */ csrs_wr (val); break; case 0x1E: /* CSTS */ csts_wr (val); break; case 0x1F: /* CSTD */ cstd_wr (val); break; case 0x20: /* RXCS */ rxcs_wr (val); break; case 0x22: /* TXCS */ txcs_wr (val); break; case 0x23: /* TXDB */ txdb_wr (val); break; case 0x40: /* T0CSR */ tmr_csr_wr (0, val); break; case 0x42: /* T0NI */ tmr_tnir[0] = val; break; case 0x43: /* T0VEC */ tmr_tivr[0] = val & TMR_VEC_MASK; break; case 0x44: /* T1CSR */ tmr_csr_wr (1, val); break; case 0x46: /* T1NI */ tmr_tnir[1] = val; break; case 0x47: /* T1VEC */ tmr_tivr[1] = val & TMR_VEC_MASK; break; case 0x4C: /* ADS0M */ ssc_adsm[0] = val & SSCADS_MASK; break; case 0x4D: /* ADS0K */ ssc_adsk[0] = val & SSCADS_MASK; break; case 0x50: /* ADS1M */ ssc_adsm[1] = val & SSCADS_MASK; break; case 0x51: /* ADS1K */ ssc_adsk[1] = val & SSCADS_MASK; break; } return; } /* Programmable timers The SSC timers, which increment at 1Mhz, cannot be accurately simulated due to the overhead that would be required for 1M clock events per second. Instead, a gross hack is used. When a timer is started, the clock interval is inspected. if (int < 0 and small) then testing timer, count instructions. Small is determined by when the requested interval is less than the size of a 100hz system clock tick. if (int >= 0 or large) then counting a real interval, schedule clock events at 100Hz using calibrated line clock delay and when the remaining time value gets small enough, behave like the small case above. If the interval register is read, then its value between events is interpolated using the current instruction count versus the count when the most recent event started, the result is scaled to the calibrated system clock, unless the interval being timed is less than a calibrated system clock tick (or the calibrated clock is running very slowly) at which time the result will be the elapsed instruction count. The powerup TOY Test sometimes fails its tolerance test. This was due to varying system load causing varying calibration values to be used at different times while referencing the TIR. While timing long intervals, we now synchronize the stepping (and calibration) of the system tick with the opportunity to reference the value. This gives precise tolerance measurement values (when interval timers are used to measure the system clock), regardless of other load issues on the host system which might cause varying values of the system clock's calibration factor. */ int32 tmr_tir_rd (int32 tmr, t_bool interp) { uint32 delta; if (interp || (tmr_csr[tmr] & TMR_CSR_RUN)) { /* interp, running? */ delta = sim_grtime () - tmr_sav[tmr]; /* delta inst */ if ((tmr_inc[tmr] == TMR_INC) && /* scale large int */ (tmr_poll > TMR_INC)) delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll); if (delta >= tmr_inc[tmr]) delta = tmr_inc[tmr] - 1; return tmr_tir[tmr] + delta; } return tmr_tir[tmr]; } void tmr_csr_wr (int32 tmr, int32 val) { if ((tmr < 0) || (tmr > 1)) return; if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ sim_cancel (&sysd_unit[tmr]); /* cancel timer */ if (tmr_csr[tmr] & TMR_CSR_RUN) /* run 1 -> 0? */ tmr_tir[tmr] = tmr_tir_rd (tmr, TRUE); /* update itr */ } tmr_csr[tmr] = tmr_csr[tmr] & ~(val & TMR_CSR_W1C); /* W1C csr */ tmr_csr[tmr] = (tmr_csr[tmr] & ~TMR_CSR_RW) | /* new r/w */ (val & TMR_CSR_RW); if (val & TMR_CSR_XFR) /* xfr set? */ tmr_tir[tmr] = tmr_tnir[tmr]; if (val & TMR_CSR_RUN) { /* run? */ if (val & TMR_CSR_XFR) /* new tir? */ sim_cancel (&sysd_unit[tmr]); /* stop prev */ if (!sim_is_active (&sysd_unit[tmr])) /* not running? */ tmr_sched (tmr); /* activate */ } else if (val & TMR_CSR_SGL) { /* single step? */ tmr_incr (tmr, 1); /* incr tmr */ if (tmr_tir[tmr] == 0) /* if ovflo, */ tmr_tir[tmr] = tmr_tnir[tmr]; /* reload tir */ } if ((tmr_csr[tmr] & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ (TMR_CSR_DON | TMR_CSR_IE)) { if (tmr) CLR_INT (TMR1); else CLR_INT (TMR0); } return; } /* Unit service */ t_stat tmr_svc (UNIT *uptr) { int32 tmr = uptr - sysd_dev.units; /* get timer # */ tmr_incr (tmr, tmr_inc[tmr]); /* incr timer */ return SCPE_OK; } /* Timer increment */ void tmr_incr (int32 tmr, uint32 inc) { uint32 new_tir = tmr_tir[tmr] + inc; /* add incr */ if (new_tir < tmr_tir[tmr]) { /* ovflo? */ tmr_tir[tmr] = 0; /* now 0 */ if (tmr_csr[tmr] & TMR_CSR_DON) /* done? set err */ tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_ERR; else tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_DON; /* set done */ if (tmr_csr[tmr] & TMR_CSR_STP) /* stop? */ tmr_csr[tmr] = tmr_csr[tmr] & ~TMR_CSR_RUN; /* clr run */ if (tmr_csr[tmr] & TMR_CSR_RUN) { /* run? */ tmr_tir[tmr] = tmr_tnir[tmr]; /* reload */ tmr_sched (tmr); /* reactivate */ } if (tmr_csr[tmr] & TMR_CSR_IE) { /* set int req */ if (tmr) SET_INT (TMR1); else SET_INT (TMR0); } } else { tmr_tir[tmr] = new_tir; /* no, upd tir */ if (tmr_csr[tmr] & TMR_CSR_RUN) /* still running? */ tmr_sched (tmr); /* reactivate */ } return; } /* Timer scheduling */ void tmr_sched (int32 tmr) { int32 clk_time = sim_is_active (&clk_unit) - 1; int32 tmr_time; tmr_sav[tmr] = sim_grtime (); /* save intvl base */ if (tmr_tir[tmr] > (0xFFFFFFFFu - TMR_INC)) { /* short interval? */ tmr_inc[tmr] = (~tmr_tir[tmr] + 1); /* inc = interval */ tmr_time = tmr_inc[tmr]; } else { tmr_inc[tmr] = TMR_INC; /* usec/interval */ tmr_time = tmr_poll; } if (tmr_time == 0) tmr_time = 1; if ((tmr_inc[tmr] == TMR_INC) && (tmr_time > clk_time)) { /* Align scheduled event to be identical to the event for the next clock tick. This lets us always see a consistent calibrated value, both for this scheduling, AND for any query of the current timer register that may happen in tmr_tir_rd (). This presumes that sim_activate will queue the interval timer behind the event for the clock tick. */ tmr_inc[tmr] = (uint32) (((double) clk_time * TMR_INC) / tmr_poll); tmr_time = clk_time; } sim_activate (&sysd_unit[tmr], tmr_time); return; } int32 tmr0_inta (void) { return tmr_tivr[0]; } int32 tmr1_inta (void) { return tmr_tivr[1]; } /* Machine check */ int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta) { int32 i, st1, st2, p2, hsir, acc; if (p1 & 0x80) /* mref? set v/p */ p1 = p1 + mchk_ref; p2 = mchk_va + 4; /* save vap */ for (i = hsir = 0; i < 16; i++) { /* find hsir */ if ((SISR >> i) & 1) hsir = i; } st1 = ((((uint32) opc) & 0xFF) << 24) | (hsir << 16) | ((CADR & 0xFF) << 8) | (MSER & 0xFF); st2 = 0x00C07000 + (delta & 0xFF); cc = intexc (SCB_MCHK, cc, 0, IE_SVE); /* take exception */ acc = ACC_MASK (KERN); /* in kernel mode */ in_ie = 1; SP = SP - 20; /* push 5 words */ Write (SP, 16, L_LONG, WA); /* # bytes */ Write (SP + 4, p1, L_LONG, WA); /* mcheck type */ Write (SP + 8, p2, L_LONG, WA); /* address */ Write (SP + 12, st1, L_LONG, WA); /* state 1 */ Write (SP + 16, st2, L_LONG, WA); /* state 2 */ in_ie = 0; return cc; } /* Console entry */ int32 con_halt (int32 code, int32 cc) { int32 temp; conpc = PC; /* save PC */ conpsl = ((PSL | cc) & 0xFFFF00FF) | CON_HLTINS; /* PSL, param */ temp = (PSL >> PSL_V_CUR) & 0x7; /* get is'cur */ if (temp > 4) /* invalid? */ conpsl = conpsl | CON_BADPSL; else STK[temp] = SP; /* save stack */ if (mapen) /* mapping on? */ conpsl = conpsl | CON_MAPON; mapen = 0; /* turn off map */ SP = IS; /* set SP from IS */ PSL = PSL_IS | PSL_IPL1F; /* PSL = 41F0000 */ JUMP (ROMBASE); /* PC = 20040000 */ return 0; /* new cc = 0 */ } /* Bootstrap */ t_stat cpu_boot (int32 unitno, DEVICE *dptr) { extern t_stat load_cmd (int32 flag, char *cptr); extern FILE *sim_log; t_stat r; PC = ROMBASE; PSL = PSL_IS | PSL_IPL1F; conpc = 0; conpsl = PSL_IS | PSL_IPL1F | CON_PWRUP; if (rom == NULL) return SCPE_IERR; if (*rom == 0) { /* no boot? */ printf ("Loading boot code from ka655x.bin\n"); if (sim_log) fprintf (sim_log, "Loading boot code from ka655x.bin\n"); r = load_cmd (0, "-R ka655x.bin"); if (r != SCPE_OK) return r; } return SCPE_OK; } /* SYSD reset */ t_stat sysd_reset (DEVICE *dptr) { int32 i; if (sim_switches & SWMASK ('P')) sysd_powerup (); /* powerup? */ for (i = 0; i < 2; i++) { tmr_csr[i] = tmr_tnir[i] = tmr_tir[i] = 0; tmr_inc[i] = tmr_sav[i] = 0; sim_cancel (&sysd_unit[i]); } csi_csr = 0; csi_unit.buf = 0; sim_cancel (&csi_unit); CLR_INT (CSI); cso_csr = CSR_DONE; cso_unit.buf = 0; sim_cancel (&cso_unit); CLR_INT (CSO); return SCPE_OK; } /* SYSD powerup */ t_stat sysd_powerup (void) { int32 i; for (i = 0; i < (CMCTLSIZE >> 2); i++) cmctl_reg[i] = 0; for (i = 0; i < 2; i++) { tmr_tivr[i] = 0; ssc_adsm[i] = ssc_adsk[i] = 0; } ka_cacr = 0; ssc_base = SSCBASE; ssc_cnf = ssc_cnf & SSCCNF_BLO; ssc_bto = 0; ssc_otp = 0; return SCPE_OK; } simh-3.8.1/VAX/vax_sys.c0000644000175000017500000021343211112314072013134 0ustar vlmvlm/* vax_sys.c: VAX simulator interface Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 19-Nov-08 RMS Moved bad block routine to I/O library 03-Nov-05 RMS Added 780 stop codes 04-Sep-05 RMS Fixed missing assignment (found by Peter Schorn) 16-Aug-05 RMS Fixed C++ declaration and cast problems 15-Sep-04 RMS Fixed bugs in character display and parse 30-Sep-04 RMS Fixed bugs in parsing indirect displacement modes Added compatibility mode support 04-Sep-04 RMS Added octa instruction support 02-Sep-04 RMS Fixed parse branch return status 13-Jul-04 RMS Fixed bad block routine 16-Jun-04 RMS Added DHQ11 support 21-Mar-04 RMS Added RXV21 support 06-May-03 RMS Added support for second DELQA 12-Oct-02 RMS Added multiple RQ controller support 10-Oct-02 RMS Added DELQA support 21-Sep-02 RMS Extended symbolic ex/mod to all byte devices 06-Sep-02 RMS Added TMSCP support 14-Jul-02 RMS Added infinite loop message */ #include "vax_defs.h" #include #if defined (FULL_VAX) #define ODC(x) (x) #else #define ODC(x) ((x) << DR_V_USPMASK) #endif extern UNIT cpu_unit; extern REG cpu_reg[]; extern int32 saved_PC; extern int32 PSL; extern int32 sim_switches; t_stat fprint_sym_m (FILE *of, uint32 addr, t_value *val); int32 fprint_sym_qoimm (FILE *of, t_value *val, int32 vp, int32 lnt); t_stat parse_char (char *cptr, t_value *val, int32 lnt); t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val); int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, int32 vp, int32 lnt, t_stat *r); int32 parse_spec (char *cptr, uint32 addr, t_value *val, int32 vp, int32 disp, t_stat *r); char *parse_rnum (char *cptr, int32 *rn); int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, int lnt, int32 minus); extern t_stat fprint_sym_cm (FILE *of, t_addr addr, t_value *bytes, int32 sw); extern t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, int32 sw); /* 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 */ REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 60; const char *sim_stop_messages[] = { "Unknown error", "HALT instruction", "Breakpoint", "CHMx on interrupt stack", "Invalid SCB vector", "Exception in interrupt or exception", "Process PTE in P0 or P1 space", "Interrupt at undefined IPL", "Fatal RQDX3 error", "Infinite loop", "Sanity timer expired", "Software done", "Reboot requested", "Unknown error", "Unknown abort code" }; /* Dispatch/decoder table The first entry contains: - FPD legal flag (DR_F) - number of specifiers for decode bits 2:0> - number of specifiers for unimplemented instructions bits<6:4> */ const uint16 drom[NUM_INST][MAX_SPEC + 1] = { 0, 0, 0, 0, 0, 0, 0, /* HALT */ 0, 0, 0, 0, 0, 0, 0, /* NOP */ 0, 0, 0, 0, 0, 0, 0, /* REI */ 0, 0, 0, 0, 0, 0, 0, /* BPT */ 0, 0, 0, 0, 0, 0, 0, /* RET */ 0, 0, 0, 0, 0, 0, 0, /* RSB */ 0, 0, 0, 0, 0, 0, 0, /* LDPCTX */ 0, 0, 0, 0, 0, 0, 0, /* SVPCTX */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* CVTPS */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* CVTSP */ 6, RL, RL, RL, RL, RL, WL, /* INDEX */ 4+DR_F, AB, RL, RW, AB, 0, 0, /* CRC */ 3, RB, RW, AB, 0, 0, 0, /* PROBER */ 3, RB, RW, AB, 0, 0, 0, /* PROBEW */ 2, AB, AB, 0, 0, 0, 0, /* INSQUE */ 2, AB, WL, 0, 0, 0, 0, /* REMQUE */ 1, BB, 0, 0, 0, 0, 0, /* BSBB */ 1, BB, 0, 0, 0, 0, 0, /* BRB */ 1, BB, 0, 0, 0, 0, 0, /* BNEQ */ 1, BB, 0, 0, 0, 0, 0, /* BEQL */ 1, BB, 0, 0, 0, 0, 0, /* BGTR */ 1, BB, 0, 0, 0, 0, 0, /* BLEQ */ 1, AB, 0, 0, 0, 0, 0, /* JSB */ 1, AB, 0, 0, 0, 0, 0, /* JMP */ 1, BB, 0, 0, 0, 0, 0, /* BGEQ */ 1, BB, 0, 0, 0, 0, 0, /* BLSS */ 1, BB, 0, 0, 0, 0, 0, /* BGTRU */ 1, BB, 0, 0, 0, 0, 0, /* BLEQU */ 1, BB, 0, 0, 0, 0, 0, /* BVC */ 1, BB, 0, 0, 0, 0, 0, /* BVS */ 1, BB, 0, 0, 0, 0, 0, /* BCC */ 1, BB, 0, 0, 0, 0, 0, /* BCS */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* ADDP4 */ 6+DR_F, RW, AB, RW, AB, RW, AB, /* ADDP6 */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* SUBP4 */ 6+DR_F, RW, AB, RW, AB, RW, AB, /* SUBP6 */ 5+DR_F, RW, AB, AB, RW, AB, 0, /* CVTPT */ 6+DR_F, RW, AB, RW, AB, RW, AB, /* MULP6 */ 5+DR_F, RW, AB, AB, RW, AB, 0, /* CVTTP */ 6+DR_F, RW, AB, RW, AB, RW, AB, /* DIVP6 */ 3+DR_F, RW, AB, AB, 0, 0, 0, /* MOVC3 */ 3+DR_F, RW, AB, AB, 0, 0, 0, /* CMPC3 */ 4+DR_F, RW, AB, AB, RB, 0, 0, /* SCANC */ 4+DR_F, RW, AB, AB, RB, 0, 0, /* SPANC */ 5+DR_F, RW, AB, RB, RW, AB, 0, /* MOVC5 */ 5+DR_F, RW, AB, RB, RW, AB, 0, /* CMPC5 */ 6+DR_F, RW, AB, RB, AB, RW, AB, /* MOVTC */ 6+DR_F, RW, AB, RB, AB, RW, AB, /* MOVTUC */ 1, BW, 0, 0, 0, 0, 0, /* BSBW */ 1, BW, 0, 0, 0, 0, 0, /* BRW */ 2, RW, WL, 0, 0, 0, 0, /* CVTWL */ 2, RW, WB, 0, 0, 0, 0, /* CVTWB */ 3+DR_F, RW, AB, AB, 0, 0, 0, /* MOVP */ 3+DR_F, RW, AB, AB, 0, 0, 0, /* CMPP3 */ 3+DR_F, RW, AB, WL, 0, 0, 0, /* CVTPL */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* CMPP4 */ 4+DR_F, RW, AB, AB, AB, 0, 0, /* EDITPC */ 4+DR_F, RW, AB, RW, AB, 0, 0, /* MATCHC */ 3+DR_F, RB, RW, AB, 0, 0, 0, /* LOCC */ 3+DR_F, RB, RW, AB, 0, 0, 0, /* SKPC */ 2, RW, WL, 0, 0, 0, 0, /* MOVZWL */ 4, RW, RW, MW, BW, 0, 0, /* ACBW */ 2, AW, WL, 0, 0, 0, 0, /* MOVAW */ 1, AW, 0, 0, 0, 0, 0, /* PUSHAW */ 2, RF, ML, 0, 0, 0, 0, /* ADDF2 */ 3, RF, RF, WL, 0, 0, 0, /* ADDF3 */ 2, RF, ML, 0, 0, 0, 0, /* SUBF2 */ 3, RF, RF, WL, 0, 0, 0, /* SUBF3 */ 2, RF, ML, 0, 0, 0, 0, /* MULF2 */ 3, RF, RF, WL, 0, 0, 0, /* MULF3 */ 2, RF, ML, 0, 0, 0, 0, /* DIVF2 */ 3, RF, RF, WL, 0, 0, 0, /* DIVF3 */ 2, RF, WB, 0, 0, 0, 0, /* CVTFB */ 2, RF, WW, 0, 0, 0, 0, /* CVTFW */ 2, RF, WL, 0, 0, 0, 0, /* CVTFL */ 2, RF, WL, 0, 0, 0, 0, /* CVTRFL */ 2, RB, WL, 0, 0, 0, 0, /* CVTBF */ 2, RW, WL, 0, 0, 0, 0, /* CVTWF */ 2, RL, WL, 0, 0, 0, 0, /* CVTLF */ 4, RF, RF, ML, BW, 0, 0, /* ACBF */ 2, RF, WL, 0, 0, 0, 0, /* MOVF */ 2, RF, RF, 0, 0, 0, 0, /* CMPF */ 2, RF, WL, 0, 0, 0, 0, /* MNEGF */ 1, RF, 0, 0, 0, 0, 0, /* TSTF */ 5, RF, RB, RF, WL, WL, 0, /* EMODF */ 3, RF, RW, AB, 0, 0, 0, /* POLYF */ 2, RF, WQ, 0, 0, 0, 0, /* CVTFD */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 2, RW, WW, 0, 0, 0, 0, /* ADAWI */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 2, AB, AQ, 0, 0, 0, 0, /* INSQHI */ 2, AB, AQ, 0, 0, 0, 0, /* INSQTI */ 2, AQ, WL, 0, 0, 0, 0, /* REMQHI */ 2, AQ, WL, 0, 0, 0, 0, /* REMQTI */ 2, RD, MQ, 0, 0, 0, 0, /* ADDD2 */ 3, RD, RD, WQ, 0, 0, 0, /* ADDD3 */ 2, RD, MQ, 0, 0, 0, 0, /* SUBD2 */ 3, RD, RD, WQ, 0, 0, 0, /* SUBD3 */ 2, RD, MQ, 0, 0, 0, 0, /* MULD2 */ 3, RD, RD, WQ, 0, 0, 0, /* MULD3 */ 2, RD, MQ, 0, 0, 0, 0, /* DIVD2 */ 3, RD, RD, WQ, 0, 0, 0, /* DIVD3 */ 2, RD, WB, 0, 0, 0, 0, /* CVTDB */ 2, RD, WW, 0, 0, 0, 0, /* CVTDW */ 2, RD, WL, 0, 0, 0, 0, /* CVTDL */ 2, RD, WL, 0, 0, 0, 0, /* CVTRDL */ 2, RB, WQ, 0, 0, 0, 0, /* CVTBD */ 2, RW, WQ, 0, 0, 0, 0, /* CVTWD */ 2, RL, WQ, 0, 0, 0, 0, /* CVTLD */ 4, RD, RD, MQ, BW, 0, 0, /* ACBD */ 2, RD, WQ, 0, 0, 0, 0, /* MOVD */ 2, RD, RD, 0, 0, 0, 0, /* CMPD */ 2, RD, WQ, 0, 0, 0, 0, /* MNEGD */ 1, RD, 0, 0, 0, 0, 0, /* TSTD */ 5, RD, RB, RD, WL, WQ, 0, /* EMODD */ 3, RD, RW, AB, 0, 0, 0, /* POLYD */ 2, RD, WL, 0, 0, 0, 0, /* CVTDF */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 3, RB, RL, WL, 0, 0, 0, /* ASHL */ 3, RB, RQ, WQ, 0, 0, 0, /* ASHQ */ 4, RL, RL, RL, WQ, 0, 0, /* EMUL */ 4, RL, RQ, WL, WL, 0, 0, /* EDIV */ 1, WQ, 0, 0, 0, 0, 0, /* CLRQ */ 2, RQ, WQ, 0, 0, 0, 0, /* MOVQ */ 2, AQ, WL, 0, 0, 0, 0, /* MOVAQ */ 1, AQ, 0, 0, 0, 0, 0, /* PUSHAQ */ 2, RB, MB, 0, 0, 0, 0, /* ADDB2 */ 3, RB, RB, WB, 0, 0, 0, /* ADDB3 */ 2, RB, MB, 0, 0, 0, 0, /* SUBB2 */ 3, RB, RB, WB, 0, 0, 0, /* SUBB3 */ 2, RB, MB, 0, 0, 0, 0, /* MULB2 */ 3, RB, RB, WB, 0, 0, 0, /* MULB3 */ 2, RB, MB, 0, 0, 0, 0, /* DIVB2 */ 3, RB, RB, WB, 0, 0, 0, /* DIVB3 */ 2, RB, MB, 0, 0, 0, 0, /* BISB2 */ 3, RB, RB, WB, 0, 0, 0, /* BISB3 */ 2, RB, MB, 0, 0, 0, 0, /* BICB2 */ 3, RB, RB, WB, 0, 0, 0, /* BICB3 */ 2, RB, MB, 0, 0, 0, 0, /* XORB2 */ 3, RB, RB, WB, 0, 0, 0, /* XORB3 */ 2, RB, WB, 0, 0, 0, 0, /* MNEGB */ 3, RB, RB, RB, 0, 0, 0, /* CASEB */ 2, RB, WB, 0, 0, 0, 0, /* MOVB */ 2, RB, RB, 0, 0, 0, 0, /* CMPB */ 2, RB, WB, 0, 0, 0, 0, /* MCOMB */ 2, RB, RB, 0, 0, 0, 0, /* BITB */ 1, WB, 0, 0, 0, 0, 0, /* CLRB */ 1, RB, 0, 0, 0, 0, 0, /* TSTB */ 1, MB, 0, 0, 0, 0, 0, /* INCB */ 1, MB, 0, 0, 0, 0, 0, /* DECB */ 2, RB, WL, 0, 0, 0, 0, /* CVTBL */ 2, RB, WW, 0, 0, 0, 0, /* CVTBW */ 2, RB, WL, 0, 0, 0, 0, /* MOVZBL */ 2, RB, WW, 0, 0, 0, 0, /* MOVZBW */ 3, RB, RL, WL, 0, 0, 0, /* ROTL */ 4, RB, RB, MB, BW, 0, 0, /* ACBB */ 2, AB, WL, 0, 0, 0, 0, /* MOVAB */ 1, AB, 0, 0, 0, 0, 0, /* PUSHAB */ 2, RW, MW, 0, 0, 0, 0, /* ADDW2 */ 3, RW, RW, WW, 0, 0, 0, /* ADDW3 */ 2, RW, MW, 0, 0, 0, 0, /* SUBW2 */ 3, RW, RW, WW, 0, 0, 0, /* SUBW3 */ 2, RW, MW, 0, 0, 0, 0, /* MULW2 */ 3, RW, RW, WW, 0, 0, 0, /* MULW3 */ 2, RW, MW, 0, 0, 0, 0, /* DIVW2 */ 3, RW, RW, WW, 0, 0, 0, /* DIVW3 */ 2, RW, MW, 0, 0, 0, 0, /* BISW2 */ 3, RW, RW, WW, 0, 0, 0, /* BISW3 */ 2, RW, MW, 0, 0, 0, 0, /* BICW2 */ 3, RW, RW, WW, 0, 0, 0, /* BICW3 */ 2, RW, MW, 0, 0, 0, 0, /* XORW2 */ 3, RW, RW, WW, 0, 0, 0, /* XORW3 */ 2, RW, WW, 0, 0, 0, 0, /* MNEGW */ 3, RW, RW, RW, 0, 0, 0, /* CASEW */ 2, RW, WW, 0, 0, 0, 0, /* MOVW */ 2, RW, RW, 0, 0, 0, 0, /* CMPW */ 2, RW, WW, 0, 0, 0, 0, /* MCOMW */ 2, RW, RW, 0, 0, 0, 0, /* BITW */ 1, WW, 0, 0, 0, 0, 0, /* CLRW */ 1, RW, 0, 0, 0, 0, 0, /* TSTW */ 1, MW, 0, 0, 0, 0, 0, /* INCW */ 1, MW, 0, 0, 0, 0, 0, /* DECW */ 1, RW, 0, 0, 0, 0, 0, /* BISPSW */ 1, RW, 0, 0, 0, 0, 0, /* BICPSW */ 1, RW, 0, 0, 0, 0, 0, /* POPR */ 1, RW, 0, 0, 0, 0, 0, /* PUSHR */ 1, RW, 0, 0, 0, 0, 0, /* CHMK */ 1, RW, 0, 0, 0, 0, 0, /* CHME */ 1, RW, 0, 0, 0, 0, 0, /* CHMS */ 1, RW, 0, 0, 0, 0, 0, /* CHMU */ 2, RL, ML, 0, 0, 0, 0, /* ADDL2 */ 3, RL, RL, WL, 0, 0, 0, /* ADDL3 */ 2, RL, ML, 0, 0, 0, 0, /* SUBL2 */ 3, RL, RL, WL, 0, 0, 0, /* SUBL3 */ 2, RL, ML, 0, 0, 0, 0, /* MULL2 */ 3, RL, RL, WL, 0, 0, 0, /* MULL3 */ 2, RL, ML, 0, 0, 0, 0, /* DIVL2 */ 3, RL, RL, WL, 0, 0, 0, /* DIVL3 */ 2, RL, ML, 0, 0, 0, 0, /* BISL2 */ 3, RL, RL, WL, 0, 0, 0, /* BISL3 */ 2, RL, ML, 0, 0, 0, 0, /* BICL2 */ 3, RL, RL, WL, 0, 0, 0, /* BICL3 */ 2, RL, ML, 0, 0, 0, 0, /* XORL2 */ 3, RL, RL, WL, 0, 0, 0, /* XORL3 */ 2, RL, WL, 0, 0, 0, 0, /* MNEGL */ 3, RL, RL, RL, 0, 0, 0, /* CASEL */ 2, RL, WL, 0, 0, 0, 0, /* MOVL */ 2, RL, RL, 0, 0, 0, 0, /* CMPL */ 2, RL, WL, 0, 0, 0, 0, /* MCOML */ 2, RL, RL, 0, 0, 0, 0, /* BITL */ 1, WL, 0, 0, 0, 0, 0, /* CLRL */ 1, RL, 0, 0, 0, 0, 0, /* TSTL */ 1, ML, 0, 0, 0, 0, 0, /* INCL */ 1, ML, 0, 0, 0, 0, 0, /* DECL */ 2, RL, ML, 0, 0, 0, 0, /* ADWC */ 2, RL, ML, 0, 0, 0, 0, /* SBWC */ 2, RL, RL, 0, 0, 0, 0, /* MTPR */ 2, RL, WL, 0, 0, 0, 0, /* MFPR */ 1, WL, 0, 0, 0, 0, 0, /* MOVPSL */ 1, RL, 0, 0, 0, 0, 0, /* PUSHL */ 2, AL, WL, 0, 0, 0, 0, /* MOVAL */ 1, AL, 0, 0, 0, 0, 0, /* PUSHAL */ 3, RL, VB, BB, 0, 0, 0, /* BBS */ 3, RL, VB, BB, 0, 0, 0, /* BBC */ 3, RL, VB, BB, 0, 0, 0, /* BBSS */ 3, RL, VB, BB, 0, 0, 0, /* BBCS */ 3, RL, VB, BB, 0, 0, 0, /* BBSC */ 3, RL, VB, BB, 0, 0, 0, /* BBCC */ 3, RL, VB, BB, 0, 0, 0, /* BBSSI */ 3, RL, VB, BB, 0, 0, 0, /* BBCCI */ 2, RL, BB, 0, 0, 0, 0, /* BLBS */ 2, RL, BB, 0, 0, 0, 0, /* BLBC */ 4, RL, RB, VB, WL, 0, 0, /* FFS */ 4, RL, RB, VB, WL, 0, 0, /* FFC */ 4, RL, RB, VB, RL, 0, 0, /* CMPV */ 4, RL, RB, VB, RL, 0, 0, /* CMPZV */ 4, RL, RB, VB, WL, 0, 0, /* EXTV */ 4, RL, RB, VB, WL, 0, 0, /* EXTZV */ 4, RL, RL, RB, VB, 0, 0, /* INSV */ 4, RL, RL, ML, BW, 0, 0, /* ACBL */ 3, RL, ML, BB, 0, 0, 0, /* AOBLSS */ 3, RL, ML, BB, 0, 0, 0, /* AOBLEQ */ 2, ML, BB, 0, 0, 0, 0, /* SOBGEQ */ 2, ML, BB, 0, 0, 0, 0, /* SOBGTR */ 2, RL, WB, 0, 0, 0, 0, /* CVTLB */ 2, RL, WW, 0, 0, 0, 0, /* CVTLW */ 6+DR_F, RB, RW, AB, RB, RW, AB, /* ASHP */ 3+DR_F, RL, RW, AB, 0, 0, 0, /* CVTLP */ 2, AB, AB, 0, 0, 0, 0, /* CALLG */ 2, RL, AB, 0, 0, 0, 0, /* CALLS */ 0, 0, 0, 0, 0, 0, 0, /* XFC */ 0, 0, 0, 0, 0, 0, 0, /* 0FD */ 0, 0, 0, 0, 0, 0, 0, /* 0FE */ 0, 0, 0, 0, 0, 0, 0, /* 0FF */ 0, 0, 0, 0, 0, 0, 0, /* 100-10F */ 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, /* 110-11F */ 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, /* 120-12F */ 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, /* 130-13F */ 0, 0, 0, 0, 0, 0, 0, ODC(2), RD, WO, 0, 0, 0, 0, /* CVTDH */ 2, RG, WL, 0, 0, 0, 0, /* CVTGF */ 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, 2, RG, MQ, 0, 0, 0, 0, /* ADDG2 */ 3, RG, RG, WQ, 0, 0, 0, /* ADDG3 */ 2, RG, MQ, 0, 0, 0, 0, /* SUBG2 */ 3, RG, RG, WQ, 0, 0, 0, /* SUBG3 */ 2, RG, MQ, 0, 0, 0, 0, /* MULG2 */ 3, RG, RG, WQ, 0, 0, 0, /* MULG3 */ 2, RG, MQ, 0, 0, 0, 0, /* DIVG2 */ 3, RG, RG, WQ, 0, 0, 0, /* DIVG3 */ 2, RG, WB, 0, 0, 0, 0, /* CVTGB */ 2, RG, WW, 0, 0, 0, 0, /* CVTGW */ 2, RG, WL, 0, 0, 0, 0, /* CVTGL */ 2, RG, WL, 0, 0, 0, 0, /* CVTRGL */ 2, RB, WQ, 0, 0, 0, 0, /* CVTBG */ 2, RW, WQ, 0, 0, 0, 0, /* CVTWG */ 2, RL, WQ, 0, 0, 0, 0, /* CVTLG */ 4, RG, RG, MQ, BW, 0, 0, /* ACBG */ 2, RG, WQ, 0, 0, 0, 0, /* MOVG */ 2, RG, RG, 0, 0, 0, 0, /* CMPG */ 2, RG, WQ, 0, 0, 0, 0, /* MNEGG */ 1, RG, 0, 0, 0, 0, 0, /* TSTG */ 5, RG, RW, RG, WL, WQ, 0, /* EMODG */ 3, RG, RW, AB, 0, 0, 0, /* POLYG */ ODC(2), RG, WO, 0, 0, 0, 0, /* CVTGH */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ ODC(2), RH, MO, 0, 0, 0, 0, /* ADDH2 */ ODC(3), RH, RH, WO, 0, 0, 0, /* ADDH3 */ ODC(2), RH, MO, 0, 0, 0, 0, /* SUBH2 */ ODC(3), RH, RH, WO, 0, 0, 0, /* SUBH3 */ ODC(2), RH, MO, 0, 0, 0, 0, /* MULH2 */ ODC(3), RH, RH, WO, 0, 0, 0, /* MULH3 */ ODC(2), RH, MO, 0, 0, 0, 0, /* DIVH2 */ ODC(3), RH, RH, WO, 0, 0, 0, /* DIVH3 */ ODC(2), RH, WB, 0, 0, 0, 0, /* CVTHB */ ODC(2), RH, WW, 0, 0, 0, 0, /* CVTHW */ ODC(2), RH, WL, 0, 0, 0, 0, /* CVTHL */ ODC(2), RH, WL, 0, 0, 0, 0, /* CVTRHL */ ODC(2), RB, WO, 0, 0, 0, 0, /* CVTBH */ ODC(2), RW, WO, 0, 0, 0, 0, /* CVTWH */ ODC(2), RL, WO, 0, 0, 0, 0, /* CVTLH */ ODC(4), RH, RH, MO, BW, 0, 0, /* ACBH */ ODC(2), RH, RO, 0, 0, 0, 0, /* MOVH */ ODC(2), RH, RH, 0, 0, 0, 0, /* CMPH */ ODC(2), RH, WO, 0, 0, 0, 0, /* MNEGH */ ODC(1), RH, 0, 0, 0, 0, 0, /* TSTH */ ODC(5), RH, RW, RH, WL, WO, 0, /* EMODH */ ODC(3), RH, RW, AB, 0, 0, 0, /* POLYH */ ODC(2), RH, WQ, 0, 0, 0, 0, /* CVTHG */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ 0, 0, 0, 0, 0, 0, 0, /* reserved */ ODC(1), WO, 0, 0, 0, 0, 0, /* CLRO */ ODC(2), RO, RO, 0, 0, 0, 0, /* MOVO */ ODC(2), AO, WL, 0, 0, 0, 0, /* MOVAO*/ ODC(1), AO, 0, 0, 0, 0, 0, /* PUSHAO*/ 0, 0, 0, 0, 0, 0, 0, /* 180-18F */ 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, /* 190-19F */ 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, ODC(2), RF, WO, 0, 0, 0, 0, /* CVTFH */ 2, RF, WQ, 0, 0, 0, 0, /* CVTFG */ 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, /* 1A0-1AF */ 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, /* 1B0-1BF */ 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, /* 1C0-1CF */ 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, /* 1D0-1DF */ 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, /* 1E0-1EF */ 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, /* 1F0-1FF */ 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, ODC(2), RH, WL, 0, 0, 0, 0, /* CVTHF */ ODC(2), RH, WQ, 0, 0, 0, 0, /* CVTHD */ 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 }; /* Opcode mnemonics table */ const char *opcode[] = { "HALT", "NOP", "REI", "BPT", "RET", "RSB", "LDPCTX", "SVPCTX", "CVTPS", "CVTSP", "INDEX", "CRC", "PROBER", "PROBEW", "INSQUE", "REMQUE", "BSBB", "BRB", "BNEQ", "BEQL", "BGTR", "BLEQ", "JSB", "JMP", "BGEQ", "BLSS", "BGTRU", "BLEQU", "BVC", "BVS", "BGEQU", "BLSSU", "ADDP4", "ADDP6", "SUBP4", "SUBP6", "CVTPT", "MULP", "CVTTP", "DIVP", "MOVC3", "CMPC3", "SCANC", "SPANC", "MOVC5", "CMPC5", "MOVTC", "MOVTUC", "BSBW", "BRW", "CVTWL", "CVTWB", "MOVP", "CMPP3", "CVTPL", "CMPP4", "EDITPC", "MATCHC", "LOCC", "SKPC", "MOVZWL", "ACBW", "MOVAW", "PUSHAW", "ADDF2", "ADDF3", "SUBF2", "SUBF3", "MULF2", "MULF3", "DIVF2", "DIVF3", "CVTFB", "CVTFW", "CVTFL", "CVTRFL", "CVTBF", "CVTWF", "CVTLF", "ACBF", "MOVF", "CMPF", "MNEGF", "TSTF", "EMODF", "POLYF", "CVTFD", NULL, "ADAWI", NULL, NULL, NULL, "INSQHI", "INSQTI", "REMQHI", "REMQTI", "ADDD2", "ADDD3", "SUBD2", "SUBD3", "MULD2", "MULD3", "DIVD2", "DIVD3", "CVTDB", "CVTDW", "CVTDL", "CVTRDL", "CVTBD", "CVTWD", "CVTLD", "ACBD", "MOVD", "CMPD", "MNEGD", "TSTD", "EMODD", "POLYD", "CVTDF", NULL, "ASHL", "ASHQ", "EMUL", "EDIV", "CLRQ", "MOVQ", "MOVAQ", "PUSHAQ", "ADDB2", "ADDB3", "SUBB2", "SUBB3", "MULB2", "MULB3", "DIVB2", "DIVB3", "BISB2", "BISB3", "BICB2", "BICB3", "XORB2", "XORB3", "MNEGB", "CASEB", "MOVB", "CMPB", "MCOMB", "BITB", "CLRB", "TSTB", "INCB", "DECB", "CVTBL", "CVTBW", "MOVZBL", "MOVZBW", "ROTL", "ACBB", "MOVAB", "PUSHAB", "ADDW2", "ADDW3", "SUBW2", "SUBW3", "MULW2", "MULW3", "DIVW2", "DIVW3", "BISW2", "BISW3", "BICW2", "BICW3", "XORW2", "XORW3", "MNEGW", "CASEW", "MOVW", "CMPW", "MCOMW", "BITW", "CLRW", "TSTW", "INCW", "DECW", "BISPSW", "BICPSW", "POPR", "PUSHR", "CHMK", "CHME", "CHMS", "CHMU", "ADDL2", "ADDL3", "SUBL2", "SUBL3", "MULL2", "MULL3", "DIVL2", "DIVL3", "BISL2", "BISL3", "BICL2", "BICL3", "XORL2", "XORL3", "MNEGL", "CASEL", "MOVL", "CMPL", "MCOML", "BITL", "CLRL", "TSTL", "INCL", "DECL", "ADWC", "SBWC", "MTPR", "MFPR", "MOVPSL", "PUSHL", "MOVAL", "PUSHAL", "BBS", "BBC", "BBSS", "BBCS", "BBSC", "BBCC", "BBSSI", "BBCCI", "BLBS", "BLBC", "FFS", "FFC", "CMPV", "CMPZV", "EXTV", "EXTZV", "INSV", "ACBL", "AOBLSS", "AOBLEQ", "SOBGEQ", "SOBGTR", "CVTLB", "CVTLW", "ASHP", "CVTLP", "CALLG", "CALLS", "XFC", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 100 - 11F */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 120 - 13F */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "CVTDH", "CVTGF", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "ADDG2", "ADDG3", "SUBG2", "SUBG3", "MULG2", "MULG3", "DIVG2", "DIVG3", "CVTGB", "CVTGW", "CVTGL", "CVTRGL", "CVTBG", "CVTWG", "CVTLG", "ACBG", "MOVG", "CMPG", "MNEGG", "TSTG", "EMODG", "POLYG", "CVTGH", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "ADDH2", "ADDH3", "SUBH2", "SUBH3", "MULH2", "MULH3", "DIVH2", "DIVH3", "CVTHB", "CVTHW", "CVTHL", "CVTRHL", "CVTBH", "CVTWH", "CVTLH", "ACBH", "MOVH", "CMPH", "MNEGH", "TSTH", "EMODH", "POLYH", "CVTHG", NULL, NULL, NULL, NULL, NULL, "CLRO", "MOVO", "MOVAO", "PUSHAO", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 180 - 19F */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "CVTFH", "CVTFG", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1A0 - 1BF */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1C0 - 1DF */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1E0 - 1FF */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "CVTHF", "CVTHD", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; const char *altcod[] = { "CLRF", "CLRD", "CLRG", "CLRH", "MOVAF", "MOVAD", "MOVAG", "MOVAH", "PUSHAF", "PUSHAD", "PUSHAG", "PUSHAH", "BNEQU", "BEQLU", "BCC", "BCS", NULL }; const int32 altop[] = { 0xD4, 0x7C, 0x7C, 0x17C, 0xDE, 0x7E, 0x7E, 0x17E, 0xDF, 0x7F, 0x7F, 0x17F, 0x12, 0x13, 0x1E, 0x1F }; const char* regname[] = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "AP", "FP", "SP", "PC" }; #define GETNUM(d,n) for (k = d = 0; k < n; k++) \ d = d | (((int32) val[vp++]) << (k * 8)) /* Symbolic decode Inputs: *of = output stream addr = current PC *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym (FILE *of, t_addr exta, t_value *val, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; int32 c, k, num, vp, lnt, rdx; t_stat r; DEVICE *dptr; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; if ((sw & SIM_SW_STOP) && (PSL & PSL_CM)) /* stop in CM? */ sw = sw | SWMASK ('P'); /* force CM print */ dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth != 8) /* byte dev only */ return SCPE_ARG; if (sw & SWMASK ('B')) /* get length */ lnt = 1; else if (sw & SWMASK ('W')) lnt = 2; else if (sw & SWMASK ('L')) lnt = 4; else lnt = (uptr == &cpu_unit)? 4: 1; if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if ((sw & SWMASK ('A')) || (sw & SWMASK ('C'))) { /* char format? */ for (vp = lnt - 1; vp >= 0; vp--) { c = (int32) val[vp] & 0x7F; fprintf (of, (c < 0x20)? "<%02X>": "%c", c); } return -(lnt - 1); /* return # chars */ } if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ (uptr == &cpu_unit)) { r = fprint_sym_cm (of, addr, val, sw); /* decode inst */ if (r <= 0) return r; } if ((sw & SWMASK ('M')) && (uptr == &cpu_unit)) { /* inst format? */ r = fprint_sym_m (of, addr, val); /* decode inst */ if (r <= 0) return r; } vp = 0; /* init ptr */ GETNUM (num, lnt); /* get number */ fprint_val (of, (uint32) num, rdx, lnt * 8, PV_RZRO); return -(vp - 1); } /* Symbolic decode for -m Inputs: of = output stream addr = current PC *val = values to decode Outputs: return = if >= 0, error code if < 0, number of extra bytes retired */ t_stat fprint_sym_m (FILE *of, uint32 addr, t_value *val) { int32 i, k, vp, inst, numspec; int32 num, spec, rn, disp, index; vp = 0; /* init ptr */ inst = (int32) val[vp++]; /* get opcode */ if (inst == 0xFD) /* 2 byte op? */ inst = 0x100 | (int32) val[vp++]; if (opcode[inst] == NULL) /* defined? */ return SCPE_ARG; numspec = DR_GETNSP (drom[inst][0]); /* get # spec */ if (numspec == 0) numspec = DR_GETUSP (drom[inst][0]); fprintf (of, "%s", opcode[inst]); /* print name */ for (i = 0; i < numspec; i++) { /* loop thru spec */ fputc (i? ',': ' ', of); /* separator */ disp = drom[inst][i + 1]; /* get drom value */ if (disp == BB) { /* byte br disp? */ GETNUM (num, 1); fprintf (of, "%-X", SXTB (num) + addr + vp); } else if (disp == BW) { /* word br disp? */ GETNUM (num, 2); fprintf (of, "%-X", SXTW (num) + addr + vp); } else { spec = (int32) val[vp++]; /* get specifier */ if ((spec & 0xF0) == IDX) { /* index? */ index = spec; /* copy, get next */ spec = (int32) val[vp++]; } else index = 0; rn = spec & 0xF; /* get reg # */ switch (spec & 0xF0) { /* case on mode */ case SH0: case SH1: case SH2: case SH3: /* s^# */ fprintf (of, "#%-X", spec); break; case GRN: /* Rn */ fprintf (of, "%-s", regname[rn]); break; case RGD: /* (Rn) */ fprintf (of, "(%-s)", regname[rn]); break; case ADC: /* -(Rn) */ fprintf (of, "-(%-s)", regname[rn]); break; case AIN: /* (Rn)+, #n */ if (rn != nPC) fprintf (of, "(%-s)+", regname[rn]); else { if (DR_LNT (disp) == L_OCTA) vp = fprint_sym_qoimm (of, val, vp, 4); else if (DR_LNT (disp) == L_QUAD) vp = fprint_sym_qoimm (of, val, vp, 2); else { GETNUM (num, DR_LNT (disp)); fprintf (of, "#%-X", num); } } break; case AID: /* @(Rn)+, @#n */ if (rn != nPC) fprintf (of, "@(%-s)+", regname[rn]); else { GETNUM (num, 4); fprintf (of, "@#%-X", num); } break; case BDD: /* @b^d(r),@b^n */ fputc ('@', of); case BDP: /* b^d(r), b^n */ GETNUM (num, 1); if (rn == nPC) fprintf (of, "%-X", addr + vp + SXTB (num)); else if (num & BSIGN) fprintf (of, "-%-X(%-s)", -num & BMASK, regname[rn]); else fprintf (of, "%-X(%-s)", num, regname[rn]); break; case WDD: /* @w^d(r),@w^n */ fputc ('@', of); case WDP: /* w^d(r), w^n */ GETNUM (num, 2); if (rn == nPC) fprintf (of, "%-X", addr + vp + SXTW (num)); else if (num & WSIGN) fprintf (of, "-%-X(%-s)", -num & WMASK, regname[rn]); else fprintf (of, "%-X(%-s)", num, regname[rn]); break; case LDD: /* @l^d(r),@l^n */ fputc ('@', of); case LDP: /* l^d(r),l^n */ GETNUM (num, 4); if (rn == nPC) fprintf (of, "%-X", addr + vp + num); else if (num & LSIGN) fprintf (of, "-%-X(%-s)", -num, regname[rn]); else fprintf (of, "%-X(%-s)", num, regname[rn]); break; } /* end case */ if (index) fprintf (of, "[%-s]", regname[index & 0xF]); } /* end else */ } /* end for */ return -(vp - 1); } /* Symbolic decode, quad/octa immediates Inputs: *of = output stream *val = pointer to input values vp = current index into val lnt = number of longwords in immediate Outputs: vp = updated index into val */ int32 fprint_sym_qoimm (FILE *of, t_value *val, int32 vp, int32 lnt) { int32 i, k, startp, num[4]; for (i = 0; i < lnt; i++) { GETNUM (num[lnt - 1 - i], 4); } for (i = startp = 0; i < lnt; i++) { if (startp) fprintf (of, "%08X", num[i]); else if (num[i] || (i == (lnt - 1))) { fprintf (of, "#%-X", num[i]); startp = 1; } } return vp; } #define PUTNUM(d,n) for (k = 0; k < n; k++) val[vp++] = (d >> (k * 8)) & 0xFF /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym (char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw) { uint32 addr = (uint32) exta; int32 k, rdx, lnt, num, vp; t_stat r; DEVICE *dptr; static const uint32 maxv[5] = { 0, 0xFF, 0xFFFF, 0, 0xFFFFFFFF }; if (uptr == NULL) /* anon = CPU */ uptr = &cpu_unit; dptr = find_dev_from_unit (uptr); /* find dev */ if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth != 8) /* byte dev only */ return SCPE_ARG; if (sw & SWMASK ('B')) /* get length */ lnt = 1; else if (sw & SWMASK ('W')) lnt = 2; else if (sw & SWMASK ('L')) lnt = 4; else lnt = (uptr == &cpu_unit)? 4: 1; if (sw & SWMASK ('D')) /* get radix */ rdx = 10; else if (sw & SWMASK ('O')) rdx = 8; else if (sw & SWMASK ('H')) rdx = 16; else rdx = dptr->dradix; if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) /* ASCII char? */ return parse_char (cptr, val, lnt); if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) /* ASCII string? */ return parse_char (cptr, val, sim_emax); if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ (uptr == &cpu_unit)) { r = parse_sym_cm (cptr, addr, val, sw); /* try to parse */ if (r <= 0) return r; } if (uptr == &cpu_unit) { /* cpu only */ r = parse_sym_m (cptr, addr, val); /* try to parse inst */ if (r <= 0) return r; } num = (int32) get_uint (cptr, rdx, maxv[lnt], &r); /* get number */ if (r != SCPE_OK) return r; vp = 0; PUTNUM (num, lnt); /* store */ return -(lnt - 1); } /* Character input for -a or -c Inputs: *cptr = pointer to input string addr = current PC *val = pointer to output values Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_char (char *cptr, t_value *val, int32 lnt) { int32 vp; if (*cptr == 0) return SCPE_ARG; vp = 0; while ((vp < lnt) && *cptr) { /* get chars */ val[vp++] = *cptr++; } return -(vp - 1); /* return # chars */ } /* Symbolic input for -m Inputs: *cptr = pointer to input string addr = current PC *val = pointer to output values Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val) { int32 i, numspec, disp, opc, vp; t_stat r; char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ for (i = 0, opc = -1; (i < NUM_INST) && (opc < 0); i++) { if (opcode[i] && strcmp (gbuf, opcode[i]) == 0) opc = i; } if (opc < 0) { /* check alternates */ for (i = 0; altcod[i] && (opc < 0); i++) { if (strcmp (gbuf, altcod[i]) == 0) opc = altop[i]; } } if (opc < 0) return SCPE_ARG; /* undefined? */ vp = 0; if (opc >= 0x100) /* 2 byte? */ val[vp++] = 0xFD; val[vp++] = opc & 0xFF; /* store opcode */ numspec = DR_GETNSP (drom[opc][0]); /* get # specifiers */ if (numspec == 0) numspec = DR_GETUSP (drom[opc][0]); for (i = 1; i <= numspec; i++) { /* loop thru specs */ if (i == numspec) cptr = get_glyph (cptr, gbuf, 0); else cptr = get_glyph (cptr, gbuf, ','); /* get specifier */ disp = drom[opc][i]; /* get drom value */ if (disp == BB) vp = parse_brdisp (gbuf, addr, val, vp, 0, &r); else if (disp == BW) vp = parse_brdisp (gbuf, addr, val, vp, 1, &r); else vp = parse_spec (gbuf, addr, val, vp, disp, &r); if (r != SCPE_OK) return r; } if (*cptr != 0) return SCPE_ARG; return -(vp - 1); } /* Parse a branch displacement Inputs: cptr = pointer to input buffer addr = current address val = pointer to output array vp = current pointer in output array lnt = length (0 = byte, 1 = word) r = pointer to status Outputs: vp = updated output pointer */ int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, int32 vp, int32 lnt, t_stat *r) { int32 k, dest, num; dest = (int32) get_uint (cptr, 16, 0xFFFFFFFF, r); /* get value */ num = dest - (addr + vp + lnt + 1); /* compute offset */ if ((num > (lnt? 32767: 127)) || (num < (lnt? -32768: -128))) *r = SCPE_ARG; else { PUTNUM (num, lnt + 1); /* store offset */ *r = SCPE_OK; } return vp; } /* Parse a specifier Inputs: cptr = pointer to input buffer addr = current address val = pointer to output array vp = current pointer in output array disp = specifier dispatch r = pointer to status Outputs: vp = updated output pointer */ #define SP_IND 0x200 /* indirect */ #define SP_V_FORCE 6 #define SP_FS 0x040 /* S^ */ #define SP_FI 0x080 /* I^ */ #define SP_FB 0x0C0 /* B^ */ #define SP_FW 0x100 /* W^ */ #define SP_FL 0x140 /* L^ */ #define SP_LIT 0x020 /* # */ #define SP_PLUS 0x010 /* plus */ #define SP_MINUS 0x008 /* minus */ #define SP_NUM 0x004 /* number */ #define SP_IDX 0x002 /* (Rn) */ #define SP_POSTP 0x001 /* trailing + */ #define M1C(c,v) if (*cptr == c) {\ cptr++; \ fl = fl | v; \ } #define SPUTNUM(v,d) if (fl & SP_MINUS) \ v = -v; \ PUTNUM (v, d) #define PARSE_LOSE { \ *r = SCPE_ARG; \ return vp; \ } #define SEL_LIM(p,m,u) ((fl & SP_PLUS)? (p): ((fl & SP_MINUS)? (m): (u))) int32 parse_spec (char *cptr, uint32 addr, t_value *val, int32 vp, int32 disp, t_stat *r) { int32 i, k, litsize, rn, index; int32 num, dispsize, mode; int32 lit[4] = { 0 }; int32 fl = 0; char c, *tptr; const char *force[] = { "S^", "I^", "B^", "W^", "L^", NULL }; *r = SCPE_OK; /* assume ok */ M1C ('@', SP_IND); /* look for @ */ if (tptr = parse_rnum (cptr, &rn)) { /* look for Rn */ if (*cptr == '[') { /* look for [Rx] */ cptr = parse_rnum (++cptr, &index); if ((cptr == NULL) || (*cptr++ != ']')) PARSE_LOSE; val[vp++] = index | IDX; } else val[vp++] = rn | GRN | (fl? 1: 0); /* Rn or @Rn */ if (*tptr != 0) /* must be done */ *r = SCPE_ARG; return vp; } for (i = 0; force[i]; i++) { /* look for x^ */ if (strncmp (cptr, force[i], 2) == 0) { cptr = cptr + 2; fl = fl | ((i + 1) << SP_V_FORCE); break; } } M1C ('#', SP_LIT); /* look for # */ M1C ('+', SP_PLUS); /* look for + */ M1C ('-', SP_MINUS); /* look for - */ for (litsize = 0;; cptr++) { /* look for mprec int */ c = *cptr; if ((c < '0') || (c > 'F') || ((c > '9') && (c < 'A'))) break; num = (c <= '9')? c - '0': c - 'A' + 10; fl = fl | SP_NUM; for (i = 3; i >= 0; i--) { lit[i] = lit[i] << 4; if (i > 0) lit[i] = lit[i] | ((lit[i - 1] >> 28) & 0xF); else lit[i] = lit[i] | num; if (lit[i] && (i > litsize)) litsize = i; } } if (*cptr == '(') { /* look for (Rn) */ cptr = parse_rnum (++cptr, &rn); if ((cptr == NULL) || (*cptr++ != ')')) PARSE_LOSE; fl = fl | SP_IDX; } M1C ('+', SP_POSTP); /* look for + */ if (*cptr == '[') { /* look for [Rx] */ cptr = parse_rnum (++cptr, &index); if ((cptr == NULL) || (*cptr++ != ']')) PARSE_LOSE; val[vp++] = index | IDX; } switch (fl) { /* case on state */ case SP_FS|SP_LIT|SP_NUM: /* S^#n */ case SP_FS|SP_LIT|SP_PLUS|SP_NUM: /* S^#+n */ if ((litsize > 0) || (lit[0] & ~0x3F)) PARSE_LOSE; val[vp++] = lit[0]; break; case SP_IDX: /* (Rn) */ val[vp++] = rn | RGD; break; case SP_MINUS|SP_IDX: /* -(Rn) */ val[vp++] = rn | ADC; break; case SP_IDX|SP_POSTP: /* (Rn)+ */ val[vp++] = rn | AIN; break; case SP_LIT|SP_NUM: /* #n */ case SP_LIT|SP_PLUS|SP_NUM: /* #+n */ if ((litsize == 0) && ((lit[0] & ~0x3F) == 0)) { val[vp++] = lit[0]; break; } /* fall thru */ case SP_LIT|SP_MINUS|SP_NUM: /* #-n */ case SP_FI|SP_LIT|SP_NUM: /* I^#n */ case SP_FI|SP_LIT|SP_PLUS|SP_NUM: /* I^#+n */ case SP_FI|SP_LIT|SP_MINUS|SP_NUM: /* I^#-n */ val[vp++] = nPC | AIN; disp = disp & DR_LNMASK; switch (disp) { /* case spec lnt */ case 00: /* check fit */ if ((litsize > 0) || (lit[0] < 0) || (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) PARSE_LOSE; SPUTNUM (lit[0], 1); /* store */ break; case 01: /* check fit */ if ((litsize > 0) || (lit[0] < 0) || (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) PARSE_LOSE; SPUTNUM (lit[0], 2); break; case 02: /* check 1 lw */ if (litsize > 0) PARSE_LOSE; SPUTNUM (lit[0], 4); break; case 03: /* check 2 lw */ if (litsize > 1) PARSE_LOSE; vp = parse_sym_qoimm (lit, val, vp, 2, fl & SP_MINUS); break; case 04: vp = parse_sym_qoimm (lit, val, vp, 4, fl & SP_MINUS); break; } /* end case lnt */ break; case SP_IND|SP_IDX|SP_POSTP: /* @(Rn)+ */ val[vp++] = rn | AID; break; case SP_IND|SP_LIT|SP_NUM: /* @#n */ if (litsize > 0) PARSE_LOSE; val[vp++] = nPC | AID; PUTNUM (lit[0], 4); break; case SP_NUM|SP_IDX: /* d(rn) */ case SP_PLUS|SP_NUM|SP_IDX: /* +d(rn) */ case SP_MINUS|SP_NUM|SP_IDX: /* -d(rn) */ case SP_IND|SP_NUM|SP_IDX: /* @d(rn) */ case SP_IND|SP_PLUS|SP_NUM|SP_IDX: /* @+d(rn) */ case SP_IND|SP_MINUS|SP_NUM|SP_IDX: /* @-d(rn) */ if (litsize > 0) PARSE_LOSE; dispsize = 4; /* find fit for */ mode = LDP; /* displacement */ if (lit[0] >= 0) { if (lit[0] <= SEL_LIM (0x7F, 0x80, 0xFF)) { dispsize = 1; mode = BDP; } else if (lit[0] <= SEL_LIM (0x7FFF, 0x8000, 0xFFFF)) { dispsize = 2; mode = WDP; } } val[vp++] = mode | rn | ((fl & SP_IND)? 0x10: 0); SPUTNUM (lit[0], dispsize); break; case SP_FB|SP_NUM|SP_IDX: /* B^d(rn) */ case SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* B^+d(rn) */ case SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* B^-d(rn) */ case SP_IND|SP_FB|SP_NUM|SP_IDX: /* @B^d(rn) */ case SP_IND|SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* @B^+d(rn) */ case SP_IND|SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* @B^-d(rn) */ if ((litsize > 0) || (lit[0] < 0) || (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) PARSE_LOSE; val[vp++] = rn | BDP | ((fl & SP_IND)? 0x10: 0); SPUTNUM (lit[0], 1); break; case SP_FW|SP_NUM|SP_IDX: /* W^d(rn) */ case SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* W^+d(rn) */ case SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* W^-d(rn) */ case SP_IND|SP_FW|SP_NUM|SP_IDX: /* @W^d(rn) */ case SP_IND|SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* @W^+d(rn) */ case SP_IND|SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* @W^-d(rn) */ if ((litsize > 0) || (lit[0] < 0) || (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) PARSE_LOSE; val[vp++] = rn | WDP | ((fl & SP_IND)? 0x10: 0); SPUTNUM (lit[0], 2); break; case SP_FL|SP_NUM|SP_IDX: /* L^d(rn) */ case SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* L^+d(rn) */ case SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* L^-d(rn) */ case SP_IND|SP_FL|SP_NUM|SP_IDX: /* @L^d(rn) */ case SP_IND|SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* @L^+d(rn) */ case SP_IND|SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* @L^-d(rn) */ if ((litsize > 0) || (lit[0] < 0)) PARSE_LOSE; val[vp++] = rn | LDP | ((fl & SP_IND)? 0x10: 0); SPUTNUM (lit[0], 4); break; case SP_NUM: /* n */ case SP_IND|SP_NUM: /* @n */ if (litsize > 0) PARSE_LOSE; num = lit[0] - (addr + vp + 2); /* fit in byte? */ if ((num >= -128) && (num <= 127)) { mode = BDP; dispsize = 1; } else { num = lit[0] - (addr + vp + 3); /* fit in word? */ if ((num >= -32768) && (num <= 32767)) { mode = WDP; dispsize = 2; } else { num = lit[0] - (addr + vp + 5); /* no, use lw */ mode = LDP; dispsize = 4; } } val[vp++] = mode | nPC | ((fl & SP_IND)? 1: 0); PUTNUM (num, dispsize); break; case SP_FB|SP_NUM: /* B^n */ case SP_IND|SP_FB|SP_NUM: /* @B^n */ num = lit[0] - (addr + vp + 2); if ((litsize > 0) || (num > 127) || (num < -128)) PARSE_LOSE; val[vp++] = nPC | BDP | ((fl & SP_IND)? 1: 0); PUTNUM (num, 1); break; case SP_FW|SP_NUM: /* W^n */ case SP_IND|SP_FW|SP_NUM: /* @W^n */ num = lit[0] - (addr + vp + 3); if ((litsize > 0) || (num > 32767) || (num < -32768)) PARSE_LOSE; val[vp++] = nPC | WDP | ((fl & SP_IND)? 1: 0); PUTNUM (num, 2); break; case SP_FL|SP_NUM: /* L^n */ case SP_IND|SP_FL|SP_NUM: /* @L^n */ num = lit[0] - (addr + vp + 5); if (litsize > 0) PARSE_LOSE; val[vp++] = nPC | LDP | ((fl & SP_IND)? 1: 0); PUTNUM (num, 4); break; default: PARSE_LOSE; } /* end case */ if (*cptr != 0) /* must be done */ *r = SCPE_ARG; return vp; } char *parse_rnum (char *cptr, int32 *rn) { int32 i, lnt; t_value regnum; char *tptr; for (i = 15; i >= 0; i--) { /* chk named reg */ lnt = strlen (regname[i]); if (strncmp (cptr, regname[i], lnt) == 0) { *rn = i; return cptr + lnt; } } if (*cptr++ != 'R') /* look for R */ return NULL; regnum = strtotv (cptr, &tptr, 10); /* look for reg # */ if ((cptr == tptr) || (regnum > 15)) return NULL; *rn = (int32) regnum; return tptr; } int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, int lnt, int32 minus) { int32 i, k, prev; for (i = prev = 0; i < lnt; i++) { if (minus) prev = lit[i] = ~lit[i] + (prev == 0); PUTNUM (lit[i], 4); } return vp; } simh-3.8.1/VAX/vax_cis.c0000644000175000017500000020276511112260560013105 0ustar vlmvlm/* vax_cis.c: VAX CIS instructions Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. On a full VAX, this module simulates the VAX commercial instruction set (CIS). On a subset VAX, this module implements the emulated instruction fault. 16-Oct-08 RMS Fixed bug in ASHP left overflow calc (Word/NibbleLShift) Fixed bug in DIVx (LntDstr calculation) 28-May-08 RMS Inlined physical memory routines 16-May-06 RMS Fixed bug in length calculation (found by Tim Stark) 03-May-06 RMS Fixed MOVTC, MOVTUC to preserve cc's through page faults Fixed MOVTUC to stop on translated == escape Fixed CVTPL to set registers before destination reg write Fixed CVTPL to set correct cc bit on overflow Fixed EDITPC to preserve cc's through page faults Fixed EDITPC EO$BLANK_ZERO count, cc test Fixed EDITPC EO$INSERT to insert fill instead of blank Fixed EDITPC EO$LOAD_PLUS/MINUS to skip character (all reported by Tim Stark) 12-Apr-04 RMS Cloned from pdp11_cis.c and vax_cpu1.c Zero length decimal strings require either zero bytes (trailing) or one byte (separate, packed). CIS instructions can run for a very long time, so they are interruptible and restartable. In the simulator, string instructions (and EDITPC) are interruptible by faults, but decimal instructions run to completion. The code is unoptimized. */ #include "vax_defs.h" #if defined (FULL_VAX) /* Decimal string structure */ #define DSTRLNT 4 #define DSTRMAX (DSTRLNT - 1) #define MAXDVAL 429496730 /* 2^32 / 10 */ #define C_SPACE 0x20 /* ASCII chars */ #define C_PLUS 0x2B #define C_MINUS 0x2D #define C_ZERO 0x30 #define C_NINE 0x39 typedef struct { uint32 sign; uint32 val[DSTRLNT]; } DSTR; static DSTR Dstr_zero = { 0, 0, 0, 0, 0 }; static DSTR Dstr_one = { 0, 0x10, 0, 0, 0 }; extern int32 R[16]; extern int32 PSL; extern int32 trpirq; extern int32 p1; extern int32 fault_PC; extern int32 ibcnt, ppc; extern int32 sim_interval; extern jmp_buf save_env; int32 ReadDstr (int32 lnt, int32 addr, DSTR *dec, int32 acc); int32 WriteDstr (int32 lnt, int32 addr, DSTR *dec, int32 v, int32 acc); int32 SetCCDstr (int32 lnt, DSTR *src, int32 pslv); int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); int32 CmpDstr (DSTR *src1, DSTR *src2); int32 TestDstr (DSTR *dsrc); void ProbeDstr (int32 lnt, int32 addr, int32 acc); int32 LntDstr (DSTR *dsrc, int32 nz); uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin); uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); int32 WordLshift (DSTR *dsrc, int32 sc); void WordRshift (DSTR *dsrc, int32 sc); void CreateTable (DSTR *dsrc, DSTR mtable[10]); int32 do_crc_4b (int32 crc, int32 tbl, int32 acc); int32 edit_read_src (int32 inc, int32 acc); void edit_adv_src (int32 inc); int32 edit_read_sign (int32 acc); extern int32 eval_int (void); /* CIS emulator */ int32 op_cis (int32 *op, int32 cc, int32 opc, int32 acc) { int32 i, j, c, t, pop, rpt, V; int32 match, fill, sign, shift; int32 ldivd, ldivr; int32 lenl, lenp; uint32 nc, d, result; t_stat r; DSTR accum, src1, src2, dst; DSTR mptable[10]; switch (opc) { /* case on opcode */ /* MOVTC Operands if PSL = 0: op[0:1] = source string descriptor op[2] = fill character op[3] = table address op[4:5] = destination string descriptor Registers if PSL = 1: R[0] = delta-PC/fill/source string length R[1] = source string address R[2] = number of bytes remaining to move R[3] = table address R[4] = saved cc's/destination string length R[5] = destination string address Condition codes: NZC = set from op[0]:op[4] V = 0 Registers: R0 = src length remaining, or 0 R1 = addr of end of source string + 1 R2 = 0 R3 = table address R4 = 0 R5 = addr of end of dest string + 1 */ case MOVTC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get fill */ R[2] = R[2] & STR_LNMASK; /* remaining move */ cc = (R[4] >> 16) & CC_MASK; /* restore cc's */ } else { CC_CMP_W (op[0], op[4]); /* set cc's */ R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ R[1] = op[1]; /* src addr */ fill = op[2]; /* set fill */ R[3] = op[3]; /* table addr */ R[4] = op[4] | ((cc & CC_MASK) << 16); /* dst len + cc's */ R[5] = op[5]; /* dst addr */ R[2] = (op[0] < op[4])? op[0]: op[4]; /* remaining move */ PSL = PSL | PSL_FPD; /* set FPD */ } if (R[2]) { /* move to do? */ int32 mvl; mvl = R[0] & STR_LNMASK; /* orig move len */ if (mvl >= (R[4] & STR_LNMASK)) mvl = R[4] & STR_LNMASK; if (((uint32) R[1]) < ((uint32) R[5])) { /* backward? */ while (R[2]) { /* loop thru char */ t = Read ((R[1] + R[2] - 1) & LMASK, L_BYTE, RA); c = Read ((R[3] + t) & LMASK, L_BYTE, RA); Write ((R[5] + R[2] - 1) & LMASK, c, L_BYTE, WA); R[2] = (R[2] - 1) & STR_LNMASK; } R[1] = (R[1] + mvl) & LMASK; /* adv src, dst */ R[5] = (R[5] + mvl) & LMASK; } else { /* forward */ while (R[2]) { /* loop thru char */ t = Read (R[1], L_BYTE, RA); /* read src */ c = Read ((R[3] + t) & LMASK, L_BYTE, RA); Write (R[5], c, L_BYTE, WA); /* write dst */ R[1] = (R[1] + 1) & LMASK; /* adv src, dst */ R[2] = (R[2] - 1) & STR_LNMASK; R[5] = (R[5] + 1) & LMASK; } } /* update lengths */ R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK); R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - mvl) & STR_LNMASK); } while (R[4] & STR_LNMASK) { /* fill if needed */ Write (R[5], fill, L_BYTE, WA); R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - 1) & STR_LNMASK); R[5] = (R[5] + 1) & LMASK; /* adv dst */ } R[0] = R[0] & STR_LNMASK; /* mask off state */ R[4] = 0; PSL = PSL & ~PSL_FPD; return cc; /* MOVTUC Operands: op[0:1] = source string descriptor op[2] = escape character op[3] = table address op[4:5] = destination string descriptor Registers if PSL = 1: R[0] = delta-PC/match/source string length R[1] = source string address R[2] = saved condition codes R[3] = table address R[4] = destination string length R[5] = destination string address Condition codes: NZC = set from op[0]:op[4] V = 1 if match to escape character Registers: R0 = src length remaining, or 0 R1 = addr of end of source string + 1 R2 = 0 R3 = table address R4 = dst length remaining, or 0 R5 = addr of end of dest string + 1 */ case MOVTUC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get match */ R[4] = R[4] & STR_LNMASK; cc = R[2] & CC_MASK; /* restore cc's */ } else { CC_CMP_W (op[0], op[4]); /* set cc's */ R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ R[1] = op[1]; /* src addr */ fill = op[2]; /* set match */ R[3] = op[3]; /* table addr */ R[4] = op[4]; /* dst len */ R[5] = op[5]; /* dst addr */ R[2] = cc; /* save cc's */ PSL = PSL | PSL_FPD; /* set FPD */ } while ((R[0] & STR_LNMASK) && R[4]) { /* while src & dst */ t = Read (R[1], L_BYTE, RA); /* read src */ c = Read ((R[3] + t) & LMASK, L_BYTE, RA); /* translate */ if (c == fill) { /* stop char? */ cc = cc | CC_V; /* set V, done */ break; } Write (R[5], c, L_BYTE, WA); /* write dst */ R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); R[1] = (R[1] + 1) & LMASK; R[4] = (R[4] - 1) & STR_LNMASK; /* adv src, dst */ R[5] = (R[5] + 1) & LMASK; } R[0] = R[0] & STR_LNMASK; /* mask off state */ R[2] = 0; PSL = PSL & ~PSL_FPD; return cc; /* MATCHC Operands: op[0:1] = substring descriptor op[2:3] = string descriptor Registers if PSL = 1: R[0] = delta-PC/match/substring length R[1] = substring address R[2] = source string length R[3] = source string address Condition codes: NZ = set from R0 VC = 0 Registers: R0 = if match, 0, else, op[0] R1 = if match, op[0] + op[1], else, op[1] R2 = if match, src bytes remaining, else, 0 R3 = if match, end of substr, else, op[2] + op[3] Notes: - If the string is zero length, and the substring is not, the outer loop exits immediately, and the result is "no match" - If the substring is zero length, the inner loop always exits immediately, and the result is a "match" - If the string is zero length, and the substring is as well, the outer loop executes, the inner loop exits immediately, and the result is a match, but the result is the length of the string (zero) - This instruction can potentially run a very long time - worst case execution on a real VAX-11/780 was more than 30 minutes. Accordingly, the instruction tests for interrupts and stops if one is found. */ case MATCHC: if (PSL & PSL_FPD) { /* FPD? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ R[2] = R[2] & STR_LNMASK; } else { R[0] = STR_PACK (0, op[0]); /* srclen + FPD data */ R[1] = op[1]; /* save operands */ R[2] = op[2]; R[3] = op[3]; PSL = PSL | PSL_FPD; /* set FPD */ } for (match = 0; R[2] >= (R[0] & STR_LNMASK); ) { for (i = 0, match = 1; match && (i < (R[0] & STR_LNMASK)); i++) { c = Read ((R[1] + i) & LMASK, L_BYTE, RA); t = Read ((R[3] + i) & LMASK, L_BYTE, RA); match = (c == t); /* continue if match */ } /* end for substring */ if (match) /* exit if match */ break; R[2] = (R[2] - 1) & STR_LNMASK; /* decr src length */ R[3] = (R[3] + 1) & LMASK; /* next string char */ if (i >= sim_interval) { /* done with interval? */ sim_interval = 0; if (r = sim_process_event ()) { /* presumably WRU */ PC = fault_PC; /* backup up PC */ ABORT (r); /* abort flushes IB */ } SET_IRQL; /* update interrupts */ if (trpirq) /* pending? stop */ ABORT (ABORT_INTR); } else sim_interval = sim_interval - i; } /* end for string */ R[0] = R[0] & STR_LNMASK; if (match) { /* if match */ R[1] = (R[1] + R[0]) & LMASK; R[2] = (R[2] - R[0]) & STR_LNMASK; R[3] = (R[3] + R[0]) & LMASK; R[0] = 0; } else { /* if no match */ R[3] = (R[3] + R[2]) & LMASK; R[2] = 0; } PSL = PSL & ~PSL_FPD; CC_IIZZ_L (R[0]); /* set cc's */ return cc; /* CRC Operands: op[0] = table address op[1] = initial CRC op[2:3] = source string descriptor Registers if PSL = 1: R[0] = result R[1] = table address R[2] = delta-PC/0/source string length R[3] = source string address Condition codes: NZ = set from result VC = 0 Registers: R0 = result R1 = 0 R2 = 0 R3 = addr + 1 of last byte in source string */ case CRC: if (PSL & PSL_FPD) { /* FPD? */ SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ } else { R[2] = STR_PACK (0, op[2]); /* srclen + FPD data */ R[0] = op[1]; /* save operands */ R[1] = op[0]; R[3] = op[3]; PSL = PSL | PSL_FPD; /* set FPD */ } while ((R[2] & STR_LNMASK) != 0) { /* loop thru chars */ c = Read (R[3], L_BYTE, RA); /* get char */ t = R[0] ^ c; /* XOR to CRC */ t = do_crc_4b (t, R[1], acc); /* do 4b shift */ R[0] = do_crc_4b (t, R[1], acc); /* do 4b shift */ R[3] = (R[3] + 1) & LMASK; R[2] = R[2] - 1; } R[1] = 0; R[2] = 0; PSL = PSL & ~PSL_FPD; CC_IIZZ_L (R[0]); /* set cc's */ return cc; /* MOVP Operands: op[0] = length op[1] = source string address op[2] = dest string address Condition codes: NZ = set from result V = 0 C = unchanged Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case MOVP: if ((PSL & PSL_FPD) || (op[0] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &dst, acc); /* read source */ cc = WriteDstr (op[0], op[2], &dst, 0, acc) | /* write dest */ (cc & CC_C); /* preserve C */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[2]; return cc; /* ADDP4, ADDP6, SUBP4, SUBP6 Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor (ADDP6, SUBP6 only) op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string (ADDP6, SUBP6 only) R4 = 0 R5 = addr of dest string */ case ADDP4: case SUBP4: op[4] = op[2]; /* copy dst */ op[5] = op[3]; case ADDP6: case SUBP6: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ if (opc & 2) /* sub? invert sign */ src1.sign = src1.sign ^ 1; if (src1.sign ^ src2.sign) { /* opp signs? sub */ if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ SubDstr (&src1, &src2, &dst); /* src2 - src1 */ dst.sign = src2.sign; /* sign = src2 */ } else { SubDstr (&src2, &src1, &dst); /* src1 - src2 */ dst.sign = src1.sign; /* sign = src1 */ } V = 0; /* can't carry */ } else { /* addition */ V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ dst.sign = src1.sign; /* set result sign */ } cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; if (opc & 1) { /* ADDP6, SUBP6? */ R[4] = 0; R[5] = op[5]; } return cc; /* MULP Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string R4 = 0 R5 = addr of dest string */ case MULP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ if (ReadDstr (op[0], op[1], &src1, acc) && /* read src1, src2 */ ReadDstr (op[2], op[3], &src2, acc)) { /* if both > 0 */ dst.sign = src1.sign ^ src2.sign; /* sign of result */ accum = Dstr_zero; /* clear accum */ NibbleRshift (&src1, 1, 0); /* shift out sign */ CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ d = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (d > 0) /* add in digit*mpcnd */ AddDstr (&mptable[d], &accum, &accum, 0); nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ NibbleRshift (&dst, 1, nc); /* result right 4 */ } V = TestDstr (&accum) != 0; /* if ovflo, set V */ } else V = 0; /* result = 0 */ cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; R[4] = 0; R[5] = op[5]; return cc; /* DIVP Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string R4 = 0 R5 = addr of dest string */ case DIVP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ldivr = ReadDstr (op[0], op[1], &src1, acc); /* get divisor */ if (ldivr == 0) { /* divisor = 0? */ SET_TRAP (TRAP_FLTDIV); /* dec div trap */ return cc; } ldivr = LntDstr (&src1, ldivr); /* get exact length */ ldivd = ReadDstr (op[2], op[3], &src2, acc); /* get dividend */ ldivd = LntDstr (&src2, ldivd); /* get exact length */ dst = Dstr_zero; /* clear dest */ NibbleRshift (&src1, 1, 0); /* right justify ops */ NibbleRshift (&src2, 1, 0); if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ dst.sign = src1.sign ^ src2.sign; /* calculate sign */ WordLshift (&src1, t / 8); /* align divr to divd */ NibbleLshift (&src1, t % 8, 0); CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 0; i <= t; i++) { /* divide loop */ for (d = 9; d > 0; d--) { /* find digit */ if (CmpDstr (&src2, &mptable[d]) >= 0) { SubDstr (&mptable[d], &src2, &src2); dst.val[0] = dst.val[0] | d; break; } /* end if */ } /* end for */ NibbleLshift (&src2, 1, 0); /* shift dividend */ NibbleLshift (&dst, 1, 0); /* shift quotient */ } /* end divide loop */ } /* end if */ cc = WriteDstr (op[4], op[5], &dst, 0, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; R[4] = 0; R[5] = op[5]; return cc; /* CMPP3, CMPP4 Operands (CMPP3): op[0] = string length op[1], op[2] = string lengths Operands (CMPP4): op[0:1] = string1 descriptor op[2:3] = string2 descriptor Condition codes: NZ = set from comparison VC = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string */ case CMPP3: op[3] = op[2]; /* reposition ops */ op[2] = op[0]; case CMPP4: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ cc = 0; if (src1.sign != src2.sign) cc = (src1.sign)? CC_N: 0; else { t = CmpDstr (&src1, &src2); /* compare strings */ if (t < 0) cc = (src1.sign? 0: CC_N); else if (t > 0) cc = (src1.sign? CC_N: 0); else cc = CC_Z; } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc ; /* ASHP Operands: op[0] = shift count op[1:2] = source string descriptor op[3] = round digit op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string */ case ASHP: if ((PSL & PSL_FPD) || (op[1] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ReadDstr (op[1], op[2], &src1, acc); /* get source */ V = 0; /* init V */ shift = op[0]; /* get shift count */ if (shift & BSIGN) { /* right shift? */ shift = BMASK + 1 - shift; /* !shift! */ WordRshift (&src1, shift / 8); /* do word shifts */ NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ t = op[3] & 0xF; /* get round nibble */ if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ AddDstr (&src1, &Dstr_one, &src1, 0); /* round */ src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ } /* end right shift */ else if (shift) { /* left shift? */ if (WordLshift (&src1, shift / 8)) /* do word shifts */ V = 1; if (NibbleLshift (&src1, shift % 8, 0)) V = 1; } /* end left shift */ cc = WriteDstr (op[4], op[5], &src1, V, acc); /* store result */ R[0] = 0; R[1] = op[2]; R[2] = 0; R[3] = op[5]; return cc ; /* CVTPL Operands: op[0:1] = source string descriptor op[2] = memory flag/register number op[3] = memory address Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = 0 */ case CVTPL: if ((PSL & PSL_FPD) || (op[0] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get source */ V = result = 0; /* clear V, result */ for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ d = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (d || result || V) { /* skip initial 0's */ if (result >= MAXDVAL) V = 1; result = ((result * 10) + d) & LMASK; if (result < d) V = 1; } /* end if */ } /* end for */ if (src1.sign) /* negative? */ result = (~result + 1) & LMASK; if (src1.sign ^ ((result & LSIGN) != 0)) /* test for overflow */ V = 1; if (op[2] < 0) /* if mem, store result */ Write (op[3], result, L_LONG, WA); /* before reg update */ R[0] = 0; /* update registers */ R[1] = op[1]; R[2] = 0; R[3] = 0; if (op[2] >= 0) /* if reg, store result */ R[op[2]] = result; /* after reg update */ if (V && (PSL & PSW_IV)) /* ovflo and IV? trap */ SET_TRAP (TRAP_INTOV); CC_IIZZ_L (result); return cc | (V? CC_V: 0); /* CVTLP Operands: op[0] = source long op[1:2] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = 0 R2 = 0 R3 = addr of dest string */ case CVTLP: if ((PSL & PSL_FPD) || (op[1] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ result = op[0]; if ((result & LSIGN) != 0) { dst.sign = 1; result = (~result + 1) & LMASK; } for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { d = result % 10; result = result / 10; dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } cc = WriteDstr (op[1], op[2], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = 0; R[2] = 0; R[3] = op[2]; return cc; /* CVTSP Operands: op[0:1] = source string descriptor op[2:3] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = address of sign byte of source string R2 = 0 R3 = addr of dest string */ case CVTSP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ t = Read (op[1], L_BYTE, RA); /* read source sign */ if (t == C_MINUS) /* sign -, */ dst.sign = 1; else if ((t != C_PLUS) && (t != C_SPACE)) /* + or blank? */ RSVD_OPND_FAULT; for (i = 1; i <= op[0]; i++) { /* loop thru chars */ c = Read ((op[1] + op[0] + 1 - i) & LMASK, L_BYTE, RA); if ((c < C_ZERO) || (c > C_NINE)) /* [0:9]? */ RSVD_OPND_FAULT; d = c & 0xF; dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } TestDstr (&dst); /* correct -0 */ cc = WriteDstr (op[2], op[3], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc; /* CVTPS Operands: op[0:1] = source string descriptor op[2:3] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTPS: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ lenp = LntDstr (&dst, lenl); /* get exact nz src len */ ProbeDstr (op[2], op[3], WA); /* test dst write */ Write (op[3], dst.sign? C_MINUS: C_PLUS, L_BYTE, WA); for (i = 1; i <= op[2]; i++) { /* loop thru chars */ d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;/* get digit */ c = d | C_ZERO; /* cvt to ASCII */ Write ((op[3] + op[2] + 1 - i) & LMASK, c, L_BYTE, WA); } cc = SetCCDstr (op[0], &dst, 0); /* set cc's */ if (lenp > op[2]) { /* src fit in dst? */ cc = cc | CC_V; /* set ovflo */ if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc; /* CVTTP Operands: op[0:1] = source string descriptor op[2] = table address op[3:4] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTTP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ for (i = 1; i <= op[0]; i++) { /* loop thru char */ c = Read ((op[1] + op[0] - i) & LMASK, L_BYTE, RA); /* read char */ if (i != 1) { /* normal byte? */ if ((c < C_ZERO) || (c > C_NINE)) /* valid digit? */ RSVD_OPND_FAULT; d = c & 0xF; } else { /* highest byte */ t = Read ((op[2] + c) & LMASK, L_BYTE, RA); /* xlate */ d = (t >> 4) & 0xF; /* digit */ t = t & 0xF; /* sign */ if ((d > 0x9) || (t < 0xA)) RSVD_OPND_FAULT; if ((t == 0xB) || (t == 0xD)) dst.sign = 1; } dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } TestDstr (&dst); /* correct -0 */ cc = WriteDstr (op[3], op[4], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[4]; return cc; /* CVTPT Operands: op[0:1] = source string descriptor op[2] = table address op[3:4] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTPT: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT; lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ lenp = LntDstr (&dst, lenl); /* get exact src len */ ProbeDstr (op[3], op[4], WA); /* test writeability */ for (i = 1; i <= op[3]; i++) { /* loop thru chars */ if (i != 1) { /* not last? */ d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ c = d + C_ZERO; /* convert */ } else { /* translate last */ t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA); c = Read ((op[2] + t) & LMASK, L_BYTE, RA); } Write ((op[4] + op[3] - i) & LMASK, c, L_BYTE, WA); } cc = SetCCDstr (op[0], &dst, 0); /* set cc's from src */ if (lenp > op[3]) { /* src fit in dst? */ cc = cc | CC_V; /* set ovflo */ if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[4]; return cc; /* EDITPC Operands: op[0:1] = source string descriptor op[2] = pattern string address op[3] = dest string address Condition codes: N = source is negative Z = source is zero V = significant digits lost C = significant digits seen Registers at packup: R0<31:16> = -count of source zeroes to supply R0<15:0> = remaining source length R1 = source address R2<31:24> = delta PC R2<19:16> = condition codes R2<15:8> = sign char R2<7:0> = fill char R3 = pattern string address R4 = original source length R5 = dest string addr Registers at end: R0 = source length R1 = source addr R2 = 0 R3 = addr of byte containing EO$END R4 = 0 R5 = addr of end of dst string + 1 Fault and abort conditions for EDITPC are complicated. In general: - It is safe to take a memory management fault on the read of any pattern byte. After correction of the fault, the pattern operator is fetched and executed again. - It is safe to take a memory management fault on a write-only operation, like fill. After correction of the fault, the pattern operator is fetched and executed again. - The move operators do not alter visible state (registers or saved cc) until all memory operations are complete. */ case EDITPC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ fill = ED_GETFILL (R[2]); /* get fill */ sign = ED_GETSIGN (R[2]); /* get sign */ cc = ED_GETCC (R[2]); /* get cc's */ R[0] = R[0] & ~0xFFE0; /* src len <= 31 */ } else { /* new instr */ if (op[0] > 31) /* lnt > 31? */ RSVD_OPND_FAULT; t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF; if ((t == 0xB) || (t == 0xD)) { cc = CC_N | CC_Z; sign = C_MINUS; } else { cc = CC_Z; sign = C_SPACE; } fill = C_SPACE; R[0] = R[4] = op[0]; /* src len */ R[1] = op[1]; /* src addr */ R[2] = STR_PACK (cc, (sign << ED_V_SIGN) | (fill << ED_V_FILL)); /* delta PC, cc, sign, fill */ R[3] = op[2]; /* pattern */ R[5] = op[3]; /* dst addr */ PSL = PSL | PSL_FPD; /* set FPD */ } for ( ;; ) { /* loop thru pattern */ pop = Read (R[3], L_BYTE, RA); /* rd pattern op */ if (pop == EO_END) /* end? */ break; if (pop & EO_RPT_FLAG) { /* repeat class? */ rpt = pop & EO_RPT_MASK; /* isolate count */ if (rpt == 0) /* can't be zero */ RSVD_OPND_FAULT; pop = pop & ~EO_RPT_MASK; /* isolate op */ } switch (pop) { /* case on op */ case EO_END_FLOAT: /* end float */ if (!(cc & CC_C)) { /* not signif? */ Write (R[5], sign, L_BYTE, WA); /* write sign */ R[5] = (R[5] + 1) & LMASK; /* now fault safe */ cc = cc | CC_C; /* set signif */ } break; case EO_CLR_SIGNIF: /* clear signif */ cc = cc & ~CC_C; /* clr C */ break; case EO_SET_SIGNIF: /* set signif */ cc = cc | CC_C; /* set C */ break; case EO_STORE_SIGN: /* store sign */ Write (R[5], sign, L_BYTE, WA); /* write sign */ R[5] = (R[5] + 1) & LMASK; /* now fault safe */ break; case EO_LOAD_FILL: /* load fill */ fill = Read ((R[3] + 1) & LMASK, L_BYTE, RA); R[2] = ED_PUTFILL (R[2], fill); /* now fault safe */ R[3]++; break; case EO_LOAD_SIGN: /* load sign */ sign = edit_read_sign (acc); R[3]++; break; case EO_LOAD_PLUS: /* load sign if + */ if (!(cc & CC_N)) sign = edit_read_sign (acc); R[3]++; break; case EO_LOAD_MINUS: /* load sign if - */ if (cc & CC_N) sign = edit_read_sign (acc); R[3]++; break; case EO_INSERT: /* insert char */ c = Read ((R[3] + 1) & LMASK, L_BYTE, RA); Write (R[5], ((cc & CC_C)? c: fill), L_BYTE, WA); R[5] = (R[5] + 1) & LMASK; /* now fault safe */ R[3]++; break; case EO_BLANK_ZERO: /* blank zero */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if (t == 0) RSVD_OPND_FAULT; if (cc & CC_Z) { /* zero? */ do { /* repeat and blank */ Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); } while (--t); } R[3]++; /* now fault safe */ break; case EO_REPL_SIGN: /* replace sign */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if (t == 0) RSVD_OPND_FAULT; if (cc & CC_Z) Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); R[3]++; /* now fault safe */ break; case EO_ADJUST_LNT: /* adjust length */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if ((t == 0) || (t > 31)) RSVD_OPND_FAULT; R[0] = R[0] & WMASK; /* clr old ld zero */ if (R[0] > t) { /* decrease */ for (i = 0; i < (R[0] - t); i++) { /* loop thru src */ d = edit_read_src (i, acc); /* get nibble */ if (d) cc = (cc | CC_V | CC_C) & ~CC_Z; } /* end for */ edit_adv_src (R[0] - t); /* adv src ptr */ } /* end else */ else R[0] = R[0] | (((R[0] - t) & WMASK) << 16); R[3]++; break; case EO_FILL: /* fill */ for (i = 0; i < rpt; i++) /* fill string */ Write ((R[5] + i) & LMASK, fill, L_BYTE, WA); R[5] = (R[5] + rpt) & LMASK; /* now fault safe */ break; case EO_MOVE: for (i = 0; i < rpt; i++) { /* for repeat */ d = edit_read_src (i, acc); /* get nibble */ if (d) /* test for non-zero */ cc = (cc | CC_C) & ~CC_Z; c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ Write ((R[5] + i) & LMASK, c, L_BYTE, WA); } /* end for */ edit_adv_src (rpt); /* advance src */ R[5] = (R[5] + rpt) & LMASK; /* advance dst */ break; case EO_FLOAT: for (i = j = 0; i < rpt; i++, j++) { /* for repeat */ d = edit_read_src (i, acc); /* get nibble */ if (d && !(cc & CC_C)) { /* nz, signif clear? */ Write ((R[5] + j) & LMASK, sign, L_BYTE, WA); cc = (cc | CC_C) & ~CC_Z; /* set signif */ j++; /* extra dst char */ } /* end if */ c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ Write ((R[5] + j) & LMASK, c, L_BYTE, WA); } /* end for */ edit_adv_src (rpt); /* advance src */ R[5] = (R[5] + j) & LMASK; /* advance dst */ break; default: /* undefined */ RSVD_OPND_FAULT; } /* end case pattern */ R[3] = (R[3] + 1) & LMASK; /* next pattern byte */ R[2] = ED_PUTCC (R[2], cc); /* update cc's */ } /* end for pattern */ if (R[0]) /* pattern too short */ RSVD_OPND_FAULT; PSL = PSL & ~PSL_FPD; /* clear FPD */ if (cc & CC_Z) /* zero? clear n */ cc = cc & ~CC_N; if ((cc & CC_V) && (PSL & PSW_DV)) /* overflow & trap enabled? */ SET_TRAP (TRAP_DECOVF); R[0] = R[4]; /* restore src len */ R[1] = R[1] - (R[0] >> 1); /* restore src addr */ R[2] = R[4] = 0; return cc; default: RSVD_INST_FAULT; } /* end case op */ return cc; } /* Get packed decimal string Arguments: lnt = decimal string length adr = decimal string address src = decimal string structure acc = access mode The routine returns the length in int32's of the non-zero part of the string. To simplify the code elsewhere, digits are range checked, and bad digits cause a fault. */ int32 ReadDstr (int32 lnt, int32 adr, DSTR *src, int32 acc) { int32 c, i, end, t; *src = Dstr_zero; /* clear result */ end = lnt / 2; /* last byte */ for (i = 0; i <= end; i++) { /* loop thru string */ c = Read ((adr + end - i) & LMASK, L_BYTE, RA); /* get byte */ if (i == 0) { /* sign char? */ t = c & 0xF; /* save sign */ c = c & 0xF0; /* erase sign */ } if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; /* if (((c & 0xF0) > 0x90) || /* check hi digit */ /* ((c & 0x0F) > 0x09)) /* check lo digit */ /* RSVD_OPND_FAULT; */ src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); } /* end for */ if ((t == 0xB) || (t == 0xD)) /* if -, set sign */ src->sign = 1; return TestDstr (src); /* clean -0 */ } /* Store decimal string Arguments: lnt = decimal string length adr = decimal string address dst = decimal string structure V = initial overflow flag acc = access mode Returns condition codes. PSL.NZ are also set to their proper values PSL.V will be set on overflow; it must be initialized elsewhere (to allow for external overflow calculations) The rules for the stored sign and the PSW sign are: - Stored sign is negative if input is negative, and the result is non-zero or there was overflow - PSL sign is negative if input is negative, and the result is non-zero Thus, the stored sign and the PSL sign will differ in one case: a negative zero generated by overflow is stored with a negative sign, but PSL.N is clear */ int32 WriteDstr (int32 lnt, int32 adr, DSTR *dst, int32 pslv, int32 acc) { int32 c, i, cc, end; end = lnt / 2; /* end of string */ ProbeDstr (end, adr, WA); /* test writeability */ cc = SetCCDstr (lnt, dst, pslv); /* set cond codes */ dst->val[0] = dst->val[0] | 0xC | dst->sign; /* set sign */ for (i = 0; i <= end; i++) { /* store string */ c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; Write ((adr + end - i) & LMASK, c, L_BYTE, WA); } /* end for */ return cc; } /* Set CC for decimal string Arguments: lnt = string length dst = decimal string structure pslv = initial V Output: cc = condition codes */ int32 SetCCDstr (int32 lnt, DSTR *dst, int32 pslv) { int32 psln, pslz, i, limit; uint32 mask; static uint32 masktab[8] = { 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 }; mask = 0; /* can't ovflo */ pslz = 1; /* assume all 0's */ limit = lnt / 8; /* limit for test */ for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ if (i == limit) /* at limit, get mask */ mask = masktab[lnt % 8]; else if (i > limit) /* beyond, all ovflo */ mask = 0xFFFFFFFF; if (dst->val[i] & mask) /* test for ovflo */ pslv = 1; dst->val[i] = dst->val[i] & ~mask; /* clr digits past end */ if (dst->val[i]) /* test nz */ pslz = 0; } dst->sign = dst->sign & ~(pslz & ~pslv); psln = dst->sign & ~pslz; /* N = sign, if ~zero */ if (pslv && (PSL & PSW_DV)) SET_TRAP (TRAP_DECOVF); return (psln? CC_N: 0) | (pslz? CC_Z: 0) | (pslv? CC_V: 0); } /* Probe decimal string for accessibility */ void ProbeDstr (int32 lnt, int32 addr, int32 acc) { Read (addr, L_BYTE, acc); Read ((addr + lnt) & LMASK, L_BYTE, acc); return; } /* Add decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string ds = dest decimal string cy = carry in Output = 1 if carry, 0 if no carry This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. We trace the history of a pair of adjacent digits to see how the carry is fixed; each parenthesized item is a 4b digit. Assume we are adding: (a)(b) I + (x)(y) J First compute I^J: (a^x)(b^y) TMP Note that the low bit of each digit is the same as the low bit of the sum of the digits, ignoring the carry, since the low bit of the sum is the xor of the bits. Now compute I+J+66 to get decimal addition with carry forced left one digit: (a+x+6+carry mod 16)(b+y+6 mod 16) SUM Note that if there was a carry from b+y+6, then the low bit of the left digit is different from the expected low bit from the xor. If we xor this SUM into TMP, then the low bit of each digit is 1 if there was a carry, and 0 if not. We need to subtract 6 from each digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift it right 4 to the digits that are affected, and subtract 6*adjustment (actually, shift it right 3 and subtract 3*adjustment). */ int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) { int32 i; uint32 sm1, sm2, tm1, tm2, tm3, tm4; for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ sm2 = sm1 + 0x66666666; /* force carry out */ cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ tm2 = tm1 ^ sm2; /* get carry flags */ tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ tm4 = 0x22222222 & ~tm3; /* clear where carry */ ds->val[i] = (sm2 - (3 * tm4)) & LMASK; /* final result */ } return cy; } /* Subtract decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string ds = dest decimal string Outputs: s2 - s1 in ds Note: the routine assumes that s1 <= s2 */ void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) { int32 i; DSTR compl; for (i = 0; i < DSTRLNT; i++) /* 10's comp s2 */ compl.val[i] = 0x99999999 - s1->val[i]; AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ return; } /* Compare decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string Output = 1 if >, 0 if =, -1 if < */ int32 CmpDstr (DSTR *s1, DSTR *s2) { int32 i; for (i = DSTRMAX; i >=0; i--) { if (s1->val[i] > s2->val[i]) return 1; if (s1->val[i] < s2->val[i]) return -1; } return 0; } /* Test decimal string for zero Arguments: dsrc = decimal string structure Returns the non-zero length of the string, in int32 units If the string is zero, the sign is cleared */ int32 TestDstr (DSTR *dsrc) { int32 i; for (i = DSTRMAX; i >= 0; i--) { if (dsrc->val[i]) return (i + 1); } dsrc->sign = 0; return 0; } /* Get exact length of decimal string Arguments: dsrc = decimal string structure nz = result from TestDstr */ int32 LntDstr (DSTR *dsrc, int32 nz) { int32 i; if (nz == 0) return 0; for (i = 7; i >= 0; i--) { if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; } return ((nz - 1) * 8) + i; } /* Create table of multiples Arguments: dsrc = base decimal string structure mtable[10] = array of decimal string structures Note that dsrc has a high order zero nibble; this guarantees that the largest multiple won't overflow Also note that mtable[0] is not filled in */ void CreateTable (DSTR *dsrc, DSTR mtable[10]) { int32 (i); mtable[1] = *dsrc; for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); return; } /* Word shift right Arguments: dsrc = decimal string structure sc = shift count */ void WordRshift (DSTR *dsrc, int32 sc) { int32 i; if (sc) { for (i = 0; i < DSTRLNT; i++) { if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc]; else dsrc->val[i] = 0; } } return; } /* Word shift left Arguments: dsrc = decimal string structure sc = shift count */ int32 WordLshift (DSTR *dsrc, int32 sc) { int32 i, c; c = 0; if (sc) { for (i = DSTRMAX; i >= 0; i--) { if (i >= sc) dsrc->val[i] = dsrc->val[i - sc]; else { c |= dsrc->val[i]; dsrc->val[i] = 0; } } } return c; } /* Nibble shift decimal string right Arguments: dsrc = decimal string structure sc = shift count cin = carry in */ uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) { int32 i, s, nc; if (s = sc * 4) { for (i = DSTRMAX; i >= 0; i--) { nc = (dsrc->val[i] << (32 - s)) & LMASK; dsrc->val[i] = ((dsrc->val[i] >> s) | cin) & LMASK; cin = nc; } return cin; } return 0; } /* Nibble shift decimal string left Arguments: dsrc = decimal string structure sc = shift count cin = carry in */ uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin) { int32 i, s, nc; if (s = sc * 4) { for (i = 0; i < DSTRLNT; i++) { nc = dsrc->val[i] >> (32 - s); dsrc->val[i] = ((dsrc->val[i] << s) | cin) & LMASK; cin = nc; } return cin; } return 0; } /* Do 4b of CRC calculation Arguments: crc = current CRC ^ char tbl = 16 lw table base Output: new CRC */ int32 do_crc_4b (int32 crc, int32 tbl, int32 acc) { int32 idx = (crc & 0xF) << 2; int32 t; crc = (crc >> 4) & 0x0FFFFFFF; t = Read ((tbl + idx) & LMASK, L_LONG, RA); return crc ^ t; } /* Edit routines */ int32 edit_read_src (int32 inc, int32 acc) { int32 c, r0, r1; if (R[0] & LSIGN) { /* ld zeroes? */ r0 = (R[0] + (inc << 16)) & LMASK; /* retire increment */ if (r0 & LSIGN) /* more? return 0 */ return 0; inc = (r0 >> 16) & 0x1F; /* effective inc */ } r1 = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK; /* eff addr */ r0 = (R[0] - inc) & 0x1F; /* eff lnt left */ if (r0 == 0) { /* nothing left? */ R[0] = -1; /* out of input */ RSVD_OPND_FAULT; } c = Read (r1, L_BYTE, RA); return (((r0 & 1)? (c >> 4): c) & 0xF); } void edit_adv_src (int32 inc) { if (R[0] & LSIGN) { /* ld zeroes? */ R[0] = (R[0] + (inc << 16)) & LMASK; /* retire 0's */ if (R[0] & LSIGN) /* more to do? */ return; inc = (R[0] >> 16) & 0x1F; /* get excess */ if (inc == 0) /* more to do? */ return; } R[1] = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;/* retire src */ R[0] = (R[0] - inc) & 0x1F; return; } int32 edit_read_sign (int32 acc) { int32 sign; sign = Read ((R[3] + 1) & LMASK, L_BYTE, RA); /* read */ R[2] = ED_PUTSIGN (R[2], sign); /* now fault safe */ return sign; } #else extern int32 R[16]; extern int32 PSL; extern int32 SCBB; extern int32 fault_PC; extern int32 ibcnt, ppc; extern int32 pcq[PCQ_SIZE]; extern int32 pcq_p; extern jmp_buf save_env; /* CIS instructions - invoke emulator interface opnd[0:5] = six operands to be pushed (if PSL = 0) cc = condition codes opc = opcode If FPD is set, push old PC and PSL on stack, vector thru SCB. If FPD is clear, push opcode, old PC, operands, new PC, and PSL on stack, vector thru SCB. In both cases, the exception occurs in the current mode. */ int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc) { int32 vec; if (PSL & PSL_FPD) { /* FPD set? */ Read (SP - 1, L_BYTE, WA); /* wchk stack */ Write (SP - 8, fault_PC, L_LONG, WA); /* push old PC */ Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ SP = SP - 8; /* decr stk ptr */ vec = ReadLP ((SCBB + SCB_EMULFPD) & PAMASK); } else { if (opc == CVTPL) /* CVTPL? .wl */ opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3]; Read (SP - 1, L_BYTE, WA); /* wchk stack */ Write (SP - 48, opc, L_LONG, WA); /* push opcode */ Write (SP - 44, fault_PC, L_LONG, WA); /* push old PC */ Write (SP - 40, opnd[0], L_LONG, WA); /* push operands */ Write (SP - 36, opnd[1], L_LONG, WA); Write (SP - 32, opnd[2], L_LONG, WA); Write (SP - 28, opnd[3], L_LONG, WA); Write (SP - 24, opnd[4], L_LONG, WA); Write (SP - 20, opnd[5], L_LONG, WA); Write (SP - 8, PC, L_LONG, WA); /* push cur PC */ Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ SP = SP - 48; /* decr stk ptr */ vec = ReadLP ((SCBB + SCB_EMULATE) & PAMASK); } PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T); JUMP (vec & ~03); /* set new PC */ return 0; /* set new cc's */ } #endif simh-3.8.1/VAX/vax_mmu.c0000644000175000017500000004720711112313420013115 0ustar vlmvlm/* vax_mmu.c - VAX memory management Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Jul-08 RMS Removed inlining support 28-May-08 RMS Inlined physical memory routines 29-Apr-07 RMS Added address masking for system page table reads 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 30-Sep-04 RMS Comment and formating changes 19-Sep-03 RMS Fixed upper/lower case linkage problems on VMS 01-Jun-03 RMS Fixed compilation problem with USE_ADDR64 This module contains the instruction simulators for Read - read virtual Write - write virtual ReadL(P) - read aligned physical longword (physical context) WriteL(P) - write aligned physical longword (physical context) ReadB(W) - read aligned physical byte (word) WriteB(W) - write aligned physical byte (word) Test - test acccess zap_tb - clear TB zap_tb_ent - clear TB entry chk_tb_ent - check TB entry set_map_reg - set up working map registers */ #include "vax_defs.h" #include typedef struct { int32 tag; /* tag */ int32 pte; /* pte */ } TLBENT; extern uint32 *M; extern const uint32 align[4]; extern int32 PSL; extern int32 mapen; extern int32 p1, p2; extern int32 P0BR, P0LR; extern int32 P1BR, P1LR; extern int32 SBR, SLR; extern int32 SISR; extern jmp_buf save_env; extern UNIT cpu_unit; int32 d_p0br, d_p0lr; /* dynamic copies */ int32 d_p1br, d_p1lr; /* altered per ucode */ int32 d_sbr, d_slr; extern int32 mchk_va, mchk_ref; /* for mcheck */ TLBENT stlb[VA_TBSIZE], ptlb[VA_TBSIZE]; static const int32 insert[4] = { 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF }; static const int32 cvtacc[16] = { 0, 0, TLB_ACCW (KERN)+TLB_ACCR (KERN), TLB_ACCR (KERN), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+TLB_ACCW (USER)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), TLB_ACCR (KERN)+TLB_ACCR (EXEC), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), TLB_ACCW (KERN)+ TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER) }; t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat tlb_reset (DEVICE *dptr); TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat); extern int32 ReadIO (uint32 pa, int32 lnt); extern void WriteIO (uint32 pa, int32 val, int32 lnt); extern int32 ReadReg (uint32 pa, int32 lnt); extern void WriteReg (uint32 pa, int32 val, int32 lnt); /* TLB data structures tlb_dev pager device descriptor tlb_unit pager units pager_reg pager register list */ UNIT tlb_unit[] = { { UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) }, { UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) } }; REG tlb_reg[] = { { NULL } }; DEVICE tlb_dev = { "TLB", tlb_unit, tlb_reg, NULL, 2, 16, VA_N_TBI * 2, 1, 16, 32, &tlb_ex, &tlb_dep, &tlb_reset, NULL, NULL, NULL }; /* Read and write virtual These routines logically fall into three phases: 1. Look up the virtual address in the translation buffer, calling the fill routine on a tag mismatch or access mismatch (invalid tlb entries have access = 0 and thus always mismatch). The fill routine handles all errors. If the resulting physical address is aligned, do an aligned physical read or write. 2. Test for unaligned across page boundaries. If cross page, look up the physical address of the second page. If not cross page, the second physical address is the same as the first. 3. Using the two physical addresses, do an unaligned read or write, with three cases: unaligned long, unaligned word within a longword, unaligned word crossing a longword boundary. Note that these routines do not handle quad or octa references. */ /* Read virtual Inputs: va = virtual address lnt = length code (BWL) acc = access code (KESU) Output: returned data, right justified in 32b longword */ int32 Read (uint32 va, int32 lnt, int32 acc) { int32 vpn, off, tbi, pa; int32 pa1, bo, sc, wl, wh; TLBENT xpte; mchk_va = va; if (mapen) { /* mapping on? */ vpn = VA_GETVPN (va); /* get vpn, offset */ off = VA_GETOFF (va); tbi = VA_GETTBI (vpn); xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) xpte = fill (va, lnt, acc, NULL); /* fill if needed */ pa = (xpte.pte & TLB_PFN) | off; /* get phys addr */ } else pa = va & PAMASK; if ((pa & (lnt - 1)) == 0) { /* aligned? */ if (lnt >= L_LONG) /* long, quad? */ return ReadL (pa); if (lnt == L_WORD) /* word? */ return ReadW (pa); return ReadB (pa); /* byte */ } if (mapen && ((off + lnt) > VA_PAGSIZE)) { /* cross page? */ vpn = VA_GETVPN (va + lnt); /* vpn 2nd page */ tbi = VA_GETTBI (vpn); xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) xpte = fill (va + lnt, lnt, acc, NULL); /* fill if needed */ pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); } else pa1 = (pa + 4) & PAMASK; /* not cross page */ bo = pa & 3; if (lnt >= L_LONG) { /* lw unaligned? */ sc = bo << 3; wl = ReadL (pa); /* read both lw */ wh = ReadL (pa1); /* extract */ return ((((wl >> sc) & align[bo]) | (wh << (32 - sc))) & LMASK); } else if (bo == 1) return ((ReadL (pa) >> 8) & WMASK); else { wl = ReadL (pa); /* word cross lw */ wh = ReadL (pa1); /* read, extract */ return (((wl >> 24) & 0xFF) | ((wh & 0xFF) << 8)); } } /* Write virtual Inputs: va = virtual address val = data to be written, right justified in 32b lw lnt = length code (BWL) acc = access code (KESU) Output: none */ void Write (uint32 va, int32 val, int32 lnt, int32 acc) { int32 vpn, off, tbi, pa; int32 pa1, bo, sc, wl, wh; TLBENT xpte; mchk_va = va; if (mapen) { vpn = VA_GETVPN (va); off = VA_GETOFF (va); tbi = VA_GETTBI (vpn); xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || ((xpte.pte & TLB_M) == 0)) xpte = fill (va, lnt, acc, NULL); pa = (xpte.pte & TLB_PFN) | off; } else pa = va & PAMASK; if ((pa & (lnt - 1)) == 0) { /* aligned? */ if (lnt >= L_LONG) /* long, quad? */ WriteL (pa, val); else if (lnt == L_WORD) /* word? */ WriteW (pa, val); else WriteB (pa, val); /* byte */ return; } if (mapen && ((off + lnt) > VA_PAGSIZE)) { vpn = VA_GETVPN (va + 4); tbi = VA_GETTBI (vpn); xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || ((xpte.pte & TLB_M) == 0)) xpte = fill (va + lnt, lnt, acc, NULL); pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); } else pa1 = (pa + 4) & PAMASK; bo = pa & 3; wl = ReadL (pa); if (lnt >= L_LONG) { sc = bo << 3; wh = ReadL (pa1); wl = (wl & insert[bo]) | ((val << sc) & LMASK); wh = (wh & ~insert[bo]) | ((val >> (32 - sc)) & insert[bo]); WriteL (pa, wl); WriteL (pa1, wh); } else if (bo == 1) { wl = (wl & 0xFF0000FF) | (val << 8); WriteL (pa, wl); } else { wh = ReadL (pa1); wl = (wl & 0x00FFFFFF) | ((val & 0xFF) << 24); wh = (wh & 0xFFFFFF00) | ((val >> 8) & 0xFF); WriteL (pa, wl); WriteL (pa1, wh); } return; } /* Test access to a byte (VAX PROBEx) */ int32 Test (uint32 va, int32 acc, int32 *status) { int32 vpn, off, tbi; TLBENT xpte; *status = PR_OK; /* assume ok */ if (mapen) { /* mapping on? */ vpn = VA_GETVPN (va); /* get vpn, off */ off = VA_GETOFF (va); tbi = VA_GETTBI (vpn); xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ if ((xpte.pte & acc) && (xpte.tag == vpn)) /* TB hit, acc ok? */ return (xpte.pte & TLB_PFN) | off; xpte = fill (va, L_BYTE, acc, status); /* fill TB */ if (*status == PR_OK) return (xpte.pte & TLB_PFN) | off; else return -1; } return va & PAMASK; /* ret phys addr */ } /* Read aligned physical (in virtual context, unless indicated) Inputs: pa = physical address, naturally aligned Output: returned data, right justified in 32b longword */ SIM_INLINE int32 ReadB (uint32 pa) { int32 dat; if (ADDR_IS_MEM (pa)) dat = M[pa >> 2]; else { mchk_ref = REF_V; if (ADDR_IS_IO (pa)) dat = ReadIO (pa, L_BYTE); else dat = ReadReg (pa, L_BYTE); } return ((dat >> ((pa & 3) << 3)) & BMASK); } SIM_INLINE int32 ReadW (uint32 pa) { int32 dat; if (ADDR_IS_MEM (pa)) dat = M[pa >> 2]; else { mchk_ref = REF_V; if (ADDR_IS_IO (pa)) dat = ReadIO (pa, L_WORD); else dat = ReadReg (pa, L_WORD); } return ((dat >> ((pa & 2)? 16: 0)) & WMASK); } SIM_INLINE int32 ReadL (uint32 pa) { if (ADDR_IS_MEM (pa)) return M[pa >> 2]; mchk_ref = REF_V; if (ADDR_IS_IO (pa)) return ReadIO (pa, L_LONG); return ReadReg (pa, L_LONG); } SIM_INLINE int32 ReadLP (uint32 pa) { if (ADDR_IS_MEM (pa)) return M[pa >> 2]; mchk_va = pa; mchk_ref = REF_P; if (ADDR_IS_IO (pa)) return ReadIO (pa, L_LONG); return ReadReg (pa, L_LONG); } /* Write aligned physical (in virtual context, unless indicated) Inputs: pa = physical address, naturally aligned val = data to be written, right justified in 32b longword Output: none */ SIM_INLINE void WriteB (uint32 pa, int32 val) { if (ADDR_IS_MEM (pa)) { int32 id = pa >> 2; int32 sc = (pa & 3) << 3; int32 mask = 0xFF << sc; M[id] = (M[id] & ~mask) | (val << sc); } else { mchk_ref = REF_V; if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_BYTE); else WriteReg (pa, val, L_BYTE); } return; } SIM_INLINE void WriteW (uint32 pa, int32 val) { if (ADDR_IS_MEM (pa)) { int32 id = pa >> 2; M[id] = (pa & 2)? (M[id] & 0xFFFF) | (val << 16): (M[id] & ~0xFFFF) | val; } else { mchk_ref = REF_V; if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_WORD); else WriteReg (pa, val, L_WORD); } return; } SIM_INLINE void WriteL (uint32 pa, int32 val) { if (ADDR_IS_MEM (pa)) M[pa >> 2] = val; else { mchk_ref = REF_V; if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_LONG); else WriteReg (pa, val, L_LONG); } return; } void WriteLP (uint32 pa, int32 val) { if (ADDR_IS_MEM (pa)) M[pa >> 2] = val; else { mchk_va = pa; mchk_ref = REF_P; if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_LONG); else WriteReg (pa, val, L_LONG); } return; } /* TLB fill This routine fills the TLB after a tag or access mismatch, or on a write if pte = 0. It fills the TLB and returns the pte to the caller. On an error, it aborts directly to the fault handler in the CPU. If called from map (VAX PROBEx), the error status is returned to the caller, and no fault occurs. */ #define MM_ERR(param) { \ if (stat) { \ *stat = param; \ return zero_pte; \ } \ p1 = MM_PARAM (acc & TLB_WACC, param); \ p2 = va; \ ABORT ((param & PR_TNV)? ABORT_TNV: ABORT_ACV); } TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat) { int32 ptidx = (((uint32) va) >> 7) & ~03; int32 tlbpte, ptead, pte, tbi, vpn; static TLBENT zero_pte = { 0, 0 }; if (va & VA_S0) { /* system space? */ if (ptidx >= d_slr) /* system */ MM_ERR (PR_LNV); ptead = (d_sbr + ptidx) & PAMASK; } else { if (va & VA_P1) { /* P1? */ if (ptidx < d_p1lr) MM_ERR (PR_LNV); ptead = d_p1br + ptidx; } else { /* P0 */ if (ptidx >= d_p0lr) MM_ERR (PR_LNV); ptead = d_p0br + ptidx; } if ((ptead & VA_S0) == 0) ABORT (STOP_PPTE); /* ppte must be sys */ vpn = VA_GETVPN (ptead); /* get vpn, tbi */ tbi = VA_GETTBI (vpn); if (stlb[tbi].tag != vpn) { /* in sys tlb? */ ptidx = ((uint32) ptead) >> 7; /* xlate like sys */ if (ptidx >= d_slr) MM_ERR (PR_PLNV); pte = ReadLP ((d_sbr + ptidx) & PAMASK); /* get system pte */ #if defined (VAX_780) if ((pte & PTE_ACC) == 0) /* spte ACV? */ MM_ERR (PR_PACV); #endif if ((pte & PTE_V) == 0) /* spte TNV? */ MM_ERR (PR_PTNV); stlb[tbi].tag = vpn; /* set stlb tag */ stlb[tbi].pte = cvtacc[PTE_GETACC (pte)] | ((pte << VA_N_OFF) & TLB_PFN); /* set stlb data */ } ptead = (stlb[tbi].pte & TLB_PFN) | VA_GETOFF (ptead); } pte = ReadL (ptead); /* read pte */ tlbpte = cvtacc[PTE_GETACC (pte)] | /* cvt access */ ((pte << VA_N_OFF) & TLB_PFN); /* set addr */ if ((tlbpte & acc) == 0) /* chk access */ MM_ERR (PR_ACV); if ((pte & PTE_V) == 0) /* check valid */ MM_ERR (PR_TNV); if (acc & TLB_WACC) { /* write? */ if ((pte & PTE_M) == 0) WriteL (ptead, pte | PTE_M); tlbpte = tlbpte | TLB_M; /* set M */ } vpn = VA_GETVPN (va); tbi = VA_GETTBI (vpn); if ((va & VA_S0) == 0) { /* process space? */ ptlb[tbi].tag = vpn; /* store tlb ent */ ptlb[tbi].pte = tlbpte; return ptlb[tbi]; } stlb[tbi].tag = vpn; /* system space */ stlb[tbi].pte = tlbpte; /* store tlb ent */ return stlb[tbi]; } /* Utility routines */ extern void set_map_reg (void) { d_p0br = P0BR & ~03; d_p1br = (P1BR - 0x800000) & ~03; /* VA<30> >> 7 */ d_sbr = (SBR - 0x1000000) & ~03; /* VA<31> >> 7 */ d_p0lr = (P0LR << 2); d_p1lr = (P1LR << 2) + 0x800000; /* VA<30> >> 7 */ d_slr = (SLR << 2) + 0x1000000; /* VA<31> >> 7 */ return; } /* Zap process (0) or whole (1) tb */ void zap_tb (int stb) { int32 i; for (i = 0; i < VA_TBSIZE; i++) { ptlb[i].tag = ptlb[i].pte = -1; if (stb) stlb[i].tag = stlb[i].pte = -1; } return; } /* Zap single tb entry corresponding to va */ void zap_tb_ent (uint32 va) { int32 tbi = VA_GETTBI (VA_GETVPN (va)); if (va & VA_S0) stlb[tbi].tag = stlb[tbi].pte = -1; else ptlb[tbi].tag = ptlb[tbi].pte = -1; return; } /* Check for tlb entry corresponding to va */ t_bool chk_tb_ent (uint32 va) { int32 vpn = VA_GETVPN (va); int32 tbi = VA_GETTBI (vpn); TLBENT xpte; xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; if (xpte.tag == vpn) return TRUE; return FALSE; } /* TLB examine */ t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { int32 tlbn = uptr - tlb_unit; int32 idx = (uint32) addr >> 1; if (idx >= VA_TBSIZE) return SCPE_NXM; if (addr & 1) *vptr = ((uint32) (tlbn? stlb[idx].pte: ptlb[idx].pte)); else *vptr = ((uint32) (tlbn? stlb[idx].tag: ptlb[idx].tag)); return SCPE_OK; } /* TLB deposit */ t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { int32 tlbn = uptr - tlb_unit; int32 idx = (uint32) addr >> 1; if (idx >= VA_TBSIZE) return SCPE_NXM; if (addr & 1) { if (tlbn) stlb[idx].pte = (int32) val; else ptlb[idx].pte = (int32) val; } else { if (tlbn) stlb[idx].tag = (int32) val; else ptlb[idx].tag = (int32) val; } return SCPE_OK; } /* TLB reset */ t_stat tlb_reset (DEVICE *dptr) { int32 i; for (i = 0; i < VA_TBSIZE; i++) stlb[i].tag = ptlb[i].tag = stlb[i].pte = ptlb[i].pte = -1; return SCPE_OK; } simh-3.8.1/VAX/ka655x.bin_old0000644000175000017500000040000010012251512013631 0ustar vlmvlm"þS1‰1‹1µZ¥ô &  Ð Ÿ ÒŸ0 Ÿ ÒŸ0 }PŸ² в QÛ*¡DÛ+¡HÊ€Ÿ Û+PíP1 íŸ 1†Û!PŸ МPÐÄPÐø PõPýŸ± Ÿ· Ÿ ÐÎPÐb PÐ|PõPýðŸ в Q}¡ ¡D}¡(aС0¡P1xÛ PáPÛ!Pá P ðŸ 1^в Q}¡D¡ }a¡(СP¡0🠟 ÐS PÐ% PÐÖPõPýŸl Ÿr ðŸ в Q}R¡}T¡}V¡}X¡ }Z¡(}\¡0Û¡8Û¡LСD¡<Ëÿ¡H¡@í¡H4П ¡П  ¡П0 ¡П4 ¡ П@ ¡ПD ¡Û¡4ÐUÔŸ Ÿ ;wП  Ð@ Ÿ0 П4 Ð@ Ÿ@ ПD ÐwUÔQnП  Ð Ÿ0 ÐüÿŸ4 Ð@ Ÿ@ ÐüŸD Ð3UÔQ2П  Ð@ Ÿ0 П4 Ð@ Ÿ@ ПD ÐwUÔQÐQŸ šŸ@ PïPPðPQðP QÐQŸ ÐxŸ  Ð|Ÿ žï«PÚPÐX ^žïýÿÿPÑP "ÐP^ÂïýÿÿPÛQš€RÀPõRúžïßüÿÿïúüÿÿ1ÓÒŸ Ÿ0 ÚŸþ }Ÿ² P}Ÿº R}ŸÂ T}ŸÊ V}ŸÒ X}ŸÚ Z}Ÿâ \Û%~ÚŽ%Ú9àŸú Пê ^Пê ^Ú8072ÔQÑPÿ ÝPûï°4|P» <`RРSšƒP0‡2ôR÷|Pº ÝQÝQŸŸL ÝPûïG/ŸŸL ûïÏžŸL Q Ÿ Ÿ *Xd}ƒ’´òŸ' þ0·PŸ ÎÔŸ ÔŸ ÔŸ  ,ŸŸ Ð- Ÿ 1Æÿ0Á00­0J1ºÿ0¿00‡00¼0%08ûïõ81¡ÿ0+1›ÿ0k00 0 01Œÿ1‰ÿ0ÂÓP Ý„ûï§3å Ÿ ݃ûï3žŸ¥ RžŸ ¢Ý¢ûïÖ°PbÝŸ& ÝRûï©K0çZ1,ÿÝ…ûïR300ÑZ1ÿ0âY0ÈZ1 ÿ0‡0¿Z1ÿ0,[0¶ZèP1úý1õþÝŒûï3ïŸú ~ûï 3ÝŸö Ÿï^ûïø2ÔP០ÈP០ÈPá Ÿ ÈP០ÈPÕŸ  ÈPïŸ QðQPïŸú QšAïÐ]QðQPïŸ QðQ PП@ QáQÈP0ÿÐPRšŸ Sžï¶]T‘Sd«¤RP«¤¤Q±PQÀT暤P080ÅYÝÝÝûï :ÐPSíŸ@ %íŸX ð Ÿ ¨Ÿ ”Ÿ ûï7ÔRïŸ PÑP  ‘Ÿ 🠔Ÿ ÐRÝŒûï¿103á Ÿ #П@ PíPèR íŸX ûï-žï/ùÿÿPÑP 9݆ûït1ÝÝÝûï=9ÉSPQ ЉP0ÜX ݈ûïH1ßÝ€ÝûïÏ ÕP0±XµüП| Pš PÃŒPYÐ ©|”É€ÉÉ‚Ƀ0öéP\0aÔV0éPK° §xVP©Pg0~éP9³g.ÐP³€§ÖPÐP~ÐV~šÉ€PÁAP~žï÷\~ûïœ0òV¯–É€žšPЬQà¬.ËàÿÿWPÉàP~šÉ€~žïž\Pâ¬ÀPÐP~ûïW0ЀigÿŸ П 0`.šP•g èŸ ïÔPµüП| Pš PÃŒPY»" ^°"®žïnП® ®Ð^Ÿ® šP1âÃ^©žïÊ©ïŸ@ R°BŸ@ É„¨ BŸ@ Ðh ©|”É€ÉÉ‚Ƀ”É0¦ éP40»š©0‚èPô©öଞïa\~ûïz/0´gÔ©x–É€ÆÐ@ ©|”É€ÉÉ‚ɃÉ0L éP40aš©0(èPô©öଞï\~ûï /0Å´gÔ©x–ɀƚPЬQЮŸ® À^ºŽïŸ@ R°É„BŸ@ ЩxW´gà¬\ËàÿÿWPÉàP~•É-šÉ€~žï;[P•ÉžïR[Pâ¬ÀPÐP~ûïš.žï÷ZPâ¬ÀPÐP~ûï.µüП| Pš PÃŒPY»" ^°"®žïnП® ®Ð^Ÿ® šP1äÃ^©žïÉ©žïç©ïŸ@ R°BŸ@ É„¨ BŸ@ Ь©|”É€”É‚ÿÉѬ .Ðh ©|É‚ÉÐ@ ©|ɂɬɀÉɃЬPžïŒ_P(`©!žï Y~ûï˜-0š éP,Ь0«þÉš©0mèPô©ö0à´gÔ©x埥 žïyY~ûïX-šPЮŸ® À^ºŽïŸ@ R°É„BŸ@ ЩxW ´g埥 µüП| Pš PÃŒPY»" ^°"®žïnП® ®Ð^Ÿ® šP1¤Ã^©žï¾©ïŸ@ R°BŸ@ É„¨ BŸ@ Ь©|”É€”É‚ÿÉѬÐ ©|¬É€ÉɃ0ˆ éPBÔ¬0šý0eˆðÉ0ZéP(0¼éP"žïsY~ûïW,0÷èPžïzY~ûïC,´gÔ©xšPЮŸ® À^ºŽïŸ@ R°É„BŸ@ ЩxW´gÐylü© ´gÐW©x>ɈR°„bËþÿÿRQÀQÐQ¢°¢ï RPÈ€PЀ SÐPƒÖPÐPƒÖPÐPcÐ QÐ@'XÿŸ П 0Î)°§S6àQS èŸ ì)°‚§óQÐÐÀ½ðÿŸ П 0ž)µ§ èŸ ñ1ZšiÐPÁHÉŠQžÉ˜RžÉÜSÉ€Q‚°0cÀ4QÀ4SõPìÐÿÿÿÿÉP°0ÉÁˆÉŠÉØÉ,0É šPÐPº<Ú7»€Ð- [»,º€ÐPÐ [ÐˉŸî Ÿ ÐPŸ ÐPÐ [0)ýÐPZžË‘P}€R|T‘ËÉ}€TÊTRÊUS|ˑЫ XЫYŸË‘ÝkÝYûïgÕPËTË‘VËUË•WÑRVÑSWàkÀZYÓÿX0ÊôXÀÐPàkæÐ««,ÐY«}Ë‘« ÝkÝ«ûÏÞüúïÖ`ÏýÂúïŠï?ÕP>(@Ÿ¥ Ÿ —Ÿå 0s3ÕP#ž¯ÓPÑŸ PÐ"PП P`ÕP0P³Ð [ÐPžË‘RžŸ€ SЂƒÀR–P‘PËÉñšËÉ~ÂnšË‰~šËŠ~ûïÄÐ [0Ð-Pž«0Q•a€P÷•PÐ*PšPŸ П Ý™ûï­ÕË‘ÐˉPÐË‘Q0'ÐˉPˀˑQ0iÝPÝŒûïwÐŽP}PRÔT.0F€PTàT PË‘ŸË‘Ý @ÝRûïäÕPâTÖRôSÏ0\áTÐ*P€PTÐ*PÐPÐ-P}PRÔT$ŸË‘Ý @ÝRûï‘ÕPšË‘P€PT0ßÖRôSÙÎTPšPP0ÑÐPÐ*PП| Pš PÃl PY,©ÔXžï!a~ûï­ÝÝQŸÉ Ÿïbûï^ÕP0ªèPÜß©iŸï5ûï ÑPlЩP±POL±PP ûïéªÐ© QÐPHÉÐQHÉÚ¼ 8Ú¼ 2Ú¼ ,м Ÿê "Û¼ Û¼ Û¼ Û¼  Пê ¼ ÐQÐPЬQá¬1‹¬?††††¡††††††††††††¼×†††††††††††††††††††††††††††††††††††††ö†††††††Ð%P1à¬Ú¼ ¬1‚Û¬PÐP¼ ÐP1tà¬ м Ÿê 1dПê ¼ 1Yà¬ м Ÿþ 1IПþ ¼ 1>ଠ𼠟ò 1,ïŸò ¼ 1ଠ𼠟ú 1 ïŸú ¼ 1П  Ÿ  àŸú 0®ï¬RËÿ¬PÃRQÀ¬QÊÿQÑPQ0ÝR0ÐŽRÕPÖ¬Ö¬ õRêÐPЬPËþÿÿPTï PUïPVV"/ÛRÛ SÑUS Û RÛ SÑUSÛ RÛ SÑUSÐ%P1ÞEbW‘VÐgX)ÑW€Ð%P1ÿÞ~~Ý€ €ÝWûÏÓüÐŽXÕP1åàXÐ!P1Ûà¬,ïXPïŸò QðQPï¬QðQPáPï§[Ð!P1ªà¬,ÈX‘VÐXgÝXŸnÝ€ ÝWûÏuüÀ^ÕP1yËàÿXYx YYÈTYÐYPЬ Qà¬ÐYQЬ PËÿYRï¬S×SÀYSÊÿSÑRSê¬RR `a°`aÐ`a}`a }`a} ¡ÐPQÐPЬPáP$áP áPáP ÈŸD  ȃŸD ЬPЬ Qà¬Ь PЬQê¬RR `a°`aÐ`a}`a }`a} ¡á¬ Ê€ŸD á¬ÐPQÐPà¬=ÑQ0 ÑQÿÿ?ÑQ "ÑQÿП PáPȈŸ š%Pà¬Ëÿßϼ PÐ#Pм Ÿò ÐPÔQПò ¼ ÐPÔQµЬTÊÿÿÿ¬úlï”òÿÿÝŸnÉ ¬~ÝTûï¹úÿÿÖTÐŽRÕPÐQ‘Rý ‘Rÿ‘Rþ#xRR×^ŸnÉ ¬~ÝTûï„úÿÿŽRÖTÕPÐQÝRŸï>e±Rÿžï7enûïÌ žïvYS±R£!ïcPžC SícéŸïeûï¡ 1;Ÿ£ïc~Ÿïædûï‰ Â ^Ô­ø@ݬß­üÅ­øPïPc~ÝTûïÕPÐQÀQTÁ­øPícP Ÿï¾dûïD Ö­øíc­ø¸±Rþÿ6±Rýÿ\Þ~~É€¬~ÝTûï¤ùÿÿÀTÕPÐQŸïdûïþ -Þ~~É@¬~ÝTûïuùÿÿÀTÕPÐQŸïfdûïÏ ÝŒûï ±R±R¯ ±RÏ1GÐT­ôÂ^ŸnÉ@¬~ÝTûïùÿÿÕPݬÝTûïÕðÿÿ2Ž~À­ônŸïæcûïj ÀTô­ü½ÐPìTQµüЬRÔ¼ ¬|||||ÝŸnË€¬PÉ@ïÛc¬~ÝRûï¥øÿÿÐŽQÕP1ê¬ ˜QQ2QQš¬PÀ@ï†cRÁQR~ŸïdûïÜš¬PÐ@ïicQÐPšP0§šTSžïdP‘S`‘S ÀPðžïðcQ2 PÀPQaS¼ ÝSŸï3dûï‰1Zà¬È€¬Ö¬úlÏÿÊ€¬׬ÀQRËðÿÿÿSPžï-cQÁQ@a~ŸïôcûïB1ËðÿÿÿSPžï cQÁQ@a~ŸïÕcûï1ïËðÿÿÿSPžïåbQÁQ@a~Ÿï´cûïú1ËËðÿÿÿSPžïÁbQÁQ@a~Ÿï•cûïÖ1§ËðÿÿÿSPžïbQÁQ@a~Ÿïwcûï²1ƒš¬P0‰}V~}T~Ÿnš¬PžïZcQÁQ@a~ûï‰ÐT¼ 1VËðÿÿÿSPžïLbQÁQ@a~Ÿïccûïa12šP09ÝTŸïRcûïI1šP0!ËðÿÿÿSPžï bQÁQ@a~ÝTŸï-cáSžï.cnûï1ãšP0êËðÿÿÿSPžïÓaQÁQ@a~ÝTŸï cáSžïcnûïÛ1¬šP0³ËðÿÿÿSPžïœaQÁQ@a~ÝTŸïíbáSžïîbnûï¤1ušP0|˜TTÁRT~ŸïÜbáSžïÙbnûï|1MšP0T2TTÁRT~ŸïÃbáSžïÀbnûïT1%šP0,ÁRT~Ÿï­báSžïªbnûï/1ìRQÐPÐQÝP|~|~ŸnÉ@ïÈ`¬~ÝRûï’õÿÿÕPÝ}ŽT}ŽVÐŽQÀAïˆ`Rµ|ÔVЬTEÂ^šdP›@¤nž@¤®Ð^QЬP@À^PÐVÐTUÖVÐTUšdPž@¤TšdPž@¤Tµd·ÕV šeP(P¥¼ ÐVP»0±`a7}`R}aTµR'šƒPàPïGQ‚ Pš…QàQï9Q‚ Q‘PQ ·T·RÕµT ÔPÐPÐPº0µ<Â^Ð^Z,ŸjÔRÔUݬûï‘B¼ݬÝZûï—ÑQ-ÐQPžï—aQ±PÁ±PÁ2ÁQžï}aSÀSQaÀQØÕR ×RšB¼P0®1¦ÿÔUÝŒûï­ÐPÑR¬ Ð$Pì QÑRQ B¼ÖRš P0ÀÔU1nÿÝŒûïwݬûïèݬŸï ûï]ÔU1Bÿ%sŸïûïHÔRݬûï·ÔU1!ÿ^U ì QÑRQªB¼ÖRšªP0PÔU1þþ1ûþÔQáPïˆPˆQá Ÿ ˆQéUˆQàPïlP€@PÝPŸï[`˜AïT`PÀPnûïÍÐUà Ÿ éUÝPŸïU`ûï¯ÐŽPáPï%P0ŒÁP@~Ð^P0yÐŽP0sµÑ¬úlïrݬŸïK`ûïaÐ¥ P”`ˆ`” ” ” Ô  Ý[Ð- [»(ÐŽ[ÐP00ÏÿÐPÝ[Ð- [žï« žï«$žïð«džïñ«lžï«(žï«,Ð Pð `ª`ÐŽ[» Ð¥ S•£—£š£QšA£P–Q‹üQ£º ÕP0æ0“0æÑP‡“ïЀPº 0šPPº » xPRxPSÀSRx PSÀSRx PSÀSRx PSÀSRÐ¥ SŠcÕR0·å0wác ác PÐQ0•£ÕRä0«åÑPRÜÐPÐ-Q—£š£QšA£P–Q‹üQ£ÐQº » ÐPRÐ¥ SÐ- [0ácùàc0» éP÷ÐRQ»$º » Ð¥ Sí Ÿ 0¬éP0¯á P0þPwÐ- [»déP(»l‘Pˆc‘PŠcác‘P‘Pˆc^‘PŒcácRžïU^R» éPúš‚Q@»$ð‘PŠc”£”£”£Õ£ У ^Ýn‘£š£QŠüQPA£–Q‹üQ£–£º Û PïPPÛ!Pá PPÛ"PïPPÚQ#ÔXÔYÔZš‹P‘P%åX‘k-ÈXÖ[ÊX‘k*ÐŒZÈXÖ[ÕZÎZZÈX0ÔZÊX‘k0ÈXkPáPïÄLxZQxZZÀQZ‚0PÀPZÖ[ÈXÝ‘.kÖ[‘*kÐŒYÎYYÖ[ÈX%ÔYkPáPï„LxYQxYYÀQY‚0PÀPYÖ[ÈXÝÑYZÐYZÈXkPàPïLÊXš‹PáPïL€ P‘PrxPP‹PÞï]Q±PaÀQÕaô<¡PµüЬPÑP0ÐP[ÐP[À\0èþáP*¯P€€-6?HQQZ`fkpuzˆŽ”š•P0•ý1ÄÿÐŒP0Œý1»ÿÐP01²ÿÐP0„1©ÿÐP0{1 ÿÐ P0r1—ÿ0C1‘ÿ0Y1‹ÿÐPÐPÐP ÐPÐPÈX0$1iÿ0_1cÿ0Y1]ÿ0S1Wÿ0M1QÿÐŒWÝWûï‡äÿÿÐPVïXPP *Gd *Gd ƒ£¿ ƒ£¿ÐVSÔTÔU1¼ÐVPÐYQ0gÐPSÃSYPÔQ0dÐPTÔU1ŸÐVPÐZQ0JÐPSÃSZPÔQ0GÐPTÔU1‚}YP00ÐVQ0*ÐPSÃSZPÔQ0'ÐPTÔUcÐVPÐYï3Æûß0 ÐPSÔTÃSYPÔQ0ÐPUCÐVPÐZQ0îÐPSÔTÃSZPÔQ0éÐPU'}YP0ÕÐVQ0ÏÐPSÃSYPÔQ0ÌÐPTÃYZPÔQ0ÀÐPU PÐTQ0Dš‡P0üôS÷ PÐUQ00ÐŒP<`VРW1æþ»ü|Vžï7[SàXžï<[SÐŒVÔT{PVVRBc~ÖTÕVñÔUÔVÔWïXPP " ((. 888 >>D,ÃTZU&ÃTZW ÃYZUÃTYWÃTZVÃTZW ÃTYWÃYZV PÐUQ00PÐWQ0‡šŽP0UûõT÷ PÐVQ0uºüàXáXáX PÃYZQ0ZáXáXáX PÃYZQ0C»<ÃPRžïWZSàXžï\ZSÐŒTÀRTÔUïdPš@cP0êúïdPš@cP0Þú×TóRUâº<» }PRÐRP0ÇúôS÷º ÝRÐPRÑR€&Ð?P0­úïRPš@ïîYP0úïRPš@ïÞYP0úÐRP0hÛÐPR•b Ð P0zúÐ P0túÐRPÐŽRÑPQÐQPÑPQÐQPµ<ЬZЬP0ÓùÑQ-Pª03ª %2AGMS_ekv~Š•šªPÐQšªPªªÐQšªP€@Pª1³ÿ0Y1 ÿ01šÿ0p1”ÿš›PªÐQ0;1‚ÿ0X1|ÿšPªÐQªª1iÿšªPªÐQˆjªª1RÿªÐQŠj”ª”ª”ª,Ÿª,ŸªšªP‘Pˆjª@ª–ªàjájšªPP0 23@@AAšªR¤ Bª‚0P PBª–ª‘ªˆjPªžïsXQ‘Áª‘ªÁ‘ªÁ xªªÁªÀQÑÿÿÿÿa˵üÐ Wå gå gÂ^Ð^V,ŸfŸ Ð$¼PÐ|sPЈîPõPýßïÐXûï‰úÿÿÝÈÝVûÏþ‘›PC‘?¦=‘c¦6<¦PÑP€áPïuXâ gáPïyXâ gà g ðg§ÐPÐ-PÒ Ÿ0 ០øÒŸ0 ,Ÿ¤ŸX ðŸX ០&ðŸX Ð [,Ÿ«Ô«&â Ÿ È@Ÿ åŸ åŸ å Ÿ åŸ ðŸ å Ÿ å Ÿ ð Ÿ 0¹áП@ PíP1'ÐX Y©vÒŸ0 П@ UïUUП QðUQðU QÐQŸ žï–WRÀEïŽWRÐS©vÛ"PàP1ÕšbPÚP#ÐEïOWQŸ Ð{PÐ=PЦPõPýôQÒ©vÛ PàP1‹©vÛ!PÓèP1x©v‘PbmÖRõS…Ÿ ÐhYPÐØ‚PÐPQPõPý©vÒŸ0 Ÿ ÐXêPÐh/PÐ02 PõPý1ÈþÐPŸ@ ÒŸ0 šŸÎ XðµüÐ [â Ÿ Ÿa ”Ÿ¦ ¬ŸÌ ¬ŸÍ ¬ Ÿc Ð- [ïûå Ÿ ÝŒûïÒ÷ÿÿ‹üŸa PšPP@ï{VPžŸ¦ P–`‘``ÝŒûïŸ÷ÿÿŸŸb ŸïSVûïŒ÷ÿÿÐP»ŸŸÒ ŸŸÐ ŸŸÏ ŸŸÎ šŸÑ ~ŸŸÍ Ÿï!VûïT÷ÿÿ‘ŸÑ =žŸ¨ PÝpÝpÝpÝpÝpÝpÝpÝpÝpÝpŸïVû ï#÷ÿÿº»»ÿŸïWVû ï÷ÿÿºúï¢VÏØþÑPЄPÐPÕŸ 1Àn¼¼¼¼¼c1«®€ ¥ ¥1šÐ%Ÿ  П  PàPÐ.Ÿ  П  Ÿ  П Ÿ Ю Ÿ П 1/ЮRžïËçÿÿPžïßçÿÿQÑRPÑRQÐ%Ÿ  ÔŸ 11*П QÐŽPš@ïPÀ^PР¡üÐa¡øÃQ^П  P}PŸ² }RŸº }TŸÂ }VŸÊ }XŸÒ }ZŸÚ }\Ÿâ Ð^Ÿê ÝnÐnRŸï·ûïÛõÿÿÀ€nûïÍõÿÿšBïRxþRR Ÿï²ûï±õÿÿôRðŸï¶ûï¡õÿÿÝŸâ ÝŸÒ ÝŸÂ ÝŸ² Ÿïûï|õÿÿÝŸæ ÝŸÖ ÝŸÆ ÝŸ¶ ŸïŸûïWõÿÿÝŸê ÝŸÚ ÝŸÊ ÝŸº Ÿï¯ûï2õÿÿÝŸÞ ÝŸÎ ÝŸ¾ ŸïÅûïõÿÿÔRŸnŸï>ûïõÿÿòRíðŸ r0 = %08X r4 = %08X r8 = %08X AP = %08X r1 = %08X r5 = %08X r9 = %08X FP = %08X r2 = %08X r6 = %08X r10 = %08X SP = %08X r3 = %08X r7 = %08X r11 = %08X Unexpected exception - (%02X) Parameter = %08X PC = %08X PSL = %08X %08X: %08X %08X %08X %08X (!!8Dn{>AA""">>>AAA"""""A>AYEEYA> >!!>>$ $>AA>AA>AA>AA    >AA""""">!@>''''>$$$$ >AA $ '$2)| '*A0x>AA!! !!!?!!! !!!?!!! ! !!!?!!!& !!!?!!!3 !!!?!!!  !!!?!!!| ? y!!???? !??3?? !3>AA&!!#%)1!!!!!!!!!!!!!!!!! !!!!!!!!&!!!!!!!3!!!!!!!~ 9 ~^!1))%#!!!!!!!!!!!!!!!!! !!!!!!!!!3!!!!!!!!cAA">AA8DBB":BBBJ: >!!> >!!> ! >!!>& >!!>3 >!!>   >!!>7H~ v!!!?!? !!?3!? !3>AA&!!!!!!!!!!!!! !!!!!&!!!!!!!!6Iy v^1)%#!!!!!!!!!! !!!!!!!!!!!!!!! ?>AA>AA$$$??> >HH>B%%$RR!Q!1N8"" @@  !!!!!!  >>AA@  @ 8@@@A> 0($"! =C@@@A><=CAAA>@@ >AAA>AAAA>>AAAa^@@     >AA@ >AAyEeY>"AAAAAA?BBB>BBBB?>AA>?BBBBBBBB?~~~>AqAAA>AAAAAAAAA>>x !A!  !AAAcUUIIAAAAACEIQaAAA>AAAAAAAA>?AAA?>AAAAAAIQ>@?AAA? !AA>A>@@@A>AAAAAAAAA>AAA"""AAAAIIIIU"AA""AAAA"@ << @"A >@@~Aa^=CAAAC=>AA>@@@^aAAAa^>AA>8DD^!!!>AA>=CAAAAA > 8 !!!A1  1A >7IIIIIA=CAAAAA>AAAAA>=CAAAC=^aAAAa^@@@9FB>A>@A>?D8!!!!!!^AA""AAIIIU"A""A!!!!!1. ! p pFI1Ð [~ˉV‘ËÉ-@Ÿ@ Tª d0üÐÝÝݯûïïÿÿ0kϨ dïŸ@ PÔR0–ËódRõ°dPàPìÐX [ÝïºKûïöÈÿÿÑPÿÿÿÿÐ,PÐPRß| PÐPŸ² ´ ï’LЫŸ¾ Пæ ŸÂ ЬŸÆ Пö ŸÚ ËÿŸú ŸÞ ïŸú Ÿâ ÁR~ŸnÝ€ÝûïÖÿÿÕPÐŽŸî (Å=ïì ÂÔŸ¶ ÔŸÊ ÔŸÎ ÔŸÒ ÔŸÖ ÔŸæ Ÿ ÐPíŸ@ ÐPRA0³°c1ß| S±cÐ Pµc1ùÝžûïðåÿÿÝŒûïãåÿÿÝŸûïÖåÿÿ0#ÕP ÝPûïÆåÿÿÝŒûï¹åÿÿÝ ûï¬åÿÿݸ ÝQÝ£ŸïGEûïZàÿÿÐPRÑR-ÝŒûï{åÿÿMÝ£ûïÎÊÿÿ°Pc>×P‘@³:·c Ý"ûïTåÿÿŒš@³QáQïB1‚ @³ôPë±cÝ&ûï/åÿÿ1fÿÐXQA0³°cÑR- ,c³Ÿ ÝŒûïåÿÿß| SÝSݬŸï­DûïæäÿÿÐ [Û‰‘ËÉÐ7PÐË‘Ÿ& ÐP‘ËÉÐ7PžË‘P<`QÀ Q‘q:·`Ð"P±`Ð&P,`°Ÿ žŸ Pš€QáQïr0ó‚  ÿíÐPÐË‘Yàkàk@Ð"PàkÐ'PÔWàkÑY  ÑYü Ð%PÐWÑYÐ&PÝWÝYûïý¶ÿÿàkÐ'Pžï”µÿÿVÔWàkàkÑY  ÑYüÿÿ?;Ð%PÖWÖWÐQÐYRÔYœRRËðÿÿÿRPÑP Ð#PÄ YÀPYõQâÑYÿÐ&P‘ËÉ ‘ËÉ Ð"PÂ^ÔX1žË™P±`Ð&PÂ^,`° nÐPš@nQáQïu/‚ @nôPížnXÝWÝXÝYûfÀ^‘ËÉÐ"PÐË‘QÑQÑQÐ&PžïP>A þPð`Ÿ  Ÿ ÐP      Ð [Û‰ïü¯ÿÿÝPÝ Ÿï¹BûïââÿÿšPÝŸ& Ÿï¬BûïËâÿÿÐPŸŸ ŸïœBûï´âÿÿÐPÝÝûïu²ÿÿÕPÝQûïp±ÿÿÕP ÝQûïûÝûïñšŸ PÑPÐPžïACQš@aPÃPQ~ŸïCCûïSâÿÿÐPÔP•ËÊ àkÐ'PÖPÝPûïJÝûïJÝûïú°ÿÿÝûïè±ÿÿ0‰ÅÐPµÐ¬QšaPáP¬ Ó¡¬Ð'PÀQ‘aåÐP»`Ð [Â^Ð^Y•ËÉ •ËÊÐ"P1ËÉËÊPÞï°ÇÿÿËÐP1ü0( ÕP1òÞ~~ËŸïEûïŸÛÿÿžï EZÀŽZP Ð6P1ÆÐ"P1ÀžïUBËÀjË‘ËɪÐ7P1¤‘Ëɪ Ð5P1–˪kPÐ'P1‰( ª ©ÔV*}FËi|FËF© JVx•šŸ¤©Mä0ÈÖV‘VËÉÍ0QɪàûPËPkPÐ'P14ßïLÝkûÏÈþ1$1Ìÿ}iFË1ÃÿŸiÝVûïHÕP1}FËPÐP«01¡ÿFËÝ«ŸiÝûïÆÿÿÕP1Ü1„ÿÐPÐP ÐPÐPÐPFËÝPŸiÝûïÍÅÿÿÕP1©1QÿFËÝŸiÝ ûï±ÅÿÿÕP115ÿ|~ŸnŸiŸï‚Bûï8Úÿÿ}ŽTP Ð6P1fÐ"P1`žï^BQÀQT}TFËžïNBPÀPU( e©1éþÞ~~ŸiŸïÃBûïíÙÿÿÐŽQP Ð6P1Ð"P1žïŸBRÔSÀQR}RFË1«þÀ^º`Óøk Ëÿÿÿ«PÈPkÓàk Ëüÿÿ«PÈPkïk«µ<Ð [ЬTЬU|DËDËÝÝUÝûïºÄÿÿÕP|~ŸnÝUŸï¥CûïFÙÿÿ}ŽRPÐ)PÈSkÔS}RDËÐP0lÿÞ~~ÝUŸïËJûïÙÿÿÐŽQÑPÐ)PQ -=ЫPÔQ}PDËÐP0éÅÃP«,PÔQ}PDËÐPЫ,PÔQ}PDËÐP}« DËÐPŸï†Jûï—ÞÿÿÐPµðÐ  W0ñéP!°§ïÞÂÿÿ´§ÐVžïPUžï*PT06Ð0 W0ÃéP!°§ï°Âÿÿ´§ÐVžïPUžï!PT0šPЬQà¬â¬ÀUÐU~ûïÞÿÿšPÅVQÀQWšg~ÂVWõP÷ÐT~žïçO~ûïíÝÿÿµüÐ WÔXÔYágpïgPi×PágÀPxPVïgPxPU0ÐT~ÐV~xVP×PÁPU~ÐU~à WPxüPRÐR~žï¨O~ûïˆÝÿÿÀ(RáRŸX žï½O~ûïoÝÿÿÀVXÀTYÀWÑW@ €ÐS~ÐY~ïˆAûïFÝÿÿÕ¬0[šPxôUPÀŸt PxVQÔTÒ€R ÕRÖTxRRôõQìxVPÀUPÑPŸt  Á€Ÿ| QÑUQŸt Qx÷QSÂSTПt Rï+AžïO~ûïÇÜÿÿx÷S~žC¢ÿ~ÐR~žïZO~ûï¬ÜÿÿžïxO~ûïžÜÿÿÐ ~žCÂÿ?~žCb~žï1O~ûïƒÜÿÿžïhO~ûïuÜÿÿÐ@~žCâÿ¿WÐW~žCÂ@~žïýN~ûïOÜÿÿxSSÔTx÷RVx÷WWžï1O~ûï1ÜÿÿÑTVÑTW?àTb;ÐTUÑTVÑTWáTb$ÖTõSéÃUT~x TPÃP~x U~žï—N~ûïéÛÿÿÖTõS²µüïŸ@ R°BŸ@ S¨ BŸ@ »  ^° ®žïnП® ®Ð^Ÿ® šP1v0’Ð W0«Ù0øéP0³ÀWÑW è0ŠÐ0WЀ VÔU0€ÙÐfTÔf0ÈÐT†éP ÕUÐWU ÕU0¦ÔUÀWÑW@0ÍÕU0šPЬQЮŸ® À^ºŽïŸ@ R°SBŸ@ žï NPâ¬ÀPÐP~ûïëÚÿÿžï NPâ¬ÀPÐP~ûïÑÚÿÿ /KFQSA set KFQSA DSSI node number SET enable a DSSI device CLEAR disable a DSSI device SHOW show current configuration HELP print this text EXIT program the KFQSA QUIT don't program the KFQSA Parameters: 0 to 7 760010 to 777774 21 (disk) or 22 (tape) j Task Name? ,j 4j ? KFQSADIRECT¤ SET CLEAR§ SHOW  HELP EXIT QUIT EXT HLT  ISP ERR  DBL ERR1  DBL ERR2  DBL ERR3  HLT INST  SCB ERR3  SCB ERR2 CHM FR ISTK CHM TO ISTK SCB RD ERR  MCHK AV  KSP AV  PSL EXC5  PSL EXC6  PSL EXC7  PSL REI5  PSL REI6  PSL REI7 @ NOSUCHDEVA DEVASSIGNB NOSUCHFILEC FILESTRUCTD BADCHKSUME BADFILEHDRF BADIRECTORYG FILNOTCNTGH ENDOFFILEI BADFILENAMEJ BUFFEROVFK CTRLERRL DEVINACTM DEVOFFLINEN MEMERRO SCBINTP SCB2NDINTQ NOROMR NOSUCHNODES INSFMAPREGT RETRYU IVDEVNAM CORRPTN! ILL REF" ILL CMD6 AMBG CMD7 INSUF ARG5 ARG OVERF# INV DGT4 QUAL OVERF1 QUAL NOVAL2 AMBG QUAL3 QUAL REQ VAL$ LTL% ILL ADR& VAL TOO LRG' SW CONF( UNK SW) UNK SYM* CHKSM+ HLTED, FND ERR- TMOUT. MEM ERR/ UNXINT0 UNIMPLEMENTED™ >>>Œ ^C € passive release  Machine check ‚ KSP invalid ƒ Power fail „ Reserved/privileged instruction … Customer reserved instruction † Reserved operand ‡ Reserved addressing mode ˆ Access violation ‰ Translation not valid Š Trace pending ‹ Breakpoint Œ unused  Arithmetic Ž unused  unused  CHMK ‘ CHME ’ CHMS “ CHMU ” unused • Corrected read data – unused — unused ˜ Memory error ™ unused š unused › unused œ unused  unused ž unused Ÿ unused   unused ¡ Software level 1 ¢ Software level 2 £ Software level 3 ¤ Software level 4 ¥ Software level 5 ¦ Software level 6 § Software level 7 ¨ Software level 8 © Software level 9 ª Software level A « Software level B ¬ Software level C ­ Software level D ® Software level E ¯ Software level F ° Interval timer ± unused ² Emulation start ³ Emulation continue ´ unused µ unused ¶ unused · unused ¸ unused ¹ unused º unused » unused ¼ unused ½ unused ¾ Console receive ¿ Console transmit ’ 1) Dansk 2) Deutsch (Deutschland/Österreich) 3) Deutsch (Schweiz) 4) English (United Kingdom) 5) English (United States/Canada) 6) Español 7) Français (Canada) 8) Français (France/Belgique) 9) Français (Suisse) 10) Italiano 11) Nederlands 12) Norsk 13) Português 14) Suomi 15) Svenska (1..15): ƒLoading system software. „Failure. …Restarting system software. †Performing normal system tests. ˆTests completed. ‰Normal operation not possible. šBootfile: Memory configuration error. žNo default boot device has been specified. ŸAvailable devices.  Device? ¡Retrying network bootstrap.ƒLogiciel-système en cours de chargement. „Panne. …Redémarrage du logiciel-système. †Système en cours de test. ˆTests terminés. ‰Exploitation du système impossible. šFichier de chargement: Erreur configuration mémoire. žUnité de démarrage par défaut inconnue. ŸUnités disponibles:  Unité? ¡Essai démarrage sur réseau.ƒCargando sistema operativo. „Fallo. …Rearrancando el sistema. †Realizando test del sistema. ˆTest completado. ‰Operacion normal imposible. š 'Fichero de boot: Error de configuración de memoria. žNo se ha especificado ningún dispositivo de arranque por omisión. ŸDispositivos disponibles:  ¿Qué dispositivo? ¡Reintentando el arranque de la red.ƒBetriebssystem wird geladen. „Fehler. …Betriebssystem wird neu gestartet. †Systemtest läuft. ˆTests abgeschlossen. ‰Normaler Betrieb nicht möglich. šLadedatei:: Fehler bei der Speicherkonfiguration. žStandard-Urladegerät wurde nicht angegeben. ŸVerfügbare Geräte:  Gerät? ¡Ladevorgang über Netzwerk läuft.ƒCaricamento software del sistema. „Errore. …Riavviamento software di sistema. †Esecuzione test del sistema. ˆTest diagnostico completato. ‰Normali operazioni non eseguibili. šProgramma di avviamento: Errore di configurazione della memoria. žUnità standard di avviamento non indicata. ŸUnità disponibili:  Unità? ¡Nuovo tentativo avvio sistema tramite rete.ƒIndlæser systemprogrammel. „Fejl. …Genstarter systemprogrammel. †Foretager normal systemtest. ˆSystemtest afsluttet. ‰Normal kørsel ikke mulig. šSysteminitieringsfil: Hukommelseskonfigurationsfejl. žDer er ikke specificeret nogen enhed til at starte fra. ŸEnheder der kan benyttes:  Hvilken enhed skal benyttes? ¡Prøver igen at starte over netværket.ƒSysteem wordt opgestart. „Fout. …Systeem wordt opnieuw opgestart. †Systeemtests worden uitgevoerd. ˆTests voltooid. ‰Normale werking niet mogelijk. šOpstartbestand: Fout in geheugenconfiguratie. žGeen standaard opstart-apparaat gespecificeerd. ŸBeschikbare apparaten:  Apparaat? ¡Netwerk-opstartprocedure wordt opnieuw gestart.ƒ Laddar systemprogram. „ Fel. … Omstart av systemprogram. † Normal systemkontroll pågår. ˆ Systemkontroll klar ‰ Systemet kan ej användas. š Startfil: Fel i minneskonfigurationen. ž Ingen standardenhet för inmatningsprogrammet har specificerats. Ÿ Tillgängliga enheter:   Enhet? ¡ Inmatningsprogrammet körs igen.ƒ Järjestelmän lataus. „ Vika. … Uusi käynnistys. † Järjestelmän testaus. ˆ Testit suoritettu. ‰ Normaali käyttö ei ole mahdollinen. š Käynnistystiedosto: Muistin määritysvirhe. ž Käynnistyslaitetta ei ole määritetty. Ÿ Käytettävissä olevat laitteet:   Laite? ¡ Verkon käynnistystä yritetään uudelleen.ƒSystemprogram leses inn. „Feil. …Systemprogram startes på nytt. †Standard systemtester utføres. ˆTester utført. ‰Normal drift umulig. šOppstartingsfil: Feil i hukommelseskonfigurasjonen. žStandard oppstartenhet er ikke spesifisert. ŸTilgjengelige enheter:  Enhet? ¡Oppstartingsprogrammet kjøres nå over nettverket.ƒ Carregamento do sistema. „ Falha do sistema. … Reincialização de sistema. † Execução de testes gerais do sistema. ˆ Fim dos testes. ‰ A corrente operação não e possivel. š Ficheiro de arranque: Erro de configuração de memória. ž Dispositivo de arranque não especificado. Ÿ Dispositivos disponíveis:   Dispositivo? ¡ Nova tentativa de arranque a partir da rede. %s-%c %c%d.%d-%d, VMB %d.%d KA655XA650KA655=={¦  þÿÿþÿÿÿÿ~~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(1CCL&/8 G %Rl I %Rl V %Rl P %Rl M %Rl ")0%Rb %Rw %Rl %Rq %Ro  "€8Enter device configuration, HELP, or EXIT Address/Vector Assignments Device is ambiguous Device is unknown Number is invalid Devices: %-12.*s Numbers: 1 to 255, default is 1 -%06o/%03oDevice CSR/vector cannot float (%d not configured) %c%*s׆ Device,Number? {¡ÿÿ1¢ÿÿf£ÿÿ¥ÿÿð¥ÿÿ‹ˆÏÌïîïÿïï€HALT`NOP`REI`BPT`RET`RSBÀLDPCTXÀSVPCTXA°CVTPSA° CVTSP’$¹ INDEXPp CRCÌ PROBERÌ PROBEWÈINSQUEÈREMQUE„BSBBdBRB„BNEQ„BEQL„BGTR„BLEQ$eJSB$eJMP„BGEQ„BLSS¤BGTRU¤BLEQUdBVCdBVS¤BGEQU¤BLSSUA° ADDP4A¸!ADDP6A°"SUBP4A¸#SUBP6´$CVTPTA˜%MULP´&CVTTPA˜'DIVP¬(MOVC3¬)CMPC3°*SCANC°+SPANC´,MOVC5´-CMPC5¸.MOVTCØ/MOVTUCN’„0BSBWN’d1BRWQ’¨2CVTWLA’¨3CVTWBŒ4MOVP¬5CMPP3¬6CVTPLA°7CMPP4Ð8EDITPCAÐ9MATCHCŒ:LOCCŒ;SKPCQ’È<MOVZWLIœ=ACBWI’¨>MOVAWI’Ä?PUSHAW’$©@ADDF2’$­AADDF3’$©BSUBF2’$­CSUBF3’$©DMULF2’$­EMULF3’$©FDIVF2’$­GDIVF3‚$©HCVTFBŠ$©ICVTFW’$©JCVTFLÈKCVTRFL¨LCVTBFQ’¨MCVTWF’$©NCVTLF’,‘OACBF’$‰PMOVF’$‰QCMPF’$©RMNEGF’$…STSTF‚$µTEMODF $­UPOLYFš$©VCVTFDI’¨XADAWIÈ\INSQHIÈ]INSQTIÈ^REMQHIÈ_REMQTIÛ¶©`ADDD2Û¶­aADDD3Û¶©bSUBD2Û¶­cSUBD3Û¶©dMULD2Û¶­eMULD3Û¶©fDIVD2Û¶­gDIVD3ö©hCVTDB˶©iCVTDWÓ¶©jCVTDLÓ¶ÉkCVTRDL¨lCVTBDY’¨mCVTWDš$©nCVTLDÛ¼‘oACBDÛ¶‰pMOVDÛ¶‰qCMPDÛ¶©rMNEGDÛ¶…sTSTDÃ4´tEMODD ¬uPOLYD¨vCVTDFŒxASHLضyASHQ’zEMULš{EDIVÛ¶…|CLRQÛ¶‰}MOVQÛ¶©~MOVAQÛ¶ÅPUSHAQ¨€ADDB2¬ADDB3¨‚SUBB2¬ƒSUBB3¨„MULB2¬…MULB3¨†DIVB2¬‡DIVB3¨ˆBISB2¬‰BISB3¨ŠBICB2¬‹BICB3¨ŒXORB2¬XORB3¨ŽMNEGB¬CASEBˆMOVBˆ‘CMPB¨’MCOMBˆ“BITB„”CLRB„•TSTB„–INCB„—DECB¨˜CVTBL¨™CVTBWÈšMOVZBLÈ›MOVZBW$œROTL ACBB¨žMOVABÄŸPUSHABI’¨ ADDW2I’¬¡ADDW3I’¨¢SUBW2I’¬£SUBW3I’¨¤MULW2I’¬¥MULW3I’¨¦DIVW2I’¬§DIVW3I’¨¨BISW2I’¬©BISW3I’¨ªBICW2I’¬«BICW3I’¨¬XORW2I’¬­XORW3I’¨®MNEGWI’¬¯CASEWI’ˆ°MOVWI’ˆ±CMPWI’¨²MCOMWI’ˆ³BITWI’„´CLRWI’„µTSTWI’„¶INCWI’„·DECWĸBISPSWĹBICPSWI’„ºPOPRI’¤»PUSHR€¼CHMK€½CHME€¾CHMS€¿CHMU’$©ÀADDL2’$­ÁADDL3’$©ÂSUBL2’$­ÃSUBL3’$©ÄMULL2’$­ÅMULL3’$©ÆDIVL2’$­ÇDIVL3’$©ÈBISL2’$­ÉBISL3’$©ÊBICL2’$­ËBICL3’$©ÌXORL2’$­ÍXORL3’$©ÎMNEGL’$­ÏCASEL’$‰ÐMOVL’$‰ÑCMPL’$©ÒMCOML’$‰ÓBITL’$…ÔCLRL’$…ÕTSTL’$…ÖINCL’$…×DECL’$‰ØADWC’$‰ÙSBWC’$‰ÚMTPR’$‰ÛMFPR’$ÅÜMOVPSL’$¥ÝPUSHL’$©ÞMOVAL’$ÅßPUSHALB%màBBSB%máBBCB%âBBSSB%ãBBCSB%äBBSCB%åBBCCB%­æBBSSIB%­çBBCCIª$‰èBLBSª$‰éBLBC$qêFFS$qëFFC$‘ìCMPV$±íCMPZV$‘îEXTV$±ïEXTZV ‘ðINSV’,‘ñACBLB%ÍòAOBLSSB%ÍóAOBLEQª$ÉôSOBGEQª$ÉõSOBGTR‚$©öCVTLBŠ$©÷CVTLWøASHP $­ùCVTLP¨úCALLG¨ûCALLS`üXFC#¨2ýCVTDH¨3ýCVTGFÛ¶©@ýADDG2Û¶­AýADDG3Û¶©BýSUBG2Û¶­CýSUBG3Û¶©DýMULG2Û¶­EýMULG3Û¶©FýDIVG2Û¶­GýDIVG3ö©HýCVTGB˶©IýCVTGWÓ¶©JýCVTGLÓ¶ÉKýCVTRGLض©LýCVTBGY’¨MýCVTWGš$©NýCVTLGÛ¼‘OýACBGÛ¶‰PýMOVGÛ¶‰QýCMPGÛ¶©RýMNEGGÛ¶…SýTSTGË´µTýEMODG ¶­UýPOLYGã¶©VýCVTGH$Iª`ýADDH2$I®aýADDH3$IªbýSUBH2$I®cýSUBH3$IªdýMULH2$I®eýMULH3$IªfýDIVH2$I®gýDIVH3IªhýCVTHB IªiýCVTHWIªjýCVTHLIÊkýCVTRHL IªlýCVTBH!¨mýCVTWH"IªnýCVTLH$M’oýACBH$IŠpýMOVH$IŠqýCMPH$IªrýMNEGH$I†sýTSTH E¶týEMODH H®uýPOLYHIªvýCVTHG$I†|ýCLRO$IŠ}ýMOVO$Iª~ýMOVAH$IÊýPUSHAH"¨˜ýCVTFH¨™ýCVTFGIªöýCVTHFIª÷ýCVTHD’$ýÿBUGLI’€þÿBUGW%-8.*s%04X %02X ??? , .address %08X #%08X#%04X @€ @€@CFILORUX[^bfiloR0R1R2R3R4R5R6R7R8R9R10R11APFPSPPC?œ@O0œP_wœ`o›œp¿œ€Žãœž4 ®p°¾pÀΧÐÞ§àîÞðþÞŸŸX¯¯ž¿¿žÏÏ=žßß=žïïežÿÿež%08XS^#%02X[%s]%s(%s)-(%s)(%s)+")0I^#%rbI^#%rwI^#%rlI^#%rqI^#%ro@(%s)+@#%08XB^%02X(%s)@B^%02X(%s)W^%04X(%s)@W^%04X(%s)L^%08X(%s)@L^%08X(%s)B^%08X@B^%08XW^%08X@W^%08XL^%08X@L^%08X    \%c\^%c%c^%c\’ž’ž £ž ¿ž ¿žÛž Ÿ!~-Ÿ¡þ-ŸÿÿKŸÿÿÿÿÿÿ%m^O c€b€x€o€u€d€s€m€br €wr €lr €qr €or €fr€dr€gr€hr€0123456789ABCDEF0123456789abcdef›› Žÿ /[[@_ 0~ ÿ›› / 0~ ÿ››0? /@~ ÿ››0? /@~ ÿ›› /@~ ÿ !~ ÿÿÿÿÿÒàà?„€à¿ÿ'  '.5<CJQ 300 600 1200 2400 4800 9600 19200 38400 ‰‰%rb.. ?%rb %1x %rb %rb %rb %rw P1=%08X P2=%08X P3=%08X P4=%08X P5=%08X P6=%08X P7=%08X P8=%08X P9=%08X P10=%08X r0=%08X r1=%08X r2=%08X r3=%08X r4=%08X r5=%08X r6=%08X r7=%08X r8=%08X ERF=%08X ­˜ [XQA0]: (BOOT/R5:%X %m)%032.*b %08X %s English (United States/Canada)Français (Canada)DanskEnglish (United Kingdom)SuomiDeutsch (Deutschland/Österreich)NederlandsItalianoFrançais (Suisse)Deutsch (Schweiz)SvenskaNorskFrançais (France/Belgique)EspañolPortuguêsUnknownòÓÁ»¢œ{pgUC;5%s …ÿÿlÀÿÿÁ…ÿÿúû X…ÿÿúÿÁ†ÿÿêû,ºÿÿÀ  ¼ÿÿ 9‡ÿÿ¶ˆÿÿضÿÿψÿÿ4~ÿÿêˆÿÿõˆÿÿêû ”‰ÿÿÿÿÿ #Šÿÿœ€ÿÿâ‰ÿÿ 4‹ÿÿθÿÿhbfθÿÿhbflθÿÿhbflgθÿÿhbflagæ¸ÿÿqboot<¹ÿÿzhostFºÿÿƒlanguage    "ºÿÿcontrol=ºÿÿbf=ºÿÿbfl=ºÿÿbflg=ºÿÿbflagTºÿÿbootoºÿÿdevices“ºÿÿethernetºÿÿlanguage̺ÿÿmemoryèºÿÿqbusòºÿÿrlv12üºÿÿuqssp»ÿÿversionhýÿÿhalthýÿÿh{ýÿÿhelpŽýÿÿdeposit¡ýÿÿexamine´ýÿÿmoveÇýÿÿsetÚýÿÿshowíýÿÿinitþÿÿunjamþÿÿboot&þÿÿstart&þÿÿs9þÿÿnextLþÿÿcontinueLþÿÿc_þÿÿsearchrþÿÿrepeat…þÿÿx˜þÿÿfind«þÿÿtest¾þÿÿ configure€r0€r1€r2€r3€r4€r5€r6€r7€r8 €r9 €r10 €r11 €r12 €r13€r14€r15 €ap €fp€sp€pc€pr$_ksp€pr$_esp€pr$_ssp€pr$_usp€pr$_isp€pr$_p0br €pr$_p0lr €pr$_p1br €pr$_p1lr €pr$_sbr €pr$_slr€pr$_pcbb€pr$_scbb€pr$_ipl€ pr$_astlv€pr$_sirr€pr$_sisr€pr$_iccr€pr$_nicr€pr$_icr€pr$_todr €pr$_rxcs!€pr$_rxdb"€pr$_txcs#€pr$_txdb$€pr$_tbdr%€pr$_cadr&€ pr$_mcesr'€pr$_mser*€ pr$_savpc+€ pr$_savpsl7€ pr$_ioreset8€ pr$_mapen9€pr$_tbia:€pr$_tbis>€pr$_sid?€ pr$_tbchk @rom@ @bdr@ €@cacr @ssc_ram €@ssc_cr  €@ssc_cdal0 €@ ssc_dledr0 €@ ssc_ad0mat4 €@ ssc_ad0msk@ €@ ssc_ad1matD €@ ssc_ad1msk €@ssc_tcr0 €@ssc_tir0 €@ ssc_tnir0  €@ ssc_tivr0 €@ssc_tcr1 €@ssc_tir1 €@ ssc_tnir1 €@ ssc_tivr1 €@memcsr0 €@memcsr1 €@memcsr2  €@memcsr3 €@memcsr4 €@memcsr5 €@memcsr6 €@memcsr7  €@memcsr8$ €@memcsr9( €@memcsr10, €@memcsr110 €@memcsr124 €@memcsr138 €@memcsr14< €@memcsr15@ €@memcsr16D €@memcsr17@ @@ipcr0B @@ipcr1D @@ipcr2F @@ipcr3 €@dscr €@dser €@dmear  €@dsear €@qbmbr0€@qbmem €@qbio€€pslh»˜À›€›š ˜ ð à À€  ˜ € € €+-*@Following is a brief summary of all the commands supported by the console: UPPERCASE denotes a keyword that you must type in | denotes an OR condition [] denotes optional parameters <> denotes a field that must be filled in with a syntactically correct value Valid qualifiers: /B /W /L /Q /INSTRUCTION /G /I /V /P /M /STEP: /N: /NOT /WRONG /U Valid commands: DEPOSIT []

[ []] EXAMINE [] [
] MOVE []
SEARCH []
[] SET BFLG SET BOOT [:] SET HOST/DUP/UQSSP [] SET HOST/DUP/UQSSP [] SET HOST/MAINTENANCE/UQSSP/SERVICE SET HOST/MAINTENANCE/UQSSP SET LANGUAGE SHOW BFLG SHOW BOOT SHOW DEVICE SHOW ETHERNET SHOW LANGUAGE SHOW MEMORY [/FULL] SHOW QBUS SHOW RLV12 SHOW UQSSP SHOW VERSION HALT INITIALIZE UNJAM CONTINUE START
REPEAT X
FIND [/MEMORY | /RPB] TEST [ []] BOOT [/R5: | /] [[:]] NEXT [count] CONFIGURE HELP Ethernet Adapter 0 (774440) -XQA Ethernet Adapter 1 (774460) -XQB%s0 (%02X-%02X-%02X-%02X-%02X-%02X) Memory %d: %08X to %08X, %dMB, %d bad pages -- FDM (fast diagnostic mode) not working Total of %dMB, %d bad pages, %d reserved pages -%08X to %08X, %d pages Memory Bitmap Console Scratch Area Qbus Map Scan of Bad Pages Scan of Qbus I/O Space Scan of Qbus Memory Space -%08X (%06o) = %04X-%08X to %08X (%08o to %08o) (%03o)nr5stepwrongbwlq instruction g i vpmrpbumemnotfulluqsspdisktapeservicedup maintenanceЬ ram> %08X€L å  æ ç ° ° ° ° ° ° ° ° !° %° )° -° 1° 5° 9° =° @° D° H° L° Q° U° Y° ]° a° e° i° m° q° u° y° }° …° ‰° ° ‘° •° ™° ° ¡° ¥° ©° ­° ±° µ° ¹° ½° Á° Ű ɰ Ͱ Ѱ Õ° Ù° ݰ á° å° é° í° ñ° õ° ù° ý° ± ± ± ± ± ± ± !± %± )± -± 1± 5± 9± =± A± E± I± M± Q± U± Y± ]± a± e± i± m± q± u± y± }± ± …± ™± ± ¡± ¥± ©± ­± ±± µ± ¹± ½± Á± ű ɱ ͱ ѱ Õ± Ù± ݱ á± å± é± í± ñ± õ± ù± ý± ² ² ² ² ² ² ÝzÝvÝrÝnÝjÝfÝbÝ^ÝZÝ VÝ RÝ NÝ JÝ FÝBÝ>Ý:Ý6Ý2Ý.Ý*Ý&Ý"ÝÝÝÝÝÝ ÝÝÝïèÿÿÝ zÝ!vÝ"rÝ#nÝ$jÝ%fÝ&bÝ'^Ý(ZÝ)VÝ*RÝ+NÝ,JÝ-FÝ.BÝ/>Ý0:Ý16Ý22Ý3.Ý4*Ý5&Ý6"Ý7Ý8Ý9Ý:Ý;Ý< Ý=Ý>Ý?ïdÿÿÝ|ÝxÝtÝpÝlÝhÝdÝ`Ý\Ý XÝ TÝ PÝ LÝ HÝDÝ@Ý<Ý8Ý4Ý0Ý,Ý(Ý$Ý ÝÝÝÝÝ ÝÝÝÈ@nï×ÿÿÝ zÝ!vÝ"rÝ#nÝ$jÝ%fÝ&bÝ'^Ý(ZÝ)VÝ*RÝ+NÝ,JÝ-FÝ.BÝ/>Ý0:Ý16Ý22Ý3.Ý4*Ý5&Ý6"Ý7Ý8Ý9Ý:Ý;Ý< Ý=Ý>Ý?È@nïIÿÿ@BDFL €x|¸¼ @`@HPX`hpx€ˆ˜ ¨°¸pPTX\”0x´THP 0P`Phl@°@?€€x`  p`PIPCR€KFQSA_0KFQSA #0€KFQSA_1KFQSA #1€KFQSA_2KFQSA #2€KFQSA_3KFQSA #3LPV11KXJ11DLV11JDZQ11DZV11DFA01RLV12TSV05RXV21DRV11WDRV11BDPV11!DMV11#DELQA#DEQNA#DESQA#€LQA#€QNA#€SQA$RQDX3$KDA50$RRD50$RQC25$ KFQSA-DISK$€RC25(TQK50(TQK70(TU81E(RV20( KFQSA-TAPE(€TK50(€TK70)KMV11*IEQ11+DHQ11+DHV11+CXA16+CXB16+CXY08.VCB01.QVSS0LNV111LNV211QPSS3DSV115ADV11C6AAV11C7AXV11C8KWV11C9ADV11D:AAV11D;VCB02;QDSS<DRV11J=DRQ3B?VSV21@IBQ01AIDV11ABIDV11BCIDV11CDIDV11DEIAV11AFIAV11BGMIRAIADQ32JDTC04KDESNAK€SNALIGQ11O€EXITP€HELPùùÿÿúÿÿúÿÿúÿÿ'úÿÿ/úÿÿIúÿÿWúÿÿ}úÿÿƒúÿÿ‰úÿÿúÿÿ•úÿÿ›úÿÿ¡úÿÿ§úÿÿ­úÿÿ³úÿÿ¹úÿÿ¿úÿÿÅúÿÿËúÿÿÕúÿÿåúÿÿíúÿÿóúÿÿùúÿÿûÿÿ ûÿÿûÿÿûÿÿ!ûÿÿ'ûÿÿ-ûÿÿ7ûÿÿGûÿÿQûÿÿWûÿÿ]ûÿÿcûÿÿmûÿÿsûÿÿ{ûÿÿûÿÿ‡ûÿÿûÿÿ•ûÿÿ›ûÿÿ£ûÿÿ©ûÿÿ±ûÿÿ·ûÿÿ½ûÿÿÃûÿÿÉûÿÿÓûÿÿÛûÿÿãûÿÿëûÿÿüÿÿ üÿÿüÿÿüÿÿüÿÿ%üÿÿ+üÿÿ1üÿÿ7üÿÿ=üÿÿCüÿÿIüÿÿOüÿÿUüÿÿ[üÿÿaüÿÿgüÿÿmüÿÿÆÆ’ÇÇT4433Ö22‘‘BÒ€€T``{$bbÊ'ccJ)QQ°*RRœ,SSb/UU 1ZZ‚1EE’2FFz5žž¤;Ê;‚‚Œ=ÁÁg?ÂÂ.AÅÅšBTT‹C66 F55~KDD–SCCïSAAWBB Y11NY00q_OO¬cNNjeMMfLLhKKÒlJJ¬nII¿pHHŽvGGå|@@p~¶œœº‚ŸŸvˆÀ À À %À -À 5À =À EÀ MÀ YÀ eÀ Ñ uÀ }À …À À ”À œÀ ¤À ¬À ½À ÅÀ ÍÀ ÕÀ ÝÀ åÀ íÀ õÀ ýÀ Á Á Á Á %Á -Á 5Á =Á EÁ MÁ UÁ ]Á eÁ mÁ uÁ }Á …Á Á •Á Á ¥Á ­Á µÁ ½Á ÅÁ ÍÁ ÕÁ ÝÁ åÁ íÁ õÁ ýÁ    À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À À Ô~®Ÿ³ Þ®~À®nš~1š~1øš ~1ðš~1èš~1àš~1Øš~1ÐÞ®~š ~1ÌÞ®~š$~1Àš(~1°š,~1¨š0~1 š4~1.š8~1š<~1ˆš@~1šD~1 šH~1šL~Ðn~Þ® ®1gšP~1WšT~1OšX~1Gš\~1?š`~17šd~1/šh~1'šl~1šp~1št~1šx~1š|~1ÿš€~1÷š„~1~1皌~1ßš~1ך”~1Ïš˜~1Çšœ~1¿š ~1·š¤~1¯š¨~1§š¬~1Ÿš°~1—š´~1š¸~1‡š¼~1šÀ~1wšÄ~1ošÈ~1gšÌ~1_šÐ~1WšÔ~1OšØ~1GšÜ~1?šà~17šä~1/šè~1'šì~1šð~1šô~1šø~1šü~Ðn~Þ®®ÐX YÕ© ¹ ©yï©wÝPÐPÐ^©(Ð@n@©(ò PöÐŽPЮ^ÞïnÚ©PلЩ^ºà»àÐX YÞïÖ©TÞïJ~ÿÿɄ۩PÐ^©ÞïeùÿÿQÚQ•©u©t©u©u©tš©uS ÑSÑS0^ /‘S0ê1Ï‘þS0"1éyÔPƒ SP' ”©ЩPÑPP©Ð@ï6P1•0q1ú©wÞïCöÿÿT•d9&%nNo such diagnostic, T 9E for list%nŸ¯×ÝïB 1I1FÖT‘S„Õ„¸ßïôõÿÿÁdŽTÐT©2¤UžEdÉ€ïSUðU4iS© 0{Êi0≩v©wPÚ©Pï0i~ÐŽŸ0 Úºà‘ÿ©w<ÚÊiЩTÈ@iÞïÍ© Ã^©|2¤U”©w”©vEdÚÔ© Ê@i %n bitmap=%lÝ©Ÿ¯íÝï}  , length=%wÝ© Ÿ¯îÝïa , checksum=%wÝ©"Ÿ¯ìÝïC  %n busmap=%lÝ©$Ÿ¯íÝï& %n return_stack=%lÝ©|Ÿ¯çÝï %n subtest_pc=%lÝÉ€Ÿ¯èÝïá %n timeout=%lÝ©\Ÿ¯ìÝïà  , error=%bÝ©vŸ¯ïÝï¨ , de_error=%bÝ©wŸ¯ìÝïŠ %n de_error_vector=%bÝ©xŸ¯äÝïd , severity_code=%bÝ©yŸ¯çÝïA , total_error_count=%wÝ©zŸ¯ãÝï »ÐP%n previous_error=%lÝ@©dŸ¯äÝïï ÕP(Ñ ÃP~ÑŽ×P, %lÝ@©dŸ¯ôÝïÅ Ôº%n last_exception_pc=%lÝÉ Ÿ¯áÝï˜  %n flags=%lÝiŸ¯ïÝï} , test_flags=%wÝ©Ÿ¯êÝï] %n highest_severity=%bÝ© Ÿ¯ãÝï6 »ï4iP%n led_display=%bÝPŸ¯éÝï º%n console_display=%bÝ© Ÿ¯äÝïæ%n save_mchk_code=%bÝ©[Ÿ¯åÝïÁ, save_err_flags=%bÝ©ZŸ¯æÝï%wÝ©XŸ¯÷ÝïŠ%n parameter_1=%lÝ©(Ÿ¯èÝïh 2=%lÝ©,Ÿ¯óÝïQ 3=%lÝ©0Ÿ¯óÝï: 4=%lÝ©4Ÿ¯óÝï# 5=%lÝ©8Ÿ¯óÝï %n parameter_6=%lÝ©<Ÿ¯èÝïê 7=%lÝ©@Ÿ¯óÝïÓ 8=%lÝ©DŸ¯óÝï¼ 9=%lÝ©HŸ¯óÝï¥ 10=%lÝ©LŸ¯óÝš© X2¤UÀTUš…WÞ©(PW© 3•…ÕX$Ô` š¥ÿQÀUÕX‘QÐe`Ð¥`œQQÀQUÕ€×XõWͺÐX YÐRÉ„ÐQ©PÞï’óÿÿSÚSÐ^©0Ú©PÐX Yð©š`SÔQ‘SÀEƒ SQ>ЩP1Õÿ‘Q© ÐAP1ÄÿÁP~˜©~Q©ÐAïmŠP0­ÿöŽ©ÐŽPá©¡š RÞïvðÿÿT•dÞ P˜ÖT‘R„Õ„ëßïZðÿÿÁdŽTÐT©2¤UžEdÉ€R©tR©uð 4i © ï4i~ÍŽŸ0 © Q»Û~Ú©PŸŸ@ ÚŽº”©wá àdà )íiï}rLá àdà  íi0µ2“  á "á á ïŸ UxUUÀUÉ€û©wï UU©y2¤UÀTUš…WÞ VÞ©(SW© &•…Ôƒš¥ÿXÀUÑXÐ…ƒ ІƒœXXÀXUõWÚš WÐWX‘û©w»Û~Ú©PÙ„ÚŽº!Êià Èiá 0¸40505‰©v©wQá ð©‘W1Öÿ‘W1Îÿ‘WõXíÐVP1þ»ÿ”©vÿ©wÚÃ^©|Ê iÊ@iÊiÔÉ ŒŸ0 ŒŸ0 Þïì© Ù€ÚÔ© •©vNðŸ0 0iŒŸ0 ŒŸ0 àiA0q40b»Û~Ú©PÐÉ„~ЩXYžÐX YÚŽºá©ï+qŒŸ0 ŒŸ0 ЩTÈ@iÞïn© Ã^©|2¤UEdÚÔ© Ê@i‘ý©wài0ù3»Û~Ú©PÙ„ÚŽº‘ÿ©w”©w‰©v©wP¶©zÔPÐ@©h@©dòPõ‘©y© ©y© ºÿ»ÿÐX Yо,É Ô© àiZàiPЩT2¤URÀTUÐUTáiHš…R±…®(µ…õRõ172eRЩ\Ý®,ÈiÞ¯«© BdÊiÕŽºÿÞ¾^üV ýVþVV©w®(©x‘`©uР©\ïgÚ"Ú Þï¾,ºÿÞ¾^Û~ðŽ®Щ|^%nTestŸ¯öÝï+%n# Address Name ParametersŸ¯ÒÝïä%nŸ¯úÝïÔÐKP_Ÿ¯ûÝï¾õPîÛP %n %lÝPŸ¯òÝï  De_SCBŸ¯ôÝïŠÐ SÞïFìÿÿQ•aPP%n%b ÝPŸ¯ôÝï[ßïìÿÿÁŽT°T~±ŽSÐ SÐS~´nðTÈŽT°TS%l ÝTŸ¯öÝïÞ¤ P%a ÝPŸ¯öÝï2¤UÀTUÐUVš…W1xÿš…P**Ÿ¯ûÝïàÑW•e Ÿ¯ûÝïÈ1"2…XÀVX%a ÝXŸ¯÷Ýï¬xPPÖPÀPUõW1!ÿ1¦ÿ»Û~Ú©PÙ„ÚŽº  Þ¯îQ‘©uÀQõП@ PíPíPšaPš¡PíŸ@ š¡PÝ© Þï© µŸ ÐŽ© ÕPš¡PП ~ÊÿÿÿnÐŽŸ ÕŽÀ¾ÔPÐn^ïŸ@ Pð©\í iП ЀŸ ÑŸ ©\öÞ¯Ó®üí®üŸîÎ ŸîΠ©\úÈ iÊ iÔVÔWÐRÐSÐRTÊVTÊWTÐTaÐaUÊWUÑUTÐSÒRRõSÝxRRÔÔaÔS»?п…Tx÷TPÖPЩQ0ÎRÖRÔSáRaøÖRòPSöÂPRx R~(TÏ‹þ¾ÞÏ…þUÃUÉ€UÁŽUÉ€º?» š…P0löº ÝXÁ^X»ÿШRÞ¨ Tš‚P‘%Pš‚P!ï0AêШTÞD¨TÐhdÐThºÿÐŽXÐn^Paš;.h.......E..........@š P0ìš P0æšVšVš VЄUÂVïVUP‘ PÀ0PÀ7P0ºãÝXЄUÔVÐÊš;WÔX{WUPUèXÑW À0P0“âXÆ WâÐŽXЄU0 ÿÝXÁ^X»ÿШRÞ¨ TЄWš‚P‘%Pš‚P!ï0VêШTÞD¨TÐhdÐThºÿÐŽXÐn^Pa­;.†.......G..........Aš P0š P0ûQ” °Q”ÐQ”» 0rÐWRš‚SÔQÕSº ÄQ×Sš‚P‘9PÂ0PÀPQäÂ7P‘P PÀPQÔ» 09ÐWRš‚SÔQÕSÐQ”º Ä Q×Sš‚PÂ0PÀPQæ0šgV»<(V§´º<”F´Õ„» ÐWR•‚0P‘PÐPS‘S7‘S 20Q‘S *‘S %S‚ØÃWRPÑPÏ•ršP01š P0+šP0%1¸ÿÃWRPƒPgº »ÔPÛ~Ú©PŸÎ5 ÚŽº»Û~Ú©P‘ P”©‘ P P“© ŸG6 Ÿ5 –©æ–©ŸG6 Ÿ5 ÚŽº P´ P¯»Û'PðP©XïPPðP©XП@ QïQPðP©XïQPðP ©XïQPðP ©XïŸ  PðP©XП QïQPðP©XïQPðP©XðQ©XÐ@ Qá Ÿ ïŸ PxPPÈPQ°aPïPPðP©Xº€,ÁSSC_powerup !"€ ãi1‰Þ¯ýÉ€Þ¯äV©vš†Qš†RÛQPÑQ!”PÑRPhã©vÐ ©(ÐRй(©, NÀ©(õRðÞ©0Qв RТТТТ ТТÒ¢Pa©vÐqPïPP©vÐTÕqõTù”©v€!·CBTCR timeout’Þ¯ýÉ€©vÞïB©0Ô©(Ð  PÐ QÈÀ`Ð`©,©vï©,RR©vï©,RF©vðiÐaRði©vÑ©(+©vÐ`©,í©,©vÈÀ`Ð`©,ï©,R”©vÖ©(Щ0¾€4ÆROM logic testUUUUªªªªÿÿÿÿÞ¯ýÉ€©vÞ¯ÛRЂSЂTÁSTUÖUu©vÒSVÑVTi©vЂQ`©vÒQPЂQÑQPQ©vÐPš‚Q‘PQBóPô©vÂR ©vΨÈhÑþÿÿÿ¨) ©vÈ hÑÿÿÿÿ¨ ©vÕh©vÈ hÑ€h1Ë©vΨÐhÐ háhë1¬Ú Ô©0Щ8¨ Îô¨Ðô©\ïçÿÿÐÑ€hï´æÿÿÑ©01°ÿ©vÔ©0Ðô©\Ðhhï“æÿÿÑ©0`©vÑÁhS©vШSIÑ þÿÿS@©vÔ©0Щ8¨ Ω,¨Щ,©\ÐÕ€hïJæÿÿÑ©0©vÑÄh ð©4i”©vœ©(QÁQ XÔ¨ЀhÐxŸ  Ð|Ÿ Õ©( Ö©0Õ©(Ö©0Ð?©0XŸTOY clock $ÿÿÿÿ9èrepeat_test_250ms_eaTolerance©vðiÔ©0Ô©8©vÛPП ©4íiðiá©4 ©vÚði©vÛPÚ©vÛPÁPTР†SÛQÑQTõSõA©vÐ QÔ¡Ð@ SÐ@BVЀWÛRÀRÁ RTÐWaÛPÑPR õSõ©v1œÐ¡XÛPÑPT õVõ©væÃX¡XЀaÀXР†WÑXWÃWXVÃXWVï©0PÑVPÐV©0ð©0ÑXWð©0é,WVÁ©,WWÑXV ©v1*ÑXW ©v1Ö©8Ñ©8©(1ÿá©4Úði”©vЀŸ !lInterval timer ÀV©vðiÔ©(Ð N©\ïOäÿÿÚÚ@ïñãÿÿÕ©(©vÚÕ©( ði”©vÚÚÚÖ©(€ \VAX CMCTL CDAL 5ÿÿÿÿ'dont_report_memory_badrepeat_countÞÿÿÿÿªªªªUUUU©vÔQÐ@ WÊÿ§ÔVÚ%Úà%©vЩTS©védHÞ¯¶SšƒU©vЃRÒgðiÐRaÐaPðiÑPR#á©©vàgõUÒò©,V”©v©vè©(òÐgXÛ'©0Ú'Ú%Ú%€m¶cache_mem_cqbic5@Istart_addrend_addraddr_incr10Ê©,Ê©0Щ0Ô©@Ô©<©vÚ%Ú%Ú'©vÕ©$1©vÕ©1ù©vЀ UàeÀUÑU ðà Ÿ 1Ó”©vÐe©@Щ(Qï°ÑQSï QRàR¹ÀQÑQ©,äÕ©<1©v1”Ö©<ðReï QSï UTðT SÀ0SËQX©v|h|¨©vÐ!C!CcÐcVÑV!C!C-©vÐaWÑW!C!C ©vÚÐ%Ú'ÐaWÛ'PÑW!C!C1" ©v“`Pó ©v•Pë ©vÚÐ%Ú'ÐaRÐcVÛ'PÑVR1ñ ©v“`Pó©v•PëÐaRÛ'P©v•PÝ©vÑVRÔÚÐ%Ú'|h|¨ÕhÕ¨ÐaWÛ'P©v•P%©vÕWÐh$h$cÐcVÐaTÛ'P©vÑTh$h$1~©v‘P€óÐQXàXãXäXÐh$h$cÐcVÐhWÛ'P©v‘P€=©vÕW5ÐQXàXãXäXÐhWÛ'P©v•P©vÕWÀ©0QÑQ©,1nþ1Gþ1 Щ@eÔ©@”©vÛ%©4Û'©8Ú'Ú%Õ©@Щ@eíiði‘©vðiÈÀŸ  Ú'Ú%€BÅCache1_diag_md addr_incrÞ¯ýÉ€©vП  ©DÐ Ÿ  Щ0Щ,0»ß©všüUÚU%Û%PÚ%Û%QÚ%Ú%©vÑPU©vÐQPÑP ©vÛ%PÑP 1þ1÷0V}RÐT©vÈXÚX%}QaÕaÐS¡Õ¡ÊXÚX%}aUÛ%WÚ%©vÛ'P1À ©vÐUPÑPRñ ©vÐVPÑPSåÒRRÒSSõT®yRR¥ ©v0ñÈXÚX%|aÕaÔ¡ÒÒÐTTqôTúÊXÚX%}aUÛ%WÚ%Û'P1_ ©vÐUPÑPí ©vÐVPÑPÝ©v0—³ÿQ1òЪªªªRÐTÈXÚX%}QaÕÐR³ÿQñÂQÒRSÐTÐaUÛ%WÛ'P-ÖTÑUR&ÖTСVÛ%WÛ'PÖTÑVRÖTÐSÐS³ÿQÇ1ÇÂQÐ÷RÐTÐaUÛ%WÛ'PáÖTÑUSeÖTСVÛ%WÛ'PÌÖTÑVSPÐRÐR³ÿQÉÂQÊXÚX%Ð TÐaUÛ%WÛ'PÖTÑUR!ÖTСVÛ%WÛ'PˆÖTÑVR ÀQ³ÿQÌ1<©v0”ÈXÚX%|aÕaÔ¡|¡Õ¡Ô¡ ©vÈXÚX%С ÊXÚX%Ô©4Ô©LÞï©Hði©vС RðiÛ'PÛ%W©vÑ©4š©víP©vÑW †ÚX%Ú'Þï©HÔ©4ði©vÕaði©vÛ'P“P1©vÑ©4u0æÈXÚX%|aÕaÔ¡ÈXÚX%ÐaÊXÚX%СÊXÚX%Ô©4Þï©HÚ'ði©vÐaRðiÛ'PÛ%W©vÑ©4©víP©vÑW Ú'1Þï ©H©v0b³ÿQ1ì<QÔTÐÿüRÈXÚX%ÐVÁTQSÈS}RcÕƒÔƒÀTÒQQÊRQõVâxQQáQ×ÊXÚX%<QÔTÐVÁTQSÈS©vÐcUÛ'P©vÑSUÀTÒQQÊRQõVÔxQQáQÉ1s©vÐQÞï©HÐTðiÔ©4ÕaÑ©4×ÀQ³ÿQëði ©vÐþÿQÞï©HÐTðiÔ©4Õ¡Ñ©4¡ÀQ³ÿQêðiÚ%‘©,–©,1ºûЩ,!©v0mÛ0JЩ0Q³ÿQ1ÇÈXÚX%ÐTÐV}PaÕÔ³ÿQòÐTÚ‘%ÐV³ÿQÞÐTšÐRÚR%ÐVÂQÐTÐaUÖTÛ%WÛ'P)ÖTÑQU"ÀQÐaUÖTÛ%WÛ'PÖTÕU ÀQ³ÿQÉ1_0ÓÚ"©vЩ0QÚR%Þï©Hði#©vÔ©4Õa$©vÛ%WÛ'PÑ©4+ÀQ³ÿQÝðiÔ©4Ú%À©(©0Ñ©01Áú”©vðiЩDŸ  Û%©<Û'©8Ú%Ú%Ú'ЩDŸ  ÈÀŸ  íiði‘©vðiÛ%®üš®ü©@Û'®ü®ü©A0·ÿÚ%Ú%ÐXð©,XÚ'Щ0Qо©LÖ©4ЩH¾%List diags ©vïA×ÿÿ”©v<¦MSCP-QBUS testš ÿ h IP_csr©vÛ©8Á©(©0š©<©vÚ´¹(Ðà©\ïÙÿÿÚ<¹0P©vЩ@ï PQ‘QB©vàP:©vá P2áP.©vÐÀQ°Q¹0<'RÐà©\ïÊØÿÿ<¹0P±PQôRæ1<©všQ°Aïå¹0Ð'RÐà©\ï•Øÿÿ±¹0AïÅôRä1ôQÎ ô©<11Dÿ©vš©<ïµØÿÿÚé8PÚP ©v´¹(Ðà©\ïCØÿÿ<¹0PàP9 ©vï PQ‘Q+ ©và P# ©v<‚€Q°Q¹0Ô©4Ð>I©\ïØÿÿÕ©4 ô©<11 ÿ”©vÚ©8<¹0©,Ú©8<¹0©,Õ©@´¹(Ú©8Õ©@´¹(š©4ªªUUððÿÿ€CÚ5DELQA ÿ device_num_addrÞޯýÉ€©vÔ©8Ð  ©,Õ©(+À©,Ñ©(!Ë©(©,Ñ©(  Ñ©( ©v1Fði©vЩ,Qµ©vµµµµµaðiÀ ©,Щ,Q°¡´¡©v<¡©4©vÑQP5©vÐqPÛ'S)©vÑQP ©vÀQÐaPÛ'S©vÑQPÂQõR´1©vÂQš€RÔõRûš€R©vÐaPÛ'S5©vÑQP,ÀQõRåÂQÚX%š€R©vÐaPÛ'S‘€SÀQõRê1µÐ©0Qš@RÈXÚX%ÊX©vÐaPÀQõRó©vÂQÔ©8ÞÏ©HÐaP©vÛ'SÛ%TÑ©8¸©vÑS¯Ú'©vÑT £ÞÏW©Hš@RÚX%©vÐaPÛ'S‘S€>ÀQõRêÚ%‘©4–©41øýÀ©(©0Щ0QÊQÑQ1Øý©vÕ©,”©vÛ%®üš®ü©DÛ'®ü®ü©EÚ%ÐXð©4XÚX%Ú'о©LЩH¾Õ©8ޯȾÖ©8íiði‘©vði0¨ÿÚ'Ú%Ú%» áQÖ©,%<© Sx SSÑQS,ï QRáR¹ÖRáR¹ Ö©,ÐQ©0ÔPÀQÑQÏÒPº €|Board ResetÞ¯ýÉ€á Ÿ ©vЀ QÔPÊ€@aò Pð©vР†©\ïÔ½ÿÿÚ Ú"Ð Ÿ Ð@ Ÿ0 П4 Ð@ Ÿ@ ПD ÐwPÔPÐPŸ ïŸ@ QðQ PÐPŸ ÉÀŸ  Ð@ PÒ€Ð`QÊÿßÿÿQíŸ@ < Q íŸ@ ÔQÐQ`Ú'š½Ÿ ÔQá Ÿ ïŸ@ QðQŸ ÚÐ PЀ€`РÐ`À Pšx€Ð€€`РÐ`š|  Ú%Ú%Ð Ÿ@ ©vÐP}€ÑPøÿôÐ Ÿ@ 0”©vÔ©0Ð@ PÒ©,Щ ©(Þï© ÚÂPõPúÚЩ(© Ú'Ö©0‘©,ÿn©,%‘©-ÿn©-‘©.ÿn©. ‘©/ÿn©/Þ¯¶¾Þ¾^€A)þCheck_for_intrsÞ¯ýÉ€©v0iÿÕ©0š©,P©vP©v”©v€$î{MEM_Setup_CSRs Þ¯ýÉ€©v0¯0¶Ô©(ÐSÈgõSöÔSÐ QÔR©vÐ aÐaP©vïPTÑTÖTÐU©vÔaÕT*íP#ЀXðSXÐXa‘Ÿ à&i1×TÖSÀQõUÆòR¥1v©v» ÐaPïPPÑPYËÿÿ?üaPÞïK©LðiÔ`Ð`RÒRSÐSàÐSà ÐSà0Ñ`S"Ó Q×SÊQÔÔÔÔðiº ðR©(1ÿðiº 1yÿ•©(>»Û©,Ú©PÝŒûïq$ÿÿÝŒûïd$ÿÿÝûïW$ÿÿÚ©,º©v1‡ÔRÐ QÐaPÓP ©víPkÀQÑQ@ áÔSÐ QÐaPïaTÑTÑTÓ QÓQ ©vàP. ©váP$ ©víPSÖSÀQÑQ@ µ ©vÕS1¡0p%1> ?IQZstart_addend_addadd_incrcont_on_errð ©4Щ(RÑR Щ,QÑQ1gÐð PÀPõRúÐ`RáR7Êÿÿ?üRÐR©(Ðð PÀPõQúÐ`RïRSÑS×SxSSÀSPÐ`RàRð ©4`Êÿÿ?üRÀ@RÐR©,LÊ©(Ê©,Ê©0Щ0ð ©4ï©(PÐ WágígPÀWÑW@ éÖPí©,PØð ©4ðiÊþýÿÿ©4Ò©<Ô©@Ô©DÔ©HÔ©LÚ%Ú%Úà%Ð@ WÒ‡< XíŸ@ ÔXíŸ@  ÐgXÊÿßÿÿXÐXgÚÚ%”©vð ©4ði»ÊÿŸD Õ©ï QQáQ¹ðQ¹ÒŸ@ ºð ©40ŒÒŸ@ ÊÿŸD 0«ÿá©4ÀQªÿQÑ©0<SÕŽði0ö#ÑQ©,+»$<©6RÑQ©,ïQSàSRÀ@QÊÿÿ?QÑQ©,ݺ$¹ЩT!´TÐSÐTUÑQTÀSTÐTUÑQTÐTQ±ð ©4»Ëÿÿ?üQSðSÐ RËÿÿ?|b~ÑŽSÀRÑR@ çïRRÀ RðR©4ºÁ@QTÊÿÿ?T0#ÑQUÑTUÐUTÑT©,Щ,Tº$Щ0S¸»´©6Ð Qáa ïaRðR©6ÕáQëºЩRÔP<© QxÿQQ¬‚PxPPåP¶PõQð°P©"Ò©<‘©wþ0!ÊÿŸD ÒŸ@ Ú%Ú'à©<Ô¹<Û'®ü®ü©CП@ ©D°ŸD ©@о©HЩL¾”©Bð©4áŸ@ ©B»Ð SЮ TéT–©BxÿTTõSòº‘©Bð©4 •©Bð©4ÐŽnÊ€ÿÿÿ®1¼ÿð ©4ði»ï ©4PÔQèP ÖQxÿPPòЩP´PÀ@PÀPÖA`ºð ©4€:ÿXÿMEM_Bitmap mark_Hard_SBEsÞ¯ýÉ€©vÔ©´© ´©"Ô©$ðið"ié©4ð"i0WüÔ©80[þá ©4©vÐQÐRàR©6Â@QôRñ©vðiÐQ©0©vÞÏl©LÒ§ü©0ÜЩ0Q<©6RïQSáSRÕÐVÐRði©vÒVTÐVaÐT¡©vÐaPСS©vÑPV$©vÑTS ©vá§üà"iÒVVõRÂxVV´1õ ©vÔa ©vÐaP ©vÕPèá"i ©và§üÛÒU©vÐUa©vÐaP©vÑPUÁá"i ©và§ü´©vÒaÒ¡©v¡¡¡¡¡¡¡a©vÐVÐaP©vÑPV[©vÐVСP©vÑPVC©v´¡©vÐVÐaP©vÑPV%©vÐVСP©vÑPV á"i ©vá§ü1 ©v<€VÐQX,ªªVhÀVX,ªªVhÀVX,ªªVhÀVX,ªªVh©vЩ0QЪªªªVÐUUUER<€SÐaPÑPV¤ÐRõSòá"i ©vá§ü1š!©vЩ0QÐRVÐUUUUR<€SÐaPÑPVÛÐRõSòá"i "©và§üÈ#©vÔQÔU<@RÞÏJ©LЩ0PÁPSÑQPÑQS ÀQíïQVàV©6À@QÊÿÿ?QÑQÌÔaÒ¡ÀRQÑQ¸1ùÞÏõ©LЩ0Q<€SÐUUUUV$©vÐaPÑPVØõSñá"i %©và§üÈ&©vÀ©0Щ0V,€fÀ€V,€fÁ€©0Q<©6SÐR<@TxSSàS ÂTõRîÐQRÂ@QÂTQÐR©$°T© ÐQ©,ÿÿ© a<© Tx÷TTÐTUxýTTÊøÿÿÿUÀ TЩQï QRÖRxRRÀRQ”qõTûÎUUšqRxURRRa0£0çú²©"©"ði1ZùÐP©,ÐV©(ÐQ©<0û©:©;©9©:©8©9©v©8Ò§ü1bü€EÔú–÷MEM_Dataÿÿÿÿªªªª*UUUUU'ÿððððpÿÿüÀ1èø©v0ª÷07úá ©4©vЩ(QÊ©0Щ00@ù1»ø¨€XÞ¯–©Hð©4¹H©C‘©CÿÖ©HÓQ ¹H©C•©C1‹©vЩHRТUš¢VÒVRÒU©LðXðVXÐXgÐUaðRXÐXgЩL¡©v”XÐXgðVXÐaPÑPU`©vÑgXW©v”XÐXgðRXСPÑP©LN©vÑgXEyUUåVÈU—©CŒÀ©H1PÿÊÿg|aá©40@úÀSQÑQT1ÿ1!ÿÐU©8Ë€ÿÿÿV©<ЩL©8Ë€ÿÿÿR©<°g©@á©44©vÍP©8~0§ùà©4#Í©<©@~0çùà©4â©41äþà"i ð©41€ÿ0Ã÷”X¨€XÒ§ü‹€ù_ùØõMEM_ByteÞ¯ýÉ€©v0öá ©4©v0¡øÐ©(Q0¾÷19÷©vÞÏË©LÁQPÑPT1šð©4Ò§üði©vÒa¡¡¡aÐaP©vÐRÑPRp©vÒaÒ¡<R°R¡<¡P©vÑPRO©v}aUÐUPÐÿÿÿRÑPR ©vÐVPÐÿÿÿRÑPR9 ©và§üðiá©40ßøÀSQÑQT1Eÿ1:ÿá©4ã©4à"iðiÝ0§ö1Úÿ1@ÿ€øJøÃôMEM_AddressÞ¯ýÉ€©v0õ0“÷á ©4©vЩ(Q0¦ö©vÐQaÀSQÑQTõê©vЩ(QÞÏ&©L0‚ö1oðiÐaPÑPQÀSQÑQTððiÜ©v©vðiП@ ©D°ŸD ©@0Æõá©4ðiÑS¼<©0Ò§ü1rÿÿÿÿÿªªªªUUUU©vЩ(QÓÿÿQÀQÊÿÿQ0ìõ1gõÞÏ:©L©vÊÿÿQÈøQÑQT4Þ¯©VÒ§üðiÐfUÐUaÐaPÑPU ©v> ©v8ðiÕ†ÔàQÊÿÿQÈüQÑQT·ÊÿÿQÀQÑQT‘1|ÿðiП@ ©D°ŸD ©@0Úôá©4Ë€!sö1óMEM_ECC_ErrorxT˜1§ô©v0ióÂW0óõá ©4©vЩ(Q0õ1ôÐQUð©4ÁUXÑXT1U1¶á©4øà"iôЧXð¼ XÐX§ÐaÊ€§Ñap‘§<jÈ€§ÐÁÊ€§ÑÁN‘§<HÈ€§ÐÁÊ€§ÑÁ,‘§<&È€§ÐÁÊ€§ÑÁ ‘§<1³Êÿ§ÔaÔÁÔÁÔÁð©4ðiÚÒgÐQUÐQ©<Þï(©LЧX¼XªXÐX§Ða¨X”XÐX§Ð©8ÕaÐa´©8ï gXïgPÒ©<©v‘©;©ví QX©vÑP ÀQ1ÐQ©<Þï(©LЧX¼XªXÐX§Ða¨X”XÐX§Ð©8ÕaÐa´©8ï gXïgPÒ©<©v‘©;­©vÑP¤©vï UPÑPX–1ÐRÀQÐQ©<Þï(©LЧX¼XªXÐX§Ða¨X”XÐX§Ð©8ÕaÐa´©8ï gXïgPÒ©< ©v±©:£ ©ví QX˜ ©vÑPÀQÐQ©<Þï(©LЧX¼XªXÐX§Ða¨X”XÐX§Ð©8ÕaÐa´©8ï gXïgPÒ©< ©v‘©:, ©vÑP#©vÃQPï PPÑPX Êÿ§Òg1ÐUQÑS ÓÿÿQ1“Þï¨VÒgЧXð X©vf¼XÐX§©vÔaá©4à"iðXÐX§ÕaG‘§XA©vÊÿ§Þï©LЩ8©vðiÕaði©všgPšfR‘PR©v‘©: ÖV•f1wÿ1tðiá©40ióÀSQÑQT1ü1„ü !"#$%&'(+-.03569: ©vȧÐaPÑP* ©vÐX§ÐaÒ©<ðiá©40ÇðÀSQÑQT1›þ1 þá©4â©41™þ0—î1âÿ±©8•©: –©:ÞÏ{ÿ¾о©Hޯо€Øïð–ìMEM_Correction1î©v0Öì0cïá ©4©vÂWЩ(QÞÏ·©L1 X^[]h,*n/km)p42v7su18|z>;=y0Jî1Åíð©4ði<¼XÒgÞ¯ÂVÐRÒUÐQ©<©vÒgÒUUÐX§ÐRaá©4à"iðXð XÐX§ÑaRf‘§X`Ê€§©vÐaP©vÑUPE©vÐgPÊþÿPÑ P.©vËÿàQ©8ÐgPÊÿàPÑP©8©vÐgPïPP‘fP1ÖÒRRÕU1cÿÖVxRRõ ©vÓ€ÀgÞÒg ©vÔR<¼XïRXUÒUUðURXÐX§Ôaá©4à"iðXð XÐX§ÕaE‘§X?Ê€§ ©vÐaP ©vÕP^ ©vÐgPáPS©vÓ€ÀPF©vÔUðRU‘PU6ÒgÖRÑR1{ÿÊÿ§ÔaÒ©<ðiá©40ŸîÀSQÑQT1qþ1vþá©4â©41oþ0oì1âÿо©Hޯ往. îMEM_FDM_Logic ÅêÞ¯ýÉ€©v1Þë0-ëЩ8Ô©<ÛPÐ P “PxüPPôïP©,Ð QÔPÐSÐaTíT*ïTV#ÑVÖV©vïQRïQUÑUV áTðRPÀQõSÆÐP©0ÑP1fëè©01IÞÏE©LðiÐ ©(Ò§ü©vï©(SàS©0À©(Ñ©(@ ã1|й(VÊÿÿ?üV,fÁVQЪªªªÐUUUUÒï©(P¾ï©(PxPPÀPQï©(PÁP©,RÒõRúÐüÿÿÐðÿÐÀÁVQš€RÒõRú1vÿ1–Ð ©(ï©(SàS©0À©(Ñ©(@ ç1ûй(VÊÿÿ?üVÐVQ©vÕévѪªªª¶©vÑUUUU©©vÑÿÿÿÿœï©(P1œï©(PxPPÀQPÑQP ©vÕó1|ï©(PÁP©,R ©vÑÿÿÿÿdõRô ©vÑüÿÿT ©vÑðÿG ©vÑÀ:ÁVS ©vÕ*ÑQS÷š€R©vÑÿÿÿÿõRð©vÓà§ü1ÿ1Ž©vÁVSÕíÑQS÷Õ©vÔQš|R0€©vÕ@©vѪªªª3©vÑUUUU&©vÑÿÿÿÿ©vÕõRù©vÓà§ü1©vÐ ©(1ï©(SàS©0À©(Ñ©(@ ç1¨Ð¹(QÊÿÿ?üQ0­ï©(PÁP©,QxPVïV©0VÔXéV*ÖXxÿVVôï©(®üÀ ®üð®ü©4à©41l©v©9ï©(P×XÐXVDÔPÀQPõVú©vÑPR¿ÐXVÖPõVû©vÑPS®ÐXVÖPõVû©vÑPTÐXVÖPõVû©vÑPUŒ1Gÿ0_ÐS<Qš€R©vÕ12õRöõSæ©vÓà§üé©vÐ ©(1ï©(SàS©0À©(Ñ©(@ ç1jй(QÊÿÿ?üQ0¬Ñ©( í©0Ïš€Q ©vÑRQ!©vÑSQ"©vÑTQ #©vÑUQ§ï©(®üÀ ®üð®ü©4à©41T©v©91…ÿï ©4P—©8ðP©<1üýð(i1çðP©<ÒPPÒ©©vÑPU5©v/©vá©4&Òfà"iÊ ©vÐRaÐaPÑPRàf 04çði­0åði¬ ©vÞÏF©LЩ(QЩ8RЩ ©vÑPU5 ©v/©vá©4&Òfà"iÊ©vÐRaÐaPÑPRàf 0˜æði­0|äði¬©vÞÏC©LЩ(QЩ©vÑPU5©v/©vá©4&Òfà"iÍ©vÐRaÐaPÑPRàf 0ÿåði°0ããði¬á©4.à&i*í(i#Õ©(Ñ©,Ñ©8ªªªª Ñ©>ÝRŸ¯ôÝï{‹ÿÿRun from ?[0=ROMŸ¯ìÝï]‹ÿÿàd ,1=Diag_RAMŸ¯ñÝï@‹ÿÿàd,2=RAMŸ¯öÝï(‹ÿÿ,3=fastest possibleŸ¯éÝï‹ÿÿ ] (0):%bß©(ß©<Ÿ¯îÝïÓ‹ÿÿ0Þˆ ÿ• ÿfàdb%n%a>>ÝRŸ¯ôÝïÅŠÿÿ/Addressing mode? [0=physical,1=virtual] (0):%bß©(ß©<Ÿ¯ÈÝïk‹ÿÿ•©(ˆ€ ÿ%n%a>>ÝRŸ¯ôÝïcŠÿÿ7Repeat? [0=no,1=on error,2=forever,>2=count>ÝRŸ¯ôÝïþ‰ÿÿ”S"Error severity ? [0,1,2,3] (2):%bß©(ß©<Ÿ¯ÕÝ﯊ÿÿ•©<©(ð©(S%n%a>>ÝRŸ¯ôÝÿÿ-Console error report? [0=none,1=full] (1):%bß©(ß©<Ÿ¯ÊÝïJŠÿÿ•©<©(ð©(S%n%a>>ÝRŸ¯ôÝï=‰ÿÿ*Stop script on error? [0=NO,1=YES] (1):%bß©(ß©<Ÿ¯ÍÝïè‰ÿÿ•©<©(ð©(SS©(0à%n%a>>ÝRŸ¯ôÝïÔˆÿÿï¡ÿXLED on entry (%b):ÝXŸ¯èÝ﬈ÿÿ%bß©(ß©<Ÿ¯ôÝï~‰ÿÿ•©(X©(0€%n%a>>ÝRŸ¯ôÝïtˆÿÿConsole on entry (%b):Ý¡ÿŸ¯ãÝïMˆÿÿ%bß©(ß©<Ÿ¯ôÝï‰ÿÿ•©(¡ÿ©(0 2¤UÀTUÐUVš…W1<û•…1ý‘¥ÿ;2…XÀVX•…%n%a>>ÝRŸ¯ôÝïí‡ÿÿ %a : %lÝ…ÝXŸ¯ðÝïÓ‡ÿÿ1¼2…XÀVX•…%n%a>>ÝRŸ¯ôÝﲇÿÿ %a : %l - %l ?Ý¥ÝeÝXŸ¯æÝÿÿ‘¥üBÐ S(%l) Ý¥Ÿ¯ôÝïo‡ÿÿ%lß©(ß©<Ÿ¯ôÝïAˆÿÿ•©<'Ð¥©(0RÀSU12%lß©(ß©<Ÿ¯ôÝïˆÿÿÐSÑ©(eÑ©(¥ 0#ÀSU11LÿõW12ú1óþé,©8ש,©(€Ã©,©8 ©,Щ(€*%nNo space, backup to previous diagnosticŸ¯ÓÝﲆÿÿ©0PÀP©,Щ0PÞÏÆùn½°bbR R R R S S Á Á4 4‘‘3 3Å ÅU UF F55  C C@2 2Ç ÇQ QT T4„ 4Å„ Å¢¥ ¦11II Â00OONNMMLLKKJJHHªªªªUUUUGG@ @@D D66üÿ€€EEZ Z'A A ¢¥¦OONNMMLLKKJJHHªªªªUUUUGG@ @€ €A A11II00§OONNMMLLKKJJHHªªªªUUUUGG@ @ @A Aº¼¡»¡¼¡®0ONMLKJHªªªªUUUU@ÿÿÿÿ€A 1A €A  B Æ `¢¥¢ B Æ `bR R  S Á 4 ‘3 Å U F 5  C @2 Ç Q 1I 0ONMLKJHªªªªUUUUG@ @D 6üÿ€4„ Å„ EZ 'A   B Æ ` b  B Æ ` ‘  3 2 1 I 0b‘ @ 93 82 71 6I 504R 3 R 2 S 1(Á 04 )Å (U 'Q &Ç %F $5 #  C " !O  N M L K J H ªªªªUUUUG @@ ÿÿÿÿD 6 üÿ€  T 4€ Å€ E Z èA õH ÷H µI ¸I ÑI .K 2K L 3L M M M M ëM úM N >N AN CN ‰P ÉP BQ |Q  `~`1!12@23#34$45%56^67&78*89(90)0-_-=+=qQQwWWeEErRRtTTyYYuUUi IIoOOpPP[{[]}]aAAsSSdDDfFFgGGhHHj JJk KKl LL;:;'"'\|\<>?A55556+57,*5 -5 !. "/59#05b$15;%25<&35=5f45ce55dg55556+57,*58 -5 !. "/59#05:$15;%C5<&D5=5iE55hB5>?j55555p57,*5n -5 !. "/59q#05:$15;%C5<&D5=5iE55ml5odk55556r+5s,*5t -5 !. "/5u#05 $15;%|5<&}5v5z~5xy{5w5555555X5€R,*5H -5I!.J"/5#05‚$Y5ƒ%Z5„&[5…5U‹5†ˆŠ5P‡‰5555Œ–57,*5Ž -5!. "/59‘#05:$15;%C5<&D5=5”E555•5’“5555Œ–57,*5Ž -5!. "/59‘#05:$15;%C5<&D5=5˜E555•5—™55556+57,*58 -5 !. "/59#05:$15;%C5<&D5=5iE55hB5>?j55556+57,*58 -5 !. "/59#05:$15;%C5<&D5=5šE555B5>?A55555F›TX5GR,*5H -5I!.J"/5K#05L$Y5M%Z5N&[5O5U\5QSW5P5V5555ž+57,*5 -5 !. "/59#05:$15;%C5<&D5=5œE5Ÿm5555555 +5,*5 -5 !. "/5¡#05 $15 %C5&¢55¤45¦5£¥5555ˆ˜ ¨¶¸ÀÈÐØàèð»üžïÞïþÿ«@žï6êþÿ«Džïw«H”«TÔ«8žïõÁþÿ«\1 DIAG0 Ô«LÔ«PЫXYÐYRš©Q<ÿÿPïoÁþÿÕP\š©PÀPÀYPÐYRÐ`Q<ÿÿPïPÁþÿÕP=ž¯´PÐYQï5éP-ûaÑP%žïˆ«(žï“«,žï«džï «lÐQºü»à}PUš¦WÀVW<‡XÐQEÑgeѧ¥±§¥ ÀWõXçÐQ'Á§VRЧ Q<ÿÿPïÀÀþÿÕPÐQ Á§VQÐPÔPºà»‚°?»<šQ»0º‚»‚°?»<ÔQ»0º‚»éPQ«U«UP»ŒÐøRÀ«XRÔS°S‚òðSõ»(ºŒÕQ ÝW»,ÐŽW ÑQÝW»(ÐŽW» ‘¯Rˆ«T1‘®Rˆ«T1€‘°R Œ«T“«T žïoPžïiP»41Z‘³RŠ«T1M‘R¼G¼Rê«TQÔQÐ Sš£Sžï¦ùÿÿ~ÄDSÀŽSšBcSxSSÀSQžïîöÿÿRšAbQÔPº „„§1VERS SYS0[SYSEXE]SYSBOOT.EXE[SYSMAINT]DIAGBOOT.EXEDUAÿÿh ¢DJAÿÿh ŽDAAÿÿh zDLA fMUAÿÿ@ RXQA`%  èPRA I @HA BÀCDE(F¬GpHIJTKÔ L„M€N€O€P€QŒRDS2TDU 2.. 1..0.. - Boot Device: `«, á Ÿ ÐPRA0¯©Þ¯ÄPš`Þ¯Ÿ žÏ;WÀÿWÊÿWžÇWšÿYÞϽwôYøÔÏPÿžÏM§žÏ §`Õˆ™ žÏ4:ÇÈžÏa:ÇÌÚWÛ>XxèXXXÏWÕ'»ÿЫ4R Т,QÐ[YúlAbºÿЫ Q}«DR}«,Tá UÐ^VžÇ ^ÕÔŒ žï-§,žïu-§(žÏ ï]'áUžÆþV}P¦}R¦$}T¦,Õ2ŸÏÄþÝŸÏ#þûÏÞÏÓþPšÏ°þ`žÏ«þ ÐP¦,`Ï¡þÏ›þå¦0}Z¦ÐVfÐVÏaþÐ\¦Ô¦Ô¦ ΦžÏS¦4,nܦ8ÐV[ÐW˰Ðx «\Ë(Ë‘Ðωü˞Ͼý\Ð^ZÐWY}«$RÔ«LÔË}R«DxRRÔ¬ ÃR¬žË¼XÐRhÔ¨š U|VÔPÔW}«$R’ƒPõRøÔQáQPÖWóQöë}«$RxR«LÐWËÂW«LÔR}«$Pž@aQšqPÀRöšQàQPÖRôQ÷ÂRËЫ\WžÇWÔQBjQ05éPÕœªXСYÀ¡ ZÁZ¡UGÐZU}«ÐŽTáÏÿ ª@Ïÿå|~šŒ~ûÏ…|~š¡~ûÏzÑNÏçþ½À =ÏÜþ²ÕŽ}ÏÇþϼþª@QáÏÐþ¨@Q0 éPÕT0Š}Q~žªS0ïžÏ ~‹ðQPP©_žÇÕàÔPWžªPÂPSžÏŠôPàQ°ÏxþR´PxðPQÂQžÊSÀ¢S0ÓéP (Q¢cšPÔPº>»|¬4|¬$|¬LÔY´PœðPSž¢RÂS š‚Pš‚QP'BRZbbÀQRÂQSÛ1QÔPXÐQТÿY1Cž¬4P\°Q«hÑQ°«hž«jPIÌž¬$P0ž«(P|`(¼ž¬DP1´ž¬È ¬,´ÏbøÐ QÔÁÒ8œÁÐÁÕ¡l ¨Ï=øš¡lСlÏ+øÀdÏ"øÐ«4RТQ»Ð[YúlAbºèPÔPCÏ îÏø‘%Ïø Õ¬HÏô÷}¬<ÏÄ÷ЬDÏÖ÷,ÊÐÏÇ÷Q,Èa0ÜþšPº>»?Ð QÔÁÔÁÔÁÔÁà Ϥ÷Ô¡lЫ4RТ,Q»Ð[YúlAbºÊ ¬,º?<ÝQ|~ЬPÑPÿÐPQÐ^PÐQ •ü×QàQ`•¬Ÿ  1ÔQŸ ÐQSÐPRácŠ cÖSõRôš¬RÑPRÐRPЬ RP‚(Pab<PÀ^ÐŽQ»ÔPŸ Š€PºFõð òÿS€üЬYÛ8QÐA©\SЬZ<¬XœXï ZWžGÈÿWx÷WWЬ [ЩPQàZÛQï ZR‘©f  ‘©f`1IÁχÿTÞDÃTž‚Ué¬ ÐEaUÊàÿUÉUÏyÿ„×Wäï ZZÊ€tÁÏSÿPœ PPÈPZÛ8PÐ@©TWÝ Ð[UЩ4P@°èPõnïüЬ WЬRÐR§PЬSÐS§`ï §\TÐUï SPÞ@bQ ÐUËÿ§TTœTT<§TPžCà0ÿÿ§XÉTÖTõUó DLDRIVER.EXEï0´©ÐPÔPð©dP° §©Pg0<§VíVî³gé©Pg0÷1Ù«?§QÄUÔV{PUVTÔU{(TUTxVVðUV±VQ.ªQ«VR¢RQ®QQ¨QðUQ©Q§©Pg0¡1ƒðTV°V§°Z§œðZQ°Q§°XR£T(Q¤Q±RQ°QR¦R®R§° QѬ ° Q©PQg0X=¤R¢RX$«§Q©?§V¶VïVUÔT°§ZœZZ°§Z1eÿ<¬QЀQ<P<¬XÐ@Xï ¬ZÖ¤>ÕUÐŽbÚS:ºÿ TUDRIVER.EXE PUDRIVER.EXEMUüÛ8PÐ@©TWЩ4Qï¨èP”¡8Ь,ËüÿW¬$°€¬(°ü©С$SÐ@©\RÞCÂTžÏxRËþÿÿRQx S¢ÈQ¢À¢ï RRéP ÐB¹PRÊàÿRɯuR„ÖRɯnRdЀÃÉÏWÿãÏVÿ³ø§ 0éP1¿ÐXžÏSÐ R´gЩ4QÅ€„¡>Q<PÔ~°§TàRTÁ©4BnоnõnýõQãÔPÕŽèP õX½<Ô P€µTï°ƒ§óR±Ð©4QÅ †¡>Q<PÔ~µ§Á©4BnоnõnýõQèÔPÕŽÁ”ϑϜÁÔÏ…Ï”ÁÏ}ÏÁTÏqψÔÏ‚þWéPN<¢PÄ€PÑPÏqþÐPÏjþ0iÐb<©d¢› ¢0QÔÏLþï¢QxQQï¢T¨@@Q©QTÏ[þˆPèP<„PÔT0*Ðb›¢1‰<<žÏÿRéÏñüžÏ6ÿRÝRšP|‚õPûÐŽR ¢ ÝѬ!1»ÔT±lЬTÕU›%¢»p0yºpéPS‘Ϧü0£éP@ ›!¢<¢ ÑXÐX¢ ÐZ¢0éP#ÀZÂXÀ•ÏgüeéPóð¢ P”ÏTüº›%¢Ѭ$¨¢ :Ѭ¨¢ ÂÏ+üU ÕTÐÏ üd<Pž¢QѬ%ž¢ QÕUÎUU¨¢ ÐUa0”èP•ÏúûžÏéýRéÏíûžÏãýR–ÏãûЩ4QÅÏÞû¡>Q<PÔ~µbÁ©4BnоnõnýõQéÔPÕŽ—ϵûéP~¨€bžÏ1þRèÏ£ûžÏgþRТÏ’ûÕTÐÏŠûdï¢ PÑPÑP ÑPDÖ¤>ÕUÐŽbÚS:ºÿ DUDRIVER.EXE PUDRIVER.EXEDUüÛ8PÐ@©TWЩ4QïÀèP”¡8Ь,ËüÿW¬$°€¬(°ü©С$SÐ@©\RÞCÂTžÏXRËþÿÿRQx S¢ÈQ¢À¢ï RRéP ÐB¹PRÊàÿRɯcR„ÖRɯ\Rd³ø§0þèPyÐXžÏ SÐ R´gЩ4QÅ@B¡>Q<PÔ~°§TàRTÁ©4BnоnõnýõQãÔPÕŽèP õX½<Ô P€µTï°ƒ§óR±ÁLϱϼÁ ϩϸ0ˆèP1_šÏôþÐe<©d¥š ¥0<éPˆPáÏæDˆP?±T5×ÏËþ/Щ4QÅ@B¡>Q<PÔ~ÕTÁ©4BnоnõnýõQéÔPÕŽ¦<„PïÏ©QxQQïÏžR¨@@Q©QRÏvþžÏ1UÐeÔ¥š¥|¥ |¥1¶€<!¯ˆÑ¬ "Ï}ÿÐU¯ÐXÏxÿÐZÏwÿЀ–˜R°§T^¨€ÏMÿ¨€ÏBÿ°gT°§TGЩ4QÅR¡>Q<PÔ~µÏ'ÿÁ©4BnоnõnýõQçÔPÕŽéP¨€ÏÿïÏRÿT±T<PÖ¤>ÕUÐŽbÚS:ºÿdhl XQDRIVER.EXEXQü Њ ©8”Ï’Û8PèP á¬,–σ´©Ý èPõnøÕŽ”ÏpÛ8PÐ@©TW°§éÏ_š¬H›§ à§ Ô¬H´§ Щ4QÏŠС$SÐ@©\RÞCÂTžÏ^ÿRËþÿÿRQx SÏ#ÈQÏï RUéP ÐE¹PUÊàÿUÉÏ3U„ÖUÉÏ+Ud,n bžÏæRg‚§‚§‚§‚§‚§ ‚éÏÔ }¢ú¬<žÏÔ¬DžÏœ V,n€¦ÐSÐPÀVžÏ£RÐQÖVb†õQúÖRõPðõSâžÏ‹R|PxQQÑQÿÿÂÿÿQ‚~‚~<ŽSÀSQÑQÿÿÂÿÿQóPÌÑQÿÿÔQª§°§§~g~±ŽQQ<PÔ~µª Á©4BnоnõnýõQèÔPÕŽèP1kžÏÐP³ ªD᪠ÀÀÀØÀÄÀ (Ø ,ÀϨ Ø ïªTÑT À PØ TÀ HØ LšPá ªÀ XØ \á ªÀ `Ø dQ<PÔ~‘ª ª Á©4BnоnõnýõQæÔPÕŽèP1ZÿЩ4PÇ >QϪ³Xªá ª<1Äà ª‡žÏŸP᪠ÀÀˆØÀŒáª ÀÀØÀ”áª@ÀÀ¨ØÀ¬4éÏa°¦ ¥1±¥¦ è¥&Ñ¥¦±¥¦ "žÏFPÀÀ ØÀ¤õn1Ìþ1ÿЦ¥°¦ ¥«ÿøªPª P .PžÏXÀ¨ ب$ÀP¨بéfÀ¨8ب<ÀP¨0ب4 Pée°¦PxPP°P€Û8PÐ@©TW°§éÏİ€§ <P»ÿЩ4TÛ8UÕU+Щ`SÊÿSЩPVï SWÞGfRÝbÐ bÚS:ÞÃQÐ QÔ¡Ð N~ÐaõnýСPÔaÇP`ê¤BÖ¤BÔ¡Ð NPÐa³€ÏÁ©4BnоnõnýõPäСPÔaÕŽÇP@ ¤>Ö¤>ÕUÐŽbÚS:ºÿÿÿöîÿÿ ÿÿ™Òïÿÿ+,Ø*ÿÿ¥CõÿÿV äÿÿ`ÈÀøÿÿ#2ü0»~]º~Щ4P@°»þKžEµ VžÏ?íTÃTVW6(¥fdžÏßëTÂW¤ÂW¤ Õ¤ ÂW¤ Õ¤ÂW¤Õ¤,ÂW¤,Õ¤0ÂW¤0ºþÞÏõþUš©fSšÏÙëTÐR š/P4ÔRÐRž«TïRSQAÏùý„ÂRð”dž«Tš„P ÷š\PšXPšP~|~Þ®~ûïôæÿÿÐŽPš Pçš Pâš PÝš‰Xž«´Y0DçÐPX‘Xâj¸šyXå·ååj©áXŠ X¦X‰:XÏ“ýË‘ X°”iž«´Y²0þƒP«ÿÕV0 þŒ€jЫkïjPðPjÐQÕjœ«þQQÂQk1¾þ0bþájЫè«0¶þÊ€ÿÿj”«ü|VXPIGEBŠj0=þ0Pÿ:X¯ì¯P8îÒïÛ1$ýCLWB01ÿ:X¯ôïàP ƒP«þŠjˆjðjåjájVà jÐRÕBÏúûóRõ¼Ð«ìRíÑR±ÔBÏ üÔBÏ%üЫèP``á j ЫðBÏñûÕ»ðá jЫôBÏüÐPBϳû1IÿÐUÐEϨûX.ÐUS0šþ0Jþ0þÐXS0Eþ0†þÐEϳûS07þÐEÏÈûS0pþ0)þóUÆ1kþájЫè«TÞkQájâjÞ«Q Þ«TQájЫèaÐaV1˜üÞ«Uõ0;þ:XÏìû-ÃPPÞ@eVá j ï«ìQÞAÏ‚ûQÄÞÏ{ûUÓÐfVÃÚ&1ïûÚ}PÏ®úžÏ²úQ}R}T}V}X}Z}\ž® }®ÔÔžÏoú[ž«äZž«´Y”iÛPžÏiûQРž¯± Рž¯¨ Р ž¯Ÿ  Р$ž¯– $1.þ}«T®ÛPžÏ5ûQРРР Р$ž« Q}R}T}V}X}Z}\}ÏúP brk at 0Oÿ0¬ÕSˆj0jà«X8ÐSU00ý0àüž¯ÓT0úüЫTS0Öü0ýÐEÏDúQÐQk0[üÐEÏVúQÐQYúlÏðú@åj â«Xâj0\ÿ0ïþåjÊ«X äjÜЫTk0 üÎÐQÐAÏÎùPAÏéù`õQïÐQÐAϹùP`AÏÓù“jÑP«T`õQáÐSÑ«TCÏ”ùõSôÐkU0ü‘'XX…óÐUkàj«þ «è»°«è»Ыè»Ú«èkáj ЫèY1?úâjaA0zZ9W70<Ð R <ÐR<ÐR|P|S×S@šC¼UÕS‘U+2‘U-ÖT)ÔQ‘UA¯¾‘UA¯ºòQî&‚A¯±UÑURzRPUPÕQò¬S»éTÎPPÐP¼ ÐPÔPݬÐnPР4PÝݬݬݬß¼ û@°š9@Í@g€@4@š ÍÝU|ÏUÏÞ¯£P}€QЀUÑ«LQòÕ¨ (¨¼<P}¨­àШ­è°¨­ìíäV<­àW<­ì­üÁ­è[­øÀVW1¤1‰úlÏ—éP÷ЬUüVéÞ¥Tœ¤Wµ¤ ×WÁV­ä°W­à­âÕ[Oë«Rx÷RRBÑRWÐWRЫSx R~@eTš¥:U>EdUïdP¯P0œ„PðPЄQÎQÀTÔPЄPïPQyPPš¤üPÖP<„PïPPЄQðü|VÑlЬP }`VÊVÔ¼ ЬUš¥4PïP~0ÿÔS)0‹ÿÕS ÕPénÐQnÀPSÕV ÐP‡ÐQ‡ÂVÕWÀ¼ ÑTUÒº}R¼<P‘¡<¡ ~С~µnÑ€Ž Õn±`nº <P< P<ÿPÔR RõPú±Ra<PHDR1HDR200512üÐÏiǼЬP'}`R::Rcž¡SžpR‘[c‘ÿÿÿxôPSÐŽP<Ÿx UÐP~ï"ÿÿÿxôPUÐŽPxUU<Ÿx SÐP~ïÿÿÿxôPSÐŽPx SSЫR<« Q xôŸH Q1ðÑQ@ôïÑþÿÿxôPQÑQ@àПH QÃÀQSÀ@RÔP@b@còÀPóžÃ@«$žÃ@Ÿ ë$Qž¡¡xôŸH QÃQSRÐR«x÷ŸH QxôŸH PÀÿÁPx÷PPÂPQÔPÝQxûQQÐÿÿÿÿ@bòQPôxPPÐŽQÑPQâPbòQPøx÷ŸH QåPbòQPøÔPxôŸH QŸj °P«"´« xôŸH QЫRïTþÿÿ<Ÿx Ÿº ÐP~ïÔýÿÿxôPŸº ÐŽPsimh-3.8.1/VAX/vax780_uba.c0000644000175000017500000010735111111052712013325 0ustar vlmvlm/* vax780_uba.c: VAX 11/780 Unibus adapter Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. uba DW780 Unibus adapter 19-Nov-08 RMS Moved I/O support routines to I/O library 28-May-08 RMS Inlined physical memory routines 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato) */ #include "vax_defs.h" /* Unibus adapter */ #define UBA_NDPATH 16 /* number of data paths */ #define UBA_NMAPR 496 /* number of map reg */ /* Unibus configuration register */ #define UBACNF_OF 0x00 #define UBACNF_ADPDN 0x00800000 /* adap pdn - ni */ #define UBACNF_ADPUP 0x00400000 /* adap pup - ni */ #define UBACNF_UBINIT 0x00040000 /* UB INIT - ni */ #define UBACNF_UBPDN 0x00020000 /* UB pdn */ #define UBACNF_UBIC 0x00010000 /* UB init done */ #define UBACNF_CODE 0x00000028 /* adapter code */ #define UBACNF_RD (SBI_FAULTS|UBACNF_W1C) #define UBACNF_W1C 0x00C70000 /* Control register */ #define UBACR_OF 0x01 #define UBACR_V_DSB 26 /* map disable */ #define UBACR_M_DSB 0x1F #define UBACR_GETDSB(x) (((x) >> (UBACR_V_DSB - 4)) & (UBACR_M_DSB << 4)) #define UBACR_IFS 0x00000040 /* SBI field intr */ #define UBACR_BRIE 0x00000020 /* BR int enable */ #define UBACR_USEFIE 0x00000010 /* UB to SBI int enb */ #define UBACR_SUEFIE 0x00000008 /* SBI to UB int enb */ #define UBACR_CNFIE 0x00000004 /* config int enb */ #define UBACR_UPF 0x00000002 /* power fail UB */ #define UBACR_ADINIT 0x00000001 /* adapter init */ #define UBACR_RD ((UBACR_M_DSB << UBACR_V_DSB) | 0x0000007E) #define UBACR_WR (UBACR_RD) #define UBA_USEFIE_SR (UBASR_RDTO|UBASR_RDS|UBASR_CRD|UBASR_CXTER| \ UBASR_CXTO|UBASR_DPPE|UBASR_IVMR|UBASR_MRPF) #define UBA_SUEFIE_SR (UBASR_UBSTO|UBASR_UBTMO) #define UBA_CNFIE_CR (UBACNF_ADPDN|UBACNF_ADPUP|UBACNF_UBINIT|\ UBACNF_UBPDN|UBACNF_UBIC) /* Status register */ #define UBASR_OF 0x02 #define UBASR_V_BR4 24 /* filled br's - ni */ #define UBASR_RDTO 0x00000400 /* read tmo - ni */ #define UBASR_RDS 0x00000200 /* read sub - ni */ #define UBASR_CRD 0x00000100 /* read crd - ni */ #define UBASR_CXTER 0x00000080 /* cmd xfr err - ni */ #define UBASR_CXTO 0x00000040 /* cmd xfr tmo - ni */ #define UBASR_DPPE 0x00000020 /* parity err - ni */ #define UBASR_IVMR 0x00000010 /* invalid map reg */ #define UBASR_MRPF 0x00000008 /* map reg par - ni */ #define UBASR_LEB 0x00000004 /* locked err */ #define UBASR_UBSTO 0x00000002 /* UB select tmo - ni */ #define UBASR_UBTMO 0x00000001 /* UB nxm */ #define UBASR_RD 0x0F0007FF #define UBASR_W1C 0x000007FF /* Diagnostic register */ #define UBADR_OF 0x03 #define UBADR_SPARE 0x80000000 /* spare */ #define UBADR_DINTR 0x40000000 /* disable intr */ #define UBADR_DMP 0x20000000 #define UBADR_DDPP 0x10000000 #define UBADR_MICOK 0x08000000 /* microseq ok */ #define UBADR_RD 0xF8000000 #define UBADR_WR 0xF0000000 #define UBADR_CNF_RD 0x07FFFFFF /* Failing map entry - read only */ #define UBAFMER_OF 0x04 #define UBAFMER_OF1 0x06 #define UBAFMER_RD 0x1FF /* Failing Unibus address - read only */ #define UBAFUBAR_OF 0x05 #define UBAFUBAR_OF1 0x07 #define UBAFUBAR_RD 0xFFFF /* Spare registers - read/write */ #define UBABRSVR_OF 0x08 /* Vector registers - read only */ #define UBABRRVR_OF 0x0C #define UBA_UVEC 0x80000000 /* Data path registers */ #define UBADPR_OF 0x010 #define UBADPR_BNE 0x80000000 /* buf not empty - ni */ #define UBADPR_BTE 0x40000000 /* buf xfr err - ni */ #define UBADPR_DIR 0x20000000 /* buf rd/wr */ #define UBADPR_STATE 0x00FF0000 /* byte full state - ni */ #define UBADPR_UA 0x0000FFFF /* Unibus addr<17:2> */ #define UBADPR_UA 0x0000FFFF /* last UB addr */ #define UBADPR_RD 0xC0FFFFFF #define UBADPR_W1C 0xC0000000 /* Map registers */ #define UBAMAP_OF 0x200 #define UBAMAP_VLD 0x80000000 /* valid */ #define UBAMAP_LWAE 0x04000000 /* LW access enb - ni */ #define UBAMAP_ODD 0x02000000 /* odd byte */ #define UBAMAP_V_DP 21 /* data path */ #define UBAMAP_M_DP 0xF #define UBAMAP_DP (UBAMAP_M_DP << UBAMAP_V_DP) #define UBAMAP_GETDP(x) (((x) >> UBAMAP_V_DP) & UBAMAP_M_DP) #define UBAMAP_PAG 0x001FFFFF #define UBAMAP_RD (0x86000000 | UBAMAP_DP | UBAMAP_PAG) #define UBAMAP_WR (UBAMAP_RD) /* Debug switches */ #define UBA_DEB_RRD 0x01 /* reg reads */ #define UBA_DEB_RWR 0x02 /* reg writes */ #define UBA_DEB_MRD 0x04 /* map reads */ #define UBA_DEB_MWR 0x08 /* map writes */ #define UBA_DEB_XFR 0x10 /* transfers */ #define UBA_DEB_ERR 0x20 /* errors */ int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */ uint32 uba_cnf = 0; /* config reg */ uint32 uba_cr = 0; /* control reg */ uint32 uba_sr = 0; /* status reg */ uint32 uba_dr = 0; /* diag ctrl */ uint32 uba_int = 0; /* UBA interrupt */ uint32 uba_fmer = 0; /* failing map reg */ uint32 uba_fubar = 0; /* failing Unibus addr */ uint32 uba_svr[4] = { 0 }; /* diag registers */ uint32 uba_rvr[4] = { 0 }; /* vector reg */ uint32 uba_dpr[UBA_NDPATH] = { 0 }; /* number data paths */ uint32 uba_map[UBA_NMAPR] = { 0 }; /* map registers */ uint32 uba_aiip = 0; /* adapter init in prog */ uint32 uba_uiip = 0; /* Unibus init in prog */ uint32 uba_aitime = 250; /* adapter init time */ uint32 uba_uitime = 12250; /* Unibus init time */ int32 autcon_enb = 1; /* autoconfig enable */ extern int32 trpirq; extern int32 autcon_enb; extern jmp_buf save_env; extern DEVICE *sim_devices[]; extern UNIT cpu_unit; extern uint32 nexus_req[NEXUS_HLVL]; extern int32 sim_switches; extern FILE *sim_log, *sim_deb; t_stat uba_svc (UNIT *uptr); t_stat uba_reset (DEVICE *dptr); t_stat uba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat uba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); t_stat uba_rdreg (int32 *val, int32 pa, int32 mode); t_stat uba_wrreg (int32 val, int32 pa, int32 lnt); int32 uba_get_ubvector (int32 lvl); void uba_ub_nxm (int32 ua); void uba_inv_map (int32 ublk); void uba_eval_int (void); void uba_adap_set_int (int32 flg); void uba_adap_clr_int (); void uba_set_dpr (uint32 ua, t_bool wr); void uba_ubpdn (int32 time); t_bool uba_map_addr (uint32 ua, uint32 *ma); t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat uba_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); extern int32 eval_int (void); extern t_stat build_dib_tab (void); /* Unibus IO page dispatches */ t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); /* Unibus interrupt request to interrupt action map */ int32 (*int_ack[IPL_HLVL][32])(); /* int ack routines */ /* Unibus interrupt request to vector map */ int32 int_vec[IPL_HLVL][32]; /* int req to vector */ /* Unibus adapter data structures uba_dev UBA device descriptor uba_unit UBA units uba_reg UBA register list */ DIB uba_dib = { TR_UBA, 0, &uba_rdreg, &uba_wrreg, 0, 0 }; UNIT uba_unit = { UDATA (&uba_svc, 0, 0) }; REG uba_reg[] = { { HRDATA (IPL14, int_req[0], 32), REG_RO }, { HRDATA (IPL15, int_req[1], 32), REG_RO }, { HRDATA (IPL16, int_req[2], 32), REG_RO }, { HRDATA (IPL17, int_req[3], 32), REG_RO }, { HRDATA (CNFR, uba_cnf, 32) }, { HRDATA (CR, uba_cr, 32) }, { HRDATA (SR, uba_sr, 32) }, { HRDATA (DR, uba_dr, 32) }, { FLDATA (INT, uba_int, 0) }, { FLDATA (NEXINT, nexus_req[IPL_UBA], TR_UBA) }, { FLDATA (AIIP, uba_aiip, 0) }, { FLDATA (UIIP, uba_uiip, 0) }, { HRDATA (FMER, uba_fmer, 32) }, { HRDATA (FUBAR, uba_fubar, 32) }, { HRDATA (BRSVR0, uba_svr[0], 32) }, { HRDATA (BRSVR1, uba_svr[1], 32) }, { HRDATA (BRSVR2, uba_svr[2], 32) }, { HRDATA (BRSVR3, uba_svr[3], 32) }, { HRDATA (BRRVR4, uba_rvr[0], 32) }, { HRDATA (BRRVR5, uba_rvr[1], 32) }, { HRDATA (BRRVR6, uba_rvr[2], 32) }, { HRDATA (BRRVR7, uba_rvr[3], 32) }, { BRDATA (DPR, uba_dpr, 16, 32, 16) }, { BRDATA (MAP, uba_map, 16, 32, 496) }, { DRDATA (AITIME, uba_aitime, 24), PV_LEFT + REG_NZ }, { DRDATA (UITIME, uba_uitime, 24), PV_LEFT + REG_NZ }, { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, { NULL } }; MTAB uba_mod[] = { { MTAB_XTD|MTAB_VDV, TR_UBA, "NEXUS", NULL, NULL, &show_nexus }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, NULL, &show_iospace }, { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", &set_autocon, &show_autocon }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", &set_autocon, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, NULL, &uba_show_virt }, { 0 } }; DEBTAB uba_deb[] = { { "REGREAD", UBA_DEB_RRD }, { "REGWRITE", UBA_DEB_RWR }, { "MAPREAD", UBA_DEB_MRD }, { "MAPWRITE", UBA_DEB_MWR }, { "XFER", UBA_DEB_XFR }, { "ERROR", UBA_DEB_ERR }, { NULL, 0 } }; DEVICE uba_dev = { "UBA", &uba_unit, uba_reg, uba_mod, 1, 16, UBADDRWIDTH, 2, 16, 16, &uba_ex, &uba_dep, &uba_reset, NULL, NULL, NULL, &uba_dib, DEV_NEXUS | DEV_DEBUG, 0, uba_deb, 0, 0 }; /* Read Unibus adapter register - aligned lw only */ t_stat uba_rdreg (int32 *val, int32 pa, int32 lnt) { int32 idx, ofs; if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>UBA: invalid adapter read mask, pa = %X, lnt = %d\r\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } ofs = NEXUS_GETOFS (pa); /* get offset */ if (uba_aiip && (ofs != UBACNF_OF)&& (ofs != UBADR_OF)) /* init in prog? */ return SCPE_NXM; /* only cnf, dr */ if (ofs >= UBAMAP_OF) { /* map? */ idx = ofs - UBAMAP_OF; if (idx >= UBA_NMAPR) /* valid? */ return SCPE_NXM; *val = uba_map[idx] & UBAMAP_RD; if (DEBUG_PRI (uba_dev, UBA_DEB_MRD)) fprintf (sim_deb, ">>UBA: map %d read, value = %X\n", idx, *val); return SCPE_OK; } switch (ofs) { /* case on offset */ case UBACNF_OF: /* CNF */ *val = (uba_cnf & UBACNF_RD) | UBACNF_CODE; break; case UBACR_OF: /* CR */ *val = uba_cr & UBACR_RD; break; case UBASR_OF: /* SR */ *val = uba_sr & UBASR_RD; break; case UBADR_OF: /* DR */ *val = (uba_dr & UBADR_RD) | UBADR_MICOK | ((uba_cnf & UBADR_CNF_RD) | UBACNF_CODE); break; case UBAFMER_OF: case UBAFMER_OF1: /* FMER */ *val = uba_fmer & UBAFMER_RD; break; case UBAFUBAR_OF: case UBAFUBAR_OF1: /* FUBAR */ *val = uba_fubar & UBAFUBAR_RD; break; case UBABRSVR_OF + 0: case UBABRSVR_OF + 1: /* BRSVR */ case UBABRSVR_OF + 2: case UBABRSVR_OF + 3: idx = ofs - UBABRSVR_OF; *val = uba_svr[idx]; break; case UBABRRVR_OF + 0: case UBABRRVR_OF + 1: /* BRRVR */ case UBABRRVR_OF + 2: case UBABRRVR_OF + 3: idx = ofs - UBABRRVR_OF; uba_rvr[idx] = uba_get_ubvector (idx); *val = uba_rvr[idx]; break; case UBADPR_OF + 0: /* DPR */ *val = 0; /* direct */ break; case UBADPR_OF + 1: case UBADPR_OF + 2: case UBADPR_OF + 3: case UBADPR_OF + 4: case UBADPR_OF + 5: case UBADPR_OF + 6: case UBADPR_OF + 7: case UBADPR_OF + 8: case UBADPR_OF + 9: case UBADPR_OF + 10: case UBADPR_OF + 11: case UBADPR_OF + 12: case UBADPR_OF + 13: case UBADPR_OF + 14: case UBADPR_OF + 15: idx = ofs - UBADPR_OF; *val = uba_dpr[idx] & UBADPR_RD; break; default: return SCPE_NXM; } if (DEBUG_PRI (uba_dev, UBA_DEB_RRD)) fprintf (sim_deb, ">>UBA: reg %d read, value = %X\n", ofs, *val); return SCPE_OK; } /* Write Unibus adapter register */ t_stat uba_wrreg (int32 val, int32 pa, int32 lnt) { int32 idx, ofs, old_cr; if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>UBA: invalid adapter write mask, pa = %X, lnt = %d\r\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } ofs = NEXUS_GETOFS (pa); /* get offset */ if (uba_aiip && (ofs != UBACNF_OF) && (ofs != UBADR_OF) && (ofs != UBACR_OF) && (ofs != UBASR_OF)) return SCPE_NXM; if (ofs >= UBAMAP_OF) { /* map? */ idx = ofs - UBAMAP_OF; if (idx >= UBA_NMAPR) /* valid? */ return SCPE_NXM; uba_map[idx] = val & UBAMAP_WR; if (DEBUG_PRI (uba_dev, UBA_DEB_MWR)) fprintf (sim_deb, ">>UBA: map %d write, value = %X\n", idx, val); return SCPE_OK; } switch (ofs) { /* case on offset */ case UBACNF_OF: /* CNF */ uba_cnf = uba_cnf & ~(val & UBACNF_W1C); /* W1C bits */ uba_adap_clr_int (); /* possible clr int */ break; case UBACR_OF: /* CR */ old_cr = uba_cr; if (val & UBACR_ADINIT) { /* adapter init */ uba_reset (&uba_dev); /* reset adapter */ uba_aiip = 1; /* set init in prog */ uba_ubpdn (uba_aitime); /* power fail UB */ } if ((val & UBACR_UPF) && !(old_cr & UBACR_UPF) /* Unibus power clear */ && !sim_is_active (&uba_unit)) uba_ubpdn (uba_aitime + uba_uitime); /* power fail UB */ uba_cr = (uba_cr & ~UBACR_WR) | (val & UBACR_WR); uba_adap_set_int (uba_cr & ~old_cr); /* possible int set */ uba_adap_clr_int (); /* possible int clr */ break; case UBASR_OF: /* SR */ uba_sr = uba_sr & ~(val & UBASR_W1C); /* W1C bits */ uba_adap_clr_int (); /* possible clr int */ break; case UBADR_OF: /* DR */ uba_dr = (uba_dr & ~UBADR_WR) | (val & UBADR_WR); uba_cnf = uba_cnf & ~(val & UBACNF_W1C); uba_adap_clr_int (); /* possible clr int */ break; case UBABRSVR_OF + 0: case UBABRSVR_OF + 1: /* BRSVR */ case UBABRSVR_OF + 2: case UBABRSVR_OF + 3: idx = ofs - UBABRSVR_OF; uba_svr[idx] = val; break; case UBADPR_OF + 0: /* DPR */ break; /* direct */ case UBADPR_OF + 1: case UBADPR_OF + 2: case UBADPR_OF + 3: case UBADPR_OF + 4: case UBADPR_OF + 5: case UBADPR_OF + 6: case UBADPR_OF + 7: case UBADPR_OF + 8: case UBADPR_OF + 9: case UBADPR_OF + 10: case UBADPR_OF + 11: case UBADPR_OF + 12: case UBADPR_OF + 13: case UBADPR_OF + 14: case UBADPR_OF + 15: idx = ofs - UBADPR_OF; uba_dpr[idx] = uba_dpr[idx] & ~(val & UBADPR_W1C); break; default: return SCPE_NXM; } if (DEBUG_PRI (uba_dev, UBA_DEB_RWR)) fprintf (sim_deb, ">>UBA: reg %d write, value = %X\n", ofs, val); return SCPE_OK; } /* Read and write Unibus I/O space */ int32 ReadUb (uint32 pa) { int32 idx, val; if (ADDR_IS_IOP (pa) && !uba_uiip) { /* iopage,!init */ idx = (pa & IOPAGEMASK) >> 1; if (iodispR[idx]) { iodispR[idx] (&val, pa, READ); return val; } } uba_ub_nxm (pa); /* UB nxm */ return 0; } void WriteUb (uint32 pa, int32 val, int32 mode) { int32 idx; if (ADDR_IS_IOP (pa) && !uba_uiip) { /* iopage,!init */ idx = (pa & IOPAGEMASK) >> 1; if (iodispW[idx]) { iodispW[idx] (val, pa, mode); return; } } uba_ub_nxm (pa); /* UB nxm */ return; } /* ReadIO - read from IO - UBA only responds to byte, aligned word Inputs: pa = physical address lnt = length (BWLQ) Output: longword of data */ int32 ReadIO (uint32 pa, int32 lnt) { uint32 iod; if ((lnt == L_BYTE) || /* byte? */ ((lnt == L_WORD) && ((pa & 1) == 0))) { /* aligned word? */ iod = ReadUb (pa); /* DATI from Unibus */ if (pa & 2) /* position */ iod = iod << 16; } else { printf (">>UBA: invalid read mask, pa = %x, lnt = %d\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ iod = 0; } SET_IRQL; return iod; } /* WriteIO - write to IO - UBA only responds to byte, aligned word Inputs: pa = physical address val = data to write, right justified in 32b longword lnt = length (BWL) Outputs: none */ void WriteIO (uint32 pa, int32 val, int32 lnt) { if (lnt == L_BYTE) /* byte? DATOB */ WriteUb (pa, val, WRITEB); else if ((lnt == L_WORD) && ((pa & 1) == 0)) /* aligned word? */ WriteUb (pa, val, WRITE); /* DATO */ else { printf (">>UBA: invalid write mask, pa = %x, lnt = %d\n", pa, lnt); sbi_set_errcnf (); /* err confirmation */ } SET_IRQL; /* update ints */ return; } /* Update UBA nexus interrupts */ void uba_eval_int (void) { int32 i; for (i = 0; i < (IPL_HMAX - IPL_HMIN); i++) /* clear all UBA req */ nexus_req[i] &= ~(1 << TR_UBA); if (((uba_dr & UBADR_DINTR) == 0) && !uba_uiip && /* intr enabled? */ (uba_cr & UBACR_IFS) && (uba_cr & UBACR_BRIE)) { for (i = 0; i < (IPL_HMAX - IPL_HMIN); i++) { if (int_req[i]) nexus_req[i] |= (1 << TR_UBA); } } if (uba_int) /* adapter int? */ SET_NEXUS_INT (UBA); return; } /* Return vector for Unibus interrupt at relative IPL level [0-3] */ int32 uba_get_ubvector (int32 lvl) { int32 i, vec; vec = 0; if ((lvl == (IPL_UBA - IPL_HMIN)) && uba_int) { /* UBA lvl, int? */ vec = UBA_UVEC; /* set flag */ uba_int = 0; /* clear int */ } if (((uba_dr & UBADR_DINTR) == 0) && !uba_uiip && /* intr enabled? */ (uba_cr & UBACR_IFS) && (uba_cr & UBACR_BRIE)) { for (i = 0; int_req[lvl] && (i < 32); i++) { if ((int_req[lvl] >> i) & 1) { int_req[lvl] = int_req[lvl] & ~(1u << i); if (int_ack[lvl][i]) return (vec | int_ack[lvl][i]()); return (vec | int_vec[lvl][i]); } } } return vec; } /* Unibus I/O buffer routines Map_ReadB - fetch byte buffer from memory Map_ReadW - fetch word buffer from memory Map_WriteB - store byte buffer into memory Map_WriteW - store word buffer into memory */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) { int32 i, j, pbc; uint32 ma, dat; ba = ba & UBADDRMASK; /* mask UB addr */ for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!uba_map_addr (ba + i, &ma)) /* page inv or NXM? */ return (bc - i); pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) fprintf (sim_deb, ">>UBA: 8b read, ma = %X, bc = %X\n", ma, pbc); if ((ma | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ *buf++ = ReadB (ma); } } else { /* yes, do by LW */ for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { dat = ReadL (ma); /* get lw */ *buf++ = dat & BMASK; /* low 8b */ *buf++ = (dat >> 8) & BMASK; /* next 8b */ *buf++ = (dat >> 16) & BMASK; /* next 8b */ *buf++ = (dat >> 24) & BMASK; } } uba_set_dpr (ba + i + pbc - L_BYTE, FALSE); } return 0; } int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) { int32 i, j, pbc; uint32 ma, dat; ba = ba & UBADDRMASK; /* mask UB addr */ bc = bc & ~01; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!uba_map_addr (ba + i, &ma)) /* page inv or NXM? */ return (bc - i); pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) fprintf (sim_deb, ">>UBA: 16b read, ma = %X, bc = %X\n", ma, pbc); if ((ma | pbc) & 1) { /* aligned word? */ for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ if ((i + j) & 1) { /* odd byte? */ *buf = (*buf & BMASK) | (ReadB (ma) << 8); buf++; } else *buf = (*buf & ~BMASK) | ReadB (ma); } } else if ((ma | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; ma = ma + 2, j = j + 2) { /* no, words */ *buf++ = ReadW (ma); /* get word */ } } else { /* yes, do by LW */ for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { dat = ReadL (ma); /* get lw */ *buf++ = dat & WMASK; /* low 16b */ *buf++ = (dat >> 16) & WMASK; /* high 16b */ } } uba_set_dpr (ba + i + pbc - L_WORD, FALSE); } return 0; } int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) { int32 i, j, pbc; uint32 ma, dat; ba = ba & UBADDRMASK; /* mask UB addr */ for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!uba_map_addr (ba + i, &ma)) /* page inv or NXM? */ return (bc - i); pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) fprintf (sim_deb, ">>UBA: 8b write, ma = %X, bc = %X\n", ma, pbc); if ((ma | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ WriteB (ma, *buf); buf++; } } else { /* yes, do by LW */ for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { dat = (uint32) *buf++; /* get low 8b */ dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */ dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */ dat = dat | (((uint32) *buf++) << 24); /* merge hi 8b */ WriteL (ma, dat); /* store lw */ } } uba_set_dpr (ba + i + pbc - L_BYTE, TRUE); } return 0; } int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) { int32 i, j, pbc; uint32 ma, dat; ba = ba & UBADDRMASK; /* mask UB addr */ bc = bc & ~01; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!uba_map_addr (ba + i, &ma)) /* page inv or NXM? */ return (bc - i); pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) fprintf (sim_deb, ">>UBA: 16b write, ma = %X, bc = %X\n", ma, pbc); if ((ma | pbc) & 1) { /* aligned word? */ for (j = 0; j < pbc; ma++, j++) { /* no, bytes */ if ((i + j) & 1) { WriteB (ma, (*buf >> 8) & BMASK); buf++; } else WriteB (ma, *buf & BMASK); } } else if ((ma | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; ma = ma + 2, j = j + 2) { /* no, words */ WriteW (ma, *buf); /* write word */ buf++; } } else { /* yes, do by LW */ for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { dat = (uint32) *buf++; /* get low 16b */ dat = dat | (((uint32) *buf++) << 16); /* merge hi 16b */ WriteL (ma, dat); /* store LW */ } } uba_set_dpr (ba + i + pbc - L_WORD, TRUE); } return 0; } /* Map an address via the translation map */ t_bool uba_map_addr (uint32 ua, uint32 *ma) { uint32 ublk, umap; ublk = ua >> VA_V_VPN; /* Unibus blk */ if ((ublk < UBACR_GETDSB (uba_cr)) || /* map disabled? */ (ublk >= UBA_NMAPR)) /* unimplemented? */ return FALSE; umap = uba_map[ublk]; /* get map */ if (umap & UBAMAP_VLD) { /* valid? */ *ma = ((umap & UBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (ua); if ((umap & UBAMAP_DP) && (umap & UBAMAP_ODD)) /* buffered dp? */ *ma = *ma + 1; /* byte offset? */ return (ADDR_IS_MEM (*ma)); /* legit addr */ } uba_inv_map (ua); /* invalid map */ return FALSE; } /* Map an address via the translation map - console version (no status changes) */ t_bool uba_map_addr_c (uint32 ua, uint32 *ma) { uint32 ublk, umap; ublk = ua >> VA_V_VPN; /* Unibus blk */ if ((ublk < UBACR_GETDSB (uba_cr)) || /* map disabled? */ (ublk >= UBA_NMAPR)) /* unimplemented? */ return FALSE; umap = uba_map[ublk]; /* get map */ if (umap & UBAMAP_VLD) { /* valid? */ *ma = ((umap & UBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (ua); if ((umap & UBAMAP_DP) && (umap & UBAMAP_ODD)) /* buffered dp? */ *ma = *ma + 1; /* byte offset? */ return TRUE; /* legit addr */ } return FALSE; } /* At end of page or transfer, update DPR register, in case next page gets an error */ void uba_set_dpr (uint32 ua, t_bool wr) { uint32 ublk, umap, dpr; ublk = ua >> VA_V_VPN; /* Unibus blk */ if (ublk >= UBA_NMAPR) /* paranoia */ return; umap = uba_map[ublk]; /* get map */ dpr = UBAMAP_GETDP (umap); /* get bdp */ if (dpr) uba_dpr[dpr] = (uba_dpr[dpr] & ~(UBADPR_UA|UBADPR_DIR)) | (wr? UBADPR_DIR: 0) | (((ua >> 2) + ((umap & UBAMAP_ODD)? 1: 0)) & UBADPR_UA); return; } /* Error routines uba_ub_nxm SBI read/write to nx Unibus address uba_inv_map Unibus reference to invalid map reg */ void uba_ub_nxm (int32 ua) { if ((uba_sr & UBASR_UBTMO) == 0) { uba_sr |= UBASR_UBTMO; uba_adap_set_int (uba_cr & UBACR_SUEFIE); uba_fubar = (ua >> 2) & UBAFUBAR_RD; } else uba_sr |= UBASR_LEB; if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) fprintf (sim_deb, ">>UBA: nxm error, ua = %X\n", ua); return; } void uba_inv_map (int32 ublk) { if ((uba_sr & UBASR_IVMR) == 0) { uba_sr |= UBASR_IVMR; uba_adap_set_int (uba_cr & UBACR_USEFIE); uba_fmer = ublk & UBAFMER_RD; } else uba_sr |= UBASR_LEB; if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) fprintf (sim_deb, ">>UBA: inv map error, ublk = %X\n", ublk); return; } /* Unibus power fail routines */ void uba_ubpdn (int32 time) { int32 i; DEVICE *dptr; uba_cnf = (uba_cnf & ~UBACNF_UBIC) | UBACNF_UBPDN; /* update cnf */ sim_activate (&uba_unit, time); /* schedule */ uba_uiip = 1; /* UB init in prog */ for (i = 0; sim_devices[i] != NULL; i++) { /* reset Unibus */ dptr = sim_devices[i]; if (dptr->reset && (dptr->flags & DEV_UBUS)) dptr->reset (dptr); } return; } /* Init timeout service routine */ t_stat uba_svc (UNIT *uptr) { if (uba_aiip) { /* adapter init? */ uba_aiip = 0; /* clear in prog */ sim_activate (uptr, uba_uitime); /* schedule UB */ } else { uba_uiip = 0; /* no, UB */ uba_cnf = (uba_cnf & ~UBACNF_UBPDN) | UBACNF_UBIC; uba_adap_set_int (uba_cr & UBACR_CNFIE); /* possible int */ } return SCPE_OK; } /* Interrupt routines */ void uba_adap_set_int (int32 flg) { if (((flg & UBACR_SUEFIE) && (uba_sr & UBA_SUEFIE_SR)) || ((flg & UBACR_USEFIE) && (uba_sr & UBA_USEFIE_SR)) || ((flg & UBACR_CNFIE) && (uba_cr & UBA_CNFIE_CR))) { uba_int = 1; if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) fprintf (sim_deb, ">>UBA: adapter int req, sr = %X, cr = %X\n", uba_sr, uba_cr); } return; } void uba_adap_clr_int () { if ((!(uba_cr & UBACR_SUEFIE) || !(uba_sr & UBA_SUEFIE_SR)) && (!(uba_cr & UBACR_USEFIE) || !(uba_sr & UBA_USEFIE_SR)) && (!(uba_cr & UBACR_CNFIE) || !(uba_cr & UBA_CNFIE_CR))) uba_int = 0; return; } /* Reset Unibus adapter */ t_stat uba_reset (DEVICE *dptr) { int32 i; uba_int = 0; uba_aiip = uba_uiip = 0; sim_cancel (&uba_unit); for (i = 0; i < IPL_HLVL; i++) { nexus_req[i] &= ~(1 << TR_UBA); int_req[i] = 0; uba_svr[i] = 0; uba_rvr[i] = 0; } for (i = 0; i < UBA_NMAPR; i++) uba_map[i] = 0; for (i = 0; i < UBA_NDPATH; i++) uba_dpr[i] = 0; uba_sr = 0; uba_cr = 0; uba_dr = 0; uba_cnf = UBACNF_UBIC; return SCPE_OK; } /* Memory examine via map (word only) */ t_stat uba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { uint32 ua = (uint32) exta, pa; if ((vptr == NULL) || (ua >= UBADDRSIZE)) return SCPE_ARG; if (uba_map_addr_c (ua, &pa) && ADDR_IS_MEM (pa)) { *vptr = (uint32) ReadW (pa); return SCPE_OK; } return SCPE_NXM; } /* Memory deposit via map (word only) */ t_stat uba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { uint32 ua = (uint32) exta, pa; if (ua >= UBADDRSIZE) return SCPE_ARG; if (uba_map_addr_c (ua, &pa) && ADDR_IS_MEM (pa)) { WriteW (pa, (int32) val); return SCPE_OK; } return SCPE_NXM; } /* Show UBA virtual address */ t_stat uba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) { t_stat r; char *cptr = (char *) desc; uint32 ua, pa; if (cptr) { ua = (uint32) get_uint (cptr, 16, UBADDRSIZE - 1, &r); if (r == SCPE_OK) { if (uba_map_addr_c (ua, &pa)) fprintf (of, "Unibus %-X = physical %-X\n", ua, pa); else fprintf (of, "Unibus %-X: invalid mapping\n", ua); return SCPE_OK; } } fprintf (of, "Invalid argument\n"); return SCPE_OK; } simh-3.8.1/VAX/ka655_patch.com0000644000175000017500000004035410774226462014031 0ustar vlmvlm$! $! This procedure patches the base KA655.BIN Boot ROM image to work under $! the SIMH simulator $ $! The second part of the patch adds support for Extended Memory in the $! simulator. A simulated system can have up to 512MB of RAM. $! $ PATCH /ABSOLUTE /NEW_VERSION /OUTPUT=cp$exe:KA655.BIN cp$src:ka655_orig.BIN ! CVAX Bootstrap Notes ! ! [2004c87e] - de$read_script ! [2004c916] - launch next test, r2 = test #, r4 = header, r5 = offset to main routine ! ! Script BA ! --------- ! ! Test 9D - Utility - ok ! Test 42 - Wait for interrupt - ok ! Test C6 - SSC register check - ok ! Test 60 [2004de37] - Serial line - requires diagnostic loopback mode and ! break detection - bypass ! 2004de99/ brw 2004e0a7 Replace/Instruction 0DE99 = 'MOVB #03,B^76(R9)' 'BRW 0000E0A7' Exit ! Test 62 - QDSS disable - ok ! ! Script BC ! --------- ! ! 40. Test 91 - CQBIC init check - ok ! 39. Test 90 [2004d748] - CQBIC registers - ok ! 38. Test 33 [2004d54e] - CMCTL init check - ok ! 37. Test 32 [2004d5b0] - CMCTL registers - ok ! 36. Test 31 [200512d0] - CMCTL CSR setup to map memory - ok ! 35. Test 49 [20052a4b] - CMCTL FDM test - requires fast diagnostic mode ! and XMX counters - bypass ! 20052a55:20052a58/ nop Delete/Instruction 12A55 = 'BBC #26,(R9),00012A5C' ! 34. Test 30 [20051909] - init map - ok ! ! Script BD ! --------- ! ! 33. Test 52 [2004e656] - prog timer 0 - ok ! 32. Test 52 [2004e656] - prog timer 1 - ok ! 31. Test 53 [2004e918] - toy clock - ok ! 30. Test C1 [2004f8f1] - SSC RAM - ok ! 29. Test 34 [2004d4a0] - ROM - checksum off due to other patches - patch ! 2004d52c:2004d52d/ nop Delete/Instruction 0D52C = 'BNEQ 0000D531' ! 2004D52C ! 28. Test C5 [2004fc0e] - SSC registers - ok ! 27. Test 55 [2004ea8c] - interval timer - ok ! 26. Test 51 [2004e42d] - CFPA - ok ! 25. Test C7 [2004D3D3] - CBTCR<31:30> - ok ! 24. Test 46 [2004ef1a] - L1 cache diagnostic mode - bypass ! 2004ef80/ brw 2004f47a Replace/Instruction 0EF80 = 'MOVB #06,B^76(R9)' 'BRW 0000F47A' ! 2004FE80 Exit ! 23. Test 35 [20050554] - L2 cache integrity test - bypass ! 20050554/ brw 20050a48 Replace/Instruction 10554 = 'INSV #00,#10,#02,(R9)' 'BRW 00010A48' ! 20050554 Exit ! 22. Test 43 [20050d65] - L1 with L2 test - bypass ! 20050d65/ brw 20050fca Replace/Instruction 10D65 = 'MOVAL B^00010D65,W^0080(R9)' 'BRW 00010FCA' ! 20050D65 Exit ! 21. (Rerun of C2) ! 20. Test 4F [20051d4f] - memory data - bypass, run from ROM ! 20055205/ 0 Replace/Byte 15205 = 3 0 ! 20055205 Exit ! 20051d4f/ brw 2005163a Replace/Instruction 11D4F = 'MOVAL B^00011D4F,W^0080(R9)' 'BRW 0001163A' ! 20051D4F Exit ! 19. Test 4E [20051edb] - memory byte write - ok, run from ROM ! 2005521c/ 0 Replace/Byte 1521C = 3 0 ! 2005521C Exit ! 18. Test 4D [20051ff3] - memory addressing - ok, run from ROM ! 20055233/ 0 Replace/Byte 15233 = 3 0 ! 20055233 Exit ! 17. Test 4C [20052190] - ECC test - bypass, run from ROM ! 2005524a/ 0 Replace/Byte 1524A = 3 0 ! 2005524A Exit ! 20052190/ brw 2005163a Replace/Instruction 12190 = 'MOVAL B^00012190,W^0080(R9)' 'BRW 0001163A' ! 20052190 Exit ! 16. Test 4B [2005264e] - masked writes with errors - bypass, run from ROM ! 20055261/ 0 Replace/Byte 15261 = 3 0 ! 20055261 Exit ! 2005264e/ brw 2005163a Replace/Instruction 1264E = 'MOVAL B^0001264E,W^0080(R9)' 'BRW 0001163A' ! 2005264E Exit ! 15. Test 4A [20052823] - single bit correction - bypass, run from ROM ! 20055278/ 0 Replace/Byte 15278 = 3 0 ! 20055278 Exit ! 20052823/ brw 2005163a Replace/Instruction 12823 = 'MOVAL B^00012823,W^0080(R9)' 'BRW 0001163A' ! 20052823 Exit ! 14. Test 48 [20053062] - memory address shorts - bypass, run from ROM ! 2005528f/ 0 Replace/Byte 1528F = 3 0 ! 2005528F Exit ! 20053062/ brw 2005163a Replace/Instruction 13062 = 'MOVAL B^00013062,W^0080(R9)' 'BRW 0001163A' ! 20053062 Exit ! 13. Test 47 [200536c3] - verify refresh - run from ROM ! 200552aa/ 0 Replace/Byte 152AA = 3 0 ! 200552AA Exit ! 12. Test 41 [] - count bad pages, relocate bit map ! 11. Test 44 [20050d34] - L1 with memory - bypass ! 20050d34/ brw 20050fca Replace/Instruction 10D34 = 'MOVAL B^00010D34,W^0080(R9)' 'BRW 00010FCA' ! 20050D34 Exit ! 10. Test 36 [2004ffdf] - L2 with memory - bypass ! 2004ffdf/ brw 20050428 Replace/Instruction 0FFDF = 'JSB L^0000CEFD' 'BRW 00010428' ! 2004FFDF Exit ! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM ! 2004dbc0/ brw 2004dd8a Replace/Instruction 0DBC0 = 'MOVB #1B,B^76(R9)' 'BRW 0000DD8A' ! 2004DBC0 Exit ! 200552f6/ 0 Replace/Byte 152F6 = 3 0 ! 200552F6 Exit ! 8. Test 54 [] - virtual mode - ok ! 7. Test 34 [] - ROM in virtual mode - see previous notes ! 6. Test C5 [] - SSC registers in virtual mode - ok ! 5. Test 45 [2004ec5d] - cache, memory, CQBIC - bypass ! 2004ec5d/ brw 2004ee90 Replace/Instruction 0EC5D = 'BICL2 #03,B^28(R9)' 'BRW 0000EE90' ! 2004EC5D Exit ! 4. Test 5A [2004eb5f] - CVAX CMCTL DAL drivers - ok ! 3. Test 41 [20051096] - reset board ! ! =========================================================================== ! ! ! All of the above patches were done against the base ROM image extracted ! from a genuine MicroVAX 3900. These were all part of SIMH prior to ! extended memory support. ! ! The Diagnostic State Variable DST$W_BITMAP_LENGTH, being 16 bits, can only ! describe a PFN Bitmap describing up to, but NOT including 256MB of RAM. To ! get to 256MB and beyond, we must correctly determine a correct bitmap size. ! all of the Diagnostic state space is in use, either since it is already ! defined, and the space beyond that is used for stack. So, we change the ! references to DST$W_BITMAP_LENGTH to call a subroutine to properly determine ! the PFN BITMAP size. ! ! Most of the code which references DST$W_BITMAP_LENGTH are done from a ! diagnostic test routine which may be relocated to RAM before execution. ! The assumption of such relocating activity is that none of the relocated code ! references any other instructions or data in the ROM image via any PC ! relative addressing modes. Given this requirement, each of the above ! patches must be done with a JSB to an explicit ROM address. As a ! consequence, the patched instruction will generally be longer than the ! instruction which is being replaced. To cleanly affect this ! we must overwrite multiple instructions and incorporate the activities of ! each of the overwritten instructions into the target subroutine. ! Additionally, even without the relocation concerns, numerous places which ! reference these extra routines may be beyond a PC relative displacement ! due to the size of the ROM. ! ! The KA655 ROM size is 128KB. As it turns out, the ROM image is only using ! approximately 105,136 bytes of the total 131072 bytes. We use this unused ! ROM space to store code which implements the required extra functionality ! Define PATCH_BASE = 00019C00 Define P$END = PATCH_BASE Define CP$K_MEMSIZE = 20080148 Define CP$K_QBMBR = 20080010 Define DST_BASE = 20140758 Define CTX_BASE = 201404B2 Define CTX$L_R2 = 8 Define CTX$A_GPTR = 66 Define CTX$L_ERROR_COUNT = 54 Define CTX$L_ERROR_STATUS = 58 Define DST$W_BITMAP_LENGTH = 20 Define DST$A_BITMAP = 1C Define DST$A_BUSMAP = 24 Define DST$W_BITMAP_CHECKSUM = 22 Define CP$CHECKSUM_RTN = 20041C6A Define GUARD$S_GUARD_BLOCK = 12 Define GUARD$l_pc = 0 Define GUARD$a_gptr = 4 Define GUARD$w_rmask = 8 Define GUARD$l_save_error_count = 0A Define GUARD$l_save_error_status = 0E ! ! This routine determines the memory size of the current system. This is ! done by referencing the CMCTL18 memory size register. On an older simulator ! or one with less than 64MB of RAM configured, this register may not exist. ! If it doesn't exist the machine check generated by the reference to the ! non-existant register is caught, and the appropriate memory size is ! determined from the existing PFN Bitmap size. ! DEFINE GETMEMSIZE_R0 = P$End+1 Deposit/Instruction GETMEMSIZE_R0 ' pushr #0 ' ' subl2 #guard$s_guard_block, sp' ' movw #0, B^guard$w_rmask (sp)' ' movab B^G$ERR, B^guard$l_pc (sp)' ' movl @#, B^guard$a_gptr (sp)' ' movl @#, B^guard$l_save_error_count (sp)' ' movl @#, B^guard$l_save_error_status (sp)' ' movl sp, @#' ' brb G$RD ' 'G$ERR: movzwl @#,R0' ' clrl @#' ' clrl @#' ' ashl #^d12,r0,r0 ' ' brb G$DON ' 'G$RD: movl @#CP$K_MEMSIZE,R0 ' 'G$DON: movl @#, sp' ' movl B^guard$a_gptr (sp), @#' ' movl B^guard$l_save_error_count (sp), @#' ' movl B^guard$l_save_error_status (sp), @#' ' addl2 #guard$s_guard_block - 2, sp' ' popr (sp)+ ' 'P$End: rsb ' Exit ! Define GETMAPENDADDR_R6 = P$End+1 Deposit/Instruction GETMAPENDADDR_R6 ' MOVZWL @#,R6' ' BNEQ X$1 ' ' MOVL R0, -(SP) ' ' JSB GETMEMSIZE_R0 ' ' ashl #-^D12,R0,R6 ' ' MOVL (SP)+, R0 ' 'X$1: addl @#,R6' 'P$End: rsb ' Exit ! DE_QDSS_ANY [2004E2A8] Uses R6 for BitMap Size ! 2004E390/ BSBW GETMAPSIZE_R6 Replace/Instruction 0E390 ' MOVZWL B^20(R9),R6 ' ' ADDL3 R6,B^1C(R9),R6 ' Exit ' JSB GETMAPENDADDR_R6 ' Exit ! ! DEFINE GETMAPSIZEMAPADDR_STACK = P$End+1 Deposit/Instruction GETMAPSIZEMAPADDR_STACK ' PUSHL @#,' ' MOVL B^4(SP),-(SP) ' ' MOVZWL @#,B^8(SP)' ' BNEQ X$2' ' MOVL R0, -(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D12,R0,B^0C(SP) ' ' MOVL (SP)+, R0 ' 'X$2: NOP ' 'P$END: RSB' Exit ! CP_FIND [200419E8] Uses (SP) for BitMap Size R1 Scratch ! 20041A16/ BSBW GETMAPSIZE_STACK Replace/Instruction 01A16 ' MOVZWL B^20(R0),-(SP) ' ' PUSHL B^1C(R0) ' Exit ' JSB GETMAPSIZEMAPADDR_STACK ' Exit ! ! CP_SCAN [200459D0] Uses R3 for BitMap Size DEFINE GETMBMEMSIZE_STACK = P$End+1 Deposit/Instruction GETMBMEMSIZE_STACK ' MOVAB L^0000AACF,-(SP) ' ' MOVL B^4(SP),-(SP) ' ' MOVL R0, -(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D20,R0,B^0C(SP) ' ' MOVL (SP)+, R0 ' ' RSB ' 'GETMAPSIZE_R3: MOVZWL @#,R3' ' BNEQ X$3' ' MOVL R0, -(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D12,R0,R3 ' ' MOVL (SP)+, R0 ' 'X$3: RSB' 'P$END: NOP' Exit ! 20045B05/ BSBW GETMBMEMSIZE_STACK Replace/Instruction 05B05 ' MOVL R8,-(SP) ' ' MOVAB L^0000AACF,-(SP) ' Exit ' JSB GETMBMEMSIZE_STACK ' Exit ! 20045B80/ BSBW GETMAPSIZE_R3 Replace/Instruction 05B80 ' MOVZWL @#20140778,R3 ' Exit ' JSB GETMAPSIZE_R3 ' Exit ! DE_CQBIC_MEMORY [2004D7B2] DEFINE GETBITMAPPAGES_R5 = P$End+1 Deposit/Instruction GETBITMAPPAGES_R5 ' MOVZWL @#,R5' ' BNEQ X$4 ' ' MOVL R0,-(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D12,R0,R5 ' ' MOVL (SP)+,R0 ' 'X$4: ASHL #3,R5,R5 ' ' RSB ' 'GETBITMAPMEMSIZE_R3: MOVZWL @#,R3' ' BNEQ X$5 ' ' MOVL R0,-(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D12,R0,R3 ' ' MOVL (SP)+,R0 ' 'X$5: ASHL #^D12,R3,R3 ' 'P$END: RSB' Exit ! 2004D8A5/ BSBW GETMAPSIZE_R5 Replace/Instruction 0D8A5 ' MOVZWL B^20(R9),R5 ' ' ASHL #03,R5,R5 ' Exit ' JSB GETBITMAPPAGES_R5 ' Exit ! 2004DA41/ BSBW GETMAPSIZE_R5 Replace/Instruction 0DA41 ' MOVZWL B^20(R9),R5 ' ' ASHL #03,R5,R5 ' Exit ' JSB GETBITMAPPAGES_R5 ' Exit ! 2004DA8C/ BSBW GETMAPSIZE_R5 Replace/Instruction 0DA8C ' MOVZWL B^20(R9),R5 ' ' ASHL #03,R5,R5 ' Exit ' JSB GETBITMAPPAGES_R5 ' Exit ! DE_CACHE_MEM_CQBIC [2004EBF0] ! 2004ECD0/ BSBW GETMAPSIZE_R3 Replace/Instruction 0ECD0 ' MOVZWL B^20(R9),R3 ' ' ASHL #0C,R3,R3 ' Exit ' JSB GETBITMAPMEMSIZE_R3 ' Exit ! CP_BOOTSTRAP DEFINE GET_X_PFNMAP_SIZEADDR_STACK = P$End+1 Deposit/Instruction GET_X_PFNMAP_SIZEADDR_STACK ' movl B^dst$a_bitmap (r11), r2' ' movzwl B^dst$w_bitmap_length (r11), r1' ' bneq X$20 ' ! Zero Bitmap size means extended mem ' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 'X$10: brw X$70 ' ! already fixed 'X$20: cmpl r1, #^D16384 ' ! Original Map of 64MB? ' blss X$10 ' ! Too Small For Extended ' JSB GETMEMSIZE_R0 ' ' ashl #-^D12, R0, r1 ' ! Map Size = MEMSIZE/512/8 ' cmpl r1, #^D16384 ' ' beql X$10 ' ! Normal 64MB map !; !; First move the Console Scratch Area (16KB), and the Qbus Map (32KB) !; to the end of physical memory. !; ' movl @#CP$K_MEMSIZE, r1 ' ! MEMSIZE ' subl3 #^D48*^D1024, r1, r3 ' ! New Destination Address ' addl #^D16384, r2 ' ! Point at end of prior Map ' clrl r0 ' ! Index 'X$63: movb (r2)[r0], (r3)[r0] ' ! Move the Console Scratch Pad and QBMRs ' aoblss #^D48*^D1024, r0, X$63 ' ' movab W^4000(r3), B^DST$A_BUSMAP(r11)' ! Save Qbus Map Register Space ' movab W^4000(r3), @#CP$K_QBMBR' ! Save Qbus Map Register Space !; !; Fixup the boot device descriptor previously saved in the scratchpad RAM !; ' subl3 #^D512, B^DST$A_BUSMAP (r11), r1' ' movab B^8(r1), B^4(r1)' !; !; Now we build a new bitmap, with all bits set except for the reserved !; area containing the bitmap itself, and the console scratch area and !; the Qbus Map. !; ' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 ' subl3 r1, r3, r2 ' ! Point at new Destination Address ' movl r2, B^dst$a_bitmap (r11)' ! Save Bitmap address ' ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 ' ashl #-^D12, @#CP$K_MEMSIZE, r0' ! Map Size = MEMSIZE/512/8 ' addl #^D48*^D1024+^D511, r0 ' ! Plus other reserved page size ' ashl #-9, r0, r0 ' ' subl r0, r1 ' ! Adjust for bitmap of reserved pages ' clrl r0 ' ' pushl r1 ' ! Save total Bit Count ' ashl #-5, r1, r1 ' ! Convert limit to Longword Count 'X$632: movl #-1,(r2)[r0] ' ! Set bitmap entry for 32 pages ' aoblss r1, r0, X$632 ' ' ashl #5, r0, r0 ' ! Convert back to Bit Count ' movl (SP)+, r1 ' ! Restore total Bit Count ' cmpl r0, r1' ' bgeq X$651' 'X$64: bbss r0, (r2), X$65 ' ! Set page bitmap entry 'X$65: aoblss r1, r0, X$64 ' ! Done ? 'X$651: ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 'X$66: bbcc r0, (r2), X$67 ' ! Clear reserved page bitmap entry 'X$67: aoblss r1, r0, X$66 ' ! Done? ' clrl r0 ' ! Zero Initial checksum value ' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 ' jsb @#cp$checksum_rtn ' ! Compute checksum for revised bitmap ' movw r0, B^dst$w_bitmap_checksum (r11)' ! save it ' clrw B^dst$w_bitmap_length (r11)' ! Mark as extended bitmap ' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 ' movl B^dst$a_bitmap (r11), r2' 'X$70: jmp GETMAPSIZEMAPADDR_STACK' ! 'GETMAPSIZE_CTX_R2: movzwl @#,@#' ' bneq X$71' ' MOVL R0, -(SP) ' ' JSB GETMEMSIZE_R0 ' ' ASHL #-^D12,R0,@#' ' MOVL (SP)+, R0 ' 'X$71: rsb' Exit Replace/Instruction 517F = 'movzwl B^20(r11), @#201404BA' ' jsb GETMAPSIZE_CTX_R2 ' Exit Replace/Instruction 514B ' MOVZWL B^20(R11),-(SP) ' ' PUSHL B^1C(R11) ' Exit ' JSB GET_X_PFNMAP_SIZEADDR_STACK' Exit ! ! DE_MEMORY [200512AC] ! CP_UTIL [] ! ! Identify the Extended Memory Mode in the Console Banner ! (i.e. KA655X-B vs KA655-B) ! Replace 83D8 = 00303436 58353536 Exit Replace/Byte 83DC = 4B 0 Exit Deposit/Instruction 1C04 ' PUSHAB L^000083E2 ' ' JSB GETMEMSIZE_R0 ' ' CMPL R0, #<^D64*^D1024*^D1024>' ' BLEQ B$1 ' ' MOVAB L^000083D6,(SP) ' 'B$1: NOP ' ' NOP ' ' NOP ' ' NOP ' ' NOP ' ' NOP ' ' NOP ' ' NOP ' ' NOP ' Exit ! ! Extended Memory Patches: ! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM ! MP Revised to bypass tests starting at test of NXM reference through MAP ! 2004db2e/ brw 2004dd8a Replace/Instruction 0db2e = 'MOVB #17,B^76(R9)' 'BRW 0DD8A' EXIT ! ! Interval Timer Patch ! In operational environments, Test 82 subtests 17 and 20 have been observed ! to ocaisionally fail. Disable the timer portion of the interval timer tests. ! 2004e7c1/ brw 2004e870 Replace/Instruction 0e7c1 = 'MOVB #10,B^76(R9)' 'BRW 0e870' EXIT ! UPDATE EXIT $ simh-3.8.1/VAX/vax780_defs.h0000644000175000017500000004661111111666660013522 0ustar vlmvlm/* vax780_defs.h: VAX 780 model-specific definitions file Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 19-Nov-08 RMS Moved I/O support routines to I/O library 29-Apr-07 RMS Modified model-specific reserved operand check macros to reflect 780 microcode patches (found by Naoki Hamada) 29-Oct-06 RMS Added clock coscheduler function 17-May-06 RMS Added CR11/CD11 support (from John Dundas) 10-May-06 RMS Added model-specific reserved operand check macros This file covers the VAX 11/780, the first VAX. System memory map 0000 0000 - 1FFF FFFF main memory 2000 0000 - 2001 FFFF nexus register space 2002 0000 - 200F FFFF reserved 2010 0000 - 2013 FFFF Unibus address space, Unibus 0 2014 0000 - 2017 FFFF Unibus address space, Unibus 1 2018 0000 - 201B FFFF Unibus address space, Unibus 2 201C 0000 - 201F FFFF Unibus address space, Unibus 3 2020 0000 - 3FFF FFFF reserved */ #ifndef FULL_VAX #define FULL_VAX 1 #endif #ifndef _VAX_780_DEFS_H_ #define _VAX_780_DEFS_H_ 1 /* Microcode constructs */ #define VAX780_SID (1 << 24) /* system ID */ #define VAX780_ECO (7 << 19) /* ucode revision */ #define VAX780_PLANT (0 << 12) /* plant (Salem NH) */ #define VAX780_SN (1234) #define CON_HLTPIN 0x0200 /* external CPU halt */ #define CON_HLTINS 0x0600 /* HALT instruction */ #define MCHK_RD_F 0x00 /* read fault */ #define MCHK_RD_A 0xF4 /* read abort */ #define MCHK_IBUF 0x0D /* read istream */ #define VER_FPLA 0x0C /* FPLA version */ #define VER_WCSP (VER_FPLA) /* WCS primary version */ #define VER_WCSS 0x12 /* WCS secondary version */ #define VER_PCS ((VER_WCSS >> 4) & 0x3) /* PCS version */ /* Interrupts */ #define IPL_HMAX 0x17 /* highest hwre level */ #define IPL_HMIN 0x14 /* lowest hwre level */ #define IPL_HLVL (IPL_HMAX - IPL_HMIN + 1) /* # hardware levels */ #define IPL_SMAX 0xF /* highest swre level */ /* Nexus constants */ #define NEXUS_NUM 16 /* number of nexus */ #define MCTL_NUM 2 /* number of mem ctrl */ #define MBA_NUM 2 /* number of MBA's */ #define TR_MCTL0 1 /* nexus assignments */ #define TR_MCTL1 2 #define TR_UBA 3 #define TR_MBA0 8 #define TR_MBA1 9 #define NEXUS_HLVL (IPL_HMAX - IPL_HMIN + 1) #define SCB_NEXUS 0x100 /* nexus intr base */ #define SBI_FAULTS 0xFC000000 /* SBI fault flags */ /* Internal I/O interrupts - relative except for clock and console */ #define IPL_CLKINT 0x18 /* clock IPL */ #define IPL_TTINT 0x14 /* console IPL */ #define IPL_MCTL0 (0x15 - IPL_HMIN) #define IPL_MCTL1 (0x15 - IPL_HMIN) #define IPL_UBA (0x15 - IPL_HMIN) #define IPL_MBA0 (0x15 - IPL_HMIN) #define IPL_MBA1 (0x15 - IPL_HMIN) /* Nexus interrupt macros */ #define SET_NEXUS_INT(dv) nexus_req[IPL_##dv] |= (1 << TR_##dv) #define CLR_NEXUS_INT(dv) nexus_req[IPL_##dv] &= ~(1 << TR_##dv) /* Machine specific IPRs */ #define MT_ACCS 40 /* FPA control */ #define MT_ACCR 41 /* FPA maint */ #define MT_WCSA 44 /* WCS address */ #define MT_WCSD 45 /* WCS data */ #define MT_SBIFS 48 /* SBI fault status */ #define MT_SBIS 49 /* SBI silo */ #define MT_SBISC 50 /* SBI silo comparator */ #define MT_SBIMT 51 /* SBI maint */ #define MT_SBIER 52 /* SBI error */ #define MT_SBITA 53 /* SBI timeout addr */ #define MT_SBIQC 54 /* SBI timeout clear */ #define MT_MBRK 60 /* microbreak */ /* Machine specific reserved operand tests */ /* 780 microcode patch 37 - only test LR<23:0> for appropriate length */ #define ML_LR_TEST(r) if ((uint32)((r) & 0xFFFFFF) > 0x200000) RSVD_OPND_FAULT /* 780 microcode patch 38 - only test PxBR<31>=1 and xBR<1:0> = 0 */ #define ML_PXBR_TEST(r) if ((((r) & 0x80000000) == 0) || \ ((r) & 0x00000003)) RSVD_OPND_FAULT #define ML_SBR_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT /* 780 microcode patch 78 - only test xCBB<1:0> = 0 */ #define ML_PA_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT #define LP_AST_TEST(r) if ((r) > AST_MAX) RSVD_OPND_FAULT #define LP_MBZ84_TEST(r) if ((r) & 0xF8C00000) RSVD_OPND_FAULT #define LP_MBZ92_TEST(r) if ((r) & 0x7FC00000) RSVD_OPND_FAULT /* Memory */ #define MAXMEMWIDTH 23 /* max mem, MS780C */ #define MAXMEMSIZE (1 << MAXMEMWIDTH) #define MAXMEMWIDTH_X 27 /* max mem, MS780E */ #define MAXMEMSIZE_X (1 << MAXMEMWIDTH_X) #define INITMEMSIZE (1 << MAXMEMWIDTH) /* initial memory size */ #define MEMSIZE (cpu_unit.capac) #define ADDR_IS_MEM(x) (((uint32) (x)) < MEMSIZE) /* Unibus I/O registers */ #define UBADDRWIDTH 18 /* Unibus addr width */ #define UBADDRSIZE (1u << UBADDRWIDTH) /* Unibus addr length */ #define UBADDRMASK (UBADDRSIZE - 1) /* Unibus addr mask */ #define IOPAGEAWIDTH 13 /* IO addr width */ #define IOPAGESIZE (1u << IOPAGEAWIDTH) /* IO page length */ #define IOPAGEMASK (IOPAGESIZE - 1) /* IO addr mask */ #define UBADDRBASE 0x20100000 /* Unibus addr base */ #define IOPAGEBASE 0x2013E000 /* IO page base */ #define ADDR_IS_IO(x) ((((uint32) (x)) >= UBADDRBASE) && \ (((uint32) (x)) < (UBADDRBASE + UBADDRSIZE))) #define ADDR_IS_IOP(x) (((uint32) (x)) >= IOPAGEBASE) /* Nexus register space */ #define REGAWIDTH 17 /* REG addr width */ #define REG_V_NEXUS 13 /* nexus number */ #define REG_M_NEXUS 0xF #define REG_V_OFS 2 /* register number */ #define REG_M_OFS 0x7FF #define REGSIZE (1u << REGAWIDTH) /* REG length */ #define REGBASE 0x20000000 /* REG addr base */ #define ADDR_IS_REG(x) ((((uint32) (x)) >= REGBASE) && \ (((uint32) (x)) < (REGBASE + REGSIZE))) #define NEXUS_GETNEX(x) (((x) >> REG_V_NEXUS) & REG_M_NEXUS) #define NEXUS_GETOFS(x) (((x) >> REG_V_OFS) & REG_M_OFS) /* ROM address space in memory controllers */ #define ROMAWIDTH 12 /* ROM addr width */ #define ROMSIZE (1u << ROMAWIDTH) /* ROM size */ #define ROM0BASE (REGBASE + (TR_MCTL0 << REG_V_NEXUS) + 0x1000) #define ROM1BASE (REGBASE + (TR_MCTL1 << REG_V_NEXUS) + 0x1000) #define ADDR_IS_ROM0(x) ((((uint32) (x)) >= ROM0BASE) && \ (((uint32) (x)) < (ROM0BASE + ROMSIZE))) #define ADDR_IS_ROM1(x) ((((uint32) (x)) >= ROM1BASE) && \ (((uint32) (x)) < (ROM1BASE + ROMSIZE))) #define ADDR_IS_ROM(x) (ADDR_IS_ROM0 (x) || ADDR_IS_ROM1 (x)) /* Other address spaces */ #define ADDR_IS_CDG(x) (0) #define ADDR_IS_NVR(x) (0) /* Unibus I/O modes */ #define READ 0 /* PDP-11 compatibility */ #define WRITE (L_WORD) #define WRITEB (L_BYTE) /* Common CSI flags */ #define CSR_V_GO 0 /* go */ #define CSR_V_IE 6 /* interrupt enable */ #define CSR_V_DONE 7 /* done */ #define CSR_V_BUSY 11 /* busy */ #define CSR_V_ERR 15 /* error */ #define CSR_GO (1u << CSR_V_GO) #define CSR_IE (1u << CSR_V_IE) #define CSR_DONE (1u << CSR_V_DONE) #define CSR_BUSY (1u << CSR_V_BUSY) #define CSR_ERR (1u << CSR_V_ERR) /* Timers */ #define TMR_CLK 0 /* 100Hz clock */ /* I/O system definitions */ #define DZ_MUXES 4 /* max # of DZV muxes */ #define DZ_LINES 8 /* lines per DZV mux */ #define VH_MUXES 4 /* max # of DHQ muxes */ #define DLX_LINES 16 /* max # of KL11/DL11's */ #define DCX_LINES 16 /* max # of DC11's */ #define MT_MAXFR (1 << 16) /* magtape max rec */ #define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ #define DEV_V_MBUS (DEV_V_UF + 1) /* Massbus */ #define DEV_V_NEXUS (DEV_V_UF + 2) /* Nexus */ #define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ #define DEV_V_FFUF (DEV_V_UF + 4) /* first free flag */ #define DEV_UBUS (1u << DEV_V_UBUS) #define DEV_MBUS (1u << DEV_V_MBUS) #define DEV_NEXUS (1u << DEV_V_NEXUS) #define DEV_FLTA (1u << DEV_V_FLTA) #define DEV_QBUS (0) #define DEV_Q18 (0) #define UNIBUS TRUE /* Unibus only */ #define DEV_RDX 16 /* default device radix */ /* Device information block For Massbus devices, ba = Massbus number lnt = Massbus ctrl type ack[0] = abort routine For Nexus devices, ba = Nexus number lnt = number of consecutive nexi */ #define VEC_DEVMAX 4 /* max device vec */ typedef struct { uint32 ba; /* base addr */ uint32 lnt; /* length */ t_stat (*rd)(int32 *dat, int32 ad, int32 md); t_stat (*wr)(int32 dat, int32 ad, int32 md); int32 vnum; /* vectors: number */ int32 vloc; /* locator */ int32 vec; /* value */ int32 (*ack[VEC_DEVMAX])(void); /* ack routine */ } DIB; /* Unibus I/O page layout - XUB,RQB,RQC,RQD float based on number of DZ's Massbus devices (RP, TU) do not appear in the Unibus IO page */ #define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ #define IOLN_DZ 010 #define IOBA_XUB (IOPAGEBASE + 000330 + (020 * (DZ_MUXES / 2))) #define IOLN_XUB 010 #define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) #define IOLN_RQB 004 #define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) #define IOLN_RQC 004 #define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) #define IOLN_RQD 004 #define IOBA_RQ (IOPAGEBASE + 012150) /* UDA50 */ #define IOLN_RQ 004 #define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ #define IOLN_TS 004 #define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ #define IOLN_RL 012 #define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ #define IOLN_XQ 020 #define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ #define IOLN_XQB 020 #define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ #define IOLN_TQ 004 #define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ #define IOLN_XU 010 #define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ #define IOLN_CR 010 #define IOBA_RX (IOPAGEBASE + 017170) /* RX11 */ #define IOLN_RX 004 #define IOBA_RY (IOPAGEBASE + 017170) /* RXV21 */ #define IOLN_RY 004 #define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ #define IOLN_QDSS 002 #define IOBA_HK (IOPAGEBASE + 017440) /* RK611 */ #define IOLN_HK 040 #define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ #define IOLN_LPT 004 #define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ #define IOLN_PTR 004 #define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ #define IOLN_PTP 004 /* Interrupt assignments; within each level, priority is right to left */ #define INT_V_DZRX 0 /* BR5 */ #define INT_V_DZTX 1 #define INT_V_HK 2 #define INT_V_RL 3 #define INT_V_RQ 4 #define INT_V_TQ 5 #define INT_V_TS 6 #define INT_V_RY 7 #define INT_V_XU 8 #define INT_V_LPT 0 /* BR4 */ #define INT_V_PTR 1 #define INT_V_PTP 2 #define INT_V_CR 3 #define INT_DZRX (1u << INT_V_DZRX) #define INT_DZTX (1u << INT_V_DZTX) #define INT_HK (1u << INT_V_HK) #define INT_RL (1u << INT_V_RL) #define INT_RQ (1u << INT_V_RQ) #define INT_TQ (1u << INT_V_TQ) #define INT_TS (1u << INT_V_TS) #define INT_RY (1u << INT_V_RY) #define INT_XU (1u << INT_V_XU) #define INT_LPT (1u << INT_V_LPT) #define INT_PTR (1u << INT_V_PTR) #define INT_PTP (1u << INT_V_PTP) #define INT_CR (1u << INT_V_CR) #define IPL_DZRX (0x15 - IPL_HMIN) #define IPL_DZTX (0x15 - IPL_HMIN) #define IPL_HK (0x15 - IPL_HMIN) #define IPL_RL (0x15 - IPL_HMIN) #define IPL_RQ (0x15 - IPL_HMIN) #define IPL_TQ (0x15 - IPL_HMIN) #define IPL_TS (0x15 - IPL_HMIN) #define IPL_RY (0x15 - IPL_HMIN) #define IPL_XU (0x15 - IPL_HMIN) #define IPL_LPT (0x14 - IPL_HMIN) #define IPL_PTR (0x14 - IPL_HMIN) #define IPL_PTP (0x14 - IPL_HMIN) #define IPL_CR (0x14 - IPL_HMIN) /* Device vectors */ #define VEC_Q 0000 #define VEC_PTR 0070 #define VEC_PTP 0074 #define VEC_XQ 0120 #define VEC_XU 0120 #define VEC_RQ 0154 #define VEC_RL 0160 #define VEC_LPT 0200 #define VEC_HK 0210 #define VEC_TS 0224 #define VEC_CR 0230 #define VEC_TQ 0260 #define VEC_RX 0264 #define VEC_RY 0264 #define VEC_DZRX 0300 #define VEC_DZTX 0304 /* Interrupt macros */ #define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) #define NVCL(dv) ((IPL_##dv * 32) + TR_##dv) #define IREQ(dv) int_req[IPL_##dv] #define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) #define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ /* Logging */ #define LOG_CPU_I 0x1 /* intexc */ #define LOG_CPU_R 0x2 /* REI */ #define LOG_CPU_P 0x4 /* context */ /* Massbus definitions */ #define MBA_RP (TR_MBA0 - TR_MBA0) /* MBA for RP */ #define MBA_TU (TR_MBA1 - TR_MBA0) /* MBA for TU */ #define MBA_RMASK 0x1F /* max 32 reg */ #define MBE_NXD 1 /* nx drive */ #define MBE_NXR 2 /* nx reg */ #define MBE_GOE 3 /* err on GO */ /* Boot definitions */ #define BOOT_MB 0 /* device codes */ #define BOOT_HK 1 /* for VMB */ #define BOOT_RL 2 #define BOOT_UDA 17 #define BOOT_TK 18 /* Function prototypes for virtual memory interface */ int32 Read (uint32 va, int32 lnt, int32 acc); void Write (uint32 va, int32 val, int32 lnt, int32 acc); /* Function prototypes for physical memory interface (inlined) */ SIM_INLINE int32 ReadB (uint32 pa); SIM_INLINE int32 ReadW (uint32 pa); SIM_INLINE int32 ReadL (uint32 pa); SIM_INLINE int32 ReadLP (uint32 pa); SIM_INLINE void WriteB (uint32 pa, int32 val); SIM_INLINE void WriteW (uint32 pa, int32 val); SIM_INLINE void WriteL (uint32 pa, int32 val); void WriteLP (uint32 pa, int32 val); /* Function prototypes for I/O */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); int32 mba_rdbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_wrbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_chbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_get_bc (uint32 mbus); void mba_upd_ata (uint32 mbus, uint32 val); void mba_set_exc (uint32 mbus); void mba_set_don (uint32 mbus); void mba_set_enbdis (uint32 mbus, t_bool dis); t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat show_nexus (FILE *st, UNIT *uptr, int32 val, void *desc); void sbi_set_errcnf (void); int32 clk_cosched (int32 wait); #include "pdp11_io_lib.h" #endif simh-3.8.1/VAX/vax_syscm.c0000644000175000017500000006114011112314210013443 0ustar vlmvlm/* vax_syscm.c: PDP-11 compatibility mode symbolic decode and parse Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 12-Nov-06 RMS Fixed operand order in EIS instructions (found by W.F.J. Mueller) 27-Sep-05 RMS Fixed warnings compiling with 64b addresses 15-Sep-04 RMS Cloned from pdp11_sys.c */ #include "vax_defs.h" #include extern UNIT cpu_unit; /* Symbol tables */ /* Warning: for literals, the class number MUST equal the field width!! */ #define I_V_CL 18 /* class bits */ #define I_M_CL 017 /* class mask */ #define I_V_NPN 0 /* no operands */ #define I_V_REG 1 /* reg */ #define I_V_SOP 2 /* operand */ #define I_V_3B 3 /* 3b literal */ #define I_V_RSOP 4 /* reg, operand */ #define I_V_BR 5 /* cond branch */ #define I_V_6B 6 /* 6b literal */ #define I_V_SOB 7 /* reg, disp */ #define I_V_8B 8 /* 8b literal */ #define I_V_DOP 9 /* double operand */ #define I_V_CCC 10 /* CC clear */ #define I_V_CCS 11 /* CC set */ #define I_V_SOPR 12 /* operand, reg */ #define I_NPN (I_V_NPN << I_V_CL) #define I_REG (I_V_REG << I_V_CL) #define I_SOP (I_V_SOP << I_V_CL) #define I_3B (I_V_3B << I_V_CL) #define I_6B (I_V_6B << I_V_CL) #define I_BR (I_V_BR << I_V_CL) #define I_8B (I_V_8B << I_V_CL) #define I_RSOP (I_V_RSOP << I_V_CL) #define I_SOB (I_V_SOB << I_V_CL) #define I_DOP (I_V_DOP << I_V_CL) #define I_CCC (I_V_CCC << I_V_CL) #define I_CCS (I_V_CCS << I_V_CL) #define I_SOPR (I_V_SOPR << I_V_CL) static const int32 masks[] = { 0177777, 0177770, 0177700, 0177770, 0177000, 0177400, 0177700, 0177000, 0177400, 0170000, 0177777, 0177777, 0177000 }; static const char *opcode[] = { "HALT","WAIT","RTI","BPT", "IOT","RESET","RTT","MFPT", "JMP","RTS","SPL", "NOP","CLC","CLV","CLV CLC", "CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", "CLN","CLN CLC","CLN CLV","CLN CLV CLC", "CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", "NOP","SEC","SEV","SEV SEC", "SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", "SEN","SEN SEC","SEN SEV","SEN SEV SEC", "SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", "SWAB","BR","BNE","BEQ", "BGE","BLT","BGT","BLE", "JSR", "CLR","COM","INC","DEC", "NEG","ADC","SBC","TST", "ROR","ROL","ASR","ASL", "MARK","MFPI","MTPI","SXT", "CSM", "TSTSET","WRTLCK", "MOV","CMP","BIT","BIC", "BIS","ADD", "MUL","DIV","ASH","ASHC", "XOR", "FADD","FSUB","FMUL","FDIV", "L2DR", "MOVC","MOVRC","MOVTC", "LOCC","SKPC","SCANC","SPANC", "CMPC","MATC", "ADDN","SUBN","CMPN","CVTNL", "CVTPN","CVTNP","ASHN","CVTLN", "L3DR", "ADDP","SUBP","CMPP","CVTPL", "MULP","DIVP","ASHP","CVTLP", "MOVCI","MOVRCI","MOVTCI", "LOCCI","SKPCI","SCANCI","SPANCI", "CMPCI","MATCI", "ADDNI","SUBNI","CMPNI","CVTNLI", "CVTPNI","CVTNPI","ASHNI","CVTLNI", "ADDPI","SUBPI","CMPPI","CVTPLI", "MULPI","DIVPI","ASHPI","CVTLPI", "SOB", "BPL","BMI","BHI","BLOS", "BVC","BVS","BCC","BCS", "BHIS","BLO", /* encode only */ "EMT","TRAP", "CLRB","COMB","INCB","DECB", "NEGB","ADCB","SBCB","TSTB", "RORB","ROLB","ASRB","ASLB", "MTPS","MFPD","MTPD","MFPS", "MOVB","CMPB","BITB","BICB", "BISB","SUB", NULL }; static const int32 opc_val[] = { 0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, 0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, 0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, 0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, 0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, 0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, 0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, 0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, 0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, 0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, 0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, 0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, 0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, 0004000+I_RSOP, 0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, 0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, 0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, 0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, 0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, 0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, 0050000+I_DOP, 0060000+I_DOP, 0070000+I_SOPR, 0071000+I_SOPR, 0072000+I_SOPR, 0073000+I_SOPR, 0074000+I_RSOP, 0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, 0076020+I_REG, 0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, 0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, 0076044+I_NPN, 0076045+I_NPN, 0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, 0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, 0076060+I_REG, 0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, 0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, 0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, 0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, 0076144+I_NPN, 0076145+I_NPN, 0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, 0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, 0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, 0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, 0077000+I_SOB, 0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, 0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, 0103000+I_BR, 0103400+I_BR, 0104000+I_8B, 0104400+I_8B, 0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, 0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, 0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, 0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, 0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, 0150000+I_DOP, 0160000+I_DOP, -1 }; static const char *rname [] = { "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" }; static const char r50_to_asc[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; /* Specifier decode Inputs: *of = output stream addr = current PC spec = specifier nval = next word flag = TRUE if decoding for CPU iflag = TRUE if decoding integer instruction Outputs: count = -number of extra words retired */ int32 fprint_spec (FILE *of, t_addr addr, int32 spec, int32 nval) { int32 reg, mode; static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 }; static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 }; reg = spec & 07; mode = ((spec >> 3) & 07); switch (mode) { case 0: fprintf (of, "%s", rname[reg]); break; case 1: fprintf (of, "(%s)", rname[reg]); break; case 2: if (reg != 7) fprintf (of, "(%s)+", rname[reg]); else fprintf (of, "#%-X", nval); break; case 3: if (reg != 7) fprintf (of, "@(%s)+", rname[reg]); else fprintf (of, "@#%-X", nval); break; case 4: fprintf (of, "-(%s)", rname[reg]); break; case 5: fprintf (of, "@-(%s)", rname[reg]); break; case 6: if (reg != 7) fprintf (of, "%-X(%s)", nval, rname[reg]); else fprintf (of, "%-X", (nval + addr + 4) & 0177777); break; case 7: if (reg != 7) fprintf (of, "@%-X(%s)", nval, rname[reg]); else fprintf (of, "@%-X", (nval + addr + 4) & 0177777); break; } /* end case */ return ((reg == 07)? pcwd[mode]: rgwd[mode]); } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra words retired */ t_stat fprint_sym_cm (FILE *of, t_addr addr, t_value *bytes, int32 sw) { int32 i, j, c1, c2, c3, inst, srcm, srcr, dstm, dstr; int32 l8b, brdisp, wd1; uint32 val[3]; for (i = j = 0; i < 3; i++, j = j + 2) val[i] = (int32) (bytes[j] | (bytes[j + 1] << 8)); if (sw & SWMASK ('R')) { /* radix 50? */ if (val[0] > 0174777) /* max value */ return SCPE_ARG; c3 = val[0] % 050; c2 = (val[0] / 050) % 050; c1 = val[0] / (050 * 050); fprintf (of, "%c%c%c", r50_to_asc[c1], r50_to_asc[c2], r50_to_asc[c3]); return -1; } if (!(sw & SWMASK ('P')) || (addr & 1) || (addr > WMASK)) return SCPE_ARG; inst = val[0]; /* inst */ wd1 = 0; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ srcm = (inst >> 6) & 077; /* opr fields */ srcr = srcm & 07; dstm = inst & 077; dstr = dstm & 07; l8b = inst & 0377; switch (j) { /* case on class */ case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ fprintf (of, "%s", opcode[i]); break; case I_V_REG: /* reg */ fprintf (of, "%s %-s", opcode[i], rname[dstr]); break; case I_V_SOP: /* sop */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, dstm, val[1]); break; case I_V_3B: /* 3b */ fprintf (of, "%s %-X", opcode[i], dstr); break; case I_V_6B: /* 6b */ fprintf (of, "%s %-X", opcode[i], dstm); break; case I_V_BR: /* cond branch */ fprintf (of, "%s ", opcode[i]); brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; fprintf (of, "%-X", (addr + brdisp) & 0177777); break; case I_V_8B: /* 8b */ fprintf (of, "%s %-X", opcode[i], l8b); break; case I_V_SOB: /* sob */ fprintf (of, "%s %s,", opcode[i], rname[srcr]); brdisp = (dstm * 2) - 2; fprintf (of, "%-X", (addr - brdisp) & 0177777); break; case I_V_RSOP: /* rsop */ fprintf (of, "%s %s,", opcode[i], rname[srcr]); wd1 = fprint_spec (of, addr, dstm, val[1]); break; case I_V_SOPR: /* sopr */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, dstm, val[1]); fprintf (of, ",%s", rname[srcr]); break; case I_V_DOP: /* dop */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, srcm, val[1]); fprintf (of, ","); wd1 += fprint_spec (of, addr - wd1 - wd1, dstm, val[1 - wd1]); break; } /* end case */ return ((wd1 * 2) - 1); } /* end if */ } /* end for */ return SCPE_ARG; /* no match */ } #define A_PND 100 /* # seen */ #define A_MIN 040 /* -( seen */ #define A_PAR 020 /* (Rn) seen */ #define A_REG 010 /* Rn seen */ #define A_PLS 004 /* + seen */ #define A_NUM 002 /* number seen */ #define A_REL 001 /* relative addr seen */ /* Register number Inputs: *cptr = pointer to input string mchar = character to match after register name Outputs: rnum = 0..7 if a legitimate register < 0 if error */ int32 get_reg (char *cptr, char mchar) { int32 i; if (*(cptr + 2) != mchar) return -1; for (i = 0; i < 8; i++) { if (strncmp (cptr, rname[i], 2) == 0) return i; } return -1; } /* Number or memory address Inputs: *cptr = pointer to input string *dptr = pointer to output displacement *pflag = pointer to accumulating flags Outputs: cptr = pointer to next character in input string NULL if parsing error Flags: 0 (no result), A_NUM (number), A_REL (relative) */ char *get_addr (char *cptr, int32 *dptr, int32 *pflag) { int32 val, minus; char *tptr; minus = 0; if (*cptr == '.') { /* relative? */ *pflag = *pflag | A_REL; cptr++; } if (*cptr == '+') { /* +? */ *pflag = *pflag | A_NUM; cptr++; } if (*cptr == '-') { /* -? */ *pflag = *pflag | A_NUM; minus = 1; cptr++; } errno = 0; val = strtoul (cptr, &tptr, 16); if (cptr == tptr) { /* no number? */ if (*pflag == (A_REL + A_NUM)) /* .+, .-? */ return NULL; *dptr = 0; return cptr; } if (errno || (*pflag == A_REL)) /* .n? */ return NULL; *dptr = (minus? -val: val) & 0177777; *pflag = *pflag | A_NUM; return tptr; } /* Specifier decode Inputs: *cptr = pointer to input string addr = current PC n1 = 0 if no extra word used -1 if extra word used in prior decode *sptr = pointer to output specifier *dptr = pointer to output displacement Outputs: status = = -1 extra word decoded = 0 ok = +1 error */ t_stat get_spec (char *cptr, int32 addr, int32 n1, int32 *sptr, int32 *dptr) { int32 reg, indir, pflag, disp; indir = 0; /* no indirect */ pflag = 0; if (*cptr == '@') { /* indirect? */ indir = 010; cptr++; } if (*cptr == '#') { /* literal? */ pflag = pflag | A_PND; cptr++; } if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ pflag = pflag | A_MIN; cptr++; } else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1; if (*cptr == '(') { /* register index? */ pflag = pflag | A_PAR; if ((reg = get_reg (cptr + 1, ')')) < 0) return 1; cptr = cptr + 4; if (*cptr == '+') { /* autoincrement? */ pflag = pflag | A_PLS; cptr++; } } else if ((reg = get_reg (cptr, 0)) >= 0) { pflag = pflag | A_REG; cptr = cptr + 2; } if (*cptr != 0) /* all done? */ return 1; switch (pflag) { /* case on syntax */ case A_REG: /* Rn, @Rn */ *sptr = indir + reg; return 0; case A_PAR: /* (Rn), @(Rn) */ if (indir) { /* @(Rn) = @0(Rn) */ *sptr = 070 + reg; *dptr = 0; return -1; } else *sptr = 010 + reg; return 0; case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ *sptr = 020 + indir + reg; return 0; case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ *sptr = 040 + indir + reg; return 0; case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ *sptr = 060 + indir + reg; *dptr = disp; return -1; case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ disp = (disp + addr) & 0177777; /* fall through */ case A_PND+A_NUM: /* #n, @#n */ *sptr = 027 + indir; *dptr = disp; return -1; case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ *sptr = 067 + indir; *dptr = (disp - 4 + (2 * n1)) & 0177777; return -1; case A_NUM: /* n, @n */ *sptr = 067 + indir; *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; return -1; default: return 1; } /* end case */ } /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, int32 sw) { int32 d, i, j, reg, spec, n1, n2, disp, pflag; int32 val[3]; int32 ad32 = (int32) addr; t_stat r; char *tptr, gbuf[CBUFSIZE]; if (sw & SWMASK ('R')) /* radix 50 */ return SCPE_ARG; if (!(sw & SWMASK ('P')) || (ad32 & 1) || (ad32 > WMASK)) return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ n1 = n2 = pflag = 0; for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; if (opcode[i] == NULL) return SCPE_ARG; val[0] = opc_val[i] & 0177777; /* get value */ j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ switch (j) { /* case on class */ case I_V_NPN: /* no operand */ break; case I_V_REG: /* register */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; val[0] = val[0] | reg; break; case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ cptr = get_glyph (cptr, gbuf, 0); /* get literal */ d = (int32) get_uint (gbuf, 16, (1 << j) - 1, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; /* put in place */ break; case I_V_BR: /* cond br */ cptr = get_glyph (cptr, gbuf, 0); /* get address */ tptr = get_addr (gbuf, &disp, &pflag); /* parse */ if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; if ((pflag & A_REL) == 0) disp = (disp - ad32) & 0177777; if ((disp & 1) || (disp > 0400) && (disp < 0177402)) return SCPE_ARG; val[0] = val[0] | (((disp - 2) >> 1) & 0377); break; case I_V_SOB: /* sob */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); cptr = get_glyph (cptr, gbuf, 0); /* get address */ tptr = get_addr (gbuf, &disp, &pflag); /* parse */ if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; if ((pflag & A_REL) == 0) disp = (disp - ad32) & 0177777; if ((disp & 1) || ((disp > 2) && (disp < 0177604))) return SCPE_ARG; val[0] = val[0] | (((2 - disp) >> 1) & 077); break; case I_V_RSOP: /* reg, sop */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); /* fall through */ case I_V_SOP: /* sop */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) return SCPE_ARG; val[0] = val[0] | spec; break; case I_V_SOPR: /* dop, reg */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) return SCPE_ARG; val[0] = val[0] | spec; cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); break; case I_V_DOP: /* double op */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) return SCPE_ARG; val[0] = val[0] | (spec << 6); cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((n2 = get_spec (gbuf, ad32, n1, &spec, &val[1 - n1])) > 0) return SCPE_ARG; val[0] = val[0] | spec; break; case I_V_CCC: case I_V_CCS: /* cond code oper */ 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 ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || (opcode[i] == NULL)) return SCPE_ARG; val[0] = val[0] | (opc_val[i] & 0177777); } break; default: return SCPE_ARG; } if (*cptr != 0) return SCPE_ARG; /* junk at end? */ for (i = j = 0; i < 3; i++, j = j + 2) { bytes[j] = val[i] & BMASK; bytes[j + 1] = (val[i] >> 8) & BMASK; } return ((2 * (n1 + n2)) - 1); } simh-3.8.1/VAX/vax780_syslist.c0000644000175000017500000001027311110570130014262 0ustar vlmvlm/* vax_syslist.c: VAX device list Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 17-May-06 RMS Added CR11/CD11 support (from John Dundas) 01-Oct-04 RMS Cloned from vax_sys.c */ #include "vax_defs.h" char sim_name[] = "VAX780"; extern DEVICE cpu_dev; extern DEVICE tlb_dev; extern DEVICE sbi_dev; extern DEVICE mctl_dev[MCTL_NUM]; extern DEVICE uba_dev; extern DEVICE mba_dev[MBA_NUM]; extern DEVICE clk_dev; extern DEVICE tmr_dev; extern DEVICE tti_dev, tto_dev; extern DEVICE fl_dev; extern DEVICE cr_dev; extern DEVICE lpt_dev; extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; extern DEVICE rl_dev; extern DEVICE hk_dev; extern DEVICE rp_dev; extern DEVICE ry_dev; extern DEVICE ts_dev; extern DEVICE tq_dev; extern DEVICE tu_dev; extern DEVICE dz_dev; extern DEVICE xu_dev, xub_dev; extern int32 sim_switches; extern UNIT cpu_unit; extern void WriteB (uint32 pa, int32 val); extern void rom_wr_B (int32 pa, int32 val); DEVICE *sim_devices[] = { &cpu_dev, &tlb_dev, &sbi_dev, &mctl_dev[0], &mctl_dev[1], &uba_dev, &mba_dev[0], &mba_dev[1], &clk_dev, &tmr_dev, &tti_dev, &tto_dev, &fl_dev, &dz_dev, &cr_dev, &lpt_dev, &rp_dev, &rl_dev, &hk_dev, &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev, &ry_dev, &tu_dev, &ts_dev, &tq_dev, &xu_dev, &xub_dev, NULL }; /* Binary loader The binary loader handles absolute system images, that is, system images linked /SYSTEM. These are simply a byte stream, with no origin or relocation information. -r load ROM0 -s load ROM1 -o for memory, specify origin */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { t_stat r; int32 val; uint32 origin, limit; if (flag) /* dump? */ return SCPE_ARG; origin = 0; /* memory */ limit = (uint32) cpu_unit.capac; if (sim_switches & SWMASK ('O')) { /* origin? */ origin = (int32) get_uint (cptr, 16, 0xFFFFFFFF, &r); if (r != SCPE_OK) return SCPE_ARG; } while ((val = getc (fileref)) != EOF) { /* read byte stream */ if (sim_switches & SWMASK ('R')) { /* ROM0? */ if (origin >= ROMSIZE) return SCPE_NXM; rom_wr_B (ROM0BASE + origin, val); } else if (sim_switches & SWMASK ('S')) { /* ROM1? */ if (origin >= ROMSIZE) return SCPE_NXM; rom_wr_B (ROM1BASE + origin, val); } else { if (origin >= limit) /* NXM? */ return SCPE_NXM; WriteB (origin, val); /* memory */ } origin = origin + 1; } return SCPE_OK; } simh-3.8.1/VAX/vax_syslist.c0000644000175000017500000001033511110605734014033 0ustar vlmvlm/* vax_sys.c: VAX simulator interface Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 17-Oct-06 RMS Re-ordered device list 17-May-06 RMS Added CR11/CD11 support (from John Dundas) 01-Oct-2004 RMS Cloned from vax_sys.c */ #include "vax_defs.h" char sim_name[] = "VAX"; extern DEVICE cpu_dev; extern DEVICE tlb_dev; extern DEVICE rom_dev; extern DEVICE nvr_dev; extern DEVICE sysd_dev; extern DEVICE qba_dev; extern DEVICE tti_dev, tto_dev; extern DEVICE cr_dev; extern DEVICE lpt_dev; extern DEVICE clk_dev; extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; extern DEVICE rl_dev; extern DEVICE ry_dev; extern DEVICE ts_dev; extern DEVICE tq_dev; extern DEVICE dz_dev; extern DEVICE csi_dev, cso_dev; extern DEVICE xq_dev, xqb_dev; extern DEVICE vh_dev; extern int32 sim_switches; extern void WriteB (uint32 pa, int32 val); extern void rom_wr_B (int32 pa, int32 val); extern UNIT cpu_unit; DEVICE *sim_devices[] = { &cpu_dev, &tlb_dev, &rom_dev, &nvr_dev, &sysd_dev, &qba_dev, &clk_dev, &tti_dev, &tto_dev, &csi_dev, &cso_dev, &dz_dev, &vh_dev, &cr_dev, &lpt_dev, &rl_dev, &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev, &ry_dev, &ts_dev, &tq_dev, &xq_dev, &xqb_dev, NULL }; /* Binary loader The binary loader handles absolute system images, that is, system images linked /SYSTEM. These are simply a byte stream, with no origin or relocation information. -r load ROM -n load NVR -o for memory, specify origin */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { t_stat r; int32 i; uint32 origin, limit; extern int32 ssc_cnf; #define SSCCNF_BLO 0x80000000 if (flag) /* dump? */ return SCPE_ARG; if (sim_switches & SWMASK ('R')) { /* ROM? */ origin = ROMBASE; limit = ROMBASE + ROMSIZE; } else if (sim_switches & SWMASK ('N')) { /* NVR? */ origin = NVRBASE; limit = NVRBASE + NVRSIZE; ssc_cnf = ssc_cnf & ~SSCCNF_BLO; } else { origin = 0; /* memory */ limit = (uint32) cpu_unit.capac; if (sim_switches & SWMASK ('O')) { /* origin? */ origin = (int32) get_uint (cptr, 16, 0xFFFFFFFF, &r); if (r != SCPE_OK) return SCPE_ARG; } } while ((i = getc (fileref)) != EOF) { /* read byte stream */ if (origin >= limit) /* NXM? */ return SCPE_NXM; if (sim_switches & SWMASK ('R')) /* ROM? */ rom_wr_B (origin, i); /* not writeable */ else WriteB (origin, i); /* store byte */ origin = origin + 1; } return SCPE_OK; } simh-3.8.1/VAX/vax780_mba.c0000644000175000017500000007137311112260034013320 0ustar vlmvlm/* vax780_mba.c: VAX 11/780 Massbus adapter Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mba0, mba1 RH780 Massbus adapter 28-May-08 RMS Inlined physical memory routines */ #include "vax_defs.h" /* Massbus */ #define MBA_NMAPR 256 /* number of map reg */ #define MBA_V_RTYPE 10 /* nexus addr: reg type */ #define MBA_M_RTYPE 0x3 #define MBART_INT 0x0 /* internal */ #define MBART_EXT 0x1 /* external */ #define MBART_MAP 0x2 /* map */ #define MBA_V_INTOFS 2 /* int reg: reg ofs */ #define MBA_M_INTOFS 0xFF #define MBA_V_DRV 7 /* ext reg: drive num */ #define MBA_M_DRV 0x7 #define MBA_V_DEVOFS 2 /* ext reg: reg ofs */ #define MBA_M_DEVOFS 0x1F #define MBA_RTYPE(x) (((x) >> MBA_V_RTYPE) & MBA_M_RTYPE) #define MBA_INTOFS(x) (((x) >> MBA_V_INTOFS) & MBA_M_INTOFS) #define MBA_EXTDRV(x) (((x) >> MBA_V_DRV) & MBA_M_DRV) #define MBA_EXTOFS(x) (((x) >> MBA_V_DEVOFS) & MBA_M_DEVOFS) /* Massbus configuration register */ #define MBACNF_OF 0x0 #define MBACNF_ADPDN 0x00800000 /* adap pdn - ni */ #define MBACNF_ADPUP 0x00400000 /* adap pup - ni */ #define MBACNF_CODE 0x00000020 #define MBACNF_RD (SBI_FAULTS|MBACNF_W1C) #define MBACNF_W1C 0x00C00000 /* Control register */ #define MBACR_OF 0x1 #define MBACR_MNT 0x00000008 /* maint */ #define MBACR_IE 0x00000004 /* int enable */ #define MBACR_ABORT 0x00000002 /* abort */ #define MBACR_INIT 0x00000001 #define MBACR_RD 0x0000000E #define MBACR_WR 0x0000000E /* Status register */ #define MBASR_OF 0x2 #define MBASR_DTBUSY 0x80000000 /* DT busy RO */ #define MBASR_NRCONF 0x40000000 /* no conf - ni W1C */ #define MBASR_CRD 0x20000000 /* CRD - ni W1C */ #define MBASR_CBH 0x00800000 /* CBHUNG - ni W1C */ #define MBASR_PGE 0x00080000 /* prog err - W1C int */ #define MBASR_NFD 0x00040000 /* nx drive - W1C int */ #define MBASR_MCPE 0x00020000 /* ctl perr - ni W1C int */ #define MBASR_ATA 0x00010000 /* attn - W1C int */ #define MBASR_SPE 0x00004000 /* silo perr - ni W1C int */ #define MBASR_DTCMP 0x00002000 /* xfr done - W1C int */ #define MBASR_DTABT 0x00001000 /* abort - W1C int */ #define MBASR_DLT 0x00000800 /* dat late - ni W1C abt */ #define MBASR_WCEU 0x00000400 /* wrchk upper - W1C abt */ #define MBASR_WCEL 0x00000200 /* wrchk lower - W1C abt */ #define MBASR_MXF 0x00000100 /* miss xfr - ni W1C abt */ #define MBASR_MBEXC 0x00000080 /* except - ni W1C abt */ #define MBASR_MBDPE 0x00000040 /* dat perr - ni W1C abt */ #define MBASR_MAPPE 0x00000020 /* map perr - ni W1C abt */ #define MBASR_INVM 0x00000010 /* inv map - W1C abt */ #define MBASR_ERCONF 0x00000008 /* err conf - ni W1C abt */ #define MBASR_RDS 0x00000004 /* RDS - ni W1C abt */ #define MBASR_ITMO 0x00000002 /* timeout - W1C abt */ #define MBASR_RTMO 0x00000001 /* rd timeout - W1C abt */ #define MBASR_RD 0xE08F7FFF #define MBASR_W1C 0x608F7FFF #define MBASR_ABORTS 0x00000FFF #define MBASR_ERRORS 0x608E49FF #define MBASR_INTR 0x000F7000 /* Virtual address register */ #define MBAVA_OF 0x3 #define MBAVA_RD 0x0001FFFF #define MBAVA_WR (MBAVA_RD) /* Byte count */ #define MBABC_OF 0x4 #define MBABC_WR 0x0000FFFF #define MBABC_V_MBC 16 /* MB count */ /* Diagnostic register */ #define MBADR_OF 0x5 #define MBADR_RD 0xFFFFFFFF #define MBADR_WR 0xFFC00000 /* Selected map entry - read only */ #define MBASMR_OF 0x6 #define MBASMR_RD (MBAMAP_RD) /* Command register (SBI) - read only */ #define MBACMD_OF 0x7 /* External registers */ #define MBA_CS1 0x00 /* device CSR1 */ #define MBA_CS1_WR 0x3F /* writeable bits */ #define MBA_CS1_DT 0x28 /* >= for data xfr */ /* Map registers */ #define MBAMAP_VLD 0x80000000 /* valid */ #define MBAMAP_PAG 0x001FFFFF #define MBAMAP_RD (MBAMAP_VLD | MBAMAP_PAG) #define MBAMAP_WR (MBAMAP_RD) /* Debug switches */ #define MBA_DEB_RRD 0x01 /* reg reads */ #define MBA_DEB_RWR 0x02 /* reg writes */ #define MBA_DEB_MRD 0x04 /* map reads */ #define MBA_DEB_MWR 0x08 /* map writes */ #define MBA_DEB_XFR 0x10 /* transfers */ #define MBA_DEB_ERR 0x20 /* errors */ uint32 mba_cnf[MBA_NUM]; /* config reg */ uint32 mba_cr[MBA_NUM]; /* control reg */ uint32 mba_sr[MBA_NUM]; /* status reg */ uint32 mba_va[MBA_NUM]; /* virt addr */ uint32 mba_bc[MBA_NUM]; /* byte count */ uint32 mba_dr[MBA_NUM]; /* diag reg */ uint32 mba_smr[MBA_NUM]; /* sel map reg */ uint32 mba_map[MBA_NUM][MBA_NMAPR]; /* map */ extern uint32 nexus_req[NEXUS_HLVL]; extern UNIT cpu_unit; extern FILE *sim_log; extern FILE *sim_deb; extern int32 sim_switches; t_stat mba_reset (DEVICE *dptr); t_stat mba_rdreg (int32 *val, int32 pa, int32 mode); t_stat mba_wrreg (int32 val, int32 pa, int32 lnt); t_bool mba_map_addr (uint32 va, uint32 *ma, uint32 mb); void mba_set_int (uint32 mb); void mba_clr_int (uint32 mb); void mba_upd_sr (uint32 set, uint32 clr, uint32 mb); DIB mba0_dib, mba1_dib; /* Massbus register dispatches */ static t_stat (*mbregR[MBA_NUM])(int32 *dat, int32 ad, int32 md); static t_stat (*mbregW[MBA_NUM])(int32 dat, int32 ad, int32 md); static int32 (*mbabort[MBA_NUM])(void); /* Massbus adapter data structures mba_dev MBA device descriptors mbax_unit MBA unit mbax_reg MBA register list */ DIB mba0_dib = { TR_MBA0, 0, &mba_rdreg, &mba_wrreg, 0, NVCL (MBA0) }; UNIT mba0_unit = { UDATA (NULL, 0, 0) }; REG mba0_reg[] = { { HRDATA (CNFR, mba_cnf[0], 32) }, { HRDATA (CR, mba_cr[0], 4) }, { HRDATA (SR, mba_sr[0], 32) }, { HRDATA (VA, mba_va[0], 17) }, { HRDATA (BC, mba_bc[0], 16) }, { HRDATA (DR, mba_dr[0], 32) }, { HRDATA (SMR, mba_dr[0], 32) }, { BRDATA (MAP, mba_map[0], 16, 32, MBA_NMAPR) }, { FLDATA (NEXINT, nexus_req[IPL_MBA0], TR_MBA0) }, { NULL } }; MTAB mba0_mod[] = { { MTAB_XTD|MTAB_VDV, TR_MBA0, "NEXUS", NULL, NULL, &show_nexus }, { 0 } }; DIB mba1_dib = { TR_MBA1, 0, &mba_rdreg, &mba_wrreg, 0, NVCL (MBA1) }; UNIT mba1_unit = { UDATA (NULL, 0, 0) }; MTAB mba1_mod[] = { { MTAB_XTD|MTAB_VDV, TR_MBA1, "NEXUS", NULL, NULL, &show_nexus }, { 0 } }; REG mba1_reg[] = { { HRDATA (CNFR, mba_cnf[1], 32) }, { HRDATA (CR, mba_cr[1], 4) }, { HRDATA (SR, mba_sr[1], 32) }, { HRDATA (VA, mba_va[1], 17) }, { HRDATA (BC, mba_bc[1], 16) }, { HRDATA (DR, mba_dr[1], 32) }, { HRDATA (SMR, mba_dr[1], 32) }, { BRDATA (MAP, mba_map[1], 16, 32, MBA_NMAPR) }, { FLDATA (NEXINT, nexus_req[IPL_MBA1], TR_MBA1) }, { NULL } }; DEBTAB mba_deb[] = { { "REGREAD", MBA_DEB_RRD }, { "REGWRITE", MBA_DEB_RWR }, { "MAPREAD", MBA_DEB_MRD }, { "MAPWRITE", MBA_DEB_MWR }, { "XFER", MBA_DEB_XFR }, { "ERROR", MBA_DEB_ERR }, { NULL, 0 } }; DEVICE mba_dev[] = { { "MBA0", &mba0_unit, mba0_reg, mba0_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &mba_reset, NULL, NULL, NULL, &mba0_dib, DEV_NEXUS | DEV_DEBUG, 0, mba_deb, 0, 0 }, { "MBA1", &mba1_unit, mba1_reg, mba1_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &mba_reset, NULL, NULL, NULL, &mba1_dib, DEV_NEXUS | DEV_DEBUG, 0, mba_deb, 0, 0 } }; /* Read Massbus adapter register */ t_stat mba_rdreg (int32 *val, int32 pa, int32 lnt) { int32 mb, ofs, drv, rtype; uint32 t; t_stat r; mb = NEXUS_GETNEX (pa) - TR_MBA0; /* get MBA */ if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>MBA%d: invalid adapter read mask, pa = %X, lnt = %d\r\n", mb, pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } if (mb >= MBA_NUM) /* valid? */ return SCPE_NXM; rtype = MBA_RTYPE (pa); /* get reg type */ switch (rtype) { /* case on type */ case MBART_INT: /* internal */ ofs = MBA_INTOFS (pa); /* check range */ switch (ofs) { case MBACNF_OF: /* CNF */ *val = (mba_cnf[mb] & MBACNF_RD) | MBACNF_CODE; break; case MBACR_OF: /* CR */ *val = mba_cr[mb] & MBACR_RD; break; case MBASR_OF: /* SR */ *val = mba_sr[mb] & MBASR_RD; break; case MBAVA_OF: /* VA */ *val = mba_va[mb] & MBAVA_RD; break; case MBABC_OF: /* BC */ t = mba_bc[mb] & MBABC_WR; *val = (t << MBABC_V_MBC) | t; break; case MBADR_OF: /* DR */ *val = mba_dr[mb] & MBADR_RD; break; case MBASMR_OF: /* SMR */ *val = mba_smr[mb] & MBASMR_RD; break; case MBACMD_OF: /* CMD */ *val = 0; break; default: return SCPE_NXM; } if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RRD)) fprintf (sim_deb, ">>MBA%d: int reg %d read, value = %X\n", mb, ofs, *val); break; case MBART_EXT: /* external */ if (!mbregR[mb]) /* device there? */ return SCPE_NXM; drv = MBA_EXTDRV (pa); /* get dev num */ ofs = MBA_EXTOFS (pa); /* get reg offs */ r = mbregR[mb] (val, ofs, drv); /* call device */ if (r == MBE_NXD) /* nx drive? */ mba_upd_sr (MBASR_NFD, 0, mb); else if (r == MBE_NXR) /* nx reg? */ return SCPE_NXM; *val |= (mba_sr[mb] & ~WMASK); /* upper 16b from SR */ if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RRD)) fprintf (sim_deb, ">>MBA%d: drv %d ext reg %d read, value = %X\n", mb, drv, ofs, *val); break; case MBART_MAP: /* map */ ofs = MBA_INTOFS (pa); *val = mba_map[mb][ofs] & MBAMAP_RD; if (DEBUG_PRI (mba_dev[mb], MBA_DEB_MRD)) fprintf (sim_deb, ">>MBA%d: map %d read, value = %X\n", mb, ofs, *val); break; default: return SCPE_NXM; } return SCPE_OK; } /* Write Massbus adapter register */ t_stat mba_wrreg (int32 val, int32 pa, int32 lnt) { int32 mb, ofs, drv, rtype; t_stat r; t_bool cs1dt; mb = NEXUS_GETNEX (pa) - TR_MBA0; /* get MBA */ if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ printf (">>MBA%d: invalid adapter write mask, pa = %X, lnt = %d\r\n", mb, pa, lnt); sbi_set_errcnf (); /* err confirmation */ return SCPE_OK; } if (mb >= MBA_NUM) /* valid? */ return SCPE_NXM; rtype = MBA_RTYPE (pa); /* get reg type */ switch (rtype) { /* case on type */ case MBART_INT: /* internal */ ofs = MBA_INTOFS (pa); /* check range */ switch (ofs) { case MBACNF_OF: /* CNF */ mba_cnf[mb] &= ~(val & MBACNF_W1C); break; case MBACR_OF: /* CR */ if (val & MBACR_INIT) /* init? */ mba_reset (&mba_dev[mb]); /* reset MBA */ if ((val & MBACR_ABORT) && (mba_sr[mb] & MBASR_DTBUSY)) { if (mbabort[mb]) /* abort? */ mbabort[mb] (); mba_upd_sr (MBASR_DTABT, 0, mb); } if ((val & MBACR_MNT) && (mba_sr[mb] & MBASR_DTBUSY)) { mba_upd_sr (MBASR_PGE, 0, mb); /* mnt & xfer? */ val = val & ~MBACR_MNT; } if ((val & MBACR_IE) == 0) mba_clr_int (mb); mba_cr[mb] = (mba_cr[mb] & ~MBACR_WR) | (val & MBACR_WR); break; case MBASR_OF: /* SR */ mba_sr[mb] = mba_sr[mb] & ~(val & MBASR_W1C); break; case MBAVA_OF: /* VA */ if (mba_sr[mb] & MBASR_DTBUSY) /* err if xfr */ mba_upd_sr (MBASR_PGE, 0, mb); else mba_va[mb] = val & MBAVA_WR; break; case MBABC_OF: /* BC */ if (mba_sr[mb] & MBASR_DTBUSY) /* err if xfr */ mba_upd_sr (MBASR_PGE, 0, mb); else mba_bc[mb] = val & MBABC_WR; break; case MBADR_OF: /* DR */ mba_dr[mb] = (mba_dr[mb] & ~MBADR_WR) | (val & MBADR_WR); break; default: return SCPE_NXM; } if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RWR)) fprintf (sim_deb, ">>MBA%d: int reg %d write, value = %X\n", mb, ofs, val); break; case MBART_EXT: /* external */ if (!mbregW[mb]) /* device there? */ return SCPE_NXM; drv = MBA_EXTDRV (pa); /* get dev num */ ofs = MBA_EXTOFS (pa); /* get reg offs */ cs1dt = (ofs == MBA_CS1) && (val & CSR_GO) && /* starting xfr? */ ((val & MBA_CS1_WR) >= MBA_CS1_DT); if (cs1dt && (mba_sr[mb] & MBASR_DTBUSY)) { /* xfr while busy? */ mba_upd_sr (MBASR_PGE, 0, mb); /* prog error */ break; } r = mbregW[mb] (val & WMASK, ofs, drv); /* write dev reg */ if (r == MBE_NXD) /* nx drive? */ mba_upd_sr (MBASR_NFD, 0, mb); else if (r == MBE_NXR) /* nx reg? */ return SCPE_NXM; if (cs1dt && (r == SCPE_OK)) /* did dt start? */ mba_sr[mb] = (mba_sr[mb] | MBASR_DTBUSY) & ~MBASR_W1C; if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RWR)) fprintf (sim_deb, ">>MBA%d: drv %d ext reg %d write, value = %X\n", mb, drv, ofs, val); break; case MBART_MAP: /* map */ ofs = MBA_INTOFS (pa); mba_map[mb][ofs] = val & MBAMAP_WR; if (DEBUG_PRI (mba_dev[mb], MBA_DEB_MWR)) fprintf (sim_deb, ">>MBA%d: map %d write, value = %X\n", mb, ofs, val); break; default: return SCPE_NXM; } return SCPE_OK; } /* Massbus I/O routine mb_rdbufW - fetch word buffer from memory mb_wrbufW - store word buffer into memory mb_chbufW - compare word buffer with memory Returns number of bytes successfully transferred/checked */ int32 mba_rdbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa, dat; if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = mba_va[mb]; /* get virt addr */ mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!mba_map_addr (ba + i, &pa, mb)) /* page inv? */ break; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_upd_sr (MBASR_RTMO, 0, mb); break; } pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) fprintf (sim_deb, ">>MBA%d: read, pa = %X, bc = %X\n", mb, pa, pbc); if ((pa | pbc) & 1) { /* aligned word? */ for (j = 0; j < pbc; pa++, j++) { /* no, bytes */ if ((i + j) & 1) { /* odd byte? */ *buf = (*buf & BMASK) | (ReadB (pa) << 8); buf++; } else *buf = (*buf & ~BMASK) | ReadB (pa); } } else if ((pa | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; pa = pa + 2, j = j + 2) { /* no, words */ *buf++ = ReadW (pa); /* get word */ } } else { /* yes, do by LW */ for (j = 0; j < pbc; pa = pa + 4, j = j + 4) { dat = ReadL (pa); /* get lw */ *buf++ = dat & WMASK; /* low 16b */ *buf++ = (dat >> 16) & WMASK; /* high 16b */ } } } mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; return i; } int32 mba_wrbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa, dat; if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = mba_va[mb]; /* get virt addr */ mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!mba_map_addr (ba + i, &pa, mb)) /* page inv? */ break; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_upd_sr (MBASR_RTMO, 0, mb); break; } pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) fprintf (sim_deb, ">>MBA%d: write, pa = %X, bc = %X\n", mb, pa, pbc); if ((pa | pbc) & 1) { /* aligned word? */ for (j = 0; j < pbc; pa++, j++) { /* no, bytes */ if ((i + j) & 1) { WriteB (pa, (*buf >> 8) & BMASK); buf++; } else WriteB (pa, *buf & BMASK); } } else if ((pa | pbc) & 3) { /* aligned LW? */ for (j = 0; j < pbc; pa = pa + 2, j = j + 2) { /* no, words */ WriteW (pa, *buf); /* write word */ buf++; } } else { /* yes, do by LW */ for (j = 0; j < pbc; pa = pa + 4, j = j + 4) { dat = (uint32) *buf++; /* get low 16b */ dat = dat | (((uint32) *buf++) << 16); /* merge hi 16b */ WriteL (pa, dat); /* store LW */ } } } mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; return i; } int32 mba_chbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa, dat, cmp; if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = mba_va[mb]; /* get virt addr */ mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (!mba_map_addr (ba + i, &pa, mb)) /* page inv? */ break; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_upd_sr (MBASR_RTMO, 0, mb); break; } pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) fprintf (sim_deb, ">>MBA%d: check, pa = %X, bc = %X\n", mb, pa, pbc); if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; for (j = 0; j < pbc; j++, pa++) { /* byte by byte */ cmp = ReadB (pa); if ((i + j) & 1) dat = (*buf++ >> 8) & BMASK; else dat = *buf & BMASK; if (cmp != dat) { mba_upd_sr ((j & 1)? MBASR_WCEU: MBASR_WCEL, 0, mb); break; } /* end if */ } /* end for j */ } /* end for i */ mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; return i; } /* Map an address via the translation map */ t_bool mba_map_addr (uint32 va, uint32 *ma, uint32 mb) { uint32 vblk = (va >> VA_V_VPN); /* map index */ uint32 mmap = mba_map[mb][vblk]; /* get map */ mba_smr[mb] = mmap; /* save map reg */ if (mmap & MBAMAP_VLD) { /* valid? */ *ma = ((mmap & MBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (va); return 1; /* legit addr */ } mba_upd_sr (MBASR_INVM, 0, mb); /* invalid map */ return 0; } /* Device access, status, and interrupt routines */ void mba_set_don (uint32 mb) { mba_upd_sr (MBASR_DTCMP, 0, mb); return; } void mba_upd_ata (uint32 mb, uint32 val) { if (val) mba_upd_sr (MBASR_ATA, 0, mb); else mba_upd_sr (0, MBASR_ATA, mb); return; } void mba_set_exc (uint32 mb) { mba_upd_sr (MBASR_MBEXC, 0, mb); if (DEBUG_PRI (mba_dev[mb], MBA_DEB_ERR)) fprintf (sim_deb, ">>MBA%d: EXC write\n", mb); return; } int32 mba_get_bc (uint32 mb) { if (mb >= MBA_NUM) return 0; return (MBABC_WR + 1) - mba_bc[mb]; } void mba_set_int (uint32 mb) { DIB *dibp; if (mb >= MBA_NUM) return; dibp = (DIB *) mba_dev[mb].ctxt; if (dibp) nexus_req[dibp->vloc >> 5] |= (1u << (dibp->vloc & 0x1F)); return; } void mba_clr_int (uint32 mb) { DIB *dibp; if (mb >= MBA_NUM) return; dibp = (DIB *) mba_dev[mb].ctxt; if (dibp) nexus_req[dibp->vloc >> 5] &= ~(1u << (dibp->vloc & 0x1F)); return; } void mba_upd_sr (uint32 set, uint32 clr, uint32 mb) { if (mb >= MBA_NUM) return; if (set & MBASR_ABORTS) set |= (MBASR_DTCMP|MBASR_DTABT); if (set & (MBASR_DTCMP|MBASR_DTABT)) mba_sr[mb] &= ~MBASR_DTBUSY; mba_sr[mb] = (mba_sr[mb] | set) & ~clr; if ((set & MBASR_INTR) && (mba_cr[mb] & MBACR_IE)) mba_set_int (mb); if ((set & MBASR_ERRORS) && (DEBUG_PRI (mba_dev[mb], MBA_DEB_ERR))) fprintf (sim_deb, ">>MBA%d: CS error = %X\n", mb, mba_sr[mb]); return; } /* Reset Massbus adapter */ t_stat mba_reset (DEVICE *dptr) { int32 i, mb; DIB *dibp; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; mb = dibp->ba - TR_MBA0; if ((mb < 0) || (mb >= MBA_NUM)) return SCPE_IERR; mba_cnf[mb] = 0; mba_cr[mb] &= MBACR_MNT; mba_sr[mb] = 0; mba_bc[mb] = 0; mba_va[mb] = 0; mba_dr[mb] = 0; mba_smr[mb] = 0; if (sim_switches & SWMASK ('P')) { for (i = 0; i < MBA_NMAPR; i++) mba_map[mb][i] = 0; } if (mbabort[mb]) /* reset device */ mbabort[mb] (); return SCPE_OK; } /* Show Massbus adapter number */ t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr = find_dev_from_unit (uptr); DIB *dibp; if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "Massbus adapter %d", dibp->ba); return SCPE_OK; } /* Enable/disable Massbus adapter */ void mba_set_enbdis (uint32 mb, t_bool dis) { if (mb >= MBA_NUM) /* valid MBA? */ return; if (dis) mba_dev[mb].flags |= DEV_DIS; else mba_dev[mb].flags &= ~DEV_DIS; return; } /* Init Mbus tables */ void init_mbus_tab (void) { uint32 i; for (i = 0; i < MBA_NUM; i++) { mbregR[i] = NULL; mbregW[i] = NULL; mbabort[i] = NULL; } return; } /* Build dispatch tables */ t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp) { uint32 idx; if ((dptr == NULL) || (dibp == NULL)) /* validate args */ return SCPE_IERR; idx = dibp->ba; /* Mbus # */ if (idx >= MBA_NUM) return SCPE_STOP; if ((mbregR[idx] && dibp->rd && /* conflict? */ (mbregR[idx] != dibp->rd)) || (mbregW[idx] && dibp->wr && (mbregW[idx] != dibp->wr)) || (mbabort[idx] && dibp->ack[0] && (mbabort[idx] != dibp->ack[0]))) { printf ("Massbus %s assignment conflict at %d\n", sim_dname (dptr), dibp->ba); if (sim_log) fprintf (sim_log, "Massbus %s assignment conflict at %d\n", sim_dname (dptr), dibp->ba); return SCPE_STOP; } if (dibp->rd) /* set rd dispatch */ mbregR[idx] = dibp->rd; if (dibp->wr) /* set wr dispatch */ mbregW[idx] = dibp->wr; if (dibp->ack[0]) /* set abort dispatch */ mbabort[idx] = dibp->ack[0]; return SCPE_OK; } simh-3.8.1/VAX/vax_defs.h0000644000175000017500000010230311111666660013252 0ustar vlmvlm/* vax_defs.h: VAX architecture definitions file Copyright (c) 1998-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. The author gratefully acknowledges the help of Stephen Shirron, Antonio Carlini, and Kevin Peterson in providing specifications for the Qbus VAX's 09-May-06 RMS Added system PTE ACV error code 03-May-06 RMS Added EDITPC get/put cc's macros 03-Nov-05 RMS Added 780 stop codes 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) 02-Sep-04 RMS Added octa specifier definitions 30-Aug-04 RMS Added octa, h_floating instruction definitions 24-Aug-04 RMS Added compatibility mode definitions 18-Apr-04 RMS Added octa, fp, string definitions 19-May-03 RMS Revised for new conditional compilation scheme 14-Jul-02 RMS Added infinite loop message 30-Apr-02 RMS Added CLR_TRAPS macro */ #ifndef _VAX_DEFS_H #define _VAX_DEFS_H 0 #ifndef VM_VAX #define VM_VAX 0 #endif #include "sim_defs.h" #include /* Stops and aborts */ #define STOP_HALT 1 /* halt */ #define STOP_IBKPT 2 /* breakpoint */ #define STOP_CHMFI 3 /* chg mode IS */ #define STOP_ILLVEC 4 /* illegal vector */ #define STOP_INIE 5 /* exc in intexc */ #define STOP_PPTE 6 /* proc pte in Px */ #define STOP_UIPL 7 /* undefined IPL */ #define STOP_RQ 8 /* fatal RQ err */ #define STOP_LOOP 9 /* infinite loop */ #define STOP_SANITY 10 /* sanity timer exp */ #define STOP_SWDN 11 /* software done (780) */ #define STOP_BOOT 12 /* reboot (780) */ #define STOP_UNKNOWN 13 /* unknown reason */ #define STOP_UNKABO 14 /* unknown abort */ #define ABORT_INTR -1 /* interrupt */ #define ABORT_MCHK (-SCB_MCHK) /* machine check */ #define ABORT_RESIN (-SCB_RESIN) /* rsvd instruction */ #define ABORT_RESAD (-SCB_RESAD) /* rsvd addr mode */ #define ABORT_RESOP (-SCB_RESOP) /* rsvd operand */ #define ABORT_CMODE (-SCB_CMODE) /* comp mode fault */ #define ABORT_ARITH (-SCB_ARITH) /* arithmetic trap */ #define ABORT_ACV (-SCB_ACV) /* access violation */ #define ABORT_TNV (-SCB_TNV) /* transl not vaid */ #define ABORT(x) longjmp (save_env, (x)) /* abort */ #define RSVD_INST_FAULT ABORT (ABORT_RESIN) #define RSVD_ADDR_FAULT ABORT (ABORT_RESAD) #define RSVD_OPND_FAULT ABORT (ABORT_RESOP) #define FLT_OVFL_FAULT p1 = FLT_OVRFLO, ABORT (ABORT_ARITH) #define FLT_DZRO_FAULT p1 = FLT_DIVZRO, ABORT (ABORT_ARITH) #define FLT_UNFL_FAULT p1 = FLT_UNDFLO, ABORT (ABORT_ARITH) #define CMODE_FAULT(cd) p1 = (cd), ABORT (ABORT_CMODE) #define MACH_CHECK(cd) p1 = (cd), ABORT (ABORT_MCHK) /* Recovery queue */ #define RQ_RN 0xF /* register */ #define RQ_V_LNT 4 /* length */ #define RQ_M_LNT 0x7 /* 0,1,2,3,4 */ #define RQ_DIR 0x800 /* 0 = -, 1 = + */ #define RQ_REC(d,r) (((d) << RQ_V_LNT) | (r)) #define RQ_GETRN(x) ((x) & RQ_RN) #define RQ_GETLNT(x) (((x) >> RQ_V_LNT) & RQ_M_LNT) /* Address space */ #define VAMASK 0xFFFFFFFF /* virt addr mask */ #define PAWIDTH 30 /* phys addr width */ #define PASIZE (1 << PAWIDTH) /* phys addr size */ #define PAMASK (PASIZE - 1) /* phys addr mask */ #define IOPAGE (1 << (PAWIDTH - 1)) /* start of I/O page */ /* Architectural constants */ #define BMASK 0x000000FF /* byte */ #define BSIGN 0x00000080 #define WMASK 0x0000FFFF /* word */ #define WSIGN 0x00008000 #define LMASK 0xFFFFFFFF /* longword */ #define LSIGN 0x80000000 #define FPSIGN 0x00008000 /* floating point */ #define L_BYTE 1 /* bytes per */ #define L_WORD 2 /* data type */ #define L_LONG 4 #define L_QUAD 8 #define L_OCTA 16 #define NUM_INST 512 /* one byte+two byte */ #define MAX_SPEC 6 /* max spec/instr */ /* Floating point formats */ #define FD_V_EXP 7 /* f/d exponent */ #define FD_M_EXP 0xFF #define FD_BIAS 0x80 /* f/d bias */ #define FD_EXP (FD_M_EXP << FD_V_EXP) #define FD_HB (1 << FD_V_EXP) /* f/d hidden bit */ #define FD_GUARD (15 - FD_V_EXP) /* # guard bits */ #define FD_GETEXP(x) (((x) >> FD_V_EXP) & FD_M_EXP) #define G_V_EXP 4 /* g exponent */ #define G_M_EXP 0x7FF #define G_BIAS 0x400 /* g bias */ #define G_EXP (G_M_EXP << G_V_EXP) #define G_HB (1 << G_V_EXP) /* g hidden bit */ #define G_GUARD (15 - G_V_EXP) /* # guard bits */ #define G_GETEXP(x) (((x) >> G_V_EXP) & G_M_EXP) #define H_V_EXP 0 /* h exponent */ #define H_M_EXP 0x7FFF #define H_BIAS 0x4000 /* h bias */ #define H_EXP (H_M_EXP << H_V_EXP) #define H_HB (1 << H_V_EXP) /* h hidden bit */ #define H_GUARD (15 - H_V_EXP) /* # guard bits */ #define H_GETEXP(x) (((x) >> H_V_EXP) & H_M_EXP) /* Memory management modes */ #define KERN 0 #define EXEC 1 #define SUPV 2 #define USER 3 /* Register and stack aliases */ #define nAP 12 #define nFP 13 #define nSP 14 #define nPC 15 #define AP R[nAP] #define FP R[nFP] #define SP R[nSP] #define PC R[nPC] #define RGMASK 0xF #define KSP STK[KERN] #define ESP STK[EXEC] #define SSP STK[SUPV] #define USP STK[USER] #define IS STK[4] /* PSL, PSW, and condition codes */ #define PSL_V_CM 31 /* compatibility mode */ #define PSL_CM (1u << PSL_V_CM) #define PSL_V_TP 30 /* trace pending */ #define PSL_TP (1 << PSL_V_TP) #define PSL_V_FPD 27 /* first part done */ #define PSL_FPD (1 << PSL_V_FPD) #define PSL_V_IS 26 /* interrupt stack */ #define PSL_IS (1 << PSL_V_IS) #define PSL_V_CUR 24 /* current mode */ #define PSL_V_PRV 22 /* previous mode */ #define PSL_M_MODE 0x3 /* mode mask */ #define PSL_CUR (PSL_M_MODE << PSL_V_CUR) #define PSL_PRV (PSL_M_MODE << PSL_V_PRV) #define PSL_V_IPL 16 /* int priority lvl */ #define PSL_M_IPL 0x1F #define PSL_IPL (PSL_M_IPL << PSL_V_IPL) #define PSL_IPL1 (0x01 << PSL_V_IPL) #define PSL_IPL1F (0x1F << PSL_V_IPL) #define PSL_MBZ (0x30200000 | PSW_MBZ) /* must be zero */ #define PSW_MBZ 0xFF00 /* must be zero */ #define PSW_DV 0x80 /* dec ovflo enable */ #define PSW_FU 0x40 /* flt undflo enable */ #define PSW_IV 0x20 /* int ovflo enable */ #define PSW_T 0x10 /* trace enable */ #define CC_N 0x08 /* negative */ #define CC_Z 0x04 /* zero */ #define CC_V 0x02 /* overflow */ #define CC_C 0x01 /* carry */ #define CC_MASK (CC_N | CC_Z | CC_V | CC_C) #define PSL_GETCUR(x) (((x) >> PSL_V_CUR) & PSL_M_MODE) #define PSL_GETPRV(x) (((x) >> PSL_V_PRV) & PSL_M_MODE) #define PSL_GETIPL(x) (((x) >> PSL_V_IPL) & PSL_M_IPL) /* Software interrupt summary register */ #define SISR_MASK 0xFFFE #define SISR_2 (1 << 2) /* AST register */ #define AST_MASK 7 #define AST_MAX 4 /* Virtual address */ #define VA_N_OFF 9 /* offset size */ #define VA_PAGSIZE (1u << VA_N_OFF) /* page size */ #define VA_M_OFF ((1u << VA_N_OFF) - 1) /* offset mask */ #define VA_V_VPN VA_N_OFF /* vpn start */ #define VA_N_VPN (31 - VA_N_OFF) /* vpn size */ #define VA_M_VPN ((1u << VA_N_VPN) - 1) /* vpn mask */ #define VA_S0 (1u << 31) /* S0 space */ #define VA_P1 (1u << 30) /* P1 space */ #define VA_N_TBI 12 /* TB index size */ #define VA_TBSIZE (1u << VA_N_TBI) /* TB size */ #define VA_M_TBI ((1u << VA_N_TBI) - 1) /* TB index mask */ #define VA_GETOFF(x) ((x) & VA_M_OFF) #define VA_GETVPN(x) (((x) >> VA_V_VPN) & VA_M_VPN) #define VA_GETTBI(x) ((x) & VA_M_TBI) /* PTE */ #define PTE_V_V 31 /* valid */ #define PTE_V (1u << PTE_V_V) #define PTE_V_ACC 27 /* access */ #define PTE_M_ACC 0xF #define PTE_ACC (PTE_M_ACC << PTE_V_ACC) #define PTE_V_M 26 /* modified */ #define PTE_M (1u << PTE_V_M) #define PTE_GETACC(x) (((x) >> PTE_V_ACC) & PTE_M_ACC) /* TLB entry */ #define TLB_V_RACC 0 /* rd acc field */ #define TLB_V_WACC 4 /* wr acc field */ #define TLB_M_ACC 0xF #define TLB_RACC (TLB_M_ACC << TLB_V_RACC) #define TLB_WACC (TLB_M_ACC << TLB_V_WACC) #define TLB_V_M 8 /* m bit */ #define TLB_M (1u << TLB_V_M) #define TLB_N_PFN (PAWIDTH - VA_N_OFF) /* ppfn size */ #define TLB_M_PFN ((1u << TLB_N_PFN) - 1) /* ppfn mask */ #define TLB_PFN (TLB_M_PFN << VA_V_VPN) /* Traps and interrupt requests */ #define TIR_V_IRQL 0 /* int request lvl */ #define TIR_V_TRAP 5 /* trap requests */ #define TIR_M_TRAP 07 #define TIR_TRAP (TIR_M_TRAP << TIR_V_TRAP) #define TRAP_INTOV (1 << TIR_V_TRAP) /* integer overflow */ #define TRAP_DIVZRO (2 << TIR_V_TRAP) /* divide by zero */ #define TRAP_FLTOVF (3 << TIR_V_TRAP) /* flt overflow */ #define TRAP_FLTDIV (4 << TIR_V_TRAP) /* flt/dec div by zero */ #define TRAP_FLTUND (5 << TIR_V_TRAP) /* flt underflow */ #define TRAP_DECOVF (6 << TIR_V_TRAP) /* decimal overflow */ #define TRAP_SUBSCR (7 << TIR_V_TRAP) /* subscript range */ #define SET_TRAP(x) trpirq = (trpirq & PSL_M_IPL) | (x) #define CLR_TRAPS trpirq = trpirq & ~TIR_TRAP #define SET_IRQL trpirq = (trpirq & TIR_TRAP) | eval_int () #define GET_TRAP(x) (((x) >> TIR_V_TRAP) & TIR_M_TRAP) #define GET_IRQL(x) (((x) >> TIR_V_IRQL) & PSL_M_IPL) /* Floating point fault parameters */ #define FLT_OVRFLO 0x8 /* flt overflow */ #define FLT_DIVZRO 0x9 /* flt div by zero */ #define FLT_UNDFLO 0xA /* flt underflow */ /* Compatability mode fault parameters */ #define CMODE_RSVI 0x0 /* reserved instr */ #define CMODE_BPT 0x1 /* BPT */ #define CMODE_IOT 0x2 /* IOT */ #define CMODE_EMT 0x3 /* EMT */ #define CMODE_TRAP 0x4 /* TRAP */ #define CMODE_ILLI 0x5 /* illegal instr */ #define CMODE_ODD 0x6 /* odd address */ /* EDITPC suboperators */ #define EO_END 0x00 /* end */ #define EO_END_FLOAT 0x01 /* end float */ #define EO_CLR_SIGNIF 0x02 /* clear signif */ #define EO_SET_SIGNIF 0x03 /* set signif */ #define EO_STORE_SIGN 0x04 /* store sign */ #define EO_LOAD_FILL 0x40 /* load fill */ #define EO_LOAD_SIGN 0x41 /* load sign */ #define EO_LOAD_PLUS 0x42 /* load sign if + */ #define EO_LOAD_MINUS 0x43 /* load sign if - */ #define EO_INSERT 0x44 /* insert */ #define EO_BLANK_ZERO 0x45 /* blank zero */ #define EO_REPL_SIGN 0x46 /* replace sign */ #define EO_ADJUST_LNT 0x47 /* adjust length */ #define EO_FILL 0x80 /* fill */ #define EO_MOVE 0x90 /* move */ #define EO_FLOAT 0xA0 /* float */ #define EO_RPT_MASK 0x0F /* rpt mask */ #define EO_RPT_FLAG 0x80 /* rpt flag */ /* EDITPC R2 packup parameters */ #define ED_V_CC 16 /* condition codes */ #define ED_M_CC 0xFF #define ED_CC (ED_M_CC << ED_V_CC) #define ED_V_SIGN 8 /* sign */ #define ED_M_SIGN 0xFF #define ED_SIGN (ED_M_SIGN << ED_V_SIGN) #define ED_V_FILL 0 /* fill */ #define ED_M_FILL 0xFF #define ED_FILL (ED_M_FILL << ED_V_FILL) #define ED_GETCC(x) (((x) >> ED_V_CC) & CC_MASK) #define ED_GETSIGN(x) (((x) >> ED_V_SIGN) & ED_M_SIGN) #define ED_GETFILL(x) (((x) >> ED_V_FILL) & ED_M_FILL) #define ED_PUTCC(r,x) (((r) & ~ED_CC) | (((x) << ED_V_CC) & ED_CC)) #define ED_PUTSIGN(r,x) (((r) & ~ED_SIGN) | (((x) << ED_V_SIGN) & ED_SIGN)) #define ED_PUTFILL(r,x) (((r) & ~ED_FILL) | (((x) << ED_V_FILL) & ED_FILL)) /* SCB offsets */ #define SCB_MCHK 0x04 /* machine chk */ #define SCB_KSNV 0x08 /* ker stk invalid */ #define SCB_PWRFL 0x0C /* power fail */ #define SCB_RESIN 0x10 /* rsvd/priv instr */ #define SCB_XFC 0x14 /* XFC instr */ #define SCB_RESOP 0x18 /* rsvd operand */ #define SCB_RESAD 0x1C /* rsvd addr mode */ #define SCB_ACV 0x20 /* ACV */ #define SCB_TNV 0x24 /* TNV */ #define SCB_TP 0x28 /* trace pending */ #define SCB_BPT 0x2C /* BPT instr */ #define SCB_CMODE 0x30 /* comp mode fault */ #define SCB_ARITH 0x34 /* arith fault */ #define SCB_CHMK 0x40 /* CHMK */ #define SCB_CHME 0x44 /* CHME */ #define SCB_CHMS 0x48 /* CHMS */ #define SCB_CHMU 0x4C /* CHMU */ #define SCB_CRDERR 0x54 /* CRD err intr */ #define SCB_MEMERR 0x60 /* mem err intr */ #define SCB_IPLSOFT 0x80 /* software intr */ #define SCB_INTTIM 0xC0 /* timer intr */ #define SCB_EMULATE 0xC8 /* emulation */ #define SCB_EMULFPD 0xCC /* emulation, FPD */ #define SCB_CSI 0xF0 /* constor input */ #define SCB_CSO 0xF4 /* constor output */ #define SCB_TTI 0xF8 /* console input */ #define SCB_TTO 0xFC /* console output */ #define SCB_INTR 0x100 /* hardware intr */ #define IPL_HLTPIN 0x1F /* halt pin IPL */ #define IPL_MEMERR 0x1D /* mem err IPL */ #define IPL_CRDERR 0x1A /* CRD err IPL */ /* Interrupt and exception types */ #define IE_SVE -1 /* severe exception */ #define IE_EXC 0 /* normal exception */ #define IE_INT 1 /* interrupt */ /* Decode ROM: opcode entry */ #define DR_F 0x80 /* FPD ok flag */ #define DR_NSPMASK 0x07 /* #specifiers */ #define DR_V_USPMASK 4 #define DR_M_USPMASK 0x70 /* #spec, sym_ */ #define DR_GETNSP(x) ((x) & DR_NSPMASK) #define DR_GETUSP(x) (((x) >> DR_V_USPMASK) & DR_M_USPMASK) /* Decode ROM: specifier entry */ #define DR_ACMASK 0x300 /* type */ #define DR_SPFLAG 0x008 /* special decode */ #define DR_LNMASK 0x007 /* length mask */ #define DR_LNT(x) (1 << (x & DR_LNMASK)) /* disp to lnt */ /* Decode ROM: length */ #define DR_BYTE 0x000 /* byte */ #define DR_WORD 0x001 /* word */ #define DR_LONG 0x002 /* long */ #define DR_QUAD 0x003 /* quad */ #define DR_OCTA 0x004 /* octa */ /* Decode ROM: operand type */ #define SH0 0x000 /* short literal */ #define SH1 0x010 #define SH2 0x020 #define SH3 0x030 #define IDX 0x040 /* indexed */ #define GRN 0x050 /* register */ #define RGD 0x060 /* register def */ #define ADC 0x070 /* autodecrement */ #define AIN 0x080 /* autoincrement */ #define AID 0x090 /* autoinc def */ #define BDP 0x0A0 /* byte disp */ #define BDD 0x0B0 /* byte disp def */ #define WDP 0x0C0 /* word disp */ #define WDD 0x0D0 /* word disp def */ #define LDP 0x0E0 /* long disp */ #define LDD 0x0F0 /* long disp def */ /* Decode ROM: access type */ #define DR_R 0x000 /* read */ #define DR_M 0x100 /* modify */ #define DR_A 0x200 /* address */ #define DR_W 0x300 /* write */ /* Decode ROM: access type and length */ #define RB (DR_R|DR_BYTE) #define RW (DR_R|DR_WORD) #define RL (DR_R|DR_LONG) #define RQ (DR_R|DR_QUAD) #define RO (DR_R|DR_OCTA) #define MB (DR_M|DR_BYTE) #define MW (DR_M|DR_WORD) #define ML (DR_M|DR_LONG) #define MQ (DR_M|DR_QUAD) #define MO (DR_M|DR_OCTA) #define AB (DR_A|DR_BYTE) #define AW (DR_A|DR_WORD) #define AL (DR_A|DR_LONG) #define AQ (DR_A|DR_QUAD) #define AO (DR_A|DR_OCTA) #define WB (DR_W|DR_BYTE) #define WW (DR_W|DR_WORD) #define WL (DR_W|DR_LONG) #define WQ (DR_W|DR_QUAD) #define WO (DR_W|DR_OCTA) /* Special dispatches. vb = variable bit field, treated as wb except for register rf = f_floating, treated as rl except for short literal rd = d_floating, treated as rq except for short literal rg = g_floating, treated as rq except for short literal rh = h_floating, treated as ro except for short literal bb = branch byte displacement bw = branch word displacement Length field must be correct */ #define VB (DR_SPFLAG|WB) /* .vb */ #define RF (DR_SPFLAG|RL) /* .rf */ #define RD (DR_SPFLAG|RQ) /* .rd */ #define RG (DR_SPFLAG|MQ) /* .rg */ #define RH (DR_SPFLAG|RO) /* .rh */ #define BB (DR_SPFLAG|WB|6) /* byte branch */ #define BW (DR_SPFLAG|WB|7) /* word branch */ /* Probe results and memory management fault codes */ #define PR_ACV 0 /* ACV */ #define PR_LNV 1 /* length viol */ #define PR_PACV 2 /* pte ACV (780) */ #define PR_PLNV 3 /* pte len viol */ #define PR_TNV 4 /* TNV */ /* #define PR_TB 5 /* impossible */ #define PR_PTNV 6 /* pte TNV */ #define PR_OK 7 /* ok */ #define MM_PARAM(w,p) (((w)? 4: 0) | ((p) & 3)) /* fault param */ /* Memory management errors */ #define MM_WRITE 4 /* write */ #define MM_EMASK 3 /* against probe */ /* Privileged registers */ #define MT_KSP 0 #define MT_ESP 1 #define MT_SSP 2 #define MT_USP 3 #define MT_IS 4 #define MT_P0BR 8 #define MT_P0LR 9 #define MT_P1BR 10 #define MT_P1LR 11 #define MT_SBR 12 #define MT_SLR 13 #define MT_PCBB 16 #define MT_SCBB 17 #define MT_IPL 18 #define MT_ASTLVL 19 #define MT_SIRR 20 #define MT_SISR 21 #define MT_ICCS 24 #define MT_NICR 25 #define MT_ICR 26 #define MT_TODR 27 #define MT_CSRS 28 #define MT_CSRD 29 #define MT_CSTS 30 #define MT_CSTD 31 #define MT_RXCS 32 #define MT_RXDB 33 #define MT_TXCS 34 #define MT_TXDB 35 #define MT_MAPEN 56 #define MT_TBIA 57 #define MT_TBIS 58 #define MT_PME 61 #define MT_SID 62 #define MT_TBCHK 63 #define BR_MASK 0xFFFFFFFC #define LR_MASK 0x003FFFFF /* Opcodes */ enum opcodes { HALT, NOP, REI, BPT, RET, RSB, LDPCTX, SVPCTX, CVTPS, CVTSP, INDEX, CRC, PROBER, PROBEW, INSQUE, REMQUE, BSBB, BRB, BNEQ, BEQL, BGTR, BLEQ, JSB, JMP, BGEQ, BLSS, BGTRU, BLEQU, BVC, BVS, BGEQU, BLSSU, ADDP4, ADDP6, SUBP4, SUBP6, CVTPT, MULP, CVTTP, DIVP, MOVC3, CMPC3, SCANC, SPANC, MOVC5, CMPC5, MOVTC, MOVTUC, BSBW, BRW, CVTWL, CVTWB, MOVP, CMPP3, CVTPL, CMPP4, EDITPC, MATCHC, LOCC, SKPC, MOVZWL, ACBW, MOVAW, PUSHAW, ADDF2, ADDF3, SUBF2, SUBF3, MULF2, MULF3, DIVF2, DIVF3, CVTFB, CVTFW, CVTFL, CVTRFL, CVTBF, CVTWF, CVTLF, ACBF, MOVF, CMPF, MNEGF, TSTF, EMODF, POLYF, CVTFD, ADAWI = 0x58, INSQHI = 0x5C, INSQTI, REMQHI, REMQTI, ADDD2, ADDD3, SUBD2, SUBD3, MULD2, MULD3, DIVD2, DIVD3, CVTDB, CVTDW, CVTDL, CVTRDL, CVTBD, CVTWD, CVTLD, ACBD, MOVD, CMPD, MNEGD, TSTD, EMODD, POLYD, CVTDF, ASHL = 0x78, ASHQ, EMUL, EDIV, CLRQ, MOVQ, MOVAQ, PUSHAQ, ADDB2, ADDB3, SUBB2, SUBB3, MULB2, MULB3, DIVB2, DIVB3, BISB2, BISB3, BICB2, BICB3, XORB2, XORB3, MNEGB, CASEB, MOVB, CMPB, MCOMB, BITB, CLRB, TSTB, INCB, DECB, CVTBL, CVTBW, MOVZBL, MOVZBW, ROTL, ACBB, MOVAB, PUSHAB, ADDW2, ADDW3, SUBW2, SUBW3, MULW2, MULW3, DIVW2, DIVW3, BISW2, BISW3, BICW2, BICW3, XORW2, XORW3, MNEGW, CASEW, MOVW, CMPW, MCOMW, BITW, CLRW, TSTW, INCW, DECW, BISPSW, BICPSW, POPR, PUSHR, CHMK, CHME, CHMS, CHMU, ADDL2, ADDL3, SUBL2, SUBL3, MULL2, MULL3, DIVL2, DIVL3, BISL2, BISL3, BICL2, BICL3, XORL2, XORL3, MNEGL, CASEL, MOVL, CMPL, MCOML, BITL, CLRL, TSTL, INCL, DECL, ADWC, SBWC, MTPR, MFPR, MOVPSL, PUSHL, MOVAL, PUSHAL, BBS, BBC, BBSS, BBCS, BBSC, BBCC, BBSSI, BBCCI, BLBS, BLBC, FFS, FFC, CMPV, CMPZV, EXTV, EXTZV, INSV, ACBL, AOBLSS, AOBLEQ, SOBGEQ, SOBGTR, CVTLB, CVTLW, ASHP, CVTLP, CALLG, CALLS, XFC, CVTDH = 0x132, CVTGF = 0x133, ADDG2 = 0x140, ADDG3, SUBG2, SUBG3, MULG2, MULG3, DIVG2, DIVG3, CVTGB, CVTGW, CVTGL, CVTRGL, CVTBG, CVTWG, CVTLG, ACBG, MOVG, CMPG, MNEGG, TSTG, EMODG, POLYG, CVTGH, ADDH2 = 0x160, ADDH3, SUBH2, SUBH3, MULH2, MULH3, DIVH2, DIVH3, CVTHB, CVTHW, CVTHL, CVTRHL, CVTBH, CVTWH, CVTLH, ACBH, MOVH, CMPH, MNEGH, TSTH, EMODH, POLYH, CVTHG, CLRO = 0x17C, MOVO, MOVAO, PUSHAO, CVTFH = 0x198, CVTFG = 0x199, CVTHF = 0x1F6, CVTHD = 0x1F7 }; /* Repeated operations */ #define SXTB(x) (((x) & BSIGN)? ((x) | ~BMASK): ((x) & BMASK)) #define SXTW(x) (((x) & WSIGN)? ((x) | ~WMASK): ((x) & WMASK)) #define SXTBW(x) (((x) & BSIGN)? ((x) | (WMASK - BMASK)): ((x) & BMASK)) #define SXTL(x) (((x) & LSIGN)? ((x) | ~LMASK): ((x) & LMASK)) #define INTOV if (PSL & PSW_IV) SET_TRAP (TRAP_INTOV) #define V_INTOV cc = cc | CC_V; INTOV #define NEG(x) ((~(x) + 1) & LMASK) /* Istream access */ #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] = fault_PC #define GET_ISTR(d,l) d = get_istr (l, acc) #define BRANCHB(d) PCQ_ENTRY, PC = PC + SXTB (d), FLUSH_ISTR #define BRANCHW(d) PCQ_ENTRY, PC = PC + SXTW (d), FLUSH_ISTR #define JUMP(d) PCQ_ENTRY, PC = (d), FLUSH_ISTR #define CMODE_JUMP(d) PCQ_ENTRY, PC = (d) #define SETPC(d) PC = (d), FLUSH_ISTR #define FLUSH_ISTR ibcnt = 0, ppc = -1 /* Character string instructions */ #define STR_V_DPC 24 /* delta PC */ #define STR_M_DPC 0xFF #define STR_V_CHR 16 /* char argument */ #define STR_M_CHR 0xFF #define STR_LNMASK 0xFFFF /* string length */ #define STR_GETDPC(x) (((x) >> STR_V_DPC) & STR_M_DPC) #define STR_GETCHR(x) (((x) >> STR_V_CHR) & STR_M_CHR) #define STR_PACK(m,x) ((((PC - fault_PC) & STR_M_DPC) << STR_V_DPC) | \ (((m) & STR_M_CHR) << STR_V_CHR) | ((x) & STR_LNMASK)) /* Read and write */ #define RA (acc) #define WA ((acc) << TLB_V_WACC) #define ACC_MASK(x) (1 << (x)) #define TLB_ACCR(x) (ACC_MASK (x) << TLB_V_RACC) #define TLB_ACCW(x) (ACC_MASK (x) << TLB_V_WACC) #define REF_V 0 #define REF_P 1 /* Condition code macros */ #define CC_ZZ1P cc = CC_Z | (cc & CC_C) #define CC_IIZZ_B(r) \ if ((r) & BSIGN) cc = CC_N; \ else if ((r) == 0) cc = CC_Z; \ else cc = 0 #define CC_IIZZ_W(r) \ if ((r) & WSIGN) cc = CC_N; \ else if ((r) == 0) cc = CC_Z; \ else cc = 0 #define CC_IIZZ_L(r) \ if ((r) & LSIGN) cc = CC_N; \ else if ((r) == 0) cc = CC_Z; \ else cc = 0 #define CC_IIZZ_Q(rl,rh) \ if ((rh) & LSIGN) cc = CC_N; \ else if (((rl) | (rh)) == 0) cc = CC_Z; \ else cc = 0 #define CC_IIZZ_FP CC_IIZZ_W #define CC_IIZP_B(r) \ if ((r) & BSIGN) cc = CC_N | (cc & CC_C); \ else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ else cc = cc & CC_C #define CC_IIZP_W(r) \ if ((r) & WSIGN) cc = CC_N | (cc & CC_C); \ else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ else cc = cc & CC_C #define CC_IIZP_L(r) \ if ((r) & LSIGN) cc = CC_N | (cc & CC_C); \ else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ else cc = cc & CC_C #define CC_IIZP_Q(rl,rh) \ if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ else if (((rl) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ else cc = cc & CC_C #define CC_IIZP_O(rl,rm2,rm1,rh) \ if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ else if (((rl) | (rm2) | (rm1) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ else cc = cc & CC_C #define CC_IIZP_FP CC_IIZP_W #define V_ADD_B(r,s1,s2) \ if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & BSIGN) { V_INTOV; } #define V_ADD_W(r,s1,s2) \ if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & WSIGN) { V_INTOV; } #define V_ADD_L(r,s1,s2) \ if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & LSIGN) { V_INTOV; } #define C_ADD(r,s1,s2) \ if (((uint32) r) < ((uint32) s2)) cc = cc | CC_C #define CC_ADD_B(r,s1,s2) \ CC_IIZZ_B (r); \ V_ADD_B (r, s1, s2); \ C_ADD (r, s1, s2) #define CC_ADD_W(r,s1,s2) \ CC_IIZZ_W (r); \ V_ADD_W (r, s1, s2); \ C_ADD (r, s1, s2) #define CC_ADD_L(r,s1,s2) \ CC_IIZZ_L (r); \ V_ADD_L (r, s1, s2); \ C_ADD (r, s1, s2) #define V_SUB_B(r,s1,s2) \ if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & BSIGN) { V_INTOV; } #define V_SUB_W(r,s1,s2) \ if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & WSIGN) { V_INTOV; } #define V_SUB_L(r,s1,s2) \ if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & LSIGN) { V_INTOV; } #define C_SUB(r,s1,s2) \ if (((uint32) s2) < ((uint32) s1)) cc = cc | CC_C #define CC_SUB_B(r,s1,s2) \ CC_IIZZ_B (r); \ V_SUB_B (r, s1, s2); \ C_SUB (r, s1, s2) #define CC_SUB_W(r,s1,s2) \ CC_IIZZ_W (r); \ V_SUB_W (r, s1, s2); \ C_SUB (r, s1, s2) #define CC_SUB_L(r,s1,s2) \ CC_IIZZ_L (r); \ V_SUB_L (r, s1, s2); \ C_SUB (r, s1, s2) #define CC_CMP_B(s1,s2) \ if (SXTB (s1) < SXTB (s2)) cc = CC_N; \ else if ((s1) == (s2)) cc = CC_Z; \ else cc = 0; \ if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C #define CC_CMP_W(s1,s2) \ if (SXTW (s1) < SXTW (s2)) cc = CC_N; \ else if ((s1) == (s2)) cc = CC_Z; \ else cc = 0; \ if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C #define CC_CMP_L(s1,s2) \ if ((s1) < (s2)) cc = CC_N; \ else if ((s1) == (s2)) cc = CC_Z; \ else cc = 0; \ if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C /* Model dependent definitions */ #if defined (VAX_780) #include "vax780_defs.h" #else #include "vaxmod_defs.h" #endif #endif /* _VAX_DEFS_H */ simh-3.8.1/VAX/vax_cmode.c0000644000175000017500000013420311112311676013412 0ustar vlmvlm/* vax_cmode.c: VAX compatibility mode Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. On a full VAX, this module implements PDP-11 compatibility mode. On a subset VAX, this module forces a fault if REI attempts to set PSL. 28-May-08 RMS Inlined physical memory routines 25-Jan-08 RMS Fixed declaration (from Mark Pizzolato) 03-May-06 RMS Fixed omission of SXT Fixed order of operand fetching in XOR 24-Aug-04 RMS Cloned from PDP-11 CPU In compatibility mode, the Istream prefetch mechanism is not used. The prefetcher will be explicitly resynchronized through intexc on any exit from compatibility mode. */ #include "vax_defs.h" #if defined (FULL_VAX) #define RdMemB(a) Read (a, L_BYTE, RA) #define RdMemMB(a) Read (a, L_BYTE, WA) #define WrMemB(d,a) Write (a, d, L_BYTE, WA) #define BRANCH_F(x) CMODE_JUMP ((PC + (((x) + (x)) & BMASK)) & WMASK) #define BRANCH_B(x) CMODE_JUMP ((PC + (((x) + (x)) | 0177400)) & WMASK) #define CC_XOR_NV(x) ((((x) & CC_N) != 0) ^ (((x) & CC_V) != 0)) #define CC_XOR_NC(x) ((((x) & CC_N) != 0) ^ (((x) & CC_C) != 0)) extern int32 R[16]; extern int32 PSL; extern int32 trpirq; extern int32 p1; extern int32 fault_PC; extern int32 recq[]; /* recovery queue */ extern int32 recqptr; /* recq pointer */ extern int32 pcq[]; extern int32 pcq_p; extern int32 ibcnt, ppc; extern int32 sim_interval; extern uint32 sim_brk_summ; extern jmp_buf save_env; int32 GeteaB (int32 spec); int32 GeteaW (int32 spec); int32 RdMemW (int32 a); int32 RdMemMW (int32 a); void WrMemW (int32 d, int32 a); int32 RdRegB (int32 rn); int32 RdRegW (int32 rn); void WrRegB (int32 val, int32 rn); void WrRegW (int32 val, int32 rn); /* Validate PSL for compatibility mode */ t_bool BadCmPSL (int32 newpsl) { if ((newpsl & (PSL_FPD|PSL_IS|PSL_CUR|PSL_PRV|PSL_IPL)) != ((USER << PSL_V_CUR) | (USER << PSL_V_PRV))) return TRUE; else return FALSE; } /* Compatibility mode execution */ int32 op_cmode (int32 cc) { int32 IR, srcspec, dstspec, srcreg, dstreg, ea; int32 i, t, src, src2, dst, sign, oc; int32 acc = ACC_MASK (USER); PC = PC & WMASK; /* PC must be 16b */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ ABORT (STOP_IBKPT); /* stop simulation */ } sim_interval = sim_interval - 1; /* count instr */ IR = RdMemW (PC); /* fetch instruction */ PC = (PC + 2) & WMASK; /* incr PC, mod 65k */ srcspec = (IR >> 6) & 077; /* src, dst specs */ dstspec = IR & 077; srcreg = (srcspec <= 07); /* src, dst = rmode? */ dstreg = (dstspec <= 07); switch ((IR >> 12) & 017) { /* decode IR<15:12> */ /* Opcode 0: no operands, specials, branches, JSR, SOPs */ case 000: /* 00xxxx */ switch ((IR >> 6) & 077) { /* decode IR<11:6> */ case 000: /* 0000xx */ switch (IR) { /* decode IR<5:0> */ case 3: /* BPT */ CMODE_FAULT (CMODE_BPT); break; case 4: /* IOT */ CMODE_FAULT (CMODE_IOT); break; case 2: /* RTI */ case 6: /* RTT */ src = RdMemW (R[6] & WMASK); /* new PC */ src2 = RdMemW ((R[6] + 2) & WMASK); /* new PSW */ R[6] = (R[6] + 4) & WMASK; cc = src2 & CC_MASK; /* update cc, T */ if (src2 & PSW_T) PSL = PSL | PSW_T; else PSL = PSL & ~PSW_T; CMODE_JUMP (src); /* update PC */ break; default: /* undefined */ CMODE_FAULT (CMODE_RSVI); break; } /* end switch IR */ break; /* end case 0000xx */ case 001: /* JMP */ if (dstreg) /* mode 0 illegal */ CMODE_FAULT (CMODE_ILLI); else { CMODE_JUMP (GeteaW (dstspec)); } break; case 002: /* 0002xx */ if (IR < 000210) { /* RTS */ dstspec = dstspec & 07; if (dstspec != 7) { /* PC <- r */ CMODE_JUMP (RdRegW (dstspec)); } dst = RdMemW (R[6]); /* t <- (sp)+ */ R[6] = (R[6] + 2) & WMASK; WrRegW (dst, dstspec); /* r <- t */ break; /* end if RTS */ } if (IR < 000240) { /* [210:237] */ CMODE_FAULT (CMODE_RSVI); break; } if (IR < 000260) /* clear CC */ cc = cc & ~(IR & CC_MASK); else cc = cc | (IR & CC_MASK); /* set CC */ break; case 003: /* SWAB */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = ((src & BMASK) << 8) | ((src >> 8) & BMASK); if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_B ((dst & BMASK)); break; case 004: case 005: /* BR */ BRANCH_F (IR); break; case 006: case 007: /* BR */ BRANCH_B (IR); break; case 010: case 011: /* BNE */ if ((cc & CC_Z) == 0) { BRANCH_F (IR); } break; case 012: case 013: /* BNE */ if ((cc & CC_Z) == 0) { BRANCH_B (IR); } break; case 014: case 015: /* BEQ */ if (cc & CC_Z) { BRANCH_F (IR); } break; case 016: case 017: /* BEQ */ if (cc & CC_Z) { BRANCH_B (IR); } break; case 020: case 021: /* BGE */ if (CC_XOR_NV (cc) == 0) { BRANCH_F (IR); } break; case 022: case 023: /* BGE */ if (CC_XOR_NV (cc) == 0) { BRANCH_B (IR); } break; case 024: case 025: /* BLT */ if (CC_XOR_NV (cc)) { BRANCH_F (IR); } break; case 026: case 027: /* BLT */ if (CC_XOR_NV (cc)) { BRANCH_B (IR); } break; case 030: case 031: /* BGT */ if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { BRANCH_F (IR); } break; case 032: case 033: /* BGT */ if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { BRANCH_B (IR); } break; case 034: case 035: /* BLE */ if ((cc & CC_Z) || CC_XOR_NV (cc)) { BRANCH_F (IR); } break; case 036: case 037: /* BLE */ if ((cc & CC_Z) || CC_XOR_NV (cc)) { BRANCH_B (IR); } break; case 040: case 041: case 042: case 043: /* JSR */ case 044: case 045: case 046: case 047: if (dstreg) { /* mode 0 illegal */ CMODE_FAULT (CMODE_ILLI); } else { srcspec = srcspec & 07; /* get reg num */ dst = GeteaW (dstspec); /* get dst addr */ src = RdRegW (srcspec); /* get src reg */ WrMemW (src, (R[6] - 2) & WMASK); /* -(sp) <- r */ R[6] = (R[6] - 2) & WMASK; if (srcspec != 7) /* r <- PC */ WrRegW (PC, srcspec); CMODE_JUMP (dst); /* PC <- dst */ } break; /* end JSR */ case 050: /* CLR */ if (dstreg) WrRegW (0, dstspec); else WrMemW (0, GeteaW (dstspec)); cc = CC_Z; break; case 051: /* COM */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = src ^ WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); cc = cc | CC_C; break; case 052: /* INC */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src + 1) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZP_W (dst); if (dst == 0100000) cc = cc | CC_V; break; case 053: /* DEC */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src - 1) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZP_W (dst); if (dst == 077777) cc = cc | CC_V; break; case 054: /* NEG */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (-src) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (dst == 0100000) cc = cc | CC_V; if (dst) cc = cc | CC_C; break; case 055: /* ADC */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src + (cc & CC_C)) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if ((src == 077777) && (dst == 0100000)) cc = cc | CC_V; if ((src == 0177777) && (dst == 0)) cc = cc | CC_C; break; case 056: /* SBC */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src - (cc & CC_C)) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if ((src == 0100000) && (dst == 077777)) cc = cc | CC_V; if ((src == 0) && (dst == 0177777)) cc = cc | CC_C; break; case 057: /* TST */ if (dstreg) src = RdRegW (dstspec); else src = RdMemW (GeteaW (dstspec)); CC_IIZZ_W (src); break; case 060: /* ROR */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src >> 1) | ((cc & CC_C)? WSIGN: 0); if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (src & 1) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 061: /* ROL */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (src & WSIGN) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 062: /* ASR */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src & WSIGN) | (src >> 1); if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (src & 1) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 063: /* ASL */ if (dstreg) src = RdRegW (dstspec); else src = RdMemMW (ea = GeteaW (dstspec)); dst = (src << 1) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (src & WSIGN) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 065: /* MFPI */ if (dstreg) /* "mov dst,-(sp)" */ dst = RdRegW (dstspec); else dst = RdMemW (GeteaW (dstspec)); WrMemW (dst, (R[6] - 2) & WMASK); R[6] = (R[6] - 2) & WMASK; CC_IIZP_W (dst); break; case 066: /* MTPI */ dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ R[6] = (R[6] + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, 6); if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, (GeteaW (dstspec) & WMASK)); CC_IIZP_W (dst); break; case 067: /* SXT */ dst = (cc & CC_N)? 0177777: 0; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, GeteaW (dstspec)); CC_IIZP_W (dst); break; default: /* undefined */ CMODE_FAULT (CMODE_RSVI); break; } /* end switch SOPs */ break; /* end case 000 */ /* Opcodes 01 - 06: double operand word instructions Compatibility mode requires source address decode, source fetch, dest address decode, dest fetch/store. Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] */ case 001: /* MOV */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) WrRegW (src, dstspec); else WrMemW (src, GeteaW (dstspec)); CC_IIZP_W (src); break; case 002: /* CMP */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); dst = (src - src2) & WMASK; CC_IIZZ_W (dst); if (((src ^ src2) & (~src2 ^ dst)) & WSIGN) cc = cc | CC_V; if (src < src2) cc = cc | CC_C; break; case 003: /* BIT */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); dst = src2 & src; CC_IIZP_W (dst); break; case 004: /* BIC */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemMW (ea = GeteaW (dstspec)); dst = src2 & ~src; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZP_W (dst); break; case 005: /* BIS */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemMW (ea = GeteaW (dstspec)); dst = src2 | src; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZP_W (dst); break; case 006: /* ADD */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemMW (ea = GeteaW (dstspec)); dst = (src2 + src) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_ADD_W (dst, src, src2); break; /* Opcode 07: EIS, FIS (not implemented), CIS Notes: - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. - Divide has three error cases: 1. Divide by zero. 2. Divide largest negative number by -1. 3. (Signed) quotient doesn't fit in 16 bits. Cases 1 and 2 must be tested in advance, to avoid C runtime errors. - ASHx left: overflow if the bits shifted out do not equal the sign of the result (convert shift out to 1/0, xor against sign). - ASHx right: if right shift sign extends, then the shift and conditional or of shifted -1 is redundant. If right shift zero extends, then the shift and conditional or does sign extension. */ case 007: /* EIS */ srcspec = srcspec & 07; /* get src reg */ switch ((IR >> 9) & 07) { /* decode IR<11:9> */ case 0: /* MUL */ if (dstreg) /* get src2 */ src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); src = RdRegW (srcspec); /* get src */ if (src2 & WSIGN) /* sext src, src2 */ src2 = src2 | ~WMASK; if (src & WSIGN) src = src | ~WMASK; dst = src * src2; /* multiply */ WrRegW ((dst >> 16) & WMASK, srcspec); /* high 16b */ WrRegW (dst & WMASK, srcspec | 1); /* low 16b */ CC_IIZZ_L (dst & LMASK); if ((dst > 077777) || (dst < -0100000)) cc = cc | CC_C; break; case 1: /* DIV */ if (dstreg) /* get src2 */ src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); t = RdRegW (srcspec); src = (((uint32) t) << 16) | RdRegW (srcspec | 1); if (src2 == 0) { /* div by 0? */ cc = CC_V | CC_C; /* set cc's */ break; /* done */ } if ((src == LSIGN) && (src2 == WMASK)) { /* -2^31 / -1? */ cc = CC_V; /* overflow */ break; /* done */ } if (src2 & WSIGN) /* sext src, src2 */ src2 = src2 | ~WMASK; if (t & WSIGN) src = src | ~LMASK; dst = src / src2; /* divide */ if ((dst > 077777) || (dst < -0100000)) { /* out of range? */ cc = CC_V; /* overflow */ break; } CC_IIZZ_W (dst & WMASK); /* set cc's */ WrRegW (dst & WMASK, srcspec); /* quotient */ WrRegW ((src - (src2 * dst)) & WMASK, srcspec | 1); break; case 2: /* ASH */ if (dstreg) /* get src2 */ src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); src2 = src2 & 077; src = RdRegW (srcspec); /* get src */ if (sign = ((src & WSIGN)? 1: 0)) src = src | ~WMASK; if (src2 == 0) { /* [0] */ dst = src; /* result */ oc = 0; /* last bit out */ } else if (src2 <= 15) { /* [1,15] */ dst = src << src2; i = (src >> (16 - src2)) & WMASK; oc = (i & 1)? CC_C: 0; if ((dst & WSIGN)? (i != WMASK): (i != 0)) oc = oc | CC_V; } else if (src2 <= 31) { /* [16,31] */ dst = 0; oc = ((src << (src2 - 16)) & 1)? CC_C: 0; if (src) oc = oc | CC_V; } else if (src2 == 32) { /* [32] = -32 */ dst = -sign; oc = sign? CC_C: 0; } else { /* [33,63] = -31,-1 */ dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); oc = ((src >> (63 - src2)) & 1)? CC_C: 0; } WrRegW (dst = dst & WMASK, srcspec); /* result */ CC_IIZZ_W (dst); cc = cc | oc; break; case 3: /* ASHC */ if (dstreg) /* get src2 */ src2 = RdRegW (dstspec); else src2 = RdMemW (GeteaW (dstspec)); src2 = src2 & 077; t = RdRegW (srcspec); src = (((uint32) t) << 16) | RdRegW (srcspec | 1); sign = (t & WSIGN)? 1: 0; /* get src sign */ if (src2 == 0) { /* [0] */ dst = src; /* result */ oc = 0; /* last bit out */ } else if (src2 <= 31) { /* [1,31] */ dst = ((uint32) src) << src2; i = ((src >> (32 - src2)) | (-sign << src2)) & LMASK; oc = (i & 1)? CC_C: 0; if ((dst & LSIGN)? (i != LMASK): (i != 0)) oc = oc | CC_V; } else if (src2 == 32) { /* [32] = -32 */ dst = -sign; oc = sign? CC_C: 0; } else { /* [33,63] = -31,-1 */ dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); oc = ((src >> (63 - src2)) & 1)? CC_C: 0; } WrRegW ((dst >> 16) & WMASK, srcspec); /* high result */ WrRegW (dst & WMASK, srcspec | 1); /* low result */ CC_IIZZ_L (dst & LMASK); cc = cc | oc; break; case 4: /* XOR */ src = RdRegW (srcspec); /* get src */ if (dstreg) /* get dst */ src2 = RdRegW (dstspec); else src2 = RdMemMW (ea = GeteaW (dstspec)); dst = src2 ^ src; if (dstreg) /* result */ WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZP_W (dst); break; case 7: /* SOB */ dst = (RdRegW (srcspec) - 1) & WMASK; /* decr reg */ WrRegW (dst, srcspec); /* result */ if (dst != 0) { /* br if zero */ CMODE_JUMP ((PC - dstspec - dstspec) & WMASK); } break; default: CMODE_FAULT (CMODE_RSVI); /* end switch EIS */ } break; /* end case 007 */ /* Opcode 10: branches, traps, SOPs */ case 010: switch ((IR >> 6) & 077) { /* decode IR<11:6> */ case 000: case 001: /* BPL */ if ((cc & CC_N) == 0) { BRANCH_F (IR); } break; case 002: case 003: /* BPL */ if ((cc & CC_N) == 0) { BRANCH_B (IR); } break; case 004: case 005: /* BMI */ if (cc & CC_N) { BRANCH_F (IR); } break; case 006: case 007: /* BMI */ if (cc & CC_N) { BRANCH_B (IR); } break; case 010: case 011: /* BHI */ if ((cc & (CC_C | CC_Z)) == 0) { BRANCH_F (IR); } break; case 012: case 013: /* BHI */ if ((cc & (CC_C | CC_Z)) == 0) { BRANCH_B (IR); } break; case 014: case 015: /* BLOS */ if (cc & (CC_C | CC_Z)) { BRANCH_F (IR); } break; case 016: case 017: /* BLOS */ if (cc & (CC_C | CC_Z)) { BRANCH_B (IR); } break; case 020: case 021: /* BVC */ if ((cc & CC_V) == 0) { BRANCH_F (IR); } break; case 022: case 023: /* BVC */ if ((cc & CC_V) == 0) { BRANCH_B (IR); } break; case 024: case 025: /* BVS */ if (cc & CC_V) { BRANCH_F (IR); } break; case 026: case 027: /* BVS */ if (cc & CC_V) { BRANCH_B (IR); } break; case 030: case 031: /* BCC */ if ((cc & CC_C) == 0) { BRANCH_F (IR); } break; case 032: case 033: /* BCC */ if ((cc & CC_C) == 0) { BRANCH_B (IR); } break; case 034: case 035: /* BCS */ if (cc & CC_C) { BRANCH_F (IR); } break; case 036: case 037: /* BCS */ if (cc & CC_C) { BRANCH_B (IR); } break; case 040: case 041: case 042: case 043: /* EMT */ CMODE_FAULT (CMODE_EMT); break; case 044: case 045: case 046: case 047: /* TRAP */ CMODE_FAULT (CMODE_TRAP); break; case 050: /* CLRB */ if (dstreg) WrRegB (0, dstspec); else WrMemB (0, GeteaB (dstspec)); cc = CC_Z; break; case 051: /* COMB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = src ^ BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); cc = cc | CC_C; break; case 052: /* INCB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src + 1) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZP_B (dst); if (dst == 0200) cc = cc | CC_V; break; case 053: /* DECB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src - 1) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZP_B (dst); if (dst == 0177) cc = cc | CC_V; break; case 054: /* NEGB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (-src) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if (dst == 0200) cc = cc | CC_V; if (dst) cc = cc | CC_C; break; case 055: /* ADCB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src + (cc & CC_C)) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if ((src == 0177) && (dst == 0200)) cc = cc | CC_V; if ((src == 0377) && (dst == 0)) cc = cc | CC_C; break; case 056: /* SBCB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src - (cc & CC_C)) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if ((src == 0200) && (dst == 0177)) cc = cc | CC_V; if ((src == 0) && (dst == 0377)) cc = cc | CC_C; break; case 057: /* TSTB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemB (GeteaB (dstspec)); CC_IIZZ_B (src); break; case 060: /* RORB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src >> 1) | ((cc & CC_C)? BSIGN: 0); if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if (src & 1) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 061: /* ROLB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if (src & BSIGN) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 062: /* ASRB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src >> 1) | (src & BSIGN); if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if (src & 1) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 063: /* ASLB */ if (dstreg) src = RdRegB (dstspec); else src = RdMemMB (ea = GeteaB (dstspec)); dst = (src << 1) & BMASK; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZZ_B (dst); if (src & BSIGN) cc = cc | CC_C; if (CC_XOR_NC (cc)) cc = cc | CC_V; break; case 065: /* MFPD */ if (dstreg) /* "mov dst,-(sp)" */ dst = RdRegW (dstspec); else dst = RdMemW (GeteaW (dstspec)); WrMemW (dst, (R[6] - 2) & WMASK); R[6] = (R[6] - 2) & WMASK; CC_IIZP_W (dst); break; case 066: /* MTPD */ dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ R[6] = (R[6] + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, 6); if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, (GeteaW (dstspec) & WMASK)); CC_IIZP_W (dst); break; default: CMODE_FAULT (CMODE_RSVI); break; } /* end switch SOPs */ break; /* end case 010 */ /* Opcodes 11 - 16: double operand byte instructions Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] */ case 011: /* MOVB */ if (srcreg) src = RdRegB (srcspec); else src = RdMemB (GeteaB (srcspec)); if (dstreg) WrRegW ((src & BSIGN)? (0xFF00 | src): src, dstspec); else WrMemB (src, GeteaB (dstspec)); CC_IIZP_B (src); break; case 012: /* CMPB */ if (srcreg) src = RdRegB (srcspec); else src = RdMemB (GeteaB (srcspec)); if (dstreg) src2 = RdRegB (dstspec); else src2 = RdMemB (GeteaB (dstspec)); dst = (src - src2) & BMASK; CC_IIZZ_B (dst); if (((src ^ src2) & (~src2 ^ dst)) & BSIGN) cc = cc | CC_V; if (src < src2) cc = cc | CC_C; break; case 013: /* BITB */ if (srcreg) src = RdRegB (srcspec); else src = RdMemB (GeteaB (srcspec)); if (dstreg) src2 = RdRegB (dstspec); else src2 = RdMemB (GeteaB (dstspec)); dst = src2 & src; CC_IIZP_B (dst); break; case 014: /* BICB */ if (srcreg) src = RdRegB (srcspec); else src = RdMemB (GeteaB (srcspec)); if (dstreg) src2 = RdRegB (dstspec); else src2 = RdMemMB (ea = GeteaB (dstspec)); dst = src2 & ~src; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZP_B (dst); break; case 015: /* BISB */ if (srcreg) src = RdRegB (srcspec); else src = RdMemB (GeteaB (srcspec)); if (dstreg) src2 = RdRegB (dstspec); else src2 = RdMemMB (ea = GeteaB (dstspec)); dst = src2 | src; if (dstreg) WrRegB (dst, dstspec); else WrMemB (dst, ea); CC_IIZP_B (dst); break; case 016: /* SUB */ if (srcreg) src = RdRegW (srcspec); else src = RdMemW (GeteaW (srcspec)); if (dstreg) src2 = RdRegW (dstspec); else src2 = RdMemMW (ea = GeteaW (dstspec)); dst = (src2 - src) & WMASK; if (dstreg) WrRegW (dst, dstspec); else WrMemW (dst, ea); CC_IIZZ_W (dst); if (((src ^ src2) & (~src ^ dst)) & WSIGN) cc = cc | CC_V; if (src2 < src) cc = cc | CC_C; break; default: CMODE_FAULT (CMODE_RSVI); break; } /* end switch op */ return cc; } /* Effective address calculations Inputs: spec = specifier <5:0> Outputs: ea = effective address */ int32 GeteaW (int32 spec) { int32 adr, reg; reg = spec & 07; /* register number */ switch (spec >> 3) { /* decode spec<5:3> */ default: /* can't get here */ case 1: /* (R) */ if (reg == 7) return (PC & WMASK); else return (R[reg] & WMASK); case 2: /* (R)+ */ if (reg == 7) PC = ((adr = PC) + 2) & WMASK; else { R[reg] = ((adr = R[reg]) + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, reg); } return adr; case 3: /* @(R)+ */ if (reg == 7) PC = ((adr = PC) + 2) & WMASK; else { R[reg] = ((adr = R[reg]) + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, reg); } return RdMemW (adr); case 4: /* -(R) */ if (reg == 7) adr = PC = (PC - 2) & WMASK; else { adr = R[reg] = (R[reg] - 2) & WMASK; recq[recqptr++] = RQ_REC (ADC|RW, reg); } return adr; case 5: /* @-(R) */ if (reg == 7) adr = PC = (PC - 2) & WMASK; else { adr = R[reg] = (R[reg] - 2) & WMASK; recq[recqptr++] = RQ_REC (ADC|RW, reg); } return RdMemW (adr); case 6: /* d(r) */ adr = RdMemW (PC); PC = (PC + 2) & WMASK; if (reg == 7) return ((PC + adr) & WMASK); else return ((R[reg] + adr) & WMASK); case 7: /* @d(R) */ adr = RdMemW (PC); PC = (PC + 2) & WMASK; if (reg == 7) adr = (PC + adr) & WMASK; else adr = (R[reg] + adr) & WMASK; return RdMemW (adr); } /* end switch */ } int32 GeteaB (int32 spec) { int32 adr, reg; reg = spec & 07; /* reg number */ switch (spec >> 3) { /* decode spec<5:3> */ default: /* can't get here */ case 1: /* (R) */ if (reg == 7) return (PC & WMASK); else return (R[reg] & WMASK); case 2: /* (R)+ */ if (reg == 7) PC = ((adr = PC) + 2) & WMASK; else if (reg == 6) { R[reg] = ((adr = R[reg]) + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, reg); } else { R[reg] = ((adr = R[reg]) + 1) & WMASK; recq[recqptr++] = RQ_REC (AIN|RB, reg); } return adr; case 3: /* @(R)+ */ if (reg == 7) PC = ((adr = PC) + 2) & WMASK; else { R[reg] = ((adr = R[reg]) + 2) & WMASK; recq[recqptr++] = RQ_REC (AIN|RW, reg); } return RdMemW (adr); case 4: /* -(R) */ if (reg == 7) adr = PC = (PC - 2) & WMASK; else if (reg == 6) { adr = R[reg] = (R[reg] - 2) & WMASK; recq[recqptr++] = RQ_REC (ADC|RW, reg); } else { adr = R[reg] = (R[reg] - 1) & WMASK; recq[recqptr++] = RQ_REC (ADC|RB, reg); } return adr; case 5: /* @-(R) */ if (reg == 7) adr = PC = (PC - 2) & WMASK; else { adr = R[reg] = (R[reg] - 2) & WMASK; recq[recqptr++] = RQ_REC (ADC|RW, reg); } return RdMemW (adr); case 6: /* d(r) */ adr = RdMemW (PC); PC = (PC + 2) & WMASK; if (reg == 7) return ((PC + adr) & WMASK); else return ((R[reg] + adr) & WMASK); case 7: /* @d(R) */ adr = RdMemW (PC); PC = (PC + 2) & WMASK; if (reg == 7) adr = (PC + adr) & WMASK; else adr = (R[reg] + adr) & WMASK; return RdMemW (adr); } /* end switch */ } /* Memory and register access routines */ int32 RdMemW (int32 a) { int32 acc = ACC_MASK (USER); if (a & 1) CMODE_FAULT (CMODE_ODD); return Read (a, L_WORD, RA); } int32 RdMemMW (int32 a) { int32 acc = ACC_MASK (USER); if (a & 1) CMODE_FAULT (CMODE_ODD); return Read (a, L_WORD, WA); } void WrMemW (int32 d, int32 a) { int32 acc = ACC_MASK (USER); if (a & 1) CMODE_FAULT (CMODE_ODD); Write (a, d, L_WORD, WA); return; } int32 RdRegB (int32 rn) { if (rn == 7) return (PC & BMASK); else return (R[rn] & BMASK); } int32 RdRegW (int32 rn) { if (rn == 7) return (PC & WMASK); else return (R[rn] & WMASK); } void WrRegB (int32 val, int32 rn) { if (rn == 7) { CMODE_JUMP ((PC & ~BMASK) | val); } else R[rn] = (R[rn] & ~BMASK) | val; return; } void WrRegW (int32 val, int32 rn) { if (rn == 7) { CMODE_JUMP (val); } else R[rn] = val; return; } #else /* Subset VAX Never legal to set CM in PSL Should never get to instruction execution */ extern jmp_buf save_env; t_bool BadCmPSL (int32 newpsl) { return TRUE; /* always bad */ } int32 op_cmode (int32 cc) { RSVD_INST_FAULT; return cc; } #endif simh-3.8.1/sim_timer.c0000644000175000017500000004733011111341222012770 0ustar vlmvlm/* sim_timer.c: simulator timer library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 22-Sep-08 RMS Added "stability threshold" for idle routine 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) 18-Jun-07 RMS Modified idle to exclude counted delays 22-Mar-07 RMS Added sim_rtcn_init_all 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato) Added throttle support 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jan-04 RMS Split out from SCP This library includes the following routines: sim_timer_init - initialize timing system sim_rtc_init - initialize calibration sim_rtc_calb - calibrate clock sim_timer_init - initialize timing system sim_idle - virtual machine idle sim_os_msec - return elapsed time in msec sim_os_sleep - sleep specified number of seconds sim_os_ms_sleep - sleep specified number of milliseconds The calibration, idle, and throttle routines are OS-independent; the _os_ routines are not. */ #include "sim_defs.h" #include t_bool sim_idle_enab = FALSE; /* global flag */ static uint32 sim_idle_rate_ms = 0; static uint32 sim_idle_stable = SIM_IDLE_STDFLT; static uint32 sim_throt_ms_start = 0; static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; static uint32 sim_throt_val = 0; static uint32 sim_throt_state = 0; static int32 sim_throt_wait = 0; extern int32 sim_interval, sim_switches; extern FILE *sim_log; extern UNIT *sim_clock_queue; t_stat sim_throt_svc (UNIT *uptr); UNIT sim_throt_unit = { UDATA (&sim_throt_svc, 0, 0) }; /* OS-dependent timer and clock routines */ /* VMS */ #if defined (VMS) #if defined (__VAX) #define sys$gettim SYS$GETTIM #endif #include #include #include const t_bool rtc_avail = TRUE; uint32 sim_os_msec () { uint32 quo, htod, tod[2]; int32 i; sys$gettim (tod); /* time 0.1usec */ /* To convert to msec, must divide a 64b quantity by 10000. This is actually done by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the high 32b of which are discarded. This can probably be done by a clever multiply... */ quo = htod = 0; for (i = 0; i < 64; i++) { /* 64b quo */ htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); tod[0] = tod[0] << 1; quo = quo << 1; /* shift quo */ if (htod >= 10000) { /* divd work? */ htod = htod - 10000; /* subtract */ quo = quo | 1; /* set quo bit */ } } return quo; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { #if defined (__VAX) return 10; /* VAX/VMS is 10ms */ #else return 1; /* Alpha/VMS is 1ms */ #endif } uint32 sim_os_ms_sleep (unsigned int msec) { uint32 stime = sim_os_msec (); uint32 qtime[2]; int32 nsfactor = -10000; static int32 zero = 0; lib$emul (&msec, &nsfactor, &zero, qtime); sys$setimr (2, qtime, 0, 0); sys$waitfr (2); return sim_os_msec () - stime; } /* Win32 routines */ #elif defined (_WIN32) #include const t_bool rtc_avail = TRUE; uint32 sim_os_msec () { if (sim_idle_rate_ms) return timeGetTime (); else return GetTickCount (); } void sim_os_sleep (unsigned int sec) { Sleep (sec * 1000); return; } void sim_timer_exit (void) { timeEndPeriod (sim_idle_rate_ms); return; } uint32 sim_os_ms_sleep_init (void) { TIMECAPS timers; if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR) return 0; if ((timers.wPeriodMin == 0) || (timers.wPeriodMin > SIM_IDLE_MAX)) return 0; if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR) return 0; atexit (sim_timer_exit); Sleep (1); Sleep (1); Sleep (1); Sleep (1); Sleep (1); return timers.wPeriodMin; /* sim_idle_rate_ms */ } uint32 sim_os_ms_sleep (unsigned int msec) { uint32 stime = sim_os_msec(); Sleep (msec); return sim_os_msec () - stime; } /* OS/2 routines, from Bruce Ray */ #elif defined (__OS2__) const t_bool rtc_avail = FALSE; uint32 sim_os_msec () { return 0; } void sim_os_sleep (unsigned int sec) { return; } uint32 sim_os_ms_sleep_init (void) { return FALSE; } uint32 sim_os_ms_sleep (unsigned int msec) { return 0; } /* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */ #elif defined (__MWERKS__) && defined (macintosh) #include #include #include #include #include #define NANOS_PER_MILLI 1000000 #define MILLIS_PER_SEC 1000 const t_bool rtc_avail = TRUE; uint32 sim_os_msec (void) { unsigned long long micros; UnsignedWide macMicros; unsigned long millis; Microseconds (&macMicros); micros = *((unsigned long long *) &macMicros); millis = micros / 1000LL; return (uint32) millis; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { return 1; } uint32 sim_os_ms_sleep (unsigned int milliseconds) { uint32 stime = sim_os_msec (); struct timespec treq; treq.tv_sec = milliseconds / MILLIS_PER_SEC; treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; (void) nanosleep (&treq, NULL); return sim_os_msec () - stime; } #else /* UNIX routines */ #include #include #include #define NANOS_PER_MILLI 1000000 #define MILLIS_PER_SEC 1000 #define sleep1Samples 100 const t_bool rtc_avail = TRUE; uint32 sim_os_msec () { struct timeval cur; struct timezone foo; uint32 msec; gettimeofday (&cur, &foo); msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); return msec; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { #if defined (_POSIX_SOURCE) /* POSIX-compliant */ struct timespec treq; uint32 msec; if (clock_getres (CLOCK_REALTIME, &treq) != 0) return 0; msec = (treq.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI; if (msec > SIM_IDLE_MAX) return 0; return msec; #else /* others */ uint32 i, t1, t2, tot, tim; for (i = 0, tot = 0; i < sleep1Samples; i++) { t1 = sim_os_msec (); sim_os_ms_sleep (1); t2 = sim_os_msec (); tot += (t2 - t1); } tim = (tot + (sleep1Samples - 1)) / sleep1Samples; if (tim == 0) tim = 1; else if (tim > SIM_IDLE_MAX) tim = 0; return tim; #endif } uint32 sim_os_ms_sleep (unsigned int milliseconds) { uint32 stime = sim_os_msec (); struct timespec treq; treq.tv_sec = milliseconds / MILLIS_PER_SEC; treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; (void) nanosleep (&treq, NULL); return sim_os_msec () - stime; } #endif /* OS independent clock calibration package */ static int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */ static int32 rtc_hz[SIM_NTIMERS] = { 0 }; /* tick rate */ static uint32 rtc_rtime[SIM_NTIMERS] = { 0 }; /* real time */ static uint32 rtc_vtime[SIM_NTIMERS] = { 0 }; /* virtual time */ static uint32 rtc_nxintv[SIM_NTIMERS] = { 0 }; /* next interval */ static int32 rtc_based[SIM_NTIMERS] = { 0 }; /* base delay */ static int32 rtc_currd[SIM_NTIMERS] = { 0 }; /* current delay */ static int32 rtc_initd[SIM_NTIMERS] = { 0 }; /* initial delay */ static uint32 rtc_elapsed[SIM_NTIMERS] = { 0 }; /* sec since init */ void sim_rtcn_init_all (void) { uint32 i; for (i = 0; i < SIM_NTIMERS; i++) { if (rtc_initd[i] != 0) sim_rtcn_init (rtc_initd[i], i); } return; } int32 sim_rtcn_init (int32 time, int32 tmr) { if (time == 0) time = 1; if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return time; rtc_rtime[tmr] = sim_os_msec (); rtc_vtime[tmr] = rtc_rtime[tmr]; rtc_nxintv[tmr] = 1000; rtc_ticks[tmr] = 0; rtc_hz[tmr] = 0; rtc_based[tmr] = time; rtc_currd[tmr] = time; rtc_initd[tmr] = time; rtc_elapsed[tmr] = 0; return time; } int32 sim_rtcn_calb (int32 ticksper, int32 tmr) { uint32 new_rtime, delta_rtime; int32 delta_vtime; if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return 10000; rtc_hz[tmr] = ticksper; rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ return rtc_currd[tmr]; rtc_ticks[tmr] = 0; /* reset ticks */ rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ if (!rtc_avail) /* no timer? */ return rtc_currd[tmr]; new_rtime = sim_os_msec (); /* wall time */ if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */ rtc_rtime[tmr] = new_rtime; /* reset wall time */ return rtc_currd[tmr]; /* can't calibrate */ } delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ rtc_rtime[tmr] = new_rtime; /* adv wall time */ rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ if (delta_rtime > 30000) /* gap too big? */ return rtc_initd[tmr]; /* can't calibr */ if (delta_rtime == 0) /* gap too small? */ rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */ else rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / ((double) delta_rtime)); /* new base rate */ delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */ if (delta_vtime > SIM_TMAX) /* limit gap */ delta_vtime = SIM_TMAX; else if (delta_vtime < -SIM_TMAX) delta_vtime = -SIM_TMAX; rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */ rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / 1000.0); /* next delay */ if (rtc_based[tmr] <= 0) /* never negative or zero! */ rtc_based[tmr] = 1; if (rtc_currd[tmr] <= 0) /* never negative or zero! */ rtc_currd[tmr] = 1; return rtc_currd[tmr]; } /* Prior interfaces - default to timer 0 */ int32 sim_rtc_init (int32 time) { return sim_rtcn_init (time, 0); } int32 sim_rtc_calb (int32 ticksper) { return sim_rtcn_calb (ticksper, 0); } /* sim_timer_init - get minimum sleep time available on this host */ t_bool sim_timer_init (void) { sim_idle_enab = FALSE; /* init idle off */ sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ return (sim_idle_rate_ms != 0); } /* sim_idle - idle simulator until next event or for specified interval Inputs: tmr = calibrated timer to use Must solve the linear equation ms_to_wait = w * ms_per_wait Or w = ms_to_wait / ms_per_wait */ t_bool sim_idle (uint32 tmr, t_bool sin_cyc) { uint32 cyc_ms, w_ms, w_idle, act_ms; int32 act_cyc; if ((sim_clock_queue == NULL) || /* clock queue empty? */ ((sim_clock_queue->flags & UNIT_IDLE) == 0) || /* event not idle-able? */ (rtc_elapsed[tmr] < sim_idle_stable)) { /* timer not stable? */ if (sin_cyc) sim_interval = sim_interval - 1; return FALSE; } cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */ if ((sim_idle_rate_ms == 0) || (cyc_ms == 0)) { /* not possible? */ if (sin_cyc) sim_interval = sim_interval - 1; return FALSE; } w_ms = (uint32) sim_interval / cyc_ms; /* ms to wait */ w_idle = w_ms / sim_idle_rate_ms; /* intervals to wait */ if (w_idle == 0) { /* none? */ if (sin_cyc) sim_interval = sim_interval - 1; return FALSE; } act_ms = sim_os_ms_sleep (w_idle); /* wait */ act_cyc = act_ms * cyc_ms; if (sim_interval > act_cyc) sim_interval = sim_interval - act_cyc; else sim_interval = 1; return TRUE; } /* Set idling - implicitly disables throttling */ t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) { t_stat r; uint32 v; if (sim_idle_rate_ms == 0) return SCPE_NOFNC; if ((val != 0) && (sim_idle_rate_ms > (uint32) val)) return SCPE_NOFNC; if (cptr) { v = (uint32) get_uint (cptr, 10, SIM_IDLE_STMAX, &r); if ((r != SCPE_OK) || (v < SIM_IDLE_STMIN)) return SCPE_ARG; sim_idle_stable = v; } sim_idle_enab = TRUE; if (sim_throt_type != SIM_THROT_NONE) { sim_set_throt (0, NULL); printf ("Throttling disabled\n"); if (sim_log) fprintf (sim_log, "Throttling disabled\n"); } return SCPE_OK; } /* Clear idling */ t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc) { sim_idle_enab = FALSE; return SCPE_OK; } /* Show idling */ t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) { if (sim_idle_enab) fprintf (st, "idle enabled, stability wait = %ds", sim_idle_stable); else fputs ("idle disabled", st); return SCPE_OK; } /* Throttling package */ t_stat sim_set_throt (int32 arg, char *cptr) { char *tptr, c; t_value val; if (arg == 0) { if ((cptr != 0) && (*cptr != 0)) return SCPE_ARG; sim_throt_type = SIM_THROT_NONE; sim_throt_cancel (); } else if (sim_idle_rate_ms == 0) return SCPE_NOFNC; else { val = strtotv (cptr, &tptr, 10); if (cptr == tptr) return SCPE_ARG; c = toupper (*tptr++); if (*tptr != 0) return SCPE_ARG; if (c == 'M') sim_throt_type = SIM_THROT_MCYC; else if (c == 'K') sim_throt_type = SIM_THROT_KCYC; else if ((c == '%') && (val > 0) && (val < 100)) sim_throt_type = SIM_THROT_PCT; else return SCPE_ARG; if (sim_idle_enab) { printf ("Idling disabled\n"); if (sim_log) fprintf (sim_log, "Idling disabled\n"); sim_clr_idle (NULL, 0, NULL, NULL); } sim_throt_val = (uint32) val; } return SCPE_OK; } t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) { if (sim_idle_rate_ms == 0) fprintf (st, "Throttling not available\n"); else { switch (sim_throt_type) { case SIM_THROT_MCYC: fprintf (st, "Throttle = %d megacycles\n", sim_throt_val); break; case SIM_THROT_KCYC: fprintf (st, "Throttle = %d kilocycles\n", sim_throt_val); break; case SIM_THROT_PCT: fprintf (st, "Throttle = %d%%\n", sim_throt_val); break; default: fprintf (st, "Throttling disabled\n"); break; } if (sim_switches & SWMASK ('D')) { fprintf (st, "Wait rate = %d ms\n", sim_idle_rate_ms); if (sim_throt_type != 0) fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); } } return SCPE_OK; } void sim_throt_sched (void) { sim_throt_state = 0; if (sim_throt_type) sim_activate (&sim_throt_unit, SIM_THROT_WINIT); return; } void sim_throt_cancel (void) { sim_cancel (&sim_throt_unit); } /* Throttle service Throttle service has three distinct states 0 take initial measurement 1 take final measurement, calculate wait values 2 periodic waits to slow down the CPU */ t_stat sim_throt_svc (UNIT *uptr) { uint32 delta_ms; double a_cps, d_cps; switch (sim_throt_state) { case 0: /* take initial reading */ sim_throt_ms_start = sim_os_msec (); sim_throt_wait = SIM_THROT_WST; sim_throt_state++; /* next state */ break; /* reschedule */ case 1: /* take final reading */ sim_throt_ms_stop = sim_os_msec (); delta_ms = sim_throt_ms_stop - sim_throt_ms_start; if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ if (sim_throt_wait >= 100000000) { /* too many inst? */ sim_throt_state = 0; /* fails in 32b! */ return SCPE_OK; } sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; sim_throt_ms_start = sim_throt_ms_stop; } else { /* long enough */ a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; else if (sim_throt_type == SIM_THROT_KCYC) d_cps = (double) sim_throt_val * 1000.0; else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; if (d_cps >= a_cps) { sim_throt_state = 0; return SCPE_OK; } sim_throt_wait = (int32) /* time between waits */ ((a_cps * d_cps * ((double) sim_idle_rate_ms)) / (1000.0 * (a_cps - d_cps))); if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ sim_throt_state = 0; return SCPE_OK; } sim_throt_state++; // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", // a_cps, d_cps, sim_throt_wait); } break; case 2: /* throttling */ sim_os_ms_sleep (1); break; } sim_activate (uptr, sim_throt_wait); /* reschedule */ return SCPE_OK; } simh-3.8.1/S3/0000755000175000017500000000000010550242700011121 5ustar vlmvlmsimh-3.8.1/S3/s3_pkb.c0000644000175000017500000003214610303752646012467 0ustar vlmvlm/* s3_pkb.c: System/3 5471 console terminal simulator Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. pkb 5471 printer/keyboard 25-Apr-03 RMS Revised for extended file support 08-Oct-02 RMS Added impossible function catcher */ #include "s3_defs.h" #include extern int32 int_req, dev_busy, dev_done, dev_disable; t_stat pkb_svc (UNIT *uptr); t_stat pkb_reset (DEVICE *dptr); extern t_stat sim_poll_kbd (void); extern t_stat sim_putchar (int32 out); extern int32 IAR[], level; extern int32 debug_reg; /* 5471 data structures pkb_dev TTI device descriptor pkb_unit TTI unit descriptor pkb_reg TTI register list pkb_mod TTI/TTO modifiers list */ /* Flag bits : (kept in pkb_unit.u3) */ #define PRT_INTREQ 0x800 /* Printer interrupt pending */ #define KBD_INTREQ 0x400 /* Request key interrupt pending */ #define KBD_INTEND 0x200 /* End or cancel key interrupt pending */ #define KBD_INTKEY 0x100 /* Return or other key interrupt pending */ #define KBD_REQLIGHT 0x20 /* Request Pending Indicator (light on/off) */ #define KBD_PROLIGHT 0x10 /* Proceed indicator (light on/off) */ #define KBD_REQINT 0x04 /* Req key interrupts enabled */ #define KBD_KEYINT 0x02 /* Other key interrupts enabled */ #define PRT_PRTINT 0x01 /* Printer interrupts enabled */ /* Keys mapped to 5471 functions */ int32 key_req = 0x01; /* Request key: ^A */ int32 key_rtn = 0x12; /* Return key: ^R */ int32 key_can = 0x1B; /* Cancel key: ESC */ int32 key_end = 0x0d; /* End key - CR */ UNIT pkb_unit = { UDATA (&pkb_svc, 0, 0), KBD_POLL_WAIT }; REG pkb_reg[] = { { HRDATA (FLAG, pkb_unit.u3, 16) }, { HRDATA (IBUF, pkb_unit.buf, 8) }, { HRDATA (OBUF, pkb_unit.u4, 8) }, { HRDATA (REQKEY, key_req, 8) }, { HRDATA (RTNKEY, key_rtn, 8) }, { HRDATA (CANKEY, key_can, 8) }, { HRDATA (ENDKEY, key_end, 8) }, { DRDATA (POS, pkb_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, pkb_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB pkb_mod[] = { { 0 } }; DEVICE pkb_dev = { "PKB", &pkb_unit, pkb_reg, pkb_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &pkb_reset, NULL, NULL, NULL }; /*-------------------------------------------------------------------*/ /* EBCDIC to ASCII translate table */ /*-------------------------------------------------------------------*/ unsigned char ebcdic_to_ascii[] = { "\x00\x01\x02\x03\xA6\x09\xA7\x7F\xA9\xB0\xB1\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\xB2\xB4\x08\xB7\x18\x19\x1A\xB8\xBA\x1D\xBB\x1F" "\xBD\xC0\x1C\xC1\xC2\x0A\x17\x1B\xC3\xC4\xC5\xC6\xC7\x05\x06\x07" "\xC8\xC9\x16\xCB\xCC\x1E\xCD\x04\xCE\xD0\xD1\xD2\x14\x15\xD3\xFC" "\x20\xD4\x83\x84\x85\xA0\xD5\x86\x87\xA4\xD6\x2E\x3C\x28\x2B\xD7" "\x26\x82\x88\x89\x8A\xA1\x8C\x8B\x8D\xD8\x21\x24\x2A\x29\x3B\x5E" "\x2D\x2F\xD9\x8E\xDB\xDC\xDD\x8F\x80\xA5\x7C\x2C\x25\x5F\x3E\x3F" "\xDE\x90\xDF\xE0\xE2\xE3\xE4\xE5\xE6\x60\x3A\x23\x40\x27\x3D\x22" "\xE7\x61\x62\x63\x64\x65\x66\x67\x68\x69\xAE\xAF\xE8\xE9\xEA\xEC" "\xF0\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\xF1\xF2\x91\xF3\x92\xF4" "\xF5\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xAD\xA8\xF6\x5B\xF7\xF8" "\x9B\x9C\x9D\x9E\x9F\xB5\xB6\xAC\xAB\xB9\xAA\xB3\xBC\x5D\xBE\xBF" "\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCA\x93\x94\x95\xA2\xCF" "\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xDA\x96\x81\x97\xA3\x98" "\x5C\xE1\x53\x54\x55\x56\x57\x58\x59\x5A\xFD\xEB\x99\xED\xEE\xEF" "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xFE\xFB\x9A\xF9\xFA\xFF" }; /*-------------------------------------------------------------------*/ /* ASCII to EBCDIC translate table */ /*-------------------------------------------------------------------*/ unsigned char ascii_to_ebcdic[] = { "\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x25\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x1A\x27\x22\x1D\x35\x1F" "\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61" "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F" "\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6" "\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D" "\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96" "\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x6A\xD0\xA1\x07" "\x68\xDC\x51\x42\x43\x44\x47\x48\x52\x53\x54\x57\x56\x58\x63\x67" "\x71\x9C\x9E\xCB\xCC\xCD\xDB\xDD\xDF\xEC\xFC\xB0\xB1\xB2\xB3\xB4" "\x45\x55\xCE\xDE\x49\x69\x04\x06\xAB\x08\xBA\xB8\xB7\xAA\x8A\x8B" "\x09\x0A\x14\xBB\x15\xB5\xB6\x17\x1B\xB9\x1C\x1E\xBC\x20\xBE\xBF" "\x21\x23\x24\x28\x29\x2A\x2B\x2C\x30\x31\xCA\x33\x34\x36\x38\xCF" "\x39\x3A\x3B\x3E\x41\x46\x4A\x4F\x59\x62\xDA\x64\x65\x66\x70\x72" "\x73\xE1\x74\x75\x76\x77\x78\x80\x8C\x8D\x8E\xEB\x8F\xED\xEE\xEF" "\x90\x9A\x9B\x9D\x9F\xA0\xAC\xAE\xAF\xFD\xFE\xFB\x3F\xEA\xFA\xFF" }; /* -------------------------------------------------------------------- */ /* Console Input: master routine */ int32 pkb (int32 op, int32 m, int32 n, int32 data) { int32 iodata= 0, ec, ac; switch (op) { case 0: /* SIO 5471 */ if (n != 0) return STOP_INVDEV; /*printf("%04X SIO %d,%d,%02X\n\r", IAR[level]-4, m, n, data);*/ if (m == 0) { /* Keyboard */ pkb_unit.u3 &= 0xFC1; pkb_unit.u3 |= data; if (data & 0x01) { pkb_unit.u3 &= ~KBD_INTREQ; pkb_unit.u3 &= ~KBD_INTKEY; pkb_unit.u3 &= ~KBD_INTEND; return RESET_INTERRUPT; } } else { /* Printer */ if (data & 0x80) { /* start print bit */ if (debug_reg & 0x80) return STOP_IBKPT; ec = pkb_unit.u4 & 0xff; ac = ebcdic_to_ascii[ec]; sim_putchar(ac); pkb_unit.u3 |= PRT_INTREQ; } if (data & 0x40) { /* Carr. Return */ sim_putchar('\n'); sim_putchar('\r'); pkb_unit.u3 |= PRT_INTREQ; } pkb_unit.u3 &= 0xFFe; if (data & 0x04) /* Print interrupt flag */ pkb_unit.u3 |= PRT_PRTINT; if (data & 0x01) { /* Reset Interrupt */ if (level < 8) { if (!(data & 0x80)) pkb_unit.u3 &= ~PRT_INTREQ; return RESET_INTERRUPT; } } } return SCPE_OK; case 1: /* LIO 5471 */ if (n != 0) return STOP_INVDEV; if (m != 1) return STOP_INVDEV; pkb_unit.u4 = (data >> 8) & 0xff; return SCPE_OK; break; case 2: /* TIO 5471 */ return STOP_INVDEV; case 3: /* SNS 5471 */ if (n != 1 && n != 3) return (STOP_INVDEV << 16); if (m == 0) { /* Keyboard data */ if (n == 1) { /* Sense bytes 0 & 1 */ iodata = (pkb_unit.buf << 8) & 0xff00; if (pkb_unit.u3 & KBD_INTREQ) iodata |= 0x80; if (pkb_unit.u3 & KBD_INTEND) iodata |= 0x40; if (pkb_unit.u3 & KBD_INTKEY) iodata |= 0x08; if (pkb_unit.buf == 0x12) /* Return key */ iodata |= 0x04; if (pkb_unit.buf == 0x03) /* Cancel key */ iodata |= 0x20; if (pkb_unit.buf == 0x0d) /* End key */ iodata |= 0x10; iodata |= ((SCPE_OK << 16) & 0xffff0000); } else { /* Sense bytes 2 & 3 */ iodata = 0; /* Manual says CE use only */ } } else { /* Printer Data */ if (n == 1) { /* Sense bytes 0 & 1 */ iodata = 0; if (pkb_unit.u3 & PRT_INTREQ) iodata |= 0x80; } else { iodata = 0; /* CE use only */ } } iodata |= ((SCPE_OK << 16) & 0xffff0000); return (iodata); case 4: /* APL 5471 */ return STOP_INVDEV; default: break; } printf (">>PKB non-existent function %d\n", op); return SCPE_OK; } /* Unit service */ t_stat pkb_svc (UNIT *uptr) { int32 temp, ac, ec; sim_activate (&pkb_unit, pkb_unit.wait); /* continue poll */ if (pkb_unit.u3 & PRT_INTREQ) { /* Printer Interrupt */ int_req |= 2; return SCPE_OK; } /* Keyboard : handle input */ if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ ac = temp & 0x7f; /* placed type ASCII char in ac */ if (pkb_unit.u3 & KBD_REQINT) { if (ac == key_req) { /* Request Key */ pkb_unit.u3 |= KBD_INTREQ; int_req |= 2; return SCPE_OK; } } if (islower(ac)) ac = toupper(ac); ec = ascii_to_ebcdic[ac]; /* Translate */ pkb_unit.buf = ec; /* put in buf */ pkb_unit.pos = pkb_unit.pos + 1; if (ac == key_end) { /* End key */ if (pkb_unit.u3 & KBD_KEYINT) { pkb_unit.u3 |= KBD_INTEND; pkb_unit.buf = 0x0d; int_req |= 2; } return SCPE_OK; } if (ac == key_can) { /* Cancel key */ if (pkb_unit.u3 & KBD_KEYINT) { pkb_unit.u3 |= KBD_INTEND; pkb_unit.buf = 0x03; int_req |= 2; } return SCPE_OK; } if (ac == key_rtn) { /* Return key */ if (pkb_unit.u3 & KBD_KEYINT) { pkb_unit.u3 |= KBD_INTKEY; pkb_unit.buf = 0x12; int_req |= 2; } return SCPE_OK; } if (pkb_unit.u3 & KBD_KEYINT) { /* Key interupts enabled ? */ int_req |= 2; /* Device 1 Interrupt! */ pkb_unit.u3 |= KBD_INTKEY; /* Set pending flag */ } return SCPE_OK; } /* Reset routine */ t_stat pkb_reset (DEVICE *dptr) { pkb_unit.buf = 0; int_req = int_req & ~0x02; /* reset interrupt */ sim_activate (&pkb_unit, pkb_unit.wait); /* activate unit */ return SCPE_OK; } t_stat pkb_setmod (UNIT *uptr, int32 value) { return SCPE_OK; } simh-3.8.1/S3/s3_sys.c0000644000175000017500000010343010303752646012524 0ustar vlmvlm/* s3_sys.c: IBM System/3 system interface Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. */ #include #include "s3_defs.h" extern DEVICE cpu_dev; extern DEVICE pkb_dev; extern DEVICE cdr_dev; extern DEVICE cdp_dev; extern DEVICE stack_dev; extern DEVICE lpt_dev; extern DEVICE r1_dev; extern DEVICE f1_dev; extern DEVICE r2_dev; extern DEVICE f2_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern unsigned char M[]; extern int32 saved_PC, IAR[]; extern char ebcdic_to_ascii[256]; char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype); int32 printf_sym (FILE *of, char *strg, int32 addr, uint32 *val, UNIT *uptr, int32 sw); /* SCP data structures sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax number of words needed 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[] = "System/3"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 6; DEVICE *sim_devices[] = { &cpu_dev, &pkb_dev, &cdr_dev, &cdp_dev, &stack_dev, &lpt_dev, &r1_dev, &f1_dev, &r2_dev, &f2_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unknown I/O Instruction", "HALT instruction", "Breakpoint", "Invalid Opcode", "Invalid Qbyte", "Invalid Address", "Invalid Device Command", "ATTN Card Reader" }; /* This is the opcode master defintion table. Each possible opcode mnemonic is defined here, with enough information to translate to and from symbolic to binary machine code. First field is the opcode's mnemonic Second field is the hex of the right nybble of the binary opcode Third field is the Q code for those with implicit Q codes Fourth field is the symbolic format of the operands: 0 - (Q-byte),(R-byte) 1 - (Q-byte),(Address) 2 - (Address),(Address),(Qbyte) 3 - (Address),(Qbyte) 4 - (device),(modifier),(function) -- these 3 make up qbyte 5 - (device),(modifier),(function),(control) 6 - (device),(modifier),(function),(Address) 7 - (displacement) -- Q byte is implicit in opcode 8 - (address) -- Qbyte is implicit in opcode 9 - (Address),(Address) -- Qbyte is implicit in opcode Fifth Field is the group number: 0 - Command Group (left op nybble is F) 1 - One Address Operations A (Left Nybble C, D, or E) 2 - Two Address Operations (Left Nybble 0,1,2,4,5,6,8,9, or A) 3 - One Address Operations B (left Nybble 3, 7, or B) There is duplication in this table -- IBM defines different opcodes that resolve to the same binary machine instruction -- e.g. JE and JZ. On input this is no problem, on output, define the one you want to appear first, the second will never appear on output. */ int32 nopcode = 75; struct opdef opcode[75] = { "HPL", 0x00,0,0,0, /* Halt Program Level */ "A", 0x06,0,1,3, /* Add to Register: A R,AADD */ "ST", 0x04,0,1,3, /* Store Register */ "L", 0x05,0,1,3, /* Load Register */ "LA", 0x02,0,1,1, /* Load Address */ "ZAZ", 0x04,0,2,2, /* Zero and Add Zoned */ "AZ", 0x06,0,2,2, /* Add Zoned Decimal */ "SZ", 0x07,0,2,2, /* Subtract Zoned Decimal */ "ALC", 0x0E,0,2,2, /* Add Logical: ALC BADD,AADD,LEN */ "SLC", 0x0F,0,2,2, /* Sub Logical: SLC BADD,AADD,LEN */ "MVC", 0x0C,0,2,2, /* Move Chars MVX BADD,AADD,LEN */ "ED", 0x0A,0,2,2, /* Edit: ED BADD,AADD,LEN */ "ITC", 0x0B,0,2,2, /* Insert Chars: ITC BADD,AADD,LEN */ "CLC", 0x0D,0,2,2, /* Compare Logical: CLC BADD,AADD,LEN */ "MVI", 0x0C,0,3,3, /* Move Immediate */ "SBN", 0x0A,0,3,3, /* Set Bits On */ "SBF", 0x0B,0,3,3, /* Set Bits Off */ "CLI", 0x0D,0,3,3, /* Compare Immediate */ "TBN", 0x08,0,3,3, /* Test Bits On */ "TBF", 0x09,0,3,3, /* Test Bits Off */ "APL", 0x01,0,4,0, /* Advance Program Level */ "SIO", 0x03,0,5,0, /* Start I/O */ "SNS", 0x00,0,6,3, /* Sense I/O */ "LIO", 0x01,0,6,3, /* Load I/O */ "TIO", 0x01,0,6,1, /* Test I/O */ "J", 0x02,0,7,0, /* Jump Unconditional */ "J", 0x02,0x87,7,0, /* Alternate J */ "JH", 0x02,132,7,0, /* Jump if High */ "JL", 0x02,130,7,0, /* Jump if Low */ "JE", 0x02,129,7,0, /* Jump if Equal */ "JNH", 0x02,4,7,0, /* Jump if Not High */ "JNL", 0x02,2,7,0, /* Jump if Not Low */ "JNE", 0x02,1,7,0, /* Jump if Not Equal */ "JOZ", 0x02,136,7,0, /* Jump if Overflow Zoned */ "JOL", 0x02,160,7,0, /* Jump if Overflow Logical */ "JNOZ", 0x02,8,7,0, /* Jump if No Overflow Zoned */ "JNOL", 0x02,32,7,0, /* Jump if No Overflow Logical */ "JT", 0x02,16,7,0, /* Jump if True */ "JF", 0x02,144,7,0, /* Jump if False */ "JP", 0x02,132,7,0, /* Jump if Plus */ "JM", 0x02,130,7,0, /* Jump if Minus */ "JZ", 0x02,129,7,0, /* Jump if Zero */ "JNP", 0x02,4,7,0, /* Jump if Not Plus */ "JNM", 0x02,2,7,0, /* Jump if Not Minus */ "JNZ", 0x02,1,7,0, /* Jump if Not Zero */ "NOPJ", 0x02,0x80,7,0, /* Never Jump - NOP */ "B", 0x00,0x00,8,1, /* Branch Unconditional */ "B", 0x00,0x87,8,1, /* Alternate B */ "BH", 0x00,0x84,8,1, /* Branch if High */ "BL", 0x00,0x82,8,1, /* Branch if Low */ "BE", 0x00,0x81,8,1, /* Branch if Equal */ "BNH", 0x00,0x04,8,1, /* Branch if Not High */ "BNL", 0x00,0x02,8,1, /* Branch if Not Low */ "BNE", 0x00,0x01,8,1, /* Branch if Not Equal */ "BOZ", 0x00,0x88,8,1, /* Branch if Overflow Zoned */ "BOL", 0x00,0xA0,8,1, /* Branch if Overflow Logical */ "BNOZ", 0x00,0x08,8,1, /* Branch if No Overflow Zoned */ "BNOL", 0x00,0x20,8,1, /* Branch if No Overflow Logical */ "BT", 0x00,0x10,8,1, /* Branch if True */ "BF", 0x00,0x90,8,1, /* Branch if False */ "BP", 0x00,0x84,8,1, /* Branch if Plus */ "BM", 0x00,0x82,8,1, /* Branch if Minus */ "BZ", 0x00,0x81,8,1, /* Branch if Zero */ "BNP", 0x00,0x04,8,1, /* Branch if Not Plus */ "BNM", 0x00,0x02,8,1, /* Branch if Not Minus */ "BNZ", 0x00,0x01,8,1, /* Branch if Not Zero */ "NOPB", 0x00,0x80,8,1, /* Never Branch - NOP */ "MZZ", 0x08,0,9,2, /* Move Zone to Zone */ "MNZ", 0x08,1,9,2, /* Move Numeric to Zone */ "MZN", 0x08,2,9,2, /* Move Zone to Numeric */ "MNN", 0x08,3,9,2, /* Move Numeric to Numeric */ "MVX", 0x08,0,2,2, /* Move Hex: MVX BADD,AADD,CODE */ "JC", 0x02,0,3,0, /* Jump on Specified Condition bits */ "BC", 0x00,0,3,1, /* Branch on Specified Condition */ "***", 0x00,0,0,0 }; int32 regcode[15] = { 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x81 }; char regname[15][8] = { "(P2IAR)", "(P1IAR)", "(IAR)", "(ARR)", "(PSR)", "(XR2)", "(XR1)", "(IAR0)", "(IAR1)", "(IAR2)", "(IAR3)", "(IAR4)", "(IAR5)", "(IAR6)", "(IAR7)" }; /* This is the binary loader. The input file is considered to be a string of literal bytes with no special format. The load starts at the current value of the P1IAR. */ int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 i, addr = 0, cnt = 0; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; addr = IAR[8]; while ((i = getc (fileref)) != EOF) { M[addr] = i & 0xff; addr++; cnt++; } /* end while */ printf ("%d Bytes loaded.\n", cnt); return (SCPE_OK); } /* Symbolic output Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: status = error code */ int32 fprint_sym (FILE *of, int32 addr, uint32 *val, UNIT *uptr, int32 sw) { int32 r; char strg[256]; strcpy(strg, ""); r = printf_sym(of, strg, addr, val, uptr, sw); if (sw & SWMASK ('A')) strcpy(strg, ""); else fprintf(of, "%s", strg); return (r); } int32 printf_sym (FILE *of, char *strg, int32 addr, uint32 *val, UNIT *uptr, int32 sw) { int32 cflag, c1, c2, group, len1, len2, inst, aaddr, baddr; int32 oplen, groupno, i, j, vpos, qbyte, da, m, n; char bld[128], bldaddr[32], boperand[32], aoperand[32]; int32 blk[16], blt[16]; int32 blkadd; cflag = (uptr == NULL) || (uptr == &cpu_unit); c1 = val[0] & 0xff; if (sw & SWMASK ('A')) { for (i = 0; i < 16; i++) { blkadd = addr + (i*16); for (j = 0; j < 16; j++) { blk[j] = M[blkadd+j] & 0xff; c2 = ebcdic_to_ascii[blk[j]]; if (c2 < 040 || c2 > 0177 || blk[j] == 07) { blt[j] = '.'; } else { blt[j] = c2; } } if (i == 0) { fprintf(of, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); } else { fprintf(of, "%X\t%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", blkadd, blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); } } return SCPE_OK; } if (sw & SWMASK ('C')) { c2 = ebcdic_to_ascii[c1]; if (c2 < 040 || c2 > 0177) { sprintf(strg, "<%02X>", c1 & 0xff); } else { sprintf (strg, "%c", c2 & 0xff); } return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; inst = val[0] & 0x0f; len1 = (val[0] >> 6) & 3; len2 = (val[0] >> 4) & 3; group = (val[0] >> 4) & 0x0f; qbyte = val[1]; /* Get total length of instruction */ if (group == 0x0f) { oplen = 3; } else { oplen = 2; if (len1 == 0) oplen += 2; if (len1 == 1 || len1 == 2) oplen++; if (len2 == 0) oplen += 2; if (len2 == 1 || len2 == 2) oplen++; } /* Find which group it belongs to */ switch (group) { case 0x0f: groupno = 0; break; case 0x0c: case 0x0d: case 0x0e: groupno = 1; break; case 0x03: case 0x07: case 0x0b: groupno = 3; break; default: groupno = 2; break; } /* find the table entry */ for (i = 0; i < nopcode; i++) { if (opcode[i].form < 7) { /* Explicit Q */ if (opcode[i].group == groupno && opcode[i].opmask == inst) break; } else { /* Implicit Q */ if (opcode[i].group == groupno && opcode[i].opmask == inst && opcode[i].q == qbyte) break; } } /* print the opcode */ if (i >= nopcode) { sprintf(strg, "%02X", val[0]); oplen = 1; } else { sprintf(bld, "%s ", opcode[i].op); /* Extract the addresses into aaddr and baddr */ strcpy(aoperand, "ERROR"); strcpy(boperand, "ERROR"); vpos = 2; aaddr = baddr = 0; switch (len1) { case 0: baddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); sprintf(boperand, "%04X", baddr); vpos = 4; break; case 1: baddr = val[vpos] & 255; sprintf(boperand, "(%02X,XR1)", baddr); vpos = 3; break; case 2: baddr = val[vpos] & 255; sprintf(boperand, "(%02X,XR2)", baddr); vpos = 3; break; default: baddr = 0; break; } switch (len2) { case 0: aaddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); if (group == 0x0C || group == 0x0D || group == 0x0E) sprintf(boperand, "%04X", aaddr); else sprintf(aoperand, "%04X", aaddr); break; case 1: aaddr = val[vpos] & 255; if (group == 0x0C || group == 0x0D || group == 0x0E) sprintf(boperand, "(%02X,XR1)", aaddr); else sprintf(aoperand, "(%02X,XR1)", aaddr); break; case 2: aaddr = val[vpos] & 255; if (group == 0x0C || group == 0x0D || group == 0x0E) sprintf(boperand, "(%02X,XR2)", aaddr); else sprintf(aoperand, "(%02X,XR2)", aaddr); break; default: aaddr = 0; break; } /* Display the operands in the correct format */ da = (qbyte >> 4) & 0x0f; m = (qbyte >> 3) & 0x01; n = (qbyte) & 0x07; switch (opcode[i].form) { case 0: sprintf(bldaddr, "%02X,%02X", qbyte, val[2]); break; case 1: if (inst == 2 || inst == 4 || inst == 5 || inst == 6) { for (i = 0; i < 16; i++) { if (regcode[i] == qbyte) break; } if (i < 16) { sprintf(bldaddr, "%s,%s", regname[i], boperand); } else { sprintf(bldaddr, "%02X,%s", qbyte, boperand); } } else { sprintf(bldaddr, "%02X,%s", qbyte, boperand); } break; case 2: if (inst > 9 || inst == 4 || inst == 6 || inst == 7) qbyte++; /* special +1 for length display */ sprintf(bldaddr, "%s,%s,%d", boperand, aoperand, qbyte); break; case 3: if (strcmp(opcode[i].op, "JC") == 0) { sprintf(bldaddr, "%04X,%02X", addr+oplen+val[2], qbyte); } else { sprintf(bldaddr, "%s,%02X", boperand, qbyte); } break; case 4: sprintf(bldaddr, "%d,%d,%d", da, m, n); break; case 5: sprintf(bldaddr, "%d,%d,%d,%02X", da, m, n, val[2]); break; case 6: sprintf(bldaddr, "%d,%d,%d,%s", da, m, n, boperand); break; case 7: sprintf(bldaddr, "%04X", addr+oplen+val[2]); break; case 8: sprintf(bldaddr, "%s", boperand); break; default: sprintf(bldaddr, "%s,%s", boperand, aoperand); break; } sprintf(strg, "%s%s", bld, bldaddr); } return -(oplen - 1); } /* 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 */ int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, uint32 *val, int32 sw) { int32 cflag, i = 0, j, r, oplen, addtyp, saveaddr, vptr; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = (unsigned int) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = ((unsigned int) cptr[0] << 8) + (unsigned int) cptr[1]; return SCPE_OK; } /* An instruction: get opcode (all characters until null, comma, left paren, or numeric (including spaces). */ while (1) { if (*cptr == ',' || *cptr == '\0' || *cptr == '(' || isdigit(*cptr)) break; gbuf[i] = toupper(*cptr); cptr++; i++; } /* kill trailing spaces if any */ gbuf[i] = '\0'; for (j = i - 1; gbuf[j] == ' '; j--) { gbuf[j] = '\0'; } /* find opcode in table */ for (j = 0; j < nopcode; j++) { if (strcmp(gbuf, opcode[j].op) == 0) break; } if (j >= nopcode) /* not found */ return SCPE_ARG; oplen = 2; /* start with op & q */ val[0] = opcode[j].opmask; /* store opcode right nybble */ switch (opcode[j].form) { /* Get operands based on operand format */ case 0: /* Single Byte Operand */ if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); /* Get Q Byte */ sscanf(gbuf, "%x", &r); val[1] = r; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); /* Get R Byte */ sscanf(gbuf, "%x", &r); val[2] = r; oplen = 3; val[0] = 0xf0 | opcode[j].opmask; break; case 1: if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); if (opcode[j].opmask == 2 || opcode[j].opmask == 4 || opcode[j].opmask == 5 || opcode[j].opmask == 6) { if (isdigit(gbuf[0])) { sscanf(gbuf, "%x", &r); } else { for (i = 0; i < 16; i++) { if (strcmp(gbuf, regname[i]) == 0) break; } if (i < 16) { r = regcode[i]; } else { return SCPE_ARG; } } } else { sscanf(gbuf, "%x", &r); } if (r > 255) return SCPE_ARG; val[1] = r; if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[2] = (addr >> 8) & 0x00ff; val[3] = addr & 0xff; oplen = 4; if (opcode[j].group == 1) val[0] = 0xC0 | opcode[j].opmask; else val[0] = 0x30 | opcode[j].opmask; break; case 1: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xD0 | opcode[j].opmask; else val[0] = 0x70 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xE0 | opcode[j].opmask; else val[0] = 0xB0 | opcode[j].opmask; break; default: return SCPE_ARG; break; } break; case 2: oplen = 2; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[2] = (addr >> 8) & 0xff; val[3] = addr & 0xff; oplen += 2; vptr = 4; val[0] = 0x00 | opcode[j].opmask; break; case 1: val[2] = addr & 0xff; oplen += 1; vptr = 3; val[0] = 0x40 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen += 1; vptr = 3; val[0] = 0x80 | opcode[j].opmask; break; default: return SCPE_ARG; break; } if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[vptr] = (addr >> 8) & 0xff; val[vptr+1] = addr & 0xff; oplen += 2; break; case 1: val[vptr] = addr & 0xff; oplen += 1; val[0] = 0x10 | val[0]; break; case 2: val[vptr] = addr & 0xff; oplen += 1; val[0] = 0x20 | val[0]; break; default: return SCPE_ARG; break; } if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%d", &r); if (opcode[j].opmask > 9 || opcode[j].opmask == 4 || opcode[j].opmask == 6 || opcode[j].opmask == 7) r--; /* special: length -1 */ val[1] = r; if (*cptr == ',') cptr++; break; case 3: saveaddr = addr; if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: if (opcode[j].group == 0) { /* Group 0 form 3 is JC with explicit Q */ if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%x", &r); if ((addr - (saveaddr+3)) > 255 || (addr - (saveaddr+3)) < 1) return SCPE_ARG; val[2] = addr - (saveaddr+3); val[1] = r; val[0] = 0xf0 | opcode[j].opmask; oplen = 3; } else { val[2] = (addr >> 8) & 0x00ff; val[3] = addr & 0xff; oplen = 4; if (opcode[j].group == 1) val[0] = 0xC0 | opcode[j].opmask; else val[0] = 0x30 | opcode[j].opmask; } break; case 1: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xD0 | opcode[j].opmask; else val[0] = 0x70 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xE0 | opcode[j].opmask; else val[0] = 0xB0 | opcode[j].opmask; break; default: return SCPE_ARG; break; } if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%x", &r); if (r > 255) return SCPE_ARG; val[1] = r; break; case 4: if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 15) return SCPE_ARG; val[1] = (r << 4) & 0xf0; val[0] = 0xf0 | opcode[j].opmask; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 1) return SCPE_ARG; val[1] |= (r << 3) & 0x08; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%d", &r); if (r > 7) return SCPE_ARG; val[1] |= r & 0x07; val[2] = 0; oplen = 3; break; case 5: if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 15) return SCPE_ARG; val[1] = (r << 4) & 0xf0; val[0] = 0xf0 | opcode[j].opmask; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 1) return SCPE_ARG; val[1] |= (r << 3) & 0x08; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 7) return SCPE_ARG; val[1] |= r & 0x07; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%x", &r); if (r > 255) return SCPE_ARG; val[2] = r; oplen = 3; break; case 6: if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 15) return SCPE_ARG; val[1] = (r << 4) & 0xf0; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 1) return SCPE_ARG; val[1] |= (r << 3) & 0x08; if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); sscanf(gbuf, "%d", &r); if (r > 7) return SCPE_ARG; val[1] |= r & 0x07; if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[2] = (addr >> 8) & 0x00ff; val[3] = addr & 0xff; oplen = 4; if (opcode[j].group == 1) val[0] = 0xC0 | opcode[j].opmask; else val[0] = 0x30 | opcode[j].opmask; break; case 1: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xD0 | opcode[j].opmask; else val[0] = 0x70 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen = 3; if (opcode[j].group == 1) val[0] = 0xE0 | opcode[j].opmask; else val[0] = 0xB0 | opcode[j].opmask; break; default: return SCPE_ARG; break; } break; case 7: if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, 0); sscanf(gbuf, "%x", &r); if ((r - (addr+3)) > 255 || (r - (addr+3)) < 1) return SCPE_ARG; val[2] = r - (addr+3); val[1] = opcode[j].q; val[0] = 0xf0 | opcode[j].opmask; oplen = 3; break; case 8: if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[2] = (addr >> 8) & 0x00ff; val[3] = addr & 0xff; oplen = 4; val[0] = 0xC0 | opcode[j].opmask; break; case 1: val[2] = addr & 0xff; oplen = 3; val[0] = 0xD0 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen = 3; val[0] = 0xE0 | opcode[j].opmask; break; default: return SCPE_ARG; break; } val[1] = opcode[j].q; break; case 9: oplen = 2; val[0] = 0; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[2] = (addr >> 8) & 0xff; val[3] = addr & 0xff; oplen += 2; vptr = 4; val[0] = 0x00 | opcode[j].opmask; break; case 1: val[2] = addr & 0xff; oplen += 1; vptr = 3; val[0] = 0x40 | opcode[j].opmask; break; case 2: val[2] = addr & 0xff; oplen += 1; vptr = 3; val[0] = 0x80 | opcode[j].opmask; break; default: return SCPE_ARG; break; } if (*cptr == ',') cptr++; cptr = parse_addr(cptr, gbuf, &addr, &addtyp); switch(addtyp) { case 0: val[vptr] = (addr >> 8) & 0xff; val[vptr+1] = addr & 0xff; oplen += 2; break; case 1: val[vptr] = addr & 0xff; oplen += 1; val[0] = 0x10 | val[0]; break; case 2: val[vptr] = addr & 0xff; oplen += 1; val[0] = 0x20 | val[0]; break; default: return SCPE_ARG; break; } val[1] = opcode[j].q; break; default: break; } return (-(oplen-1)); } char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype) { int32 nybble = 0; char temp[32]; cptr = get_glyph(cptr, gbuf, ','); if (gbuf[0] == '(') { /* XR relative */ strcpy(temp, gbuf+1); sscanf(temp, "%x", addr); if (*cptr == ',') cptr++; cptr = get_glyph(cptr, gbuf, ','); nybble = -1; if (strcmp(gbuf, "XR1)") == 0) nybble = 1; if (strcmp(gbuf, "XR2)") == 0) nybble = 2; } else { /* Direct */ sscanf(gbuf, "%x", addr); nybble = 0; } *addrtype = nybble; return cptr; } simh-3.8.1/S3/s3_cd.c0000644000175000017500000003674310303752644012306 0ustar vlmvlm/* s3_cd.c: IBM 1442 card reader/punch Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. cdr card reader cdp card punch cdp2 card punch stacker 2 25-Apr-03 RMS Revised for extended file support 08-Oct-02 RMS Added impossible function catcher Normally, cards are represented as ASCII text streams terminated by newlines. This allows cards to be created and edited as normal files. Set the EBCDIC flag on the card unit allows cards to be read or punched in EBCDIC format, suitable for binary data. */ #include "s3_defs.h" #include extern uint8 M[]; extern char ebcdic_to_ascii[256]; extern char ascii_to_ebcdic[256]; int32 s1sel, s2sel; char rbuf[CBUFSIZE]; /* > CDR_WIDTH */ t_stat cdr_svc (UNIT *uptr); t_stat cdr_boot (int32 unitno, DEVICE *dptr); t_stat cdr_attach (UNIT *uptr, char *cptr); t_stat cd_reset (DEVICE *dptr); t_stat read_card (int32 ilnt, int32 mod); t_stat punch_card (int32 ilnt, int32 mod); int32 DAR; /* Data address register */ int32 LCR; /* Length Count Register */ int32 lastcard = 0; /* Last card switch */ int32 carderr = 0; /* Error switch */ int32 pcherror = 0; /* Punch error */ int32 notready = 0; /* Not ready error */ int32 cdr_ebcdic = 0; /* EBCDIC mode on reader */ int32 cdp_ebcdic = 0; /* EBCDIC mode on punch */ extern int32 GetMem(int32 addr); extern int32 PutMem(int32 addr, int32 data); /* Card reader data structures cdr_dev CDR descriptor cdr_unit CDR unit descriptor cdr_reg CDR register list */ UNIT cdr_unit = { UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), 100 }; REG cdr_reg[] = { { FLDATA (LAST, lastcard, 0) }, { FLDATA (ERR, carderr, 0) }, { FLDATA (NOTRDY, notready, 0) }, { HRDATA (DAR, DAR, 16) }, { HRDATA (LCR, LCR, 16) }, { FLDATA (EBCDIC, cdr_ebcdic, 0) }, { FLDATA (S2, s2sel, 0) }, { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, { NULL } }; DEVICE cdr_dev = { "CDR", &cdr_unit, cdr_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, &cdr_boot, &cdr_attach, NULL }; /* CDP data structures cdp_dev CDP device descriptor cdp_unit CDP unit descriptor cdp_reg CDP register list */ UNIT cdp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; REG cdp_reg[] = { { FLDATA (ERR, pcherror, 0) }, { FLDATA (EBCDIC, cdp_ebcdic, 0) }, { FLDATA (S2, s2sel, 0) }, { FLDATA (NOTRDY, notready, 0) }, { HRDATA (DAR, DAR, 16) }, { HRDATA (LCR, LCR, 16) }, { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; DEVICE cdp_dev = { "CDP", &cdp_unit, cdp_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, NULL, NULL, NULL }; /* Stacker data structures stack_dev STACK device descriptor stack_unit STACK unit descriptors stack_reg STACK register list */ UNIT stack_unit[] = { { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } }; REG stack_reg[] = { { DRDATA (POS0, stack_unit[0].pos, 32), PV_LEFT }, { NULL } }; DEVICE stack_dev = { "CDP2", stack_unit, stack_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &cd_reset, NULL, NULL, NULL }; /* -------------------------------------------------------------------- */ /* 1442: master routine */ int32 crd (int32 op, int32 m, int32 n, int32 data) { int32 iodata; switch (op) { case 0: /* SIO 1442 */ /* if (n == 1) return STOP_IBKPT; */ switch (data) { /* Select stacker */ case 0x00: break; case 0x01: s2sel = 1; break; default: break; } switch (n) { case 0x00: /* Feed */ iodata = SCPE_OK; break; case 0x01: /* Read only */ if (cdr_ebcdic) iodata = read_card(0, 1); else iodata = read_card(0, 0); break; case 0x02: /* Punch and feed */ iodata = punch_card(0, 0); break; case 0x03: /* Read Col Binary */ iodata = read_card(0, 1); break; case 0x04: /* Punch no feed */ iodata = punch_card(0, 1); break; default: return STOP_INVDEV; } return iodata; case 1: /* LIO 1442 */ switch (n) { case 0x00: /* Load LCR */ LCR = data & 0xffff; break; case 0x04: DAR = data & 0xffff; break; default: return STOP_INVDEV; } return SCPE_OK; case 2: /* TIO 1442 */ iodata = 0; switch (n) { case 0x00: /* Error */ if (carderr || pcherror || notready) iodata = 1; if ((cdr_unit.flags & UNIT_ATT) == 0) iodata = 1; /* attached? */ break; case 0x02: /* Busy */ if (sim_is_active (&cdr_unit)) iodata = 1; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); case 3: /* SNS 1442 */ iodata = 0; switch (n) { case 0x01: break; case 0x02: break; case 0x03: if (carderr) iodata |= 0x80; if (lastcard) iodata |= 0x40; if (pcherror) iodata |= 0x20; if ((cdr_unit.flags & UNIT_ATT) == 0) iodata |= 0x08; if (notready) iodata |= 0x08; break; case 0x04: iodata = DAR; break; default: return (STOP_INVDEV << 16); } iodata |= ((SCPE_OK << 16) & 0xffff0000); return (iodata); case 4: /* APL 1442 */ iodata = 0; switch (n) { case 0x00: /* Error */ if (carderr || pcherror || notready) iodata = 1; if ((cdr_unit.flags & UNIT_ATT) == 0) iodata = 1; /* attached? */ break; case 0x02: /* Busy */ if (sim_is_active (&cdr_unit)) iodata = 1; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); default: break; } printf (">>CRD non-existent function %d\n", op); return SCPE_OK; } /* Card read routine mod 0 = ASCII read mod 1 = EBCDIC read */ t_stat read_card (int32 ilnt, int32 mod) { int32 i; t_stat r; if (sim_is_active (&cdr_unit)) { /* busy? */ sim_cancel (&cdr_unit); /* cancel */ if (r = cdr_svc (&cdr_unit)) return r; /* process */ } if (((cdp_unit.flags & UNIT_ATT) != 0 || (stack_unit[0].flags & UNIT_ATT) != 0) && /* Punch is attached and */ (cdr_unit.flags & UNIT_ATT) == 0) { /* reader is not --- */ for (i = 0; i < 80; i++) { /* Assume blank cards in hopper */ PutMem(DAR, 0x40); DAR++; } sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ return SCPE_OK; } if ((cdr_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ lastcard = carderr = notready = s1sel = s2sel = 0; /* default stacker */ for (i = 0; i < CBUFSIZE; i++) rbuf[i] = 0x20; /* clear buffer */ if (mod) { for (i = 0; i < 80; i++) { rbuf[i] = fgetc(cdr_unit.fileref); /* Read EBCDIC */ } } else { fgets (rbuf, CBUFSIZE, cdr_unit.fileref); /* read Ascii */ } if (feof (cdr_unit.fileref)) { /* eof? */ notready = 1; return STOP_NOCD; } if (ferror (cdr_unit.fileref)) { /* error? */ perror ("Card reader I/O error"); clearerr (cdr_unit.fileref); carderr = 1; return SCPE_OK; } cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ i = getc (cdr_unit.fileref); /* see if more */ if (feof (cdr_unit.fileref)) lastcard = 1; /* eof? set flag */ fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); for (i = 0; i < 80; i++) { if (mod == 0) { /* If ASCII mode... */ if (rbuf[i] == '\n' || /* remove ASCII CR/LF */ rbuf[i] == '\r' || rbuf[i] == 0x00) rbuf[i] = ' '; rbuf[i] = ascii_to_ebcdic[rbuf[i]]; /* convert to EBCDIC */ } PutMem(DAR, rbuf[i]); /* Copy to main memory */ DAR++; } sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ return SCPE_OK; } /* Card reader service. If a stacker select is active, copy to the selected stacker. Otherwise, copy to the normal stacker. If the unit is unattached, simply exit. */ t_stat cdr_svc (UNIT *uptr) { int32 i; if (s2sel) uptr = &stack_unit[0]; /* stacker 1? */ else uptr = &stack_unit[0]; /* then default */ if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ for (i = 0; i < CDR_WIDTH; i++) rbuf[i] = ebcdic_to_ascii[rbuf[i]]; for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; rbuf[CDR_WIDTH] = 0; /* null at end */ fputs (rbuf, uptr -> fileref); /* write card */ fputc ('\n', uptr -> fileref); /* plus new line */ if (ferror (uptr -> fileref)) { /* error? */ perror ("Card stacker I/O error"); clearerr (uptr -> fileref); } uptr -> pos = ftell (uptr -> fileref); /* update position */ return SCPE_OK; } /* Card punch routine mod: not used */ t_stat punch_card (int32 ilnt, int32 mod) { int32 i, colcount; static char pbuf[CDP_WIDTH + 1]; /* + null */ UNIT *uptr; if (s2sel) uptr = &stack_unit[0]; /* stack 2? */ else uptr = &cdp_unit; /* normal output */ if ((uptr -> flags & UNIT_ATT) == 0) { /* Attached? */ notready = 1; return SCPE_OK; } pcherror = s1sel = notready = 0; /* clear flags */ colcount = 128 - LCR; for (i = 0; i < colcount; i++) { /* Fetch data */ if (cdp_ebcdic) pbuf[i] = GetMem(DAR) & 0xff; else pbuf[i] = ebcdic_to_ascii[GetMem(DAR)]; DAR++; } for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; pbuf[CDP_WIDTH] = 0; /* trailing null */ if (!cdp_ebcdic) { fputs (pbuf, uptr -> fileref); /* output card */ fputc ('\n', uptr -> fileref); /* plus new line */ } else { for (i = 0; i < 80; i++) { fputc(pbuf[i], uptr -> fileref); } } if (ferror (uptr -> fileref)) { /* error? */ perror ("Card punch I/O error"); clearerr (uptr -> fileref); pcherror = 1; } uptr -> pos = ftell (uptr -> fileref); /* update position */ return SCPE_OK; } /* Select stack routine Modifiers have been checked by the caller Modifiers are 1, 2, for the respective stack */ t_stat select_stack (int32 ilnt, int32 mod) { if (mod == 1) s1sel = 1; else if (mod == 2) s2sel = 1; return SCPE_OK; } /* Card reader/punch reset */ t_stat cd_reset (DEVICE *dptr) { lastcard = carderr = notready = pcherror = 0; /* clear indicators */ s1sel = s2sel = 0; /* clear stacker sel */ sim_cancel (&cdr_unit); /* clear reader event */ return SCPE_OK; } /* Card reader attach */ t_stat cdr_attach (UNIT *uptr, char *cptr) { carderr = lastcard = notready = 0; /* clear last card */ return attach_unit (uptr, cptr); } /* Bootstrap routine */ t_stat cdr_boot (int32 unitno, DEVICE *dptr) { cdr_ebcdic = 1; DAR = 0; LCR = 80; read_card(0, 1); return SCPE_OK; } simh-3.8.1/S3/s3_disk.c0000644000175000017500000007216310303752644012646 0ustar vlmvlm/* s3_disk.c: IBM 5444 Disk Drives Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. r1 Removeable disk 1 f1 Fixed disk 1 r2 Removeable disk 2 f2 Fixed disk 2 25-Apr-03 RMS Revised for extended file support 08-Oct-02 RMS Added impossible function catcher */ #include "s3_defs.h" #include extern uint8 M[]; extern int32 IAR[], level; extern FILE *trace; extern int32 debug_reg; char dbuf[DSK_SECTSIZE]; /* Disk buffer */ int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data); int32 read_sector(UNIT *uptr, char *dbuf, int32 sect); int32 write_sector(UNIT *uptr, char *dbuf, int32 sect); t_stat r1_svc (UNIT *uptr); t_stat r1_boot (int32 unitno, DEVICE *dptr); t_stat r1_attach (UNIT *uptr, char *cptr); t_stat r1_reset (DEVICE *dptr); t_stat f1_svc (UNIT *uptr); t_stat f1_boot (int32 unitno, DEVICE *dptr); t_stat f1_attach (UNIT *uptr, char *cptr); t_stat f1_reset (DEVICE *dptr); t_stat r2_svc (UNIT *uptr); t_stat r2_boot (int32 unitno, DEVICE *dptr); t_stat r2_attach (UNIT *uptr, char *cptr); t_stat r2_reset (DEVICE *dptr); t_stat f2_svc (UNIT *uptr); t_stat f2_boot (int32 unitno, DEVICE *dptr); t_stat f2_attach (UNIT *uptr, char *cptr); t_stat f2_reset (DEVICE *dptr); extern int32 GetMem(int32 addr); extern int32 PutMem(int32 addr, int32 data); char opstr[5][5] = { "SIO", "LIO", "TIO", "SNS", "APL" }; int32 DDAR[2]; /* Data address register */ int32 DCAR[2]; /* Disk Control Address Register */ int32 diskerr[2] = { 0, 0 }; /* Error status */ int32 notrdy[2] = { 0, 0 }; /* Not ready error */ int32 seekbusy[2] = { 0, 0 }; /* Drive busy flags */ int32 seekhead[2] = { 0, 0 }; /* Disk head 0,1 */ int32 found[2] = { 0, 0 }; /* Scan found bit */ int32 RIDsect[2] = { 0, 0 }; /* for Read ID */ /* Disk data structures xy_dev CDR descriptor xy_unit CDR unit descriptor xy_reg CDR register list x = F or R y = 1 or 2 */ UNIT r1_unit = { UDATA (&r1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; REG r1_reg[] = { { FLDATA (NOTRDY, notrdy[0], 0) }, { FLDATA (SEEK, seekbusy[0], 0) }, { HRDATA (DAR, DDAR[0], 16) }, { HRDATA (CAR, DCAR[0], 16) }, { HRDATA (ERR, diskerr[0], 16) }, { DRDATA (CYL, r1_unit.u3, 8) }, { DRDATA (HEAD, seekhead[0], 8) }, { DRDATA (POS, r1_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, r1_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, dbuf, 8, 8, 256) }, { NULL } }; DEVICE r1_dev = { "R1", &r1_unit, r1_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &r1_reset, &r1_boot, &r1_attach, NULL }; UNIT f1_unit = { UDATA (&f1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; REG f1_reg[] = { { FLDATA (NOTRDY, notrdy[0], 0) }, { FLDATA (SEEK, seekbusy[0], 0) }, { HRDATA (DAR, DDAR[0], 16) }, { HRDATA (CAR, DCAR[0], 16) }, { HRDATA (ERR, diskerr[0], 16) }, { DRDATA (CYL, f1_unit.u3, 8) }, { DRDATA (HEAD, seekhead[0], 8) }, { DRDATA (POS, f1_unit.pos, 32), PV_LEFT }, { DRDATA (TIME, f1_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, dbuf, 8, 8, 256) }, { NULL } }; DEVICE f1_dev = { "F1", &f1_unit, f1_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &f1_reset, &f1_boot, &f1_attach, NULL }; UNIT r2_unit = { UDATA (&r2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; REG r2_reg[] = { { FLDATA (NOTRDY, notrdy[1], 0) }, { FLDATA (SEEK, seekbusy[1], 0) }, { HRDATA (DAR, DDAR[1], 16) }, { HRDATA (CAR, DCAR[1], 16) }, { HRDATA (ERR, diskerr[1], 16) }, { DRDATA (CYL, r2_unit.u3, 8) }, { DRDATA (HEAD, seekhead[1], 8) }, { DRDATA (POS, r2_unit.pos, 32), PV_LEFT }, { DRDATA (TIME, r2_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, dbuf, 8, 8, 256) }, { NULL } }; DEVICE r2_dev = { "R2", &r2_unit, r2_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &r2_reset, &r2_boot, &r2_attach, NULL }; UNIT f2_unit = { UDATA (&f2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; REG f2_reg[] = { { FLDATA (NOTRDY, notrdy[1], 0) }, { FLDATA (SEEK, seekbusy[1], 0) }, { HRDATA (DAR, DDAR[1], 16) }, { HRDATA (CAR, DCAR[1], 16) }, { HRDATA (ERR, diskerr[1], 16) }, { DRDATA (CYL, f2_unit.u3, 8) }, { DRDATA (HEAD, seekhead[1], 8) }, { DRDATA (POS, f2_unit.pos, 32), PV_LEFT }, { DRDATA (TIME, f2_unit.wait, 24), PV_LEFT }, { BRDATA (BUF, dbuf, 8, 8, 256) }, { NULL } }; DEVICE f2_dev = { "F2", &f2_unit, f2_reg, NULL, 1, 10, 31, 1, 8, 7, NULL, NULL, &f2_reset, &f2_boot, &f2_attach, NULL }; /* -------------------------------------------------------------------- */ /* 5444: master routines */ int32 dsk1 (int32 op, int32 m, int32 n, int32 data) { int32 r; r = dsk(0, op, m, n, data); return (r); } int32 dsk2 (int32 op, int32 m, int32 n, int32 data) { int32 r; r = dsk(1, op, m, n, data); return (r); } /* 5444: operational routine */ int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data) { int32 iodata, i, j, u, sect, nsects, addr, r, c, res; int32 F, C, S, N, usave; UNIT *uptr; u = m; if (disk == 1) u += 2; F = GetMem(DCAR[disk]+0); /* Flag bits */ C = GetMem(DCAR[disk]+1); /* Cylinder */ S = GetMem(DCAR[disk]+2); /* Sector */ N = GetMem(DCAR[disk]+3); /* Number of sectors */ switch (u) { case 0: uptr = r1_dev.units; break; case 1: uptr = f1_dev.units; break; case 2: uptr = r2_dev.units; break; case 3: uptr = f2_dev.units; break; default: break; } if (debug_reg & 0x02) fprintf(trace, "==> %04X %s %01X,%d,%04X DAR=%04X CAR=%04X C=%02X, S=%02X, N=%02X\n", IAR[level], opstr[op], m, n, data, DDAR[disk], DCAR[disk], C, S, N); switch (op) { /* SIO 5444 */ case 0: if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; diskerr[disk] = 0; /* SIO resets errors */ found[disk] = 0; /* ... and found bit */ iodata = 0; switch (n) { case 0x00: /* Seek */ if (S & 0x80) seekhead[disk] = 1; else seekhead[disk] = 0; if (S & 1) { uptr -> u3 += N; } else { uptr -> u3 -= N; } if (uptr -> u3 < 0) uptr -> u3 = 0; if (uptr -> u3 > 203) { uptr -> u3 = 0; diskerr[disk] |= 0x0100; if (debug_reg & 0x02) fprintf(trace, "==> Seek Past End of Disk\n"); } /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); /* Seek arms are the same for both disks on a drive: update the other arm */ usave = uptr -> u3; if (u == 0) uptr = f1_dev.units; if (u == 1) uptr = r1_dev.units; if (u == 2) uptr = f2_dev.units; if (u == 3) uptr = r2_dev.units; uptr -> u3 = usave; seekbusy[disk] = 1; iodata = SCPE_OK; break; case 0x01: /* Read */ switch (data) { case 0: /* Read data */ sect = (S >> 2) & 0x3F; nsects = N + 1; addr = DDAR[disk]; for (i = 0; i < nsects; i++) { r = read_sector(uptr, dbuf, sect); if (r != 1 || uptr->u3 != C) { diskerr[disk] |= 0x0800; break; } for (j = 0; j < DSK_SECTSIZE; j++) { PutMem(addr, dbuf[j]); addr++; } if ((sect == 55) ) { /* HJS MODS */ S = sect; N = nsects - i - 2; if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ DDAR[disk] = addr & 0xFFFF; /* HJS mod */ PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); sim_activate(uptr, 1); iodata = SCPE_OK; break; } sect++; S = sect - 1; N = nsects - i - 2; if (sect == 24) sect = 32; } DDAR[disk] = addr & 0xFFFF; /* HJS mod */ PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); iodata = SCPE_OK; break; case 1: /* Read ID */ if (uptr -> u3 > 0 && uptr -> u3 < 4) PutMem(DCAR[disk], 1); else PutMem(DCAR[disk], 0); PutMem(DCAR[disk]+1, uptr -> u3); PutMem(DCAR[disk]+2, RIDsect[disk]); RIDsect[disk]++; if (RIDsect[disk] > 23) RIDsect[disk] = 32; if (RIDsect[disk] > 55) RIDsect[disk] = 0; break; case 2: /* Read Diagnostic */ iodata = STOP_INVDEV; break; case 3: /* Verify */ sect = (S >> 2) & 0x3F; nsects = N + 1; addr = DDAR[disk]; for (i = 0; i < nsects; i++) { r = read_sector(uptr, dbuf, sect); if (r != 1 || uptr->u3 != C) { diskerr[disk] |= 0x0800; break; } if ((sect == 55) ) { /* HJS MODS */ S = sect; N = nsects - i - 2; if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); sim_activate(uptr, 1); iodata = SCPE_OK; break; } sect++; S = sect - 1; N = nsects - i - 2; if (sect == 24) sect = 32; } DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); break; default: return STOP_INVDEV; } break; case 0x02: /* Write */ switch (data) { case 0: /* Write Data */ sect = (S >> 2) & 0x3F; nsects = N + 1; addr = DDAR[disk]; for (i = 0; i < nsects; i++) { for (j = 0; j < DSK_SECTSIZE; j++) { dbuf[j] = GetMem(addr); addr++; } r = write_sector(uptr, dbuf, sect); if (r != 1 || uptr->u3 != C) { diskerr[disk] |= 0x0400; break; } if ((sect == 55) ) { /* HJS MODS */ S = sect; N = nsects - i - 2; if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); sim_activate(uptr, 1); iodata = SCPE_OK; break; } sect++; S = sect - 1; N = nsects - i - 2; if (sect == 24) sect = 32; } DDAR[disk] = addr & 0xFFFF; /* HJS mod */ PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); break; case 1: /* Write identifier */ if (seekhead[disk] == 0) S = 0; else S = 0x80; N = 23; sect = (S >> 2) & 0x3F; nsects = N + 1; addr = DDAR[disk]; for (i = 0; i < nsects; i++) { for (j = 0; j < DSK_SECTSIZE; j++) { dbuf[j] = GetMem(addr); } r = write_sector(uptr, dbuf, sect); if (r != 1) { diskerr[disk] |= 0x0400; break; } if ((sect == 55) ) { S = sect; N = nsects - i - 2; if (N > 0) diskerr[disk] |= 0x0020; DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); sim_activate(uptr, 1); iodata = SCPE_OK; break; } sect++; S = sect - 1; N = nsects - i - 2; if (sect == 24) sect = 32; } DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); break; default: return STOP_INVDEV; } break; case 0x03: /* Scan */ sect = (S >> 2) & 0x3F; nsects = N + 1; addr = DDAR[disk]; for (i = 0; i < nsects; i++) { r = read_sector(uptr, dbuf, sect); if (r != 1 || uptr->u3 != C) { diskerr[disk] |= 0x0800; break; } res = 0; for (j = 0; j < DSK_SECTSIZE; j++) { c = GetMem(addr); if (j != 0xff) { if (dbuf[i] < c) res = 1; if (dbuf[i] > c) res = 3; } addr++; } if (res == 0) found[disk] = 1; if (res == data) break; if ((sect == 55) ) { /* HJS MODS */ S = sect; N = nsects - i - 2; if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ DDAR[disk] = addr & 0xFFFF; PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); sim_activate(uptr, 1); iodata = SCPE_OK; break; } sect++; S = sect - 1; N = nsects - i - 2; if (sect == 24) sect = 32; } PutMem(DCAR[disk]+2, S << 2); PutMem(DCAR[disk]+3, N); /*sim_activate(uptr, uptr -> wait);*/ sim_activate(uptr, 1); break; default: return STOP_INVDEV; } return iodata; /* LIO 5444 */ case 1: if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; switch (n) { case 0x04: /* Data Addr */ DDAR[disk] = data; break; case 0x06: /* Control Addr */ DCAR[disk] = data; break; default: return STOP_INVDEV; } return SCPE_OK; /* TIO 5444 */ case 2: if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT << 16; iodata = 0; switch (n) { case 0x00: /* Error */ if (diskerr[disk] || notrdy[disk]) iodata = 1; if ((uptr -> flags & UNIT_ATT) == 0) iodata = 1; break; case 0x02: /* Busy */ if (sim_is_active (uptr)) iodata = 1; break; case 0x04: if (found[disk]) iodata = 1; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); /* SNS 5444 */ case 3: if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT << 16; iodata = 0; switch (n) { case 0x01: break; case 0x02: iodata = diskerr[disk]; if (notrdy[disk]) iodata |= 0x4000; if ((uptr -> flags & UNIT_ATT) == 0) iodata |= 0x4000; if (seekbusy[disk]) iodata |= 0x0010; if (uptr -> u3 == 0) iodata |= 0x0040; break; case 0x03: iodata = 0; break; case 0x04: iodata = DDAR[disk]; break; case 0x06: iodata = DCAR[disk]; break; default: return (STOP_INVDEV << 16); } iodata |= ((SCPE_OK << 16) & 0xffff0000); return (iodata); /* APL 5444 */ case 4: if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT << 16; iodata = 0; switch (n) { case 0x00: /* Error */ if (diskerr[disk] || notrdy[disk]) iodata = 1; if ((uptr -> flags & UNIT_ATT) == 0) iodata = 1; break; case 0x02: /* Busy */ if (sim_is_active (uptr)) iodata = 1; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); default: break; } printf (">>DSK%d non-existent function %d\n", disk, op); return SCPE_OK; } /* Disk unit service. If a stacker select is active, copy to the selected stacker. Otherwise, copy to the normal stacker. If the unit is unattached, simply exit. */ t_stat r1_svc (UNIT *uptr) { seekbusy[0] = 0; return SCPE_OK; } t_stat f1_svc (UNIT *uptr) { seekbusy[0] = 0; return SCPE_OK; } t_stat r2_svc (UNIT *uptr) { seekbusy[1] = 0; return SCPE_OK; } t_stat f2_svc (UNIT *uptr) { seekbusy[1] = 0; return SCPE_OK; } /* Disk reset */ t_stat r1_reset (DEVICE *dptr) { diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ found[0] = 0; sim_cancel (&r1_unit); /* clear event */ r1_unit.u3 = 0; /* cylinder 0 */ return SCPE_OK; } t_stat f1_reset (DEVICE *dptr) { diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ found[0] = 0; sim_cancel (&f1_unit); /* clear event */ f1_unit.u3 = 0; /* cylinder 0 */ return SCPE_OK; } t_stat r2_reset (DEVICE *dptr) { diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ found[1] = 0; sim_cancel (&r2_unit); /* clear event */ r2_unit.u3 = 0; /* cylinder 0 */ return SCPE_OK; } t_stat f2_reset (DEVICE *dptr) { diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ found[1] = 0; sim_cancel (&f2_unit); /* clear event */ f2_unit.u3 = 0; /* cylinder 0 */ return SCPE_OK; } /* Disk unit attach */ t_stat r1_attach (UNIT *uptr, char *cptr) { diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ found[0] = 0; uptr -> u3 = 0; /* cylinder 0 */ return attach_unit (uptr, cptr); } t_stat f1_attach (UNIT *uptr, char *cptr) { diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ found[0] = 0; uptr -> u3 = 0; /* cylinder 0 */ return attach_unit (uptr, cptr); } t_stat r2_attach (UNIT *uptr, char *cptr) { diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ found[1] = 0; uptr -> u3 = 0; /* cylinder 0 */ return attach_unit (uptr, cptr); } t_stat f2_attach (UNIT *uptr, char *cptr) { diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ found[1] = 0; uptr -> u3 = 0; /* cylinder 0 */ return attach_unit (uptr, cptr); } /* Bootstrap routine */ t_stat r1_boot (int32 unitno, DEVICE *dptr) { int i; r1_unit.u3 = 0; read_sector(r1_dev.units, dbuf, 0); for (i = 0; i < 256; i++) { M[i] = dbuf[i]; } return SCPE_OK; } t_stat f1_boot (int32 unitno, DEVICE *dptr) { int i; f1_unit.u3 = 0; read_sector(f1_dev.units, dbuf, 0); for (i = 0; i < 256; i++) { M[i] = dbuf[i]; } return SCPE_OK; } t_stat r2_boot (int32 unitno, DEVICE *dptr) { int i; r2_unit.u3 = 0; read_sector(r2_dev.units, dbuf, 0); for (i = 0; i < 256; i++) { M[i] = dbuf[i]; } return SCPE_OK; } t_stat f2_boot (int32 unitno, DEVICE *dptr) { int i; f2_unit.u3 = 0; read_sector(f2_dev.units, dbuf, 0); for (i = 0; i < 256; i++) { M[i] = dbuf[i]; } return SCPE_OK; } /* Raw Disk Data In/Out */ int32 read_sector(UNIT *uptr, char *dbuf, int32 sect) { static int32 rtn, realsect; static long pos; /* calculate real sector no */ if (sect > 23) realsect = sect - 8; else realsect = sect; /* physically read the sector */ pos = DSK_CYLSIZE * uptr -> u3; pos += DSK_SECTSIZE * realsect; rtn = fseek(uptr -> fileref, pos, 0); rtn = fread(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); return (rtn); } int32 write_sector(UNIT *uptr, char *dbuf, int32 sect) { static int32 rtn, realsect; static long pos; /* calculate real sector no */ if (sect > 23) realsect = sect - 8; else realsect = sect; if (uptr -> u3 == 0 && realsect == 32) rtn = 0; /* physically write the sector */ pos = DSK_CYLSIZE * uptr -> u3; pos += DSK_SECTSIZE * realsect; rtn = fseek(uptr -> fileref, pos, 0); rtn = fwrite(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); return (rtn); } simh-3.8.1/S3/s3_cpu.c0000644000175000017500000022233010303752644012474 0ustar vlmvlm/* s3_cpu.c: IBM System/3 CPU simulator Copyright (c) 2001-2005, Charles E. Owen HPL & SLC instruction code Copyright (c) 2001 by Henk Stegeman Decimal Arithmetic Copyright (c) 2000 by Roger Bowler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. ------------------------------------------------------------------------------ cpu System/3 (models 10 and 15) central processor The IBM System/3 was a popular small-business computing system introduced in 1969 as an entry-level system for businesses that could not afford the lowest rungs of the System/360. Its architecture is inspired by and in some ways similar to the 360, but to save cost the instruction set is much smaller and the I/O channel system greatly simplified. There is no compatibilty between the two systems. The original System/3 had two models, 6 and 10, and these came in two configurations: card system and disk system. The unique feature of the /3 was the use of 96-column cards, although traditional 80-column cards were supprted also via attachment of a 1442 reader/punch. System/3 is a batch-oriented system, controlled by an operating system known as SCP (System Control Program), with it's own job control language known as OCL (simpler and more logical than the JCL used on the mainframes). Original models did not support multiprogramming or any form of interactivity. (There was a hardware dual-program facility available on the model 10 at the high end). The line grew throughout the 1970s, overlapping the low end of the 360 line with the introduction of the model 15. The 15 (and later larger variations of the model 12) broke the 64K limit designed in the original models by adding a simple address translation unit to support up to 512K bytes. The model 15 added a system of storage protection and allowed multiprogramming in up to 3 partitions. Communications were added to allow support of multiple 3270 terminals and the models 12 and 15 broke the batch orientation and facilitated interactive use via the CCP (communications control program). The System/3 was effectively replaced by the much easier to manage and use System/34 and System/36 at the low and middle of the range, and by System/370 or System/38 at the high end. This simulator implements the model 10 and model 15. Models 4, 6, 8, and 12 are not supported (these were technical variations on the design which offered no functionality not present on either 10 or 15). The System/3 is a byte-oriented machine with a data path of 8 bits in all models, and an address width of 16 bits. The register state for the System/3 CPU is: BAR <0:15> Operand 1 address register AAR <0:15> Operand 2 address register XR1 <0:15> Index Register 1 XR2 <0:15> Index Register 2 PSR <0:15> Condition Register IAR [0:9]<0:15> Instruction Address Register (p1, p2, plus 1 for each interrupt) ARR [0:9]<0:15> Address Recall Register (p1, p2, plus 1 for each interrupt) (The P2 IAR & ARR are used for the Dual Program feature) Instruction formats follow the same basic pattern: a 1-byte opcode, a 1-byte "Q byte", and one or two addresses following in a format defined by the first 4 bits of the opcode: Op Code Q Byte Address(es) 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... | A 1 | A 2 | operation | | (defined by operation)| | Format based on A1, A2 +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... { --- } <---------------- Bits 00 = Operand 2 specified by 2-byte direct addr Bits 01 = Operand 2 is 1-byte displacement + XR1 Bits 10 = Operand 2 is 1-byte displacement + XR2 Bits 11 = Operand 2 is not used { --- } <---------------------- Bits 00 = Operand 1 specified by 2-byte direct addr Bits 01 = Operand 1 is 1-byte displacement + XR1 Bits 10 = Operand 1 is 1-byte displacement + XR2 Bits 11 = Operand 1 is not used Instructions come in 3 basic formats, of varying lengths which are determined by the top 4 bits of opcode defined above. Minimum instruction length is 3 bytes, maximum is 6. 1) Command Format (Bits 0-3 are 1111): +------------+ +------------+ +------------+ | Opcode | | Q-byte | | R-byte + +------------+ +------------+ +------------+ (The meaning of Q-byte and R-byte defined by the operation) 2) One Address Instructions (either bits 0-1 or bits 2-3 are 01): Direct Addressing Format: +------------+ +------------+ +-----------+----------+ | Opcode | | Q-byte | | MSB + LSB + +------------+ +------------+ +-----------+----------+ Base-Displacement Format: +------------+ +------------+ +------------+ | Opcode | | Q-byte | |displacement+ +------------+ +------------+ +------------+ Opcodes are 0011xxxx or 1100xxxx. Q-byte can be: 1) An immediate operand 2) A mask 3) A branch condition 4) A data selection 2) Two Address Instructions (neither bits 0-1 nor bits 2-3 are both 11): Operand 1 Address Direct (opcodes 0001 or 0010): +------------+ +------------+ +----------+----------+ +------------+ | Opcode | | Q-byte | | MSB + LSB + |displacement| +------------+ +------------+ +----------+----------+ +------------+ Operand 2 Address Direct (opcodes 0100 or 1000): +------------+ +------------+ +------------+ +----------+----------+ | Opcode | | Q-byte | |displacement| | MSB + LSB + +------------+ +------------+ +------------+ +----------+----------+ Both Addresses Direct (opcode 0000): +------------+ +------------+ +----------+----------+ +-----------+----------+ | Opcode | | Q-byte | | MSB + LSB + + MSB + LSB + +------------+ +------------+ +----------+----------+ +-----------+----------+ Both Addresses Displacement (opcodes 0101, 0110, 1001, or 1010): +------------+ +------------+ +------------+ +------------+ | Opcode | | Q-byte | |displacement| |displacement| +------------+ +------------+ +------------+ +------------+ Assembler Mnemonic Format ------------------------- The assembler format contains the same elements as the machine language operation, but not always in the same format. The operation code frequently specifies both the opcode and the Q byte, and the top nybble of the opcode is determined by the format of the addresses. Addresses take two forms: the direct address in hex, or a relative address specified thusly: (byte,XRx) where 'byte' is a 1-byte offset, and XRx is either XR1 or XR2 for the two index registers. Use these formats when 'address' is indicated below: When 'reg' is mentioned, a mnemonic may be used for the register, thusly: IAR Instruction Address Register for the current program level ARR Address Recall Register for the current program level P1IAR IAR for Program Level 1 P2IAR IAR for Program Level 2 PSR Program Status Register 0x01 - Equal 0x02 - Low 0x04 - High 0x08 - Decimal overflow 0x10 - Test false 0x20 - Binary overflow 0x40 - Not used 0x80 - Not used XR1 Index Register 1 XR2 Index Register 2 IARx IAR for the interrupt level x (x = 0 thru 7) All other operands mentioned below are single-byte hex, except for the length (len) operand of the two-address instructions, which is a decimal length in the range 1-256. No-address formats: ------------------ HPL hex,hex Halt Program Level, the operands are the Q and R bytes One-address formats: ------------------- A reg,address Add to register CLI address,byte Compare Logical Immediate MVI address,byte Move Immediate TBF address,mask Test Bits Off TBN address,mask Test Bits On SBF address,mask Set Bits Off SBN address,mask Set Bits On ST reg,address Store Register L reg,address Load Register LA reg,address Load Address JC address,cond Jump on Condition BC address,cond Branch on Condition These operations do not specify a qbyte, it is implicit in the opcode: B address Unconditional branch to address BE address Branch Equal BNE address Branch Not Equal BH address Branch High BNH address Branch Not High BL address Branch Low BNL address Branch Not Low BT address Branch True BF address Branch False BP address Branch Plus BM address Branch Minus BNP address Branch Not Plus BNM address Branch Not Minus BZ address Branch Zero BNZ address Branch Not Zero BOZ address Branch Overflow Zoned BOL address Branch Overflow Logical BNOZ address Branch No Overflow Zoned BNOL address Branch No Overflow Logical NOPB address No - never jump (substitute J for B above for a set of Jumps -- 1-byte operand (not 2), always jumps forward up to 255 bytes. In this case, 'address' cannot be less than the current address, nor greater than the current address + 255) Two-address formats (first address is destination, len is decimal 1-256): ------------------- MVC address,address,len Move Characters CLC address,address,len Compare Logical Characters ALC address,address,len Add Logical Characters SLC address,address,len Subtract Logical Characters ED address,address,len Edit ITC address,address,len Insert and Test Characters AZ address,address,len Add Zoned Decimal SZ address,address,len Subtract Zoned Decimal MNN address,address Move Numeric to Numeric MNZ address,address Move Numeric to Zone MZZ address,address Move Zone to Zone MZN address,address Move Zone to Numeric I/O Format ---------- In the I/O format, there are always 3 fields: da - Device Address 0-15 (decimal) m - Modifier 0-1 n - Function 0-7 The meaning of these is entirely defined by the device addressed. There may be an optional control byte, or an optional address (based on the type of instruction). SNS da,m,n,address Sense I/O LIO da,m,n,address Load I/O TIO da,m,n,address Test I/O SIO da,m,n,cc Start I/O -- cc is a control byte APL da,m,n Advance Program Level --------------------------------------------- Here is a handy opcode cross-reference table: --------------------------------------------- | x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF ---+------------------------------------------------------------------ 0x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 1x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 2x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 3x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - | 4x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 5x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 6x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 7x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - | 8x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC 9x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC Ax | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC Bx | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - | Cx | BC TIO LA - - - - - - - - - - - - - Dx | BC TIO LA - - - - - - - - - - - - - Ex | BC TIO LA - - - - - - - - - - - - - Fx | HPL APL JC SIO - - - - - - - - - - - - This routine is the instruction decode routine for System/3. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered program check caused by invalid opcode or qbyte or address or I/O spec unknown I/O device and STOP_DEV flag set I/O error in I/O simulator 2. Interrupts. There are 8 levels of interrupt, each with it's own IAR (program counter). When an interrupt occurs, execution begins at the location in the IAR for that level interrupt. The program must save and restore state. Each device is assigned both a level and a priority in hardware. Interrupts are reset via an SIO instruction, when this happens, the program level IAR resumes control. Interrupts are maintained in the global variable int_req, which is zero if no interrupts are pending, otherwise, the lower 16 bits represent devices, rightmost bit being device 0. Each device requesting an interrupt sets its bit on. 3. Non-existent memory. On the System/3, any reference to non-existent memory (read or write) causes a program check and machine stop. 4. Adding I/O devices. These modules must be modified: ibms3_defs.h add interrupt request definition ibms3_cpu.c add IOT mask, PI mask, and routine to dev_table ibms3_sys.c add pointer to data structures to sim_devices */ #include "s3_defs.h" #define UNIT_V_M15 (UNIT_V_UF) /* Model 15 extensions */ #define UNIT_M15 (1 << UNIT_V_M15) #define UNIT_V_DPF (UNIT_V_UF+1) /* Dual Programming */ #define UNIT_DPF (1 << UNIT_V_DPF) #define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) uint8 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 AAR = 0; /* Operand 1 addr reg */ int32 BAR = 0; /* Operand 2 addr reg */ int32 XR1 = 0; /* Index register 1 */ int32 XR2 = 0; /* Index register 2 */ int32 PSR = 0; /* Condition Register */ int32 IAR[10] = { 0 }; /* IAR 0-7 = int level 8=P1 9=P2 */ int32 ARR[10] = { 0 }; /* ARR 0-7 = int level 8=P1 9=P2 */ int32 dev_disable = 0; /* interrupt disable mask */ int32 int_req = 0; /* Interrupt request device bitmap */ int32 level = 8; /* Current Execution Level*/ int32 stop_dev = 0; /* stop on ill dev */ int32 SR = 0; /* Switch Register */ int32 saved_PC; /* Saved (old) PC) */ int32 debug_reg = 0; /* set for debug/trace */ int32 debug_flag = 0; /* 1 when trace.log open */ FILE *trace; extern int32 sim_int_char; extern int32 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_boot (int32 unitno, DEVICE *dptr1); extern int32 pkb (int32 op, int32 m, int32 n, int32 data); extern int32 crd (int32 op, int32 m, int32 n, int32 data); extern int32 lpt (int32 op, int32 m, int32 n, int32 data); extern int32 dsk1 (int32 op, int32 m, int32 n, int32 data); extern int32 dsk2 (int32 op, int32 m, int32 n, int32 data); extern int32 cpu (int32 op, int32 m, int32 n, int32 data); extern t_stat sim_activate (UNIT *uptr, int32 delay); extern int32 fprint_sym (FILE *of, int32 addr, uint32 *val, UNIT *uptr, int32 sw); int32 nulldev (int32 opcode, int32 m, int32 n, int32 data); int add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); static int32 compare(int32 byte1, int32 byte2, int32 cond); static int32 condition(int32 qbyte); static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign); static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign); static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count); static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign); int32 GetMem(int32 addr); int32 PutMem(int32 addr, int32 data); /* IOT dispatch table */ /* System/3 supports only 16 unique device addresses! */ struct ndev dev_table[16] = { { 0, 0, &cpu }, /* Device 0: CPU control */ { 1, 0, &pkb }, /* Device 1: 5471 console printer/keyboard */ { 0, 0, &nulldev }, { 0, 0, &nulldev }, { 0, 0, &nulldev }, { 0, 0, &crd }, /* Device 5: 1442 card reader/punch */ { 0, 0, &nulldev }, /* Device 6: 3410 Tape drives 1 & 2 */ { 0, 0, &nulldev }, /* Device 7: 3410 Tape drives 3 & 4 */ { 0, 0, &nulldev }, { 0, 0, &nulldev }, { 0, 0, &dsk1 }, /* Device 10: 5444 Disk Drive 1 */ { 0, 0, &dsk2 }, /* Device 11: 5444 Disk Drive 2 */ { 0, 0, &nulldev }, /* Device 12: 5448 Disk Drive 1 */ { 0, 0, &nulldev }, /* DEvice 13: 5448 Disk Drive 2 */ { 0, 0, &lpt }, /* Device 14: 1403/5203 Printer */ { 0, 0, &nulldev } /* Device 15: 5424 MFCU */ }; /* Priority assigned to interrupt levels */ int32 priority[8] = {8, 7, 5, 4, 3, 6, 2, 1}; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { HRDATA (IAR, saved_PC, 16), REG_RO }, { HRDATA (IAR-P1, IAR[8], 16) }, { HRDATA (IAR-P2, IAR[9], 16) }, { HRDATA (ARR-P1, ARR[8], 16) }, { HRDATA (ARR-P2, ARR[9], 16) }, { HRDATA (AAR, AAR, 16) }, { HRDATA (BAR, BAR, 16) }, { HRDATA (XR1, XR1, 16) }, { HRDATA (XR2, XR2, 16) }, { HRDATA (PSR, PSR, 16) }, { HRDATA (SR, SR, 16) }, { HRDATA (INT, int_req, 16), REG_RO }, { HRDATA (LEVEL, level, 16) }, { HRDATA (IAR0, IAR[0], 16) }, { HRDATA (IAR1, IAR[1], 16) }, { HRDATA (IAR2, IAR[2], 16) }, { HRDATA (IAR3, IAR[3], 16) }, { HRDATA (IAR4, IAR[4], 16) }, { HRDATA (IAR5, IAR[5], 16) }, { HRDATA (IAR6, IAR[6], 16) }, { HRDATA (IAR7, IAR[7], 16) }, { HRDATA (ARR0, ARR[0], 16) }, { HRDATA (ARR1, ARR[1], 16) }, { HRDATA (ARR2, ARR[2], 16) }, { HRDATA (ARR3, ARR[3], 16) }, { HRDATA (ARR4, ARR[4], 16) }, { HRDATA (ARR5, ARR[5], 16) }, { HRDATA (ARR6, ARR[6], 16) }, { HRDATA (ARR7, ARR[7], 16) }, { HRDATA (DISABLE, dev_disable, 16), REG_RO }, { FLDATA (STOP_DEV, stop_dev, 0) }, { HRDATA (WRU, sim_int_char, 8) }, { HRDATA (DEBUG, debug_reg, 16) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_M15, UNIT_M15, "M15", "M15", NULL }, { UNIT_M15, 0, "M10", "M10", NULL }, { UNIT_DPF, UNIT_DPF, "DPF", "DPF", NULL }, { UNIT_DPF, 0, "NODPF", "NODPF", NULL }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 16, 16, 1, 16, 8, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; t_stat sim_instr (void) { extern int32 sim_interval; register int32 PC, IR; int32 i, j, carry, zero, op1, op2; int32 opcode = 0, qbyte = 0, rbyte = 0; int32 opaddr, addr1, addr2, dlen1, dlen2, r; int32 int_savelevel = 8, intpri, intlev, intdev, intmask; int32 devno, devm, devn; char display[3][9]; int32 val [32]; register t_stat reason; /* Restore register state */ PC = IAR[level]; /* load local PC */ reason = 0; /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; } if (int_req) { /* interrupt? */ intpri = 16; for (i = 0; i < 16; i++) { /* Get highest priority device */ if ((int_req >> i) & 0x01) { intlev = dev_table[i].level; if (priority[intlev] < intpri) { intdev = i; intpri = priority[intlev]; } } } intmask = 1 << intdev; /* mask is interrupting dev bit */ int_req = ~int_req & intmask; /* Turn off int_req for device */ int_savelevel = level; /* save current level for reset */ level = dev_table[intdev].level; /* get int level from device */ PC = IAR[level]; /* Use int level IAR for new PC */ } /* end interrupt */ if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } /* Machine Instruction Execution Here */ if ((debug_reg == 0) && debug_flag == 1) { fclose(trace); debug_flag = 0; } if (debug_reg) { if (!debug_flag) { trace = fopen("trace.log", "w"); debug_flag = 1; } } if (debug_reg & 0x01) { fprintf(trace, "ARR=%04X XR1=%04X XR2=%04X IAR=%04X ", ARR[level], XR1, XR2, PC); val[0] = GetMem(PC); val[1] = GetMem(PC+1); val[2] = GetMem(PC+2); val[3] = GetMem(PC+3); val[4] = GetMem(PC+4); val[5] = GetMem(PC+5); fprint_sym(trace, PC, (uint32 *) val, &cpu_unit, SWMASK('M')); fprintf(trace, "\n"); } saved_PC = PC; opaddr = GetMem(PC) & 0xf0; /* fetch addressing mode */ opcode = GetMem(PC) & 0x0f; /* fetch opcode */ PC = (PC + 1) & AMASK; sim_interval = sim_interval - 1; qbyte = GetMem(PC) & 0xff; /* fetch qbyte */ PC = (PC + 1) & AMASK; if (opaddr == 0xf0) { /* Is it command format? */ rbyte = GetMem(PC) & 0xff; PC = (PC + 1) & AMASK; switch (opcode) { case 0x00: /* HPL: Halt Program Level */ for (i = 0; i < 3; i++) { for (j = 0; j < 9; j++) { display[i][j] = ' '; } } /* First line */ if (qbyte & 0x04) display[0][2] = '_' ; if (rbyte & 0x04) display[0][6] = '_' ; /* Second line */ if (qbyte & 0x08) display[1][1] = '|' ; if (rbyte & 0x08) display[1][5] = '|' ; if (qbyte & 0x10) display[1][2] = '_' ; if (rbyte & 0x10) display[1][6] = '_' ; if (qbyte & 0x02) display[1][3] = '|' ; if (rbyte & 0x02) display[1][7] = '|' ; /* Third line */ if (qbyte & 0x20) display[2][1] = '|' ; if (rbyte & 0x20) display[2][5] = '|' ; if (qbyte & 0x40) display[2][2] = '_' ; if (rbyte & 0x40) display[2][6] = '_' ; if (qbyte & 0x01) display[2][3] = '|' ; if (rbyte & 0x01) display[2][7] = '|' ; /* Print display segment array */ printf("\n\r"); for (i = 0; i < 3; i++) { for (j = 0; j < 9; j++) { printf ("%c", display[i][j]); } printf ("\n\r"); } reason = STOP_HALT; break; case 0x01: /* APL: Advance Program Level */ devno = (qbyte >> 4) & 0x0f; devm = (qbyte >> 3) & 0x01; devn = qbyte & 0x07; op1 = dev_table[devno].routine(4, devm, devn, rbyte); if (op1 & 0x01) { if (cpu_unit.flags & UNIT_DPF) { /* Dual Programming? */ if (level == 8) /* Yes: switch program levels */ level = 9; else level = 8; PC = IAR[level]; } else { /* No: Loop on this inst */ PC = PC - 3; } } reason = (op1 >> 16) & 0xffff; break; case 0x02: /* JC: Jump on Condition */ if (condition(qbyte) == 1) { PC = (PC + rbyte) & AMASK; } break; case 0x03: /* SIO: Start I/O */ devno = (qbyte >> 4) & 0x0f; devm = (qbyte >> 3) & 0x01; devn = qbyte & 0x07; reason = dev_table[devno].routine(0, devm, devn, rbyte); if (reason == RESET_INTERRUPT) { reason = SCPE_OK; IAR[level] = PC; level = int_savelevel; PC = IAR[level]; } break; default: reason = STOP_INVOP; break; } /* switch (opcode) */ IAR[level] = PC; continue; } /* Not command format: fetch the addresses */ addr1 = (opaddr >> 6) & 3; addr2 = (opaddr >> 4) & 3; switch (addr1) { case 0: BAR = GetMem(PC) << 8; PC = (PC + 1) & AMASK; BAR |=GetMem(PC); PC = (PC + 1) & AMASK; break; case 1: BAR = GetMem(PC); BAR = (BAR + XR1) & AMASK; PC = (PC + 1) & AMASK; break; case 2: BAR = GetMem(PC); BAR = (BAR + XR2) & AMASK; PC = (PC + 1) & AMASK; break; case 3: break; default: break; } /* switch (addr1) */ switch (addr2) { case 0: AAR = GetMem(PC) << 8; PC = (PC + 1) & AMASK; AAR |= GetMem(PC); PC = (PC + 1) & AMASK; break; case 1: AAR = GetMem(PC); AAR = (AAR + XR1) & AMASK; PC = (PC + 1) & AMASK; break; case 2: AAR = GetMem(PC); AAR = (AAR + XR2) & AMASK; PC = (PC + 1) & AMASK; break; case 3: break; default: break; } /* switch (addr1) */ switch (opaddr) { case 0x00: case 0x10: case 0x20: case 0x40: case 0x50: case 0x60: case 0x80: case 0x90: case 0xa0: switch (opcode) { case 4: /* ZAZ: Zero and Add Zoned */ dlen2 = qbyte & 0x0f; dlen1 = (qbyte >> 4) & 0xf; dlen1 += dlen2; op1 = BAR; for (i = 0; i < (dlen1+1); i++) { PutMem(op1, 0xf0); op1--; } r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); PSR &= 0xF8; /* HJS mod */ switch (r) { case 0: PSR |= 0x01; break; case 1: PSR |= 0x02; break; case 2: PSR |= 0x04; break; default: break; } break; case 6: /* AZ: Add Zoned */ dlen2 = qbyte & 0x0f; dlen1 = (qbyte >> 4) & 0xf; dlen1 += dlen2; r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); PSR &= 0xF0; switch (r) { case 0: PSR |= 0x01; break; case 1: PSR |= 0x02; break; case 2: PSR |= 0x04; break; case 3: PSR |= 0x08; break; default: break; } break; case 7: /* SZ: Subtract Zoned */ dlen2 = qbyte & 0x0f; dlen1 = (qbyte >> 4) & 0xf; dlen1 += dlen2; r = subtract_zoned(BAR, dlen1+1, AAR, dlen2+1); PSR &= 0xF0; switch (r) { case 0: PSR |= 0x01; break; case 1: PSR |= 0x02; break; case 2: PSR |= 0x04; break; case 3: PSR |= 0x08; break; default: break; } break; case 8: /* MVX: Move Hex */ op1 = GetMem(BAR); op2 = GetMem(AAR); switch (qbyte) { case 0: /* Zone to zone */ op1 = (op1 & 0x0F) | (op2 & 0xF0); break; case 1: /* Numeric to zone */ op1 = (op1 & 0x0F) | (op2 << 4); break; case 2: /* Zone to numeric */ op1 = (op1 & 0xF0) | (op2 >> 4); break; case 3: /* Numeric to numeric */ op1 = (op1 & 0xF0) | (op2 & 0x0F); break; default: reason = STOP_INVQ; break; } PutMem(BAR, op1); break; case 0xa: /* ED: Edit */ zero = 1; PSR &= 0xF8; IR = GetMem(AAR); if ((IR & 0xf0) != 0xF0) PSR |= 0x02; else PSR |= 0x04; while (qbyte > -1) { op2 = GetMem(AAR); op1 = GetMem(BAR); if (op1 == 0x20) { op2 |= 0xf0; PutMem(BAR, op2); AAR--; if (op2 != 0xF0) zero = 0; } BAR--; qbyte--; } if (zero) PSR |= 0x01; break; case 0xb: /* ITC: Insert and Test Chars */ op2 = GetMem(AAR); while (qbyte > -1) { op1 = GetMem(BAR); if (op1 >= 0xF1 && op1 <= 0xF9) break; PutMem(BAR, op2); BAR++; qbyte--; } ARR[level] = BAR; break; case 0xc: /* MVC: Move Characters */ while (qbyte > -1) { PutMem(BAR, GetMem(AAR)); BAR--; AAR--; qbyte--; } break; case 0xd: /* CLC: Compare Characters */ PSR &= 0xF8; i = BAR = BAR - qbyte; j = AAR = AAR - qbyte; while (qbyte > -1) { if (GetMem(i) > GetMem(j)) { PSR |= 0x04; break; } if (GetMem(i) < GetMem(j)) { PSR |= 0x02; break; } i++; j++; qbyte--; } if (qbyte == -1) PSR |= 0x01; break; case 0xe: /* ALC: Add Logical Characters */ carry = 0; zero = 1; while (qbyte > -1) { IR = GetMem(BAR) + GetMem(AAR) + carry; if (IR & 0x100) carry = 1; else carry = 0; if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ PutMem(BAR,(IR & 0xFF)); BAR--; AAR--; qbyte--; } PSR &= 0xD8; if (zero) PSR |= 0x01; /* Equal */ if (!zero && !carry) PSR |= 0x02; /* Low */ if (!zero && carry) PSR |= 0x04; /* High */ if (carry) PSR |= 0x20; /* Overflow */ break; case 0xf: /* SLC: Subtract Logical Characters */ carry = 1; zero = 1; while (qbyte > -1) { IR = GetMem(BAR) + (0xFF - GetMem(AAR)) + carry; if (IR & 0x100) carry = 1; else carry = 0; if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ PutMem(BAR,(IR & 0xFF)); BAR--; AAR--; qbyte--; } PSR &= 0xF8; if (zero) PSR |= 0x01; /* Equal */ if (!zero && !carry) PSR |= 0x02; /* Low */ if (!zero && carry) PSR |= 0x04; /* High */ break; default: reason = STOP_INVOP; break; } IAR[level] = PC; continue; break; case 0x30: case 0x70: case 0xb0: switch (opcode) { case 0: /* SNS: Sense I/O */ devno = (qbyte >> 4) & 0x0f; devm = (qbyte >> 3) & 0x01; devn = qbyte & 0x07; i = dev_table[devno].routine(3, devm, devn, rbyte); PutMem(BAR, i & 0xff); BAR--; PutMem(BAR, (i >> 8) & 0xff); reason = (i >> 16) & 0xffff; break; case 1: /* LIO: Load I/O */ devno = (qbyte >> 4) & 0x0f; devm = (qbyte >> 3) & 0x01; devn = qbyte & 0x07; op1 = GetMem(BAR); BAR--; op1 |= (GetMem(BAR) << 8) & 0xff00; reason = dev_table[devno].routine(1, devm, devn, op1); break; case 4: /* ST: Store Register */ switch (qbyte) { case 0x01: PutMem(BAR, XR1 & 0xff); BAR--; PutMem(BAR, (XR1 >> 8) & 0xff); break; case 0x02: PutMem(BAR, XR2 & 0xff); BAR--; PutMem(BAR, (XR2 >> 8) & 0xff); break; case 0x04: PutMem(BAR, PSR & 0xFF); BAR--; PutMem(BAR, 0); /* LCRR, not imp. */ break; case 0x08: PutMem(BAR, ARR[level] & 0xff); BAR--; PutMem(BAR, (ARR[level] >> 8) & 0xff); break; case 0x10: PutMem(BAR, IAR[level] & 0xff); BAR--; PutMem(BAR, (IAR[level] >> 8) & 0xff); break; case 0x20: PutMem(BAR, IAR[8] & 0xff); BAR--; PutMem(BAR, (IAR[8] >> 8) & 0xff); break; case 0x40: PutMem(BAR, IAR[9] & 0xff); BAR--; PutMem(BAR, (IAR[9] >> 8) & 0xff); break; case 0x80: PutMem(BAR, IAR[0] & 0xff); BAR--; PutMem(BAR, (IAR[0] >> 8) & 0xff); break; case 0x81: PutMem(BAR, IAR[7] & 0xff); BAR--; PutMem(BAR, (IAR[7] >> 8) & 0xff); break; case 0x82: PutMem(BAR, IAR[6] & 0xff); BAR--; PutMem(BAR, (IAR[6] >> 8) & 0xff); break; case 0x84: PutMem(BAR, IAR[5] & 0xff); BAR--; PutMem(BAR, (IAR[5] >> 8) & 0xff); break; case 0x88: PutMem(BAR, IAR[4] & 0xff); BAR--; PutMem(BAR, (IAR[4] >> 8) & 0xff); break; case 0x90: PutMem(BAR, IAR[3] & 0xff); BAR--; PutMem(BAR, (IAR[3] >> 8) & 0xff); break; case 0xA0: PutMem(BAR, IAR[2] & 0xff); BAR--; PutMem(BAR, (IAR[2] >> 8) & 0xff); break; case 0xC0: PutMem(BAR, IAR[1] & 0xff); BAR--; PutMem(BAR, (IAR[1] >> 8) & 0xff); break; default: reason = STOP_INVQ; break; } break; case 5: /* L: Load Register */ switch (qbyte) { case 0x01: XR1 = GetMem(BAR) & 0xff; BAR--; XR1 |= (GetMem(BAR) << 8) & 0xff00; break; case 0x02: XR2 = GetMem(BAR) & 0xff; BAR--; XR2 |= (GetMem(BAR) << 8) & 0xff00; break; case 0x04: PSR = GetMem(BAR) & 0xff; BAR--; break; case 0x08: ARR[level] = GetMem(BAR) & 0xff; BAR--; ARR[level] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x10: IAR[level] = GetMem(BAR) & 0xff; BAR--; IAR[level] |= (GetMem(BAR) << 8) & 0xff00; PC = IAR[level]; break; case 0x20: IAR[8] = GetMem(BAR) & 0xff; BAR--; IAR[8] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x40: IAR[9] = GetMem(BAR) & 0xff; BAR--; IAR[9] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x80: IAR[0] = GetMem(BAR) & 0xff; BAR--; IAR[0] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x81: IAR[7] = GetMem(BAR) & 0xff; BAR--; IAR[7] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x82: IAR[6] = GetMem(BAR) & 0xff; BAR--; IAR[6] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x84: IAR[5] = GetMem(BAR) & 0xff; BAR--; IAR[5] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x88: IAR[4] = GetMem(BAR) & 0xff; BAR--; IAR[4] |= (GetMem(BAR) << 8) & 0xff00; break; case 0x90: IAR[3] = GetMem(BAR) & 0xff; BAR--; IAR[3] |= (GetMem(BAR) << 8) & 0xff00; break; case 0xA0: IAR[2] = GetMem(BAR) & 0xff; BAR--; IAR[2] |= (GetMem(BAR) << 8) & 0xff00; break; case 0xC0: IAR[1] = GetMem(BAR) & 0xff; BAR--; IAR[1] |= (GetMem(BAR) << 8) & 0xff00; break; default: reason = STOP_INVQ; break; } break; case 6: /* A: Add to Register */ IR = GetMem(BAR) & 0x00ff; BAR--; IR |= (GetMem(BAR) << 8) & 0xff00; switch (qbyte) { case 0x01: IR += XR1; XR1 = IR & AMASK; break; case 0x02: IR += XR2; XR2 = IR & AMASK; break; case 0x04: IR += PSR; PSR = IR & AMASK; break; case 0x08: IR += ARR[level]; ARR[level] = IR & AMASK; break; case 0x10: IR += IAR[level]; IAR[level] = IR & AMASK; break; case 0x20: IR += IAR[8]; IAR[8] = IR & AMASK; break; case 0x40: IR += IAR[9]; IAR[9] = IR & AMASK; break; case 0x80: IR += IAR[0]; IAR[0] = IR & AMASK; break; case 0x81: IR += IAR[7]; IAR[7] = IR & AMASK; break; case 0x82: IR += IAR[6]; IAR[6] = IR & AMASK; break; case 0x84: IR += IAR[5]; IAR[5] = IR & AMASK; break; case 0x88: IR += IAR[4]; IAR[4] = IR & AMASK; break; case 0x90: IR += IAR[3]; IAR[3] = IR & AMASK; break; case 0xA0: IR += IAR[2]; IAR[2] = IR & AMASK; break; case 0xC0: IR += IAR[1]; IAR[1] = IR & AMASK; break; default: reason = STOP_INVQ; break; } PSR &= 0xD8; if ((IR & 0xffff) == 0) PSR |= 0x01; /* Zero */ if ((IR & 0xffff) != 0 && !(IR & 0x10000)) PSR |= 0x02; /* Low */ if ((IR & 0xffff) != 0 && (IR & 0x10000)) PSR |= 0x04; /* High */ if ((IR & 0x10000)) PSR |= 0x20; /* Bin overflow */ break; case 8: /* TBN: Test Bits On */ IR = GetMem(BAR); PSR &= 0xFF; if ((IR & qbyte) != qbyte) PSR |= 0x10; break; case 9: /* TBF: Test Bits Off */ IR = GetMem(BAR); PSR &= 0xFF; if ((IR & qbyte)) PSR |= 0x10; break; case 0xa: /* SBN: Set Bits On */ IR = GetMem(BAR); IR |= qbyte; PutMem(BAR, IR); break; case 0xb: /* SBF: Set Bits Off */ IR = GetMem(BAR); IR &= ~qbyte; PutMem(BAR, IR); break; case 0xc: /* MVI: Move Immediate */ PutMem(BAR, qbyte); break; case 0xd: /* CLI: Compare Immediate */ PSR = compare(GetMem(BAR), qbyte, PSR); break; default: reason = STOP_INVOP; break; } IAR[level] = PC; continue; break; case 0xc0: case 0xd0: case 0xe0: switch (opcode) { case 0: /* BC: Branch on Condition */ ARR[level] = AAR & AMASK; if (condition(qbyte) == 1) { IR = ARR[level]; ARR[level] = PC & AMASK; PC = IR; } break; case 1: /* TIO: Test I/O */ devno = (qbyte >> 4) & 0x0f; devm = (qbyte >> 3) & 0x01; devn = qbyte & 0x07; op1 = dev_table[devno].routine(2, devm, devn, rbyte); if (op1 & 0x01) { ARR[level] = AAR & AMASK; IR = ARR[level]; ARR[level] = PC & AMASK; PC = IR; } reason = (op1 >> 16) & 0xffff; break; case 2: /* LA: Load Address */ switch (qbyte) { case 1: XR1 = AAR; break; case 2: XR2 = AAR; break; default: reason = STOP_INVQ; break; } break; default: reason = STOP_INVOP; break; } /* switch (opcode) */ IAR[level] = PC; continue; default: reason = STOP_INVOP; break; } /* switch (opaddr) */ } /* end while (reason == 0) */ /* Simulation halted */ saved_PC = PC; return reason; } /* On models 4-12, these memory functions could be inline, but on a model 15 with ATU address mapping must be performed so they are in functions here for future expansion. */ /* Fetch a byte from memory */ int32 GetMem(int32 addr) { return M[addr] & 0xff; } /* Place a byte in memory */ int32 PutMem(int32 addr, int32 data) { M[addr] = data & 0xff; return 0; } /* Check the condition register against the qbyte and return 1 if true */ int32 condition(int32 qbyte) { int32 r = 0, t, q; t = (qbyte & 0xf0) >> 4; q = qbyte & 0x0f; if (qbyte & 0x80) { /* True if any condition tested = 1*/ if (((qbyte & 0x3f) & PSR) != 0) r = 1; } else { /* True if all conditions tested = 0 */ if (((qbyte & 0x3f) & PSR) == 0) r = 1; } /* these bits reset by a test */ if (qbyte & 0x10) PSR &= 0xEF; /* Reset test false if used */ if (qbyte & 0x08) PSR &= 0xF7; /* Reset decimal overflow if tested */ if (qbyte == 0x00) r = 1; /* unconditional branch */ if (qbyte == 0x80) r = 0; /* force no branch */ if (t >=0 && t < 8 && (q == 7 || q == 0xf)) r = 0; /* no-op */ if (t > 7 && t < 0x10 && (q == 7 || q == 0xf)) r = 1; /* Force branch */ return (r); } /* Given operand 1 and operand 2, compares the two and returns the System/3 condition register bits appropriately, given the condition register initial state in parameter 3 */ int32 compare(int32 byte1, int32 byte2, int32 cond) { int32 r; r = cond & 0xF8; /* mask off all but unaffected bits 2,3,4 */ if (byte1 == byte2) r |= 0x01; /* set equal bit */ if (byte1 < byte2) r |= 0x02; /* set less-than bit */ if (byte1 > byte2) r |= 0x04; /* set greater than bit */ return r; } /*-------------------------------------------------------------------*/ /* Add two zoned decimal operands */ /* */ /* Input: */ /* addr1 Logical address of packed decimal storage operand 1 */ /* len1 Length minus one of storage operand 1 (range 0-15) */ /* addr2 Logical address of packed decimal storage operand 2 */ /* len2 Length minus one of storage operand 2 (range 0-15) */ /* Output: */ /* The return value is the condition code: */ /* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ /* */ /* A program check may be generated if either logical address */ /* causes an addressing, translation, or fetch protection */ /* exception, or if either operand causes a data exception */ /* because of invalid decimal digits or sign, or if the */ /* first operand is store protected. Depending on the PSW */ /* program mask, decimal overflow may cause a program check. */ /*-------------------------------------------------------------------*/ int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) { int cc; /* Condition code */ uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ int count1, count2, count3; /* Significant digit counters*/ int sign1, sign2, sign3; /* Sign of operands & result */ /* Load operands into work areas */ load_decimal (addr1, len1, dec1, &count1, &sign1); load_decimal (addr2, len2, dec2, &count2, &sign2); /* Add or subtract operand values */ if (count2 == 0) { /* If second operand is zero then result is first operand */ memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); count3 = count1; sign3 = sign1; } else if (count1 == 0) { /* If first operand is zero then result is second operand */ memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); count3 = count2; sign3 = sign2; } else if (sign1 == sign2) { /* If signs are equal then add operands */ add_decimal (dec1, dec2, dec3, &count3); sign3 = sign1; } else { /* If signs are opposite then subtract operands */ subtract_decimal (dec1, dec2, dec3, &count3, &sign3); if (sign1 < 0) sign3 = -sign3; } /* Set condition code */ cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; /* Overflow if result exceeds first operand length */ if (count3 > len1) cc = 3; /* Set positive sign if result is zero */ if (count3 == 0) sign3 = 1; /* Store result into first operand location */ store_decimal (addr1, len1, dec3, sign3); /* Return condition code */ return cc; } /* end function add_packed */ /*-------------------------------------------------------------------*/ /* Subtract two zoned decimal operands */ /* */ /* Input: */ /* addr1 Logical address of packed decimal storage operand 1 */ /* len1 Length minus one of storage operand 1 (range 0-15) */ /* addr2 Logical address of packed decimal storage operand 2 */ /* len2 Length minus one of storage operand 2 (range 0-15) */ /* Output: */ /* The return value is the condition code: */ /* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ /* */ /* A program check may be generated if either logical address */ /* causes an addressing, translation, or fetch protection */ /* exception, or if either operand causes a data exception */ /* because of invalid decimal digits or sign, or if the */ /* first operand is store protected. Depending on the PSW */ /* program mask, decimal overflow may cause a program check. */ /*-------------------------------------------------------------------*/ int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) { int cc; /* Condition code */ uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ int count1, count2, count3; /* Significant digit counters*/ int sign1, sign2, sign3; /* Sign of operands & result */ /* Load operands into work areas */ load_decimal (addr1, len1, dec1, &count1, &sign1); load_decimal (addr2, len2, dec2, &count2, &sign2); /* Add or subtract operand values */ if (count2 == 0) { /* If second operand is zero then result is first operand */ memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); count3 = count1; sign3 = sign1; } else if (count1 == 0) { /* If first operand is zero then result is -second operand */ memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); count3 = count2; sign3 = -sign2; } else if (sign1 != sign2) { /* If signs are opposite then add operands */ add_decimal (dec1, dec2, dec3, &count3); sign3 = sign1; } else { /* If signs are equal then subtract operands */ subtract_decimal (dec1, dec2, dec3, &count3, &sign3); if (sign1 < 0) sign3 = -sign3; } /* Set condition code */ cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; /* Overflow if result exceeds first operand length */ if (count3 > len1) cc = 3; /* Set positive sign if result is zero */ if (count3 == 0) sign3 = 1; /* Store result into first operand location */ store_decimal (addr1, len1, dec3, sign3); /* Return condition code */ return cc; } /* end function subtract_packed */ /*-------------------------------------------------------------------*/ /* Add two decimal byte strings as unsigned decimal numbers */ /* */ /* Input: */ /* dec1 A 31-byte area containing the decimal digits of */ /* the first operand. Each byte contains one decimal */ /* digit in the low-order 4 bits of the byte. */ /* dec2 A 31-byte area containing the decimal digits of */ /* the second operand. Each byte contains one decimal */ /* digit in the low-order 4 bits of the byte. */ /* Output: */ /* result Points to a 31-byte area to contain the result */ /* digits. One decimal digit is placed in the low-order */ /* 4 bits of each byte. */ /* count Points to an integer to receive the number of */ /* digits in the result excluding leading zeroes. */ /* This field is set to zero if the result is all zero, */ /* or to MAX_DECIMAL_DIGITS+1 if overflow occurred. */ /*-------------------------------------------------------------------*/ static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count) { int d; /* Decimal digit */ int i; /* Array subscript */ int n = 0; /* Significant digit counter */ int carry = 0; /* Carry indicator */ /* Add digits from right to left */ for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) { /* Add digits from first and second operands */ d = dec1[i] + dec2[i] + carry; /* Check for carry into next digit */ if (d > 9) { d -= 10; carry = 1; } else { carry = 0; } /* Check for significant digit */ if (d != 0) n = MAX_DECIMAL_DIGITS - i; /* Store digit in result */ result[i] = d; } /* end for */ /* Check for carry out of leftmost digit */ if (carry) n = MAX_DECIMAL_DIGITS + 1; /* Return significant digit counter */ *count = n; } /* end function add_decimal */ /*-------------------------------------------------------------------*/ /* Subtract two decimal byte strings as unsigned decimal numbers */ /* */ /* Input: */ /* dec1 A 31-byte area containing the decimal digits of */ /* the first operand. Each byte contains one decimal */ /* digit in the low-order 4 bits of the byte. */ /* dec2 A 31-byte area containing the decimal digits of */ /* the second operand. Each byte contains one decimal */ /* digit in the low-order 4 bits of the byte. */ /* Output: */ /* result Points to a 31-byte area to contain the result */ /* digits. One decimal digit is placed in the low-order */ /* 4 bits of each byte. */ /* count Points to an integer to receive the number of */ /* digits in the result excluding leading zeroes. */ /* This field is set to zero if the result is all zero. */ /* sign -1 if the result is negative (operand2 > operand1) */ /* +1 if the result is positive (operand2 <= operand1) */ /*-------------------------------------------------------------------*/ static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign) { int d; /* Decimal digit */ int i; /* Array subscript */ int n = 0; /* Significant digit counter */ int borrow = 0; /* Borrow indicator */ int rc; /* Return code */ uint8 *higher; /* -> Higher value operand */ uint8 *lower; /* -> Lower value operand */ /* Compare digits to find which operand has higher numeric value */ rc = memcmp (dec1, dec2, MAX_DECIMAL_DIGITS); /* Return positive zero result if both operands are equal */ if (rc == 0) { memset (result, 0, MAX_DECIMAL_DIGITS); *count = 0; *sign = +1; return; } /* Point to higher and lower value operands and set sign */ if (rc > 0) { higher = dec1; lower = dec2; *sign = +1; } else { lower = dec1; higher = dec2; *sign = -1; } /* Subtract digits from right to left */ for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) { /* Subtract lower operand digit from higher operand digit */ d = higher[i] - lower[i] - borrow; /* Check for borrow from next digit */ if (d < 0) { d += 10; borrow = 1; } else { borrow = 0; } /* Check for significant digit */ if (d != 0) n = MAX_DECIMAL_DIGITS - i; /* Store digit in result */ result[i] = d; } /* end for */ /* Return significant digit counter */ *count = n; } /* end function subtract_decimal */ /*-------------------------------------------------------------------*/ /* Load a zoned decimal storage operand into a decimal byte string */ /* */ /* Input: */ /* addr Logical address of zoned decimal storage operand */ /* len Length minus one of storage operand (range 0-15) */ /* Output: */ /* result Points to a 31-byte area into which the decimal */ /* digits are loaded. One decimal digit is loaded */ /* into the low-order 4 bits of each byte, and the */ /* result is padded to the left with high-order zeroes */ /* if the storage operand contains less than 31 digits. */ /* count Points to an integer to receive the number of */ /* digits in the result excluding leading zeroes. */ /* This field is set to zero if the result is all zero. */ /* sign Points to an integer which will be set to -1 if a */ /* negative sign was loaded from the operand, or +1 if */ /* a positive sign was loaded from the operand. */ /* */ /* A program check may be generated if the logical address */ /* causes an addressing, translation, or fetch protection */ /* exception, or if the operand causes a data exception */ /* because of invalid decimal digits or sign. */ /*-------------------------------------------------------------------*/ static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign) { int h; /* Hexadecimal digit */ int i, j; /* Array subscripts */ int n; /* Significant digit counter */ if ((GetMem(addr) & 0xf0) == 0xD0) *sign = -1; else *sign = 1; j = len; for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { if (j) { h = GetMem(addr) & 0x0f; addr--; j--; } else { h = 0; } result [i-1] = h; if (h > 0) n = i; } *count = 32 - n; } /* end function load_decimal */ /*-------------------------------------------------------------------*/ /* Store decimal byte string into packed decimal storage operand */ /* */ /* Input: */ /* addr Logical address of packed decimal storage operand */ /* len Length minus one of storage operand (range 0-15) */ /* dec A 31-byte area containing the decimal digits to be */ /* stored. Each byte contains one decimal digit in */ /* the low-order 4 bits of the byte. */ /* sign -1 if a negative sign is to be stored, or +1 if a */ /* positive sign is to be stored. */ /* */ /* A program check may be generated if the logical address */ /* causes an addressing, translation, or protection exception. */ /*-------------------------------------------------------------------*/ static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign) { int i, j, a; /* Array subscripts */ j = len; a = addr; for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { if (j) { PutMem(a, (dec[i-1] | 0xf0)); a--; j--; } else { break; } } if (sign == -1) { PutMem(addr, (GetMem(addr) & 0x0f)); PutMem(addr, (GetMem(addr) | 0xf0)); } } /* end function store_decimal */ /* CPU Device Control */ int32 cpu (int32 op, int32 m, int32 n, int32 data) { int32 iodata = 0; switch (op) { case 0x00: /* Start IO */ return SCPE_OK; case 0x01: /* LIO */ return SCPE_OK; case 0x02: /* TIO */ break; case 0x03: /* SNS */ /* SNS CPU gets the data switches */ iodata = SR; break; case 0x04: /* APL */ break; default: break; } return ((SCPE_OK << 16) | iodata); } /* Null device */ int32 nulldev (int32 opcode, int32 m, int32 n, int32 data) { if (opcode == 1) return SCPE_OK; /* Ok to LIO unconfigured devices? */ return STOP_INVDEV; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int_req = 0; level = 8; 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] & 0xff; 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 & 0xff; return SCPE_OK; } 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; } t_stat cpu_boot (int32 unitno, DEVICE *dptr) { level = 8; IAR[8] = 0; return SCPE_OK; } simh-3.8.1/S3/system3.txt0000644000175000017500000003776507312420446013322 0ustar vlmvlm The IBM System/3 simulator is configured as follows: CPU 5410 (Model 10) CPU with 64KB of memory. PKB 5471 Printer/Keyboard console. CDR 1442 Card Reader CDP 1442 Card Punch CDP2 1442 2nd stacker LPT 1403 Line Printer R1 5444 Top Drive (removeable) F1 5444 Top Drive (fixed) R2 5444 Bottom Drive (removeable) F2 5444 Bottom Drive (fixed The only CPU options are to set Model 15 mode (not implemented), DPF (Dual Programming Facility, not implemented), and the memory size 8K, 16K, 32K, 48K, or 64K. CPU registers are the standard System/3 set: name size Description IAR-P1 16 Instruction Address Register for Program Level 1 ARR-P2 16 Address Recall Register for Program Level 1 IAR-P2 16 IAR for Program Level 2 (not implemented) ARR-P2 16 ARR for Program Level 2 (not implemented) AAR 16 A-Address Register BAR 16 B-Address Register PSR 16 Program Status Register XR1 16 Index Register 1 XR2 16 Index Register 2 IAR<0:7> 16 IAR for interrupt level 0 thru 7 ARR<0:7> 16 ARR for interrupt level 0 thru 7 Plus these simulator registers: IAR 16 Value of last IAR used. LEVEL 8 Current operating level (8=P1, 9=P2, 0 thru 7 = Interrupt level) SR 16 Front Panel switches INT 16 Interrupt Request Flags WRU 8 Simulator Interrupt Character BREAK 17 Breakpoint Address DEBUG 16 Debugging bits: 0x01: Write all instructions executed to file trace.log. 0x02: Write details of all Disk I/O requests to trace.log. 0x80: Breakpoint on first character written to 5471 printer. 1 5471 Printer/Keyboard This is the operator console. It has the following registers: FLAG 5471 Flag Bytes IBUF: Input character from keyboard OBUF: Output character to printer POS: Number of characters printed TIME: Delay for device operation REQKEY: ASCII value of key mapped to 5471 REQUEST key RTNKEY: ASCII value of key mapped to 5471 RETURN key ENDKEY: ASCII value of key mapped to 5471 END key CANKEY: ASCII value of key mapped to 5471 CANCEL key 2 1442 Card Reader. This reader reads 80-column cards; the input is usually an ASCII file which is translated to EBCDIC when read, but optionally can be a binary file in EBCDIC format (such as an object program). LAST Last card switch ERR Card Reader Error NOTRDY 1442 reader not ready (not attached or past EOF) DAR Data Address Register (shared with punch) LCR Length Count Register (shared with punch) EBCDIC EBCDIC mode flag: if 1, input is 80-col EBCDIC binary. (IPL from 1442 automatically sets this to 1). S2 Stacker 2 is selected when this is 1 POS Number of cards read TIME Device Delay The real hardware 1442 had only 1 hopper for cards, whether these were used for blank cards for punching, or cards to be read. Cards could be read without a feed cycle, then punched. When punching cards, the SCP does a read of a card, makes sure it is blank, and then punches it. To simulate this without requiring that a stack of blank lines be attached to the card reader device, a special feature of the simulator is this: if no file is attached to the cdr device, but a file is attached to the cdp or the cdp2 devices, any read from the reader will return a blank card -- i.e. when punching, an unattached cdr device is assumed to be loaded with an unlimited supply of blank cards. 3 1442 Card Punch. Normally cards are written to the attached disk file as ASCII with newline/cr delimiters. But an optional flag allows writing as 80-column binary EBCDIC. ERR Card Punch Error EBCDIC When this is 1, output will be 80-col EBCDIC. S2 When this is 1, output is placed in stacker 2 NOTRDY 1442 punch not ready (not attached) DAR Data Address Register (shared with reader) LCR Length Count Register (shared with reader) POS Number of cards punched TIME Device Delay 4 1442 Stacker 2. When cards are to be punched in stacker 2, attach a disk file to this device (cdp2) to hold that output. Note: When SCP punches cards, the default is to punch in stacker 2. POS0 Number of cards punched. 5 1403 Printer. This is a 132-column output device, emulating the famous IBM 1403, models 2, 6, and N1. Output is always translated to ASCII with newline/CR delimiters. Page advance is output as a form feed. ERR 1403 error flags LPDAR Data Address Register LPFLR Forms Length Register LPIAR Image Address Register LINECT Current Line on form POS Number of lines printed 6 5444 Disk Drives (R1, R2, F1, F2) The 5444 came as a set of two drives, each with two disks. The top disk in a drive was removable, the bottom fixed. The first drive consists of disks R1 and F1, the second drive R2 and F2. Each disk holds 2,467,600 bytes of user data, plus 3 alternate tracks and a CE track. Flagging of alternate tracks is not supported in this version of the simulator. NOTRDY Drive not ready (not attached) SEEK Drive is busy with a seek operation DAR Data Address Register CAR Control Address Register ERR Error Flags (16 bits) CYL Current Cylinder (0 thru 203) HEAD Current head (0 or 1) POS Current position in attached disk file TIME Device Delay 7 Symbolic Display and Input The System/3 Simulator supports symbolic display and input. Display is controlled by command line switches: (none) display as hex EBCDIC -c display bytes as characters -m display instruction mnemonics. -a display a 256-byte block of memory in both hex and ASCII. The symbolic format contains the same elements as the machine language operation, but not always in the same order. The operation code frequently specifies both the opcode and the Q byte, and the top nybble of the opcode is determined by the format of the addresses. Addresses take two forms: the direct address in hex, or a relative address specified thusly: (byte,XRx) where 'byte' is a 1-byte offset, and XRx is either XR1 or XR2 for the two index registers. Use these formats when 'address' is indicated below: When 'reg' is mentioned, a mnemonic may be used for the register, thusly: IAR Instruction Address Register for the current program level ARR Address Recall Register for the current program level P1IAR IAR for Program Level 1 P2IAR IAR for Program Level 2 PSR Program Status Register XR1 Index Register 1 XR2 Index Register 2 IARx IAR for the interrupt level x (x = 0 thru 7) All other operands mentioned below are single-byte hex, except for the length (len) operand of the two-address instructions, which is a decimal length in the range 1-256. In operations where there is a source and a destination, the destination is always operand 1, the source is operand 2. No-address formats: ------------------ HPL hex,hex Halt Program Level, the operands are the Q and R bytes. One-address formats: ------------------- A reg,address Add to register CLI address,byte Compare Logical Immediate MVI address,byte Move Immediate TBF address,mask Test Bits Off TBN address,mask Test Bits On SBF address,mask Set Bits Off SBN address,mask Set Bits On ST reg,address Store Register L reg,address Load Register LA reg,address Load Address (reg can only be XR1 or XR2) JC address,cond Jump on Condition BC address,cond Branch on Condition These operations do not specify a qbyte, it is implicit in the opcode: B address Unconditional branch to address BE address Branch Equal BNE address Branch Not Equal BH address Branch High BNH address Branch Not High BL address Branch Low BNL address Branch Not Low BT address Branch True BF address Branch False BP address Branch Plus BM address Branch Minus BNP address Branch Not Plus BNM address Branch Not Minus BZ address Branch Zero BNZ address Branch Not Zero BOZ address Branch Overflow Zoned BOL address Branch Overflow Logical BNOZ address Branch No Overflow Zoned BNOL address Branch No Overflow Logical NOPB address No - never branch (substitute J for B above for a set of Jumps -- 1-byte operand (not 2), always jumps forward up to 255 bytes from the address following the Jump instruction. In this case, 'address' cannot be less than the current address, nor greater than the current address + 255) Two-address formats (first address is destination, len is decimal 1-256): ------------------- MVC address,address,len Move Characters CLC address,address,len Compare Logical Characters ALC address,address,len Add Logical Characters SLC address,address,len Subtract Logical Characters ED address,address,len Edit ITC address,address,len Insert and Test Characters AZ address,address,len Add Zoned Decimal SZ address,address,len Subtract Zoned Decimal MNN address,address Move Numeric to Numeric MNZ address,address Move Numeric to Zone MZZ address,address Move Zone to Zone MZN address,address Move Zone to Numeric I/O Format ---------- In the I/O format, there are always 3 fields: da - Device Address 0-15 (decimal) m - Modifier 0-1 n - Function 0-7 The meaning of these is entirely defined by the device addressed. There may be an optional control byte, or an optional address (based on the type of instruction). SNS da,m,n,address Sense I/O LIO da,m,n,address Load I/O TIO da,m,n,address Test I/O SIO da,m,n,cc Start I/O -- cc is a control byte APL da,m,n Advance Program Level 8 Device Programming. Note: On a model 15, interrupts are used for all devices. On other models, interrupts are only used for the printer/keyboard. This is a summary of the DA, M, N, and CC codes for all supported devices: 5471 Printer Keyboard --------------------- The PKB has 2 visible indicators: Proceed and Request Pending. It has a normal keyboard and 4 special keys: Request, Return, End, and Cancel. SIO 1,0,0,XX Start Keyboard Operation, bit masks for XX are: X'20': Request Pending Indicator On X'10': Proceed Indicator On X'04': Request Key Interrupts Enable (1) Disable (0) X'02': Other Key Interrupts Enable (1) Disable (0) X'01': Reset request key and other key interrupts SIO 1,1,0,XX Start Printer Operation, bit masks for XX are: X'80': Start Printing X'40': Start Carrier Return X'04': Printer Interrupt Enable(1) or Disable (0) X'01': Reset Printer Interrupt LIO 1,1,0,addr Load Printer Output Character addr is address of low-order (highest numbered) byte of two-byte field, and high-order byte (that is, addr - 1) is loaded into output register to print. Printing is done one character at a time. SNS 1,0,1,addr Sense Status Bytes 0 and 1: Byte 0 (leftmost) is the character typed in in EBCDIC. Byte 1 is status: X'80': Request key interrupt pending X'40': End or Cancel key interrupt pending X'20': Cancel key pressed X'10': End Key Pressed X'08': Return or data key interrupt pending X'04': Return key pressed SNS 1,0,3,addr Sense Status Bytes 2 and 3: returns 0000 in this sim. 1442 Reader/Punch ----------------- SIO 5,0,0,XX Feed Card without reading/punching XX is stacker select for all functions: 0 = stacker 1 (normal), 1 = stacker 2. SIO 5,0,1,XX Read Card SIO 5,0,2,XX Punch and Feed SIO 5,0,3,XX Read Column Binary SIO 5,0,4,XX Punch with no feed TIO 5,0,0,addr Branch to addr if not ready or busy TIO 5,0,2,addr Branch to addr if busy TIO 5,0,5,addr (mod 15 only) branch if interrupt pending APL 5,0,0 Loop (or switch prog levels) if not ready/busy APL 5,0,2 Loop (or switch) if busy APL 5,0,5 Loop (or switch) if interrupt pending (mod 15 only) LIO 5,0,0,addr Load 2-byte field to Length Count Register LIO 5,0,4,addr Load 2-byte field to Data Address Register (DAR is incremented by a read/punch operation and must be reset every card) SNS 5,0,1,addr Sense CE indicators (0000 returned in this sim) SNS 5,0,2,addr Sense CE indicators (0000 returned in this sim) SNS 5,0,3,addr Sense Status Indicators: (only simulated bits shown) X'8000': Read Check X'4000': Last Card X'2000': Punch Check X'1000': Data Overrun X'0800': Not Ready 1403 Printer ------------ SIO 14,0,0,XX Line space XX lines (0-3 valid in XX) SIO 14,0,2,XX Print a line space (0-3 valid in XX) SIO 14,0,4,XX Skip Only (line number 1-112 in XX) SIO 14,0,6,XX Print and Skip (line number 0-112 in XX) TIO 14,0,0,addr Branch to addr if not ready TIO 14,0,2,addr Branch to addr if buffer busy TIO 14,0,3,addr Branch to addr if interrupt pending (mod 15 only) TIO 14,0,4,addr Branch if carriage busy TIO 14,0,6,addr Branch if printer busy APL 14,0,0 Loop (or switch prog levels) if not ready/check APL 14,0,2 Loop (or switch) if buffer busy APL 14,0,3 Loop (or switch) if interrupt pending (mod 15 only) APL 14,0,4 Loop (or switch) if carriage busy APL 14,0,6 Loop (or switch) if printer busy LIO 14,0,0,addr Load 1 byte to Forms Length Reg at addr-1 LIO 14,0,4,addr Load 2 bytes to Chain Image Address Register LIO 14,0,6,addr Load 2 bytes to Data Address Register SNS 14,0,0,addr Sense Character Count SNS 14,0,4,addr Sense LPIAR (Image Address Register) SNS 14,0,6,addr Sense LPDAR (data addres register) 5444 Disk Drives ---------------- Each drive has two disks (upper and lower), each disk has two surfaces (upper and lower), each surface has 24 256-byte sectors, sectors are number 0 thru 23 on upper surface, 32 thru 55 on lower. d = drive, 0 is R1/F1, 1 is R2/F2 s = surface, 0 = upper (removable), 1 = lower (fixed) The Control register points to the leftmost byte of a 4-byte control field in memory with these bytes: F - Flag byte (not supported in this sim) C - Cylinder Address (0-203) S - Sector Number (0-23, or 32-55) in top 6 bits N - Number of sectors minus 1 These have meaning for all operations except seek, seek uses the fields differently. SIO 1d,s,0,XX Seek, XX not used, control field is used: F - not used C - not used S - high bit is head to be used 0-upper 1-lower low bit is direction to move 0-back 1-forward N - number of cylinders to move SIO 1d,s,1,XX Read, values of XX are as follows: X'00': Read Data X'01': Read Identifier (updates control field, no data is read) X'02': Read Data Diagnostic X'03': Verify (does not read, but checks) SIO 1d,s,2,XX Write, values of XX are as follows: X'00': Write Data X'01': Write Identifier (24 sectors with byte at data address register) SIO 1d,s,3,XX Scan. All 256 bytes in memory at data address register are compared to disk sectors on current track, except those bytes of X'FF' are not compared. Values of XX are: X'00': Scan Equal X'01': Scan Low or Equal X'02': Scan High or Equal LIO 1d,0,4,addr Load Data Address Register LIO 1d,0,6,addr Load Disk Control Address Register TIO 1d,0,0,addr Branch if not ready/unit check TIO 1d,0,2,addr Branch if busy TIO 1d,0,4,addr Branch if Scan Found APL 1d,0,0 Loop if not ready/unit check APL 1d,0,2 Loop if busy APL 1d,0,4 Loop if scan found SNS 1d,0,2,addr Sense Status Bytes 0 and 1: (simulated bits only are shown, otehrs are 0): X'1000': equipment check X'0800': data check X'0400': No record found X'0100': Seek Check (past cyl 203) X'0080': Scan equal Hit X'0040': Cylinder 0 X'0020': End of Cylinder X'0010': Seek Busy SNS 1d,0,3,addr Sense bytes 2 and 3 (0000 in this sim) SNS 1d,0,4,addr Sense Data Address Register SNS 1d,0,6,addr Sense Control Address Register simh-3.8.1/S3/readme_s3.txt0000644000175000017500000000531307376341510013541 0ustar vlmvlm Welcome to the IBM System/3 Model 10 SIMH simulator. --------------------------------------------------- To compile under linux: cc s3*.c scp*.c sim_rev.c -o s3 This code can be compiled and run as a console application using Microsoft Visual C++. To IPL the provided SCP distribution disk: ./s3 sim> at r1 m10scp.dsk sim> at f1 f1f1f1.dsk sim> at lpt print.txt sim> d sr 5471 sim> boot r1 // DATE 06/14/01 // NOHALT // LOAD $MAINT,R1 // RUN // COPY FROM-R1,LIBRARY-ALL,NAME-DIR,TO-PRINT // END (A printout of the libraries and directories on the SCP DTR disk will be in the file print.txt) The text file "system3.txt" gives details on the simulators implementation of System/3 hardware. A write up on the use of the SCP and the OCL job control language is in the text file "userguide.txt". This includes examples of using the utility programs, and a tutorial guiding you thru a sysgen. A nearly complete listing of all possible SCP halts is in the document "haltguide.txt". IMPORTANT NOTES: 1) How to correct typing errors when using the System/3 console: If you make an error, press ESC, which will cancel the current line being typed and print a quote in position 1. Then you can use CTRL/R to retype characters up until the error, then type correctly. Or simply retype the line. BACKSPACE DOES NOT WORK with the SCP. 2) While the simulator allows disk images to be independently attached to any disk unit, on the real hardware R1 and F1 were on a single spindle, and R2 and F2 likewise. It is not possible using SCP to attach R1 without attaching a disk image to F1 also, because SCP will always look at F1 even when IPLed off R1. The OS distributed with the simulator is version 16 of the Model 10 SCP. This is sysgenned with support only for R1 and F1. If you do a sysgen to support R2 amd F2 also, you must have images attached to all 4 disks when you IPL, because SCP looks at all drives when it starts up, and you will get an "Unattached Unit" error if you fail to have one attached. 3) The 1442 card reader had in reality one card input hopper and two stackers. This means the same path is used for reading and punching cards. When punching cards, SCP does a read operation and inspects the card read for blanks, and if it is not blank, issues a YH halt. SCP will not punch data onto non-blank cards. This feature causes problems in the simulator, and as a result if you punch cards from SCP, YOU MUST not have any file attached to the CDR device. Leaving this device unattached presents an infinite supply of blank cards to SCP for punching. -- End of README_S3.txt -- simh-3.8.1/S3/haltguide.txt0000644000175000017500000007306407375463464013671 0ustar vlmvlm IBM System/3 Model 8/10 SCP ********** Halt Guide ********** This following list is my own reformatting and rewording of the official IBM Halt Guide for the Model 8/10 SCP. The halts are those displayed on the message display unit. The list is in alphabetical order for easy reference. When the system halts, the two 7-segment displays will display the halt as listed here, and the system console (or printer if the log device is the printer) will print the "SCP Message" below. To respond to the halt, deposit one of the valid response numbers (0 thru 3) into the SR, and then use the C command to continue. Unless otherwise stated, a response of 0 means to continue and accept the error, 1 means to retry the operation or re-read the statement in error, 2 means to cancel the job and retain added records (if any) and 3 means to end the job and discard any added records in files. This is a listing of those halts likely to be encountered using SCP on the simuator, it is not the complete list of all possible halts. Halt SCP Message Description ---- ----------- ----------- 00 Invalid response to another halt. Deposit a valid value (0 thru 3) in SR. 0A A 5448 Disk Unit not ready. 0C 5448 Disk Equipment Check 0E Permanent disk error during logging. 0F ID0FXX 23 Invalid cylinder number on disk operation. XX = Disk Drive. 0H ID0HXX 23 Invalid sector number on disk operation XX = Disk Drive. 0Y IK0Y0X 123 3741 Error. X: 1=not ready 2=wrong mode 3=parity error 5=record length error 0 ID0 XX 23 Disk Data Check XX = Disk Drive. 0- ID0-XX Invalid disk operation code: Start cancels job. XX = Disk Drive. 10 3 No input file allocate, user error. 11 0 23 Square root of a negative field 12 0 23 Divide Overflow 13 0 23 Divide by zero 14 0 23 Varible index zero of out of range 15 0 23 Sequenced table is out of sequence 16 0 23 (RPG) OBject tables expected. /* Read. 17 0 23 (RPG) Object table exceeds specified length 18 0 23 (RPG) Terminal errors in source program. 19 0 3 (RPG) Warning errors in source program. 0=continue. 1A 3 (RPG) Out of core memory 1C 23 Unidentified halt has been issued. Probable system error. 1E 0 3 (RPG) Demand file at end of file. 1F 23 (RPG) End of file or end of extent. If during RPG compilation, expand $SOURCE or $WORK. 1H 0 23 Duplicate keys found during build of indexed file. 0=skip this record and continue. 1J 0 23 Out of sequence keys during build of indexed file. 0=skip this record and continue. 1L 0 23 Key changed during record update. User error. 0=continue, do not update record. 1P 01 Forms in printer need positioning. 1U 123 No record found on direct or indexed file. 1Y 0 23 (RPG) Invalid numeric data to DSPLY statement. 1 0 3 Object program ready to punch tables. 20 1 3 Disk Sort: Invalid header and no // SOURCE 21 01 3 Disk Sort: Name on // SOURCE not found 22 0 2 Disk Sort: Warning errors found. 23 3 Disk Sort: Unrecoverable error. 25 3 Disk Sort: Terminal errors in sort statements. 27 0 Disk Sort: In debug mode, finished pass. 2C 0 3 Disk Sort: No Input Records selected. 2E 3 Disk Sort: Workfile too small. 2F 23 Disk Sort: Output file too small. 2L DT2LY2 3 Tape Record too large to process. DT2LY7 3 No FILE statement for tape file open. DT2LY9 3 No enough storage for tape operation DT2LTC 3 Invalid tape header length DT2LYF 123 Incorrect block length read from tape 2P Permanent tape error. 2U 12 Tape unit is write protected. 2Y 3 Invalid device specification in object. 2- 0 3 First statement was not a Tape Sort header. 30 EG30 3 Space not available on R1 or F1. UB30A1 0 3 Active files exist on output disk UB30AF 0 3 Active files exist on 5448 disk UB30H1 0 3 Wrong capacity or uninitialized pack UB30NS 3 No 5448 disk for $PCOPY UB30TP 0 3 Pack change required. UC30AF 3 Active or system files on target UC30BD 3 Volume label cannot be read UC30SP 3 Not enough space for work file UP30AF 3 Active or system files on target 31 UI31AF 0 3 Active or system files on target 0=proceed to next unit to initialize UI30WP 01 3 Wrong pack, name does not match. 32 UB32Bx 01 3 5444 pack is not a $PCOPY backup pack. UB32NP 01 3 Unit specified is not a $PCOPY pack. UC32BD 3 FROM pack is a TO pack from an interrupted COPYPACK run. UC32BP 3 Output pack is a $PCOPY output pack. Must be initialized or reset by a RESET statement to be used. UC32DS 3 Packs FROM and TO are different sizes. 33 UI33PU 0 3 Pack defective, cannot be initialized. 34 Ux34 1 3 Keyword in a utility control statement is invalid. 35 UC35xx 1 3 Error in $COPY or $KCOPY control statement. 36 UI36CE 0 3 CE track defective on unit F1. 37 UC37xx 0 3 Pack change required. xx: FP=mount on R1, IP=pack on COPYIN, OP=pack on COPYO. 38 UA38XX 0 3 Wrong pack mounted. UB38DA 01 3 Dates do not match. UB38DM 01 3 2nd 5444 pack not from same backup set as 1st. UB38IP 01 3 PACKIN keyword pack not same as pack mounted. UB38OP 01 3 PACKO keyword not same as pack mounted. 3A UC3Axx 3 Key out of sequence (DP), invalid high key (HK), out of space to copy file (XE), or disk I/O error. 3C UC3CCS 3 COPYFILE out of core. UC3CNF 3 Module not found, name is logged as R XXXXXX. 3E UC3EOX 0 3 COPYFILE output not as big as size of input. 3F UC3Fxx 1 3 Error in COPYFILE statement. 3J UC3Fxx 3 Invalid specification for Copy/Dump. 3P UC3Pxx 1 3 Error in COPYPACK, RESET, or LABELS statement. 3Y UI3YIS 0 3 Requested secondary init when primary required. 3 UI3 xx 1 3 Error in VOL statement. 40 DD40 3 File has been referenced as an output or add file and the file is already allocated. 4A DD4A 3 File had already been opened and is re-opened. 4C DD4C 3 Multivolumne file spec error. 4E DD4E 3 FILE indicates a multivolule file being built, but program compiled for single volume. 4F DD4F 3 Print buffers not aligned in program. 4H DD4H 0 3 Unordered load specified for ISAM. Ordered load must be specified on RPG file specs. 4J DD4J 3 All file specs have been checked and there were errors. 4L DD4L 3 Referenced file already allocated. 4P DD4P 3 Prgram/FILE statement mismatch. 4U DD4U 3 File referenced as update, already allocated. 4Y DD4Y 3 File has an incorrect device specification. 4 DD4 3 No FILE specification for referenced file. 4' DD4' 3 Attempting reference to a file in 2 levels, one or both using RETAIN-S. 50 UA50ID 2 Bad track which can't be reassigned. 51 UR51 12 Can't use Alternate Track program in procedure. 52 ML52 12 EOJ for Card List program. 53 IU53 1 3 Number of VOL statements does not agree with number of units on UIN statement. 54 EO54 3 End-of-file. 55 UF55xx 1 3 Error in SCRATCH or REMOVE statement. 56 UA56TS 0 3 ASSIGN track is over disk capacity. UA56XX 0 3 Unit specified is uninitialized. 57 UF57WP 01 3 File delete program. Wrong pack is mounted. 0: Mount correct pack and continue. 1: correct statement and retry. 5A UA5Axx 012 Alternate track assignment error. 5C MR5Cxx 1 3 Invalid reformat specs. 5F UF5Fxx 1 3 Error in DISPLAY statement. 5H UA5HEU 0123 Primary track is still defective. 5L UF5LAF 0 3 PRogram try to delete files that are being used by another program. UF5LNF 0 3 File not found. UF5LTM 23 Too many files specified. Max is 40. 5U UI5Uxx 1 3 Error in UIN statement. 5Y UR5Yxx 1 3 Error in REBUILD statment. 5 UA5 xx 1 3 Error in ALT statement. 5- 3 Tape Sort error occurred. 5' UF5'N1 0 3 Pack cannot be used. Not inited. UF5'NU 0 3 Pack was used as TO pack on a COPYPACK job that required early termination. Can only be used for another COPYPACK job. 60 LM60SY 0 3 Cannot remove or change library size on pack from which $MAINT was loaded. 61 LM61EP 0 3 Trying to copy a system to a library that is 1) not empty, 2) not allocated with large enough SWA, or 3) not allocated with enough space. LM61NS 0 3 System does not exist on FROM pack. 62 LM62CS 01 3 Check Sum error. LM62DR Can't determine if REMOVE is data or control. LM62EF FROM, TO, or AFTER statement does not exist or is out of sequence. LM62ND NO data records following INSERT or REPLACE. LM62SQ Records are out of sequence. LM62TP Incorrect type record. 63 LM63DE 0 3 Directory entry error. Name can't be found or attributes don't match, or attempt to remove dir entry with MODIFY. 64 LM64DS 0 3 Syntax error in ALLOCATE. 65 LM65UN 0 3 Pack not properly initialized. 66 LA66xx 3 Error with LOAD * function. 67 EL67NL 0 3 Library does not exist. 68 EL68DF 0 3 No room in library or directory. 69 XX69HE 3 Disk I/O Error while using library. 6A LM6Axx 1 3 $MAINT has detected a syntax error on a control statement. xx gives a hint of what might be wrong. AL: SOURCE or OBJECT missing or invalid AZ: SYSTEM missing or invalid D2: FROM, TO, or WORK is R2 or F2, not available DK: Duplicate keyword DS: Invalid DIRSIZE FL: Invalid or missing FILE keyword FM: Invalid or missing FROM keyword IK: Invalid keyword IN: Invalid INCR keyword IS: first 3 columns must be // blank IV: Invalid statement identifier LB: Invalid LIBRARY keyword LS: Invalid LIST keyword NK: No keywords NM: Invalid NAME keyword NU: Invalid NEWNAME keyword OM: Invalid OMIT keyword RL: Invalid RECL keyword RS: Invalid RESER keyword RT: Invalid RETAIN keyword SF: INvalid SEQFLD keyword SQ: Invalid FROM, TO, or AFTER in MODIFY mode XC: Invalid record. XD: Duplicate keyword XF: $$SYFG could not be found. XL: LIBRARY keyword missing XM: NAME keyword missing XN: NAME parameter is invalid XP: Library does not exist on this pack XS: Syntax error XT: Invalid library type XV: INvalid operation 6C LM6CSP 0 3 Not enough space on pack. LM6CSW 0 3 Space not available for work file. 6E LM6EOF 0 3 Overflow in seq field during RESER. LM6EDP 0 3 Entry with same name already exists in library. 6H EL6HDT 0 3 Trying to replace perm with temp entry. LM6HDP 0 3 NEWNAME is already in library. 6J LM6JCC 0 3 Control statements are missing. 6L UA6L 3 Log device is required for this program. 6Y LM6YNN 1 3 No NEWNAME when copying to same library. 6 LM6 BC 3 Invalid character in source record. LM6 CM 0 3 Invalid object deck. LM6 ND 0 3 No data between COPY and CEND. 6- LM6-BC 01 Entry containing a blank card being placed in library. 0: accept, 1: skip and read next card. 6' LM6'CE 1 3 // CEND expected but not found. 1: Retry, provide CEND. NOTE: For option 3, if a module was being replaced, it may have been deleted but new module not loaded. 70 CR70 3 Too many overrides for procedure. Max is 25. 71 CR71 0 3 OCL Syntax Error. 73 CR73 0 // PARTITION given in invalid location. 74 CR74 3 /& between LOAD and RUN or CALL and RUN. 75 CR75 23 Extraneous statement. 76 CR76 0 3 // Missing from OCL statement. 77 CR77 23 Invalid OCL statement identifier. 78 CR78 0 3 Unknown OCL keyword. 79 CR79 23 Continuation expected but not received. 7A CR7A 3 A second LOAD or CALL found before run, or a CALL in procedure overrides. 7C CR7C 0 3 // COMPILE found between jobs. 7E CR7E 0 3 // DATE found between jobs. 0: Ignore and continue. 7F CR7F 0 3 // FILE found between jobs. Must go between // LOAD or // CALL and // RUN statements. 7H CR7H 0 3 // SWITCH found between jobs. 7J CR7J 23 // READER found between LOAD or CALL and RUN. 7L CI7Lxx 23 Error when reading a tape file. 7P New print chain expected. Load it and press START. 7U CR7U 3 RUN statement not preceeded by LOAD or CALL. 7Y CI7Yxx 23 Error outputing a tape file. 7 CR7 3 Too many utility control statements, max is 25. 7- CR7- 0 // PARTITION was read but system does not support Dual Programming. 7' Error during tape processing. 80 CR80 0 // DATE card has not been entered. 81 CR81 23 Error in LOAD statement. 83 CR83 23 Error in LOAD * statement. 84 CR84 23 Error in CALL statement 85 CR85 23 Second SWITCH statement found. 86 CR86 23 Invalid paramter in switch statement. 88 CR88 1 3 Procedure not found. 89 CR89 01 // DATE has already been given. 0 - accept the new date as the date. 1 - leave the old date as the current date. 8A CR8A01 0 Invalid date specified. CR8A02 0 DATE parameter missing. 8C CR8C 23 Second DATE found. 8E CR8E01 23 Date specified incorrectly. 8F CR8Fxx 23 Invalid BSCA statement. 8H CR8H 3 More than 9 levels of procedures have been called. 8J CR8J 0 Invalid // READER parameter. 8L CR8L 0 Desired system input device being used by other program. 8P CR8P 0 Output device not defined. 8U CU8UIP 23 Invalid HIKEY in FILE statement: non-numeric. CR8UKL Parameter length mismatch. CR8ULO Key greater than 29. CR8UPL HIKEY-P greater than 15. CR8USQ HIKEY parameters not in sequence. 8Y CR8Y 0 Not logging can be done. Log turned off by other program level. 8- CR8- 0 3 Logging requested but cannot be done. 90 CR90 0 // PAUSE statement read. Check printer or console for instructions and continue. PAUSE was outside LOAD and RUN. 91 CR90 0 // PAUSE statement read. Check printer or console for instructions and continue. PAUSE was inside LOAD and RUN. 92 CR92 23 COMPILE already recieved for this job. 93 CR93 23 Error in COMPILE statement. 94 CR94 23 Error in COMPILE statement. 95 CR95 23 Error in COMPILE statement. 96 CR96 0 23 System error. An OCL error was found, but the system cannot resolve the error. 97 CR97 0 Error in LOG statement. 98 CR98 23 Error in LOG statement. 99 CR99 0 23 Error in LOG statement. 9A CR9A 23 Indicated action on last OCL statement read will be ignored due to previous errors detected. 9C CR9Cxx 123 Incorrect tape volume online. 9E CR9E 0 Logging device being used by other program level. 9F CR9F 0 23 Logging device in use by other program. 9H CR9H 23 Log device in use. 9J CR9J 0 Error in FORMS statement. 9L CR9L 0 23 Error in FORMS statement. 9P CR9P 23 Error in FORMS statement. 9U CR9U 0 3 Other program has gotten a // IMAGE or other program level is using the printer. 9Y CR9Y 0 23 Logging device not sysgenned or CCP has it. 9 CR9 0 23 Same as 9Y. 9- CR9- 0 3 Other program level received a // FORMS or other level using the printer. 9' CR9' 0 Same as 9Y. A0 CRa0xx 23 Syntax error in FILE statement. A1 CRA1xx 23 Keyword error in FILE statement. A2 CRA2xx 23 Parameter error on FILE statement. xx gives parameter: 01 NAME, 02 UNIT, 03 PACK, 04 LABEL, 05 RETAIN, 06 DATE, 07 RECORDS, 08 TRACKS, 09 LOCATION, AS ASCII, BL BLKL, CV CONVERT, DF DEFER, DN DENSITY, EN END, PT PARITY, RC RECL, RF RECFM, RL REEL, SP SPLIT, TN TRANSLATE. A3 CRA3xx 23 Missing Parameter on FILE statement, xx = NN: NAME, NP: PACK, NU: UNIT, OP: no parameters. A4 CRA4xx 23 Invalid parameter combination in FILE statement: AS: ASCII-YES and RECFM-D/DB on 7-track tape AV: ASCII-YES and RECFB-V/VB AY: RECFM-D/DB without ASCII-YES CT: CONVERT-ON and TRANSLATE DI: UNIT says tape but disk parameters given DN: DENSITY-800 not supported. FS: RECFM is fexed and block or rec len less than 18 IL: Incorrected RECL or BLKL for RECFM IP: SPLIT or LOCATION used with RECORDS / TRACKS. IR: LABEL, DATE or RETAIN wirh REEL-NL or REEL-NS NS: Not all units are 7-track PC: CONVERT-ON and PARITY-EVEN RC: CONVERT-ON not given with RECFM-V/VB for 7-track SD: DENSITY-1600 invalid for 7-track SL: LOCATION missing or invalid for SPLIT. SM: SPLIT invalid for multivolume files. ST: 7-track paras with 9-track unit SU: SPlit can't be used with 5444 TL: TRACKS/LOCATION invalid with unit TP: UNIT is disk but tape paras given TR: TRACKS and RECORDS both given A6 CRA6xx 23 Error in FILE statement for multivolumne files. A7 CRA7xx 23 Error in IMAGE statement. A8 CRA8xx 0 Error in IMAGE statements o disk. A9 CRA9xx 0 23 Same as A8. AA CRAAxx 23 Same as A8. AC CRAC 0 Invalid hex character in chain image. AE CRAE 0 23 Same as AC. AF CRAF 23 Same as AC. AH CRAH 0 Error in IMAGE statement. AJ CRAJ 0 23 Same as AH AL CRALxx 0 Error in PARTITION statement. AP CRMN 0 3 Either reocvery option has has been selected during a job, or OCL errors have occurred for this job. 0: Continue iwth next job, or no data cards in reader for this job, otherwise, 3 to cancel. AU CRAUxx 23 Error in PARTITION statement. A CRA 23 Total number of volumes for a FILE statement exceeds 40 (!). A- CRA-xx 0 23 Error in PARTITION statement. A' CRA' 3 No space remaining is System work area. Too many FILE statements are in this job. C1-C9 IFC1 123 1442 Check, various causes. CL UDCLxx 1 3 5445 Data Interchange Utility error E7 DKE7 0 3 Incorrect record length for attached 3741 E8 UTE8xx 1 3 Error in Tape Init VOL statement. E9 UTE9xy 0 3 Error during Tape Init Processing. F8 DDF8 3 RPG--LIne counter specs omitted and skip past page size for printer. F9 CIF9xy 23 Tape drive not available, x = drive #. FA CIFA 3 Program requesting Data Recorder, unsupported. FC CIFC 3 Program requesting CRT, unsupported. FE DDFE 0 3 Program requesting line line on printer that exceeds sysgen value. FF RPQ routine error. Press start to continue. FH CIFH 123 BSCA line not supported. FJ CIFJ01 123 1442 not supported but requested CIFJ02 123 3741 not supported but requested FL CIFL 123 Printer/keyboard not supported or unavailable. FP CIFP 123 Printer not supported or allocated to other level FU CIFU 123 MFCU not supported or allocated to other level FY CIFY 23 Device is not supported or in use. F CIF 23 Conflict with a resource being used by other level. H0-H9 0 23 RPG Programmed halt indicator is on. HA CIHA 3 Out of space on $SOURCE during compile. HC CIHC 3 Program given on LOAD statement not found. HE Hardware error. Simulator has messed up. HF CIHF 0 3 // COMPILE read but not required. HJ CIHJ01 1 3 Program not found on removable unit, 1: mount new unit and retry. CIHJ02 3 Program not found, but removable unit in use. HL CIHL 3 Inquiry request made but program is wrong type. HP CIHP 3 Insufficient main storage for program. LMHP 3 $MAINT function out of storage. NOTE: After the cancel, IPL from the system pack or the pack will be unusable. HU CIHUxx 3 Source program not found on disk. IF a 1 option is present, you can mount a new removeable pack. HY CCHYNN 0 A checkpoint is received and accepted. H CCH NN 0 23 A restart has been requested. H' CIH' 3 An uninitialized pack has been referenced. J0-J9 123 Record with specified match field out of sequence. This is an RPG error, the 2nd digit indicates which RPG file statement the error applies to in the source program. 0=greater than statement 9, otherwise indicates the file statement number. JA CIJA 3 Trying to laod a program that requires or allows inquiry while another inquiry program is running in the other level. JC CIJCxx 3 Program cannot be run for this reason (xx): 01: Must be dedicated and other level active 02: Program in other level must be dedicated 03: $$RSTR cannot run in level 2 04: CHeckpointed program not allowed in level 2 05: Program can't run while checkpoint active JE CIJE 0 3 Level 1 partition too small. JF CIJF 3 Attempt to start inquiry program but keyboard in use. JH CIJF 3 Attempt to start program which allows interrupts in level 2. JJ CIJJ 3 No object library on pack requested for load. JL CIJL 3 Not enough storage for program. DPF only. JP System input device in use by other level. JU 0123 Cancel request made from interrupt key. 0: ignore 1: continue, request ignored JY CIJYRD 0 2 Inquiry request made and accepted. J- 3 Attempt to run a CCP program, but CCP not running. J' 01 3 Inquiry request is completed, interrupted program can now resume. L0-L9 123 RPG. Unidentified record, 2nd digit gives file statement number in source program 1-9, 0 means greater than 9. Can also occur if record is out of sequence. LA CILA 23 Too little storage for number of files in program. LC CILC 23 Too little storage for requested allocation. LE CILE 23 No FILE or an incorrect FILE for a file requested by current program. LH CILH 23 No space given for an output file on FILE statement. LJ CILJ 23 Attempt to output to existing permanent file. LL CILL 0 23 Attempt to output over an existing temporary file. LP CILP 23 File already exists. LU CILU 123 Pack name requested but wrong pack mounted. 1: retry after mounting correct pack. LY CILYxx 23 Attempt to allocate space that isn't available. xx=02 means space not available in split cylinder area. L LML CP 01 3 $MAINT detected attempt to modify a program on a pack with an active checkpoint. L- CIL- 3 Attempt to add a split cylinder to a split cyl file while other level is fiddling with a split cylinder file. L' CIl' 23 Trying to allocate a split cylinder file before allocating the first split cylinder file in a group. P1-P8 Printer hardware errors, should not occur in sim. PC IPPC 0 23 Unprintable character. PH CIPH 23 LOCATION plus TRACKS goes past end of pack. PJ CIPJxx 1 A Pack is to be remounted, pack name printed before half code, xx= unit. PU CIPU 3 Duplicate file names in the FILE statements. PY CIPY01 3 ISAM file requires at least 2 tracks. CIPY02 3 ISAM file can't be split cylinder. P' CIP'xx 23 Too many scratch or work files. U0-U9 0123 RPG. Unidentified record in file, 2nd digit of halt is file statement in RPG source, 0= greater than 9. UA CIUA 3 Attempt to create a multivolume file in invalid. UC CIUC 3 The printed actived file cannot be found in the list of scratch files. UE CIUExx 1 3 PACK parameter does not match pack name on unit. xx = Unit referenced. 1 = Mount another pack and continue. UF CIUF 3 Disk file referenced by name and date not found. UH CIUH 3 Attempt to create multivolume file failed, because name alreayd exists. UJ CIUJ 3 A LOCATION was specified for an existing disk file and the file exists but not at that location. UL CIUL 3 File on // FILE statement not found, and no size in TRACKS or RECORDS was given. UP CIUP 3 Permanent file referenced with RETAIN-S UU CIUU 3 Disk Pack not available. UY CIUY 3 File is a System/3 BASIC file which must be unique. U CIU 3 Existing file: TRACKS/RECORDS or LOCATION mismatch. U- General CCP halt. Press start to see subhalt. Refer to CCP manual for more info. U' CIU' 23 VTOC is full, or more than 2 multivolume files per pack, or more than 2 ISAM files using HIKEY parameter. YH CRYH 0 3 Cards are being punched, but card read from reader was not blank. This means you are trying to punch with a file attached to the CDR device. Unattach the file and take the zero option. 0 (blank 0) FILE WRITE switch in off position. 1 (blank 1) Permanent DIsk I/O Error 2 RC 211 3 COBOL. Out of room on $WORK. RC 212 3 Out of room on $SOURCE. RC 213 3 Out of room on $WORKX. RC 214 3 Subprogram name table greater than 20. RC 219 0 3 C or E level diagnostics during compile. RC 2A1 23 Subscript invalid RC 2A2 23 Negative exponent or 0 degrees in program RC 2F1 23 MFCU File not open or opened improperly RC 2F2 23 1442 File not open or opened improperly RC 2F3 23 1403/5203 File not open or opened improperly RC 2F4 23 5444 Disk File not open or opened improperly RC 2F5 23 5444 File not open or opened improperly RC 2F7 23 5444 File not open or opened improperly RC 2F8 23 Tape File not open or opened improperly RC 2H1 23 OPEN attempted after CLOSE WITH LOCK RC 2H2 23 Error during ACCEPT RC 2H3 23 $$STOP not found RC 2H4 23 CHeckpoint could not be taken. RC 2H5 23 $$STIC not found for ACCEPT RC 2H6 23 Parameter mismatch CALL and USING RC 2H7 23 ACCEPT after /& read RC 2H8 23 OPEN for a file already OPEN RC 2 0 3 Too little core for compile RC 2 1 3 PROCEDURE or DATA division not found. RC 2 3 3 Program has more than 65535 statements (!) RC 2 4 3 Source name on COMPILE statement not found 3 R 3XX 0 3 COBOL Stop literal. XX is user-specified. 0 continues program 3 cancels. 4 VF 4NF 3 Program not found. Program library and not printed before halt message. 6 RF 6XX 0 23 FORTRAN stop statement. 7 RF 701 23 Source member on COMPILE not found RF 702 23 Object program too large for core. 8 CS 8 1 3 System input device allocated to other level. 9 CS 9 1 3 System input device has an error. This usually means the card hopper is empty (i.e. EOF on the file attached to the reader but SCP wants more input). A DC A 123 Number of characters entered from keyboard incorrect. C DD C 0 23 Unprintable character for printer/keyboard. E DC E 123 Hardware error, PKB F DC F 0 23 End of forms, PKB L DD L 0 3 Records with duplicate keys have been loaded into ISAM file. Each dup key is logged followed by blank P halt. 0: continue. Index will contain duplicate keys. 3: cancel, file is not usable, reload it. P DD P 0 3 Duplicate key encountered. The key is printed on the log. 0: continue, halt will recur for any other duplicates, then blank L appears. U DD U 3 Disk I/O error while sorting ISAM index. Y DD Y 3 System error during file termination. -0 DD-0XX 3 ISAM multivolume file being used and high key not found for current columme, or does not agree with HIKEY spec. XX=unit number. -1 DD-1XX 123 Halt -P occurred and option 0 taken. But, the pack mounted is not a part of volume set. -2 DD-2XX 123 Multivolume load sequence error. -3 DD-3XX 123 Multivolume load sequence error. -4 DD-4XX 0123 Warning that one or more volumes are about to be bypassed. -5 DD-5XX 123 Multivolume file not found. 1: mount correct pack. -6 DD-6XX 0 23 Warning. ENd of volume and HIKEY not found. -7 DD-7XX 1 3 -A halt and option 1 taken. But the pack referenced does not match pack name. -8 DD-8XX 3 Multivolume file referenced but file isn't multivolume. -9 DD-9XX 3 Add to a multivolumen file, but last pack not mounted. -A DD-AXX 1 3 Add to existing multivolume filebut no room. -C DD-CXX 3 Multivolume file error. Probably out of sequence volume mounts. -E DD-EXX 123 Next volume cannot be processed, because the location is not available or space is not available or there are scratch files on the pack. -F DD-Fxx 123 Finished a volume, next cannot be processed, mount the correct pack or cancel. -H DD-Hxx 3 HIKEY length does not match file. -J DD-Jxx 01 3 First volume referenced is not volumme 1. 0: continue with this volume, 1: mount another pack. -L DD-Lxx 3 Output to multivolume, but file isn't multivolume or referenced volume isn't first one of set. -P DD-PXX 0123 Mount next volume. XX=unit number. 0: continue bypassing volumes, 1: mount next volume. -U DD-UXX 1 3 Halt -J just occurred and 0 or 1 taken. But the pack name is incorrect or the file isn't found. - DD- 123 Multivolume key error. Key too low or high for volume. -' DD-' 123 Sequential add to multivolume file, but HIKEY record missing on previous volume. '0 GM'0DE 3 SYSGEN. I/O Error on reader. GM'0EX 3 SYSGEN. End of extent on MACOUT or $SOURCE. GM'0IC 1 3 SYSGEN. Option dependent on a preceding option, the preceding one was omitted or invalid. GM'0ID 1 3 SYSGEN invalid delimiter. GM'0IK 1 3 SYSGEN invalid keyword. GM'0IR 1 3 SYSGEN invalid option. GM'0IS 1 3 SYSGEN sequence error. GM'0NF 1 3 SYSGEN entry in cols 8-12 not found. GM'0NS 3 SYSGEN Requested source program not found. GM'0EM 1 3 SYSGEN. END statement not found. GM'0NP 3 SYSGEN. Module $SGXP2, $SGXP3, $SGXP4, $SGXP5, or $SGXP6 missing for sysgen, or $MPXP2, $MPXP3 or $MPXP4 missing for macro processor. '1 GG'1 3 System Generation Errors. '2 0 3 Error during macro processor run. '3 3 Invalid 5445 disk label record. '4 GG'4EX 3 Out of room on Sysgen, or disk error. ----------------------- End of haltguide.txt --------------------------- simh-3.8.1/S3/s3_lp.c0000644000175000017500000003253010303752644012321 0ustar vlmvlm/* s3_lp.c: IBM 1403 line printer simulator Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. lpt 1403 line printer 25-Apr-03 RMS Revised for extended file support 08-Oct-02 RMS Added impossible function catcher */ #include "s3_defs.h" extern uint8 M[]; extern char bcd_to_ascii[64]; extern int32 iochk, ind[64]; int32 cct[CCT_LNT] = { 03 }; int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *cptr); t_stat write_line (int32 ilnt, int32 mod); t_stat space (int32 lines, int32 lflag); t_stat carriage_control (int32 action, int32 mod); extern unsigned char ebcdic_to_ascii[256]; #define UNIT_V_PCHAIN (UNIT_V_UF + 0) #define UNIT_M_PCHAIN 03 #define M_UCS 00 /* Universal */ #define M_PCF 00 /* full */ #define M_PCA 01 /* business */ #define M_PCH 02 /* Fortran */ #define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN) #define UCS (M_UCS << UNIT_V_PCHAIN) #define PCF (M_PCF << UNIT_V_PCHAIN) #define PCA (M_PCA << UNIT_V_PCHAIN) #define PCH (M_PCH << UNIT_V_PCHAIN) #define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN) #define CHP(ch,val) ((val) & (1 << (ch))) int32 LPDAR; /* Data Address */ int32 LPFLR; /* Forms Length */ int32 LPIAR; /* Image address */ int32 linectr; /* current line # */ int32 lpterror = 0; int32 CC9 = 0; int32 CC12 = 0; /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; REG lpt_reg[] = { { FLDATA (ERR, lpterror, 0) }, { HRDATA (LPDAR, LPDAR, 16) }, { HRDATA (LPFLR, LPFLR, 8) }, { HRDATA (LPIAR, LPIAR, 16) }, { DRDATA (LINECT, linectr, 8) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, { DRDATA (LINES, lines, 8), PV_LEFT }, { DRDATA (CCTP, cctptr, 8), PV_LEFT }, { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { UNIT_PCHAIN, UCS, "UCS", "UCS", NULL }, { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL }, { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 7, NULL, NULL, &lpt_reset, NULL, NULL, NULL }; /* -------------------------------------------------------------------- */ /* Printer: master routine */ int32 lpt (int32 op, int32 m, int32 n, int32 data) { int32 iodata; switch (op) { case 0: /* SIO 1403 */ iodata = 0; printf("\0"); switch (n) { case 0x00: /* Spacing only */ if (data > 0 && data < 4) iodata = carriage_control(2, data); break; case 0x02: /* Print & space */ iodata = write_line(0, 0); if (data > 3) data = 0; if (iodata == SCPE_OK) iodata = carriage_control(2, data); break; case 0x04: /* Skip only */ iodata = carriage_control(4, data); break; case 0x06: /* Print and skip */ iodata = write_line(0, 0); if (iodata == SCPE_OK) iodata = carriage_control(4, data); break; default: return STOP_INVDEV; } return iodata; case 1: /* LIO 1403 */ switch (n) { case 0x00: /* LPFLR */ LPFLR = (data >> 8) & 0xff; break; case 0x04: LPIAR = data & 0xffff; break; case 0x06: LPDAR = data & 0xffff; break; default: return STOP_INVDEV; } return SCPE_OK; case 2: /* TIO 1403 */ iodata = 0; switch (n) { case 0x00: /* Not ready/check */ if (lpterror) iodata = 1; if ((lpt_unit.flags & UNIT_ATT) == 0) iodata = 1; break; case 0x02: /* Buffer Busy */ iodata = 0; break; case 0x04: /* Carriage Busy */ iodata = 0; break; case 0x06: /* Printer busy */ iodata = 0; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); case 3: /* SNS 1403 */ switch (n) { case 0x00: /* Line count */ iodata = (linectr << 8); break; case 0x02: /* Timing data */ iodata = 0; break; case 0x03: /* Check data */ iodata = 0; break; case 0x04: /* LPIAR */ iodata = LPIAR; break; case 0x06: /* LPDAR */ iodata = LPDAR; break; default: return (STOP_INVDEV << 16); } return ((SCPE_OK << 16) | iodata); case 4: /* APL 1403 */ iodata = 0; return ((SCPE_OK << 16) | iodata); default: break; } printf (">>LPT non-existent function %d\n", op); return SCPE_OK; } /* Print routine Modifiers have been checked by the caller S = suppress automatic newline */ t_stat write_line (int32 ilnt, int32 mod) { int32 i, t, lc; static char lbuf[LPT_WIDTH + 1]; /* + null */ if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; lpterror = 0; lc = LPDAR; /* clear error */ for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ t = M[lc]; lbuf[i] = ebcdic_to_ascii[t & 0xff]; M[lc] = 0x40; /* HJS MOD */ lc++; } for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; fputs (lbuf, lpt_unit.fileref); /* write line */ if (lines) space (lines, lflag); /* cc action? do it */ else if (mod == 0) space (1, FALSE); /* default? 1 line */ else { fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ } lines = lflag = 0; /* clear cc action */ if (ferror (lpt_unit.fileref)) { /* error? */ perror ("Line printer I/O error"); clearerr (lpt_unit.fileref); lpterror = 1; } return SCPE_OK; } /* Carriage control routine Parameters: action = 00, skip to channel now = 01, space lines after = 02, space lines now = 03, skip to channel after = 04, skip to line number mod = number of lines or channel number or line number */ t_stat carriage_control (int32 action, int32 mod) { int32 i; if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; switch (action) { case 0: /* to channel now */ if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ if (CHP (mod, cct[(cctptr + i) % cctlnt])) return space (i, TRUE); } return STOP_INVDEV; /* runaway channel */ case 1: /* space after */ if (mod <= 3) { lines = mod; /* save # lines */ lflag = FALSE; /* flag spacing */ CC9 = CC12 = 0; } return SCPE_OK; case 2: /* space now */ if (mod <= 3) return space (mod, FALSE); return SCPE_OK; case 3: /* to channel after */ if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ CC9 = CC12 = 0; for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ if (CHP (mod, cct[(cctptr + i) % cctlnt])) { lines = i; /* save # lines */ lflag = TRUE; /* flag skipping */ return SCPE_OK; } } return STOP_INVDEV; case 4: /* To line # */ if (mod < 2) { fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ linectr = 1; } else { if (mod <= linectr) { fputs ("\n\f", lpt_unit.fileref); linectr = 1; } while (1) { if (linectr == mod) break; space(1, 0); } } return SCPE_OK; } return SCPE_OK; } /* Space routine - space or skip n lines Inputs: count = number of lines to space or skip sflag = skip (TRUE) or space (FALSE) */ t_stat space (int32 count, int32 sflag) { int32 i; if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ if (sflag && CHP (0, cct[cctptr])) { /* skip, top of form? */ fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ linectr = 1; } else { for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); } lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ CC9 = CHP (9, cct[cctptr]) != 0; /* set indicators */ CC12 = CHP (12, cct[cctptr]) != 0; linectr += count; if (linectr > LPFLR) linectr -= LPFLR; return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { cctptr = 0; /* clear cct ptr */ lines = linectr = lflag = 0; /* no cc action */ lpterror = 0; return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { cctptr = 0; /* clear cct ptr */ lines = 0; /* no cc action */ lpterror = 0; linectr = 0; return attach_unit (uptr, cptr); } simh-3.8.1/S3/s3_defs.h0000644000175000017500000001206410302350736012627 0ustar vlmvlm/* s3_defs.h: IBM System/3 simulator definitions Copyright (c) 2001-2005, Charles E. Owen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Charles E. Owen shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Charles E. Owen. */ #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_INVOP 4 /* program check - invalid op */ #define STOP_INVQ 5 /* Prog check - invalid Q */ #define STOP_INVADDR 6 /* Prog check - invalid addr */ #define STOP_INVDEV 7 /* Prog check - invalid dev cmd */ #define STOP_NOCD 8 /* ATTN card reader */ #define RESET_INTERRUPT 77 /* special return from SIO */ /* Memory */ #define MAXMEMSIZE 65536 /* max memory size */ #define AMASK (MAXMEMSIZE - 1) /* logical addr mask */ #define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define MAX_DECIMAL_DIGITS 31 /* max size of a decimal number */ #define CDR_WIDTH 80 /* Max card size */ #define CDP_WIDTH 80 /* Punch width */ #define LPT_WIDTH 132 #define CCT_LNT 132 #define DSK_SECTSIZE 256 /* Sector length */ #define DSK_CYLSIZE 256*48 /* Cylinder length */ /* I/O structure The I/O structure is tied together by dev_table, indexed by the device number. Each entry in dev_table consists of level Interrupt level for device (0-7) priority Priority for device (1-8) routine IOT action routine */ struct ndev { int32 level; /* interrupt level */ int32 pri; /* Device priority */ int32 (*routine)(); /* dispatch routine */ }; /* Structure to define operation codes */ struct opdef { char op[6]; /* Mnemonic for op */ int32 opmask; /* Bits set on in opcode */ int32 q; /* Qbyte */ int32 form; /* Forms are: 0 - 1-byte hex operand 1 - 1-byte register addr, A-Addr 2 - A-addr,B-addr,Qbyte 3 - A-addr,Qbyte 4 - da,m,n 5 - da,m,n,cc 6 - da,m,n,A-addr 7 - 1-address implict Q 8 - 2-address implict Q */ int32 group; /* Group Code: 0 - Command Format (0xFx) 1 - 1-address A (0xx) 2 - 2-address (0x<0,1,2,4,5,6,8,9,A>x) 3 - 1-address B (0x<3,7,B>x) */ }; simh-3.8.1/HP2100/0000755000175000017500000000000011113202302011434 5ustar vlmvlmsimh-3.8.1/HP2100/hp2100_dq.c0000644000175000017500000012536411107411526013226 0ustar vlmvlm/* hp2100_dq.c: HP 2100 12565A disk simulator Copyright (c) 1993-2006, Bill McDermith Copyright (c) 2004-2008 J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of 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. DQ 12565A 2883 disk system 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size 26-Jun-08 JDB Rewrote device I/O to model backplane signals 28-Dec-06 JDB Added ioCRS state to I/O decoders 01-Mar-05 JDB Added SET UNLOAD/LOAD 07-Oct-04 JDB Fixed enable/disable from either device Shortened xtime from 5 to 3 (drive avg 156KW/second) Fixed not ready/any error status Fixed RAR model 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Fixed SR setting in IBL Revised IBL loader Implemented DMA SRQ (follows FLG) 25-Apr-03 RMS Fixed bug in status check 10-Nov-02 RMS Added boot command, rebuilt like 12559/13210 09-Jan-02 WOM Copied dp driver and mods for 2883 Reference: - 12565A Disc Interface Kit Operating and Service Manual (12565-90003, Aug-1973) Differences between 12559/13210 and 12565 controllers - 12565 stops transfers on address miscompares; 12559/13210 only stops writes - 12565 does not set error on positioner busy - 12565 does not set positioner busy if already on cylinder - 12565 does not need eoc logic, it will hit an invalid head number The controller's "Record Address Register" (RAR) contains the CHS address of the last Position or Load Address command executed. The RAR is shared among all drives on the controller. In addition, each drive has an internal position register that contains the last cylinder and head position transferred to the drive during Position command execution (sector operations always start with the RAR sector position). In a real drive, the address field of the sector under the head is read and compared to the RAR. When they match, the target sector is under the head and is ready for reading or writing. If a match doesn't occur, an Address Error is indicated. In the simulator, the address field is obtained from the drive's current position register during a read, i.e., the "on-disc" address field is assumed to match the current position. The following implemented behaviors have been inferred from secondary sources (diagnostics, operating system drivers, etc.), due to absent or contradictory authoritative information; future correction may be needed: 1. Read Address command starts at the sector number in the RAR. */ #include "hp2100_defs.h" #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) #define FNC u3 /* saved function */ #define DRV u4 /* drive number (DC) */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ #define DQ_N_NUMWD 7 #define DQ_NUMWD (1 << DQ_N_NUMWD) /* words/sector */ #define DQ_NUMSC 23 /* sectors/track */ #define DQ_NUMSF 20 /* tracks/cylinder */ #define DQ_NUMCY 203 /* cylinders/disk */ #define DQ_SIZE (DQ_NUMSF * DQ_NUMCY * DQ_NUMSC * DQ_NUMWD) #define DQ_NUMDRV 2 /* # drives */ /* Command word */ #define CW_V_FNC 12 /* function */ #define CW_M_FNC 017 #define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC) /* 000 /* unused */ #define FNC_STA 001 /* status check */ #define FNC_RCL 002 /* recalibrate */ #define FNC_SEEK 003 /* seek */ #define FNC_RD 004 /* read */ #define FNC_WD 005 /* write */ #define FNC_RA 006 /* read address */ #define FNC_WA 007 /* write address */ #define FNC_CHK 010 /* check */ #define FNC_LA 013 /* load address */ #define FNC_AS 014 /* address skip */ #define FNC_SEEK1 020 /* fake - seek1 */ #define FNC_SEEK2 021 /* fake - seek2 */ #define FNC_SEEK3 022 /* fake - seek3 */ #define FNC_CHK1 023 /* fake - check1 */ #define FNC_LA1 024 /* fake - ldaddr1 */ #define CW_V_DRV 0 /* drive */ #define CW_M_DRV 01 #define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV) /* Disk address words */ #define DA_V_CYL 0 /* cylinder */ #define DA_M_CYL 0377 #define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) #define DA_V_HD 8 /* head */ #define DA_M_HD 037 #define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD) #define DA_V_SC 0 /* sector */ #define DA_M_SC 037 #define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) #define DA_CKMASK 0777 /* check mask */ /* Status in dqc_sta[drv] - (d) = dynamic */ #define STA_DID 0000200 /* drive ID (d) */ #define STA_NRDY 0000100 /* not ready (d) */ #define STA_EOC 0000040 /* end of cylinder */ #define STA_AER 0000020 /* addr error */ #define STA_FLG 0000010 /* flagged */ #define STA_BSY 0000004 /* seeking */ #define STA_DTE 0000002 /* data error */ #define STA_ERR 0000001 /* any error */ #define STA_ANYERR (STA_NRDY | STA_EOC | STA_AER | STA_FLG | STA_DTE) FLIP_FLOP dqc_command = CLEAR; /* cch command flip-flop */ FLIP_FLOP dqc_control = CLEAR; /* cch control flip-flop */ FLIP_FLOP dqc_flag = CLEAR; /* cch flag flip-flop */ FLIP_FLOP dqc_flagbuf = CLEAR; /* cch flag buffer flip-flop */ int32 dqc_busy = 0; /* cch xfer */ int32 dqc_cnt = 0; /* check count */ int32 dqc_stime = 100; /* seek time */ int32 dqc_ctime = 100; /* command time */ int32 dqc_xtime = 3; /* xfer time */ int32 dqc_dtime = 2; /* dch time */ FLIP_FLOP dqd_command = CLEAR; /* dch command flip-flop */ FLIP_FLOP dqd_control = CLEAR; /* dch control flip-flop */ FLIP_FLOP dqd_flag = CLEAR; /* dch flag flip-flop */ FLIP_FLOP dqd_flagbuf = CLEAR; /* dch flag buffer flip-flop */ int32 dqd_obuf = 0, dqd_ibuf = 0; /* dch buffers */ int32 dqc_obuf = 0; /* cch buffers */ int32 dqd_xfer = 0; /* xfer in prog */ int32 dqd_wval = 0; /* write data valid */ int32 dq_ptr = 0; /* buffer ptr */ uint8 dqc_rarc = 0; /* RAR cylinder */ uint8 dqc_rarh = 0; /* RAR head */ uint8 dqc_rars = 0; /* RAR sector */ uint8 dqc_ucyl[DQ_NUMDRV] = { 0 }; /* unit cylinder */ uint8 dqc_uhed[DQ_NUMDRV] = { 0 }; /* unit head */ uint16 dqc_sta[DQ_NUMDRV] = { 0 }; /* unit status */ uint16 dqxb[DQ_NUMWD]; /* sector buffer */ DEVICE dqd_dev, dqc_dev; uint32 dqdio (uint32 select_code, IOSIG signal, uint32 data); uint32 dqcio (uint32 select_code, IOSIG signal, uint32 data); t_stat dqc_svc (UNIT *uptr); t_stat dqd_svc (UNIT *uptr); t_stat dqc_reset (DEVICE *dptr); t_stat dqc_attach (UNIT *uptr, char *cptr); t_stat dqc_detach (UNIT* uptr); t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat dqc_boot (int32 unitno, DEVICE *dptr); void dq_god (int32 fnc, int32 drv, int32 time); void dq_goc (int32 fnc, int32 drv, int32 time); /* DQD data structures dqd_dev DQD device descriptor dqd_unit DQD unit list dqd_reg DQD register list */ DIB dq_dib[] = { { DQD, &dqdio }, { DQC, &dqcio } }; #define dqd_dib dq_dib[0] #define dqc_dib dq_dib[1] UNIT dqd_unit = { UDATA (&dqd_svc, 0, 0) }; REG dqd_reg[] = { { ORDATA (IBUF, dqd_ibuf, 16) }, { ORDATA (OBUF, dqd_obuf, 16) }, { BRDATA (DBUF, dqxb, 8, 16, DQ_NUMWD) }, { DRDATA (BPTR, dq_ptr, DQ_N_NUMWD) }, { FLDATA (CMD, dqd_command, 0) }, { FLDATA (CTL, dqd_control, 0) }, { FLDATA (FLG, dqd_flag, 0) }, { FLDATA (FBF, dqd_flagbuf, 0) }, { FLDATA (XFER, dqd_xfer, 0) }, { FLDATA (WVAL, dqd_wval, 0) }, { ORDATA (DEVNO, dqd_dib.devno, 6), REG_HRO }, { NULL } }; MTAB dqd_mod[] = { { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dqd_dev }, { 0 } }; DEVICE dqd_dev = { "DQD", &dqd_unit, dqd_reg, dqd_mod, 1, 10, DQ_N_NUMWD, 1, 8, 16, NULL, NULL, &dqc_reset, NULL, NULL, NULL, &dqd_dib, DEV_DISABLE }; /* DQC data structures dqc_dev DQC device descriptor dqc_unit DQC unit list dqc_reg DQC register list dqc_mod DQC modifier list */ UNIT dqc_unit[] = { { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) }, { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) } }; REG dqc_reg[] = { { ORDATA (OBUF, dqc_obuf, 16) }, { ORDATA (BUSY, dqc_busy, 2), REG_RO }, { ORDATA (CNT, dqc_cnt, 9) }, { FLDATA (CMD, dqc_command, 0) }, { FLDATA (CTL, dqc_control, 0) }, { FLDATA (FLG, dqc_flag, 0) }, { FLDATA (FBF, dqc_flagbuf, 0) }, { DRDATA (RARC, dqc_rarc, 8), PV_RZRO | REG_FIT }, { DRDATA (RARH, dqc_rarh, 5), PV_RZRO | REG_FIT }, { DRDATA (RARS, dqc_rars, 5), PV_RZRO | REG_FIT }, { BRDATA (CYL, dqc_ucyl, 10, 8, DQ_NUMDRV), PV_RZRO }, { BRDATA (HED, dqc_uhed, 10, 5, DQ_NUMDRV), PV_RZRO }, { BRDATA (STA, dqc_sta, 8, 16, DQ_NUMDRV) }, { DRDATA (CTIME, dqc_ctime, 24), PV_LEFT }, { DRDATA (DTIME, dqc_dtime, 24), PV_LEFT }, { DRDATA (STIME, dqc_stime, 24), PV_LEFT }, { DRDATA (XTIME, dqc_xtime, 24), REG_NZ + PV_LEFT }, { URDATA (UFNC, dqc_unit[0].FNC, 8, 8, 0, DQ_NUMDRV, REG_HRO) }, { ORDATA (DEVNO, dqc_dib.devno, 6), REG_HRO }, { NULL } }; MTAB dqc_mod[] = { { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dqc_load_unload }, { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dqc_load_unload }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dqd_dev }, { 0 } }; DEVICE dqc_dev = { "DQC", dqc_unit, dqc_reg, dqc_mod, DQ_NUMDRV, 8, 24, 1, 8, 16, NULL, NULL, &dqc_reset, &dqc_boot, &dqc_attach, &dqc_detach, &dqc_dib, DEV_DISABLE }; /* Data channel I/O signal handler */ uint32 dqdio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ dqd_flag = dqd_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ dqd_flag = dqd_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (dqd); break; case ioSFS: /* skip if flag is set */ setstdSKF (dqd); break; case ioIOI: /* I/O data input */ data = dqd_ibuf; break; case ioIOO: /* I/O data output */ dqd_obuf = data; if (!dqc_busy || dqd_xfer) dqd_wval = 1; /* if !overrun, valid */ break; case ioPOPIO: /* power-on preset to I/O */ dqd_flag = dqd_flagbuf = SET; /* set flag and flag buffer */ dqd_obuf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ dqd_command = CLEAR; /* clear command */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ dqd_control = CLEAR; /* clear control */ dqd_xfer = 0; /* clr xfer */ break; case ioSTC: /* set control flip-flop */ dqd_command = SET; /* set ctl, cmd */ dqd_control = SET; if (dqc_busy && !dqd_xfer) /* overrun? */ dqc_sta[dqc_busy - 1] |= STA_DTE; break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, dqd); /* set standard PRL signal */ setstdIRQ (select_code, dqd); /* set standard IRQ signal */ setstdSRQ (select_code, dqd); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ dqd_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dqdio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dqdio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command channel I/O signal handler. Implementation notes: 1. The input buffer register is not connected to the disc controller. Pullups on the card and an inversion result in reading zeros when IOI is signalled. */ uint32 dqcio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 fnc, drv; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ dqc_flag = dqc_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ dqc_flag = dqc_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (dqc); break; case ioSFS: /* skip if flag is set */ setstdSKF (dqc); break; case ioIOI: /* I/O data input */ data = 0; /* no data */ break; case ioIOO: /* I/O data output */ dqc_obuf = data; break; case ioPOPIO: /* power-on preset to I/O */ dqc_flag = dqc_flagbuf = SET; /* set flag and flag buffer */ dqc_obuf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ dqc_command = CLEAR; /* clear command */ dqc_control = CLEAR; /* clear control */ if (dqc_busy) sim_cancel (&dqc_unit[dqc_busy - 1]); sim_cancel (&dqd_unit); /* cancel dch */ dqd_xfer = 0; /* clr dch xfer */ dqc_busy = 0; /* clr busy */ break; case ioSTC: /* set control flip-flop */ dqc_control = SET; /* set ctl */ if (!dqc_command) { /* cmd clr? */ dqc_command = SET; /* set cmd */ drv = CW_GETDRV (dqc_obuf); /* get fnc, drv */ fnc = CW_GETFNC (dqc_obuf); /* from cmd word */ switch (fnc) { /* case on fnc */ case FNC_SEEK: case FNC_RCL: /* seek, recal */ case FNC_CHK: /* check */ dqc_sta[drv] = 0; /* clear status */ case FNC_STA: case FNC_LA: /* rd sta, load addr */ dq_god (fnc, drv, dqc_dtime); /* sched dch xfer */ break; case FNC_RD: case FNC_WD: /* read, write */ case FNC_RA: case FNC_WA: /* rd addr, wr addr */ case FNC_AS: /* address skip */ dq_goc (fnc, drv, dqc_ctime); /* sched drive */ break; } /* end case */ } /* end if !CMD */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, dqc); /* set standard PRL signal */ setstdIRQ (select_code, dqc); /* set standard IRQ signal */ setstdSRQ (select_code, dqc); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ dqc_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dqcio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dqcio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Start data channel operation */ void dq_god (int32 fnc, int32 drv, int32 time) { dqd_unit.DRV = drv; /* save unit */ dqd_unit.FNC = fnc; /* save function */ sim_activate (&dqd_unit, time); return; } /* Start controller operation */ void dq_goc (int32 fnc, int32 drv, int32 time) { int32 t; if (t = sim_is_active (&dqc_unit[drv])) { /* still seeking? */ sim_cancel (&dqc_unit[drv]); /* cancel */ time = time + t; /* include seek time */ } dqc_sta[drv] = 0; /* clear status */ dq_ptr = 0; /* init buf ptr */ dqc_busy = drv + 1; /* set busy */ dqd_xfer = 1; /* xfer in prog */ dqc_unit[drv].FNC = fnc; /* save function */ sim_activate (&dqc_unit[drv], time); /* activate unit */ return; } /* Data channel unit service This routine handles the data channel transfers. It also handles data transfers that are blocked by seek in progress. uptr->DRV = target drive uptr->FNC = target function Seek substates seek - transfer cylinder seek1 - transfer head/surface, sched drive Recalibrate substates rcl - clear cyl/head/surface, sched drive Load address la - transfer cylinder la1 - transfer head/surface, finish operation Status check - transfer status, finish operation Check data chk - transfer sector count, sched drive */ t_stat dqd_svc (UNIT *uptr) { int32 drv, st; drv = uptr->DRV; /* get drive no */ switch (uptr->FNC) { /* case function */ case FNC_LA: /* arec, need cyl */ case FNC_SEEK: /* seek, need cyl */ if (dqd_command) { /* dch active? */ dqc_rarc = DA_GETCYL (dqd_obuf); /* set RAR from cyl word */ dqd_wval = 0; /* clr data valid */ dqd_command = CLEAR; /* clr dch cmd */ dqdio (dqd_dib.devno, ioENF, 0); /* set dch flg */ if (uptr->FNC == FNC_LA) uptr->FNC = FNC_LA1; else uptr->FNC = FNC_SEEK1; /* advance state */ } sim_activate (uptr, dqc_xtime); /* no, wait more */ break; case FNC_LA1: /* arec, need hd/sec */ case FNC_SEEK1: /* seek, need hd/sec */ if (dqd_command) { /* dch active? */ dqc_rarh = DA_GETHD (dqd_obuf); /* set RAR from head */ dqc_rars = DA_GETSC (dqd_obuf); /* set RAR from sector */ dqd_wval = 0; /* clr data valid */ dqd_command = CLEAR; /* clr dch cmd */ dqdio (dqd_dib.devno, ioENF, 0); /* set dch flg */ if (uptr->FNC == FNC_LA1) { dqc_command = CLEAR; /* clr cch cmd */ dqcio (dqc_dib.devno, ioENF, 0); /* set cch flg */ break; /* done if Load Address */ } if (sim_is_active (&dqc_unit[drv])) break; /* if busy, seek check */ st = abs (dqc_rarc - dqc_ucyl[drv]) * dqc_stime; if (st == 0) st = dqc_xtime; /* if on cyl, min time */ else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */ dqc_ucyl[drv] = dqc_rarc; /* transfer RAR */ dqc_uhed[drv] = dqc_rarh; sim_activate (&dqc_unit[drv], st); /* schedule op */ dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */ } else sim_activate (uptr, dqc_xtime); /* no, wait more */ break; case FNC_RCL: /* recalibrate */ dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */ if (sim_is_active (&dqc_unit[drv])) break; /* ignore if busy */ st = dqc_ucyl[drv] * dqc_stime; /* calc diff */ if (st == 0) st = dqc_xtime; /* if on cyl, min time */ else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */ sim_activate (&dqc_unit[drv], st); /* schedule drive */ dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */ dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */ break; case FNC_STA: /* read status */ if (dqd_command) { /* dch active? */ if ((dqc_unit[drv].flags & UNIT_UNLOAD) == 0) /* drive up? */ dqd_ibuf = dqc_sta[drv] & ~STA_DID; else dqd_ibuf = STA_NRDY; if (dqd_ibuf & STA_ANYERR) /* errors? set flg */ dqd_ibuf = dqd_ibuf | STA_ERR; if (drv) dqd_ibuf = dqd_ibuf | STA_DID; dqc_command = CLEAR; /* clr cch cmd */ dqd_command = CLEAR; /* clr dch cmd */ dqdio (dqd_dib.devno, ioENF, 0); /* set dch flg */ dqc_sta[drv] = dqc_sta[drv] & ~STA_ANYERR; /* clr sta flags */ } else sim_activate (uptr, dqc_xtime); /* wait more */ break; case FNC_CHK: /* check, need cnt */ if (dqd_command) { /* dch active? */ dqc_cnt = dqd_obuf & DA_CKMASK; /* get count */ dqd_wval = 0; /* clr data valid */ dq_goc (FNC_CHK1, drv, dqc_ctime); /* sched drv */ } else sim_activate (uptr, dqc_xtime); /* wait more */ break; default: return SCPE_IERR; } return SCPE_OK; } /* Drive unit service This routine handles the data transfers. Seek substates seek2 - done Recalibrate substate rcl1 - done Check data substates chk1 - finish operation Read Read address Address skip (read without header check) Write Write address */ #define GETDA(x,y,z) \ (((((x) * DQ_NUMSF) + (y)) * DQ_NUMSC) + (z)) * DQ_NUMWD t_stat dqc_svc (UNIT *uptr) { int32 da, drv, devc, devd, err; err = 0; /* assume no err */ drv = uptr - dqc_dev.units; /* get drive no */ devc = dqc_dib.devno; /* get cch devno */ devd = dqd_dib.devno; /* get dch devno */ if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ dqc_command = CLEAR; /* clr cch cmd */ dqcio (dqc_dib.devno, ioENF, 0); /* set cch flg */ dqc_sta[drv] = 0; /* clr status */ dqc_busy = 0; /* ctlr is free */ dqd_xfer = dqd_wval = 0; return SCPE_OK; } switch (uptr->FNC) { /* case function */ case FNC_SEEK2: /* seek done */ if (dqc_ucyl[drv] >= DQ_NUMCY) { /* out of range? */ dqc_sta[drv] = dqc_sta[drv] | STA_BSY | STA_ERR; /* seek check */ dqc_ucyl[drv] = 0; /* seek to cyl 0 */ } else dqc_sta[drv] = dqc_sta[drv] & ~STA_BSY; /* drive not busy */ case FNC_SEEK3: if (dqc_busy || dqc_flag) { /* ctrl busy? */ uptr->FNC = FNC_SEEK3; /* next state */ sim_activate (uptr, dqc_xtime); /* ctrl busy? wait */ } else { dqc_command = CLEAR; /* clr cch cmd */ dqcio (dqc_dib.devno, ioENF, 0); /* set cch flg */ } return SCPE_OK; case FNC_RA: /* read addr */ if (!dqd_command) break; /* dch clr? done */ if (dq_ptr == 0) dqd_ibuf = dqc_ucyl[drv]; /* 1st word? */ else if (dq_ptr == 1) { /* second word? */ dqd_ibuf = (dqc_uhed[drv] << DA_V_HD) | /* use drive head */ (dqc_rars << DA_V_SC); /* and RAR sector */ dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ } else break; dq_ptr = dq_ptr + 1; dqd_command = CLEAR; /* clr dch cmd */ dqdio (dqd_dib.devno, ioENF, 0); /* set dch flg */ sim_activate (uptr, dqc_xtime); /* sched next word */ return SCPE_OK; case FNC_AS: /* address skip */ case FNC_RD: /* read */ case FNC_CHK1: /* check */ if (dq_ptr == 0) { /* new sector? */ if (!dqd_command && (uptr->FNC != FNC_CHK1)) break; if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */ (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */ (dqc_rars >= DQ_NUMSC)) { /* bad sector? */ dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */ break; } if (dqc_rarh >= DQ_NUMSF) { /* bad head? */ dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */ break; } da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */ dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ if (dqc_rars == 0) /* wrap? incr head */ dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1; if (err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET)) break; fxread (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref); if (err = ferror (uptr->fileref)) break; } dqd_ibuf = dqxb[dq_ptr++]; /* get word */ if (dq_ptr >= DQ_NUMWD) { /* end of sector? */ if (uptr->FNC == FNC_CHK1) { /* check? */ dqc_cnt = (dqc_cnt - 1) & DA_CKMASK; /* decr count */ if (dqc_cnt == 0) break; /* if zero, done */ } dq_ptr = 0; /* wrap buf ptr */ } if (dqd_command && dqd_xfer) { /* dch on, xfer? */ dqdio (dqd_dib.devno, ioENF, 0); /* set flag */ } dqd_command = CLEAR; /* clr dch cmd */ sim_activate (uptr, dqc_xtime); /* sched next word */ return SCPE_OK; case FNC_WA: /* write address */ case FNC_WD: /* write */ if (dq_ptr == 0) { /* sector start? */ if (!dqd_command && !dqd_wval) break; /* xfer done? */ if (uptr->flags & UNIT_WPRT) { /* write protect? */ dqc_sta[drv] = dqc_sta[drv] | STA_FLG; break; /* done */ } if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */ (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */ (dqc_rars >= DQ_NUMSC)) { /* bad sector? */ dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */ break; } if (dqc_rarh >= DQ_NUMSF) { /* bad head? */ dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */ break; } } dqxb[dq_ptr++] = dqd_wval? dqd_obuf: 0; /* store word/fill */ dqd_wval = 0; /* clr data valid */ if (dq_ptr >= DQ_NUMWD) { /* buffer full? */ da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */ dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ if (dqc_rars == 0) /* wrap? incr head */ dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1; if (err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET)) return TRUE; fxwrite (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref); if (err = ferror (uptr->fileref)) break; dq_ptr = 0; } if (dqd_command && dqd_xfer) { /* dch on, xfer? */ dqdio (dqd_dib.devno, ioENF, 0); /* set flag */ } dqd_command = CLEAR; /* clr dch cmd */ sim_activate (uptr, dqc_xtime); /* sched next word */ return SCPE_OK; default: return SCPE_IERR; } /* end case fnc */ dqc_command = CLEAR; /* clr cch cmd */ dqcio (dqc_dib.devno, ioENF, 0); /* set cch flg */ dqc_busy = 0; /* ctlr is free */ dqd_xfer = dqd_wval = 0; if (err != 0) { /* error? */ perror ("DQ I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat dqc_reset (DEVICE *dptr) { int32 drv; hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &dqd_dev)? &dqc_dev: &dqd_dev); if (sim_switches & SWMASK ('P')) { /* PON reset? */ dqd_ibuf = 0; /* clear buffers */ dqd_obuf = 0; dqc_obuf = 0; dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */ } if (dptr == &dqc_dev) /* command channel reset? */ dqcio (dqc_dib.devno, ioPOPIO, 0); /* send POPIO signal to command channel */ else /* data channel reset */ dqdio (dqd_dib.devno, ioPOPIO, 0); /* send POPIO signal to data channel */ dqc_busy = 0; /* reset controller state */ dqd_xfer = 0; dqd_wval = 0; dq_ptr = 0; sim_cancel (&dqd_unit); /* cancel dch */ for (drv = 0; drv < DQ_NUMDRV; drv++) { /* loop thru drives */ sim_cancel (&dqc_unit[drv]); /* cancel activity */ dqc_unit[drv].FNC = 0; /* clear function */ dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */ dqc_sta[drv] = 0; /* clear status */ } return SCPE_OK; } /* Attach routine */ t_stat dqc_attach (UNIT *uptr, char *cptr) { t_stat r; r = attach_unit (uptr, cptr); /* attach unit */ if (r == SCPE_OK) dqc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */ return r; } /* Detach routine */ t_stat dqc_detach (UNIT* uptr) { dqc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */ return detach_unit (uptr); /* detach unit */ } /* Load and unload heads */ t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) { if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */ if (value == UNIT_UNLOAD) /* unload heads? */ uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ else uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ return SCPE_OK; } /* 7900/7901/2883/2884 bootstrap routine (HP 12992A ROM) */ const BOOT_ROM dq_rom = { 0102501, /*ST LIA 1 ; get switches */ 0106501, /* LIB 1 */ 0013765, /* AND D7 ; isolate hd */ 0005750, /* BLF,CLE,SLB */ 0027741, /* JMP RD */ 0005335, /* RBR,SLB,ERB ; <13>->E, set = 2883 */ 0027717, /* JMP IS */ 0102611, /*LP OTA CC ; do 7900 status to */ 0103711, /* STC CC,C ; clear first seek */ 0102310, /* SFS DC */ 0027711, /* JMP *-1 */ 0002004, /* INA ; get next drive */ 0053765, /* CPA D7 ; all cleared? */ 0002001, /* RSS */ 0027707, /* JMP LP */ 0067761, /*IS LDB SEEKC ; get seek comnd */ 0106610, /* OTB DC ; issue cyl addr (0) */ 0103710, /* STC DC,C ; to dch */ 0106611, /* OTB CC ; seek cmd */ 0103711, /* STC CC,C ; to cch */ 0102310, /* SFS DC ; addr wd ok? */ 0027724, /* JMP *-1 ; no, wait */ 0006400, /* CLB */ 0102501, /* LIA 1 ; get switches */ 0002051, /* SEZ,SLA,RSS ; subchan = 1 or ISS */ 0047770, /* ADB BIT9 ; head 2 */ 0106610, /* OTB DC ; head/sector */ 0103710, /* STC DC,C ; to dch */ 0102311, /* SFS CC ; seek done? */ 0027734, /* JMP *-1 ; no, wait */ 0063731, /* LDA ISSRD ; get read read */ 0002341, /* SEZ,CCE,RSS ; iss disc? */ 0001100, /* ARS ; no, make 7900 read */ 0067776, /*RD LDB DMACW ; DMA control */ 0106606, /* OTB 6 */ 0067762, /* LDB ADDR1 ; memory addr */ 0077741, /* STB RD ; make non re-executable */ 0106602, /* OTB 2 */ 0102702, /* STC 2 ; flip DMA ctrl */ 0067764, /* LDB COUNT ; word count */ 0106602, /* OTB 2 */ 0002041, /* SEZ,RSS */ 0027766, /* JMP NW */ 0102611, /* OTA CC ; to cch */ 0103710, /* STC DC,C ; start dch */ 0103706, /* STC 6,C ; start DMA */ 0103711, /* STC CC,C ; start cch */ 0037773, /* ISZ SK */ 0027773, /* JMP SK */ 0030000, /*SEEKC 030000 */ 0102011, /*ADDR1 102011 */ 0102055, /*ADDR2 102055 */ 0164000, /*COUNT -6144. */ 0000007, /*D7 7 */ 0106710, /*NW CLC DC ; set 'next wd is cmd' flag */ 0001720, /* ALF,ALF ; move to head number loc */ 0001000, /*BIT9 ALS */ 0103610, /* OTA DC,C ; output cold load cmd */ 0103706, /* STC 6,C ; start DMA */ 0102310, /* SFS DC ; done? */ 0027773, /* JMP *-1 ; no, wait */ 0117763, /*XT JSB ADDR2,I ; start program */ 0120010, /*DMACW 120000+DC */ 0000000 /* -ST */ }; t_stat dqc_boot (int32 unitno, DEVICE *dptr) { int32 dev; if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ dev = dqd_dib.devno; /* get data chan dev */ if (ibl_copy (dq_rom, dev)) return SCPE_IERR; /* copy boot to memory */ SR = (SR & IBL_OPT) | IBL_DQ | (dev << IBL_V_DEV); /* set SR */ return SCPE_OK; } simh-3.8.1/HP2100/hp2100_cpu3.c0000644000175000017500000012176711062235642013502 0ustar vlmvlm/* hp2100_cpu3.c: HP 2100/1000 FFP/DBI instructions Copyright (c) 2005-2008, J. David Bryan 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 AUTHOR 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 author. CPU3 Fast FORTRAN and Double Integer instructions 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 05-Aug-08 JDB Updated mp_dms_jmp calling sequence 27-Feb-08 JDB Added DBI self-test instruction 23-Oct-07 JDB Fixed unsigned-divide bug in .DDI 17-Oct-07 JDB Fixed unsigned-multiply bug in .DMP 16-Oct-06 JDB Calls FPP for extended-precision math 12-Oct-06 JDB Altered DBLE, DDINT for F-Series FFP compatibility 26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions 09-Aug-06 JDB Added double-integer instruction set 18-Feb-05 JDB Add 2100/21MX Fast FORTRAN Processor instructions Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #if defined (HAVE_INT64) /* int64 support available */ #include "hp2100_fp1.h" #else /* int64 support unavailable */ #include "hp2100_fp.h" #endif /* end of int64 support */ /* Fast FORTRAN Processor. The Fast FORTRAN Processor (FFP) is a set of FORTRAN language accelerators and extended-precision (three-word) floating point routines. Although the FFP is an option for the 2100 and later CPUs, each implements the FFP in a slightly different form. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A 12907A 12977B 13306B std The instruction codes are mapped to routines as follows: Instr. 2100 1000-M 1000-E 1000-F Instr. 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ 105200 -- [nop] [nop] [test] 105220 .XFER .XFER .XFER .XFER 105201 DBLE DBLE DBLE DBLE 105221 .GOTO .GOTO .GOTO .GOTO 105202 SNGL SNGL SNGL SNGL 105222 ..MAP ..MAP ..MAP ..MAP 105203 .XMPY .XMPY .XMPY .DNG 105223 .ENTR .ENTR .ENTR .ENTR 105204 .XDIV .XDIV .XDIV .DCO 105224 .ENTP .ENTP .ENTP .ENTP 105205 .DFER .DFER .DFER .DFER 105225 -- .PWR2 .PWR2 .PWR2 105206 -- .XPAK .XPAK .XPAK 105226 -- .FLUN .FLUN .FLUN 105207 -- XADD XADD .BLE 105227 $SETP $SETP $SETP $SETP 105210 -- XSUB XSUB .DIN 105230 -- .PACK .PACK .PACK 105211 -- XMPY XMPY .DDE 105231 -- -- .CFER .CFER 105212 -- XDIV XDIV .DIS 105232 -- -- -- ..FCM 105213 .XADD .XADD .XADD .DDS 105233 -- -- -- ..TCM 105214 .XSUB .XSUB .XSUB .NGL 105234 -- -- -- -- 105215 -- .XCOM .XCOM .XCOM 105235 -- -- -- -- 105216 -- ..DCM ..DCM ..DCM 105236 -- -- -- -- 105217 -- DDINT DDINT DDINT 105237 -- -- -- -- The F-Series maps different instructions to several of the standard FFP opcodes. We first look for these and dispatch them appropriately before falling into the handler for the common instructions. The math functions use the F-Series FPP for implementation. The FPP requires that the host compiler support 64-bit integers. Therefore, if 64-bit integers are not available, the math instructions of the FFP are disabled. We allow this partial implementation as an aid in running systems generated for the FFP. Most system programs did not use the math instructions, but almost all use .ENTR. Supporting the latter even on systems that do not support the former still allows such systems to boot. Implementation notes: 1. The "$SETP" instruction is sometimes listed as ".SETP" in the documentation. 2. Extended-precision arithmetic routines (e.g., .XMPY) exist on the 1000-F, but they are assigned instruction codes in the single-precision floating-point module range. They are replaced by several double integer instructions, which we dispatch to the double integer handler. 3. The software implementation of ..MAP supports 1-, 2-, or 3-dimensional arrays, designated by setting A = -1, 0, and +1, respectively. The firmware implementation supports only 2- and 3-dimensional access. 4. The documentation for ..MAP for the 2100 FFP shows A = 0 or -1 for two or three dimensions, respectively, but the 1000 FFP shows A = 0 or +1. The firmware actually only checks the LSB of A. 5. The .DFER and .XFER implementations for the 2100 FFP return X+4 and Y+4 in the A and B registers, whereas the 1000 FFP returns X+3 and Y+3. 6. The .XFER implementation for the 2100 FFP returns to P+2, whereas the 1000 implementation returns to P+1. 7. The firmware implementations of DBLE, .BLE, and DDINT clear the overflow flag. The software implementations do not change overflow. 8. The M/E-Series FFP arithmetic instructions (.XADD, etc.) return negative infinity on negative overflow and positive infinity on positive overflow. The equivalent F-Series instructions return positive infinity on both. 9. The protected memory lower bound for the .GOTO instruction is 2. Additional references: - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) - Implementing the HP 2100 Fast FORTRAN Processor (12907-90010, Nov-1974) */ static const OP_PAT op_ffp_f[32] = { /* patterns for F-Series only */ OP_N, OP_AAF, OP_AX, OP_N, /* [tst] DBLE SNGL .DNG */ OP_N, OP_AA, OP_A, OP_AAF, /* .DCO .DFER .XPAK .BLE */ OP_N, OP_N, OP_N, OP_N, /* .DIN .DDE .DIS .DDS */ OP_AT, OP_A, OP_A, OP_AAX, /* .NGL .XCOM ..DCM DDINT */ OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */ OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */ OP_RC, OP_AA, OP_R, OP_A, /* .PACK .CFER ..FCM ..TCM */ OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ }; static const OP_PAT op_ffp_e[32] = { /* patterns for 2100/M/E-Series */ OP_N, OP_AAF, OP_AX, OP_AXX, /* [nop] DBLE SNGL .XMPY */ OP_AXX, OP_AA, OP_A, OP_AAXX, /* .XDIV .DFER .XPAK XADD */ OP_AAXX, OP_AAXX, OP_AAXX, OP_AXX, /* XSUB XMPY XDIV .XADD */ OP_AXX, OP_A, OP_A, OP_AAX, /* .XSUB .XCOM ..DCM DDINT */ OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */ OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */ OP_RC, OP_AA, OP_N, OP_N, /* .PACK .CFER --- --- */ OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ }; t_stat cpu_ffp (uint32 IR, uint32 intrq) { OP fpop; OPS op, op2; uint32 entry; uint32 j, sa, sb, sc, da, dc, ra, MA; int32 expon; t_stat reason = SCPE_OK; #if defined (HAVE_INT64) /* int64 support available */ int32 i; #endif /* end of int64 support */ entry = IR & 037; /* mask to entry point */ if (UNIT_CPU_MODEL != UNIT_1000_F) { /* 2100/M/E-Series? */ if (op_ffp_e[entry] != OP_N) if (reason = cpu_ops (op_ffp_e[entry], op, intrq)) /* get instruction operands */ return reason; } #if defined (HAVE_INT64) /* int64 support available */ else { /* F-Series */ if (op_ffp_f[entry] != OP_N) if (reason = cpu_ops (op_ffp_f[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<4:0> */ case 000: /* [tst] 105200 (OP_N) */ XR = 4; /* firmware revision */ SR = 0102077; /* test passed code */ AR = 0; /* test clears A/B */ BR = 0; PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DBI */ return reason; case 003: /* .DNG 105203 (OP_N) */ return cpu_dbi (0105323, intrq); /* remap to double int handler */ case 004: /* .DCO 105204 (OP_N) */ return cpu_dbi (0105324, intrq); /* remap to double int handler */ case 007: /* .BLE 105207 (OP_AAF) */ O = fp_cvt (&op[2], fp_f, fp_t); /* convert value and clear overflow */ WriteOp (op[1].word, op[2], fp_t); /* write double-precision value */ return reason; case 010: /* .DIN 105210 (OP_N) */ return cpu_dbi (0105330, intrq); /* remap to double int handler */ case 011: /* .DDE 105211 (OP_N) */ return cpu_dbi (0105331, intrq); /* remap to double int handler */ case 012: /* .DIS 105212 (OP_N) */ return cpu_dbi (0105332, intrq); /* remap to double int handler */ case 013: /* .DDS 105213 (OP_N) */ return cpu_dbi (0105333, intrq); /* remap to double int handler */ case 014: /* .NGL 105214 (OP_AT) */ O = fp_cvt (&op[1], fp_t, fp_f); /* convert value */ AR = op[1].fpk[0]; /* move MSB to A */ BR = op[1].fpk[1]; /* move LSB to B */ return reason; case 032: /* ..FCM 105232 (OP_R) */ O = fp_pcom (&op[0], fp_f); /* complement value */ AR = op[0].fpk[0]; /* return result */ BR = op[0].fpk[1]; /* to A/B registers */ return reason; case 033: /* ..TCM 105233 (OP_A) */ fpop = ReadOp (op[0].word, fp_t); /* read 4-word value */ O = fp_pcom (&fpop, fp_t); /* complement it */ WriteOp (op[0].word, fpop, fp_t); /* write 4-word value */ return reason; } /* fall thru if not special to F */ } #endif /* end of int64 support */ switch (entry) { /* decode IR<4:0> */ /* FFP module 1 */ case 000: /* [nop] 105200 (OP_N) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 M/E-series */ return stop_inst; /* trap if not */ break; #if defined (HAVE_INT64) /* int64 support available */ case 001: /* DBLE 105201 (OP_AAF) */ O = fp_cvt (&op[2], fp_f, fp_x); /* convert value and clear overflow */ WriteOp (op[1].word, op[2], fp_x); /* write extended-precision value */ break; case 002: /* SNGL 105202 (OP_AX) */ O = fp_cvt (&op[1], fp_x, fp_f); /* convert value */ AR = op[1].fpk[0]; /* move MSB to A */ BR = op[1].fpk[1]; /* move LSB to B */ break; case 003: /* .XMPY 105203 (OP_AXX) */ i = 0; /* params start at op[0] */ goto XMPY; /* process as XMPY */ case 004: /* .XDIV 105204 (OP_AXX) */ i = 0; /* params start at op[0] */ goto XDIV; /* process as XDIV */ #endif /* end of int64 support */ case 005: /* .DFER 105205 (OP_AA) */ BR = op[0].word; /* get destination address */ AR = op[1].word; /* get source address */ goto XFER; /* do transfer */ #if defined (HAVE_INT64) /* int64 support available */ case 006: /* .XPAK 105206 (OP_A) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } fpop = ReadOp (op[0].word, fp_x); /* read unpacked */ O = fp_nrpack (&fpop, fpop, (int16) AR, fp_x); /* nrm/rnd/pack mantissa, exponent */ WriteOp (op[0].word, fpop, fp_x); /* write result */ break; case 007: /* XADD 105207 (OP_AAXX) */ i = 1; /* params start at op[1] */ XADD: /* enter here from .XADD */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } O = fp_exec (001, &fpop, op[i + 1], op[i + 2]); /* three-word add */ WriteOp (op[i].word, fpop, fp_x); /* write sum */ break; case 010: /* XSUB 105210 (OP_AAXX) */ i = 1; /* params start at op[1] */ XSUB: /* enter here from .XSUB */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } O = fp_exec (021, &fpop, op[i + 1], op[i + 2]); /* three-word subtract */ WriteOp (op[i].word, fpop, fp_x); /* write difference */ break; case 011: /* XMPY 105211 (OP_AAXX) */ i = 1; /* params start at op[1] */ XMPY: /* enter here from .XMPY */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } O = fp_exec (041, &fpop, op[i + 1], op[i + 2]); /* three-word multiply */ WriteOp (op[i].word, fpop, fp_x); /* write product */ break; case 012: /* XDIV 105212 (OP_AAXX) */ i = 1; /* params start at op[1] */ XDIV: /* enter here from .XDIV */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } O = fp_exec (061, &fpop, op[i + 1], op[i + 2]); /* three-word divide */ WriteOp (op[i].word, fpop, fp_x); /* write quotient */ break; case 013: /* .XADD 105213 (OP_AXX) */ i = 0; /* params start at op[0] */ goto XADD; /* process as XADD */ case 014: /* .XSUB 105214 (OP_AXX) */ i = 0; /* params start at op[0] */ goto XSUB; /* process as XSUB */ case 015: /* .XCOM 105215 (OP_A) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ fpop = ReadOp (op[0].word, fp_x); /* read unpacked */ AR = fp_ucom (&fpop, fp_x); /* complement and rtn exp adj */ WriteOp (op[0].word, fpop, fp_x); /* write result */ break; case 016: /* ..DCM 105216 (OP_A) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } fpop = ReadOp (op[0].word, fp_x); /* read operand */ O = fp_pcom (&fpop, fp_x); /* complement (can't ovf neg) */ WriteOp (op[0].word, fpop, fp_x); /* write result */ break; case 017: /* DDINT 105217 (OP_AAX) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ if (intrq) { /* interrupt pending? */ PC = err_PC; /* restart instruction */ break; } O = fp_trun (&fpop, op[2], fp_x); /* truncate operand (can't ovf) */ WriteOp (op[1].word, fpop, fp_x); /* write result */ break; #endif /* end of int64 support */ /* FFP module 2 */ case 020: /* .XFER 105220 (OP_N) */ if (UNIT_CPU_TYPE == UNIT_TYPE_2100) PC = (PC + 1) & VAMASK; /* 2100 .XFER returns to P+2 */ XFER: /* enter here from .DFER */ sc = 3; /* set count for 3-wd xfer */ goto CFER; /* do transfer */ case 021: /* .GOTO 105221 (OP_AK) */ if ((int16) op[1].word < 1) /* index < 1? */ op[1].word = 1; /* reset min */ sa = PC + op[1].word - 1; /* point to jump target */ if (sa >= op[0].word) /* must be <= last target */ sa = op[0].word - 1; da = ReadW (sa); /* get jump target */ if (reason = resolve (da, &MA, intrq)) { /* resolve indirects */ PC = err_PC; /* irq restarts instruction */ break; } mp_dms_jmp (MA, 2); /* validate jump addr */ PCQ_ENTRY; /* record last PC */ PC = MA; /* jump */ BR = op[0].word; /* (for 2100 FFP compat) */ break; case 022: /* ..MAP 105222 (OP_KKKK) */ op[1].word = op[1].word - 1; /* decrement 1st subscr */ if ((AR & 1) == 0) /* 2-dim access? */ op[1].word = op[1].word + /* compute element offset */ (op[2].word - 1) * op[3].word; else { /* 3-dim access */ if (reason = cpu_ops (OP_KK, op2, intrq)) { /* get 1st, 2nd ranges */ PC = err_PC; /* irq restarts instruction */ break; } op[1].word = op[1].word + /* offset */ ((op[3].word - 1) * op2[1].word + op[2].word - 1) * op2[0].word; } AR = (op[0].word + op[1].word * BR) & DMASK; /* return element address */ break; case 023: /* .ENTR 105223 (OP_A) */ MA = PC - 3; /* get addr of entry point */ ENTR: /* enter here from .ENTP */ da = op[0].word; /* get addr of 1st formal */ dc = MA - da; /* get count of formals */ sa = ReadW (MA); /* get addr of return point */ ra = ReadW (sa++); /* get rtn, ptr to 1st actual */ WriteW (MA, ra); /* stuff rtn into caller's ent */ sc = ra - sa; /* get count of actuals */ if (sc > dc) /* use min (actuals, formals) */ sc = dc; for (j = 0; j < sc; j++) { MA = ReadW (sa++); /* get addr of actual */ if (reason = resolve (MA, &MA, intrq)) { /* resolve indirect */ PC = err_PC; /* irq restarts instruction */ break; } WriteW (da++, MA); /* put addr into formal */ } AR = ra; /* return address */ BR = da; /* addr of 1st unused formal */ break; case 024: /* .ENTP 105224 (OP_A) */ MA = PC - 5; /* get addr of entry point */ goto ENTR; case 025: /* .PWR2 105225 (OP_RK) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */ expon = expon + (int16) (op[1].word); /* multiply by 2**n */ fp_pack (&fpop, fpop, expon, fp_f); /* repack value */ AR = fpop.fpk[0]; /* return result */ BR = fpop.fpk[1]; /* to A/B registers */ break; case 026: /* .FLUN 105226 (OP_R) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */ AR = (int16) expon; /* return expon to A */ BR = fpop.fpk[1]; /* and low mant to B */ break; case 027: /* $SETP 105227 (OP_K) */ j = sa = AR; /* save initial value */ sb = BR; /* save initial address */ AR = 0; /* AR will return = 0 */ BR = BR & VAMASK; /* addr must be direct */ do { WriteW (BR, j); /* write value to address */ j = (j + 1) & DMASK; /* incr value */ BR = (BR + 1) & VAMASK; /* incr address */ op[0].word = op[0].word - 1; /* decr count */ if (op[0].word && intrq) { /* more and intr? */ AR = sa; /* restore A */ BR = sb; /* restore B */ PC = err_PC; /* restart instruction */ break; } } while (op[0].word != 0); /* loop until count exhausted */ break; case 030: /* .PACK 105230 (OP_RC) */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ O = fp_nrpack (&fpop, op[0], /* nrm/rnd/pack value */ (int16) (op[1].word), fp_f); AR = fpop.fpk[0]; /* return result */ BR = fpop.fpk[1]; /* to A/B registers */ break; case 031: /* .CFER 105231 (OP_AA) */ if ((UNIT_CPU_MODEL != UNIT_1000_E) && /* must be 1000 E-series */ (UNIT_CPU_MODEL != UNIT_1000_F)) /* or 1000 F-series */ return stop_inst; /* trap if not */ BR = op[0].word; /* get destination address */ AR = op[1].word; /* get source address */ sc = 4; /* set for 4-wd xfer */ CFER: /* enter here from .XFER */ for (j = 0; j < sc; j++) { /* xfer loop */ WriteW (BR, ReadW (AR)); /* transfer word */ AR = (AR + 1) & VAMASK; /* bump source addr */ BR = (BR + 1) & VAMASK; /* bump destination addr */ } E = 0; /* routine clears E */ if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { /* 2100 (and .DFER/.XFER)? */ AR = (AR + 1) & VAMASK; /* 2100 FFP returns X+4, Y+4 */ BR = (BR + 1) & VAMASK; } break; default: /* others undefined */ reason = stop_inst; } return reason; } /* Double-Integer Instructions. The double-integer instructions were added to the HP instruction set at revision 1920 of the 1000-F. They were immediately adopted in a number of HP software products, most notably the RTE file management package (FMP) routines. As these routines are used in nearly every RTE program, F-Series programs were almost always a few hundred bytes smaller than their M- and E-Series counterparts. This became significant as RTE continued to grow in size, and some customer programs ran out of address space on E-Series machines. While HP never added double-integer instructions to the standard E-Series, a product from the HP "specials group," HP 93585A, provided microcoded replacements for the E-Series. This could provide just enough address-space savings to allow programs to load in E-Series systems, in addition to accelerating these common operations. There was no equivalent M-Series microcode, due to the limited micromachine address space on that system. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A 93585A std The routines are mapped to instruction codes as follows: Instr. 1000-E 1000-F Description ------ ------ ------ ----------------------------------------- [test] 105320 -- [self test] .DAD 105321 105014 Double integer add .DMP 105322 105054 Double integer multiply .DNG 105323 105203 Double integer negate .DCO 105324 105204 Double integer compare .DDI 105325 105074 Double integer divide .DDIR 105326 105134 Double integer divide (reversed) .DSB 105327 105034 Double integer subtract .DIN 105330 105210 Double integer increment .DDE 105331 105211 Double integer decrement .DIS 105332 105212 Double integer increment and skip if zero .DDS 105333 105213 Double integer decrement and skip if zero .DSBR 105334 105114 Double integer subtraction (reversed) On the F-Series, the double-integer instruction codes are split among the floating-point processor and the Fast FORTRAN Processor ranges. They are dispatched from those respective simulators for processing here. Implementation notes: 1. Opcodes 105335-105337 are NOPs in the microcode. They generate unimplemented instructions stops under simulation. 2. This is an implementation of Revision 2 of the microcode, which was released as ROM part numbers 93585-80003, 93585-80005, and 93585-80001 (Revision 1 substituted -80002 for -80005). 3. The F-Series firmware executes .DMP and .DDI/.DDIR by floating the 32-bit double integer to a 48-bit extended-precision number, calling the FPP to execute the extended-precision multiply/divide, and then fixing the product to a 32-bit double integer. We simulate these directly with 64- or 32-bit integer arithmetic. Additional references: - 93585A Microcode Source (93585-18002 Rev. 2005) - 93585A Double Integer Instructions Installation and Reference Manual (93585-90007) */ static const OP_PAT op_dbi[16] = { OP_N, OP_JD, OP_JD, OP_J, /* [test] .DAD .DMP .DNG */ OP_JD, OP_JD, OP_JD, OP_JD, /* .DCO .DDI .DDIR .DSB */ OP_J, OP_J, OP_A, OP_A, /* .DIN .DDE .DIS .DDS */ OP_JD, OP_N, OP_N, OP_N /* .DSBR --- --- --- */ }; t_stat cpu_dbi (uint32 IR, uint32 intrq) { OP din; OPS op; uint32 entry, t; t_stat reason = SCPE_OK; entry = IR & 017; /* mask to entry point */ if (op_dbi[entry] != OP_N) if (reason = cpu_ops (op_dbi[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<3:0> */ case 000: /* [test] 105320 (OP_N) */ XR = 2; /* set revision */ BR = 0377; /* side effect of microcode */ SR = 0102077; /* set "pass" code */ PC = (PC + 1) & VAMASK; /* return to P+1 */ t = (AR << 16) | BR; /* set t for return */ break; case 001: /* .DAD 105321 (OP_JD) */ t = op[0].dword + op[1].dword; /* add values */ E = E | (t < op[0].dword); /* carry if result smaller */ O = (((~op[0].dword ^ op[1].dword) & /* overflow if sign wrong */ (op[0].dword ^ t) & SIGN32) != 0); break; case 002: /* .DMP 105322 (OP_JD) */ { #if defined (HAVE_INT64) /* int64 support available */ t_int64 t64; t64 = (t_int64) INT32 (op[0].dword) * /* multiply signed values */ (t_int64) INT32 (op[1].dword); O = ((t64 < -(t_int64) 0x80000000) || /* overflow if out of range */ (t64 > (t_int64) 0x7FFFFFFF)); if (O) t = ~SIGN32; /* if overflow, rtn max pos */ else t = (uint32) (t64 & DMASK32); /* else lower 32 bits of result */ #else /* int64 support unavailable */ uint32 sign, xu, yu, rh, rl; sign = ((int32) op[0].dword < 0) ^ /* save sign of result */ ((int32) op[1].dword < 0); xu = (uint32) abs ((int32) op[0].dword); /* make operands pos */ yu = (uint32) abs ((int32) op[1].dword); if ((xu & 0xFFFF0000) == 0 && /* 16 x 16 multiply? */ (yu & 0xFFFF0000) == 0) { t = xu * yu; /* do it */ O = 0; /* can't overflow */ } else if ((xu & 0xFFFF0000) != 0 && /* 32 x 32 multiply? */ (yu & 0xFFFF0000) != 0) O = 1; /* always overflows */ else { /* 16 x 32 or 32 x 16 */ rl = (xu & 0xFFFF) * (yu & 0xFFFF); /* form 1st partial product */ if ((xu & 0xFFFF0000) == 0) rh = xu * (yu >> 16) + (rl >> 16); /* 16 x 32 2nd partial */ else rh = (xu >> 16) * yu + (rl >> 16); /* 32 x 16 2nd partial */ O = (rh > 0x7FFF + sign); /* check for out of range */ if (O == 0) t = (rh << 16) | (rl & 0xFFFF); /* combine partials */ } if (O) t = ~SIGN32; /* if overflow, rtn max pos */ else if (sign) t = ~t + 1; /* if result neg, 2s compl */ #endif /* end of int64 support */ } break; case 003: /* .DNG 105323 (OP_J) */ t = ~op[0].dword + 1; /* negate value */ O = (op[0].dword == SIGN32); /* overflow if max neg */ if (op[0].dword == 0) /* borrow if result zero */ E = 1; break; case 004: /* .DCO 105324 (OP_JD) */ t = op[0].dword; /* copy for later store */ if ((int32) op[0].dword < (int32) op[1].dword) PC = (PC + 1) & VAMASK; /* < rtns to P+2 */ else if ((int32) op[0].dword > (int32) op[1].dword) PC = (PC + 2) & VAMASK; /* > rtns to P+3 */ break; /* = rtns to P+1 */ case 005: /* .DDI 105325 (OP_JD) */ DDI: O = ((op[1].dword == 0) || /* overflow if div 0 */ ((op[0].dword == SIGN32) && /* or max neg div -1 */ ((int32) op[1].dword == -1))); if (O) t = ~SIGN32; /* rtn max pos for ovf */ else t = (uint32) (INT32 (op[0].dword) / /* else return quotient */ INT32 (op[1].dword)); break; case 006: /* .DDIR 105326 (OP_JD) */ t = op[0].dword; /* swap operands */ op[0].dword = op[1].dword; op[1].dword = t; goto DDI; /* continue at .DDI */ case 007: /* .DSB 105327 (OP_JD) */ DSB: t = op[0].dword - op[1].dword; /* subtract values */ E = E | (op[0].dword < op[1].dword); /* borrow if minu < subtr */ O = (((op[0].dword ^ op[1].dword) & /* overflow if sign wrong */ (op[0].dword ^ t) & SIGN32) != 0); break; case 010: /* .DIN 105330 (OP_J) */ t = op[0].dword + 1; /* increment value */ O = (t == SIGN32); /* overflow if sign flipped */ if (t == 0) E = 1; /* carry if result zero */ break; case 011: /* .DDE 105331 (OP_J) */ t = op[0].dword - 1; /* decrement value */ O = (t == ~SIGN32); /* overflow if sign flipped */ if ((int32) t == -1) E = 1; /* borrow if result -1 */ break; case 012: /* .DIS 105332 (OP_A) */ din = ReadOp (op[0].word, in_d); /* get value */ t = din.dword = din.dword + 1; /* increment value */ WriteOp (op[0].word, din, in_d); /* store it back */ if (t == 0) PC = (PC + 1) & VAMASK; /* skip if result zero */ break; case 013: /* .DDS 105333 (OP_A) */ din = ReadOp (op[0].word, in_d); /* get value */ t = din.dword = din.dword - 1; /* decrement value */ WriteOp (op[0].word, din, in_d); /* write it back */ if (t == 0) PC = (PC + 1) & VAMASK; /* skip if result zero */ break; case 014: /* .DSBR 105334 (OP_JD) */ t = op[0].dword; /* swap operands */ op[0].dword = op[1].dword; op[1].dword = t; goto DSB; /* continue at .DSB */ default: /* others undefined */ t = (AR << 16) | BR; /* set t for NOP */ reason = stop_inst; } if (reason == SCPE_OK) { /* if return OK */ AR = (t >> 16) & DMASK; /* break result */ BR = t & DMASK; /* into A and B */ } return reason; } simh-3.8.1/HP2100/hp2100_mpx.c0000644000175000017500000037320611113066134013424 0ustar vlmvlm/* hp2100_mpx.c: HP 12792C eight-channel asynchronous multiplexer simulator Copyright (c) 2008, J. David Bryan 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 AUTHOR 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 author. MPX 12792C 8-channel multiplexer card 25-Nov-08 JDB Revised for new multiplexer library SHOW routines 14-Nov-08 JDB Cleaned up VC++ size mismatch warnings for zero assignments 03-Oct-08 JDB Fixed logic for ENQ/XOFF transmit wait 07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size 26-Jun-08 JDB Rewrote device I/O to model backplane signals 26-May-08 JDB Created MPX device References: - HP 12792B 8-Channel Asynchronous Multiplexer Subsystem Installation and Reference Manual (12792-90020, Jul-1984) - HP 12792B/C 8-Channel Asynchronous Multiplexer Subsystem User's Manual (5955-8867, Jun-1993) - HP 12792B/C 8-Channel Asynchronous Multiplexer Subsystem Configuration Guide (5955-8868, Jun-1993) - HP 1000 series 8-channel Multiplexer Firmware External Reference Specification (October 19, 1982) - HP 12792/12040 Multiplexer Firmware Source (24999-18312, revision C) - Zilog Components Data Book (00-2034-04, 1985) The 12792A/B/C/D was an eight-line asynchronous serial multiplexer that connected terminals, modems, serial line printers, and "black box" devices that used the RS-232 standard to the CPU. It used an on-board microprocessor and provided input and output buffering to support block-mode reads from HP 264x and 262x terminals at speeds up to 19.2K baud. The card handled character editing, echoing, ENQ/ACK handshaking, and read terminator detection, substantially reducing the load on the CPU over the earlier 12920 multiplexer. It was supported by HP under RTE-MIII, RTE-IVB, and RTE-6/VM. Under simulation, it connects with HP terminal emulators via Telnet to a user-specified port. The single interface card contained a Z80 CPU, DMA controller, CTC, four two-channel SIO UARTs, 16K of RAM, 8K of ROM, and I/O backplane latches and control circuitry. The card executed a high-level command set, and data transfer to and from the CPU was via the on-board DMA controller and the DCPC in the CPU. The 12792 for the M/E/F series and the 12040 multiplexer for the A/L series differed only in backplane design. Early ROMs were card-specific, but later ones were interchangeable; the code would determine whether it was executing on an MEF card or an AL card. Four major firmware revisions were made. These were labelled "A", "B", "C", and "D". The A, B, and C revisions were interchangeable from the perspective of the OS driver; the D was different and required an updated driver. Specifically: Op. Sys. Driver Part Number Rev -------- ------ -------------------- --- RTE-MIII DVM00 12792-16002 Rev.2032 A RTE-IVB DVM00 12792-16002 Rev.5000 ABC RTE-6/VM DVM00 12792-16002 Rev.5000 ABC RTE-6/VM DV800 92084-15068 Rev.6000 D RTE-A IDM00 92077-16754 Rev.5020 ABC RTE-A ID800 92077-16887 Rev.6200 D Revisions A-C have an upward-compatible command set that partitions each OS request into several sub-commands. Each command is initiated by setting the control flip-flop on the card, which causes a non-maskable interrupt (NMI) on the card's Z80 processor. The D-revision firmware uses a completely different command set. The commands are slightly modified versions of the original EXEC calls (read, write, and control) and are generally passed to the card directly for action. This simulation supports the C revision. D-revision support may be added later. Twelve programmable baud rates are supported by the multiplexer. These "realistic" rates are simulated by scheduling I/O service based on the appropriate number of 1000 E-Series instructions for the rate selected. The simulation provides both the "realistic timing" described above, as well as an optimized "fast timing" option. Optimization makes three improvements: 1. Buffered characters are transferred via Telnet in blocks. 2. ENQ/ACK handshaking is done locally without involving the Telnet client. 3. BS and DEL respond visually more like prior RTE terminal drivers. HP did not offer a functional diagnostic for the 12792. Instead, a Z80 program that tested the operation of the hardware was downloaded to the card, and a "go/no-go" status was returned to indicate the hardware condition. Because this is a functional simulation of the multiplexer and not a Z80 emulation, the diagnostic cannot be used to test the implementation. Implementation notes: 1. The 12792 had two baud-rate generators that were assigned to lines by the wiring configuration in the I/O cable connector hood. Two of the four CTC counters were used to implement the BRGs for all eight lines. Only subsets of the configurable rates were allowed for lines connected to the same BRG, and assigning mutually incompatible rates caused corruption of the rates on lines assigned earlier. Under simulation, any baud rate may be assigned to any line without interaction, and assignments of lines to BRGs is not implemented. 2. Revisions B and C added support for the 37214A Systems Modem subsystem and the RTE-A Virtual Control Panel (VCP). Under simulation, the modem commands return status codes indicating that no modems are present, and the VCP commands are not implemented. */ #include #include "hp2100_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" /* Bitfield constructor. Given a bitfield starting bit number and width in bits, declare two constants: one for the starting bit number, and one for the positioned field mask. That is, given a definition such as: BITFIELD(SMALLFIELD,5,2) ...this macro produces: static const uint32 SMALLFIELD_V = 5; static const uint32 SMALLFIELD = ((1 << (2)) - 1) << (5); The latter reduces to 3 << 5, or 0x00000060. Note: C requires constant expressions in initializers for objects with static storage duration, so initializing a static object with a BITFIELD value is illegal (a "static const" object is not a constant!). */ #define BITFIELD(NAME,STARTBIT,BITWIDTH) \ static const uint32 NAME ## _V = STARTBIT; \ static const uint32 NAME = ((1 << (BITWIDTH)) - 1) << (STARTBIT); /* Program constants */ #define MPX_DATE_CODE 2416 /* date code for C firmware */ #define RD_BUF_SIZE 514 /* read buffer size */ #define WR_BUF_SIZE 514 /* write buffer size */ #define RD_BUF_LIMIT 254 /* read buffer limit */ #define WR_BUF_LIMIT 254 /* write buffer limit */ #define KEY_DEFAULT 255 /* default port key */ /* Service times: DATA_DELAY = 1.25 us (Z80 DMA data word transfer time) PARAM_DELAY = 25 us (STC to STF for first word of two-word command) CMD_DELAY = 400 us (STC to STF for one or two-word command execution) */ #define DATA_DELAY 2 /* data transfer time */ #define PARAM_DELAY 40 /* parameter request time */ #define CMD_DELAY 630 /* command completion time */ /* Unit references */ #define MPX_PORTS 8 /* number of visible units */ #define MPX_CNTLS 2 /* number of control units */ #define mpx_cntl (mpx_unit [MPX_PORTS + 0]) /* controller unit */ #define mpx_poll (mpx_unit [MPX_PORTS + 1]) /* Telnet polling unit */ /* Character constants */ #define EOT '\004' #define ENQ '\005' #define ACK '\006' #define BS '\010' #define LF '\012' #define CR '\015' #define DC1 '\021' #define DC2 '\022' #define DC3 '\023' #define ESC '\033' #define RS '\036' #define DEL '\177' #define XON DC1 #define XOFF DC3 /* Device flags */ #define DEV_V_REV_D (DEV_V_UF + 0) /* firmware revision D (not implemented) */ #define DEV_REV_D (1 << DEV_V_REV_D) /* Unit flags */ #define UNIT_V_FASTTIME (UNIT_V_UF + 0) /* fast timing mode */ #define UNIT_V_CAPSLOCK (UNIT_V_UF + 1) /* caps lock mode */ #define UNIT_FASTTIME (1 << UNIT_V_FASTTIME) #define UNIT_CAPSLOCK (1 << UNIT_V_CAPSLOCK) /* Debug flags */ #define DEB_CMDS (1 << 0) /* commands and status */ #define DEB_CPU (1 << 1) /* CPU I/O */ #define DEB_BUF (1 << 2) /* buffer gets and puts */ #define DEB_XFER (1 << 3) /* character reads and writes */ /* Multiplexer commands for revisions A/B/C. Commands are either one or two words in length. The one-word format is: +-------------------------------+-------------------------------+ | 0 . 1 | command opcode | command parameter | +-------------------------------+-------------------------------+ 15 - 8 7 - 0 The two-word format is: +-------------------------------+-------------------------------+ | 1 . 1 | command opcode | command value | +-------------------------------+-------------------------------+ | command parameter | +---------------------------------------------------------------+ 15 - 8 7 - 0 Commands implemented by firmware revision: Rev Cmd Value Operation Status Value(s) Returned --- --- ----- ------------------------------- ------------------------------- ABC 100 - No operation 000000 ABC 101 - Reset to power-on defaults 100000 ABC 102 - Enable unsolicited input None, unless UI pending ABC 103 1 Disable unsolicited interrupts 000000 ABC 103 2 Abort DMA transfer 000000 ABC 104 - Acknowledge Second word of UI status ABC 105 key Cancel first receive buffer 000000 ABC 106 key Cancel all received buffers 000000 ABC 107 - Fast binary read (none) -BC 140 chr VCP put byte 000000 -BC 141 - VCP put buffer 000000 -BC 142 - VCP get byte Character from port 0 -BC 143 - VCP get buffer 000120 -BC 144 - Exit VCP mode 000000 -BC 157 - Enter VCP mode 000000 ABC 300 - No operation 000000 ABC 301 key Request write buffer 000000 or 000376 ABC 302 key Write data to buffer (none) ABC 303 key Set port key 000000 or date code of firmware ABC 304 key Set receive type 000000 ABC 305 key Set character count 000000 ABC 306 key Set flow control 000000 ABC 307 key Read data from buffer (none) ABC 310 - Download executable (none) -BC 311 key Connect line 000000 or 140000 if no modem -BC 312 key Disconnect line 000000 or 140000 if no modem -BC 315 key Get modem/port status modem status or 000200 if no modem -BC 316 key Enable/disable modem loopback 000000 or 140000 if no modem -BC 320 key Terminate active receive buffer 000000 */ /* One-word command codes */ #define CMD_NOP 0100 /* No operation */ #define CMD_RESET 0101 /* Reset firmware to power-on defaults */ #define CMD_ENABLE_UI 0102 /* Enable unsolicited input */ #define CMD_DISABLE 0103 /* Disable interrupts / Abort DMA Transfer */ #define CMD_ACK 0104 /* Acknowledge */ #define CMD_CANCEL 0105 /* Cancel first receive buffer */ #define CMD_CANCEL_ALL 0106 /* Cancel all received buffers */ #define CMD_BINARY_READ 0107 /* Fast binary read */ #define CMD_VCP_PUT 0140 /* VCP put byte */ #define CMD_VCP_PUT_BUF 0141 /* VCP put buffer */ #define CMD_VCP_GET 0142 /* VCP get byte */ #define CMD_VCP_GET_BUF 0143 /* VCP get buffer */ #define CMD_VCP_EXIT 0144 /* Exit VCP mode */ #define CMD_VCP_ENTER 0157 /* Enter VCP mode */ /* Two-word command codes */ #define CMD_REQ_WRITE 0301 /* Request write buffer */ #define CMD_WRITE 0302 /* Write data to buffer */ #define CMD_SET_KEY 0303 /* Set port key */ #define CMD_SET_RCV 0304 /* Set receive type */ #define CMD_SET_COUNT 0305 /* Set character count */ #define CMD_SET_FLOW 0306 /* Set flow control */ #define CMD_READ 0307 /* Read data from buffer */ #define CMD_DL_EXEC 0310 /* Download executable */ #define CMD_CN_LINE 0311 /* Connect line */ #define CMD_DC_LINE 0312 /* Disconnect line */ #define CMD_GET_STATUS 0315 /* Get modem/port status */ #define CMD_LOOPBACK 0316 /* Enable/disable modem loopback */ #define CMD_TERM_BUF 0320 /* Terminate active receive buffer */ /* Sub-command codes */ #define SUBCMD_UI 1 /* Disable unsolicited interrupts */ #define SUBCMD_DMA 2 /* Abort DMA transfer */ #define CMD_TWO_WORDS 0200 /* two-word command flag */ /* Unsolicited interrupt reasons */ #define UI_REASON_V 8 /* interrupt reason */ #define UI_REASON (((1 << 8) - 1) << (UI_REASON_V)) /* (UI_REASON_V must be a constant!) */ BITFIELD (UI_PORT, 0, 3) /* interrupt port number */ #define UI_WRBUF_AVAIL (1 << UI_REASON_V) /* Write buffer available */ #define UI_LINE_CONN (2 << UI_REASON_V) /* Modem line connected */ #define UI_LINE_DISC (3 << UI_REASON_V) /* Modem line disconnected */ #define UI_BRK_RECD (4 << UI_REASON_V) /* Break received */ #define UI_RDBUF_AVAIL (5 << UI_REASON_V) /* Read buffer available */ /* Return status to CPU */ #define ST_OK 0000000 /* Command OK */ #define ST_DIAG_OK 0000015 /* Diagnostic passes */ #define ST_VCP_SIZE 0000120 /* VCP buffer size = 80 chars */ #define ST_NO_SYSMDM 0000200 /* No systems modem card */ #define ST_TEST_OK 0100000 /* Self test OK */ #define ST_NO_MODEM 0140000 /* No modem card on port */ #define ST_BAD_KEY 0135320 /* Bad port key = 0xBAD0 */ /* Bit flags */ #define RS_OVERFLOW 0040000 /* Receive status: buffer overflow occurred */ #define RS_PARTIAL 0020000 /* Receive status: buffer is partial */ #define RS_ETC_RS 0014000 /* Receive status: terminated by RS */ #define RS_ETC_DC2 0010000 /* Receive status: terminated by DC2 */ #define RS_ETC_CR 0004000 /* Receive status: terminated by CR */ #define RS_ETC_EOT 0000000 /* Receive status: terminated by EOT */ #define RS_CHAR_COUNT 0003777 /* Receive status: character count */ #define WR_NO_ENQACK 0020000 /* Write: no ENQ/ACK this xfer */ #define WR_ADD_CRLF 0010000 /* Write: add CR/LF if not '_' */ #define WR_PARTIAL 0004000 /* Write: write is partial */ #define WR_LENGTH 0003777 /* Write: write length in bytes */ #define RT_END_ON_CR 0000200 /* Receive type: end xfer on CR */ #define RT_END_ON_RS 0000100 /* Receive type: end xfer on RS */ #define RT_END_ON_EOT 0000040 /* Receive type: end xfer on EOT */ #define RT_END_ON_DC2 0000020 /* Receive type: end xfer on DC2 */ #define RT_END_ON_CNT 0000010 /* Receive type: end xfer on count */ #define RT_END_ON_CHAR 0000004 /* Receive type: end xfer on character */ #define RT_ENAB_EDIT 0000002 /* Receive type: enable input editing */ #define RT_ENAB_ECHO 0000001 /* Receive type: enable input echoing */ #define FC_FORCE_XON 0000002 /* Flow control: force XON */ #define FC_XONXOFF 0000001 /* Flow control: enable XON/XOFF */ #define CL_GUARD 0000040 /* Connect line: guard tone off or on */ #define CL_STANDARD 0000020 /* Connect line: standard 212 or V.22 */ #define CL_BITS 0000010 /* Connect line: bits 10 or 9 */ #define CL_MODE 0000004 /* Connect line: mode originate or answer */ #define CL_DIAL 0000002 /* Connect line: dial manual or automatic */ #define CL_SPEED 0000001 /* Connect line: speed low or high */ #define DL_AUTO_ANSWER 0000001 /* Disconnect line: auto-answer enable or disable */ #define LB_SPEED 0000004 /* Loopback test: speed low or high */ #define LB_MODE 0000002 /* Loopback test: mode analog or digital */ #define LB_TEST 0000001 /* Loopback test: test disable or enable */ #define GS_NO_SYSMDM 0000200 /* Get status: systems modem present or absent */ #define GS_SYSMDM_TO 0000100 /* Get status: systems modem OK or timed out */ #define GS_NO_MODEM 0000040 /* Get status: modem present or absent */ #define GS_SPEED 0000020 /* Get status: speed low or high */ #define GS_LINE 0000001 /* Get status: line disconnected or connected */ /* Bit fields (name, starting bit, bit width) */ BITFIELD (CMD_OPCODE, 8, 8) /* Command: opcode */ BITFIELD (CMD_KEY, 0, 8) /* Command: key */ BITFIELD (SK_BPC, 14, 2) /* Set key: bits per character */ BITFIELD (SK_MODEM, 13, 1) /* Set key: hardwired or modem */ BITFIELD (SK_BRG, 12, 1) /* Set key: baud rate generator 0/1 */ BITFIELD (SK_STOPBITS, 10, 2) /* Set key: stop bits */ BITFIELD (SK_PARITY, 8, 2) /* Set key: parity select */ BITFIELD (SK_ENQACK, 7, 1) /* Set key: disable or enable ENQ/ACK */ BITFIELD (SK_BAUDRATE, 3, 4) /* Set key: port baud rate */ BITFIELD (SK_PORT, 0, 3) /* Set key: port number */ BITFIELD (FL_ALERT, 11, 1) /* Port flags: alert for terminate recv buffer */ BITFIELD (FL_XOFF, 10, 1) /* Port flags: XOFF stopped transmission */ BITFIELD (FL_BREAK, 9, 1) /* Port flags: UI / break detected */ BITFIELD (FL_HAVEBUF, 8, 1) /* Port flags: UI / read buffer available */ BITFIELD (FL_WANTBUF, 7, 1) /* Port flags: UI / write buffer available */ BITFIELD (FL_RDOVFLOW, 6, 1) /* Port flags: read buffers overflowed */ BITFIELD (FL_RDFILL, 5, 1) /* Port flags: read buffer is filling */ BITFIELD (FL_RDEMPT, 4, 1) /* Port flags: read buffer is emptying */ BITFIELD (FL_WRFILL, 3, 1) /* Port flags: write buffer is filling */ BITFIELD (FL_WREMPT, 2, 1) /* Port flags: write buffer is emptying */ BITFIELD (FL_WAITACK, 1, 1) /* Port flags: ENQ sent, waiting for ACK */ BITFIELD (FL_DO_ENQACK, 0, 1) /* Port flags: do ENQ/ACK handshake */ #define SK_BRG_1 SK_BRG #define SK_BRG_0 0 #define FL_RDFLAGS (FL_RDEMPT | FL_RDFILL | FL_RDOVFLOW) #define FL_WRFLAGS (FL_WREMPT | FL_WRFILL) #define FL_UI_PENDING (FL_WANTBUF | FL_HAVEBUF | FL_BREAK) #define ACK_LIMIT 1000 /* poll timeout for ACK response */ #define ENQ_LIMIT 80 /* output chars before ENQ */ /* Packed field values */ #define SK_BPC_5 (0 << SK_BPC_V) #define SK_BPC_6 (1 << SK_BPC_V) #define SK_BPC_7 (2 << SK_BPC_V) #define SK_BPC_8 (3 << SK_BPC_V) #define SK_STOP_1 (1 << SK_STOPBITS_V) #define SK_STOP_15 (2 << SK_STOPBITS_V) #define SK_STOP_2 (3 << SK_STOPBITS_V) #define SK_BAUD_NOCHG (0 << SK_BAUDRATE_V) #define SK_BAUD_50 (1 << SK_BAUDRATE_V) #define SK_BAUD_75 (2 << SK_BAUDRATE_V) #define SK_BAUD_110 (3 << SK_BAUDRATE_V) #define SK_BAUD_1345 (4 << SK_BAUDRATE_V) #define SK_BAUD_150 (5 << SK_BAUDRATE_V) #define SK_BAUD_300 (6 << SK_BAUDRATE_V) #define SK_BAUD_1200 (7 << SK_BAUDRATE_V) #define SK_BAUD_1800 (8 << SK_BAUDRATE_V) #define SK_BAUD_2400 (9 << SK_BAUDRATE_V) #define SK_BAUD_4800 (10 << SK_BAUDRATE_V) #define SK_BAUD_9600 (11 << SK_BAUDRATE_V) #define SK_BAUD_19200 (12 << SK_BAUDRATE_V) /* Default values */ #define SK_PWRUP_0 (SK_BPC_8 | SK_BRG_0 | SK_STOP_1 | SK_BAUD_9600) #define SK_PWRUP_1 (SK_BPC_8 | SK_BRG_1 | SK_STOP_1 | SK_BAUD_9600) #define RT_PWRUP (RT_END_ON_CR | RT_END_ON_CHAR | RT_ENAB_EDIT | RT_ENAB_ECHO) /* Command helpers */ #define GET_OPCODE(w) (((w) & CMD_OPCODE) >> CMD_OPCODE_V) #define GET_KEY(w) (((w) & CMD_KEY) >> CMD_KEY_V) #define GET_BPC(w) (((w) & SK_BPC) >> SK_BPC_V) #define GET_BAUDRATE(w) (((w) & SK_BAUDRATE) >> SK_BAUDRATE_V) #define GET_PORT(w) (((w) & SK_PORT) >> SK_PORT_V) #define GET_UIREASON(w) (((w) & UI_REASON) >> UI_REASON_V) #define GET_UIPORT(w) (((w) & UI_PORT) >> UI_PORT_V) /* Multiplexer controller state variables */ typedef enum { idle, cmd, param, exec } STATE; STATE mpx_state = idle; /* controller state */ uint16 mpx_ibuf = 0; /* status/data in */ uint16 mpx_obuf = 0; /* command/data out */ uint32 mpx_cmd = 0; /* current command */ uint32 mpx_param = 0; /* current parameter */ uint32 mpx_port = 0; /* current port number for R/W */ uint32 mpx_portkey = 0; /* current port's key */ int32 mpx_iolen = 0; /* length of current I/O xfer */ t_bool mpx_uien = FALSE; /* unsolicited interrupts enabled */ uint32 mpx_uicode = 0; /* unsolicited interrupt reason and port */ FLIP_FLOP mpx_control = CLEAR; /* control flip-flop */ FLIP_FLOP mpx_flag = CLEAR; /* flag flip-flop */ FLIP_FLOP mpx_flagbuf = CLEAR; /* flag buffer flip-flop */ /* Multiplexer per-line state variables */ uint8 mpx_key [MPX_PORTS]; /* port keys */ uint16 mpx_config [MPX_PORTS]; /* port configuration */ uint16 mpx_rcvtype [MPX_PORTS]; /* receive type */ uint16 mpx_charcnt [MPX_PORTS]; /* character count */ uint16 mpx_flowcntl [MPX_PORTS]; /* flow control */ uint8 mpx_enq_cntr [MPX_PORTS]; /* ENQ character counter */ uint16 mpx_ack_wait [MPX_PORTS]; /* ACK wait timer */ uint16 mpx_flags [MPX_PORTS]; /* line state flags */ /* Multiplexer buffer selectors */ typedef enum { ioread, iowrite } IO_OPER; /* I/O operation */ typedef enum { get, put } BUF_SELECT; /* buffer selector */ static const char *const io_op [] = { "read", /* operation names */ "write" }; static const uint32 buf_size [] = { RD_BUF_SIZE, /* buffer sizes */ WR_BUF_SIZE }; static uint32 emptying_flags [2]; /* buffer emptying flags [IO_OPER] */ static uint32 filling_flags [2]; /* buffer filling flags [IO_OPER] */ /* Multiplexer per-line buffer variables */ typedef uint16 BUF_INDEX [MPX_PORTS] [2]; /* buffer index (read and write) */ BUF_INDEX mpx_put; /* read/write buffer add index */ BUF_INDEX mpx_sep; /* read/write buffer separator index */ BUF_INDEX mpx_get; /* read/write buffer remove index */ uint8 mpx_rbuf [MPX_PORTS] [RD_BUF_SIZE]; /* read buffer */ uint8 mpx_wbuf [MPX_PORTS] [WR_BUF_SIZE]; /* write buffer */ /* Multiplexer local routines */ static t_bool exec_command (void); static void poll_connection (void); static void controller_reset (void); static uint32 service_time (uint16 control_word); static int32 key_to_port (uint32 key); static void buf_init (IO_OPER rw, uint32 port); static uint8 buf_get (IO_OPER rw, uint32 port); static void buf_put (IO_OPER rw, uint32 port, uint8 ch); static void buf_remove (IO_OPER rw, uint32 port); static void buf_term (IO_OPER rw, uint32 port, uint8 header); static void buf_free (IO_OPER rw, uint32 port); static void buf_cancel (IO_OPER rw, uint32 port, BUF_SELECT which); static uint32 buf_len (IO_OPER rw, uint32 port, BUF_SELECT which); static uint32 buf_avail (IO_OPER rw, uint32 port); /* Multiplexer global routines */ uint32 mpx_io (uint32 select_code, IOSIG signal, uint32 data); t_stat mpx_line_svc (UNIT *uptr); t_stat mpx_cntl_svc (UNIT *uptr); t_stat mpx_poll_svc (UNIT *uptr); t_stat mpx_reset (DEVICE *dptr); t_stat mpx_attach (UNIT *uptr, char *cptr); t_stat mpx_detach (UNIT *uptr); t_stat mpx_status (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat mpx_set_frev (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc); /* MPX data structures. mpx_order MPX line connection order table mpx_ldsc MPX line descriptors mpx_desc MPX multiplexer descriptor mpx_dib MPX device information block mpx_unit MPX unit list mpx_reg MPX register list mpx_mod MPX modifier list mpx_deb MPX debug list mpx_dev MPX device descriptor The first eight units correspond to the eight multiplexer line ports. These handle character I/O via the Telnet library. A ninth unit acts as the card controller, executing commands and transferring data to and from the I/O buffers. A tenth unit is responsible for polling for connections and socket I/O. It also holds the master socket. The character I/O service routines run only when there are characters to read or write. They operate at the approximate baud rates of the terminals (in CPU instructions per second) in order to be compatible with the OS drivers. The controller service routine runs only when a command is executing or a data transfer to or from the CPU is in progress. The Telnet poll must run continuously, but it may operate much more slowly, as the only requirement is that it must not present a perceptible lag to human input. To be compatible with CPU idling, it is co-scheduled with the master poll timer, which uses a ten millisecond period. The controller and poll units are hidden by disabling them, so as to present a logical picture of the multiplexer to the user. However, we cannot attach to a disabled unit, so the poll unit is enabled prior to attaching and disabled thereafter. */ int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */ TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */ TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order }; /* device descriptor */ DIB mpx_dib = { MPX, &mpx_io }; DEVICE mpx_dev; UNIT mpx_unit [] = { { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 0 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 1 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 2 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 3 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 4 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 5 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 6 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 7 */ { UDATA (&mpx_cntl_svc, UNIT_DIS, 0) }, /* controller unit */ { UDATA (&mpx_poll_svc, UNIT_ATTABLE | UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */ }; REG mpx_reg [] = { { DRDATA (STATE, mpx_state, 3) }, { ORDATA (IBUF, mpx_ibuf, 16), REG_FIT }, { ORDATA (OBUF, mpx_obuf, 16), REG_FIT }, { ORDATA (CMD, mpx_cmd, 8) }, { ORDATA (PARAM, mpx_param, 16) }, { DRDATA (PORT, mpx_port, 8), PV_LEFT }, { DRDATA (PORTKEY, mpx_portkey, 8), PV_LEFT }, { DRDATA (IOLEN, mpx_iolen, 16), PV_LEFT }, { FLDATA (UIEN, mpx_uien, 0) }, { GRDATA (UIPORT, mpx_uicode, 10, 3, 0) }, { GRDATA (UICODE, mpx_uicode, 10, 3, UI_REASON_V) }, { BRDATA (KEYS, mpx_key, 10, 8, MPX_PORTS) }, { BRDATA (PCONFIG, mpx_config, 8, 16, MPX_PORTS) }, { BRDATA (RCVTYPE, mpx_rcvtype, 8, 16, MPX_PORTS) }, { BRDATA (CHARCNT, mpx_charcnt, 8, 16, MPX_PORTS) }, { BRDATA (FLOWCNTL, mpx_flowcntl, 8, 16, MPX_PORTS) }, { BRDATA (ENQCNTR, mpx_enq_cntr, 10, 7, MPX_PORTS) }, { BRDATA (ACKWAIT, mpx_ack_wait, 10, 10, MPX_PORTS) }, { BRDATA (PFLAGS, mpx_flags, 2, 12, MPX_PORTS) }, { BRDATA (RBUF, mpx_rbuf, 8, 8, MPX_PORTS * RD_BUF_SIZE) }, { BRDATA (WBUF, mpx_wbuf, 8, 8, MPX_PORTS * WR_BUF_SIZE) }, { BRDATA (GET, mpx_get, 10, 10, MPX_PORTS * 2) }, { BRDATA (SEP, mpx_sep, 10, 10, MPX_PORTS * 2) }, { BRDATA (PUT, mpx_put, 10, 10, MPX_PORTS * 2) }, { FLDATA (CTL, mpx_control, 0) }, { FLDATA (FLG, mpx_flag, 0) }, { FLDATA (FBF, mpx_flagbuf, 0) }, { ORDATA (DEVNO, mpx_dib.devno, 6), REG_HRO }, { BRDATA (CONNORD, mpx_order, 10, 32, MPX_PORTS), REG_HRO }, { NULL } }; MTAB mpx_mod [] = { { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "REV", NULL, &mpx_set_frev, &mpx_show_frev, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LINEORDER", "LINEORDER", &tmxr_set_lnorder, &tmxr_show_lnorder, &mpx_desc }, { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &mpx_desc }, { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &mpx_desc }, { MTAB_XTD | MTAB_VDV, 0, "", NULL, NULL, &mpx_status, &mpx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, &mpx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &mpx_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mpx_desc }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mpx_dev }, { 0 } }; DEBTAB mpx_deb [] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "BUF", DEB_BUF }, { "XFER", DEB_XFER }, { NULL, 0 } }; DEVICE mpx_dev = { "MPX", /* device name */ mpx_unit, /* unit array */ mpx_reg, /* register array */ mpx_mod, /* modifier array */ MPX_PORTS + MPX_CNTLS, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ &tmxr_ex, /* examine routine */ &tmxr_dep, /* deposit routine */ &mpx_reset, /* reset routine */ NULL, /* boot routine */ &mpx_attach, /* attach routine */ &mpx_detach, /* detach routine */ &mpx_dib, /* device information block */ DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ 0, /* debug control flags */ mpx_deb, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* I/O signal handler. Commands are sent to the card via an OTA/B. Issuing an STC SC,C causes the mux to accept the word (STC causes a NMI on the card). If the command uses one word, command execution will commence, and the flag will set on completion. If the command uses two words, the flag will be set, indicating that the second word should be output via an OTA/B. Command execution will commence upon receipt, and the flag will set on completion. When the flag sets for command completion, status or data may be read from the card via an LIA/B. If additional status or data words are expected, the flag will set when they are available. A command consists of an opcode in the high byte, and a port key or command parameter in the low byte. Undefined commands are treated as NOPs. The card firmware executes commands as part of a twelve-event round-robin scheduling poll. The card NMI service routine simply sets a flag that is interrogated during polling. The poll sequence is advanced after each command. This implies that successive commands incur a delay of at least one poll-loop's execution time. On an otherwise quiescent card, this delay is approximately 460 Z80 instructions, or about 950 usec. The average command initiation time is half of that, or roughly 425 usec. If a detected command requires a second word, the card sits in a tight loop, waiting for the OTx that indicates that the parameter is available. Command initiation from parameter receipt is about 25 usec. For reads and writes to card buffers, the on-board DMA controller is used. The CPU uses DCPC to handle the transfer, but the data transfer time is limited by the Z80 DMA, which can process a word in about 1.25 usec. For most cards, the hardware POPIO signal sets the flag buffer and flag flip-flops, while CRS clears the control flip-flop. For this card, the control and flags are cleared together by CRS, and POPIO is not used. Implementation notes: 1. "Enable unsolicited input" is the only command that does not set the device flag upon completion. Therefore, the CPU has no way of knowing when the command has completed. Because the command in the input latch is recorded in the NMI handler, but actual execution only begins when the scheduler polls for the command indication, it is possible for another command to be sent to the card before the "Enable unsolicited input" command is recognized. In this case, the second command overwrites the first and is executed by the scheduler poll. Under simulation, this condition occurs when the OTx and STC processors are entered with mpx_state = cmd. 2. The "Fast binary read" command inhibits all other commands until the card is reset. 3. The card does not respond to POPIO. However, as PRESET asserts POPIO and CRS together, we fall into the latter from the former. */ uint32 mpx_io (uint32 select_code, IOSIG signal, uint32 data) { static const char *output_state [] = { "Command", "Command override", "Parameter", "Data" }; static const char *input_state [] = { "Status", "Invalid status", "Parameter", "Data" }; const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 delay; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ mpx_flag = mpx_flagbuf = CLEAR; /* clear flag and flag buffer */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fputs (">>MPX cmds: [CLF] Flag cleared\n", sim_deb); break; case ioSTF: /* set flag flip-flop */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fputs (">>MPX cmds: [STF] Flag set\n", sim_deb); /* fall into ENF */ case ioENF: /* enable flag */ mpx_flag = mpx_flagbuf = SET; /* set flag and flag buffer */ break; case ioSFC: /* skip if flag is clear */ setstdSKF (mpx); break; case ioSFS: /* skip if flag is set */ setstdSKF (mpx); break; case ioIOI: /* I/O data input */ data = mpx_ibuf; /* return info */ if (DEBUG_PRI (mpx_dev, DEB_CPU)) fprintf (sim_deb, ">>MPX cpu: [LIx%s] %s = %06o\n", hold_or_clear, input_state [mpx_state], data); if (mpx_state == exec) /* if this is input data word */ sim_activate (&mpx_cntl, DATA_DELAY); /* continue transmission */ break; case ioIOO: /* I/O data output */ if (DEBUG_PRI (mpx_dev, DEB_CPU)) fprintf (sim_deb, ">>MPX cpu: [OTx%s] %s = %06o\n", hold_or_clear, output_state [mpx_state], data); mpx_obuf = data; /* save word */ if (mpx_state == param) { /* if this is parameter word */ sim_activate (&mpx_cntl, CMD_DELAY); /* do command now */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: [OTx%s] Command %03o parameter %06o scheduled, " "time = %d\n", hold_or_clear, mpx_cmd, mpx_obuf, CMD_DELAY); } else if (mpx_state == exec) /* else if this is output data word */ sim_activate (&mpx_cntl, DATA_DELAY); /* then do transmission */ break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ controller_reset (); /* reset firmware to power-on defaults */ mpx_obuf = 0; /* clear output buffer */ mpx_control = CLEAR; /* clear control */ mpx_flagbuf = CLEAR; /* clear flag buffer */ mpx_flag = CLEAR; /* clear flag */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fputs (">>MPX cmds: [CRS] Controller reset\n", sim_deb); break; case ioCLC: /* clear control flip-flop */ mpx_control = CLEAR; /* clear control */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: [CLC%s] Control cleared\n", hold_or_clear); break; case ioSTC: /* set control flip-flop */ mpx_control = SET; /* set control */ if (mpx_cmd == CMD_BINARY_READ) /* executing fast binary read? */ break; /* further command execution inhibited */ mpx_cmd = GET_OPCODE (mpx_obuf); /* get command opcode */ mpx_portkey = GET_KEY (mpx_obuf); /* get port key */ if (mpx_state == cmd) /* already scheduled? */ sim_cancel (&mpx_cntl); /* cancel to get full delay */ mpx_state = cmd; /* set command state */ if (mpx_cmd & CMD_TWO_WORDS) /* two-word command? */ delay = PARAM_DELAY; /* specify parameter wait */ else /* one-word command */ delay = CMD_DELAY; /* specify command wait */ sim_activate (&mpx_cntl, delay); /* schedule command */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: [STC%s] Command %03o key %d scheduled, " "time = %d\n", hold_or_clear, mpx_cmd, mpx_portkey, delay); break; case ioEDT: /* end data transfer */ if (DEBUG_PRI (mpx_dev, DEB_CPU)) fputs (">>MPX cpu: [EDT] DCPC transfer ended\n", sim_deb); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, mpx); /* set standard PRL signal */ setstdIRQ (select_code, mpx); /* set standard IRQ signal */ setstdSRQ (select_code, mpx); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ mpx_flagbuf = CLEAR; /* clear flag buffer */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ mpx_io (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ mpx_io (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command executor. We are called by the controller service routine to process one- and two-word commands. For two-word commands, the parameter word is present in mpx_param. The return value indicates whether the card flag should be set upon completion. Most commands execute and complete directly. The read and write commands, however, transition to the execution state to simulate the DMA transfer, and the "Download executable" command does the same to receive the download from the CPU. Several commands were added for the B firmware revision, and the various revisions of the RTE drivers sent some commands that were never implemented in the mux firmware. The command protocol treated unknown commands as NOPs, meaning that the command (and parameter, if it was a two-word command) was absorbed and the card flag was set as though the command completed normally. This allowed interoperability between firmware and driver revisions. Commands that refer to ports do so indirectly by passing a port key, rather than a port number. The key-to-port translation is established by the "Set port key" command. If a key is not found in the table, the command is not executed, and the status return is ST_BAD_KEY, which in hex is "BAD0". Implementation notes: 1. The "Reset to power-on defaults" command causes the firmware to disable interrupts and jump to the power-on initialization routine, exactly as though the Z80 had received a hardware reset. 2. The "Abort DMA transfer" command works because STC causes NMI, so the command is executed even in the middle of a DMA transfer. The OTx of the command will be sent to the buffer if a "Write data to buffer" command is in progress, but the STC will cause this routine to be called, which will cancel the buffer and return the controller to the idle state. Note that this command might be sent with no transfer in progress, in which case nothing is done. 3. In response to an "Enable unsolicited interrupts" command, the controller service is scheduled to check for a pending UI. If one is found, the first UI status word is placed in the input buffer, and an interrupt is generated by setting the flag. This causes entry to the driver, which issues an "Acknowledge" command to obtain the second status word. It is possible, however, for the interrupt to be ignored. For example, the driver may be waiting for a "write buffer available" UI when it is called to begin a write to a different port. If the flag is set by the UI after RTE has been entered, the interrupt will be held off, and the STC sc,C instruction that begins the command sequence will clear the flag, removing the interrupt entirely. In this case, the controller will reissue the UI when the next "Enable unsolicited interrupts" command is sent. Note that the firmware reissues the same UI, rather than recomputing UIs and potentially selecting a different one of higher priority. 4. The "Fast binary read" command apparently was intended to facilitate booting from a 264x tape drive, although no boot loader ROM for the multiplexer was ever released. It sends the fast binary read escape sequence (ESC e) to the terminal and then packs each pair of characters received into a word and sends it to the CPU, accompanied by the device flag. The multiplexer firmware disables interrupts and then manipulates the SIO for port 0 directly. Significantly, it does no interpretation of the incoming data and sits in an endless I/O loop, so the only way to exit the command is to reset the card with a CRS (front panel PRESET or CLC 0 instruction execution). Sending a command will not work; although the NMI will interrupt the fast binary read, the NMI handler simply sets a flag that is tested by the scheduler poll. Because the processor is in an endless loop, control never returns to the scheduler, so the command is never seen. 5. The "Terminate active receive buffer" behavior is a bit tricky. If the read buffer has characters, the buffer is terminated as though a "terminate on count" condition occurred. If the buffer is empty, however, a "terminate on count = 1" condition is established. When a character is received, the buffer is terminated, and the buffer termination count is reset to 254. */ static t_bool exec_command (void) { int32 port; uint32 svc_time; t_bool set_flag = TRUE; /* flag is normally set on completion */ STATE next_state = idle; /* command normally executes to completion */ mpx_ibuf = ST_OK; /* return status is normally OK */ switch (mpx_cmd) { case CMD_NOP: /* no operation */ break; /* just ignore */ case CMD_RESET: /* reset firmware */ controller_reset (); /* reset program variables */ mpx_ibuf = ST_TEST_OK; /* return self-test OK code */ break; case CMD_ENABLE_UI: mpx_uien = TRUE; /* enable unsolicited interrupts */ sim_activate (&mpx_cntl, CMD_DELAY); /* and schedule controller for UI check */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Controller status check scheduled, " "time = %d\n", CMD_DELAY); set_flag = FALSE; /* do not set the flag at completion */ break; case CMD_DISABLE: switch (mpx_portkey) { case SUBCMD_UI: mpx_uien = FALSE; /* disable unsolicited interrupts */ break; case SUBCMD_DMA: if (mpx_flags [mpx_port] & FL_WRFILL) /* write buffer xfer in progress? */ buf_cancel (iowrite, mpx_port, put); /* cancel it */ else if (mpx_flags [mpx_port] & FL_RDEMPT) /* read buffer xfer in progress? */ buf_cancel (ioread, mpx_port, get); /* cancel it */ break; } break; case CMD_ACK: /* acknowledge unsolicited interrupt */ switch (mpx_uicode & UI_REASON) { case UI_WRBUF_AVAIL: /* write buffer notification */ mpx_flags [mpx_port] &= ~FL_WANTBUF; /* clear flag */ mpx_ibuf = WR_BUF_LIMIT; /* report write buffer available */ break; case UI_RDBUF_AVAIL: /* read buffer notification */ mpx_flags [mpx_port] &= ~FL_HAVEBUF; /* clear flag */ mpx_ibuf = buf_get (ioread, mpx_port) << 8 | /* get header value and position */ buf_len (ioread, mpx_port, get); /* and include buffer length */ if (mpx_flags [mpx_port] & FL_RDOVFLOW) { /* did a buffer overflow? */ mpx_ibuf = mpx_ibuf | RS_OVERFLOW; /* report it */ mpx_flags [mpx_port] &= ~FL_RDOVFLOW; /* clear overflow flag */ } break; case UI_BRK_RECD: /* break received */ mpx_flags [mpx_port] &= ~FL_BREAK; /* clear flag */ mpx_ibuf = 0; /* 2nd word is zero */ break; } mpx_uicode = 0; /* clear notification code */ break; case CMD_CANCEL: /* cancel first read buffer */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ buf_cancel (ioread, port, get); /* cancel get buffer */ if ((buf_avail (ioread, port) == 1) && /* one buffer remaining? */ !(mpx_flags [port] & FL_RDFILL)) /* and not filling it? */ mpx_flags [port] |= FL_HAVEBUF; /* indicate buffer availability */ break; case CMD_CANCEL_ALL: /* cancel all read buffers */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ buf_init (ioread, port); /* reinitialize read buffers */ break; case CMD_BINARY_READ: /* fast binary read */ for (port = 0; port < MPX_PORTS; port++) sim_cancel (&mpx_unit [port]); /* cancel I/O on all lines */ mpx_flags [0] = 0; /* clear port 0 state flags */ mpx_enq_cntr [0] = 0; /* clear port 0 ENQ counter */ mpx_ack_wait [0] = 0; /* clear port 0 ACK wait timer */ tmxr_putc_ln (&mpx_ldsc [0], ESC); /* send fast binary read */ tmxr_putc_ln (&mpx_ldsc [0], 'e'); /* escape sequence to port 0 */ tmxr_poll_tx (&mpx_desc); /* flush output */ next_state = exec; /* set execution state */ break; case CMD_REQ_WRITE: /* request write buffer */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ if (buf_avail (iowrite, port) > 0) /* is a buffer available? */ mpx_ibuf = WR_BUF_LIMIT; /* report write buffer limit */ else { mpx_ibuf = 0; /* report none available */ mpx_flags [port] |= FL_WANTBUF; /* set buffer request */ } break; case CMD_WRITE: /* write to buffer */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) { /* port defined? */ mpx_port = port; /* save port number */ mpx_iolen = mpx_param & WR_LENGTH; /* save request length */ next_state = exec; /* set execution state */ } break; case CMD_SET_KEY: /* set port key and configuration */ port = GET_PORT (mpx_param); /* get target port number */ mpx_key [port] = (uint8) mpx_portkey; /* set port key */ mpx_config [port] = mpx_param; /* set port configuration word */ svc_time = service_time (mpx_param); /* get service time for baud rate */ if (svc_time) /* want to change? */ mpx_unit [port].wait = svc_time; /* set service time */ mpx_ibuf = MPX_DATE_CODE; /* return firmware date code */ break; case CMD_SET_RCV: /* set receive type */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ mpx_rcvtype [port] = mpx_param; /* save port receive type */ break; case CMD_SET_COUNT: /* set character count */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ mpx_charcnt [port] = mpx_param; /* save port character count */ break; case CMD_SET_FLOW: /* set flow control */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ mpx_flowcntl [port] = mpx_param & FC_XONXOFF; /* save port flow control */ if (mpx_param & FC_FORCE_XON) /* force XON? */ mpx_flags [port] &= ~FL_XOFF; /* resume transmission if suspended */ break; case CMD_READ: /* read from buffer */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) { /* port defined? */ mpx_port = port; /* save port number */ mpx_iolen = mpx_param; /* save request length */ sim_activate (&mpx_cntl, DATA_DELAY); /* schedule the transfer */ next_state = exec; /* set execution state */ set_flag = FALSE; /* no flag until word ready */ } break; case CMD_DL_EXEC: /* Download executable */ mpx_iolen = mpx_param; /* save request length */ next_state = exec; /* set execution state */ break; case CMD_CN_LINE: /* connect modem line */ case CMD_DC_LINE: /* disconnect modem line */ case CMD_LOOPBACK: /* enable/disable modem loopback */ mpx_ibuf = ST_NO_MODEM; /* report "no modem installed" */ break; case CMD_GET_STATUS: /* get modem status */ mpx_ibuf = ST_NO_SYSMDM; /* report "no systems modem card" */ break; case CMD_TERM_BUF: /* terminate active receive buffer */ port = key_to_port (mpx_portkey); /* get port */ if (port >= 0) /* port defined? */ if (buf_len (ioread, port, put) > 0) { /* any chars in buffer? */ buf_term (ioread, port, 0); /* terminate buffer and set header */ if (buf_avail (ioread, port) == 1) /* first read buffer? */ mpx_flags [port] |= FL_HAVEBUF; /* indicate availability */ } else { /* buffer is empty */ mpx_charcnt [port] = 1; /* set to terminate on one char */ mpx_flags [port] |= FL_ALERT; /* set alert flag */ } break; case CMD_VCP_PUT: /* VCP put byte */ case CMD_VCP_PUT_BUF: /* VCP put buffer */ case CMD_VCP_GET: /* VCP get byte */ case CMD_VCP_GET_BUF: /* VCP get buffer */ case CMD_VCP_EXIT: /* Exit VCP mode */ case CMD_VCP_ENTER: /* Enter VCP mode */ default: /* unknown command */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Unknown command %03o ignored\n", mpx_cmd); } mpx_state = next_state; return set_flag; } /* Multiplexer controller service. The controller service handles commands and data transfers to and from the CPU. The delay in scheduling the controller service represents the firmware command or data execution time. The controller may be in one of four states upon entry: idle, first word of command received (cmd), command parameter received (param), or data transfer (exec). Entry in the command state causes execution of one-word commands and solicitation of command parameters for two-word commands, which are executed when entering in the parameter state. Entry in the data transfer state moves one word between the CPU and a read or write buffer. For writes, the write buffer is filled with words from the CPU. Once the indicated number of words have been transferred, the appropriate line service is scheduled to send the characters. For reads, characters are unloaded from the read buffer to the CPU; an odd-length transfer is padded with a blank. A read of fewer characters than are present in the buffer will return the remaining characters when the next read is performed. Each read or write is terminated by the CPU sending one additional word (the RTE drivers send -1). The command completes when this word is acknowledged by the card setting the device flag. For zero-length writes, this additional word will be the only word sent. Data transfer is also used by the "Download executable" command to absorb the downloaded program. The firmware jumps to location 5100 hex in the downloaded program upon completion of reception. It is the responsibility of the program to return to the multiplexer firmware and to return to the CPU whatever status is appropriate when it is done. Under simulation, we simply "sink" the program and return status compatible with the multiplexer diagnostic program to simulate a passing test. Entry in the idle state checks for unsolicited interrupts. UIs are sent to the host when the controller is idle, UIs have been enabled, and a UI condition exists. If a UI is not acknowledged, it will remain pending and will be reissued the next time the controller is idle and UIs have been enabled. UI conditions are kept in the per-port flags. The UI conditions are write buffer available, read buffer available, break received, modem line connected, and modem line disconnected. The latter two conditions are not implemented in this simulation. If a break condition occurs at the same time as a read buffer completion, the break has priority; the buffer UI will occur after the break UI is acknowledged. The firmware checks for UI condition flags as part of the scheduler polling loop. Under simulation, though, UIs can occur only in two places: the point of origin (e.g., termination of a read buffer), or the "Enable unsolicited input" command executor. UIs will be generated at the point of origin only if the simulator is idle. If the simulator is not idle, it is assumed that UIs have been disabled to execute the current command and will be reenabled when the command sequence is complete. When the multiplexer is reset, and before the port keys are set, all ports enter "echoplex" mode. In this mode, characters received are echoed back as a functional test. Each port terminates buffers on CR reception. We detect this condition, cancel the buffer, and discard the buffer termination UI. Implementation notes: 1. The firmware transfers the full amount requested by the CPU, even if the transfer is longer than the buffer. Also, zero-length transfers program the card DMA chip to transfer 0 bytes; this results in a transfer of 217 bytes, per the Zilog databook. Under simulation, writes beyond the buffer are accepted from the CPU but discarded, and reads beyond the buffer return blanks. 2. We should never return from this routine in the "cmd" state, so debugging will report "internal error!" if we do. */ t_stat mpx_cntl_svc (UNIT *uptr) { uint8 ch; uint32 i; t_bool add_crlf; t_bool set_flag = TRUE; STATE last_state = mpx_state; static const char *cmd_state [] = { "complete", "internal error!", "waiting for parameter", "executing" }; switch (mpx_state) { /* dispatch on current state */ case idle: /* controller idle */ set_flag = FALSE; /* assume no UI */ if (mpx_uicode) { /* unacknowledged UI? */ if (mpx_uien == TRUE) { /* interrupts enabled? */ mpx_port = GET_UIPORT (mpx_uicode); /* get port number */ mpx_portkey = mpx_key [mpx_port]; /* get port key */ mpx_ibuf = mpx_uicode & UI_REASON | mpx_portkey; /* report UI reason and port key */ set_flag = TRUE; /* reissue host interrupt */ mpx_uien = FALSE; /* disable UI */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Port %d key %d unsolicited interrupt reissued, " "reason = %d\n", mpx_port, mpx_portkey, GET_UIREASON (mpx_uicode)); } } else { /* no unacknowledged UI */ for (i = 0; i < MPX_PORTS; i++) { /* check all ports for UIs */ if (mpx_flags [i] & FL_UI_PENDING) { /* pending UI? */ mpx_portkey = mpx_key [i]; /* get port key */ if (mpx_portkey == KEY_DEFAULT) { /* key defined? */ if (mpx_flags [i] & FL_HAVEBUF) /* no, is this read buffer avail? */ buf_cancel (ioread, i, get); /* cancel buffer */ mpx_flags [i] &= ~FL_UI_PENDING; /* cancel pending UI */ } else if (mpx_uien == TRUE) { /* interrupts enabled? */ if ((mpx_flags [i] & FL_WANTBUF) && /* port wants a write buffer? */ (buf_avail (iowrite, i) > 0)) /* and one is available? */ mpx_uicode = UI_WRBUF_AVAIL; /* set UI reason */ else if (mpx_flags [i] & FL_BREAK) /* received a line BREAK? */ mpx_uicode = UI_BRK_RECD; /* set UI reason */ else if (mpx_flags [i] & FL_HAVEBUF) /* have a read buffer ready? */ mpx_uicode = UI_RDBUF_AVAIL; /* set UI reason */ if (mpx_uicode) { /* UI to send? */ mpx_port = i; /* set port number for Acknowledge */ mpx_ibuf = mpx_uicode | mpx_portkey; /* merge UI reason and port key */ mpx_uicode = mpx_uicode | mpx_port; /* save UI reason and port */ set_flag = TRUE; /* interrupt host */ mpx_uien = FALSE; /* disable UI */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Port %d key %d unsolicited interrupt generated, " "reason = %d\n", i, mpx_portkey, GET_UIREASON (mpx_uicode)); break; /* quit after first UI */ } } } } } break; case cmd: /* command state */ if (mpx_cmd & CMD_TWO_WORDS) /* two-word command? */ mpx_state = param; /* look for parameter before executing */ else set_flag = exec_command (); /* execute one-word command */ break; case param: /* parameter get state */ mpx_param = mpx_obuf; /* save parameter */ set_flag = exec_command (); /* execute two-word command */ break; case exec: /* execution state */ switch (mpx_cmd) { case CMD_BINARY_READ: /* fast binary read */ mpx_flags [0] &= ~FL_HAVEBUF; /* data word was picked up by CPU */ set_flag = FALSE; /* suppress device flag */ break; case CMD_WRITE: /* transfer data to buffer */ if (mpx_iolen <= 0) { /* last (or only) entry? */ mpx_state = idle; /* idle controller */ if (mpx_iolen < 0) /* tie-off for buffer complete? */ break; /* we're done */ } add_crlf = ((mpx_param & /* CRLF should be added */ (WR_ADD_CRLF | WR_PARTIAL)) == WR_ADD_CRLF); for (i = 0; i < 2; i++) /* output one or two chars */ if (mpx_iolen > 0) { /* more to do? */ if (i) /* high or low byte? */ ch = (uint8) (mpx_obuf & 0377); /* low byte */ else ch = mpx_obuf >> 8; /* high byte */ if ((mpx_iolen == 1) && /* final char? */ (ch == '_') && add_crlf) { /* underscore and asking for CRLF? */ add_crlf = FALSE; /* suppress CRLF */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d character '_' " "suppressed CR/LF\n", mpx_port); } else if (buf_len (iowrite, mpx_port, put) < WR_BUF_LIMIT) buf_put (iowrite, mpx_port, ch); /* add char to buffer if space avail */ mpx_iolen = mpx_iolen - 1; /* drop remaining count */ } if (mpx_iolen == 0) { /* buffer done? */ if (add_crlf) { /* want CRLF? */ buf_put (iowrite, mpx_port, CR); /* add CR to buffer */ buf_put (iowrite, mpx_port, LF); /* add LF to buffer */ } buf_term (iowrite, mpx_port, mpx_param >> 8); /* terminate buffer */ mpx_iolen = -1; /* mark as done */ } if (DEBUG_PRI (mpx_dev, DEB_CMDS) && (sim_is_active (&mpx_unit [mpx_port]) == 0)) fprintf (sim_deb, ">>MPX cmds: Port %d service scheduled, " "time = %d\n", mpx_port, mpx_unit [mpx_port].wait); sim_activate (&mpx_unit [mpx_port], /* start line service */ mpx_unit [mpx_port].wait); break; case CMD_READ: /* transfer data from buffer */ if (mpx_iolen < 0) { /* input complete? */ if (mpx_obuf == 0177777) { /* "tie-off" word received? */ if (buf_len (ioread, mpx_port, get) == 0) { /* buffer now empty? */ buf_free (ioread, mpx_port); /* free buffer */ if (buf_avail (ioread, mpx_port) == 1) /* another buffer available? */ mpx_flags [mpx_port] |= FL_HAVEBUF; /* indicate availability */ } mpx_state = idle; /* idle controller */ } else set_flag = FALSE; /* ignore word */ break; } for (i = 0; i < 2; i++) /* input one or two chars */ if (mpx_iolen > 0) { /* more to transfer? */ if (buf_len (ioread, mpx_port, get) > 0) /* more chars available? */ ch = buf_get (ioread, mpx_port); /* get char from buffer */ else /* buffer exhausted */ ch = ' '; /* pad with blank */ if (i) /* high or low byte? */ mpx_ibuf = mpx_ibuf | ch; /* low byte */ else mpx_ibuf = ch << 8; /* high byte */ mpx_iolen = mpx_iolen - 1; /* drop count */ } else /* odd number of chars */ mpx_ibuf = mpx_ibuf | ' '; /* pad last with blank */ if (mpx_iolen == 0) /* end of host xfer? */ mpx_iolen = -1; /* mark as done */ break; case CMD_DL_EXEC: /* sink data from host */ if (mpx_iolen <= 0) { /* final entry? */ mpx_state = idle; /* idle controller */ mpx_ibuf = ST_DIAG_OK; /* return diag passed status */ } else { if (mpx_iolen > 0) /* more from host? */ mpx_iolen = mpx_iolen - 2; /* sink two bytes */ if (mpx_iolen <= 0) /* finished download? */ sim_activate (&mpx_cntl, CMD_DELAY); /* schedule completion */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Download completion scheduled, " "time = %d\n", CMD_DELAY); } break; default: /* no other entries allowed */ return SCPE_IERR; /* simulator error! */ } break; } if (DEBUG_PRI (mpx_dev, DEB_CMDS) && /* debug print? */ (last_state != mpx_state)) { /* and state change? */ fprintf (sim_deb, ">>MPX cmds: Command %03o ", mpx_cmd); if ((mpx_cmd & CMD_TWO_WORDS) && (mpx_state != param)) fprintf (sim_deb, "parameter %06o ", mpx_param); fputs (cmd_state [mpx_state], sim_deb); fputc ('\n', sim_deb); } if (set_flag) { mpx_io (mpx_dib.devno, ioENF, 0); /* set device flag */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fputs (">>MPX cmds: Flag set\n", sim_deb); } return SCPE_OK; } /* Multiplexer line service. The line service routine is used to transmit and receive characters. It is started when a buffer is ready for output or when the Telnet poll routine determines that there are characters ready for input, and it is stopped when there are no more characters to output or input. When a line is quiescent, this routine does not run. Service times are selected to approximate the baud rate setting of the multiplexer port. "Fast timing" mode enables three optimizations. First, buffered characters are transferred via Telnet in blocks, rather than a character at a time; this reduces network traffic and decreases simulator overhead (there is only one service routine entry per block, rather than one per character). Second, ENQ/ACK handshaking is done locally, without involving the Telnet client. Third, when editing and echo is enabled, entering BS echoes a backspace, a space, and a backspace, and entering DEL echoes a backslash, a carriage return, and a line feed, providing better compatibility with prior RTE terminal drivers. Each read and write buffer begins with a reserved header byte that stores per-buffer information, such as whether handshaking should be suppressed during output, or the specific cause of termination for input. Buffer termination sets the header byte with the appropriate flags. For output, a character counter is maintained and is incremented if ENQ/ACK handshaking is enabled for the current port and request. If the counter limit is reached, an ENQ is sent, and a flag is set to suspend transmission until an ACK is received. If the last character of the buffer is sent, the write buffer is freed, and a UI check is made if the controller is idle, in case a write buffer request is pending. For input, the character is retrieved from the Telnet buffer. If a BREAK was received, break status is set, and the character is discarded (the current multiplexer library implementation always returns a NUL with a BREAK indication). If the character is an XOFF, and XON/XOFF pacing is enabled, a flag is set, and transmission is suspended until a corresponding XON is received. If the character is an ACK and is in response to a previously sent ENQ, it is discarded, and transmission is reenabled. If editing is enabled, a BS will delete the last character in the read buffer, and a DEL will delete the entire buffer. Otherwise, buffer termination conditions are checked (end on character, end on count, or buffer full), and if observed, the read buffer is terminated, and a read buffer available UI condition is signalled. Implementation notes: 1. The firmware echoes an entered BS before checking the buffer count to see if there are any characters to delete. Under simulation, we only echo if the buffer is not empty. 2. The "Fast binary read" command inhibits the normal transmit and receive processing. Instead, a pair of characters are sought on line 0 to fill the input buffer. When they are received, the device flag is set. The CPU will do a LIx sc,C to retrieve the data and reset the flag. */ t_stat mpx_line_svc (UNIT *uptr) { const uint32 port = uptr - mpx_unit; /* port number */ const uint16 rt = mpx_rcvtype [port]; /* receive type for port */ const uint32 data_bits = 5 + GET_BPC (mpx_config [port]); /* number of data bits */ const uint32 data_mask = (1 << data_bits) - 1; /* mask for data bits */ const t_bool fast_timing = (uptr->flags & UNIT_FASTTIME) != 0; /* port is set for fast timing */ const t_bool fast_binary_read = (mpx_cmd == CMD_BINARY_READ); /* fast binary read in progress */ uint8 ch; int32 chx; uint16 read_length; t_stat status = SCPE_OK; t_bool recv_loop = !fast_binary_read; /* bypass if fast binary read */ t_bool xmit_loop = !(fast_binary_read || /* bypass if fast read or output suspended */ (mpx_flags [port] & (FL_WAITACK | FL_XOFF))); /* Transmission service */ while (xmit_loop && (buf_len (iowrite, port, get) > 0)) { /* character available to output? */ if ((mpx_flags [port] & FL_WREMPT) == 0) { /* has buffer started emptying? */ chx = buf_get (iowrite, port) << 8; /* get header value and position */ if (fast_timing || (chx & WR_NO_ENQACK) || /* do we want handshake? */ !(mpx_config [port] & SK_ENQACK)) /* and configured for handshake? */ mpx_flags [port] &= ~FL_DO_ENQACK; /* no, so clear flag */ else mpx_flags [port] |= FL_DO_ENQACK; /* yes, so set flag */ continue; /* "continue" for zero-length write */ } if (mpx_flags [port] & FL_DO_ENQACK) /* do handshake for this buffer? */ mpx_enq_cntr [port] = mpx_enq_cntr [port] + 1; /* bump character counter */ if (mpx_enq_cntr [port] > ENQ_LIMIT) { /* ready for ENQ? */ mpx_enq_cntr [port] = 0; /* clear ENQ counter */ mpx_ack_wait [port] = 0; /* clear ACK wait timer */ mpx_flags [port] |= FL_WAITACK; /* set wait for ACK */ ch = ENQ; status = tmxr_putc_ln (&mpx_ldsc [port], ch); /* transmit ENQ */ xmit_loop = FALSE; /* stop further transmission */ } else { /* not ready for ENQ */ ch = buf_get (iowrite, port) & data_mask; /* get char and mask to bit width */ status = tmxr_putc_ln (&mpx_ldsc [port], ch); /* transmit the character */ xmit_loop = (status == SCPE_OK) && fast_timing; /* continue transmission? */ } if ((status == SCPE_OK) && /* transmitted OK? */ DEBUG_PRI (mpx_dev, DEB_XFER)) fprintf (sim_deb, ">>MPX xfer: Port %d character %s transmitted\n", port, fmt_char (ch)); else xmit_loop = FALSE; if (buf_len (iowrite, port, get) == 0) { /* buffer complete? */ buf_free (iowrite, port); /* free buffer */ if (mpx_state == idle) /* controller idle? */ mpx_cntl_svc (&mpx_cntl); /* check for UI */ } } /* Reception service */ while (recv_loop && /* OK to process? */ (chx = tmxr_getc_ln (&mpx_ldsc [port]))) { /* and new char available? */ if (chx & SCPE_BREAK) { /* break detected? */ mpx_flags [port] |= FL_BREAK; /* set break status */ if (DEBUG_PRI (mpx_dev, DEB_XFER)) fputs (">>MPX xfer: Break detected\n", sim_deb); if (mpx_state == idle) /* controller idle? */ mpx_cntl_svc (&mpx_cntl); /* check for UI */ continue; /* discard NUL that accompanied BREAK */ } ch = chx & data_mask; /* mask to bits per char */ if ((ch == XOFF) && /* XOFF? */ (mpx_flowcntl [port] & FC_XONXOFF)) { /* and handshaking enabled? */ mpx_flags [port] |= FL_XOFF; /* suspend transmission */ if (DEBUG_PRI (mpx_dev, DEB_XFER)) fprintf (sim_deb, ">>MPX xfer: Port %d character XOFF " "suspends transmission\n", port); recv_loop = fast_timing; /* set to loop if fast mode */ continue; } else if ((ch == XON) && /* XON? */ (mpx_flags [port] & FL_XOFF)) { /* and currently suspended? */ mpx_flags [port] &= ~FL_XOFF; /* resume transmission */ if (DEBUG_PRI (mpx_dev, DEB_XFER)) fprintf (sim_deb, ">>MPX xfer: Port %d character XON " "resumes transmission\n", port); recv_loop = fast_timing; /* set to loop if fast mode */ continue; } if (DEBUG_PRI (mpx_dev, DEB_XFER)) fprintf (sim_deb, ">>MPX xfer: Port %d character %s received\n", port, fmt_char (ch)); if ((ch == ACK) && (mpx_flags [port] & FL_WAITACK)) { /* ACK and waiting for it? */ mpx_flags [port] = mpx_flags [port] & ~FL_WAITACK; /* clear wait flag */ recv_loop = FALSE; /* absorb character */ } else if ((buf_avail (ioread, port) == 0) && /* no free buffer available for char? */ !(mpx_flags [port] & FL_RDFILL)) { /* and not filling last buffer? */ mpx_flags [port] |= FL_RDOVFLOW; /* set buffer overflow flag */ recv_loop = fast_timing; /* continue loop if fast mode */ } else { /* buffer is available */ if (rt & RT_ENAB_EDIT) /* editing enabled? */ if (ch == BS) { /* backspace? */ if (buf_len (ioread, port, put) > 0) /* at least one character in buffer? */ buf_remove (ioread, port); /* remove last char */ if (rt & RT_ENAB_ECHO) { /* echo enabled? */ tmxr_putc_ln (&mpx_ldsc [port], BS); /* echo BS */ if (fast_timing) { /* fast timing mode? */ tmxr_putc_ln (&mpx_ldsc [port], ' '); /* echo space */ tmxr_putc_ln (&mpx_ldsc [port], BS); /* echo BS */ } } continue; } else if (ch == DEL) { /* delete line? */ buf_cancel (ioread, port, put); /* cancel put buffer */ if (rt & RT_ENAB_ECHO) { /* echo enabled? */ if (fast_timing) /* fast timing mode? */ tmxr_putc_ln (&mpx_ldsc [port], '\\'); /* echo backslash */ tmxr_putc_ln (&mpx_ldsc [port], CR); /* echo CR */ tmxr_putc_ln (&mpx_ldsc [port], LF); /* and LF */ } continue; } if (uptr->flags & UNIT_CAPSLOCK) /* caps lock mode? */ ch = toupper (ch); /* convert to upper case if lower */ if (rt & RT_ENAB_ECHO) /* echo enabled? */ tmxr_putc_ln (&mpx_ldsc [port], ch); /* echo the char */ if (rt & RT_END_ON_CHAR) { /* end on character? */ recv_loop = FALSE; /* assume termination */ if ((ch == CR) && (rt & RT_END_ON_CR)) { if (rt & RT_ENAB_ECHO) /* echo enabled? */ tmxr_putc_ln (&mpx_ldsc [port], LF); /* send LF */ mpx_param = RS_ETC_CR; /* set termination condition */ } else if ((ch == RS) && (rt & RT_END_ON_RS)) mpx_param = RS_ETC_RS; /* set termination condition */ else if ((ch == EOT) && (rt & RT_END_ON_EOT)) mpx_param = RS_ETC_EOT; /* set termination condition */ else if ((ch == DC2) && (rt & RT_END_ON_DC2)) mpx_param = RS_ETC_DC2; /* set termination condition */ else recv_loop = TRUE; /* no termination */ } if (recv_loop) /* no termination condition? */ buf_put (ioread, port, ch); /* put character in buffer */ read_length = buf_len (ioread, port, put); /* get current buffer length */ if ((rt & RT_END_ON_CNT) && /* end on count */ (read_length == mpx_charcnt [port])) { /* and count reached? */ recv_loop = FALSE; /* set termination */ mpx_param = 0; /* no extra termination info */ if (mpx_flags [port] & FL_ALERT) { /* was this alert for term rcv buffer? */ mpx_flags [port] &= ~FL_ALERT; /* clear alert flag */ mpx_charcnt [port] = RD_BUF_LIMIT; /* reset character count */ } } else if (read_length == RD_BUF_LIMIT) { /* buffer now full? */ recv_loop = FALSE; /* set termination */ mpx_param = mpx_param | RS_PARTIAL; /* and partial buffer flag */ } if (recv_loop) /* no termination condition? */ recv_loop = fast_timing; /* set to loop if fast mode */ else { /* termination occurred */ if (DEBUG_PRI (mpx_dev, DEB_XFER)) { fprintf (sim_deb, ">>MPX xfer: Port %d read terminated on ", port); if (mpx_param & RS_PARTIAL) fputs ("buffer full\n", sim_deb); else if (rt & RT_END_ON_CHAR) fprintf (sim_deb, "character %s\n", fmt_char (ch)); else fprintf (sim_deb, "count = %d\n", mpx_charcnt [port]); } if (buf_len (ioread, port, put) == 0) { /* zero-length read? */ buf_put (ioread, port, 0); /* dummy put to reserve header */ buf_remove (ioread, port); /* back out dummy char leaving header */ } buf_term (ioread, port, mpx_param >> 8); /* terminate buffer and set header */ if (buf_avail (ioread, port) == 1) /* first read buffer? */ mpx_flags [port] |= FL_HAVEBUF; /* indicate availability */ if (mpx_state == idle) /* controller idle? */ mpx_cntl_svc (&mpx_cntl); /* check for UI */ } } } /* Housekeeping */ if (fast_binary_read) { /* fast binary read in progress? */ if (port == 0) { /* on port 0? */ chx = tmxr_getc_ln (&mpx_ldsc [0]); /* see if a character is ready */ if (chx && !(mpx_flags [0] & FL_HAVEBUF)) { /* character ready and buffer empty? */ if (mpx_flags [0] & FL_WANTBUF) { /* second character? */ mpx_ibuf = mpx_ibuf | (chx & DMASK8); /* merge it into word */ mpx_flags [0] |= FL_HAVEBUF; /* mark buffer as ready */ mpx_io (mpx_dib.devno, ioENF, 0); /* set device flag */ if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fputs (">>MPX cmds: Flag and SRQ set\n", sim_deb); } else /* first character */ mpx_ibuf = (chx & DMASK8) << 8; /* put in top half of word */ mpx_flags [0] ^= FL_WANTBUF; /* toggle byte flag */ } sim_activate (uptr, uptr->wait); /* reschedule service for fast response */ } } else { /* normal service */ tmxr_poll_tx (&mpx_desc); /* output any accumulated characters */ if ((buf_avail (iowrite, port) < 2) && /* more to transmit? */ !(mpx_flags [port] & (FL_WAITACK | FL_XOFF)) || /* and transmission not suspended */ tmxr_rqln (&mpx_ldsc [port])) /* or more to receive? */ sim_activate (uptr, uptr->wait); /* reschedule service */ else if (DEBUG_PRI (mpx_dev, DEB_CMDS)) fprintf (sim_deb, ">>MPX cmds: Port %d service stopped\n", port); } return SCPE_OK; } /* Telnet poll service. This service routine is used to poll for Telnet connections and incoming characters. It starts when the socket is attached and stops when the socket is detached. Each line is then checked for a pending ENQ/ACK handshake. If one is pending, the ACK counter is incremented, and if it times out, another ENQ is sent to avoid stalls. Lines are also checked for available characters, and the corresponding line I/O service routine is scheduled if needed. */ t_stat mpx_poll_svc (UNIT *uptr) { uint32 i; t_stat status = SCPE_OK; poll_connection (); /* check for new connection */ tmxr_poll_rx (&mpx_desc); /* poll for input */ for (i = 0; i < MPX_PORTS; i++) { /* check lines */ if (mpx_flags [i] & FL_WAITACK) { /* waiting for ACK? */ mpx_ack_wait [i] = mpx_ack_wait [i] + 1; /* increment ACK wait timer */ if (mpx_ack_wait [i] > ACK_LIMIT) { /* has wait timed out? */ mpx_ack_wait [i] = 0; /* reset counter */ status = tmxr_putc_ln (&mpx_ldsc [i], ENQ); /* send ENQ again */ tmxr_poll_tx (&mpx_desc); /* transmit it */ if ((status == SCPE_OK) && /* transmitted OK? */ DEBUG_PRI (mpx_dev, DEB_XFER)) fprintf (sim_deb, ">>MPX xfer: Port %d character ENQ retransmitted\n", i); } } if (tmxr_rqln (&mpx_ldsc [i])) /* chars available? */ sim_activate (&mpx_unit [i], mpx_unit [i].wait); /* activate I/O service */ } if (uptr->wait == POLL_FIRST) /* first poll? */ uptr->wait = sync_poll (INITIAL); /* initial synchronization */ else /* not first */ uptr->wait = sync_poll (SERVICE); /* continue synchronization */ sim_activate (uptr, uptr->wait); /* continue polling */ return SCPE_OK; } /* Simulator reset routine. The hardware CRS signal generates a reset signal to the Z80 and its peripherals. This causes execution of the power up initialization code. The CRS signal also has these hardware effects: - clears control - clears flag - clears flag buffer - clears backplane ready - clears the output buffer register Implementation notes: 1. Under simulation, we also clear the input buffer register, even though the hardware doesn't. 2. We set up the first poll for Telnet connections to occur "immediately" upon execution, so that clients will be connected before execution begins. Otherwise, a fast program may access the multiplexer before the poll service routine activates. 3. We must set the "emptying_flags" and "filling_flags" values here, because they cannot be initialized statically, even though the values are constant. */ t_stat mpx_reset (DEVICE *dptr) { if (sim_switches & SWMASK ('P')) { /* PON reset? */ emptying_flags [ioread] = FL_RDEMPT; /* initialize buffer flags constants */ emptying_flags [iowrite] = FL_WREMPT; filling_flags [ioread] = FL_RDFILL; filling_flags [iowrite] = FL_WRFILL; } mpx_io (mpx_dib.devno, ioPOPIO, 0); /* send POPIO signal */ mpx_ibuf = 0; /* clear input buffer */ if (mpx_poll.flags & UNIT_ATT) { /* network attached? */ mpx_poll.wait = POLL_FIRST; /* set up poll */ sim_activate (&mpx_poll, mpx_poll.wait); /* start Telnet poll immediately */ } else sim_cancel (&mpx_poll); /* else stop Telnet poll */ return SCPE_OK; } /* Attach the multiplexer to a Telnet port. We are called by the ATTACH MPX command to attach the multiplexer to the listening port indicated by . Logically, it is the multiplexer device that is attached; however, SIMH only allows units to be attached. This makes sense for devices such as tape drives, where the attached media is a property of a specific drive. In our case, though, the listening port is a property of the multiplexer card, not of any given serial line. As ATTACH MPX is equivalent to ATTACH MPX0, the port would, by default, be attached to the first serial line and be reported there in a SHOW MPX command. To preserve the logical picture, we attach the port to the Telnet poll unit, which is normally disabled, inhibiting its display. Attaching to a disabled unit is not allowed, so we first enable the unit, then attach it, then disable it again. Attachment is reported by the "mpx_status" routine below. The Telnet poll service routine is synchronized with the other input polling devices in the simulator to facilitate idling. */ t_stat mpx_attach (UNIT *uptr, char *cptr) { t_stat status = SCPE_OK; if (uptr != mpx_unit) /* not unit 0? */ return SCPE_NOATT; /* can't attach */ mpx_poll.flags = mpx_poll.flags & ~UNIT_DIS; /* enable unit */ status = tmxr_attach (&mpx_desc, &mpx_poll, cptr); /* attach to socket */ mpx_poll.flags = mpx_poll.flags | UNIT_DIS; /* disable unit */ if (status == SCPE_OK) { mpx_poll.wait = POLL_FIRST; /* set up poll */ sim_activate (&mpx_poll, mpx_poll.wait); /* start Telnet poll immediately */ } return status; } /* Detach the multiplexer. Normally, we are called by the DETACH MPX command, which is equivalent to DETACH MPX0. However, we may be called with other units in two cases. A DETACH ALL command will call us for unit 9 (the poll unit) if it is attached. Also, during simulator shutdown, we will be called for units 0-8 (detach_all in scp.c calls the detach routines of all units that do NOT have UNIT_ATTABLE), as well as for unit 9 if it is attached. In both cases, it is imperative that we return SCPE_OK, otherwise any remaining device detaches will not be performed. */ t_stat mpx_detach (UNIT *uptr) { t_stat status = SCPE_OK; int32 i; if ((uptr == mpx_unit) || (uptr == &mpx_poll)) { /* base unit or poll unit? */ status = tmxr_detach (&mpx_desc, &mpx_poll); /* detach socket */ for (i = 0; i < MPX_PORTS; i++) { mpx_ldsc [i].rcve = 0; /* disable line reception */ sim_cancel (&mpx_unit [i]); /* cancel any scheduled I/O */ } sim_cancel (&mpx_poll); /* stop Telnet poll */ } return status; } /* Show multiplexer status */ t_stat mpx_status (FILE *st, UNIT *uptr, int32 val, void *desc) { if (mpx_poll.flags & UNIT_ATT) /* attached to socket? */ fprintf (st, "attached to port %s, ", mpx_poll.filename); else fprintf (st, "not attached, "); tmxr_show_summ (st, uptr, val, desc); /* report connection count */ return SCPE_OK; } /* Set firmware revision. Currently, we support only revision C, so the MTAB entry does not have an "mstring" entry. When we add revision D support, an "mstring" entry of "REV" will enable changing the firmware revision. */ t_stat mpx_set_frev (UNIT *uptr, int32 val, char *cptr, void *desc) { if ((cptr == NULL) || /* no parameter? */ (*cptr < 'C') || (*cptr > 'D') || /* or not C or D? */ (*(cptr + 1) != '\0')) /* or not just one character? */ return SCPE_ARG; /* bad argument */ else { if (*cptr == 'C') /* setting revision C? */ mpx_dev.flags = mpx_dev.flags & ~DEV_REV_D; /* clear 'D' flag */ else if (*cptr == 'D') /* setting revision D? */ mpx_dev.flags = mpx_dev.flags | DEV_REV_D; /* set 'D' flag */ return SCPE_OK; } } /* Show firmware revision */ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc) { if (mpx_dev.flags & DEV_REV_D) fputs ("12792D", st); else fputs ("12792C", st); return SCPE_OK; } /* Local routines */ /* Poll for new Telnet connections */ static void poll_connection (void) { int32 new_line; new_line = tmxr_poll_conn (&mpx_desc); /* check for new connection */ if (new_line >= 0) /* new connection established? */ mpx_ldsc [new_line].rcve = 1; /* enable line to receive */ return; } /* Controller reset. This is the card microprocessor reset, not the simulator reset routine. It simulates a power-on restart of the Z80 firmware. When it is called from the simulator reset routine, that routine will take care of setting the card flip-flops appropriately. */ static void controller_reset (void) { uint32 i; mpx_state = idle; /* idle state */ mpx_cmd = 0; /* clear command */ mpx_param = 0; /* clear parameter */ mpx_uien = FALSE; /* disable interrupts */ for (i = 0; i < MPX_PORTS; i++) { /* clear per-line variables */ buf_init (iowrite, i); /* initialize write buffers */ buf_init (ioread, i); /* initialize read buffers */ mpx_key [i] = KEY_DEFAULT; /* clear port key to default */ if (i == 0) /* default port configurations */ mpx_config [0] = SK_PWRUP_0; /* port 0 is separate from 1-7 */ else mpx_config [i] = SK_PWRUP_1 | i; mpx_rcvtype [i] = RT_PWRUP; /* power on config for echoplex */ mpx_charcnt [i] = 0; /* default character count */ mpx_flowcntl [i] = 0; /* default flow control */ mpx_flags [i] = 0; /* clear state flags */ mpx_enq_cntr [i] = 0; /* clear ENQ counter */ mpx_ack_wait [i] = 0; /* clear ACK wait timer */ mpx_unit [i].wait = service_time (mpx_config [i]); /* set terminal I/O time */ sim_cancel (&mpx_unit [i]); /* cancel line I/O */ } sim_cancel (&mpx_cntl); /* cancel controller */ return; } /* Calculate service time from baud rate. Service times are based on 1580 instructions per second, which is the 1000 E-Series execution speed. Baud rate 0 means "don't change" and is handled by the "Set port key" command executor. Baud rate settings of 13-15 are marked as "reserved" in the user manual, but the firmware defines these as 38400, 9600, and 9600 baud, respectively. */ static uint32 service_time (uint16 control_word) { /* Baud Rates 0- 7 : --, 50, 75, 110, 134.5, 150, 300, 1200, Baud Rates 8-15 : 1800, 2400, 4800, 9600, 19200, 38400, 9600, 9600 */ static const int32 ticks [] = { 0, 316000, 210667, 143636, 117472, 105333, 52667, 13167, 8778, 6583, 3292, 1646, 823, 411, 1646, 1646 }; return ticks [GET_BAUDRATE (control_word)]; /* return service time for indicated rate */ } /* Translate port key to port number. Port keys are scanned in reverse port order, so if more than one port has the same port key, commands specifying that key will affect the highest numbered port. If a port key is the reserved value 255, then the port key has not been set. In this case, set the input buffer to 0xBAD0 and return -1 to indicate failure. */ static int32 key_to_port (uint32 key) { int32 i; for (i = MPX_PORTS - 1; i >= 0; i--) /* scan in reverse order */ if (mpx_key [i] == key) /* key found? */ return i; /* return port number */ mpx_ibuf = ST_BAD_KEY; /* key not found: set status */ return -1; /* return failure code */ } /* Buffer manipulation routines. The 12792 hardware provides 16K bytes of RAM to the microprocessor. From this pool, the firmware allocates per-port read/write buffers and state variables, global variables, and the system stack. Allocations are static and differ between firmware revisions. The A/B/C revisions allocate two 254-byte read buffers and two 254-byte write buffers per port. Assuming an idle condition, the first write to a port transfers characters to the first write buffer. When the transfer completes, the SIO begins transmitting. During transmission, a second write can be initiated, which transfers characters to the second write buffer. If a third write is attempted before the first buffer has been released, it will be denied until the SIO completes transmission; then, if enabled, an unsolicited interrupt will occur to announce buffer availability. The "active" (filling) buffer alternates between the two. At idle, characters received will fill the first read buffer. When the read completes according to the previously set termination criteria, an unsolicited interrupt will occur (if enabled) to announce buffer availability. If more characters are received before the first buffer has been transferred to the CPU, they will fill the second buffer. If that read also completes, additional characters will be discarded until the first buffer has been emptied. The "active" (emptying) buffer alternates between the two. With this configuration, two one-character writes or reads will allocate both available buffers, even though each was essentially empty. The D revision allocates one 1024-byte FIFO read buffer and one 892-byte write buffer per port. As with the A/B/C revisions, the first write to a port transfers characters to the write buffer, and serial transmission begins when the write completes. However, the write buffer is not a FIFO, so the host is not permitted another write request until the entire buffer has been transmitted. The read buffer is a FIFO. Characters received are placed into the FIFO as a stream. Unlike the A/B/C revisions, character editing and termination conditions are not evaluated until the buffer is read. Therefore, a full 1024 characters may be received before additional characters would be discarded. When the first character is received, an unsolicited interrupt occurs (if enabled) to announce data reception. A host read may then be initiated. The write buffer is used temporarily to process characters from the read buffer. Characters are copied from the read to the write buffer while editing as directed by the configuration accompanying the read request (e.g., deleting the character preceding a BS, stripping CR/LF, etc.). When the termination condition is found, the read command completes. Incoming characters may be added to the FIFO while this is occurring. In summary, the revision differences in buffer handling are: Revisions A/B/C: - two 254-byte receive buffers - a buffer is "full" when the terminator character or count is received - termination type must be established before the corresponding read - data is echoed as it is received Revision D: - one 1024-byte receive buffer - buffer is "full" only when 1024 characters are received - the concept of a buffer terminator does not apply, as the data is not examined until a read is requested and characters are retrieved from the FIFO. - data is not echoed until it is read To implement the C revision behavior, while preserving the option of reusing the buffer handlers for future D revision support, the dual 254-byte buffers are implemented as a single 514-byte circular FIFO with capacity limited to 254 bytes per buffer. This reserves space for a CR and LF and for a header byte in each buffer. The header byte preserves per-buffer state information. In this implementation, the buffer "put" index points at the next free location, and the buffer "get" index points at the next character to retrieve. In addition to "put" and "get" indexes, a third "separator" index is maintained to divide the FIFO into two areas corresponding to the two buffers, and a "buffer filling" flag is maintained for each FIFO that is set by the fill (put) routine and cleared by the terminate buffer routine. Graphically, the implementation is as follows for buffer "B[]", get "G", put "P", and separator "S" indexes: 1. Initialize: 2. Fill first buffer: G = S = P = 0 B[P] = char; Incr (P) |------------------------------| |---------|--------------------| G G P --> S S P 3. Terminate first buffer: 4. Fill second buffer: if S == G then S = P else nop B[P] = char; Incr (P) |------------|-----------------| |------------|------|----------| G /---> S G S P --> * ----/ P 5. Terminate second buffer: 6. Empty first buffer: if S == G then S = P else nop char = B[G]; Incr (G) |------------|------------|----| |----|-------|------------|----| G S P G --> S P 7. First buffer is empty: 8. Free first buffer: G == S if !filling then S = P else nop |------------|------------|----| |------------|------------|----| G P G /---> S S * ----/ P 9. Empty second buffer: 10. Second buffer empty: char = B[G]; Incr (G) G == S |----------------|--------|----| |-------------------------|----| G --> S G P S P 11. Free second buffer: if !filling then S = P else nop |-------------------------|----| G S P We also provide the following utility routines: - Remove Character: Decr (P) - Cancel Buffer: if S == G then P = G else G = S - Buffer Length: if S < G then return S + BUFSIZE - G else return S - G - Buffers Available: if G == P then return 2 else if G != S != P then return 0 else return 1 The "buffer filling" flag is necessary for the "free" routine to decide whether to advance the separator index. If the first buffer is to be freed, then G == S and S != P. If the second buffer is already filled, then S = P. However, if the buffer is still filling, then S must remain at G. This cannot be determined from G, S, and P alone. A "buffer emptying" flag is also employed to record whether the per-buffer header has been obtained. This allows the buffer length to exclude the header and reflect only the characters present. */ /* Increment a buffer index with wraparound */ static uint16 buf_incr (BUF_INDEX index, uint32 port, IO_OPER rw, int increment) { index [port] [rw] = (index [port] [rw] + buf_size [rw] + increment) % buf_size [rw]; return index [port] [rw]; } /* Initialize the buffer. Initialization sets the three indexes to zero and clears the buffer state flags. */ static void buf_init (IO_OPER rw, uint32 port) { mpx_get [port] [rw] = 0; /* clear indexes */ mpx_sep [port] [rw] = 0; mpx_put [port] [rw] = 0; if (rw == ioread) mpx_flags [mpx_port] &= ~(FL_RDFLAGS); /* clear read buffer flags */ else mpx_flags [mpx_port] &= ~(FL_WRFLAGS); /* clear write buffer flags */ return; } /* Get a character from the buffer. The character indicated by the "get" index is retrieved from the buffer, and the index is incremented with wraparound. If the buffer is now empty, the "buffer emptying" flag is cleared. Otherwise, it is set to indicate that characters have been removed from the buffer. */ static uint8 buf_get (IO_OPER rw, uint32 port) { uint8 ch; uint32 index = mpx_get [port] [rw]; /* current get index */ if (rw == ioread) ch = mpx_rbuf [port] [index]; /* get char from read buffer */ else ch = mpx_wbuf [port] [index]; /* get char from write buffer */ buf_incr (mpx_get, port, rw, +1); /* increment circular get index */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) if (mpx_flags [port] & emptying_flags [rw]) fprintf (sim_deb, ">>MPX buf: Port %d character %s get from %s buffer " "[%d]\n", port, fmt_char (ch), io_op [rw], index); else fprintf (sim_deb, ">>MPX buf: Port %d header %03o get from %s buffer " "[%d]\n", port, ch, io_op [rw], index); if (mpx_get [port] [rw] == mpx_sep [port] [rw]) /* buffer now empty? */ mpx_flags [port] &= ~emptying_flags [rw]; /* clear "buffer emptying" flag */ else mpx_flags [port] |= emptying_flags [rw]; /* set "buffer emptying" flag */ return ch; } /* Put a character to the buffer. The character is written to the buffer in the slot indicated by the "put" index, and the index is incremented with wraparound. The first character put to a new buffer reserves space for the header and sets the "buffer filling" flag. */ static void buf_put (IO_OPER rw, uint32 port, uint8 ch) { uint32 index; if ((mpx_flags [port] & filling_flags [rw]) == 0) { /* first put to this buffer? */ mpx_flags [port] |= filling_flags [rw]; /* set buffer filling flag */ index = mpx_put [port] [rw]; /* get current put index */ buf_incr (mpx_put, port, rw, +1); /* reserve space for header */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d reserved header " "for %s buffer [%d]\n", port, io_op [rw], index); } index = mpx_put [port] [rw]; /* get current put index */ if (rw == ioread) mpx_rbuf [port] [index] = ch; /* put char in read buffer */ else mpx_wbuf [port] [index] = ch; /* put char in write buffer */ buf_incr (mpx_put, port, rw, +1); /* increment circular put index */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d character %s put to %s buffer " "[%d]\n", port, fmt_char (ch), io_op [rw], index); return; } /* Remove the last character put to the buffer. The most-recent character put to the buffer is removed by decrementing the "put" index with wraparound. */ static void buf_remove (IO_OPER rw, uint32 port) { uint8 ch; uint32 index; index = buf_incr (mpx_put, port, rw, -1); /* decrement circular put index */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) { if (rw == ioread) ch = mpx_rbuf [port] [index]; /* pick up char from read buffer */ else ch = mpx_wbuf [port] [index]; /* pick up char from write buffer */ fprintf (sim_deb, ">>MPX buf: Port %d character %s removed from %s buffer " "[%d]\n", port, fmt_char (ch), io_op [rw], index); } return; } /* Terminate the buffer. The buffer is marked to indicate that filling is complete and that the next "put" operation should begin a new buffer. The header value is stored in first byte of buffer, which is reserved, and the "buffer filling" flag is cleared. */ static void buf_term (IO_OPER rw, uint32 port, uint8 header) { uint32 index = mpx_sep [port] [rw]; /* separator index */ if (rw == ioread) mpx_rbuf [port] [index] = header; /* put header in read buffer */ else mpx_wbuf [port] [index] = header; /* put header in write buffer */ mpx_flags [port] = mpx_flags [port] & ~filling_flags [rw]; /* clear filling flag */ if (mpx_get [port] [rw] == index) /* reached separator? */ mpx_sep [port] [rw] = mpx_put [port] [rw]; /* move sep to end of next buffer */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d header %03o terminated %s buffer\n", port, header, io_op [rw]); return; } /* Free the buffer. The buffer is marked to indicate that it is available for reuse, and the "buffer emptying" flag is reset. */ static void buf_free (IO_OPER rw, uint32 port) { if ((mpx_flags [port] & filling_flags [rw]) == 0) /* not filling next buffer? */ mpx_sep [port] [rw] = mpx_put [port] [rw]; /* move separator to end of next buffer */ /* else it will be moved when terminated */ mpx_flags [port] = mpx_flags [port] & ~emptying_flags [rw]; /* clear emptying flag */ if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d released %s buffer\n", port, io_op [rw]); return; } /* Cancel the selected buffer. The selected buffer is marked to indicate that it is empty. Either the "put" buffer or the "get" buffer may be selected. */ static void buf_cancel (IO_OPER rw, uint32 port, BUF_SELECT which) { if (which == put) { /* cancel put buffer? */ mpx_put [port] [rw] = mpx_sep [port] [rw]; /* move put back to separator */ mpx_flags [port] &= ~filling_flags [rw]; /* clear filling flag */ } else { /* cancel get buffer */ if (mpx_sep [port] [rw] == mpx_get [port] [rw]) { /* filling first buffer? */ mpx_put [port] [rw] = mpx_get [port] [rw]; /* cancel first buffer */ mpx_flags [port] &= ~filling_flags [rw]; /* clear filling flag */ } else { /* not filling first buffer */ mpx_get [port] [rw] = mpx_sep [port] [rw]; /* cancel first buffer */ if ((mpx_flags [port] & filling_flags [rw]) == 0) /* not filling second buffer? */ mpx_sep [port] [rw] = mpx_put [port] [rw]; /* move separator to end of next buffer */ } mpx_flags [port] &= ~emptying_flags [rw]; /* clear emptying flag */ } if (DEBUG_PRI (mpx_dev, DEB_BUF)) fprintf (sim_deb, ">>MPX buf: Port %d cancelled %s buffer\n", port, io_op [rw]); return; } /* Get the buffer length. The current length of the selected buffer (put or get) is returned. For ease of use, the returned length does NOT include the header byte, i.e., it reflects only the characters contained in the buffer. If the put buffer is selected, and the buffer is filling, or the get buffer is selected, and the buffer is not emptying, then subtract one from the length for the allocated header. */ static uint32 buf_len (IO_OPER rw, uint32 port, BUF_SELECT which) { int32 length; if (which == put) length = mpx_put [port] [rw] - mpx_sep [port] [rw] - /* calculate length */ ((mpx_flags [port] & filling_flags [rw]) != 0); /* account for allocated header */ else { length = mpx_sep [port] [rw] - mpx_get [port] [rw]; /* calculate length */ if (length && !(mpx_flags [port] & emptying_flags [rw])) /* not empty and not yet emptying? */ length = length - 1; /* account for allocated header */ } if (length < 0) /* is length negative? */ return length + buf_size [rw]; /* account for wraparound */ else return length; } /* Return the number of free buffers available. Either 0, 1, or 2 free buffers will be available. A buffer is available if it contains no characters (including the header byte). */ static uint32 buf_avail (IO_OPER rw, uint32 port) { if (mpx_get [port] [rw] == mpx_put [port] [rw]) /* get and put indexes equal? */ return 2; /* all buffers are free */ else if ((mpx_get [port] [rw] != mpx_sep [port] [rw]) && /* get, separator, and put */ (mpx_sep [port] [rw] != mpx_put [port] [rw])) /* all different? */ return 0; /* no buffers are free */ else return 1; /* one buffer free */ } simh-3.8.1/HP2100/hp2100_sys.c0000644000175000017500000007656411057571226013457 0ustar vlmvlm/* hp2100_sys.c: HP 2100 simulator interface Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 03-Sep-08 JDB Fixed IAK instruction dual-use mnemonic display 07-Aug-08 JDB Moved hp_setdev, hp_showdev from hp2100_cpu.c Changed sim_load to use WritePW instead of direct M[] access 18-Jun-08 JDB Added PIF device 17-Jun-08 JDB Moved fmt_char() function from hp2100_baci.c 26-May-08 JDB Added MPX device 24-Apr-08 JDB Changed fprint_sym to handle step with irq pending 07-Dec-07 JDB Added BACI device 27-Nov-07 JDB Added RTE OS/VMA/EMA mnemonics 21-Dec-06 JDB Added "fwanxm" external for sim_load check 19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF messages 25-Sep-04 JDB Added memory protect device Fixed display of CCA/CCB/CCE instructions 01-Jun-04 RMS Added latent 13037 support 19-Apr-04 RMS Recognize SFS x,C and SFC x,C 22-Mar-02 RMS Revised for dynamically allocated memory 14-Feb-02 RMS Added DMS instructions 04-Feb-02 RMS Fixed bugs in alter/skip display and parsing 01-Feb-02 RMS Added terminal multiplexor support 16-Jan-02 RMS Added additional device support 17-Sep-01 RMS Removed multiconsole support 27-May-01 RMS Added multiconsole support 14-Mar-01 RMS Revised load/dump interface (again) 30-Oct-00 RMS Added examine to file support 15-Oct-00 RMS Added dynamic device number support 27-Oct-98 RMS V2.4 load interface */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include extern DEVICE cpu_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern DEVICE mp_dev; extern DEVICE dma0_dev, dma1_dev; extern DEVICE ptr_dev, ptp_dev; extern DEVICE tty_dev, clk_dev; extern DEVICE lps_dev; extern DEVICE lpt_dev; extern DEVICE baci_dev; extern DEVICE mpx_dev; extern DEVICE mtd_dev, mtc_dev; extern DEVICE msd_dev, msc_dev; extern DEVICE dpd_dev, dpc_dev; extern DEVICE dqd_dev, dqc_dev; extern DEVICE drd_dev, drc_dev; extern DEVICE ds_dev; extern DEVICE muxl_dev, muxu_dev, muxc_dev; extern DEVICE ipli_dev, iplo_dev; extern DEVICE pif_dev; /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "HP 2100"; char halt_msg[] = "HALT instruction xxxxxx"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 3; DEVICE *sim_devices[] = { &cpu_dev, &mp_dev, &dma0_dev, &dma1_dev, &ptr_dev, &ptp_dev, &tty_dev, &clk_dev, &lps_dev, &lpt_dev, &baci_dev, &mpx_dev, &dpd_dev, &dpc_dev, &dqd_dev, &dqc_dev, &drd_dev, &drc_dev, &ds_dev, &mtd_dev, &mtc_dev, &msd_dev, &msc_dev, &muxl_dev, &muxu_dev, &muxc_dev, &ipli_dev, &iplo_dev, &pif_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Unimplemented instruction", "Non-existent I/O device", halt_msg, "Breakpoint", "Indirect address loop", "Indirect address interrupt (should not happen!)", "No connection on interprocessor link", "Device/unit offline", "Device/unit powered off" }; /* Binary loader The binary loader consists of blocks preceded and trailed by zero frames. A block consists of 16b words (punched big endian), as follows: count'xxx origin word 0 : word count-1 checksum The checksum includes the origin but not the count. */ int32 fgetw (FILE *fileref) { int c1, c2; if ((c1 = fgetc (fileref)) == EOF) return -1; if ((c2 = fgetc (fileref)) == EOF) return -1; return ((c1 & 0377) << 8) | (c2 & 0377); } t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 origin, csum, zerocnt, count, word, i; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; for (zerocnt = 1;; zerocnt = -10) { /* block loop */ for (;; zerocnt++) { /* skip 0's */ if ((count = fgetc (fileref)) == EOF) return SCPE_OK; else if (count) break; else if (zerocnt == 0) return SCPE_OK; } if (fgetc (fileref) == EOF) return SCPE_FMT; if ((origin = fgetw (fileref)) < 0) return SCPE_FMT; csum = origin; /* seed checksum */ for (i = 0; i < count; i++) { /* get data words */ if ((word = fgetw (fileref)) < 0) return SCPE_FMT; WritePW (origin, word); origin = origin + 1; csum = csum + word; } if ((word = fgetw (fileref)) < 0) return SCPE_FMT; if ((word ^ csum) & DMASK) return SCPE_CSUM; } } /* Symbol tables */ #define I_V_FL 16 /* flag start */ #define I_M_FL 017 /* flag mask */ #define I_V_NPN 0 /* no operand */ #define I_V_NPC 1 /* no operand + C */ #define I_V_MRF 2 /* mem ref */ #define I_V_ASH 3 /* alter/skip, shift */ #define I_V_ESH 4 /* extended shift */ #define I_V_EMR 5 /* extended mem ref */ #define I_V_IO1 6 /* I/O + HC */ #define I_V_IO2 7 /* I/O only */ #define I_V_EGZ 010 /* ext grp, 1 op + 0 */ #define I_V_EG2 011 /* ext grp, 2 op */ #define I_V_ALT 012 /* alternate use instr */ #define I_NPN (I_V_NPN << I_V_FL) #define I_NPC (I_V_NPC << I_V_FL) #define I_MRF (I_V_MRF << I_V_FL) #define I_ASH (I_V_ASH << I_V_FL) #define I_ESH (I_V_ESH << I_V_FL) #define I_EMR (I_V_EMR << I_V_FL) #define I_IO1 (I_V_IO1 << I_V_FL) #define I_IO2 (I_V_IO2 << I_V_FL) #define I_EGZ (I_V_EGZ << I_V_FL) #define I_EG2 (I_V_EG2 << I_V_FL) #define I_ALT (I_V_ALT << I_V_FL) static const int32 masks[] = { 0177777, 0176777, 0074000, 0170000, 0177760, 0177777, 0176700, 0177700, 0177777, 0177777, 0177777 }; static const char *opcode[] = { /* These mnemonics are used by debug printouts, so put them first. */ "$LIBR", "$LIBX", ".TICK", ".TNAM", /* RTE-6/VM OS firmware */ ".STIO", ".FNW", ".IRT", ".LLS", ".SIP", ".YLD", ".CPM", ".ETEQ", ".ENTN", "$OTST", ".ENTC", ".DSPI", "$DCPC", "$MPV", "$DEV", "$TBG", /* alternates for dual-use */ ".PMAP", "$LOC", "$VTST",/* --- */ /* RTE-6/VM VMA firmware */ /* --- --- --- --- */ ".IMAP", ".IMAR", ".JMAP", ".JMAR", ".LPXR", ".LPX", ".LBPR", ".LBP", ".EMIO", "MMAP", "$ETST",/* --- */ /* RTE-IV EMA firmware */ /* --- --- --- --- */ /* --- --- --- --- */ /* --- --- --- */ ".EMAP", /* Regular mnemonics. */ "NOP", "NOP", "AND", "JSB", "XOR", "JMP", "IOR", "ISZ", "ADA", "ADB" ,"CPA", "CPB", "LDA", "LDB", "STA", "STB", "DIAG", "ASL", "LSL", "TIMER", "RRL", "ASR", "LSR", "RRR", "MPY", "DIV", "DLD", "DST", "FAD", "FSB", "FMP", "FDV", "FIX", "FLT", "STO", "CLO", "SOC", "SOS", "HLT", "STF", "CLF", "SFC", "SFS", "MIA", "MIB", "LIA", "LIB", "OTA", "OTB", "STC", "CLC", "SYA", "USA", "PAA", "PBA", "XMA", "XLA", "XSA", "XCA", "LFA", "RSA", "RVA", "MBI", "MBF", "MBW", "MWI", "MWF", "MWW", "SYB", "USB", "PAB", "PBB", "SSM", "JRS", "XMM", "XMS", "XMB", "XLB", "XSB", "XCB", "LFB", "RSB", "RVB", "DJP", "DJS", "SJP", "SJS", "UJP", "UJS", "SAX", "SBX", "CAX", "CBX", "LAX", "LBX", "STX", "CXA", "CXB", "LDX", "ADX", "XAX", "XBX", "SAY", "SBY", "CAY", "CBY", "LAY", "LBY", "STY", "CYA", "CYB", "LDY", "ADY", "XAY", "XBY", "ISX", "DSX", "JLY", "LBT", "SBT", "MBT", "CBT", "SBT", "ISY", "DSY", "JPY", "SBS", "CBS", "TBS", "CMW", "MVW", NULL, /* decode only */ NULL }; static const int32 opc_val[] = { 0105340+I_NPN, 0105341+I_NPN, 0105342+I_NPN, 0105343+I_NPN, /* RTE-6/VM OS */ 0105344+I_NPN, 0105345+I_NPN, 0105346+I_NPN, 0105347+I_NPN, 0105350+I_NPN, 0105351+I_NPN, 0105352+I_NPN, 0105353+I_NPN, 0105354+I_ALT, 0105355+I_ALT, 0105356+I_ALT, 0105357+I_ALT, 0105354+I_NPN, 0105355+I_NPN, 0105356+I_NPN, 0105357+I_NPN, /* alternates */ 0105240+I_ALT, 0105241+I_ALT, 0105242+I_ALT, /* --- */ /* RTE-6/VM VMA */ /* --- --- --- --- */ 0105250+I_NPN, 0105251+I_NPN, 0105252+I_NPN, 0105253+I_NPN, 0105254+I_NPN, 0105255+I_NPN, 0105256+I_NPN, 0105257+I_ALT, 0105240+I_NPN, 0105241+I_NPN, 0105242+I_NPN, /* RTE-IV EMA */ /* --- --- --- --- */ /* --- --- --- --- */ /* --- --- --- */ 0105257+I_NPN, 0000000+I_NPN, 0002000+I_NPN, 0010000+I_MRF, 0014000+I_MRF, 0020000+I_MRF, 0024000+I_MRF, 0030000+I_MRF, 0034000+I_MRF, 0040000+I_MRF, 0044000+I_MRF, 0050000+I_MRF, 0054000+I_MRF, 0060000+I_MRF, 0064000+I_MRF, 0070000+I_MRF, 0074000+I_MRF, 0100000+I_NPN, 0100020+I_ESH, 0100040+I_ESH, 0100060+I_NPN, 0100100+I_ESH, 0101020+I_ESH, 0101040+I_ESH, 0101100+I_ESH, 0100200+I_EMR, 0100400+I_EMR, 0104200+I_EMR, 0104400+I_EMR, 0105000+I_EMR, 0105020+I_EMR, 0105040+I_EMR, 0105060+I_EMR, 0105100+I_NPN, 0105120+I_NPN, 0102101+I_NPN, 0103101+I_NPN, 0102201+I_NPC, 0102301+I_NPC, 0102000+I_IO1, 0102100+I_IO2, 0103100+I_IO2, 0102200+I_IO1, 0102300+I_IO1, 0102400+I_IO1, 0106400+I_IO1, 0102500+I_IO1, 0106500+I_IO1, 0102600+I_IO1, 0106600+I_IO1, 0102700+I_IO1, 0106700+I_IO1, 0101710+I_NPN, 0101711+I_NPN, 0101712+I_NPN, 0101713+I_NPN, 0101722+I_NPN, 0101724+I_EMR, 0101725+I_EMR, 0101726+I_EMR, 0101727+I_NPN, 0101730+I_NPN, 0101731+I_NPN, 0105702+I_NPN, 0105703+I_NPN, 0105704+I_NPN, 0105705+I_NPN, 0105706+I_NPN, 0105707+I_NPN, 0105710+I_NPN, 0105711+I_NPN, 0105712+I_NPN, 0105713+I_NPN, 0105714+I_EMR, 0105715+I_EG2, 0105720+I_NPN, 0105721+I_NPN, 0105722+I_NPN, 0105724+I_EMR, 0105725+I_EMR, 0105726+I_EMR, 0105727+I_NPN, 0105730+I_NPN, 0105731+I_NPN, 0105732+I_EMR, 0105733+I_EMR, 0105734+I_EMR, 0105735+I_EMR, 0105736+I_EMR, 0105737+I_EMR, 0101740+I_EMR, 0105740+I_EMR, 0101741+I_NPN, 0105741+I_NPN, 0101742+I_EMR, 0105742+I_EMR, 0105743+I_EMR, 0101744+I_NPN, 0105744+I_NPN, 0105745+I_EMR, 0105746+I_EMR, 0101747+I_NPN, 0105747+I_NPN, 0101750+I_EMR, 0105750+I_EMR, 0101751+I_NPN, 0105751+I_NPN, 0101752+I_EMR, 0105752+I_EMR, 0105753+I_EMR, 0101754+I_NPN, 0105754+I_NPN, 0105755+I_EMR, 0105756+I_EMR, 0101757+I_NPN, 0105757+I_NPN, 0105760+I_NPN, 0105761+I_NPN, 0105762+I_EMR, 0105763+I_NPN, 0105764+I_NPN, 0105765+I_EGZ, 0105766+I_EGZ, 0105767+I_NPN, 0105770+I_NPN, 0105771+I_NPN, 0105772+I_EMR, 0105773+I_EG2, 0105774+I_EG2, 0105775+I_EG2, 0105776+I_EGZ, 0105777+I_EGZ, 0000000+I_ASH, /* decode only */ -1 }; /* Decode tables for shift and alter/skip groups */ static const char *stab[] = { "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", "CLA", "CMA", "CCA", "CLB", "CMB", "CCB", "SEZ", "CLE", "CLE", "CME", "CCE", "SSA", "SSB", "SLA", "SLB", "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", "INA", "INB", "SZA", "SZB", "RSS", NULL }; static const int32 mtab[] = { 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007400, 0007400, 0007400, 0007400, 0007400, 0007400, 0002040, 0002040, 0002300, 0002300, 0002300, 0006020, 0006020, 0004010, 0004010, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006004, 0006004, 0006002, 0006002, 0002001, 0 }; static const int32 vtab[] = { 0001000, 0001100, 0001200, 0001300, 0001400, 0001500, 0001600, 0001700, 0005000, 0005100, 0005200, 0005300, 0005400, 0005500, 0005600, 0005700, 0002400, 0003000, 0003400, 0006400, 0007000, 0007400, 0002040, 0000040, 0002100, 0002200, 0002300, 0002020, 0006020, 0000010, 0004010, 0000020, 0000021, 0000022, 0000023, 0000024, 0000025, 0000026, 0000027, 0004020, 0004021, 0004022, 0004023, 0004024, 0004025, 0004026, 0004027, 0002004, 0006004, 0002002, 0006002, 0002001, -1 }; /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to data *uptr = pointer to unit sw = switches Outputs: return = status code */ #define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, cm, i, j, inst, disp; uint32 irq; cflag = (uptr == NULL) || (uptr == &cpu_unit); inst = val[0]; if (sw & SWMASK ('A')) { /* ASCII? */ if (inst > 0377) return SCPE_ARG; fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (sw & SWMASK ('C')) { /* characters? */ fprintf (of, FMTASC ((inst >> 8) & 0177)); fprintf (of, FMTASC (inst & 0177)); return SCPE_OK; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* If we are being called as a result of a VM stop to display the next instruction to be executed, check to see if an interrupt is pending and not deferred. If so, then display the interrupt source and the trap cell instruction as the instruction to be executed, rather than the instruction at the current PC. */ if (sw & SIM_SW_STOP) { /* simulator stop? */ irq = calc_int (); /* check interrupt */ if (irq && (!ion_defer || !calc_defer())) { /* pending interrupt and not deferred? */ addr = irq; /* set display address to trap cell */ inst = val[0] = ReadIO (irq, SMAP); /* load trap cell instruction */ val[1] = ReadIO (irq + 1, SMAP); /* might be multi-word */ val[2] = ReadIO (irq + 2, SMAP); /* although it's unlikely */ fprintf (of, "IAK %2o: ", irq); /* report acknowledged interrupt */ } } for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: /* no operands */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_NPC: /* no operands + C */ fprintf (of, "%s", opcode[i]); if (inst & I_HC) fprintf (of, " C"); break; case I_V_MRF: /* mem ref */ disp = inst & I_DISP; /* displacement */ fprintf (of, "%s ", opcode[i]); /* opcode */ if (inst & I_CP) { /* current page? */ if (cflag) fprintf (of, "%-o", (addr & I_PAGENO) | disp); else fprintf (of, "C %-o", disp); } else fprintf (of, "%-o", disp); /* page zero */ if (inst & I_IA) fprintf (of, ",I"); break; case I_V_ASH: /* shift, alter-skip */ cm = FALSE; for (i = 0; mtab[i] != 0; i++) { if ((inst & mtab[i]) == vtab[i]) { inst = inst & ~(vtab[i] & 01777); if (cm) fprintf (of, ","); cm = TRUE; fprintf (of, "%s", stab[i]); } } if (!cm) return SCPE_ARG; /* nothing decoded? */ break; case I_V_ESH: /* extended shift */ disp = inst & 017; /* shift count */ if (disp == 0) disp = 16; fprintf (of, "%s %d", opcode[i], disp); break; case I_V_EMR: /* extended mem ref */ fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); if (val[1] & I_IA) fprintf (of, ",I"); return -1; /* extra word */ case I_V_IO1: /* IOT with H/C */ fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK); if (inst & I_HC) fprintf (of, ",C"); break; case I_V_IO2: /* IOT */ fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK); break; case I_V_EGZ: /* ext grp 1 op + 0 */ fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); if (val[1] & I_IA) fprintf (of, ",I"); return -2; /* extra words */ case I_V_EG2: /* ext grp 2 op */ fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); if (val[1] & I_IA) fprintf (of, ",I"); fprintf (of, " %-o", val[2] & VAMASK); if (val[2] & I_IA) fprintf (of, ",I"); return -2; /* extra words */ case I_V_ALT: /* alternate use instr */ if ((inst >= 0105354) && (inst <= 0105357) && /* RTE-6/VM OS range? */ (addr >= 2) && (addr <= 077)) /* in trap cell? */ continue; /* use alternate mnemonic */ else if ((inst >= 0105240) && /* RTE-6/VM VMA range? */ (inst <= 0105257) && (cpu_unit.flags & UNIT_EMA)) /* EMA enabled? */ continue; /* use EMA mnemonics */ else fprintf (of, "%s", opcode[i]); /* print opcode */ break; } return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* Get address with indirection Inputs: *cptr = pointer to input string Outputs: val = address -1 if error */ int32 get_addr (char *cptr) { int32 d; t_stat r; char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, ','); /* get next field */ d = get_uint (gbuf, 8, VAMASK, &r); /* construe as addr */ if (r != SCPE_OK) return -1; if (*cptr != 0) { /* more? */ cptr = get_glyph (cptr, gbuf, 0); /* look for indirect */ if (*cptr != 0) return -1; /* should be done */ if (strcmp (gbuf, "I")) return -1; /* I? */ d = d | I_IA; } return d; } /* Symbolic input Inputs: *iptr = 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 *iptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 cflag, d, i, j, k, clef, tbits; t_stat r, ret; char *cptr, gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*iptr)) iptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*iptr == '\'') && iptr++)) { /* ASCII char? */ if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = (t_value) iptr[0] & 0177; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*iptr == '"') && iptr++)) { /* char string? */ if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ val[0] = (((t_value) iptr[0] & 0177) << 8) | ((t_value) iptr[1] & 0177); return SCPE_OK; } ret = SCPE_OK; cptr = get_glyph (iptr, gbuf, 0); /* get opcode */ for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; if (opcode[i]) { /* found opcode? */ 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_NPN: /* no operand */ break; case I_V_NPC: /* no operand + C */ if (*cptr != 0) { cptr = get_glyph (cptr, gbuf, 0); if (strcmp (gbuf, "C")) return SCPE_ARG; val[0] = val[0] | I_HC; } break; case I_V_MRF: /* mem ref */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if (k = (strcmp (gbuf, "C") == 0)) { /* C specified? */ val[0] = val[0] | I_CP; cptr = get_glyph (cptr, gbuf, 0); } else if (k = (strcmp (gbuf, "Z") == 0)) { /* Z specified? */ cptr = get_glyph (cptr, gbuf, ','); } if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; if ((d & VAMASK) <= I_DISP) val[0] = val[0] | d; else if (cflag && !k && (((addr ^ d) & I_PAGENO) == 0)) val[0] = val[0] | (d & (I_IA | I_DISP)) | I_CP; else return SCPE_ARG; break; case I_V_ESH: /* extended shift */ cptr = get_glyph (cptr, gbuf, 0); d = get_uint (gbuf, 10, 16, &r); if ((r != SCPE_OK) || (d == 0)) return SCPE_ARG; val[0] = val[0] | (d & 017); break; case I_V_EMR: /* extended mem ref */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; val[1] = d; ret = -1; break; case I_V_IO1: /* IOT + optional C */ cptr = get_glyph (cptr, gbuf, ','); /* get device */ d = get_uint (gbuf, 8, I_DEVMASK, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; if (*cptr != 0) { cptr = get_glyph (cptr, gbuf, 0); if (strcmp (gbuf, "C")) return SCPE_ARG; val[0] = val[0] | I_HC; } break; case I_V_IO2: /* IOT */ cptr = get_glyph (cptr, gbuf, 0); /* get device */ d = get_uint (gbuf, 8, I_DEVMASK, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; break; case I_V_EGZ: /* ext grp 1 op + 0 */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; val[1] = d; val[2] = 0; ret = -2; break; case I_V_EG2: /* ext grp 2 op */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get next field */ if ((k = get_addr (gbuf)) < 0) return SCPE_ARG; val[1] = d; val[2] = k; ret = -2; break; } /* end case */ if (*cptr != 0) return SCPE_ARG; /* junk at end? */ return ret; } /* end if opcode */ /* Shift or alter-skip Each opcode is matched by a mask, specifiying the bits affected, and the value, specifying the value. As opcodes are processed, the mask values are used to specify which fields have already been filled in. The mask has two subfields, the type bits (A/B and A/S), and the field bits. The type bits, once specified by any instruction, must be consistent in all other instructions. The mask bits assure that no field is filled in twice. Two special cases: 1. The dual shift field in shift requires checking how much of the target word has been filled in before assigning the shift value. To implement this, shifts are listed twice is the decode table. If the current subopcode is a shift in the first part of the table (entries 0..15), and CLE has been seen or the first shift field is filled in, the code forces a mismatch. The glyph will match in the second part of the table. 2. CLE processing must be deferred until the instruction can be classified as shift or alter-skip, since it has two different bit values in the two classes. To implement this, CLE seen is recorded as a flag and processed after all other subopcodes. */ clef = FALSE; tbits = 0; val[0] = 0; for (cptr = get_glyph (iptr, gbuf, ','); gbuf[0] != 0; cptr = get_glyph (cptr, gbuf, ',')) { /* loop thru glyphs */ if (strcmp (gbuf, "CLE") == 0) { /* CLE? */ if (clef) return SCPE_ARG; /* already seen? */ clef = TRUE; /* set flag */ continue; } for (i = 0; stab[i] != NULL; i++) { /* find subopcode */ if ((strcmp (gbuf, stab[i]) == 0) && ((i >= 16) || (!clef && ((val[0] & 001710) == 0)))) break; } if (stab[i] == NULL) return SCPE_ARG; if (tbits & mtab[i] & (I_AB | I_ASKP) & (vtab[i] ^ val[0])) return SCPE_ARG; if (tbits & mtab[i] & ~(I_AB | I_ASKP)) return SCPE_ARG; tbits = tbits | mtab[i]; /* fill type+mask */ val[0] = val[0] | vtab[i]; /* fill value */ } if (clef) { /* CLE seen? */ if (val[0] & I_ASKP) { /* alter-skip? */ if (tbits & 0100) return SCPE_ARG; /* already filled in? */ else val[0] = val[0] | 0100; } else val[0] = val[0] | 040; /* fill in shift */ } return ret; } /* Format a character into a printable string. Control characters are translated to readable strings. Printable characters retain their original form but are enclosed in single quotes. Characters outside of the ASCII range are represented as escaped octal values. */ const char *fmt_char (uint8 ch) { static const char *const ctl [] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; static char rep [5]; if (ch <= '\037') /* ASCII control character? */ return ctl [ch]; /* return string representation */ else if (ch == '\177') /* ASCII delete? */ return "DEL"; /* return string representation */ else if (ch > '\177') { /* beyond printable range? */ sprintf (rep, "\\%03o", ch); /* format value */ return rep; /* return escaped octal code */ } else { /* printable character */ rep [0] = '\''; /* form string */ rep [1] = ch; /* containing character */ rep [2] = '\''; rep [3] = '\0'; return rep; /* return quoted character */ } } /* Set device number */ t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc) { DEVICE *dptr = (DEVICE *) desc; DIB *dibp; int32 i, newdev; t_stat r; if (cptr == NULL) return SCPE_ARG; if ((desc == NULL) || (num > 1)) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newdev = get_uint (cptr, 8, I_DEVMASK - num, &r); if (r != SCPE_OK) return r; if (newdev < VARDEV) return SCPE_ARG; for (i = 0; i <= num; i++, dibp++) dibp->devno = newdev + i; return SCPE_OK; } /* Show device number */ t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc) { DEVICE *dptr = (DEVICE *) desc; DIB *dibp; int32 i; if ((desc == NULL) || (num > 1)) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "devno=%o", dibp->devno); for (i = 1; i <= num; i++) fprintf (st, "/%o", dibp->devno + i); return SCPE_OK; } simh-3.8.1/HP2100/hp2100_ms.c0000644000175000017500000015124611107411526013237 0ustar vlmvlm/* hp2100_ms.c: HP 2100 13181A/13183A magnetic tape simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. MS 13181A 7970B 800bpi nine track magnetic tape 13183A 7970E 1600bpi nine track magnetic tape 11-Aug-08 JDB Revised to use AR instead of saved_AR in boot 26-Jun-08 JDB Rewrote device I/O to model backplane signals 28-Dec-06 JDB Added ioCRS state to I/O decoders 18-Sep-06 JDB Fixed 2nd CLR after WC causing another write Improve debug reporting, add debug flags 14-Sep-06 JDB Removed local BOT flag, now uses sim_tape_bot 30-Aug-06 JDB Added erase gap support, improved tape lib err reporting 07-Jul-06 JDB Added CAPACITY as alternate for REEL Fixed EOT test for unlimited reel size 16-Feb-06 RMS Revised for new EOT test 22-Jul-05 RMS Fixed compiler warning on Solaris (from Doug Glyn) 01-Mar-05 JDB Added SET OFFLINE; rewind/offline now does not detach 07-Oct-04 JDB Fixed enable/disable from either device 14-Aug-04 JDB Fixed many functional and timing problems (from Dave Bryan) - fixed erroneous execution of rejected command - fixed erroneous execution of select-only command - fixed erroneous execution of clear command - fixed odd byte handling for read - fixed spurious odd byte status on 13183A EOF - modified handling of end of medium - added detailed timing, with fast and realistic modes - added reel sizes to simulate end of tape - added debug printouts 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Fixed SR setting in IBL Revised IBL loader Implemented DMA SRQ (follows FLG) 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library 18-Oct-02 RMS Added BOOT command, added 13183A support 30-Sep-02 RMS Revamped error handling 29-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length test References: - 13181B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual (13181-90901, Nov-1982) - 13183B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual (13183-90901, Nov-1983) - SIMH Magtape Representation and Handling (Bob Supnik, 30-Aug-2006) */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "sim_tape.h" #define UNIT_V_OFFLINE (MTUF_V_UF + 0) /* unit offline */ #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) #define MS_NUMDR 4 /* number of drives */ #define DB_N_SIZE 16 /* max data buf */ #define DBSIZE (1 << DB_N_SIZE) /* max data cmd */ #define FNC u3 /* function */ #define UST u4 /* unit status */ #define REEL u5 /* tape reel size */ #define BPI_13181 800 /* 800 bpi for 13181 cntlr */ #define BPI_13183 1600 /* 1600 bpi for 13183 cntlr */ #define GAP_13181 48 /* gap is 4.8 inches for 13181 cntlr */ #define GAP_13183 30 /* gap is 3.0 inches for 13183 cntlr */ #define TCAP (300 * 12 * 800) /* 300 ft capacity at 800 bpi */ /* Debug flags */ #define DEB_CMDS (1 << 0) /* command init and compl */ #define DEB_CPU (1 << 1) /* CPU I/O */ #define DEB_RWS (1 << 2) /* tape reads, writes, status */ /* Command - msc_fnc */ #define FNC_CLR 00110 /* clear */ #define FNC_GAP 00015 /* write gap */ #define FNC_GFM 00215 /* gap+file mark */ #define FNC_RC 00023 /* read */ #define FNC_WC 00031 /* write */ #define FNC_FSR 00003 /* forward space */ #define FNC_BSR 00041 /* backward space */ #define FNC_FSF 00203 /* forward file */ #define FNC_BSF 00241 /* backward file */ #define FNC_REW 00101 /* rewind */ #define FNC_RWS 00105 /* rewind and offline */ #define FNC_WFM 00211 /* write file mark */ #define FNC_RFF 00223 /* read file fwd (diag) */ #define FNC_RRR 00061 /* read record rev (diag) */ #define FNC_CMPL 00400 /* completion state */ #define FNC_V_SEL 9 /* select */ #define FNC_M_SEL 017 #define FNC_GETSEL(x) (((x) >> FNC_V_SEL) & FNC_M_SEL) #define FNF_MOT 00001 /* motion */ #define FNF_OFL 00004 #define FNF_WRT 00010 /* write */ #define FNF_REV 00040 /* reverse */ #define FNF_RWD 00100 /* rewind */ #define FNF_CHS 00400 /* change select */ #define FNC_SEL ((FNC_M_SEL << FNC_V_SEL) | FNF_CHS) /* Status - stored in msc_sta, unit.UST (u), or dynamic (d) */ #define STA_PE 0100000 /* 1600 bpi (d) */ #define STA_V_SEL 13 /* unit sel (d) */ #define STA_M_SEL 03 #define STA_SEL (STA_M_SEL << STA_V_SEL) #define STA_ODD 0004000 /* odd bytes */ #define STA_REW 0002000 /* rewinding (u) */ #define STA_TBSY 0001000 /* transport busy (d) */ #define STA_BUSY 0000400 /* ctrl busy */ #define STA_EOF 0000200 /* end of file */ #define STA_BOT 0000100 /* beg of tape (d) */ #define STA_EOT 0000040 /* end of tape (d) */ #define STA_TIM 0000020 /* timing error */ #define STA_REJ 0000010 /* programming error */ #define STA_WLK 0000004 /* write locked (d) */ #define STA_PAR 0000002 /* parity error */ #define STA_LOCAL 0000001 /* local (d) */ #define STA_DYN (STA_PE | STA_SEL | STA_TBSY | STA_BOT | \ STA_EOT | STA_WLK | STA_LOCAL) enum { A13181, A13183 } ms_ctype = A13181; /* ctrl type */ int32 ms_timing = 1; /* timing type */ FLIP_FLOP msc_control = CLEAR; FLIP_FLOP msc_flag = CLEAR; FLIP_FLOP msc_flagbuf = CLEAR; int32 msc_sta = 0; /* status */ int32 msc_buf = 0; /* buffer */ int32 msc_usl = 0; /* unit select */ int32 msc_1st = 0; /* first service */ int32 msc_stopioe = 1; /* stop on error */ FLIP_FLOP msd_control = CLEAR; FLIP_FLOP msd_flag = CLEAR; FLIP_FLOP msd_flagbuf = CLEAR; int32 msd_buf = 0; /* data buffer */ uint8 msxb[DBSIZE] = { 0 }; /* data buffer */ t_mtrlnt ms_ptr = 0, ms_max = 0; /* buffer ptrs */ /* Hardware timing at 45 IPS 13181 13183 (based on 1580 instr/msec) instr msec SCP instr msec SCP -------------------- -------------------- - BOT start delay : btime = 161512 102.22 184 252800 160.00 288 - motion cmd start delay : ctime = 14044 8.89 16 17556 11.11 20 - GAP traversal time : gtime = 175553 111.11 200 105333 66.67 120 - IRG traversal time : itime = 24885 15.75 - 27387 17.33 - - rewind initiation time : rtime = 878 0.56 1 878 0.56 1 - data xfer time / word : xtime = 88 55.56us - 44 27.78us - NOTE: The 13181-60001 Rev. 1629 tape diagnostic fails test 17B subtest 6 with "E116 BYTE TIME SHORT" if the correct data transfer time is used for 13181A interface. Set "xtime" to 115 (instructions) to pass that diagnostic. Rev. 2040 of the tape diagnostic fixes this problem and passes with the correct data transfer time. */ int32 msc_btime = 0; /* BOT start delay */ int32 msc_ctime = 0; /* motion cmd start delay */ int32 msc_gtime = 0; /* GAP traversal time */ int32 msc_itime = 0; /* IRG traversal time */ int32 msc_rtime = 0; /* rewind initiation time */ int32 msc_xtime = 0; /* data xfer time / word */ typedef int32 TIMESET[6]; /* set of controller times */ int32 *const timers[] = { &msc_btime, &msc_ctime, &msc_gtime, &msc_itime, &msc_rtime, &msc_xtime }; const TIMESET msc_times[3] = { { 161512, 14044, 175553, 24885, 878, 88 }, /* 13181A */ { 252800, 17556, 105333, 27387, 878, 44 }, /* 13183A */ { 1, 1000, 1, 1, 100, 10 } /* FAST */ }; DEVICE msd_dev, msc_dev; uint32 msdio (uint32 select_code, IOSIG signal, uint32 data); uint32 mscio (uint32 select_code, IOSIG signal, uint32 data); t_stat msc_svc (UNIT *uptr); t_stat msc_reset (DEVICE *dptr); t_stat msc_attach (UNIT *uptr, char *cptr); t_stat msc_detach (UNIT *uptr); t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat msc_boot (int32 unitno, DEVICE *dptr); t_stat ms_write_gap (UNIT *uptr); t_stat ms_map_err (UNIT *uptr, t_stat st); t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc); void ms_config_timing (void); char *ms_cmd_name (uint32 cmd); t_stat ms_clear (void); /* MSD data structures msd_dev MSD device descriptor msd_unit MSD unit list msd_reg MSD register list */ DIB ms_dib[] = { { MSD, &msdio }, { MSC, &mscio } }; #define msd_dib ms_dib[0] #define msc_dib ms_dib[1] UNIT msd_unit = { UDATA (NULL, 0, 0) }; REG msd_reg[] = { { ORDATA (BUF, msd_buf, 16) }, { FLDATA (CTL, msd_control, 0) }, { FLDATA (FLG, msd_flag, 0) }, { FLDATA (FBF, msd_flagbuf, 0) }, { BRDATA (DBUF, msxb, 8, 8, DBSIZE) }, { DRDATA (BPTR, ms_ptr, DB_N_SIZE + 1) }, { DRDATA (BMAX, ms_max, DB_N_SIZE + 1) }, { ORDATA (DEVNO, msd_dib.devno, 6), REG_HRO }, { NULL } }; MTAB msd_mod[] = { { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &msd_dev }, { 0 } }; DEVICE msd_dev = { "MSD", &msd_unit, msd_reg, msd_mod, 1, 10, DB_N_SIZE, 1, 8, 8, NULL, NULL, &msc_reset, NULL, NULL, NULL, &msd_dib, DEV_DISABLE }; /* MSC data structures msc_dev MSC device descriptor msc_unit MSC unit list msc_reg MSC register list msc_mod MSC modifier list msc_deb MSC debug flags */ UNIT msc_unit[] = { { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_OFFLINE, 0) }, { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_OFFLINE, 0) }, { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_OFFLINE, 0) }, { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_OFFLINE, 0) } }; REG msc_reg[] = { { ORDATA (STA, msc_sta, 12) }, { ORDATA (BUF, msc_buf, 16) }, { ORDATA (USEL, msc_usl, 2) }, { FLDATA (FSVC, msc_1st, 0) }, { FLDATA (CTL, msc_control, 0) }, { FLDATA (FLG, msc_flag, 0) }, { FLDATA (FBF, msc_flagbuf, 0) }, { URDATA (POS, msc_unit[0].pos, 10, T_ADDR_W, 0, MS_NUMDR, PV_LEFT) }, { URDATA (FNC, msc_unit[0].FNC, 8, 8, 0, MS_NUMDR, REG_HRO) }, { URDATA (UST, msc_unit[0].UST, 8, 12, 0, MS_NUMDR, REG_HRO) }, { URDATA (REEL, msc_unit[0].REEL, 10, 2, 0, MS_NUMDR, REG_HRO) }, { DRDATA (BTIME, msc_btime, 24), REG_NZ + PV_LEFT }, { DRDATA (CTIME, msc_ctime, 24), REG_NZ + PV_LEFT }, { DRDATA (GTIME, msc_gtime, 24), REG_NZ + PV_LEFT }, { DRDATA (ITIME, msc_itime, 24), REG_NZ + PV_LEFT }, { DRDATA (RTIME, msc_rtime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, msc_xtime, 24), REG_NZ + PV_LEFT }, { FLDATA (TIMING, ms_timing, 0), REG_HRO }, { FLDATA (STOP_IOE, msc_stopioe, 0) }, { FLDATA (CTYPE, ms_ctype, 0), REG_HRO }, { ORDATA (DEVNO, msc_dib.devno, 6), REG_HRO }, { NULL } }; MTAB msc_mod[] = { { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, { UNIT_OFFLINE, 0, "online", "ONLINE", msc_online }, { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VUN, 0, "CAPACITY", "CAPACITY", &ms_set_reelsize, &ms_show_reelsize, NULL }, { MTAB_XTD | MTAB_VUN | MTAB_NMO, 1, "REEL", "REEL", &ms_set_reelsize, &ms_show_reelsize, NULL }, { MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "13181A", &ms_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "13183A", &ms_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &ms_showtype, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "REALTIME", &ms_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "FASTTIME", &ms_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, NULL, &ms_show_timing, NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &msd_dev }, { 0 } }; DEBTAB msc_deb[] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "RWS", DEB_RWS }, { NULL, 0 } }; DEVICE msc_dev = { "MSC", msc_unit, msc_reg, msc_mod, MS_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &msc_reset, &msc_boot, &msc_attach, &msc_detach, &msc_dib, DEV_DISABLE | DEV_DEBUG, 0, msc_deb, NULL, NULL }; /* Data channel I/O signal handler */ uint32 msdio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ msd_flag = msd_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ msd_flag = msd_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (msd); break; case ioSFS: /* skip if flag is set */ setstdSKF (msd); break; case ioIOI: /* I/O data input */ data = msd_buf; break; case ioIOO: /* I/O data output */ msd_buf = data; /* store data */ break; case ioPOPIO: /* power-on preset to I/O */ ms_clear (); /* issue CLR to controller */ /* fall into CRS handler */ case ioCRS: /* control reset */ msd_flag = msd_flagbuf = SET; /* set flag and flag buffer */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ msd_control = CLEAR; break; case ioSTC: /* set control flip-flop */ msd_control = SET; break; case ioEDT: /* end data transfer */ msd_flag = msd_flagbuf = CLEAR; /* same as CLF */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, msd); /* set standard PRL signal */ setstdIRQ (select_code, msd); /* set standard IRQ signal */ setstdSRQ (select_code, msd); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ msd_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ msdio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ msdio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command channel I/O signal handler. Implementation notes: 1. Commands are usually initiated with an STC cc,C instruction. The CLR command completes immediately and sets the flag. This requires that we ignore the CLF part (but still process the SIR). 2. The command channel card clears its flag and flag buffer on EDT, but as it never asserts SRQ, it will never get EDT. Under simulation, we omit the EDT handler. 3. In hardware, the command channel card passes PRH to PRL. The data card actually drives PRL with both channels' control and flag states. That is, the priority chain is broken at the data card, even when the command card is interrupting. This works in hardware, but we must break PRL at the command card under simulation to allow the command card to interrupt. */ uint32 mscio (uint32 select_code, IOSIG signal, uint32 data) { static const uint8 map_sel[16] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 sched_time; UNIT *uptr = msc_dev.units + msc_usl; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ msc_flag = msc_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ msc_flag = msc_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (msc); break; case ioSFS: /* skip if flag is set */ setstdSKF (msc); break; case ioIOI: /* I/O data input */ data = msc_sta & ~STA_DYN; /* get card status */ if ((uptr->flags & UNIT_OFFLINE) == 0) { /* online? */ data = data | uptr->UST; /* add unit status */ if (sim_tape_bot (uptr)) /* BOT? */ data = data | STA_BOT; if (sim_is_active (uptr) && /* TBSY unless RWD at BOT */ !((uptr->FNC & FNF_RWD) && sim_tape_bot (uptr))) data = data | STA_TBSY; if (sim_tape_wrp (uptr)) /* write prot? */ data = data | STA_WLK; if (sim_tape_eot (uptr)) /* EOT? */ data = data | STA_EOT; } else data = data | STA_TBSY | STA_LOCAL; if (ms_ctype == A13183) /* 13183A? */ data = data | STA_PE | (msc_usl << STA_V_SEL); if (DEBUG_PRI (msc_dev, DEB_CPU)) fprintf (sim_deb, ">>MSC LIx: Status = %06o\n", data); break; case ioIOO: /* I/O data output */ if (DEBUG_PRI (msc_dev, DEB_CPU)) fprintf (sim_deb, ">>MSC OTx: Command = %06o\n", data); msc_buf = data; msc_sta = msc_sta & ~STA_REJ; /* clear reject */ if ((data & 0377) == FNC_CLR) /* clear always ok */ break; if (msc_sta & STA_BUSY) { /* busy? reject */ msc_sta = msc_sta | STA_REJ; /* dont chg select */ break; } if (data & FNF_CHS) { /* select change */ msc_usl = map_sel[FNC_GETSEL (data)]; /* is immediate */ uptr = msc_dev.units + msc_usl; if (DEBUG_PRI (msc_dev, DEB_CMDS)) fprintf (sim_deb, ">>MSC OTx: Unit %d selected\n", msc_usl); } if (((data & FNF_MOT) && sim_is_active (uptr)) || ((data & FNF_REV) && sim_tape_bot (uptr)) || ((data & FNF_WRT) && sim_tape_wrp (uptr))) msc_sta = msc_sta | STA_REJ; /* reject? */ break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ msc_flag = msc_flagbuf = SET; /* set flag and flag buffer */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ msc_control = CLEAR; break; case ioSTC: /* set control flip-flop */ if (!(msc_sta & STA_REJ)) { /* last cmd rejected? */ if ((msc_buf & 0377) == FNC_CLR) { /* clear? */ ms_clear (); /* issue CLR to controller */ msc_control = SET; /* set CTL for STC */ msc_flag = msc_flagbuf = SET; /* set FLG for completion */ signal = ioSTC; /* eliminate possible CLF */ if (DEBUG_PRI (msc_dev, DEB_CMDS)) fputs (">>MSC STC: Controller cleared\n", sim_deb); break; /* command completes immediately */ } uptr->FNC = msc_buf & 0377; /* save function */ if (uptr->FNC & FNF_RWD) { /* rewind? */ if (!sim_tape_bot (uptr)) /* not at BOT? */ uptr->UST = STA_REW; /* set rewinding */ sched_time = msc_rtime; /* set response time */ } else { if (sim_tape_bot (uptr)) /* at BOT? */ sched_time = msc_btime; /* use BOT start time */ else if ((uptr->FNC == FNC_GAP) || (uptr->FNC == FNC_GFM)) sched_time = msc_gtime; /* use gap traversal time */ else sched_time = 0; if (uptr->FNC != FNC_GAP) sched_time += msc_ctime; /* add base command time */ } if (msc_buf & ~FNC_SEL) { /* NOP for unit sel alone */ sim_activate (uptr, sched_time); /* else schedule op */ if (DEBUG_PRI (msc_dev, DEB_CMDS)) fprintf (sim_deb, ">>MSC STC: Unit %d command %03o (%s) scheduled, " "pos = %d, time = %d\n", msc_usl, uptr->FNC, ms_cmd_name (uptr->FNC), uptr->pos, sched_time); } else if (DEBUG_PRI (msc_dev, DEB_CMDS)) fputs (">>MSC STC: Unit select (NOP)\n", sim_deb); msc_sta = STA_BUSY; /* ctrl is busy */ msc_1st = 1; msc_control = SET; /* go */ } break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, msc); /* set standard PRL signal */ setstdIRQ (select_code, msc); /* set standard IRQ signal */ setstdSRQ (select_code, msc); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ msc_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ mscio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ mscio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt. In addition to decreasing the timing intervals, the FASTTIME option enables two additional optimizations: WFM for GFM substitution, and BOT gap elimination. If FASTTIME is selected, gap and file mark (GFM) commands are processed as WFM (write file mark) commands. That is, the preceding GAP is not performed. Also, the initial gap that normally precedes the first data record or EOF mark at the beginning of the tape is omitted. These omissions result in smaller tape image files. If REALTIME is selected, the gaps are included. Note that the gaps (and realistic timing) are necessary to pass the 7970 diagnostics. */ t_stat msc_svc (UNIT *uptr) { int32 unum; t_mtrlnt tbc; t_stat st, r = SCPE_OK; unum = uptr - msc_dev.units; /* get unit number */ if ((uptr->FNC != FNC_RWS) && (uptr->flags & UNIT_OFFLINE)) { /* offline? */ msc_sta = (msc_sta | STA_REJ) & ~STA_BUSY; /* reject */ mscio (msc_dib.devno, ioENF, 0); /* set flag */ return IORETURN (msc_stopioe, SCPE_UNATT); } switch (uptr->FNC) { /* case on function */ case FNC_RWS: /* rewind offline */ sim_tape_rewind (uptr); /* rewind tape */ uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */ uptr->UST = 0; /* clear REW status */ break; /* we're done */ case FNC_REW: /* rewind */ if (uptr->UST & STA_REW) { /* rewind in prog? */ uptr->FNC |= FNC_CMPL; /* set compl state */ sim_activate (uptr, msc_ctime); /* sched completion */ } break; /* anyway, ctrl done */ case FNC_REW | FNC_CMPL: /* complete rewind */ sim_tape_rewind (uptr); /* rewind tape */ uptr->UST = 0; /* clear REW status */ return SCPE_OK; /* drive is free */ case FNC_GFM: /* gap + file mark */ if (ms_timing == 1) /* fast timing? */ goto DO_WFM; /* do plain file mark */ /* else fall into GAP */ case FNC_GAP: /* erase gap */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d wrote gap\n", unum); if ((r = ms_write_gap (uptr)) || /* write tape gap; error? */ (uptr->FNC != FNC_GFM)) /* not GFM? */ break; /* bail out now */ /* else drop into WFM */ case FNC_WFM: /* write file mark */ if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d wrote initial gap\n", unum); if (st = ms_write_gap (uptr)) { /* write initial gap; error? */ r = ms_map_err (uptr, st); /* map error */ break; /* terminate operation */ } } DO_WFM: if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d wrote file mark\n", unum); if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = ms_map_err (uptr, st); /* map error */ msc_sta = STA_EOF; /* set EOF status */ break; case FNC_FSR: /* space forward */ if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ r = ms_map_err (uptr, st); /* map error */ if (tbc & 1) msc_sta = msc_sta | STA_ODD; else msc_sta = msc_sta & ~STA_ODD; break; case FNC_BSR: /* space reverse */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ r = ms_map_err (uptr, st); /* map error */ if (tbc & 1) msc_sta = msc_sta | STA_ODD; else msc_sta = msc_sta & ~STA_ODD; break; case FNC_FSF: /* space fwd file */ while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) { if (sim_tape_eot (uptr)) break; /* EOT stops */ } r = ms_map_err (uptr, st); /* map error */ break; case FNC_BSF: /* space rev file */ while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; r = ms_map_err (uptr, st); /* map error */ break; case FNC_RFF: /* diagnostic read */ case FNC_RC: /* read */ if (msc_1st) { /* first svc? */ msc_1st = ms_ptr = ms_max = 0; /* clr 1st flop */ st = sim_tape_rdrecf (uptr, msxb, &ms_max, DBSIZE); /* read rec */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d read %d word record\n", unum, ms_max / 2); if (st == MTSE_RECE) msc_sta = msc_sta | STA_PAR; /* rec in err? */ else if (st != MTSE_OK) { /* other error? */ r = ms_map_err (uptr, st); /* map error */ if (r == SCPE_OK) { /* recoverable? */ sim_activate (uptr, msc_itime); /* sched IRG */ uptr->FNC |= FNC_CMPL; /* set completion */ return SCPE_OK; } break; /* err, done */ } if (ms_ctype == A13183) msc_sta = msc_sta | STA_ODD; /* set ODD for 13183A */ } if (msd_control && (ms_ptr < ms_max)) { /* DCH on, more data? */ if (msd_flag) msc_sta = msc_sta | STA_TIM | STA_PAR; msd_buf = ((uint16) msxb[ms_ptr] << 8) | ((ms_ptr + 1 == ms_max) ? 0 : msxb[ms_ptr + 1]); ms_ptr = ms_ptr + 2; msdio (msd_dib.devno, ioENF, 0); /* set flag */ sim_activate (uptr, msc_xtime); /* re-activate */ return SCPE_OK; } if (ms_max & 1) msc_sta = msc_sta | STA_ODD; /* set ODD by rec len */ else msc_sta = msc_sta & ~STA_ODD; sim_activate (uptr, msc_itime); /* sched IRG */ if (uptr->FNC == FNC_RFF) msc_1st = 1; /* diagnostic? */ else uptr->FNC |= FNC_CMPL; /* set completion */ return SCPE_OK; case FNC_RFF | FNC_CMPL: /* diagnostic read completion */ case FNC_RC | FNC_CMPL: /* read completion */ break; case FNC_WC: /* write */ if (msc_1st) { /* first service? */ msc_1st = ms_ptr = 0; /* no data xfer on first svc */ if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d wrote initial gap\n", unum); if (st = ms_write_gap (uptr)) { /* write initial gap; error? */ r = ms_map_err (uptr, st); /* map error */ break; /* terminate operation */ } } } else { /* not 1st, next char */ if (ms_ptr < DBSIZE) { /* room in buffer? */ msxb[ms_ptr] = msd_buf >> 8; /* store 2 char */ msxb[ms_ptr + 1] = msd_buf & 0377; ms_ptr = ms_ptr + 2; } else msc_sta = msc_sta | STA_PAR; } if (msd_control) { /* xfer flop set? */ msdio (msd_dib.devno, ioENF, 0); /* set flag */ sim_activate (uptr, msc_xtime); /* re-activate */ return SCPE_OK; } if (ms_ptr) { /* any data? write */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC svc: Unit %d wrote %d word record\n", unum, ms_ptr / 2); if (st = sim_tape_wrrecf (uptr, msxb, ms_ptr)) { /* write, err? */ r = ms_map_err (uptr, st); /* map error */ break; } } sim_activate (uptr, msc_itime); /* sched IRG */ uptr->FNC |= FNC_CMPL; /* set completion */ return SCPE_OK; case FNC_WC | FNC_CMPL: /* write completion */ break; case FNC_RRR: /* not supported */ default: /* unknown command */ if (DEBUG_PRI (msc_dev, DEB_CMDS)) fprintf (sim_deb, ">>MSC svc: Unit %d command %03o is unknown (NOP)\n", unum, uptr->FNC); break; } mscio (msc_dib.devno, ioENF, 0); /* set flag */ msc_sta = msc_sta & ~STA_BUSY; /* update status */ if (DEBUG_PRI (msc_dev, DEB_CMDS)) fprintf (sim_deb, ">>MSC svc: Unit %d command %03o (%s) complete\n", unum, uptr->FNC & 0377, ms_cmd_name (uptr->FNC)); return r; } /* Write an erase gap */ t_stat ms_write_gap (UNIT *uptr) { t_stat st; uint32 gap_len = ms_ctype ? GAP_13183 : GAP_13181; /* establish gap length */ uint32 tape_bpi = ms_ctype ? BPI_13183 : BPI_13181; /* establish nominal bpi */ if (st = sim_tape_wrgap (uptr, gap_len, tape_bpi)) /* write gap */ return ms_map_err (uptr, st); /* map error if failure */ else return SCPE_OK; } /* Map tape error status */ t_stat ms_map_err (UNIT *uptr, t_stat st) { int32 unum = uptr - msc_dev.units; /* get unit number */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC err: Unit %d tape library status = %d\n", unum, st); switch (st) { case MTSE_FMT: /* illegal fmt */ msc_sta = msc_sta | STA_REJ; /* reject cmd */ return SCPE_FMT; /* format error */ case MTSE_UNATT: /* unattached */ msc_detach (uptr); /* resync status (ignore rtn) */ msc_sta = msc_sta | STA_REJ; /* reject cmd */ return SCPE_UNATT; /* unit unattached */ case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_EOM: /* end of medium */ case MTSE_TMK: /* end of file */ msc_sta = msc_sta | STA_EOF; if (ms_ctype == A13181) msc_sta = msc_sta | STA_ODD; /* EOF also sets ODD for 13181A */ break; case MTSE_INVRL: /* invalid rec lnt */ msc_sta = msc_sta | STA_PAR; return SCPE_MTRLNT; case MTSE_IOERR: /* IO error */ msc_sta = msc_sta | STA_PAR; /* error */ if (msc_stopioe) return SCPE_IOERR; break; case MTSE_RECE: /* record in error */ msc_sta = msc_sta | STA_PAR; /* error */ break; case MTSE_WRP: /* write protect */ msc_sta = msc_sta | STA_REJ; /* reject */ break; } return SCPE_OK; } /* Controller clear */ t_stat ms_clear (void) { int32 i; t_stat st; UNIT *uptr; for (i = 0; i < MS_NUMDR; i++) { /* look for write in progr */ uptr = &msc_unit [i]; /* get pointer to unit */ if (sim_is_active (uptr) && /* unit active? */ (uptr->FNC == FNC_WC) && /* and last cmd write? */ (ms_ptr > 0)) { /* and partial buffer? */ if (DEBUG_PRI (msc_dev, DEB_RWS)) fprintf (sim_deb, ">>MSC rws: Unit %d wrote %d word partial record\n", i, ms_ptr / 2); if (st = sim_tape_wrrecf (uptr, msxb, ms_ptr | MTR_ERF)) ms_map_err (uptr, st); /* discard any error */ ms_ptr = 0; /* clear partial */ } if ((uptr->UST & STA_REW) == 0) sim_cancel (uptr); /* stop if not rew */ } msc_sta = msc_1st = 0; /* clr ctlr status */ return SCPE_OK; } /* Reset routine */ t_stat msc_reset (DEVICE *dptr) { int32 i; UNIT *uptr; hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &msd_dev) ? &msc_dev : &msd_dev); if (sim_switches & SWMASK ('P')) /* PON reset? */ ms_config_timing (); if (dptr == &msc_dev) /* command channel reset? */ mscio (msc_dib.devno, ioPOPIO, 0); /* send POPIO signal to command channel */ else /* data channel reset */ msdio (msd_dib.devno, ioPOPIO, 0); /* send POPIO signal to data channel */ msc_buf = msd_buf = 0; msc_sta = msc_usl = 0; msc_1st = 0; for (i = 0; i < MS_NUMDR; i++) { uptr = msc_dev.units + i; sim_tape_reset (uptr); sim_cancel (uptr); uptr->UST = 0; } return SCPE_OK; } /* Attach routine */ t_stat msc_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); /* attach unit */ if (r == SCPE_OK) uptr->flags = uptr->flags & ~UNIT_OFFLINE; /* set online */ return r; } /* Detach routine */ t_stat msc_detach (UNIT* uptr) { uptr->UST = 0; /* clear status */ uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */ return sim_tape_detach (uptr); /* detach unit */ } /* Online routine */ t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_OK; else return SCPE_UNATT; } /* Configure timing */ void ms_config_timing (void) { uint32 i, tset; tset = (ms_timing << 1) | (ms_timing ? 0 : ms_ctype); /* select timing set */ for (i = 0; i < (sizeof (timers) / sizeof (timers[0])); i++) *timers[i] = msc_times[tset][i]; /* assign times */ } /* Set controller timing */ t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc) { if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; ms_timing = val; ms_config_timing (); return SCPE_OK; } /* Show controller timing */ t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc) { if (ms_timing) fputs ("fast timing", st); else fputs ("realistic timing", st); return SCPE_OK; } /* Set controller type */ t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; for (i = 0; i < MS_NUMDR; i++) { if (msc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } ms_ctype = val; ms_config_timing (); /* update for new type */ return SCPE_OK; } /* Show controller type */ t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) { if (ms_ctype == A13183) fprintf (st, "13183A"); else fprintf (st, "13181A"); return SCPE_OK; } /* Set unit reel size val = 0 -> SET MSCn CAPACITY=n val = 1 -> SET MSCn REEL=n */ t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 reel; t_stat status; if (val == 0) { status = sim_tape_set_capac (uptr, val, cptr, desc); if (status == SCPE_OK) uptr->REEL = 0; return status; } if (cptr == NULL) return SCPE_ARG; reel = (int32) get_uint (cptr, 10, 2400, &status); if (status != SCPE_OK) return status; else switch (reel) { case 0: uptr->REEL = 0; /* type 0 = unlimited/custom */ break; case 600: uptr->REEL = 1; /* type 1 = 600 foot */ break; case 1200: uptr->REEL = 2; /* type 2 = 1200 foot */ break; case 2400: uptr->REEL = 3; /* type 3 = 2400 foot */ break; default: return SCPE_ARG; } uptr->capac = uptr->REEL ? (TCAP << uptr->REEL) << ms_ctype : 0; return SCPE_OK; } /* Show unit reel size val = 0 -> SHOW MSC or SHOW MSCn or SHOW MSCn CAPACITY val = 1 -> SHOW MSCn REEL */ t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc) { t_stat status = SCPE_OK; if (uptr->REEL == 0) status = sim_tape_show_capac (st, uptr, val, desc); else fprintf (st, "%4d foot reel", 300 << uptr->REEL); if (val == 1) fputc ('\n', st); /* MTAB_NMO omits \n */ return status; } /* Translate command to mnemonic for debug logging The command names and descriptions are taken from the 13181 interface manual. */ char *ms_cmd_name (uint32 cmd) { switch (cmd & 0377) { case FNC_WC: return "WCC"; /* Write command */ case FNC_WFM: return "WFM"; /* Write file mark */ case FNC_RC: return "RRF"; /* Read record forward */ case FNC_FSR: return "FSR"; /* Forward space record */ case FNC_FSF: return "FSF"; /* Forward space file */ case FNC_GAP: return "GAP"; /* Write gap */ case FNC_BSR: return "BSR"; /* Backspace record */ case FNC_BSF: return "BSF"; /* Backspace file */ case FNC_REW: return "REW"; /* Rewind */ case FNC_RWS: return "RWO"; /* Rewind off-line */ case FNC_CLR: return "CLR"; /* Clear controller */ case FNC_GFM: return "GFM"; /* Gap file mark */ case FNC_RFF: return "RFF"; /* Read forward until file mark (diag) */ case FNC_RRR: return "RRR"; /* Read record in reverse (diag) */ default: return "???"; /* Unknown command */ } } /* 7970B/7970E bootstrap routine (HP 12992D ROM) */ const BOOT_ROM ms_rom = { 0106501, /*ST LIB 1 ; read sw */ 0006011, /* SLB,RSS ; bit 0 set? */ 0027714, /* JMP RD ; no read */ 0003004, /* CMA,INA ; A is ctr */ 0073775, /* STA WC ; save */ 0067772, /* LDA SL0RW ; sel 0, rew */ 0017762, /*FF JSB CMD ; do cmd */ 0102311, /* SFS CC ; done? */ 0027707, /* JMP *-1 ; wait */ 0067774, /* LDB FFC ; get file fwd */ 0037775, /* ISZ WC ; done files? */ 0027706, /* JMP FF ; no */ 0067773, /*RD LDB RDCMD ; read cmd */ 0017762, /* JSB CMD ; do cmd */ 0103710, /* STC DC,C ; start dch */ 0102211, /* SFC CC ; read done? */ 0027752, /* JMP STAT ; no, get stat */ 0102310, /* SFS DC ; any data? */ 0027717, /* JMP *-3 ; wait */ 0107510, /* LIB DC,C ; get rec cnt */ 0005727, /* BLF,BLF ; move to lower */ 0007000, /* CMB ; make neg */ 0077775, /* STA WC ; save */ 0102211, /* SFC CC ; read done? */ 0027752, /* JMP STAT ; no, get stat */ 0102310, /* SFS DC ; any data? */ 0027727, /* JMP *-3 ; wait */ 0107510, /* LIB DC,C ; get load addr */ 0074000, /* STB 0 ; start csum */ 0077762, /* STA CMD ; save address */ 0027742, /* JMP *+4 */ 0177762, /*NW STB CMD,I ; store data */ 0040001, /* ADA 1 ; add to csum */ 0037762, /* ISZ CMD ; adv addr ptr */ 0102310, /* SFS DC ; any data? */ 0027742, /* JMP *-1 ; wait */ 0107510, /* LIB DC,C ; get word */ 0037775, /* ISZ WC ; done? */ 0027737, /* JMP NW ; no */ 0054000, /* CPB 0 ; csum ok? */ 0027717, /* JMP RD+3 ; yes, cont */ 0102011, /* HLT 11 ; no, halt */ 0102511, /*ST LIA CC ; get status */ 0001727, /* ALF,ALF ; get eof bit */ 0002020, /* SSA ; set? */ 0102077, /* HLT 77 ; done */ 0001727, /* ALF,ALF ; put status back */ 0001310, /* RAR,SLA ; read ok? */ 0102000, /* HLT 0 ; no */ 0027714, /* JMP RD ; read next */ 0000000, /*CMD 0 */ 0106611, /* OTB CC ; output cmd */ 0102511, /* LIA CC ; check for reject */ 0001323, /* RAR,RAR */ 0001310, /* RAR,SLA */ 0027763, /* JMP CMD+1 ; try again */ 0103711, /* STC CC,C ; start command */ 0127762, /* JMP CMD,I ; exit */ 0001501, /*SL0RW 001501 ; select 0, rewind */ 0001423, /*RDCMD 001423 ; read record */ 0000203, /*FFC 000203 ; space forward file */ 0000000, /*WC 000000 */ 0000000, 0000000 }; t_stat msc_boot (int32 unitno, DEVICE *dptr) { int32 dev; if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ dev = msd_dib.devno; /* get data chan dev */ if (ibl_copy (ms_rom, dev)) return SCPE_IERR; /* copy boot to memory */ SR = (SR & IBL_OPT) | IBL_MS | (dev << IBL_V_DEV); /* set SR */ if ((sim_switches & SWMASK ('S')) && AR) SR = SR | 1; /* skip? */ return SCPE_OK; } simh-3.8.1/HP2100/hp2100_lpt.c0000644000175000017500000003126411107405100013403 0ustar vlmvlm/* hp2100_lpt.c: HP 2100 12845B line printer simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. LPT 12845B 2607 line printer 26-Jun-08 JDB Rewrote device I/O to model backplane signals Changed CTIME register width to match documentation 22-Jan-07 RMS Added UNIT_TEXT flag 28-Dec-06 JDB Added ioCRS state to I/O decoders 19-Nov-04 JDB Added restart when set online, etc. 29-Sep-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON Fixed status returns for error conditions Fixed TOF handling so form remains on line 0 03-Jun-04 RMS Fixed timing (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Implemented DMA SRQ (follows FLG) 25-Apr-03 RMS Revised for extended file support 24-Oct-02 RMS Cloned from 12653A Reference: - 12845A Line Printer Operating and Service Manual (12845-90001, Aug-1972) The 2607 provides three status bits via the interface: bit 15 -- printer ready (online) bit 14 -- paper out bit 0 -- printer idle The expected status returns are: 140001 -- power off or cable disconnected 100001 -- power on, paper loaded, printer ready 100000 -- power on, paper loaded, printer busy 040000 -- power on, paper out (at bottom-of-form) 000000 -- power on, paper out (not at BOF) / print button up / platen open Manual Note: "2-33. PAPER OUT SIGNAL. [...] The signal is asserted only when the format tape in the line printer has reached the bottom of form." These simulator commands provide the listed printer states: SET LPT POWEROFF --> power off or cable disconnected SET LPT POWERON --> power on SET LPT OFFLINE --> print button up SET LPT ONLINE --> print button down ATT LPT --> paper loaded DET LPT --> paper out */ #include "hp2100_defs.h" #define LPT_PAGELNT 60 /* page length */ #define LPT_NBSY 0000001 /* not busy */ #define LPT_PAPO 0040000 /* paper out */ #define LPT_RDY 0100000 /* ready */ #define LPT_PWROFF LPT_RDY | LPT_PAPO | LPT_NBSY /* power-off status */ #define LPT_CTL 0100000 /* control output */ #define LPT_CHAN 0000100 /* skip to chan */ #define LPT_SKIPM 0000077 /* line count mask */ #define LPT_CHANM 0000007 /* channel mask */ #define UNIT_V_POWEROFF (UNIT_V_UF + 0) /* unit powered off */ #define UNIT_V_OFFLINE (UNIT_V_UF + 1) /* unit offline */ #define UNIT_POWEROFF (1 << UNIT_V_POWEROFF) #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) FLIP_FLOP lpt_control = CLEAR; FLIP_FLOP lpt_flag = CLEAR; FLIP_FLOP lpt_flagbuf = CLEAR; int32 lpt_ctime = 4; /* char time */ int32 lpt_ptime = 10000; /* print time */ int32 lpt_stopioe = 0; /* stop on error */ int32 lpt_lcnt = 0; /* line count */ static int32 lpt_cct[8] = { 1, 1, 1, 2, 3, LPT_PAGELNT/2, LPT_PAGELNT/4, LPT_PAGELNT/6 }; DEVICE lpt_dev; uint32 lptio (uint32 select_code, IOSIG signal, uint32 data); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat lpt_attach (UNIT *uptr, char *cptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { LPT, &lptio }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0) }; REG lpt_reg[] = { { ORDATA (BUF, lpt_unit.buf, 7) }, { FLDATA (CTL, lpt_control, 0) }, { FLDATA (FLG, lpt_flag, 0) }, { FLDATA (FBF, lpt_flagbuf, 0) }, { DRDATA (LCNT, lpt_lcnt, 7) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT }, { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { ORDATA (DEVNO, lpt_dib.devno, 6), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", NULL }, { UNIT_POWEROFF, 0, "power on", "POWERON", lpt_restart }, { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, { UNIT_OFFLINE, 0, "online", "ONLINE", lpt_restart }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &lpt_dev }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lpt_reset, NULL, &lpt_attach, NULL, &lpt_dib, DEV_DISABLE }; /* I/O signal handler */ uint32 lptio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ lpt_flag = lpt_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ lpt_flag = lpt_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (lpt); break; case ioSFS: /* skip if flag is set */ setstdSKF (lpt); break; case ioIOI: /* I/O data input */ if (lpt_unit.flags & UNIT_POWEROFF) /* power off? */ data = LPT_PWROFF; else if (!(lpt_unit.flags & UNIT_OFFLINE)) { /* online? */ if (lpt_unit.flags & UNIT_ATT) { /* paper loaded? */ data = LPT_RDY; if (!sim_is_active (&lpt_unit)) /* printer busy? */ data = data | LPT_NBSY; } else if (lpt_lcnt == LPT_PAGELNT - 1) /* paper out, at BOF? */ data = LPT_PAPO; } else data = 0; break; case ioIOO: /* I/O data output */ lpt_unit.buf = data & (LPT_CTL | 0177); break; case ioPOPIO: /* power-on preset to I/O */ lpt_flag = lpt_flagbuf = SET; /* set flag and flag buffer */ lpt_unit.buf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ lpt_control = CLEAR; break; case ioSTC: /* set control flip-flop */ lpt_control = SET; sim_activate (&lpt_unit, /* schedule op */ (lpt_unit.buf & LPT_CTL)? lpt_ptime: lpt_ctime); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, lpt); /* set standard PRL signal */ setstdIRQ (select_code, lpt); /* set standard IRQ signal */ setstdSRQ (select_code, lpt); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ lpt_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ lptio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ lptio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service */ t_stat lpt_svc (UNIT *uptr) { int32 i, skip, chan; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); else if (uptr->flags & UNIT_OFFLINE) /* offline? */ return IORETURN (lpt_stopioe, STOP_OFFLINE); else if (uptr->flags & UNIT_POWEROFF) /* powered off? */ return IORETURN (lpt_stopioe, STOP_PWROFF); lptio (lpt_dib.devno, ioENF, 0); /* set flag */ if (uptr->buf & LPT_CTL) { /* control word? */ if (uptr->buf & LPT_CHAN) { chan = uptr->buf & LPT_CHANM; if (chan == 0) { /* top of form? */ fputc ('\f', uptr->fileref); /* ffeed */ lpt_lcnt = 0; /* reset line cnt */ skip = 0; } else if (chan == 1) skip = LPT_PAGELNT - lpt_lcnt - 1; else skip = lpt_cct[chan] - (lpt_lcnt % lpt_cct[chan]); } else { skip = uptr->buf & LPT_SKIPM; if (skip == 0) fputc ('\r', uptr->fileref); } for (i = 0; i < skip; i++) fputc ('\n', uptr->fileref); lpt_lcnt = (lpt_lcnt + skip) % LPT_PAGELNT; } else fputc (uptr->buf & 0177, uptr->fileref); /* no, just add char */ if (ferror (uptr->fileref)) { perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lpt_unit.pos = ftell (uptr->fileref); /* update pos */ return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { lptio (lpt_dib.devno, ioPOPIO, 0); /* send POPIO signal */ sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } /* Restart I/O routine If I/O is started via STC, and the printer is powered off, offline, or out of paper, the CTL and CMD flip-flops will set, a service event will be scheduled, and the service routine will be entered. If STOP_IOE is not set, the I/O operation will "hang" at that point until the printer is powered on, set online, or paper is supplied (attached). If a pending operation is "hung" when this routine is called, it is restarted, which clears CTL and sets FBF and FLG, completing the original I/O request. */ t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc) { if (lpt_control && !sim_is_active (uptr)) sim_activate (uptr, 0); /* reschedule I/O */ return SCPE_OK; } /* Attach routine */ t_stat lpt_attach (UNIT *uptr, char *cptr) { lpt_lcnt = 0; /* top of form */ lpt_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ return attach_unit (uptr, cptr); } simh-3.8.1/HP2100/hp2100_dp.c0000644000175000017500000014643511107411526013227 0ustar vlmvlm/* hp2100_dp.c: HP 2100 12557A/13210A disk simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. DP 12557A 2871 disk subsystem 13210A 7900 disk subsystem 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size 26-Jun-08 JDB Rewrote device I/O to model backplane signals 28-Dec-06 JDB Added ioCRS state to I/O decoders 01-Mar-05 JDB Added SET UNLOAD/LOAD 07-Oct-04 JDB Fixed enable/disable from either device Fixed ANY ERROR status for 12557A interface Fixed unattached drive status for 12557A interface Status cmd without prior STC DC now completes (12557A) OTA/OTB CC on 13210A interface also does CLC CC Fixed RAR model Fixed seek check on 13210 if sector out of range 20-Aug-04 JDB Fixes from Dave Bryan - Check status on unattached drive set busy and not ready - Check status tests wrong unit for write protect status - Drive on line sets ATN, will set FLG if polling 15-Aug-04 RMS Controller resumes polling for ATN interrupts after read status (found by Dave Bryan) 22-Jul-04 RMS Controller sets ATN for all commands except read status (found by Dave Bryan) 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Fixed SR setting in IBL Fixed interpretation of SR<0> Revised IBL loader Implemented DMA SRQ (follows FLG) 25-Apr-03 RMS Revised for extended file support Fixed bug(s) in boot (found by Terry Newton) 10-Nov-02 RMS Added BOOT command, fixed numerous bugs 15-Jan-02 RMS Fixed INIT handling (found by Bill McDermith) 10-Jan-02 RMS Fixed f(x)write call (found by Bill McDermith) 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW 24-Nov-01 RMS Changed STA to be an array 07-Sep-01 RMS Moved function prototypes 29-Nov-00 RMS Made variable names unique 21-Nov-00 RMS Fixed flag, buffer power up state References: - 7900A Disc Drive Operating and Service Manual (07900-90002, Feb-1975) - 13210A Disc Drive Interface Kit Operating and Service Manual (13210-90003, Nov-1974) - 12557A Cartridge Disc Interface Kit Operating and Service Manual (12557-90001, Sep-1970) The simulator uses a number of state variables: dpc_busy set to drive number + 1 when the controller is busy of the unit in use dpd_xfer set to 1 if the data channel is executing a data transfer dpd_wval set to 1 by OTx if either !dpc_busy or dpd_xfer dpc_poll set to 1 if attention polling is enabled dpc_busy and dpd_xfer are set together at the start of a read, write, refine, or init. When data transfers are complete (CLC DC), dpd_xfer is cleared, but the operation is not necessarily over. When the operation is complete, dpc_busy is cleared and the command channel flag is set. dpc_busy && !dpd_xfer && STC DC (controller is busy, data channel transfer has been terminated by CLC DC, but a word has been placed in the data channel buffer) indicates data overrun. dpd_wval is used in write operations to fill out the sector buffer with 0's if only a partial sector has been transferred. dpc_poll indicates whether seek completion polling can occur. It is cleared by reset and CLC CC and set by issuance of a seek or completion of check status. The controller's "Record Address Register" (RAR) contains the CHS address of the last Seek or Address Record command executed. The RAR is shared among all drives on the controller. In addition, each drive has an internal position register that contains the last cylinder position transferred to the drive during Seek command execution (data operations always start with the RAR head and sector position). In a real drive, the address field of the sector under the head is read and compared to the RAR. When they match, the target sector is under the head and is ready for reading or writing. If a match doesn't occur, an Address Error is indicated. In the simulator, the address field is obtained from the drive's current position register during a read, i.e., the "on-disc" address field is assumed to match the current position. The following implemented behaviors have been inferred from secondary sources (diagnostics, operating system drivers, etc.), due to absent or contradictory authoritative information; future correction may be needed: 1. Status bit 15 (ATTENTION) does not set bit 0 (ANY ERROR) on the 12557A. 2. Omitting STC DC before Status Check does not set DC flag but does poll. */ #include "hp2100_defs.h" #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) #define FNC u3 /* saved function */ #define DRV u4 /* drive number (DC) */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ #define DP_N_NUMWD 7 #define DP_NUMWD (1 << DP_N_NUMWD) /* words/sector */ #define DP_NUMSC2 12 /* sectors/srf 12557 */ #define DP_NUMSC3 24 /* sectors/srf 13210 */ #define DP_NUMSC (dp_ctype? DP_NUMSC3: DP_NUMSC2) #define DP_NUMSF 4 /* surfaces/cylinder */ #define DP_NUMCY 203 /* cylinders/disk */ #define DP_SIZE2 (DP_NUMSF * DP_NUMCY * DP_NUMSC2 * DP_NUMWD) #define DP_SIZE3 (DP_NUMSF * DP_NUMCY * DP_NUMSC3 * DP_NUMWD) #define DP_NUMDRV 4 /* # drives */ /* Command word */ #define CW_V_FNC 12 /* function */ #define CW_M_FNC 017 #define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC) #define FNC_STA 000 /* status check */ #define FNC_WD 001 /* write */ #define FNC_RD 002 /* read */ #define FNC_SEEK 003 /* seek */ #define FNC_REF 005 /* refine */ #define FNC_CHK 006 /* check */ #define FNC_INIT 011 /* init */ #define FNC_AR 013 /* address */ #define FNC_SEEK1 020 /* fake - seek1 */ #define FNC_SEEK2 021 /* fake - seek2 */ #define FNC_SEEK3 022 /* fake - seek3 */ #define FNC_CHK1 023 /* fake - check1 */ #define FNC_AR1 024 /* fake - arec1 */ #define CW_V_DRV 0 /* drive */ #define CW_M_DRV 03 #define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV) /* Disk address words */ #define DA_V_CYL 0 /* cylinder */ #define DA_M_CYL 0377 #define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) #define DA_V_HD 8 /* head */ #define DA_M_HD 03 #define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD) #define DA_V_SC 0 /* sector */ #define DA_M_SC2 017 #define DA_M_SC3 037 #define DA_M_SC (dp_ctype? DA_M_SC3: DA_M_SC2) #define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) #define DA_CKMASK2 037 /* check mask */ #define DA_CKMASK3 077 #define DA_CKMASK (dp_ctype? DA_CKMASK3: DA_CKMASK2) /* Status in dpc_sta[drv], (u) = unused in 13210, (d) = dynamic */ #define STA_ATN 0100000 /* attention (u) */ #define STA_1ST 0040000 /* first status */ #define STA_OVR 0020000 /* overrun */ #define STA_RWU 0010000 /* rw unsafe NI (u) */ #define STA_ACU 0004000 /* access unsafe NI */ #define STA_HUNT 0002000 /* hunting NI (12557) */ #define STA_PROT 0002000 /* protected (13210) */ #define STA_SKI 0001000 /* incomplete NI (u) */ #define STA_SKE 0000400 /* seek error */ /* 0000200 /* unused */ #define STA_NRDY 0000100 /* not ready (d) */ #define STA_EOC 0000040 /* end of cylinder */ #define STA_AER 0000020 /* addr error */ #define STA_FLG 0000010 /* flagged */ #define STA_BSY 0000004 /* seeking */ #define STA_DTE 0000002 /* data error */ #define STA_ERR 0000001 /* any error (d) */ #define STA_ERSET2 (STA_1ST | STA_OVR | STA_RWU | STA_ACU | \ STA_SKI | STA_SKE | STA_NRDY | \ STA_EOC | STA_AER | STA_DTE) /* 12557A error set */ #define STA_ERSET3 (STA_ATN | STA_1ST | STA_OVR | STA_RWU | STA_ACU | \ STA_SKI | STA_SKE | STA_NRDY | STA_EOC | STA_AER | \ STA_FLG | STA_BSY | STA_DTE) /* 13210A error set */ #define STA_ANYERR (dp_ctype ? STA_ERSET3 : STA_ERSET2) #define STA_UNLOADED (dp_ctype ? (STA_NRDY | STA_BSY) : STA_NRDY) #define STA_MBZ13 (STA_ATN | STA_RWU | STA_SKI) /* zero in 13210 */ FLIP_FLOP dpc_command = CLEAR; /* cch command flip-flop */ FLIP_FLOP dpc_control = CLEAR; /* cch control flip-flop */ FLIP_FLOP dpc_flag = CLEAR; /* cch flag flip-flop */ FLIP_FLOP dpc_flagbuf = CLEAR; /* cch flag buffer flip-flop */ enum { A12557, A13210 } dp_ctype = A13210; /* ctrl type */ int32 dpc_busy = 0; /* cch unit */ int32 dpc_poll = 0; /* cch poll enable */ int32 dpc_cnt = 0; /* check count */ int32 dpc_eoc = 0; /* end of cyl */ int32 dpc_stime = 100; /* seek time */ int32 dpc_ctime = 100; /* command time */ int32 dpc_xtime = 5; /* xfer time */ int32 dpc_dtime = 2; /* dch time */ int32 dpd_obuf = 0, dpd_ibuf = 0; /* dch buffers */ int32 dpc_obuf = 0; /* cch buffers */ FLIP_FLOP dpd_command = CLEAR; /* dch command flip-flop */ FLIP_FLOP dpd_control = CLEAR; /* dch control flip-flop */ FLIP_FLOP dpd_flag = CLEAR; /* dch flag flip-flop */ FLIP_FLOP dpd_flagbuf = CLEAR; /* dch flag buffer flip-flop */ int32 dpd_xfer = 0; /* xfer in prog */ int32 dpd_wval = 0; /* write data valid */ int32 dp_ptr = 0; /* buffer ptr */ uint8 dpc_rarc = 0; /* RAR cylinder */ uint8 dpc_rarh = 0; /* RAR head */ uint8 dpc_rars = 0; /* RAR sector */ uint8 dpc_ucyl[DP_NUMDRV] = { 0 }; /* unit cylinder */ uint16 dpc_sta[DP_NUMDRV] = { 0 }; /* status regs */ uint16 dpxb[DP_NUMWD]; /* sector buffer */ DEVICE dpd_dev, dpc_dev; uint32 dpdio (uint32 select_code, IOSIG signal, uint32 data); uint32 dpcio (uint32 select_code, IOSIG signal, uint32 data); t_stat dpc_svc (UNIT *uptr); t_stat dpd_svc (UNIT *uptr); t_stat dpc_reset (DEVICE *dptr); t_stat dpc_attach (UNIT *uptr, char *cptr); t_stat dpc_detach (UNIT* uptr); t_stat dpc_boot (int32 unitno, DEVICE *dptr); void dp_god (int32 fnc, int32 drv, int32 time); void dp_goc (int32 fnc, int32 drv, int32 time); t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); /* DPD data structures dpd_dev DPD device descriptor dpd_unit DPD unit list dpd_reg DPD register list */ DIB dp_dib[] = { { DPD, &dpdio }, { DPC, &dpcio } }; #define dpd_dib dp_dib[0] #define dpc_dib dp_dib[1] UNIT dpd_unit = { UDATA (&dpd_svc, 0, 0) }; REG dpd_reg[] = { { ORDATA (IBUF, dpd_ibuf, 16) }, { ORDATA (OBUF, dpd_obuf, 16) }, { BRDATA (DBUF, dpxb, 8, 16, DP_NUMWD) }, { DRDATA (BPTR, dp_ptr, DP_N_NUMWD) }, { FLDATA (CMD, dpd_command, 0) }, { FLDATA (CTL, dpd_control, 0) }, { FLDATA (FLG, dpd_flag, 0) }, { FLDATA (FBF, dpd_flagbuf, 0) }, { FLDATA (XFER, dpd_xfer, 0) }, { FLDATA (WVAL, dpd_wval, 0) }, { ORDATA (DEVNO, dpd_dib.devno, 6), REG_HRO }, { NULL } }; MTAB dpd_mod[] = { { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dpd_dev }, { 0 } }; DEVICE dpd_dev = { "DPD", &dpd_unit, dpd_reg, dpd_mod, 1, 10, DP_N_NUMWD, 1, 8, 16, NULL, NULL, &dpc_reset, NULL, NULL, NULL, &dpd_dib, DEV_DISABLE }; /* DPC data structures dpc_dev DPC device descriptor dpc_unit DPC unit list dpc_reg DPC register list dpc_mod DPC modifier list */ UNIT dpc_unit[] = { { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) } }; REG dpc_reg[] = { { ORDATA (OBUF, dpc_obuf, 16) }, { ORDATA (BUSY, dpc_busy, 4), REG_RO }, { ORDATA (CNT, dpc_cnt, 5) }, { FLDATA (CMD, dpc_command, 0) }, { FLDATA (CTL, dpc_control, 0) }, { FLDATA (FLG, dpc_flag, 0) }, { FLDATA (FBF, dpc_flagbuf, 0) }, { FLDATA (EOC, dpc_eoc, 0) }, { FLDATA (POLL, dpc_poll, 0) }, { DRDATA (RARC, dpc_rarc, 8), PV_RZRO | REG_FIT }, { DRDATA (RARH, dpc_rarh, 2), PV_RZRO | REG_FIT }, { DRDATA (RARS, dpc_rars, 5), PV_RZRO | REG_FIT }, { BRDATA (CYL, dpc_ucyl, 10, 8, DP_NUMDRV), PV_RZRO }, { BRDATA (STA, dpc_sta, 8, 16, DP_NUMDRV) }, { DRDATA (CTIME, dpc_ctime, 24), PV_LEFT }, { DRDATA (DTIME, dpc_dtime, 24), PV_LEFT }, { DRDATA (STIME, dpc_stime, 24), PV_LEFT }, { DRDATA (XTIME, dpc_xtime, 24), REG_NZ | PV_LEFT }, { FLDATA (CTYPE, dp_ctype, 0), REG_HRO }, { URDATA (UFNC, dpc_unit[0].FNC, 8, 8, 0, DP_NUMDRV, REG_HRO) }, { URDATA (CAPAC, dpc_unit[0].capac, 10, T_ADDR_W, 0, DP_NUMDRV, PV_LEFT | REG_HRO) }, { ORDATA (DEVNO, dpc_dib.devno, 6), REG_HRO }, { NULL } }; MTAB dpc_mod[] = { { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dpc_load_unload }, { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dpc_load_unload }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "13210A", &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "12557A", &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &dp_showtype, NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dpd_dev }, { 0 } }; DEVICE dpc_dev = { "DPC", dpc_unit, dpc_reg, dpc_mod, DP_NUMDRV, 8, 24, 1, 8, 16, NULL, NULL, &dpc_reset, &dpc_boot, &dpc_attach, &dpc_detach, &dpc_dib, DEV_DISABLE }; /* Data channel I/O signal handler. For the 12557A, the card contains the usual control, flag, and flag buffer flip-flops. PRL, IRQ, and SRQ are standard. A command flip-flop indicates that data is available. For the 13210A, the card has a flag and a flag buffer flip-flop, but no control or interrupt flip-flop. SRQ is standard. IRQ and PRL are not driven, and the card does not respond to IAK. STC sets the command flip-flop to initiate a data transfer. CLC has no effect. Implementation notes: 1. The CRS signal clears the drive attention register. Under simulation, drive attention status is generated dynamically, so there is no attention register. */ uint32 dpdio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ dpd_flag = dpd_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ dpd_flag = dpd_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (dpd); break; case ioSFS: /* skip if flag is set */ setstdSKF (dpd); break; case ioIOI: /* I/O data input */ data = dpd_ibuf; break; case ioIOO: /* I/O data output */ dpd_obuf = data; if (!dpc_busy || dpd_xfer) /* if !overrun */ dpd_wval = 1; /* valid */ break; case ioPOPIO: /* power-on preset to I/O */ dpd_flag = dpd_flagbuf = SET; /* set flag buffer and flag */ if (dp_ctype == A12557) /* 12557? */ dpd_obuf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ dpd_command = CLEAR; /* clear command */ if (dp_ctype == A12557) /* 12557? */ dpd_control = CLEAR; /* clear control */ else { /* 13210 */ dpc_rarc = 0; /* clear controller cylinder address */ dpc_ucyl [CW_GETDRV (dpc_obuf)] = 0; /* clear last drive addressed cylinder */ } break; case ioCLC: /* clear control flip-flop */ if (dp_ctype == A12557) /* 12557? */ dpd_control = CLEAR; /* clear control */ dpd_xfer = 0; /* clr xfer in progress */ break; case ioSTC: /* set control flip-flop */ if (dp_ctype == A12557) /* 12557? */ dpd_control = SET; /* set control */ dpd_command = SET; /* set cmd */ if (dpc_busy && !dpd_xfer) /* overrun? */ dpc_sta[dpc_busy - 1] |= STA_OVR; break; case ioSIR: /* set interrupt request */ if (dp_ctype == A12557) { /* 12557? */ setstdPRL (select_code, dpd); /* set standard PRL signal */ setstdIRQ (select_code, dpd); /* set standard IRQ signal */ } setstdSRQ (select_code, dpd); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ if (dp_ctype == A12557) /* 12557? */ dpd_flagbuf = CLEAR; /* clear flag buffer */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dpdio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dpdio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command channel I/O signal handler. The 12557A and 13210A have the usual control, flag, and flag buffer flip-flops. Only the 12557A has a command flip-flop. IRQ, PRL, and SRQ are standard. Implementation notes: 1. In hardware, the command channel card passes PRH to PRL. The data card actually drives PRL with the command channel's control and flag states, even though the command channel's control, flag, and flag buffer drive IRQH. That is, the priority chain is broken at the data card, although the command card is interrupting. This works in hardware, but we must break PRL at the command card under simulation to allow the command card to interrupt. */ uint32 dpcio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 i, fnc, drv; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ dpc_flag = dpc_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ dpc_flag = dpc_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (dpc); break; case ioSFS: /* skip if flag is set */ setstdSKF (dpc); break; case ioIOI: /* I/O data input */ data = 0; for (i = 0; i < DP_NUMDRV; i++) /* form attention register value */ if (dpc_sta[i] & STA_ATN) data = data | (1 << i); break; case ioIOO: /* I/O data output */ dpc_obuf = data; if (dp_ctype == A13210) /* 13210? */ dpcio (select_code, ioCLC, 0); /* OTx causes CLC */ break; case ioPOPIO: /* power-on preset to I/O */ dpc_flag = dpc_flagbuf = SET; /* set flag buffer and flag */ if (dp_ctype == A12557) /* 12557? */ dpd_obuf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ dpc_control = CLEAR; /* clear control */ if (dp_ctype == A12557) /* 12557? */ dpc_command = CLEAR; /* clear command */ break; case ioCLC: /* clear control flip-flop */ dpc_control = CLEAR; /* clr ctl */ if (dp_ctype == A12557) /* 12557? */ dpc_command = CLEAR; /* cancel non-seek */ if (dpc_busy) sim_cancel (&dpc_unit[dpc_busy - 1]); sim_cancel (&dpd_unit); /* cancel dch */ dpd_xfer = 0; /* clr dch xfer */ dpc_busy = 0; /* clr cch busy */ dpc_poll = 0; /* clr cch poll */ break; case ioSTC: /* set control flip-flop */ dpc_control = SET; /* set ctl */ if ((dp_ctype == A13210) || !dpc_command) { /* 13210 or command is clear? */ if (dp_ctype == A12557) /* 12557? */ dpc_command = SET; /* set command */ drv = CW_GETDRV (dpc_obuf); /* get fnc, drv */ fnc = CW_GETFNC (dpc_obuf); /* from cmd word */ switch (fnc) { /* case on fnc */ case FNC_SEEK: /* seek */ dpc_poll = 1; /* enable polling */ dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */ break; case FNC_STA: /* rd sta */ if (dp_ctype == A13210) /* 13210? clr dch flag */ dpdio (dpd_dib.devno, ioCLF, 0); case FNC_CHK: /* check */ case FNC_AR: /* addr rec */ dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */ break; case FNC_RD: case FNC_WD: /* read, write */ case FNC_REF: case FNC_INIT: /* refine, init */ dp_goc (fnc, drv, dpc_ctime); /* sched drive */ break; } /* end case */ } /* end if */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, dpc); /* set standard PRL signal */ setstdIRQ (select_code, dpc); /* set standard IRQ signal */ setstdSRQ (select_code, dpc); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ dpc_flagbuf = CLEAR; /* clear flag buffer */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dpcio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dpcio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Start data channel operation */ void dp_god (int32 fnc, int32 drv, int32 time) { dpd_unit.DRV = drv; /* save unit */ dpd_unit.FNC = fnc; /* save function */ sim_activate (&dpd_unit, time); return; } /* Start controller operation */ void dp_goc (int32 fnc, int32 drv, int32 time) { int32 t; if (t = sim_is_active (&dpc_unit[drv])) { /* still seeking? */ sim_cancel (&dpc_unit[drv]); /* stop seek */ dpc_sta[drv] = dpc_sta[drv] & ~STA_BSY; /* clear busy */ time = time + t; /* include seek time */ } dp_ptr = 0; /* init buf ptr */ dpc_eoc = 0; /* clear end cyl */ dpc_busy = drv + 1; /* set busy */ dpd_xfer = 1; /* xfer in prog */ dpc_unit[drv].FNC = fnc; /* save function */ dpc_sta[drv] = dpc_sta[drv] & ~STA_ATN; /* clear ATN */ sim_activate (&dpc_unit[drv], time); /* activate unit */ return; } /* Data channel unit service This routine handles the data channel transfers. It also handles data transfers that are blocked by seek in progress. uptr->DRV = target drive uptr->FNC = target function Seek substates seek - transfer cylinder seek1 - transfer head/surface Address record ar - transfer cylinder ar1 - transfer head/surface, finish operation Status check - transfer status, finish operation Check data chk - transfer sector count */ t_stat dpd_svc (UNIT *uptr) { int32 i, drv, st; drv = uptr->DRV; /* get drive no */ switch (uptr->FNC) { /* case function */ case FNC_AR: /* arec, need cyl */ case FNC_SEEK: /* seek, need cyl */ if (dpd_command) { /* dch active? */ dpc_rarc = DA_GETCYL (dpd_obuf); /* set RAR from cyl word */ dpd_wval = 0; /* clr data valid */ dpd_command = CLEAR; /* clr dch cmd */ dpdio (dpd_dib.devno, ioENF, 0); /* set dch flg */ if (uptr->FNC == FNC_AR) uptr->FNC = FNC_AR1; else uptr->FNC = FNC_SEEK1; /* advance state */ } sim_activate (uptr, dpc_xtime); /* no, wait more */ break; case FNC_AR1: /* arec, need hd/sec */ case FNC_SEEK1: /* seek, need hd/sec */ if (dpd_command) { /* dch active? */ dpc_rarh = DA_GETHD (dpd_obuf); /* set RAR from head */ dpc_rars = DA_GETSC (dpd_obuf); /* set RAR from sector */ dpd_wval = 0; /* clr data valid */ dpd_command = CLEAR; /* clr dch cmd */ dpdio (dpd_dib.devno, ioENF, 0); /* set dch flg */ if (uptr->FNC == FNC_AR1) { dpc_command = CLEAR; /* clr cch cmd */ dpcio (dpc_dib.devno, ioENF, 0); /* set cch flg */ dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set drv attn */ break; /* done if Address Record */ } if (sim_is_active (&dpc_unit[drv])) { /* if busy, */ dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */ break; /* allow prev seek to cmpl */ } if ((dpc_rarc >= DP_NUMCY) || /* invalid cyl? */ ((dp_ctype == A13210) && /* or 13210A */ (dpc_rars >= DP_NUMSC3))) { /* and invalid sector? */ dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */ sim_activate (&dpc_unit[drv], 1); /* schedule drive no-wait */ dpc_unit[drv].FNC = FNC_SEEK3; /* do immed compl w/poll */ break; } st = abs (dpc_rarc - dpc_ucyl[drv]) * dpc_stime; if (st == 0) st = dpc_stime; /* min time */ dpc_ucyl[drv] = dpc_rarc; /* transfer RAR */ sim_activate (&dpc_unit[drv], st); /* schedule drive */ dpc_sta[drv] = (dpc_sta[drv] | STA_BSY) & ~(STA_SKE | STA_SKI | STA_HUNT); dpc_unit[drv].FNC = FNC_SEEK2; /* set operation */ } else sim_activate (uptr, dpc_xtime); /* no, wait more */ break; case FNC_STA: /* read status */ if (dpd_command || (dp_ctype == A13210)) { /* dch act or 13210? */ if ((dpc_unit[drv].flags & UNIT_UNLOAD) == 0) { /* drive up? */ dpd_ibuf = dpc_sta[drv] & ~STA_ERR; /* clear err */ if (dp_ctype == A13210) dpd_ibuf = /* 13210? */ (dpd_ibuf & ~(STA_MBZ13 | STA_PROT)) | (dpc_unit[drv].flags & UNIT_WPRT? STA_PROT: 0); } else dpd_ibuf = STA_UNLOADED; /* not ready */ if (dpd_ibuf & STA_ANYERR) /* errors? set flg */ dpd_ibuf = dpd_ibuf | STA_ERR; dpc_command = CLEAR; /* clr cch cmd */ dpd_command = CLEAR; /* clr dch cmd */ dpdio (dpd_dib.devno, ioENF, 0); /* set dch flg */ } dpc_sta[drv] = dpc_sta[drv] & /* clr sta flags */ ~(STA_ATN | STA_1ST | STA_OVR | STA_RWU | STA_ACU | STA_EOC | STA_AER | STA_FLG | STA_DTE); dpc_poll = 1; /* enable polling */ for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ if (dpc_sta[i] & STA_ATN) { /* any ATN set? */ dpcio (dpc_dib.devno, ioENF, 0); /* set cch flg */ break; } } break; case FNC_CHK: /* check, need cnt */ if (dpd_command) { /* dch active? */ dpc_cnt = dpd_obuf & DA_CKMASK; /* get count */ dpd_wval = 0; /* clr data valid */ dp_goc (FNC_CHK1, drv, dpc_xtime); /* sched drv */ } else sim_activate (uptr, dpc_xtime); /* wait more */ break; default: return SCPE_IERR; } return SCPE_OK; } /* Drive unit service This routine handles the data transfers. Seek substates seek2 - done Refine sector - erase sector, finish operation Check data chk1 - finish operation Read Write */ #define GETDA(x,y,z) \ (((((x) * DP_NUMSF) + (y)) * DP_NUMSC) + (z)) * DP_NUMWD t_stat dpc_svc (UNIT *uptr) { int32 da, drv, err; err = 0; /* assume no err */ drv = uptr - dpc_dev.units; /* get drive no */ if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ dpc_command = CLEAR; /* clr cch cmd */ dpcio (dpc_dib.devno, ioENF, 0); /* set cch flg */ dpc_sta[drv] = 0; /* clr status */ dpc_busy = 0; /* ctlr is free */ dpc_poll = 0; /* polling disabled */ dpd_xfer = 0; dpd_wval = 0; return SCPE_OK; } switch (uptr->FNC) { /* case function */ case FNC_SEEK2: /* positioning done */ dpc_sta[drv] = (dpc_sta[drv] | STA_ATN) & ~STA_BSY; /* fall into cmpl */ case FNC_SEEK3: /* seek complete */ if (dpc_poll) { /* polling enabled? */ dpc_command = CLEAR; /* clr cch cmd */ dpcio (dpc_dib.devno, ioENF, 0); /* set cch flg */ } return SCPE_OK; case FNC_REF: /* refine sector */ break; /* just a NOP */ case FNC_RD: /* read */ case FNC_CHK1: /* check */ if (dp_ptr == 0) { /* new sector? */ if (!dpd_command && (uptr->FNC != FNC_CHK1)) break; if (dpc_rarc != dpc_ucyl[drv]) /* RAR cyl miscompare? */ dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, read */ if (dpc_rars >= DP_NUMSC) { /* bad sector? */ dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, stop */ break; } if (dpc_eoc) { /* end of cyl? */ dpc_sta[drv] = dpc_sta[drv] | STA_EOC; break; } da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */ dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */ if (dpc_rars == 0) { /* wrap? */ dpc_rarh = dpc_rarh ^ 1; /* incr head */ dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */ } if (err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET)) break; fxread (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref); if (err = ferror (uptr->fileref)) break; } dpd_ibuf = dpxb[dp_ptr++]; /* get word */ if (dp_ptr >= DP_NUMWD) { /* end of sector? */ if (uptr->FNC == FNC_CHK1) { /* check? */ dpc_cnt = (dpc_cnt - 1) & DA_CKMASK; /* decr count */ if (dpc_cnt == 0) break; /* stop at zero */ } dp_ptr = 0; /* wrap buf ptr */ } if (dpd_command && dpd_xfer) /* dch on, xfer? */ dpdio (dpd_dib.devno, ioENF, 0); /* set dch flg */ dpd_command = CLEAR; /* clr dch cmd */ sim_activate (uptr, dpc_xtime); /* sched next word */ return SCPE_OK; case FNC_INIT: /* init */ case FNC_WD: /* write */ if (dp_ptr == 0) { /* start sector? */ if (!dpd_command && !dpd_wval) break; /* xfer done? */ if (uptr->flags & UNIT_WPRT) { /* wr prot? */ dpc_sta[drv] = dpc_sta[drv] | STA_FLG; /* set status */ break; /* done */ } if ((dpc_rarc != dpc_ucyl[drv]) || /* RAR cyl miscompare? */ (dpc_rars >= DP_NUMSC)) { /* bad sector? */ dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* address error */ break; } if (dpc_eoc) { /* end of cyl? */ dpc_sta[drv] = dpc_sta[drv] | STA_EOC; /* set status */ break; /* done */ } } dpxb[dp_ptr++] = dpd_wval? dpd_obuf: 0; /* store word/fill */ dpd_wval = 0; /* clr data valid */ if (dp_ptr >= DP_NUMWD) { /* buffer full? */ da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */ dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */ if (dpc_rars == 0) { /* wrap? */ dpc_rarh = dpc_rarh ^ 1; /* incr head */ dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */ } if (err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET)) break; fxwrite (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref); if (err = ferror (uptr->fileref)) break; /* error? */ dp_ptr = 0; /* next sector */ } if (dpd_command && dpd_xfer) /* dch on, xfer? */ dpdio (dpd_dib.devno, ioENF, 0); /* set dch flg */ dpd_command = CLEAR; /* clr dch cmd */ sim_activate (uptr, dpc_xtime); /* sched next word */ return SCPE_OK; default: return SCPE_IERR; } /* end case fnc */ dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set ATN */ dpc_command = CLEAR; /* clr cch cmd */ dpcio (dpc_dib.devno, ioENF, 0); /* set cch flg */ dpc_busy = 0; /* ctlr is free */ dpd_xfer = dpd_wval = 0; if (err != 0) { /* error? */ perror ("DP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat dpc_reset (DEVICE *dptr) { int32 drv; hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &dpd_dev)? &dpc_dev: &dpd_dev); if (sim_switches & SWMASK ('P')) { /* PON reset? */ dpd_ibuf = 0; /* clear buffers */ dpd_obuf = 0; dpc_obuf = 0; dpc_rarc = dpc_rarh = dpc_rars = 0; /* clear RAR */ } if (dptr == &dpc_dev) /* command channel reset? */ dpcio (dpc_dib.devno, ioPOPIO, 0); /* send POPIO signal to command channel */ else /* data channel reset */ dpdio (dpd_dib.devno, ioPOPIO, 0); /* send POPIO signal to data channel */ dpc_busy = 0; /* reset controller state */ dpc_poll = 0; dpd_xfer = 0; dpd_wval = 0; dpc_eoc = 0; dp_ptr = 0; sim_cancel (&dpd_unit); /* cancel dch */ for (drv = 0; drv < DP_NUMDRV; drv++) { /* loop thru drives */ sim_cancel (&dpc_unit[drv]); /* cancel activity */ dpc_unit[drv].FNC = 0; /* clear function */ dpc_ucyl[drv] = 0; /* clear drive pos */ if (dpc_unit[drv].flags & UNIT_ATT) dpc_sta[drv] = dpc_sta[drv] & STA_1ST; /* first seek status */ else dpc_sta[drv] = 0; /* clear status */ } return SCPE_OK; } /* Attach routine */ t_stat dpc_attach (UNIT *uptr, char *cptr) { t_stat r; r = attach_unit (uptr, cptr); /* attach unit */ if (r == SCPE_OK) dpc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */ return r; } /* Detach routine */ t_stat dpc_detach (UNIT* uptr) { dpc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */ return detach_unit (uptr); /* detach unit */ } /* Load and unload heads */ t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) { uint32 drv; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */ if (value == UNIT_UNLOAD) /* unload heads? */ uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ else { /* load heads */ uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ drv = uptr - dpc_dev.units; /* get drive no */ dpc_sta[drv] = dpc_sta[drv] | STA_ATN | STA_1ST; /* update status */ if (dpc_poll) /* polling enabled? */ dpcio (dpc_dib.devno, ioENF, 0); /* set flag */ } return SCPE_OK; } /* Set controller type */ t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; for (i = 0; i < DP_NUMDRV; i++) { if (dpc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } for (i = 0; i < DP_NUMDRV; i++) dpc_unit[i].capac = (val? DP_SIZE3: DP_SIZE2); dp_ctype = val; return SCPE_OK; } /* Show controller type */ t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) { if (dp_ctype == A13210) fprintf (st, "13210A"); else fprintf (st, "12557A"); return SCPE_OK; } /* 7900/7901 bootstrap routine (HP 12992F ROM) */ const BOOT_ROM dp_rom = { 0106710, /*ST CLC DC ; clr dch */ 0106711, /* CLC CC ; clr cch */ 0017757, /* JSB STAT ; get status */ 0067746, /*SK LDB SKCMD ; seek cmd */ 0106610, /* OTB DC ; cyl # */ 0103710, /* STC DC,C ; to dch */ 0106611, /* OTB CC ; seek cmd */ 0103711, /* STC CC,C ; to cch */ 0102310, /* SFS DC ; addr wd ok? */ 0027710, /* JMP *-1 ; no, wait */ 0006400, /* CLB */ 0102501, /* LIA 1 ; read switches */ 0002011, /* SLA,RSS ; <0> set? */ 0047747, /* ADB BIT9 ; head 2 = removable */ 0106610, /* OTB DC ; head/sector */ 0103710, /* STC DC,C ; to dch */ 0102311, /* SFS CC ; seek done? */ 0027720, /* JMP *-1 ; no, wait */ 0017757, /* JSB STAT ; get status */ 0067776, /* LDB DMACW ; DMA control */ 0106606, /* OTB 6 */ 0067750, /* LDB ADDR1 ; memory addr */ 0106602, /* OTB 2 */ 0102702, /* STC 2 ; flip DMA ctrl */ 0067752, /* LDB CNT ; word count */ 0106602, /* OTB 2 */ 0063745, /* LDB RDCMD ; read cmd */ 0102611, /* OTA CC ; to cch */ 0103710, /* STC DC,C ; start dch */ 0103706, /* STC 6,C ; start DMA */ 0103711, /* STC CC,C ; start cch */ 0102311, /* SFS CC ; done? */ 0027737, /* JMP *-1 ; no, wait */ 0017757, /* JSB STAT ; get status */ 0027775, /* JMP XT ; done */ 0037766, /*FSMSK 037766 ; status mask */ 0004000, /*STMSK 004000 ; unsafe mask */ 0020000, /*RDCMD 020000 ; read cmd */ 0030000, /*SKCMD 030000 ; seek cmd */ 0001000, /*BIT9 001000 ; head 2 select */ 0102011, /*ADDR1 102011 */ 0102055, /*ADDR2 102055 */ 0164000, /*CNT -6144. */ 0, 0, 0, 0, /* unused */ 0000000, /*STAT 0 */ 0002400, /* CLA ; status request */ 0102611, /* OTC CC ; to cch */ 0103711, /* STC CC,C ; start cch */ 0102310, /* SFS DC ; done? */ 0027763, /* JMP *-1 */ 0102510, /* LIA DC ; get status */ 0013743, /* AND FSMSK ; mask 15,14,3,0 */ 0002003, /* SZA,RSS ; drive ready? */ 0127757, /* JMP STAT,I ; yes */ 0013744, /* AND STMSK ; fault? */ 0002002, /* SZA */ 0102030, /* HLT 30 ; yes */ 0027700, /* JMP ST ; no, retry */ 0117751, /*XT JSB ADDR2,I ; start program */ 0120010, /*DMACW 120000+DC */ 0000000 /* -ST */ }; t_stat dpc_boot (int32 unitno, DEVICE *dptr) { int32 dev; if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ dev = dpd_dib.devno; /* get data chan dev */ if (ibl_copy (dp_rom, dev)) return SCPE_IERR; /* copy boot to memory */ SR = (SR & IBL_OPT) | IBL_DP | (dev << IBL_V_DEV); /* set SR */ if (sim_switches & SWMASK ('R')) SR = SR | IBL_DP_REM; /* boot from removable? */ return SCPE_OK; } simh-3.8.1/HP2100/hp2100_mux.c0000644000175000017500000016332711113075000013422 0ustar vlmvlm/* hp2100_mux.c: HP 2100 12920A terminal multiplexor simulator Copyright (c) 2002-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. MUX,MUXL,MUXM 12920A terminal multiplexor 25-Nov-08 JDB Revised for new multiplexer library SHOW routines 09-Oct-08 JDB "muxl_unit" defined one too many units (17 instead of 16) 10-Sep-08 JDB SHOW MUX CONN/STAT with SET MUX DIAG is no longer disallowed 07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach 27-Aug-08 JDB Added LINEORDER support 12-Aug-08 JDB Added BREAK deferral to allow RTE break-mode to work 26-Jun-08 JDB Rewrote device I/O to model backplane signals 16-Apr-08 JDB Sync mux poll with console poll for idle compatibility 06-Mar-07 JDB Corrected "mux_sta" size from 16 to 21 elements Fixed "muxc_reset" to clear lines 16-20 26-Feb-07 JDB Added debug printouts Fixed control card OTx to set current channel number Fixed to set "muxl_ibuf" in response to a transmit interrupt Changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits Fixed to set "mux_rchp" when a line break is received Fixed incorrect "odd_par" table values Reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity Fixed mux reset (ioCRS) to clear port parameters Fixed to use PUT_DCH instead of PUT_CCH for data channel status 10-Feb-07 JDB Added DIAG/TERM modifiers to implement diagnostic mode 28-Dec-06 JDB Added ioCRS state to I/O decoders 02-Jun-06 JDB Fixed compiler warning for mux_ldsc init 22-Nov-05 RMS Revised for new terminal processing routines 29-Jun-05 RMS Added SET MUXLn DISCONNECT 07-Oct-04 JDB Allow enable/disable from any device 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Implemented DMA SRQ (follows FLG) 05-Jan-04 RMS Revised for tmxr library changes 21-Dec-03 RMS Added invalid character screening for TSB (from Mike Gemeny) 09-May-03 RMS Added network device flag 01-Nov-02 RMS Added 7B/8B support 22-Aug-02 RMS Updated for changes to sim_tmxr Reference: - 12920A Asynchronous Multiplexer Interface Kits Operating and Service Manual (12920-90001, Oct-1972) The 12920A was a 16-channel asynchronous terminal multiplexer. It supported direct-connected terminals as well as modems at speeds up to 2400 baud. It was the primary terminal multiplexer for the HP 2000 series of Time-Shared BASIC systems. The multiplexer was implemented as a three-card set consisting of a lower data card, an upper data card, and a modem control card. Under simulation, these are implemented by three devices: MUXL lower data card (lines) MUX upper data card (scanner) MUXM control card (modem control) The lower and upper data cards must be in adjacent I/O slots. The control card may be placed in any slot, although in practice it was placed in the slot above the upper data card, so that all three cards were physically together. The 12920A supported one or two control cards (two cards were used with 801-type automatic dialers). Under simulation, only one control card is supported. Implementation notes: 1. If a BREAK is detected during an input poll, and we are not in diagnostic mode, we defer recognition until either a character is output or a second successive input poll occurs. This is necessary for RTE break-mode operation. Without this deferral, a BREAK during output would be ignored by the RTE driver, making it impossible to stop a long listing. The problem is due to timing differences between simulated and real time. The RTE multiplexer driver is a privileged driver. Privileged drivers bypass RTE to provide rapid interrupt handling. To inform RTE that an operation is complete, e.g., that a line has been written, the interrupt section of the driver sets a device timeout of one clock tick (10 milliseconds). When that timeout occurs, RTE is entered normally to complete the I/O transaction. While the completion timeout is pending, the driver ignores any further interrupts from the multiplexer line. The maximum communication rate for the multiplexer is 2400 baud, or approximately 4.2 milliseconds per character transferred. A typical line of 20 characters would therefore take ~85 milliseconds, plus the 10 millisecond completion timeout, or about 95 milliseconds total. BREAK recognition would be ignored for roughly 10% of that time. At lower baud rates, recognition would be ignored for a correspondingly smaller percentage of the time. However, SIMH uses an optimized timing of 500 instructions per character transfer, rather than the ~6600 instructions that a character transfer should take, and so a typical 20-character line will take about 11,000 instructions. On the other hand, the clock tick is calibrated to real time, and 10 milliseconds of real time takes about 420,000 instructions on a 2.0 GHz PC. To be recognized, then, the BREAK key must be pressed in a window that is open for about 2.5% of the time. Therefore, the BREAK key will be ignored about 97.5% of the time, and RTE break-mode effectively will not work. Deferring BREAK recognition until the next character is output ensures that the BREAK interrupt will be accepted (the simulator delivers input interrupts before output interrupts, so the BREAK interrupt arrives before the output character transmit interrupt). If an output operation is not in progress, then the BREAK will be recognized at the next input poll. */ #include #include "hp2100_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" /* Unit references */ #define MUX_LINES 16 /* number of user lines */ #define MUX_ILINES 5 /* number of diag rcv only lines */ /* Service times */ #define MUXL_WAIT 500 /* Unit flags */ #define UNIT_V_MDM (TTUF_V_UF + 0) /* modem control */ #define UNIT_V_DIAG (TTUF_V_UF + 1) /* loopback diagnostic */ #define UNIT_MDM (1 << UNIT_V_MDM) #define UNIT_DIAG (1 << UNIT_V_DIAG) /* Debug flags */ #define DEB_CMDS (1 << 0) /* Command initiation and completion */ #define DEB_CPU (1 << 1) /* CPU I/O */ #define DEB_XFER (1 << 2) /* Socket receive and transmit */ /* Channel number (OTA upper, LIA lower or upper) */ #define MUX_V_CHAN 10 /* channel num */ #define MUX_M_CHAN 037 #define MUX_CHAN(x) (((x) >> MUX_V_CHAN) & MUX_M_CHAN) /* OTA, lower = parameters or data */ #define OTL_P 0100000 /* parameter */ #define OTL_TX 0040000 /* transmit */ #define OTL_ENB 0020000 /* enable */ #define OTL_TPAR 0010000 /* xmt parity */ #define OTL_ECHO 0010000 /* rcv echo */ #define OTL_DIAG 0004000 /* diagnose */ #define OTL_SYNC 0004000 /* sync */ #define OTL_V_LNT 8 /* char length */ #define OTL_M_LNT 07 #define OTL_LNT(x) (((x) >> OTL_V_LNT) & OTL_M_LNT) #define OTL_V_BAUD 0 /* baud rate */ #define OTL_M_BAUD 0377 #define OTL_BAUD(x) (((x) >> OTL_V_BAUD) & OTL_M_BAUD) #define OTL_CHAR 03777 /* char mask */ #define OTL_PAR 0200 /* char parity */ /* LIA, lower = received data */ #define LIL_PAR 0100000 /* parity */ #define PUT_DCH(x) (((x) & MUX_M_CHAN) << MUX_V_CHAN) #define LIL_CHAR 01777 /* character */ /* LIA, upper = status */ #define LIU_SEEK 0100000 /* seeking NI */ #define LIU_DG 0000010 /* diagnose */ #define LIU_BRK 0000004 /* break */ #define LIU_LOST 0000002 /* char lost */ #define LIU_TR 0000001 /* trans/rcv */ /* OTA, control */ #define OTC_SCAN 0100000 /* scan */ #define OTC_UPD 0040000 /* update */ #define OTC_V_CHAN 10 /* channel */ #define OTC_M_CHAN 017 #define OTC_CHAN(x) (((x) >> OTC_V_CHAN) & OTC_M_CHAN) #define OTC_EC2 0000200 /* enable Cn upd */ #define OTC_EC1 0000100 #define OTC_C2 0000040 /* Cn flops */ #define OTC_C1 0000020 #define OTC_V_C 4 /* S1 to C1 */ #define OTC_ES2 0000010 /* enb comparison */ #define OTC_ES1 0000004 #define OTC_V_ES 2 #define OTC_SS2 0000002 /* SSn flops */ #define OTC_SS1 0000001 #define OTC_RW (OTC_ES2|OTC_ES1|OTC_SS2|OTC_SS1) #define RTS OCT_C2 /* C2 = rts */ #define DTR OTC_C1 /* C1 = dtr */ /* LIA, control */ #define LIC_MBO 0140000 /* always set */ #define LIC_V_CHAN 10 /* channel */ #define LIC_M_CHAN 017 #define PUT_CCH(x) (((x) & OTC_M_CHAN) << OTC_V_CHAN) #define LIC_I2 0001000 /* change flags */ #define LIC_I1 0000400 #define LIC_S2 0000002 /* Sn flops */ #define LIC_S1 0000001 #define LIC_V_I 8 /* S1 to I1 */ #define CDET LIC_S2 /* S2 = cdet */ #define DSR LIC_S1 /* S1 = dsr */ #define LIC_TSTI(ch) (((muxc_lia[ch] ^ muxc_ota[ch]) & \ ((muxc_ota[ch] & (OTC_ES2|OTC_ES1)) >> OTC_V_ES)) \ << LIC_V_I) /* Program constants */ static const uint8 odd_par [256] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 000-017 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 020-037 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 040-067 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 060-077 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 100-117 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 120-137 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 140-157 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 160-177 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 200-217 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 220-237 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 240-267 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 260-277 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 300-317 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 320-337 */ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 340-357 */ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 /* 360-377 */ }; #define RCV_PAR(x) (odd_par[(x) & 0377] ? 0 : LIL_PAR) #define XMT_PAR(x) (odd_par[(x) & 0377] ? 0 : OTL_PAR) /* Multiplexer controller state variables */ FLIP_FLOP muxl_control = CLEAR; FLIP_FLOP muxl_flag = CLEAR; FLIP_FLOP muxl_flagbuf = CLEAR; uint32 muxl_ibuf = 0; /* low in: rcv data */ uint32 muxl_obuf = 0; /* low out: param */ uint32 muxu_ibuf = 0; /* upr in: status */ uint32 muxu_obuf = 0; /* upr out: chan */ FLIP_FLOP muxc_control = CLEAR; FLIP_FLOP muxc_flag = CLEAR; FLIP_FLOP muxc_flagbuf = CLEAR; uint32 muxc_chan = 0; /* ctrl chan */ uint32 muxc_scan = 0; /* ctrl scan */ /* Multiplexer per-line state variables */ uint16 mux_sta [MUX_LINES + MUX_ILINES]; /* line status */ uint16 mux_rpar [MUX_LINES + MUX_ILINES]; /* rcv param */ uint16 mux_xpar [MUX_LINES]; /* xmt param */ uint8 mux_rchp [MUX_LINES + MUX_ILINES]; /* rcv chr pend */ uint8 mux_xdon [MUX_LINES]; /* xmt done */ uint8 muxc_ota [MUX_LINES]; /* ctrl: Cn,ESn,SSn */ uint8 muxc_lia [MUX_LINES]; /* ctrl: Sn */ uint8 mux_defer [MUX_LINES]; /* break deferred flags */ /* Multiplexer per-line buffer variables */ uint16 mux_rbuf[MUX_LINES + MUX_ILINES]; /* rcv buf */ uint16 mux_xbuf[MUX_LINES]; /* xmt buf */ /* Multiplexer local routines */ void mux_receive (int32 ln, int32 c, t_bool diag); void mux_data_int (void); void mux_ctrl_int (void); void mux_diag (int32 c); /* Multiplexer global routines */ uint32 muxlio (uint32 select_code, IOSIG signal, uint32 data); uint32 muxuio (uint32 select_code, IOSIG signal, uint32 data); uint32 muxcio (uint32 select_code, IOSIG signal, uint32 data); t_stat muxi_svc (UNIT *uptr); t_stat muxo_svc (UNIT *uptr); t_stat muxc_reset (DEVICE *dptr); t_stat mux_attach (UNIT *uptr, char *cptr); t_stat mux_detach (UNIT *uptr); t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc); /* MUX data structures. mux_order MUX line connection order table mux_ldsc MUX line descriptors mux_desc MUX multiplexer descriptor */ int32 mux_order [MUX_LINES] = { -1 }; /* connection order */ TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */ TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order }; /* device descriptor */ DIB mux_dib[] = { { MUXL, &muxlio }, { MUXU, &muxuio } }; #define muxl_dib mux_dib[0] #define muxu_dib mux_dib[1] /* MUXL data structures. muxl_dib MUXL device information block muxl_unit MUXL unit list muxl_reg MUXL register list muxl_mod MUXL modifier list muxl_dev MUXL device descriptor */ DEVICE muxl_dev; UNIT muxl_unit[] = { { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT } }; REG muxl_reg[] = { { FLDATA (CTL, muxl_control, 0) }, { FLDATA (FLG, muxl_flag, 0) }, { FLDATA (FBF, muxl_flagbuf, 0) }, { BRDATA (STA, mux_sta, 8, 16, MUX_LINES + MUX_ILINES) }, { BRDATA (RPAR, mux_rpar, 8, 16, MUX_LINES + MUX_ILINES) }, { BRDATA (XPAR, mux_xpar, 8, 16, MUX_LINES) }, { BRDATA (RBUF, mux_rbuf, 8, 16, MUX_LINES + MUX_ILINES) }, { BRDATA (XBUF, mux_xbuf, 8, 16, MUX_LINES) }, { BRDATA (RCHP, mux_rchp, 8, 1, MUX_LINES + MUX_ILINES) }, { BRDATA (XDON, mux_xdon, 8, 1, MUX_LINES) }, { BRDATA (BDFR, mux_defer, 8, 1, MUX_LINES) }, { URDATA (TIME, muxl_unit[0].wait, 10, 24, 0, MUX_LINES, REG_NZ + PV_LEFT) }, { ORDATA (DEVNO, muxl_dib.devno, 6), REG_HRO }, { NULL } }; MTAB muxl_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL, NULL, NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL }, { UNIT_MDM, UNIT_MDM, "dataset", "DATASET", NULL, NULL, NULL }, { UNIT_MDM, 0, "no dataset", "NODATASET", NULL, NULL, NULL }, { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &mux_desc }, { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &mux_desc }, { MTAB_XTD | MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, { 0 } }; DEVICE muxl_dev = { "MUXL", /* device name */ muxl_unit, /* unit array */ muxl_reg, /* register array */ muxl_mod, /* modifier array */ MUX_LINES, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &muxc_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &muxl_dib, /* device information block */ DEV_DISABLE, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* MUXU data structures muxu_dib MUXU device information block muxu_unit MUXU unit list muxu_reg MUXU register list muxu_mod MUXU modifier list muxu_deb MUXU debug list muxu_dev MUXU device descriptor */ DEVICE muxu_dev; UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_FIRST }; REG muxu_reg[] = { { ORDATA (IBUF, muxu_ibuf, 16) }, { ORDATA (OBUF, muxu_obuf, 16) }, { ORDATA (DEVNO, muxu_dib.devno, 6), REG_HRO }, { NULL } }; MTAB muxu_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", &mux_setdiag, NULL, NULL }, { UNIT_DIAG, 0, "terminal mode", "TERM", &mux_setdiag, NULL, NULL }, { UNIT_ATT, UNIT_ATT, "", NULL, NULL, &tmxr_show_summ, &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LINEORDER", "LINEORDER", &tmxr_set_lnorder, &tmxr_show_lnorder, &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &mux_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, { 0 } }; DEBTAB muxu_deb [] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "XFER", DEB_XFER }, { NULL, 0 } }; DEVICE muxu_dev = { "MUX", /* device name */ &muxu_unit, /* unit array */ muxu_reg, /* register array */ muxu_mod, /* modifier array */ 1, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ &tmxr_ex, /* examine routine */ &tmxr_dep, /* deposit routine */ &muxc_reset, /* reset routine */ NULL, /* boot routine */ &mux_attach, /* attach routine */ &mux_detach, /* detach routine */ &muxu_dib, /* device information block */ DEV_NET | DEV_DISABLE | DEV_DEBUG, /* device flags */ 0, /* debug control flags */ muxu_deb, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* MUXC data structures. muxc_dib MUXC device information block muxc_unit MUXC unit list muxc_reg MUXC register list muxc_mod MUXC modifier list muxc_dev MUXC device descriptor */ DEVICE muxc_dev; DIB muxc_dib = { MUXC, &muxcio }; UNIT muxc_unit = { UDATA (NULL, 0, 0) }; REG muxc_reg[] = { { FLDATA (CTL, muxc_control, 0) }, { FLDATA (FLG, muxc_flag, 0) }, { FLDATA (FBF, muxc_flagbuf, 0) }, { FLDATA (SCAN, muxc_scan, 0) }, { ORDATA (CHAN, muxc_chan, 4) }, { BRDATA (DSO, muxc_ota, 8, 6, MUX_LINES) }, { BRDATA (DSI, muxc_lia, 8, 2, MUX_LINES) }, { ORDATA (DEVNO, muxc_dib.devno, 6), REG_HRO }, { NULL } }; MTAB muxc_mod[] = { { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxc_dev }, { 0 } }; DEVICE muxc_dev = { "MUXM", /* device name */ &muxc_unit, /* unit array */ muxc_reg, /* register array */ muxc_mod, /* modifier array */ 1, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &muxc_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &muxc_dib, /* device information block */ DEV_DISABLE, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* Lower data card I/O signal handler. Implementation notes: 1. The operating manual says that "at least 100 milliseconds of CLC 0s must be programmed" by systems employing the multiplexer to ensure that the multiplexer resets. In practice, such systems issue 128K CLC 0 instructions. As we provide debug logging of multiplexer resets, a CRS counter is used to ensure that only one debug line is printed in response to these 128K CRS invocations. */ uint32 muxlio (uint32 select_code, IOSIG signal, uint32 data) { int32 ln; const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); const IOSIG base_signal = IOBASE (signal); /* derive base signal */ static uint32 crs_count = 0; /* cntr for ioCRS repeat */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ muxl_flag = muxl_flagbuf = CLEAR; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fputs (">>MUXl cmds: [CLF] Flag cleared\n", sim_deb); mux_data_int (); /* look for new int */ break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ muxl_flag = muxl_flagbuf = SET; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fputs (">>MUXl cmds: [STF] Flag set\n", sim_deb); break; case ioSFC: /* skip if flag is clear */ setstdSKF (muxl); break; case ioSFS: /* skip if flag is set */ setstdSKF (muxl); break; case ioIOI: /* I/O data input */ data = muxl_ibuf; if (DEBUG_PRI (muxu_dev, DEB_CPU)) fprintf (sim_deb, ">>MUXl cpu: [LIx%s] Data = %06o\n", hold_or_clear, data); break; case ioIOO: /* I/O data output */ muxl_obuf = data; /* store data */ if (DEBUG_PRI (muxu_dev, DEB_CPU)) if (data & OTL_P) fprintf (sim_deb, ">>MUXl cpu: [OTx%s] Parameter = %06o\n", hold_or_clear, data); else fprintf (sim_deb, ">>MUXl cpu: [OTx%s] Data = %06o\n", hold_or_clear, data); break; case ioPOPIO: /* power-on preset to I/O */ muxl_flag = muxl_flagbuf = SET; /* set flag andflag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ if (crs_count) /* already reset? */ break; /* skip redundant clear */ muxl_control = CLEAR; /* clear control flip-flop */ for (ln = 0; ln < MUX_LINES; ln++) { /* clear transmit info */ mux_xbuf[ln] = mux_xpar[ln] = 0; muxc_ota[ln] = muxc_lia[ln] = mux_xdon[ln] = 0; } for (ln = 0; ln < (MUX_LINES + MUX_ILINES); ln++) { mux_rbuf[ln] = mux_rpar[ln] = 0; /* clear receive info */ mux_sta[ln] = mux_rchp[ln] = 0; } break; case ioCLC: /* clear control flip-flop */ muxl_control = CLEAR; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: [CLC%s] Data interrupt inhibited\n", hold_or_clear); break; case ioSTC: /* set control flip-flop */ muxl_control = SET; /* set control */ ln = MUX_CHAN (muxu_obuf); /* get chan # */ if (muxl_obuf & OTL_TX) { /* transmit? */ if (ln < MUX_LINES) { /* line valid? */ if (muxl_obuf & OTL_P) { /* parameter? */ mux_xpar[ln] = muxl_obuf; /* store param value */ if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: [STC%s] Transmit channel %d parameter %06o stored\n", hold_or_clear, ln, muxl_obuf); } else { /* data */ if (mux_xpar[ln] & OTL_TPAR) /* parity requested? */ muxl_obuf = /* add parity bit */ muxl_obuf & ~OTL_PAR | XMT_PAR(muxl_obuf); mux_xbuf[ln] = muxl_obuf; /* load buffer */ if (sim_is_active (&muxl_unit[ln])) { /* still working? */ mux_sta[ln] = mux_sta[ln] | LIU_LOST; /* char lost */ if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: [STC%s] Transmit channel %d data overrun\n", hold_or_clear, ln); } else { if (muxu_unit.flags & UNIT_DIAG) /* loopback? */ mux_ldsc[ln].conn = 1; /* connect this line */ sim_activate (&muxl_unit[ln], muxl_unit[ln].wait); if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: [STC%s] Transmit channel %d data %06o scheduled\n", hold_or_clear, ln, muxl_obuf); } } } else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* line invalid */ fprintf (sim_deb, ">>MUXl cmds: [STC%s] Transmit channel %d invalid\n", hold_or_clear, ln); } else /* receive */ if (ln < (MUX_LINES + MUX_ILINES)) { /* line valid? */ if (muxl_obuf & OTL_P) { /* parameter? */ mux_rpar[ln] = muxl_obuf; /* store param value */ if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: [STC%s] Receive channel %d parameter %06o stored\n", hold_or_clear, ln, muxl_obuf); } else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* data (invalid action) */ fprintf (sim_deb, ">>MUXl cmds: [STC%s] Receive channel %d parameter %06o invalid action\n", hold_or_clear, ln, muxl_obuf); } else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* line invalid */ fprintf (sim_deb, ">>MUXl cmds: [STC%s] Receive channel %d invalid\n", hold_or_clear, ln); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, muxl); /* set standard PRL signal */ setstdIRQ (select_code, muxl); /* set standard IRQ signal */ setstdSRQ (select_code, muxl); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ muxl_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ muxlio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ muxlio (select_code, ioSIR, 0); /* set interrupt request */ if (signal == ioCRS) /* control reset? */ crs_count = crs_count + 1; /* increment count */ else if (crs_count && (signal != ioSIR)) { /* counting CRSes? */ if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* report reset count */ fprintf (sim_deb, ">>MUXl cmds: [CRS] Multiplexer reset %d times\n", crs_count); crs_count = 0; /* clear counter */ } return data; } /* Upper data card I/O signal handler. The upper data card does not have a control, flag, or flag buffer flip-flop. It does not drive the IRQ or SRQ lines, so the I/O dispatcher does not handle the ioSIR signal. Implementation notes: 1. The upper and lower data card hardware takes a number of actions in response to the CRS signal. Under simulation, these actions are taken by the lower data card CRS handler. */ uint32 muxuio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioIOI: /* I/O data input */ data = muxu_ibuf; if (DEBUG_PRI (muxu_dev, DEB_CPU)) fprintf (sim_deb, ">>MUXu cpu: [LIx] Status = %06o, channel = %d\n", data, MUX_CHAN(data)); break; case ioIOO: /* I/O data output */ muxu_obuf = data; /* store data */ if (DEBUG_PRI (muxu_dev, DEB_CPU)) fprintf (sim_deb, ">>MUXu cpu: [OTx] Data channel = %d\n", MUX_CHAN(data)); break; default: /* all other signals */ break; /* are ignored */ } return data; } /* Control card I/O signal handler. In diagnostic mode, the control signals C1 and C2 are looped back to status signals S1 and S2. Changing the control signals may cause an interrupt, so a test is performed after IOO processing. */ uint32 muxcio (uint32 select_code, IOSIG signal, uint32 data) { const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 ln, old; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ muxc_flag = muxc_flagbuf = CLEAR; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fputs (">>MUXc cmds: [CLF] Flag cleared\n", sim_deb); mux_ctrl_int (); /* look for new int */ break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ muxc_flag = muxc_flagbuf = SET; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fputs (">>MUXc cmds: [STF] Flag set\n", sim_deb); break; case ioSFC: /* skip if flag is clear */ setstdSKF (muxc); break; case ioSFS: /* skip if flag is set */ setstdSKF (muxc); break; case ioIOI: /* I/O data input */ data = LIC_MBO | PUT_CCH (muxc_chan) | /* mbo, chan num */ LIC_TSTI (muxc_chan) | /* I2, I1 */ (muxc_ota[muxc_chan] & (OTC_ES2 | OTC_ES1)) | /* ES2, ES1 */ (muxc_lia[muxc_chan] & (LIC_S2 | LIC_S1)); /* S2, S1 */ if (DEBUG_PRI (muxu_dev, DEB_CPU)) fprintf (sim_deb, ">>MUXc cpu: [LIx%s] Status = %06o, channel = %d\n", hold_or_clear, data, muxc_chan); muxc_chan = (muxc_chan + 1) & LIC_M_CHAN; /* incr channel */ break; case ioIOO: /* I/O data output */ ln = muxc_chan = OTC_CHAN (data); /* set channel */ if (data & OTC_SCAN) muxc_scan = 1; /* set scan flag */ else muxc_scan = 0; if (data & OTC_UPD) { /* update? */ old = muxc_ota[ln]; /* save prior val */ muxc_ota[ln] = /* save ESn,SSn */ (muxc_ota[ln] & ~OTC_RW) | (data & OTC_RW); if (data & OTC_EC2) /* if EC2, upd C2 */ muxc_ota[ln] = (muxc_ota[ln] & ~OTC_C2) | (data & OTC_C2); if (data & OTC_EC1) /* if EC1, upd C1 */ muxc_ota[ln] = (muxc_ota[ln] & ~OTC_C1) | (data & OTC_C1); if (muxu_unit.flags & UNIT_DIAG) /* loopback? */ muxc_lia[ln ^ 1] = /* set S1, S2 to C1, C2 */ (muxc_lia[ln ^ 1] & ~(LIC_S2 | LIC_S1)) | (muxc_ota[ln] & (OTC_C1 | OTC_C2)) >> OTC_V_C; else if ((muxl_unit[ln].flags & UNIT_MDM) && /* modem ctrl? */ (old & DTR) && /* DTR drop? */ !(muxc_ota[ln] & DTR)) { tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n"); tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */ muxc_lia[ln] = 0; /* dataset off */ } } /* end update */ if (DEBUG_PRI (muxu_dev, DEB_CPU)) fprintf (sim_deb, ">>MUXc cpu: [OTx%s] Parameter = %06o, channel = %d\n", hold_or_clear, data, ln); if ((muxu_unit.flags & UNIT_DIAG) && (!muxc_flag)) /* loopback and flag clear? */ mux_ctrl_int (); /* status chg may interrupt */ break; case ioPOPIO: /* power-on preset to I/O */ muxc_flag = muxc_flagbuf = SET; /* set flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ muxc_control = CLEAR; break; case ioSTC: /* set control flip-flop */ muxc_control = SET; break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, muxc); /* set standard PRL signal */ setstdIRQ (select_code, muxc); /* set standard IRQ signal */ setstdSRQ (select_code, muxc); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ muxc_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ muxcio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ muxcio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service - receive side Poll for new connections Poll all active lines for input */ t_stat muxi_svc (UNIT *uptr) { int32 ln, c; t_bool loopback; loopback = ((muxu_unit.flags & UNIT_DIAG) != 0); /* diagnostic mode? */ if (!loopback) { /* terminal mode? */ if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ if (uptr->wait == POLL_FIRST) /* first poll? */ uptr->wait = sync_poll (INITIAL); /* initial synchronization */ else /* not first */ uptr->wait = sync_poll (SERVICE); /* continue synchronization */ sim_activate (uptr, uptr->wait); /* continue polling */ ln = tmxr_poll_conn (&mux_desc); /* look for connect */ if (ln >= 0) { /* got one? */ if ((muxl_unit[ln].flags & UNIT_MDM) && /* modem ctrl? */ (muxc_ota[ln] & DTR)) /* DTR? */ muxc_lia[ln] = muxc_lia[ln] | CDET; /* set cdet */ muxc_lia[ln] = muxc_lia[ln] | DSR; /* set dsr */ mux_ldsc[ln].rcve = 1; /* rcv enabled */ } tmxr_poll_rx (&mux_desc); /* poll for input */ } for (ln = 0; ln < MUX_LINES; ln++) { /* loop thru lines */ if (mux_ldsc[ln].conn) { /* connected? */ if (loopback) { /* diagnostic mode? */ c = mux_xbuf[ln ^ 1] & OTL_CHAR; /* get char from xmit line */ if (c == 0) /* all char bits = 0? */ c = c | SCPE_BREAK; /* set break flag */ mux_ldsc[ln].conn = 0; /* clear connection */ } else if (mux_defer[ln]) /* break deferred? */ c = SCPE_BREAK; /* supply it now */ else c = tmxr_getc_ln (&mux_ldsc[ln]); /* get char from Telnet */ if (c) /* valid char? */ mux_receive (ln, c, loopback); /* process it */ } else /* not connected */ if (!loopback) /* terminal mode? */ muxc_lia[ln] = 0; /* line disconnected */ } if (!muxl_flag) mux_data_int (); /* scan for data int */ if (!muxc_flag) mux_ctrl_int (); /* scan modem */ return SCPE_OK; } /* Unit service - transmit side */ t_stat muxo_svc (UNIT *uptr) { int32 c, fc, ln, altln; t_bool loopback; ln = uptr - muxl_unit; /* line # */ altln = ln ^ 1; /* alt. line for diag mode */ fc = mux_xbuf[ln] & OTL_CHAR; /* full character data */ c = fc & 0377; /* Telnet character data */ loopback = ((muxu_unit.flags & UNIT_DIAG) != 0); /* diagnostic mode? */ if (mux_ldsc[ln].conn) { /* connected? */ if (mux_ldsc[ln].xmte) { /* xmt enabled? */ if (loopback) /* diagnostic mode? */ mux_ldsc[ln].conn = 0; /* clear connection */ else if (mux_defer[ln]) /* break deferred? */ mux_receive (ln, SCPE_BREAK, loopback); /* process it now */ if ((mux_xbuf[ln] & OTL_SYNC) == 0) { /* start bit 0? */ TMLN *lp = &mux_ldsc[ln]; /* get line */ c = sim_tt_outcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); if (mux_xpar[ln] & OTL_DIAG) /* xmt diagnose? */ mux_diag (fc); /* before munge */ if (loopback) { /* diagnostic mode? */ mux_ldsc[altln].conn = 1; /* set recv connection */ sim_activate (&muxu_unit, 1); /* schedule receive */ } else { /* no loopback */ if (c >= 0) /* valid? */ tmxr_putc_ln (lp, c); /* output char */ tmxr_poll_tx (&mux_desc); /* poll xmt */ } } mux_xdon[ln] = 1; /* set for xmit irq */ if (DEBUG_PRI (muxu_dev, DEB_XFER) && (loopback | (c >= 0))) fprintf (sim_deb, ">>MUXl xfer: Line %d character %s sent\n", ln, fmt_char ((uint8) (loopback ? fc : c))); } else { /* buf full */ tmxr_poll_tx (&mux_desc); /* poll xmt */ sim_activate (uptr, muxl_unit[ln].wait); /* wait */ return SCPE_OK; } } if (!muxl_flag) mux_data_int (); /* scan for int */ return SCPE_OK; } /* Process a character received from a multiplexer port */ void mux_receive (int32 ln, int32 c, t_bool diag) { if (c & SCPE_BREAK) { /* break? */ if (mux_defer[ln] || diag) { /* break deferred or diagnostic mode? */ mux_defer[ln] = 0; /* process now */ mux_rbuf[ln] = 0; /* break returns NUL */ mux_sta[ln] = mux_sta[ln] | LIU_BRK; /* set break status */ if (DEBUG_PRI (muxu_dev, DEB_XFER)) if (diag) fputs (">>MUXl xfer: Break detected\n", sim_deb); else fputs (">>MUXl xfer: Deferred break processed\n", sim_deb); } else { mux_defer[ln] = 1; /* defer break */ if (DEBUG_PRI (muxu_dev, DEB_XFER)) fputs (">>MUXl xfer: Break detected and deferred\n", sim_deb); return; } } else { /* normal */ if (mux_rchp[ln]) /* char already pending? */ mux_sta[ln] = mux_sta[ln] | LIU_LOST; if (!diag) { /* terminal mode? */ c = sim_tt_inpcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); if (mux_rpar[ln] & OTL_ECHO) { /* echo? */ TMLN *lp = &mux_ldsc[ln]; /* get line */ tmxr_putc_ln (lp, c); /* output char */ tmxr_poll_tx (&mux_desc); /* poll xmt */ } } mux_rbuf[ln] = c; /* save char */ } mux_rchp[ln] = 1; /* char pending */ if (DEBUG_PRI (muxu_dev, DEB_XFER)) fprintf (sim_deb, ">>MUXl xfer: Line %d character %s received\n", ln, fmt_char ((uint8) c)); if (mux_rpar[ln] & OTL_DIAG) /* diagnose this line? */ mux_diag (c); /* do diagnosis */ return; } /* Look for data interrupt */ void mux_data_int (void) { int32 i; for (i = 0; i < MUX_LINES; i++) { /* rcv lines */ if ((mux_rpar[i] & OTL_ENB) && mux_rchp[i]) { /* enabled, char? */ muxl_ibuf = PUT_DCH (i) | /* lo buf = char */ mux_rbuf[i] & LIL_CHAR | RCV_PAR (mux_rbuf[i]); muxu_ibuf = PUT_DCH (i) | mux_sta[i]; /* hi buf = stat */ mux_rchp[i] = 0; /* clr char, stat */ mux_sta[i] = 0; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: Receive channel %d interrupt requested\n", i); muxlio (muxl_dib.devno, ioENF, 0); /* interrupt */ return; } } for (i = 0; i < MUX_LINES; i++) { /* xmt lines */ if ((mux_xpar[i] & OTL_ENB) && mux_xdon[i]) { /* enabled, done? */ muxl_ibuf = PUT_DCH (i) | /* lo buf = last rcv char */ mux_rbuf[i] & LIL_CHAR | RCV_PAR (mux_rbuf[i]); muxu_ibuf = PUT_DCH (i) | mux_sta[i] | LIU_TR; /* hi buf = stat */ mux_xdon[i] = 0; /* clr done, stat */ mux_sta[i] = 0; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: Transmit channel %d interrupt requested\n", i); muxlio (muxl_dib.devno, ioENF, 0); /* interrupt */ return; } } for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) { /* diag lines */ if ((mux_rpar[i] & OTL_ENB) && mux_rchp[i]) { /* enabled, char? */ muxl_ibuf = PUT_DCH (i) | /* lo buf = char */ mux_rbuf[i] & LIL_CHAR | RCV_PAR (mux_rbuf[i]); muxu_ibuf = PUT_DCH (i) | mux_sta[i] | LIU_DG; /* hi buf = stat */ mux_rchp[i] = 0; /* clr char, stat */ mux_sta[i] = 0; if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXl cmds: Receive channel %d interrupt requested\n", i); muxlio (muxl_dib.devno, ioENF, 0); /* interrupt */ return; } } return; } /* Look for control interrupt If either of the incoming status bits does not match the stored status, and the corresponding mismatch is enabled, a control interrupt request is generated. Depending on the scan flag, we check either all 16 lines or just the current line. If an interrupt is requested, the channel counter indicates the interrupting channel. */ void mux_ctrl_int (void) { int32 i, line_count; line_count = (muxc_scan ? MUX_LINES : 1); /* check one or all lines */ for (i = 0; i < line_count; i++) { if (muxc_scan) /* scanning? */ muxc_chan = (muxc_chan + 1) & LIC_M_CHAN; /* step channel */ if (LIC_TSTI (muxc_chan)) { /* status change? */ if (DEBUG_PRI (muxu_dev, DEB_CMDS)) fprintf (sim_deb, ">>MUXc cmds: Control channel %d interrupt requested (poll = %d)\n", muxc_chan, i + 1); muxcio (muxc_dib.devno, ioENF, 0); /* set flag */ break; } } return; } /* Set diagnostic lines for given character */ void mux_diag (int32 c) { int32 i; for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) { if (c & SCPE_BREAK) { /* break? */ mux_sta[i] = mux_sta[i] | LIU_BRK; mux_rbuf[i] = 0; /* no char */ } else { if (mux_rchp[i]) mux_sta[i] = mux_sta[i] | LIU_LOST; mux_rchp[i] = 1; mux_rbuf[i] = c; } } return; } /* Reset an individual line */ void mux_reset_ln (int32 i) { mux_rbuf[i] = mux_xbuf[i] = 0; /* clear state */ mux_rpar[i] = mux_xpar[i] = 0; mux_rchp[i] = mux_xdon[i] = 0; mux_sta[i] = mux_defer[i] = 0; muxc_ota[i] = muxc_lia[i] = 0; /* clear modem */ if (mux_ldsc[i].conn && /* connected? */ ((muxu_unit.flags & UNIT_DIAG) == 0)) /* term mode? */ muxc_lia[i] = muxc_lia[i] | DSR | /* cdet, dsr */ (muxl_unit[i].flags & UNIT_MDM? CDET: 0); sim_cancel (&muxl_unit[i]); return; } /* Reset routine for lower data, upper data, and control cards */ t_stat muxc_reset (DEVICE *dptr) { int32 i; if (dptr == &muxc_dev) { /* make all consistent */ hp_enbdis_pair (dptr, &muxl_dev); hp_enbdis_pair (dptr, &muxu_dev); } else if (dptr == &muxl_dev) { hp_enbdis_pair (dptr, &muxc_dev); hp_enbdis_pair (dptr, &muxu_dev); } else { hp_enbdis_pair (dptr, &muxc_dev); hp_enbdis_pair (dptr, &muxl_dev); } if (dptr == &muxl_dev) /* lower data reset? */ muxlio (muxl_dib.devno, ioPOPIO, 0); /* send POPIO signal to lower data card */ else if (dptr == &muxu_dev) /* upper data reset? */ muxuio (muxu_dib.devno, ioPOPIO, 0); /* send POPIO signal to upper data card */ else /* control card reset */ muxcio (muxc_dib.devno, ioPOPIO, 0); /* send POPIO signal to control card */ muxc_chan = muxc_scan = 0; /* init modem scan */ if (muxu_unit.flags & UNIT_ATT) { /* master att? */ muxu_unit.wait = POLL_FIRST; /* set up poll */ sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */ } else sim_cancel (&muxu_unit); /* else stop */ for (i = 0; i < MUX_LINES; i++) mux_reset_ln (i); /* reset lines 0-15 */ for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) /* reset lines 16-20 */ mux_rbuf[i] = mux_rpar[i] = mux_sta[i] = mux_rchp[i] = 0; return SCPE_OK; } /* Attach master unit */ t_stat mux_attach (UNIT *uptr, char *cptr) { t_stat status = SCPE_OK; if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */ return SCPE_NOFNC; /* command not allowed */ status = tmxr_attach (&mux_desc, uptr, cptr); /* attach */ if (status == SCPE_OK) { muxu_unit.wait = POLL_FIRST; /* set up poll */ sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */ } return SCPE_OK; } /* Detach master unit */ t_stat mux_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&mux_desc, uptr); /* detach */ for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */ sim_cancel (uptr); /* stop poll */ return r; } /* Diagnostic/normal mode routine, Diagnostic testing wants to exercise as much of the regular simulation code as possible to ensure good test coverage. Normally, input polling and output transmission only occurs on connected lines. In diagnostic mode, line connection flags are set selectively to enable processing on the lines under test. The alternative to this would require duplicating the send/receive code; the diagnostic would then test the copy but not the actual code used for normal character transfers, which is undesirable. Therefore, to enable diagnostic mode, we must force a disconnect of the master socket and any connected Telnet lines, which clears the connection flags on all lines. Then we set the "transmission enabled" flags on all lines to enable output character processing for the diagnostic. (Normally, all of the flags are set when the multiplexer is first attached. Until then, the enable flags default to "not enabled," so we enable them explicitly here.) */ t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 ln; if (val) { /* set diag? */ mux_detach (uptr); /* detach lines */ for (ln = 0; ln < MUX_LINES; ln++) /* enable transmission */ mux_ldsc[ln].xmte = 1; /* on all lines */ } else { /* set term */ for (ln = 0; ln < MUX_LINES; ln++) /* clear connections */ mux_ldsc[ln].conn = 0; /* on all lines */ } return SCPE_OK; } simh-3.8.1/HP2100/hp2100_dr.c0000644000175000017500000007357111111106130013214 0ustar vlmvlm/* hp2100_dr.c: HP 2100 12606B/12610B fixed head disk/drum simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. DR 12606B 2770/2771 fixed head disk 12610B 2773/2774/2775 drum 09-Jul-08 JDB Revised drc_boot to use ibl_copy 26-Jun-08 JDB Rewrote device I/O to model backplane signals 28-Dec-06 JDB Added ioCRS state to I/O decoders 07-Oct-04 JDB Fixed enable/disable from either device Fixed sector return in status word Provided protected tracks and "Writing Enabled" status bit Fixed DMA last word write, incomplete sector fill value Added "parity error" status return on writes for 12606 Added track origin test for 12606 Added SCP test for 12606 Fixed 12610 SFC operation Added "Sector Flag" status bit Added "Read Inhibit" status bit for 12606 Fixed current-sector determination Added PROTECTED, UNPROTECTED, TRACKPROT modifiers 26-Aug-04 RMS Fixed CLC to stop operation (from Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Revised boot rom to use IBL algorithm Implemented DMA SRQ (follows FLG) 27-Jul-03 RMS Fixed drum sizes Fixed variable capacity interaction with SAVE/RESTORE 10-Nov-02 RMS Added BOOT command References: - 12606B Disc Memory Interface Kit Operating and Service Manual (12606-90012, Mar-1970) - 12610B Drum Memory Interface Kit Operating and Service Manual (12610-9001, Feb-1970) These head-per-track devices are buffered in memory, to minimize overhead. The drum data channel does not have a command flip-flop. Its control flip-flop is not wired into the interrupt chain; accordingly, the simulator uses command rather than control for the data channel. Its flag does not respond to SFS, SFC, or STF. The drum control channel does not have any of the traditional flip-flops. The 12606 interface implements two diagnostic tests. An SFS CC instruction will skip if the disk has passed the track origin (sector 0) since the last CLF CC instruction, and an SFC CC instruction will skip if the Sector Clock Phase (SCP) flip-flop is clear, indicating that the current sector is accessible. The 12610 interface does not support these tests; the SKF signal is not driven, so neither SFC CC nor SFS CC will skip. The interface implements a track protect mechanism via a switch and a set of on-card diodes. The switch sets the protected/unprotected status, and the particular diodes installed indicate the range of tracks (a power of 2) that are read-only in the protected mode. Somewhat unusually, writing to a protected track completes normally, but the data isn't actually written, as the write current is inhibited. There is no "failure" status indication. Instead, a program must note the lack of "Writing Enabled" status before the write is attempted. Specifications (2770/2771): - 90 sectors per logical track - 45 sectors per revolution - 64 words per sector - 2880 words per revolution - 3450 RPM = 17.4 ms/revolution - data timing = 6.0 us/word, 375 us/sector - inst timing = 4 inst/word, 11520 inst/revolution Specifications 2773/2774/2775: - 32 sectors per logical track - 32 sectors per revolution - 64 words per sector - 2048 words per revolution - 3450 RPM = 17.4 ms/revolution - data timing = 8.5 us/word, 550 us/sector - inst timing = 6 inst/word, 12288 inst/revolution */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include /* Constants */ #define DR_NUMWD 64 /* words/sector */ #define DR_FNUMSC 90 /* fhd sec/track */ #define DR_DNUMSC 32 /* drum sec/track */ #define DR_NUMSC ((drc_unit.flags & UNIT_DRUM)? DR_DNUMSC: DR_FNUMSC) #define DR_SIZE (512 * DR_DNUMSC * DR_NUMWD) /* initial size */ #define DR_FTIME 4 /* fhd per-word time */ #define DR_DTIME 6 /* drum per-word time */ #define DR_OVRHEAD 5 /* overhead words at track start */ #define UNIT_V_PROT (UNIT_V_UF + 0) /* track protect */ #define UNIT_V_SZ (UNIT_V_UF + 1) /* disk vs drum */ #define UNIT_M_SZ 017 /* size */ #define UNIT_PROT (1 << UNIT_V_PROT) #define UNIT_SZ (UNIT_M_SZ << UNIT_V_SZ) #define UNIT_DRUM (1 << UNIT_V_SZ) /* low order bit */ #define SZ_180K 000 /* disks */ #define SZ_360K 002 #define SZ_720K 004 #define SZ_1024K 001 /* drums: default size */ #define SZ_1536K 003 #define SZ_384K 005 #define SZ_512K 007 #define SZ_640K 011 #define SZ_768K 013 #define SZ_896K 015 #define DR_GETSZ(x) (((x) >> UNIT_V_SZ) & UNIT_M_SZ) /* Command word */ #define CW_WR 0100000 /* write vs read */ #define CW_V_FTRK 7 /* fhd track */ #define CW_M_FTRK 0177 #define CW_V_DTRK 5 /* drum track */ #define CW_M_DTRK 01777 #define MAX_TRK (((drc_unit.flags & UNIT_DRUM)? CW_M_DTRK: CW_M_FTRK) + 1) #define CW_GETTRK(x) ((drc_unit.flags & UNIT_DRUM)? \ (((x) >> CW_V_DTRK) & CW_M_DTRK): \ (((x) >> CW_V_FTRK) & CW_M_FTRK)) #define CW_PUTTRK(x) ((drc_unit.flags & UNIT_DRUM)? \ (((x) & CW_M_DTRK) << CW_V_DTRK): \ (((x) & CW_M_FTRK) << CW_V_FTRK)) #define CW_V_FSEC 0 /* fhd sector */ #define CW_M_FSEC 0177 #define CW_V_DSEC 0 /* drum sector */ #define CW_M_DSEC 037 #define CW_GETSEC(x) ((drc_unit.flags & UNIT_DRUM)? \ (((x) >> CW_V_DSEC) & CW_M_DSEC): \ (((x) >> CW_V_FSEC) & CW_M_FSEC)) #define CW_PUTSEC(x) ((drc_unit.flags & UNIT_DRUM)? \ (((x) & CW_M_DSEC) << CW_V_DSEC): \ (((x) & CW_M_FSEC) << CW_V_FSEC)) /* Status register, ^ = dynamic */ #define DRS_V_NS 8 /* ^next sector */ #define DRS_M_NS 0177 #define DRS_SEC 0100000 /* ^sector flag */ #define DRS_RDY 0000200 /* ^ready */ #define DRS_RIF 0000100 /* ^read inhibit */ #define DRS_SAC 0000040 /* sector coincidence */ #define DRS_ABO 0000010 /* abort */ #define DRS_WEN 0000004 /* ^write enabled */ #define DRS_PER 0000002 /* parity error */ #define DRS_BSY 0000001 /* ^busy */ #define CALC_SCP(x) (((int32) fmod ((x) / (double) dr_time, \ (double) (DR_NUMWD))) >= (DR_NUMWD - 3)) int32 drc_cw = 0; /* fnc, addr */ int32 drc_sta = 0; /* status */ int32 drc_run = 0; /* run flip-flop */ FLIP_FLOP drd_control = CLEAR; FLIP_FLOP drd_flag = CLEAR; int32 drd_ibuf = 0; /* input buffer */ int32 drd_obuf = 0; /* output buffer */ int32 drd_ptr = 0; /* sector pointer */ int32 drc_pcount = 1; /* number of prot tracks */ int32 dr_stopioe = 1; /* stop on error */ int32 dr_time = DR_DTIME; /* time per word */ static int32 sz_tab[16] = { 184320, 1048576, 368640, 1572864, 737280, 393216, 0, 524288, 0, 655360, 0, 786432, 0, 917504, 0, 0 }; DEVICE drd_dev, drc_dev; uint32 drdio (uint32 select_code, IOSIG signal, uint32 data); uint32 drcio (uint32 select_code, IOSIG signal, uint32 data); t_stat drc_svc (UNIT *uptr); t_stat drc_reset (DEVICE *dptr); t_stat drc_attach (UNIT *uptr, char *cptr); t_stat drc_boot (int32 unitno, DEVICE *dptr); int32 dr_incda (int32 trk, int32 sec, int32 ptr); int32 dr_seccntr (double simtime); t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dr_show_prot (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* DRD data structures drd_dev device descriptor drd_unit unit descriptor drd_reg register list */ DIB dr_dib[] = { { DRD, &drdio }, { DRC, &drcio } }; #define drd_dib dr_dib[0] #define drc_dib dr_dib[1] UNIT drd_unit[] = { { UDATA (NULL, 0, 0) }, { UDATA (NULL, UNIT_DIS, 0) } }; #define TMR_ORG 0 /* origin timer */ #define TMR_INH 1 /* inhibit timer */ REG drd_reg[] = { { ORDATA (IBUF, drd_ibuf, 16) }, { ORDATA (OBUF, drd_obuf, 16) }, { FLDATA (CTL, drd_control, 0) }, { FLDATA (FLG, drd_flag, 0) }, { ORDATA (BPTR, drd_ptr, 6) }, { ORDATA (DEVNO, drd_dib.devno, 6), REG_HRO }, { NULL } }; MTAB drd_mod[] = { { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &drd_dev }, { 0 } }; DEVICE drd_dev = { "DRD", drd_unit, drd_reg, drd_mod, 2, 0, 0, 0, 0, 0, NULL, NULL, &drc_reset, NULL, NULL, NULL, &drd_dib, DEV_DISABLE }; /* DRC data structures drc_dev device descriptor drc_unit unit descriptor drc_mod unit modifiers drc_reg register list */ UNIT drc_unit = { UDATA (&drc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UNIT_MUSTBUF+UNIT_DRUM+UNIT_BINK, DR_SIZE) }; REG drc_reg[] = { { DRDATA (PCNT, drc_pcount, 10), REG_HIDDEN | PV_LEFT }, { ORDATA (CW, drc_cw, 16) }, { ORDATA (STA, drc_sta, 16) }, { FLDATA (RUN, drc_run, 0) }, { DRDATA (TIME, dr_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, dr_stopioe, 0) }, { ORDATA (DEVNO, drc_dib.devno, 6), REG_HRO }, { DRDATA (CAPAC, drc_unit.capac, 24), REG_HRO }, { NULL } }; MTAB drc_mod[] = { { UNIT_DRUM, 0, "disk", NULL, NULL }, { UNIT_DRUM, UNIT_DRUM, "drum", NULL, NULL }, { UNIT_SZ, (SZ_180K << UNIT_V_SZ), NULL, "180K", &dr_set_size }, { UNIT_SZ, (SZ_360K << UNIT_V_SZ), NULL, "360K", &dr_set_size }, { UNIT_SZ, (SZ_720K << UNIT_V_SZ), NULL, "720K", &dr_set_size }, { UNIT_SZ, (SZ_384K << UNIT_V_SZ), NULL, "384K", &dr_set_size }, { UNIT_SZ, (SZ_512K << UNIT_V_SZ), NULL, "512K", &dr_set_size }, { UNIT_SZ, (SZ_640K << UNIT_V_SZ), NULL, "640K", &dr_set_size }, { UNIT_SZ, (SZ_768K << UNIT_V_SZ), NULL, "768K", &dr_set_size }, { UNIT_SZ, (SZ_896K << UNIT_V_SZ), NULL, "896K", &dr_set_size }, { UNIT_SZ, (SZ_1024K << UNIT_V_SZ), NULL, "1024K", &dr_set_size }, { UNIT_SZ, (SZ_1536K << UNIT_V_SZ), NULL, "1536K", &dr_set_size }, { UNIT_PROT, UNIT_PROT, "protected", "PROTECTED", NULL }, { UNIT_PROT, 0, "unprotected", "UNPROTECTED", NULL }, { MTAB_XTD | MTAB_VDV, 0, "TRACKPROT", "TRACKPROT", &dr_set_prot, &dr_show_prot, NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &drd_dev }, { 0 } }; DEVICE drc_dev = { "DRC", &drc_unit, drc_reg, drc_mod, 1, 8, 21, 1, 8, 16, NULL, NULL, &drc_reset, &drc_boot, &drc_attach, NULL, &drc_dib, DEV_DISABLE }; /* Data channel I/O signal handler. The data channel card does not follow the usual interface I/O configuration. PRL is always asserted, the card does not drive IRQ, FLG, or SKF and does not respond to IAK. SRQ is driven by the output of the flag flip-flop, which obeys CLF only. There is no flag buffer. The control flip-flop obeys STC and CLC. Clearing control clears the flag flip-flop, and setting control sets the flag flip-flop if the interface is configured for writing. On the 12606B, POPIO and CRS clear the track address register. Implementation notes: 1. In response to CRS, the 12606B data channel clears only the track address register; the command channel clears the sector address register and the direction flip-flop. Under simulation, all three form the control word, and as CRS is sent to all devices, we simply clear the control word here. */ uint32 drdio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 t; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ drd_flag = CLEAR; break; case ioENF: /* enable flag */ drd_flag = SET; break; case ioIOI: /* I/O data input */ data = drd_ibuf; break; case ioIOO: /* I/O data output */ drd_obuf = data; break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ if (!(drc_unit.flags & UNIT_DRUM)) /* 12606B? */ drc_cw = 0; /* clear control word */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ drd_flag = drd_control = CLEAR; /* clear control and flag */ if (!drc_run) /* cancel curr op */ sim_cancel (&drc_unit); drc_sta = drc_sta & ~DRS_SAC; /* clear SAC flag */ break; case ioSTC: /* set control flip-flop */ drd_control = SET; /* set ctl */ if (drc_cw & CW_WR) /* writing? */ drd_flag = SET; /* prime DMA */ drc_sta = 0; /* clr status */ drd_ptr = 0; /* clear sec ptr */ sim_cancel (&drc_unit); /* cancel curr op */ t = CW_GETSEC (drc_cw) - dr_seccntr (sim_gtime()); if (t <= 0) t = t + DR_NUMSC; sim_activate (&drc_unit, t * DR_NUMWD * dr_time); break; case ioSIR: /* set interrupt request */ setstdSRQ (select_code, drd); /* set SRQ signal */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ drdio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ drdio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command channel I/O signal dispatcher. The command channel card does not follow the usual interface I/O configuration. PRL is always asserted, the card does not drive IRQ, FLG, or SRQ and does not respond to IAK. There are no control, flag, or flag buffer flip-flops. CLF clears the track origin flip-flop; STF is ignored. The 12606B drives SKF, whereas the 12610B does not. On the 12610B, SFS tests the Track Origin flip-flop, and SFC tests the Sector Clock Phase (SCP) flip-flop. Implementation notes: 1. CRS clears the Run Flip-Flop, stopping the current operation. Under simulation, we allow the data channel signal handler to do this, as the same operation is invoked by CLC DC, and as CRS is sent to all devices. 2. The command channel cannot interrupt, so there is no SIR handler. */ uint32 drcio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 sec; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ if (!(drc_unit.flags & UNIT_DRUM)) { /* disk? */ sec = dr_seccntr (sim_gtime ()); /* current sector */ sim_cancel (&drd_unit[TMR_ORG]); /* sched origin tmr */ sim_activate (&drd_unit[TMR_ORG], (DR_FNUMSC - sec) * DR_NUMWD * dr_time); } break; case ioSFC: /* skip if flag is clear */ if (!(drc_unit.flags & UNIT_DRUM)) /* 12606? */ setSKF (!(CALC_SCP (sim_gtime()))); /* skip if nearing end of sector */ break; case ioSFS: /* skip if flag is set */ if (!(drc_unit.flags & UNIT_DRUM)) /* 12606? */ setSKF (!sim_is_active (&drd_unit[TMR_ORG])); /* skip if origin seen */ break; case ioIOI: /* I/O data input */ data = drc_sta; /* static bits */ if (!(drc_unit.flags & UNIT_PROT) || /* not protected? */ (CW_GETTRK(drc_cw) >= drc_pcount)) /* or not in range? */ data = data | DRS_WEN; /* set wrt enb status */ if (drc_unit.flags & UNIT_ATT) { /* attached? */ data = data | (dr_seccntr (sim_gtime()) << DRS_V_NS) | DRS_RDY; if (sim_is_active (&drc_unit)) /* op in progress? */ data = data | DRS_BSY; if (CALC_SCP (sim_gtime())) /* SCP ff set? */ data = data | DRS_SEC; /* set sector flag */ if (sim_is_active (&drd_unit[TMR_INH]) && /* inhibit timer on? */ !(drc_cw & CW_WR)) data = data | DRS_RIF; /* set read inh flag */ } break; case ioIOO: /* I/O data output */ if (!(drc_unit.flags & UNIT_DRUM)) { /* disk? */ sim_cancel (&drd_unit[TMR_INH]); /* schedule inhibit timer */ sim_activate (&drd_unit[TMR_INH], DR_FTIME * DR_NUMWD); } drc_cw = data; /* get control word */ break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ break; /* allow data channel to handle this */ default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ drcio (select_code, ioCLF, 0); /* issue CLF */ return data; } /* Unit service */ t_stat drc_svc (UNIT *uptr) { int32 trk, sec; uint32 da; uint16 *bptr = (uint16 *) uptr->filebuf; if ((uptr->flags & UNIT_ATT) == 0) { drc_sta = DRS_ABO; return IORETURN (dr_stopioe, SCPE_UNATT); } trk = CW_GETTRK (drc_cw); sec = CW_GETSEC (drc_cw); da = ((trk * DR_NUMSC) + sec) * DR_NUMWD; drc_sta = drc_sta | DRS_SAC; drc_run = 1; /* set run ff */ if (drc_cw & CW_WR) { /* write? */ if ((da < uptr->capac) && (sec < DR_NUMSC)) { bptr[da + drd_ptr] = drd_obuf; if (((uint32) (da + drd_ptr)) >= uptr->hwmark) uptr->hwmark = da + drd_ptr + 1; } drd_ptr = dr_incda (trk, sec, drd_ptr); /* inc disk addr */ if (drd_control) { /* dch active? */ drdio (drd_dib.devno, ioENF, 0); /* set SRQ */ sim_activate (uptr, dr_time); /* sched next word */ } else { /* done */ if (drd_ptr) /* need to fill? */ for ( ; drd_ptr < DR_NUMWD; drd_ptr++) bptr[da + drd_ptr] = drd_obuf; /* fill with last word */ if (!(drc_unit.flags & UNIT_DRUM)) /* disk? */ drc_sta = drc_sta | DRS_PER; /* parity bit sets on write */ drc_run = 0; /* clear run ff */ } } /* end write */ else { /* read */ if (drd_control) { /* dch active? */ if ((da >= uptr->capac) || (sec >= DR_NUMSC)) drd_ibuf = 0; else drd_ibuf = bptr[da + drd_ptr]; drd_ptr = dr_incda (trk, sec, drd_ptr); drdio (drd_dib.devno, ioENF, 0); /* set SRQ */ sim_activate (uptr, dr_time); /* sched next word */ } else drc_run = 0; /* clear run ff */ } return SCPE_OK; } /* Increment current disk address */ int32 dr_incda (int32 trk, int32 sec, int32 ptr) { ptr = ptr + 1; /* inc pointer */ if (ptr >= DR_NUMWD) { /* end sector? */ ptr = 0; /* new sector */ sec = sec + 1; /* adv sector */ if (sec >= DR_NUMSC) { /* end track? */ sec = 0; /* new track */ trk = trk + 1; /* adv track */ if (trk >= MAX_TRK) trk = 0; /* wraps at max */ } drc_cw = (drc_cw & CW_WR) | CW_PUTTRK (trk) | CW_PUTSEC (sec); } return ptr; } /* Read the sector counter The hardware sector counter contains the number of the next sector that will pass under the heads (so it is one ahead of the current sector). For the duration of the last sector of the track, the sector counter contains 90 for the 12606 and 0 for the 12610. The sector counter resets to 0 at track origin and increments at the start of the first sector. Therefore, the counter value ranges from 0-90 for the 12606 and 0-31 for the 12610. The 0 state is quite short in the 12606 and long in the 12610, relative to the other sector counter states. The simulated sector counter is calculated from the simulation time, based on the time per word and the number of words per track. */ int32 dr_seccntr (double simtime) { int32 curword; curword = (int32) fmod (simtime / (double) dr_time, (double) (DR_NUMWD * DR_NUMSC + DR_OVRHEAD)); if (curword <= DR_OVRHEAD) return 0; else return ((curword - DR_OVRHEAD) / DR_NUMWD + ((drc_unit.flags & UNIT_DRUM)? 0: 1)); } /* Reset routine */ t_stat drc_reset (DEVICE *dptr) { hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &drd_dev)? &drc_dev: &drd_dev); if (sim_switches & SWMASK ('P')) /* PON reset? */ drc_sta = drc_cw = drd_ptr = 0; /* clear controller state variables */ if (dptr == &drc_dev) /* command channel reset? */ drcio (drc_dib.devno, ioPOPIO, 0); /* send POPIO signal to command channel */ else /* data channel reset */ drdio (drd_dib.devno, ioPOPIO, 0); /* send POPIO signal to data channel */ sim_cancel (&drc_unit); sim_cancel (&drd_unit[TMR_ORG]); sim_cancel (&drd_unit[TMR_INH]); return SCPE_OK; } /* Attach routine */ t_stat drc_attach (UNIT *uptr, char *cptr) { int32 sz = sz_tab[DR_GETSZ (uptr->flags)]; if (sz == 0) return SCPE_IERR; uptr->capac = sz; return attach_unit (uptr, cptr); } /* Set protected track count */ t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 count; t_stat status; if (cptr == NULL) return SCPE_ARG; count = (int32) get_uint (cptr, 10, 768, &status); if (status != SCPE_OK) return status; else switch (count) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: drc_pcount = count; break; case 256: case 512: case 768: if (drc_unit.flags & UNIT_DRUM) drc_pcount = count; else return SCPE_ARG; break; default: return SCPE_ARG; } return SCPE_OK; } /* Show protected track count */ t_stat dr_show_prot (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "protected tracks=%d", drc_pcount); return SCPE_OK; } /* Set size routine */ t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 sz; int32 szindex; if (val < 0) return SCPE_IERR; if ((sz = sz_tab[szindex = DR_GETSZ (val)]) == 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = sz; if (szindex & UNIT_DRUM) dr_time = DR_DTIME; /* drum */ else { dr_time = DR_FTIME; /* disk */ if (drc_pcount > 128) drc_pcount = 128; /* max prot track count */ } return SCPE_OK; } /* Fixed head disk/drum bootstrap routine (disc subset of disc/paper tape loader) */ #define BOOT_START 060 static const BOOT_ROM dr_rom = { /* padded to start at x7760 */ 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, 0020010, /*DMA 20000+DC */ 0000000, /* 0 */ 0107700, /* CLC 0,C */ 0063756, /* LDA DMA ; DMA ctrl */ 0102606, /* OTA 6 */ 0002700, /* CLA,CCE */ 0102611, /* OTA CC ; trk = sec = 0 */ 0001500, /* ERA ; A = 100000 */ 0102602, /* OTA 2 ; DMA in, addr */ 0063777, /* LDA M64 */ 0102702, /* STC 2 */ 0102602, /* OTA 2 ; DMA wc = -64 */ 0103706, /* STC 6,C ; start DMA */ 0067776, /* LDB JSF ; get JMP . */ 0074077, /* STB 77 ; in base page */ 0102710, /* STC DC ; start disc */ 0024077, /*JSF JMP 77 ; go wait */ 0177700 /*M64 -100 */ }; t_stat drc_boot (int32 unitno, DEVICE *dptr) { const int32 dev = drd_dib.devno; /* data chan select code */ if (unitno != 0) /* only unit 0 */ return SCPE_NOFNC; if (ibl_copy (dr_rom, dev)) /* copy boot to memory */ return SCPE_IERR; WritePW (PC + IBL_DPC, dr_rom [IBL_DPC]); /* restore overwritten word */ WritePW (PC + IBL_END, dr_rom [IBL_END]); /* restore overwritten word */ PC = PC + BOOT_START; /* correct starting address */ return SCPE_OK; } simh-3.8.1/HP2100/hp2100_baci.c0000644000175000017500000022442611113064650013516 0ustar vlmvlm/* hp2100_baci.c: HP 12966A buffered asynchronous communications interface simulator Copyright (c) 2007-2008, J. David Bryan 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 AUTHOR 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 author. BACI 12966A BACI card 25-Nov-08 JDB Revised for new multiplexer library SHOW routines 11-Sep-08 JDB Fixed STC,C losing interrupt request on BREAK 07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h Changed Telnet poll to connect immediately after reset or attach 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size 26-Jun-08 JDB Rewrote device I/O to model backplane signals 17-Jun-08 JDB Moved fmt_char() function to hp2100_sys.c 13-Jun-08 JDB Cleaned up debug reporting for sim_activate calls 16-Apr-08 JDB Separated terminal I/O and Telnet poll for idle compatibility 07-Dec-07 JDB Created BACI device References: - HP 12966A Buffered Asynchronous Data Communications Interface Installation and Reference Manual (12966-90001, Jul-1982) - Western Digital Communications Products Handbook (Jun-1984) The 12966A BACI card supplanted the 12531C Teletype and 12880A CRT interfaces as the primary terminal connection for HP 1000 systems. The main advantage of this card over the others was its 128-character FIFO memory. While this allowed more efficient I/O than its interrupt-per-character predecessors, the most significant advantage was that block input from the 264x-series of CRT terminals was supported. The 264x were the first HP-supported terminals to provide local editing and character storage, as well as mass storage via dual DC-100 minicartridge drives. This support meant that input from the terminal could come in bursts at the full baud rate, which would overrun the older cards that needed a small intercharacter handling time. Also, the older cards placed a substantial load on the CPU in high-baud-rate output applications. Indeed, block output under RTE on a 1000 M-Series with a 12880A CRT card would saturate the CPU at about 5700 baud. For a while, the BACI and the earlier cards were both supported as the system console interface, and RTE primary systems were generated with drivers for both cards. The boot-time I/O reconfigurator would detect the presence of the BACI card and would dynamically select the correct driver (DVR05 vs. DVR00). However, the 12880A card faded quickly as the 264x and later 262x terminals gained in popularity, and support for the 12880A was dropped in favor of the BACI. This meant that later RTE primary systems could only be run on CPUs containing a BACI card. The simulation supports terminal and diagnostic modes. The latter simulates the installation of the 12966-60003 diagnostic loopback connector on the card. Fifteen programmable baud rates were supported by the BACI. We simulate these "realistic" rates by scheduling I/O service based on the appropriate number of 1000 E-Series instructions for the rate selected. We also provide an "external rate" that is equivalent to 9600 baud, as most terminals were set to their maximum speeds. We support the 12966A connected to an HP terminal emulator via Telnet. Internally, we model the BACI as a terminal multiplexer with one line. The simulation is complicated by the half-duplex nature of the card (there is only one FIFO, used selectively either for transmission or reception) and the double-buffered UART (a Western Digital TR1863A), which has holding registers as well as a shift registers for transmission and reception. We model both sets of device registers. During an output operation, the first character output to the card passes through the FIFO and into the transmitter holding register. Subsequent characters remain in the FIFO. If the FIFO is then turned around by a mode switch from transmission to reception, the second character output becomes the first character input to the CPU, as the first character output remains in the THR. Also, the FIFO counter reflects the combined state of the FIFO and the THR: it is incremented by a "shift in" to the FIFO and decremented by the "transmit complete" signal from the UART. This has two implications: 1. If the FIFO is turned around before the character in the THR is transmitted, the counter will not decrement when transmission is complete, so the FIFO will show as "empty" when the counter reads "1". 2. The FIFO counter will indicate "half full" and "full" one character before the FIFO itself reaches those stages. The diagnostic hood connects the UART clock to a spare output register. This allows the diagnostic to supply programmed clock pulses to the UART. The serial transmit and receive lines from the UART are also available to the diagnostic. Functional operation is checked by supplying or testing serial data while clocking the UART sixteen times for each bit. This meant that we had to model the UART shift registers for faithful hardware simulation. The simulation provides both the "realistic timing" described above, as well as an "optimized (fast) timing" option. Optimization makes three improvements: 1. On output, characters in the FIFO are emptied into the Telnet buffer as a block, rather than one character per service call, and on input, all of the characters available in the Telnet buffer are loaded into the FIFO as a block. 2. The ENQ/ACK handshake is done locally, without involving the Telnet client. 3. Input occurring during an output operation is delayed until the second or third consecutive ENQ/ACK handshake. During development, it was noted that a comparatively long time elapsed (approximately 30 milliseconds on a 3 GHz system) between the transmission of an ENQ and the reception of the ACK. As the RTE BACI driver, DVR05, does three ENQ/ACKs at the end of each line, plus an additional ENQ/ACK every 33 characters within a line, maximum throughput was about ten lines per second. The source of this delay is not understood but apparently lies within the terminal emulator, as it was observed with two emulators from two different companies. Absorbing the ENQ and generating the ACK locally provided a dramatic improvement in output speed. However, as a result, RTE break-mode became effectively impossible, i.e., striking a key during output no longer produced the break-mode prompt. This was traced to the RTE driver. DVR05 only checks for an input character during ENQ/ACK processing, and then only during the second and third end-of-line handshakes. When the ENQ/ACKs were eliminated, break-mode also disappeared. The workaround is to save a character received during output and supply it during the second or third consecutive handshake. This ensures that break-mode is recognized. Because the driver tries to "cheat" the card by selecting receive mode before the ENQ has actually been transmitted (in order to save an interrupt), the FIFO counter becomes "off by one" and is reset with a master clear at the end of each handshake. This would normally clear the UART receiving register, thereby losing the deferred character. We work around this by skipping the register clear in "fast timing" mode. */ #include #include "hp2100_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" /* Program limits */ #define FIFO_SIZE 128 /* read/write buffer size */ /* Character constants */ #define ENQ '\005' #define ACK '\006' /* Unit flags */ #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ #define UNIT_V_FASTTIME (UNIT_V_UF + 1) /* fast timing mode */ #define UNIT_V_CAPSLOCK (UNIT_V_UF + 2) /* caps lock mode */ #define UNIT_DIAG (1 << UNIT_V_DIAG) #define UNIT_FASTTIME (1 << UNIT_V_FASTTIME) #define UNIT_CAPSLOCK (1 << UNIT_V_CAPSLOCK) /* Debug flags */ #define DEB_CMDS (1 << 0) /* commands and status */ #define DEB_CPU (1 << 1) /* CPU I/O */ #define DEB_BUF (1 << 2) /* buffer gets and puts */ #define DEB_XFER (1 << 3) /* character reads and writes */ /* Bit flags */ #define OUT_MR 0100000 /* common master reset */ #define OUT_ENCM 0000040 /* ID1: enable character mode */ #define OUT_ENCB 0000020 /* ID1: enable CB */ #define OUT_ENCC 0000010 /* ID1: enable CC */ #define OUT_ENCE 0000004 /* ID1: enable CE */ #define OUT_ENCF 0000002 /* ID1: enable CF */ #define OUT_ENSXX 0000001 /* ID1: enable SBB/SCF */ #define OUT_DIAG 0000040 /* ID2: diagnostic output */ #define OUT_REFCB 0000020 /* ID2: reference CB */ #define OUT_REFCC 0000010 /* ID2: reference CC */ #define OUT_REFCE 0000004 /* ID2: reference CE */ #define OUT_REFCF 0000002 /* ID2: reference CF */ #define OUT_REFSXX 0000001 /* ID2: reference SBB/SCF */ #define OUT_STBITS 0000040 /* ID3: number of stop bits */ #define OUT_ECHO 0000020 /* ID3: enable echo */ #define OUT_PARITY 0000010 /* ID3: enable parity */ #define OUT_PAREVEN 0000004 /* ID3: even parity or odd */ #define OUT_XMIT 0000400 /* ID4: transmit or receive */ #define OUT_CA 0000200 /* ID4: CA on */ #define OUT_CD 0000100 /* ID4: CD on */ #define OUT_SXX 0000040 /* ID4: SBA/SCA on */ #define OUT_DCPC 0000020 /* ID4: DCPC on */ #define OUT_CSC 0000040 /* ID5: clear special char interrupt */ #define OUT_CBH 0000020 /* ID5: clear buffer half-full interrupt */ #define OUT_CBF 0000010 /* ID5: clear buffer full interrupt */ #define OUT_CBE 0000004 /* ID5: clear buffer empty interrupt */ #define OUT_CBRK 0000002 /* ID5: clear break interrupt */ #define OUT_COVR 0000001 /* ID5: clear overrun/parity interrupt */ #define OUT_SPFLAG 0000400 /* ID6: special character */ #define OUT_IRQCLR (OUT_CBH | OUT_CBF | OUT_CBE | OUT_CBRK | OUT_COVR) #define IN_VALID 0100000 /* received data: character valid */ #define IN_SPFLAG 0040000 /* received data: is special character */ #define IN_DEVINT 0100000 /* status: device interrupt */ #define IN_SPCHAR 0040000 /* status: special char has been recd */ #define IN_SPARE 0010000 /* status: spare receiver state */ #define IN_TEST 0004000 /* status: unprocessed serial data line */ #define IN_BUFHALF 0001000 /* status: buffer is half full */ #define IN_BUFFULL 0000400 /* status: buffer is full */ #define IN_BUFEMPTY 0000200 /* status: buffer is empty */ #define IN_BREAK 0000100 /* status: break detected */ #define IN_OVRUNPE 0000040 /* status: overrun or parity error */ #define IN_CB 0000020 /* status: CB is on */ #define IN_CC 0000010 /* status: CC is on */ #define IN_CE 0000004 /* status: CE is on */ #define IN_CF 0000002 /* status: CF is on */ #define IN_SXX 0000001 /* status: SBB/SCF is on */ #define IN_MODEM (IN_CB | IN_CC | IN_CE | IN_CF | IN_SXX) #define IN_DIAG (IN_DEVINT | IN_SPARE | IN_TEST | IN_MODEM) #define IN_STDIRQ (IN_DEVINT | IN_SPCHAR | IN_BREAK | IN_OVRUNPE) #define IN_FIFOIRQ (IN_BUFEMPTY | IN_BUFHALF | IN_BUFFULL) /* Packed starting bit numbers */ #define OUT_V_ID 12 /* common output word ID */ #define OUT_V_DATA 0 /* ID 0: output data character */ #define OUT_V_CHARSIZE 0 /* ID 3: character size */ #define OUT_V_BAUDRATE 0 /* ID 4: baud rate */ #define OUT_V_SPCHAR 0 /* ID 6: special character */ #define IN_V_CHARCNT 8 /* data: char count in buffer */ #define IN_V_DATA 0 /* data: input character */ #define IN_V_IRQCLR 5 /* status: interrupt status clear */ /* Packed bit widths */ #define OUT_W_ID 3 #define OUT_W_DATA 8 #define OUT_W_CHARSIZE 2 #define OUT_W_BAUDRATE 4 #define OUT_W_SPCHAR 8 #define IN_W_CHARCNT 6 #define IN_W_DATA 8 /* Packed bit masks */ #define OUT_M_ID ((1 << OUT_W_ID) - 1) #define OUT_M_DATA ((1 << OUT_W_DATA) - 1) #define OUT_M_CHARSIZE ((1 << OUT_W_CHARSIZE) - 1) #define OUT_M_BAUDRATE ((1 << OUT_W_BAUDRATE) - 1) #define OUT_M_SPCHAR ((1 << OUT_W_SPCHAR) - 1) #define IN_M_CHARCNT ((1 << IN_W_CHARCNT) - 1) #define IN_M_DATA ((1 << IN_W_DATA) - 1) /* Packed field masks */ #define OUT_ID (OUT_M_ID << OUT_V_ID) #define OUT_DATA (OUT_M_DATA << OUT_V_DATA) #define OUT_CHARSIZE (OUT_M_CHARSIZE << OUT_V_CHARSIZE) #define OUT_BAUDRATE (OUT_M_BAUDRATE << OUT_V_BAUDRATE) #define OUT_SPCHAR (OUT_M_SPCHAR << OUT_V_SPCHAR) #define IN_CHARCNT (IN_M_CHARCNT << IN_V_CHARCNT) #define IN_DATA (IN_M_DATA << IN_V_DATA) /* Command helpers */ #define TO_CHARCNT(c) (((c) << IN_V_CHARCNT) & IN_CHARCNT) #define GET_ID(i) (((i) & OUT_ID) >> OUT_V_ID) #define GET_BAUDRATE(b) (((b) & OUT_BAUDRATE) >> OUT_V_BAUDRATE) #define IO_MODE (baci_icw & OUT_XMIT) #define XMIT OUT_XMIT #define RECV 0 #define CLEAR_HR 0 /* UART holding register clear value */ #define CLEAR_R -1 /* UART register clear value */ /* Unit references */ #define baci_term baci_unit[0] /* terminal I/O unit */ #define baci_poll baci_unit[1] /* Telnet polling unit */ /* BACI state variables */ FLIP_FLOP baci_control = CLEAR; /* control flip-flop */ FLIP_FLOP baci_flag = CLEAR; /* flag flip-flop */ FLIP_FLOP baci_flagbuf = CLEAR; /* flag buffer flip-flop */ FLIP_FLOP baci_srq = CLEAR; /* SRQ flip-flop */ FLIP_FLOP baci_lockout = CLEAR; /* interrupt lockout flip-flop */ uint16 baci_ibuf = 0; /* status/data in */ uint16 baci_obuf = 0; /* command/data out */ uint16 baci_status = 0; /* current status */ uint16 baci_edsiw = 0; /* enable device status word */ uint16 baci_dsrw = 0; /* device status reference word */ uint16 baci_cfcw = 0; /* character frame control word */ uint16 baci_icw = 0; /* interface control word */ uint16 baci_isrw = 0; /* interrupt status reset word */ uint32 baci_fput = 0; /* FIFO buffer add index */ uint32 baci_fget = 0; /* FIFO buffer remove index */ uint32 baci_fcount = 0; /* FIFO buffer counter */ uint32 baci_bcount = 0; /* break counter */ uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ uint8 baci_spchar [256]; /* special character RAM */ uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */ uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */ int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */ int32 baci_uart_rr = CLEAR_R; /* UART receiver register */ uint32 baci_uart_clk = 0; /* UART transmit/receive clock */ t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ uint32 baci_enq_cntr = 0; /* ENQ seen counter */ /* Terminal multiplexer library interface */ TMLN baci_ldsc = { 0 }; /* line descriptor */ TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */ /* BACI local routines */ static int32 service_time (uint32 control_word); static void update_status (void); static void master_reset (void); static uint32 fifo_get (void); static void fifo_put (uint8 ch); static void clock_uart (void); /* BACI global routines */ uint32 baci_io (uint32 select_code, IOSIG signal, uint32 data); t_stat baci_term_svc (UNIT *uptr); t_stat baci_poll_svc (UNIT *uptr); t_stat baci_reset (DEVICE *dptr); t_stat baci_attach (UNIT *uptr, char *cptr); t_stat baci_detach (UNIT *uptr); /* BACI data structures baci_dib BACI device information block baci_dev BACI device descriptor baci_unit BACI unit list baci_reg BACI register list baci_mod BACI modifier list baci_deb BACI debug list Two units are used: one to handle character I/O via the Telnet library, and another to poll for connections and input. The character I/O service routine runs only when there are characters to read or write. It operates at the approximate baud rate of the terminal (in CPU instructions per second) in order to be compatible with the OS drivers. The Telnet poll must run continuously, but it can operate much more slowly, as the only requirement is that it must not present a perceptible lag to human input. To be compatible with CPU idling, it is co-scheduled with the master poll timer, which uses a ten millisecond period. */ DIB baci_dib = { BACI, &baci_io }; DEVICE baci_dev; UNIT baci_unit[] = { { UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */ { UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } }; /* Telnet poll unit */ REG baci_reg[] = { { ORDATA (IBUF, baci_ibuf, 16), REG_FIT }, { ORDATA (OBUF, baci_obuf, 16), REG_FIT }, { ORDATA (STATUS, baci_status, 16), REG_FIT }, { ORDATA (EDSIW, baci_edsiw, 16), REG_FIT }, { ORDATA (DSRW, baci_dsrw, 16), REG_FIT }, { ORDATA (CFCW, baci_cfcw, 16), REG_FIT }, { ORDATA (ICW, baci_icw, 16), REG_FIT }, { ORDATA (ISRW, baci_isrw, 16), REG_FIT }, { DRDATA (FIFOPUT, baci_fput, 8) }, { DRDATA (FIFOGET, baci_fget, 8) }, { DRDATA (FIFOCNTR, baci_fcount, 8) }, { DRDATA (BRKCNTR, baci_bcount, 16) }, { BRDATA (FIFO, baci_fifo, 8, 8, FIFO_SIZE) }, { BRDATA (SPCHAR, baci_spchar, 8, 1, 256) }, { ORDATA (UARTTHR, baci_uart_thr, 16), REG_FIT }, { ORDATA (UARTTR, baci_uart_tr, 16), REG_NZ }, { ORDATA (UARTRHR, baci_uart_rhr, 16), REG_FIT }, { ORDATA (UARTRR, baci_uart_rr, 16), REG_NZ }, { DRDATA (UARTCLK, baci_uart_clk, 16) }, { DRDATA (CTIME, baci_term.wait, 19), REG_RO }, { FLDATA (ENQFLAG, baci_enq_seen, 0), REG_HRO }, { DRDATA (ENQCNTR, baci_enq_cntr, 16), REG_HRO }, { FLDATA (LKO, baci_lockout, 0) }, { FLDATA (CTL, baci_control, 0) }, { FLDATA (FLG, baci_flag, 0) }, { FLDATA (FBF, baci_flagbuf, 0) }, { FLDATA (SRQ, baci_srq, 0) }, { ORDATA (DEVNO, baci_dib.devno, 6), REG_HRO }, { NULL } }; MTAB baci_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL, NULL, NULL }, { UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL }, { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, &baci_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc }, { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev }, { 0 } }; DEBTAB baci_deb[] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "BUF", DEB_BUF }, { "XFER", DEB_XFER }, { NULL, 0 } }; DEVICE baci_dev = { "BACI", /* device name */ baci_unit, /* unit array */ baci_reg, /* register array */ baci_mod, /* modifier array */ 2, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ &tmxr_ex, /* examine routine */ &tmxr_dep, /* deposit routine */ &baci_reset, /* reset routine */ NULL, /* boot routine */ &baci_attach, /* attach routine */ &baci_detach, /* detach routine */ &baci_dib, /* device information block */ DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ 0, /* debug control flags */ baci_deb, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* I/O signal handler. The BACI processes seven types of output words and supplies two types of input words. Output word type is identified by an ID code in bits 14-12. Input word type is determined by the state of the control flip-flop. The card has the usual control, flag buffer, flag, and SRQ flip-flops. However, they have the following unusual characteristics: - STC is not required to transfer a character. - Flag is not set after character transfer completes. - FLAG and SRQ are decoupled and are set independently. An interrupt lockout flip-flop is used to prevent the generation of multiple interrupts until the cause of the first interrupt is identified and cleared by the CPU. Implementation notes: 1. The STC handler checks to see if it was invoked for STC SC or STC SC,C. In the latter case, the check for new interrupt requests is deferred until after the CLF. Otherwise, the flag set by the interrupt check would be cleared, and the interrupt would be lost. */ uint32 baci_io (uint32 select_code, IOSIG signal, uint32 data) { const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); const IOSIG base_signal = IOBASE (signal); /* derive base signal */ uint8 ch; uint32 mask; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ baci_flag = baci_flagbuf = CLEAR; /* clear flag and flag buffer */ baci_srq = CLEAR; /* clear SRQ */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: [CLF] Flag and SRQ cleared\n", sim_deb); update_status (); /* FLG might set when SRQ clears */ break; case ioSTF: /* set flag flip-flop */ baci_flag = baci_flagbuf = SET; /* set flag and flag buffer */ baci_lockout = SET; /* set lockout */ baci_srq = SET; /* set SRQ */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: [STF] Flag, SRQ, and lockout set\n", sim_deb); break; case ioENF: /* enable flag */ baci_flag = baci_flagbuf = SET; /* set device flag and flag buffer */ baci_lockout = SET; /* set lockout */ break; case ioSFC: /* skip if flag is clear */ setstdSKF (baci); break; case ioSFS: /* skip if flag is set */ setstdSKF (baci); break; case ioIOI: /* I/O data input */ if (baci_control) { /* control set? */ baci_ibuf = TO_CHARCNT (baci_fcount); /* get FIFO count */ if (IO_MODE == RECV) /* receiving? */ baci_ibuf = baci_ibuf | fifo_get (); /* add char and validity flag */ data = baci_ibuf; /* return received data */ if (DEBUG_PRI (baci_dev, DEB_CPU)) fprintf (sim_deb, ">>BACI cpu: [LIx%s] Received data = %06o\n", hold_or_clear, data); } else { /* control clear? */ data = baci_status; /* return status */ if (DEBUG_PRI (baci_dev, DEB_CPU)) fprintf (sim_deb, ">>BACI cpu: [LIx%s] Status = %06o\n", hold_or_clear, data); } break; case ioIOO: /* I/O data output */ if (DEBUG_PRI (baci_dev, DEB_CPU)) fprintf (sim_deb, ">>BACI cpu: [OTx%s] Command = %06o\n", hold_or_clear, data); baci_obuf = data; if (baci_obuf & OUT_MR) { /* master reset? */ master_reset (); /* do before processing */ baci_io (select_code, ioSIR, 0); /* set interrupt request */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ">>BACI cmds: [OTx%s] Master reset\n", hold_or_clear); } switch (GET_ID (baci_obuf)) { /* isolate ID code */ case 0: /* transmit data */ if (IO_MODE == XMIT) { /* transmitting? */ ch = baci_obuf & OUT_DATA; /* mask to character */ fifo_put (ch); /* queue character */ if (baci_term.flags & UNIT_ATT) { /* attached to network? */ if (DEBUG_PRI (baci_dev, DEB_CMDS) && /* debugging? */ (sim_is_active (&baci_term) == 0)) /* service stopped? */ fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service scheduled, " "time = %d\n", hold_or_clear, baci_term.wait); if (baci_fcount == 1) /* first char to xmit? */ sim_activate_abs (&baci_term, /* start service with full char time */ baci_term.wait); else sim_activate (&baci_term, /* start service if not running */ baci_term.wait); } } break; case 1: /* enable device status interrupt */ baci_edsiw = baci_obuf; /* load new enable word */ update_status (); /* may have enabled an interrupt */ break; case 2: /* device status reference */ if ((baci_term.flags & UNIT_DIAG) && /* diagnostic mode? */ (baci_dsrw & OUT_DIAG) && /* and last DIAG was high? */ !(baci_obuf & OUT_DIAG) && /* and new DIAG is low? */ !(baci_icw & OUT_BAUDRATE)) /* and clock is external? */ clock_uart (); /* pulse UART clock */ baci_dsrw = baci_obuf; /* load new reference word */ update_status (); /* clocking UART may interrupt */ break; case 3: /* character frame control */ baci_cfcw = baci_obuf; /* load new frame word */ break; case 4: /* interface control */ if ((baci_icw ^ baci_obuf) & OUT_BAUDRATE) { /* baud rate change? */ baci_term.wait = service_time (baci_obuf); /* set service time to match rate */ if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ if (baci_obuf & OUT_BAUDRATE) { /* internal baud rate requested? */ sim_activate (&baci_term, /* activate I/O service */ baci_term.wait); if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service scheduled, " "time = %d\n", hold_or_clear, baci_term.wait); } else { /* external rate */ sim_cancel (&baci_term); /* stop I/O service */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service stopped\n", hold_or_clear); } } baci_icw = baci_obuf; /* load new reference word */ update_status (); /* loopback may change status */ break; case 5: /* interrupt status reset */ baci_isrw = baci_obuf; /* load new reset word */ mask = (baci_isrw & OUT_IRQCLR) << /* form reset mask */ IN_V_IRQCLR; /* for common irqs */ if (baci_isrw & OUT_CSC) /* add special char mask bit */ mask = mask | IN_SPCHAR; /* if requested */ baci_status = baci_status & ~mask; /* clear specified status bits */ break; case 6: /* special character */ baci_spchar [baci_obuf & OUT_SPCHAR] = /* set special character entry */ ((baci_obuf & OUT_SPFLAG) != 0); break; } break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ master_reset (); /* issue master reset */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: [CRS] Master reset\n", sim_deb); break; case ioCLC: /* clear control flip-flop */ baci_control = CLEAR; /* clear control */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ">>BACI cmds: [CLC%s] Control cleared\n", hold_or_clear); break; case ioSTC: /* set control flip-flop */ baci_control = SET; /* set control */ baci_lockout = CLEAR; /* clear lockout */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ">>BACI cmds: [STC%s] Control set and lockout cleared\n", hold_or_clear); if (signal == ioSTC) /* STC without ,C ? */ update_status (); /* clearing lockout might interrupt */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, baci); /* set standard PRL signal */ setstdIRQ (select_code, baci); /* set standard IRQ signal */ setSRQ (select_code, baci_srq); /* set SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ baci_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ baci_io (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ baci_io (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* BACI terminal service. The terminal service routine is used to transmit and receive characters. In terminal mode, it is started when a character is ready for output or when the Telnet poll routine determines that there are characters ready for input and stopped when there are no more characters to output or input. When the terminal is quiescent, this routine does not run. In diagnostic mode, it is started whenever an internal baud rate is set and stopped when the external clock is requested. In this mode, the routine will be called without an attached socket, so character I/O will be skipped. Because there is only one FIFO, the card is half-duplex and must be configured for transmit or receive mode. The UART, though, is double- buffered, so it may transmit and receive simultaneously. We implement both the UART shift and holding registers for each mode. If a character is received by the UART while the card is in transmit mode, it will remain in the receiver holding register (RHR). When the mode is reversed, the RHR contents will be unloaded into the FIFO. Conversely, transmit mode enables the output of the FIFO to be unloaded into the transmitter holding register (THR). Characters received or transmitted pass through the receiver register (RR) or transmitter register (TR), respectively. They are not strictly necessary in terminal (Telnet) transactions but are critical to diagnostic operations. The UART signals an overrun if a complete character is received while the RHR still contains the previous character. The BACI does not use this signal, though; an overrun is only indicated if the FIFO is full, and another character is received. In "fast timing" mode, we defer the recognition of a received character until the card is put into receive mode for the second or third consecutive ENQ/ACK handshake. This improves RTE break-mode recognition. "Realistic timing" mode behaves as the hardware does: a character present in the RHR is unloaded into the FIFO as soon as receive mode is set. Fast timing mode also enables internal ENQ/ACK handshaking. We allow one character time for the RTE driver to turn the card around, as otherwise the ACK may not be seen by the driver. Also, the local ACK is supplied after any received characters, as the driver detects operator attention only when the first character after an ENQ is not an ACK. Finally, fast timing enables buffer combining. For output, all characters present in the FIFO are unloaded into the Telnet buffer before initiating a packet send. For input, all characters present in the Telnet buffer are loaded into the FIFO. This reduces network traffic and decreases simulator overhead (there is only one service routine entry per block, rather than one per character). In fast output mode, it is imperative that not less than 1500 instructions elapse between the first character load to the FIFO and the initiation of transmission. The RTE driver must have enough time to output the maximum number of contiguous characters (33) and reset the interrupt status flags before the service routine is entered. Because all of the characters are transmitted as a block, the FIFO empty flag will be set by the service routine. If the driver has not yet exited at that time, the buffer-empty interrupt will be cleared when the interrupt status reset is done. The symptom will be a 3.8-second pause in output until the driver times out. To avoid this, the OTx output character handler does an absolute schedule for the first character to ensure that a full character time is used. */ t_stat baci_term_svc (UNIT *uptr) { uint32 data_bits, data_mask; const t_bool fast_timing = (baci_term.flags & UNIT_FASTTIME) != 0; const t_bool is_attached = (baci_term.flags & UNIT_ATT) != 0; t_stat status = SCPE_OK; t_bool xmit_loop = TRUE; t_bool recv_loop = TRUE; /* Transmission */ while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UART? */ data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ baci_uart_tr = baci_uart_thr & data_mask; /* mask data into transmitter register */ if ((baci_uart_tr == ENQ) && fast_timing) { /* char is ENQ and fast timing? */ baci_enq_seen = TRUE; /* set flag instead of transmitting */ baci_enq_cntr = baci_enq_cntr + 1; /* bump ENQ counter */ recv_loop = FALSE; /* skip recv to allow time before ACK */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: Character ENQ absorbed internally, " "ENQ count = %d\n", baci_enq_cntr); } else { /* character is not an ENQ */ baci_enq_cntr = 0; /* reset ENQ counter */ if (is_attached) { /* attached to network? */ status = tmxr_putc_ln (&baci_ldsc, /* transmit the character */ baci_uart_tr); if ((status == SCPE_OK) && /* transmitted OK? */ DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: Character %s " "transmitted from UART\n", fmt_char (baci_uart_tr)); } } if (status == SCPE_OK) { /* transmitted OK? */ baci_uart_tr = CLEAR_R; /* clear transmitter register */ if (IO_MODE == XMIT) { /* transmit mode? */ baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ baci_uart_thr = fifo_get (); /* get next char into UART */ update_status (); /* update FIFO status */ } else /* receive mode */ baci_uart_thr = CLEAR_HR; /* clear holding register */ xmit_loop = fast_timing && !baci_enq_seen; /* loop if fast mode and char not ENQ */ } else xmit_loop = FALSE; } /* Deferred reception */ if (recv_loop && /* ok to process? */ baci_uart_rhr && (IO_MODE == RECV) && /* and deferred char in RHR in recv mode? */ (!baci_enq_seen || (baci_enq_cntr >= 2))) { /* and either no ENQ or at least 2nd ENQ? */ baci_uart_rhr = baci_uart_rhr & ~IN_VALID; /* clear valid bit */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: Deferred character %s processed\n", fmt_char ((uint8) baci_uart_rhr)); fifo_put ((uint8) baci_uart_rhr); /* move deferred character to FIFO */ baci_uart_rhr = CLEAR_HR; /* clear RHR */ update_status (); /* update FIFO status */ } /* Reception */ while (recv_loop && /* OK to process? */ (baci_uart_rr = tmxr_getc_ln (&baci_ldsc))) { /* and new character available? */ if (baci_uart_rr & SCPE_BREAK) { /* break detected? */ baci_status = baci_status | IN_BREAK; /* set break status */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fputs (">>BACI xfer: Break detected\n", sim_deb); } data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ baci_uart_rhr = baci_uart_rr & data_mask; /* mask data into holding register */ baci_uart_rr = CLEAR_R; /* clear receiver register */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: Character %s received by UART\n", fmt_char ((uint8) baci_uart_rhr)); if (baci_term.flags & UNIT_CAPSLOCK) /* caps lock mode? */ baci_uart_rhr = toupper (baci_uart_rhr); /* convert to upper case if lower */ if (baci_cfcw & OUT_ECHO) /* echo wanted? */ tmxr_putc_ln (&baci_ldsc, baci_uart_rhr); /* send it back */ if ((IO_MODE == RECV) && !baci_enq_seen) { /* receive mode and not ENQ/ACK? */ fifo_put ((uint8) baci_uart_rhr); /* put data in FIFO */ baci_uart_rhr = CLEAR_HR; /* clear RHR */ update_status (); /* update FIFO status (may set flag) */ recv_loop = fast_timing && !IRQ (baci_dib.devno); /* loop if fast mode and no IRQ */ } else { /* xmit or ENQ/ACK, leave char in RHR */ baci_uart_rhr = baci_uart_rhr | IN_VALID; /* set character valid bit */ recv_loop = FALSE; /* terminate loop */ } } /* Housekeeping */ if (recv_loop && baci_enq_seen) { /* OK to process and ENQ seen? */ baci_enq_seen = FALSE; /* reset flag */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fputs (">>BACI xfer: Character ACK generated internally\n", sim_deb); fifo_put (ACK); /* fake ACK from terminal */ update_status (); /* update FIFO status */ } if (is_attached) /* attached to network? */ tmxr_poll_tx (&baci_desc); /* output any accumulated chars */ if ((baci_uart_thr & IN_VALID) || baci_enq_seen || /* more to transmit? */ tmxr_rqln (&baci_ldsc)) /* or more to receive? */ sim_activate (uptr, uptr->wait); /* reschedule service */ else if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: Terminal service stopped\n", sim_deb); return status; } /* BACI Telnet poll service. This service routine is used to poll for Telnet connections and incoming characters. If characters are available, the terminal I/O service routine is scheduled. It starts when the socket is attached and stops when the socket is detached. */ t_stat baci_poll_svc (UNIT *uptr) { if (baci_term.flags & UNIT_ATT) { /* attached to network? */ if (tmxr_poll_conn (&baci_desc) >= 0) /* new connection established? */ baci_ldsc.rcve = 1; /* enable line to receive */ tmxr_poll_rx (&baci_desc); /* poll for input */ if (tmxr_rqln (&baci_ldsc)) /* chars available? */ sim_activate (&baci_term, baci_term.wait); /* activate I/O service */ } if (uptr->wait == POLL_FIRST) /* first poll? */ uptr->wait = sync_poll (INITIAL); /* initial synchronization */ else /* not first */ uptr->wait = sync_poll (SERVICE); /* continue synchronization */ sim_activate (uptr, uptr->wait); /* continue polling */ return SCPE_OK; } /* Simulator reset routine */ t_stat baci_reset (DEVICE *dptr) { baci_io (baci_dib.devno, ioPOPIO, 0); /* send POPIO signal */ baci_ibuf = 0; /* clear input buffer */ baci_obuf = 0; /* clear output buffer */ baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */ baci_enq_seen = FALSE; /* reset ENQ seen flag */ baci_enq_cntr = 0; /* clear ENQ counter */ baci_term.wait = service_time (baci_icw); /* set terminal I/O time */ if (baci_term.flags & UNIT_ATT) { /* device attached? */ baci_poll.wait = POLL_FIRST; /* set up poll */ sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */ } else sim_cancel (&baci_poll); /* else stop Telnet poll */ return SCPE_OK; } /* Attach controller */ t_stat baci_attach (UNIT *uptr, char *cptr) { t_stat status = SCPE_OK; status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */ if (status == SCPE_OK) { baci_poll.wait = POLL_FIRST; /* set up poll */ sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */ } return status; } /* Detach controller */ t_stat baci_detach (UNIT *uptr) { t_stat status; status = tmxr_detach (&baci_desc, uptr); /* detach socket */ baci_ldsc.rcve = 0; /* disable line reception */ sim_cancel (&baci_poll); /* stop Telnet poll */ return status; } /* Local routines */ /* Master reset. This is the programmed card master reset, not the simulator reset routine. Master reset normally clears the UART registers. However, if we are in "fast timing" mode, the receiver holding register may hold a deferred character. In this case, we do not clear the RHR, unless we are called from the simulator reset routine. The HP BACI manual states that master reset "Clears Service Request (SRQ)." An examination of the schematic, though, shows that it sets SRQ instead. */ static void master_reset (void) { baci_fput = baci_fget = 0; /* clear FIFO indexes */ baci_fcount = 0; /* clear FIFO counter */ memset (baci_fifo, 0, sizeof (baci_fifo)); /* clear FIFO data */ baci_uart_thr = CLEAR_HR; /* clear transmitter holding register */ if (!(baci_term.flags & UNIT_FASTTIME)) /* real time mode? */ baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */ baci_uart_tr = CLEAR_R; /* clear transmitter register */ baci_uart_rr = CLEAR_R; /* clear receiver register */ baci_uart_clk = 0; /* clear UART clock */ baci_bcount = 0; /* clear break counter */ baci_control = CLEAR; /* clear control */ baci_flag = baci_flagbuf = SET; /* set flag and flag buffer */ baci_srq = SET; /* set SRQ */ baci_lockout = SET; /* set lockout flip-flop */ baci_edsiw = 0; /* clear interrupt enables */ baci_dsrw = 0; /* clear status reference */ baci_cfcw = baci_cfcw & ~OUT_ECHO; /* clear echo flag */ baci_icw = baci_icw & OUT_BAUDRATE; /* clear interface control */ if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ baci_status = baci_status & ~IN_MODEM | IN_SPARE; /* clear loopback status, set BA */ return; } /* Update status. In diagnostic mode, several of the modem output lines are looped back to the input lines. Also, CD is tied to BB (received data), which is presented on the TEST status bit via an inversion. Echo mode couples BB to BA (transmitted data), which is presented on the SPARE status bit. If a modem line interrupt condition is present and enabled, the DEVINT status bit is set. Other potential "standard" interrupt sources are the special character, break detected, and overrun/parity error bits. If DCPC transfers are not selected, then the FIFO interrupts (buffer empty, half-full, and full) and the "data ready" condition (i.e., receive and character modes enabled and FIFO not empty) also produces an interrupt request. An interrupt request will set the card flag unless either the lockout or SRQ flip-flops are set. SRQ will set if DCPC mode is enabled and there is room (transmit mode) or data (receive mode) in the FIFO. */ static void update_status (void) { if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */ baci_status = baci_status & ~IN_DIAG; /* clear loopback flags */ if (baci_icw & OUT_SXX) /* SCA to SCF and CF */ baci_status = baci_status | IN_SXX | IN_CF; if ((baci_icw & OUT_CA) && (baci_fcount < 128)) /* CA to CC and CE */ baci_status = baci_status | IN_CC | IN_CE; if (baci_icw & OUT_CD) /* CD to CB */ baci_status = baci_status | IN_CB; else { baci_status = baci_status | IN_TEST; /* BB is inversion of CD */ if (baci_cfcw & OUT_ECHO) baci_status = baci_status | IN_SPARE; /* BB couples to BA with echo */ } if (!(baci_cfcw & OUT_ECHO) && (baci_uart_tr & 1)) /* no echo and UART TR set? */ baci_status = baci_status | IN_SPARE; /* BA to SPARE */ } if (baci_edsiw & (baci_status ^ baci_dsrw) & IN_MODEM) /* device interrupt? */ baci_status = baci_status | IN_DEVINT; /* set flag */ if ((baci_status & IN_STDIRQ) || /* standard interrupt? */ !(baci_icw & OUT_DCPC) && /* or under program control */ (baci_status & IN_FIFOIRQ) || /* and FIFO interrupt? */ (IO_MODE == RECV) && /* or receiving */ (baci_edsiw & OUT_ENCM) && /* and char mode */ (baci_fget != baci_fput)) { /* and FIFO not empty? */ if (baci_lockout) { /* interrupt lockout? */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: Lockout prevents flag set", sim_deb); } else if (baci_srq) { /* SRQ set? */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: SRQ prevents flag set", sim_deb); } else { baci_io (baci_dib.devno, ioENF, 0); /* set flag */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: Flag and lockout set", sim_deb); } if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ", status = %06o\n", baci_status); } if ((baci_icw & OUT_DCPC) && /* DCPC enabled? */ ((IO_MODE == XMIT) && (baci_fcount < 128) || /* and xmit and room in FIFO */ (IO_MODE == RECV) && (baci_fcount > 0))) { /* or recv and data in FIFO? */ if (baci_lockout) { /* interrupt lockout? */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: Lockout prevents SRQ set", sim_deb); } else { baci_srq = SET; /* set SRQ */ baci_io (baci_dib.devno, ioSIR, 0); /* set interrupt request */ if (DEBUG_PRI (baci_dev, DEB_CMDS)) fputs (">>BACI cmds: SRQ set", sim_deb); } if (DEBUG_PRI (baci_dev, DEB_CMDS)) fprintf (sim_deb, ", status = %06o\n", baci_status); } return; } /* Calculate service time from baud rate. Service times are based on 1580 instructions per second, which is the 1000 E-Series execution speed. The "external clock" rate uses the 9600 baud rate, as most real terminals were set to their maximum rate. Note that the RTE driver has a race condition that will trip if the service time is less than 1500 instructions. Therefore, these times cannot be shortened arbitrarily. */ static int32 service_time (uint32 control_word) { static const int32 ticks [] = { 1646, 316000, 210667, 143636, 117472, 105333, 52667, 26333, 17556, 13667, 8778, 6583, 4389, 3292, 2194, 1646 }; return ticks [GET_BAUDRATE (control_word)]; /* return service time for indicated rate */ } /* FIFO manipulation routines. The BACI is a half-duplex device that has a single 128-byte FIFO that is used for both transmitting and receiving. Whether the FIFO is connected to the input or output of the UART is determined by the XMIT bit in word 4. A separate 8-bit FIFO up/down counter is used to track the number of bytes available. FIFO operations are complicated slightly by the UART, which is double-buffered. The FIFO is modeled as a circular 128-byte array. Separate get and put indexes track the current data extent. A FIFO character counter is used to derive empty, half-full, and full status indications, and counts greater than 128 are possible. In the transmit mode, an OTA/B with word type 0 generates SI (shift in) to load the FIFO and increment the FIFO counter. When the UART is ready for a character, THRE (UART transmitter holding register empty) and OR (FIFO output ready) generate THRL (transmitter holding register load) and SO (FIFO shift out) to unload the FIFO into the UART. When transmission of the character over the serial line is complete, TRE (UART transmitter register empty) decrements the FIFO counter. In the receive mode, the UART sets DR (data received) when has obtained a character, which generates SI (FIFO shift in) to load the FIFO and increment the FIFO counter. This also clocks PE (UART parity error) and IR (FIFO input ready) into the overrun/parity error flip-flop. An LIA/B with control set and with OR (FIFO output ready) set, indicating valid data is available, generates SO (FIFO shift out) to unload the FIFO and decrement the FIFO counter. Presuming an empty FIFO and UART, double-buffering in the transmit mode means that the first byte deposited into the FIFO is removed and loaded into the UART transmitter holding register. Even though the FIFO is actually empty, the FIFO counter remains at 1, because FIFO decrement does not occur until the UART actually transmits the data byte. The intended mode of operation is to wait until the buffer-empty interrupt occurs, which will happen when the final character is transmitted from the UART, before switching the BACI into receive mode. The counter will match the FIFO contents properly, i.e., will be zero, when the UART transmission completes. However, during diagnostic operation, FIFO testing will take this "extra" count into consideration. For example, after a master reset, if ten bytes are written to the FIFO in transmit mode, the first byte will pass through to the UART transmitter holding register, and the next nine bytes will fill the FIFO. The first byte read in receive mode will be byte 2, not byte 1; the latter remains in the UART. After the ninth byte is read, OR (FIFO output ready) will drop, resetting the valid data flip-flop and inhibiting any further FIFO counter decrement pulses. The counter will remain at 1 until another master reset is done. The same situation occurs in the RTE driver during ENQ/ACK handshakes. The driver sets the card to transmit mode, sends an ENQ, waits for a short time for the character to "bubble through" the FIFO and into the UART transmitter holding register, and then switches the card to receive mode to await the interrupt from the reception of the ACK. This is done to avoid the overhead of the interrupt after the ENQ is transmitted. However, switching the card into receive mode before the ENQ is actually transmitted means that the FIFO counter will not decrement when that occurs, leaving the counter in an "off by one" configuration. To remedy this, the driver does a master reset after the ACK is received. Therefore, for proper operation, we must simulate both the UART double-buffering and the decoupling of the FIFO and FIFO character counter. */ /* Get a character from the FIFO. In receive mode, getting a character from the FIFO decrements the character counter concurrently. In transmit mode, the counter must not be decremented until the character is actually sent; in this latter case, the caller is responsible for decrementing. Attempting to get a character when the FIFO is empty returns the last valid data and does not alter the FIFO indexes. Because the FIFO counter may indicate more characters than are actually in the FIFO, the count is not an accurate indicator of FIFO fill status. We account for this by examining the get and put indexes. If these are equal, then the FIFO is either empty or exactly full. We differentiate by examining the FIFO counter and seeing if it is >= 128, indicating an (over)full condition. If it is < 128, then the FIFO is empty, even if the counter is not 0. */ static uint32 fifo_get (void) { uint32 data; data = baci_fifo [baci_fget]; /* get character */ if ((baci_fget != baci_fput) || (baci_fcount >= 128)) { /* FIFO occupied? */ if (IO_MODE == RECV) /* receive mode? */ baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ if (DEBUG_PRI (baci_dev, DEB_BUF)) fprintf (sim_deb, ">>BACI buf: Character %s get from FIFO [%d], " "character counter = %d\n", fmt_char (data), baci_fget, baci_fcount); baci_fget = (baci_fget + 1) % FIFO_SIZE; /* bump index modulo array size */ if (baci_spchar [data]) /* is it a special character? */ data = data | IN_SPFLAG; /* set flag */ data = data | IN_VALID; /* set valid flag in return */ } else /* FIFO empty */ if (DEBUG_PRI (baci_dev, DEB_BUF)) fprintf (sim_deb, ">>BACI buf: Attempted get on empty FIFO, " "character count = %d\n", baci_fcount); if (baci_fcount == 0) /* count now zero? */ baci_status = baci_status | IN_BUFEMPTY; /* set buffer empty flag */ update_status (); /* update FIFO status */ return data; /* return character */ } /* Put a character into the FIFO. In transmit mode, available characters are unloaded from the FIFO into the UART transmitter holding register as soon as the THR is empty. That is, given an empty FIFO and THR, a stored character will pass through the FIFO and into the THR immediately. Otherwise, the character will remain in the FIFO. In either case, the FIFO character counter is incremented. In receive mode, characters are only unloaded from the FIFO explicitly, so stores always load the FIFO and increment the counter. */ static void fifo_put (uint8 ch) { uint32 index = 0; t_bool pass_thru; pass_thru = (IO_MODE == XMIT) && /* pass thru if XMIT and THR empty */ !(baci_uart_thr & IN_VALID); if (pass_thru) /* pass char thru to UART */ baci_uart_thr = ch | IN_VALID; /* and set valid character flag */ else { /* RECV or THR occupied */ index = baci_fput; /* save current index */ baci_fifo [baci_fput] = ch; /* put char in FIFO */ baci_fput = (baci_fput + 1) % FIFO_SIZE; /* bump index modulo array size */ } baci_fcount = baci_fcount + 1; /* increment occupancy counter */ if (DEBUG_PRI (baci_dev, DEB_BUF)) if (pass_thru) fprintf (sim_deb, ">>BACI buf: Character %s put to UART transmitter holding register, " "character counter = 1\n", fmt_char (ch)); else fprintf (sim_deb, ">>BACI buf: Character %s put to FIFO [%d], " "character counter = %d\n", fmt_char (ch), index, baci_fcount); if ((IO_MODE == RECV) && (baci_spchar [ch])) /* receive mode and special character? */ baci_status = baci_status | IN_SPCHAR; /* set special char seen flag */ if (baci_fcount == 64) /* FIFO half full? */ baci_status = baci_status | IN_BUFHALF; else if (baci_fcount == 128) /* FIFO completely full? */ baci_status = baci_status | IN_BUFFULL; else if (baci_fcount > 128) /* FIFO overrun? */ baci_status = baci_status | IN_OVRUNPE; update_status (); /* update FIFO status */ return; } /* Clock the UART. In the diagnostic mode, the DIAG output is connected to the EXT CLK input. If the baud rate of the Interface Control Word is set to "external clock," then raising and lowering the DIAG output will pulse the UART transmitter and receiver clock lines, initiating transmission or reception of serial data. Sixteen pulses are needed to shift one bit through the UART. The diagnostic hood ties CD to BB (received data), so bits presented to CD via the Interface Control Word can be clocked into the UART receiver register (RR). Similarly, the UART transmitter register (TR) shifts data onto BA (transmitted data), and the hood ties BA to SPARE, so transmitted bits are presented to the SPARE bit in the status word. "baci_uart_clk" contains the number of clock pulses remaining for the current character transfer. Calling this routine with "baci_uart_clk" = 0 initiates a transfer. The value will be a multiple of 16 and will account for the start bit, the data bits, the optional parity bit, and the stop bits. The transfer terminates when the count reaches zero (or eight, if 1.5 stop bits is selected during transmission). Every sixteen pulses when the lower four bits of the clock count are zero, the transmitter or receiver register will be shifted to present or receive a new serial bit. The registers are initialized to all ones for proper handling of the stop bits. A break counter is maintained and incremented whenever a space (0) condition is seen on the serial line. After 160 clock times (10 bits) of continuous zero data, the "break seen" status is set. This routine is not used in terminal mode. */ static void clock_uart (void) { uint32 uart_bits, data_bits, data_mask, parity, bit_low, i; if (baci_uart_clk > 0) { /* transfer in progress? */ bit_low = (baci_icw & OUT_CD); /* get current receive bit */ if ((baci_uart_clk & 017) == 0) /* end of a bit? */ if (IO_MODE == XMIT) /* transmit? */ baci_uart_tr = baci_uart_tr >> 1; /* shift new bit onto line */ else /* receive? */ baci_uart_rr = (baci_uart_rr >> 1) & /* shift new bit in */ (bit_low ? ~SIGN : -1); /* (inverted sense) */ if (bit_low) { /* another low bit? */ baci_bcount = baci_bcount + 1; /* update break counter */ if (baci_bcount == 160) { /* break held long enough? */ baci_status = baci_status | IN_BREAK; /* set break flag */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fputs (">>BACI xfer: Break detected\n", sim_deb); } } else /* high bit? */ baci_bcount = 0; /* reset break counter */ baci_uart_clk = baci_uart_clk - 1; /* decrement clocks remaining */ if ((IO_MODE == XMIT) && /* transmit mode? */ ((baci_uart_clk == 0) || /* and end of character? */ (baci_uart_clk == 8) && /* or last stop bit */ (baci_cfcw & OUT_STBITS) && /* and extra stop bit requested */ ((baci_cfcw & OUT_CHARSIZE) == 0))) { /* and 1.5 stop bits used? */ baci_uart_clk = 0; /* clear clock count */ baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ baci_uart_thr = fifo_get (); /* get next char into THR */ update_status (); /* update FIFO status */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: UART transmitter empty, " "holding register = %06o\n", baci_uart_thr); } else if ((IO_MODE == RECV) && /* receive mode? */ (baci_uart_clk == 0)) { /* and end of character? */ data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ uart_bits = data_bits + /* calculate UART bits as data bits */ ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ ((baci_cfcw & OUT_STBITS) != 0); /* plus extra stop bit if used */ baci_uart_rhr = baci_uart_rr >> (16 - uart_bits); /* position data to right align */ baci_uart_rr = CLEAR_R; /* clear receiver register */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: UART receiver = %06o (%s)\n", baci_uart_rhr, fmt_char (baci_uart_rhr & data_mask)); fifo_put (baci_uart_rhr & data_mask); /* put data in FIFO */ update_status (); /* update FIFO status */ if (baci_cfcw & OUT_PARITY) { /* parity present? */ data_mask = data_mask << 1 | 1; /* widen mask to encompass parity */ uart_bits = baci_uart_rhr & data_mask; /* get data plus parity */ parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ for (i = 0; i < data_bits + 1; i++) { /* calc parity of data + parity bit */ parity = parity ^ uart_bits; /* parity calculated in LSB */ uart_bits = uart_bits >> 1; } if (parity & 1) { /* parity error? */ baci_status = baci_status | IN_OVRUNPE; /* report it */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fputs (">>BACI xfer: Parity error detected\n", sim_deb); } } } } if ((baci_uart_clk == 0) && /* start of transfer? */ ((IO_MODE == RECV) || /* and receive mode */ (baci_uart_thr & IN_VALID))) { /* or character ready to transmit? */ data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ uart_bits = data_bits + /* calculate UART bits as data bits */ ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ 2 + ((baci_cfcw & OUT_STBITS) != 0); /* plus start/stop and extra stop if used */ baci_uart_clk = 16 * uart_bits; /* calculate clocks pulses expected */ if (IO_MODE == XMIT) { /* transmit mode? */ data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ baci_uart_tr = baci_uart_thr & data_mask; /* mask data into holding register */ if (baci_cfcw & OUT_PARITY) { /* add parity to this transmission? */ uart_bits = baci_uart_tr; /* copy data bits */ parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ for (i = 0; i < data_bits; i++) { /* calculate parity of data */ parity = parity ^ uart_bits; /* parity calculated in LSB */ uart_bits = uart_bits >> 1; } data_mask = data_mask << 1 | 1; /* extend mask for the parity bit */ baci_uart_tr = baci_uart_tr | /* include parity in transmission register */ (parity & 1) << data_bits; /* (mask to parity bit and position it) */ } baci_uart_tr = (~data_mask | baci_uart_tr) << 2 | 1; /* form serial data stream */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: UART transmitter = %06o (%s), " "clock count = %d\n", baci_uart_tr & DMASK, fmt_char (baci_uart_thr & data_mask), baci_uart_clk); } else { baci_uart_rr = CLEAR_R; /* clear receiver register */ if (DEBUG_PRI (baci_dev, DEB_XFER)) fprintf (sim_deb, ">>BACI xfer: UART receiver empty, " "clock count = %d\n", baci_uart_clk); } } return; } simh-3.8.1/HP2100/hp2100_pif.c0000644000175000017500000003657411107411526013404 0ustar vlmvlm/* hp2100_pif.c: HP 12620A/12936A privileged interrupt fence simulator Copyright (c) 2008, J. David Bryan 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 AUTHOR 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 author. PIF 12620A/12936A privileged interrupt fence 26-Jun-08 JDB Rewrote device I/O to model backplane signals 18-Jun-08 JDB Created PIF device References: - 12620A Breadboard Interface Kit Operating and Service Manual (12620-90001, May-1978) - 12936A Privileged Interrupt Fence Accessory Installation and Service Manual (12936-90001, Mar-1974) The Privileged Interupt Fence (PIF) was used in DOS and RTE systems to provide privileged interrupt capability. In non-privileged systems, DOS and RTE vectored all interrupts though the Central Interrupt Control (CIC) routine. Within CIC, the interrupt system was turned off, the interrupt was categorized, the associated driver was identified and mapped into logical memory (if necessary), and the driver entered to handle the device service. When the driver exited, the interrupt system was turned on before returning to the point of interruption in the user's program. In addition, the DOS and RTE operating systems themselves executed with the interrupt system off, as they were not reentrant. This process proved too lengthy for certain devices, which would lose interrupts or be forced to limit I/O speeds as a result. To allow faster service, a driver could be written as a "privileged" driver and generated into a privileged system. A privileged system operated with the interrupt system on when handling unprivileged device interrupts or executing within the operating system. The PIF card was installed in the I/O backplane to separate privileged from unprivileged devices by controlling the interrupt priority chain signal (PRL) to lower-priority devices. The privileged cards located below the fence were allowed to interrupt the service routines of the unprivileged cards that were located above the fence. When an unprivileged device interrupted, CIC would be entered as usual, and the interrupt system would be turned off. However, after the system state was saved, the PIF would be configured to break the priority chain (deny PRL), so that subsequent interrupts from all unprivileged devices would be deferred. Then the interrupt system would be turned on before normal CIC processing continued. Interrupts from additional unprivileged devices would be held off by the PIF until the driver completed and CIC returned, just as in a non-privileged system. However, if a privileged device interrupted, the interrupt would be allowed, because the interrupt system was on, and the priority chain was intact for the devices below the fence. A privileged device bypassed CIC and entered the associated device driver directly, and this would occur even if an unprivileged device driver or the operating system itself were executing. This provided very fast interrupt service time. HP produced two PIF cards: the 12936A Privileged Interrupt Fence Accessory for DOS, and the 12620A Breadboard Interface for RTE. They behaved quite differently and were not interchangeable. The 12620A had the standard control and flag circuitry. It behaved as most cards did; setting control and flag together lowered PRL and generated an interrupt. The control and flag flip-flops were set and cleared with STC/CLC and STF/CLF instructions. The SFS/SFC instructions could be used to test the flag state. The 12936A had a unique behavior. Setting either control or flag lowered PRL. An interrupt occurred when flag was set and control was clear. The control flip-flop was controlled with STC/CLC. The flag flip-flop was set with OTA/B and cleared with CLF. SFC and SFS were not implemented and never skipped. */ #include "hp2100_defs.h" /* Device flags */ #define DEV_V_12936 (DEV_V_UF + 0) /* 12936A card */ #define DEV_12936 (1 << DEV_V_12936) /* PIF state variables */ FLIP_FLOP pif_control = CLEAR; /* control flip-flop */ FLIP_FLOP pif_flag = CLEAR; /* flag flip-flop */ FLIP_FLOP pif_flagbuf = CLEAR; /* flag buffer flip-flop */ /* PIF global routines */ uint32 pif_io (uint32 select_code, IOSIG signal, uint32 data); t_stat pif_reset (DEVICE *dptr); t_stat pif_set_card (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, void *desc); /* PIF data structures. pif_dib PIF device information block pif_unit PIF unit list pif_reg PIF register list pif_mod PIF modifier list pif_deb PIF debug list pif_dev PIF device descriptor Implementation note: 1. The SIMH developer's manual says that a device's unit list may be NULL. However, if this is done, the register state cannot be examined or altered via SCP. To work around this problem, we define a dummy unit that is not used otherwise. */ DEVICE pif_dev; DIB pif_dib = { PIF, &pif_io }; UNIT pif_unit = { UDATA (NULL, 0, 0) /* dummy unit */ }; REG pif_reg [] = { { FLDATA (CTL, pif_control, 0) }, { FLDATA (FLG, pif_flag, 0) }, { FLDATA (FBF, pif_flagbuf, 0) }, { ORDATA (DEVNO, pif_dib.devno, 6), REG_HRO }, { NULL } }; MTAB pif_mod [] = { { MTAB_XTD | MTAB_VDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &pif_dev }, { 0 } }; DEVICE pif_dev = { "PIF", /* device name */ &pif_unit, /* unit array */ pif_reg, /* register array */ pif_mod, /* modifier array */ 1, /* number of units */ 10, /* address radix */ 31, /* address width */ 1, /* address increment */ 8, /* data radix */ 8, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &pif_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &pif_dib, /* device information block */ DEV_DEBUG | DEV_DISABLE, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* I/O signal handler. Operation of the 12620A and the 12936A is different. The I/O responses of the two cards are summarized below: Signal 12620A Action 12936A Action ------ -------------------- -------------------- POPIO Set FBF, FLG Clear FBF, FLG CRS Clear CTL Clear CTL CLC Clear CTL Clear CTL STC Set CTL Set CTL CLF Clear FBF, FLG Clear FBF, FLG STF Set FBF, FLG none SFC Skip if FLG clear none SFS Skip if FLG set none IOI none none IOO none Set FBF, FLG PRL ~(CTL * FLG) ~(CTL + FLG) IRQ CTL * FLG * FBF ~CTL * FLG * FBF IAK Clear FBF Clear FBF SRQ Follows FLG Not driven Note that PRL and IRQ are non-standard for the 12936A. */ uint32 pif_io (uint32 select_code, IOSIG signal, uint32 data) { const char *hold_or_clear = (signal > ioCLF ? ",C" : ""); const t_bool is_rte_pif = (pif_dev.flags & DEV_12936) == 0; const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ pif_flag = pif_flagbuf = CLEAR; /* clear flag buffer and flag */ if (DEBUG_PRS (pif_dev)) fputs (">>PIF: [CLF] Flag cleared\n", sim_deb); break; case ioSTF: /* set flag flip-flop */ if (is_rte_pif) { /* RTE PIF? */ pif_flag = pif_flagbuf = SET; /* set flag buffer and flag */ if (DEBUG_PRS (pif_dev)) fputs (">>PIF: [STF] Flag set\n", sim_deb); } break; case ioSFC: /* skip if flag is clear */ if (is_rte_pif) /* RTE PIF? */ setstdSKF (pif); /* card responds to SFC */ break; case ioSFS: /* skip if flag is set */ if (is_rte_pif) /* RTE PIF? */ setstdSKF (pif); /* card responds to SFS */ break; case ioIOO: /* I/O data output */ if (!is_rte_pif) { /* DOS PIF? */ pif_flag = pif_flagbuf = SET; /* set flag buffer and flag */ pif_io (select_code, ioSIR, 0); /* set IRQ (not normally done for IOO) */ if (DEBUG_PRS (pif_dev)) fprintf (sim_deb, ">>PIF: [OTx%s] Flag set\n", hold_or_clear); } break; case ioPOPIO: /* power-on preset to I/O */ pif_flag = pif_flagbuf = /* set or clear flag and flag buffer */ (is_rte_pif ? SET : CLEAR); if (DEBUG_PRS (pif_dev)) fprintf (sim_deb, ">>PIF: [POPIO] Flag %s\n", (is_rte_pif ? "set" : "cleared")); /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ pif_control = CLEAR; /* clear control */ if (DEBUG_PRS (pif_dev)) fprintf (sim_deb, ">>PIF: [%s%s] Control cleared\n", (signal == ioCRS ? "CRS" : "CLC"), hold_or_clear); break; case ioSTC: /* set control flip-flop */ pif_control = SET; /* set control */ if (DEBUG_PRS (pif_dev)) fprintf (sim_deb, ">>PIF: [STC%s] Control set\n", hold_or_clear); break; case ioSIR: /* set interrupt request */ if (is_rte_pif) { /* RTE PIF? */ setstdPRL (select_code, pif); /* set standard PRL signal */ setstdIRQ (select_code, pif); /* set standard IRQ signal */ setstdSRQ (select_code, pif); /* set standard SRQ signal */ } else { /* DOS PIF */ setPRL (select_code, !(pif_control | pif_flag)); setIRQ (select_code, !pif_control & pif_flag & pif_flagbuf); } if (DEBUG_PRS (pif_dev)) fprintf (sim_deb, ">>PIF: [SIR] PRL = %d, IRQ = %d\n", PRL (select_code), IRQ (select_code)); break; case ioIAK: /* interrupt acknowledge */ pif_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ pif_io (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ pif_io (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Simulator reset routine */ t_stat pif_reset (DEVICE *dptr) { pif_io (pif_dib.devno, ioPOPIO, 0); /* send POPIO signal */ return SCPE_OK; } /* Set card type. val == 0 --> set to 12936A (DOS PIF) val == 1 --> set to 12620A (RTE PIF) */ t_stat pif_set_card (UNIT *uptr, int32 val, char *cptr, void *desc) { if ((val < 0) || (val > 1) || (cptr != NULL)) /* sanity check */ return SCPE_ARG; /* bad argument */ if (val) /* DOS PIF selected? */ pif_dev.flags = pif_dev.flags | DEV_12936; /* set to 12936A */ else /* RTE PIF selected */ pif_dev.flags = pif_dev.flags & ~DEV_12936; /* set to 12620A */ return SCPE_OK; } /* Show card type */ t_stat pif_show_card (FILE *st, UNIT *uptr, int32 val, void *desc) { if (pif_dev.flags & DEV_12936) fputs ("12936A", st); else fputs ("12620A", st); return SCPE_OK; } simh-3.8.1/HP2100/hp2100_cpu2.c0000644000175000017500000015156711062235650013501 0ustar vlmvlm/* hp2100_cpu2.c: HP 2100/1000 FP/DMS/EIG/IOP instructions Copyright (c) 2005-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. CPU2 Floating-point, dynamic mapping, extended, and I/O processor instructions 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 05-Aug-08 JDB Updated mp_dms_jmp calling sequence Fixed DJP, SJP, and UJP jump target validation 30-Jul-08 JDB RVA/B conditionally updates dms_vr before returning value 19-Dec-06 JDB DMS self-test now executes as NOP on 1000-M 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions 22-Feb-05 JDB Fixed missing MPCK on JRS target 21-Jan-05 JDB Reorganized CPU option and operand processing flags Split code along microcode modules 15-Jan-05 RMS Cloned from hp2100_cpu.c Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #if !defined (HAVE_INT64) /* int64 support unavailable */ #include "hp2100_fp.h" /* Single-Precision Floating Point Instructions The 2100 and 1000 CPUs share the single-precision (two word) floating-point instruction codes. Floating-point firmware was an option on the 2100 and was standard on the 1000-M and E. The 1000-F had a standard hardware Floating Point Processor that executed these six instructions and added extended- and double-precision floating- point instructions, as well as double-integer instructions (the FPP is simulated separately). Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A 12901A std std N/A The instruction codes for the 2100 and 1000-M/E systems are mapped to routines as follows: Instr. 2100/1000-M/E Description ------ ------------- ----------------------------------- 105000 FAD Single real add 105020 FSB Single real subtract 105040 FMP Single real multiply 105060 FDV Single real divide 105100 FIX Single integer to single real fix 105120 FLT Single real to single integer float Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be executed by any instruction in the range 105000-105017. Implementation note: rather than have two simulators that each executes the single-precision FP instruction set, we compile conditionally, based on the availability of 64-bit integer support in the host compiler. 64-bit integers are required for the FPP, so if they are available, then the FPP is used to handle the six single-precision instructions for the 2100 and M/E-Series, and this function is omitted. If support is unavailable, this function is used instead. Implementation note: the operands to FAD, etc. are floating-point values, so OP_F would normally be used. However, the firmware FP support routines want floating-point operands as 32-bit integer values, so OP_D is used to achieve this. */ static const OP_PAT op_fp[8] = { OP_D, OP_D, OP_D, OP_D, /* FAD FSB FMP FDV */ OP_N, OP_N, OP_N, OP_N /* FIX FLT --- --- */ }; t_stat cpu_fp (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry; entry = (IR >> 4) & 017; /* mask to entry point */ if (op_fp[entry] != OP_N) if (reason = cpu_ops (op_fp[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<7:4> */ case 000: /* FAD 105000 (OP_D) */ O = f_as (op[0].dword, 0); /* add, upd ovflo */ break; case 001: /* FSB 105020 (OP_D) */ O = f_as (op[0].dword, 1); /* sub, upd ovflo */ break; case 002: /* FMP 105040 (OP_D) */ O = f_mul (op[0].dword); /* mul, upd ovflo */ break; case 003: /* FDV 105060 (OP_D) */ O = f_div (op[0].dword); /* div, upd ovflo */ break; case 004: /* FIX 105100 (OP_N) */ O = f_fix (); /* fix, upd ovflo */ break; case 005: /* FLT 105120 (OP_N) */ O = f_flt (); /* float, upd ovflo */ break; default: /* should be impossible */ return SCPE_IERR; } return reason; } #endif /* int64 support unavailable */ /* Dynamic Mapping System The 1000 Dynamic Mapping System (DMS) consisted of the 12731A Memory Expansion Module (MEM) card and 38 instructions to expand the basic 32K logical address space to a 1024K physical space. The MEM provided four maps of 32 mapping registers each: a system map, a user map, and two DCPC maps. DMS worked in conjunction with memory protect to provide a "protected mode" in which memory read and write violations could be trapped, and that inhibited "privileged" instruction execution that attempted to alter the memory mapping. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A 12976B 13307B std The instruction codes are mapped to routines as follows: Instr. 1000-M 1000-E/F Instr. 1000-M 1000-E/F ------ ------ -------- ------ ------ -------- 10x700 [xmm] [xmm] 10x720 XMM XMM 10x701 [nop] [test] 10x721 XMS XMS 10x702 MBI MBI 10x722 XM* XM* 10x703 MBF MBF 10x723 [nop] [nop] 10x704 MBW MBW 10x724 XL* XL* 10x705 MWI MWI 10x725 XS* XS* 10x706 MWF MWF 10x726 XC* XC* 10x707 MWW MWW 10x727 LF* LF* 10x710 SY* SY* 10x730 RS* RS* 10x711 US* US* 10x731 RV* RV* 10x712 PA* PA* 10x732 DJP DJP 10x713 PB* PB* 10x733 DJS DJS 10x714 SSM SSM 10x734 SJP SJP 10x715 JRS JRS 10x735 SJS SJS 10x716 [nop] [nop] 10x736 UJP UJP 10x717 [nop] [nop] 10x737 UJS UJS Instructions that use IR bit 9 to select the A or B register are designated with a * above (e.g., 101710 is SYA, and 105710 is SYB). For those that do not use this feature, either the 101xxx or 105xxx code will execute the corresponding instruction, although the 105xxx form is the documented instruction code. Implementation notes: 1. Instruction code 10x700 will execute the XMM instruction, although 10x720 is the documented instruction value. 2. Instruction code 10x701 will complement the A or B register, as indicated, on 1000-E and F-Series machines. This instruction is a NOP on M-Series machines. 3. The DMS privilege violation rules are: - load map and CTL5 set (XMM, XMS, XM*, SY*, US*, PA*, PB*) - load state or fence and UMAP set (JRS, DJP, DJS, SJP, SJS, UJP, UJS, LF*) 4. The 1000 manual is incorrect in stating that M*I, M*W, XS* are privileged. 5. The protected memory lower bound for the DJP, SJP, UJP, and JRS instructions is 2. */ static const OP_PAT op_dms[32] = { OP_N, OP_N, OP_N, OP_N, /* [xmm] [test] MBI MBF */ OP_N, OP_N, OP_N, OP_N, /* MBW MWI MWF MWW */ OP_N, OP_N, OP_N, OP_N, /* SYA/B USA/B PAA/B PBA/B */ OP_A, OP_KA, OP_N, OP_N, /* SSM JRS nop nop */ OP_N, OP_N, OP_N, OP_N, /* XMM XMS XMA/B nop */ OP_A, OP_A, OP_A, OP_N, /* XLA/B XSA/B XCA/B LFA/B */ OP_N, OP_N, OP_A, OP_A, /* RSA/B RVA/B DJP DJS */ OP_A, OP_A, OP_A, OP_A /* SJP SJS UJP UJS */ }; t_stat cpu_dms (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry, absel; uint32 i, t, mapi, mapj; absel = (IR & I_AB)? 1: 0; /* get A/B select */ entry = IR & 037; /* mask to entry point */ if (op_dms[entry] != OP_N) if (reason = cpu_ops (op_dms[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<3:0> */ /* DMS module 1 */ case 000: /* [undefined] 105700 (OP_N) */ goto XMM; /* decodes as XMM */ case 001: /* [self test] 105701 (OP_N) */ if (UNIT_CPU_MODEL != UNIT_1000_M) /* executes as NOP on 1000-M */ ABREG[absel] = ~ABREG[absel]; /* CMA or CMB */ break; case 002: /* MBI 105702 (OP_N) */ AR = AR & ~1; /* force A, B even */ BR = BR & ~1; while (XR != 0) { /* loop */ t = ReadB (AR); /* read curr */ WriteBA (BR, t); /* write alt */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq && !(AR & 1)) { /* more, int, even? */ PC = err_PC; /* stop for now */ break; } } break; case 003: /* MBF 105703 (OP_N) */ AR = AR & ~1; /* force A, B even */ BR = BR & ~1; while (XR != 0) { /* loop */ t = ReadBA (AR); /* read alt */ WriteB (BR, t); /* write curr */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq && !(AR & 1)) { /* more, int, even? */ PC = err_PC; /* stop for now */ break; } } break; case 004: /* MBW 105704 (OP_N) */ AR = AR & ~1; /* force A, B even */ BR = BR & ~1; while (XR != 0) { /* loop */ t = ReadBA (AR); /* read alt */ WriteBA (BR, t); /* write alt */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq && !(AR & 1)) { /* more, int, even? */ PC = err_PC; /* stop for now */ break; } } break; case 005: /* MWI 105705 (OP_N) */ while (XR != 0) { /* loop */ t = ReadW (AR & VAMASK); /* read curr */ WriteWA (BR & VAMASK, t); /* write alt */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq) { /* more and intr? */ PC = err_PC; /* stop for now */ break; } } break; case 006: /* MWF 105706 (OP_N) */ while (XR != 0) { /* loop */ t = ReadWA (AR & VAMASK); /* read alt */ WriteW (BR & VAMASK, t); /* write curr */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq) { /* more and intr? */ PC = err_PC; /* stop for now */ break; } } break; case 007: /* MWW 105707 (OP_N) */ while (XR != 0) { /* loop */ t = ReadWA (AR & VAMASK); /* read alt */ WriteWA (BR & VAMASK, t); /* write alt */ AR = (AR + 1) & DMASK; /* incr ptrs */ BR = (BR + 1) & DMASK; XR = (XR - 1) & DMASK; if (XR && intrq) { /* more and intr? */ PC = err_PC; /* stop for now */ break; } } break; case 010: /* SYA, SYB 10x710 (OP_N) */ case 011: /* USA, USB 10x711 (OP_N) */ case 012: /* PAA, PAB 10x712 (OP_N) */ case 013: /* PBA, PBB 10x713 (OP_N) */ mapi = (IR & 03) << VA_N_PAG; /* map base */ if (ABREG[absel] & SIGN) { /* store? */ for (i = 0; i < MAP_LNT; i++) { t = dms_rmap (mapi + i); /* map to memory */ WriteW ((ABREG[absel] + i) & VAMASK, t); } } else { /* load */ dms_viol (err_PC, MVI_PRV); /* priv if PRO */ for (i = 0; i < MAP_LNT; i++) { t = ReadW ((ABREG[absel] + i) & VAMASK); dms_wmap (mapi + i, t); /* mem to map */ } } ABREG[absel] = (ABREG[absel] + MAP_LNT) & DMASK; break; case 014: /* SSM 105714 (OP_A) */ WriteW (op[0].word, dms_upd_sr ()); /* store stat */ break; case 015: /* JRS 105715 (OP_KA) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ dms_enb = 0; /* assume off */ dms_ump = SMAP; if (op[0].word & 0100000) { /* set enable? */ dms_enb = 1; if (op[0].word & 0040000) dms_ump = UMAP; /* set/clr usr */ } mp_dms_jmp (op[1].word, 2); /* mpck jmp target */ PCQ_ENTRY; /* save old PC */ PC = op[1].word; /* jump */ ion_defer = 1; /* defer intr */ break; /* DMS module 2 */ case 020: /* XMM 105720 (OP_N) */ XMM: if (XR == 0) break; /* nop? */ while (XR != 0) { /* loop */ if (XR & SIGN) { /* store? */ t = dms_rmap (AR); /* map to mem */ WriteW (BR & VAMASK, t); XR = (XR + 1) & DMASK; } else { /* load */ dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ t = ReadW (BR & VAMASK); /* mem to map */ dms_wmap (AR, t); XR = (XR - 1) & DMASK; } AR = (AR + 1) & DMASK; BR = (BR + 1) & DMASK; if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */ PC = err_PC; /* stop for now */ break; } } break; case 021: /* XMS 105721 (OP_N) */ if ((XR & SIGN) || (XR == 0)) break; /* nop? */ dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ while (XR != 0) { dms_wmap (AR, BR); /* AR to map */ XR = (XR - 1) & DMASK; AR = (AR + 1) & DMASK; BR = (BR + 1) & DMASK; if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */ PC = err_PC; break; } } break; case 022: /* XMA, XMB 10x722 (OP_N) */ dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ if (ABREG[absel] & 0100000) mapi = UMAP; else mapi = SMAP; if (ABREG[absel] & 0000001) mapj = PBMAP; else mapj = PAMAP; for (i = 0; i < MAP_LNT; i++) { t = dms_rmap (mapi + i); /* read map */ dms_wmap (mapj + i, t); /* write map */ } break; case 024: /* XLA, XLB 10x724 (OP_A) */ ABREG[absel] = ReadWA (op[0].word); /* load alt */ break; case 025: /* XSA, XSB 10x725 (OP_A) */ WriteWA (op[0].word, ABREG[absel]); /* store alt */ break; case 026: /* XCA, XCB 10x726 (OP_A) */ if (ABREG[absel] != ReadWA (op[0].word)) /* compare alt */ PC = (PC + 1) & VAMASK; break; case 027: /* LFA, LFB 10x727 (OP_N) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ dms_sr = (dms_sr & ~(MST_FLT | MST_FENCE)) | (ABREG[absel] & (MST_FLT | MST_FENCE)); break; case 030: /* RSA, RSB 10x730 (OP_N) */ ABREG[absel] = dms_upd_sr (); /* save stat */ break; case 031: /* RVA, RVB 10x731 (OP_N) */ ABREG[absel] = dms_upd_vr (err_PC); /* return updated violation register */ break; case 032: /* DJP 105732 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ dms_enb = 0; /* disable map */ dms_ump = SMAP; mp_dms_jmp (op[0].word, 2); /* validate jump addr */ PCQ_ENTRY; /* save curr PC */ PC = op[0].word; /* new PC */ ion_defer = 1; break; case 033: /* DJS 105733 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ WriteW (op[0].word, PC); /* store ret addr */ PCQ_ENTRY; /* save curr PC */ PC = (op[0].word + 1) & VAMASK; /* new PC */ dms_enb = 0; /* disable map */ dms_ump = SMAP; ion_defer = 1; /* defer intr */ break; case 034: /* SJP 105734 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ dms_enb = 1; /* enable system */ dms_ump = SMAP; mp_dms_jmp (op[0].word, 2); /* validate jump addr */ PCQ_ENTRY; /* save curr PC */ PC = op[0].word; /* jump */ ion_defer = 1; /* defer intr */ break; case 035: /* SJS 105735 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ t = PC; /* save retn addr */ PCQ_ENTRY; /* save curr PC */ PC = (op[0].word + 1) & VAMASK; /* new PC */ dms_enb = 1; /* enable system */ dms_ump = SMAP; WriteW (op[0].word, t); /* store ret addr */ ion_defer = 1; /* defer intr */ break; case 036: /* UJP 105736 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ dms_enb = 1; /* enable user */ dms_ump = UMAP; mp_dms_jmp (op[0].word, 2); /* validate jump addr */ PCQ_ENTRY; /* save curr PC */ PC = op[0].word; /* jump */ ion_defer = 1; /* defer intr */ break; case 037: /* UJS 105737 (OP_A) */ if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ t = PC; /* save retn addr */ PCQ_ENTRY; /* save curr PC */ PC = (op[0].word + 1) & VAMASK; /* new PC */ dms_enb = 1; /* enable user */ dms_ump = UMAP; WriteW (op[0].word, t); /* store ret addr */ ion_defer = 1; /* defer intr */ break; default: /* others NOP */ break; } return reason; } /* Extended Instruction Group The Extended Instruction Group (EIG) adds 32 index and 10 bit/byte/word manipulation instructions to the 1000 base set. These instructions use the new X and Y index registers that were added to the 1000. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A std std std The instruction codes are mapped to routines as follows: Instr. 1000-M/E/F Instr. 1000-M/E/F ------ ---------- ------ ---------- 10x740 S*X 10x760 ISX 10x741 C*X 10x761 DSX 10x742 L*X 10x762 JLY 10x743 STX 10x763 LBT 10x744 CX* 10x764 SBT 10x745 LDX 10x765 MBT 10x746 ADX 10x766 CBT 10x747 X*X 10x767 SFB 10x750 S*Y 10x770 ISY 10x751 C*Y 10x771 DSY 10x752 L*Y 10x772 JPY 10x753 STY 10x773 SBS 10x754 CY* 10x774 CBS 10x755 LDY 10x775 TBS 10x756 ADY 10x776 CMW 10x757 X*Y 10x777 MVW Instructions that use IR bit 9 to select the A or B register are designated with a * above (e.g., 101740 is SAX, and 105740 is SBX). For those that do not use this feature, either the 101xxx or 105xxx code will execute the corresponding instruction, although the 105xxx form is the documented instruction code. Implementation notes: 1. The LBT, SBT, MBT, and MVW instructions are used as part of the 2100 IOP implementation. When so called, the MBT and MVW instructions have the additional restriction that the count must be positive. 2. The protected memory lower bound for the JLY and JPY instructions is 0. */ static const OP_PAT op_eig[32] = { OP_A, OP_N, OP_A, OP_A, /* S*X C*X L*X STX */ OP_N, OP_K, OP_K, OP_N, /* CX* LDX ADX X*X */ OP_A, OP_N, OP_A, OP_A, /* S*Y C*Y L*Y STY */ OP_N, OP_K, OP_K, OP_N, /* CY* LDY ADY X*Y */ OP_N, OP_N, OP_A, OP_N, /* ISX DSX JLY LBT */ OP_N, OP_KV, OP_KV, OP_N, /* SBT MBT CBT SFB */ OP_N, OP_N, OP_C, OP_KA, /* ISY DSY JPY SBS */ OP_KA, OP_KK, OP_KV, OP_KV /* CBS TBS CMW MVW */ }; t_stat cpu_eig (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry, absel; uint32 t, v1, v2, wc; int32 sop1, sop2; absel = (IR & I_AB)? 1: 0; /* get A/B select */ entry = IR & 037; /* mask to entry point */ if (op_eig[entry] != OP_N) if (reason = cpu_ops (op_eig[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<4:0> */ /* EIG module 1 */ case 000: /* SAX, SBX 10x740 (OP_A) */ op[0].word = (op[0].word + XR) & VAMASK; /* indexed addr */ WriteW (op[0].word, ABREG[absel]); /* store */ break; case 001: /* CAX, CBX 10x741 (OP_N) */ XR = ABREG[absel]; /* copy to XR */ break; case 002: /* LAX, LBX 10x742 (OP_A) */ op[0].word = (op[0].word + XR) & VAMASK; /* indexed addr */ ABREG[absel] = ReadW (op[0].word); /* load */ break; case 003: /* STX 105743 (OP_A) */ WriteW (op[0].word, XR); /* store XR */ break; case 004: /* CXA, CXB 10x744 (OP_N) */ ABREG[absel] = XR; /* copy from XR */ break; case 005: /* LDX 105745 (OP_K)*/ XR = op[0].word; /* load XR */ break; case 006: /* ADX 105746 (OP_K) */ t = XR + op[0].word; /* add to XR */ if (t > DMASK) E = 1; /* set E, O */ if (((~XR ^ op[0].word) & (XR ^ t)) & SIGN) O = 1; XR = t & DMASK; break; case 007: /* XAX, XBX 10x747 (OP_N) */ t = XR; /* exchange XR */ XR = ABREG[absel]; ABREG[absel] = t; break; case 010: /* SAY, SBY 10x750 (OP_A) */ op[0].word = (op[0].word + YR) & VAMASK; /* indexed addr */ WriteW (op[0].word, ABREG[absel]); /* store */ break; case 011: /* CAY, CBY 10x751 (OP_N) */ YR = ABREG[absel]; /* copy to YR */ break; case 012: /* LAY, LBY 10x752 (OP_A) */ op[0].word = (op[0].word + YR) & VAMASK; /* indexed addr */ ABREG[absel] = ReadW (op[0].word); /* load */ break; case 013: /* STY 105753 (OP_A) */ WriteW (op[0].word, YR); /* store YR */ break; case 014: /* CYA, CYB 10x754 (OP_N) */ ABREG[absel] = YR; /* copy from YR */ break; case 015: /* LDY 105755 (OP_K) */ YR = op[0].word; /* load YR */ break; case 016: /* ADY 105756 (OP_K) */ t = YR + op[0].word; /* add to YR */ if (t > DMASK) E = 1; /* set E, O */ if (((~YR ^ op[0].word) & (YR ^ t)) & SIGN) O = 1; YR = t & DMASK; break; case 017: /* XAY, XBY 10x757 (OP_N) */ t = YR; /* exchange YR */ YR = ABREG[absel]; ABREG[absel] = t; break; /* EIG module 2 */ case 020: /* ISX 105760 (OP_N) */ XR = (XR + 1) & DMASK; /* incr XR */ if (XR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ break; case 021: /* DSX 105761 (OP_N) */ XR = (XR - 1) & DMASK; /* decr XR */ if (XR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ break; case 022: /* JLY 105762 (OP_A) */ mp_dms_jmp (op[0].word, 0); /* validate jump addr */ PCQ_ENTRY; YR = PC; /* ret addr to YR */ PC = op[0].word; /* jump */ break; case 023: /* LBT 105763 (OP_N) */ AR = ReadB (BR); /* load byte */ BR = (BR + 1) & DMASK; /* incr ptr */ break; case 024: /* SBT 105764 (OP_N) */ WriteB (BR, AR); /* store byte */ BR = (BR + 1) & DMASK; /* incr ptr */ break; case 025: /* MBT 105765 (OP_KV) */ wc = ReadW (op[1].word); /* get continuation count */ if (wc == 0) wc = op[0].word; /* none? get initiation count */ if ((wc & SIGN) && (UNIT_CPU_TYPE == UNIT_TYPE_2100)) break; /* < 0 is NOP for 2100 IOP */ while (wc != 0) { /* while count */ WriteW (op[1].word, wc); /* for MP abort */ t = ReadB (AR); /* move byte */ WriteB (BR, t); AR = (AR + 1) & DMASK; /* incr src */ BR = (BR + 1) & DMASK; /* incr dst */ wc = (wc - 1) & DMASK; /* decr cnt */ if (intrq && wc) { /* intr, more to do? */ PC = err_PC; /* back up PC */ break; } } WriteW (op[1].word, wc); /* clean up inline */ break; case 026: /* CBT 105766 (OP_KV) */ wc = ReadW (op[1].word); /* get continuation count */ if (wc == 0) wc = op[0].word; /* none? get initiation count */ while (wc != 0) { /* while count */ WriteW (op[1].word, wc); /* for MP abort */ v1 = ReadB (AR); /* get src1 */ v2 = ReadB (BR); /* get src2 */ if (v1 != v2) { /* compare */ PC = (PC + 1 + (v1 > v2)) & VAMASK; BR = (BR + wc) & DMASK; /* update BR */ wc = 0; /* clr interim */ break; } AR = (AR + 1) & DMASK; /* incr src1 */ BR = (BR + 1) & DMASK; /* incr src2 */ wc = (wc - 1) & DMASK; /* decr cnt */ if (intrq && wc) { /* intr, more to do? */ PC = err_PC; /* back up PC */ break; } } WriteW (op[1].word, wc); /* clean up inline */ break; case 027: /* SFB 105767 (OP_N) */ v1 = AR & 0377; /* test byte */ v2 = (AR >> 8) & 0377; /* term byte */ for (;;) { /* scan */ t = ReadB (BR); /* read byte */ if (t == v1) break; /* test match? */ BR = (BR + 1) & DMASK; if (t == v2) { /* term match? */ PC = (PC + 1) & VAMASK; break; } if (intrq) { /* int pending? */ PC = err_PC; /* back up PC */ break; } } break; case 030: /* ISY 105770 (OP_N) */ YR = (YR + 1) & DMASK; /* incr YR */ if (YR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ break; case 031: /* DSY 105771 (OP_N) */ YR = (YR - 1) & DMASK; /* decr YR */ if (YR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ break; case 032: /* JPY 105772 (OP_C) */ op[0].word = (op[0].word + YR) & VAMASK; /* index, no indir */ mp_dms_jmp (op[0].word, 0); /* validate jump addr */ PCQ_ENTRY; PC = op[0].word; /* jump */ break; case 033: /* SBS 105773 (OP_KA) */ WriteW (op[1].word, /* set bits */ ReadW (op[1].word) | op[0].word); break; case 034: /* CBS 105774 (OP_KA) */ WriteW (op[1].word, /* clear bits */ ReadW (op[1].word) & ~op[0].word); break; case 035: /* TBS 105775 (OP_KK) */ if ((op[1].word & op[0].word) != op[0].word) /* test bits */ PC = (PC + 1) & VAMASK; break; case 036: /* CMW 105776 (OP_KV) */ wc = ReadW (op[1].word); /* get continuation count */ if (wc == 0) wc = op[0].word; /* none? get initiation count */ while (wc != 0) { /* while count */ WriteW (op[1].word, wc); /* for abort */ v1 = ReadW (AR & VAMASK); /* first op */ v2 = ReadW (BR & VAMASK); /* second op */ sop1 = (int32) SEXT (v1); /* signed */ sop2 = (int32) SEXT (v2); if (sop1 != sop2) { /* compare */ PC = (PC + 1 + (sop1 > sop2)) & VAMASK; BR = (BR + wc) & DMASK; /* update BR */ wc = 0; /* clr interim */ break; } AR = (AR + 1) & DMASK; /* incr src1 */ BR = (BR + 1) & DMASK; /* incr src2 */ wc = (wc - 1) & DMASK; /* decr cnt */ if (intrq && wc) { /* intr, more to do? */ PC = err_PC; /* back up PC */ break; } } WriteW (op[1].word, wc); /* clean up inline */ break; case 037: /* MVW 105777 (OP_KV) */ wc = ReadW (op[1].word); /* get continuation count */ if (wc == 0) wc = op[0].word; /* none? get initiation count */ if ((wc & SIGN) && (UNIT_CPU_TYPE == UNIT_TYPE_2100)) break; /* < 0 is NOP for 2100 IOP */ while (wc != 0) { /* while count */ WriteW (op[1].word, wc); /* for abort */ t = ReadW (AR & VAMASK); /* move word */ WriteW (BR & VAMASK, t); AR = (AR + 1) & DMASK; /* incr src */ BR = (BR + 1) & DMASK; /* incr dst */ wc = (wc - 1) & DMASK; /* decr cnt */ if (intrq && wc) { /* intr, more to do? */ PC = err_PC; /* back up PC */ break; } } WriteW (op[1].word, wc); /* clean up inline */ break; } return reason; } /* 2000 I/O Processor The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system I/O processor. Most 2000 systems were delivered with 2100 CPUs, although IOP microcode was developed for the 1000-M and 1000-E. As the I/O processors were specific to the 2000 system, general compatibility with other CPU microcode options was unnecessary, and indeed no other options were possible for the 2100. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A 13206A 13207A 22702A N/A The routines are mapped to instruction codes as follows: Instr. 2100 1000-M/E Description ------ ---------- ---------- -------------------------------------------- SAI 105060-117 101400-037 Store A indexed by B (+/- offset in IR<4:0>) LAI 105020-057 105400-037 Load A indexed by B (+/- offset in IR<4:0>) CRC 105150 105460 Generate CRC REST 105340 105461 Restore registers from stack READF 105220 105462 Read F register (stack pointer) INS -- 105463 Initialize F register (stack pointer) ENQ 105240 105464 Enqueue PENQ 105257 105465 Priority enqueue DEQ 105260 105466 Dequeue TRSLT 105160 105467 Translate character ILIST 105000 105470 Indirect address list (similar to $SETP) PRFEI 105222 105471 Power fail exit with I/O PRFEX 105223 105472 Power fail exit PRFIO 105221 105473 Power fail I/O SAVE 105362 105474 Save registers to stack MBYTE 105120 105765 Move bytes (MBT) MWORD 105200 105777 Move words (MVW) SBYTE 105300 105764 Store byte (SBT) LBYTE 105320 105763 Load byte (LBT) The INS instruction was not required in the 2100 implementation because the stack pointer was actually the memory protect fence register and so could be loaded directly with an OTA/B 05. Also, the 1000 implementation did not offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent instructions from the standard Extended Instruction Group were used instead. Note that the 2100 MBYTE and MWORD instructions operate slightly differently from the 1000 MBT and MVW instructions. Specifically, the move count is signed on the 2100 and unsigned on the 1000. A negative count on the 2100 results in a NOP. The simulator remaps the 2100 instructions to the 1000 codes. The four EIG equivalents are dispatched to the EIG simulator. The rest are handled here. Additional reference: - HP 2000 Computer System Sources and Listings Documentation (22687-90020, undated), section 3, pages 2-74 through 2-91. */ static const OP_PAT op_iop[16] = { OP_V, OP_N, OP_N, OP_N, /* CRC RESTR READF INS */ OP_N, OP_N, OP_N, OP_V, /* ENQ PENQ DEQ TRSLT */ OP_AC, OP_CVA, OP_A, OP_CV, /* ILIST PRFEI PRFEX PRFIO */ OP_N, OP_N, OP_N, OP_N /* SAVE --- --- --- */ }; t_stat cpu_iop (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry; uint32 hp, tp, i, t, wc, MA; if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { /* 2100 IOP? */ if ((IR >= 0105020) && (IR <= 0105057)) /* remap LAI */ IR = 0105400 | (IR - 0105020); else if ((IR >= 0105060) && (IR <= 0105117)) /* remap SAI */ IR = 0101400 | (IR - 0105060); else { switch (IR) { /* remap others */ case 0105000: IR = 0105470; break; /* ILIST */ case 0105120: return cpu_eig (0105765, intrq); /* MBYTE (maps to MBT) */ case 0105150: IR = 0105460; break; /* CRC */ case 0105160: IR = 0105467; break; /* TRSLT */ case 0105200: return cpu_eig (0105777, intrq); /* MWORD (maps to MVW) */ case 0105220: IR = 0105462; break; /* READF */ case 0105221: IR = 0105473; break; /* PRFIO */ case 0105222: IR = 0105471; break; /* PRFEI */ case 0105223: IR = 0105472; break; /* PRFEX */ case 0105240: IR = 0105464; break; /* ENQ */ case 0105257: IR = 0105465; break; /* PENQ */ case 0105260: IR = 0105466; break; /* DEQ */ case 0105300: return cpu_eig (0105764, intrq); /* SBYTE (maps to SBT) */ case 0105320: return cpu_eig (0105763, intrq); /* LBYTE (maps to LBT) */ case 0105340: IR = 0105461; break; /* REST */ case 0105362: IR = 0105474; break; /* SAVE */ default: /* all others invalid */ return stop_inst; } } } entry = IR & 077; /* mask to entry point */ if (entry <= 037) { /* LAI/SAI 10x400-437 */ MA = ((entry - 020) + BR) & VAMASK; /* +/- offset */ if (IR & I_AB) AR = ReadW (MA); /* AB = 1 -> LAI */ else WriteW (MA, AR); /* AB = 0 -> SAI */ return reason; } else if (entry <= 057) /* IR = 10x440-457? */ return stop_inst; /* not part of IOP */ entry = entry - 060; /* offset 10x460-477 */ if (op_iop[entry] != OP_N) if (reason = cpu_ops (op_iop[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<5:0> */ case 000: /* CRC 105460 (OP_V) */ t = ReadW (op[0].word) ^ (AR & 0377); /* xor prev CRC and char */ for (i = 0; i < 8; i++) { /* apply polynomial */ t = (t >> 1) | ((t & 1) << 15); /* rotate right */ if (t & SIGN) t = t ^ 020001; /* old t<0>? xor */ } WriteW (op[0].word, t); /* rewrite CRC */ break; case 001: /* RESTR 105461 (OP_N) */ iop_sp = (iop_sp - 1) & VAMASK; /* decr stack ptr */ t = ReadW (iop_sp); /* get E and O */ O = ((t >> 1) ^ 1) & 1; /* restore O */ E = t & 1; /* restore E */ iop_sp = (iop_sp - 1) & VAMASK; /* decr sp */ BR = ReadW (iop_sp); /* restore B */ iop_sp = (iop_sp - 1) & VAMASK; /* decr sp */ AR = ReadW (iop_sp); /* restore A */ if (UNIT_CPU_MODEL == UNIT_2100) mp_fence = iop_sp; /* 2100 keeps sp in MP FR */ break; case 002: /* READF 105462 (OP_N) */ AR = iop_sp; /* copy stk ptr */ break; case 003: /* INS 105463 (OP_N) */ iop_sp = AR; /* init stk ptr */ break; case 004: /* ENQ 105464 (OP_N) */ hp = ReadW (AR & VAMASK); /* addr of head */ tp = ReadW ((AR + 1) & VAMASK); /* addr of tail */ WriteW ((BR - 1) & VAMASK, 0); /* entry link */ WriteW ((tp - 1) & VAMASK, BR); /* tail link */ WriteW ((AR + 1) & VAMASK, BR); /* queue tail */ if (hp != 0) PC = (PC + 1) & VAMASK; /* q not empty? skip */ break; case 005: /* PENQ 105465 (OP_N) */ hp = ReadW (AR & VAMASK); /* addr of head */ WriteW ((BR - 1) & VAMASK, hp); /* becomes entry link */ WriteW (AR & VAMASK, BR); /* queue head */ if (hp == 0) /* q empty? */ WriteW ((AR + 1) & VAMASK, BR); /* queue tail */ else PC = (PC + 1) & VAMASK; /* skip */ break; case 006: /* DEQ 105466 (OP_N) */ BR = ReadW (AR & VAMASK); /* addr of head */ if (BR) { /* queue not empty? */ hp = ReadW ((BR - 1) & VAMASK); /* read hd entry link */ WriteW (AR & VAMASK, hp); /* becomes queue head */ if (hp == 0) /* q now empty? */ WriteW ((AR + 1) & VAMASK, (AR + 1) & DMASK); PC = (PC + 1) & VAMASK; /* skip */ } break; case 007: /* TRSLT 105467 (OP_V) */ wc = ReadW (op[0].word); /* get count */ if (wc & SIGN) break; /* cnt < 0? */ while (wc != 0) { /* loop */ MA = (AR + AR + ReadB (BR)) & VAMASK; t = ReadB (MA); /* xlate */ WriteB (BR, t); /* store char */ BR = (BR + 1) & DMASK; /* incr ptr */ wc = (wc - 1) & DMASK; /* decr cnt */ if (wc && intrq) { /* more and intr? */ WriteW (op[0].word, wc); /* save count */ PC = err_PC; /* stop for now */ break; } } break; case 010: /* ILIST 105470 (OP_AC) */ do { /* for count */ WriteW (op[0].word, AR); /* write AR to mem */ AR = (AR + 1) & DMASK; /* incr AR */ op[0].word = (op[0].word + 1) & VAMASK; /* incr MA */ op[1].word = (op[1].word - 1) & DMASK; /* decr count */ } while (op[1].word != 0); break; case 011: /* PRFEI 105471 (OP_CVA) */ WriteW (op[1].word, 1); /* set flag */ reason = iogrp (op[0].word, 0); /* execute I/O instr */ op[0].word = op[2].word; /* set rtn and fall through */ case 012: /* PRFEX 105472 (OP_A) */ PCQ_ENTRY; PC = ReadW (op[0].word) & VAMASK; /* jump indirect */ WriteW (op[0].word, 0); /* clear exit */ break; case 013: /* PRFIO 105473 (OP_CV) */ WriteW (op[1].word, 1); /* set flag */ reason = iogrp (op[0].word, 0); /* execute instr */ break; case 014: /* SAVE 105474 (OP_N) */ WriteW (iop_sp, AR); /* save A */ iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ WriteW (iop_sp, BR); /* save B */ iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ t = ((O ^ 1) << 1) | E; /* merge E and O */ WriteW (iop_sp, t); /* save E and O */ iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ if (UNIT_CPU_TYPE == UNIT_TYPE_2100) mp_fence = iop_sp; /* 2100 keeps sp in MP FR */ break; default: /* instruction undefined */ return stop_inst; } return reason; } simh-3.8.1/HP2100/hp2100_cpu0.c0000644000175000017500000002666611064535246013506 0ustar vlmvlm/* hp2100_cpu0.c: HP 1000 user microcode and unimplemented instruction set stubs Copyright (c) 2006-2008, J. David Bryan 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 AUTHOR 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 author. CPU0 User microcode and unimplemented firmware options 18-Sep-08 JDB .FLUN and self-tests for VIS and SIGNAL are NOP if not present 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) Added "user microcode" dispatcher for unclaimed instructions 26-Feb-08 HV Removed and implemented "cpu_vis" and "cpu_signal" 22-Nov-07 JDB Removed and implemented "cpu_rte_ema" 12-Nov-07 JDB Removed and implemented "cpu_rte_vma" and "cpu_rte_os" 01-Dec-06 JDB Removed and implemented "cpu_sis". 26-Sep-06 JDB Created Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. This file contains template simulations for the firmware options that have not yet been implemented. When a given firmware option is implemented, it should be moved out of this file and into another (or its own, depending on complexity). It also contains a user-microprogram dispatcher to allow simulation of site-specific firmware. All UIG instructions unclaimed by installed firmware options are directed here and may be simulated by writing the appropriate code. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" /* Distributed System. Distributed System firmware was provided with the HP 91740A DS/1000 product for use with the HP 12771A (12665A) Serial Interface and 12773A Modem Interface system interconnection kits. Firmware permitted high-speed transfers with minimum impact to the processor. The advent of the "intelligent" 12794A and 12825A HDLC cards, the 12793A and 12834A Bisync cards, and the 91750A DS-1000/IV software obviated the need for CPU firmware, as essentially the firmware was moved onto the I/O cards. Primary documentation for the DS instructions has not been located. However, examination of the DS/1000 sources reveals that two instruction were used by the DVA65 Serial Interface driver (91740-18071) and placed in the trap cells of the communications interfaces. Presumably they handled interrupts from the cards. Implementation of the DS instructions will also require simulation of the 12665A Hardwired Serial Data Interface Card and the 12620A RTE Privileged Interrupt Fence. These are required for DS/1000. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A 91740A 91740B 91740B The routines are mapped to instruction codes as follows: Instr. 1000-M 1000-E/F Description ------ ------ -------- ---------------------------------------------- 105520 105300 "Open loop" (trap cell handler) 105521 105301 "Closed loop" (trap cell handler) 105522 105302 [unknown] [test] 105524 105304 [self test] Notes: 1. The E/F-Series opcodes were moved from 105340-357 to 105300-317 at revision 1813. 2. DS/1000 ROM data are available from Bitsavers. Additional references (documents unavailable): - HP 91740A M-Series Distributed System (DS/1000) Firmware Installation Manual (91740-90007). - HP 91740B Distributed System (DS/1000) Firmware Installation Manual (91740-90009). */ static const OP_PAT op_ds[16] = { OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ }; t_stat cpu_ds (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry; entry = IR & 017; /* mask to entry point */ if (op_ds[entry] != OP_N) if (reason = cpu_ops (op_ds[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<3:0> */ default: /* others undefined */ reason = stop_inst; } return reason; } /* User firmware dispatcher. All UIG instructions unclaimed by installed firmware options are directed here. User- or site-specific firmware may be simulated by dispatching to the appropriate simulator routine. Unimplemented instructions should return "stop_inst" to cause a simulator stop if enabled. Implementation notes: 1. This routine may be passed any opcode in the ranges 101400-101737 and 105000-105737. The 10x740-777 range is dedicated to the EIG instructions and is unavailable for user microprograms. 2. HP operating systems and subsystems depend on the following instructions to execute as NOP and return success if the corresponding firmware is not installed: 105226 -- Fast FORTRAN Processor .FLUN instruction 105355 -- RTE-6/VM OS self-test instruction 105477 -- Vector Instruction Set self-test 105617 -- SIGNAL/1000 self-test These instructions are executed to determine firmware configuration dynamically. If you use any of these opcodes for your own use, be aware that certain HP programs may fail. 3. User microprograms occupied one or more firmware modules, each containing 16 potential instruction entry points. A skeleton dispatcher for the 32 possible modules is implemented below, along with a sample module. */ t_stat cpu_user (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2116/15/14 CPU? */ return stop_inst; /* user microprograms not supported */ switch (IR) { /* opcodes for firmware detection */ case 0105226: /* FFP .FLUN */ case 0105355: /* RTE-6/VM OS self-test */ case 0105477: /* VIS self-test */ case 0105617: /* SIGNAL/1000 self-test */ return SCPE_OK; /* execute as NOP */ } switch ((IR >> 4) & 037) { /* decode IR<8:4> */ /* case 000: /* 105000-105017 */ /* return cpu_user_00 (IR, intrq); /* uncomment to handle instruction */ /* case 001: /* 105020-105037 */ /* return cpu_user_01 (IR, intrq); /* uncomment to handle instruction */ /* case 0nn: /* other cases as needed */ /* return cpu_user_nn (IR, intrq); /* uncomment to handle instruction */ case 020: /* 10x400-10x417 */ return cpu_user_20 (IR, intrq); /* call sample dispatcher */ /* case 021: /* 10x420-10x437 */ /* return cpu_user_21 (IR, intrq); /* uncomment to handle instruction */ /* case 0nn: /* other cases as needed */ /* return cpu_user_nn (IR, intrq); /* uncomment to handle instruction */ default: /* others undefined */ reason = stop_inst; } return reason; } /* Example user microprogram simulator. User- or site-specific firmware may be simulated by writing the appropriate code below. Unimplemented instructions should return "stop_inst" to cause a simulator stop if enabled. For information on the operand patterns used in the "op_user" array, see the comments preceding the "cpu_ops" routine in "hp2100_cpu1.c" and the "operand processing encoding" constants in "hp2100_cpu1.h". */ static const OP_PAT op_user_20[16] = { OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ }; t_stat cpu_user_20 (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 entry; entry = IR & 017; /* mask to entry point */ if (op_user_20 [entry] != OP_N) if (reason = cpu_ops (op_user_20 [entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<4:0> */ case 000: /* 10x400 */ /* break; /* uncomment to handle instruction */ case 001: /* 10x401 */ /* break; /* uncomment to handle instruction */ /* case 0nn: /* other cases as needed */ /* break; /* uncomment to handle instruction */ default: /* others undefined */ reason = stop_inst; } return reason; } simh-3.8.1/HP2100/hp2100_ipl.c0000644000175000017500000007263711107411526013412 0ustar vlmvlm/* hp2100_ipl.c: HP 2000 interprocessor link simulator Copyright (c) 2002-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. IPLI, IPLO 12875A interprocessor link 07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach 15-Jul-08 JDB Revised EDT handler to refine completion delay conditions 09-Jul-08 JDB Revised ipl_boot to use ibl_copy 26-Jun-08 JDB Rewrote device I/O to model backplane signals 01-Mar-07 JDB IPLI EDT delays DMA completion interrupt for TSB Added debug printouts 28-Dec-06 JDB Added ioCRS state to I/O decoders 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Oct-04 JDB Fixed enable/disable from either device 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Implemented DMA SRQ (follows FLG) 21-Dec-03 RMS Adjusted ipl_ptime for TSB (from Mike Gemeny) 09-May-03 RMS Added network device flag 31-Jan-03 RMS Links are full duplex (found by Mike Gemeny) Reference: - 12875A Processor Interconnect Kit Operating and Service Manual (12875-90002, Jan-1974) The 12875A Processor Interconnect Kit consists four 12566A Microcircuit Interface cards. Two are used in each processor. One card in each system is used to initiate transmissions to the other, and the second card is used to receive transmissions from the other. Each pair of cards forms a bidirectional link, as the sixteen data lines are cross-connected, so that data sent and status returned are supported. In each processor, data is sent on the lower priority card and received on the higher priority card. Two sets of cards are used to support simultaneous transmission in both directions. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "sim_sock.h" #include "sim_tmxr.h" #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ #define UNIT_V_ACTV (UNIT_V_UF + 1) /* making connection */ #define UNIT_V_ESTB (UNIT_V_UF + 2) /* connection established */ #define UNIT_V_HOLD (UNIT_V_UF + 3) /* character holding */ #define UNIT_DIAG (1 << UNIT_V_DIAG) #define UNIT_ACTV (1 << UNIT_V_ACTV) #define UNIT_ESTB (1 << UNIT_V_ESTB) #define UNIT_HOLD (1 << UNIT_V_HOLD) #define IBUF buf /* input buffer */ #define OBUF wait /* output buffer */ #define DSOCKET u3 /* data socket */ #define LSOCKET u4 /* listening socket */ /* Debug flags */ #define DEB_CMDS (1 << 0) /* Command initiation and completion */ #define DEB_CPU (1 << 1) /* CPU I/O */ #define DEB_XFER (1 << 2) /* Socket receive and transmit */ extern DIB ptr_dib; /* need PTR select code for boot */ typedef enum { CIN, COUT } CARD; /* ipli/iplo selector */ int32 ipl_edtdelay = 1; /* EDT delay (msec) */ int32 ipl_ptime = 31; /* polling interval */ int32 ipl_stopioe = 0; /* stop on error */ int32 ipl_hold[2] = { 0 }; /* holding character */ FLIP_FLOP ipl_control [2] = { CLEAR, CLEAR }; FLIP_FLOP ipl_flag [2] = { CLEAR, CLEAR }; FLIP_FLOP ipl_flagbuf [2] = { CLEAR, CLEAR }; DEVICE ipli_dev, iplo_dev; uint32 iplio (uint32 select_code, IOSIG signal, uint32 data); t_stat ipl_svc (UNIT *uptr); t_stat ipl_reset (DEVICE *dptr); t_stat ipl_attach (UNIT *uptr, char *cptr); t_stat ipl_detach (UNIT *uptr); t_stat ipl_boot (int32 unitno, DEVICE *dptr); t_stat ipl_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ipl_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc); t_bool ipl_check_conn (UNIT *uptr); /* Debug flags table */ DEBTAB ipl_deb[] = { { "CMDS", DEB_CMDS }, { "CPU", DEB_CPU }, { "XFER", DEB_XFER }, { NULL, 0 } }; /* IPLI data structures ipli_dev IPLI device descriptor ipli_unit IPLI unit descriptor ipli_reg IPLI register list */ DIB ipl_dib[] = { { IPLI, &iplio }, { IPLO, &iplio } }; #define ipli_dib ipl_dib[0] #define iplo_dib ipl_dib[1] UNIT ipl_unit[] = { { UDATA (&ipl_svc, UNIT_ATTABLE, 0) }, { UDATA (&ipl_svc, UNIT_ATTABLE, 0) } }; #define ipli_unit ipl_unit[0] #define iplo_unit ipl_unit[1] REG ipli_reg[] = { { ORDATA (IBUF, ipli_unit.IBUF, 16) }, { ORDATA (OBUF, ipli_unit.OBUF, 16) }, { FLDATA (CTL, ipl_control [CIN], 0) }, { FLDATA (FLG, ipl_flag [CIN], 0) }, { FLDATA (FBF, ipl_flagbuf [CIN], 0) }, { ORDATA (HOLD, ipl_hold[CIN], 8) }, { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, ipl_stopioe, 0) }, { ORDATA (DEVNO, ipli_dib.devno, 6), REG_HRO }, { NULL } }; MTAB ipl_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", &ipl_setdiag }, { UNIT_DIAG, 0, "link mode", "LINK", &ipl_setdiag }, { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &ipl_dscln, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ipli_dev }, { 0 } }; DEVICE ipli_dev = { "IPLI", &ipli_unit, ipli_reg, ipl_mod, 1, 10, 31, 1, 16, 16, &tmxr_ex, &tmxr_dep, &ipl_reset, &ipl_boot, &ipl_attach, &ipl_detach, &ipli_dib, DEV_NET | DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, ipl_deb, NULL, NULL }; /* IPLO data structures iplo_dev IPLO device descriptor iplo_unit IPLO unit descriptor iplo_reg IPLO register list */ REG iplo_reg[] = { { ORDATA (IBUF, iplo_unit.IBUF, 16) }, { ORDATA (OBUF, iplo_unit.OBUF, 16) }, { FLDATA (CTL, ipl_control [COUT], 0) }, { FLDATA (FLG, ipl_flag [COUT], 0) }, { FLDATA (FBF, ipl_flagbuf [COUT], 0) }, { ORDATA (HOLD, ipl_hold[COUT], 8) }, { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, { ORDATA (DEVNO, iplo_dib.devno, 6), REG_HRO }, { NULL } }; DEVICE iplo_dev = { "IPLO", &iplo_unit, iplo_reg, ipl_mod, 1, 10, 31, 1, 16, 16, &tmxr_ex, &tmxr_dep, &ipl_reset, &ipl_boot, &ipl_attach, &ipl_detach, &iplo_dib, DEV_NET | DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, ipl_deb, NULL, NULL }; /* I/O signal handler for the IPLI and IPLO devices. In the link mode, the IPLI and IPLO devices are linked via network connections to the corresponding cards in another CPU instance. In the diagnostic mode, we simulate the attachment of the interprocessor cable between IPLI and IPLO in this machine. Implementation notes: 1. Because this routine is written to handle two devices, the flip-flops are stored in arrays, preventing the use of the "setstd" macros for PRL, IRQ, and SRQ signals. The logic for all three is standard, however. 2. 2000 Access has a race condition that manifests itself by an apparently normal boot and operational system console but no PLEASE LOG IN response to terminals connected to the multiplexer. The frequency of occurrence is higher on multiprocessor host systems, where the SP and IOP instances may execute concurrently. The cause is this code in the SP disc loader source (2883.asm, 7900.asm, 790X.asm, 79X3.asm, and 79XX.asm): LDA SDVTR REQUEST JSB IOPMA,I DEVICE TABLE [...] STC DMAHS,C TURN ON DMA SFS DMAHS WAIT FOR JMP *-1 DEVICE TABLE STC CH2,C SET CORRECT CLC CH2 FLAG DIRECTION The STC/CLC normally would cause a second "request device table" command to be recognized by the IOP, except that the IOP DMA setup routine "DMAXF" (in D61.asm) has specified an end-of-block CLC that holds off the IPL interrupt, and the completion interrupt routine "DMACP" ends with a STC,C that clears the IPL flag. In hardware, the two CPUs are essentially interlocked by the DMA transfer, and DMA completion interrupts occur almost simultaneously. Therefore, the STC/CLC in the SP is guaranteed to occur before the STC,C in the IOP. Under simulation, and especially on multiprocessor hosts, that guarantee does not hold. If the STC/CLC occurs after the STC,C, then the IOP starts a second device table DMA transfer, which the SP is not expecting. The IOP never processes the subsequent "start timesharing" command, and the muxtiplexer is non-reponsive. We employ a workaround that decreases the incidence of the problem: DMA output completion interrupts are delayed to allow the other SIMH instance a chance to process its own DMA completion. We do this by processing the EDT (End Data Transfer) I/O backplane signal and "sleep"ing for a short time if the transfer was an output transfer to the input channel, i.e., a data response to the SP. This improves the race condition by delaying the IOP until the SP has a chance to receive the last word, recognize its own DMA input completion, drop out of the SFS loop, and execute the STC/CLC. The condition is only improved, and not solved, because "sleep"ing the IOP doesn't guarantee that the SP will actually execute. It's possible that a higher-priority host process will preempt the SP, and that at the sleep expiration, the SP still has not executed the STC/CLC. Still, in testing, the incidence dropped dramatically, so the problem is much less intrusive. */ uint32 iplio (uint32 select_code, IOSIG signal, uint32 data) { const CARD card = (select_code == iplo_dib.devno); /* set card selector */ UNIT *const uptr = &(ipl_unit [card]); /* associated unit pointer */ const char uc = (card == CIN) ? 'I' : 'O'; /* identify unit for debug */ const DEVICE *dbdev = (card == CIN) ? &ipli_dev : &iplo_dev; /* identify device for debug */ const char *iotype[] = { "Status", "Command" }; const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 sta; char msg[2]; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ ipl_flag [card] = ipl_flagbuf [card] = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ ipl_flag [card] = ipl_flagbuf [card] = SET; break; case ioSFC: /* skip if flag is clear */ setSKF (!ipl_flag [card]); break; case ioSFS: /* skip if flag is set */ setSKF (ipl_flag [card]); break; case ioIOI: /* I/O data input */ data = uptr->IBUF; /* get return data */ if (DEBUG_PRJ (dbdev, DEB_CPU)) fprintf (sim_deb, ">>IPL%c LIx: %s = %06o\n", uc, iotype [card ^ 1], data); break; case ioIOO: /* I/O data output */ uptr->OBUF = data; if (DEBUG_PRJ (dbdev, DEB_CPU)) fprintf (sim_deb, ">>IPL%c OTx: %s = %06o\n", uc, iotype [card], data); break; case ioPOPIO: /* power-on preset to I/O */ ipl_flag [card] = ipl_flagbuf [card] = SET; /* set flag buffer and flag */ uptr->OBUF = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ ipl_control [card] = CLEAR; /* clear control */ if (DEBUG_PRJ (dbdev, DEB_CMDS)) fprintf (sim_deb, ">>IPL%c CRS: Control cleared\n", uc); break; case ioCLC: /* clear control flip-flop */ ipl_control [card] = CLEAR; /* clear ctl */ if (DEBUG_PRJ (dbdev, DEB_CMDS)) fprintf (sim_deb, ">>IPL%c CLC: Control cleared\n", uc); break; case ioSTC: /* set control flip-flop */ ipl_control [card] = SET; /* set ctl */ if (DEBUG_PRJ (dbdev, DEB_CMDS)) fprintf (sim_deb, ">>IPL%c STC: Control set\n", uc); if (uptr->flags & UNIT_ATT) { /* attached? */ if ((uptr->flags & UNIT_ESTB) == 0) /* established? */ if (!ipl_check_conn (uptr)) { /* not established? */ data = STOP_NOCONN << IOT_V_REASON; /* lose */ break; } msg[0] = (uptr->OBUF >> 8) & 0377; msg[1] = uptr->OBUF & 0377; sta = sim_write_sock (uptr->DSOCKET, msg, 2); if (DEBUG_PRJ (dbdev, DEB_XFER)) fprintf (sim_deb, ">>IPL%c STC: Socket write = %06o, status = %d\n", uc, uptr->OBUF, sta); if (sta == SOCKET_ERROR) { printf ("IPL: socket write error\n"); data = SCPE_IOERR << IOT_V_REASON; break; } sim_os_sleep (0); } else if (uptr->flags & UNIT_DIAG) { /* diagnostic mode? */ ipl_unit [card ^ 1].IBUF = uptr->OBUF; /* output to other */ iplio (ipl_dib [card ^ 1].devno, ioENF, 0); /* set other flag */ } else data = SCPE_UNATT << IOT_V_REASON; /* lose */ break; case ioEDT: /* end data transfer */ if ((cpu_unit.flags & UNIT_IOP) && /* are we the IOP? */ ((IOSIG) data == ioIOO) && (card == CIN)) { /* and doing output on input card? */ if (DEBUG_PRJ (dbdev, DEB_CMDS)) fprintf (sim_deb, ">>IPL%c EDT: Delaying DMA completion interrupt for %d msec\n", uc, ipl_edtdelay); sim_os_ms_sleep (ipl_edtdelay); /* delay completion */ } break; case ioSIR: /* set interrupt request */ setPRL (select_code, !(ipl_control [card] & ipl_flag [card])); setIRQ (select_code, ipl_control [card] & ipl_flag [card] & ipl_flagbuf [card]); setSRQ (select_code, ipl_flag [card]); break; case ioIAK: /* interrupt acknowledge */ ipl_flagbuf [card] = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ iplio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ iplio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service - poll for input */ t_stat ipl_svc (UNIT *uptr) { CARD card; int32 nb; char msg[2], uc; DEVICE *dbdev; /* device ptr for debug */ if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* not attached? */ sim_activate (uptr, ipl_ptime); /* reactivate */ if ((uptr->flags & UNIT_ESTB) == 0) /* not established? */ if (!ipl_check_conn (uptr)) /* check for conn */ return SCPE_OK; /* cot connected */ nb = sim_read_sock (uptr->DSOCKET, msg, ((uptr->flags & UNIT_HOLD)? 1: 2)); if (nb < 0) { /* connection closed? */ printf ("IPL: socket read error\n"); return SCPE_IOERR; } if (nb == 0) return SCPE_OK; /* no data? */ card = (uptr == &iplo_unit); /* set card selector */ if (uptr->flags & UNIT_HOLD) { /* holdover byte? */ uptr->IBUF = (ipl_hold[card] << 8) | (((int32) msg[0]) & 0377); uptr->flags = uptr->flags & ~UNIT_HOLD; } else if (nb == 1) { ipl_hold[card] = ((int32) msg[0]) & 0377; uptr->flags = uptr->flags | UNIT_HOLD; } else uptr->IBUF = ((((int32) msg[0]) & 0377) << 8) | (((int32) msg[1]) & 0377); iplio (ipl_dib [card].devno, ioENF, 0); /* set flag */ uc = (card == CIN) ? 'I' : 'O'; /* identify unit for debug */ dbdev = (card == CIN) ? &ipli_dev : &iplo_dev; /* identify device for debug */ if (DEBUG_PRJ (dbdev, DEB_XFER)) fprintf (sim_deb, ">>IPL%c svc: Socket read = %06o, status = %d\n", uc, uptr->IBUF, nb); return SCPE_OK; } t_bool ipl_check_conn (UNIT *uptr) { SOCKET sock; if (uptr->flags & UNIT_ESTB) return TRUE; /* established? */ if (uptr->flags & UNIT_ACTV) { /* active connect? */ if (sim_check_conn (uptr->DSOCKET, 0) <= 0) return FALSE; } else { sock = sim_accept_conn (uptr->LSOCKET, NULL); /* poll connect */ if (sock == INVALID_SOCKET) return FALSE; /* got a live one? */ uptr->DSOCKET = sock; /* save data socket */ } uptr->flags = uptr->flags | UNIT_ESTB; /* conn established */ return TRUE; } /* Reset routine. Implementation notes: 1. We set up the first poll for socket connections to occur "immediately" upon execution, so that clients will be connected before execution begins. Otherwise, a fast program may access the IPL before the poll service routine activates. */ t_stat ipl_reset (DEVICE *dptr) { UNIT *uptr = dptr->units; hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &ipli_dev)? &iplo_dev: &ipli_dev); if (sim_switches & SWMASK ('P')) /* PON reset? */ uptr->IBUF = uptr->OBUF = 0; /* clr buffers */ if (dptr == &ipli_dev) /* input channel reset? */ iplio (ipli_dib.devno, ioPOPIO, 0); /* send POPIO signal */ else /* output channel reset */ iplio (iplo_dib.devno, ioPOPIO, 0); /* send POPIO signal */ if (uptr->flags & UNIT_ATT) /* socket attached? */ sim_activate (uptr, POLL_FIRST); /* activate first poll "immediately" */ else sim_cancel (uptr); /* deactivate unit */ uptr->flags = uptr->flags & ~UNIT_HOLD; /* clear holding flag */ return SCPE_OK; } /* Attach routine attach -l - listen for connection on port attach -c - connect to ip address and port */ t_stat ipl_attach (UNIT *uptr, char *cptr) { SOCKET newsock; uint32 i, t, ipa, ipp, oldf; char *tptr; t_stat r; r = get_ipaddr (cptr, &ipa, &ipp); if ((r != SCPE_OK) || (ipp == 0)) return SCPE_ARG; oldf = uptr->flags; if (oldf & UNIT_ATT) ipl_detach (uptr); if ((sim_switches & SWMASK ('C')) || ((sim_switches & SIM_SW_REST) && (oldf & UNIT_ACTV))) { if (ipa == 0) ipa = 0x7F000001; newsock = sim_connect_sock (ipa, ipp); if (newsock == INVALID_SOCKET) return SCPE_IOERR; printf ("Connecting to IP address %d.%d.%d.%d, port %d\n", (ipa >> 24) & 0xff, (ipa >> 16) & 0xff, (ipa >> 8) & 0xff, ipa & 0xff, ipp); if (sim_log) fprintf (sim_log, "Connecting to IP address %d.%d.%d.%d, port %d\n", (ipa >> 24) & 0xff, (ipa >> 16) & 0xff, (ipa >> 8) & 0xff, ipa & 0xff, ipp); uptr->flags = uptr->flags | UNIT_ACTV; uptr->LSOCKET = 0; uptr->DSOCKET = newsock; } else { if (ipa != 0) return SCPE_ARG; newsock = sim_master_sock (ipp); if (newsock == INVALID_SOCKET) return SCPE_IOERR; printf ("Listening on port %d\n", ipp); if (sim_log) fprintf (sim_log, "Listening on port %d\n", ipp); uptr->flags = uptr->flags & ~UNIT_ACTV; uptr->LSOCKET = newsock; uptr->DSOCKET = 0; } uptr->IBUF = uptr->OBUF = 0; uptr->flags = (uptr->flags | UNIT_ATT) & ~(UNIT_ESTB | UNIT_HOLD); tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ if (tptr == NULL) { /* no memory? */ ipl_detach (uptr); /* close sockets */ return SCPE_MEM; } strcpy (tptr, cptr); /* copy ipaddr:port */ uptr->filename = tptr; /* save */ sim_activate (uptr, POLL_FIRST); /* activate first poll "immediately" */ if (sim_switches & SWMASK ('W')) { /* wait? */ for (i = 0; i < 30; i++) { /* check for 30 sec */ if (t = ipl_check_conn (uptr)) break; /* established? */ if ((i % 10) == 0) /* status every 10 sec */ printf ("Waiting for connnection\n"); sim_os_sleep (1); /* sleep 1 sec */ } if (t) printf ("Connection established\n"); } return SCPE_OK; } /* Detach routine */ t_stat ipl_detach (UNIT *uptr) { if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ if (uptr->flags & UNIT_ACTV) sim_close_sock (uptr->DSOCKET, 1); else { if (uptr->flags & UNIT_ESTB) /* if established, */ sim_close_sock (uptr->DSOCKET, 0); /* close data socket */ sim_close_sock (uptr->LSOCKET, 1); /* closen listen socket */ } free (uptr->filename); /* free string */ uptr->filename = NULL; uptr->LSOCKET = 0; uptr->DSOCKET = 0; uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_ACTV | UNIT_ESTB); sim_cancel (uptr); /* don't poll */ return SCPE_OK; } /* Disconnect routine */ t_stat ipl_dscln (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if (((uptr->flags & UNIT_ATT) == 0) || (uptr->flags & UNIT_ACTV) || ((uptr->flags & UNIT_ESTB) == 0)) return SCPE_NOFNC; sim_close_sock (uptr->DSOCKET, 0); uptr->DSOCKET = 0; uptr->flags = uptr->flags & ~UNIT_ESTB; return SCPE_OK; } /* Diagnostic/normal mode routine */ t_stat ipl_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val) { ipli_unit.flags = ipli_unit.flags | UNIT_DIAG; iplo_unit.flags = iplo_unit.flags | UNIT_DIAG; } else { ipli_unit.flags = ipli_unit.flags & ~UNIT_DIAG; iplo_unit.flags = iplo_unit.flags & ~UNIT_DIAG; } return SCPE_OK; } /* Interprocessor link bootstrap routine (HP Access Manual) */ #define MAX_BASE 073 #define IPL_PNTR 074 #define PTR_PNTR 075 #define IPL_DEVA 076 #define PTR_DEVA 077 static const BOOT_ROM ipl_rom = { 0163774, /*BBL LDA ICK,I ; IPL sel code */ 0027751, /* JMP CFG ; go configure */ 0107700, /*ST CLC 0,C ; intr off */ 0002702, /* CLA,CCE,SZA ; skip in */ 0063772, /*CN LDA M26 ; feed frame */ 0002307, /*EOC CCE,INA,SZA,RSS ; end of file? */ 0027760, /* JMP EOT ; yes */ 0017736, /* JSB READ ; get #char */ 0007307, /* CMB,CCE,INB,SZB,RSS ; 2's comp; null? */ 0027705, /* JMP EOC ; read next */ 0077770, /* STB WC ; word in rec */ 0017736, /* JSB READ ; get feed frame */ 0017736, /* JSB READ ; get address */ 0074000, /* STB 0 ; init csum */ 0077771, /* STB AD ; save addr */ 0067771, /*CK LDB AD ; check addr */ 0047773, /* ADB MAXAD ; below loader */ 0002040, /* SEZ ; E =0 => OK */ 0102055, /* HLT 55 */ 0017736, /* JSB READ ; get word */ 0040001, /* ADA 1 ; cont checksum */ 0177771, /* STB AD,I ; store word */ 0037771, /* ISZ AD */ 0000040, /* CLE ; force wd read */ 0037770, /* ISZ WC ; block done? */ 0027717, /* JMP CK ; no */ 0017736, /* JSB READ ; get checksum */ 0054000, /* CPB 0 ; ok? */ 0027704, /* JMP CN ; next block */ 0102011, /* HLT 11 ; bad csum */ 0000000, /*RD 0 */ 0006600, /* CLB,CME ; E reg byte ptr */ 0103700, /*IO1 STC RDR,C ; start reader */ 0102300, /*IO2 SFS RDR ; wait */ 0027741, /* JMP *-1 */ 0106400, /*IO3 MIB RDR ; get byte */ 0002041, /* SEZ,RSS ; E set? */ 0127736, /* JMP RD,I ; no, done */ 0005767, /* BLF,CLE,BLF ; shift byte */ 0027740, /* JMP IO1 ; again */ 0163775, /* LDA PTR,I ; get ptr code */ 0043765, /*CFG ADA SFS ; config IO */ 0073741, /* STA IO2 */ 0043766, /* ADA STC */ 0073740, /* STA IO1 */ 0043767, /* ADA MIB */ 0073743, /* STA IO3 */ 0027702, /* JMP ST */ 0063777, /*EOT LDA PSC ; put select codes */ 0067776, /* LDB ISC ; where xloader wants */ 0102077, /* HLT 77 */ 0027702, /* JMP ST */ 0000000, /* NOP */ 0102300, /*SFS SFS 0 */ 0001400, /*STC 1400 */ 0002500, /*MIB 2500 */ 0000000, /*WC 0 */ 0000000, /*AD 0 */ 0177746, /*M26 -26 */ 0000000, /*MAX -BBL */ 0007776, /*ICK ISC */ 0007777, /*PTR IPT */ 0000000, /*ISC 0 */ 0000000 /*IPT 0 */ }; t_stat ipl_boot (int32 unitno, DEVICE *dptr) { const int32 devi = ipli_dib.devno; const int32 devp = ptr_dib.devno; ibl_copy (ipl_rom, devi); /* copy bootstrap to memory */ SR = (devi << IBL_V_DEV) | devp; /* set SR */ WritePW (PC + MAX_BASE, (~PC + 1) & DMASK); /* fix ups */ WritePW (PC + IPL_PNTR, ipl_rom[IPL_PNTR] | PC); WritePW (PC + PTR_PNTR, ipl_rom[PTR_PNTR] | PC); WritePW (PC + IPL_DEVA, devi); WritePW (PC + PTR_DEVA, devp); return SCPE_OK; } simh-3.8.1/HP2100/hp2100_cpu6.c0000644000175000017500000012361211064502046013471 0ustar vlmvlm/* hp2100_cpu6.c: HP 1000 RTE-6/VM OS instructions Copyright (c) 2006-2008, J. David Bryan 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 AUTHOR 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 author. CPU6 RTE-6/VM OS instructions 18-Sep-08 JDB Corrected .SIP debug formatting 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 26-Jun-08 JDB Rewrote device I/O to model backplane signals 27-Nov-07 JDB Implemented OS instructions 26-Sep-06 JDB Created Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" /* external variables */ extern jmp_buf save_env; /* MP abort handler */ /* Offsets to data and addresses within RTE. */ static const uint32 xi = 0001647; /* XI address */ static const uint32 intba = 0001654; /* INTBA address */ static const uint32 intlg = 0001655; /* INTLG address */ static const uint32 eqt1 = 0001660; /* EQT1 address */ static const uint32 eqt11 = 0001672; /* EQT11 address */ static const uint32 pvcn = 0001712; /* PVCN address */ static const uint32 xsusp = 0001730; /* XSUSP address */ static const uint32 dummy = 0001737; /* DUMMY address */ static const uint32 mptfl = 0001770; /* MPTFL address */ static const uint32 eqt12 = 0001771; /* EQT12 address */ static const uint32 eqt15 = 0001774; /* EQT15 address */ static const uint32 vctr = 0002000; /* VCTR address */ static const uint32 CLC_0 = 0004700; /* CLC 0 instruction */ static const uint32 STC_0 = 0000700; /* STC 0 instruction */ static const uint32 CLF_0 = 0001100; /* CLF 0 instruction */ static const uint32 STF_0 = 0000100; /* STF 0 instruction */ static const uint32 SFS_0_C = 0003300; /* SFS 0,C instruction */ enum vctr_offsets { dms_offset = 0, /* DMS status */ int_offset, /* interrupt system status */ sc_offset, /* select code */ clck_offset, /* TBG IRQ handler */ cic4_offset, /* illegal IRQ handler */ cic2_offset, /* device IRQ handler */ sked_offset, /* prog sched IRQ handler */ rqst_offset, /* EXEC request handler */ cic_offset, /* IRQ location */ perr_offset, /* parity error IRQ handler */ mper_offset, /* memory protect IRQ handler */ lxnd_offset }; /* $LIBR return */ /* RTE-6/VM Operating System Instructions The OS instructions were added to acccelerate certain time-consuming operations of the RTE-6/VM operating system, HP product number 92084A. Microcode was available for the E- and F-Series; the M-Series used software equivalents. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A 92084A 92084A The routines are mapped to instruction codes as follows: Instr. 1000-E/F Description ------ -------- ---------------------------------------------- $LIBR 105340 Enter privileged/reentrant library routine $LIBX 105341 Exit privileged/reentrant library routine .TICK 105342 TBG tick interrupt handler .TNAM 105343 Find ID segment that matches name .STIO 105344 Configure I/O instructions .FNW 105345 Find word with user increment .IRT 105346 Interrupt return processing .LLS 105347 Linked list search .SIP 105350 Skip if interrupt pending .YLD 105351 .SIP completion return point .CPM 105352 Compare words LT/EQ/GT .ETEQ 105353 Set up EQT pointers in base page .ENTN 105354 Transfer parameter addresses (utility) $OTST * 105355 OS firmware self test .ENTC 105356 Transfer parameter addresses (priv/reent) .DSPI 105357 Set display indicator Opcodes 105354-105357 are "dual use" instructions that take different actions, depending on whether they are executed from a trap cell during an interrupt. When executed from a trap cell, they have these actions: Instr. 1000-E/F Description ------ -------- ---------------------------------------------- $DCPC * 105354 DCPC channel interrupt processing $MPV * 105355 MP/DMS/PE interrupt processing $DEV * 105356 Standard device interrupt processing $TBG * 105357 TBG interrupt processing * These mnemonics are recognized by symbolic examine/deposit but are not official HP mnemonics. Implementation notes: 1. The microcode differentiates between interrupt processing and normal execution of the "dual use" instructions by testing the CPU flag. Interrupt vectoring sets the flag; a normal instruction fetch clears it. Under simulation, interrupt vectoring is indicated by the value of the "iotrap" parameter (0 = normal instruction, 1 = trap cell instruction). 2. The operand patterns for .ENTN and .ENTC normally would be coded as "OP_A", as each takes a single address as a parameter. However, because they might also be executed from a trap cell, we cannot assume that P+1 is an address, or we might cause a DM abort when trying to resolve indirects. Therefore, "OP_A" handling is done within each routine, once the type of use is determined. 3. The microcode for .ENTC, .ENTN, .FNW, .LLS, .TICK, and .TNAM explicitly checks for interrupts during instruction execution. In addition, the .STIO, .CPM, and .LLS instructions implicitly check for interrupts during parameter indirect resolution. Because the simulator calculates interrupt requests only between instructions, this behavior is not simulated. 4. The microcode executes certain I/O instructions (e.g., CLF 0) by building the instruction in the IR and executing an IOG micro-order. We simulate this behavior by calling the "iogrp" handler with the appropriate instruction, rather than manipulating the I/O system directly, so that we will remain unaffected by any future changes to the underlying I/O simulation structure. 5. The $OTST and .DSPI microcode uses features (reading the RPL switches and boot loader ROM data, loading the display register) that are not simulated. The remaining functions of the $OTST instruction are provided. The .DSPI instruction is a NOP or unimplemented instruction stop. 6. Because of the volume of calls to the OS firmware, debug printouts attempt to write only one line per instruction invocation. This means that calling and returned register values are printed separately, with a newline added at the end of execution. However, many instructions can MP or DM abort, either intentionally or due to improper use. That would leave debug lines without the required trailing newlines. There are two ways to address this: either we could replace the CPU's setjmp buffer with one that points to a routine that adds the missing newline, or we can add a semaphore that is tested on entry to see if it is already set, implying a longjmp occurred, and then add the newline if so. The latter would add the newline when the MP trap cell instruction was entered or when the next user-level instruction was executed. However, the merged-line problem would still exist if some other module generated intervening debug printouts. So we do the former. This does mean that this routine must be changed if the MP abort mechanism is changed. 7. The $LIBX instruction is executed to complete either a privileged or reentrant execution. In the former case, the privileged nest counter ($PVCN) is decremented. In the latter, $PVCN decrement is attempted but the write will trap with an MP violation, as reentrant routines execute with the interrupt system on. RTE will then complete the release of memory allocated for the original $LIBR call. 8. The documentation for the .SIP and .YLD instructions is misleading in several places. Comments in the RTE $SIP source file say that .SIP doesn't return if a "known" interrupt is pending. Actually, .SIP always returns, either to P+1 for no pending interrupt, or to P+2 if one is pending. There is no check for "known" interrupt handlers. The microcode source comments say that the interrupting select code is returned in the B register. Actually, the B register is unchanged. The RTE Tech Specs say that .SIP "services any pending system interrupts." Actually, .SIP only checks for interrupts; no servicing is performed. For .YLD, the microcode comments say that two parameters are passed: the new P value, and the interrupting select code. Actually, only the new P value is passed. The .SIP and .YLD simulations follow the actual microcode rather than the documentation. Additional references: - RTE-6/VM OS Microcode Source (92084-18831, revision 8). - RTE-6/VM Technical Specifications (92084-90015, Apr-1983). */ /* Save the CPU registers. The CPU registers are saved in the current ID segment in preparation for interrupt handling. Although the RTE base page has separate pointers for the P, A, B, and E/O registers, they are always contiguous, and the microcode simply increments the P-register pointer (XSUSP) to store the remaining values. This routine is called from the trap cell interrupt handlers and from the $LIBX processor. In the latter case, the privileged system interrupt handling is not required, so it is bypassed. In either case, the current map will be the system map when we are called. */ static t_stat cpu_save_regs (uint32 iotrap) { uint16 save_area, priv_fence; t_stat reason = SCPE_OK; save_area = ReadW (xsusp); /* addr of PABEO save area */ WriteW (save_area + 0, PC); /* save P */ WriteW (save_area + 1, AR); /* save A */ WriteW (save_area + 2, BR); /* save B */ WriteW (save_area + 3, (E << 15) & SIGN | O & 1); /* save E and O */ save_area = ReadW (xi); /* addr of XY save area */ WriteWA (save_area + 0, XR); /* save X (in user map) */ WriteWA (save_area + 1, YR); /* save Y (in user map) */ if (iotrap) { /* do priv setup only if IRQ */ priv_fence = ReadW (dummy); /* get priv fence select code */ if (priv_fence) { /* privileged system? */ reason = iogrp (STC_0 + priv_fence, iotrap); /* STC SC on priv fence */ reason = iogrp (CLC_0 + DMA0, iotrap); /* CLC 6 to inh IRQ on DCPC 0 */ reason = iogrp (CLC_0 + DMA1, iotrap); /* CLC 7 to inh IRQ on DCPC 1 */ reason = iogrp (STF_0, iotrap); /* turn interrupt system back on */ } } return reason; } /* Save the machine state at interrupt. This routine is called from each of the trap cell instructions. Its purpose is to save the complete state of the machine in preparation for interrupt handling. For the MP/DMS/PE interrupt, the interrupting device must not be cleared and the CPU registers must not be saved until it is established that the interrupt is not caused by a parity error. Parity errors cannot be inhibited, so the interrupt may have occurred while in RTE. Saving the registers would overwrite the user's registers that were saved at RTE entry. Note that the trap cell instructions are dual-use and invoke this routine only when they are executed during interrupts. Therefore, the current map will always be the system map when we are called. */ static t_stat cpu_save_state (uint32 iotrap) { uint16 vectors; uint32 saved_PC, int_sys_off; t_stat reason; saved_PC = PC; /* save current PC */ reason = iogrp (SFS_0_C, iotrap); /* turn interrupt system off */ int_sys_off = (PC == saved_PC); /* set flag if already off */ PC = saved_PC; /* restore PC in case it bumped */ vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ WriteW (vectors + dms_offset, dms_upd_sr ()); /* save DMS status (SSM) */ WriteW (vectors + int_offset, int_sys_off); /* save int status */ WriteW (vectors + sc_offset, intaddr); /* save select code */ WriteW (mptfl, 1); /* show MP is off */ if (intaddr != 5) { /* only if not MP interrupt */ reason = iogrp (CLF_0 + intaddr, iotrap); /* issue CLF to device */ cpu_save_regs (iotrap); /* save CPU registers */ } return reason; } /* Get the interrupt table entry corresponding to a select code. Return the word in the RTE interrupt table that corresponds to the interrupting select code. Return 0 if the select code is beyond the end of the table. */ uint32 cpu_get_intbl (uint32 select_code) { uint16 interrupt_table; /* interrupt table (starts with SC 06) */ uint16 table_length; /* length of interrupt table */ interrupt_table = ReadW (intba); /* get int table address */ table_length = ReadW (intlg); /* get int table length */ if (select_code - 6 > table_length) /* SC beyond end of table? */ return 0; /* return 0 for illegal interrupt */ else return ReadW (interrupt_table + select_code - 6); /* else return table entry */ } /* RTE-6/VM OS instruction dispatcher. Debugging printouts are provided with the OS and OSTBG debug flags. The OS flag enables tracing for all instructions except for the three-instruction sequence executed for the time-base generator interrupt ($TBG, .TICK, and .IRT). The OSTBG flag enables tracing for just the TBG sequence. The flags are separate, as the TBG generates 100 interrupts per second. Use caution when specifying the OSTBG flag, as the debug output file will grow rapidly. Note that the OS flag enables the .IRT instruction trace for all cases except a TBG interrupt. The default (user microcode) dispatcher will allow the firmware self-test instruction (105355) to execute as NOP. This is because RTE-6/VM will always test for the presence of OS and VMA firmware on E/F-Series machines. If the firmware is not present, then these instructions will return to P+1, and RTE will then HLT 21. This means that RTE-6/VM will not run on an E/F-Series machine without the OS and VMA firmware. Howwever, RTE allows the firmware instructions to be disabled for debugging purposes. If the firmware is present and returns to P+2 but sets the X register to 0, then RTE will use software equivalents. We enable this condition when the OS firmware is enabled (SET CPU VMA), the OS debug flag is set (SET CPU DEBUG=OS), but debug output has been disabled (SET CONSOLE NODEBUG). That is: OS Debug Firmware Debug Output Tracing Self-Test Instruction ======== ===== ====== ======= ===================== disabled x x off NOP enabled clear x off X = revision code enabled set off off X = 0 enabled set on on X = revision code */ static const OP_PAT op_os[16] = { OP_A, OP_A, OP_N, OP_N, /* $LIBR $LIBX .TICK .TNAM */ OP_A, OP_K, OP_A, OP_KK, /* .STIO .FNW .IRT .LLS */ OP_N, OP_C, OP_KK, OP_N, /* .SIP .YLD .CPM .ETEQ */ OP_N, OP_N, OP_N, OP_N /* .ENTN $OTST .ENTC .DSPI */ }; t_stat cpu_rte_os (uint32 IR, uint32 intrq, uint32 iotrap) { t_stat reason = SCPE_OK; OPS op; OP_PAT pattern; uint32 entry, count, cp, sa, da, i, ma; uint16 vectors, save_area, priv_fence, eoreg, eqt, key; char test[6], target[6]; jmp_buf mp_handler; int abortval; t_bool debug_print; static t_bool tbg_tick = FALSE; /* set if processing TBG interrupt */ entry = IR & 017; /* mask to entry point */ pattern = op_os[entry]; /* get operand pattern */ if (pattern != OP_N) if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ return reason; tbg_tick = tbg_tick || (IR == 0105357) && iotrap; /* set TBG interrupting flag */ debug_print = (DEBUG_PRI (cpu_dev, DEB_OS) && !tbg_tick) || (DEBUG_PRI (cpu_dev, DEB_OSTBG) && tbg_tick); if (debug_print) { fprintf (sim_deb, ">>CPU OS: IR = %06o (", IR); /* print preamble and IR */ fprint_sym (sim_deb, (iotrap ? intaddr : err_PC), /* print instruction mnemonic */ (t_value *) &IR, NULL, SWMASK('M')); fputc (')', sim_deb); fprint_ops (pattern, op); /* print operands */ memcpy (mp_handler, save_env, sizeof (jmp_buf)); /* save original MP handler */ abortval = setjmp (save_env); /* set new MP abort handler */ if (abortval != 0) { /* MP abort? */ fputs ("...MP abort\n", sim_deb); /* report it and terminate line */ memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ longjmp (save_env, abortval); /* transfer to MP handler */ } } switch (entry) { /* decode IR<3:0> */ case 000: /* $LIBR 105340 (OP_A) */ if ((op[0].word != 0) || /* reentrant call? */ (mp_control && (ReadW (dummy) != 0))) { /* or priv call + MP on + priv sys? */ if (dms_ump) { /* called from user map? */ dms_viol (err_PC, MVI_PRV); /* privilege violation */ } dms_ump = SMAP; /* set system map */ vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ PC = ReadW (vectors + mper_offset); /* vector to $MPER for processing */ } else { /* privileged call */ if (mp_control) { /* memory protect on? */ mp_control = CLEAR; /* turn it off */ reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ WriteW (mptfl, 1); /* show MP is off */ save_area = ReadW (xsusp); /* get addr of P save area */ if (dms_ump) /* user map current? */ WriteWA (save_area, (PC - 2) & VAMASK); /* set point of suspension */ else /* system map current */ WriteW (save_area, (PC - 2) & VAMASK); /* set point of suspension */ } WriteW (pvcn, (ReadW (pvcn) + 1) & DMASK); /* increment priv nest counter */ } break; case 001: /* $LIBX 105341 (OP_A) */ PC = ReadW (op[0].word); /* set P to return point */ count = (ReadW (pvcn) - 1) & DMASK; /* decrement priv nest counter */ WriteW (pvcn, count); /* write it back */ if (count == 0) { /* end of priv mode? */ dms_ump = SMAP; /* set system map */ reason = cpu_save_regs (iotrap); /* save registers */ vectors = ReadW (vctr); /* get address of vectors */ PC = ReadW (vectors + lxnd_offset); /* vector to $LXND for processing */ } break; case 002: /* .TICK 105342 (OP_N) */ if (debug_print) /* debugging? */ fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ do { eqt = (ReadW (AR) + 1) & DMASK; /* bump timeout from EQT15 */ if (eqt != 1) { /* was timeout active? */ WriteW (AR, eqt); /* yes, write it back */ if (eqt == 0) /* did timeout expire? */ break; /* P+0 return for timeout */ } AR = (AR + 15) & DMASK; /* point at next EQT15 */ BR = (BR - 1) & DMASK; /* decrement count of EQTs */ } while ((BR > 0) && (eqt != 0)); /* loop until timeout or done */ if (BR == 0) /* which termination condition? */ PC = (PC + 1) & VAMASK; /* P+1 return for no timeout */ if (debug_print) /* debugging? */ fprint_regs ("; result:", /* print return registers */ REG_A | REG_B | REG_P_REL, err_PC + 1); break; case 003: /* .TNAM 105343 (OP_N) */ if (debug_print) /* debugging? */ fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ E = 1; /* preset flag for not found */ cp = (BR << 1) & DMASK; /* form char addr (B is direct) */ for (i = 0; i < 5; i++) { /* copy target name */ target[i] = (char) ReadB (cp); /* name is only 5 chars */ cp = (cp + 1) & DMASK; } if ((target[0] == '\0') && (target[1] == '\0')) /* if name is null, */ break; /* return immed to P+0 */ key = ReadW (AR); /* get first keyword addr */ while (key != 0) { /* end of keywords? */ cp = ((key + 12) << 1) & DMASK; /* form char addr of name */ for (i = 0; i < 6; i++) { /* copy test name */ test[i] = (char) ReadB (cp); /* name is only 5 chars */ cp = (cp + 1) & DMASK; /* but copy 6 to get flags */ } if (strncmp (target, test, 5) == 0) { /* names match? */ AR = (key + 15) & DMASK; /* A = addr of IDSEG [15] */ BR = key; /* B = addr of IDSEG [0] */ E = (uint32) ((test[5] >> 4) & 1); /* E = short ID segment bit */ PC = (PC + 1) & VAMASK; /* P+1 for found return */ break; } AR = (AR + 1) & DMASK; /* bump to next keyword */ key = ReadW (AR); /* get next keyword */ }; if (debug_print) /* debugging? */ fprint_regs ("; result:", /* print return registers */ REG_A | REG_B | REG_E | REG_P_REL, err_PC + 1); break; case 004: /* .STIO 105344 (OP_A) */ count = op[0].word - PC; /* get count of operands */ if (debug_print) /* debugging? */ fprintf (sim_deb, /* print registers on entry */ ", A = %06o, count = %d", AR, count); for (i = 0; i < count; i++) { ma = ReadW (PC); /* get operand address */ if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */ PC = err_PC; /* IRQ restarts instruction */ break; } WriteW (ma, ReadW (ma) & ~I_DEVMASK | AR); /* set SC into instruction */ PC = (PC + 1) & VAMASK; /* bump to next */ } break; case 005: /* .FNW 105345 (OP_K) */ if (debug_print) /* debugging? */ fprint_regs (",", REG_A | REG_B | REG_X, 0); /* print entry registers */ while (XR != 0) { /* all comparisons done? */ key = ReadW (BR); /* read a buffer word */ if (key == AR) { /* does it match? */ PC = (PC + 1) & VAMASK; /* P+1 found return */ break; } BR = (BR + op[0].word) & DMASK; /* increment buffer ptr */ XR = (XR - 1) & DMASK; /* decrement remaining count */ } /* P+0 not found return */ if (debug_print) /* debugging? */ fprint_regs ("; result:", /* print return registers */ REG_A | REG_B | REG_X | REG_P_REL, err_PC + 2); break; case 006: /* .IRT 105346 (OP_A) */ save_area = ReadW (xsusp); /* addr of PABEO save area */ WriteW (op[0].word, ReadW (save_area + 0)); /* restore P to DEF RTN */ AR = ReadW (save_area + 1); /* restore A */ BR = ReadW (save_area + 2); /* restore B */ eoreg = ReadW (save_area + 3); /* get combined E and O */ E = (eoreg >> 15) & 1; /* restore E */ O = eoreg & 1; /* restore O */ save_area = ReadW (xi); /* addr of XY save area */ XR = ReadWA (save_area + 0); /* restore X (from user map) */ YR = ReadWA (save_area + 1); /* restore Y (from user map) */ reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ WriteW (mptfl, 0); /* show MP is on */ priv_fence = ReadW (dummy); /* get priv fence select code */ if (priv_fence) { /* privileged system? */ reason = iogrp (CLC_0 + priv_fence, iotrap); /* CLC SC on priv fence */ reason = iogrp (STF_0 + priv_fence, iotrap); /* STF SC on priv fence */ if (cpu_get_intbl (DMA0) & SIGN) /* DCPC 0 active? */ reason = iogrp (STC_0 + DMA0, iotrap); /* STC 6 to enable IRQ on DCPC 0 */ if (cpu_get_intbl (DMA1) & SIGN) /* DCPC 1 active? */ reason = iogrp (STC_0 + DMA1, iotrap); /* STC 7 to enable IRQ on DCPC 1 */ } tbg_tick = 0; /* .IRT terminates TBG servicing */ break; case 007: /* .LLS 105347 (OP_KK) */ if (debug_print) /* debugging? */ fprint_regs (",", REG_A | REG_B | REG_E, 0); /* print entry registers */ AR = AR & ~SIGN; /* clear sign bit of A */ while ((AR != 0) && ((AR & SIGN) == 0)) { /* end of list or bad list? */ key = ReadW ((AR + op[1].word) & VAMASK); /* get key value */ if ((E == 0) && (key == op[0].word) || /* for E = 0, key = arg? */ (E != 0) && (key > op[0].word)) /* for E = 1, key > arg? */ break; /* search is done */ BR = AR; /* B = last link */ AR = ReadW (AR); /* A = next link */ } if (AR == 0) /* exhausted list? */ PC = (PC + 1) & VAMASK; /* P+1 arg not found */ else if ((AR & SIGN) == 0) /* good link? */ PC = (PC + 2) & VAMASK; /* P+2 arg found */ /* P+0 bad link */ if (debug_print) /* debugging? */ fprint_regs ("; result:", /* print return registers */ REG_A | REG_B | REG_P_REL, err_PC + 3); break; case 010: /* .SIP 105350 (OP_N) */ reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ intrq = calc_int (); /* check for interrupt requests */ reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ if (intrq) /* was interrupt pending? */ PC = (PC + 1) & VAMASK; /* P+1 return for pending IRQ */ /* P+0 return for no pending IRQ */ if (debug_print) /* debugging? */ fprintf (sim_deb, /* print return registers */ ", CIR = %02o, return = P+%d", intrq, PC - (err_PC + 1)); break; case 011: /* .YLD 105351 (OP_C) */ PC = op[0].word; /* pick up point of resumption */ reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ ion_defer = 0; /* kill defer so irq occurs immed */ break; case 012: /* .CPM 105352 (OP_KK) */ if (INT16 (op[0].word) > INT16 (op[1].word)) PC = (PC + 2) & VAMASK; /* P+2 arg1 > arg2 */ else if (INT16 (op[0].word) < INT16 (op[1].word)) PC = (PC + 1) & VAMASK; /* P+1 arg1 < arg2 */ /* P+0 arg1 = arg2 */ if (debug_print) /* debugging? */ fprint_regs (",", REG_P_REL, err_PC + 3); /* print return registers */ break; case 013: /* .ETEQ 105353 (OP_N) */ eqt = ReadW (eqt1); /* get addr of EQT1 */ if (AR != eqt) { /* already set up? */ for (eqt = eqt1; eqt <= eqt11; eqt++) /* init EQT1-EQT11 */ WriteW (eqt & VAMASK, (AR++ & DMASK)); for (eqt = eqt12; eqt <= eqt15; eqt++) /* init EQT12-EQT15 */ WriteW (eqt & VAMASK, (AR++ & DMASK)); /* (not contig with EQT1-11) */ } if (debug_print) /* debugging? */ fprintf (sim_deb, /* print return registers */ ", A = %06o, EQT1 = %06o", AR, eqt); break; case 014: /* .ENTN/$DCPC 105354 (OP_N) */ if (iotrap) { /* in trap cell? */ reason = cpu_save_state (iotrap); /* DMA interrupt */ AR = cpu_get_intbl (intaddr) & ~SIGN; /* get intbl value and strip sign */ goto DEVINT; /* vector by intbl value */ } else { /* .ENTN instruction */ ma = (PC - 2) & VAMASK; /* get addr of entry point */ ENTX: /* enter here from .ENTC */ reason = cpu_ops (OP_A, op, intrq); /* get instruction operand */ da = op[0].word; /* get addr of 1st formal */ count = ma - da; /* get count of formals */ sa = ReadW (ma); /* get addr of 1st actual */ WriteW (ma, (sa + count) & VAMASK); /* adjust return point to skip actuals */ if (debug_print) /* debugging? */ fprintf (sim_deb, /* print entry registers */ ", op [0] = %06o, pcount = %d", da, count); for (i = 0; i < count; i++) { /* parameter loop */ ma = ReadW (sa); /* get addr of actual */ sa = (sa + 1) & VAMASK; /* increment address */ if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */ PC = err_PC; /* irq restarts instruction */ break; } WriteW (da, ma); /* put addr into formal */ da = (da + 1) & VAMASK; /* increment address */ } if (entry == 016) /* call was .ENTC? */ AR = sa; /* set A to return address */ } break; case 015: /* $OTST/$MPV 105355 (OP_N) */ if (iotrap) { /* in trap cell? */ reason = cpu_save_state (iotrap); /* MP/DMS/PE interrupt */ vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ if (mp_viol & SIGN) { /* parity error? */ WriteW (vectors + cic_offset, PC); /* save point of suspension in $CIC */ PC = ReadW (vectors + perr_offset); /* vector to $PERR for processing */ } else { /* MP/DMS violation */ cpu_save_regs (iotrap); /* save CPU registers */ PC = ReadW (vectors + rqst_offset); /* vector to $RQST for processing */ } if (debug_print) { /* debugging? */ fprint_regs (",", REG_CIR, 0); /* print interrupt source */ /* and cause */ if (mp_viol & SIGN) fputs (", parity error", sim_deb); else if (mp_mevff) fputs (", DM violation", sim_deb); else fputs (", MP violation", sim_deb); } } else { /* self-test instruction */ YR = 0000000; /* RPL switch (not implemented) */ AR = 0000000; /* LDR [B] (not implemented) */ SR = 0102077; /* test passed code */ PC = (PC + 1) & VAMASK; /* P+1 return for firmware OK */ if ((cpu_dev.dctrl & DEB_OS) && /* OS debug flag set, */ (sim_deb == NULL)) /* but debugging disabled? */ XR = 0; /* rev = 0 means RTE won't use ucode */ else XR = 010; /* firmware revision 10B = 8 */ if (debug_print) /* debugging? */ fprint_regs (",", REG_X | REG_P_REL, /* print return registers */ err_PC + 1); } break; case 016: /* .ENTC/$DEV 105356 (OP_N) */ if (iotrap) { /* in trap cell? */ reason = cpu_save_state (iotrap); /* device interrupt */ AR = cpu_get_intbl (intaddr); /* get interrupt table value */ DEVINT: vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ if (INT16 (AR) < 0) /* negative (program ID)? */ PC = ReadW (vectors + sked_offset); /* vector to $SKED for processing */ else if (AR > 0) /* positive (EQT address)? */ PC = ReadW (vectors + cic2_offset); /* vector to $CIC2 for processing */ else /* zero (illegal interrupt) */ PC = ReadW (vectors + cic4_offset); /* vector to $CIC4 for processing */ if (debug_print) /* debugging? */ fprintf (sim_deb, /* print return registers */ ", CIR = %02o, INTBL = %06o", intaddr, AR); } else { /* .ENTC instruction */ ma = (PC - 4) & VAMASK; /* get addr of entry point */ goto ENTX; /* continue with common processing */ } break; case 017: /* .DSPI/$TBG 105357 (OP_N) */ if (iotrap) { /* in trap cell? */ reason = cpu_save_state (iotrap); /* TBG interrupt */ vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ PC = ReadW (vectors + clck_offset); /* vector to $CLCK for processing */ if (debug_print) /* debugging? */ fprint_regs (",", REG_CIR, 0); /* print interrupt source */ } else /* .DSPI instruction */ reason = stop_inst; /* not implemented yet */ break; } if (debug_print) { /* debugging? */ fputc ('\n', sim_deb); /* terminate line */ memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ } return reason; } simh-3.8.1/HP2100/hp2100_fp1.c0000644000175000017500000017014511046637734013322 0ustar vlmvlm/* hp2100_fp1.c: HP 1000 multiple-precision floating point routines Copyright (c) 2005-2008, J. David Bryan 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 AUTHOR 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 author. 08-Jun-08 JDB Quieted bogus gcc warning in fp_exec 10-May-08 JDB Fixed uninitialized return in fp_accum when setting 19-Mar-08 JDB Reworked "complement" to avoid inlining bug in gcc-4.x 01-Dec-06 JDB Reworked into generalized multiple-precision ops for FPP 12-Oct-06 JDB Altered x_trun for F-Series FFP compatibility Added F-Series ..TCM FFP helpers Primary references: - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) This module implements multiple-precision floating-point operations to support the 1000 F-Series hardware Floating Point Processor. It employs 64-bit integer arithmetic for speed and simplicity of implementation. The host compiler must support 64-bit integers, and the HAVE_INT64 symbol must be defined during compilation. If this symbol is not defined, then FPP support is not available. HP 2100/1000 computers used a proprietary floating-point format. The 2100 had optional firmware that provided two-word floating-point add, subtract, multiply, and divide, as well as single-integer fix and float. The 1000-M/E provided the same two-word firmware operations as standard equipment. Three-word extended-precision instructions for the 2100 and 1000-M/E were provided by the optional Fast FORTRAN Processor firmware. The 1000-F substituted a hardware floating point processor for the firmware in previous machines. In addition to the two- and three-word formats, the F-Series introduced a four-word double-precision format. A five-word format that provided extra range in the exponent by unpacking it from the mantissa was also provided, although this capability was not documented in the user manual. In addition, the FPP improved the accuracy of floating-point calculations, as the firmware versions sacrificed a few bits of precision to gain speed. Consequently, operations on the F-Series may return results that differ slightly from the same operations on the M/E-Series or the 2100. F-Series units after date code 1920 also provided two-word double-integer instructions in firmware, as well as double-integer fix and float operations. The original 32-bit floating-point format is as follows: 15 14 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |MS| mantissa high | : M +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa low | exponent |XS| : M + 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 15 8 7 1 0 Both 23-bit mantissa and 7-bit exponent are in twos-complement form. The exponent sign bit has been rotated into the LSB of the second word. The extended-precision floating-point format is a 48-bit extension of the 32-bit format used for single precision. A packed extended-precision value consists of a 39-bit mantissa and a 7-bit exponent. The format is as follows: 15 14 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |MS| mantissa high | : M +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa middle | : M + 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa low | exponent |XS| : M + 2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 15 8 7 1 0 The double-precision floating-point format is similar to the 48-bit extended-precision format, although with a 55-bit mantissa: 15 14 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |MS| mantissa high | : M +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa middle high | : M + 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa middle low | : M + 2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa low | exponent |XS| : M + 3 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 15 8 7 1 0 The FPP also supports a special five-word expanded-exponent format: 15 14 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |MS| mantissa high | : M +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa middle high | : M + 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa middle low | : M + 2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | mantissa low | : M + 3 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | exponent |XS| : M + 4 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 15 8 7 1 0 The exponent is a full 16-bit twos-complement value, but the allowed range is only 10 bits, i.e., -512 to +511. In a normalized value, the sign and MSB of the mantissa differ. Zero is represented by all words = 0. Internally, unpacked floating-point values are contained in a structure having a signed 64-bit mantissa and a signed 32-bit exponent. Mantissas are left-justified with the unused bits masked to zero. Exponents are right-justified. The precision is indicated by the value of a structure field. HP terminology for the three-word floating-point format is confused. Some documents refer to it as "double precision," while others use "extended precision." The instruction mnemonics begin with "X" (e.g., .XADD), suggesting the extended-precision term. HP apparently intended that the four-word double-precision format would be called "triple-precision," as the instruction mnemonics begin with "T" (e.g., ".TADD" for the four-word add instruction). The source files for the software simulations of these instructions for the M/E-Series also explicitly refer to "triple precision math." However, the engineering documentation and the F-Series reference manual both use the double-precision term. This module adopts the single/extended/double terminology and uses the initial letters of the instructions (F/X/T) to indicate the precision used. The FPP hardware consisted of two circuit boards that interfaced to the main CPU via the Microprogammable Processor Port (MPP) that had been introduced with the 1000 E-Series. One board contained argument registers and ALUs, split into separate mantissa and exponent parts. The other contained a state machine sequencer. FPP results were copied automatically to the argument registers in addition to being available over the MPP, so that chained operations could be executed from these "accumulators" without reloading. The FPP operated independently of the CPU. An opcode, specifying one of the six operations (add, subtract, multiply, divide, fix, or float) was sent to the FPP, and a start command was given. Operands of appropriate precision were then supplied to the FPP. Once the operands were received, the FPP would execute and set a flag when the operation was complete. The result would then be retrieved from the FPP. The floating-point instruction firmware in the CPU initiated the desired FPP operation and handled operand reads from and result writes to main memory. Under simulation, "fp_exec" provides the six arithmetic operations analogous to FPP execution. The remainder of the functions are helpers that were provided by firmware in the 1000-F but that can reuse code needed to simulate the FPP hardware. As with the hardware, "fp_exec" retains the last result in an internal accumulator that may be referenced in subsequent operations. NOTE: this module also provides the floating-point support for the firmware single-precision 1000-M/E base set and extended-precision FFP instructions. Because the firmware and hardware implementations returned slightly different results, particularly with respect to round-off, conditional checks are implemented in the arithmetic routines. In some cases, entirely different algorithms are used to ensure fidelity with the real machines. Functionally, this means that the 2100/1000-M/E and 1000-F floating-point diagnostics are not interchangeable, and failures are to be expected if a diagnostic is run on the wrong machine. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #include "hp2100_fp1.h" #if defined (HAVE_INT64) /* we need int64 support */ /* Field widths. */ #define IN_W_SIGN 1 #define IN_W_SMAGN 15 #define IN_W_DMAGN 31 #define FP_W_MSIGN 1 #define FP_W_FMANT 23 #define FP_W_XMANT 39 #define FP_W_TMANT 55 #define FP_W_EMANT 55 #define FP_W_EXPANDEXP 9 #define FP_W_EXP 7 #define FP_W_ESIGN 1 /* Starting bit numbers. */ #define IN_V_SIGN (64 - IN_W_SIGN) #define IN_V_SNUM (64 - IN_W_SIGN - IN_W_SMAGN) #define IN_V_DNUM (64 - IN_W_SIGN - IN_W_DMAGN) #define FP_V_FNUM (64 - FP_W_MSIGN - FP_W_FMANT - FP_W_EXP - FP_W_ESIGN) #define FP_V_XNUM (64 - FP_W_MSIGN - FP_W_XMANT - FP_W_EXP - FP_W_ESIGN) #define FP_V_TNUM (64 - FP_W_MSIGN - FP_W_TMANT - FP_W_EXP - FP_W_ESIGN) #define FP_V_ENUM (64 - FP_W_MSIGN - FP_W_EMANT - FP_W_EXP - FP_W_ESIGN) #define FP_V_MSIGN (64 - FP_W_MSIGN) #define FP_V_FMANT (64 - FP_W_MSIGN - FP_W_FMANT) #define FP_V_XMANT (64 - FP_W_MSIGN - FP_W_XMANT) #define FP_V_TMANT (64 - FP_W_MSIGN - FP_W_TMANT) #define FP_V_EMANT (64 - FP_W_MSIGN - FP_W_EMANT) #define FP_V_EXP 1 #define FP_V_ESIGN 0 /* Right-aligned field masks. */ #define IN_M_SIGN (((t_uint64) 1 << IN_W_SIGN) - 1) #define IN_M_SMAGN (((t_uint64) 1 << IN_W_SMAGN) - 1) #define IN_M_DMAGN (((t_uint64) 1 << IN_W_DMAGN) - 1) #define FP_M_MSIGN (((t_uint64) 1 << FP_W_MSIGN) - 1) #define FP_M_FMANT (((t_uint64) 1 << FP_W_FMANT) - 1) #define FP_M_XMANT (((t_uint64) 1 << FP_W_XMANT) - 1) #define FP_M_TMANT (((t_uint64) 1 << FP_W_TMANT) - 1) #define FP_M_EMANT (((t_uint64) 1 << FP_W_EMANT) - 1) #define FP_M_EXPANDEXP ((1 << FP_W_EXPANDEXP) - 1) #define FP_M_EXP ((1 << FP_W_EXP) - 1) #define FP_M_ESIGN ((1 << FP_W_ESIGN) - 1) /* In-place field masks. */ #define IN_SIGN (IN_M_SIGN << IN_V_SIGN) #define IN_SMAGN (IN_M_SMAGN << IN_V_SNUM) #define IN_DMAGN (IN_M_DMAGN << IN_V_DNUM) #define FP_MSIGN (FP_M_MSIGN << FP_V_MSIGN) #define FP_FMANT (FP_M_FMANT << FP_V_FMANT) #define FP_XMANT (FP_M_XMANT << FP_V_XMANT) #define FP_TMANT (FP_M_TMANT << FP_V_TMANT) #define FP_EMANT (FP_M_EMANT << FP_V_EMANT) #define FP_EXP (FP_M_EXP << FP_V_EXP) #define FP_ESIGN (FP_M_ESIGN << FP_V_ESIGN) /* In-place record masks. */ #define IN_SSMAGN (IN_SIGN | IN_SMAGN) #define IN_SDMAGN (IN_SIGN | IN_DMAGN) #define FP_SFMANT (FP_MSIGN | FP_FMANT) #define FP_SXMANT (FP_MSIGN | FP_XMANT) #define FP_STMANT (FP_MSIGN | FP_TMANT) #define FP_SEMANT (FP_MSIGN | FP_EMANT) #define FP_SEXP (FP_ESIGN | FP_EXP) /* Minima and maxima. */ #define FP_ONEHALF ((t_int64) 1 << (FP_V_MSIGN - 1)) /* mantissa = 0.5 */ #define FP_MAXPMANT ((t_int64) FP_EMANT) /* maximum pos mantissa */ #define FP_MAXNMANT ((t_int64) FP_MSIGN) /* maximum neg mantissa */ #define FP_MAXPEXP (FP_M_EXPANDEXP) /* maximum pos expanded exponent */ #define FP_MAXNEXP (-(FP_MAXPEXP + 1)) /* maximum neg expanded exponent */ /* Floating-point helpers. */ #define DENORM(x) ((((x) ^ (x) << 1) & FP_MSIGN) == 0) #define TO_EXP(e) (int8) ((e >> FP_V_EXP & FP_M_EXP) | \ (e & FP_M_ESIGN ? ~FP_M_EXP : 0)) /* Property constants. */ static const t_int64 p_half_lsb[6] = { ((t_int64) 1 << IN_V_SNUM) - 1, /* different than FP! */ ((t_int64) 1 << IN_V_DNUM) - 1, /* different than FP! */ (t_int64) 1 << (FP_V_FMANT - 1), (t_int64) 1 << (FP_V_XMANT - 1), (t_int64) 1 << (FP_V_TMANT - 1), (t_int64) 1 << (FP_V_EMANT - 1) }; static const t_int64 n_half_lsb[6] = { 0, 0, ((t_int64) 1 << (FP_V_FMANT - 1)) - 1, ((t_int64) 1 << (FP_V_XMANT - 1)) - 1, ((t_int64) 1 << (FP_V_TMANT - 1)) - 1, ((t_int64) 1 << (FP_V_EMANT - 1)) - 1 }; static const uint32 op_start[6] = { IN_V_SNUM, IN_V_DNUM, FP_V_FMANT, FP_V_XMANT, FP_V_TMANT, FP_V_EMANT }; static const t_int64 mant_mask[6] = { IN_SSMAGN, IN_SDMAGN, FP_SFMANT, FP_SXMANT, FP_STMANT, FP_SEMANT }; static const uint32 op_bits[6] = { IN_W_SMAGN, IN_W_DMAGN, FP_W_FMANT + FP_W_MSIGN, FP_W_XMANT + FP_W_MSIGN, FP_W_TMANT + FP_W_MSIGN, FP_W_EMANT + FP_W_MSIGN }; static const t_int64 op_mask[6] = { ~(t_int64) 0 << IN_V_SNUM, ~(t_int64) 0 << IN_V_DNUM, ~(t_int64) 0 << FP_V_FNUM, ~(t_int64) 0 << FP_V_XNUM, ~(t_int64) 0 << FP_V_TNUM, ~(t_int64) 0 << FP_V_ENUM }; static const uint32 int_p_max[2] = { IN_M_SMAGN, IN_M_DMAGN }; /* Internal unpacked floating-point representation. */ typedef struct { t_int64 mantissa; int32 exponent; OPSIZE precision; } FPU; /* Low-level helper routines. */ /* Arithmetic shift right for mantissa only. Returns TRUE if any one-bits are shifted out (for F-series only). */ static t_bool asr (FPU *operand, int32 shift) { t_uint64 mask; t_bool bits_lost; if (UNIT_CPU_MODEL == UNIT_1000_F) { /* F-series? */ mask = ((t_uint64) 1 << shift) - 1; /* mask for lost bits */ bits_lost = ((operand->mantissa & mask) != 0); /* flag if any lost */ } else bits_lost = FALSE; operand->mantissa = operand->mantissa >> shift; /* mantissa is int, so ASR */ return bits_lost; } /* Logical shift right for mantissa and exponent. Shifts mantissa and corrects exponent for mantissa overflow. Returns TRUE if any one-bits are shifted out (for F-series only). */ static t_bool lsrx (FPU *operand, int32 shift) { t_uint64 mask; t_bool bits_lost; if (UNIT_CPU_MODEL == UNIT_1000_F) { /* F-series? */ mask = ((t_uint64) 1 << shift) - 1; /* mask for lost bits */ bits_lost = ((operand->mantissa & mask) != 0); /* flag if any lost */ } else bits_lost = FALSE; operand->mantissa = (t_uint64) operand->mantissa >> shift; /* uint, so LSR */ operand->exponent = operand->exponent + shift; /* correct exponent */ return bits_lost; } /* Unpack an operand into a long integer. Returns a left-aligned integer or mantissa. Does not mask to precision; this should be done subsequently if desired. */ static t_int64 unpack_int (OP packed, OPSIZE precision) { uint32 i; t_uint64 unpacked = 0; if (precision == in_s) unpacked = (t_uint64) packed.word << 48; /* unpack single integer */ else if (precision == in_d) unpacked = (t_uint64) packed.dword << 32; /* unpack double integer */ else { if (precision == fp_e) /* five word operand? */ precision = fp_t; /* only four mantissa words */ for (i = 0; i < 4; i++) /* unpack fp 2 to 4 words */ if (i < TO_COUNT (precision)) unpacked = unpacked << 16 | packed.fpk[i]; else unpacked = unpacked << 16; } return (t_int64) unpacked; } /* Unpack a packed operand. The packed value is split into separate mantissa and exponent variables. The multiple words of the mantissa are concatenated into a single 64-bit signed value, and the exponent is shifted with recovery of the sign. */ static FPU unpack (OP packed, OPSIZE precision) { FPU unpacked; unpacked.precision = precision; /* set value's precision */ unpacked.mantissa = /* unpack and mask mantissa */ unpack_int (packed, precision) & mant_mask[precision]; switch (precision) { case fp_f: case fp_x: case fp_t: unpacked.exponent = /* unpack exponent from correct word */ TO_EXP (packed.fpk[(uint32) precision - 1]); break; case fp_e: unpacked.exponent = /* unpack expanded exponent */ (int16) (packed.fpk[4] >> FP_V_EXP | /* rotate sign into place */ (packed.fpk[4] & 1 ? SIGN : 0)); break; case fp_a: /* no action for value in accum */ case in_s: /* integers don't use exponent */ case in_d: /* integers don't use exponent */ default: unpacked.exponent = 0; break; } return unpacked; } /* Pack a long integer into an operand. */ static OP pack_int (t_int64 unpacked, OPSIZE precision) { int32 i; OP packed; if (precision == in_s) packed.word = (uint16) (unpacked >> 48) & DMASK; /* pack single integer */ else if (precision == in_d) packed.dword = (uint32) (unpacked >> 32) & DMASK32; /* pack double integer */ else { if (precision == fp_e) /* five word operand? */ precision = fp_t; /* only four mantissa words */ for (i = 3; i >= 0; i--) { /* pack fp 2 to 4 words */ packed.fpk[i] = (uint16) unpacked & DMASK; unpacked = unpacked >> 16; } } return packed; } /* Pack an unpacked floating-point number. The 64-bit mantissa is split into the appropriate number of 16-bit words. The exponent is rotated to incorporate the sign bit and merged into the appropriate word. */ static OP pack (FPU unpacked) { OP packed; uint8 exp; packed = pack_int (unpacked.mantissa, unpacked.precision); /* pack mantissa */ exp = ((uint8) unpacked.exponent << FP_V_EXP) | /* rotate exponent */ ((unpacked.exponent < 0) << FP_V_ESIGN); switch (unpacked.precision) { /* merge exponent into correct word */ case in_s: /* no action for integers */ case in_d: break; case fp_f: /* merge into last word */ case fp_x: case fp_t: packed.fpk[(uint32) unpacked.precision - 1] = (packed.fpk[(uint32) unpacked.precision - 1] & ~FP_SEXP) | exp; break; case fp_e: /* place in separate word */ packed.fpk[4] = ((uint16) unpacked.exponent << FP_V_EXP) | ((unpacked.exponent < 0) << FP_V_ESIGN); break; case fp_a: /* no action for value in accum */ break; } return packed; } /* Normalize an unpacked floating-point number. Floating-point numbers are in normal form if the sign bit and the MSB of the mantissa differ. Unnormalized numbers are shifted as needed with appropriate exponent modification. */ static void normalize (FPU *unpacked) { if (unpacked->mantissa) /* non-zero? */ while (DENORM (unpacked->mantissa)) { /* normal form? */ unpacked->exponent = unpacked->exponent - 1; /* no, so left shift */ unpacked->mantissa = unpacked->mantissa << 1; } else unpacked->exponent = 0; /* clean for zero */ return; } /* Round an unpacked floating-point number and check for overflow. An unpacked floating-point number is rounded by adding one-half of the LSB value, maintaining symmetry around zero. If rounding resulted in a mantissa overflow, the result logically is shifted to the right with an appropriate exponent modification. Finally, the result is checked for exponent underflow or overflow, and the appropriate approximation (zero or infinity) is returned. Rounding in hardware involves a special mantissa extension register that holds three "guard" bits and one "sticky" bit. These represent the value of bits right-shifted out the mantissa register. Under simulation, we track such right-shifts and utilize the lower eight bits of the 64-bit mantissa value to simulate the extension register. Overflow depends on whether the FPP expanded-exponent form is being used (this expands the exponent range by two bits). If overflow is detected, the value representing infinity is dependent on whether the operation is on behalf of the Fast FORTRAN Processor. The F-Series FPP returns positive infinity on both positive and negative overflow for all precisions. The 2100 and M/E-Series FFPs return negative infinity on negative overflow of extended-precision values. Single-precision overflows on these machines always return positive infinity. The number to be rounded must be normalized upon entry. */ static uint32 roundovf (FPU *unpacked, t_bool expand) { uint32 overflow; t_bool sign; sign = (unpacked->mantissa < 0); /* save mantissa sign */ if (sign) /* round and mask the number */ unpacked->mantissa = (unpacked->mantissa + n_half_lsb[unpacked->precision]) & mant_mask[unpacked->precision]; else unpacked->mantissa = (unpacked->mantissa + p_half_lsb[unpacked->precision]) & mant_mask[unpacked->precision]; if (sign != (unpacked->mantissa < 0)) /* mantissa overflow? */ lsrx (unpacked, 1); /* correct by shifting */ else normalize (unpacked); /* renorm may be needed */ if (unpacked->mantissa == 0) { /* result zero? */ unpacked->mantissa = 0; /* return zero */ unpacked->exponent = 0; overflow = 0; /* with overflow clear */ } else if (unpacked->exponent < /* result underflow? */ (FP_MAXNEXP >> (expand ? 0 : 2))) { unpacked->mantissa = 0; /* return zero */ unpacked->exponent = 0; overflow = 1; /* and set overflow */ } else if (unpacked->exponent > /* result overflow? */ (FP_MAXPEXP >> (expand ? 0 : 2))) { if (sign && /* negative value? */ (unpacked->precision == fp_x) && /* extended precision? */ (UNIT_CPU_MODEL != UNIT_1000_F)) { /* not F-series? */ unpacked->mantissa = FP_MAXNMANT; /* return negative infinity */ unpacked->exponent = FP_MAXPEXP & FP_M_EXP; } else { unpacked->mantissa = FP_MAXPMANT; /* return positive infinity */ unpacked->exponent = FP_MAXPEXP & FP_M_EXP; } overflow = 1; /* and set overflow */ } else overflow = 0; /* value is in range */ return overflow; } /* Normalize, round, and pack an unpacked floating-point number. */ static uint32 nrpack (OP *packed, FPU unpacked, t_bool expand) { uint32 overflow; normalize (&unpacked); /* normalize for rounding */ overflow = roundovf (&unpacked, expand); /* round and check for overflow */ *packed = pack (unpacked); /* pack result */ return overflow; } /* Low-level arithmetic routines. */ /* Complement an unpacked number. */ static void complement (FPU *result) { if (result->mantissa == FP_MAXNMANT) { /* maximum negative? */ result->mantissa = FP_ONEHALF; /* complement of -1.0 * 2 ^ n */ result->exponent = result->exponent + 1; /* is 0.5 * 2 ^ (n + 1) */ } else result->mantissa = -result->mantissa; /* negate mantissa */ return; } /* Add two unpacked numbers. The mantissas are first aligned if necessary by scaling the smaller of the two operands. If the magnitude of the difference between the exponents is greater than the number of significant bits, then the smaller number has been scaled to zero (swamped), and so the sum is simply the larger operand. Otherwise, the sum is computed and checked for overflow, which has occured if the signs of the operands are the same but differ from that of the result. Scaling and renormalization is perfomed if overflow occurred. */ static void add (FPU *sum, FPU augend, FPU addend) { int32 magn; t_bool bits_lost; if (augend.mantissa == 0) *sum = addend; /* X + 0 = X */ else if (addend.mantissa == 0) *sum = augend; /* 0 + X = X */ else { magn = augend.exponent - addend.exponent; /* difference exponents */ if (magn > 0) { /* addend smaller? */ *sum = augend; /* preset augend */ bits_lost = asr (&addend, magn); /* align addend */ } else { /* augend smaller? */ *sum = addend; /* preset addend */ magn = -magn; /* make difference positive */ bits_lost = asr (&augend, magn); /* align augend */ } if (magn <= (int32) op_bits[augend.precision]) { /* value swamped? */ sum->mantissa = /* no, add mantissas */ addend.mantissa + augend.mantissa; if (((addend.mantissa < 0) == (augend.mantissa < 0)) && /* mantissa overflow? */ ((addend.mantissa < 0) != (sum->mantissa < 0))) { bits_lost = bits_lost | lsrx (sum, 1); /* restore value */ sum->mantissa = /* restore sign */ sum-> mantissa | (addend.mantissa & FP_MSIGN); } if (bits_lost) /* any bits lost? */ sum->mantissa = sum->mantissa | 1; /* include one for rounding */ } } return; } /* Multiply two unpacked numbers. The single-precision firmware (FMP) operates differently from the firmware extended-precision (.XMPY) and the hardware multiplies of any precision. Firmware implementations form 16-bit x 16-bit = 32-bit partial products and sum them to form the result. The hardware uses a series of shifts and adds. This means that firmware FMP and hardware FMP return slightly different values, as may be seen by attempting to run the firmware FMP diagnostic on the FPP. The FMP microcode calls a signed multiply routine to calculate three partial products (all but LSB * LSB). Because the LSBs are unsigned, i.e., all bits significant, the two MSB * LSB products are calculated using LSB/2. The unsigned right-shift ensures a positive LSB with no significant bits lost, because the lower eight bits are unused (they held the vacated exponent). In order to sum the partial products, the LSB of the result of MSB * MSB is also right-shifted before addition. Note, though, that this loses a significant bit. After summation, the result is left-shifted to correct for the original right shifts. The .XMPY microcode negates both operands as necessary to produce positive values and then forms six of the nine 16-bit x 16-bit = 32-bit unsigned multiplications required for a full 96-bit product. Given a 48-bit multiplicand "a1a2a3" and a 48-bit multiplier "b1b2b3", the firmware performs these calculations to develop a 48-bit product: a1 a2 a3 +-------+-------+-------+ b1 b2 b3 +-------+-------+-------+ _________________________ a1 * b3 [p1] +-------+-------+ a2 * b2 [p2] +-------+-------+ a1 * b2 [p3] +-------+-------+ a3 * b1 [p4] +-------+-------+ a2 * b1 [p5] +-------+-------+ a1 * b1 [p6] +-------+-------+ _________________________________ product +-------+-------+-------+ The least-significant words of partial products [p1], [p2], and [p4] are used only to develop a carry bit into the 48-bit sum. The product is complemented as necessary to restore the sign. The basic FPP hardware algorithm scans the multiplier and adds a shifted copy of the multiplicand whenever a one-bit is detected. To avoid successive adds when a string of ones is encountered (because adds are more expensive than shifts), the hardware instead adds the multiplicand shifted by N+1+P and subtracts the multiplicand shifted by P to obtain the equivalent value with a maximum of two operations. Instead of implementing either the .XMPY firmware algorithm or the hardware shift-and-add algorithm directly, it is more efficient under simulation to use 32 x 32 = 64-bit multiplications, thereby reducing the number required from six to four (64-bit "c1c2" x 64-bit "d1d2"): ah al +-------+-------+ bh bl +-------+-------+ _________________ al * bl [ll] +-------+-------+ ah * bl [hl] +-------+-------+ al * bh [lh] +-------+-------+ ah * bh [hh] +-------+-------+ _________________________________ product +-------+-------+ However, the FMP algorithm is implemented directly from the microcode to preserve the fidelity of the simulation, i.e., to lose the same amount of precision. */ static void multiply (FPU *product, FPU multiplicand, FPU multiplier) { uint32 ah, al, bh, bl, sign = 0; t_uint64 hh, hl, lh, ll, carry; int16 ch, cl, dh, dl; t_bool firmware; product->precision = multiplicand.precision; /* set precision */ if ((multiplicand.mantissa == 0) || /* 0 * X = 0 */ (multiplier.mantissa == 0)) /* X * 0 = 0 */ product->mantissa = product->exponent = 0; else { firmware = (UNIT_CPU_MODEL != UNIT_1000_F); /* set firmware flag */ if (!firmware || (product->precision != fp_f)) { /* hardware? */ if (multiplicand.mantissa < 0) { /* negative? */ complement (&multiplicand); /* complement operand */ sign = ~sign; /* track sign */ } if (multiplier.mantissa < 0) { /* negative? */ complement (&multiplier); /* complement operand */ sign = ~sign; /* track sign */ } } product->exponent = /* compute exponent */ multiplicand.exponent + multiplier.exponent + 1; ah = (uint32) (multiplicand.mantissa >> 32); /* split multiplicand */ al = (uint32) (multiplicand.mantissa & DMASK32); /* into high and low parts */ bh = (uint32) (multiplier.mantissa >> 32); /* split multiplier */ bl = (uint32) (multiplier.mantissa & DMASK32); /* into high and low parts */ if (firmware && (product->precision == fp_f)) { /* single-precision firmware? */ ch = (int16) (ah >> 16) & DMASK; /* split 32-bit multiplicand */ cl = (int16) (ah & 0xfffe); /* into high and low parts */ dh = (int16) (bh >> 16) & DMASK; /* split 32-bit multiplier */ dl = (int16) (bh & 0xfffe); /* into high and low parts */ hh = (t_uint64) (((int32) ch * dh) & ~1); /* form cross products */ hl = (t_uint64) (((t_int64) ch * (t_int64) (uint16) dl + (t_int64) dh * (t_int64) (uint16) cl) & 0xfffffffffffe0000); product->mantissa = (t_uint64) (((t_int64) hh << 32) + /* sum partials */ ((t_int64) hl << 16)); } else { hh = ((t_uint64) ah * bh); /* form four cross products */ hl = ((t_uint64) ah * bl); /* using 32 x 32 = */ lh = ((t_uint64) al * bh); /* 64-bit multiplies */ ll = ((t_uint64) al * bl); carry = ((ll >> 32) + (uint32) hl + (uint32) lh) >> 32; /* form carry */ product->mantissa = hh + (hl >> 32) + (lh >> 32) + carry; /* sum partials */ if (sign) /* negate if required */ complement (product); } } return; } /* Divide two unpacked numbers. As with multiply, the single-precision firmware (FDV) operates differently from the firmware extended-precision (.XDIV) and the hardware divisions of any precision. Firmware implementations utilize a "divide and correct" algorithm, wherein the quotient is estimated and then corrected by comparing the dividend to the product of the quotient and the divisor. The hardware uses a series of shifts and subtracts. This means that firmware FDV and hardware FDV once again return slightly different values. Under simulation, the classic divide-and-correct method is employed, using 64-bit / 32-bit = 32-bit divisions. This involves dividing the 64-bit dividend "a1a2a3a4" by the first 32-bit digit "b1b2" of the 64-bit divisor "b1b2b3b4". The resulting 32-bit quotient is ... The microcoded single-precision division avoids overflows by right-shifting some values, which leads to a loss of precision in the LSBs. We duplicate the firmware algorithm here to preserve the fidelity of the simulation. */ static void divide (FPU *quotient, FPU dividend, FPU divisor) { uint32 sign = 0; t_int64 bh, bl, r1, r0, p1, p0; t_uint64 q, q1, q0; t_bool firmware; int32 ah, div, cp; int16 dh, dl, pq1, pq2, cq; quotient->precision = dividend.precision; /* set precision */ if (divisor.mantissa == 0) { /* division by zero? */ if (dividend.mantissa < 0) quotient->mantissa = FP_MSIGN; /* return minus infinity */ else quotient->mantissa = ~FP_MSIGN; /* or plus infinity */ quotient->exponent = FP_MAXPEXP + 1; } else if (dividend.mantissa == 0) /* dividend zero? */ quotient->mantissa = quotient->exponent = 0; /* yes; result is zero */ else { firmware = (UNIT_CPU_MODEL != UNIT_1000_F); /* set firmware flag */ if (!firmware || (quotient->precision != fp_f)) { /* hardware or FFP? */ if (dividend.mantissa < 0) { /* negative? */ complement (÷nd); /* complement operand */ sign = ~sign; /* track sign */ } if (divisor.mantissa < 0) { /* negative? */ complement (&divisor); /* complement operand */ sign = ~sign; /* track sign */ } } quotient->exponent = /* division subtracts exponents */ dividend.exponent - divisor.exponent; bh = divisor.mantissa >> 32; /* split divisor */ bl = divisor.mantissa & DMASK32; /* into high and low parts */ if (firmware && (quotient->precision == fp_f)) { /* single-precision firmware? */ quotient->exponent = quotient->exponent + 1; /* fix exponent */ ah = (int32) (dividend.mantissa >> 32); /* split dividend */ dh = (int16) (bh >> 16); /* split divisor again */ dl = (int16) bh; div = ah >> 2; /* ASR 2 to prevent overflow */ pq1 = div / dh; /* form first partial quotient */ div = ((div % dh) & ~1) << 15; /* ASR 1, move rem to upper */ pq2 = div / dh; /* form second partial quotient */ div = (uint16) dl << 13; /* move divisor LSB to upper, LSR 3 */ cq = div / dh; /* form correction quotient */ cp = -cq * pq1; /* and correction product */ cp = (((cp >> 14) & ~3) + (int32) pq2) << 1; /* add corr prod and 2nd partial quo */ quotient->mantissa = /* add 1st partial quo and align */ (t_uint64) (((int32) pq1 << 16) + cp) << 32; } else { /* hardware or FFP */ q1 = (t_uint64) (dividend.mantissa / bh); /* form 1st trial quotient */ r1 = dividend.mantissa % bh; /* and remainder */ p1 = (r1 << 24) - (bl >> 8) * q1; /* calculate correction */ while (p1 < 0) { /* correction needed? */ q1 = q1 - 1; /* trial quotient too large */ p1 = p1 + (divisor.mantissa >> 8); /* increase remainder */ } q0 = (t_uint64) ((p1 << 8) / bh); /* form 2nd trial quotient */ r0 = (p1 << 8) % bh; /* and remainder */ p0 = (r0 << 24) - (bl >> 8) * q0; /* calculate correction */ while (p0 < 0) { /* correction needed? */ q0 = q0 - 1; /* trial quotient too large */ p0 = p0 + (divisor.mantissa >> 8); /* increase remainder */ } q = (q1 << 32) + q0; /* sum quotient digits */ if (q1 & 0xffffffff00000000) { /* did we lose MSB? */ q = (q >> 1) | 0x8000000000000000; /* shift right and replace bit */ quotient->exponent = quotient->exponent + 1;/* bump exponent for shift */ } if (q & 0x8000000000000000) /* lose normalization? */ q = q >> 1; /* correct */ quotient->mantissa = (t_int64) q; } if (sign) complement (quotient); /* negate if required */ } return; } /* Fix an unpacked number. A floating-point value is converted to an integer. The desired precision of the result (single or double integer) must be set before calling. Values less than 0.5 (i.e., with negative exponents) underflow to zero. If the value exceeds the specified integer range, the maximum integer value is returned and overflow is set. Otherwise, the floating-point value is right-shifted to zero the exponent. The result is then rounded. */ static uint32 fix (FPU *result, FPU operand) { uint32 overflow; t_bool bits_lost; if (operand.exponent < 0) { /* value < 0.5? */ result->mantissa = 0; /* result rounds to zero */ overflow = 0; /* clear for underflow */ } else if (operand.exponent > /* value > integer size? */ (int32) op_bits[result->precision]) { result->mantissa = /* return max int value */ (t_uint64) int_p_max[result->precision] << op_start[result->precision]; overflow = 1; /* and set overflow */ } else { /* value in range */ bits_lost = asr (&operand, /* shift to zero exponent */ op_bits[result->precision] - operand.exponent); if (operand.mantissa < 0) { /* value negative? */ if (bits_lost) /* bits lost? */ operand.mantissa = operand.mantissa | 1; /* include one for rounding */ operand.mantissa = operand.mantissa + /* round result */ p_half_lsb[result->precision]; } result->mantissa = operand.mantissa & /* mask to precision */ op_mask[result->precision]; overflow = 0; } result->exponent = 0; /* tidy up for integer value */ return overflow; } /* Float an integer to an unpacked number. An integer is converted to a floating-point value. The desired precision of the result must be set before calling. Conversion is simply a matter of copying the integer value, setting an exponent that reflects the right-aligned position of the bits, and normalizing. */ static void ffloat (FPU *result, FPU operand) { result->mantissa = operand.mantissa; /* set value */ result->exponent = op_bits[operand.precision]; /* set exponent */ normalize (result); /* normalize */ return; } /* High-level floating-point routines. */ /* Determine operand precisions. The precisions of the operands and result are determined by decoding an operation opcode and returned to the caller. Pass NULL for both of the operands if only the result precision is wanted. Pass NULL for the result if only the operand precisions are wanted. Implementation note: 1. gcc-4.3.0 complains at -O3 that operand_l/r may not be initialized. Because of the mask, the switch statement covers all cases, but gcc doesn't realize this. The "default" case is redundant but eliminates the warning. */ void fp_prec (uint16 opcode, OPSIZE *operand_l, OPSIZE *operand_r, OPSIZE *result) { OPSIZE fp_size, int_size; fp_size = (OPSIZE) ((opcode & 0003) + 2); /* fp_f, fp_x, fp_t, fp_e */ int_size = (OPSIZE) ((opcode & 0004) >> 2); /* in_s, in_d */ if (operand_l && operand_r) { /* want operand precisions? */ switch (opcode & 0120) { /* mask out opcode bit 5 */ case 0000: /* add/mpy */ case 0020: /* sub/div */ *operand_l = fp_size; /* assume first op is fp */ if (opcode & 0004) /* operand internal? */ *operand_r = fp_a; /* second op is accum */ else *operand_r = fp_size; /* second op is fp */ break; case 0100: /* fix/accum as integer */ *operand_l = fp_size; /* first op is fp */ *operand_r = fp_a; /* second op is always null */ break; case 0120: /* flt/accum as float */ default: /* keeps compiler quiet for uninit warning */ *operand_l = int_size; /* first op is integer */ *operand_r = fp_a; /* second op is always null */ break; } if (opcode & 0010) /* operand internal? */ *operand_l = fp_a; /* first op is accum */ } if (result) /* want result precision? */ if ((opcode & 0120) == 0100) /* fix? */ *result = int_size; /* result is integer */ else /* all others */ *result = fp_size; /* result is fp */ return; } /* Floating Point Processor executor. The executor simulates the MPP interface between the CPU and the FPP. The operation to be performed is specified by the supplied opcode, which conforms to the FPP hardware interface, as follows: Bits Value Action ---- ----- ---------------------------------------------- 7 0 Exponent range is standard (+/-127) 1 Exponent range is expanded (+/-511) 6-4 000 Add 001 Subtract 010 Multiply 011 Divide 100 Fix 101 Float 110 (diagnostic) 111 (diagnostic) 3 0 Left operand is supplied 1 Left operand in accumulator 2 0 Right operand is supplied (ADD/SUB/MPY/DIV) Single integer operation (FIX/FLT) 1 Right operand in accumulator (ADD/SUB/MPY/DIV) Double integer operation (FIX/FLT) 1-0 00 2-word operation 01 3-word operation 10 4-word operation 11 5-word operation If the opcode specifies that the left (or right) operand is in the accumulator, then the value supplied for that parameter is not used. All results are automatically left in the accumulator. If the result is not needed externally, then NULL may be passed for the result parameter. To support accumulator set/get operations under simulation, the opcode is expanded to include a special mode, indicated by bit 15 = 1. In this mode, if the result parameter is NULL, then the accumulator is set from the value passed as operand_l. If the result parameter is not null, then the accumulator value is returned as the result, and operand_l is ignored. The precision of the operation is performed as specified by the OPSIZE value passed in bits 2-0 of the opcode. The function returns 1 if the operation overflows and 0 if not. */ uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r) { static FPU accumulator; FPU uoperand_l, uoperand_r; OPSIZE op_l_prec, op_r_prec, rslt_prec; uint32 overflow; if (opcode & SIGN) { /* accumulator mode? */ rslt_prec = (OPSIZE) (opcode & 0017); /* get operation precision */ if (result) { /* get accumulator? */ op_l_prec = accumulator.precision; /* save accum prec temp */ accumulator.precision = rslt_prec; /* set desired precision */ *result = pack (accumulator); /* pack accumulator */ accumulator.precision = op_l_prec; /* restore correct prec */ } else /* set accumulator */ accumulator = unpack (operand_l, rslt_prec); /* unpack from operand */ return 0; /* no overflow from accum ops */ } fp_prec (opcode, &op_l_prec, &op_r_prec, &rslt_prec); /* calc precs from opcode */ if (op_l_prec == fp_a) /* left operand in accum? */ uoperand_l = accumulator; /* copy it */ else /* operand supplied */ uoperand_l = unpack (operand_l, op_l_prec); /* unpack from parameter */ if (op_r_prec == fp_a) /* right operand in accum? */ uoperand_r = accumulator; /* copy it */ else /* operand supplied */ uoperand_r = unpack (operand_r, op_r_prec); /* unpack from parameter */ switch (opcode & 0160) { /* dispatch operation */ case 0000: /* add */ add (&accumulator, uoperand_l, uoperand_r); break; case 0020: /* subtract */ complement (&uoperand_r); add (&accumulator, uoperand_l, uoperand_r); break; case 0040: /* multiply */ multiply (&accumulator, uoperand_l, uoperand_r); break; case 0060: /* divide */ divide (&accumulator, uoperand_l, uoperand_r); break; case 0100: /* fix */ accumulator.precision = rslt_prec; overflow = fix (&accumulator, uoperand_l); if (result) /* result wanted? */ *result = pack_int (accumulator.mantissa, /* pack integer */ rslt_prec); return overflow; case 0120: /* float */ accumulator.precision = rslt_prec; ffloat (&accumulator, uoperand_l); if (result) /* result wanted? */ *result = pack (accumulator); /* pack FP (FLT does not round) */ return 0; case 0140: /* (diagnostic) */ case 0160: /* (diagnostic) */ return 0; } if (UNIT_CPU_MODEL != UNIT_1000_F) /* firmware implementation? */ accumulator.mantissa = accumulator.mantissa & /* mask to precision */ op_mask[accumulator.precision]; normalize (&accumulator); /* normalize */ overflow = roundovf (&accumulator, opcode & 0200); /* round and check for overflow */ if (result) /* result wanted? */ *result = pack (accumulator); /* pack result */ return overflow; } /* Set or get accumulator at desired precision. This function provides access to the FPP accumulator. In hardware, the accumulator may be read at a given precision by sending the FPP an opcode encoded with the desired precision and then reading words from the FPP /without/ initiating the operation, i.e., without starting the processor. Under simulation, pass this function a NULL operand and the desired precision to read the accumulator. Pass a pointer to an operand and the desired precision to set the accumulator; the return value in this case is not defined. */ OP fp_accum (const OP *operand, OPSIZE precision) { OP result = NOP; uint16 opcode = (uint16) precision | SIGN; /* add special mode bit */ if (operand) fp_exec (opcode, NULL, *operand, NOP); /* set accum */ else fp_exec (opcode, &result, NOP, NOP); /* get accum */ return result; } /* Pack an unpacked floating-point number. An unpacked mantissa is passed as a "packed" number with an unused exponent. The mantissa and separately-passed exponent are packed into the in-memory floating-point format. Note that all bits are significant in the mantissa (no masking is done). */ uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) { FPU unpacked; unpacked.mantissa = unpack_int (mantissa, precision); /* unpack mantissa */ unpacked.exponent = exponent; /* set exponent */ unpacked.precision = precision; /* set precision */ *result = pack (unpacked); /* pack them */ return 0; } /* Normalize, round, and pack an unpacked floating-point number. An unpacked mantissa is passed as a "packed" number with an unused exponent. The mantissa and separately-passed exponent are normalized, rounded, and packed into the in-memory floating-point format. Note that all bits are significant in the mantissa (no masking is done). */ uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) { FPU unpacked; unpacked.mantissa = unpack_int (mantissa, precision); /* unpack mantissa */ unpacked.exponent = exponent; /* set exponent */ unpacked.precision = precision; /* set precision */ return nrpack (result, unpacked, FALSE); /* norm/rnd/pack them */ } /* Unpack a packed floating-point number. A floating-point number, packed into the in-memory format, is unpacked into separate mantissa and exponent values. The unpacked mantissa is returned in a "packed" structure with an exponent of zero. Mantissa or exponent may be null if that part isn't wanted. */ uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision) { FPU unpacked; unpacked = unpack (packed, precision); /* unpack mantissa and exponent */ if (exponent) /* exponent wanted? */ *exponent = unpacked.exponent; /* return exponent */ if (mantissa) /* mantissa wanted? */ *mantissa = pack_int (unpacked.mantissa, fp_t); /* return full-size mantissa */ return 0; } /* Complement an unpacked mantissa. An unpacked mantissa is passed as a "packed" number with a zero exponent. The exponent increment, i.e., either zero or one, depending on whether a renormalization was required, is returned. Note that all bits are significant in the mantissa. */ uint32 fp_ucom (OP *mantissa, OPSIZE precision) { FPU unpacked; unpacked.mantissa = unpack_int (*mantissa, precision); /* unpack mantissa */ unpacked.exponent = 0; /* clear undefined exponent */ unpacked.precision = precision; /* set precision */ complement (&unpacked); /* negate it */ *mantissa = pack_int (unpacked.mantissa, precision); /* replace mantissa */ return (uint32) unpacked.exponent; /* return exponent increment */ } /* Complement a floating-point number. */ uint32 fp_pcom (OP *packed, OPSIZE precision) { FPU unpacked; unpacked = unpack (*packed, precision); /* unpack the number */ complement (&unpacked); /* negate it */ return nrpack (packed, unpacked, FALSE); /* and norm/rnd/pack */ } /* Truncate a floating-point number. */ uint32 fp_trun (OP *result, OP source, OPSIZE precision) { t_bool bits_lost; FPU unpacked; FPU one = { FP_ONEHALF, 1, 0 }; /* 0.5 * 2 ** 1 = 1.0 */ OP zero = { { 0, 0, 0, 0, 0 } }; /* 0.0 */ t_uint64 mask = mant_mask[precision] & ~FP_MSIGN; unpacked = unpack (source, precision); if (unpacked.exponent < 0) /* number < 0.5? */ *result = zero; /* return 0 */ else if (unpacked.exponent >= (int32) op_bits[precision]) /* no fractional bits? */ *result = source; /* already integer */ else { mask = (mask >> unpacked.exponent) & mask; /* mask fractional bits */ bits_lost = ((unpacked.mantissa & mask) != 0); /* flag if bits lost */ unpacked.mantissa = unpacked.mantissa & ~mask; /* mask off fraction */ if ((unpacked.mantissa < 0) && bits_lost) /* negative? */ add (&unpacked, unpacked, one); /* truncate toward zero */ nrpack (result, unpacked, FALSE); /* (overflow cannot occur) */ } return 0; /* clear overflow on return */ } /* Convert a floating-point number from one precision to another. */ uint32 fp_cvt (OP *result, OPSIZE source_precision, OPSIZE dest_precision) { FPU unpacked; unpacked = unpack (*result, source_precision); unpacked.precision = dest_precision; return nrpack (result, unpacked, FALSE); /* norm/rnd/pack */ } #endif /* end of int64 support */ simh-3.8.1/HP2100/hp2100_defs.h0000644000175000017500000004251611062255240013544 0ustar vlmvlm/* hp2100_defs.h: HP 2100 simulator definitions Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 07-Sep-08 JDB Added POLL_FIRST to indicate immediate connection attempt 15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h 26-Jun-08 JDB Rewrote device I/O to model backplane signals 25-Jun-08 JDB Added PIF device 17-Jun-08 JDB Declared fmt_char() function 26-May-08 JDB Added MPX device 24-Apr-08 JDB Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks 14-Apr-08 JDB Changed TMR_MUX to TMR_POLL for idle support Added POLLMODE, sync_poll() declaration Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks 07-Dec-07 JDB Added BACI device 10-Nov-07 JDB Added 16/32-bit unsigned-to-signed conversions 11-Jan-07 JDB Added 12578A DMA byte packing to DMA structure 28-Dec-06 JDB Added CRS backplane signal as I/O pseudo-opcode Added DMASK32 32-bit mask value 21-Dec-06 JDB Changed MEM_ADDR_OK for 21xx loader support 12-Sep-06 JDB Define NOTE_IOG to recalc interrupts after instr exec Rename STOP_INDINT to NOTE_INDINT (not a stop condition) 30-Dec-04 JDB Added IBL_DS_HEAD head number mask 19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF stop codes 25-Apr-04 RMS Added additional IBL definitions Added DMA EDT I/O pseudo-opcode 25-Apr-03 RMS Revised for extended file support 24-Oct-02 RMS Added indirect address interrupt 08-Feb-02 RMS Added DMS definitions 01-Feb-02 RMS Added terminal multiplexor support 16-Jan-02 RMS Added additional device support 30-Nov-01 RMS Added extended SET/SHOW support 15-Oct-00 RMS Added dynamic device numbers 14-Apr-99 RMS Changed t_addr to unsigned The author gratefully acknowledges the help of Jeff Moffat in answering questions about the HP2100; and of Dave Bryan in adding features and correcting errors throughout the simulator. */ #ifndef _HP2100_DEFS_H_ #define _HP2100_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop and notification codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_IODV 2 /* must be 2 */ #define STOP_HALT 3 /* HALT */ #define STOP_IBKPT 4 /* breakpoint */ #define STOP_IND 5 /* indirect loop */ #define NOTE_INDINT 6 /* indirect intr */ #define STOP_NOCONN 7 /* no connection */ #define STOP_OFFLINE 8 /* device offline */ #define STOP_PWROFF 9 /* device powered off */ #define NOTE_IOG 10 /* I/O instr executed */ /* Memory */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define VA_N_SIZE 15 /* virtual addr size */ #define VASIZE (1 << VA_N_SIZE) #define VAMASK 077777 /* virt addr mask */ #define PA_N_SIZE 20 /* phys addr size */ #define PASIZE (1 << PA_N_SIZE) #define PAMASK (PASIZE - 1) /* phys addr mask */ /* Architectural constants */ #define SIGN32 020000000000 /* 32b sign */ #define DMASK32 037777777777 /* 32b data mask/maximum value */ #define DMAX32 017777777777 /* 32b maximum signed value */ #define SIGN 0100000 /* 16b sign */ #define DMASK 0177777 /* 16b data mask/maximum value */ #define DMAX 0077777 /* 16b maximum signed value */ #define DMASK8 0377 /* 8b data mask/maximum value */ /* Portable conversions (sign-extension, unsigned-to-signed) */ #define SEXT(x) ((int32) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK))) #define INT16(u) ((u) > DMAX ? (-(int16) (DMASK - (u)) - 1) : (int16) (u)) #define INT32(u) ((u) > DMAX32 ? (-(int32) (DMASK32 - (u)) - 1) : (int32) (u)) /* Timers */ #define TMR_CLK 0 /* clock */ #define TMR_POLL 1 /* input polling */ #define POLL_RATE 100 /* poll 100 times per second */ #define POLL_FIRST 1 /* first poll is "immediate" */ #define POLL_WAIT 15800 /* initial poll ~ 10 msec. */ typedef enum { INITIAL, SERVICE } POLLMODE; /* poll synchronization modes */ /* I/O instruction sub-opcodes */ #define soHLT 0 /* halt */ #define soFLG 1 /* set/clear flag */ #define soSFC 2 /* skip on flag clear */ #define soSFS 3 /* skip on flag set */ #define soMIX 4 /* merge into A/B */ #define soLIX 5 /* load into A/B */ #define soOTX 6 /* output from A/B */ #define soCTL 7 /* set/clear control */ /* I/O backplane signals. The IOSIG declarations mirror the I/O backplane signals. These are sent to the device I/O signal handlers for action. Normally, only one signal may be sent at a time. However, the ioCLF signal may be added (arithmetically) to another signal; the handlers will process the other signal first and then the CLF signal. Implementation notes: 1. The first valid signal must have a value > 0, and ioCLF must be enumerated last, so that adding ioCLF produces a result > ioCLF. 2. The signals are structured so that all those that might change the interrupt state are enumerated after ioSIR. The handlers will detect this and add an ioSIR signal automatically. 3. In hardware, the POPIO signal is asserted concurrently with the CRS signal. Under simulation, ioPOPIO implies ioCRS, so the handlers are structured to fall from POPIO handling into CRS handling. It is not necessary to send both signals for a PRESET. 4. In hardware, the SIR signal is generated unconditionally every T5 period to time the setting of the IRQ flip-flop. Under simulation, ioSIR is sent to set the PRL, IRQ, and SRQ signals as indicated by the interface logic. It is necessary to send ioSIR only when that logic indicates a change in one or more of the three signals. 5. In hardware, the ENF signal is unconditionally generated every T2 period to time the setting of the flag flip-flop and to reset the IRQ flip-flop. If the flag buffer flip-flip is set, then flag will be set by ENF. If the flag buffer is clear, ENF will not affect flag. Under simulation, ioENF is sent to set the flag buffer and flag flip-flops. For those interfaces where this action is identical to that provided by STF, the ioENF handler may simply fall into the ioSTF handler. 6. The ioSKF signal is never sent to an I/O device. Rather, it is returned from the device if the SFC or SFS condition is true. 7. A device will receive ioNONE when a HLT instruction is executed, and the H/C bit is clear (i.e., no CLF generated). */ typedef enum { CLEAR, SET } FLIP_FLOP; /* flip-flop type and values */ typedef enum { ioNONE, /* no signal asserted */ ioSKF, /* skip on flag */ ioSFC, /* skip if flag is clear */ ioSFS, /* skip if flag is set */ ioIOI, /* I/O data input */ ioIOO, /* I/O data output */ ioEDT, /* end data transfer */ ioSIR, /* set interrupt request */ ioIAK, /* interrupt acknowledge */ ioCRS, /* control reset */ ioPOPIO, /* power-on preset to I/O */ ioCLC, /* clear control flip-flop */ ioSTC, /* set control flip-flop */ ioENF, /* enable flag */ ioSTF, /* set flag flip-flop */ ioCLF } IOSIG; /* clear flag flip-flop */ /* I/O devices - fixed assignments */ #define CPU 000 /* interrupt control */ #define OVF 001 /* overflow */ #define DMALT0 002 /* DMA 0 alternate */ #define DMALT1 003 /* DMA 1 alternate */ #define PWR 004 /* power fail */ #define PRO 005 /* parity/mem protect */ #define DMA0 006 /* DMA channel 0 */ #define DMA1 007 /* DMA channel 1 */ #define OPTDEV DMALT0 /* start of optional devices */ #define VARDEV (DMA1 + 1) /* start of var assign */ #define M_NXDEV (INT_M (CPU) | INT_M (OVF) | \ INT_M (DMALT0) | INT_M (DMALT1)) #define M_FXDEV (M_NXDEV | INT_M (PWR) | INT_M (PRO) | \ INT_M (DMA0) | INT_M (DMA1)) /* I/O devices - variable assignment defaults */ #define PTR 010 /* 12597A-002 paper tape reader */ #define TTY 011 /* 12531C teleprinter */ #define PTP 012 /* 12597A-005 paper tape punch */ #define CLK 013 /* 12539C time-base generator */ #define LPS 014 /* 12653A line printer */ #define LPT 015 /* 12845A line printer */ #define MTD 020 /* 12559A data */ #define MTC 021 /* 12559A control */ #define DPD 022 /* 12557A data */ #define DPC 023 /* 12557A control */ #define DQD 024 /* 12565A data */ #define DQC 025 /* 12565A control */ #define DRD 026 /* 12610A data */ #define DRC 027 /* 12610A control */ #define MSD 030 /* 13181A data */ #define MSC 031 /* 13181A control */ #define IPLI 032 /* 12566B link in */ #define IPLO 033 /* 12566B link out */ #define DS 034 /* 13037A control */ #define BACI 035 /* 12966A Buffered Async Comm Interface */ #define MPX 036 /* 12792A/B/C 8-channel multiplexer */ #define PIF 037 /* 12620A/12936A Privileged Interrupt Fence */ #define MUXL 040 /* 12920A lower data */ #define MUXU 041 /* 12920A upper data */ #define MUXC 042 /* 12920A control */ /* IBL assignments */ #define IBL_V_SEL 14 /* ROM select */ #define IBL_M_SEL 03 #define IBL_PTR 0000000 /* PTR */ #define IBL_DP 0040000 /* disk: DP */ #define IBL_DQ 0060000 /* disk: DQ */ #define IBL_MS 0100000 /* option 0: MS */ #define IBL_DS 0140000 /* option 1: DS */ #define IBL_MAN 0010000 /* RPL/man boot */ #define IBL_V_DEV 6 /* dev in <11:6> */ #define IBL_OPT 0000070 /* options in <5:3> */ #define IBL_DP_REM 0000001 /* DP removable */ #define IBL_DS_HEAD 0000003 /* DS head number */ #define IBL_LNT 64 /* boot ROM length */ #define IBL_MASK (IBL_LNT - 1) /* boot length mask */ #define IBL_DPC (IBL_LNT - 2) /* DMA ctrl word */ #define IBL_END (IBL_LNT - 1) /* last location */ typedef uint16 BOOT_ROM [IBL_LNT]; /* boot ROM data */ /* Dynamic device information table */ typedef uint32 IODISP (uint32 select_code, IOSIG signal, uint32 data); /* I/O signal dispatch function */ typedef struct { uint32 devno; /* device select code */ IODISP *iot; /* pointer to I/O signal dispatcher */ } DIB; /* I/O macros */ #define IOBASE(S) ((S) > ioCLF ? (S) - ioCLF : (S)) /* base signal from compound signal */ #define INT_V(x) ((x) & 037) /* device bit position */ #define INT_M(x) (1u << INT_V (x)) /* device bit mask */ #define setSKF(B) data = (uint32) ((B) ? ioSKF : ioNONE) #define setPRL(S,B) dev_prl[(S)/32] = dev_prl[(S)/32] & ~INT_M (S) | (((B) & 1) << INT_V (S)) #define setIRQ(S,B) dev_irq[(S)/32] = dev_irq[(S)/32] & ~INT_M (S) | (((B) & 1) << INT_V (S)) #define setSRQ(S,B) dev_srq[(S)/32] = dev_srq[(S)/32] & ~INT_M (S) | (((B) & 1) << INT_V (S)) #define setstdSKF(N) setSKF ((base_signal == ioSFC) && !N ## _flag || \ (base_signal == ioSFS) && N ## _flag) #define setstdPRL(S,N) setPRL ((S), !(N ## _control & N ## _flag)); #define setstdIRQ(S,N) setIRQ ((S), N ## _control & N ## _flag & N ## _flagbuf); #define setstdSRQ(S,N) setSRQ ((S), N ## _flag); #define PRL(S) ((dev_prl[(S)/32] >> INT_V (S)) & 1) #define IRQ(S) ((dev_irq[(S)/32] >> INT_V (S)) & 1) #define SRQ(S) ((dev_srq[(S)/32] >> INT_V (S)) & 1) #define IOT_V_REASON 16 #define IORETURN(F,V) ((F) ? (V) : SCPE_OK) /* stop on I/O error */ /* CPU state */ extern uint32 SR; /* S register (for IBL) */ extern uint32 dev_prl [2], dev_irq [2], dev_srq [2]; /* I/O signal vectors */ /* Simulator state */ extern FILE *sim_deb; extern FILE *sim_log; extern int32 sim_step; extern int32 sim_switches; /* CPU functions */ extern t_stat ibl_copy (const BOOT_ROM rom, int32 dev); extern void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp); /* System functions */ extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); extern const char *fmt_char (uint8 ch); extern t_stat hp_setdev (UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat hp_showdev (FILE *st, UNIT *uptr, int32 val, void *desc); /* Standard device functions */ extern int32 sync_poll (POLLMODE poll_mode); #endif simh-3.8.1/HP2100/hp2100_diag.txt0000644000175000017500000031221711046650654014127 0ustar vlmvlm SIMH/HP 21XX DIAGNOSTICS PERFORMANCE ==================================== Last update: 2008-08-07 The HP 24396 diagnostic suite has been run against the SIMH HP 21xx simulation. Diagnostic programs were obtained from two magnetic tapes, HP 24396-13601 Rev. 1713 and Rev. 2326, plus a few standalone paper tapes. For each diagnostic, the recommended standard tests were selected, plus any available optional tests that broadened the test coverage. Except where noted in the individual diagnostic reports, the test system configuration is the default SIMH configuration with these alterations: * All I/O devices are enabled. * The CPU is configured as a 1000-E with 128KW of memory. Detailed diagnostic configuration, operation, and results are given after the summary table. These may be used to duplicate the diagnostic results. The results of the diagnostic runs are summarized below: Date SIMH DSN Diagnostic Name Code Vers. Result ------ --------------------------------------- ---- ----- ------------- 000200 Diagnostic Configurator Pretest 1627 3.2-3 Passed 101100 Memory Reference Instruction Group 1624 3.2-3 Passed 101001 Alter-Skip Instruction Group 1431 3.2-3 Passed 101002 Shift-Rotate Instruction Group 1431 3.2-3 Passed 102200 Core Memory (2100/16/15/14) 1624 3.3-0 Passed 102104 Semiconductor Memory (21MX) 1644 3.2-3 Passed 101004 EAU Instruction Group 1431 3.2-3 Passed 101207 Floating Point Instruction Group 1551 3.2-3 Passed 102001 Memory Protect 1431 3.7-0 Passed 102002 Memory Parity Check 1431 - No simulation 102305 Memory Protect/Parity Error 1705 3.3-0 Partial 101206 Power Fail/Auto Restart 1635 - No simulation 141203 I/O Instruction Group - I/O Extender 2326 3.2-3 Passed 143300 General Purpose Register 1813 3.2-3 Passed 101105 Direct Memory Access (2114/15/16) 1502 3.7-0 Passed 101220 Direct Memory Access (2100/21MX) 1705 3.2-3 Passed 101011 Extended Instruction Group (Index) 1432 3.2-3 Passed 101112 Extended Instruction Group (Word, Byte) 1728 3.2-3 Passed 101110 2100 Fast FORTRAN Package 1632 3.4-0 Partial 101213 M/E-Series Fast FORTRAN Package 1 1822 3.4-0 Passed 101114 M/E-Series Fast FORTRAN Package 2 1632 3.4-0 Passed 101121 F-Series FPP/SIS/FFP 1926 3.7-0 Passed 101016 2000/Access Comm Processor for 2100 1526 3.2-3 Partial 102103 Memory Expansion Unit 1830 3.2-3 Passed 102103 Semiconductor Memory Microcoded 21MX 1644 - No simulation 103301 Time Base Generator 1830 3.2-3 Passed 103115 12936 Privileged Interrupt 1643 3.8-1 Passed 143300 12620 Breadboard Interface/Priv Int 1813 3.8-1 Passed 103105 12908/12978 WCS 256 Word 1502 - No simulation 103023 13197 WCS 1024 Word 1640 - No simulation 103207 12889 Hardwired Serial Interface 1717 - No simulation 103122 59310 Interface Bus Interface 1728 - No simulation 103003 12587 Asynchronous Data Set Interface 1552 - No simulation 103110 12920 Asynchronous Multiplexer (Data) 1805 3.7-1 Passed 103011 12920 Asynchronous Multiplexer (Cntl) 1444 3.7-1 Passed 103012 12621 Synchronous Data Set (Receive) 1532 - No simulation 103013 12622 Synchronous Data Set (Send) 1532 - No simulation 103116 12967 Synchronous Interface 1438 - No simulation 103017 12966 Asynchronous Data Set 1519 3.8-0 Passed 103121 12968 Asynchronous Comm. Interface 1602 - No simulation 103024 12821 ICD Disc Interface 1928 - No simulation 104000 2600 Keyboard Display Terminal 1615 - No simulation 104003 Teleprinter 1509 3.2-3 Partial 144105 2762A/B Terminal (Terminet) 1546 - No simulation 104007 2615 Video Terminal 1347 - No simulation 104011 2640 Interactive Terminal 1502 - No simulation 104012 2644 Mini Data Station (non CTU) 1542 - No simulation 104013 2644 Mini Data Station (CTU Only) 1542 - No simulation 104017 92900 Terminal Subsystem (3070, 40280) 1643 - No simulation 105000 2610/14 Line Printer 1451 - No simulation 105101 2767 Line Printer 1611 3.3-0 Passed 105102 2607 Line Printer 1446 3.3-0 Passed 145103 2613/17/18 Line Printer 1633 - No simulation 105104 9866 Line Printer 1541 - No simulation 105106 2631 Printer 1913 - No simulation 105107 2635 Printing Terminal 1913 - No simulation 105105 2608 Line Printer 2026 - No simulation 111001 Disc File (2883) 1451 3.3-0 Partial 111104 12732 Flexible Disc Subsystem 1708 - No simulation 151302 7900/01 Cartridge Disc 1805 3.2-3 Partial 151403 7905/06/20/25 Disc 1805 3.3-1 Partial 104117 92900 Terminal Subsystem 1814 - No simulation 112200 9-Track Magnetic Tape (7970, 13181/3) 2040 3.2-3 Partial 112102 7/9-Track Magnetic Tape (13184 Interf.) 1629 - No simulation 010000 Diagnostic Cross Link 1627 - No simulation 011000 7900/05/20 Disc Initialization 1627 - No simulation 146200 Paper Tape Reader/Punch 1725 3.2-3 Passed 107000 Digital Plotter Interface (CALCOMP) 1540 - No simulation 113100 2892 Card Reader 1537 - No simulation 113001 2894 Card Reader Punch 1728 - No simulation 113003 7261 Card Reader 1546 - No simulation 103006 12909B PROM Writer 1420 - No simulation The following stand-alone diagnostics were run for devices not supported by the 24396 suite: Date SIMH Part Number DSN Diagnostic Name Code Vers. Result ----------- ------ ------------------------------------ ---- ----- ---------- 13207-16001 101217 2000/Access Comm Processor for 21MX 1728 3.2-3 Passed 20433-60001 -- HP 3030 Magnetic Tape Subsystem -- - Not tested 22682-16017 177777 HP 2100 Fixed Head Disc/Drum (277x) 1612 3.3-0 Passed 24197-60001 -- 12875 Processor Interconnect Cable B 3.7-1 Passed 24203-60001 -- HP2100A Cartridge Disc Memory (2871) A 3.3-0 Partial The following online diagnostics were run for devices not supported by the offline diagnostics: Date Host Date SIMH Part Number Diagnostic Name Code Op. Sys. Code Vers. Result ----------- ------------------------------- ---- -------- ---- ----- ---------- 92067-16013 Extended Memory Area Firmware 1805 RTE-IVB 5010 3.8-0 Passed 92084-16423 Virtual Memory Area Firmware 2121 RTE-6/VM 6200 3.8-0 Passed 12824-16002 Vector Instruction Set Firmware 2026 RTE-IVB 5010 3.8-0 Passed 12829-16006 Vector Instruction Set Firmware 2226 RTE-6/VM 6200 3.8-0 Passed 92835-16006 SIGNAL/1000 Firmware Diagnostic 2040 RTE-6/VM 6200 3.8-0 Passed The "SIMH Version" is the version number of the earliest SIMH system that was tested with the given diagnostic. Earlier versions may or may not work properly. The "Result" column indicates the level of success in passing the given diagnostic: Term Meaning ------------- --------------------------------------------------------------- Passed All of the standard tests relevant to the hardware model passed without error. Optional "utility" tests, where present, were not run unless they broadened the test coverage. Partial One or more of the standard tests relevant to the hardware model were either excluded or failed as expected, due to known limitations in the simulation, e.g., the lack of "defective cylinder" flags in a disc simulation. Failed One or more of the standard tests relevant to the hardware model failed unexpectedly. Not tested The diagnostic has not been run with the device simulation. No simulation A simulation of the given device does not exist. See the "Test Notes" associated with each diagnostic report below for details on subsets, limitations, or errors encountered. 24396 DIAGNOSTIC SUITE DETAILED EXECUTION AND RESULTS ===================================================== Each execution note below presumes that the target diagnostic has been loaded. For all runs other than the diagnostic configurator pretest, the configurator was used in automatic mode to load the target diagnostic via its Diagnostic Serial Number (DSN), as follows: sim> attach -r MSC0 24396-13601_Rev-2326.abin.tape sim> deposit S 000000 sim> boot MSC0 HALT instruction 102077 sim> deposit A [DSN] sim> deposit B 000000 sim> deposit S 113011 sim> reset sim> go 100 For the pretest, only the first three commands above were used to load the diagnostic configurator. -------------------------------------------- DSN 000200 - Diagnostic Configurator Pretest -------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> deposit S 000011 sim> reset sim> go 2 TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------- DSN 101100 - Memory Reference Instruction Group ----------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. ------------------------------------ DSN 101001 - Alter-Skip Instructions ------------------------------------ TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. -------------------------------------- DSN 101002 - Shift-Rotate Instructions -------------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------- DSN 102200 - Core Memory (2100/16/15/14) ---------------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> set CPU 2100 sim> set CPU 32K sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. --------------------------------- DSN 102104 - Semiconductor Memory --------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> deposit S 001000 sim> reset sim> go 100 HALT instruction 102075 sim> deposit A 054777 sim> deposit S 000000 sim> reset sim> go TEST REPORT: HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: The standard tests 00-10, plus optional tests 13, 14, and 16 are executed. ---------------------------------- DSN 101004 - EAU Instruction Group ---------------------------------- TESTED DEVICE: CPU (hp2100_cpu1.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: 2100 SERIES EAU DIAGNOSTIC END OF PASS 1 HALT instruction 102077 TEST RESULT: Passed. --------------------------------------------- DSN 101207 - Floating Point Instruction Group --------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu2.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: 2100-21MX FLOATING POINT DIAGNOSTIC PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------- DSN 102001 - Memory Protect --------------------------- TESTED DEVICE: MP (hp2100_cpu.c) CONFIGURATION: sim> set CPU 2100 sim> set CPU 32K sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: HP 2100 SERIES MEMORY PROTECT DIAGNOSTIC H07. PRESS PRESET (EXT/INT), RUN HALT instruction 102007 sim> reset sim> go H13. PRESS HALT, PRESET(INT), RUN IN LESS THAN 15 SEC. [CTRL+E] Simulation stopped sim> reset sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------- DSN 102305 - Memory Protect/Parity Error ---------------------------------------- TESTED DEVICE: MP (hp2100_cpu.c) CONFIGURATION: sim> set LPS diag sim> deposit S 140014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> reset sim> go MEMORY PROTECT-PARITY ERROR DIAGNOSTIC HALT instruction 102075 sim> deposit A 035777 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H061 POWER DOWN COMPUTER INSTALL JUMPERS PER TABLE 3-5 IN MOD POWER UP COMPUTER HALT instruction 102061 sim> set MP jsbout,intout,sel1in sim> go H314 PRESS HALT,PRESET AND RUN WITHIN 30 SECONDS [CTRL+E] Simulation stopped sim> reset sim> go PASS 000001 H062 POWER DOWN COMPUTER SET JUMPERS TO INITIAL SETTINGS PER TABLE 3-1 IN MOD POWER UP COMPUTER HALT instruction 102062 sim> set MP jsbin,intin,sel1out sim> go HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Test 10 is not executed. This test verifies parity error detection. This feature is not simulated. ---------------------------------- DSN 141103 - I/O Instruction Group ---------------------------------- TESTED DEVICE: CPU (hp2100_cpu.c) CONFIGURATION: sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: I-O INSTRUCTION GROUP & CHANNEL OR EXTENDER DIAGNOSTIC DSN 141103 H033 SET S-REG TO 125252, PRESS RUN HALT instruction 102033 sim> deposit S 125252 sim> go H033 SET S-REG TO 052525, PRESS RUN HALT instruction 102033 sim> deposit S 052525 sim> go H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ------------------------------------- DSN 143300 - General Purpose Register ------------------------------------- TESTED DEVICE: LPS (hp2100_lps.c) CONFIGURATION: sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: GENERAL PURPOSE REGISTER DIAGNOSTIC, DSN 143300 H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BASIC I-O COMPLETED PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------------- DSN 101105 - Direct Memory Access (2114/15/16) ---------------------------------------------- TESTED DEVICE: DMA0/DMA1 (hp2100_cpu.c) CONFIGURATION: sim> set CPU 2116 sim> set CPU 16K sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H0. START DMA DIAGNOSTIC HALT instruction 102027 sim> reset sim> go H77. END DIAGNOSTIC HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------------- DSN 101105 - Direct Memory Access (2114/15/16) ---------------------------------------------- TESTED DEVICE: DMA0 (hp2100_cpu.c) CONFIGURATION: sim> set CPU 2114 sim> set CPU 16K sim> set LPS diag sim> deposit 003612 004036 sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H0. START DMA DIAGNOSTIC HALT instruction 102027 sim> reset sim> go H77. END DIAGNOSTIC HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: Diagnostic tapes 24322-16001 Revs. 1431 and 1502 have a bug in Test 17. Test 17 checks byte unpacking for output transfers on 2115 and 2116 computers. The documentation says that it is not performed on 2114 computers. The prolog for Test 17 does check if packing is supported, and if not, it skips the test. However, the target of the skip is not the entry to Test 20 but is instead the short version of Test 17. When DMA channel 2 is tested in Test 17, it fails with: E53. D2 FLG CLR E71. D2 OUT. GOOD=000001, BAD=000000 ...because there is no DMA channel 2 on the 2114. We work around this problem by setting the jump target to Test 20 with "deposit 003612 004036". --------------------------------------------- DSN 101220 - Direct Memory Access (2100/21MX) --------------------------------------------- TESTED DEVICE: DCPC0/DCPC1 (hp2100_cpu.c) CONFIGURATION: sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: DMA-DCPC DIAGNOSTIC H324 PRESS PRESET AND RUN HALT instruction 107024 sim> reset sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------- DSN 101011 - Extended Instruction Group (Index) ----------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu2.c) CONFIGURATION: sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: EIG (INDEX) DIAGNOSTIC PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------------------------------- DSN 101112 - Extended Instruction Group (Word, Byte, Bit) --------------------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu2.c) CONFIGURATION: sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: EIG (WORD,BYTE,BIT) DIAGNOSTIC DSN 101112 PASS 000001 HALT instruction 102077 TEST RESULT: Passed. -------------------------------------- DSN 101110 - 2100 Fast FORTRAN Package -------------------------------------- TESTED DEVICE: CPU (hp2100_cpu3.c) CONFIGURATION: sim> set CPU 2100 sim> set CPU 32K sim> set CPU FFP sim> deposit S 000013 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: START 2100A-S FFP DIAGNOSTIC H030 .GOTO TEST H050 .ENTR TEST H060 .ENTP TEST H100 .SETP TEST H110 ..MAP TEST H120 SNGL TEST H130 DBLE TEST H140 .XADD TEST TEST 07 E142 NOT INTERRUPTIBLE HALT instruction 106042 sim> go H150 .XSUB TEST H160 .XMPY TEST TEST 11 E162 NOT INTERRUPTIBLE HALT instruction 106062 sim> go H200 .XDIV TEST H210 .DFER TEST H220 .XFER TEST PASS 000001 HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Tests 07 and 11 test the interruptibility of the .XADD and .XMPY instructions. These features are not simulated. ---------------------------------------------- DSN 101213 - M/E-Series Fast FORTRAN Package 1 ---------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu3.c) CONFIGURATION: sim> set CPU FFP sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: START 21MX FFP DIAGNOSTIC 1 H110 ..MAP TEST H120 SNGL TEST H130 DBLE TEST H210 .DFER TEST H220 .XFER TEST H230 PWR2 TEST H240 .PACK TEST H250 FLUN TEST H260 .XPAK TEST H300 .XCOM TEST H310 ..DCM TEST H320 DDINT TEST H330 .CFER TEST PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------------- DSN 101114 - M/E-Series Fast FORTRAN Package 2 ---------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu3.c) CONFIGURATION: sim> set CPU FFP sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: START 21MX FFP DIAGNOSTIC 2 H030 .GOTO TEST H050 .ENTR TEST H060 .ENTP TEST H100 .SETP TEST H115 XADD TEST H125 XSUB TEST H135 XMPY TEST H140 .XADD TEST H150 .XSUB TEST H160 .XMPY TEST H200 .XDIV TEST H215 XDIV TEST PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------- DSN 101121 - F-Series FPP/SIS/FFP --------------------------------- TESTED DEVICE: CPU (hp2100_cpu3.c) CONFIGURATION: sim> set CPU 1000-F sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: FPP-SIS-FFP DIAGNOSTIC DSN 101121 BEGIN BASIC CONTROL TEST OVFL TEST CONF TEST BASE RETN TEST SIS1 RETN TEST SIS2 RETN TEST SIS3 RETN TEST FPP1 RETN TEST FFP2 RETN TEST FFP3 RETN TEST END BASIC CONTROL TEST LONG PASSES FIXS TEST FIXD TEST FLTS TEST FLTD TEST ADD TEST SUB TEST MPY TEST DIV TEST ACC TEST SIS1 TEST SIS2 TEST SIS3 TEST FFP1 TEST FFP2 TEST FFP3 TEST PASS 00001 HALT instruction 102077 TEST RESULT: Passed. ------------------------------------------------ DSN 101016 - 2000/Access Comm Processor for 2100 ------------------------------------------------ TESTED DEVICE: CPU (hp2100_cpu2.c) CONFIGURATION: sim> set CPU 2100 sim> set CPU 32K sim> set CPU IOP sim> deposit S 000013 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: 2100 2000-ACCESS COMM. PROC. FIRMWARE DIAGNOSTIC H030 CRC TEST H040 ENQ, DEQ AND PENQ TESTS H060 IAL TEST H110 READF, SAVE AND RESTR TESTS H120 LAI AND SAI TESTS H130 PFREX TEST H140 PFREI TEST H150 PFRIO TEST H160 STORE-LOAD BYTE, TRSLT AND BYTE MOVE TEST TEST 10 E165 TRSLT NOT INTERRUPTIBLE HALT instruction 106065 sim> go H230 WORD MOVE TEST TEST 11 E234 WORD MOVE NOT INTERRUPTIBLE HALT instruction 103034 sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Tests 10 and 11 test the interruptibility of the TRSLT and MWORD instructions. These features are not simulated. ---------------------------------- DSN 102103 - Memory Expansion Unit ---------------------------------- TESTED DEVICE: CPU (hp2100_cpu2.c) CONFIGURATION: sim> set LPS diag sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> reset sim> go MEMORY EXPANSION MODULE DIAGNOSTIC, DSN = 102103 HALT instruction 102075 sim> deposit A 177777 sim> deposit B 000037 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H115 PRESS HALT-PRESET-RUN IN LESS THAN 10 SECONDS [CTRL+E] Simulation stopped sim> reset sim> go H117 PRESET TEST COMPLETE H327 00128K OF CONTIGUOUS MEMORY INSTALLED H024 PRESS PRESET, RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP PASS 000001 HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: The standard tests 00-22 plus optional tests 23 and 24 are executed. Test 25 (Register Crusher Test) is not executed. This test is designed specifically for the RAM chips present on the hardware and isn't relevant to simulation. Test 23 cannot be run with more than 256K of memory, or the diagnostic will be corrupted. There is a fixed-size table in revision 1830 that overflows if memory size is greater than 256K. -------------------------------- DSN 103301 - Time Base Generator -------------------------------- TESTED DEVICE: CLK (hp2100_stddev.c) CONFIGURATION: sim> set CLK diag sim> deposit S 100013 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: TBG DIAGNOSTIC, DSN = 103301 H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H030 TEST 01 IN PROGRESS H030 TEST 02 IN PROGRESS H030 TEST 03 IN PROGRESS H030 TEST 04 IN PROGRESS H030 TEST 05 IN PROGRESS H030 TEST 06 IN PROGRESS H030 TEST 07 IN PROGRESS H030 TEST 10 IN PROGRESS H030 TEST 11 IN PROGRESS H030 TEST 12 IN PROGRESS PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------- DSN 103115 - Privileged Interrupt --------------------------------- TESTED DEVICE: PIF (hp2100_pif.c) CONFIGURATION: sim> set PIF 12936A sim> set LPT DEV=44 sim> deposit S 004414 sim> reset sim> go 100 HALT instruction 102070 sim> deposit S 000037 sim> go HALT instruction 102074 sim> deposit S 000000 sim> go TEST REPORT: 12936 PRIVILEGED INTERRUPT CARD DIAGNOSTIC H032 PRESS PRESET(EXT), RUN HALT instruction 102032 sim> reset sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ------------------------------------- DSN 143300 - General Purpose Register ------------------------------------- TESTED DEVICE: PIF (hp2100_pif.c) CONFIGURATION: sim> set PIF 12620A sim> deposit S 000037 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> go TEST REPORT: GENERAL PURPOSE REGISTER DIAGNOSTIC, DSN 143300 HALT instruction 102075 sim> deposit A 000001 sim> deposit S 000000 sim> go H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BASIC I-O COMPLETED PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------------------------- DSN 103110 - 12920A Asynchronous Multiplexer (Data) --------------------------------------------------- TESTED DEVICE: MUX, MUXL (hp2100_mux.c) CONFIGURATION: sim> set MUX DIAG sim> deposit S 004040 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: ASYNC MULTIPLEXER DATA BOARD DIAGNOSTIC DSN 103110 H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP PASS 000001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------------------------- DSN 103011 - 12920A Asynchronous Multiplexer (Cntl) --------------------------------------------------- TESTED DEVICE: MUXM (hp2100_mux.c) CONFIGURATION: sim> set MUX DIAG sim> deposit S 004042 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: ASYNC MULTIPLEXER CONTROL BOARD DIAGNOSTIC H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ---------------------------------------- DSN 103017 - 12966 Asynchronous Data Set ---------------------------------------- TESTED DEVICE: BACI (hp2100_baci.c) CONFIGURATION: sim> set BACI realtime sim> set BACI diag sim> deposit S 000035 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: BUFFERED ASYNC COMM INTFC DIAG H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP PASS 000001 TEST RESULT: Passed. ------------------------ DSN 104003 - Teleprinter ------------------------ TESTED DEVICE: TTY (hp2100_stddev.c) CONFIGURATION: sim> deposit S 000011 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> reset sim> go START TTY DIAGNOSTIC HALT instruction 102075 sim> deposit A 000373 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H030 TURN TTY PUNCH ON PRESS RUN HALT instruction 102030 sim> attach TTY2 scratch.2752.punch sim> go H045 TURN TTY PUNCH OFF PRESS RUN HALT instruction 102045 sim> detach TTY2 sim> deposit S 100000 sim> go HALT instruction 102076 sim> go !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ HALT instruction 102076 sim> set console WRU=003 sim> go INPUT THE FOLLOWING: 1 2 3 4 5 6 7 8 9 0 : - Q W E R T Y U I O P A S D F G H J K L ; Z X C V B N M , . / SHIFT+ ! " # $ % & ' ( ) * = _ @ + ^ < > ? CNTRL+ WRU TAPE NTAP XOFF EOT RU BELL TAB VT FORM RBOT CR LF HALT instruction 102076 sim> set console WRU=005 sim> go INPUT ANY KEY T H I S 040 I S 040 A 040 T E S T [CTRL+E] Simulation stopped sim> deposit S 000002 sim> go [CTRL+E] Simulation stopped sim> deposit S 000000 sim> go H044 INPUT TERMINATED ECHO MODE ANY INPUT IS ECHOED THIS IS A TEST [CTRL+E] Simulation stopped sim> deposit S 000002 sim> go [CTRL+E] Simulation stopped sim> deposit S 100000 sim> go H044 INPUT TERMINATED HALT instruction 102076 sim> deposit TTY TTIME 158000 sim> deposit S 000000 sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Test 2 is not executed. This test uses the teleprinter paper tape reader. This feature is not simulated. Test 7 is the oscillator tolerance test, so the TTY TTIME is set for realistic timing. ------------------------------ DSN 105101 - 2767 Line Printer ------------------------------ TESTED DEVICE: LPS (hp2100_lps.c) CONFIGURATION: sim> set LPS realtime sim> attach LPS scratch.2767.printer sim> deposit S 000014 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: 2767 L.P. DIAGNOSTIC H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H035 TURN OFF L.P. POWER HALT instruction 102035 sim> set LPS poweroff sim> go H036 TURN ON L.P. POWER HALT instruction 102036 sim> set LPS poweron sim> go H033 PUT L.P. ON-LINE HALT instruction 102033 sim> set LPS online sim> go H034 MASTER CLEAR L.P. HALT instruction 102034 sim> set LPS offline sim> go H033 PUT L.P. ON-LINE HALT instruction 102033 sim> set LPS online sim> go H040 PUT L.P. OFF-LINE. TOGGLE TOP-OF-FORM SWITCH HALT instruction 102040 sim> set LPS offline sim> go H033 PUT L.P. ON-LINE HALT instruction 102033 sim> set LPS online sim> go H041 PUT L.P. OFF-LINE. TOGGLE PAPER-STEP 5 TIMES HALT instruction 102041 sim> set LPS offline sim> go H033 PUT L.P. ON-LINE HALT instruction 102033 sim> set LPS online sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: The simulation provides no manual Master Clear, Top of Form, or Paper Step functions, so these are merely presumed above. ------------------------------ DSN 105102 - 2607 Line Printer ------------------------------ TESTED DEVICE: LPT (hp2100_lpt.c) CONFIGURATION: sim> attach LPT scratch.2607.printer sim> deposit S 100015 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> reset sim> go 2607 LINE PRINTER DIAGNOSTIC HALT instruction 102075 sim> deposit A 000377 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H040 PWR OFF LP,PRESS RUN HALT instruction 102040 sim> set LPT poweroff sim> go H041 PWR ON LP,READY LP,PRESS RUN HALT instruction 102041 sim> set LPT poweron sim> go H042 PRINT SW OFF,PRESS RUN HALT instruction 102042 sim> set LPT offline sim> go H043 PRINT SW ON,PRESS RUN HALT instruction 102043 sim> set LPT online sim> go H044 OPEN PLATEN,PRESS RUN HALT instruction 102044 sim> set LPT offline sim> go H045 CLOSE PLATEN,PRESS RUN HALT instruction 102045 sim> set LPT online sim> go H046 REMOVE PAPER FROM LP,PRESS RUN HALT instruction 102046 sim> detach LPT sim> go H047 RESTORE PAPER IN LP, READY LP,PRESS RUN HALT instruction 102047 sim> attach LPT scratch.2607.printer sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: The standard tests 00-07 are executed. Test 08 (operator design) is selected as a standard test in this diagnostic only and so is excluded manually. ----------------------------------------------------- DSN 111001 - HP2100A Disc File (2883) (multiple unit) ----------------------------------------------------- TESTED DEVICE: DQ (hp2100_dq.c) CONFIGURATION: sim> attach DQC0 scratch.U0.2883.disc sim> attach DQC1 scratch.U1.2883.disc sim> reset sim> go 100 H0 HP 2100 SERIES DISC FILE(2883) DIAGNOSTIC H72 ENTER SELECT CODES,DMA CHANNEL IN SWITCH REGISTER,PRESS RUN HALT instruction 107001 sim> deposit S 002411 sim> go H1 ENTER PROGRAM OPTIONS IN SWITCH REGISTER,PRESS RUN HALT instruction 107077 sim> deposit S 000400 sim> go TEST REPORT: H65 PASS 0001 H65 PASS 0002 [CTRL+E] Simulation stopped TEST RESULT: Passed. TEST NOTES: Two passes are required to test all head/unit combinations. -------------------------------------------------------- DSN 111001 - HP2100A Disc File (2883) (user interaction) -------------------------------------------------------- TESTED DEVICE: DQ (hp2100_dq.c) CONFIGURATION: sim> attach DQC0 scratch.U0.2883.disc sim> reset sim> go 100 H0 HP 2100 SERIES DISC FILE(2883) DIAGNOSTIC H72 ENTER SELECT CODES,DMA CHANNEL IN SWITCH REGISTER,PRESS RUN HALT instruction 107001 sim> deposit S 002411 sim> go H1 ENTER PROGRAM OPTIONS IN SWITCH REGISTER,PRESS RUN HALT instruction 107077 sim> deposit S 000142 sim> go TEST REPORT: H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> go H37 READ ADDRESS IN S0 E47 DATA WORD 0000 IS 000000 SHOULD BE 100000 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0002 UNIT 00 HALT instruction 102001 sim> go H37 READ ADDRESS IN S0 E47 DATA WORD 0000 IS 000000 SHOULD BE 100001 H51 CYL 0001 HEAD 01 SECTOR 00 WORD COUNT 0002 UNIT 00 HALT instruction 102001 sim> go H33 WRITE DEFECTIVE TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H41 READ DEFECTIVE TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> go H33 WRITE DEFECTIVE TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H41 READ DEFECTIVE TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H42 WRITE PROTECTED TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H36 WRITE ADDRESS IN S0 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0046 UNIT 00 HALT instruction 102001 sim> go H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> go H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> go H70 DISABLE UNIT 0,PUSH RUN HALT instruction 102002 sim> set DQC0 unloaded sim> go H40 ENABLE UNIT 0 [CTRL+E] Simulation stopped sim> set DQC0 loaded sim> go H71 PRESS PRESET THEN PRESS RUN HALT instruction 102002 sim> deposit S 010140 sim> reset sim> go H74 SHORT PASS H65 PASS 0001 HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Step 0 tests the FORMAT OVERRIDE switch, the use of the flagged track bit to indicate a protected or defective track, and the ability to write a sector address field that differs from the sector location to indicate track sparing. These features are not simulated. ---------------------------------------------------------- DSN 151302 - 7900/01 Cartridge Disc Memory (multiple unit) ---------------------------------------------------------- TESTED DEVICE: DP (hp2100_dp.c) CONFIGURATION: sim> attach DPC0 scratch.U0.7900.disc sim> attach DPC1 scratch.U1.7900.disc sim> attach DPC2 scratch.U2.7900.disc sim> attach DPC3 scratch.U3.7900.disc sim> deposit S 000022 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000004 sim> reset sim> go H0 7900/7901 CARTRIDGE DISC MEMORY DIAGNOSTIC H24 CYLINDER TABLE 000,001,002,004,008,016,032,064,128,202 H25 WISH TO CHANGE? NO H27 PATTERN TABLE 000000 177777 125252 052525 007417 170360 162745 163346 155555 022222 H25 WISH TO CHANGE? NO H62 TYPE A FOR HEADS 0,1;B FOR 2,3;C FOR ALTERNATELY 0,1 THEN 2,3 C H23 00020 ERRORS/PASS ALLOWED H25 WISH TO CHANGE? NO H37 UNIT TABLE/ 01 DRIVE(S); 0 H25 WISH TO CHANGE? YES H34 ENTER UNIT NUMBERS(0-3)SEPARATED BY COMMAS 0,1,2,3 H37 UNIT TABLE/ 04 DRIVE(S); 0 1 2 3 H25 WISH TO CHANGE? [CTRL+E] Simulation stopped sim> deposit S 000000 sim> go NO TEST REPORT: H65 LONG PASS 0001,HEADS 0/1,UNIT 00, 0000 ERRORS H65 LONG PASS 0002,HEADS 0/1,UNIT 01, 0000 ERRORS H65 LONG PASS 0003,HEADS 0/1,UNIT 02, 0000 ERRORS H65 LONG PASS 0004,HEADS 0/1,UNIT 03, 0000 ERRORS,MULTI-DRIVE H65 LONG PASS 0005,HEADS 2/3,UNIT 00, 0000 ERRORS H65 LONG PASS 0006,HEADS 2/3,UNIT 01, 0000 ERRORS H65 LONG PASS 0007,HEADS 2/3,UNIT 02, 0000 ERRORS H65 LONG PASS 0008,HEADS 2/3,UNIT 03, 0000 ERRORS,MULTI-DRIVE [CTRL+E] Simulation stopped TEST RESULT: Passed. TEST NOTES: Eight passes are required to test all head/unit combinations. ------------------------------------------------------------- DSN 151302 - 7900/01 Cartridge Disc Memory (user interaction) ------------------------------------------------------------- TESTED DEVICE: DP (hp2100_dp.c) CONFIGURATION: sim> attach DPC0 scratch.U0.7900.disc sim> deposit S 000022 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000160 sim> reset sim> go TEST REPORT: H0 7900/7901 CARTRIDGE DISC MEMORY DIAGNOSTIC H66 SET OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H46 READ IN STEP 04 E64 STATUS IS 000000 SHOULD BE 000010 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H22 CYCLIC CHECK IN STEP 04 E64 STATUS IS 000000 SHOULD BE 000010 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0001 UNIT 00 HALT instruction 102001 sim> go H67 CLEAR OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H46 READ IN STEP 07 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H45 WRITE IN STEP 08 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H36 INITIALIZE DATA IN STEP 09 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 6144 UNIT 00 HALT instruction 102001 sim> go H66 SET OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H67 CLEAR OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H70 UNLOAD UNIT 0,PUSH RUN HALT instruction 102002 sim> set DPC0 unloaded sim> go H40 PROTECT U/D THEN READY UNIT 0 [CTRL+E] Simulation stopped sim> set DPC0 locked sim> set DPC0 loaded sim> go H41 CLEAR U/D PROTECT,LOAD,PUSH RUN HALT instruction 102002 sim> set DPC0 writeenabled sim> go H71 PRESS PRESET(S) THEN PRESS RUN HALT instruction 102002 sim> reset sim> go H65 SHORT PASS 0001,HEADS 0/1,UNIT 00, 0005 ERRORS [CTRL+E] Simulation stopped TEST RESULT: Partially passed. TEST NOTES: Steps 4, 7, 8, and 9 test the defective and protected cylinder bits and the FORMAT switch. These features are not simulated. ----------------------------------------------- DSN 151403 - 7905/06/20/25 Disc (multiple unit) ----------------------------------------------- TESTED DEVICE: DS (hp2100_ds.c) CONFIGURATION: sim> set DS0 7905 sim> set DS1 7906 sim> set DS2 7920 sim> set DS3 7925 sim> set DS4 7905 sim> set DS5 7906 sim> set DS6 7920 sim> set DS7 7925 sim> attach DS0 scratch.U0.7905.disc sim> attach DS1 scratch.U1.7906.disc sim> attach DS2 scratch.U2.7920.disc sim> attach DS3 scratch.U3.7925.disc sim> attach DS4 scratch.U4.7905.disc sim> attach DS5 scratch.U5.7906.disc sim> attach DS6 scratch.U6.7920.disc sim> attach DS7 scratch.U7.7925.disc sim> deposit S 000034 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000004 sim> reset sim> go H0 79XX/13037 DISC MEMORY DIAGNOSTIC H37 UNIT TABLE: 01 DRIVE(S); 0 H25 WISH TO CHANGE? YES H34 ENTER UNIT NUMBERS(0-7)SEPARATED BY COMMAS 0,1,2,3,4,5,6,7 H37 UNIT TABLE: 08 DRIVE(S); 0 1 2 3 4 5 6 7 H25 WISH TO CHANGE? NO ENTER:(U)NIT,(?) ERRS,(H)EAD,(O)UTPUT,(P)ATT,(S)OFT,(C)YL,(M)CPU,(E)XIT H H62 HEAD TABLE; UNIT 0 7905A , 02 HEAD(S) 0 1 H62 HEAD TABLE; UNIT 1 7906A , 02 HEAD(S) 0 1 H62 HEAD TABLE; UNIT 2 7920A , 05 HEAD(S) 0 1 2 3 4 H62 HEAD TABLE; UNIT 3 7925A , 09 HEAD(S) 0 1 2 3 4 5 6 7 8 H62 HEAD TABLE; UNIT 4 7905A , 02 HEAD(S) 0 1 H62 HEAD TABLE; UNIT 5 7906A , 02 HEAD(S) 0 1 H62 HEAD TABLE; UNIT 6 7920A , 05 HEAD(S) 0 1 2 3 4 H62 HEAD TABLE; UNIT 7 7925A , 09 HEAD(S) 0 1 2 3 4 5 6 7 8 H25 WISH TO CHANGE? YES H132 TYPE UNITS YOU WISH TO CHANGE SEPERATED BY COMMAS 0,1,4,5 H62 HEAD TABLE; UNIT 0 7905A , 02 HEAD(S) 0 1 H106 ENTER HEADS SEPARATED BY COMMAS 0,1,2 H62 HEAD TABLE; UNIT 0 7905A , 03 HEAD(S) 0 1 2 H25 WISH TO CHANGE? NO H62 HEAD TABLE; UNIT 1 7906A , 02 HEAD(S) 0 1 H106 ENTER HEADS SEPARATED BY COMMAS 0,1,2,3 H62 HEAD TABLE; UNIT 1 7906A , 04 HEAD(S) 0 1 2 3 H25 WISH TO CHANGE? NO H62 HEAD TABLE; UNIT 4 7905A , 02 HEAD(S) 0 1 H106 ENTER HEADS SEPARATED BY COMMAS 0,1,2 H62 HEAD TABLE; UNIT 4 7905A , 03 HEAD(S) 0 1 2 H25 WISH TO CHANGE? NO H62 HEAD TABLE; UNIT 5 7906A , 02 HEAD(S) 0 1 H106 ENTER HEADS SEPARATED BY COMMAS 0,1,2,3 H62 HEAD TABLE; UNIT 5 7906A , 04 HEAD(S) 0 1 2 3 H25 WISH TO CHANGE? NO ENTER:(U)NIT,(?) ERRS,(H)EAD,(O)UTPUT,(P)ATT,(S)OFT,(C)YL,(M)CPU,(E)XIT E TEST REPORT: H121 WARNING-FORMAT SWITCH OFF H65 LONG PASS 0001,HEAD 012 ,UNIT 0,0000 ERRORS-0000 SOFT H65 LONG PASS 0002,HEAD 0123 ,UNIT 1,0000 ERRORS-0000 SOFT H65 LONG PASS 0003,HEAD 01234 ,UNIT 2,0000 ERRORS-0000 SOFT H65 LONG PASS 0004,HEAD 012345678,UNIT 3,0000 ERRORS-0000 SOFT H65 LONG PASS 0005,HEAD 012 ,UNIT 4,0000 ERRORS-0000 SOFT H65 LONG PASS 0006,HEAD 0123 ,UNIT 5,0000 ERRORS-0000 SOFT H65 LONG PASS 0007,HEAD 01234 ,UNIT 6,0000 ERRORS-0000 SOFT H65 LONG PASS 0008,HEAD 012345678,UNIT 7,0000 ERRORS-0000 SOFT,MULTI-UNIT [CTRL+E] Simulation stopped TEST RESULT: Passed. TEST NOTES: Eight passes are required to test all head/unit combinations. -------------------------------------------------- DSN 151403 - 7905/06/20/25 Disc (user interaction) -------------------------------------------------- TESTED DEVICE: DS (hp2100_ds.c) CONFIGURATION: sim> set DS0 7905 sim> attach DS0 scratch.U0.7905.disc sim> deposit S 000034 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000120 sim> reset sim> go H0 79XX/13037 DISC MEMORY DIAGNOSTIC H37 UNIT TABLE: 01 DRIVE(S); 0 H25 WISH TO CHANGE? NO TEST REPORT: H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> set DS0 format sim> go H46 READ IN STEP 04 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0000/00/00-LAST 0000/00/01 WORD COUNT 00128,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H22 VERIFY IN STEP 04 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0000/00/00-LAST 0001/00/00 WORD COUNT 00048,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> set DS0 noformat sim> go H46 READ IN STEP 07 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 0 0 0 0 0 0 SHOULD BE 0 0 1 10001 XXXX XXXX / 0 000010 0 0 0 0 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "DEFECTIVE TRK " START 0001/00/00-LAST 0001/00/01 WORD COUNT 00128,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H45 WRITE IN STEP 08 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 0 0 0 0 0 0 SHOULD BE 0 1 0 10110 XXXX XXXX / 0 000010 0 0 0 0 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "WRT PROTEC TRK" START 0000/00/00-LAST 0000/00/01 WORD COUNT 00128,OLD CYL 0001,UNIT 00 HALT instruction 102001 sim> go H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN HALT instruction 102002 sim> set DS0 format sim> go H45 WRITE IN STEP 10 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0000/00/00-LAST 0000/00/08 WORD COUNT 01024,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H70 UNLOAD UNIT 0,PUSH RUN HALT instruction 102002 sim> set DS0 unloaded sim> go H107 READY UNIT 0 [CTRL+E] Simulation stopped sim> set DS0 loaded sim> go H142 PROTECT U/D,PUSH RUN HALT instruction 102002 sim> set DS0 locked sim> go H143 CLEAR U/D PROTECT,PUSH RUN HALT instruction 102002 sim> set DS0 writeenabled sim> go H110 PRESS PRESET(S),PRESS RUN HALT instruction 102002 sim> reset sim> go H46 READ IN STEP 38 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 0 00111 0000 0000 / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "CYL CMP ERROR " START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 39 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 0 01001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "HD/SEC CMP ERR" START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 40 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 0 01001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "HD/SEC CMP ERR" START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 41 E47 DATA WORD 0065 IS 075126 SHOULD BE 030400 E47 DATA WORD 0066 IS 000762 SHOULD BE 030400 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 0 01111 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "POSS CORR DATA" START 0000/00/00-LAST 0000/00/03 WORD COUNT 00128,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 42 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 0 01000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "UNCOR DATA ERR" START 0000/00/00-LAST 0000/00/03 WORD COUNT 00276,OLD CYL 0000,UNIT 00 HALT instruction 102001 sim> go H22 VERIFY IN STEP 43 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 0 0 1 10001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "DEFECTIVE TRK " START 0016/00/00-LAST 0017/00/00 WORD COUNT 00048,OLD CYL 0128,UNIT 00 HALT instruction 102001 sim> go H22 VERIFY IN STEP 43 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 1 0 0 10000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "SPR TRK ACCESS" START 0128/01/00-LAST 0129/01/00 WORD COUNT 00048,OLD CYL 0016,UNIT 00 HALT instruction 102001 sim> go H45 WRITE IN STEP 43 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0128,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 43 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0016,UNIT 00 HALT instruction 102001 sim> go H46 READ IN STEP 43 E47 DATA WORD 0000 IS 156164 SHOULD BE 144300 E47 DATA WORD 0001 IS 023302 SHOULD BE 117306 E47 DATA WORD 0002 IS 114642 SHOULD BE 045322 H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 H137 TERMINATION STATUS IS "NORMAL COMPLET" START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0016,UNIT 00 HALT instruction 102001 sim> go H65 SHORT PASS 0001,HEAD 01 ,UNIT 0,0015 ERRORS-0015 SOFT [CTRL+E] Simulation stopped TEST RESULT: Partially passed. TEST NOTES: Steps 4, 8, and 10 test the protected cylinder bit. Step 7 tests the defective cylinder bit. Steps 38, 39, and 40 test the Write Full Sector command. Steps 41 and 42 test error correction. Step 43 tests the spare cylinder bit and track sparing. These features are not simulated. ------------------------------------------------- DSN 112200 - 9-Track Magnetic Tape (7970B, 13181) ------------------------------------------------- TESTED DEVICE: MS (hp2100_ms.c) CONFIGURATION: sim> detach MSC0 sim> set MSC 13181A sim> set MSC realtime sim> attach MSC0 scratch.U0.7970.tape sim> attach MSC1 scratch.U1.7970.tape sim> attach MSC2 scratch.U2.7970.tape sim> attach MSC3 scratch.U3.7970.tape sim> deposit S 102030 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000217 sim> reset sim> go TEST REPORT: 7970-13181 DIAG. H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H025 FOR DATA CH H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H025 FOR CMND CH H154 UNIT 000000 H034 UNIT ON-LINE H155 STATUS IS 0 000 000 001 000 000 H154 UNIT 000001 H034 UNIT ON-LINE H155 STATUS IS 0 000 000 001 000 000 H154 UNIT 000002 H034 UNIT ON-LINE H155 STATUS IS 0 000 000 001 000 000 H154 UNIT 000003 H034 UNIT ON-LINE H155 STATUS IS 0 000 000 001 000 000 H154 UNIT 000000 H102 RECORD 000117 H054 COMMAND 000061 H155 STATUS IS 0 000 000 000 000 000 H155 AND SHOULD BE 0 000 000 000 000 000 TEST 23 E135 LRCC ERROR HALT instruction 106035 sim> go H154 UNIT 000000 H102 RECORD 000117 H054 COMMAND 000061 H155 STATUS IS 0 000 000 000 000 000 H155 AND SHOULD BE 0 000 000 000 000 000 E141 CRCC ERROR HALT instruction 106041 sim> go H126 EXCHANGE REELS HALT instruction 106026 sim> attach MSC0 scratch.U3.7970.tape sim> attach MSC1 scratch.U2.7970.tape sim> attach MSC2 scratch.U1.7970.tape sim> attach MSC3 scratch.U0.7970.tape sim> go H127 SET SW 13 TO LOOP HALT instruction 106027 sim> go H130 REMOVE WRITE RING HALT instruction 106030 sim> set MSC0 locked sim> go H131 REPLACE WRITE RING HALT instruction 106031 sim> set MSC0 writeenabled sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC0 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC1 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC2 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC3 online sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Partially passed. TEST NOTES: Test 23 verifies the LRCC and CRCC values obtained from the interface. These features are not simulated. (Setting bit 7 of the S register during configuration eliminates most LRCC/CRCC checks but does not inhibit test 23.) If test 34 is selected manually, E065 WRITE ERROR will occur. This is due to the implementation of the tape simulation library. Test 34 writes data in a single record until a data error or EOT occurs (conceivably 20+ megabytes for the largest reel size at 800 bpi). Because the tape simulation library writes complete records, the 7970 simulator must use a data buffer to accumulate the entire record before calling "sim_tape_wrrecf" to write the record. The simulator uses a data buffer of 32768 words. When the buffer is full, parity-error status is returned to the program. ------------------------------------------------- DSN 112200 - 9-Track Magnetic Tape (7970E, 13183) ------------------------------------------------- TESTED DEVICE: MS (hp2100_ms.c) CONFIGURATION: sim> detach MSC0 sim> set MSC 13183A sim> set MSC realtime sim> attach MSC0 scratch.U0.7970.tape sim> attach MSC1 scratch.U1.7970.tape sim> attach MSC2 scratch.U2.7970.tape sim> attach MSC3 scratch.U3.7970.tape sim> deposit S 104030 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000017 sim> reset sim> go TEST REPORT: 7970-13183 DIAG. H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H025 FOR DATA CH H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H025 FOR CMND CH H154 UNIT 000000 H034 UNIT ON-LINE H155 STATUS IS 1 000 000 001 000 000 H154 UNIT 000001 H034 UNIT ON-LINE H155 STATUS IS 1 010 000 001 000 000 H154 UNIT 000002 H034 UNIT ON-LINE H155 STATUS IS 1 100 000 001 000 000 H154 UNIT 000003 H034 UNIT ON-LINE H155 STATUS IS 1 110 000 001 000 000 H126 EXCHANGE REELS HALT instruction 106026 sim> attach MSC0 scratch.U3.7970.tape sim> attach MSC1 scratch.U2.7970.tape sim> attach MSC2 scratch.U1.7970.tape sim> attach MSC3 scratch.U0.7970.tape sim> go H127 SET SW 13 TO LOOP HALT instruction 106027 sim> go H130 REMOVE WRITE RING HALT instruction 106030 sim> set MSC0 locked sim> go H131 REPLACE WRITE RING HALT instruction 106031 sim> set MSC0 writeenabled sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC0 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC1 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC2 online sim> go H137 PUT TAPE UNIT ON-LINE HALT instruction 106037 sim> set MSC3 online sim> go PASS 000001 HALT instruction 102077 TEST RESULT: Passed. ------------------------------------ DSN 146200 - Paper Tape Reader/Punch ------------------------------------ TESTED DEVICE: PTR and PTP (hp2100_stddev.c) CONFIGURATION: sim> deposit S 001012 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 001000 sim> reset sim> go PAPER TAPE READER AND PUNCH DIAGNOSTIC DSN 146200 HALT instruction 102075 sim> deposit A 000200 sim> reset sim> go H060 TO MAKE LOOP, PUNCH ON AND RUN HALT instruction 102060 sim> attach PTP loop.2895.punch sim> go PASS 000001 HALT instruction 102077 sim> detach PTP sim> deposit S 001000 sim> reset sim> go 2000 PAPER TAPE READER AND PUNCH DIAGNOSTIC DSN 146200 HALT instruction 102075 sim> deposit A 003177 sim> deposit S 000000 sim> reset sim> go TEST REPORT: H050 BI-O ON PUNCH H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H055 BI-O ON READER H024 PRESS PRESET (EXT&INT),RUN HALT instruction 102024 sim> reset sim> go H025 BI-O COMP H051 ALL CHARTR COMBINATIONS, PUNCH ONLY TURN PUNCH ON, PRESS RUN HALT instruction 102051 sim> attach PTP scratch.2895.punch sim> go H052 ALL CHARTR COMBINATIONS, VERIFY TEAR TAPE AT PUNCH, PLACE IN READER, PRESS RUN HALT instruction 102052 sim> detach PTP sim> attach PTR scratch.2895.punch sim> go H054 PLACE LOOP IN READER-PRESS RUN TO START READ, SET BIT0 TO 1 TO EXIT TEST, SET BIT0 TO 0 HALT instruction 102054 sim> set PTR diag sim> attach PTR loop.2895.punch sim> deposit S 000001 sim> go [CTRL+E] Simulation stopped sim> deposit S 000000 sim> go H054 PLACE LOOP IN READER-PRESS RUN TO START READ, SET BIT0 TO 1 TO EXIT TEST, SET BIT0 TO 0 HALT instruction 102054 sim> deposit S 000001 sim> go [CTRL+E] Simulation stopped sim> deposit PTR TIME 100 sim> deposit PTP TIME 200 sim> deposit S 000000 sim> go H056 TURN PUNCH ON, PRESS RUN. PUNCH ROUTINE WILL START. LOAD THE TAPE BEING PUNCHED INTO THE READER. TO START READ, SET BIT0 TO 1 TO EXIT, SET BIT0 TO 0 HALT instruction 102056 sim> set PTR reader sim> attach PTR scratch.2895.punch sim> attach PTP scratch.2895.punch sim> go [CTRL+E] Simulation stopped sim> deposit S 000001 sim> go [CTRL+E] Simulation stopped sim> deposit S 000000 sim> go H057 TO COMPLETE, TEAR TAPE, PRESS RUN HALT instruction 102057 sim> go H063 READER SPEED TEST. PLACE LOOP IN READER BIT 5=0 FOR 2748-58, BIT 5=1 FOR 2737. PRESS RUN. HALT instruction 102063 sim> set PTR diag sim> attach PTR loop.2895.punch sim> deposit PTR TIME 3150 sim> go H066 TEST 11 COMPLETE H100 PUNCH SPEED TEST. BIT 6=0 FOR 2895 OR BIT 6=1 FOR 2753-PRESS RUN HALT instruction 106000 sim> deposit PTP TIME 20790 sim> go H103 TEST 12 COMPLETE PASS 000001 HALT instruction 102077 TEST RESULT: Passed. TEST NOTES: Test 07 is executed to punch a tape loop that is used in tests 04, 05, and 11. Then the default tests 00-06, plus tests 11 and 12, are executed. Test 06 punches and reads the same tape concurrently (the tape coming out of the punch is then fed into the reader). Under simulation, it is necessary to delay starting the read until the punch buffer has been flushed to the disc. Also, this test depends on the reader being at least twice as fast as the punch, so the PTR/PTP TIME registers are adjusted accordingly. Test 11 and test 12 are speed tests, so the PTR and PTP TIMEs are set for realistic timing. STAND-ALONE DIAGNOSTIC DETAILED EXECUTION AND RESULTS ===================================================== Each execution note below presumes that the target diagnostic has been loaded. For all runs, the diagnostic configurator was used in automatic mode to load the target diagnostic from a paper tape image, as follows: sim> attach -r MSC0 24396-13601_Rev-2326.abin.tape sim> deposit S 000000 sim> boot MSC0 HALT instruction 102077 sim> attach PTR [paper-tape-image-file] sim> deposit S 001011 sim> reset sim> go 100 ------------------------------------------------ DSN 101217 - 2000/Access Comm Processor for 21MX ------------------------------------------------ TESTED DEVICE: CPU (hp2100_cpu2.c) BINARY TAPE: 13207-16001 Rev. 1728 CONFIGURATION: sim> set CPU IOP sim> deposit S 000013 sim> reset sim> go 100 HALT instruction 102074 sim> deposit S 000000 sim> reset sim> go TEST REPORT: 21MX 2000 COMPUTER SYSTEM COMM. PROC. FIRMWARE DIAGNOSTIC H030 CRC TEST H040 ENQ, DEQ AND PENQ TESTS H060 IAL TEST H110 INS,READF, SAVE AND RESTR TESTS H120 LAI AND SAI TESTS H130 PFREX TEST H140 PFREI TEST H150 PFRIO TEST PASS 000001 HALT instruction 102077 TEST RESULT: Passed. -------------------------------------------- DSN (none) - HP 3030 Magnetic Tape Subsystem -------------------------------------------- TESTED DEVICE: MT (hp2100_mt.c) BINARY TAPE: None available. CONFIGURATION: (none) TEST REPORT: (none) TEST RESULT: Not tested. TEST NOTES: No copy of the diagnostic (HP product number 20433) has been found. ----------------------------------------------------------- DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2770) ----------------------------------------------------------- TESTED DEVICE: DR (hp2100_dr.c) BINARY TAPE: 22682-16017 Rev. 1612 CONFIGURATION: sim> reset sim> go 100 H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN HALT instruction 107001 sim> set DRC 180K sim> set DRC trackprot=8 sim> attach DRC0 scratch.U0.2770.disc sim> deposit S 002611 sim> go H1 CONFIGURATION COMPLETE H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN HALT instruction 107077 sim> deposit S 010000 sim> go TEST REPORT: H12 DEVICE HAS 90 SECTORS H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN HALT instruction 102002 sim> set DRC protected sim> go H14 DEVICE HAS 0032 TRACKS,THE FOLLOWING ARE PROTECTED: H63 0000 TO 0007 H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H36 PASS 0001 HALT instruction 102077 TEST RESULT: Passed. --------------------------------------------------------------- DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2771-001) --------------------------------------------------------------- TESTED DEVICE: DR (hp2100_dr.c) BINARY TAPE: 22682-16017 Rev. 1612 CONFIGURATION: sim> reset sim> go 100 H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN HALT instruction 107001 sim> set DRC 720K sim> set DRC trackprot=32 sim> attach DRC0 scratch.U0.2771.disc sim> deposit S 002611 sim> go H1 CONFIGURATION COMPLETE H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN HALT instruction 107077 sim> deposit S 010000 sim> go TEST REPORT: H12 DEVICE HAS 90 SECTORS H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN HALT instruction 102002 sim> set DRC protected sim> go H14 DEVICE HAS 0128 TRACKS,THE FOLLOWING ARE PROTECTED: H63 0000 TO 0031 H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H36 PASS 0001 HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------------------- DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2773) ----------------------------------------------------------- TESTED DEVICE: DR (hp2100_dr.c) BINARY TAPE: 22682-16017 Rev. 1612 CONFIGURATION: sim> reset sim> go 100 H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN HALT instruction 107001 sim> set DRC 384K sim> set DRC trackprot=16 sim> attach DRC0 scratch.U0.2773.disc sim> deposit S 002611 sim> go H1 CONFIGURATION COMPLETE H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN HALT instruction 107077 sim> deposit S 010000 sim> go TEST REPORT: H12 DEVICE HAS 32 SECTORS H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN HALT instruction 102002 sim> set DRC protected sim> go H14 DEVICE HAS 0192 TRACKS,THE FOLLOWING ARE PROTECTED: H63 0000 TO 0015 H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H36 PASS 0001 HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------------------- DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2775) ----------------------------------------------------------- TESTED DEVICE: DR (hp2100_dr.c) BINARY TAPE: 22682-16017 Rev. 1612 CONFIGURATION: sim> reset sim> go 100 H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN HALT instruction 107001 sim> set DRC 1536K sim> set DRC trackprot=64 sim> attach DRC0 scratch.U0.2775.disc sim> deposit S 002611 sim> go H1 CONFIGURATION COMPLETE H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN HALT instruction 107077 sim> deposit S 010000 sim> go TEST REPORT: H12 DEVICE HAS 32 SECTORS H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN HALT instruction 102002 sim> set DRC protected sim> go H14 DEVICE HAS 0768 TRACKS,THE FOLLOWING ARE PROTECTED: H63 0000 TO 0063 H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN HALT instruction 102002 sim> set DRC unprotected sim> go H36 PASS 0001 HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------- DSN (none) - 12875 Processor Interconnect Cable ----------------------------------------------- TESTED DEVICE: IPLI, IPLO (hp2100_ipl.c) BINARY TAPE: 24197-60001 Rev. B CONFIGURATION: sim> set IPLI DIAG sim> set IPLO DIAG sim> deposit S 003332 sim> reset sim> go 2 HALT instruction 107076 sim> deposit S 010000 sim> reset sim> go HALT instruction 107077 sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: H14. START 12875 CABLE DIAGNOSTIC H77. END 12875 CABLE DIAGNOSTIC HALT instruction 102077 TEST RESULT: Passed. ----------------------------------------------------------------- DSN (none) - HP2100A Cartridge Disc Memory (2871) (multiple unit) ----------------------------------------------------------------- TESTED DEVICE: DP (hp2100_dp.c) BINARY TAPE: 24203-60001 Rev. A CONFIGURATION: sim> set DPC 12557A sim> attach DPC0 scratch.U0.2871.disc sim> attach DPC1 scratch.U1.2871.disc sim> attach DPC2 scratch.U2.2871.disc sim> attach DPC3 scratch.U3.2871.disc sim> deposit S 002211 sim> reset sim> go 2 HALT instruction 107077 sim> deposit S 000400 sim> reset sim> go 100 H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC H34 ENTER UNIT NUMBERS(0-3)SEPARATED BY COMMAS 0,1,2,3 H33 RESET SWITCH 8 HALT instruction 102002 sim> deposit S 000004 sim> go H24 CYLINDER TABLE 000,001,002,004,008,016,032,064,128,202 H25 WISH TO ALTER TABLE? NO H27 PATTERN TABLE 000000 177777 125252 052525 007417 170360 162745 163346 155555 022222 H25 WISH TO ALTER TABLE? NO H62 TYPE A FOR HEADS 0,1;B FOR 2,3;C FOR ALTERNATELY 0,1 THEN 2,3 C H32 RESET SWITCH 2 HALT instruction 102002 sim> deposit S 000000 sim> reset sim> go 100 TEST REPORT: H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC H65 PASS 0001 H65 PASS 0002 H65 PASS 0003 H65 PASS 0004 [CTRL+E] Simulation stopped TEST RESULT: Passed. TEST NOTES: Four passes are required to test all head/unit combinations. -------------------------------------------------------------------- DSN (none) - HP2100A Cartridge Disc Memory (2871) (user interaction) -------------------------------------------------------------------- TESTED DEVICE: DP (hp2100_dp.c) BINARY TAPE: 24203-60001 Rev. A CONFIGURATION: sim> set DPC 12557A sim> attach DPC0 scratch.U0.2871.disc sim> deposit S 002211 sim> reset sim> go 2 HALT instruction 107077 sim> deposit S 010020 sim> reset sim> go 100 TEST REPORT: H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC H66 SET OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H37 READ AFTER WRITE ADDRESS IN S0 E64 STATUS IS 000000 SHOULD BE 000010 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H22 CYCLIC CHECK IN S0 E64 STATUS IS 000000 SHOULD BE 000010 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H67 CLEAR OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H41 READ DEFECTIVE TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000031 H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H42 WRITE PROTECTED TRACK IN S0 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 HALT instruction 102001 sim> go H36 WRITE ADDRESS IN S0 E64 STATUS IS 000000 SHOULD BE 000011 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 3072 UNIT 00 HALT instruction 102001 sim> go H66 SET OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H67 CLEAR OVERRIDE SWITCH,PUSH RUN HALT instruction 102002 sim> go H70 UNLOCK UNIT 0,PUSH RUN HALT instruction 102002 sim> set DPC0 unloaded sim> go H40 READY UNIT 0 [CTRL+E] Simulation stopped sim> set DPC0 loaded sim> go H71 PRESS PRESET THEN PRESS RUN HALT instruction 102002 sim> deposit S 000140 sim> reset sim> go H65 PASS 0001 TEST RESULT: Partially passed. TEST NOTES: Step 0 tests the the defective and protected cylinder bits and the FORMAT OVERRIDE switch. These features are not simulated. ONLINE DIAGNOSTIC DETAILED EXECUTION AND RESULTS ================================================ Online diagnostics were run under the control of the indicated operating systems. Unless otherwise noted, the programs were loaded with the default configuration specified by the associated linker command file or the operating system. ------------------------------------------------ #EMA - Extended Memory Array Firmware Diagnostic ------------------------------------------------ TESTED DEVICE: CPU (hp2100_cpu5.c) BINARY FILE: 92067-16013 Rev. 1805 HOST SYSTEM: RTE-IVB Rev. 5010 CONFIGURATION: sim> set CPU EMA sim> go TEST REPORT: EMA ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION TEST RESULT: Passed. ------------------------------------------------ VMACK - Virtual Memory Array Firmware Diagnostic ------------------------------------------------ TESTED DEVICE: CPU (hp2100_cpu5.c) BINARY FILE: 92084-16423 Rev. 2121 HOST SYSTEM: RTE-6/VM Rev. 6200 CONFIGURATION: sim> set CPU 1000-F sim> set CPU VMA sim> go TEST REPORT: VMACK - VMA FIRMWARE DIAGNOSTIC, FIRMWARE REV# 003 VMACK - .IMAR NO ERRORS DETECTED PASS# 1 VMACK - .JMAR NO ERRORS DETECTED PASS# 1 VMACK - .LBP NO ERRORS DETECTED PASS# 1 VMACK - .LBPR NO ERRORS DETECTED PASS# 1 VMACK - .LPX NO ERRORS DETECTED PASS# 1 VMACK - .LPXR NO ERRORS DETECTED PASS# 1 VMACK - .PMAP NO ERRORS DETECTED PASS# 1 VMACK - .IMAP NO ERRORS DETECTED PASS# 1 VMACK - .JMAP NO ERRORS DETECTED PASS# 1 TEST RESULT: Passed. -------------------------------------------------- VISOD - Vector Instruction Set Firmware Diagnostic -------------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu7.c) BINARY FILE: 12824-16002 Rev. 2026 HOST SYSTEM: RTE-IVB Rev. 5010 CONFIGURATION: sim> set CPU 1000-F sim> set CPU VIS sim> go TEST REPORT: VIS ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION TEST RESULT: Passed. -------------------------------------------------- VISOD - Vector Instruction Set Firmware Diagnostic -------------------------------------------------- TESTED DEVICE: CPU (hp2100_cpu7.c) BINARY FILE: 12829-16006 Rev. 2226 HOST SYSTEM: RTE-6/VM Rev. 6200 CONFIGURATION: sim> set CPU 1000-F sim> set CPU VIS sim> go TEST REPORT: VIS ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION TEST RESULT: Passed. --------------------------------------- SDIAG - SIGNAL/1000 Firmware Diagnostic --------------------------------------- TESTED DEVICE: CPU (hp2100_cpu7.c) BINARY FILE: 92835-16006 Rev. 2040 HOST SYSTEM: RTE-6/VM Rev. 6200 CONFIGURATION: sim> set CPU 1000-F sim> set CPU VIS sim> set CPU SIGNAL sim> go TEST REPORT: SIGNAL/1000 FIRMWARE DIAGNOSTIC SIGNAL/1000 FIRMWARE DIAGNOSTIC SUCCESSFUL COMPLETION TEST RESULT: Passed. simh-3.8.1/HP2100/hp2100_fp.c0000644000175000017500000004165111060066416013225 0ustar vlmvlm/* hp2100_fp.c: HP 2100 floating point instructions Copyright (c) 2002-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Jan-08 JDB Corrected fp_unpack mantissa high-word return (from Mark Pizzolato) 01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2 22-Jul-05 RMS Fixed compiler warning in Solaris (from Doug Glyn) 25-Feb-05 JDB Added FFP helpers f_pack, f_unpack, f_pwr2 11-Feb-05 JDB Fixed missing negative overflow renorm in StoreFP 26-Dec-04 RMS Separated A/B from M[0/1] for DMA IO (from Dave Bryan) 15-Jul-03 RMS Fixed signed/unsigned warning 21-Oct-02 RMS Recoded for compatibility with 21MX microcode algorithms The HP2100 uses a unique binary floating point format: 15 14 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |S | fraction high | : A +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | fraction low | exponent |XS| : A + 1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 15 8 7 1 0 where S = 0 for plus fraction, 1 for minus fraction fraction = s.bbbbb..., 24 binary digits exponent = 2**+/-n XS = 0 for plus exponent, 1 for minus exponent Numbers can be normalized or unnormalized but are always normalized when loaded. Unpacked floating point numbers are stored in structure ufp exp = exponent, 2's complement h'l = fraction, 2's comp, left justified This routine tries to reproduce the algorithms of the 2100/21MX microcode in order to achieve 'bug-for-bug' compatibility. In particular, - The FIX code produces various results in B. - The fraction multiply code uses 16b x 16b multiplies to produce a 31b result. It always loses the low order bit of the product. - The fraction divide code is an approximation that may produce an error of 1 LSB. - Signs are tracked implicitly as part of the fraction. Unnormalized inputs may cause the packup code to produce the wrong sign. - "Unclean" zeros (zero fraction, non-zero exponent) are processed like normal operands. Implementation notes: 1. The 2100/1000-M/E Fast FORTRAN Processor (FFP) and 1000 F-Series Floating Point Processor (FPP) simulations require that the host compiler support 64-bit integers and the HAVE_INT64 symbol be defined during compilation. If this symbol is defined, two-word floating-point operations are handled in the FPP code, and this module is not used. If it is not defined, then FFP and FPP operations are not available, and this module provides the floating-point support. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #include "hp2100_fp.h" #if !defined (HAVE_INT64) /* int64 support unavailable */ struct ufp { /* unpacked fp */ int32 exp; /* exp */ uint32 fr; /* frac */ }; #define FP_V_SIGN 31 /* sign */ #define FP_M_SIGN 01 #define FP_V_FR 8 /* fraction */ #define FP_M_FR 077777777 #define FP_V_EXP 1 /* exponent */ #define FP_M_EXP 0177 #define FP_V_EXPS 0 /* exp sign */ #define FP_M_EXPS 01 #define FP_SIGN (FP_M_SIGN << FP_V_SIGN) #define FP_FR (FP_M_FR << FP_V_FR) #define FP_EXP (FP_M_EXP << FP_V_EXP) #define FP_EXPS (FP_M_EXPS << FP_V_EXPS) #define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN) #define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP) #define FP_GETEXPS(x) (((x) >> FP_V_EXPS) & FP_M_EXPS) #define FP_NORM (1 << (FP_V_SIGN - 1)) /* normalized */ #define FP_LOW (1 << FP_V_FR) #define FP_RNDP (1 << (FP_V_FR - 1)) /* round for plus */ #define FP_RNDM (FP_RNDP - 1) /* round for minus */ #define FPAB ((((uint32) AR) << 16) | ((uint32) BR)) /* Fraction shift; 0 < shift < 32 */ #define FR_ARS(v,s) (((v) >> (s)) | (((v) & FP_SIGN)? \ (((uint32) DMASK32) << (32 - (s))): 0)) & DMASK32 #define FR_NEG(v) ((~(v) + 1) & DMASK32) uint32 UnpackFP (struct ufp *fop, uint32 opnd); void NegFP (struct ufp *fop); void NormFP (struct ufp *fop); uint32 PackFP (struct ufp *fop); uint32 StoreFP (struct ufp *fop); /* Floating to integer conversion */ uint32 f_fix (void) { struct ufp fop; uint32 res = 0; UnpackFP (&fop, FPAB); /* unpack op */ if (fop.exp < 0) { /* exp < 0? */ AR = 0; /* result = 0 */ return 0; /* B unchanged */ } if (fop.exp > 15) { /* exp > 15? */ BR = AR; /* B has high bits */ AR = 077777; /* result = 77777 */ return 1; /* overflow */ } if (fop.exp < 15) { /* if not aligned */ res = FR_ARS (fop.fr, 15 - fop.exp); /* shift right */ AR = (res >> 16) & DMASK; /* AR gets result */ } BR = AR; if ((AR & SIGN) && ((fop.fr | res) & DMASK)) /* any low bits lost? */ AR = (AR + 1) & DMASK; /* round up */ return 0; } /* Integer to floating conversion */ uint32 f_flt (void) { struct ufp res = { 15, 0 }; /* +, 2**15 */ res.fr = ((uint32) AR) << 16; /* left justify */ StoreFP (&res); /* store result */ return 0; /* clr overflow */ } /* Floating point add/subtract */ uint32 f_as (uint32 opnd, t_bool sub) { struct ufp fop1, fop2, t; int32 ediff; UnpackFP (&fop1, FPAB); /* unpack A-B */ UnpackFP (&fop2, opnd); /* get op */ if (sub) { /* subtract? */ fop2.fr = FR_NEG (fop2.fr); /* negate frac */ if (fop2.fr == ((uint32) FP_SIGN)) { /* -1/2? */ fop2.fr = fop2.fr >> 1; /* special case */ fop2.exp = fop2.exp + 1; } } if (fop1.fr == 0) fop1 = fop2; /* op1 = 0? res = op2 */ else if (fop2.fr != 0) { /* op2 = 0? no add */ if (fop1.exp < fop2.exp) { /* |op1| < |op2|? */ t = fop2; /* swap operands */ fop2 = fop1; fop1 = t; } ediff = fop1.exp - fop2.exp; /* get exp diff */ if (ediff <= 24) { if (ediff) fop2.fr = FR_ARS (fop2.fr, ediff); /* denorm, signed */ if ((fop1.fr ^ fop2.fr) & FP_SIGN) /* unlike signs? */ fop1.fr = fop1.fr + fop2.fr; /* eff subtract */ else { /* like signs */ fop1.fr = fop1.fr + fop2.fr; /* eff add */ if (fop2.fr & FP_SIGN) { /* both -? */ if ((fop1.fr & FP_SIGN) == 0) { /* overflow? */ fop1.fr = FP_SIGN | (fop1.fr >> 1); /* renormalize */ fop1.exp = fop1.exp + 1; /* incr exp */ } } else if (fop1.fr & FP_SIGN) { /* both +, cry out? */ fop1.fr = fop1.fr >> 1; /* renormalize */ fop1.exp = fop1.exp + 1; /* incr exp */ } } /* end else like */ } /* end if ediff */ } /* end if fop2 */ return StoreFP (&fop1); /* store result */ } /* Floating point multiply - passes diagnostic */ uint32 f_mul (uint32 opnd) { struct ufp fop1, fop2; struct ufp res = { 0, 0 }; int32 shi1, shi2, t1, t2, t3, t4, t5; UnpackFP (&fop1, FPAB); /* unpack A-B */ UnpackFP (&fop2, opnd); /* unpack op */ if (fop1.fr && fop2.fr) { /* if both != 0 */ res.exp = fop1.exp + fop2.exp + 1; /* exp = sum */ shi1 = SEXT (fop1.fr >> 16); /* mpy hi */ shi2 = SEXT (fop2.fr >> 16); /* mpc hi */ t1 = shi2 * ((int32) ((fop1.fr >> 1) & 077600)); /* mpc hi * (mpy lo/2) */ t2 = shi1 * ((int32) ((fop2.fr >> 1) & 077600)); /* mpc lo * (mpy hi/2) */ t3 = t1 + t2; /* cross product */ t4 = (shi1 * shi2) & ~1; /* mpy hi * mpc hi */ t5 = (SEXT (t3 >> 16)) << 1; /* add in cross */ res.fr = (t4 + t5) & DMASK32; /* bit<0> is lost */ } return StoreFP (&res); /* store */ } /* Floating point divide - reverse engineered from diagnostic */ uint32 divx (uint32 ba, uint32 dvr, uint32 *rem) { int32 sdvd = 0, sdvr = 0; uint32 q, r; if (ba & FP_SIGN) sdvd = 1; /* 32b/16b signed dvd */ if (dvr & SIGN) sdvr = 1; /* use old-fashioned */ if (sdvd) ba = (~ba + 1) & DMASK32; /* unsigned divides, */ if (sdvr) dvr = (~dvr + 1) & DMASK; /* as results may ovflo */ q = ba / dvr; r = ba % dvr; if (sdvd ^ sdvr) q = (~q + 1) & DMASK; if (sdvd) r = (~r + 1) & DMASK; if (rem) *rem = r; return q; } uint32 f_div (uint32 opnd) { struct ufp fop1, fop2; struct ufp quo = { 0, 0 }; uint32 ba, q0, q1, q2, dvrh; UnpackFP (&fop1, FPAB); /* unpack A-B */ UnpackFP (&fop2, opnd); /* unpack op */ dvrh = (fop2.fr >> 16) & DMASK; /* high divisor */ if (dvrh == 0) { /* div by zero? */ AR = 0077777; /* return most pos */ BR = 0177776; return 1; } if (fop1.fr) { /* dvd != 0? */ quo.exp = fop1.exp - fop2.exp + 1; /* exp = diff */ ba = FR_ARS (fop1.fr, 2); /* prevent ovflo */ q0 = divx (ba, dvrh, &ba); /* Q0 = dvd / dvrh */ ba = (ba & ~1) << 16; /* remainder */ ba = FR_ARS (ba, 1); /* prevent ovflo */ q1 = divx (ba, dvrh, NULL); /* Q1 = rem / dvrh */ ba = (fop2.fr & 0xFF00) << 13; /* dvrl / 8 */ q2 = divx (ba, dvrh, NULL); /* dvrl / dvrh */ ba = -(SEXT (q2)) * (SEXT (q0)); /* -Q0 * Q2 */ ba = (ba >> 16) & 0xFFFF; /* save ms half */ if (q1 & SIGN) quo.fr = quo.fr - 0x00010000; /* Q1 < 0? -1 */ if (ba & SIGN) quo.fr = quo.fr - 0x00010000; /* -Q0*Q2 < 0? */ quo.fr = quo.fr + ((ba << 2) & 0xFFFF) + q1; /* rest prod, add Q1 */ quo.fr = quo.fr << 1; /* shift result */ quo.fr = quo.fr + (q0 << 16); /* add Q0 */ } /* end if fop1.h */ return StoreFP (&quo); /* store result */ } /* Utility routines */ /* Unpack operand */ uint32 UnpackFP (struct ufp *fop, uint32 opnd) { fop->fr = opnd & FP_FR; /* get frac */ fop->exp = FP_GETEXP (opnd); /* get exp */ if (FP_GETEXPS (opnd)) fop->exp = fop->exp | ~FP_M_EXP; /* < 0? sext */ return FP_GETSIGN (opnd); /* return sign */ } /* Normalize unpacked floating point number */ void NormFP (struct ufp *fop) { if (fop->fr) { /* any fraction? */ uint32 test = (fop->fr >> 1) & FP_NORM; while ((fop->fr & FP_NORM) == test) { /* until norm */ fop->exp = fop->exp - 1; fop->fr = (fop->fr << 1); } } else fop->exp = 0; /* clean 0 */ return; } /* Pack fp number */ uint32 PackFP (struct ufp *fop) { return (fop->fr & FP_FR) | /* merge frac */ ((fop->exp & FP_M_EXP) << FP_V_EXP) | /* and exp */ ((fop->exp < 0)? (1 << FP_V_EXPS): 0); /* add exp sign */ } /* Round fp number, store, generate overflow */ uint32 StoreFP (struct ufp *fop) { uint32 sign, svfr, hi, ov = 0; NormFP (fop); /* normalize */ svfr = fop->fr; /* save fraction */ sign = FP_GETSIGN (fop->fr); /* save sign */ fop->fr = (fop->fr + (sign? FP_RNDM: FP_RNDP)) & FP_FR; /* round */ if ((fop->fr ^ svfr) & FP_SIGN) { /* sign change? */ fop->fr = fop->fr >> 1; /* renormalize */ fop->exp = fop->exp + 1; } else NormFP (fop); /* check for norm */ if (fop->fr == 0) hi = 0; /* result 0? */ else if (fop->exp < -(FP_M_EXP + 1)) { /* underflow? */ hi = 0; /* store clean 0 */ ov = 1; } else if (fop->exp > FP_M_EXP) { /* overflow? */ hi = 0x7FFFFFFE; /* all 1's */ ov = 1; } else hi = PackFP (fop); /* pack mant and exp */ AR = (hi >> 16) & DMASK; BR = hi & DMASK; return ov; } /* Single-precision Fast FORTRAN Processor helpers. */ /* Pack mantissa and exponent and return fp value. */ uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) { struct ufp fop; uint32 val; fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1]; fop.exp = exponent; val = PackFP (&fop); result->fpk[0] = (int16) (val >> 16); result->fpk[1] = (int16) val; return 0; } /* Normalize, round, and pack mantissa and exponent and return fp value. */ uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) { struct ufp fop; uint32 ovf; fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1]; fop.exp = exponent; ovf = StoreFP (&fop); result->fpk[0] = AR; result->fpk[1] = BR; return ovf; } /* Unpack fp number in into mantissa and exponent. */ uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision) { struct ufp fop; uint32 operand; operand = ((uint32) packed.fpk[0] << 16) | packed.fpk[1]; UnpackFP (&fop, operand); mantissa->fpk[0] = (uint16) (fop.fr >> 16); mantissa->fpk[1] = (uint16) fop.fr; *exponent = fop.exp; return 0; } #endif /* int64 support unavailable */ simh-3.8.1/HP2100/hp2100_cpu.c0000644000175000017500000051564311107453460013417 0ustar vlmvlm/* hp2100_cpu.c: HP 21xx/1000 CPU simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. CPU 2114C/2115A/2116C/2100A/1000-M/E/F central processing unit 12731A memory expansion module MP 12581A/12892B memory protect DMA0,DMA1 12607B/12578A/12895A direct memory access controller DCPC0,DCPC1 12897B dual channel port controller 30-Sep-08 JDB Breakpoints on interrupt trap cells now work 05-Sep-08 JDB VIS and IOP are now mutually exclusive on 1000-F 11-Aug-08 JDB Removed A/B shadow register variables 07-Aug-08 JDB Moved hp_setdev, hp_showdev to hp2100_sys.c Moved non-existent memory checks to WritePW 05-Aug-08 JDB Fixed mp_dms_jmp to accept lower bound, check write protection 30-Jul-08 JDB Corrected DMS violation register set conditions Refefined ABORT to pass address, moved def to hp2100_cpu.h Combined dms and dms_io routines 29-Jul-08 JDB JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort 11-Jul-08 JDB Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA 26-Jun-08 JDB Rewrote device I/O to model backplane signals EDT no longer passes DMA channel 30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag 28-Apr-08 JDB Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE 24-Apr-08 JDB Fixed single stepping through interrupts 20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags 03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode 26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA 15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, mp_mevff clear on interrupt with I/O instruction in trap cell 04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM) 28-Apr-07 RMS Removed clock initialization 02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter 11-Jan-07 JDB Added 12578A DMA byte packing 28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices 26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs Fixed improper interrupt servicing in resolve 21-Dec-06 JDB Added 21xx loader enable/disable support 16-Dec-06 JDB Added 2114 and 2115 CPU options. Added support for 12607B (2114) and 12578A (2115/6) DMA 01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64) SHOW CPU displays 1000-M/E instead of 21MX-M/E 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c 12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve 26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode 12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts resolve returns NOTE_INDINT to service held-off interrupt 16-Aug-06 JDB Added support for future microcode options, future F-Series 09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms Enhanced CPU option validity checking Added DCPC as a synonym for DMA for 21MX simulations 26-Dec-05 JDB Improved reporting in dev_conflict 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 21-Jan-05 JDB Reorganized CPU option flags 15-Jan-05 RMS Split out EAU and MAC instructions 26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan) DMA reset shouldn't clear control words (from Dave Bryan) Alternate CTL flop not visible as register (from Dave Bryan) Fixed CBS, SBS, TBS to perform virtual reads Separated A/B from M[0/1] for DMA IO (from Dave Bryan) Fixed bug in JPY (from Dave Bryan) 25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E) TIMER/EXECUTE/DIAG instructions disabled for 21MX-M T-register reflects changes in M-register when halted 25-Sep-04 JDB Moved MP into its own device; added MP option jumpers Modified DMA to allow disabling Modified SET CPU 2100/2116 to truncate memory > 32K Added -F switch to SET CPU to force memory truncation Fixed S-register behavior on 2116 Fixed LIx/MIx behavior for DMA on 2116 and 2100 Fixed LIx/MIx behavior for empty I/O card slots Modified WRU to be REG_HRO Added BRK and DEL to save console settings Fixed use of "unsigned int16" in cpu_reset Modified memory size routine to return SCPE_INCOMP if memory size truncation declined 20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan) Back up PC on instruction errors (from Dave Bryan) 14-May-04 RMS Fixed bugs and added features from Dave Bryan - SBT increments B after store - DMS console map must check dms_enb - SFS x,C and SFC x,C work - MP violation clears automatically on interrupt - SFS/SFC 5 is not gated by protection enabled - DMS enable does not disable mem prot checks - DMS status inconsistent at simulator halt - Examine/deposit are checking wrong addresses - Physical addresses are 20b not 15b - Revised DMS to use memory rather than internal format - Added instruction printout to HALT message - Added M and T internal registers - Added N, S, and U breakpoints Revised IBL facility to conform to microcode Added DMA EDT I/O pseudo-opcode Separated DMA SRQ (service request) from FLG 12-Mar-03 RMS Added logical name support 02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny) 22-Nov-02 RMS Added 21MX IOP support 24-Oct-02 RMS Fixed bugs in IOP and extended instructions Fixed bugs in memory protection and DMS Added clock calibration 25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer) 26-Jul-02 RMS Restructured extended instructions, added IOP support 22-Mar-02 RMS Changed to allocate memory array dynamically 11-Mar-02 RMS Cleaned up setjmp/auto variable interaction 17-Feb-02 RMS Added DMS support Fixed bugs in extended instructions 03-Feb-02 RMS Added terminal multiplexor support Changed PCQ macro to use unmodified PC Fixed flop restore logic (found by Bill McDermith) Fixed SZx,SLx,RSS bug (found by Bill McDermith) Added floating point support 16-Jan-02 RMS Added additional device support 07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith) 07-Dec-01 RMS Revised to use breakpoint package 03-Dec-01 RMS Added extended SET/SHOW support 10-Aug-01 RMS Removed register in declarations 26-Nov-00 RMS Fixed bug in dual device number routine 21-Nov-00 RMS Fixed bug in reset routine 15-Oct-00 RMS Added dynamic device number support References: - 2100A Computer Reference Manual (02100-90001, Dec-1971) - Model 2100A Computer Installation and Maintenance Manual (02100-90002, Aug-1972) - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - HP 1000 M/E/F-Series Computers I/O Interfacing Guide (02109-90006, Sep-1980) - 12607A Direct Memory Access Operating and Service Manual (12607-90002, Jan-1970) - 12578A/12578A-01 Direct Memory Access Operating and Service Manual (12578-9001, Mar-1972) - 12892B Memory Protect Installation Manual (12892-90007, Jun-1978) The register state for the HP 2116 CPU is: AR<15:0> A register - addressable as location 0 BR<15:0> B register - addressable as location 1 PC<14:0> P register - program counter SR<15:0> S register - switch register MR<14:0> M register - memory address TR<15:0> T register - memory data E extend flag (carry out) O overflow flag The 2100 adds memory protection logic: mp_fence<14:0> memory fence register mp_viol<15:0> memory protection violation register (F register) The 21MX adds a pair of index registers and memory expansion logic: XR<15:0> X register YR<15:0> Y register dms_sr<15:0> dynamic memory system status register dms_vr<15:0> dynamic memory system violation register The original HP 2116 has four instruction formats: memory reference, shift, alter/skip, and I/O. The HP 2100 added extended memory reference and extended arithmetic. The HP21MX added extended byte, bit, and word instructions as well as extended memory. The memory reference format is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| op |cp| offset | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <14:11> mnemonic action 0010 AND A = A & M[MA] 0011 JSB M[MA] = P, P = MA + 1 0100 XOR A = A ^ M[MA] 0101 JMP P = MA 0110 IOR A = A | M[MA] 0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 1000 ADA A = A + M[MA] 1001 ADB B = B + M[MA] 1010 CPA skip if A != M[MA] 1011 CPB skip if B != M[MA] 1100 LDA A = M[MA] 1101 LDB B = M[MA] 1110 STA M[MA] = A 1111 STB M[MA] = B <15,10> mode action 0,0 page zero direct MA = IR<9:0> 0,1 current page direct MA = PC<14:0>'IR,9:0> 1,0 page zero indirect MA = M[IR<9:0>] 1,1 current page indirect MA = M[PC<14:10>'IR<9:0>] Memory reference instructions can access an address space of 32K words. An instruction can directly reference the first 1024 words of memory (called page zero), as well as 1024 words of the current page; it can indirectly access all 32K. The shift format is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | \---+---/ | | | \---+---/ | | | | | | | | | | | | | +---- shift 2 opcode | | | | | +---------- skip if low bit == 0 | | | | +------------- shift 2 enable | | | +---------------- clear Extend | | +---------------------- shift 1 opcode | +---------------------------- shift 1 enable +---------------------------------- A/B select The alter/skip format is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | \-+-/ \-+-/ | | | | | | | | | | | | | | +- reverse skip sense | | | | | | | +---- skip if register == 0 | | | | | | +------- increment register | | | | | +---------- skip if low bit == 0 | | | | +------------- skip if sign bit == 0 | | | +---------------- skip if Extend == 0 | | +--------------------- clr/com/set Extend | +--------------------------- clr/com/set register +---------------------------------- A/B select The I/O transfer format is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | \---+---/\-------+-------/ | | | | | | | +--------- device select | | +---------------------- opcode | +---------------------------- hold/clear flag +---------------------------------- A/B select The IO transfer instruction controls the specified device. Depending on the opcode, the instruction may set or clear the device flag, start or stop I/O, or read or write data. The 2100 added an extended memory reference instruction; the 21MX added extended arithmetic, operate, byte, word, and bit instructions. Note that the HP 21xx is, despite the right-to-left bit numbering, a big endian system. Bits <15:8> are byte 0, and bits <7:0> are byte 1. The extended memory reference format (HP 2100) is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 0 0 0|op| 0| opcode | extended mem ref +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| operand address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The extended arithmetic format (HP 2100) is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The extended operate format (HP 21MX) is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The extended byte and word format (HP 21MX) is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| operand address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The extended bit operate format (HP 21MX) is: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| operand address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| operand address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered infinite indirection loop unimplemented instruction and stop_inst flag set unknown I/O device and stop_dev flag set I/O error in I/O simulator 2. Interrupts. I/O devices are modelled by substituting software states for I/O backplane signals. Signals generated by I/O instructions and DMA cycles are dispatched to the target device for action. Backplane signals are processed sequentially, except for the "clear flag" signal, which may be generated in parallel with another signal. For example, the "STC sc,C" instruction generates the "set control" and the "clear flag" signals concurrently. CPU interrupt signals are modelled as three parallel arrays: - device request priority as bit vector dev_prl [2] [31..0] - device interrupt requests as bit vector dev_irq [2] [31..0] - device service requests as bit vector dev_srq [2] [31..0] Each array forms a 64-bit vector, with bits 0-31 of the first element corresponding to select codes 00-37 octal, and bits 0-31 of the second element corresponding to select codes 40-77 octal. The HP 2100 interrupt structure is based on the PRH, PRL, IRQ, and IAK signals. PRH indicates that no higher-priority device is interrupting. PRL indicates to lower-priority devices that a given device is not interrupting. IRQ indicates that a given device is requesting an interrupt. IAK indicates that the given device's interrupt request is being acknowledged. PRH and PRL form a hardware priority chain that extends from interface to interface on the backplane. We model just PRL, as PRH is calculated from the PRLs of higher-priority devices. Typical I/O devices have a flag, flag buffer, and control flip-flop. If a device's flag, flag buffer, and control bits are set, and the device is the highest priority on the interrupt chain, it requests an interrupt by asserting IRQ. When the interrupt is acknowledged with IAK, the flag buffer is cleared, preventing further interrupt requests from that device. The combination of flag and control set blocks interrupts from lower priority devices. Service requests are used to trigger the DMA service logic. Setting the device flag typically also sets SRQ, although SRQ may be calculated independently. 3. Non-existent memory. On the HP 2100, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes need be checked against memory size. On the 21xx machines, doing SET CPU LOADERDISABLE decreases available memory size by 64 words. 4. Adding I/O devices. These modules must be modified: hp2100_defs.h add interrupt request definition hp2100_sys.c add sim_devices table entry 5. Instruction interruptibility. The simulator is fast enough, compared to the run-time of the longest instructions, for interruptibility not to matter. But the HP diagnostics explicitly test interruptibility in EIS and DMS instructions, and long indirect address chains. Accordingly, the simulator does "just enough" to pass these tests. In particular, if an interrupt is pending but deferred at the beginning of an interruptible instruction, the interrupt is taken at the appropriate point; but there is no testing for new interrupts during execution (that is, the event timer is not called). 6. Interrupt deferral. At instruction fetch time, a pending interrupt request will be deferred if the previous instruction was a JMP indirect, JSB indirect, STC, CLC, STF, CLF, or was executing from an interrupt trap cell. In addition, the following instructions will cause deferral on the 1000 series: SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, and UJS. On the HP 1000, the request is always deferred until after the current instruction completes. On the 21xx, the request is deferred unless the current instruction is an MRG instruction other than JMP or JMP,I or JSB,I. Note that for the 21xx, SFS and SFC are not included in the deferral criteria. 7. Terminology. The 1000 series of computers was originally called the 21MX at introduction. The 21MX (occasionally, 21MXM) corresponds to the 1000 M-Series, and the 21MXE (occasionally, 21XE) corresponds to the 1000 E-Series. The model numbers were changed before the introduction of the 1000 F-Series, although some internal HP documentation refers to a 21MXF. The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI (Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used somewhat interchangeably to refer to the logical-to-physical memory address translation option provided on the 1000-Series. DMS consists of the MEM card (12731A) and the DMI firmware (13307A). However, MEM and MEU have been used interchangeably to refer to the mapping card, as have DMI and DMS to refer to the firmware instructions. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" /* Memory protect constants */ #define UNIT_V_MP_JSB (UNIT_V_UF + 0) /* MP jumper W5 */ #define UNIT_V_MP_INT (UNIT_V_UF + 1) /* MP jumper W6 */ #define UNIT_V_MP_SEL1 (UNIT_V_UF + 2) /* MP jumper W7 */ #define UNIT_MP_JSB (1 << UNIT_V_MP_JSB) /* 1 = W5 is out */ #define UNIT_MP_INT (1 << UNIT_V_MP_INT) /* 1 = W6 is out */ #define UNIT_MP_SEL1 (1 << UNIT_V_MP_SEL1) /* 1 = W7 is out */ /* DMA channels */ #define DMA_OE 020000000000 /* byte packing odd/even flag */ #define DMA1_STC 0100000 /* DMA - issue STC */ #define DMA1_PB 0040000 /* DMA - pack bytes */ #define DMA1_CLC 0020000 /* DMA - issue CLC */ #define DMA2_OI 0100000 /* DMA - output/input */ struct DMA { /* DMA channel */ uint32 cw1; /* device select */ uint32 cw2; /* direction, address */ uint32 cw3; /* word count */ uint32 latency; /* 1st cycle delay */ uint32 packer; /* byte-packer holding reg */ }; #define DMAR0 1 #define DMAR1 2 /* Command line switches */ #define ALL_BKPTS (SWMASK('E')|SWMASK('N')|SWMASK('S')|SWMASK('U')) #define ALL_MAPMODES (SWMASK('S')|SWMASK('U')|SWMASK('P')|SWMASK('Q')) /* RTE base-page addresses. */ static const uint32 xeqt = 0001717; /* XEQT address */ static const uint32 tbg = 0001674; /* TBG address */ /* DOS base-page addresses. */ static const uint32 m64 = 0000040; /* constant -64 address */ static const uint32 p64 = 0000067; /* constant +64 address */ /* CPU local data */ static uint32 jsb_plb = 2; /* protected lower bound for JSB */ static uint32 saved_MR = 0; /* between executions */ static uint32 fwanxm = 0; /* first word addr of nx mem */ /* CPU global data */ uint16 *M = NULL; /* memory */ uint16 ABREG[2]; /* A/B registers */ uint32 PC = 0; /* P register */ uint32 SR = 0; /* S register */ uint32 MR = 0; /* M register */ uint32 TR = 0; /* T register */ uint32 XR = 0; /* X register */ uint32 YR = 0; /* Y register */ uint32 E = 0; /* E register */ uint32 O = 0; /* O register */ FLIP_FLOP ion = CLEAR; /* interrupt enable */ t_bool ion_defer = FALSE; /* interrupt defer */ uint32 intaddr = 0; /* interrupt addr */ uint32 stop_inst = 1; /* stop on ill inst */ uint32 stop_dev = 0; /* stop on ill dev */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ uint32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ uint32 dev_prl [2] = { ~(uint32) 0, ~(uint32) 0 }; /* device priority low bit vector */ uint32 dev_irq [2] = { 0, 0 }; /* device interrupt request bit vector */ uint32 dev_srq [2] = { 0, 0 }; /* device service request bit vector */ /* Memory protect global data */ FLIP_FLOP mp_control = CLEAR; /* MP control flip-flop */ FLIP_FLOP mp_flag = CLEAR; /* MP flag flip-flop */ FLIP_FLOP mp_flagbuf = CLEAR; /* MP flag buffer flip-flop */ FLIP_FLOP mp_mevff = CLEAR; /* memory expansion violation flip-flop */ FLIP_FLOP mp_evrff = SET; /* enable violation register flip-flop */ uint32 mp_fence = 0; /* MP fence register */ uint32 mp_viol = 0; /* MP violation register */ uint32 iop_sp = 0; /* iop stack reg */ uint32 ind_max = 16; /* iadr nest limit */ uint32 err_PC = 0; /* error PC */ jmp_buf save_env; /* MP abort handler */ /* DMA global data */ struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */ FLIP_FLOP dma_xferen [2] = { CLEAR, CLEAR }; /* transfer enable flip-flops */ FLIP_FLOP dma_control [2] = { CLEAR, CLEAR }; /* control flip-flops */ FLIP_FLOP dma_flag [2] = { CLEAR, CLEAR }; /* flag flip-flops */ FLIP_FLOP dma_flagbuf [2] = { CLEAR, CLEAR }; /* flag buffer flip-flops */ FLIP_FLOP dma_select [2] = { CLEAR, CLEAR }; /* register select flip-flops */ /* Dynamic mapping system global data */ uint32 dms_enb = 0; /* dms enable */ uint32 dms_ump = 0; /* dms user map */ uint32 dms_sr = 0; /* dms status reg */ uint32 dms_vr = 0; /* dms violation reg */ uint16 dms_map[MAP_NUM * MAP_LNT] = { 0 }; /* dms maps */ /* External data */ extern int32 sim_interval; extern int32 sim_int_char; extern int32 sim_brk_char; extern int32 sim_del_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern DEVICE *sim_devices[]; extern char halt_msg[]; extern t_bool sim_idle_enab; extern DIB clk_dib; /* CLK DIB for idle check */ /* CPU local routines */ static t_stat Ea (uint32 IR, uint32 *addr, uint32 irq); static uint16 ReadTAB (uint32 va); static uint32 dms (uint32 va, uint32 map, uint32 prot); static uint32 shift (uint32 inval, uint32 flag, uint32 oper); static void dma_cycle (uint32 chan, uint32 map); static uint32 calc_dma (void); static t_bool dev_conflict (void); static uint32 devdisp (uint32 select_code, IOSIG signal, uint32 data); /* CPU global routines */ 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_boot (int32 unitno, DEVICE *dptr); t_stat mp_reset (DEVICE *dptr); t_stat dma0_reset (DEVICE *dptr); t_stat dma1_reset (DEVICE *dptr); t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc); t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc); t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc); t_stat cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc); t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc); t_stat cpu_set_idle (UNIT *uptr, int32 option, char *cptr, void *desc); t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); void hp_post_cmd (t_bool from_scp); uint32 cpuio (uint32 select_code, IOSIG signal, uint32 data); uint32 ovflio (uint32 select_code, IOSIG signal, uint32 data); uint32 pwrfio (uint32 select_code, IOSIG signal, uint32 data); uint32 protio (uint32 select_code, IOSIG signal, uint32 data); uint32 dmasio (uint32 select_code, IOSIG signal, uint32 data); uint32 dmapio (uint32 select_code, IOSIG signal, uint32 data); uint32 nullio (uint32 select_code, IOSIG signal, uint32 data); /* External routines */ extern t_stat cpu_eau (uint32 IR, uint32 intrq); extern t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap); extern t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap); extern void (*sim_vm_post) (t_bool from_scp); /* Table of CPU features by model. Fields: - typ: standard features plus typically configured options. - opt: complete list of optional features. - maxmem: maximum configurable memory in 16-bit words. Features in the "typical" list are enabled when the CPU model is selected. If a feature appears in the "typical" list but NOT in the "optional" list, then it is standard equipment and cannot be disabled. If a feature appears in the "optional" list, then it may be enabled or disabled as desired by the user. */ struct FEATURE_TABLE { /* CPU model feature table: */ uint32 typ; /* - typical features */ uint32 opt; /* - optional features */ uint32 maxmem; /* - maximum memory */ }; static struct FEATURE_TABLE cpu_features[] = { /* features in UNIT_xxxx order*/ { UNIT_DMA | UNIT_MP, /* UNIT_2116 */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU, 32768 }, { UNIT_DMA, /* UNIT_2115 */ UNIT_PFAIL | UNIT_DMA | UNIT_EAU, 8192 }, { UNIT_DMA, /* UNIT_2114 */ UNIT_PFAIL | UNIT_DMA, 16384 }, { 0, 0, 0 }, { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* UNIT_2100 */ UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP, 32768 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_M */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | UNIT_IOP | UNIT_FFP | UNIT_DS, 1048576 }, { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_E */ UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA, 1048576 }, { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* UNIT_1000_F */ UNIT_FFP | UNIT_DBI | UNIT_DMS, UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_VIS | UNIT_IOP | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA, 1048576 } }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list cpu_deb CPU debug flags */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 0) }; REG cpu_reg[] = { { ORDATA (P, PC, 15) }, { ORDATA (A, AR, 16), REG_FIT }, { ORDATA (B, BR, 16), REG_FIT }, { ORDATA (M, MR, 15) }, { ORDATA (T, TR, 16), REG_RO }, { ORDATA (X, XR, 16) }, { ORDATA (Y, YR, 16) }, { ORDATA (S, SR, 16) }, { FLDATA (E, E, 0) }, { FLDATA (O, O, 0) }, { FLDATA (ION, ion, 0) }, { FLDATA (ION_DEFER, ion_defer, 0) }, { ORDATA (CIR, intaddr, 6) }, { FLDATA (DMSENB, dms_enb, 0) }, { FLDATA (DMSCUR, dms_ump, VA_N_PAG) }, { ORDATA (DMSSR, dms_sr, 16) }, { ORDATA (DMSVR, dms_vr, 16) }, { BRDATA (DMSMAP, dms_map, 8, 16, MAP_NUM * MAP_LNT) }, { ORDATA (IOPSP, iop_sp, 16) }, { FLDATA (STOP_INST, stop_inst, 0) }, { FLDATA (STOP_DEV, stop_dev, 1) }, { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (JSBPLB, jsb_plb, 32), REG_HRO }, { ORDATA (SAVEDMR, saved_MR, 32), REG_HRO }, { ORDATA (FWANXM, fwanxm, 32), REG_HRO }, { ORDATA (WRU, sim_int_char, 8), REG_HRO }, { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, { ORDATA (DEL, sim_del_char, 8), REG_HRO }, { BRDATA (PRL, dev_prl, 8, 32, 2), REG_HRO }, { BRDATA (IRQ, dev_irq, 8, 32, 2), REG_HRO }, { BRDATA (SRQ, dev_srq, 8, 32, 2), REG_HRO }, { NULL } }; /* CPU modifier table. The 21MX monikers are deprecated in favor of the 1000 designations. See the "HP 1000 Series Naming History" on the back inside cover of the Technical Reference Handbook. */ MTAB cpu_mod[] = { { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, "2116" }, { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, "2115" }, { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, "2114" }, { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, "2100" }, { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, "1000-E" }, { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, "1000-E" }, { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, "1000-M" }, { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, "1000-M" }, #if defined (HAVE_INT64) { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, "1000-F" }, #endif { MTAB_XTD | MTAB_VDV, 1, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "NOIDLE", &cpu_set_idle, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "LOADERENABLE", &cpu_set_ldr, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "LOADERDISABLE", &cpu_set_ldr, NULL, NULL }, { UNIT_EAU, UNIT_EAU, "EAU", "EAU", &cpu_set_opt, NULL, NULL }, { UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_EAU, NULL, "NOEAU", &cpu_clr_opt, NULL, NULL }, { UNIT_FP, UNIT_FP, "FP", "FP", &cpu_set_opt, NULL, NULL }, { UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_FP, NULL, "NOFP", &cpu_clr_opt, NULL, NULL }, { UNIT_IOP, UNIT_IOP, "IOP", "IOP", &cpu_set_opt, NULL, NULL }, { UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_IOP, NULL, "NOIOP", &cpu_clr_opt, NULL, NULL }, { UNIT_DMS, UNIT_DMS, "DMS", "DMS", &cpu_set_opt, NULL, NULL }, { UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_DMS, NULL, "NODMS", &cpu_clr_opt, NULL, NULL }, { UNIT_FFP, UNIT_FFP, "FFP", "FFP", &cpu_set_opt, NULL, NULL }, { UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_FFP, NULL, "NOFFP", &cpu_clr_opt, NULL, NULL }, { UNIT_DBI, UNIT_DBI, "DBI", "DBI", &cpu_set_opt, NULL, NULL }, { UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_DBI, NULL, "NODBI", &cpu_clr_opt, NULL, NULL }, { UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &cpu_set_opt, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_EMA, NULL, "NOEMA", &cpu_clr_opt, NULL, NULL }, { UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &cpu_set_opt, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_VMAOS, NULL, "NOVMA", &cpu_clr_opt, NULL, NULL }, { UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &cpu_set_opt, NULL, NULL }, #if defined (HAVE_INT64) { UNIT_VIS, UNIT_VIS, "VIS", "VIS", &cpu_set_opt, NULL, NULL }, { UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_VIS, NULL, "NOVIS", &cpu_clr_opt, NULL, NULL }, { UNIT_SIGNAL, UNIT_SIGNAL,"SIGNAL", "SIGNAL", &cpu_set_opt, NULL, NULL }, { UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &cpu_clr_opt, NULL, NULL }, #endif /* Future microcode support. { UNIT_DS, UNIT_DS, "DS", "DS", &cpu_set_opt, NULL, NULL }, { UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_DS, NULL, "NODS", &cpu_clr_opt, NULL, NULL }, */ { MTAB_XTD | MTAB_VDV, 4096, NULL, "4K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 8192, NULL, "8K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 12288, NULL, "12K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 16384, NULL, "16K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 24576, NULL, "24K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 32768, NULL, "32K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 65536, NULL, "64K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 131072, NULL, "128K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 262144, NULL, "256K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 524288, NULL, "512K", &cpu_set_size, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1048576, NULL, "1024K", &cpu_set_size, NULL, NULL }, { 0 } }; DEBTAB cpu_deb[] = { { "OS", DEB_OS }, { "OSTBG", DEB_OSTBG }, { "VMA", DEB_VMA }, { "EMA", DEB_EMA }, { "VIS", DEB_VIS }, { "SIG", DEB_SIG }, { NULL, 0 } }; DEVICE cpu_dev = { "CPU", /* device name */ &cpu_unit, /* unit array */ cpu_reg, /* register array */ cpu_mod, /* modifier array */ 1, /* number of units */ 8, /* address radix */ PA_N_SIZE, /* address width */ 1, /* address increment */ 8, /* data radix */ 16, /* data width */ &cpu_ex, /* examine routine */ &cpu_dep, /* deposit routine */ &cpu_reset, /* reset routine */ &cpu_boot, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ NULL, /* device information block */ DEV_DEBUG, /* device flags */ 0, /* debug control flags */ cpu_deb, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* Memory protect data structures mp_dib MP device information block mp_dev MP device descriptor mp_unit MP unit descriptor mp_reg MP register list mp_mod MP modifiers list */ DIB mp_dib = { PRO, &protio }; UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; /* default is JSB in, INT in, SEL1 out */ REG mp_reg[] = { { FLDATA (CTL, mp_control, 0) }, { FLDATA (FLG, mp_flag, 0) }, { FLDATA (FBF, mp_flagbuf, 0) }, { ORDATA (FR, mp_fence, 15) }, { ORDATA (VR, mp_viol, 16) }, { FLDATA (EVR, mp_evrff, 0) }, { FLDATA (MEV, mp_mevff, 0) }, { NULL } }; MTAB mp_mod[] = { { UNIT_MP_JSB, UNIT_MP_JSB, "JSB (W5) out", "JSBOUT", NULL }, { UNIT_MP_JSB, 0, "JSB (W5) in", "JSBIN", NULL }, { UNIT_MP_INT, UNIT_MP_INT, "INT (W6) out", "INTOUT", NULL }, { UNIT_MP_INT, 0, "INT (W6) in", "INTIN", NULL }, { UNIT_MP_SEL1, UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT", NULL }, { UNIT_MP_SEL1, 0, "SEL1 (W7) in", "SEL1IN", NULL }, { 0 } }; DEVICE mp_dev = { "MP", /* device name */ &mp_unit, /* unit array */ mp_reg, /* register array */ mp_mod, /* modifier array */ 1, /* number of units */ 8, /* address radix */ 1, /* address width */ 1, /* address increment */ 8, /* data radix */ 16, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &mp_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &mp_dib, /* device information block */ DEV_DISABLE | DEV_DIS, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* DMA controller data structures dmax_dib DMAx device information block dmax_dev DMAx device descriptor dmax_reg DMAx register list */ DIB dma0_dib = { DMA0, &dmapio }; UNIT dma0_unit = { UDATA (NULL, 0, 0) }; REG dma0_reg[] = { { FLDATA (XFR, dma_xferen [0], 0) }, { FLDATA (CTL, dma_control [0], 0) }, { FLDATA (FLG, dma_flag [0], 0) }, { FLDATA (FBF, dma_flagbuf [0], 0) }, { FLDATA (CTL2, dma_select [0], 0) }, { ORDATA (CW1, dmac[0].cw1, 16) }, { ORDATA (CW2, dmac[0].cw2, 16) }, { ORDATA (CW3, dmac[0].cw3, 16) }, { DRDATA (LATENCY, dmac[0].latency, 8) }, { FLDATA (BYTE, dmac[0].packer, 31) }, { ORDATA (PACKER, dmac[0].packer, 8) }, { NULL } }; DEVICE dma0_dev = { "DMA0", /* device name */ &dma0_unit, /* unit array */ dma0_reg, /* register array */ NULL, /* modifier array */ 1, /* number of units */ 8, /* address radix */ 1, /* address width */ 1, /* address increment */ 8, /* data radix */ 16, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &dma0_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &dma0_dib, /* device information block */ DEV_DISABLE, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ DIB dma1_dib = { DMA1, &dmapio }; UNIT dma1_unit = { UDATA (NULL, 0, 0) }; REG dma1_reg[] = { { FLDATA (XFR, dma_xferen [1], 0) }, { FLDATA (CTL, dma_control [1], 0) }, { FLDATA (FLG, dma_flag [1], 0) }, { FLDATA (FBF, dma_flagbuf [1], 0) }, { FLDATA (CTL3, dma_select [1], 0) }, { ORDATA (CW1, dmac[1].cw1, 16) }, { ORDATA (CW2, dmac[1].cw2, 16) }, { ORDATA (CW3, dmac[1].cw3, 16) }, { DRDATA (LATENCY, dmac[1].latency, 8) }, { FLDATA (BYTE, dmac[1].packer, 31) }, { ORDATA (PACKER, dmac[1].packer, 8) }, { NULL } }; DEVICE dma1_dev = { "DMA1", /* device name */ &dma1_unit, /* unit array */ dma1_reg, /* register array */ NULL, /* modifier array */ 1, /* number of units */ 8, /* address radix */ 1, /* address width */ 1, /* address increment */ 8, /* data radix */ 16, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &dma1_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ NULL, /* detach routine */ &dma1_dib, /* device information block */ DEV_DISABLE, /* device flags */ 0, /* debug control flags */ NULL, /* debug flag name table */ NULL, /* memory size change routine */ NULL }; /* logical device name */ /* Interrupt deferral table (1000 version) */ /* Deferral for I/O subops: soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */ static t_bool defer_tab [] = { FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE }; /* Device dispatch table */ IODISP *dtab[64] = { &cpuio, &ovflio }; /* init with immutable devices */ /* Execute CPU instructions. This routine is the instruction decode routine for the HP 2100. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. */ t_stat sim_instr (void) { uint32 intrq, dmarq; /* set after setjmp */ uint32 iotrap = 0; /* set after setjmp */ t_stat reason; /* set after setjmp */ int32 i; /* temp */ DEVICE *dptr; /* temp */ DIB *dibp; /* temp */ int abortval; /* Restore register state */ if (dev_conflict ()) return SCPE_STOP; /* check consistency */ err_PC = PC = PC & VAMASK; /* load local PC */ reason = 0; /* Restore I/O state */ dev_prl [0] = dev_prl [1] = ~(uint32) 0; /* set all priority lows */ dev_irq [0] = dev_irq [1] = 0; /* clear all interrupt requests */ dev_srq [0] = dev_srq [1] = 0; /* clear all service requests */ for (i = OPTDEV; i <= I_DEVMASK; i++) /* default optional devices dispatch */ dtab [i] = &nullio; dtab [PWR] = &pwrfio; /* for now, powerfail is always present */ for (i = 0; dptr = sim_devices [i]; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* exist, enabled? */ dtab [dibp->devno] = dibp->iot; /* set I/O signal dispatch */ dtab [dibp->devno] (dibp->devno, ioSIR, 0); /* set interrupt request state */ } } if (dtab [DMA0] == &dmapio) /* first DMA channel enabled? */ dtab [DMALT0] = &dmasio; /* set up secondary handler */ if (dtab [DMA1] == &dmapio) /* second DMA channel enabled? */ dtab [DMALT1] = &dmasio; /* set up secondary handler */ /* Configure interrupt deferral table */ if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* 21xx series? */ defer_tab [soSFC] = defer_tab [soSFS] = FALSE; /* SFC/S doesn't defer */ else /* 1000 series */ defer_tab [soSFC] = defer_tab [soSFS] = TRUE; /* SFC/S does defer */ /* Set MP abort handling. If an abort occurs in memory protection, the relocation routine executes a longjmp to this area OUTSIDE the main simulation loop. Memory protection errors are the only sources of aborts in the HP 2100. All referenced variables must be globals, and all sim_instr scoped automatics must be set after the setjmp. To initiate an MP abort, use the MP_ABORT macro and pass the violation address. MP_ABORT should only be called if "mp_control" is SET, as aborts do not occur if MP is turned off. An MP interrupt (SC 05) is qualified by "ion" but not by "ion_defer". If the interrupt system is off when an MP violation is detected, the violating instruction will be aborted, even though no interrupt occurs. In this case, neither the flag nor flag buffer are set, and EVR is not cleared. Implementation notes: 1. The protected lower bound address for the JSB instruction depends on the W5 jumper setting. If W5 is in, then the lower bound is 2, allowing JSBs to the A and B registers. If W5 is out, then the lower bound is 0, just as with JMP. 2. The violation address is passed to enable the MEM violation register to be updated. The "longjmp" routine will not pass a value of 0; it is converted internally to 1. This is OK, because only the page number of the address value is used, and locations 0 and 1 are both on page 0. 3. This routine is used both for MP and MEM violations. The MEV flip-flop will be clear for the former and set for the latter. The MEV violation register will be updated by "dms_upd_vr" only if the call is NOT for an MEM violation; if it is, then the VR has already been set and should not be disturbed. */ jsb_plb = (mp_unit.flags & UNIT_MP_JSB) ? 0 : 2; /* set protected lower bound for JSB */ abortval = setjmp (save_env); /* set abort hdlr */ if (abortval) { /* memory protect abort? */ dms_upd_vr (abortval); /* update violation register (if not MEV) */ if (ion) /* interrupt system on? */ protio (PRO, ioENF, 0); /* set flag */ } dmarq = calc_dma (); /* initial recalc of DMA masks */ intrq = calc_int (); /* initial recalc of interrupts */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ uint32 IR, MA, absel, v1, t, skip; if (sim_interval <= 0) { /* event timeout? */ if (reason = sim_process_event ()) /* process event service */ break; /* service status not OK */ dmarq = calc_dma (); /* recalc DMA reqs */ intrq = calc_int (); /* recalc interrupts */ } if (dmarq) { if (dmarq & DMAR0) /* DMA channel 1 request? */ dma_cycle (0, PAMAP); /* do one DMA cycle using port A map */ if (dmarq & DMAR1) /* DMA channel 2 request? */ dma_cycle (1, PBMAP); /* do one DMA cycle using port B map */ dmarq = calc_dma (); /* recalc DMA requests */ intrq = calc_int (); /* recalc interrupts */ } if (intrq && ion_defer) /* interrupt pending but deferred? */ ion_defer = calc_defer (); /* confirm deferral */ /* Check for pending interrupt request. Interrupt recognition is controlled by three state variables: "ion", "ion_defer", and "intrq". "ion" corresponds to the INTSYS flip-flop in the 1000 CPU, "ion_defer" corresponds to the INTEN flip-flop, and "intrq" corresponds to the NRMINT flip-flop. STF 00 and CLF 00 set and clear INTSYS, turning the interrupt system on and off. Micro-orders ION and IOFF set and clear INTEN, deferring or allowing certain interrupts. An IRQ signal from a device, qualified by the corresponding PRL signal, will set NRMINT to request a normal interrupt; an IOFF or IAK will clear it. Under simulation, "ion" is controlled by STF/CLF 00. "ion_defer" is set or cleared as appropriate by the individual instruction simulators. "intrq" is set to the successfully interrupting device's select code, or to zero if there is no qualifying interrupt request. Presuming PRL is set to allow priority to an interrupting device: 1. Power fail (SC 04) may interrupt if "ion_defer" is clear; this is not conditional on "ion" being set. 2. Memory protect (SC 05) may interrupt if "ion" is set; this is not conditional on "ion_defer" being clear. 3. Parity error (SC 05) may interrupt always; this is not conditional on "ion" being set or "ion_defer" being clear. 4. All other devices (SC 06 and up) may interrupt if "ion" is set and "ion_defer" is clear. Qualification with "ion" is performed by "calc_int", except for case 2, which is qualified by the MP abort handler above (because qualification occurs on the MP card, rather than in the CPU). Therefore, we need only qualify by "ion_defer" here. */ if (intrq && ((intrq == PRO) || !ion_defer)) { /* interrupt request? */ if (sim_brk_summ && /* any breakpoints? */ sim_brk_test (intrq, SWMASK ('E') | /* unconditional or right type for DMS? */ (dms_enb ? SWMASK ('S') : SWMASK ('N')))) { reason = STOP_IBKPT; /* stop simulation */ break; } intaddr = intrq; /* save int addr in CIR */ intrq = 0; /* clear request */ ion_defer = TRUE; /* defer interrupts */ iotrap = 1; /* mark as I/O trap cell instr */ if (dms_enb) /* dms enabled? */ dms_sr = dms_sr | MST_ENBI; /* set in status */ else /* not enabled */ dms_sr = dms_sr & ~MST_ENBI; /* clear in status */ if (dms_ump) { /* user map enabled at interrupt? */ dms_sr = dms_sr | MST_UMPI; /* set in status */ dms_ump = SMAP; /* switch to system map */ } else /* system map enabled at interrupt */ dms_sr = dms_sr & ~MST_UMPI; /* clear in status */ IR = ReadW (intaddr); /* get trap cell instruction */ devdisp (intaddr, ioIAK, IR); /* acknowledge interrupt */ if (intaddr != PRO) /* not MP interrupt? */ protio (intaddr, ioIAK, IR); /* send IAK for device to MP too */ } else { /* normal instruction */ iotrap = 0; /* not a trap cell instruction */ err_PC = PC; /* save PC for error */ if (sim_brk_summ && /* any breakpoints? */ sim_brk_test (PC, SWMASK ('E') | /* unconditional or */ (dms_enb? (dms_ump? SWMASK ('U'): SWMASK ('S')): SWMASK ('N')))) { /* or right type for DMS? */ reason = STOP_IBKPT; /* stop simulation */ break; } if (mp_evrff) mp_viol = PC; /* if ok, upd mp_viol */ IR = ReadW (PC); /* fetch instr */ PC = (PC + 1) & VAMASK; ion_defer = FALSE; } sim_interval = sim_interval - 1; /* count instruction */ /* Instruction decode. The 21MX does a 256-way decode on IR<15:8> 15 14 13 12 11 10 09 08 instruction x <-!= 0-> x x x x memory reference 0 0 0 0 x 0 x x shift 0 0 0 0 x 0 x x alter-skip 1 0 0 0 x 1 x x IO 1 0 0 0 0 0 x 0 extended arithmetic 1 0 0 0 0 0 0 1 divide (decoded as 100400) 1 0 0 0 1 0 0 0 double load (decoded as 104000) 1 0 0 0 1 0 0 1 double store (decoded as 104400) 1 0 0 0 1 0 1 0 extended instr group 0 (A/B must be set) 1 0 0 0 x 0 1 1 extended instr group 1 (A/B ignored) */ absel = (IR & I_AB)? 1: 0; /* get A/B select */ switch ((IR >> 8) & 0377) { /* decode IR<15:8> */ /* Memory reference instructions */ case 0020:case 0021:case 0022:case 0023: case 0024:case 0025:case 0026:case 0027: case 0220:case 0221:case 0222:case 0223: case 0224:case 0225:case 0226:case 0227: if (reason = Ea (IR, &MA, intrq)) break; /* AND */ AR = AR & ReadW (MA); break; /* JSB is a little tricky. It is possible to generate both an MP and a DM violation simultaneously, as the MP and MEM cards validate in parallel. Consider a JSB to a location under the MP fence and on a write-protected page. This situation must be reported as a DM violation, because it has priority (SFS 5 and SFC 5 check only the MEVFF, which sets independently of the MP fence violation). Under simulation, this means that DM violations must be checked, and the MEVFF must be set, before an MP abort is taken. This is done by the "mp_dms_jmp" routine. */ case 0230:case 0231:case 0232:case 0233: case 0234:case 0235:case 0236:case 0237: ion_defer = TRUE; /* defer if JSB,I */ case 0030:case 0031:case 0032:case 0033: case 0034:case 0035:case 0036:case 0037: if (reason = Ea (IR, &MA, intrq)) break; /* JSB */ mp_dms_jmp (MA, jsb_plb); /* validate jump address */ WriteW (MA, PC); /* store PC */ PCQ_ENTRY; PC = (MA + 1) & VAMASK; /* jump */ break; case 0040:case 0041:case 0042:case 0043: case 0044:case 0045:case 0046:case 0047: case 0240:case 0241:case 0242:case 0243: case 0244:case 0245:case 0246:case 0247: if (reason = Ea (IR, &MA, intrq)) break; /* XOR */ AR = AR ^ ReadW (MA); break; /* CPU idle processing. The 21xx/1000 CPUs have no "wait for interrupt" instruction. Idling in HP operating systems consists of sitting in "idle loops" that end with JMP instructions. We test for certain known patterns when a JMP instruction is executed to decide if the simulator should idle. Idling must not occur if an interrupt is pending. As mentioned in the "General Notes" above, HP CPUs will defer interrupts if certain instructions are executed. OS interrupt handlers exit via such deferring instructions. If there is a pending interrupt when the OS is otherwise idle, the idle loop will execute one instruction before reentering the interrupt handler. If we call sim_idle() in this case, we will lose interrupts. Consider the situation in RTE. Under simulation, the TTY and CLK events are co-scheduled, with the CLK expiring one instruction after the TTY. When the TTY interrupts, $CIC in RTE is entered. One instruction later, the CLK expires and posts its interrupt, but it is not immediately handled, because the JSB $CIC,I / JMP $CIC0,I / SFS 0,C instruction entry sequence continually defers interrupts until the interrupt system is turned off. When $CIC returns via $IRT, one instruction of the idle loop is executed, even though the CLK interrupt is still pending, because the UJP instruction used to return also defers interrupts. If sim_idle() is called at this point, the simulator will sleep when it should be handling the pending CLK interrupt. When it awakes, TTY expiration will be moved forward to the next instruction. The still-pending CLK interrupt will then be recognized, and $CIC will be entered. But the TTY and then the CLK will then expire and attempt to interrupt again, although they are deferred by the $CIC entry sequence. This causes the second CLK interrupt to be missed, as processing of the first one is just now being started. Similarly, at the end of the CLK handling, the TTY interrupt is still pending. When $IRT returns to the idle loop, sim_idle() would be called again, so the TTY and then CLK interrupt a third time. Because the second TTY interrupt is still pending, $CIC is entered, but the third TTY interrupt is lost. We solve this problem by testing for a pending interrupt before calling sim_idle(). The system isn't really quiescent if it is just about to handle an interrupt. */ case 0250:case 0251:case 0252:case 0253: case 0254:case 0255:case 0256:case 0257: ion_defer = TRUE; /* defer if JMP,I */ case 0050:case 0051:case 0052:case 0053: case 0054:case 0055:case 0056:case 0057: if (reason = Ea (IR, &MA, intrq)) break; /* JMP */ mp_dms_jmp (MA, 0); /* validate jump addr */ PCQ_ENTRY; PC = MA; /* jump */ /* Idle conditions by operating system: RTE-6/VM: - ISZ / JMP *-1 - mp_fence = 0 - XEQT (address 1717B) = 0 - DMS on with system map enabled - RTE verification: TBG (address 1674B) = CLK select code RTE though RTE-IVB: - JMP * - mp_fence = 0 - XEQT (address 1717B) = 0 - DMS on with user map enabled (RTE-III through RTE-IVB only) - RTE verification: TBG (address 1674B) = CLK select code DOS through DOS-III: - STF 0 / CCA / CCB / JMP *-3 - DOS verification: A = B = -1, address 40B = -64, address 67B = +64 - Note that in DOS, the TBG is set to 100 milliseconds */ if ((sim_idle_enab) && (intrq == 0)) /* idle enabled w/o pending irq? */ if (((PC == err_PC) || /* RTE through RTE-IVB */ ((PC == (err_PC - 1)) && /* RTE-6/VM */ ((ReadW (PC) & I_MRG) == I_ISZ))) && /* RTE jump target */ (mp_fence == CLEAR) && (M [xeqt] == 0) && /* RTE idle indications */ (M [tbg] == clk_dib.devno) || /* RTE verification */ (PC == (err_PC - 3)) && /* DOS through DOS-III */ (ReadW (PC) == I_STF) && /* DOS jump target */ (AR == 0177777) && (BR == 0177777) && /* DOS idle indication */ (M [m64] == 0177700) && /* DOS verification */ (M [p64] == 0000100)) /* DOS verification */ sim_idle (TMR_POLL, FALSE); /* idle the simulator */ break; case 0060:case 0061:case 0062:case 0063: case 0064:case 0065:case 0066:case 0067: case 0260:case 0261:case 0262:case 0263: case 0264:case 0265:case 0266:case 0267: if (reason = Ea (IR, &MA, intrq)) break; /* IOR */ AR = AR | ReadW (MA); break; case 0070:case 0071:case 0072:case 0073: case 0074:case 0075:case 0076:case 0077: case 0270:case 0271:case 0272:case 0273: case 0274:case 0275:case 0276:case 0277: if (reason = Ea (IR, &MA, intrq)) break; /* ISZ */ t = (ReadW (MA) + 1) & DMASK; WriteW (MA, t); if (t == 0) PC = (PC + 1) & VAMASK; break; case 0100:case 0101:case 0102:case 0103: case 0104:case 0105:case 0106:case 0107: case 0300:case 0301:case 0302:case 0303: case 0304:case 0305:case 0306:case 0307: if (reason = Ea (IR, &MA, intrq)) break; /* ADA */ v1 = ReadW (MA); t = AR + v1; if (t > DMASK) E = 1; if (((~AR ^ v1) & (AR ^ t)) & SIGN) O = 1; AR = t & DMASK; break; case 0110:case 0111:case 0112:case 0113: case 0114:case 0115:case 0116:case 0117: case 0310:case 0311:case 0312:case 0313: case 0314:case 0315:case 0316:case 0317: if (reason = Ea (IR, &MA, intrq)) break; /* ADB */ v1 = ReadW (MA); t = BR + v1; if (t > DMASK) E = 1; if (((~BR ^ v1) & (BR ^ t)) & SIGN) O = 1; BR = t & DMASK; break; case 0120:case 0121:case 0122:case 0123: case 0124:case 0125:case 0126:case 0127: case 0320:case 0321:case 0322:case 0323: case 0324:case 0325:case 0326:case 0327: if (reason = Ea (IR, &MA, intrq)) break; /* CPA */ if (AR != ReadW (MA)) PC = (PC + 1) & VAMASK; break; case 0130:case 0131:case 0132:case 0133: case 0134:case 0135:case 0136:case 0137: case 0330:case 0331:case 0332:case 0333: case 0334:case 0335:case 0336:case 0337: if (reason = Ea (IR, &MA, intrq)) break; /* CPB */ if (BR != ReadW (MA)) PC = (PC + 1) & VAMASK; break; case 0140:case 0141:case 0142:case 0143: case 0144:case 0145:case 0146:case 0147: case 0340:case 0341:case 0342:case 0343: case 0344:case 0345:case 0346:case 0347: if (reason = Ea (IR, &MA, intrq)) break; /* LDA */ AR = ReadW (MA); break; case 0150:case 0151:case 0152:case 0153: case 0154:case 0155:case 0156:case 0157: case 0350:case 0351:case 0352:case 0353: case 0354:case 0355:case 0356:case 0357: if (reason = Ea (IR, &MA, intrq)) break; /* LDB */ BR = ReadW (MA); break; case 0160:case 0161:case 0162:case 0163: case 0164:case 0165:case 0166:case 0167: case 0360:case 0361:case 0362:case 0363: case 0364:case 0365:case 0366:case 0367: if (reason = Ea (IR, &MA, intrq)) break; /* STA */ WriteW (MA, AR); break; case 0170:case 0171:case 0172:case 0173: case 0174:case 0175:case 0176:case 0177: case 0370:case 0371:case 0372:case 0373: case 0374:case 0375:case 0376:case 0377: if (reason = Ea (IR, &MA, intrq)) break; /* STB */ WriteW (MA, BR); break; /* Alter/skip instructions */ case 0004:case 0005:case 0006:case 0007: case 0014:case 0015:case 0016:case 0017: skip = 0; /* no skip */ if (IR & 000400) t = 0; /* CLx */ else t = ABREG[absel]; if (IR & 001000) t = t ^ DMASK; /* CMx */ if (IR & 000001) { /* RSS? */ if ((IR & 000040) && (E != 0)) skip = 1; /* SEZ,RSS */ if (IR & 000100) E = 0; /* CLE */ if (IR & 000200) E = E ^ 1; /* CME */ if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */ ((t & 0100001) == 0100001)) skip = 1; if (((IR & 000030) == 000020) && /* SSx,RSS */ ((t & SIGN) != 0)) skip = 1; if (((IR & 000030) == 000010) && /* SLx,RSS */ ((t & 1) != 0)) skip = 1; if (IR & 000004) { /* INx */ t = (t + 1) & DMASK; if (t == 0) E = 1; if (t == SIGN) O = 1; } if ((IR & 000002) && (t != 0)) skip = 1; /* SZx,RSS */ if ((IR & 000072) == 0) skip = 1; /* RSS */ } /* end if RSS */ else { if ((IR & 000040) && (E == 0)) skip = 1; /* SEZ */ if (IR & 000100) E = 0; /* CLE */ if (IR & 000200) E = E ^ 1; /* CME */ if ((IR & 000020) && /* SSx */ ((t & SIGN) == 0)) skip = 1; if ((IR & 000010) && /* SLx */ ((t & 1) == 0)) skip = 1; if (IR & 000004) { /* INx */ t = (t + 1) & DMASK; if (t == 0) E = 1; if (t == SIGN) O = 1; } if ((IR & 000002) && (t == 0)) skip = 1; /* SZx */ } /* end if ~RSS */ ABREG[absel] = t; /* store result */ PC = (PC + skip) & VAMASK; /* add in skip */ break; /* end if alter/skip */ /* Shift instructions */ case 0000:case 0001:case 0002:case 0003: case 0010:case 0011:case 0012:case 0013: t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */ if (IR & 000040) E = 0; /* CLE */ if ((IR & 000010) && ((t & 1) == 0)) /* SLx */ PC = (PC + 1) & VAMASK; ABREG[absel] = shift (t, IR & 00020, IR); /* do second shift */ break; /* end if shift */ /* I/O instructions */ case 0204:case 0205:case 0206:case 0207: case 0214:case 0215:case 0216:case 0217: reason = iogrp (IR, iotrap); /* execute instr */ break; /* end if I/O */ /* Extended arithmetic */ case 0200: /* EAU group 0 */ case 0201: /* divide */ case 0202: /* EAU group 2 */ case 0210: /* DLD */ case 0211: /* DST */ reason = cpu_eau (IR, intrq); /* extended arith */ break; /* Extended instructions */ case 0212: /* UIG 0 extension */ reason = cpu_uig_0 (IR, intrq, iotrap); /* extended opcode */ break; case 0203: /* UIG 1 extension */ case 0213: reason = cpu_uig_1 (IR, intrq, iotrap); /* extended opcode */ break; } /* end case IR */ if (reason == NOTE_IOG) { /* I/O instr exec? */ dmarq = calc_dma (); /* recalc DMA masks */ intrq = calc_int (); /* recalc interrupts */ reason = 0; /* continue */ } else if (reason == NOTE_INDINT) { /* intr pend during indir? */ PC = err_PC; /* back out of inst */ reason = 0; /* continue */ } } /* end while */ /* Simulation halted */ if (iotrap && (reason == STOP_HALT)) MR = intaddr; /* HLT in trap cell? */ else MR = (PC - 1) & VAMASK; /* no, M = P - 1 */ TR = ReadTAB (MR); /* last word fetched */ saved_MR = MR; /* save for T cmd update */ if ((reason == STOP_RSRV) || (reason == STOP_IODV) || /* instr error? */ (reason == STOP_IND)) PC = err_PC; /* back up PC */ dms_upd_sr (); /* update dms_sr */ dms_upd_vr (MR); /* update dms_vr */ if (reason == STOP_HALT) /* programmed halt? */ cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (ignore errors) */ pcq_r->qptr = pcq_p; /* update pc q ptr */ if (dms_enb) /* default breakpoint type */ if (dms_ump) sim_brk_dflt = SWMASK ('U'); /* to current map mode */ else sim_brk_dflt = SWMASK ('S'); else sim_brk_dflt = SWMASK ('N'); return reason; } /* Resolve indirect addresses. An indirect chain is followed until a direct address is obtained. Under simulation, a maximum number of indirect levels are allowed (typically 16), after which the instruction will be aborted. If the memory protect feature is present, an indirect counter is used that allows a pending interrupt to be serviced if more than three levels of indirection are encountered. If MP jumper W6 ("INT") is out and MP is enabled, then pending interrupts are serviced immediately. When employing the indirect counter, the hardware clears a pending interrupt deferral after the third indirection and aborts the instruction after the fourth. */ t_stat resolve (uint32 MA, uint32 *addr, uint32 irq) { uint32 i; t_bool pending = (irq && !(mp_unit.flags & DEV_DIS)); t_bool int_enable = ((mp_unit.flags & UNIT_MP_INT) && mp_control); for (i = 0; (i < ind_max) && (MA & I_IA); i++) { /* resolve multilevel */ if (pending) { /* interrupt pending and MP enabled? */ if ((i == 2) || int_enable) /* 3rd level indirect or INT out? */ ion_defer = FALSE; /* reenable interrrupts */ if ((i > 2) || int_enable) /* 4th or higher or INT out? */ return NOTE_INDINT; /* break out now */ } MA = ReadW (MA & VAMASK); /* follow address chain */ } if (MA & I_IA) return STOP_IND; /* indirect loop? */ *addr = MA; return SCPE_OK; } /* Get effective address from IR */ static t_stat Ea (uint32 IR, uint32 *addr, uint32 irq) { uint32 MA; MA = IR & (I_IA | I_DISP); /* ind + disp */ if (IR & I_CP) MA = ((PC - 1) & I_PAGENO) | MA; /* current page? */ return resolve (MA, addr, irq); /* resolve indirects */ } /* Shift micro operation */ static uint32 shift (uint32 t, uint32 flag, uint32 op) { uint32 oldE; op = op & 07; /* get shift op */ if (flag) { /* enabled? */ switch (op) { /* case on operation */ case 00: /* signed left shift */ return ((t & SIGN) | ((t << 1) & 077777)); case 01: /* signed right shift */ return ((t & SIGN) | (t >> 1)); case 02: /* rotate left */ return (((t << 1) | (t >> 15)) & DMASK); case 03: /* rotate right */ return (((t >> 1) | (t << 15)) & DMASK); case 04: /* left shift, 0 sign */ return ((t << 1) & 077777); case 05: /* ext right rotate */ oldE = E; E = t & 1; return ((t >> 1) | (oldE << 15)); case 06: /* ext left rotate */ oldE = E; E = (t >> 15) & 1; return (((t << 1) | oldE) & DMASK); case 07: /* rotate left four */ return (((t << 4) | (t >> 12)) & DMASK); } /* end case */ } /* end if */ if (op == 05) E = t & 1; /* disabled ext rgt rot */ if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */ return t; /* input unchanged */ } /* I/O instruction decode. If memory protect is enabled, and the instruction is not in a trap cell, then HLT instructions are illegal and will cause a memory protect violation. If jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is out, then only I/O instructions to select code 1 are legal, and I/O to other select codes will cause a violation. If the instruction is allowed, then the I/O signal corresponding to the instruction is determined, the state of the interrupt deferral flag is set. Then the signal is dispatched to the device simulator indicated by the target select code. The return value is split into status and data values, with the latter containing the SKF signal state or data to be returned in the A or B registers. Implementation notes: 1. If the H/C (hold/clear flag) bit is set, then the ioCLF signal is added (not ORed) to the base signal derived from the I/O instruction. 2. ioNONE is dispatched for HLT instructions because although HLT does not assert any backplane signals, the H/C bit may be set. If it is, then the result will be to dispatch ioCLF. 3. Device simulators return either ioSKF or ioNONE in response to an SFC or SFS signal. ioSKF means that the instruction should skip. Because device simulators return the "data" parameter value by default, we initialize that parameter to ioNONE to ensure that a simulator that does not implement SFC or SFS does not skip, which is the correct action for an interface that does not drive the SKF signal. 4. STF/CLF and STC/CLC share sub-opcode values and must be further decoded by the state of instruction register bits 9 and 11, respectively. 5. We return NOTE_IOG for normal status instead of SCPE_OK to request that interrupts be recalculated at the end of the instruction (execution of the I/O group instructions can change the interrupt priority chain). */ t_stat iogrp (uint32 ir, uint32 iotrap) { /* Translation for I/O subopcodes: soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */ static const IOSIG generate_signal [] = { ioNONE, ioSTF, ioSFC, ioSFS, ioIOI, ioIOI, ioIOO, ioSTC }; const uint32 dev = ir & I_DEVMASK; /* device select code */ const uint32 sop = I_GETIOOP (ir); /* I/O subopcode */ const uint32 ab = (ir & I_AB) != 0; /* A/B register select */ const t_bool clf = (ir & I_HC) != 0; /* H/C flag select */ uint32 iodata = (SCPE_OK << IOT_V_REASON) | (uint32) ioNONE; /* initialize for SKF test */ uint32 ioreturn; t_stat iostat; IOSIG iosig; if (!iotrap && mp_control && /* instr not in trap cell and MP on? */ ((sop == soHLT) || /* and is HLT? */ ((dev != OVF) && (mp_unit.flags & UNIT_MP_SEL1)))) { /* or is not SC 01 and SEL1 out? */ if (sop == soLIX) /* MP violation; is LIA/B instruction? */ ABREG [ab] = 0; /* A/B writes anyway */ MP_ABORT (err_PC); /* MP abort */ } iosig = generate_signal [sop]; /* generate I/O signal */ ion_defer = defer_tab [sop]; /* defer depending on instruction */ if (sop == soOTX) /* OTA/B instruction? */ iodata = (SCPE_OK << IOT_V_REASON) | ABREG [ab]; /* pass A/B register value */ else if ((sop == soCTL) && (ir & I_CTL)) /* CLC instruction? */ iosig = ioCLC; /* change STC to CLC signal */ if ((sop == soFLG) && clf) /* CLF instruction? */ iosig = ioCLF; /* change STF to CLF signal */ else if (clf) /* CLF with another instruction? */ iosig = iosig + ioCLF; /* add CLF signal */ ioreturn = devdisp (dev, iosig, iodata); /* dispatch I/O signal */ iostat = (t_stat) (ioreturn >> IOT_V_REASON); /* extract status */ iodata = ioreturn & DMASK; /* extract return data value */ if (((sop == soSFC) || (sop == soSFS)) && /* testing flag state? */ ((IOSIG) iodata == ioSKF)) /* and SKF asserted? */ PC = (PC + 1) & VAMASK; /* bump P to skip next instruction */ else if (sop == soLIX) /* LIA/B instruction? */ ABREG [ab] = iodata; /* load returned data */ else if (sop == soMIX) /* MIA/B instruction? */ ABREG [ab] = ABREG [ab] | iodata; /* merge returned data */ else if (sop == soHLT) { /* HLT instruction? */ const int32 len = strlen (halt_msg); /* find end msg */ sprintf (&halt_msg[len - 6], "%06o", ir); /* add the halt */ return STOP_HALT; } if (iostat == SCPE_OK) /* normal status? */ return NOTE_IOG; /* request interrupt recalc */ else /* abnormal status */ return iostat; /* return it */ } /* Device dispatcher */ static uint32 devdisp (uint32 select_code, IOSIG signal, uint32 data) { return dtab [select_code] (select_code, signal, data); } /* Calculate DMA requests */ static uint32 calc_dma (void) { uint32 r = 0; if (dma_xferen [0] && SRQ (dmac[0].cw1 & I_DEVMASK)) /* check DMA0 cycle */ r = r | DMAR0; if (dma_xferen [1] && SRQ (dmac[1].cw1 & I_DEVMASK)) /* check DMA1 cycle */ r = r | DMAR1; return r; } /* Determine whether a pending interrupt deferral should be inhibited. Execution of certain instructions generally cause a pending interrupt to be deferred until the succeeding instruction completes. However, the interrupt deferral rules differ on the 21xx vs. the 1000. The 1000 always defers until the completion of the instruction following a deferring instruction. The 21xx defers unless the following instruction is an MRG instruction other than JMP or JMP,I or JSB,I. If it is, then the deferral is inhibited, i.e., the pending interrupt will be serviced. See the "Set Phase Logic Flowchart," transition from phase 1A to phase 1B, and the "Theory of Operation," "Control Section Detailed Theory," "Phase Control Logic," "Phase 1B" paragraph in the Model 2100A Computer Installation and Maintenance Manual for details. */ t_bool calc_defer (void) { uint16 IR; if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx series? */ IR = ReadW (PC); /* prefetch next instr */ if (((IR & I_MRG & ~I_AB) != 0000000) && /* is MRG instruction? */ ((IR & I_MRG_I) != I_JSB_I) && /* but not JSB,I? */ ((IR & I_MRG) != I_JMP)) /* and not JMP or JMP,I? */ return FALSE; /* yes, so inhibit deferral */ else return TRUE; /* no, so allow deferral */ } else return TRUE; /* 1000 always allows deferral */ } /* Calculate interrupt requests. The interrupt request (IRQ) of the highest-priority device for which all higher-priority PRL bits are set is granted. That is, there must be an unbroken chain of priority to a device requesting an interrupt for that request to be granted. A device sets its IRQ bit to request an interrupt, and it clears its PRL bit to prevent lower-priority devices from interrupting. IRQ is cleared by an interrupt acknowledge (IAK) signal. PRL generally remains low while a device's interrupt service routine is executing to prevent preemption. IRQ and PRL indicate one of four possible states for a device: IRQ PRL Device state --- --- ---------------------- 0 1 Not interrupting 1 0 Interrupt requested 0 0 Interrupt acknowledged 1 1 (not allowed) Note that PRL must be dropped when requesting an interrupt (IRQ set). This is a hardware requirement of the 1000 series. The IRQ lines from the backplane are not priority encoded. Instead, the PRL chain expresses the priority by allowing only one IRQ line to be active at a time. This allows a simple pull-down encoding of the CIR inputs. The end of priority chain is marked by the highest-priority (lowest-order) bit that is clear. The device corresponding to that bit is the only device that may interrupt (a higher priority device that had IRQ set would also have had PRL set, which is a state violation). We calculate a priority mask by ANDing the complement of the PRL bits with an increment of the PRL bits. Only the lowest-order bit will differ. For example: dev_prl : ...1 1 0 1 1 0 1 1 1 1 1 1 (PRL denied for SC 06 and 11) dev_prl + 1 : ...1 1 0 1 1 1 0 0 0 0 0 0 ~dev_prl : ...0 0 1 0 0 1 0 0 0 0 0 0 ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (break is at SC 06) The interrupt requests are then ANDed with the priority mask to determine if a request is pending: pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source) dev_irq : ...0 0 1 0 0 1 0 0 0 0 0 0 (devices requesting interrupts) ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant) The select code corresponding to the granted request is then returned to the caller. If ION is clear, only power fail (SC 04) and parity error (SC 05) are eligible to interrupt (memory protect shares SC 05, but qualification occurs in the MP abort handler, so if SC 05 is interrupting when ION is clear, it must be a parity error interrupt). */ uint32 calc_int (void) { uint32 sc, pri_mask [2], req_grant [2]; pri_mask [0] = ~dev_prl [0] & (dev_prl [0] + 1); /* calculate lower priority mask */ req_grant [0] = pri_mask [0] & dev_irq [0]; /* calculate lower request to grant */ if (ion) /* interrupt system on? */ if ((req_grant [0] == 0) && (pri_mask [0] == 0)) { /* no requests in lower set and PRL unbroken? */ pri_mask [1] = ~dev_prl [1] & (dev_prl [1] + 1); /* calculate upper priority mask */ req_grant [1] = pri_mask [1] & dev_irq [1]; /* calculate upper request to grant */ } else /* lower set has request */ req_grant [1] = 0; /* no grants to upper set */ else { /* interrupt system off */ req_grant [0] = req_grant [0] & /* only PF and PE can interrupt */ (INT_M (PWR) | INT_M (PRO)); req_grant [1] = 0; } if (req_grant [0]) /* device in lower half? */ for (sc = 0; sc <= 31; sc++) /* determine interrupting select code */ if (req_grant [0] & 1) /* grant this request? */ return sc; /* return this select code */ else /* not this one */ req_grant [0] = req_grant [0] >> 1; /* position next request */ else if (req_grant [1]) /* device in upper half */ for (sc = 32; sc <= 63; sc++) /* determine interrupting select code */ if (req_grant [1] & 1) /* grant this request? */ return sc; /* return this select code */ else /* not this one */ req_grant [1] = req_grant [1] >> 1; /* position next request */ return 0; /* no interrupt granted */ } /* Memory access routines. These routines access memory for reads and writes. They validate the accesses for MP and MEM violations, if enabled. The following routines are provided: - ReadPW : Read a word using a physical address - ReadB : Read a byte using the current map - ReadBA : Read a byte using the alternate map - ReadW : Read a word using the current map - ReadWA : Read a word using the alternate map - ReadIO : Read a word using the specified map without protection - ReadTAB : Read a word using the current map without protection - WritePW : Write a word using a physical address - WriteB : Write a byte using the current map - WriteBA : Write a byte using the alternate map - WriteW : Write a word using the current map - WriteWA : Write a word using the alternate map - WriteIO : Write a word using the specified map without protection The memory protect (MP) and memory expansion module (MEM) accessories provide a protected mode that guards against improper accesses by user programs. They may be enabled or disabled independently, although protection requires that both be enabled. MP checks that memory writes do not fall below the Memory Protect Fence Register (MPFR) value, and MEM checks that read/write protection rules on the target page are compatible with the access desired. If either check fails, and MP is enabled, then the request is aborted. Each mapped routine calls "dms" if DMS is enabled to translate the logical address supplied to a physical address. "dms" performs a protection check and aborts without returning if the check fails. The write routines perform an additional memory-protect check and abort if a violation occurs (so, to pass, a page must be writable AND the target must be above the MP fence). Note that MP uses a lower bound of 2 for memory writes, allowing unrestricted access to the A and B registers (addressed as locations 0 and 1). */ #define MP_TEST(va) (mp_control && ((va) >= 2) && ((va) < mp_fence)) /* Read a word using a physical address */ uint16 ReadPW (uint32 pa) { if (pa <= 1) /* read locations 0 or 1? */ return ABREG[pa]; /* return A/B register */ else /* location >= 2 */ return M[pa]; /* return physical memory value */ } /* Read a byte using the current map */ uint8 ReadB (uint32 va) { int32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va >> 1, dms_ump, RDPROT); /* translate address */ else /* MEM disabled */ pa = va >> 1; /* use logical as physical address */ if (va & 1) /* low byte addressed? */ return (ReadPW (pa) & 0377); /* mask to lower byte */ else /* high byte addressed */ return ((ReadPW (pa) >> 8) & 0377); /* position higher byte and mask */ } /* Read a byte using the alternate map */ uint8 ReadBA (uint32 va) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va >> 1, dms_ump ^ MAP_LNT, RDPROT); /* translate address using alternate map */ else /* MEM disabled */ pa = va >> 1; /* use logical as physical address */ if (va & 1) /* low byte addressed? */ return (ReadPW (pa) & 0377); /* mask to lower byte */ else /* high byte addressed */ return ((ReadPW (pa) >> 8) & 0377); /* position higher byte and mask */ } /* Read a word using the current map */ uint16 ReadW (uint32 va) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, dms_ump, RDPROT); /* translate address */ else /* MEM disabled */ pa = va; /* use logical as physical address */ return ReadPW (pa); /* return word */ } /* Read a word using the alternate map */ uint16 ReadWA (uint32 va) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, dms_ump ^ MAP_LNT, RDPROT); /* translate address using alternate map */ else /* MEM disabled */ pa = va; /* use logical as physical address */ return ReadPW (pa); /* return word */ } /* Read a word using the specified map without protection */ uint16 ReadIO (uint32 va, uint32 map) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, map, NOPROT); /* translate address with no protection */ else /* MEM disabled */ pa = va; /* use logical as physical address */ return M[pa]; /* return word without A/B interception */ } /* Read a word using the current map without protection */ static uint16 ReadTAB (uint32 va) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, dms_ump, NOPROT); /* translate address with no protection */ else /* MEM disabled */ pa = va; /* use logical as physical address */ return ReadPW (pa); /* return word */ } /* Write a word using a physical address */ void WritePW (uint32 pa, uint32 dat) { if (pa <= 1) /* write locations 0 or 1? */ ABREG[pa] = dat & DMASK; /* store A/B register */ else if (pa < fwanxm) /* 2 <= location <= LWA memory? */ M[pa] = dat & DMASK; /* store physical memory value */ return; } /* Write a byte using the current map */ void WriteB (uint32 va, uint32 dat) { uint32 pa, t; if (dms_enb) /* MEM enabled? */ pa = dms (va >> 1, dms_ump, WRPROT); /* translate address */ else /* MEM disabled */ pa = va >> 1; /* use logical as physical address */ if (MP_TEST (va >> 1)) /* MPCK? */ MP_ABORT (va >> 1); /* MP violation */ t = ReadPW (pa); /* get word */ if (va & 1) /* low byte addressed? */ t = (t & 0177400) | (dat & 0377); /* merge in lower byte */ else /* high byte addressed */ t = (t & 0377) | ((dat & 0377) << 8); /* position higher byte and merge */ WritePW (pa, t); /* store word */ return; } /* Write a byte using the alternate map */ void WriteBA (uint32 va, uint32 dat) { uint32 pa, t; if (dms_enb) { /* MEM enabled? */ dms_viol (va >> 1, MVI_WPR); /* always a violation if protected */ pa = dms (va >> 1, dms_ump ^ MAP_LNT, WRPROT); /* translate address using alternate map */ } else /* MEM disabled */ pa = va >> 1; /* use logical as physical address */ if (MP_TEST (va >> 1)) /* MPCK? */ MP_ABORT (va >> 1); /* MP violation */ t = ReadPW (pa); /* get word */ if (va & 1) /* low byte addressed? */ t = (t & 0177400) | (dat & 0377); /* merge in lower byte */ else /* high byte addressed */ t = (t & 0377) | ((dat & 0377) << 8); /* position higher byte and merge */ WritePW (pa, t); /* store word */ return; } /* Write a word using the current map */ void WriteW (uint32 va, uint32 dat) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, dms_ump, WRPROT); /* translate address */ else /* MEM disabled */ pa = va; /* use logical as physical address */ if (MP_TEST (va)) /* MPCK? */ MP_ABORT (va); /* MP violation */ WritePW (pa, dat); /* store word */ return; } /* Write a word using the alternate map */ void WriteWA (uint32 va, uint32 dat) { int32 pa; if (dms_enb) { /* MEM enabled? */ dms_viol (va, MVI_WPR); /* always a violation if protected */ pa = dms (va, dms_ump ^ MAP_LNT, WRPROT); /* translate address using alternate map */ } else /* MEM disabled */ pa = va; /* use logical as physical address */ if (MP_TEST (va)) /* MPCK? */ MP_ABORT (va); /* MP violation */ WritePW (pa, dat); /* store word */ return; } /* Write a word using the specified map without protection */ void WriteIO (uint32 va, uint32 dat, uint32 map) { uint32 pa; if (dms_enb) /* MEM enabled? */ pa = dms (va, map, NOPROT); /* translate address with no protection */ else /* MEM disabled */ pa = va; /* use logical as physical address */ if (pa < fwanxm) M[pa] = dat & DMASK; /* store word without A/B interception */ return; } /* Mapped access check. Returns TRUE if the address will be mapped (presuming MEM is enabled). */ static t_bool is_mapped (uint32 va) { uint32 dms_fence = dms_sr & MST_FENCE; /* get BP fence value */ if (va >= 02000) /* above the base bage? */ return TRUE; /* always mapped */ else return (dms_sr & MST_FLT) ? (va < dms_fence) : /* below BP fence and lower portion mapped? */ (va >= dms_fence); /* or above BP fence and upper portion mapped? */ } /* DMS relocation. This routine translates logical into physical addresses. It must be called only when DMS is enabled, as that condition is not checked. The logical address, desired map, and desired access type are supplied. If the access is legal, the mapped physical address is returned; if it is not, then a MEM violation is indicated. The current map may be specified by passing "dms_ump" as the "map" parameter, or a specific map may be used. Normally, read and write accesses pass RDPROT or WRPROT as the "prot" parameter to request access checking. For DMA accesses, NOPROT must be passed to inhibit access checks. This routine checks for read, write, and base-page violations and will call "dms_viol" as appropriate. The latter routine will abort if MP is enabled, or will return if protection is off. */ static uint32 dms (uint32 va, uint32 map, uint32 prot) { uint32 pgn, mpr; if (va <= 1) /* reference to A/B register? */ return va; /* use address */ if (!is_mapped (va)) { /* unmapped? */ if ((va >= 2) && (prot == WRPROT)) /* base page write access? */ dms_viol (va, MVI_BPG); /* signal a base page violation */ return va; /* use unmapped address */ } pgn = VA_GETPAG (va); /* get page num */ mpr = dms_map[map + pgn]; /* get map reg */ if (mpr & prot) /* desired access disallowed? */ dms_viol (va, prot); /* signal protection violation */ return (MAP_GETPAG (mpr) | VA_GETOFF (va)); /* return mapped address */ } /* DMS relocation for console access. Console access allows the desired map to be specified by switches on the command line. All protection checks are off for console access. This routine is called to restore a saved configuration, and mapping is not used for restoration. */ static uint32 dms_cons (uint32 va, int32 sw) { uint32 map_sel; if ((dms_enb == 0) || /* DMS off? */ (sw & (SWMASK ('N') | SIM_SW_REST))) /* no mapping rqst or save/rest? */ return va; /* use physical address */ else if (sw & SWMASK ('S')) map_sel = SMAP; else if (sw & SWMASK ('U')) map_sel = UMAP; else if (sw & SWMASK ('P')) map_sel = PAMAP; else if (sw & SWMASK ('Q')) map_sel = PBMAP; else map_sel = dms_ump; /* dflt to log addr, cur map */ if (va >= VASIZE) return MEMSIZE; /* virtual, must be 15b */ else if (dms_enb) return dms (va, map_sel, NOPROT); /* DMS on? go thru map */ else return va; /* else return virtual */ } /* Memory protect and DMS validation for jumps. Jumps are a special case of write validation. The target address is treated as a write, even when no physical write takes place, so jumping to a write-protected page causes a MEM violation. In addition, a MEM violation is indicated if the jump is to the unmapped portion of the base page. Finally, jumping to a location under the memory-protect fence causes an MP violation. Because the MP and MEM hardware works in parallel, all three violations may exist concurrently. For example, a JMP to the unmapped portion of the base page that is write protected and under the MP fence will indicate a base-page, write, and MP violation, whereas a JMP to the mapped portion will indicate a write and MP violation (BPV is inhibited by the MEBEN signal). If MEM and MP violations occur concurrently, the MEM violation takes precedence, as the SFS and SFC instructions test the MEV flip-flop. The lower bound of protected memory is passed in the "plb" argument. This must be either 0 or 2. All violations are qualified by the MPCND signal, which responds to the lower bound. Therefore, if the lower bound is 2, and if the part below the base-page fence is unmapped, or if the base page is write-protected, then a MEM violation will occur only if the access is not to locations 0 or 1. The instruction set firmware uses a lower bound of 0 for JMP, JLY, and JPY (and for JSB with W5 out), and of 2 for DJP, SJP, UJP, JRS, and .GOTO (and JSB with W5 in). Finally, all violations are inhibited if MP is off (mp_control is CLEAR), and MEM violations are inhibited if the MEM is disabled. */ void mp_dms_jmp (uint32 va, uint32 plb) { uint32 violation = 0; uint32 pgn = VA_GETPAG (va); /* get page number */ if (mp_control) { /* MP on? */ if (dms_enb) { /* MEM on? */ if (dms_map [dms_ump + pgn] & WRPROT) /* page write protected? */ violation = MVI_WPR; /* write violation occured */ if (!is_mapped (va) && (va >= plb)) /* base page target? */ violation = violation | MVI_BPG; /* base page violation occured */ if (violation) /* any violation? */ dms_viol (va, violation); /* signal MEM violation */ } if ((va >= plb) && (va < mp_fence)) /* jump under fence? */ MP_ABORT (va); /* signal MP violation */ } return; } /* DMS read and write map registers */ uint16 dms_rmap (uint32 mapi) { mapi = mapi & MAP_MASK; return (dms_map[mapi] & ~MAP_RSVD); } void dms_wmap (uint32 mapi, uint32 dat) { mapi = mapi & MAP_MASK; dms_map[mapi] = (uint16) (dat & ~MAP_RSVD); return; } /* Process a MEM violation. A MEM violation will report the cause in the violation register. This occurs even if the MEM is not in the protected mode (i.e., MP is not enabled). If MP is enabled, an MP abort is taken with the MEV flip-flop set. Otherwise, we return to the caller. */ void dms_viol (uint32 va, uint32 st) { dms_vr = st | dms_upd_vr (va); /* set violation cause in register */ if (mp_control) { /* memory protect on? */ mp_mevff = SET; /* record memory expansion violation */ MP_ABORT (va); /* abort */ } return; } /* Update the MEM violation register. In hardware, the MEM violation register (VR) is clocked on every memory read, every memory write above the lower bound of protected memory, and every execution of a privileged DMS instruction. The register is not clocked when MP is disabled by an MP or MEM error (i.e., when MEVFF sets or CTL5FF clears), in order to capture the state of the MEM. In other words, the VR continually tracks the memory map register accessed plus the MEM state (MEBEN, MAPON, and USR) until a violation occurs, and then it's "frozen." Under simulation, we do not have to update the VR on every memory access, because the visible state is only available via a programmed RVA/B instruction or via the SCP interface. Therefore, it is sufficient if the register is updated: - at a MEM violation (when freezing) - at an MP violation (when freezing) - during RVA/B execution (if not frozen) - before returning to SCP after a simulator stop (if not frozen) */ uint32 dms_upd_vr (uint32 va) { if (mp_control && (mp_mevff == CLEAR)) { /* violation register unfrozen? */ dms_vr = VA_GETPAG (va) | /* set map address */ (dms_enb ? MVI_MEM : 0) | /* and MEM enabled */ (dms_ump ? MVI_UMP : 0); /* and user map enabled */ if (is_mapped (va)) /* is addressed mapped? */ dms_vr = dms_vr | MVI_MEB; /* ME bus is enabled */ } return dms_vr; } /* Update the MEM status register */ uint32 dms_upd_sr (void) { dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO); if (dms_enb) dms_sr = dms_sr | MST_ENB; if (dms_ump) dms_sr = dms_sr | MST_UMP; if (mp_control) dms_sr = dms_sr | MST_PRO; return dms_sr; } /* CPU (SC 0) I/O signal handler. I/O instructions for select code 0 manipulate the interrupt system. STF and CLF turn the interrupt system on and off, and SFS and SFC test the state of the interrupt system. When the interrupt system is off, only power fail and parity error interrupts are allowed. Implementation notes: 1. An IOI signal reads the floating I/O bus (0 on all machines). 2. A CLC 0 issues CRS to all devices, not CLC. While most cards react identically to CRS and CLC, some do not, e.g., the 12566B when used as an I/O diagnostic target. 3. RTE uses the undocumented SFS 0,C instruction to both test and turn off the interrupt system. This is confirmed in the "RTE-6/VM Technical Specifications" manual (HP 92084-90015), section 2.3.1 "Process the Interrupt", subsection "A.1 $CIC": "Test to see if the interrupt system is on or off. This is done with the SFS 0,C instruction. In either case, turn it off (the ,C does it)." ...and in section 5.8, "Parity Error Detection": "Because parity error interrupts can occur even when the interrupt system is off, the code at $CIC must be able to save the complete system status. The major hole in being able to save the complete state is in saving the interrupt system state. In order to do this in both the 21MX and the 21XE the instruction 103300 was used to both test the interrupt system and turn it off." 4. Select code 0 cannot interrupt, so there is no SIR handler. */ uint32 cpuio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ uint32 sc; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ ion = CLEAR; /* turn interrupt system off */ break; case ioSTF: /* set flag flip-flop */ ion = SET; /* turn interrupt system on */ break; case ioSFC: /* skip if flag is clear */ setSKF (!ion); /* skip if interrupt system is off */ break; case ioSFS: /* skip if flag is set */ setSKF (ion); /* skip if interupt system is on */ break; case ioIOI: /* I/O input */ data = 0; /* returns 0 */ break; case ioCLC: /* clear control flip-flop */ for (sc = 6; sc <= I_DEVMASK; sc++) /* send CRS to devices */ devdisp (sc, ioCRS, 0); /* from select code 6 and up */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ cpuio (select_code, ioCLF, 0); /* issue CLF */ return data; } /* Overflow/S-register (SC 1) I/O signal handler. Flag instructions directed to select code 1 manipulate the overflow (O) register. Input and output instructions access the switch (S) register. On the 2115 and 2116, there is no S-register indicator, so it is effectively read-only. On the other machines, a front-panel display of the S-register is provided. On all machines, front-panel switches are provided to set the contents of the S register. Implementation notes: 1. Select code 1 cannot interrupt, so there is no SIR handler. */ uint32 ovflio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ O = 0; /* clear overflow */ break; case ioSTF: /* set flag flip-flop */ O = 1; /* set overflow */ break; case ioSFC: /* skip if flag is clear */ setSKF (!O); /* skip if overflow is clear */ break; case ioSFS: /* skip if flag is set */ setSKF (O); /* skip if overflow is set */ break; case ioIOI: /* I/O input */ data = SR; /* read switch register value */ break; case ioIOO: /* I/O output */ if ((UNIT_CPU_MODEL != UNIT_2116) && /* no S register display on */ (UNIT_CPU_MODEL != UNIT_2115)) /* 2116 and 2115 machines */ SR = data; /* write S register value */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ ovflio (select_code, ioCLF, 0); /* issue CLF */ return data; } /* Power fail (SC 4) I/O signal handler. Power fail detection is standard on 2100 and 1000 systems and is optional on 21xx systems. Power fail recovery is standard on the 2100 and optional on the others. Power failure or restoration will cause an interrupt on select code 4. The direction of power change (down or up) can be tested by SFC. We do not implement power fail under simulation. However, the central interrupt register (CIR) is always read by an IOI directed to select code 4. */ uint32 pwrfio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioSTC: /* set control flip-flop */ break; /* reinitializes power fail */ case ioCLC: /* clear control flip-flop */ break; /* reinitializes power fail */ case ioSFC: /* skip if flag is clear */ break; /* skips if power fail occurred */ case ioIOI: /* I/O input */ data = intaddr; /* input CIR value */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ pwrfio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ pwrfio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Memory protect/parity error (SC 5) I/O signal handler. The memory protect card has a number of non-standard features: - CLF and STF affect the parity error enable flip-flop, not the flag - SFC and SFS test the memory expansion violation flip-flop, not the flag - POPIO clears control, flag, and flag buffer instead of setting the flags - CLC does not clear control (the only way to turn off MP is to cause a violation) - PRL and IRQ are a function of the flag only, not flag and control - IAK is used unqualified by IRQ The IAK backplane signal is asserted when any interrupt is acknowledged by the CPU. Normally, an interface qualifies IAK with its own IRQ to ensure that it responds only to an acknowledgement of its own request. The MP card does this to reset its flag buffer and flag flip-flops, and to reset the parity error indication. However, it also responds to an unqualified IAK (i.e., for any interface) as follows: - clears the MPV flip-flop - clears the indirect counter - clears the control flip-flop - sets the INTPT flip-flop The INTPT flip-flop indicates an occurrence of an interrupt. If the trap cell of the interrupting device contains an I/O instruction that is not a HLT, action equivalent to STC 05 is taken, i.e.: - sets the control flip-flop - set the EVR flip-flop - clears the MEV flip-flop - clears the PARERR flip-flop In other words, an interrupt for any device will disable MP unless the trap cell contains an I/O instruction other than a HLT. Implementation notes: 1. Because the card uses IAK unqualified, this routine is called whenever any interrupt occurs. If the MP card itself is not interrupting, the select code passed will not be SC 05. In either case, the trap cell instruction is passed in the "data" parameter. 2. The MEV flip-flop records memory expansion (a.k.a. dynamic mapping) violations. It is set when an DM violation is encountered and can be tested via SFC/SFS. 3. MP cannot be turned off in hardware, except by causing a violation. Microcode typically does this by executing an IOG micro-order with select code /= 1, followed by an IAK to clear the interrupt and a FTCH to clear the INTPT flip-flop. Under simulation, mp_control may be set to CLEAR to produce the same effect. 4. Parity error logic is not implemented. */ uint32 protio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ break; /* turns off PE interrupt */ case ioSTF: /* set flag flip-flop */ break; /* turns on PE interrupt */ case ioENF: /* enable flag */ mp_flag = mp_flagbuf = SET; /* set flag buffer and flag flip-flops */ mp_evrff = CLEAR; /* inhibit violation register updates */ break; case ioSFC: /* skip if flag is clear */ setSKF (!mp_mevff); /* skip if MP interrupt */ break; case ioSFS: /* skip if flag is set */ setSKF (mp_mevff); /* skip if DMS interrupt */ break; case ioIOI: /* I/O input */ data = mp_viol; /* read MP violation register */ break; case ioIOO: /* I/O output */ mp_fence = data & VAMASK; /* write to MP fence register */ if (cpu_unit.flags & UNIT_2100) /* 2100 IOP uses MP fence */ iop_sp = mp_fence; /* as a stack pointer */ break; case ioPOPIO: /* power-on preset to I/O */ mp_control = CLEAR; /* clear control flip-flop */ mp_flag = mp_flagbuf = CLEAR; /* clear flag and flag buffer flip-flops */ mp_mevff = CLEAR; /* clear memory expansion violation flip-flop */ mp_evrff = SET; /* set enable violation register flip-flop */ break; case ioSTC: /* set control flip-flop */ mp_control = SET; /* turn on MP */ mp_mevff = CLEAR; /* clear memory expansion violation flip-flop */ mp_evrff = SET; /* set enable violation register flip-flop */ break; case ioSIR: /* set interrupt request */ setPRL (PRO, !mp_flag); /* set PRL signal */ setIRQ (PRO, mp_flag); /* set IRQ signal */ break; case ioIAK: /* interrupt acknowledge */ if (select_code == PRO) /* MP interrupt acknowledgement? */ mp_flag = mp_flagbuf = CLEAR; /* clear flag and flag buffer */ if (((data & I_NMRMASK) != I_IO) || /* trap cell instruction not I/O */ (I_GETIOOP (data) == soHLT)) /* or is halt? */ mp_control = CLEAR; /* turn protection off */ else { /* non-HLT I/O instruction leaves MP on */ mp_mevff = CLEAR; /* but clears MEV flip-flop */ mp_evrff = SET; /* and reenables violation register flip-flop */ } break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ protio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ protio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* DMA/DCPC secondary (SC 2/3) I/O signal handler. DMA consists of one (12607B) or two (12578A/12895A/12897B) channels. Each channel uses two select codes: 2 and 6 for channel 1, and 3 and 7 for channel 2. The lower select codes are used to configure the memory address register (control word 2) and the word count register (control word 3). The upper select codes are used to configure the service select register (control word 1) and to activate and terminate the transfer. There are differences in the implementations of the memory address and word count registers among the various cards. The 12607B (2114) supports 14-bit addresses and 13-bit word counts. The 12578A (2115/6) supports 15-bit addresses and 14-bit word counts. The 12895A (2100) and 12897B (1000) support 15-bit addresses and 16-bit word counts. Implementation notes: 1. Because the I/O bus floats to zero on 211x computers, an IOI (read word count) returns zeros in the unused bit locations, even though the word count is a negative value. 2. Select codes 2 and 3 cannot interrupt, so there is no SIR handler. */ uint32 dmasio (uint32 select_code, IOSIG signal, uint32 data) { const uint32 ch = select_code & 1; /* DMA channel number */ const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioIOI: /* I/O data input */ if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */ data = dmac [ch].cw3 & 0017777; /* only 13-bit count */ else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2115/2116? */ data = dmac [ch].cw3 & 0037777; /* only 14-bit count */ else /* other models */ data = dmac [ch].cw3; /* rest use full value */ break; /* read remaining word count */ case ioIOO: /* I/O data output */ if (dma_select [ch]) /* word count selected? */ dmac [ch].cw3 = data; /* save count */ else /* memory address selected */ if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */ dmac [ch].cw2 = data & 0137777; /* only 14-bit address */ else /* other models */ dmac [ch].cw2 = data; /* full address stored */ break; case ioCLC: /* clear control flip-flop */ dma_select [ch] = CLEAR; /* set for word count access */ break; case ioSTC: /* set control flip-flop */ dma_select [ch] = SET; /* set for memory address access */ break; default: /* all other signals */ break; /* are ignored */ } return data; } /* DMA/DCPC primary (SC 6/7) I/O signal handler. The primary DMA control interface and the service select register are manipulated through select codes 6 and 7. Each channel has transfer enable, control, flag, and flag buffer flip-flops. Transfer enable must be set via STC to start DMA. Control is used only to enable the DMA completion interrupt; it is set by STC and cleared by CLC. Flag and flag buffer are set at transfer completion to signal an interrupt. STF may be issued to abort a transfer in progress. Again, there are hardware differences between the various DMA cards. The 12607B (2114) stores only bits 2-0 of the select code and interprets them as select codes 10-16 (SRQ17 is not decoded). The 12578A (2115/16), 12895A (2100), and 12897B (1000) support the full range of select codes (10-77 octal). Implementation notes: 1. An IOI reads the floating S-bus (high on the 1000, low on the 21xx). 2. The CRS signal on the DMA card resets the secondary (SC 2/3) select flip-flops. Under simulation, ioCRS is dispatched to select codes 6 and up, so we reset the flip-flop in our handler. 3. The 12578A card does not start the transfer until one instruction after the STC 6/7 has executed. The diagnostic tests for this, so we implement a startup latency counter to provide the proper delay. 4. The 12578A supports byte-sized transfers by setting bit 14. Bit 14 is ignored by all other DMA cards, which support word transfers only. Under simulation, we use a byte-packing/unpacking register to hold one byte while the other is read or written during the DMA cycle. */ uint32 dmapio (uint32 select_code, IOSIG signal, uint32 data) { const uint32 ch = select_code & 1; /* DMA channel number */ const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ dma_flag [ch] = dma_flagbuf [ch] = CLEAR; /* clear flag and flag buffer */ break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ dma_flag [ch] = dma_flagbuf [ch] = SET; /* set flag and flag buffer */ dma_xferen [ch] = CLEAR; /* clear transfer enable to abort transfer */ break; case ioSFC: /* skip if flag is clear */ setSKF (!dma_flag [ch]); /* skip if transfer in progress */ break; case ioSFS: /* skip if flag is set */ setSKF (dma_flag [ch]); /* skip if transfer is complete */ break; case ioIOI: /* I/O data input */ if (UNIT_CPU_TYPE == UNIT_TYPE_1000) /* 1000? */ data = DMASK; /* return all ones */ else /* other models */ data = 0; /* return all zeros */ break; case ioIOO: /* I/O data output */ if (UNIT_CPU_MODEL == UNIT_2114) /* 12607? */ dmac [ch].cw1 = (data & 0137707) | 010; /* mask SC, convert to 10-17 */ else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 12578? */ dmac [ch].cw1 = data; /* store full select code, flags */ else /* 12895, 12897 */ dmac [ch].cw1 = data & ~DMA1_PB; /* clip byte-packing flag */ break; case ioPOPIO: /* power-on preset to I/O */ dma_flag [ch] = dma_flagbuf [ch] = SET; /* set flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ dma_xferen [ch] = CLEAR; /* clear transfer enable */ dma_select [ch] = CLEAR; /* set secondary for word count access */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ dma_control [ch] = CLEAR; /* clear control */ break; case ioSTC: /* set control flip-flop */ if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* slow DMA card? */ dmac [ch].latency = 1; /* needs startup latency */ else dmac [ch].latency = 0; /* DCPC starts immediately */ dmac [ch].packer = 0; /* clear packing register */ dma_xferen [ch] = dma_control [ch] = SET; /* set transfer enable and control */ break; case ioSIR: /* set interrupt request */ setPRL (select_code, !(dma_control [ch] & dma_flag [ch])); setIRQ (select_code, dma_control [ch] & dma_flag [ch] & dma_flagbuf [ch]); break; case ioIAK: /* interrupt acknowledge */ dma_flagbuf [ch] = CLEAR; /* clear flag buffer */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dmapio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dmapio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unassigned select code I/O signal handler. The 21xx/1000 I/O structure requires that no empty slots exist between interface cards. This is due to the hardware priority chaining (PRH/PRL). If it is necessary to leave unused I/O slots, HP 12777A Priority Jumper Cards must be installed in them to maintain priority continuity. Under simulation, every unassigned I/O slot behaves as though a 12777A were resident. Implementation notes: 1. For select codes < 10 octal, an IOI reads the floating S-bus (high on the 1000, low on the 21xx). For select codes >= 10 octal, an IOI reads the floating I/O bus (low on all machines). 2. If "stop_dev" is TRUE, then the simulator will stop when an unassigned device is accessed. */ uint32 nullio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioIOI: /* I/O data input */ if ((select_code < VARDEV) && /* internal device */ (UNIT_CPU_TYPE == UNIT_TYPE_1000)) /* and 1000? */ data = DMASK; /* return all ones */ else /* external or other model */ data = 0; /* return all zeros */ break; default: /* all other signals */ break; /* are ignored */ } return (stop_dev << IOT_V_REASON) | data; /* flag missing device */ } /* DMA cycle routine The 12578A card supports byte-packing. If bit 14 in control word 1 is set, each transfer will involve one read/write from memory and two output/input operations in order to transfer sequential bytes to/from the device. The last cycle (word count reaches 0) logic is quite tricky. Input cases: - CLC requested: issue CLC Output cases: - neither STC nor CLC requested: issue CLF - STC requested but not CLC: issue STC,C - CLC requested but not STC: issue CLC,C - STC and CLC both requested: issue STC,C and CLC,C, in that order Either case: issue EDT Implementation notes: 1. The EDT signal is sent to the device signal handler with the "data" parameter set to ioIOI or ioIOO to indicate the completion of an input or output transfer, respectively. The IPL device is the only one that uses this (at the moment). */ static void dma_cycle (uint32 ch, uint32 map) { uint32 temp, dev; int32 MA; int32 inp = dmac[ch].cw2 & DMA2_OI; /* input flag */ int32 byt = dmac[ch].cw1 & DMA1_PB; /* pack bytes flag */ if (dmac[ch].latency) { /* start-up latency? */ dmac[ch].latency = dmac[ch].latency - 1; /* decrease it */ return; /* that's all this cycle */ } dev = dmac[ch].cw1 & I_DEVMASK; /* get device */ MA = dmac[ch].cw2 & VAMASK; /* get mem addr */ if (inp) { /* input cycle? */ temp = devdisp (dev, ioIOI, 0); /* do I/O input */ if (byt) { /* byte packing? */ if (dmac[ch].packer & DMA_OE) { /* second byte? */ temp = (dmac[ch].packer << 8) | /* merge stored byte */ (temp & DMASK8); WriteIO (MA, temp, map); /* store word data */ } else /* first byte */ dmac[ch].packer = (temp & DMASK8); /* save it */ dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */ } else /* no byte packing */ WriteIO (MA, temp, map); /* store word data */ } else { /* output cycle */ if (byt) { /* byte packing? */ if (dmac[ch].packer & DMA_OE) /* second byte? */ temp = dmac[ch].packer & DMASK8; /* retrieve it */ else { /* first byte */ dmac[ch].packer = ReadIO (MA, map); /* read word data */ temp = (dmac[ch].packer >> 8) & DMASK8; /* get high byte */ } dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */ } else /* no byte packing */ temp = ReadIO (MA, map); /* read word data */ devdisp (dev, ioIOO, temp); /* do I/O output */ } if ((dmac[ch].packer & DMA_OE) == 0) { /* new byte or no packing? */ dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | /* increment address */ ((dmac[ch].cw2 + 1) & VAMASK); dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* increment word count */ } if (dmac[ch].cw3) { /* more to do? */ if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */ devdisp (dev, ioSTC + ioCLF, 0); /* do STC,C dev */ else devdisp (dev, ioCLF, 0); /* else CLF dev */ } else { if (inp) { /* last cycle, input? */ if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */ devdisp (dev, ioCLC, 0); /* yes */ } /* end input */ else { /* output */ if ((dmac[ch].cw1 & (DMA1_STC | DMA1_CLC)) == 0) devdisp (dev, ioCLF, 0); /* clear flag */ if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */ devdisp (dev, ioSTC + ioCLF, 0); /* do STC,C dev */ if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */ devdisp (dev, ioCLC + ioCLF, 0); /* yes */ } /* end output */ dmapio (DMA0 + ch, ioENF, 0); /* set DMA channel flag */ devdisp (dev, ioEDT, (uint32) (inp ? ioIOI : ioIOO)); /* send EDT to device */ } return; } /* Reset routines. The reset routines are called to simulate either an initial power on condition or a front-panel PRESET button press. For initial power on (corresponds to PON signal assertion in the CPU), the "P" command switch will be set. For PRESET (corresponds to POPIO and CRS assertion), the switch will be clear. SCP delivers a power-on reset to all devices when the simulator is started. A RUN, BOOT, RESET, or RESET ALL command delivers a PRESET to all devices. A RESET delivers a PRESET to a specific device. */ /* CPU reset. If this is the first call after simulator startup, allocate the initial memory array, set the default CPU model, and install the default BBL. A PON reset initializes certain CPU registers. The 1000 series does a microcoded memory clear and leaves the T and P registers set as a result. Front-panel PRESET performs additional initialization. We also handle MEM preset here. Because PRESET is dispatched to every device separately, each of which will handle POPIO and CRS in response, we do not need to dispatch POPIO ourselves. */ t_stat cpu_reset (DEVICE *dptr) { if (M == NULL) { /* initial call after startup? */ pcq_r = find_reg ("PCQ", NULL, dptr); /* get PC queue pointer */ if (pcq_r) /* defined? */ pcq_r->qptr = 0; /* initialize queue */ else /* not defined */ return SCPE_IERR; /* internal error */ M = calloc (PASIZE, sizeof (uint16)); /* alloc mem */ if (M == NULL) /* alloc fail? */ return SCPE_MEM; else { /* do one-time init */ MEMSIZE = 32768; /* set initial memory size */ cpu_set_model (NULL, UNIT_2116, NULL, NULL); /* set initial CPU model */ SR = 001000; /* select PTR boot ROM at SC 10 */ cpu_boot (0, NULL); /* install loader for 2116 */ cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (was enabled) */ SR = 0; /* clear S */ sim_vm_post = &hp_post_cmd; /* set cmd post proc */ sim_brk_types = ALL_BKPTS; /* register allowed breakpoint types */ } } if (sim_switches & SWMASK ('P')) { /* PON reset? */ AR = 0; /* clear A register */ BR = 0; /* clear B register */ SR = 0; /* clear S register */ TR = 0; /* clear T register */ E = 1; /* set E register */ if (UNIT_CPU_FAMILY == UNIT_FAMILY_1000) { /* 1000 series? */ memset (M, 0, MEMSIZE * 2); /* zero allocated memory */ MR = 0077777; /* set M register */ PC = 0100000; /* set P register */ } else { /* 21xx series */ MR = 0; /* clear M register */ PC = 0; /* clear P register */ } } O = 0; /* PRESET: clear O register */ ion = CLEAR; /* PRESET: turn off interrupt system */ ion_defer = FALSE; /* PRESET: clear interrupt deferral */ dms_enb = 0; /* POPIO: turn DMS off */ dms_ump = 0; /* POPIO: init to system map */ dms_sr = 0; /* POPIO: clear status register and BP fence */ dms_vr = 0; /* POPIO: clear violation register */ sim_brk_dflt = SWMASK ('N'); /* type is nomap as DMS is off */ return SCPE_OK; } /* Memory protect reset */ t_stat mp_reset (DEVICE *dptr) { protio (PRO, ioPOPIO, 0); /* send POPIO signal */ mp_fence = 0; /* clear fence register */ mp_viol = 0; /* clear violation register */ return SCPE_OK; } /* DMA channel 1 reset */ t_stat dma0_reset (DEVICE *tptr) { if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */ hp_enbdis_pair (&dma0_dev, &dma1_dev); /* make pair cons */ if (sim_switches & SWMASK ('P')) /* PON reset? */ dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0; /* clear control word registers */ dmapio (DMA0, ioPOPIO, 0); /* send POPIO signal */ dmac[0].latency = dmac[0].packer = 0; /* clear latency and byte packer */ return SCPE_OK; } /* DMA channel 2 reset */ t_stat dma1_reset (DEVICE *tptr) { if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */ hp_enbdis_pair (&dma1_dev, &dma0_dev); /* make pair cons */ if (sim_switches & SWMASK ('P')) /* PON reset? */ dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0; /* clear control word registers */ dmapio (DMA1, ioPOPIO, 0); /* send POPIO signal */ dmac[1].latency = dmac[1].packer = 0; /* clear latency and byte packer */ return SCPE_OK; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { uint16 d; if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */ return SCPE_NOFNC; /* command not allowed */ addr = dms_cons (addr, sw); /* translate address as indicated */ if (addr >= MEMSIZE) /* beyond memory limits? */ return SCPE_NXM; /* non-existent memory */ if ((sw & SIM_SW_REST) || (addr >= 2)) /* restoring or memory access? */ d = M[addr]; /* return memory value */ else /* not restoring and A/B access */ d = ABREG[addr]; /* return A/B register value */ if (vptr != NULL) *vptr = d & DMASK; /* store return value */ return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */ return SCPE_NOFNC; /* command not allowed */ addr = dms_cons (addr, sw); /* translate address as indicated */ if (addr >= MEMSIZE) /* beyond memory limits? */ return SCPE_NXM; /* non-existent memory */ if ((sw & SIM_SW_REST) || (addr >= 2)) /* restoring or memory access? */ M[addr] = val & DMASK; /* store memory value */ else /* not restoring and A/B access */ ABREG[addr] = val & DMASK; /* store A/B register value */ return SCPE_OK; } /* Make a pair of devices consistent */ void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp) { if (ccp->flags & DEV_DIS) dcp->flags = dcp->flags | DEV_DIS; else dcp->flags = dcp->flags & ~DEV_DIS; return; } /* VM command post-processor Update T register to contents of memory addressed by M register if M register has changed. */ void hp_post_cmd (t_bool from_scp) { if (MR != saved_MR) { /* M changed since last update? */ saved_MR = MR; TR = ReadTAB (MR); /* sync T with new M */ } return; } /* Test for device conflict */ static t_bool dev_conflict (void) { DEVICE *dptr; DIB *dibp; uint32 i, j, k; t_bool is_conflict = FALSE; uint32 conflicts[I_DEVMASK + 1] = { 0 }; for (i = 0; dptr = sim_devices[i]; i++) { dibp = (DIB *) dptr->ctxt; if (dibp && !(dptr->flags & DEV_DIS)) if (++conflicts[dibp->devno] > 1) is_conflict = TRUE; } if (is_conflict) { sim_ttcmd(); for (i = 0; i <= I_DEVMASK; i++) { if (conflicts[i] > 1) { k = conflicts[i]; printf ("Select code %o conflict:", i); if (sim_log) fprintf (sim_log, "Select code %o conflict:", i); for (j = 0; dptr = sim_devices[j]; j++) { dibp = (DIB *) dptr->ctxt; if (dibp && !(dptr->flags & DEV_DIS) && (i == dibp->devno)) { if (k < conflicts[i]) { printf (" and"); if (sim_log) fputs (" and", sim_log); } printf (" %s", sim_dname (dptr)); if (sim_log) fprintf (sim_log, " %s", sim_dname (dptr)); k = k - 1; if (k == 0) { putchar ('\n'); if (sim_log) fputc ('\n', sim_log); break; } } } } } } return is_conflict; } /* Change CPU memory size. On a 21xx, move the current loader to the top of the new memory size. Then clear "non-existent memory" so that reads return zero, per spec. Validation: - New size <= maximum size for current CPU. - New size a positive multiple of 4K (progamming error if not). - If new size < old size, truncation accepted. */ t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc) { int32 mc = 0; uint32 i; uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ uint32 old_size = MEMSIZE; /* current memory size */ if ((uint32) new_size > cpu_features[model].maxmem) return SCPE_NOFNC; /* mem size unsupported */ if ((new_size <= 0) || (new_size > PASIZE) || ((new_size & 07777) != 0)) return SCPE_NXM; /* invalid size (prog err) */ if (!(sim_switches & SWMASK ('F'))) { /* force truncation? */ for (i = new_size; i < MEMSIZE; i++) mc = mc | M[i]; if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) return SCPE_INCOMP; } if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx CPU? */ cpu_set_ldr (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */ MEMSIZE = new_size; /* set new memory size */ fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */ } else /* loader unsupported */ fwanxm = MEMSIZE = new_size; /* set new memory size */ for (i = fwanxm; i < old_size; i++) M[i] = 0; /* zero non-existent memory */ return SCPE_OK; } /* Change CPU models. For convenience, MP and DMA are typically enabled if available; they may be disabled subsequently if desired. Note that the 2114 supports only one DMA channel (channel 0). All other models support two channels. Validation: - Sets standard equipment and convenience features. - Changes DMA device name to DCPC if 1000 is selected. - Enforces maximum memory allowed (doesn't change otherwise). - Disables loader on 21xx machines. */ t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc) { uint32 old_family = UNIT_CPU_FAMILY; /* current CPU type */ uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */ uint32 new_index = new_model >> UNIT_V_CPU; /* new CPU model index */ uint32 new_memsize; t_stat result; cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS | /* set typical features */ cpu_features[new_index].typ & UNIT_OPTS; /* mask pseudo-opts */ if (cpu_features[new_index].typ & UNIT_MP) /* MP in typ config? */ mp_dev.flags = mp_dev.flags & ~DEV_DIS; /* enable it */ else mp_dev.flags = mp_dev.flags | DEV_DIS; /* disable it */ if (cpu_features[new_index].opt & UNIT_MP) /* MP an option? */ mp_dev.flags = mp_dev.flags | DEV_DISABLE; /* make it alterable */ else mp_dev.flags = mp_dev.flags & ~DEV_DISABLE; /* make it unalterable */ if (cpu_features[new_index].typ & UNIT_DMA) { /* DMA in typ config? */ dma0_dev.flags = dma0_dev.flags & ~DEV_DIS; /* enable DMA channel 0 */ if (new_model == UNIT_2114) /* 2114 has only one channel */ dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */ else /* all others have two channels */ dma1_dev.flags = dma1_dev.flags & ~DEV_DIS; /* enable it */ } else { dma0_dev.flags = dma0_dev.flags | DEV_DIS; /* disable channel 0 */ dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */ } if (cpu_features[new_index].opt & UNIT_DMA) { /* DMA an option? */ dma0_dev.flags = dma0_dev.flags | DEV_DISABLE; /* make it alterable */ if (new_model == UNIT_2114) /* 2114 has only one channel */ dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */ else /* all others have two channels */ dma1_dev.flags = dma1_dev.flags | DEV_DISABLE; /* make it alterable */ } else { dma0_dev.flags = dma0_dev.flags & ~DEV_DISABLE; /* make it unalterable */ dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */ } if ((old_family == UNIT_FAMILY_1000) && /* if current family is 1000 */ (new_family == UNIT_FAMILY_21XX)) { /* and new family is 21xx */ deassign_device (&dma0_dev); /* delete DCPC names */ deassign_device (&dma1_dev); } else if ((old_family == UNIT_FAMILY_21XX) && /* if current family is 21xx */ (new_family == UNIT_FAMILY_1000)) { /* and new family is 1000 */ assign_device (&dma0_dev, "DCPC0"); /* change DMA device name */ assign_device (&dma1_dev, "DCPC1"); /* to DCPC for familiarity */ } if ((MEMSIZE == 0) || /* current mem size not set? */ (MEMSIZE > cpu_features[new_index].maxmem)) /* current mem size too large? */ new_memsize = cpu_features[new_index].maxmem; /* set it to max supported */ else new_memsize = MEMSIZE; /* or leave it unchanged */ result = cpu_set_size (uptr, new_memsize, NULL, NULL); /* set memory size */ if (result == SCPE_OK) /* memory change OK? */ if (new_family == UNIT_FAMILY_21XX) /* 21xx CPU? */ fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */ else fwanxm = MEMSIZE; /* loader reserved only for 21xx */ return result; } /* Display the CPU model and optional loader status. Loader status is displayed for 21xx models and suppressed for 1000 models. */ t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) { fputs ((char *) desc, st); /* write model name */ if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* valid only for 21xx */ if (fwanxm < MEMSIZE) /* loader area non-existent? */ fputs (", loader disabled", st); /* yes, so access disabled */ else fputs (", loader enabled", st); /* no, so access enabled */ return SCPE_OK; } /* Set a CPU option. Validation: - Checks that the current CPU model supports the option selected. - If CPU is 1000-F, ensures that VIS and IOP are mutually exclusive. - If CPU is 2100, ensures that FP/FFP and IOP are mutually exclusive. - If CPU is 2100, ensures that FP is enabled if FFP enabled (FP is required for FFP installation). */ t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc) { uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ if ((cpu_features[model].opt & option) == 0) /* option supported? */ return SCPE_NOFNC; /* no */ if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { if ((option == UNIT_FP) || (option == UNIT_FFP)) /* 2100 IOP and FP/FFP options */ uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */ else if (option == UNIT_IOP) uptr->flags = uptr->flags & ~(UNIT_FP | UNIT_FFP); if (option == UNIT_FFP) /* 2100 FFP option requires FP */ uptr->flags = uptr->flags | UNIT_FP; } else if (UNIT_CPU_MODEL == UNIT_1000_F) if (option == UNIT_VIS) /* 1000-F IOP and VIS options */ uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */ else if (option == UNIT_IOP) uptr->flags = uptr->flags & ~UNIT_VIS; return SCPE_OK; } /* Clear a CPU option. Validation: - Checks that the current CPU model supports the option selected. - Clears flag from unit structure (we are processing MTAB_XTD entries). - If CPU is 2100, ensures that FFP is disabled if FP disabled (FP is required for FFP installation). */ t_bool cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc) { uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ if ((cpu_features[model].opt & option) == 0) /* option supported? */ return SCPE_NOFNC; /* no */ uptr->flags = uptr->flags & ~option; /* disable option */ if ((UNIT_CPU_TYPE == UNIT_TYPE_2100) && /* disabling 2100 FP? */ (option == UNIT_FP)) uptr->flags = uptr->flags & ~UNIT_FFP; /* yes, so disable FFP too */ return SCPE_OK; } /* 21xx loader enable/disable function. The 21xx CPUs store their initial binary loaders in the last 64 words of available memory. This memory is protected by a LOADER ENABLE switch on the front panel. When the switch is off (disabled), main memory effectively ends 64 locations earlier, i.e., the loader area is treated as non-existent. Because these are core machines, the loader is retained when system power is off. 1000 CPUs do not have a protected loader feature. Instead, loaders are stored in PROMs and are copied into main memory for execution by the IBL switch. Under simulation, we keep both a total configured memory size (MEMSIZE) and a current configured memory size (fwanxm = "first word address of non-existent memory"). When the two are equal, the loader is enabled. When the current size is less than the total size, the loader is disabled. Disabling the loader copies the last 64 words to a shadow array, zeros the corresponding memory, and decreases the last word of addressable memory by 64. Enabling the loader reverses this process. Disabling may be done manually by user command or automatically when a halt instruction is executed. Enabling occurs only by user command. This differs slightly from actual machine operation, which additionally disables the loader when a manual halt is performed. We do not do this to allow breakpoints within and single-stepping through the loaders. */ t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc) { static BOOT_ROM loader; int32 i; t_bool is_enabled = (fwanxm == MEMSIZE); if ((UNIT_CPU_FAMILY != UNIT_FAMILY_21XX) || /* valid only for 21xx */ (MEMSIZE == 0)) /* and for initialized memory */ return SCPE_NOFNC; if (is_enabled && (enable == 0)) { /* disable loader? */ fwanxm = MEMSIZE - IBL_LNT; /* decrease available memory */ for (i = 0; i < IBL_LNT; i++) { /* copy loader */ loader[i] = M[fwanxm + i]; /* from memory */ M[fwanxm + i] = 0; /* and zero location */ } } else if ((!is_enabled) && (enable == 1)) { /* enable loader? */ for (i = 0; i < IBL_LNT; i++) /* copy loader */ M[fwanxm + i] = loader[i]; /* to memory */ fwanxm = MEMSIZE; /* increase available memory */ } return SCPE_OK; } /* Idle set/clear. Idling must have a calibrated clock before sleeping, or the TBG rate will be wrong. When we handle a SET CPU IDLE, we want to... ...do something to allow the clock to stabilize before actually enabling. Probably set sim_idle_enab immediately, so idling is reported as on, but don't actually call sim_idle() until clock stabilizes. Maybe "cpu_idle" = 0 and then = 1 after 100 clocks? */ t_stat cpu_set_idle (UNIT *uptr, int32 option, char *cptr, void *desc) { if (option) return sim_set_idle (uptr, 10, NULL, NULL); else return sim_clr_idle (uptr, 0, NULL, NULL); } /* Idle display */ t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) { return sim_show_idle (st, uptr, val, desc); } /* IBL routine (CPU boot) */ t_stat cpu_boot (int32 unitno, DEVICE *dptr) { extern const BOOT_ROM ptr_rom, dq_rom, ms_rom, ds_rom; int32 dev = (SR >> IBL_V_DEV) & I_DEVMASK; int32 sel = (SR >> IBL_V_SEL) & IBL_M_SEL; if (dev < 010) return SCPE_NOFNC; switch (sel) { case 0: /* PTR boot */ ibl_copy (ptr_rom, dev); break; case 1: /* DP/DQ boot */ ibl_copy (dq_rom, dev); break; case 2: /* MS boot */ ibl_copy (ms_rom, dev); break; case 3: /* DS boot */ ibl_copy (ds_rom,dev); break; } return SCPE_OK; } /* IBL boot ROM copy - Use memory size to set the initial PC and base of the boot area - Copy boot ROM to memory, updating I/O instructions - Place 2's complement of boot base in last location Notes: - SR settings are done by the caller - Boot ROM's must be assembled with a device code of 10 (10 and 11 for devices requiring two codes) */ t_stat ibl_copy (const BOOT_ROM rom, int32 dev) { int32 i; uint16 wd; cpu_set_ldr (NULL, TRUE, NULL, NULL); /* enable loader (ignore errors) */ if (dev < 010) return SCPE_ARG; /* valid device? */ PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */ for (i = 0; i < IBL_LNT; i++) { /* copy bootstrap */ wd = rom[i]; /* get word */ if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */ ((wd & I_DEVMASK) >= 010) && /* dev >= 10? */ (I_GETIOOP (wd) != soHLT)) /* not a HALT? */ M[PC + i] = (wd + (dev - 010)) & DMASK; /* change dev code */ else M[PC + i] = wd; /* leave unchanged */ } M[PC + IBL_DPC] = (M[PC + IBL_DPC] + (dev - 010)) & DMASK; /* patch DMA ctrl */ M[PC + IBL_END] = (~PC + 1) & DMASK; /* fill in start of boot */ return SCPE_OK; } simh-3.8.1/HP2100/hp2100_cpu5.c0000644000175000017500000017537011062235702013500 0ustar vlmvlm/* hp2100_cpu5.c: HP 1000 RTE-6/VM VMA and RTE-IV EMA instructions Copyright (c) 2007-2008, Holger Veit Copyright (c) 2006-2008, J. David Bryan 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 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. CPU5 RTE-6/VM and RTE-IV firmware option instructions 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 30-Jul-08 JDB Redefined ABORT to pass address, moved def to hp2100_cpu.h 26-Jun-08 JDB Rewrote device I/O to model backplane signals 01-May-08 HV Fixed mapping bug in "cpu_ema_emap" 21-Apr-08 JDB Added EMA support from Holger 25-Nov-07 JDB Added TF fix from Holger 07-Nov-07 HV VMACK diagnostic tests 1...32 passed 19-Oct-07 JDB Corrected $LOC operand profile to OP_CCCACC 03-Oct-07 HV Moved RTE-6/VM instrs from hp2100_cpu0.c 26-Sep-06 JDB Created Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" /* RTE-6/VM Virtual Memory Area Instructions RTE-6/VM (product number 92084A) introduced Virtual Memory Area (VMA) instructions -- a superset of the RTE-IV EMA instructions. Different microcode was supplied with the operating system that replaced the microcode used with RTE-IV. Microcode was limited to the E/F-Series, and the M-Series used software equivalents. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A 92084A 92084A The routines are mapped to instruction codes as follows: Instr. 1000-E/F Description ------ -------- ---------------------------------------------- .PMAP 105240 Map VMA page into map register $LOC 105241 Load on call [test] 105242 [self test] .SWP 105243 [Swap A and B registers] .STAS 105244 [STA B; LDA SP] .LDAS 105245 [LDA SP] .MYAD 105246 [NOP in microcode] .UMPY 105247 [Unsigned multiply and add] .IMAP 105250 Integer element resolve address and map .IMAR 105251 Integer element resolve address .JMAP 105252 Double integer element resolve address and map .JMAR 105253 Double integer element resolve address .LPXR 105254 Map pointer in P+1 plus offset in P+2 .LPX 105255 Map pointer in A/B plus offset in P+1 .LBPR 105256 Map pointer in P+1 .LBP 105257 Map pointer in A/B registers Implementation notes: 1. The opcodes 105243-247 are undocumented and do not appear to be used in any HP software. 2. The opcode list in the CE Handbook incorrectly shows 105246 as ".MYAD - multiply 2 signed integers." The microcode listing shows that this instruction was deleted, and the opcode is now a NOP. 3. RTE-IV EMA and RTE-6 VMA instructions shared the same address space, so a given machine could run one or the other, but not both. Additional references: - RTE-6/VM VMA/EMA Microcode Source (92084-18828, revision 3). - RTE-6/VM Technical Specifications (92084-90015, Apr-1983). - M/E/F-Series Computer Systems CE Handbook (5950-3767, Jul-1984). */ static const OP_PAT op_vma[16] = { OP_N, OP_CCCACC, OP_N, OP_N, /* .PMAP $LOC [test] .SWAP */ OP_N, OP_N, OP_N, OP_K, /* .STAS .LDAS .MYAD .UMPY */ OP_A, OP_A, OP_A, OP_A, /* .IMAP .IMAR .JMAP .JMAR */ OP_AA, OP_A, OP_A, OP_N /* .LPXR .LPX .LBPR .LBP */ }; /* some addresses in page0 of RTE-6/VM */ static const uint32 idx = 0001645; static const uint32 xmata = 0001646; static const uint32 xi = 0001647; static const uint32 xeqt = 0001717; static const uint32 vswp = 0001776; static const uint32 umaps = 0003740; static const uint32 page30 = 0074000; static const uint32 page31 = 0076000; static const uint32 ptemiss = 0176000; /* frequent constants in paging */ #define SUITMASK 0176000 #define NILPAGE 0176000 #define PAGEIDX 0001777 #define MSEGMASK 0076000 #define RWPROT 0141777 /* microcode version of resolve(): allows a much higher # of indirection levels. Used for instance for LBP microcode diagnostics which will check > 100 levels. */ #define VMA_INDMAX 200 static t_stat vma_resolve (uint32 MA, uint32 *addr, t_bool debug) { uint32 i; uint32 faultma = MA; for (i = 0; (i < VMA_INDMAX) && (MA & I_IA); i++) { /* resolve multilevel */ MA = ReadW (MA & VAMASK); /* follow address chain */ } if (MA & I_IA) { if (debug) fprintf(sim_deb,">>CPU VMA: vma_resolve indirect loop addr=%06o\n",faultma); return STOP_IND; /* indirect loop */ } *addr = MA; return SCPE_OK; } /* $LOC ASSEMBLER CALLING SEQUENCE: $MTHK NOP RETURN ADDRESS OF CALL (REDONE AFTER THIS ROUTINE) JSB $LOC .DTAB OCT LGPG# LOGICAL PAGE # AT WHICH THE NODE TO * BE MAPPED IN BELONGS (0-31) OCT RELPG RELATIVE PAGE OFFSET FROM BEGINING * OF PARTITION OF WHERE THAT NODE RESIDES. * (0 - 1023) OCT RELBP RELATIVE PAGE OFFSET FROM BEGINING OF * PARTITION OF WHERE BASE PAGE RESIDES * (0 - 1023) CNODE DEF .CNOD THIS IS THE ADDRESS OF CURRENT PATH # WORD .ORD OCT XXXXX THIS NODE'S LEAF # (IE PATH #) .NOD# OCT XXXXX THIS NODE'S ORDINAL # */ static t_stat cpu_vma_loc(OPS op,uint32 intrq,t_bool debug) { uint32 eqt,mls,pnod,lstpg,fstpg,rotsz,lgpg,relpg,relbp,matloc,ptnpg,physpg,cnt,pgs,umapr; eqt = ReadIO(xeqt,UMAP); /* get ID segment */ mls = ReadIO(eqt+33,SMAP); /* get word33 of alternate map */ if ((mls & 0x8000) == 0) { /* this is not an MLS prog! */ PC = err_PC; if (debug) fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: not an MLS program\n", PC); if (mp_control) MP_ABORT (eqt+33); /* allow an MP abort */ return STOP_HALT; /* FATAL error! */ } pnod = mls & 01777; /* get #pages of mem res nodes */ if (pnod == 0) { /* no pages? FATAL! */ PC = err_PC; if (debug) fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: no mem resident pages\n", PC); if (mp_control) MP_ABORT (eqt+33); /* allow an MP abort */ return STOP_HALT; } lstpg = (ReadIO(eqt+29,SMAP) >> 10) - 1; /* last page# of code */ fstpg = ReadIO(eqt+23,SMAP) >> 10; /* index to 1st addr + mem nodes */ rotsz = fstpg - (ReadIO(eqt+22,SMAP) >> 10); /* #pages in root */ lgpg = op[0].word; /* lets do some consistency checks, CPU halt if they fail */ if (lstpg < lgpg || lgpg < fstpg) { /* assert LSTPG >= LGPG# >= FSTPG */ PC = err_PC; if (debug) fprintf(sim_deb, ">>CPU VMA: $LOC at P=%06o: failed check LSTPG >= LGPG# >= FSTPG\n",PC); if (mp_control) MP_ABORT (eqt+22); /* allow an MP abort */ return STOP_HALT; } relpg = op[1].word; if (pnod < relpg || relpg < (rotsz+1)) { /* assert #PNOD >= RELPG >= ROTSZ+1 */ PC = err_PC; if (debug) fprintf(sim_deb, ">>CPU VMA: $LOC at %06o: failed check #PNOD >= RELPG >= ROTSZ+1\n",PC); if (mp_control) MP_ABORT (eqt+22); /* allow an MP abort */ return STOP_HALT; } relbp = op[2].word; if (relbp != 0) /* assert RELBP == 0 OR */ if (pnod < relbp || relbp < (rotsz+1)) { /* #PNOD >= RELBP >= ROTSZ+1 */ PC = err_PC; if (debug) fprintf(sim_deb, ">>CPU VMA: $LOC at P=%06o: failed check: #PNOD >= RELBP >= ROTSZ+1\n",PC); if (mp_control) MP_ABORT (eqt+22); /* allow an MP abort */ return STOP_HALT; } cnt = lstpg - lgpg + 1; /* #pages to map */ pgs = pnod - relpg + 1; /* #pages from start node to end of code */ if (pgs < cnt) cnt = pgs; /* ensure minimum, so not to map into EMA */ matloc = ReadIO(xmata,UMAP); /* get MAT $LOC address */ ptnpg = ReadIO(matloc+3,SMAP) & 01777; /* index to start phys pg */ physpg = ptnpg + relpg; /* phys pg # of node */ umapr = 32 + lgpg; /* map register to start */ /* do an XMS with AR=umapr,BR=physpg,XR=cnt */ if (debug) fprintf(sim_deb, ">>CPU VMA: $LOC map %d pgs from phys=%06o to mapr=%d\n", cnt,physpg,umapr); while (cnt != 0) { dms_wmap (umapr, physpg); /* map pages of new overlay segment */ cnt = (cnt - 1) & DMASK; umapr = (umapr + 1) & DMASK; physpg = (physpg + 1) & DMASK; } dms_wmap(32,relbp+ptnpg); /* map base page again */ WriteW(op[3].word,op[4].word); /* path# we are going to */ PC = (PC - 8) & DMASK; /* adjust PC to return address */ /* word before the $LOC microinstr. */ PC = (ReadW(PC) - 1) & DMASK; /* but the call has to be rerun, */ /* so must skip back to the original call */ /* which will now lead to the real routine */ if (debug) fprintf(sim_deb,">>CPU VMA: $LOC done: path#=%06o, P=%06o\n",op[4].word,PC); return SCPE_OK; } /* map pte into last page return FALSE if page fault, nil flag in PTE or suit mismatch return TRUE if suit match, physpg = physical page or page=0 -> last+1 page */ static t_bool cpu_vma_ptevl(uint32 pagid,uint32* physpg) { uint32 suit; uint32 pteidx = pagid & 0001777; /* build index */ uint32 reqst = pagid & SUITMASK; /* required suit */ uint32 pteval = ReadW(page31 | pteidx); /* get PTE entry */ *physpg = pteval & 0001777; /* store physical page number */ suit = pteval & SUITMASK; /* suit number seen */ if (pteval == NILPAGE) return FALSE; /* NIL value in PTE */ return suit == reqst || !*physpg; /* good page or last+1 */ } /* handle page fault */ static t_stat cpu_vma_fault(uint32 x,uint32 y,int32 mapr, uint32 ptepg,uint32 ptr,uint32 faultpc, t_bool debug) { uint32 pre = ReadIO(xi,UMAP); /* get program preamble */ uint32 ema = ReadIO(pre+2,UMAP); /* get address of $EMA$/$VMA$ */ WriteIO(ema,faultpc,UMAP); /* write addr of fault instr */ XR = x; /* X = faulting page */ YR = y; /* Y = faulting address for page */ if (mapr>0) dms_wmap(mapr+UMAP,ptepg); /* map PTE into specified user dmsmap */ /* do a safety check: first instr of $EMA$/$VMA$ must be a DST instr */ if (ReadIO(ema+1,UMAP) != 0104400) { if (debug) fprintf(sim_deb, ">>CPU VMA: pg fault: no EMA/VMA user code present\n"); if (mp_control) MP_ABORT (ema+1); /* allow an MP abort */ return STOP_HALT; /* FATAL: no EMA/VMA! */ } PC = (ema+1) & VAMASK; /* restart $EMA$ user code, */ /* will return to fault instruction */ AR = (ptr >> 16) & DMASK; /* restore A, B */ BR = ptr & DMASK; E = 0; /* enforce E = 0 */ if (debug) fprintf(sim_deb, ">>CPU VMA: Call pg fault OS exit, AR=%06o BR=%06o P=%06o\n", AR, BR, PC); return SCPE_OK; } /* map in PTE into last page, return false, if page fault */ static t_bool cpu_vma_mapte(uint32* ptepg) { uint32 idext,idext2; uint32 dispatch = ReadIO(vswp,UMAP) & 01777; /* get fresh dispatch flag */ t_bool swapflag = TRUE; if (dispatch == 0) { /* not yet set */ idext = ReadIO(idx,UMAP); /* go into IDsegment extent */ if (idext != 0) { /* is ema/vma program? */ dispatch = ReadWA(idext+1) & 01777; /* get 1st ema page: new vswp */ WriteIO(vswp,dispatch,UMAP); /* move into $VSWP */ idext2 = ReadWA(idext+2); /* get swap bit */ swapflag = (idext2 & 020000) != 0; /* bit 13 = swap bit */ } } if (dispatch) { /* some page is defined */ dms_wmap(31 + UMAP,dispatch); /* map $VSWP to register 31 */ *ptepg = dispatch; /* return PTEPG# for later */ } return swapflag; /* true for swap bit set */ } /* .LBP ASSEMBLER CALLING SEQUENCE: DLD PONTR TRANSLATE 32 BIT POINTER TO 15 JSB .LBP BIT POINTER. 32 bit pointer: ----------AR------------ -----BR----- 15 14....10 9....4 3...0 15.10 9....0 L<----------------------------------- L=1 local reference bit XXXXXXXX<------------------------- 5 bit unused PPPPPP PPPPP PPPPP<------ 16 bit PAGEID SSSSSS<------------------ SUIT# within PAGEID PPPPP PPPPP<------ 10 bit PAGEID index into PTE OOOOOO 10 bit OFFSET */ static t_stat cpu_vma_lbp(uint32 ptr,uint32 aoffset,uint32 faultpc,uint32 intrq,t_bool debug) { uint32 pagid,offset,ptrl,pgidx,ptepg; uint16 p30,p31,suit; t_stat reason = SCPE_OK; uint32 faultab = ptr; /* remember A,B for page fault */ ptr += aoffset; /* add the offset e.g. for .LPX */ if (debug) fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: ptr=%o/%o\n", (ptr>>16) & DMASK,ptr & DMASK); O = 0; /* clear overflow */ if (ptr & 0x80000000) { /* is it a local reference? */ ptrl = ptr & VAMASK; if ((ptr&I_IA) && (reason = vma_resolve (ReadW (ptrl), &ptrl, debug))) return reason; /* yes, resolve indirect ref */ BR = ptrl & VAMASK; /* address is local */ AR = (ptr >> 16) & DMASK; if (debug) fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: local ref AR=%06o BR=%06o\n",AR,BR); return SCPE_OK; } pagid = (ptr >> 10) & DMASK; /* extract page id (16 bit idx, incl suit*/ offset = ptr & 01777; /* and offset */ suit = pagid & SUITMASK; /* suit of page */ pgidx = pagid & PAGEIDX; /* index into PTE */ if (!cpu_vma_mapte(&ptepg)) /* map in PTE */ return cpu_vma_fault(65535,ptemiss,-1,ptepg,faultab,faultpc, debug); /* oops, must init PTE */ /* ok, we have the PTE mapped to page31 */ /* the microcode tries to reads two consecutive data pages into page30 and page31 */ /* read the 1st page value from PTE */ p30 = ReadW(page31 | pgidx) ^ suit; if (!p30) /* matched suit for 1st page */ return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); /* suit switch situation: 1st page is in last idx of PTE, then following page * must be in idx 0 of PTE */ if (pgidx==01777) { /* suit switch situation */ pgidx = 0; /* select correct idx 0 */ suit = pagid+1; /* suit needs increment */ if (suit==0) { /* is it page 65536? */ offset += 02000; /* adjust to 2nd page */ suit = NILPAGE; pgidx = 01777; } } else pgidx++; /* select next page */ p31 = ReadW(page31 | pgidx) ^ suit; if (!p31) { /* matched suit for 2nd page */ dms_wmap(31+UMAP,p30); if (p30 & SUITMASK) return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); if (!(p31 ^ NILPAGE)) /* suit is 63: fault */ return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug); offset += 02000; /* adjust offset to last user map because */ /* the address requested page 76xxx */ } else { dms_wmap(30+UMAP,p30); if (p30 & SUITMASK) return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); dms_wmap(31+UMAP,p31); if (p31 & SUITMASK) return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug); } AR = pagid; /* return pagid in A */ BR = page30+offset; /* mapped address in B */ if (debug) fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: map done AR=%06o BR=%o6o\n",AR,BR); return SCPE_OK; } /* .PMAP ASSEMBLER CALLING SEQUENCE: LDA UMAPR (MSEG - 31) LDB PAGID (0-65535) JSB .PMAP GO MAP IT IN A-REG = REASON, NOTE 1 > SEE NOTE 2> NOTE 1 : IF BIT 15 OF A-REG SET, THEN ALL NORMAL BRANCHES TO THE $EMA$/$VMA$ CODE WILL BE CHANGED TO P+1 EXIT. THE A-REG WILL BE THE REASON THE MAPPING WAS NOT SUCCESSFUL IF BIT 15 OF THE A-REG WAS NOT SET. THIS WAS DONE SO THAT A ROUTINE ($VMA$) CAN DO A MAPPING WITHOUT THE POSSIBILITY OF BEING RE-CURRED. IT IS USED BY $VMA$ AND PSTVM IN THE PRIVLEDGED MODE. NOTE 2: E-REG WILL = 1 IF THE LAST+1 PAGE IS REQUESTED AND MAPPED READ/WRITE PROTECTED ON A GOOD P+2 RETURN. */ static t_stat cpu_vma_pmap(uint32 umapr,uint32 pagid, t_bool debug) { uint32 physpg, ptr, pgpte; uint32 mapnm = umapr & 0x7fff; /* strip off bit 15 */ if (debug) fprintf(sim_deb, ">>CPU VMA: .PMAP AR=%06o(umapr) BR=%06o(pagid)\n",umapr,pagid); if (mapnm > 31) { /* check for invalid map register */ AR = 80; /* error: corrupt EMA/VMA system */ if (debug) fprintf(sim_deb, ">>CPU VMA: .PMAP invalid mapr: AR=80, exit P+1\n"); return SCPE_OK; /* return exit PC+1 */ } ptr = (umapr << 16) | (pagid & DMASK); /* build the ptr argument for vma_fault */ if (!cpu_vma_mapte(&pgpte)) { /* map the PTE */ if (umapr & 0x8000) { XR = 65535; YR = ptemiss; if (debug) fprintf(sim_deb, ">>CPU VMA: .PMAP pg fault&bit15: XR=%06o YR=%06o, exit P+1\n", XR, YR); return SCPE_OK; /* use PC+1 error exit */ } return cpu_vma_fault(65535,ptemiss,-1,pgpte,ptr,PC-1,debug); /* oops: fix PTE */ } /* PTE is successfully mapped to page31 and dmsmap[63] */ if (!cpu_vma_ptevl(pagid,&physpg)) { if (umapr & 0x8000) { XR = pagid; YR = page31; if (debug) fprintf(sim_deb, ">>CPU VMA: .PMAP pg map&bit15: XR=%06o YR=%06o, exit P+1\n", XR, YR); return SCPE_OK; /* use PC+1 error exit*/ } return cpu_vma_fault(pagid,page31,31,pgpte,ptr,PC-1,debug); /* page not present */ } E = 1; if (physpg == 0) /* last+1 page ? */ physpg = RWPROT; /* yes, use page 1023 RW/Protected */ else E = 0; /* normal page to map */ dms_wmap(mapnm+UMAP,physpg); /* map page to user page reg */ if (mapnm != 31) /* unless already unmapped, */ dms_wmap(31+UMAP,RWPROT); /* unmap PTE */ AR = (umapr + 1) & DMASK; /* increment mapr for next call */ BR = (pagid + 1) & DMASK; /* increment pagid for next call */ O = 0; /* clear overflow */ PC = (PC + 1) & VAMASK; /* normal PC+2 return */ if (debug) fprintf(sim_deb,">>CPU VMA: .PMAP map done: AR=%06o BR=%o6o exit P+2\n",AR,BR); return SCPE_OK; } /* array calc helper for .imar, .jmar, .imap, .jmap ij=in_s: 16 bit descriptors ij=in_d: 32 bit descriptors This helper expects mainly the following arguments: dtbl: pointer to an array descriptor table atbl: pointer to the table of actual subscripts where subscript table is the following: atbl-> DEF last_subscript,I (point to single or double integer) ... DEF first subscript,I (point to single or double integer) where Descriptor_table is the following table: dtbl-> DEC #dimensions DEC/DIN next-to-last dimension (single or double integer) ... DEC/DIN first dimension (single or double integer) DEC elementsize in words DEC high,low offset from start of EMA to element(0,0...0) Note that subscripts are counting from 0 */ static t_stat cpu_vma_ijmar(OPSIZE ij,uint32 dtbl,uint32 atbl,uint32* dimret, uint32 intrq,t_bool debug) { t_stat reason = SCPE_OK; uint32 ndim,MA,i,ws; int32 accu,ax,dx; OP din; int opsz = ij==in_d ? 2 : 1; ndim = ReadW(dtbl++); /* get #dimensions itself */ if (debug) { fprintf(sim_deb, ">>CPU VMA array calc #dim=%d, size=%d\n",ndim,opsz); fprintf(sim_deb, ">>CPU VMA: array actual subscripts ("); for (i=0; i0) fputc(',',sim_deb); fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word)); } fprintf(sim_deb,")\n>>CPU VMA: array descriptor table ("); if (ndim) { for (i=0; i0) fputc(',',sim_deb); fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word)); } i = dtbl+1+(ndim-1)*opsz; ws = ReadW(i-1); } else { i = dtbl; ws = 1; } fprintf(sim_deb,")\n>>CPU VMA: array elemsz=%d base=%o/%o\n", ws,ReadW(i),ReadW(i+1)); } if (dimret) *dimret = ndim; /* return dimensions */ if (ndim == 0) { /* no dimensions: */ AR = ReadW(dtbl++); /* return the array base itself */ BR = ReadW(dtbl); if (debug) fprintf(sim_deb,">>CPU VMA: #dim=0, AR=%06o, BR=%06o\n",AR,BR); return SCPE_OK; } /* calculate * (...(An*Dn-1)+An-1)*Dn-2)+An-2....)+A2)*D1)+A1)*#words + Array base * Depending on ij, Ax and Dx can be 16 or 32 bit */ accu = 0; while (ndim-- > 0) { MA = ReadW(atbl++); /* get addr of subscript */ if ((reason = resolve (MA, &MA, intrq))) /* and resolve it */ return reason; din = ReadOp(MA,ij); /* get actual subscript value */ ax = ij==in_d ? INT32(din.dword) : INT16(din.word); accu += ax; /* add to accu */ if (ndim==0) ij = in_s; /* #words is single */ din = ReadOp(dtbl,ij); /* get dimension from descriptor table */ if (ij==in_d) { dx = INT32(din.dword); /* either get double or single dimension */ dtbl += 2; } else { dx = INT16(din.word); dtbl++; } accu *= dx; /* multiply */ } din = ReadOp(dtbl,in_d); /* add base address */ accu += din.dword; AR = (accu >> 16) & DMASK; /* transfer to AB */ BR = accu & DMASK; if (debug) fprintf(sim_deb,">>CPU VMA: resulting virt addr=%o (AR=%06o, BR=%06o)\n",accu,AR,BR); return reason; } /* * This is the main handler for the RTE6/VMA microcodes */ t_stat cpu_rte_vma (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; OP_PAT pattern; uint32 entry,t32,ndim; uint32 dtbl,atbl; /* descriptor table ptr, actual args ptr */ OP dop0,dop1; uint32 pcsave = (PC+1) & VAMASK; /* save PC to check for redo in imap/jmap */ t_bool debug = DEBUG_PRI (cpu_dev, DEB_VMA); entry = IR & 017; /* mask to entry point */ pattern = op_vma[entry]; /* get operand pattern */ if (pattern != OP_N) if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ return reason; if (debug) { /* debugging? */ fprintf (sim_deb, ">>CPU VMA: IR = %06o (", IR); /* print preamble and IR */ fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ NULL, SWMASK('M')); fprintf (sim_deb, "), P = %06o, XEQT = %06o", /* print location and program ID */ err_PC, ReadW (xeqt)); fprint_ops (pattern, op); /* print operands */ fputc ('\n', sim_deb); /* terminate line */ } switch (entry) { /* decode IR<3:0> */ case 000: /* .PMAP 105240 (OP_N) */ reason = cpu_vma_pmap(AR,BR,debug); /* map pages */ break; case 001: /* $LOC 105241 (OP_CCCACC) */ reason = cpu_vma_loc(op,intrq,debug); /* handle the coroutine switch */ break; case 002: /* [test] 105242 (OP_N) */ XR = 3; /* refer to src code 92084-18828 rev 3 */ SR = 0102077; /* HLT 77 instruction */ YR = 1; /* ROMs correctly installed */ PC = (PC+1) & VAMASK; /* skip instr if VMA/EMA ROM installed */ break; case 003: /* [swap] 105243 (OP_N) */ t32 = AR; /* swap A and B registers */ AR = BR; BR = t32; break; case 004: /* [---] 105244 (OP_N) */ reason = stop_inst; /* fragment of dead code */ break; /* in microrom */ case 005: /* [---] 105245 (OP_N) */ reason = stop_inst; /* fragment of dead code */ break; /* in microrom */ case 006: /* [nop] 105246 (OP_N) */ break; /* do nothing */ case 007: /* [umpy] 105247 (OP_K) */ t32 = AR * op[0].word; /* get multiplier */ t32 += BR; /* add B */ AR = (t32 >> 16) & DMASK; /* move result back to AB */ BR = t32 & DMASK; O = 0; /* instr clears OV */ break; case 010: /* .IMAP 105250 (OP_A) */ dtbl = op[0].word; atbl = PC; if ((reason = cpu_vma_ijmar(in_s,dtbl,atbl,&ndim,intrq,debug))) /* calc the virt address to AB */ return reason; t32 = (AR << 16) | (BR & DMASK); if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug))) return reason; if (PC==pcsave) PC = (PC+ndim) & VAMASK; /* adjust PC: skip ndim subscript words */ break; case 011: /* .IMAR 105251 (OP_A) */ dtbl = ReadW(op[0].word); atbl = (op[0].word+1) & VAMASK; reason = cpu_vma_ijmar(in_s,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */ break; case 012: /* .JMAP 105252 (OP_A) */ dtbl = op[0].word; atbl = PC; if ((reason = cpu_vma_ijmar(in_d,dtbl,atbl,&ndim,intrq,debug))) /* calc the virtual address to AB */ return reason; t32 = (AR << 16) | (BR & DMASK); if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug))) return reason; if (PC==pcsave) PC = (PC + ndim) & VAMASK; /* adjust PC: skip ndim subscript dword ptr */ break; case 013: /* .JMAR 105253 (OP_A) */ dtbl = ReadW(op[0].word); atbl = (op[0].word+1) & VAMASK; reason = cpu_vma_ijmar(in_d,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */ break; case 014: /* .LPXR 105254 (OP_AA) */ dop0 = ReadOp(op[0].word,in_d); /* get pointer from arg */ dop1 = ReadOp(op[1].word,in_d); t32 = dop0.dword + dop1.dword; /* add offset to it */ reason = cpu_vma_lbp(t32,0,PC-3,intrq,debug); break; case 015: /* .LPX 105255 (OP_A) */ t32 = (AR << 16) | (BR & DMASK); /* pointer in AB */ dop0 = ReadOp(op[0].word,in_d); reason = cpu_vma_lbp(t32,dop0.dword,PC-2,intrq,debug); break; case 016: /* .LBPR 105256 (OP_A) */ dop0 = ReadOp(op[0].word,in_d); /* get the pointer */ reason = cpu_vma_lbp(dop0.dword,0,PC-2,intrq,debug); break; case 017: /* .LBP 105257 (OP_N) */ t32 = (AR << 16) | (BR & DMASK); reason = cpu_vma_lbp(t32,0,PC-1,intrq,debug); break; } return reason; } /* RTE-IV Extended Memory Area Instructions The RTE-IV operating system (HP product number 92067A) introduced the Extended Memory Area (EMA) instructions. EMA provided a mappable data area up to one megaword in size. These three instructions accelerated data accesses to variables stored in EMA partitions. Support was limited to E/F-Series machines; M-Series machines used software equivalents. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A 92067A 92067A The routines are mapped to instruction codes as follows: Instr. 1000-E/F Description ------ -------- ---------------------------------------------- .EMIO 105240 EMA I/O MMAP 105241 Map physical to logical memory [test] 105242 [self test] .EMAP 105257 Resolve array element address Notes: 1. RTE-IV EMA and RTE-6 VMA instructions share the same address space, so a given machine can run one or the other, but not both. Additional references: - RTE-IVB Programmer's Reference Manual (92068-90004, Dec-1983). - RTE-IVB Technical Specifications (92068-90013, Jan-1980). */ static const OP_PAT op_ema[16] = { OP_AKA, OP_AKK, OP_N, OP_N, /* .EMIO MMAP [test] --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_AAA /* --- --- --- .EMAP */ }; /* calculate the 32 bit EMA subscript for an array */ static t_bool cpu_ema_resolve(uint32 dtbl,uint32 atbl,uint32* sum) { int32 sub, act, low, sz; uint32 MA, base; int32 ndim = ReadW(dtbl++); /* # dimensions */ if (ndim < 0) return FALSE; /* invalid? */ *sum = 0; /* accu for index calc */ while (ndim > 0) { MA = ReadW(atbl++); /* get address of A(N) */ resolve (MA, &MA, 0); act = ReadW(MA); /* A(N) */ low = ReadW(dtbl++); /* -L(N) */ sub = SEXT(act) + SEXT(low); /* subscript */ if (sub & 0xffff8000) return FALSE; /* overflow? */ *sum += sub; /* accumulate */ sz = ReadW(dtbl++); sz = SEXT(sz); if (sz < 0) return FALSE; *sum *= sz; if (*sum > (512*1024)) return FALSE; /* overflow? */ ndim--; } base = (ReadW(dtbl+1)<<16) | (ReadW(dtbl) & 0xffff); /* base of array in EMA */ if (base & 0x8000000) return FALSE; *sum += base; /* calculate address into EMA */ if (*sum & 0xf8000000) return FALSE; /* overflow? */ return TRUE; } /* implementation of VIS RTE-IVB EMA support * .ERES microcode routine, resolves only EMA addresses * Call: * .OCT 101474B * DEF RTN error return (rtn), good return is rtn+1 * DEF DUMMY dummy argument for compatibility with .EMAP * DEF TABLE[,I] array declaration (dtbl) * DEF A(N)[,I] actual subscripts (atbl) * DEF A(N-1)[,I] * ... * DEF A(2)[,I] * DEF A(1)[,I] * RTN EQU * error return A="20", B="EM" * RTN+1 EQU *+1 good return B=logical address * * TABLE DEC # # dimensions * DEC -L(N) * DEC D(N-1) * DEC -L(N-1) lower bound (n-1)st dim * DEC D(N-2) (n-2)st dim * ... * DEC D(1) 1st dim * DEC -L(1) lower bound 1st dim * DEC # # words/element * OFFSET 1 EMA Low * OFFSET 2 EMA High */ t_stat cpu_ema_eres(uint32 *rtn,uint32 dtbl,uint32 atbl,t_bool debug) { uint32 sum; if (cpu_ema_resolve(dtbl,atbl,&sum)) { /* calculate subscript */ AR = sum & 0xffff; BR = sum >> 16; if (!(BR & SIGN)) { /* no overflow? */ (*rtn)++; /* return via good exit */ return SCPE_OK; } } AR = 0x3230; /* error condition: */ BR = 0x454d; /* AR = '20', BR = 'EM' */ return SCPE_OK; /* return via unmodified rtn */ } /* implementation of VIS RTE-IVB EMA support * .ESEG microcode routine * Call: * LDA FIRST first map to set * LDB N # of maps to set * .OCT 101475B/105475B * DEF RTN ptr to return * DEF TABLE map table * RTN EQU * error return A="21", B="EM" * RTN+1 EQU *+1 good return B=logical address * * load maps FIRST to FIRST+N from TABLE, with FIRST = FIRST + LOG_START MSEG * update map table in base page. Set LOG_START MSEG=0 if opcode==105475 */ t_stat cpu_ema_eseg(uint32* rtn, uint32 IR, uint32 tbl, t_bool debug) { uint32 xidex,eqt,idext0,idext1; uint32 msegsz,phys,msegn,last,emasz,pg0,pg1,pg,i,lp; if ((BR & SIGN) || BR==0) goto em21; /* #maps not positive? */ xidex = ReadIO(idx,UMAP); /* read ID extension */ if (xidex==0) goto em21; idext0 = ReadWA(xidex+0); /* get 1st word idext */ msegsz = idext0 & 037; /* S7 MSEG size */ WriteIO(xidex+0, idext0 | 0100000, SMAP); /* enforce nonstd MSEG */ idext1 = ReadWA(xidex+1); /* get 2nd word idext */ phys = idext1 & 01777; /* S5 phys start of EMA */ msegn = (idext1 >> 11) & 037; /* S9 get logical start MSEG# */ if (IR & 04000) { /* opcode == 105475? (.VPRG) */ msegn = 0; /* log start = 0 */ msegsz = 32; /* size = full range */ } last = AR-1 + BR; /* last page */ if (last > msegsz) goto em21; /* too many? error */ eqt = ReadIO(xeqt,UMAP); emasz = (ReadWA(eqt+28) & 01777) - 1; /* S6 EMA size in pages */ /* locations 1740...1777 of user base page contain the map entries we need. * They are normally hidden by BP fence, therefore they have to be accessed by * another fence-less map register. uCode uses #1 temporarily */ pg0 = dms_rmap(UMAP+0); /* read map #0 */ pg1 = dms_rmap(UMAP+1); /* save map #1 */ dms_wmap(UMAP+1,pg0); /* copy #0 into reg #1 */ lp = AR + msegn; /* first */ for (i=0; i emasz) pg |= 0140000; /* write protect if outside */ pg += phys; /* adjust into EMA page range */ WriteIO(umaps+lp+i, pg, UMAP); /* copy pg to user map */ /* printf("MAP val %oB to reg %d (addr=%oB)\n",pg,lp+i,umaps+lp+i); */ dms_wmap(UMAP+lp+i, pg); /* set DMS reg */ } dms_wmap(UMAP+1,pg1); /* restore map #1 */ O = 0; /* clear overflow */ (*rtn)++; /* return via good exit */ return SCPE_OK; em21: AR = 0x3231; /* error condition: */ BR = 0x454d; /* AR = '21', BR = 'EM' */ return SCPE_OK; /* return via unmodified rtn */ } /* implementation of VIS RTE-IVB EMA support * .VSET microcode routine * Call: * .OCT 101476B * DEF RTN return address * DEF VIN input vector * DEF VOUT output vector * DEF MAPS * OCT #SCALARS * OCT #VECTORS * OCT K 1024/(#words/element) * RTN EQU * error return (B,A) = "VI22" * RTN+1 EQU *+1 hard return, A = K/IMAX * RTN+2 EQU *+2 easy return, A = 0, B = 2* #VCTRS */ t_stat cpu_ema_vset(uint32* rtn, OPS op, t_bool debug) { uint32 vin = op[0].word; /* S1 */ uint32 vout = op[1].word; /* S2 */ uint32 maps = op[2].word; /* S3 */ uint32 scalars = op[3].word; /* S4 */ uint32 vectors = op[4].word; /* S5 */ uint32 k = op[5].word; /* S6 */ uint32 imax = 0; /* imax S11*/ uint32 xidex,idext1,mseg,phys, addr, i, MA; t_bool negflag = FALSE; for (i=0; i> 1) & MSEGMASK; /* S9 get logical start MSEG */ phys = idext1 & 01777; /* phys start of EMA */ for (i=0; i> 10) & 0xffff; /* get page */ WriteW(maps++, addr); /* save page# */ WriteW(maps++, addr+1); /* save next page# as well */ MA = ReadW(vin++); /* get index into Y */ resolve(MA, &MA, 0); YR = ReadW(MA); /* get index value */ WriteW(vout++, MA); /* copy address of index */ if (YR & SIGN) { /* index is negative */ negflag = TRUE; /* mark a negative index (HARD) */ YR = (~YR + 1) & DMASK; /* make index positive */ } if (imax < YR) imax = YR; /* set maximum index */ mseg += 04000; /* incr mseg address by 2 more pages */ } MA = ReadW(vin); /* get N index into Y */ resolve(MA, &MA, 0); YR = ReadW(MA); WriteW(vout++, MA); vin++; /* copy address of N */ if (imax==0) goto easy; /* easy case */ AR = k / imax; AR++; /* calculate K/IMAX */ if (negflag) goto hard; /* had a negative index? */ if (YR > AR) goto hard; easy: (*rtn)++; /* return via exit 2 */ AR = 0; hard: (*rtn)++; /* return via exit 1 */ BR = 2 * op[4].word; /* B = 2* vectors */ return SCPE_OK; vi22: /* error condition */ AR=0x3232; /* AR = '22' */ BR=0x5649; /* BR = 'VI' */ return SCPE_OK; /* return via unmodified e->rtn */ } typedef struct ema4 { uint32 mseg; /* logical start of MSEG */ uint32 msegsz; /* size of std mseg in pgs */ uint32 pgoff; /* pg # in EMA containing element */ uint32 offs; /* offset into page of element */ uint32 msoff; /* total offset to element in MSEG */ uint32 emasz; /* size of ema in pgs */ uint32 msegno; /* # of std mseg */ uint32 ipgs; /* # of pgs to start of MSEG */ uint32 npgs; /* # of pgs needed */ uint32 spmseg; /* first phys pg of MSEG */ } EMA4; static t_bool cpu_ema_emas(uint32 dtbl,uint32 atbl,EMA4* e) { uint32 xidex, eqt; uint32 sum, msegsz,pgoff,offs,emasz,msegno,msoff,ipgs; if (!cpu_ema_resolve(dtbl,atbl,&sum)) return FALSE; /* calculate 32 bit index */ xidex = ReadIO(idx,UMAP); /* read ID extension */ msegsz = ReadWA(xidex+0) & 037; /* S5 # pgs for std MSEG */ pgoff = sum >> 10; /* S2 page containing element */ offs = sum & 01777; /* S6 offset in page to element */ if (pgoff > 1023) return FALSE; /* overflow? */ eqt = ReadIO(xeqt,UMAP); emasz = ReadWA(eqt+28) & 01777; /* S EMA size in pages */ if (pgoff > emasz) return FALSE; /* outside EMA? */ msegno = pgoff / msegsz; /* S4 # of MSEG */ msoff = pgoff % msegsz; /* offset within MSEG in pgs */ ipgs = pgoff - msoff; /* S7 # pgs to start of MSEG */ msoff = msoff << 10; /* offset within MSEG in words */ msoff += offs; /* S1 offset to element in words */ e->msegsz = msegsz; /* return calculated data */ e->pgoff = pgoff; e->offs = offs; e->emasz = emasz; e->msegno = msegno; e->ipgs = ipgs; e->msoff = msoff; return TRUE; } static t_bool cpu_ema_mmap01(EMA4* e) { uint32 xidex,idext0, pg, pg0, pg1, i; uint32 base = e->mseg >> 10; /* get the # of first MSEG DMS reg */ xidex = ReadIO(idx,UMAP); /* get ID extension */ idext0 = ReadWA(xidex+1); if (e->npgs==0) return FALSE; /* no pages to map? */ if ((e->npgs+1+e->ipgs) <= e->emasz) e->npgs++; /* actually map npgs+1 pgs */ /* locations 1740...1777 of user base page contain the map entries we need. * They are normally hidden by BP fence, therefore they have to be accessed by * another fence-less map register. uCode uses #1, macro code uses $DVCT (==2) */ pg0 = dms_rmap(UMAP+0); /* read base page map# */ pg1 = dms_rmap(UMAP+1); /* save map# 1 */ dms_wmap(UMAP+1,pg0); /* map #0 into reg #1 */ for (i=0; (base+i)<32; i++) { pg = inpgs ? e->spmseg : 0140000; /* write protect if outside */ WriteIO(umaps+base+i, pg, UMAP); /* copy pg to user map */ /* printf("MAP val %d to reg %d (addr=%o)\n",pg,base+i,umaps+base+i); */ dms_wmap(UMAP+base+i, pg); /* set DMS reg */ e->spmseg++; } dms_wmap(UMAP+1,pg1); /* restore map #1 */ xidex = ReadIO(idx,UMAP); /* get ID extension */ idext0 = ReadWA(xidex+0); if (e->msegno == 0xffff) /* non std mseg */ idext0 |= 0x8000; /* set nonstd marker */ else idext0 = (idext0 & 037) | (e->msegno<<5); /* set new current mseg# */ WriteIO(xidex, idext0, SMAP); /* save back value */ AR = 0; /* was successful */ return TRUE; } static t_bool cpu_ema_mmap02(EMA4* e) { uint32 xidex, eqt, idext1; uint32 mseg,phys,spmseg,emasz,msegsz,msegno; xidex = ReadIO(idx,UMAP); /* get ID extension */ msegsz = ReadWA(xidex+0) & 037; /* P size of std MSEG */ idext1 = ReadWA(xidex+1); mseg = (idext1 >> 1) & MSEGMASK; /* S9 get logical start MSEG */ phys = idext1 & 01777; /* S phys start of EMA */ spmseg = phys + e->ipgs; /* S7 phys pg# of MSEG */ msegno = e->ipgs / msegsz; if ((e->ipgs % msegsz) != 0) /* non std MSEG? */ msegno = 0xffff; /* S4 yes, set marker */ if (e->npgs > msegsz) return FALSE; /* map more pages than MSEG sz? */ eqt = ReadIO(xeqt,UMAP); emasz = ReadWA(eqt+28) & 01777; /* B EMA size in pages */ if ((e->ipgs+e->npgs) > emasz) return FALSE; /* outside EMA? */ if ((e->ipgs+msegsz) > emasz) /* if MSEG overlaps end of EMA */ e->npgs = emasz - e->ipgs; /* only map until end of EMA */ e->emasz = emasz; /* copy arguments */ e->msegsz = msegsz; e->msegno = msegno; e->spmseg = spmseg; e->mseg = mseg; return cpu_ema_mmap01(e); } static t_stat cpu_ema_mmap(uint32 ipage,uint32 npgs, t_bool debug) { uint32 xidex; EMA4 ema4, *e = &ema4; e->ipgs = ipage; /* S6 set the arguments */ e->npgs = npgs; /* S5 */ AR = 0; xidex = ReadIO(idx,UMAP); if ((ipage & SIGN) || /* negative page displacement? */ (npgs & SIGN) || /* negative # of pages? */ xidex == 0 || /* no EMA? */ !cpu_ema_mmap02(e)) /* mapping failed? */ AR = 0177777; /* return with error */ return SCPE_OK; /* leave */ } static t_bool cpu_ema_emat(EMA4* e) { uint32 xidex,idext0; uint32 curmseg,phys,msnum,lastpgs; xidex = ReadIO(idx,UMAP); /* read ID extension */ idext0 = ReadWA(xidex+0); /* get current segment */ curmseg = idext0 >> 5; if ((idext0 & 0100000) || /* was nonstd MSEG? */ curmseg != e->msegno) { /* or different MSEG last time? */ phys = ReadWA(xidex+1) & 01777; /* physical start pg of EMA */ e->spmseg = phys + e->ipgs; /* physical start pg of MSEG */ msnum = e->emasz / e->msegsz; /* find last MSEG# */ lastpgs = e->emasz % e->msegsz; /* #pgs in last MSEG */ if (lastpgs==0) msnum--; /* adjust # of last MSEG */ e->npgs = msnum==e->msegno ? lastpgs : e->msegsz; /* for last MSEG, only map available pgs */ if (!cpu_ema_mmap01(e)) return FALSE; /* map npgs pages at ipgs */ } BR = e->mseg + e->msoff; /* return address of element */ return TRUE; /* and everything done */ } /* .EMIO microcode routine, resolves element addr for EMA array * and maps the appropriate map segment * * Call: * OCT 105250B * DEF RTN error return (rtn), good return is rtn+1 * DEF BUFLEN length of buffer in words (bufl) * DEF TABLE[,I] array declaration (dtbl) * DEF A(N)[,I] actual subscripts (atbl) * DEF A(N-1)[,I] * ... * DEF A(2)[,I] * DEF A(1)[,I] * RTN EQU * error return A="15", B="EM" * RTN+1 EQU *+1 good return B=logical address * * TABLE DEC # # dimensions * DEC -L(N) * DEC D(N-1) * DEC -L(N-1) lower bound (n-1)st dim * DEC D(N-2) (n-2)st dim * ... * DEC D(1) 1st dim * DEC -L(1) lower bound 1st dim * DEC # # words/element * OFFSET 1 EMA Low * OFFSET 2 EMA High */ static t_stat cpu_ema_emio(uint32* rtn,uint32 bufl,uint32 dtbl,uint32 atbl,t_bool debug) { uint32 xidex, idext1; uint32 mseg, bufpgs, npgs; EMA4 ema4, *e = &ema4; xidex = ReadIO(idx,UMAP); /* read ID extension */ if (bufl & SIGN || /* buffer length negative? */ xidex==0) goto em16; /* no EMA declared? */ idext1 = ReadWA(xidex+1); /* |logstrt mseg|d|physstrt ema| */ mseg = (idext1 >> 1) & MSEGMASK; /* get logical start MSEG */ if (!cpu_ema_emas(dtbl,atbl,e)) goto em16; /* resolve address */ bufpgs = (bufl + e->offs) >> 10; /* # of pgs reqd for buffer */ if ((bufl + e->offs) & 01777) bufpgs++; /* S11 add 1 if not at pg boundary */ if ((bufpgs + e->pgoff) > e->emasz) goto em16; /* exceeds EMA limit? */ npgs = (e->msoff + bufl) >> 10; /* # of pgs reqd for MSEG */ if ((e->msoff + bufl) & 01777) npgs++; /* add 1 if not at pg boundary */ if (npgs < e->msegsz) { e->mseg = mseg; /* logical stat of MSEG */ if (!cpu_ema_emat(e)) goto em16; /* do a std mapping */ } else { BR = mseg + e->offs; /* logical start of buffer */ e->npgs = bufpgs; /* S5 # pgs required */ e->ipgs = e->pgoff; /* S6 page offset to reqd pg */ if (!cpu_ema_mmap02(e)) goto em16; /* do nonstd mapping */ } (*rtn)++; /* return via good exit */ return SCPE_OK; em16: /* error condition */ AR=0x3136; /* AR = '16' */ BR=0x454d; /* BR = 'EM' */ return SCPE_OK; /* return via unmodified rtn */ } /* .EMAP microcode routine, resolves both EMA/non-EMA calls * Call: * OCT 105257B * DEF RTN error return (rtn), good return is rtn+1 * DEF ARRAY[,I] array base (abase) * DEF TABLE[,I] array declaration (dtbl) * DEF A(N)[,I] actual subscripts (atbl) * DEF A(N-1)[,I] * ... * DEF A(2)[,I] * DEF A(1)[,I] * RTN EQU * error return A="15", B="EM" * RTN+1 EQU *+1 good return B=logical address * * TABLE DEC # # dimensions * DEC -L(N) * DEC D(N-1) * DEC -L(N-1) lower bound (n-1)st dim * DEC D(N-2) (n-2)st dim * ... * DEC D(1) 1st dim * DEC -L(1) lower bound 1st dim * DEC # # words/element * OFFSET 1 EMA Low * OFFSET 2 EMA High */ static t_stat cpu_ema_emap(uint32* rtn,uint32 abase,uint32 dtbl,uint32 atbl,t_bool debug) { uint32 xidex, eqt, idext0, idext1; int32 sub, act, low, ndim, sz; uint32 offs, pgoff, emasz, phys, msgn, mseg, sum, MA, pg0, pg1; xidex = ReadIO(idx,UMAP); /* read ID Extension */ if (xidex) { /* is EMA declared? */ idext1 = ReadWA(xidex+1); /* get word 1 of idext */ mseg = (idext1 >> 1) & MSEGMASK; /* get logical start MSEG */ if (abase >= mseg) { /* EMA reference? */ if (!cpu_ema_resolve(dtbl,atbl,&sum)) /* calculate subscript */ goto em15; offs = sum & 01777; /* address offset within page */ pgoff = sum >> 10; /* ema offset in pages */ if (pgoff > 1023) goto em15; /* overflow? */ eqt = ReadIO(xeqt,UMAP); emasz = ReadWA(eqt+28) & 01777; /* EMA size in pages */ phys = idext1 & 01777; /* physical start pg of EMA */ if (pgoff > emasz) goto em15; /* outside EMA range? */ msgn = mseg >> 10; /* get # of 1st MSEG reg */ phys += pgoff; pg0 = dms_rmap(UMAP+0); /* read base page map# */ pg1 = dms_rmap(UMAP+1); /* save map# 1 */ dms_wmap(UMAP+1,pg0); /* map #0 into reg #1 */ WriteIO(umaps+msgn, phys, UMAP); /* store 1st mapped pg in user map */ dms_wmap(UMAP+msgn, phys); /* and set the map register */ phys = (pgoff+1)==emasz ? 0140000 : phys+1; /* protect 2nd map if end of EMA */ WriteIO(umaps+msgn+1, phys, UMAP); /* store 2nd mapped pg in user map */ dms_wmap(UMAP+msgn+1, phys); /* and set the map register */ dms_wmap(UMAP+1,pg1); /* restore map #1 */ idext0 = ReadWA(xidex+0) | 0100000; /* set NS flag in id extension */ WriteIO(xidex+0, idext0, SMAP); /* save back value */ AR = 0; /* was successful */ BR = mseg + offs; /* calculate log address */ (*rtn)++; /* return via good exit */ return SCPE_OK; } } /* not EMA reference */ ndim = ReadW(dtbl++); if (ndim<0) goto em15; /* negative ´dimensions */ sum = 0; /* accu for index calc */ while (ndim > 0) { MA = ReadW(atbl++); /* get address of A(N) */ resolve (MA, &MA, 0); act = ReadW(MA); /* A(N) */ low = ReadW(dtbl++); /* -L(N) */ sub = SEXT(act) + SEXT(low); /* subscript */ if (sub & 0xffff8000) goto em15; /* overflow? */ sum += sub; /* accumulate */ sz = ReadW(dtbl++); sz = SEXT(sz); if (sz < 0) goto em15; sum *= sz; /* and multiply with sz of dimension */ if (sum & 0xffff8000) goto em15; /* overflow? */ ndim--; } BR = abase + sum; /* add displacement */ (*rtn)++; /* return via good exit */ return SCPE_OK; em15: /* error condition */ AR=0x3135; /* AR = '15' */ BR=0x454d; /* BR = 'EM' */ return SCPE_OK; /* return via unmodified e->rtn */ } t_stat cpu_rte_ema (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; OP_PAT pattern; uint32 entry, rtn; t_bool debug = DEBUG_PRI (cpu_dev, DEB_EMA); entry = IR & 017; /* mask to entry point */ pattern = op_ema[entry]; /* get operand pattern */ if (pattern != OP_N) if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ return reason; if (debug) { /* debugging? */ fprintf (sim_deb, ">>CPU EMA: PC = %06o, IR = %06o (", err_PC,IR); /* print preamble and IR */ fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ NULL, SWMASK('M')); fputc (')', sim_deb); fprint_ops (pattern, op); /* print operands */ fputc ('\n', sim_deb); /* terminate line */ } switch (entry) { /* decode IR<3:0> */ case 000: /* .EMIO 105240 (OP_A) */ rtn = op[0].word; reason = cpu_ema_emio(&rtn, op[1].word, op[2].word, PC, debug); /* handle the EMIO instruction */ PC = rtn; if (debug) fprintf (sim_deb, ">>CPU EMA: return .EMIO: AR = %06o, BR = %06o, rtn=%s\n", AR, BR, PC==op[0].word?"error":"good"); break; case 001: /* .MMAP 105241 (OP_AKK) */ reason = cpu_ema_mmap(op[1].word, op[2].word, debug); /* handle the MMAP instruction */ if (debug) fprintf (sim_deb, ">>CPU EMA: return .MMAP: AR = %06o\n",AR); break; case 002: /* [test] 105242 (OP_N) */ /* effectively, this code just returns without error: * real microcode will set S register to 102077B when in single step mode */ if (sim_step==1) { if (debug) fprintf(sim_deb, ">>CPU EMA: EMA option 92067 correctly installed: S=102077\n"); SR = 0102077; } break; case 017: /* .EMAP 105247 (OP_A) */ rtn = op[0].word; /* error return */ reason = cpu_ema_emap(&rtn, op[1].word, op[2].word, PC, debug); /* handle the EMAP instruction */ PC = rtn; if (debug) { fprintf (sim_deb, ">>CPU EMA: return .EMAP: AR = %06o, BR = %06o, rtn=%s\n", AR, BR, PC==op[0].word?"error":"good"); } break; default: /* others undefined */ reason = stop_inst; } return reason; } simh-3.8.1/HP2100/hp2100_lps.c0000644000175000017500000006044511107411526013416 0ustar vlmvlm/* hp2100_lps.c: HP 2100 12653A/2767 line printer simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. LPS 12653A 2767 line printer 12566B microcircuit interface with loopback diagnostic connector 26-Jun-08 JDB Rewrote device I/O to model backplane signals 10-May-07 RMS Added UNIT_TEXT flag 11-Jan-07 JDB CLC cancels I/O event if DIAG (jumper W9 in "A" pos) Added ioCRS state to I/O decoders 19-Nov-04 JDB Added restart when set online, etc. Fixed col count for non-printing chars 01-Oct-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON Fixed status returns for error conditions Fixed handling of non-printing characters Fixed handling of characters after column 80 Improved timing model accuracy for RTE Added fast/realistic timing Added debug printouts 03-Jun-04 RMS Fixed timing (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Implemented DMA SRQ (follows FLG) 25-Apr-03 RMS Revised for extended file support 24-Oct-02 RMS Added microcircuit test features 30-May-02 RMS Widened POS to 32b 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW 07-Sep-01 RMS Moved function prototypes 21-Nov-00 RMS Fixed flag, fbf power up state Added command flop 15-Oct-00 RMS Added variable device number support References: - 2767A Line Printer Operating and Service Manual (02767-90002, Oct-1973) - 12566B, 12566B-001, 12566B-002, 12566B-003 Microcircuit Interface Kits Operating and Service Manual (12566-90015, Apr-1976) This module simulates two different devices. In "diagnostic mode," it simulates a 12566B microcircuit interface card with a loopback connector and the jumpers set as required for execution of the General Purpose Register diagnostic. In non-diagnostic mode, it simulates a 12653A line printer interface card and a 2767 line printer. The 12566B interface with the loopback connector ties the device command output to the device flag input. Setting control therefore causes device flag to set almost immediately. Device command is active only during that interim. Under simulation, the loopback occurs within the STC handler, and CMD is never set. The 2767 impact printer has a rotating drum with 80 columns of 64 raised characters. ASCII codes 32 through 95 (SPACE through "_") form the print repertoire. The printer responds to the control characters FF, LF, and CR. The 80 columns are divided into four zones of 20 characters each that are addressed sequentially. Received characters are buffered in a 20-character memory. When the 20th printable character is received, the current zone is printed, and the memory is reset. In the absence of print command characters, a zone print operation will commence after each group of 20 printable characters is transmitted to the printer. The print command characters have these actions: * CR -- print the characters in the current zone, reset to zone 1, and clear the buffer memory. * LF -- same as CR, plus advances the paper one line. * FF -- same as CR, plus advances the paper to the top of the next form. The 2767 provides two status bits via the interface: bit 15 -- printer not ready bit 0 -- printer busy The expected status returns are: 100001 -- power off or cable disconnected 100001 -- initial power on, then changes to 000001 within sixty seconds of initial power on 000001 -- power on, paper unloaded or printer offline or not idle 000000 -- power on, paper loaded and printer online and idle These simulator commands provide the listed printer states: SET LPS POWEROFF --> power off or cable disconnected SET LPS POWERON --> power on SET LPS OFFLINE --> printer offline SET LPS ONLINE --> printer online ATT LPS --> paper loaded DET LPS --> paper out The following implemented behaviors have been inferred from secondary sources (diagnostics, operating system drivers, etc.), due to absent or contradictory authoritative information; future correction may be needed: 1. Paper out sets BUSY instead of NOT READY. 2. Print operation in progress sets BUSY instead of NOT READY. 3. Characters not in the print repertoire are replaced with blanks. 4. The 81st and succeeding characters overprint the current line. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #define LPS_ZONECNT 20 /* zone char count */ #define LPS_PAGECNT 80 /* page char count */ #define LPS_PAGELNT 60 /* page line length */ #define LPS_FORMLNT 66 /* form line length */ /* Printer power states */ #define LPS_ON 0 /* power is on */ #define LPS_OFF 1 /* power is off */ #define LPS_TURNING_ON 2 /* power is turning on */ #define LPS_BUSY 0000001 /* busy status */ #define LPS_NRDY 0100000 /* not ready status */ #define LPS_PWROFF LPS_BUSY | LPS_NRDY /* power-off status */ #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ #define UNIT_V_POWEROFF (UNIT_V_UF + 1) /* unit powered off */ #define UNIT_V_OFFLINE (UNIT_V_UF + 2) /* unit offline */ #define UNIT_DIAG (1 << UNIT_V_DIAG) #define UNIT_POWEROFF (1 << UNIT_V_POWEROFF) #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) FLIP_FLOP lps_control = CLEAR; FLIP_FLOP lps_flag = CLEAR; FLIP_FLOP lps_flagbuf = CLEAR; int32 lps_ccnt = 0; /* character count */ int32 lps_lcnt = 0; /* line count */ int32 lps_stopioe = 0; /* stop on error */ int32 lps_sta = 0; /* printer status */ int32 lps_timing = 1; /* timing type */ uint32 lps_power = LPS_ON; /* power state */ /* Hardware timing: (based on 1580 instr/msec) instr msec calc msec ------------------------ - character transfer time : ctime = 2 2 us - per-zone printing time : ptime = 55300 35 40 - per-line paper slew time : stime = 17380 11 13 - power-on ready delay time : rtime = 158000 100 NOTE: the printer acknowledges before the print motion has stopped to allow for continuous slew, so the set times are a bit less than the calculated operation time from the manual. NOTE: the 2767 diagnostic checks completion times, so the realistic timing must be used. Because simulator timing is in instructions, and because the diagnostic uses the TIMER instruction (~1580 executions per millisecond) when running on a 1000-E/F but a software timing loop (~400-600 executions per millisecond) when running on anything else, realistic timings are decreased by three-fourths when not executing on an E/F. */ int32 lps_ctime = 0; /* char xfer time */ int32 lps_ptime = 0; /* zone printing time */ int32 lps_stime = 0; /* paper slew time */ int32 lps_rtime = 0; /* power-on ready time */ typedef int32 TIMESET[4]; /* set of controller times */ int32 *const lps_timers[] = { &lps_ctime, &lps_ptime, &lps_stime, &lps_rtime }; const TIMESET lps_times[2] = { { 2, 55300, 17380, 158000 }, /* REALTIME */ { 2, 1000, 1000, 1000 } /* FASTTIME */ }; DEVICE lps_dev; uint32 lpsio (uint32 select_code, IOSIG signal, uint32 data); t_stat lps_svc (UNIT *uptr); t_stat lps_reset (DEVICE *dptr); t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat lps_attach (UNIT *uptr, char *cptr); t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc); /* LPS data structures lps_dev LPS device descriptor lps_unit LPS unit descriptor lps_reg LPS register list */ DIB lps_dib = { LPS, &lpsio }; UNIT lps_unit = { UDATA (&lps_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0) }; REG lps_reg[] = { { ORDATA (BUF, lps_unit.buf, 16) }, { ORDATA (STA, lps_sta, 16) }, { ORDATA (POWER, lps_power, 2), REG_RO }, { FLDATA (CTL, lps_control, 0) }, { FLDATA (FLG, lps_flag, 0) }, { FLDATA (FBF, lps_flagbuf, 0) }, { DRDATA (CCNT, lps_ccnt, 7), PV_LEFT }, { DRDATA (LCNT, lps_lcnt, 7), PV_LEFT }, { DRDATA (POS, lps_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (CTIME, lps_ctime, 24), PV_LEFT }, { DRDATA (PTIME, lps_ptime, 24), PV_LEFT }, { DRDATA (STIME, lps_stime, 24), PV_LEFT }, { DRDATA (RTIME, lps_rtime, 24), PV_LEFT }, { FLDATA (TIMING, lps_timing, 0), REG_HRO }, { FLDATA (STOP_IOE, lps_stopioe, 0) }, { ORDATA (DEVNO, lps_dib.devno, 6), REG_HRO }, { NULL } }; MTAB lps_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, "printer mode", "PRINTER", NULL }, { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", lps_poweroff }, { UNIT_POWEROFF, 0, "power on", "POWERON", lps_poweron }, { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, { UNIT_OFFLINE, 0, "online", "ONLINE", lps_restart }, { MTAB_XTD | MTAB_VDV, 0, NULL, "REALTIME", &lps_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "FASTTIME", &lps_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, NULL, &lps_show_timing, NULL }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &lps_dev }, { 0 } }; DEVICE lps_dev = { "LPS", &lps_unit, lps_reg, lps_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lps_reset, NULL, &lps_attach, NULL, &lps_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG }; /* I/O signal handler */ uint32 lpsio (uint32 select_code, IOSIG signal, uint32 data) { const t_bool clf = (signal > ioCLF); const IOSIG base_signal = IOBASE (signal); /* derive base signal */ int32 sched; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ lps_flag = lps_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ lps_flag = lps_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (lps); break; case ioSFS: /* skip if flag is set */ setstdSKF (lps); break; case ioIOI: /* I/O data input */ if ((lps_unit.flags & UNIT_DIAG) == 0) { /* real lpt? */ if (lps_power == LPS_ON) { /* power on? */ if (((lps_unit.flags & UNIT_ATT) == 0) || /* paper out? */ (lps_unit.flags & UNIT_OFFLINE) || /* offline? */ sim_is_active (&lps_unit)) lps_sta = LPS_BUSY; else lps_sta = 0; } else lps_sta = LPS_PWROFF; } data = lps_sta; /* diag, rtn status */ if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS LIx: Status %06o returned\n", data); break; case ioIOO: /* I/O data output */ if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS OTx: Character %06o output\n", data); lps_unit.buf = data; break; case ioPOPIO: /* power-on preset to I/O */ lps_flag = lps_flagbuf = SET; /* set flag and flag buffer */ lps_unit.buf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ lps_control = CLEAR; /* clear control */ sim_cancel (&lps_unit); /* deactivate unit */ break; case ioCLC: /* clear control flip-flop */ lps_control = CLEAR; if ((lps_unit.flags & UNIT_DIAG) && clf) /* diagnostic mode and clearing flag? */ sim_cancel (&lps_unit); /* prevent FLG/SRQ */ break; case ioSTC: /* set control flip-flop */ lps_control = SET; /* set control */ if (lps_unit.flags & UNIT_DIAG) { /* diagnostic? */ lps_sta = lps_unit.buf; /* loop back data */ sim_activate (&lps_unit, 2); /* schedule flag */ } else { /* real lpt, sched */ if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS STC: Character %06o scheduled for line %d, column %d, ", lps_unit.buf, lps_lcnt + 1, lps_ccnt + 1); if ((lps_unit.buf != '\f') && (lps_unit.buf != '\n') && (lps_unit.buf != '\r')) { /* normal char */ lps_ccnt = lps_ccnt + 1; /* incr char counter */ if (lps_ccnt % LPS_ZONECNT == 0) /* end of zone? */ sched = lps_ptime; /* print zone */ else sched = lps_ctime; /* xfer char */ } else { /* print cmd */ if (lps_ccnt % LPS_ZONECNT == 0) /* last zone printed? */ sched = lps_ctime; /* yes, so just char time */ else sched = lps_ptime; /* no, so print needed */ lps_ccnt = 0; /* reset char counter */ if (lps_unit.buf == '\n') { /* line advance */ lps_lcnt = (lps_lcnt + 1) % LPS_PAGELNT; if (lps_lcnt > 0) sched = sched + lps_stime; else sched = sched + /* allow for perf skip */ lps_stime * (LPS_FORMLNT - LPS_PAGELNT); } else if (lps_unit.buf == '\f') { /* form advance */ sched = sched + lps_stime * (LPS_FORMLNT - lps_lcnt); lps_lcnt = 0; } } sim_activate (&lps_unit, sched); if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, "time = %d\n", sched); } break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, lps); /* set standard PRL signal */ setstdIRQ (select_code, lps); /* set standard IRQ signal */ setstdSRQ (select_code, lps); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ lps_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ lpsio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ lpsio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service */ t_stat lps_svc (UNIT *uptr) { int32 c = uptr->buf & 0177; if (lps_power == LPS_TURNING_ON) { /* printer warmed up? */ lps_power = LPS_ON; /* change state */ lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ if (DEBUG_PRS (lps_dev)) fputs (">>LPS svc: Power state is ON\n", sim_deb); return SCPE_OK; /* done */ } if (uptr->flags & UNIT_DIAG) { /* diagnostic? */ lpsio (lps_dib.devno, ioENF, 0); /* set flag */ return SCPE_OK; /* done */ } if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lps_stopioe, SCPE_UNATT); else if (uptr->flags & UNIT_OFFLINE) /* offline? */ return IORETURN (lps_stopioe, STOP_OFFLINE); else if (uptr->flags & UNIT_POWEROFF) /* powered off? */ return IORETURN (lps_stopioe, STOP_PWROFF); lpsio (lps_dib.devno, ioENF, 0); /* set flag */ if (((c < ' ') || (c > '_')) && /* non-printing char? */ (c != '\f') && (c != '\n') && (c != '\r')) { if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS svc: Character %06o erased\n", c); c = ' '; /* replace with blank */ } if (lps_ccnt > LPS_PAGECNT) { /* 81st character? */ fputc ('\r', uptr->fileref); /* return to line start */ uptr->pos = uptr->pos + 1; /* update pos */ lps_ccnt = 1; /* reset char counter */ if (DEBUG_PRS (lps_dev)) fputs (">>LPS svc: Line wraparound to column 1\n", sim_deb); } fputc (c, uptr->fileref); /* "print" char */ uptr->pos = uptr->pos + 1; /* update pos */ if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS svc: Character %06o printed\n", c); if ((lps_lcnt == 0) && (c == '\n')) { /* LF did TOF? */ fputc ('\f', uptr->fileref); /* do perf skip */ uptr->pos = uptr->pos + 1; /* update pos */ if (DEBUG_PRS (lps_dev)) fputs (">>LPS svc: Perforation skip to TOF\n", sim_deb); } if (ferror (uptr->fileref)) { perror ("LPS I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat lps_reset (DEVICE *dptr) { if (sim_switches & SWMASK ('P')) { /* PON reset? */ lps_power = LPS_ON; /* power is on */ lps_set_timing (NULL, lps_timing, NULL, NULL); /* init timing set */ } lpsio (lps_dib.devno, ioPOPIO, 0); /* send POPIO signal */ lps_sta = 0; /* clear status */ sim_cancel (&lps_unit); /* deactivate unit */ return SCPE_OK; } /* Restart I/O routine If I/O is started via STC, and the printer is powered off, offline, or out of paper, the CTL and CMD flip-flops will set, a service event will be scheduled, and the service routine will be entered. If STOP_IOE is not set, the I/O operation will "hang" at that point until the printer is powered on, set online, or paper is supplied (attached). If a pending operation is "hung" when this routine is called, it is restarted, which clears CTL and sets FBF and FLG, completing the original I/O request. */ t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc) { if (lps_control && !sim_is_active (uptr)) sim_activate (uptr, 0); /* reschedule I/O */ return SCPE_OK; } /* Printer power off */ t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc) { lps_power = LPS_OFF; /* change state */ if (DEBUG_PRS (lps_dev)) fputs (">>LPS set: Power state is OFF\n", sim_deb); return SCPE_OK; } /* Printer power on */ t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc) { if (lps_unit.flags & UNIT_DIAG) { /* diag mode? */ lps_power = LPS_ON; /* no delay */ if (DEBUG_PRS (lps_dev)) fputs (">>LPS set: Power state is ON\n", sim_deb); } else { lps_power = LPS_TURNING_ON; /* change state */ lps_unit.flags |= UNIT_OFFLINE; /* set offline */ sim_activate (&lps_unit, lps_rtime); /* schedule ready */ if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, ">>LPS set: Power state is TURNING ON, scheduled time = %d\n", lps_rtime ); } return SCPE_OK; } /* Attach routine */ t_stat lps_attach (UNIT *uptr, char *cptr) { lps_ccnt = lps_lcnt = 0; /* top of form */ lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ return attach_unit (uptr, cptr); } /* Set printer timing Realistic timing is factored, depending on CPU model, to account for the timing method employed by the diagnostic. */ t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i, factor = 1; lps_timing = (val != 0); /* determine choice */ if ((lps_timing == 0) && /* calc speed factor */ (UNIT_CPU_MODEL != UNIT_1000_E) && (UNIT_CPU_MODEL != UNIT_1000_F)) factor = 4; for (i = 0; i < (sizeof (lps_timers) / sizeof (lps_timers[0])); i++) *lps_timers[i] = lps_times[lps_timing][i] / factor; /* assign times */ return SCPE_OK; } /* Show printer timing */ t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc) { if (lps_timing) fputs ("fast timing", st); else fputs ("realistic timing", st); return SCPE_OK; } simh-3.8.1/HP2100/hp2100_mt.c0000644000175000017500000006505011107411526013235 0ustar vlmvlm/* hp2100_mt.c: HP 2100 12559A magnetic tape simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. MT 12559A 3030 nine track magnetic tape 04-Sep-08 JDB Fixed missing flag after CLR command 02-Sep-08 JDB Moved write enable and format commands from MTD to MTC 26-Jun-08 JDB Rewrote device I/O to model backplane signals 28-Dec-06 JDB Added ioCRS state to I/O decoders 07-Oct-04 JDB Allow enable/disable from either device 14-Aug-04 RMS Modified handling of end of medium (suggested by Dave Bryan) 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Implemented DMA SRQ (follows FLG) 21-Dec-03 RMS Adjusted msc_ctime for TSB (from Mike Gemeny) 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library 30-Sep-02 RMS Revamped error handling 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length test 20-Jan-02 RMS Fixed bug on last character write 03-Dec-01 RMS Added read only unit, extended SET/SHOW support 07-Sep-01 RMS Moved function prototypes 30-Nov-00 RMS Made variable names unique 04-Oct-98 RMS V2.4 magtape format References: - 12559A 9-Track Magnetic Tape Unit Interface Kit Operating and Service Manual (12559-9001, Jul-1970) - SIMH Magtape Representation and Handling (Bob Supnik, 30-Aug-2006) The 3030 was one of HP's earliest tape drives. The 12559A controller supported a single 800 bpi, 9-track drive, operating at 75 inches per second. It had two unusual characteristics: - The controller accepted only one byte per I/O word, rather than packing two bytes per word. - The drive could not read or write fewer than 12 bytes per record. The first behavior meant that DMA operation required the byte-unpacking feature of the 12578A DMA card for the 2116 computer. The second meant that software drivers had to pad short records with blanks or nulls. Implementation notes: 1. The HP 3030 Magnetic Tape Subsystem diagnostic, part number 20433-60001, has never been located, so this simulator has not been fully tested. It does pass a functional test under DOS-III using driver DVR22. */ #include "hp2100_defs.h" #include "sim_tape.h" #define DB_V_SIZE 16 /* max data buf */ #define DBSIZE (1 << DB_V_SIZE) /* max data cmd */ /* Command - mtc_fnc */ #define FNC_CLR 0300 /* clear */ #define FNC_WC 0031 /* write */ #define FNC_RC 0023 /* read */ #define FNC_GAP 0011 /* write gap */ #define FNC_FSR 0003 /* forward space */ #define FNC_BSR 0041 /* backward space */ #define FNC_REW 0201 /* rewind */ #define FNC_RWS 0101 /* rewind and offline */ #define FNC_WFM 0035 /* write file mark */ /* Status - stored in mtc_sta, (d) = dynamic */ #define STA_LOCAL 0400 /* local (d) */ #define STA_EOF 0200 /* end of file */ #define STA_BOT 0100 /* beginning of tape */ #define STA_EOT 0040 /* end of tape */ #define STA_TIM 0020 /* timing error */ #define STA_REJ 0010 /* programming error */ #define STA_WLK 0004 /* write locked (d) */ #define STA_PAR 0002 /* parity error */ #define STA_BUSY 0001 /* busy (d) */ FLIP_FLOP mtd_flag = CLEAR; FLIP_FLOP mtd_flagbuf = CLEAR; FLIP_FLOP mtc_control = CLEAR; FLIP_FLOP mtc_flag = CLEAR; FLIP_FLOP mtc_flagbuf = CLEAR; int32 mtc_fnc = 0; /* function */ int32 mtc_sta = 0; /* status register */ int32 mtc_dtf = 0; /* data xfer flop */ int32 mtc_1st = 0; /* first svc flop */ int32 mtc_ctime = 40; /* command wait */ int32 mtc_gtime = 1000; /* gap stop time */ int32 mtc_xtime = 15; /* data xfer time */ int32 mtc_stopioe = 1; /* stop on error */ uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */ t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */ static const uint32 mtc_cmd[] = { FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM }; DEVICE mtd_dev, mtc_dev; uint32 mtdio (uint32 select_code, IOSIG signal, uint32 data); uint32 mtcio (uint32 select_code, IOSIG signal, uint32 data); t_stat mtc_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mtc_attach (UNIT *uptr, char *cptr); t_stat mtc_detach (UNIT *uptr); t_stat mt_map_err (UNIT *uptr, t_stat st); t_stat mt_clear (void); /* MTD data structures mtd_dev MTD device descriptor mtd_unit MTD unit list mtd_reg MTD register list */ DIB mt_dib[] = { { MTD, &mtdio }, { MTC, &mtcio } }; #define mtd_dib mt_dib[0] #define mtc_dib mt_dib[1] UNIT mtd_unit = { UDATA (NULL, 0, 0) }; REG mtd_reg[] = { { FLDATA (FLG, mtd_flag, 0) }, { FLDATA (FBF, mtd_flagbuf, 0) }, { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) }, { DRDATA (BPTR, mt_ptr, DB_V_SIZE + 1) }, { DRDATA (BMAX, mt_max, DB_V_SIZE + 1) }, { ORDATA (DEVNO, mtd_dib.devno, 6), REG_HRO }, { NULL } }; MTAB mtd_mod[] = { { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mtd_dev }, { 0 } }; DEVICE mtd_dev = { "MTD", &mtd_unit, mtd_reg, mtd_mod, 1, 10, 16, 1, 8, 8, NULL, NULL, &mt_reset, NULL, NULL, NULL, &mtd_dib, DEV_DISABLE | DEV_DIS }; /* MTC data structures mtc_dev MTC device descriptor mtc_unit MTC unit list mtc_reg MTC register list mtc_mod MTC modifier list */ UNIT mtc_unit = { UDATA (&mtc_svc, UNIT_ATTABLE + UNIT_ROABLE, 0) }; REG mtc_reg[] = { { ORDATA (FNC, mtc_fnc, 8) }, { ORDATA (STA, mtc_sta, 9) }, { ORDATA (BUF, mtc_unit.buf, 8) }, { FLDATA (CTL, mtc_control, 0) }, { FLDATA (FLG, mtc_flag, 0) }, { FLDATA (FBF, mtc_flagbuf, 0) }, { FLDATA (DTF, mtc_dtf, 0) }, { FLDATA (FSVC, mtc_1st, 0) }, { DRDATA (POS, mtc_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (CTIME, mtc_ctime, 24), REG_NZ + PV_LEFT }, { DRDATA (GTIME, mtc_gtime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, mtc_stopioe, 0) }, { ORDATA (DEVNO, mtc_dib.devno, 6), REG_HRO }, { NULL } }; MTAB mtc_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mtd_dev }, { 0 } }; DEVICE mtc_dev = { "MTC", &mtc_unit, mtc_reg, mtc_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mtc_attach, &mtc_detach, &mtc_dib, DEV_DISABLE | DEV_DIS }; /* Data channel I/O signal handler The 12559A data channel interface has a number of non-standard features: - The card does not drive PRL or IRQ. - The card does not respond to IAK. - There is no control flip-flop; CLC resets the data transfer flip-flop. - POPIO issues a CLR command and clears the flag and flag buffer flip-flops. - CRS is not used. Implementation notes: 1. The data channel has a flag buffer flip-flop (necessary for the proper timing of the flag flip-flop), but the data channel does not interrupt, so the flag buffer serves no other purpose. */ uint32 mtdio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ mtd_flag = mtd_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ mtd_flag = mtd_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (mtd); break; case ioSFS: /* skip if flag is set */ setstdSKF (mtd); break; case ioIOI: /* I/O data input */ data = mtc_unit.buf; break; case ioIOO: /* I/O data output */ mtc_unit.buf = data & 0377; /* store data */ break; case ioPOPIO: /* power-on preset to I/O */ mt_clear (); /* issue CLR to controller */ mtd_flag = mtd_flagbuf = CLEAR; /* clear flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ break; case ioCLC: /* clear control flip-flop */ mtc_dtf = 0; /* clr xfer flop */ mtd_flag = mtd_flagbuf = CLEAR; /* clear flag and flag buffer */ break; case ioSIR: /* set interrupt request */ setstdSRQ (select_code, mtd); /* set standard SRQ signal */ break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ mtdio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ mtdio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Command channel I/O signal handler. The 12559A command interface is reasonably standard, although POPIO clears, rather than sets, the flag and flag buffer flip-flops. One unusual feature is that commands are initiated when they are output to the interface with OTA/B, rather than waiting until control is set with STC. STC simply enables command-channel interrupts. Implementation notes: 1. In hardware, the command channel card passes PRH to PRL. The data card actually drives PRL with the command channel's control and flag states. That is, the priority chain is broken at the data card, although the command card is interrupting. This works in hardware, but we must break PRL at the command card under simulation to allow the command card to interrupt. 2. In hardware, the CLR command takes 5 milliseconds to complete. During this time, the BUSY bit is set in the status word. Under simulation, we complete immediately, and the BUSY bit never sets.. */ uint32 mtcio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ uint32 i; int32 valid; switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ mtc_flag = mtc_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ mtc_flag = mtc_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (mtc); break; case ioSFS: /* skip if flag is set */ setstdSKF (mtc); break; case ioIOI: /* I/O data input */ data = mtc_sta & ~(STA_LOCAL | STA_WLK | STA_BUSY); if (mtc_unit.flags & UNIT_ATT) { /* construct status */ if (sim_is_active (&mtc_unit)) data = data | STA_BUSY; if (sim_tape_wrp (&mtc_unit)) data = data | STA_WLK; } else data = data | STA_BUSY | STA_LOCAL; break; case ioIOO: /* I/O data output */ data = data & 0377; mtc_sta = mtc_sta & ~STA_REJ; /* clear reject */ if (data == FNC_CLR) { /* clear? */ mt_clear (); /* send CLR to controller */ mtd_flag = mtd_flagbuf = CLEAR; /* clear data flag and flag buffer */ mtc_flag = mtc_flagbuf = SET; /* set command flag and flag buffer */ break; /* command completes immediately */ } for (i = valid = 0; i < sizeof (mtc_cmd); i++) /* is fnc valid? */ if (data == mtc_cmd[i]) valid = 1; if (!valid || sim_is_active (&mtc_unit) || /* is cmd valid? */ ((mtc_sta & STA_BOT) && (data == FNC_BSR)) || (sim_tape_wrp (&mtc_unit) && ((data == FNC_WC) || (data == FNC_GAP) || (data == FNC_WFM)))) mtc_sta = mtc_sta | STA_REJ; else { sim_activate (&mtc_unit, mtc_ctime); /* start tape */ mtc_fnc = data; /* save function */ mtc_sta = STA_BUSY; /* unit busy */ mt_ptr = 0; /* init buffer ptr */ mtcio (select_code, ioCLF, 0); /* clear flags */ mtcio (mtd_dib.devno, ioCLF, 0); mtc_1st = 1; /* set 1st flop */ mtc_dtf = 1; /* set xfer flop */ } break; case ioPOPIO: /* power-on preset to I/O */ mtc_flag = mtc_flagbuf = CLEAR; /* clear flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ mtc_control = CLEAR; break; case ioSTC: /* set control flip-flop */ mtc_control = SET; break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, mtc); /* set standard PRL signal */ setstdIRQ (select_code, mtc); /* set standard IRQ signal */ setstdSRQ (select_code, mtc); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ mtc_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ mtcio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ mtcio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt Can't be write locked, can only write lock detached unit */ t_stat mtc_svc (UNIT *uptr) { t_mtrlnt tbc; t_stat st, r = SCPE_OK; if ((mtc_unit.flags & UNIT_ATT) == 0) { /* offline? */ mtc_sta = STA_LOCAL | STA_REJ; /* rejected */ mtcio (mtc_dib.devno, ioENF, 0); /* set cch flg */ return IORETURN (mtc_stopioe, SCPE_UNATT); } switch (mtc_fnc) { /* case on function */ case FNC_REW: /* rewind */ sim_tape_rewind (uptr); /* BOT */ mtc_sta = STA_BOT; /* update status */ break; case FNC_RWS: /* rewind and offline */ sim_tape_rewind (uptr); /* clear position */ return sim_tape_detach (uptr); /* don't set cch flg */ case FNC_WFM: /* write file mark */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mt_map_err (uptr, st); /* map error */ mtc_sta = STA_EOF; /* set EOF status */ break; case FNC_GAP: /* erase gap */ break; case FNC_FSR: /* space forward */ if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ r = mt_map_err (uptr, st); /* map error */ break; case FNC_BSR: /* space reverse */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ r = mt_map_err (uptr, st); /* map error */ break; case FNC_RC: /* read */ if (mtc_1st) { /* first svc? */ mtc_1st = mt_ptr = 0; /* clr 1st flop */ st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */ if (st == MTSE_RECE) mtc_sta = mtc_sta | STA_PAR; /* rec in err? */ else if (st != MTSE_OK) { /* other error? */ r = mt_map_err (uptr, st); /* map error */ if (r == SCPE_OK) { /* recoverable? */ sim_activate (uptr, mtc_gtime); /* sched IRG */ mtc_fnc = 0; /* NOP func */ return SCPE_OK; } break; /* non-recov, done */ } if (mt_max < 12) { /* record too short? */ mtc_sta = mtc_sta | STA_PAR; /* set flag */ break; } } if (mtc_dtf && (mt_ptr < mt_max)) { /* more chars? */ if (mtd_flag) mtc_sta = mtc_sta | STA_TIM; mtc_unit.buf = mtxb[mt_ptr++]; /* fetch next */ mtdio (mtd_dib.devno, ioENF, 0); /* set dch flg */ sim_activate (uptr, mtc_xtime); /* re-activate */ return SCPE_OK; } sim_activate (uptr, mtc_gtime); /* schedule gap */ mtc_fnc = 0; /* nop */ return SCPE_OK; case FNC_WC: /* write */ if (mtc_1st) mtc_1st = 0; /* no xfr on first */ else { if (mt_ptr < DBSIZE) { /* room in buffer? */ mtxb[mt_ptr++] = mtc_unit.buf; mtc_sta = mtc_sta & ~STA_BOT; /* clear BOT */ } else mtc_sta = mtc_sta | STA_PAR; } if (mtc_dtf) { /* xfer flop set? */ mtdio (mtd_dib.devno, ioENF, 0); /* set dch flg */ sim_activate (uptr, mtc_xtime); /* re-activate */ return SCPE_OK; } if (mt_ptr) { /* write buffer */ if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) { /* write, err? */ r = mt_map_err (uptr, st); /* map error */ break; /* done */ } } sim_activate (uptr, mtc_gtime); /* schedule gap */ mtc_fnc = 0; /* nop */ return SCPE_OK; default: /* unknown */ break; } mtcio (mtc_dib.devno, ioENF, 0); /* set cch flg */ mtc_sta = mtc_sta & ~STA_BUSY; /* not busy */ return r; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* unattached */ mtc_sta = mtc_sta | STA_REJ; /* reject */ case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_EOM: /* end of medium */ case MTSE_TMK: /* end of file */ mtc_sta = mtc_sta | STA_EOF; /* eof */ break; case MTSE_IOERR: /* IO error */ mtc_sta = mtc_sta | STA_PAR; /* error */ if (mtc_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ mtc_sta = mtc_sta | STA_PAR; return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ mtc_sta = mtc_sta | STA_PAR; /* error */ break; case MTSE_BOT: /* reverse into BOT */ mtc_sta = mtc_sta | STA_BOT; /* set status */ break; case MTSE_WRP: /* write protect */ mtc_sta = mtc_sta | STA_REJ; /* reject */ break; } return SCPE_OK; } /* Controller clear */ t_stat mt_clear (void) { t_stat st; if (sim_is_active (&mtc_unit) && /* write in prog? */ (mtc_fnc == FNC_WC) && (mt_ptr > 0)) { /* yes, bad rec */ if (st = sim_tape_wrrecf (&mtc_unit, mtxb, mt_ptr | MTR_ERF)) mt_map_err (&mtc_unit, st); } if (((mtc_fnc == FNC_REW) || (mtc_fnc == FNC_RWS)) && sim_is_active (&mtc_unit)) sim_cancel (&mtc_unit); mtc_1st = mtc_dtf = 0; mtc_sta = mtc_sta & STA_BOT; return SCPE_OK; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { hp_enbdis_pair (dptr, /* make pair cons */ (dptr == &mtd_dev) ? &mtc_dev : &mtd_dev); if (dptr == &mtc_dev) /* command channel reset? */ mtcio (mtc_dib.devno, ioPOPIO, 0); /* send POPIO signal to command channel */ else /* data channel reset */ mtdio (mtd_dib.devno, ioPOPIO, 0); /* send POPIO signal to data channel */ mtc_fnc = 0; mtc_1st = mtc_dtf = 0; sim_cancel (&mtc_unit); /* cancel activity */ sim_tape_reset (&mtc_unit); if (mtc_unit.flags & UNIT_ATT) mtc_sta = (sim_tape_bot (&mtc_unit)? STA_BOT: 0) | (sim_tape_wrp (&mtc_unit)? STA_WLK: 0); else mtc_sta = STA_LOCAL | STA_BUSY; return SCPE_OK; } /* Attach routine */ t_stat mtc_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); /* attach unit */ if (r != SCPE_OK) return r; /* update status */ mtc_sta = STA_BOT; return r; } /* Detach routine */ t_stat mtc_detach (UNIT* uptr) { mtc_sta = 0; /* update status */ return sim_tape_detach (uptr); /* detach unit */ } simh-3.8.1/HP2100/hp2100_cpu1.c0000644000175000017500000012741411062235674013500 0ustar vlmvlm/* hp2100_cpu1.c: HP 2100/1000 EAU simulator and UIG dispatcher Copyright (c) 2005-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. CPU1 Extended arithmetic and optional microcode dispatchers 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Moved option-present tests to UIG dispatchers Call "user microcode" dispatcher for unclaimed UIG instructions 20-Apr-08 JDB Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64 28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts 17-Nov-07 JDB Enabled DIAG as NOP on 1000 F-Series 04-Jan-07 JDB Added special DBI dispatcher for non-INT64 diagnostic 29-Dec-06 JDB Allows RRR as NOP if 2114 (diag config test) 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 16-Oct-06 JDB Generalized operands for F-Series FP types 26-Sep-06 JDB Split hp2100_cpu1.c into multiple modules to simplify extensions Added iotrap parameter to UIG dispatchers for RTE microcode 22-Feb-05 JDB Removed EXECUTE instruction (is NOP in actual microcode) 21-Jan-05 JDB Reorganized CPU option and operand processing flags Split code along microcode modules 15-Jan-05 RMS Cloned from hp2100_cpu.c Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) - HP 93585A Double Integer Firmware Package Installation and Programming Manual (93585-90007, Feb-1984) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. This source file contains the Extended Arithmetic Unit simulator and the User Instruction Group (a.k.a. "Macro") dispatcher for the 2100 and 1000 (21MX) CPUs. The UIG simulators reside in separate source files, due to the large number of firmware options available for these machines. Unit flags indicate which options are present in the current system. This module also provides generalized instruction operand processing. The 2100 and 1000 machines were microprogrammable; the 2116/15/14 machines were not. Both user- and HP-written microprograms were supported. The microcode address space of the 2100 encompassed four modules of 256 words each. The 1000 M-series expanded that to sixteen modules, and the 1000 E/F-series expanded that still further to sixty-four modules. Each CPU had its own microinstruction set, although the micromachines of the various 1000 models were similar internally. The UIG instructions were divided into ranges assigned to HP firmware options, reserved for future HP use, and reserved for user microprograms. User microprograms could occupy any range not already used on a given machine, but in practice, some effort was made to avoid the HP-reserved ranges. User microprogram simulation is supported by routing any UIG instruction not allocated to an installed firmware option to a user-firmware dispatcher. Site-specific microprograms may be simulated there. In the absence of such a simulation, an unimplemented instruction stop will occur. Regarding option instruction sets, there was some commonality across CPU types. EAU instructions were identical across all models, and the floating point set was the same on the 2100 and 1000. Other options implemented proper instruction supersets (e.g., the Fast FORTRAN Processor from 2100 to 1000-M to 1000-E to 1000-F) or functional equivalence with differing code points (the 2000 I/O Processor from 2100 to 1000, and the extended-precision floating-point instructions from 1000-E to 1000-F). The 2100 decoded the EAU and UIG sets separately in hardware and supported only the UIG 0 code points. Bits 7-4 of a UIG instruction decoded one of sixteen entry points in the lowest-numbered module after module 0. Those entry points could be used directly (as for the floating-point instructions), or additional decoding based on bits 3-0 could be implemented. The 1000 generalized the instruction decoding to a series of microcoded jumps, based on the bits in the instruction. Bits 15-8 indicated the group of the current instruction: EAU (200, 201, 202, 210, and 211), UIG 0 (212), or UIG 1 (203 and 213). UIG 0, UIG 1, and some EAU instructions were decoded further by selecting one of sixteen modules within the group via bits 7-4. Finally, each UIG module decoded up to sixteen instruction entry points via bits 3-0. Jump tables for all firmware options were contained in the base set, so modules needed only to be concerned with decoding their individual entry points within the module. While the 2100 and 1000 hardware decoded these instruction sets differently, the decoding mechanism of the simulation follows that of the 1000 E/F-series. Where needed, CPU type- or model-specific behavior is simulated. The design of the 1000 microinstruction set was such that executing an instruction for which no microcode was present (e.g., executing a FFP instruction when the FFP firmware was not installed) resulted in a NOP. Under simulation, such execution causes an undefined instruction stop if "stop_inst" is non-zero and a NOP otherwise. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" /* EAU The Extended Arithmetic Unit (EAU) adds ten instructions with double-word operands, including multiply, divide, shifts, and rotates. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A 12579A 12579A std std std std The instruction codes are mapped to routines as follows: Instr. Bits Code 15-8 7-4 2116 2100 1000-M 1000-E 1000-F Note ------ ---- --- ------ ------ ------ ------ ------ --------------------- 100000 200 00 [diag] [diag] [self test] 100020 200 01 ASL ASL ASL ASL ASL Bits 3-0 encode shift 100040 200 02 LSL LSL LSL LSL LSL Bits 3-0 encode shift 100060 200 03 TIMER TIMER [deterministic delay] 100100 200 04 RRL RRL RRL RRL RRL Bits 3-0 encode shift 100200 200 10 MPY MPY MPY MPY MPY 100400 201 xx DIV DIV DIV DIV DIV 101020 202 01 ASR ASR ASR ASR ASR Bits 3-0 encode shift 101040 202 02 LSR LSR LSR LSR LSR Bits 3-0 encode shift 101100 202 04 RRR RRR RRR RRR RRR Bits 3-0 encode shift 104200 210 xx DLD DLD DLD DLD DLD 104400 211 xx DST DST DST DST DST The remaining codes for bits 7-4 are undefined and will cause a simulator stop if enabled. On a real 1000-M, all undefined instructions in the 200 group decode as MPY, and all in the 202 group decode as NOP. On a real 1000-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP; all others cause erroneous execution. EAU instruction decoding on the 1000 M-series is convoluted. The JEAU microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump address. The map is detailed on page IC-84 of the ERD. The 1000 E/F-series add two undocumented instructions to the 200 group: TIMER and DIAG. These are described in the ERD on page IA 5-5, paragraph 5-7. The M-series executes these as MPY and RRL, respectively. A third instruction, EXECUTE (100120), is also described but was never implemented, and the E/F-series microcode execute a NOP for this instruction code. Notes: 1. Under simulation, TIMER, DIAG, and EXECUTE cause undefined instruction stops if the CPU is set to 21xx. DIAG and EXECUTE also cause stops on the 1000-M. TIMER does not, because it is used by several HP programs to differentiate between M- and E/F-series machines. 2. DIAG is not implemented under simulation. On the E/F, it performs a destructive test of all installed memory. Because of this, it is only functional if the machine is halted, i.e., if the instruction is executed with the INSTR STEP button. If it is executed in a program, the result is NOP. 3. RRR is permitted and executed as NOP if the CPU is a 2114, as the presence of the EAU is tested by the diagnostic configurator to differentiate between 2114 and 2100/1000 CPUs. */ t_stat cpu_eau (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; uint32 rs, qs, sc, v1, v2, t; int32 sop1, sop2; if ((cpu_unit.flags & UNIT_EAU) == 0) /* option installed? */ if ((UNIT_CPU_MODEL == UNIT_2114) && (IR == 0101100)) /* 2114 and RRR 16? */ return SCPE_OK; /* allowed as NOP */ else return stop_inst; /* fail */ switch ((IR >> 8) & 0377) { /* decode IR<15:8> */ case 0200: /* EAU group 0 */ switch ((IR >> 4) & 017) { /* decode IR<7:4> */ case 000: /* DIAG 100000 */ if ((UNIT_CPU_MODEL != UNIT_1000_E) && /* must be 1000 E-series */ (UNIT_CPU_MODEL != UNIT_1000_F)) /* or 1000 F-series */ return stop_inst; /* trap if not */ break; /* DIAG is NOP unless halted */ case 001: /* ASL 100020-100037 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ O = 0; /* clear ovflo */ while (sc-- != 0) { /* bit by bit */ t = BR << 1; /* shift B */ BR = (BR & SIGN) | (t & 077777) | (AR >> 15); AR = (AR << 1) & DMASK; if ((BR ^ t) & SIGN) O = 1; } break; case 002: /* LSL 100040-100057 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; AR = (AR << sc) & DMASK; /* BR'AR lsh left */ break; case 003: /* TIMER 100060 */ if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ return stop_inst; /* trap if not */ if (UNIT_CPU_MODEL == UNIT_1000_M) /* 1000 M-series? */ goto MPY; /* decode as MPY */ BR = (BR + 1) & DMASK; /* increment B */ if (BR) PC = err_PC; /* if !=0, repeat */ break; case 004: /* RRL 100100-100117 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ t = BR; /* BR'AR rot left */ BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; AR = ((AR << sc) | (t >> (16 - sc))) & DMASK; break; case 010: /* MPY 100200 (OP_K) */ MPY: if (reason = cpu_ops (OP_K, op, intrq)) /* get operand */ break; sop1 = SEXT (AR); /* sext AR */ sop2 = SEXT (op[0].word); /* sext mem */ sop1 = sop1 * sop2; /* signed mpy */ BR = (sop1 >> 16) & DMASK; /* to BR'AR */ AR = sop1 & DMASK; O = 0; /* no overflow */ break; default: /* others undefined */ return stop_inst; } break; case 0201: /* DIV 100400 (OP_K) */ if (reason = cpu_ops (OP_K, op, intrq)) /* get operand */ break; if (rs = qs = BR & SIGN) { /* save divd sign, neg? */ AR = (~AR + 1) & DMASK; /* make B'A pos */ BR = (~BR + (AR == 0)) & DMASK; /* make divd pos */ } v2 = op[0].word; /* divr = mem */ if (v2 & SIGN) { /* neg? */ v2 = (~v2 + 1) & DMASK; /* make divr pos */ qs = qs ^ SIGN; /* sign of quotient */ } if (BR >= v2) O = 1; /* divide work? */ else { /* maybe... */ O = 0; /* assume ok */ v1 = (BR << 16) | AR; /* 32b divd */ AR = (v1 / v2) & DMASK; /* quotient */ BR = (v1 % v2) & DMASK; /* remainder */ if (AR) { /* quotient > 0? */ if (qs) AR = (~AR + 1) & DMASK; /* apply quo sign */ if ((AR ^ qs) & SIGN) O = 1; /* still wrong? ovflo */ } if (rs) BR = (~BR + 1) & DMASK; /* apply rem sign */ } break; case 0202: /* EAU group 2 */ switch ((IR >> 4) & 017) { /* decode IR<7:4> */ case 001: /* ASR 101020-101037 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; BR = (SEXT (BR) >> sc) & DMASK; /* BR'AR ash right */ O = 0; break; case 002: /* LSR 101040-101057 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; BR = BR >> sc; /* BR'AR log right */ break; case 004: /* RRR 101100-101117 */ sc = (IR & 017)? (IR & 017): 16; /* get sc */ t = AR; /* BR'AR rot right */ AR = ((AR >> sc) | (BR << (16 - sc))) & DMASK; BR = ((BR >> sc) | (t << (16 - sc))) & DMASK; break; default: /* others undefined */ return stop_inst; } break; case 0210: /* DLD 104200 (OP_D) */ if (reason = cpu_ops (OP_D, op, intrq)) /* get operand */ break; AR = (op[0].dword >> 16) & DMASK; /* load AR */ BR = op[0].dword & DMASK; /* load BR */ break; case 0211: /* DST 104400 (OP_A) */ if (reason = cpu_ops (OP_A, op, intrq)) /* get operand */ break; WriteW (op[0].word, AR); /* store AR */ WriteW ((op[0].word + 1) & VAMASK, BR); /* store BR */ break; default: /* should never get here */ return SCPE_IERR; /* bad call from cpu_instr */ } return reason; } /* UIG 0 The first User Instruction Group (UIG) encodes firmware options for the 2100 and 1000. Instruction codes 105000-105377 are assigned to microcode options as follows: Instructions Option Name 2100 1000-M 1000-E 1000-F ------------- -------------------------- ------ ------ ------ ------ 105000-105362 2000 I/O Processor opt - - - 105000-105137 Floating Point opt std std std 105200-105237 Fast FORTRAN Processor opt opt opt std 105240-105257 RTE-IVA/B Extended Memory - - opt opt 105240-105257 RTE-6/VM Virtual Memory - - opt opt 105300-105317 Distributed System - - opt opt 105320-105337 Double Integer - - opt - 105320-105337 Scientific Instruction Set - - - std 105340-105357 RTE-6/VM Operating System - - opt opt If the 2100 IOP is installed, the only valid UIG instructions are IOP instructions, as the IOP used the full 2100 microcode addressing space. The IOP dispatcher remaps the 2100 codes to 1000 codes for execution. The F-Series moved the three-word extended real instructions from the FFP range to the base floating-point range and added four-word double real and two-word double integer instructions. The double integer instructions occupied some of the vacated extended real instruction codes in the FFP, with the rest assigned to the floating-point range. Consequently, many instruction codes for the F-Series are different from the E-Series. Implementation notes: 1. Product 93585A, available from the "Specials" group, added double integer microcode to the E-Series. The instruction codes were different from those in the F-Series to avoid conflicting with the E-Series FFP. 2. To run the double-integer instructions diagnostic in the absence of 64-bit integer support (and therefore of F-Series simulation), a special DBI dispatcher may be enabled by defining ENABLE_DIAG during compilation. This dispatcher will remap the F-Series DBI instructions to the E-Series codes, so that the F-Series diagnostic may be run. Because several of the F-Series DBI instruction codes replace M/E-Series FFP codes, this dispatcher will only operate if FFP is disabled. Note that enabling the dispatcher will produce non-standard FP behavior. For example, any code in the range 105000-105017 normally would execute a FAD instruction. With the dispatcher enabled, 105014 would execute a .DAD, while the other codes would execute a FAD. Therefore, ENABLE_DIAG should only be used to run the diagnostic and is not intended for general use. 3. Any instruction not claimed by an installed option will be sent to the user microcode dispatcher. */ t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap) { if ((cpu_unit.flags & UNIT_IOP) && /* I/O Processor? */ (UNIT_CPU_TYPE == UNIT_TYPE_2100)) /* and 2100 CPU? */ return cpu_iop (IR, intrq); /* dispatch to IOP */ #if !defined (HAVE_INT64) && defined (ENABLE_DIAG) /* special DBI diagnostic dispatcher */ if (((cpu_unit.flags & UNIT_FFP) == 0) && /* FFP absent? */ (cpu_unit.flags & UNIT_DBI)) /* and DBI present? */ switch (IR & 0377) { case 0014: /* .DAD 105014 */ return cpu_dbi (0105321, intrq); case 0034: /* .DSB 105034 */ return cpu_dbi (0105327, intrq); case 0054: /* .DMP 105054 */ return cpu_dbi (0105322, intrq); case 0074: /* .DDI 105074 */ return cpu_dbi (0105325, intrq); case 0114: /* .DSBR 105114 */ return cpu_dbi (0105334, intrq); case 0134: /* .DDIR 105134 */ return cpu_dbi (0105326, intrq); case 0203: /* .DNG 105203 */ return cpu_dbi (0105323, intrq); case 0204: /* .DCO 105204 */ return cpu_dbi (0105324, intrq); case 0210: /* .DIN 105210 */ return cpu_dbi (0105330, intrq); case 0211: /* .DDE 105211 */ return cpu_dbi (0105331, intrq); case 0212: /* .DIS 105212 */ return cpu_dbi (0105332, intrq); case 0213: /* .DDS 105213 */ return cpu_dbi (0105333, intrq); } /* otherwise, continue */ #endif /* end of special DBI dispatcher */ switch ((IR >> 4) & 017) { /* decode IR<7:4> */ case 000: /* 105000-105017 */ case 001: /* 105020-105037 */ case 002: /* 105040-105057 */ case 003: /* 105060-105077 */ case 004: /* 105100-105117 */ case 005: /* 105120-105137 */ if (cpu_unit.flags & UNIT_FP) /* FP option installed? */ #if defined (HAVE_INT64) /* int64 support available */ return cpu_fpp (IR, intrq); /* Floating Point Processor */ #else /* int64 support unavailable */ return cpu_fp (IR, intrq); /* Firmware Floating Point */ #endif /* end of int64 support */ else break; case 010: /* 105200-105217 */ case 011: /* 105220-105237 */ if (cpu_unit.flags & UNIT_FFP) /* FFP option installed? */ return cpu_ffp (IR, intrq); /* Fast FORTRAN Processor */ else break; case 012: /* 105240-105257 */ if (cpu_unit.flags & UNIT_VMAOS) /* VMA/OS option installed? */ return cpu_rte_vma (IR, intrq); /* RTE-6 VMA */ else if (cpu_unit.flags & UNIT_EMA) /* EMA option installed? */ return cpu_rte_ema (IR, intrq); /* RTE-4 EMA */ else break; case 014: /* 105300-105317 */ if (cpu_unit.flags & UNIT_DS) /* DS option installed? */ return cpu_ds (IR, intrq); /* Distributed System */ else break; case 015: /* 105320-105337 */ #if defined (HAVE_INT64) /* int64 support available */ if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-series? */ return cpu_sis (IR, intrq); /* Scientific Instruction is standard */ else /* M/E-series */ #endif /* end of int64 support */ if (cpu_unit.flags & UNIT_DBI) /* DBI option installed? */ return cpu_dbi (IR, intrq); /* Double integer */ else break; case 016: /* 105340-105357 */ if (cpu_unit.flags & UNIT_VMAOS) /* VMA/OS option installed? */ return cpu_rte_os (IR, intrq, iotrap); /* RTE-6 OS */ else break; } return cpu_user (IR, intrq); /* try user microcode */ } /* UIG 1 The second User Instruction Group (UIG) encodes firmware options for the 1000. Instruction codes 101400-101777 and 105400-105777 are assigned to microcode options as follows ("x" is "1" or "5" below): Instructions Option Name 1000-M 1000-E 1000-F ------------- ---------------------------- ------ ------ ------ 10x400-10x437 2000 IOP opt opt opt 10x460-10x477 2000 IOP opt opt opt 10x460-10x477 Vector Instruction Set - - opt 10x520-10x537 Distributed System opt - - 10x600-10x617 SIGNAL/1000 Instruction Set - - opt 10x700-10x737 Dynamic Mapping System opt opt std 10x740-10x777 Extended Instruction Group std std std Only 1000 systems execute these instructions. Implementation notes: 1. The Distributed System (DS) microcode was mapped to different instruction ranges for the M-Series and the E/F-Series. The sequence of instructions was identical, though, so we remap the former range to the latter before dispatching. 2. Any instruction not claimed by an installed option will be sent to the user microcode dispatcher. */ t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap) { if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* 1000 execution? */ return stop_inst; /* no, so trap */ switch ((IR >> 4) & 017) { /* decode IR<7:4> */ case 000: /* 105400-105417 */ case 001: /* 105420-105437 */ if (cpu_unit.flags & UNIT_IOP) /* IOP option installed? */ return cpu_iop (IR, intrq); /* 2000 I/O Processor */ else break; case 003: /* 105460-105477 */ #if defined (HAVE_INT64) /* int64 support available */ if (cpu_unit.flags & UNIT_VIS) /* VIS option installed? */ return cpu_vis (IR, intrq); /* Vector Instruction Set */ else #endif /* end of int64 support */ if (cpu_unit.flags & UNIT_IOP) /* IOP option installed? */ return cpu_iop (IR, intrq); /* 2000 I/O Processor */ else break; case 005: /* 105520-105537 */ if (cpu_unit.flags & UNIT_DS) { /* DS option installed? */ IR = IR ^ 0000620; /* remap to 105300-105317 */ return cpu_ds (IR, intrq); /* Distributed System */ } else break; #if defined (HAVE_INT64) /* int64 support available */ case 010: /* 105600-105617 */ if (cpu_unit.flags & UNIT_SIGNAL) /* SIGNAL option installed? */ return cpu_signal (IR, intrq); /* SIGNAL/1000 Instructions */ else break; #endif /* end of int64 support */ case 014: /* 105700-105717 */ case 015: /* 105720-105737 */ if (cpu_unit.flags & UNIT_DMS) /* DMS option installed? */ return cpu_dms (IR, intrq); /* Dynamic Mapping System */ else break; case 016: /* 105740-105757 */ case 017: /* 105760-105777 */ return cpu_eig (IR, intrq); /* Extended Instruction Group */ } return cpu_user (IR, intrq); /* try user microcode */ } /* Read a multiple-precision operand value. */ OP ReadOp (uint32 va, OPSIZE precision) { OP operand; uint32 i; if (precision == in_s) operand.word = ReadW (va); /* read single integer */ else if (precision == in_d) operand.dword = ReadW (va) << 16 | /* read double integer */ ReadW ((va + 1) & VAMASK); /* merge high and low words */ else for (i = 0; i < (uint32) precision; i++) { /* read fp 2 to 5 words */ operand.fpk[i] = ReadW (va); va = (va + 1) & VAMASK; } return operand; } /* Write a multiple-precision operand value. */ void WriteOp (uint32 va, OP operand, OPSIZE precision) { uint32 i; if (precision == in_s) WriteW (va, operand.word); /* write single integer */ else if (precision == in_d) { WriteW (va, (operand.dword >> 16) & DMASK); /* write double integer */ WriteW ((va + 1) & VAMASK, operand.dword & DMASK); /* high word, then low word */ } else for (i = 0; i < (uint32) precision; i++) { /* write fp 2 to 5 words */ WriteW (va, operand.fpk[i]); va = (va + 1) & VAMASK; } return; } /* Get instruction operands. Operands for a given instruction are specifed by an "operand pattern" consisting of flags indicating the types and storage methods. The pattern directs how each operand is to be retrieved and whether the operand value or address is returned in the operand array. Typically, a microcode simulation handler will define an OP_PAT array, with each element containing an operand pattern corresponding to the simulated instruction. Operand patterns are defined in the header file accompanying this source file. After calling this function with the appropriate operand pattern and a pointer to an array of OPs, operands are decoded and stored sequentially in the array. The following operand encodings are defined: Code Operand Description Example Return ------ ---------------------------------------- ----------- ------------ OP_NUL No operand present [inst] None OP_IAR Integer constant in A register LDA I Value of I [inst] ... I DEC 0 OP_DAB Double integer constant in A/B registers DLD J Value of J [inst] ... J DEC 0,0 OP_FAB 2-word FP constant in A/B registers DLD F Value of F [inst] ... F DEC 0.0 OP_CON Inline 1-word constant [inst] Value of C C DEC 0 ... OP_VAR Inline 1-word variable [inst] Address of V V BSS 1 ... OP_ADR Inline address [inst] Address of A DEF A ... A EQU * OP_ADK Address of integer constant [inst] Value of K DEF K ... K DEC 0 OP_ADD Address of double integer constant [inst] Value of D DEF D ... D DEC 0,0 OP_ADF Address of 2-word FP constant [inst] Value of F DEF F ... F DEC 0.0 OP_ADX Address of 3-word FP constant [inst] Value of X DEF X ... X DEX 0.0 OP_ADT Address of 4-word FP constant [inst] Value of T DEF T ... T DEY 0.0 OP_ADE Address of 5-word FP constant [inst] Value of E DEF E ... E DEC 0,0,0,0,0 Address operands, i.e., those having a DEF to the operand, will be resolved to direct addresses. If an interrupt is pending and more than three levels of indirection are used, the routine returns without completing operand retrieval (the instruction will be retried after interrupt servicing). Addresses are always resolved in the current DMS map. An operand pattern consists of one or more operand encodings, corresponding to the operands required by a given instruction. Values are returned in sequence to the operand array. */ t_stat cpu_ops (OP_PAT pattern, OPS op, uint32 irq) { t_stat reason = SCPE_OK; OP_PAT flags; uint32 i, MA; for (i = 0; i < OP_N_F; i++) { flags = pattern & OP_M_FLAGS; /* get operand pattern */ if (flags >= OP_ADR) /* address operand? */ if (reason = resolve (ReadW (PC), &MA, irq)) /* resolve indirects */ return reason; switch (flags) { case OP_NUL: /* null operand */ return reason; /* no more, so quit */ case OP_IAR: /* int in A */ (*op++).word = AR; /* get one-word value */ break; case OP_JAB: /* dbl-int in A/B */ (*op++).dword = (AR << 16) | BR; /* get two-word value */ break; case OP_FAB: /* 2-word FP in A/B */ (*op).fpk[0] = AR; /* get high FP word */ (*op++).fpk[1] = BR; /* get low FP word */ break; case OP_CON: /* inline constant operand */ *op++ = ReadOp (PC, in_s); /* get value */ break; case OP_VAR: /* inline variable operand */ (*op++).word = PC; /* get pointer to variable */ break; case OP_ADR: /* inline address operand */ (*op++).word = MA; /* get address */ break; case OP_ADK: /* address of int constant */ *op++ = ReadOp (MA, in_s); /* get value */ break; case OP_ADD: /* address of dbl-int constant */ *op++ = ReadOp (MA, in_d); /* get value */ break; case OP_ADF: /* address of 2-word FP const */ *op++ = ReadOp (MA, fp_f); /* get value */ break; case OP_ADX: /* address of 3-word FP const */ *op++ = ReadOp (MA, fp_x); /* get value */ break; case OP_ADT: /* address of 4-word FP const */ *op++ = ReadOp (MA, fp_t); /* get value */ break; case OP_ADE: /* address of 5-word FP const */ *op++ = ReadOp (MA, fp_e); /* get value */ break; default: return SCPE_IERR; /* not implemented */ } if (flags >= OP_CON) /* operand after instruction? */ PC = (PC + 1) & VAMASK; /* yes, so bump to next */ pattern = pattern >> OP_N_FLAGS; /* move next pattern into place */ } return reason; } /* Print operands to the debug device. The values of an operand array are printed to the debug device. The types of the operands are specified by an operand pattern. Typically, the operand pattern is the same one that was used to fill the array originally. */ void fprint_ops (OP_PAT pattern, OPS op) { OP_PAT flags; uint32 i; for (i = 0; i < OP_N_F; i++) { flags = pattern & OP_M_FLAGS; /* get operand pattern */ switch (flags) { case OP_NUL: /* null operand */ return; /* no more, so quit */ case OP_IAR: /* int in A */ case OP_CON: /* inline constant operand */ case OP_VAR: /* inline variable operand */ case OP_ADR: /* inline address operand */ case OP_ADK: /* address of int constant */ fprintf (sim_deb, ", op[%d] = %06o", i, op[i].word); break; case OP_JAB: /* dbl-int in A/B */ case OP_ADD: /* address of dbl-int constant */ fprintf (sim_deb, ", op[%d] = %011o", i, op[i].dword); break; case OP_FAB: /* 2-word FP in A/B */ case OP_ADF: /* address of 2-word FP const */ fprintf (sim_deb, ", op[%d] = (%06o, %06o)", i, op[i].fpk[0], op[i].fpk[1]); break; case OP_ADX: /* address of 3-word FP const */ fprintf (sim_deb, ", op[%d] = (%06o, %06o, %06o)", i, op[i].fpk[0], op[i].fpk[1], op[i].fpk[2]); break; case OP_ADT: /* address of 4-word FP const */ fprintf (sim_deb, ", op[%d] = (%06o, %06o, %06o, %06o)", i, op[i].fpk[0], op[i].fpk[1], op[i].fpk[2], op[i].fpk[3]); break; case OP_ADE: /* address of 5-word FP const */ fprintf (sim_deb, ", op[%d] = (%06o, %06o, %06o, %06o, %06o)", i, op[i].fpk[0], op[i].fpk[1], op[i].fpk[2], op[i].fpk[3], op[i].fpk[4]); break; default: fprintf (sim_deb, "UNKNOWN OPERAND TYPE"); /* not implemented */ } pattern = pattern >> OP_N_FLAGS; /* move next pattern into place */ } } /* Print CPU registers to the debug device. One or more CPU registers may be printed to the debug output device, which must be valid before calling. */ void fprint_regs (char *caption, uint32 regs, uint32 base) { static uint32 ARX, BRX, PRL; /* static so addresses are constant */ static const char *reg_names[] = { "CIR", "A", "B", "E", "X", "Y", "O", "P", "return" }; static const uint32 *reg_ptrs[] = { &intaddr, &ARX, &BRX, &E, &XR, &YR, &O, &PC, &PRL }; static const char *formats[] = { "%02o", "%06o", "%06o", "%01o", "%06o", "%06o", "%01o", "%06o", "P+%d" }; static char format[20] = " %s = "; /* base format string */ static const int eos = 6; /* length of base format string */ uint32 i; t_bool first = TRUE; /* first-time through flag */ ARX = AR; /* copy 16-bit value to static variable */ BRX = BR; /* copy 16-bit value to static variable */ PRL = PC - base; /* compute value in static variable */ for (i = 0; i < REG_COUNT; i++) { if (regs & 1) { /* register requested? */ if (first) /* first time? */ fputs (caption, sim_deb); /* print caption */ else fputc (',', sim_deb); /* print separator */ strcpy (&format[eos], formats[i]); /* copy format specifier */ fprintf (sim_deb, format, reg_names[i], *reg_ptrs[i]); first = FALSE; } regs = regs >> 1; /* align next register flag */ } return; } simh-3.8.1/HP2100/hp2100_bugfixes.txt0000644000175000017500000065555311113066034015040 0ustar vlmvlm HP 2100 SIMULATOR BUG FIX WRITEUPS ================================== Last update: 2008-09-30 1. PROBLEM: Booting from magnetic tape reports "HALT instruction, P: 77756 (000400)". However, [77755] is HLT 77 (102077), [77756] is ALF,ALF (001727). VERSION: 3.2-0 OBSERVATION: The value 000400 is supposed to be "ALF,ALF", i.e., the decoded memory value at P. CAUSE: "fprint_stopped" in "scp.c" calls "cpu_ex" in "hp2100_cpu.c", which calls "dms_cons" to display the virtual (logical) address. However, at the halt in the mag tape boot loader, DMS is not enabled, so the map registers are meaningless (they happen to be zeros, so the access is to physical address 001756). RESOLUTION: Alter "dms_cons" in "hp2100_cpu.c" to condition DMS mapping on "dms_enb". STATUS: Fixed in version 3.2-1. 2. PROBLEM: Terminal output from RTE is indented three spaces, e.g., "START RECONFIGURATION" appears as " START RECONFIGURATION", and the "- " prompts after answering "YES" to "I/O RECONFIGURATION?" do not appear. VERSION: 3.2-0 OBSERVATION: Use of a debugger reveals that the output sequence to the TTY is - . CAUSE: RTE is outputting nulls to allow the (physical) TTY carriage time to move, but these are overwriting the prompt character in the simulation (note that a real TTY absorbs nulls, i.e., they don't affect the printed output). The TTY emulator should strip nulls from output to console. RESOLUTION: Alter "tto_out" in "hp2100_stddev.c" to suppress nulls sent to the TTY printer. STATUS: Fixed in version 3.2-1. 3. PROBLEM: Completing the reconfiguration and exiting hangs the system after printing the first few characters of the output line. RTE is stuck in a loop in $CIC. VERSION: 3.2-0 OBSERVATION: At the entry to $CIC (system map address 43221), RTE uses the undocumented instruction "SFS 0,C" to both test and turn off the interrupt system. This is confirmed in the "RTE-6/VM Technical Specifications" manual (HP 92084-90015), section 2.3.1 "Process the Interrupt", subsection "A.1 $CIC": "Test to see if the interrupt system is on or off. This is done with the SFS 0,C instruction. In either case, turn it off (the ,C does it)." ...and in section 5.8, "Parity Error Detection": "Because parity error interrupts can occur even when the interrupt system is off, the code at $CIC must be able to save the complete system status. The major hole in being able to save the complete state is in saving the interrupt system state. In order to do this in both the 21MX and the 21XE the instruction 103300 was used to both test the interrupt system and turn it off." CAUSE: The simulator does not respond to the "H/C bit" on the "SFS 0" instruction, so the interrupt system is not turned off. RESOLUTION: Modify "hp2100_cpu.c" and the various devices to respond to the "H/C bit" on the "SFS" and "SFC" instructions, and modify "hp2100_sys.c" to decode the "H/C bit" on those instructions (note that while the documentation refers specifically only to "SFS 0", the schematics of the 21MX appear to indicate that the bit will work on any SFS instruction -- and the SFC instruction too, for that matter). STATUS: Fixed in version 3.2-1. 4. PROBLEM: RTE sits in the idle loop. TBG/TTY interrupts are not occurring, so "SET TIME" is not output. VERSION: 3.2-0 OBSERVATION: The memory protect flag is set, inhibiting all lower-priority interrupts, such as the TBG and TTY. If the MP flag is cleared manually, RTE prints "SET TIME" and comes up sufficiently to respond to operator attention commands. The system time is seen to increment properly. Unlike most other I/O devices, the MP flag flip-flop is cleared automatically when the interrupt is acknowledged and not by a programmed instruction (CLF and STF affect the parity error enable FF instead). Section 4.4.3 "Memory Protect and I/O Interrupt Generation" of the "HP 1000 M/E/F-Series Computers Engineering and Reference Documentation" (HP 92851-90001) says: "When IAK occurs and IRQ5 is asserted, the FLAGBFF is cleared, FLAGFF clocked off at next T2, and IRQ5 will no longer occur." CAUSE: The MP flag flip-flop is not being cleared automatically when the interrupt is acknowledged. RESOLUTION: Modify "hp2100_cpu.c" to reset the MP flag on IAK5. STATUS: Fixed in version 3.2-1. OBSERVATION: The MEV flag indicates the source of the interrupt (set for DMS violation, clear for MP violation). If this is tested with a SFS or SFC instruction after an MP interrupt, it is observed that DMS interrupts are not being indicated properly. SFS 05 never skips. CAUSE: The MP flag is being used to condition the response for SFS 05 and SFC 05. Examination of the schematics for the MP card in the engineering documentation shows that the SFS and SFC I/O backplane signals gate the output of the MEVFF onto the SKF line unconditionally. RESOLUTION: Modify "hp2100_cpu.c" to remove conditioning on MP flag for SFS 05, SFC 05. STATUS: Fixed in version 3.2-1. 5. PROBLEM: Attempting to run any program causes a DM violation. VERSION: 3.2-0 OBSERVATION: BCKOF is scheduled when the system starts and is the first program to DM abort. Examining the DMS maps seems to indicate that the user and system maps are set up reasonably. However, examining memory with the "ex -u" and "ex -s" commands reveals the same data in both sets of locations. The "ex" command isn't using the DMS maps properly. CAUSE: String constants are used instead of character constants, preventing the DMS map switches from being recognized. RESOLUTION: Modify "hp2100_cpu.c" to use character constants rather than string constants in "dms_cons" so that DMS map switches work correctly. STATUS: Fixed in version 3.2-1. OBSERVATION: The DM abort is occurring when JSB EXEC is done from a user program. The EXEC target is below the MP fence, and the expected action is an MP violation interrupt that is recognized and processed by the system as a legal call to the system executive. However, the MP violation isn't occurring, so the SJP instruction at the actual EXEC entry point (present to catch EXEC calls made with the interrupt system off) is attempted, and that causes the DM violation, due to execution of a protected instruction from the user map. CAUSE: Memory writes aren't being checked for an MP violation if DMS is enabled, i.e., if DMS is enabled, and the page is writable, then whether the target is below the MP fence is not checked. RESOLUTION: Modify "hp2100_cpu.c" to do MP check on all writes after DMS translation and violation checks are done (so, to pass, the page must be writable AND the target must be above the MP fence). STATUS: Fixed in version 3.2-1. 6. PROBLEM: The "WHZAT" program isn't showing the current time, program type, priority, etc. VERSION: 3.2-0 OBSERVATION: Running the program with "RU,WHZAT" shows that the current time (etc.) is simply missing, as though zero-length strings are being written, or all characters in the string are being written to the same location. CAUSE: The SBT instruction isn't incrementing the B register, so all characters are being overwritten. RESOLUTION: Modify the processing of the SBT instruction in "hp2100_cpu.c" to increment B. STATUS: Fixed in version 3.2-1. 7. PROBLEM: The simulator may abort with an access exception when examining memory. VERSION: 3.2-0 OBSERVATION: If DMS is enabled but a map register contains a page greater than defined memory, attempting to examine the logical address corresponding to that page register causes an access exception. CAUSE: The "cpu_ex" and "cpu_dep" routines attempt to prevent this, but the validation is being made on the logical, not the physical address. RESOLUTION: Modify "cpu_ex" and "cpu_dep" in "hp2100_cpu.c" to check the physical addresses against the physical memory limit. STATUS: Fixed in version 3.2-1. 8. PROBLEM: Pressing a key during output does not give an RTE prompt. VERSION: 3.2-0 OBSERVATION: Running, e.g., WHZAT and pressing a key during the listing does not interrupt the system as expected. Pressing a key when the system is idle does give a prompt. CAUSE: Detection of key presses during output is accomplished by DVR00 with the 12531C card by reading the data register after output is complete. If no key was pressed, the register will have the value of 377 octal. If a key was pressed, the value will be something other than this. SIMH is not passing the keystrokes into the output data register. RESOLUTION: Modify tty routines in "hp2100_stddev.c" to simulate the shift of a character into the data register concurrently with an output operation. STATUS: Fixed in version 3.2-1. 9. ENHANCEMENT: Programmed halt should report the halt code (i.e., the numeric HLT instruction). VERSION: 3.2-0 OBSERVATION: When a programmed halt occurs on the actual 21MX, the T register (current memory contents) is automatically selected on the CPU front panel. The T register displays the numeric HLT instruction. Many HP programs communicate program status via the numeric halt instruction codes themselves. For example, a HLT 77 (102077) is universally used to mean "proper operation completed." The mag tape boot loader, for instance, will HLT 11 (102011) if a checksum error is detected and HLT 00 (102000) if the mag tape status is anything unexpected. The HP diagnostics also make extensive use of halt codes, and their numeric values are tabulated in the diagnostic manuals to correspond with certain results. Currently, the simulator displays only the P register value (which points to HLT + 1) and the contents of the memory location at P (which displays the instruction one beyond the HLT), e.g.: HALT instruction, P: 77756 (ALF,ALF) sim> This, however, fails to communicate the status implied by the HLT code, which must be obtained by entering "ex 77755" after the halt. RESOLUTION: Modify "hp2100_sys.c" to make the halt status message a variable instead of a constant string, and modify "hp2100_cpu.c" to format the status message with the halt code, as follows: HALT instruction 102077, P: 77756 (ALF,ALF) sim> STATUS: Fixed in version 3.2-1. 10. ENHANCEMENT: Add an M register (current pointer to memory) and a T register (contents of the memory location at P). VERSION: 3.2-0 OBSERVATION: The 21MX computer presents eight hardware registers: A, B, M, T, P, S, O, and E. From the 21MX M-Series Computer Operating and Reference Manual (02108-90037): "The M-register hold the address of the memory cell currently being read from or written into by the CPU. "The data transferred into or out of memory is routed through the T-register. When displayed, the T-register indicates the contents of the memory location currently pointed to by the M-register. The A- or B-register contents are displayed if the M-register contents are 000000 or 000001, respectively." However, the simulator does not expose these registers as part of the CPU state. Internally, they are not needed, but the simulation user would expect to be able to view and set their contents, so they should be implemented. When the machine halts, the front panel microroutines display the T register after initiating a read of memory via the M register. So T always reflects the contents of memory addressed by M. For machine halts due to the front panel HALT button being pressed or due to execution of a HLT instruction not in an interrupt trap cell, M is set to P-1. If, however, the machine halts due to the execution of a HLT instruction in an interrupt trap cell, M is set instead to the address of the trap cell (P still points to the next instruction to be executed). RESOLUTION: Modify "hp2100_cpu.c" to add M and T registers to the CPU state. T must be read-only, because there is no way to determine positively when a store into T has been done. STATUS: Fixed in version 3.2-1. 11. ENHANCEMENT: Change the DMS map register contents to display and enter in the format as documented by the HP hardware manual. VERSION: 3.2-0 OBSERVATION: The simulated DMS map registers are stored in an internal format that does not correspond to the real DMS hardware. Specifically, the physical page address is shifted left by ten bits, and the read- and write-protect bits are in bits 1-0 rather than 15-14. This is done for performance reasons and is reasonable and proper. However, this internal format is exposed as the external representation, which is unfamiliar to the simulation user. The user expects to be able to view and set the DMS map registers in the same format as the real machine. RESOLUTION: Modify "hp2100_cpu.c" and "hp2100_defs.h" to use the documented format. STATUS: Fixed in version 3.2-1. 12. ENHANCEMENT: Provide map-specific simulation breakpoints. VERSION: 3.2-0 OBSERVATION: When DMS is enabled, separate address spaces exist for system and user programs. In debugging, one may have to set breakpoints in either address space. Currently, breakpoints are map-agnostic, i.e., only the address needs to match. This leads to the potential for breaking when not intended and can be frustrating if, for example, the desired user-mode break location happens to correspond with the TBG handling code in the system. RESOLUTION: Modify "hp2100_cpu.c" to add map-specific breakpoints. STATUS: Fixed in version 3.2-1. 13. ENHANCEMENT: Rename the F register to MPFR. VERSION: 3.2-0 OBSERVATION: There is no F register defined in the 21MX register set. Its name is confusing to the new simulation user. Moreover, there is an MPVR (memory protect violation register), but the memory protect fence register appears to be missing. Renaming makes the exposed register set more consistent with HP nomenclature. RESOLUTION: Modify "hp2100_cpu.c" to change the name of the register from "F" to "MPFR", and move the location in the CPU state to precede the memory protect violation register "MPVR", so that these two MP-related values appear together. STATUS: Fixed in version 3.2-1. 14. ENHANCEMENT: Rename the IADDR register to CIR. VERSION: 3.2-0 OBSERVATION: The address of the currently interrupting device is contained in the Central Interrupt Register (CIR) in HP documentation. Renaming makes the exposed register set more consistent with HP nomenclature. RESOLUTION: Modify "hp2100_cpu.c" to change the name of the register from "IADDR" to "CIR". STATUS: Fixed in version 3.2-1. 15. PROBLEM: Under RTE, the backspace key deletes the entire line, rather than just the last character entered. VERSION: 3.2-0 OBSERVATION: Pressing the backspace key should delete the last character entered. Pressing the DEL key (CTRL+BACKSPACE) should delete the entire line. RTE is behaving as though DEL were being sent when the backspace key is pressed. CAUSE: The simulator is unconditionally translating backspace (CTRL+H) to DEL (CTRL+BACKSPACE), ostensibly for the convenience of some DEC operating system. STATUS: Fixed in version 3.2-1. 16. ENHANCEMENT: Provide a settable "auto linefeed" mode so that the TTY will follow each CR with LF (DSGEN and DOS itself require that lines end with CR and LF, contrary to RTE, which uses CR only). VERSION: 3.2-0 OBSERVATION: Always following ENTER with CTRL+J is awkward. An "AUTO LF" mode is desirable. RESOLUTION: Implement a "SET TTY AUTOLF" command to implement "auto linefeed" mode. STATUS: Fixed in version 3.2-1. 17. PROBLEM: Loading an absolute binary paper tape image with "BOOT PTR" causes the boot loader to hang. VERSION: 3.2-0 OBSERVATION: BOOT PTR looks for 12 feed frames (nulls) to signify EOT. A real paper tape would have feed frames punched after the file data for a trailer. CAUSE: At the end of the attached file, "ptr_svc" (hp2100_stddev.c) fails if STOP_IOE is set, otherwise silently does nothing. SIMH wrongly requires that the feed frames appear in the attached file, rather than supplying the feed frames from the PTR simulation. If they are not in the file, the simulation hangs, just as the real paper tape reader would do if the tape ran out. RESOLUTION: Alter "ptr_svc" (hp2100_stddev.c) to fail if STOP_IOE is set, or to supply feed frames upon encountering the end of the attached file. "SET PTR TRLLIM" sets the maximum number of feed frames to supply. Note that RTE needs at least 30 feed frames before signalling EOT. STATUS: Fixed in version 3.2-1. 18. PROBLEM: The 7900 boot loader fails to load any data from the disc into memory. VERSION: 3.2-0 OBSERVATION: The loader completes, but memory is untouched. CAUSE: There is a transcription error in the boot loader code. RESOLUTION: Alter "dp_rom" (hp2100_dp.c) to change the erroneous "OTA 6,C" to the correct "SFS 6,C". STATUS: Fixed in version 3.2-1. 19. PROBLEM: Using the DOS-III D2607 (DVR12) driver with the LPT (2607/12845) simulation results in garbled output. VERSION: 3.2-0 OBSERVATION: Doing an "ATTACH LPT 2607.printer" and then a ":JOB,FRED" in DOS results in the following: 00000000 0C 0A 4A 4F 20 52 44 20-32 4D 59 37 20 54 4D 3D ..JO RD 2MY7 TM= 00000010 30 33 4D 4E 20 32 34 53-43 2E 4A 61 46 56 46 7F 03MN 24SC.JaFVF. 00000020 7F 7F 7F 47 18 73 43 46-21 4D 09 1A 0B 31 1C 67 ...G.sCF!M...1.g 00000030 0A . ...instead of the expected: 00000000 4A 4F 42 20 46 52 45 44-20 20 31 32 2D 4D 41 59 JOB FRED 12-MAY 00000010 2D 37 35 20 20 54 49 4D-45 3D 31 30 35 33 20 4D -75 TIME=1053 M 00000020 49 4E 2E 20 33 32 2E 31-20 53 45 43 53 2E 0D 0A IN. 32.1 SECS... CAUSE: The intercharacter wait time is too long (1000 instructions). The driver waits a maximum of 300 instructions before exiting to wait for the interrupt. However, there is a bug in the driver that garbles output if the wait time expires. It never does when using a real printer, so the bug wasn't seen. Note that the interline time (100 instructions) is actually shorter than the intercharacter time! Also, the interline time appears to be set to the "serial output time," which bears no relation to the parallel line printer! RESOLUTION: Change the intercharacter time to 5 instructions and the interline time to 10,000 instructions in hp2100_lpt.c. STATUS: Fixed in version 3.2-1. 20. PROBLEM: Issuing a read on a magnetic tape for fewer words than are in the pending record (e.g., doing ":LI,8,B" when there are more than 128 words in the next record) results in a parity error ("IOPE L 8 E 8 S 0 22"). VERSION: 3.2-0 OBSERVATION: FMGR only reads the first 128 words of a record. Records longer than 256 bytes should be truncated when listing. Instead, timing errors (status 22) are reported. Records shorter than 128 words are listed properly. CAUSE: DMA termination before the end-of-record is not being handled properly by the 7970E simulator. The RTE driver sets up DMA control word 1 with the STC bit (bit 15) clear and the CLC bit (bit 13) set. The DMA transfer proceeds apace until DMA control word 3 (word count) goes to zero. At this point, the last cycle logic in "dma_cycle" (hp2100_cpu.c, lines 2477-2480) issues a CLC to the mag tape data channel. In "msdio" (hp2100_ms.c, lines 272-275), the command and control flags are cleared in response. The catch here is that the next I/O data transfer is still pending; it was set in "msc_svc" (hp2100_ms.c, lines 461-467) via "sim_activate", because there were still words in the buffer to transfer. When that time expires, "msc_svc" is called again, and because the data flag is still set (the CLC to the data channel issued by DMA to end the transfer occurred _instead_ of the CLF that is issued between data transfers), the parity error and timing overrun bits are set into the return status at line 462 of hp2100_ms.c. RESOLUTION: Alter "msc_svc" (hp2100_ms.c) to terminate a read if the control flip-flop is reset (by DMA termination). STATUS: Fixed in version 3.2-1. 21. PROBLEM: Switching pending line edit modes (under EDIT or EDITR) by entering the appropriate control codes (e.g., CTRL+I or CTRL+C) print graphic characters that disrupt the one-for-one alignment needed for editing. VERSION: 3.2-1 OBSERVATION: Output of control characters that normally do not print or cause observable actions (e.g., CR or BEL) should be suppressed, so that simulated behavior mimics the action of a real terminal. CAUSE: All characters except NULs are output by the TTY routine. RESOLUTION: Alter "tto_out" (hp2100_stddev.c) to suppress output for all control characters (characters < 40 octal), except for BEL, BS, LF, and CR. STATUS: Fixed in version 3.2-2. 22. PROBLEM: Doing an EDIT pending line character insert with CTRL+S doesn't work. VERSION: 3.2-1 OBSERVATION: CTRL+S is not passed through to the simulated program. Instead, pressing CTRL+S and typing simply absorbs the first character, and the editor stays in "replace" mode for the succeeding characters. CAUSE: The keyboard "peek" routine that checks for a pending input character does not operate in "raw" mode. The simulator calls "_kbhit" to determine if an input character is pending and "_getch" to retrieve that character. "_getch" calls the Windows routine "SetConsoleMode" to set the input mode to "raw" (i.e., no processing of the input characters). However, "_kbhit" does not, and so the CTRL+S is intercepted and processed by Windows. RESOLUTION: Modify "sim_ttrun" and "sim_ttcmd" (sim_console.c) to switch the console into and out of "raw" mode. This inhibits "_kbhit" from interpreting the input character stream. As an added benefit, CTRL+C is no longer interpreted as SIGINT, so all of the associated signal-handling code ("win_handler", etc.) may be removed. STATUS: Fixed in version 3.2-2. 23. PROBLEM: The documentation for the DMSMAP register set is wrong. VERSION: 3.2-1 OBSERVATION: "hp2100_doc.txt" says: CPU registers include the visible state of the processor as well as the control registers for the interrupt system. name models size comments DMSMAP[4][16] 21MX 16 DMS maps should be: DMSMAP[4][32] 21MX 16 DMS maps ...as there are 32 map registers (1 per 1K page) per set. RESOLUTION: Fix the text. STATUS: Fixed in version 3.2-2. 24. PROBLEM: The documentation for the 7900 disc boot is wrong. VERSION: 3.2-1 OBSERVATION: "hp2100_doc.txt" says: The 12557A/13210A supports the BOOT command. BOOT DPC copies the IBL for 7900 class disks into memory and starts it running. BOOT -R DP boots from the removable platter (head 2). Entering "boot -r dp" gives "Non-existent device." The correct command is "boot -r dpc". RESOLUTION: Fix the text. STATUS: Fixed in version 3.2-2. 25. PROBLEM: Logging console output to a file produces CR CR LF as line terminators. VERSION: 3.2-1 OBSERVATION: When console logging is enabled, simulator messages as well as the console output from the simulated system are written to the log file. The former outputs CR LF at the end of each line. The latter outputs CR CR LF. CAUSE: The log file is opened in "text mode" by default, which translates LFs (C newlines) to CR LF sequences. Simulator messages terminate with newlines, and these are translated to CR LF sequences. When the simulated system writes characters to the console, they are also written to the log file. When the simulated system outputs a CR, it is output verbatim. When it follows that with an LF, however, that gets translated into a CR LF, so the log file then has CR CR LF as the end of line sequence. RESOLUTION: Flush the accumulated file stream buffer and change the file mode from TEXT to BINARY in "sim_ttrun" (sim_console.c) when the simulation starts, and then back to TEXT in "sim_ttcmd" when the simulation ends. STATUS: Fixed in version 3.2-2. 26. ENHANCEMENT: For certain errors that stop the simulation, reset the PC to report the instruction causing the error, rather than reporting the next instruction. VERSION: 3.2-2 OBSERVATION: Some stops are triggered by the attempted execution of instructions. In these cases, it is more helpful to report the instruction causing the error than the next instruction. Currently, all stops report the instruction beyond the cause of the stop (i.e., "P + 1"). The table below indicates those stops where it would be more helpful to report the instruction causing the stop (i.e., "P"): PC Code Message Text ===== =========== ==================================== P STOP_RSRV Unimplemented instruction P STOP_IODV Non-existent I/O device P STOP_IND Indirect address loop P STOP_NOCONN No connection on interprocessor link RESOLUTION: Before exiting "sim_instr" (hp2100_cpu.c), reset "PC" to "err_PC" for the above cases. STATUS: Fixed in version 3.2-3. 27. ENHANCEMENT: Add an "echo" command to print arbitrary strings on the simulation console for use in simulation command files. VERSION: 3.2-2 OBSERVATION: Simulation command files allow automation of complex or tedious simulator setups. Because of the potentially lengthy sequence, it would be helpful if the command file had a way to inform the user where it was in the process. Providing a command to do this allows messages such as "Loading diagnostic," "Configuring diagnostic," etc., to be printed during command file execution. RESOLUTION: Implement an "echo " command (scp.c). STATUS: Fixed in version 3.2-3. 28. PROBLEM: Booting 2000E TSB hangs after printing "READY". VERSION: 3.2-2 OBSERVATION: The code is stuck in a loop, waiting for the 7900 disc data channel flag to set. CAUSE: To perform a disc read, the TSB disc driver issues a seek command but does not wait for seek completion before issuing the read command to the interface. This is allowed by the interface manual. The eventual interrupt signifies the completion of both the seek and the read commands. However, the "drive attention" flag that is normally generated at the end of the seek isn't set if the commands overlap in this manner. When a read command is received with a seek in progress, the simulator cancels the seek timer and establishes a read timer of a longer duration in its place. But the cancellation of the seek timer also cancels the setting of the "drive attention" bit that would have occurred had the seek completed normally, and the simulator doesn't supply it explicitly in this case. The HP "7900A Disc Drive Operating and Service Manual" (07900-90002) says, in section 4-67, "Attention is set high everytime a seek has been completed and Access Ready comes high." TSB code loads the "drive attention" word from the command channel to create a "request status" command. The code assumes that either bit 0 or bit 1 will be set, so an "ADA =D-1" is done to transform the retrieved 000001 or 000010 into 000000 or 000001, respectively. This effectively becomes a "request status for unit 0/1" command, which is output to the drive as a command. However, the simulator bug causes the drive attention word to be 0, so the ADA makes the value 177777. This is an illegal command, so the data channel flag never sets. RESOLUTION: Alter "dp_goc" (hp2100_dp.c) to set drive attention when seek completion is simulated. STATUS: Fixed in version 3.2-3. 29. PROBLEM: Running 2000/Access, the 7900 disc fails to format. VERSION: 3.2-2 OBSERVATION: The code is hung in a loop, waiting for a drive attention flag after the execution of an "Initialize Data" command. CAUSE: The 13210A disc interface passes through attention flags that the drives generate as a result of seek completions. However, the interface also generates its own drive attention flag at the conclusion of every command except "Status Check." This internally generated flag is not being provided by the 7900 simulator. The schematics and flowcharts in the 13210A manual indicate that a local attention bit, derived from the unit number in the last command, is provided at the conclusion of every command issued except: * "Status Check" -- executing this command clears the attention bit. * "Seek" -- if the drive is not ready, then a local attention bit is provided immediately, else the attention bit from the drive is provided when the seek completes. RESOLUTION: Alter (hp2100_dp.c) to provide the needed attention bits. STATUS: Fixed in version 3.2-3. 30. PROBLEM: Booting 2000/Access reports "CAN'T USE TAPE" when loading from 7970. VERSION: 3.2-2 OBSERVATION: No data is returned in response to reading the first tape record. CAUSE: Rewind at BOT should return immediately but is not. Access does not wait for rewind to complete, so it issues the read command while the transport is busy. The command is rejected, so Access tries a CLEAR and then a retry, but a bug in Access causes DMA to be started after the CLEAR is sent. When CLEAR completes, READ is attempted again, but DMA is not reset. Also, the simulator is processing rejected commands when STC CC,C follows a rejection. This should be a NOP. RESOLUTION: Change hp2100_ms.c to do immediate completion for REWIND at BOT and to NOP an STC CC,C after a command reject. Note that this "fixes" the problem as long as the tape is at load point when the Access bootstrap is run. This would normally be the case, but it appears as though Access wouldn't work if the tape had to be rewound! STATUS: Fixed in version 3.2-3. 31. PROBLEM: Running the 7970 diagnostic reports "Unit not attached, P: 02741 (CLF 77)" when executing Test 0. VERSION: 3.2-2 OBSERVATION: The error is occurring in the basic I/O test for the command channel. The test for the data channel is succeeding. CAUSE: The diagnostic does a STC CC as part of the I/O test. The last command sent was to the interface was SL3. Unit selects are not supposed to be executed, but examination of the card schematics reveals that this will set the command FF and the card busy bit and take no further action. The simulator, however, is scheduling an I/O event in response, and when the event occurs, unit 3 is not attached, so an error is reported. RESOLUTION: Modify "mscio" (hp2100_ms.c) to not schedule an I/O event if the last command was a unit select. STATUS: Fixed in version 3.2-3. 32. PROBLEM: Running the 7970 diagnostic reports "Magtape library I/O error: Invalid argument" when executing Test 4. VERSION: 3.2-2 OBSERVATION: The error occurs when a write is aborted with a clear command. CAUSE: If a CLR command is issued with a write in progress, the simulator attempts to mark the record as bad on the tape by adding the "MTR_ERF" flag to the "sim_tape_wrrecf" call. Unfortunately, that function does not remove the flag before calling "sim_fwrite", and so the eventual OS call sees the equivalent of a very large record length and therefore returns EINVAL. RESOLUTION: Modify "sim_tape_wrrecf" (sim_tape.c) to mask off the "MTR_ERF" flag when determining the record length. STATUS: Fixed in version 3.2-3. OBSERVATION: The library error is not stopping the simulator, even though the STOP_IOE variable is set. CAUSE: "sim_tape_ioerr" is returning "SCPE_IOERR" instead of "MTSE_IOERR", and "ms_map_err" maps this to "SCPE_OK", so the simulator isn't halted. RESOLUTION: Modify "sim_tape_ioerr" (sim_tape.c) to return "MTSE_IOERR" instead of "SCPE_IOERR". STATUS: Fixed in version 3.2-3. 33. PROBLEM: Running the 7970 diagnostic reports a number of timing errors, with events taking longer or shorter than expected. VERSION: 3.2-2 OBSERVATION: The diagnostic times certain tape functions (e.g., the time from issuing a WRITE command until the first data is requested). Most of these are reported as diagnostic failures. CAUSE: I/O time modelling is not done properly. In some cases, the times indicated are incorrect. In others, certain characteristics (e.g., that operations from BOT take longer, due to the initial gap) aren't modelled at all. RESOLUTION: Revise "mscio" and "msc_svc" (hp2100_ms.c) to model actual I/O timing characteristics correctly. STATUS: Fixed in version 3.2-3. 34. ENHANCEMENT: Provide a method of selecting between realistic and fast (optimized) command execution times for the 7970 simulator. VERSION: 3.2-2 OBSERVATION: The 7970 diagnostic checks command execution times, so to pass, the simulator must model these times. However, they are generally much longer than are required by the various operating systems. RESOLUTION: Modify "hp2100_ms.c" to add SET MSC REALTIME, SET MSC FASTTIME, and SHOW MSC TIMING commands. Timing is now set according to the timing and interface models in use. STATUS: Fixed in version 3.2-3. 35. ENHANCEMENT: Provide a means of printing the internal state of the 7970 tape simulator. VERSION: 3.2-2 OBSERVATION: Debugging tape errors would be easier if the tape interface commands and status were observable and recordable. SIMH provides a "DEBUG" mode command set to allow devices to provide this information. RESOLUTION: Modify "hp2100_ms.c" to add debug-mode calls. STATUS: Fixed in version 3.2-3. 36. PROBLEM: The 7970 tape diagnostic fails Test 12, Subtest 4. VERSION: 3.2-2 OBSERVATION: Test 12 forces data and timing errors. Execution reports: H154 UNIT 000000 H102 RECORD 000103 H054 COMMAND 000223 H155 STATUS IS 1 000 100 010 000 010 H155 AND SHOULD BE 1 000 000 010 010 010 TEST 12 E100 DATA OR ODD BYTE ERROR In test 12, step 3, a 100-word WRITE is interrupted after 64 words with a CLEAR command, followed by a WRITE FILE MARK. The diagnostic manual says, "This procedure creates a record with a known parity error." The simulator CLEAR command processing detects the write-in-progress and writes a simulated tape record with the MTR_ERF flag to indicate a bad record. In step 4, the records are backspaced, and a READ UNTIL FILE MARK command is issued without transferring any data. This should set the timing error bit (bit 4) in the status word. In the status word reported, it is not set. CAUSE: The simulator implementation of the CLEAR command erroneously clears the data channel command FF. When the READ UNTIL FILE MARK command is issued, no data transfer is attempted, so the timing error does not occur. RESOLUTION: Modify the CLR command in "mscio" (hp2100_ms.c) to leave the data channel control and flag FFs untouched. STATUS: Fixed in version 3.2-3. 37. PROBLEM: Running the RTE off-line disc backup program DBKUP and doing a save to tape with verify hangs after printing "VERIFYING." VERSION: 3.2-2 OBSERVATION: Using the 7970 debug mode reveals that the program does a rewind in preparation for verifying. Then, after the command completes but while the rewind is in progress, a read is issued. This is rejected due to REW + TBUSY being set (rewind still in progress). After rejection, a clear is issued and completes. At this point, the program appears to hang. CAUSE: The RTE tape driver retries rejected commands by clearing the interface and reissuing the originally rejected command. However, the simulator erroneously clears both command and data channel control FFs and sets both flag FFs in response to the CLR command. Clearing the control FFs means that no completion interrupt is generated as a result of the CLR, so the driver is never reentered to reissue the rejected command, and the program stays in the I/O suspend state forever. RESOLUTION: Modify the CLR command in "mscio" (hp2100_ms.c) to set both the command control and data FFs. STATUS: Fixed in version 3.2-3. 38. PROBLEM: The 13183A (7970) simulator reports "odd byte" status when an EOF is detected. VERSION: 3.2-2 OBSERVATION: For the NRZI (13181A) interface, an EOF is a single special character in the data stream, so odd byte status is set when it is detected. For the PE (13183A) interface, EOF is an erasure pattern that is detected by the drive itself and communicated to the interface as a status line. Odd byte status is not set when the 13183A interface indicates an EOF. CAUSE: Odd byte status on EOF is not conditional on interface type. RESOLUTION: Modify "ms_map_err" (hp2100_ms.c) to condition odd byte status with EOF on interface type. OBSERVATION: The FSF and BSF processors in "msc_svc" treat EOF separately from other tape errors, but the separate code takes precisely the same action as does the generic error mapper. RESOLUTION: Modify "msc_svc" (hp2100_ms.c) to remove the separate treatment and call the generic error mapper unconditionally. STATUS: Fixed in version 3.2-3. 39. PROBLEM: The 7970 simulator does not report "odd byte" status when a tape record with an odd byte length is read. VERSION: 3.2-2 OBSERVATION: A tape record containing an odd number of bytes is read, but the odd byte status bit isn't set at completion of the read. CAUSE: The RC and RFF processors in "msc_svc" are not testing for this condition. RESOLUTION: Modify "msc_svc" (hp2100_ms.c) to set the odd byte status bit if the last record read contained an odd number of bytes and to zero the unused byte (as specified on page 4-11 of the 13181B manual). STATUS: Fixed in version 3.2-3. 40. PROBLEM: The 7970 simulator fails Test 12, Subtest 2 when configured as a 13183A interface. VERSION: 3.2-2 OBSERVATION: The test issues a RFF command and waits for the first data flag. It then reads status in a loop and waits for the odd byte bit to set before continuing. If this bit doesn't set within 65K iterations, the test fails. CAUSE: The 13183A hardware passes the odd/even byte FF output through as the odd byte status bit, so this bit will be seen to toggle as data is received. The simulator, by contrast, sets the odd byte flag only at the end of the transfer. While the interface manual states that the odd byte status is only valid after the command flag FF sets, the diagnostic depends on seeing this bit toggle once during the transfer. The 13181A hardware enables the odd byte status bit only when the end-of-record is detected. However, because odd byte status occurs when EOF is detected, the diagnostic test will still succeed, albeit at the end of the RFF command rather than at the beginning. RESOLUTION: Modify "msc_svc" (hp2100_ms.c) to set the odd byte status bit at the beginning of the transfer if configured as a 13183A interface and then to set or clear it as appropriate at the end of the transfer. STATUS: Fixed in version 3.2-3. 41. ENHANCEMENT: Add a configurable reel length setting to the 7970 simulator and provide end-of-tape status returns. VERSION: 3.2-2 OBSERVATION: The 7970 diagnostic provides an option to inhibit rewinds during test loops to allow the EOT status to be tested. The simulated tape, however, is effectively infinite; EOT is never returned, as there is no predefined tape length. An option to provide a simulated end-of-tape indication would be helpful. RESOLUTION: Modify "hp2100_ms.c" to add SET MSCn REEL= and SHOW MSCn REEL and to return EOT status if motion beyond the defined tape length is attempted. Reel lengths may be set to 600, 1200, or 2400 (feet). Setting the length to 0 inhibits EOT, i.e., the reel length is effectively unlimited. Modify "mscio" to return EOT status when current tape position is beyond a calculated end-of-tape marker position (marker position is calculated as the ideal tape reel capacity, i.e., the number of bytes per inch times the length of the tape in inches). STATUS: Fixed in version 3.2-3. 42. PROBLEM: Running the RTE off-line disc backup program PSAVE and doing a save to a new tape gives an initial "IOPE" after specifying the tape label. VERSION: 3.2-2 OBSERVATION: Upping the driver causes the program to continue properly. Saving to a "used" tape does not exhibit this problem. CAUSE: The PSAVE program is attempting to read the new tape. The tape simulation library is reporting MTSE_EOM (end of medium), as the newly created tape image file is zero-length. This is translated to STA_PAR by "ms_map_err". In response, the RTE tape driver retries the read ten times and then gives up and reports the parity error. RESOLUTION: End-of-medium has no hardware analog; one cannot have a physical tape of zero length. So the translation to simulated tape status is arbitrary. A new physical tape will "run away," i.e., never return data. Some programs, e.g., the RTE tape driver, are written to detect this. However, those that aren't will simply hang. A more useful translation is to return EOF marks when a motion is attempted beyond the end of the medium, as many programs interpret two successive EOFs as "logical end-of-medium." Modify "ms_map_err" (hp2100_ms.c) to return EOF status for MSTE_EOM. STATUS: Fixed in version 3.2-3. 43. PROBLEM: EDIT/1000 uses the HT character (CTRL+I) to insert a tab, but printing of this character is suppressed. VERSION: 3.2-2 OBSERVATION: There is no visual indication that the TAB key was pressed to insert a HT character. CAUSE: "CNTL_SET" does not include the HT character. RESOLUTION: Modify "hp2100_stddev.c" to add "HT_FLAG", defined as "CHAR_FLAG('\t')", to "CNTL_SET". STATUS: Fixed in version 3.2-3. 44. PROBLEM: The 7900 disc diagnostic fails Step 55 if two or more units are connected. VERSION: 3.2-2 OBSERVATION: Altering the unit table at the start of the diagnostic to include two units (e.g., "0,1") and then running a short pass produces this output: H65 SHORT PASS 0004,HEADS 0/1,UNIT 00, 0000 ERRORS H44 SEEK IN STEP 55 E10 NO COMMAND FLAG H33 ATTENTION/SEEK-STATUS 000002 000000 H51 CYL 0202 HEAD 01 SECTOR 19 WORD COUNT 0128 UNIT 00 The step tests overlapping seeks. CAUSE: The command channel flag set that normally indicates seek command completion is not being deferred by the CLC CC issued in preparation for sending another command. The simulator must defer the flag set until a subsequent STATUS CHECK command is issued (this command normally does not set the command channel flag but will do so if a pending drive attention bit is set). RESOLUTION: Add a "poll attention" state to the simulator and set the command channel flag if polling is enabled and one or more drive attention bits are set. STATUS: Fixed in version 3.2-3. 45. PROBLEM: The 7900 disc diagnostic fails the not-ready tests in Step 14. VERSION: 3.2-2 OBSERVATION: Running the 7900 diagnostic with S bit 4 set to execute the interactive part of Section 1 causes this failure: H70 UNLOAD UNIT 0,PUSH RUN HALT instruction 102002, P: 03364 (JSB 1430) sim> detach dpc0 sim> go H46 READ IN STEP 14 E64 STATUS IS 000101 SHOULD BE 000105 H51 CYL 0202 HEAD 00 SECTOR 00 WORD COUNT 1024 UNIT 00 The diagnostic is expecting the DRIVE BUSY bit to be set. CAUSE: The "unit not attached" simulator state maps to the "drive not ready" hardware state. In this state, both the NOT READY and the DRIVE BUSY status bits should be set. Referring to the "Drive Control Assembly A9" schematic on page 5-43 of the "7900A Disc Drive Operating and Service Manual" (HP 07900-90002), the "Drive Ready" signal is forced low via U22B if the "Load Switch Off" signal is asserted (setting the "Load Switch" off unloads the heads). Also, the "Access Ready" signal is forced low via U35A if the "Drive Ready" signal is low. Schematic "Input/Output Multiplex Assembly A7" on page 5-39 shows that these signals are inverted and driven onto the cable to the CPU interface. The 13210A interface manual schematic for "Disc Interface PCA 1" shows that both signals are inverted twice and presented to the CPU as status bits 6 and 2, respectively. So "not Drive Ready" becomes NOT READY, and "not Access Ready" becomes DRIVE BUSY. RESOLUTION: Modify "dpd_svc" (hp2100_dp.c) to set both the NOT READY and DRIVE BUSY bits when the unit is not attached. STATUS: Fixed in version 3.2-3. 46. PROBLEM: The 7900 disc diagnostic loops forever in Step 15. VERSION: 3.2-2 OBSERVATION: Running the 7900 diagnostic with S bit 4 set to execute the interactive part of Section 1 causes this failure: H40 PROTECT U/D THEN READY UNIT 0 [CTRL+E] Simulation stopped, P: 76734 (TIMER) sim> set dpc0 locked sim> att dpc0 7900-U0.scratch.disc sim> go H40 PROTECT U/D THEN READY UNIT 0 H40 PROTECT U/D THEN READY UNIT 0 H40 PROTECT U/D THEN READY UNIT 0 The diagnostic is waiting for the CC flag to set when the drive becomes ready (i.e., when the unit is attached). CAUSE: Section 4-67 of the "7900A Disc Drive Operating and Service Manual" (HP 07900-90002) says, "Attention is set high everytime a seek has completed and Access Ready comes high." This includes the initial head-loading seek when the drive becomes ready. The "Troubleshooting Diagrams (Sheet 2 of 4)" on page 5-17 show that after the heads load, Drive Ready, First Status, Access Ready (a.k.a. not Busy), and Attention are asserted. The corresponding code in "dpc_attach" sets First Status but not Attention. In addition, the last diagnostic command prior to the loop is a STATUS CHECK. This leaves the 13210A interface polling for attention bits, and when one is asserted, the command channel flag FF is set. However, the simulator makes no provision for this; the flag is checked once at the end of the status command, but no further checks are made thereafter. RESOLUTION: Modify "dpc_attach" (hp2100_dp.c) to set the ATN flag when the unit is attached and, if drive polling is enabled, to set the command channel flag. STATUS: Fixed in version 3.2-3. 47. PROBLEM: The 7900 disc diagnostic fails the write-protect tests in Step 16. VERSION: 3.2-2 OBSERVATION: Running the 7900 diagnostic with S bit 4 set to execute the interactive part of Section 1 causes this failure: H40 PROTECT U/D THEN READY UNIT 0 [CTRL+E] Simulation stopped, P: 76734 (TIMER) sim> set dpc0 locked sim> attach dpc0 7900-U0.scratch.disc sim> go H44 SEEK IN STEP 16 E64 STATUS IS 040001 SHOULD BE 042001 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 The diagnostic is expecting the DATA PROTECT bit to be set. CAUSE: The UNIT_WPRT flag is being checked in the FNC_STA processing in "dpd_svc", but the referenced unit is the data channel unit, not the command channel unit where the flag is actually set. RESOLUTION: Alter "dpd_svc" (hp2100_dp.c) to check the command channel unit instead of the data channel unit when looking for write-protect indication. STATUS: Fixed in version 3.2-3. 48. PROBLEM: The 7970E diagnostic hangs in test 33 if the tape is not at BOT. VERSION: 3.2-2 OBSERVATION: The test issues a REWIND/OFFLINE to each unit in turn and looks for the REW status bit to deny before proceeding. CAUSE: The simulator resets this bit for the REWIND command but not for REWIND/OFFLINE. More generically, though, the simulator is reporting unit status (REW, BOT) when the unit is off-line. RESOLUTION: Modify "mscio" (hp2100_ms.c) to remove unit-specific status from the status return when the unit is not attached. STATUS: Fixed in version 3.2-3. OBSERVATION: The status for REWIND/OFFLINE when not at BOT isn't quite correct. The hardware indicates "Rewinding" (bit 10) for a short time before going off-line. CAUSE: The simulator is detaching (i.e., going off-line) immediately upon command execution. RESOLUTION: Modify "mscio" (hp2100_ms.c) to detach after the interface execution delay. STATUS: Fixed in version 3.2-3. OBSERVATION: The status for REWIND and REWIND/OFFLINE when at BOT isn't quite correct. The hardware does not indicate "Tape Unit Busy" (bit 9) if the unit is at BOT, because the tape never moves. CAUSE: The simulator generates "Tape Unit Busy" whenever a service event is scheduled, but this status should not occur if a rewind is issued at BOT. RESOLUTION: Modify "mscio" (hp2100_ms.c) to condition STA_TBSY on rewind at BOT. STATUS: Fixed in version 3.2-3. 49. PROBLEM: The "do" command does not obey the "-v" ("verbose") option switch when console logging is in effect. VERSION: 3.2-2 OBSERVATION: Command file commands are always written to the console log file, regardless of the setting of the "-v" switch. Commands are only displayed on the console when "-v" is specified. The console log file, therefore, is not a copy of what appeared on the console. CAUSE: Output of the file commands is not conditional on the "-v" switch. RESOLUTION: Modify "do_cmd" (scp.c) to condition writing file commands to the console log on the "-v" switch. STATUS: Fixed in version 3.2-3. 50. PROBLEM: The "echo" command does not echo to the console log file. VERSION: 3.2-2 OBSERVATION: The "echo" command writes its argument only to the console. If logging is in effect, the echoed strings will not appear in the file. CAUSE: This action was omitted. RESOLUTION: Modify "echo_cmd" (scp.c) to copy the echoed argument string to the console log file if logging is in effect. STATUS: Fixed in version 3.2-3. 51. PROBLEM: The diagnostic configurator mis-identifies the host CPU. VERSION: 3.2-3 OBSERVATION: Running the diagnostic configurator in conversational mode produces these hardware detection results using various CPU settings (note that STOP_INST must first be set to 0 to prevent unimplemented instruction traps): set cpu 2116 --> "2114, DMA, NO MPRT, 32K MEMORY" set cpu 2100 --> "21MX E, DMA, NO MPRT, 32K MEMORY" set cpu 21MX --> "21MX E, DMA, MPRT, 32K MEMORY" CAUSE: Two model-specific behaviors are not implemented: * The S-register is read-only on the 2115/2116. * LIA 6/7 (actually, the "floating" state of the internal S-bus) returns -1 on the 21MX, and 0 on the 2114/2115/2116/2100. These behaviors are tested by the configurator to determine the CPU type. NOTE: the 21MX is detected as a "E-Series" model. This is due to the presence of the TIMER instruction (TIMER is not implemented on the "M-Series" and is decoded as an MPY instruction on that system). RESOLUTION: Modify "ovfio", "dmpio", and "nulio" (hp2100_cpu.c) to implement the above behaviors. STATUS: Fixed in version 3.3-0. 52. PROBLEM: Displaying the CCA, CCB, and CCE instructions via "examine -m" prints "CLA,CMA", "CLB,CMB", and "CLE,CME" respectively. VERSION: 3.2-3 OBSERVATION: While "CLA,CMA" (e.g.) is logically what the "CCA" instruction does, it is invalid assembler syntax (although it is accepted by the "deposit" routine). CAUSE: The "mtab" array contains values to mask the instruction under consideration to the significant bits. For the CLA/B, CMA/B, and CCA/B instructions, the mask values are 006400, 007000, and 007400, respectively. They should all be 007400. For the CLE, CME, and CCE instructions, the mask values are 002100, 002200, and 002300. They should all be 002300. RESOLUTION: Modify "mtab" (hp2100_sys.c) to use the proper masks for these alter-skip group instructions. STATUS: Fixed in version 3.3-0. 53. PROBLEM: The paper tape diagnostic has several tests that depend on creating and using a tape loop. VERSION: 3.2-3 OBSERVATION: Tests 4, 5, and 11 use a loop of tape. The pattern for the loop is punched by test 7. To run tests 4, 5, and 11, multiple copies of the pattern must be stored in a "loop" file, and the tests must be exited before the "loop" runs out. A better solution would be to have a settable "loop mode" that rewinds the tape image file when EOF is encountered. RESOLUTION: Modify "ptr_mod" (hp2100_stddev.c) to add SET PTR DIAG and SET PTR READER commands, and modify "ptr_svc" to add support for loop mode. STATUS: Fixed in version 3.3-0. 54. PROBLEM: The time base generator (CLK) cannot be disabled. VERSION: 3.2-3 OBSERVATION: The TBG was an option for HP systems, and certain DOS operating system features behave differently, depending on the presence or absence of the TBG. It is desirable to allow those features to be observed during simulation. CAUSE: The "clk_dev" structure lacks the DEV_DISABLE flag. RESOLUTION: Modify "clk_dev" (hp2100_stddev.c) to add the DEV_DISABLE flag. STATUS: Fixed in version 3.3-0. 55. ENHANCEMENT: Move the memory protect simulation from the CPU to a new MP device, allow MP to be disabled, and add the 12892B memory protect feature jumpers W5 (JSB), W6 (INT), and W7 (SEL1). VERSION: 3.2-3 OBSERVATION: Memory protect is an option card in 2116/21MX systems and should have its own device entry in the simulator. The device should be disabled to indicate that the card is absent. Setting the CPU model to 2100 or 21MX should enable MP, although it may be subsequently disabled if desired. Setting the CPU model to 2116 should disable MP. The simulator should initialize with MP disabled. The "B" version of the 21MX memory protect card added three jumpers to modify the "standard" memory protect behavior. The W5 (JSB) option prohibited JSB to locations 0 and 1. The W6 (INT) option inhibited the indirect interrupt holdoff. The W7 (SEL1) option allowed I/O instructions referencing select codes other than 1. RESOLUTION: Modify "hp2100_cpu.c" to create the MP device and add commands for setting the above options and support for the associated features. STATUS: Fixed in version 3.3-0. 56. ENHANCEMENT: Allow DMA to be disabled. VERSION: 3.2-3 OBSERVATION: DMA is an option card on all machines, so disabling it should be allowed. Note that disabling DMA0 should disable DMA1 and vice-versa. (There was no single-channel DMA option except on the 2114.) RESOLUTION: Modify "hp2100_cpu.c" to permit DMA to be disabled. STATUS: Fixed in version 3.3-0. 57. PROBLEM: Setting the CPU to 21MX and a memory size > 32K and then changing the CPU to either 2100 or 2116 does not reset the memory size to a legal value. VERSION: 3.2-3 OBSERVATION: The 2100 and 2116 machines have a maximum memory size of 32K. This limit is enforced when setting the memory size, i.e., "Invalid argument" is reported when attempting to set these machines to a memory size > 32K. However, if the machine is first set to 21MX, the memory size is increased beyond 32K, and then the machine is reset to 2100 or 2116, the memory size will remain larger than 32K. CAUSE: No check on memory size is made when the machine type is set. RESOLUTION: Modify "cpu_mod[]" (hp2100_cpu.c) to call "cpu_set_opt" when changing the CPU model, and modify "cpu_set_opt" to call "cpu_set_size" if the current memory size is > 32K and the new model is 2100 or 2116. If the memory above 32K is not empty, and the "Really truncate memory" question is answered in the negative, "Command not completed" is printed, and the CPU change is aborted. STATUS: Fixed in version 3.3-0. 58. PROBLEM: According to the HELP display, SET , SET , and SET CONSOLE should allow a comma-separated list of parameters, but such commands are rejected with "Non-existent parameter." VERSION: 3.2-3 OBSERVATION: Doing HELP SET lists the following syntax for the above commands: set console arg{,arg...} set console options set arg{,arg...} set device parameters set arg{,arg...} set unit parameters None of these work, however, as each accepts only a single argument. Note that the corresponding SHOWs do accept multiple arguments. CAUSE: The "get_glyph" routines that parse the command parameters are missing the option to indicate that commas are glyph separators. RESOLUTION: Modify the appropriate calls to "get_glyph" (scp.c, sim_console.c) to specify ',' as the the "optional end of glyph character" parameter. STATUS: Fixed in version 3.3-0. 59. ENHANCEMENT: The 2607 line printer simulator (LPT) now supports local OFFLINE/ONLINE and POWEROFF/POWERON settings. VERSION: 3.2-3 OBSERVATION: The 2607 printer returns different status for power-off and offline conditions. A local "power off" command is needed to simulate the power-off or cable-disconnected state, and a local offline command is needed to simulate the PRINT button up state. This allows proper status to be returned to programs that expect it (e.g., RTE, diagnostics). RESOLUTION: Modify "lptio" (hp2100_lpt.c) to implement local power off and offline settings and to return proper status for these conditions. STATUS: Fixed in version 3.3-0. 60. PROBLEM: The 2607 line printer simulator (LPT) does not supply the proper status for the paper-out condition. VERSION: 3.2-3 OBSERVATION: Paper-out is simulated by detaching the printer image file. When detached, the simulator returns status 040000 (paper out). However, the 12845 Line Printer Operating and Service Manual (HP 12845-90001) states in section 2-33, "[The paper-out] signal is asserted only when the format tape in the line printer has reached the bottom of form." So the expected status should be 000000 unless the printer is positioned at BOF. CAUSE: "lptio" is not checking for BOF before returning paper-out status. RESOLUTION: Modify "lptio" (hp2100_lpt.c) to set the paper-out status bit only if the current print location is BOF. STATUS: Fixed in version 3.3-0. 61. PROBLEM: Issuing a TOF to the 2607 line printer (LPT) leaves the paper on the second line instead of the first. VERSION: 3.2-3 OBSERVATION: The RTE driver for the 2607 printer implements a top-of-form request by issuing a VFU call to channel zero. On a real printer, this leaves the paper positioned at the first line on the page. The simulator, however, leaves the paper positioned at the second line. Examining the LPT registers shows that LCNT is 0 immediately after an ATTACH but is 1 after a TOF request. CAUSE: The TOF is simulated by sending a form-feed to the printer image file. This is being incorrectly followed by a line-feed and a line counter increment. RESOLUTION: Modify "lpt_svc" (hp2100_lpt.c) to suppress the line-feed and line counter increment after a TOF request. STATUS: Fixed in version 3.3-0. 62. ENHANCEMENT: The 2767 line printer simulator (LPS) now supports local OFFLINE/ONLINE and POWEROFF/POWERON settings. VERSION: 3.2-3 OBSERVATION: The 2767 printer returns different status for power-off and offline conditions. A local "power off" command is needed to simulate the power-off or cable-disconnected state, and a local offline command is needed to simulate the PRINT button up state. This allows proper status to be returned to programs that expect it (e.g., RTE, diagnostics). RESOLUTION: Modify "lpsio" (hp2100_lps.c) to implement local power off and offline settings and to return proper status for these conditions. STATUS: Fixed in version 3.3-0. 63. PROBLEM: Command files that reduce CPU memory size cannot run unattended. VERSION: 3.2-3 OBSERVATION: Command files that change CPU settings will pause for operator intervention if memory size is being reduced, previous memory size was more than 32K, and the memory being truncated contained non-zero data. In this case, a prompt ("Really truncate memory?") is issued to the console. As the response is not taken from the command file, there is no way to continue without user intervention. CAUSE: The "cpu_set_size" routine calls "get_yn", which reads from "stdin." RESOLUTION: Modify "cpu_set_size" (hp2100_cpu.c) to respond to a new "-F" switch that indicates that truncation should be forced without prompting. STATUS: Fixed in version 3.3-0. 64. PROBLEM: Attempting to output to the 2767 simulator (LPS) via RTE-IVB causes not-ready and illegal interrupt errors. VERSION: 3.2-3 OBSERVATION: With the 2767 printer assigned to select code 21 and logical unit 12, attempting to print results in "IONR L 12 E12 S 0 0", followed by one "ILL INT 21" error for each character output. CAUSE: The RTE driver understands that the 2767 prints in four 20-character zones and that character output within a zone is buffered. It therefore assumes that a buffered character will be accepted within three instruction times. If the printer is still busy after that, the printer is declared "not ready" and is downed. Subsequent interrupts are not expected (the printer is assumed to be malfunctioning), resulting in the illegal interrupt messages. The 2767 simulator defines the character transfer time as four instructions and has no provision for detecting print zones. The driver assumes that it can fill a zone rapidly within the driver and will have to exit the driver to wait for an interrupt at the end of each zone. RESOLUTION: Modify "lpsio" and "lps_svc" (hp2100_lps.c) to reduce the buffer transfer time to two instructions and to determine the end of a zone in order to take an appropriately longer time before interrupting. STATUS: Fixed in version 3.3-0. 65. ENHANCEMENT: Provide a method of selecting between realistic and fast (optimized) command execution times for the 2767 simulator. VERSION: 3.2-3 OBSERVATION: The 2767 diagnostic checks command execution times, so to pass, the simulator must model these times. However, they are generally much longer than are required by the various operating systems. RESOLUTION: Modify "hp2100_lps.c" to add SET LPS REALTIME and SET LPS FASTTIME commands. Timing is now set according to the timing model in use. STATUS: Fixed in version 3.3-0. 66. ENHANCEMENT: Provide a means of printing the internal state of the 2767 printer simulator. VERSION: 3.2-3 OBSERVATION: Debugging printer errors would be easier if the printer interface commands and status were observable and recordable. SIMH provides a "DEBUG" mode command set to allow devices to provide this information. RESOLUTION: Modify "hp2100_lps.c" to add debug-mode printouts. STATUS: Fixed in version 3.3-0. 67. PROBLEM: The console "break" and "delete" character settings are not saved across a simulation SAVE/RESTORE. VERSION: 3.2-3 OBSERVATION: The console interrupt character set via SET CONSOLE WRU= is preserved when the simulation is SAVEd and then later RESTOREd. However, the values set via SET CONSOLE BRK= and SET CONSOLE DEL= are lost and revert to their default settings. CAUSE: Only "sim_int_char" is included in the hidden CPU state. RESOLUTION: Modify "cpu_reg" (hp2100_cpu.c) to include BRK and DEL registers corresponding to "sim_brk_char" and "sim_del_char". STATUS: Fixed in version 3.3-0. 68. PROBLEM: Attached device output files and debug log files cannot be examined after a simulation stop. VERSION: 3.2-3 OBSERVATION: After stopping simulation, either via a breakpoint or CTRL+E, viewing attached device output files or the device debug log file reveals incomplete data, limiting the ability to determine what has been output at the point of interruption. CAUSE: All files are buffered, and the last bytes output haven't been flushed to disk. RESOLUTION: Modify "run_cmd" (scp.c) to flush the console log file, the debug log file, and any attached output files before returning. STATUS: Fixed in version 3.3-0. 69. PROBLEM: Attempting to disable the DP controller by doing SET DPD DISABLED is rejected with "Command not allowed." Attempting to disable the DR controller by doing SET DRD DISABLED is accepted, but the controller remains enabled. VERSION: 3.2-3 OBSERVATION: Section 2.3 of "hp2100_doc.txt" states, "For devices with more than one device number, disabling or enabling any device in the set disables all the devices." This is not true, however, for most multiple-card devices. SET DISABLED is rejected for the DPD, DQD, MSD, MUXL, and MUXM devices. For the DRD, IPLI, and MTD devices, the command is accepted but does not disable the device. CAUSE: The "DEV_DISABLE" flag is missing from the "DEVICE" structures of the DPD, DQD, MSD, MUXL, and MUXM devices. Also, for all multiple devices, the device "dev_reset" function must call "hp_enbdis_pair" with the appropriate parameter to synchronize the enable/disable state of both devices. RESOLUTION: Modify the "DEVICE" structures and "dev_reset" routines as needed to the affected source files (hp2100_dp.c, hp2100_dq.c, hp2100_dr.c, hp2100_ipl.c, hp2100_ms.c, hp2100_mt.c, and hp2100_mux.c). STATUS: Fixed in version 3.3-0. 70. PROBLEM: The 2871 disc diagnostic fails Status Checks in Step 1. The checks are related to the ANY ERROR bit. VERSION: 3.2-3 OBSERVATION: Running the 2871 diagnostic causes this failure: H44 SEEK IN S1 E64 STATUS IS 000001 SHOULD BE 000000 H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0000 UNIT 00 The diagnostic is not expecting the ANY ERROR bit (bit 0) to be set with the ATTENTION bit (bit 15). The simulator is returning status 100001 from the seek operation (bit 15 is always masked by the diagnostic before reporting). Resuming the diagnostic produces this error: H44 SEEK IN S1 E64 STATUS IS 000005 SHOULD BE 000004 H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0000 UNIT 00 The diagnostic is not expecting the ANY ERROR bit (bit 0) to be set with the BUSY bit (bit 2). CAUSE: The ANY ERROR bit is set by the simulator if any status bit is set other than bit 10 (HUNTING) or bit 7 (unused). This is correct for the 13210A interface but not for the 12557A interface. From the "12557A Cartridge Disc Interface Operating and Service Manual" (HP 12557-90001, September 1970), Table 2.6, "Disc Status Word" lists the following meanings for the status bits: Bit 0: ANY ERROR. A "1" indicates that any of the remaining 15 bits (except bits 2, 3, and 7) is a "1". Bit 15: ATTENTION. A "1" indicates that an operation previously in progress on the selected disc drive unit has terminated either through normal completion or due to occurrence of an error or other unusual condition. During execution of all commands except Status Check, the condition is generated when command execution terminates regardless of the cause for termination. This would imply that the ANY ERROR bit would set with the ATTENTION bit. However, on page 2-16, Section 2.50, "Design Considerations," this statement appears: Following each interrupt, the program must issue a Status Check command to the disc drive unit that executed the storage command and verify that the ANY ERROR bit (bit 0) is not a "1" in the disc status word. Given that the ATTENTION bit sets for each command completion, and given that the ANY ERROR bit is expected to be zero after a normal command completion, the implication is that ATTENTION does not set ANY ERROR. RESOLUTION: Modify "dpcio" (hp2100_dp.c) to set the ANY ERROR bit for all status bits except bits 2, 3, 7, and 15 if the 12557A interface is selected. STATUS: Fixed in version 3.3-0. 71. PROBLEM: The 2871 disc diagnostic fails not-ready Status Checks in Step 0. VERSION: 3.2-3 OBSERVATION: Running the 2871 diagnostic causes this failure: H43 UNIT 0 NOT READY CHECK IN S0 E64 STATUS IS 000105 SHOULD BE 000101 H51 CYL 0202 HEAD 00 SECTOR 00 WORD COUNT 3072 UNIT 00 The diagnostic is not expecting the DRIVE BUSY bit (bit 2) to be set when the drive is not ready. CAUSE: The simulator is returning both NOT READY and DRIVE BUSY. This is correct for the 13210A interface but not for the 12557A interface. RESOLUTION: Modify "dpd_svc" (hp2100_dp.c) to set the DRIVE BUSY bit for the "drive not ready" condition only if the 13210A interface is selected. STATUS: Fixed in version 3.3-0. 72. PROBLEM: The 2871 disc diagnostic fails the head-load test in Step 0. VERSION: 3.2-3 OBSERVATION: Running the 2871 diagnostic reports this message to test for head loading: H40 READY UNIT 0 After stopping the simulation, attaching a disc image file, and resuming, the above message continues to repeat. The diagnostic is expecting the command-channel flag to set and drive status to return ATTENTION (bit 15) and FIRST SEEK (bit 14). CAUSE: To prepare the interface to poll for drive attention, the diagnostic issues a Status Check command to the interface. However, because the returned status word is not of interest, the diagnostic does not precede this with an STC,C to the data channel. As the data command flip-flop is not set, the simulator waits in "dpd_svc" in state "FNC_STA", rather than proceeding to state "FNC_STA1", where the poll flag is set. With the poll flag clear, the subsequent file attach does not set the command-channel flag or the associated drive status. Figure 3-7, "Status Check Operation Flow Diagram", on page 3-17 of the "12557A Cartridge Disc Interface Operating and Service Manual" (HP 12557-90001, September 1970) implies that the data-channel command flip-flop must be set before the command-channel control flip-flop is set to initiate the command. However, there is no hardware interlock on the interface to require this. Moreover, the diagnostic clearly expects the drive attention to be detected, so the drive poll must occur, even without the data transfer. The STC DC asserts the "data encode" signal to the disc controller, and the STC CC asserts the "command encode" signal. The latter initiates the Status Check operation, but there is no indication as to what happens if the "data encode" assertion does not precede it. Typical operation would be that "device encode" initiates the operation and "device flag" signals the termination. Without "device encode", "device flag" wouldn't occur. Based on the diagnostic expectation, the implication is that the data-channel flag does not set, but the Status Check command does complete, and drive polling does start. RESOLUTION: Modify "dpd_svc" (hp2100_dp.c) to complete the Status Check command and proceed to polling without setting the data-channel flag if the command flip-flop is not set, and the 12557A interface is selected. STATUS: Fixed in version 3.3-0. 73. PROBLEM: The 2883 diagnostic fails the cyclic-check test in Step 4. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H22 CYCLIC CHECK IN S4 E10 NO COMMAND FLAG H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0000 UNIT 00 The error is a result of the diagnostic executing a Cyclic Check command with a sector count of 0. Coupled with an initial seek to cylinder 0, head 0, and sector 0, this should check the maximum of 460 sectors before terminating with an End of Cylinder status. CAUSE: The diagnostic is timing out. The "12565A Disc Interface Kit Operating and Service Manual" (HP 12565-90003, August 1973) states in Section 2-45 on page 2-11, "The data rate of the disc drive is 156,000 words per second," giving a transfer time of 6.41 microseconds. At an average of 2 microseconds per instruction, the transfer time should be 3 instructions. It is currently set to 5. RESOLUTION: Change "dqc_xtime" (hp2100_dq.c) from 5 to 3. STATUS: Fixed in version 3.3-0. 74. PROBLEM: The 2883 disc diagnostic fails not-ready Status Checks in Step 0. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H43 UNIT 0 NOT READY CHECK IN S0 E64 STATUS IS 000104 SHOULD BE 000101 H51 CYL 0202 HEAD 19 SECTOR 00 WORD COUNT 0046 UNIT 00 The diagnostic is expecting the ANY ERROR bit (bit 0) and is not expecting the POSITIONER BUSY bit (bit 2) to be set when the drive is not ready. CAUSE: The simulator is returning both NOT READY and POSITIONER BUSY. From the "12565A Disc Interface Kit Operating and Service Manual" (12565-90003, Aug-1973), page 2-12, Table 2-5, "Disc Status Word," we have: Bit 6, NOT READY. A "1" indicates that the selected disc drive unit is not connected to the disc controller, or is not sequenced up with disc spinning and heads loaded, or is in an unsafe condition. Bit 2, POSITIONER BUSY. A "1" indicates the selected drive is busy executing a Position command. Bit 0, ANY ERROR. A "1" indicates that "PL0 unsafe" condition has been detected or that one or more of the remaining 7 bits is a "1". RESOLUTION: Modify "dqd_svc" (hp2100_dq.c) to set the ANY ERROR bit and remove the POSITIONER BUSY bit for the "drive not ready" condition. STATUS: Fixed in version 3.3-0. 75. PROBLEM: Doing an OTA/OTB to the command channel of the 13210A interface fails to clear the control and command flip-flops. VERSION: 3.2-3 OBSERVATION: From the "13210A Disc Drive Interface Kit Operating and Service Manual" (13210-90003, Nov-1974), examination of Figure 5-2, "Disc Interface 1 PCA Schematic Diagram" shows that doing an OTA or OTB to the command channel will clear the control and command flip-flops. Gate U16C feeds both the qualified CLC and IOO signals to the reset side of the command-channel control flip-flop. Therefore, an output operation additionally will act as though a CLC had been done. CAUSE: The action was omitted. RESOLUTION: Modify "dpcio" (hp2100_dp.c) to perform a CLC CC if an OTA or OTB CC occurs, and the 13210A interface is selected. STATUS: Fixed in version 3.3-0. 76. PROBLEM: The 2883 disc diagnostic fails the multi-unit Cyclic Check test in Step 5. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H22 CYCLIC CHECK IN S5 E64 STATUS IS 000000 SHOULD BE 000021 H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 The diagnostic does a seek to CHS 0/0/0 of unit 0, followed by a seek to CHS 1/0/0 of unit 1, followed by a Cyclic Check of one sector of unit 0. This should cause ADDRESS ERROR, because the second seek sets the controller Record Address Register (RAR) to 1/0/0, the read of unit 0 is done from 0/0/0 (set by the first seek), and the two do not compare. However, the simulator returns NO ERROR. CAUSE: The DQ simulator has separate RARs for each unit. The 12565A controller has a single RAR that is shared between all units. (Note that the DP simulator has the same problem.) RESOLUTION: Modify "hp2100_dq.c" and "hp2100_dp.c" to implement a single, shared RAR and per-unit current positions. STATUS: Fixed in version 3.3-0. 77. PROBLEM: The 2773 (DR) drum diagnostic is unable to determine the number of sectors per track during initialization. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H46 DEVICE PARAMETER DETERMINATION E47 UNABLE TO DETERMINE SECTORS PER TRACK H44 TRACK 0000 SECTOR 00 WORD COUNT 0000 The diagnostic is attempting to determine the number of sectors per track by repeatedly reading the disc status word and examining the current sector field. CAUSE: The disc status word is malformed. The next sector address should appear in bits 14-8, but instead they are ORed with the lower-byte status flags, corrupting the status return value. RESOLUTION: Modify "drcio" (hp2100_dr.c) to shift the next sector address to the upper byte before merging the status flags. STATUS: Fixed in version 3.3-0. 78. PROBLEM: The 2773 (DR) drum diagnostic reports read/write status failures. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H41 WRITE IN ST E35 STATUS IS 0 000 110 010 000 000 SHOULD BE D DDD DDD D10 D00 1D0 H44 TRACK 0000 SECTOR 00 WORD COUNT 0064 The diagnostic is expecting the Writing Enabled Flag bit to be set. CAUSE: The simulation fails to return Writing Enabled status on tracks for which writing is permitted (all tracks). RESOLUTION: Modify "drcio" (hp2100_dr.c) to set the Writing Enabled status when the track control word is output. STATUS: Fixed in version 3.3-0. 79. PROBLEM: The 7900 disc drive (DP) fails to seek check if an invalid sector is supplied. VERSION: 3.2-3 OBSERVATION: From the "13210A Disc Drive Interface Kit Operating and Service Manual" (13210-90003, Nov-1974), section 3-55 states that Seek Check status is set during a Seek command for three conditions: the cylinder addressed is greater than 202, the sector addressed is greater than 23, or a head-positioning operation is still in progress. The simulator fails to implement the second condition. CAUSE: The check is omitted. RESOLUTION: Modify "dpd_svc" (hp2100_dp.c) to set Seek Check status if the sector is out of range and the 13210A interface is selected. STATUS: Fixed in version 3.3-0. 80. PROBLEM: The 2773 (DR) drum diagnostic fails the read test in Step 2. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H42 READ IN S2 E43 DATA WORD 0063 IS 000000 SHOULD BE 046160 H44 TRACK 0000 SECTOR 00 WORD COUNT 0064 Examination of the data file reveals that the failure is occurring on write. The last word of the buffer is not being written to the drum (64 words are to be transferred via DMA, but only 63 are output). CAUSE: The DMA control word is set up to do a CLC on the last word. On all words but the last, DMA dispatches an OTA DC followed by a CLF DC. On the last word, DMA dispatches OTA DC followed by CLC DC,C. This does a "sim_cancel", causing the scheduled transfer of the last word to be aborted. RESOLUTION: Modify "hp2100_dr.c" to add "drc_run" to model the "Run Flip-Flop" from the hardware interface, and call "sim_cancel" in "drdio" only if "drc_run" is zero (i.e., not during a transfer). STATUS: Fixed in version 3.3-0. 81. PROBLEM: If a partial sector is written to the 2773 drum, the remainder of the sector is filled with zeroes instead of replicating the last word written. VERSION: 3.2-3 OBSERVATION: The "12606B Disc Memory Interface Kit Operating and Service Manual" (12606-90012, Mar-1970) and "12610B Drum Memory Interface Kit Operating and Service Manual" (12610-9001, Feb-1970) state in Section 4-91 and 4-92, respectively, that "...The last word will be repeated on the drum until the end of the sector is reached." The simulator replicates zeros instead. CAUSE: The wrong value was used. RESOLUTION: Modify "drc_svc" (hp2100_dr.c) to use "drd_obuf" instead of "0" to fill out the remainder of a sector. STATUS: Fixed in version 3.3-0. 82. PROBLEM: The 2773 (DR) diagnostic fails the sector address check in Step 1. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H21 SECTOR ADDRESS CHECK IN S1 E55 SECTOR 27 MISSING IN STATUS H44 TRACK 0000 SECTOR 00 WORD COUNT 0000 The number of the missing sector is random. The diagnostic checks to ensure that each sector in the track is detected by checking current sector field of the status word. The loop to read status words is 13 instructions long. The simulator computes a current sector number from the current time; this sector changes every 10 instructions. Therefore, in a 13-instruction loop, a sector eventually will be skipped. CAUSE: The timing model of the drum is incorrect. Sectors should increment about every 256 instructions for the 2770/2771 and every 384 instructions for the 2773/2774/2775. RESOLUTION: Modify "dr_set_size" (hp2100_dr.c) to set the per-word transfer time to reflect the model in use, and modify "GET_CURSEC" to determine the sector number properly from the current simulation time. STATUS: Fixed in version 3.3-0. 83. PROBLEM: The 2770 (DR) diagnostic fails the write test in step T. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H41 WRITE IN ST E7 PARITY BIT ERROR H44 TRACK 0000 SECTOR 00 WORD COUNT 0064 The diagnostic is expecting the parity error bit (bit 1) to be set at the conclusion of writes when using the 12606 interface. This is an artifact of the interface design. CAUSE: The status return from the 12606 interface is not modelled properly. RESOLUTION: Modify "drv_svc" (hp2100_dr.c) to return DRS_PER at the conclusion of writes when configured as a 2770/2771 disk. STATUS: Fixed in version 3.3-0. 84. PROBLEM: The 2770 (DR) diagnostic fails the track origin test in step T. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: E2 CLF OR SFS FAILED-CHANNEL 27 The diagnostic is expecting an SFS CC instruction to skip when the track origin is detected. Section 3-62 of the "12606B Disc Memory Interface Kit Operating and Service Manual" (12606-90012, March 1970) states, "If the track origin has been passed since performance of the CLF instruction, a program skip occurs." This is not occurring. CAUSE: The track origin detection feature of the 12606 interface is not implemented. RESOLUTION: Modify "drcio" (hp2100_dr.c) to schedule an "origin passed" event on the data channel when CLF is executed and to check to see if that event timer is still running when SFS is executed to determine if the track origin has passed. STATUS: Fixed in version 3.3-0. 85. PROBLEM: The 2770 (DR) diagnostic fails the SCP flip-flop test in step T. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: E3 SFC FAILED WITH FLAG CLEAR-CHANNEL 27 The diagnostic is expecting an SFC CC instruction to skip when the SCP (Sector Clock Phase) flip-flop is clear. Section 3-65 of the "12606B Disc Memory Interface Kit Operating and Service Manual" (12606-90012, March 1970) states, "If the SCP flip-flop is clear, a program skip takes place. If the flip-flop is in the set state, no skip occurs." This is not occurring. Also, the SCP flip-flop state is not being reflected in status bit 15 ("Sector Flag"). Finally, the 12610 command-channel interface does not drive the SKF backplane signal, so SFC CC on that interface should never skip. CAUSE: The SCP test feature of the 12606 interface is not implemented. RESOLUTION: Modify "drcio" (hp2100_dr.c) to skip when SFC CC is executed if the simulated head position is more than three words from the end of the current sector and the 12606 interface is selected, not to skip when SFC CC is executed and the 12610 interface is selected, and to reflect the SCP flip-flop state in bit 15 of the status word for both interfaces. STATUS: Fixed in version 3.3-0. 86. PROBLEM: The 2770 (DR) diagnostic fails the read inhibit test in step 1. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H53 READ INHIBIT CHECK IN S1 E35 STATUS IS 0 011 001 110 000 100 SHOULD BE D DDD DDD D11 D00 100 H44 TRACK 0000 SECTOR 00 WORD COUNT 0000 The diagnostic is expecting the read inhibit bit (bit 6) to be set for one sector time after an OTA/OTB instruction specifies a read operation when using the 12606 interface. Section 4-113 of the "12606B Disc Memory Interface Kit Operating and Service Manual" (12606-90012, March 1970) states, "...The RI [Read Inhibit] signal from the disc ensures that at least a full sector elapses between the occurrence of sector coincidence and the setting of the SAC FF." This is not occurring. CAUSE: The read-inhibit feature of the 12606 interface is not implemented. RESOLUTION: Modify "drcio" (hp2100_dr.c) to save the simulation time when an OTA/OTB is executed that specifies a read operation and to compare that to the current simulation time when LIA/LIB is executed and set the Read Inhibit status bit if one sector time has not elapsed. STATUS: Fixed in version 3.3-0. 87. PROBLEM: The 2770 (DR) diagnostic fails the sector address check in step 1. VERSION: 3.2-3 OBSERVATION: Running the diagnostic causes this failure: H66 BEGIN S1 H21 SECTOR ADDRESS CHECK IN S1 E55 SECTOR 90 MISSING IN STATUS H44 TRACK 0000 SECTOR 00 WORD COUNT 0000 The diagnostic checks to ensure that each sector in the track is detected by checking current sector field of the status word. The sector counter is one ahead of the sector currently under the head. For the 90-sector 2770/2771 disk, sector numbers are expected to range from 0 to 90, with the 90 state being provided while the last sector is under the head, and the 0 state being provided transiently between the "Track Origin" signal and the start of the first sector. Note that this problem does not occur on the 32-sector 2773/2774/2775 drum, because the sector counter is only five bits long, so instead of indicating sector 32 while sector 31 is under the head, the counter wraps around to zero while the last sector is under the head, and the 0 state persists a bit longer than the others. CAUSE: The simulated sector counter is calculated incorrectly. RESOLUTION: Replace the previous "GET_CURSEC" macro with a new "dr_seccntr" function (hp2100_dr.c) to model the sector counter accurately. STATUS: Fixed in version 3.3-0. 88. ENHANCEMENT: Add a TRACKPROT=n modifier to specify the number of protected tracks and PROTECTED and UNPROTECTED modifiers to change the protection state of the designated tracks to the 277x (DR) simulator. VERSION: 3.2-3 OBSERVATION: The 12606/12610 interfaces provide a track protection switch on the data channel card and specification of the number of tracks to be protected. The simulation should provide this feature. RESOLUTION: Modify "drc_mod" (hp2100_dr.c) to add track protection features to the command channel (Bob says that this is a "controller" feature). STATUS: Fixed in version 3.3-0. 89. PROBLEM: The 2767 line printer should not print non-printing characters. VERSION: 3.2-3 OBSERVATION: The 2767 printer repertoire is the 64 character ASCII subset from codes 32 to 95 (SPACE to "_"). Section 4-6 of the "2767A Line Printer Operating and Service Manual" (HP 02767-90002) says, in part, "On entering the print cycle, the characters in memory are checked for nonprintable characters and scanned and compared against the output of the character counter. Nonprintable characters are immediately erased from memory." This does not occur with the LPS simulator; all characters are passed through to the line printer image file. CAUSE: There is no check for non-printing characters. RESOLUTION: Modify "lps_svc" (hp2100_lps.c) to replace non-printing characters with blanks (equivalent to the hardware not firing the associated print hammer). STATUS: Fixed in version 3.3-0. 90. PROBLEM: The 2767 line printer should overprint the current line if sent more than 80 characters. VERSION: 3.2-3 OBSERVATION: The 2767 printer drum is 80 columns wide. Section 4-4 of the "2767A Line Printer Operating and Service Manual" (HP 02767-90002) says, in part, "The 80 print positions are divided into four zones, each having 20 consecutive print positions," and Section 4-5 says, in part, "Up to 20 characters can be received and stored in this manner, and the print cycle is started on receipt of either the 20th character or a format control character." Section 4-8 says, "If the print cycle is originally initiated on receipt of the 20th printable character, then signal ZCAV is generated upon completion of printing. The zone control register is incremented by 1 and DEMAND LINE enabled. The next printable character received will be printed in the leftmost position of zone 2." The implication is that the 81st printable character sent will be printed in zone 1, column 1. CAUSE: There is no check for the maximum character count per line. RESOLUTION: Modify "lps_svc" (hp2100_lps.c) to output a CR after every 80 characters sent without an intervening LF or FF to simulate overprinting. STATUS: Fixed in version 3.3-0. 91. PROBLEM: The DO command does not report errors to the log file. VERSION: 3.3-0 OBSERVATION: Commands contained in a DO file that cause errors do not report the errors to the console log file. They are reported to the console. For example: sim> set console log=wibble.log Logging to file "wibble.log" sim> wibble Unknown command sim> do wibble.sim (contains "wibble" as a command) Unknown command sim> quit Goodbye Log file closed But wibble.log contains: Logging to file "wibble.log" sim> wibble Unknown command sim> do wibble.sim sim> quit Goodbye Log file closed Note that the second "Unknown command" message is missing from the log file. CAUSE: "do_cmd" reports errors "stdout" only. RESOLUTION: Modify "do_cmd" to report errors to "sim_log" if it is not null. STATUS: Fixed in version 3.3-1. 92. ENHANCEMENT: The T register now reflects changes to the M register made during simulation stop. VERSION: 3.3-0 OBSERVATION: On a real HP 21xx, the T (memory contents) register is updated automatically after changing the M (memory address) register while the CPU is halted. Under simulation during a simulation stop, this does not occur. Providing it would very useful, though, as it would allow the ASSERT command to test the contents of memory locations. In particular, it would allow the diagnostics command file to test the Diagnostic Serial Number of the loaded program to ensure that the expected value is present. RESOLUTION: Modify "hp2100_cpu.c" to add a "sim_vm_post" routine that updates the T register. STATUS: Fixed in version 3.3-1. 93. PROBLEM: The 2767 and 2607 (LPS and LPT) simulators do not respond properly to output operations initiated when the printers are powered off, offline, or out of paper. VERSION: 3.3-0 OBSERVATION: On the hardware, issuing an STC to start a print operation with the power off or with the printer offline or out of paper sets the control and command flip-flops, sending the "device command" signal to the printer. The operation then "hangs" until the error is corrected, at which point the "device flag" signal is returned to the card. This causes the flag buffer and flag flip-flops to set, completing the operation. On the simulator, the operation hangs forever if the paper is out, or completes normally if the printer is powered off or offline. Both actions are wrong. CAUSE: There is no provision for detecting the correction of the foregoing situations and rescheduling the I/O event. RESOLUTION: Modify "lpt_svc" and "lps_svc" to stop execution if STOP_IOE is set and the printer is powered off, offline, or out of paper. Add "lpt_restart" and "lps_restart" routines to restart a hung I/O operation when the printer is powered on, set online, or attached. Modify "hp2100_defs.h" and "sim_stop_messages" (hp2100_sys.c) to add support for STOP_OFFLINE and STOP_PWROFF simulator stop codes. STATUS: Fixed in version 3.3-1. 94. PROBLEM: The column count on the 2767 printer doesn't increment when blanks are substituted for non-printing characters. VERSION: 3.3-0 OBSERVATION: Control characters sent to the printer are replaced by blanks before being output to the file. However, the column counter does not increment for the replaced characters. CAUSE: Logic error in "lpsio". RESOLUTION: Modify "lpsio" (hp2100_lps.c) to count replaced non-printing characters in the column count. STATUS: Fixed in version 3.3-1. 95. PROBLEM: Attempting to reboot RTE-IVB after a successful boot fails with HLT 02. VERSION: 3.3-0 OBSERVATION: Starting SIMH and booting RTE-IVB works as expected. However, if the simulation is halted, and an attempt is made to boot RTE a second time, the boot fails. Examination of memory shows that the bootstrap extension is being loaded at the wrong address. The 7900 boot loader outputs DMA control word 2 to select code 2, then sets the control flip-flop on select code 2, then outputs DMA control word 3. This sequence depends on the select code 2 control flip-flop (CTL2FF) being clear before the loader executes. Examination shows that the BOOT command is not clearing this flip-flop, so both outputs write to control word 3, leaving control word 2 (the target address) set to 0. CAUSE: The "dma0_reset" function is not clearing CTL2FF (on the hardware, the front panel PRESET button clears CTL2FF). RESOLUTION: Modify "dma0_reset" and "dma1_reset" (hp2100_cpu.c) to clear the control flip-flops on select codes 2 and 3, respectively, as well as clearing the control flip-flops on select codes 6 and 7. STATUS: Fixed in version 3.3-1. 96. PROBLEM: The control flip-flops on select codes 2 and 3 (the DMA initialization channels) are not visible. VERSION: 3.3-0 OBSERVATION: Displaying the DMA channels shows the values of the control (and flag, etc.) flip-flops for select codes 6 and 7. The control flip-flops of channels 2 and 3 are not visible and may not be altered via the simulator user interface. CAUSE: CTL(2) and CTL(3) have no register assignments in the DMA devices. RESOLUTION: Modify "dma0_dev" and "dma1_dev" (hp2100_cpu.c) to add registers for the control flip-flops on select codes 2 and 3. STATUS: Fixed in version 3.3-1. 97. PROBLEM: RESET erroneously clears the DMA control words 1-3. VERSION: 3.3-0 OBSERVATION: Attempting to slow-boot RTE from a 7905 disc fails with a "Data Overrun" error from the disc controller. Examination shows that the disc read isn't performed because DMA Control Word 1 (select code) is zero. CAUSE: The RESET (preset) that is done as part of the slow-boot process is calling "dma0_reset", which is clearing the three DMA control words. The 12897B schematic shows that CRS does not alter the control registers. RESOLUTION: Modify "dma0_reset" and "dma1_reset" (hp2100_cpu.c) to clear the control words only on power-on reset. STATUS: Fixed in version 3.3-1. 98. PROBLEM: DMA transfers to addresses 0/1 erroneously overwrite the A/B register contents. VERSION: 3.3-0 OBSERVATION: Attempting to boot RTE from a 7905 disc fails with a "Indirect address loop" simulation halt. Examination shows that the B register, which is being used as an address pointer, is corrupted by a DMA transfer from the disc to address 00001. The disc read succeeds but overwrites the A and B register contents in the process. Section I, Paragraph 4-17, "Store Field", of the "HP 1000 M/E/F-Series Computers Engineering and Reference Documentation" (HP 92851-90001) says: "The A and B addressable flip-slops (ABFF) [38A] can be clocked at the end of every microcycle. Addresses 0 and 1 are detected on the M-bus and the flip-flops are set accordingly. When DCPC uses the M-bus the ABFFST signal is suppressed." CAUSE: The "ReadIO" and "WriteIO" routines, used by DMA, are not separating accesses to locations 0/1 from accesses to A/B. RESOLUTION: Modify hp2100_cpu.c to separate the A/B registers from memory locations 0/1 and to map them equivalently, except during DMA accesses. STATUS: Fixed in version 3.3-1. 99. ENHANCEMENT: Add SET CPU modifiers for 21MX-M and 21MX-E variants. VERSION: 3.3-0 OBSERVATION: The RTE-6/VM startup routine ($STRT) determines whether it is executing on a M-series or E-series by executing the TIMER instruction and seeing if the B register is incremented. If it is, then OS microcode instructions are used, but these are not supported by SIMH, and an "Unimplemented instruction" simulation stop occurs. RTE-6/VM will boot if the CPU is detected as an M-series. RESOLUTION: Modify hp2100_cpu.c to add SET CPU 21MX-M and SET CPU 21MX-E modifiers, and enable the TIMER instruction only if the E-series variant is selected. STATUS: Fixed in version 3.3-1. 100. PROBLEM: The JPY instruction does not work. VERSION: 3.3-0 OBSERVATION: JPY is supposed to add the contents of P+1 to the Y register and use the result as the jump target address. Actually, JPY is adding the contents of P+2. CAUSE: The "e_inst" array that indicates how to process operands for the extended instructions has the wrong value for the JPY entry. RESOLUTION: Modify "e_inst" (hp2100_cpu.c) to replace the erroneous "X_MR" value with the correct "X_NO" value. STATUS: Fixed in version 3.3-1. 101. PROBLEM: The JRS instruction does not perform a memory protect check on the jump target. VERSION: 3.3-1 OBSERVATION: A JRS to a location below the MP fence is allowed, presuming that DMS conditions are satisfied. CAUSE: The JRS simulation routine is missing a memory protect check on the target address. RESOLUTION: Add a call to "mp_dms_jmp" in the JRS simulator routine (hp2100_cpu1.c) to validate the target address. STATUS: Fixed in version 3.3-2. 102. PROBLEM: The EXECUTE instruction was never implemented on the 21MX-E. VERSION: 3.3-1 OBSERVATION: Section 5.7, "Special Instructions," of the "HP 1000 M/E/F-Series Computers Engineering and Reference Documentation" (HP 92851-90001) documents three "unsupported" instructions added to the 21MX-E series CPU: DIAG, TIMER, and EXECUTE. Examination of the microcode reveals that the EXECUTE instruction (100120) was never implemented; the microcode executes a NOP for this instruction code. CAUSE: Improper documentation. RESOLUTION: Alter "cpu_eau" (hp2100_cpu1.c) to handle EXECUTE as an undefined instruction. STATUS: Fixed in version 3.3-2. 103. PROBLEM: Rounding negative unpacked floating-point numbers may yield unnormalized results. VERSION: 3.3-1 OBSERVATION: The floating-point pack routine first rounds by adding +/- 1/2 LSB to the mantissa. If rounding causes a carry, the resulting value is unnormalized. An overflow check is made on positive numbers (i.e., "011..." becoming "100..."), but no check for carry into the MSB-1 is made for negative numbers ("101..." becoming "110..."). CAUSE: The case was omitted. RESOLUTION: Modify "StoreFP" (hp2100_fp.c) to add a normalization check for negative numbers. STATUS: Fixed in version 3.3-2. 104. ENHANCEMENT: Add a command to abort command file execution if a specified simulator condition is not met. VERSION: 3.3-1 OBSERVATION: Command files need a means of reacting to unexpected program behavior. Currently, if a program deviates from expected behavior, e.g., if a diagnostic fails, the command file will become unsynchronized with the program, leading to nonsensical operation. To provide an escape mechanism for this situation, a command that tests assertions of the simulator state and aborts a running command file if the assertion fails is needed. The syntax is: ASSERT {} {} If is not specified, CPU is assumed. is a register (scalar or subscripted) belonging to the indicated device. The and optional are the same as those provided by the "examine" and "deposit" commands. The s are expressed in the radix specified for , not in the radix for the device. If the and are specified, the target register value is first altered as indicated. The result is then compared to the via the . If the result is false, an "Assertion failed" message is printed, and any running command file is aborted. Otherwise, the command has no effect. RESOLUTION: Modify "scp.c" to add "assert_cmd" and associated command table entries. STATUS: Fixed in version 3.3-2. 105. ENHANCEMENT: The option flags for the various CPU models and options were reorganized. VERSION: 3.3-2 OBSERVATION: To simplify handling of optional instruction sets, the flags describing the configuration of the simulated system are reorganized into CPU type, model, and options. This allows simple testing of a class of machines (e.g., all 21MX models) or installed options (e.g., IOP microcode on any CPU), without having to test each possible machine/option combination. RESOLUTION: Modify option flags in "hp2100_cpu.c" and "hp2100_cpu.h" to reflect logical hardware grouping and change "cpu_set_opt" accordingly. STATUS: Fixed in version 3.4-0. 106. ENHANCEMENT: Modularize the handling of optional instruction sets to allow for future microcode option simulations. VERSION: 3.3-2 OBSERVATION: The current CPU simulation decodes all UIG instructions inline, so that microcode options that share instruction codes (e.g., the 2100 IOP and the 2100 FP/FFP) must have tests for CPU type at each code point. Also, tabular instruction operand processing is complicated when instructions with differing requirements share code points. RESOLUTION: Split optional CPU instruction (EAU/UIG) processing into its own source file (hp2100_cpu1.c), represent each option as a function that determines CPU applicability and decodes its own instructions, and restructure operand processing so that it is per-option-module, rather than global for all options. STATUS: Fixed in version 3.4-0. 107. ENHANCEMENT: Add the Fast FORTRAN Processor (FFP) microcode option. VERSION: 3.3-2 OBSERVATION: The Pascal/1000 compiler will not load in an RTE system with a three-page driver partition if the FFP option is not present (required to reduce code size to fit the logical address space). Also, RTE systems generated with the FFP option will not run unless the option is present. RESOLUTION: Add a simulation of the FFP to "hp2100_cpu1.c", add a new extended-precision floating point module "hp2100_fp1.c", and add FFP helpers to "hp2100_fp.c" for single-precision operations. STATUS: Fixed in version 3.4-0. 108. ENHANCEMENT: Separate the online/offline and attach/detach functions for the magnetic tape and disc drive simulations. VERSION: 3.3-2 OBSERVATION: Currently, devices that have loadable media and an offline mode simulate both via attach and detach, i.e., attached implies online, and detached implies offline. It is desirable to separate the two, so that performing a magnetic tape "rewind/offline" command or disc "head unload" action does not detach the image file. The RTE tape backup programs set the tape units offline when they are exited, and it is awkward to have to respecify the image filename in an attach command in order to put the unit back online for a succeeding operation (the real tape drive merely requires pressing the "ONLINE" button). Also, being able to "down" untargeted disc drives when performing certain read/write operations, e.g., new system installation, is a useful safety measure (simply toggling the "UNLOAD" switch accomplishes this on a real disc drive). RESOLUTION: Modify "hp2100_ms.c" to add SET OFFLINE/ONLINE and "hp2100_dp.c", "hp2100_dq.c", and "hp2100_ds.c" to add SET UNLOADED/LOADED commands, as well as to decouple setting a device offline from detaching the associated image file. STATUS: Fixed in version 3.4-0. 109. ENHANCEMENT: Allow the DO command to nest to some finite level. VERSION: 3.3-2 OBSERVATION: Allowing a limited depth of nested DO invocations is useful for modularizing simulator command files. The current prohibition is not necessary, as "do_cmd" is reentrant. RESOLUTION: Modify "do_cmd" (scp.c) to allow DO command nesting, provide a recursion counter to disallow infinite nesting, and alter the text of the SCPE_NEST error to reflect allowed nesting. STATUS: Fixed in version 3.4-0. 110. PROBLEM: SET DEBUG=n1,n2,... doesn't work. VERSION: 3.3-2 OBSERVATION: For devices with multiple debug flags, trying to set more than one with the above command fails with "Non-existent parameter." Setting the flags one at a time with separate commands works as expected. CAUSE: The command parser breaks SET commands at commas, so "n2" is interpreted as the next top-level SET command, rather than as another debug flag. RESOLUTION: Alter the debug flag separator from "," to ";". STATUS: Fixed in version 3.4-0. 111. ENHANCEMENT: Allow SET DEBUG to mean "set all debug flags." VERSION: 3.3-2 OBSERVATION: Currently, if a device has multiple debug flags, SET DEBUG is rejected. To set all flags, they must be specified individually. RESOLUTION: Alter "set_dev_debug" (scp.c) to set all debug flags if none are specified in the SET DEBUG command. STATUS: Fixed in version 3.4-0. 112. ENHANCEMENT: Improve reporting of conflicting I/O assignments. VERSION: 3.5-1 OBSERVATION: The current "dev_conflict" (hp2100_cpu.c) routine has three behaviors that might be improved: 1. It reports only the first device conflict encountered. 2. It reports the name and select code of only one of the two conflicting devices. 3. It reports the select code in decimal. Here is a console log demonstrating these behaviors: sim> set ds dev=12 sim> set muxm dev=12 sim> set lpt dev=13 sim> run DS device number conflict, devno = 10 Simulation stopped, P: 00000 (NOP) We altered the default configuration to place PTP, DS, and MUXM at select code 12 (octal), and CLK and LPT at select code 13 (octal). Note that the above reported select code (10) is decimal. RESOLUTION: Modify "dev_conflict" behavior as follows: 1. Report all device conflicts in ascending select code order. 2. Report device names for all conflicting devices. 3. Report conflicting select codes in octal. Here is the same console log demonstrating the enhanced behaviors: sim> set ds dev=12 sim> set muxm dev=12 sim> set lpt dev=13 sim> run Select code 12 conflict: PTP and DS and MUXM Select code 13 conflict: CLK and LPT Simulation stopped, P: 00000 (NOP) STATUS: Fixed in version 3.5-2. 113. PROBLEM: "SET CONSOLE DEBUG" with no parameter crashes the simulator. VERSION: 3.6-0 OBSERVATION: Entering "SET CONSOLE DEBUG" without the "=" causes the simulator to crash with an access error. CAUSE: Null pointer dereferenced in "sim_set_debon". RESOLUTION: Return SCPE_2FARG if "cptr" is null (no parameter supplied) or points to a null character (empty parameter supplied). STATUS: Fixed in version 3.6-1. 114. PROBLEM: Nested command files do not abort on assertion failure. VERSION: 3.6-0 OBSERVATION: While a failed assertion will abort a running command file, it will not abort if the assertion is in a nested command file invocation. CAUSE: "do_cmd" is always passing back SCPE_OK, regardless of whether an invoked command returns an error status. This is apparently an attempt to avoid duplicate error messages if the last command in a command file fails (the error is printed within "do_cmd" and then again in the main command loop). RESOLUTION: Modify "do_cmd" (scp.c) to return all command error codes and to return SCPE_OK on command file EOF. STATUS: Fixed in version 3.7-0. 115. ENHANCEMENT: Provide an -E switch to DO to abort a command file on any error. VERSION: 3.6-0 OBSERVATION: Current DO processing ignores command errors. That is, if a command returns an error, the error message is printed, but processing continues with the next command in the file. This is inherently risky, as command files must be written with the expectation that every command will succeed (because there is no error trapping or conditional execution). RESOLUTION: Add a new -E switch to cause command file execution to be aborted at the first error encountered. Note that SCPE_STEP is not considered an error, and simulator-specific errors, e.g., "infinite indirect loop," does not cause an error abort (simulator limitation). STATUS: Fixed in version 3.7-0. 116. ENHANCEMENT: Two gcc compiler warnings are corrected. VERSION: 3.6-0 OBSERVATION: Running gcc in strict ISO C99 standard mode (-std=c99 -Wall -pedantic) reveals two warnings: HP2100/hp2100_mux.c:160: warning: missing braces around initializer HP2100/hp2100_mux.c:160: warning: (near initialization for `mux_ldsc[0]') and: sim_ether.c: In function `eth_mac_scan': sim_ether.c:271: warning: short unsigned int format, short int arg (arg 3) CAUSE: The first warning is due to an incompletely specified declaration. The variable is an array of structures, so the (partial) initializer should be "{ { 0 } }" but is actually "{ 0 }". The second warning is due to a mismatch between a "sscanf" format and the corresponding parameter type. The format, "%hx", requires a short unsigned int parameter, but the parameter is declared as short int. RESOLUTION: The code causing the warnings is corrected. STATUS: Fixed in version 3.7-0. 117. PROBLEM: The 7970 magnetic tape simulator defaults tape capacity to a 300-foot reel instead of to an unlimited-size reel. VERSION: 3.6-0 OBSERVATION: In the absence of an explicit size command, the 7970 tape drive reel size is intended to default to an unlimited size, wherein EOT never occurs. In fact, EOT occurs after 300 feet. CAUSE: The logic was inadvertently broken on February 16, 2006 when the "revision for new EOT test" was made. RESOLUTION: Remove the "capac" assignments in "mscio" and "msc_svc" and add an assignment to "ms_set_reelsize" (hp2100_ms.c), allowing the default capacity to remain as 0 (a zero capacity causes "sim_tape_eot" to return FALSE). STATUS: Fixed in version 3.6-1. 118. ENHANCEMENT: Add CAPACITY to 7970 simulator as an alternate to REEL. VERSION: 3.6-0 OBSERVATION: Other magnetic tape simulators allow setting a CAPACITY to indicate the number of megabytes to read or write before returning EOT. The 7970 simulator REEL size predated CAPACITY, but it's desirable for commonality if both were allowed. RESOLUTION: Alter hp2100_ms.c to support SET CAPACITY in addition to SET REEL, and enhance SHOW to display the capacity in MB or feet as appropriate. STATUS: Fixed in version 3.6-1. 119. PROBLEM: The RTE off-line magnetic tape restore program (DSKUP) hangs on the first write to the 79xx/13037 disc. VERSION: 3.6-1 OBSERVATION: The RTE offline restore program hangs when it tries to write to the 79xx disc. The program is looping in a routine that obtains drive status by sending the REQUEST STATUS command to the drive. CAUSE: The program expects that the REQUEST STATUS operation will clear the status. If this operation returns DRIVE ATTENTION, the program loops until it does not. However, the simulator does not the clear status value, so after the seek completes, DRIVE ATTENTION is returned forever. Page 10-10 of the 13037 Disc Controller Technical Information Package (HP 13037-90902, August 1980) contains this description of the REQUEST STATUS command: "After receipt of this command, the controller returns two status words to the interface. [...] The controller then clears Status-1 and waits for a command from the same interface or a timeout to occur." The controller firmware routine STATS handles the status request. It calls routine CLRST. The comment for the CLRST routine is, "Subroutine CLRST clears status for all commands but status request," but examination of the routine shows that it is unconditional. RESOLUTION: Modify the "DSC_RSTA" case in "ds_docmd" (hp2100_ds.c) to clear status-1 after returning it. STATUS: Fixed in version 3.7-0. 120. ENHANCEMENT: Separate TTY mode settings so that keyboard and display may be set independently. VERSION: 3.6-1 OBSERVATION: HP terminals had a CAPS LOCK setting that allowed upper-case input with mixed-case output. The current TTY simulator allows several I/O options, but the keyboard and display settings are locked together. RESOLUTION: Modified "tty_set_opt" (hp2100_stddev.c) to allow keyboard (TTY0) and display (TTY1) to be set independently. STATUS: Fixed in version 3.7-0. 121. ENHANCEMENT: Add the HP 93585A double integer instruction set firmware option. VERSION: 3.6-1 OBSERVATION: Later versions of RTE-6/VM were not supported on the 21MX M-Series due to logical address space limitations. The RTE-6 OS and VMA firmware options were not available for the M-Series, and some vital system programs exceeded the available address space and failed to load when the software equivalents were used. To run these programs, either the OS/VMA or double integer firmware support must be added to reduce the address space required. RESOLUTION: Add an implementation of the double integer instruction set to "hp2100_cpu1.c" and add "DBI/NODBI" options to "cpu_mod[]" (hp2100_cpu.c) to enable and disable the instructions. Note that the 93585A product worked only on the E-Series, but it is available under simulation on the M-Series as well. STATUS: Fixed in version 3.7-0. 122. ENHANCEMENT: Add "1000-M" and "1000-E" CPU options as synonyms for "21MX-M" and "21MX-E". VERSION: 3.6-1 OBSERVATION: The 21MX computer series was renamed the 1000 series with the introduction of the F-Series in 1978. The 21MX/21MX-M became the 1000 M-Series, and the 21XE/21MX-E became the 1000 E-Series. There is some internal HP documentation that refers to the F-Series as the 21MX-F, but the machine was introduced as the 1000 F-Series, and the other machines were renamed at the same time. RESOLUTION: Modify "cpu_mod[]" (hp2100_cpu.c) to add "1000-M" and "1000-E" options. STATUS: Fixed in version 3.7-0. 123. ENHANCEMENT: The DMA device is automatically assigned the logical name "DCPC" when SET CPU 21MX is done. VERSION: 3.6-1 OBSERVATION: The term "DMA" is used with the 2116 and 2100 machines. For the 21MX, the equivalent card is termed the "Dual Channel Port Controller." "DCPC" is used exclusively in the HP 21MX literature, and users are used to working with the "DCPC" name. RESOLUTION: Modify "cpu_set_opt" (hp2100_cpu.c) to assign and deassign logical names in response to SET CPU 2116/2100/21MX commands, and add "assign_device" and "deassign_device" (scp.h) to the list of global routines. Note that this enhancement does not proscribe users from using the DMA device name with 21MX simulations. STATUS: Fixed in version 3.7-0. 124. PROBLEM: Running FC under RTE aborts the simulation with an "Invalid magtape record length" error. VERSION: 3.6-1 OBSERVATION: Attempting to run the RTE "FC" ("File Copy") tape archive program to generate a tape image fails. The Read command is failing with tape library error 4, "Invalid record length." Enabling the debug mode of the 7970 tape drive simulator reveals that FC is attempting to leave space at the beginning of the tape for the archive directory by issuing a series of GAP commands. After the files are stored, the tape is rewound, and the directory is written, intending to overwrite the erased area. CAUSE: FC writes items to the tape in this order: header, marker, comment, directory, data file(s), and two EOFs. FC is issuing GAP commands to leave space at the start of the tape for the tape header, which must be written after the tape is complete, because the header indicates the number of data files that fit on the tape. The SIMH mag tape library does not implement the "erase gap" feature, and the 7970 simulator treats GAP as a NOP, so no space is reserved at the start of the tape image. When FC rewinds and writes the directory, it overwrites the existing records, resulting in a corrupt tape image. RESOLUTION: Implement an "erase gap" feature in the SIMH tape library by defining GAP metadata markers, adding a "sim_tape_wrgap" command and enhancing the "sim_tape_rdlntf" and "sim_tape_rdlntr" internal functions to skip over GAP metadata markers (sim_tape.c). Alter the 7970 simulator (hp2100_ms.c) to use it. Also, update the "mtdump" utility to report erase gaps in tape images. Note: All HP 7970 mag tape drivers (SIO, BCS, DOS, RTE) employ the GFM (erase gap and write file mark) command to write an EOF to tape. Also, the tape diagnostic tests that an initial gap precedes the first data record or EOF written at BOT (a function of the interface card). Consequently, generated tape images contain substantial amounts of GAP metadata. In almost all cases, they are unnecessary. Therefore, these gaps are written only if the REALTIME option is selected. Note that this does not affect the GAP command itself, which always writes gap metadata to tape images. STATUS: Fixed in version 3.7-0. 125. ENHANCEMENT: Improve error reporting from the 7970 tape simulator. VERSION: 3.6-1 OBSERVATION: The new "erase gap" support is only implemented for SIMH-format tape images. Attempting to write an erase gap with other formats selected correctly returns MTSE_FMT from the library. However, the 7970 simulator maps that error (and the MTSE_UNATT error) to SCPE_IERR. The resulting "Internal error" message does not help the user identify the source of the problem. RESOLUTION: Modify "ms_map_err" (hp2100_ms.c) to return SCPE_FMT and SCPE_UNATT for the tape library errors MTSE_FMT and MTSE_UNATT, respectively. STATUS: Fixed in version 3.7-0. 126. PROBLEM: "calc_dma" and "calc_int" are being called needlessly for most UIG 0 and UIG 1 instructions. VERSION: 3.6-1 OBSERVATION: The "calc_dma" and "calc_int" routines must be called after any routine that might change the I/O priority chain or set SRQ. This would be after any I/O Group instruction or card I/O action (i.e., card service routine called). In "hp2100_cpu.c", the dispatch points for "cpu_uig_0" and "cpu_uig_1" call these routines unconditionally, but they're only needed if an IOP "PRFIO" or "PRFEI" instruction is executed (these execute standard I/O instructions as part of their actions). CAUSE: The current code was a temporary expediency when the IOP instructions were moved into a separate source module. RESOLUTION: Define a new "NOTE_IOG" status return (hp2100_defs.h) to request recalculation of I/O interrupts after instruction execution completes, and rename "STOP_INDINT" to "NOTE_INDINT" to reflect that it notifies the main instruction execution loop of an interruptibility condition, rather than stopping the simulation. Alter "iogrp" and "resolve" (hp2100_cpu.c) respectively, to use these notification codes. STATUS: Fixed in version 3.7-0. 127. ENHANCEMENT: Use the tape simulator library routine "sim_tape_bot" to determine BOT status dynamically for the 7970 simulator. VERSION: 3.6-1. OBSERVATION: The 7970 simulator maintains its own BOT status by tracking rewinds and motion commands. It would be simpler to use the routine provided by the tape simulation library for this, rather than tracking each tape movement. Note that prior to the addition of erase gap support, this would not work. The diagnostic moved the tape off of BOT by using the GAP command, but this was a NOP for the tape simulation library, and the tape remained at BOT, leading to diagnostic failures. RESOLUTION: Modify "hp2100_ms.c" to use "sim_tape_bot" instead of tracking BOT internally. STATUS: Fixed in version 3.7-0. 128. PROBLEM: Sending a "controller clear" command to the 7970 magnetic tape simulator may cause an unintended write. VERSION: 3.6-1 OBSERVATION: Clearing a write-in-progress properly writes any accumulated partial record. Sending a second clear may write the record again. CAUSE: Receipt of a CLR command initiates a check for a write-in-progress among active units. If the data buffer pointer is non-zero, then partial data has been accumulated, and this is written to the tape image. The data buffer pointer is normally zeroed when a write record command is received and the command time delay has transpired. If a second write command is sent, and another CLR is done before the command time has transpired (and therefore before any data has been received from the CPU), then the previous partial record will be written again. This happens because the buffer pointer was not cleared and so implies the presence of another partial buffer of data. RESOLUTION: Modify "mscio" (hp2100_ms.c) to clear the buffer pointer after a partial record is written. STATUS: Fixed in version 3.7-0. 129. ENHANCEMENT: Improve debugging information from the 7970 simulator. VERSION: 3.6-1 OBSERVATION: Debugging problems such as the "controller clear" bug would be easier if the debug logging decoded the tape commands and included all controller actions. Currently, tape commands are reported in octal, and only some actions are reported. RESOLUTION: Modify "hp2100_ms.c" to add additional debug logging and debug flags to select subsets of the available information. STATUS: Fixed in version 3.7-0. 130. ENHANCEMENT: Partition the various microcode options in "hp2100_cpu1.c" into separate modules for easier maintenance. VERSION: 3.6-1 OBSERVATION: With the addition of the double integer instructions and potential addition of the RTE-6/VM OS and VMA instructions, the microcode option source module, "hp2100_cpu1.c", is becoming unwieldy. It is currently the largest HP source module -- about 50% larger than the rest of the CPU implementation. RESOLUTION: Move the microcode options into separate source files, grouped by function, and restrict "hp2100_cpu1.c" to contain dispatching and common routines. STATUS: Fixed in version 3.7-0. 131. PROBLEM: Errors in nested command files give no indication where the error occurred. VERSION: 3.6-1 OBSERVATION: Unless the -V switch is specified, errors in command files report the error message but not the offending command. With the advent of nested command files, the problem becomes more acute, as there is no indication in which of perhaps several nested command files the offending command is located, nor even which command is causing the error. And because -V is not transitive, each DO command appearing in each command file must be edited to add the -V switch if the error is to be located. CAUSE: The implication of errors in nested command files was overlooked when nesting was enabled. RESOLUTION: Modify "do_cmd" (scp.c) to echo commands causing errors, regardless of the -V switch, unless -Q (quiet) is supplied when starting SIMH. Also, report the name of the file containing an offending command. Note: because commands returning error status are now displayed, error message processing for the ASSERT command is simplified. In particular, the extra code that merged the assertion into the error message is no longer required. STATUS: Fixed in version 3.7-0. 132. PROBLEM: The simulator stops with an "Indirect address loop" error when running the HP 1000-F FFP diagnostic .GOTO test. VERSION: 3.6-1 OBSERVATION: According to the HP 2100 documentation, the simulator will stop if "more than INDMAX indirect references are detected during memory reference address decoding." INDMAX defaults to 16. However, attempting a reference with exactly 16 indirects stops the simulator with an "Indirect address loop" error. CAUSE: The indirect address resolution loop in the "resolve" function executes a maximum of INDMAX times. However, the decision to report an error considers only whether the loop counter reached INDMAX and not whether the indirect chain was resolved. Therefore, resolution on the last available pass through the loop is still reported as an error. RESOLUTION: Modify "resolve" (hp2100_cpu.c) to report an error if the indirect chain is not resolved after exiting the loop. STATUS: Fixed in version 3.7-0. 133. ENHANCEMENT: Add support for the HP 1000 F-Series CPU model. VERSION: 3.6-1 OBSERVATION: The Fast FORTRAN Processor option adds simulation support for three-word floating-point operations. Generalizing these to support two, three, and four-word operations would allow simulation of the F-Series floating-point processor. RESOLUTION: Rework the FFP arithmetic simulations (hp2100_cpu3.c) into general operations on multiple-precision operands. Add support for the F-Series FPP instructions. Add support for the F-Series Scientific Instruction Set (SIS) firmware. Add "1000-F" as a CPU option (hp2100_cpu.c). Note: rather than have two floating-point simulations (hp2100_fp.c and hp2100_fp1.c) that provide the two-word single-precision floating-point instructions, they are alternately conditionally compiled, depending on whether 64-bit integers are supported. As the FPP depends on this support, compiling with it enables the FPP and therefore the F-Series option, and "hp2100_fp1.c" handles the single-precision instructions for the other CPU models. If 64-bit support is not available, then "hp2100_fp.c" handles the single-precision instructions, and the F-Series is not available. STATUS: Fixed in version 3.7-0. 134. ENHANCEMENT: Add support for the 2114 and 2115 CPU models. VERSION: 3.6-1 OBSERVATION: The 2114 and 2115 are reduced-feature versions of the 2116 computer. One could restrict the 2116 environment to give an approximation of the 2114 and 2115. However, these units used a unique DMA card that behaved somewhat differently than that used in the 2100 and 1000 (the 12607 card for the 2114 supported only one DMA channel, for example). So it would be desirable to support the 2114 and 2115 directly and therefore more faithfully. RESOLUTION: Add "2114" and "2115" CPU model options (hp2100_cpu.c). STATUS: Fixed in version 3.7-0. 135. ENHANCEMENT: Add support for 12K and 24K memory sizes. VERSION: 3.6-1 OBSERVATION: The 2114 and 2115 CPUs supported up to 16K of memory in 4K increments. For accurate simulation, finer granularity than the current 4K/8K/16K/32K choices is needed. RESOLUTION: Alter the table of memory size (hp2100_cpu.c) to add 12K and 24K options. STATUS: Fixed in version 3.7-0. 136. PROBLEM: The DMS self-test instruction (10x701) should be a NOP on 1000-M machines. VERSION: 3.6-1 OBSERVATION: The DMS self-test instruction should complement the A or B register only on the 1000-E and F. On the M, it should be a NOP. In fact, it complements on the M as well. CAUSE: Oversight. RESOLUTION: Modify "cpu_dms" (hp2100_cpu2.c) to execute 10x701 as NOP on M-Series machines. STATUS: Fixed in version 3.7-0. 137. ENHANCEMENT: Add support for 21xx loader enable and disable. VERSION: 3.6-1 OBSERVATION: The 21xx CPUs are core-based machines. Binary loaders are kept in the top 64 memory locations and are protected from reading and writing by front panel LOADER ENABLE switches. When the switch is off, main memory effectively ends 64 locations earlier, i.e., the loader area is treated as non-existent. Some 21xx diagnostics test for this feature and will not proceed if the loader area is unprotected. RESOLUTION: Modify hp2100_cpu.c to add loader protection for 21xx models. STATUS: Fixed in version 3.7-0. 138. PROBLEM: The General Purpose Register Diagnostic fails when run on a 2100. VERSION: 3.6-1 OBSERVATION: The GP register diagnostic and other diagnostics that test the I/O system fail when run on 21xx CPUs. The failure is in the Basic I/O test, Test 00. The failure is, "E015 INT RTN ADDR ERROR." CAUSE: The 21xx and 1000 CPUs behave differently when holding off interrupt requests after executing certain instructions. At instruction fetch time, a pending interrupt request may be deferred if the previous instruction was a JMP indirect, JSB indirect, STC, CLC, STF, CLF, SFS (1000 only), or SFC (1000 only), or was executing from an interrupt trap cell. If the CPU is a 1000, then the request is always deferred until after the current instruction completes. If the CPU is a 21xx, then the request is deferred unless the current instruction is an MRG instruction other than JMP or JMP,I or JSB,I. Note that for the 21xx, SFS and SFC are not included in the deferral criteria. RESOLUTION: Modify "sim_instr" (hp2100_cpu.c) to clear "ion_defer" if executing on a 21xx-series CPU and the current instruction is an MRG instruction other than JMP or JMP,I or JSB,I. STATUS: Fixed in version 3.7-0. 139. PROBLEM: The 2100-specific Memory Protect Diagnostic fails when testing indirect holdoffs. VERSION: 3.6-1 OBSERVATION: Running the 2100-specific MP diagnostic fails, even though the combined 2100/21MX MP diagnostic passes. The failure is: E26. RETURN ADDRESS INCORRECT FOR CHAINED JMP,I INTERRUPTS CAUSE: The memory protect feature adds an indirect counter that allows a pending interrupt to be serviced if more than three levels of indirection are encountered. Currently, the "resolve" routine handles this by returning a status code that aborts the current instruction if an interrupt is pending and the third indirect level is encountered. However, the actual action of the hardware is to clear any interrupt deferral at the third level and to abort the instruction at the fourth. The diagnostic tests that a two-level JMP,I jumps and defers interrupts, a three-level JMP,I jumps and then allows interrupts, and a four-level JMP,I aborts and then allows interrupts. RESOLUTION: Modify "resolve" (hp2100_cpu.c) to obey the foregoing rules, and modify "sim_instr" to set "ion_defer" before calling "resolve" for JMP,I and JSB,I instructions. STATUS: Fixed in version 3.7-0. 140. PROBLEM: The 2114/2115/2116/2100 DMA diagnostic fails with an unexpected trap cell halt. VERSION: 3.6-1 OBSERVATION: Running the 21xx-specific DMA diagnostic fails, even though the combined 2100/21MX DMA diagnostic passes. The failure manifests itself as an unexpected trap cell halt, 106002. CAUSE: The diagnostic issues STF 6 and STC 6 instructions to cause a DMA interrupt without a transfer to test the priority chain. This sets the transfer (command) flip-flop on SC 6. In the next test, it does a CLC 0 and then sets up a one-word DMA transfer from the test device. Then it asserts device SRQ by doing CLC SC and STF SC, and finally it starts the device and DMA with STC SC and STC 6,C. However, with command set, asserting SRQ starts the transfer immediately, even though the control flip-flop is clear. So the word count has rolled over to zero and the transfer terminated by the time the STC 6,C is done to "start" the transfer. At that point, a second transfer is started, and the word count of zero implies a transfer of 64K words, which begins scribbling over memory. As the value 140000 had been written to the card before the transfer, and as the card is in a loopback mode, 140000 is read from the card on each transfer, and so this value overwrites memory. Eventually, the diagnostic attempts a jump indirect through an overwritten location. The target value 140000 is interpreted as a DEF 40000,I, and because location 40000 contains zero, control transfers to location 0, leading to execution of the trap cell halt 106002 in location 2. The problem is that the simulator incorrectly implements CRS ("Control Reset," the backplane signal that is generated by the CLC 0 instruction) by sending a CLC SC to each I/O card. For many cards, CLC 0 (CRS) and CLC SC (CLC) invoke the same action. They do not on the DMA card, which clears the control flip-flop for CLC, but clears the control and command flip-flops for CRS. Clearing the command flip-flop prevents the DMA transfer from starting until the STC 6,C instruction in the diagnostic is executed. RESOLUTION: Modify "cpuio" (hp2100_cpu.c) to send a new signal, "ioCRS", in response to CLC 0 and modify the I/O handlers of all devices to handle "ioCRS" as "ioCTL" temporarily until each card response can be verified from the schematics. STATUS: Fixed in version 3.7-0. 141. PROBLEM: The 12566B diagnostic interface card (LPS) does not clear the command flip-flop when CLC is done. VERSION: 3.6-1 OBSERVATION: In SET LPS DIAG mode, a 12566B microcircuit interface card with a loopback connector is simulated. This is provided for the use of certain diagnostics that test the I/O system. Attempting to use this simulation with the 2114/2115/2116/2100 DMA diagnostic fails with: E136. D1-I/O FLG SET even though the combined 2100/21MX DMA diagnostic passes. CAUSE: The diagnostic requires that jumper W9 be set to the "A" position. This enables clearing of the device command flip-flop when the CLC instruction is executed. Clearing CMD is intended to stop any I/O in progress. The diagnostic sets up a one-word output with STC and CLC specified in the control word. At the end of the transfer, "dma_cycle" (hp2100_cpu.c) correctly sends LPS a STC,C followed by a CLC,C. The STC,C starts a transfer and therefore schedules an I/O event for completion in one instruction. The CLC,C clears the control flip-flop and the device flag, but because "sim_cancel" is not called, the I/O event remains. When it fires, the device flag is set. The diagnostic is expecting the flag to be clear. The 2100/21MX diagnostic tests for flag clear by using a control word that has neither STC nor CLC present. This generates a CLF to the interface, which correctly clears the device flag (without starting another operation). RESOLUTION: Modify "lpsio" (hp2100_lps.c) diagnostic mode to latch the input data on STC and schedule the command clear and flag set in two instructions. Also, clear the command flip-flop and cancel any pending I/O event if CLC is executed in diagnostic mode. This more correctly implements the response of the hardware under DMA control. STATUS: Fixed in version 3.7-0. 142. ENHANCEMENT: Add diagnostic loopback capability to the 12920A multiplexer. VERSION: 3.7-0 OBSERVATION: To run the HP multiplexer diagnostic, a loopback cable is needed that interconnects two ports. To test all sixteen ports, eight cables are needed, or the diagnostic must be run eight times while moving the single cable from port pair to port pair. The diagnostic cannot be run under simulation, because the 12920A simulator does not provide loopback capability. RESOLUTION: Add DIAG/TERM commands to switch between diagnostic cable (loopback) mode and terminal cable (Telnet connection) mode. STATUS: Fixed in version 3.7-1. 143. PROBLEM: The 12920A multiplexer control card diagnostic fails in test 0. VERSION: 3.7-0 OBSERVATION: Running the control diagnostic reports this failure: TEST 00 E027 PRESET DID NOT CLEAR STATUS ON PORT 01 The diagnostic is testing each channel after PRESET to verify that the status is reset, but the value returned is not as expected. CAUSE: Page 3-6, paragraph 3-38 of the multiplexer service manual states, "The channel number is four bits (10 through 13) of every output or input word. When the scan bit is cleared (logic 0) during an OTA/B command, the channel number does not change and the status of the same channel is loaded by the next LIA/B command." The diagnostic sets the channel number by an OTA to the control card select code. However, the "ioOTX" handler in "muxcio" is not setting the channel to the supplied value for subsequent LIA/B use. RESOLUTION: Set "muxc_chan" (hp2100_mux.c) to the channel number supplied in the "ioOTX" handler in "muxcio." STATUS: Fixed in version 3.7-1. 144. PROBLEM: The 12920A multiplexer control card diagnostic fails in test 4. VERSION: 3.7-0 OBSERVATION: Running the control diagnostic reports this failure: TEST 04 E034 STORED STATUS NO. 1 FAILED TO INTERRUPT The diagnostic sets the multiplexer channel to interrupt on a change in S1 status, but the interrupt did not occur as expected. CAUSE: The "mux_cntl_int" returns immediately if "muxc_scan" (the scan bit) is zero. This behavior is incorrect; with the scan bit set to zero, only the current channel should be tested for interrupt. Note that Figure 3-3, the "Simplified Schematic Diagram" on page 3-9 of the service manual shows that the status interrupt is conditional on the scan bit, but the actual schematic in figure 5-4 on page 5-15 shows that this is not the case. RESOLUTION: Modify "mux_cntl_int" (hp2100_mux.c) to test the current channel for a status interrupt condition if "muxc_scan" is zero, rather than returning directly. STATUS: Fixed in version 3.7-1. 145. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 1. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 01 E032 SEND PORT NUMBER IS 00 SHOULD BE 01 The diagnostic is reading the transmitted data from the lower select code to determine the transmit channel, but the channel number is wrong. CAUSE: The "mux_data_int" function is setting only the upper select code status value in response to a transmit interrupt. The lower select code card schematic, figure 5-3 on page 5-11 of the service manual, shows that the interrupting channel number is presented on bits 14-10 of the status words supplied by both the upper and lower cards. RESOLUTION: Modify "mux_data_int" (hp2100_mux.c) to set "muxl_ibuf" as well as "muxu_ibuf" in response to a transmit interrupt. STATUS: Fixed in version 3.7-1. 146. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 1. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 01 E034 DATA RECEIVED ON PORT 00 IS 2125 SHOULD BE 3525 Data is sent out on one channel is compared for equality when received on the other channel. The values are not equal. CAUSE: Characters delivered to the multiplexer are contained in bits 10-0 of data words output from the CPU. In the "ioCTL" handler in "muxlio," the output word is masked with OTL_CHAR (01777) to retain just the data before storing the result in "mux_xbuf". However, "mux_xbuf" and the corresponding "mux_rbuf" are declared as "uint8", so the upper three bits are lost. RESOLUTION: Change the declarations of "mux_xbuf" and "mux_rbuf" (hp2100_mux.c) from "uint8" to "uint16" to retain all of the character data bits. STATUS: Fixed in version 3.7-1. 147. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 2. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 02 E041 BREAK BIT SHOULD BE SET The diagnostic is transmitting an all-space character and testing whether the receiver detects this as a break. The break bit is not being set. CAUSE: The error is misleading. The actual cause is that an interrupt is not occurring on the receive channel, because "mux_rchp" is not being set for the target line in "muxi_svc" if SCPE_BREAK is detected, even though the break flag is being set in the status word. RESOLUTION: Modify "muxi_svc" (hp2100_mux.c) to indicate a pending character if a break is detected. STATUS: Fixed in version 3.7-1. 148. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 3. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 03 E042 PARITY BIT SHOULD BE SET The diagnostic is checking that the "parity check" bit (bit 15) of the received status word is 1 when odd parity is sent. The bit is 0. CAUSE: The "odd_par" table has numerous errors in it. For example, the values in columns 006 and 007 should be the opposite of the values in columns 016 and 017, but in many cases they are not. Also, the "RCV_PAR" macro is setting LIL_PAR if the data has even parity, not odd parity. For example, it returns LIL_PAR on a data value of zero. Paragraph 3-23 on page 3-6 of the service manual says, "The parity bit is set (logic 1) for odd parity and turned off (logic 0) for even parity." RESOLUTION: Correct the "odd_par" table (hp2100_mux.c) to reflect the correct odd parity for all values. Reverse the sense of the test in "RCV_PAR" so that "LIL_PAR" is returned if the received value has odd parity. STATUS: Fixed in version 3.7-1. 149. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 3. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 03 E043 RAW PARITY BIT 7 The diagnostic is checking that bit 7 of the data word contains the desired parity (odd or even). Bit 7 has the wrong value. CAUSE: Parity is not being generated for transmitted characters. RESOLUTION: Modify the "ioCTL" handler in "muxlio" (hp2100_mux.c) to generate odd parity and add it to the data if bit 12 of the transmission configuration word is set. STATUS: Fixed in version 3.7-1. 150. PROBLEM: The 12920A multiplexer data card diagnostic fails in test 4. VERSION: 3.7-0 OBSERVATION: Running the data diagnostic reports this failure: TEST 04 E033 RECEIVE PORT NUMBER IS 00 SHOULD BE 16 The diagnostic is configuring the diagnose channels and presuming that an initial CLC 0 will clear the configuration parameters for all channels. CAUSE: The CLC handler is not performing the master clear function, so the previously configured channel 0 is interrupting before the expected channel 16. Also, the interrupting channel number is truncated to four bits by the "PUT_CCH" macro in "mux_data_int", so an interrupt on channel 16 is reported as being on channel 0. Control card channel numbers are four bits in size, but data channel numbers are five bits; the wrong macro is being used to form the status word. RESOLUTION: Modify the "ioCRS" handler in "muxlio" (hp2100_mux.c) to clear all 37 channel transmit and receive parameters in response to a CLC 0. Modify "mux_data_int" to use the "PUT_DCH" macro to put the data channel number into the return status. STATUS: Fixed in version 3.7-1. 151. ENHANCEMENT: Add debug printouts to the 12920A multiplexer. VERSION: 3.7-0 OBSERVATION: Debugging multiplexer behavior would be easier if the internal state of the simulator was observable and recordable. RESOLUTION: Modify "hp2100_mux.c" to add debug-mode printouts. STATUS: Fixed in version 3.7-1. 152. ENHANCEMENT: Add debug printouts to the 12875A Interprocessor Link. VERSION: 3.7-0 OBSERVATION: Debugging HP 2000 Time Shared BASIC systems would be easier if the internal state of the link simulator was observable and recordable. RESOLUTION: Modify "hp2100_ipl.c" to add debug-mode printouts. Modify "sim_defs.h" to add a "DEBUG_PRJ" macro. STATUS: Fixed in version 3.7-1. 153. PROBLEM: The 2000 Access terminal multiplexer does not initialize properly approximately three starts in ten on multiprocessor host systems. VERSION: 3.7-0 OBSERVATION: Booting the 2000 Access Time Shared BASIC system appears to start the system correctly, but the terminal multiplexer does not work. Typing a CR does not produce the expected "PLEASE LOG IN" message, even though the system console is responsive. Restarting the system often corrects the problem. CAUSE: There is a race condition between the system processor (SP) and the I/O processor (IOP) during initialization. A 321-word DMA transfer is done from the IOP to the SP. Immediately after DMA completion, the SP pulses the interprocessor link to "set correct flag direction" (according to the Access source). The SP depends on the IOP still being in the DMA completion interrupt handler when that pulse occurs, so that it does not cause an interrupt and subsequent command processing. On a multiprocessor host system, the SP and IOP SIMH processes may run in parallel. If the SP is blocked after DMA completion and before the IPL pulse, the IOP may complete its own DMA completion interrupt handling and therefore see the pulse as a second DMA command request. If that occurs, the IOP hangs in the DMA transfer, so it never completes initialization of the terminal multiplexer. RESOLUTION: Modify the "ioEDT" handler in "iplio" (hp2100_ipl.c) to sleep for one millisecond before signalling a DMA completion interrupt for an output transfer. This allows the SP time to pulse the IPL before the IOP processes the DMA completion interrupt. Modify "dma_cycle" (hp2100_cpu.c) to pass the DMA channel number and I/O direction flag in the "dat" parameter to EDT handlers. Note that this is a workaround, and not a solution, as the SP can still block between DMA completion and IPL pulsing, which would allow the IOP to complete its DMA handling first. STATUS: Fixed in version 3.7-1. 154. PROBLEM: The 12920A multiplexer simulator encounters a buffer overrun error when the five "diagnose" lines are employed. VERSION: 3.7-0 OBSERVATION: Multiplexer line status is kept in "mux_sta", which is defined with 16 elements. However, there are 21 receive lines for which status is kept. When "mux_diag" is called to service the "diagnose" lines (lines 16-20), "mux_sta" is indexed beyond the end of its definition. CAUSE: The size should be "MUX_LINES + MUX_ILINES" instead of "MUX_LINES". RESOLUTION: Modify the size of "mux_sta" (hp2100_mux.c) from 16 to 21 elements. STATUS: Fixed in version 3.7-1. 155. PROBLEM: Resetting the 12920A multiplexer does not clear status for the receive-only "diagnose" lines. VERSION: 3.7-0 OBSERVATION: Line status is kept in "mux_sta[0..20]". Doing a multiplexer reset (e.g. RESET, RUN, etc.) clears line status only in lines 0-15. CAUSE: Multiplexer line reset is handled by "mux_reset_ln" in response to a device reset. "mux_reset_ln" is called only for lines 0-15. RESOLUTION: Modify "muxc_reset" (hp2100_mux.c) to clear the variables associated with lines 16-20. STATUS: Fixed in version 3.7-1. 156. PROBLEM: Breakpoint actions aren't executed properly if the breakpoint occurs in a DO file. VERSION: 3.7-0 OBSERVATION: Breakpoint actions are not reliably executed if they appear in a DO file. Given this "t.sim" command file: break 100; e 0-1 go break 200; e 2-3 go e 4-5 ...then entering "do t.sim" at the command prompt produces this output: Breakpoint, P: 00100 (NOP) Breakpoint, P: 00200 (NOP) 4: 000000 5: 000000 sim> e 2-3 2: 000000 3: 000000 Note that the "e 0-1" is not executed at all, and the "e 2-3" is executed after the "e 4-5". CAUSE: Breakpoint actions are executed by a call to "sim_brk_getact" in the main execution loop. The call is missing from the execution loop in "do_cmd". In the test case, the "e 2-3" is being executed by the "sim_brk_getact" in the main execution loop after command file execution terminates. This out-of-sequence execution could have serious consequences, e.g. if the command were intended to clear a log file prior to a debug run ("! del big.log") but instead deleted it at the end of the run when the DO file terminated. RESOLUTION: Modify "do_cmd" (scp.c) to incorporate a call to "sim_brk_getact" to process breakpoint commands as they occur. STATUS: Fixed in version 3.7-1. 157. PROBLEM: The .DMP instruction returns erroneous results. VERSION: 3.7-3 OBSERVATION: After creating a FMGR file that occupies the rest of the cartridge, the "next track" field in the directory list is wildly incorrect. CAUSE: An unsigned multiply is done instead of a signed multiply. Multiplying by a small negative number returns an overflow condition. RESOLUTION: Convert the operands to signed integers before multiplying in "hp2100_cpu3.c". STATUS: Fixed in version 3.8-0. 158. PROBLEM: The .DDI instruction returns erroneous results. VERSION: 3.7-3 OBSERVATION: Attempting to scan an indexed library file that is split into multiple extents returns FMGR -012 (SOF or EOF error). Accessing the library file sequentially avoids the error. CAUSE: Extent calculations are in error. An unsigned divide is done instead of a signed divide. RESOLUTION: Convert the operands to signed integers before dividing in "hp2100_cpu3.c" STATUS: Fixed in version 3.8-0. 159. ENHANCEMENT: Portable unsigned-to-signed conversions were added. VERSION: 3.7-3 OBSERVATION: Conversions from unsigned to signed values, e.g., from "uint16" to "int16", using casts or union store/load are not portable. They will fail if the size in bits is > 16. Portable versions are needed. RESOLUTION: Add portable "INT16" and "INT32" macros (hp2100_defs.h) to provide uint16-to-int16 and uint32-to-int32 conversions. STATUS: Fixed in version 3.8-0. 160. PROBLEM: The action of jumpers W5 (JSB), W6 (INT), and W7 (SEL1) for the 12892B Memory Protect card are reversed. VERSION: 3.7-3 OBSERVATION: The SET/SHOW MP command sets/reports the jumpers in the wrong state. A jumper flag of 1 is reported as "in" but it is treated as "out" by the simulation. CAUSE: The "mp_mod" table treats a jumper flag bit on as indicating an installed jumper, but the flag bit actually indicates a removed jumper. RESOLUTION: Reverse the jumper sense in the "mp_mod" table (hp2100_cpu.c). STATUS: Fixed in version 3.8-0. 161. PROBLEM: The action of jumper W5 (JSB) is incorrect. VERSION: 3.7-3 OBSERVATION: Executing a JSB below the MP fence and to a write-protected page should cause a DM violation. This occurs if W5 is in, but an MP violation is reported if W5 is out. CAUSE: The W5 check is wrong. RESOLUTION: Correct the JSB handler in "sim_instr" (hp2100_cpu.c) to report a DM error with W5 out (unless the instruction is JSB 0 or JSB 1, in which case an MP error is correct). STATUS: Fixed in version 3.8-0. 162. PROBLEM: The memory protect MEVFF is not reset when an I/O instruction is executed from a trap cell during an interrupt. VERSION: 3.7-3 OBSERVATION: The Memory Expansion Violation Flip-Flop (MEVFF) is set on any DMS violation: read protect, write protect, base-page protect, or privilege. The MEVFF is cleared when MP is re-enabled after the violation is handled. Any interrupt request automatically disables MP. MP is re-enabled explicitly via an STC 5 instruction or implicitly after a non-HLT I/O instruction is executed in the interrupt trap cell. This latter case does not clear the MEVFF under simulation. CAUSE: Improper coding in the interrupt handler. RESOLUTION: Modify "sim_instr" (hp2100_cpu.c) to set "mp_mevff" to zero if a non-HLT I/O instruction is executed from a trap cell. STATUS: Fixed in version 3.8-0. 163. PROBLEM: Running certain RTE-6/VM configurations will cause an "unimplemented instruction" stop for the DIAG (100000) instruction. VERSION: 3.7-3 OBSERVATION: If an RTE-6/VM system is generated with a firmware replacement for the $LIBR routine, and a program using the software equivalent is run under that system, an "unimplemented instruction" stop occurs. This is actually due to a bug in $RQST (EXEC6). The instruction sequence executed is: XOR INSTR NOW HAVE THE ADDRESS RAL,CLE,SLA,ERA IF INDIRECT INDR XLA A,I GET NEXT LEVEL RAL,CLE,SLA,ERA CHECK FOR MULTI LEVEL JMP INDR FOUND ONE SO LOOP (MUST END) If the sign bit of the A register is zero, the first "RAL,CLE,SLA,ERA" improperly skips the first word of the two-word instruction "XLA A,I" and executes the second word (100000). This decodes as a DIAG instruction. DIAG should execute as a NOP with the CPU running, as it is only effective when executed from single-step mode. This would mask the bug, as the second "RAL,CLE,SLA,ERA" would also skip, taking execution out of the sequence; the bug fix would be to replace the first "RAL,CLE,SLA,ERA" with a "JMP *+3". However, the simulator stops instead. CAUSE: The DIAG processor executes as NOP on the E-Series, but no equivalent test is made for the F-Series. RESOLUTION: Modify "cpu_eau" (hp2100_cpu1.c) to allow DIAG as NOP on the F-Series as well as the E-Series. STATUS: Fixed in version 3.8-0. 164. ENHANCEMENT: Add the RTE-6/VM operating system accelerator and virtual memory firmware instructions. VERSION: 3.7-3 OBSERVATION: RTE-6/VM "primary" (i.e., factory distribution) systems after revision 2401 were generated with the OS/VMA firmware replacements. Such systems will not run under SIMH due to the lack of firmware support. To get later revision systems running without firmware replacements requires a bootstrapping process that begins with revision 2340 and generates successive systems until the desired revision is reached. Moreover, later revisions of some programs (e.g., TF) will not load due to exceeding the logical address space available when software replacements are used. RESOLUTION: Add the OS/VMA instructions (hp2100_cpu5.c, hp2100_cpu6.c) to support later primaries and to provide address space reductions in later programs. Add CPU debug support and flags for OS and VMA instructions. STATUS: Fixed in version 3.8-0. 165. ENHANCEMENT: Change the default breakpoint type from the current static setting of "-e" (break unconditionally) to a dynamic setting that matches the current DMS mapping ("-n", "-s", or "-u"). VERSION: 3.2-1 OBSERVATION: After reaching a map-specific breakpoint (e.g., a system-map breakpoint to debug a device driver), the most common action is to examine memory locations and set another breakpoint farther ahead in the code. That breakpoint will, of course, be set in the same mapping mode as the one just reached, i.e., in the current DMS mapping mode. Therefore, defaulting to "the same map as is currently enabled" leads to the most-used cases not requiring additional switches (and therefore the chance of operator error). RESOLUTION: Before exiting "sim_instr" (hp2100_cpu.c), set "sim_brk_dflt" to a switch corresponding to the current DMS mapping mode. STATUS: Fixed in version 3.8-0. 166. ENHANCEMENT: Change the default examine/deposit addressing mode from the current static setting of "address is physical" to a dynamic setting that matches the current DMS mapping ("-s" or "-u"), and provide a new modifier option ("-n") to specify that an address is a physical address. VERSION: 3.2-1 OBSERVATION: After reaching a breakpoint, it is common to examine memory contents. The most common requirement is to examine memory under the currently enabled map, i.e., if a break occurred under the system map, then examination of system memory is most likely to be requested (and correspondingly for user-map breakpoints). However, the current default is to examine the first 32K of physical memory. This is a reasonable default for non-DMS systems, or when DMS is not enabled, but is awkward when debugging mapped environments. A switch ("-v") is currently provided to request access under the current DMS map, but debugging with DMS active essentially requires specifying that switch on every EXAMINE and DEPOSIT command. It would be more useful if this action were the default. RESOLUTION: Modify "cpu_ex", "cpu_dep", and "dms_cons" (hp2100_cpu.c) to respond to redefined switch modifiers as follows: Old New Description === === =========================================================== -v if DMS enabled, use current map, else use unmapped -n use unmapped -s -s if DMS enabled, use system map, else illegal -u -u if DMS enabled, use user map, else illegal -p -p if DMS enabled, use DCPC port A map, else illegal -q -q if DMS enabled, use DCPC port B map, else illegal If a map specifier is used when DMS is not enabled, "Command not allowed" results. Note that the SAVE and RESTORE commands always access memory unmapped. Also, note that operation in non-DMS environments is unchanged, i.e., EXAMINE and DEPOSIT with no modifiers still access physical memory as before. STATUS: Fixed in version 3.8-0. 167. ENHANCEMENT: NOBR with no argument clears breakpoint at the current PC. VERSION: 3.7-0 OBSERVATION: Breakpoints are often required only once, e.g., when establishing which of several paths through a routine is taken. In this case, when a breakpoint is reached, it is immediately removed. The existing breakpoint clear syntax requires specification of the address. It would be helpful if the address defaulted to the current PC, i.e., the location of the breakpoint just hit. RESOLUTION: Modify "ssh_break" (scp.c) to allow omission of the address argument and, if omitted, to clear the breakpoint corresponding to the current PC. STATUS: Fixed in version 3.8-0. 168. ENHANCEMENT: The SHOW VERSION command now reports the patch level. VERSION: 3.3-2 OBSERVATION: Having multiple patched versions of SIMH that report the same version number leads to confusion. But official releases often increment the minor version only. RESOLUTION: Modify "show_version" (scp.c) and "sim_rev.h" to add a reported "patch delta" version number. STATUS: Fixed in version 3.8-0. 169. PROBLEM: The DPTR register in the DS device cannot be set to any value other than 0 or 1. VERSION: 3.7-3 OBSERVATION: The DPTR register is documented as an 8-bit "sector buffer pointer." However, it is implemented as a single-bit flag in the REG structure. This prohibits setting any value other than 0/1. CAUSE: DPTR is improperly defined with the FLDATA macro. It should use DRDATA instead. RESOLUTION: Modify "ds_reg" (hp2100_ds.c) to define the DPTR register as DRDATA instead of FLDATA. STATUS: Fixed in version 3.8-0. 170. ENHANCEMENT: Add an implementation of the 12966A Buffered Asynchronous Communications Interface (BACI) card. VERSION: 3.7-3 OBSERVATION: Newer RTE primary systems will not run without a system console connected to a BACI card using DVR05, as support for the Teletype interface using DVR00 had been dropped. Reconfiguring to a DVR00 driver is problematic if another "type 00 driver" (e.g., DVM00) is present in the equipment table ahead of DVR00. Also, some RTE features, such as command-stack editing, don't work with the Teletype interface. Having a BACI simulation would allow these systems to run "out of the box." RESOLUTION: Add a BACI simulation (hp2100_baci.c) to the HP simulator. STATUS: Fixed in version 3.8-0. 171. ENHANCEMENT: Expose the current time base generator (CLK) poll time via a device register. VERSION: 3.7-3 OBSERVATION: It is often helpful to know the number of simulated CPU instructions per second on a host machine. As the CLK device is calibrated to real time, knowing the tick rate and the service poll time would allow the calculation of the simulated MIPS. The tick rate is given by the "SEL" register, but the poll time is set using a local variable and is not visible to the user. RESOLUTION: Added global variable "clk_tick" and register "IPTICK" (instructions per tick) to the CLK device (hp2100_stddev.c). STATUS: Fixed in version 3.8-0. 172. PROBLEM: The "ioCRS" actions are incorrect in several devices. VERSION: 3.7-3 OBSERVATION: The "ioCRS" signal was added in 3.7-0 to all devices. As an expedient, the action was defaulted to CLC SC, which was how CRS was handled before. Most devices handle CRS as CLC, but not all do. In particular, the TTY and DS devices do not. CAUSE: Expediency. RESOLUTION: Modified the "ioCRS" handlers in "tty_svc" (hp2100_stddev.c) and "ds_svc" (hp2100_ds.c) to implement the control reset signal correctly. STATUS: Fixed in version 3.8-0. 173. PROBLEM: The paper tape reader hangs at EOT after "rewinding" a tape. VERSION: 3.7-3 OBSERVATION: The POS register records the current position of the file attached to the PTR device. The manual says, "...by changing POS, the user can backspace or advance the reader." Attempting to re-read a tape by setting POS to 0 causes the reader to hang when the end-of-tape is encountered the second time. CAUSE: The trailing-null counter, "ptr_trlcnt", is not reset when the position is. Therefore, the automatic trailer function does not work the second time, and the reader hangs. RESOLUTION: Reset "ptr_trlcnt" when a non-null character is read. STATUS: Fixed in version 3.8-0. 174. PROBLEM: The .PWR2 instruction returns the wrong value in the A register. VERSION: 3.7-3 OBSERVATION: The .PWR2 instruction returns the result of the expression (ab * 2 ^ n) in the A and B registers. The B-register value is correct, but the A-register value is always 0. CAUSE: The conversion of the high-word value in "fp_unpack" from "fop" to "mantissa" is incorrect. Specifically, the cast to 16 bits should be done on the shifted value, but it is improperly done on the unshifted value, so that shifting right by 16 always yields a zero value. Note that the only other instruction to use "fp_unpack" is .FLUN, but that discards the A-register (high mantissa) value and instead returns the exponent in A, so the error does not manifest itself there. Also note that there are two "fp_unpack" implementations. The one in error is the firmware floating-point version. The hardware floating-point version in "hp2100_fp1.c" is correct and is used when HAVE_INT64 is defined during compilation. RESOLUTION: Modify "fp_unpack" (hp2100_fp.c) to correct the conversion. STATUS: Fixed in version 3.8-0. 175. PROBLEM: The DBI self-test instruction does not skip. VERSION: 3.7-3 OBSERVATION: The double-integer firmware self-test is supposed to set the S register to 102077 octal and return to P+1. Neither of these actions occur. CAUSE: At the time that the DBI firmware was implemented, the source microcode and the installation manual were unavailable. Subsequently, the source microcode was located, and the self-test action is now known. RESOLUTION: Modify "cpu_dbi" (hp2100_cpu3.c) to add the proper implementation of the DBI self-test instruction. STATUS: Fixed in version 3.8-0. 176. PROBLEM: The DEPOSIT command will change in some other device if the name is unique to that other device. VERSION: 3.7-3 OBSERVATION: Entering "deposit ptr ppos 0" actually changes the "ppos" register in the "tty" device. It should give an error that "ppos" does not exist in the "ptr" device. CAUSE: The "exdep_cmd" routine is calling "find_reg_glob" if the "find_reg" routine returns a not-found error for the selected device. "find_reg_glob" searches for a unique name among all devices and returns it if found. RESOLUTION: None. STATUS: Fixed in version 3.8-0. 177. PROBLEM: The four-word double-precision sine and cosine functions return erroneous results. VERSION: 3.7-3 OBSERVATION: The .SIN and .COS functions return improper values when SIS firmware is present. When the firmware is absent, the results are correct. CAUSE: .SIN and .COS call /CMRT, the common range reduction routine. This routine is implemented in the SIS firmware. The /CMRT firmware simulation is not setting the B register properly to the lower 16 bits of the reduction multiple. RESOLUTION: Correct "cpu_sis" (hp2100_cpu4.c) to return the proper value in the B register for the /CMRT instruction. STATUS: Fixed in version 3.8-0. 178. PROBLEM: The free HP 700/92 terminal emulator, QCTERM from AICS, does not work with SIMH. VERSION: 3.7-0 OBSERVATION: Attempting to run QCTERM as a Telnet client with SIMH loses characters. Specifically, the first character typed after a CR is lost. CAUSE: QCTERM is sending "bare" carriage-return characters to SIMH. SIMH presumes that CR will always be followed by LF or NUL in text mode, so it simply drops the next character. For QCTERM, this is the first character of the subsequent transmission. Examination of the Telnet connection initiation code shows that SIMH is sending several command sequences but is not checking the client replies (except for binary mode). A correct negotiation mechanism must be implemented to handle the variety of Telnet clients properly. WORKAROUND: Modify the TNS_SKIP case in "tmxr_poll_rx" (sim_txmxr.c) to skip only LF or NUL following CR. Any other character is processed as is. STATUS: Fixed in version 3.8-0. 179. ENHANCEMENT: Add infrastructure changes to support CPU idling in a future release. VERSION: 3.7-3 OBSERVATION: Idle support would be a welcome addition to the HP simulator. RESOLUTION: Modify hp2100_stddev.c to change the TTY (console) input poll to use a 10 millisecond calibrated timer, to provide a synchronization routine for use by other devices with input polls, and to synchronize the CLK to the console poll if it is set for a 10-millisecond period. Add UNIT_IDLE flags to the CLK and TTY input units. Modify hp2100_mux.c and hp2100_baci.c to synchronize Telnet polling with the console poll. STATUS: Fixed in version 3.8-0. 180. PROBLEM: There is some dead code in hp2100_stddev.c, now that control character handling is in sim_console.c. VERSION: 3.7-0 OBSERVATION: In version 3.2-2, "tto_out" (hp2100_stddev.c) was altered to suppress output for all control characters (characters < 40 octal), except for BEL, BS, LF, and CR. This was in support of the RTE line editor. In version 3.5-2, generalized support for control character output suppression was added to sim_console.c. This obviated the HP-specific handling. However, some of that code remained in hp2100_stddev.c. CAUSE: Oversight. RESOLUTION: Removed the redundant code. STATUS: Fixed in version 3.8-0. 181. ENHANCEMENT: Add the RTE-IVB extended memory area firmware instructions. VERSION: 3.7-3 OBSERVATION: The Pascal/1000 compiler (HP 92832A) relies on EMA instructions to manage its internal memory. EMA software is available, but the compiler can exceed the available logical address space if they are employed, due to the size of the software routines. RESOLUTION: Add the EMA instructions (hp2100_cpu5.c) to provide address space reductions in the Pascal compiler. Add CPU debug support and flags for the EMA instructions. STATUS: Fixed in version 3.8-0. 182. ENHANCEMENT: Add the Vector Instruction Set firmware instructions. VERSION: 3.7-3 OBSERVATION: VIS was used in some HP programs, notably SPICE. RESOLUTION: Add the VIS instructions (hp2100_cpu7.c) to provide support for HP-SPICE. Add CPU debug support and flags for the VIS instructions. STATUS: Fixed in version 3.8-0. 183. PROBLEM: Single-stepping through interrupts does not report instruction execution properly. VERSION: 3.7-3 OBSERVATION: When single-stepping, the simulator prints the next instruction to be executed before pausing for a command. When an interrupt is pending, the instruction printed is not correct. Moreover, a single-step command at this point will execute two instructions. CAUSE: There are two problems with the simulator. The first is with the simulator routine that prints the next instruction to be executed at the end of a step. It is not checking whether an interrupt is pending. The instruction printed is the next instruction that would have been executed, if there had not been an interrupt pending. But because there was an interrupt pending, the next instruction actually executed is the trap-cell instruction. The second problem is that the simulator is not counting down events during the trap cell instruction execution. During each normal instruction, the simulator decreases the event counter, including the step counter. But it omits the decrement for the trap cell instruction. So single-stepping with an interrupt pending actually causes two instruction executions: the trap-cell instruction, and the subsequent instruction (usually the target of the JMP or JSB in the trap cell). RESOLUTION: Modify "fprint_sym" (hp2100_sys.c) to check for a pending interrupt, and if so, to print the trap cell instruction instead of the instruction at PC. Modify "sim_instr" (hp2100_cpu.c) to decrement the event counter for trap cell instructions. STATUS: Fixed in version 3.8-0. 184. PROBLEM: The TTY output interrupt time is too short for MSU BASIC. VERSION: 3.7-3 OBSERVATION: When running MSU BASIC, this code eventually produces an "Indirect address loop, P: 37001 (STA 1,I)" error: 10 PRINT "HELLO WORLD!" 20 GOTO 10 30 END CAUSE: The TTY output rate is abnormally fast compared to the original hardware. The ASR-33 operated at 10 characters per second. The HP 2116 processor ran at about 600 instructions per millisecond. Therefore, the TTY would interrupt approximately every 60000 CPU instructions. But the default SIMH configuration (SERIAL_OUT_WAIT) is to interrupt every 100 instructions -- about 600x the rate of the actual Teletype. MSU BASIC (a contributed library program) maintains per-user I/O state buffers, one for each of four users, plus one for the I/O system. When a TTY interrupt occurs, the program copies the per-user state into the I/O state buffer, enters the TTY driver to output a character, copies the updated I/O state back to the per-user buffer, and returns to a monitor loop to wait for the completion interrupt, which would occur 100 milliseconds later on a real machine. It takes 85 instructions from the STC that starts the TTY output until the updated state copy is completed. With the TTIME default of 100 instructions, that is normally just enough time to complete the buffer transfer before another interrupt occurs. However, MSU BASIC also runs the time base generator (CLK) with a one-second period. The TBG interrupt handler takes from 25 to 71 instructions. If the TBG interrupts while the TTY event is active, it will absorb enough instructions to cause the TTY interrupt to occur before the updated state copy is finished. That leaves the per-user state buffer inconsistent. As a result of the TTY interrupt, that inconsistent buffer is copied to the I/O state buffer, and mayhem ensues. RESOLUTION: Lengthened the TTY output time in "tty_unit" (hp2100_stddev.c) from 100 to 200 instructions. STATUS: Fixed in version 3.8-0. 185. ENHANCEMENT: Add the SIGNAL/1000 firmware instructions. VERSION: 3.7-3 OBSERVATION: SIGNAL provides firmware acceleration for Fast Fourier Transforms and was used in some signal processing applications. RESOLUTION: Add the SIGNAL instructions (hp2100_cpu7.c). Add CPU debug support and flags for the SIGNAL instructions. STATUS: Fixed in version 3.8-0. 186. ENHANCEMENT: Add idle support to the HP 2100 simulator. VERSION: 3.8-0 OBSERVATION: The DOS and RTE operating systems keep the current time of day by counting TBG ticks. To maintain accurate time, a simulation must run continuously. Given this requirement for continuous operation, it would be helpful if the simulator idled the host processor when these operating systems were idle themselves. RESOLUTION: Alter "cpu_mod" to add SET CPU IDLE/NOIDLE commands, and alter "sim_instr" to add idle detection for DOS and RTE (hp2100_cpu.c). STATUS: Fixed in version 3.8-1. 187. ENHANCEMENT: Report the device and line number for Telnet connections. VERSION: 3.8-0 OBSERVATION: When connecting a Telnet client to a simulator device via the multiplexer library, the client receives a "welcome" message of the format: Connected to the HP2100 simulator It would be helpful if the user knew to which device and line the client had connected. For example: Connected to the HP2100 simulator MUX device, line 3 The report for single-line devices, e.g., additional terminal devices, would suppress the line number: Connected to the HP2100 simulator BACI device RESOLUTION: Modify sim_tmxr.h to add a "DEVICE *dptr" field at the end of the TMXR structure. Change tmxr_attach() to look up the device from the unit via find_dev_from_unit() and set "dptr" to point at the device. Change tmxr_poll_conn() to print the device name and line number (if more than one line defined) in the greeting message. STATUS: Fixed in version 3.8-1. 188. ENHANCEMENT: Add a simulation of the 12792C eight-channel multiplexer. VERSION: 3.8-0 OBSERVATION: The main terminal multiplexer for later RTEs was the 12792, and direct support was generated into primary systems from HP. The A/B/C revisions of the multiplexer firmware used the same protocol and drivers on RTE-IVB and RTE-6/VM. The D revision used an incompatible protocol and required different drivers that were supported only on RTE-6/VM. RESOLUTION: Add the MPX device (hp2100_mpx.c) to simulate the 12792A/B/C, and alter "hp2100_sys.c" and "hp2100_defs.h" to add the device structure and default select code assignment. STATUS: Fixed in version 3.8-1. 189. ENHANCEMENT: Add a mechanism to provide a device-specified connection order for terminal multiplexers. VERSION: 3.8-0 OBSERVATION: Some operating systems allow per-line device drivers for multiplexers (e.g., the HP 12792 and 12920 under RTE). These change the line behavior, so that the existing model of multiplexers as pools of identical lines is no longer valid. A method of specifying line connection order is needed, so that connection to specific device drivers is possible. RESOLUTION: Modify the TMXR structure (sim_tmxr.h) to add an "int32 *lnorder" field that points at an array specifying the line connection order. Modify "tmxr_poll_conn" (sim_tmxr.c) to connect in the order given by *lnorder, if defined, else to connect in ascending port number order. Add "tmxr_set_lnorder" and "tmxr_show_lnorder" routines to provide support for SET LINEORDER= and SHOW LINEORDER commands. STATUS: Fixed in version 3.8-1. 190. ENHANCEMENT: Add a simulation of the 12620A/12936A Privileged Interrupt Fences. VERSION: 3.8-0 OBSERVATION: Privileged DOS and RTE systems require the use of a privileged interrupt fence card. This is needed to run the 12920A 16-channel multiplexer under RTE. When configured for DIAG operation, the LPS device may be used as an RTE fence, although the corresponding line printer function is then lost. The DOS fence (12936A) had a unique operation that is not duplicated by any existing simulation. RESOLUTION: Add the PIF device (hp2100_pif.c) to simulate the 12620A and 12936A, and alter "hp2100_sys.c" and "hp2100_defs.h" to add the device structure and default select code assignment. STATUS: Fixed in version 3.8-1. 191. PROBLEM: The action of certain I/O cards (e.g., the 12936A Privileged Interrupt Fence) cannot be modelled by SIMH. VERSION: 3.8-0 OBSERVATION: Certain I/O actions cannot be implemented within the current design of the I/O simulation. For example, the 12936A card breaks the interrupt priority chain when flag OR control is set. Simulation assumes that priority is broken when flag AND control are set. CAUSE: The hardware has I/O signals for interrupt request (IRQ) and interrupt priority to lower-priority devices (PRL). These signals are not modelled directly in SIMH. Rather, they are implied by control, flag, and flag buffer set (for IRQ) and control and flag set (for PRL). If an I/O card does not follow these conventions, then the proper action cannot be simulated. RESOLUTION: Modify the I/O simulation structure to model hardware signals, rather than I/O instructions. Verify each simulated device's action in response to each I/O backplane signal. Verify each device's reset routine to ensure the proper response to RESET (POPIO signal) and RESET -P (PON signal). STATUS: Fixed in version 3.8-1. 192. PROBLEM: Escaping backslashes in DO commands does not work. VERSION: 3.8-0 OBSERVATION: The SIMH User's Guide says in Section 3.13, "Executing Command Files:" The string %n is recognized as meaning argument n from the DO command line. The character \ has the usual UNIX meaning of an escape character; the next character is interpreted literally, even if it is % or \. The sequence "\%" is recognized as a literal "%" character. The sequence "\\" is not recognized as a literal "\" character; instead, it is left unaltered. In fact, "\%" is the only recognized escape; "\" followed by any other character will not be processed, i.e., the "\" and that character will remain. This makes using parameters in Windows file paths impossible, as this: attach dev c:\path\to\\%1 substitutes for "%1" but leaves the double-backslashes, and this: attach dev c:\path\to\%1 ...does not substitute for "%1" and parses as "c:\path\to%1". Actually, the documented behavior (escaping every character) is undesirable, as it will invalidate every current command file that uses Windows path names. Were it implemented as documented, then a path such as "c:\path\to\file" would be parsed as "c:pathtofile". Even restricting the change to escaping just "\" and "%" will still invalidate current command files that use network paths (e.g., "\\server\\share\\path\to\file" will become "\server\share\path\to\file", which is a local path. This at least is fixable, whereas there is no workaround for the current situation. CAUSE: The argument substituter is checking only for the "\%" case. RESOLUTION: Modify "sub_args" (scp.c) to accept "\\" as a literal backslash, in addition to "\%" as a literal percent sign. STATUS: Fixed in version 3.8-1. 193. PROBLEM: The DR and IPL boot loaders do not work. VERSION: 3.8-0 OBSERVATION: Attempting to boot the DR or IPL devices results in a hang in the bootstrap. Examination shows that the I/O instructions are not being configured. CAUSE: The loader protection feature added at revision 3.7-0 must be turned off in order to write programmatically to the boot loader area. Protection is automatically disabled for the devices using the "ibl_copy" function in the CPU, but the DR and IPL devices install their bootstraps within their boot routines and do not call "ibl_copy". RESOLUTION: Modify "ipl_boot" (hp2100_ipl.c) and "drc_boot" (hp2100_dr.c) to use the "ibl_copy" routine. STATUS: Fixed in version 3.8-1. 194. PROBLEM: Omitted parameters to DO command files do not substitute null strings for the corresponding arguments. VERSION: 3.8-0 OBSERVATION: Given a command file "cmdfile" containing "echo %1 and %2", the command "do cmdfile a b" results in: a and b ...which is as expected. However, "do cmdfile a" results in: a and %2 ...which is unexpected; the expected response is: a and ...i.e., the null string is substituted for "%2". This would be consistent with argument substitution in operating system command shells. CAUSE: Arguments for omitted parameters are not being considered for substitution. RESOLUTION: Modify "do_cmd" (scp.c) to initialize omitted arguments to NULL and modify "sub_args" to skip null arguments during substitution. STATUS: Fixed in version 3.8-1. 195. PROBLEM: JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort. VERSION: 3.8-0 OBSERVATION: The upper bound of protected memory is set by the memory protect fence, and the lower bound is normally location 2. However, the lower bound is 0 if the instruction is a JMP, or if the instruction is a JSB and jumper W5 is out. That is, a JMP or a JSB (with W5 out) to any location under the memory protect fence will cause a violation. If the fence is set to or below the lower bound, though, then MP violations will not occur. However, a JSB 0 or JSB 1 with the fence set to 0 or 1 and jumper W5 out still causes an MP abort. CAUSE: Improper coding of the W5 test in the JSB simulation. RESOLUTION: Modify the instruction dispatcher "sim_instr" (hp2100_cpu.c) to set the protected lower bound for JSB as indicated by W5 and to test the target address against the lower bound as well as the MP fence. STATUS: Fixed in version 3.8-1. 196. PROBLEM: The MEM (DMS) violation register is not being set properly. VERSION: 3.8-0 OBSERVATION: The STC handler within the MP I/O instruction routine "proio" contains an explicit clear of the MEM violation register. There is no such action shown on either the MP or MEM card schematics. When the statement is removed, the Memory Expansion Module Diagnostic (DSN 102103) fails in TST21, the "Violation Register Map Bits Test," with an "E302 VR MAP 00" error. TST21 generates three MEM violations and one MP violation. The value of the MEM violation register is checked after all four violations to confirm that the map register address corresponds to the violation location. The value after the MP violation is in error; the violation register still contains the value from the prior MEM violation. CAUSE: The simulator updates the violation register whenever a MEM violation occurs. The hardware actually updates the violation register for every memory read, every memory write above the lower bound of protected memory, and every execution of a privileged DMS instruction. The register is "frozen" when MP is disabled by an MP or DM error to capture the state of the MEM (MEVFF sets or CTL5FF clears). Examining the violation register value after each MEM violation produces the expected result. Examining it after the MP violation does not, because the register is not being set. As it happens, the MP violation in the diagnostic occurs on page 0, so the problem is masked if the violation register is set to 0 when MP is enabled. Other bits in the register are wrong in this case, but the diagnostic does not check them. It would be proper to fix this problem by updating the violation register after each memory access, as is done in hardware. Fortunately, this isn't necessary, as the visible state of the violation register is only available via a programmed RVA/B instruction or via the SCP interface. Therefore, it is sufficient if the register is updated: - at a DM violation (when freezing) - at an MP violation (when freezing) - during RVA/B execution (if not frozen) - before returning to SCP after a simulator stop (if not frozen) The first of these conditions is currently implemented. The other three must be added to address the issue. RESOLUTION: Add a new "dms_upd_vr" routine (hp2100_cpu.c) to update the MEM violation register value. Modify the ABORT macro to take the address of the last memory access as the parameter. Modify the MP abort handler to use the memory address to update the MEM violation register. Add new update calls to the RVA/B simulator and to the cleanup code at the end of "sim_instr" before returning to SCP. STATUS: Fixed in version 3.8-1. 197. PROBLEM: The ME Bus Enabled bit in the MEM violation register is not being set properly. VERSION: 3.8-0 OBSERVATION: The ME Bus Enabled bit in the violation register reflects the state of the MEBEN (ME Bus Enable) signal on the MEM card. MEBEN is asserted if the MEM is enabled (MAPON signal) and the last memory access was not to the unmapped portion of the base page (OFA signal). Under simulation, this bit is set if a MEM violation is either a read violation or a write violation, and it is clear otherwise (base page or privileged instruction violation). This is correct only for the base page violation case; for the other three cases, MEBEN may be either high or low. CAUSE: Incorrect logic in the "dms_viol" routine. A base page violation, by definition, occurs if a write is attempted to the unmapped portion of the base page. MEBEN must be off in this case (BPV is qualified by -MEBEN). Each of the other three violations could occur with MEBEN either asserted or denied. Consider a read from the base page with map 0 indicating read protection. If the read is from the mapped portion, MEBEN will be asserted. If the read is from the unmapped portion, MEBEN will be denied. In either case, a read violation will be indicated. The same conditions pertain to a write to the base page with map 0 indicating write protection, and to an attempted privileged instruction execution from the base page. RESOLUTION: Modify "dms_upd_vr" (hp2100_cpu.c) to call a new "is_mapped" routine to determine if an access is to unmapped memory. STATUS: Fixed in version 3.8-1. 198. PROBLEM: JMP to a write-protected page fails to signal a DMS violation. VERSION: 3.8-0 OBSERVATION: The "21MX M-Series Computer Operating and Reference Manual" states on page 4-2: Any attempt to write to a write-protected page will result in a write violation and the memory will not be altered. In addition, if a page is write-protected, a jump or jump indirect instruction to that page will cause a write violation and the jump will not occur. The write violation for a JMP to a write-protected page does not occur. CAUSE: Coding error in "mp_dms_jmp". MEM write and base-page violations are checked when an MPCK micro-order is executed. MPCK asserts the MPCND signal if the address is above the lower bound of protected memory (0 for a JMP). MPCND is qualified with the accessed page's write-protect bit for write violations, and with an "unmapped access to the base page" signal for base-page violations. Any instruction that executes MPCK will enable these two violation checks, and all jump-type instructions do. Under simulation, the MEM check for base-page violations is done by the "mp_dms_jmp" routine. However, this routine does not check for write violations. RESOLUTION: Modify "mp_dms_jmp" (hp2100_cpu.c) to check for write violations as well as base-page violations. STATUS: Fixed in version 3.8-1. 199. PROBLEM: .GOTO to A/B causes incorrect MP violation. VERSION: 3.8-0 OBSERVATION: The lower bound of protected memory is normally location 2, allowing unrestricted access to the A and B registers (locations 0 and 1, respectively). The MP card checks the instruction register and uses a lower bound of 0 for JMP, as well as for JSB if jumper W5 is out. The JLY and JPY microcode also requests a lower bound of 0 by setting the IR to the JMP opcode before the MP check. Under simulation, the MP check against a lower bound of 0 is done by the "mp_dms_jmp" routine (hp2100_cpu.c). However, this routine is also called for the DJP, SJP, UJP, JRS, and .GOTO instructions. These latter instructions should allow access to the A/B registers, but they don't. CAUSE: Logic error. RESOLUTION: Modify "mp_dms_jmp" (hp2100_cpu.c) to accept the protected lower bound as a parameter, and modify the JMP, JSB, JLY, JPY, DJP, SJP, UJP, JRS, and .GOTO instruction handlers (hp2100_cpu.c, hp2100_cpu2.c, and hp2100_cpu3.c) to pass the desired lower bound value. STATUS: Fixed in version 3.8-1. 200. PROBLEM: UJP fails erroneously with a write-protect violation. VERSION: 3.8-0 OBSERVATION: Attempting to enable the user map with a UJP instruction fails if the target page in the system map is write-protected. CAUSE: The instruction is checking the jump target in the wrong map. The DJP, SJP, and UJP instructions validate that the target address is not below the MP fence and on a write-protected page. In firmware, these instructions alter the Memory Expansion Unit before validating the jump address. For example, UJP enables the user map and then checks the target address using the user map. Under simulation, the "mp_dms_jmp" check is issued before the map is changed, leading to failures. RESOLUTION: Modify "cpu_dms" (hp2100_cpu2.c) to move the "mp_dms_jmp" checks to after the MEU update for the DJP, SJP, and UJP instructions. STATUS: Fixed in version 3.8-1. 201. PROBLEM: Several HP 2100 devices use registers variables < 32 bits without REG_FIT. VERSION: 3.8-0 OBSERVATION: Scalar register variables must be either 32 bits in size or declared with the REG_FIT flag. In the absence of REG_FIT, a 32-bit access is assumed by the examine and deposit routines. Several HP 2100 devices use 8- or 16-bit scalar variables as registers without REG_FIT. This will cause failures on big-endian machines. Note that arrayed registers are automatically accessed at the minimum size implied by the "width" and "offset" values and therefore do not need REG_FIT. CAUSE: Coding errors. RESOLUTION: Modify hp2100_baci.c, hp2100_dp.c, hp2100_dq.c, and hp2100_mpx.c to add REG_FIT where needed. STATUS: Fixed in version 3.8-1. 202. PROBLEM: The HP 2100 CPU simulation shadows the A and B registers unnecessarily. VERSION: 3.8-0 OBSERVATION: The A and B register values are stored in two places: as "uint16 ABREG[2]" duing execution, and as "uint32 saved_AR" and "uint32 saved_BR" between executions. The latter was to accommodate the requirement that register variables must be 32 bits in size. That requirement was removed in version 3.5-2 for registers declared with the REG_FIT flag. With REG_FIT, "ABREG[0]" and "ABREG[1]" can be used directly as register values, and the code associated with saving and restoring the A and B registers can be eliminated. CAUSE: The code wasn't updated to take advantage of the new feature. RESOLUTION: Modify hp2100_cpu.c to use "ABREG[]" variables as register values with REG_FIT, and remove the "saved_AR" and "saved_BR" values and associated code. Modify "msc_boot" (hp2100_ms.c) to use "AR" instead of "saved_AR". STATUS: Fixed in version 3.8-1. 203. PROBLEM: RTE break mode does not work with the 12920A multiplexer on fast host machines. VERSION: 3.8-0 OBSERVATION: Hitting the BREAK key when the terminal is idle or output is in progress should produce an RTE prompt. BREAK works properly when the terminal is idle, but hitting BREAK when output is in progress does not produce a prompt. This means that long outputs can be neither paused nor aborted. CAUSE: The RTE multiplexer driver is a privileged driver. Privileged drivers bypass RTE to provide rapid interrupt handling. To inform RTE that an operation is complete, e.g., that a line has been written, the interrupt section of the driver sets a device timeout of one clock tick (10 milliseconds). When that timeout occurs, RTE is entered normally to complete the I/O transaction. While the completion timeout is pending, the driver ignores any further interrupts from the multiplexer line. The maximum communication rate for the multiplexer is 2400 baud, or approximately 4.2 milliseconds per character transferred. A typical line of 20 characters would therefore take ~85 milliseconds, plus the 10 millisecond completion timeout, or about 95 milliseconds total. BREAK recognition would be ignored for roughly 10% of that time. At lower baud rates, recognition would be ignored for a correspondingly smaller percentage of the time. However, SIMH uses an optimized timing of 500 instructions per character transfer, rather than the ~6600 instructions that a character transfer should take, and so a typical 20-character line will take about 11,000 instructions. On the other hand, the clock tick is calibrated to real time, and 10 milliseconds of real time takes about 420,000 instructions on a 2.0 GHz PC. To be recognized, then, the BREAK key must be pressed in a window that is open for about 2.5% of the time. Therefore, the BREAK key will be ignored about 97.5% of the time, and RTE break-mode effectively will not work. RESOLUTION: Defer BREAK recognition until either a character is output or a second successive input poll occurs, providing that we are not in diagnostic mode. This ensures that the BREAK interrupt will be accepted. Added a "mux_defer[]" flag (hp2100_mux.c) to record break deferrals. STATUS: Fixed in version 3.8-1. 204. ENHANCEMENT: Add line connection order support to the 12920A multiplexer. VERSION: 3.8-0 OBSERVATION: RTE and DOS provide per-line device drivers for the 12920A multiplexer. A method of specifying line connection order is needed, so that connection to specific device drivers is possible. RESOLUTION: Add "SET MUX LINEORDER=" and "SHOW MUX LINEORDER" commands to the 12920A simulator. STATUS: Fixed in version 3.8-1. 205. PROBLEM: The LOCKED, WRITEENABLED, and FORMAT commands do not work as documented. VERSION: 3.8-0 OBSERVATION: The HP2100 documentation says that the "SET MTC LOCKED" command write-locks the tape drive. Attempting this, however, results in a "Non-existent parameter" error. CAUSE: The commands are part of the wrong command table (MTD instead of MTC). RESOLUTION: Modify "mtd_mod" and "mtc_mod" (hp2100_mt.c) to move the LOCKED, WRITEENABLED, and FORMAT commands to the command channel to be compatible with the MS tape device. STATUS: Fixed in version 3.8-1. 206. PROBLEM: Wrong mnemonic reported in IAK display for RTE-6/VM OS dual-use microcode instructions. VERSION: 3.8-0 OBSERVATION: RTE-6/VM OS instructions have four "dual-use" opcodes. These have different meanings, and thus different mnemonics, depending on whether they are used in interrupt trap cells or not. For example, the 105357 opcode is $TBG (time-base generator interrupt handler) if in a trap cell and .DSPI (set display indicator) if not. The mnemonic is correct for the EXAMINE command. A single-step through an interrupt acknowledgement displays the wrong mnemonic: [CTRL+E] Simulation stopped, P: 02040 (JMP 2037) sim> s Step expired, P: 02037 (IAK 15: .DSPI) sim> e -m 15 15: $TBG The IAK report should also display $TBG. CAUSE: The "fprint_sym" routine detects the IAK and obtains the trap cell value, but it fails to change the instruction address, so the address remains that of the interrupted instruction. As dual-use mnemonics depend on the instruction address, the mnemonic reported is incorrect. RESOLUTION: Modify "fprint_sym" (hp2100_sys.c) to set the instruction address for an interrupt acknowledgement to the interrupt trap cell address. STATUS: Fixed in version 3.8-1. 207. PROBLEM: The 3030 mag tape does not interrupt after a CLR command. VERSION: 3.8-0 OBSERVATION: Page 2-6 of the 12559A 9-Track Magnetic Tape Unit Interface Kit Operating and Service Manual says that the CLR command channel flag flip-flop when the command completes. The MT simulator does not, so the command channel does not interrupt. CAUSE: Coding error. RESOLUTION: Modify "mtcio" (hp2100_mt.c) to set the command channel flag when the command completes. STATUS: Fixed in version 3.8-1. 208. PROBLEM: Exiting the simulator does not report "Disconnected from the HP 2100 simulator" on MUX sessions. VERSION: 3.8-0 OBSERVATION: Exiting the simulator detaches all devices. Detaching multiplexer-type devices, such as BACI and MPX, reports "Disconnected from the simulator" on each connected Telnet session. However, no such report appears on MUX sessions. Doing a DETACH ALL does report the disconnection on MUX sessions. CAUSE: As part of simulator shutdown, "detach_all" is called with the "shutdown" parameter set to TRUE. This causes the detach routines for non-attachable units to be called. "ds_detach" calls "detach_unit", which returns SCPE_NOATT for unit 8 (the controller unit). "detach_all" exits on a non-zero status return from a device detach routine, so the devices after DS in the device array are never detached. Doing a DETACH ALL calls "detach_all" with the "shutdown" parameter set to FALSE. This calls device detach routines only for attached units. All of these return SCPE_OK, so MUX is eventually detached, and the disconnection reports are sent to the MUX sessions. Note that the same problem would arise if an attached unit fails to detach cleanly, e.g., due to a file write error. The remaining devices would not be detached before simulator exit. RESOLUTION: Modify "detach_all" (scp.c) to ignore errors from device detach routines during shutdown, so that all devices will be detached. STATUS: Fixed in version 3.8-1. 209. ENHANCEMENT: Add a microcode simulation module for site-specific microprograms. VERSION: 3.8-0 OBSERVATION: The 2100 and 1000 CPUs supported user microprogramming. A user of the HP2100 simulator may have user-written microcode that he would like to add to SIMH. It would be helpful to have the infrastructure in place to aid in the implementation of site-specific microprogram simulations. RESOLUTION: Add a new "cpu_user" microcode dispatcher and an example skeleton microcode simulator (hp2100_cpu0.c). Alter "cpu_uig_0" and "cpu_uig_1" (hp2100_cpu1.c) to route any instruction not allocated to an installed firmware option to the user-microcode dispatcher. In the absence of a user-microcode simulation for a given instruction, execution will cause an undefined instruction stop. STATUS: Fixed in version 3.8-1. 210. PROBLEM: The VIS and IOP options conflict on the 1000-F. VERSION: 3.8-0 OBSERVATION: The Vector Instruction Set and 2000/Access I/O Processor instructions share the same opcode range. Only one or the other should be present at a time. The SET CPU option processor does not enforce this. CAUSE: The case was overlooked when VIS was added. RESOLUTION: Modify "cpu_set_opt" (hp2100_cpu.c) to make the VIS and IOP options mutually exclusive on the 1000-F. STATUS: Fixed in version 3.8-1. 211. PROBLEM: Pressing BREAK on a BACI terminal under RTE locks the card. VERSION: 3.8-0 OBSERVATION: Under RTE, pressing the BREAK key on a BACI terminal session will cause that session to lock up. If the session was writing, it will resume in four seconds. If it was reading or idle, it will re-enable after the timeout period specified for the device in RTE. If no timeout was specified, then the session will remain locked forever. CAUSE: The RTE driver operates the BACI in character mode. Normally, pressing a key while the session is writing or idle will bring up the RTE system prompt. Pressing BREAK enters a NUL into the FIFO and interrupts with the BREAK CONDITION bit set in the status word. The driver ignores this by sending a "reset break status" command and then re-enabling interrupts with an STC sc,C instruction. But the original interrupt was caused not by the BREAK status (there is no explicit interrupt-on-BREAK function) but rather by the presence of a received character in the FIFO. So the BACI attempts to interrupt again, this time with the BREAK CONDITION bit clear. This second interrupt would normally be allowed, as the STC sc,C instruction clears the "interrupt lockout" flag that was set when the first interupt was generated. However, the STC signal handler checks for interrupt status between the STC and the succeeding CLF, rather than after the CLF. The result is that the STC clears lockout, then the FIFO status sets the flag and lockout, and then the CLF clears the flag, leaving interrupt lockout set. At that point, no interrupt is pending, and no future interrupts can occur, because the lockout flag prevents them. When device timeout occurs, the card is reinitialized and then re-enabled, so it becomes responsive again. RESOLUTION: Modify "baci_io" (hp2100_baci.c) to update the interrupt status after the CLF of a STC sc,C instruction. STATUS: Fixed in version 3.8-1. 212. PROBLEM: Setting a breakpoint on an interrupt trap cell does not work. VERSION: 3.8-0 OBSERVATION: If a breakpoint is set on an interrupt trap cell, the breakpoint does not trip when the corresponding interrupt occurs and the trap cell contents are executed. CAUSE: The breakpoint detection code is only in the "normal instruction" execution path. RESOLUTION: Modify "sim_instr" (hp2100_cpu.c) to add breakpoint detection to the "interrupt trap cell" execution path. STATUS: Fixed in version 3.8-1. simh-3.8.1/HP2100/hp2100_cpu4.c0000644000175000017500000015662511062235634013505 0ustar vlmvlm/* hp2100_cpu4.c: HP 1000 FPP/SIS Copyright (c) 2006-2008, J. David Bryan 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 AUTHOR 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 author. CPU4 Floating Point Processor and Scientific Instruction Set 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 18-Mar-08 JDB Fixed B register return bug in /CMRT 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #if defined (HAVE_INT64) /* int64 support available */ #include "hp2100_fp1.h" /* Floating-Point Processor. The 1000 F-Series replaces the six 2100/1000-M/E single-precision firmware floating-point instructions with a hardware floating-point processor (FPP). The FPP executes single-, extended-, and double-precision floating-point instructions, as well as double-integer instructions. All of the floating-point instructions, as well as the single- and double-integer fix and float instructions, are handled here. Pure double-integer instructions are dispatched to the double-integer handler for simulation. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A N/A std For the F-Series, the instruction codes are mapped to routines as follows: Instr. 1000-F Description ------ ------ ------------------------------------- 105000 FAD Single real add 105001 .XADD Extended real add 105002 .TADD Double real add 105003 [EAD] [5-word add] 105004 [tst] [Floating Point Processor self test] 105005 [xpd] [Expand exponent] 105006 [rst] [Floating Point Processor reset] 105007 [stk] [Process stack of operands] 105010 [chk] [FPP addressing check] 105014 .DAD Double integer add 105020 FSB Single real subtract 105021 .XSUB Extended real subtract 105022 .TSUB Double real subtract 105023 [ESB] [5-word subtract] 105034 .DSB Double integer subtract 105040 FMP Single real multiply 105041 .XMPY Extended real multiply 105042 .TMPY Double real multiply 105043 [EMP] [5-word multiply] 105054 .DMP Double integer multiply 105060 FDV Single real divide 105061 .XDIV Extended real divide 105062 .TDIV Double real divide 105063 [EDV] [5-word divide] 105074 .DDI Double integer divide 105100 FIX Single real to integer fix 105101 .XFXS Extended real to integer fix (.DINT) 105102 .TXFS Double real to integer fix (.TINT) 105103 [EFS] [5-word FIXS] 105104 .FIXD Real to double integer fix 105105 .XFXD Extended real to double integer fix 105106 .TFXD Double real to double integer fix 105107 [EFD] [5-word FIXD] 105114 .DSBR Double integer subtraction (reversed) 105120 FLT Integer to single real float 105121 .XFTS Integer to extended real float (.IDBL) 105122 .TFTS Integer to double real float (.ITBL) 105123 [ELS] [5-word FLTS] 105124 .FLTD Double integer to real float 105125 .XFTD Double integer to extended real float 105126 .TFTD Double integer to double real float 105127 [ELD] [5-word FLTD] 105134 .DDIR Double integer divide (reversed) Implementation note: rather than have two simulators that each executes the single-precision FP instruction set, we compile conditionally, based on the availability of 64-bit integer support in the host compiler. 64-bit integers are required for the FPP, so if they are available, then we handle the single-precision instructions for the 2100 and M/E-Series here, and the firmware simulation is omitted. If support is unavailable, then the firmware function is used instead. Notes: 1. Single-precision arithmetic instructions (.FAD, etc.) and extended- and double-precision F-Series FPP arithmetic instructions (.XADD, .TADD, etc.) return positive infinity on both positive and negative overflow. The equivalent extended-precision M/E-Series FFP instructions return negative infinity on negative overflow and positive infinity on positive overflow. 2. The items in brackets above are undocumented instructions that are used by the 12740 FPP-SIS-FFP diagnostic only. 3. The five-word arithmetic instructions (e.g., 105003) use an expanded operand format that dedicates a separate word to the exponent. See the implementation notes in the hardware floating-point processor simulation for details. 4. The "self test" instruction (105004) returned to P+1 for early F-Series units without double-integer support. Units incorporating such support returned to P+2. 5. The "expand exponent" instruction (105005) is used as a "prefix" instruction to enable a 10-bit exponent range. It is placed immediately before a 5-word arithmetic instruction sequence, e.g., immediately preceding an EAD instruction sequence. The arithmetic instruction executes normally, except that under/overflow is not indicated unless the exponent exceeds the 10-bit range, instead of the normal 8-bit range. If overflow is indicated, the exponent is still set to +128. Note that as 2-, 3-, and 4-word packed numbers only have room for 8-bit exponents, the Expand Exponent instruction serves no useful purpose in conjunction with instructions associated with these precisions. If used, the resulting values may be in error, as overflow from the 8-bit exponents will not be indicated. 6. The "FPP reset" instruction (105006) is provided to reset a hung box, e.g., in cases where an improper number of parameters is supplied. The hardware resets its internal state machine in response to this instruction. Under simulation, the instruction has no effect, as the simulated FPP cannot hang. 7. The "process stack" instruction (105007) executes a series of FPP instruction sets in sequence. Each set consists of a single FPP instruction and associated operands that specifies the operation, followed by a "result" instruction and operand. The result instruction is optional and is only used to specify the result precision; the instruction itself is not executed. If the result instruction is NOP, then the result precision is that of the executed FPP instruction. If the result operand is null, then the result is kept in the internal FPP accumulator for later use. The calling sequence is as follows: STK Process stack instruction DEF ERRTN Address of error return DEF SET1 Address of first instruction set DEF SET2 Address of second instruction set . . . ERRTN EQU * Return here if execution in error OKRTN EQU * Return here if execution OK Instruction sets are specified as follows (e.g.): SET1 .TADD Operation instruction (NOP to terminate series) DEC 4 Number of words in first operand (or 0 if accum) DEF OP1 Address of first operand DEC 4 Number of words in second operand (or 0 if accum) DEF OP2 Address of second operand .XADD Result precision conversion instruction (or NOP) DEC 3 Number of words to store (or 0 if no store) DEF RSLT Address of buffer to hold value The primary use of the "process stack" instruction is to enable chained operations employing the FPP accumulator for intermediate results and to enable expanded exponent usage across multiple instructions. 8. The "addressing check" instruction sets bit 0 of the L register to 1, copies the X register value to the FPP, and then reads the FPP and stores the result in the Y register. Setting the L register bit 0 to 1 normally deselects the FPP, so that the value in Y is 177777. However, the FPP box has a strap that inverts the selection logic, even though the box will not work with the base-set firmware if this is done. The "addressing check" instruction is provided to test whether the strap is in the alternate location. Under simulation, the return value is always 177777, indicating that the strap is correctly set. Additional references: - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) - FPP-SIS-FFP Diagnostic Source (12740-18001, Rev. 1926) */ static const OP_PAT op_fpp[96] = { OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FAD .XADD .TADD .EADD */ OP_N, OP_C, OP_N, OP_A, /* [tst] [xpd] [rst] [stk] */ OP_N, OP_N, OP_N, OP_N, /* [chk] --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* .DAD --- --- --- */ OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FSB .XSUB .TSUB .ESUB */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* .DSB --- --- --- */ OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FMP .XMPY .TMPY .EMPY */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* .DMP --- --- --- */ OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FDV .XDIV .TDIV .EDIV */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* .DDI --- --- --- */ OP_R, OP_X, OP_T, OP_E, /* FIX .XFXS .TFXS .EFXS */ OP_R, OP_X, OP_T, OP_E, /* .FIXD .XFXD .TFXD .EFXD */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N, /* .DSBR --- --- --- */ OP_I, OP_IA, OP_IA, OP_IA, /* FLT .XFTS .TFTS .EFTS */ OP_J, OP_JA, OP_JA, OP_JA, /* .FLTD .XFTD .TFTD .EFTD */ OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ OP_N, OP_N, OP_N, OP_N /* .DDIR --- --- --- */ }; t_stat cpu_fpp (uint32 IR, uint32 intrq) { OP fpop; OPS op; OPSIZE op1_prec, op2_prec, rslt_prec, cvt_prec; uint16 opcode, rtn_addr, stk_ptr; uint32 entry; t_stat reason = SCPE_OK; if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-Series? */ opcode = (uint16) (IR & 0377); /* yes, use full opcode */ else opcode = (uint16) (IR & 0160); /* no, use 6 SP FP opcodes */ entry = opcode & 0177; /* map to <6:0> */ if (op_fpp[entry] != OP_N) if (reason = cpu_ops (op_fpp[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<6:0> */ case 0000: /* FAD 105000 (OP_RF) */ case 0020: /* FSB 105020 (OP_RF) */ case 0040: /* FMP 105040 (OP_RF) */ case 0060: /* FDV 105060 (OP_RF) */ O = fp_exec (opcode, &fpop, op[0], op[1]); /* execute operation */ AR = fpop.fpk[0]; /* return result to A/B */ BR = fpop.fpk[1]; break; case 0001: /* .XADD 105001 (OP_AXX) */ case 0002: /* .TADD 105002 (OP_ATT) */ case 0003: /* .EADD 105003 (OP_AEE) */ case 0021: /* .XSUB 105021 (OP_AXX) */ case 0022: /* .TSUB 105022 (OP_ATT) */ case 0023: /* .ESUB 105023 (OP_AEE) */ case 0041: /* .XMPY 105041 (OP_AXX) */ case 0042: /* .TMPY 105042 (OP_ATT) */ case 0043: /* .EMPY 105043 (OP_AEE) */ case 0061: /* .XDIV 105061 (OP_AXX) */ case 0062: /* .TDIV 105062 (OP_ATT) */ case 0063: /* .EDIV 105063 (OP_AEE) */ O = fp_exec (opcode, &fpop, op[1], op[2]); /* execute operation */ fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */ WriteOp (op[0].word, fpop, rslt_prec); /* write result */ break; case 0004: /* [tst] 105004 (OP_N) */ XR = 3; /* firmware revision */ SR = 0102077; /* test passed code */ PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DBI */ break; case 0005: /* [xpd] 105005 (OP_C) */ return cpu_fpp (op[0].word | 0200, intrq); /* set bit 7, execute instr */ case 0006: /* [rst] 105006 (OP_N) */ break; /* do nothing for FPP reset */ case 0007: /* [stk] 105007 (OP_A) */ O = 0; /* clear overflow */ stk_ptr = PC; /* save ptr to next buf */ rtn_addr = op[0].word; /* save return address */ while (TRUE) { PC = ReadW (stk_ptr) & VAMASK; /* point at next instruction set */ stk_ptr = (stk_ptr + 1) & VAMASK; reason = cpu_ops (OP_CCACACCA, op, intrq); /* get instruction set */ if (reason) { PC = err_PC; /* irq restarts */ break; } if (op[0].word == 0) { /* opcode = NOP? */ PC = (rtn_addr + 1) & VAMASK; /* bump to good return */ break; /* done */ } fp_prec ((uint16) (op[0].word & 0377), /* determine operand precisions */ &op1_prec, &op2_prec, &rslt_prec); if (TO_COUNT(op1_prec) != op[1].word) { /* first operand precisions agree? */ PC = rtn_addr; /* no, so take error return */ break; } else if (op1_prec != fp_a) /* operand in accumulator? */ op[1] = ReadOp (op[2].word, op1_prec); /* no, so get operand 1 */ if (TO_COUNT(op2_prec) != op[3].word) { /* second operand precisions agree? */ PC = rtn_addr; /* no, so take error return */ break; } else if (op2_prec != fp_a) /* operand in accumulator? */ op[2] = ReadOp (op[4].word, op2_prec); /* no, so get operand 2 */ O = O | /* execute instruction */ fp_exec ((uint16) (op[0].word & 0377), /* and accumulate overflow */ &fpop, op[1], op[2]); if (op[5].word) { /* precision conversion? */ fp_prec ((uint16) (op[5].word & 0377), /* determine conversion precision */ NULL, NULL, &cvt_prec); fpop = fp_accum (NULL, cvt_prec); /* convert result */ } else /* no conversion specified */ cvt_prec = rslt_prec; /* so use original precision */ if (op[6].word) /* store result? */ WriteOp (op[7].word, fpop, cvt_prec); /* yes, so write it */ } break; case 0010: /* [chk] 105010 (OP_N) */ YR = 0177777; /* -1 if selection strap OK */ break; case 0014: /* .DAD 105014 (OP_N) */ return cpu_dbi (0105321, intrq); /* remap to double int handler */ case 0034: /* .DSB 105034 (OP_N) */ return cpu_dbi (0105327, intrq); /* remap to double int handler */ case 0054: /* .DMP 105054 (OP_N) */ return cpu_dbi (0105322, intrq); /* remap to double int handler */ case 0074: /* .DDI 105074 (OP_N) */ return cpu_dbi (0105325, intrq); /* remap to double int handler */ case 0100: /* FIX 105100 (OP_R) */ case 0101: /* .XFXS 105101 (OP_X) */ case 0102: /* .TFXS 105102 (OP_T) */ case 0103: /* .EFXS 105103 (OP_E) */ O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */ AR = fpop.fpk[0]; /* save result */ break; case 0104: /* .FIXD 105104 (OP_R) */ case 0105: /* .XFXD 105105 (OP_X) */ case 0106: /* .TFXD 105106 (OP_T) */ case 0107: /* .EFXD 105107 (OP_E) */ O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */ AR = (fpop.dword >> 16) & DMASK; /* save result */ BR = fpop.dword & DMASK; /* in A and B */ break; case 0114: /* .DSBR 105114 (OP_N) */ return cpu_dbi (0105334, intrq); /* remap to double int handler */ case 0120: /* FLT 105120 (OP_I) */ case 0124: /* .FLTD 105124 (OP_J) */ O = fp_exec (opcode, &fpop, op[0], NOP); /* float to single */ AR = fpop.fpk[0]; /* save result */ BR = fpop.fpk[1]; /* into A/B */ break; case 0121: /* .XFTS 105121 (OP_IA) */ case 0122: /* .TFTS 105122 (OP_IA) */ case 0123: /* .EFTS 105123 (OP_IA) */ case 0125: /* .XFTD 105125 (OP_JA) */ case 0126: /* .TFTD 105126 (OP_JA) */ case 0127: /* .EFTD 105127 (OP_JA) */ O = fp_exec (opcode, &fpop, op[0], NOP); /* float integer */ fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */ WriteOp (op[1].word, fpop, rslt_prec); /* write result */ break; case 0134: /* .DDIR 105134 (OP_N) */ return cpu_dbi (0105326, intrq); /* remap to double int handler */ default: /* others undefined */ reason = stop_inst; } return reason; } /* Scientific Instruction Set. The SIS adds single-precision trigonometric and logarithmic, and double-precision polynomial evaluation instructions to the 1000-F instruction set. The SIS is standard on the 1000-F. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A N/A std The routines are mapped to instruction codes as follows: Instr. 1000-F Description ------ ------ ---------------------------------------------- TAN 105320 Tangent SQRT 105321 Square root ALOG 105322 Natural logarithm ATAN 105323 Arc tangent COS 105324 Cosine SIN 105325 Sine EXP 105326 E to the power X ALOGT 105327 Common logarithm TANH 105330 Hyperbolic tangent DPOLY 105331 Double-precision polynomial evaluation /CMRT 105332 Double-precision common range reduction /ATLG 105333 Compute (1-x)/(1+x) for .ATAN and .LOG .FPWR 105334 Single-precision exponentiation .TPWR 105335 Double-precision exponentiation [tst] 105337 [self test] The SIS simulation follows the F-Series SIS microcode, which, in turn, follows the algebraic approximations given in the Relocatable Library manual descriptions of the equivalent software routines. Notes: 1. The word following the DPOLY instruction contains up to three flag bits to indicate one of several polynomial forms to evaluate. The comments in the DPOLY software library routine source interchange the actions of the bit 14 and bit 0 flags. The DPOLY description in the Technical Reference Handbook is correct. 2. Several instructions (e.g., DPOLY) are documented as leaving undefined values in the A, B, X, Y, E, or O registers. Simulation does not attempt to reproduce the same values as would be obtained with the hardware. 3. The SIS uses the hardware FPP of the F-Series. FPP malfunctions are detected by the SIS firmware and are indicated by a memory-protect violation and setting the overflow flag. Under simulation, malfunctions cannot occur. 4. We use OP_IIT for the .FPWR operand pattern. The "II" is redundant, but it aligns the operands with the OP_IAT of .TPWR, so the code may be shared. Additional references: - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) - HP 1000 E-Series and F-Series Computer Microprogramming Reference Manual (02109-90004, Apr-1980). */ /* Common single-precision range reduction for SIN, COS, TAN, and EXP. This routine is called by the SIN, COS, TAN, and EXP handlers to reduce the range of the argument. Reduction is performed in extended-precision. We calculate: multiple = (nearest even integer to argument * multiplier) argument = argument * multiplier - multiple */ static uint32 reduce (OP *argument, int32 *multiple, OP multiplier) { OP product, count; uint32 overflow; fp_cvt (argument, fp_f, fp_x); /* convert to extended precision */ fp_exec (0041, &product, *argument, multiplier); /* product = argument * multiplier */ overflow = fp_exec (0111, &count, NOP, NOP); /* count = FIX (acc) */ if ((int16) count.word >= 0) /* nearest even integer */ count.word = count.word + 1; count.word = count.word & ~1; *multiple = (int16) count.word; if (overflow == 0) { /* in range? */ fp_exec (0121, ACCUM, count, NOP); /* acc = FLT (count) */ overflow = fp_exec (0025, ACCUM, product, NOP); /* acc = product - acc */ *argument = fp_accum (NULL, fp_f); /* trim to single-precision */ } return overflow; } /* SIS dispatcher. */ static const OP_PAT op_sis[16] = { OP_R, OP_R, OP_R, OP_R, /* TAN SQRT ALOG ATAN */ OP_R, OP_R, OP_R, OP_R, /* COS SIN EXP ALOGT */ OP_R, OP_CATAKK, OP_AAT, OP_A, /* TANH DPOLY /CMRT /ATLG */ OP_IIF, OP_IAT, OP_N, OP_N /* .FPWR .TPWR --- [tst] */ }; t_stat cpu_sis (uint32 IR, uint32 intrq) { OPS op; OP arg, coeff, pwr, product, count, result; int16 f, p; int32 multiple, power, exponent, rsltexp; uint32 entry, i; t_bool flag, sign; t_stat reason = SCPE_OK; static const OP tan_c4 = { { 0137763, 0051006 } }; /* DEC -4.0030956 */ static const OP tan_c3 = { { 0130007, 0051026 } }; /* DEC -1279.5424 */ static const OP tan_c2 = { { 0040564, 0012761 } }; /* DEC 0.0019974806 */ static const OP tan_c1 = { { 0045472, 0001375 } }; /* DEC 0.14692695 */ static const OP alog_c3 = { { 0065010, 0063002 } }; /* DEC 1.6567626301 */ static const OP alog_c2 = { { 0125606, 0044404 } }; /* DEC -2.6398577035 */ static const OP alog_c1 = { { 0051260, 0037402 } }; /* DEC 1.2920070987 */ static const OP atan_c4 = { { 0040257, 0154404 } }; /* DEC 2.0214656 */ static const OP atan_c3 = { { 0132062, 0133406 } }; /* DEC -4.7376165 */ static const OP atan_c2 = { { 0047407, 0173775 } }; /* DEC 0.154357652 */ static const OP atan_c1 = { { 0053447, 0014002 } }; /* DEC 1.3617611 */ static const OP sin_c4 = { { 0132233, 0040745 } }; /* DEC -0.000035950439 */ static const OP sin_c3 = { { 0050627, 0122361 } }; /* DEC 0.002490001 */ static const OP sin_c2 = { { 0126521, 0011373 } }; /* DEC -0.0807454325 */ static const OP sin_c1 = { { 0062207, 0166400 } }; /* DEC 0.78539816 */ static const OP cos_c4 = { { 0126072, 0002753 } }; /* DEC -0.00031957 */ static const OP cos_c3 = { { 0040355, 0007767 } }; /* DEC 0.015851077 */ static const OP cos_c2 = { { 0130413, 0011377 } }; /* DEC -0.30842483 */ static const OP cos_c1 = { { 0040000, 0000002 } }; /* DEC 1.0 */ static const OP sqrt_a2 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */ static const OP sqrt_b2 = { { 0065324, 0126377 } }; /* DEC 0.4173076 */ static const OP sqrt_a1 = { { 0065324, 0126400 } }; /* DEC 0.8346152 */ static const OP sqrt_b1 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */ static const OP exp_c2 = { { 0073000, 0070771 } }; /* DEC 0.05761803 */ static const OP exp_c1 = { { 0056125, 0041406 } }; /* DEC 5.7708162 */ static const OP tanh_c3 = { { 0050045, 0022004 } }; /* DEC 2.5045337 */ static const OP tanh_c2 = { { 0041347, 0101404 } }; /* DEC 2.0907609 */ static const OP tanh_c1 = { { 0052226, 0047375 } }; /* DEC 0.16520923 */ static const OP minus_1 = { { 0100000, 0000000 } }; /* DEC -1.0 */ static const OP plus_1 = { { 0040000, 0000002 } }; /* DEC +1.0 */ static const OP plus_half = { { 0040000, 0000000 } }; /* DEC +0.5 */ static const OP ln_2 = { { 0054271, 0006000 } }; /* DEC 0.6931471806 (ln 2.0) */ static const OP log_e = { { 0067455, 0166377 } }; /* DEC 0.43429228 (log e) */ static const OP pi_over_4 = { { 0062207, 0166400 } }; /* Pi / 4.0 */ static const OP pi_over_2 = { { 0062207, 0166402 } }; /* Pi / 2.0 */ static const OP four_over_pi = { { 0050574, 0140667, 0023402 } }; /* 4.0 / Pi */ static const OP two_over_ln2 = { { 0056125, 0016624, 0127404 } }; /* 2.0 / ln(2.0) */ static const OP t_one = { { 0040000, 0000000, 0000000, 0000002 } }; /* DEY 1.0 */ entry = IR & 017; /* mask to entry point */ if (op_sis[entry] != OP_N) if (reason = cpu_ops (op_sis[entry], op, intrq)) /* get instruction operands */ return reason; switch (entry) { /* decode IR<3:0> */ case 000: /* TAN 105320 (OP_R) */ O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */ if (O) { /* out of range? */ op[0].fpk[0] = '0' << 8 | '9'; /* return '09' */ op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */ break; /* error return is P+1 */ } fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */ fp_exec (0010, ACCUM, NOP, tan_c4); /* acc = acc + C4 */ fp_exec (0064, ACCUM, tan_c3, NOP); /* acc = C3 / acc */ fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */ fp_exec (0050, ACCUM, NOP, tan_c2); /* acc = acc * C2 */ fp_exec (0010, ACCUM, NOP, tan_c1); /* acc = acc + C1 */ fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ if (multiple & 0002) /* multiple * 2 odd? */ fp_exec (0064, &op[0], minus_1, NOP); /* res = -1.0 / acc */ PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; case 001: /* SQRT 105321 (OP_R) */ O = 0; /* clear overflow */ if (op[0].fpk[0] == 0) { /* arg = 0? */ PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; } else if ((int16) op[0].fpk[0] < 0) { /* sqrt of neg? */ op[0].fpk[0] = '0' << 8 | '3'; /* return '03' */ op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */ O = 1; /* set overflow */ break; /* error return is P+1 */ } fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ if (exponent & 1) { /* exponent odd? */ fp_exec (0040, ACCUM, op[1], sqrt_a1); /* acc = op1 * A1 */ fp_exec (0010, &op[2], NOP, sqrt_b1); /* op2 = acc + B1 */ op[1].fpk[1] = op[1].fpk[1] + 2; /* op1 = op1 * 2.0 */ } else { /* exponent even */ fp_exec (0040, ACCUM, op[1], sqrt_a2); /* acc = op1 * A2 */ fp_exec (0010, &op[2], NOP, sqrt_b2); /* op2 = acc + B2 */ } fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */ fp_exec (0010, &op[2], NOP, op[2]); /* op2 = acc + op2 */ op[1].fpk[1] = op[1].fpk[1] + 4; /* op1 = op1 * 4.0 */ fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */ fp_exec (0010, &op[0], NOP, op[2]); /* res = acc + op2 */ power = (exponent >> 1) - 2; if (op[0].fpk[0]) { /* calc x * 2**n */ fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ exponent = exponent + power; /* multiply by 2**n */ if ((exponent > 0177) || /* exponent overflow? */ (exponent < -0200)) { /* or underflow? */ O = 1; /* rtn unscaled val, set ovf */ break; /* error return is P+1 */ } else fp_pack (&op[0], op[1], exponent, fp_f);/* repack result */ } PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; case 002: /* ALOG 105322 (OP_R) */ case 007: /* ALOGT 105327 (OP_R) */ O = 0; /* clear overflow */ if ((int16) op[0].fpk[0] <= 0) { /* log of neg or zero? */ op[0].fpk[0] = '0' << 8 | '2'; /* return '02' */ op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */ O = 1; /* set overflow */ break; /* error return is P+1 */ } fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ if (op[0].fpk[0] < 0055000) { /* out of range? */ exponent = exponent - 1; /* drop exponent */ op[1].fpk[1] = op[1].fpk[1] | 2; /* set "exponent" to 1 */ } op[2].fpk[0] = exponent; fp_exec (0120, &op[3], op[2], NOP); /* op3 = FLT(exponent) */ fp_exec (0020, &op[4], op[1], plus_1); /* op4 = op1 - 1.0 */ fp_exec (0000, ACCUM, op[1], plus_1); /* acc = op1 + 1.0 */ fp_exec (0064, &op[5], op[4], NOP); /* op5 = op4 / acc */ fp_exec (0054, ACCUM, NOP, NOP); /* acc = acc * acc */ fp_exec (0030, ACCUM, NOP, alog_c3); /* acc = acc - c3 */ fp_exec (0064, ACCUM, alog_c2, NOP); /* acc = c2 / acc */ fp_exec (0010, ACCUM, NOP, alog_c1); /* acc = acc + c1 */ fp_exec (0050, ACCUM, NOP, op[5]); /* acc = acc * op5 */ fp_exec (0010, ACCUM, NOP, op[3]); /* acc = acc + op3 */ fp_exec (0050, &op[0], NOP, ln_2); /* res = acc * ln2 */ if (entry == 007) /* ALOGT? */ fp_exec (0050, &op[0], NOP, log_e); /* res = acc * log(e) */ PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; case 003: /* ATAN 105323 (OP_R) */ O = 0; /* clear overflow */ if (op[0].fpk[0] == 0) /* argument zero? */ break; /* result zero */ flag = (op[0].fpk[1] & 1); /* get exponent sign */ sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ if (flag == 0) { /* exp pos? (abs >= 0.5)? */ if (sign) /* argument negative? */ fp_pcom (&op[0], fp_f); /* make positive */ if (op[0].fpk[1] & 0374) { /* arg >= 2? */ fp_exec(0060, &op[0], plus_1, op[0]); /* arg = 1.0 / arg */ op[2] = pi_over_2; /* constant = pi / 2.0 */ } else { fp_exec (0020, &op[1], plus_1, op[0]); /* op1 = 1.0 - arg */ fp_exec (0000, ACCUM, plus_1, op[0]); /* acc = 1.0 + arg */ fp_exec (0064, &op[0], op[1], NOP); /* arg = op1 / acc */ op[2] = pi_over_4; /* constant = pi / 4.0 */ } } fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg * arg */ fp_exec (0010, ACCUM, NOP, atan_c4); /* acc = acc + C4 */ fp_exec (0064, ACCUM, atan_c3, NOP); /* acc = C3 / acc */ fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */ fp_exec (0050, ACCUM, NOP, atan_c2); /* acc = acc * C2 */ fp_exec (0010, ACCUM, NOP, atan_c1); /* acc = acc + C1 */ fp_exec (0064, &op[0], op[0], NOP); /* res = arg / acc */ if (flag == 0) { /* exp pos? (abs >= 0.5)? */ fp_exec (0030, &op[0], NOP, op[2]); /* res = acc - pi / n */ if (sign == 0) /* argument positive? */ fp_pcom (&op[0], fp_f); /* make negative */ } break; case 004: /* COS 105324 (OP_R) */ case 005: /* SIN 105325 (OP_R) */ O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */ if (O) { /* out of range? */ op[0].fpk[0] = '0' << 8 | '5'; /* return '05' */ op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */ break; /* error return is P+1 */ } multiple = multiple / 2 + (entry == 004); /* add one for cosine */ flag = (multiple & 1); /* decide on series */ fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */ if (flag) { fp_exec (0050, ACCUM, NOP, cos_c4); /* acc = acc * c4 */ fp_exec (0010, ACCUM, NOP, cos_c3); /* acc = acc + c3 */ fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ fp_exec (0010, ACCUM, NOP, cos_c2); /* acc = acc + c2 */ fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ fp_exec (0010, &op[0], NOP, cos_c1); /* res = acc + c1 */ } else { fp_exec (0050, ACCUM, NOP, sin_c4); /* acc = acc * c4 */ fp_exec (0010, ACCUM, NOP, sin_c3); /* acc = acc + c3 */ fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ fp_exec (0010, ACCUM, NOP, sin_c2); /* acc = acc + c2 */ fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ fp_exec (0010, ACCUM, NOP, sin_c1); /* acc = acc + c1 */ fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ } if (multiple & 0002) /* multiple * 2 odd? */ fp_pcom (&op[0], fp_f); /* make negative */ PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; case 006: /* EXP 105326 (OP_R) */ sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ O = reduce (&op[0], &multiple, two_over_ln2); /* reduce range */ multiple = multiple / 2; /* get true multiple */ if ((sign == 0) && (O | (multiple > 128))) { /* pos and ovf or out of range? */ op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */ op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */ O = 1; /* set overflow */ break; /* error return is P+1 */ } else if (sign && (multiple < -128)) { /* neg and out of range? */ op[0].fpk[0] = 0; /* result is zero */ op[0].fpk[1] = 0; O = 0; /* clear for underflow */ PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; } fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */ fp_exec (0050, ACCUM, NOP, exp_c2); /* acc = acc * c2 */ fp_exec (0030, ACCUM, NOP, op[0]); /* acc = acc - op0 */ fp_exec (0010, ACCUM, NOP, exp_c1); /* acc = acc + c1 */ fp_exec (0064, ACCUM, op[0], NOP); /* acc = op0 / acc */ fp_exec (0010, &op[0], NOP, plus_half); /* res = acc + 0.5 */ power = multiple + 1; if (op[0].fpk[0]) { /* calc x * 2**n */ fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ exponent = exponent + power; /* multiply by 2**n */ if ((exponent > 0177) || /* exponent overflow? */ (exponent < -0200)) { /* or underflow? */ if (sign == 0) { /* arg positive? */ op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */ op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */ O = 1; /* set overflow */ } else { op[0].fpk[0] = 0; /* result is zero */ op[0].fpk[1] = 0; O = 0; /* clear for underflow */ } break; /* error return is P+1 */ } else { fp_pack (&op[0], op[1], exponent, fp_f);/* repack value */ O = 0; } } PC = (PC + 1) & VAMASK; /* normal return is P+2 */ break; case 010: /* TANH 105330 (OP_R) */ O = 0; sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ if (op[0].fpk[1] & 1) { /* abs (arg) < 0.5? */ fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */ fp_exec (0010, ACCUM, NOP, tanh_c3); /* acc = acc + c3 */ fp_exec (0064, ACCUM, tanh_c2, NOP); /* acc = c2 / acc */ fp_exec (0010, ACCUM, NOP, tanh_c1); /* acc = acc + c1 */ fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ } else if (op[0].fpk[1] & 0370) /* abs (arg) >= 8.0? */ if (sign) /* arg negative? */ op[0] = minus_1; /* result = -1.0 */ else /* arg positive */ op[0] = plus_1; /* result = +1.0 */ else { /* 0.5 <= abs (arg) < 8.0 */ BR = BR + 2; /* arg = arg * 2.0 */ cpu_sis (0105326, intrq); /* calc exp (arg) */ PC = (PC - 1) & VAMASK; /* correct P (always good rtn) */ op[0].fpk[0] = AR; /* save value */ op[0].fpk[1] = BR; fp_exec (0020, &op[1], op[0], plus_1); /* op1 = op0 - 1.0 */ fp_exec (0000, ACCUM, op[0], plus_1); /* acc = op0 + 1.0 */ fp_exec (0064, &op[0], op[1], NOP); /* res = op1 / acc */ } break; case 011: /* DPOLY 105331 (OP_CATAKK) */ O = 0; /* clear overflow */ AR = op[0].word; /* get flag word */ if ((int16) AR >= 0) { /* flags present? */ AR = 1; /* no, so set default */ arg = op[2]; /* arg = X */ } else /* bit 15 set */ fp_exec (0042, &arg, op[2], op[2]); /* arg = X ^ 2 */ coeff = ReadOp (op[3].word, fp_t); /* get first coefficient */ op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ fp_accum (&coeff, fp_t); /* acc = coeff */ for (i = 0; i < op[4].word; i++) { /* compute numerator */ fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */ coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */ op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */ } if (AR & 1) /* bit 0 set? */ op[6] = fp_accum (NULL, fp_t); /* save numerator */ else fp_exec (0046, &op[6], op[2], NOP); /* acc = X * acc */ if (op[5].word) { /* n > 0 ? */ fp_accum (&t_one, fp_t); /* acc = 1.0 */ for (i = 0; i < op[5].word; i++) { /* compute denominator */ fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */ coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */ op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */ } if (AR & 0040000) /* bit 14 set? */ fp_exec (0032, ACCUM, NOP, op[6]); /* acc = den - num */ fp_exec (0066, &op[6], op[6], NOP); /* op6 = num / den */ } WriteOp (op[1].word, op[6], fp_t); /* write result */ if (O) /* overflow? */ op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ break; case 012: /* /CMRT 105332 (OP_AAT) */ O = 0; f = (int16) AR; /* save flags */ coeff = ReadOp (op[1].word, fp_t); /* get coefficient (C) */ fp_unpack (NULL, &exponent, op[2], fp_t); /* unpack exponent */ if ((f == -1) || (exponent < 4)) { /* TANH or abs (arg) < 16.0? */ /* result = x * c - n */ fp_exec (0042, &product, op[2], coeff); /* product = arg * C */ O = fp_exec (0112, &count, NOP, NOP); /* count = FIX (acc) */ if ((int16) count.word >= 0) /* nearest even integer */ count.word = count.word + 1; BR = count.word = count.word & ~1; /* save LSBs of N */ O = O | fp_exec (0122, ACCUM, count, NOP); /* acc = FLT (count) */ if (O) { /* out of range? */ op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ break; /* error return is P+1 */ } fp_exec (0026, &result, product, NOP); /* acc = product - acc */ fp_unpack (NULL, &rsltexp, result, fp_t); /* unpack exponent */ /* determine if cancellation matters */ if ((f < 0) || (f == 2) || (f == 6) || /* EXP, TANH, or COS? */ (exponent - rsltexp < 5)) { /* bits lost < 5? */ WriteOp (op[0].word, result, fp_t); /* write result */ PC = (PC + 1) & VAMASK; /* P+2 return for good result */ op[0].fpk[1] = BR; /* return LSBs of N in B */ break; /* all done! */ } } /* result = (xu * cu - n) + (x - xu) * c + xu * cl */ if (exponent >= (8 + 16 * (f >= 0))) { /* exp >= 8 (EXP,TANH)? */ op[0].fpk[0] = 0; /* or 24 (SIN/COS/TAN)? */ break; /* range error return is P+1 */ } op[3].fpk[0] = coeff.fpk[0]; /* form upper bits of C (CU) */ op[3].fpk[1] = coeff.fpk[1] & 0177770; op[3].fpk[2] = 0; op[3].fpk[3] = coeff.fpk[3] & 0000377; op[4].fpk[0] = op[2].fpk[0]; /* form upper bits of X (XU) */ op[4].fpk[1] = op[2].fpk[1] & 0177770; op[4].fpk[2] = 0; op[4].fpk[3] = op[2].fpk[3] & 0000377; fp_exec (0042, &op[5], op[3], op[4]); /* op5 = cu * xu */ fp_exec (0116, &op[6], NOP, NOP); /* op6 = fix (acc) (2wd) */ if ((int32) op[6].dword >= 0) /* nearest even integer */ op[6].dword = op[6].dword + 1; op[6].dword = op[6].dword & ~1; BR = op[6].dword & DMASK; /* save LSBs of N */ O = fp_exec (0126, ACCUM, op[6], NOP); /* acc = flt (op6) */ if (O) { /* overflow? */ op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ break; /* range error return is P+1 */ } fp_exec (0026, &op[7], op[5], NOP); /* op7 = cu * xu - n */ fp_exec (0022, ACCUM, op[2], op[4]); /* acc = x - xu */ fp_exec (0052, ACCUM, NOP, coeff); /* acc = (x - xu) * c */ fp_exec (0012, &op[5], NOP, op[7]); /* op5 = acc + (cu * xu - n) */ op[1].word = (op[1].word + 4) & VAMASK; /* point at second coefficient */ coeff = ReadOp (op[1].word, fp_t); /* get coefficient (CL) */ fp_exec (0042, ACCUM, op[4], coeff); /* acc = xu * cl */ fp_exec (0012, &result, NOP, op[5]); /* result = acc + (x - xu) * c + (cu * xu - n) */ WriteOp (op[0].word, result, fp_t); /* write result */ PC = (PC + 1) & VAMASK; /* P+2 return for good result */ op[0].fpk[1] = BR; /* return LSBs of N in B */ break; case 013: /* /ATLG 105333 (OP_A) */ arg = ReadOp (op[0].word, fp_t); /* get argument */ fp_exec (0022, &op[1], t_one, arg); /* op1 = 1.0 - arg */ fp_exec (0002, ACCUM, t_one, arg); /* acc = 1.0 + arg */ fp_exec (0066, &op[1], op[1], NOP); /* res = op1 / acc */ WriteOp (op[0].word, op[1], fp_t); /* write result */ break; case 014: /* .FPWR 105334 (OP_IIF) */ p = 0; /* set to single-precision */ goto NPWR; case 015: /* .TPWR 105335 (OP_IAT) */ p = 2; /* set to double-precision */ NPWR: if (op[2].fpk[0]) { /* non-zero base? */ fp_exec (0120, &pwr, op[0], NOP); /* float power */ sign = ((int16) pwr.fpk[0] < 0); /* save sign of power */ i = (pwr.fpk[0] << 2) & DMASK; /* clear it */ fp_unpack (NULL, &exponent, pwr, fp_f); /* unpack exponent */ if (sign == 0) exponent = exponent - 1; O = 0; /* clear overflow */ fp_accum (&op[2], (fp_f + p)); /* acc = arg */ while (exponent-- > 0) { O = O | fp_exec ((uint16) (0054 | p), /* square acc */ ACCUM, NOP, NOP); if (i & SIGN) O = O | fp_exec ((uint16) (0050 | p), /* acc = acc * arg */ ACCUM, NOP, op[2]); i = i << 1; } op[2] = fp_accum (NULL, (fp_f + p)); /* get accum */ if (op[2].fpk[0] == 0) /* result zero? */ O = 1; /* underflow */ } if (entry == 014) /* .FPWR ? */ op[0] = op[2]; /* copy result */ else /* .TPWR */ WriteOp (op[1].word, op[2], fp_t); /* write result */ break; case 017: /* [tst] 105337 (OP_N) */ XR = 4; /* firmware revision */ SR = 0102077; /* test passed code */ PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DPOLY */ return reason; default: /* others undefined */ return stop_inst; } AR = op[0].fpk[0]; /* save result */ BR = op[0].fpk[1]; /* into A/B */ return reason; } #endif /* end of int64 support */ simh-3.8.1/HP2100/hp2100_fp.h0000644000175000017500000000435310543661014013230 0ustar vlmvlm/* hp2100_fp.h: HP 2100/21MX floating point definitions Copyright (c) 2002-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 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2 26-Sep-06 JDB Moved from hp2100_fp.c to simplify extensions */ #ifndef _HP2100_FP_H_ #define _HP2100_FP_H_ /* Firmware floating-point routines */ uint32 f_as (uint32 op, t_bool sub); /* FAD/FSB */ uint32 f_mul (uint32 op); /* FMP */ uint32 f_div (uint32 op); /* FDV */ uint32 f_fix (void); /* FIX */ uint32 f_flt (void); /* FLT */ /* Firmware FFP helpers */ uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision); #endif simh-3.8.1/HP2100/hp2100_ds.c0000644000175000017500000022245211107411526013224 0ustar vlmvlm/* hp2100_ds.c: HP 2100 13037 disk controller simulator Copyright (c) 2004-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. DS 13037 disk controller 26-Jun-08 JDB Rewrote device I/O to model backplane signals 31-Dec-07 JDB Corrected and verified ioCRS action 20-Dec-07 JDB Corrected DPTR register definition from FLDATA to DRDATA 28-Dec-06 JDB Added ioCRS state to I/O decoders 03-Aug-06 JDB Fixed REQUEST STATUS command to clear status-1 Removed redundant attached test in "ds_detach" 18-Mar-05 RMS Added attached test to detach routine 01-Mar-05 JDB Added SET UNLOAD/LOAD Reference: - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980) States of the controller: the controller uP runs all the time, but most of the time it is waiting for an event. The simulator only 'runs' the controller when there's an event to process: change in CPU interface state, change in disk state, or timeout. The controller has three states: - Idle. No operations other than seek or recalibrate are in progress, and the CPU interface is disconnected. The controller responds both to new commands and to drive attention interrupts. - Wait. No operations other than seek or recalibrate are in progress, but the CPU interface is connected. The controller responds to new commands but not to drive attention interrupts. - Busy. The controller is processing a command. The controller does not respond to new commands or to drive attention interrupts. The controller busy state is loosely related to the testable (visible) busy flop. If the visible busy flop is set, the controller is in the busy state; but the controller can also be busy (processing an invalid opcode or invalid unit) while visible busy is clear. Omissions: the following features are not implemented: - Drive hold. Since this is a single CPU implementation, the drives are always available to the CPU. - Spare, defective, protected. The disk files carry only data. - Formatting. The disk files carry only data. - ECC. Data errors are always uncorrectable. */ #include #include "hp2100_defs.h" #define DS_NUMDR 8 /* max drives */ #define DS_DRMASK (DS_NUMDR - 1) #define DS_NUMWD 128 /* data words/sec */ #define DS_NUMWDF 138 /* total words/sec */ #define DS_FSYNC 0 /* sector offsets */ #define DS_FCYL 1 #define DS_FHS 2 #define DS_FDATA 3 #define DS_FIFO_SIZE 16 /* fifo size */ #define DS_FIFO_EMPTY (ds_fifo_cnt == 0) #define ds_ctrl ds_unit[DS_NUMDR] /* ctrl thread */ #define ds_timer ds_unit[DS_NUMDR + 1] /* timeout thread */ #define GET_CURSEC(x,d) ((int32) fmod (sim_gtime() / ((double) (x)), \ ((double) (drv_tab[d].sc)))) /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ #define UNIT_V_DTYPE (UNIT_V_UF + 2) /* disk type */ #define UNIT_M_DTYPE 3 #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ #define UNIT_V_FMT (UNIT_V_UF + 5) /* format enabled */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_FMT (1 << UNIT_V_FMT) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define UNIT_WPR (UNIT_WLK | UNIT_RO) /* write prot */ /* Parameters in the unit descriptor */ #define FNC u3 /* function */ #define CYL u4 /* current cylinder */ #define STA u5 /* status */ /* Arguments to subroutines */ #define CLR_BUSY 0 /* clear visible busy */ #define SET_BUSY 1 /* set visible busy */ /* Command word - <12:8> are opcode, <7:0> are opcode dependent cold load read <7:6> = head <5:0> = sector set file mask <7:4> = retry count <3:0> = file mask (auto-seek options) commands with units <7> = hold flag <4:0> = unit number */ #define DSC_V_OP 8 /* opcode */ #define DSC_M_OP 037 #define DSC_COLD 000 /* cold load read */ #define DSC_RECAL 001 /* recalibrate */ #define DSC_SEEK 002 /* seek */ #define DSC_RSTA 003 /* request status */ #define DSC_RSA 004 /* request sector addr */ #define DSC_READ 005 /* read */ #define DSC_RFULL 006 /* read full */ #define DSC_VFY 007 /* verify */ #define DSC_WRITE 010 /* write */ #define DSC_WFULL 011 /* write full */ #define DSC_CLEAR 012 /* clear */ #define DSC_INIT 013 /* initialize */ #define DSC_AREC 014 /* address record */ #define DSC_RSYN 015 /* request syndrome */ #define DSC_ROFF 016 /* read with offset */ #define DSC_SFM 017 /* set file mask */ #define DSC_RNOVFY 022 /* read no verify */ #define DSC_WTIO 023 /* write TIO */ #define DSC_RDA 024 /* request disk addr */ #define DSC_END 025 /* end */ #define DSC_WAKE 026 /* wakeup */ #define DSC_ATN 035 /* pseudo: ATN */ #define DSC_BADU 036 /* pseudo: bad unit */ #define DSC_BADF 037 /* pseudo: bad opcode */ #define DSC_NEXT 0040 /* state increment */ #define DSC_2ND 0040 /* subcommand states */ #define DSC_3RD 0100 #define DSC_4TH 0140 #define DSC_V_CHD 6 /* cold load head */ #define DSC_M_CHD 03 #define DSC_V_CSC 0 /* cold load sector */ #define DSC_M_CSC 077 #define DSC_V_RTY 4 /* retry count */ #define DSC_M_RTY 017 #define DSC_V_DECR 3 /* seek decrement */ #define DSC_V_SPEN 2 /* enable sparing */ #define DSC_V_CYLM 1 /* cylinder mode */ #define DSC_V_AUTO 0 /* auto seek */ #define DSC_V_HOLD 7 /* hold flag */ #define DSC_V_UNIT 0 /* unit */ #define DSC_M_UNIT 017 #define DSC_V_SPAR 15 /* INIT spare */ #define DSC_V_PROT 14 /* INIT protected */ #define DSC_V_DFCT 13 /* INIT defective */ #define DSC_HOLD (1u << DSC_V_HOLD) #define DSC_DECR (1u << DSC_V_DECR) #define DSC_SPEN (1u << DSC_V_SPEN) #define DSC_CYLM (1u << DSC_V_CYLM) #define DSC_AUTO (1u << DSC_V_AUTO) #define DSC_FMASK ((DSC_M_RTY << DSC_V_RTY)|DSC_DECR|\ DSC_SPEN|DSC_CYLM|DSC_AUTO) #define DSC_GETOP(x) (((x) >> DSC_V_OP) & DSC_M_OP) #define DSC_GETUNIT(x) (((x) >> DSC_V_UNIT) & DSC_M_UNIT) #define DSC_GETCHD(x) (((x) >> DSC_V_CHD) & DSC_M_CHD) #define DSC_GETCSC(x) (((x) >> DSC_V_CSC) & DSC_M_CSC) #define DSC_SPAR (1u << DSC_V_SPAR) #define DSC_PROT (1u << DSC_V_PROT) #define DSC_DFCT (1u << DSC_V_DFCT) /* Command flags */ #define CMF_UNDF 001 /* undefined */ #define CMF_CLREC 002 /* clear eoc flag */ #define CMF_CLRS 004 /* clear status */ #define CMF_UIDLE 010 /* requires unit no */ /* Cylinder words - 16b */ /* Head/sector word */ #define DSHS_V_HD 8 /* head */ #define DSHS_M_HD 037 #define DSHS_V_SC 0 /* sector */ #define DSHS_M_SC 0377 #define DSHS_HD (DSHS_M_HD << DSHS_V_HD) #define DSHS_SC (DSHS_M_SC << DSHS_V_SC) #define DSHS_GETHD(x) (((x) >> DSHS_V_HD) & DSHS_M_HD) #define DSHS_GETSC(x) (((x) >> DSHS_V_SC) & DSHS_M_SC) /* Status 1 */ #define DS1_V_SPAR 15 /* spare - na */ #define DS1_V_PROT 14 /* protected - na */ #define DS1_V_DFCT 13 /* defective - na */ #define DS1_V_STAT 8 /* status */ #define DS1_OK (000 << DS1_V_STAT) /* normal */ #define DS1_ILLOP (001 << DS1_V_STAT) /* illegal opcode */ #define DS1_AVAIL (002 << DS1_V_STAT) /* available */ #define DS1_CYLCE (007 << DS1_V_STAT) /* cyl compare err */ #define DS1_UNCOR (010 << DS1_V_STAT) /* uncor data err */ #define DS1_HSCE (011 << DS1_V_STAT) /* h/s compare err */ #define DS1_IOPE (012 << DS1_V_STAT) /* IO oper err - na */ #define DS1_EOCYL (014 << DS1_V_STAT) /* end cylinder */ #define DS1_OVRUN (016 << DS1_V_STAT) /* overrun */ #define DS1_CORDE (017 << DS1_V_STAT) /* correctible - na */ #define DS1_ILLST (020 << DS1_V_STAT) /* illegal spare - na */ #define DS1_DEFTK (021 << DS1_V_STAT) /* defective trk - na */ #define DS1_ACCER (022 << DS1_V_STAT) /* access not rdy - na */ #define DS1_S2ERR (023 << DS1_V_STAT) /* status 2 error */ #define DS1_TKPER (026 << DS1_V_STAT) /* protected trk - na */ #define DS1_UNAVL (027 << DS1_V_STAT) /* illegal unit */ #define DS1_ATN (037 << DS1_V_STAT) /* attention */ #define DS1_V_UNIT 0 #define DS1_SPAR (1u << DS1_V_SPAR) #define DS1_PROT (1u << DS1_V_PROT) #define DS1_DFCT (1u << DS1_V_DFCT) /* Status 2, ^ = kept in unit status, * = dynamic */ #define DS2_ERR 0100000 /* *error */ #define DS2_V_ID 9 /* drive type */ #define DS2_ATN 0000200 /* ^attention */ #define DS2_RO 0000100 /* *read only */ #define DS2_FRM 0000040 /* *format */ #define DS2_FLT 0000020 /* fault - na */ #define DS2_FS 0000010 /* ^first status */ #define DS2_SC 0000004 /* ^seek error */ #define DS2_NR 0000002 /* *not ready */ #define DS2_BS 0000001 /* *busy */ #define DS2_ALLERR (DS2_FLT|DS2_SC|DS2_NR|DS2_BS) /* Controller state */ #define DS_IDLE 0 /* idle */ #define DS_WAIT 1 /* command wait */ #define DS_BUSY 2 /* busy */ /* This controller supports four different disk drive types: type #sectors/ #surfaces/ #cylinders/ surface cylinder drive 7905 48 3 411 =15MB 7906 48 4 411 =20MB 7920 48 5 823 =50MB 7925 64 9 823 =120MB In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. The 7905 and 7906 have fixed and removable platters. Consequently, they are almost always accessed with cylinders limited to each platter. The 7920 and 7925 have multiple-platter packs, and so are almost always accessed with cylinders that span all surfaces. Disk image files are arranged as a linear set of tracks. To improve locality, tracks on the 7905 and 7906 images are grouped per-platter, i.e., all tracks on heads 0 and 1, followed by all tracks on head 2 (and, for the 7906, head 3), whereas tracks on the 7920 and 7925 are sequential by cylinder and head number. This variable-access geometry is accomplished by defining a "heads per cylinder" value for the fixed and removable sections of each drive that indicates the number of heads that should be grouped for locality. The removable values are set to 2 on the 7905 and 7906, indicating that those drives typically use cylinders of two surfaces. They are set to the number of surfaces per drive for the 7920 and 7925, as those typically use cylinders encompassing the entire spindle. */ #define GET_DA(x,y,z,t) \ (((((y) < drv_tab[t].rh)? \ (x) * drv_tab[t].rh + (y): \ drv_tab[t].cyl * drv_tab[t].rh + \ ((x) * drv_tab[t].fh + (y) - drv_tab[t].rh)) * \ drv_tab[t].sc + (z)) * DS_NUMWD) #define D7905_DTYPE 0 #define D7905_SECT 48 #define D7905_SURF 3 #define D7905_RH 2 #define D7905_FH (D7905_SURF - D7905_RH) #define D7905_CYL 411 #define D7905_ID (2 << DS2_V_ID) #define D7905_SIZE (D7905_SECT * D7905_SURF * D7905_CYL * DS_NUMWD) #define D7906_DTYPE 1 #define D7906_SECT 48 #define D7906_SURF 4 #define D7906_RH 2 #define D7906_FH (D7906_SURF - D7906_RH) #define D7906_CYL 411 #define D7906_ID (0 << DS2_V_ID) #define D7906_SIZE (D7906_SECT * D7906_SURF * D7906_CYL * DS_NUMWD) #define D7920_DTYPE 2 #define D7920_SECT 48 #define D7920_SURF 5 #define D7920_RH D7920_SURF #define D7920_FH (D7920_SURF - D7920_RH) #define D7920_CYL 823 #define D7920_ID (1 << DS2_V_ID) #define D7920_SIZE (D7920_SECT * D7920_SURF * D7920_CYL * DS_NUMWD) #define D7925_DTYPE 3 #define D7925_SECT 64 #define D7925_SURF 9 #define D7925_RH D7925_SURF #define D7925_FH (D7925_SURF - D7925_RH) #define D7925_CYL 823 #define D7925_ID (3 << DS2_V_ID) #define D7925_SIZE (D7925_SECT * D7925_SURF * D7925_CYL * DS_NUMWD) struct drvtyp { uint32 sc; /* sectors */ uint32 hd; /* surfaces */ uint32 cyl; /* cylinders */ uint32 size; /* #blocks */ uint32 id; /* device type */ uint32 rh; /* removable surfaces */ uint32 fh; /* fixed surfaces */ }; static struct drvtyp drv_tab[] = { { D7905_SECT, D7905_SURF, D7905_CYL, D7905_SIZE, D7905_ID, D7905_RH, D7905_FH }, { D7906_SECT, D7906_SURF, D7906_CYL, D7906_SIZE, D7906_ID, D7906_RH, D7906_FH }, { D7920_SECT, D7920_SURF, D7920_CYL, D7920_SIZE, D7920_ID, D7920_RH, D7920_FH }, { D7925_SECT, D7925_SURF, D7925_CYL, D7925_SIZE, D7925_ID, D7925_RH, D7925_FH }, { 0 } }; FLIP_FLOP ds_control = CLEAR; FLIP_FLOP ds_flag = CLEAR; FLIP_FLOP ds_flagbuf = CLEAR; FLIP_FLOP ds_srq = CLEAR; uint32 ds_fifo[DS_FIFO_SIZE] = { 0 }; /* fifo */ uint32 ds_fifo_ip = 0; /* insertion ptr */ uint32 ds_fifo_rp = 0; /* removal ptr */ uint32 ds_fifo_cnt = 0; /* count */ uint32 ds_cmd = 0; /* command word */ uint32 ds_sr1 = 0; /* status word 1 */ uint32 ds_busy = 0; /* busy flag */ uint32 ds_eoc = 0; /* end of cylinder */ uint32 ds_eod = 0; /* end of data */ uint32 ds_fmask = 0; /* file mask */ uint32 ds_cmdf = 0; /* command follows */ uint32 ds_cmdp = 0; /* command present */ uint32 ds_cyl = 0; /* disk address: cyl */ uint32 ds_hs = 0; /* disk address: hs */ uint32 ds_vctr = 0; /* verify counter */ uint32 ds_state = 0; /* controller state */ uint32 ds_lastatn = 0; /* last atn intr */ int32 ds_stime = 100; /* seek time */ int32 ds_rtime = 100; /* inter-sector time */ int32 ds_ctime = 3; /* command time */ int32 ds_dtime = 1; /* dch time */ int32 ds_tmo = 2749200; /* timeout = 1.74 sec */ uint32 ds_ptr = 0; /* buffer ptr */ uint16 dsxb[DS_NUMWDF]; /* sector buffer */ static const uint32 ds_opflags[32] = { /* flags for ops */ CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* cold read */ CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* recalibrate */ CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* seek */ 0, /* read status */ CMF_CLRS, /* read sector */ CMF_CLRS|CMF_UIDLE, /* read */ CMF_CLRS|CMF_UIDLE, /* read full */ CMF_CLRS|CMF_UIDLE, /* verify */ CMF_CLRS|CMF_UIDLE, /* write */ CMF_CLRS|CMF_UIDLE, /* write full */ CMF_CLRS, /* clear */ CMF_CLRS|CMF_UIDLE, /* init */ CMF_CLREC|CMF_CLRS, /* addr record */ 0, /* read syndrome */ CMF_CLRS|CMF_UIDLE, /* read offset */ CMF_CLRS, /* set file mask */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_CLRS|CMF_UIDLE, /* read no verify */ CMF_CLRS, /* write TIO */ CMF_CLRS, /* read disk addr */ CMF_CLRS, /* end */ CMF_CLRS, /* wake */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS, /* undefined */ CMF_UNDF|CMF_CLRS /* undefined */ }; DEVICE ds_dev; uint32 dsio (uint32 select_code, IOSIG signal, uint32 data); t_stat ds_svc_c (UNIT *uptr); t_stat ds_svc_u (UNIT *uptr); t_stat ds_svc_t (UNIT *uptr); t_stat ds_reset (DEVICE *dptr); t_stat ds_attach (UNIT *uptr, char *cptr); t_stat ds_detach (UNIT *uptr); t_stat ds_boot (int32 unitno, DEVICE *dptr); t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); void ds_poll (void); void ds_docmd (uint32 cmd); void ds_doatn (void); uint32 ds_updds2 (UNIT *uptr); void ds_cmd_done (t_bool sf, uint32 sr1); void ds_wait_for_cpu (UNIT *uptr, uint32 newst); void ds_set_idle (void); void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy); void ds_reqad (uint16 *cyl, uint16 *hs); void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst); t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy); void ds_next_sec (UNIT *uptr); void ds_next_cyl (UNIT *uptr); t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy); void ds_start_wr (UNIT *uptr, t_bool vfy); void ds_cont_rd (UNIT *uptr, uint32 bsize); t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize); void ds_end_rw (UNIT *uptr, uint32 newst); t_stat ds_set_uncorr (UNIT *uptr); t_stat ds_clear (void); void ds_sched_atn (UNIT *uptr); uint32 ds_fifo_read (void); void ds_fifo_write (uint32 dat); void ds_fifo_reset (void); /* DS data structures ds_dev DS device descriptor ds_unit DS unit list ds_reg DS register list ds_mod DS modifier list */ DIB ds_dib = { DS, &dsio }; UNIT ds_unit[] = { { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, { UDATA (&ds_svc_c, UNIT_DIS, 0) }, { UDATA (&ds_svc_t, UNIT_DIS, 0) } }; REG ds_reg[] = { { ORDATA (CMD, ds_cmd, 16) }, { BRDATA (FIFO, ds_fifo, 8, 16, DS_FIFO_SIZE) }, { ORDATA (SR1, ds_sr1, 16) }, { ORDATA (VCTR, ds_vctr, 16) }, { ORDATA (FMASK, ds_fmask, 8) }, { ORDATA (CYL, ds_cyl, 16) }, { ORDATA (HS, ds_hs, 16) }, { ORDATA (STATE, ds_state, 2), REG_RO }, { ORDATA (LASTA, ds_lastatn, 3) }, { DRDATA (FIP, ds_fifo_ip, 4) }, { DRDATA (FRP, ds_fifo_rp, 4) }, { DRDATA (FCNT, ds_fifo_cnt, 5) }, { FLDATA (CTL, ds_control, 0) }, { FLDATA (FLG, ds_flag, 0) }, { FLDATA (FBF, ds_flagbuf, 0) }, { FLDATA (SRQ, ds_srq, 0) }, { FLDATA (BUSY, ds_busy, 0) }, { FLDATA (CMDF, ds_cmdf, 0) }, { FLDATA (CMDP, ds_cmdp, 0) }, { FLDATA (EOC, ds_eoc, 0) }, { FLDATA (EOD, ds_eod, 0) }, { BRDATA (DBUF, dsxb, 8, 16, DS_NUMWDF) }, { DRDATA (DPTR, ds_ptr, 8) }, { DRDATA (CTIME, ds_ctime, 24), PV_LEFT + REG_NZ }, { DRDATA (DTIME, ds_dtime, 24), PV_LEFT + REG_NZ }, { DRDATA (STIME, ds_stime, 24), PV_LEFT + REG_NZ }, { DRDATA (RTIME, ds_rtime, 24), PV_LEFT + REG_NZ }, { DRDATA (TIMEOUT, ds_tmo, 31), PV_LEFT + REG_NZ }, { URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0, DS_NUMDR + 1, PV_LEFT | REG_HRO) }, { URDATA (UFNC, ds_unit[0].FNC, 8, 8, 0, DS_NUMDR + 1, REG_HRO) }, { URDATA (USTA, ds_unit[0].STA, 8, 16, 0, DS_NUMDR + 1, REG_HRO) }, { URDATA (CAPAC, ds_unit[0].capac, 10, T_ADDR_W, 0, DS_NUMDR, PV_LEFT | REG_HRO) }, { ORDATA (DEVNO, ds_dib.devno, 6), REG_HRO }, { NULL } }; MTAB ds_mod[] = { { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", ds_load_unload }, { UNIT_UNLOAD, 0, "heads loaded", "LOADED", ds_load_unload }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL }, { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL }, { (UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "7905", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "7906", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "7920", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "7925", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE), "7905", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE), "7906", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE), "7920", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE), "7925", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), (D7905_DTYPE << UNIT_V_DTYPE), NULL, "7905", &ds_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (D7906_DTYPE << UNIT_V_DTYPE), NULL, "7906", &ds_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (D7920_DTYPE << UNIT_V_DTYPE), NULL, "7920", &ds_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (D7925_DTYPE << UNIT_V_DTYPE), NULL, "7925", &ds_set_size }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ds_dev }, { 0 } }; DEVICE ds_dev = { "DS", ds_unit, ds_reg, ds_mod, DS_NUMDR + 2, 8, 27, 1, 8, 16, NULL, NULL, &ds_reset, &ds_boot, &ds_attach, &ds_detach, &ds_dib, DEV_DISABLE }; /* I/O signal handler. The 13175A disc interface is unusual in that the flag and SRQ signals are decoupled. This is done to allow DMA transfers at the maximum possible speed (driving SRQ from the flag limits transfers to only every other cycle). SRQ is based on the card's FIFO; if data or room in the FIFO is available, SRQ is set to transfer it. The flag is only used to signal an interrupt at the end of a command. Also unusual is that SFC and SFS test different things, rather than complementaty states of the same thing. SFC tests the busy flip-flop, and SFS tests the flag flip-flop. Implementation notes: 1. The dispatcher runs the command poll after each I/O signal, except for SIR and ENF. Running the poll for these two will cause multi-drive access to fail. */ uint32 dsio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ ds_flag = ds_flagbuf = CLEAR; /* clear flag */ ds_srq = CLEAR; /* CLF clears SRQ */ break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ ds_flag = ds_flagbuf = SET; /* set flag and flag buffer */ break; case ioSFC: /* skip if flag is clear */ setSKF (ds_busy == 0); /* skip if not busy */ break; case ioSFS: /* skip if flag is set */ setstdSKF (ds); break; case ioIOI: /* I/O data input */ data = ds_fifo_read (); break; case ioIOO: /* I/O data output */ if (ds_cmdf) { /* expecting command? */ ds_cmd = data; /* save command */ ds_cmdf = 0; ds_cmdp = 1; /* command present */ } else ds_fifo_write (data); /* put in fifo */ break; case ioPOPIO: /* power-on preset to I/O */ ds_flag = ds_flagbuf = SET; /* set flag and flag buffer */ ds_cmdp = 0; /* clear command ready */ /* fall into CRS handler */ case ioCRS: /* control reset */ ds_control = CLEAR; /* clear control */ ds_cmdf = 0; /* not expecting command */ ds_clear (); /* do controller CLEAR */ break; case ioCLC: /* clear control flip-flop */ ds_control = CLEAR; /* clear control */ ds_cmdf = 1; /* expecting command */ ds_cmdp = 0; /* none pending */ ds_eod = 1; /* set EOD flag */ ds_fifo_reset (); /* clear fifo */ break; case ioSTC: /* set control flip-flop */ ds_control = SET; /* set control */ break; case ioEDT: /* end data transfer */ ds_eod = 1; /* flag end transfer */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, ds); /* set standard PRL signal */ setstdIRQ (select_code, ds); /* set standard IRQ signal */ setSRQ (select_code, ds_srq); /* set SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ ds_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ dsio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ dsio (select_code, ioSIR, 0); /* set interrupt request */ if ((signal != ioSIR) && (signal != ioENF)) /* if not IRQ update */ ds_poll (); /* run the controller */ return data; } /* Run the controller polling loop, based on ds_state: IDLE commands and ATN interrupts WAIT commands only BUSY nothing */ void ds_poll (void) { if ((ds_state != DS_BUSY) && ds_cmdp) /* cmd pending? */ ds_docmd (ds_cmd); /* do it */ if ((ds_state == DS_IDLE) && ds_control) /* idle? */ ds_doatn (); /* check ATN */ return; } /* Process a command - ctrl state is either IDLE or WAIT. - A drive may be processing a seek or recalibrate - The controller unit is idle - If the command can be processed, ds_state is set to BUSY, and the interface command buffer is cleared - If the command cannot be processed, ds_state is set to WAIT, and the command is retained in the interface command buffer */ void ds_docmd (uint32 cmd) { uint32 op, f, dtyp, unum; op = DSC_GETOP (cmd); /* operation */ f = ds_opflags[op]; /* flags */ if (op == DSC_COLD) unum = 0; /* boot force unit 0 */ else unum = DSC_GETUNIT (cmd); /* get unit */ if ((f & CMF_UIDLE) && (unum < DS_NUMDR) && /* idle required */ sim_is_active (&ds_unit[unum])) { /* but unit busy? */ ds_state = DS_WAIT; /* wait */ return; } ds_cmdp = 0; /* flush command */ ds_state = DS_BUSY; /* ctrl is busy */ if (f & CMF_CLRS) ds_sr1 = 0; /* clear status */ if (f & CMF_CLREC) ds_eoc = 0; /* clear end cyl */ if (f & CMF_UNDF) { /* illegal op? */ ds_sched_ctrl_op (DSC_BADF, 0, CLR_BUSY); /* sched, clr busy */ return; } switch (op) { /* Drive commands */ case DSC_COLD: /* cold load read */ ds_fmask = DSC_SPEN; /* sparing enabled */ ds_cyl = 0; /* cylinder 0 */ ds_hs = (DSC_GETCHD (ds_cmd) << DSHS_V_HD) | /* reformat hd/sec */ (DSC_GETCSC (ds_cmd) << DSHS_V_SC); case DSC_RECAL: /* recalibrate */ case DSC_SEEK: /* seek */ case DSC_READ: /* read */ case DSC_RFULL: /* read full */ case DSC_ROFF: /* read offset */ case DSC_RNOVFY: /* read no verify */ case DSC_VFY: /* verify */ case DSC_WRITE: /* write */ case DSC_WFULL: /* write full */ case DSC_INIT: /* init */ ds_sr1 = unum; /* init status */ if (unum >= DS_NUMDR) { /* invalid unit? */ ds_sched_ctrl_op (DSC_BADU, unum, CLR_BUSY);/* sched, not busy */ return; } if (op == DSC_INIT) ds_sr1 |= /* init? */ ((cmd & DSC_SPAR)? DS1_SPAR: 0) | /* copy SPD to stat1 */ ((cmd & DSC_PROT)? DS1_PROT: 0) | ((cmd & DSC_DFCT)? DS1_DFCT: 0); ds_unit[unum].FNC = op; /* save op */ ds_unit[unum].STA &= ~DS2_ATN; /* clear ATN */ sim_cancel (&ds_unit[unum]); /* cancel current */ sim_activate (&ds_unit[unum], ds_ctime); /* schedule unit */ ds_busy = 1; /* set visible busy */ break; /* Read status commands */ case DSC_RSTA: /* read status */ dsxb[1] = ds_sr1; /* return SR1 */ ds_sr1 = 0; /* clear SR1 */ if (unum < DS_NUMDR) { /* return SR2 */ dsxb[0] = ds_updds2 (&ds_unit[unum]); ds_unit[unum].STA &= ~DS2_FS; /* clear 1st */ } else dsxb[0] = DS2_ERR|DS2_NR; ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ break; case DSC_RSA: /* read sector address */ dtyp = GET_DTYPE (ds_unit[unum].flags); /* get unit type */ dsxb[0] = GET_CURSEC (ds_dtime * DS_NUMWD, dtyp); /* rot position */ ds_sched_ctrl_op (DSC_RSTA, 1, SET_BUSY); /* sched 1 wd, busy */ break; case DSC_RDA: /* read disk address */ ds_reqad (&dsxb[1], &dsxb[0]); /* return disk address */ ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ break; case DSC_RSYN: /* read syndrome */ dsxb[6] = ds_sr1; /* return SR1 */ ds_reqad (&dsxb[5], &dsxb[4]); /* return disk address */ dsxb[3] = dsxb[2] = dsxb[1] = dsxb[0] = 0; /* syndrome is 0 */ ds_sched_ctrl_op (DSC_RSTA, 7, SET_BUSY); /* sched 7 wds, busy */ break; /* Other controller commands */ case DSC_SFM: /* set file mask */ case DSC_CLEAR: /* clear */ case DSC_AREC: /* address record */ case DSC_WAKE: /* wakeup */ case DSC_WTIO: /* write TIO */ ds_sched_ctrl_op (op, 0, SET_BUSY); /* schedule, busy */ break; case DSC_END: /* end */ ds_set_idle (); /* idle ctrl */ break; } return; } /* Check for attention */ void ds_doatn (void) { uint32 i; for (i = 0; i < DS_NUMDR; i++) { /* intr disabled? */ ds_lastatn = (ds_lastatn + 1) & DS_DRMASK; /* loop through units */ if (ds_unit[ds_lastatn].STA & DS2_ATN) { /* ATN set? */ ds_unit[ds_lastatn].STA &= ~DS2_ATN; /* clear ATN */ dsio (ds_dib.devno, ioENF, 0); /* request interrupt */ ds_sr1 = DS1_ATN | ds_lastatn; /* set up status 1 */ ds_state = DS_WAIT; /* block atn intrs */ return; } } return; } /* Controller service The argument for the function, if any, is stored in uptr->CYL */ t_stat ds_svc_c (UNIT *uptr) { uint32 op; op = uptr->FNC; switch (op) { case DSC_AREC: /* address record */ ds_wait_for_cpu (uptr, DSC_AREC|DSC_2ND); /* set flag, new state */ break; case DSC_AREC | DSC_2ND: /* poll done */ if (!DS_FIFO_EMPTY) { /* OTA ds? */ ds_cyl = ds_fifo_read (); /* save cylinder */ ds_wait_for_cpu (uptr, DSC_AREC|DSC_3RD); /* set flag, new state */ } else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_AREC | DSC_3RD: /* poll done */ if (!DS_FIFO_EMPTY) { /* OTA ds? */ ds_hs = ds_fifo_read (); /* save head/sector */ ds_cmd_done (0, DS1_OK); /* op done, no flag */ } else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_RSTA: /* rd stat (all forms) */ if (DS_FIFO_EMPTY) { /* fifo empty? */ uptr->CYL--; ds_fifo_write (dsxb[uptr->CYL]); /* store next status */ ds_wait_for_cpu (uptr, DSC_RSTA | (uptr->CYL? 0: DSC_2ND)); /* set flag, new state */ } else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_RSTA | DSC_2ND: /* poll done */ if (DS_FIFO_EMPTY) ds_cmd_done (0, DS1_OK); /* op done? no flag */ else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_CLEAR: /* clear */ ds_clear (); /* reset ctrl */ ds_control = CLEAR; /* clear CTL, SRQ */ ds_srq = CLEAR; dsio (ds_dib.devno, ioSIR, 0); /* set interrupt request */ ds_cmd_done (1, DS1_OK); /* op done, set flag */ break; case DSC_SFM: /* set file mask */ ds_fmask = ds_cmd & DSC_FMASK; ds_cmd_done (1, DS1_OK); /* op done, set flag */ break; case DSC_WTIO: /* write I/O */ ds_cmd_done (0, DS1_OK); /* op done, no flag */ break; case DSC_WAKE: /* wakeup */ ds_cmd_done (1, DS1_AVAIL); /* op done, set flag */ break; case DSC_BADU: /* invalid unit */ if (uptr->CYL > 10) ds_cmd_done (1, DS1_UNAVL); /* [11,16]? bad unit */ else ds_cmd_done (1, DS1_S2ERR); /* else unit not ready */ break; case DSC_BADF: /* invalid operation */ ds_cmd_done (1, DS1_ILLOP); /* op done, set flag */ break; default: return SCPE_IERR; } ds_poll (); /* run the controller */ return SCPE_OK; } /* Timeout service */ t_stat ds_svc_t (UNIT *uptr) { int32 i; for (i = 0; i < (DS_NUMDR + 1); i++) /* cancel all ops */ sim_cancel (&ds_unit[i]); ds_set_idle (); /* idle the controller */ ds_fmask = 0; /* clear file mask */ ds_poll (); /* run the controller */ return SCPE_OK; } /* Unit service */ t_stat ds_svc_u (UNIT *uptr) { uint32 op, dtyp; t_stat r; op = uptr->FNC; dtyp = GET_DTYPE (uptr->flags); switch (op) { /* case on function */ /* Seek and recalibrate */ case DSC_RECAL: /* recalibrate */ if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ ds_start_seek (uptr, 0, DSC_RECAL|DSC_2ND); /* set up seek */ ds_set_idle (); /* ctrl is idle */ } else ds_cmd_done (1, DS1_S2ERR); /* not ready error */ break; case DSC_RECAL | DSC_2ND: /* recal complete */ uptr->STA = uptr->STA | DS2_ATN; /* set attention */ break; case DSC_SEEK: /* seek */ ds_wait_for_cpu (uptr, DSC_SEEK|DSC_2ND); /* set flag, new state */ break; case DSC_SEEK | DSC_2ND: /* waiting for word 1 */ if (!DS_FIFO_EMPTY) { /* OTA ds? */ ds_cyl = ds_fifo_read (); /* save cylinder */ ds_wait_for_cpu (uptr, DSC_SEEK|DSC_3RD); /* set flag, new state */ } else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_SEEK | DSC_3RD: /* waiting for word 2 */ if (!DS_FIFO_EMPTY) { /* OTA ds? */ ds_hs = ds_fifo_read (); /* save head/sector */ if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ ds_start_seek (uptr, ds_cyl, DSC_SEEK|DSC_4TH); /* set up seek */ ds_set_idle (); /* ctrl is idle */ } else ds_cmd_done (1, DS1_S2ERR); /* else not ready error */ } else sim_activate (uptr, ds_ctime); /* continue poll */ break; case DSC_SEEK | DSC_4TH: /* seek complete */ uptr->STA = uptr->STA | DS2_ATN; /* set attention */ break; /* Read variants */ case DSC_ROFF: /* read with offset */ ds_wait_for_cpu (uptr, DSC_ROFF|DSC_2ND); /* set flag, new state */ break; case DSC_ROFF | DSC_2ND: /* poll done */ if (!DS_FIFO_EMPTY) { /* OTA ds? new state */ ds_fifo_read (); /* drain fifo */ uptr->FNC = DSC_READ; dsio (ds_dib.devno, ioENF, 0); /* handshake */ } sim_activate (uptr, ds_ctime); /* schedule unit */ break; case DSC_COLD: /* cold load read */ if ((uptr->flags & UNIT_UNLOAD) == 0) /* drive up? */ ds_start_seek (uptr, 0, DSC_READ); /* set up seek */ else ds_cmd_done (1, DS1_S2ERR); /* no, not ready error */ break; case DSC_READ: /* read */ if (r = ds_start_rd (uptr, 0, 1)) return r; /* new sector; error? */ break; case DSC_READ | DSC_2ND: /* word transfer */ ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ break; case DSC_READ | DSC_3RD: /* end of sector */ ds_end_rw (uptr, DSC_READ); /* see if more to do */ break; case DSC_RNOVFY: /* read, no verify */ if (r = ds_start_rd (uptr, 0, 0)) return r; /* new sector; error? */ break; case DSC_RNOVFY | DSC_2ND: /* word transfer */ ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ break; case DSC_RNOVFY | DSC_3RD: /* end of sector */ ds_end_rw (uptr, DSC_RNOVFY); /* see if more to do */ break; case DSC_RFULL: /* read full */ dsxb[DS_FSYNC] = 0100376; /* fill in header */ dsxb[DS_FCYL] = uptr->CYL; dsxb[DS_FHS] = ds_hs; /* before h/s update */ if (r = ds_start_rd (uptr, DS_FDATA, 0)) /* new sector; error? */ return r; break; case DSC_RFULL | DSC_2ND: /* word transfer */ ds_cont_rd (uptr, DS_NUMWDF); /* xfr wd, check end */ break; case DSC_RFULL | DSC_3RD: /* end of sector */ ds_end_rw (uptr, DSC_RFULL); /* see if more to do */ break; case DSC_VFY: /* verify */ ds_wait_for_cpu (uptr, DSC_VFY|DSC_2ND); /* set flag, new state */ break; case DSC_VFY | DSC_2ND: /* poll done */ if (!DS_FIFO_EMPTY) { /* OTA ds? */ ds_vctr = ds_fifo_read (); /* save count */ uptr->FNC = DSC_VFY | DSC_3RD; /* next state */ sim_activate (uptr, ds_rtime); /* delay for transfer */ } else sim_activate (uptr, ds_ctime); /* no, continue poll */ break; case DSC_VFY | DSC_3RD: /* start sector */ if (ds_start_rw (uptr, ds_dtime * DS_NUMWD, 1)) break; /* new sector; error? */ ds_next_sec (uptr); /* increment hd, sc */ break; case DSC_VFY | DSC_4TH: /* end sector */ ds_vctr = (ds_vctr - 1) & DMASK; /* decrement count */ if (ds_vctr) ds_end_rw (uptr, DSC_VFY|DSC_3RD); /* more to do? */ else ds_cmd_done (1, DS1_OK); /* no, set done */ break; /* Write variants */ case DSC_WRITE: /* write */ ds_start_wr (uptr, 1); /* new sector */ break; case DSC_WRITE | DSC_2ND: if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ return r; /* error? */ break; case DSC_WRITE | DSC_3RD: /* end sector */ ds_end_rw (uptr, DSC_WRITE); /* see if more to do */ break; case DSC_INIT: /* init */ ds_start_wr (uptr, 0); /* new sector */ break; case DSC_INIT | DSC_2ND: if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ return r; /* error? */ break; case DSC_INIT | DSC_3RD: /* end sector */ ds_end_rw (uptr, DSC_INIT); /* see if more to do */ break; case DSC_WFULL: /* write full */ ds_start_wr (uptr, 0); /* new sector */ break; case DSC_WFULL | DSC_2ND: if (r = ds_cont_wr (uptr, DS_FDATA, DS_NUMWDF)) /* write word */ return r; /* error */ break; case DSC_WFULL | DSC_3RD: ds_end_rw (uptr, DSC_WFULL); /* see if more to do */ break; default: break; } ds_poll (); return SCPE_OK; } /* Schedule timed wait for CPU response - Set flag to get CPU attention - Set specified unit to 'newstate' and schedule - Schedule timeout */ void ds_wait_for_cpu (UNIT *uptr, uint32 newst) { dsio (ds_dib.devno, ioENF, 0); /* set flag */ uptr->FNC = newst; /* new state */ sim_activate (uptr, ds_ctime); /* activate unit */ sim_cancel (&ds_timer); /* activate timeout */ sim_activate (&ds_timer, ds_tmo); return; } /* Set idle state - Controller is set to idle state - Visible busy is cleared - Timeout is cancelled */ void ds_set_idle (void) { ds_busy = 0; /* busy clear */ ds_state = DS_IDLE; /* ctrl idle */ sim_cancel (&ds_timer); /* no timeout */ return; } /* Set wait state - Set flag if required - Set controller to wait state - Clear visible busy - Schedule timeout */ void ds_cmd_done (t_bool sf, uint32 sr1) { if (sf) /* set host flag? */ dsio (ds_dib.devno, ioENF, 0); /* set flag */ ds_busy = 0; /* clear visible busy */ ds_sr1 = ds_sr1 | sr1; /* final status */ ds_state = DS_WAIT; /* ctrl waiting */ sim_cancel (&ds_timer); /* activate timeout */ sim_activate (&ds_timer, ds_tmo); return; } /* Return drive status (status word 2) */ uint32 ds_updds2 (UNIT *uptr) { uint32 sta; uint32 dtyp = GET_DTYPE (uptr->flags); sta = drv_tab[dtyp].id | /* form status */ uptr->STA | /* static bits */ ((uptr->flags & UNIT_WPR)? DS2_RO: 0) | /* dynamic bits */ ((uptr->flags & UNIT_FMT)? DS2_FRM: 0) | ((uptr->flags & UNIT_UNLOAD)? DS2_NR | DS2_BS: 0) | (sim_is_active (uptr)? DS2_BS: 0); if (sta & DS2_ALLERR) sta = sta | DS2_ERR; /* set error */ return sta; } /* Schedule controller operation */ void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy) { ds_ctrl.FNC = op; /* save op */ ds_ctrl.CYL = arg; /* save argument */ ds_busy = busy; /* set visible busy */ sim_activate (&ds_ctrl, ds_ctime); /* schedule */ sim_cancel (&ds_timer); /* activate timeout */ sim_activate (&ds_timer, ds_tmo); return; } /* Request address - if pending eoc, report cylinder + 1 */ void ds_reqad (uint16 *cyl, uint16 *hs) { *cyl = ds_cyl + (ds_eoc? 1: 0); *hs = ds_hs; return; } /* Start seek - schedule whether in bounds or out of bounds */ void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst) { int32 t; uint32 hd, sc; uint32 dtyp = GET_DTYPE (uptr->flags); uptr->FNC = newst; /* set new state */ if (cyl >= drv_tab[dtyp].cyl) { /* out of bounds? */ t = 0; /* don't change cyl */ uptr->STA = uptr->STA | DS2_SC; /* set seek check */ } else { t = abs (uptr->CYL - cyl); /* delta cylinders */ uptr->CYL = cyl; /* put on cylinder */ hd = DSHS_GETHD (ds_hs); /* invalid head or sec? */ sc = DSHS_GETSC (ds_hs); if ((hd >= drv_tab[dtyp].hd) || (sc >= drv_tab[dtyp].sc)) uptr->STA = uptr->STA | DS2_SC; /* set seek check */ else uptr->STA = uptr->STA & ~DS2_SC; /* clear seek check */ } sim_activate (uptr, ds_stime * (t + 1)); /* schedule */ return; } /* Start next sector for read or write - If error, set command done, return TRUE, nothing is scheduled - If implicit seek, return TRUE, implicit seek is scheduled, but state is not changed - we will return here when seek is done - Otherwise, advance state, set position in file, schedule next state */ t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy) { uint32 da, hd, sc; uint32 dtyp = GET_DTYPE (uptr->flags); ds_eod = 0; /* init eod */ ds_ptr = 0; /* init buffer ptr */ if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ ds_cmd_done (1, DS1_S2ERR); return TRUE; } if (ds_eoc) { /* at end of cylinder? */ ds_next_cyl (uptr); /* auto seek to next */ return TRUE; /* or error */ } if (vfy && ((uint32) uptr->CYL != ds_cyl)) { /* on wrong cylinder? */ if (ds_cyl >= drv_tab[dtyp].cyl) /* seeking to bad? */ ds_cmd_done (1, DS1_CYLCE); /* lose */ else ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek right cyl */ return TRUE; } hd = DSHS_GETHD (ds_hs); sc = DSHS_GETSC (ds_hs); if ((uint32) uptr->CYL >= drv_tab[dtyp].cyl) { /* valid cylinder? */ uptr->STA = uptr->STA | DS2_SC; /* set seek check */ ds_cmd_done (1, DS1_S2ERR); /* error */ return TRUE; } if ((hd >= drv_tab[dtyp].hd) || /* valid head, sector? */ (sc >= drv_tab[dtyp].sc)) { ds_cmd_done (1, DS1_HSCE); /* no, error */ return TRUE; } da = GET_DA (uptr->CYL, hd, sc, dtyp); /* position in file */ sim_fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); /* set file pos */ uptr->FNC += DSC_NEXT; /* next state */ sim_activate (uptr, tm); /* activate unit */ return FALSE; } /* Start next sector for read - Do common start for read and write - If error, return, command has been terminated, nothing scheduled - If implicit seek, return, seek scheduled - If no error or seek, state has been advanced and unit scheduled - Read sector - If read error, terminate command and return, nothing scheduled - If no error, advance head/sector, next state scheduled */ t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy) { uint32 t; if (ds_start_rw (uptr, ds_rtime, vfy)) return SCPE_OK; /* new sec; err or seek? */ t = sim_fread (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); for (t = t + off ; t < DS_NUMWDF; t++) dsxb[t] = 0; /* fill sector */ if (ferror (uptr->fileref)) /* error? */ return ds_set_uncorr (uptr); /* say uncorrectable */ ds_next_sec (uptr); /* increment hd, sc */ return SCPE_OK; } /* Start next sector for write - Do common start for read and write - If error, return, command has been terminated, nothing scheduled - If implicit seek, return, seek scheduled - If no error or seek, state has been advanced and unit scheduled - Clear buffer - Set service request */ void ds_start_wr (UNIT *uptr, t_bool vfy) { uint32 i; if ((uptr->flags & UNIT_WPR) || /* write protected? */ (!vfy && ((uptr->flags & UNIT_FMT) == 0))) { /* format, not enbl? */ ds_cmd_done (1, DS1_S2ERR); /* error */ return; } if (ds_start_rw (uptr, ds_rtime, vfy)) return; /* new sec; err or seek? */ for (i = 0; i < DS_NUMWDF; i++) dsxb[i] = 0; /* clear buffer */ ds_srq = SET; /* request word */ dsio (ds_dib.devno, ioSIR, 0); /* set interrupt request */ return; } /* Advance to next sector (but not next cylinder) */ void ds_next_sec (UNIT *uptr) { uint32 dtyp = GET_DTYPE (uptr->flags); ds_hs = ds_hs + 1; /* increment sector */ if (DSHS_GETSC (ds_hs) < drv_tab[dtyp].sc) return; /* end of track? */ ds_hs = ds_hs & ~DSHS_SC; /* yes, wrap sector */ if (ds_fmask & DSC_CYLM) { /* cylinder mode? */ ds_hs = ds_hs + (1 << DSHS_V_HD); /* increment head */ if (DSHS_GETHD (ds_hs) < drv_tab[dtyp].hd) return; /* end of cyl? */ ds_hs = ds_hs & ~DSHS_HD; /* 0 head */ } ds_eoc = 1; /* flag end cylinder */ return; } /* Advance to next cylinder - If autoseek enabled, seek to cylinder +/- 1 - Otherwise, done with end of cylinder error */ void ds_next_cyl (UNIT *uptr) { if (ds_fmask & DSC_AUTO) { /* auto seek allowed? */ if (ds_fmask & DSC_DECR) ds_cyl = (ds_cyl - 1) & DMASK; else ds_cyl = (ds_cyl + 1) & DMASK; ds_eoc = 0; /* clear end cylinder */ ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek, same state */ } else ds_cmd_done (1, DS1_EOCYL); /* no, end of cyl err */ return; } /* Transfer word for read - If end of data, terminate command, nothing scheduled - Otherwise, transfer word, advance state if last word, schedule */ void ds_cont_rd (UNIT *uptr, uint32 bsize) { if (ds_eod) ds_cmd_done (1, DS1_OK); /* DMA end? done */ else if (ds_srq) { /* overrun? */ ds_cmd_done (1, DS1_OVRUN); /* set done */ return; } else { ds_fifo_write (dsxb[ds_ptr++]); /* next word */ ds_srq = SET; /* request service */ dsio (ds_dib.devno, ioSIR, 0); /* set interrupt request */ if (ds_ptr >= bsize) uptr->FNC += DSC_NEXT; /* sec done? next state */ sim_activate (uptr, ds_dtime); /* schedule */ } return; } /* Transfer word for write - Copy word from fifo to buffer - If end of data, write buffer, terminate command, nothing scheduled - If end of sector, write buffer, next state, schedule - Otherwises, set service request, schedule */ t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize) { uint32 i, dat; if (ds_srq) { /* overrun? */ ds_cmd_done (1, DS1_OVRUN); /* set done */ return SCPE_OK; } dsxb[ds_ptr++] = dat = ds_fifo_read (); /* next word */ if (ds_eod || (ds_ptr >= bsize)) { /* xfr or sector done? */ for (i = ds_ptr; i < bsize; i++) dsxb[i] = dat; /* fill sector */ sim_fwrite (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); if (ferror (uptr->fileref)) /* error on write? */ return ds_set_uncorr (uptr); /* uncorrectable */ ds_next_sec (uptr); /* increment hd, sc */ if (ds_eod) { /* end data? */ ds_cmd_done (1, DS1_OK); /* set done */ return SCPE_OK; } else uptr->FNC += DSC_NEXT; /* no, next state */ } else { ds_srq = SET; /* request next word */ dsio (ds_dib.devno, ioSIR, 0); /* set interrupt request */ } sim_activate (uptr, ds_dtime); /* schedule */ return SCPE_OK; } /* End sector for read or write - If end of data, terminate command, nothing scheduled - If end of cylinder, schedule next cylinder - Else schedule start of next sector */ void ds_end_rw (UNIT *uptr, uint32 newst) { uptr->FNC = newst; /* new state */ if (ds_eod) ds_cmd_done (1, DS1_OK); /* done? */ else if (ds_eoc) ds_next_cyl (uptr); /* end cyl? seek */ else sim_activate (uptr, ds_rtime); /* normal transfer */ return; } /* Report uncorrectable data error */ t_stat ds_set_uncorr (UNIT *uptr) { sim_cancel (uptr); /* cancel any operation */ ds_cmd_done (1, DS1_UNCOR); /* done with error */ perror ("DS I/O error"); /* visible error */ clearerr (uptr->fileref); ds_poll (); /* force poll */ return SCPE_IOERR; } /* Fifo read */ uint32 ds_fifo_read (void) { uint32 dat; if (ds_fifo_cnt == 0) return ds_fifo[ds_fifo_rp]; dat = ds_fifo[ds_fifo_rp++]; if (ds_fifo_rp >= DS_FIFO_SIZE) ds_fifo_rp = 0; ds_fifo_cnt--; return dat; } void ds_fifo_write (uint32 dat) { ds_fifo[ds_fifo_ip++] = dat; if (ds_fifo_ip >= DS_FIFO_SIZE) ds_fifo_ip = 0; if (ds_fifo_cnt < DS_FIFO_SIZE) ds_fifo_cnt++; return; } void ds_fifo_reset (void) { uint32 i; ds_fifo_ip = ds_fifo_rp = ds_fifo_cnt = 0; for (i = 0; i < DS_FIFO_SIZE; i++) ds_fifo[i] = 0; return; } /* Controller clear */ t_stat ds_clear (void) { int32 i; ds_cmd = 0; /* clear command */ ds_cmdf = ds_cmdp = 0; /* clear commands flops */ ds_fifo_reset (); /* clear fifo */ ds_eoc = ds_eod = 0; ds_busy = 0; ds_state = DS_IDLE; /* ctrl idle */ ds_lastatn = 0; ds_fmask = 0; ds_ptr = 0; ds_cyl = ds_hs = 0; ds_vctr = 0; for (i = 0; i < DS_NUMDR; i++) { /* loop thru drives */ sim_cancel (&ds_unit[i]); /* cancel activity */ ds_unit[i].FNC = 0; /* clear function */ ds_unit[i].CYL = 0; ds_unit[i].STA = 0; } sim_cancel (&ds_ctrl); sim_cancel (&ds_timer); return SCPE_OK; } /* Reset routine. The PON signal clears the Interface Selected flip-flop, disconnecting the interface from the disc controller. Under simulation, the interface always remains connected to the controller, so we take no special action on power-up. */ t_stat ds_reset (DEVICE *dptr) { dsio (ds_dib.devno, ioPOPIO, 0); /* send POPIO signal */ ds_srq = CLEAR; /* clear SRQ */ return SCPE_OK; } /* Device attach */ t_stat ds_attach (UNIT *uptr, char *cptr) { uint32 i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) return r; /* error? */ ds_load_unload (uptr, 0, NULL, NULL); /* if OK, load heads */ ds_sched_atn (uptr); /* schedule attention */ if (((uptr->flags & UNIT_AUTO) == 0) || /* static size? */ ((p = sim_fsize (uptr->fileref)) == 0)) return SCPE_OK; /* new file? */ for (i = 0; drv_tab[i].sc != 0; i++) { /* find best fit */ if (p <= (drv_tab[i].size * sizeof (uint16))) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Device detach */ t_stat ds_detach (UNIT *uptr) { ds_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads if attached */ return detach_unit (uptr); } /* Load and unload heads */ t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) { if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to [un]load */ if (value == UNIT_UNLOAD) { /* unload heads? */ uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ uptr->STA = DS2_ATN; /* update drive status */ ds_sched_atn (uptr); /* schedule attention */ } else { /* load heads */ uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ uptr->STA = DS2_ATN | DS2_FS; /* update drive status */ } return SCPE_OK; } /* Schedule attention interrupt if CTL set, not restore, and controller idle */ void ds_sched_atn (UNIT *uptr) { int32 i; if (!ds_control || (sim_switches & SIM_SW_REST)) return; for (i = 0; i < (DS_NUMDR + 1); i++) { /* check units, ctrl */ if (sim_is_active (ds_dev.units + i)) return; } uptr->FNC = DSC_ATN; /* pseudo operation */ sim_activate (uptr, 1); /* do immediately */ return; } /* Set size command validation routine */ t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[GET_DTYPE (val)].size; return SCPE_OK; } /* 13037 bootstrap routine (HP 12992B ROM) */ const BOOT_ROM ds_rom = { 0017727, /* STRT JSB STAT ; get status */ 0002021, /* SSA,RSS ; is drive ready? */ 0027742, /* JMP DMA ; yes, set up DMA */ 0013714, /* AND B20 ; no, check status bits */ 0002002, /* SZA ; faulty or hard down? */ 0102030, /* HLT 30B ; HALT 30B */ 0027700, /* JMP STRT ; try again */ 0102011, /* ADR1 OCT 102011 */ 0102055, /* ADR2 OCT 102055 */ 0164000, /* CNT DEC -6144 */ 0000007, /* D7 OCT 7 */ 0001400, /* STCM OCT 1400 */ 0000020, /* B20 OCT 20 */ 0017400, /* STMS OCT 17400 */ 0000000, /* 9 NOP's */ 0000000, 0000000, 0000000, 0000000, 0000000, 0000000, 0000000, 0000000, 0000000, /* STAT NOP ; status check routine */ 0107710, /* CLC DC,C ; set command mode */ 0063713, /* LDA STCM ; get status command */ 0102610, /* OTA DC ; output status command */ 0102310, /* SFS DC ; wait for stat#1 word */ 0027733, /* JMP *-1 */ 0107510, /* LIB DC,C ; B-reg - status#1 word */ 0102310, /* SFS DC ; wait for stat#2 word */ 0027736, /* JMP *-1 */ 0103510, /* LIA DC,C ; A-reg - status#2 word */ 0127727, /* JMP STAT,I ; return */ 0067776, /* DMA LDB DMAC ; get DMA control word */ 0106606, /* OTB 6 ; output DMA ctrl word */ 0067707, /* LDB ADR1 ; get memory address */ 0106702, /* CLC 2 ; set memory addr mode */ 0106602, /* OTB 2 ; output mem addr to DMA */ 0102702, /* STC 2 ; set word count mode */ 0067711, /* LDB CNT ; get word count */ 0106602, /* OTB 2 ; output word cnt to DMA */ 0106710, /* CLC CLC DC ; set command follows */ 0102501, /* LIA 1 ; load switches */ 0106501, /* LIB 1 ; register settings */ 0013712, /* AND D7 ; isolate head number */ 0005750, /* BLF,CLE,SLB ; bit 12 = 0? */ 0027762, /* JMP *+3 ; no, manual boot */ 0002002, /* SZA ; yes, RPL, head# = 0? */ 0001000, /* ALS ; no, head# = 1 --> 2 */ 0001720, /* ALF,ALS ; form cold load */ 0001000, /* ALS ; command word */ 0103706, /* STC 6,C ; activate DMA */ 0103610, /* OTA DC,C ; output cold load cmd */ 0102310, /* SFS DC ; is cold load done? */ 0027766, /* JMP *-1 ; no, wait */ 0017727, /* JSB STAT ; yes, get status */ 0060001, /* LDA 1 ; get status word #1 */ 0013715, /* AND STMS ; isolate status bits */ 0002002, /* SZA ; is transfer ok? */ 0027700, /* JMP STRT ; no, try again */ 0117710, /* JSB ADR2,I ; yes, start program */ 0000010, /* DMAC ABS DC ; DMA command word */ 0170100, /* ABS -STRT */ }; t_stat ds_boot (int32 unitno, DEVICE *dptr) { int32 dev; if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ dev = ds_dib.devno; /* get data chan dev */ if (ibl_copy (ds_rom, dev)) return SCPE_IERR; /* copy boot to memory */ SR = (SR & (IBL_OPT | IBL_DS_HEAD)) | IBL_DS | IBL_MAN | (dev << IBL_V_DEV); return SCPE_OK; } simh-3.8.1/HP2100/hp2100_cpu7.c0000644000175000017500000011610211062235612013466 0ustar vlmvlm/* hp2100_cpu7.c: HP 1000 VIS and SIGNAL/1000 microcode Copyright (c) 2008, Holger Veit Copyright (c) 2006-2008, J. David Bryan 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 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. CPU7 Vector Instruction Set and SIGNAL firmware 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 30-Apr-08 JDB Updated SIGNAL code from Holger 24-Apr-08 HV Implemented SIGNAL 20-Apr-08 JDB Updated comments 26-Feb-08 HV Implemented VIS Primary references: - HP 1000 M/E/F-Series Computers Technical Reference Handbook (5955-0282, Mar-1980) - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation (92851-90001, Mar-1981) - Macro/1000 Reference Manual (92059-90001, Dec-1992) Additional references are listed with the associated firmware implementations, as are the HP option model numbers pertaining to the applicable CPUs. */ #include "hp2100_defs.h" #include "hp2100_cpu.h" #include "hp2100_cpu1.h" #if defined (HAVE_INT64) /* int64 support available */ #include "hp2100_fp1.h" static const OP zero = { { 0, 0, 0, 0, 0 } }; /* DEC 0.0D0 */ /* Vector Instruction Set The VIS provides instructions that operate on one-dimensional arrays of floating-point values. Both single- and double-precision operations are supported. VIS uses the F-Series floating-point processor to handle the floating-point math. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A N/A 12824A The routines are mapped to instruction codes as follows: Single-Precision Double-Precision Instr. Opcode Subcod Instr. Opcode Subcod Description ------ ------ ------ ------ ------ ------ ----------------------------- VADD 101460 000000 DVADD 105460 004002 Vector add VSUB 101460 000020 DVSUB 105460 004022 Vector subtract VMPY 101460 000040 DVMPY 105460 004042 Vector multiply VDIV 101460 000060 DVDIV 105460 004062 Vector divide VSAD 101460 000400 DVSAD 105460 004402 Scalar-vector add VSSB 101460 000420 DVSSB 105460 004422 Scalar-vector subtract VSMY 101460 000440 DVSMY 105460 004442 Scalar-vector multiply VSDV 101460 000460 DVSDV 105460 004462 Scalar-vector divide VPIV 101461 0xxxxx DVPIV 105461 0xxxxx Vector pivot VABS 101462 0xxxxx DVABS 105462 0xxxxx Vector absolute value VSUM 101463 0xxxxx DVSUM 105463 0xxxxx Vector sum VNRM 101464 0xxxxx DVNRM 105464 0xxxxx Vector norm VDOT 101465 0xxxxx DVDOT 105465 0xxxxx Vector dot product VMAX 101466 0xxxxx DVMAX 105466 0xxxxx Vector maximum value VMAB 101467 0xxxxx DVMAB 105467 0xxxxx Vector maximum absolute value VMIN 101470 0xxxxx DVMIN 105470 0xxxxx Vector minimum value VMIB 101471 0xxxxx DVMIB 105471 0xxxxx Vector minimum absolute value VMOV 101472 0xxxxx DVMOV 105472 0xxxxx Vector move VSWP 101473 0xxxxx DVSWP 105473 0xxxxx Vector swap .ERES 101474 -- -- -- -- Resolve array element address .ESEG 101475 -- -- -- -- Load MSEG maps .VSET 101476 -- -- -- -- Vector setup [test] -- -- -- 105477 -- [self test] Instructions use IR bit 11 to select single- or double-precision format. The double-precision instruction names begin with "D" (e.g., DVADD vs. VADD). Most VIS instructions are two words in length, with a sub-opcode immediately following the primary opcode. Notes: 1. The .VECT (101460) and .DVCT (105460) opcodes preface a single- or double-precision arithmetic operation that is determined by the sub-opcode value. The remainder of the dual-precision sub-opcode values are "don't care," except for requiring a zero in bit 15. 2. The VIS uses the hardware FPP of the F-Series. FPP malfunctions are detected by the VIS firmware and are indicated by a memory-protect violation and setting the overflow flag. Under simulation, malfunctions cannot occur. Additional references: - 12824A Vector Instruction Set User's Manual (12824-90001, Jun-1979). - VIS Microcode Source (12824-18059, revision 3). */ /* implemented in hp2100_cpu5.c (RTE-IV EMA functions) */ extern t_stat cpu_ema_eres(uint32* rtn,uint32 dtbl,uint32 atbl, t_bool debug); extern t_stat cpu_ema_eseg(uint32* rtn,uint32 ir,uint32 tbl, t_bool debug); extern t_stat cpu_ema_vset(uint32* rtn,OPS op, t_bool debug); static const OP_PAT op_vis[16] = { OP_N, OP_AAKAKAKK,OP_AKAKK, OP_AAKK, /* .VECT VPIV VABS VSUM */ OP_AAKK, OP_AAKAKK, OP_AAKK, OP_AAKK, /* VNRM VDOT VMAX VMAB */ OP_AAKK, OP_AAKK, OP_AKAKK, OP_AKAKK, /* VMIN VMIB VMOV VSWP */ OP_AA, OP_A, OP_AAACCC,OP_N /* .ERES .ESEG .VSET [test] */ }; static const t_bool op_ftnret[16] = { FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, }; /* handle the scalar/vector base ops */ static void vis_svop(uint32 subcode, OPS op, OPSIZE opsize) { OP v1,v2; int16 delta = opsize==fp_f ? 2 : 4; OP s = ReadOp(op[0].word,opsize); uint32 v1addr = op[1].word; int16 ix1 = INT16(op[2].word) * delta; uint32 v2addr = op[3].word; int16 ix2 = INT16(op[4].word) * delta; int16 i, n = INT16(op[5].word); uint32 fpuop = (subcode & 060) | (opsize==fp_f ? 0 : 2); if (n <= 0) return; for (i=0; ifpk[0] & 0100000) static void vis_abs(OP* in, uint32 opsize) { uint32 sign = GET_MSIGN(in); /* get sign */ if (sign) (void)fp_pcom(in, opsize); /* if negative, make positive */ } static void vis_minmax(OPS op,OPSIZE opsize,t_bool domax,t_bool doabs) { OP v1,vmxmn,res; int16 delta = opsize==fp_f ? 2 : 4; uint32 mxmnaddr = op[0].word; uint32 v1addr = op[1].word; int16 ix1 = INT16(op[2].word) * delta; int16 n = INT16(op[3].word); int16 i,mxmn,sign; int32 subop = 020 | (opsize==fp_f ? 0 : 2); if (n <= 0) return; mxmn = 0; /* index of maxmin element */ vmxmn = ReadOp(v1addr,opsize); /* initialize with first element */ if (doabs) vis_abs(&vmxmn,opsize); /* ABS(v[1]) if requested */ for (i = 0; ifpk[0] = in.fpk[0]; out->fpk[1] = (in.fpk[1] & 0177400) | (in.fpk[3] & 0377); } static void vis_vsmnm(OPS op,OPSIZE opsize,t_bool doabs) { uint32 fpuop; OP v1,sumnrm = zero; int16 delta = opsize==fp_f ? 2 : 4; uint32 saddr = op[0].word; uint32 v1addr = op[1].word; int16 ix1 = INT16(op[2].word) * delta; int16 i,n = INT16(op[3].word); if (n <= 0) return; /* calculates sumnrm = sumnrm + DBLE(v1[i]) resp DBLE(ABS(v1[i])) for incrementing i */ for (i=0; i>CPU VIS: IR = %06o/%06o (", /* print preamble and IR */ IR, subcode); fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ NULL, SWMASK('M')); fprintf (sim_deb, "), P = %06o", err_PC); /* print location */ fprint_ops (pattern, op); /* print operands */ fputc ('\n', sim_deb); /* terminate line */ } switch (entry) { /* decode IR<3:0> */ case 000: /* .VECT (OP_special) */ if (subcode & 0400) vis_svop(subcode,op,opsize); /* scalar/vector op */ else vis_vvop(subcode,op,opsize); /* vector/vector op */ break; case 001: /* VPIV (OP_(A)AAKAKAKK) */ vis_vpiv(op,opsize); break; case 002: /* VABS (OP_(A)AKAKK) */ vis_vabs(op,opsize); break; case 003: /* VSUM (OP_(A)AAKK) */ vis_vsmnm(op,opsize,FALSE); break; case 004: /* VNRM (OP_(A)AAKK) */ vis_vsmnm(op,opsize,TRUE); break; case 005: /* VDOT (OP_(A)AAKAKK) */ vis_vdot(op,opsize); break; case 006: /* VMAX (OP_(A)AAKK) */ vis_minmax(op,opsize,TRUE,FALSE); break; case 007: /* VMAB (OP_(A)AAKK) */ vis_minmax(op,opsize,TRUE,TRUE); break; case 010: /* VMIN (OP_(A)AAKK) */ vis_minmax(op,opsize,FALSE,FALSE); break; case 011: /* VMIB (OP_(A)AAKK) */ vis_minmax(op,opsize,FALSE,TRUE); break; case 012: /* VMOV (OP_(A)AKAKK) */ vis_movswp(op,opsize,FALSE); break; case 013: /* VSWP (OP_(A)AKAKK) */ vis_movswp(op,opsize,TRUE); break; case 014: /* .ERES (OP_(A)AA) */ reason = cpu_ema_eres(&rtn,op[2].word,PC,debug); /* handle the ERES instruction */ PC = rtn; if (debug) fprintf (sim_deb, ">>CPU VIS: return .ERES: AR = %06o, BR = %06o, rtn=%s\n", AR, BR, PC==op[0].word ? "error" : "good"); break; case 015: /* .ESEG (OP_(A)A) */ reason = cpu_ema_eseg(&rtn,IR,op[0].word,debug); /* handle the ESEG instruction */ PC = rtn; if (debug) fprintf (sim_deb, ">>CPU VIS: return .ESEG: AR = %06o , BR = %06o, rtn=%s\n", AR, BR, rtn==rtn1 ? "error" : "good"); break; case 016: /* .VSET (OP_(A)AAACCC) */ reason = cpu_ema_vset(&rtn,op,debug); PC = rtn; if (debug) fprintf (sim_deb, ">>CPU VIS: return .VSET: AR = %06o BR = %06o, rtn=%s\n", AR, BR, rtn==rtn1 ? "error" : (rtn==(rtn1+1) ? "hard" : "easy") ); break; case 017: /* [test] (OP_N) */ XR = 3; /* firmware revision */ SR = 0102077; /* test passed code */ PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/VIS */ break; default: /* others undefined */ reason = stop_inst; } return reason; } /* SIGNAL/1000 Instructions The SIGNAL/1000 instructions provide fast Fourier transforms and complex arithmetic. They utilize the F-Series floating-point processor and the Vector Instruction Set. Option implementation by CPU was as follows: 2114 2115 2116 2100 1000-M 1000-E 1000-F ------ ------ ------ ------ ------ ------ ------ N/A N/A N/A N/A N/A N/A 92835A The routines are mapped to instruction codes as follows: Instr. 1000-F Description ------ ------ ---------------------------------------------- BITRV 105600 Bit reversal BTRFY 105601 Butterfly algorithm UNSCR 105602 Unscramble for phasor MPY PRSCR 105603 Unscramble for phasor MPY BITR1 105604 Swap two elements in array (alternate format) BTRF1 105605 Butterfly algorithm (alternate format) .CADD 105606 Complex number addition .CSUB 105607 Complex number subtraction .CMPY 105610 Complex number multiplication .CDIV 105611 Complex number division CONJG 105612 Complex conjugate ..CCM 105613 Complex complement AIMAG 105614 Return imaginary part CMPLX 105615 Form complex number [nop] 105616 [no operation] [test] 105617 [self test] Notes: 1. SIGNAL/1000 ROM data are available from Bitsavers. Additional references (documents unavailable): - HP Signal/1000 User Reference and Installation Manual (92835-90002). - SIGNAL/1000 Microcode Source (92835-18075, revision 2). */ #define RE(x) (x+0) #define IM(x) (x+2) static const OP_PAT op_signal[16] = { OP_AAKK, OP_AAFFKK, OP_AAFFKK,OP_AAFFKK, /* BITRV BTRFY UNSCR PRSCR */ OP_AAAKK, OP_AAAFFKK,OP_AAA, OP_AAA, /* BITR1 BTRF1 .CADD .CSUB */ OP_AAA, OP_AAA, OP_AAA, OP_A, /* .CMPY .CDIV CONJG ..CCM */ OP_AA, OP_AAFF, OP_N, OP_N /* AIMAG CMPLX --- [test]*/ }; /* complex addition helper */ static void sig_caddsub(uint32 addsub,OPS op) { OP a,b,c,d,p1,p2; a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ b = ReadOp(IM(op[1].word), fp_f); c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ d = ReadOp(IM(op[2].word), fp_f); (void)fp_exec(addsub,&p1, a, c); /* add real */ (void)fp_exec(addsub,&p2, b, d); /* add imag */ WriteOp(RE(op[0].word), p1, fp_f); /* write result */ WriteOp(IM(op[0].word), p2, fp_f); /* write result */ } /* butterfly operation helper */ static void sig_btrfy(uint32 re,uint32 im,OP wr,OP wi,uint32 k, uint32 n2) { /* * v(k)-------->o-->o----> v(k) * \ / * x * / \ * v(k+N/2)---->o-->o----> v(k+N/2) * Wn -1 * */ OP p1,p2,p3,p4; OP v1r = ReadOp(re+k, fp_f); /* read v1 */ OP v1i = ReadOp(im+k, fp_f); OP v2r = ReadOp(re+k+n2, fp_f); /* read v2 */ OP v2i = ReadOp(im+k+n2, fp_f); /* (p1,p2) := cmul(w,v2) */ (void)fp_exec(040, &p1, wr, v2r); /* S7,8 p1 := wr*v2r */ (void)fp_exec(040, ACCUM, wi, v2i); /* ACCUM := wi*v2i */ (void)fp_exec(024, &p1, p1, NOP); /* S7,S8 p1 := wr*v2r-wi*v2i ==real(w*v2) */ (void)fp_exec(040, &p2, wi, v2r); /* S9,10 p2 := wi*v2r */ (void)fp_exec(040, ACCUM, wr, v2i); /* ACCUM := wr*v2i */ (void)fp_exec(004, &p2, p2, NOP); /* S9,10 p2 := wi*v2r+wr*v2i ==imag(w*v2) */ /* v2 := v1 - (p1,p2) */ (void)fp_exec(020, &p3, v1r, p1); /* v2r := v1r-real(w*v2) */ (void)fp_exec(020, &p4, v1i, p2); /* v2i := v1i-imag(w*v2) */ WriteOp(re+k+n2, p3, fp_f); /* write v2r */ WriteOp(im+k+n2, p4, fp_f); /* write v2i */ /* v1 := v1 + (p1,p2) */ (void)fp_exec(0, &p3, v1r, p1); /* v1r := v1r+real(w*v2) */ (void)fp_exec(0, &p4, v1i, p2); /* v1i := v1i+imag(w*v2) */ WriteOp(re+k, p3, fp_f); /* write v1r */ WriteOp(im+k, p4, fp_f); /* write v1i */ O = 0; } /* helper for bit reversal * idx is 0-based already */ static void sig_bitrev(uint32 re,uint32 im, uint32 idx, uint32 log2n, int sz) { uint32 i, org=idx, rev = 0; OP v1r,v1i,v2r,v2i; for (i=0; i>= 1; } if (rev < idx) return; /* avoid swapping same pair twice in loop */ idx *= sz; /* adjust for element size */ rev *= sz; /* (REAL*4 vs COMPLEX*8) */ v1r = ReadOp(re+idx, fp_f); /* read 1st element */ v1i = ReadOp(im+idx, fp_f); v2r = ReadOp(re+rev, fp_f); /* read 2nd element */ v2i = ReadOp(im+rev, fp_f); WriteOp(re+idx, v2r, fp_f); /* swap elements */ WriteOp(im+idx, v2i, fp_f); WriteOp(re+rev, v1r, fp_f); WriteOp(im+rev, v1i, fp_f); } /* helper for PRSCR/UNSCR */ static OP sig_scadd(uint32 oper,t_bool addh, OP a, OP b) { OP r; static const OP plus_half = { { 0040000, 0000000 } }; /* DEC +0.5 */ (void)fp_exec(oper,&r,a,b); /* calculate r := a +/- b */ if (addh) (void)fp_exec(044,&r,plus_half,NOP); /* if addh set, multiply by 0.5 */ return r; } /* complex multiply helper */ static void sig_cmul(OP *r, OP *i, OP a, OP b, OP c, OP d) { OP p; (void)fp_exec(040, &p , a, c); /* p := ac */ (void)fp_exec(040, ACCUM, b, d); /* ACCUM := bd */ (void)fp_exec(024, r, p , NOP); /* real := ac-bd */ (void)fp_exec(040, &p, a, d); /* p := ad */ (void)fp_exec(040, ACCUM, b, c); /* ACCUM := bc */ (void)fp_exec(004, i, p, NOP); /* imag := ad+bc */ } t_stat cpu_signal (uint32 IR, uint32 intrq) { t_stat reason = SCPE_OK; OPS op; OP a,b,c,d,p1,p2,p3,p4,m1,m2,wr,wi; uint32 entry, v, idx1, idx2; int32 exc, exd; t_bool debug = DEBUG_PRI (cpu_dev, DEB_SIG); entry = IR & 017; /* mask to entry point */ if (op_signal[entry] != OP_N) if (reason = cpu_ops (op_signal[entry], op, intrq)) /* get instruction operands */ return reason; if (debug) { /* debugging? */ fprintf (sim_deb, ">>CPU SIG: IR = %06o (", IR); /* print preamble and IR */ fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ NULL, SWMASK('M')); fprintf (sim_deb, "), P = %06o", err_PC); /* print location */ fprint_ops (op_signal[entry], op); /* print operands */ fputc ('\n', sim_deb); /* terminate line */ } switch (entry) { /* decode IR<3:0> */ case 000: /* BITRV (OP_AAKK) */ /* BITRV * bit reversal for FFT * JSB BITRV * DEF ret(,I) return address * DEF vect,I base address of array * DEF idx,I index bitmap to be reversed (one-based) * DEF nbits,I number of bits of index * * Given a complex*8 vector of nbits (power of 2), this calculates: * swap( vect[idx], vect[rev(idx)]) where rev(i) is the bitreversed value of i */ sig_bitrev(op[1].word, op[1].word+2, op[2].word-1, op[3].word, 4); PC = op[0].word & VAMASK; break; case 001: /* BTRFY (OP_AAFFKK) */ /* BTRFY - butterfly operation * JSB BTRFY * DEF ret(,I) return address * DEF vect(,I) complex*8 vector * DEF wr,I real part of W * DEF wi,I imag part of W * DEF node,I index of 1st op (1 based) * DEF lmax,I offset to 2nd op (0 based) */ sig_btrfy(op[1].word, op[1].word+2, op[2], op[3], 2*(op[4].word-1), 2*op[5].word); PC = op[0].word & VAMASK; break; case 002: /* UNSCR (OP_AAFFKK) */ /* UNSCR unscramble for phasor MPY * JSB UNSCR * DEF ret(,I) * DEF vector,I * DEF WR * DEF WI * DEF idx1,I * DEF idx2,I */ v = op[1].word; idx1 = 2 * (op[4].word - 1); idx2 = 2 * (op[5].word - 1); wr = op[2]; /* read WR */ wi = op[3]; /* read WI */ p1 = ReadOp(RE(v + idx1), fp_f); /* S1 VR[idx1] */ p2 = ReadOp(RE(v + idx2), fp_f); /* S2 VR[idx2] */ p3 = ReadOp(IM(v + idx1), fp_f); /* S9 VI[idx1] */ p4 = ReadOp(IM(v + idx2), fp_f); /* S10 VI[idx2] */ c = sig_scadd(000, TRUE, p3, p4); /* S5,6 0.5*(p3+p4) */ d = sig_scadd(020, TRUE, p2, p1); /* S7,8 0.5*(p2-p1) */ sig_cmul(&m1, &m2, wr, wi, c, d); /* (WR,WI) * (c,d) */ c = sig_scadd(000, TRUE, p1, p2); /* 0.5*(p1+p2) */ d = sig_scadd(020, TRUE, p3, p4); /* 0.5*(p3-p4) */ (void)fp_exec(000, &p1, c, m1); /* VR[idx1] := 0.5*(p1+p2) + real(W*(c,d)) */ WriteOp(RE(v + idx1), p1, fp_f); (void)fp_exec(000, &p2, d, m2); /* VI[idx1] := 0.5*(p3-p4) + imag(W*(c,d)) */ WriteOp(IM(v + idx1), p2, fp_f); (void)fp_exec(020, &p1, c, m1); /* VR[idx2] := 0.5*(p1+p2) - imag(W*(c,d)) */ WriteOp(RE(v + idx2), p1, fp_f); (void)fp_exec(020, &p2, d, m2); /* VI[idx2] := 0.5*(p3-p4) - imag(W*(c,d)) */ WriteOp(IM(v + idx2), p2, fp_f); PC = op[0].word & VAMASK; break; case 003: /* PRSCR (OP_AAFFKK) */ /* PRSCR unscramble for phasor MPY * JSB PRSCR * DEF ret(,I) * DEF vector,I * DEF WR * DEF WI * DEF idx1,I * DEF idx2,I */ v = op[1].word; idx1 = 2 * (op[4].word - 1); idx2 = 2 * (op[5].word - 1); wr = op[2]; /* read WR */ wi = op[3]; /* read WI */ p1 = ReadOp(RE(v + idx1), fp_f); /* VR[idx1] */ p2 = ReadOp(RE(v + idx2), fp_f); /* VR[idx2] */ p3 = ReadOp(IM(v + idx1), fp_f); /* VI[idx1] */ p4 = ReadOp(IM(v + idx2), fp_f); /* VI[idx2] */ c = sig_scadd(020, FALSE, p1, p2); /* p1-p2 */ d = sig_scadd(000, FALSE, p3, p4); /* p3+p4 */ sig_cmul(&m1,&m2, wr, wi, c, d); /* (WR,WI) * (c,d) */ c = sig_scadd(000, FALSE, p1, p2); /* p1+p2 */ d = sig_scadd(020, FALSE, p3,p4); /* p3-p4 */ (void)fp_exec(020, &p1, c, m2); /* VR[idx1] := (p1-p2) - imag(W*(c,d)) */ WriteOp(RE(v + idx1), p1, fp_f); (void)fp_exec(000, &p2, d, m1); /* VI[idx1] := (p3-p4) + real(W*(c,d)) */ WriteOp(IM(v + idx1), p2, fp_f); (void)fp_exec(000, &p1, c, m2); /* VR[idx2] := (p1+p2) + imag(W*(c,d)) */ WriteOp(RE(v + idx2), p1, fp_f); (void)fp_exec(020, &p2, m1, d); /* VI[idx2] := imag(W*(c,d)) - (p3-p4) */ WriteOp(IM(v + idx2), p2, fp_f); PC = op[0].word & VAMASK; break; case 004: /* BITR1 (OP_AAAKK) */ /* BITR1 * bit reversal for FFT, alternative version * JSB BITR1 * DEF ret(,I) return address if already swapped * DEF revect,I base address of real vect * DEF imvect,I base address of imag vect * DEF idx,I index bitmap to be reversed (one-based) * DEF nbits,I number of bits of index * * Given a complex*8 vector of nbits (power of 2), this calculates: * swap( vect[idx], vect[rev(idx)]) where rev(i) is the bitreversed value of i * * difference to BITRV is that BITRV uses complex*8, and BITR1 uses separate real*4 * vectors for Real and Imag parts */ sig_bitrev(op[1].word, op[2].word, op[3].word-1, op[4].word, 2); PC = op[0].word & VAMASK; break; case 005: /* BTRF1 (OP_AAAFFKK) */ /* BTRF1 - butterfly operation with real*4 vectors * JSB BTRF1 * DEF ret(,I) return address * DEF rvect,I real part of vector * DEF ivect,I imag part of vector * DEF wr,I real part of W * DEF wi,I imag part of W * DEF node,I index (1 based) * DEF lmax,I index (0 based) */ sig_btrfy(op[1].word, op[2].word, op[3], op[4], op[5].word-1, op[6].word); PC = op[0].word & VAMASK; break; case 006: /* .CADD (OP_AAA) */ /* .CADD Complex addition * JSB .CADD * DEF result,I * DEF oprd1,I * DEF oprd2,I * complex addition is: (a+bi) + (c+di) => (a+c) + (b+d)i */ sig_caddsub(000,op); break; case 007: /* .CSUB (OP_AAA) */ /* .CSUB Complex subtraction * JSB .CSUB * DEF result,I * DEF oprd1,I * DEF oprd2,I * complex subtraction is: (a+bi) - (c+di) => (a - c) + (b - d)i */ sig_caddsub(020,op); break; case 010: /* .CMUL (OP_AAA) */ /* .CMPY Complex multiplication * call: * JSB .CMPY * DEF result,I * DEF oprd1,I * DEF oprd2,I * complex multiply is: (a+bi)*(c+di) => (ac-bd) + (ad+bc)i */ a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ b = ReadOp(IM(op[1].word), fp_f); c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ d = ReadOp(IM(op[2].word), fp_f); sig_cmul(&p1, &p2, a, b, c, d); WriteOp(RE(op[0].word), p1, fp_f); /* write real result */ WriteOp(IM(op[0].word), p2, fp_f); /* write imag result */ break; case 011: /* .CDIV (OP_AAA) */ /* .CDIV Complex division * call: * JSB .CDIV * DEF result,I * DEF oprd1,I * DEF oprd2,I * complex division is: (a+bi)/(c+di) => ((ac+bd) + (bc-ad)i)/(c^2+d^2) */ a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ b = ReadOp(IM(op[1].word), fp_f); c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ d = ReadOp(IM(op[2].word), fp_f); (void)fp_unpack (NULL, &exc, c, fp_f); /* get exponents */ (void)fp_unpack (NULL, &exd, d, fp_f); if (exc < exd) { /* ensure c/d < 1 */ p1 = a; a = c; c = p1; /* swap dividend and divisor */ p1 = b; b = d; d = p1; } (void)fp_exec(060, &p1, d, c); /* p1,accu := d/c */ (void)fp_exec(044, ACCUM, d, NOP); /* ACCUM := dd/c */ (void)fp_exec(004, &p2, c, NOP); /* p2 := c + dd/c */ (void)fp_exec(040, ACCUM, b, p1); /* ACCUM := bd/c */ (void)fp_exec(004, ACCUM, a, NOP); /* ACCUM := a + bd/c */ (void)fp_exec(070, &p3, NOP, p2); /* p3 := (a+bd/c)/(c+dd/c) == (ac+bd)/(cc+dd) */ WriteOp(RE(op[0].word), p3, fp_f); /* Write real result */ (void)fp_exec(040, ACCUM, a, p1); /* ACCUM := ad/c */ (void)fp_exec(030, ACCUM, NOP, b); /* ACCUM := ad/c - b */ if (exd < exc) { /* was not swapped? */ (void)fp_exec(024, ACCUM, zero, NOP); /* ACCUM := -ACCUM */ } (void)fp_exec(070, &p3, NOP, p2); /* p3 := (b-ad/c)/(c+dd/c) == (bc-ad)/cc+dd) */ WriteOp(IM(op[0].word), p3, fp_f); /* Write imag result */ break; case 012: /* CONJG (OP_AAA) */ /* CONJG build A-Bi from A+Bi * call: * JSB CONJG * DEF RTN * DEF res,I result * DEF arg,I input argument */ a = ReadOp(RE(op[2].word), fp_f); /* read real */ b = ReadOp(IM(op[2].word), fp_f); /* read imag */ (void)fp_pcom(&b, fp_f); /* negate imag */ WriteOp(RE(op[1].word), a, fp_f); /* write real */ WriteOp(IM(op[1].word), b, fp_f); /* write imag */ break; case 013: /* ..CCM (OP_A) */ /* ..CCM complement complex * call * JSB ..CCM * DEF arg * build (-RE,-IM) */ v = op[0].word; a = ReadOp(RE(v), fp_f); /* read real */ b = ReadOp(IM(v), fp_f); /* read imag */ (void)fp_pcom(&a, fp_f); /* negate real */ (void)fp_pcom(&b, fp_f); /* negate imag */ WriteOp(RE(v), a, fp_f); /* write real */ WriteOp(IM(v), b, fp_f); /* write imag */ break; case 014: /* AIMAG (OP_AA) */ /* AIMAG return the imaginary part in AB * JSB AIMAG * DEF *+2 * DEF cplx(,I) * returns: AB imaginary part of complex number */ a = ReadOp(IM(op[1].word), fp_f); /* read imag */ AR = a.fpk[0]; /* move MSB to A */ BR = a.fpk[1]; /* move LSB to B */ break; case 015: /* CMPLX (OP_AFF) */ /* CMPLX form a complex number * JSB CMPLX * DEF *+4 * DEF result,I complex number * DEF repart,I real value * DEF impart,I imaginary value */ WriteOp(RE(op[1].word), op[2], fp_f); /* write real part */ WriteOp(IM(op[1].word), op[3], fp_f); /* write imag part */ break; case 017: /* [slftst] (OP_N) */ XR = 2; /* firmware revision */ SR = 0102077; /* test passed code */ PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/SIGNAL1000 */ break; case 016: /* invalid */ default: /* others undefined */ reason = stop_inst; } return reason; } #endif /* end of int64 support */ simh-3.8.1/HP2100/hp2100_fp1.h0000644000175000017500000000501610550544400013303 0ustar vlmvlm/* hp2100_fp1.h: HP 2100/1000 multiple-precision floating point definitions Copyright (c) 2005-2006, J. David Bryan 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 AUTHOR 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 author. 16-Oct-06 JDB Generalized FP calling sequences for F-Series 12-Oct-06 JDB Altered x_trun for F-Series FFP compatibility */ #ifndef _HP2100_FP1_H_ #define _HP2100_FP1_H_ 0 /* Special operands. */ #define ACCUM NULL /* result not returned */ static const OP NOP = { { 0, 0, 0, 0, 0 } }; /* unneeded operand */ /* Generalized floating-point handlers. */ void fp_prec (uint16 opcode, OPSIZE *operand_l, OPSIZE *operand_r, OPSIZE *result); uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r); OP fp_accum (const OP *operand, OPSIZE precision); uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision); uint32 fp_ucom (OP *mantissa, OPSIZE precision); uint32 fp_pcom (OP *packed, OPSIZE precision); uint32 fp_trun (OP *result, OP source, OPSIZE precision); uint32 fp_cvt (OP *result, OPSIZE source_precision, OPSIZE dest_precision); #endif simh-3.8.1/HP2100/hp2100_cpu.h0000644000175000017500000003722211050127232013404 0ustar vlmvlm/* hp2100_cpu.h: HP 2100 CPU definitions Copyright (c) 2005-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 15-Jul-08 JDB Rearranged declarations with hp2100_cpu.c and hp2100_defs.h 26-Jun-08 JDB Added mp_control to CPU state externals 24-Apr-08 JDB Added calc_defer() prototype 20-Apr-08 JDB Added DEB_VIS and DEB_SIG debug flags 26-Nov-07 JDB Added extern sim_deb, cpu_dev, DEB flags for debug printouts 05-Nov-07 JDB Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl, ReadIO, WriteIO for RTE-6/VM microcode support 16-Dec-06 JDB Added UNIT_2115 and UNIT_2114 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c 26-Sep-06 JDB Added CPU externs for microcode simulators 16-Aug-06 JDB Added UNIT_EMA for future RTE-4 EMA microcode Added UNIT_VMA for future RTE-6 VMA and OS microcode Added UNIT_1000_F for future F-Series support 09-Aug-06 JDB Added UNIT_DBI for double integer microcode 21-Jan-05 JDB Reorganized CPU option flags 14-Jan-05 RMS Cloned from hp2100_cpu.c CPU models are broken down into family, type, and series to facilitate option validation. Bit 3 encodes the family, bit 2 encodes the type, and bits 1:0 encode the series within the type. */ #ifndef _HP2100_CPU_H_ #define _HP2100_CPU_H_ 0 #include /* CPU model definition flags */ #define CPU_V_SERIES 0 #define CPU_V_TYPE 2 #define CPU_V_FAMILY 3 #define FAMILY_21XX (0 << CPU_V_FAMILY) #define FAMILY_1000 (1 << CPU_V_FAMILY) #define TYPE_211X (0 << CPU_V_TYPE) /* 2114, 2115, 2116 */ #define TYPE_2100 (1 << CPU_V_TYPE) /* 2100A, 2100S */ #define TYPE_1000MEF (0 << CPU_V_TYPE) /* 1000-M, 1000-E, 1000-F */ #define TYPE_1000AL (1 << CPU_V_TYPE) /* 1000-L, A600, A700, A900, A990 */ #define SERIES_16 (0 << CPU_V_SERIES) /* 211X */ #define SERIES_15 (1 << CPU_V_SERIES) /* 211X */ #define SERIES_14 (2 << CPU_V_SERIES) /* 211X */ #define SERIES_00 (0 << CPU_V_SERIES) /* 2100 */ #define SERIES_M (0 << CPU_V_SERIES) /* 1000 */ #define SERIES_E (1 << CPU_V_SERIES) /* 1000 */ #define SERIES_F (2 << CPU_V_SERIES) /* 1000 */ /* CPU unit flags */ #define UNIT_M_CPU 017 /* CPU model mask [3:0] */ #define UNIT_M_TYPE 014 /* CPU type mask [3:2] */ #define UNIT_M_FAMILY 010 /* CPU family mask [3:3] */ #define UNIT_V_CPU (UNIT_V_UF + 0) /* CPU model bits 0-3 */ #define UNIT_V_EAU (UNIT_V_UF + 4) /* EAU installed */ #define UNIT_V_FP (UNIT_V_UF + 5) /* FP installed */ #define UNIT_V_IOP (UNIT_V_UF + 6) /* IOP installed */ #define UNIT_V_DMS (UNIT_V_UF + 7) /* DMS installed */ #define UNIT_V_FFP (UNIT_V_UF + 8) /* FFP installed */ #define UNIT_V_DBI (UNIT_V_UF + 9) /* DBI installed */ #define UNIT_V_EMA (UNIT_V_UF + 10) /* RTE-4 EMA installed */ #define UNIT_V_VMAOS (UNIT_V_UF + 11) /* RTE-6 VMA/OS installed */ #define UNIT_V_VIS (UNIT_V_UF + 12) /* VIS installed */ #define UNIT_V_SIGNAL (UNIT_V_UF + 13) /* SIGNAL/1000 installed */ /* Future microcode expansion; reuse flags bottom-up if needed */ #define UNIT_V_DS (UNIT_V_UF + 14) /* DS installed */ /* Unit models */ #define UNIT_MODEL_MASK (UNIT_M_CPU << UNIT_V_CPU) #define UNIT_2116 ((FAMILY_21XX | TYPE_211X | SERIES_16) << UNIT_V_CPU) #define UNIT_2115 ((FAMILY_21XX | TYPE_211X | SERIES_15) << UNIT_V_CPU) #define UNIT_2114 ((FAMILY_21XX | TYPE_211X | SERIES_14) << UNIT_V_CPU) #define UNIT_2100 ((FAMILY_21XX | TYPE_2100 | SERIES_00) << UNIT_V_CPU) #define UNIT_1000_M ((FAMILY_1000 | TYPE_1000MEF | SERIES_M) << UNIT_V_CPU) #define UNIT_1000_E ((FAMILY_1000 | TYPE_1000MEF | SERIES_E) << UNIT_V_CPU) #define UNIT_1000_F ((FAMILY_1000 | TYPE_1000MEF | SERIES_F) << UNIT_V_CPU) /* Unit types */ #define UNIT_TYPE_MASK (UNIT_M_TYPE << UNIT_V_CPU) #define UNIT_TYPE_211X ((FAMILY_21XX | TYPE_211X) << UNIT_V_CPU) #define UNIT_TYPE_2100 ((FAMILY_21XX | TYPE_2100) << UNIT_V_CPU) #define UNIT_TYPE_1000 ((FAMILY_1000 | TYPE_1000MEF) << UNIT_V_CPU) /* Unit families */ #define UNIT_FAMILY_MASK (UNIT_M_FAMILY << UNIT_V_CPU) #define UNIT_FAMILY_21XX (FAMILY_21XX << UNIT_V_CPU) #define UNIT_FAMILY_1000 (FAMILY_1000 << UNIT_V_CPU) /* Unit accessors */ #define UNIT_CPU_MODEL (cpu_unit.flags & UNIT_MODEL_MASK) #define UNIT_CPU_TYPE (cpu_unit.flags & UNIT_TYPE_MASK) #define UNIT_CPU_FAMILY (cpu_unit.flags & UNIT_FAMILY_MASK) #define CPU_MODEL_INDEX (UNIT_CPU_MODEL >> UNIT_V_CPU) /* Unit features */ #define UNIT_EAU (1 << UNIT_V_EAU) #define UNIT_FP (1 << UNIT_V_FP) #define UNIT_IOP (1 << UNIT_V_IOP) #define UNIT_DMS (1 << UNIT_V_DMS) #define UNIT_FFP (1 << UNIT_V_FFP) #define UNIT_DBI (1 << UNIT_V_DBI) #define UNIT_EMA (1 << UNIT_V_EMA) #define UNIT_VMAOS (1 << UNIT_V_VMAOS) #define UNIT_VIS (1 << UNIT_V_VIS) #define UNIT_DS (1 << UNIT_V_DS) #define UNIT_SIGNAL (1 << UNIT_V_SIGNAL) #define UNIT_EMA_VMA (UNIT_EMA | UNIT_VMAOS) #define UNIT_OPTS (UNIT_EAU | UNIT_FP | UNIT_IOP | \ UNIT_DMS | UNIT_FFP | UNIT_DBI | \ UNIT_EMA | UNIT_VMAOS | \ UNIT_VIS | UNIT_DS | UNIT_SIGNAL) /* "Pseudo-option" flags used only for option testing; never set into UNIT structure. */ #define UNIT_V_PFAIL (UNIT_V_UF - 1) /* Power fail installed */ #define UNIT_V_DMA (UNIT_V_UF - 2) /* DMA installed */ #define UNIT_V_MP (UNIT_V_UF - 3) /* Memory protect installed */ #define UNIT_PFAIL (1 << UNIT_V_PFAIL) #define UNIT_DMA (1 << UNIT_V_DMA) #define UNIT_MP (1 << UNIT_V_MP) #define UNIT_NONE 0 /* no options */ /* Debug flags */ #define DEB_OS (1 << 0) /* RTE-6/VM OS firmware non-TBG processing */ #define DEB_OSTBG (1 << 1) /* RTE-6/VM OS firmware TBG processing */ #define DEB_VMA (1 << 2) /* RTE-6/VM VMA firmware instructions */ #define DEB_EMA (1 << 3) /* RTE-6/VM EMA firmware instructions */ #define DEB_VIS (1 << 4) /* E/F-Series VIS firmware instructions */ #define DEB_SIG (1 << 5) /* F-Series SIGNAL/1000 firmware instructions */ /* PC queue. */ #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] = err_PC /* Memory reference instructions */ #define I_IA 0100000 /* indirect address */ #define I_AB 0004000 /* A/B select */ #define I_CP 0002000 /* current page */ #define I_DISP 0001777 /* page displacement */ #define I_PAGENO 0076000 /* page number */ /* Other instructions */ #define I_NMRMASK 0172000 /* non-mrf opcode */ #define I_ASKP 0002000 /* alter/skip */ #define I_IO 0102000 /* I/O */ #define I_CTL 0004000 /* CTL on/off */ #define I_HC 0001000 /* hold/clear */ #define I_DEVMASK 0000077 /* device select code mask */ #define I_GETIOOP(x) (((x) >> 6) & 07) /* I/O sub op */ /* Instruction masks */ #define I_MRG 0074000 /* MRG instructions */ #define I_MRG_I (I_MRG | I_IA) /* MRG indirect instruction group */ #define I_JSB 0014000 /* JSB instruction */ #define I_JSB_I (I_JSB | I_IA) /* JSB,I instruction */ #define I_JMP 0024000 /* JMP instruction */ #define I_ISZ 0034000 /* ISZ instruction */ #define I_IOG 0107700 /* I/O group instruction */ #define I_SFS 0102300 /* SFS instruction */ #define I_STF 0102100 /* STF instruction */ /* Memory management */ #define VA_N_OFF 10 /* offset width */ #define VA_M_OFF ((1 << VA_N_OFF) - 1) /* offset mask */ #define VA_GETOFF(x) ((x) & VA_M_OFF) #define VA_N_PAG (VA_N_SIZE - VA_N_OFF) /* page width */ #define VA_V_PAG (VA_N_OFF) /* page offset */ #define VA_M_PAG ((1 << VA_N_PAG) - 1) /* page mask */ #define VA_GETPAG(x) (((x) >> VA_V_PAG) & VA_M_PAG) /* Maps */ #define MAP_NUM 4 /* num maps */ #define MAP_LNT (1 << VA_N_PAG) /* map length */ #define MAP_MASK ((MAP_NUM * MAP_LNT) - 1) #define SMAP 0 /* system map */ #define UMAP (SMAP + MAP_LNT) /* user map */ #define PAMAP (UMAP + MAP_LNT) /* port A map */ #define PBMAP (PAMAP + MAP_LNT) /* port B map */ /* DMS map entries */ #define MAP_V_RPR 15 /* read prot */ #define MAP_V_WPR 14 /* write prot */ #define RDPROT (1 << MAP_V_RPR) /* read access check */ #define WRPROT (1 << MAP_V_WPR) /* write access check */ #define NOPROT 0 /* no access check */ #define MAP_RSVD 0036000 /* reserved bits */ #define MAP_N_PAG (PA_N_SIZE - VA_N_OFF) /* page width */ #define MAP_V_PAG (VA_N_OFF) #define MAP_M_PAG ((1 << MAP_N_PAG) - 1) #define MAP_GETPAG(x) (((x) & MAP_M_PAG) << MAP_V_PAG) /* MEM status register */ #define MST_ENBI 0100000 /* MEM enabled at interrupt */ #define MST_UMPI 0040000 /* User map selected at inerrupt */ #define MST_ENB 0020000 /* MEM enabled currently */ #define MST_UMP 0010000 /* User map selected currently */ #define MST_PRO 0004000 /* Protected mode enabled currently */ #define MST_FLT 0002000 /* Base page portion mapped */ #define MST_FENCE 0001777 /* Base page fence */ /* MEM violation register */ #define MVI_V_RPR 15 /* must be same as */ #define MVI_V_WPR 14 /* MAP_V_xPR */ #define MVI_RPR (1 << MVI_V_RPR) /* rd viol */ #define MVI_WPR (1 << MVI_V_WPR) /* wr viol */ #define MVI_BPG 0020000 /* base page viol */ #define MVI_PRV 0010000 /* priv viol */ #define MVI_MEB 0000200 /* me bus enb @ viol */ #define MVI_MEM 0000100 /* mem enb @ viol */ #define MVI_UMP 0000040 /* usr map @ viol */ #define MVI_PAG 0000037 /* pag sel */ /* CPU registers */ #define AR ABREG[0] /* A = reg 0 */ #define BR ABREG[1] /* B = reg 1 */ extern uint16 ABREG[2]; /* A/B regs (use AR/BR) */ extern uint32 PC; /* P register */ extern uint32 SR; /* S register */ extern uint32 MR; /* M register */ extern uint32 TR; /* T register */ extern uint32 XR; /* X register */ extern uint32 YR; /* Y register */ extern uint32 E; /* E register */ extern uint32 O; /* O register */ /* CPU state */ extern uint32 err_PC; extern uint32 dms_enb; extern uint32 dms_ump; extern uint32 dms_sr; extern uint32 dms_vr; extern uint32 mp_control; extern uint32 mp_fence; extern uint32 mp_viol; extern uint32 mp_mevff; extern uint32 iop_sp; extern t_bool ion_defer; extern uint32 intaddr; extern uint16 pcq [PCQ_SIZE]; extern uint32 pcq_p; extern uint32 stop_inst; extern UNIT cpu_unit; extern DEVICE cpu_dev; extern jmp_buf save_env; /* CPU functions */ #define MP_ABORT(va) longjmp (save_env, (va)) extern t_stat resolve (uint32 MA, uint32 *addr, uint32 irq); extern uint16 ReadPW (uint32 pa); extern uint8 ReadB (uint32 va); extern uint8 ReadBA (uint32 va); extern uint16 ReadW (uint32 va); extern uint16 ReadWA (uint32 va); extern uint16 ReadIO (uint32 va, uint32 map); extern void WritePW (uint32 pa, uint32 dat); extern void WriteB (uint32 va, uint32 dat); extern void WriteBA (uint32 va, uint32 dat); extern void WriteW (uint32 va, uint32 dat); extern void WriteWA (uint32 va, uint32 dat); extern void WriteIO (uint32 va, uint32 dat, uint32 map); extern t_stat iogrp (uint32 ir, uint32 iotrap); extern uint32 calc_int (void); extern t_bool calc_defer (void); extern void mp_dms_jmp (uint32 va, uint32 plb); extern uint16 dms_rmap (uint32 mapi); extern void dms_wmap (uint32 mapi, uint32 dat); extern void dms_viol (uint32 va, uint32 st); extern uint32 dms_upd_vr (uint32 va); extern uint32 dms_upd_sr (void); #endif simh-3.8.1/HP2100/hp2100_cpu1.h0000644000175000017500000003716011062237552013500 0ustar vlmvlm/* hp2100_cpu1.h: HP 2100/1000 firmware dispatcher definitions Copyright (c) 2006-2008, J. David Bryan 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 AUTHOR 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 author. 11-Sep-08 JDB Moved microcode function prototypes here 30-Apr-08 JDB Corrected OP_AFF to OP_AAFF for SIGNAL/1000 Removed unused operand patterns 23-Feb-08 HV Added more OP_* for SIGNAL/1000 and VIS 28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts 19-Oct-07 JDB Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC 16-Oct-06 JDB Generalized operands for F-Series FP types 26-Sep-06 JDB Split from hp2100_cpu1.c */ #ifndef _HP2100_CPU1_H_ #define _HP2100_CPU1_H_ /* Register print encoding */ #define REG_COUNT 9 /* count of print flags */ #define REG_CIR (1 << 0) /* print central interrupt register */ #define REG_A (1 << 1) /* print A register */ #define REG_B (1 << 2) /* print B register */ #define REG_E (1 << 3) /* print E register */ #define REG_X (1 << 4) /* print X register */ #define REG_Y (1 << 5) /* print Y register */ #define REG_O (1 << 6) /* print O register */ #define REG_P (1 << 7) /* print P register */ #define REG_P_REL (1 << 8) /* print P register as relative */ /* Operand processing encoding */ /* Base operand types. Note that all address encodings must be grouped together after OP_ADR. */ #define OP_NUL 0 /* no operand */ #define OP_IAR 1 /* 1-word int in A reg */ #define OP_JAB 2 /* 2-word int in A/B regs */ #define OP_FAB 3 /* 2-word FP const in A/B regs */ #define OP_CON 4 /* inline 1-word constant */ #define OP_VAR 5 /* inline 1-word variable */ #define OP_ADR 6 /* inline address */ #define OP_ADK 7 /* addr of 1-word int const */ #define OP_ADD 8 /* addr of 2-word int const */ #define OP_ADF 9 /* addr of 2-word FP const */ #define OP_ADX 10 /* addr of 3-word FP const */ #define OP_ADT 11 /* addr of 4-word FP const */ #define OP_ADE 12 /* addr of 5-word FP const */ #define OP_N_FLAGS 4 /* number of bits needed for flags */ #define OP_M_FLAGS ((1 << OP_N_FLAGS) - 1) /* mask for flag bits */ #define OP_N_F (8 * sizeof (uint32) / OP_N_FLAGS) /* max number of op fields */ #define OP_V_F1 (0 * OP_N_FLAGS) /* 1st operand field */ #define OP_V_F2 (1 * OP_N_FLAGS) /* 2nd operand field */ #define OP_V_F3 (2 * OP_N_FLAGS) /* 3rd operand field */ #define OP_V_F4 (3 * OP_N_FLAGS) /* 4th operand field */ #define OP_V_F5 (4 * OP_N_FLAGS) /* 5th operand field */ #define OP_V_F6 (5 * OP_N_FLAGS) /* 6th operand field */ #define OP_V_F7 (6 * OP_N_FLAGS) /* 7th operand field */ #define OP_V_F8 (7 * OP_N_FLAGS) /* 8th operand field */ /* Operand processing patterns */ #define OP_N (OP_NUL << OP_V_F1) #define OP_I (OP_IAR << OP_V_F1) #define OP_J (OP_JAB << OP_V_F1) #define OP_R (OP_FAB << OP_V_F1) #define OP_C (OP_CON << OP_V_F1) #define OP_V (OP_VAR << OP_V_F1) #define OP_A (OP_ADR << OP_V_F1) #define OP_K (OP_ADK << OP_V_F1) #define OP_D (OP_ADD << OP_V_F1) #define OP_X (OP_ADX << OP_V_F1) #define OP_T (OP_ADT << OP_V_F1) #define OP_E (OP_ADE << OP_V_F1) #define OP_IA ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2)) #define OP_JA ((OP_JAB << OP_V_F1) | (OP_ADR << OP_V_F2)) #define OP_JD ((OP_JAB << OP_V_F1) | (OP_ADD << OP_V_F2)) #define OP_RC ((OP_FAB << OP_V_F1) | (OP_CON << OP_V_F2)) #define OP_RK ((OP_FAB << OP_V_F1) | (OP_ADK << OP_V_F2)) #define OP_RF ((OP_FAB << OP_V_F1) | (OP_ADF << OP_V_F2)) #define OP_CV ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2)) #define OP_AC ((OP_ADR << OP_V_F1) | (OP_CON << OP_V_F2)) #define OP_AA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2)) #define OP_AK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2)) #define OP_AX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2)) #define OP_AT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2)) #define OP_KV ((OP_ADK << OP_V_F1) | (OP_VAR << OP_V_F2)) #define OP_KA ((OP_ADK << OP_V_F1) | (OP_ADR << OP_V_F2)) #define OP_KK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2)) #define OP_IIF ((OP_IAR << OP_V_F1) | (OP_IAR << OP_V_F2) | \ (OP_ADF << OP_V_F3)) #define OP_IAT ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADT << OP_V_F3)) #define OP_CVA ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2) | \ (OP_ADR << OP_V_F3)) #define OP_AAA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADR << OP_V_F3)) #define OP_AAF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADF << OP_V_F3)) #define OP_AAX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADX << OP_V_F3)) #define OP_AAT ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADT << OP_V_F3)) #define OP_AKA ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ (OP_ADR << OP_V_F3)) #define OP_AKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ (OP_ADK << OP_V_F3)) #define OP_AXX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2) | \ (OP_ADX << OP_V_F3)) #define OP_ATT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2) | \ (OP_ADT << OP_V_F3)) #define OP_AEE ((OP_ADR << OP_V_F1) | (OP_ADE << OP_V_F2) | \ (OP_ADE << OP_V_F3)) #define OP_AAXX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADX << OP_V_F3) | (OP_ADX << OP_V_F4)) #define OP_AAFF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4)) #define OP_AAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4)) #define OP_KKKK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2) | \ (OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4)) #define OP_AAAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ (OP_ADK << OP_V_F5)) #define OP_AKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ (OP_ADK << OP_V_F5)) #define OP_AAACCC ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \ (OP_CON << OP_V_F5) | (OP_CON << OP_V_F6)) #define OP_AAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4) | \ (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) #define OP_AAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \ (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) #define OP_CATAKK ((OP_CON << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADT << OP_V_F3) | (OP_ADR << OP_V_F4) | \ (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) #define OP_CCCACC ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \ (OP_CON << OP_V_F3) | (OP_ADR << OP_V_F4) | \ (OP_CON << OP_V_F5) | (OP_CON << OP_V_F6)) #define OP_AAAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_ADF << OP_V_F4) | \ (OP_ADF << OP_V_F5) | (OP_ADK << OP_V_F6) | \ (OP_ADK << OP_V_F7)) #define OP_AKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ (OP_ADR << OP_V_F5) | (OP_ADK << OP_V_F6) | \ (OP_ADK << OP_V_F7)) #define OP_AAKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ (OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \ (OP_ADK << OP_V_F5) | (OP_ADR << OP_V_F6) | \ (OP_ADK << OP_V_F7) | (OP_ADK << OP_V_F8)) #define OP_CCACACCA ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \ (OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \ (OP_ADR << OP_V_F5) | (OP_CON << OP_V_F6) | \ (OP_CON << OP_V_F7) | (OP_ADR << OP_V_F8)) /* Operand precisions (compatible with F-Series FPP): - S = 1-word integer - D = 2-word integer - F = 2-word single-precision floating-point - X = 3-word extended-precision floating-point - T = 4-word double-precision floating-point - E = 5-word expanded-exponent floating-point - A = null operand (operand is in FPP accumulator) 5-word floating-point numbers are supported by the F-Series Floating-Point Processor hardware, but the instruction codes are not documented. Note that ordering is important, as we depend on the "fp" type codes to reflect the number of words needed. */ typedef enum { in_s, in_d, fp_f, fp_x, fp_t, fp_e, fp_a } OPSIZE; /* Conversion from operand size to word count */ #define TO_COUNT(s) ((s == fp_a) ? 0 : (uint32) (s + (s < fp_f))) /* HP in-memory representation of a packed floating-point number. Actual value will use two, three, four, or five words, as needed. */ typedef uint16 FPK[5]; /* Operand processing types. NOTE: Microsoft VC++ 6.0 does not support the C99 standard, so we cannot initialize unions by arbitrary variant ("designated initializers"). Therefore, we follow the C90 form of initializing via the first named variant. The FPK variant must appear first in the OP structure, as we define a number of FPK constants in other modules. */ typedef union { /* general operand */ FPK fpk; /* floating-point value */ uint16 word; /* 16-bit integer */ uint32 dword; /* 32-bit integer */ } OP; typedef OP OPS[OP_N_F]; /* operand array */ typedef uint32 OP_PAT; /* operand pattern */ /* Microcode dispatcher functions (grouped by cpu module number) */ extern t_stat cpu_ds (uint32 IR, uint32 intrq); /* [0] Distributed System stub */ extern t_stat cpu_user (uint32 IR, uint32 intrq); /* [0] User firmware dispatcher */ extern t_stat cpu_user_20 (uint32 IR, uint32 intrq); /* [0] Module 20 user microprograms stub */ extern t_stat cpu_eau (uint32 IR, uint32 intrq); /* [1] EAU group simulator */ extern t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap); /* [1] UIG group 0 dispatcher */ extern t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap); /* [1] UIG group 1 dispatcher */ #if !defined (HAVE_INT64) /* int64 support unavailable */ extern t_stat cpu_fp (uint32 IR, uint32 intrq); /* [2] Firmware Floating Point */ #endif extern t_stat cpu_dms (uint32 IR, uint32 intrq); /* [2] Dynamic mapping system */ extern t_stat cpu_eig (uint32 IR, uint32 intrq); /* [2] Extended instruction group */ extern t_stat cpu_iop (uint32 IR, uint32 intrq); /* [2] 2000 I/O Processor */ extern t_stat cpu_ffp (uint32 IR, uint32 intrq); /* [3] Fast FORTRAN Processor */ extern t_stat cpu_dbi (uint32 IR, uint32 intrq); /* [3] Double-Integer instructions */ #if defined (HAVE_INT64) /* int64 support available */ extern t_stat cpu_fpp (uint32 IR, uint32 intrq); /* [4] Floating Point Processor */ extern t_stat cpu_sis (uint32 IR, uint32 intrq); /* [4] Scientific Instruction Set */ #endif extern t_stat cpu_rte_vma (uint32 IR, uint32 intrq); /* [5] RTE-6 VMA */ extern t_stat cpu_rte_ema (uint32 IR, uint32 intrq); /* [5] RTE-IV EMA */ extern t_stat cpu_rte_os (uint32 IR, uint32 intrq, uint32 iotrap); /* [6] RTE-6 OS */ #if defined (HAVE_INT64) /* int64 support available */ extern t_stat cpu_vis (uint32 IR, uint32 intrq); /* [7] Vector Instruction Set */ extern t_stat cpu_signal (uint32 IR, uint32 intrq); /* [7] SIGNAL/1000 Instructions */ #endif /* Microcode helper functions */ OP ReadOp (uint32 va, OPSIZE precision); /* generalized operand read */ void WriteOp (uint32 va, OP operand, OPSIZE precision); /* generalized operand write */ t_stat cpu_ops (OP_PAT pattern, OPS op, uint32 irq); /* operand processor */ void fprint_ops (OP_PAT pattern, OPS op); /* debug print operands */ void fprint_regs (char *caption, uint32 regs, uint32 base); /* debug print CPU registers */ #endif simh-3.8.1/HP2100/hp2100_stddev.c0000644000175000017500000013172011107411526014104 0ustar vlmvlm/* hp2100_stddev.c: HP2100 standard devices simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. PTR 12597A-002 paper tape reader interface PTP 12597A-005 paper tape punch interface TTY 12531C buffered teleprinter interface CLK 12539C time base generator 26-Jun-08 JDB Rewrote device I/O to model backplane signals 25-Apr-08 JDB Changed TTY output wait from 100 to 200 for MSU BASIC 18-Apr-08 JDB Removed redundant control char handling definitions 14-Apr-08 JDB Changed TTY console poll to 10 msec. real time Synchronized CLK with TTY if set for 10 msec. Added UNIT_IDLE to TTY and CLK 09-Jan-08 JDB Fixed PTR trailing null counter for tape re-read 31-Dec-07 JDB Added IPTICK register to CLK to display CPU instr/tick Corrected and verified ioCRS actions 28-Dec-06 JDB Added ioCRS state to I/O decoders 22-Nov-05 RMS Revised for new terminal processing routines 13-Sep-04 JDB Added paper tape loop mode, DIAG/READER modifiers to PTR Added PV_LEFT to PTR TRLLIM register Modified CLK to permit disable 15-Aug-04 RMS Added tab to control char set (from Dave Bryan) 14-Jul-04 RMS Generalized handling of control char echoing (from Dave Bryan) 26-Apr-04 RMS Fixed SFS x,C and SFC x,C Fixed SR setting in IBL Fixed input behavior during typeout for RTE-IV Suppressed nulls on TTY output for RTE-IV Implemented DMA SRQ (follows FLG) 29-Mar-03 RMS Added support for console backpressure 25-Apr-03 RMS Added extended file support 22-Dec-02 RMS Added break support 01-Nov-02 RMS Revised BOOT command for IBL ROMs Fixed bug in TTY reset, TTY starts in input mode Fixed bug in TTY mode OTA, stores data as well Fixed clock to add calibration, proper start/stop Added UC option to TTY output 30-May-02 RMS Widened POS to 32b 22-Mar-02 RMS Revised for dynamically allocated memory 03-Nov-01 RMS Changed DEVNO to use extended SET/SHOW 29-Nov-01 RMS Added read only unit support 24-Nov-01 RMS Changed TIME to an array 07-Sep-01 RMS Moved function prototypes 21-Nov-00 RMS Fixed flag, buffer power up state Added status input for ptp, tty 15-Oct-00 RMS Added dynamic device number support References: - 2748B Tape Reader Operating and Service Manual (02748-90041, Oct-1977) - 12597A 8-Bit Duplex Register Interface Kit Operating and Service Manual (12597-9002, Sep-1974) - 12531C Buffered Teleprinter Interface Kit Operating and Service Manual (12531-90033, Nov-1972) - 12539C Time Base Generator Interface Kit Operating and Service Manual (12539-90008, Jan-1975) The reader and punch, like most HP devices, have a command flop. The teleprinter and clock do not. Reader diagnostic mode simulates a tape loop by rewinding the tape image file upon EOF. Normal mode EOF action is to supply TRLLIM nulls and then either return SCPE_IOERR or SCPE_OK without setting the device flag. To support CPU idling, the teleprinter interface (which doubles as the simulator console) polls for input using a calibrated timer with a ten millisecond period. Other polled-keyboard input devices (multiplexers and the BACI card) synchronize with the console poll to ensure maximum available idle time. The console poll is guaranteed to run, as the TTY device cannot be disabled. The clock (time base generator) autocalibrates. If the CLK is set to a ten millisecond period (e.g., as under RTE), it is synchronized to the console poll. Otherwise (e.g., as under DOS or TSB, which use 100 millisecond periods), it runs asynchronously. If the specified clock frequency is below 10Hz, the clock service routine runs at 10Hz and counts down a repeat counter before generating an interrupt. Autocalibration will not work if the clock is running at 1Hz or less. Clock diagnostic mode corresponds to inserting jumper W2 on the 12539C. This turns off autocalibration and divides the longest time intervals down by 10**3. The clk_time values were chosen to allow the diagnostic to pass its clock calibration test. */ #include "hp2100_defs.h" #define TTY_OUT_WAIT 200 /* TTY output wait */ #define UNIT_V_DIAG (TTUF_V_UF + 0) /* diag mode */ #define UNIT_V_AUTOLF (TTUF_V_UF + 1) /* auto linefeed */ #define UNIT_DIAG (1 << UNIT_V_DIAG) #define UNIT_AUTOLF (1 << UNIT_V_AUTOLF) #define PTP_LOW 0000040 /* low tape */ #define TM_MODE 0100000 /* mode change */ #define TM_KBD 0040000 /* enable keyboard */ #define TM_PRI 0020000 /* enable printer */ #define TM_PUN 0010000 /* enable punch */ #define TP_BUSY 0100000 /* busy */ #define CLK_V_ERROR 4 /* clock overrun */ #define CLK_ERROR (1 << CLK_V_ERROR) FLIP_FLOP ptr_control = CLEAR; FLIP_FLOP ptr_flag = CLEAR; FLIP_FLOP ptr_flagbuf = CLEAR; int32 ptr_stopioe = 0; /* stop on error */ int32 ptr_trlcnt = 0; /* trailer counter */ int32 ptr_trllim = 40; /* trailer to add */ FLIP_FLOP ptp_control = CLEAR; FLIP_FLOP ptp_flag = CLEAR; FLIP_FLOP ptp_flagbuf = CLEAR; int32 ptp_stopioe = 0; FLIP_FLOP tty_control = CLEAR; FLIP_FLOP tty_flag = CLEAR; FLIP_FLOP tty_flagbuf = CLEAR; int32 ttp_stopioe = 0; int32 tty_buf = 0; /* tty buffer */ int32 tty_mode = 0; /* tty mode */ int32 tty_shin = 0377; /* tty shift in */ int32 tty_lf = 0; /* lf flag */ FLIP_FLOP clk_control = CLEAR; FLIP_FLOP clk_flag = CLEAR; FLIP_FLOP clk_flagbuf = CLEAR; int32 clk_select = 0; /* clock time select */ int32 clk_error = 0; /* clock error */ int32 clk_ctr = 0; /* clock counter */ int32 clk_time[8] = { /* clock intervals */ 155, 1550, 15500, 155000, 155000, 155000, 155000, 155000 }; int32 clk_tps[8] = { /* clock tps */ 10000, 1000, 100, 10, 10, 10, 10, 10 }; int32 clk_rpt[8] = { /* number of repeats */ 1, 1, 1, 1, 10, 100, 1000, 10000 }; uint32 clk_tick = 0; /* instructions per tick */ DEVICE ptr_dev, ptp_dev, tty_dev, clk_dev; uint32 ptrio (uint32 select_code, IOSIG signal, uint32 data); t_stat ptr_svc (UNIT *uptr); t_stat ptr_attach (UNIT *uptr, char *cptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); uint32 ptpio (uint32 select_code, IOSIG signal, uint32 data); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); uint32 ttyio (uint32 select_code, IOSIG signal, uint32 data); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tty_reset (DEVICE *dptr); t_stat tty_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tty_set_alf (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tto_out (int32 c); t_stat ttp_out (int32 c); uint32 clkio (uint32 select_code, IOSIG signal, uint32 data); t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); int32 clk_delay (int32 flg); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_mod PTR modifiers ptr_reg PTR register list */ DIB ptr_dib = { PTR, &ptrio }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 8) }, { FLDATA (CTL, ptr_control, 0) }, { FLDATA (FLG, ptr_flag, 0) }, { FLDATA (FBF, ptr_flagbuf, 0) }, { DRDATA (TRLCTR, ptr_trlcnt, 8), REG_HRO }, { DRDATA (TRLLIM, ptr_trllim, 8), PV_LEFT }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { ORDATA (DEVNO, ptr_dib.devno, 6), REG_HRO }, { NULL } }; MTAB ptr_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, "reader mode", "READER", NULL }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ptr_dev }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, &ptr_attach, NULL, &ptr_dib, DEV_DISABLE }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_mod PTP modifiers ptp_reg PTP register list */ DIB ptp_dib = { PTP, &ptpio }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (CTL, ptp_control, 0) }, { FLDATA (FLG, ptp_flag, 0) }, { FLDATA (FBF, ptp_flagbuf, 0) }, { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptp_stopioe, 0) }, { ORDATA (DEVNO, ptp_dib.devno, 6), REG_HRO }, { NULL } }; MTAB ptp_mod[] = { { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ptp_dev }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, &ptp_dib, DEV_DISABLE }; /* TTY data structures tty_dev TTY device descriptor tty_unit TTY unit descriptor tty_reg TTY register list tty_mod TTy modifiers list */ #define TTI 0 #define TTO 1 #define TTP 2 DIB tty_dib = { TTY, &ttyio }; UNIT tty_unit[] = { { UDATA (&tti_svc, UNIT_IDLE | TT_MODE_UC, 0), POLL_WAIT }, { UDATA (&tto_svc, TT_MODE_UC, 0), TTY_OUT_WAIT }, { UDATA (&tto_svc, UNIT_SEQ | UNIT_ATTABLE | TT_MODE_8B, 0), SERIAL_OUT_WAIT } }; REG tty_reg[] = { { ORDATA (BUF, tty_buf, 8) }, { ORDATA (MODE, tty_mode, 16) }, { ORDATA (SHIN, tty_shin, 8), REG_HRO }, { FLDATA (CTL, tty_control, 0) }, { FLDATA (FLG, tty_flag, 0) }, { FLDATA (FBF, tty_flagbuf, 0) }, { FLDATA (KLFP, tty_lf, 0), REG_HRO }, { DRDATA (KPOS, tty_unit[TTI].pos, T_ADDR_W), PV_LEFT }, { DRDATA (KTIME, tty_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPOS, tty_unit[TTO].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (PPOS, tty_unit[TTP].pos, T_ADDR_W), PV_LEFT }, { FLDATA (STOP_IOE, ttp_stopioe, 0) }, { ORDATA (DEVNO, tty_dib.devno, 6), REG_HRO }, { NULL } }; MTAB tty_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_opt }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_opt }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_opt }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_opt }, { UNIT_AUTOLF, UNIT_AUTOLF, "autolf", "AUTOLF", &tty_set_alf }, { UNIT_AUTOLF, 0 , NULL, "NOAUTOLF", &tty_set_alf }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &tty_dev }, { 0 } }; DEVICE tty_dev = { "TTY", tty_unit, tty_reg, tty_mod, 3, 10, 31, 1, 8, 8, NULL, NULL, &tty_reset, NULL, NULL, NULL, &tty_dib, 0 }; /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_mod CLK modifiers clk_reg CLK register list */ DIB clk_dib = { CLK, &clkio }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0) }; REG clk_reg[] = { { ORDATA (SEL, clk_select, 3) }, { DRDATA (CTR, clk_ctr, 14) }, { FLDATA (CTL, clk_control, 0) }, { FLDATA (FLG, clk_flag, 0) }, { FLDATA (FBF, clk_flagbuf, 0) }, { FLDATA (ERR, clk_error, CLK_V_ERROR) }, { BRDATA (TIME, clk_time, 10, 24, 8) }, { DRDATA (IPTICK, clk_tick, 24), PV_RSPC | REG_RO }, { ORDATA (DEVNO, clk_dib.devno, 6), REG_HRO }, { NULL } }; MTAB clk_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, "calibrated", "CALIBRATED", NULL }, { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &clk_dev }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, DEV_DISABLE }; /* Paper tape reader I/O signal handler. Implementation notes: 1. The 12597A duplex register card is used to interface the paper tape reader to the computer. This card has a device command flip-flop, which supplies the READ signal to the tape reader. Under simulation, this state is implied by the activation of the PTR unit. 2. The POPIO signal clears the output buffer of the duplex card. However, the buffer outputs are not used by the paper tape reader. Under simulation, we omit the buffer clear. */ uint32 ptrio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ ptr_flag = ptr_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ ptr_flag = ptr_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (ptr); break; case ioSFS: /* skip if flag is set */ setstdSKF (ptr); break; case ioIOI: /* I/O data input */ data = ptr_unit.buf; break; case ioPOPIO: /* power-on preset to I/O */ ptr_flag = ptr_flagbuf = SET; /* set flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ ptr_control = CLEAR; break; case ioSTC: /* set control flip-flop */ ptr_control = SET; sim_activate (&ptr_unit, ptr_unit.wait); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, ptr); /* set standard PRL signal */ setstdIRQ (select_code, ptr); /* set standard IRQ signal */ setstdSRQ (select_code, ptr); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ ptr_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ ptrio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ ptrio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); while ((temp = getc (ptr_unit.fileref)) == EOF) { /* read byte, error? */ if (feof (ptr_unit.fileref)) { /* end of file? */ if ((ptr_unit.flags & UNIT_DIAG) && (ptr_unit.pos > 0)) { rewind (ptr_unit.fileref); /* rewind if loop mode */ ptr_unit.pos = 0; } else { if (ptr_trlcnt >= ptr_trllim) { /* added all trailer? */ if (ptr_stopioe) { /* stop on error? */ printf ("PTR end of file\n"); return SCPE_IOERR; } else return SCPE_OK; /* no, just hang */ } ptr_trlcnt++; /* count trailer */ temp = 0; /* read a zero */ break; } } else { /* no, real error */ perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } } ptrio (ptr_dib.devno, ioENF, 0); /* set flag */ ptr_unit.buf = temp & 0377; /* put byte in buf */ ptr_unit.pos = ftell (ptr_unit.fileref); if (temp) /* character non-null? */ ptr_trlcnt = 0; /* clear trailing null counter */ return SCPE_OK; } /* Attach routine - clear the trailer counter */ t_stat ptr_attach (UNIT *uptr, char *cptr) { ptr_trlcnt = 0; return attach_unit (uptr, cptr); } /* Reset routine - called from SCP */ t_stat ptr_reset (DEVICE *dptr) { ptrio (ptr_dib.devno, ioPOPIO, 0); /* send POPIO signal */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Paper tape reader bootstrap routine (HP 12992K ROM) */ const BOOT_ROM ptr_rom = { 0107700, /*ST CLC 0,C ; intr off */ 0002401, /* CLA,RSS ; skip in */ 0063756, /*CN LDA M11 ; feed frame */ 0006700, /* CLB,CCE ; set E to rd byte */ 0017742, /* JSB READ ; get #char */ 0007306, /* CMB,CCE,INB,SZB ; 2's comp */ 0027713, /* JMP *+5 ; non-zero byte */ 0002006, /* INA,SZA ; feed frame ctr */ 0027703, /* JMP *-3 */ 0102077, /* HLT 77B ; stop */ 0027700, /* JMP ST ; next */ 0077754, /* STA WC ; word in rec */ 0017742, /* JSB READ ; get feed frame */ 0017742, /* JSB READ ; get address */ 0074000, /* STB 0 ; init csum */ 0077755, /* STB AD ; save addr */ 0067755, /*CK LDB AD ; check addr */ 0047777, /* ADB MAXAD ; below loader */ 0002040, /* SEZ ; E =0 => OK */ 0027740, /* JMP H55 */ 0017742, /* JSB READ ; get word */ 0040001, /* ADA 1 ; cont checksum */ 0177755, /* STA AD,I ; store word */ 0037755, /* ISZ AD */ 0000040, /* CLE ; force wd read */ 0037754, /* ISZ WC ; block done? */ 0027720, /* JMP CK ; no */ 0017742, /* JSB READ ; get checksum */ 0054000, /* CPB 0 ; ok? */ 0027702, /* JMP CN ; next block */ 0102011, /* HLT 11 ; bad csum */ 0027700, /* JMP ST ; next */ 0102055, /*H55 HALT 55 ; bad address */ 0027700, /* JMP ST ; next */ 0000000, /*RD 0 */ 0006600, /* CLB,CME ; E reg byte ptr */ 0103710, /* STC RDR,C ; start reader */ 0102310, /* SFS RDR ; wait */ 0027745, /* JMP *-1 */ 0106410, /* MIB RDR ; get byte */ 0002041, /* SEZ,RSS ; E set? */ 0127742, /* JMP RD,I ; no, done */ 0005767, /* BLF,CLE,BLF ; shift byte */ 0027744, /* JMP RD+2 ; again */ 0000000, /*WC 000000 ; word count */ 0000000, /*AD 000000 ; address */ 0177765, /*M11 -11 ; feed count */ 0, 0, 0, 0, 0, 0, 0, 0, /* unused */ 0, 0, 0, 0, 0, 0, 0, /* unused */ 0000000 /*MAXAD -ST ; max addr */ }; t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 dev; dev = ptr_dib.devno; /* get device no */ if (ibl_copy (ptr_rom, dev)) return SCPE_IERR; /* copy boot to memory */ SR = (SR & IBL_OPT) | IBL_PTR | (dev << IBL_V_DEV); /* set SR */ return SCPE_OK; } /* Paper tape punch I/O signal handler. Implementation notes: 1. The 12597A duplex register card is used to interface the paper tape punch to the computer. This card has a device command flip-flop, which supplies the PUNCH signal to the tape reader. Under simulation, this state is implied by the activation of the PTP unit. */ uint32 ptpio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ ptp_flag = ptp_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ ptp_flag = ptp_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (ptp); break; case ioSFS: /* skip if flag is set */ setstdSKF (ptp); break; case ioIOI: /* I/O data input */ if ((ptp_unit.flags & UNIT_ATT) == 0) /* not attached? */ data = PTP_LOW; /* report as out of tape */ else data = 0; break; case ioIOO: /* I/O data output */ ptp_unit.buf = data; break; case ioPOPIO: /* power-on preset to I/O */ ptp_flag = ptp_flagbuf = SET; /* set flag and flag buffer */ ptp_unit.buf = 0; /* clear output buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ ptp_control = CLEAR; break; case ioSTC: /* set control flip-flop */ ptp_control = SET; sim_activate (&ptp_unit, ptp_unit.wait); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, ptp); /* set standard PRL signal */ setstdIRQ (select_code, ptp); /* set standard IRQ signal */ setstdSRQ (select_code, ptp); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ ptp_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ ptpio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ ptpio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { ptpio (ptp_dib.devno, ioENF, 0); /* set flag */ if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* output byte */ perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ftell (ptp_unit.fileref); /* update position */ return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptpio (ptp_dib.devno, ioPOPIO, 0); /* send POPIO signal */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Terminal I/O signal handler */ uint32 ttyio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ tty_flag = tty_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ tty_flag = tty_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (tty); break; case ioSFS: /* skip if flag is set */ setstdSKF (tty); break; case ioIOI: /* I/O data input */ data = tty_buf; if (!(tty_mode & TM_KBD) && sim_is_active (&tty_unit[TTO])) data = data | TP_BUSY; break; case ioIOO: /* I/O data output */ if (data & TM_MODE) tty_mode = data & (TM_KBD|TM_PRI|TM_PUN); tty_buf = data & 0377; break; case ioPOPIO: /* power-on preset to I/O */ /* fall into CRS handler */ case ioCRS: /* control reset */ tty_control = CLEAR; /* clear control */ tty_flag = tty_flagbuf = SET; /* set flag and flag buffer */ tty_mode = TM_KBD; /* set tty, clear print/punch */ tty_shin = 0377; /* input inactive */ tty_lf = 0; /* no lf pending */ break; case ioCLC: /* clear control flip-flop */ tty_control = CLEAR; break; case ioSTC: /* set control flip-flop */ tty_control = SET; if (!(tty_mode & TM_KBD)) /* output? */ sim_activate (&tty_unit[TTO], tty_unit[TTO].wait); break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, tty); /* set standard PRL signal */ setstdIRQ (select_code, tty); /* set standard IRQ signal */ setstdSRQ (select_code, tty); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ tty_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ ttyio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ ttyio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* TTY input service routine. The console input poll routine is scheduled with a ten millisecond period using a calibrated timer, which is the source of event timing for all of the keyboard polling routines. Synchronizing other keyboard polls with the console poll ensures maximum idle time. Several HP operating systems require a CR and LF sequence for line termination. This is awkward on a PC, as there is no LF key (CTRL+J is needed instead). We provide an AUTOLF mode to add a LF automatically to each CR input. When this mode is set, entering CR will set a flag, which will cause a LF to be supplied automatically at the next input poll. The 12531C teleprinter interface and the later 12880A CRT interface provide a clever mechanism to detect a keypress during output. This is used by DOS and RTE to allow the user to interrupt lengthy output operations to enter system commands. Referring to the 12531C schematic, the terminal input enters on pin X ("DATA FROM EIA COMPATIBLE DEVICE"). The signal passes through four transistor inversions (Q8, Q1, Q2, and Q3) to appear on pin 12 of NAND gate U104C. If the flag flip-flop is not set, the terminal input passes to the (inverted) output of U104C and thence to the D input of the first of the flip-flops forming the data register. In the idle condition (no key pressed), the terminal input line is marking (voltage negative), so in passing through a total of five inversions, a logic one is presented at the serial input of the data register. During an output operation, the register is parallel loaded and serially shifted, sending the output data through the register to the device and -- this is the crux -- filling the register with logic ones from U104C. At the end of the output operation, the card flag is set, an interrupt occurs, and the RTE driver is entered. The driver then does an LIA SC to read the contents of the data register. If no key has been pressed during the output operation, the register will read as all ones (octal 377). If, however, any key was struck, at least one zero bit will be present. If the register value doesn't equal 377, the driver sets the system "operator attention" flag, which will cause DOS or RTE to output an asterisk prompt and initiate a terminal read when the current output line is completed. */ t_stat tti_svc (UNIT *uptr) { int32 c; uptr->wait = sim_rtcn_calb (POLL_RATE, TMR_POLL); /* calibrate poll timer */ sim_activate (uptr, uptr->wait); /* continue poll */ tty_shin = 0377; /* assume inactive */ if (tty_lf) { /* auto lf pending? */ c = 012; /* force lf */ tty_lf = 0; } else { if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ if (c & SCPE_BREAK) c = 0; /* break? */ else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); tty_lf = ((c & 0177) == 015) && (uptr->flags & UNIT_AUTOLF); } if (tty_mode & TM_KBD) { /* keyboard enabled? */ tty_buf = c; /* put char in buf */ uptr->pos = uptr->pos + 1; ttyio (tty_dib.devno, ioENF, 0); /* set flag */ if (c) { tto_out (c); /* echo? */ return ttp_out (c); /* punch? */ } } else tty_shin = c; /* no, char shifts in */ return SCPE_OK; } /* TTY output service routine */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = tty_buf; /* get char */ tty_buf = tty_shin; /* shift in */ tty_shin = 0377; /* line inactive */ if ((r = tto_out (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } ttyio (tty_dib.devno, ioENF, 0); /* set flag */ return ttp_out (c); /* punch if enabled */ } t_stat tto_out (int32 c) { t_stat r; if (tty_mode & TM_PRI) { /* printing? */ c = sim_tt_outcvt (c, TT_GET_MODE (tty_unit[TTO].flags)); if (c >= 0) { /* valid? */ if (r = sim_putchar_s (c)) return r; /* output char */ tty_unit[TTO].pos = tty_unit[TTO].pos + 1; } } return SCPE_OK; } t_stat ttp_out (int32 c) { if (tty_mode & TM_PUN) { /* punching? */ if ((tty_unit[TTP].flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ttp_stopioe, SCPE_UNATT); if (putc (c, tty_unit[TTP].fileref) == EOF) { /* output char */ perror ("TTP I/O error"); clearerr (tty_unit[TTP].fileref); return SCPE_IOERR; } tty_unit[TTP].pos = ftell (tty_unit[TTP].fileref); } return SCPE_OK; } /* TTY reset routine */ t_stat tty_reset (DEVICE *dptr) { if (sim_switches & SWMASK ('P')) /* PON reset? */ tty_buf = 0; /* clear buffer */ ttyio (tty_dib.devno, ioPOPIO, 0); /* send POPIO signal */ tty_unit[TTI].wait = POLL_WAIT; /* reset initial poll */ sim_rtcn_init (tty_unit[TTI].wait, TMR_POLL); /* init poll timer */ sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* activate poll */ sim_cancel (&tty_unit[TTO]); /* cancel output */ return SCPE_OK; } t_stat tty_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 u = uptr - tty_dev.units; if (u > TTO) return SCPE_NOFNC; if ((u == TTI) && (val == TT_MODE_7P)) val = TT_MODE_7B; tty_unit[u].flags = (tty_unit[u].flags & ~TT_MODE) | val; return SCPE_OK; } t_stat tty_set_alf (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 u = uptr - tty_dev.units; if (u != TTI) return SCPE_NOFNC; return SCPE_OK; } /* Synchronize polling. Return an event time corresponding either with the amount of time remaining in the current poll (mode = INITIAL) or the amount of time in a full poll period (mode = SERVICE). If the former call is made when the device service routine is started, then making the latter call during unit service will ensure that the polls remain synchronized. */ int32 sync_poll (POLLMODE poll_mode) { int32 poll_time; if (poll_mode == INITIAL) { poll_time = sim_is_active (&tty_unit[TTI]); if (poll_time) return poll_time; else return POLL_WAIT; } else return tty_unit[TTI].wait; } /* Clock I/O signal handler. The time base generator (CLK) provides periodic interrupts from 100 microseconds to 1000 seconds. The CLK uses a calibrated timer to provide the time base. For periods ranging from 1 to 1000 seconds, a 100 millisecond timer is used, and 10 to 10000 ticks are counted before setting the device flag to indicate that the period has expired. If the period is set to ten milliseconds, the console poll timer is used instead of an independent timer. This is to maximize the idle period. In diagnostic mode, the clock period is set to the expected number of CPU instructions, rather than wall-clock time, so that the diagnostic executes as expected. */ uint32 clkio (uint32 select_code, IOSIG signal, uint32 data) { const IOSIG base_signal = IOBASE (signal); /* derive base signal */ switch (base_signal) { /* dispatch base I/O signal */ case ioCLF: /* clear flag flip-flop */ clk_flag = clk_flagbuf = CLEAR; break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ clk_flag = clk_flagbuf = SET; break; case ioSFC: /* skip if flag is clear */ setstdSKF (clk); break; case ioSFS: /* skip if flag is set */ setstdSKF (clk); break; case ioIOI: /* I/O data input */ data = clk_error; break; case ioIOO: /* I/O data output */ clk_select = data & 07; /* save select */ sim_cancel (&clk_unit); /* stop the clock */ clk_control = CLEAR; /* clear control */ clkio (select_code, ioSIR, 0); /* set interrupt request (IOO normally doesn't) */ break; case ioPOPIO: /* power-on preset to I/O */ clk_flag = clk_flagbuf = SET; /* set flag and flag buffer */ /* fall into CRS handler */ case ioCRS: /* control reset */ /* fall into CLC handler */ case ioCLC: /* clear control flip-flop */ clk_control = CLEAR; sim_cancel (&clk_unit); /* deactivate unit */ break; case ioSTC: /* set control flip-flop */ clk_control = SET; if (clk_unit.flags & UNIT_DIAG) /* diag mode? */ clk_unit.flags = clk_unit.flags & ~UNIT_IDLE; /* not calibrated */ else clk_unit.flags = clk_unit.flags | UNIT_IDLE; /* is calibrated */ if (!sim_is_active (&clk_unit)) { /* clock running? */ clk_tick = clk_delay (0); /* get tick count */ if ((clk_unit.flags & UNIT_DIAG) == 0) /* calibrated? */ if (clk_select == 2) /* 10 msec. interval? */ clk_tick = sync_poll (INITIAL); /* sync poll */ else sim_rtcn_init (clk_tick, TMR_CLK); /* initialize timer */ sim_activate (&clk_unit, clk_tick); /* start clock */ clk_ctr = clk_delay (1); /* set repeat ctr */ } clk_error = 0; /* clear error */ break; case ioSIR: /* set interrupt request */ setstdPRL (select_code, clk); /* set standard PRL signal */ setstdIRQ (select_code, clk); /* set standard IRQ signal */ setstdSRQ (select_code, clk); /* set standard SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ clk_flagbuf = CLEAR; break; default: /* all other signals */ break; /* are ignored */ } if (signal > ioCLF) /* multiple signals? */ clkio (select_code, ioCLF, 0); /* issue CLF */ else if (signal > ioSIR) /* signal affected interrupt status? */ clkio (select_code, ioSIR, 0); /* set interrupt request */ return data; } /* CLK unit service. As with the I/O handler, if the time base period is set to ten milliseconds, the console poll timer is used instead of an independent timer. */ t_stat clk_svc (UNIT *uptr) { if (!clk_control) /* control clear? */ return SCPE_OK; /* done */ if (clk_unit.flags & UNIT_DIAG) /* diag mode? */ clk_tick = clk_delay (0); /* get fixed delay */ else if (clk_select == 2) /* 10 msec period? */ clk_tick = sync_poll (SERVICE); /* sync poll */ else clk_tick = sim_rtcn_calb (clk_tps[clk_select], TMR_CLK); /* calibrate delay */ sim_activate (uptr, clk_tick); /* reactivate */ clk_ctr = clk_ctr - 1; /* decrement counter */ if (clk_ctr <= 0) { /* end of interval? */ if (clk_flag) clk_error = CLK_ERROR; /* overrun? error */ else clkio (clk_dib.devno, ioENF, 0); /* set flag */ clk_ctr = clk_delay (1); /* reset counter */ } return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { if (sim_switches & SWMASK ('P')) { /* PON reset? */ clk_error = 0; /* clear error */ clk_select = 0; /* clear select */ clk_ctr = 0; /* clear counter */ } clkio (clk_dib.devno, ioPOPIO, 0); /* send POPIO signal */ return SCPE_OK; } /* Clock delay routine */ int32 clk_delay (int32 flg) { int32 sel = clk_select; if ((clk_unit.flags & UNIT_DIAG) && (sel >= 4)) sel = sel - 3; if (flg) return clk_rpt[sel]; else return clk_time[sel]; } simh-3.8.1/sim_ether.c0000644000175000017500000015443011004202636012765 0ustar vlmvlm/* sim_ether.c: OS-dependent network routines ------------------------------------------------------------------------------ Copyright (c) 2002-2007, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ This ethernet simulation is based on the PCAP and WinPcap packages. PCAP/WinPcap was chosen as the basis for network code since it is the most "universal" of the various network packages available. Using this style has allowed rapid network development for the major SIMH platforms. Developing a network package specifically for SIMH was rejected due to the time required; the advantage would be a more easily compiled and integrated code set. There are various problems associated with use of ethernet networking, which would be true regardless of the network package used, since there are no universally accepted networking methods. The most serious of these is getting the proper networking package loaded onto the system, since most environments do not come with the network interface packages loaded. The second most serious network issue relates to security. The network simulation needs to simulate operating system level functionality (packet driving). However, the host network programming interfaces tend to operate at the user level of functionality, so getting to the full functionality of the network interface usually requires that the person executing the network code be a privileged user of the host system. See the PCAP/WinPcap documentation for the appropriate host platform if unprivileged use of networking is needed - there may be known workarounds. Define one of the two macros below to enable networking: USE_NETWORK - Create statically linked network code USE_SHARED - Create dynamically linked network code (_WIN32 only) ------------------------------------------------------------------------------ Supported/Tested Platforms: Windows(NT,2K,XP,2K3) WinPcap V3.0+ Linux libpcap at least 0.9 OpenBSD,FreeBSD,NetBSD libpcap at least 0.9 MAC OS/X libpcap at least 0.9 Solaris Sparc libpcap at least 0.9 Solaris Intel libpcap at least 0.9 AIX ?? HP/UX ?? Compaq Tru64 Unix ?? VMS Alpha/Itanium VMS only, needs VMS libpcap WinPcap is available from: http://winpcap.polito.it/ libpcap for VMS is available from: http://simh.trailing-edge.com/sources/vms-pcap.zip libpcap for other Unix platforms is available at: Current Version: http://www.tcpdump.org/daily/libpcap-current.tar.gz Released Version: http://www.tcpdump.org/release/ Note: You can only use the released version if it is at least version 0.9 We've gotten the tarball, unpacked, built and installed it with: gzip -dc libpcap-current.tar.gz | tar xvf - cd libpcap-directory-name ./configure make make install Note: The "make install" step generally will have to be done as root. This will install libpcap in /usr/local/lib and /usr/local/include It is then important to make sure that you get the just installed libpcap components referenced during your build. This is generally achieved by invoking gcc with: -isystem /usr/local/include -L /usr/local/lib Note: Building for the platforms indicated above, with the indicated libpcap, should automatically leverage the appropriate mechanisms contained here. Things are structured so that it is likely to work for any other as yet untested platform. If it works for you, please let the author know so we can update the table above. If it doesn't work, then the following #define variables can influence the operation on an untested platform. USE_BPF - Determines if this code leverages a libpcap/WinPcap provided bpf packet filtering facility. All tested environments have bpf facilities that work the way we need them to. However a new one might not. undefine this variable to let this code do its own filtering. USE_SETNONBLOCK - Specifies whether the libpcap environment's non-blocking semantics are to be leveraged. This helps to manage the varying behaviours of the kernel packet facilities leveraged by libpcap. USE_READER_THREAD - Specifies that packet reading should be done in the context of a separate thread. The Posix threading APIs are used. This option is less efficient than the default non-threaded approach, but it exists since some platforms don't want to work with nonblocking libpcap semantics. OpenBSD and NetBSD either don't have pthread APIs available, or they are too buggy to be useful. Using the threaded approach may require special compile and/or link time switches (i.e. -lpthread or -pthread, etc.) Consult the documentation for your platform as needed. MUST_DO_SELECT - Specifies that when USE_READER_THREAD is active, that select() should be used to determin when available packets are ready for reading. Otherwise, we depend on the libpcap/kernel packet timeout specified on pcap_open_live. If USE_READER_THREAD is not set, then MUST_DO_SELECT is irrelevant NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap which doesn't provide a pcap_sendpacket API. NOTE: Changing these defines is done in either sim_ether.h OR on the global compiler command line which builds all of the modules included in a simulator. ------------------------------------------------------------------------------ Modification history: 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada) 15-May-07 DTH Added dynamic loading of wpcap.dll; Corrected exceed max index bug in ethX lookup 04-May-07 DTH Corrected failure to look up ethernet device names in the registry on Windows XP x64 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter 15-Dec-05 DTH Patched eth_host_devices [remove non-ethernet devices] (from Mark Pizzolato and Galen Tackett, 08-Jun-05) Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05) 30-Nov-05 DTH Added option to regenerate CRC on received packets; some ethernet devices need to pass it on to the simulation, and by the time libpcap/winpcap gets the packet, the host OS network layer has already stripped CRC out of the packet 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt) 25-Mar-04 MP Revised comments and minor #defines to deal with updated libpcap which now provides pcap_sendpacket on all platforms. 04-Feb-04 MP Returned success/fail status from eth_write to support determining if the current libpcap connection can successfully write packets. Added threaded approach to reading packets since this works better on some platforms (solaris intel) than the inconsistently implemented non-blocking read approach. 04-Feb-04 DTH Converted ETH_DEBUG to sim_debug 13-Jan-04 MP tested and fixed on OpenBSD, NetBS and FreeBSD. 09-Jan-04 MP removed the BIOCSHDRCMPLT ioctl() for OS/X 05-Jan-04 DTH Added eth_mac_scan 30-Dec-03 DTH Cleaned up queue routines, added no network support message 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq 15-Dec-03 MP polished generic libpcap support. 05-Dec-03 DTH Genericized eth_devices() and #ifdefs 03-Dec-03 MP Added Solaris support 02-Dec-03 DTH Corrected decnet fix to use reflection counting 01-Dec-03 DTH Added BPF source filtering and reflection counting 28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs() 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code 19-Nov-03 MP Fixed BPF functionality on Linux/BSD. 17-Nov-03 DTH Added xBSD simplification 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code 13-Nov-03 DTH Merged in __FreeBSD__ support 21-Oct-03 MP Added enriched packet dumping for debugging 20-Oct-03 MP Added support for multiple ethernet devices on VMS 20-Sep-03 Ankan Add VMS support (Alpha only) 29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to format ethernet addresses the way the BPF compile engine wants to see them. Added BPF support to filter packets Added missing printf in eth_close 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. 06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace 30-May-03 DTH Changed WIN32 to _WIN32 for consistency 07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also work on Red Hat 6.2-sparc and Debian 3.0r1-sparc. 03-Mar-03 MP Changed logging to be consistent on stdout and sim_log 01-Feb-03 MP Changed type of local variables in eth_packet_trace to conform to the interface needs of eth_mac_fmt wich produces char data instead of unsigned char data. Suggested by the DECC compiler. 15-Jan-03 DTH Corrected PacketGetAdapterNames parameter2 datatype 26-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source Added networking documentation Changed _DEBUG to ETH_DEBUG 20-Dec-02 MP Added display of packet CRC to the eth_packet_trace. This helps distinguish packets with identical lengths and protocols. 05-Dec-02 MP With the goal of draining the input buffer more rapidly changed eth_read to call pcap_dispatch repeatedly until either a timeout returns nothing or a packet allowed by the filter is seen. This more closely reflects how the pcap layer will work when the filtering is actually done by a bpf filter. 31-Oct-02 DTH Added USE_NETWORK conditional Reworked not attached test Added OpenBSD support (from Federico Schwindt) Added ethX detection simplification (from Megan Gentry) Removed sections of temporary code Added parameter validation 23-Oct-02 DTH Beta 5 released 22-Oct-02 DTH Added all_multicast and promiscuous support Fixed not attached behavior 21-Oct-02 DTH Added NetBSD support (from Jason Thorpe) Patched buffer size to make sure entire packet is read in Made 'ethX' check characters passed as well as length Corrected copyright again 16-Oct-02 DTH Beta 4 released Corrected copyright 09-Oct-02 DTH Beta 3 released Added pdp11 write acceleration (from Patrick Caulfield) 08-Oct-02 DTH Beta 2 released Integrated with 2.10-0p4 Added variable vector and copyrights 04-Oct-02 DTH Added linux support (from Patrick Caulfield) 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 24-Sep-02 DTH Finished eth_devices, eth_getname 18-Sep-02 DTH Callbacks implemented 13-Sep-02 DTH Basic packet read/write written 20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation ------------------------------------------------------------------------------ */ #include #include "sim_ether.h" #include "sim_sock.h" extern FILE *sim_log; /*============================================================================*/ /* OS-independant ethernet routines */ /*============================================================================*/ t_stat eth_mac_scan (ETH_MAC* mac, char* strmac) { int i, j; short unsigned int num; char cptr[18]; int len = strlen(strmac); const ETH_MAC zeros = {0,0,0,0,0,0}; const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; ETH_MAC newmac; /* format of string must be 6 double-digit hex bytes with valid separators ideally, this mac scanner could allow more flexible formatting later */ if (len != 17) return SCPE_ARG; /* copy string to local storage for mangling */ strcpy(cptr, strmac); /* make sure byte separators are OK */ for (i=2; i> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; return(crc ^ mask); } void eth_add_crc32(ETH_PACK* packet) { if (packet->len <= ETH_MAX_PACKET) { uint32 crc = eth_crc32(0, packet->msg, packet->len); /* calculate CRC */ uint32 ncrc = htonl(crc); /* CRC in network order */ int size = sizeof(ncrc); /* size of crc field */ memcpy(&packet->msg[packet->len], &ncrc, size); /* append crc to packet */ packet->crc_len = packet->len + size; /* set packet crc length */ } else { packet->crc_len = 0; /* appending crc would destroy packet */ } } void eth_setcrc(ETH_DEV* dev, int need_crc) { dev->need_crc = need_crc; } void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int dmp) { if (dev->dptr->dctrl & dev->dbit) { char src[20]; char dst[20]; unsigned short* proto = (unsigned short*) &msg[12]; uint32 crc = eth_crc32(0, msg, len); eth_mac_fmt((ETH_MAC*)&msg[0], dst); eth_mac_fmt((ETH_MAC*)&msg[6], src); sim_debug(dev->dbit, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", txt, dst, src, ntohs(*proto), len, crc); if (dmp) { int i, same, group, sidx, oidx; char outbuf[80], strbuf[18]; static char hex[] = "0123456789ABCDEF"; for (i=same=0; i 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { ++same; continue; } if (same > 0) { sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), i-1); same = 0; } group = (((len - i) > 16) ? 16 : (len - i)); for (sidx=oidx=0; sidx>4)&0xf]; outbuf[oidx++] = hex[msg[i+sidx]&0xf]; if (isprint(msg[i+sidx])) strbuf[sidx] = msg[i+sidx]; else strbuf[sidx] = '.'; } outbuf[oidx] = '\0'; strbuf[sidx] = '\0'; sim_debug(dev->dbit, dev->dptr, "%04X%-48s %s\r\n", i, outbuf, strbuf); } if (same > 0) sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), len-1); } } } void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, char* txt) { eth_packet_trace_ex(dev, msg, len, txt, 1/*len > ETH_MAX_PACKET*/); } char* eth_getname(int number, char* name) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); if (count <= number) return 0; strcpy(name, list[number].name); return name; } char* eth_getname_bydesc(char* desc, char* name) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); int i; int j=strlen(desc); for (i=0; i s2) return 1; if (s1 == 0) return 0; } return 0; } char* eth_getname_byname(char* name, char* temp) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); int i, n, found; found = 0; n = strlen(name); for (i=0; ireflections = -1; /* not established yet */ } t_stat eth_show (FILE* st, UNIT* uptr, int32 val, void* desc) { ETH_LIST list[ETH_MAX_DEVICE]; int number = eth_devices(ETH_MAX_DEVICE, list); fprintf(st, "ETH devices:\n"); if (number == -1) fprintf(st, " network support not available in simulator\n"); else if (number == 0) fprintf(st, " no network devices are available\n"); else { int i, min, len; for (i=0, min=0; i min) min = len; for (i=0; iitem) { size_t size = sizeof(struct eth_item) * max; que->max = max; que->item = (struct eth_item *) malloc(size); if (que->item) { /* init dynamic memory */ memset(que->item, 0, size); } else { /* failed to allocate memory */ char* msg = "EthQ: failed to allocate dynamic queue[%d]\r\n"; printf(msg, max); if (sim_log) fprintf(sim_log, msg, max); return SCPE_MEM; }; }; return SCPE_OK; } void ethq_clear(ETH_QUE* que) { /* clear packet array */ memset(que->item, 0, sizeof(struct eth_item) * que->max); /* clear rest of structure */ que->count = que->head = que->tail = que->loss = que->high = 0; } void ethq_remove(ETH_QUE* que) { struct eth_item* item = &que->item[que->head]; if (que->count) { memset(item, 0, sizeof(struct eth_item)); if (++que->head == que->max) que->head = 0; que->count--; } } void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) { struct eth_item* item; /* if queue empty, set pointers to beginning */ if (!que->count) { que->head = 0; que->tail = -1; } /* find new tail of the circular queue */ if (++que->tail == que->max) que->tail = 0; if (++que->count > que->max) { que->count = que->max; /* lose oldest packet */ if (++que->head == que->max) que->head = 0; que->loss++; } if (que->count > que->high) que->high = que->count; /* set information in (new) tail item */ item = &que->item[que->tail]; item->type = type; item->packet.len = pack->len; item->packet.used = 0; item->packet.crc_len = pack->crc_len; memcpy(item->packet.msg, pack->msg, ((pack->len > pack->crc_len) ? pack->len : pack->crc_len)); item->packet.status = status; } /*============================================================================*/ /* Non-implemented versions */ /*============================================================================*/ #if !defined (USE_NETWORK) && !defined(USE_SHARED) t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) {return SCPE_NOFNC;} t_stat eth_close (ETH_DEV* dev) {return SCPE_NOFNC;} t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) {return SCPE_NOFNC;} int eth_devices (int max, ETH_LIST* dev) {return -1;} #else /* endif unimplemented */ /*============================================================================*/ /* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */ /* OpenVMS Alpha uses a WinPcap port and an associated execlet */ /*============================================================================*/ #if defined (xBSD) && !defined(__APPLE__) #include #include #endif /* xBSD */ #include #include /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include #endif #if defined(_WIN32) && defined(USE_SHARED) /* Dynamic DLL loading technique and modified source comes from Etherial/WireShark capture_pcap.c */ /* Dynamic DLL load variables */ static HINSTANCE hDll = 0; /* handle to DLL */ static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ static char* no_wpcap = "wpcap load failure"; /* define pointers to pcap functions needed */ static void (*p_pcap_close) (pcap_t *); static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, char *, int, bpf_u_int32); static int (*p_pcap_datalink) (pcap_t *); static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *); static int (*p_pcap_findalldevs) (pcap_if_t **, char *); static void (*p_pcap_freealldevs) (pcap_if_t *); static void (*p_pcap_freecode) (struct bpf_program *); static char* (*p_pcap_geterr) (pcap_t *); static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *); static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len); static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); static char* (*p_pcap_lib_version) (void); /* load function pointer from DLL */ void load_function(char* function, void** func_ptr) { *func_ptr = GetProcAddress(hDll, function); if (*func_ptr == 0) { char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; printf (msg, function); if (sim_log) fprintf (sim_log, msg, function); dll_loaded = 3; } } /* load wpcap.dll as required */ int load_wpcap(void) { switch(dll_loaded) { case 0: /* not loaded */ /* attempt to load DLL */ hDll = LoadLibrary(TEXT("wpcap.dll")); if (hDll == 0) { /* failed to load DLL */ char* msg = "Eth: Failed to load wpcap.dll\r\n"; char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; printf (msg); printf (msg2); if (sim_log) { fprintf (sim_log, msg); fprintf (sim_log, msg2); } dll_loaded = 2; break; } else { /* DLL loaded OK */ dll_loaded = 1; } /* load required functions; sets dll_load=3 on error */ load_function("pcap_close", (void**) &p_pcap_close); load_function("pcap_compile", (void**) &p_pcap_compile); load_function("pcap_datalink", (void**) &p_pcap_datalink); load_function("pcap_dispatch", (void**) &p_pcap_dispatch); load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); load_function("pcap_freecode", (void**) &p_pcap_freecode); load_function("pcap_geterr", (void**) &p_pcap_geterr); load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); load_function("pcap_open_live", (void**) &p_pcap_open_live); load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); load_function("pcap_setfilter", (void**) &p_pcap_setfilter); load_function("pcap_lib_version", (void**) &p_pcap_lib_version); if (dll_loaded == 1) { /* log successful load */ char* version = p_pcap_lib_version(); printf("%s\n", version); if (sim_log) fprintf(sim_log, "%s\n", version); } break; default: /* loaded or failed */ break; } return (dll_loaded == 1) ? 1 : 0; } /* define functions with dynamic revectoring */ void pcap_close(pcap_t* a) { if (load_wpcap() != 0) { p_pcap_close(a); } } int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { if (load_wpcap() != 0) { return p_pcap_compile(a, b, c, d, e); } else { return 0; } } int pcap_datalink(pcap_t* a) { if (load_wpcap() != 0) { return p_pcap_datalink(a); } else { return 0; } } int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { if (load_wpcap() != 0) { return p_pcap_dispatch(a, b, c, d); } else { return 0; } } int pcap_findalldevs(pcap_if_t** a, char* b) { if (load_wpcap() != 0) { return p_pcap_findalldevs(a, b); } else { *a = 0; strcpy(b, no_wpcap); return -1; } } void pcap_freealldevs(pcap_if_t* a) { if (load_wpcap() != 0) { p_pcap_freealldevs(a); } } void pcap_freecode(struct bpf_program* a) { if (load_wpcap() != 0) { p_pcap_freecode(a); } } char* pcap_geterr(pcap_t* a) { if (load_wpcap() != 0) { return p_pcap_geterr(a); } else { return (char*) 0; } } int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { if (load_wpcap() != 0) { return p_pcap_lookupnet(a, b, c, d); } else { return 0; } } pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) { if (load_wpcap() != 0) { return p_pcap_open_live(a, b, c, d, e); } else { return (pcap_t*) 0; } } int pcap_sendpacket(pcap_t* a, const u_char* b, int c) { if (load_wpcap() != 0) { return p_pcap_sendpacket(a, b, c); } else { return 0; } } int pcap_setfilter(pcap_t* a, struct bpf_program* b) { if (load_wpcap() != 0) { return p_pcap_setfilter(a, b); } else { return 0; } } #endif /* Some platforms have always had pcap_sendpacket */ #if defined(_WIN32) || defined(VMS) #define HAS_PCAP_SENDPACKET 1 #else /* The latest libpcap and WinPcap all have pcap_sendpacket */ #if !defined (NEED_PCAP_SENDPACKET) #define HAS_PCAP_SENDPACKET 1 #endif #endif #if !defined (HAS_PCAP_SENDPACKET) /* libpcap has no function to write a packet, so we need to implement pcap_sendpacket() for compatibility with the WinPcap base code. Return value: 0=Success, -1=Failure */ int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) { #if defined (__linux) return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1; #else return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1; #endif /* linux */ } #endif /* !HAS_PCAP_SENDPACKET */ #if defined (USE_READER_THREAD) #include void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); static void * _eth_reader(void *arg) { ETH_DEV* volatile dev = (ETH_DEV*)arg; int status; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 200*1000; sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); while (dev->handle) { #if defined (MUST_DO_SELECT) int sel_ret; fd_set setl; FD_ZERO(&setl); FD_SET(pcap_get_selectable_fd((pcap_t *)dev->handle), &setl); sel_ret = select(1+pcap_get_selectable_fd((pcap_t *)dev->handle), &setl, NULL, NULL, &timeout); if (sel_ret < 0 && errno != EINTR) break; if (sel_ret > 0) { /* dispatch read request queue available packets */ status = pcap_dispatch((pcap_t*)dev->handle, -1, ð_callback, (u_char*)dev); } #else /* dispatch read request queue available packets */ status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); #endif } sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); return NULL; } #endif t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { const int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; char errbuf[PCAP_ERRBUF_SIZE]; char temp[1024]; char* savname = name; int num; char* msg; /* initialize device */ eth_zero(dev); /* translate name of type "ethX" to real device name */ if ((strlen(name) == 4) && (tolower(name[0]) == 'e') && (tolower(name[1]) == 't') && (tolower(name[2]) == 'h') && isdigit(name[3]) ) { num = atoi(&name[3]); savname = eth_getname(num, temp); if (savname == 0) /* didn't translate */ return SCPE_OPENERR; } else { /* are they trying to use device description? */ savname = eth_getname_bydesc(name, temp); if (savname == 0) { /* didn't translate */ /* probably is not ethX and has no description */ savname = eth_getname_byname(name, temp); if (savname == 0) /* didn't translate */ return SCPE_OPENERR; } } /* attempt to connect device */ memset(errbuf, 0, sizeof(errbuf)); dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (!dev->handle) { /* can't open device */ msg = "Eth: pcap_open_live error - %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); return SCPE_OPENERR; } else { msg = "Eth: opened %s\r\n"; printf (msg, savname); if (sim_log) fprintf (sim_log, msg, savname); } /* save name of device */ dev->name = malloc(strlen(savname)+1); strcpy(dev->name, savname); /* save debugging information */ dev->dptr = dptr; dev->dbit = dbit; #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) /* Tell the kernel that the header is fully-formed when it gets it. This is required in order to fake the src address. */ { int one = 1; ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); } #endif /* xBSD */ #if defined (USE_READER_THREAD) { pthread_attr_t attr; ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ pthread_mutex_init (&dev->lock, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev); pthread_attr_destroy(&attr); } #else /* !defined (USE_READER_THREAD */ #ifdef USE_SETNONBLOCK /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ if (pcap_setnonblock (dev->handle, 1, errbuf) == -1) { msg = "Eth: Failed to set non-blocking: %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); } #endif #endif /* !defined (USE_READER_THREAD */ return SCPE_OK; } t_stat eth_close(ETH_DEV* dev) { char* msg = "Eth: closed %s\r\n"; pcap_t *pcap; /* make sure device exists */ if (!dev) return SCPE_UNATT; /* close the device */ pcap = (pcap_t *)dev->handle; dev->handle = NULL; pcap_close(pcap); printf (msg, dev->name); if (sim_log) fprintf (sim_log, msg, dev->name); #if defined (USE_READER_THREAD) pthread_join (dev->reader_thread, NULL); #endif /* clean up the mess */ free(dev->name); eth_zero(dev); return SCPE_OK; } t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) { ETH_PACK send, recv; t_stat status; int i; struct timeval delay; /* build a packet */ memset (&send, 0, sizeof(ETH_PACK)); send.len = ETH_MIN_PACKET; /* minimum packet size */ memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ send.msg[12] = 0x90; /* loopback packet type */ for (i=14; ireflections = 0; eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); /* send the packet */ status = eth_write (dev, &send, NULL); if (status != SCPE_OK) { char *msg; msg = "Eth: Error Transmitting packet: %s\r\n" "You may need to run as root, or install a libpcap version\r\n" "which is at least 0.9 from www.tcpdump.org\r\n"; printf(msg, strerror(errno)); if (sim_log) fprintf (sim_log, msg, strerror(errno)); return status; } /* if/when we have a sim_os_msleep() we'll use it here instead of this select() */ delay.tv_sec = 0; delay.tv_usec = 50*1000; select(0, NULL, NULL, NULL, &delay); /* make sure things settle into the read path */ /* empty the read queue and count the reflections */ do { memset (&recv, 0, sizeof(ETH_PACK)); status = eth_read (dev, &recv, NULL); if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0) dev->reflections++; } while (recv.len > 0); sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); return dev->reflections; } t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status = 1; /* default to failure */ /* make sure device exists */ if (!dev) return SCPE_UNATT; /* make sure packet exists */ if (!packet) return SCPE_ARG; /* make sure packet is acceptable length */ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { eth_packet_trace (dev, packet->msg, packet->len, "writing"); /* dispatch write request (synchronous; no need to save write info to dev) */ status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); /* detect sending of decnet loopback packet */ if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg)) dev->decnet_self_sent += dev->reflections; } /* if packet->len */ /* call optional write callback function */ if (routine) (routine)(status); return ((status == 0) ? SCPE_OK : SCPE_IOERR); } void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { ETH_DEV* dev = (ETH_DEV*) info; #ifdef USE_BPF int to_me = 1; #else /* !USE_BPF */ int to_me = 0; int from_me = 0; int i; #ifdef ETH_DEBUG // eth_packet_trace (dev, data, header->len, "received"); #endif for (i = 0; i < dev->addr_count; i++) { if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; } /* all multicast mode? */ if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; /* promiscuous mode? */ if (dev->promiscuous) to_me = 1; #endif /* USE_BPF */ /* detect sending of decnet loopback packet */ if (DECNET_SELF_FRAME(dev->decnet_addr, data)) { /* lower reflection count - if already zero, pass it on */ if (dev->decnet_self_sent > 0) { dev->decnet_self_sent--; to_me = 0; } #ifndef USE_BPF else from_me = 0; #endif } #ifdef USE_BPF if (to_me) { #else /* !USE_BPF */ if (to_me && !from_me) { #endif #if defined (USE_READER_THREAD) ETH_PACK tmp_packet; /* set data in passed read packet */ tmp_packet.len = header->len; memcpy(tmp_packet.msg, data, header->len); if (dev->need_crc) eth_add_crc32(&tmp_packet); eth_packet_trace (dev, tmp_packet.msg, tmp_packet.len, "rcvqd"); pthread_mutex_lock (&dev->lock); ethq_insert(&dev->read_queue, 2, &tmp_packet, 0); pthread_mutex_unlock (&dev->lock); #else /* set data in passed read packet */ dev->read_packet->len = header->len; memcpy(dev->read_packet->msg, data, header->len); if (dev->need_crc) eth_add_crc32(dev->read_packet); eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); /* call optional read callback function */ if (dev->read_callback) (dev->read_callback)(0); #endif } } t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status; /* make sure device exists */ if (!dev) return SCPE_UNATT; /* make sure packet exists */ if (!packet) return SCPE_ARG; #if !defined (USE_READER_THREAD) /* set read packet */ dev->read_packet = packet; packet->len = 0; /* set optional callback routine */ dev->read_callback = routine; /* dispatch read request to either receive a filtered packet or timeout */ do { status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); } while ((status) && (0 == packet->len)); #else /* USE_READER_THREAD */ status = 0; pthread_mutex_lock (&dev->lock); if (dev->read_queue.count > 0) { ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; packet->len = item->packet.len; memcpy(packet->msg, item->packet.msg, packet->len); if (routine) routine(status); ethq_remove(&dev->read_queue); } pthread_mutex_unlock (&dev->lock); #endif return SCPE_OK; } t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) { int i; bpf_u_int32 bpf_subnet, bpf_netmask; char buf[110+66*ETH_FILTER_MAX]; char errbuf[PCAP_ERRBUF_SIZE]; char mac[20]; char* buf2; t_stat status; #ifdef USE_BPF struct bpf_program bpf; char* msg; #endif /* make sure device exists */ if (!dev) return SCPE_UNATT; /* filter count OK? */ if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) return SCPE_ARG; else if (!addresses) return SCPE_ARG; /* set new filter addresses */ for (i = 0; i < addr_count; i++) memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); dev->addr_count = addr_count; /* store other flags */ dev->all_multicast = all_multicast; dev->promiscuous = promiscuous; /* print out filter information if debugging */ if (dev->dptr->dctrl & dev->dbit) { sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); for (i = 0; i < addr_count; i++) { char mac[20]; eth_mac_fmt(&dev->filter_address[i], mac); sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); } if (dev->all_multicast) sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); if (dev->promiscuous) sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } /* test reflections */ if (dev->reflections == -1) status = eth_reflect(dev, dev->filter_address[0]); /* setup BPF filters and other fields to minimize packet delivery */ strcpy(buf, ""); /* construct destination filters - since the real ethernet interface was set into promiscuous mode by eth_open(), we need to filter out the packets that our simulated interface doesn't want. */ if (!dev->promiscuous) { for (i = 0; i < addr_count; i++) { eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf, mac)) /* eliminate duplicates */ sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "", mac); } if (dev->all_multicast) sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : ""); } /* construct source filters - this prevents packets from being reflected back by systems where WinPcap and libpcap cause packet reflections. Note that some systems do not reflect packets at all. This *assumes* that the simulated NIC will not send out packets with multicast source fields. */ if ((addr_count > 0) && (dev->reflections > 0)) { if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], " and "); sprintf (&buf[strlen(buf)], "not ("); buf2 = &buf[strlen(buf)]; for (i = 0; i < addr_count; i++) { if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf2, mac)) /* eliminate duplicates */ sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); } sprintf (&buf[strlen(buf)], ")"); } /* When starting, DECnet sends out a packet with the source and destination addresses set to the same value as the DECnet MAC address. This packet is designed to find and help diagnose DECnet address conflicts. Normally, this packet would not be seen by the sender, only by the other machine that has the same DECnet address. If the ethernet subsystem is reflecting packets, DECnet will fail to start if it sees the reflected packet, since it thinks another system is using this DECnet address. We have to let these packets through, so that if another machine has the same DECnet address that we can detect it. Both eth_write() and eth_callback() help by checking the reflection count - eth_write() adds the reflection count to dev->decnet_self_sent, and eth_callback() check the value - if the dev->decnet_self_sent count is zero, then the packet has come from another machine with the same address, and needs to be passed on to the simulated machine. */ memset(dev->decnet_addr, 0, sizeof(ETH_MAC)); /* check for decnet address in filters */ if ((addr_count) && (dev->reflections > 0)) { for (i = 0; i < addr_count; i++) { eth_mac_fmt(&dev->filter_address[i], mac); if (memcmp(mac, "AA:00:04", 8) == 0) { memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC)); /* let packets through where dst and src are the same as our decnet address */ sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); break; } } } sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); /* get netmask, which is required for compiling */ if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) { bpf_netmask = 0; } #ifdef USE_BPF /* compile filter string */ if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); msg = "Eth: pcap_compile error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); /* show erroneous BPF string */ msg = "Eth: BPF string is: |%s|\r\n"; printf (msg, buf); if (sim_log) fprintf (sim_log, msg, buf); } else { /* apply compiled filter string */ if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); msg = "Eth: pcap_setfilter error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); } else { #ifdef USE_SETNONBLOCK /* set file non-blocking */ status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ } pcap_freecode(&bpf); } #endif /* USE_BPF */ return SCPE_OK; } /* The libpcap provided API pcap_findalldevs() on most platforms, will leverage the getifaddrs() API if it is available in preference to alternate platform specific methods of determining the interface list. A limitation of getifaddrs() is that it returns only interfaces which have associated addresses. This may not include all of the interesting interfaces that we are interested in since a host may have dedicated interfaces for a simulator, which is otherwise unused by the host. One could hand craft the the build of libpcap to specifically use alternate methods to implement pcap_findalldevs(). However, this can get tricky, and would then result in a sort of deviant libpcap. This routine exists to allow platform specific code to validate and/or extend the set of available interfaces to include any that are not returned by pcap_findalldevs. */ int eth_host_devices(int used, int max, ETH_LIST* list) { pcap_t* conn; int i, j, datalink; char errbuf[PCAP_ERRBUF_SIZE]; for (i=0; i sizeof(regval))) { RegCloseKey (reghnd); continue; } /* registry value seems OK, finish up and replace description */ RegCloseKey (reghnd ); sprintf (list[i].desc, "%s", regval); } } /* for */ #endif return used; } int eth_devices(int max, ETH_LIST* list) { pcap_if_t* alldevs; pcap_if_t* dev; int i = 0; char errbuf[PCAP_ERRBUF_SIZE]; #ifndef DONT_USE_PCAP_FINDALLDEVS /* retrieve the device list */ if (pcap_findalldevs(&alldevs, errbuf) == -1) { char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); } else { /* copy device list into the passed structure */ for (i=0, dev=alldevs; dev; dev=dev->next) { if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; list[i].num = i; sprintf(list[i].name, "%s", dev->name); if (dev->description) sprintf(list[i].desc, "%s", dev->description); else sprintf(list[i].desc, "%s", "No description available"); if (i++ >= max) break; } /* free device list */ pcap_freealldevs(alldevs); } #endif /* Add any host specific devices and/or validate those already found */ i = eth_host_devices(i, max, list); /* return device count */ return i; } #endif /* USE_NETWORK */ simh-3.8.1/sim_console.h0000644000175000017500000000741411111655430013327 0ustar vlmvlm/* sim_console.h: simulator console I/O library headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 22-Jun-06 RMS Implemented SET/SHOW PCHAR 22-Nov-05 RMS Added central input/output conversion support 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy 28-May-04 RMS Added SET/SHOW CONSOLE 02-Jan-04 RMS Removed timer routines, added Telnet console routines */ #ifndef _SIM_CONSOLE_H_ #define _SIM_CONSOLE_H_ 0 #define TTUF_V_MODE (UNIT_V_UF + 0) #define TTUF_W_MODE 2 #define TTUF_MODE_7B 0 #define TTUF_MODE_8B 1 #define TTUF_MODE_UC 2 #define TTUF_MODE_7P 3 #define TTUF_KSR (1u << TTUF_W_MODE) #define TTUF_M_MODE ((1u << TTUF_W_MODE) - 1) #define TTUF_V_UF (TTUF_V_MODE + TTUF_W_MODE) #define TT_MODE (TTUF_M_MODE << TTUF_V_MODE) #define TT_MODE_7B (TTUF_MODE_7B << TTUF_V_MODE) #define TT_MODE_8B (TTUF_MODE_8B << TTUF_V_MODE) #define TT_MODE_UC (TTUF_MODE_UC << TTUF_V_MODE) #define TT_MODE_7P (TTUF_MODE_7P << TTUF_V_MODE) #define TT_MODE_KSR (TT_MODE_UC) #define TT_GET_MODE(x) (((x) >> TTUF_V_MODE) & TTUF_M_MODE) t_stat sim_set_console (int32 flag, char *cptr); t_stat sim_set_kmap (int32 flag, char *cptr); t_stat sim_set_telnet (int32 flag, char *cptr); t_stat sim_set_notelnet (int32 flag, char *cptr); t_stat sim_set_logon (int32 flag, char *cptr); t_stat sim_set_logoff (int32 flag, char *cptr); t_stat sim_set_debon (int32 flag, char *cptr); t_stat sim_set_deboff (int32 flag, char *cptr); t_stat sim_set_pchar (int32 flag, char *cptr); t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_check_console (int32 sec); t_stat sim_poll_kbd (void); t_stat sim_putchar (int32 c); t_stat sim_putchar_s (int32 c); t_stat sim_ttinit (void); t_stat sim_ttrun (void); t_stat sim_ttcmd (void); t_stat sim_ttclose (void); t_stat sim_os_poll_kbd (void); t_stat sim_os_putchar (int32 out); int32 sim_tt_inpcvt (int32 c, uint32 mode); int32 sim_tt_outcvt (int32 c, uint32 mode); #endif simh-3.8.1/0readme_ethernet.txt0000644000175000017500000003475610260653022014630 0ustar vlmvlmThis file contains information about the SIMH Ethernet package. ------------------------------------------------------------------------------- The XQ emulator is a host-independant software emulation of Digital's DELQA (M7516) and DEQNA (M7504) Q-bus ethernet cards for the SIMH emulator. The XU emulator is a host-independant software emulation of Digital's DEUNA (M7792/M7793) and DELUA (M7521) Unibus ethernet cards for the SIMH emulator. The XQ and XU simulators use the Sim_Ether module to execute host-specific packet reads and writes, since all operating systems talk to real ethernet cards/controllers differently. See the comments at the top of sim_ether.c for the list of currently supported host platforms. The Sim_Ether module sets the selected ethernet card into promiscuous mode to gather all packets, then filters out the packets that it doesn't want. In Windows, packets having the same source MAC address as the controller are ignored for WinPCAP compatibility (see Windows notes below). If your ethernet card is plugged into a switch, the promiscuous mode setting should not cause much of a problem, since the switch will still filter out most of the undesirable traffic. You will only see "excessive" traffic if you are on a direct or hub(repeater) segment. Using the libpcap/WinPcap interface, the simulated computer cannot "talk" to the host computer via the selected interface, since the packets are not reflected back to the host. The workaround for this is to use a second NIC in the host and connect them both into the same network; then the host and the simulator can communicate over the physical LAN. Universal TUN/TAP support provides another solution for the above dual-NIC problem for systems that support Universal TUN/TAP. Since the TUN/TAP interface is at a different network level, the host can create a TAP device for the simulator and then bridge or route packets between the TAP device and the real network interface. Note that the TAP device and any bridging or routing must be established before running the simulator; SIMH does not create, bridge, or route TAP devices for you. ------------------------------------------------------------------------------- Windows notes: 1. The Windows-specific code uses the WinPCAP 3.0 package from http://www.winpcap.org. This package for windows simulates the libpcap package that is freely available for un*x systems. 2. You must *install* the WinPCAP runtime package. 3. The first time the WinPCAP driver is used, it will be dynamically loaded, and the user must be an Administrator on the machine to do so. If you need to run as an unprivileged user, you must set the service to autostart. See the WinPCAP documentation for details on the static loading workaround. 4. If you want to use TAP devices, they must be created before running SIMH. (TAP component from the OpenVPN project; http://openvpn.sourceforge.net) 5. Compaq PATHWORKS 32 v7.2 also enabled bridging for the ethernet adapters when the DECNET and LAT drivers were installed; TAP was not needed. Building on Windows: 1. Install WinPCAP 3.0 runtime and the WinPCAP Developer's kit. 2. Put the required .h files (bittypes,devioctl,ip6_misc,packet32,pcap, pcap-stdinc).h from the WinPCAP 3.0 developer's kit in the compiler's path 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 3.0 developer's kit in the linker's path 4. If you're using Borland C++, use COFF2OMF to convert the .lib files into a format that can be used by the compiler. 5. Define USE_NETWORK. 6. Build it! ------------------------------------------------------------------------------- Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- Sim_Ether has been reworked to be more universal; because of this, you will need to get a version of libpcap that is 0.9 or greater. This can be downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c for details. At the time of this release, the "Current Version" available at: http://www.tcpdump.org/daily/libpcap-current.tar.gz is the latest checked-in source code that is actually higher than the released 0.8.3 version number. Specifically, for all platforms, it contains code that opens the ethernet device in Read/Write mode instead of the Read-Only mode that previous libpcap versions for platforms which use one of pcap-bpf.c, pcap-pf.c, or pcap-snit.c. This capabiligy now exists to support a newly provided generic packet sending capability. ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- 1. For all platforms, you must run SIMH(scp) with sufficient privilege to allow the ethernet card can be set into promiscuous mode and to write packets through the driver. For most Unix/Unix-like platforms this will mean running as root. For systems which use bpf devices (NetBSD, OpenBSD, FreeBSD and OS/X) it is possible to set permissions on the bpf devices to allow read and write access to users other than root (For example: chmod 666 /dev/bpf*). Doing this, has its own security issues. Additional alternative methods for avoiding the 'run as root' requirement will be welcomed. 2. If you want to use TAP devices, they must be created before running SIMH. Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: 1. Get/make/install the libpcap package for your operating system. Sources: All : http://www.tcpdump.org/ Older versions of libpcap can be found, for various systems, at: Linux : search for your variant on http://rpmfind.net OS/X : Apple Developer's site? NOTE: These repositories will not likely contain a version of libpcap greater than 0.8.1 for several years since other packages in these repositories don't depend on a later version than they currently have. 2. Use 'make USE_NETWORK=1' 3. Build it! ------------------------------------------------------------------------------- OpenVMS Alpha notes: 1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is when required VCI promiscuous mode support was added. Hobbyists can get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. Running a simulator built with ethernet support on a version of VMS prior to 7.3-1 will behave as if there is no ethernet support built in due to the inability of the software to set the PCAPVCM into promiscuous mode. An example display of fully functional ethernet support: sim> SHOW XQ ETH ETH devices: 0 we0 (VMS Device: _EWA0:) 1 we1 (VMS Device: _EWB0:) An example display when the simulator was built without ethernet support or is not running the required version of VMS: sim> SHOW XQ ETH ETH devices: no network devices are available 2. You must place the PCAPVCM.EXE execlet in SYS$LOADABLE_IMAGES before running a simulator with ethernet support. Note: This is done by the build commands in descrip.mms. 3. You must have CMKRNL privilege to SHOW or ATTACH an ethernet device; alternatively, you can INSTALL the simulator with CMKRNL privilege. 4. If you use a second adapter to communicate to the host, SOME protocol that creates an I/O structure (SCS, DECNET, TCP) must be running on the adapter prior trying to connect with SIMH, or the host may crash. The execlet is not written to create an I/O structure for the device. Building on OpenVMS Alpha: The current descrip.mms file will build simulators capable of using ethernet support with them automatically. These currently are: VAX, PDP11, and PDP10. The descrip.mms driven builds will also build the pcap library and build and install the VCI execlet. 1. Fetch the VMS-PCAP zip file from: http://simh.trailing-edge.com/sources/vms-pcap.zip 2. Unzip it into the base of the simh distribution directory. 3. Build the simulator(s) with MMS or MMK: $ MMx {VAX,PDP11,PDP10, etc...} ------------------------------------------------------------------------------- VAX simulator support: An OpenVMS VAX v7.2 system with DECNET Phase IV, MultiNet 4.4a, and LAT 5.3 has been successfully run. Other testers have reported success booting NetBSD and OpenVMS VAX 5.5-2 also. PDP11 simulator support: An RT-11 v5.3 system with a freeware TCP/IP stack has been successfully run. Other testers have reported that RSX with DECNET and the NetBSD operating systems also work. RSTS/E v10.1 has preliminary support - RSTS/E boots and enables the XH (XQ) device - DECNET and LAT software have not been tested. The XU module has been tested by a third party for basic packet functionality under a modified RSX11M environment. I am unable to test it in-house until someone can arrange to send me a disk image containing a stock RSTS/E or RSX11M+ system image that also contains DECNET, LAT, and/or TCP/IP software. ------------------------------------------------------------------------------- How to debug problems with the ethernet subsystems: PLEASE read the host-specific notes in sim_ether.c! While running SCP, the following commands can be used to enable debug messages: scp> SET DEBUG STDERR scp> SET XQ DEBUG={ETH|TRC|REG|WRN|CSR|VAR|SAN|SET|PCK} scp> SET XU DEBUG={ETH|TRC|REG|WRN} Documentation of the functionality of these debug modifiers can be found in pdp11_xq.h and pdp11_xu.h. Inline debugging has replaced the previous #ifdef style of debugging, which required recompilation before debugging. ------------------------------------------------------------------------------- Things planned for future releases: 1. PDP-11 bootstrap/bootrom 2. Full MOP implementation 3. DESQA support (if someone can get me the user manuals) 4. DETQA support [DELQA-Turbo] (I have the manual) ------------------------------------------------------------------------------- Things which I need help with: 1. Information about Remote MOP processing 2. VAX/PDP-11 hardware diagnotics image files and docs, to test XQ thoroughly. 3. Feedback on operation with other VAX/PDP-11 OS's. ------------------------------------------------------------------------------- Please send all patches, questions, feedback, clarifications, and help to: david DOT hittner AT ngc DOT com Thanks, and Enjoy!! Dave =============================================================================== Change Log =============================================================================== 19-Mar-04 Release: 1. Genericized Sim_Ether code, reduced #ifdefs (David Hittner) 2. Further refinement of sim_ether, qualified more platforms (Mark Pizzolato) 3. Added XU module (David Hittner) 4. Corrected XQ interrupt signalling for PDP11s (David Hittner) 5. Added inline debugging support (David Hittner) ------------------------------------------------------------------------------- 26-Nov-03 Release: 1. Added VMS support to Sim_Ether; created pcap-vms port (Anders Ahgren) 2. Added DECNET duplicate detection for Windows (Mark Pizzolato) 3. Added BPF filtering to increase efficiency (Mark Pizzolato) 4. Corrected XQ Runt processing (Mark Pizzolato) 5. Corrected XQ Sofware Reset (Mark Pizzolato) 6. Corrected XQ Multicast/Promiscuous mode setting/resetting (Mark Pizzolato) 7. Added Universal TUN/TAP support (Mark Pizzolato) 8. Added FreeBSD support (Edward Brocklesby) 9. Corrected interrupts on XQB device (David Hittner) ------------------------------------------------------------------------------- 05-Jun-03 Release: 1. Added SET/SHOW XQ STATS (David Hittner) 2. Added SHOW XQ FILTERS (David Hittner) 3. Added ability to split rcv packets into multiple buffers (David Hittner) 4. Added explicit runt & giant packet processing (David Hittner) ------------------------------------------------------------------------------- 30-May-03 Release: 1. Corrected bug in xq_setmac introduced in v3.0 (multiple people) 2. Made XQ rcv buffer allocation dynamic to reduce scp size (David Hittner) 3. Optimized some structs, removed legacy variables (Mark Pizzolato) 4. Changed #ifdef WIN32 to _WIN32 for consistancy (Mark Pizzolato) ------------------------------------------------------------------------------- 06-May-03 Release: 1. Added second XQ controller (David Hittner) 2. Added SIMH v3.0 compatibility (David Hittner) 3. Removed SET ADDRESS functionality (David Hittner) ------------------------------------------------------------------------------- 10-Apr-03 Release: 1. Added preliminary support for RSTS/E (David Hittner) 2. Added PDP-11 bootrom load via CSR flags (David Hittner) 3. Support for SPARC linux (Mark Pizzolato) ------------------------------------------------------------------------------- 11-Mar-03 Release: 1. Added support for RT-11 TCP/IP 2. Corrected interrupts (thanks to Tom Evans and Bob Supnik) 3. Moved change log to the bottom of the readme file, cleaned up document ------------------------------------------------------------------------------- 16-Jan-03 Release: 1. Added VMScluster support (thanks to Mark Pizzolato) 2. Verified VAX remote boot functionality (>>>B XQA0) 3. Added major performance enhancements (thanks to Mark Pizzolato again) 4. Changed _DEBUG tracers to XQ_DEBUG and ETH_DEBUG 5. Added local packet processing 6. Added system id broadcast ------------------------------------------------------------------------------- 08-Nov-02 Release: 1. Added USE_NETWORK conditional to Sim_Ether 2. Fixed behaviour of SHOW XQ ETH if no devices exist 3. Added OpenBSD support to Sim_Ether (courtesy of Federico Schwindt) 4. Added ethX detection simplification (from Megan Gentry) =============================================================================== simh-3.8.1/sim_defs.h0000644000175000017500000006512711107312406012611 0ustar vlmvlm/* sim_defs.h: simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Jul-08 RMS Removed inlining support 28-May-08 RMS Added inlining support 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) 18-Jun-07 RMS Added UNIT_IDLE flag 18-Mar-07 RMS Added UNIT_TEXT flag 07-Mar-07 JDB Added DEBUG_PRJ macro 18-Oct-06 RMS Added limit check for clock synchronized keyboard waits 13-Jul-06 RMS Guarantee CBUFSIZE is at least 256 07-Jan-06 RMS Added support for breakpoint spaces Added REG_FIT flag 16-Aug-05 RMS Fixed C++ declaration and cast problems 11-Mar-05 RMS Moved 64b data type definitions outside USE_INT64 07-Feb-05 RMS Added assertion fail stop 05-Nov-04 RMS Added support for SHOW opt=val 20-Oct-04 RMS Converted all base types to typedefs 21-Sep-04 RMS Added switch to flag stop message printout 06-Feb-04 RMS Moved device and unit user flags fields (V3.2) RMS Added REG_VMAD 29-Dec-03 RMS Added output stall status 15-Jun-03 RMS Added register flag REG_VMIO 23-Apr-03 RMS Revised for 32b/64b t_addr 14-Mar-03 RMS Lengthened default serial output wait 31-Mar-03 RMS Added u5, u6 fields 18-Mar-03 RMS Added logical name support Moved magtape definitions to sim_tape.h Moved breakpoint definitions from scp.c 03-Mar-03 RMS Added sim_fsize 08-Feb-03 RMS Changed sim_os_sleep to void, added match_ext 05-Jan-03 RMS Added hidden switch definitions, device dyn memory support, parameters for function pointers, case sensitive SET support 22-Dec-02 RMS Added break flag 08-Oct-02 RMS Increased simulator error code space Added Telnet errors Added end of medium support Added help messages to CTAB Added flag and context fields to DEVICE Added restore flag masks Revised 64b definitions 02-May-02 RMS Removed log status codes 22-Apr-02 RMS Added magtape record length error 30-Dec-01 RMS Generalized timer package, added circular arrays 07-Dec-01 RMS Added breakpoint package 01-Dec-01 RMS Added read-only unit support, extended SET/SHOW features, improved error messages 24-Nov-01 RMS Added unit-based registers 27-Sep-01 RMS Added queue count prototype 17-Sep-01 RMS Removed multiple console support 07-Sep-01 RMS Removed conditional externs on function prototypes 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 17-Jul-01 RMS Added additional function prototypes 27-May-01 RMS Added multiple console support 15-May-01 RMS Increased string buffer size 25-Feb-01 RMS Revisions for V2.6 15-Oct-00 RMS Editorial revisions for V2.5 11-Jul-99 RMS Added unsigned int data types 14-Apr-99 RMS Converted t_addr to unsigned 04-Oct-98 RMS Additional definitions for V2.4 The interface between the simulator control package (SCP) and the simulator consists of the following routines and data structures sim_name simulator name string sim_devices[] array of pointers to simulated devices sim_PC pointer to saved PC register descriptor sim_interval simulator interval to next event sim_stop_messages[] array of pointers to stop messages sim_instr() instruction execution routine sim_load() binary loader routine sim_emax maximum number of words in an instruction In addition, the simulator must supply routines to print and parse architecture specific formats print_sym print symbolic output parse_sym parse symbolic input */ #ifndef _SIM_DEFS_H_ #define _SIM_DEFS_H_ 0 #include #include #include #include #include #include #include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* Length specific integer declarations */ typedef signed char int8; typedef signed short int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef int t_stat; /* status */ typedef int t_bool; /* boolean */ /* 64b integers */ #if defined (__GNUC__) /* GCC */ typedef signed long long t_int64; typedef unsigned long long t_uint64; #elif defined (_WIN32) /* Windows */ typedef signed __int64 t_int64; typedef unsigned __int64 t_uint64; #elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ typedef signed __int64 t_int64; typedef unsigned __int64 t_uint64; #elif defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ typedef signed long t_int64; typedef unsigned long t_uint64; #else /* default */ #define t_int64 signed long long #define t_uint64 unsigned long long #endif /* end 64b */ #if defined (USE_INT64) /* 64b data */ typedef t_int64 t_svalue; /* signed value */ typedef t_uint64 t_value; /* value */ #else /* 32b data */ typedef int32 t_svalue; typedef uint32 t_value; #endif /* end 64b data */ #if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */ typedef t_uint64 t_addr; #define T_ADDR_W 64 #else /* 32b address */ typedef uint32 t_addr; #define T_ADDR_W 32 #endif /* end 64b address */ /* Stubs for inlining */ #define SIM_INLINE /* System independent definitions */ #define FLIP_SIZE (1 << 16) /* flip buf size */ #if !defined (PATH_MAX) /* usually in limits */ #define PATH_MAX 512 #endif #if (PATH_MAX >= 128) #define CBUFSIZE (128 + PATH_MAX) /* string buf size */ #else #define CBUFSIZE 256 #endif /* Breakpoint spaces definitions */ #define SIM_BKPT_N_SPC 64 /* max number spaces */ #define SIM_BKPT_V_SPC 26 /* location in arg */ /* Extended switch definitions (bits >= 26) */ #define SIM_SW_HIDE (1u << 26) /* enable hiding */ #define SIM_SW_REST (1u << 27) /* attach/restore */ #define SIM_SW_REG (1u << 28) /* register value */ #define SIM_SW_STOP (1u << 29) /* stop message */ /* Simulator status codes 0 ok 1 - (SCPE_BASE - 1) simulator specific SCPE_BASE - n general */ #define SCPE_OK 0 /* normal return */ #define SCPE_BASE 64 /* base for messages */ #define SCPE_NXM (SCPE_BASE + 0) /* nxm */ #define SCPE_UNATT (SCPE_BASE + 1) /* no file */ #define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */ #define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */ #define SCPE_FMT (SCPE_BASE + 4) /* loader format */ #define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */ #define SCPE_OPENERR (SCPE_BASE + 6) /* open error */ #define SCPE_MEM (SCPE_BASE + 7) /* alloc error */ #define SCPE_ARG (SCPE_BASE + 8) /* argument error */ #define SCPE_STEP (SCPE_BASE + 9) /* step expired */ #define SCPE_UNK (SCPE_BASE + 10) /* unknown command */ #define SCPE_RO (SCPE_BASE + 11) /* read only */ #define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */ #define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */ #define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */ #define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */ #define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */ #define SCPE_EOF (SCPE_BASE + 17) /* end of file */ #define SCPE_REL (SCPE_BASE + 18) /* relocation error */ #define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */ #define SCPE_ALATT (SCPE_BASE + 20) /* already attached */ #define SCPE_TIMER (SCPE_BASE + 21) /* hwre timer err */ #define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */ #define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */ #define SCPE_SUB (SCPE_BASE + 24) /* subscript err */ #define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */ #define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */ #define SCPE_NORO (SCPE_BASE + 27) /* rd only not ok */ #define SCPE_INVSW (SCPE_BASE + 28) /* invalid switch */ #define SCPE_MISVAL (SCPE_BASE + 29) /* missing value */ #define SCPE_2FARG (SCPE_BASE + 30) /* too few arguments */ #define SCPE_2MARG (SCPE_BASE + 31) /* too many arguments */ #define SCPE_NXDEV (SCPE_BASE + 32) /* nx device */ #define SCPE_NXUN (SCPE_BASE + 33) /* nx unit */ #define SCPE_NXREG (SCPE_BASE + 34) /* nx register */ #define SCPE_NXPAR (SCPE_BASE + 35) /* nx parameter */ #define SCPE_NEST (SCPE_BASE + 36) /* nested DO */ #define SCPE_IERR (SCPE_BASE + 37) /* internal error */ #define SCPE_MTRLNT (SCPE_BASE + 38) /* tape rec lnt error */ #define SCPE_LOST (SCPE_BASE + 39) /* Telnet conn lost */ #define SCPE_TTMO (SCPE_BASE + 40) /* Telnet conn timeout */ #define SCPE_STALL (SCPE_BASE + 41) /* Telnet conn stall */ #define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */ #define SCPE_KFLAG 0010000 /* tti data flag */ #define SCPE_BREAK 0020000 /* tti break flag */ /* Print value format codes */ #define PV_RZRO 0 /* right, zero fill */ #define PV_RSPC 1 /* right, space fill */ #define PV_LEFT 2 /* left justify */ /* Default timing parameters */ #define KBD_POLL_WAIT 5000 /* keyboard poll */ #define KBD_MAX_WAIT 500000 #define SERIAL_IN_WAIT 100 /* serial in time */ #define SERIAL_OUT_WAIT 100 /* serial output */ #define NOQUEUE_WAIT 10000 /* min check time */ #define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x)) #define KBD_WAIT(w,s) ((w)? w: KBD_LIM_WAIT (s)) /* Convert switch letter to bit mask */ #define SWMASK(x) (1u << (((int) (x)) - ((int) 'A'))) /* String match */ #define MATCH_CMD(ptr,cmd) strncmp ((ptr), (cmd), strlen (ptr)) /* Device data structure */ struct sim_device { char *name; /* name */ struct sim_unit *units; /* units */ struct sim_reg *registers; /* registers */ struct sim_mtab *modifiers; /* modifiers */ uint32 numunits; /* #units */ uint32 aradix; /* address radix */ uint32 awidth; /* address width */ uint32 aincr; /* addr increment */ uint32 dradix; /* data radix */ uint32 dwidth; /* data width */ t_stat (*examine)(t_value *v, t_addr a, struct sim_unit *up, int32 sw); /* examine routine */ t_stat (*deposit)(t_value v, t_addr a, struct sim_unit *up, int32 sw); /* deposit routine */ t_stat (*reset)(struct sim_device *dp);/* reset routine */ t_stat (*boot)(int32 u, struct sim_device *dp); /* boot routine */ t_stat (*attach)(struct sim_unit *up, char *cp); /* attach routine */ t_stat (*detach)(struct sim_unit *up); /* detach routine */ void *ctxt; /* context */ uint32 flags; /* flags */ uint32 dctrl; /* debug control */ struct sim_debtab *debflags; /* debug flags */ t_stat (*msize)(struct sim_unit *up, int32 v, char *cp, void *dp); /* mem size routine */ char *lname; /* logical name */ }; /* Device flags */ #define DEV_V_DIS 0 /* dev disabled */ #define DEV_V_DISABLE 1 /* dev disable-able */ #define DEV_V_DYNM 2 /* mem size dynamic */ #define DEV_V_NET 3 /* network attach */ #define DEV_V_DEBUG 4 /* debug capability */ #define DEV_V_RAW 5 /* raw supported */ #define DEV_V_RAWONLY 6 /* only raw supported */ #define DEV_V_UF_31 12 /* user flags, V3.1 */ #define DEV_V_UF 16 /* user flags */ #define DEV_V_RSV 31 /* reserved */ #define DEV_DIS (1 << DEV_V_DIS) #define DEV_DISABLE (1 << DEV_V_DISABLE) #define DEV_DYNM (1 << DEV_V_DYNM) #define DEV_NET (1 << DEV_V_NET) #define DEV_DEBUG (1 << DEV_V_DEBUG) #define DEV_RAW (1 << DEV_V_RAW) #define DEV_RAWONLY (1 << DEV_V_RAWONLY) #define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1)) #define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1)) #define DEV_RFLAGS (DEV_UFMASK|DEV_DIS) /* restored flags */ /* Unit data structure Parts of the unit structure are device specific, that is, they are not referenced by the simulator control package and can be freely used by device simulators. Fields starting with 'buf', and flags starting with 'UF', are device specific. The definitions given here are for a typical sequential device. */ struct sim_unit { struct sim_unit *next; /* next active */ t_stat (*action)(struct sim_unit *up); /* action routine */ char *filename; /* open file name */ FILE *fileref; /* file reference */ void *filebuf; /* memory buffer */ uint32 hwmark; /* high water mark */ int32 time; /* time out */ uint32 flags; /* flags */ t_addr capac; /* capacity */ t_addr pos; /* file position */ int32 buf; /* buffer */ int32 wait; /* wait */ int32 u3; /* device specific */ int32 u4; /* device specific */ int32 u5; /* device specific */ int32 u6; /* device specific */ }; /* Unit flags */ #define UNIT_V_UF_31 12 /* dev spec, V3.1 */ #define UNIT_V_UF 16 /* device specific */ #define UNIT_V_RSV 31 /* reserved!! */ #define UNIT_ATTABLE 000001 /* attachable */ #define UNIT_RO 000002 /* read only */ #define UNIT_FIX 000004 /* fixed capacity */ #define UNIT_SEQ 000010 /* sequential */ #define UNIT_ATT 000020 /* attached */ #define UNIT_BINK 000040 /* K = power of 2 */ #define UNIT_BUFABLE 000100 /* bufferable */ #define UNIT_MUSTBUF 000200 /* must buffer */ #define UNIT_BUF 000400 /* buffered */ #define UNIT_ROABLE 001000 /* read only ok */ #define UNIT_DISABLE 002000 /* disable-able */ #define UNIT_DIS 004000 /* disabled */ #define UNIT_RAW 010000 /* raw mode */ #define UNIT_TEXT 020000 /* text mode */ #define UNIT_IDLE 040000 /* idle eligible */ #define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1)) #define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) #define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */ /* Register data structure */ struct sim_reg { char *name; /* name */ void *loc; /* location */ uint32 radix; /* radix */ uint32 width; /* width */ uint32 offset; /* starting bit */ uint32 depth; /* save depth */ uint32 flags; /* flags */ uint32 qptr; /* circ q ptr */ }; #define REG_FMT 00003 /* see PV_x */ #define REG_RO 00004 /* read only */ #define REG_HIDDEN 00010 /* hidden */ #define REG_NZ 00020 /* must be non-zero */ #define REG_UNIT 00040 /* in unit struct */ #define REG_CIRC 00100 /* circular array */ #define REG_VMIO 00200 /* use VM data print/parse */ #define REG_VMAD 00400 /* use VM addr print/parse */ #define REG_FIT 01000 /* fit access to size */ #define REG_HRO (REG_RO | REG_HIDDEN) /* hidden, read only */ /* Command tables, base and alternate formats */ struct sim_ctab { char *name; /* name */ t_stat (*action)(int32 flag, char *cptr); /* action routine */ int32 arg; /* argument */ char *help; /* help string */ }; struct sim_c1tab { char *name; /* name */ t_stat (*action)(struct sim_device *dptr, struct sim_unit *uptr, int32 flag, char *cptr); /* action routine */ int32 arg; /* argument */ char *help; /* help string */ }; struct sim_shtab { char *name; /* name */ t_stat (*action)(FILE *st, struct sim_device *dptr, struct sim_unit *uptr, int32 flag, char *cptr); int32 arg; /* argument */ char *help; /* help string */ }; /* Modifier table - only extended entries have disp, reg, or flags */ struct sim_mtab { uint32 mask; /* mask */ uint32 match; /* match */ char *pstring; /* print string */ char *mstring; /* match string */ t_stat (*valid)(struct sim_unit *up, int32 v, char *cp, void *dp); /* validation routine */ t_stat (*disp)(FILE *st, struct sim_unit *up, int32 v, void *dp); /* display routine */ void *desc; /* value descriptor */ /* REG * if MTAB_VAL */ /* int * if not */ }; #define MTAB_XTD (1u << UNIT_V_RSV) /* ext entry flag */ #define MTAB_VDV 001 /* valid for dev */ #define MTAB_VUN 002 /* valid for unit */ #define MTAB_VAL 004 /* takes a value */ #define MTAB_NMO 010 /* only if named */ #define MTAB_NC 020 /* no UC conversion */ #define MTAB_SHP 040 /* show takes parameter */ /* Search table */ struct sim_schtab { int32 logic; /* logical operator */ int32 boolop; /* boolean operator */ t_value mask; /* mask for logical */ t_value comp; /* comparison for boolean */ }; /* Breakpoint table */ struct sim_brktab { t_addr addr; /* address */ int32 typ; /* mask of types */ int32 cnt; /* proceed count */ char *act; /* action string */ }; /* Debug table */ struct sim_debtab { char *name; /* control name */ uint32 mask; /* control bit */ }; #define DEBUG_PRS(d) (sim_deb && d.dctrl) #define DEBUG_PRD(d) (sim_deb && d->dctrl) #define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) #define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) /* The following macros define structure contents */ #define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),(cap),0,0 #if defined (__STDC__) || defined (_WIN32) #define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1 #define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1 #define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1 #define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1 #define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1 #define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep) #define URDATA(nm,loc,rdx,wd,off,dep,fl) \ #nm, &(loc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) #else #define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1 #define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1 #define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1 #define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1 #define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1 #define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep) #define URDATA(nm,loc,rdx,wd,off,dep,fl) \ "nm", &(loc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) #endif /* Typedefs for principal structures */ typedef struct sim_device DEVICE; typedef struct sim_unit UNIT; typedef struct sim_reg REG; typedef struct sim_ctab CTAB; typedef struct sim_c1tab C1TAB; typedef struct sim_shtab SHTAB; typedef struct sim_mtab MTAB; typedef struct sim_schtab SCHTAB; typedef struct sim_brktab BRKTAB; typedef struct sim_debtab DEBTAB; /* Function prototypes */ #include "scp.h" #include "sim_console.h" #include "sim_timer.h" #include "sim_fio.h" #endif simh-3.8.1/sim_fio.c0000644000175000017500000002277011111337744012444 0ustar vlmvlm/* sim_fio.c: simulator file I/O library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica) 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) 15-May-06 RMS Added sim_fsize_name 21-Apr-06 RMS Added FreeBSD large file support (from Mark Martinec) 19-Nov-05 RMS Added OS/X large file support (from Peter Schorn) 16-Aug-05 RMS Fixed C++ declaration and cast problems 17-Jul-04 RMS Fixed bug in optimized sim_fread (reported by Scott Bailey) 26-May-04 RMS Optimized sim_fread (suggested by John Dundas) 02-Jan-04 RMS Split out from SCP This library includes: sim_finit - initialize package sim_fopen - open file sim_fread - endian independent read (formerly fxread) sim_write - endian independent write (formerly fxwrite) sim_fseek - extended (>32b) seek (formerly fseek_ext) sim_fsize - get file size sim_fopen and sim_fseek are OS-dependent. The other routines are not. sim_fsize is always a 32b routine (it is used only with small capacity random access devices like fixed head disks and DECtapes). */ #include "sim_defs.h" static unsigned char sim_flip[FLIP_SIZE]; int32 sim_end = 1; /* 1 = little */ /* OS-independent, endian independent binary I/O package For consistency, all binary data read and written by the simulator is stored in little endian data order. That is, in a multi-byte data item, the bytes are written out right to left, low order byte to high order byte. On a big endian host, data is read and written from high byte to low byte. Consequently, data written on a little endian system must be byte reversed to be usable on a big endian system, and vice versa. These routines are analogs of the standard C runtime routines fread and fwrite. If the host is little endian, or the data items are size char, then the calls are passed directly to fread or fwrite. Otherwise, these routines perform the necessary byte swaps. Sim_fread swaps in place, sim_fwrite uses an intermediate buffer. */ int32 sim_finit (void) { union {int32 i; char c[sizeof (int32)]; } end_test; end_test.i = 1; /* test endian-ness */ sim_end = end_test.c[0]; return sim_end; } size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr) { size_t c, j; int32 k; unsigned char by, *sptr, *dptr; if ((size == 0) || (count == 0)) /* check arguments */ return 0; c = fread (bptr, size, count, fptr); /* read buffer */ if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ return c; /* done */ for (j = 0, dptr = sptr = (unsigned char *) bptr; j < c; j++) { /* loop on items */ for (k = size - 1; k >= (((int32) size + 1) / 2); k--) { by = *sptr; /* swap end-for-end */ *sptr++ = *(dptr + k); *(dptr + k) = by; } sptr = dptr = dptr + size; /* next item */ } return c; } size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr) { size_t c, j, nelem, nbuf, lcnt, total; int32 i, k; unsigned char *sptr, *dptr; if ((size == 0) || (count == 0)) /* check arguments */ return 0; if (sim_end || (size == sizeof (char))) /* le or byte? */ return fwrite (bptr, size, count, fptr); /* done */ nelem = FLIP_SIZE / size; /* elements in buffer */ nbuf = count / nelem; /* number buffers */ lcnt = count % nelem; /* count in last buf */ if (lcnt) nbuf = nbuf + 1; else lcnt = nelem; total = 0; sptr = (unsigned char *) bptr; /* init input ptr */ for (i = nbuf; i > 0; i--) { /* loop on buffers */ c = (i == 1)? lcnt: nelem; for (j = 0, dptr = sim_flip; j < c; j++) { /* loop on items */ for (k = size - 1; k >= 0; k--) *(dptr + k) = *sptr++; dptr = dptr + size; } c = fwrite (sim_flip, size, c, fptr); if (c == 0) return total; total = total + c; } return total; } /* Get file size */ uint32 sim_fsize_name (char *fname) { FILE *fp; uint32 sz; if ((fp = sim_fopen (fname, "rb")) == NULL) return 0; sz = sim_fsize (fp); fclose (fp); return sz; } uint32 sim_fsize (FILE *fp) { uint32 pos, sz; if (fp == NULL) return 0; pos = ftell (fp); fseek (fp, 0, SEEK_END); sz = ftell (fp); fseek (fp, pos, SEEK_SET); return sz; } /* OS-dependent routines */ /* Optimized file open */ FILE *sim_fopen (const char *file, const char *mode) { #if defined (VMS) return fopen (file, mode, "ALQ=32", "DEQ=4096", "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm"); #elif defined (USE_INT64) && defined (USE_ADDR64) && defined (__linux) return fopen64 (file, mode); #else return fopen (file, mode); #endif } /* Long seek */ #if defined (USE_INT64) && defined (USE_ADDR64) /* 64b VMS */ #if (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ #define _SIM_IO_FSEEK_EXT_ 1 static t_int64 fpos_t_to_int64 (fpos_t *pos) { unsigned short *w = (unsigned short *) pos; /* endian dep! */ t_int64 result; result = w[1]; result <<= 16; result += w[0]; result <<= 9; result += w[2]; return result; } static void int64_to_fpos_t (t_int64 ipos, fpos_t *pos, size_t mbc) { unsigned short *w = (unsigned short *) pos; int bufsize = mbc << 9; w[3] = 0; w[2] = (unsigned short) (ipos % bufsize); ipos -= w[2]; ipos >>= 9; w[0] = (unsigned short) ipos; ipos >>= 16; w[1] = (unsigned short) ipos; if ((w[2] == 0) && (w[0] || w[1])) { w[2] = bufsize; w[0] -= mbc; } return; } int sim_fseek (FILE *st, t_addr offset, int whence) { t_addr fileaddr; fpos_t filepos; switch (whence) { case SEEK_SET: fileaddr = offset; break; case SEEK_CUR: if (fgetpos (st, &filepos)) return (-1); fileaddr = fpos_t_to_int64 (&filepos); fileaddr = fileaddr + offset; break; default: errno = EINVAL; return (-1); } int64_to_fpos_t (fileaddr, &filepos, 127); return fsetpos (st, &filepos); } #endif /* Alpha UNIX - natively 64b */ #if defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ #define _SIM_IO_FSEEK_EXT_ 1 int sim_fseek (FILE *st, t_addr offset, int whence) { return fseek (st, offset, whence); } #endif /* Windows */ #if defined (_WIN32) #define _SIM_IO_FSEEK_EXT_ 1 int sim_fseek (FILE *st, t_addr offset, int whence) { fpos_t fileaddr; switch (whence) { case SEEK_SET: fileaddr = offset; break; case SEEK_CUR: if (fgetpos (st, &fileaddr)) return (-1); fileaddr = fileaddr + offset; break; default: errno = EINVAL; return (-1); } return fsetpos (st, &fileaddr); } #endif /* end Windows */ /* Linux */ #if defined (__linux) #define _SIM_IO_FSEEK_EXT_ 1 int sim_fseek (FILE *st, t_addr xpos, int origin) { return fseeko64 (st, xpos, origin); } #endif /* end Linux with LFS */ /* Apple OS/X */ #if defined (__APPLE__) || defined (__FreeBSD__) #define _SIM_IO_FSEEK_EXT_ 1 int sim_fseek (FILE *st, t_addr xpos, int origin) { return fseeko (st, xpos, origin); } #endif /* end Apple OS/X */ #endif /* end 64b seek defs */ /* Default: no OS-specific routine has been defined */ #if !defined (_SIM_IO_FSEEK_EXT_) #define _SIM_IO_FSEEK_EXT_ 0 int sim_fseek (FILE *st, t_addr xpos, int origin) { return fseek (st, (int32) xpos, origin); } #endif uint32 sim_taddr_64 = _SIM_IO_FSEEK_EXT_; simh-3.8.1/scp.h0000644000175000017500000001252411111655430011600 0ustar vlmvlm/* scp.h: simulator control program headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 09-Aug-06 JDB Added assign_device and deassign_device 14-Jul-06 RMS Added sim_activate_abs 06-Jan-06 RMS Added fprint_stopped_gen Changed arg type in sim_brk_test 07-Feb-05 RMS Added ASSERT command 09-Sep-04 RMS Added reset_all_p 14-Feb-04 RMS Added debug prototypes (from Dave Hittner) 02-Jan-04 RMS Split out from SCP */ #ifndef _SIM_SCP_H_ #define _SIM_SCP_H_ 0 /* run_cmd parameters */ #define RU_RUN 0 /* run */ #define RU_GO 1 /* go */ #define RU_STEP 2 /* step */ #define RU_CONT 3 /* continue */ #define RU_BOOT 4 /* boot */ /* get_sim_opt parameters */ #define CMD_OPT_SW 001 /* switches */ #define CMD_OPT_OF 002 /* output file */ #define CMD_OPT_SCH 004 /* search */ #define CMD_OPT_DFT 010 /* defaults */ /* Command processors */ t_stat reset_cmd (int32 flag, char *ptr); t_stat exdep_cmd (int32 flag, char *ptr); t_stat eval_cmd (int32 flag, char *ptr); t_stat load_cmd (int32 flag, char *ptr); t_stat run_cmd (int32 flag, char *ptr); t_stat attach_cmd (int32 flag, char *ptr); t_stat detach_cmd (int32 flag, char *ptr); t_stat assign_cmd (int32 flag, char *ptr); t_stat deassign_cmd (int32 flag, char *ptr); t_stat save_cmd (int32 flag, char *ptr); t_stat restore_cmd (int32 flag, char *ptr); t_stat exit_cmd (int32 flag, char *ptr); t_stat set_cmd (int32 flag, char *ptr); t_stat show_cmd (int32 flag, char *ptr); t_stat brk_cmd (int32 flag, char *ptr); t_stat do_cmd (int32 flag, char *ptr); t_stat assert_cmd (int32 flag, char *ptr); t_stat help_cmd (int32 flag, char *ptr); t_stat spawn_cmd (int32 flag, char *ptr); t_stat echo_cmd (int32 flag, char *ptr); /* Utility routines */ t_stat sim_process_event (void); t_stat sim_activate (UNIT *uptr, int32 interval); t_stat sim_activate_abs (UNIT *uptr, int32 interval); t_stat sim_cancel (UNIT *uptr); int32 sim_is_active (UNIT *uptr); double sim_gtime (void); uint32 sim_grtime (void); int32 sim_qcount (void); t_stat attach_unit (UNIT *uptr, char *cptr); t_stat detach_unit (UNIT *uptr); t_stat assign_device (DEVICE *dptr, char *cptr); t_stat deassign_device (DEVICE *dptr); t_stat reset_all (uint32 start_device); t_stat reset_all_p (uint32 start_device); char *sim_dname (DEVICE *dptr); t_stat get_yn (char *ques, t_stat deflt); char *get_sim_opt (int32 opt, char *cptr, t_stat *st); char *get_glyph (char *iptr, char *optr, char mchar); char *get_glyph_nc (char *iptr, char *optr, char mchar); t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status); char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, uint32 rdx, t_addr max, char term); t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp); t_value strtotv (char *cptr, char **endptr, uint32 radix); t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt); CTAB *find_cmd (char *gbuf); DEVICE *find_dev (char *ptr); DEVICE *find_unit (char *ptr, UNIT **uptr); DEVICE *find_dev_from_unit (UNIT *uptr); REG *find_reg (char *ptr, char **optr, DEVICE *dptr); CTAB *find_ctab (CTAB *tab, char *gbuf); C1TAB *find_c1tab (C1TAB *tab, char *gbuf); SHTAB *find_shtab (SHTAB *tab, char *gbuf); BRKTAB *sim_brk_fnd (t_addr loc); uint32 sim_brk_test (t_addr bloc, uint32 btyp); void sim_brk_clrspc (uint32 spc); char *match_ext (char *fnam, char *ext); t_stat sim_cancel_step (void); void sim_debug_u16 (uint32 dbits, DEVICE* dptr, const char* const* bitdefs, uint16 before, uint16 after, int terminate); void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr); #endif simh-3.8.1/sim_sock.c0000644000175000017500000002432511111340114012605 0ustar vlmvlm/* sim_sock.c: OS-dependent socket routines Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt) 16-Aug-05 RMS Fixed spurious SIGPIPE signal error in Unix 14-Apr-05 RMS Added WSAEINPROGRESS test (from Tim Riker) 09-Jan-04 RMS Fixed typing problem in Alpha Unix (found by Tim Chapman) 17-Apr-03 RMS Fixed non-implemented version of sim_close_sock (found by Mark Pizzolato) 17-Dec-02 RMS Added sim_connect_socket, sim_create_socket 08-Oct-02 RMS Revised for .NET compatibility 22-Aug-02 RMS Changed calling sequence for sim_accept_conn 22-May-02 RMS Added OS2 EMX support from Holger Veit 06-Feb-02 RMS Added VMS support from Robert Alan Byer 16-Sep-01 RMS Added Macintosh support from Peter Schorn 02-Sep-01 RMS Fixed UNIX bugs found by Mirian Lennox and Tom Markson */ #include "sim_defs.h" #include "sim_sock.h" #include /* OS dependent routines sim_master_sock create master socket sim_accept_conn accept connection sim_read_sock read from socket sim_write_sock write from socket sim_close_sock close socket sim_setnonblock set socket non-blocking sim_msg_sock send message to socket */ int32 sim_sock_cnt = 0; /* First, all the non-implemented versions */ #if defined (__OS2__) && !defined (__EMX__) SOCKET sim_master_sock (int32 port) { return INVALID_SOCKET; } SOCKET sim_connect_sock (int32 ip, int32 port) { return INVALID_SOCKET; } SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) { return INVALID_SOCKET; } int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) { return -1; } int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes) { return 0; } void sim_close_sock (SOCKET sock, t_bool master) { return; } SOCKET sim_setnonblock (SOCKET sock) { return SOCKET_ERROR; } #else /* endif unimpl */ /* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */ SOCKET sim_err_sock (SOCKET s, char *emsg, int32 flg) { int32 err = WSAGetLastError (); printf ("Sockets: %s error %d\n", emsg, err); sim_close_sock (s, flg); return INVALID_SOCKET; } SOCKET sim_create_sock (void) { SOCKET newsock; int32 err; #if defined (_WIN32) WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD (1, 1); if (sim_sock_cnt == 0) { err = WSAStartup (wVersionRequested, &wsaData); /* start Winsock */ if (err != 0) { printf ("Winsock: startup error %d\n", err); return INVALID_SOCKET; } } sim_sock_cnt = sim_sock_cnt + 1; #endif /* endif Win32 */ #if defined (SIGPIPE) signal (SIGPIPE, SIG_IGN); /* no pipe signals */ #endif newsock = socket (AF_INET, SOCK_STREAM, 0); /* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ err = WSAGetLastError (); printf ("Sockets: socket error %d\n", err); return INVALID_SOCKET; } return newsock; } SOCKET sim_master_sock (int32 port) { SOCKET newsock; struct sockaddr_in name; int32 sta; newsock = sim_create_sock (); /* create socket */ if (newsock == INVALID_SOCKET) /* socket error? */ return newsock; name.sin_family = AF_INET; /* name socket */ name.sin_port = htons ((unsigned short) port); /* insert port */ name.sin_addr.s_addr = htonl (INADDR_ANY); /* insert addr */ sta = bind (newsock, (struct sockaddr *) &name, sizeof (name)); if (sta == SOCKET_ERROR) /* bind error? */ return sim_err_sock (newsock, "bind", 1); sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ return sim_err_sock (newsock, "fcntl", 1); sta = listen (newsock, 1); /* listen on socket */ if (sta == SOCKET_ERROR) /* listen error? */ return sim_err_sock (newsock, "listen", 1); return newsock; /* got it! */ } SOCKET sim_connect_sock (int32 ip, int32 port) { SOCKET newsock; struct sockaddr_in name; int32 sta; newsock = sim_create_sock (); /* create socket */ if (newsock == INVALID_SOCKET) /* socket error? */ return newsock; name.sin_family = AF_INET; /* name socket */ name.sin_port = htons ((unsigned short) port); /* insert port */ name.sin_addr.s_addr = htonl (ip); /* insert addr */ sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ return sim_err_sock (newsock, "fcntl", 1); sta = connect (newsock, (struct sockaddr *) &name, sizeof (name)); if ((sta == SOCKET_ERROR) && (WSAGetLastError () != WSAEWOULDBLOCK) && (WSAGetLastError () != WSAEINPROGRESS)) return sim_err_sock (newsock, "connect", 1); return newsock; /* got it! */ } SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) { int32 sta, err; #if defined (macintosh) || defined (__linux) || \ defined (__APPLE__) || defined (__OpenBSD__) socklen_t size; #elif defined (_WIN32) || defined (__EMX__) ||\ (defined (__ALPHA) && defined (__unix__)) int size; #else size_t size; #endif SOCKET newsock; struct sockaddr_in clientname; if (master == 0) /* not attached? */ return INVALID_SOCKET; size = sizeof (clientname); newsock = accept (master, (struct sockaddr *) &clientname, &size); if (newsock == INVALID_SOCKET) { /* error? */ err = WSAGetLastError (); if (err != WSAEWOULDBLOCK) printf ("Sockets: accept error %d\n", err); return INVALID_SOCKET; } if (ipaddr != NULL) *ipaddr = ntohl (clientname.sin_addr.s_addr); sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ return sim_err_sock (newsock, "fcntl", 0); return newsock; } int32 sim_check_conn (SOCKET sock, t_bool rd) { fd_set rw_set, er_set; fd_set *rw_p = &rw_set; fd_set *er_p = &er_set; struct timeval tz; timerclear (&tz); FD_ZERO (rw_p); FD_ZERO (er_p); FD_SET (sock, rw_p); FD_SET (sock, er_p); if (rd) select ((int) sock + 1, rw_p, NULL, er_p, &tz); else select ((int) sock + 1, NULL, rw_p, er_p, &tz); if (FD_ISSET (sock, rw_p)) return 1; if (FD_ISSET (sock, er_p)) return -1; return 0; } int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) { int32 rbytes, err; rbytes = recv (sock, buf, nbytes, 0); if (rbytes == 0) /* disconnect */ return -1; if (rbytes == SOCKET_ERROR) { err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) /* no data */ return 0; printf ("Sockets: read error %d\n", err); return -1; } return rbytes; } int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes) { return send (sock, msg, nbytes, 0); } void sim_close_sock (SOCKET sock, t_bool master) { #if defined (_WIN32) closesocket (sock); if (master) { sim_sock_cnt = sim_sock_cnt - 1; if (sim_sock_cnt <= 0) { WSACleanup (); sim_sock_cnt = 0; } } #else close (sock); #endif return; } #if defined (_WIN32) /* Windows */ SOCKET sim_setnonblock (SOCKET sock) { unsigned long non_block = 1; return ioctlsocket (sock, FIONBIO, &non_block); /* set nonblocking */ } #elif defined (VMS) /* VMS */ SOCKET sim_setnonblock (SOCKET sock) { int non_block = 1; return ioctl (sock, FIONBIO, &non_block); /* set nonblocking */ } #else /* Mac, Unix, OS/2 */ int32 sim_setnonblock (SOCKET sock) { int32 fl, sta; fl = fcntl (sock, F_GETFL,0); /* get flags */ if (fl == -1) return SOCKET_ERROR; sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK); /* set nonblock */ if (sta == -1) return SOCKET_ERROR; #if !defined (macintosh) && !defined (__EMX__) /* Unix only */ sta = fcntl (sock, F_SETOWN, getpid()); /* set ownership */ if (sta == -1) return SOCKET_ERROR; #endif return 0; } #endif /* endif !Win32 && !VMS */ #endif /* end else !implemented */ simh-3.8.1/PDP1/0000755000175000017500000000000011112107064011336 5ustar vlmvlmsimh-3.8.1/PDP1/pdp1_defs.h0000644000175000017500000002001311111663032013351 0ustar vlmvlm/* pdp1_defs.h: 18b PDP simulator definitions Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Dec-06 RMS Added 16-channel sequence break support 22-Jul-05 RMS Fixed definition of CPLS_DPY 08-Feb-04 PLB Added support for display 08-Dec-03 RMS Added support for parallel drum 18-Oct-03 RMS Added DECtape off reel message 22-Jul-03 RMS Updated for "hardware" RIM loader Revised to detect I/O wait hang 05-Dec-02 RMS Added IOT skip support (required by drum) 14-Apr-99 RMS Changed t_addr to unsigned The PDP-1 was Digital's first computer. The system design evolved during its life, and as a result, specifications are sketchy or contradictory. This simulator is based on the 1962 maintenance manual. This simulator implements the following options: Automatic multiply/divide Type 10 Memory extension control Type 15 Parallel drum Type 23 Serial drum Type 24 Graphic display Type 30 Line printer control Type 62 Microtape (DECtape) control Type 550 */ #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 */ #define STOP_XCT 4 /* nested XCT's */ #define STOP_IND 5 /* nested indirects */ #define STOP_WAIT 6 /* IO wait hang */ #define STOP_DTOFF 7 /* DECtape off reel */ #define ERR_RMV 10 /* restrict mode viol */ /* 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 DAMASK 0007777 /* direct addr */ #define EPCMASK (AMASK & ~DAMASK) /* extended addr */ #define IA 0010000 /* indirect flag */ #define IO_WAIT 0010000 /* I/O sync wait */ #define IO_CPLS 0004000 /* completion pulse */ #define OP_DAC 0240000 /* DAC */ #define OP_DIO 0320000 /* DIO */ #define OP_JMP 0600000 /* JMP */ #define GEN_CPLS(x) (((x) ^ ((x) << 1)) & IO_WAIT) /* completion pulse? */ /* Program flags/sense switches */ #define PF_V_L 7 #define PF_V_RNG 6 #define PF_L (1u << PF_V_L) #define PF_RNG (1u << PF_V_RNG) #define PF_SS_1 0040 #define PF_SS_2 0020 #define PF_SS_3 0010 #define PF_SS_4 0004 #define PF_SS_5 0002 #define PF_SS_6 0001 #define PF_VR_ALL 0377 #define PF_SS_ALL 0077 /* Restict mode */ #define RTB_IOT 0400000 #define RTB_ILL 0200000 #define RTB_HLT 0100000 #define RTB_DBK 0040000 #define RTB_CHR 0020000 #define RTB_MB_MASK 0017777 #define RM45_V_BNK 14 #define RM45_M_BNK 003 #define RM48_V_BNK 12 #define RM48_M_BNK 017 #define RN45_SIZE 4 /* IOT subroutine return codes */ #define IOT_V_SKP 18 /* skip */ #define IOT_SKP (1 << IOT_V_SKP) #define IOT_V_REASON (IOT_V_SKP + 1) /* reason */ #define IOT_REASON (1 << IOT_V_REASON) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* I/O status flags */ #define IOS_V_LPN 17 /* light pen */ #define IOS_V_PTR 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_PTR (1 << IOS_V_PTR) #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) /* Completion pulses */ #define CPLS_V_PTR 5 #define CPLS_V_PTP 4 #define CPLS_V_TTO 3 #define CPLS_V_LPT 2 #define CPLS_V_DPY 1 #define CPLS_PTR (1 << CPLS_V_PTR) #define CPLS_PTP (1 << CPLS_V_PTP) #define CPLS_TTO (1 << CPLS_V_TTO) #define CPLS_LPT (1 << CPLS_V_LPT) #define CPLS_DPY (1 << CPLS_V_DPY) /* One channel sequence break */ #define SB_V_IP 0 /* in progress */ #define SB_V_RQ 1 /* request */ #define SB_V_ON 2 /* enabled */ #define SB_IP (1 << SB_V_IP) #define SB_RQ (1 << SB_V_RQ) #define SB_ON (1 << SB_V_ON) /* 16 channel sequence break */ #define SBS_LVLS 16 /* num levels */ #define SBS_LVL_MASK (SBS_LVLS - 1) #define SBS_LVL_RMV 14 /* restrict level */ #define SBS_MASK(x) (1u << (SBS_LVLS - 1 - (x))) /* level to mask */ /* Timers */ #define TMR_CLK 0 /* Device routines */ t_stat dev_req_int (int32 lvl); t_stat dev_set_sbs (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dev_show_sbs (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/PDP1/pdp1_lp.c0000644000175000017500000002110011112107064013033 0ustar vlmvlm/* pdp1_lp.c: PDP-1 line printer simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be bused in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt Type 62 line printer for the PDP-1 19-Jan-07 RMS Added UNIT_TEXT flag 21-Dec-06 RMS Added 16-channel SBS support 07-Sep-03 RMS Changed ioc to ios 23-Jul-03 RMS Fixed bugs in instruction decoding, overprinting Revised to detect I/O wait hang 25-Apr-03 RMS Revised for extended file support 30-May-02 RMS Widened POS to 32b 13-Apr-01 RMS Revised for register arrays */ #include "pdp1_defs.h" #define BPTR_MAX 40 /* pointer max */ #define LPT_BSIZE (BPTR_MAX * 3) /* line size */ #define BPTR_MASK 077 /* buf ptr mask */ int32 lpt_spc = 0; /* print (0) vs spc */ int32 lpt_ovrpr = 0; /* overprint */ int32 lpt_stopioe = 0; /* stop on error */ int32 lpt_bptr = 0; /* buffer ptr */ int32 lpt_sbs = 0; /* SBS level */ char lpt_buf[LPT_BSIZE + 1] = { 0 }; static const unsigned char lpt_trans[64] = { ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', '@','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' }; extern int32 ios, cpls, iosta; extern int32 stop_inst; t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit lpt_reg LPT register list */ UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lpt_reg[] = { { ORDATA (BUF, lpt_unit.buf, 8) }, { FLDATA (PNT, iosta, IOS_V_PNT) }, { FLDATA (SPC, iosta, IOS_V_SPC) }, { FLDATA (RPLS, cpls, CPLS_V_LPT) }, { DRDATA (BPTR, lpt_bptr, 6) }, { ORDATA (LPT_STATE, lpt_spc, 6), REG_HRO }, { FLDATA (LPT_OVRPR, lpt_ovrpr, 0), REG_HRO }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, { DRDATA (SBSLVL, lpt_sbs, 4), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &lpt_sbs }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lpt_reset, NULL, NULL, NULL, NULL, DEV_DISABLE }; /* Line printer IOT routine */ int32 lpt (int32 inst, int32 dev, int32 dat) { int32 i; if (lpt_dev.flags & DEV_DIS) /* disabled? */ return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ if ((inst & 07000) == 01000) { /* fill buf */ if (lpt_bptr < BPTR_MAX) { /* limit test ptr */ i = lpt_bptr * 3; /* cvt to chr ptr */ lpt_buf[i] = lpt_trans[(dat >> 12) & 077]; lpt_buf[i + 1] = lpt_trans[(dat >> 6) & 077]; lpt_buf[i + 2] = lpt_trans[dat & 077]; } lpt_bptr = (lpt_bptr + 1) & BPTR_MASK; return dat; } if ((inst & 07000) == 02000) { /* space */ iosta = iosta & ~IOS_SPC; /* space, clear flag */ lpt_spc = (inst >> 6) & 077; /* state = space n */ } else if ((inst & 07000) == 00000) { /* print */ iosta = iosta & ~IOS_PNT; /* clear flag */ lpt_spc = 0; /* state = print */ } else return (stop_inst << IOT_V_REASON) | dat; /* not implemented */ if (GEN_CPLS (inst)) { /* comp pulse? */ ios = 0; /* clear flop */ cpls = cpls | CPLS_LPT; /* request completion */ } else cpls = cpls & ~CPLS_LPT; sim_activate (&lpt_unit, lpt_unit.wait); /* activate */ return dat; } /* Unit service, printer is in one of three states lpt_spc = 000 write buffer to file, set overprint lpt_iot = 02x space command x, clear overprint */ t_stat lpt_svc (UNIT *uptr) { int32 i; static const char *lpt_cc[] = { "\n", "\n\n", "\n\n\n", "\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\f" }; if (cpls & CPLS_LPT) { /* completion pulse? */ ios = 1; /* restart */ cpls = cpls & ~CPLS_LPT; /* clr pulse pending */ } dev_req_int (lpt_sbs); /* req interrupt */ if (lpt_spc) { /* space? */ iosta = iosta | IOS_SPC; /* set flag */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); fputs (lpt_cc[lpt_spc & 07], uptr->fileref); /* print cctl */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lpt_ovrpr = 0; /* dont overprint */ } else { iosta = iosta | IOS_PNT; /* print */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lpt_stopioe, SCPE_UNATT); if (lpt_ovrpr) /* overprint? */ fputc ('\r', uptr->fileref); fputs (lpt_buf, uptr->fileref); /* print buffer */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* test error */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lpt_bptr = 0; for (i = 0; i <= LPT_BSIZE; i++) /* clear buffer */ lpt_buf[i] = 0; lpt_ovrpr = 1; /* set overprint */ } return SCPE_OK; } /* Reset routine */ t_stat lpt_reset (DEVICE *dptr) { int32 i; lpt_bptr = 0; /* clear buffer ptr */ for (i = 0; i <= LPT_BSIZE; i++) /* clear buffer */ lpt_buf[i] = 0; lpt_spc = 0; /* clear state */ lpt_ovrpr = 0; /* clear overprint */ cpls = cpls & ~CPLS_LPT; iosta = iosta & ~(IOS_PNT | IOS_SPC); /* clear flags */ sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } simh-3.8.1/PDP1/pdp1_diag.txt0000644000175000017500000000170510450563770013750 0ustar vlmvlmMAINDEC 1 Instruction Test sim> att -e ptr digital-1-12-m-rim.bin sim> boot ptr HALT instruction, PC: 000002 (JMP 3000) sim> c (Test runs until interrupted unless there are errors) ^E Simulation stopped, PC: 000007 (SZS2 I) sim> d ss 77 sim> run 32 (Test runs until interrupted unless there are errors) ^E Simulation stopped, PC: 000032 (SZS1) sim> d tw 777777 sim> d ss 0 sim> run 7772 Undefined instruction, PC: 000001 (SZS1) sim> ex ac,io,pf AC: 000777 IO: 777000 PF: 77 (These are the expected final values for the diagnostic) MAINDEC 4 Multiply/Divide Test sim> att -e ptr maindec-4_4-16-68.bin sim> break 0 sim> boot ptr Breakpoint, PC: 000000 (JMP 532) sim> set cpu mdv sim> c (Test runs until interrupted unless there are errors) ^E Simulation stopped, PC: ... sim> set cpu nomdv sim> run 1 (Test runs until interrupted unless there are errors) ^E Simulation stopped, PC: ... simh-3.8.1/PDP1/pdp1_cpu.c0000644000175000017500000021173511111663342013233 0ustar vlmvlm/* pdp1_cpu.c: PDP-1 CPU simulator Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu PDP-1 central processor 30-May-07 RMS Fixed typo in SBS clear (from Norm Lastovica) 28-Dec-06 RMS Added 16-channel SBS support, PDP-1D support 28-Jun-06 RMS Fixed bugs in MUS and DIV 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 09-Nov-04 RMS Added instruction history 07-Sep-03 RMS Added additional explanation on I/O simulation 01-Sep-03 RMS Added address switches for hardware readin 23-Jul-03 RMS Revised to detect I/O wait hang 05-Dec-02 RMS Added drum support 06-Oct-02 RMS Revised for V2.10 20-Aug-02 RMS Added DECtape support 30-Dec-01 RMS Added old PC queue 07-Dec-01 RMS Revised to use breakpoint package 30-Nov-01 RMS Added extended SET/SHOW support 16-Dec-00 RMS Fixed bug in XCT address calculation 14-Apr-99 RMS Changed t_addr to unsigned The PDP-1 was Digital's first computer. Although Digital built four other 18b computers, the later systems (the PDP-4, PDP-7, PDP-9, and PDP-15) were similar to each other and quite different from the PDP-1. Accordingly, the PDP-1 requires a distinct simulator. The register state for the PDP-1 is: AC<0:17> accumulator IO<0:17> IO register OV overflow flag PC<0:15> program counter IOSTA I/O status register SBS<0:2> sequence break flip flops IOH I/O halt flip flop IOS I/O synchronizer (completion) flip flop EXTM extend mode PF<1:6> program flags SS<1:6> sense switches TW<0:17> test word (switch register) The 16-channel sequence break system adds additional state: sbs_req<0:15> interrupt requests sbs_enb<0:15> enabled levels sbs_act<0:15> active levels The PDP-1D adds additional state: L link (SN 45 only) RNG ring mode RM restrict mode RMASK restrict mode mask RNAME rename table (SN 45 only) RTB restict mode trap buffer (SN 45 only) Questions: cks: which bits are line printer print done and space done? cks: is there a bit for sequence break enabled (yes, according to the 1963 Handbook) sbs: do sequence breaks accumulate while the system is disabled (yes, according to the Maintenance Manual) The PDP-1 has seven instruction formats: memory reference, skips, shifts, load immediate, I/O transfer, operate, and (PDP-1D) special. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op |in| address | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:4> <5> mnemonic action 00 02 AND AC = AC & M[MA] 04 IOR AC = AC | M[MA] 06 XOR AC = AC ^ M[MA] 10 XCT M[MA] is executed as an instruction 12 LCH load character (PDP-1D) 14 DCH store character (PDP-1D) 16 0 CAL M[100] = AC, AC = PC, PC = 101 16 1 JDA M[MA] = AC, AC = PC, PC = MA + 1 20 LAC AC = M[MA] 22 LIO IO = M[MA] 24 DAC M[MA] = AC 26 DAP M[MA]<6:17> = AC<6:17> 30 DIP M[MA]<0:5> = AC<0:5> 32 DIO M[MA] = IO 34 DZM M[MA] = 0 36 TAD L'AC = AC + M[MA] + L 40 ADD AC = AC + M[MA] 42 SUB AC = AC - M[MA] 44 IDX AC = M[MA] = M[MA] + 1 46 ISP AC = M[MA] = M[MA] + 1, skip if AC >= 0 50 SAD skip if AC != M[MA] 52 SAS skip if AC == M[MA] 54 MUL AC'IO = AC * M[MA] 56 DIV AC, IO = AC'IO / M[MA] 60 JMP PC = MA 62 JSP AC = PC, PC = MA Memory reference instructions can access an address space of 64K words. The address space is divided into sixteen 4K word fields. An instruction can directly address, via its 12b address, the entire current field. If extend mode is off, indirect addresses access the current field, and indirect addressing is multi-level; if off, they can access all 64K, and indirect addressing is single level. The skip format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 0 1 0| | | | | | | | | | | | | | skip +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | \______/ \______/ | | | | | | | | | | | | | | | | | +---- program flags | | | | | | | +------------- sense switches | | | | | | +------------------- AC == 0 | | | | | +---------------------- AC >= 0 | | | | +------------------------- AC < 0 | | | +---------------------------- OV == 0 | | +------------------------------- IO >= 0 | +---------------------------------- IO != 0 (PDP-1D) +------------------------------------- invert skip The shift format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 0 1 1| subopcode | encoded count | shift +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The load immediate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 0| S| immediate | LAW +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:4> mnemonic action 70 LAW if S = 0, AC = IR<6:17> else AC = ~IR<6:17> The I/O transfer format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 1| W| C| subopcode | device | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The IO transfer instruction sends the the specified subopcode to specified I/O device. The I/O device may take data from the IO or return data to the IO, initiate or cancel operations, etc. The W bit specifies whether the CPU waits for completion, the C bit whether a completion pulse will be returned from the device. The special operate format (PDP-1D) is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 1 0| | | | | | | | | | | | | | special +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | | | | | | | +------- CML (3) | | | | | | | | | +---------- CLL (1) | | | | | | | | +------------- SZL (1) | | | | | | | +---------------- SCF (1) | | | | | | +------------------- SCI (1) | | | | | +---------------------- SCM (2) | | | | +------------------------- IDA (3) | | | +---------------------------- IDC (4) | | +------------------------------- IFI (2) | +---------------------------------- IIF (2) +------------------------------------- reverse skip The special operate instruction can be microprogrammed. The standard operate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 1 1| | | | | | | | | | | | | | operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | \______/ | | | | | | | | | | | | | | | | | | | | | +---- PF select | | | | | | | | | +---------- clear/set PF | | | | | | | | +------------- LIA (PDP-1D) | | | | | | | +---------------- LAI (PDP-1D) | | | | | | +------------------- or PC | | | | | +---------------------- CLA | | | | +------------------------- halt | | | +---------------------------- CMA | | +------------------------------- or TW | +---------------------------------- CLI +------------------------------------- CMI (PDP-1D) The standard operate instruction can be microprogrammed. This routine is the instruction decode routine for the PDP-1. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered unimplemented instruction and STOP_INST flag set XCT loop indirect address loop infinite wait state I/O error in I/O simulator 2. Interrupts. With a single channel sequence break system, the PDP-1 has a single break request (flop b2, here sbs). If sequence breaks are enabled (flop sbm, here sbs), and one is not already in progress (flop b4, here sbs), a sequence break occurs. With a 16-channel sequence break system, the PDP-1 has 16 request flops (sbs_req), 16 enable flops (sbs_enb), and 16 active flops (sbs_act). It also has 16 synchronizer flops, which are not needed in simulation. 3. Arithmetic. The PDP-1 is a 1's complement system. In 1's complement arithmetic, a negative number is represented by the complement (XOR 0777777) of its absolute value. Addition of 1's complement numbers requires propagating the carry out of the high order bit back to the low order bit. 4. Adding I/O devices. Three modules must be modified: pdp1_defs.h add interrupt request definition pdp1_cpu.c add IOT dispatch code pdp1_sys.c add sim_devices table entry */ #include "pdp1_defs.h" #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_MDV (UNIT_V_UF + 0) /* mul/div */ #define UNIT_V_SBS (UNIT_V_UF + 1) #define UNIT_V_1D (UNIT_V_UF + 2) #define UNIT_V_1D45 (UNIT_V_UF + 3) #define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ #define UNIT_MDV (1 << UNIT_V_MDV) #define UNIT_SBS (1 << UNIT_V_SBS) #define UNIT_1D (1 << UNIT_V_1D) #define UNIT_1D45 (1 << UNIT_V_1D45) #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 MA_GETBNK(x) ((cpu_unit.flags & UNIT_1D45)? \ (((x) >> RM45_V_BNK) & RM45_M_BNK): \ (((x) >> RM48_V_BNK) & RM48_M_BNK)) 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 IO = 0; /* IO */ int32 PC = 0; /* PC */ int32 MA = 0; /* MA */ int32 MB = 0; /* MB */ int32 OV = 0; /* overflow */ int32 SS = 0; /* sense switches */ int32 PF = 0; /* program flags */ int32 TA = 0; /* address switches */ int32 TW = 0; /* test word */ int32 iosta = 0; /* status reg */ int32 sbs = 0; /* sequence break */ int32 sbs_init = 0; /* seq break start */ int32 ioh = 0; /* I/O halt */ int32 ios = 0; /* I/O syncronizer */ int32 cpls = 0; /* pending compl */ int32 sbs_req = 0; /* sbs requests */ int32 sbs_enb = 0; /* sbs enabled */ int32 sbs_act = 0; /* sbs active */ int32 extm = 0; /* ext mem mode */ int32 rm = 0; /* restrict mode */ int32 rmask = 0; /* restrict mask */ int32 rname[RN45_SIZE]; /* rename table */ int32 rtb = 0; /* restr trap buf */ int32 extm_init = 0; /* ext mem startup */ int32 stop_inst = 0; /* stop rsrv inst */ int32 xct_max = 16; /* XCT limit */ int32 ind_max = 16; /* ind limit */ 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 */ 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_1d (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat Ea (int32 IR); t_stat Ea_ch (int32 IR, int32 *byte_num); int32 inc_bp (int32 bp); t_stat set_rmv (int32 code); int32 sbs_eval (void); int32 sbs_ffo (int32 mask); t_stat Read (void); t_stat Write (void); extern int32 ptr (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); extern int32 clk (int32 inst, int32 dev, int32 dat); extern int32 dcs (int32 inst, int32 dev, int32 dat); const int32 sc_map[512] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, /* 00000xxxx */ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00001xxxx */ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00010xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00011xxxx */ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00100xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00101xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00110xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 00111xxxx */ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 01000xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01001xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01010xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01011xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01100xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01101xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01110xxxx */ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 01111xxxx */ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 10000xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10001xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10010xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10011xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10100xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10101xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10110xxxx */ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 11000xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11001xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11010xxxx */ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11100xxxx */ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11101xxxx */ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11110xxxx */ 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9 /* 11111xxxx */ }; const int32 ffo_map[256] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 }; const int32 byt_shf[4] = { 0, 0, 6, 12 }; /* 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, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, PC, ASIZE) }, { ORDATA (AC, AC, 18) }, { ORDATA (IO, IO, 18) }, { ORDATA (MA, MA, 16) }, { ORDATA (MB, MB, 18) }, { FLDATA (OV, OV, 0) }, { ORDATA (PF, PF, 8) }, { ORDATA (SS, SS, 6) }, { ORDATA (TA, TA, ASIZE) }, { ORDATA (TW, TW, 18) }, { FLDATA (EXTM, extm, 0) }, { FLDATA (RNGM, PF, PF_V_RNG) }, { FLDATA (L, PF, PF_V_L) }, { FLDATA (RM, rm, 0) }, { ORDATA (RMASK, rmask, 18) }, { ORDATA (RTB, rtb, 18) }, { BRDATA (RNAME, rname, 8, 2, RN45_SIZE) }, { FLDATA (SBON, sbs, SB_V_ON) }, { FLDATA (SBRQ, sbs, SB_V_RQ) }, { FLDATA (SBIP, sbs, SB_V_IP) }, { ORDATA (SBSREQ, sbs_req, 16) }, { ORDATA (SBSENB, sbs_enb, 16) }, { ORDATA (SBSACT, sbs_act, 16) }, { ORDATA (IOSTA, iosta, 18), REG_RO }, { ORDATA (CPLS, cpls, 6) }, { FLDATA (IOH, ioh, 0) }, { FLDATA (IOS, ios, 0) }, { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { FLDATA (STOP_INST, stop_inst, 0) }, { FLDATA (SBS_INIT, sbs_init, SB_V_ON) }, { FLDATA (EXTM_INIT, extm_init, 0) }, { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, { DRDATA (IND_MAX, ind_max, 8), PV_LEFT + REG_NZ }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_1D+UNIT_1D45, 0, "standard CPU", "PDP1C" }, { UNIT_1D+UNIT_1D45, UNIT_1D, "PDP-1D #48", "PDP1D48", &cpu_set_1d }, { UNIT_1D+UNIT_1D45, UNIT_1D+UNIT_1D45, "PDP1D #45", "PDP1D45", &cpu_set_1d }, { UNIT_MDV, UNIT_MDV, "multiply/divide", "MDV", NULL }, { UNIT_MDV, 0, "no multiply/divide", "NOMDV", NULL }, { UNIT_SBS, UNIT_SBS, "SBS", "SBS", NULL }, { UNIT_SBS, 0, "no SBS", "NOSBS", NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 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, 0 }; t_stat sim_instr (void) { extern int32 sim_interval; int32 IR, op, i, t, xct_count; int32 sign, signd, v, sbs_lvl, byno; int32 dev, pulse, io_data, sc, skip; t_stat reason; static int32 fs_test[8] = { 0, PF_SS_1, PF_SS_2, PF_SS_3, PF_SS_4, PF_SS_5, PF_SS_6, PF_SS_ALL }; #define EPC_WORD ((OV << 17) | (extm << 16) | PC) #define INCR_ADDR(x) (((x) & EPCMASK) | (((x) + 1) & DAMASK)) #define DECR_ADDR(x) (((x) & EPCMASK) | (((x) - 1) & DAMASK)) #define ABS(x) ((x) ^ (((x) & SIGN)? DMASK: 0)) if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ cpu_unit.flags |= UNIT_SBS|UNIT_MDV; /* 16-chan SBS, mdv */ if (!(cpu_unit.flags & UNIT_1D45)) { /* SN 48? */ PF &= ~PF_L; /* no link */ rtb = 0; /* no RTB */ for (i = 0; i < RN45_SIZE; i++) /* no rename */ rname[i] = i; } } else { /* standard PDP-1 */ PF &= ~(PF_L|PF_RNG); /* no link, ring */ rm = 0; /* no restrict mode */ rtb = 0; /* no RTB */ for (i = 0; i < RN45_SIZE; i++) /* no rename */ rname[i] = i; } if (cpu_unit.flags & UNIT_SBS) { /* 16-chan SBS? */ sbs = sbs & SB_ON; /* yes, only SB ON */ sbs_lvl = sbs_eval (); /* eval SBS system */ } else sbs_lvl = sbs_req = sbs_enb = sbs_act = 0; /* no, clr SBS sys */ /* Main instruction fetch/decode loop: check events and interrupts */ reason = 0; while (reason == 0) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; sbs_lvl = sbs_eval (); /* eval sbs system */ } if ((cpu_unit.flags & UNIT_SBS)? /* test interrupt */ ((sbs & SB_ON) && sbs_lvl): /* 16-chan SBS? */ (sbs == (SB_ON | SB_RQ))) { /* 1-chan SBS? */ if (cpu_unit.flags & UNIT_SBS) { /* 16-chan intr */ int32 lvl = sbs_lvl - 1; /* get level */ MA = lvl << 2; /* status block */ sbs_req &= ~SBS_MASK (lvl); /* clr lvl request */ sbs_act |= SBS_MASK (lvl); /* set lvl active */ sbs_lvl = sbs_eval (); /* re-eval SBS */ } else { /* 1-chan intr */ MA = 0; /* always level 0 */ sbs = SB_ON | SB_IP; /* set in prog flag */ } PCQ_ENTRY; /* save old PC */ MB = AC; /* save AC */ Write (); MA = MA + 1; MB = EPC_WORD; /* save OV'EXT'PC */ Write (); MA = MA + 1; MB = IO; /* save IO */ Write (); PC = MA + 1; /* PC = block + 3 */ extm = 0; /* extend off */ OV = 0; /* clear overflow */ } if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } /* Fetch, decode instruction */ MA = PC; if (Read ()) /* fetch inst */ break; IR = MB; /* save in IR */ PC = INCR_ADDR (PC); /* increment PC */ xct_count = 0; /* track XCT's */ sim_interval = sim_interval - 1; if (hst_lnt) { /* history enabled? */ hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = MA | HIST_PC; /* save state */ hst[hst_p].ir = IR; hst[hst_p].ovac = (OV << HIST_V_SHF) | AC; hst[hst_p].pfio = (PF << HIST_V_SHF) | IO; } xct_instr: /* label for XCT */ op = ((IR >> 13) & 037); /* get opcode */ switch (op) { /* decode IR<0:4> */ /* Logical, load, store instructions */ case 001: /* AND */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = AC & MB; break; case 002: /* IOR */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = AC | MB; break; case 003: /* XOR */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = AC ^ MB; break; case 004: /* XCT */ if (xct_count >= xct_max) { /* too many XCT's? */ reason = STOP_XCT; break; } if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; xct_count = xct_count + 1; /* count XCT's */ IR = MB; /* get instruction */ goto xct_instr; /* go execute */ case 005: /* LCH */ if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ if (reason = Ea_ch (IR, &byno)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = (MB << byt_shf[byno]) & 0770000; /* extract byte */ } else reason = stop_inst; /* no, illegal */ break; case 006: /* DCH */ if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ if (reason = Ea_ch (IR, &byno)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; MB = (MB & ~(0770000 >> byt_shf[byno])) | /* insert byte */ ((AC & 0770000) >> byt_shf[byno]); Write (); /* rewrite */ AC = ((AC << 6) | (AC >> 12)) & DMASK; /* rot AC left 6 */ } else reason = stop_inst; /* no, illegal */ break; case 007: /* CAL, JDA */ MA = (PC & EPCMASK) | ((IR & IA)? (IR & DAMASK): 0100); if (hst_p) /* history enabled? */ hst[hst_p].ea = MA; PCQ_ENTRY; MB = AC; /* save AC */ AC = EPC_WORD; PC = INCR_ADDR (MA); reason = Write (); break; case 010: /* LAC */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = MB; break; case 011: /* LIO */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; IO = MB; break; case 012: /* DAC */ if (reason = Ea (IR)) /* MA <- eff addr */ break; MB = AC; reason = Write (); break; case 013: /* DAP */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; MB = (AC & DAMASK) | (MB & ~DAMASK); reason = Write (); break; case 014: /* DIP */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; MB = (AC & ~DAMASK) | (MB & DAMASK); reason = Write (); break; case 015: /* DIO */ if (reason = Ea (IR)) /* MA <- eff addr */ break; MB = IO; reason = Write (); break; case 016: /* DZM */ if (reason = Ea (IR)) /* MA <- eff addr */ break; MB = 0; reason = Write (); break; /* Add, subtract, control Add is performed in sequential steps, as follows: 1. add 2. end around carry propagate 3. overflow check 4. -0 cleanup Subtract is performed in sequential steps, as follows: 1. complement AC 2. add 3. end around carry propagate 4. overflow check 5. complement AC Because no -0 check is done, (-0) - (+0) yields a result of -0 */ case 017: /* TAD */ if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = AC + MB + ((PF & PF_L)? 1: 0); /* AC + opnd + L */ if (AC > DMASK) /* carry? set L */ PF = PF | PF_L; else PF = PF & ~PF_L; /* no, clear L */ AC = AC & DMASK; /* mask AC */ } else reason = stop_inst; /* no, illegal */ break; case 020: /* ADD */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; t = AC; AC = AC + MB; if (AC > 0777777) /* end around carry */ AC = (AC + 1) & DMASK; if (((~t ^ MB) & (t ^ AC)) & SIGN) OV = 1; if (AC == DMASK) /* minus 0 cleanup */ AC = 0; break; case 021: /* SUB */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; t = AC ^ DMASK; /* complement AC */ AC = t + MB; /* -AC + MB */ if (AC > DMASK) /* end around carry */ AC = (AC + 1) & DMASK; if (((~t ^ MB) & (t ^ AC)) & SIGN) OV = 1; AC = AC ^ DMASK; /* recomplement AC */ break; case 022: /* IDX */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = MB + 1; if (AC >= DMASK) AC = (AC + 1) & DMASK; MB = AC; reason = Write (); break; case 023: /* ISP */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; AC = MB + 1; if (AC >= DMASK) AC = (AC + 1) & DMASK; MB = AC; if (!(AC & SIGN)) PC = INCR_ADDR (PC); reason = Write (); break; case 024: /* SAD */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; if (AC != MB) PC = INCR_ADDR (PC); break; case 025: /* SAS */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; if (AC == MB) PC = INCR_ADDR (PC); break; case 030: /* JMP */ if (sbs && /* SBS enabled? */ ((PC & EPCMASK) == 0) && /* in bank 0? */ ((IR & (IA|07703)) == (IA|00001)) && /* jmp i 00x1/5? */ ((cpu_unit.flags & UNIT_SBS) || /* 16-chan SBS or */ ((IR & 00074) == 0))) { /* jmp i 0001? */ if (cpu_unit.flags & UNIT_SBS) { /* 16-chan SBS dbk? */ int32 lvl = (IR >> 2) & SBS_LVL_MASK; /* lvl = MA<14:15> */ sbs_act &= ~SBS_MASK (lvl); /* clr level active */ sbs_lvl = sbs_eval (); /* eval SBS system */ } else sbs = sbs & ~SB_IP; /* 1-chan dbk */ PCQ_ENTRY; /* save old PC */ MA = IR & DAMASK; /* ind addr */ Read (); /* eff addr word */ OV = (MB >> 17) & 1; /* restore OV */ extm = (MB >> 16) & 1; /* restore ext mode */ PC = MB & AMASK; /* jmp i 00x1/5 */ if (hst_p) /* history enabled? */ hst[hst_p].ea = PC; } else { /* normal JMP */ if (reason = Ea (IR)) /* MA <- eff addr */ break; PCQ_ENTRY; PC = MA; } break; case 031: /* JSP */ if (reason = Ea (IR)) /* MA <- eff addr */ break; AC = EPC_WORD; PCQ_ENTRY; PC = MA; break; case 034: /* LAW */ AC = (IR & 07777) ^ ((IR & IA)? 0777777: 0); break; /* Multiply and divide Multiply and divide step and hardware multiply are exact implementations. Hardware divide is a 2's complement analog to the actual hardware. */ case 026: /* MUL */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; if (cpu_unit.flags & UNIT_MDV) { /* hardware? */ sign = AC ^ MB; /* result sign */ IO = ABS (AC); /* IO = |AC| */ v = ABS (MB); /* v = |mpy| */ for (i = AC = 0; i < 17; i++) { if (IO & 1) AC = AC + v; IO = (IO >> 1) | ((AC & 1) << 17); AC = AC >> 1; } if ((sign & SIGN) && (AC | IO)) { /* negative, > 0? */ AC = AC ^ DMASK; IO = IO ^ DMASK; } } else { /* multiply step */ if (IO & 1) AC = AC + MB; if (AC > DMASK) AC = (AC + 1) & DMASK; IO = (IO >> 1) | ((AC & 1) << 17); AC = AC >> 1; } break; case 027: /* DIV */ if (reason = Ea (IR)) /* MA <- eff addr */ break; if (reason = Read ()) /* MB <- data */ break; if (cpu_unit.flags & UNIT_MDV) { /* hardware */ sign = AC ^ MB; /* result sign */ signd = AC; /* remainder sign */ v = ABS (MB); /* v = |divr| */ if (ABS (AC) >= v) /* overflow? */ break; if (AC & SIGN) { AC = AC ^ DMASK; /* AC'IO = |AC'IO| */ IO = IO ^ DMASK; } for (i = t = 0; i < 18; i++) { if (t) AC = (AC + v) & DMASK; else AC = (AC - v) & DMASK; t = AC >> 17; if (i != 17) AC = ((AC << 1) | (IO >> 17)) & DMASK; IO = ((IO << 1) | (t ^ 1)) & 0777777; } if (t) /* fix remainder */ AC = (AC + v) & DMASK; t = ((signd & SIGN) && AC)? AC ^ DMASK: AC; AC = ((sign & SIGN) && IO)? IO ^ DMASK: IO; IO = t; PC = INCR_ADDR (PC); /* skip */ } else { /* divide step */ t = AC >> 17; AC = ((AC << 1) | (IO >> 17)) & DMASK; IO = ((IO << 1) | (t ^ 1)) & DMASK; if (IO & 1) AC = AC + (MB ^ DMASK); else AC = AC + MB + 1; if (AC > DMASK) AC = (AC + 1) & DMASK; if (AC == DMASK) AC = 0; } break; /* Skips */ case 032: /* skip */ v = (IR >> 3) & 07; /* sense switches */ t = IR & 07; /* program flags */ skip = (((cpu_unit.flags & UNIT_1D) && (IR & 04000) && (IO != 0)) || /* SNI (PDP-1D) */ ((IR & 02000) && !(IO & SIGN)) || /* SPI */ ((IR & 01000) && (OV == 0)) || /* SZO */ ((IR & 00400) && (AC & SIGN)) || /* SMA */ ((IR & 00200) && !(AC & SIGN)) || /* SPA */ ((IR & 00100) && (AC == 0)) || /* SZA */ (v && ((SS & fs_test[v]) == 0)) || /* SZSn */ (t && ((PF & fs_test[t]) == 0))); /* SZFn */ if (IR & IA) /* invert skip? */ skip = skip ^ 1; if (skip) PC = INCR_ADDR (PC); if (IR & 01000) /* SOV clears OV */ OV = 0; break; /* Shifts */ case 033: sc = sc_map[IR & 0777]; /* map shift count */ switch ((IR >> 9) & 017) { /* case on IR<5:8> */ case 001: /* RAL */ AC = ((AC << sc) | (AC >> (18 - sc))) & DMASK; break; case 002: /* RIL */ IO = ((IO << sc) | (IO >> (18 - sc))) & DMASK; break; case 003: /* RCL */ t = AC; AC = ((AC << sc) | (IO >> (18 - sc))) & DMASK; IO = ((IO << sc) | (t >> (18 - sc))) & DMASK; break; case 005: /* SAL */ t = (AC & SIGN)? DMASK: 0; AC = (AC & SIGN) | ((AC << sc) & 0377777) | (t >> (18 - sc)); break; case 006: /* SIL */ t = (IO & SIGN)? DMASK: 0; IO = (IO & SIGN) | ((IO << sc) & 0377777) | (t >> (18 - sc)); break; case 007: /* SCL */ t = (AC & SIGN)? DMASK: 0; AC = (AC & SIGN) | ((AC << sc) & 0377777) | (IO >> (18 - sc)); IO = ((IO << sc) | (t >> (18 - sc))) & DMASK; break; case 011: /* RAR */ AC = ((AC >> sc) | (AC << (18 - sc))) & DMASK; break; case 012: /* RIR */ IO = ((IO >> sc) | (IO << (18 - sc))) & DMASK; break; case 013: /* RCR */ t = IO; IO = ((IO >> sc) | (AC << (18 - sc))) & DMASK; AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; break; case 015: /* SAR */ t = (AC & SIGN)? DMASK: 0; AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; break; case 016: /* SIR */ t = (IO & SIGN)? DMASK: 0; IO = ((IO >> sc) | (t << (18 - sc))) & DMASK; break; case 017: /* SCR */ t = (AC & SIGN)? DMASK: 0; IO = ((IO >> sc) | (AC << (18 - sc))) & DMASK; AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; break; default: /* undefined */ reason = stop_inst; break; } /* end switch shf */ break; /* Special operates (PDP-1D) - performed in order shown */ case 036: /* special */ if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ if (IR & 000100) /* SCI */ IO = 0; if (IR & 000040) /* SCF */ PF = 0; if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ if ((IR & 000020) && /* SZL/SNL? */ (((PF & PF_L) == 0) == ((IR & IA) == 0))) PC = INCR_ADDR (PC); if (IR & 000010) /* CLL */ PF = PF & ~PF_L; if (IR & 000200) { /* SCM */ AC = (AC ^ DMASK) + ((PF & PF_L)? 1: 0); if (AC > DMASK) /* carry? set L */ PF = PF | PF_L; else PF = PF & ~PF_L; /* no, clear L */ AC = AC & DMASK; /* mask AC */ } } t = IO & PF_VR_ALL; if (IR & 004000) /* IIF */ IO = IO | PF; if (IR & 002000) /* IFI */ PF = PF | t; if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ if (IR & 000004) /* CML */ PF = PF ^ PF_L; if (IR & 000400) /* IDA */ AC = (PF & PF_RNG)? (AC & 0777770) | ((AC + 1) & 07): (AC + 1) & DMASK; } else PF = PF & ~PF_L; /* no link */ if (IR & 01000) /* IDC */ AC = inc_bp (AC); } else reason = stop_inst; /* no, illegal */ break; /* Operates - performed in the order shown */ case 037: /* operate */ if (IR & 004000) /* CLI */ IO = 0; if (IR & 000200) /* CLA */ AC = 0; if (IR & 002000) /* LAT */ AC = AC | TW; if (IR & 000100) /* LAP */ AC = AC | EPC_WORD; if (IR & 001000) /* CMA */ AC = AC ^ DMASK; if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ if (IR & 010000) /* CMI */ IO = IO ^ DMASK; MB = IO; if (IR & 000020) /* LIA */ IO = AC; if (IR & 000040) /* LAI */ AC = MB; } t = IR & 07; /* flag select */ if (IR & 010) /* STFn */ PF = PF | fs_test[t]; else PF = PF & ~fs_test[t]; /* CLFn */ if (IR & 000400) { /* HLT */ if (rm && !sbs_act) /* restrict, ~brk? */ reason = set_rmv (RTB_HLT); /* violation */ else reason = STOP_HALT; /* no, halt */ } break; /* IOT - The simulator behaves functionally like a real PDP-1 but does not use the same mechanisms or state bits. In particular, - If an IOT does not specify IO_WAIT, the IOT will be executed, and the I/O halt flag (IOH) will not be disturbed. On the real PDP-1, IOH is stored in IHS, IOH is cleared, the IOT is executed, and then IOH is restored from IHS. Because IHS is not otherwise used, it is not explicitly simulated. - If an IOT does specify IO_WAIT, then IOH specifies whether an I/O halt (wait) is already in progress. > If already set, I/O wait is in progress. The simulator looks for a completion pulse (IOS). If there is a pulse, IOH is cleared. If not, the IOT is fetched again. In either case, execution of the IOT is skipped. > If not set, I/O wait must start. IOH is set, the PC is backed up, and the IOT is executed. - On a real PDP-1, IOC is the I/O command enable and enables the IOT pulses. In the simulator, the enabling of IOT pulses is done through code flow, and IOC is not explicitly simulated. */ case 035: if (rm && !sbs_act) { /* restrict, ~brk? */ reason = set_rmv (RTB_IOT); /* violation */ break; } if (IR & IO_WAIT) { /* wait? */ if (ioh) { /* I/O halt? */ if (ios) /* comp pulse? done */ ioh = 0; else { /* wait more */ PC = DECR_ADDR (PC); /* re-execute */ if (cpls == 0) { /* pending pulses? */ reason = STOP_WAIT; /* no, CPU hangs */ break; } sim_interval = 0; /* force event */ } break; /* skip iot */ } ioh = 1; /* turn on halt */ PC = DECR_ADDR (PC); /* re-execute */ } dev = IR & 077; /* get dev addr */ pulse = (IR >> 6) & 077; /* get pulse data */ io_data = IO; /* default data */ switch (dev) { /* case on dev */ case 000: /* I/O wait */ break; case 001: if (IR & 003700) /* DECtape */ io_data = dt (IR, dev, IO); else io_data = ptr (IR, dev, IO); /* paper tape rdr */ break; case 002: case 030: /* paper tape rdr */ io_data = ptr (IR, dev, IO); break; case 003: /* typewriter */ io_data = tto (IR, dev, IO); break; case 004: /* keyboard */ io_data = tti (IR, dev, IO); break; case 005: case 006: /* paper tape punch */ io_data = ptp (IR, dev, IO); break; case 010: /* leave ring mode */ if (cpu_unit.flags & UNIT_1D) PF = PF & ~PF_RNG; else reason = stop_inst; break; case 011: /* enter ring mode */ if (cpu_unit.flags & UNIT_1D) PF = PF | PF_RNG; else reason = stop_inst; break; case 022: /* data comm sys */ io_data = dcs (IR, dev, IO); break; case 032: /* clock */ io_data = clk (IR, dev, IO); break; case 033: /* check status */ io_data = iosta | ((sbs & SB_ON)? IOS_SQB: 0); break; case 035: /* check trap buf */ if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ io_data = rtb; rtb = 0; } else reason = stop_inst; break; case 045: /* line printer */ io_data = lpt (IR, dev, IO); break; case 050: /* deact seq break */ if (cpu_unit.flags & UNIT_SBS) sbs_enb &= ~SBS_MASK (pulse & SBS_LVL_MASK); else reason = stop_inst; break; case 051: /* act seq break */ if (cpu_unit.flags & UNIT_SBS) sbs_enb |= SBS_MASK (pulse & SBS_LVL_MASK); else reason = stop_inst; break; case 052: /* start seq break */ if (cpu_unit.flags & UNIT_SBS) sbs_req |= SBS_MASK (pulse & SBS_LVL_MASK); else reason = stop_inst; break; case 053: /* clear all chan */ if (cpu_unit.flags & UNIT_SBS) sbs_enb = 0; else reason = stop_inst; break; case 054: /* seq brk off */ sbs = sbs & ~SB_ON; break; case 055: /* seq brk on */ sbs = sbs | SB_ON; break; case 056: /* clear seq brk */ sbs = 0; /* clear PI */ sbs_req = 0; sbs_enb = 0; sbs_act = 0; break; case 061: case 062: case 063: /* drum */ io_data = drm (IR, dev, IO); break; case 064: /* drum/leave rm */ if (cpu_unit.flags & UNIT_1D) rm = 0; else io_data = drm (IR, dev, IO); break; case 065: /* enter rm */ if (cpu_unit.flags & UNIT_1D) { rm = 1; rmask = IO; } else reason = stop_inst; break; case 066: /* rename mem */ if (cpu_unit.flags & UNIT_1D45) { /* SN45? */ int32 from = (IR >> 9) & RM45_M_BNK; int32 to = (IR >> 6) & RM45_M_BNK; rname[from] = to; } else reason = stop_inst; break; case 067: /* reset renaming */ if (cpu_unit.flags & UNIT_1D45) { /* SN45 */ for (i = 0; i < RN45_SIZE; i++) rname[i] = i; } else reason = stop_inst; break; case 074: /* extend mode */ extm = (IR >> 11) & 1; /* set from IR<6> */ break; default: /* undefined */ reason = stop_inst; break; } /* end switch dev */ IO = io_data & DMASK; if (io_data & IOT_SKP) /* skip? */ PC = INCR_ADDR (PC); if (io_data >= IOT_REASON) reason = io_data >> IOT_V_REASON; sbs_lvl = sbs_eval (); /* eval SBS system */ break; default: /* undefined */ if (rm && !sbs_act) /* restrict, ~brk? */ reason = set_rmv (RTB_ILL); /* violation */ else reason = STOP_RSRV; /* halt */ break; } /* end switch op */ if (reason == ERR_RMV) { /* restrict viol? */ sbs_req |= SBS_MASK (SBS_LVL_RMV); /* request break */ sbs_lvl = sbs_eval (); /* re-eval SBS */ reason = 0; /* continue */ } } /* end while */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Effective address routine for standard memory reference instructions */ t_stat Ea (int32 IR) { int32 i; t_stat r; MA = (PC & EPCMASK) | (IR & DAMASK); /* direct address */ if (IR & IA) { /* indirect addr? */ if (extm) { /* extend? */ if (r = Read ()) /* read; err? */ return r; MA = MB & AMASK; /* one level */ } else { /* multi-level */ for (i = 0; i < ind_max; i++) { /* count indirects */ if (r = Read ()) /* get ind word */ return r; MA = (PC & EPCMASK) | (MB & DAMASK); if ((MB & IA) == 0) break; } if (i >= ind_max) /* indirect loop? */ return STOP_IND; } /* end else !extm */ } /* end if indirect */ if (hst_p) /* history enabled? */ hst[hst_p].ea = MA; return SCPE_OK; } /* Effective address routine for character instructions */ t_stat Ea_ch (int32 IR, int32 *bn) { int32 i; t_stat r; MA = (PC & EPCMASK) | (IR & DAMASK); /* direct address */ if (extm) { /* extend? */ if (r = Read ()) /* read; err? */ return r; } else { /* multi-level */ for (i = 0; i < ind_max; i++) { /* count indirects */ if (r = Read ()) /* get ind word */ return r; if ((MB & IA) == 0) break; MA = (PC & EPCMASK) | (MB & DAMASK); } if (i >= ind_max) /* indirect loop? */ return STOP_IND; } /* end else !extm */ if (IR & IA) { /* automatic mode? */ if (rm & !sbs_act & ((MB & 0607777) == 0607777)) /* page cross? */ return set_rmv (RTB_CHR); MB = inc_bp (MB); /* incr byte ptr */ Write (); /* rewrite */ } *bn = (MB >> 16) & 03; /* byte num */ if (extm) /* final ea */ MA = MB & AMASK; else MA = (PC & EPCMASK) | (MB & DAMASK); if (hst_p) /* history enabled? */ hst[hst_p].ea = MA; return SCPE_OK; } /* Increment byte pointer, allowing for ring mode */ int32 inc_bp (int32 bp) { bp = bp + (1 << 16); /* add to bit<1> */ if (bp > DMASK) { /* carry out? */ if (PF & PF_RNG) /* ring mode? */ bp = (1 << 16) | (bp & 0177770) | ((bp + 1) & 07); else bp = (1 << 16) | ((bp + 1) & AMASK); } return bp; } /* Read and write memory */ t_stat Read (void) { if (rm && !sbs_act) { /* restrict check? */ int32 bnk = MA_GETBNK (MA); /* get bank */ if ((rmask << bnk) & SIGN) return set_rmv (0); } MB = M[MA]; if (hst_p) /* history enabled? */ hst[hst_p].opnd = MB; return SCPE_OK; } t_stat Write (void) { if (hst_p) /* hist? old contents */ hst[hst_p].opnd = M[MA]; if (rm && !sbs_act) { /* restrict check? */ int32 bnk = MA_GETBNK (MA); /* get bank */ if ((rmask << bnk) & SIGN) return set_rmv (0); } if (MEM_ADDR_OK (MA)) M[MA] = MB; return SCPE_OK; } /* Restrict mode trap */ t_stat set_rmv (int32 code) { rtb = code | (MB & RTB_MB_MASK); return ERR_RMV; } /* Evaluate SBS system */ int32 sbs_eval (void) { int32 hi; if (cpu_unit.flags & UNIT_SBS) { /* SBS enabled? */ if (sbs_req == 0) /* any requests? */ return 0; hi = sbs_ffo (sbs_req); /* find highest */ if (hi < sbs_ffo (sbs_act)) /* higher? */ return hi + 1; } return 0; } /* Find first one in a 16b field */ int32 sbs_ffo (int32 mask) { if (mask & 0177400) return ffo_map[(mask >> 8) & 0377]; else return (ffo_map[mask & 0377] + 8); } /* Device request interrupt */ t_stat dev_req_int (int32 lvl) { if (cpu_unit.flags & UNIT_SBS) { /* SBS enabled? */ if (lvl >= SBS_LVLS) /* invalid level? */ return SCPE_IERR; if (sbs_enb & SBS_MASK (lvl)) /* level active? */ sbs_req |= SBS_MASK (lvl); /* set SBS request */ } else sbs |= SB_RQ; /* PI request */ return SCPE_OK; } /* Device set/show SBS level */ t_stat dev_set_sbs (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 *lvl = (int32 *) desc; int32 newlvl; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; newlvl = get_uint (cptr, 10, SBS_LVLS - 1, &r); if (r != SCPE_OK) return SCPE_ARG; *lvl = newlvl; return SCPE_OK; } t_stat dev_show_sbs (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 *lvl = (int32 *) desc; if (lvl == NULL) return SCPE_IERR; fprintf (st, "SBS level %d", *lvl); return SCPE_OK; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int32 i; sbs = sbs_init; extm = extm_init; ioh = 0; ios = 0; cpls = 0; sbs_act = 0; sbs_req = 0; sbs_enb = 0; OV = 0; PF = 0; MA = 0; MB = 0; rm = 0; rtb = 0; rmask = 0; for (i = 0; i < RN45_SIZE; i++) rname[i] = i; 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; } /* Set PDP-1D */ t_stat cpu_set_1d (UNIT *uptr, int32 val, char *cptr, void *desc) { uptr->flags |= UNIT_SBS|UNIT_MDV; return SCPE_OK; } /* 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) /* enabled? */ return SCPE_NOFNC; 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 = (h->pfio >> HIST_V_SHF) & PF_VR_ALL; /* prog flags */ 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; } simh-3.8.1/PDP1/pdp1_clk.c0000644000175000017500000001111511107374006013204 0ustar vlmvlm/* pdp1_clk.c: PDP-1D clock simulator Copyright (c) 2006-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be bused in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. clk PDP-1D clock Note that the clock is run at 1/8 of real speed (125Hz instead of 1Khz), to provide for eventual implementation of idling. */ #include "pdp1_defs.h" #define CLK_HWRE_TPS 1000 /* hardware freq */ #define CLK_TPS 125 /* sim freq */ #define CLK_CNTS (CLK_HWRE_TPS / CLK_TPS) /* counts per tick */ #define CLK_C1MIN (1000 * 60) /* counts per min */ #define CLK_C32MS 32 /* counts per 32ms */ int32 clk32ms_sbs = 0; /* 32ms SBS level */ int32 clk1min_sbs = 0; /* 1min SBS level */ int32 clk_cntr = 0; int32 tmxr_poll = 5000; extern int32 stop_inst; t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit clk_reg CLK register list */ UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 5000 }; REG clk_reg[] = { { ORDATA (CNTR, clk_cntr, 16) }, { DRDATA (SBS32LVL, clk32ms_sbs, 4), REG_HRO }, { DRDATA (SBS1MLVL, clk1min_sbs, 4), REG_HRO }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBS32MSLVL", "SBS32MSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &clk32ms_sbs }, { MTAB_XTD|MTAB_VDV, 0, "SBS1MINLVL", "SBS1MINLVL", &dev_set_sbs, &dev_show_sbs, (void *) &clk1min_sbs }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &clk_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }; /* Clock IOT routine */ int32 clk (int32 inst, int32 dev, int32 dat) { int32 used, incr; if (clk_dev.flags & DEV_DIS) /* disabled? */ return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ used = tmxr_poll - (sim_is_active (&clk_unit) - 1); incr = (used * CLK_CNTS) / tmxr_poll; return clk_cntr + incr; } /* Unit service, generate appropriate interrupts */ t_stat clk_svc (UNIT *uptr) { if (clk_dev.flags & DEV_DIS) /* disabled? */ return SCPE_OK; tmxr_poll = sim_rtcn_calb (CLK_TPS, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, tmxr_poll); /* reactivate unit */ clk_cntr = clk_cntr + CLK_CNTS; /* incr counter */ if ((clk_cntr % CLK_C32MS) == 0) /* 32ms interval? */ dev_req_int (clk32ms_sbs); /* req intr */ if (clk_cntr >= CLK_C1MIN) { /* 1min interval? */ dev_req_int (clk1min_sbs); /* req intr */ clk_cntr = 0; /* reset counter */ } return SCPE_OK; } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { if (clk_dev.flags & DEV_DIS) sim_cancel (&clk_unit); /* disabled? */ else { tmxr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); sim_activate_abs (&clk_unit, tmxr_poll); /* activate unit */ } clk_cntr = 0; /* clear counter */ return SCPE_OK; } simh-3.8.1/PDP1/pdp1_drm.c0000644000175000017500000003613411111663734013231 0ustar vlmvlm/* pdp1_drm.c: PDP-1 drum simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. drp Type 23 parallel drum drm Type 24 serial drum 21-Dec-06 RMS Added 16-chan SBS support 08-Dec-03 RMS Added parallel drum support Fixed bug in DBL/DCN decoding 26-Oct-03 RMS Cleaned up buffer copy code 23-Jul-03 RMS Fixed incorrect logical, missing activate 05-Dec-02 RMS Cloned from pdp18b_drm.c */ #include "pdp1_defs.h" #include /* Serial drum constants */ #define DRM_NUMWDS 256 /* words/sector */ #define DRM_NUMSC 2 /* sectors/track */ #define DRM_NUMTR 256 /* tracks/drum */ #define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */ #define DRM_SIZE (DRM_NUMTR * DRM_NUMWDT) /* words/drum */ #define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */ /* Parallel drum constants */ #define DRP_NUMWDT 4096 /* words/track */ #define DRP_NUMTK 32 /* tracks/drum */ #define DRP_SIZE (DRP_NUMWDT * DRP_NUMTK) /* words/drum */ #define DRP_V_RWE 17 /* read/write enable */ #define DRP_V_FLD 12 /* drum field */ #define DRP_M_FLD 037 #define DRP_TAMASK 07777 /* track address */ #define DRP_WCMASK 07777 /* word count */ #define DRP_MAINCM 07777 /* mem addr incr */ #define DRP_GETRWE(x) (((x) >> DRP_V_RWE) & 1) #define DRP_GETRWF(x) (((x) >> DRP_V_FLD) & DRP_M_FLD) /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ #define DRM_READ 000 /* read */ #define DRM_WRITE 010 /* write */ #define DRP_RW 000 /* read/write */ #define DRP_BRK 001 /* break on address */ #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DRM_NUMWDT))) extern int32 M[]; extern int32 iosta; extern int32 stop_inst; extern UNIT cpu_unit; /* Serial drum variables */ uint32 drm_da = 0; /* track address */ uint32 drm_ma = 0; /* memory address */ uint32 drm_err = 0; /* error flag */ uint32 drm_wlk = 0; /* write lock */ int32 drm_time = 4; /* inter-word time */ int32 drm_sbs = 0; /* SBS level */ int32 drm_stopioe = 1; /* stop on error */ /* Parallel drum variables */ uint32 drp_rde = 0; /* read enable */ uint32 drp_wre = 0; /* write enable */ uint32 drp_rdf = 0; /* read field */ uint32 drp_wrf = 0; /* write field */ uint32 drp_ta = 0; /* track address */ uint32 drp_wc = 0; /* word count */ uint32 drp_ma = 0; /* memory address */ uint32 drp_err = 0; /* error */ int32 drp_time = 2; /* inter-word time */ int32 drp_stopioe = 1; /* stop on error */ /* Forward declarations */ t_stat drm_svc (UNIT *uptr); t_stat drm_reset (DEVICE *dptr); t_stat drp_svc (UNIT *uptr); t_stat drp_reset (DEVICE *dptr); /* DRM data structures drm_dev DRM device descriptor drm_unit DRM unit descriptor drm_reg DRM register list */ UNIT drm_unit = { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DRM_SIZE) }; REG drm_reg[] = { { ORDATA (DA, drm_da, 9) }, { ORDATA (MA, drm_ma, 16) }, { FLDATA (DONE, iosta, IOS_V_DRM) }, { FLDATA (ERR, drm_err, 0) }, { ORDATA (WLK, drm_wlk, 32) }, { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO }, { FLDATA (STOP_IOE, drm_stopioe, 0) }, { NULL } }; MTAB drm_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "APILVL", "APILVL", &dev_set_sbs, &dev_show_sbs, (void *) &drm_sbs }, { 0 } }; DEVICE drm_dev = { "DRM", &drm_unit, drm_reg, drm_mod, 1, 8, 20, 1, 8, 18, NULL, NULL, &drm_reset, NULL, NULL, NULL, NULL, DEV_DISABLE }; /* DRP data structures drp_dev DRP device descriptor drp_unit DRP unit descriptor drp_reg DRP register list */ UNIT drp_unit = { UDATA (&drp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DRM_SIZE) }; REG drp_reg[] = { { ORDATA (TA, drp_ta, 12) }, { ORDATA (RDF, drp_rdf, 5) }, { FLDATA (RDE, drp_rde, 0) }, { FLDATA (WRF, drp_wrf, 5) }, { FLDATA (WRE, drp_wre, 0) }, { ORDATA (MA, drp_ma, 16) }, { ORDATA (WC, drp_wc, 12) }, { FLDATA (BUSY, iosta, IOS_V_DRP) }, { FLDATA (ERR, drp_err, 0) }, { DRDATA (TIME, drp_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, drp_stopioe, 0) }, { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO }, { NULL } }; DEVICE drp_dev = { "DRP", &drp_unit, drp_reg, NULL, 1, 8, 20, 1, 8, 18, NULL, NULL, &drp_reset, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 drm (int32 IR, int32 dev, int32 dat) { int32 t; int32 pulse = (IR >> 6) & 037; if ((drm_dev.flags & DEV_DIS) == 0) { /* serial enabled? */ if ((pulse != 001) && (pulse != 011)) /* invalid pulse? */ return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ switch (dev) { /* switch on device */ case 061: /* DWR, DRD */ drm_ma = dat & AMASK; /* load mem addr */ drm_unit.FUNC = pulse & DRM_WRITE; /* save function */ break; case 062: /* DBL, DCN */ if ((pulse & 010) == 0) /* DBL? */ drm_da = dat & DRM_SMASK; /* load sector # */ iosta = iosta & ~IOS_DRM; /* clear flags */ drm_err = 0; t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); if (t <= 0) /* wrap around? */ t = t + DRM_NUMWDT; sim_activate (&drm_unit, t); /* start operation */ break; case 063: /* DTD */ if (pulse == 011) return (stop_inst << IOT_V_REASON) | dat; if (iosta & IOS_DRM) /* skip if done */ return (dat | IOT_SKP); break; case 064: /* DSE, DSP */ if ((drm_err == 0) || (pulse & 010)) /* no error, par test? */ return (dat | IOT_SKP); } /* end case */ return dat; } /* end if serial */ if ((drp_dev.flags & DEV_DIS) == 0) { /* parallel enabled? */ switch (dev) { /* switch on device */ case 061: /* DIA, DBA */ drp_err = 0; /* clear error */ iosta = iosta & ~IOS_DRP; /* not busy */ drp_rde = DRP_GETRWE (dat); /* set read enable */ drp_rdf = DRP_GETRWF (dat); /* set read field */ drp_ta = dat & DRP_TAMASK; /* set track addr */ if (IR & 02000) { /* DBA? */ t = drp_ta - GET_POS (drp_time); /* delta words */ if (t <= 0) /* wrap around? */ t = t + DRP_NUMWDT; sim_activate (&drp_unit, t); /* start operation */ drp_unit.FUNC = DRP_BRK; /* mark as break */ } else drp_unit.FUNC = DRP_RW; /* no, read/write */ break; case 062: /* DWC, DRA */ if (IR & 02000) dat = GET_POS (drp_time) | /* DRA, get position */ (drp_err? 0400000: 0); else { /* DWC */ drp_wre = DRP_GETRWE (dat); /* set write enable */ drp_wrf = DRP_GETRWF (dat); /* set write field */ drp_wc = dat & DRP_WCMASK; /* set word count */ } break; case 063: /* DCL */ drp_ma = dat & AMASK; /* set mem address */ t = drp_ta - GET_POS (drp_time); /* delta words */ if (t <= 0) /* wrap around? */ t = t + DRP_NUMWDT; sim_activate (&drp_unit, t); /* start operation */ iosta = iosta | IOS_DRP; /* set busy */ break; case 064: /* not assigned */ return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ } /* end case */ return dat; } /* end if parallel */ return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ } /* Serial unit service - this code assumes the entire drum is buffered */ t_stat drm_svc (UNIT *uptr) { uint32 i, da; uint32 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ drm_err = 1; /* set error */ iosta = iosta | IOS_DRM; /* set done */ dev_req_int (drm_sbs); /* req intr */ return IORETURN (drm_stopioe, SCPE_UNATT); } da = drm_da * DRM_NUMWDS; /* compute dev addr */ for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */ if (uptr->FUNC == DRM_READ) { /* read? */ if (MEM_ADDR_OK (drm_ma)) /* if !nxm */ M[drm_ma] = fbuf[da]; /* read word */ } else { /* write */ if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1; else { /* not locked */ fbuf[da] = M[drm_ma]; /* write word */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; } } drm_ma = (drm_ma + 1) & AMASK; /* incr mem addr */ } drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */ iosta = iosta | IOS_DRM; /* set done */ dev_req_int (drm_sbs); /* req intr */ return SCPE_OK; } /* Reset routine */ t_stat drm_reset (DEVICE *dptr) { if ((drm_dev.flags & DEV_DIS) == 0) drp_dev.flags = drp_dev.flags | DEV_DIS; drm_da = drm_ma = drm_err = 0; iosta = iosta & ~IOS_DRM; sim_cancel (&drm_unit); drm_unit.FUNC = 0; return SCPE_OK; } /* Parallel unit service - this code assumes the entire drum is buffered */ t_stat drp_svc (UNIT *uptr) { uint32 i, lim; uint32 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ drp_err = 1; /* set error */ iosta = iosta & ~IOS_DRP; /* clear busy */ if (uptr->FUNC) /* req intr */ dev_req_int (drm_sbs); return IORETURN (drp_stopioe, SCPE_UNATT); } if (uptr->FUNC == DRP_RW) { /* read/write? */ lim = drp_wc? drp_wc: DRP_TAMASK + 1; /* eff word count */ for (i = 0; i < lim; i++) { /* do transfer */ if (drp_wre) /* write enabled? */ fbuf[(drp_wrf << DRP_V_FLD) | drp_ta] = M[drp_ma]; if (drp_rde && MEM_ADDR_OK (drp_ma)) /* read enabled? */ M[drp_ma] = fbuf[(drp_rdf << DRP_V_FLD) | drp_ta]; drp_ta = (drp_ta + 1) & DRP_TAMASK; /* incr track addr */ drp_ma = ((drp_ma & ~DRP_MAINCM) | ((drp_ma + 1) & DRP_MAINCM)); } /* end for */ } /* end if */ iosta = iosta & ~IOS_DRP; /* clear busy */ if (uptr->FUNC) /* req intr */ dev_req_int (drm_sbs); return SCPE_OK; } /* Reset routine */ t_stat drp_reset (DEVICE *dptr) { if ((drp_dev.flags & DEV_DIS) == 0) drm_dev.flags = drm_dev.flags | DEV_DIS; drp_ta = 0; drp_rde = drp_rdf = drp_wre = drp_wrf = 0; drp_err = 0; drp_ma = 0; drp_wc = 0; iosta = iosta & ~IOS_DRP; sim_cancel (&drp_unit); drp_unit.FUNC = 0; return SCPE_OK; } simh-3.8.1/PDP1/pdp1_dt.c0000644000175000017500000013422511112107064013044 0ustar vlmvlm/* pdp1_dt.c: 18b DECtape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dt Type 550/555 DECtape 21-Dec-06 RMS Added 16-channel SBS support 23-Jun-06 RMS Fixed conflict in ATTACH switches Revised header format 16-Aug-05 RMS Fixed C++ declaration and cast problems 25-Jan-04 RMS Revised for device debug support 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR 26-Oct-03 RMS Cleaned up buffer copy code 18-Oct-03 RMS Added DECtape off reel message, simplified timing 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 17-Oct-02 RMS Fixed bug in end of reel logic 06-Oct-02 RMS Added device disable support 13-Aug-02 RMS Cloned from pdp18b_dt.c 18b DECtapes are represented in memory by fixed length buffer of 32b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the TD8E formatter) is: reverse end zone 8192 reverse end zone codes ~ 10 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 8192 forward end zone codes ~ 10 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of read all and write all. Read all assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write all writes only the data words and dumps the interblock words in the bit bucket. The Type 550 controller has a 4b unit select field, for units 1-8. The code assumes that the GETUNIT macro returns a unit number in the range of 0-7, with 8 represented as 0, and an invalid unit as -1. */ #include "pdp1_defs.h" #define DT_NUMDR 8 /* #drives */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define DT_WC 030 /* word count */ #define DT_CA 031 /* current addr */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_BLKWD 1 /* blk no word in h/t */ #define DT_CSMWD 4 /* checksum word in h/t */ #define DT_HTWRD 5 /* header/trailer words */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word size in lines */ #define D18_BSIZE 256 /* block size in 18b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D11_FILSIZ (D18_CAPAC * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 86 /* block size in 18b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) /* This controller */ #define DT_CAPAC D18_CAPAC /* default */ #define DT_WSIZE D18_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) #define DT_QREZ(u) (((u)->pos) < DT_EZLIN) #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) /* Status register A */ #define DTA_V_UNIT 12 /* unit select */ #define DTA_M_UNIT 017 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) #define DTA_V_MOT 4 /* motion */ #define DTA_M_MOT 03 #define DTA_V_FNC 0 /* function */ #define DTA_M_FNC 07 #define FNC_MOVE 00 /* move */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_WRIT 03 /* write */ #define FNC_RALL 05 /* read all */ #define FNC_WALL 06 /* write all */ #define FNC_WMRK 07 /* write timing */ #define DTA_STSTP (1u << (DTA_V_MOT + 1)) #define DTA_FWDRV (1u << DTA_V_MOT) #define DTA_MODE 0 /* not implemented */ #define DTA_RW 077 #define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)] #define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \ dev_req_int (dt_sbs); #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) /* Status register B */ #define DTB_V_DTF 17 /* data flag */ #define DTB_V_BEF 16 /* block end flag */ #define DTB_V_ERF 15 /* error flag */ #define DTB_V_END 14 /* end of tape */ #define DTB_V_TIM 13 /* timing err */ #define DTB_V_REV 12 /* reverse */ #define DTB_V_GO 11 /* go */ #define DTB_V_MRK 10 /* mark trk err */ #define DTB_V_SEL 9 /* select err */ #define DTB_DTF (1u << DTB_V_DTF) #define DTB_BEF (1u << DTB_V_BEF) #define DTB_ERF (1u << DTB_V_ERF) #define DTB_END (1u << DTB_V_END) #define DTB_TIM (1u << DTB_V_TIM) #define DTB_REV (1u << DTB_V_REV) #define DTB_GO (1u << DTB_V_GO) #define DTB_MRK (1u << DTB_V_MRK) #define DTB_SEL (1u << DTB_V_SEL) #define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL) /* DECtape state */ #define DTS_V_MOT 3 /* motion */ #define DTS_M_MOT 07 #define DTS_STOP 0 /* stopped */ #define DTS_DECF 2 /* decel, fwd */ #define DTS_DECR 3 /* decel, rev */ #define DTS_ACCF 4 /* accel, fwd */ #define DTS_ACCR 5 /* accel, rev */ #define DTS_ATSF 6 /* @speed, fwd */ #define DTS_ATSR 7 /* @speed, rev */ #define DTS_DIR 01 /* dir mask */ #define DTS_V_FNC 0 /* function */ #define DTS_M_FNC 07 #define DTS_OFR 7 /* "off reel" */ #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) #define DTS_V_2ND 6 /* next state */ #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ ((DTS_STA (y, z)) << DTS_V_2ND) #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ ((DTS_STA (y, z)) << DTS_V_3RD) #define DTS_NXTSTA(x) (x >> DTS_V_2ND) /* Operation substates */ #define DTO_WCO 1 /* wc overflow */ #define DTO_SOB 2 /* start of block */ /* Logging */ #define LOG_MS 001 /* move, search */ #define LOG_RW 002 /* read write */ #define LOG_BL 004 /* block # lblk */ #define ABS(x) (((x) < 0)? (-(x)): (x)) extern int32 M[]; extern int32 stop_inst; extern UNIT cpu_unit; extern int32 sim_switches; extern int32 sim_is_running; extern FILE *sim_deb; int32 dtsa = 0; /* status A */ int32 dtsb = 0; /* status B */ int32 dtdb = 0; /* data buffer */ int32 dt_sbs = 0; /* SBS level */ int32 dt_ltime = 12; /* interline time */ int32 dt_dctime = 40000; /* decel time */ int32 dt_substate = 0; int32 dt_logblk = 0; int32 dt_stopoffr = 0; static const int32 map_unit[16] = { /* Type 550 unit map */ -1, 1, 2, 3, 4, 5, 6, 7, 0, -1, -1, -1, -1, -1, -1, -1 }; t_stat dt_svc (UNIT *uptr); t_stat dt_reset (DEVICE *dptr); t_stat dt_attach (UNIT *uptr, char *cptr); t_stat dt_detach (UNIT *uptr); void dt_deselect (int32 oldf); void dt_newsa (int32 newf); void dt_newfnc (UNIT *uptr, int32 newsta); t_bool dt_setpos (UNIT *uptr); void dt_schedez (UNIT *uptr, int32 dir); void dt_seterr (UNIT *uptr, int32 e); int32 dt_comobv (int32 val); int32 dt_csum (UNIT *uptr, int32 blk); int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); /* DT data structures dt_dev DT device descriptor dt_unit DT unit list dt_reg DT register list dt_mod DT modifier list */ UNIT dt_unit[] = { { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) } }; REG dt_reg[] = { { ORDATA (DTSA, dtsa, 18) }, { ORDATA (DTSB, dtsb, 18) }, { ORDATA (DTDB, dtdb, 18) }, { FLDATA (DTF, dtsb, DTB_V_DTF) }, { FLDATA (BEF, dtsb, DTB_V_BEF) }, { FLDATA (ERF, dtsb, DTB_V_ERF) }, { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, { ORDATA (SUBSTATE, dt_substate, 2) }, { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO) }, { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, DT_NUMDR, REG_HRO) }, { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, { DRDATA (SBSLVL, dt_sbs, 4), REG_HRO }, { NULL } }; MTAB dt_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &dt_sbs }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { 0 } }; DEBTAB dt_deb[] = { { "MOTION", LOG_MS }, { "DATA", LOG_RW }, { "BLOCK", LOG_BL }, { NULL, 0 } }; DEVICE dt_dev = { "DT", dt_unit, dt_reg, dt_mod, DT_NUMDR, 8, 24, 1, 8, 18, NULL, NULL, &dt_reset, NULL, &dt_attach, &dt_detach, NULL, DEV_DISABLE | DEV_DEBUG, 0, dt_deb, NULL, NULL }; /* IOT routine */ int32 dt (int32 IR, int32 dev, int32 dat) { int32 pulse = (IR >> 6) & 037; int32 fnc, mot, unum; UNIT *uptr = NULL; if (dt_dev.flags & DEV_DIS) /* disabled? */ return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ unum = DTA_GETUNIT (dtsa); /* get unit no */ if (unum >= 0) /* get unit */ uptr = dt_dev.units + unum; if (pulse == 003) { /* MSE */ if ((dtsa ^ dat) & DTA_UNIT) /* new unit? */ dt_deselect (dtsa); dtsa = (dtsa & ~DTA_UNIT) | (dat & DTA_UNIT); dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); } if (pulse == 004) { /* MLC */ dtsa = (dtsa & ~DTA_RW) | (dat & DTA_RW); /* load dtsa */ dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); fnc = DTA_GETFNC (dtsa); /* get fnc */ if ((uptr == NULL) || /* invalid? */ ((uptr->flags) & UNIT_DIS) || /* disabled? */ (fnc >= FNC_WMRK) || /* write mark? */ ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WLK)) || ((fnc == FNC_WALL) && (uptr->flags & UNIT_WLK))) dt_seterr (uptr, DTB_SEL); /* select err */ else dt_newsa (dtsa); } if (pulse == 005) { /* MRD */ dat = (dat & ~DMASK) | dtdb; dtsb = dtsb & ~(DTB_DTF | DTB_BEF); } if (pulse == 006) { /* MWR */ dtdb = dat & DMASK; dtsb = dtsb & ~(DTB_DTF | DTB_BEF); } if (pulse == 007) { /* MRS */ dtsb = dtsb & ~(DTB_REV | DTB_GO); /* clr rev, go */ if (uptr) { /* valid unit? */ mot = DTS_GETMOT (uptr->STATE); /* get motion */ if (mot & DTS_DIR) /* rev? set */ dtsb = dtsb | DTB_REV; if ((mot >= DTS_ACCF) || (uptr->STATE & 0777700)) dtsb = dtsb | DTB_GO; /* accel? go */ } dat = (dat & ~DMASK) | dtsb; } DT_UPDINT; return dat; } /* Unit deselect */ void dt_deselect (int32 oldf) { int32 old_unit, old_mot; UNIT *uptr; old_unit = DTA_GETUNIT (oldf); /* get unit no */ if (old_unit < 0) /* invalid? */ return; uptr = dt_dev.units + old_unit; /* get unit */ old_mot = DTS_GETMOT (uptr->STATE); if (old_mot >= DTS_ATSF) /* at speed? */ dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); else if (old_mot >= DTS_ACCF) /* accelerating? */ DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); return; } /* Command register change 1. If change in motion, stop to start - schedule acceleration - set function as next state 2. If change in motion, start to stop - if not already decelerating (could be reversing), schedule deceleration 3. If change in direction, - if not decelerating, schedule deceleration - set accelerating (other dir) as next state - set function as next next state 4. If not accelerating or at speed, - schedule acceleration - set function as next state 5. If not yet at speed, - set function as next state 6. If at speed, - set function as current state, schedule function */ void dt_newsa (int32 newf) { int32 new_unit, prev_mot, new_fnc; int32 prev_mving, new_mving, prev_dir, new_dir; UNIT *uptr; new_unit = DTA_GETUNIT (newf); /* new unit */ if (new_unit < 0) /* invalid? */ return; uptr = dt_dev.units + new_unit; if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ dt_seterr (uptr, DTB_SEL); /* no, error */ return; } prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ prev_mving = prev_mot != DTS_STOP; /* previous moving? */ prev_dir = prev_mot & DTS_DIR; /* previous dir? */ new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ new_fnc = DTA_GETFNC (newf); /* new function? */ if ((prev_mving | new_mving) == 0) /* stop to stop */ return; if (new_mving & ~prev_mving) { /* start? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mving & ~new_mving) { /* stop? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ return; } if (prev_dir ^ new_dir) { /* dir chg? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ return; } if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* cancel cur */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mot < DTS_ATSF) { /* not at speed? */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ return; } /* Schedule new DECtape function This routine is only called if - the selected unit is attached - the selected unit is at speed (forward or backward) This routine - updates the selected unit's position - updates the selected unit's state - schedules the new operation */ void dt_newfnc (UNIT *uptr, int32 newsta) { int32 fnc, dir, blk, unum, newpos; uint32 oldpos; oldpos = uptr->pos; /* save old pos */ if (dt_setpos (uptr)) /* update pos */ return; uptr->STATE = newsta; /* update state */ fnc = DTS_GETFNC (uptr->STATE); /* set variables */ dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; unum = (int32) (uptr - dt_dev.units); if (oldpos == uptr->pos) /* bump pos */ uptr->pos = uptr->pos + (dir? -1: 1); blk = DT_LIN2BL (uptr->pos, uptr); if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ dt_seterr (uptr, DTB_END); /* set ez flag, stop */ return; } sim_cancel (uptr); /* cancel cur op */ dt_substate = DTO_SOB; /* substate = block start */ switch (fnc) { /* case function */ case DTS_OFR: /* off reel */ if (dir) /* rev? < start */ newpos = -1000; else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ break; case FNC_MOVE: /* move */ dt_schedez (uptr, dir); /* sched end zone */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", unum, (dir? "backward": "forward")); return; /* done */ case FNC_SRCH: /* search */ if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; else newpos = DT_BLK2LN ((DT_QREZ (uptr)? 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", unum, (dir? "backward": "forward")); break; case FNC_WRIT: /* write */ case FNC_READ: /* read */ case FNC_RALL: /* read all */ case FNC_WALL: /* write all */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; else newpos = DT_EZLIN + (DT_WSIZE - 1); } else { newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE; if (!dir) newpos = newpos + (DT_WSIZE - 1); } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: read all block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); break; default: dt_seterr (uptr, DTB_SEL); /* bad state */ return; } if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */ dtsb = dtsb | DTB_DTF; /* set data flag */ DT_UPDINT; } sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/dt_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool dt_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 mot = DTS_GETMOT (uptr->STATE); int32 unum, delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (mot & ~DTS_DIR) { /* case on motion */ case DTS_STOP: /* stop */ delta = 0; break; case DTS_DECF: /* slowing */ ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case DTS_ACCF: /* accelerating */ ulin = ut / (uint32) dt_ltime; udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; delta = (ulin * ulin) / (2 * udelt); break; case DTS_ATSF: /* at speed */ delta = ut / (uint32) dt_ltime; break; } if (mot & DTS_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel? */ uptr->STATE = uptr->pos = 0; unum = (int32) (uptr - dt_dev.units); if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ dt_seterr (uptr, DTB_SEL); /* error */ return TRUE; } return FALSE; } /* Unit service Unit must be attached, detach cancels operation */ t_stat dt_svc (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; int32 fnc = DTS_GETFNC (uptr->STATE); int32 *fbuf = (int32 *) uptr->filebuf; int32 blk, wrd, ma, relpos; uint32 ba; /* Motion cases Decelerating - if next state != stopped, must be accel reverse Accelerating - next state must be @speed, schedule function At speed - do functional processing */ switch (mot) { case DTS_DECF: case DTS_DECR: /* decelerating */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ if (uptr->STATE) /* not stopped? */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ return SCPE_OK; case DTS_ACCF: case DTS_ACCR: /* accelerating */ dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ return SCPE_OK; case DTS_ATSF: case DTS_ATSR: /* at speed */ break; /* check function */ default: /* other */ dt_seterr (uptr, DTB_SEL); /* state error */ return SCPE_OK; } /* Functional cases Move - must be at end zone Search - transfer block number, schedule next block Off reel - detach unit (it must be deselected) */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); if (DT_QEZ (uptr)) { /* in end zone? */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; } blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ switch (fnc) { /* at speed, check fnc */ case FNC_MOVE: /* move */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; case DTS_OFR: /* off reel */ detach_unit (uptr); /* must be deselected */ uptr->STATE = uptr->pos = 0; /* no visible action */ break; /* Search */ case FNC_SRCH: /* search */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ dtdb = blk; /* store block # */ dtsb = dtsb | DTB_DTF; /* set DTF */ break; /* Read and read all */ case FNC_READ: case FNC_RALL: if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; dtdb = fbuf[ba]; /* get tape word */ dtsb = dtsb | DTB_DTF; /* set flag */ } else { ma = (2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1; wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ #if defined (OLD_TYPE550) if ((wrd == 0) || /* skip 1st, last */ (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; #endif if ((fnc == FNC_READ) && /* read, skip if not */ (wrd != DT_CSMWD) && /* fwd, rev cksum */ (wrd != ma)) break; dtdb = dt_gethdr (uptr, blk, relpos); if (wrd == (dir? DT_CSMWD: ma)) /* at end csum? */ dtsb = dtsb | DTB_BEF; /* end block */ else dtsb = dtsb | DTB_DTF; /* else next word */ } if (dir) dtdb = dt_comobv (dtdb); break; /* Write and write all */ case FNC_WRIT: case FNC_WALL: if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; if (dir) /* get data word */ fbuf[ba] = dt_comobv (dtdb); else fbuf[ba] = dtdb; if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (wrd == (dir? 0: DTU_BSIZE (uptr) - 1)) dtsb = dtsb | DTB_BEF; /* end block */ else dtsb = dtsb | DTB_DTF; /* else next word */ } else { wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ #if defined (OLD_TYPE550) if ((wrd == 0) || /* skip 1st, last */ (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; #endif if ((fnc == FNC_WRIT) && /* wr, skip if !csm */ (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1))) break; dtsb = dtsb | DTB_DTF; /* set flag */ } break; default: dt_seterr (uptr, DTB_SEL); /* impossible state */ break; } DT_UPDINT; /* update interrupts */ return SCPE_OK; } /* Utility routines */ /* Set error flag */ void dt_seterr (UNIT *uptr, int32 e) { int32 mot = DTS_GETMOT (uptr->STATE); dtsa = dtsa & ~DTA_STSTP; /* clear go */ dtsb = dtsb | DTB_ERF | e; /* set error flag */ if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ sim_cancel (uptr); /* cancel activity */ if (dt_setpos (uptr)) /* update position */ return; sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ } DT_UPDINT; return; } /* Schedule end zone */ void dt_schedez (UNIT *uptr, int32 dir) { int32 newpos; if (dir) /* rev? rev ez */ newpos = DT_EZLIN - DT_WSIZE; else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Complement obverse routine */ int32 dt_comobv (int32 dat) { dat = dat ^ 0777777; /* compl obverse */ dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | ((dat >> 3) & 0700) | ((dat & 0700) << 3) | ((dat & 070) << 9) | ((dat & 07) << 15); return dat; } /* Checksum routine */ int32 dt_csum (UNIT *uptr, int32 blk) { int32 *fbuf = (int32 *) uptr->filebuf; int32 ba = blk * DTU_BSIZE (uptr); int32 i, csum, wrd; csum = 0777777; for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i]; /* get word */ csum = csum + wrd; /* 1's comp add */ if (csum > 0777777) csum = (csum + 1) & 0777777; } return (csum ^ 0777777); /* 1's comp res */ } /* Get header word */ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) { int32 wrd = relpos / DT_WSIZE; if (wrd == DT_BLKWD) /* fwd blknum */ return blk; if (wrd == DT_CSMWD) /* rev csum */ return 0777777; if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ return (dt_csum (uptr, blk)); if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ return dt_comobv (blk); return 0; /* all others */ } /* Reset routine */ t_stat dt_reset (DEVICE *dptr) { int32 i, prev_mot; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */ uptr = dt_dev.units + i; if (sim_is_running) { /* CAF? */ prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ if (dt_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } dtsa = dtsb = 0; /* clear status */ DT_UPDINT; /* reset interrupt */ return SCPE_OK; } /* IORS routine */ int32 dt_iors (void) { #if defined IOS_DTA return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0); #else return 0; #endif } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 12b, read 12b format and convert to 18b in buffer If 16b, read 16b format and convert to 18b in buffer If 18b/36b, read data into buffer */ t_stat dt_attach (UNIT *uptr, char *cptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, sz, k, *fbuf; int32 u = uptr - dt_dev.units; t_stat r; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) /* error? */ return r; if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ if (sim_switches & SWMASK ('T')) /* att 12b? */ uptr->flags = uptr->flags | UNIT_8FMT; else if (sim_switches & SWMASK ('S')) /* att 16b? */ uptr->flags = uptr->flags | UNIT_11FMT; else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ (sz = sim_fsize (uptr->fileref))) { if (sz == D8_FILSIZ) uptr->flags = uptr->flags | UNIT_8FMT; else if (sz == D11_FILSIZ) uptr->flags = uptr->flags | UNIT_11FMT; } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ printf ("%s%d: ", sim_dname (&dt_dev), u); if (uptr->flags & UNIT_8FMT) printf ("12b format"); else if (uptr->flags & UNIT_11FMT) printf ("16b format"); else printf ("18b/36b format"); printf (", buffering file in memory\n"); if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | ((uint32) (pdp8b[k + 1] >> 6) & 077); fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | ((uint32) pdp8b[k + 2] & 07777); ba = ba + 2; } /* end blk loop */ } /* end file loop */ uptr->hwmark = ba; } /* end if */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; for (k = 0; k < D18_BSIZE; k++) fbuf[ba++] = pdp11b[k]; } uptr->hwmark = ba; /* end elif */ } else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), uptr->capac, uptr->fileref); uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ return SCPE_OK; } /* Detach routine Cancel in progress operation If 12b, convert 18b buffer to 12b and write to file If 16b, convert 18b buffer to 16b and write to file If 18b/36b, write buffer to file Deallocate buffer */ t_stat dt_detach (UNIT* uptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, k, *fbuf; int32 u = uptr - dt_dev.units; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (sim_is_active (uptr)) { sim_cancel (uptr); if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; DT_UPDINT; } uptr->STATE = uptr->pos = 0; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ pdp8b[k] = (fbuf[ba] >> 6) & 07777; pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | ((fbuf[ba + 1] >> 12) & 077); pdp8b[k + 2] = fbuf[ba + 1] & 07777; ba = ba + 2; } /* end loop blk */ fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 12b */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D18_BSIZE; k++) /* loop blk */ pdp11b[k] = fbuf[ba++] & 0177777; fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 16b */ else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ uptr->hwmark, uptr->fileref); if (ferror (uptr->fileref)) perror ("I/O error"); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ return detach_unit (uptr); } simh-3.8.1/PDP1/pdp1_stddev.c0000644000175000017500000005544111107376746013753 0ustar vlmvlm/* pdp1_stddev.c: PDP-1 standard devices Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr paper tape reader ptp paper tape punch tti keyboard tto teleprinter 21-Dec-06 RMS Added 16-channel sequence break support 29-Oct-03 RMS Added PTR FIODEC-to-ASCII translation (from Phil Budne) 07-Sep-03 RMS Changed ioc to ios 30-Aug-03 RMS Revised PTR to conform to Maintenance Manual; added deadlock prevention on errors 23-Jul-03 RMS Revised to detect I/O wait hang 25-Apr-03 RMS Revised for extended file support 22-Dec-02 RMS Added break support 29-Nov-02 RMS Fixed output flag initialization (found by Derek Peschel) 21-Nov-02 RMS Changed typewriter to half duplex (found by Derek Peschel) 06-Oct-02 RMS Revised for V2.10 30-May-02 RMS Widened POS to 32b 29-Nov-01 RMS Added read only unit support 07-Sep-01 RMS Moved function prototypes 10-Jun-01 RMS Fixed comment 30-Oct-00 RMS Standardized device naming Note: PTP timeout must be >10X faster that TTY output timeout for Macro to work correctly! */ #include "pdp1_defs.h" #define FIODEC_STOP 013 /* stop code */ #define FIODEC_UC 074 #define FIODEC_LC 072 #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 PTR_LEADER 20 /* ASCII leader chars */ int32 ptr_state = 0; int32 ptr_wait = 0; int32 ptr_stopioe = 0; int32 ptr_uc = 0; /* upper/lower case */ int32 ptr_hold = 0; /* holding buffer */ int32 ptr_leader = PTR_LEADER; /* leader count */ int32 ptr_sbs = 0; /* SBS level */ int32 ptp_stopioe = 0; int32 ptp_sbs = 0; /* SBS level */ int32 tti_hold = 0; /* tti hold buf */ int32 tti_sbs = 0; /* SBS level */ int32 tty_buf = 0; /* tty buffer */ int32 tty_uc = 0; /* tty uc/lc */ int32 tto_sbs = 0; extern int32 ios, ioh, cpls, iosta; extern int32 PF, IO, PC, TA; extern int32 M[]; int ptr_get_ascii (UNIT *uptr); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat tty_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); t_stat ptr_attach (UNIT *uptr, char *cptr); /* Character translation tables */ int32 fiodec_to_ascii[128] = { ' ', '1', '2', '3', '4', '5', '6', '7', /* lower case */ '8', '9', 0, 0, 0, 0, 0, 0, '0', '/', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, ',', 0, 0, '\t', 0, '@', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 0, 0, '-', ')', '\\', '(', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '{', '.', '}', '\b', 0, '\r', ' ', '"', '\'', '~', '#', '!', '&', '<', /* upper case */ '>', '^', 0, 0, 0, 0, 0, 0, '`', '?', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0, '=', 0, 0, '\t', 0, '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 0, 0, '+', ']', '|', '[', 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '{', '*', '}', '\b', 0, '\r' }; int32 ascii_to_fiodec[128] = { 0, 0, 0, 0, 0, 0, 0, 0, BOTH+075, BOTH+036, 0, 0, 0, BOTH+077, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, BOTH+0, UC+005, UC+001, UC+004, 0, 0, UC+006, UC+002, 057, 055, UC+073, UC+054, 033, 054, 073, 021, 020, 001, 002, 003, 004, 005, 006, 007, 010, 011, 0, 0, UC+007, UC+033, UC+010, UC+021, 040, UC+061, UC+062, UC+063, UC+064, UC+065, UC+066, UC+067, UC+070, UC+071, UC+041, UC+042, UC+043, UC+044, UC+045, UC+046, UC+047, UC+050, UC+051, UC+022, UC+023, UC+024, UC+025, UC+026, UC+027, UC+030, UC+031, UC+057, 056, UC+055, UC+011, UC+040, UC+020, 061, 062, 063, 064, 065, 066, 067, 070, 071, 041, 042, 043, 044, 045, 046, 047, 050, 051, 022, 023, 024, 025, 026, 027, 030, 031, 0, UC+056, 0, UC+003, BOTH+075 }; /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit ptr_reg PTR register list */ UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 18) }, { FLDATA (UC, ptr_uc, UC_V) }, { FLDATA (DONE, iosta, IOS_V_PTR) }, { FLDATA (RPLS, cpls, CPLS_V_PTR) }, { ORDATA (HOLD, ptr_hold, 9), REG_HRO }, { ORDATA (STATE, ptr_state, 5), REG_HRO }, { FLDATA (WAIT, ptr_wait, 0), REG_HRO }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { DRDATA (LEADER, ptr_leader, 6), REG_HRO }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { DRDATA (SBSLVL, ptr_sbs, 4), REG_HRO }, { NULL } }; MTAB ptr_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &ptr_sbs }, { UNIT_ASCII, UNIT_ASCII, "ASCII", "ASCII", NULL }, { UNIT_ASCII, 0, "FIODEC", "FIODEC", NULL }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, &ptr_attach, NULL, NULL, 0 }; /* 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) }, { FLDATA (RPLS, cpls, CPLS_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) }, { DRDATA (SBSLVL, ptp_sbs, 4), REG_HRO }, { NULL } }; MTAB ptp_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &ptp_sbs }, { 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, 0 }; /* 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 }, { DRDATA (SBSLVL, tti_sbs, 4), REG_HRO }, { NULL } }; MTAB tti_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &tti_sbs }, { 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, 0 }; /* 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 (RPLS, cpls, CPLS_V_TTO) }, { FLDATA (DONE, iosta, IOS_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { DRDATA (SBSLVL, tto_sbs, 4), REG_HRO }, { NULL } }; MTAB tto_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &tto_sbs }, { 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, 0 }; /* 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 IO. It is cleared by any RB->IO 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 PTR service routine clears IOH on any error during a rpa/rpb i. */ int32 ptr (int32 inst, int32 dev, int32 dat) { if (dev == 0030) { /* RRB */ iosta = iosta & ~IOS_PTR; /* clear status */ return ptr_unit.buf; /* return data */ } if (dev == 0002) /* RPB, mode = binary */ ptr_state = 18; else if (sim_is_active (&ptr_unit)) { /* RPA, running? */ sim_cancel (&ptr_unit); /* stop reader */ return dat; } else ptr_state = 0; /* mode = alpha */ ptr_unit.buf = 0; /* clear buffer */ if (inst & IO_WAIT) /* set ptr wait */ ptr_wait = 1; else ptr_wait = 0; /* from IR<5> */ if (GEN_CPLS (inst)) { /* comp pulse? */ ios = 0; cpls = cpls | CPLS_PTR; } else cpls = cpls & ~CPLS_PTR; sim_activate (&ptr_unit, ptr_unit.wait); /* start reader */ return dat; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ if (ptr_wait) /* if wait, clr ioh */ ptr_wait = ioh = 0; if ((cpls & CPLS_PTR) || ptr_stopioe) return SCPE_UNATT; return SCPE_OK; } if ((uptr->flags & UNIT_ASCII) && (ptr_state == 0)) /* ASCII mode, alpha read? */ temp = ptr_get_ascii (uptr); /* get processed char */ else 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 (ptr_wait) /* if wait, clr ioh */ ptr_wait = ioh = 0; if (feof (uptr->fileref)) { if ((cpls & CPLS_PTR) || ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } if (ptr_state == 0) /* alpha */ uptr->buf = temp & 0377; else if (temp & 0200) { /* binary */ ptr_state = ptr_state - 6; uptr->buf = uptr->buf | ((temp & 077) << ptr_state); } if (ptr_state == 0) { /* done? */ if (cpls & CPLS_PTR) { /* completion pulse? */ iosta = iosta & ~IOS_PTR; /* clear flag */ IO = uptr->buf; /* fill IO */ ios = 1; /* restart */ cpls = cpls & ~CPLS_PTR; } else { /* no, interrupt */ iosta = iosta | IOS_PTR; /* set flag */ dev_req_int (ptr_sbs); /* req interrupt */ } } else sim_activate (uptr, uptr->wait); /* get next char */ return SCPE_OK; } /* Read next ASCII character */ int ptr_get_ascii (UNIT *uptr) { int c; int32 in; if (ptr_leader > 0) { /* leader? */ ptr_leader = ptr_leader - 1; /* count down */ return 0; } if (ptr_hold & CW) { /* char waiting? */ in = ptr_hold & TT_WIDTH; /* return char */ ptr_hold = 0; /* not waiting */ } else { for (;;) { /* until valid char */ if ((c = getc (uptr->fileref)) == EOF) /* get next char, EOF? */ return FIODEC_STOP; /* return STOP */ uptr->pos = uptr->pos + 1; /* count char */ c = c & 0177; /* cut to 7b */ if (c == '\n') /* NL -> CR */ c = '\r'; else if (c == '\r') /* ignore CR */ continue; in = ascii_to_fiodec[c]; /* convert char */ if ((in == 0) && (c != ' ')) /* ignore unknowns */ continue; if ((in & BOTH) || ((in & UC) == ptr_uc)) /* case match? */ in = in & TT_WIDTH; /* cut to 6b */ else { /* no, case shift */ ptr_hold = in | CW; /* set char waiting */ ptr_uc = in & UC; /* set case */ in = ptr_uc? FIODEC_UC: FIODEC_LC; /* return case */ } /* end else */ break; } /* end for */ } /* end else */ in = in * 010040201; /* even parity from */ in = in | 027555555400; /* HACKMEM 167 */ in = in % (9 << 7); return in & 0377; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { ptr_state = 0; /* clear state */ ptr_wait = 0; ptr_hold = 0; ptr_uc = 0; ptr_unit.buf = 0; cpls = cpls & ~CPLS_PTR; iosta = iosta & ~IOS_PTR; /* clear flag */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Attach routine */ t_stat ptr_attach (UNIT *uptr, char *cptr) { ptr_leader = PTR_LEADER; /* set up leader */ return attach_unit (uptr, cptr); } /* Bootstrap routine */ int32 ptr_getw (UNIT *uptr) { int32 i, tmp, word; for (i = word = 0; i < 3;) { if ((tmp = getc (uptr->fileref)) == EOF) return -1; uptr->pos = uptr->pos + 1; if (tmp & 0200) { word = (word << 6) | (tmp & 077); i++; } } return word; } t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 origin, val; int32 fld = TA & EPCMASK; for (;;) { if ((val = ptr_getw (&ptr_unit)) < 0) return SCPE_FMT; if (((val & 0760000) == OP_DIO) || /* DIO? */ ((val & 0760000) == OP_DAC)) { /* hack - Macro1 err */ origin = val & DAMASK; if ((val = ptr_getw (&ptr_unit)) < 0) return SCPE_FMT; M[fld | origin] = val; } else if ((val & 0760000) == OP_JMP) { /* JMP? */ PC = fld | (val & DAMASK); break; } else return SCPE_FMT; /* bad instr */ } return SCPE_OK; /* done */ } /* Paper tape punch: IOT routine */ int32 ptp (int32 inst, int32 dev, int32 dat) { iosta = iosta & ~IOS_PTP; /* clear flag */ ptp_unit.buf = (dev == 0006)? ((dat >> 12) | 0200): (dat & 0377); if (GEN_CPLS (inst)) { /* comp pulse? */ ios = 0; cpls = cpls | CPLS_PTP; } else cpls = cpls & ~CPLS_PTP; sim_activate (&ptp_unit, ptp_unit.wait); /* start unit */ return dat; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { if (cpls & CPLS_PTP) { /* completion pulse? */ ios = 1; /* restart */ cpls = cpls & ~CPLS_PTP; } iosta = iosta | IOS_PTP; /* set flag */ dev_req_int (ptp_sbs); /* req interrupt */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return IORETURN (ptp_stopioe, 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 */ cpls = cpls & ~CPLS_PTP; 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 */ if (inst & (IO_WAIT | IO_CPLS)) /* wait or sync? */ return (STOP_RSRV << IOT_V_REASON) | (tty_buf & 077); return tty_buf & 077; } int32 tto (int32 inst, int32 dev, int32 dat) { iosta = iosta & ~IOS_TTO; /* clear flag */ tty_buf = dat & TT_WIDTH; /* load buffer */ if (GEN_CPLS (inst)) { /* comp pulse? */ ios = 0; cpls = cpls | CPLS_TTO; } else cpls = cpls & ~CPLS_TTO; sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ return dat; } /* Unit service routines */ t_stat tti_svc (UNIT *uptr) { int32 in, temp; 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) /* ignore break */ return SCPE_OK; temp = temp & 0177; if (temp == 0177) /* rubout? bs */ temp = '\b'; sim_putchar (temp); /* echo */ if (temp == '\r') /* cr? add nl */ sim_putchar ('\n'); in = ascii_to_fiodec[temp]; /* translate char */ if (in == 0) /* no xlation? */ return SCPE_OK; 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? FIODEC_UC: FIODEC_LC; tti_hold = in | CW; /* set 2nd waiting */ } } iosta = iosta | IOS_TTI; /* set flag */ dev_req_int (tti_sbs); /* req interrupt */ PF = PF | PF_SS_1; /* set prog flag 1 */ uptr->pos = uptr->pos + 1; return SCPE_OK; } t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; if (tty_buf == FIODEC_UC) /* upper case? */ tty_uc = UC; else if (tty_buf == FIODEC_LC) /* lower case? */ tty_uc = 0; else { c = fiodec_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); } } if (cpls & CPLS_TTO) { /* completion pulse? */ ios = 1; /* restart */ cpls = cpls & ~CPLS_TTO; } iosta = iosta | IOS_TTO; /* set flag */ dev_req_int (tto_sbs); /* req interrupt */ 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 */ cpls = cpls & ~CPLS_TTO; 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; } simh-3.8.1/PDP1/pdp1_dcs.c0000644000175000017500000003631411111663444013216 0ustar vlmvlm/* pdp1_dcs.c: PDP-1D terminal multiplexor simulator Copyright (c) 2006-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dcs Type 630 data communications subsystem 19-Nov-2008 RMS Revised for common TMXR show routines This module implements up to 32 individual serial interfaces. */ #include "pdp1_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #define DCS_LINES 32 /* lines */ #define DCS_LINE_MASK (DCS_LINES - 1) #define DCSL_WAIT 1000 /* output wait */ #define DCS_NUMLIN dcs_desc.lines int32 dcs_sbs = 0; /* SBS level */ uint32 dcs_send = 0; /* line for send */ uint32 dcs_scan = 0; /* line for scanner */ uint8 dcs_flg[DCS_LINES]; /* line flags */ uint8 dcs_buf[DCS_LINES]; /* line bufffers */ extern int32 iosta, stop_inst; extern int32 tmxr_poll; TMLN dcs_ldsc[DCS_LINES] = { 0 }; /* line descriptors */ TMXR dcs_desc = { DCS_LINES, 0, 0, dcs_ldsc }; /* mux descriptor */ t_stat dcsi_svc (UNIT *uptr); t_stat dcso_svc (UNIT *uptr); t_stat dcs_reset (DEVICE *dptr); t_stat dcs_attach (UNIT *uptr, char *cptr); t_stat dcs_detach (UNIT *uptr); t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); void dcs_reset_ln (int32 ln); void dcs_scan_next (t_bool unlk); /* DCS data structures dcs_dev DCS device descriptor dcs_unit DCS unit descriptor dcs_reg DCS register list dcs_mod DCS modifiers list */ UNIT dcs_unit = { UDATA (&dcsi_svc, UNIT_ATTABLE, 0) }; REG dcs_reg[] = { { BRDATA (BUF, dcs_buf, 8, 8, DCS_LINES) }, { BRDATA (FLAGS, dcs_flg, 8, 1, DCS_LINES) }, { FLDATA (SCNF, iosta, IOS_V_DCS) }, { ORDATA (SCAN, dcs_scan, 5) }, { ORDATA (SEND, dcs_send, 5) }, { DRDATA (SBSLVL, dcs_sbs, 4), REG_HRO }, { NULL } }; MTAB dcs_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", &dev_set_sbs, &dev_show_sbs, (void *) &dcs_sbs }, { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &dcs_vlines, &tmxr_show_lines, (void *) &dcs_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &dcs_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &dcs_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &dcs_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &dcs_desc }, { 0 } }; DEVICE dcs_dev = { "DCS", &dcs_unit, dcs_reg, dcs_mod, 1, 10, 31, 1, 8, 8, &tmxr_ex, &tmxr_dep, &dcs_reset, NULL, &dcs_attach, &dcs_detach, NULL, DEV_NET | DEV_DISABLE | DEV_DIS }; /* DCSL data structures dcsl_dev DCSL device descriptor dcsl_unit DCSL unit descriptor dcsl_reg DCSL register list dcsl_mod DCSL modifiers list */ UNIT dcsl_unit[] = { { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT } }; MTAB dcsl_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dcs_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &dcs_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &dcs_desc }, { 0 } }; REG dcsl_reg[] = { { URDATA (TIME, dcsl_unit[0].wait, 10, 24, 0, DCS_LINES, REG_NZ + PV_LEFT) }, { NULL } }; DEVICE dcsl_dev = { "DCSL", dcsl_unit, dcsl_reg, dcsl_mod, DCS_LINES, 10, 31, 1, 8, 8, NULL, NULL, &dcs_reset, NULL, NULL, NULL, NULL, DEV_DIS }; /* DCS IOT routine */ int32 dcs (int32 inst, int32 dev, int32 dat) { int32 pls = (inst >> 6) & 077; if (dcs_dev.flags & DEV_DIS) /* disabled? */ return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ if (pls & 020) /* pulse 20? clr IO */ dat = 0; switch (pls & 057) { /* case IR<6,8:11> */ case 000: /* RCH */ dat |= dcs_buf[dcs_scan]; /* return line buf */ dcs_flg[dcs_scan] = 0; /* clr line flag */ break; case 001: /* RRC */ dat |= dcs_scan; /* return line num */ break; case 010: /* RCC */ dat |= dcs_buf[dcs_scan]; /* return line buf */ dcs_flg[dcs_scan] = 0; /* clr line flag */ /* fall through */ case 011: /* RSC */ dcs_scan_next (TRUE); /* unlock scanner */ break; case 040: /* TCB */ dcs_buf[dcs_send] = dat & 0377; /* load buffer */ dcs_flg[dcs_send] = 0; /* clr line flag */ sim_activate (&dcsl_unit[dcs_send], dcsl_unit[dcs_send].wait); break; case 041: /* SSB */ dcs_send = dat & DCS_LINE_MASK; /* load line num */ break; case 050: /* TCC */ dcs_buf[dcs_scan] = dat & 0377; /* load buffer */ dcs_flg[dcs_scan] = 0; /* clr line flag */ sim_activate (&dcsl_unit[dcs_scan], dcsl_unit[dcs_scan].wait); dcs_scan_next (TRUE); /* unlock scanner */ break; default: return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ } /* end case */ return dat; } /* Unit service - receive side Poll all active lines for input Poll for new connections */ t_stat dcsi_svc (UNIT *uptr) { int32 ln, c, out; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; if (dcs_dev.flags & DEV_DIS) return SCPE_OK; sim_activate (uptr, tmxr_poll); /* continue poll */ ln = tmxr_poll_conn (&dcs_desc); /* look for connect */ if (ln >= 0) { /* got one? */ dcs_ldsc[ln].rcve = 1; /* set rcv enable */ } tmxr_poll_rx (&dcs_desc); /* poll for input */ for (ln = 0; ln < DCS_NUMLIN; ln++) { /* loop thru lines */ if (dcs_ldsc[ln].conn) { /* connected? */ if (c = tmxr_getc_ln (&dcs_ldsc[ln])) { /* get char */ if (c & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (c, TT_GET_MODE (dcsl_unit[ln].flags)|TTUF_KSR); dcs_buf[ln] = c; /* save char */ dcs_flg[ln] = 1; /* set line flag */ dcs_scan_next (FALSE); /* kick scanner */ out = sim_tt_outcvt (c & 0177, TT_GET_MODE (dcsl_unit[ln].flags)); if (out >= 0) { tmxr_putc_ln (&dcs_ldsc[ln], out); /* echo char */ tmxr_poll_tx (&dcs_desc); /* poll xmt */ } } } else dcs_ldsc[ln].rcve = 0; /* disconnected */ } /* end for */ return SCPE_OK; } /* Unit service - transmit side */ t_stat dcso_svc (UNIT *uptr) { int32 c; uint32 ln = uptr - dcsl_unit; /* line # */ if (dcs_dev.flags & DEV_DIS) return SCPE_OK; if (dcs_ldsc[ln].conn) { /* connected? */ if (dcs_ldsc[ln].xmte) { /* xmt enabled? */ c = sim_tt_outcvt (dcs_buf[ln] & 0177, TT_GET_MODE (uptr->flags)); if (c >= 0) /* output char */ tmxr_putc_ln (&dcs_ldsc[ln], c); tmxr_poll_tx (&dcs_desc); /* poll xmt */ } else { /* buf full */ tmxr_poll_tx (&dcs_desc); /* poll xmt */ sim_activate (uptr, uptr->wait); /* reschedule */ return SCPE_OK; } } dcs_flg[ln] = 1; /* set line flag */ dcs_scan_next (FALSE); /* kick scanner */ return SCPE_OK; } /* Kick scanner */ void dcs_scan_next (t_bool unlk) { int32 i; if (unlk) /* unlock? */ iosta &= ~IOS_DCS; else if (iosta & IOS_DCS) /* no, locked? */ return; for (i = 0; i < DCS_LINES; i++) { /* scan flags */ dcs_scan = (dcs_scan + 1) & DCS_LINE_MASK; /* next flag */ if (dcs_flg[dcs_scan] != 0) { /* flag set? */ iosta |= IOS_DCS; /* lock scanner */ dev_req_int (dcs_sbs); /* request intr */ return; } } return; } /* Reset routine */ t_stat dcs_reset (DEVICE *dptr) { int32 i; if (dcs_dev.flags & DEV_DIS) /* master disabled? */ dcsl_dev.flags = dcsl_dev.flags | DEV_DIS; /* disable lines */ else dcsl_dev.flags = dcsl_dev.flags & ~DEV_DIS; if (dcs_unit.flags & UNIT_ATT) /* master att? */ sim_activate_abs (&dcs_unit, tmxr_poll); /* activate */ else sim_cancel (&dcs_unit); /* else stop */ for (i = 0; i < DCS_LINES; i++) /* reset lines */ dcs_reset_ln (i); dcs_send = 0; dcs_scan = 0; iosta &= ~IOS_DCS; /* clr intr req */ return SCPE_OK; } /* Attach master unit */ t_stat dcs_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&dcs_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_activate_abs (uptr, tmxr_poll); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat dcs_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&dcs_desc, uptr); /* detach */ for (i = 0; i < DCS_LINES; i++) /* disable rcv */ dcs_ldsc[i].rcve = 0; sim_cancel (uptr); /* stop poll */ return r; } /* Change number of lines */ t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, DCS_LINES, &r); if ((r != SCPE_OK) || (newln == DCS_NUMLIN)) return r; if (newln == 0) return SCPE_ARG; if (newln < DCS_LINES) { for (i = newln, t = 0; i < DCS_NUMLIN; i++) t = t | dcs_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < DCS_NUMLIN; i++) { if (dcs_ldsc[i].conn) { tmxr_linemsg (&dcs_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&dcs_ldsc[i]); /* reset line */ } dcsl_unit[i].flags = dcsl_unit[i].flags | UNIT_DIS; dcs_reset_ln (i); } } else { for (i = DCS_NUMLIN; i < newln; i++) { dcsl_unit[i].flags = dcsl_unit[i].flags & ~UNIT_DIS; dcs_reset_ln (i); } } DCS_NUMLIN = newln; return SCPE_OK; } /* Reset an individual line */ void dcs_reset_ln (int32 ln) { sim_cancel (&dcsl_unit[ln]); dcs_buf[ln] = 0; dcs_flg[ln] = 0; return; } simh-3.8.1/PDP1/pdp1_sys.c0000644000175000017500000005635411112107064013261 0ustar vlmvlm/* pdp1_sys.c: PDP-1 simulator interface Copyright (c) 1993-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 03-Jan-07 RMS Fixed bugs in block loader, char input 21-Dec-06 RMS Added 16-channel sequence break support, PDP-1D support 06-Apr-04 RMS Fixed bug in binary loader (found by Mark Crispin) 08-Feb-04 PLB Merged display support 08-Dec-03 RMS Added parallel drum support, drum mnemonics 18-Oct-03 RMS Added DECtape off reel message 01-Sep-03 RMS Added support for loading in multiple fields 22-Jul-03 RMS Updated for "hardware" RIM loader 05-Dec-02 RMS Added drum support 21-Nov-02 RMS Changed typewriter to half duplex 20-Aug-02 RMS Added DECtape support 17-Sep-01 RMS Removed multiconsole support 13-Jul-01 RMS Fixed RIM loader format 27-May-01 RMS Added multiconsole support 14-Mar-01 RMS Revised load/dump interface (again) 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface 20-Oct-97 RMS Fixed endian-dependence in RIM loader (found by Michael Somos) */ #include "pdp1_defs.h" #include extern DEVICE cpu_dev; extern DEVICE clk_dev; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE tti_dev; extern DEVICE tto_dev; extern DEVICE lpt_dev; extern DEVICE dt_dev; extern DEVICE drm_dev; extern DEVICE drp_dev; extern DEVICE dcs_dev, dcsl_dev; extern DEVICE dpy_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern int32 M[]; extern int32 PC; extern int32 ascii_to_fiodec[], fiodec_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[] = "PDP-1"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &clk_dev, &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, &lpt_dev, &dt_dev, &drm_dev, &drp_dev, &dcs_dev, &dcsl_dev, /* &dpy_dev, */ 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" }; /* Binary loader - supports both RIM format and Macro block format */ int32 pdp1_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; } t_stat rim_load (FILE *inf, int32 fld) { int32 origin, val; for (;;) { if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; if (((val & 0760000) == OP_DIO) || /* DIO? */ ((val & 0760000) == OP_DAC)) { /* hack - Macro1 err */ origin = val & DAMASK; if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; M[fld | origin] = val; } else if ((val & 0760000) == OP_JMP) { /* JMP? */ PC = fld | (val & DAMASK); break; } else return SCPE_FMT; /* bad instr */ } return SCPE_OK; /* done */ } t_stat blk_load (FILE *inf, int32 fld) { int32 val, start, count, csum; for (;;) { if ((val = pdp1_getw (inf)) < 0) /* get word, EOF? */ return SCPE_FMT; if ((val & 0760000) == OP_DIO) { /* DIO? */ csum = val; /* init checksum */ start = val & DAMASK; /* starting addr */ if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; if ((val & 0760000) != OP_DIO) return SCPE_FMT; csum = csum + val; if (csum > DMASK) csum = (csum + 1) & DMASK; count = (val & DAMASK) - start; /* block count */ if (count <= 0) return SCPE_FMT; while (count--) { /* loop on data */ if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; csum = csum + val; if (csum > DMASK) csum = (csum + 1) & DMASK; M[fld | start] = val; start = (start + 1) & DAMASK; } if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; if (val != csum) return SCPE_CSUM; } else if ((val & 0760000) == OP_JMP) { /* JMP? */ PC = fld | (val & DAMASK); break; } else return SCPE_FMT; /* bad instr */ } return SCPE_OK; /* done */ } t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { t_stat sta; int32 fld; if (flag != 0) return SCPE_ARG; if (cptr && (*cptr != 0)) { fld = get_uint (cptr, 8, AMASK, &sta); if (sta != SCPE_OK) return sta; fld = fld & EPCMASK; } else fld = 0; sta = rim_load (fileref, fld); if (sta != SCPE_OK) return sta; if ((sim_switches & SWMASK ('B')) || match_ext (fnam, "BIN")) return blk_load (fileref, fld); return SCPE_OK; } /* Symbol tables */ #define I_V_FL 18 /* inst class */ #define I_M_FL 017 /* class mask */ #define I_V_NPN 0 /* no operand */ #define I_V_IOT 1 /* IOT */ #define I_V_LAW 2 /* LAW */ #define I_V_MRF 3 /* memory reference */ #define I_V_MRI 4 /* mem ref no ind */ #define I_V_OPR 5 /* OPR */ #define I_V_SKP 6 /* skip */ #define I_V_SHF 7 /* shift */ #define I_V_SPC 8 /* special */ #define I_NPN (I_V_NPN << I_V_FL) /* no operand */ #define I_IOT (I_V_IOT << I_V_FL) /* IOT */ #define I_LAW (I_V_LAW << I_V_FL) /* LAW */ #define I_MRF (I_V_MRF << I_V_FL) /* memory reference */ #define I_MRI (I_V_MRI << I_V_FL) /* mem ref no ind */ #define I_OPR (I_V_OPR << I_V_FL) /* OPR */ #define I_SKP (I_V_SKP << I_V_FL) /* skip */ #define I_SHF (I_V_SHF << I_V_FL) /* shift */ #define I_SPC (I_V_SPC << I_V_FL) static const int32 masks[] = { 0777777, 0760077, 0760000, 0760000, 0770000, 0760017, 0760077, 0777000, 0760003 }; static const char *opcode[] = { "AND", "IOR", "XOR", "XCT", /* mem refs */ "LAC", "LIO", "DAC", "DAP", "DIP", "DIO", "DZM", "ADD", "SUB", "IDX", "ISP", "SAD", "SAS", "MUL", "DIV", "JMP", "JSP", "LCH", "DCH", "TAD", "CAL", "JDA", /* mem ref no ind */ "LAW", "IOH", "RPA", "RPB", "RRB", /* I/O instructions */ "PPA", "PPB", "TYO", "TYI", "DPY", "DSC", "ASC", "ISC", "CAC", "LSM", "ESM", "CBS", "LEM", "EEM", "CKS", "MSE", "MLC", "MRD", "MWR", "MRS", "DIA", "DBA", "DWC", "DRA", "DCL", "DRD", "DWR", "DBL", "DCN", "DTD", "DSE", "DSP", "LRG", "ERG", "LRM", "ERM", "RNM", "RSM", "RCK", "CTB", "RCH", "RCC", "TCC", "TCB", "RRC", "SSB", "RSC", "SKP", "SKP I", "CLO", /* base as NPNs */ "SFT", "SPC", "OPR", "RAL", "RIL", "RCL", /* shifts */ "SAL", "SIL", "SCL", "RAR", "RIR", "RCR", "SAR", "SIR", "SCR", "SZF1", "SZF2", "SZF3", /* skips */ "SZF4", "SZF5", "SZF6", "SZF7", "SZS1", "SZS1 SZF1", "SZS1 SZF2", "SZS1 SZ3", "SZS1 SZF4", "SZS1 SZF5", "SZS1 SZF6", "SZS1 SZF7", "SZS2", "SZS2 SZF1", "SZS2 SZF2", "SZS2 SZ3", "SZS2 SZF4", "SZS2 SZF5", "SZS2 SZF6", "SZS2 SZF7", "SZS3", "SZS3 SZF1", "SZS3 SZF2", "SZS3 SZ3", "SZS3 SZF4", "SZS3 SZF5", "SZS3 SZF6", "SZS3 SZF7", "SZS4", "SZS4 SZF1", "SZS4 SZF2", "SZS4 SZ3", "SZS4 SZF4", "SZS4 SZF5", "SZS4 SZF6", "SZS4 SZF7", "SZS5", "SZS5 SZF1", "SZS5 SZF2", "SZS5 SZ3", "SZS5 SZF4", "SZS5 SZF5", "SZS5 SZF6", "SZS5 SZF7", "SZS6", "SZS6 SZF1", "SZS6 SZF2", "SZS6 SZ3", "SZS6 SZF4", "SZS6 SZF5", "SZS6 SZF6", "SZS6 SZF7", "SZS7", "SZS7 SZF1", "SZS7 SZF2", "SZS7 SZ3", "SZS7 SZF4", "SZS7 SZF5", "SZS7 SZF6", "SZS7 SZF7", "CLF1", "CLF2", "CLF3", /* operates */ "CLF4", "CLF5", "CLF6", "CLF7", "STF1", "STF2", "STF3", "STF4", "STF5", "STF6", "STF7", "FF1", "FF2", "FF3", /* specials */ "SZA", "SPA", "SMA", /* uprog skips */ "SZO", "SPI", "SNI", "I", /* encode only */ "LIA", "LAI", "SWP", /* uprog opers */ "LAP", "CLA", "HLT", "CMA", "LAT", "CLI", "CMI", "CML", "CLL", "SZL", /* uprog specials */ "SCF", "SCI", "SCM", "IDA", "IDC", "IFI", "IIF", NULL, NULL, NULL, /* decode only */ NULL, }; static const int32 opc_val[] = { 0020000+I_MRF, 0040000+I_MRF, 0060000+I_MRF, 0100000+I_MRF, 0200000+I_MRF, 0220000+I_MRF, 0240000+I_MRF, 0260000+I_MRF, 0300000+I_MRF, 0320000+I_MRF, 0340000+I_MRF, 0400000+I_MRF, 0420000+I_MRF, 0440000+I_MRF, 0460000+I_MRF, 0500000+I_MRF, 0520000+I_MRF, 0540000+I_MRF, 0560000+I_MRF, 0600000+I_MRF, 0620000+I_MRF, 0120000+I_MRF, 0140000+I_MRF, 0360000+I_MRF, 0160000+I_MRI, 0170000+I_MRI, 0700000+I_LAW, 0730000+I_NPN, 0720001+I_IOT, 0720002+I_IOT, 0720030+I_IOT, 0720005+I_IOT, 0720006+I_IOT, 0720003+I_IOT, 0720004+I_IOT, 0720007+I_IOT, 0720050+I_IOT, 0720051+I_IOT, 0720052+I_IOT, 0720053+I_NPN, 0720054+I_NPN, 0720055+I_NPN, 0720056+I_NPN, 0720074+I_NPN, 0724074+I_NPN, 0720033+I_NPN, 0720301+I_NPN, 0720401+I_NPN, 0720501+I_NPN, 0720601+I_NPN, 0720701+I_NPN, 0720061+I_NPN, 0722061+I_NPN, 0720062+I_NPN, 0722062+I_NPN, 0720063+I_NPN, 0720161+I_NPN, 0721161+I_NPN, 0720162+I_NPN, 0721162+I_NPN, 0720163+I_NPN, 0720164+I_NPN, 0721164+I_NPN, 0720010+I_NPN, 0720011+I_NPN, 0720064+I_NPN, 0720065+I_NPN, 0720066+I_IOT, 0720067+I_NPN, 0720032+I_NPN, 0720035+I_NPN, 0720022+I_NPN, 0721022+I_NPN, 0725022+I_NPN, 0724022+I_NPN, 0720122+I_NPN, 0724122+I_NPN, 0721122+I_NPN, 0640000+I_NPN, 0650000+I_NPN, 0651600+I_NPN, 0660000+I_NPN, 0740000+I_NPN, 0760000+I_NPN, 0661000+I_SHF, 0662000+I_SHF, 0663000+I_SHF, 0665000+I_SHF, 0666000+I_SHF, 0667000+I_SHF, 0671000+I_SHF, 0672000+I_SHF, 0673000+I_SHF, 0675000+I_SHF, 0676000+I_SHF, 0677000+I_SHF, 0640001+I_SKP, 0640002+I_SKP, 0640003+I_SKP, 0640004+I_SKP, 0640005+I_SKP, 0640006+I_SKP, 0640007+I_SKP, 0640010+I_SKP, 0640011+I_SKP, 0640012+I_SKP, 0640013+I_SKP, 0640014+I_SKP, 0640015+I_SKP, 0640016+I_SKP, 0640017+I_SKP, 0640020+I_SKP, 0640021+I_SKP, 0640022+I_SKP, 0640023+I_SKP, 0640024+I_SKP, 0640025+I_SKP, 0640026+I_SKP, 0640027+I_SKP, 0640030+I_SKP, 0640031+I_SKP, 0640032+I_SKP, 0640033+I_SKP, 0640034+I_SKP, 0640035+I_SKP, 0640036+I_SKP, 0640037+I_SKP, 0640040+I_SKP, 0640041+I_SKP, 0640042+I_SKP, 0640043+I_SKP, 0640044+I_SKP, 0640045+I_SKP, 0640046+I_SKP, 0640047+I_SKP, 0640050+I_SKP, 0640051+I_SKP, 0640052+I_SKP, 0640053+I_SKP, 0640054+I_SKP, 0640055+I_SKP, 0640056+I_SKP, 0640057+I_SKP, 0640060+I_SKP, 0640061+I_SKP, 0640062+I_SKP, 0640063+I_SKP, 0640064+I_SKP, 0640065+I_SKP, 0640066+I_SKP, 0640067+I_SKP, 0640070+I_SKP, 0640071+I_SKP, 0640072+I_SKP, 0640073+I_SKP, 0640074+I_SKP, 0640075+I_SKP, 0640076+I_SKP, 0640077+I_SKP, 0760001+I_OPR, 0760002+I_OPR, 0760003+I_OPR, 0760004+I_OPR, 0760005+I_OPR, 0760006+I_OPR, 0760007+I_OPR, 0760011+I_OPR, 0760012+I_OPR, 0760013+I_OPR, 0760014+I_OPR, 0760015+I_OPR, 0760016+I_OPR, 0760017+I_OPR, 0740001+I_SPC, 0740002+I_SPC, 0740003+I_OPR, 0640100+I_SKP, 0640200+I_SKP, 0640400+I_SKP, 0641000+I_SKP, 0642000+I_SKP, 0644000+I_SKP, 0010000+I_SKP, /* encode only */ 0760020+I_OPR, 0760040+I_OPR, 0760060+I_NPN, 0760100+I_OPR, 0760200+I_OPR, 0760400+I_OPR, 0761000+I_OPR, 0762000+I_OPR, 0764000+I_OPR, 0770000+I_OPR, 0740004+I_SPC, 0740010+I_SPC, 0740020+I_SPC, 0740040+I_SPC, 0740100+I_SPC, 0740200+I_SPC, 0740400+I_SPC, 0741000+I_SPC, 0742000+I_SPC, 0744000+I_SPC, 0640000+I_SKP, 0740000+I_SPC, 0760000+I_OPR, /* decode only */ -1 }; /* Operate or skip decode Inputs: *of = output stream inst = mask bits class = instruction class code sp = space needed? Outputs: status = space needed? */ int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) { int32 i, j; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((j == class) && (opc_val[i] & inst)) { /* same class? */ inst = inst & ~opc_val[i]; /* mask bit set? */ fprintf (of, (sp? " %s": "%s"), opcode[i]); sp = 1; } } return sp; } /* 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) fiodec_to_ascii[x] #define ASCTOSIX(x) (ascii_to_fiodec[x] & 077) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, sp, inst, disp, ma; 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 (fiodec_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 */ disp = inst & 007777; ma = (addr & EPCMASK) | disp; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: /* no operands */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_IOT: /* IOT */ disp = (inst - opc_val[i]) & 017777; if (disp == IA) fprintf (of, "%s I", opcode[i]); else if (disp) fprintf (of, "%s %-o", opcode[i], disp); else fprintf (of, "%s", opcode[i]); break; case I_V_LAW: /* LAW */ cflag = 0; /* fall thru to MRF */ case I_V_MRF: /* mem ref */ fprintf (of, "%s%s%-o", opcode[i], ((inst & IA)? " I ": " "), (cflag? ma: disp)); break; case I_V_MRI: /* mem ref no ind */ fprintf (of, "%s %-o", opcode[i], (cflag? ma: disp)); break; case I_V_OPR: /* operates */ sp = fprint_opr (of, inst & 017760, j, 0); if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); break; case I_V_SKP: /* skips */ sp = fprint_opr (of, inst & 007700, j, 0); if (opcode[i]) sp = fprintf (of, (sp? " %s": "%s"), opcode[i]); if (inst & IA) fprintf (of, sp? " I": "I"); break; case I_V_SPC: /* specials */ sp = fprint_opr (of, inst & 007774, j, 0); if (opcode[i]) sp = fprintf (of, (sp? " %s": "%s"), opcode[i]); if (inst & IA) fprintf (of, sp? " I": "I"); break; case I_V_SHF: /* shifts */ fprintf (of, "%s %-d", opcode[i], sc_map[inst & 0777]); break; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* 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) { 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) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0]; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; 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) /* junk at end? */ return SCPE_ARG; return SCPE_OK; } simh-3.8.1/sim_tape.h0000644000175000017500000001474611111655550012627 0ustar vlmvlm/* sim_tape.h: simulator tape support library definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Aug-06 JDB Added erase gap support 14-Feb-06 RMS Added variable tape capacity 17-Dec-05 RMS Added write support for Paul Pierce 7b format 02-May-05 RMS Added support for Paul Pierce 7b format */ #ifndef _SIM_TAPE_H_ #define _SIM_TAPE_H_ 0 /* SIMH/E11 tape format */ typedef uint32 t_mtrlnt; /* magtape rec lnt */ #define MTR_TMK 0x00000000 /* tape mark */ #define MTR_EOM 0xFFFFFFFF /* end of medium */ #define MTR_GAP 0xFFFFFFFE /* primary gap */ #define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */ #define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */ #define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */ #define MTR_MAXLEN 0x00FFFFFF /* max len is 24b */ #define MTR_ERF 0x80000000 /* error flag */ #define MTR_F(x) ((x) & MTR_ERF) /* record error flg */ #define MTR_L(x) ((x) & ~MTR_ERF) /* record length */ /* TPC tape format */ typedef uint16 t_tpclnt; /* magtape rec lnt */ /* P7B tape format */ #define P7B_SOR 0x80 /* start of record */ #define P7B_PAR 0x40 /* parity */ #define P7B_DATA 0x3F /* data */ #define P7B_DPAR (P7B_PAR|P7B_DATA) /* data and parity */ #define P7B_EOF 0x0F /* eof character */ #define TPC_TMK 0x0000 /* tape mark */ /* Unit flags */ #define MTUF_V_PNU (UNIT_V_UF + 0) /* position not upd */ #define MTUF_V_WLK (UNIT_V_UF + 1) /* write locked */ #define MTUF_V_FMT (UNIT_V_UF + 2) /* tape file format */ #define MTUF_W_FMT 3 /* 3b of formats */ #define MTUF_N_FMT (1u << MTUF_W_FMT) /* number of formats */ #define MTUF_M_FMT ((1u << MTUF_W_FMT) - 1) #define MTUF_F_STD 0 /* SIMH format */ #define MTUF_F_E11 1 /* E11 format */ #define MTUF_F_TPC 2 /* TPC format */ #define MTUF_F_P7B 3 /* P7B format */ #define MUTF_F_TDF 4 /* TDF format */ #define MTUF_V_UF (MTUF_V_FMT + MTUF_W_FMT) #define MTUF_PNU (1u << MTUF_V_PNU) #define MTUF_WLK (1u << MTUF_V_WLK) #define MTUF_FMT (MTUF_M_FMT << MTUF_V_FMT) #define MTUF_WRP (MTUF_WLK | UNIT_RO) #define MT_F_STD (MTUF_F_STD << MTUF_V_FMT) #define MT_F_E11 (MTUF_F_E11 << MTUF_V_FMT) #define MT_F_TPC (MTUF_F_TPC << MTUF_V_FMT) #define MT_F_P7B (MTUF_F_P7B << MTUF_V_FMT) #define MT_F_TDF (MTUF_F_TDF << MTUF_V_FMT) #define MT_SET_PNU(u) (u)->flags = (u)->flags | MTUF_PNU #define MT_CLR_PNU(u) (u)->flags = (u)->flags & ~MTUF_PNU #define MT_TST_PNU(u) ((u)->flags & MTUF_PNU) #define MT_GET_FMT(u) (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT) /* Return status codes */ #define MTSE_OK 0 /* no error */ #define MTSE_TMK 1 /* tape mark */ #define MTSE_UNATT 2 /* unattached */ #define MTSE_IOERR 3 /* IO error */ #define MTSE_INVRL 4 /* invalid rec lnt */ #define MTSE_FMT 5 /* invalid format */ #define MTSE_BOT 6 /* beginning of tape */ #define MTSE_EOM 7 /* end of medium */ #define MTSE_RECE 8 /* error in record */ #define MTSE_WRP 9 /* write protected */ /* Prototypes */ t_stat sim_tape_attach (UNIT *uptr, char *cptr); t_stat sim_tape_detach (UNIT *uptr); t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc); t_stat sim_tape_wrtmk (UNIT *uptr); t_stat sim_tape_wreom (UNIT *uptr); t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi); t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc); t_stat sim_tape_rewind (UNIT *uptr); t_stat sim_tape_reset (UNIT *uptr); t_bool sim_tape_bot (UNIT *uptr); t_bool sim_tape_wrp (UNIT *uptr); t_bool sim_tape_eot (UNIT *uptr); t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/PDP18B/0000755000175000017500000000000011112110732011524 5ustar vlmvlmsimh-3.8.1/PDP18B/pdp18b_mt.c0000644000175000017500000005153311107424616013512 0ustar vlmvlm/* pdp18b_mt.c: 18b PDP magnetic tape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. mt (PDP-9) TC59 magtape (PDP-15) TC59D magtape 14-Nov-08 RMS Replaced mt_log with standard debug facility 16-Feb-06 RMS Added tape capacity checking 16-Aug-05 RMS Fixed C++ declaration and cast problems 18-Mar-05 RMS Added attached test to detach routine 14-Jan-04 RMS Revised IO device call interface 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 04-Mar-03 RMS Fixed bug in MTTR 01-Mar-03 RMS Fixed bug in interrupt handling Revised for magtape library 02-Feb-03 RMS Revised IOT decoding 30-Oct-02 RMS Revised BOT handling, added error record handling 05-Oct-02 RMS Added DIB, device number support Revamped error recovery 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length test 06-Jan-02 RMS Revised enabled/disable support 29-Nov-01 RMS Added read only unit support 25-Nov-01 RMS Revised interrupt structure Changed UST, POS, FLG to arrays 26-Apr-01 RMS Added device enable/disable support 15-Feb-01 RMS Fixed 3-cycle data break sequence 04-Oct-98 RMS V2.4 magtape format 22-Jan-97 RMS V2.3 magtape format 29-Jun-96 RMS Added unit enable/disable support Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32 byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. */ #include "pdp18b_defs.h" #include "sim_tape.h" #define MT_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ #define MT_MAXFR (1 << 16) /* max record length */ #define MT_WC 032 /* word count */ #define MT_CA 033 /* current addr */ #define WC_SIZE (1 << 12) /* max word count */ #define WC_MASK (WC_SIZE - 1) /* Command/unit - mt_cu */ #define CU_V_UNIT 15 /* unit */ #define CU_M_UNIT 07 #define CU_PARITY 0040000 /* parity select */ #define CU_DUMP 0020000 /* dump mode */ #define CU_ERASE 0010000 /* ext rec gap */ #define CU_V_CMD 9 /* command */ #define CU_M_CMD 07 #define FN_NOP 00 #define FN_REWIND 01 #define FN_READ 02 #define FN_CMPARE 03 #define FN_WRITE 04 #define FN_WREOF 05 #define FN_SPACEF 06 #define FN_SPACER 07 #define CU_IE 0000400 /* interrupt enable */ #define CU_V_TYPE 6 /* drive type */ #define CU_M_TYPE 03 #define TY_9TK 3 #define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) #define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) #define GET_TYPE(x) (((x) >> CU_V_TYPE) & CU_M_TYPE) #define PACKED(x) (((x) & CU_DUMP) || (GET_TYPE (x) != TY_9TK)) /* Status - stored in mt_sta or (*) uptr->USTAT */ #define STA_ERR 0400000 /* error */ #define STA_REW 0200000 /* *rewinding */ #define STA_BOT 0100000 /* *start of tape */ #define STA_ILL 0040000 /* illegal cmd */ #define STA_PAR 0020000 /* parity error */ #define STA_EOF 0010000 /* *end of file */ #define STA_EOT 0004000 /* *end of tape */ #define STA_CPE 0002000 /* compare error */ #define STA_RLE 0001000 /* rec lnt error */ #define STA_DLT 0000400 /* data late */ #define STA_BAD 0000200 /* bad tape */ #define STA_DON 0000100 /* done */ #define STA_CLR 0000077 /* always clear */ #define STA_DYN (STA_REW | STA_BOT | STA_EOF | STA_EOT) /* kept in USTAT */ extern int32 M[]; extern int32 int_hwre[API_HLVL+1]; extern UNIT cpu_unit; extern FILE *sim_deb; int32 mt_cu = 0; /* command/unit */ int32 mt_sta = 0; /* status register */ int32 mt_time = 10; /* record latency */ int32 mt_stopioe = 1; /* stop on error */ uint8 *mtxb = NULL; /* transfer buffer */ DEVICE mt_dev; int32 mt (int32 dev, int32 pulse, int32 dat); int32 mt_iors (void); t_stat mt_svc (UNIT *uptr); t_stat mt_reset (DEVICE *dptr); t_stat mt_attach (UNIT *uptr, char *cptr); t_stat mt_detach (UNIT *uptr); int32 mt_updcsta (UNIT *uptr, int32 val); t_stat mt_map_err (UNIT *uptr, t_stat st); UNIT *mt_busy (void); /* MT data structures mt_dev MT device descriptor mt_unit MT unit list mt_reg MT register list mt_mod MT modifier list */ DIB mt_dib = { DEV_MT, 1, &mt_iors, { &mt } }; UNIT mt_unit[] = { { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } }; REG mt_reg[] = { { ORDATA (STA, mt_sta, 18) }, { ORDATA (CMD, mt_cu, 18) }, { ORDATA (WC, M[MT_WC], 18) }, { ORDATA (CA, M[MT_CA], 18) }, { FLDATA (INT, int_hwre[API_MTA], INT_V_MTA) }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { DRDATA (TIME, mt_time, 24), PV_LEFT }, { URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) }, { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT | REG_RO) }, { ORDATA (DEVNO, mt_dib.dev, 6), REG_HRO }, { NULL } }; MTAB mt_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno, NULL }, { 0 } }; DEVICE mt_dev = { "MT", mt_unit, mt_reg, mt_mod, MT_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach, &mt_dib, DEV_DISABLE | DEV_DEBUG }; /* IOT routine */ int32 mt (int32 dev, int32 pulse, int32 dat) { int32 f, sb; UNIT *uptr; uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ mt_updcsta (uptr, 0); /* update status */ sb = pulse & 060; /* subop */ if (pulse & 01) { if ((sb == 000) && (uptr->flags & UNIT_ATT) && /* MTTR */ !sim_is_active (uptr)) dat = IOT_SKP | dat; else if ((sb == 020) && !mt_busy ()) /* MTCR */ dat = IOT_SKP | dat; else if ((sb == 040) && (mt_sta & (STA_ERR | STA_DON))) /* MTSF */ dat = IOT_SKP | dat; } if ((pulse & 06) && DEBUG_PRS (mt_dev)) fprintf (sim_deb, "[MT%d: IOT=%o, AC=%o, sta=%o]\n", GET_UNIT (mt_cu), 0707300 + pulse, dat, mt_sta); if (pulse & 02) { if (sb == 000) /* MTRC */ dat = dat | (mt_cu & 0777700); else if (sb == 020) { /* MTAF, MTLC */ if (!mt_busy ()) /* if not busy, clr */ mt_cu = mt_sta = 0; mt_sta = mt_sta & ~(STA_ERR | STA_DON); /* clear flags */ } else if (sb == 040) dat = dat | mt_sta; /* MTRS */ } if (pulse & 04) { if (sb == 000) { /* MTGO */ f = GET_CMD (mt_cu); /* get function */ if (mt_busy () || sim_is_active (uptr) || (f == FN_NOP) || (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr->USTAT & STA_BOT)) || (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) || ((uptr->flags & UNIT_ATT) == 0)) mt_sta = mt_sta | STA_ILL | STA_ERR; /* set illegal op */ else { if (f == FN_REWIND) /* rewind? */ uptr->USTAT = STA_REW; else mt_sta = uptr->USTAT = 0; /* no, clear status */ sim_activate (uptr, mt_time); /* start io */ } } if (sb == 020) /* MTCM, MTLC */ mt_cu = (mt_cu & 0770700) | (dat & 0777700); /* load status */ } mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ return dat; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt */ t_stat mt_svc (UNIT *uptr) { int32 c, c1, c2, c3, f, i, p, u; int32 wc, xma; t_mtrlnt tbc, cbc; t_bool passed_eot; t_stat st, r = SCPE_OK; u = (int32) (uptr - mt_dev.units); /* get unit number */ f = GET_CMD (mt_cu); /* get command */ wc = WC_SIZE - (M[MT_WC] & WC_MASK); /* word count is 12b */ if (uptr->USTAT & STA_REW) { /* rewind? */ sim_tape_rewind (uptr); /* rewind tape */ if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT; else uptr->USTAT = 0; if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr, STA_DON); if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, "[MT%d: rewind complete, sta=%o]\n", u, mt_sta); return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ mt_updcsta (uptr, STA_ILL); /* illegal operation */ return IORETURN (mt_stopioe, SCPE_UNATT); } passed_eot = sim_tape_eot (uptr); /* passed EOT? */ switch (f) { /* case on function */ case FN_READ: /* read */ case FN_CMPARE: /* read/compare */ st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ if (st == MTSE_RECE) /* rec in err? */ mt_sta = mt_sta | STA_PAR | STA_ERR; else if (st != MTSE_OK) { /* other error? */ mt_sta = mt_sta | STA_RLE | STA_ERR; /* set RLE flag */ r = mt_map_err (uptr, st); /* map error */ break; } cbc = PACKED (mt_cu)? wc * 3: wc * 2; /* expected bc */ if (tbc != cbc) /* wrong size? */ mt_sta = mt_sta | STA_RLE | STA_ERR; if (tbc < cbc) { /* record small? */ cbc = tbc; /* use smaller */ wc = PACKED (mt_cu)? ((tbc + 2) / 3): ((tbc + 1) / 2); } for (i = p = 0; i < wc; i++) { /* copy buffer */ M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC, CA */ M[MT_CA] = (M[MT_CA] + 1) & DMASK; xma = M[MT_CA] & AMASK; if (PACKED (mt_cu)) { /* packed? */ c1 = mtxb[p++] & 077; c2 = mtxb[p++] & 077; c3 = mtxb[p++] & 077; c = (c1 << 12) | (c2 << 6) | c3; } else { c1 = mtxb[p++]; c2 = mtxb[p++]; c = (c1 << 8) | c2; } if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; else if ((f == FN_CMPARE) && (c != (M[xma] & (PACKED (mt_cu)? DMASK: 0177777)))) { mt_updcsta (uptr, STA_CPE); break; } } /* end for */ break; case FN_WRITE: /* write */ tbc = PACKED (mt_cu)? wc * 3: wc * 2; xma = M[MT_CA] & AMASK; /* get mem addr */ for (i = p = 0; i < wc; i++) { /* copy buf to tape */ xma = (xma + 1) & AMASK; /* incr mem addr */ if (PACKED (mt_cu)) { /* packed? */ mtxb[p++] = (M[xma] >> 12) & 077; mtxb[p++] = (M[xma] >> 6) & 077; mtxb[p++] = M[xma] & 077; } else { mtxb[p++] = (M[xma] >> 8) & 0377; mtxb[p++] = M[xma] & 0377; } } /* end for */ if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) /* write rec, err? */ r = mt_map_err (uptr, st); /* map error */ else { M[MT_CA] = (M[MT_CA] + wc) & DMASK; /* advance mem addr */ M[MT_WC] = 0; /* clear word cnt */ } mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */ break; case FN_WREOF: if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = mt_map_err (uptr, st); /* map error */ else uptr->USTAT = STA_EOF; mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */ break; case FN_SPACEF: /* space forward */ do { M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ r = mt_map_err (uptr, st); /* map error */ break; } } while ((M[MT_WC] != 0) && (passed_eot || !sim_tape_eot (uptr))); break; case FN_SPACER: /* space reverse */ do { M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ r = mt_map_err (uptr, st); /* map error */ break; } } while (M[MT_WC] != 0); break; } /* end case */ if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ uptr->USTAT = uptr->USTAT | STA_EOT; mt_updcsta (uptr, STA_DON); /* set done */ if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, "MT%d: fnc=%d done, ma=%o, wc=%o, sta=%o]\n", u, f, M[MT_CA], M[MT_WC], mt_sta); return r; } /* Update controller status */ int32 mt_updcsta (UNIT *uptr, int32 news) { mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN) | news; if ((mt_sta & (STA_ERR | STA_DON)) && (mt_cu & CU_IE)) SET_INT (MTA); else CLR_INT (MTA); /* int request */ return mt_sta; } /* Test if controller busy */ UNIT *mt_busy (void) { int32 u; UNIT *uptr; for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ uptr = mt_dev.units + u; if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0)) return uptr; } return NULL; } /* Map tape error status */ t_stat mt_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ mt_sta = mt_sta | STA_ILL | STA_ERR; case MTSE_OK: /* no error */ return SCPE_IERR; case MTSE_TMK: /* end of file */ uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */ mt_sta = mt_sta | STA_ERR; break; case MTSE_IOERR: /* IO error */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ if (mt_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ break; case MTSE_EOM: /* end of medium */ mt_sta = mt_sta | STA_BAD | STA_ERR; /* set end tape */ break; case MTSE_BOT: /* reverse into BOT */ uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ mt_sta = mt_sta | STA_ERR; break; case MTSE_WRP: /* write protect */ mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ break; } return SCPE_OK; } /* Reset routine */ t_stat mt_reset (DEVICE *dptr) { int32 u; UNIT *uptr; mt_cu = mt_sta = 0; for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ uptr = mt_dev.units + u; sim_tape_reset (uptr); /* reset tape */ sim_cancel (uptr); /* cancel activity */ if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT; else uptr->USTAT = 0; } mt_updcsta (&mt_unit[0], 0); /* update status */ if (mtxb == NULL) mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); if (mtxb == NULL) return SCPE_MEM; return SCPE_OK; } /* IORS routine */ int32 mt_iors (void) { return (mt_sta & (STA_ERR | STA_DON))? IOS_MTA: 0; } /* Attach routine */ t_stat mt_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->USTAT = STA_BOT; mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ return r; } /* Detach routine */ t_stat mt_detach (UNIT* uptr) { if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (!sim_is_active (uptr)) uptr->USTAT = 0; mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ return sim_tape_detach (uptr); } simh-3.8.1/PDP18B/pdp18b_dt.c0000644000175000017500000020422611112110436013464 0ustar vlmvlm/* pdp18b_dt.c: 18b DECtape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dt (PDP-4, PDP-7) Type 550/555 DECtape (PDP-9) TC02/TU55 DECtape (PDP-15) TC15/TU56 DECtape 23-Jun-06 RMS Fixed switch conflict in ATTACH Revised Type 550 header based on DECTOG formatter 13-Jun-06 RMS Fixed checksum calculation bug in Type 550 16-Aug-05 RMS Fixed C++ declaration and cast problems 25-Jan-04 RMS Revised for device debug support 14-Jan-04 RMS Revised IO device call interface Changed sim_fsize calling sequence, added STOP_OFFR 26-Oct-03 RMS Cleaned up buffer copy code 18-Oct-03 RMS Fixed reverse checksum in read all Added DECtape off reel message Simplified timing 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 17-Oct-02 RMS Fixed bug in end of reel logic 05-Oct-02 RMS Added DIB, device number support 12-Sep-02 RMS Added 16b format support 13-Aug-02 RMS Corrected Type 550 unit select logic 25-Jul-02 RMS Added PDP-4 support 30-May-02 RMS Widened POS to 32b 10-Feb-02 RMS Added PDP-7 support 06-Jan-02 RMS Revised enable/disable support 29-Nov-01 RMS Added read only unit support 25-Nov-01 RMS Revised interrupt structure Changed POS, STATT, LASTT, FLG to arrays 29-Aug-01 RMS Added casts to PDP-8 unpack routine 17-Jul-01 RMS Moved function prototype 11-May-01 RMS Fixed bug in reset 26-Apr-01 RMS Added device enable/disable support 15-Mar-01 RMS Added 129th word to PDP-8 format 18b DECtapes are represented in memory by fixed length buffer of 32b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the PDP-7 formatter) is: reverse end zone 7144 reverse end zone codes ~ 12 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 7144 forward end zone codes ~ 12 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). PDP-4/7 DECtapes came in two formats. The first 5 controllers used a 4 word header/trailer (missing word 0/4). All later serial numbers used the standard header. The later, standard header/trailer is simulated here. Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of read all and write all. Read all assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write all writes only the data words and dumps the interblock words in the bit bucket. The Type 550 controller has a 4b unit select field, for units 1-8; the TC02 has a 3b unit select field, with unit 8 being represented as 0. The code assumes that the GETUNIT macro returns a unit number in the range of 0-7, with 8 represented as 0, and an invalid unit as -1. */ #include "pdp18b_defs.h" #define DT_NUMDR 8 /* #drives */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define DT_WC 030 /* word count */ #define DT_CA 031 /* current addr */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_BLKWD 1 /* blk no word in h/t */ #define DT_CSMWD 4 /* checksum word in h/t */ #define DT_HTWRD 5 /* header/trailer words */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word size in lines */ #define D18_BSIZE 256 /* block size in 18b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D11_FILSIZ (D18_CAPAC * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 86 /* block size in 18b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) /* This controller */ #define DT_CAPAC D18_CAPAC /* default */ #define DT_WSIZE D18_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) #define DT_QREZ(u) (((u)->pos) < DT_EZLIN) #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) /* Status register A */ #if defined (TC02) /* TC02/TC15 */ #define DTA_V_UNIT 15 /* unit select */ #define DTA_M_UNIT 07 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) #define DTA_V_MOT 13 /* motion */ #define DTA_M_MOT 03 #define DTA_V_MODE 12 /* mode */ #define DTA_V_FNC 9 /* function */ #define DTA_M_FNC 07 #define FNC_MOVE 00 /* move */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_RALL 03 /* read all */ #define FNC_WRIT 04 /* write */ #define FNC_WALL 05 /* write all */ #define FNC_WMRK 06 /* write timing */ #define DTA_V_ENB 8 /* int enable */ #define DTA_V_CERF 7 /* clr error flag */ #define DTA_V_CDTF 6 /* clr DECtape flag */ #define DTA_FWDRV (1u << (DTA_V_MOT + 1)) #define DTA_STSTP (1u << DTA_V_MOT) #define DTA_MODE (1u << DTA_V_MODE) #define DTA_ENB (1u << DTA_V_ENB) #define DTA_CERF (1u << DTA_V_CERF) #define DTA_CDTF (1u << DTA_V_CDTF) #define DTA_RW (0777700 & ~(DTA_CERF | DTA_CDTF)) #define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) #define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ SET_INT (DTA); \ else CLR_INT (DTA); #else /* Type 550 */ #define DTA_V_UNIT 12 /* unit select */ #define DTA_M_UNIT 017 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) #define DTA_V_MOT 4 /* motion */ #define DTA_M_MOT 03 #define DTA_V_FNC 0 /* function */ #define DTA_M_FNC 07 #define FNC_MOVE 00 /* move */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_WRIT 03 /* write */ #define FNC_RALL 05 /* read all */ #define FNC_WALL 06 /* write all */ #define FNC_WMRK 07 /* write timing */ #define DTA_STSTP (1u << (DTA_V_MOT + 1)) #define DTA_FWDRV (1u << DTA_V_MOT) #define DTA_MODE 0 /* not implemented */ #define DTA_RW 077 #define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)] #define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \ SET_INT (DTA); \ else CLR_INT (DTA); #endif #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) /* Status register B */ #if defined (TC02) /* TC02/TC15 */ #define DTB_V_ERF 17 /* error flag */ #define DTB_V_MRK 16 /* mark trk err */ #define DTB_V_END 15 /* end zone err */ #define DTB_V_SEL 14 /* select err */ #define DTB_V_PAR 13 /* parity err */ #define DTB_V_TIM 12 /* timing err */ #define DTB_V_DTF 6 /* DECtape flag */ #define DTB_ERF (1u << DTB_V_ERF) #define DTB_MRK (1u << DTB_V_MRK) #define DTB_END (1u << DTB_V_END) #define DTB_SEL (1u << DTB_V_SEL) #define DTB_PAR (1u << DTB_V_PAR) #define DTB_TIM (1u << DTB_V_TIM) #define DTB_DTF (1u << DTB_V_DTF) #define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ DTB_PAR | DTB_TIM) #else /* Type 550 */ #define DTB_V_DTF 17 /* data flag */ #define DTB_V_BEF 16 /* block end flag */ #define DTB_V_ERF 15 /* error flag */ #define DTB_V_END 14 /* end of tape */ #define DTB_V_TIM 13 /* timing err */ #define DTB_V_REV 12 /* reverse */ #define DTB_V_GO 11 /* go */ #define DTB_V_MRK 10 /* mark trk err */ #define DTB_V_SEL 9 /* select err */ #define DTB_DTF (1u << DTB_V_DTF) #define DTB_BEF (1u << DTB_V_BEF) #define DTB_ERF (1u << DTB_V_ERF) #define DTB_END (1u << DTB_V_END) #define DTB_TIM (1u << DTB_V_TIM) #define DTB_REV (1u << DTB_V_REV) #define DTB_GO (1u << DTB_V_GO) #define DTB_MRK (1u << DTB_V_MRK) #define DTB_SEL (1u << DTB_V_SEL) #define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL) #endif /* DECtape state */ #define DTS_V_MOT 3 /* motion */ #define DTS_M_MOT 07 #define DTS_STOP 0 /* stopped */ #define DTS_DECF 2 /* decel, fwd */ #define DTS_DECR 3 /* decel, rev */ #define DTS_ACCF 4 /* accel, fwd */ #define DTS_ACCR 5 /* accel, rev */ #define DTS_ATSF 6 /* @speed, fwd */ #define DTS_ATSR 7 /* @speed, rev */ #define DTS_DIR 01 /* dir mask */ #define DTS_V_FNC 0 /* function */ #define DTS_M_FNC 07 #define DTS_OFR 7 /* "off reel" */ #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) #define DTS_V_2ND 6 /* next state */ #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ ((DTS_STA (y, z)) << DTS_V_2ND) #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ ((DTS_STA (y, z)) << DTS_V_3RD) #define DTS_NXTSTA(x) (x >> DTS_V_2ND) /* Operation substates */ #define DTO_WCO 1 /* wc overflow */ #define DTO_SOB 2 /* start of block */ /* Logging */ #define LOG_MS 001 /* move, search */ #define LOG_RW 002 /* read, write */ #define LOG_RA 004 /* read all */ #define LOG_BL 010 /* block # lblk */ #define ABS(x) (((x) < 0)? (-(x)): (x)) extern int32 M[]; extern int32 int_hwre[API_HLVL+1]; extern UNIT cpu_unit; extern int32 sim_switches; extern int32 sim_is_running; extern FILE *sim_deb; int32 dtsa = 0; /* status A */ int32 dtsb = 0; /* status B */ int32 dtdb = 0; /* data buffer */ int32 dt_ltime = 12; /* interline time */ int32 dt_dctime = 40000; /* decel time */ int32 dt_substate = 0; int32 dt_logblk = 0; int32 dt_stopoffr = 0; /* stop on off reel */ static const int32 map_unit[16] = { /* Type 550 unit map */ -1, 1, 2, 3, 4, 5, 6, 7, 0, -1, -1, -1, -1, -1, -1, -1 }; DEVICE dt_dev; int32 dt75 (int32 dev, int32 pulse, int32 dat); int32 dt76 (int32 dev, int32 pulse, int32 dat); int32 dt_iors (void); t_stat dt_svc (UNIT *uptr); t_stat dt_reset (DEVICE *dptr); t_stat dt_attach (UNIT *uptr, char *cptr); t_stat dt_detach (UNIT *uptr); void dt_deselect (int32 oldf); void dt_newsa (int32 newf); void dt_newfnc (UNIT *uptr, int32 newsta); t_bool dt_setpos (UNIT *uptr); void dt_schedez (UNIT *uptr, int32 dir); void dt_seterr (UNIT *uptr, int32 e); int32 dt_comobv (int32 val); int32 dt_csum (UNIT *uptr, int32 blk); int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); /* DT data structures dt_dev DT device descriptor dt_unit DT unit list dt_reg DT register list dt_mod DT modifier list */ DIB dt_dib = { DEV_DTA, 2, &dt_iors, { &dt75, &dt76 } }; UNIT dt_unit[] = { { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, DT_CAPAC) } }; REG dt_reg[] = { { ORDATA (DTSA, dtsa, 18) }, { ORDATA (DTSB, dtsb, 18) }, { ORDATA (DTDB, dtdb, 18) }, { FLDATA (INT, int_hwre[API_DTA], INT_V_DTA) }, #if defined (DTA_V_ENB) { FLDATA (ENB, dtsa, DTA_V_ENB) }, #endif { FLDATA (DTF, dtsb, DTB_V_DTF) }, #if defined (DTB_V_BEF) { FLDATA (BEF, dtsb, DTB_V_BEF) }, #endif { FLDATA (ERF, dtsb, DTB_V_ERF) }, #if defined (TC02) /* TC02/TC15 */ { ORDATA (WC, M[DT_WC], 18) }, { ORDATA (CA, M[DT_CA], 18) }, #endif { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, { ORDATA (SUBSTATE, dt_substate, 2) }, { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO) }, { URDATA (LASTT, dt_unit[0].LASTT, 10, T_ADDR_W, 0, DT_NUMDR, REG_HRO) }, { ORDATA (DEVNO, dt_dib.dev, 6), REG_HRO }, { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, { NULL } }; MTAB dt_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEBTAB dt_deb[] = { { "MOTION", LOG_MS }, { "DATA", LOG_RW }, { "READALL", LOG_RA }, { "BLOCK", LOG_BL }, { NULL, 0 } }; DEVICE dt_dev = { "DT", dt_unit, dt_reg, dt_mod, DT_NUMDR, 8, 24, 1, 8, 18, NULL, NULL, &dt_reset, NULL, &dt_attach, &dt_detach, &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, dt_deb, NULL, NULL }; /* IOT routines */ #if defined (TC02) /* TC02/TC15 */ int32 dt75 (int32 dev, int32 pulse, int32 dat) { int32 old_dtsa = dtsa, fnc; UNIT *uptr; if (((pulse & 060) == 040) && (pulse & 05)) { /* select */ if (pulse & 01) /* DTCA */ dtsa = 0; if (pulse & 02) /* DTRA!... */ dat = dtsa; if (pulse & 04) { /* DTXA */ if ((dat & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; if ((dat & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; dtsa = dtsa ^ (dat & DTA_RW); } if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ fnc = DTA_GETFNC (dtsa); /* get fnc */ if (((uptr->flags) & UNIT_DIS) || /* disabled? */ (fnc >= FNC_WMRK) || /* write mark? */ ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)) || ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT))) dt_seterr (uptr, DTB_SEL); /* select err */ else dt_newsa (dtsa); /* new func */ DT_UPDINT; return dat; } if ((pulse & 067) == 042) /* DTRA */ return dtsa; if ((pulse & 067) == 061) /* DTEF */ return ((dtsb & DTB_ERF)? IOT_SKP + dat: dat); if ((pulse & 067) == 062) /* DTRB */ return dtsb; if ((pulse & 067) == 063) /* DTEF!DTRB */ return ((dtsb & DTB_ERF)? IOT_SKP + dtsb: dtsb); return dat; } int32 dt76 (int32 dev, int32 pulse, int32 dat) { if ((pulse & 01) && (dtsb & DTB_DTF)) /* DTDF */ return IOT_SKP + dat; return dat; } #else /* Type 550 */ int32 dt75 (int32 dev, int32 pulse, int32 dat) { if (((pulse & 041) == 001) && (dtsb & DTB_DTF)) /* MMDF */ dat = dat | IOT_SKP; else if (((pulse & 041) == 041) && (dtsb & DTB_ERF)) /* MMEF */ dat = dat | IOT_SKP; if (pulse & 002) { /* MMRD */ dat = (dat & ~DMASK) | dtdb; dtsb = dtsb & ~(DTB_DTF | DTB_BEF); } if (pulse & 004) { /* MMWR */ dtdb = dat & DMASK; dtsb = dtsb & ~(DTB_DTF | DTB_BEF); } DT_UPDINT; return dat; } int32 dt76 (int32 dev, int32 pulse, int32 dat) { int32 fnc, mot, unum; UNIT *uptr = NULL; unum = DTA_GETUNIT (dtsa); /* get unit no */ if (unum >= 0) /* get unit */ uptr = dt_dev.units + unum; if ((pulse & 001) && (dtsb & DTB_BEF)) /* MMBF */ dat = dat | IOT_SKP; if (pulse & 002) { /* MMRS */ dtsb = dtsb & ~(DTB_REV | DTB_GO); /* clr rev, go */ if (uptr) { /* valid unit? */ mot = DTS_GETMOT (uptr->STATE); /* get motion */ if (mot & DTS_DIR) /* rev? set */ dtsb = dtsb | DTB_REV; if ((mot >= DTS_ACCF) || (uptr->STATE & 0777700)) dtsb = dtsb | DTB_GO; /* accel? go */ } dat = (dat & ~DMASK) | dtsb; } if ((pulse & 044) == 044) { /* MMSE */ if ((dtsa ^ dat) & DTA_UNIT) /* new unit? */ dt_deselect (dtsa); dtsa = (dtsa & ~DTA_UNIT) | (dat & DTA_UNIT); dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); } else if ((pulse & 044) == 004) { /* MMLC */ dtsa = (dtsa & ~DTA_RW) | (dat & DTA_RW); /* load dtsa */ dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); fnc = DTA_GETFNC (dtsa); /* get fnc */ if ((uptr == NULL) || /* invalid? */ ((uptr->flags) & UNIT_DIS) || /* disabled? */ (fnc >= FNC_WMRK) || /* write mark? */ ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WLK)) || ((fnc == FNC_WALL) && (uptr->flags & UNIT_WLK))) dt_seterr (uptr, DTB_SEL); /* select err */ else dt_newsa (dtsa); } DT_UPDINT; return dat; } #endif /* Unit deselect */ void dt_deselect (int32 oldf) { int32 old_unit, old_mot; UNIT *uptr; old_unit = DTA_GETUNIT (oldf); /* get unit no */ if (old_unit < 0) /* invalid? */ return; uptr = dt_dev.units + old_unit; /* get unit */ old_mot = DTS_GETMOT (uptr->STATE); if (old_mot >= DTS_ATSF) /* at speed? */ dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); else if (old_mot >= DTS_ACCF) /* accelerating? */ DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); return; } /* Command register change 1. If change in motion, stop to start - schedule acceleration - set function as next state 2. If change in motion, start to stop - if not already decelerating (could be reversing), schedule deceleration 3. If change in direction, - if not decelerating, schedule deceleration - set accelerating (other dir) as next state - set function as next next state 4. If not accelerating or at speed, - schedule acceleration - set function as next state 5. If not yet at speed, - set function as next state 6. If at speed, - set function as current state, schedule function */ void dt_newsa (int32 newf) { int32 new_unit, prev_mot, new_fnc; int32 prev_mving, new_mving, prev_dir, new_dir; UNIT *uptr; new_unit = DTA_GETUNIT (newf); /* new unit */ if (new_unit < 0) /* invalid? */ return; uptr = dt_dev.units + new_unit; if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ dt_seterr (uptr, DTB_SEL); /* no, error */ return; } prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ prev_mving = prev_mot != DTS_STOP; /* previous moving? */ prev_dir = prev_mot & DTS_DIR; /* previous dir? */ new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ new_fnc = DTA_GETFNC (newf); /* new function? */ if ((prev_mving | new_mving) == 0) /* stop to stop */ return; if (new_mving & ~prev_mving) { /* start? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mving & ~new_mving) { /* stop? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ return; } if (prev_dir ^ new_dir) { /* dir chg? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ return; } if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* cancel cur */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mot < DTS_ATSF) { /* not at speed? */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ return; } /* Schedule new DECtape function This routine is only called if - the selected unit is attached - the selected unit is at speed (forward or backward) This routine - updates the selected unit's position - updates the selected unit's state - schedules the new operation */ void dt_newfnc (UNIT *uptr, int32 newsta) { int32 fnc, dir, blk, unum, newpos; #if defined (TC02) int32 relpos; #endif uint32 oldpos; oldpos = uptr->pos; /* save old pos */ if (dt_setpos (uptr)) /* update pos */ return; uptr->STATE = newsta; /* update state */ fnc = DTS_GETFNC (uptr->STATE); /* set variables */ dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; unum = (int32) (uptr - dt_dev.units); if (oldpos == uptr->pos) /* bump pos */ uptr->pos = uptr->pos + (dir? -1: 1); blk = DT_LIN2BL (uptr->pos, uptr); if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ dt_seterr (uptr, DTB_END); /* set ez flag, stop */ return; } sim_cancel (uptr); /* cancel cur op */ dt_substate = DTO_SOB; /* substate = block start */ switch (fnc) { /* case function */ case DTS_OFR: /* off reel */ if (dir) /* rev? < start */ newpos = -1000; else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ break; case FNC_MOVE: /* move */ dt_schedez (uptr, dir); /* sched end zone */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", unum, (dir? "backward": "forward")); return; /* done */ case FNC_SRCH: /* search */ if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; else newpos = DT_BLK2LN ((DT_QREZ (uptr)? 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", unum, (dir? "backward": "forward")); break; case FNC_WRIT: /* write */ case FNC_READ: /* read */ #if defined (TC02) /* TC02/TC15 */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dt_seterr (uptr, DTB_SEL); return; } if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); break; #endif case FNC_RALL: /* read all */ case FNC_WALL: /* write all */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; else newpos = DT_EZLIN + (DT_WSIZE - 1); } else { newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE; if (!dir) newpos = newpos + (DT_WSIZE - 1); } if (DEBUG_PRI (dt_dev, LOG_RA) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: read all block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous]": " ")); break; default: dt_seterr (uptr, DTB_SEL); /* bad state */ return; } #if defined (TYPE550) /* Type 550 */ if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */ dtsb = dtsb | DTB_DTF; /* set data flag */ DT_UPDINT; } #endif sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/dt_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool dt_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 mot = DTS_GETMOT (uptr->STATE); int32 unum, delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (mot & ~DTS_DIR) { /* case on motion */ case DTS_STOP: /* stop */ delta = 0; break; case DTS_DECF: /* slowing */ ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case DTS_ACCF: /* accelerating */ ulin = ut / (uint32) dt_ltime; udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; delta = (ulin * ulin) / (2 * udelt); break; case DTS_ATSF: /* at speed */ delta = ut / (uint32) dt_ltime; break; } if (mot & DTS_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel? */ uptr->STATE = uptr->pos = 0; unum = (int32) (uptr - dt_dev.units); if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ dt_seterr (uptr, DTB_SEL); /* error */ return TRUE; } return FALSE; } /* Unit service Unit must be attached, detach cancels operation */ t_stat dt_svc (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; int32 fnc = DTS_GETFNC (uptr->STATE); int32 *fbuf = (int32 *) uptr->filebuf; int32 unum = uptr - dt_dev.units; int32 blk, wrd, ma, relpos; uint32 ba; /* Motion cases Decelerating - if next state != stopped, must be accel reverse Accelerating - next state must be @speed, schedule function At speed - do functional processing */ switch (mot) { case DTS_DECF: case DTS_DECR: /* decelerating */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ if (uptr->STATE) /* not stopped? */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ return SCPE_OK; case DTS_ACCF: case DTS_ACCR: /* accelerating */ dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ return SCPE_OK; case DTS_ATSF: case DTS_ATSR: /* at speed */ break; /* check function */ default: /* other */ dt_seterr (uptr, DTB_SEL); /* state error */ return SCPE_OK; } /* Functional cases Move - must be at end zone Search - transfer block number, schedule next block Off reel - detach unit (it must be deselected) */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); if (DT_QEZ (uptr)) { /* in end zone? */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; } blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ switch (fnc) { /* at speed, check fnc */ case FNC_MOVE: /* move */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; case DTS_OFR: /* off reel */ detach_unit (uptr); /* must be deselected */ uptr->STATE = uptr->pos = 0; /* no visible action */ break; /* TC02/TC15 service */ /* Search */ #if defined (TC02) /* TC02/TC15 */ case FNC_SRCH: /* search */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* inc WC */ ma = M[DT_CA] & AMASK; /* get mem addr */ if (MEM_ADDR_OK (ma)) /* store block # */ M[ma] = blk; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: found block %d\n", unum, blk); break; /* Read has four subcases Start of block, not wc ovf - check that DTF is clear, otherwise normal Normal - increment MA, WC, copy word from tape to memory if read dir != write dir, bits must be scrambled if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - if end of block reached, timing error, otherwise, continue to next word */ case FNC_READ: /* read */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start of block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal read */ M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & DMASK; ma = M[DT_CA] & AMASK; /* mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dtdb = fbuf[ba]; /* get tape word */ if (dir) /* rev? comp obv */ dtdb = dt_comobv (dtdb); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dtdb; if (M[DT_WC] == 0) /* wc ovf? */ dt_substate = DTO_WCO; case DTO_WCO: /* wc ovf, not sob */ if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* next block */ if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ dt_seterr (uptr, DTB_TIM); /* timing error */ else sim_activate (uptr, DT_WSIZE * dt_ltime); break; } /* end case subst */ break; /* Write has four subcases Start of block, not wc ovf - check that DTF is clear, set block direction Normal - increment MA, WC, copy word from memory to tape if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - copy 0 to tape if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - schedule end zone */ case FNC_WRIT: /* write */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal write */ M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & DMASK; case DTO_WCO: /* wc ovflo */ ma = M[DT_CA] & AMASK; /* mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dtdb = dt_substate? 0: M[ma]; /* get word */ if (dir) /* rev? comp obv */ dtdb = dt_comobv (dtdb); fbuf[ba] = dtdb; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case subst */ break; /* Read all has two subcases Not word count overflow - increment MA, WC, copy word from tape to memory Word count overflow - schedule end zone */ case FNC_RALL: /* read all */ switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & DMASK; ma = M[DT_CA] & AMASK; /* mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; dtdb = fbuf[ba]; /* get tape word */ } else dtdb = dt_gethdr (uptr, blk, relpos); /* get hdr */ if (dir) /* rev? comp obv */ dtdb = dt_comobv (dtdb); sim_activate (uptr, DT_WSIZE * dt_ltime); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dtdb; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; /* Write all has two subcases Not word count overflow - increment MA, WC, copy word from memory to tape Word count overflow - schedule end zone */ case FNC_WALL: /* write all */ switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & DMASK; ma = M[DT_CA] & AMASK; /* mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dtdb = M[ma]; /* get mem word */ if (dir) dtdb = dt_comobv (dtdb); wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; fbuf[ba] = dtdb; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; } /* /* ignore hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; /* Type 550 service */ /* Search */ #else /* Type 550 */ case FNC_SRCH: /* search */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ dtdb = blk; /* store block # */ dtsb = dtsb | DTB_DTF; /* set DTF */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: search found block %d\n", unum, blk); break; /* Read and read all */ case FNC_READ: case FNC_RALL: if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; dtdb = fbuf[ba]; /* get tape word */ dtsb = dtsb | DTB_DTF; /* set flag */ } else { ma = (2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1; wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ #if defined (OLD_TYPE550) if ((wrd == 0) || /* skip 1st, last */ (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; #endif if ((fnc == FNC_READ) && /* read, skip if not */ (wrd != DT_CSMWD) && /* fwd, rev cksum */ (wrd != ma)) break; dtdb = dt_gethdr (uptr, blk, relpos); if (wrd == (dir? DT_CSMWD: ma)) /* at end csum? */ dtsb = dtsb | DTB_BEF; /* end block */ else dtsb = dtsb | DTB_DTF; /* else next word */ } if (dir) dtdb = dt_comobv (dtdb); break; /* Write and write all */ case FNC_WRIT: case FNC_WALL: if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; if (dir) /* get data word */ fbuf[ba] = dt_comobv (dtdb); else fbuf[ba] = dtdb; if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (wrd == (dir? 0: DTU_BSIZE (uptr) - 1)) dtsb = dtsb | DTB_BEF; /* end block */ else dtsb = dtsb | DTB_DTF; /* else next word */ } else { wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ #if defined (OLD_TYPE550) if ((wrd == 0) || /* skip 1st, last */ (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; #endif if ((fnc == FNC_WRIT) && /* wr, skip if !csm */ (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1))) break; dtsb = dtsb | DTB_DTF; /* set flag */ } break; #endif default: dt_seterr (uptr, DTB_SEL); /* impossible state */ break; } /* end case function */ DT_UPDINT; /* update interrupts */ return SCPE_OK; } /* Utility routines */ /* Set error flag */ void dt_seterr (UNIT *uptr, int32 e) { int32 mot = DTS_GETMOT (uptr->STATE); dtsa = dtsa & ~DTA_STSTP; /* clear go */ dtsb = dtsb | DTB_ERF | e; /* set error flag */ if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ sim_cancel (uptr); /* cancel activity */ if (dt_setpos (uptr)) /* update position */ return; sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ } DT_UPDINT; return; } /* Schedule end zone */ void dt_schedez (UNIT *uptr, int32 dir) { int32 newpos; if (dir) /* rev? rev ez */ newpos = DT_EZLIN - DT_WSIZE; else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Complement obverse routine */ int32 dt_comobv (int32 dat) { dat = dat ^ DMASK; /* compl obverse */ dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | ((dat >> 3) & 0700) | ((dat & 0700) << 3) | ((dat & 070) << 9) | ((dat & 07) << 15); return dat; } /* Checksum routine */ int32 dt_csum (UNIT *uptr, int32 blk) { int32 *fbuf = (int32 *) uptr->filebuf; int32 ba = blk * DTU_BSIZE (uptr); int32 i, csum, wrd; #if defined (TC02) /* TC02/TC15 */ csum = 077; /* init csum */ for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i] ^ DMASK; /* get ~word */ csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; } return (csum & 077); #else /* Type 550 */ csum = 0777777; for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i]; /* get word */ csum = csum + wrd; /* 1's comp add */ if (csum > DMASK) csum = (csum + 1) & DMASK; } return (csum ^ DMASK); /* 1's comp res */ #endif } /* Get header word */ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) { int32 wrd = relpos / DT_WSIZE; if (wrd == DT_BLKWD) /* fwd blknum */ return blk; #if defined (TC02) /* TC02/TC15 */ if (wrd == DT_CSMWD) /* rev csum */ return 077; if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ return (dt_csum (uptr, blk) << 12); #else /* Type 550 */ if (wrd == DT_CSMWD) /* rev csum */ return 0777777; if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ return (dt_csum (uptr, blk)); #endif if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ return dt_comobv (blk); return 0; /* all others */ } /* Reset routine */ t_stat dt_reset (DEVICE *dptr) { int32 i, prev_mot; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */ uptr = dt_dev.units + i; if (sim_is_running) { /* CAF? */ prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ if (dt_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } dtsa = dtsb = 0; /* clear status */ DT_UPDINT; /* reset interrupt */ return SCPE_OK; } /* IORS routine */ int32 dt_iors (void) { #if defined IOS_DTA return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0); #else return 0; #endif } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 12b, read 12b format and convert to 18b in buffer If 16b, read 16b format and convert to 18b in buffer If 18b/36b, read data into buffer */ t_stat dt_attach (UNIT *uptr, char *cptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, sz, k, *fbuf; int32 u = uptr - dt_dev.units; t_stat r; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) /* error? */ return r; if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ if (sim_switches & SWMASK ('T')) /* att 12b? */ uptr->flags = uptr->flags | UNIT_8FMT; else if (sim_switches & SWMASK ('S')) /* att 16b? */ uptr->flags = uptr->flags | UNIT_11FMT; else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ (sz = sim_fsize (uptr->fileref))) { if (sz == D8_FILSIZ) uptr->flags = uptr->flags | UNIT_8FMT; else if (sz == D11_FILSIZ) uptr->flags = uptr->flags | UNIT_11FMT; } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ printf ("%s%d: ", sim_dname (&dt_dev), u); if (uptr->flags & UNIT_8FMT) printf ("12b format"); else if (uptr->flags & UNIT_11FMT) printf ("16b format"); else printf ("18b/36b format"); printf (", buffering file in memory\n"); if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | ((uint32) (pdp8b[k + 1] >> 6) & 077); fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | ((uint32) pdp8b[k + 2] & 07777); ba = ba + 2; /* end blk loop */ } } /* end file loop */ uptr->hwmark = ba; } /* end if */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; for (k = 0; k < D18_BSIZE; k++) fbuf[ba++] = pdp11b[k]; } uptr->hwmark = ba; } /* end elif */ else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), uptr->capac, uptr->fileref); uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ return SCPE_OK; } /* Detach routine Cancel in progress operation If 12b, convert 18b buffer to 12b and write to file If 16b, convert 18b buffer to 16b and write to file If 18b/36b, write buffer to file Deallocate buffer */ t_stat dt_detach (UNIT* uptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, k, *fbuf; int32 u = uptr - dt_dev.units; if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; if (sim_is_active (uptr)) { sim_cancel (uptr); if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; DT_UPDINT; } uptr->STATE = uptr->pos = 0; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ pdp8b[k] = (fbuf[ba] >> 6) & 07777; pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | ((fbuf[ba + 1] >> 12) & 077); pdp8b[k + 2] = fbuf[ba + 1] & 07777; ba = ba + 2; } /* end loop blk */ fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 12b */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D18_BSIZE; k++) /* loop blk */ pdp11b[k] = fbuf[ba++] & 0177777; fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 16b */ else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ uptr->hwmark, uptr->fileref); if (ferror (uptr->fileref)) perror ("I/O error"); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ return detach_unit (uptr); } simh-3.8.1/PDP18B/pdp18b_fpp.c0000644000175000017500000010764511107426474013671 0ustar vlmvlm/* pdp18b_fpp.c: FP15 floating point processor simulator Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. fpp PDP-15 floating point processor 06-Jul-06 RMS Fixed bugs in left shift, multiply 31-Oct-04 RMS Fixed URFST to mask low 9b of fraction Fixed exception PC setting 10-Apr-04 RMS JEA is 15b not 18b The FP15 instruction format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 0 1| subop | microcoded modifiers | floating point +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ Indirection is always single level. The FP15 supports four data formats: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | S| 2's complement integer | A: integer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | S| 2's complement integer (high) | A: extended integer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 2's complement integer (low) | A+1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | fraction (low) |SE|2's complement exponent| A: single floating +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |SF| fraction (high) | A+1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |SE| 2's complement exponent | A: double floating +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |SF| fraction (high) | A+1 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | fraction (low) | A+2 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ #include "pdp18b_defs.h" /* Instruction */ #define FI_V_OP 8 /* subopcode */ #define FI_M_OP 017 #define FI_GETOP(x) (((x) >> FI_V_OP) & FI_M_OP) #define FI_NOLOAD 0200 /* don't load */ #define FI_DP 0100 /* single/double */ #define FI_FP 0040 /* int/flt point */ #define FI_NONORM 0020 /* don't normalize */ #define FI_NORND 0010 /* don't round */ #define FI_V_SGNOP 0 /* A sign change */ #define FI_M_SGNOP 03 #define FI_GETSGNOP(x) (((x) >> FI_V_SGNOP) & FI_M_SGNOP) /* Exception register */ #define JEA_V_SIGN 17 /* A sign */ #define JEA_V_GUARD 16 /* guard */ #define JEA_EAMASK 077777 /* exc address */ #define JEA_OFF_OVF 0 /* ovf offset */ #define JEA_OFF_UNF 2 /* unf offset */ #define JEA_OFF_DIV 4 /* div offset */ #define JEA_OFF_MM 6 /* mem mgt offset */ /* Status codes - must relate directly to JEA offsets */ #define FP_OK 0 /* no error - mbz */ #define FP_OVF (JEA_OFF_OVF + 1) /* overflow */ #define FP_UNF (JEA_OFF_UNF + 1) /* underflow */ #define FP_DIV (JEA_OFF_DIV + 1) /* divide exception */ #define FP_MM (JEA_OFF_MM + 1) /* mem mgt error */ /* Unpacked floating point fraction */ #define UFP_FH_CARRY 0400000 /* carry out */ #define UFP_FH_NORM 0200000 /* normalized */ #define UFP_FH_MASK 0377777 /* hi mask */ #define UFP_FL_MASK 0777777 /* low mask */ #define UFP_FL_SMASK 0777000 /* low mask, single */ #define UFP_FL_SRND 0000400 /* round bit, single */ #define GET_SIGN(x) (((x) >> 17) & 1) #define SEXT18(x) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK)) #define SEXT9(x) (((x) & 0400)? ((x) | ~0377): ((x) & 0377)) enum fop { FOP_TST, FOP_SUB, FOP_RSUB, FOP_MUL, FOP_DIV, FOP_RDIV, FOP_LD, FOP_ST, FOP_FLT, FOP_FIX, FOP_LFMQ, FOP_JEA, FOP_ADD, FOP_BR, FOP_DIAG, FOP_UND }; typedef struct { int32 exp; /* exponent */ int32 sign; /* sign */ int32 hi; /* hi frac, 17b */ int32 lo; /* lo frac, 18b */ } UFP; static int32 fir; /* instruction */ static int32 jea; /* exc address */ static int32 fguard; /* guard bit */ static int32 stop_fpp = STOP_RSRV; /* stop if fp dis */ static UFP fma; /* FMA */ static UFP fmb; /* FMB */ static UFP fmq; /* FMQ - hi,lo only */ extern int32 M[MAXMEMSIZE]; extern int32 pcq[PCQ_SIZE]; extern int32 pcq_p; extern int32 PC; extern int32 trap_pending, usmd; t_stat fp15_reset (DEVICE *dptr); t_stat fp15_opnd (int32 ir, int32 addr, UFP *a); t_stat fp15_store (int32 ir, int32 addr, UFP *a); t_stat fp15_iadd (int32 ir, UFP *a, UFP *b, t_bool sub); t_stat fp15_imul (int32 ir, UFP *a, UFP *b); t_stat fp15_idiv (int32 ir, UFP *a, UFP *b); t_stat fp15_fadd (int32 ir, UFP *a, UFP *b, t_bool sub); t_stat fp15_fmul (int32 ir, UFP *a, UFP *b); t_stat fp15_fdiv (int32 ir, UFP *a, UFP *b); t_stat fp15_fix (int32 ir, UFP *a); t_stat fp15_norm (int32 ir, UFP *a, UFP *b, t_bool rnd); t_stat fp15_exc (int32 sta); void fp15_asign (int32 ir, UFP *a); void dp_add (UFP *a, UFP *b); void dp_sub (UFP *a, UFP *b); void dp_inc (UFP *a); int32 dp_cmp (UFP *a, UFP *b); void dp_mul (UFP *a, UFP *b); void dp_lsh_1 (UFP *a, UFP *b); void dp_rsh_1 (UFP *a, UFP *b); void dp_dnrm_r (int32 ir, UFP *a, int32 sc); void dp_swap (UFP *a, UFP *b); extern t_stat Read (int32 ma, int32 *dat, int32 cyc); extern t_stat Write (int32 ma, int32 dat, int32 cyc); extern int32 Incr_addr (int32 addr); extern int32 Jms_word (int32 t); /* FPP data structures fpp_dev FPP device descriptor fpp_unit FPP unit fpp_reg FPP register list fpp_mod FPP modifier list */ UNIT fpp_unit = { UDATA (NULL, 0, 0) }; REG fpp_reg[] = { { ORDATA (FIR, fir, 12) }, { ORDATA (EPA, fma.exp, 18) }, { FLDATA (FMAS, fma.sign, 0) }, { ORDATA (FMAH, fma.hi, 17) }, { ORDATA (FMAL, fma.lo, 18) }, { ORDATA (EPB, fmb.exp, 18) }, { FLDATA (FMBS, fmb.sign, 0) }, { ORDATA (FMBH, fmb.hi, 17) }, { ORDATA (FMBL, fmb.lo, 18) }, { FLDATA (FGUARD, fguard, 0) }, { ORDATA (FMQH, fmq.hi, 17) }, { ORDATA (FMQL, fmq.lo, 18) }, { ORDATA (JEA, jea, 15) }, { FLDATA (STOP_FPP, stop_fpp, 0) }, { NULL } }; DEVICE fpp_dev = { "FPP", &fpp_unit, fpp_reg, NULL, 1, 8, 1, 1, 8, 18, NULL, NULL, &fp15_reset, NULL, NULL, NULL, NULL, DEV_DISABLE }; /* Instruction decode for FP15 The CPU actually fetches the instruction and the word after. If the instruction is 71XXXX, the CPU executes it as a NOP, and the FP15 fools the CPU into thinking that the second word is also a NOP. Indirect addresses are resolved during fetch, unless the NOLOAD modifier is set and the instruction is not a store. */ t_stat fp15 (int32 ir) { int32 ar, ma, fop, dat; t_stat sta = FP_OK; if (fpp_dev.flags & DEV_DIS) /* disabled? */ return (stop_fpp? STOP_FPDIS: SCPE_OK); fir = ir & 07777; /* save subop + mods */ ma = PC; /* fetch next word */ PC = Incr_addr (PC); if (Read (ma, &ar, RD)) /* error? MM exc */ return fp15_exc (FP_MM); fop = FI_GETOP (fir); /* get subopcode */ if ((ar & SIGN) && /* indirect? */ ((fop == FOP_ST) || !(ir & FI_NOLOAD))) { /* store or load? */ ma = ar & AMASK; /* fetch indirect */ if (Read (ma, &ar, RD)) return fp15_exc (FP_MM); } fma.exp = SEXT18 (fma.exp); /* sext exponents */ fmb.exp = SEXT18 (fmb.exp); switch (fop) { /* case on subop */ case FOP_TST: /* NOP */ break; case FOP_SUB: /* subtract */ if (sta = fp15_opnd (fir, ar, &fmb)) /* fetch op to FMB */ break; if (fir & FI_FP) /* fp? */ sta = fp15_fadd (fir, &fma, &fmb, 1); /* yes, fp sub */ else sta = fp15_iadd (fir, &fma, &fmb, 1); /* no, int sub */ break; case FOP_RSUB: /* reverse sub */ fmb = fma; /* FMB <- FMA */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; if (fir & FI_FP) /* fp? */ sta = fp15_fadd (fir, &fma, &fmb, 1); /* yes, fp sub */ else sta = fp15_iadd (fir, &fma, &fmb, 1); /* no, int sub */ break; case FOP_MUL: /* multiply */ if (sta = fp15_opnd (fir, ar, &fmb)) /* fetch op to FMB */ break; if (fir & FI_FP) /* fp? */ sta = fp15_fmul (fir, &fma, &fmb); /* yes, fp mul */ else sta = fp15_imul (fir, &fma, &fmb); /* no, int mul */ break; case FOP_DIV: /* divide */ if (sta = fp15_opnd (fir, ar, &fmb)) /* fetch op to FMB */ break; if (sta = fp15_opnd (fir, ar, &fmb)) break; /* fetch op to FMB */ if (fir & FI_FP) /* fp? */ sta = fp15_fdiv (fir, &fma, &fmb); /* yes, fp div */ else sta = fp15_idiv (fir, &fma, &fmb); /* no, int div */ break; case FOP_RDIV: /* reverse divide */ fmb = fma; /* FMB <- FMA */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; if (fir & FI_FP) /* fp? */ sta = fp15_fdiv (fir, &fma, &fmb); /* yes, fp div */ else sta = fp15_idiv (fir, &fma, &fmb); /* no, int div */ break; case FOP_LD: /* load */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; fp15_asign (fir, &fma); /* modify A sign */ if (fir & FI_FP) /* fp? */ sta = fp15_norm (ir, &fma, NULL, 0); /* norm, no round */ break; case FOP_ST: /* store */ fp15_asign (fir, &fma); /* modify A sign */ sta = fp15_store (fir, ar, &fma); /* store result */ break; case FOP_FLT: /* float */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; fma.exp = 35; fp15_asign (fir, &fma); /* adjust A sign */ sta = fp15_norm (ir, &fma, NULL, 0); /* norm, no found */ break; case FOP_FIX: /* fix */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; sta = fp15_fix (fir, &fma); /* fix */ break; case FOP_LFMQ: /* load FMQ */ if (sta = fp15_opnd (fir, ar, &fma)) /* fetch op to FMA */ break; dp_swap (&fma, &fmq); /* swap FMA, FMQ */ fp15_asign (fir, &fma); /* adjust A sign */ if (fir & FI_FP) /* fp? */ sta = fp15_norm (ir, &fma, &fmq, 0); /* yes, norm, no rnd */ break; case FOP_JEA: /* JEA */ if (ir & 0200) { /* store? */ dat = jea | (fma.sign << JEA_V_SIGN) | (fguard << JEA_V_GUARD); sta = Write (ar, dat, WR); } else { /* no, load */ if (sta = Read (ar, &dat, RD)) break; fguard = (dat >> JEA_V_GUARD) & 1; jea = dat & JEA_EAMASK; } break; case FOP_ADD: /* add */ if (sta = fp15_opnd (fir, ar, &fmb)) /* fetch op to FMB */ break; if (fir & FI_FP) /* fp? */ sta = fp15_fadd (fir, &fma, &fmb, 0); /* yes, fp add */ else sta = fp15_iadd (fir, &fma, &fmb, 0); /* no, int add */ break; case FOP_BR: /* branch */ if (((fir & 001) && ((fma.hi | fma.lo) == 0)) || ((fir & 002) && fma.sign) || ((fir & 004) && !fma.sign) || ((fir & 010) && ((fma.hi | fma.lo) != 0)) || ((fir & 020) && fguard)) { /* cond met? */ PCQ_ENTRY; /* save current PC */ PC = (PC & BLKMASK) | (ar & IAMASK); /* branch within 32K */ } break; default: break; } /* end switch op */ fma.exp = fma.exp & DMASK; /* mask exp to 18b */ fmb.exp = fmb.exp & DMASK; if (sta != FP_OK) return fp15_exc (sta); /* error? */ return SCPE_OK; } /* Operand load and store */ t_stat fp15_opnd (int32 ir, int32 addr, UFP *fpn) { int32 i, numwd, wd[3]; fguard = 0; /* clear guard */ if (ir & FI_NOLOAD) /* no load? */ return FP_OK; if (ir & FI_FP) /* fp? at least 2 */ numwd = 2; else numwd = 1; /* else at least 1 */ if (ir & FI_DP) /* dp? 1 more */ numwd = numwd + 1; for (i = 0; i < numwd; i++) { /* fetch words */ if (Read (addr, &wd[i], RD)) return FP_MM; addr = (addr + 1) & AMASK; } if (ir & FI_FP) { /* fp? */ fpn->sign = GET_SIGN (wd[1]); /* frac sign */ fpn->hi = wd[1] & UFP_FH_MASK; /* frac high */ if (ir & FI_DP) { /* dp? */ fpn->exp = SEXT18 (wd[0]); /* exponent */ fpn->lo = wd[2]; /* frac low */ } else { /* sp */ fpn->exp = SEXT9 (wd[0]); /* exponent */ fpn->lo = wd[0] & UFP_FL_SMASK; /* frac low */ } } else { fpn->sign = GET_SIGN (wd[0]); /* int, get sign */ if (ir & FI_DP) { /* dp? */ fpn->lo = wd[1]; /* 2 words */ fpn->hi = wd[0]; } else { /* single */ fpn->lo = wd[0]; /* 1 word */ fpn->hi = fpn->sign? DMASK: 0; /* sign extended */ } if (fpn->sign) { /* negative? */ fpn->lo = (-fpn->lo) & UFP_FL_MASK; /* take abs val */ fpn->hi = (~fpn->hi + (fpn->lo == 0)) & UFP_FH_MASK; } } return FP_OK; } t_stat fp15_store (int32 ir, int32 addr, UFP *a) { int32 i, numwd, wd[3]; t_stat sta; fguard = 0; /* clear guard */ if (ir & FI_FP) { /* fp? */ if (sta = fp15_norm (ir, a, NULL, 0)) /* normalize */ return sta; if (ir & FI_DP) { /* dp? */ wd[0] = a->exp & DMASK; /* exponent */ wd[1] = (a->sign << 17) | a->hi; /* hi frac */ wd[2] = a->lo; /* low frac */ numwd = 3; /* 3 words */ } else { /* single */ if (!(ir & FI_NORND) && (a->lo & UFP_FL_SRND)) { /* round? */ a->lo = (a->lo + UFP_FL_SRND) & UFP_FL_SMASK; a->hi = (a->hi + (a->lo == 0)) & UFP_FH_MASK; if ((a->hi | a->lo) == 0) { /* carry out? */ a->hi = UFP_FH_NORM; /* shift back */ a->exp = a->exp + 1; } } if (a->exp > 0377) /* sp ovf? */ return FP_OVF; if (a->exp < -0400) /* sp unf? */ return FP_UNF; wd[0] = (a->exp & 0777) | (a->lo & UFP_FL_SMASK); /* low frac'exp */ wd[1] = (a->sign << 17) | a->hi; /* hi frac */ numwd = 2; /* 2 words */ } } else { fmb.lo = (-a->lo) & UFP_FL_MASK; /* 2's complement */ fmb.hi = (~a->hi + (fmb.lo == 0)) & UFP_FH_MASK; /* to FMB */ if (ir & FI_DP) { /* dp? */ if (a->sign) { /* negative? */ wd[0] = fmb.hi | SIGN; /* store FMB */ wd[1] = fmb.lo; } else { /* pos, store FMA */ wd[0] = a->hi; wd[1] = a->lo; } numwd = 2; /* 2 words */ } else { /* single */ if (a->hi || (a->lo & SIGN)) /* check int ovf */ return FP_OVF; if (a->sign) /* neg? store FMB */ wd[0] = fmb.lo; else wd[0] = a->lo; /* pos, store FMA */ numwd = 1; /* 1 word */ } } for (i = 0; i < numwd; i++) { /* store words */ if (Write (addr, wd[i], WR)) return FP_MM; addr = (addr + 1) & AMASK; } return FP_OK; } /* Integer arithmetic routines */ /* Integer add - overflow only on add, if carry out of high fraction */ t_stat fp15_iadd (int32 ir, UFP *a, UFP *b, t_bool sub) { fmq.hi = fmq.lo = 0; /* clear FMQ */ if (a->sign ^ b->sign ^ sub) /* eff subtract? */ dp_sub (a, b); else { dp_add (a, b); /* no, add */ if (a->hi & UFP_FH_CARRY) { /* carry out? */ a->hi = a->hi & UFP_FH_MASK; /* mask to 35b */ return FP_OVF; /* overflow */ } } fp15_asign (ir, a); /* adjust A sign */ return FP_OK; } /* Integer multiply - overflow if high result (FMQ after swap) non-zero */ t_stat fp15_imul (int32 ir, UFP *a, UFP *b) { a->sign = a->sign ^ b->sign; /* sign of result */ dp_mul (a, b); /* a'FMQ <- a * b */ dp_swap (a, &fmq); /* swap a, FMQ */ if (fmq.hi | fmq.lo) /* FMQ != 0? ovf */ return FP_OVF; fp15_asign (ir, a); /* adjust A sign */ return FP_OK; } /* Integer divide - actually done as fraction divide - If divisor zero, error - If dividend zero, done - Normalize dividend and divisor together - If divisor normalized but dividend not, result is zero - If divisor not normalized, normalize and count shifts - Do fraction divide for number of shifts, +1, steps Note that dp_lsh_1 returns a 72b result; the last right shift guarantees a 71b remainder. The quotient cannot exceed 71b */ t_stat fp15_idiv (int32 ir, UFP *a, UFP *b) { int32 i, sc; a->sign = a->sign ^ b->sign; /* sign of result */ fmq.hi = fmq.lo = 0; /* clear quotient */ a->exp = 0; /* clear a exp */ if ((b->hi | b->lo) == 0) /* div by 0? */ return FP_DIV; if ((a->hi | a->lo) == 0) /* div into 0? */ return FP_OK; while (((a->hi & UFP_FH_NORM) == 0) && /* normalize divd */ ((b->hi & UFP_FH_NORM) == 0)) { /* and divr */ dp_lsh_1 (a, NULL); /* lsh divd, divr */ dp_lsh_1 (b, NULL); /* can't carry out */ } if (!(a->hi & UFP_FH_NORM) && (b->hi & UFP_FH_NORM)) { /* divr norm, divd not? */ dp_swap (a, &fmq); /* quo = 0 (fmq), rem = a */ return FP_OK; } while ((b->hi & UFP_FH_NORM) == 0) { /* normalize divr */ dp_lsh_1 (b, NULL); /* can't carry out */ a->exp = a->exp + 1; /* count steps */ } sc = a->exp; for (i = 0; i <= sc; i++) { /* n+1 steps */ dp_lsh_1 (&fmq, NULL); /* left shift quo */ if (dp_cmp (a, b) >= 0) { /* sub work? */ dp_sub (a, b); /* a -= b */ if (i == 0) /* first step? */ a->exp = a->exp + 1; fmq.lo = fmq.lo | 1; /* set quo bit */ } dp_lsh_1 (a, NULL); /* left shift divd */ } dp_rsh_1 (a, NULL); /* shift back */ dp_swap (a, &fmq); /* swap a, FMQ */ fp15_asign (ir, a); /* adjust A sign */ return FP_OK; } /* Floating point arithmetic routines */ /* Floating add - Special add case, overflow if carry out increments exp out of range - All cases, overflow/underflow detected in normalize */ t_stat fp15_fadd (int32 ir, UFP *a, UFP *b, t_bool sub) { int32 ediff; fmq.hi = fmq.lo = 0; /* clear FMQ */ ediff = a->exp - b->exp; /* exp diff */ if (((a->hi | a->lo) == 0) || (ediff < -35)) { /* a = 0 or "small"? */ *a = *b; /* rslt is b */ a->sign = a->sign ^ sub; /* or -b if sub */ } else if (((b->hi | b->lo) != 0) && (ediff <= 35)) { /* b!=0 && ~"small"? */ if (ediff > 0) /* |a| > |b|? dnorm b */ dp_dnrm_r (ir, b, ediff); else if (ediff < 0) { /* |a| < |b|? */ a->exp = b->exp; /* b exp is rslt */ dp_dnrm_r (ir, a, -ediff); /* denorm A */ } if (a->sign ^ b->sign ^ sub) /* eff sub? */ dp_sub (a, b); else { /* eff add */ dp_add (a, b); /* add */ if (a->hi & UFP_FH_CARRY) { /* carry out? */ fguard = a->lo & 1; /* set guard */ dp_rsh_1 (a, NULL); /* right shift */ a->exp = a->exp + 1; /* incr exponent */ if (!(ir & FI_NORND) && fguard) /* rounding? */ dp_inc (a); } } } /* end if b != 0 */ fp15_asign (ir, a); /* adjust A sign */ return fp15_norm (ir, a, NULL, 0); /* norm, no round */ } /* Floating multiply - overflow/underflow detected in normalize */ t_stat fp15_fmul (int32 ir, UFP *a, UFP *b) { a->sign = a->sign ^ b->sign; /* sign of result */ a->exp = a->exp + b->exp; /* exp of result */ dp_mul (a, b); /* mul fractions */ fp15_asign (ir, a); /* adjust A sign */ return fp15_norm (ir, a, &fmq, 1); /* norm and round */ } /* Floating divide - overflow/underflow detected in normalize */ t_stat fp15_fdiv (int32 ir, UFP *a, UFP *b) { int32 i; a->sign = a->sign ^ b->sign; /* sign of result */ a->exp = a->exp - b->exp; /* exp of result */ fmq.hi = fmq.lo = 0; /* clear quotient */ if (!(b->hi & UFP_FH_NORM)) /* divr not norm? */ return FP_DIV; if (a->hi | a->lo) { /* divd non-zero? */ fp15_norm (0, a, NULL, 0); /* normalize divd */ for (i = 0; (fmq.hi & UFP_FH_NORM) == 0; i++) { /* until quo */ dp_lsh_1 (&fmq, NULL); /* left shift quo */ if (dp_cmp (a, b) >= 0) { /* sub work? */ dp_sub (a, b); /* a = a - b */ if (i == 0) a->exp = a->exp + 1; fmq.lo = fmq.lo | 1; /* set quo bit */ } dp_lsh_1 (a, NULL); /* left shift divd */ } dp_rsh_1 (a, NULL); /* shift back */ dp_swap (a, &fmq); /* swap a, FMQ */ } fp15_asign (ir, a); /* adjust A sign */ return fp15_norm (ir, a, &fmq, 1); /* norm and round */ } /* Floating to integer - overflow only if exponent out of range */ t_stat fp15_fix (int32 ir, UFP *a) { int32 i; fmq.hi = fmq.lo = 0; /* clear FMQ */ if (a->exp > 35) /* exp > 35? ovf */ return FP_OVF; if (a->exp < 0) /* exp <0 ? rslt 0 */ a->hi = a->lo = 0; else { for (i = a->exp; i < 35; i++) /* denorm frac */ dp_rsh_1 (a, &fmq); if (fmq.hi & UFP_FH_NORM) { /* last out = 1? */ fguard = 1; /* set guard */ if (!(ir & FI_NORND)) /* round */ dp_inc (a); } } fp15_asign (ir, a); /* adjust A sign */ return FP_OK; } /* Double precision routines */ /* Double precision add - returns 72b result (including carry) */ void dp_add (UFP *a, UFP *b) { a->lo = (a->lo + b->lo) & UFP_FL_MASK; /* add low */ a->hi = a->hi + b->hi + (a->lo < b->lo); /* add hi + carry */ return; } /* Double precision increment - returns 72b result (including carry) */ void dp_inc (UFP *a) { a->lo = (a->lo + 1) & UFP_FL_MASK; /* inc low */ a->hi = a->hi + (a->lo == 0); /* propagate carry */ return; } /* Double precision subtract - result always fits in 71b */ void dp_sub (UFP *a, UFP *b) { if (dp_cmp (a,b) >= 0) { /* |a| >= |b|? */ a->hi = (a->hi - b->hi - (a->lo < b->lo)) & UFP_FH_MASK; a->lo = (a->lo - b->lo) & UFP_FL_MASK; /* a - b */ } else { a->hi = (b->hi - a->hi - (b->lo < a->lo)) & UFP_FH_MASK; a->lo = (b->lo - a->lo) & UFP_FL_MASK; /* b - a */ a->sign = a->sign ^ 1; /* change a sign */ } return; } /* Double precision compare - returns +1 (>), 0 (=), -1 (<) */ int32 dp_cmp (UFP *a, UFP *b) { if (a->hi < b->hi) return -1; if (a->hi > b->hi) return +1; if (a->lo < b->lo) return -1; if (a->lo > b->lo) return +1; return 0; } /* Double precision multiply - returns 70b result in a'fmq */ void dp_mul (UFP *a, UFP *b) { int32 i; fmq.hi = a->hi; /* FMQ <- a */ fmq.lo = a->lo; a->hi = a->lo = 0; /* a <- 0 */ if ((fmq.hi | fmq.lo) == 0) return; if ((b->hi | b->lo) == 0) { fmq.hi = fmq.lo = 0; return; } for (i = 0; i < 35; i++) { /* 35 iterations */ if (fmq.lo & 1) /* FMQ<35>? a += b */ dp_add (a, b); dp_rsh_1 (a, &fmq); /* rsh a'FMQ */ } return; } /* Double (quad) precision left shift - returns 72b (143b) result */ void dp_lsh_1 (UFP *a, UFP *b) { int32 t = b? b->hi: 0; a->hi = (a->hi << 1) | ((a->lo >> 17) & 1); a->lo = ((a->lo << 1) | ((t >> 16) & 1)) & UFP_FL_MASK; if (b) { b->hi = ((b->hi << 1) | ((b->lo >> 17) & 1)) & UFP_FH_MASK; b->lo = (b->lo << 1) & UFP_FL_MASK; } return; } /* Double (quad) precision right shift - returns 71b (142b) result */ void dp_rsh_1 (UFP *a, UFP *b) { if (b) { b->lo = (b->lo >> 1) | ((b->hi & 1) << 17); b->hi = (b->hi >> 1) | ((a->lo & 1) << 16); } a->lo = (a->lo >> 1) | ((a->hi & 1) << 17); a->hi = a->hi >> 1; return; } /* Double precision denormalize and round - returns 71b result */ void dp_dnrm_r (int32 ir, UFP *a, int32 sc) { int32 i; if (sc <= 0) /* legit? */ return; for (i = 0; i < sc; i++) /* dnorm to fmq */ dp_rsh_1 (a, &fmq); if (!(ir & FI_NORND) && (fmq.hi & UFP_FH_NORM)) /* round & fmq<1>? */ dp_inc (a); /* incr a */ return; } /* Double precision swap */ void dp_swap (UFP *a, UFP *b) { int32 t; t = a->hi; /* swap fractions */ a->hi = b->hi; b->hi = t; t = a->lo; a->lo = b->lo; b->lo = t; return; } /* Support routines */ void fp15_asign (int32 fir, UFP *a) { int32 sgnop = FI_GETSGNOP (fir); switch (sgnop) { /* modify FMA sign */ case 1: a->sign = 0; break; case 2: a->sign = 1; break; case 3: a->sign = a->sign ^ 1; break; default: break; } return; } /* FP15 normalization and rounding - Do normalization if enabled (NOR phase, part 1) Normalization also does zero detect - Do rounding if enabled (NOR phase, part 2) */ t_stat fp15_norm (int32 ir, UFP *a, UFP *b, t_bool rnd) { a->hi = a->hi & UFP_FH_MASK; /* mask a */ a->lo = a->lo & UFP_FL_MASK; if (b) { /* if b, mask */ b->hi = b->hi & UFP_FH_MASK; b->lo = b->lo & UFP_FL_MASK; } if (!(ir & FI_NONORM)) { /* norm enabled? */ if ((a->hi | a->lo) || (b && (b->hi | b->lo))) { /* frac != 0? */ while ((a->hi & UFP_FH_NORM) == 0) { /* until norm */ dp_lsh_1 (a, b); /* lsh a'b, no cry */ a->exp = a->exp - 1; /* decr exp */ } } else a->sign = a->exp = 0; /* true zero */ } if (rnd && b && (b->hi & UFP_FH_NORM)) { /* rounding? */ fguard = 1; /* set guard */ if (!(ir & FI_NORND)) { /* round enabled? */ dp_inc (a); /* add 1 */ if (a->hi & UFP_FH_CARRY) { /* carry out? */ a->hi = UFP_FH_NORM; /* set hi bit */ a->exp = a->exp + 1; /* incr exp */ } } } if (a->exp > (int32) 0377777) /* overflow? */ return FP_OVF; if (a->exp < (int32) -0400000) /* underflow? */ return FP_UNF; return FP_OK; } /* Exception */ t_stat fp15_exc (t_stat sta) { int32 ma, mb; if (sta == FP_MM) /* if mm, kill trap */ trap_pending = 0; ma = (jea & JEA_EAMASK) + sta - 1; /* JEA address */ PCQ_ENTRY; /* record branch */ PC = Incr_addr (PC); /* PC+1 for "JMS" */ mb = Jms_word (usmd); /* form JMS word */ if (Write (ma, mb, WR)) /* store */ return SCPE_OK; PC = (ma + 1) & IAMASK; /* new PC */ return SCPE_OK; } /* Reset routine */ t_stat fp15_reset (DEVICE *dptr) { jea = 0; fir = 0; fguard = 0; fma.exp = fma.hi = fma.lo = fma.sign = 0; fmb.exp = fmb.hi = fmb.lo = fmb.sign = 0; fmq.exp = fmq.hi = fmq.lo = fmq.sign = 0; return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_tt1.c0000644000175000017500000003546311112110732013571 0ustar vlmvlm/* pdp18b_ttx.c: PDP-9/15 additional terminals simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ttix,ttox LT15/LT19 terminal input/output 19-Nov-08 RMS Revised for common TMXR show routines 18-Jun-07 RMS Added UNIT_IDLE flag 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 22-Nov-05 RMS Revised for new terminal processing routines 29-Jun-05 RMS Added SET TTOXn DISCONNECT 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS 14-Jan-04 RMS Cloned from pdp8_ttx.c This module implements 16 individual serial interfaces similar in function to the console. These interfaces are mapped to Telnet based connections as though they were the four lines of a terminal multiplexor. The connection polling mechanism is superimposed onto the keyboard of the first interface. */ #include "pdp18b_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #if defined (PDP15) #define TTX_MAXL 16 /* max number of lines */ #elif defined (PDP9) #define TTX_MAXL 4 #else #define TTX_MAXL 1 #endif uint32 ttix_done = 0; /* input flags */ uint32 ttox_done = 0; /* output flags */ uint8 ttix_buf[TTX_MAXL] = { 0 }; /* input buffers */ uint8 ttox_buf[TTX_MAXL] = { 0 }; /* output buffers */ TMLN ttx_ldsc[TTX_MAXL] = { 0 }; /* line descriptors */ TMXR ttx_desc = { 1, 0, 0, ttx_ldsc }; /* mux descriptor */ #define ttx_lines ttx_desc.lines /* current number of lines */ extern int32 int_hwre[API_HLVL+1]; extern int32 tmxr_poll; extern int32 stop_inst; DEVICE ttix_dev, ttox_dev; int32 ttix (int32 dev, int32 pulse, int32 dat); int32 ttox (int32 dev, int32 pulse, int32 dat); t_stat ttix_svc (UNIT *uptr); t_bool ttix_test_done (int32 ln); void ttix_set_done (int32 ln); void ttix_clr_done (int32 ln); t_stat ttox_svc (UNIT *uptr); t_bool ttox_test_done (int32 ln); void ttox_set_done (int32 ln); void ttox_clr_done (int32 ln); int32 ttx_getln (int32 dev, int32 pulse); t_stat ttx_attach (UNIT *uptr, char *cptr); t_stat ttx_detach (UNIT *uptr); t_stat ttx_reset (DEVICE *dptr); void ttx_reset_ln (int32 i); t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); /* TTIx data structures ttix_dev TTIx device descriptor ttix_unit TTIx unit descriptor ttix_reg TTIx register list ttix_mod TTIx modifiers list */ DIB ttix_dib = { DEV_TTO1, 8, NULL, { &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix } }; UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG ttix_reg[] = { { BRDATA (BUF, ttix_buf, 8, 8, TTX_MAXL) }, { ORDATA (DONE, ttix_done, TTX_MAXL) }, { FLDATA (INT, int_hwre[API_TTI1], INT_V_TTI1) }, { DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT }, { ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO }, { NULL } }; MTAB ttix_mod[] = { { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &ttx_vlines, &tmxr_show_lines, (void *) &ttx_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno, NULL }, { 0 } }; DEVICE tti1_dev = { "TTIX", &ttix_unit, ttix_reg, ttix_mod, 1, 10, 31, 1, 8, 8, &tmxr_ex, &tmxr_dep, &ttx_reset, NULL, &ttx_attach, &ttx_detach, &ttix_dib, DEV_NET | DEV_DISABLE }; /* TTOx data structures ttox_dev TTOx device descriptor ttox_unit TTOx unit descriptor ttox_reg TTOx register list */ UNIT ttox_unit[] = { { UDATA (&ttox_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT } }; REG ttox_reg[] = { { BRDATA (BUF, ttox_buf, 8, 8, TTX_MAXL) }, { ORDATA (DONE, ttox_done, TTX_MAXL) }, { FLDATA (INT, int_hwre[API_TTO1], INT_V_TTO1) }, { URDATA (TIME, ttox_unit[0].wait, 10, 24, 0, TTX_MAXL, PV_LEFT) }, { NULL } }; MTAB ttox_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &ttx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &ttx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &ttx_desc }, { 0 } }; DEVICE tto1_dev = { "TTOX", ttox_unit, ttox_reg, ttox_mod, TTX_MAXL, 10, 31, 1, 8, 8, NULL, NULL, &ttx_reset, NULL, NULL, NULL, NULL, DEV_DISABLE }; /* Terminal input: IOT routine */ int32 ttix (int32 dev, int32 pulse, int32 dat) { int32 ln = ttx_getln (dev, pulse); /* line # */ if (ln > ttx_lines) return dat; if (pulse & 001) { /* KSF1 */ if (ttix_test_done (ln)) dat = dat | IOT_SKP; } if (pulse & 002) { /* KRB1 */ ttix_clr_done (ln); /* clear flag */ dat = dat | ttix_buf[ln]; /* return buffer */ } return dat; } /* Unit service */ t_stat ttix_svc (UNIT *uptr) { int32 ln, c, temp; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; sim_activate (uptr, tmxr_poll); /* continue poll */ ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ if (ln >= 0) /* got one? rcv enab */ ttx_ldsc[ln].rcve = 1; tmxr_poll_rx (&ttx_desc); /* poll for input */ for (ln = 0; ln < TTX_MAXL; ln++) { /* loop thru lines */ if (ttx_ldsc[ln].conn) { /* connected? */ if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */ if (temp & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR); ttix_buf[ln] = c; ttix_set_done (ln); } } } return SCPE_OK; } /* Interrupt handling routines */ t_bool ttix_test_done (int32 ln) { if (ttix_done & (1 << ln)) return TRUE; return FALSE; } void ttix_set_done (int32 ln) { ttix_done = ttix_done | (1 << ln); SET_INT (TTI1); return; } void ttix_clr_done (int32 ln) { ttix_done = ttix_done & ~(1 << ln); if (ttix_done) { SET_INT (TTI1); } else { CLR_INT (TTI1); } return; } /* Terminal output: IOT routine */ int32 ttox (int32 dev, int32 pulse, int32 dat) { int32 ln = ttx_getln (dev, pulse); /* line # */ if (ln > ttx_lines) return dat; if (pulse & 001) { /* TSF */ if (ttox_test_done (ln)) dat = dat | IOT_SKP; } if (pulse & 002) /* clear flag */ ttox_clr_done (ln); if (pulse & 004) { /* load buffer */ sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate unit */ ttox_buf[ln] = dat & 0377; /* load buffer */ } return dat; } /* Unit service */ t_stat ttox_svc (UNIT *uptr) { int32 c, ln = uptr - ttox_unit; /* line # */ if (ttx_ldsc[ln].conn) { /* connected? */ if (ttx_ldsc[ln].xmte) { /* tx enabled? */ TMLN *lp = &ttx_ldsc[ln]; /* get line */ c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR); if (c >= 0) /* output char */ tmxr_putc_ln (lp, c); tmxr_poll_tx (&ttx_desc); /* poll xmt */ } else { tmxr_poll_tx (&ttx_desc); /* poll xmt */ sim_activate (uptr, ttox_unit[ln].wait); /* wait */ return SCPE_OK; } } ttox_set_done (ln); /* set done */ return SCPE_OK; } /* Interrupt handling routines */ t_bool ttox_test_done (int32 ln) { if (ttox_done & (1 << ln)) return TRUE; return FALSE; } void ttox_set_done (int32 ln) { ttox_done = ttox_done | (1 << ln); SET_INT (TTO1); return; } void ttox_clr_done (int32 ln) { ttox_done = ttox_done & ~(1 << ln); if (ttox_done) { SET_INT (TTO1); } else { CLR_INT (TTO1); } return; } /* Compute relative line number This algorithm does not assign contiguous line numbers of ascending LT19's. Rather, line numbers follow a simple progression based on the relative IOT number and the subdevice select */ int32 ttx_getln (int32 dev, int32 pulse) { int32 rdno = ((dev - ttix_dib.dev) >> 1) & 3; #if defined (PDP15) /* PDP-15? */ int32 sub = (pulse >> 4) & 3; return (rdno * 4) + sub; /* use dev, subdev */ #else /* others */ return rdno; /* use dev only */ #endif } /* Reset routine */ t_stat ttx_reset (DEVICE *dptr) { int32 ln; if (dptr->flags & DEV_DIS) { /* sync enables */ ttix_dev.flags = ttix_dev.flags | DEV_DIS; ttox_dev.flags = ttox_dev.flags | DEV_DIS; } else { ttix_dev.flags = ttix_dev.flags & ~DEV_DIS; ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; } if (ttix_unit.flags & UNIT_ATT) /* if attached, */ sim_activate (&ttix_unit, tmxr_poll); /* activate */ else sim_cancel (&ttix_unit); /* else stop */ for (ln = 0; ln < TTX_MAXL; ln++) /* for all lines */ ttx_reset_ln (ln); return SCPE_OK; } /* Reset line n */ void ttx_reset_ln (int32 ln) { ttix_buf[ln] = 0; /* clear buf, */ ttox_buf[ln] = 0; ttix_clr_done (ln); /* clear done */ ttox_clr_done (ln); sim_cancel (&ttox_unit[ln]); /* stop poll */ return; } /* Attach master unit */ t_stat ttx_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_activate (uptr, tmxr_poll); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat ttx_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&ttx_desc, uptr); /* detach */ sim_cancel (uptr); /* stop poll */ for (i = 0; i < TTX_MAXL; i++) /* disable rcv */ ttx_ldsc[i].rcve = 0; return r; } /* Change number of lines */ t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, TTX_MAXL, &r); if ((r != SCPE_OK) || (newln == ttx_lines)) return r; if (newln == 0) return SCPE_ARG; if (newln < ttx_lines) { for (i = newln, t = 0; i < ttx_lines; i++) t = t | ttx_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < ttx_lines; i++) { if (ttx_ldsc[i].conn) { tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&ttx_ldsc[i]); /* reset line */ } ttox_unit[i].flags = ttox_unit[i].flags | UNIT_DIS; ttx_reset_ln (i); } } else { for (i = ttx_lines; i < newln; i++) { ttox_unit[i].flags = ttox_unit[i].flags & ~UNIT_DIS; ttx_reset_ln (i); } } ttx_lines = newln; return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_rb.c0000644000175000017500000002667511112110732013471 0ustar vlmvlm/* pdp18b_rb.c: RB09 fixed head disk simulator Copyright (c) 2003-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rb RB09 fixed head disk 14-Jan-04 RMS Revised IO device call interface 26-Oct-03 RMS Cleaned up buffer copy code The RB09 is a head-per-track disk. It uses the single cycle data break facility. To minimize overhead, the entire RB09 is buffered in memory. Two timing parameters are provided: rb_time Interword timing. Must be non-zero. rb_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise, DMA occurs in a burst. */ #include "pdp18b_defs.h" #include /* Constants */ #define RB_NUMWD 64 /* words/sector */ #define RB_NUMSC 80 /* sectors/track */ #define RB_NUMTR 200 /* tracks/disk */ #define RB_WLKTR 10 /* tracks/wlock switch */ #define RB_SIZE (RB_NUMTR * RB_NUMSC * RB_NUMWD) /* words/drive */ /* Function/status register */ #define RBS_ERR 0400000 /* error */ #define RBS_PAR 0200000 /* parity error */ #define RBS_ILA 0100000 /* ill addr error */ #define RBS_TIM 0040000 /* timing transfer */ #define RBS_NRY 0020000 /* not ready error */ #define RBS_DON 0010000 /* done */ #define RBS_IE 0004000 /* int enable */ #define RBS_BSY 0002000 /* busy */ #define RBS_WR 0001000 /* read/write */ #define RBS_XOR (RBS_IE|RBS_BSY|RBS_WR) /* set by XOR */ #define RBS_MBZ 0000777 /* always clear */ #define RBS_EFLGS (RBS_PAR|RBS_ILA|RBS_TIM|RBS_NRY) /* error flags */ /* BCD disk address */ #define RBA_V_TR 8 #define RBA_M_TR 0x1FF #define RBA_V_SC 0 #define RBA_M_SC 0xFF #define RBA_GETTR(x) (((x) >> RBA_V_TR) & RBA_M_TR) #define RBA_GETSC(x) (((x) >> RBA_V_SC) & RBA_M_SC) #define GET_POS(x) ((int) fmod (sim_gtime () / ((double) (x)), \ ((double) (RB_NUMSC * RB_NUMWD)))) extern int32 M[]; extern int32 int_hwre[API_HLVL+1]; extern UNIT cpu_unit; int32 rb_sta = 0; /* status register */ int32 rb_da = 0; /* disk address */ int32 rb_ma = 0; /* current addr */ int32 rb_wc = 0; /* word count */ int32 rb_wlk = 0; /* write lock */ int32 rb_time = 10; /* inter-word time */ int32 rb_burst = 1; /* burst mode flag */ int32 rb_stopioe = 1; /* stop on error */ DEVICE rb_dev; int32 rb71 (int32 dev, int32 pulse, int32 AC); t_stat rb_svc (UNIT *uptr); t_stat rb_reset (DEVICE *dptr); int32 rb_updsta (int32 new); int32 rb_make_da (int32 dat); int32 rb_make_bcd (int32 dat); int32 rb_set_da (int32 dat, int32 old); int32 rb_set_bcd (int32 dat); /* RB data structures rb_dev RF device descriptor rb_unit RF unit descriptor rb_reg RF register list */ DIB rb_dib = { DEV_RB, 1, NULL, { &rb71 } }; UNIT rb_unit = { UDATA (&rb_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RB_SIZE) }; REG rb_reg[] = { { ORDATA (STA, rb_sta, 18) }, { ORDATA (DA, rb_da, 20) }, { ORDATA (WC, rb_wc, 16) }, { ORDATA (MA, rb_ma, ADDRSIZE) }, { FLDATA (INT, int_hwre[API_RB], INT_V_RB) }, { ORDATA (WLK, rb_wlk, RB_NUMTR / RB_WLKTR) }, { DRDATA (TIME, rb_time, 24), PV_LEFT + REG_NZ }, { FLDATA (BURST, rb_burst, 0) }, { FLDATA (STOP_IOE, rb_stopioe, 0) }, { ORDATA (DEVNO, rb_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rb_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE rb_dev = { "RB", &rb_unit, rb_reg, rb_mod, 1, 8, 21, 1, 8, 18, NULL, NULL, &rb_reset, NULL, NULL, NULL, &rb_dib, DEV_DIS | DEV_DISABLE }; /* IOT routines */ int32 rb71 (int32 dev, int32 pulse, int32 AC) { int32 tow, t, sb = pulse & 060; if (pulse & 001) { if (sb == 000) /* DBCF */ rb_sta = rb_sta & ~(RBS_ERR | RBS_EFLGS | RBS_DON); if ((sb == 020) && (rb_sta & (RBS_ERR | RBS_DON))) AC = AC | IOT_SKP; /* DBSF */ if (sb == 040) /* DBCS */ rb_sta = 0; } if (pulse & 002) { if (sb == 000) /* DBRD */ AC = AC | rb_make_da (rb_da); if (sb == 020) /* DBRS */ AC = AC | rb_sta; if (sb == 040) /* DBLM */ rb_ma = AC & AMASK; } if (pulse & 004) { if (sb == 000) /* DBLD */ rb_da = rb_set_da (AC, rb_da); if (sb == 020) /* DBLW */ rb_wc = AC & 0177777; if (sb == 040) { /* DBLS */ rb_sta = (rb_sta & RBS_XOR) ^ (AC & ~RBS_MBZ); if (rb_sta & RBS_BSY) { /* busy set? */ if (!sim_is_active (&rb_unit)) { /* schedule */ tow = rb_da % (RB_NUMSC * RB_NUMWD); t = tow - GET_POS (rb_time); if (t < 0) t = t + (RB_NUMSC * RB_NUMWD); sim_activate (&rb_unit, t * rb_time); } } else sim_cancel (&rb_unit); /* no, stop */ } } rb_updsta (0); /* update status */ return AC; } int32 rb_make_da (int32 da) { int32 t = da / (RB_NUMSC * RB_NUMWD); /* bin track */ int32 s = (da % (RB_NUMSC * RB_NUMWD)) / RB_NUMWD; /* bin sector */ int32 bcd_t = rb_make_bcd (t); /* bcd track */ int32 bcd_s = rb_make_bcd (s); /* bcd sector */ return (bcd_t << RBA_V_TR) | (bcd_s << RBA_V_SC); } int32 rb_set_da (int32 bcda, int32 old_da) { int32 bcd_t = RBA_GETTR (bcda); /* bcd track */ int32 bcd_s = RBA_GETSC (bcda); /* bcd sector */ int32 t = rb_set_bcd (bcd_t); /* bin track */ int32 s = rb_set_bcd (bcd_s); /* bin sector */ if ((t >= RB_NUMTR) || (t < 0) || /* invalid? */ (s >= RB_NUMSC) || (s < 0)) { rb_updsta (RBS_ILA); /* error */ return old_da; /* don't change */ } else return (((t * RB_NUMSC) + s) * RB_NUMWD); /* new da */ } int32 rb_make_bcd (int32 bin) { int32 d, i, r; for (r = i = 0; bin != 0; bin = bin / 10) { /* while nz */ d = bin % 10; /* dec digit */ r = r | (d << i); /* insert bcd */ i = i + 4; } return r; } int32 rb_set_bcd (int32 bcd) { int32 d, i, r; for (r = 0, i = 1; bcd != 0; bcd = bcd >> 4) { /* while nz */ d = bcd & 0xF; /* bcd digit */ if (d >= 10) /* invalid? */ return -1; r = r + (d * i); /* insert bin */ i = i * 10; } return r; } /* Unit service - disk is buffered in memory */ t_stat rb_svc (UNIT *uptr) { int32 t, sw; int32 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ rb_updsta (RBS_NRY | RBS_DON); /* set nxd, done */ return IORETURN (rb_stopioe, SCPE_UNATT); } do { if (rb_sta & RBS_WR) { /* write? */ t = rb_da / (RB_NUMSC * RB_NUMWD); /* track */ sw = t / RB_WLKTR; /* switch */ if ((rb_wlk >> sw) & 1) { /* write locked? */ rb_updsta (RBS_ILA | RBS_DON); break; } else { /* not locked */ fbuf[rb_da] = M[rb_ma]; /* write word */ if (((t_addr) rb_da) >= uptr->hwmark) uptr->hwmark = rb_da + 1; } } else if (MEM_ADDR_OK (rb_ma)) /* read, valid addr? */ M[rb_ma] = fbuf[rb_da]; /* read word */ rb_wc = (rb_wc + 1) & 0177777; /* incr word count */ rb_ma = (rb_ma + 1) & AMASK; /* incr mem addr */ rb_da = rb_da + 1; /* incr disk addr */ if (rb_da > RB_SIZE) /* disk wraparound? */ rb_da = 0; } while ((rb_wc != 0) && (rb_burst != 0)); /* brk if wc, no brst */ if ((rb_wc != 0) && ((rb_sta & RBS_ERR) == 0)) /* more to do? */ sim_activate (&rb_unit, rb_time); /* sched next */ else rb_updsta (RBS_DON); /* set done */ return SCPE_OK; } /* Update status */ int32 rb_updsta (int32 new) { rb_sta = (rb_sta | new) & ~(RBS_ERR | RBS_MBZ); /* clear err, mbz */ if (rb_sta & RBS_EFLGS) /* error? */ rb_sta = rb_sta | RBS_ERR; if (rb_sta & RBS_DON) /* done? clear busy */ rb_sta = rb_sta & ~RBS_BSY; if ((rb_sta & (RBS_ERR | RBS_DON)) && (rb_sta & RBS_IE)) SET_INT (RB); /* set or clr intr */ else CLR_INT (RB); return rb_sta; } /* Reset routine */ t_stat rb_reset (DEVICE *dptr) { rb_sta = rb_da = 0; rb_wc = rb_ma = 0; rb_updsta (0); sim_cancel (&rb_unit); return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_rf.c0000644000175000017500000003505211112110732013462 0ustar vlmvlm/* pdp18b_rf.c: fixed head disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rf (PDP-9) RF09/RF09 (PDP-15) RF15/RS09 04-Oct-06 RMS Fixed bug, DSCD does not clear function register 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) 14-Jan-04 RMS Revised IO device call interface Changed sim_fsize calling sequence 26-Oct-03 RMS Cleaned up buffer copy code 26-Jul-03 RMS Fixed bug in set size routine 14-Mar-03 RMS Fixed variable platter interaction with save/restore 03-Mar-03 RMS Fixed autosizing 12-Feb-03 RMS Removed 8 platter sizing hack 05-Feb-03 RMS Fixed decode bugs, added variable and autosizing 05-Oct-02 RMS Added DIB, dev number support 06-Jan-02 RMS Revised enable/disable support 25-Nov-01 RMS Revised interrupt structure 24-Nov-01 RMS Changed WLK to array 26-Apr-01 RMS Added device enable/disable support 15-Feb-01 RMS Fixed 3 cycle data break sequencing 30-Nov-99 RMS Added non-zero requirement to rf_time 14-Apr-99 RMS Changed t_addr to unsigned The RFxx is a head-per-track disk. It uses the multicycle data break facility. To minimize overhead, the entire RFxx is buffered in memory. Two timing parameters are provided: rf_time Interword timing. Must be non-zero. rf_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise, DMA occurs in a burst. */ #include "pdp18b_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT 07 #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define RF_NUMWD 2048 /* words/track */ #define RF_NUMTR 128 /* tracks/disk */ #define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ #define RF_NUMDK 8 /* disks/controller */ #define RF_WMASK (RF_NUMWD - 1) /* word mask */ #define RF_WC 036 /* word count */ #define RF_CA 037 /* current addr */ /* Function/status register */ #define RFS_ERR 0400000 /* error */ #define RFS_HDW 0200000 /* hardware error */ #define RFS_APE 0100000 /* addr parity error */ #define RFS_MXF 0040000 /* missed transfer */ #define RFS_WCE 0020000 /* write check error */ #define RFS_DPE 0010000 /* data parity error */ #define RFS_WLO 0004000 /* write lock error */ #define RFS_NED 0002000 /* non-existent disk */ #define RFS_DCH 0001000 /* data chan timing */ #define RFS_PGE 0000400 /* programming error */ #define RFS_DON 0000200 /* transfer complete */ #define RFS_V_FNC 1 /* function */ #define RFS_M_FNC 03 #define RFS_FNC (RFS_M_FNC << RFS_V_FNC) #define FN_NOP 0 #define FN_READ 1 #define FN_WRITE 2 #define FN_WCHK 3 #define RFS_IE 0000001 /* interrupt enable */ #define RFS_CLR 0000170 /* always clear */ #define RFS_EFLGS (RFS_HDW | RFS_APE | RFS_MXF | RFS_WCE | \ RFS_DPE | RFS_WLO | RFS_NED ) /* error flags */ #define RFS_FR (RFS_FNC|RFS_IE) #define GET_FNC(x) (((x) >> RFS_V_FNC) & RFS_M_FNC) #define GET_POS(x) ((int) fmod (sim_gtime () / ((double) (x)), \ ((double) RF_NUMWD))) #define RF_BUSY (sim_is_active (&rf_unit)) extern int32 M[]; extern int32 int_hwre[API_HLVL+1]; extern UNIT cpu_unit; int32 rf_sta = 0; /* status register */ int32 rf_da = 0; /* disk address */ int32 rf_dbuf = 0; /* data buffer */ int32 rf_wlk[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* write lock */ int32 rf_time = 10; /* inter-word time */ int32 rf_burst = 1; /* burst mode flag */ int32 rf_stopioe = 1; /* stop on error */ DEVICE rf_dev; int32 rf70 (int32 dev, int32 pulse, int32 dat); int32 rf72 (int32 dev, int32 pulse, int32 dat); int32 rf_iors (void); t_stat rf_svc (UNIT *uptr); t_stat rf_reset (DEVICE *dptr); int32 rf_updsta (int32 new); t_stat rf_attach (UNIT *uptr, char *cptr); t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); /* RF data structures rf_dev RF device descriptor rf_unit RF unit descriptor rf_reg RF register list */ DIB rf_dib = { DEV_RF, 3, &rf_iors, { &rf70, NULL, &rf72 } }; UNIT rf_unit = { UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_AUTO, RF_DKSIZE) }; REG rf_reg[] = { { ORDATA (STA, rf_sta, 18) }, { ORDATA (DA, rf_da, 22) }, { ORDATA (WC, M[RF_WC], 18) }, { ORDATA (CA, M[RF_CA], 18) }, { ORDATA (BUF, rf_dbuf, 18) }, { FLDATA (INT, int_hwre[API_RF], INT_V_RF) }, { BRDATA (WLK, rf_wlk, 8, 16, RF_NUMDK) }, { DRDATA (TIME, rf_time, 24), PV_LEFT + REG_NZ }, { FLDATA (BURST, rf_burst, 0) }, { FLDATA (STOP_IOE, rf_stopioe, 0) }, { DRDATA (CAPAC, rf_unit.capac, 31), PV_LEFT + REG_HRO }, { ORDATA (DEVNO, rf_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rf_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &rf_set_size }, { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &rf_set_size }, { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &rf_set_size }, { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &rf_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE rf_dev = { "RF", &rf_unit, rf_reg, rf_mod, 1, 8, 21, 1, 8, 18, NULL, NULL, &rf_reset, NULL, &rf_attach, NULL, &rf_dib, DEV_DISABLE }; /* IOT routines */ int32 rf70 (int32 dev, int32 pulse, int32 dat) { int32 t, sb; sb = pulse & 060; /* subopcode */ if (pulse & 01) { if ((sb == 000) && (rf_sta & (RFS_ERR | RFS_DON))) /* DSSF */ dat = IOT_SKP | dat; else if (sb == 020) /* DSCC */ rf_reset (&rf_dev); else if (sb == 040) { /* DSCF */ if (RF_BUSY) /* busy inhibits */ rf_sta = rf_sta | RFS_PGE; else rf_sta = rf_sta & ~(RFS_FNC | RFS_IE); /* clear func */ } } if (pulse & 02) { if (RF_BUSY) /* busy sets PGE */ rf_sta = rf_sta | RFS_PGE; else if (sb == 000) /* DRBR */ dat = dat | rf_dbuf; else if (sb == 020) /* DRAL */ dat = dat | (rf_da & DMASK); else if (sb == 040) /* DSFX */ rf_sta = rf_sta ^ (dat & (RFS_FNC | RFS_IE)); /* xor func */ else if (sb == 060) /* DRAH */ dat = dat | (rf_da >> 18) | ((rf_sta & RFS_NED)? 010: 0); } if (pulse & 04) { if (RF_BUSY) /* busy sets PGE */ rf_sta = rf_sta | RFS_PGE; else if (sb == 000) /* DLBR */ rf_dbuf = dat & DMASK; else if (sb == 020) /* DLAL */ rf_da = (rf_da & ~DMASK) | (dat & DMASK); else if (sb == 040) { /* DSCN */ rf_sta = rf_sta & ~RFS_DON; /* clear done */ if (GET_FNC (rf_sta) != FN_NOP) { t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new */ if (t < 0) /* wrap around? */ t = t + RF_NUMWD; sim_activate (&rf_unit, t * rf_time); /* schedule op */ } } else if (sb == 060) { /* DLAH */ rf_da = (rf_da & DMASK) | ((dat & 07) << 18); if ((uint32) rf_da >= rf_unit.capac) /* for sizing */ rf_updsta (RFS_NED); } } rf_updsta (0); /* update status */ return dat; } int32 rf72 (int32 dev, int32 pulse, int32 dat) { int32 sb = pulse & 060; if (pulse & 02) { if (sb == 000) /* DLOK */ dat = dat | GET_POS (rf_time) | (sim_is_active (&rf_unit)? 0400000: 0); else if (sb == 040) { /* DSCD */ if (RF_BUSY) /* busy inhibits */ rf_sta = rf_sta | RFS_PGE; else rf_sta = rf_sta & RFS_FR; rf_updsta (0); } else if (sb == 060) { /* DSRS */ if (RF_BUSY) /* busy sets PGE */ rf_sta = rf_sta | RFS_PGE; dat = dat | rf_updsta (0); } } return dat; } /* Unit service - assumes the entire disk is buffered */ t_stat rf_svc (UNIT *uptr) { int32 f, pa, d, t; int32 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ rf_updsta (RFS_NED | RFS_DON); /* set nxd, done */ return IORETURN (rf_stopioe, SCPE_UNATT); } f = GET_FNC (rf_sta); /* get function */ do { if ((uint32) rf_da >= uptr->capac) { /* disk overflow? */ rf_updsta (RFS_NED); /* nx disk error */ break; } M[RF_WC] = (M[RF_WC] + 1) & DMASK; /* incr word count */ pa = M[RF_CA] = (M[RF_CA] + 1) & AMASK; /* incr mem addr */ if ((f == FN_READ) && MEM_ADDR_OK (pa)) /* read? */ M[pa] = fbuf[rf_da]; if ((f == FN_WCHK) && (M[pa] != fbuf[rf_da])) { /* write check? */ rf_updsta (RFS_WCE); /* flag error */ break; } if (f == FN_WRITE) { /* write? */ d = (rf_da >> 18) & 07; /* disk */ t = (rf_da >> 14) & 017; /* track groups */ if ((rf_wlk[d] >> t) & 1) { /* write locked? */ rf_updsta (RFS_WLO); break; } else { /* not locked */ fbuf[rf_da] = M[pa]; /* write word */ if (((uint32) rf_da) >= uptr->hwmark) uptr->hwmark = rf_da + 1; } } rf_da = rf_da + 1; /* incr disk addr */ } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ sim_activate (&rf_unit, rf_time); /* sched next */ else rf_updsta (RFS_DON); return SCPE_OK; } /* Update status */ int32 rf_updsta (int32 new) { rf_sta = (rf_sta | new) & ~(RFS_ERR | RFS_CLR); if (rf_sta & RFS_EFLGS) rf_sta = rf_sta | RFS_ERR; if ((rf_sta & (RFS_ERR | RFS_DON)) && (rf_sta & RFS_IE)) SET_INT (RF); else CLR_INT (RF); return rf_sta; } /* Reset routine */ t_stat rf_reset (DEVICE *dptr) { rf_sta = rf_da = rf_dbuf = 0; rf_updsta (0); sim_cancel (&rf_unit); return SCPE_OK; } /* IORS routine */ int32 rf_iors (void) { return ((rf_sta & (RFS_ERR | RFS_DON))? IOS_RF: 0); } /* Attach routine */ t_stat rf_attach (UNIT *uptr, char *cptr) { uint32 p, sz; uint32 ds_bytes = RF_DKSIZE * sizeof (int32); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= RF_NUMDK) p = RF_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; return attach_unit (uptr, cptr); } /* Change disk size */ t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETP (val) * RF_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_rp.c0000644000175000017500000004756011107426474013524 0ustar vlmvlm/* pdp18b_rp.c: RP15/RP02 disk pack simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rp RP15/RP02 disk pack 14-Jan-04 RMS Revised IO device call interface 06-Feb-03 RMS Revised IOT decoding, fixed bug in initiation 05-Oct-02 RMS Added DIB, device number support 06-Jan-02 RMS Revised enable/disable support 29-Nov-01 RMS Added read only unit support 25-Nov-01 RMS Revised interrupt structure Changed FLG to array 26-Apr-01 RMS Added device enable/disable support 14-Apr-99 RMS Changed t_addr to unsigned 29-Jun-96 RMS Added unit enable/disable support */ #include "pdp18b_defs.h" /* Constants */ #define RP_NUMWD 256 /* words/sector */ #define RP_NUMSC 10 /* sectors/surface */ #define RP_NUMSF 20 /* surfaces/cylinder */ #define RP_NUMCY 203 /* cylinders/drive */ #define RP_NUMDR 8 /* drives/controller */ #define RP_SIZE (RP_NUMCY * RP_NUMSF * RP_NUMSC * RP_NUMWD) /* words/drive */ /* Unit specific flags */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */ #define UNIT_WLK (1u << UNIT_V_WLK) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ #define FUNC u4 /* function */ /* Status register A */ #define STA_V_UNIT 15 /* unit select */ #define STA_M_UNIT 07 #define STA_V_FUNC 12 /* function */ #define STA_M_FUNC 07 #define FN_IDLE 0 #define FN_READ 1 #define FN_WRITE 2 #define FN_RECAL 3 #define FN_SEEK 4 #define FN_RDALL 5 #define FN_WRALL 6 #define FN_WRCHK 7 #define FN_2ND 010 /* second state flag */ #define STA_IED 0004000 /* int enable done */ #define STA_IEA 0002000 /* int enable attn */ #define STA_GO 0001000 /* go */ #define STA_WPE 0000400 /* write lock error */ #define STA_NXC 0000200 /* nx cyl error */ #define STA_NXF 0000100 /* nx surface error */ #define STA_NXS 0000040 /* nx sector error */ #define STA_HNF 0000020 /* hdr not found */ #define STA_SUWP 0000010 /* sel unit wrt lock */ #define STA_SUSI 0000004 /* sel unit seek inc */ #define STA_DON 0000002 /* done */ #define STA_ERR 0000001 /* error */ #define STA_RW 0777000 /* read/write */ #define STA_EFLGS (STA_WPE | STA_NXC | STA_NXF | STA_NXS | \ STA_HNF | STA_SUSI) /* error flags */ #define STA_DYN (STA_SUWP | STA_SUSI) /* per unit status */ #define GET_UNIT(x) (((x) >> STA_V_UNIT) & STA_M_UNIT) #define GET_FUNC(x) (((x) >> STA_V_FUNC) & STA_M_FUNC) /* Status register B */ #define STB_V_ATT0 17 /* unit 0 attention */ #define STB_ATTN 0776000 /* attention flags */ #define STB_SUFU 0001000 /* sel unit unsafe */ #define STB_PGE 0000400 /* programming error */ #define STB_EOP 0000200 /* end of pack */ #define STB_TME 0000100 /* timing error */ #define STB_FME 0000040 /* format error */ #define STB_WCE 0000020 /* write check error */ #define STB_WPE 0000010 /* word parity error */ #define STB_LON 0000004 /* long parity error */ #define STB_SUSU 0000002 /* sel unit seeking */ #define STB_SUNR 0000001 /* sel unit not rdy */ #define STB_EFLGS (STB_SUFU | STB_PGE | STB_EOP | STB_TME | STB_FME | \ STB_WCE | STB_WPE | STB_LON ) /* error flags */ #define STB_DYN (STB_SUFU | STB_SUSU | STB_SUNR) /* per unit */ /* Disk address */ #define DA_V_SECT 0 /* sector */ #define DA_M_SECT 017 #define DA_V_SURF 5 #define DA_M_SURF 037 #define DA_V_CYL 10 /* cylinder */ #define DA_M_CYL 0377 #define GET_SECT(x) (((x) >> DA_V_SECT) & DA_M_SECT) #define GET_SURF(x) (((x) >> DA_V_SURF) & DA_M_SURF) #define GET_CYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) #define GET_DA(x) ((((GET_CYL (x) * RP_NUMSF) + GET_SURF (x)) * \ RP_NUMSC) + GET_SECT (x)) #define RP_MIN 2 #define MAX(x,y) (((x) > (y))? (x): (y)) extern int32 M[]; extern int32 int_hwre[API_HLVL+1], nexm; extern UNIT cpu_unit; int32 rp_sta = 0; /* status A */ int32 rp_stb = 0; /* status B */ int32 rp_ma = 0; /* memory address */ int32 rp_da = 0; /* disk address */ int32 rp_wc = 0; /* word count */ int32 rp_busy = 0; /* busy */ int32 rp_stopioe = 1; /* stop on error */ int32 rp_swait = 10; /* seek time */ int32 rp_rwait = 10; /* rotate time */ DEVICE rp_dev; int32 rp63 (int32 dev, int32 pulse, int32 dat); int32 rp64 (int32 dev, int32 pulse, int32 dat); int32 rp_iors (void); t_stat rp_svc (UNIT *uptr); void rp_updsta (int32 newa, int32 newb); t_stat rp_reset (DEVICE *dptr); t_stat rp_attach (UNIT *uptr, char *cptr); t_stat rp_detach (UNIT *uptr); /* RP15 data structures rp_dev RP device descriptor rp_unit RP unit list rp_reg RP register list rp_mod RP modifier list */ DIB rp_dib = { DEV_RP, 2, &rp_iors, { &rp63, &rp64 } }; UNIT rp_unit[] = { { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) } }; REG rp_reg[] = { { ORDATA (STA, rp_sta, 18) }, { ORDATA (STB, rp_stb, 18) }, { ORDATA (DA, rp_da, 18) }, { ORDATA (MA, rp_ma, 18) }, { ORDATA (WC, rp_wc, 18) }, { FLDATA (INT, int_hwre[API_RP], INT_V_RP) }, { FLDATA (BUSY, rp_busy, 0) }, { FLDATA (STOP_IOE, rp_stopioe, 0) }, { DRDATA (STIME, rp_swait, 24), PV_LEFT }, { DRDATA (RTIME, rp_rwait, 24), PV_LEFT }, { ORDATA (DEVNO, rp_dib.dev, 6), REG_HRO }, { NULL } }; MTAB rp_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE rp_dev = { "RP", rp_unit, rp_reg, rp_mod, RP_NUMDR, 8, 24, 1, 8, 18, NULL, NULL, &rp_reset, NULL, &rp_attach, &rp_detach, &rp_dib, DEV_DISABLE }; /* IOT routines */ int32 rp63 (int32 dev, int32 pulse, int32 dat) { int32 sb = pulse & 060; /* subopcode */ rp_updsta (0, 0); if (pulse & 01) { if ((sb == 000) && /* DPSF */ ((rp_sta & (STA_DON | STA_ERR)) || (rp_stb & STB_ATTN))) dat = IOT_SKP | dat; else if ((sb == 020) && (rp_stb & STB_ATTN)) /* DPSA */ dat = IOT_SKP | dat; else if ((sb == 040) && (rp_sta & STA_DON)) /* DPSJ */ dat = IOT_SKP | dat; else if ((sb == 060) && (rp_sta & STA_ERR)) /* DPSE */ dat = IOT_SKP | dat; } if (pulse & 02) { if (sb == 000) /* DPOSA */ dat = dat | rp_sta; else if (sb == 020) /* DPOSB */ dat = dat | rp_stb; } if (pulse & 04) { if (rp_busy) { /* busy? */ rp_updsta (0, STB_PGE); return dat; } else if (sb == 000) { /* DPLA */ rp_da = dat & DMASK; if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); } else if (sb == 020) { /* DPCS */ rp_sta = rp_sta & ~(STA_HNF | STA_DON); rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | STB_TME | STB_PGE | STB_EOP); rp_updsta (0, 0); } else if (sb == 040) /* DPCA */ rp_ma = dat & DMASK; else if (sb == 060) /* DPWC */ rp_wc = dat & DMASK; } return dat; } /* IOT 64 */ int32 rp64 (int32 dev, int32 pulse, int32 dat) { int32 u, f, c, sb; UNIT *uptr; sb = pulse & 060; if (pulse & 01) { if (sb == 020) /* DPSN */ dat = IOT_SKP | dat; } if (pulse & 02) { if (sb == 000) /* DPOU */ dat = dat | rp_unit[GET_UNIT (rp_sta)].CYL; else if (sb == 020) /* DPOA */ dat = dat | rp_da; else if (sb == 040) /* DPOC */ dat = dat | rp_ma; else if (sb == 060) /* DPOW */ dat = dat | rp_wc; } if (pulse & 04) { if (rp_busy) { /* busy? */ rp_updsta (0, STB_PGE); return dat; } if (sb == 000) /* DPCF */ rp_sta = rp_sta & ~STA_RW; else if (sb == 020) /* DPLZ */ rp_sta = rp_sta & (dat | ~STA_RW); else if (sb == 040) /* DPLO */ rp_sta = rp_sta | (dat & STA_RW); else if (sb == 060) /* DPLF */ rp_sta = (rp_sta & ~STA_RW) | (dat & STA_RW); rp_sta = rp_sta & ~STA_DON; /* clear done */ u = GET_UNIT (rp_sta); /* get unit num */ uptr = rp_dev.units + u; /* select unit */ if ((rp_sta & STA_GO) && !sim_is_active (uptr)) { f = uptr->FUNC = GET_FUNC (rp_sta); /* get function */ rp_busy = 1; /* set ctrl busy */ rp_sta = rp_sta & ~(STA_HNF | STA_DON); /* clear flags */ rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | STB_TME | STB_PGE | STB_EOP | (1 << (STB_V_ATT0 - u))); if (((uptr->flags & UNIT_ATT) == 0) || (f == FN_IDLE) || (f == FN_SEEK) || (f == FN_RECAL)) sim_activate (uptr, RP_MIN); /* short delay */ else { c = GET_CYL (rp_da); c = abs (c - uptr->CYL) * rp_swait; /* seek time */ sim_activate (uptr, MAX (RP_MIN, c + rp_rwait)); } } } rp_updsta (0, 0); return dat; } /* Unit service If function = idle, clear busy If seek or recal initial state, clear attention line, compute seek time, put on cylinder, set second state If unit not attached, give error If seek or recal second state, set attention line, compute errors Else complete data transfer command The unit control block contains the function and cylinder for the current command. */ static int32 fill[RP_NUMWD] = { 0 }; t_stat rp_svc (UNIT *uptr) { int32 f, u, comp, cyl, sect, surf; int32 err, pa, da, wc, awc, i; u = (int32) (uptr - rp_dev.units); /* get drv number */ f = uptr->FUNC; /* get function */ if (f == FN_IDLE) { /* idle? */ rp_busy = 0; /* clear busy */ return SCPE_OK; } if ((f == FN_SEEK) || (f == FN_RECAL)) { /* seek or recal? */ rp_busy = 0; /* not busy */ cyl = (f == FN_SEEK)? GET_CYL (rp_da): 0; /* get cylinder */ sim_activate (uptr, MAX (RP_MIN, abs (cyl - uptr->CYL) * rp_swait)); uptr->CYL = cyl; /* on cylinder */ uptr->FUNC = FN_SEEK | FN_2ND; /* set second state */ rp_updsta (0, 0); /* update status */ return SCPE_OK; } if (f == (FN_SEEK | FN_2ND)) { /* seek done? */ rp_updsta (0, rp_stb | (1 << (STB_V_ATT0 - u))); /* set attention */ return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ return IORETURN (rp_stopioe, SCPE_UNATT); } if ((f == FN_WRITE) && (uptr->flags & UNIT_WPRT)) { /* write locked? */ rp_updsta (STA_DON | STA_WPE, 0); /* error */ return SCPE_OK; } if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); if (rp_sta & (STA_NXS | STA_NXF | STA_NXC)) { /* or bad disk addr? */ rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ return SCPE_OK; } pa = rp_ma & AMASK; /* get mem addr */ da = GET_DA (rp_da) * RP_NUMWD; /* get disk addr */ wc = 01000000 - rp_wc; /* get true wc */ if (((uint32) (pa + wc)) > MEMSIZE) { /* memory overrun? */ nexm = 1; /* set nexm flag */ wc = MEMSIZE - pa; /* limit xfer */ } if ((da + wc) > RP_SIZE) { /* disk overrun? */ rp_updsta (0, STB_EOP); /* error */ wc = RP_SIZE - da; /* limit xfer */ } err = fseek (uptr->fileref, da * sizeof (int), SEEK_SET); if ((f == FN_READ) && (err == 0)) { /* read? */ awc = fxread (&M[pa], sizeof (int32), wc, uptr->fileref); for ( ; awc < wc; awc++) M[pa + awc] = 0; err = ferror (uptr->fileref); } if ((f == FN_WRITE) && (err == 0)) { /* write? */ fxwrite (&M[pa], sizeof (int32), wc, uptr->fileref); err = ferror (uptr->fileref); if ((err == 0) && (i = (wc & (RP_NUMWD - 1)))) { fxwrite (fill, sizeof (int), i, uptr->fileref); err = ferror (uptr->fileref); } } if ((f == FN_WRCHK) && (err == 0)) { /* write check? */ for (i = 0; (err == 0) && (i < wc); i++) { awc = fxread (&comp, sizeof (int32), 1, uptr->fileref); if (awc == 0) comp = 0; if (comp != M[pa + i]) rp_updsta (0, STB_WCE); } err = ferror (uptr->fileref); } rp_wc = (rp_wc + wc) & DMASK; /* final word count */ rp_ma = (rp_ma + wc) & DMASK; /* final mem addr */ da = (da + wc + (RP_NUMWD - 1)) / RP_NUMWD; /* final sector num */ cyl = da / (RP_NUMSC * RP_NUMSF); /* get cyl */ if (cyl >= RP_NUMCY) cyl = RP_NUMCY - 1; surf = (da % (RP_NUMSC * RP_NUMSF)) / RP_NUMSC; /* get surface */ sect = (da % (RP_NUMSC * RP_NUMSF)) % RP_NUMSC; /* get sector */ rp_da = (cyl << DA_V_CYL) | (surf << DA_V_SURF) | (sect << DA_V_SECT); rp_busy = 0; /* clear busy */ rp_updsta (STA_DON, 0); /* set done */ if (err != 0) { /* error? */ perror ("RP I/O error"); clearerr (uptr->fileref); return IORETURN (rp_stopioe, SCPE_IOERR); } return SCPE_OK; } /* Update status */ void rp_updsta (int32 newa, int32 newb) { int32 f; UNIT *uptr; uptr = rp_dev.units + GET_UNIT (rp_sta); rp_sta = (rp_sta & ~(STA_DYN | STA_ERR)) | newa; rp_stb = (rp_stb & ~STB_DYN) | newb; if (uptr->flags & UNIT_WPRT) rp_sta = rp_sta | STA_SUWP; if ((uptr->flags & UNIT_ATT) == 0) rp_stb = rp_stb | STB_SUFU | STB_SUNR; else if (sim_is_active (uptr)) { f = (uptr->FUNC) & STA_M_FUNC; if ((f == FN_SEEK) || (f == FN_RECAL)) rp_stb = rp_stb | STB_SUSU | STB_SUNR; } else if (uptr->CYL >= RP_NUMCY) rp_sta = rp_sta | STA_SUSI; if ((rp_sta & STA_EFLGS) || (rp_stb & STB_EFLGS)) rp_sta = rp_sta | STA_ERR; if (((rp_sta & (STA_ERR | STA_DON)) && (rp_sta & STA_IED)) || ((rp_stb & STB_ATTN) && (rp_sta & STA_IEA))) SET_INT (RP); else CLR_INT (RP); return; } /* Reset routine */ t_stat rp_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rp_sta = rp_stb = rp_da = rp_wc = rp_ma = rp_busy = 0; CLR_INT (RP); for (i = 0; i < RP_NUMDR; i++) { uptr = rp_dev.units + i; sim_cancel (uptr); uptr->CYL = uptr->FUNC = 0; } return SCPE_OK; } /* IORS routine */ int32 rp_iors (void) { return ((rp_sta & (STA_ERR | STA_DON)) || (rp_stb & STB_ATTN))? IOS_RP: 0; } /* Attach unit */ t_stat rp_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); rp_updsta (0, 0); return reason; } /* Detach unit */ t_stat rp_detach (UNIT *uptr) { t_stat reason; reason = detach_unit (uptr); rp_updsta (0, 0); return reason; } simh-3.8.1/PDP18B/pdp18b_sys.c0000644000175000017500000013167611112110440013676 0ustar vlmvlm/* pdp18b_sys.c: 18b PDP's simulator interface Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Oct-06 RMS Added infinite loop stop 18-Oct-06 RMS Re-ordered device list 02-Oct-06 RMS Added RDCLK instruction 12-Jun-06 RMS Added Fiodec, Baudot display RMS Generalized LOAD to handle HRI, RIM, or BIN files 22-Jul-05 RMS Removed AAS, error in V1 reference manual 09-Jan-04 RMS Fixed instruction table errors 18-Oct-03 RMS Added DECtape off reel message 30-Jul-03 RMS Fixed FPM class mask 18-Jul-03 RMS Added FP15 support 02-Mar-03 RMS Split loaders apart for greater flexibility 09-Feb-03 RMS Fixed bug in FMTASC (found by Hans Pufal) 31-Jan-03 RMS Added support for RB09 05-Oct-02 RMS Added variable device number support 25-Jul-02 RMS Added PDP-4 DECtape support 10-Feb-02 RMS Added PDP-7 DECtape IOT's 03-Feb-02 RMS Fixed typo (found by Robert Alan Byer) 17-Sep-01 RMS Removed multiconsole support 27-May-01 RMS Added second Teletype support 18-May-01 RMS Added PDP-9,-15 API IOT's 12-May-01 RMS Fixed bug in RIM loaders 14-Mar-01 RMS Added extension detection of RIM format tapes 21-Jan-01 RMS Added DECtape support 30-Nov-00 RMS Added PDP-9,-15 RIM/BIN loader format 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface 20-Oct-97 RMS Fixed endian dependence in RIM loader (found by Michael Somos) */ #include "pdp18b_defs.h" #include extern DEVICE cpu_dev; #if defined (PDP15) extern DEVICE fpp_dev; #endif extern DEVICE ptr_dev, ptp_dev; extern DEVICE tti_dev, tto_dev; extern UNIT tti_unit, tto_unit; extern DEVICE clk_dev; #if defined (TYPE62) extern DEVICE lp62_dev; #endif #if defined (TYPE647) extern DEVICE lp647_dev; #endif #if defined (LP09) extern DEVICE lp09_dev; #endif #if defined (LP15) extern DEVICE lp15_dev; #endif extern DEVICE dt_dev; #if defined (DRM) extern DEVICE drm_dev; #endif #if defined (RB) extern DEVICE rb_dev; #endif #if defined (RF) extern DEVICE rf_dev; #endif #if defined (RP) extern DEVICE rp_dev; #endif #if defined (MTA) extern DEVICE mt_dev; #endif #if defined (TTY1) extern DEVICE tti1_dev, tto1_dev; extern UNIT tti1_unit, tto1_unit; #endif extern UNIT cpu_unit; extern REG cpu_reg[]; extern int32 M[]; extern int32 memm; extern int32 PC; extern const char asc_to_baud[128]; extern const char baud_to_asc[64]; extern const char fio_to_asc[64]; /* 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 */ #if defined (PDP4) char sim_name[] = "PDP-4"; #elif defined (PDP7) char sim_name[] = "PDP-7"; #elif defined (PDP9) char sim_name[] = "PDP-9"; #elif defined (PDP15) char sim_name[] = "PDP-15"; #endif REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 2; DEVICE *sim_devices[] = { &cpu_dev, &clk_dev, #if defined (PDP15) &fpp_dev, #endif &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, #if defined (TYPE62) &lp62_dev, #endif #if defined (TYPE647) &lp647_dev, #endif #if defined (LP09) &lp09_dev, #endif #if defined (LP15) &lp15_dev, #endif #if defined (DRM) &drm_dev, #endif #if defined (RB) &rb_dev, #endif #if defined (RF) &rf_dev, #endif #if defined (RP) &rp_dev, #endif &dt_dev, #if defined (MTA) &mt_dev, #endif #if defined (TTY1) &tti1_dev, &tto1_dev, #endif NULL }; const char *sim_stop_messages[] = { "Unknown error", "Undefined instruction", "HALT instruction", "Breakpoint", "Nested XCT's", "Invalid API interrupt", "Non-standard device number", "Memory management error", "FP15 instruction disabled", "DECtape off reel", "Infinite loop" }; /* Binary loaders */ int32 getword (FILE *fileref, int32 *hi) { int32 word, bits, st, ch; word = st = bits = 0; do { if ((ch = getc (fileref)) == EOF) return -1; if (ch & 0200) { word = (word << 6) | (ch & 077); bits = (bits << 1) | ((ch >> 6) & 1); st++; } } while (st < 3); if (hi != NULL) *hi = bits; return word; } /* PDP-4/PDP-7 RIM format loader Tape format dac addr data : dac addr data jmp addr or hlt */ t_stat rim_load_47 (FILE *fileref, char *cptr) { int32 origin, val; if (*cptr != 0) return SCPE_2MARG; origin = 0200; for (;;) { if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; if ((val & 0760000) == 0040000) { /* DAC? */ origin = val & 017777; if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; if (MEM_ADDR_OK (origin)) M[origin++] = val; } else if ((val & 0760000) == OP_JMP) { /* JMP? */ PC = ((origin - 1) & 060000) | (val & 017777); return SCPE_OK; } else if (val == OP_HLT) /* HLT? */ break; else return SCPE_FMT; /* error */ } return SCPE_OK; /* done */ } /* PDP-7/9/15 hardware read-in format loader Tape format (read in address specified externally) data : data word to execute (bit 1 of last character set) */ t_stat hri_load_7915 (FILE *fileref, char *cptr) { int32 bits, origin, val; char gbuf[CBUFSIZE]; t_stat r; if (*cptr != 0) { /* more input? */ cptr = get_glyph (cptr, gbuf, 0); /* get origin */ origin = get_uint (gbuf, 8, AMASK, &r); if (r != SCPE_OK) return r; if (*cptr != 0) /* no more */ return SCPE_ARG; } else origin = 0200; /* default 200 */ for (;;) { /* word loop */ if ((val = getword (fileref, &bits)) < 0) return SCPE_FMT; if (bits & 1) { /* end of tape? */ if ((val & 0760000) == OP_JMP) PC = ((origin - 1) & 060000) | (val & 017777); else if (val != OP_HLT) return SCPE_FMT; break; } else if (MEM_ADDR_OK (origin)) M[origin++] = val; } return SCPE_OK; } /* PDP-9/15 BIN format loader BIN format (starts after RIM bootstrap) block/ origin (>= 0) count checksum data : data block/ : endblock/ origin (< 0) */ t_stat bin_load_915 (FILE *fileref, char *cptr) { int32 i, val, bits, origin, count, cksum; if (*cptr != 0) /* no arguments */ return SCPE_2MARG; do { val = getword (fileref, & bits); /* find end RIM */ } while ((val >= 0) && ((bits & 1) == 0)); if (val < 0) /* no RIM? rewind */ rewind (fileref); for (;;) { /* block loop */ if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; if (val & SIGN) { if (val != DMASK) PC = val & 077777; break; } cksum = origin = val; /* save origin */ if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; cksum = cksum + val; /* add to cksum */ count = (-val) & DMASK; /* save count */ if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; cksum = cksum + val; /* add to cksum */ for (i = 0; i < count; i++) { if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; cksum = cksum + val; if (MEM_ADDR_OK (origin)) M[origin++] = val; } if ((cksum & DMASK) != 0) return SCPE_CSUM; } return SCPE_OK; } /* Binary loader, all formats */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { extern int32 sim_switches; if (flag != 0) return SCPE_NOFNC; if (sim_switches & SWMASK ('S')) /* RIM format? */ return rim_load_47 (fileref, cptr); if (sim_switches & SWMASK ('R')) /* HRI format? */ return hri_load_7915 (fileref, cptr); if (!(sim_switches & SWMASK ('B')) && /* .rim extension? */ match_ext (fnam, "RIM")) { int32 val, bits; do { /* look for HRI flag */ val = getword (fileref, &bits); } while ((val >= 0) && ((bits & 1) == 0)); rewind (fileref); /* rewind file */ if (val < 0) /* eof reached? */ return rim_load_47 (fileref, cptr); return hri_load_7915 (fileref, cptr); /* no, HRI */ } return bin_load_915 (fileref, cptr); /* must be BIN */ } /* Symbol tables */ #define I_V_FL 18 /* inst class */ #define I_M_FL 017 /* class mask */ #define I_V_DC 22 /* default count */ #define I_V_NPN 0 /* no operand */ #define I_V_NPI 1 /* no operand IOT */ #define I_V_IOT 2 /* IOT */ #define I_V_MRF 3 /* memory reference */ #define I_V_OPR 4 /* OPR */ #define I_V_LAW 5 /* LAW */ #define I_V_XR 6 /* index */ #define I_V_XR9 7 /* index literal */ #define I_V_EST 8 /* EAE setup */ #define I_V_ESH 9 /* EAE shift */ #define I_V_EMD 10 /* EAE mul-div */ #define I_V_FPM 11 /* FP15 mem ref */ #define I_V_FPI 12 /* FP15 indirect */ #define I_V_FPN 13 /* FP15 no operand */ #define I_NPN (I_V_NPN << I_V_FL) #define I_NPI (I_V_NPI << I_V_FL) #define I_IOT (I_V_IOT << I_V_FL) #define I_MRF (I_V_MRF << I_V_FL) #define I_OPR (I_V_OPR << I_V_FL) #define I_LAW (I_V_LAW << I_V_FL) #define I_XR (I_V_XR << I_V_FL) #define I_XR9 (I_V_XR9 << I_V_FL) #define I_EST (I_V_EST << I_V_FL) #define I_ESH (I_V_ESH << I_V_FL) #define I_EMD (I_V_EMD << I_V_FL) #define I_FPM (I_V_FPM << I_V_FL) #define I_FPI (I_V_FPI << I_V_FL) #define I_FPN (I_V_FPN << I_V_FL) #define MD(x) ((I_EMD) + ((x) << I_V_DC)) static const int32 masks[] = { 0777777, 0777767, 0770000, 0760000, 0763730, 0760000, 0777000, 0777000, 0740700, 0760700, 0777700, 0777777, 0777777, 0777777 }; /* If both NPN (clear AC) and NPI versions of an IOT are defined, the NPN version must come first */ static const char *opcode[] = { "CAL", "DAC", "JMS", "DZM", /* mem refs */ "LAC", "XOR", "ADD", "TAD", "XCT", "ISZ", "AND", "SAD", "JMP", #if defined (PDP9) || defined (PDP15) /* mem ref ind */ "CAL*", "DAC*", "JMS*", "DZM*", /* normal */ "LAC*", "XOR*", "ADD*", "TAD*", "XCT*", "ISZ*", "AND*", "SAD*", "JMP*", #else "CAL I", "DAC I", "JMS I", "DZM I", /* decode only */ "LAC I", "XOR I", "ADD I", "TAD I", "XCT I", "ISZ I", "AND I", "SAD I", "JMP I", #endif "LAW", /* LAW */ "LACQ", "LACS", "ABS", "GSM", "LMQ", /* EAE */ "MUL", "MULS", "DIV", "DIVS", "IDIV", "IDIVS", "FRDIV", "FRDIVS", "NORM", "NORMS", "MUY", "LLK MUY", "DVI", "LLK DVI", "NMI", "NMIS", "LRS", "LRSS", "LLS", "LLSS", "ALS", "ALSS", "EAE-setup", "EAE", /* setup, general */ "CLSF", "IOF", "ION", "CLOF", "CLON", /* standard IO devs */ "RSF", "RRB", "RCF", "RSA", "RSB", "PSF", "PCF", "PSA", "PSB", "PLS", "KSF", "KRB", "KCF", "IORS", "IOOS", "TSF", "TCF", "TPC", "TLS", #if defined (TYPE62) /* Type 62 */ "LPSF", "LPCF", "LPLD", "LPSE", "LSSF", "LSCF", "LSPR", #endif #if defined (TYPE647) /* Type 647 */ "LPSF", "LPCB", "LPCD", "LPCD", "LPCD", "LPL2", "LPLD", "LPL1", "LPEF", "LPCF", "LPCF", "LPCF", "LPCF", "LPPB", "LPLS", "LPPS", #endif #if defined (LP09) "LSDF", "LSEF", "LSCF", "LPLD", "LIOF", "LION", #endif #if defined (LP15) /* LP15 */ "LPSF", "LPPM", "LPP1", "LPDI", "LPRS", "LPOS", "LPEI", "LPCD", "LPCF", #endif #if defined (DRM) /* drum */ "DRLR", "DRLW", "DRSS", "DRCS", "DRSF", "DRSN", "DRCF", "DRLCRD", "DRLCWR", "DRLBLK", "DRCONT", "DRSF", "DRSOK", "DRCF", #endif #if defined (RB) /* RB09 */ "DBCF", "DBRD", "DBLD", "DBSF", "DBRS", "DBLW", "DBCS", "DBLM", "DBLS", #endif #if defined (RF) /* RF09 */ "DSSF", "DSCC", "DSCF", "DRBR", "DRAL", "DSFX", "DRAH", "DLBR", "DLAL", "DSCN", "DLAH", "DLOK", "DSCD", "DSRS", "DGHS", "DGSS", #endif #if defined (RP) "DPSF", "DPSA", "DPSJ", "DPSE", "DPRSA", "DPOSA", "DPRSB", "DPOSB", "DPRM", "DPOM", "DPLA", "DPCS", "DPCA", "DPWC", "DPLM", "DPEM", "DPSN", "DPRU", "DPOU", "DPRA", "DPOA", "DPRC", "DPOC", "DPRW", "DPOW", "DPCF", "DPLZ", "DPCN", "DPLO", "DPLF", #endif #if defined (MTA) /* TC59 */ "MTTR", "MTCR", "MTSF", "MTRC", "MTAF", "MTRS", "MTGO", "MTCM", "MTLC", #endif #if defined (TYPE550) /* Type 550 */ "MMDF", "MMEF", "MMRD", "MMWR", "MMBF", "MMRS", "MMLC", "MMSE", #elif defined (TC02) /* TC02/TC15 */ "DTCA", "DTRA", "DTXA", "DTLA", "DTEF", "DTRB", "DTDF", #endif #if defined (TTY1) "KSF1", "KRB1", "TSF1", "TCF1", "TLS1", "TCF1!TLS1", #endif #if defined (PDP7) "ITON", "TTS", "SKP7", "CAF", "SEM", "EEM", "EMIR", "LEM", #endif #if defined (PDP9) "SKP7", "SEM", "EEM", "LEM", "LPDI", "LPEI", #endif #if defined (PDP15) "SPCO", "SKP15", "RES", "SBA", "DBA", "EBA", "RDMM", "ORMM", "LDMM", "MPLR", "ENB", "INH", "RDCLK","MPRC", "IPFH", "PAX", "PAL", "AAC", "PXA", "AXS", "PXL", "PLA", "PLX", "CLAC","CLX", "CLLR", "AXR", "FPT", /* FP15 */ "ISB", "ESB", /* mem ref */ "FSB", "URFSB", "UNFSB", "UUFSB", "DSB", "URDSB", "UNDSB", "UUDSB", "IRS", "ERS", "FRS", "URFRS", "UNFRS", "UUFRS", "DRS", "URDRS", "UNDRS", "UUDRS", "IMP", "EMP", "FMP", "URFMP", "UNFMP", "UUFMP", "DMP", "URDMP", "UNDMP", "UUDMP", "IDV", "EDV", "FDV", "URFDV", "UNFDV", "UUFDV", "DDV", "URDDV", "UNDDV", "UUDDV", "IRD", "ERD", "FRD", "URFRD", "UNFRD", "UUFRD", "DRD", "URDRD", "UNDRD", "UUDRD", "ILD", "ELD", "FLD", "UNFLD", "DLD", "UNDLD", "IST", "EST", "FST", "URFST", "UNFST", "UUFST", "DST", "UNDST", "ILF", "UNILF", "ELF", "UNELF", "FLX", "URFLX", "DLX", "URDLX", "ILQ", "ELQ", "FLQ", "UNFLQ", "DLQ", "UNDLQ", "LJE", "SJE", "IAD", "EAD", "FAD", "URFAD", "UNFAD", "UUFAD", "DAD", "URDAD", "UNDAD", "UUDAD", "BZA", "BMA", "BLE", "BPA", "BRU", "BNA", "BAC", "ISB*", "ESB*", /* indirect */ "FSB*", "URFSB*", "UNFSB*", "UUFSB*", "DSB*", "URDSB*", "UNDSB*", "UUDSB*", "IRS*", "ERS*", "FRS*", "URFRS*", "UNFRS*", "UUFRS*", "DRS*", "URDRS*", "UNDRS*", "UUDRS*", "IMP*", "EMP*", "FMP*", "URFMP*", "UNFMP*", "UUFMP*", "DMP*", "URDMP*", "UNDMP*", "UUDMP*", "IDV*", "EDV*", "FDV*", "URFDV*", "UNFDV*", "UUFDV*", "DDV*", "URDDV*", "UNDDV*", "UUDDV*", "IRD*", "ERD", "FRD*", "URFRD*", "UNFRD*", "UUFRD*", "DRD*", "URDRD*", "UNDRD*", "UUDRD*", "ILD*", "ELD", "FLD*", "UNFLD*", "DLD*", "UNDLD*", "IST*", "EST", "FST*", "URFST*", "UNFST*", "UUFST*", "DST*", "UNDST*", "ILF*", "UNILF*", "ELF*", "UNELF*", "FLX*", "URFLX*", "DLX*", "URDLX*", "ILQ*", "ELQ*", "FLQ*", "UNFLQ*", "DLQ*", "UNDLQ*", "LJE*", "SJE*", "IAD*", "EAD*", "FAD*", "URFAD*", "UNFAD*", "UUFAD*", "DAD*", "URDAD*", "UNDAD*", "UUDAD*", "FLA", "UNFLA", "FXA", "URFXA", /* no operand */ "SWQ", "UNSWQ", "FZR", "FAB", "FNG", "FCM", "FNM", #endif #if defined (PDP9) || defined (PDP15) "MPSK", "MPSNE", "MPCV", "MPEU", "MPLD", "MPCNE", "PFSF", "TTS", "CAF", "DBK", "DBR", "SPI", "RPL", "ISA", #endif "IOT", /* general */ "NOP", "STL", "RCL", "RCR", "CLC", "LAS", "GLK", "OPR", "SMA", "SZA", "SZA SMA", "SNL", "SNL SMA", "SNL SZA", "SNL SZA SMA", "SKP", "SPA", "SNA", "SNA SPA", "SZL", "SZL SPA", "SZL SNA", "SZL SZA SPA", "RAL", "SMA RAL", "SZA RAL", "SZA SMA RAL", "SNL RAL", "SNL SMA RAL", "SNL SZA RAL", "SNL SZA SMA RAL", "SKP RAL", "SPA RAL", "SNA RAL", "SNA SPA RAL", "SZL RAL", "SZL SPA RAL", "SZL SNA RAL", "SZL SZA SPA RAL", "RAR", "SMA RAR", "SZA RAR", "SZA SMA RAR", "SNL RAR", "SNL SMA RAR", "SNL SZA RAR", "SNL SZA SMA RAR", "SKP RAR", "SPA RAR", "SNA RAR", "SNA SPA RAR", "SZL RAR", "SZL SPA RAR", "SZL SNA RAR", "SZL SZA SPA RAR", #if defined (PDP15) "IAC", "SMA IAC", "SZA IAC", "SZA SMA IAC", "SNL IAC", "SNL SMA IAC", "SNL SZA IAC", "SNL SZA SMA IAC", "SKP IAC", "SPA IAC", "SNA IAC", "SNA SPA IAC", "SZL IAC", "SZL SPA IAC", "SZL SNA IAC", "SZL SZA SPA IAC", #else "RAL RAR", "SMA RAL RAR", "SZA RAL RAR", "SZA SMA RAL RAR", "SNL RAL RAR", "SNL SMA RAL RAR", "SNL SZA RAL RAR", "SNL SZA SMA RAL RAR", "SKP RAL RAR", "SPA RAL RAR", "SNA RAL RAR", "SNA SPA RAL RAR", "SZL RAL RAR", "SZL SPA RAL RAR", "SZL SNA RAL RAR", "SZL SZA SPA RAL RAR", #endif "RTWO", "SMA RTWO", "SZA RTWO", "SZA SMA RTWO", "SNL RTWO", "SNL SMA RTWO", "SNL SZA RTWO", "SNL SZA SMA RTWO", "SKP RTWO", "SPA RTWO", "SNA RTWO", "SNA SPA RTWO", "SZL RTWO", "SZL SPA RTWO", "SZL SNA RTWO", "SZL SZA SPA RTWO", "RTL", "SMA RTL", "SZA RTL", "SZA SMA RTL", "SNL RTL", "SNL SMA RTL", "SNL SZA RTL", "SNL SZA SMA RTL", "SKP RTL", "SPA RTL", "SNA RTL", "SNA SPA RTL", "SZL RTL", "SZL SPA RTL", "SZL SNA RTL", "SZL SZA SPA RTL", "RTR", "SMA RTR", "SZA RTR", "SZA SMA RTR", "SNL RTR", "SNL SMA RTR", "SNL SZA RTR", "SNL SZA SMA RTR", "SKP RTR", "SPA RTR", "SNA RTR", "SNA SPA RTR", "SZL RTR", "SZL SPA RTR", "SZL SNA RTR", "SZL SZA SPA RTR", #if defined (PDP15) "BSW", "SMA BSW", "SZA BSW", "SZA SMA BSW", "SNL BSW", "SNL SMA BSW", "SNL SZA BSW", "SNL SZA SMA BSW", "SKP BSW", "SPA BSW", "SNA BSW", "SNA SPA BSW", "SZL BSW", "SZL SPA BSW", "SZL SNA BSW", "SZL SZA SPA BSW", #else "RTL RTR", "SMA RTL RTR", "SZA RTL RTR", "SZA SMA RTL RTR", "SNL RTL RTR", "SNL SMA RTL RTR", "SNL SZA RTL RTR", "SNL SZA SMA RTL RTR", "SKP RTL RTR", "SPA RTL RTR", "SNA RTL RTR", "SNA SPA RTL RTR", "SZL RTL RTR", "SZL SPA RTL RTR", "SZL SNA RTL RTR", "SZL SZA SPA RTL RTR", #endif "LLK", "CLQ", "LSN", "OACQ", "ECLA", /* encode only masks */ "CMQ", "OMQ", "OSC", "CLA", "CLL", "CML", "CMA", "OAS", "HLT", NULL }; static const int32 opc_val[] = { 0000000+I_MRF, 0040000+I_MRF, 0100000+I_MRF, 0140000+I_MRF, 0200000+I_MRF, 0240000+I_MRF, 0300000+I_MRF, 0340000+I_MRF, 0400000+I_MRF, 0440000+I_MRF, 0500000+I_MRF, 0540000+I_MRF, 0600000+I_MRF, 0020000+I_MRF, 0060000+I_MRF, 0120000+I_MRF, 0160000+I_MRF, 0220000+I_MRF, 0260000+I_MRF, 0320000+I_MRF, 0360000+I_MRF, 0420000+I_MRF, 0460000+I_MRF, 0520000+I_MRF, 0560000+I_MRF, 0620000+I_MRF, 0760000+I_LAW, 0641002+I_NPN, 0641001+I_NPN, 0644000+I_NPN, 0664000+I_NPN, 0652000+I_NPN, 0653100+MD(022), 0657100+MD(022), 0640300+MD(023), 0644300+MD(023), 0653300+MD(023), 0657300+MD(023), 0650300+MD(023), 0654300+MD(023), 0640400+MD(044), 0660400+MD(044), 0640100+I_ESH, 0660100+I_ESH, 0640300+I_ESH, 0660300+I_ESH, 0640400+I_ESH, 0660400+I_ESH, 0640500+I_ESH, 0660500+I_ESH, 0640600+I_ESH, 0660600+I_ESH, 0640700+I_ESH, 0660700+I_ESH, 0640000+I_EST, 0640000+I_IOT, 0700001+I_NPI, 0700002+I_NPI, 0700042+I_NPI, 0700004+I_NPI, 0700044+I_NPI, 0700101+I_NPI, 0700112+I_NPN, 0700102+I_NPI, 0700104+I_NPI, 0700144+I_NPI, 0700201+I_NPI, 0700202+I_NPI, 0700204+I_NPI, 0700244+I_NPI, 0700206+I_NPI, 0700301+I_NPI, 0700312+I_NPN, 0700302+I_NPI, 0700314+I_NPN, 0700304+I_NPI, 0700401+I_NPI, 0700402+I_NPI, 0700404+I_NPI, 0700406+I_NPI, #if defined (TYPE62) 0706501+I_NPI, 0706502+I_NPI, 0706542+I_NPI, 0706506+I_NPI, 0706601+I_NPI, 0706602+I_NPI, 0706606+I_NPI, #endif #if defined (TYPE647) 0706501+I_NPI, 0706502+I_NPI, 0706522+I_NPI, 0706542+I_NPI, 0706562+I_NPI, 0706526+I_NPI, 0706546+I_NPI, 0706566+I_NPI, 0706601+I_NPI, 0706602+I_NPI, 0706622+I_NPI, 0706642+I_NPI, 0706662+I_NPI, 0706606+I_NPI, 0706626+I_NPI, 0706646+I_NPI, #endif #if defined (LP09) 0706601+I_NPI, 0706621+I_NPI, 0706602+I_NPI, 0706622+I_NPI, 0706604+I_NPI, 0706644+I_NPI, #endif #if defined (LP15) 0706501+I_NPI, 0706521+I_NPI, 0706541+I_NPI, 0706561+I_NPI, 0706552+I_NPN, 0706542+I_NPI, 0706544+I_NPI, 0706621+I_NPI, 0706641+I_NPI, #endif #if defined (DRM) 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, #endif #if defined (RB) 0707101+I_NPI, 0707112+I_NPN, 0707104+I_NPI, 0707121+I_NPI, 0707132+I_NPN, 0707124+I_NPI, 0707141+I_NPI, 0707142+I_NPI, 0707144+I_NPI, #endif #if defined (RF) 0707001+I_NPI, 0707021+I_NPI, 0707041+I_NPI, 0707002+I_NPI, 0707022+I_NPI, 0707042+I_NPI, 0707062+I_NPI, 0707004+I_NPI, 0707024+I_NPI, 0707044+I_NPI, 0707064+I_NPI, 0707202+I_NPI, 0707242+I_NPI, 0707262+I_NPI, 0707204+I_NPI, 0707224+I_NPI, #endif #if defined (RP) 0706301+I_NPI, 0706321+I_NPI, 0706341+I_NPI, 0706361+I_NPI, 0706312+I_NPN, 0706302+I_NPI, 0706332+I_NPN, 0706322+I_NPI, 0706352+I_NPN, 0706342+I_NPI, 0706304+I_NPI, 0706324+I_NPI, 0706344+I_NPI, 0706364+I_NPI, 0706411+I_NPN, 0706401+I_NPI, 0706421+I_NPI, 0706412+I_NPN, 0706402+I_NPI, 0706432+I_NPN, 0706422+I_NPI, 0706452+I_NPN, 0706442+I_NPI, 0706472+I_NPN, 0706462+I_NPI, 0706404+I_NPI, 0706424+I_NPI, 0706454+I_NPN, 0706444+I_NPI, 0706464+I_NPI, #endif #if defined (MTA) 0707301+I_NPI, 0707321+I_NPI, 0707341+I_NPI, 0707312+I_NPN, 0707322+I_NPI, 0707352+I_NPN, 0707304+I_NPI, 0707324+I_NPI, 0707326+I_NPI, #endif #if defined (TYPE550) /* Type 550 */ 0707501+I_NPI, 0707541+I_NPI, 0707512+I_NPN, 0707504+I_NPI, 0707601+I_NPI, 0707612+I_NPN, 0707604+I_NPI, 0707644+I_NPI, #elif defined (TC02) /* TC02/TC15 */ 0707541+I_NPI, 0707552+I_NPN, 0707544+I_NPI, 0707545+I_NPI, 0707561+I_NPI, 0707572+I_NPN, 0707601+I_NPI, #endif #if defined (TTY1) 0704101+I_NPI, 0704112+I_NPN, 0704001+I_NPI, 0704002+I_NPI, 0704004+I_NPI, 0704006+I_NPI, #endif #if defined (PDP7) 0703201+I_NPI, 0703301+I_NPI, 0703341+I_NPI, 0703302+I_NPI, 0707701+I_NPI, 0707702+I_NPI, 0707742+I_NPI, 0707704+I_NPI, #endif #if defined (PDP9) 0703341+I_NPI, 0707701+I_NPI, 0707702+I_NPI, 0707704+I_NPI, 0706504+I_NPI, 0706604+I_NPI, #endif #if defined (PDP15) 0703341+I_NPI, 0707741+I_NPI, 0707742+I_NPI, 0707761+I_NPI, 0707762+I_NPI, 0707764+I_NPI, 0700032+I_NPN, 0700022+I_NPI, 0700024+I_NPI, 0701724+I_NPI, 0705521+I_NPI, 0705522+I_NPI, 0701772+I_NPN, 0701762+I_NPI, 0701764+I_NPI, 0721000+I_XR, 0722000+I_XR, 0723000+I_XR9, 0724000+I_XR, 0725000+I_XR9, 0726000+I_XR, 0730000+I_XR, 0731000+I_XR, 0734000+I_XR, 0735000+I_XR, 0736000+I_XR, 0737000+I_XR9, 0710314+I_FPN, 0710400+I_FPM, 0710500+I_FPM, 0710440+I_FPM, 0710450+I_FPM, 0710460+I_FPM, 0710470+I_FPM, 0710540+I_FPM, 0710550+I_FPM, 0710560+I_FPM, 0710570+I_FPM, 0711000+I_FPM, 0711100+I_FPM, 0711040+I_FPM, 0711050+I_FPM, 0711060+I_FPM, 0711070+I_FPM, 0711140+I_FPM, 0711150+I_FPM, 0711160+I_FPM, 0711170+I_FPM, 0711400+I_FPM, 0711500+I_FPM, 0711440+I_FPM, 0711450+I_FPM, 0711460+I_FPM, 0711470+I_FPM, 0711540+I_FPM, 0711550+I_FPM, 0711560+I_FPM, 0711570+I_FPM, 0712000+I_FPM, 0712100+I_FPM, 0712040+I_FPM, 0712050+I_FPM, 0712060+I_FPM, 0712070+I_FPM, 0712140+I_FPM, 0712150+I_FPM, 0712160+I_FPM, 0712170+I_FPM, 0712400+I_FPM, 0712500+I_FPM, 0712440+I_FPM, 0712450+I_FPM, 0712460+I_FPM, 0712470+I_FPM, 0712540+I_FPM, 0712550+I_FPM, 0712560+I_FPM, 0712570+I_FPM, 0713000+I_FPM, 0713100+I_FPM, 0713050+I_FPM, 0713070+I_FPM, 0713150+I_FPM, 0713170+I_FPM, 0713600+I_FPM, 0713700+I_FPM, 0713640+I_FPM, 0713650+I_FPM, 0713660+I_FPM, 0713670+I_FPM, 0713750+I_FPM, 0713770+I_FPM, 0714010+I_FPM, 0714030+I_FPM, 0714110+I_FPM, 0714130+I_FPM, 0714460+I_FPM, 0714470+I_FPM, 0714560+I_FPM, 0714570+I_FPM, 0715000+I_FPM, 0715100+I_FPM, 0715050+I_FPM, 0715070+I_FPM, 0715150+I_FPM, 0715170+I_FPM, 0715400+I_FPM, 0715600+I_FPM, 0716000+I_FPM, 0716100+I_FPM, 0716040+I_FPM, 0716050+I_FPM, 0716060+I_FPM, 0716070+I_FPM, 0716140+I_FPM, 0716150+I_FPM, 0716160+I_FPM, 0716170+I_FPM, 0716601+I_FPM, 0716602+I_FPM, 0716603+I_FPM, 0716604+I_FPM, 0716606+I_FPM, 0716610+I_FPM, 0716620+I_FPM, 0710400+I_FPI, 0710500+I_FPI, /* indirect */ 0710440+I_FPI, 0710450+I_FPI, 0710460+I_FPI, 0710470+I_FPI, 0710540+I_FPI, 0710550+I_FPI, 0710560+I_FPI, 0710570+I_FPI, 0711000+I_FPI, 0711100+I_FPI, 0711040+I_FPI, 0711050+I_FPI, 0711060+I_FPI, 0711070+I_FPI, 0711140+I_FPI, 0711150+I_FPI, 0711160+I_FPI, 0711170+I_FPI, 0711400+I_FPI, 0711500+I_FPI, 0711440+I_FPI, 0711450+I_FPI, 0711460+I_FPI, 0711470+I_FPI, 0711540+I_FPI, 0711550+I_FPI, 0711560+I_FPI, 0711570+I_FPI, 0712000+I_FPI, 0712100+I_FPI, 0712040+I_FPI, 0712050+I_FPI, 0712060+I_FPI, 0712070+I_FPI, 0712140+I_FPI, 0712150+I_FPI, 0712160+I_FPI, 0712170+I_FPI, 0712400+I_FPI, 0712500+I_FPI, 0712440+I_FPI, 0712450+I_FPI, 0712460+I_FPI, 0712470+I_FPI, 0712540+I_FPI, 0712550+I_FPI, 0712560+I_FPI, 0712570+I_FPI, 0713000+I_FPI, 0713100+I_FPI, 0713050+I_FPI, 0713070+I_FPI, 0713150+I_FPI, 0713170+I_FPI, 0713600+I_FPI, 0713700+I_FPI, 0713640+I_FPI, 0713650+I_FPI, 0713660+I_FPI, 0713670+I_FPI, 0713750+I_FPI, 0713770+I_FPI, 0714010+I_FPI, 0714030+I_FPI, 0714110+I_FPI, 0714130+I_FPI, 0714460+I_FPI, 0714470+I_FPI, 0714560+I_FPI, 0714570+I_FPI, 0715000+I_FPI, 0715100+I_FPI, 0715050+I_FPI, 0715070+I_FPI, 0715150+I_FPI, 0715170+I_FPI, 0715400+I_FPI, 0715600+I_FPI, 0716000+I_FPI, 0716100+I_FPI, 0716040+I_FPI, 0716050+I_FPI, 0716060+I_FPI, 0716070+I_FPI, 0716140+I_FPI, 0716150+I_FPI, 0716160+I_FPI, 0716170+I_FPI, 0714210+I_FPN, 0714230+I_FPN, 0714660+I_FPN, 0714670+I_FPN, 0715250+I_FPN, 0715270+I_FPN, 0711200+I_FPN, 0713271+I_FPN, 0713272+I_FPN, 0713273+I_FPN, 0713250+I_FPN, #endif #if defined (PDP9) || defined (PDP15) 0701701+I_NPI, 0701741+I_NPI, 0701702+I_NPI, 0701742+I_NPI, 0701704+I_NPI, 0701744+I_NPI, 0703201+I_NPI, 0703301+I_NPI, 0703302+I_NPI, 0703304+I_NPI, 0703344+I_NPI, 0705501+I_NPI, 0705512+I_NPN, 0705504+I_NPI, #endif 0700000+I_IOT, 0740000+I_NPN, 0744002+I_NPN, 0744010+I_NPN, 0744020+I_NPN, 0750001+I_NPN, 0750004+I_NPN, 0750010+I_NPN, 0740000+I_OPR, 0740100+I_OPR, 0740200+I_OPR, 0740300+I_OPR, 0740400+I_OPR, 0740500+I_OPR, 0740600+I_OPR, 0740700+I_OPR, 0741000+I_OPR, 0741100+I_OPR, 0741200+I_OPR, 0741300+I_OPR, 0741400+I_OPR, 0741500+I_OPR, 0741600+I_OPR, 0741700+I_OPR, 0740010+I_OPR, 0740110+I_OPR, 0740210+I_OPR, 0740310+I_OPR, 0740410+I_OPR, 0740510+I_OPR, 0740610+I_OPR, 0740710+I_OPR, 0741010+I_OPR, 0741110+I_OPR, 0741210+I_OPR, 0741310+I_OPR, 0741410+I_OPR, 0741510+I_OPR, 0741610+I_OPR, 0741710+I_OPR, 0740020+I_OPR, 0740120+I_OPR, 0740220+I_OPR, 0740320+I_OPR, 0740420+I_OPR, 0740520+I_OPR, 0740620+I_OPR, 0740720+I_OPR, 0741020+I_OPR, 0741120+I_OPR, 0741220+I_OPR, 0741320+I_OPR, 0741420+I_OPR, 0741520+I_OPR, 0741620+I_OPR, 0741720+I_OPR, 0740030+I_OPR, 0740130+I_OPR, 0740230+I_OPR, 0740330+I_OPR, 0740430+I_OPR, 0740530+I_OPR, 0740630+I_OPR, 0740730+I_OPR, 0741030+I_OPR, 0741130+I_OPR, 0741230+I_OPR, 0741330+I_OPR, 0741430+I_OPR, 0741530+I_OPR, 0741630+I_OPR, 0741730+I_OPR, 0742000+I_OPR, 0742100+I_OPR, 0742200+I_OPR, 0742300+I_OPR, 0742400+I_OPR, 0742500+I_OPR, 0742600+I_OPR, 0742700+I_OPR, 0743000+I_OPR, 0743100+I_OPR, 0743200+I_OPR, 0743300+I_OPR, 0743400+I_OPR, 0743500+I_OPR, 0743600+I_OPR, 0743700+I_OPR, 0742010+I_OPR, 0742110+I_OPR, 0742210+I_OPR, 0742310+I_OPR, 0742410+I_OPR, 0742510+I_OPR, 0742610+I_OPR, 0742710+I_OPR, 0743010+I_OPR, 0743110+I_OPR, 0743210+I_OPR, 0743310+I_OPR, 0743410+I_OPR, 0743510+I_OPR, 0743610+I_OPR, 0743710+I_OPR, 0742020+I_OPR, 0742120+I_OPR, 0742220+I_OPR, 0742320+I_OPR, 0742420+I_OPR, 0742520+I_OPR, 0742620+I_OPR, 0742720+I_OPR, 0743020+I_OPR, 0743120+I_OPR, 0743220+I_OPR, 0743320+I_OPR, 0743420+I_OPR, 0743520+I_OPR, 0743620+I_OPR, 0743720+I_OPR, 0742030+I_OPR, 0742130+I_OPR, 0742230+I_OPR, 0742330+I_OPR, 0742430+I_OPR, 0742530+I_OPR, 0742630+I_OPR, 0742730+I_OPR, 0743030+I_OPR, 0743130+I_OPR, 0743230+I_OPR, 0743330+I_OPR, 0743430+I_OPR, 0743530+I_OPR, 0743630+I_OPR, 0743730+I_OPR, 0660000+I_EST, 0650000+I_EST, 0644000+I_EST, 0642000+I_EST, 0641000+I_EST, 0640004+I_EST, 0640002+I_EST, 0640001+I_EST, 0750000+I_OPR, 0744000+I_OPR, 0740002+I_OPR, 0740001+I_OPR, 0740004+I_OPR, 0740040+I_OPR, -1 }; /* Operate or EAE decode Inputs: *of = output stream inst = mask bits class = instruction class code sp = space needed? Outputs: status = space needed? */ int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) { int32 i, j; for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((j == class) && (opc_val[i] & inst)) { /* same class? */ inst = inst & ~opc_val[i]; /* mask bit set? */ fprintf (of, (sp? " %s": "%s"), opcode[i]); sp = 1; } } return sp; } static int32 rar (int32 c) { c = c & 077; return (c >> 1) | (c << 5); } /* 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) (((x) >= 040)? (x): ((x) + 0100)) t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, k, sp, inst, disp, ma; 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 ('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 defined (PDP4) || defined (PDP7) if (sw & SWMASK ('F')) { /* FIODEC? */ fprintf (of, "%c", fio_to_asc[(inst >> 12) & 077]); fprintf (of, "%c", fio_to_asc[(inst >> 6) & 077]); fprintf (of, "%c", fio_to_asc[inst & 077]); return SCPE_OK; } if (sw & SWMASK ('B')) { /* Baudot? */ fprintf (of, "%c", baud_to_asc[rar (inst >> 12) & 077]); fprintf (of, "%c", baud_to_asc[rar (inst >> 6) & 077]); fprintf (of, "%c", baud_to_asc[rar (inst) & 077]); return SCPE_OK; } #endif #if defined (PDP15) if (sw & SWMASK ('P')) { /* packed ASCII? */ i = val[1]; fprintf (of, FMTASC ((inst >> 11) & 0177)); fprintf (of, FMTASC ((inst >> 4) & 0177)); fprintf (of, FMTASC (((inst << 3) | (i >> 15)) & 0177)); fprintf (of, FMTASC ((i >> 8) & 0177)); fprintf (of, FMTASC ((i >> 1) & 0177)); return -1; } #endif if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* Instruction decode */ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ switch (j) { /* case on class */ case I_V_NPN: /* no operands */ case I_V_XR: /* index no opers */ fprintf (of, "%s", opcode[i]); /* opcode */ break; case I_V_NPI: /* IOT no operand */ fprintf (of, "%s", opcode[i]); /* opcode */ if (inst & 010) fprintf (of, " +10"); break; case I_V_IOT: /* IOT or EAE */ fprintf (of, "%s %-o", opcode[i], inst & 037777); break; case I_V_MRF: /* mem ref */ #if defined (PDP15) if (memm) { disp = inst & B_DAMASK; ma = (addr & (AMASK & ~B_DAMASK)) | disp; } else { disp = inst & P_DAMASK; ma = (addr & (AMASK & ~P_DAMASK)) | disp; } fprintf (of, "%s %-o", opcode[i], (cflag? ma & AMASK: disp)); if (!memm && (inst & I_IDX)) fprintf (of, ",X"); #else disp = inst & B_DAMASK; ma = (addr & (AMASK & ~B_DAMASK)) | disp; fprintf (of, "%s %-o", opcode[i], (cflag? ma & AMASK: disp)); #endif break; case I_V_OPR: /* operate */ if (sp = (inst & 03730)) fprintf (of, "%s", opcode[i]); fprint_opr (of, inst & 014047, I_V_OPR, sp); break; case I_V_LAW: /* LAW */ fprintf (of, "%s %-o", opcode[i], inst & 017777); break; case I_V_XR9: /* index with lit */ disp = inst & 0777; if (disp & 0400) fprintf (of, "%s -%-o", opcode[i], 01000 - disp); else fprintf (of, "%s %-o", opcode[i], disp); break; case I_V_EST: /* EAE setup */ fprint_opr (of, inst & 037007, I_V_EST, 0); break; case I_V_ESH: /* EAE shift */ sp = fprint_opr (of, inst & 017000, I_V_EST, 0); fprintf (of, (sp? " %s %-o": "%s %-o"), opcode[i], inst & 077); break; case I_V_EMD: /* EAE mul-div */ disp = inst & 077; /* get actual val */ k = (opc_val[i] >> I_V_DC) & 077; /* get default val */ if (disp == k) fprintf (of, "%s", opcode[i]); else if (disp < k) fprintf (of, "%s -%-o", opcode[i], k - disp); else fprintf (of, "%s +%-o", opcode[i], disp - k); break; case I_V_FPM: case I_V_FPI: /* FP15 mem ref */ fprintf (of, "%s", opcode[i]); if (val[1] & SIGN) fputc ('*', of); fprintf (of, " %-o", val[1] & ~SIGN); return -1; case I_V_FPN: /* FP15 no operand */ fprintf (of, "%s", opcode[i]); return -1; } /* end case */ return SCPE_OK; } /* end if */ } /* end for */ return SCPE_ARG; } /* 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 = 0; if (*cptr == '+') { *sign = 1; cptr++; } else if (*cptr == '-') { *sign = -1; cptr++; } return get_uint (cptr, 8, 0777777, 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) { int32 cflag, d, i, j, k, sign, damask, epcmask; t_stat r, sta = SCPE_OK; char gbuf[CBUFSIZE]; cflag = (uptr == NULL) || (uptr == &cpu_unit); while (isspace (*cptr)) cptr++; for (i = 1; (i < 5) && (cptr[i] != 0); i++) { if (cptr[i] == 0) { for (j = i + 1; j <= 5; j++) cptr[j] = 0; } } if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (t_value) cptr[0] | 0200; return SCPE_OK; } if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) cptr[0] & 077) << 12) | (((t_value) cptr[1] & 077) << 6) | ((t_value) cptr[2] & 077); return SCPE_OK; } #if defined (PDP15) if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = (((t_value) cptr[0] & 0177) << 11) | (((t_value) cptr[1] & 0177) << 4) | (((t_value) cptr[2] & 0170) >> 3); val[1] = (((t_value) cptr[2] & 0007) << 15) | (((t_value) cptr[3] & 0177) << 8) | (((t_value) cptr[4] & 0177) << 1); return -1; } #endif 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_XR: /* index */ break; case I_V_XR9: /* index literal */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ d = get_sint (gbuf, &sign, &r); if (r != SCPE_OK) return SCPE_ARG; if (((sign >= 0) && (d > 0377)) || ((sign < 0) && (d > 0400))) return SCPE_ARG; val[0] = val[0] | ((sign >= 0)? d: (01000 - d)); break; case I_V_LAW: /* law */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ d = get_uint (gbuf, 8, 017777, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; break; case I_V_MRF: /* mem ref */ #if defined (PDP15) if (memm) damask = B_DAMASK; else damask = P_DAMASK; cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ #else damask = B_DAMASK; cptr = get_glyph (cptr, gbuf, 0); /* get next field */ #endif #if defined (PDP4) || defined (PDP7) if (strcmp (gbuf, "I") == 0) { /* indirect? */ val[0] = val[0] | I_IND; cptr = get_glyph (cptr, gbuf, 0); } #endif epcmask = AMASK & ~damask; /* get ePC */ d = get_uint (gbuf, 8, AMASK, &r); /* get addr */ if (r != SCPE_OK) return SCPE_ARG; if (d <= damask) /* fit in 12/13b? */ val[0] = val[0] | d; else if (cflag && (((addr ^ d) & epcmask) == 0)) val[0] = val[0] | (d & damask); /* hi bits = ePC? */ else return SCPE_ARG; #if defined (PDP15) if (!memm) { cptr = get_glyph (cptr, gbuf, 0); if (gbuf[0] != 0) { if (strcmp (gbuf, "X") != 0) return SCPE_ARG; val[0] = val[0] | I_IDX; } } #endif break; case I_V_EMD: /* or'able */ val[0] = val[0] | ((opc_val[i] >> I_V_DC) & 077); /* default shift */ case I_V_EST: case I_V_ESH: case I_V_NPN: case I_V_NPI: case I_V_IOT: case I_V_OPR: 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 ^ val[0]) & 0740000) != 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; case I_V_FPM: /* FP15 mem ref */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ val[1] = get_uint (gbuf, 8, AMASK, &r); /* get addr */ if (r != SCPE_OK) return SCPE_ARG; sta = -1; break; case I_V_FPI: /* FP15 ind mem ref */ cptr = get_glyph (cptr, gbuf, 0); /* get next field */ val[1] = get_uint (gbuf, 8, AMASK, &r) | SIGN; /* get @addr */ if (r != SCPE_OK) return SCPE_ARG; sta = -1; break; case I_V_FPN: /* FP15 no operand */ val[1] = 0; sta = -1; break; } /* end case */ if (*cptr != 0) /* junk at end? */ return SCPE_ARG; return sta; } simh-3.8.1/PDP18B/pdp18b_diag.txt0000644000175000017500000000404010450303460014353 0ustar vlmvlm18b PDP Diagnostics 1. PDP-4 2. PDP-7 2.1 PDP-7 Instruction Test (Maindec 701) The diagnostic must be boot loaded, as it jumps dynamically out of the RIM load process into its own loader. At start, set SR<1:16> to a non-zero value. The diagnostic executes four HLT's as part of initial tests and then runs to completion. Normal HLT is at 2623 (PC = 2624). sim> att -e ptr digital-7-54-m-rim.bin sim> boot ptr HALT instruction, PC: 17670 (AND 17727) sim> d sr 4 ; any even value between 2 and 377776 sim> run 170 HALT instruction, PC: 00171 (CML CMA) sim> ex ac,l AC: 000000 L: 0 sim> c HALT instruction, PC: 00173 (SPA) sim> ex ac,l AC: 777777 L: 1 sim> c HALT instruction, PC: 00176 (SPA) sim> ex ac,l AC: 000000 L: 0 sim> c HALT instruction, PC: 00201 (LAC 4116) sim> ex ac,l AC: 000004 L: 0 sim> c HALT instruction, PC: 02624 (JMP 201) 3. PDP-9 4. PDP-15 Operating Instructions, PDP-15 diagnostics MAINDEC-15-D0A1-PH Instruction test 1 Read in: 200 Start: 200 Breakpoint: 7274 for one pass MAINDEC-15-D0A2-PH Instruction test 1A Read in: 200 Start: 200 Breakpoint: 4437 for one pass MAINDEC-15-D0AA-PB Index register test Read in: 17700 (ignored, binary tape) Start: 200 Halts: at 214, set BANKM = 0 Runs to: prints END at end of pass MAINDEC-15-D0BB-PH Instruction test 2 Read in: 200 Start: 200 SR: 1 to run clock test Breakpoint: 6403 for one pass MAINDEC-15-D0CA-PH Memory address test Read in: 7200 Start: 7200 Breakpoint: 7577 for one pass MAINDEC-15-D0EA-PH JMP-Y interrupt test Read in: 7400 Start: 7400 Breakpoint: 7551 for one pass MAINDEC-15-D0FA-PH JMS-Y interrupt test Read in: 7400 Start: 7400 Breakpoint: 7577 for one pass MAINDEC-15-D0KA-PH ISZ test Read in: 200 Start: 200 Breakpoint: 7704 for one pass MAINDEC-15-D1CD-PB Extended memory test Read in: 200 (ignored, binary tape) Start: 200 Halts after printout, set SR = 30000 Breakpoint: 563 for one pass simh-3.8.1/PDP18B/pdp18b_drm.c0000644000175000017500000002237111112110440013631 0ustar vlmvlm/* pdp18b_drm.c: drum/fixed head disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. drm (PDP-4,PDP-7) Type 24 serial drum 14-Jan-04 RMS Revised IO device call interface 26-Oct-03 RMS Cleaned up buffer copy code 05-Dec-02 RMS Updated from Type 24 documentation 22-Nov-02 RMS Added PDP-4 support 05-Feb-02 RMS Added DIB, device number support 03-Feb-02 RMS Fixed bug in reset routine (found by Robert Alan Byer) 06-Jan-02 RMS Revised enable/disable support 25-Nov-01 RMS Revised interrupt structure 10-Jun-01 RMS Cleaned up IOT decoding to reflect hardware 26-Apr-01 RMS Added device enable/disable support 14-Apr-99 RMS Changed t_addr to unsigned */ #include "pdp18b_defs.h" #include /* Constants */ #define DRM_NUMWDS 256 /* words/sector */ #define DRM_NUMSC 2 /* sectors/track */ #define DRM_NUMTR 256 /* tracks/drum */ #define DRM_NUMDK 1 /* drum/controller */ #define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */ #define DRM_SIZE (DRM_NUMDK * DRM_NUMTR * DRM_NUMWDT) /* words/drum */ #define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ #define DRM_READ 000 /* read */ #define DRM_WRITE 040 /* write */ #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DRM_NUMWDT))) extern int32 M[]; extern int32 int_hwre[API_HLVL+1]; extern UNIT cpu_unit; int32 drm_da = 0; /* track address */ int32 drm_ma = 0; /* memory address */ int32 drm_err = 0; /* error flag */ int32 drm_wlk = 0; /* write lock */ int32 drm_time = 10; /* inter-word time */ int32 drm_stopioe = 1; /* stop on error */ DEVICE drm_dev; int32 drm60 (int32 dev, int32 pulse, int32 AC); int32 drm61 (int32 dev, int32 pulse, int32 AC); int32 drm62 (int32 dev, int32 pulse, int32 AC); int32 drm_iors (void); t_stat drm_svc (UNIT *uptr); t_stat drm_reset (DEVICE *dptr); t_stat drm_boot (int32 unitno, DEVICE *dptr); /* DRM data structures drm_dev DRM device descriptor drm_unit DRM unit descriptor drm_reg DRM register list */ DIB drm_dib = { DEV_DRM, 3 ,&drm_iors, { &drm60, &drm61, &drm62 } }; UNIT drm_unit = { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, DRM_SIZE) }; REG drm_reg[] = { { ORDATA (DA, drm_da, 9) }, { ORDATA (MA, drm_ma, 16) }, { FLDATA (INT, int_hwre[API_DRM], INT_V_DRM) }, { FLDATA (DONE, int_hwre[API_DRM], INT_V_DRM) }, { FLDATA (ERR, drm_err, 0) }, { ORDATA (WLK, drm_wlk, 32) }, { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, drm_stopioe, 0) }, { ORDATA (DEVNO, drm_dib.dev, 6), REG_HRO }, { NULL } }; MTAB drm_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE drm_dev = { "DRM", &drm_unit, drm_reg, drm_mod, 1, 8, 20, 1, 8, 18, NULL, NULL, &drm_reset, &drm_boot, NULL, NULL, &drm_dib, DEV_DISABLE }; /* IOT routines */ int32 drm60 (int32 dev, int32 pulse, int32 AC) { if ((pulse & 027) == 06) { /* DRLR, DRLW */ drm_ma = AC & 0177777; /* load mem addr */ drm_unit.FUNC = pulse & DRM_WRITE; /* save function */ } return AC; } int32 drm61 (int32 dev, int32 pulse, int32 AC) { int32 t; if (pulse & 001) { /* DRSF */ if (TST_INT (DRM)) AC = AC | IOT_SKP; } if (pulse & 002) { /* DRCF */ CLR_INT (DRM); /* clear done */ drm_err = 0; /* clear error */ } if (pulse & 004) { /* DRSS */ drm_da = AC & DRM_SMASK; /* load sector # */ t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); if (t <= 0) /* wrap around? */ t = t + DRM_NUMWDT; sim_activate (&drm_unit, t * drm_time); /* schedule op */ } return AC; } int32 drm62 (int32 dev, int32 pulse, int32 AC) { int32 t; if (pulse & 001) { /* DRSN */ if (drm_err == 0) AC = AC | IOT_SKP; } if (pulse & 004) { /* DRCS */ CLR_INT (DRM); /* clear done */ drm_err = 0; /* clear error */ t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); if (t <= 0) /* wrap around? */ t = t + DRM_NUMWDT; sim_activate (&drm_unit, t * drm_time); /* schedule op */ } return AC; } /* Unit service This code assumes the entire drum is buffered. */ t_stat drm_svc (UNIT *uptr) { int32 i; uint32 da; int32 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ drm_err = 1; /* set error */ SET_INT (DRM); /* set done */ return IORETURN (drm_stopioe, SCPE_UNATT); } da = drm_da * DRM_NUMWDS; /* compute dev addr */ for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */ if (uptr->FUNC == DRM_READ) { /* read? */ if (MEM_ADDR_OK (drm_ma)) /* if !nxm */ M[drm_ma] = fbuf[da]; /* read word */ } else { /* write */ if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1; else { /* not locked */ fbuf[da] = M[drm_ma]; /* write word */ if (da >= uptr->hwmark) uptr->hwmark = da + 1; } } drm_ma = (drm_ma + 1) & 0177777; /* incr mem addr */ } drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */ SET_INT (DRM); /* set done */ return SCPE_OK; } /* Reset routine */ t_stat drm_reset (DEVICE *dptr) { drm_da = drm_ma = drm_err = 0; CLR_INT (DRM); /* clear done */ sim_cancel (&drm_unit); return SCPE_OK; } /* IORS routine */ int32 drm_iors (void) { return (TST_INT (DRM)? IOS_DRM: 0); } /* Bootstrap routine */ #define BOOT_START 02000 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) static const int32 boot_rom[] = { 0750000, /* CLA ; dev, mem addr */ 0706006, /* DRLR ; load ma */ 0706106, /* DRSS ; load da, start */ 0706101, /* DRSF ; wait for done */ 0602003, /* JMP .-1 */ 0600000 /* JMP 0 ; enter boot */ }; t_stat drm_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 PC; if (drm_dib.dev != DEV_DRM) /* non-std addr? */ return STOP_NONSTD; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; PC = BOOT_START; return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_defs.h0000644000175000017500000005446111111664106014016 0ustar vlmvlm/* pdp18b_defs.h: 18b PDP simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Oct-06 RMS Added infinite loop stop 14-Jan-04 RMS Revised IO device call interface 18-Oct-03 RMS Added DECtape off reel message 18-Jul-03 RMS Added FP15 support Added XVM support Added EAE option for PDP-4 25-Apr-03 RMS Revised for extended file support 04-Feb-03 RMS Added RB09, LP09 support 22-Nov-02 RMS Added PDP-4 drum support 05-Oct-02 RMS Added DIB structure 25-Jul-02 RMS Added PDP-4 DECtape support 10-Feb-02 RMS Added PDP-7 DECtape support 25-Nov-01 RMS Revised interrupt structure 27-May-01 RMS Added second Teletype support 21-Jan-01 RMS Added DECtape support 14-Apr-99 RMS Changed t_addr to unsigned 02-Jan-96 RMS Added fixed head and moving head disks 31-Dec-95 RMS Added memory management 19-Mar-95 RMS Added dynamic memory size The author gratefully acknowledges the help of Craig St. Clair and Deb Tevonian in locating archival material about the 18b PDP's, and of Al Kossow and Max Burnet in making documentation and software available. */ #ifndef _PDP18B_DEFS_H_ #define _PDP18B_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Models: only one should be defined model memory CPU options I/O options PDP4 8K Type 18 EAE Type 65 KSR-28 Teletype (Baudot) ??Type 16 mem extension integral paper tape reader Type 75 paper tape punch integral real time clock Type 62 line printer (Hollerith) Type 550/555 DECtape Type 24 serial drum PDP7 32K Type 177 EAE Type 649 KSR-33 Teletype Type 148 mem extension Type 444 paper tape reader Type 75 paper tape punch integral real time clock Type 647B line printer (sixbit) Type 550/555 DECtape Type 24 serial drum PDP9 32K KE09A EAE KSR-33 Teletype KF09A auto pri intr PC09A paper tape reader and punch KG09B mem extension integral real time clock KP09A power detection Type 647D/E line printer (sixbit) KX09A mem protection LP09 line printer (ASCII) RF09/RS09 fixed head disk RB09 fixed head disk TC59 magnetic tape TC02/TU55 DECtape LT09A additional Teletypes PDP15 128K KE15 EAE KSR-35 Teletype KA15 auto pri intr PC15 paper tape reader and punch KF15 power detection KW15 real time clock KM15 mem protection LP09 line printer KT15 mem relocation LP15 line printer FP15 floating point RP15 disk pack XVM option RF15/RF09 fixed head disk TC59D magnetic tape TC15/TU56 DECtape LT15/LT19 additional Teletypes ??Indicates not implemented. The PDP-4 manual refers to a memory ??extension control; there is no documentation on it. */ #if !defined (PDP4) && !defined (PDP7) && !defined (PDP9) && !defined (PDP15) #define PDP15 0 /* default to PDP-15 */ #endif /* Simulator stop codes */ #define STOP_RSRV 1 /* must be 1 */ #define STOP_HALT 2 /* HALT */ #define STOP_IBKPT 3 /* breakpoint */ #define STOP_XCT 4 /* nested XCT's */ #define STOP_API 5 /* invalid API int */ #define STOP_NONSTD 6 /* non-std dev num */ #define STOP_MME 7 /* mem mgt error */ #define STOP_FPDIS 8 /* fp inst, fpp disabled */ #define STOP_DTOFF 9 /* DECtape off reel */ #define STOP_LOOP 10 /* infinite loop */ /* Peripheral configuration */ #if defined (PDP4) #define ADDRSIZE 13 #define KSR28 0 /* Baudot terminal */ #define TYPE62 0 /* Hollerith printer */ #define TYPE550 0 /* DECtape */ #define DRM 0 /* drum */ #elif defined (PDP7) #define ADDRSIZE 15 #define TYPE647 0 /* sixbit printer */ #define TYPE550 0 /* DECtape */ #define DRM 0 /* drum */ #elif defined (PDP9) #define ADDRSIZE 15 #define TYPE647 0 /* sixbit printer */ #define LP09 0 /* ASCII printer */ #define RB 0 /* fixed head disk */ #define RF 0 /* fixed head disk */ #define MTA 0 /* magtape */ #define TC02 0 /* DECtape */ #define TTY1 4 /* second Teletype(s) */ #define BRMASK 0076000 /* bounds mask */ #elif defined (PDP15) #define ADDRSIZE 17 #define LP09 0 /* ASCII printer */ #define LP15 0 /* DMA printer */ #define RF 0 /* fixed head disk */ #define RP 0 /* disk pack */ #define MTA 0 /* magtape */ #define TC02 0 /* DECtape */ #define TTY1 16 /* second Teletype(s) */ #define BRMASK 0377400 /* bounds mask */ #define BRMASK_XVM 0777400 /* bounds mask, XVM */ #endif /* Memory */ #define AMASK ((1 << ADDRSIZE) - 1) /* address mask */ #define IAMASK 077777 /* ind address mask */ #define BLKMASK (AMASK & (~IAMASK)) /* block mask */ #define MAXMEMSIZE (1 << ADDRSIZE) /* max memory size */ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) /* Instructions */ #define I_V_OP 14 /* opcode */ #define I_M_OP 017 #define I_V_IND 13 /* indirect */ #define I_V_IDX 12 /* index */ #define I_IND (1 << I_V_IND) #define I_IDX (1 << I_V_IDX) #define B_DAMASK 017777 /* bank mode address */ #define B_EPCMASK (AMASK & ~B_DAMASK) #define P_DAMASK 007777 /* page mode address */ #define P_EPCMASK (AMASK & ~P_DAMASK) /* Memory cycles */ #define FE 0 #define DF 1 #define RD 2 #define WR 3 /* Memory status codes */ #define MM_OK 0 #define MM_ERR 1 /* Memory management relocation checks (PDP-15 KT15 and XVM only) */ #define REL_C -1 /* console */ #define REL_R 0 /* read */ #define REL_W 1 /* write */ /* Architectural constants */ #define DMASK 0777777 /* data mask */ #define LINK (DMASK + 1) /* link */ #define LACMASK (LINK | DMASK) /* link + data */ #define SIGN 0400000 /* sign bit */ #define OP_JMS 0100000 /* JMS */ #define OP_JMP 0600000 /* JMP */ #define OP_HLT 0740040 /* HLT */ /* IOT subroutine return codes */ #define IOT_V_SKP 18 /* skip */ #define IOT_V_REASON 19 /* reason */ #define IOT_SKP (1 << IOT_V_SKP) #define IOT_REASON (1 << IOT_V_REASON) #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* PC change queue */ #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 /* XVM memory management registers */ #define MM_RDIS 0400000 /* reloc disabled */ #define MM_V_GM 15 /* G mode */ #define MM_M_GM 03 #define MM_GM (MM_M_GM << MM_V_GM) #define MM_G_W0 0077777 /* virt addr width */ #define MM_G_W1 0177777 #define MM_G_W2 0777777 #define MM_G_W3 0377777 #define MM_G_B0 0060000 /* SAS base */ #define MM_G_B1 0160000 #define MM_G_B2 0760000 #define MM_G_B3 0360000 #define MM_UIOT 0040000 /* user mode IOT's */ #define MM_WP 0020000 /* share write prot */ #define MM_SH 0010000 /* share enabled */ #define MM_V_SLR 10 /* segment length reg */ #define MM_M_SLR 03 #define MM_SLR_L0 001000 /* SAS length */ #define MM_SLR_L1 002000 #define MM_SLR_L2 010000 #define MM_SLR_L3 020000 #define MM_SBR_MASK 01777 /* share base reg */ #define MM_GETGM(x) (((x) >> MM_V_GM) & MM_M_GM) #define MM_GETSLR(x) (((x) >> MM_V_SLR) & MM_M_SLR) /* Device information block */ #define DEV_MAXBLK 8 /* max dev block */ #define DEV_MAX 64 /* total devices */ typedef struct { uint32 dev; /* base dev number */ uint32 num; /* number of slots */ int32 (*iors)(void); /* IORS responder */ int32 (*dsp[DEV_MAXBLK])(int32 dev, int32 pulse, int32 dat); } DIB; /* Standard device numbers */ #define DEV_PTR 001 /* paper tape reader */ #define DEV_PTP 002 /* paper tape punch */ #define DEV_TTI 003 /* console input */ #define DEV_TTO 004 /* console output */ #define DEV_TTI1 041 /* extra terminals */ #define DEV_TTO1 040 #define DEV_DRM 060 /* drum */ #define DEV_RP 063 /* RP15 */ #define DEV_LPT 065 /* line printer */ #define DEV_RF 070 /* RF09 */ #define DEV_RB 071 /* RB09 */ #define DEV_MT 073 /* magtape */ #define DEV_DTA 075 /* dectape */ /* Interrupt system The interrupt system can be modelled on either the flag driven system of the PDP-4 and PDP-7 or the API driven system of the PDP-9 and PDP-15. If flag based, API is hard to implement; if API based, IORS requires extra code for implementation. I've chosen an API based model. API channel Device API priority Notes 00 software 4 4 01 software 5 5 02 software 6 6 03 software 7 7 04 TC02/TC15 1 05 TC59D 1 06 drum 1 PDP-9 only 07 RB09 1 PDP-9 only 10 paper tape reader 2 11 real time clock 3 12 power fail 0 13 memory parity 0 14 display 2 15 card reader 2 16 line printer 2 17 A/D converter 0 20 interprocessor buffer 3 21 360 link 3 PDP-9 only 22 data phone 2 PDP-15 only 23 RF09/RF15 1 24 RP15 1 PDP-15 only 25 plotter 1 PDP-15 only 26 - 27 - 30 - 31 - 32 - 33 - 34 LT15 TTO 3 PDP-15 only 35 LT15 TTI 3 PDP-15 only 36 - 37 - On the PDP-9, any API level active masks PI, and PI does not mask API. On the PDP-15, only the hardware API levels active mask PI, and PI masks the API software levels. */ #define API_ML0 0200 /* API masks: level 0 */ #define API_ML1 0100 #define API_ML2 0040 #define API_ML3 0020 #define API_ML4 0010 #define API_ML5 0004 #define API_ML6 0002 #define API_ML7 0001 /* level 7 */ #if defined (PDP9) /* levels which mask PI */ #define API_MASKPI (API_ML0|API_ML1|API_ML2|API_ML3|API_ML4|API_ML5|API_ML6|API_ML7) #else #define API_MASKPI (API_ML0|API_ML1|API_ML2|API_ML3) #endif #define API_HLVL 4 /* hwre levels */ #define ACH_SWRE 040 /* swre int vec */ /* API level 0 */ #define INT_V_PWRFL 0 /* powerfail */ #define INT_PWRFL (1 << INT_V_PWRFL) #define API_PWRFL 0 #define ACH_PWRFL 052 /* API level 1 */ #define INT_V_DTA 0 /* DECtape */ #define INT_V_MTA 1 /* magtape */ #define INT_V_DRM 2 /* drum */ #define INT_V_RF 3 /* fixed head disk */ #define INT_V_RP 4 /* disk pack */ #define INT_V_RB 5 /* RB disk */ #define INT_DTA (1 << INT_V_DTA) #define INT_MTA (1 << INT_V_MTA) #define INT_DRM (1 << INT_V_DRM) #define INT_RF (1 << INT_V_RF) #define INT_RP (1 << INT_V_RP) #define INT_RB (1 << INT_V_RB) #define API_DTA 1 #define API_MTA 1 #define API_DRM 1 #define API_RF 1 #define API_RP 1 #define API_RB 1 #define ACH_DTA 044 #define ACH_MTA 045 #define ACH_DRM 046 #define ACH_RB 047 #define ACH_RF 063 #define ACH_RP 064 /* API level 2 */ #define INT_V_PTR 0 /* paper tape reader */ #define INT_V_LPT 1 /* line printer */ #define INT_V_LPTSPC 2 /* line printer spc */ #define INT_PTR (1 << INT_V_PTR) #define INT_LPT (1 << INT_V_LPT) #define INT_LPTSPC (1 << INT_V_LPTSPC) #define API_PTR 2 #define API_LPT 2 #define API_LPTSPC 2 #define ACH_PTR 050 #define ACH_LPT 056 /* API level 3 */ #define INT_V_CLK 0 /* clock */ #define INT_V_TTI1 1 /* LT15 keyboard */ #define INT_V_TTO1 2 /* LT15 output */ #define INT_CLK (1 << INT_V_CLK) #define INT_TTI1 (1 << INT_V_TTI1) #define INT_TTO1 (1 << INT_V_TTO1) #define API_CLK 3 #define API_TTI1 3 #define API_TTO1 3 #define ACH_CLK 051 #define ACH_TTI1 075 #define ACH_TTO1 074 /* PI level */ #define INT_V_TTI 0 /* console keyboard */ #define INT_V_TTO 1 /* console output */ #define INT_V_PTP 2 /* paper tape punch */ #define INT_TTI (1 << INT_V_TTI) #define INT_TTO (1 << INT_V_TTO) #define INT_PTP (1 << INT_V_PTP) #define API_TTI 4 /* PI level */ #define API_TTO 4 #define API_PTP 4 /* Interrupt macros */ #define SET_INT(dv) int_hwre[API_##dv] = int_hwre[API_##dv] | INT_##dv #define CLR_INT(dv) int_hwre[API_##dv] = int_hwre[API_##dv] & ~INT_##dv #define TST_INT(dv) (int_hwre[API_##dv] & INT_##dv) /* I/O status flags for the IORS instruction bit PDP-4 PDP-7 PDP-9 PDP-15 0 intr on intr on intr on intr on 1 tape rdr flag* tape rdr flag* tape rdr flag* tape rdr flag* 2 tape pun flag* tape pun flag* tape pun flag* tape pun flag* 3 keyboard flag* keyboard flag* keyboard flag* keyboard flag* 4 type out flag* type out flag* type out flag* type out flag* 5 display flag* display flag* light pen flag* light pen flag* 6 clk ovflo flag* clk ovflo flag* clk ovflo flag* clk ovflo flag* 7 clk enable flag clk enable flag clk enable flag clk enable flag 8 mag tape flag* mag tape flag* tape rdr empty* tape rdr empty* 9 card rdr col* * tape pun empty tape pun empty 10 card rdr ~busy DECtape flag* DECtape flag* 11 card rdr error magtape flag* magtape flag* 12 card rdr EOF disk pack flag* 13 card pun row* DECdisk flag* DECdisk flag* 14 card pun error lpt flag* 15 lpt flag* lpt flag* lpt flag* 16 lpt space flag* lpt error flag lpt error flag 17 drum flag* drum flag* */ #define IOS_ION 0400000 /* interrupts on */ #define IOS_PTR 0200000 /* tape reader */ #define IOS_PTP 0100000 /* tape punch */ #define IOS_TTI 0040000 /* keyboard */ #define IOS_TTO 0020000 /* terminal */ #define IOS_LPEN 0010000 /* light pen */ #define IOS_CLK 0004000 /* clock */ #define IOS_CLKON 0002000 /* clock enable */ #define IOS_DTA 0000200 /* DECtape */ #define IOS_RP 0000040 /* disk pack */ #define IOS_RF 0000020 /* fixed head disk */ #define IOS_DRM 0000001 /* drum */ #if defined (PDP4) || defined (PDP7) #define IOS_MTA 0001000 /* magtape */ #define IOS_LPT 0000004 /* line printer */ #define IOS_LPT1 0000002 /* line printer stat */ #elif defined (PDP9) #define IOS_PTRERR 0001000 /* reader empty */ #define IOS_PTPERR 0000400 /* punch empty */ #define IOS_MTA 0000100 /* magtape */ #define IOS_LPT 0000004 /* line printer */ #define IOS_LPT1 0000002 /* line printer stat */ #elif defined (PDP15) #define IOS_PTRERR 0001000 /* reader empty */ #define IOS_PTPERR 0000400 /* punch empty */ #define IOS_MTA 0000100 /* magtape */ #define IOS_LPT 0000010 /* line printer */ #define IOS_LPT1 0000000 /* not used */ #endif /* Function prototypes */ t_stat set_devno (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_devno (FILE *st, UNIT *uptr, int32 val, void *desc); #endif simh-3.8.1/PDP18B/pdp18b_stddev.c0000644000175000017500000012406411112110440014342 0ustar vlmvlm/* pdp18b_stddev.c: 18b PDP's standard devices Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr paper tape reader ptp paper tape punch tti keyboard tto teleprinter clk clock 18-Jun-07 RMS Added UNIT_IDLE to console input, clock 18-Oct-06 RMS Added PDP-15 programmable duplex control Fixed handling of non-printable characters in KSR mode Changed clock to be free-running Fixed out-of-tape behavior for PDP-9 vs PDP-15 Synced keyboard to clock 30-Jun-06 RMS Fixed KSR-28 shift tracking 20-Jun-06 RMS Added KSR ASCII reader support 13-Jun-06 RMS Fixed Baudot letters/figures inversion for PDP-4 Fixed PDP-4/PDP-7 default terminal to be local echo 22-Nov-05 RMS Revised for new terminal processing routines 28-May-04 RMS Removed SET TTI CTRL-C 16-Feb-04 RMS Fixed bug in hardware read-in mode bootstrap 14-Jan-04 RMS Revised IO device call interface CAF does not turn off the clock 29-Dec-03 RMS Added console backpressure support 26-Jul-03 RMS Increased PTP, TTO timeouts for PDP-15 operating systems Added hardware read-in mode support for PDP-7/9/15 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Clean up flags on detach 01-Mar-03 RMS Added SET/SHOW CLK freq, SET TTI CTRL-C 22-Dec-02 RMS Added break support 01-Nov-02 RMS Added 7B/8B support to terminal 05-Oct-02 RMS Added DIBs, device number support, IORS call 14-Jul-02 RMS Added ASCII reader/punch support (from Hans Pufal) 30-May-02 RMS Widened POS to 32b 29-Nov-01 RMS Added read only unit support 25-Nov-01 RMS Revised interrupt structure 17-Sep-01 RMS Removed multiconsole support 07-Sep-01 RMS Added terminal multiplexor support 17-Jul-01 RMS Moved function prototype 10-Jun-01 RMS Cleaned up IOT decoding to reflect hardware 27-May-01 RMS Added multiconsole support 10-Mar-01 RMS Added funny format loader support 05-Mar-01 RMS Added clock calibration support 22-Dec-00 RMS Added PDP-9/15 half duplex support 30-Nov-00 RMS Fixed PDP-4/7 bootstrap loader for 4K systems 30-Oct-00 RMS Standardized register naming 06-Jan-97 RMS Fixed PDP-4 console input 16-Dec-96 RMS Fixed bug in binary ptr service */ #include "pdp18b_defs.h" #include #define UNIT_V_RASCII (UNIT_V_UF + 0) /* reader ASCII */ #define UNIT_RASCII (1 << UNIT_V_RASCII) #define UNIT_V_KASCII (UNIT_V_UF + 1) /* KSR ASCII */ #define UNIT_KASCII (1 << UNIT_V_KASCII) #define UNIT_V_PASCII (UNIT_V_UF + 0) /* punch ASCII */ #define UNIT_PASCII (1 << UNIT_V_PASCII) extern int32 M[]; extern int32 int_hwre[API_HLVL+1], PC, ASW; extern int32 sim_switches; extern int32 sim_is_running; extern UNIT cpu_unit; int32 clk_state = 0; int32 ptr_err = 0, ptr_stopioe = 0, ptr_state = 0; int32 ptp_err = 0, ptp_stopioe = 0; int32 tti_2nd = 0; /* 2nd char waiting */ int32 tty_shift = 0; /* KSR28 shift state */ int32 tti_fdpx = 0; /* prog mode full duplex */ int32 clk_tps = 60; /* ticks/second */ int32 tmxr_poll = 16000; /* term mux poll */ uint32 clk_task_last = 0; uint32 clk_task_timer = 0; const int32 asc_to_baud[128] = { 000,000,000,000,000,000,000,064, /* bell */ 000,000,0110,000,000,0102,000,000, /* lf, cr */ 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 0104,066,061,045,062,000,053,072, /* space - ' */ 076,051,000,000,046,070,047,067, /* ( - / */ 055,075,071,060,052,041,065,074, /* 0 - 7 */ 054,043,056,057,000,000,000,063, /* 8 - ? */ 000,030,023,016,022,020,026,013, /* @ - G */ 005,014,032,036,011,007,006,003, /* H - O */ 015,035,012,024,001,034,017,031, /* P - W */ 027,025,021,000,000,000,000,000, /* X - _ */ 000,030,023,016,022,020,026,013, /* ` - g */ 005,014,032,036,011,007,006,003, /* h - o */ 015,035,012,024,001,034,017,031, /* p - w */ 027,025,021,000,000,000,000,000 /* x - DEL */ }; const char baud_to_asc[64] = { 0 ,'T',015,'O',' ','H','N','M', 012,'L','R','G','I','P','C','V', 'E','Z','D','B','S','Y','F','X', 'A','W','J', 0 ,'U','Q','K', 0, 0 ,'5','\r','9',' ','#',',','.', 012,')','4','&','8','0',':',';', '3','"','$','?','\a','6','!','/', '-','2','\'',0 ,'7','1','(', 0 }; int32 ptr (int32 dev, int32 pulse, int32 dat); int32 ptp (int32 dev, int32 pulse, int32 dat); int32 tti (int32 dev, int32 pulse, int32 dat); int32 tto (int32 dev, int32 pulse, int32 dat); int32 clk_iors (void); int32 ptr_iors (void); int32 ptp_iors (void); int32 tti_iors (void); int32 tto_iors (void); t_stat clk_svc (UNIT *uptr); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat ptr_attach (UNIT *uptr, char *cptr); t_stat ptp_attach (UNIT *uptr, char *cptr); t_stat ptr_detach (UNIT *uptr); t_stat ptp_detach (UNIT *uptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); int32 clk_task_upd (t_bool clr); extern int32 upd_iors (void); /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit clk_reg CLK register list */ DIB clk_dib = { 0, 0, &clk_iors, { NULL } }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 }; REG clk_reg[] = { { FLDATA (INT, int_hwre[API_CLK], INT_V_CLK) }, { FLDATA (DONE, int_hwre[API_CLK], INT_V_CLK) }, { FLDATA (ENABLE, clk_state, 0) }, #if defined (PDP15) { ORDATA (TASKTIMER, clk_task_timer, 18) }, { DRDATA (TASKLAST, clk_task_last, 32), REG_HRO }, #endif { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &clk_show_freq, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, 0 }; /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit ptr_reg PTR register list */ DIB ptr_dib = { DEV_PTR, 1, &ptr_iors, { &ptr } }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 18) }, { FLDATA (INT, int_hwre[API_PTR], INT_V_PTR) }, { FLDATA (DONE, int_hwre[API_PTR], INT_V_PTR) }, #if defined (IOS_PTRERR) { FLDATA (ERR, ptr_err, 0) }, #endif { ORDATA (STATE, ptr_state, 5), REG_HRO }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB ptr_mod[] = { { UNIT_RASCII, UNIT_RASCII, "even parity ASCII", NULL }, { UNIT_KASCII, UNIT_KASCII, "forced parity ASCII", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, &ptr_attach, &ptr_detach, &ptr_dib, 0 }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit ptp_reg PTP register list */ DIB ptp_dib = { DEV_PTP, 1, &ptp_iors, { &ptp } }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (INT, int_hwre[API_PTP], INT_V_PTP) }, { FLDATA (DONE, int_hwre[API_PTP], INT_V_PTP) }, #if defined (IOS_PTPERR) { FLDATA (ERR, ptp_err, 0) }, #endif { 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[] = { { UNIT_PASCII, UNIT_PASCII, "7b ASCII", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, &ptp_attach, &ptp_detach, &ptp_dib, 0 }; /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit tti_reg TTI register list */ #if defined (KSR28) #define TTI_WIDTH 5 #define TTI_FIGURES (1 << TTI_WIDTH) #define TTI_BOTH (1 << (TTI_WIDTH + 1)) #define BAUDOT_LETTERS 037 #define BAUDOT_FIGURES 033 #else #define TTI_WIDTH 8 #endif #define TTI_MASK ((1 << TTI_WIDTH) - 1) #define TTUF_V_HDX (TTUF_V_UF + 0) /* half duplex */ #define TTUF_HDX (1 << TTUF_V_HDX) DIB tti_dib = { DEV_TTI, 1, &tti_iors, { &tti } }; UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE+TT_MODE_KSR+TTUF_HDX, 0), 0 }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, TTI_WIDTH) }, #if defined (KSR28) { ORDATA (BUF2ND, tti_2nd, TTI_WIDTH), REG_HRO }, #endif { FLDATA (INT, int_hwre[API_TTI], INT_V_TTI) }, { FLDATA (DONE, int_hwre[API_TTI], INT_V_TTI) }, #if defined (PDP15) { FLDATA (FDPX, tti_fdpx, 0) }, #endif { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tti_mod[] = { #if !defined (KSR28) { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, #endif { TTUF_HDX, 0 , "full duplex", "FDX", NULL }, { TTUF_HDX, TTUF_HDX, "half duplex", "HDX", NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit tto_reg TTO register list */ #if defined (KSR28) #define TTO_WIDTH 5 #define TTO_FIGURES (1 << TTO_WIDTH) #else #define TTO_WIDTH 8 #endif #define TTO_MASK ((1 << TTO_WIDTH) - 1) DIB tto_dib = { DEV_TTO, 1, &tto_iors, { &tto } }; UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), 1000 }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, TTO_WIDTH) }, #if defined (KSR28) { FLDATA (SHIFT, tty_shift, 0), REG_HRO }, #endif { FLDATA (INT, int_hwre[API_TTO], INT_V_TTO) }, { FLDATA (DONE, int_hwre[API_TTO], INT_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { #if !defined (KSR28) { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, #endif { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* Clock: IOT routine */ int32 clk (int32 dev, int32 pulse, int32 dat) { if (pulse & 001) { /* CLSF */ if (TST_INT (CLK)) dat = dat | IOT_SKP; } if (pulse & 004) { /* CLON/CLOF */ CLR_INT (CLK); /* clear flag */ if (pulse & 040) /* CLON */ clk_state = 1; else clk_state = 0; /* CLOF */ } return dat; } /* Unit service */ t_stat clk_svc (UNIT *uptr) { int32 t; t = sim_rtc_calb (clk_tps); /* calibrate clock */ tmxr_poll = t; /* set mux poll */ sim_activate (&clk_unit, t); /* reactivate unit */ #if defined (PDP15) clk_task_upd (FALSE); /* update task timer */ #endif if (clk_state) { /* clock on? */ M[7] = (M[7] + 1) & DMASK; /* incr counter */ if (M[7] == 0) /* ovrflo? set flag */ SET_INT (CLK); } return SCPE_OK; } #if defined (PDP15) /* Task timer update (PDP-15 XVM only) The task timer increments monotonically at 100Khz. Since this can't be simulated accurately, updates are done by interpolation since the last reading. The timer is also updated at clock events to keep the cycle counters from wrapping around more than once between updates. */ int32 clk_task_upd (t_bool clr) { uint32 delta, val, iusec10; uint32 cur = sim_grtime (); uint32 old = clk_task_timer; double usec10; if (cur > clk_task_last) delta = cur - clk_task_last; else delta = clk_task_last - cur; usec10 = ((((double) delta) * 100000.0) / (((double) tmxr_poll) * ((double) clk_tps))); iusec10 = (int32) usec10; val = (clk_task_timer + iusec10) & DMASK; if (clr) clk_task_timer = 0; else clk_task_timer = val; clk_task_last = cur; return ((int32) val); } #endif /* IORS service */ int32 clk_iors (void) { return (TST_INT (CLK)? IOS_CLK: 0); } /* Reset routine */ t_stat clk_reset (DEVICE *dptr) { int32 t; CLR_INT (CLK); /* clear flag */ if (!sim_is_running) { /* RESET (not CAF)? */ t = sim_rtc_init (clk_unit.wait); /* init calibration */ tmxr_poll = t; /* set mux poll */ sim_activate_abs (&clk_unit, t); /* activate unit */ clk_state = 0; /* clock off */ clk_task_timer = 0; clk_task_last = 0; } return SCPE_OK; } /* Set frequency */ t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; clk_tps = val; return SCPE_OK; } /* Show frequency */ t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); return SCPE_OK; } /* Paper tape reader out-of-tape handling The PDP-4 and PDP-7 readers behaved like most early DEC readers; when they ran out of tape, they hung. It was up to the program to sense this condition by running a timer. The PDP-9 reader controller synthesized the out of tape condition by noticing whether there was a transition on the feed hole within a window. The out-of-tape flag was treated like the reader flag in most cases. The PDP-15 reader controller received the out-of-tape flag as a static condition from the reader itself and simply reported it via IORS. */ /* Paper tape reader: IOT routine */ int32 ptr (int32 dev, int32 pulse, int32 dat) { if (pulse & 001) { /* RSF */ if (TST_INT (PTR)) dat = dat | IOT_SKP; } if (pulse & 002) { /* RRB, RCF */ CLR_INT (PTR); /* clear flag */ dat = dat | ptr_unit.buf; /* return buffer */ } if (pulse & 004) { /* RSA, RSB */ ptr_state = (pulse & 040)? 18: 0; /* set mode */ CLR_INT (PTR); /* clear flag */ #if !defined (PDP15) /* except on PDP15 */ ptr_err = 0; /* clear error */ #endif ptr_unit.buf = 0; /* clear buffer */ sim_activate (&ptr_unit, ptr_unit.wait); } return dat; } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ #if defined (IOS_PTRERR) SET_INT (PTR); /* if err, set flag */ ptr_err = 1; /* set error */ #endif return IORETURN (ptr_stopioe, SCPE_UNATT); } if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ #if defined (IOS_PTRERR) SET_INT (PTR); /* if err, set flag */ ptr_err = 1; /* set error */ #endif if (feof (ptr_unit.fileref)) { if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } if (ptr_state == 0) { /* ASCII */ if (ptr_unit.flags & UNIT_RASCII) { /* want parity? */ ptr_unit.buf = temp = temp & 0177; /* parity off */ while (temp = temp & (temp - 1)) ptr_unit.buf = ptr_unit.buf ^ 0200; /* count bits */ ptr_unit.buf = ptr_unit.buf ^ 0200; /* set even parity */ } else if (ptr_unit.flags & UNIT_KASCII) /* KSR ASCII? */ ptr_unit.buf = (temp | 0200) & 0377; /* forced parity */ else ptr_unit.buf = temp & 0377; } else if (temp & 0200) { /* binary */ ptr_state = ptr_state - 6; ptr_unit.buf = ptr_unit.buf | ((temp & 077) << ptr_state); } if (ptr_state == 0) /* if done, set flag */ SET_INT (PTR); else sim_activate (&ptr_unit, ptr_unit.wait); /* else restart */ ptr_unit.pos = ptr_unit.pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { ptr_state = 0; /* clear state */ ptr_unit.buf = 0; CLR_INT (PTR); /* clear flag */ #if defined (PDP15) /* PDP15, static err */ if (((ptr_unit.flags & UNIT_ATT) == 0) || feof (ptr_unit.fileref)) ptr_err = 1; else #endif ptr_err = 0; /* all other, clr err */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* IORS service */ int32 ptr_iors (void) { return ((TST_INT (PTR)? IOS_PTR: 0) #if defined (IOS_PTRERR) | (ptr_err? IOS_PTRERR: 0) #endif ); } /* Attach routine */ t_stat ptr_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); if (reason != SCPE_OK) return reason; ptr_err = 0; /* attach clrs error */ ptr_unit.flags = ptr_unit.flags & ~(UNIT_RASCII|UNIT_KASCII); if (sim_switches & SWMASK ('A')) ptr_unit.flags = ptr_unit.flags | UNIT_RASCII; if (sim_switches & SWMASK ('K')) ptr_unit.flags = ptr_unit.flags | UNIT_KASCII; return SCPE_OK; } /* Detach routine */ t_stat ptr_detach (UNIT *uptr) { #if defined (PDP15) ptr_err = 1; #endif ptr_unit.flags = ptr_unit.flags & ~UNIT_RASCII; return detach_unit (uptr); } /* Hardware RIM loader routines, PDP-7/9/15 */ int32 ptr_getw (UNIT *uptr, int32 *hi) { int32 word, bits, st, ch; word = st = bits = 0; do { if ((ch = getc (uptr->fileref)) == EOF) return -1; uptr->pos = uptr->pos + 1; if (ch & 0200) { word = (word << 6) | (ch & 077); bits = (bits << 1) | ((ch >> 6) & 1); st++; } } while (st < 3); if (hi != NULL) *hi = bits; return word; } t_stat ptr_rim_load (UNIT *uptr, int32 origin) { int32 bits, val; for (;;) { /* word loop */ if ((val = ptr_getw (uptr, &bits)) < 0) return SCPE_FMT; if (bits & 1) { /* end of tape? */ if ((val & 0760000) == OP_JMP) { PC = ((origin - 1) & 060000) | (val & 017777); return SCPE_OK; } else if (val == OP_HLT) return STOP_HALT; break; } else if (MEM_ADDR_OK (origin)) M[origin++] = val; } return SCPE_FMT; } #if defined (PDP4) || defined (PDP7) /* Bootstrap routine, PDP-4 and PDP-7 In a 4K system, the boostrap resides at 7762-7776. In an 8K or greater system, the bootstrap resides at 17762-17776. Because the program is so small, simple masking can be used to remove addr<5> for a 4K system. */ #define BOOT_START 017577 #define BOOT_FPC 017577 /* funny format loader */ #define BOOT_RPC 017770 /* RIM loader */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) static const int32 boot_rom[] = { 0700144, /* rsb */ 0117762, /* ff, jsb r1b */ 0057666, /* dac done 1 */ 0117762, /* jms r1b */ 0057667, /* dac done 2 */ 0117762, /* jms r1b */ 0040007, /* dac conend */ 0057731, /* dac conbeg */ 0440007, /* isz conend */ 0117762, /* blk, jms r1b */ 0057673, /* dac cai */ 0741100, /* spa */ 0617665, /* jmp done */ 0117762, /* jms r1b */ 0057777, /* dac tem1 */ 0317673, /* add cai */ 0057775, /* dac cks */ 0117713, /* jms r1a */ 0140010, /* dzm word */ 0457777, /* cont, isz tem1 */ 0617632, /* jmp cont1 */ 0217775, /* lac cks */ 0740001, /* cma */ 0740200, /* sza */ 0740040, /* hlt */ 0700144, /* rsb */ 0617610, /* jmp blk */ 0117713, /* cont1, jms r1a */ 0057762, /* dac tem2 */ 0117713, /* jms r1a */ 0742010, /* rtl */ 0742010, /* rtl */ 0742010, /* rtl */ 0742010, /* rtl */ 0317762, /* add tem2 */ 0057762, /* dac tem2 */ 0117713, /* jms r1a */ 0742020, /* rtr */ 0317726, /* add cdsp */ 0057713, /* dac r1a */ 0517701, /* and ccma */ 0740020, /* rar */ 0317762, /* add tem2 */ 0437713, /* xct i r1a */ 0617622, /* jmp cont */ 0617672, /* dsptch, jmp code0 */ 0617670, /* jmp code1 */ 0617700, /* jmp code2 */ 0617706, /* jmp code3 */ 0417711, /* xct code4 */ 0617732, /* jmp const */ 0740000, /* nop */ 0740000, /* nop */ 0740000, /* nop */ 0200007, /* done, lac conend */ 0740040, /* xx */ 0740040, /* xx */ 0517727, /* code1, and imsk */ 0337762, /* add i tem2 */ 0300010, /* code0, add word */ 0740040, /* cai, xx */ 0750001, /* clc */ 0357673, /* tad cai */ 0057673, /* dac cai */ 0617621, /* jmp cont-1 */ 0711101, /* code2, spa cla */ 0740001, /* ccma, cma */ 0277762, /* xor i tem2 */ 0300010, /* add word */ 0040010, /* code2a, dac word */ 0617622, /* jmp cont */ 0057711, /* code3, dac code4 */ 0217673, /* lac cai */ 0357701, /* tad ccma */ 0740040, /* code4, xx */ 0617622, /* jmp cont */ 0000000, /* r1a, 0 */ 0700101, /* rsf */ 0617714, /* jmp .-1 */ 0700112, /* rrb */ 0700104, /* rsa */ 0057730, /* dac tem */ 0317775, /* add cks */ 0057775, /* dac cks */ 0217730, /* lac tem */ 0744000, /* cll */ 0637713, /* jmp i r1a */ 0017654, /* cdsp, dsptch */ 0760000, /* imsk, 760000 */ 0000000, /* tem, 0 */ 0000000, /* conbeg, 0 */ 0300010, /* const, add word */ 0060007, /* dac i conend */ 0217731, /* lac conbeg */ 0040010, /* dac index */ 0220007, /* lac i conend */ 0560010, /* con1, sad i index */ 0617752, /* jmp find */ 0560010, /* sad i index */ 0617752, /* jmp find */ 0560010, /* sad i index */ 0617752, /* jmp find */ 0560010, /* sad i index */ 0617752, /* jmp find */ 0560010, /* sad i index */ 0617752, /* jmp find */ 0617737, /* jmp con1 */ 0200010, /* find, lac index */ 0540007, /* sad conend */ 0440007, /* isz conend */ 0617704, /* jmp code2a */ 0000000, 0000000, 0000000, 0000000, 0000000, /* r1b, 0 */ 0700101, /* rsf */ 0617763, /* jmp .-1 */ 0700112, /* rrb */ 0700144, /* rsb */ 0637762, /* jmp i r1b */ 0700144, /* go, rsb */ 0117762, /* g, jms r1b */ 0057775, /* dac cks */ 0417775, /* xct cks */ 0117762, /* jms r1b */ 0000000, /* cks, 0 */ 0617771 /* jmp g */ }; t_stat ptr_boot (int32 unitno, DEVICE *dptr) { int32 i, mask, wd; extern int32 sim_switches; #if defined (PDP7) if (sim_switches & SWMASK ('H')) /* hardware RIM load? */ return ptr_rim_load (&ptr_unit, ASW); #endif if (ptr_dib.dev != DEV_PTR) /* non-std addr? */ return STOP_NONSTD; if (MEMSIZE < 8192) /* 4k? */ mask = 0767777; else mask = 0777777; for (i = 0; i < BOOT_LEN; i++) { wd = boot_rom[i]; if ((wd >= 0040000) && (wd < 0640000)) wd = wd & mask; M[(BOOT_START & mask) + i] = wd; } PC = ((sim_switches & SWMASK ('F'))? BOOT_FPC: BOOT_RPC) & mask; return SCPE_OK; } #else /* PDP-9 and PDP-15 have built-in hardware RIM loaders */ t_stat ptr_boot (int32 unitno, DEVICE *dptr) { return ptr_rim_load (&ptr_unit, ASW); } #endif /* Paper tape punch: IOT routine */ int32 ptp (int32 dev, int32 pulse, int32 dat) { if (pulse & 001) { /* PSF */ if (TST_INT (PTP)) dat = dat | IOT_SKP; } if (pulse & 002) /* PCF */ CLR_INT (PTP); if (pulse & 004) { /* PSA, PSB, PLS */ CLR_INT (PTP); /* clear flag */ ptp_unit.buf = (pulse & 040)? /* load punch buf */ (dat & 077) | 0200: dat & 0377; /* bin or alpha */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ } return dat; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { SET_INT (PTP); /* set flag */ if ((ptp_unit.flags & UNIT_ATT) == 0) { /* not attached? */ ptp_err = 1; /* set error */ return IORETURN (ptp_stopioe, SCPE_UNATT); } if (ptp_unit.flags & UNIT_PASCII) { /* ASCII mode? */ ptp_unit.buf = ptp_unit.buf & 0177; /* force 7b */ if ((ptp_unit.buf == 0) || (ptp_unit.buf == 0177)) return SCPE_OK; /* skip null, del */ } if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* I/O error? */ ptp_err = 1; /* set error */ perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ptp_unit.pos + 1; return SCPE_OK; } /* IORS service */ int32 ptp_iors (void) { return ((TST_INT (PTP)? IOS_PTP: 0) #if defined (IOS_PTPERR) | (ptp_err? IOS_PTPERR: 0) #endif ); } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; CLR_INT (PTP); /* clear flag */ ptp_err = (ptp_unit.flags & UNIT_ATT)? 0: 1; sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Attach routine */ t_stat ptp_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); if (reason != SCPE_OK) return reason; ptp_err = 0; ptp_unit.flags = ptp_unit.flags & ~UNIT_PASCII; if (sim_switches & SWMASK ('A')) ptp_unit.flags = ptp_unit.flags | UNIT_PASCII; return reason; } /* Detach routine */ t_stat ptp_detach (UNIT *uptr) { ptp_err = 1; ptp_unit.flags = ptp_unit.flags & ~UNIT_PASCII; return detach_unit (uptr); } /* Terminal input: IOT routine */ int32 tti (int32 dev, int32 pulse, int32 dat) { if (pulse & 001) { /* KSF */ if (TST_INT (TTI)) dat = dat | IOT_SKP; } if (pulse & 002) { /* KRS/KRB */ CLR_INT (TTI); /* clear flag */ dat = dat | tti_unit.buf & TTI_MASK; /* return buffer */ #if defined (PDP15) if (pulse & 020) /* KRS? */ tti_fdpx = 1; else tti_fdpx = 0; /* no, KRB */ #endif } if (pulse & 004) { /* IORS */ dat = dat | upd_iors (); } return dat; } /* Unit service */ t_stat tti_svc (UNIT *uptr) { #if defined (KSR28) /* Baudot... */ int32 in, c, out; sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ if (tti_2nd) { /* char waiting? */ uptr->buf = tti_2nd; /* return char */ tti_2nd = 0; /* not waiting */ } else { if ((in = sim_poll_kbd ()) < SCPE_KFLAG) return in; c = asc_to_baud[in & 0177]; /* translate char */ if (c == 0) /* untranslatable? */ return SCPE_OK; if ((c & TTI_BOTH) || /* case insensitive? */ (((c & TTI_FIGURES)? 1: 0) == tty_shift)) /* right case? */ uptr->buf = c & TTI_MASK; else { /* send case change */ if (c & TTI_FIGURES) { /* to figures? */ uptr->buf = BAUDOT_FIGURES; tty_shift = 1; } else { /* no, to letters */ uptr->buf = BAUDOT_LETTERS; tty_shift = 0; } tti_2nd = c & TTI_MASK; /* save actual char */ } if ((uptr->flags & TTUF_HDX) && /* half duplex? */ ((out = sim_tt_outcvt (in, TT_GET_MODE (uptr->flags) | TTUF_KSR)) >= 0)) { sim_putchar (out); tto_unit.pos = tto_unit.pos + 1; } } #else /* ASCII... */ int32 c, out; sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; out = c & 0177; /* mask echo to 7b */ if (c & SCPE_BREAK) /* break? */ c = 0; else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); if ((uptr->flags & TTUF_HDX) && !tti_fdpx && out && /* half duplex and */ ((out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags) | TTUF_KSR)) >= 0)) { sim_putchar (out); /* echo */ tto_unit.pos = tto_unit.pos + 1; } uptr->buf = c; /* got char */ #endif uptr->pos = uptr->pos + 1; SET_INT (TTI); /* set flag */ return SCPE_OK; } /* IORS service */ int32 tti_iors (void) { return (TST_INT (TTI)? IOS_TTI: 0); } /* Reset routine */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; /* clear buffer */ tti_2nd = 0; tty_shift = 0; /* clear state */ tti_fdpx = 0; /* clear dpx mode */ CLR_INT (TTI); /* clear flag */ sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll)); return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto (int32 dev, int32 pulse, int32 dat) { if (pulse & 001) { /* TSF */ if (TST_INT (TTO)) dat = dat | IOT_SKP; } if (pulse & 002) /* clear flag */ CLR_INT (TTO); if (pulse & 004) { /* load buffer */ sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ tto_unit.buf = dat & TTO_MASK; /* load buffer */ } return dat; } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; #if defined (KSR28) /* Baudot... */ if (uptr->buf == BAUDOT_FIGURES) /* set figures? */ tty_shift = 1; else if (uptr->buf == BAUDOT_LETTERS) /* set letters? */ tty_shift = 0; else { c = baud_to_asc[uptr->buf | (tty_shift << 5)]; /* translate */ #else c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (c >= 0) { #endif if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry? */ return ((r == SCPE_STALL)? SCPE_OK: r); } } SET_INT (TTO); /* set flag */ uptr->pos = uptr->pos + 1; return SCPE_OK; } /* IORS service */ int32 tto_iors (void) { return (TST_INT (TTO)? IOS_TTO: 0); } /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; /* clear buffer */ tty_shift = 0; /* clear state */ CLR_INT (TTO); /* clear flag */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } /* Set mode */ t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; return SCPE_OK; } simh-3.8.1/PDP18B/pdp18b_lp.c0000644000175000017500000007424311112110440013467 0ustar vlmvlm/* pdp18b_lp.c: 18b PDP's line printer simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lp62 (PDP-4) Type 62 line printer lp647 (PDP-7,9) Type 647 line printer lp09 (PDP-9,15) LP09 line printer lp15 (PDP-15) LP15 line printer 19-Jan-07 RMS Added UNIT_TEXT flag 11-Jun-06 RMS Made character translation table global scope 14-Jan-04 RMS Revised IO device call interface 23-Jul-03 RMS Fixed overprint bug in Type 62 25-Apr-03 RMS Revised for extended file support 05-Feb-03 RMS Added LP09, fixed conditionalization 05-Oct-02 RMS Added DIB, device number support 30-May-02 RMS Widened POS to 32b 03-Feb-02 RMS Fixed typo (found by Robert Alan Byer) 25-Nov-01 RMS Revised interrupt structure 19-Sep-01 RMS Fixed bug in 647 13-Feb-01 RMS Revised for register arrays 15-Feb-01 RMS Fixed 3 cycle data break sequence 30-Oct-00 RMS Standardized register naming 20-Aug-98 RMS Fixed compilation problem in BeOS 03-Jan-97 RMS Fixed bug in Type 62 state handling */ #include "pdp18b_defs.h" extern int32 int_hwre[API_HLVL+1]; const char fio_to_asc[64] = { ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', 'o','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' }; #if defined (TYPE62) /* Type 62 line printer */ #define LP62_BSIZE 120 /* line size */ #define BPTR_MAX 40 /* pointer max */ #define BPTR_MASK 077 /* buf ptr max */ int32 lp62_spc = 0; /* print vs spc */ int32 lp62_ovrpr = 0; /* overprint */ int32 lp62_stopioe = 0; int32 lp62_bp = 0; /* buffer ptr */ char lp62_buf[LP62_BSIZE + 1] = { 0 }; static const char *lp62_cc[] = { "\n", "\n\n", "\n\n\n", "\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\f" }; DEVICE lp62_dev; int32 lp62_65 (int32 dev, int32 pulse, int32 dat); int32 lp62_66 (int32 dev, int32 pulse, int32 dat); int32 lp62_iors (void); t_stat lp62_svc (UNIT *uptr); t_stat lp62_reset (DEVICE *dptr); /* Type 62 LPT data structures lp62_dev LPT device descriptor lp62_unit LPT unit lp62_reg LPT register list */ DIB lp62_dib = { DEV_LPT, 2, &lp62_iors, { &lp62_65, &lp62_66 } }; UNIT lp62_unit = { UDATA (&lp62_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lp62_reg[] = { { ORDATA (BUF, lp62_unit.buf, 8) }, { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, { FLDATA (DONE, int_hwre[API_LPT], INT_V_LPT) }, { FLDATA (SPC, int_hwre[API_LPTSPC], INT_V_LPTSPC) }, { DRDATA (BPTR, lp62_bp, 6) }, { ORDATA (STATE, lp62_spc, 6), REG_HRO }, { FLDATA (OVRPR, lp62_ovrpr, 0), REG_HRO }, { DRDATA (POS, lp62_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lp62_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lp62_stopioe, 0) }, { BRDATA (LBUF, lp62_buf, 8, 8, LP62_BSIZE) }, { ORDATA (DEVNO, lp62_dib.dev, 6), REG_HRO }, { NULL } }; MTAB lp62_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE lp62_dev = { "LPT", &lp62_unit, lp62_reg, lp62_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lp62_reset, NULL, NULL, NULL, &lp62_dib, DEV_DISABLE }; /* IOT routines */ int32 lp62_65 (int32 dev, int32 pulse, int32 dat) { int32 i; if ((pulse & 01) && TST_INT (LPT)) /* LPSF */ dat = IOT_SKP | dat; if (pulse & 02) { int32 sb = pulse & 060; /* subopcode */ if (sb == 000) /* LPCF */ CLR_INT (LPT); if ((sb == 040) && (lp62_bp < BPTR_MAX)) { /* LPLD */ i = lp62_bp * 3; /* cvt to chr ptr */ lp62_buf[i] = fio_to_asc[(dat >> 12) & 077]; lp62_buf[i + 1] = fio_to_asc[(dat >> 6) & 077]; lp62_buf[i + 2] = fio_to_asc[dat & 077]; lp62_bp = (lp62_bp + 1) & BPTR_MASK; } } if (pulse & 04) { /* LPSE */ lp62_spc = 0; /* print */ sim_activate (&lp62_unit, lp62_unit.wait); /* activate */ } return dat; } int32 lp62_66 (int32 dev, int32 pulse, int32 dat) { if ((pulse & 01) && TST_INT (LPTSPC)) /* LSSF */ dat = IOT_SKP | dat; if (pulse & 02) /* LSCF */ CLR_INT (LPTSPC); if (pulse & 04) { /* LSPR */ lp62_spc = 020 | (dat & 07); /* space */ sim_activate (&lp62_unit, lp62_unit.wait); /* activate */ } return dat; } /* Unit service, action based on lp62_spc lp62_spc = 0 write buffer to file, set overprint lp62_spc = 2x space command x, clear overprint */ t_stat lp62_svc (UNIT *uptr) { int32 i; if (lp62_spc) { /* space? */ SET_INT (LPTSPC); /* set flag */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lp62_stopioe, SCPE_UNATT); fputs (lp62_cc[lp62_spc & 07], uptr->fileref); /* print cctl */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lp62_ovrpr = 0; /* clear overprint */ } else { SET_INT (LPT); /* print */ if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (lp62_stopioe, SCPE_UNATT); if (lp62_ovrpr) /* overprint? */ fputc ('\r', uptr->fileref); fputs (lp62_buf, uptr->fileref); /* print buffer */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* test error */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lp62_bp = 0; for (i = 0; i <= LP62_BSIZE; i++) /* clear buffer */ lp62_buf[i] = 0; lp62_ovrpr = 1; /* set overprint */ } return SCPE_OK; } /* Reset routine */ t_stat lp62_reset (DEVICE *dptr) { int32 i; CLR_INT (LPT); /* clear intrs */ CLR_INT (LPTSPC); sim_cancel (&lp62_unit); /* deactivate unit */ lp62_bp = 0; /* clear buffer ptr */ for (i = 0; i <= LP62_BSIZE; i++) /* clear buffer */ lp62_buf[i] = 0; lp62_spc = 0; /* clear state */ lp62_ovrpr = 0; /* clear overprint */ return SCPE_OK; } /* IORS routine */ int32 lp62_iors (void) { return (TST_INT (LPT)? IOS_LPT: 0) | (TST_INT (LPTSPC)? IOS_LPT1: 0); } #endif #if defined (TYPE647) /* Type 647 line printer */ #define LP647_BSIZE 120 /* line size */ int32 lp647_don = 0; /* ready */ int32 lp647_ie = 1; /* int enable */ int32 lp647_err = 0; /* error */ int32 lp647_iot = 0; /* saved state */ int32 lp647_stopioe = 0; int32 lp647_bp = 0; /* buffer ptr */ char lp647_buf[LP647_BSIZE] = { 0 }; static const char *lp647_cc[] = { "\n", "\n\n", "\n\n\n", "\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\f" }; DEVICE lp647_dev; int32 lp647_65 (int32 dev, int32 pulse, int32 dat); int32 lp647_66 (int32 dev, int32 pulse, int32 dat); int32 lp647_iors (void); t_stat lp647_svc (UNIT *uptr); t_stat lp647_reset (DEVICE *dptr); t_stat lp647_attach (UNIT *uptr, char *cptr); t_stat lp647_detach (UNIT *uptr); /* Type 647 LPT data structures lp647_dev LPT device descriptor lp647_unit LPT unit lp647_reg LPT register list */ DIB lp647_dib = { DEV_LPT, 2, &lp647_iors, { &lp647_65, &lp647_66 } }; UNIT lp647_unit = { UDATA (&lp647_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lp647_reg[] = { { ORDATA (BUF, lp647_unit.buf, 8) }, { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, { FLDATA (DONE, lp647_don, 0) }, #if defined (PDP9) { FLDATA (ENABLE, lp647_ie, 0) }, #endif { FLDATA (ERR, lp647_err, 0) }, { DRDATA (BPTR, lp647_bp, 7) }, { ORDATA (SCMD, lp647_iot, 6), REG_HRO }, { DRDATA (POS, lp647_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lp647_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lp647_stopioe, 0) }, { BRDATA (LBUF, lp647_buf, 8, 8, LP647_BSIZE) }, { ORDATA (DEVNO, lp647_dib.dev, 6), REG_HRO }, { NULL } }; MTAB lp647_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE lp647_dev = { "LPT", &lp647_unit, lp647_reg, lp647_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lp647_reset, NULL, &lp647_attach, &lp647_detach, &lp647_dib, DEV_DISABLE }; /* IOT routines */ int32 lp647_65 (int32 dev, int32 pulse, int32 dat) { int32 i, sb; sb = pulse & 060; /* subcode */ if ((pulse & 01) && lp647_don) /* LPSF */ dat = IOT_SKP | dat; if (pulse & 02) { /* pulse 02 */ lp647_don = 0; /* clear done */ CLR_INT (LPT); /* clear int req */ if (sb == 000) { /* LPCB */ for (i = 0; i < LP647_BSIZE; i++) lp647_buf[i] = 0; lp647_bp = 0; /* reset buf ptr */ lp647_don = 1; /* set done */ if (lp647_ie) /* set int */ SET_INT (LPT); } } if (pulse & 004) { /* LPDI */ switch (sb) { /* case on subcode */ case 000: /* LPDI */ #if defined (PDP9) lp647_ie = 0; /* clear int enable */ CLR_INT (LPT); /* clear int req */ #endif break; case 040: /* LPB3 */ if (lp647_bp < LP647_BSIZE) { lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | ((dat >> 12) & 077); lp647_bp = lp647_bp + 1; } case 020: /* LPB2 */ if (lp647_bp < LP647_BSIZE) { lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | ((dat >> 6) & 077); lp647_bp = lp647_bp + 1; } case 060: /* LPB1 */ if (lp647_bp < LP647_BSIZE) { lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | (dat & 077); lp647_bp = lp647_bp + 1; } lp647_don = 1; /* set done */ if (lp647_ie) /* set int */ SET_INT (LPT); break; } /* end case */ } return dat; } int32 lp647_66 (int32 dev, int32 pulse, int32 dat) { if ((pulse & 01) && lp647_err) /* LPSE */ dat = IOT_SKP | dat; if (pulse & 02) { /* LPCF */ lp647_don = 0; /* clear done, int */ CLR_INT (LPT); } if (pulse & 04) { if ((pulse & 060) < 060) { /* LPLS, LPPB, LPPS */ lp647_iot = (pulse & 060) | (dat & 07); /* save parameters */ sim_activate (&lp647_unit, lp647_unit.wait); /* activate */ } #if defined (PDP9) else { /* LPEI */ lp647_ie = 1; /* set int enable */ if (lp647_don) SET_INT (LPT); } #endif } return dat; } /* Unit service. lp647_iot specifies the action to be taken lp647_iot = 0x print only lp647_iot = 2x space only, x is spacing command lp647_iot = 4x print then space, x is spacing command */ t_stat lp647_svc (UNIT *uptr) { int32 i; char pbuf[LP647_BSIZE + 2]; lp647_don = 1; if (lp647_ie) /* set flag */ SET_INT (LPT); if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ lp647_err = 1; /* set error */ return IORETURN (lp647_stopioe, SCPE_UNATT); } if ((lp647_iot & 020) == 0) { /* print? */ for (i = 0; i < lp647_bp; i++) /* translate buffer */ pbuf[i] = lp647_buf[i] | ((lp647_buf[i] >= 040)? 0: 0100); if ((lp647_iot & 060) == 0) pbuf[lp647_bp++] = '\r'; pbuf[lp647_bp++] = 0; /* append nul */ for (i = 0; i < LP647_BSIZE; i++) /* clear buffer */ lp647_buf[i] = 0; fputs (pbuf, uptr->fileref); /* print buffer */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); lp647_bp = 0; return SCPE_IOERR; } lp647_bp = 0; /* clear buffer ptr */ } if (lp647_iot & 060) { /* space? */ fputs (lp647_cc[lp647_iot & 07], uptr->fileref); /* write cctl */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } } return SCPE_OK; } /* Reset routine */ t_stat lp647_reset (DEVICE *dptr) { int32 i; lp647_don = 0; /* clear done */ lp647_err = (lp647_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ lp647_ie = 1; /* set enable */ CLR_INT (LPT); /* clear int */ sim_cancel (&lp647_unit); /* deactivate unit */ lp647_bp = 0; /* clear buffer ptr */ lp647_iot = 0; /* clear state */ for (i = 0; i < LP647_BSIZE; i++) /* clear buffer */ lp647_buf[i] = 0; return SCPE_OK; } /* IORS routine */ int32 lp647_iors (void) { return (lp647_don? IOS_LPT: 0) | (lp647_err? IOS_LPT1: 0); } /* Attach routine */ t_stat lp647_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); lp647_err = (lp647_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ return reason; } /* Detach routine */ t_stat lp647_detach (UNIT *uptr) { lp647_err = 1; return detach_unit (uptr); } #endif #if defined (LP09) /* LP09 line printer */ #define LP09_BSIZE 132 /* line size */ int32 lp09_don = 0; /* ready */ int32 lp09_err = 0; /* error */ int32 lp09_ie = 1; /* int enable */ int32 lp09_stopioe = 0; DEVICE lp09_dev; int32 lp09_66 (int32 dev, int32 pulse, int32 dat); int32 lp09_iors (void); t_stat lp09_svc (UNIT *uptr); t_stat lp09_reset (DEVICE *dptr); t_stat lp09_attach (UNIT *uptr, char *cptr); t_stat lp09_detach (UNIT *uptr); /* LP09 LPT data structures lp09_dev LPT device descriptor lp09_unit LPT unit lp09_reg LPT register list */ DIB lp09_dib = { DEV_LPT, 2, &lp09_iors, { NULL, &lp09_66 } }; UNIT lp09_unit = { UDATA (&lp09_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lp09_reg[] = { { ORDATA (BUF, lp09_unit.buf, 7) }, { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, { FLDATA (DONE, lp09_don, 0) }, { FLDATA (ENABLE, lp09_ie, 0) }, { FLDATA (ERR, lp09_err, 0) }, { DRDATA (POS, lp09_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lp09_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lp09_stopioe, 0) }, { ORDATA (DEVNO, lp09_dib.dev, 6), REG_HRO }, { NULL } }; MTAB lp09_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE lp09_dev = { "LP9", &lp09_unit, lp09_reg, lp09_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lp09_reset, NULL, &lp09_attach, &lp09_detach, &lp09_dib, DEV_DISABLE | DEV_DIS }; /* IOT routines */ int32 lp09_66 (int32 dev, int32 pulse, int32 dat) { int32 sb = pulse & 060; /* subopcode */ if (pulse & 001) { if ((sb == 000) && lp09_don) /* LSDF */ dat = IOT_SKP | dat; if ((sb == 020) && lp09_err) /* LSEF */ dat = IOT_SKP | dat; } if (pulse & 002) { if (sb == 000) { /* LSCF */ lp09_don = 0; /* clear done, int */ CLR_INT (LPT); } else if (sb == 020) { /* LPLD */ lp09_don = 0; /* clear done, int */ CLR_INT (LPT); lp09_unit.buf = dat & 0177; /* load char */ if ((lp09_unit.buf == 015) || (lp09_unit.buf == 014) || (lp09_unit.buf == 012)) sim_activate (&lp09_unit, lp09_unit.wait); else dat = dat | (lp09_svc (&lp09_unit) << IOT_V_REASON); } } if (pulse & 004) { if (sb == 000) { /* LIOF */ lp09_ie = 0; /* clear int enab */ CLR_INT (LPT); /* clear int */ } else if (sb == 040) { /* LION */ lp09_ie = 1; /* set int enab */ if (lp09_don) /* if done, set int */ SET_INT (LPT); } } return dat; } /* Unit service */ t_stat lp09_svc (UNIT *uptr) { int32 c; lp09_don = 1; /* set done */ if (lp09_ie) /* int enb? req int */ SET_INT (LPT); if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ lp09_err = 1; /* set error */ return IORETURN (lp09_stopioe, SCPE_UNATT); } c = uptr->buf & 0177; /* get char */ if ((c == 0) || (c == 0177)) /* skip NULL, DEL */ return SCPE_OK; fputc (c, uptr->fileref); /* print char */ uptr->pos = ftell (uptr->fileref); /* update position */ if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Reset routine */ t_stat lp09_reset (DEVICE *dptr) { lp09_don = 0; /* clear done */ lp09_err = (lp09_unit.flags & UNIT_ATT)? 0: 1; /* compute error */ lp09_ie = 1; /* set enable */ CLR_INT (LPT); /* clear int */ return SCPE_OK; } /* IORS routine */ int32 lp09_iors (void) { return (lp09_don? IOS_LPT: 0); } /* Attach routine */ t_stat lp09_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); lp09_err = (lp09_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ return reason; } /* Detach routine */ t_stat lp09_detach (UNIT *uptr) { lp09_err = 1; return detach_unit (uptr); } #endif #if defined (LP15) /* LP15 line printer */ #define LP15_BSIZE 132 /* line size */ #define LPT_WC 034 /* word count */ #define LPT_CA 035 /* current addr */ /* Status register */ #define STA_ERR 0400000 /* error */ #define STA_ALM 0200000 /* alarm */ #define STA_OVF 0100000 /* line overflow */ #define STA_IHT 0040000 /* illegal HT */ #define STA_BUSY 0020000 /* busy */ #define STA_DON 0010000 /* done */ #define STA_ILK 0004000 /* interlock */ #define STA_EFLGS (STA_ALM | STA_OVF | STA_IHT | STA_ILK) #define STA_CLR 0003777 /* always clear */ extern int32 M[]; int32 lp15_sta = 0; int32 lp15_ie = 1; int32 lp15_stopioe = 0; int32 lp15_mode = 0; int32 lp15_lc = 0; int32 lp15_bp = 0; char lp15_buf[LP15_BSIZE + 1] = { 0 }; DEVICE lp15_dev; int32 lp15_65 (int32 dev, int32 pulse, int32 dat); int32 lp15_66 (int32 dev, int32 pulse, int32 dat); int32 lp15_iors (void); t_stat lp15_svc (UNIT *uptr); t_stat lp15_reset (DEVICE *dptr); int32 lp15_updsta (int32 new); /* LP15 LPT data structures lp15_dev LPT device descriptor lp15_unit LPT unit lp15_reg LPT register list */ DIB lp15_dib = { DEV_LPT, 2, &lp15_iors, { &lp15_65, &lp15_66 } }; UNIT lp15_unit = { UDATA (&lp15_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lp15_reg[] = { { ORDATA (STA, lp15_sta, 18) }, { ORDATA (CA, M[LPT_CA], 18) }, { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, { FLDATA (ENABLE, lp15_ie, 0) }, { DRDATA (LCNT, lp15_lc, 9) }, { DRDATA (BPTR, lp15_bp, 8) }, { FLDATA (MODE, lp15_mode, 0) }, { DRDATA (POS, lp15_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lp15_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lp15_stopioe, 0) }, { BRDATA (LBUF, lp15_buf, 8, 8, LP15_BSIZE) }, { ORDATA (DEVNO, lp15_dib.dev, 6), REG_HRO }, { NULL } }; MTAB lp15_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, { 0 } }; DEVICE lp15_dev = { "LPT", &lp15_unit, lp15_reg, lp15_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &lp15_reset, NULL, NULL, NULL, &lp15_dib, DEV_DISABLE }; /* IOT routines */ int32 lp15_65 (int32 dev, int32 pulse, int32 dat) { int32 header, sb; sb = pulse & 060; /* subopcode */ if (pulse & 01) { if ((sb == 000) && (lp15_sta & (STA_ERR | STA_DON))) /* LPSF */ dat = IOT_SKP | dat; else if ((sb == 020) || (sb == 040)) { /* LPP1, LPPM */ sim_activate (&lp15_unit, lp15_unit.wait); /* activate */ header = M[(M[LPT_CA] + 1) & AMASK]; /* get first word */ M[LPT_CA] = (M[LPT_CA] + 2) & DMASK; lp15_mode = header & 1; /* mode */ if (sb == 040) /* line count */ lp15_lc = 1; else lp15_lc = (header >> 9) & 0377; if (lp15_lc == 0) lp15_lc = 256; lp15_bp = 0; /* reset buf ptr */ } else if (sb == 060) /* LPDI */ lp15_ie = 0; } if ((pulse & 02) && (sb == 040)) /* LPOS, LPRS */ dat = dat | lp15_updsta (0); if ((pulse & 04) && (sb == 040)) /* LPEI */ lp15_ie = 1; lp15_updsta (0); /* update status */ return dat; } int32 lp15_66 (int32 dev, int32 pulse, int32 dat) { if (pulse == 021) /* LPCD */ lp15_sta = lp15_sta & ~STA_DON; if (pulse == 041) /* LPCF */ lp15_sta = 0; lp15_updsta (0); /* update status */ return dat; } /* Unit service */ t_stat lp15_svc (UNIT *uptr) { int32 i, ccnt, more, w0, w1; char c[5]; static const char *ctrl[040] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\f", "\r", NULL, NULL, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "\n\n", "\n\n\n", "\n", "\n\n\n\n\n\n\n\n\n\n", NULL, NULL, NULL, NULL, NULL, NULL, "\r", NULL, NULL, NULL, NULL }; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ lp15_updsta (STA_DON | STA_ALM); /* set done, err */ return IORETURN (lp15_stopioe, SCPE_UNATT); } for (more = 1; more != 0; ) { /* loop until ctrl */ w0 = M[(M[LPT_CA] + 1) & AMASK]; /* get first word */ w1 = M[(M[LPT_CA] + 2) & AMASK]; /* get second word */ M[LPT_CA] = (M[LPT_CA] + 2) & DMASK; /* advance mem addr */ if (lp15_mode) { /* unpacked? */ c[0] = w0 & 0177; c[1] = w1 & 0177; ccnt = 2; } else { /* packed */ c[0] = (w0 >> 11) & 0177; c[1] = (w0 >> 4) & 0177; c[2] = (((w0 << 3) | (w1 >> 15))) & 0177; c[3] = (w1 >> 8) & 0177; c[4] = (w1 >> 1) & 0177; ccnt = 5; } for (i = 0; i < ccnt; i++) { /* loop through */ if ((c[i] <= 037) && ctrl[c[i]]) { /* control char? */ lp15_buf[lp15_bp] = 0; /* append nul */ fputs (lp15_buf, uptr->fileref); /* print line */ fputs (ctrl[c[i]], uptr->fileref); /* space */ uptr->pos = ftell (uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("LPT I/O error"); clearerr (uptr->fileref); lp15_bp = 0; lp15_updsta (STA_DON | STA_ALM); return SCPE_IOERR; } lp15_bp = more = 0; } else { if (lp15_bp < LP15_BSIZE) lp15_buf[lp15_bp++] = c[i]; else lp15_sta = lp15_sta | STA_OVF; } } } lp15_lc = lp15_lc - 1; /* decr line count */ if (lp15_lc) /* more to do? */ sim_activate (&lp15_unit, uptr->wait); else lp15_updsta (STA_DON); /* no, set done */ return SCPE_OK; } /* Update status */ int32 lp15_updsta (int32 new) { lp15_sta = (lp15_sta | new) & ~(STA_CLR | STA_ERR | STA_BUSY); if (lp15_sta & STA_EFLGS) /* update errors */ lp15_sta = lp15_sta | STA_ERR; if (sim_is_active (&lp15_unit)) lp15_sta = lp15_sta | STA_BUSY; if (lp15_ie && (lp15_sta & STA_DON)) SET_INT (LPT); else CLR_INT (LPT); /* update int */ return lp15_sta; } /* Reset routine */ t_stat lp15_reset (DEVICE *dptr) { lp15_mode = lp15_lc = lp15_bp = 0; /* clear controls */ sim_cancel (&lp15_unit); /* deactivate unit */ lp15_sta = 0; /* clear status */ lp15_ie = 1; /* enable interrupts */ lp15_updsta (0); /* update status */ return SCPE_OK; } /* IORS routine */ int32 lp15_iors (void) { return ((lp15_sta & STA_DON)? IOS_LPT: 0); } #endif simh-3.8.1/PDP18B/pdp18b_cpu.c0000644000175000017500000030204211112110440013632 0ustar vlmvlm/* pdp18b_cpu.c: 18b PDP CPU simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu PDP-4/7/9/15 central processor 28-Apr-07 RMS Removed clock initialization 26-Dec-06 RMS Fixed boundary test in KT15/XVM (reported by Andrew Warkentin) 30-Oct-06 RMS Added idle and infinite loop detection 08-Oct-06 RMS Added RDCLK instruction Fixed bug, PC off by one on fetch mem mmgt error PDP-15 sets API 3 on mem mmgt trap (like PI) PDP-15 sets API 4 on CAL only if 0-3 inactive CAF clears memory management mode register 27-Jun-06 RMS Reset clears AC, L, and MQ 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 22-Jul-05 RMS Removed AAS, error in V1 reference manual 06-Nov-04 RMS Added =n to SHOW HISTORY 26-Mar-04 RMS Fixed warning from -std=c99 14-Jan-04 RMS Fixed g_mode in XVM implementation PDP-15 index, autoincrement generate 18b addresses Revised IO device call interface 31-Dec-03 RMS Fixed bug in cpu_set_hist 02-Nov-03 RMS Changed PDP-9,-15 default to API 26-Oct-03 RMS Fixed bug in PDP-4,-7,-9 autoincrement addressing 19-Sep-03 RMS Changed instruction history to be dynamically sized 31-Aug-03 RMS Added instruction history Fixed PDP-15-specific implementation of API priorities 16-Aug-03 RMS Fixed PDP-15-specific handling of EAE unsigned mul/div 27-Jul-03 RMS Added FP15 support Added XVM support Added EAE option to PDP-4 Added PDP-15 "re-entrancy ECO" Fixed memory protect/skip interaction Fixed CAF not to reset CPU 12-Mar-03 RMS Added logical name support 18-Feb-03 RMS Fixed three EAE bugs (found by Hans Pufal) 05-Oct-02 RMS Added DIBs, device number support 25-Jul-02 RMS Added DECtape support for PDP-4 06-Jan-02 RMS Revised enable/disable support 30-Dec-01 RMS Added old PC queue 30-Nov-01 RMS Added extended SET/SHOW support 25-Nov-01 RMS Revised interrupt structure 19-Sep-01 RMS Fixed bug in EAE (found by Dave Conroy) 17-Sep-01 RMS Fixed typo in conditional 10-Aug-01 RMS Removed register from declarations 17-Jul-01 RMS Moved function prototype 27-May-01 RMS Added second Teletype support, fixed bug in API 18-May-01 RMS Added PDP-9,-15 API option 16-May-01 RMS Fixed bugs in protection checks 26-Apr-01 RMS Added device enable/disable support 25-Jan-01 RMS Added DECtape support 18-Dec-00 RMS Added PDP-9,-15 memm init register 30-Nov-00 RMS Fixed numerous PDP-15 bugs 14-Apr-99 RMS Changed t_addr to unsigned The 18b PDP family has five distinct architectural variants: PDP-1, PDP-4, PDP-7, PDP-9, and PDP-15. Of these, the PDP-1 is so unique as to require a different simulator. The PDP-4, PDP-7, PDP-9, and PDP-15 are "upward compatible", with each new variant adding distinct architectural features and incompatibilities. The register state for the 18b PDP's is: all AC<0:17> accumulator all MQ<0:17> multiplier-quotient all L link flag all PC<0:x> program counter all IORS I/O status register PDP-7, PDP-9 EXTM extend mode PDP-15 BANKM bank mode PDP-7 USMD trap mode PDP-9, PDP-15 USMD user mode PDP-9, PDP-15 BR bounds register PDP-15 RR relocation register PDP-15 XVM MMR memory management register PDP-15 XR index register PDP-15 LR limit register The PDP-4, PDP-7, and PDP-9 have five instruction formats: memory reference, load immediate, I/O transfer, EAE, and operate. The PDP-15 adds a sixth, index operate, and a seventh, floating point. The memory reference format for the PDP-4, PDP-7, and PDP-9, and for the PDP-15 in bank mode, is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op |in| address | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The PDP-15 in page mode trades an address bit for indexing capability: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | op |in| X| address | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:3> mnemonic action 00 CAL JMS with MA = 20 04 DAC M[MA] = AC 10 JMS M[MA] = L'mem'user'PC, PC = MA + 1 14 DZM M[MA] = 0 20 LAC AC = M[MA] 24 XOR AC = AC ^ M[MA] 30 ADD L'AC = AC + M[MA] one's complement 34 TAD L'AC = AC + M[MA] 40 XCT M[MA] is executed as an instruction 44 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 50 AND AC = AC & M[MA] 54 SAD skip if AC != M[MA] 60 JMP PC = MA On the PDP-4, PDP-7, and PDP-9, and the PDP-15 in bank mode, memory reference instructions can access an address space of 32K words. The address space is divided into four 8K word fields. An instruction can directly address, via its 13b address, the entire current field. On the PDP-4, PDP-7, and PDP-9, if extend mode is off, indirect addresses access the current field; if on (or a PDP-15), they can access all 32K. On the PDP-15 in page mode, memory reference instructions can access an address space of 128K words. The address is divided into four 32K word blocks, each of which consists of eight 4K pages. An instruction can directly address, via its 12b address, the current page. Indirect addresses can access the current block. Indexed and autoincrement addresses can access all 128K. On the PDP-4 and PDP-7, if an indirect address in in locations 00010- 00017 of any field, the indirect address is incremented and rewritten to memory before use. On the PDP-9 and PDP-15, only locations 00010- 00017 of field zero autoincrement; special logic will redirect indirect references to 00010-00017 to field zero, even if (on the PDP-9) extend mode is off. The EAE format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 0 1| | | | | | | | | | | | | | | EAE +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | | | | | | | | | | | | | +- or SC (3) | | | | | | | | | | | | +---- or MQ (3) | | | | | | | | | | | +------- compl MQ (3) | | | | | | | | \______________/ | | | | | | | | | | | | | | \_____/ +--------- shift count | | | | | | | | | | | +---------------------- EAE command (3) | | | | +---------------------------- clear AC (2) | | | +------------------------------- or AC (2) | | +---------------------------------- load EAE sign (1) | +------------------------------------- clear MQ (1) +---------------------------------------- load link (1) The I/O transfer format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 0 0| device | sdv |cl| pulse | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The IO transfer instruction sends the the specified pulse to the specified I/O device and sub-device. The I/O device may take data from the AC, return data to the AC, initiate or cancel operations, or skip on status. On the PDP-4, PDP-7, and PDP-9, bits <4:5> were designated as subdevice bits but were never used; the PDP-15 requires them to be zero. On the PDP-15, the floating point format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 0 1| subopcode | floating point +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |in| address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ Indirection is always single level. On the PDP-15, the index operate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 0 1| subopcode | immediate | index operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The index operate instructions provide various operations on the index and limit registers. The operate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 1 0| | | | | | | | | | | | | | operate +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | | | | | | | | | | | +- CMA (3) | | | | | | | | | | | +---- CML (3) | | | | | | | | | | +------- OAS (3) | | | | | | | | | +---------- RAL (3) | | | | | | | | +------------- RAR (3) | | | | | | | +---------------- HLT (4) | | | | | | +------------------- SMA (1) | | | | | +---------------------- SZA (1) | | | | +------------------------- SNL (1) | | | +---------------------------- invert skip (1) | | +------------------------------- rotate twice (2) | +---------------------------------- CLL (2) +------------------------------------- CLA (2) The operate instruction can be microprogrammed to perform operations on the AC and link. The load immediate format is: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 1 1 1 1 1| immediate | LAW +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ <0:4> mnemonic action 76 LAW AC = IR This routine is the instruction decode routine for the 18b PDP's. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered unimplemented instruction and STOP_INST flag set nested XCT's I/O error in I/O simulator 2. Interrupts. Interrupt requests are maintained in the int_hwre array. int_hwre[0:3] corresponds to API levels 0-3; int_hwre[4] holds PI requests. 3. Arithmetic. The 18b PDP's implements both 1's and 2's complement arithmetic for signed numbers. In 1's complement arithmetic, a negative number is represented by the complement (XOR 0777777) of its absolute value. Addition of 1's complement numbers requires propagating the carry out of the high order bit back to the low order bit. 4. Adding I/O devices. Three modules must be modified: pdp18b_defs.h add interrupt request definition pdp18b_sys.c add sim_devices table entry */ #include "pdp18b_defs.h" #define SEXT(x) ((int32) (((x) & SIGN)? (x) | ~DMASK: (x) & DMASK)) #define UNIT_V_NOEAE (UNIT_V_UF + 0) /* EAE absent */ #define UNIT_V_NOAPI (UNIT_V_UF + 1) /* API absent */ #define UNIT_V_PROT (UNIT_V_UF + 2) /* protection */ #define UNIT_V_RELOC (UNIT_V_UF + 3) /* relocation */ #define UNIT_V_XVM (UNIT_V_UF + 4) /* XVM */ #define UNIT_V_MSIZE (UNIT_V_UF + 5) /* dummy mask */ #define UNIT_NOEAE (1 << UNIT_V_NOEAE) #define UNIT_NOAPI (1 << UNIT_V_NOAPI) #define UNIT_PROT (1 << UNIT_V_PROT) #define UNIT_RELOC (1 << UNIT_V_RELOC) #define UNIT_XVM (1 << UNIT_V_XVM) #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define OP_KSF 0700301 #define HIST_API 0x40000000 #define HIST_PI 0x20000000 #define HIST_PC 0x10000000 #define HIST_MIN 64 #define HIST_MAX 65536 #define HIST_M_LVL 0x3F #define HIST_V_LVL 6 typedef struct { int32 pc; int32 ir; int32 ir1; int32 lac; int32 mq; } InstHistory; #define XVM (cpu_unit.flags & UNIT_XVM) #define RELOC (cpu_unit.flags & UNIT_RELOC) #define PROT (cpu_unit.flags & UNIT_PROT) #if defined (PDP4) #define EAE_DFLT UNIT_NOEAE #else #define EAE_DFLT 0 #endif #if defined (PDP4) || defined (PDP7) #define API_DFLT UNIT_NOAPI #define PROT_DFLT 0 #define ASW_DFLT 017763 #else #define API_DFLT 0 #define PROT_DFLT UNIT_PROT #define ASW_DFLT 017720 #endif int32 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 LAC = 0; /* link'AC */ int32 MQ = 0; /* MQ */ int32 PC = 0; /* PC */ int32 iors = 0; /* IORS */ int32 ion = 0; /* int on */ int32 ion_defer = 0; /* int defer */ int32 ion_inh = 0; /* int inhibit */ int32 int_pend = 0; /* int pending */ int32 int_hwre[API_HLVL+1] = { 0 }; /* int requests */ int32 api_enb = 0; /* API enable */ int32 api_req = 0; /* API requests */ int32 api_act = 0; /* API active */ int32 memm = 0; /* mem mode */ #if defined (PDP15) int32 memm_init = 1; /* mem init */ #else int32 memm_init = 0; #endif int32 usmd = 0; /* user mode */ int32 usmd_buf = 0; /* user mode buffer */ int32 usmd_defer = 0; /* user mode defer */ int32 trap_pending = 0; /* trap pending */ int32 emir_pending = 0; /* emir pending */ int32 rest_pending = 0; /* restore pending */ int32 BR = 0; /* mem mgt bounds */ int32 RR = 0; /* mem mgt reloc */ int32 MMR = 0; /* XVM mem mgt */ int32 nexm = 0; /* nx mem flag */ int32 prvn = 0; /* priv viol flag */ int32 SC = 0; /* shift count */ int32 eae_ac_sign = 0; /* EAE AC sign */ int32 SR = 0; /* switch register */ int32 ASW = ASW_DFLT; /* address switches */ int32 XR = 0; /* index register */ int32 LR = 0; /* limit register */ int32 stop_inst = 0; /* stop on rsrv inst */ int32 xct_max = 16; /* nested XCT limit */ #if defined (PDP15) int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ #else int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ #endif 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; /* instruction history */ extern int32 sim_int_char; extern int32 sim_interval; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern t_bool sim_idle_enab; extern DEVICE *sim_devices[]; extern FILE *sim_log; t_bool build_dev_tab (void); 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); void cpu_caf (void); void cpu_inst_hist (int32 addr, int32 inst); void cpu_intr_hist (int32 flag, int32 lvl); int32 upd_iors (void); int32 api_eval (int32 *pend); t_stat Read (int32 ma, int32 *dat, int32 cyc); t_stat Write (int32 ma, int32 dat, int32 cyc); t_stat Ia (int32 ma, int32 *ea, t_bool jmp); int32 Incr_addr (int32 addr); int32 Jms_word (int32 t); #if defined (PDP15) #define INDEX(i,x) if (!memm && ((i) & I_IDX)) \ x = ((x) + XR) & DMASK int32 Prot15 (int32 ma, t_bool bndchk); int32 Reloc15 (int32 ma, int32 acc); int32 RelocXVM (int32 ma, int32 acc); extern t_stat fp15 (int32 ir); extern int32 clk_task_upd (t_bool clr); #else #define INDEX(i,x) #endif extern int32 clk (int32 dev, int32 pulse, int32 AC); int32 (*dev_tab[DEV_MAX])(int32 dev, int32 pulse, int32 AC); /* device dispatch */ int32 (*dev_iors[DEV_MAX])(void); /* IORS dispatch */ static const int32 api_ffo[256] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 }; static const int32 api_vec[API_HLVL][32] = { { ACH_PWRFL }, /* API 0 */ { ACH_DTA, ACH_MTA, ACH_DRM, ACH_RF, ACH_RP, ACH_RB }, /* API 1 */ { ACH_PTR, ACH_LPT, ACH_LPT }, /* API 2 */ { ACH_CLK, ACH_TTI1, ACH_TTO1 } /* API 3 */ }; /* 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+EAE_DFLT+API_DFLT+PROT_DFLT, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, PC, ADDRSIZE) }, { ORDATA (AC, LAC, 18) }, { FLDATA (L, LAC, 18) }, { ORDATA (MQ, MQ, 18) }, { ORDATA (SC, SC, 6) }, { FLDATA (EAE_AC_SIGN, eae_ac_sign, 18) }, { ORDATA (SR, SR, 18) }, { ORDATA (ASW, ASW, ADDRSIZE) }, { ORDATA (IORS, iors, 18), REG_RO }, { BRDATA (INT, int_hwre, 8, 32, API_HLVL+1), REG_RO }, { FLDATA (INT_PEND, int_pend, 0), REG_RO }, { FLDATA (ION, ion, 0) }, { ORDATA (ION_DELAY, ion_defer, 2) }, #if defined (PDP7) { FLDATA (TRAPM, usmd, 0) }, { FLDATA (TRAPP, trap_pending, 0) }, { FLDATA (EXTM, memm, 0) }, { FLDATA (EXTM_INIT, memm_init, 0) }, { FLDATA (EMIRP, emir_pending, 0) }, #endif #if defined (PDP9) { FLDATA (APIENB, api_enb, 0) }, { ORDATA (APIREQ, api_req, 8) }, { ORDATA (APIACT, api_act, 8) }, { ORDATA (BR, BR, ADDRSIZE) }, { FLDATA (USMD, usmd, 0) }, { FLDATA (USMDBUF, usmd_buf, 0) }, { FLDATA (USMDDEF, usmd_defer, 0) }, { FLDATA (NEXM, nexm, 0) }, { FLDATA (PRVN, prvn, 0) }, { FLDATA (TRAPP, trap_pending, 0) }, { FLDATA (EXTM, memm, 0) }, { FLDATA (EXTM_INIT, memm_init, 0) }, { FLDATA (EMIRP, emir_pending, 0) }, { FLDATA (RESTP, rest_pending, 0) }, { FLDATA (PWRFL, int_hwre[API_PWRFL], INT_V_PWRFL) }, #endif #if defined (PDP15) { FLDATA (ION_INH, ion_inh, 0) }, { FLDATA (APIENB, api_enb, 0) }, { ORDATA (APIREQ, api_req, 8) }, { ORDATA (APIACT, api_act, 8) }, { ORDATA (XR, XR, 18) }, { ORDATA (LR, LR, 18) }, { ORDATA (BR, BR, 18) }, { ORDATA (RR, RR, 18) }, { ORDATA (MMR, MMR, 18) }, { FLDATA (USMD, usmd, 0) }, { FLDATA (USMDBUF, usmd_buf, 0) }, { FLDATA (USMDDEF, usmd_defer, 0) }, { FLDATA (NEXM, nexm, 0) }, { FLDATA (PRVN, prvn, 0) }, { FLDATA (TRAPP, trap_pending, 0) }, { FLDATA (BANKM, memm, 0) }, { FLDATA (BANKM_INIT, memm_init, 0) }, { FLDATA (RESTP, rest_pending, 0) }, { FLDATA (PWRFL, int_hwre[API_PWRFL], INT_V_PWRFL) }, #endif { BRDATA (PCQ, pcq, 8, ADDRSIZE, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { FLDATA (STOP_INST, stop_inst, 0) }, { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, { ORDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, #if defined (PDP9) || defined (PDP15) { UNIT_NOAPI, UNIT_NOAPI, "no API", "NOAPI", NULL }, { UNIT_NOAPI, 0, "API", "API", NULL }, { UNIT_PROT+UNIT_RELOC+UNIT_XVM, 0, "no memory protect", "NOPROTECT", NULL }, { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT, "memory protect", "PROTECT", NULL }, #endif #if defined (PDP15) { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT+UNIT_RELOC, "memory relocation", "RELOCATION", NULL }, { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT+UNIT_RELOC+UNIT_XVM, "XVM", "XVM", NULL }, #endif { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, #if defined (PDP4) { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, #endif { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, #if (MAXMEMSIZE > 8192) { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, #endif #if (MAXMEMSIZE > 32768) { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, { UNIT_MSIZE, 81920, NULL, "80K", &cpu_set_size }, { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size }, { UNIT_MSIZE, 114688, NULL, "112K", &cpu_set_size }, { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, #endif { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, ADDRSIZE, 1, 8, 18, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; t_stat sim_instr (void) { int32 api_int, api_usmd, skp; int32 iot_data, device, pulse; int32 last_IR; t_stat reason; if (build_dev_tab ()) /* build, chk tables */ return SCPE_STOP; PC = PC & AMASK; /* clean variables */ LAC = LAC & LACMASK; MQ = MQ & DMASK; reason = 0; last_IR = -1; if (cpu_unit.flags & UNIT_NOAPI) /* no API? */ api_enb = api_req = api_act = 0; api_int = api_eval (&int_pend); /* eval API */ api_usmd = 0; /* not API user cycle */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ int32 IR, MA, MB, esc, t, xct_count; int32 link_init, fill; if (sim_interval <= 0) { /* check clock queue */ if (reason = sim_process_event ()) break; api_int = api_eval (&int_pend); /* eval API */ } /* PDP-4 and PDP-7 traps and interrupts PDP-4 no trap PDP-7 trap: extend mode forced on, M[0] = PC, PC = 2 PDP-4, PDP-7 programmable interrupts only */ #if defined (PDP4) || defined (PDP7) #if defined (PDP7) if (trap_pending) { /* trap pending? */ PCQ_ENTRY; /* save old PC */ MB = Jms_word (1); /* save state */ ion = 0; /* interrupts off */ memm = 1; /* extend on */ emir_pending = trap_pending = 0; /* emir, trap off */ usmd = usmd_buf = 0; /* user mode off */ Write (0, MB, WR); /* save in 0 */ PC = 2; /* fetch next from 2 */ } #endif if (int_pend && ion && !ion_defer) { /* interrupt? */ PCQ_ENTRY; /* save old PC */ MB = Jms_word (usmd); /* save state */ ion = 0; /* interrupts off */ memm = 0; /* extend off */ emir_pending = rest_pending = 0; /* emir, restore off */ usmd = usmd_buf = 0; /* user mode off */ Write (0, MB, WR); /* physical write */ PC = 1; /* fetch next from 1 */ } if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } #endif /* end PDP-4/PDP-7 */ /* PDP-9 and PDP-15 traps and interrupts PDP-9 trap: extend mode ???, M[0/20] = PC, PC = 0/21 PDP-15 trap: bank mode unchanged, M[0/20] = PC, PC = 0/21 PDP-9, PDP-15 API and program interrupts */ #if defined (PDP9) || defined (PDP15) if (trap_pending) { /* trap pending? */ PCQ_ENTRY; /* save old PC */ MB = Jms_word (1); /* save state */ if (ion) { /* int on? */ ion = 0; /* interrupts off */ MA = 0; /* treat like PI */ #if defined (PDP15) ion_defer = 2; /* free instruction */ if (!(cpu_unit.flags & UNIT_NOAPI)) { /* API? */ api_act = api_act | API_ML3; /* set lev 3 active */ api_int = api_eval (&int_pend); /* re-evaluate */ } #endif } else MA = 020; /* sortof like CAL */ emir_pending = rest_pending = trap_pending = 0; /* emir,rest,trap off */ usmd = usmd_buf = 0; /* user mode off */ Write (MA, MB, WR); /* physical write */ PC = MA + 1; /* fetch next */ } if (api_int && !ion_defer) { /* API intr? */ int32 i, lvl = api_int - 1; /* get req level */ if (hst_lnt) /* record */ cpu_intr_hist (HIST_API, lvl); api_act = api_act | (API_ML0 >> lvl); /* set level active */ if (lvl >= API_HLVL) { /* software req? */ MA = ACH_SWRE + lvl - API_HLVL; /* vec = 40:43 */ api_req = api_req & ~(API_ML0 >> lvl); /* remove request */ } else { MA = 0; /* assume fails */ for (i = 0; i < 32; i++) { /* loop hi to lo */ if ((int_hwre[lvl] >> i) & 1) { /* int req set? */ MA = api_vec[lvl][i]; /* get vector */ break; /* and stop */ } } } if (MA == 0) { /* bad channel? */ reason = STOP_API; /* API error */ break; } api_int = api_eval (&int_pend); /* no API int */ api_usmd = usmd; /* API user mode cycle */ usmd = usmd_buf = 0; /* user mode off */ emir_pending = rest_pending = 0; /* emir, restore off */ xct_count = 0; Read (MA, &IR, FE); /* fetch instruction */ goto xct_instr; } if (int_pend && ion && !ion_defer && /* int pending, enabled? */ !(api_enb && (api_act & API_MASKPI))) { /* API off or not masking PI? */ PCQ_ENTRY; /* save old PC */ if (hst_lnt) /* record */ cpu_intr_hist (HIST_PI, 0); MB = Jms_word (usmd); /* save state */ ion = 0; /* interrupts off */ ion_defer = 2; /* free instruction */ #if defined (PDP9) /* PDP-9, */ memm = 0; /* extend off */ #else /* PDP-15 */ if (!(cpu_unit.flags & UNIT_NOAPI)) { /* API? */ api_act = api_act | API_ML3; /* set lev 3 active */ api_int = api_eval (&int_pend); /* re-evaluate */ } #endif emir_pending = rest_pending = 0; /* emir, restore off */ usmd = usmd_buf = 0; /* user mode off */ Write (0, MB, WR); /* physical write */ PC = 1; /* fetch next from 1 */ } if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } if (!usmd_defer) /* no IOT? load usmd */ usmd = usmd_buf; else usmd_defer = 0; /* cancel defer */ #endif /* PDP-9/PDP-15 */ /* Instruction fetch and address decode */ xct_count = 0; /* track nested XCT's */ MA = PC; /* fetch at PC */ if (Read (MA, &IR, FE)) /* fetch instruction */ continue; PC = Incr_addr (PC); /* increment PC */ xct_instr: /* label for API, XCT */ if (hst_lnt) /* history? */ cpu_inst_hist (MA, IR); if (ion_defer) /* count down defer */ ion_defer = ion_defer - 1; if (sim_interval) sim_interval = sim_interval - 1; #if defined (PDP15) /* PDP15 */ if (memm) /* bank mode dir addr */ MA = (MA & B_EPCMASK) | (IR & B_DAMASK); else MA = (MA & P_EPCMASK) | (IR & P_DAMASK); /* page mode dir addr */ #else /* others */ MA = (MA & B_EPCMASK) | (IR & B_DAMASK); /* bank mode only */ #endif switch ((IR >> 13) & 037) { /* decode IR<0:4> */ /* LAC: opcode 20 */ case 011: /* LAC, indir */ if (Ia (MA, &MA, 0)) break; case 010: /* LAC, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; LAC = (LAC & LINK) | MB; break; /* DAC: opcode 04 */ case 003: /* DAC, indir */ if (Ia (MA, &MA, 0)) break; case 002: /* DAC, dir */ INDEX (IR, MA); Write (MA, LAC & DMASK, WR); break; /* DZM: opcode 14 */ case 007: /* DZM, indir */ if (Ia (MA, &MA, 0)) break; case 006: /* DZM, direct */ INDEX (IR, MA); Write (MA, 0, WR); break; /* AND: opcode 50 */ case 025: /* AND, ind */ if (Ia (MA, &MA, 0)) break; case 024: /* AND, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; LAC = LAC & (MB | LINK); break; /* XOR: opcode 24 */ case 013: /* XOR, ind */ if (Ia (MA, &MA, 0)) break; case 012: /* XOR, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; LAC = LAC ^ MB; break; /* ADD: opcode 30 */ case 015: /* ADD, indir */ if (Ia (MA, &MA, 0)) break; case 014: /* ADD, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; t = (LAC & DMASK) + MB; if (t > DMASK) /* end around carry */ t = (t + 1) & DMASK; if (((~LAC ^ MB) & (LAC ^ t)) & SIGN) /* overflow? */ LAC = LINK | t; /* set link */ else LAC = (LAC & LINK) | t; break; /* TAD: opcode 34 */ case 017: /* TAD, indir */ if (Ia (MA, &MA, 0)) break; case 016: /* TAD, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; LAC = (LAC + MB) & LACMASK; break; /* ISZ: opcode 44 */ case 023: /* ISZ, indir */ if (Ia (MA, &MA, 0)) break; case 022: /* ISZ, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; MB = (MB + 1) & DMASK; if (Write (MA, MB, WR)) break; if (MB == 0) PC = Incr_addr (PC); break; /* SAD: opcode 54 */ case 027: /* SAD, indir */ if (Ia (MA, &MA, 0)) break; case 026: /* SAD, dir */ INDEX (IR, MA); if (Read (MA, &MB, RD)) break; if ((LAC & DMASK) != MB) PC = Incr_addr (PC); break; /* XCT: opcode 40 */ case 021: /* XCT, indir */ if (Ia (MA, &MA, 0)) break; case 020: /* XCT, dir */ INDEX (IR, MA); if ((api_usmd | usmd) && (xct_count != 0)) { /* chained and usmd? */ if (usmd) /* trap if usmd */ prvn = trap_pending = 1; break; /* nop if api_usmd */ } if (xct_count >= xct_max) { /* too many XCT's? */ reason = STOP_XCT; break; } xct_count = xct_count + 1; /* count XCT's */ #if defined (PDP9) ion_defer = 1; /* defer intr */ #endif if (Read (MA, &IR, FE)) /* fetch inst, mm err? */ break; goto xct_instr; /* go execute */ /* CAL: opcode 00 - api_usmd records whether usmd = 1 at start of API cycle On the PDP-4 and PDP-7, CAL (I) is exactly the same as JMS (I) 20 On the PDP-9 and PDP-15, CAL clears user mode On the PDP-9 and PDP-15 with API, CAL activates level 4 On the PDP-15, CAL goes to absolute 20, regardless of mode */ case 001: case 000: /* CAL */ t = usmd; /* save user mode */ #if defined (PDP15) /* PDP15 */ MA = 020; /* MA = abs 20 */ ion_defer = 1; /* "free instruction" */ #else /* others */ if (memm) /* if ext, abs 20 */ MA = 020; else MA = (PC & B_EPCMASK) | 020; /* else bank-rel 20 */ #endif #if defined (PDP9) || defined (PDP15) usmd = usmd_buf = 0; /* clear user mode */ if ((cpu_unit.flags & UNIT_NOAPI) == 0) { /* if API, act lvl 4 */ #if defined (PDP15) /* PDP15: if 0-3 inactive */ if ((api_act & (API_ML0|API_ML1|API_ML2|API_ML3)) == 0) #endif api_act = api_act | API_ML4; api_int = api_eval (&int_pend); } #endif if (IR & I_IND) { /* indirect? */ if (Ia (MA, &MA, 0)) break; } PCQ_ENTRY; MB = Jms_word (api_usmd | t); /* save state */ Write (MA, MB, WR); PC = Incr_addr (MA); break; /* JMS: opcode 010 - api_usmd records whether usmd = 1 at start of API cycle */ case 005: /* JMS, indir */ if (Ia (MA, &MA, 0)) break; case 004: /* JMS, dir */ INDEX (IR, MA); PCQ_ENTRY; #if defined (PDP15) /* PDP15 */ if (!usmd) /* "free instruction" */ ion_defer = 1; #endif MB = Jms_word (api_usmd | usmd); /* save state */ if (Write (MA, MB, WR)) break; PC = Incr_addr (MA) & AMASK; break; /* JMP: opcode 60 */ case 031: /* JMP, indir */ if (Ia (MA, &MA, 1)) break; INDEX (IR, MA); PCQ_ENTRY; /* save old PC */ PC = MA & AMASK; break; /* JMP direct - check for idle */ case 030: /* JMP, dir */ INDEX (IR, MA); PCQ_ENTRY; /* save old PC */ if (sim_idle_enab) { /* idling enabled? */ t_bool iof = (ion_inh != 0) || /* IOF if inhibited */ ((ion == 0) && (api_enb == 0)); /* or PI and api off */ if (((MA ^ (PC - 2)) & AMASK) == 0) { /* 1) JMP *-1? */ if (iof && (last_IR == OP_KSF) && /* iof, prv KSF, */ !TST_INT (TTI)) /* no TTI flag? */ sim_idle (0, FALSE); /* we're idle */ } else if (((MA ^ (PC - 1)) & AMASK) == 0) { /* 2) JMP *? */ if (iof) /* iof? inf loop */ reason = STOP_LOOP; else sim_idle (0, FALSE); /* ion? idle */ } } /* end idle */ PC = MA & AMASK; break; /* OPR: opcode 74 */ case 037: /* OPR, indir */ LAC = (LAC & LINK) | IR; /* LAW */ break; case 036: /* OPR, dir */ skp = 0; /* assume no skip */ switch ((IR >> 6) & 017) { /* decode IR<8:11> */ case 0: /* nop */ break; case 1: /* SMA */ if ((LAC & SIGN) != 0) skp = 1; break; case 2: /* SZA */ if ((LAC & DMASK) == 0) skp = 1; break; case 3: /* SZA | SMA */ if (((LAC & DMASK) == 0) || ((LAC & SIGN) != 0)) skp = 1; break; case 4: /* SNL */ if (LAC >= LINK) skp = 1; break; case 5: /* SNL | SMA */ if (LAC >= SIGN) skp = 1; break; case 6: /* SNL | SZA */ if ((LAC >= LINK) || (LAC == 0)) skp = 1; break; case 7: /* SNL | SZA | SMA */ if ((LAC >= SIGN) || (LAC == 0)) skp = 1; break; case 010: /* SKP */ skp = 1; break; case 011: /* SPA */ if ((LAC & SIGN) == 0) skp = 1; break; case 012: /* SNA */ if ((LAC & DMASK) != 0) skp = 1; break; case 013: /* SNA & SPA */ if (((LAC & DMASK) != 0) && ((LAC & SIGN) == 0)) skp = 1; break; case 014: /* SZL */ if (LAC < LINK) skp = 1; break; case 015: /* SZL & SPA */ if (LAC < SIGN) skp = 1; break; case 016: /* SZL & SNA */ if ((LAC < LINK) && (LAC != 0)) skp = 1; break; case 017: /* SZL & SNA & SPA */ if ((LAC < SIGN) && (LAC != 0)) skp = 1; break; } /* end switch skips */ switch (((IR >> 9) & 014) | (IR & 03)) { /* IR<5:6,16:17> */ case 0: /* NOP */ break; case 1: /* CMA */ LAC = LAC ^ DMASK; break; case 2: /* CML */ LAC = LAC ^ LINK; break; case 3: /* CML CMA */ LAC = LAC ^ LACMASK; break; case 4: /* CLL */ LAC = LAC & DMASK; break; case 5: /* CLL CMA */ LAC = (LAC & DMASK) ^ DMASK; break; case 6: /* CLL CML = STL */ LAC = LAC | LINK; break; case 7: /* CLL CML CMA */ LAC = (LAC | LINK) ^ DMASK; break; case 010: /* CLA */ LAC = LAC & LINK; break; case 011: /* CLA CMA = STA */ LAC = LAC | DMASK; break; case 012: /* CLA CML */ LAC = (LAC & LINK) ^ LINK; break; case 013: /* CLA CML CMA */ LAC = (LAC | DMASK) ^ LINK; break; case 014: /* CLA CLL */ LAC = 0; break; case 015: /* CLA CLL CMA */ LAC = DMASK; break; case 016: /* CLA CLL CML */ LAC = LINK; break; case 017: /* CLA CLL CML CMA */ LAC = LACMASK; break; } /* end decode */ if (IR & 0000004) { /* OAS */ #if defined (PDP9) || defined (PDP15) if (usmd) /* trap if usmd */ prvn = trap_pending = 1; else if (!api_usmd) /* nop if api_usmd */ #endif LAC = LAC | SR; } switch (((IR >> 8) & 04) | ((IR >> 3) & 03)) { /* decode IR<7,13:14> */ case 1: /* RAL */ LAC = ((LAC << 1) | (LAC >> 18)) & LACMASK; break; case 2: /* RAR */ LAC = ((LAC >> 1) | (LAC << 18)) & LACMASK; break; case 3: /* RAL RAR */ #if defined (PDP15) /* PDP-15 */ LAC = (LAC + 1) & LACMASK; /* IAC */ #else /* PDP-4,-7,-9 */ reason = stop_inst; /* undefined */ #endif break; case 5: /* RTL */ LAC = ((LAC << 2) | (LAC >> 17)) & LACMASK; break; case 6: /* RTR */ LAC = ((LAC >> 2) | (LAC << 17)) & LACMASK; break; case 7: /* RTL RTR */ #if defined (PDP15) /* PDP-15 */ LAC = ((LAC >> 9) & 0777) | ((LAC & 0777) << 9) | (LAC & LINK); /* BSW */ #else /* PDP-4,-7,-9 */ reason = stop_inst; /* undefined */ #endif break; } /* end switch rotate */ if (IR & 0000040) { /* HLT */ if (usmd) /* trap if usmd */ prvn = trap_pending = 1; else if (!api_usmd) /* nop if api_usmd */ reason = STOP_HALT; } if (skp) /* if skip, inc PC */ PC = Incr_addr (PC); break; /* end OPR */ /* EAE: opcode 64 The EAE is microprogrammed to execute variable length signed and unsigned shift, multiply, divide, and normalize. Most commands are controlled by a six bit step counter (SC). In the hardware, the step counter is complemented on load and then counted up to zero; timing guarantees an initial increment, which completes the two's complement load. In the simulator, the SC is loaded normally and then counted down to zero; the read SC command compensates. */ case 033: case 032: /* EAE */ if (cpu_unit.flags & UNIT_NOEAE) /* disabled? */ break; if (IR & 0020000) /* IR<4>? AC0 to L */ LAC = ((LAC << 1) & LINK) | (LAC & DMASK); if (IR & 0010000) /* IR<5>? clear MQ */ MQ = 0; if ((IR & 0004000) && (LAC & SIGN)) /* IR<6> and minus? */ eae_ac_sign = LINK; /* set eae_ac_sign */ else eae_ac_sign = 0; /* if not, unsigned */ if (IR & 0002000) /* IR<7>? or AC */ MQ = (MQ | LAC) & DMASK; else if (eae_ac_sign) /* if not, |AC| */ LAC = LAC ^ DMASK; if (IR & 0001000) /* IR<8>? clear AC */ LAC = LAC & LINK; link_init = LAC & LINK; /* link temporary */ fill = link_init? DMASK: 0; /* fill = link */ esc = IR & 077; /* get eff SC */ switch ((IR >> 6) & 07) { /* case on IR<9:11> */ case 0: /* setup */ if (IR & 04) /* IR<15>? ~MQ */ MQ = MQ ^ DMASK; if (IR & 02) /* IR<16>? or MQ */ LAC = LAC | MQ; if (IR & 01) /* IR<17>? or SC */ LAC = LAC | ((-SC) & 077); break; /* Multiply uses a shift and add algorithm. The PDP-15, unlike prior implementations, factors IR<6> (signed multiply) into the calculation of the result sign. */ case 1: /* multiply */ if (Read (PC, &MB, FE)) /* get next word */ break; PC = Incr_addr (PC); /* increment PC */ if (eae_ac_sign) /* EAE AC sign? ~MQ */ MQ = MQ ^ DMASK; LAC = LAC & DMASK; /* clear link */ SC = esc; /* init SC */ do { /* loop */ if (MQ & 1) /* MQ<17>? add */ LAC = LAC + MB; MQ = (MQ >> 1) | ((LAC & 1) << 17); LAC = LAC >> 1; /* shift AC'MQ right */ SC = (SC - 1) & 077; /* decrement SC */ } while (SC != 0); /* until SC = 0 */ #if defined (PDP15) if ((IR & 0004000) && (eae_ac_sign ^ link_init)) { #else if (eae_ac_sign ^ link_init) { /* result negative? */ #endif LAC = LAC ^ DMASK; MQ = MQ ^ DMASK; } break; /* Divide uses a non-restoring divide. Divide uses a subtract and shift algorithm. The quotient is generated in true form. The PDP-15, unlike prior implementations, factors IR<6> (signed divide) into the calculation of the result sign. */ case 3: /* divide */ if (Read (PC, &MB, FE)) /* get next word */ break; PC = Incr_addr (PC); /* increment PC */ if (eae_ac_sign) /* EAE AC sign? ~MQ */ MQ = MQ ^ DMASK; if ((LAC & DMASK) >= MB) { /* overflow? */ LAC = (LAC - MB) | LINK; /* set link */ break; } LAC = LAC & DMASK; /* clear link */ t = 0; /* init loop */ SC = esc; /* init SC */ do { /* loop */ if (t) LAC = (LAC + MB) & LACMASK; else LAC = (LAC - MB) & LACMASK; t = (LAC >> 18) & 1; /* quotient bit */ if (SC > 1) /* skip if last */ LAC =((LAC << 1) | (MQ >> 17)) & LACMASK; MQ = ((MQ << 1) | (t ^ 1)) & DMASK; /* shift in quo bit */ SC = (SC - 1) & 077; /* decrement SC */ } while (SC != 0); /* until SC = 0 */ if (t) LAC = (LAC + MB) & LACMASK; if (eae_ac_sign) /* sgn rem = sgn divd */ LAC = LAC ^ DMASK; #if defined (PDP15) if ((IR & 0004000) && (eae_ac_sign ^ link_init)) #else if (eae_ac_sign ^ link_init) /* result negative? */ #endif MQ = MQ ^ DMASK; break; /* EAE shifts, whether left or right, fill from the link. If the operand sign has been copied to the link, this provides correct sign extension for one's complement numbers. */ case 4: /* normalize */ #if defined (PDP15) if (!usmd) /* free instructions */ ion_defer = 2; #endif for (SC = esc; ((LAC & SIGN) == ((LAC << 1) & SIGN)); ) { LAC = (LAC << 1) | ((MQ >> 17) & 1); MQ = (MQ << 1) | (link_init >> 18); SC = (SC - 1) & 077; if (SC == 0) break; } LAC = link_init | (LAC & DMASK); /* trim AC, restore L */ MQ = MQ & DMASK; /* trim MQ */ SC = SC & 077; /* trim SC */ break; case 5: /* long right shift */ if (esc < 18) { MQ = ((LAC << (18 - esc)) | (MQ >> esc)) & DMASK; LAC = ((fill << (18 - esc)) | (LAC >> esc)) & LACMASK; } else { if (esc < 36) MQ = ((fill << (36 - esc)) | (LAC >> (esc - 18))) & DMASK; else MQ = fill; LAC = link_init | fill; } SC = 0; /* clear step count */ break; case 6: /* long left shift */ if (esc < 18) { LAC = link_init | (((LAC << esc) | (MQ >> (18 - esc))) & DMASK); MQ = ((MQ << esc) | (fill >> (18 - esc))) & DMASK; } else { if (esc < 36) LAC = link_init | (((MQ << (esc - 18)) | (fill >> (36 - esc))) & DMASK); else LAC = link_init | fill; MQ = fill; } SC = 0; /* clear step count */ break; case 7: /* AC left shift */ if (esc < 18) LAC = link_init | (((LAC << esc) | (fill >> (18 - esc))) & DMASK); else LAC = link_init | fill; SC = 0; /* clear step count */ break; } /* end switch IR */ break; /* end case EAE */ /* PDP-15 index operates: opcode 72 */ case 035: /* index operates */ #if defined (PDP15) t = (IR & 0400)? (IR | 0777000): (IR & 0377); /* sext immediate */ switch ((IR >> 9) & 017) { /* case on IR<5:8> */ case 001: /* PAX */ XR = LAC & DMASK; break; case 002: /* PAL */ LR = LAC & DMASK; break; case 003: /* AAC */ LAC = (LAC & LINK) | ((LAC + t) & DMASK); break; case 004: /* PXA */ LAC = (LAC & LINK) | XR; break; case 005: /* AXS */ XR = (XR + t) & DMASK; if (SEXT (XR) >= SEXT (LR)) PC = Incr_addr (PC); break; case 006: /* PXL */ LR = XR; break; case 010: /* PLA */ LAC = (LAC & LINK) | LR; break; case 011: /* PLX */ XR = LR; break; case 014: /* CLAC */ LAC = LAC & LINK; break; case 015: /* CLX */ XR = 0; break; case 016: /* CLLR */ LR = 0; break; case 017: /* AXR */ XR = (XR + t) & DMASK; break; } /* end switch IR */ break; /* end case */ #endif /* IOT: opcode 70 The 18b PDP's have different definitions of various control IOT's. IOT PDP-4 PDP-7 PDP-9 PDP-15 700002 IOF IOF IOF IOF 700022 undefined undefined undefined ORMM (XVM) 700042 ION ION ION ION 700024 undefined undefined undefined LDMM (XVM) 700062 undefined ITON undefined undefined 701701 undefined undefined MPSK MPSK 701741 undefined undefined MPSNE MPSNE 701702 undefined undefined MPCV MPCV 701722 undefined undefined undefined MPRC (XVM) 701742 undefined undefined MPEU MPEU 701704 undefined undefined MPLD MPLD 701724 undefined undefined undefined MPLR (KT15, XVM) 701744 undefined undefined MPCNE MPCNE 701764 undefined undefined undefined IPFH (XVM) 703201 undefined undefined PFSF PFSF 703301 undefined TTS TTS TTS 703341 undefined SKP7 SKP7 SPCO 703302 undefined CAF CAF CAF 703304 undefined undefined DBK DBK 703344 undefined undefined DBR DBR 705501 undefined undefined SPI SPI 705521 undefined undefined undefined ENB 705502 undefined undefined RPL RPL 705522 undefined undefined undefined INH 705504 undefined undefined ISA ISA 707701 undefined SEM SEM undefined 707741 undefined undefined undefined SKP15 707761 undefined undefined undefined SBA 707702 undefined EEM EEM undefined 707742 undefined EMIR EMIR RES 707762 undefined undefined undefined DBA 707704 undefined LEM LEM undefined 707764 undefined undefined undefined EBA */ case 034: /* IOT */ #if defined (PDP15) if (IR & 0010000) { /* floating point? */ reason = fp15 (IR); /* process */ break; } #endif if ((api_usmd | usmd) && /* user, not XVM UIOT? */ (!XVM || !(MMR & MM_UIOT))) { if (usmd) /* trap if user */ prvn = trap_pending = 1; break; /* nop if api_usmd */ } device = (IR >> 6) & 077; /* device = IR<6:11> */ pulse = IR & 067; /* pulse = IR<12:17> */ if (IR & 0000010) /* clear AC? */ LAC = LAC & LINK; iot_data = LAC & DMASK; /* AC unchanged */ /* PDP-4 system IOT's */ #if defined (PDP4) switch (device) { /* decode IR<6:11> */ case 0: /* CPU and clock */ if (pulse == 002) /* IOF */ ion = 0; else if (pulse == 042) /* ION */ ion = ion_defer = 1; else iot_data = clk (device, pulse, iot_data); break; #endif /* PDP-7 system IOT's */ #if defined (PDP7) switch (device) { /* decode IR<6:11> */ case 0: /* CPU and clock */ if (pulse == 002) /* IOF */ ion = 0; else if (pulse == 042) /* ION */ ion = ion_defer = 1; else if (pulse == 062) /* ITON */ usmd = usmd_buf = ion = ion_defer = 1; else iot_data = clk (device, pulse, iot_data); break; case 033: /* CPU control */ if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); else if (pulse == 002) /* CAF - skip CPU */ reset_all (1); break; case 077: /* extended memory */ if ((pulse == 001) && memm) PC = Incr_addr (PC); else if (pulse == 002) /* EEM */ memm = 1; else if (pulse == 042) /* EMIR */ memm = emir_pending = 1; /* ext on, restore */ else if (pulse == 004) /* LEM */ memm = 0; break; #endif /* PDP-9 system IOT's */ #if defined (PDP9) ion_defer = 1; /* delay interrupts */ usmd_defer = 1; /* defer load user */ switch (device) { /* decode IR<6:11> */ case 000: /* CPU and clock */ if (pulse == 002) /* IOF */ ion = 0; else if (pulse == 042) /* ION */ ion = ion_defer = 1; else iot_data = clk (device, pulse, iot_data); break; case 017: /* mem protection */ if (PROT) { /* enabled? */ if ((pulse == 001) && prvn) /* MPSK */ PC = Incr_addr (PC); else if ((pulse == 041) && nexm) /* MPSNE */ PC = Incr_addr (PC); else if (pulse == 002) /* MPCV */ prvn = 0; else if (pulse == 042) /* MPEU */ usmd_buf = 1; else if (pulse == 004) /* MPLD */ BR = LAC & BRMASK; else if (pulse == 044) /* MPCNE */ nexm = 0; } else reason = stop_inst; break; case 032: /* power fail */ if ((pulse == 001) && (TST_INT (PWRFL))) PC = Incr_addr (PC); break; case 033: /* CPU control */ if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); else if (pulse == 002) { /* CAF */ reset_all (1); /* reset all exc CPU */ cpu_caf (); /* CAF to CPU */ } else if (pulse == 044) /* DBR */ rest_pending = 1; if (((cpu_unit.flags & UNIT_NOAPI) == 0) && (pulse & 004)) { int32 t = api_ffo[api_act & 0377]; api_act = api_act & ~(API_ML0 >> t); } break; case 055: /* API control */ if (cpu_unit.flags & UNIT_NOAPI) reason = stop_inst; else if (pulse == 001) { /* SPI */ if (((LAC & SIGN) && api_enb) || ((LAC & 0377) > api_act)) iot_data = iot_data | IOT_SKP; } else if (pulse == 002) { /* RPL */ iot_data = iot_data | (api_enb << 17) | (api_req << 8) | api_act; } else if (pulse == 004) { /* ISA */ api_enb = (iot_data & SIGN)? 1: 0; api_req = api_req | ((LAC >> 8) & 017); api_act = api_act | (LAC & 0377); } break; case 077: /* extended memory */ if ((pulse == 001) && memm) PC = Incr_addr (PC); else if (pulse == 002) /* EEM */ memm = 1; else if (pulse == 042) /* EMIR */ memm = emir_pending = 1; /* ext on, restore */ else if (pulse == 004) /* LEM */ memm = 0; break; #endif /* PDP-15 system IOT's - includes "re-entrancy ECO" ENB/INH as standard */ #if defined (PDP15) ion_defer = 1; /* delay interrupts */ usmd_defer = 1; /* defer load user */ switch (device) { /* decode IR<6:11> */ case 000: /* CPU and clock */ if (pulse == 002) /* IOF */ ion = 0; else if (pulse == 042) /* ION */ ion = ion_defer = 1; else if (XVM && (pulse == 022)) /* ORMM/RDMM */ iot_data = MMR; else if (XVM && (pulse == 024)) /* LDMM */ MMR = iot_data; else iot_data = clk (device, pulse, iot_data); break; case 017: /* mem protection */ if (PROT) { /* enabled? */ t = XVM? BRMASK_XVM: BRMASK; if ((pulse == 001) && prvn) /* MPSK */ PC = Incr_addr (PC); else if ((pulse == 041) && nexm) /* MPSNE */ PC = Incr_addr (PC); else if (pulse == 002) /* MPCV */ prvn = 0; else if (pulse == 042) /* MPEU */ usmd_buf = 1; else if (XVM && (pulse == 062)) /* RDCLK */ iot_data = clk_task_upd (TRUE); else if (pulse == 004) /* MPLD */ BR = LAC & t; else if (RELOC && (pulse == 024)) /* MPLR */ RR = LAC & t; else if (pulse == 044) /* MPCNE */ nexm = 0; } else reason = stop_inst; break; case 032: /* power fail */ if ((pulse == 001) && (TST_INT (PWRFL))) PC = Incr_addr (PC); break; case 033: /* CPU control */ if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); else if (pulse == 002) { /* CAF */ reset_all (2); /* reset all exc CPU, FP15 */ cpu_caf (); /* CAF to CPU */ } else if (pulse == 044) /* DBR */ rest_pending = 1; if (((cpu_unit.flags & UNIT_NOAPI) == 0) && (pulse & 004)) { int32 t = api_ffo[api_act & 0377]; api_act = api_act & ~(API_ML0 >> t); } break; case 055: /* API control */ if (cpu_unit.flags & UNIT_NOAPI) reason = stop_inst; else if (pulse == 001) { /* SPI */ if (((LAC & SIGN) && api_enb) || ((LAC & 0377) > api_act)) iot_data = iot_data | IOT_SKP; } else if (pulse == 002) { /* RPL */ iot_data = iot_data | (api_enb << 17) | (api_req << 8) | api_act; } else if (pulse == 004) { /* ISA */ api_enb = (iot_data & SIGN)? 1: 0; api_req = api_req | ((LAC >> 8) & 017); api_act = api_act | (LAC & 0377); } else if (pulse == 021) /* ENB */ ion_inh = 0; else if (pulse == 022) /* INH */ ion_inh = 1; break; case 077: /* bank addressing */ if ((pulse == 041) || ((pulse == 061) && memm)) PC = Incr_addr (PC); /* SKP15, SBA */ else if (pulse == 042) /* RES */ rest_pending = 1; else if (pulse == 062) /* DBA */ memm = 0; else if (pulse == 064) /* EBA */ memm = 1; break; #endif /* IOT, continued */ default: /* devices */ if (dev_tab[device]) /* defined? */ iot_data = dev_tab[device] (device, pulse, iot_data); else reason = stop_inst; /* stop on flag */ break; } /* end switch device */ LAC = LAC | (iot_data & DMASK); if (iot_data & IOT_SKP) PC = Incr_addr (PC); if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; api_int = api_eval (&int_pend); /* eval API */ break; /* end case IOT */ } /* end switch opcode */ api_usmd = 0; /* API cycle over */ last_IR = IR; /* save IR for next */ } /* end while */ /* Simulation halted */ iors = upd_iors (); /* get IORS */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* Evaluate API */ int32 api_eval (int32 *pend) { int32 i, hi; *pend = 0; /* assume no intr */ #if defined (PDP15) /* PDP15 only */ if (ion_inh) /* inhibited? */ return 0; #endif for (i = 0; i < API_HLVL+1; i++) { /* any intr? */ if (int_hwre[i]) *pend = 1; } if (api_enb == 0) /* off? no req */ return 0; api_req = api_req & ~(API_ML0|API_ML1|API_ML2|API_ML3); /* clr req<0:3> */ for (i = 0; i < API_HLVL; i++) { /* loop thru levels */ if (int_hwre[i]) /* req on level? */ api_req = api_req | (API_ML0 >> i); /* set api req */ } hi = api_ffo[api_req & 0377]; /* find hi req */ if (hi < api_ffo[api_act & 0377]) return (hi + 1); return 0; } /* Process IORS instruction */ int32 upd_iors (void) { int32 d, p; d = (ion? IOS_ION: 0); /* ION */ for (p = 0; dev_iors[p] != NULL; p++) /* loop thru table */ d = d | dev_iors[p](); /* OR in results */ return d; } #if defined (PDP4) || defined (PDP7) /* Read, write, indirect, increment routines On the PDP-4 and PDP-7, There are autoincrement locations in every field. If a field does not exist, it is impossible to generate an autoincrement reference (all instructions are CAL). Indirect addressing range is determined by extend mode. JMP I with EMIR pending can only clear extend There is no memory protection, nxm reads zero and ignores writes. */ t_stat Read (int32 ma, int32 *dat, int32 cyc) { ma = ma & AMASK; if (MEM_ADDR_OK (ma)) *dat = M[ma] & DMASK; else *dat = 0; return MM_OK; } t_stat Write (int32 ma, int32 dat, int32 cyc) { ma = ma & AMASK; if (MEM_ADDR_OK (ma)) M[ma] = dat & DMASK; return MM_OK; } t_stat Ia (int32 ma, int32 *ea, t_bool jmp) { int32 t; t_stat sta = MM_OK; if ((ma & B_DAMASK & ~07) == 010) { /* autoindex? */ Read (ma, &t, DF); /* add 1 before use */ t = (t + 1) & DMASK; sta = Write (ma, t, DF); } else sta = Read (ma, &t, DF); /* fetch indirect */ if (jmp) { /* jmp i? */ if (emir_pending && (((t >> 16) & 1) == 0)) memm = 0; emir_pending = rest_pending = 0; } if (memm) /* extend? 15b ia */ *ea = t & IAMASK; else *ea = (ma & B_EPCMASK) | (t & B_DAMASK); /* bank-rel ia */ return sta; } int32 Incr_addr (int32 ma) { return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); } int32 Jms_word (int32 t) { return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | ((t & 1) << 15) | (PC & IAMASK)); } #endif #if defined (PDP9) /* Read, write, indirect, increment routines On the PDP-9, The autoincrement registers are in field zero only. Regardless of extend mode, indirect addressing through 00010-00017 will access absolute locations 00010-00017. Indirect addressing range is determined by extend mode. If extend mode is off, and autoincrementing is used, the resolved address is in bank 0 (KG09B maintenance manual). JMP I with EMIR pending can only clear extend JMP I with DBK pending restores L, user mode, extend mode Memory protection is implemented for foreground/background operation. */ t_stat Read (int32 ma, int32 *dat, int32 cyc) { ma = ma & AMASK; if (usmd) { /* user mode? */ if (!MEM_ADDR_OK (ma)) { /* nxm? */ nexm = prvn = trap_pending = 1; /* set flags, trap */ *dat = 0; return MM_ERR; } if ((cyc != DF) && (ma < BR)) { /* boundary viol? */ prvn = trap_pending = 1; /* set flag, trap */ *dat = 0; return MM_ERR; } } if (MEM_ADDR_OK (ma)) /* valid mem? ok */ *dat = M[ma] & DMASK; else { *dat = 0; /* set flag, no trap */ nexm = 1; } return MM_OK; } t_stat Write (int32 ma, int32 dat, int32 cyc) { ma = ma & AMASK; if (usmd) { if (!MEM_ADDR_OK (ma)) { /* nxm? */ nexm = prvn = trap_pending = 1; /* set flags, trap */ return MM_ERR; } if ((cyc != DF) && (ma < BR)) { /* boundary viol? */ prvn = trap_pending = 1; /* set flag, trap */ return MM_ERR; } } if (MEM_ADDR_OK (ma)) /* valid mem? ok */ M[ma] = dat & DMASK; else nexm = 1; /* set flag, no trap */ return MM_OK; } t_stat Ia (int32 ma, int32 *ea, t_bool jmp) { int32 t; t_stat sta = MM_OK; if ((ma & B_DAMASK & ~07) == 010) { /* autoindex? */ ma = ma & 017; /* always in bank 0 */ Read (ma, &t, DF); /* +1 before use */ t = (t + 1) & DMASK; sta = Write (ma, t, DF); } else sta = Read (ma, &t, DF); if (jmp) { /* jmp i? */ if (emir_pending && (((t >> 16) & 1) == 0)) memm = 0; if (rest_pending) { /* restore pending? */ LAC = ((t << 1) & LINK) | (LAC & DMASK); /* restore L */ memm = (t >> 16) & 1; /* restore extend */ usmd = usmd_buf = (t >> 15) & 1; /* restore user */ } emir_pending = rest_pending = 0; } if (memm) /* extend? 15b ia */ *ea = t & IAMASK; else *ea = (ma & B_EPCMASK) | (t & B_DAMASK); /* bank-rel ia */ return sta; } int32 Incr_addr (int32 ma) { return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); } int32 Jms_word (int32 t) { return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | ((t & 1) << 15) | (PC & IAMASK)); } #endif #if defined (PDP15) /* Read, write, indirect, increment routines On the PDP-15, The autoincrement registers are in page zero only. Regardless of bank mode, indirect addressing through 00010-00017 will access absolute locations 00010-00017. Indirect addressing range is determined by autoincrementing. Any indirect can trigger a restore. Memory protection is implemented for foreground/background operation. Read and write mask addresses to 17b except for XVM systems */ t_stat Read (int32 ma, int32 *dat, int32 cyc) { int32 pa; if (usmd) { /* user mode? */ if (XVM) /* XVM relocation? */ pa = RelocXVM (ma, REL_R); else if (RELOC) /* PDP-15 relocation? */ pa = Reloc15 (ma, REL_R); else pa = Prot15 (ma, cyc == FE); /* PDP-15 prot, fetch only */ if (pa < 0) { /* error? */ *dat = 0; return MM_ERR; } } else pa = ma & AMASK; /* no prot or reloc */ if (MEM_ADDR_OK (pa)) /* valid mem? ok */ *dat = M[pa] & DMASK; else { nexm = 1; /* set flag, no trap */ *dat = 0; } return MM_OK; } t_stat Write (int32 ma, int32 dat, int32 cyc) { int32 pa; if (usmd) { /* user mode? */ if (XVM) /* XVM relocation? */ pa = RelocXVM (ma, REL_W); else if (RELOC) /* PDP-15 relocation? */ pa = Reloc15 (ma, REL_W); else pa = Prot15 (ma, cyc != DF); /* PDP-15 prot, !defer */ if (pa < 0) /* error? */ return MM_ERR; } else pa = ma & AMASK; /* no prot or reloc */ if (MEM_ADDR_OK (pa)) /* valid mem? ok */ M[pa] = dat & DMASK; else nexm = 1; /* set flag, no trap */ return MM_OK; } /* XVM will do 18b defers if user_mode and G_Mode != 0 */ t_stat Ia (int32 ma, int32 *ea, t_bool jmp) { int32 gmode, t; int32 damask = memm? B_DAMASK: P_DAMASK; static const int32 g_mask[4] = { MM_G_W0, MM_G_W1, MM_G_W2, MM_G_W3 }; t_stat sta = MM_OK; if ((ma & damask & ~07) == 010) { /* autoincrement? */ ma = ma & 017; /* always in bank 0 */ Read (ma, &t, DF); /* +1 before use */ t = (t + 1) & DMASK; sta = Write (ma, t, DF); } else sta = Read (ma, &t, DF); if (rest_pending) { /* restore pending? */ LAC = ((t << 1) & LINK) | (LAC & DMASK); /* restore L */ memm = (t >> 16) & 1; /* restore bank */ usmd = usmd_buf = (t >> 15) & 1; /* restore user */ emir_pending = rest_pending = 0; } gmode = MM_GETGM (MMR); /* get G_mode */ if (usmd && XVM && gmode) /* XVM user mode? */ *ea = t & g_mask[gmode]; /* mask ia to size */ else if ((ma & damask & ~07) == 010) /* autoindex? */ *ea = t & DMASK; else *ea = (PC & BLKMASK) | (t & IAMASK); /* within 32K */ return sta; } t_stat Incr_addr (int32 ma) { if (memm) return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); return ((ma & P_EPCMASK) | ((ma + 1) & P_DAMASK)); } /* XVM will store all 18b of PC if user mode and G_mode != 0 */ int32 Jms_word (int32 t) { if (usmd && XVM && (MMR & MM_GM)) return PC; return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | ((t & 1) << 15) | (PC & IAMASK)); } /* PDP-15 protection (KM15 option) */ int32 Prot15 (int32 ma, t_bool bndchk) { ma = ma & AMASK; /* 17b addressing */ if (!MEM_ADDR_OK (ma)) { /* nxm? */ nexm = prvn = trap_pending = 1; /* set flags, trap */ return -1; } if (bndchk && (ma < BR)) { /* boundary viol? */ prvn = trap_pending = 1; /* set flag, trap */ return -1; } return ma; /* no relocation */ } /* PDP-15 relocation and protection (KT15 option) */ int32 Reloc15 (int32 ma, int32 rc) { int32 pa; ma = ma & AMASK; /* 17b addressing */ if (ma > (BR | 0377)) { /* boundary viol? */ if (rc != REL_C) /* set flag, trap */ prvn = trap_pending = 1; return -1; } pa = (ma + RR) & AMASK; /* relocate address */ if (!MEM_ADDR_OK (pa)) { /* nxm? */ if (rc != REL_C) /* set flags, trap */ nexm = prvn = trap_pending = 1; return -1; } return pa; } /* XVM relocation and protection option */ int32 RelocXVM (int32 ma, int32 rc) { int32 pa, gmode, slr; static const int32 g_base[4] = { MM_G_B0, MM_G_B1, MM_G_B2, MM_G_B3 }; static const int32 slr_lnt[4] = { MM_SLR_L0, MM_SLR_L1, MM_SLR_L2, MM_SLR_L3 }; gmode = MM_GETGM (MMR); /* get G_mode */ slr = MM_GETSLR (MMR); /* get segment length */ if (MMR & MM_RDIS) /* reloc disabled? */ pa = ma; else if ((MMR & MM_SH) && /* shared enabled and */ (ma >= g_base[gmode]) && /* >= shared base and */ (ma < (g_base[gmode] + slr_lnt[slr]))) { /* < shared end? */ if (ma & 017400) { /* ESAS? */ if ((rc == REL_W) && (MMR & MM_WP)) { /* write and protected? */ prvn = trap_pending = 1; /* set flag, trap */ return -1; } pa = (((MMR & MM_SBR_MASK) << 8) + ma) & DMASK; /* ESAS reloc */ } else pa = RR + (ma & 0377); /* no, ISAS reloc */ } else { if (ma > (BR | 0377)) { /* normal reloc, viol? */ if (rc != REL_C) /* set flag, trap */ prvn = trap_pending = 1; return -1; } pa = (RR + ma) & DMASK; /* relocate address */ } if (!MEM_ADDR_OK (pa)) { /* nxm? */ if (rc != REL_C) /* set flags, trap */ nexm = prvn = trap_pending = 1; return -1; } return pa; } #endif /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { LAC = 0; MQ = 0; SC = 0; eae_ac_sign = 0; ion = ion_defer = ion_inh = 0; CLR_INT (PWRFL); api_enb = api_req = api_act = 0; BR = 0; RR = 0; MMR = 0; usmd = usmd_buf = usmd_defer = 0; memm = memm_init; nexm = prvn = trap_pending = 0; emir_pending = rest_pending = 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; } /* CAF routine (CPU reset isn't called by CAF) */ void cpu_caf (void) { api_enb = api_req = api_act = 0; /* reset API system */ nexm = prvn = trap_pending = 0; /* reset MM system */ usmd = usmd_buf = usmd_defer = 0; MMR = 0; return; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { #if defined (PDP15) if (usmd && (sw & SWMASK ('V'))) { if (XVM) addr = RelocXVM (addr, REL_C); else if (RELOC) addr = Reloc15 (addr, REL_C); if ((int32) addr < 0) return STOP_MME; } #endif 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 defined (PDP15) if (usmd && (sw & SWMASK ('V'))) { if (XVM) addr = RelocXVM (addr, REL_C); else if (RELOC) addr = Reloc15 (addr, REL_C); if ((int32) addr < 0) return STOP_MME; } #endif 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 device number for a device */ t_stat set_devno (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newdev; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ if ((r != SCPE_OK) || (newdev == dibp->dev)) return r; dibp->dev = newdev; /* store */ return SCPE_OK; } /* Show device number for a device */ t_stat show_devno (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "devno=%02o", dibp->dev); if (dibp->num > 1) fprintf (st, "-%2o", dibp->dev + dibp->num - 1); return SCPE_OK; } /* CPU device handler - should never get here! */ int32 bad_dev (int32 dev, int32 pulse, int32 AC) { return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ } /* Build device dispatch table */ t_bool build_dev_tab (void) { DEVICE *dptr; DIB *dibp; uint32 i, j, p; static const uint8 std_dev[] = #if defined (PDP4) { 000 }; #elif defined (PDP7) { 000, 033, 077 }; #else { 000, 017, 033, 055, 077 }; #endif for (i = 0; i < DEV_MAX; i++) { /* clr tables */ dev_tab[i] = NULL; dev_iors[i] = NULL; } for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ dev_tab[std_dev[i]] = &bad_dev; for (i = p = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ if (dibp->iors) /* if IORS, add */ dev_iors[p++] = dibp->iors; for (j = 0; j < dibp->num; j++) { /* loop thru disp */ if (dibp->dsp[j]) { /* any dispatch? */ if (dev_tab[dibp->dev + j]) { /* already filled? */ printf ("%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); if (sim_log) fprintf (sim_log, "%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); return TRUE; } dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ } /* end if dsp */ } /* end for j */ } /* end if enb */ } /* end for i */ return FALSE; } /* 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 l, j, k, di, lnt; char *cptr = (char *) desc; t_value sim_eval[2]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 L AC MQ IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(di++) % hst_lnt]; /* entry pointer */ if (h->pc & HIST_PC) { /* instruction? */ l = (h->lac >> 18) & 1; /* link */ fprintf (st, "%06o %o %06o %06o ", h->pc & AMASK, l, h->lac & DMASK, h->mq); sim_eval[0] = h->ir; sim_eval[1] = h->ir1; if ((fprint_sym (st, h->pc & AMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %06o", h->ir); } /* end else instruction */ else if (h->pc & (HIST_API | HIST_PI)) { /* interrupt event? */ if (h->pc & HIST_PI) /* PI? */ fprintf (st, "%06o PI LVL 0-4 =", h->pc & AMASK); else fprintf (st, "%06o API %d LVL 0-4 =", h->pc & AMASK, h->mq); for (j = API_HLVL; j >= 0; j--) fprintf (st, " %02o", (h->ir >> (j * HIST_V_LVL)) & HIST_M_LVL); } else continue; /* invalid */ fputc ('\n', st); /* end line */ } /* end for */ return SCPE_OK; } /* Record events in history table */ void cpu_inst_hist (int32 addr, int32 inst) { t_value word = 0; hst[hst_p].pc = addr | HIST_PC; hst[hst_p].ir = inst; if (cpu_ex (&word, (addr + 1) & AMASK, &cpu_unit, SWMASK ('V'))) hst[hst_p].ir1 = 0; else hst[hst_p].ir1 = word; hst[hst_p].lac = LAC; hst[hst_p].mq = MQ; hst_p = (hst_p + 1); if (hst_p >= hst_lnt) hst_p = 0; return; } void cpu_intr_hist (int32 flag, int32 lvl) { int32 j; hst[hst_p].pc = PC | flag; hst[hst_p].ir = 0; for (j = 0; j < API_HLVL+1; j++) hst[hst_p].ir = (hst[hst_p].ir << HIST_V_LVL) | (int_hwre[j] & HIST_M_LVL); hst[hst_p].ir1 = 0; hst[hst_p].lac = 0; hst[hst_p].mq = lvl; hst_p = (hst_p + 1); if (hst_p >= hst_lnt) hst_p = 0; return; } simh-3.8.1/AltairZ80/0000755000175000017500000000000011111432566012357 5ustar vlmvlmsimh-3.8.1/AltairZ80/i8272.c0000644000175000017500000013226711111142272013301 0ustar vlmvlm/************************************************************************* * * * $Id: i8272.c 1999 2008-07-22 04:25:28Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Generic Intel 8272 Disk Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ /* Change log: - 19-Apr-2008, Tony Nicholson, added other .IMD formats - 06-Aug-2008, Tony Nicholson, READID should use HDS bit and add support for logical Head and Cylinder maps in the .IMD image file (AGN) */ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" #include "i8272.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define FMT_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define IRQ_MSG (1 << 8) #define I8272_MAX_DRIVES 4 #define I8272_MAX_SECTOR 26 #define I8272_SECTOR_LEN 8192 /* 2^(7 + I8272_MAX_N) == I8272_SECTOR_LEN */ #define I8272_MAX_N 6 #define CMD_PHASE 0 #define EXEC_PHASE 1 #define DATA_PHASE 2 typedef union { uint8 raw[I8272_SECTOR_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; DISK_INFO *imd; uint8 ntracks; /* number of tracks */ uint8 nheads; /* number of heads */ uint32 sectsize; /* sector size, not including pre/postamble */ uint8 track; /* Current Track */ uint8 ready; /* Is drive ready? */ } I8272_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug-n-Play Information */ uint32 fdc_dma_addr;/* DMA Transfer Address */ uint8 fdc_msr; /* 8272 Main Status Register */ uint8 fdc_phase; /* Phase that the 8272 is currently in */ uint8 fdc_srt; /* Step Rate in ms */ uint8 fdc_hut; /* Head Unload Time in ms */ uint8 fdc_hlt; /* Head Load Time in ms */ uint8 fdc_nd; /* Non-DMA Mode 1=Non-DMA, 0=DMA */ uint8 fdc_head; /* H Head Number */ uint8 fdc_sector; /* R Record (Sector) */ uint8 fdc_sec_len; /* N Sector Length, in bytes: 2^(7 + fdc_sec_len), fdc_sec_len <= I8272_MAX_N */ uint8 fdc_eot; /* EOT End of Track (Final sector number of cyl) */ uint8 fdc_gpl; /* GPL Gap3 Length */ uint8 fdc_dtl; /* DTL Data Length */ uint8 fdc_mt; /* Multiple sectors */ uint8 fdc_mfm; /* MFM mode */ uint8 fdc_sk; /* Skip Deleted Data */ uint8 fdc_hds; /* Head Select */ uint8 fdc_fillbyte; /* Fill-byte used for FORMAT TRACK */ uint8 fdc_sc; /* Sector count for FORMAT TRACK */ uint8 fdc_sectorcount; /* Current sector being formatted by FORMAT TRACK */ uint8 fdc_sectormap[I8272_MAX_SECTOR]; /* Physical to logical sector map for FORMAT TRACK */ uint8 fdc_status[3];/* Status Register Bytes */ uint8 fdc_seek_end; /* Seek was executed successfully */ uint8 cmd_index; /* Index of command byte */ uint8 cmd[10]; /* Storage for current command */ uint8 cmd_len; /* FDC Command Length */ uint8 result_index; /* Index of result byte */ uint8 result[10]; /* Result data */ uint8 result_len; /* FDC Result Length */ uint8 sel_drive; /* Currently selected drive */ I8272_DRIVE_INFO drive[I8272_MAX_DRIVES]; } I8272_INFO; static SECTOR_FORMAT sdata; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); /* These are needed for DMA. PIO Mode has not been implemented yet. */ extern void PutByteDMA(const uint32 Addr, const uint32 Value); extern uint8 GetByteDMA(const uint32 Addr); #define UNIT_V_I8272_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_I8272_WLK (1 << UNIT_V_I8272_WLK) #define UNIT_V_I8272_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_I8272_VERBOSE (1 << UNIT_V_I8272_VERBOSE) #define I8272_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ #define I8272_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ /* Intel 8272 Commands */ #define I8272_READ_TRACK 0x02 #define I8272_SPECIFY 0x03 #define I8272_SENSE_DRIVE_STATUS 0x04 #define I8272_WRITE_DATA 0x05 #define I8272_READ_DATA 0x06 #define I8272_RECALIBRATE 0x07 #define I8272_SENSE_INTR_STATUS 0x08 #define I8272_WRITE_DELETED_DATA 0x09 #define I8272_READ_ID 0x0A #define I8272_READ_DELETED_DATA 0x0C #define I8272_FORMAT_TRACK 0x0D #define I8272_SEEK 0x0F #define I8272_SCAN_EQUAL 0x11 #define I8272_SCAN_LOW_EQUAL 0x19 #define I8272_SCAN_HIGH_EQUAL 0x1D /* SENSE DRIVE STATUS bit definitions */ #define DRIVE_STATUS_TWO_SIDED 0x08 #define DRIVE_STATUS_TRACK0 0x10 #define DRIVE_STATUS_READY 0x20 #define DRIVE_STATUS_WP 0x40 #define DRIVE_STATUS_FAULT 0x80 static void raise_i8272_interrupt(void); static int32 i8272dev(const int32 port, const int32 io, const int32 data); static t_stat i8272_reset(DEVICE *dptr); int32 find_unit_index (UNIT *uptr); I8272_INFO i8272_info_data = { { 0x0, 0, 0xC0, 2 } }; I8272_INFO *i8272_info = &i8272_info_data; uint8 i8272_irq = 1; static UNIT i8272_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) } }; static MTAB i8272_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_I8272_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_I8272_WLK, UNIT_I8272_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_I8272_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_I8272_VERBOSE, UNIT_I8272_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(i8272_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB i8272_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "FMT", FMT_MSG }, { "VERBOSE",VERBOSE_MSG }, { "IRQ", IRQ_MSG }, { NULL, 0 } }; DEVICE i8272_dev = { "I8272", i8272_unit, NULL, i8272_mod, I8272_MAX_DRIVES, 10, 31, 1, I8272_MAX_DRIVES, I8272_MAX_DRIVES, NULL, NULL, &i8272_reset, NULL, &i8272_attach, &i8272_detach, &i8272_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, i8272_dt, NULL, "Intel/NEC(765) FDC Core I8272" }; static uint8 I8272_Setup_Cmd(uint8 fdc_cmd); /* Reset routine */ static t_stat i8272_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &i8272dev, TRUE); } else { /* Connect I/O Ports at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &i8272dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } /* find_unit_index find index of a unit Inputs: uptr = pointer to unit Outputs: result = index of device */ int32 find_unit_index (UNIT *uptr) { DEVICE *dptr; uint32 i; if (uptr == NULL) return (-1); dptr = find_dev_from_unit(uptr); for(i=0; inumunits; i++) { if(dptr->units + i == uptr) { break; } } if(i == dptr->numunits) { return (-1); } return (i); } /* Attach routine */ t_stat i8272_attach(UNIT *uptr, char *cptr) { char header[4]; t_stat r; int32 i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ uptr->capac = sim_fsize(uptr->fileref); i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } DBG_PRINT(("Attach I8272%d\n", i)); i8272_info->drive[i].uptr = uptr; /* Default to drive not ready */ i8272_info->drive[i].ready = 0; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(strncmp(header, "IMD", 3)) { printf("I8272: Only IMD disk images are supported\n"); i8272_info->drive[i].uptr = NULL; return SCPE_OPENERR; } } else { /* create a disk image file in IMD format. */ if (diskCreate(uptr->fileref, "$Id: i8272.c 1999 2008-07-22 04:25:28Z hharte $") != SCPE_OK) { printf("I8272: Failed to create IMD disk.\n"); i8272_info->drive[i].uptr = NULL; return SCPE_OPENERR; } uptr->capac = sim_fsize(uptr->fileref); } uptr->u3 = IMAGE_TYPE_IMD; if (uptr->flags & UNIT_I8272_VERBOSE) { printf("I8272%d: attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); } if(uptr->u3 == IMAGE_TYPE_IMD) { if (uptr->flags & UNIT_I8272_VERBOSE) printf("--------------------------------------------------------\n"); i8272_info->drive[i].imd = diskOpen(uptr->fileref, uptr->flags & UNIT_I8272_VERBOSE); if (uptr->flags & UNIT_I8272_VERBOSE) printf("\n"); if (i8272_info->drive[i].imd == NULL) { printf("I8272: IMD disk corrupt.\n"); i8272_info->drive[i].uptr = NULL; return SCPE_OPENERR; } i8272_info->drive[i].ready = 1; } else { i8272_info->drive[i].imd = NULL; } return SCPE_OK; } /* Detach routine */ t_stat i8272_detach(UNIT *uptr) { t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } DBG_PRINT(("Detach I8272%d\n", i)); r = diskClose(&i8272_info->drive[i].imd); i8272_info->drive[i].ready = 0; if (r != SCPE_OK) return r; r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) return r; return SCPE_OK; } static int32 i8272dev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("I8272: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP, PCX, io ? "OUT" : " IN", port, data)); if(io) { I8272_Write(port, data); return 0; } else { return(I8272_Read(port)); } } uint8 I8272_Set_DMA(const uint32 dma_addr) { i8272_info->fdc_dma_addr = dma_addr & 0xFFFFFF; return 0; } extern uint8 floorlog2(unsigned int n); #define I8272_MSR_RQM (1 << 7) #define I8272_MSR_DATA_OUT (1 << 6) #define I8272_MSR_NON_DMA (1 << 5) #define I8272_MSR_FDC_BUSY (1 << 4) uint8 I8272_Read(const uint32 Addr) { uint8 cData; I8272_DRIVE_INFO *pDrive; pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } cData = 0x00; switch(Addr & 0x3) { case I8272_FDC_MSR: cData = i8272_info->fdc_msr | I8272_MSR_RQM; if(i8272_info->fdc_phase == CMD_PHASE) { cData &= ~I8272_MSR_DATA_OUT; } else { cData |= I8272_MSR_DATA_OUT; } #if 0 if(i8272_info->fdc_phase == EXEC_PHASE) { cData |= I8272_MSR_FDC_BUSY; } else { cData |= ~I8272_MSR_FDC_BUSY; } #endif /* 0 hharte */ TRACE_PRINT(STATUS_MSG, ("I8272: " ADDRESS_FORMAT " RD FDC MSR = 0x%02x" NLP, PCX, cData)); break; case I8272_FDC_DATA: if(i8272_info->fdc_phase == DATA_PHASE) { cData = i8272_info->result[i8272_info->result_index]; TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " RD Data, phase=%d, [%d]=0x%02x" NLP, PCX, i8272_info->fdc_phase, i8272_info->result_index, cData)); i8272_irq = 0; i8272_info->result_index ++; if(i8272_info->result_index == i8272_info->result_len) { TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " result phase complete." NLP, PCX)); i8272_info->fdc_phase = CMD_PHASE; } } else { cData = i8272_info->result[0]; /* hack, in theory any value should be ok but this makes "format" work */ TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " error, reading data register when not in data phase. " "Returning 0x%02x" NLP, PCX, cData)); } break; default: TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " Cannot read register %x" NLP, PCX, Addr)); cData = 0xFF; } return (cData); } static char *messages[0x20] = { /* 0 1 2 3 */ "Undefined Command 0x0","Undefined Command 0x1","Read Track", "Specify", /* 4 5 6 7 */ "Sense Drive Status", "Write Data", "Read Data", "Recalibrate", /* 8 9 A B */ "Sense Interrupt Status", "Write Deleted Data", "Read ID", "Undefined Command 0xB", /* C D E F */ "Read Deleted Data", "Format Track", "Undefined Command 0xE","Seek", /* 10 11 12 13 */ "Undefined Command 0x10","Scan Equal", "Undefined Command 0x12","Undefined Command 0x13", /* 14 15 16 17 */ "Undefined Command 0x14","Undefined Command 0x15","Undefined Command 0x16","Undefined Command 0x17", /* 18 19 1A 1B */ "Undefined Command 0x18","Scan Low Equal", "Undefined Command 0x1A","Undefined Command 0x1B", /* 1C 1D 1E 1F */ "Undefined Command 0x1C","Scan High Equal", "Undefined Command 0x1E","Undefined Command 0x1F" }; uint8 I8272_Write(const uint32 Addr, uint8 cData) { I8272_DRIVE_INFO *pDrive; unsigned int flags = 0; unsigned int readlen; uint8 disk_read = 0; int32 i; pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } switch(Addr & 0x3) { case I8272_FDC_MSR: TRACE_PRINT(WR_DATA_MSG, ("I8272: " ADDRESS_FORMAT " WR Drive Select Reg=%02x" NLP, PCX, cData)); break; case I8272_FDC_DATA: i8272_info->fdc_msr &= 0xF0; TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " WR Data, phase=%d, index=%d" NLP, PCX, i8272_info->fdc_phase, i8272_info->cmd_index)); if(i8272_info->fdc_phase == CMD_PHASE) { i8272_info->cmd[i8272_info->cmd_index] = cData; if(i8272_info->cmd_index == 0) { TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " CMD=0x%02x[%s]" NLP, PCX, cData & 0x1F, messages[cData & 0x1F])); I8272_Setup_Cmd(cData & 0x1F); } i8272_info->cmd_index ++; if(i8272_info->cmd_len == i8272_info->cmd_index) { i8272_info->cmd_index = 0; i8272_info->fdc_phase = EXEC_PHASE; } } if(i8272_info->fdc_phase == EXEC_PHASE) { switch(i8272_info->cmd[0] & 0x1F) { case I8272_READ_DATA: case I8272_WRITE_DATA: case I8272_READ_DELETED_DATA: case I8272_WRITE_DELETED_DATA: case I8272_READ_TRACK: case I8272_SCAN_LOW_EQUAL: case I8272_SCAN_HIGH_EQUAL: case I8272_SCAN_EQUAL: i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7; i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5; i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } if(pDrive->track != i8272_info->cmd[2]) { i8272_info->fdc_seek_end = 1; } else { i8272_info->fdc_seek_end = 0; } if(pDrive->track != i8272_info->cmd[2]) { TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " ERROR: CMD=0x%02x[%s]: Drive: %d, Command wants track %d, but positioner is on track %d." NLP, PCX, i8272_info->cmd[0] & 0x1F, messages[i8272_info->cmd[0] & 0x1F], i8272_info->sel_drive, i8272_info->cmd[2], pDrive->track)); } pDrive->track = i8272_info->cmd[2]; i8272_info->fdc_head = i8272_info->cmd[3] & 1; /* AGN mask to head 0 or 1 */ i8272_info->fdc_sector = i8272_info->cmd[4]; i8272_info->fdc_sec_len = i8272_info->cmd[5]; if(i8272_info->fdc_sec_len > I8272_MAX_N) { TRACE_PRINT(ERROR_MSG, ("I8272: " ADDRESS_FORMAT " Illegal sector size %d [N=%d]. Reset to %d [N=%d]." NLP, PCX, 128 << i8272_info->fdc_sec_len, i8272_info->fdc_sec_len, 128 << I8272_MAX_N, I8272_MAX_N)); i8272_info->fdc_sec_len = I8272_MAX_N; } i8272_info->fdc_eot = i8272_info->cmd[6]; i8272_info->fdc_gpl = i8272_info->cmd[7]; i8272_info->fdc_dtl = i8272_info->cmd[8]; TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " CMD=0x%02x[%s]: Drive: %d, %s %s, C=%d. H=%d. S=%d, N=%d, EOT=%02x, GPL=%02x, DTL=%02x" NLP, PCX, i8272_info->cmd[0] & 0x1F, messages[i8272_info->cmd[0] & 0x1F], i8272_info->sel_drive, i8272_info->fdc_mt ? "Multi" : "Single", i8272_info->fdc_mfm ? "MFM" : "FM", pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sector, i8272_info->fdc_sec_len, i8272_info->fdc_eot, i8272_info->fdc_gpl, i8272_info->fdc_dtl)); i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03); i8272_info->fdc_status[0] |= 0x40; i8272_info->fdc_status[1] = 0; i8272_info->fdc_status[2] = 0; i8272_info->result[0] = i8272_info->fdc_status[0]; i8272_info->result[1] = i8272_info->fdc_status[1]; i8272_info->result[2] = i8272_info->fdc_status[2]; i8272_info->result[3] = pDrive->imd->track[pDrive->track][i8272_info->fdc_head].logicalCyl[i8272_info->fdc_sector]; /* AGN logicalCyl */ i8272_info->result[4] = pDrive->imd->track[pDrive->track][i8272_info->fdc_head].logicalHead[i8272_info->fdc_sector]; /* AGN logicalHead */ i8272_info->result[5] = i8272_info->fdc_sector; i8272_info->result[6] = i8272_info->fdc_sec_len; break; case I8272_READ_ID: /* READ ID */ i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } /* Compute the i8272 "N" value from the sectorsize of this */ /* disk's current track - i.e. N = log2(sectsize) - log2(128) */ /* The calculation also works for non-standard format disk images with */ /* sectorsizes of 2048, 4096 and 8192 bytes */ i8272_info->fdc_sec_len = floorlog2( pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].sectsize) - 7; /* AGN fix to use fdc_hds (was fdc_head)*/ /* For now always return the starting sector number */ /* but could return (say) a valid sector number based */ /* on elapsed time for a more "realistic" simulation. */ /* This would allow disk analysis programs that use */ /* READID to detect non-standard disk formats. */ i8272_info->fdc_sector = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].start_sector; if((i8272_info->fdc_sec_len == 0xF8) || (i8272_info->fdc_sec_len > I8272_MAX_N)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("I8272: " ADDRESS_FORMAT " Illegal sector size N=%d. Reset to 0." NLP, PCX, i8272_info->fdc_sec_len)); i8272_info->fdc_sec_len = 0; return 0xFF; } i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03); i8272_info->fdc_status[1] = 0; i8272_info->fdc_status[2] = 0; i8272_info->result[0] = i8272_info->fdc_status[0]; i8272_info->result[1] = i8272_info->fdc_status[1]; i8272_info->result[2] = i8272_info->fdc_status[2]; i8272_info->result[3] = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].logicalCyl[i8272_info->fdc_sector]; /* AGN logicalCyl */ i8272_info->result[4] = pDrive->imd->track[pDrive->track][i8272_info->fdc_hds].logicalHead[i8272_info->fdc_sector]; /* AGN logicalHead */ i8272_info->result[5] = i8272_info->fdc_sector; i8272_info->result[6] = i8272_info->fdc_sec_len; break; case I8272_RECALIBRATE: /* RECALIBRATE */ i8272_info->sel_drive = i8272_info->cmd[1] & 0x03; pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } pDrive->track = 0; i8272_info->fdc_phase = CMD_PHASE; /* No result phase */ i8272_info->fdc_seek_end = 1; TRACE_PRINT(SEEK_MSG, ("I8272: " ADDRESS_FORMAT " Recalibrate: Drive 0x%02x" NLP, PCX, i8272_info->sel_drive)); break; case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; i8272_info->fdc_head = i8272_info->fdc_hds; i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } if(pDrive->track != i8272_info->cmd[2]) { i8272_info->fdc_seek_end = 1; } else { i8272_info->fdc_seek_end = 0; } i8272_info->fdc_sec_len = i8272_info->cmd[2]; if(i8272_info->fdc_sec_len > I8272_MAX_N) { TRACE_PRINT(ERROR_MSG, ("I8272: " ADDRESS_FORMAT " Illegal sector size %d [N=%d]. Reset to %d [N=%d]." NLP, PCX, 128 << i8272_info->fdc_sec_len, i8272_info->fdc_sec_len, 128 << I8272_MAX_N, I8272_MAX_N)); i8272_info->fdc_sec_len = I8272_MAX_N; } i8272_info->fdc_sc = i8272_info->cmd[3]; i8272_info->fdc_gpl = i8272_info->cmd[4]; i8272_info->fdc_fillbyte = i8272_info->cmd[5]; TRACE_PRINT(FMT_MSG, ("I8272: " ADDRESS_FORMAT " Format Drive: %d, %s, C=%d. H=%d. N=%d, SC=%d, GPL=%02x, FILL=%02x" NLP, PCX, i8272_info->sel_drive, i8272_info->fdc_mfm ? "MFM" : "FM", pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sec_len, i8272_info->fdc_sc, i8272_info->fdc_gpl, i8272_info->fdc_fillbyte)); i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 0x03); i8272_info->fdc_status[1] = 0; i8272_info->fdc_status[2] = 0; i8272_info->fdc_sectorcount = 0; i8272_info->result[0] = i8272_info->fdc_status[0]; i8272_info->result[1] = i8272_info->fdc_status[1]; i8272_info->result[2] = i8272_info->fdc_status[2]; i8272_info->result[3] = pDrive->track; i8272_info->result[4] = i8272_info->fdc_head; /* AGN for now we cannot format with logicalHead */ i8272_info->result[5] = i8272_info->fdc_sector; /* AGN ditto for logicalCyl */ i8272_info->result[6] = i8272_info->fdc_sec_len; break; case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */ TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Sense Interrupt Status" NLP, PCX)); i8272_info->result[0] = i8272_info->fdc_seek_end ? 0x20 : 0x00; /* SEEK_END */ i8272_info->result[0] |= i8272_info->sel_drive; i8272_info->result[1] = pDrive->track; i8272_irq = 0; break; case I8272_SPECIFY: /* SPECIFY */ i8272_info->fdc_srt = 16 - ((i8272_info->cmd[1] & 0xF0) >> 4); i8272_info->fdc_hut = (i8272_info->cmd[1] & 0x0F) * 16; i8272_info->fdc_hlt = ((i8272_info->cmd[2] & 0xFE) >> 1) * 2; i8272_info->fdc_nd = (i8272_info->cmd[2] & 0x01); i8272_info->fdc_phase = CMD_PHASE; /* No result phase */ TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Specify: SRT=%d, HUT=%d, HLT=%d, ND=%s" NLP, PCX, i8272_info->fdc_srt, i8272_info->fdc_hut, i8272_info->fdc_hlt, i8272_info->fdc_nd ? "NON-DMA" : "DMA")); break; case I8272_SENSE_DRIVE_STATUS: /* Setup Status3 Byte */ i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } i8272_info->result[0] = (pDrive->ready) ? DRIVE_STATUS_READY : 0; /* Drive Ready */ if(imdGetSides(pDrive->imd) == 2) { i8272_info->result[0] |= DRIVE_STATUS_TWO_SIDED; /* Two-sided? */ } if(imdIsWriteLocked(pDrive->imd)) { i8272_info->result[0] |= DRIVE_STATUS_WP; /* Write Protected? */ } i8272_info->result[0] |= (i8272_info->fdc_hds & 1) << 2; i8272_info->result[0] |= (i8272_info->sel_drive & 0x03); i8272_info->result[0] |= (pDrive->track == 0) ? DRIVE_STATUS_TRACK0 : 0x00; /* Track 0 */ TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Sense Drive Status = 0x%02x" NLP, PCX, i8272_info->result[0])); break; case I8272_SEEK: /* SEEK */ i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7; i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5; i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); pDrive = &i8272_info->drive[i8272_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } pDrive->track = i8272_info->cmd[2]; i8272_info->fdc_head = i8272_info->fdc_hds; /*AGN seek should save the head */ i8272_info->fdc_seek_end = 1; TRACE_PRINT(SEEK_MSG, ("I8272: " ADDRESS_FORMAT " Seek Drive: %d, %s %s, C=%d. Skip Deleted Data=%s Head Select=%s" NLP, PCX, i8272_info->sel_drive, i8272_info->fdc_mt ? "Multi" : "Single", i8272_info->fdc_mfm ? "MFM" : "FM", i8272_info->cmd[2], i8272_info->fdc_sk ? "True" : "False", i8272_info->fdc_hds ? "True" : "False")); break; default: /* INVALID */ break; } if(i8272_info->fdc_phase == EXEC_PHASE) { switch(i8272_info->cmd[0] & 0x1F) { case I8272_READ_TRACK: printf("I8272: " ADDRESS_FORMAT " Read a track (untested.)" NLP, PCX); i8272_info->fdc_sector = 1; /* Read entire track from sector 1...eot */ case I8272_READ_DATA: case I8272_READ_DELETED_DATA: disk_read = 1; case I8272_WRITE_DATA: case I8272_WRITE_DELETED_DATA: for(;i8272_info->fdc_sector<=i8272_info->fdc_eot;i8272_info->fdc_sector++) { TRACE_PRINT(RD_DATA_MSG, ("I8272: " ADDRESS_FORMAT " %s Data, sector: %d sector len=%d" NLP, PCX, disk_read ? "RD" : "WR", i8272_info->fdc_sector, 128 << i8272_info->fdc_sec_len)); if(pDrive->imd == NULL) { printf(".imd is NULL!" NLP); } if(disk_read) { /* Read sector */ sectRead(pDrive->imd, pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sector, sdata.raw, 128 << i8272_info->fdc_sec_len, &flags, &readlen); for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) { PutByteDMA(i8272_info->fdc_dma_addr, sdata.raw[i]); i8272_info->fdc_dma_addr++; } TRACE_PRINT(RD_DATA_MSG, ("I8272: " ADDRESS_FORMAT " T:%d/H:%d/S:%d/L:%4d: Data transferred to RAM at 0x%06x" NLP, PCX, pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sector, 128 << i8272_info->fdc_sec_len, i8272_info->fdc_dma_addr - i)); } else { /* Write */ for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) { sdata.raw[i] = GetByteDMA(i8272_info->fdc_dma_addr); i8272_info->fdc_dma_addr++; } TRACE_PRINT(WR_DATA_MSG, ("I8272: " ADDRESS_FORMAT " Data transferred from RAM at 0x%06x" NLP, PCX, i8272_info->fdc_dma_addr)); sectWrite(pDrive->imd, pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sector, sdata.raw, 128 << i8272_info->fdc_sec_len, &flags, &readlen); } i8272_info->result[5] = i8272_info->fdc_sector; i8272_info->result[1] = 0x80; } break; case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ for(i8272_info->fdc_sector = 1;i8272_info->fdc_sector<=i8272_info->fdc_sc;i8272_info->fdc_sector++) { TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Format Track %d, Sector=%d, len=%d" NLP, PCX, pDrive->track, i8272_info->fdc_sector, 128 << i8272_info->fdc_sec_len)); if(i8272_info->fdc_sectorcount >= I8272_MAX_SECTOR) { TRACE_PRINT(ERROR_MSG, ("I8272: " ADDRESS_FORMAT " Illegal sector count" NLP, PCX)); i8272_info->fdc_sectorcount = 0; } i8272_info->fdc_sectormap[i8272_info->fdc_sectorcount] = i8272_info->fdc_sector; i8272_info->fdc_sectorcount++; if(i8272_info->fdc_sectorcount == i8272_info->fdc_sc) { trackWrite(pDrive->imd, pDrive->track, i8272_info->fdc_head, i8272_info->fdc_sc, 128 << i8272_info->fdc_sec_len, i8272_info->fdc_sectormap, i8272_info->fdc_mfm ? 3 : 0, i8272_info->fdc_fillbyte, &flags); /* Recalculate disk size */ pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref); } } break; case I8272_SCAN_LOW_EQUAL: /* SCAN LOW OR EQUAL */ case I8272_SCAN_HIGH_EQUAL: /* SCAN HIGH OR EQUAL */ case I8272_SCAN_EQUAL: /* SCAN EQUAL */ TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Scan Data" NLP, PCX)); TRACE_PRINT(ERROR_MSG, ("I8272: " ADDRESS_FORMAT " ERROR: Scan not implemented." NLP, PCX)); break; case I8272_READ_ID: /* READ ID */ TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " READ ID Drive %d result ST0=%02x ST1=%02x ST2=%02x C=%d H=%d R=%02x N=%d" NLP, PCX, i8272_info->sel_drive, i8272_info->result[0], i8272_info->result[1],i8272_info->result[2],i8272_info->result[3], i8272_info->result[4],i8272_info->result[5],i8272_info->result[6])); break; default: break; } } if(i8272_info->result_len != 0) { i8272_info->fdc_phase ++; } else { i8272_info->fdc_phase = CMD_PHASE; } i8272_info->result_index = 0; if((i8272_info->cmd[0] & 0x1F) != I8272_SENSE_INTR_STATUS) { raise_i8272_interrupt(); } } break; } cData = 0x00; return (cData); } static uint8 I8272_Setup_Cmd(uint8 fdc_cmd) { uint8 result = 0; switch(fdc_cmd) { case I8272_READ_DATA: case I8272_WRITE_DATA: case I8272_READ_DELETED_DATA: case I8272_WRITE_DELETED_DATA: case I8272_READ_TRACK: case I8272_SCAN_LOW_EQUAL: case I8272_SCAN_HIGH_EQUAL: case I8272_SCAN_EQUAL: i8272_info->cmd_len = 9; i8272_info->result_len = 7; break; case I8272_READ_ID: /* READ ID */ i8272_info->cmd_len = 2; i8272_info->result_len = 7; break; case I8272_RECALIBRATE: /* RECALIBRATE */ i8272_info->cmd_len = 2; i8272_info->result_len = 0; break; case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ i8272_info->cmd_len = 6; i8272_info->result_len = 7; break; case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */ i8272_info->cmd_len = 1; i8272_info->result_len = 2; break; case I8272_SPECIFY: /* SPECIFY */ i8272_info->cmd_len = 3; i8272_info->result_len = 0; break; case I8272_SENSE_DRIVE_STATUS: /* SENSE DRIVE STATUS */ i8272_info->cmd_len = 2; i8272_info->result_len = 1; break; case I8272_SEEK: /* SEEK */ i8272_info->cmd_len = 3; i8272_info->result_len = 0; break; default: /* INVALID */ i8272_info->cmd_len = 1; i8272_info->result_len = 1; result = -1; break; } return (result); } extern void raise_disk1a_interrupt(void); static void raise_i8272_interrupt(void) { TRACE_PRINT(IRQ_MSG, ("I8272: " ADDRESS_FORMAT " FDC Interrupt" NLP, PCX)); i8272_irq = 1; raise_disk1a_interrupt(); } simh-3.8.1/AltairZ80/altairz80_cpu.c0000644000175000017500000105712211111142454015212 0ustar vlmvlm/* altairz80_cpu.c: MITS Altair CPU (8080 and Z80) Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 Code for Z80 CPU from Frank D. Cringle ((c) 1995 under GNU license) */ #include "altairz80_defs.h" #include #define SWITCHCPU_DEFAULT 0xfd /* Debug flags */ #define IN_MSG (1 << 0) #define OUT_MSG (1 << 1) #if defined (_WIN32) #include #else #include #endif #define PCQ_SIZE 64 /* must be 2**n */ #define PCQ_SIZE_LOG2 6 /* log2 of PCQ_SIZE */ #define PCQ_MASK (PCQ_SIZE - 1) #define PCQ_ENTRY(PC) if (pcq[pcq_p] != (PC)) { pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC); } #define FLAG_C 1 #define FLAG_N 2 #define FLAG_P 4 #define FLAG_H 16 #define FLAG_Z 64 #define FLAG_S 128 #define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f #define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) #define LOW_DIGIT(x) ((x) & 0xf) #define HIGH_DIGIT(x) (((x) >> 4) & 0xf) #define LOW_REGISTER(x) ((x) & 0xff) #define HIGH_REGISTER(x) (((x) >> 8) & 0xff) #define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) #define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) #define PARITY(x) parityTable[(x) & 0xff] /* SET_PV and SET_PV2 are used to provide correct PARITY flag semantics for the 8080 in cases where the Z80 uses the overflow flag */ #define SET_PVS(s) ((chiptype == CHIP_TYPE_Z80) ? (((cbits >> 6) ^ (cbits >> 5)) & 4) : (PARITY(s))) #define SET_PV (SET_PVS(sum)) #define SET_PV2(x) ((chiptype == CHIP_TYPE_Z80) ? (((temp == (x)) << 2)) : (PARITY(temp))) /* CHECK_CPU_8080 must be invoked whenever a Z80 only instruction is executed In case a Z80 instruction is executed on an 8080 the following two cases exist: 1) Trapping is enabled: execution stops 2) Trapping is not enabled: decoding continues with the next byte */ #define CHECK_CPU_8080 \ if (chiptype == CHIP_TYPE_8080) { \ if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ reason = STOP_OPCODE; \ goto end_decode; \ } \ else { \ sim_brk_pend[0] = FALSE; \ continue; \ } \ } /* CHECK_CPU_Z80 must be invoked whenever a non Z80 instruction is executed */ #define CHECK_CPU_Z80 \ if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ reason = STOP_OPCODE; \ goto end_decode; \ } #define POP(x) { \ register uint32 y = RAM_PP(SP); \ x = y + (RAM_PP(SP) << 8); \ } #define JPC(cond) { \ tStates += 10; \ if (cond) { \ PCQ_ENTRY(PCX); \ PC = GET_WORD(PC); \ } \ else { \ PC += 2; \ } \ } #define CALLC(cond) { \ if (cond) { \ register uint32 adrr = GET_WORD(PC); \ CHECK_BREAK_WORD(SP - 2); \ PUSH(PC + 2); \ PCQ_ENTRY(PCX); \ PC = adrr; \ tStates += 17; \ } \ else { \ sim_brk_pend[0] = FALSE; \ PC += 2; \ tStates += 10; \ } \ } extern int32 sim_int_char; extern int32 sio0s (const int32 port, const int32 io, const int32 data); extern int32 sio0d (const int32 port, const int32 io, const int32 data); extern int32 sio1s (const int32 port, const int32 io, const int32 data); extern int32 sio1d (const int32 port, const int32 io, const int32 data); extern int32 dsk10 (const int32 port, const int32 io, const int32 data); extern int32 dsk11 (const int32 port, const int32 io, const int32 data); extern int32 dsk12 (const int32 port, const int32 io, const int32 data); extern int32 netStatus (const int32 port, const int32 io, const int32 data); extern int32 netData (const int32 port, const int32 io, const int32 data); extern int32 nulldev (const int32 port, const int32 io, const int32 data); extern int32 hdsk_io (const int32 port, const int32 io, const int32 data); extern int32 simh_dev (const int32 port, const int32 io, const int32 data); extern int32 sr_dev (const int32 port, const int32 io, const int32 data); extern void install_ALTAIRbootROM(void); extern void do_SIMH_sleep(void); extern FILE *sim_deb; extern t_stat sim_instr_nommu(void); extern uint8 MOPT[MAXBANKSIZE]; extern t_stat sim_instr_8086(void); extern void cpu8086reset(void); /* function prototypes */ static t_stat cpu_set_switcher (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_reset_switcher(UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_show_switcher (FILE *st, UNIT *uptr, int32 val, void *desc); static int32 switchcpu_io (const int32 port, const int32 io, const int32 data); static t_stat cpu_set_altairrom (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_noaltairrom (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_nommu (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_banked (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_nonbanked (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_ramtype (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_chiptype (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_set_memory (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat cpu_clear_command (UNIT *uptr, int32 value, char *cptr, void *desc); static void cpu_clear(void); static t_stat cpu_show (FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat chip_show (FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); static t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw); static t_stat cpu_reset(DEVICE *dptr); static t_stat sim_instr_mmu(void); static uint32 GetBYTE(register uint32 Addr); static void PutWORD(register uint32 Addr, const register uint32 Value); static void PutBYTE(register uint32 Addr, const register uint32 Value); void out(const uint32 Port, const uint32 Value); uint32 in(const uint32 Port); void altairz80_init(void); t_stat sim_instr(void); t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); uint8 GetBYTEWrapper(const uint32 Addr); void PutBYTEWrapper(const uint32 Addr, const uint32 Value); uint8 GetByteDMA(const uint32 Addr); void PutByteDMA(const uint32 Addr, const uint32 Value); int32 getBankSelect(void); void setBankSelect(const int32 b); uint32 getCommon(void); t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag); uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); void PutBYTEExtended(register uint32 Addr, const register uint32 Value); uint32 GetBYTEExtended(register uint32 Addr); void cpu_raise_interrupt(uint32 irq); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_CPU_ALTAIRROM | UNIT_CPU_STOPONHALT | UNIT_CPU_MMU, MAXBANKSIZE) }; uint32 PCX = 0; /* external view of PC */ int32 AF_S; /* AF register */ int32 BC_S; /* BC register */ int32 DE_S; /* DE register */ int32 HL_S; /* HL register */ int32 IX_S; /* IX register */ int32 IY_S; /* IY register */ int32 PC_S = 0; /* 8080 / Z80 program counter */ int32 PCX_S = 0xFFFF0; /* 8086 program counter */ int32 SP_S; /* SP register */ int32 AF1_S; /* alternate AF register */ int32 BC1_S; /* alternate BC register */ int32 DE1_S; /* alternate DE register */ int32 HL1_S; /* alternate HL register */ int32 IFF_S; /* Interrupt Flip Flop */ int32 IR_S; /* Interrupt (upper) / Refresh (lower) register */ int32 AX_S; /* AX register (8086) */ int32 BX_S; /* BX register (8086) */ int32 CX_S; /* CX register (8086) */ int32 DX_S; /* DX register (8086) */ int32 CS_S; /* CS register (8086) */ int32 DS_S; /* DS register (8086) */ int32 ES_S; /* ES register (8086) */ int32 SS_S; /* SS register (8086) */ int32 DI_S; /* DI register (8086) */ int32 SI_S; /* SI register (8086) */ int32 BP_S; /* BP register (8086) */ int32 SPX_S; /* SP register (8086) */ int32 IP_S; /* IP register (8086) */ int32 FLAGS_S; /* flags register (8086) */ int32 SR = 0; /* switch register */ static int32 bankSelect = 0; /* determines selected memory bank */ static uint32 common = 0xc000; /* addresses >= 'common' are in common memory */ static uint32 previousCapacity = MAXBANKSIZE; /* safe for previous memory capacity */ static uint32 clockFrequency = 0; /* in kHz, 0 means as fast as possible */ static uint32 sliceLength = 10; /* length of time-slice for CPU speed */ /* adjustment in milliseconds */ static uint32 executedTStates = 0; /* executed t-states */ static uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ static int32 pcq_p = 0; /* PC queue ptr */ static REG *pcq_r = NULL; /* PC queue reg ptr */ /* data structure for IN/OUT instructions */ struct idev { int32 (*routine)(const int32, const int32, const int32); }; static int32 switcherPort = SWITCHCPU_DEFAULT; static struct idev oldSwitcherDevice = { NULL }; REG cpu_reg[] = { { HRDATA (AF, AF_S, 16) }, { HRDATA (BC, BC_S, 16) }, { HRDATA (DE, DE_S, 16) }, { HRDATA (HL, HL_S, 16) }, { HRDATA (IX, IX_S, 16) }, { HRDATA (IY, IY_S, 16) }, { HRDATA (PC, PC_S, 16 + MAXBANKSLOG2) }, /* 8080 / Z80 PC [6] */ { HRDATA (PCX, PCX_S, 16 + MAXBANKSLOG2) }, /* 8086 PC [7] */ { HRDATA (SP, SP_S, 16) }, { HRDATA (AF1, AF1_S, 16) }, { HRDATA (BC1, BC1_S, 16) }, { HRDATA (DE1, DE1_S, 16) }, { HRDATA (HL1, HL1_S, 16) }, { GRDATA (IFF, IFF_S, 2, 2, 0) }, { FLDATA (IR, IR_S, 8) }, { HRDATA (AX, AX_S, 16) }, /* 8086 */ { GRDATA (AL, AX_S, 16, 8, 0) }, /* 8086, low 8 bits of AX */ { GRDATA (AH, AX_S, 16, 8, 8) }, /* 8086, high 8 bits of AX */ { HRDATA (BX, BX_S, 16) }, /* 8086 */ { GRDATA (BL, BX_S, 16, 8, 0) }, /* 8086, low 8 bits of BX */ { GRDATA (BH, BX_S, 16, 8, 8) }, /* 8086, high 8 bits of BX */ { HRDATA (CX, CX_S, 16) }, /* 8086 */ { GRDATA (CL, CX_S, 16, 8, 0) }, /* 8086, low 8 bits of CX */ { GRDATA (CH, CX_S, 16, 8, 8) }, /* 8086, high 8 bits of CX */ { HRDATA (DX, DX_S, 16) }, /* 8086 */ { GRDATA (DL, DX_S, 16, 8, 0) }, /* 8086, low 8 bits of DX */ { GRDATA (DH, DX_S, 16, 8, 8) }, /* 8086, high 8 bits of DX */ { HRDATA (SPX, SPX_S, 16) }, /* 8086 */ { HRDATA (BP, BP_S, 16) }, /* 8086, Base Pointer */ { HRDATA (SI, SI_S, 16) }, /* 8086, Source Index */ { HRDATA (DI, DI_S, 16) }, /* 8086, Destination Index */ { HRDATA (CS, CS_S, 16) }, /* 8086, Code Segment */ { HRDATA (DS, DS_S, 16) }, /* 8086, Data Segment */ { HRDATA (ES, ES_S, 16) }, /* 8086, Extra Segment */ { HRDATA (SS, SS_S, 16) }, /* 8086, Stack Segment */ { HRDATA (FLAGS, FLAGS_S, 16) }, /* 8086, FLAGS */ { HRDATA (IP, IP_S, 16), REG_RO }, /* 8086, set via PC */ { FLDATA (OPSTOP, cpu_unit.flags, UNIT_CPU_V_OPSTOP), REG_HRO }, { HRDATA (SR, SR, 8) }, { HRDATA (BANK, bankSelect, MAXBANKSLOG2) }, { HRDATA (COMMON, common, 32) }, { HRDATA (SWITCHERPORT, switcherPort, 8), }, { DRDATA (CLOCK, clockFrequency, 32) }, { DRDATA (SLICE, sliceLength, 16) }, { DRDATA (TSTATES, executedTStates, 32), REG_RO }, { HRDATA (CAPACITY, cpu_unit.capac, 32), REG_RO }, { HRDATA (PREVCAP, previousCapacity, 32), REG_RO }, { BRDATA (PCQ, pcq, 16, 16, PCQ_SIZE), REG_RO + REG_CIRC }, { DRDATA (PCQP, pcq_p, PCQ_SIZE_LOG2), REG_HRO }, { HRDATA (WRU, sim_int_char, 8) }, { NULL } }; static MTAB cpu_mod[] = { { MTAB_XTD | MTAB_VDV, CHIP_TYPE_8080, NULL, "8080", &cpu_set_chiptype }, { MTAB_XTD | MTAB_VDV, CHIP_TYPE_Z80, NULL, "Z80", &cpu_set_chiptype }, { MTAB_XTD | MTAB_VDV, CHIP_TYPE_8086, NULL, "8086", &cpu_set_chiptype }, { UNIT_CPU_OPSTOP, UNIT_CPU_OPSTOP, "ITRAP", "ITRAP", NULL, &chip_show }, { UNIT_CPU_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL, &chip_show }, { UNIT_CPU_STOPONHALT, UNIT_CPU_STOPONHALT,"STOPONHALT", "STOPONHALT", NULL }, { UNIT_CPU_STOPONHALT, 0, "LOOPONHALT", "LOOPONHALT", NULL }, { UNIT_CPU_BANKED, UNIT_CPU_BANKED, "BANKED", "BANKED", &cpu_set_banked }, { UNIT_CPU_BANKED, 0, "NONBANKED", "NONBANKED", &cpu_set_nonbanked }, { UNIT_CPU_ALTAIRROM, UNIT_CPU_ALTAIRROM, "ALTAIRROM", "ALTAIRROM", &cpu_set_altairrom }, { UNIT_CPU_ALTAIRROM, 0, "NOALTAIRROM", "NOALTAIRROM", &cpu_set_noaltairrom}, { UNIT_CPU_VERBOSE, UNIT_CPU_VERBOSE, "VERBOSE", "VERBOSE", NULL, &cpu_show }, { UNIT_CPU_VERBOSE, 0, "QUIET", "QUIET", NULL }, { MTAB_VDV, 0, NULL, "CLEARMEMORY", &cpu_clear_command }, { UNIT_CPU_MMU, UNIT_CPU_MMU, "MMU", "MMU", NULL }, { UNIT_CPU_MMU, 0, "NOMMU", "NOMMU", &cpu_set_nommu }, { MTAB_XTD | MTAB_VDV, 0, NULL, "MEMORY", &cpu_set_memory }, { UNIT_CPU_SWITCHER, UNIT_CPU_SWITCHER, "SWITCHER", "SWITCHER", &cpu_set_switcher, &cpu_show_switcher }, { UNIT_CPU_SWITCHER, 0, "NOSWITCHER", "NOSWITCHER", &cpu_reset_switcher, &cpu_show_switcher }, { MTAB_XTD | MTAB_VDV, 0, NULL, "AZ80", &cpu_set_ramtype }, { MTAB_XTD | MTAB_VDV, 1, NULL, "HRAM", &cpu_set_ramtype }, { MTAB_XTD | MTAB_VDV, 2, NULL, "VRAM", &cpu_set_ramtype }, { MTAB_XTD | MTAB_VDV, 3, NULL, "CRAM", &cpu_set_ramtype }, { MTAB_VDV, 4, NULL, "4KB", &cpu_set_size }, { MTAB_VDV, 8, NULL, "8KB", &cpu_set_size }, { MTAB_VDV, 12, NULL, "12KB", &cpu_set_size }, { MTAB_VDV, 16, NULL, "16KB", &cpu_set_size }, { MTAB_VDV, 20, NULL, "20KB", &cpu_set_size }, { MTAB_VDV, 24, NULL, "24KB", &cpu_set_size }, { MTAB_VDV, 28, NULL, "28KB", &cpu_set_size }, { MTAB_VDV, 32, NULL, "32KB", &cpu_set_size }, { MTAB_VDV, 36, NULL, "36KB", &cpu_set_size }, { MTAB_VDV, 40, NULL, "40KB", &cpu_set_size }, { MTAB_VDV, 44, NULL, "44KB", &cpu_set_size }, { MTAB_VDV, 48, NULL, "48KB", &cpu_set_size }, { MTAB_VDV, 52, NULL, "52KB", &cpu_set_size }, { MTAB_VDV, 56, NULL, "56KB", &cpu_set_size }, { MTAB_VDV, 60, NULL, "60KB", &cpu_set_size }, { MTAB_VDV, 64, NULL, "64KB", &cpu_set_size }, { 0 } }; /* Debug Flags */ static DEBTAB cpu_dt[] = { { "LOG_IN", IN_MSG }, { "LOG_OUT", OUT_MSG }, { NULL, 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 16, 16, 1, 16, 8, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_dt, NULL, NULL }; /* This is the I/O configuration table. There are 255 possible device addresses, if a device is plugged to a port it's routine address is here, 'nulldev' means no device is available */ static struct idev dev_table[256] = { {&nulldev}, {&nulldev}, {&sio0d}, {&sio0s}, /* 00 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 04 */ {&dsk10}, {&dsk11}, {&dsk12}, {&nulldev}, /* 08 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0C */ {&sio0s}, {&sio0d}, {&sio1s}, {&sio1d}, /* 10 */ {&sio0s}, {&sio0d}, {&sio0s}, {&sio0d}, /* 14 */ {&sio0s}, {&sio0d}, {&nulldev}, {&nulldev}, /* 18 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 1C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 20 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 24 */ {&netStatus},{&netData},{&netStatus},{&netData}, /* 28 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 2C */ {&nulldev}, {&nulldev}, {&netStatus},{&netData}, /* 30 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 34 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 38 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 3C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 40 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 44 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 48 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 4C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 50 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 54 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 58 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 5C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 60 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 64 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 68 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 6C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 70 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 74 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 78 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 7C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 80 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 84 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 88 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 8C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 90 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 94 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 98 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 9C */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A8 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* AC */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B8 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* BC */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C8 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* CC */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D8 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* DC */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* E0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* E4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* E8 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* EC */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F0 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F4 */ {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F8 */ {&nulldev}, {&hdsk_io}, {&simh_dev}, {&sr_dev} /* FC */ }; static int32 ramtype = 0; #define MAX_RAM_TYPE 3 int32 chiptype = CHIP_TYPE_8080; void out(const uint32 Port, const uint32 Value) { if ((cpu_dev.dctrl & OUT_MSG) && sim_deb) { fprintf(sim_deb, "CPU: " ADDRESS_FORMAT " OUT(port=0x%04x [%5d], value=0x%04x [%5d])\n", PCX, Port, Port, Value, Value); fflush(sim_deb); } dev_table[Port & 0xff].routine(Port, 1, Value); if ((cpu_dev.dctrl & OUT_MSG) && sim_deb) { fprintf(sim_deb, "CPU: " ADDRESS_FORMAT " OUT(port=0x%04x [%5d], value=0x%04x [%5d]) done\n", PCX, Port, Port, Value, Value); fflush(sim_deb); } } uint32 in(const uint32 Port) { uint32 result; if ((cpu_dev.dctrl & IN_MSG) && sim_deb) { fprintf(sim_deb, "CPU: " ADDRESS_FORMAT " IN(port=0x%04x [%5d])\n", PCX, Port, Port); fflush(sim_deb); } result = dev_table[Port & 0xff].routine(Port, 0, 0); if ((cpu_dev.dctrl & IN_MSG) && sim_deb) { fprintf(sim_deb, "CPU: " ADDRESS_FORMAT " IN(port=0x%04x [%5d]) = 0x%04x [%5d]\n", PCX, Port, Port, result, result); fflush(sim_deb); } return result; } /* the following tables precompute some common subexpressions parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8) cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8) negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) */ /* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ static const uint8 parityTable[256] = { 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, }; /* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ static const uint8 incTable[257] = { 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 }; /* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ static const uint8 decTable[256] = { 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, }; /* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ static const uint8 cbitsTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, }; /* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ static const uint16 cbitsDup8Table[512] = { 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, }; /* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ static const uint8 cbitsDup16Table[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, }; /* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ static const uint8 cbits2Table[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ static const uint16 rrcaTable[256] = { 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, }; /* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ static const uint16 rraTable[256] = { 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, }; /* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ static const uint16 addTable[512] = { 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, }; /* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ static const uint16 subTable[256] = { 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, }; /* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ static const uint16 andTable[256] = { 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, }; /* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ static const uint16 xororTable[256] = { 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, }; /* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ static const uint8 rotateShiftTable[256] = { 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, }; /* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ static const uint8 incZ80Table[257] = { 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, }; /* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ static const uint8 decZ80Table[256] = { 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, }; /* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ static const uint8 cbitsZ80Table[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, }; /* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ static const uint8 cbitsZ80DupTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, }; /* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ static const uint8 cbits2Z80Table[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8), i = 0..511 */ static const uint8 cbits2Z80DupTable[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, }; /* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ static const uint8 negTable[256] = { 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ static const uint16 rrdrldTable[256] = { 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, }; /* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ static const uint8 cpTable[256] = { 64, 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, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, }; /* remove comments to generate table contents and define globally NEED_SIM_VM_INIT static void altairz80_init(void); void (*sim_vm_init) (void) = &altairz80_init; static void altairz80_init(void) { */ /* parityTable */ /* uint32 i, v; for (i = 0; i < 256; i++) { v = ((i & 1) + ((i & 2) >> 1) + ((i & 4) >> 2) + ((i & 8) >> 3) + ((i & 16) >> 4) + ((i & 32) >> 5) + ((i & 64) >> 6) + ((i & 128) >> 7)) % 2 ? 0 : 4; printf("%1d,", v); if ( ((i+1) & 0xf) == 0) { printf("\n"); } } */ /* incTable */ /* uint32 temp, v; for (temp = 0; temp <= 256; temp++) { v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0) << 4); printf("%3d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* decTable */ /* uint32 temp, v; for (temp = 0; temp < 256; temp++) { v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0xf) << 4) | 2; printf("%3d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* cbitsTable */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | ((cbits >> 8) & 1); printf("%2d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* cbitsDup8Table */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | ((cbits >> 8) & 1) | ((cbits & 0xff) << 8) | (cbits & 0xa8) | (((cbits & 0xff) == 0) << 6); printf("0x%04x,", v); if ( ((cbits+1) & 0x7) == 0) { printf("\n"); } } */ /* cbitsDup16Table */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | ((cbits >> 8) & 1) | (cbits & 0x28); printf("%2d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* cbits2Table */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | ((cbits >> 8) & 1) | 2; printf("%2d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* rrcaTable */ /* uint32 temp, sum, v; for (temp = 0; temp < 256; temp++) { sum = temp >> 1; v = ((temp & 1) << 15) | (sum << 8) | (sum & 0x28) | (temp & 1); printf("0x%04x,", v); if ( ((temp+1) & 0x7) == 0) { printf("\n"); } } */ /* rraTable */ /* uint32 temp, sum, v; for (temp = 0; temp < 256; temp++) { sum = temp >> 1; v = (sum << 8) | (sum & 0x28) | (temp & 1); printf("0x%04x,", v); if ( ((temp+1) & 0x7) == 0) { printf("\n"); } } */ /* addTable */ /* uint32 sum, v; for (sum = 0; sum < 512; sum++) { v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6); printf("0x%04x,", v); if ( ((sum+1) & 0x7) == 0) { printf("\n"); } } */ /* subTable */ /* uint32 sum, v; for (sum = 0; sum < 256; sum++) { v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6) | 2; printf("0x%04x,", v); if ( ((sum+1) & 0x7) == 0) { printf("\n"); } } */ /* andTable */ /* uint32 sum, v; for (sum = 0; sum < 256; sum++) { v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | 0x10 | parityTable[sum]; printf("0x%04x,", v); if ( ((sum+1) & 0x7) == 0) { printf("\n"); } } */ /* xororTable */ /* uint32 sum, v; for (sum = 0; sum < 256; sum++) { v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | parityTable[sum]; printf("0x%04x,", v); if ( ((sum+1) & 0x7) == 0) { printf("\n"); } } */ /* rotateShiftTable */ /* uint32 temp, v; for (temp = 0; temp < 256; temp++) { v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | PARITY(temp); printf("%3d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* incZ80Table */ /* uint32 temp, v; for (temp = 0; temp < 256; temp++) { v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0) << 4) | ((temp == 0x80) << 2); printf("%3d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* decZ80Table */ /* uint32 temp, v; for (temp = 0; temp < 256; temp++) { v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0xf) << 4) | ((temp == 0x7f) << 2) | 2; printf("%3d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* cbitsZ80Table */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | ((cbits >> 8) & 1); printf("%2d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* cbitsZ80DupTable */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | ((cbits >> 8) & 1) | (cbits & 0xa8); printf("%3d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* cbits2Z80Table */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1); printf("%2d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* cbits2Z80DupTable */ /* uint32 cbits, v; for (cbits = 0; cbits < 512; cbits++) { v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1) | (cbits & 0xa8); printf("%3d,", v); if ( ((cbits+1) & 0xf) == 0) { printf("\n"); } } */ /* negTable */ /* uint32 temp, v; for (temp = 0; temp < 256; temp++) { v = (((temp & 0x0f) != 0) << 4) | ((temp == 0x80) << 2) | 2 | (temp != 0); printf("%2d,", v); if ( ((temp+1) & 0xf) == 0) { printf("\n"); } } */ /* rrdrldTable */ /* uint32 acu, v; for (acu = 0; acu < 256; acu++) { v = (acu << 8) | (acu & 0xa8) | (((acu & 0xff) == 0) << 6) | parityTable[acu]; printf("0x%04x,", v); if ( ((acu+1) & 0x7) == 0) { printf("\n"); } } */ /* cpTable */ /* uint32 sum, v; for (sum = 0; sum < 256; sum++) { v = (sum & 0x80) | (((sum & 0xff) == 0) << 6); printf("%3d,", v); if ( ((sum+1) & 0xf) == 0) { printf("\n"); } } */ /* remove comments to generate table contents } */ /* Memory management */ #define LOG2PAGESIZE 8 #define PAGESIZE (1 << LOG2PAGESIZE) static uint8 M[MAXMEMORY]; /* RAM which is present */ struct mdev { /* Structure to describe a 2^LOG2PAGESIZE byte page of address space */ /* There are four cases isRAM isEmpty routine code TRUE FALSE NULL W page is random access memory (RAM) FALSE TRUE NULL U no memory at this location FALSE FALSE NULL R page is read only memory (ROM) FALSE FALSE not NULL M page is mapped to memory mapped I/O routine other combinations are undefined! */ uint32 isRAM; uint32 isEmpty; int32 (*routine)(const int32, const int32, const int32); }; typedef struct mdev MDEV; static MDEV ROM_PAGE = {FALSE, FALSE, NULL}; /* this makes a page ROM */ static MDEV RAM_PAGE = {TRUE, FALSE, NULL}; /* this makes a page RAM */ static MDEV EMPTY_PAGE = {FALSE, TRUE, NULL}; /* this is non-existing memory */ static MDEV mmu_table[MAXMEMORY >> LOG2PAGESIZE]; /* Memory and I/O Resource Mapping and Unmapping routine. */ uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap) { uint32 page, i, addr; if (resource_type == RESOURCE_TYPE_MEMORY) { for (i = 0; i < (size >> LOG2PAGESIZE); i++) { addr = (baseaddr & 0xfff00) + (i << LOG2PAGESIZE); if ((cpu_unit.flags & UNIT_CPU_BANKED) && (addr < common)) addr |= bankSelect << MAXBANKSIZELOG2; page = addr >> LOG2PAGESIZE; if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("%s memory 0x%05x, handler=%p\n", unmap ? "Unmapping" : " Mapping", addr, routine); if (unmap) { if (mmu_table[page].routine == routine) /* unmap only if it was mapped */ if (MEMORYSIZE < MAXBANKSIZE) if (addr < MEMORYSIZE) mmu_table[page] = RAM_PAGE; else mmu_table[page] = EMPTY_PAGE; else mmu_table[page] = RAM_PAGE; } else { mmu_table[page] = ROM_PAGE; mmu_table[page].routine = routine; } } } else if (resource_type == RESOURCE_TYPE_IO) { for (i = baseaddr; i < baseaddr + size; i++) if (unmap) { if (dev_table[i & 0xff].routine == routine) { if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Unmapping IO %04x, handler=%p\n", i, routine); dev_table[i & 0xff].routine = &nulldev; } } else { if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf(" Mapping IO %04x, handler=%p\n", i, routine); dev_table[i & 0xff].routine = routine; } } else { printf("%s: cannot map unknown resource type %d\n", __FUNCTION__, resource_type); return -1; } return 0; } static void PutBYTE(register uint32 Addr, const register uint32 Value) { MDEV m; Addr &= ADDRMASK; /* registers are NOT guaranteed to be always 16-bit values */ if ((cpu_unit.flags & UNIT_CPU_BANKED) && (Addr < common)) Addr |= bankSelect << MAXBANKSIZELOG2; m = mmu_table[Addr >> LOG2PAGESIZE]; if (m.isRAM) M[Addr] = Value; else if (m.routine) m.routine(Addr, 1, Value); else if (cpu_unit.flags & UNIT_CPU_VERBOSE) { if (m.isEmpty) printf("CPU: " ADDRESS_FORMAT " Attempt to write to non existing memory " ADDRESS_FORMAT "." NLP, PCX, Addr); else printf("CPU: " ADDRESS_FORMAT " Attempt to write to ROM " ADDRESS_FORMAT "." NLP, PCX, Addr); } } void PutBYTEExtended(register uint32 Addr, const register uint32 Value) { MDEV m; Addr &= ADDRMASKEXTENDED; m = mmu_table[Addr >> LOG2PAGESIZE]; if (m.isRAM) M[Addr] = Value; else if (m.routine) m.routine(Addr, 1, Value); else if (cpu_unit.flags & UNIT_CPU_VERBOSE) { if (m.isEmpty) printf("CPU: " ADDRESS_FORMAT " Attempt to write to non existing memory " ADDRESS_FORMAT "." NLP, PCX, Addr); else printf("CPU: " ADDRESS_FORMAT " Attempt to write to ROM " ADDRESS_FORMAT "." NLP, PCX, Addr); } } static void PutWORD(register uint32 Addr, const register uint32 Value) { PutBYTE(Addr, Value); PutBYTE(Addr + 1, Value >> 8); } static uint32 GetBYTE(register uint32 Addr) { MDEV m; Addr &= ADDRMASK; /* registers are NOT guaranteed to be always 16-bit values */ if ((cpu_unit.flags & UNIT_CPU_BANKED) && (Addr < common)) Addr |= bankSelect << MAXBANKSIZELOG2; m = mmu_table[Addr >> LOG2PAGESIZE]; if (m.isRAM) return M[Addr]; /* RAM */ if (m.routine) return m.routine(Addr, 0, 0); /* memory mapped I/O */ if (m.isEmpty) { if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("CPU: " ADDRESS_FORMAT " Attempt to read from non existing memory " ADDRESS_FORMAT "." NLP, PCX, Addr); return 0xff; } return M[Addr]; /* ROM */ } uint32 GetBYTEExtended(register uint32 Addr) { MDEV m; Addr &= ADDRMASKEXTENDED; m = mmu_table[Addr >> LOG2PAGESIZE]; if (m.isRAM) return M[Addr]; if (m.routine) return m.routine(Addr, 0, 0); if (m.isEmpty) { if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("CPU: " ADDRESS_FORMAT " Attempt to read from non existing memory " ADDRESS_FORMAT "." NLP, PCX, Addr); return 0xff; } return M[Addr]; } int32 getBankSelect(void) { return bankSelect; } void setBankSelect(const int32 b) { bankSelect = b; } uint32 getCommon(void) { return common; } /* memory access during a simulation */ uint8 GetBYTEWrapper(const uint32 Addr) { if (chiptype == CHIP_TYPE_8086) return GetBYTEExtended(Addr); else if (cpu_unit.flags & UNIT_CPU_MMU) return GetBYTE(Addr); else return MOPT[Addr & ADDRMASK]; } /* memory access during a simulation */ void PutBYTEWrapper(const uint32 Addr, const uint32 Value) { if (chiptype == CHIP_TYPE_8086) PutBYTEExtended(Addr, Value); else if (cpu_unit.flags & UNIT_CPU_MMU) PutBYTE(Addr, Value); else MOPT[Addr & ADDRMASK] = Value & 0xff; } /* DMA memory access during a simulation, suggested by Tony Nicholson */ uint8 GetByteDMA(const uint32 Addr) { if ((chiptype == CHIP_TYPE_8086) || (cpu_unit.flags & UNIT_CPU_MMU)) return GetBYTEExtended(Addr); else return MOPT[Addr & ADDRMASK]; } void PutByteDMA(const uint32 Addr, const uint32 Value) { if ((chiptype == CHIP_TYPE_8086) || (cpu_unit.flags & UNIT_CPU_MMU)) PutBYTEExtended(Addr, Value); else MOPT[Addr & ADDRMASK] = Value & 0xff; } #define RAM_PP(Addr) GetBYTE(Addr++) #define RAM_MM(Addr) GetBYTE(Addr--) #define GET_WORD(Addr) (GetBYTE(Addr) | (GetBYTE(Addr + 1) << 8)) #define PUT_BYTE_PP(a,v) PutBYTE(a++, v) #define PUT_BYTE_MM(a,v) PutBYTE(a--, v) #define MM_PUT_BYTE(a,v) PutBYTE(--a, v) #define MASK_BRK (TRUE + 1) /* this is a modified version of sim_brk_test with two differences: 1) is does not set sim_brk_pend to FALSE (this is left to the instruction decode) 2) it returns MASK_BRK if a breakpoint is found but should be ignored */ static int32 sim_brk_lookup (const t_addr loc, const int32 btyp) { extern t_bool sim_brk_pend[SIM_BKPT_N_SPC]; extern t_addr sim_brk_ploc[SIM_BKPT_N_SPC]; extern char *sim_brk_act; BRKTAB *bp; if ((bp = sim_brk_fnd (loc)) && /* entry in table? */ (btyp & bp -> typ) && /* type match? */ (!sim_brk_pend[0] || (loc != sim_brk_ploc[0])) && /* new location? */ (--(bp -> cnt) <= 0)) { /* count reach 0? */ bp -> cnt = 0; /* reset count */ sim_brk_ploc[0] = loc; /* save location */ sim_brk_act = bp -> act; /* set up actions */ sim_brk_pend[0] = TRUE; /* don't do twice */ return TRUE; } return (sim_brk_pend[0] && (loc == sim_brk_ploc[0])) ? MASK_BRK : FALSE; } static void prepareMemoryAccessMessage(t_addr loc) { extern char memoryAccessMessage[]; sprintf(memoryAccessMessage, "Memory access breakpoint [%05xh]", loc); } #define PUSH(x) { \ MM_PUT_BYTE(SP, (x) >> 8); \ MM_PUT_BYTE(SP, x); \ } #define CHECK_BREAK_BYTE(a) \ if (sim_brk_summ && sim_brk_test((a) & 0xffff, SWMASK('M'))) { \ reason = STOP_MEM; \ prepareMemoryAccessMessage((a) & 0xffff); \ goto end_decode; \ } #define CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2, iCode) \ if (sim_brk_summ) { \ br1 = sim_brk_lookup((a1) & 0xffff, SWMASK('M')); \ br2 = br1 ? FALSE : sim_brk_lookup((a2) & 0xffff, SWMASK('M'));\ if ((br1 == MASK_BRK) || (br2 == MASK_BRK)) { \ sim_brk_pend[0] = FALSE; \ } \ else if (br1 || br2) { \ reason = STOP_MEM; \ if (br1) { \ prepareMemoryAccessMessage((a1) & 0xffff); \ } \ else { \ prepareMemoryAccessMessage((a2) & 0xffff); \ } \ iCode; \ goto end_decode; \ } \ else { \ sim_brk_pend[0] = FALSE; \ } \ } #define CHECK_BREAK_TWO_BYTES(a1, a2) CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2,;) #define CHECK_BREAK_WORD(a) CHECK_BREAK_TWO_BYTES(a, (a + 1)) #define HALTINSTRUCTION 0x76 /* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR Pre condition temp == value of register B at entry of the instruction acu == value of transferred byte (IN or OUT) Post condition F is set correctly Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where x == (C + 1) & 0xff for INIR x == L for OTIR and OTDR x == (C - 1) & 0xff for INDR Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where x == (C + 1) & 0xff for INI x == L for OUTI and OUTD x == (C - 1) & 0xff for IND */ #define INOUTFLAGS(syxz, x) \ AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ ((acu & 0x80) >> 6) | /* NF */ \ ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ parityTable[((acu + (x)) & 7) ^ temp] /* PF */ #define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) #define INOUTFLAGS_NONZERO(x) \ INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) int32 switch_cpu_now = TRUE; /* hharte */ t_stat sim_instr (void) { uint32 i; t_stat result; if ((chiptype == CHIP_TYPE_8086) || (cpu_unit.flags & UNIT_CPU_MMU)) do { result = (chiptype == CHIP_TYPE_8086) ? sim_instr_8086() : sim_instr_mmu(); } while (switch_cpu_now == FALSE); else { for (i = 0; i < MAXBANKSIZE; i++) MOPT[i] = M[i]; result = sim_instr_nommu(); for (i = 0; i < MAXBANKSIZE; i++) M[i] = MOPT[i]; } return result; } static t_stat sim_instr_mmu (void) { extern int32 sim_interval; extern t_bool sim_brk_pend[SIM_BKPT_N_SPC]; extern int32 timerInterrupt; extern int32 timerInterruptHandler; extern int32 keyboardInterrupt; extern uint32 keyboardInterruptHandler; extern uint32 sim_os_msec(void); extern const t_bool rtc_avail; extern uint32 sim_brk_summ; int32 reason = SCPE_OK; register uint32 specialProcessing; register uint32 AF; register uint32 BC; register uint32 DE; register uint32 HL; register uint32 PC; register uint32 SP; register uint32 IX; register uint32 IY; register uint32 temp = 0; register uint32 acu = 0; register uint32 sum; register uint32 cbits; register uint32 op; register uint32 adr; /* tStates contains the number of t-states executed. One t-state is executed in one microsecond on a 1MHz CPU. tStates is used for real-time simulations. */ register uint32 tStates; uint32 tStatesInSlice; /* number of t-states in 10 mSec time-slice */ uint32 startTime, now; int32 br1, br2, tStateModifier = FALSE; switch_cpu_now = TRUE; /* hharte */ AF = AF_S; BC = BC_S; DE = DE_S; HL = HL_S; PC = PC_S & ADDRMASK; SP = SP_S; IX = IX_S; IY = IY_S; specialProcessing = clockFrequency | timerInterrupt | keyboardInterrupt | sim_brk_summ; tStates = 0; if (rtc_avail) { startTime = sim_os_msec(); tStatesInSlice = sliceLength*clockFrequency; } else { /* make sure that sim_os_msec() is not called later */ clockFrequency = startTime = tStatesInSlice = 0; } /* main instruction fetch/decode loop */ while (switch_cpu_now == TRUE) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ #if !UNIX_PLATFORM if ((reason = sim_os_poll_kbd()) == SCPE_STOP) { /* poll on platforms without reliable signalling */ break; } #endif if ( (reason = sim_process_event()) ) break; else specialProcessing = clockFrequency | timerInterrupt | keyboardInterrupt | sim_brk_summ; } if (specialProcessing) { /* quick check for special processing */ if (clockFrequency && (tStates >= tStatesInSlice)) { /* clockFrequency != 0 implies that real time clock is available */ startTime += sliceLength; tStates -= tStatesInSlice; if (startTime > (now = sim_os_msec())) { #if defined (_WIN32) Sleep(startTime - now); #else usleep(1000 * (startTime - now)); #endif } } if (timerInterrupt && (IFF_S & 1)) { timerInterrupt = FALSE; specialProcessing = clockFrequency | sim_brk_summ; IFF_S = 0; /* disable interrupts */ CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (timerInterrupt = TRUE, IFF_S |= 1)); if ((GetBYTE(PC) == HALTINSTRUCTION) && ((cpu_unit.flags & UNIT_CPU_STOPONHALT) == 0)) { PUSH(PC + 1); PCQ_ENTRY(PC); } else { PUSH(PC); PCQ_ENTRY(PC - 1); } PC = timerInterruptHandler & ADDRMASK; } if (keyboardInterrupt && (IFF_S & 1)) { keyboardInterrupt = FALSE; specialProcessing = clockFrequency | sim_brk_summ; IFF_S = 0; /* disable interrupts */ CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (keyboardInterrupt = TRUE, IFF_S |= 1)); if ((GetBYTE(PC) == HALTINSTRUCTION) && ((cpu_unit.flags & UNIT_CPU_STOPONHALT) == 0)) { PUSH(PC + 1); PCQ_ENTRY(PC); } else { PUSH(PC); PCQ_ENTRY(PC - 1); } PC = keyboardInterruptHandler & ADDRMASK; } if (sim_brk_summ) { if (sim_brk_lookup(PC, SWMASK('E')) == TRUE) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } if (sim_brk_test(GetBYTE(PC), (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } } } PCX = PC; sim_interval--; /* make sure that each instructions properly sets sim_brk_pend: 1) Either directly to FALSE if no memory access takes place or 2) through a call to a Check... routine */ switch(RAM_PP(PC)) { case 0x00: /* NOP */ tStates += 4; sim_brk_pend[0] = FALSE; break; case 0x01: /* LD BC,nnnn */ tStates += 10; sim_brk_pend[0] = FALSE; BC = GET_WORD(PC); PC += 2; break; case 0x02: /* LD (BC),A */ tStates += 7; CHECK_BREAK_BYTE(BC) PutBYTE(BC, HIGH_REGISTER(AF)); break; case 0x03: /* INC BC */ tStates += 6; sim_brk_pend[0] = FALSE; ++BC; break; case 0x04: /* INC B */ tStates += 4; sim_brk_pend[0] = FALSE; BC += 0x100; temp = HIGH_REGISTER(BC); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x05: /* DEC B */ tStates += 4; sim_brk_pend[0] = FALSE; BC -= 0x100; temp = HIGH_REGISTER(BC); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x06: /* LD B,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(BC, RAM_PP(PC)); break; case 0x07: /* RLCA */ tStates += 4; sim_brk_pend[0] = FALSE; AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | (AF & 0xc4) | ((AF >> 15) & 1); break; case 0x08: /* EX AF,AF' */ tStates += 4; sim_brk_pend[0] = FALSE; CHECK_CPU_8080; temp = AF; AF = AF1_S; AF1_S = temp; break; case 0x09: /* ADD HL,BC */ tStates += 11; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; BC &= ADDRMASK; sum = HL + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; HL = sum; break; case 0x0a: /* LD A,(BC) */ tStates += 7; CHECK_BREAK_BYTE(BC) SET_HIGH_REGISTER(AF, GetBYTE(BC)); break; case 0x0b: /* DEC BC */ tStates += 6; sim_brk_pend[0] = FALSE; --BC; break; case 0x0c: /* INC C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC) + 1; SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x0d: /* DEC C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC) - 1; SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x0e: /* LD C,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(BC, RAM_PP(PC)); break; case 0x0f: /* RRCA */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; break; case 0x10: /* DJNZ dd */ sim_brk_pend[0] = FALSE; CHECK_CPU_8080; if ((BC -= 0x100) & 0xff00) { PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; tStates += 13; } else { PC++; tStates += 8; } break; case 0x11: /* LD DE,nnnn */ tStates += 10; sim_brk_pend[0] = FALSE; DE = GET_WORD(PC); PC += 2; break; case 0x12: /* LD (DE),A */ tStates += 7; CHECK_BREAK_BYTE(DE) PutBYTE(DE, HIGH_REGISTER(AF)); break; case 0x13: /* INC DE */ tStates += 6; sim_brk_pend[0] = FALSE; ++DE; break; case 0x14: /* INC D */ tStates += 4; sim_brk_pend[0] = FALSE; DE += 0x100; temp = HIGH_REGISTER(DE); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x15: /* DEC D */ tStates += 4; sim_brk_pend[0] = FALSE; DE -= 0x100; temp = HIGH_REGISTER(DE); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x16: /* LD D,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(DE, RAM_PP(PC)); break; case 0x17: /* RLA */ tStates += 4; sim_brk_pend[0] = FALSE; AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | (AF & 0xc4) | ((AF >> 15) & 1); break; case 0x18: /* JR dd */ tStates += 12; sim_brk_pend[0] = FALSE; CHECK_CPU_8080; PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; break; case 0x19: /* ADD HL,DE */ tStates += 11; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; DE &= ADDRMASK; sum = HL + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; HL = sum; break; case 0x1a: /* LD A,(DE) */ tStates += 7; CHECK_BREAK_BYTE(DE) SET_HIGH_REGISTER(AF, GetBYTE(DE)); break; case 0x1b: /* DEC DE */ tStates += 6; sim_brk_pend[0] = FALSE; --DE; break; case 0x1c: /* INC E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE) + 1; SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x1d: /* DEC E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE) - 1; SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x1e: /* LD E,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(DE, RAM_PP(PC)); break; case 0x1f: /* RRA */ tStates += 4; sim_brk_pend[0] = FALSE; AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; break; case 0x20: /* JR NZ,dd */ sim_brk_pend[0] = FALSE; CHECK_CPU_8080; if (TSTFLAG(Z)) { PC++; tStates += 7; } else { PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; tStates += 12; } break; case 0x21: /* LD HL,nnnn */ tStates += 10; sim_brk_pend[0] = FALSE; HL = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),HL */ tStates += 16; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, HL); PC += 2; break; case 0x23: /* INC HL */ tStates += 6; sim_brk_pend[0] = FALSE; ++HL; break; case 0x24: /* INC H */ tStates += 4; sim_brk_pend[0] = FALSE; HL += 0x100; temp = HIGH_REGISTER(HL); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x25: /* DEC H */ tStates += 4; sim_brk_pend[0] = FALSE; HL -= 0x100; temp = HIGH_REGISTER(HL); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x26: /* LD H,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(HL, RAM_PP(PC)); break; case 0x27: /* DAA */ tStates += 4; sim_brk_pend[0] = FALSE; acu = HIGH_REGISTER(AF); temp = LOW_DIGIT(acu); cbits = TSTFLAG(C); if (TSTFLAG(N)) { /* last operation was a subtract */ int hd = cbits || acu > 0x99; if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ if (temp > 5) { SETFLAG(H, 0); } acu -= 6; acu &= 0xff; } if (hd) acu -= 0x160; /* adjust high digit */ } else { /* last operation was an add */ if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ SETFLAG(H, (temp > 9)); acu += 6; } if (cbits || ((acu & 0x1f0) > 0x90)) acu += 0x60; /* adjust high digit */ } AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; break; case 0x28: /* JR Z,dd */ sim_brk_pend[0] = FALSE; CHECK_CPU_8080; if (TSTFLAG(Z)) { PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; tStates += 12; } else { PC++; tStates += 7; } break; case 0x29: /* ADD HL,HL */ tStates += 11; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; sum = HL + HL; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; HL = sum; break; case 0x2a: /* LD HL,(nnnn) */ tStates += 16; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); HL = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC HL */ tStates += 6; sim_brk_pend[0] = FALSE; --HL; break; case 0x2c: /* INC L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL) + 1; SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x2d: /* DEC L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL) - 1; SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x2e: /* LD L,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(HL, RAM_PP(PC)); break; case 0x2f: /* CPL */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; break; case 0x30: /* JR NC,dd */ sim_brk_pend[0] = FALSE; CHECK_CPU_8080; if (TSTFLAG(C)) { PC++; tStates += 7; } else { PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; tStates += 12; } break; case 0x31: /* LD SP,nnnn */ tStates += 10; sim_brk_pend[0] = FALSE; SP = GET_WORD(PC); PC += 2; break; case 0x32: /* LD (nnnn),A */ tStates += 13; temp = GET_WORD(PC); CHECK_BREAK_BYTE(temp); PutBYTE(temp, HIGH_REGISTER(AF)); PC += 2; break; case 0x33: /* INC SP */ tStates += 6; sim_brk_pend[0] = FALSE; ++SP; break; case 0x34: /* INC (HL) */ tStates += 11; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL) + 1; PutBYTE(HL, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x35: /* DEC (HL) */ tStates += 11; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL) - 1; PutBYTE(HL, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x36: /* LD (HL),nn */ tStates += 10; CHECK_BREAK_BYTE(HL); PutBYTE(HL, RAM_PP(PC)); break; case 0x37: /* SCF */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; break; case 0x38: /* JR C,dd */ sim_brk_pend[0] = FALSE; CHECK_CPU_8080; if (TSTFLAG(C)) { PCQ_ENTRY(PCX); PC += (int8) GetBYTE(PC) + 1; tStates += 12; } else { PC++; tStates += 7; } break; case 0x39: /* ADD HL,SP */ tStates += 11; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; SP &= ADDRMASK; sum = HL + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; HL = sum; break; case 0x3a: /* LD A,(nnnn) */ tStates += 13; temp = GET_WORD(PC); CHECK_BREAK_BYTE(temp); SET_HIGH_REGISTER(AF, GetBYTE(temp)); PC += 2; break; case 0x3b: /* DEC SP */ tStates += 6; sim_brk_pend[0] = FALSE; --SP; break; case 0x3c: /* INC A */ tStates += 4; sim_brk_pend[0] = FALSE; AF += 0x100; temp = HIGH_REGISTER(AF); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x3d: /* DEC A */ tStates += 4; sim_brk_pend[0] = FALSE; AF -= 0x100; temp = HIGH_REGISTER(AF); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x3e: /* LD A,nn */ tStates += 7; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, RAM_PP(PC)); break; case 0x3f: /* CCF */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); break; case 0x40: /* LD B,B */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x41: /* LD B,C */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | ((BC & 0xff) << 8); break; case 0x42: /* LD B,D */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | (DE & ~0xff); break; case 0x43: /* LD B,E */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | ((DE & 0xff) << 8); break; case 0x44: /* LD B,H */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | (HL & ~0xff); break; case 0x45: /* LD B,L */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | ((HL & 0xff) << 8); break; case 0x46: /* LD B,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_HIGH_REGISTER(BC, GetBYTE(HL)); break; case 0x47: /* LD B,A */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & 0xff) | (AF & ~0xff); break; case 0x48: /* LD C,B */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | ((BC >> 8) & 0xff); break; case 0x49: /* LD C,C */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x4a: /* LD C,D */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | ((DE >> 8) & 0xff); break; case 0x4b: /* LD C,E */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | (DE & 0xff); break; case 0x4c: /* LD C,H */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | ((HL >> 8) & 0xff); break; case 0x4d: /* LD C,L */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | (HL & 0xff); break; case 0x4e: /* LD C,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_LOW_REGISTER(BC, GetBYTE(HL)); break; case 0x4f: /* LD C,A */ tStates += 4; sim_brk_pend[0] = FALSE; BC = (BC & ~0xff) | ((AF >> 8) & 0xff); break; case 0x50: /* LD D,B */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | (BC & ~0xff); break; case 0x51: /* LD D,C */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | ((BC & 0xff) << 8); break; case 0x52: /* LD D,D */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x53: /* LD D,E */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | ((DE & 0xff) << 8); break; case 0x54: /* LD D,H */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | (HL & ~0xff); break; case 0x55: /* LD D,L */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | ((HL & 0xff) << 8); break; case 0x56: /* LD D,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_HIGH_REGISTER(DE, GetBYTE(HL)); break; case 0x57: /* LD D,A */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & 0xff) | (AF & ~0xff); break; case 0x58: /* LD E,B */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | ((BC >> 8) & 0xff); break; case 0x59: /* LD E,C */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | (BC & 0xff); break; case 0x5a: /* LD E,D */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | ((DE >> 8) & 0xff); break; case 0x5b: /* LD E,E */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x5c: /* LD E,H */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | ((HL >> 8) & 0xff); break; case 0x5d: /* LD E,L */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | (HL & 0xff); break; case 0x5e: /* LD E,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_LOW_REGISTER(DE, GetBYTE(HL)); break; case 0x5f: /* LD E,A */ tStates += 4; sim_brk_pend[0] = FALSE; DE = (DE & ~0xff) | ((AF >> 8) & 0xff); break; case 0x60: /* LD H,B */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | (BC & ~0xff); break; case 0x61: /* LD H,C */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | ((BC & 0xff) << 8); break; case 0x62: /* LD H,D */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | (DE & ~0xff); break; case 0x63: /* LD H,E */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | ((DE & 0xff) << 8); break; case 0x64: /* LD H,H */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x65: /* LD H,L */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | ((HL & 0xff) << 8); break; case 0x66: /* LD H,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_HIGH_REGISTER(HL, GetBYTE(HL)); break; case 0x67: /* LD H,A */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & 0xff) | (AF & ~0xff); break; case 0x68: /* LD L,B */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | ((BC >> 8) & 0xff); break; case 0x69: /* LD L,C */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | (BC & 0xff); break; case 0x6a: /* LD L,D */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | ((DE >> 8) & 0xff); break; case 0x6b: /* LD L,E */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | (DE & 0xff); break; case 0x6c: /* LD L,H */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | ((HL >> 8) & 0xff); break; case 0x6d: /* LD L,L */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x6e: /* LD L,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_LOW_REGISTER(HL, GetBYTE(HL)); break; case 0x6f: /* LD L,A */ tStates += 4; sim_brk_pend[0] = FALSE; HL = (HL & ~0xff) | ((AF >> 8) & 0xff); break; case 0x70: /* LD (HL),B */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, HIGH_REGISTER(BC)); break; case 0x71: /* LD (HL),C */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, LOW_REGISTER(BC)); break; case 0x72: /* LD (HL),D */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, HIGH_REGISTER(DE)); break; case 0x73: /* LD (HL),E */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, LOW_REGISTER(DE)); break; case 0x74: /* LD (HL),H */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, HIGH_REGISTER(HL)); break; case 0x75: /* LD (HL),L */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, LOW_REGISTER(HL)); break; case HALTINSTRUCTION: /* HALT */ tStates += 4; sim_brk_pend[0] = FALSE; PC--; if (cpu_unit.flags & UNIT_CPU_STOPONHALT) { reason = STOP_HALT; goto end_decode; } sim_interval = 0; do_SIMH_sleep(); /* reduce CPU load in busy wait */ break; case 0x77: /* LD (HL),A */ tStates += 7; CHECK_BREAK_BYTE(HL); PutBYTE(HL, HIGH_REGISTER(AF)); break; case 0x78: /* LD A,B */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | (BC & ~0xff); break; case 0x79: /* LD A,C */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | ((BC & 0xff) << 8); break; case 0x7a: /* LD A,D */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | (DE & ~0xff); break; case 0x7b: /* LD A,E */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | ((DE & 0xff) << 8); break; case 0x7c: /* LD A,H */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | (HL & ~0xff); break; case 0x7d: /* LD A,L */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (AF & 0xff) | ((HL & 0xff) << 8); break; case 0x7e: /* LD A,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); SET_HIGH_REGISTER(AF, GetBYTE(HL)); break; case 0x7f: /* LD A,A */ tStates += 4; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x80: /* ADD A,B */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x81: /* ADD A,C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x82: /* ADD A,D */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x83: /* ADD A,E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x84: /* ADD A,H */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x85: /* ADD A,L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x86: /* ADD A,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x87: /* ADD A,A */ tStates += 4; sim_brk_pend[0] = FALSE; cbits = 2 * HIGH_REGISTER(AF); AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); break; case 0x88: /* ADC A,B */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x89: /* ADC A,C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8a: /* ADC A,D */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8b: /* ADC A,E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8c: /* ADC A,H */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8d: /* ADC A,L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8e: /* ADC A,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8f: /* ADC A,A */ tStates += 4; sim_brk_pend[0] = FALSE; cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); break; case 0x90: /* SUB B */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x91: /* SUB C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x92: /* SUB D */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x93: /* SUB E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x94: /* SUB H */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x95: /* SUB L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x96: /* SUB (HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x97: /* SUB A */ tStates += 4; sim_brk_pend[0] = FALSE; AF = (chiptype == CHIP_TYPE_Z80) ? 0x42 : 0x46; break; case 0x98: /* SBC A,B */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x99: /* SBC A,C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9a: /* SBC A,D */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9b: /* SBC A,E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9c: /* SBC A,H */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9d: /* SBC A,L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9e: /* SBC A,(HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9f: /* SBC A,A */ tStates += 4; sim_brk_pend[0] = FALSE; cbits = -TSTFLAG(C); AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); break; case 0xa0: /* AND B */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF & BC) >> 8) & 0xff]; break; case 0xa1: /* AND C */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & BC) & 0xff]; break; case 0xa2: /* AND D */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF & DE) >> 8) & 0xff]; break; case 0xa3: /* AND E */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & DE) & 0xff]; break; case 0xa4: /* AND H */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF & HL) >> 8) & 0xff]; break; case 0xa5: /* AND L */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & HL) & 0xff]; break; case 0xa6: /* AND (HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); AF = andTable[((AF >> 8) & GetBYTE(HL)) & 0xff]; break; case 0xa7: /* AND A */ tStates += 4; sim_brk_pend[0] = FALSE; AF = andTable[(AF >> 8) & 0xff]; break; case 0xa8: /* XOR B */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF ^ BC) >> 8) & 0xff]; break; case 0xa9: /* XOR C */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ BC) & 0xff]; break; case 0xaa: /* XOR D */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF ^ DE) >> 8) & 0xff]; break; case 0xab: /* XOR E */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ DE) & 0xff]; break; case 0xac: /* XOR H */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF ^ HL) >> 8) & 0xff]; break; case 0xad: /* XOR L */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ HL) & 0xff]; break; case 0xae: /* XOR (HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); AF = xororTable[((AF >> 8) ^ GetBYTE(HL)) & 0xff]; break; case 0xaf: /* XOR A */ tStates += 4; sim_brk_pend[0] = FALSE; AF = 0x44; break; case 0xb0: /* OR B */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF | BC) >> 8) & 0xff]; break; case 0xb1: /* OR C */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | BC) & 0xff]; break; case 0xb2: /* OR D */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF | DE) >> 8) & 0xff]; break; case 0xb3: /* OR E */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | DE) & 0xff]; break; case 0xb4: /* OR H */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF | HL) >> 8) & 0xff]; break; case 0xb5: /* OR L */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | HL) & 0xff]; break; case 0xb6: /* OR (HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); AF = xororTable[((AF >> 8) | GetBYTE(HL)) & 0xff]; break; case 0xb7: /* OR A */ tStates += 4; sim_brk_pend[0] = FALSE; AF = xororTable[(AF >> 8) & 0xff]; break; case 0xb8: /* CP B */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(BC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xb9: /* CP C */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(BC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xba: /* CP D */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(DE); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbb: /* CP E */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(DE); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbc: /* CP H */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbd: /* CP L */ tStates += 4; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbe: /* CP (HL) */ tStates += 7; CHECK_BREAK_BYTE(HL); temp = GetBYTE(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbf: /* CP A */ tStates += 4; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | (chiptype == CHIP_TYPE_Z80 ? 0x42 : 0x46)); break; case 0xc0: /* RET NZ */ if (TSTFLAG(Z)) { sim_brk_pend[0] = FALSE; tStates += 5; } else { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } break; case 0xc1: /* POP BC */ tStates += 10; CHECK_BREAK_WORD(SP); POP(BC); break; case 0xc2: /* JP NZ,nnnn */ sim_brk_pend[0] = FALSE; JPC(!TSTFLAG(Z)); /* also updates tStates */ break; case 0xc3: /* JP nnnn */ sim_brk_pend[0] = FALSE; JPC(1); /* also updates tStates */ break; case 0xc4: /* CALL NZ,nnnn */ CALLC(!TSTFLAG(Z)); /* also updates tStates */ break; case 0xc5: /* PUSH BC */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(BC); break; case 0xc6: /* ADD A,nn */ tStates += 7; sim_brk_pend[0] = FALSE; temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0xc7: /* RST 0 */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0; break; case 0xc8: /* RET Z */ if (TSTFLAG(Z)) { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } else { sim_brk_pend[0] = FALSE; tStates += 5; } break; case 0xc9: /* RET */ tStates += 10; CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); break; case 0xca: /* JP Z,nnnn */ sim_brk_pend[0] = FALSE; JPC(TSTFLAG(Z)); /* also updates tStates */ break; case 0xcb: /* CB prefix */ CHECK_CPU_8080; adr = HL; switch ((op = GetBYTE(PC)) & 7) { case 0: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = HIGH_REGISTER(BC); tStates += 8; break; case 1: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = LOW_REGISTER(BC); tStates += 8; break; case 2: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = HIGH_REGISTER(DE); tStates += 8; break; case 3: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = LOW_REGISTER(DE); tStates += 8; break; case 4: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = HIGH_REGISTER(HL); tStates += 8; break; case 5: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = LOW_REGISTER(HL); tStates += 8; break; case 6: CHECK_BREAK_BYTE(adr); ++PC; acu = GetBYTE(adr); tStateModifier = TRUE; tStates += 15; break; case 7: sim_brk_pend[0] = tStateModifier = FALSE; ++PC; acu = HIGH_REGISTER(AF); tStates += 8; break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ switch (op & 0x38) { case 0x00: /* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg1; case 0x08: /* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg1; case 0x10: /* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg1; case 0x18: /* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg1; case 0x20: /* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg1; case 0x28: /* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg1; case 0x30: /* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg1; case 0x38: /* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg1: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ } break; case 0x40: /* BIT */ if (tStateModifier) tStates -= 3; if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PutBYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xcc: /* CALL Z,nnnn */ CALLC(TSTFLAG(Z)); /* also updates tStates */ break; case 0xcd: /* CALL nnnn */ CALLC(1); /* also updates tStates */ break; case 0xce: /* ADC A,nn */ tStates += 7; sim_brk_pend[0] = FALSE; temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0xcf: /* RST 8 */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 8; break; case 0xd0: /* RET NC */ if (TSTFLAG(C)) { sim_brk_pend[0] = FALSE; tStates += 5; } else { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } break; case 0xd1: /* POP DE */ tStates += 10; CHECK_BREAK_WORD(SP); POP(DE); break; case 0xd2: /* JP NC,nnnn */ sim_brk_pend[0] = FALSE; JPC(!TSTFLAG(C)); /* also updates tStates */ break; case 0xd3: /* OUT (nn),A */ tStates += 11; sim_brk_pend[0] = FALSE; out(RAM_PP(PC), HIGH_REGISTER(AF)); break; case 0xd4: /* CALL NC,nnnn */ CALLC(!TSTFLAG(C)); /* also updates tStates */ break; case 0xd5: /* PUSH DE */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(DE); break; case 0xd6: /* SUB nn */ tStates += 7; sim_brk_pend[0] = FALSE; temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0xd7: /* RST 10H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x10; break; case 0xd8: /* RET C */ if (TSTFLAG(C)) { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } else { sim_brk_pend[0] = FALSE; tStates += 5; } break; case 0xd9: /* EXX */ tStates += 4; sim_brk_pend[0] = FALSE; CHECK_CPU_8080; temp = BC; BC = BC1_S; BC1_S = temp; temp = DE; DE = DE1_S; DE1_S = temp; temp = HL; HL = HL1_S; HL1_S = temp; break; case 0xda: /* JP C,nnnn */ sim_brk_pend[0] = FALSE; JPC(TSTFLAG(C)); /* also updates tStates */ break; case 0xdb: /* IN A,(nn) */ tStates += 11; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, in(RAM_PP(PC))); break; case 0xdc: /* CALL C,nnnn */ CALLC(TSTFLAG(C)); /* also updates tStates */ break; case 0xdd: /* DD prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x09: /* ADD IX,BC */ tStates += 15; sim_brk_pend[0] = FALSE; IX &= ADDRMASK; BC &= ADDRMASK; sum = IX + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; IX = sum; break; case 0x19: /* ADD IX,DE */ tStates += 15; sim_brk_pend[0] = FALSE; IX &= ADDRMASK; DE &= ADDRMASK; sum = IX + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; IX = sum; break; case 0x21: /* LD IX,nnnn */ tStates += 14; sim_brk_pend[0] = FALSE; IX = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),IX */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, IX); PC += 2; break; case 0x23: /* INC IX */ tStates += 10; sim_brk_pend[0] = FALSE; ++IX; break; case 0x24: /* INC IXH */ tStates += 9; sim_brk_pend[0] = FALSE; IX += 0x100; AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; break; case 0x25: /* DEC IXH */ tStates += 9; sim_brk_pend[0] = FALSE; IX -= 0x100; AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; break; case 0x26: /* LD IXH,nn */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, RAM_PP(PC)); break; case 0x29: /* ADD IX,IX */ tStates += 15; sim_brk_pend[0] = FALSE; IX &= ADDRMASK; sum = IX + IX; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; IX = sum; break; case 0x2a: /* LD IX,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); IX = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC IX */ tStates += 10; sim_brk_pend[0] = FALSE; --IX; break; case 0x2c: /* INC IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX) + 1; SET_LOW_REGISTER(IX, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x2d: /* DEC IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX) - 1; SET_LOW_REGISTER(IX, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x2e: /* LD IXL,nn */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, RAM_PP(PC)); break; case 0x34: /* INC (IX+dd) */ tStates += 23; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr) + 1; PutBYTE(adr, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x35: /* DEC (IX+dd) */ tStates += 23; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr) - 1; PutBYTE(adr, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x36: /* LD (IX+dd),nn */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, RAM_PP(PC)); break; case 0x39: /* ADD IX,SP */ tStates += 15; sim_brk_pend[0] = FALSE; IX &= ADDRMASK; SP &= ADDRMASK; sum = IX + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; IX = sum; break; case 0x44: /* LD B,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); break; case 0x45: /* LD B,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); break; case 0x46: /* LD B,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(BC, GetBYTE(adr)); break; case 0x4c: /* LD C,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); break; case 0x4d: /* LD C,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); break; case 0x4e: /* LD C,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(BC, GetBYTE(adr)); break; case 0x54: /* LD D,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); break; case 0x55: /* LD D,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); break; case 0x56: /* LD D,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(DE, GetBYTE(adr)); break; case 0x5c: /* LD E,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); break; case 0x5d: /* LD E,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); break; case 0x5e: /* LD E,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(DE, GetBYTE(adr)); break; case 0x60: /* LD IXH,B */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); break; case 0x61: /* LD IXH,C */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); break; case 0x62: /* LD IXH,D */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); break; case 0x63: /* LD IXH,E */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); break; case 0x64: /* LD IXH,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x65: /* LD IXH,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); break; case 0x66: /* LD H,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(HL, GetBYTE(adr)); break; case 0x67: /* LD IXH,A */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); break; case 0x68: /* LD IXL,B */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); break; case 0x69: /* LD IXL,C */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); break; case 0x6a: /* LD IXL,D */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); break; case 0x6b: /* LD IXL,E */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); break; case 0x6c: /* LD IXL,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); break; case 0x6d: /* LD IXL,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x6e: /* LD L,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(HL, GetBYTE(adr)); break; case 0x6f: /* LD IXL,A */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); break; case 0x70: /* LD (IX+dd),B */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(BC)); break; case 0x71: /* LD (IX+dd),C */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(BC)); break; case 0x72: /* LD (IX+dd),D */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(DE)); break; case 0x73: /* LD (IX+dd),E */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(DE)); break; case 0x74: /* LD (IX+dd),H */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(HL)); break; case 0x75: /* LD (IX+dd),L */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(HL)); break; case 0x77: /* LD (IX+dd),A */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(AF)); break; case 0x7c: /* LD A,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); break; case 0x7d: /* LD A,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); break; case 0x7e: /* LD A,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(AF, GetBYTE(adr)); break; case 0x84: /* ADD A,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x85: /* ADD A,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x86: /* ADD A,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8c: /* ADC A,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8d: /* ADC A,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8e: /* ADC A,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x96: /* SUB (IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x94: /* SUB IXH */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9c: /* SBC A,IXH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x95: /* SUB IXL */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9d: /* SBC A,IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x9e: /* SBC A,(IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xa4: /* AND IXH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = andTable[((AF & IX) >> 8) & 0xff]; break; case 0xa5: /* AND IXL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & IX) & 0xff]; break; case 0xa6: /* AND (IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = andTable[((AF >> 8) & GetBYTE(adr)) & 0xff]; break; case 0xac: /* XOR IXH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF ^ IX) >> 8) & 0xff]; break; case 0xad: /* XOR IXL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ IX) & 0xff]; break; case 0xae: /* XOR (IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = xororTable[((AF >> 8) ^ GetBYTE(adr)) & 0xff]; break; case 0xb4: /* OR IXH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF | IX) >> 8) & 0xff]; break; case 0xb5: /* OR IXL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | IX) & 0xff]; break; case 0xb6: /* OR (IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = xororTable[((AF >> 8) | GetBYTE(adr)) & 0xff]; break; case 0xbc: /* CP IXH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IX); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbd: /* CP IXL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IX); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbe: /* CP (IX+dd) */ tStates += 19; adr = IX + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xcb: /* CB prefix */ adr = IX + (int8) RAM_PP(PC); switch ((op = GetBYTE(PC)) & 7) { case 0: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(BC); break; case 1: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(BC); break; case 2: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(DE); break; case 3: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(DE); break; case 4: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(HL); break; case 5: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(HL); break; case 6: CHECK_BREAK_BYTE(adr); ++PC; acu = GetBYTE(adr); break; case 7: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(AF); break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ tStates += 23; switch (op & 0x38) { case 0x00: /* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg2; case 0x08: /* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg2; case 0x10: /* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg2; case 0x18: /* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg2; case 0x20: /* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg2; case 0x28: /* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg2; case 0x30: /* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg2; case 0x38: /* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg2: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ } break; case 0x40: /* BIT */ tStates += 20; if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ tStates += 23; temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ tStates += 23; temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PutBYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xe1: /* POP IX */ tStates += 14; CHECK_BREAK_WORD(SP); POP(IX); break; case 0xe3: /* EX (SP),IX */ tStates += 23; CHECK_BREAK_WORD(SP); temp = IX; POP(IX); PUSH(temp); break; case 0xe5: /* PUSH IX */ tStates += 15; CHECK_BREAK_WORD(SP - 2); PUSH(IX); break; case 0xe9: /* JP (IX) */ tStates += 8; sim_brk_pend[0] = FALSE; PCQ_ENTRY(PCX); PC = IX; break; case 0xf9: /* LD SP,IX */ tStates += 10; sim_brk_pend[0] = FALSE; SP = IX; break; default: /* ignore DD */ sim_brk_pend[0] = FALSE; CHECK_CPU_Z80; PC--; } break; case 0xde: /* SBC A,nn */ tStates += 7; sim_brk_pend[0] = FALSE; temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0xdf: /* RST 18H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x18; break; case 0xe0: /* RET PO */ if (TSTFLAG(P)) { sim_brk_pend[0] = FALSE; tStates += 5; } else { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } break; case 0xe1: /* POP HL */ tStates += 10; CHECK_BREAK_WORD(SP); POP(HL); break; case 0xe2: /* JP PO,nnnn */ sim_brk_pend[0] = FALSE; JPC(!TSTFLAG(P)); /* also updates tStates */ break; case 0xe3: /* EX (SP),HL */ tStates += 19; CHECK_BREAK_WORD(SP); temp = HL; POP(HL); PUSH(temp); break; case 0xe4: /* CALL PO,nnnn */ CALLC(!TSTFLAG(P)); /* also updates tStates */ break; case 0xe5: /* PUSH HL */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(HL); break; case 0xe6: /* AND nn */ tStates += 7; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & RAM_PP(PC)) & 0xff]; break; case 0xe7: /* RST 20H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x20; break; case 0xe8: /* RET PE */ if (TSTFLAG(P)) { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } else { sim_brk_pend[0] = FALSE; tStates += 5; } break; case 0xe9: /* JP (HL) */ tStates += 4; sim_brk_pend[0] = FALSE; PCQ_ENTRY(PCX); PC = HL; break; case 0xea: /* JP PE,nnnn */ sim_brk_pend[0] = FALSE; JPC(TSTFLAG(P)); /* also updates tStates */ break; case 0xeb: /* EX DE,HL */ tStates += 4; sim_brk_pend[0] = FALSE; temp = HL; HL = DE; DE = temp; break; case 0xec: /* CALL PE,nnnn */ CALLC(TSTFLAG(P)); /* also updates tStates */ break; case 0xed: /* ED prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x40: /* IN B,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(BC, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x41: /* OUT (C),B */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); break; case 0x42: /* SBC HL,BC */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; BC &= ADDRMASK; sum = HL - BC - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x43: /* LD (nnnn),BC */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, BC); PC += 2; break; case 0x44: /* NEG */ case 0x4C: /* NEG, unofficial */ case 0x54: /* NEG, unofficial */ case 0x5C: /* NEG, unofficial */ case 0x64: /* NEG, unofficial */ case 0x6C: /* NEG, unofficial */ case 0x74: /* NEG, unofficial */ case 0x7C: /* NEG, unofficial */ tStates += 8; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(AF); AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; break; case 0x45: /* RETN */ case 0x55: /* RETN, unofficial */ case 0x5D: /* RETN, unofficial */ case 0x65: /* RETN, unofficial */ case 0x6D: /* RETN, unofficial */ case 0x75: /* RETN, unofficial */ case 0x7D: /* RETN, unofficial */ tStates += 14; IFF_S |= IFF_S >> 1; CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); break; case 0x46: /* IM 0 */ tStates += 8; sim_brk_pend[0] = FALSE; /* interrupt mode 0 */ break; case 0x47: /* LD I,A */ tStates += 9; sim_brk_pend[0] = FALSE; IR_S = (IR_S & 0xff) | (AF & ~0xff); break; case 0x48: /* IN C,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x49: /* OUT (C),C */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), LOW_REGISTER(BC)); break; case 0x4a: /* ADC HL,BC */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; BC &= ADDRMASK; sum = HL + BC + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; HL = sum; break; case 0x4b: /* LD BC,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); BC = GET_WORD(temp); PC += 2; break; case 0x4d: /* RETI */ tStates += 14; IFF_S |= IFF_S >> 1; CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); break; case 0x4f: /* LD R,A */ tStates += 9; sim_brk_pend[0] = FALSE; IR_S = (IR_S & ~0xff) | ((AF >> 8) & 0xff); break; case 0x50: /* IN D,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(DE, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x51: /* OUT (C),D */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); break; case 0x52: /* SBC HL,DE */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; DE &= ADDRMASK; sum = HL - DE - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x53: /* LD (nnnn),DE */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, DE); PC += 2; break; case 0x56: /* IM 1 */ tStates += 8; sim_brk_pend[0] = FALSE; /* interrupt mode 1 */ break; case 0x57: /* LD A,I */ tStates += 9; sim_brk_pend[0] = FALSE; AF = (AF & 0x29) | (IR_S & ~0xff) | ((IR_S >> 8) & 0x80) | (((IR_S & ~0xff) == 0) << 6) | ((IFF_S & 2) << 1); break; case 0x58: /* IN E,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x59: /* OUT (C),E */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), LOW_REGISTER(DE)); break; case 0x5a: /* ADC HL,DE */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; DE &= ADDRMASK; sum = HL + DE + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; HL = sum; break; case 0x5b: /* LD DE,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); DE = GET_WORD(temp); PC += 2; break; case 0x5e: /* IM 2 */ tStates += 8; sim_brk_pend[0] = FALSE; /* interrupt mode 2 */ break; case 0x5f: /* LD A,R */ tStates += 9; sim_brk_pend[0] = FALSE; AF = (AF & 0x29) | ((IR_S & 0xff) << 8) | (IR_S & 0x80) | (((IR_S & 0xff) == 0) << 6) | ((IFF_S & 2) << 1); break; case 0x60: /* IN H,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(HL, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x61: /* OUT (C),H */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); break; case 0x62: /* SBC HL,HL */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; sum = HL - HL - TSTFLAG(C); AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80DupTable[(sum >> 8) & 0x1ff]; HL = sum; break; case 0x63: /* LD (nnnn),HL */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, HL); PC += 2; break; case 0x67: /* RRD */ tStates += 18; sim_brk_pend[0] = FALSE; temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); PutBYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); break; case 0x68: /* IN L,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x69: /* OUT (C),L */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), LOW_REGISTER(HL)); break; case 0x6a: /* ADC HL,HL */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; sum = HL + HL + TSTFLAG(C); AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80DupTable[sum >> 8]; HL = sum; break; case 0x6b: /* LD HL,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); HL = GET_WORD(temp); PC += 2; break; case 0x6f: /* RLD */ tStates += 18; sim_brk_pend[0] = FALSE; temp = GetBYTE(HL); acu = HIGH_REGISTER(AF); PutBYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); break; case 0x70: /* IN (C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(temp, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x71: /* OUT (C),0 */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), 0); break; case 0x72: /* SBC HL,SP */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; SP &= ADDRMASK; sum = HL - SP - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x73: /* LD (nnnn),SP */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, SP); PC += 2; break; case 0x78: /* IN A,(C) */ tStates += 12; sim_brk_pend[0] = FALSE; temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(AF, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x79: /* OUT (C),A */ tStates += 12; sim_brk_pend[0] = FALSE; out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); break; case 0x7a: /* ADC HL,SP */ tStates += 15; sim_brk_pend[0] = FALSE; HL &= ADDRMASK; SP &= ADDRMASK; sum = HL + SP + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; HL = sum; break; case 0x7b: /* LD SP,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); SP = GET_WORD(temp); PC += 2; break; case 0xa0: /* LDI */ tStates += 16; CHECK_BREAK_TWO_BYTES(HL, DE); acu = RAM_PP(HL); PUT_BYTE_PP(DE, acu); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | (((--BC & ADDRMASK) != 0) << 2); break; case 0xa1: /* CPI */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = HIGH_REGISTER(AF); temp = RAM_PP(HL); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | ((--BC & ADDRMASK) != 0) << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. INI/INIR/IND/INDR use the C flag in stead of the L register. There is a catch though, because not the value of C is used, but C + 1 if it's INI/INIR or C - 1 if it's IND/INDR. So, first of all INI/INIR: HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ case 0xa2: /* INI */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = in(LOW_REGISTER(BC)); PutBYTE(HL, acu); ++HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L after the increment or decrement of HL; add the value written to the I/O port to; call that k for now. If k > 255, then the CF and HF flags are set. The PF flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. HF and CF Both set if ((HL) + L > 255) PF The parity of ((((HL) + L) & 7) xor B) */ case 0xa3: /* OUTI */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = GetBYTE(HL); out(LOW_REGISTER(BC), acu); ++HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); break; case 0xa8: /* LDD */ tStates += 16; CHECK_BREAK_TWO_BYTES(HL, DE); acu = RAM_MM(HL); PUT_BYTE_MM(DE, acu); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | (((--BC & ADDRMASK) != 0) << 2); break; case 0xa9: /* CPD */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = HIGH_REGISTER(AF); temp = RAM_MM(HL); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | ((--BC & ADDRMASK) != 0) << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. INI/INIR/IND/INDR use the C flag in stead of the L register. There is a catch though, because not the value of C is used, but C + 1 if it's INI/INIR or C - 1 if it's IND/INDR. And last IND/INDR: HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ case 0xaa: /* IND */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = in(LOW_REGISTER(BC)); PutBYTE(HL, acu); --HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); break; case 0xab: /* OUTD */ tStates += 16; CHECK_BREAK_BYTE(HL); acu = GetBYTE(HL); out(LOW_REGISTER(BC), acu); --HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); break; case 0xb0: /* LDIR */ tStates -= 5; acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { tStates += 21; CHECK_BREAK_TWO_BYTES(HL, DE); acu = RAM_PP(HL); PUT_BYTE_PP(DE, acu); } while (--BC); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); break; case 0xb1: /* CPIR */ tStates -= 5; acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { tStates += 21; CHECK_BREAK_BYTE(HL); temp = RAM_PP(HL); op = --BC != 0; sum = acu - temp; } while (op && sum != 0); cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | op << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; case 0xb2: /* INIR */ tStates -= 5; temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { tStates += 21; CHECK_BREAK_BYTE(HL); acu = in(LOW_REGISTER(BC)); PutBYTE(HL, acu); ++HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); break; case 0xb3: /* OTIR */ tStates -= 5; temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { tStates += 21; CHECK_BREAK_BYTE(HL); acu = GetBYTE(HL); out(LOW_REGISTER(BC), acu); ++HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO(LOW_REGISTER(HL)); break; case 0xb8: /* LDDR */ tStates -= 5; BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { tStates += 21; CHECK_BREAK_TWO_BYTES(HL, DE); acu = RAM_MM(HL); PUT_BYTE_MM(DE, acu); } while (--BC); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); break; case 0xb9: /* CPDR */ tStates -= 5; acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { tStates += 21; CHECK_BREAK_BYTE(HL); temp = RAM_MM(HL); op = --BC != 0; sum = acu - temp; } while (op && sum != 0); cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | op << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; case 0xba: /* INDR */ tStates -= 5; temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { tStates += 21; CHECK_BREAK_BYTE(HL); acu = in(LOW_REGISTER(BC)); PutBYTE(HL, acu); --HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); break; case 0xbb: /* OTDR */ tStates -= 5; temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { tStates += 21; CHECK_BREAK_BYTE(HL); acu = GetBYTE(HL); out(LOW_REGISTER(BC), acu); --HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO(LOW_REGISTER(HL)); break; default: /* ignore ED and following byte */ sim_brk_pend[0] = FALSE; CHECK_CPU_Z80; } break; case 0xee: /* XOR nn */ tStates += 7; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; break; case 0xef: /* RST 28H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x28; break; case 0xf0: /* RET P */ if (TSTFLAG(S)) { sim_brk_pend[0] = FALSE; tStates += 5; } else { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } break; case 0xf1: /* POP AF */ tStates += 10; CHECK_BREAK_WORD(SP); POP(AF); break; case 0xf2: /* JP P,nnnn */ sim_brk_pend[0] = FALSE; JPC(!TSTFLAG(S)); /* also updates tStates */ break; case 0xf3: /* DI */ tStates += 4; sim_brk_pend[0] = FALSE; IFF_S = 0; break; case 0xf4: /* CALL P,nnnn */ CALLC(!TSTFLAG(S)); /* also updates tStates */ break; case 0xf5: /* PUSH AF */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(AF); break; case 0xf6: /* OR nn */ tStates += 7; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; break; case 0xf7: /* RST 30H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x30; break; case 0xf8: /* RET M */ if (TSTFLAG(S)) { CHECK_BREAK_WORD(SP); PCQ_ENTRY(PCX); POP(PC); tStates += 11; } else { sim_brk_pend[0] = FALSE; tStates += 5; } break; case 0xf9: /* LD SP,HL */ tStates += 6; sim_brk_pend[0] = FALSE; SP = HL; break; case 0xfa: /* JP M,nnnn */ sim_brk_pend[0] = FALSE; JPC(TSTFLAG(S)); /* also updates tStates */ break; case 0xfb: /* EI */ tStates += 4; sim_brk_pend[0] = FALSE; IFF_S = 3; break; case 0xfc: /* CALL M,nnnn */ CALLC(TSTFLAG(S)); /* also updates tStates */ break; case 0xfd: /* FD prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x09: /* ADD IY,BC */ tStates += 15; sim_brk_pend[0] = FALSE; IY &= ADDRMASK; BC &= ADDRMASK; sum = IY + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; IY = sum; break; case 0x19: /* ADD IY,DE */ tStates += 15; sim_brk_pend[0] = FALSE; IY &= ADDRMASK; DE &= ADDRMASK; sum = IY + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; IY = sum; break; case 0x21: /* LD IY,nnnn */ tStates += 14; sim_brk_pend[0] = FALSE; IY = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),IY */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); PutWORD(temp, IY); PC += 2; break; case 0x23: /* INC IY */ tStates += 10; sim_brk_pend[0] = FALSE; ++IY; break; case 0x24: /* INC IYH */ tStates += 9; sim_brk_pend[0] = FALSE; IY += 0x100; AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; break; case 0x25: /* DEC IYH */ tStates += 9; sim_brk_pend[0] = FALSE; IY -= 0x100; AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; break; case 0x26: /* LD IYH,nn */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, RAM_PP(PC)); break; case 0x29: /* ADD IY,IY */ tStates += 15; sim_brk_pend[0] = FALSE; IY &= ADDRMASK; sum = IY + IY; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; IY = sum; break; case 0x2a: /* LD IY,(nnnn) */ tStates += 20; temp = GET_WORD(PC); CHECK_BREAK_WORD(temp); IY = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC IY */ tStates += 10; sim_brk_pend[0] = FALSE; --IY; break; case 0x2c: /* INC IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY) + 1; SET_LOW_REGISTER(IY, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x2d: /* DEC IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY) - 1; SET_LOW_REGISTER(IY, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x2e: /* LD IYL,nn */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, RAM_PP(PC)); break; case 0x34: /* INC (IY+dd) */ tStates += 23; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr) + 1; PutBYTE(adr, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x35: /* DEC (IY+dd) */ tStates += 23; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr) - 1; PutBYTE(adr, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x36: /* LD (IY+dd),nn */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, RAM_PP(PC)); break; case 0x39: /* ADD IY,SP */ tStates += 15; sim_brk_pend[0] = FALSE; IY &= ADDRMASK; SP &= ADDRMASK; sum = IY + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; IY = sum; break; case 0x44: /* LD B,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); break; case 0x45: /* LD B,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); break; case 0x46: /* LD B,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(BC, GetBYTE(adr)); break; case 0x4c: /* LD C,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); break; case 0x4d: /* LD C,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); break; case 0x4e: /* LD C,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(BC, GetBYTE(adr)); break; case 0x54: /* LD D,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); break; case 0x55: /* LD D,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); break; case 0x56: /* LD D,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(DE, GetBYTE(adr)); break; case 0x5c: /* LD E,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); break; case 0x5d: /* LD E,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); break; case 0x5e: /* LD E,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(DE, GetBYTE(adr)); break; case 0x60: /* LD IYH,B */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); break; case 0x61: /* LD IYH,C */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); break; case 0x62: /* LD IYH,D */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); break; case 0x63: /* LD IYH,E */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); break; case 0x64: /* LD IYH,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x65: /* LD IYH,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); break; case 0x66: /* LD H,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(HL, GetBYTE(adr)); break; case 0x67: /* LD IYH,A */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); break; case 0x68: /* LD IYL,B */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); break; case 0x69: /* LD IYL,C */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); break; case 0x6a: /* LD IYL,D */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); break; case 0x6b: /* LD IYL,E */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); break; case 0x6c: /* LD IYL,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); break; case 0x6d: /* LD IYL,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; /* nop */ break; case 0x6e: /* LD L,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_LOW_REGISTER(HL, GetBYTE(adr)); break; case 0x6f: /* LD IYL,A */ tStates += 9; sim_brk_pend[0] = FALSE; SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); break; case 0x70: /* LD (IY+dd),B */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(BC)); break; case 0x71: /* LD (IY+dd),C */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(BC)); break; case 0x72: /* LD (IY+dd),D */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(DE)); break; case 0x73: /* LD (IY+dd),E */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(DE)); break; case 0x74: /* LD (IY+dd),H */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(HL)); break; case 0x75: /* LD (IY+dd),L */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, LOW_REGISTER(HL)); break; case 0x77: /* LD (IY+dd),A */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); PutBYTE(adr, HIGH_REGISTER(AF)); break; case 0x7c: /* LD A,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); break; case 0x7d: /* LD A,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); break; case 0x7e: /* LD A,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); SET_HIGH_REGISTER(AF, GetBYTE(adr)); break; case 0x84: /* ADD A,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x85: /* ADD A,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x86: /* ADD A,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8c: /* ADC A,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8d: /* ADC A,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8e: /* ADC A,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x96: /* SUB (IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x94: /* SUB IYH */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9c: /* SBC A,IYH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x95: /* SUB IYL */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9d: /* SBC A,IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x9e: /* SBC A,(IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xa4: /* AND IYH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = andTable[((AF & IY) >> 8) & 0xff]; break; case 0xa5: /* AND IYL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = andTable[((AF >> 8) & IY) & 0xff]; break; case 0xa6: /* AND (IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = andTable[((AF >> 8) & GetBYTE(adr)) & 0xff]; break; case 0xac: /* XOR IYH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF ^ IY) >> 8) & 0xff]; break; case 0xad: /* XOR IYL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) ^ IY) & 0xff]; break; case 0xae: /* XOR (IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = xororTable[((AF >> 8) ^ GetBYTE(adr)) & 0xff]; break; case 0xb4: /* OR IYH */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF | IY) >> 8) & 0xff]; break; case 0xb5: /* OR IYL */ tStates += 9; sim_brk_pend[0] = FALSE; AF = xororTable[((AF >> 8) | IY) & 0xff]; break; case 0xb6: /* OR (IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); AF = xororTable[((AF >> 8) | GetBYTE(adr)) & 0xff]; break; case 0xbc: /* CP IYH */ tStates += 9; sim_brk_pend[0] = FALSE; temp = HIGH_REGISTER(IY); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbd: /* CP IYL */ tStates += 9; sim_brk_pend[0] = FALSE; temp = LOW_REGISTER(IY); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbe: /* CP (IY+dd) */ tStates += 19; adr = IY + (int8) RAM_PP(PC); CHECK_BREAK_BYTE(adr); temp = GetBYTE(adr); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xcb: /* CB prefix */ adr = IY + (int8) RAM_PP(PC); switch ((op = GetBYTE(PC)) & 7) { case 0: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(BC); break; case 1: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(BC); break; case 2: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(DE); break; case 3: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(DE); break; case 4: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(HL); break; case 5: sim_brk_pend[0] = FALSE; ++PC; acu = LOW_REGISTER(HL); break; case 6: CHECK_BREAK_BYTE(adr); ++PC; acu = GetBYTE(adr); break; case 7: sim_brk_pend[0] = FALSE; ++PC; acu = HIGH_REGISTER(AF); break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ tStates += 23; switch (op & 0x38) { case 0x00: /* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg3; case 0x08: /* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg3; case 0x10: /* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg3; case 0x18: /* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg3; case 0x20: /* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg3; case 0x28: /* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg3; case 0x30: /* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg3; case 0x38: /* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg3: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ } break; case 0x40: /* BIT */ tStates += 20; if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ tStates += 23; temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ tStates += 23; temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PutBYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xe1: /* POP IY */ tStates += 14; CHECK_BREAK_WORD(SP); POP(IY); break; case 0xe3: /* EX (SP),IY */ tStates += 23; CHECK_BREAK_WORD(SP); temp = IY; POP(IY); PUSH(temp); break; case 0xe5: /* PUSH IY */ tStates += 15; CHECK_BREAK_WORD(SP - 2); PUSH(IY); break; case 0xe9: /* JP (IY) */ tStates += 8; sim_brk_pend[0] = FALSE; PCQ_ENTRY(PCX); PC = IY; break; case 0xf9: /* LD SP,IY */ tStates += 10; sim_brk_pend[0] = FALSE; SP = IY; break; default: /* ignore FD */ sim_brk_pend[0] = FALSE; CHECK_CPU_Z80; PC--; } break; case 0xfe: /* CP nn */ tStates += 7; sim_brk_pend[0] = FALSE; temp = RAM_PP(PC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xff: /* RST 38H */ tStates += 11; CHECK_BREAK_WORD(SP - 2); PUSH(PC); PCQ_ENTRY(PCX); PC = 0x38; } } /* It we stopped processing instructions because of a switch to the other * CPU, then fixup the reason code. */ if (switch_cpu_now == FALSE) { reason = SCPE_OK; } end_decode: /* simulation halted */ PC_S = ((reason == STOP_OPCODE) || (reason == STOP_MEM)) ? PCX : (PC & ADDRMASK); pcq_r -> qptr = pcq_p; /* update pc q ptr */ AF_S = AF; BC_S = BC; DE_S = DE; HL_S = HL; IX_S = IX; IY_S = IY; SP_S = SP; executedTStates = tStates; return reason; } /* reset routine */ static t_stat cpu_reset(DEVICE *dptr) { extern uint32 sim_brk_types, sim_brk_dflt; /* breakpoint info */ int32 i; AF_S = AF1_S = 0; BC_S = DE_S = HL_S = 0; BC1_S = DE1_S = HL1_S = 0; IR_S = IX_S = IY_S = SP_S = 0; IFF_S = 3; setBankSelect(0); cpu8086reset(); sim_brk_types = (SWMASK('E') | SWMASK('I') | SWMASK('M')); sim_brk_dflt = SWMASK('E'); for (i = 0; i < PCQ_SIZE; i++) pcq[i] = 0; pcq_p = 0; pcq_r = find_reg("PCQ", NULL, dptr); if (pcq_r) pcq_r -> qptr = 0; else return SCPE_IERR; return SCPE_OK; } t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM) { int32 i; if (addr & (PAGESIZE - 1)) return SCPE_IERR; for (i = 0; i < size; i++) { if (makeROM && ((i & (PAGESIZE - 1)) == 0)) mmu_table[(i + addr) >> LOG2PAGESIZE] = ROM_PAGE; M[i + addr] = bootrom[i] & 0xff; } return SCPE_OK; } /* memory examine */ static t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { int32 oldBankSelect; if (chiptype == CHIP_TYPE_8086) *vptr = GetBYTEExtended(addr); else { oldBankSelect = getBankSelect(); setBankSelect((addr >> MAXBANKSIZELOG2) & BANKMASK); *vptr = GetBYTE(addr & ADDRMASK); setBankSelect(oldBankSelect); } return SCPE_OK; } /* memory deposit */ static t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) { int32 oldBankSelect; if (chiptype == CHIP_TYPE_8086) PutBYTEExtended(addr, val); else { oldBankSelect = getBankSelect(); setBankSelect((addr >> MAXBANKSIZELOG2) & BANKMASK); PutBYTE(addr & ADDRMASK, val); setBankSelect(oldBankSelect); } return SCPE_OK; } struct cpuflag { int32 mask; /* bit mask within CPU status register */ char name; /* character to print if flag is set */ }; typedef struct cpuflag CPUFLAG; static CPUFLAG cpuflags8086[] = { {1 << 11, 'O'}, {1 << 10, 'D'}, {1 << 9, 'I'}, {1 << 8, 'T'}, {1 << 7, 'S'}, {1 << 6, 'Z'}, {1 << 4, 'A'}, {1 << 2, 'P'}, {1 << 0, 'C'}, {0, 0} /* last mask must be 0 */ }; static CPUFLAG cpuflags8080[] = { {1 << 7, 'S'}, {1 << 6, 'Z'}, {1 << 4, 'A'}, {1 << 3, 'P'}, {1 << 1, 'N'}, {1 << 0, 'C'}, {0, 0} /* last mask must be 0 */ }; static CPUFLAG cpuflagsZ80[] = { {1 << 7, 'S'}, {1 << 6, 'Z'}, {1 << 4, 'A'}, {1 << 3, 'V'}, {1 << 1, 'N'}, {1 << 0, 'C'}, {0, 0} /* last mask must be 0 */ }; /* needs to be set for each chiptype <= MAX_CHIP_TYPE */ static char *chipTypeToString[] = { "8080", "Z80", "8086" }; static int32 *flagregister[] = { &AF_S, &AF_S, &FLAGS_S }; static CPUFLAG *cpuflags[] = { cpuflags8080, cpuflagsZ80, cpuflags8086 }; /* needs to be set for each ramtype <= MAX_RAM_TYPE */ static char *ramTypeToString[] = { "AZ80", "HRAM", "VRAM", "CRAM" }; static t_stat chip_show(FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf(st, cpu_unit.flags & UNIT_CPU_OPSTOP ? "ITRAP, " : "NOITRAP, "); if (chiptype <= MAX_CHIP_TYPE) fprintf(st, chipTypeToString[chiptype]); fprintf(st, ", "); if (ramtype <= MAX_RAM_TYPE) fprintf(st, ramTypeToString[ramtype]); return SCPE_OK; } static t_stat cpu_show(FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 i, maxBanks, first = TRUE; MDEV m; maxBanks = ((cpu_unit.flags & UNIT_CPU_BANKED) || (chiptype == CHIP_TYPE_8086)) ? MAXBANKS : 1; fprintf(st, "VERBOSE,\n "); for (i = 0; i < 4; i++) fprintf(st, "0123456789ABCDEF"); fprintf(st, " [16k]"); for (i = 0; i < (maxBanks * (MAXBANKSIZE >> LOG2PAGESIZE)); i++) { if ((i & 0x3f) == 0) fprintf(st, "\n%05X: ", (i << LOG2PAGESIZE)); m = mmu_table[i]; if (m.isRAM) fprintf(st, "W"); else if (m.isEmpty) fprintf(st, "U"); else if (m.routine) fprintf(st, "M"); else fprintf(st, "R"); } fprintf(st, ",\n0x["); /* show which ports are assigned */ for (i = 0; i < 256; i++) if (dev_table[i].routine != &nulldev) { if (first) first = FALSE; else fprintf(st, " "); fprintf(st, "%02X", i); } fprintf(st, "]"); if (chiptype <= MAX_CHIP_TYPE) { first = TRUE; /* show verbose CPU flags */ for (i = 0; cpuflags[chiptype][i].mask; i++) if (*flagregister[chiptype] & cpuflags[chiptype][i].mask) { if (first) { first = FALSE; fprintf(st, " "); } fprintf(st, "%c", cpuflags[chiptype][i].name); } } return SCPE_OK; } static void cpu_clear(void) { uint32 i; for (i = 0; i < MAXMEMORY; i++) M[i] = 0; for (i = 0; i < (MAXMEMORY >> LOG2PAGESIZE); i++) mmu_table[i] = RAM_PAGE; for (i = (MEMORYSIZE >> LOG2PAGESIZE); i < (MAXMEMORY >> LOG2PAGESIZE); i++) mmu_table[i] = EMPTY_PAGE; if (cpu_unit.flags & UNIT_CPU_ALTAIRROM) install_ALTAIRbootROM(); } static t_stat cpu_clear_command(UNIT *uptr, int32 value, char *cptr, void *desc) { cpu_clear(); return SCPE_OK; } static t_stat cpu_set_altairrom(UNIT *uptr, int32 value, char *cptr, void *desc) { install_ALTAIRbootROM(); return SCPE_OK; } static t_stat cpu_set_noaltairrom(UNIT *uptr, int32 value, char *cptr, void *desc) { mmu_table[ALTAIR_ROM_LOW >> LOG2PAGESIZE] = MEMORYSIZE < MAXBANKSIZE ? EMPTY_PAGE : RAM_PAGE; return SCPE_OK; } static t_stat cpu_set_nommu(UNIT *uptr, int32 value, char *cptr, void *desc) { if (chiptype == CHIP_TYPE_8086) { printf("Cannot switch off MMU for 8086 CPU.\n"); return SCPE_ARG; } if (cpu_unit.flags & UNIT_CPU_BANKED) { printf("Cannot switch off MMU for banked memory.\n"); return SCPE_ARG; } if (((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) && (MEMORYSIZE < MAXBANKSIZE)) { printf("Cannot switch off MMU when memory is %iKB < %iKB.\n", MEMORYSIZE >> KBLOG2, MAXBANKSIZE >> KBLOG2); return SCPE_ARG; } return SCPE_OK; } static t_stat cpu_set_banked(UNIT *uptr, int32 value, char *cptr, void *desc) { if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { if (MEMORYSIZE <= MAXBANKSIZE) previousCapacity = MEMORYSIZE; MEMORYSIZE = MAXMEMORY; cpu_dev.awidth = MAXBANKSIZELOG2 + MAXBANKSLOG2; cpu_clear(); } else if (chiptype == CHIP_TYPE_8086) { printf("Cannot use banked memory for 8086 CPU.\n"); return SCPE_ARG; } return SCPE_OK; } static t_stat cpu_set_nonbanked(UNIT *uptr, int32 value, char *cptr, void *desc) { if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { MEMORYSIZE = previousCapacity; cpu_dev.awidth = MAXBANKSIZELOG2; cpu_clear(); } return SCPE_OK; } static int32 bankseldev(const int32 port, const int32 io, const int32 data) { if (io) { switch(ramtype) { case 1: if (data & 0x40) { printf("HRAM: Parity %s" NLP, data & 1 ? "ON" : "OFF"); } else { printf("HRAM BANKSEL=%02x" NLP, data); } break; case 2: /* printf("VRAM BANKSEL=%02x" NLP, data);*/ switch(data & 0xFF) { case 0x01: /* case 0x41: // OASIS uses this for some reason? */ setBankSelect(0); break; case 0x02: /* case 0x42: // OASIS uses this for some reason? */ setBankSelect(1); break; case 0x04: setBankSelect(2); break; case 0x08: setBankSelect(3); break; case 0x10: setBankSelect(4); break; case 0x20: setBankSelect(5); break; case 0x40: setBankSelect(6); break; case 0x80: setBankSelect(7); break; default: /* printf("Invalid bank select 0x%02x for VRAM" NLP, data);*/ break; } break; case 3: /* printf(ADDRESS_FORMAT " CRAM BANKSEL=%02x" NLP, PCX, data); */ switch(data & 0x7F) { case 0x01: setBankSelect(0); break; case 0x02: setBankSelect(1); break; case 0x04: setBankSelect(2); break; case 0x08: setBankSelect(3); break; case 0x10: setBankSelect(4); break; case 0x20: setBankSelect(5); break; case 0x40: setBankSelect(6); break; /* case 0x80: */ /* setBankSelect(7); */ /* break; */ default: printf("Invalid bank select 0x%02x for CRAM" NLP, data); break; } break; case 0: default: break; } return 0; } else { return(0xFF); } } static void cpu_set_chiptype_short(int32 value, uint32 need_cpu_clear) { extern REG *sim_PC; if ((chiptype == value) || (chiptype > MAX_CHIP_TYPE)) return; /* nothing to do */ if ((chiptype == CHIP_TYPE_8080) && (value == CHIP_TYPE_Z80) || (chiptype == CHIP_TYPE_Z80) && (value == CHIP_TYPE_8080)) { chiptype = value; return; } chiptype = value; if (chiptype == CHIP_TYPE_8086) { if (MEMORYSIZE <= MAXBANKSIZE) previousCapacity = MEMORYSIZE; MEMORYSIZE = MAXMEMORY; cpu_unit.flags &= ~(UNIT_CPU_BANKED | UNIT_CPU_ALTAIRROM); cpu_unit.flags |= UNIT_CPU_MMU; cpu_dev.awidth = MAXBANKSIZELOG2 + MAXBANKSLOG2; if (need_cpu_clear) cpu_clear(); sim_PC = &cpu_reg[7]; } else if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { MEMORYSIZE = previousCapacity; cpu_dev.awidth = MAXBANKSIZELOG2; if (need_cpu_clear) cpu_clear(); sim_PC = &cpu_reg[6]; } } static t_stat cpu_set_chiptype(UNIT *uptr, int32 value, char *cptr, void *desc) { cpu_set_chiptype_short(value, TRUE); return SCPE_OK; } static int32 switchcpu_io(const int32 port, const int32 io, const int32 data) { int32 new_chiptype = 0; if (io == 0) { /* Read, switch CPU */ switch(chiptype) { case CHIP_TYPE_8080: case CHIP_TYPE_Z80: if (cpu_unit.flags & UNIT_CPU_VERBOSE) { printf("CPU: " ADDRESS_FORMAT " SWITCH(port=%02x) to 8086" NLP, PCX, port); } new_chiptype = CHIP_TYPE_8086; switch_cpu_now = FALSE; /* hharte */ break; case CHIP_TYPE_8086: if (cpu_unit.flags & UNIT_CPU_VERBOSE) { printf("CPU: " ADDRESS_FORMAT " SWITCH(port=%02x) to 8085/Z80" NLP, PCX, port); } new_chiptype = CHIP_TYPE_Z80; switch_cpu_now = FALSE; /* hharte */ break; default: printf("%s: invalid chiptype: %d\n", __FUNCTION__, chiptype); break; } cpu_set_chiptype_short(new_chiptype, FALSE); return(0xFF); /* Return High-Z Data */ } else { printf("%s: Set EXT_ADDR=%02x\n", __FUNCTION__, data); } return 0; } static t_stat cpu_show_switcher(FILE *st, UNIT *uptr, int32 val, void *desc) { if ((cpu_unit.flags & UNIT_CPU_SWITCHER) && (switcherPort >= 0)) fprintf(st, "SWITCHER=0x%02x", switcherPort); else fprintf(st, "NOSWITCHER"); return SCPE_OK; } static t_stat cpu_set_switcher(UNIT *uptr, int32 value, char *cptr, void *desc) { struct idev safe; switcherPort &= 0xff; safe = dev_table[switcherPort]; if (sim_map_resource(switcherPort, 1, RESOURCE_TYPE_IO, &switchcpu_io, FALSE)) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, switcherPort); return SCPE_ARG; } oldSwitcherDevice = safe; return SCPE_OK; } static t_stat cpu_reset_switcher(UNIT *uptr, int32 value, char *cptr, void *desc) { if (sim_map_resource(switcherPort, 1, RESOURCE_TYPE_IO, oldSwitcherDevice.routine, FALSE)) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, switcherPort); return SCPE_ARG; } return SCPE_OK; } static t_stat cpu_set_ramtype(UNIT *uptr, int32 value, char *cptr, void *desc) { if (value == ramtype) { if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("RAM Selection unchanged\n"); return SCPE_OK; } switch(ramtype) { case 1: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Unmapping NorthStar HRAM\n"); sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE); break; case 2: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Unmapping Vector RAM\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE); break; case 3: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Unmapping Cromemco RAM\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE); break; case 0: default: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Unmapping AltairZ80 RAM\n"); break; } switch(value) { case 1: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("NorthStar HRAM Selected\n"); sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE); break; case 2: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Vector RAM Selected\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE); break; case 3: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("Cromemco RAM Selected\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE); break; case 0: default: if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("AltairZ80 RAM Selected\n"); break; } ramtype = value; return SCPE_OK; } /* set memory to 'size' kilo byte */ static t_stat set_size(uint32 size) { uint32 maxsize = (((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) && ((cpu_unit.flags & UNIT_CPU_BANKED) == 0)) ? MAXBANKSIZE : MAXMEMORY; size <<= KBLOG2; if (cpu_unit.flags & UNIT_CPU_BANKED) size &= ~ADDRMASK; cpu_unit.flags |= UNIT_CPU_MMU; if (size < KB) MEMORYSIZE = KB; else if (size > maxsize) MEMORYSIZE = maxsize; else MEMORYSIZE = size; cpu_dev.awidth = MAXBANKSIZELOG2; if (size > MAXBANKSIZE) cpu_dev.awidth += MAXBANKSLOG2; cpu_clear(); return SCPE_OK; } static t_stat cpu_set_size(UNIT *uptr, int32 value, char *cptr, void *desc) { return set_size(value); } static t_stat cpu_set_memory(UNIT *uptr, int32 value, char *cptr, void *desc) { uint32 size, result, i; if (cptr == NULL) return SCPE_ARG; result = sscanf(cptr, "%i%n", &size, &i); if ((result == 1) && (cptr[i] == 'K') && ((cptr[i + 1] == 0) || (cptr[i + 1] == 'B') && (cptr[i + 2] == 0))) return set_size(size); return SCPE_ARG; } /* AltairZ80 Simulator initialization */ void altairz80_init(void) { cpu_clear(); } void (*sim_vm_init) (void) = &altairz80_init; /* This is the binary loader. The input file is considered to be a string of literal bytes with no special format. The load starts at the current value of the PC if no start address is given. If the input string ends with ROM (not case sensitive) the memory area is made read only. ALTAIRROM/NOALTAIRROM settings are ignored. */ #define PLURAL(x) (x), (x) == 1 ? "" : "s" t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag) { int32 i; uint32 addr, cnt = 0, org, pagesModified = 0, makeROM = FALSE; t_addr j, lo, hi; char *result; MDEV m; char gbuf[CBUFSIZE]; if (flag) { result = get_range(NULL, cptr, &lo, &hi, 16, ADDRMASKEXTENDED, 0); if (result == NULL) return SCPE_ARG; for (j = lo; j <= hi; j++) { if (putc(GetBYTEExtended(j), fileref) == EOF) return SCPE_IOERR; } printf("%d byte%s dumped [%x - %x].\n", PLURAL(hi + 1 - lo), lo, hi); } else { if (*cptr == 0) addr = (chiptype == CHIP_TYPE_8086) ? PCX_S : PC_S; else { get_glyph(cptr, gbuf, 0); if (strcmp(gbuf, "ROM") == 0) { addr = (chiptype == CHIP_TYPE_8086) ? PCX_S : PC_S; makeROM = TRUE; } else { addr = strtotv(cptr, &result, 16) & ADDRMASKEXTENDED; if (cptr == result) return SCPE_ARG; while (isspace(*result)) result++; get_glyph(result, gbuf, 0); if (strcmp(gbuf, "ROM") == 0) makeROM = TRUE; } } /* addr is start address to load to, makeROM == TRUE iff memory should become ROM */ org = addr; while ((addr < MAXMEMORY) && ((i = getc(fileref)) != EOF)) { m = mmu_table[addr >> LOG2PAGESIZE]; if (!m.isRAM && m.isEmpty) { mmu_table[addr >> LOG2PAGESIZE] = RAM_PAGE; pagesModified++; m = RAM_PAGE; } if (makeROM) { mmu_table[addr >> LOG2PAGESIZE] = ROM_PAGE; m = ROM_PAGE; } if (!m.isRAM && m.routine) m.routine(addr, 1, i); else M[addr] = i; addr++; cnt++; } /* end while */ printf("%d byte%s [%d page%s] loaded at %x%s.\n", PLURAL(cnt), PLURAL((cnt + 0xff) >> 8), org, makeROM ? " [ROM]" : ""); if (pagesModified) printf("Warning: %d page%s modified.\n", PLURAL(pagesModified)); } return SCPE_OK; } void cpu_raise_interrupt(uint32 irq) { extern void cpu8086_intr(uint8 intrnum); if (chiptype == CHIP_TYPE_8086) { cpu8086_intr(irq); } else if (cpu_unit.flags & UNIT_CPU_VERBOSE) { printf("Interrupts not fully supported for chiptype: %s\n", (chiptype <= MAX_CHIP_TYPE) ? chipTypeToString[chiptype] : "????"); } } simh-3.8.1/AltairZ80/nasm.h0000644000175000017500000002624111043102676013473 0ustar vlmvlm/* nasm.h main header file for the Netwide Assembler: inter-module interface * * The Netwide Assembler is copyright (C) 1996 Simon Tatham and * Julian Hall. All rights reserved. The software is * redistributable under the licence given in the file "Licence" * distributed in the NASM archive. * * initial version: 27/iii/95 by Simon Tatham */ #ifndef NASM_NASM_H #define NASM_NASM_H #include #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 /* comes in handy */ #endif #ifndef TRUE #define TRUE 1 #endif /* * Name pollution problems: on Digital UNIX pulls in some * strange hardware header file which sees fit to define R_SP. We * undefine it here so as not to break the enum below. */ #ifdef R_SP #undef R_SP #endif /* * Special values for expr->type. ASSUMPTION MADE HERE: the number * of distinct register names (i.e. possible "type" fields for an * expr structure) does not exceed 124 (EXPR_REG_START through * EXPR_REG_END). */ #define EXPR_REG_START 1 /* * Here we define the operand types. These are implemented as bit * masks, since some are subsets of others; e.g. AX in a MOV * instruction is a special operand type, whereas AX in other * contexts is just another 16-bit register. (Also, consider CL in * shift instructions, DX in OUT, etc.) */ /* size, and other attributes, of the operand */ #define BITS8 0x00000001L #define BITS16 0x00000002L #define BITS32 0x00000004L #define BITS64 0x00000008L /* FPU only */ #define BITS80 0x00000010L /* FPU only */ #define FAR 0x00000020L /* grotty: this means 16:16 or */ /* 16:32, like in CALL/JMP */ #define NEAR 0x00000040L #define SHORT 0x00000080L /* and this means what it says :) */ #define SIZE_MASK 0x000000FFL /* all the size attributes */ #define NON_SIZE (~SIZE_MASK) #define TO 0x00000100L /* reverse effect in FADD, FSUB &c */ #define COLON 0x00000200L /* operand is followed by a colon */ /* type of operand: memory reference, register, etc. */ #define MEMORY 0x00204000L #define REGISTER 0x00001000L /* register number in 'basereg' */ #define IMMEDIATE 0x00002000L #define REGMEM 0x00200000L /* for r/m, ie EA, operands */ #define REGNORM 0x00201000L /* 'normal' reg, qualifies as EA */ #define REG8 0x00201001L #define REG16 0x00201002L #define REG32 0x00201004L #define MMXREG 0x00201008L /* MMX registers */ #define XMMREG 0x00201010L /* XMM Katmai reg */ #define FPUREG 0x01000000L /* floating point stack registers */ #define FPU0 0x01000800L /* FPU stack register zero */ /* special register operands: these may be treated differently */ #define REG_SMASK 0x00070000L /* a mask for the following */ #define REG_ACCUM 0x00211000L /* accumulator: AL, AX or EAX */ #define REG_AL 0x00211001L /* REG_ACCUM | BITSxx */ #define REG_AX 0x00211002L /* ditto */ #define REG_EAX 0x00211004L /* and again */ #define REG_COUNT 0x00221000L /* counter: CL, CX or ECX */ #define REG_CL 0x00221001L /* REG_COUNT | BITSxx */ #define REG_CX 0x00221002L /* ditto */ #define REG_ECX 0x00221004L /* another one */ #define REG_DL 0x00241001L #define REG_DX 0x00241002L #define REG_EDX 0x00241004L #define REG_SREG 0x00081002L /* any segment register */ #define REG_CS 0x01081002L /* CS */ #define REG_DESS 0x02081002L /* DS, ES, SS (non-CS 86 registers) */ #define REG_FSGS 0x04081002L /* FS, GS (386 extended registers) */ #define REG_SEG67 0x08081002L /* Non-implemented segment registers */ #define REG_CDT 0x00101004L /* CRn, DRn and TRn */ #define REG_CREG 0x08101004L /* CRn */ #define REG_DREG 0x10101004L /* DRn */ #define REG_TREG 0x20101004L /* TRn */ /* special type of EA */ #define MEM_OFFS 0x00604000L /* simple [address] offset */ /* special type of immediate operand */ #define ONENESS 0x00800000L /* so UNITY == IMMEDIATE | ONENESS */ #define UNITY 0x00802000L /* for shift/rotate instructions */ #define BYTENESS 0x40000000L /* so SBYTE == IMMEDIATE | BYTENESS */ #define SBYTE 0x40002000L /* for op r16/32,immediate instrs. */ /* Register names automatically generated from regs.dat */ /* automatically generated from ./regs.dat - do not edit */ enum reg_enum { R_AH = EXPR_REG_START, R_AL, R_AX, R_BH, R_BL, R_BP, R_BX, R_CH, R_CL, R_CR0, R_CR1, R_CR2, R_CR3, R_CR4, R_CR5, R_CR6, R_CR7, R_CS, R_CX, R_DH, R_DI, R_DL, R_DR0, R_DR1, R_DR2, R_DR3, R_DR4, R_DR5, R_DR6, R_DR7, R_DS, R_DX, R_EAX, R_EBP, R_EBX, R_ECX, R_EDI, R_EDX, R_ES, R_ESI, R_ESP, R_FS, R_GS, R_MM0, R_MM1, R_MM2, R_MM3, R_MM4, R_MM5, R_MM6, R_MM7, R_SEGR6, R_SEGR7, R_SI, R_SP, R_SS, R_ST0, R_ST1, R_ST2, R_ST3, R_ST4, R_ST5, R_ST6, R_ST7, R_TR0, R_TR1, R_TR2, R_TR3, R_TR4, R_TR5, R_TR6, R_TR7, R_XMM0, R_XMM1, R_XMM2, R_XMM3, R_XMM4, R_XMM5, R_XMM6, R_XMM7, REG_ENUM_LIMIT }; enum { /* condition code names */ C_A, C_AE, C_B, C_BE, C_C, C_E, C_G, C_GE, C_L, C_LE, C_NA, C_NAE, C_NB, C_NBE, C_NC, C_NE, C_NG, C_NGE, C_NL, C_NLE, C_NO, C_NP, C_NS, C_NZ, C_O, C_P, C_PE, C_PO, C_S, C_Z }; /* * Note that because segment registers may be used as instruction * prefixes, we must ensure the enumerations for prefixes and * register names do not overlap. */ enum { /* instruction prefixes */ PREFIX_ENUM_START = REG_ENUM_LIMIT, P_A16 = PREFIX_ENUM_START, P_A32, P_LOCK, P_O16, P_O32, P_REP, P_REPE, P_REPNE, P_REPNZ, P_REPZ, P_TIMES }; enum { /* extended operand types */ EOT_NOTHING, EOT_DB_STRING, EOT_DB_NUMBER }; enum { /* special EA flags */ EAF_BYTEOFFS = 1, /* force offset part to byte size */ EAF_WORDOFFS = 2, /* force offset part to [d]word size */ EAF_TIMESTWO = 4 /* really do EAX*2 not EAX+EAX */ }; enum { /* values for `hinttype' */ EAH_NOHINT = 0, /* no hint at all - our discretion */ EAH_MAKEBASE = 1, /* try to make given reg the base */ EAH_NOTBASE = 2 /* try _not_ to make reg the base */ }; typedef struct { /* operand to an instruction */ long type; /* type of operand */ int addr_size; /* 0 means default; 16; 32 */ int basereg, indexreg, scale; /* registers and scale involved */ int hintbase, hinttype; /* hint as to real base register */ long segment; /* immediate segment, if needed */ long offset; /* any immediate number */ long wrt; /* segment base it's relative to */ int eaflags; /* special EA flags */ int opflags; /* see OPFLAG_* defines below */ } operand; #define OPFLAG_FORWARD 1 /* operand is a forward reference */ #define OPFLAG_EXTERN 2 /* operand is an external reference */ typedef struct extop { /* extended operand */ struct extop *next; /* linked list */ long type; /* defined above */ char *stringval; /* if it's a string, then here it is */ int stringlen; /* ... and here's how long it is */ long segment; /* if it's a number/address, then... */ long offset; /* ... it's given here ... */ long wrt; /* ... and here */ } extop; #define MAXPREFIX 4 typedef struct { /* an instruction itself */ char *label; /* the label defined, or NULL */ int prefixes[MAXPREFIX]; /* instruction prefixes, if any */ int nprefix; /* number of entries in above */ int opcode; /* the opcode - not just the string */ int condition; /* the condition code, if Jcc/SETcc */ int operands; /* how many operands? 0-3 * (more if db et al) */ operand oprs[3]; /* the operands, defined as above */ extop *eops; /* extended operands */ int eops_float; /* true if DD and floating */ long times; /* repeat count (TIMES prefix) */ int forw_ref; /* is there a forward reference? */ } insn; enum geninfo { GI_SWITCH }; /* * values for the `type' parameter to an output function. Each one * must have the actual number of _bytes_ added to it. * * Exceptions are OUT_RELxADR, which denote an x-byte relocation * which will be a relative jump. For this we need to know the * distance in bytes from the start of the relocated record until * the end of the containing instruction. _This_ is what is stored * in the size part of the parameter, in this case. * * Also OUT_RESERVE denotes reservation of N bytes of BSS space, * and the contents of the "data" parameter is irrelevant. * * The "data" parameter for the output function points to a "long", * containing the address in question, unless the type is * OUT_RAWDATA, in which case it points to an "unsigned char" * array. */ #define OUT_RAWDATA 0x00000000UL #define OUT_ADDRESS 0x10000000UL #define OUT_REL2ADR 0x20000000UL #define OUT_REL4ADR 0x30000000UL #define OUT_RESERVE 0x40000000UL #define OUT_TYPMASK 0xF0000000UL #define OUT_SIZMASK 0x0FFFFFFFUL /* * The type definition macros * for debugging * * low 3 bits: reserved * next 5 bits: type * next 24 bits: number of elements for arrays (0 for labels) */ #define TY_UNKNOWN 0x00 #define TY_LABEL 0x08 #define TY_BYTE 0x10 #define TY_WORD 0x18 #define TY_DWORD 0x20 #define TY_FLOAT 0x28 #define TY_QWORD 0x30 #define TY_TBYTE 0x38 #define TY_COMMON 0xE0 #define TY_SEG 0xE8 #define TY_EXTERN 0xF0 #define TY_EQU 0xF8 #define TYM_TYPE(x) ((x) & 0xF8) #define TYM_ELEMENTS(x) (((x) & 0xFFFFFF00) >> 8) #define TYS_ELEMENTS(x) ((x) << 8) /* * ----- * Other * ----- */ /* * This is a useful #define which I keep meaning to use more often: * the number of elements of a statically defined array. */ #define elements(x) ( sizeof(x) / sizeof(*(x)) ) extern int tasm_compatible_mode; /* * This declaration passes the "pass" number to all other modules * "pass0" assumes the values: 0, 0, ..., 0, 1, 2 * where 0 = optimizing pass * 1 = pass 1 * 2 = pass 2 */ extern int pass0; /* this is globally known */ extern int optimizing; #endif simh-3.8.1/AltairZ80/s100_ss1.c0000644000175000017500000006024211043117044013773 0ustar vlmvlm/************************************************************************* * * * $Id: s100_ss1.c 1997 2008-07-18 05:29:52Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro System Support 1 module for SIMH. * * Note this does not include the Boot ROM on the System Support 1 Card * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define TRACE_MSG (1 << 1) #define PIC_MSG (1 << 2) #define TC_MSG (1 << 3) #define RTC_MSG (1 << 4) #define MATH_MSG (1 << 5) #define UART_MSG (1 << 6) #define IRQ_MSG (1 << 7) #define SS1_MAX_TIMERS 3 typedef struct { PNP_INFO pnp; /* Plug and Play */ } SS1_INFO; static SS1_INFO ss1_info_data = { { 0x0, 0, 0x50, 16 } }; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint32 PCX; static t_stat ss1_reset(DEVICE *ss1_dev); static t_stat ss1_svc (UNIT *uptr); static uint8 SS1_Read(const uint32 Addr); static uint8 SS1_Write(const uint32 Addr, uint8 cData); static int32 ss1dev(const int32 port, const int32 io, const int32 data); void raise_ss1_interrupt(uint8 isr_index); /* SS1 Interrupt Controller notes: * * Msster 8259: * IRQ0 = VI0 * IRQ1 = VI1 - DISK3 Interrupt * IRQ2 = VI2 - IF3 Rx Interrupt * IRQ3 = VI3 - IF3 Tx Interrupt * IRQ4 = VI4 - DISK1A * IRQ5 = VI5 - ? * IRQ6 = VI6 * * * Slave 8259: * IRQ0 = VI7 0x48 * IRQ1 = Timer0 0x49 * IRQ2 = Timer1 0x4A * IRQ3 = Timer2 0x4B * IRQ4 = 9511 SVRQ 0x4C * IRQ5 = 9511 END 0x4D * IRQ6 = 2651 TxRDY 0x4E * IRQ7 = 2651 RxRDY 0x4F */ #define MASTER_PIC 0 #define SLAVE_PIC 1 #define VI0_IRQ_OFFSET 0 #define VI1_IRQ_OFFSET 1 #define VI2_IRQ_OFFSET 2 #define VI3_IRQ_OFFSET 3 #define VI4_IRQ_OFFSET 4 #define VI5_IRQ_OFFSET 5 #define VI6_IRQ_OFFSET 6 #define VI7_IRQ_OFFSET 0 #define TC0_IRQ_OFFSET 1 #define TC1_IRQ_OFFSET 2 #define TC2_IRQ_OFFSET 3 #define MSVRQ_IRQ_OFFSET 4 #define MEND_IRQ_OFFSET 5 #define TXRDY_IRQ_OFFSET 6 #define RXRDY_IRQ_OFFSET 7 typedef struct { uint8 config_cnt; uint8 ICW[5]; uint8 IMR; /* OCW1 = IMR */ uint8 OCW2; uint8 OCW3; uint8 IRR; uint8 ISR; } I8259_REGS; I8259_REGS ss1_pic[2]; /* SS1 Timer notes: * * T0, T1, T2 inputs connected to 2MHz clock on SS1 * T0 IRQ connected to Slave IRQ 1 * T1 IRQ connected to Slave IRQ 2 * T2 IRQ connected to Slave IRQ 3 */ typedef struct { uint16 count[3]; /* Current counter value for each timer. */ uint8 mode[3]; /* Current mode of each timer. */ uint8 bcd[3]; uint8 rl[3]; uint8 CTL; } I8253_REGS; I8253_REGS ss1_tc[1]; #define I8253_CTL_SC_MASK 0xC0 #define I8253_CTL_RL_MASK 0x30 #define I8253_CTL_MODE_MASK 0x0E #define I8253_CTL_BCD 0x01 typedef struct { uint8 digit_sel; uint8 flags; } RTC_REGS; RTC_REGS ss1_rtc[1]; static UNIT ss1_unit[] = { { UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, { UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, { UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, { UDATA (&ss1_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) } }; static REG ss1_reg[] = { { HRDATA (MPIC_IMR, ss1_pic[MASTER_PIC].IMR, 8), }, { HRDATA (MPIC_IRR, ss1_pic[MASTER_PIC].IRR, 8), }, { HRDATA (MPIC_ISR, ss1_pic[MASTER_PIC].ISR, 8), }, { HRDATA (MPIC_OCW2, ss1_pic[MASTER_PIC].OCW2, 8), }, { HRDATA (MPIC_OCW3, ss1_pic[MASTER_PIC].OCW3, 8), }, { HRDATA (SPIC_IMR, ss1_pic[SLAVE_PIC].IMR, 8), }, { HRDATA (SPIC_IRR, ss1_pic[SLAVE_PIC].IRR, 8), }, { HRDATA (SPIC_ISR, ss1_pic[SLAVE_PIC].ISR, 8), }, { HRDATA (SPIC_OCW2, ss1_pic[SLAVE_PIC].OCW2, 8), }, { HRDATA (SPIC_OCW3, ss1_pic[SLAVE_PIC].OCW3, 8), }, { HRDATA (T0_MODE, ss1_tc[0].mode, 3), }, { HRDATA (T0_COUNT, ss1_tc[0].count, 16), }, { HRDATA (T1_MODE, ss1_tc[1].mode, 3), }, { HRDATA (T1_COUNT, ss1_tc[1].count, 16), }, { HRDATA (T2_MODE, ss1_tc[2].mode, 3), }, { HRDATA (T2_COUNT, ss1_tc[2].count, 16), }, { HRDATA (RTC_DIGIT, ss1_rtc[0].digit_sel, 4), }, { HRDATA (RTC_FLAGS, ss1_rtc[0].flags, 4), }, { NULL } }; static MTAB ss1_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(ss1_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB ss1_dt[] = { { "ERROR", ERROR_MSG }, { "TRACE", TRACE_MSG }, { "PIC", PIC_MSG }, { "TC", TC_MSG }, { "RTC", RTC_MSG }, { "MATH", MATH_MSG }, { "UART", UART_MSG }, { "IRQ", IRQ_MSG }, { NULL, 0 } }; DEVICE ss1_dev = { "SS1", ss1_unit, ss1_reg, ss1_mod, SS1_MAX_TIMERS, 10, 31, 1, SS1_MAX_TIMERS, SS1_MAX_TIMERS, NULL, NULL, &ss1_reset, NULL, NULL, NULL, &ss1_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, ss1_dt, NULL, "Compupro System Support 1 SS1" }; /* Reset routine */ static t_stat ss1_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, TRUE); } else { /* Connect SS1 at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } else { DBG_PRINT(("SS1: Mapped I/O resource at 0x%04x, len=%d\n", pnp->io_base, pnp->io_size)); ss1_unit[0].u4 = 0; ss1_unit[1].u4 = 1; ss1_unit[2].u4 = 2; ss1_unit[3].u4 = 3; ss1_pic[MASTER_PIC].IMR = 0xFF; ss1_pic[SLAVE_PIC].IMR = 0xFF; } } return SCPE_OK; } static int32 ss1dev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("SS1: IO %s, Port %02x\n", io ? "WR" : "RD", port)); if(io) { SS1_Write(port, data); return 0; } else { return(SS1_Read(port)); } } #define SS1_M8259_L 0x00 #define SS1_M8259_H 0x01 #define SS1_S8259_L 0x02 #define SS1_S8259_H 0x03 #define SS1_8253_TC0 0x04 #define SS1_8253_TC1 0x05 #define SS1_8253_TC2 0x06 #define SS1_8253_CTL 0x07 #define SS1_9511A_DATA 0x08 #define SS1_9511A_CMD 0x09 #define SS1_RTC_CMD 0x0A #define SS1_RTC_DATA 0x0B #define SS1_UART_DATA 0x0C #define SS1_UART_STAT 0x0D #define SS1_UART_MODE 0x0E #define SS1_UART_CMD 0x0F extern int32 sio0d(const int32 port, const int32 io, const int32 data); extern int32 sio0s(const int32 port, const int32 io, const int32 data); static struct tm currentTime; static int32 toBCD(const int32 x); static uint8 SS1_Read(const uint32 Addr) { uint8 cData = 0x00; uint8 sel_pic = MASTER_PIC; uint8 sel_tc = 0; time_t now; switch(Addr & 0x0F) { case SS1_S8259_L: sel_pic = SLAVE_PIC; case SS1_M8259_L: if((ss1_pic[sel_pic].OCW3 & 0x03) == 0x03) { cData = ss1_pic[sel_pic].ISR; TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC ISR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); } else if((ss1_pic[sel_pic].OCW3 & 0x03) == 0x02) { cData = ss1_pic[sel_pic].IRR; TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC IRR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); } else { cData = 0xFF; } break; case SS1_S8259_H: sel_pic = SLAVE_PIC; case SS1_M8259_H: cData = ss1_pic[sel_pic].IMR; TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " RD: %s PIC IMR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); ss1_pic[sel_pic].IMR = cData; break; case SS1_8253_CTL: cData = ss1_tc[0].CTL; TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " RD: TC CTL=0x%02x." NLP, PCX, cData)); break; case SS1_8253_TC2: sel_tc++; case SS1_8253_TC1: sel_tc++; case SS1_8253_TC0: TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " RD: TC [%d]=0x%02x." NLP, PCX, sel_tc, cData)); break; case SS1_9511A_DATA: case SS1_9511A_CMD: TRACE_PRINT(MATH_MSG, ("SS1: " ADDRESS_FORMAT " RD: Math Coprocessor not Implemented." NLP, PCX)); break; case SS1_RTC_CMD: cData = 0xFF; TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " RD: RTC Cmd=0x%02x." NLP, PCX, cData)); break; case SS1_RTC_DATA: time(&now); currentTime = *localtime(&now); switch(ss1_rtc[0].digit_sel) { case 0: cData = toBCD(currentTime.tm_sec) & 0xF; break; case 1: cData = (toBCD(currentTime.tm_sec) >> 4) & 0xF; break; case 2: cData = toBCD(currentTime.tm_min) & 0xF; break; case 3: cData = (toBCD(currentTime.tm_min) >> 4) & 0xF; break; case 4: cData = toBCD(currentTime.tm_hour) & 0xF; break; case 5: cData = (toBCD(currentTime.tm_hour) >> 4) & 0x3; cData |= 0x08; /* Set to 24-hour format */ break; case 6: cData = toBCD(currentTime.tm_wday) & 0xF; break; case 7: cData = toBCD(currentTime.tm_mday) & 0xF; break; case 8: cData = (toBCD(currentTime.tm_mday) >> 4) & 0xF; break; case 9: cData = toBCD(currentTime.tm_mon+1) & 0xF; break; case 10: cData = (toBCD(currentTime.tm_mon+1) >> 4) & 0xF; break; case 11: cData = toBCD(currentTime.tm_year-22) & 0xF; break; case 12: cData = (toBCD(currentTime.tm_year-22) >> 4) & 0xF; break; default: cData = 0; break; } TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " RD: RTC Data[%x]=0x%02x." NLP, PCX, ss1_rtc[0].digit_sel, cData)); break; case SS1_UART_DATA: cData = sio0d(Addr, 0, 0); TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART Data=0x%02x." NLP, PCX, cData)); break; case SS1_UART_STAT: cData = sio0s(Addr, 0, 0); TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART Stat=0x%02x." NLP, PCX, cData)); break; case SS1_UART_MODE: case SS1_UART_CMD: TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART not Implemented." NLP, PCX)); break; } return (cData); } uint16 newcount = 0; uint8 bc; static void generate_ss1_interrupt(void); static uint8 SS1_Write(const uint32 Addr, uint8 cData) { uint8 sel_pic = MASTER_PIC; uint8 sel_tc = 0; uint8 sel_timer = 0; switch(Addr & 0x0F) { case SS1_S8259_L: sel_pic = SLAVE_PIC; case SS1_M8259_L: if(cData & 0x10) { TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC ICW1=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); ss1_pic[sel_pic].ICW[1] = cData; ss1_pic[sel_pic].config_cnt=1; } else { if(cData & 0x08) { /* OCW3 */ TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC OCW3=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); ss1_pic[sel_pic].OCW3 = cData; } else { /* OCW2 */ TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC OCW2=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); ss1_pic[sel_pic].OCW2 = cData; } } break; case SS1_S8259_H: sel_pic = SLAVE_PIC; case SS1_M8259_H: if(ss1_pic[sel_pic].config_cnt == 0) { TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC IMR=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), cData)); ss1_pic[sel_pic].IMR = cData; generate_ss1_interrupt(); } else { ss1_pic[sel_pic].config_cnt++; TRACE_PRINT(PIC_MSG, ("SS1: " ADDRESS_FORMAT " WR: %s PIC ICW%d=0x%02x." NLP, PCX, (sel_pic ? "Slave " : "Master"), ss1_pic[sel_pic].config_cnt, cData)); ss1_pic[sel_pic].ICW[ss1_pic[sel_pic].config_cnt] = cData; ss1_unit[0].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC0_IRQ_OFFSET; ss1_unit[1].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC1_IRQ_OFFSET; ss1_unit[2].u3 = ss1_pic[SLAVE_PIC].ICW[2]+TC2_IRQ_OFFSET; if(ss1_pic[sel_pic].config_cnt == 4) { ss1_pic[sel_pic].config_cnt = 0; } } break; case SS1_8253_CTL: ss1_tc[0].CTL = cData; sel_timer = (ss1_tc[0].CTL & I8253_CTL_SC_MASK) >> 6; TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " WR: TC CTL=0x%02x." NLP, PCX, ss1_tc[0].CTL)); if(ss1_tc[0].CTL & I8253_CTL_BCD) { TRACE_PRINT(ERROR_MSG, ("SS1: " ADDRESS_FORMAT " Timer %d: BCD Mode not supported: TC CTL=0x%02x." NLP, PCX, sel_timer, ss1_tc[0].CTL)); } ss1_tc[0].bcd[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_BCD); ss1_tc[0].mode[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_MODE_MASK) >> 1; ss1_tc[0].rl[sel_timer] = (ss1_tc[0].CTL & I8253_CTL_RL_MASK) >> 4; TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " Timer %d: Mode: %d, RL=%d, %s." NLP, PCX, sel_timer, ss1_tc[0].mode[sel_timer], ss1_tc[0].rl[sel_timer], ss1_tc[0].bcd[sel_timer] ? "BCD" : "Binary")); newcount = 0; bc=0; break; case SS1_8253_TC2: sel_tc++; case SS1_8253_TC1: sel_tc++; case SS1_8253_TC0: if(ss1_tc[0].rl[sel_timer] == 3) { if(bc==0) { newcount = cData; } if(bc==1) { newcount |= (cData << 8); sim_activate(&ss1_unit[sel_tc], newcount); } bc++; } if(ss1_tc[0].rl[sel_timer] == 2) { newcount = (cData << 8); sim_activate(&ss1_unit[sel_tc], newcount); } TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " WR: TC [%d]=0x%02x." NLP, PCX, sel_tc, cData)); if(sel_tc == 0) { } break; case SS1_9511A_DATA: case SS1_9511A_CMD: TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: Math Coprocessor not Implemented." NLP, PCX)); break; case SS1_RTC_CMD: ss1_rtc[0].digit_sel = cData & 0x0F; ss1_rtc[0].flags = (cData >> 4) & 0x0F; TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " WR: RTC Cmd=0x%02x (%s%s%s SEL=%x)" NLP, PCX, cData, ss1_rtc[0].flags & 0x4 ? "HOLD " :"", ss1_rtc[0].flags & 0x2 ? "WR" :"", ss1_rtc[0].flags & 0x1 ? "RD" :"", ss1_rtc[0].digit_sel)) break; case SS1_RTC_DATA: TRACE_PRINT(RTC_MSG, ("SS1: " ADDRESS_FORMAT " WR: RTC Data=0x%02x" NLP, PCX, cData)); break; case SS1_UART_DATA: TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART Data=0x%02x." NLP, PCX, cData)); sio0d(Addr, 1, cData); break; case SS1_UART_STAT: TRACE_PRINT(UART_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART Stat=0x%02x." NLP, PCX, cData)); sio0s(Addr, 1, cData); break; case SS1_UART_MODE: case SS1_UART_CMD: TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART not Implemented." NLP, PCX)); break; } return(0); } void raise_ss1_interrupt(uint8 isr_index) { uint8 irq_bit; if(isr_index < 7) { /* VI0-6 on master PIC */ irq_bit = (1 << isr_index); ss1_pic[MASTER_PIC].ISR |= irq_bit; generate_ss1_interrupt(); } else { /* VI7 is on slave PIC */ ss1_pic[SLAVE_PIC].ISR |= 1; generate_ss1_interrupt(); } } extern void cpu_raise_interrupt(uint32 irq); static void generate_ss1_interrupt(void) { uint8 irq, irq_pend, irq_index = 0, irq_bit = 0; uint8 pic; for(pic=MASTER_PIC;pic<=SLAVE_PIC;pic++) { irq_pend = (~ss1_pic[pic].IMR) & ss1_pic[pic].ISR; while(irq_pend) { irq_bit = irq_pend & 1; if(irq_bit) { ss1_pic[pic].IRR |= (irq_bit << irq_index); irq = ss1_pic[pic].ICW[2]+irq_index; TRACE_PRINT(IRQ_MSG, ("Handling interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d" NLP, pic ? "SLAVE" : "MASTER", ss1_pic[pic].IMR, ss1_pic[pic].ISR, ss1_pic[pic].IRR, irq_index)); cpu_raise_interrupt(irq); ss1_pic[pic].IRR &= ~(irq_bit << irq_index); ss1_pic[pic].ISR &= ~(irq_bit << irq_index); if(irq_pend & 0x7E) { /* TRACE_PRINT(IRQ_MSG, ("Requeue interrupt on %s PIC: IMR=0x%02x, ISR=0x%02x, IRR=0x%02x, index=%d" NLP, pic ? "SLAVE" : "MASTER", ss1_pic[pic].IMR, ss1_pic[pic].ISR, ss1_pic[pic].IRR, irq_index)); */ sim_activate(&ss1_unit[3], 1000); /* requeue, because more interrupts are pending. */ } break; } else { irq_index++; irq_pend = irq_pend >> 1; } } } } /* Unit service routine */ /* Unit 0-2 = Timer0-2, Unit3=ISR queue */ static t_stat ss1_svc (UNIT *uptr) { uint8 cData; uint8 irq_bit = 0; /* Handle SS1 UART Rx interrupts here. */ cData = sio0s(0x5D, 0, 0); if(cData & 2) { /* && ((ss1_pic[SLAVE_PIC].IMR & 0x80) == 0)) { */ ss1_pic[SLAVE_PIC].ISR |= 0x80; generate_ss1_interrupt(); sim_activate(uptr, 1000); /* requeue, because we still need to handle the timer interrupt. */ } else if((cData & 1) && ((ss1_pic[SLAVE_PIC].IMR & 0x40) == 0)) { TRACE_PRINT(IRQ_MSG, ("SS1: " ADDRESS_FORMAT " Calling UART Tx ISR." NLP, PCX)); ss1_pic[SLAVE_PIC].ISR |= 0x40; generate_ss1_interrupt(); sim_activate(uptr, 1000); /* requeue, because we still need to handle the timer interrupt. */ } else if (uptr->u4 == 0x3) { /* ISR was requeued because additional interrupts were pending. */ generate_ss1_interrupt(); } else { switch(uptr->u4) { case 0: irq_bit = 2; break; case 1: irq_bit = 4; break; case 2: irq_bit = 8; break; } if(ss1_tc[0].mode[uptr->u4] == 0x0) { TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " Calling Timer%d ISR." NLP, PCX, uptr->u4)); ss1_pic[SLAVE_PIC].ISR |= irq_bit; generate_ss1_interrupt(); } if(ss1_tc[0].mode[uptr->u4] == 0x3) { TRACE_PRINT(TC_MSG, ("SS1: " ADDRESS_FORMAT " Calling Timer%d ISR." NLP, PCX, uptr->u4)); ss1_pic[SLAVE_PIC].ISR |= irq_bit; generate_ss1_interrupt(); TRACE_PRINT(TC_MSG, ("Timer %d, mode %d, reloading\n", uptr->u4, ss1_tc[0].mode[uptr->u4])); sim_activate(uptr, 33280); } } sim_activate(&ss1_unit[3], 1000000); // requeue, because more interrupts are pending. return SCPE_OK; } static int32 toBCD(const int32 x) { return (x / 10) * 16 + (x % 10); } simh-3.8.1/AltairZ80/wd179x.h0000644000175000017500000001000411031461306013561 0ustar vlmvlm/************************************************************************* * * * $Id: wd179x.h 1907 2008-05-21 07:04:17Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Generic Intel 8272 Disk Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ extern t_stat wd179x_attach(UNIT *uptr, char *cptr); extern t_stat wd179x_detach(UNIT *uptr); extern uint8 WD179X_Set_DMA(const uint32 dma_addr); extern uint8 WD179X_Read(const uint32 Addr); extern uint8 WD179X_Write(const uint32 Addr, uint8 cData); extern void wd179x_external_restore(void); #define WD179X_FDC_MSR 0 /* R=FDC Main Status Register, W=Drive Select Register */ #define WD179X_FDC_DATA 1 /* R/W FDC Data Register */ #define WD179X_STATUS 0 #define WD179X_TRACK 1 #define WD179X_SECTOR 2 #define WD179X_DATA 3 typedef struct { PNP_INFO pnp; /* Plug-n-Play Information */ uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */ uint8 hld; /* WD179X Head Load Output */ uint8 drq; /* WD179X DMA Request Output */ uint8 ddens; /* WD179X Double-Density Input */ uint8 fdc_head; /* H Head Number */ uint8 sel_drive; /* Currently selected drive */ uint8 drivetype; /* 8 or 5 depending on disk type. */ } WD179X_INFO_PUB; simh-3.8.1/AltairZ80/insnsa.c0000644000175000017500000052526610766755744014063 0ustar vlmvlm/* This file auto-generated from insns.dat by insns.pl - don't edit it */ #include "nasm.h" #include "insns.h" static struct itemplate instrux_AAA[] = { {I_AAA, 0, {0,0,0}, "\1\x37", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_AAD[] = { {I_AAD, 0, {0,0,0}, "\2\xD5\x0A", IF_8086}, {I_AAD, 1, {IMMEDIATE,0,0}, "\1\xD5\24", IF_8086|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_AAM[] = { {I_AAM, 0, {0,0,0}, "\2\xD4\x0A", IF_8086}, {I_AAM, 1, {IMMEDIATE,0,0}, "\1\xD4\24", IF_8086|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_AAS[] = { {I_AAS, 0, {0,0,0}, "\1\x3F", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_ADC[] = { {I_ADC, 2, {MEMORY,REG8,0}, "\300\1\x10\101", IF_8086|IF_SM}, {I_ADC, 2, {REG8,REG8,0}, "\1\x10\101", IF_8086}, {I_ADC, 2, {MEMORY,REG16,0}, "\320\300\1\x11\101", IF_8086|IF_SM}, {I_ADC, 2, {REG16,REG16,0}, "\320\1\x11\101", IF_8086}, {I_ADC, 2, {MEMORY,REG32,0}, "\321\300\1\x11\101", IF_386|IF_SM}, {I_ADC, 2, {REG32,REG32,0}, "\321\1\x11\101", IF_386}, {I_ADC, 2, {REG8,MEMORY,0}, "\301\1\x12\110", IF_8086|IF_SM}, {I_ADC, 2, {REG8,REG8,0}, "\1\x12\110", IF_8086}, {I_ADC, 2, {REG16,MEMORY,0}, "\320\301\1\x13\110", IF_8086|IF_SM}, {I_ADC, 2, {REG16,REG16,0}, "\320\1\x13\110", IF_8086}, {I_ADC, 2, {REG32,MEMORY,0}, "\321\301\1\x13\110", IF_386|IF_SM}, {I_ADC, 2, {REG32,REG32,0}, "\321\1\x13\110", IF_386}, {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\202\15", IF_8086}, {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\202\15", IF_386}, {I_ADC, 2, {REG_AL,IMMEDIATE,0}, "\1\x14\21", IF_8086|IF_SM}, {I_ADC, 2, {REG_AX,SBYTE,0}, "\320\1\x83\202\15", IF_8086|IF_SM}, {I_ADC, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x15\31", IF_8086|IF_SM}, {I_ADC, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\202\15", IF_386|IF_SM}, {I_ADC, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x15\41", IF_386|IF_SM}, {I_ADC, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, {I_ADC, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, {I_ADC, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, {I_ADC, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ADD[] = { {I_ADD, 2, {MEMORY,REG8,0}, "\300\17\101", IF_8086|IF_SM}, {I_ADD, 2, {REG8,REG8,0}, "\17\101", IF_8086}, {I_ADD, 2, {MEMORY,REG16,0}, "\320\300\1\x01\101", IF_8086|IF_SM}, {I_ADD, 2, {REG16,REG16,0}, "\320\1\x01\101", IF_8086}, {I_ADD, 2, {MEMORY,REG32,0}, "\321\300\1\x01\101", IF_386|IF_SM}, {I_ADD, 2, {REG32,REG32,0}, "\321\1\x01\101", IF_386}, {I_ADD, 2, {REG8,MEMORY,0}, "\301\1\x02\110", IF_8086|IF_SM}, {I_ADD, 2, {REG8,REG8,0}, "\1\x02\110", IF_8086}, {I_ADD, 2, {REG16,MEMORY,0}, "\320\301\1\x03\110", IF_8086|IF_SM}, {I_ADD, 2, {REG16,REG16,0}, "\320\1\x03\110", IF_8086}, {I_ADD, 2, {REG32,MEMORY,0}, "\321\301\1\x03\110", IF_386|IF_SM}, {I_ADD, 2, {REG32,REG32,0}, "\321\1\x03\110", IF_386}, {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\200\15", IF_8086}, {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\200\15", IF_386}, {I_ADD, 2, {REG_AL,IMMEDIATE,0}, "\1\x04\21", IF_8086|IF_SM}, {I_ADD, 2, {REG_AX,SBYTE,0}, "\320\1\x83\200\15", IF_8086|IF_SM}, {I_ADD, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x05\31", IF_8086|IF_SM}, {I_ADD, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\200\15", IF_386|IF_SM}, {I_ADD, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x05\41", IF_386|IF_SM}, {I_ADD, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, {I_ADD, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, {I_ADD, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, {I_ADD, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ADDPD[] = { {I_ADDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, {I_ADDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ADDPS[] = { {I_ADDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, {I_ADDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_ADDSD[] = { {I_ADDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, {I_ADDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_ADDSS[] = { {I_ADDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, {I_ADDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_ADDSUBPD[] = { {I_ADDSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_ADDSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_ADDSUBPS[] = { {I_ADDSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_ADDSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_AND[] = { {I_AND, 2, {MEMORY,REG8,0}, "\300\1\x20\101", IF_8086|IF_SM}, {I_AND, 2, {REG8,REG8,0}, "\1\x20\101", IF_8086}, {I_AND, 2, {MEMORY,REG16,0}, "\320\300\1\x21\101", IF_8086|IF_SM}, {I_AND, 2, {REG16,REG16,0}, "\320\1\x21\101", IF_8086}, {I_AND, 2, {MEMORY,REG32,0}, "\321\300\1\x21\101", IF_386|IF_SM}, {I_AND, 2, {REG32,REG32,0}, "\321\1\x21\101", IF_386}, {I_AND, 2, {REG8,MEMORY,0}, "\301\1\x22\110", IF_8086|IF_SM}, {I_AND, 2, {REG8,REG8,0}, "\1\x22\110", IF_8086}, {I_AND, 2, {REG16,MEMORY,0}, "\320\301\1\x23\110", IF_8086|IF_SM}, {I_AND, 2, {REG16,REG16,0}, "\320\1\x23\110", IF_8086}, {I_AND, 2, {REG32,MEMORY,0}, "\321\301\1\x23\110", IF_386|IF_SM}, {I_AND, 2, {REG32,REG32,0}, "\321\1\x23\110", IF_386}, {I_AND, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\204\15", IF_8086}, {I_AND, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\204\15", IF_386}, {I_AND, 2, {REG_AL,IMMEDIATE,0}, "\1\x24\21", IF_8086|IF_SM}, {I_AND, 2, {REG_AX,SBYTE,0}, "\320\1\x83\204\15", IF_8086|IF_SM}, {I_AND, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x25\31", IF_8086|IF_SM}, {I_AND, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\204\15", IF_386|IF_SM}, {I_AND, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x25\41", IF_386|IF_SM}, {I_AND, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, {I_AND, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, {I_AND, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, {I_AND, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, {I_AND, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, {I_AND, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ANDNPD[] = { {I_ANDNPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2}, {I_ANDNPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ANDNPS[] = { {I_ANDNPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_KATMAI|IF_SSE}, {I_ANDNPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x55\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_ANDPD[] = { {I_ANDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2}, {I_ANDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ANDPS[] = { {I_ANDPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_KATMAI|IF_SSE}, {I_ANDPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x54\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_ARPL[] = { {I_ARPL, 2, {MEMORY,REG16,0}, "\300\1\x63\101", IF_286|IF_PROT|IF_SM}, {I_ARPL, 2, {REG16,REG16,0}, "\1\x63\101", IF_286|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_BOUND[] = { {I_BOUND, 2, {REG16,MEMORY,0}, "\320\301\1\x62\110", IF_186}, {I_BOUND, 2, {REG32,MEMORY,0}, "\321\301\1\x62\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_BSF[] = { {I_BSF, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBC\110", IF_386|IF_SM}, {I_BSF, 2, {REG16,REG16,0}, "\320\2\x0F\xBC\110", IF_386}, {I_BSF, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBC\110", IF_386|IF_SM}, {I_BSF, 2, {REG32,REG32,0}, "\321\2\x0F\xBC\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_BSR[] = { {I_BSR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBD\110", IF_386|IF_SM}, {I_BSR, 2, {REG16,REG16,0}, "\320\2\x0F\xBD\110", IF_386}, {I_BSR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBD\110", IF_386|IF_SM}, {I_BSR, 2, {REG32,REG32,0}, "\321\2\x0F\xBD\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_BSWAP[] = { {I_BSWAP, 1, {REG32,0,0}, "\321\1\x0F\10\xC8", IF_486}, ITEMPLATE_END }; static struct itemplate instrux_BT[] = { {I_BT, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA3\101", IF_386|IF_SM}, {I_BT, 2, {REG16,REG16,0}, "\320\2\x0F\xA3\101", IF_386}, {I_BT, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA3\101", IF_386|IF_SM}, {I_BT, 2, {REG32,REG32,0}, "\321\2\x0F\xA3\101", IF_386}, {I_BT, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\204\25", IF_386|IF_SB}, {I_BT, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\204\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_BTC[] = { {I_BTC, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xBB\101", IF_386|IF_SM}, {I_BTC, 2, {REG16,REG16,0}, "\320\2\x0F\xBB\101", IF_386}, {I_BTC, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xBB\101", IF_386|IF_SM}, {I_BTC, 2, {REG32,REG32,0}, "\321\2\x0F\xBB\101", IF_386}, {I_BTC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\207\25", IF_386|IF_SB}, {I_BTC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\207\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_BTR[] = { {I_BTR, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB3\101", IF_386|IF_SM}, {I_BTR, 2, {REG16,REG16,0}, "\320\2\x0F\xB3\101", IF_386}, {I_BTR, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB3\101", IF_386|IF_SM}, {I_BTR, 2, {REG32,REG32,0}, "\321\2\x0F\xB3\101", IF_386}, {I_BTR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\206\25", IF_386|IF_SB}, {I_BTR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\206\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_BTS[] = { {I_BTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xAB\101", IF_386|IF_SM}, {I_BTS, 2, {REG16,REG16,0}, "\320\2\x0F\xAB\101", IF_386}, {I_BTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xAB\101", IF_386|IF_SM}, {I_BTS, 2, {REG32,REG32,0}, "\321\2\x0F\xAB\101", IF_386}, {I_BTS, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\205\25", IF_386|IF_SB}, {I_BTS, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\205\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_CALL[] = { {I_CALL, 1, {IMMEDIATE,0,0}, "\322\1\xE8\64", IF_8086}, {I_CALL, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE8\64", IF_8086}, {I_CALL, 1, {IMMEDIATE|FAR,0,0}, "\322\1\x9A\34\37", IF_8086}, {I_CALL, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE8\64", IF_8086}, {I_CALL, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE8\64", IF_8086}, {I_CALL, 1, {IMMEDIATE|BITS16|FAR,0,0}, "\320\1\x9A\34\37", IF_8086}, {I_CALL, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE8\64", IF_386}, {I_CALL, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE8\64", IF_386}, {I_CALL, 1, {IMMEDIATE|BITS32|FAR,0,0}, "\321\1\x9A\34\37", IF_386}, {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\x9A\35\30", IF_8086}, {I_CALL, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\x9A\31\30", IF_8086}, {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\x9A\31\30", IF_8086}, {I_CALL, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\x9A\41\30", IF_386}, {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\x9A\41\30", IF_386}, {I_CALL, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\203", IF_8086}, {I_CALL, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\203", IF_8086}, {I_CALL, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\203", IF_386}, {I_CALL, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\202", IF_8086}, {I_CALL, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\202", IF_8086}, {I_CALL, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\202", IF_386}, {I_CALL, 1, {REG16,0,0}, "\320\300\1\xFF\202", IF_8086}, {I_CALL, 1, {REG32,0,0}, "\321\300\1\xFF\202", IF_386}, {I_CALL, 1, {MEMORY,0,0}, "\322\300\1\xFF\202", IF_8086}, {I_CALL, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\202", IF_8086}, {I_CALL, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\202", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_CBW[] = { {I_CBW, 0, {0,0,0}, "\320\1\x98", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CDQ[] = { {I_CDQ, 0, {0,0,0}, "\321\1\x99", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_CLC[] = { {I_CLC, 0, {0,0,0}, "\1\xF8", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CLD[] = { {I_CLD, 0, {0,0,0}, "\1\xFC", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CLFLUSH[] = { {I_CLFLUSH, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\207", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CLI[] = { {I_CLI, 0, {0,0,0}, "\1\xFA", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CLTS[] = { {I_CLTS, 0, {0,0,0}, "\2\x0F\x06", IF_286|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_CMC[] = { {I_CMC, 0, {0,0,0}, "\1\xF5", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CMP[] = { {I_CMP, 2, {MEMORY,REG8,0}, "\300\1\x38\101", IF_8086|IF_SM}, {I_CMP, 2, {REG8,REG8,0}, "\1\x38\101", IF_8086}, {I_CMP, 2, {MEMORY,REG16,0}, "\320\300\1\x39\101", IF_8086|IF_SM}, {I_CMP, 2, {REG16,REG16,0}, "\320\1\x39\101", IF_8086}, {I_CMP, 2, {MEMORY,REG32,0}, "\321\300\1\x39\101", IF_386|IF_SM}, {I_CMP, 2, {REG32,REG32,0}, "\321\1\x39\101", IF_386}, {I_CMP, 2, {REG8,MEMORY,0}, "\301\1\x3A\110", IF_8086|IF_SM}, {I_CMP, 2, {REG8,REG8,0}, "\1\x3A\110", IF_8086}, {I_CMP, 2, {REG16,MEMORY,0}, "\320\301\1\x3B\110", IF_8086|IF_SM}, {I_CMP, 2, {REG16,REG16,0}, "\320\1\x3B\110", IF_8086}, {I_CMP, 2, {REG32,MEMORY,0}, "\321\301\1\x3B\110", IF_386|IF_SM}, {I_CMP, 2, {REG32,REG32,0}, "\321\1\x3B\110", IF_386}, {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\207\15", IF_8086}, {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\207\15", IF_386}, {I_CMP, 2, {REG_AL,IMMEDIATE,0}, "\1\x3C\21", IF_8086|IF_SM}, {I_CMP, 2, {REG_AX,SBYTE,0}, "\320\1\x83\207\15", IF_8086|IF_SM}, {I_CMP, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x3D\31", IF_8086|IF_SM}, {I_CMP, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\207\15", IF_386|IF_SM}, {I_CMP, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x3D\41", IF_386|IF_SM}, {I_CMP, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, {I_CMP, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, {I_CMP, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, {I_CMP, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CMPEQPD[] = { {I_CMPEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPEQPS[] = { {I_CMPEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, {I_CMPEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPEQSD[] = { {I_CMPEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, {I_CMPEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPEQSS[] = { {I_CMPEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, {I_CMPEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPLEPD[] = { {I_CMPLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPLEPS[] = { {I_CMPLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, {I_CMPLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPLESD[] = { {I_CMPLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, {I_CMPLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPLESS[] = { {I_CMPLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, {I_CMPLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPLTPD[] = { {I_CMPLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPLTPS[] = { {I_CMPLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, {I_CMPLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPLTSD[] = { {I_CMPLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, {I_CMPLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPLTSS[] = { {I_CMPLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, {I_CMPLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNEQPD[] = { {I_CMPNEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPNEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNEQPS[] = { {I_CMPNEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, {I_CMPNEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNEQSD[] = { {I_CMPNEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, {I_CMPNEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNEQSS[] = { {I_CMPNEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, {I_CMPNEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLEPD[] = { {I_CMPNLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPNLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLEPS[] = { {I_CMPNLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, {I_CMPNLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLESD[] = { {I_CMPNLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, {I_CMPNLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLESS[] = { {I_CMPNLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, {I_CMPNLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLTPD[] = { {I_CMPNLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPNLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLTPS[] = { {I_CMPNLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, {I_CMPNLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLTSD[] = { {I_CMPNLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, {I_CMPNLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPNLTSS[] = { {I_CMPNLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, {I_CMPNLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPORDPD[] = { {I_CMPORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPORDPS[] = { {I_CMPORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, {I_CMPORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPORDSD[] = { {I_CMPORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, {I_CMPORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPORDSS[] = { {I_CMPORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, {I_CMPORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPPD[] = { {I_CMPPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_CMPPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_CMPPS[] = { {I_CMPPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, {I_CMPPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_CMPSB[] = { {I_CMPSB, 0, {0,0,0}, "\332\1\xA6", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CMPSD[] = { {I_CMPSD, 0, {0,0,0}, "\332\321\1\xA7", IF_386}, {I_CMPSD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_CMPSD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_CMPSS[] = { {I_CMPSS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, {I_CMPSS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_CMPSW[] = { {I_CMPSW, 0, {0,0,0}, "\332\320\1\xA7", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CMPUNORDPD[] = { {I_CMPUNORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_CMPUNORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPUNORDPS[] = { {I_CMPUNORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, {I_CMPUNORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPUNORDSD[] = { {I_CMPUNORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, {I_CMPUNORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CMPUNORDSS[] = { {I_CMPUNORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, {I_CMPUNORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CMPXCHG[] = { {I_CMPXCHG, 2, {MEMORY,REG8,0}, "\300\2\x0F\xB0\101", IF_PENT|IF_SM}, {I_CMPXCHG, 2, {REG8,REG8,0}, "\2\x0F\xB0\101", IF_PENT}, {I_CMPXCHG, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB1\101", IF_PENT|IF_SM}, {I_CMPXCHG, 2, {REG16,REG16,0}, "\320\2\x0F\xB1\101", IF_PENT}, {I_CMPXCHG, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB1\101", IF_PENT|IF_SM}, {I_CMPXCHG, 2, {REG32,REG32,0}, "\321\2\x0F\xB1\101", IF_PENT}, ITEMPLATE_END }; static struct itemplate instrux_CMPXCHG486[] = { {I_CMPXCHG486, 2, {MEMORY,REG8,0}, "\300\2\x0F\xA6\101", IF_486|IF_SM|IF_UNDOC}, {I_CMPXCHG486, 2, {REG8,REG8,0}, "\2\x0F\xA6\101", IF_486|IF_UNDOC}, {I_CMPXCHG486, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, {I_CMPXCHG486, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_486|IF_UNDOC}, {I_CMPXCHG486, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, {I_CMPXCHG486, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_486|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_CMPXCHG8B[] = { {I_CMPXCHG8B, 1, {MEMORY,0,0}, "\300\2\x0F\xC7\201", IF_PENT}, ITEMPLATE_END }; static struct itemplate instrux_COMISD[] = { {I_COMISD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, {I_COMISD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_COMISS[] = { {I_COMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, {I_COMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CPUID[] = { {I_CPUID, 0, {0,0,0}, "\2\x0F\xA2", IF_PENT}, ITEMPLATE_END }; static struct itemplate instrux_CVTDQ2PD[] = { {I_CVTDQ2PD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTDQ2PD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTDQ2PS[] = { {I_CVTDQ2PS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTDQ2PS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTPD2DQ[] = { {I_CVTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTPD2PI[] = { {I_CVTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTPD2PS[] = { {I_CVTPD2PS, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPD2PS, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTPI2PD[] = { {I_CVTPI2PD, 2, {XMMREG,MMXREG,0}, "\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPI2PD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTPI2PS[] = { {I_CVTPI2PS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, {I_CVTPI2PS, 2, {XMMREG,MMXREG,0}, "\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, ITEMPLATE_END }; static struct itemplate instrux_CVTPS2DQ[] = { {I_CVTPS2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTPS2PD[] = { {I_CVTPS2PD, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTPS2PD, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTPS2PI[] = { {I_CVTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, {I_CVTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, ITEMPLATE_END }; static struct itemplate instrux_CVTSD2SI[] = { {I_CVTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTSD2SS[] = { {I_CVTSD2SS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTSD2SS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTSI2SD[] = { {I_CVTSI2SD, 2, {XMMREG,REG32,0}, "\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTSI2SD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTSI2SS[] = { {I_CVTSI2SS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_SD|IF_AR1}, {I_CVTSI2SS, 2, {XMMREG,REG32,0}, "\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CVTSS2SD[] = { {I_CVTSS2SD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTSS2SD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTSS2SI[] = { {I_CVTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, {I_CVTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CVTTPD2DQ[] = { {I_CVTTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTTPD2PI[] = { {I_CVTTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTTPS2DQ[] = { {I_CVTTPS2DQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_CVTTPS2PI[] = { {I_CVTTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, {I_CVTTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, ITEMPLATE_END }; static struct itemplate instrux_CVTTSD2SI[] = { {I_CVTTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, {I_CVTTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_CVTTSS2SI[] = { {I_CVTTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, {I_CVTTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_CWD[] = { {I_CWD, 0, {0,0,0}, "\320\1\x99", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_CWDE[] = { {I_CWDE, 0, {0,0,0}, "\321\1\x98", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_DAA[] = { {I_DAA, 0, {0,0,0}, "\1\x27", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_DAS[] = { {I_DAS, 0, {0,0,0}, "\1\x2F", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_DB[] = { ITEMPLATE_END }; static struct itemplate instrux_DD[] = { ITEMPLATE_END }; static struct itemplate instrux_DEC[] = { {I_DEC, 1, {REG16,0,0}, "\320\10\x48", IF_8086}, {I_DEC, 1, {REG32,0,0}, "\321\10\x48", IF_386}, {I_DEC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\201", IF_8086}, {I_DEC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\201", IF_8086}, {I_DEC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\201", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_DIV[] = { {I_DIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\206", IF_8086}, {I_DIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\206", IF_8086}, {I_DIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\206", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_DIVPD[] = { {I_DIVPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, {I_DIVPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_DIVPS[] = { {I_DIVPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, {I_DIVPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_DIVSD[] = { {I_DIVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, {I_DIVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_DIVSS[] = { {I_DIVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, {I_DIVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_DQ[] = { ITEMPLATE_END }; static struct itemplate instrux_DT[] = { ITEMPLATE_END }; static struct itemplate instrux_DW[] = { ITEMPLATE_END }; static struct itemplate instrux_EMMS[] = { {I_EMMS, 0, {0,0,0}, "\2\x0F\x77", IF_PENT|IF_MMX}, ITEMPLATE_END }; static struct itemplate instrux_ENTER[] = { {I_ENTER, 2, {IMMEDIATE,IMMEDIATE,0}, "\1\xC8\30\25", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_EQU[] = { {I_EQU, 1, {IMMEDIATE,0,0}, "\0", IF_8086}, {I_EQU, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\0", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_F2XM1[] = { {I_F2XM1, 0, {0,0,0}, "\2\xD9\xF0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FABS[] = { {I_FABS, 0, {0,0,0}, "\2\xD9\xE1", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FADD[] = { {I_FADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\200", IF_8086|IF_FPU}, {I_FADD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\200", IF_8086|IF_FPU}, {I_FADD, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, {I_FADD, 1, {FPUREG,0,0}, "\1\xD8\10\xC0", IF_8086|IF_FPU}, {I_FADD, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, {I_FADD, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FADDP[] = { {I_FADDP, 1, {FPUREG,0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, {I_FADDP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FBLD[] = { {I_FBLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, {I_FBLD, 1, {MEMORY,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FBSTP[] = { {I_FBSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, {I_FBSTP, 1, {MEMORY,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCHS[] = { {I_FCHS, 0, {0,0,0}, "\2\xD9\xE0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCLEX[] = { {I_FCLEX, 0, {0,0,0}, "\3\x9B\xDB\xE2", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVB[] = { {I_FCMOVB, 1, {FPUREG,0,0}, "\1\xDA\10\xC0", IF_P6|IF_FPU}, {I_FCMOVB, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVBE[] = { {I_FCMOVBE, 1, {FPUREG,0,0}, "\1\xDA\10\xD0", IF_P6|IF_FPU}, {I_FCMOVBE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVE[] = { {I_FCMOVE, 1, {FPUREG,0,0}, "\1\xDA\10\xC8", IF_P6|IF_FPU}, {I_FCMOVE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVNB[] = { {I_FCMOVNB, 1, {FPUREG,0,0}, "\1\xDB\10\xC0", IF_P6|IF_FPU}, {I_FCMOVNB, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVNBE[] = { {I_FCMOVNBE, 1, {FPUREG,0,0}, "\1\xDB\10\xD0", IF_P6|IF_FPU}, {I_FCMOVNBE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVNE[] = { {I_FCMOVNE, 1, {FPUREG,0,0}, "\1\xDB\10\xC8", IF_P6|IF_FPU}, {I_FCMOVNE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVNU[] = { {I_FCMOVNU, 1, {FPUREG,0,0}, "\1\xDB\10\xD8", IF_P6|IF_FPU}, {I_FCMOVNU, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCMOVU[] = { {I_FCMOVU, 1, {FPUREG,0,0}, "\1\xDA\10\xD8", IF_P6|IF_FPU}, {I_FCMOVU, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOM[] = { {I_FCOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\202", IF_8086|IF_FPU}, {I_FCOM, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\202", IF_8086|IF_FPU}, {I_FCOM, 1, {FPUREG,0,0}, "\1\xD8\10\xD0", IF_8086|IF_FPU}, {I_FCOM, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOMI[] = { {I_FCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xF0", IF_P6|IF_FPU}, {I_FCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xF0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOMIP[] = { {I_FCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xF0", IF_P6|IF_FPU}, {I_FCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xF0", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOMP[] = { {I_FCOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\203", IF_8086|IF_FPU}, {I_FCOMP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\203", IF_8086|IF_FPU}, {I_FCOMP, 1, {FPUREG,0,0}, "\1\xD8\10\xD8", IF_8086|IF_FPU}, {I_FCOMP, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOMPP[] = { {I_FCOMPP, 0, {0,0,0}, "\2\xDE\xD9", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FCOS[] = { {I_FCOS, 0, {0,0,0}, "\2\xD9\xFF", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDECSTP[] = { {I_FDECSTP, 0, {0,0,0}, "\2\xD9\xF6", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDISI[] = { {I_FDISI, 0, {0,0,0}, "\3\x9B\xDB\xE1", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDIV[] = { {I_FDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\206", IF_8086|IF_FPU}, {I_FDIV, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\206", IF_8086|IF_FPU}, {I_FDIV, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, {I_FDIV, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, {I_FDIV, 1, {FPUREG,0,0}, "\1\xD8\10\xF0", IF_8086|IF_FPU}, {I_FDIV, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDIVP[] = { {I_FDIVP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, {I_FDIVP, 1, {FPUREG,0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDIVR[] = { {I_FDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\207", IF_8086|IF_FPU}, {I_FDIVR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\207", IF_8086|IF_FPU}, {I_FDIVR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, {I_FDIVR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, {I_FDIVR, 1, {FPUREG,0,0}, "\1\xD8\10\xF8", IF_8086|IF_FPU}, {I_FDIVR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FDIVRP[] = { {I_FDIVRP, 1, {FPUREG,0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, {I_FDIVRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FEMMS[] = { {I_FEMMS, 0, {0,0,0}, "\2\x0F\x0E", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_FENI[] = { {I_FENI, 0, {0,0,0}, "\3\x9B\xDB\xE0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FFREE[] = { {I_FFREE, 1, {FPUREG,0,0}, "\1\xDD\10\xC0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FFREEP[] = { {I_FFREEP, 1, {FPUREG,0,0}, "\1\xDF\10\xC0", IF_286|IF_FPU|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_FIADD[] = { {I_FIADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\200", IF_8086|IF_FPU}, {I_FIADD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\200", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FICOM[] = { {I_FICOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\202", IF_8086|IF_FPU}, {I_FICOM, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\202", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FICOMP[] = { {I_FICOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\203", IF_8086|IF_FPU}, {I_FICOMP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\203", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FIDIV[] = { {I_FIDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\206", IF_8086|IF_FPU}, {I_FIDIV, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FIDIVR[] = { {I_FIDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\207", IF_8086|IF_FPU}, {I_FIDIVR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\207", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FILD[] = { {I_FILD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\200", IF_8086|IF_FPU}, {I_FILD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\200", IF_8086|IF_FPU}, {I_FILD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\205", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FIMUL[] = { {I_FIMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\201", IF_8086|IF_FPU}, {I_FIMUL, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\201", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FINCSTP[] = { {I_FINCSTP, 0, {0,0,0}, "\2\xD9\xF7", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FINIT[] = { {I_FINIT, 0, {0,0,0}, "\3\x9B\xDB\xE3", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FIST[] = { {I_FIST, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\202", IF_8086|IF_FPU}, {I_FIST, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\202", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FISTP[] = { {I_FISTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\203", IF_8086|IF_FPU}, {I_FISTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\203", IF_8086|IF_FPU}, {I_FISTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\207", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FISTTP[] = { {I_FISTTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDD\201", IF_PRESCOTT|IF_FPU}, {I_FISTTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDB\201", IF_PRESCOTT|IF_FPU}, {I_FISTTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\201", IF_PRESCOTT|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FISUB[] = { {I_FISUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\204", IF_8086|IF_FPU}, {I_FISUB, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\204", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FISUBR[] = { {I_FISUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\205", IF_8086|IF_FPU}, {I_FISUBR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\205", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLD[] = { {I_FLD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\200", IF_8086|IF_FPU}, {I_FLD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\200", IF_8086|IF_FPU}, {I_FLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\205", IF_8086|IF_FPU}, {I_FLD, 1, {FPUREG,0,0}, "\1\xD9\10\xC0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLD1[] = { {I_FLD1, 0, {0,0,0}, "\2\xD9\xE8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDCW[] = { {I_FLDCW, 1, {MEMORY,0,0}, "\300\1\xD9\205", IF_8086|IF_FPU|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_FLDENV[] = { {I_FLDENV, 1, {MEMORY,0,0}, "\300\1\xD9\204", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDL2E[] = { {I_FLDL2E, 0, {0,0,0}, "\2\xD9\xEA", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDL2T[] = { {I_FLDL2T, 0, {0,0,0}, "\2\xD9\xE9", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDLG2[] = { {I_FLDLG2, 0, {0,0,0}, "\2\xD9\xEC", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDLN2[] = { {I_FLDLN2, 0, {0,0,0}, "\2\xD9\xED", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDPI[] = { {I_FLDPI, 0, {0,0,0}, "\2\xD9\xEB", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FLDZ[] = { {I_FLDZ, 0, {0,0,0}, "\2\xD9\xEE", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FMUL[] = { {I_FMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\201", IF_8086|IF_FPU}, {I_FMUL, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\201", IF_8086|IF_FPU}, {I_FMUL, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, {I_FMUL, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, {I_FMUL, 1, {FPUREG,0,0}, "\1\xD8\10\xC8", IF_8086|IF_FPU}, {I_FMUL, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FMULP[] = { {I_FMULP, 1, {FPUREG,0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, {I_FMULP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNCLEX[] = { {I_FNCLEX, 0, {0,0,0}, "\2\xDB\xE2", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNDISI[] = { {I_FNDISI, 0, {0,0,0}, "\2\xDB\xE1", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNENI[] = { {I_FNENI, 0, {0,0,0}, "\2\xDB\xE0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNINIT[] = { {I_FNINIT, 0, {0,0,0}, "\2\xDB\xE3", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNOP[] = { {I_FNOP, 0, {0,0,0}, "\2\xD9\xD0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNSAVE[] = { {I_FNSAVE, 1, {MEMORY,0,0}, "\300\1\xDD\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNSTCW[] = { {I_FNSTCW, 1, {MEMORY,0,0}, "\300\1\xD9\207", IF_8086|IF_FPU|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_FNSTENV[] = { {I_FNSTENV, 1, {MEMORY,0,0}, "\300\1\xD9\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FNSTSW[] = { {I_FNSTSW, 1, {MEMORY,0,0}, "\300\1\xDD\207", IF_8086|IF_FPU|IF_SW}, {I_FNSTSW, 1, {REG_AX,0,0}, "\2\xDF\xE0", IF_286|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FPATAN[] = { {I_FPATAN, 0, {0,0,0}, "\2\xD9\xF3", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FPREM[] = { {I_FPREM, 0, {0,0,0}, "\2\xD9\xF8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FPREM1[] = { {I_FPREM1, 0, {0,0,0}, "\2\xD9\xF5", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FPTAN[] = { {I_FPTAN, 0, {0,0,0}, "\2\xD9\xF2", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FRNDINT[] = { {I_FRNDINT, 0, {0,0,0}, "\2\xD9\xFC", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FRSTOR[] = { {I_FRSTOR, 1, {MEMORY,0,0}, "\300\1\xDD\204", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSAVE[] = { {I_FSAVE, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSCALE[] = { {I_FSCALE, 0, {0,0,0}, "\2\xD9\xFD", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSETPM[] = { {I_FSETPM, 0, {0,0,0}, "\2\xDB\xE4", IF_286|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSIN[] = { {I_FSIN, 0, {0,0,0}, "\2\xD9\xFE", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSINCOS[] = { {I_FSINCOS, 0, {0,0,0}, "\2\xD9\xFB", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSQRT[] = { {I_FSQRT, 0, {0,0,0}, "\2\xD9\xFA", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FST[] = { {I_FST, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\202", IF_8086|IF_FPU}, {I_FST, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\202", IF_8086|IF_FPU}, {I_FST, 1, {FPUREG,0,0}, "\1\xDD\10\xD0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSTCW[] = { {I_FSTCW, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\207", IF_8086|IF_FPU|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_FSTENV[] = { {I_FSTENV, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\206", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSTP[] = { {I_FSTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\203", IF_8086|IF_FPU}, {I_FSTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\203", IF_8086|IF_FPU}, {I_FSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\207", IF_8086|IF_FPU}, {I_FSTP, 1, {FPUREG,0,0}, "\1\xDD\10\xD8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSTSW[] = { {I_FSTSW, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\207", IF_8086|IF_FPU|IF_SW}, {I_FSTSW, 1, {REG_AX,0,0}, "\3\x9B\xDF\xE0", IF_286|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSUB[] = { {I_FSUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\204", IF_8086|IF_FPU}, {I_FSUB, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\204", IF_8086|IF_FPU}, {I_FSUB, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, {I_FSUB, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, {I_FSUB, 1, {FPUREG,0,0}, "\1\xD8\10\xE0", IF_8086|IF_FPU}, {I_FSUB, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSUBP[] = { {I_FSUBP, 1, {FPUREG,0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, {I_FSUBP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSUBR[] = { {I_FSUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\205", IF_8086|IF_FPU}, {I_FSUBR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\205", IF_8086|IF_FPU}, {I_FSUBR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, {I_FSUBR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, {I_FSUBR, 1, {FPUREG,0,0}, "\1\xD8\10\xE8", IF_8086|IF_FPU}, {I_FSUBR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FSUBRP[] = { {I_FSUBRP, 1, {FPUREG,0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, {I_FSUBRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FTST[] = { {I_FTST, 0, {0,0,0}, "\2\xD9\xE4", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FUCOM[] = { {I_FUCOM, 1, {FPUREG,0,0}, "\1\xDD\10\xE0", IF_386|IF_FPU}, {I_FUCOM, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE0", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FUCOMI[] = { {I_FUCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xE8", IF_P6|IF_FPU}, {I_FUCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xE8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FUCOMIP[] = { {I_FUCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xE8", IF_P6|IF_FPU}, {I_FUCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xE8", IF_P6|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FUCOMP[] = { {I_FUCOMP, 1, {FPUREG,0,0}, "\1\xDD\10\xE8", IF_386|IF_FPU}, {I_FUCOMP, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE8", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FUCOMPP[] = { {I_FUCOMPP, 0, {0,0,0}, "\2\xDA\xE9", IF_386|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FWAIT[] = { {I_FWAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_FXAM[] = { {I_FXAM, 0, {0,0,0}, "\2\xD9\xE5", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FXCH[] = { {I_FXCH, 0, {0,0,0}, "\2\xD9\xC9", IF_8086|IF_FPU}, {I_FXCH, 1, {FPUREG,0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, {I_FXCH, 2, {FPUREG,FPU0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, {I_FXCH, 2, {FPU0,FPUREG,0}, "\1\xD9\11\xC8", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FXRSTOR[] = { {I_FXRSTOR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\201", IF_P6|IF_SSE|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FXSAVE[] = { {I_FXSAVE, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\200", IF_P6|IF_SSE|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FXTRACT[] = { {I_FXTRACT, 0, {0,0,0}, "\2\xD9\xF4", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FYL2X[] = { {I_FYL2X, 0, {0,0,0}, "\2\xD9\xF1", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_FYL2XP1[] = { {I_FYL2XP1, 0, {0,0,0}, "\2\xD9\xF9", IF_8086|IF_FPU}, ITEMPLATE_END }; static struct itemplate instrux_HADDPD[] = { {I_HADDPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_HADDPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_HADDPS[] = { {I_HADDPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_HADDPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_HLT[] = { {I_HLT, 0, {0,0,0}, "\1\xF4", IF_8086|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_HSUBPD[] = { {I_HSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_HSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_HSUBPS[] = { {I_HSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, {I_HSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_IBTS[] = { {I_IBTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_386|IF_SW|IF_UNDOC}, {I_IBTS, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_386|IF_UNDOC}, {I_IBTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_386|IF_SD|IF_UNDOC}, {I_IBTS, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_386|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_ICEBP[] = { {I_ICEBP, 0, {0,0,0}, "\1\xF1", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_IDIV[] = { {I_IDIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\207", IF_8086}, {I_IDIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\207", IF_8086}, {I_IDIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\207", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_IMUL[] = { {I_IMUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\205", IF_8086}, {I_IMUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\205", IF_8086}, {I_IMUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\205", IF_386}, {I_IMUL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xAF\110", IF_386|IF_SM}, {I_IMUL, 2, {REG16,REG16,0}, "\320\2\x0F\xAF\110", IF_386}, {I_IMUL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xAF\110", IF_386|IF_SM}, {I_IMUL, 2, {REG32,REG32,0}, "\321\2\x0F\xAF\110", IF_386}, {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS8}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, {I_IMUL, 3, {REG16,MEMORY,SBYTE}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS16}, "\320\301\1\x69\110\32", IF_186|IF_SM}, {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE}, "\320\301\135\1\x69\110\132", IF_186|IF_SM}, {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS8}, "\320\1\x6B\110\16", IF_186}, {I_IMUL, 3, {REG16,REG16,SBYTE}, "\320\1\x6B\110\16", IF_186|IF_SM}, {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS16}, "\320\1\x69\110\32", IF_186}, {I_IMUL, 3, {REG16,REG16,IMMEDIATE}, "\320\135\1\x69\110\132", IF_186|IF_SM}, {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS8}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, {I_IMUL, 3, {REG32,MEMORY,SBYTE}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS32}, "\321\301\1\x69\110\42", IF_386|IF_SM}, {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE}, "\321\301\145\1\x69\110\142", IF_386|IF_SM}, {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS8}, "\321\1\x6B\110\16", IF_386}, {I_IMUL, 3, {REG32,REG32,SBYTE}, "\321\1\x6B\110\16", IF_386|IF_SM}, {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS32}, "\321\1\x69\110\42", IF_386}, {I_IMUL, 3, {REG32,REG32,IMMEDIATE}, "\321\145\1\x69\110\142", IF_386|IF_SM}, {I_IMUL, 2, {REG16,IMMEDIATE|BITS8,0}, "\320\1\x6B\100\15", IF_186}, {I_IMUL, 2, {REG16,SBYTE,0}, "\320\1\x6B\100\15", IF_186|IF_SM}, {I_IMUL, 2, {REG16,IMMEDIATE|BITS16,0}, "\320\1\x69\100\31", IF_186}, {I_IMUL, 2, {REG16,IMMEDIATE,0}, "\320\134\1\x69\100\131", IF_186|IF_SM}, {I_IMUL, 2, {REG32,IMMEDIATE|BITS8,0}, "\321\1\x6B\100\15", IF_386}, {I_IMUL, 2, {REG32,SBYTE,0}, "\321\1\x6B\100\15", IF_386|IF_SM}, {I_IMUL, 2, {REG32,IMMEDIATE|BITS32,0}, "\321\1\x69\100\41", IF_386}, {I_IMUL, 2, {REG32,IMMEDIATE,0}, "\321\144\1\x69\100\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_IN[] = { {I_IN, 2, {REG_AL,IMMEDIATE,0}, "\1\xE4\25", IF_8086|IF_SB}, {I_IN, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xE5\25", IF_8086|IF_SB}, {I_IN, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xE5\25", IF_386|IF_SB}, {I_IN, 2, {REG_AL,REG_DX,0}, "\1\xEC", IF_8086}, {I_IN, 2, {REG_AX,REG_DX,0}, "\320\1\xED", IF_8086}, {I_IN, 2, {REG_EAX,REG_DX,0}, "\321\1\xED", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_INC[] = { {I_INC, 1, {REG16,0,0}, "\320\10\x40", IF_8086}, {I_INC, 1, {REG32,0,0}, "\321\10\x40", IF_386}, {I_INC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\200", IF_8086}, {I_INC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\200", IF_8086}, {I_INC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\200", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_INCBIN[] = { ITEMPLATE_END }; static struct itemplate instrux_INSB[] = { {I_INSB, 0, {0,0,0}, "\1\x6C", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_INSD[] = { {I_INSD, 0, {0,0,0}, "\321\1\x6D", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_INSW[] = { {I_INSW, 0, {0,0,0}, "\320\1\x6D", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_INT[] = { {I_INT, 1, {IMMEDIATE,0,0}, "\1\xCD\24", IF_8086|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_INT01[] = { {I_INT01, 0, {0,0,0}, "\1\xF1", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_INT03[] = { {I_INT03, 0, {0,0,0}, "\1\xCC", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_INT1[] = { {I_INT1, 0, {0,0,0}, "\1\xF1", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_INT3[] = { {I_INT3, 0, {0,0,0}, "\1\xCC", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_INTO[] = { {I_INTO, 0, {0,0,0}, "\1\xCE", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_INVD[] = { {I_INVD, 0, {0,0,0}, "\2\x0F\x08", IF_486|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_INVLPG[] = { {I_INVLPG, 1, {MEMORY,0,0}, "\300\2\x0F\x01\207", IF_486|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_IRET[] = { {I_IRET, 0, {0,0,0}, "\322\1\xCF", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_IRETD[] = { {I_IRETD, 0, {0,0,0}, "\321\1\xCF", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_IRETW[] = { {I_IRETW, 0, {0,0,0}, "\320\1\xCF", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_JCXZ[] = { {I_JCXZ, 1, {IMMEDIATE,0,0}, "\310\1\xE3\50", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_JECXZ[] = { {I_JECXZ, 1, {IMMEDIATE,0,0}, "\311\1\xE3\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_JMP[] = { {I_JMP, 1, {IMMEDIATE|SHORT,0,0}, "\1\xEB\50", IF_8086}, {I_JMP, 1, {IMMEDIATE,0,0}, "\371\1\xEB\50", IF_8086}, {I_JMP, 1, {IMMEDIATE,0,0}, "\322\1\xE9\64", IF_8086}, {I_JMP, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE9\64", IF_8086}, {I_JMP, 1, {IMMEDIATE|FAR,0,0}, "\322\1\xEA\34\37", IF_8086}, {I_JMP, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE9\64", IF_8086}, {I_JMP, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE9\64", IF_8086}, {I_JMP, 1, {IMMEDIATE|BITS16|FAR,0,0}, "\320\1\xEA\34\37", IF_8086}, {I_JMP, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE9\64", IF_386}, {I_JMP, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE9\64", IF_386}, {I_JMP, 1, {IMMEDIATE|BITS32|FAR,0,0}, "\321\1\xEA\34\37", IF_386}, {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\xEA\35\30", IF_8086}, {I_JMP, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\xEA\31\30", IF_8086}, {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\xEA\31\30", IF_8086}, {I_JMP, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\xEA\41\30", IF_386}, {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\xEA\41\30", IF_386}, {I_JMP, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\205", IF_8086}, {I_JMP, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\205", IF_8086}, {I_JMP, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\205", IF_386}, {I_JMP, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\204", IF_8086}, {I_JMP, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\204", IF_8086}, {I_JMP, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\204", IF_386}, {I_JMP, 1, {REG16,0,0}, "\320\300\1\xFF\204", IF_8086}, {I_JMP, 1, {REG32,0,0}, "\321\300\1\xFF\204", IF_386}, {I_JMP, 1, {MEMORY,0,0}, "\322\300\1\xFF\204", IF_8086}, {I_JMP, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\204", IF_8086}, {I_JMP, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\204", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_JMPE[] = { {I_JMPE, 1, {IMMEDIATE,0,0}, "\322\2\x0F\xB8\64", IF_IA64}, {I_JMPE, 1, {IMMEDIATE|BITS16,0,0}, "\320\2\x0F\xB8\64", IF_IA64}, {I_JMPE, 1, {IMMEDIATE|BITS32,0,0}, "\321\2\x0F\xB8\64", IF_IA64}, {I_JMPE, 1, {REGMEM|BITS16,0,0}, "\320\2\x0F\x00\206", IF_IA64}, {I_JMPE, 1, {REGMEM|BITS32,0,0}, "\321\2\x0F\x00\206", IF_IA64}, ITEMPLATE_END }; static struct itemplate instrux_LAHF[] = { {I_LAHF, 0, {0,0,0}, "\1\x9F", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_LAR[] = { {I_LAR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x02\110", IF_286|IF_PROT|IF_SM}, {I_LAR, 2, {REG16,REG16,0}, "\320\2\x0F\x02\110", IF_286|IF_PROT}, {I_LAR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x02\110", IF_386|IF_PROT|IF_SM}, {I_LAR, 2, {REG32,REG32,0}, "\321\2\x0F\x02\110", IF_386|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_LDDQU[] = { {I_LDDQU, 2, {XMMREG,MEMORY,0}, "\3\xF2\x0F\xF0\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_LDMXCSR[] = { {I_LDMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\202", IF_KATMAI|IF_SSE|IF_SD}, ITEMPLATE_END }; static struct itemplate instrux_LDS[] = { {I_LDS, 2, {REG16,MEMORY,0}, "\320\301\1\xC5\110", IF_8086}, {I_LDS, 2, {REG32,MEMORY,0}, "\321\301\1\xC5\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LEA[] = { {I_LEA, 2, {REG16,MEMORY,0}, "\320\301\1\x8D\110", IF_8086}, {I_LEA, 2, {REG32,MEMORY,0}, "\321\301\1\x8D\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LEAVE[] = { {I_LEAVE, 0, {0,0,0}, "\1\xC9", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_LES[] = { {I_LES, 2, {REG16,MEMORY,0}, "\320\301\1\xC4\110", IF_8086}, {I_LES, 2, {REG32,MEMORY,0}, "\321\301\1\xC4\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LFENCE[] = { {I_LFENCE, 0, {0,0,0}, "\3\x0F\xAE\xE8", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_LFS[] = { {I_LFS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB4\110", IF_386}, {I_LFS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB4\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LGDT[] = { {I_LGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\202", IF_286|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_LGS[] = { {I_LGS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB5\110", IF_386}, {I_LGS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB5\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LIDT[] = { {I_LIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\203", IF_286|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_LLDT[] = { {I_LLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, {I_LLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, {I_LLDT, 1, {REG16,0,0}, "\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_LMSW[] = { {I_LMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, {I_LMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, {I_LMSW, 1, {REG16,0,0}, "\2\x0F\x01\206", IF_286|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_LOADALL[] = { {I_LOADALL, 0, {0,0,0}, "\2\x0F\x07", IF_386|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_LOADALL286[] = { {I_LOADALL286, 0, {0,0,0}, "\2\x0F\x05", IF_286|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_LODSB[] = { {I_LODSB, 0, {0,0,0}, "\1\xAC", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_LODSD[] = { {I_LODSD, 0, {0,0,0}, "\321\1\xAD", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LODSW[] = { {I_LODSW, 0, {0,0,0}, "\320\1\xAD", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_LOOP[] = { {I_LOOP, 1, {IMMEDIATE,0,0}, "\312\1\xE2\50", IF_8086}, {I_LOOP, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE2\50", IF_8086}, {I_LOOP, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE2\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LOOPE[] = { {I_LOOPE, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, {I_LOOPE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, {I_LOOPE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LOOPNE[] = { {I_LOOPNE, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, {I_LOOPNE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, {I_LOOPNE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LOOPNZ[] = { {I_LOOPNZ, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, {I_LOOPNZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, {I_LOOPNZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LOOPZ[] = { {I_LOOPZ, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, {I_LOOPZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, {I_LOOPZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LSL[] = { {I_LSL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x03\110", IF_286|IF_PROT|IF_SM}, {I_LSL, 2, {REG16,REG16,0}, "\320\2\x0F\x03\110", IF_286|IF_PROT}, {I_LSL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x03\110", IF_386|IF_PROT|IF_SM}, {I_LSL, 2, {REG32,REG32,0}, "\321\2\x0F\x03\110", IF_386|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_LSS[] = { {I_LSS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB2\110", IF_386}, {I_LSS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB2\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_LTR[] = { {I_LTR, 1, {MEMORY,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, {I_LTR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, {I_LTR, 1, {REG16,0,0}, "\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_MASKMOVDQU[] = { {I_MASKMOVDQU, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF7\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MASKMOVQ[] = { {I_MASKMOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF7\110", IF_KATMAI|IF_MMX}, ITEMPLATE_END }; static struct itemplate instrux_MAXPD[] = { {I_MAXPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, {I_MAXPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MAXPS[] = { {I_MAXPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, {I_MAXPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MAXSD[] = { {I_MAXSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, {I_MAXSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MAXSS[] = { {I_MAXSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, {I_MAXSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MFENCE[] = { {I_MFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF0", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MINPD[] = { {I_MINPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, {I_MINPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MINPS[] = { {I_MINPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, {I_MINPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MINSD[] = { {I_MINSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, {I_MINSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MINSS[] = { {I_MINSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, {I_MINSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MONITOR[] = { {I_MONITOR, 0, {0,0,0}, "\3\x0F\x01\xC8", IF_PRESCOTT}, {I_MONITOR, 3, {REG_EAX,REG_ECX,REG_EDX}, "\3\x0F\x01\xC8", IF_PRESCOTT}, ITEMPLATE_END }; static struct itemplate instrux_MOV[] = { {I_MOV, 2, {MEMORY,REG_SREG,0}, "\300\1\x8C\101", IF_8086|IF_SM}, {I_MOV, 2, {REG16,REG_SREG,0}, "\320\1\x8C\101", IF_8086}, {I_MOV, 2, {REG32,REG_SREG,0}, "\321\1\x8C\101", IF_386}, {I_MOV, 2, {REG_SREG,MEMORY,0}, "\301\1\x8E\110", IF_8086|IF_SM}, {I_MOV, 2, {REG_SREG,REG16,0}, "\1\x8E\110", IF_8086}, {I_MOV, 2, {REG_SREG,REG32,0}, "\1\x8E\110", IF_386}, {I_MOV, 2, {REG_AL,MEM_OFFS,0}, "\301\1\xA0\45", IF_8086|IF_SM}, {I_MOV, 2, {REG_AX,MEM_OFFS,0}, "\301\320\1\xA1\45", IF_8086|IF_SM}, {I_MOV, 2, {REG_EAX,MEM_OFFS,0}, "\301\321\1\xA1\45", IF_386|IF_SM}, {I_MOV, 2, {MEM_OFFS,REG_AL,0}, "\300\1\xA2\44", IF_8086|IF_SM}, {I_MOV, 2, {MEM_OFFS,REG_AX,0}, "\300\320\1\xA3\44", IF_8086|IF_SM}, {I_MOV, 2, {MEM_OFFS,REG_EAX,0}, "\300\321\1\xA3\44", IF_386|IF_SM}, {I_MOV, 2, {REG32,REG_CREG,0}, "\2\x0F\x20\101", IF_386|IF_PRIV}, {I_MOV, 2, {REG32,REG_DREG,0}, "\2\x0F\x21\101", IF_386|IF_PRIV}, {I_MOV, 2, {REG32,REG_TREG,0}, "\2\x0F\x24\101", IF_386|IF_PRIV}, {I_MOV, 2, {REG_CREG,REG32,0}, "\2\x0F\x22\110", IF_386|IF_PRIV}, {I_MOV, 2, {REG_DREG,REG32,0}, "\2\x0F\x23\110", IF_386|IF_PRIV}, {I_MOV, 2, {REG_TREG,REG32,0}, "\2\x0F\x26\110", IF_386|IF_PRIV}, {I_MOV, 2, {MEMORY,REG8,0}, "\300\1\x88\101", IF_8086|IF_SM}, {I_MOV, 2, {REG8,REG8,0}, "\1\x88\101", IF_8086}, {I_MOV, 2, {MEMORY,REG16,0}, "\320\300\1\x89\101", IF_8086|IF_SM}, {I_MOV, 2, {REG16,REG16,0}, "\320\1\x89\101", IF_8086}, {I_MOV, 2, {MEMORY,REG32,0}, "\321\300\1\x89\101", IF_386|IF_SM}, {I_MOV, 2, {REG32,REG32,0}, "\321\1\x89\101", IF_386}, {I_MOV, 2, {REG8,MEMORY,0}, "\301\1\x8A\110", IF_8086|IF_SM}, {I_MOV, 2, {REG8,REG8,0}, "\1\x8A\110", IF_8086}, {I_MOV, 2, {REG16,MEMORY,0}, "\320\301\1\x8B\110", IF_8086|IF_SM}, {I_MOV, 2, {REG16,REG16,0}, "\320\1\x8B\110", IF_8086}, {I_MOV, 2, {REG32,MEMORY,0}, "\321\301\1\x8B\110", IF_386|IF_SM}, {I_MOV, 2, {REG32,REG32,0}, "\321\1\x8B\110", IF_386}, {I_MOV, 2, {REG8,IMMEDIATE,0}, "\10\xB0\21", IF_8086|IF_SM}, {I_MOV, 2, {REG16,IMMEDIATE,0}, "\320\10\xB8\31", IF_8086|IF_SM}, {I_MOV, 2, {REG32,IMMEDIATE,0}, "\321\10\xB8\41", IF_386|IF_SM}, {I_MOV, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, {I_MOV, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, {I_MOV, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, {I_MOV, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, {I_MOV, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, {I_MOV, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVAPD[] = { {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x29\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVAPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x29\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVAPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVAPS[] = { {I_MOVAPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x28\110", IF_KATMAI|IF_SSE}, {I_MOVAPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x29\101", IF_KATMAI|IF_SSE}, {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x28\110", IF_KATMAI|IF_SSE}, {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x29\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVD[] = { {I_MOVD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6E\110", IF_PENT|IF_MMX|IF_SD}, {I_MOVD, 2, {MMXREG,REG32,0}, "\2\x0F\x6E\110", IF_PENT|IF_MMX}, {I_MOVD, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7E\101", IF_PENT|IF_MMX|IF_SD}, {I_MOVD, 2, {REG32,MMXREG,0}, "\2\x0F\x7E\101", IF_PENT|IF_MMX}, {I_MOVD, 2, {XMMREG,REG32,0}, "\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVDDUP[] = { {I_MOVDDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, {I_MOVDDUP, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_MOVDQ2Q[] = { {I_MOVDQ2Q, 2, {MMXREG,XMMREG,0}, "\3\xF2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVDQA[] = { {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVDQA, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVDQA, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVDQU[] = { {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVDQU, 2, {MEMORY,XMMREG,0}, "\333\300\2\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVDQU, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVHLPS[] = { {I_MOVHLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x12\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVHPD[] = { {I_MOVHPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x17\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x16\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVHPS[] = { {I_MOVHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x16\110", IF_KATMAI|IF_SSE}, {I_MOVHPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x17\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVLHPS[] = { {I_MOVLHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x16\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVLPD[] = { {I_MOVLPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x13\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x12\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVLPS[] = { {I_MOVLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x12\110", IF_KATMAI|IF_SSE}, {I_MOVLPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x13\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVMSKPD[] = { {I_MOVMSKPD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x50\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVMSKPS[] = { {I_MOVMSKPS, 2, {REG32,XMMREG,0}, "\2\x0F\x50\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVNTDQ[] = { {I_MOVNTDQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xE7\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVNTI[] = { {I_MOVNTI, 2, {MEMORY,REG32,0}, "\300\2\x0F\xC3\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVNTPD[] = { {I_MOVNTPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x2B\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVNTPS[] = { {I_MOVNTPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x2B\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVNTQ[] = { {I_MOVNTQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\xE7\101", IF_KATMAI|IF_MMX|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVQ[] = { {I_MOVQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6F\110", IF_PENT|IF_MMX|IF_SM}, {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6F\110", IF_PENT|IF_MMX}, {I_MOVQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7F\101", IF_PENT|IF_MMX|IF_SM}, {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x7F\101", IF_PENT|IF_MMX}, {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xD6\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVQ2DQ[] = { {I_MOVQ2DQ, 2, {XMMREG,MMXREG,0}, "\333\2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVSB[] = { {I_MOVSB, 0, {0,0,0}, "\1\xA4", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_MOVSD[] = { {I_MOVSD, 0, {0,0,0}, "\321\1\xA5", IF_386}, {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVSD, 2, {MEMORY,XMMREG,0}, "\300\3\xF2\x0F\x11\101", IF_WILLAMETTE|IF_SSE2}, {I_MOVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MOVSHDUP[] = { {I_MOVSHDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, {I_MOVSHDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_MOVSLDUP[] = { {I_MOVSLDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, {I_MOVSLDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, ITEMPLATE_END }; static struct itemplate instrux_MOVSS[] = { {I_MOVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, {I_MOVSS, 2, {MEMORY,XMMREG,0}, "\300\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVSW[] = { {I_MOVSW, 0, {0,0,0}, "\320\1\xA5", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_MOVSX[] = { {I_MOVSX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBE\110", IF_386|IF_SB}, {I_MOVSX, 2, {REG16,REG8,0}, "\320\2\x0F\xBE\110", IF_386}, {I_MOVSX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xBE\110", IF_386}, {I_MOVSX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xBF\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_MOVUPD[] = { {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, {I_MOVUPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x11\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_MOVUPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MOVUPS[] = { {I_MOVUPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, {I_MOVUPS, 2, {MEMORY,XMMREG,0}, "\300\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MOVZX[] = { {I_MOVZX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB6\110", IF_386|IF_SB}, {I_MOVZX, 2, {REG16,REG8,0}, "\320\2\x0F\xB6\110", IF_386}, {I_MOVZX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xB6\110", IF_386}, {I_MOVZX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xB7\110", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_MUL[] = { {I_MUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\204", IF_8086}, {I_MUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\204", IF_8086}, {I_MUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\204", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_MULPD[] = { {I_MULPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, {I_MULPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_MULPS[] = { {I_MULPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_KATMAI|IF_SSE}, {I_MULPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x59\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MULSD[] = { {I_MULSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, {I_MULSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_MULSS[] = { {I_MULSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, {I_MULSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_MWAIT[] = { {I_MWAIT, 0, {0,0,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, {I_MWAIT, 2, {REG_EAX,REG_ECX,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, ITEMPLATE_END }; static struct itemplate instrux_NEG[] = { {I_NEG, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\203", IF_8086}, {I_NEG, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\203", IF_8086}, {I_NEG, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\203", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_NOP[] = { {I_NOP, 0, {0,0,0}, "\1\x90", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_NOT[] = { {I_NOT, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\202", IF_8086}, {I_NOT, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\202", IF_8086}, {I_NOT, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\202", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_OR[] = { {I_OR, 2, {MEMORY,REG8,0}, "\300\1\x08\101", IF_8086|IF_SM}, {I_OR, 2, {REG8,REG8,0}, "\1\x08\101", IF_8086}, {I_OR, 2, {MEMORY,REG16,0}, "\320\300\1\x09\101", IF_8086|IF_SM}, {I_OR, 2, {REG16,REG16,0}, "\320\1\x09\101", IF_8086}, {I_OR, 2, {MEMORY,REG32,0}, "\321\300\1\x09\101", IF_386|IF_SM}, {I_OR, 2, {REG32,REG32,0}, "\321\1\x09\101", IF_386}, {I_OR, 2, {REG8,MEMORY,0}, "\301\1\x0A\110", IF_8086|IF_SM}, {I_OR, 2, {REG8,REG8,0}, "\1\x0A\110", IF_8086}, {I_OR, 2, {REG16,MEMORY,0}, "\320\301\1\x0B\110", IF_8086|IF_SM}, {I_OR, 2, {REG16,REG16,0}, "\320\1\x0B\110", IF_8086}, {I_OR, 2, {REG32,MEMORY,0}, "\321\301\1\x0B\110", IF_386|IF_SM}, {I_OR, 2, {REG32,REG32,0}, "\321\1\x0B\110", IF_386}, {I_OR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\201\15", IF_8086}, {I_OR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\201\15", IF_386}, {I_OR, 2, {REG_AL,IMMEDIATE,0}, "\1\x0C\21", IF_8086|IF_SM}, {I_OR, 2, {REG_AX,SBYTE,0}, "\320\1\x83\201\15", IF_8086|IF_SM}, {I_OR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x0D\31", IF_8086|IF_SM}, {I_OR, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\201\15", IF_386|IF_SM}, {I_OR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x0D\41", IF_386|IF_SM}, {I_OR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, {I_OR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, {I_OR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, {I_OR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, {I_OR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, {I_OR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_ORPD[] = { {I_ORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_ORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_ORPS[] = { {I_ORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x56\110", IF_KATMAI|IF_SSE}, {I_ORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x56\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_OUT[] = { {I_OUT, 2, {IMMEDIATE,REG_AL,0}, "\1\xE6\24", IF_8086|IF_SB}, {I_OUT, 2, {IMMEDIATE,REG_AX,0}, "\320\1\xE7\24", IF_8086|IF_SB}, {I_OUT, 2, {IMMEDIATE,REG_EAX,0}, "\321\1\xE7\24", IF_386|IF_SB}, {I_OUT, 2, {REG_DX,REG_AL,0}, "\1\xEE", IF_8086}, {I_OUT, 2, {REG_DX,REG_AX,0}, "\320\1\xEF", IF_8086}, {I_OUT, 2, {REG_DX,REG_EAX,0}, "\321\1\xEF", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_OUTSB[] = { {I_OUTSB, 0, {0,0,0}, "\1\x6E", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_OUTSD[] = { {I_OUTSD, 0, {0,0,0}, "\321\1\x6F", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_OUTSW[] = { {I_OUTSW, 0, {0,0,0}, "\320\1\x6F", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_PACKSSDW[] = { {I_PACKSSDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6B\110", IF_PENT|IF_MMX|IF_SM}, {I_PACKSSDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6B\110", IF_PENT|IF_MMX}, {I_PACKSSDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2}, {I_PACKSSDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PACKSSWB[] = { {I_PACKSSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x63\110", IF_PENT|IF_MMX|IF_SM}, {I_PACKSSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x63\110", IF_PENT|IF_MMX}, {I_PACKSSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2}, {I_PACKSSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PACKUSWB[] = { {I_PACKUSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x67\110", IF_PENT|IF_MMX|IF_SM}, {I_PACKUSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x67\110", IF_PENT|IF_MMX}, {I_PACKUSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2}, {I_PACKUSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PADDB[] = { {I_PADDB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFC\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFC\110", IF_PENT|IF_MMX}, {I_PADDB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2}, {I_PADDB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PADDD[] = { {I_PADDD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFE\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFE\110", IF_PENT|IF_MMX}, {I_PADDD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2}, {I_PADDD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PADDQ[] = { {I_PADDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, {I_PADDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PADDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, {I_PADDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PADDSB[] = { {I_PADDSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEC\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEC\110", IF_PENT|IF_MMX}, {I_PADDSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PADDSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PADDSIW[] = { {I_PADDSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x51\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PADDSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x51\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PADDSW[] = { {I_PADDSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xED\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xED\110", IF_PENT|IF_MMX}, {I_PADDSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PADDSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PADDUSB[] = { {I_PADDUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDC\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDC\110", IF_PENT|IF_MMX}, {I_PADDUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PADDUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PADDUSW[] = { {I_PADDUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDD\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDD\110", IF_PENT|IF_MMX}, {I_PADDUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PADDUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PADDW[] = { {I_PADDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFD\110", IF_PENT|IF_MMX|IF_SM}, {I_PADDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFD\110", IF_PENT|IF_MMX}, {I_PADDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2}, {I_PADDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PAND[] = { {I_PAND, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDB\110", IF_PENT|IF_MMX|IF_SM}, {I_PAND, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDB\110", IF_PENT|IF_MMX}, {I_PAND, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2}, {I_PAND, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PANDN[] = { {I_PANDN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDF\110", IF_PENT|IF_MMX|IF_SM}, {I_PANDN, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDF\110", IF_PENT|IF_MMX}, {I_PANDN, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2}, {I_PANDN, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PAUSE[] = { {I_PAUSE, 0, {0,0,0}, "\333\1\x90", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PAVEB[] = { {I_PAVEB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x50\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PAVEB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x50\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PAVGB[] = { {I_PAVGB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE0\110", IF_KATMAI|IF_MMX}, {I_PAVGB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE0\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PAVGB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2}, {I_PAVGB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PAVGUSB[] = { {I_PAVGUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW|IF_SM}, {I_PAVGUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PAVGW[] = { {I_PAVGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE3\110", IF_KATMAI|IF_MMX}, {I_PAVGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE3\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PAVGW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2}, {I_PAVGW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPEQB[] = { {I_PCMPEQB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x74\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPEQB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x74\110", IF_PENT|IF_MMX}, {I_PCMPEQB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPEQB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPEQD[] = { {I_PCMPEQD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x76\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPEQD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x76\110", IF_PENT|IF_MMX}, {I_PCMPEQD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPEQD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPEQW[] = { {I_PCMPEQW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x75\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPEQW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x75\110", IF_PENT|IF_MMX}, {I_PCMPEQW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPEQW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPGTB[] = { {I_PCMPGTB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x64\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPGTB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x64\110", IF_PENT|IF_MMX}, {I_PCMPGTB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPGTB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPGTD[] = { {I_PCMPGTD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x66\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPGTD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x66\110", IF_PENT|IF_MMX}, {I_PCMPGTD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPGTD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PCMPGTW[] = { {I_PCMPGTW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x65\110", IF_PENT|IF_MMX|IF_SM}, {I_PCMPGTW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x65\110", IF_PENT|IF_MMX}, {I_PCMPGTW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2}, {I_PCMPGTW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PDISTIB[] = { {I_PDISTIB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PEXTRW[] = { {I_PEXTRW, 3, {REG32,MMXREG,IMMEDIATE}, "\2\x0F\xC5\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PEXTRW, 3, {REG32,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC5\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PF2ID[] = { {I_PF2ID, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW|IF_SM}, {I_PF2ID, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PF2IW[] = { {I_PF2IW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW|IF_SM}, {I_PF2IW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFACC[] = { {I_PFACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW|IF_SM}, {I_PFACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFADD[] = { {I_PFADD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW|IF_SM}, {I_PFADD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFCMPEQ[] = { {I_PFCMPEQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW|IF_SM}, {I_PFCMPEQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFCMPGE[] = { {I_PFCMPGE, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW|IF_SM}, {I_PFCMPGE, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFCMPGT[] = { {I_PFCMPGT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW|IF_SM}, {I_PFCMPGT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFMAX[] = { {I_PFMAX, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW|IF_SM}, {I_PFMAX, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFMIN[] = { {I_PFMIN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW|IF_SM}, {I_PFMIN, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFMUL[] = { {I_PFMUL, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW|IF_SM}, {I_PFMUL, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFNACC[] = { {I_PFNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW|IF_SM}, {I_PFNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFPNACC[] = { {I_PFPNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW|IF_SM}, {I_PFPNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFRCP[] = { {I_PFRCP, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW|IF_SM}, {I_PFRCP, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFRCPIT1[] = { {I_PFRCPIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW|IF_SM}, {I_PFRCPIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFRCPIT2[] = { {I_PFRCPIT2, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW|IF_SM}, {I_PFRCPIT2, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFRSQIT1[] = { {I_PFRSQIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW|IF_SM}, {I_PFRSQIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFRSQRT[] = { {I_PFRSQRT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW|IF_SM}, {I_PFRSQRT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFSUB[] = { {I_PFSUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW|IF_SM}, {I_PFSUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PFSUBR[] = { {I_PFSUBR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW|IF_SM}, {I_PFSUBR, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PI2FD[] = { {I_PI2FD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW|IF_SM}, {I_PI2FD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PI2FW[] = { {I_PI2FW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW|IF_SM}, {I_PI2FW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PINSRW[] = { {I_PINSRW, 3, {MMXREG,REG16,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PINSRW, 3, {MMXREG,REG32,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PINSRW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PINSRW, 3, {MMXREG,MEMORY|BITS16,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PINSRW, 3, {XMMREG,REG16,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PINSRW, 3, {XMMREG,REG32,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PINSRW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PINSRW, 3, {XMMREG,MEMORY|BITS16,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PMACHRIW[] = { {I_PMACHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5E\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMADDWD[] = { {I_PMADDWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF5\110", IF_PENT|IF_MMX|IF_SM}, {I_PMADDWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF5\110", IF_PENT|IF_MMX}, {I_PMADDWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PMADDWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PMAGW[] = { {I_PMAGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x52\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PMAGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x52\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMAXSW[] = { {I_PMAXSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEE\110", IF_KATMAI|IF_MMX}, {I_PMAXSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEE\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PMAXSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2}, {I_PMAXSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMAXUB[] = { {I_PMAXUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDE\110", IF_KATMAI|IF_MMX}, {I_PMAXUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDE\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PMAXUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2}, {I_PMAXUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMINSW[] = { {I_PMINSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEA\110", IF_KATMAI|IF_MMX}, {I_PMINSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEA\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PMINSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2}, {I_PMINSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMINUB[] = { {I_PMINUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDA\110", IF_KATMAI|IF_MMX}, {I_PMINUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDA\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PMINUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2}, {I_PMINUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMOVMSKB[] = { {I_PMOVMSKB, 2, {REG32,MMXREG,0}, "\2\x0F\xD7\110", IF_KATMAI|IF_MMX}, {I_PMOVMSKB, 2, {REG32,XMMREG,0}, "\3\x66\x0F\xD7\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PMULHRIW[] = { {I_PMULHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PMULHRIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMULHRWA[] = { {I_PMULHRWA, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW|IF_SM}, {I_PMULHRWA, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PMULHRWC[] = { {I_PMULHRWC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PMULHRWC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x59\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMULHUW[] = { {I_PMULHUW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE4\110", IF_KATMAI|IF_MMX}, {I_PMULHUW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE4\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PMULHUW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2}, {I_PMULHUW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMULHW[] = { {I_PMULHW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE5\110", IF_PENT|IF_MMX|IF_SM}, {I_PMULHW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE5\110", IF_PENT|IF_MMX}, {I_PMULHW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PMULHW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PMULLW[] = { {I_PMULLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD5\110", IF_PENT|IF_MMX|IF_SM}, {I_PMULLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD5\110", IF_PENT|IF_MMX}, {I_PMULLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PMULLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PMULUDQ[] = { {I_PMULUDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, {I_PMULUDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PMULUDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, {I_PMULUDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PMVGEZB[] = { {I_PMVGEZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5C\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMVLZB[] = { {I_PMVLZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMVNZB[] = { {I_PMVNZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PMVZB[] = { {I_PMVZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x58\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_POP[] = { {I_POP, 1, {REG16,0,0}, "\320\10\x58", IF_8086}, {I_POP, 1, {REG32,0,0}, "\321\10\x58", IF_386}, {I_POP, 1, {REGMEM|BITS16,0,0}, "\320\300\1\x8F\200", IF_8086}, {I_POP, 1, {REGMEM|BITS32,0,0}, "\321\300\1\x8F\200", IF_386}, {I_POP, 1, {REG_CS,0,0}, "\1\x0F", IF_8086|IF_UNDOC}, {I_POP, 1, {REG_DESS,0,0}, "\4", IF_8086}, {I_POP, 1, {REG_FSGS,0,0}, "\1\x0F\5", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_POPA[] = { {I_POPA, 0, {0,0,0}, "\322\1\x61", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_POPAD[] = { {I_POPAD, 0, {0,0,0}, "\321\1\x61", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_POPAW[] = { {I_POPAW, 0, {0,0,0}, "\320\1\x61", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_POPF[] = { {I_POPF, 0, {0,0,0}, "\322\1\x9D", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_POPFD[] = { {I_POPFD, 0, {0,0,0}, "\321\1\x9D", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_POPFW[] = { {I_POPFW, 0, {0,0,0}, "\320\1\x9D", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_POR[] = { {I_POR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEB\110", IF_PENT|IF_MMX|IF_SM}, {I_POR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEB\110", IF_PENT|IF_MMX}, {I_POR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_POR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCH[] = { {I_PREFETCH, 1, {MEMORY,0,0}, "\2\x0F\x0D\200", IF_PENT|IF_3DNOW|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCHNTA[] = { {I_PREFETCHNTA, 1, {MEMORY,0,0}, "\300\2\x0F\x18\200", IF_KATMAI}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCHT0[] = { {I_PREFETCHT0, 1, {MEMORY,0,0}, "\300\2\x0F\x18\201", IF_KATMAI}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCHT1[] = { {I_PREFETCHT1, 1, {MEMORY,0,0}, "\300\2\x0F\x18\202", IF_KATMAI}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCHT2[] = { {I_PREFETCHT2, 1, {MEMORY,0,0}, "\300\2\x0F\x18\203", IF_KATMAI}, ITEMPLATE_END }; static struct itemplate instrux_PREFETCHW[] = { {I_PREFETCHW, 1, {MEMORY,0,0}, "\2\x0F\x0D\201", IF_PENT|IF_3DNOW|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PSADBW[] = { {I_PSADBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF6\110", IF_KATMAI|IF_MMX}, {I_PSADBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF6\110", IF_KATMAI|IF_MMX|IF_SM}, {I_PSADBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2}, {I_PSADBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PSHUFD[] = { {I_PSHUFD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PSHUFD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PSHUFHW[] = { {I_PSHUFHW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PSHUFHW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PSHUFLW[] = { {I_PSHUFLW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_PSHUFLW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PSHUFW[] = { {I_PSHUFW, 3, {MMXREG,MMXREG,IMMEDIATE}, "\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, {I_PSHUFW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SM2|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_PSLLD[] = { {I_PSLLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF2\110", IF_PENT|IF_MMX|IF_SM}, {I_PSLLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF2\110", IF_PENT|IF_MMX}, {I_PSLLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\206\25", IF_PENT|IF_MMX}, {I_PSLLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSLLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2}, {I_PSLLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSLLDQ[] = { {I_PSLLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\207\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSLLQ[] = { {I_PSLLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF3\110", IF_PENT|IF_MMX|IF_SM}, {I_PSLLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF3\110", IF_PENT|IF_MMX}, {I_PSLLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\206\25", IF_PENT|IF_MMX}, {I_PSLLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSLLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2}, {I_PSLLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSLLW[] = { {I_PSLLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF1\110", IF_PENT|IF_MMX|IF_SM}, {I_PSLLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF1\110", IF_PENT|IF_MMX}, {I_PSLLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\206\25", IF_PENT|IF_MMX}, {I_PSLLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSLLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2}, {I_PSLLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRAD[] = { {I_PSRAD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE2\110", IF_PENT|IF_MMX|IF_SM}, {I_PSRAD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE2\110", IF_PENT|IF_MMX}, {I_PSRAD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\204\25", IF_PENT|IF_MMX}, {I_PSRAD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSRAD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2}, {I_PSRAD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRAW[] = { {I_PSRAW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE1\110", IF_PENT|IF_MMX|IF_SM}, {I_PSRAW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE1\110", IF_PENT|IF_MMX}, {I_PSRAW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\204\25", IF_PENT|IF_MMX}, {I_PSRAW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSRAW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2}, {I_PSRAW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRLD[] = { {I_PSRLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD2\110", IF_PENT|IF_MMX|IF_SM}, {I_PSRLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD2\110", IF_PENT|IF_MMX}, {I_PSRLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\202\25", IF_PENT|IF_MMX}, {I_PSRLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSRLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2}, {I_PSRLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRLDQ[] = { {I_PSRLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\203\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRLQ[] = { {I_PSRLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD3\110", IF_PENT|IF_MMX|IF_SM}, {I_PSRLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD3\110", IF_PENT|IF_MMX}, {I_PSRLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\202\25", IF_PENT|IF_MMX}, {I_PSRLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSRLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2}, {I_PSRLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSRLW[] = { {I_PSRLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD1\110", IF_PENT|IF_MMX|IF_SM}, {I_PSRLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD1\110", IF_PENT|IF_MMX}, {I_PSRLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\202\25", IF_PENT|IF_MMX}, {I_PSRLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSRLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2}, {I_PSRLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, ITEMPLATE_END }; static struct itemplate instrux_PSUBB[] = { {I_PSUBB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF8\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF8\110", IF_PENT|IF_MMX}, {I_PSUBB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBD[] = { {I_PSUBD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFA\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFA\110", IF_PENT|IF_MMX}, {I_PSUBD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBQ[] = { {I_PSUBQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, {I_PSUBQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, {I_PSUBQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PSUBSB[] = { {I_PSUBSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE8\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE8\110", IF_PENT|IF_MMX}, {I_PSUBSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBSIW[] = { {I_PSUBSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, {I_PSUBSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x55\110", IF_PENT|IF_MMX|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_PSUBSW[] = { {I_PSUBSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE9\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE9\110", IF_PENT|IF_MMX}, {I_PSUBSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBUSB[] = { {I_PSUBUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD8\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD8\110", IF_PENT|IF_MMX}, {I_PSUBUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBUSW[] = { {I_PSUBUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD9\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD9\110", IF_PENT|IF_MMX}, {I_PSUBUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSUBW[] = { {I_PSUBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF9\110", IF_PENT|IF_MMX|IF_SM}, {I_PSUBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF9\110", IF_PENT|IF_MMX}, {I_PSUBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PSUBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PSWAPD[] = { {I_PSWAPD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW|IF_SM}, {I_PSWAPD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKHBW[] = { {I_PUNPCKHBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x68\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKHBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x68\110", IF_PENT|IF_MMX}, {I_PUNPCKHBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKHBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKHDQ[] = { {I_PUNPCKHDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6A\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKHDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6A\110", IF_PENT|IF_MMX}, {I_PUNPCKHDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKHDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKHQDQ[] = { {I_PUNPCKHQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2}, {I_PUNPCKHQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKHWD[] = { {I_PUNPCKHWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x69\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKHWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x69\110", IF_PENT|IF_MMX}, {I_PUNPCKHWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKHWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKLBW[] = { {I_PUNPCKLBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x60\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKLBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x60\110", IF_PENT|IF_MMX}, {I_PUNPCKLBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKLBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKLDQ[] = { {I_PUNPCKLDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x62\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKLDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x62\110", IF_PENT|IF_MMX}, {I_PUNPCKLDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKLDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKLQDQ[] = { {I_PUNPCKLQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2}, {I_PUNPCKLQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_PUNPCKLWD[] = { {I_PUNPCKLWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x61\110", IF_PENT|IF_MMX|IF_SM}, {I_PUNPCKLWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x61\110", IF_PENT|IF_MMX}, {I_PUNPCKLWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PUNPCKLWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_PUSH[] = { {I_PUSH, 1, {REG16,0,0}, "\320\10\x50", IF_8086}, {I_PUSH, 1, {REG32,0,0}, "\321\10\x50", IF_386}, {I_PUSH, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\206", IF_8086}, {I_PUSH, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\206", IF_386}, {I_PUSH, 1, {REG_CS,0,0}, "\6", IF_8086}, {I_PUSH, 1, {REG_DESS,0,0}, "\6", IF_8086}, {I_PUSH, 1, {REG_FSGS,0,0}, "\1\x0F\7", IF_386}, {I_PUSH, 1, {IMMEDIATE|BITS8,0,0}, "\1\x6A\14", IF_186}, {I_PUSH, 1, {SBYTE,0,0}, "\1\x6A\14", IF_186}, {I_PUSH, 1, {IMMEDIATE|BITS16,0,0}, "\320\133\1\x68\130", IF_186}, {I_PUSH, 1, {IMMEDIATE|BITS32,0,0}, "\321\143\1\x68\140", IF_386}, {I_PUSH, 1, {IMMEDIATE,0,0}, "\1\x68\34", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_PUSHA[] = { {I_PUSHA, 0, {0,0,0}, "\322\1\x60", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_PUSHAD[] = { {I_PUSHAD, 0, {0,0,0}, "\321\1\x60", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_PUSHAW[] = { {I_PUSHAW, 0, {0,0,0}, "\320\1\x60", IF_186}, ITEMPLATE_END }; static struct itemplate instrux_PUSHF[] = { {I_PUSHF, 0, {0,0,0}, "\322\1\x9C", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_PUSHFD[] = { {I_PUSHFD, 0, {0,0,0}, "\321\1\x9C", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_PUSHFW[] = { {I_PUSHFW, 0, {0,0,0}, "\320\1\x9C", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_PXOR[] = { {I_PXOR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEF\110", IF_PENT|IF_MMX|IF_SM}, {I_PXOR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEF\110", IF_PENT|IF_MMX}, {I_PXOR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, {I_PXOR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_RCL[] = { {I_RCL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\202", IF_8086}, {I_RCL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\202", IF_8086}, {I_RCL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\202\25", IF_186|IF_SB}, {I_RCL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\202", IF_8086}, {I_RCL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\202", IF_8086}, {I_RCL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\202\25", IF_186|IF_SB}, {I_RCL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\202", IF_386}, {I_RCL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\202", IF_386}, {I_RCL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\202\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_RCPPS[] = { {I_RCPPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, {I_RCPPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_RCPSS[] = { {I_RCPSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, {I_RCPSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_RCR[] = { {I_RCR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\203", IF_8086}, {I_RCR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\203", IF_8086}, {I_RCR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\203\25", IF_186|IF_SB}, {I_RCR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\203", IF_8086}, {I_RCR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\203", IF_8086}, {I_RCR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\203\25", IF_186|IF_SB}, {I_RCR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\203", IF_386}, {I_RCR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\203", IF_386}, {I_RCR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\203\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_RDMSR[] = { {I_RDMSR, 0, {0,0,0}, "\2\x0F\x32", IF_PENT|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_RDPMC[] = { {I_RDPMC, 0, {0,0,0}, "\2\x0F\x33", IF_P6}, ITEMPLATE_END }; static struct itemplate instrux_RDSHR[] = { {I_RDSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x36\200", IF_P6|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_RDTSC[] = { {I_RDTSC, 0, {0,0,0}, "\2\x0F\x31", IF_PENT}, ITEMPLATE_END }; static struct itemplate instrux_RESB[] = { {I_RESB, 1, {IMMEDIATE,0,0}, "\340", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_RESD[] = { ITEMPLATE_END }; static struct itemplate instrux_RESQ[] = { ITEMPLATE_END }; static struct itemplate instrux_REST[] = { ITEMPLATE_END }; static struct itemplate instrux_RESW[] = { ITEMPLATE_END }; static struct itemplate instrux_RET[] = { {I_RET, 0, {0,0,0}, "\1\xC3", IF_8086}, {I_RET, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_RETF[] = { {I_RETF, 0, {0,0,0}, "\1\xCB", IF_8086}, {I_RETF, 1, {IMMEDIATE,0,0}, "\1\xCA\30", IF_8086|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_RETN[] = { {I_RETN, 0, {0,0,0}, "\1\xC3", IF_8086}, {I_RETN, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, ITEMPLATE_END }; static struct itemplate instrux_ROL[] = { {I_ROL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\200", IF_8086}, {I_ROL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\200", IF_8086}, {I_ROL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\200\25", IF_186|IF_SB}, {I_ROL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\200", IF_8086}, {I_ROL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\200", IF_8086}, {I_ROL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\200\25", IF_186|IF_SB}, {I_ROL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\200", IF_386}, {I_ROL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\200", IF_386}, {I_ROL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\200\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_ROR[] = { {I_ROR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\201", IF_8086}, {I_ROR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\201", IF_8086}, {I_ROR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\201\25", IF_186|IF_SB}, {I_ROR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\201", IF_8086}, {I_ROR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\201", IF_8086}, {I_ROR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\201\25", IF_186|IF_SB}, {I_ROR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\201", IF_386}, {I_ROR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\201", IF_386}, {I_ROR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\201\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_RSDC[] = { {I_RSDC, 2, {REG_SREG,MEMORY|BITS80,0}, "\301\2\x0F\x79\110", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_RSLDT[] = { {I_RSLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7B\200", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_RSM[] = { {I_RSM, 0, {0,0,0}, "\2\x0F\xAA", IF_PENT|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_RSQRTPS[] = { {I_RSQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, {I_RSQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_RSQRTSS[] = { {I_RSQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, {I_RSQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_RSTS[] = { {I_RSTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7D\200", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_SAHF[] = { {I_SAHF, 0, {0,0,0}, "\1\x9E", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_SAL[] = { {I_SAL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, {I_SAL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, {I_SAL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, {I_SAL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, {I_SAL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, {I_SAL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, {I_SAL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, {I_SAL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, {I_SAL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_SALC[] = { {I_SALC, 0, {0,0,0}, "\1\xD6", IF_8086|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_SAR[] = { {I_SAR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\207", IF_8086}, {I_SAR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\207", IF_8086}, {I_SAR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\207\25", IF_186|IF_SB}, {I_SAR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\207", IF_8086}, {I_SAR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\207", IF_8086}, {I_SAR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\207\25", IF_186|IF_SB}, {I_SAR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\207", IF_386}, {I_SAR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\207", IF_386}, {I_SAR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\207\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_SBB[] = { {I_SBB, 2, {MEMORY,REG8,0}, "\300\1\x18\101", IF_8086|IF_SM}, {I_SBB, 2, {REG8,REG8,0}, "\1\x18\101", IF_8086}, {I_SBB, 2, {MEMORY,REG16,0}, "\320\300\1\x19\101", IF_8086|IF_SM}, {I_SBB, 2, {REG16,REG16,0}, "\320\1\x19\101", IF_8086}, {I_SBB, 2, {MEMORY,REG32,0}, "\321\300\1\x19\101", IF_386|IF_SM}, {I_SBB, 2, {REG32,REG32,0}, "\321\1\x19\101", IF_386}, {I_SBB, 2, {REG8,MEMORY,0}, "\301\1\x1A\110", IF_8086|IF_SM}, {I_SBB, 2, {REG8,REG8,0}, "\1\x1A\110", IF_8086}, {I_SBB, 2, {REG16,MEMORY,0}, "\320\301\1\x1B\110", IF_8086|IF_SM}, {I_SBB, 2, {REG16,REG16,0}, "\320\1\x1B\110", IF_8086}, {I_SBB, 2, {REG32,MEMORY,0}, "\321\301\1\x1B\110", IF_386|IF_SM}, {I_SBB, 2, {REG32,REG32,0}, "\321\1\x1B\110", IF_386}, {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\203\15", IF_8086}, {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\203\15", IF_386}, {I_SBB, 2, {REG_AL,IMMEDIATE,0}, "\1\x1C\21", IF_8086|IF_SM}, {I_SBB, 2, {REG_AX,SBYTE,0}, "\320\1\x83\203\15", IF_8086|IF_SM}, {I_SBB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x1D\31", IF_8086|IF_SM}, {I_SBB, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\203\15", IF_386|IF_SM}, {I_SBB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x1D\41", IF_386|IF_SM}, {I_SBB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, {I_SBB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, {I_SBB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, {I_SBB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_SCASB[] = { {I_SCASB, 0, {0,0,0}, "\332\1\xAE", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_SCASD[] = { {I_SCASD, 0, {0,0,0}, "\332\321\1\xAF", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_SCASW[] = { {I_SCASW, 0, {0,0,0}, "\332\320\1\xAF", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_SFENCE[] = { {I_SFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF8", IF_KATMAI}, ITEMPLATE_END }; static struct itemplate instrux_SGDT[] = { {I_SGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\200", IF_286}, ITEMPLATE_END }; static struct itemplate instrux_SHL[] = { {I_SHL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, {I_SHL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, {I_SHL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, {I_SHL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, {I_SHL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, {I_SHL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, {I_SHL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, {I_SHL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, {I_SHL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_SHLD[] = { {I_SHLD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHLD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHLD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHLD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHLD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xA5\101", IF_386|IF_SM}, {I_SHLD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xA5\101", IF_386}, {I_SHLD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xA5\101", IF_386|IF_SM}, {I_SHLD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xA5\101", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_SHR[] = { {I_SHR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\205", IF_8086}, {I_SHR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\205", IF_8086}, {I_SHR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\205\25", IF_186|IF_SB}, {I_SHR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\205", IF_8086}, {I_SHR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\205", IF_8086}, {I_SHR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\205\25", IF_186|IF_SB}, {I_SHR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\205", IF_386}, {I_SHR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\205", IF_386}, {I_SHR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\205\25", IF_386|IF_SB}, ITEMPLATE_END }; static struct itemplate instrux_SHRD[] = { {I_SHRD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHRD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHRD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHRD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, {I_SHRD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xAD\101", IF_386|IF_SM}, {I_SHRD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xAD\101", IF_386}, {I_SHRD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xAD\101", IF_386|IF_SM}, {I_SHRD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xAD\101", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_SHUFPD[] = { {I_SHUFPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, {I_SHUFPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_SHUFPS[] = { {I_SHUFPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, {I_SHUFPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, ITEMPLATE_END }; static struct itemplate instrux_SIDT[] = { {I_SIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\201", IF_286}, ITEMPLATE_END }; static struct itemplate instrux_SLDT[] = { {I_SLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\200", IF_286}, {I_SLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\200", IF_286}, {I_SLDT, 1, {REG16,0,0}, "\320\1\x0F\17\200", IF_286}, {I_SLDT, 1, {REG32,0,0}, "\321\1\x0F\17\200", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_SMI[] = { {I_SMI, 0, {0,0,0}, "\1\xF1", IF_386|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_SMINT[] = { {I_SMINT, 0, {0,0,0}, "\2\x0F\x38", IF_P6|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_SMINTOLD[] = { {I_SMINTOLD, 0, {0,0,0}, "\2\x0F\x7E", IF_486|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_SMSW[] = { {I_SMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\204", IF_286}, {I_SMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\204", IF_286}, {I_SMSW, 1, {REG16,0,0}, "\320\2\x0F\x01\204", IF_286}, {I_SMSW, 1, {REG32,0,0}, "\321\2\x0F\x01\204", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_SQRTPD[] = { {I_SQRTPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, {I_SQRTPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_SQRTPS[] = { {I_SQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, {I_SQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_SQRTSD[] = { {I_SQRTSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, {I_SQRTSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_SQRTSS[] = { {I_SQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, {I_SQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_STC[] = { {I_STC, 0, {0,0,0}, "\1\xF9", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_STD[] = { {I_STD, 0, {0,0,0}, "\1\xFD", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_STI[] = { {I_STI, 0, {0,0,0}, "\1\xFB", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_STMXCSR[] = { {I_STMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\203", IF_KATMAI|IF_SSE|IF_SD}, ITEMPLATE_END }; static struct itemplate instrux_STOSB[] = { {I_STOSB, 0, {0,0,0}, "\1\xAA", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_STOSD[] = { {I_STOSD, 0, {0,0,0}, "\321\1\xAB", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_STOSW[] = { {I_STOSW, 0, {0,0,0}, "\320\1\xAB", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_STR[] = { {I_STR, 1, {MEMORY,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, {I_STR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, {I_STR, 1, {REG16,0,0}, "\320\1\x0F\17\201", IF_286|IF_PROT}, {I_STR, 1, {REG32,0,0}, "\321\1\x0F\17\201", IF_386|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_SUB[] = { {I_SUB, 2, {MEMORY,REG8,0}, "\300\1\x28\101", IF_8086|IF_SM}, {I_SUB, 2, {REG8,REG8,0}, "\1\x28\101", IF_8086}, {I_SUB, 2, {MEMORY,REG16,0}, "\320\300\1\x29\101", IF_8086|IF_SM}, {I_SUB, 2, {REG16,REG16,0}, "\320\1\x29\101", IF_8086}, {I_SUB, 2, {MEMORY,REG32,0}, "\321\300\1\x29\101", IF_386|IF_SM}, {I_SUB, 2, {REG32,REG32,0}, "\321\1\x29\101", IF_386}, {I_SUB, 2, {REG8,MEMORY,0}, "\301\1\x2A\110", IF_8086|IF_SM}, {I_SUB, 2, {REG8,REG8,0}, "\1\x2A\110", IF_8086}, {I_SUB, 2, {REG16,MEMORY,0}, "\320\301\1\x2B\110", IF_8086|IF_SM}, {I_SUB, 2, {REG16,REG16,0}, "\320\1\x2B\110", IF_8086}, {I_SUB, 2, {REG32,MEMORY,0}, "\321\301\1\x2B\110", IF_386|IF_SM}, {I_SUB, 2, {REG32,REG32,0}, "\321\1\x2B\110", IF_386}, {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\205\15", IF_8086}, {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\205\15", IF_386}, {I_SUB, 2, {REG_AL,IMMEDIATE,0}, "\1\x2C\21", IF_8086|IF_SM}, {I_SUB, 2, {REG_AX,SBYTE,0}, "\320\1\x83\205\15", IF_8086|IF_SM}, {I_SUB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x2D\31", IF_8086|IF_SM}, {I_SUB, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\205\15", IF_386|IF_SM}, {I_SUB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x2D\41", IF_386|IF_SM}, {I_SUB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, {I_SUB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, {I_SUB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, {I_SUB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_SUBPD[] = { {I_SUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, {I_SUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_SUBPS[] = { {I_SUBPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, {I_SUBPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_SUBSD[] = { {I_SUBSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, {I_SUBSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_SUBSS[] = { {I_SUBSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, {I_SUBSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_SVDC[] = { {I_SVDC, 2, {MEMORY|BITS80,REG_SREG,0}, "\300\2\x0F\x78\101", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_SVLDT[] = { {I_SVLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7A\200", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_SVTS[] = { {I_SVTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7C\200", IF_486|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_SYSCALL[] = { {I_SYSCALL, 0, {0,0,0}, "\2\x0F\x05", IF_P6|IF_AMD}, ITEMPLATE_END }; static struct itemplate instrux_SYSENTER[] = { {I_SYSENTER, 0, {0,0,0}, "\2\x0F\x34", IF_P6}, ITEMPLATE_END }; static struct itemplate instrux_SYSEXIT[] = { {I_SYSEXIT, 0, {0,0,0}, "\2\x0F\x35", IF_P6|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_SYSRET[] = { {I_SYSRET, 0, {0,0,0}, "\2\x0F\x07", IF_P6|IF_PRIV|IF_AMD}, ITEMPLATE_END }; static struct itemplate instrux_TEST[] = { {I_TEST, 2, {MEMORY,REG8,0}, "\300\1\x84\101", IF_8086|IF_SM}, {I_TEST, 2, {REG8,REG8,0}, "\1\x84\101", IF_8086}, {I_TEST, 2, {MEMORY,REG16,0}, "\320\300\1\x85\101", IF_8086|IF_SM}, {I_TEST, 2, {REG16,REG16,0}, "\320\1\x85\101", IF_8086}, {I_TEST, 2, {MEMORY,REG32,0}, "\321\300\1\x85\101", IF_386|IF_SM}, {I_TEST, 2, {REG32,REG32,0}, "\321\1\x85\101", IF_386}, {I_TEST, 2, {REG8,MEMORY,0}, "\301\1\x84\110", IF_8086|IF_SM}, {I_TEST, 2, {REG16,MEMORY,0}, "\320\301\1\x85\110", IF_8086|IF_SM}, {I_TEST, 2, {REG32,MEMORY,0}, "\321\301\1\x85\110", IF_386|IF_SM}, {I_TEST, 2, {REG_AL,IMMEDIATE,0}, "\1\xA8\21", IF_8086|IF_SM}, {I_TEST, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xA9\31", IF_8086|IF_SM}, {I_TEST, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xA9\41", IF_386|IF_SM}, {I_TEST, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, {I_TEST, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, {I_TEST, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, {I_TEST, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, {I_TEST, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, {I_TEST, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_UCOMISD[] = { {I_UCOMISD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, {I_UCOMISD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, ITEMPLATE_END }; static struct itemplate instrux_UCOMISS[] = { {I_UCOMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, {I_UCOMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_UD0[] = { {I_UD0, 0, {0,0,0}, "\2\x0F\xFF", IF_286|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_UD1[] = { {I_UD1, 0, {0,0,0}, "\2\x0F\xB9", IF_286|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_UD2[] = { {I_UD2, 0, {0,0,0}, "\2\x0F\x0B", IF_286}, ITEMPLATE_END }; static struct itemplate instrux_UMOV[] = { {I_UMOV, 2, {MEMORY,REG8,0}, "\300\2\x0F\x10\101", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x10\101", IF_386|IF_UNDOC}, {I_UMOV, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x11\101", IF_386|IF_UNDOC}, {I_UMOV, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x11\101", IF_386|IF_UNDOC}, {I_UMOV, 2, {REG8,MEMORY,0}, "\301\2\x0F\x12\110", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x12\110", IF_386|IF_UNDOC}, {I_UMOV, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x13\110", IF_386|IF_UNDOC}, {I_UMOV, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x13\110", IF_386|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_UNPCKHPD[] = { {I_UNPCKHPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2}, {I_UNPCKHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_UNPCKHPS[] = { {I_UNPCKHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x15\110", IF_KATMAI|IF_SSE}, {I_UNPCKHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x15\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_UNPCKLPD[] = { {I_UNPCKLPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2}, {I_UNPCKLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_UNPCKLPS[] = { {I_UNPCKLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x14\110", IF_KATMAI|IF_SSE}, {I_UNPCKLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x14\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_VERR[] = { {I_VERR, 1, {MEMORY,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, {I_VERR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, {I_VERR, 1, {REG16,0,0}, "\1\x0F\17\204", IF_286|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_VERW[] = { {I_VERW, 1, {MEMORY,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, {I_VERW, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, {I_VERW, 1, {REG16,0,0}, "\1\x0F\17\205", IF_286|IF_PROT}, ITEMPLATE_END }; static struct itemplate instrux_WAIT[] = { {I_WAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_WBINVD[] = { {I_WBINVD, 0, {0,0,0}, "\2\x0F\x09", IF_486|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_WRMSR[] = { {I_WRMSR, 0, {0,0,0}, "\2\x0F\x30", IF_PENT|IF_PRIV}, ITEMPLATE_END }; static struct itemplate instrux_WRSHR[] = { {I_WRSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x37\200", IF_P6|IF_CYRIX|IF_SMM}, ITEMPLATE_END }; static struct itemplate instrux_XADD[] = { {I_XADD, 2, {MEMORY,REG8,0}, "\300\2\x0F\xC0\101", IF_486|IF_SM}, {I_XADD, 2, {REG8,REG8,0}, "\2\x0F\xC0\101", IF_486}, {I_XADD, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xC1\101", IF_486|IF_SM}, {I_XADD, 2, {REG16,REG16,0}, "\320\2\x0F\xC1\101", IF_486}, {I_XADD, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xC1\101", IF_486|IF_SM}, {I_XADD, 2, {REG32,REG32,0}, "\321\2\x0F\xC1\101", IF_486}, ITEMPLATE_END }; static struct itemplate instrux_XBTS[] = { {I_XBTS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xA6\110", IF_386|IF_SW|IF_UNDOC}, {I_XBTS, 2, {REG16,REG16,0}, "\320\2\x0F\xA6\110", IF_386|IF_UNDOC}, {I_XBTS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xA6\110", IF_386|IF_SD|IF_UNDOC}, {I_XBTS, 2, {REG32,REG32,0}, "\321\2\x0F\xA6\110", IF_386|IF_UNDOC}, ITEMPLATE_END }; static struct itemplate instrux_XCHG[] = { {I_XCHG, 2, {REG_AX,REG16,0}, "\320\11\x90", IF_8086}, {I_XCHG, 2, {REG_EAX,REG32,0}, "\321\11\x90", IF_386}, {I_XCHG, 2, {REG16,REG_AX,0}, "\320\10\x90", IF_8086}, {I_XCHG, 2, {REG32,REG_EAX,0}, "\321\10\x90", IF_386}, {I_XCHG, 2, {REG8,MEMORY,0}, "\301\1\x86\110", IF_8086|IF_SM}, {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\110", IF_8086}, {I_XCHG, 2, {REG16,MEMORY,0}, "\320\301\1\x87\110", IF_8086|IF_SM}, {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\110", IF_8086}, {I_XCHG, 2, {REG32,MEMORY,0}, "\321\301\1\x87\110", IF_386|IF_SM}, {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\110", IF_386}, {I_XCHG, 2, {MEMORY,REG8,0}, "\300\1\x86\101", IF_8086|IF_SM}, {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\101", IF_8086}, {I_XCHG, 2, {MEMORY,REG16,0}, "\320\300\1\x87\101", IF_8086|IF_SM}, {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\101", IF_8086}, {I_XCHG, 2, {MEMORY,REG32,0}, "\321\300\1\x87\101", IF_386|IF_SM}, {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\101", IF_386}, ITEMPLATE_END }; static struct itemplate instrux_XLAT[] = { {I_XLAT, 0, {0,0,0}, "\1\xD7", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_XLATB[] = { {I_XLATB, 0, {0,0,0}, "\1\xD7", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_XOR[] = { {I_XOR, 2, {MEMORY,REG8,0}, "\300\1\x30\101", IF_8086|IF_SM}, {I_XOR, 2, {REG8,REG8,0}, "\1\x30\101", IF_8086}, {I_XOR, 2, {MEMORY,REG16,0}, "\320\300\1\x31\101", IF_8086|IF_SM}, {I_XOR, 2, {REG16,REG16,0}, "\320\1\x31\101", IF_8086}, {I_XOR, 2, {MEMORY,REG32,0}, "\321\300\1\x31\101", IF_386|IF_SM}, {I_XOR, 2, {REG32,REG32,0}, "\321\1\x31\101", IF_386}, {I_XOR, 2, {REG8,MEMORY,0}, "\301\1\x32\110", IF_8086|IF_SM}, {I_XOR, 2, {REG8,REG8,0}, "\1\x32\110", IF_8086}, {I_XOR, 2, {REG16,MEMORY,0}, "\320\301\1\x33\110", IF_8086|IF_SM}, {I_XOR, 2, {REG16,REG16,0}, "\320\1\x33\110", IF_8086}, {I_XOR, 2, {REG32,MEMORY,0}, "\321\301\1\x33\110", IF_386|IF_SM}, {I_XOR, 2, {REG32,REG32,0}, "\321\1\x33\110", IF_386}, {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\206\15", IF_8086}, {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\206\15", IF_386}, {I_XOR, 2, {REG_AL,IMMEDIATE,0}, "\1\x34\21", IF_8086|IF_SM}, {I_XOR, 2, {REG_AX,SBYTE,0}, "\320\1\x83\206\15", IF_8086|IF_SM}, {I_XOR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x35\31", IF_8086|IF_SM}, {I_XOR, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\206\15", IF_386|IF_SM}, {I_XOR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x35\41", IF_386|IF_SM}, {I_XOR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, {I_XOR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, {I_XOR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, {I_XOR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_XORPD[] = { {I_XORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2}, {I_XORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, ITEMPLATE_END }; static struct itemplate instrux_XORPS[] = { {I_XORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x57\110", IF_KATMAI|IF_SSE}, {I_XORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x57\110", IF_KATMAI|IF_SSE}, ITEMPLATE_END }; static struct itemplate instrux_XSTORE[] = { {I_XSTORE, 0, {0,0,0}, "\3\x0F\xA7\xC0", IF_P6|IF_CYRIX}, ITEMPLATE_END }; static struct itemplate instrux_CMOVcc[] = { {I_CMOVcc, 2, {REG16,MEMORY,0}, "\320\301\1\x0F\330\x40\110", IF_P6|IF_SM}, {I_CMOVcc, 2, {REG16,REG16,0}, "\320\1\x0F\330\x40\110", IF_P6}, {I_CMOVcc, 2, {REG32,MEMORY,0}, "\321\301\1\x0F\330\x40\110", IF_P6|IF_SM}, {I_CMOVcc, 2, {REG32,REG32,0}, "\321\1\x0F\330\x40\110", IF_P6}, ITEMPLATE_END }; static struct itemplate instrux_Jcc[] = { {I_Jcc, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\x0F\330\x80\64", IF_386}, {I_Jcc, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\x0F\330\x80\64", IF_386}, {I_Jcc, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\x0F\330\x80\64", IF_386}, {I_Jcc, 1, {IMMEDIATE|SHORT,0,0}, "\330\x70\50", IF_8086}, {I_Jcc, 1, {IMMEDIATE,0,0}, "\370\330\x70\50", IF_8086}, {I_Jcc, 1, {IMMEDIATE,0,0}, "\1\x0F\330\x80\64", IF_386}, {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x71\373\1\xE9\64", IF_8086}, {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x70\50", IF_8086}, ITEMPLATE_END }; static struct itemplate instrux_SETcc[] = { {I_SETcc, 1, {MEMORY,0,0}, "\300\1\x0F\330\x90\200", IF_386|IF_SB}, {I_SETcc, 1, {REG8,0,0}, "\300\1\x0F\330\x90\200", IF_386}, ITEMPLATE_END }; struct itemplate *nasm_instructions[] = { instrux_AAA, instrux_AAD, instrux_AAM, instrux_AAS, instrux_ADC, instrux_ADD, instrux_ADDPD, instrux_ADDPS, instrux_ADDSD, instrux_ADDSS, instrux_ADDSUBPD, instrux_ADDSUBPS, instrux_AND, instrux_ANDNPD, instrux_ANDNPS, instrux_ANDPD, instrux_ANDPS, instrux_ARPL, instrux_BOUND, instrux_BSF, instrux_BSR, instrux_BSWAP, instrux_BT, instrux_BTC, instrux_BTR, instrux_BTS, instrux_CALL, instrux_CBW, instrux_CDQ, instrux_CLC, instrux_CLD, instrux_CLFLUSH, instrux_CLI, instrux_CLTS, instrux_CMC, instrux_CMP, instrux_CMPEQPD, instrux_CMPEQPS, instrux_CMPEQSD, instrux_CMPEQSS, instrux_CMPLEPD, instrux_CMPLEPS, instrux_CMPLESD, instrux_CMPLESS, instrux_CMPLTPD, instrux_CMPLTPS, instrux_CMPLTSD, instrux_CMPLTSS, instrux_CMPNEQPD, instrux_CMPNEQPS, instrux_CMPNEQSD, instrux_CMPNEQSS, instrux_CMPNLEPD, instrux_CMPNLEPS, instrux_CMPNLESD, instrux_CMPNLESS, instrux_CMPNLTPD, instrux_CMPNLTPS, instrux_CMPNLTSD, instrux_CMPNLTSS, instrux_CMPORDPD, instrux_CMPORDPS, instrux_CMPORDSD, instrux_CMPORDSS, instrux_CMPPD, instrux_CMPPS, instrux_CMPSB, instrux_CMPSD, instrux_CMPSS, instrux_CMPSW, instrux_CMPUNORDPD, instrux_CMPUNORDPS, instrux_CMPUNORDSD, instrux_CMPUNORDSS, instrux_CMPXCHG, instrux_CMPXCHG486, instrux_CMPXCHG8B, instrux_COMISD, instrux_COMISS, instrux_CPUID, instrux_CVTDQ2PD, instrux_CVTDQ2PS, instrux_CVTPD2DQ, instrux_CVTPD2PI, instrux_CVTPD2PS, instrux_CVTPI2PD, instrux_CVTPI2PS, instrux_CVTPS2DQ, instrux_CVTPS2PD, instrux_CVTPS2PI, instrux_CVTSD2SI, instrux_CVTSD2SS, instrux_CVTSI2SD, instrux_CVTSI2SS, instrux_CVTSS2SD, instrux_CVTSS2SI, instrux_CVTTPD2DQ, instrux_CVTTPD2PI, instrux_CVTTPS2DQ, instrux_CVTTPS2PI, instrux_CVTTSD2SI, instrux_CVTTSS2SI, instrux_CWD, instrux_CWDE, instrux_DAA, instrux_DAS, instrux_DB, instrux_DD, instrux_DEC, instrux_DIV, instrux_DIVPD, instrux_DIVPS, instrux_DIVSD, instrux_DIVSS, instrux_DQ, instrux_DT, instrux_DW, instrux_EMMS, instrux_ENTER, instrux_EQU, instrux_F2XM1, instrux_FABS, instrux_FADD, instrux_FADDP, instrux_FBLD, instrux_FBSTP, instrux_FCHS, instrux_FCLEX, instrux_FCMOVB, instrux_FCMOVBE, instrux_FCMOVE, instrux_FCMOVNB, instrux_FCMOVNBE, instrux_FCMOVNE, instrux_FCMOVNU, instrux_FCMOVU, instrux_FCOM, instrux_FCOMI, instrux_FCOMIP, instrux_FCOMP, instrux_FCOMPP, instrux_FCOS, instrux_FDECSTP, instrux_FDISI, instrux_FDIV, instrux_FDIVP, instrux_FDIVR, instrux_FDIVRP, instrux_FEMMS, instrux_FENI, instrux_FFREE, instrux_FFREEP, instrux_FIADD, instrux_FICOM, instrux_FICOMP, instrux_FIDIV, instrux_FIDIVR, instrux_FILD, instrux_FIMUL, instrux_FINCSTP, instrux_FINIT, instrux_FIST, instrux_FISTP, instrux_FISTTP, instrux_FISUB, instrux_FISUBR, instrux_FLD, instrux_FLD1, instrux_FLDCW, instrux_FLDENV, instrux_FLDL2E, instrux_FLDL2T, instrux_FLDLG2, instrux_FLDLN2, instrux_FLDPI, instrux_FLDZ, instrux_FMUL, instrux_FMULP, instrux_FNCLEX, instrux_FNDISI, instrux_FNENI, instrux_FNINIT, instrux_FNOP, instrux_FNSAVE, instrux_FNSTCW, instrux_FNSTENV, instrux_FNSTSW, instrux_FPATAN, instrux_FPREM, instrux_FPREM1, instrux_FPTAN, instrux_FRNDINT, instrux_FRSTOR, instrux_FSAVE, instrux_FSCALE, instrux_FSETPM, instrux_FSIN, instrux_FSINCOS, instrux_FSQRT, instrux_FST, instrux_FSTCW, instrux_FSTENV, instrux_FSTP, instrux_FSTSW, instrux_FSUB, instrux_FSUBP, instrux_FSUBR, instrux_FSUBRP, instrux_FTST, instrux_FUCOM, instrux_FUCOMI, instrux_FUCOMIP, instrux_FUCOMP, instrux_FUCOMPP, instrux_FWAIT, instrux_FXAM, instrux_FXCH, instrux_FXRSTOR, instrux_FXSAVE, instrux_FXTRACT, instrux_FYL2X, instrux_FYL2XP1, instrux_HADDPD, instrux_HADDPS, instrux_HLT, instrux_HSUBPD, instrux_HSUBPS, instrux_IBTS, instrux_ICEBP, instrux_IDIV, instrux_IMUL, instrux_IN, instrux_INC, instrux_INCBIN, instrux_INSB, instrux_INSD, instrux_INSW, instrux_INT, instrux_INT01, instrux_INT03, instrux_INT1, instrux_INT3, instrux_INTO, instrux_INVD, instrux_INVLPG, instrux_IRET, instrux_IRETD, instrux_IRETW, instrux_JCXZ, instrux_JECXZ, instrux_JMP, instrux_JMPE, instrux_LAHF, instrux_LAR, instrux_LDDQU, instrux_LDMXCSR, instrux_LDS, instrux_LEA, instrux_LEAVE, instrux_LES, instrux_LFENCE, instrux_LFS, instrux_LGDT, instrux_LGS, instrux_LIDT, instrux_LLDT, instrux_LMSW, instrux_LOADALL, instrux_LOADALL286, instrux_LODSB, instrux_LODSD, instrux_LODSW, instrux_LOOP, instrux_LOOPE, instrux_LOOPNE, instrux_LOOPNZ, instrux_LOOPZ, instrux_LSL, instrux_LSS, instrux_LTR, instrux_MASKMOVDQU, instrux_MASKMOVQ, instrux_MAXPD, instrux_MAXPS, instrux_MAXSD, instrux_MAXSS, instrux_MFENCE, instrux_MINPD, instrux_MINPS, instrux_MINSD, instrux_MINSS, instrux_MONITOR, instrux_MOV, instrux_MOVAPD, instrux_MOVAPS, instrux_MOVD, instrux_MOVDDUP, instrux_MOVDQ2Q, instrux_MOVDQA, instrux_MOVDQU, instrux_MOVHLPS, instrux_MOVHPD, instrux_MOVHPS, instrux_MOVLHPS, instrux_MOVLPD, instrux_MOVLPS, instrux_MOVMSKPD, instrux_MOVMSKPS, instrux_MOVNTDQ, instrux_MOVNTI, instrux_MOVNTPD, instrux_MOVNTPS, instrux_MOVNTQ, instrux_MOVQ, instrux_MOVQ2DQ, instrux_MOVSB, instrux_MOVSD, instrux_MOVSHDUP, instrux_MOVSLDUP, instrux_MOVSS, instrux_MOVSW, instrux_MOVSX, instrux_MOVUPD, instrux_MOVUPS, instrux_MOVZX, instrux_MUL, instrux_MULPD, instrux_MULPS, instrux_MULSD, instrux_MULSS, instrux_MWAIT, instrux_NEG, instrux_NOP, instrux_NOT, instrux_OR, instrux_ORPD, instrux_ORPS, instrux_OUT, instrux_OUTSB, instrux_OUTSD, instrux_OUTSW, instrux_PACKSSDW, instrux_PACKSSWB, instrux_PACKUSWB, instrux_PADDB, instrux_PADDD, instrux_PADDQ, instrux_PADDSB, instrux_PADDSIW, instrux_PADDSW, instrux_PADDUSB, instrux_PADDUSW, instrux_PADDW, instrux_PAND, instrux_PANDN, instrux_PAUSE, instrux_PAVEB, instrux_PAVGB, instrux_PAVGUSB, instrux_PAVGW, instrux_PCMPEQB, instrux_PCMPEQD, instrux_PCMPEQW, instrux_PCMPGTB, instrux_PCMPGTD, instrux_PCMPGTW, instrux_PDISTIB, instrux_PEXTRW, instrux_PF2ID, instrux_PF2IW, instrux_PFACC, instrux_PFADD, instrux_PFCMPEQ, instrux_PFCMPGE, instrux_PFCMPGT, instrux_PFMAX, instrux_PFMIN, instrux_PFMUL, instrux_PFNACC, instrux_PFPNACC, instrux_PFRCP, instrux_PFRCPIT1, instrux_PFRCPIT2, instrux_PFRSQIT1, instrux_PFRSQRT, instrux_PFSUB, instrux_PFSUBR, instrux_PI2FD, instrux_PI2FW, instrux_PINSRW, instrux_PMACHRIW, instrux_PMADDWD, instrux_PMAGW, instrux_PMAXSW, instrux_PMAXUB, instrux_PMINSW, instrux_PMINUB, instrux_PMOVMSKB, instrux_PMULHRIW, instrux_PMULHRWA, instrux_PMULHRWC, instrux_PMULHUW, instrux_PMULHW, instrux_PMULLW, instrux_PMULUDQ, instrux_PMVGEZB, instrux_PMVLZB, instrux_PMVNZB, instrux_PMVZB, instrux_POP, instrux_POPA, instrux_POPAD, instrux_POPAW, instrux_POPF, instrux_POPFD, instrux_POPFW, instrux_POR, instrux_PREFETCH, instrux_PREFETCHNTA, instrux_PREFETCHT0, instrux_PREFETCHT1, instrux_PREFETCHT2, instrux_PREFETCHW, instrux_PSADBW, instrux_PSHUFD, instrux_PSHUFHW, instrux_PSHUFLW, instrux_PSHUFW, instrux_PSLLD, instrux_PSLLDQ, instrux_PSLLQ, instrux_PSLLW, instrux_PSRAD, instrux_PSRAW, instrux_PSRLD, instrux_PSRLDQ, instrux_PSRLQ, instrux_PSRLW, instrux_PSUBB, instrux_PSUBD, instrux_PSUBQ, instrux_PSUBSB, instrux_PSUBSIW, instrux_PSUBSW, instrux_PSUBUSB, instrux_PSUBUSW, instrux_PSUBW, instrux_PSWAPD, instrux_PUNPCKHBW, instrux_PUNPCKHDQ, instrux_PUNPCKHQDQ, instrux_PUNPCKHWD, instrux_PUNPCKLBW, instrux_PUNPCKLDQ, instrux_PUNPCKLQDQ, instrux_PUNPCKLWD, instrux_PUSH, instrux_PUSHA, instrux_PUSHAD, instrux_PUSHAW, instrux_PUSHF, instrux_PUSHFD, instrux_PUSHFW, instrux_PXOR, instrux_RCL, instrux_RCPPS, instrux_RCPSS, instrux_RCR, instrux_RDMSR, instrux_RDPMC, instrux_RDSHR, instrux_RDTSC, instrux_RESB, instrux_RESD, instrux_RESQ, instrux_REST, instrux_RESW, instrux_RET, instrux_RETF, instrux_RETN, instrux_ROL, instrux_ROR, instrux_RSDC, instrux_RSLDT, instrux_RSM, instrux_RSQRTPS, instrux_RSQRTSS, instrux_RSTS, instrux_SAHF, instrux_SAL, instrux_SALC, instrux_SAR, instrux_SBB, instrux_SCASB, instrux_SCASD, instrux_SCASW, instrux_SFENCE, instrux_SGDT, instrux_SHL, instrux_SHLD, instrux_SHR, instrux_SHRD, instrux_SHUFPD, instrux_SHUFPS, instrux_SIDT, instrux_SLDT, instrux_SMI, instrux_SMINT, instrux_SMINTOLD, instrux_SMSW, instrux_SQRTPD, instrux_SQRTPS, instrux_SQRTSD, instrux_SQRTSS, instrux_STC, instrux_STD, instrux_STI, instrux_STMXCSR, instrux_STOSB, instrux_STOSD, instrux_STOSW, instrux_STR, instrux_SUB, instrux_SUBPD, instrux_SUBPS, instrux_SUBSD, instrux_SUBSS, instrux_SVDC, instrux_SVLDT, instrux_SVTS, instrux_SYSCALL, instrux_SYSENTER, instrux_SYSEXIT, instrux_SYSRET, instrux_TEST, instrux_UCOMISD, instrux_UCOMISS, instrux_UD0, instrux_UD1, instrux_UD2, instrux_UMOV, instrux_UNPCKHPD, instrux_UNPCKHPS, instrux_UNPCKLPD, instrux_UNPCKLPS, instrux_VERR, instrux_VERW, instrux_WAIT, instrux_WBINVD, instrux_WRMSR, instrux_WRSHR, instrux_XADD, instrux_XBTS, instrux_XCHG, instrux_XLAT, instrux_XLATB, instrux_XOR, instrux_XORPD, instrux_XORPS, instrux_XSTORE, instrux_CMOVcc, instrux_Jcc, instrux_SETcc, }; simh-3.8.1/AltairZ80/s100_disk2.c0000644000175000017500000006531511051674724014323 0ustar vlmvlm/************************************************************************* * * * $Id: s100_disk2.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro DISK2 Hard Disk Controller module for SIMH. * * This module must be used in conjunction with the CompuPro Selector * * Channel Module for proper operation. * * * * Environment: * * User mode only * * * *************************************************************************/ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define IRQ_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define DISK2_MAX_DRIVES 4 typedef union { uint8 raw[2051]; struct { uint8 header[3]; uint8 data[2048]; } u; } SECTOR_FORMAT; static SECTOR_FORMAT sdata; typedef struct { UNIT *uptr; DISK_INFO *imd; uint16 ntracks; /* number of tracks */ uint8 nheads; /* number of heads */ uint8 nsectors; /* number of sectors/track */ uint32 sectsize; /* sector size, not including pre/postamble */ uint16 track; /* Current Track */ uint8 ready; /* Is drive ready? */ } DISK2_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 sel_drive; /* Currently selected drive */ uint8 head_sel; /* Head select (signals to drive itself) */ uint8 head; /* Head set by write to the HEAD register */ uint8 cyl; /* Cyl that the current operation is targetting */ uint8 sector; /* Sector the current READ/WRITE operation is targetting */ uint8 hdr_sector; /* Current sector for WRITE_HEADER */ uint8 ctl_attn; uint8 ctl_run; uint8 ctl_op; uint8 ctl_fault_clr; uint8 ctl_us; uint8 timeout; uint8 crc_error; uint8 overrun; uint8 seek_complete; uint8 write_fault; DISK2_DRIVE_INFO drive[DISK2_MAX_DRIVES]; } DISK2_INFO; static DISK2_INFO disk2_info_data = { { 0x0, 0, 0xC8, 2 } }; static DISK2_INFO *disk2_info = &disk2_info_data; /* Default geometry for a 20MB hard disk. */ #define C20MB_NTRACKS 243 #define C20MB_NHEADS 8 #define C20MB_NSECTORS 11 #define C20MB_SECTSIZE 1024 static int32 ntracks = C20MB_NTRACKS; static int32 nheads = C20MB_NHEADS; static int32 nsectors = C20MB_NSECTORS; static int32 sectsize = C20MB_SECTSIZE; extern uint32 PCX; extern REG *sim_PC; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern int32 selchan_dma(uint8 *buf, uint32 len); extern int32 find_unit_index(UNIT *uptr); extern void raise_ss1_interrupt(uint8 intnum); #define UNIT_V_DISK2_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_DISK2_WLK (1 << UNIT_V_DISK2_WLK) #define UNIT_V_DISK2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_DISK2_VERBOSE (1 << UNIT_V_DISK2_VERBOSE) #define DISK2_CAPACITY (C20MB_NTRACKS*C20MB_NHEADS*C20MB_NSECTORS*C20MB_SECTSIZE) /* Default Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ static t_stat disk2_reset(DEVICE *disk2_dev); static t_stat disk2_attach(UNIT *uptr, char *cptr); static t_stat disk2_detach(UNIT *uptr); static void raise_disk2_interrupt(void); static int32 disk2dev(const int32 port, const int32 io, const int32 data); static uint8 DISK2_Read(const uint32 Addr); static uint8 DISK2_Write(const uint32 Addr, uint8 cData); static UNIT disk2_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) } }; static REG disk2_reg[] = { { DRDATA (NTRACKS, ntracks, 10), }, { DRDATA (NHEADS, nheads, 8), }, { DRDATA (NSECTORS, nsectors, 8), }, { DRDATA (SECTSIZE, sectsize, 11), }, { HRDATA (SEL_DRIVE, disk2_info_data.sel_drive, 3), }, { HRDATA (CYL, disk2_info_data.cyl, 8), }, { HRDATA (HEAD, disk2_info_data.head, 8), }, { HRDATA (SECTOR, disk2_info_data.sector, 8), }, { NULL } }; static MTAB disk2_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_DISK2_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_DISK2_WLK, UNIT_DISK2_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_DISK2_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_DISK2_VERBOSE, UNIT_DISK2_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(disk2_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB disk2_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "IRQ", IRQ_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE disk2_dev = { "DISK2", disk2_unit, disk2_reg, disk2_mod, DISK2_MAX_DRIVES, 10, 31, 1, DISK2_MAX_DRIVES, DISK2_MAX_DRIVES, NULL, NULL, &disk2_reset, NULL, &disk2_attach, &disk2_detach, &disk2_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, disk2_dt, NULL, "Compupro Hard Disk Controller DISK2" }; /* Reset routine */ static t_stat disk2_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, TRUE); } else { /* Connect DISK2 at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } /* Attach routine */ static t_stat disk2_attach(UNIT *uptr, char *cptr) { t_stat r = SCPE_OK; DISK2_DRIVE_INFO *pDrive; char header[4]; int i = 0; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } pDrive = &disk2_info->drive[i]; pDrive->ready = 1; disk2_info->write_fault = 1; pDrive->track = 5; pDrive->ntracks = ntracks; pDrive->nheads = nheads; pDrive->nsectors = nsectors; pDrive->sectsize = sectsize; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ if(sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize); } pDrive->uptr = uptr; /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "IMD")) { uptr->u3 = IMAGE_TYPE_IMD; } else if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; disk2_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_DISK2_VERBOSE) printf("DISK2%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if(uptr->capac < 318000) { printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); disk2_detach(uptr); return SCPE_OPENERR; } if (uptr->flags & UNIT_DISK2_VERBOSE) printf("--------------------------------------------------------\n"); disk2_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_DISK2_VERBOSE)); if (uptr->flags & UNIT_DISK2_VERBOSE) printf("\n"); } else { disk2_info->drive[i].imd = NULL; } return SCPE_OK; } /* Detach routine */ t_stat disk2_detach(UNIT *uptr) { t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } if (uptr->flags & UNIT_DISK2_VERBOSE) printf("Detach DISK2%d\n", i); r = detach_unit(uptr); /* detach unit */ if ( r != SCPE_OK) return r; return SCPE_OK; } static int32 disk2dev(const int32 port, const int32 io, const int32 data) { /* TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); */ if(io) { DISK2_Write(port, data); return 0; } else { return(DISK2_Read(port)); } } #define DISK2_CSR 0 /* R=DISK2 Status / W=DISK2 Control Register */ #define DISK2_DATA 1 /* R=Step Pulse / W=Write Data Register */ static uint8 DISK2_Read(const uint32 Addr) { uint8 cData; DISK2_DRIVE_INFO *pDrive; pDrive = &disk2_info->drive[disk2_info->sel_drive]; cData = 0x00; switch(Addr & 0x1) { case DISK2_CSR: cData = (disk2_info->ctl_attn) << 7; cData |= (disk2_info->timeout) << 6; cData |= (disk2_info->crc_error) << 5; cData |= (disk2_info->overrun) << 4; cData |= (pDrive->ready == 0) ? 0x08 : 0x00; cData |= (disk2_info->seek_complete == 0) ? 0x04 : 0x00; cData |= (disk2_info->write_fault) << 1; cData |= ((pDrive->track != 0) || (disk2_info->seek_complete == 0)) ? 0x01 : 0x00; TRACE_PRINT(STATUS_MSG, ("DISK2: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)); disk2_info->seek_complete = 1; break; case DISK2_DATA: if(disk2_info->ctl_op & 0x04) { if(pDrive->track < pDrive->ntracks) { pDrive->track ++; } } else { if(pDrive->track > 0) { pDrive->track --; } } TRACE_PRINT(SEEK_MSG, ("DISK2: " ADDRESS_FORMAT " Step %s, Track=%d" NLP, PCX, disk2_info->ctl_op & 0x04 ? "IN" : "OUT", pDrive->track)); disk2_info->seek_complete = 0; cData = 0xFF; /* Return High-Z data */ break; } return (cData); } #define DISK2_OP_DRIVE 0x00 #define DISK2_OP_CYL 0x01 #define DISK2_OP_HEAD 0x02 #define DISK2_OP_SECTOR 0x03 #define DISK2_CMD_NULL 0x00 #define DISK2_CMD_READ_DATA 0x01 #define DISK2_CMD_WRITE_DATA 0x02 #define DISK2_CMD_WRITE_HEADER 0x03 #define DISK2_CMD_READ_HEADER 0x04 static uint8 DISK2_Write(const uint32 Addr, uint8 cData) { uint32 track_offset; uint8 result = 0; uint8 i; long file_offset; DISK2_DRIVE_INFO *pDrive; pDrive = &disk2_info->drive[disk2_info->sel_drive]; switch(Addr & 0x1) { case DISK2_CSR: /* Write CTL register */ disk2_info->ctl_attn = (cData & 0x80) >> 7; disk2_info->ctl_run = (cData & 0x40) >> 6; disk2_info->ctl_op = (cData & 0x38) >> 3; disk2_info->ctl_fault_clr = (cData & 0x04) >> 2; if(disk2_info->ctl_fault_clr == 1) { disk2_info->timeout = 0; } disk2_info->ctl_us = (cData & 0x03); TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " ATTN*=%d, RUN=%d, OP=%d, FAULT_CLR=%d, US=%d" NLP, PCX, disk2_info->ctl_attn, disk2_info->ctl_run, disk2_info->ctl_op, disk2_info->ctl_fault_clr, disk2_info->ctl_us)); /* FIXME: seek_complete = 1 is needed by CP/M, but why? Also, maybe related, * there appears to be a bug in the seeking logic. For some reason, the * pDrive->track does not equal the disk2_info->cyl, when doing READ_DATA and * WRITE_DATA commands. For this reason, disk2_info->cyl is used instead of * pDrive->track for these commands. For READ_HEADER and WRITE_HEADER, * pDrive->track is used, because the DISK2 format program (DISK2.COM) does not * issue DISK2_OP_CYL. The root cause of this anomaly needs to be determined, * because it is surely a bug in the logic somewhere. */ /* pDrive->track may be different from disk2_info->cyl when a program such as DISK2.COM moves the position of the track without informing the CP/M BIOS which stores the current track for each drive. This appears to be an application program bug. */ disk2_info->seek_complete = 1; if(disk2_info->ctl_run == 1) { disk2_info->timeout = 0; track_offset = disk2_info->cyl * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); switch(disk2_info->ctl_op) { case DISK2_CMD_NULL: TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " NULL Command" NLP, PCX)); break; case DISK2_CMD_READ_DATA: TRACE_PRINT(RD_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: (C:%d/H:%d/S:%d)" NLP, PCX, disk2_info->cyl, disk2_info->head, disk2_info->sector)); if(disk2_info->head_sel != disk2_info->head) { printf("DISK2: " ADDRESS_FORMAT " READ_DATA: head_sel != head" NLP, PCX); } /* See FIXME above... that might be why this does not work properly... */ if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */ TRACE_PRINT(ERROR_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: cyl=%d, track=%d" NLP, PCX, disk2_info->cyl, pDrive->track)); pDrive->track = disk2_info->cyl; /* update track */ } sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); for(i=0;insectors;i++) { /* Read sector */ fread(sdata.raw, (pDrive->sectsize + 3), 1, (pDrive->uptr)->fileref); if(sdata.u.header[2] == disk2_info->sector) { if(sdata.u.header[0] != disk2_info->cyl) { /*pDrive->track) { */ printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: track" NLP, PCX); disk2_info->timeout = 1; } if(sdata.u.header[1] != disk2_info->head) { printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: head" NLP, PCX); disk2_info->timeout = 1; } selchan_dma(sdata.u.data, pDrive->sectsize); break; } if(i == pDrive->nsectors) { printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX); disk2_info->timeout = 1; } } break; case DISK2_CMD_WRITE_DATA: TRACE_PRINT(WR_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA: (C:%d/H:%d/S:%d)" NLP, PCX, disk2_info->cyl, disk2_info->head, disk2_info->sector)); if(disk2_info->head_sel != disk2_info->head) { printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA: head_sel != head" NLP, PCX); } if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */ TRACE_PRINT(ERROR_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA = 0x%02x, cyl=%d, track=%d" NLP, PCX, cData, disk2_info->cyl, pDrive->track)); pDrive->track = disk2_info->cyl; /* update track */ } sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); for(i=0;insectors;i++) { /* Read sector */ file_offset = ftell((pDrive->uptr)->fileref); fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref); if(sdata.u.header[2] == disk2_info->sector) { if(sdata.u.header[0] != disk2_info->cyl) { printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: track" NLP, PCX); disk2_info->timeout = 1; } if(sdata.u.header[1] != disk2_info->head) { printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: head" NLP, PCX); disk2_info->timeout = 1; } selchan_dma(sdata.u.data, pDrive->sectsize); sim_fseek((pDrive->uptr)->fileref, file_offset+3, SEEK_SET); fwrite(sdata.u.data, (pDrive->sectsize), 1, (pDrive->uptr)->fileref); break; } fread(sdata.raw, pDrive->sectsize, 1, (pDrive->uptr)->fileref); if(i == pDrive->nsectors) { printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX); disk2_info->timeout = 1; } } break; case DISK2_CMD_WRITE_HEADER: track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_HEADER Command: track=%d (%d), Head=%d, Sector=%d" NLP, PCX, pDrive->track, disk2_info->cyl, disk2_info->head_sel, disk2_info->hdr_sector)); i = disk2_info->hdr_sector; selchan_dma(sdata.raw, 3); sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * (pDrive->sectsize + 3) * pDrive->nsectors) + (i * (pDrive->sectsize + 3)), SEEK_SET); fwrite(sdata.raw, 3, 1, (pDrive->uptr)->fileref); disk2_info->hdr_sector++; if(disk2_info->hdr_sector >= pDrive->nsectors) { disk2_info->hdr_sector = 0; disk2_info->timeout = 1; } break; case DISK2_CMD_READ_HEADER: track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " READ_HEADER Command" NLP, PCX)); sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref); selchan_dma(sdata.raw, 3); break; default: printf("DISK2: " ADDRESS_FORMAT " Unknown CMD=%d" NLP, PCX, disk2_info->ctl_op); break; } raise_disk2_interrupt(); disk2_info->ctl_attn = 0; } break; case DISK2_DATA: switch(disk2_info->ctl_op) { case DISK2_OP_DRIVE: switch(cData >> 4) { case 0x01: disk2_info->sel_drive = 0; break; case 0x02: disk2_info->sel_drive = 1; break; case 0x04: disk2_info->sel_drive = 2; break; case 0x08: disk2_info->sel_drive = 3; break; default: printf("DISK2: " ADDRESS_FORMAT " Error, invalid drive select=0x%x" NLP, PCX, cData >> 4); break; } disk2_info->head_sel = cData & 0x0F; TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [DRIVE]=%d, Head=%d" NLP, PCX, disk2_info->sel_drive, disk2_info->head)); break; case DISK2_OP_CYL: disk2_info->cyl = cData; TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [CYL] = %02x" NLP, PCX, cData)); break; case DISK2_OP_HEAD: disk2_info->head = cData; TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [HEAD] = %02x" NLP, PCX, cData)); break; case DISK2_OP_SECTOR: disk2_info->sector = cData; TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register [SECTOR] = %02x" NLP, PCX, cData)); break; default: TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register unknown op [%d] = %02x" NLP, PCX, disk2_info->ctl_op, cData)); break; } } return (result); } #define SS1_VI1_INT 1 /* DISK2/DISK3 interrupts tied to VI1 */ static void raise_disk2_interrupt(void) { TRACE_PRINT(IRQ_MSG, ("DISK2: " ADDRESS_FORMAT " Interrupt" NLP, PCX)); raise_ss1_interrupt(SS1_VI1_INT); } simh-3.8.1/AltairZ80/insnsd.c0000644000175000017500000055530711043102676014040 0ustar vlmvlm/* This file auto-generated from insns.dat by insns.pl - don't edit it */ #include "nasm.h" #include "insns.h" static struct itemplate instrux[] = { /* 0*/ {I_AAA, 0, {0,0,0}, "\1\x37", IF_8086}, /* 1*/ {I_AAD, 0, {0,0,0}, "\2\xD5\x0A", IF_8086}, /* 2*/ {I_AAD, 1, {IMMEDIATE,0,0}, "\1\xD5\24", IF_8086|IF_SB}, /* 3*/ {I_AAM, 0, {0,0,0}, "\2\xD4\x0A", IF_8086}, /* 4*/ {I_AAM, 1, {IMMEDIATE,0,0}, "\1\xD4\24", IF_8086|IF_SB}, /* 5*/ {I_AAS, 0, {0,0,0}, "\1\x3F", IF_8086}, /* 6*/ {I_ADC, 2, {MEMORY,REG8,0}, "\300\1\x10\101", IF_8086|IF_SM}, /* 7*/ {I_ADC, 2, {REG8,REG8,0}, "\1\x10\101", IF_8086}, /* 8*/ {I_ADC, 2, {MEMORY,REG16,0}, "\320\300\1\x11\101", IF_8086|IF_SM}, /* 9*/ {I_ADC, 2, {REG16,REG16,0}, "\320\1\x11\101", IF_8086}, /* 10*/ {I_ADC, 2, {MEMORY,REG32,0}, "\321\300\1\x11\101", IF_386|IF_SM}, /* 11*/ {I_ADC, 2, {REG32,REG32,0}, "\321\1\x11\101", IF_386}, /* 12*/ {I_ADC, 2, {REG8,MEMORY,0}, "\301\1\x12\110", IF_8086|IF_SM}, /* 13*/ {I_ADC, 2, {REG8,REG8,0}, "\1\x12\110", IF_8086}, /* 14*/ {I_ADC, 2, {REG16,MEMORY,0}, "\320\301\1\x13\110", IF_8086|IF_SM}, /* 15*/ {I_ADC, 2, {REG16,REG16,0}, "\320\1\x13\110", IF_8086}, /* 16*/ {I_ADC, 2, {REG32,MEMORY,0}, "\321\301\1\x13\110", IF_386|IF_SM}, /* 17*/ {I_ADC, 2, {REG32,REG32,0}, "\321\1\x13\110", IF_386}, /* 18*/ {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\202\15", IF_8086}, /* 19*/ {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\202\15", IF_386}, /* 20*/ {I_ADC, 2, {REG_AL,IMMEDIATE,0}, "\1\x14\21", IF_8086|IF_SM}, /* 21*/ {I_ADC, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x15\31", IF_8086|IF_SM}, /* 22*/ {I_ADC, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x15\41", IF_386|IF_SM}, /* 23*/ {I_ADC, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, /* 24*/ {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, /* 25*/ {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, /* 26*/ {I_ADC, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, /* 27*/ {I_ADC, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, /* 28*/ {I_ADC, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, /* 29*/ {I_ADD, 2, {MEMORY,REG8,0}, "\300\17\101", IF_8086|IF_SM}, /* 30*/ {I_ADD, 2, {REG8,REG8,0}, "\17\101", IF_8086}, /* 31*/ {I_ADD, 2, {MEMORY,REG16,0}, "\320\300\1\x01\101", IF_8086|IF_SM}, /* 32*/ {I_ADD, 2, {REG16,REG16,0}, "\320\1\x01\101", IF_8086}, /* 33*/ {I_ADD, 2, {MEMORY,REG32,0}, "\321\300\1\x01\101", IF_386|IF_SM}, /* 34*/ {I_ADD, 2, {REG32,REG32,0}, "\321\1\x01\101", IF_386}, /* 35*/ {I_ADD, 2, {REG8,MEMORY,0}, "\301\1\x02\110", IF_8086|IF_SM}, /* 36*/ {I_ADD, 2, {REG8,REG8,0}, "\1\x02\110", IF_8086}, /* 37*/ {I_ADD, 2, {REG16,MEMORY,0}, "\320\301\1\x03\110", IF_8086|IF_SM}, /* 38*/ {I_ADD, 2, {REG16,REG16,0}, "\320\1\x03\110", IF_8086}, /* 39*/ {I_ADD, 2, {REG32,MEMORY,0}, "\321\301\1\x03\110", IF_386|IF_SM}, /* 40*/ {I_ADD, 2, {REG32,REG32,0}, "\321\1\x03\110", IF_386}, /* 41*/ {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\200\15", IF_8086}, /* 42*/ {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\200\15", IF_386}, /* 43*/ {I_ADD, 2, {REG_AL,IMMEDIATE,0}, "\1\x04\21", IF_8086|IF_SM}, /* 44*/ {I_ADD, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x05\31", IF_8086|IF_SM}, /* 45*/ {I_ADD, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x05\41", IF_386|IF_SM}, /* 46*/ {I_ADD, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, /* 47*/ {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, /* 48*/ {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, /* 49*/ {I_ADD, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, /* 50*/ {I_ADD, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, /* 51*/ {I_ADD, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, /* 52*/ {I_AND, 2, {MEMORY,REG8,0}, "\300\1\x20\101", IF_8086|IF_SM}, /* 53*/ {I_AND, 2, {REG8,REG8,0}, "\1\x20\101", IF_8086}, /* 54*/ {I_AND, 2, {MEMORY,REG16,0}, "\320\300\1\x21\101", IF_8086|IF_SM}, /* 55*/ {I_AND, 2, {REG16,REG16,0}, "\320\1\x21\101", IF_8086}, /* 56*/ {I_AND, 2, {MEMORY,REG32,0}, "\321\300\1\x21\101", IF_386|IF_SM}, /* 57*/ {I_AND, 2, {REG32,REG32,0}, "\321\1\x21\101", IF_386}, /* 58*/ {I_AND, 2, {REG8,MEMORY,0}, "\301\1\x22\110", IF_8086|IF_SM}, /* 59*/ {I_AND, 2, {REG8,REG8,0}, "\1\x22\110", IF_8086}, /* 60*/ {I_AND, 2, {REG16,MEMORY,0}, "\320\301\1\x23\110", IF_8086|IF_SM}, /* 61*/ {I_AND, 2, {REG16,REG16,0}, "\320\1\x23\110", IF_8086}, /* 62*/ {I_AND, 2, {REG32,MEMORY,0}, "\321\301\1\x23\110", IF_386|IF_SM}, /* 63*/ {I_AND, 2, {REG32,REG32,0}, "\321\1\x23\110", IF_386}, /* 64*/ {I_AND, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\204\15", IF_8086}, /* 65*/ {I_AND, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\204\15", IF_386}, /* 66*/ {I_AND, 2, {REG_AL,IMMEDIATE,0}, "\1\x24\21", IF_8086|IF_SM}, /* 67*/ {I_AND, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x25\31", IF_8086|IF_SM}, /* 68*/ {I_AND, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x25\41", IF_386|IF_SM}, /* 69*/ {I_AND, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, /* 70*/ {I_AND, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, /* 71*/ {I_AND, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, /* 72*/ {I_AND, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, /* 73*/ {I_AND, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, /* 74*/ {I_AND, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, /* 75*/ {I_ARPL, 2, {MEMORY,REG16,0}, "\300\1\x63\101", IF_286|IF_PROT|IF_SM}, /* 76*/ {I_ARPL, 2, {REG16,REG16,0}, "\1\x63\101", IF_286|IF_PROT}, /* 77*/ {I_BOUND, 2, {REG16,MEMORY,0}, "\320\301\1\x62\110", IF_186}, /* 78*/ {I_BOUND, 2, {REG32,MEMORY,0}, "\321\301\1\x62\110", IF_386}, /* 79*/ {I_BSF, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBC\110", IF_386|IF_SM}, /* 80*/ {I_BSF, 2, {REG16,REG16,0}, "\320\2\x0F\xBC\110", IF_386}, /* 81*/ {I_BSF, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBC\110", IF_386|IF_SM}, /* 82*/ {I_BSF, 2, {REG32,REG32,0}, "\321\2\x0F\xBC\110", IF_386}, /* 83*/ {I_BSR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBD\110", IF_386|IF_SM}, /* 84*/ {I_BSR, 2, {REG16,REG16,0}, "\320\2\x0F\xBD\110", IF_386}, /* 85*/ {I_BSR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBD\110", IF_386|IF_SM}, /* 86*/ {I_BSR, 2, {REG32,REG32,0}, "\321\2\x0F\xBD\110", IF_386}, /* 87*/ {I_BSWAP, 1, {REG32,0,0}, "\321\1\x0F\10\xC8", IF_486}, /* 88*/ {I_BT, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA3\101", IF_386|IF_SM}, /* 89*/ {I_BT, 2, {REG16,REG16,0}, "\320\2\x0F\xA3\101", IF_386}, /* 90*/ {I_BT, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA3\101", IF_386|IF_SM}, /* 91*/ {I_BT, 2, {REG32,REG32,0}, "\321\2\x0F\xA3\101", IF_386}, /* 92*/ {I_BT, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\204\25", IF_386|IF_SB}, /* 93*/ {I_BT, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\204\25", IF_386|IF_SB}, /* 94*/ {I_BTC, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xBB\101", IF_386|IF_SM}, /* 95*/ {I_BTC, 2, {REG16,REG16,0}, "\320\2\x0F\xBB\101", IF_386}, /* 96*/ {I_BTC, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xBB\101", IF_386|IF_SM}, /* 97*/ {I_BTC, 2, {REG32,REG32,0}, "\321\2\x0F\xBB\101", IF_386}, /* 98*/ {I_BTC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\207\25", IF_386|IF_SB}, /* 99*/ {I_BTC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\207\25", IF_386|IF_SB}, /* 100*/ {I_BTR, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB3\101", IF_386|IF_SM}, /* 101*/ {I_BTR, 2, {REG16,REG16,0}, "\320\2\x0F\xB3\101", IF_386}, /* 102*/ {I_BTR, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB3\101", IF_386|IF_SM}, /* 103*/ {I_BTR, 2, {REG32,REG32,0}, "\321\2\x0F\xB3\101", IF_386}, /* 104*/ {I_BTR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\206\25", IF_386|IF_SB}, /* 105*/ {I_BTR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\206\25", IF_386|IF_SB}, /* 106*/ {I_BTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xAB\101", IF_386|IF_SM}, /* 107*/ {I_BTS, 2, {REG16,REG16,0}, "\320\2\x0F\xAB\101", IF_386}, /* 108*/ {I_BTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xAB\101", IF_386|IF_SM}, /* 109*/ {I_BTS, 2, {REG32,REG32,0}, "\321\2\x0F\xAB\101", IF_386}, /* 110*/ {I_BTS, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\205\25", IF_386|IF_SB}, /* 111*/ {I_BTS, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\205\25", IF_386|IF_SB}, /* 112*/ {I_CALL, 1, {IMMEDIATE,0,0}, "\322\1\xE8\64", IF_8086}, /* 113*/ {I_CALL, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE8\64", IF_8086}, /* 114*/ {I_CALL, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE8\64", IF_8086}, /* 115*/ {I_CALL, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE8\64", IF_8086}, /* 116*/ {I_CALL, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE8\64", IF_386}, /* 117*/ {I_CALL, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE8\64", IF_386}, /* 118*/ {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\x9A\35\30", IF_8086}, /* 119*/ {I_CALL, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\x9A\31\30", IF_8086}, /* 120*/ {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\x9A\31\30", IF_8086}, /* 121*/ {I_CALL, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\x9A\41\30", IF_386}, /* 122*/ {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\x9A\41\30", IF_386}, /* 123*/ {I_CALL, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\203", IF_8086}, /* 124*/ {I_CALL, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\203", IF_8086}, /* 125*/ {I_CALL, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\203", IF_386}, /* 126*/ {I_CALL, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\202", IF_8086}, /* 127*/ {I_CALL, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\202", IF_8086}, /* 128*/ {I_CALL, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\202", IF_386}, /* 129*/ {I_CALL, 1, {REG16,0,0}, "\320\300\1\xFF\202", IF_8086}, /* 130*/ {I_CALL, 1, {REG32,0,0}, "\321\300\1\xFF\202", IF_386}, /* 131*/ {I_CALL, 1, {MEMORY,0,0}, "\322\300\1\xFF\202", IF_8086}, /* 132*/ {I_CALL, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\202", IF_8086}, /* 133*/ {I_CALL, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\202", IF_386}, /* 134*/ {I_CBW, 0, {0,0,0}, "\320\1\x98", IF_8086}, /* 135*/ {I_CDQ, 0, {0,0,0}, "\321\1\x99", IF_386}, /* 136*/ {I_CLC, 0, {0,0,0}, "\1\xF8", IF_8086}, /* 137*/ {I_CLD, 0, {0,0,0}, "\1\xFC", IF_8086}, /* 138*/ {I_CLI, 0, {0,0,0}, "\1\xFA", IF_8086}, /* 139*/ {I_CLTS, 0, {0,0,0}, "\2\x0F\x06", IF_286|IF_PRIV}, /* 140*/ {I_CMC, 0, {0,0,0}, "\1\xF5", IF_8086}, /* 141*/ {I_CMP, 2, {MEMORY,REG8,0}, "\300\1\x38\101", IF_8086|IF_SM}, /* 142*/ {I_CMP, 2, {REG8,REG8,0}, "\1\x38\101", IF_8086}, /* 143*/ {I_CMP, 2, {MEMORY,REG16,0}, "\320\300\1\x39\101", IF_8086|IF_SM}, /* 144*/ {I_CMP, 2, {REG16,REG16,0}, "\320\1\x39\101", IF_8086}, /* 145*/ {I_CMP, 2, {MEMORY,REG32,0}, "\321\300\1\x39\101", IF_386|IF_SM}, /* 146*/ {I_CMP, 2, {REG32,REG32,0}, "\321\1\x39\101", IF_386}, /* 147*/ {I_CMP, 2, {REG8,MEMORY,0}, "\301\1\x3A\110", IF_8086|IF_SM}, /* 148*/ {I_CMP, 2, {REG8,REG8,0}, "\1\x3A\110", IF_8086}, /* 149*/ {I_CMP, 2, {REG16,MEMORY,0}, "\320\301\1\x3B\110", IF_8086|IF_SM}, /* 150*/ {I_CMP, 2, {REG16,REG16,0}, "\320\1\x3B\110", IF_8086}, /* 151*/ {I_CMP, 2, {REG32,MEMORY,0}, "\321\301\1\x3B\110", IF_386|IF_SM}, /* 152*/ {I_CMP, 2, {REG32,REG32,0}, "\321\1\x3B\110", IF_386}, /* 153*/ {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\207\15", IF_8086}, /* 154*/ {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\207\15", IF_386}, /* 155*/ {I_CMP, 2, {REG_AL,IMMEDIATE,0}, "\1\x3C\21", IF_8086|IF_SM}, /* 156*/ {I_CMP, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x3D\31", IF_8086|IF_SM}, /* 157*/ {I_CMP, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x3D\41", IF_386|IF_SM}, /* 158*/ {I_CMP, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, /* 159*/ {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, /* 160*/ {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, /* 161*/ {I_CMP, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, /* 162*/ {I_CMP, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, /* 163*/ {I_CMP, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, /* 164*/ {I_CMPSB, 0, {0,0,0}, "\332\1\xA6", IF_8086}, /* 165*/ {I_CMPSD, 0, {0,0,0}, "\332\321\1\xA7", IF_386}, /* 166*/ {I_CMPSW, 0, {0,0,0}, "\332\320\1\xA7", IF_8086}, /* 167*/ {I_CMPXCHG, 2, {MEMORY,REG8,0}, "\300\2\x0F\xB0\101", IF_PENT|IF_SM}, /* 168*/ {I_CMPXCHG, 2, {REG8,REG8,0}, "\2\x0F\xB0\101", IF_PENT}, /* 169*/ {I_CMPXCHG, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB1\101", IF_PENT|IF_SM}, /* 170*/ {I_CMPXCHG, 2, {REG16,REG16,0}, "\320\2\x0F\xB1\101", IF_PENT}, /* 171*/ {I_CMPXCHG, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB1\101", IF_PENT|IF_SM}, /* 172*/ {I_CMPXCHG, 2, {REG32,REG32,0}, "\321\2\x0F\xB1\101", IF_PENT}, /* 173*/ {I_CMPXCHG486, 2, {MEMORY,REG8,0}, "\300\2\x0F\xA6\101", IF_486|IF_SM|IF_UNDOC}, /* 174*/ {I_CMPXCHG486, 2, {REG8,REG8,0}, "\2\x0F\xA6\101", IF_486|IF_UNDOC}, /* 175*/ {I_CMPXCHG486, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, /* 176*/ {I_CMPXCHG486, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_486|IF_UNDOC}, /* 177*/ {I_CMPXCHG486, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, /* 178*/ {I_CMPXCHG486, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_486|IF_UNDOC}, /* 179*/ {I_CMPXCHG8B, 1, {MEMORY,0,0}, "\300\2\x0F\xC7\201", IF_PENT}, /* 180*/ {I_CPUID, 0, {0,0,0}, "\2\x0F\xA2", IF_PENT}, /* 181*/ {I_CWD, 0, {0,0,0}, "\320\1\x99", IF_8086}, /* 182*/ {I_CWDE, 0, {0,0,0}, "\321\1\x98", IF_386}, /* 183*/ {I_DAA, 0, {0,0,0}, "\1\x27", IF_8086}, /* 184*/ {I_DAS, 0, {0,0,0}, "\1\x2F", IF_8086}, /* 185*/ {I_DEC, 1, {REG16,0,0}, "\320\10\x48", IF_8086}, /* 186*/ {I_DEC, 1, {REG32,0,0}, "\321\10\x48", IF_386}, /* 187*/ {I_DEC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\201", IF_8086}, /* 188*/ {I_DEC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\201", IF_8086}, /* 189*/ {I_DEC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\201", IF_386}, /* 190*/ {I_DIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\206", IF_8086}, /* 191*/ {I_DIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\206", IF_8086}, /* 192*/ {I_DIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\206", IF_386}, /* 193*/ {I_EMMS, 0, {0,0,0}, "\2\x0F\x77", IF_PENT|IF_MMX}, /* 194*/ {I_ENTER, 2, {IMMEDIATE,IMMEDIATE,0}, "\1\xC8\30\25", IF_186}, /* 195*/ {I_EQU, 1, {IMMEDIATE,0,0}, "\0", IF_8086}, /* 196*/ {I_EQU, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\0", IF_8086}, /* 197*/ {I_F2XM1, 0, {0,0,0}, "\2\xD9\xF0", IF_8086|IF_FPU}, /* 198*/ {I_FABS, 0, {0,0,0}, "\2\xD9\xE1", IF_8086|IF_FPU}, /* 199*/ {I_FADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\200", IF_8086|IF_FPU}, /* 200*/ {I_FADD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\200", IF_8086|IF_FPU}, /* 201*/ {I_FADD, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, /* 202*/ {I_FADD, 1, {FPUREG,0,0}, "\1\xD8\10\xC0", IF_8086|IF_FPU}, /* 203*/ {I_FADD, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, /* 204*/ {I_FADD, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC0", IF_8086|IF_FPU}, /* 205*/ {I_FADDP, 1, {FPUREG,0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, /* 206*/ {I_FADDP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, /* 207*/ {I_FBLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, /* 208*/ {I_FBLD, 1, {MEMORY,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, /* 209*/ {I_FBSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, /* 210*/ {I_FBSTP, 1, {MEMORY,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, /* 211*/ {I_FCHS, 0, {0,0,0}, "\2\xD9\xE0", IF_8086|IF_FPU}, /* 212*/ {I_FCLEX, 0, {0,0,0}, "\3\x9B\xDB\xE2", IF_8086|IF_FPU}, /* 213*/ {I_FCMOVB, 1, {FPUREG,0,0}, "\1\xDA\10\xC0", IF_P6|IF_FPU}, /* 214*/ {I_FCMOVB, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC0", IF_P6|IF_FPU}, /* 215*/ {I_FCMOVBE, 1, {FPUREG,0,0}, "\1\xDA\10\xD0", IF_P6|IF_FPU}, /* 216*/ {I_FCMOVBE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD0", IF_P6|IF_FPU}, /* 217*/ {I_FCMOVE, 1, {FPUREG,0,0}, "\1\xDA\10\xC8", IF_P6|IF_FPU}, /* 218*/ {I_FCMOVE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC8", IF_P6|IF_FPU}, /* 219*/ {I_FCMOVNB, 1, {FPUREG,0,0}, "\1\xDB\10\xC0", IF_P6|IF_FPU}, /* 220*/ {I_FCMOVNB, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC0", IF_P6|IF_FPU}, /* 221*/ {I_FCMOVNBE, 1, {FPUREG,0,0}, "\1\xDB\10\xD0", IF_P6|IF_FPU}, /* 222*/ {I_FCMOVNBE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD0", IF_P6|IF_FPU}, /* 223*/ {I_FCMOVNE, 1, {FPUREG,0,0}, "\1\xDB\10\xC8", IF_P6|IF_FPU}, /* 224*/ {I_FCMOVNE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC8", IF_P6|IF_FPU}, /* 225*/ {I_FCMOVNU, 1, {FPUREG,0,0}, "\1\xDB\10\xD8", IF_P6|IF_FPU}, /* 226*/ {I_FCMOVNU, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD8", IF_P6|IF_FPU}, /* 227*/ {I_FCMOVU, 1, {FPUREG,0,0}, "\1\xDA\10\xD8", IF_P6|IF_FPU}, /* 228*/ {I_FCMOVU, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD8", IF_P6|IF_FPU}, /* 229*/ {I_FCOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\202", IF_8086|IF_FPU}, /* 230*/ {I_FCOM, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\202", IF_8086|IF_FPU}, /* 231*/ {I_FCOM, 1, {FPUREG,0,0}, "\1\xD8\10\xD0", IF_8086|IF_FPU}, /* 232*/ {I_FCOM, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD0", IF_8086|IF_FPU}, /* 233*/ {I_FCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xF0", IF_P6|IF_FPU}, /* 234*/ {I_FCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xF0", IF_P6|IF_FPU}, /* 235*/ {I_FCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xF0", IF_P6|IF_FPU}, /* 236*/ {I_FCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xF0", IF_P6|IF_FPU}, /* 237*/ {I_FCOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\203", IF_8086|IF_FPU}, /* 238*/ {I_FCOMP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\203", IF_8086|IF_FPU}, /* 239*/ {I_FCOMP, 1, {FPUREG,0,0}, "\1\xD8\10\xD8", IF_8086|IF_FPU}, /* 240*/ {I_FCOMP, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD8", IF_8086|IF_FPU}, /* 241*/ {I_FCOMPP, 0, {0,0,0}, "\2\xDE\xD9", IF_8086|IF_FPU}, /* 242*/ {I_FCOS, 0, {0,0,0}, "\2\xD9\xFF", IF_386|IF_FPU}, /* 243*/ {I_FDECSTP, 0, {0,0,0}, "\2\xD9\xF6", IF_8086|IF_FPU}, /* 244*/ {I_FDISI, 0, {0,0,0}, "\3\x9B\xDB\xE1", IF_8086|IF_FPU}, /* 245*/ {I_FDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\206", IF_8086|IF_FPU}, /* 246*/ {I_FDIV, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\206", IF_8086|IF_FPU}, /* 247*/ {I_FDIV, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, /* 248*/ {I_FDIV, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, /* 249*/ {I_FDIV, 1, {FPUREG,0,0}, "\1\xD8\10\xF0", IF_8086|IF_FPU}, /* 250*/ {I_FDIV, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF0", IF_8086|IF_FPU}, /* 251*/ {I_FDIVP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, /* 252*/ {I_FDIVP, 1, {FPUREG,0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, /* 253*/ {I_FDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\207", IF_8086|IF_FPU}, /* 254*/ {I_FDIVR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\207", IF_8086|IF_FPU}, /* 255*/ {I_FDIVR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, /* 256*/ {I_FDIVR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, /* 257*/ {I_FDIVR, 1, {FPUREG,0,0}, "\1\xD8\10\xF8", IF_8086|IF_FPU}, /* 258*/ {I_FDIVR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF8", IF_8086|IF_FPU}, /* 259*/ {I_FDIVRP, 1, {FPUREG,0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, /* 260*/ {I_FDIVRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, /* 261*/ {I_FEMMS, 0, {0,0,0}, "\2\x0F\x0E", IF_PENT|IF_3DNOW}, /* 262*/ {I_FENI, 0, {0,0,0}, "\3\x9B\xDB\xE0", IF_8086|IF_FPU}, /* 263*/ {I_FFREE, 1, {FPUREG,0,0}, "\1\xDD\10\xC0", IF_8086|IF_FPU}, /* 264*/ {I_FFREEP, 1, {FPUREG,0,0}, "\1\xDF\10\xC0", IF_286|IF_FPU|IF_UNDOC}, /* 265*/ {I_FIADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\200", IF_8086|IF_FPU}, /* 266*/ {I_FIADD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\200", IF_8086|IF_FPU}, /* 267*/ {I_FICOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\202", IF_8086|IF_FPU}, /* 268*/ {I_FICOM, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\202", IF_8086|IF_FPU}, /* 269*/ {I_FICOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\203", IF_8086|IF_FPU}, /* 270*/ {I_FICOMP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\203", IF_8086|IF_FPU}, /* 271*/ {I_FIDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\206", IF_8086|IF_FPU}, /* 272*/ {I_FIDIV, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\206", IF_8086|IF_FPU}, /* 273*/ {I_FIDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\207", IF_8086|IF_FPU}, /* 274*/ {I_FIDIVR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\207", IF_8086|IF_FPU}, /* 275*/ {I_FILD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\200", IF_8086|IF_FPU}, /* 276*/ {I_FILD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\200", IF_8086|IF_FPU}, /* 277*/ {I_FILD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\205", IF_8086|IF_FPU}, /* 278*/ {I_FIMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\201", IF_8086|IF_FPU}, /* 279*/ {I_FIMUL, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\201", IF_8086|IF_FPU}, /* 280*/ {I_FINCSTP, 0, {0,0,0}, "\2\xD9\xF7", IF_8086|IF_FPU}, /* 281*/ {I_FINIT, 0, {0,0,0}, "\3\x9B\xDB\xE3", IF_8086|IF_FPU}, /* 282*/ {I_FIST, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\202", IF_8086|IF_FPU}, /* 283*/ {I_FIST, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\202", IF_8086|IF_FPU}, /* 284*/ {I_FISTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\203", IF_8086|IF_FPU}, /* 285*/ {I_FISTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\203", IF_8086|IF_FPU}, /* 286*/ {I_FISTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\207", IF_8086|IF_FPU}, /* 287*/ {I_FISTTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDD\201", IF_PRESCOTT|IF_FPU}, /* 288*/ {I_FISTTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDB\201", IF_PRESCOTT|IF_FPU}, /* 289*/ {I_FISTTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\201", IF_PRESCOTT|IF_FPU}, /* 290*/ {I_FISUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\204", IF_8086|IF_FPU}, /* 291*/ {I_FISUB, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\204", IF_8086|IF_FPU}, /* 292*/ {I_FISUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\205", IF_8086|IF_FPU}, /* 293*/ {I_FISUBR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\205", IF_8086|IF_FPU}, /* 294*/ {I_FLD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\200", IF_8086|IF_FPU}, /* 295*/ {I_FLD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\200", IF_8086|IF_FPU}, /* 296*/ {I_FLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\205", IF_8086|IF_FPU}, /* 297*/ {I_FLD, 1, {FPUREG,0,0}, "\1\xD9\10\xC0", IF_8086|IF_FPU}, /* 298*/ {I_FLD1, 0, {0,0,0}, "\2\xD9\xE8", IF_8086|IF_FPU}, /* 299*/ {I_FLDCW, 1, {MEMORY,0,0}, "\300\1\xD9\205", IF_8086|IF_FPU|IF_SW}, /* 300*/ {I_FLDENV, 1, {MEMORY,0,0}, "\300\1\xD9\204", IF_8086|IF_FPU}, /* 301*/ {I_FLDL2E, 0, {0,0,0}, "\2\xD9\xEA", IF_8086|IF_FPU}, /* 302*/ {I_FLDL2T, 0, {0,0,0}, "\2\xD9\xE9", IF_8086|IF_FPU}, /* 303*/ {I_FLDLG2, 0, {0,0,0}, "\2\xD9\xEC", IF_8086|IF_FPU}, /* 304*/ {I_FLDLN2, 0, {0,0,0}, "\2\xD9\xED", IF_8086|IF_FPU}, /* 305*/ {I_FLDPI, 0, {0,0,0}, "\2\xD9\xEB", IF_8086|IF_FPU}, /* 306*/ {I_FLDZ, 0, {0,0,0}, "\2\xD9\xEE", IF_8086|IF_FPU}, /* 307*/ {I_FMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\201", IF_8086|IF_FPU}, /* 308*/ {I_FMUL, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\201", IF_8086|IF_FPU}, /* 309*/ {I_FMUL, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, /* 310*/ {I_FMUL, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, /* 311*/ {I_FMUL, 1, {FPUREG,0,0}, "\1\xD8\10\xC8", IF_8086|IF_FPU}, /* 312*/ {I_FMUL, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC8", IF_8086|IF_FPU}, /* 313*/ {I_FMULP, 1, {FPUREG,0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, /* 314*/ {I_FMULP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, /* 315*/ {I_FNCLEX, 0, {0,0,0}, "\2\xDB\xE2", IF_8086|IF_FPU}, /* 316*/ {I_FNDISI, 0, {0,0,0}, "\2\xDB\xE1", IF_8086|IF_FPU}, /* 317*/ {I_FNENI, 0, {0,0,0}, "\2\xDB\xE0", IF_8086|IF_FPU}, /* 318*/ {I_FNINIT, 0, {0,0,0}, "\2\xDB\xE3", IF_8086|IF_FPU}, /* 319*/ {I_FNOP, 0, {0,0,0}, "\2\xD9\xD0", IF_8086|IF_FPU}, /* 320*/ {I_FNSAVE, 1, {MEMORY,0,0}, "\300\1\xDD\206", IF_8086|IF_FPU}, /* 321*/ {I_FNSTCW, 1, {MEMORY,0,0}, "\300\1\xD9\207", IF_8086|IF_FPU|IF_SW}, /* 322*/ {I_FNSTENV, 1, {MEMORY,0,0}, "\300\1\xD9\206", IF_8086|IF_FPU}, /* 323*/ {I_FNSTSW, 1, {MEMORY,0,0}, "\300\1\xDD\207", IF_8086|IF_FPU|IF_SW}, /* 324*/ {I_FNSTSW, 1, {REG_AX,0,0}, "\2\xDF\xE0", IF_286|IF_FPU}, /* 325*/ {I_FPATAN, 0, {0,0,0}, "\2\xD9\xF3", IF_8086|IF_FPU}, /* 326*/ {I_FPREM, 0, {0,0,0}, "\2\xD9\xF8", IF_8086|IF_FPU}, /* 327*/ {I_FPREM1, 0, {0,0,0}, "\2\xD9\xF5", IF_386|IF_FPU}, /* 328*/ {I_FPTAN, 0, {0,0,0}, "\2\xD9\xF2", IF_8086|IF_FPU}, /* 329*/ {I_FRNDINT, 0, {0,0,0}, "\2\xD9\xFC", IF_8086|IF_FPU}, /* 330*/ {I_FRSTOR, 1, {MEMORY,0,0}, "\300\1\xDD\204", IF_8086|IF_FPU}, /* 331*/ {I_FSAVE, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\206", IF_8086|IF_FPU}, /* 332*/ {I_FSCALE, 0, {0,0,0}, "\2\xD9\xFD", IF_8086|IF_FPU}, /* 333*/ {I_FSETPM, 0, {0,0,0}, "\2\xDB\xE4", IF_286|IF_FPU}, /* 334*/ {I_FSIN, 0, {0,0,0}, "\2\xD9\xFE", IF_386|IF_FPU}, /* 335*/ {I_FSINCOS, 0, {0,0,0}, "\2\xD9\xFB", IF_386|IF_FPU}, /* 336*/ {I_FSQRT, 0, {0,0,0}, "\2\xD9\xFA", IF_8086|IF_FPU}, /* 337*/ {I_FST, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\202", IF_8086|IF_FPU}, /* 338*/ {I_FST, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\202", IF_8086|IF_FPU}, /* 339*/ {I_FST, 1, {FPUREG,0,0}, "\1\xDD\10\xD0", IF_8086|IF_FPU}, /* 340*/ {I_FSTCW, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\207", IF_8086|IF_FPU|IF_SW}, /* 341*/ {I_FSTENV, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\206", IF_8086|IF_FPU}, /* 342*/ {I_FSTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\203", IF_8086|IF_FPU}, /* 343*/ {I_FSTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\203", IF_8086|IF_FPU}, /* 344*/ {I_FSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\207", IF_8086|IF_FPU}, /* 345*/ {I_FSTP, 1, {FPUREG,0,0}, "\1\xDD\10\xD8", IF_8086|IF_FPU}, /* 346*/ {I_FSTSW, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\207", IF_8086|IF_FPU|IF_SW}, /* 347*/ {I_FSTSW, 1, {REG_AX,0,0}, "\3\x9B\xDF\xE0", IF_286|IF_FPU}, /* 348*/ {I_FSUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\204", IF_8086|IF_FPU}, /* 349*/ {I_FSUB, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\204", IF_8086|IF_FPU}, /* 350*/ {I_FSUB, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, /* 351*/ {I_FSUB, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, /* 352*/ {I_FSUB, 1, {FPUREG,0,0}, "\1\xD8\10\xE0", IF_8086|IF_FPU}, /* 353*/ {I_FSUB, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE0", IF_8086|IF_FPU}, /* 354*/ {I_FSUBP, 1, {FPUREG,0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, /* 355*/ {I_FSUBP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, /* 356*/ {I_FSUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\205", IF_8086|IF_FPU}, /* 357*/ {I_FSUBR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\205", IF_8086|IF_FPU}, /* 358*/ {I_FSUBR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, /* 359*/ {I_FSUBR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, /* 360*/ {I_FSUBR, 1, {FPUREG,0,0}, "\1\xD8\10\xE8", IF_8086|IF_FPU}, /* 361*/ {I_FSUBR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE8", IF_8086|IF_FPU}, /* 362*/ {I_FSUBRP, 1, {FPUREG,0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, /* 363*/ {I_FSUBRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, /* 364*/ {I_FTST, 0, {0,0,0}, "\2\xD9\xE4", IF_8086|IF_FPU}, /* 365*/ {I_FUCOM, 1, {FPUREG,0,0}, "\1\xDD\10\xE0", IF_386|IF_FPU}, /* 366*/ {I_FUCOM, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE0", IF_386|IF_FPU}, /* 367*/ {I_FUCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xE8", IF_P6|IF_FPU}, /* 368*/ {I_FUCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xE8", IF_P6|IF_FPU}, /* 369*/ {I_FUCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xE8", IF_P6|IF_FPU}, /* 370*/ {I_FUCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xE8", IF_P6|IF_FPU}, /* 371*/ {I_FUCOMP, 1, {FPUREG,0,0}, "\1\xDD\10\xE8", IF_386|IF_FPU}, /* 372*/ {I_FUCOMP, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE8", IF_386|IF_FPU}, /* 373*/ {I_FUCOMPP, 0, {0,0,0}, "\2\xDA\xE9", IF_386|IF_FPU}, /* 374*/ {I_FXAM, 0, {0,0,0}, "\2\xD9\xE5", IF_8086|IF_FPU}, /* 375*/ {I_FXCH, 0, {0,0,0}, "\2\xD9\xC9", IF_8086|IF_FPU}, /* 376*/ {I_FXCH, 1, {FPUREG,0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, /* 377*/ {I_FXCH, 2, {FPUREG,FPU0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, /* 378*/ {I_FXCH, 2, {FPU0,FPUREG,0}, "\1\xD9\11\xC8", IF_8086|IF_FPU}, /* 379*/ {I_FXTRACT, 0, {0,0,0}, "\2\xD9\xF4", IF_8086|IF_FPU}, /* 380*/ {I_FYL2X, 0, {0,0,0}, "\2\xD9\xF1", IF_8086|IF_FPU}, /* 381*/ {I_FYL2XP1, 0, {0,0,0}, "\2\xD9\xF9", IF_8086|IF_FPU}, /* 382*/ {I_HLT, 0, {0,0,0}, "\1\xF4", IF_8086|IF_PRIV}, /* 383*/ {I_IDIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\207", IF_8086}, /* 384*/ {I_IDIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\207", IF_8086}, /* 385*/ {I_IDIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\207", IF_386}, /* 386*/ {I_IMUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\205", IF_8086}, /* 387*/ {I_IMUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\205", IF_8086}, /* 388*/ {I_IMUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\205", IF_386}, /* 389*/ {I_IMUL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xAF\110", IF_386|IF_SM}, /* 390*/ {I_IMUL, 2, {REG16,REG16,0}, "\320\2\x0F\xAF\110", IF_386}, /* 391*/ {I_IMUL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xAF\110", IF_386|IF_SM}, /* 392*/ {I_IMUL, 2, {REG32,REG32,0}, "\321\2\x0F\xAF\110", IF_386}, /* 393*/ {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS8}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, /* 394*/ {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS16}, "\320\301\1\x69\110\32", IF_186|IF_SM}, /* 395*/ {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS8}, "\320\1\x6B\110\16", IF_186}, /* 396*/ {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS16}, "\320\1\x69\110\32", IF_186}, /* 397*/ {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS8}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, /* 398*/ {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS32}, "\321\301\1\x69\110\42", IF_386|IF_SM}, /* 399*/ {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS8}, "\321\1\x6B\110\16", IF_386}, /* 400*/ {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS32}, "\321\1\x69\110\42", IF_386}, /* 401*/ {I_IMUL, 2, {REG16,IMMEDIATE|BITS8,0}, "\320\1\x6B\100\15", IF_186}, /* 402*/ {I_IMUL, 2, {REG16,IMMEDIATE|BITS16,0}, "\320\1\x69\100\31", IF_186}, /* 403*/ {I_IMUL, 2, {REG32,IMMEDIATE|BITS8,0}, "\321\1\x6B\100\15", IF_386}, /* 404*/ {I_IMUL, 2, {REG32,IMMEDIATE|BITS32,0}, "\321\1\x69\100\41", IF_386}, /* 405*/ {I_IN, 2, {REG_AL,IMMEDIATE,0}, "\1\xE4\25", IF_8086|IF_SB}, /* 406*/ {I_IN, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xE5\25", IF_8086|IF_SB}, /* 407*/ {I_IN, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xE5\25", IF_386|IF_SB}, /* 408*/ {I_IN, 2, {REG_AL,REG_DX,0}, "\1\xEC", IF_8086}, /* 409*/ {I_IN, 2, {REG_AX,REG_DX,0}, "\320\1\xED", IF_8086}, /* 410*/ {I_IN, 2, {REG_EAX,REG_DX,0}, "\321\1\xED", IF_386}, /* 411*/ {I_INC, 1, {REG16,0,0}, "\320\10\x40", IF_8086}, /* 412*/ {I_INC, 1, {REG32,0,0}, "\321\10\x40", IF_386}, /* 413*/ {I_INC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\200", IF_8086}, /* 414*/ {I_INC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\200", IF_8086}, /* 415*/ {I_INC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\200", IF_386}, /* 416*/ {I_INSB, 0, {0,0,0}, "\1\x6C", IF_186}, /* 417*/ {I_INSD, 0, {0,0,0}, "\321\1\x6D", IF_386}, /* 418*/ {I_INSW, 0, {0,0,0}, "\320\1\x6D", IF_186}, /* 419*/ {I_INT, 1, {IMMEDIATE,0,0}, "\1\xCD\24", IF_8086|IF_SB}, /* 420*/ {I_INT1, 0, {0,0,0}, "\1\xF1", IF_386}, /* 421*/ {I_INT3, 0, {0,0,0}, "\1\xCC", IF_8086}, /* 422*/ {I_INTO, 0, {0,0,0}, "\1\xCE", IF_8086}, /* 423*/ {I_INVD, 0, {0,0,0}, "\2\x0F\x08", IF_486|IF_PRIV}, /* 424*/ {I_INVLPG, 1, {MEMORY,0,0}, "\300\2\x0F\x01\207", IF_486|IF_PRIV}, /* 425*/ {I_IRET, 0, {0,0,0}, "\322\1\xCF", IF_8086}, /* 426*/ {I_IRETD, 0, {0,0,0}, "\321\1\xCF", IF_386}, /* 427*/ {I_IRETW, 0, {0,0,0}, "\320\1\xCF", IF_8086}, /* 428*/ {I_JCXZ, 1, {IMMEDIATE,0,0}, "\310\1\xE3\50", IF_8086}, /* 429*/ {I_JECXZ, 1, {IMMEDIATE,0,0}, "\311\1\xE3\50", IF_386}, /* 430*/ {I_JMP, 1, {IMMEDIATE|SHORT,0,0}, "\1\xEB\50", IF_8086}, /* 431*/ {I_JMP, 1, {IMMEDIATE,0,0}, "\322\1\xE9\64", IF_8086}, /* 432*/ {I_JMP, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE9\64", IF_8086}, /* 433*/ {I_JMP, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE9\64", IF_386}, /* 434*/ {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\xEA\35\30", IF_8086}, /* 435*/ {I_JMP, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\xEA\31\30", IF_8086}, /* 436*/ {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\xEA\31\30", IF_8086}, /* 437*/ {I_JMP, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\xEA\41\30", IF_386}, /* 438*/ {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\xEA\41\30", IF_386}, /* 439*/ {I_JMP, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\205", IF_8086}, /* 440*/ {I_JMP, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\205", IF_8086}, /* 441*/ {I_JMP, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\205", IF_386}, /* 442*/ {I_JMP, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\204", IF_8086}, /* 443*/ {I_JMP, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\204", IF_8086}, /* 444*/ {I_JMP, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\204", IF_386}, /* 445*/ {I_JMP, 1, {REG16,0,0}, "\320\300\1\xFF\204", IF_8086}, /* 446*/ {I_JMP, 1, {REG32,0,0}, "\321\300\1\xFF\204", IF_386}, /* 447*/ {I_JMP, 1, {MEMORY,0,0}, "\322\300\1\xFF\204", IF_8086}, /* 448*/ {I_JMP, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\204", IF_8086}, /* 449*/ {I_JMP, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\204", IF_386}, /* 450*/ {I_JMPE, 1, {IMMEDIATE,0,0}, "\322\2\x0F\xB8\64", IF_IA64}, /* 451*/ {I_JMPE, 1, {IMMEDIATE|BITS16,0,0}, "\320\2\x0F\xB8\64", IF_IA64}, /* 452*/ {I_JMPE, 1, {IMMEDIATE|BITS32,0,0}, "\321\2\x0F\xB8\64", IF_IA64}, /* 453*/ {I_JMPE, 1, {REGMEM|BITS16,0,0}, "\320\2\x0F\x00\206", IF_IA64}, /* 454*/ {I_JMPE, 1, {REGMEM|BITS32,0,0}, "\321\2\x0F\x00\206", IF_IA64}, /* 455*/ {I_LAHF, 0, {0,0,0}, "\1\x9F", IF_8086}, /* 456*/ {I_LAR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x02\110", IF_286|IF_PROT|IF_SM}, /* 457*/ {I_LAR, 2, {REG16,REG16,0}, "\320\2\x0F\x02\110", IF_286|IF_PROT}, /* 458*/ {I_LAR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x02\110", IF_386|IF_PROT|IF_SM}, /* 459*/ {I_LAR, 2, {REG32,REG32,0}, "\321\2\x0F\x02\110", IF_386|IF_PROT}, /* 460*/ {I_LDS, 2, {REG16,MEMORY,0}, "\320\301\1\xC5\110", IF_8086}, /* 461*/ {I_LDS, 2, {REG32,MEMORY,0}, "\321\301\1\xC5\110", IF_386}, /* 462*/ {I_LEA, 2, {REG16,MEMORY,0}, "\320\301\1\x8D\110", IF_8086}, /* 463*/ {I_LEA, 2, {REG32,MEMORY,0}, "\321\301\1\x8D\110", IF_386}, /* 464*/ {I_LEAVE, 0, {0,0,0}, "\1\xC9", IF_186}, /* 465*/ {I_LES, 2, {REG16,MEMORY,0}, "\320\301\1\xC4\110", IF_8086}, /* 466*/ {I_LES, 2, {REG32,MEMORY,0}, "\321\301\1\xC4\110", IF_386}, /* 467*/ {I_LFS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB4\110", IF_386}, /* 468*/ {I_LFS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB4\110", IF_386}, /* 469*/ {I_LGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\202", IF_286|IF_PRIV}, /* 470*/ {I_LGS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB5\110", IF_386}, /* 471*/ {I_LGS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB5\110", IF_386}, /* 472*/ {I_LIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\203", IF_286|IF_PRIV}, /* 473*/ {I_LLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, /* 474*/ {I_LLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, /* 475*/ {I_LLDT, 1, {REG16,0,0}, "\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, /* 476*/ {I_LMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, /* 477*/ {I_LMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, /* 478*/ {I_LMSW, 1, {REG16,0,0}, "\2\x0F\x01\206", IF_286|IF_PRIV}, /* 479*/ {I_LOADALL, 0, {0,0,0}, "\2\x0F\x07", IF_386|IF_UNDOC}, /* 480*/ {I_LOADALL286, 0, {0,0,0}, "\2\x0F\x05", IF_286|IF_UNDOC}, /* 481*/ {I_LODSB, 0, {0,0,0}, "\1\xAC", IF_8086}, /* 482*/ {I_LODSD, 0, {0,0,0}, "\321\1\xAD", IF_386}, /* 483*/ {I_LODSW, 0, {0,0,0}, "\320\1\xAD", IF_8086}, /* 484*/ {I_LOOP, 1, {IMMEDIATE,0,0}, "\312\1\xE2\50", IF_8086}, /* 485*/ {I_LOOP, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE2\50", IF_8086}, /* 486*/ {I_LOOP, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE2\50", IF_386}, /* 487*/ {I_LOOPE, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, /* 488*/ {I_LOOPE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, /* 489*/ {I_LOOPE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, /* 490*/ {I_LOOPNE, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, /* 491*/ {I_LOOPNE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, /* 492*/ {I_LOOPNE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, /* 493*/ {I_LOOPNZ, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, /* 494*/ {I_LOOPNZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, /* 495*/ {I_LOOPNZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, /* 496*/ {I_LOOPZ, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, /* 497*/ {I_LOOPZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, /* 498*/ {I_LOOPZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, /* 499*/ {I_LSL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x03\110", IF_286|IF_PROT|IF_SM}, /* 500*/ {I_LSL, 2, {REG16,REG16,0}, "\320\2\x0F\x03\110", IF_286|IF_PROT}, /* 501*/ {I_LSL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x03\110", IF_386|IF_PROT|IF_SM}, /* 502*/ {I_LSL, 2, {REG32,REG32,0}, "\321\2\x0F\x03\110", IF_386|IF_PROT}, /* 503*/ {I_LSS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB2\110", IF_386}, /* 504*/ {I_LSS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB2\110", IF_386}, /* 505*/ {I_LTR, 1, {MEMORY,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, /* 506*/ {I_LTR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, /* 507*/ {I_LTR, 1, {REG16,0,0}, "\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, /* 508*/ {I_MONITOR, 0, {0,0,0}, "\3\x0F\x01\xC8", IF_PRESCOTT}, /* 509*/ {I_MOV, 2, {MEMORY,REG_SREG,0}, "\300\1\x8C\101", IF_8086|IF_SM}, /* 510*/ {I_MOV, 2, {REG16,REG_SREG,0}, "\320\1\x8C\101", IF_8086}, /* 511*/ {I_MOV, 2, {REG32,REG_SREG,0}, "\321\1\x8C\101", IF_386}, /* 512*/ {I_MOV, 2, {REG_SREG,MEMORY,0}, "\301\1\x8E\110", IF_8086|IF_SM}, /* 513*/ {I_MOV, 2, {REG_SREG,REG16,0}, "\1\x8E\110", IF_8086}, /* 514*/ {I_MOV, 2, {REG_SREG,REG32,0}, "\1\x8E\110", IF_386}, /* 515*/ {I_MOV, 2, {REG_AL,MEM_OFFS,0}, "\301\1\xA0\45", IF_8086|IF_SM}, /* 516*/ {I_MOV, 2, {REG_AX,MEM_OFFS,0}, "\301\320\1\xA1\45", IF_8086|IF_SM}, /* 517*/ {I_MOV, 2, {REG_EAX,MEM_OFFS,0}, "\301\321\1\xA1\45", IF_386|IF_SM}, /* 518*/ {I_MOV, 2, {MEM_OFFS,REG_AL,0}, "\300\1\xA2\44", IF_8086|IF_SM}, /* 519*/ {I_MOV, 2, {MEM_OFFS,REG_AX,0}, "\300\320\1\xA3\44", IF_8086|IF_SM}, /* 520*/ {I_MOV, 2, {MEM_OFFS,REG_EAX,0}, "\300\321\1\xA3\44", IF_386|IF_SM}, /* 521*/ {I_MOV, 2, {REG32,REG_CREG,0}, "\2\x0F\x20\101", IF_386|IF_PRIV}, /* 522*/ {I_MOV, 2, {REG32,REG_DREG,0}, "\2\x0F\x21\101", IF_386|IF_PRIV}, /* 523*/ {I_MOV, 2, {REG32,REG_TREG,0}, "\2\x0F\x24\101", IF_386|IF_PRIV}, /* 524*/ {I_MOV, 2, {REG_CREG,REG32,0}, "\2\x0F\x22\110", IF_386|IF_PRIV}, /* 525*/ {I_MOV, 2, {REG_DREG,REG32,0}, "\2\x0F\x23\110", IF_386|IF_PRIV}, /* 526*/ {I_MOV, 2, {REG_TREG,REG32,0}, "\2\x0F\x26\110", IF_386|IF_PRIV}, /* 527*/ {I_MOV, 2, {MEMORY,REG8,0}, "\300\1\x88\101", IF_8086|IF_SM}, /* 528*/ {I_MOV, 2, {REG8,REG8,0}, "\1\x88\101", IF_8086}, /* 529*/ {I_MOV, 2, {MEMORY,REG16,0}, "\320\300\1\x89\101", IF_8086|IF_SM}, /* 530*/ {I_MOV, 2, {REG16,REG16,0}, "\320\1\x89\101", IF_8086}, /* 531*/ {I_MOV, 2, {MEMORY,REG32,0}, "\321\300\1\x89\101", IF_386|IF_SM}, /* 532*/ {I_MOV, 2, {REG32,REG32,0}, "\321\1\x89\101", IF_386}, /* 533*/ {I_MOV, 2, {REG8,MEMORY,0}, "\301\1\x8A\110", IF_8086|IF_SM}, /* 534*/ {I_MOV, 2, {REG8,REG8,0}, "\1\x8A\110", IF_8086}, /* 535*/ {I_MOV, 2, {REG16,MEMORY,0}, "\320\301\1\x8B\110", IF_8086|IF_SM}, /* 536*/ {I_MOV, 2, {REG16,REG16,0}, "\320\1\x8B\110", IF_8086}, /* 537*/ {I_MOV, 2, {REG32,MEMORY,0}, "\321\301\1\x8B\110", IF_386|IF_SM}, /* 538*/ {I_MOV, 2, {REG32,REG32,0}, "\321\1\x8B\110", IF_386}, /* 539*/ {I_MOV, 2, {REG8,IMMEDIATE,0}, "\10\xB0\21", IF_8086|IF_SM}, /* 540*/ {I_MOV, 2, {REG16,IMMEDIATE,0}, "\320\10\xB8\31", IF_8086|IF_SM}, /* 541*/ {I_MOV, 2, {REG32,IMMEDIATE,0}, "\321\10\xB8\41", IF_386|IF_SM}, /* 542*/ {I_MOV, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, /* 543*/ {I_MOV, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, /* 544*/ {I_MOV, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, /* 545*/ {I_MOV, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, /* 546*/ {I_MOV, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, /* 547*/ {I_MOV, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, /* 548*/ {I_MOVD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6E\110", IF_PENT|IF_MMX|IF_SD}, /* 549*/ {I_MOVD, 2, {MMXREG,REG32,0}, "\2\x0F\x6E\110", IF_PENT|IF_MMX}, /* 550*/ {I_MOVD, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7E\101", IF_PENT|IF_MMX|IF_SD}, /* 551*/ {I_MOVD, 2, {REG32,MMXREG,0}, "\2\x0F\x7E\101", IF_PENT|IF_MMX}, /* 552*/ {I_MOVQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6F\110", IF_PENT|IF_MMX|IF_SM}, /* 553*/ {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6F\110", IF_PENT|IF_MMX}, /* 554*/ {I_MOVQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7F\101", IF_PENT|IF_MMX|IF_SM}, /* 555*/ {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x7F\101", IF_PENT|IF_MMX}, /* 556*/ {I_MOVSB, 0, {0,0,0}, "\1\xA4", IF_8086}, /* 557*/ {I_MOVSD, 0, {0,0,0}, "\321\1\xA5", IF_386}, /* 558*/ {I_MOVSW, 0, {0,0,0}, "\320\1\xA5", IF_8086}, /* 559*/ {I_MOVSX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBE\110", IF_386|IF_SB}, /* 560*/ {I_MOVSX, 2, {REG16,REG8,0}, "\320\2\x0F\xBE\110", IF_386}, /* 561*/ {I_MOVSX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xBE\110", IF_386}, /* 562*/ {I_MOVSX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xBF\110", IF_386}, /* 563*/ {I_MOVZX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB6\110", IF_386|IF_SB}, /* 564*/ {I_MOVZX, 2, {REG16,REG8,0}, "\320\2\x0F\xB6\110", IF_386}, /* 565*/ {I_MOVZX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xB6\110", IF_386}, /* 566*/ {I_MOVZX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xB7\110", IF_386}, /* 567*/ {I_MUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\204", IF_8086}, /* 568*/ {I_MUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\204", IF_8086}, /* 569*/ {I_MUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\204", IF_386}, /* 570*/ {I_MWAIT, 0, {0,0,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, /* 571*/ {I_NEG, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\203", IF_8086}, /* 572*/ {I_NEG, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\203", IF_8086}, /* 573*/ {I_NEG, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\203", IF_386}, /* 574*/ {I_NOP, 0, {0,0,0}, "\1\x90", IF_8086}, /* 575*/ {I_NOT, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\202", IF_8086}, /* 576*/ {I_NOT, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\202", IF_8086}, /* 577*/ {I_NOT, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\202", IF_386}, /* 578*/ {I_OR, 2, {MEMORY,REG8,0}, "\300\1\x08\101", IF_8086|IF_SM}, /* 579*/ {I_OR, 2, {REG8,REG8,0}, "\1\x08\101", IF_8086}, /* 580*/ {I_OR, 2, {MEMORY,REG16,0}, "\320\300\1\x09\101", IF_8086|IF_SM}, /* 581*/ {I_OR, 2, {REG16,REG16,0}, "\320\1\x09\101", IF_8086}, /* 582*/ {I_OR, 2, {MEMORY,REG32,0}, "\321\300\1\x09\101", IF_386|IF_SM}, /* 583*/ {I_OR, 2, {REG32,REG32,0}, "\321\1\x09\101", IF_386}, /* 584*/ {I_OR, 2, {REG8,MEMORY,0}, "\301\1\x0A\110", IF_8086|IF_SM}, /* 585*/ {I_OR, 2, {REG8,REG8,0}, "\1\x0A\110", IF_8086}, /* 586*/ {I_OR, 2, {REG16,MEMORY,0}, "\320\301\1\x0B\110", IF_8086|IF_SM}, /* 587*/ {I_OR, 2, {REG16,REG16,0}, "\320\1\x0B\110", IF_8086}, /* 588*/ {I_OR, 2, {REG32,MEMORY,0}, "\321\301\1\x0B\110", IF_386|IF_SM}, /* 589*/ {I_OR, 2, {REG32,REG32,0}, "\321\1\x0B\110", IF_386}, /* 590*/ {I_OR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\201\15", IF_8086}, /* 591*/ {I_OR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\201\15", IF_386}, /* 592*/ {I_OR, 2, {REG_AL,IMMEDIATE,0}, "\1\x0C\21", IF_8086|IF_SM}, /* 593*/ {I_OR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x0D\31", IF_8086|IF_SM}, /* 594*/ {I_OR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x0D\41", IF_386|IF_SM}, /* 595*/ {I_OR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, /* 596*/ {I_OR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, /* 597*/ {I_OR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, /* 598*/ {I_OR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, /* 599*/ {I_OR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, /* 600*/ {I_OR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, /* 601*/ {I_OUT, 2, {IMMEDIATE,REG_AL,0}, "\1\xE6\24", IF_8086|IF_SB}, /* 602*/ {I_OUT, 2, {IMMEDIATE,REG_AX,0}, "\320\1\xE7\24", IF_8086|IF_SB}, /* 603*/ {I_OUT, 2, {IMMEDIATE,REG_EAX,0}, "\321\1\xE7\24", IF_386|IF_SB}, /* 604*/ {I_OUT, 2, {REG_DX,REG_AL,0}, "\1\xEE", IF_8086}, /* 605*/ {I_OUT, 2, {REG_DX,REG_AX,0}, "\320\1\xEF", IF_8086}, /* 606*/ {I_OUT, 2, {REG_DX,REG_EAX,0}, "\321\1\xEF", IF_386}, /* 607*/ {I_OUTSB, 0, {0,0,0}, "\1\x6E", IF_186}, /* 608*/ {I_OUTSD, 0, {0,0,0}, "\321\1\x6F", IF_386}, /* 609*/ {I_OUTSW, 0, {0,0,0}, "\320\1\x6F", IF_186}, /* 610*/ {I_PACKSSDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6B\110", IF_PENT|IF_MMX|IF_SM}, /* 611*/ {I_PACKSSDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6B\110", IF_PENT|IF_MMX}, /* 612*/ {I_PACKSSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x63\110", IF_PENT|IF_MMX|IF_SM}, /* 613*/ {I_PACKSSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x63\110", IF_PENT|IF_MMX}, /* 614*/ {I_PACKUSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x67\110", IF_PENT|IF_MMX|IF_SM}, /* 615*/ {I_PACKUSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x67\110", IF_PENT|IF_MMX}, /* 616*/ {I_PADDB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFC\110", IF_PENT|IF_MMX|IF_SM}, /* 617*/ {I_PADDB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFC\110", IF_PENT|IF_MMX}, /* 618*/ {I_PADDD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFE\110", IF_PENT|IF_MMX|IF_SM}, /* 619*/ {I_PADDD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFE\110", IF_PENT|IF_MMX}, /* 620*/ {I_PADDSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEC\110", IF_PENT|IF_MMX|IF_SM}, /* 621*/ {I_PADDSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEC\110", IF_PENT|IF_MMX}, /* 622*/ {I_PADDSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x51\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 623*/ {I_PADDSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x51\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 624*/ {I_PADDSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xED\110", IF_PENT|IF_MMX|IF_SM}, /* 625*/ {I_PADDSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xED\110", IF_PENT|IF_MMX}, /* 626*/ {I_PADDUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDC\110", IF_PENT|IF_MMX|IF_SM}, /* 627*/ {I_PADDUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDC\110", IF_PENT|IF_MMX}, /* 628*/ {I_PADDUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDD\110", IF_PENT|IF_MMX|IF_SM}, /* 629*/ {I_PADDUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDD\110", IF_PENT|IF_MMX}, /* 630*/ {I_PADDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFD\110", IF_PENT|IF_MMX|IF_SM}, /* 631*/ {I_PADDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFD\110", IF_PENT|IF_MMX}, /* 632*/ {I_PAND, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDB\110", IF_PENT|IF_MMX|IF_SM}, /* 633*/ {I_PAND, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDB\110", IF_PENT|IF_MMX}, /* 634*/ {I_PANDN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDF\110", IF_PENT|IF_MMX|IF_SM}, /* 635*/ {I_PANDN, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDF\110", IF_PENT|IF_MMX}, /* 636*/ {I_PAVEB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x50\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 637*/ {I_PAVEB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x50\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 638*/ {I_PAVGUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW|IF_SM}, /* 639*/ {I_PAVGUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW}, /* 640*/ {I_PCMPEQB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x74\110", IF_PENT|IF_MMX|IF_SM}, /* 641*/ {I_PCMPEQB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x74\110", IF_PENT|IF_MMX}, /* 642*/ {I_PCMPEQD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x76\110", IF_PENT|IF_MMX|IF_SM}, /* 643*/ {I_PCMPEQD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x76\110", IF_PENT|IF_MMX}, /* 644*/ {I_PCMPEQW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x75\110", IF_PENT|IF_MMX|IF_SM}, /* 645*/ {I_PCMPEQW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x75\110", IF_PENT|IF_MMX}, /* 646*/ {I_PCMPGTB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x64\110", IF_PENT|IF_MMX|IF_SM}, /* 647*/ {I_PCMPGTB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x64\110", IF_PENT|IF_MMX}, /* 648*/ {I_PCMPGTD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x66\110", IF_PENT|IF_MMX|IF_SM}, /* 649*/ {I_PCMPGTD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x66\110", IF_PENT|IF_MMX}, /* 650*/ {I_PCMPGTW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x65\110", IF_PENT|IF_MMX|IF_SM}, /* 651*/ {I_PCMPGTW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x65\110", IF_PENT|IF_MMX}, /* 652*/ {I_PDISTIB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 653*/ {I_PF2ID, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW|IF_SM}, /* 654*/ {I_PF2ID, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW}, /* 655*/ {I_PFACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW|IF_SM}, /* 656*/ {I_PFACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW}, /* 657*/ {I_PFADD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW|IF_SM}, /* 658*/ {I_PFADD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW}, /* 659*/ {I_PFCMPEQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW|IF_SM}, /* 660*/ {I_PFCMPEQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW}, /* 661*/ {I_PFCMPGE, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW|IF_SM}, /* 662*/ {I_PFCMPGE, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW}, /* 663*/ {I_PFCMPGT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW|IF_SM}, /* 664*/ {I_PFCMPGT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW}, /* 665*/ {I_PFMAX, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW|IF_SM}, /* 666*/ {I_PFMAX, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW}, /* 667*/ {I_PFMIN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW|IF_SM}, /* 668*/ {I_PFMIN, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW}, /* 669*/ {I_PFMUL, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW|IF_SM}, /* 670*/ {I_PFMUL, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW}, /* 671*/ {I_PFRCP, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW|IF_SM}, /* 672*/ {I_PFRCP, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW}, /* 673*/ {I_PFRCPIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW|IF_SM}, /* 674*/ {I_PFRCPIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW}, /* 675*/ {I_PFRCPIT2, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW|IF_SM}, /* 676*/ {I_PFRCPIT2, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW}, /* 677*/ {I_PFRSQIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW|IF_SM}, /* 678*/ {I_PFRSQIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW}, /* 679*/ {I_PFRSQRT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW|IF_SM}, /* 680*/ {I_PFRSQRT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW}, /* 681*/ {I_PFSUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW|IF_SM}, /* 682*/ {I_PFSUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW}, /* 683*/ {I_PFSUBR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW|IF_SM}, /* 684*/ {I_PFSUBR, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW}, /* 685*/ {I_PI2FD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW|IF_SM}, /* 686*/ {I_PI2FD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW}, /* 687*/ {I_PMACHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5E\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 688*/ {I_PMADDWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF5\110", IF_PENT|IF_MMX|IF_SM}, /* 689*/ {I_PMADDWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF5\110", IF_PENT|IF_MMX}, /* 690*/ {I_PMAGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x52\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 691*/ {I_PMAGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x52\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 692*/ {I_PMULHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 693*/ {I_PMULHRIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 694*/ {I_PMULHRWA, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW|IF_SM}, /* 695*/ {I_PMULHRWA, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW}, /* 696*/ {I_PMULHRWC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 697*/ {I_PMULHRWC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x59\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 698*/ {I_PMULHW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE5\110", IF_PENT|IF_MMX|IF_SM}, /* 699*/ {I_PMULHW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE5\110", IF_PENT|IF_MMX}, /* 700*/ {I_PMULLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD5\110", IF_PENT|IF_MMX|IF_SM}, /* 701*/ {I_PMULLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD5\110", IF_PENT|IF_MMX}, /* 702*/ {I_PMVGEZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5C\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 703*/ {I_PMVLZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 704*/ {I_PMVNZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 705*/ {I_PMVZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x58\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 706*/ {I_POP, 1, {REG16,0,0}, "\320\10\x58", IF_8086}, /* 707*/ {I_POP, 1, {REG32,0,0}, "\321\10\x58", IF_386}, /* 708*/ {I_POP, 1, {REGMEM|BITS16,0,0}, "\320\300\1\x8F\200", IF_8086}, /* 709*/ {I_POP, 1, {REGMEM|BITS32,0,0}, "\321\300\1\x8F\200", IF_386}, /* 710*/ {I_POP, 1, {REG_DESS,0,0}, "\4", IF_8086}, /* 711*/ {I_POP, 1, {REG_FSGS,0,0}, "\1\x0F\5", IF_386}, /* 712*/ {I_POPA, 0, {0,0,0}, "\322\1\x61", IF_186}, /* 713*/ {I_POPAD, 0, {0,0,0}, "\321\1\x61", IF_386}, /* 714*/ {I_POPAW, 0, {0,0,0}, "\320\1\x61", IF_186}, /* 715*/ {I_POPF, 0, {0,0,0}, "\322\1\x9D", IF_8086}, /* 716*/ {I_POPFD, 0, {0,0,0}, "\321\1\x9D", IF_386}, /* 717*/ {I_POPFW, 0, {0,0,0}, "\320\1\x9D", IF_8086}, /* 718*/ {I_POR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEB\110", IF_PENT|IF_MMX|IF_SM}, /* 719*/ {I_POR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEB\110", IF_PENT|IF_MMX}, /* 720*/ {I_PREFETCH, 1, {MEMORY,0,0}, "\2\x0F\x0D\200", IF_PENT|IF_3DNOW|IF_SM}, /* 721*/ {I_PREFETCHW, 1, {MEMORY,0,0}, "\2\x0F\x0D\201", IF_PENT|IF_3DNOW|IF_SM}, /* 722*/ {I_PSLLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF2\110", IF_PENT|IF_MMX|IF_SM}, /* 723*/ {I_PSLLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF2\110", IF_PENT|IF_MMX}, /* 724*/ {I_PSLLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\206\25", IF_PENT|IF_MMX}, /* 725*/ {I_PSLLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF3\110", IF_PENT|IF_MMX|IF_SM}, /* 726*/ {I_PSLLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF3\110", IF_PENT|IF_MMX}, /* 727*/ {I_PSLLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\206\25", IF_PENT|IF_MMX}, /* 728*/ {I_PSLLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF1\110", IF_PENT|IF_MMX|IF_SM}, /* 729*/ {I_PSLLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF1\110", IF_PENT|IF_MMX}, /* 730*/ {I_PSLLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\206\25", IF_PENT|IF_MMX}, /* 731*/ {I_PSRAD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE2\110", IF_PENT|IF_MMX|IF_SM}, /* 732*/ {I_PSRAD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE2\110", IF_PENT|IF_MMX}, /* 733*/ {I_PSRAD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\204\25", IF_PENT|IF_MMX}, /* 734*/ {I_PSRAW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE1\110", IF_PENT|IF_MMX|IF_SM}, /* 735*/ {I_PSRAW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE1\110", IF_PENT|IF_MMX}, /* 736*/ {I_PSRAW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\204\25", IF_PENT|IF_MMX}, /* 737*/ {I_PSRLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD2\110", IF_PENT|IF_MMX|IF_SM}, /* 738*/ {I_PSRLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD2\110", IF_PENT|IF_MMX}, /* 739*/ {I_PSRLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\202\25", IF_PENT|IF_MMX}, /* 740*/ {I_PSRLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD3\110", IF_PENT|IF_MMX|IF_SM}, /* 741*/ {I_PSRLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD3\110", IF_PENT|IF_MMX}, /* 742*/ {I_PSRLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\202\25", IF_PENT|IF_MMX}, /* 743*/ {I_PSRLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD1\110", IF_PENT|IF_MMX|IF_SM}, /* 744*/ {I_PSRLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD1\110", IF_PENT|IF_MMX}, /* 745*/ {I_PSRLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\202\25", IF_PENT|IF_MMX}, /* 746*/ {I_PSUBB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF8\110", IF_PENT|IF_MMX|IF_SM}, /* 747*/ {I_PSUBB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF8\110", IF_PENT|IF_MMX}, /* 748*/ {I_PSUBD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFA\110", IF_PENT|IF_MMX|IF_SM}, /* 749*/ {I_PSUBD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFA\110", IF_PENT|IF_MMX}, /* 750*/ {I_PSUBSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE8\110", IF_PENT|IF_MMX|IF_SM}, /* 751*/ {I_PSUBSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE8\110", IF_PENT|IF_MMX}, /* 752*/ {I_PSUBSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, /* 753*/ {I_PSUBSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x55\110", IF_PENT|IF_MMX|IF_CYRIX}, /* 754*/ {I_PSUBSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE9\110", IF_PENT|IF_MMX|IF_SM}, /* 755*/ {I_PSUBSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE9\110", IF_PENT|IF_MMX}, /* 756*/ {I_PSUBUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD8\110", IF_PENT|IF_MMX|IF_SM}, /* 757*/ {I_PSUBUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD8\110", IF_PENT|IF_MMX}, /* 758*/ {I_PSUBUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD9\110", IF_PENT|IF_MMX|IF_SM}, /* 759*/ {I_PSUBUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD9\110", IF_PENT|IF_MMX}, /* 760*/ {I_PSUBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF9\110", IF_PENT|IF_MMX|IF_SM}, /* 761*/ {I_PSUBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF9\110", IF_PENT|IF_MMX}, /* 762*/ {I_PUNPCKHBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x68\110", IF_PENT|IF_MMX|IF_SM}, /* 763*/ {I_PUNPCKHBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x68\110", IF_PENT|IF_MMX}, /* 764*/ {I_PUNPCKHDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6A\110", IF_PENT|IF_MMX|IF_SM}, /* 765*/ {I_PUNPCKHDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6A\110", IF_PENT|IF_MMX}, /* 766*/ {I_PUNPCKHWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x69\110", IF_PENT|IF_MMX|IF_SM}, /* 767*/ {I_PUNPCKHWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x69\110", IF_PENT|IF_MMX}, /* 768*/ {I_PUNPCKLBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x60\110", IF_PENT|IF_MMX|IF_SM}, /* 769*/ {I_PUNPCKLBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x60\110", IF_PENT|IF_MMX}, /* 770*/ {I_PUNPCKLDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x62\110", IF_PENT|IF_MMX|IF_SM}, /* 771*/ {I_PUNPCKLDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x62\110", IF_PENT|IF_MMX}, /* 772*/ {I_PUNPCKLWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x61\110", IF_PENT|IF_MMX|IF_SM}, /* 773*/ {I_PUNPCKLWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x61\110", IF_PENT|IF_MMX}, /* 774*/ {I_PUSH, 1, {REG16,0,0}, "\320\10\x50", IF_8086}, /* 775*/ {I_PUSH, 1, {REG32,0,0}, "\321\10\x50", IF_386}, /* 776*/ {I_PUSH, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\206", IF_8086}, /* 777*/ {I_PUSH, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\206", IF_386}, /* 778*/ {I_PUSH, 1, {REG_CS,0,0}, "\6", IF_8086}, /* 779*/ {I_PUSH, 1, {REG_DESS,0,0}, "\6", IF_8086}, /* 780*/ {I_PUSH, 1, {REG_FSGS,0,0}, "\1\x0F\7", IF_386}, /* 781*/ {I_PUSH, 1, {IMMEDIATE|BITS8,0,0}, "\1\x6A\14", IF_186}, /* 782*/ {I_PUSH, 1, {IMMEDIATE|BITS16,0,0}, "\320\133\1\x68\130", IF_186}, /* 783*/ {I_PUSH, 1, {IMMEDIATE|BITS32,0,0}, "\321\143\1\x68\140", IF_386}, /* 784*/ {I_PUSH, 1, {IMMEDIATE,0,0}, "\1\x68\34", IF_186}, /* 785*/ {I_PUSHA, 0, {0,0,0}, "\322\1\x60", IF_186}, /* 786*/ {I_PUSHAD, 0, {0,0,0}, "\321\1\x60", IF_386}, /* 787*/ {I_PUSHAW, 0, {0,0,0}, "\320\1\x60", IF_186}, /* 788*/ {I_PUSHF, 0, {0,0,0}, "\322\1\x9C", IF_8086}, /* 789*/ {I_PUSHFD, 0, {0,0,0}, "\321\1\x9C", IF_386}, /* 790*/ {I_PUSHFW, 0, {0,0,0}, "\320\1\x9C", IF_8086}, /* 791*/ {I_PXOR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEF\110", IF_PENT|IF_MMX|IF_SM}, /* 792*/ {I_PXOR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEF\110", IF_PENT|IF_MMX}, /* 793*/ {I_RCL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\202", IF_8086}, /* 794*/ {I_RCL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\202", IF_8086}, /* 795*/ {I_RCL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\202\25", IF_186|IF_SB}, /* 796*/ {I_RCL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\202", IF_8086}, /* 797*/ {I_RCL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\202", IF_8086}, /* 798*/ {I_RCL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\202\25", IF_186|IF_SB}, /* 799*/ {I_RCL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\202", IF_386}, /* 800*/ {I_RCL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\202", IF_386}, /* 801*/ {I_RCL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\202\25", IF_386|IF_SB}, /* 802*/ {I_RCR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\203", IF_8086}, /* 803*/ {I_RCR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\203", IF_8086}, /* 804*/ {I_RCR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\203\25", IF_186|IF_SB}, /* 805*/ {I_RCR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\203", IF_8086}, /* 806*/ {I_RCR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\203", IF_8086}, /* 807*/ {I_RCR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\203\25", IF_186|IF_SB}, /* 808*/ {I_RCR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\203", IF_386}, /* 809*/ {I_RCR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\203", IF_386}, /* 810*/ {I_RCR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\203\25", IF_386|IF_SB}, /* 811*/ {I_RDSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x36\200", IF_P6|IF_CYRIX|IF_SMM}, /* 812*/ {I_RDMSR, 0, {0,0,0}, "\2\x0F\x32", IF_PENT|IF_PRIV}, /* 813*/ {I_RDPMC, 0, {0,0,0}, "\2\x0F\x33", IF_P6}, /* 814*/ {I_RDTSC, 0, {0,0,0}, "\2\x0F\x31", IF_PENT}, /* 815*/ {I_RESB, 1, {IMMEDIATE,0,0}, "\340", IF_8086}, /* 816*/ {I_RET, 0, {0,0,0}, "\1\xC3", IF_8086}, /* 817*/ {I_RET, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, /* 818*/ {I_RETF, 0, {0,0,0}, "\1\xCB", IF_8086}, /* 819*/ {I_RETF, 1, {IMMEDIATE,0,0}, "\1\xCA\30", IF_8086|IF_SW}, /* 820*/ {I_RETN, 0, {0,0,0}, "\1\xC3", IF_8086}, /* 821*/ {I_RETN, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, /* 822*/ {I_ROL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\200", IF_8086}, /* 823*/ {I_ROL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\200", IF_8086}, /* 824*/ {I_ROL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\200\25", IF_186|IF_SB}, /* 825*/ {I_ROL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\200", IF_8086}, /* 826*/ {I_ROL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\200", IF_8086}, /* 827*/ {I_ROL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\200\25", IF_186|IF_SB}, /* 828*/ {I_ROL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\200", IF_386}, /* 829*/ {I_ROL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\200", IF_386}, /* 830*/ {I_ROL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\200\25", IF_386|IF_SB}, /* 831*/ {I_ROR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\201", IF_8086}, /* 832*/ {I_ROR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\201", IF_8086}, /* 833*/ {I_ROR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\201\25", IF_186|IF_SB}, /* 834*/ {I_ROR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\201", IF_8086}, /* 835*/ {I_ROR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\201", IF_8086}, /* 836*/ {I_ROR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\201\25", IF_186|IF_SB}, /* 837*/ {I_ROR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\201", IF_386}, /* 838*/ {I_ROR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\201", IF_386}, /* 839*/ {I_ROR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\201\25", IF_386|IF_SB}, /* 840*/ {I_RSDC, 2, {REG_SREG,MEMORY|BITS80,0}, "\301\2\x0F\x79\110", IF_486|IF_CYRIX|IF_SMM}, /* 841*/ {I_RSLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7B\200", IF_486|IF_CYRIX|IF_SMM}, /* 842*/ {I_RSM, 0, {0,0,0}, "\2\x0F\xAA", IF_PENT|IF_SMM}, /* 843*/ {I_RSTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7D\200", IF_486|IF_CYRIX|IF_SMM}, /* 844*/ {I_SAHF, 0, {0,0,0}, "\1\x9E", IF_8086}, /* 845*/ {I_SALC, 0, {0,0,0}, "\1\xD6", IF_8086|IF_UNDOC}, /* 846*/ {I_SAR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\207", IF_8086}, /* 847*/ {I_SAR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\207", IF_8086}, /* 848*/ {I_SAR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\207\25", IF_186|IF_SB}, /* 849*/ {I_SAR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\207", IF_8086}, /* 850*/ {I_SAR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\207", IF_8086}, /* 851*/ {I_SAR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\207\25", IF_186|IF_SB}, /* 852*/ {I_SAR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\207", IF_386}, /* 853*/ {I_SAR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\207", IF_386}, /* 854*/ {I_SAR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\207\25", IF_386|IF_SB}, /* 855*/ {I_SBB, 2, {MEMORY,REG8,0}, "\300\1\x18\101", IF_8086|IF_SM}, /* 856*/ {I_SBB, 2, {REG8,REG8,0}, "\1\x18\101", IF_8086}, /* 857*/ {I_SBB, 2, {MEMORY,REG16,0}, "\320\300\1\x19\101", IF_8086|IF_SM}, /* 858*/ {I_SBB, 2, {REG16,REG16,0}, "\320\1\x19\101", IF_8086}, /* 859*/ {I_SBB, 2, {MEMORY,REG32,0}, "\321\300\1\x19\101", IF_386|IF_SM}, /* 860*/ {I_SBB, 2, {REG32,REG32,0}, "\321\1\x19\101", IF_386}, /* 861*/ {I_SBB, 2, {REG8,MEMORY,0}, "\301\1\x1A\110", IF_8086|IF_SM}, /* 862*/ {I_SBB, 2, {REG8,REG8,0}, "\1\x1A\110", IF_8086}, /* 863*/ {I_SBB, 2, {REG16,MEMORY,0}, "\320\301\1\x1B\110", IF_8086|IF_SM}, /* 864*/ {I_SBB, 2, {REG16,REG16,0}, "\320\1\x1B\110", IF_8086}, /* 865*/ {I_SBB, 2, {REG32,MEMORY,0}, "\321\301\1\x1B\110", IF_386|IF_SM}, /* 866*/ {I_SBB, 2, {REG32,REG32,0}, "\321\1\x1B\110", IF_386}, /* 867*/ {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\203\15", IF_8086}, /* 868*/ {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\203\15", IF_386}, /* 869*/ {I_SBB, 2, {REG_AL,IMMEDIATE,0}, "\1\x1C\21", IF_8086|IF_SM}, /* 870*/ {I_SBB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x1D\31", IF_8086|IF_SM}, /* 871*/ {I_SBB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x1D\41", IF_386|IF_SM}, /* 872*/ {I_SBB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, /* 873*/ {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, /* 874*/ {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, /* 875*/ {I_SBB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, /* 876*/ {I_SBB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, /* 877*/ {I_SBB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, /* 878*/ {I_SCASB, 0, {0,0,0}, "\332\1\xAE", IF_8086}, /* 879*/ {I_SCASD, 0, {0,0,0}, "\332\321\1\xAF", IF_386}, /* 880*/ {I_SCASW, 0, {0,0,0}, "\332\320\1\xAF", IF_8086}, /* 881*/ {I_SGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\200", IF_286}, /* 882*/ {I_SHL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, /* 883*/ {I_SHL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, /* 884*/ {I_SHL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, /* 885*/ {I_SHL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, /* 886*/ {I_SHL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, /* 887*/ {I_SHL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, /* 888*/ {I_SHL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, /* 889*/ {I_SHL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, /* 890*/ {I_SHL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, /* 891*/ {I_SHLD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 892*/ {I_SHLD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 893*/ {I_SHLD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 894*/ {I_SHLD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 895*/ {I_SHLD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xA5\101", IF_386|IF_SM}, /* 896*/ {I_SHLD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xA5\101", IF_386}, /* 897*/ {I_SHLD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xA5\101", IF_386|IF_SM}, /* 898*/ {I_SHLD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xA5\101", IF_386}, /* 899*/ {I_SHR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\205", IF_8086}, /* 900*/ {I_SHR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\205", IF_8086}, /* 901*/ {I_SHR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\205\25", IF_186|IF_SB}, /* 902*/ {I_SHR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\205", IF_8086}, /* 903*/ {I_SHR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\205", IF_8086}, /* 904*/ {I_SHR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\205\25", IF_186|IF_SB}, /* 905*/ {I_SHR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\205", IF_386}, /* 906*/ {I_SHR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\205", IF_386}, /* 907*/ {I_SHR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\205\25", IF_386|IF_SB}, /* 908*/ {I_SHRD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 909*/ {I_SHRD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 910*/ {I_SHRD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 911*/ {I_SHRD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, /* 912*/ {I_SHRD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xAD\101", IF_386|IF_SM}, /* 913*/ {I_SHRD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xAD\101", IF_386}, /* 914*/ {I_SHRD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xAD\101", IF_386|IF_SM}, /* 915*/ {I_SHRD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xAD\101", IF_386}, /* 916*/ {I_SIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\201", IF_286}, /* 917*/ {I_SLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\200", IF_286}, /* 918*/ {I_SLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\200", IF_286}, /* 919*/ {I_SLDT, 1, {REG16,0,0}, "\320\1\x0F\17\200", IF_286}, /* 920*/ {I_SLDT, 1, {REG32,0,0}, "\321\1\x0F\17\200", IF_386}, /* 921*/ {I_SMI, 0, {0,0,0}, "\1\xF1", IF_386|IF_UNDOC}, /* 922*/ {I_SMINT, 0, {0,0,0}, "\2\x0F\x38", IF_P6|IF_CYRIX}, /* 923*/ {I_SMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\204", IF_286}, /* 924*/ {I_SMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\204", IF_286}, /* 925*/ {I_SMSW, 1, {REG16,0,0}, "\320\2\x0F\x01\204", IF_286}, /* 926*/ {I_SMSW, 1, {REG32,0,0}, "\321\2\x0F\x01\204", IF_386}, /* 927*/ {I_STC, 0, {0,0,0}, "\1\xF9", IF_8086}, /* 928*/ {I_STD, 0, {0,0,0}, "\1\xFD", IF_8086}, /* 929*/ {I_STI, 0, {0,0,0}, "\1\xFB", IF_8086}, /* 930*/ {I_STOSB, 0, {0,0,0}, "\1\xAA", IF_8086}, /* 931*/ {I_STOSD, 0, {0,0,0}, "\321\1\xAB", IF_386}, /* 932*/ {I_STOSW, 0, {0,0,0}, "\320\1\xAB", IF_8086}, /* 933*/ {I_STR, 1, {MEMORY,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, /* 934*/ {I_STR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, /* 935*/ {I_STR, 1, {REG16,0,0}, "\320\1\x0F\17\201", IF_286|IF_PROT}, /* 936*/ {I_STR, 1, {REG32,0,0}, "\321\1\x0F\17\201", IF_386|IF_PROT}, /* 937*/ {I_SUB, 2, {MEMORY,REG8,0}, "\300\1\x28\101", IF_8086|IF_SM}, /* 938*/ {I_SUB, 2, {REG8,REG8,0}, "\1\x28\101", IF_8086}, /* 939*/ {I_SUB, 2, {MEMORY,REG16,0}, "\320\300\1\x29\101", IF_8086|IF_SM}, /* 940*/ {I_SUB, 2, {REG16,REG16,0}, "\320\1\x29\101", IF_8086}, /* 941*/ {I_SUB, 2, {MEMORY,REG32,0}, "\321\300\1\x29\101", IF_386|IF_SM}, /* 942*/ {I_SUB, 2, {REG32,REG32,0}, "\321\1\x29\101", IF_386}, /* 943*/ {I_SUB, 2, {REG8,MEMORY,0}, "\301\1\x2A\110", IF_8086|IF_SM}, /* 944*/ {I_SUB, 2, {REG8,REG8,0}, "\1\x2A\110", IF_8086}, /* 945*/ {I_SUB, 2, {REG16,MEMORY,0}, "\320\301\1\x2B\110", IF_8086|IF_SM}, /* 946*/ {I_SUB, 2, {REG16,REG16,0}, "\320\1\x2B\110", IF_8086}, /* 947*/ {I_SUB, 2, {REG32,MEMORY,0}, "\321\301\1\x2B\110", IF_386|IF_SM}, /* 948*/ {I_SUB, 2, {REG32,REG32,0}, "\321\1\x2B\110", IF_386}, /* 949*/ {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\205\15", IF_8086}, /* 950*/ {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\205\15", IF_386}, /* 951*/ {I_SUB, 2, {REG_AL,IMMEDIATE,0}, "\1\x2C\21", IF_8086|IF_SM}, /* 952*/ {I_SUB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x2D\31", IF_8086|IF_SM}, /* 953*/ {I_SUB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x2D\41", IF_386|IF_SM}, /* 954*/ {I_SUB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, /* 955*/ {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, /* 956*/ {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, /* 957*/ {I_SUB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, /* 958*/ {I_SUB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, /* 959*/ {I_SUB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, /* 960*/ {I_SVDC, 2, {MEMORY|BITS80,REG_SREG,0}, "\300\2\x0F\x78\101", IF_486|IF_CYRIX|IF_SMM}, /* 961*/ {I_SVLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7A\200", IF_486|IF_CYRIX|IF_SMM}, /* 962*/ {I_SVTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7C\200", IF_486|IF_CYRIX|IF_SMM}, /* 963*/ {I_SYSCALL, 0, {0,0,0}, "\2\x0F\x05", IF_P6|IF_AMD}, /* 964*/ {I_SYSENTER, 0, {0,0,0}, "\2\x0F\x34", IF_P6}, /* 965*/ {I_SYSEXIT, 0, {0,0,0}, "\2\x0F\x35", IF_P6|IF_PRIV}, /* 966*/ {I_SYSRET, 0, {0,0,0}, "\2\x0F\x07", IF_P6|IF_PRIV|IF_AMD}, /* 967*/ {I_TEST, 2, {MEMORY,REG8,0}, "\300\1\x84\101", IF_8086|IF_SM}, /* 968*/ {I_TEST, 2, {REG8,REG8,0}, "\1\x84\101", IF_8086}, /* 969*/ {I_TEST, 2, {MEMORY,REG16,0}, "\320\300\1\x85\101", IF_8086|IF_SM}, /* 970*/ {I_TEST, 2, {REG16,REG16,0}, "\320\1\x85\101", IF_8086}, /* 971*/ {I_TEST, 2, {MEMORY,REG32,0}, "\321\300\1\x85\101", IF_386|IF_SM}, /* 972*/ {I_TEST, 2, {REG32,REG32,0}, "\321\1\x85\101", IF_386}, /* 973*/ {I_TEST, 2, {REG8,MEMORY,0}, "\301\1\x84\110", IF_8086|IF_SM}, /* 974*/ {I_TEST, 2, {REG16,MEMORY,0}, "\320\301\1\x85\110", IF_8086|IF_SM}, /* 975*/ {I_TEST, 2, {REG32,MEMORY,0}, "\321\301\1\x85\110", IF_386|IF_SM}, /* 976*/ {I_TEST, 2, {REG_AL,IMMEDIATE,0}, "\1\xA8\21", IF_8086|IF_SM}, /* 977*/ {I_TEST, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xA9\31", IF_8086|IF_SM}, /* 978*/ {I_TEST, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xA9\41", IF_386|IF_SM}, /* 979*/ {I_TEST, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, /* 980*/ {I_TEST, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, /* 981*/ {I_TEST, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, /* 982*/ {I_TEST, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, /* 983*/ {I_TEST, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, /* 984*/ {I_TEST, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, /* 985*/ {I_UD0, 0, {0,0,0}, "\2\x0F\xFF", IF_286|IF_UNDOC}, /* 986*/ {I_UD1, 0, {0,0,0}, "\2\x0F\xB9", IF_286|IF_UNDOC}, /* 987*/ {I_UD2, 0, {0,0,0}, "\2\x0F\x0B", IF_286}, /* 988*/ {I_UMOV, 2, {MEMORY,REG8,0}, "\300\2\x0F\x10\101", IF_386|IF_UNDOC|IF_SM}, /* 989*/ {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x10\101", IF_386|IF_UNDOC}, /* 990*/ {I_UMOV, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, /* 991*/ {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x11\101", IF_386|IF_UNDOC}, /* 992*/ {I_UMOV, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, /* 993*/ {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x11\101", IF_386|IF_UNDOC}, /* 994*/ {I_UMOV, 2, {REG8,MEMORY,0}, "\301\2\x0F\x12\110", IF_386|IF_UNDOC|IF_SM}, /* 995*/ {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x12\110", IF_386|IF_UNDOC}, /* 996*/ {I_UMOV, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, /* 997*/ {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x13\110", IF_386|IF_UNDOC}, /* 998*/ {I_UMOV, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, /* 999*/ {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x13\110", IF_386|IF_UNDOC}, /*1000*/ {I_VERR, 1, {MEMORY,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, /*1001*/ {I_VERR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, /*1002*/ {I_VERR, 1, {REG16,0,0}, "\1\x0F\17\204", IF_286|IF_PROT}, /*1003*/ {I_VERW, 1, {MEMORY,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, /*1004*/ {I_VERW, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, /*1005*/ {I_VERW, 1, {REG16,0,0}, "\1\x0F\17\205", IF_286|IF_PROT}, /*1006*/ {I_WAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, /*1007*/ {I_FWAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, /*1008*/ {I_WBINVD, 0, {0,0,0}, "\2\x0F\x09", IF_486|IF_PRIV}, /*1009*/ {I_WRSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x37\200", IF_P6|IF_CYRIX|IF_SMM}, /*1010*/ {I_WRMSR, 0, {0,0,0}, "\2\x0F\x30", IF_PENT|IF_PRIV}, /*1011*/ {I_XADD, 2, {MEMORY,REG8,0}, "\300\2\x0F\xC0\101", IF_486|IF_SM}, /*1012*/ {I_XADD, 2, {REG8,REG8,0}, "\2\x0F\xC0\101", IF_486}, /*1013*/ {I_XADD, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xC1\101", IF_486|IF_SM}, /*1014*/ {I_XADD, 2, {REG16,REG16,0}, "\320\2\x0F\xC1\101", IF_486}, /*1015*/ {I_XADD, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xC1\101", IF_486|IF_SM}, /*1016*/ {I_XADD, 2, {REG32,REG32,0}, "\321\2\x0F\xC1\101", IF_486}, /*1017*/ {I_XCHG, 2, {REG_AX,REG16,0}, "\320\11\x90", IF_8086}, /*1018*/ {I_XCHG, 2, {REG_EAX,REG32,0}, "\321\11\x90", IF_386}, /*1019*/ {I_XCHG, 2, {REG16,REG_AX,0}, "\320\10\x90", IF_8086}, /*1020*/ {I_XCHG, 2, {REG32,REG_EAX,0}, "\321\10\x90", IF_386}, /*1021*/ {I_XCHG, 2, {REG8,MEMORY,0}, "\301\1\x86\110", IF_8086|IF_SM}, /*1022*/ {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\110", IF_8086}, /*1023*/ {I_XCHG, 2, {REG16,MEMORY,0}, "\320\301\1\x87\110", IF_8086|IF_SM}, /*1024*/ {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\110", IF_8086}, /*1025*/ {I_XCHG, 2, {REG32,MEMORY,0}, "\321\301\1\x87\110", IF_386|IF_SM}, /*1026*/ {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\110", IF_386}, /*1027*/ {I_XCHG, 2, {MEMORY,REG8,0}, "\300\1\x86\101", IF_8086|IF_SM}, /*1028*/ {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\101", IF_8086}, /*1029*/ {I_XCHG, 2, {MEMORY,REG16,0}, "\320\300\1\x87\101", IF_8086|IF_SM}, /*1030*/ {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\101", IF_8086}, /*1031*/ {I_XCHG, 2, {MEMORY,REG32,0}, "\321\300\1\x87\101", IF_386|IF_SM}, /*1032*/ {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\101", IF_386}, /*1033*/ {I_XLATB, 0, {0,0,0}, "\1\xD7", IF_8086}, /*1034*/ {I_XLAT, 0, {0,0,0}, "\1\xD7", IF_8086}, /*1035*/ {I_XOR, 2, {MEMORY,REG8,0}, "\300\1\x30\101", IF_8086|IF_SM}, /*1036*/ {I_XOR, 2, {REG8,REG8,0}, "\1\x30\101", IF_8086}, /*1037*/ {I_XOR, 2, {MEMORY,REG16,0}, "\320\300\1\x31\101", IF_8086|IF_SM}, /*1038*/ {I_XOR, 2, {REG16,REG16,0}, "\320\1\x31\101", IF_8086}, /*1039*/ {I_XOR, 2, {MEMORY,REG32,0}, "\321\300\1\x31\101", IF_386|IF_SM}, /*1040*/ {I_XOR, 2, {REG32,REG32,0}, "\321\1\x31\101", IF_386}, /*1041*/ {I_XOR, 2, {REG8,MEMORY,0}, "\301\1\x32\110", IF_8086|IF_SM}, /*1042*/ {I_XOR, 2, {REG8,REG8,0}, "\1\x32\110", IF_8086}, /*1043*/ {I_XOR, 2, {REG16,MEMORY,0}, "\320\301\1\x33\110", IF_8086|IF_SM}, /*1044*/ {I_XOR, 2, {REG16,REG16,0}, "\320\1\x33\110", IF_8086}, /*1045*/ {I_XOR, 2, {REG32,MEMORY,0}, "\321\301\1\x33\110", IF_386|IF_SM}, /*1046*/ {I_XOR, 2, {REG32,REG32,0}, "\321\1\x33\110", IF_386}, /*1047*/ {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\206\15", IF_8086}, /*1048*/ {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\206\15", IF_386}, /*1049*/ {I_XOR, 2, {REG_AL,IMMEDIATE,0}, "\1\x34\21", IF_8086|IF_SM}, /*1050*/ {I_XOR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x35\31", IF_8086|IF_SM}, /*1051*/ {I_XOR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x35\41", IF_386|IF_SM}, /*1052*/ {I_XOR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, /*1053*/ {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, /*1054*/ {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, /*1055*/ {I_XOR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, /*1056*/ {I_XOR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, /*1057*/ {I_XOR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, /*1058*/ {I_XSTORE, 0, {0,0,0}, "\3\x0F\xA7\xC0", IF_P6|IF_CYRIX}, /*1059*/ {I_CMOVcc, 2, {REG16,MEMORY,0}, "\320\301\1\x0F\330\x40\110", IF_P6|IF_SM}, /*1060*/ {I_CMOVcc, 2, {REG16,REG16,0}, "\320\1\x0F\330\x40\110", IF_P6}, /*1061*/ {I_CMOVcc, 2, {REG32,MEMORY,0}, "\321\301\1\x0F\330\x40\110", IF_P6|IF_SM}, /*1062*/ {I_CMOVcc, 2, {REG32,REG32,0}, "\321\1\x0F\330\x40\110", IF_P6}, /*1063*/ {I_Jcc, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\x0F\330\x80\64", IF_386}, /*1064*/ {I_Jcc, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\x0F\330\x80\64", IF_386}, /*1065*/ {I_Jcc, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\x0F\330\x80\64", IF_386}, /*1066*/ {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x70\50", IF_8086}, /*1067*/ {I_SETcc, 1, {MEMORY,0,0}, "\300\1\x0F\330\x90\200", IF_386|IF_SB}, /*1068*/ {I_SETcc, 1, {REG8,0,0}, "\300\1\x0F\330\x90\200", IF_386}, /*1069*/ {I_ADDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, /*1070*/ {I_ADDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, /*1071*/ {I_ADDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, /*1072*/ {I_ADDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, /*1073*/ {I_ANDNPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_KATMAI|IF_SSE}, /*1074*/ {I_ANDNPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x55\110", IF_KATMAI|IF_SSE}, /*1075*/ {I_ANDPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_KATMAI|IF_SSE}, /*1076*/ {I_ANDPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x54\110", IF_KATMAI|IF_SSE}, /*1077*/ {I_CMPEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, /*1078*/ {I_CMPEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, /*1079*/ {I_CMPEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, /*1080*/ {I_CMPEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, /*1081*/ {I_CMPLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, /*1082*/ {I_CMPLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, /*1083*/ {I_CMPLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, /*1084*/ {I_CMPLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, /*1085*/ {I_CMPLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, /*1086*/ {I_CMPLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, /*1087*/ {I_CMPLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, /*1088*/ {I_CMPLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, /*1089*/ {I_CMPNEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, /*1090*/ {I_CMPNEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, /*1091*/ {I_CMPNEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, /*1092*/ {I_CMPNEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, /*1093*/ {I_CMPNLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, /*1094*/ {I_CMPNLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, /*1095*/ {I_CMPNLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, /*1096*/ {I_CMPNLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, /*1097*/ {I_CMPNLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, /*1098*/ {I_CMPNLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, /*1099*/ {I_CMPNLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, /*1100*/ {I_CMPNLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, /*1101*/ {I_CMPORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, /*1102*/ {I_CMPORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, /*1103*/ {I_CMPORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, /*1104*/ {I_CMPORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, /*1105*/ {I_CMPUNORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, /*1106*/ {I_CMPUNORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, /*1107*/ {I_CMPUNORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, /*1108*/ {I_CMPUNORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, /*1109*/ {I_CMPPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1110*/ {I_CMPPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1111*/ {I_CMPSS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1112*/ {I_CMPSS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1113*/ {I_COMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, /*1114*/ {I_COMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, /*1115*/ {I_CVTPI2PS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1116*/ {I_CVTPI2PS, 2, {XMMREG,MMXREG,0}, "\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1117*/ {I_CVTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1118*/ {I_CVTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1119*/ {I_CVTSI2SS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_SD|IF_AR1}, /*1120*/ {I_CVTSI2SS, 2, {XMMREG,REG32,0}, "\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE}, /*1121*/ {I_CVTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, /*1122*/ {I_CVTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, /*1123*/ {I_CVTTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1124*/ {I_CVTTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, /*1125*/ {I_CVTTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, /*1126*/ {I_CVTTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, /*1127*/ {I_DIVPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, /*1128*/ {I_DIVPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, /*1129*/ {I_DIVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, /*1130*/ {I_DIVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, /*1131*/ {I_LDMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\202", IF_KATMAI|IF_SSE|IF_SD}, /*1132*/ {I_MAXPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, /*1133*/ {I_MAXPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, /*1134*/ {I_MAXSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, /*1135*/ {I_MAXSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, /*1136*/ {I_MINPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, /*1137*/ {I_MINPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, /*1138*/ {I_MINSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, /*1139*/ {I_MINSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, /*1140*/ {I_MOVAPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x28\110", IF_KATMAI|IF_SSE}, /*1141*/ {I_MOVAPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x29\101", IF_KATMAI|IF_SSE}, /*1142*/ {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x28\110", IF_KATMAI|IF_SSE}, /*1143*/ {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x29\101", IF_KATMAI|IF_SSE}, /*1144*/ {I_MOVHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x16\110", IF_KATMAI|IF_SSE}, /*1145*/ {I_MOVHPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x17\101", IF_KATMAI|IF_SSE}, /*1146*/ {I_MOVLHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x16\110", IF_KATMAI|IF_SSE}, /*1147*/ {I_MOVLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x12\110", IF_KATMAI|IF_SSE}, /*1148*/ {I_MOVLPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x13\101", IF_KATMAI|IF_SSE}, /*1149*/ {I_MOVHLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x12\110", IF_KATMAI|IF_SSE}, /*1150*/ {I_MOVMSKPS, 2, {REG32,XMMREG,0}, "\2\x0F\x50\110", IF_KATMAI|IF_SSE}, /*1151*/ {I_MOVNTPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x2B\101", IF_KATMAI|IF_SSE}, /*1152*/ {I_MOVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, /*1153*/ {I_MOVSS, 2, {MEMORY,XMMREG,0}, "\300\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, /*1154*/ {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, /*1155*/ {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, /*1156*/ {I_MOVUPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, /*1157*/ {I_MOVUPS, 2, {MEMORY,XMMREG,0}, "\300\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, /*1158*/ {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, /*1159*/ {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, /*1160*/ {I_MULPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_KATMAI|IF_SSE}, /*1161*/ {I_MULPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x59\110", IF_KATMAI|IF_SSE}, /*1162*/ {I_MULSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, /*1163*/ {I_MULSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, /*1164*/ {I_ORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x56\110", IF_KATMAI|IF_SSE}, /*1165*/ {I_ORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x56\110", IF_KATMAI|IF_SSE}, /*1166*/ {I_RCPPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, /*1167*/ {I_RCPPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, /*1168*/ {I_RCPSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, /*1169*/ {I_RCPSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, /*1170*/ {I_RSQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, /*1171*/ {I_RSQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, /*1172*/ {I_RSQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, /*1173*/ {I_RSQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, /*1174*/ {I_SHUFPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1175*/ {I_SHUFPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, /*1176*/ {I_SQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, /*1177*/ {I_SQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, /*1178*/ {I_SQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, /*1179*/ {I_SQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, /*1180*/ {I_STMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\203", IF_KATMAI|IF_SSE|IF_SD}, /*1181*/ {I_SUBPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, /*1182*/ {I_SUBPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, /*1183*/ {I_SUBSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, /*1184*/ {I_SUBSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, /*1185*/ {I_UCOMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, /*1186*/ {I_UCOMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, /*1187*/ {I_UNPCKHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x15\110", IF_KATMAI|IF_SSE}, /*1188*/ {I_UNPCKHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x15\110", IF_KATMAI|IF_SSE}, /*1189*/ {I_UNPCKLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x14\110", IF_KATMAI|IF_SSE}, /*1190*/ {I_UNPCKLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x14\110", IF_KATMAI|IF_SSE}, /*1191*/ {I_XORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x57\110", IF_KATMAI|IF_SSE}, /*1192*/ {I_XORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x57\110", IF_KATMAI|IF_SSE}, /*1193*/ {I_FXRSTOR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\201", IF_P6|IF_SSE|IF_FPU}, /*1194*/ {I_FXSAVE, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\200", IF_P6|IF_SSE|IF_FPU}, /*1195*/ {I_PREFETCHNTA, 1, {MEMORY,0,0}, "\300\2\x0F\x18\200", IF_KATMAI}, /*1196*/ {I_PREFETCHT0, 1, {MEMORY,0,0}, "\300\2\x0F\x18\201", IF_KATMAI}, /*1197*/ {I_PREFETCHT1, 1, {MEMORY,0,0}, "\300\2\x0F\x18\202", IF_KATMAI}, /*1198*/ {I_PREFETCHT2, 1, {MEMORY,0,0}, "\300\2\x0F\x18\203", IF_KATMAI}, /*1199*/ {I_SFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF8", IF_KATMAI}, /*1200*/ {I_MASKMOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF7\110", IF_KATMAI|IF_MMX}, /*1201*/ {I_MOVNTQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\xE7\101", IF_KATMAI|IF_MMX|IF_SM}, /*1202*/ {I_PAVGB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE0\110", IF_KATMAI|IF_MMX}, /*1203*/ {I_PAVGB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE0\110", IF_KATMAI|IF_MMX|IF_SM}, /*1204*/ {I_PAVGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE3\110", IF_KATMAI|IF_MMX}, /*1205*/ {I_PAVGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE3\110", IF_KATMAI|IF_MMX|IF_SM}, /*1206*/ {I_PEXTRW, 3, {REG32,MMXREG,IMMEDIATE}, "\2\x0F\xC5\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, /*1207*/ {I_PINSRW, 3, {MMXREG,REG16,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, /*1208*/ {I_PINSRW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, /*1209*/ {I_PMAXSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEE\110", IF_KATMAI|IF_MMX}, /*1210*/ {I_PMAXSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEE\110", IF_KATMAI|IF_MMX|IF_SM}, /*1211*/ {I_PMAXUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDE\110", IF_KATMAI|IF_MMX}, /*1212*/ {I_PMAXUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDE\110", IF_KATMAI|IF_MMX|IF_SM}, /*1213*/ {I_PMINSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEA\110", IF_KATMAI|IF_MMX}, /*1214*/ {I_PMINSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEA\110", IF_KATMAI|IF_MMX|IF_SM}, /*1215*/ {I_PMINUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDA\110", IF_KATMAI|IF_MMX}, /*1216*/ {I_PMINUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDA\110", IF_KATMAI|IF_MMX|IF_SM}, /*1217*/ {I_PMOVMSKB, 2, {REG32,MMXREG,0}, "\2\x0F\xD7\110", IF_KATMAI|IF_MMX}, /*1218*/ {I_PMULHUW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE4\110", IF_KATMAI|IF_MMX}, /*1219*/ {I_PMULHUW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE4\110", IF_KATMAI|IF_MMX|IF_SM}, /*1220*/ {I_PSADBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF6\110", IF_KATMAI|IF_MMX}, /*1221*/ {I_PSADBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF6\110", IF_KATMAI|IF_MMX|IF_SM}, /*1222*/ {I_PSHUFW, 3, {MMXREG,MMXREG,IMMEDIATE}, "\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, /*1223*/ {I_PSHUFW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SM2|IF_SB|IF_AR2}, /*1224*/ {I_PF2IW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW|IF_SM}, /*1225*/ {I_PF2IW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW}, /*1226*/ {I_PFNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW|IF_SM}, /*1227*/ {I_PFNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW}, /*1228*/ {I_PFPNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW|IF_SM}, /*1229*/ {I_PFPNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW}, /*1230*/ {I_PI2FW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW|IF_SM}, /*1231*/ {I_PI2FW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW}, /*1232*/ {I_PSWAPD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW|IF_SM}, /*1233*/ {I_PSWAPD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW}, /*1234*/ {I_MASKMOVDQU, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF7\110", IF_WILLAMETTE|IF_SSE2}, /*1235*/ {I_CLFLUSH, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\207", IF_WILLAMETTE|IF_SSE2}, /*1236*/ {I_MOVNTDQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xE7\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1237*/ {I_MOVNTI, 2, {MEMORY,REG32,0}, "\300\2\x0F\xC3\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1238*/ {I_MOVNTPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x2B\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1239*/ {I_PAUSE, 0, {0,0,0}, "\333\1\x90", IF_WILLAMETTE|IF_SSE2}, /*1240*/ {I_LFENCE, 0, {0,0,0}, "\3\x0F\xAE\xE8", IF_WILLAMETTE|IF_SSE2}, /*1241*/ {I_MFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF0", IF_WILLAMETTE|IF_SSE2}, /*1242*/ {I_MOVD, 2, {XMMREG,REG32,0}, "\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, /*1243*/ {I_MOVD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, /*1244*/ {I_MOVD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, /*1245*/ {I_MOVD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, /*1246*/ {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, /*1247*/ {I_MOVDQA, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1248*/ {I_MOVDQA, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1249*/ {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, /*1250*/ {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, /*1251*/ {I_MOVDQU, 2, {MEMORY,XMMREG,0}, "\333\300\2\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1252*/ {I_MOVDQU, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1253*/ {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, /*1254*/ {I_MOVDQ2Q, 2, {MMXREG,XMMREG,0}, "\3\xF2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, /*1255*/ {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, /*1256*/ {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, /*1257*/ {I_MOVQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xD6\101", IF_WILLAMETTE|IF_SSE2}, /*1258*/ {I_MOVQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, /*1259*/ {I_MOVQ2DQ, 2, {XMMREG,MMXREG,0}, "\333\2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, /*1260*/ {I_PACKSSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2}, /*1261*/ {I_PACKSSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1262*/ {I_PACKSSDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2}, /*1263*/ {I_PACKSSDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1264*/ {I_PACKUSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2}, /*1265*/ {I_PACKUSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1266*/ {I_PADDB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2}, /*1267*/ {I_PADDB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1268*/ {I_PADDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2}, /*1269*/ {I_PADDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1270*/ {I_PADDD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2}, /*1271*/ {I_PADDD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1272*/ {I_PADDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, /*1273*/ {I_PADDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1274*/ {I_PADDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, /*1275*/ {I_PADDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1276*/ {I_PADDSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1277*/ {I_PADDSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2}, /*1278*/ {I_PADDSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1279*/ {I_PADDSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2}, /*1280*/ {I_PADDUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1281*/ {I_PADDUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2}, /*1282*/ {I_PADDUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1283*/ {I_PADDUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2}, /*1284*/ {I_PAND, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2}, /*1285*/ {I_PAND, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1286*/ {I_PANDN, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2}, /*1287*/ {I_PANDN, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1288*/ {I_PAVGB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2}, /*1289*/ {I_PAVGB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1290*/ {I_PAVGW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2}, /*1291*/ {I_PAVGW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1292*/ {I_PCMPEQB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2}, /*1293*/ {I_PCMPEQB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1294*/ {I_PCMPEQW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2}, /*1295*/ {I_PCMPEQW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1296*/ {I_PCMPEQD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2}, /*1297*/ {I_PCMPEQD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1298*/ {I_PCMPGTB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2}, /*1299*/ {I_PCMPGTB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1300*/ {I_PCMPGTW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2}, /*1301*/ {I_PCMPGTW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1302*/ {I_PCMPGTD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2}, /*1303*/ {I_PCMPGTD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1304*/ {I_PEXTRW, 3, {REG32,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC5\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1305*/ {I_PINSRW, 3, {XMMREG,REG16,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1306*/ {I_PINSRW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1307*/ {I_PMADDWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1308*/ {I_PMADDWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2}, /*1309*/ {I_PMAXSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2}, /*1310*/ {I_PMAXSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1311*/ {I_PMAXUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2}, /*1312*/ {I_PMAXUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1313*/ {I_PMINSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2}, /*1314*/ {I_PMINSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1315*/ {I_PMINUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2}, /*1316*/ {I_PMINUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1317*/ {I_PMOVMSKB, 2, {REG32,XMMREG,0}, "\3\x66\x0F\xD7\110", IF_WILLAMETTE|IF_SSE2}, /*1318*/ {I_PMULHUW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2}, /*1319*/ {I_PMULHUW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1320*/ {I_PMULHW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1321*/ {I_PMULHW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2}, /*1322*/ {I_PMULLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1323*/ {I_PMULLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2}, /*1324*/ {I_PMULUDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, /*1325*/ {I_PMULUDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1326*/ {I_PMULUDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, /*1327*/ {I_PMULUDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1328*/ {I_POR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1329*/ {I_POR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2}, /*1330*/ {I_PSADBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2}, /*1331*/ {I_PSADBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1332*/ {I_PSHUFD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1333*/ {I_PSHUFD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, /*1334*/ {I_PSHUFHW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1335*/ {I_PSHUFHW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, /*1336*/ {I_PSHUFLW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1337*/ {I_PSHUFLW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, /*1338*/ {I_PSLLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\207\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1339*/ {I_PSLLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1340*/ {I_PSLLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2}, /*1341*/ {I_PSLLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1342*/ {I_PSLLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1343*/ {I_PSLLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2}, /*1344*/ {I_PSLLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1345*/ {I_PSLLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1346*/ {I_PSLLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2}, /*1347*/ {I_PSLLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1348*/ {I_PSRAW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1349*/ {I_PSRAW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2}, /*1350*/ {I_PSRAW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1351*/ {I_PSRAD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1352*/ {I_PSRAD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2}, /*1353*/ {I_PSRAD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1354*/ {I_PSRLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\203\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1355*/ {I_PSRLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1356*/ {I_PSRLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2}, /*1357*/ {I_PSRLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1358*/ {I_PSRLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1359*/ {I_PSRLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2}, /*1360*/ {I_PSRLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1361*/ {I_PSRLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1362*/ {I_PSRLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2}, /*1363*/ {I_PSRLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, /*1364*/ {I_PSUBB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1365*/ {I_PSUBB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2}, /*1366*/ {I_PSUBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1367*/ {I_PSUBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2}, /*1368*/ {I_PSUBD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1369*/ {I_PSUBD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2}, /*1370*/ {I_PSUBQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, /*1371*/ {I_PSUBQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1372*/ {I_PSUBQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, /*1373*/ {I_PSUBQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1374*/ {I_PSUBSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1375*/ {I_PSUBSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2}, /*1376*/ {I_PSUBSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1377*/ {I_PSUBSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2}, /*1378*/ {I_PSUBUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1379*/ {I_PSUBUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2}, /*1380*/ {I_PSUBUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1381*/ {I_PSUBUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2}, /*1382*/ {I_PUNPCKHBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1383*/ {I_PUNPCKHBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2}, /*1384*/ {I_PUNPCKHWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1385*/ {I_PUNPCKHWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2}, /*1386*/ {I_PUNPCKHDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1387*/ {I_PUNPCKHDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2}, /*1388*/ {I_PUNPCKHQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2}, /*1389*/ {I_PUNPCKHQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1390*/ {I_PUNPCKLBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1391*/ {I_PUNPCKLBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2}, /*1392*/ {I_PUNPCKLWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1393*/ {I_PUNPCKLWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2}, /*1394*/ {I_PUNPCKLDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1395*/ {I_PUNPCKLDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2}, /*1396*/ {I_PUNPCKLQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2}, /*1397*/ {I_PUNPCKLQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1398*/ {I_PXOR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1399*/ {I_PXOR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2}, /*1400*/ {I_ADDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, /*1401*/ {I_ADDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1402*/ {I_ADDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, /*1403*/ {I_ADDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, /*1404*/ {I_ANDNPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2}, /*1405*/ {I_ANDNPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1406*/ {I_ANDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2}, /*1407*/ {I_ANDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1408*/ {I_CMPEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1409*/ {I_CMPEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, /*1410*/ {I_CMPEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, /*1411*/ {I_CMPEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, /*1412*/ {I_CMPLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1413*/ {I_CMPLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, /*1414*/ {I_CMPLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, /*1415*/ {I_CMPLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, /*1416*/ {I_CMPLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1417*/ {I_CMPLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, /*1418*/ {I_CMPLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, /*1419*/ {I_CMPLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, /*1420*/ {I_CMPNEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1421*/ {I_CMPNEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, /*1422*/ {I_CMPNEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, /*1423*/ {I_CMPNEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, /*1424*/ {I_CMPNLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1425*/ {I_CMPNLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, /*1426*/ {I_CMPNLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, /*1427*/ {I_CMPNLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, /*1428*/ {I_CMPNLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1429*/ {I_CMPNLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, /*1430*/ {I_CMPNLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, /*1431*/ {I_CMPNLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, /*1432*/ {I_CMPORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1433*/ {I_CMPORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, /*1434*/ {I_CMPORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, /*1435*/ {I_CMPORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, /*1436*/ {I_CMPUNORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1437*/ {I_CMPUNORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, /*1438*/ {I_CMPUNORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, /*1439*/ {I_CMPUNORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, /*1440*/ {I_CMPPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1441*/ {I_CMPPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, /*1442*/ {I_CMPSD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1443*/ {I_CMPSD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1444*/ {I_COMISD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, /*1445*/ {I_COMISD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, /*1446*/ {I_CVTDQ2PD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, /*1447*/ {I_CVTDQ2PD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, /*1448*/ {I_CVTDQ2PS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, /*1449*/ {I_CVTDQ2PS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1450*/ {I_CVTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, /*1451*/ {I_CVTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1452*/ {I_CVTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, /*1453*/ {I_CVTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, /*1454*/ {I_CVTPD2PS, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1455*/ {I_CVTPD2PS, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1456*/ {I_CVTPI2PD, 2, {XMMREG,MMXREG,0}, "\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, /*1457*/ {I_CVTPI2PD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, /*1458*/ {I_CVTPS2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, /*1459*/ {I_CVTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1460*/ {I_CVTPS2PD, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1461*/ {I_CVTPS2PD, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1462*/ {I_CVTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, /*1463*/ {I_CVTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, /*1464*/ {I_CVTSD2SS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1465*/ {I_CVTSD2SS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1466*/ {I_CVTSI2SD, 2, {XMMREG,REG32,0}, "\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, /*1467*/ {I_CVTSI2SD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, /*1468*/ {I_CVTSS2SD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1469*/ {I_CVTSS2SD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, /*1470*/ {I_CVTTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, /*1471*/ {I_CVTTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, /*1472*/ {I_CVTTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, /*1473*/ {I_CVTTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1474*/ {I_CVTTPS2DQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, /*1475*/ {I_CVTTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1476*/ {I_CVTTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, /*1477*/ {I_CVTTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, /*1478*/ {I_DIVPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, /*1479*/ {I_DIVPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1480*/ {I_DIVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, /*1481*/ {I_DIVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, /*1482*/ {I_MAXPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, /*1483*/ {I_MAXPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1484*/ {I_MAXSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, /*1485*/ {I_MAXSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, /*1486*/ {I_MINPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, /*1487*/ {I_MINPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1488*/ {I_MINSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, /*1489*/ {I_MINSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, /*1490*/ {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2}, /*1491*/ {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x29\110", IF_WILLAMETTE|IF_SSE2}, /*1492*/ {I_MOVAPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x29\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1493*/ {I_MOVAPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1494*/ {I_MOVHPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x17\101", IF_WILLAMETTE|IF_SSE2}, /*1495*/ {I_MOVHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x16\110", IF_WILLAMETTE|IF_SSE2}, /*1496*/ {I_MOVLPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x13\101", IF_WILLAMETTE|IF_SSE2}, /*1497*/ {I_MOVLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x12\110", IF_WILLAMETTE|IF_SSE2}, /*1498*/ {I_MOVMSKPD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x50\110", IF_WILLAMETTE|IF_SSE2}, /*1499*/ {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, /*1500*/ {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, /*1501*/ {I_MOVSD, 2, {MEMORY,XMMREG,0}, "\300\3\xF2\x0F\x11\101", IF_WILLAMETTE|IF_SSE2}, /*1502*/ {I_MOVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, /*1503*/ {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, /*1504*/ {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, /*1505*/ {I_MOVUPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x11\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1506*/ {I_MOVUPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1507*/ {I_MULPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, /*1508*/ {I_MULPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1509*/ {I_MULSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, /*1510*/ {I_MULSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, /*1511*/ {I_ORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1512*/ {I_ORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2}, /*1513*/ {I_SHUFPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, /*1514*/ {I_SHUFPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM|IF_SB|IF_AR2}, /*1515*/ {I_SQRTPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, /*1516*/ {I_SQRTPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1517*/ {I_SQRTSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, /*1518*/ {I_SQRTSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, /*1519*/ {I_SUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, /*1520*/ {I_SUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1521*/ {I_SUBSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, /*1522*/ {I_SUBSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, /*1523*/ {I_UCOMISD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, /*1524*/ {I_UCOMISD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, /*1525*/ {I_UNPCKHPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2}, /*1526*/ {I_UNPCKHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1527*/ {I_UNPCKLPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2}, /*1528*/ {I_UNPCKLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1529*/ {I_XORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2}, /*1530*/ {I_XORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, /*1531*/ {I_ADDSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1532*/ {I_ADDSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, /*1533*/ {I_ADDSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1534*/ {I_ADDSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, /*1535*/ {I_HADDPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1536*/ {I_HADDPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, /*1537*/ {I_HADDPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1538*/ {I_HADDPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, /*1539*/ {I_HSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1540*/ {I_HSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, /*1541*/ {I_HSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, /*1542*/ {I_HSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, /*1543*/ {I_LDDQU, 2, {XMMREG,MEMORY,0}, "\3\xF2\x0F\xF0\110", IF_PRESCOTT|IF_SSE3}, /*1544*/ {I_MOVDDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, /*1545*/ {I_MOVDDUP, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, /*1546*/ {I_MOVSHDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, /*1547*/ {I_MOVSHDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, /*1548*/ {I_MOVSLDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, /*1549*/ {I_MOVSLDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, /*1550 23*/ {I_ADC, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\202\21", IF_8086|IF_SM}, /*1551 26*/ {I_ADC, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\202\21", IF_8086|IF_SM}, /*1552 46*/ {I_ADD, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\200\21", IF_8086|IF_SM}, /*1553 49*/ {I_ADD, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\200\21", IF_8086|IF_SM}, /*1554 69*/ {I_AND, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\204\21", IF_8086|IF_SM}, /*1555 72*/ {I_AND, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\204\21", IF_8086|IF_SM}, /*1556 158*/ {I_CMP, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\207\21", IF_8086|IF_SM}, /*1557 161*/ {I_CMP, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\207\21", IF_8086|IF_SM}, /*1558 595*/ {I_OR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\201\21", IF_8086|IF_SM}, /*1559 598*/ {I_OR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\201\21", IF_8086|IF_SM}, /*1560 872*/ {I_SBB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\203\21", IF_8086|IF_SM}, /*1561 875*/ {I_SBB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\203\21", IF_8086|IF_SM}, /*1562 954*/ {I_SUB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\205\21", IF_8086|IF_SM}, /*1563 957*/ {I_SUB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\205\21", IF_8086|IF_SM}, /*1564 1052*/ {I_XOR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x82\206\21", IF_8086|IF_SM}, /*1565 1055*/ {I_XOR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x82\206\21", IF_8086|IF_SM}, /*1566*/ ITEMPLATE_END }; static struct itemplate *itable_00[] = { instrux + 29, instrux + 30, NULL }; static struct itemplate *itable_01[] = { instrux + 31, instrux + 32, instrux + 33, instrux + 34, NULL }; static struct itemplate *itable_02[] = { instrux + 35, instrux + 36, NULL }; static struct itemplate *itable_03[] = { instrux + 37, instrux + 38, instrux + 39, instrux + 40, NULL }; static struct itemplate *itable_04[] = { instrux + 43, NULL }; static struct itemplate *itable_05[] = { instrux + 44, instrux + 45, NULL }; static struct itemplate *itable_06[] = { instrux + 778, instrux + 779, NULL }; static struct itemplate *itable_07[] = { instrux + 710, NULL }; static struct itemplate *itable_08[] = { instrux + 578, instrux + 579, NULL }; static struct itemplate *itable_09[] = { instrux + 580, instrux + 581, instrux + 582, instrux + 583, NULL }; static struct itemplate *itable_0A[] = { instrux + 584, instrux + 585, NULL }; static struct itemplate *itable_0B[] = { instrux + 586, instrux + 587, instrux + 588, instrux + 589, NULL }; static struct itemplate *itable_0C[] = { instrux + 592, NULL }; static struct itemplate *itable_0D[] = { instrux + 593, instrux + 594, NULL }; static struct itemplate *itable_0E[] = { instrux + 778, instrux + 779, NULL }; static struct itemplate *itable_0F[] = { instrux + 79, instrux + 80, instrux + 81, instrux + 82, instrux + 83, instrux + 84, instrux + 85, instrux + 86, instrux + 87, instrux + 88, instrux + 89, instrux + 90, instrux + 91, instrux + 92, instrux + 93, instrux + 94, instrux + 95, instrux + 96, instrux + 97, instrux + 98, instrux + 99, instrux + 100, instrux + 101, instrux + 102, instrux + 103, instrux + 104, instrux + 105, instrux + 106, instrux + 107, instrux + 108, instrux + 109, instrux + 110, instrux + 111, instrux + 139, instrux + 167, instrux + 168, instrux + 169, instrux + 170, instrux + 171, instrux + 172, instrux + 173, instrux + 174, instrux + 175, instrux + 176, instrux + 177, instrux + 178, instrux + 179, instrux + 180, instrux + 193, instrux + 261, instrux + 389, instrux + 390, instrux + 391, instrux + 392, instrux + 423, instrux + 424, instrux + 450, instrux + 451, instrux + 452, instrux + 453, instrux + 454, instrux + 456, instrux + 457, instrux + 458, instrux + 459, instrux + 467, instrux + 468, instrux + 469, instrux + 470, instrux + 471, instrux + 472, instrux + 473, instrux + 474, instrux + 475, instrux + 476, instrux + 477, instrux + 478, instrux + 479, instrux + 480, instrux + 499, instrux + 500, instrux + 501, instrux + 502, instrux + 503, instrux + 504, instrux + 505, instrux + 506, instrux + 507, instrux + 508, instrux + 521, instrux + 522, instrux + 523, instrux + 524, instrux + 525, instrux + 526, instrux + 548, instrux + 549, instrux + 550, instrux + 551, instrux + 552, instrux + 553, instrux + 554, instrux + 555, instrux + 559, instrux + 560, instrux + 561, instrux + 562, instrux + 563, instrux + 564, instrux + 565, instrux + 566, instrux + 570, instrux + 610, instrux + 611, instrux + 612, instrux + 613, instrux + 614, instrux + 615, instrux + 616, instrux + 617, instrux + 618, instrux + 619, instrux + 620, instrux + 621, instrux + 622, instrux + 623, instrux + 624, instrux + 625, instrux + 626, instrux + 627, instrux + 628, instrux + 629, instrux + 630, instrux + 631, instrux + 632, instrux + 633, instrux + 634, instrux + 635, instrux + 636, instrux + 637, instrux + 638, instrux + 639, instrux + 640, instrux + 641, instrux + 642, instrux + 643, instrux + 644, instrux + 645, instrux + 646, instrux + 647, instrux + 648, instrux + 649, instrux + 650, instrux + 651, instrux + 652, instrux + 653, instrux + 654, instrux + 655, instrux + 656, instrux + 657, instrux + 658, instrux + 659, instrux + 660, instrux + 661, instrux + 662, instrux + 663, instrux + 664, instrux + 665, instrux + 666, instrux + 667, instrux + 668, instrux + 669, instrux + 670, instrux + 671, instrux + 672, instrux + 673, instrux + 674, instrux + 675, instrux + 676, instrux + 677, instrux + 678, instrux + 679, instrux + 680, instrux + 681, instrux + 682, instrux + 683, instrux + 684, instrux + 685, instrux + 686, instrux + 687, instrux + 688, instrux + 689, instrux + 690, instrux + 691, instrux + 692, instrux + 693, instrux + 694, instrux + 695, instrux + 696, instrux + 697, instrux + 698, instrux + 699, instrux + 700, instrux + 701, instrux + 702, instrux + 703, instrux + 704, instrux + 705, instrux + 711, instrux + 718, instrux + 719, instrux + 720, instrux + 721, instrux + 722, instrux + 723, instrux + 724, instrux + 725, instrux + 726, instrux + 727, instrux + 728, instrux + 729, instrux + 730, instrux + 731, instrux + 732, instrux + 733, instrux + 734, instrux + 735, instrux + 736, instrux + 737, instrux + 738, instrux + 739, instrux + 740, instrux + 741, instrux + 742, instrux + 743, instrux + 744, instrux + 745, instrux + 746, instrux + 747, instrux + 748, instrux + 749, instrux + 750, instrux + 751, instrux + 752, instrux + 753, instrux + 754, instrux + 755, instrux + 756, instrux + 757, instrux + 758, instrux + 759, instrux + 760, instrux + 761, instrux + 762, instrux + 763, instrux + 764, instrux + 765, instrux + 766, instrux + 767, instrux + 768, instrux + 769, instrux + 770, instrux + 771, instrux + 772, instrux + 773, instrux + 780, instrux + 791, instrux + 792, instrux + 811, instrux + 812, instrux + 813, instrux + 814, instrux + 840, instrux + 841, instrux + 842, instrux + 843, instrux + 881, instrux + 891, instrux + 892, instrux + 893, instrux + 894, instrux + 895, instrux + 896, instrux + 897, instrux + 898, instrux + 908, instrux + 909, instrux + 910, instrux + 911, instrux + 912, instrux + 913, instrux + 914, instrux + 915, instrux + 916, instrux + 917, instrux + 918, instrux + 919, instrux + 920, instrux + 922, instrux + 923, instrux + 924, instrux + 925, instrux + 926, instrux + 933, instrux + 934, instrux + 935, instrux + 936, instrux + 960, instrux + 961, instrux + 962, instrux + 963, instrux + 964, instrux + 965, instrux + 966, instrux + 985, instrux + 986, instrux + 987, instrux + 988, instrux + 989, instrux + 990, instrux + 991, instrux + 992, instrux + 993, instrux + 994, instrux + 995, instrux + 996, instrux + 997, instrux + 998, instrux + 999, instrux + 1000, instrux + 1001, instrux + 1002, instrux + 1003, instrux + 1004, instrux + 1005, instrux + 1008, instrux + 1009, instrux + 1010, instrux + 1011, instrux + 1012, instrux + 1013, instrux + 1014, instrux + 1015, instrux + 1016, instrux + 1058, instrux + 1059, instrux + 1060, instrux + 1061, instrux + 1062, instrux + 1063, instrux + 1064, instrux + 1065, instrux + 1067, instrux + 1068, instrux + 1069, instrux + 1070, instrux + 1071, instrux + 1072, instrux + 1073, instrux + 1074, instrux + 1075, instrux + 1076, instrux + 1077, instrux + 1078, instrux + 1079, instrux + 1080, instrux + 1081, instrux + 1082, instrux + 1083, instrux + 1084, instrux + 1085, instrux + 1086, instrux + 1087, instrux + 1088, instrux + 1089, instrux + 1090, instrux + 1091, instrux + 1092, instrux + 1093, instrux + 1094, instrux + 1095, instrux + 1096, instrux + 1097, instrux + 1098, instrux + 1099, instrux + 1100, instrux + 1101, instrux + 1102, instrux + 1103, instrux + 1104, instrux + 1105, instrux + 1106, instrux + 1107, instrux + 1108, instrux + 1109, instrux + 1110, instrux + 1111, instrux + 1112, instrux + 1113, instrux + 1114, instrux + 1115, instrux + 1116, instrux + 1117, instrux + 1118, instrux + 1119, instrux + 1120, instrux + 1121, instrux + 1122, instrux + 1123, instrux + 1124, instrux + 1125, instrux + 1126, instrux + 1127, instrux + 1128, instrux + 1129, instrux + 1130, instrux + 1131, instrux + 1132, instrux + 1133, instrux + 1134, instrux + 1135, instrux + 1136, instrux + 1137, instrux + 1138, instrux + 1139, instrux + 1140, instrux + 1141, instrux + 1142, instrux + 1143, instrux + 1144, instrux + 1145, instrux + 1146, instrux + 1147, instrux + 1148, instrux + 1149, instrux + 1150, instrux + 1151, instrux + 1152, instrux + 1153, instrux + 1154, instrux + 1155, instrux + 1156, instrux + 1157, instrux + 1158, instrux + 1159, instrux + 1160, instrux + 1161, instrux + 1162, instrux + 1163, instrux + 1164, instrux + 1165, instrux + 1166, instrux + 1167, instrux + 1168, instrux + 1169, instrux + 1170, instrux + 1171, instrux + 1172, instrux + 1173, instrux + 1174, instrux + 1175, instrux + 1176, instrux + 1177, instrux + 1178, instrux + 1179, instrux + 1180, instrux + 1181, instrux + 1182, instrux + 1183, instrux + 1184, instrux + 1185, instrux + 1186, instrux + 1187, instrux + 1188, instrux + 1189, instrux + 1190, instrux + 1191, instrux + 1192, instrux + 1193, instrux + 1194, instrux + 1195, instrux + 1196, instrux + 1197, instrux + 1198, instrux + 1199, instrux + 1200, instrux + 1201, instrux + 1202, instrux + 1203, instrux + 1204, instrux + 1205, instrux + 1206, instrux + 1207, instrux + 1208, instrux + 1209, instrux + 1210, instrux + 1211, instrux + 1212, instrux + 1213, instrux + 1214, instrux + 1215, instrux + 1216, instrux + 1217, instrux + 1218, instrux + 1219, instrux + 1220, instrux + 1221, instrux + 1222, instrux + 1223, instrux + 1224, instrux + 1225, instrux + 1226, instrux + 1227, instrux + 1228, instrux + 1229, instrux + 1230, instrux + 1231, instrux + 1232, instrux + 1233, instrux + 1235, instrux + 1237, instrux + 1240, instrux + 1241, instrux + 1250, instrux + 1251, instrux + 1252, instrux + 1253, instrux + 1255, instrux + 1258, instrux + 1259, instrux + 1272, instrux + 1273, instrux + 1324, instrux + 1325, instrux + 1334, instrux + 1335, instrux + 1370, instrux + 1371, instrux + 1446, instrux + 1447, instrux + 1448, instrux + 1449, instrux + 1460, instrux + 1461, instrux + 1468, instrux + 1469, instrux + 1474, instrux + 1475, NULL }; static struct itemplate *itable_10[] = { instrux + 6, instrux + 7, NULL }; static struct itemplate *itable_11[] = { instrux + 8, instrux + 9, instrux + 10, instrux + 11, NULL }; static struct itemplate *itable_12[] = { instrux + 12, instrux + 13, NULL }; static struct itemplate *itable_13[] = { instrux + 14, instrux + 15, instrux + 16, instrux + 17, NULL }; static struct itemplate *itable_14[] = { instrux + 20, NULL }; static struct itemplate *itable_15[] = { instrux + 21, instrux + 22, NULL }; static struct itemplate *itable_16[] = { instrux + 778, instrux + 779, NULL }; static struct itemplate *itable_17[] = { instrux + 710, NULL }; static struct itemplate *itable_18[] = { instrux + 855, instrux + 856, NULL }; static struct itemplate *itable_19[] = { instrux + 857, instrux + 858, instrux + 859, instrux + 860, NULL }; static struct itemplate *itable_1A[] = { instrux + 861, instrux + 862, NULL }; static struct itemplate *itable_1B[] = { instrux + 863, instrux + 864, instrux + 865, instrux + 866, NULL }; static struct itemplate *itable_1C[] = { instrux + 869, NULL }; static struct itemplate *itable_1D[] = { instrux + 870, instrux + 871, NULL }; static struct itemplate *itable_1E[] = { instrux + 778, instrux + 779, NULL }; static struct itemplate *itable_1F[] = { instrux + 710, NULL }; static struct itemplate *itable_20[] = { instrux + 52, instrux + 53, NULL }; static struct itemplate *itable_21[] = { instrux + 54, instrux + 55, instrux + 56, instrux + 57, NULL }; static struct itemplate *itable_22[] = { instrux + 58, instrux + 59, NULL }; static struct itemplate *itable_23[] = { instrux + 60, instrux + 61, instrux + 62, instrux + 63, NULL }; static struct itemplate *itable_24[] = { instrux + 66, NULL }; static struct itemplate *itable_25[] = { instrux + 67, instrux + 68, NULL }; static struct itemplate *itable_26[] = { NULL }; static struct itemplate *itable_27[] = { instrux + 183, NULL }; static struct itemplate *itable_28[] = { instrux + 937, instrux + 938, NULL }; static struct itemplate *itable_29[] = { instrux + 939, instrux + 940, instrux + 941, instrux + 942, NULL }; static struct itemplate *itable_2A[] = { instrux + 943, instrux + 944, NULL }; static struct itemplate *itable_2B[] = { instrux + 945, instrux + 946, instrux + 947, instrux + 948, NULL }; static struct itemplate *itable_2C[] = { instrux + 951, NULL }; static struct itemplate *itable_2D[] = { instrux + 952, instrux + 953, NULL }; static struct itemplate *itable_2E[] = { NULL }; static struct itemplate *itable_2F[] = { instrux + 184, NULL }; static struct itemplate *itable_30[] = { instrux + 1035, instrux + 1036, NULL }; static struct itemplate *itable_31[] = { instrux + 1037, instrux + 1038, instrux + 1039, instrux + 1040, NULL }; static struct itemplate *itable_32[] = { instrux + 1041, instrux + 1042, NULL }; static struct itemplate *itable_33[] = { instrux + 1043, instrux + 1044, instrux + 1045, instrux + 1046, NULL }; static struct itemplate *itable_34[] = { instrux + 1049, NULL }; static struct itemplate *itable_35[] = { instrux + 1050, instrux + 1051, NULL }; static struct itemplate *itable_36[] = { NULL }; static struct itemplate *itable_37[] = { instrux + 0, NULL }; static struct itemplate *itable_38[] = { instrux + 141, instrux + 142, NULL }; static struct itemplate *itable_39[] = { instrux + 143, instrux + 144, instrux + 145, instrux + 146, NULL }; static struct itemplate *itable_3A[] = { instrux + 147, instrux + 148, NULL }; static struct itemplate *itable_3B[] = { instrux + 149, instrux + 150, instrux + 151, instrux + 152, NULL }; static struct itemplate *itable_3C[] = { instrux + 155, NULL }; static struct itemplate *itable_3D[] = { instrux + 156, instrux + 157, NULL }; static struct itemplate *itable_3E[] = { NULL }; static struct itemplate *itable_3F[] = { instrux + 5, NULL }; static struct itemplate *itable_40[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_41[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_42[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_43[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_44[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_45[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_46[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_47[] = { instrux + 411, instrux + 412, NULL }; static struct itemplate *itable_48[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_49[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4A[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4B[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4C[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4D[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4E[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_4F[] = { instrux + 185, instrux + 186, NULL }; static struct itemplate *itable_50[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_51[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_52[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_53[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_54[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_55[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_56[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_57[] = { instrux + 774, instrux + 775, NULL }; static struct itemplate *itable_58[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_59[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5A[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5B[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5C[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5D[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5E[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_5F[] = { instrux + 706, instrux + 707, NULL }; static struct itemplate *itable_60[] = { instrux + 785, instrux + 786, instrux + 787, NULL }; static struct itemplate *itable_61[] = { instrux + 712, instrux + 713, instrux + 714, NULL }; static struct itemplate *itable_62[] = { instrux + 77, instrux + 78, NULL }; static struct itemplate *itable_63[] = { instrux + 75, instrux + 76, NULL }; static struct itemplate *itable_64[] = { NULL }; static struct itemplate *itable_65[] = { NULL }; static struct itemplate *itable_66[] = { instrux + 1234, instrux + 1236, instrux + 1238, instrux + 1242, instrux + 1243, instrux + 1244, instrux + 1245, instrux + 1246, instrux + 1247, instrux + 1248, instrux + 1249, instrux + 1256, instrux + 1257, instrux + 1260, instrux + 1261, instrux + 1262, instrux + 1263, instrux + 1264, instrux + 1265, instrux + 1266, instrux + 1267, instrux + 1268, instrux + 1269, instrux + 1270, instrux + 1271, instrux + 1274, instrux + 1275, instrux + 1276, instrux + 1277, instrux + 1278, instrux + 1279, instrux + 1280, instrux + 1281, instrux + 1282, instrux + 1283, instrux + 1284, instrux + 1285, instrux + 1286, instrux + 1287, instrux + 1288, instrux + 1289, instrux + 1290, instrux + 1291, instrux + 1292, instrux + 1293, instrux + 1294, instrux + 1295, instrux + 1296, instrux + 1297, instrux + 1298, instrux + 1299, instrux + 1300, instrux + 1301, instrux + 1302, instrux + 1303, instrux + 1304, instrux + 1305, instrux + 1306, instrux + 1307, instrux + 1308, instrux + 1309, instrux + 1310, instrux + 1311, instrux + 1312, instrux + 1313, instrux + 1314, instrux + 1315, instrux + 1316, instrux + 1317, instrux + 1318, instrux + 1319, instrux + 1320, instrux + 1321, instrux + 1322, instrux + 1323, instrux + 1326, instrux + 1327, instrux + 1328, instrux + 1329, instrux + 1330, instrux + 1331, instrux + 1332, instrux + 1333, instrux + 1338, instrux + 1339, instrux + 1340, instrux + 1341, instrux + 1342, instrux + 1343, instrux + 1344, instrux + 1345, instrux + 1346, instrux + 1347, instrux + 1348, instrux + 1349, instrux + 1350, instrux + 1351, instrux + 1352, instrux + 1353, instrux + 1354, instrux + 1355, instrux + 1356, instrux + 1357, instrux + 1358, instrux + 1359, instrux + 1360, instrux + 1361, instrux + 1362, instrux + 1363, instrux + 1364, instrux + 1365, instrux + 1366, instrux + 1367, instrux + 1368, instrux + 1369, instrux + 1372, instrux + 1373, instrux + 1374, instrux + 1375, instrux + 1376, instrux + 1377, instrux + 1378, instrux + 1379, instrux + 1380, instrux + 1381, instrux + 1382, instrux + 1383, instrux + 1384, instrux + 1385, instrux + 1386, instrux + 1387, instrux + 1388, instrux + 1389, instrux + 1390, instrux + 1391, instrux + 1392, instrux + 1393, instrux + 1394, instrux + 1395, instrux + 1396, instrux + 1397, instrux + 1398, instrux + 1399, instrux + 1400, instrux + 1401, instrux + 1404, instrux + 1405, instrux + 1406, instrux + 1407, instrux + 1408, instrux + 1409, instrux + 1412, instrux + 1413, instrux + 1416, instrux + 1417, instrux + 1420, instrux + 1421, instrux + 1424, instrux + 1425, instrux + 1428, instrux + 1429, instrux + 1432, instrux + 1433, instrux + 1436, instrux + 1437, instrux + 1440, instrux + 1441, instrux + 1444, instrux + 1445, instrux + 1452, instrux + 1453, instrux + 1454, instrux + 1455, instrux + 1456, instrux + 1457, instrux + 1458, instrux + 1459, instrux + 1470, instrux + 1471, instrux + 1472, instrux + 1473, instrux + 1478, instrux + 1479, instrux + 1482, instrux + 1483, instrux + 1486, instrux + 1487, instrux + 1490, instrux + 1491, instrux + 1492, instrux + 1493, instrux + 1494, instrux + 1495, instrux + 1496, instrux + 1497, instrux + 1498, instrux + 1503, instrux + 1504, instrux + 1505, instrux + 1506, instrux + 1507, instrux + 1508, instrux + 1511, instrux + 1512, instrux + 1513, instrux + 1514, instrux + 1515, instrux + 1516, instrux + 1519, instrux + 1520, instrux + 1523, instrux + 1524, instrux + 1525, instrux + 1526, instrux + 1527, instrux + 1528, instrux + 1529, instrux + 1530, instrux + 1531, instrux + 1532, instrux + 1535, instrux + 1536, instrux + 1539, instrux + 1540, NULL }; static struct itemplate *itable_67[] = { NULL }; static struct itemplate *itable_68[] = { instrux + 782, instrux + 783, instrux + 784, NULL }; static struct itemplate *itable_69[] = { instrux + 394, instrux + 396, instrux + 398, instrux + 400, instrux + 402, instrux + 404, NULL }; static struct itemplate *itable_6A[] = { instrux + 781, NULL }; static struct itemplate *itable_6B[] = { instrux + 393, instrux + 395, instrux + 397, instrux + 399, instrux + 401, instrux + 403, NULL }; static struct itemplate *itable_6C[] = { instrux + 416, NULL }; static struct itemplate *itable_6D[] = { instrux + 417, instrux + 418, NULL }; static struct itemplate *itable_6E[] = { instrux + 607, NULL }; static struct itemplate *itable_6F[] = { instrux + 608, instrux + 609, NULL }; static struct itemplate *itable_70[] = { instrux + 1066, NULL }; static struct itemplate *itable_71[] = { instrux + 1066, NULL }; static struct itemplate *itable_72[] = { instrux + 1066, NULL }; static struct itemplate *itable_73[] = { instrux + 1066, NULL }; static struct itemplate *itable_74[] = { instrux + 1066, NULL }; static struct itemplate *itable_75[] = { instrux + 1066, NULL }; static struct itemplate *itable_76[] = { instrux + 1066, NULL }; static struct itemplate *itable_77[] = { instrux + 1066, NULL }; static struct itemplate *itable_78[] = { instrux + 1066, NULL }; static struct itemplate *itable_79[] = { instrux + 1066, NULL }; static struct itemplate *itable_7A[] = { instrux + 1066, NULL }; static struct itemplate *itable_7B[] = { instrux + 1066, NULL }; static struct itemplate *itable_7C[] = { instrux + 1066, NULL }; static struct itemplate *itable_7D[] = { instrux + 1066, NULL }; static struct itemplate *itable_7E[] = { instrux + 1066, NULL }; static struct itemplate *itable_7F[] = { instrux + 1066, NULL }; static struct itemplate *itable_80[] = { instrux + 23, instrux + 26, instrux + 46, instrux + 49, instrux + 69, instrux + 72, instrux + 158, instrux + 161, instrux + 595, instrux + 598, instrux + 872, instrux + 875, instrux + 954, instrux + 957, instrux + 1052, instrux + 1055, NULL }; static struct itemplate *itable_81[] = { instrux + 24, instrux + 25, instrux + 27, instrux + 28, instrux + 47, instrux + 48, instrux + 50, instrux + 51, instrux + 70, instrux + 71, instrux + 73, instrux + 74, instrux + 159, instrux + 160, instrux + 162, instrux + 163, instrux + 596, instrux + 597, instrux + 599, instrux + 600, instrux + 873, instrux + 874, instrux + 876, instrux + 877, instrux + 955, instrux + 956, instrux + 958, instrux + 959, instrux + 1053, instrux + 1054, instrux + 1056, instrux + 1057, NULL }; static struct itemplate *itable_82[] = { instrux + 1550, instrux + 1551, instrux + 1552, instrux + 1553, instrux + 1554, instrux + 1555, instrux + 1556, instrux + 1557, instrux + 1558, instrux + 1559, instrux + 1560, instrux + 1561, instrux + 1562, instrux + 1563, instrux + 1564, instrux + 1565, NULL }; static struct itemplate *itable_83[] = { instrux + 18, instrux + 19, instrux + 41, instrux + 42, instrux + 64, instrux + 65, instrux + 153, instrux + 154, instrux + 590, instrux + 591, instrux + 867, instrux + 868, instrux + 949, instrux + 950, instrux + 1047, instrux + 1048, NULL }; static struct itemplate *itable_84[] = { instrux + 967, instrux + 968, instrux + 973, NULL }; static struct itemplate *itable_85[] = { instrux + 969, instrux + 970, instrux + 971, instrux + 972, instrux + 974, instrux + 975, NULL }; static struct itemplate *itable_86[] = { instrux + 1021, instrux + 1022, instrux + 1027, instrux + 1028, NULL }; static struct itemplate *itable_87[] = { instrux + 1023, instrux + 1024, instrux + 1025, instrux + 1026, instrux + 1029, instrux + 1030, instrux + 1031, instrux + 1032, NULL }; static struct itemplate *itable_88[] = { instrux + 527, instrux + 528, NULL }; static struct itemplate *itable_89[] = { instrux + 529, instrux + 530, instrux + 531, instrux + 532, NULL }; static struct itemplate *itable_8A[] = { instrux + 533, instrux + 534, NULL }; static struct itemplate *itable_8B[] = { instrux + 535, instrux + 536, instrux + 537, instrux + 538, NULL }; static struct itemplate *itable_8C[] = { instrux + 509, instrux + 510, instrux + 511, NULL }; static struct itemplate *itable_8D[] = { instrux + 462, instrux + 463, NULL }; static struct itemplate *itable_8E[] = { instrux + 512, instrux + 513, instrux + 514, NULL }; static struct itemplate *itable_8F[] = { instrux + 708, instrux + 709, NULL }; static struct itemplate *itable_90[] = { instrux + 574, instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, instrux + 1239, NULL }; static struct itemplate *itable_91[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_92[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_93[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_94[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_95[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_96[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_97[] = { instrux + 1017, instrux + 1018, instrux + 1019, instrux + 1020, NULL }; static struct itemplate *itable_98[] = { instrux + 134, instrux + 182, NULL }; static struct itemplate *itable_99[] = { instrux + 135, instrux + 181, NULL }; static struct itemplate *itable_9A[] = { instrux + 118, instrux + 119, instrux + 120, instrux + 121, instrux + 122, NULL }; static struct itemplate *itable_9B[] = { instrux + 212, instrux + 244, instrux + 262, instrux + 281, instrux + 331, instrux + 340, instrux + 341, instrux + 346, instrux + 347, instrux + 1006, instrux + 1007, NULL }; static struct itemplate *itable_9C[] = { instrux + 788, instrux + 789, instrux + 790, NULL }; static struct itemplate *itable_9D[] = { instrux + 715, instrux + 716, instrux + 717, NULL }; static struct itemplate *itable_9E[] = { instrux + 844, NULL }; static struct itemplate *itable_9F[] = { instrux + 455, NULL }; static struct itemplate *itable_A0[] = { instrux + 515, NULL }; static struct itemplate *itable_A1[] = { instrux + 516, instrux + 517, NULL }; static struct itemplate *itable_A2[] = { instrux + 518, NULL }; static struct itemplate *itable_A3[] = { instrux + 519, instrux + 520, NULL }; static struct itemplate *itable_A4[] = { instrux + 556, NULL }; static struct itemplate *itable_A5[] = { instrux + 557, instrux + 558, NULL }; static struct itemplate *itable_A6[] = { instrux + 164, NULL }; static struct itemplate *itable_A7[] = { instrux + 165, instrux + 166, NULL }; static struct itemplate *itable_A8[] = { instrux + 976, NULL }; static struct itemplate *itable_A9[] = { instrux + 977, instrux + 978, NULL }; static struct itemplate *itable_AA[] = { instrux + 930, NULL }; static struct itemplate *itable_AB[] = { instrux + 931, instrux + 932, NULL }; static struct itemplate *itable_AC[] = { instrux + 481, NULL }; static struct itemplate *itable_AD[] = { instrux + 482, instrux + 483, NULL }; static struct itemplate *itable_AE[] = { instrux + 878, NULL }; static struct itemplate *itable_AF[] = { instrux + 879, instrux + 880, NULL }; static struct itemplate *itable_B0[] = { instrux + 539, NULL }; static struct itemplate *itable_B1[] = { instrux + 539, NULL }; static struct itemplate *itable_B2[] = { instrux + 539, NULL }; static struct itemplate *itable_B3[] = { instrux + 539, NULL }; static struct itemplate *itable_B4[] = { instrux + 539, NULL }; static struct itemplate *itable_B5[] = { instrux + 539, NULL }; static struct itemplate *itable_B6[] = { instrux + 539, NULL }; static struct itemplate *itable_B7[] = { instrux + 539, NULL }; static struct itemplate *itable_B8[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_B9[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BA[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BB[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BC[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BD[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BE[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_BF[] = { instrux + 540, instrux + 541, NULL }; static struct itemplate *itable_C0[] = { instrux + 795, instrux + 804, instrux + 824, instrux + 833, instrux + 848, instrux + 884, instrux + 901, NULL }; static struct itemplate *itable_C1[] = { instrux + 798, instrux + 801, instrux + 807, instrux + 810, instrux + 827, instrux + 830, instrux + 836, instrux + 839, instrux + 851, instrux + 854, instrux + 887, instrux + 890, instrux + 904, instrux + 907, NULL }; static struct itemplate *itable_C2[] = { instrux + 817, instrux + 821, NULL }; static struct itemplate *itable_C3[] = { instrux + 816, instrux + 820, NULL }; static struct itemplate *itable_C4[] = { instrux + 465, instrux + 466, NULL }; static struct itemplate *itable_C5[] = { instrux + 460, instrux + 461, NULL }; static struct itemplate *itable_C6[] = { instrux + 542, instrux + 545, NULL }; static struct itemplate *itable_C7[] = { instrux + 543, instrux + 544, instrux + 546, instrux + 547, NULL }; static struct itemplate *itable_C8[] = { instrux + 194, NULL }; static struct itemplate *itable_C9[] = { instrux + 464, NULL }; static struct itemplate *itable_CA[] = { instrux + 819, NULL }; static struct itemplate *itable_CB[] = { instrux + 818, NULL }; static struct itemplate *itable_CC[] = { instrux + 421, NULL }; static struct itemplate *itable_CD[] = { instrux + 419, NULL }; static struct itemplate *itable_CE[] = { instrux + 422, NULL }; static struct itemplate *itable_CF[] = { instrux + 425, instrux + 426, instrux + 427, NULL }; static struct itemplate *itable_D0[] = { instrux + 793, instrux + 802, instrux + 822, instrux + 831, instrux + 846, instrux + 882, instrux + 899, NULL }; static struct itemplate *itable_D1[] = { instrux + 796, instrux + 799, instrux + 805, instrux + 808, instrux + 825, instrux + 828, instrux + 834, instrux + 837, instrux + 849, instrux + 852, instrux + 885, instrux + 888, instrux + 902, instrux + 905, NULL }; static struct itemplate *itable_D2[] = { instrux + 794, instrux + 803, instrux + 823, instrux + 832, instrux + 847, instrux + 883, instrux + 900, NULL }; static struct itemplate *itable_D3[] = { instrux + 797, instrux + 800, instrux + 806, instrux + 809, instrux + 826, instrux + 829, instrux + 835, instrux + 838, instrux + 850, instrux + 853, instrux + 886, instrux + 889, instrux + 903, instrux + 906, NULL }; static struct itemplate *itable_D4[] = { instrux + 3, instrux + 4, NULL }; static struct itemplate *itable_D5[] = { instrux + 1, instrux + 2, NULL }; static struct itemplate *itable_D6[] = { instrux + 845, NULL }; static struct itemplate *itable_D7[] = { instrux + 1033, instrux + 1034, NULL }; static struct itemplate *itable_D8[] = { instrux + 199, instrux + 202, instrux + 204, instrux + 229, instrux + 231, instrux + 232, instrux + 237, instrux + 239, instrux + 240, instrux + 245, instrux + 249, instrux + 250, instrux + 253, instrux + 257, instrux + 258, instrux + 307, instrux + 311, instrux + 312, instrux + 348, instrux + 352, instrux + 353, instrux + 356, instrux + 360, instrux + 361, NULL }; static struct itemplate *itable_D9[] = { instrux + 197, instrux + 198, instrux + 211, instrux + 242, instrux + 243, instrux + 280, instrux + 294, instrux + 297, instrux + 298, instrux + 299, instrux + 300, instrux + 301, instrux + 302, instrux + 303, instrux + 304, instrux + 305, instrux + 306, instrux + 319, instrux + 321, instrux + 322, instrux + 325, instrux + 326, instrux + 327, instrux + 328, instrux + 329, instrux + 332, instrux + 334, instrux + 335, instrux + 336, instrux + 337, instrux + 342, instrux + 364, instrux + 374, instrux + 375, instrux + 376, instrux + 377, instrux + 378, instrux + 379, instrux + 380, instrux + 381, NULL }; static struct itemplate *itable_DA[] = { instrux + 213, instrux + 214, instrux + 215, instrux + 216, instrux + 217, instrux + 218, instrux + 227, instrux + 228, instrux + 265, instrux + 267, instrux + 269, instrux + 271, instrux + 273, instrux + 278, instrux + 290, instrux + 292, instrux + 373, NULL }; static struct itemplate *itable_DB[] = { instrux + 219, instrux + 220, instrux + 221, instrux + 222, instrux + 223, instrux + 224, instrux + 225, instrux + 226, instrux + 233, instrux + 234, instrux + 275, instrux + 282, instrux + 284, instrux + 288, instrux + 296, instrux + 315, instrux + 316, instrux + 317, instrux + 318, instrux + 333, instrux + 344, instrux + 367, instrux + 368, NULL }; static struct itemplate *itable_DC[] = { instrux + 200, instrux + 201, instrux + 203, instrux + 230, instrux + 238, instrux + 246, instrux + 247, instrux + 248, instrux + 254, instrux + 255, instrux + 256, instrux + 308, instrux + 309, instrux + 310, instrux + 349, instrux + 350, instrux + 351, instrux + 357, instrux + 358, instrux + 359, NULL }; static struct itemplate *itable_DD[] = { instrux + 263, instrux + 287, instrux + 295, instrux + 320, instrux + 323, instrux + 330, instrux + 338, instrux + 339, instrux + 343, instrux + 345, instrux + 365, instrux + 366, instrux + 371, instrux + 372, NULL }; static struct itemplate *itable_DE[] = { instrux + 205, instrux + 206, instrux + 241, instrux + 251, instrux + 252, instrux + 259, instrux + 260, instrux + 266, instrux + 268, instrux + 270, instrux + 272, instrux + 274, instrux + 279, instrux + 291, instrux + 293, instrux + 313, instrux + 314, instrux + 354, instrux + 355, instrux + 362, instrux + 363, NULL }; static struct itemplate *itable_DF[] = { instrux + 207, instrux + 208, instrux + 209, instrux + 210, instrux + 235, instrux + 236, instrux + 264, instrux + 276, instrux + 277, instrux + 283, instrux + 285, instrux + 286, instrux + 289, instrux + 324, instrux + 369, instrux + 370, NULL }; static struct itemplate *itable_E0[] = { instrux + 490, instrux + 491, instrux + 492, instrux + 493, instrux + 494, instrux + 495, NULL }; static struct itemplate *itable_E1[] = { instrux + 487, instrux + 488, instrux + 489, instrux + 496, instrux + 497, instrux + 498, NULL }; static struct itemplate *itable_E2[] = { instrux + 484, instrux + 485, instrux + 486, NULL }; static struct itemplate *itable_E3[] = { instrux + 428, instrux + 429, NULL }; static struct itemplate *itable_E4[] = { instrux + 405, NULL }; static struct itemplate *itable_E5[] = { instrux + 406, instrux + 407, NULL }; static struct itemplate *itable_E6[] = { instrux + 601, NULL }; static struct itemplate *itable_E7[] = { instrux + 602, instrux + 603, NULL }; static struct itemplate *itable_E8[] = { instrux + 112, instrux + 113, instrux + 114, instrux + 115, instrux + 116, instrux + 117, NULL }; static struct itemplate *itable_E9[] = { instrux + 431, instrux + 432, instrux + 433, NULL }; static struct itemplate *itable_EA[] = { instrux + 434, instrux + 435, instrux + 436, instrux + 437, instrux + 438, NULL }; static struct itemplate *itable_EB[] = { instrux + 430, NULL }; static struct itemplate *itable_EC[] = { instrux + 408, NULL }; static struct itemplate *itable_ED[] = { instrux + 409, instrux + 410, NULL }; static struct itemplate *itable_EE[] = { instrux + 604, NULL }; static struct itemplate *itable_EF[] = { instrux + 605, instrux + 606, NULL }; static struct itemplate *itable_F0[] = { NULL }; static struct itemplate *itable_F1[] = { instrux + 420, instrux + 921, NULL }; static struct itemplate *itable_F2[] = { instrux + 1254, instrux + 1336, instrux + 1337, instrux + 1402, instrux + 1403, instrux + 1410, instrux + 1411, instrux + 1414, instrux + 1415, instrux + 1418, instrux + 1419, instrux + 1422, instrux + 1423, instrux + 1426, instrux + 1427, instrux + 1430, instrux + 1431, instrux + 1434, instrux + 1435, instrux + 1438, instrux + 1439, instrux + 1442, instrux + 1443, instrux + 1450, instrux + 1451, instrux + 1462, instrux + 1463, instrux + 1464, instrux + 1465, instrux + 1466, instrux + 1467, instrux + 1476, instrux + 1477, instrux + 1480, instrux + 1481, instrux + 1484, instrux + 1485, instrux + 1488, instrux + 1489, instrux + 1499, instrux + 1500, instrux + 1501, instrux + 1502, instrux + 1509, instrux + 1510, instrux + 1517, instrux + 1518, instrux + 1521, instrux + 1522, instrux + 1533, instrux + 1534, instrux + 1537, instrux + 1538, instrux + 1541, instrux + 1542, instrux + 1543, instrux + 1544, instrux + 1545, NULL }; static struct itemplate *itable_F3[] = { instrux + 1546, instrux + 1547, instrux + 1548, instrux + 1549, NULL }; static struct itemplate *itable_F4[] = { instrux + 382, NULL }; static struct itemplate *itable_F5[] = { instrux + 140, NULL }; static struct itemplate *itable_F6[] = { instrux + 190, instrux + 383, instrux + 386, instrux + 567, instrux + 571, instrux + 575, instrux + 979, instrux + 982, NULL }; static struct itemplate *itable_F7[] = { instrux + 191, instrux + 192, instrux + 384, instrux + 385, instrux + 387, instrux + 388, instrux + 568, instrux + 569, instrux + 572, instrux + 573, instrux + 576, instrux + 577, instrux + 980, instrux + 981, instrux + 983, instrux + 984, NULL }; static struct itemplate *itable_F8[] = { instrux + 136, NULL }; static struct itemplate *itable_F9[] = { instrux + 927, NULL }; static struct itemplate *itable_FA[] = { instrux + 138, NULL }; static struct itemplate *itable_FB[] = { instrux + 929, NULL }; static struct itemplate *itable_FC[] = { instrux + 137, NULL }; static struct itemplate *itable_FD[] = { instrux + 928, NULL }; static struct itemplate *itable_FE[] = { instrux + 187, instrux + 413, NULL }; static struct itemplate *itable_FF[] = { instrux + 123, instrux + 124, instrux + 125, instrux + 126, instrux + 127, instrux + 128, instrux + 129, instrux + 130, instrux + 131, instrux + 132, instrux + 133, instrux + 188, instrux + 189, instrux + 414, instrux + 415, instrux + 439, instrux + 440, instrux + 441, instrux + 442, instrux + 443, instrux + 444, instrux + 445, instrux + 446, instrux + 447, instrux + 448, instrux + 449, instrux + 776, instrux + 777, NULL }; struct itemplate **itable[] = { itable_00, itable_01, itable_02, itable_03, itable_04, itable_05, itable_06, itable_07, itable_08, itable_09, itable_0A, itable_0B, itable_0C, itable_0D, itable_0E, itable_0F, itable_10, itable_11, itable_12, itable_13, itable_14, itable_15, itable_16, itable_17, itable_18, itable_19, itable_1A, itable_1B, itable_1C, itable_1D, itable_1E, itable_1F, itable_20, itable_21, itable_22, itable_23, itable_24, itable_25, itable_26, itable_27, itable_28, itable_29, itable_2A, itable_2B, itable_2C, itable_2D, itable_2E, itable_2F, itable_30, itable_31, itable_32, itable_33, itable_34, itable_35, itable_36, itable_37, itable_38, itable_39, itable_3A, itable_3B, itable_3C, itable_3D, itable_3E, itable_3F, itable_40, itable_41, itable_42, itable_43, itable_44, itable_45, itable_46, itable_47, itable_48, itable_49, itable_4A, itable_4B, itable_4C, itable_4D, itable_4E, itable_4F, itable_50, itable_51, itable_52, itable_53, itable_54, itable_55, itable_56, itable_57, itable_58, itable_59, itable_5A, itable_5B, itable_5C, itable_5D, itable_5E, itable_5F, itable_60, itable_61, itable_62, itable_63, itable_64, itable_65, itable_66, itable_67, itable_68, itable_69, itable_6A, itable_6B, itable_6C, itable_6D, itable_6E, itable_6F, itable_70, itable_71, itable_72, itable_73, itable_74, itable_75, itable_76, itable_77, itable_78, itable_79, itable_7A, itable_7B, itable_7C, itable_7D, itable_7E, itable_7F, itable_80, itable_81, itable_82, itable_83, itable_84, itable_85, itable_86, itable_87, itable_88, itable_89, itable_8A, itable_8B, itable_8C, itable_8D, itable_8E, itable_8F, itable_90, itable_91, itable_92, itable_93, itable_94, itable_95, itable_96, itable_97, itable_98, itable_99, itable_9A, itable_9B, itable_9C, itable_9D, itable_9E, itable_9F, itable_A0, itable_A1, itable_A2, itable_A3, itable_A4, itable_A5, itable_A6, itable_A7, itable_A8, itable_A9, itable_AA, itable_AB, itable_AC, itable_AD, itable_AE, itable_AF, itable_B0, itable_B1, itable_B2, itable_B3, itable_B4, itable_B5, itable_B6, itable_B7, itable_B8, itable_B9, itable_BA, itable_BB, itable_BC, itable_BD, itable_BE, itable_BF, itable_C0, itable_C1, itable_C2, itable_C3, itable_C4, itable_C5, itable_C6, itable_C7, itable_C8, itable_C9, itable_CA, itable_CB, itable_CC, itable_CD, itable_CE, itable_CF, itable_D0, itable_D1, itable_D2, itable_D3, itable_D4, itable_D5, itable_D6, itable_D7, itable_D8, itable_D9, itable_DA, itable_DB, itable_DC, itable_DD, itable_DE, itable_DF, itable_E0, itable_E1, itable_E2, itable_E3, itable_E4, itable_E5, itable_E6, itable_E7, itable_E8, itable_E9, itable_EA, itable_EB, itable_EC, itable_ED, itable_EE, itable_EF, itable_F0, itable_F1, itable_F2, itable_F3, itable_F4, itable_F5, itable_F6, itable_F7, itable_F8, itable_F9, itable_FA, itable_FB, itable_FC, itable_FD, itable_FE, itable_FF, }; simh-3.8.1/AltairZ80/s100_64fdc.c0000644000175000017500000043167211111141264014201 0ustar vlmvlm/************************************************************************* * * * $Id: s100_64fdc.c 1999 2008-07-22 04:25:28Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Cromemco 4FDC/16FDC/64FDC Floppy Controller module for SIMH. * * This module is a wrapper around the wd179x FDC module, and adds the * * Cromemco-specific registers as well as the Cromemco RDOS Boot ROM. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_defs.h" /* simulator definitions */ #include "wd179x.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define DRIVE_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define IRQ_MSG (1 << 8) #define CROMFDC_MAX_DRIVES 4 #define CROMFDC_ROM_SIZE (8 * 1024) #define CROMFDC_ADDR_MASK (CROMFDC_ROM_SIZE - 1) #define CROMFDC_SIM_64US 186 /* Number of "ticks" in 64uS, where does this come from? */ typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_addr; /* DMA Transfer Address */ uint8 rom_disabled; /* TRUE if ROM has been disabled */ uint8 motor_on; uint8 autowait; uint8 rtc; uint8 imask; /* Interrupt Mask Register */ uint8 ipend; /* Interrupt Pending Register */ } CROMFDC_INFO; extern WD179X_INFO_PUB *wd179x_info; static CROMFDC_INFO cromfdc_info_data = { { 0xC000, CROMFDC_ROM_SIZE, 0x3, 2 } }; static CROMFDC_INFO *cromfdc_info = &cromfdc_info_data; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); static t_stat cromfdc_svc (UNIT *uptr); extern REG *sim_PC; extern uint32 PCX; /* external view of PC */ #define UNIT_V_CROMFDC_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_CROMFDC_WLK (1 << UNIT_V_CROMFDC_WLK) #define UNIT_V_CROMFDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_CROMFDC_VERBOSE (1 << UNIT_V_CROMFDC_VERBOSE) #define UNIT_V_CROMFDC_ROM (UNIT_V_UF + 2) /* boot ROM enabled */ #define UNIT_CROMFDC_ROM (1 << UNIT_V_CROMFDC_ROM) #define CROMFDC_CAPACITY (77*1*26*128) /* Default SSSD 8" (IBM 3740) Disk Capacity */ #define MOTOR_TO_LIMIT 128 static t_stat cromfdc_reset(DEVICE *cromfdc_dev); static t_stat cromfdc_boot(int32 unitno, DEVICE *dptr); static int32 cromfdc_ext(const int32 port, const int32 io, const int32 data); static int32 cromfdc_timer(const int32 port, const int32 io, const int32 data); static int32 cromfdc_control(const int32 port, const int32 io, const int32 data); static int32 cromfdc_banksel(const int32 port, const int32 io, const int32 data); static int32 cromfdcrom(const int32 port, const int32 io, const int32 data); static int32 ccs2810_uart_status(const int32 port, const int32 io, const int32 data); static int32 dipswitch = 0; /* 5-position DIP switch on 64FDC card */ static int32 bootstrap = 0; /* 0 for RDOS 2.52, 1 for RDOS 3.12. */ static int32 crofdc_type = 64; /* controller type, either 4, 16, or 64 for Cromemco, 50 for CCS-2422. */ static int32 crofdc_boot = 1; /* BOOT jumper setting, default is auto-boot */ static int32 crofdc_inh_init = 0; /* Inhibit Init (Format) switch, default is not inhibited */ /* Disk Control/Flags Register, 0x34 (IN) / CCS Status Register 1 */ #define CROMFDC_FLAG_DRQ (1 << 7) /* DRQ (All controllers) */ #define CROMFDC_FLAG_BOOT (1 << 6) /* boot# jumper (active low) (All controllers) */ #define CROMFDC_FLAG_SEL_REQ (1 << 5) /* Head Load (4FDC, 16FDC) / Select Request (64FDC) / CCS2422 HLD */ #define CROMFDC_FLAG_INH_INIT (1 << 4) /* Unassigned (4FDC) / Inhibit_Init# (16FDC, 64FDC) / CCS2422 DS4 */ #define CROMFDC_FLAG_MTRON (1 << 3) /* Unassigned (4FDC) / Motor On (16FDC, 64FDC) / CCS2422 DS3 */ #define CROMFDC_FLAG_MTO (1 << 2) /* Unassigned (4FDC) / Motor Timeout (16FDC, 64FDC) / CCS2422 DS2 */ #define CROMFDC_FLAG_ATO (1 << 1) /* Unassigned (4FDC) / Autowait Timeout (16FDC, 64FDC) / CCS2422 DS1 */ #define CROMFDC_FLAG_EOJ (1 << 0) /* End of Job (INTRQ) (All Controllers) (16FDC, 64FDC) */ /* Disk Control/Flags Register, 0x34 (OUT) / CCS Control Register 1 */ #define CROMFDC_CTRL_AUTOWAIT (1 << 7) /* Auto Wait Enable (All controllers) */ #define CROMFDC_CTRL_DDENS (1 << 6) /* Unassigned (4FDC) / Double Density (16FDC, 64FDC) */ #define CROMFDC_CTRL_MTRON (1 << 5) /* Motor On (All controllers) */ #define CROMFDC_CTRL_MAXI (1 << 4) /* Maxi (8") (All controllers) */ #define CROMFDC_CTRL_DS4 (1 << 3) /* Drive Select 4 (All controllers) */ #define CROMFDC_CTRL_DS3 (1 << 2) /* Drive Select 3 (All controllers) */ #define CROMFDC_CTRL_DS2 (1 << 1) /* Drive Select 2 (All controllers) */ #define CROMFDC_CTRL_DS1 (1 << 0) /* Drive Select 1 (All controllers) */ /* 64FDC Auxiliary Disk Command, 0x04 (OUT) / CCS Control Register 2 */ #define CROMFDC_AUX_RESERVED7 (1 << 7) /* Unused (All Controllers) */ #define CROMFDC_AUX_EJECT (1 << 6) /* 4FDC Eject Left* / 16FDC Eject*, 64FDC Unused / CCS2422 - Side Select# */ #define CCSFDC_CMD_SIDE (1 << 6) /* CCS2422 - Side Select# */ #define CROMFDC_AUX_SEL_OVERRIDE (1 << 5) /* 4FDC Eject Right* / 16FDC, 64FDC Drive Select Override */ #define CROMFDC_AUX_FAST_SEEK (1 << 4) /* 4FDC, 16FDC Fast Seek* / 64FDC Unused */ #define CROMFDC_AUX_RESTORE (1 << 3) /* 4FDC, 16FDC Restore* / 64FDC Unused */ #define CROMFDC_AUX_CTRL_OUT (1 << 2) /* Control Out* (All Controllers) */ #define CROMFDC_AUX_CMD_SIDE (1 << 1) /* 16FDC, 64FDC: Side Select* Low=Side 1, High=Side 0. */ #define CROMFDC_AUX_RESERVED0 (1 << 0) /* Unused (All Controllers) */ /* 64FDC Interrupt Mask Register, 0x03 (OUT) */ #define CROMFDC_IRQ_TIMER5 (1 << 7) /* Timer5 Interrupt Mask */ #define CROMFDC_IRQ_TIMER4 (1 << 6) /* Timer4 Interrupt Mask */ #define CROMFDC_IRQ_TBE (1 << 5) /* Transmit Buffer Empty Interrupt Mask */ #define CROMFDC_IRQ_RDA (1 << 4) /* Read Data Available Interrupt Mask */ #define CROMFDC_IRQ_TIMER3 (1 << 3) /* Timer3 Interrupt Mask */ #define CROMFDC_IRQ_EOJ (1 << 2) /* End of Job Interrupt Mask */ #define CROMFDC_IRQ_TIMER2 (1 << 1) /* Timer2 Interrupt Mask */ #define CROMFDC_IRQ_TIMER1 (1 << 0) /* Timer1 Interrupt Mask */ /* 64FDC interrupt "restart" instruction opcodes */ #define CROMFDC_TIMER1_RST 0xC7 /* RST0 - 0xC7 */ #define CROMFDC_TIMER2_RST 0xCF /* RST8 - 0xCF */ #define CROMFDC_EOJ_RST 0xD7 /* RST10 - 0xD7 */ #define CROMFDC_TIMER3_RST 0xDF /* RST18 - 0xDF */ #define CROMFDC_RDA_RST 0xE7 /* RST20 - 0xE7 */ #define CROMFDC_TBE_RST 0xEF /* RST28 - 0xEF */ #define CROMFDC_TIMER4_RST 0xF7 /* RST30 - 0xF7 */ #define CROMFDC_TIMER5_RST 0xFF /* RST38 - 0xFF */ #define RST_OPCODE_TO_VECTOR(x) (x & 0x38) static unsigned char cromfdc_irq_table[8] = { CROMFDC_TIMER1_RST, CROMFDC_TIMER2_RST, CROMFDC_EOJ_RST, CROMFDC_TIMER3_RST, CROMFDC_RDA_RST, CROMFDC_TBE_RST, CROMFDC_TIMER4_RST, CROMFDC_TIMER5_RST }; static uint8 ipend_to_rst_opcode(uint8 ipend) { uint8 active_intr; uint8 i,j = 0; active_intr = cromfdc_info->imask & cromfdc_info->ipend; for(i=1;i != 0;i <<= 1) { /* printf("%d: %d" NLP, i, active_intr & i); */ if (active_intr & i) { return(cromfdc_irq_table[j]); } j++; } return (0); } /* The CROMFDC does not really have RAM associated with it, but for ease of integration with the * SIMH/AltairZ80 Resource Mapping Scheme, rather than Map and Unmap the ROM, simply implement our * own RAM that can be swapped in when the CROMFDC Boot ROM is disabled. */ static uint8 cromfdcram[CROMFDC_ROM_SIZE]; static UNIT cromfdc_unit[] = { { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE + UNIT_CROMFDC_ROM, CROMFDC_CAPACITY), 1024 }, { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) }, { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) }, { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) } }; static REG cromfdc_reg[] = { { HRDATA (DIPSW, dipswitch, 8), }, { DRDATA (BOOTSTRAP, bootstrap, 2), }, { DRDATA (FDCTYPE, crofdc_type, 8), }, { DRDATA (BOOT, crofdc_boot, 10), }, { DRDATA (INHINIT, crofdc_inh_init, 10), }, { NULL } }; static MTAB cromfdc_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_CROMFDC_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_CROMFDC_WLK, UNIT_CROMFDC_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_CROMFDC_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_CROMFDC_VERBOSE, UNIT_CROMFDC_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { UNIT_CROMFDC_ROM, 0, "NOROM", "NOROM", NULL }, { UNIT_CROMFDC_ROM, UNIT_CROMFDC_ROM, "ROM", "ROM", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(cromfdc_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB cromfdc_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "DRIVE", DRIVE_MSG }, { "VERBOSE",VERBOSE_MSG }, { "IRQ", IRQ_MSG }, { NULL, 0 } }; DEVICE cromfdc_dev = { "CROMFDC", cromfdc_unit, cromfdc_reg, cromfdc_mod, CROMFDC_MAX_DRIVES, 10, 31, 1, CROMFDC_MAX_DRIVES, CROMFDC_MAX_DRIVES, NULL, NULL, &cromfdc_reset, &cromfdc_boot, &wd179x_attach, &wd179x_detach, &cromfdc_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, cromfdc_dt, NULL, "Cromemco 4/16/64 FDC CROMFDC" }; /* This is the CROMFDC RDOS-II ROM. * The CROMFDC has a single 8K ROM; however ths simulation includes * two different versions of RDOS: * RDOS 2.52 and RDOS 3.12 * RDOS 2.52 is the default, but RDOS 3.12 can be * selected with 'd cromfdc bootstrap ' at the SIMH SCP Prompt. */ static uint8 cromfdc_rom[2][CROMFDC_ROM_SIZE] = { { /* RDOS 2.52 */ 0xF3, 0x18, 0x3C, 0xC3, 0x30, 0xC0, 0xC3, 0x04, 0xC5, 0xC3, 0x37, 0xC0, 0xC3, 0x3B, 0xC0, 0xC3, 0x9E, 0xCB, 0xC3, 0x37, 0xCD, 0xC3, 0xD4, 0xC4, 0xC3, 0x52, 0xCD, 0xC3, 0xAB, 0xC5, 0xC3, 0x4B, 0xC2, 0xC3, 0x6C, 0xCC, 0xC3, 0x76, 0xCC, 0xC3, 0xEA, 0xCB, 0xC3, 0xFE, 0xC7, 0xC3, 0x97, 0xCC, 0x32, 0x77, 0x00, 0x78, 0xC3, 0x87, 0xC3, 0x32, 0x75, 0x00, 0xC9, 0x32, 0x76, 0x00, 0xC9, 0xAF, 0xD3, 0x03, 0x47, 0xD9, 0x2F, 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x21, 0x2E, 0x00, 0xF9, 0x25, 0x20, 0xFD, 0x74, 0x2C, 0x20, 0xFC, 0x24, 0x22, 0x62, 0x00, 0x22, 0x64, 0x00, 0xCD, 0x4B, 0xC2, 0xDB, 0x34, 0xE6, 0x40, 0xC2, 0x39, 0xC1, 0xDB, 0x04, 0x2F, 0xE6, 0x03, 0x32, 0x77, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x8D, 0xCD, 0xA8, 0xCF, 0x57, 0xD3, 0x34, 0x06, 0x64, 0xCD, 0x28, 0xC1, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0x10, 0xF5, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x32, 0x78, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0xF7, 0xC1, 0x32, 0x7E, 0x00, 0x21, 0x80, 0x00, 0x22, 0x7B, 0x00, 0x22, 0x79, 0x00, 0x3E, 0x42, 0x32, 0x70, 0x00, 0x06, 0x02, 0xC5, 0xCD, 0x37, 0xCD, 0xD4, 0xCB, 0xCD, 0xD4, 0x52, 0xCD, 0xC1, 0x30, 0x13, 0xCD, 0x28, 0xC1, 0x10, 0xEE, 0xCD, 0x06, 0xC8, 0x0D, 0x55, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x20, 0xF4, 0x18, 0x2D, 0x3A, 0x80, 0x00, 0xFE, 0x40, 0x28, 0x04, 0xFE, 0xE5, 0x20, 0x07, 0xCD, 0x06, 0xC8, 0x0D, 0xCE, 0x18, 0x1B, 0xCD, 0x28, 0xC1, 0xCD, 0xA8, 0xCF, 0x57, 0x3A, 0x77, 0x00, 0x08, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, 0x8D, 0x37, 0xC3, 0x80, 0x00, 0xCD, 0x06, 0xC8, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3A, 0x71, 0x00, 0xB7, 0xC4, 0x59, 0xC3, 0x18, 0x09, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x1B, 0xC0, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, 0x7C, 0xD3, 0x04, 0xCD, 0x06, 0xC8, 0x0D, 0x43, 0x72, 0x6F, 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, 0x53, 0x20, 0x30, 0x32, 0x2E, 0x35, 0x32, 0x8D, 0xAF, 0x32, 0x6C, 0x00, 0x3A, 0x7E, 0x00, 0x47, 0xCD, 0xE2, 0xC1, 0x31, 0x2E, 0x00, 0xCD, 0x06, 0xC8, 0xBB, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xA7, 0x28, 0xE5, 0xCD, 0xC1, 0xC2, 0x28, 0xE4, 0x78, 0xFE, 0x17, 0x30, 0x47, 0x87, 0x21, 0x8B, 0xC1, 0xCD, 0x86, 0xC1, 0x7E, 0x23, 0x66, 0x6F, 0xCD, 0xC7, 0xC1, 0x18, 0xCF, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xFD, 0xCC, 0xC8, 0xC1, 0xBD, 0xC1, 0x2B, 0xC7, 0x2A, 0xC6, 0xBD, 0xC1, 0xC4, 0xC1, 0xBD, 0xC1, 0x45, 0xC2, 0xBD, 0xC1, 0xBD, 0xC1, 0x39, 0xC4, 0x45, 0xC6, 0xBD, 0xC1, 0x36, 0xC6, 0xBD, 0xC1, 0xC2, 0xC6, 0x16, 0xC5, 0x7A, 0xC6, 0x41, 0xC9, 0xBD, 0xC1, 0x52, 0xC6, 0x1A, 0xC5, 0xAF, 0x32, 0x6C, 0x00, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0x91, 0xCD, 0x53, 0xC8, 0xE9, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x67, 0xC0, 0xFE, 0x45, 0xD2, 0x2A, 0xC4, 0xD6, 0x41, 0xDA, 0x2A, 0xC4, 0xF5, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC3, 0x6C, 0xC0, 0x3A, 0x6C, 0x00, 0xB7, 0xC8, 0xCD, 0xFE, 0xC7, 0xCB, 0x58, 0xC0, 0xCB, 0x78, 0x3E, 0x3B, 0xC4, 0xFE, 0xC7, 0xC3, 0xFE, 0xC7, 0x7A, 0xD3, 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, 0x01, 0x00, 0x02, 0xCD, 0x3B, 0xC2, 0x3E, 0xD4, 0xD3, 0x30, 0xCD, 0x34, 0xC2, 0x28, 0xDF, 0xDB, 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0xCD, 0x27, 0xC8, 0xCD, 0x97, 0xCC, 0xDB, 0x04, 0xE6, 0x08, 0x28, 0x5D, 0x3E, 0x0A, 0xD3, 0x02, 0x21, 0xD0, 0x07, 0xCD, 0xF1, 0xCF, 0x3E, 0x08, 0xD3, 0x02, 0x16, 0x64, 0x15, 0x28, 0xED, 0x21, 0xB9, 0xC2, 0x0E, 0x00, 0x3E, 0x19, 0x06, 0x09, 0xD3, 0x02, 0xED, 0xA3, 0x28, 0xEE, 0xCD, 0x98, 0xC2, 0xCD, 0x98, 0xC2, 0x38, 0xE9, 0xFE, 0x0D, 0x3E, 0x09, 0x20, 0xEC, 0x3E, 0x0D, 0xD3, 0x01, 0x21, 0xA0, 0x0F, 0xCD, 0xF1, 0xCF, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0xEC, 0xC3, 0x97, 0xCC, 0xD5, 0x11, 0xA0, 0x8C, 0xCD, 0x76, 0xCC, 0x28, 0x05, 0xCD, 0x6C, 0xCC, 0x18, 0x06, 0x1B, 0x7A, 0xB3, 0x20, 0xF1, 0x37, 0xD1, 0xC9, 0x3E, 0x09, 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xC3, 0x97, 0xCC, 0x90, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x01, 0xD6, 0x41, 0xDA, 0xBD, 0xC1, 0x47, 0x13, 0x1A, 0xFE, 0x3B, 0xC0, 0x78, 0xFE, 0x04, 0xD2, 0x2A, 0xC4, 0xC6, 0x41, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x04, 0x20, 0x03, 0x06, 0x80, 0x13, 0xC5, 0xCD, 0xB7, 0xC3, 0xC1, 0x70, 0x30, 0x06, 0x3A, 0x6D, 0x00, 0xB0, 0x77, 0x37, 0xF5, 0xCD, 0x84, 0xC3, 0x32, 0x78, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0x04, 0xC6, 0x3E, 0x48, 0x32, 0x70, 0x00, 0xCD, 0x37, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0xDA, 0x0D, 0xC4, 0xF1, 0xDA, 0xB5, 0xC3, 0xCD, 0x04, 0xC6, 0x21, 0x00, 0x01, 0x22, 0x7B, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x52, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0x30, 0x2D, 0x3A, 0x71, 0x00, 0xB7, 0xF2, 0x45, 0xC3, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0x18, 0xDB, 0xCD, 0x06, 0xC8, 0x43, 0x61, 0x6E, 0x27, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x8D, 0x1E, 0x01, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, 0xAF, 0xC9, 0x01, 0x10, 0x00, 0x11, 0x7A, 0x01, 0xCD, 0xEE, 0xC3, 0x0E, 0x01, 0xCD, 0xEE, 0xC3, 0x3A, 0x78, 0x01, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xC8, 0x3A, 0x7E, 0x00, 0xE6, 0xCC, 0xB0, 0x2A, 0x6A, 0x00, 0x77, 0xCD, 0xF6, 0xC3, 0x32, 0x7E, 0x00, 0xCB, 0x47, 0x11, 0x0A, 0x10, 0x21, 0x00, 0x02, 0x20, 0x0D, 0xCB, 0x4F, 0x11, 0x05, 0x08, 0x20, 0x06, 0x11, 0x12, 0x1A, 0x21, 0x80, 0x00, 0x22, 0x79, 0x00, 0xCB, 0x7F, 0x3E, 0x4C, 0x42, 0x28, 0x03, 0x3E, 0x27, 0x43, 0x32, 0x6F, 0x00, 0x78, 0x32, 0x6E, 0x00, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0x01, 0x10, 0x00, 0xCD, 0xDE, 0xC3, 0x0E, 0x01, 0xCD, 0xDE, 0xC3, 0xCD, 0x14, 0xC8, 0xFE, 0x43, 0x20, 0x06, 0xCB, 0xC8, 0x13, 0xCD, 0x14, 0xC8, 0xB7, 0xC2, 0xB9, 0xC1, 0x78, 0x32, 0x6D, 0x00, 0x37, 0xC9, 0xCD, 0x14, 0xC8, 0x13, 0xFE, 0x53, 0xC8, 0xFE, 0x44, 0xC2, 0xB9, 0xC1, 0x78, 0xB1, 0x47, 0xC9, 0x1A, 0x13, 0x13, 0xFE, 0x44, 0xC0, 0x18, 0xF4, 0x3A, 0x6C, 0x00, 0xD6, 0x41, 0x32, 0x77, 0x00, 0x21, 0x66, 0x00, 0xCD, 0x86, 0xC1, 0x22, 0x6A, 0x00, 0x7E, 0xB7, 0xC9, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, 0xC3, 0x55, 0xC1, 0xCD, 0x06, 0xC8, 0x20, 0x45, 0x72, 0x72, 0xAD, 0x3A, 0x70, 0x00, 0xCD, 0xFE, 0xC7, 0x3A, 0x71, 0x00, 0xCD, 0xDB, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x41, 0x2D, 0x44, 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x8D, 0xC3, 0x55, 0xC1, 0xCD, 0x27, 0xC8, 0x3A, 0x77, 0x00, 0xF5, 0x3A, 0x6C, 0x00, 0xF5, 0x3E, 0x41, 0xF5, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x2A, 0x6A, 0x00, 0x46, 0xC4, 0x63, 0xC4, 0xF1, 0x3C, 0xFE, 0x45, 0x38, 0xEC, 0xF1, 0x32, 0x6C, 0x00, 0xF1, 0x32, 0x77, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x20, 0xA0, 0xCD, 0xE2, 0xC1, 0xCD, 0x06, 0xC8, 0x3B, 0xA0, 0xCB, 0x58, 0xC4, 0xFC, 0xC7, 0xCB, 0x78, 0xCC, 0xFC, 0xC7, 0xCB, 0x60, 0xCD, 0x98, 0xC4, 0xCD, 0xFC, 0xC7, 0xCB, 0x40, 0xCD, 0x98, 0xC4, 0xCB, 0x48, 0x28, 0x0A, 0xCD, 0x06, 0xC8, 0x20, 0x43, 0x72, 0x6F, 0x6D, 0x69, 0xF8, 0xC3, 0x97, 0xCC, 0x3E, 0x44, 0x20, 0x02, 0x3E, 0x53, 0xC3, 0xFE, 0xC7, 0x47, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x78, 0xFE, 0x53, 0x28, 0x2C, 0xCD, 0xB0, 0xC8, 0xDA, 0xBD, 0xC1, 0xE5, 0xCD, 0xE1, 0xC4, 0x3E, 0x53, 0x32, 0x70, 0x00, 0xE1, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, 0x3A, 0x6F, 0x00, 0xBD, 0x38, 0xE8, 0x7D, 0x32, 0x75, 0x00, 0xCD, 0xD4, 0xC4, 0xD0, 0xC3, 0x0D, 0xC4, 0xCD, 0xF8, 0xC5, 0xC3, 0xCB, 0xCD, 0x13, 0xCD, 0xE1, 0xC4, 0xC3, 0xCB, 0xCD, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xCD, 0xAD, 0xC8, 0xD8, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, 0x7D, 0xB7, 0x20, 0x04, 0x32, 0x78, 0x00, 0xC9, 0x3D, 0x20, 0xF2, 0xCD, 0xF6, 0xC3, 0xCB, 0x67, 0x28, 0x06, 0x3E, 0x01, 0x32, 0x78, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x53, 0x2E, 0x53, 0x69, 0x64, 0x65, 0x64, 0x8D, 0xC3, 0x55, 0xC1, 0x06, 0x52, 0x18, 0x02, 0x06, 0x57, 0x1A, 0xFE, 0x44, 0xC2, 0xBD, 0xC1, 0x78, 0x32, 0x70, 0x00, 0xCD, 0xBB, 0xC5, 0xE5, 0xFD, 0xE1, 0x60, 0x69, 0xD5, 0xE5, 0x7B, 0x32, 0x76, 0x00, 0xFD, 0x22, 0x7B, 0x00, 0xCD, 0xD4, 0xC4, 0x3A, 0x70, 0x00, 0xFE, 0x57, 0x37, 0x3F, 0xCC, 0xAB, 0xC5, 0x38, 0x08, 0x3A, 0x70, 0x00, 0xFE, 0x52, 0xCC, 0x52, 0xCD, 0xE1, 0xD1, 0xDA, 0x0A, 0xC4, 0xED, 0x4B, 0x79, 0x00, 0xFD, 0x09, 0x03, 0xED, 0x42, 0x23, 0xDA, 0xF2, 0xC5, 0x1C, 0x3A, 0x6E, 0x00, 0xBB, 0x30, 0xC6, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x47, 0x3A, 0x6F, 0x00, 0xB8, 0x38, 0x08, 0x78, 0x32, 0x75, 0x00, 0x1E, 0x01, 0x18, 0xB0, 0x1D, 0xCD, 0xF2, 0xC5, 0xCD, 0x06, 0xC8, 0x4E, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x3A, 0xA0, 0xFD, 0xE5, 0xE1, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x45, 0x6E, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x44, 0x69, 0x73, 0x6B, 0x8D, 0xC3, 0x55, 0xC1, 0x21, 0x7E, 0x00, 0x46, 0xC5, 0xCB, 0xCE, 0xCD, 0x5F, 0xCD, 0xC1, 0x78, 0x32, 0x7E, 0x00, 0xC9, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x13, 0xCD, 0x2F, 0xC8, 0xAF, 0xB2, 0xC2, 0xBD, 0xC1, 0xB3, 0xCA, 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0xCD, 0xF8, 0xC5, 0xE1, 0xD1, 0xC1, 0x3A, 0x6E, 0x00, 0xBB, 0x38, 0xEA, 0xDB, 0x31, 0x57, 0xEB, 0xCD, 0xFC, 0xC7, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0xFC, 0xC7, 0x3A, 0x78, 0x00, 0xC3, 0xF0, 0xC7, 0xCD, 0xDE, 0xC5, 0xC3, 0x97, 0xCC, 0x3A, 0x75, 0x00, 0xB7, 0x20, 0x20, 0x3A, 0x78, 0x00, 0xB7, 0x20, 0x1A, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC0, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, 0xC8, 0x32, 0x7F, 0x00, 0xCB, 0xEF, 0xCB, 0x87, 0x32, 0x7E, 0x00, 0xC3, 0x9B, 0xC3, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC8, 0x3A, 0x7F, 0x00, 0xC3, 0x87, 0xC3, 0xCD, 0x53, 0xC8, 0x4D, 0xED, 0x78, 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0xAD, 0xC8, 0xE5, 0xCD, 0x1B, 0xC8, 0xCD, 0x53, 0xC8, 0xD1, 0x4D, 0xED, 0x59, 0xC9, 0xCD, 0x2F, 0xC8, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, 0xD1, 0xC1, 0x18, 0x03, 0xCD, 0x2F, 0xC8, 0x1A, 0xBE, 0x28, 0x19, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0x1A, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xEB, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0x97, 0xCC, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xDC, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0xC2, 0xA1, 0xC4, 0x13, 0x2A, 0x64, 0x00, 0xCD, 0x5B, 0xC8, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, 0x03, 0x2B, 0x18, 0x1A, 0xCD, 0xF1, 0xC6, 0x30, 0x07, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0xDA, 0xAF, 0x80, 0x20, 0x03, 0x23, 0x18, 0x07, 0x48, 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0x22, 0x64, 0x00, 0x18, 0xC7, 0xCD, 0x65, 0xC8, 0xC5, 0xCD, 0xF1, 0xC6, 0xD1, 0xDA, 0xBD, 0xC1, 0xAF, 0x80, 0xCA, 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0x11, 0x2E, 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x60, 0xC7, 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x2E, 0x00, 0xCD, 0x14, 0xC8, 0xB7, 0x28, 0x29, 0xFE, 0x2C, 0x13, 0x28, 0xF5, 0x4F, 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xAD, 0xC8, 0x7D, 0xE1, 0x38, 0x12, 0x77, 0x23, 0x04, 0x18, 0xDE, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, 0xD6, 0x77, 0x23, 0x04, 0x18, 0xF3, 0x11, 0x2E, 0x00, 0xE1, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0x20, 0x01, 0x13, 0x01, 0x80, 0x00, 0x2A, 0x62, 0x00, 0xCD, 0x4E, 0xC8, 0x1E, 0x10, 0xAF, 0xB0, 0x20, 0x0A, 0x3E, 0x0F, 0xB9, 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x60, 0xC7, 0x22, 0x62, 0x00, 0xC1, 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, 0xCD, 0xE2, 0xC7, 0xC5, 0xE5, 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xA0, 0xC7, 0xCD, 0xA0, 0xC7, 0x7E, 0xCD, 0xE7, 0xC7, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xA0, 0xC7, 0x3E, 0x3A, 0xBB, 0x20, 0xF8, 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x92, 0xC7, 0x10, 0xF9, 0xC3, 0x97, 0xCC, 0xE6, 0x7F, 0xFE, 0x7F, 0x28, 0x04, 0xFE, 0x20, 0x30, 0x62, 0x3E, 0x2E, 0x18, 0x5E, 0x1C, 0x18, 0x59, 0x3E, 0x31, 0x11, 0x2E, 0x00, 0x12, 0xD5, 0xCD, 0xEA, 0xCB, 0xE3, 0x23, 0x46, 0x04, 0x23, 0xE5, 0x05, 0x28, 0x17, 0x7E, 0xCD, 0xD2, 0xC7, 0x77, 0x23, 0xFE, 0x22, 0x28, 0x04, 0xFE, 0x27, 0x20, 0xEF, 0x05, 0x28, 0x06, 0xBE, 0x23, 0x20, 0xF9, 0x18, 0xE6, 0x36, 0x00, 0xD1, 0xE1, 0xC9, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0xCD, 0xFC, 0xC7, 0xF1, 0x18, 0x05, 0x7C, 0xCD, 0xE7, 0xC7, 0x7D, 0xF5, 0x1F, 0x1F, 0x1F, 0x1F, 0xCD, 0xF0, 0xC7, 0xF1, 0xE6, 0x0F, 0xFE, 0x0A, 0x38, 0x02, 0xC6, 0x07, 0xC6, 0x30, 0x18, 0x02, 0x3E, 0x20, 0xF5, 0xE6, 0x7F, 0xCD, 0x99, 0xCC, 0xF1, 0xC9, 0xE3, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xFE, 0xC7, 0xF2, 0x07, 0xC8, 0xE3, 0xC9, 0x1A, 0xFE, 0x20, 0xC0, 0x13, 0x18, 0xF9, 0xCD, 0x14, 0xC8, 0xFE, 0x2C, 0xC0, 0x13, 0xCD, 0x14, 0xC8, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0xC3, 0xBD, 0xC1, 0x21, 0x80, 0x00, 0x44, 0x4D, 0xCD, 0x68, 0xC8, 0xE5, 0xC5, 0xCD, 0x1B, 0xC8, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0xCD, 0x27, 0xC8, 0xEB, 0xC1, 0xE1, 0xC9, 0xCD, 0x65, 0xC8, 0x18, 0xD9, 0xCD, 0x68, 0xC8, 0x18, 0xD4, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0x18, 0xCC, 0xE5, 0xCD, 0xAD, 0xC8, 0x38, 0x01, 0xE3, 0xE1, 0x18, 0xC2, 0x37, 0x18, 0x01, 0xB7, 0x08, 0xC5, 0xE5, 0xCD, 0xAD, 0xC8, 0x30, 0x08, 0x08, 0xDA, 0xBD, 0xC1, 0x08, 0xE1, 0x18, 0x01, 0xF1, 0xCD, 0x87, 0xC8, 0x30, 0x06, 0x08, 0xC1, 0xD0, 0xC3, 0xBD, 0xC1, 0xF1, 0xC9, 0xCD, 0x1B, 0xC8, 0x1A, 0xFE, 0x53, 0x20, 0x01, 0x13, 0xE5, 0xF5, 0xCD, 0xAD, 0xC8, 0x38, 0x0F, 0x44, 0x4D, 0xF1, 0xE1, 0x28, 0x07, 0x79, 0x95, 0x4F, 0x78, 0x9C, 0x47, 0x03, 0xB7, 0xC9, 0xF1, 0xE1, 0xCA, 0xBD, 0xC1, 0x37, 0xC9, 0xCD, 0x14, 0xC8, 0xCD, 0xE0, 0xC8, 0xD8, 0xD5, 0x13, 0xCD, 0xE0, 0xC8, 0x30, 0xFA, 0xD1, 0xFE, 0x2E, 0x28, 0x05, 0xCD, 0xCA, 0xC8, 0xA7, 0xC9, 0xCD, 0xF4, 0xC8, 0xA7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xE0, 0xC8, 0x38, 0x09, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x13, 0x18, 0xF2, 0xFE, 0x48, 0xC0, 0x13, 0xC9, 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x38, 0x09, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, 0x07, 0xD6, 0x30, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0x10, 0xC9, 0x13, 0x38, 0x0D, 0xC5, 0x29, 0x44, 0x4D, 0x29, 0x29, 0x09, 0xC1, 0xCD, 0x86, 0xC1, 0x18, 0xED, 0xFE, 0x2E, 0xC8, 0xC3, 0xBD, 0xC1, 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0x01, 0x18, 0x00, 0x11, 0x00, 0x01, 0x21, 0x29, 0xC9, 0xED, 0xB0, 0xC3, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x00, 0x10, 0x21, 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0x3E, 0x01, 0xD3, 0x40, 0xD1, 0xE1, 0xC1, 0xED, 0xB0, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x5A, 0xF5, 0x20, 0x01, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC4, 0x1B, 0xC9, 0xCD, 0x06, 0xC8, 0x0D, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0xBA, 0x01, 0x00, 0x10, 0xCD, 0xFC, 0xC7, 0x79, 0xCD, 0xF0, 0xC7, 0x0C, 0x10, 0xF6, 0xCD, 0x97, 0xCC, 0x06, 0x07, 0xCD, 0xFC, 0xC7, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0xFC, 0xC7, 0xE5, 0x11, 0x00, 0x10, 0x7C, 0xFE, 0xC9, 0x20, 0x09, 0x7D, 0xFE, 0x8D, 0x38, 0x04, 0xFE, 0x9C, 0x38, 0x12, 0x46, 0x3E, 0x55, 0x77, 0xBE, 0x20, 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x70, 0x3E, 0x58, 0x18, 0x09, 0x70, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xDA, 0x3E, 0x5E, 0xCD, 0xFE, 0xC7, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x28, 0x65, 0x67, 0x2C, 0x20, 0x41, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x3B, 0x29, 0xA0, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x97, 0xCC, 0xCD, 0xC1, 0xC2, 0xC2, 0xB9, 0xC1, 0xCD, 0x06, 0xC8, 0x53, 0x65, 0x65, 0x6B, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x3A, 0x8D, 0x06, 0x15, 0x21, 0x20, 0xCA, 0xDB, 0x31, 0x32, 0x7D, 0x00, 0x7E, 0xC5, 0xE5, 0xFE, 0xFF, 0x20, 0x2E, 0xCD, 0x06, 0xC8, 0x20, 0x52, 0x65, 0x73, 0x74, 0x6F, 0x72, 0x65, 0xBA, 0xCD, 0x04, 0xC6, 0xCD, 0x37, 0xCD, 0x08, 0xCD, 0x1E, 0xC6, 0x08, 0x18, 0x2C, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFE, 0x06, 0x07, 0x08, 0x09, 0x00, 0xFE, 0x27, 0x00, 0x15, 0x00, 0x01, 0xFE, 0xFF, 0xFE, 0x27, 0xFE, 0xFE, 0x20, 0x06, 0xCD, 0x97, 0xCC, 0xB7, 0x18, 0x12, 0x32, 0x75, 0x00, 0xCD, 0xDB, 0xC7, 0xCD, 0x06, 0xC8, 0xBA, 0xCD, 0xD4, 0xC4, 0xF5, 0xCD, 0x7A, 0xCB, 0xF1, 0xE1, 0xC1, 0xDA, 0x97, 0xCC, 0x23, 0x10, 0xA2, 0xCD, 0x06, 0xC8, 0x0D, 0x52, 0x65, 0x61, 0x64, 0x2F, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x8D, 0xCD, 0x94, 0xCB, 0xCD, 0xB0, 0xCB, 0xDA, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0D, 0x45, 0x53, 0x43, 0x3D, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x20, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x3D, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0xA0, 0xCD, 0xA3, 0xC7, 0x21, 0x00, 0x09, 0xCD, 0xA2, 0xCB, 0x01, 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF9, 0xCD, 0xC1, 0xCB, 0x30, 0x6E, 0xCD, 0x06, 0xC8, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0xF4, 0x1E, 0x01, 0xCD, 0xDE, 0xC5, 0xCD, 0x06, 0xC8, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6F, 0x79, 0x65, 0x64, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x20, 0x69, 0x73, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x30, 0x44, 0x30, 0x30, 0xF3, 0x2A, 0x79, 0x00, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x8D, 0xC9, 0x21, 0x00, 0x0B, 0xCD, 0xA2, 0xCB, 0xCD, 0xB0, 0xCB, 0x38, 0x87, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0x65, 0xA0, 0xED, 0x4B, 0x79, 0x00, 0x11, 0x00, 0x09, 0x21, 0x00, 0x0B, 0x1A, 0xBE, 0x20, 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x01, 0x37, 0xCD, 0xD1, 0xCB, 0xCD, 0x94, 0xCB, 0xCD, 0xC1, 0xCB, 0xDA, 0xC7, 0xCA, 0xC3, 0x97, 0xCC, 0x38, 0x06, 0xCD, 0x06, 0xC8, 0x4F, 0xCB, 0xC9, 0xCD, 0x06, 0xC8, 0x65, 0x72, 0x72, 0x6F, 0x72, 0xA0, 0x3A, 0x71, 0x00, 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x44, 0x61, 0x74, 0xE1, 0x21, 0x00, 0x0D, 0x22, 0x7B, 0x00, 0xC9, 0x22, 0x7B, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0xEE, 0xC9, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x72, 0x65, 0x61, 0x64, 0xA0, 0xCD, 0x52, 0xCD, 0x18, 0x10, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0xA0, 0xCD, 0xAB, 0xC5, 0xF5, 0xCD, 0x7A, 0xCB, 0xCD, 0x97, 0xCC, 0xF1, 0xC9, 0x3E, 0x27, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3E, 0x01, 0x32, 0x76, 0x00, 0xC3, 0xCB, 0xCD, 0xC5, 0xE5, 0x06, 0x00, 0x21, 0x02, 0x00, 0x19, 0xCD, 0x64, 0xCC, 0xFE, 0x10, 0xCC, 0xBB, 0xCC, 0x28, 0xF6, 0xFE, 0x1B, 0xCA, 0x8E, 0xCC, 0xFE, 0x08, 0x28, 0x04, 0xFE, 0x7F, 0x20, 0x11, 0xAF, 0xB0, 0x28, 0xE5, 0x2B, 0x05, 0xCD, 0x4B, 0xCC, 0x7E, 0xFE, 0x20, 0xDC, 0x4B, 0xCC, 0x18, 0xD8, 0xFE, 0x0D, 0xCC, 0x45, 0xCC, 0x28, 0x1B, 0xFE, 0x15, 0x20, 0x08, 0xCD, 0x45, 0xCC, 0xCD, 0x97, 0xCC, 0x18, 0xBF, 0x4F, 0x1A, 0x3D, 0xB8, 0x28, 0xBF, 0x79, 0xCD, 0x45, 0xCC, 0x77, 0x23, 0x04, 0x18, 0xB6, 0x36, 0x00, 0x78, 0x13, 0x12, 0x1B, 0xE1, 0xC1, 0xC9, 0xF5, 0xCD, 0x52, 0xCC, 0xF1, 0xC9, 0xCD, 0x06, 0xC8, 0x08, 0x20, 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x43, 0xFE, 0x0D, 0x28, 0x3F, 0xF5, 0xCD, 0x06, 0xC8, 0xDE, 0xF1, 0xC6, 0x40, 0x18, 0x35, 0xCD, 0x23, 0xCD, 0xCD, 0x76, 0xCC, 0x28, 0xF8, 0xCD, 0x76, 0xCC, 0x28, 0xFB, 0xDB, 0x01, 0xE6, 0x7F, 0xC9, 0xDB, 0x00, 0xE6, 0x40, 0xC9, 0xCD, 0x76, 0xCC, 0xC8, 0xCD, 0x6C, 0xCC, 0xFE, 0x13, 0xCC, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0x2E, 0x00, 0xCD, 0x97, 0xCC, 0xC3, 0x55, 0xC1, 0x3E, 0x0D, 0xF5, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0xCB, 0xCC, 0xCD, 0x23, 0xCD, 0xDB, 0x00, 0xE6, 0x80, 0x28, 0xFA, 0xF1, 0xD3, 0x01, 0xFE, 0x0D, 0xC0, 0x3E, 0x0A, 0xCD, 0x99, 0xCC, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0xC0, 0xF5, 0xD9, 0x3E, 0x80, 0xA8, 0x47, 0xCB, 0x78, 0xD9, 0xC4, 0xC9, 0xCC, 0xF1, 0xC9, 0x3E, 0x11, 0xF5, 0xE5, 0x21, 0x08, 0x00, 0xCD, 0xF1, 0xCF, 0xE1, 0xF1, 0xF5, 0xFE, 0x11, 0x28, 0x13, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0x20, 0x05, 0xCD, 0xBB, 0xCC, 0xF1, 0xC9, 0xDB, 0x54, 0x2F, 0xE6, 0x20, 0x28, 0xED, 0xF1, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4F, 0xC2, 0xBD, 0xC1, 0x13, 0x1A, 0xFE, 0x4E, 0x28, 0x0E, 0xFE, 0x46, 0xC2, 0xBD, 0xC1, 0xD9, 0xCB, 0xA8, 0xD9, 0x3E, 0xFF, 0xD3, 0x04, 0xC9, 0xD9, 0xCB, 0xE8, 0xD9, 0x3E, 0x01, 0x32, 0x72, 0x00, 0xC9, 0xD9, 0xCB, 0x68, 0xD9, 0xC8, 0xC5, 0xCD, 0xA8, 0xCF, 0xC1, 0xD3, 0x34, 0xCD, 0xD6, 0xCF, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0x16, 0x02, 0xD5, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0xD5, 0x3E, 0x0A, 0x32, 0x75, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0x15, 0x20, 0xE9, 0x18, 0x18, 0x16, 0x0A, 0xD5, 0xCD, 0x58, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x18, 0x0B, 0x16, 0x04, 0xD5, 0xCD, 0x9F, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x3A, 0x71, 0x00, 0x4F, 0x37, 0xC9, 0x97, 0x32, 0x75, 0x00, 0x32, 0x78, 0x00, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0xCD, 0x25, 0xCE, 0x38, 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, 0xF7, 0xC3, 0x3E, 0xCE, 0x3E, 0xC4, 0xD3, 0x30, 0xCD, 0xD6, 0xCF, 0xE6, 0x57, 0xD3, 0x04, 0xCD, 0xE4, 0xCF, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0x16, 0xCD, 0xCF, 0xCF, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x18, 0x1F, 0x30, 0xF7, 0x3E, 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0x50, 0xCE, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x05, 0x1F, 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0x97, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0x3A, 0x75, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x76, 0x00, 0xD3, 0x32, 0x3A, 0x7D, 0x00, 0xD3, 0x31, 0x91, 0xCA, 0xCF, 0xCF, 0xCD, 0x25, 0xCE, 0x38, 0x0F, 0xF6, 0x10, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x45, 0xCD, 0xD6, 0xCF, 0xE6, 0x4F, 0xD3, 0x04, 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, 0x30, 0xF7, 0xDB, 0x30, 0x2E, 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0xCD, 0xCF, 0xCF, 0x18, 0x2B, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x0A, 0xCB, 0x5F, 0x3E, 0x0E, 0x28, 0x02, 0x3E, 0x0C, 0xA7, 0xC9, 0xCB, 0x5F, 0x3E, 0x0F, 0x28, 0xF8, 0x37, 0xC9, 0xCD, 0xCF, 0xCF, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x98, 0x37, 0xC0, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xA7, 0xC9, 0xCD, 0x8F, 0xCE, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x16, 0xED, 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0x5D, 0xCE, 0xDB, 0x34, 0xCB, 0x4F, 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0xC8, 0x18, 0x08, 0xCD, 0xCF, 0xCF, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, 0xE4, 0xCF, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, 0xED, 0xA3, 0xC2, 0xB3, 0xCE, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0xCF, 0xCF, 0xCD, 0xE4, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x7E, 0x00, 0xCB, 0x4F, 0xC8, 0xCD, 0x00, 0xCF, 0x38, 0x0A, 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, 0xCD, 0x8F, 0xCE, 0xED, 0x4B, 0x79, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, 0x1E, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, 0xDB, 0x33, 0x3E, 0x80, 0xCD, 0x55, 0xCF, 0x2A, 0x79, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, 0x33, 0x2A, 0x7B, 0x00, 0xC9, 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x72, 0x00, 0xCD, 0xA8, 0xCF, 0xD3, 0x34, 0xF5, 0xE5, 0xCD, 0xD6, 0xCF, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x72, 0x00, 0xA7, 0x28, 0x21, 0x21, 0x90, 0x01, 0x3A, 0x73, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x74, 0x00, 0x67, 0x3A, 0x78, 0x00, 0x32, 0x74, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, 0xCD, 0xE4, 0xCF, 0x18, 0x06, 0x21, 0x20, 0x4E, 0xCD, 0xF1, 0xCF, 0xE1, 0x78, 0x32, 0x73, 0x00, 0x3E, 0x01, 0x32, 0x72, 0x00, 0xF1, 0xB1, 0xC9, 0x3A, 0x77, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, 0x10, 0xFD, 0x47, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, 0xCB, 0xF0, 0x78, 0xF6, 0x20, 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, 0xD6, 0xCF, 0xD3, 0x04, 0xAF, 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x78, 0x00, 0xA7, 0x28, 0x02, 0x06, 0x7D, 0x78, 0xC1, 0xC9, 0x21, 0x08, 0x00, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, 0x00, 0xC5, 0x2B, 0x06, 0x1C, 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0xFF, 0xF3, 0x18, 0x3C, 0xC3, 0x30, 0xC0, 0xC3, 0x04, 0xC5, 0xC3, 0x37, 0xC0, 0xC3, 0x3B, 0xC0, 0xC3, 0x9E, 0xCB, 0xC3, 0x37, 0xCD, 0xC3, 0xD4, 0xC4, 0xC3, 0x52, 0xCD, 0xC3, 0xAB, 0xC5, 0xC3, 0x4B, 0xC2, 0xC3, 0x6C, 0xCC, 0xC3, 0x76, 0xCC, 0xC3, 0xEA, 0xCB, 0xC3, 0xFE, 0xC7, 0xC3, 0x97, 0xCC, 0x32, 0x77, 0x00, 0x78, 0xC3, 0x87, 0xC3, 0x32, 0x75, 0x00, 0xC9, 0x32, 0x76, 0x00, 0xC9, 0xAF, 0xD3, 0x03, 0x47, 0xD9, 0x2F, 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x21, 0x2E, 0x00, 0xF9, 0x25, 0x20, 0xFD, 0x74, 0x2C, 0x20, 0xFC, 0x24, 0x22, 0x62, 0x00, 0x22, 0x64, 0x00, 0xCD, 0x4B, 0xC2, 0xDB, 0x34, 0xE6, 0x40, 0xC2, 0x39, 0xC1, 0xDB, 0x04, 0x2F, 0xE6, 0x03, 0x32, 0x77, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x8D, 0xCD, 0xA8, 0xCF, 0x57, 0xD3, 0x34, 0x06, 0x64, 0xCD, 0x28, 0xC1, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0x10, 0xF5, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x32, 0x78, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0xF7, 0xC1, 0x32, 0x7E, 0x00, 0x21, 0x80, 0x00, 0x22, 0x7B, 0x00, 0x22, 0x79, 0x00, 0x3E, 0x42, 0x32, 0x70, 0x00, 0x06, 0x02, 0xC5, 0xCD, 0x37, 0xCD, 0xD4, 0xCB, 0xCD, 0xD4, 0x52, 0xCD, 0xC1, 0x30, 0x13, 0xCD, 0x28, 0xC1, 0x10, 0xEE, 0xCD, 0x06, 0xC8, 0x0D, 0x55, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x20, 0xF4, 0x18, 0x2D, 0x3A, 0x80, 0x00, 0xFE, 0x40, 0x28, 0x04, 0xFE, 0xE5, 0x20, 0x07, 0xCD, 0x06, 0xC8, 0x0D, 0xCE, 0x18, 0x1B, 0xCD, 0x28, 0xC1, 0xCD, 0xA8, 0xCF, 0x57, 0x3A, 0x77, 0x00, 0x08, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, 0x8D, 0x37, 0xC3, 0x80, 0x00, 0xCD, 0x06, 0xC8, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3A, 0x71, 0x00, 0xB7, 0xC4, 0x59, 0xC3, 0x18, 0x09, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x1B, 0xC0, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, 0x7C, 0xD3, 0x04, 0xCD, 0x06, 0xC8, 0x0D, 0x43, 0x72, 0x6F, 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, 0x53, 0x20, 0x30, 0x32, 0x2E, 0x35, 0x32, 0x8D, 0xAF, 0x32, 0x6C, 0x00, 0x3A, 0x7E, 0x00, 0x47, 0xCD, 0xE2, 0xC1, 0x31, 0x2E, 0x00, 0xCD, 0x06, 0xC8, 0xBB, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xA7, 0x28, 0xE5, 0xCD, 0xC1, 0xC2, 0x28, 0xE4, 0x78, 0xFE, 0x17, 0x30, 0x47, 0x87, 0x21, 0x8B, 0xC1, 0xCD, 0x86, 0xC1, 0x7E, 0x23, 0x66, 0x6F, 0xCD, 0xC7, 0xC1, 0x18, 0xCF, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xFD, 0xCC, 0xC8, 0xC1, 0xBD, 0xC1, 0x2B, 0xC7, 0x2A, 0xC6, 0xBD, 0xC1, 0xC4, 0xC1, 0xBD, 0xC1, 0x45, 0xC2, 0xBD, 0xC1, 0xBD, 0xC1, 0x39, 0xC4, 0x45, 0xC6, 0xBD, 0xC1, 0x36, 0xC6, 0xBD, 0xC1, 0xC2, 0xC6, 0x16, 0xC5, 0x7A, 0xC6, 0x41, 0xC9, 0xBD, 0xC1, 0x52, 0xC6, 0x1A, 0xC5, 0xAF, 0x32, 0x6C, 0x00, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0x91, 0xCD, 0x53, 0xC8, 0xE9, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x67, 0xC0, 0xFE, 0x45, 0xD2, 0x2A, 0xC4, 0xD6, 0x41, 0xDA, 0x2A, 0xC4, 0xF5, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC3, 0x6C, 0xC0, 0x3A, 0x6C, 0x00, 0xB7, 0xC8, 0xCD, 0xFE, 0xC7, 0xCB, 0x58, 0xC0, 0xCB, 0x78, 0x3E, 0x3B, 0xC4, 0xFE, 0xC7, 0xC3, 0xFE, 0xC7, 0x7A, 0xD3, 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, 0x01, 0x00, 0x02, 0xCD, 0x3B, 0xC2, 0x3E, 0xD4, 0xD3, 0x30, 0xCD, 0x34, 0xC2, 0x28, 0xDF, 0xDB, 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0xCD, 0x27, 0xC8, 0xCD, 0x97, 0xCC, 0xDB, 0x04, 0xE6, 0x08, 0x28, 0x5D, 0x3E, 0x0A, 0xD3, 0x02, 0x21, 0xD0, 0x07, 0xCD, 0xF1, 0xCF, 0x3E, 0x08, 0xD3, 0x02, 0x16, 0x64, 0x15, 0x28, 0xED, 0x21, 0xB9, 0xC2, 0x0E, 0x00, 0x3E, 0x19, 0x06, 0x09, 0xD3, 0x02, 0xED, 0xA3, 0x28, 0xEE, 0xCD, 0x98, 0xC2, 0xCD, 0x98, 0xC2, 0x38, 0xE9, 0xFE, 0x0D, 0x3E, 0x09, 0x20, 0xEC, 0x3E, 0x0D, 0xD3, 0x01, 0x21, 0xA0, 0x0F, 0xCD, 0xF1, 0xCF, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0xEC, 0xC3, 0x97, 0xCC, 0xD5, 0x11, 0xA0, 0x8C, 0xCD, 0x76, 0xCC, 0x28, 0x05, 0xCD, 0x6C, 0xCC, 0x18, 0x06, 0x1B, 0x7A, 0xB3, 0x20, 0xF1, 0x37, 0xD1, 0xC9, 0x3E, 0x09, 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xC3, 0x97, 0xCC, 0x90, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x01, 0xD6, 0x41, 0xDA, 0xBD, 0xC1, 0x47, 0x13, 0x1A, 0xFE, 0x3B, 0xC0, 0x78, 0xFE, 0x04, 0xD2, 0x2A, 0xC4, 0xC6, 0x41, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x04, 0x20, 0x03, 0x06, 0x80, 0x13, 0xC5, 0xCD, 0xB7, 0xC3, 0xC1, 0x70, 0x30, 0x06, 0x3A, 0x6D, 0x00, 0xB0, 0x77, 0x37, 0xF5, 0xCD, 0x84, 0xC3, 0x32, 0x78, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0x04, 0xC6, 0x3E, 0x48, 0x32, 0x70, 0x00, 0xCD, 0x37, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0xDA, 0x0D, 0xC4, 0xF1, 0xDA, 0xB5, 0xC3, 0xCD, 0x04, 0xC6, 0x21, 0x00, 0x01, 0x22, 0x7B, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x52, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0x30, 0x2D, 0x3A, 0x71, 0x00, 0xB7, 0xF2, 0x45, 0xC3, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0x18, 0xDB, 0xCD, 0x06, 0xC8, 0x43, 0x61, 0x6E, 0x27, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x8D, 0x1E, 0x01, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, 0xAF, 0xC9, 0x01, 0x10, 0x00, 0x11, 0x7A, 0x01, 0xCD, 0xEE, 0xC3, 0x0E, 0x01, 0xCD, 0xEE, 0xC3, 0x3A, 0x78, 0x01, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xC8, 0x3A, 0x7E, 0x00, 0xE6, 0xCC, 0xB0, 0x2A, 0x6A, 0x00, 0x77, 0xCD, 0xF6, 0xC3, 0x32, 0x7E, 0x00, 0xCB, 0x47, 0x11, 0x0A, 0x10, 0x21, 0x00, 0x02, 0x20, 0x0D, 0xCB, 0x4F, 0x11, 0x05, 0x08, 0x20, 0x06, 0x11, 0x12, 0x1A, 0x21, 0x80, 0x00, 0x22, 0x79, 0x00, 0xCB, 0x7F, 0x3E, 0x4C, 0x42, 0x28, 0x03, 0x3E, 0x27, 0x43, 0x32, 0x6F, 0x00, 0x78, 0x32, 0x6E, 0x00, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0x01, 0x10, 0x00, 0xCD, 0xDE, 0xC3, 0x0E, 0x01, 0xCD, 0xDE, 0xC3, 0xCD, 0x14, 0xC8, 0xFE, 0x43, 0x20, 0x06, 0xCB, 0xC8, 0x13, 0xCD, 0x14, 0xC8, 0xB7, 0xC2, 0xB9, 0xC1, 0x78, 0x32, 0x6D, 0x00, 0x37, 0xC9, 0xCD, 0x14, 0xC8, 0x13, 0xFE, 0x53, 0xC8, 0xFE, 0x44, 0xC2, 0xB9, 0xC1, 0x78, 0xB1, 0x47, 0xC9, 0x1A, 0x13, 0x13, 0xFE, 0x44, 0xC0, 0x18, 0xF4, 0x3A, 0x6C, 0x00, 0xD6, 0x41, 0x32, 0x77, 0x00, 0x21, 0x66, 0x00, 0xCD, 0x86, 0xC1, 0x22, 0x6A, 0x00, 0x7E, 0xB7, 0xC9, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, 0xC3, 0x55, 0xC1, 0xCD, 0x06, 0xC8, 0x20, 0x45, 0x72, 0x72, 0xAD, 0x3A, 0x70, 0x00, 0xCD, 0xFE, 0xC7, 0x3A, 0x71, 0x00, 0xCD, 0xDB, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x41, 0x2D, 0x44, 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x8D, 0xC3, 0x55, 0xC1, 0xCD, 0x27, 0xC8, 0x3A, 0x77, 0x00, 0xF5, 0x3A, 0x6C, 0x00, 0xF5, 0x3E, 0x41, 0xF5, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x2A, 0x6A, 0x00, 0x46, 0xC4, 0x63, 0xC4, 0xF1, 0x3C, 0xFE, 0x45, 0x38, 0xEC, 0xF1, 0x32, 0x6C, 0x00, 0xF1, 0x32, 0x77, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x20, 0xA0, 0xCD, 0xE2, 0xC1, 0xCD, 0x06, 0xC8, 0x3B, 0xA0, 0xCB, 0x58, 0xC4, 0xFC, 0xC7, 0xCB, 0x78, 0xCC, 0xFC, 0xC7, 0xCB, 0x60, 0xCD, 0x98, 0xC4, 0xCD, 0xFC, 0xC7, 0xCB, 0x40, 0xCD, 0x98, 0xC4, 0xCB, 0x48, 0x28, 0x0A, 0xCD, 0x06, 0xC8, 0x20, 0x43, 0x72, 0x6F, 0x6D, 0x69, 0xF8, 0xC3, 0x97, 0xCC, 0x3E, 0x44, 0x20, 0x02, 0x3E, 0x53, 0xC3, 0xFE, 0xC7, 0x47, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x78, 0xFE, 0x53, 0x28, 0x2C, 0xCD, 0xB0, 0xC8, 0xDA, 0xBD, 0xC1, 0xE5, 0xCD, 0xE1, 0xC4, 0x3E, 0x53, 0x32, 0x70, 0x00, 0xE1, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, 0x3A, 0x6F, 0x00, 0xBD, 0x38, 0xE8, 0x7D, 0x32, 0x75, 0x00, 0xCD, 0xD4, 0xC4, 0xD0, 0xC3, 0x0D, 0xC4, 0xCD, 0xF8, 0xC5, 0xC3, 0xCB, 0xCD, 0x13, 0xCD, 0xE1, 0xC4, 0xC3, 0xCB, 0xCD, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xCD, 0xAD, 0xC8, 0xD8, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, 0x7D, 0xB7, 0x20, 0x04, 0x32, 0x78, 0x00, 0xC9, 0x3D, 0x20, 0xF2, 0xCD, 0xF6, 0xC3, 0xCB, 0x67, 0x28, 0x06, 0x3E, 0x01, 0x32, 0x78, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x53, 0x2E, 0x53, 0x69, 0x64, 0x65, 0x64, 0x8D, 0xC3, 0x55, 0xC1, 0x06, 0x52, 0x18, 0x02, 0x06, 0x57, 0x1A, 0xFE, 0x44, 0xC2, 0xBD, 0xC1, 0x78, 0x32, 0x70, 0x00, 0xCD, 0xBB, 0xC5, 0xE5, 0xFD, 0xE1, 0x60, 0x69, 0xD5, 0xE5, 0x7B, 0x32, 0x76, 0x00, 0xFD, 0x22, 0x7B, 0x00, 0xCD, 0xD4, 0xC4, 0x3A, 0x70, 0x00, 0xFE, 0x57, 0x37, 0x3F, 0xCC, 0xAB, 0xC5, 0x38, 0x08, 0x3A, 0x70, 0x00, 0xFE, 0x52, 0xCC, 0x52, 0xCD, 0xE1, 0xD1, 0xDA, 0x0A, 0xC4, 0xED, 0x4B, 0x79, 0x00, 0xFD, 0x09, 0x03, 0xED, 0x42, 0x23, 0xDA, 0xF2, 0xC5, 0x1C, 0x3A, 0x6E, 0x00, 0xBB, 0x30, 0xC6, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x47, 0x3A, 0x6F, 0x00, 0xB8, 0x38, 0x08, 0x78, 0x32, 0x75, 0x00, 0x1E, 0x01, 0x18, 0xB0, 0x1D, 0xCD, 0xF2, 0xC5, 0xCD, 0x06, 0xC8, 0x4E, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x3A, 0xA0, 0xFD, 0xE5, 0xE1, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x45, 0x6E, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x44, 0x69, 0x73, 0x6B, 0x8D, 0xC3, 0x55, 0xC1, 0x21, 0x7E, 0x00, 0x46, 0xC5, 0xCB, 0xCE, 0xCD, 0x5F, 0xCD, 0xC1, 0x78, 0x32, 0x7E, 0x00, 0xC9, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x13, 0xCD, 0x2F, 0xC8, 0xAF, 0xB2, 0xC2, 0xBD, 0xC1, 0xB3, 0xCA, 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0xCD, 0xF8, 0xC5, 0xE1, 0xD1, 0xC1, 0x3A, 0x6E, 0x00, 0xBB, 0x38, 0xEA, 0xDB, 0x31, 0x57, 0xEB, 0xCD, 0xFC, 0xC7, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0xFC, 0xC7, 0x3A, 0x78, 0x00, 0xC3, 0xF0, 0xC7, 0xCD, 0xDE, 0xC5, 0xC3, 0x97, 0xCC, 0x3A, 0x75, 0x00, 0xB7, 0x20, 0x20, 0x3A, 0x78, 0x00, 0xB7, 0x20, 0x1A, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC0, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, 0xC8, 0x32, 0x7F, 0x00, 0xCB, 0xEF, 0xCB, 0x87, 0x32, 0x7E, 0x00, 0xC3, 0x9B, 0xC3, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC8, 0x3A, 0x7F, 0x00, 0xC3, 0x87, 0xC3, 0xCD, 0x53, 0xC8, 0x4D, 0xED, 0x78, 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0xAD, 0xC8, 0xE5, 0xCD, 0x1B, 0xC8, 0xCD, 0x53, 0xC8, 0xD1, 0x4D, 0xED, 0x59, 0xC9, 0xCD, 0x2F, 0xC8, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, 0xD1, 0xC1, 0x18, 0x03, 0xCD, 0x2F, 0xC8, 0x1A, 0xBE, 0x28, 0x19, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0x1A, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xEB, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0x97, 0xCC, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xDC, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0xC2, 0xA1, 0xC4, 0x13, 0x2A, 0x64, 0x00, 0xCD, 0x5B, 0xC8, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, 0x03, 0x2B, 0x18, 0x1A, 0xCD, 0xF1, 0xC6, 0x30, 0x07, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0xDA, 0xAF, 0x80, 0x20, 0x03, 0x23, 0x18, 0x07, 0x48, 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0x22, 0x64, 0x00, 0x18, 0xC7, 0xCD, 0x65, 0xC8, 0xC5, 0xCD, 0xF1, 0xC6, 0xD1, 0xDA, 0xBD, 0xC1, 0xAF, 0x80, 0xCA, 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0x11, 0x2E, 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x60, 0xC7, 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x2E, 0x00, 0xCD, 0x14, 0xC8, 0xB7, 0x28, 0x29, 0xFE, 0x2C, 0x13, 0x28, 0xF5, 0x4F, 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xAD, 0xC8, 0x7D, 0xE1, 0x38, 0x12, 0x77, 0x23, 0x04, 0x18, 0xDE, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, 0xD6, 0x77, 0x23, 0x04, 0x18, 0xF3, 0x11, 0x2E, 0x00, 0xE1, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0x20, 0x01, 0x13, 0x01, 0x80, 0x00, 0x2A, 0x62, 0x00, 0xCD, 0x4E, 0xC8, 0x1E, 0x10, 0xAF, 0xB0, 0x20, 0x0A, 0x3E, 0x0F, 0xB9, 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x60, 0xC7, 0x22, 0x62, 0x00, 0xC1, 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, 0xCD, 0xE2, 0xC7, 0xC5, 0xE5, 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xA0, 0xC7, 0xCD, 0xA0, 0xC7, 0x7E, 0xCD, 0xE7, 0xC7, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xA0, 0xC7, 0x3E, 0x3A, 0xBB, 0x20, 0xF8, 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x92, 0xC7, 0x10, 0xF9, 0xC3, 0x97, 0xCC, 0xE6, 0x7F, 0xFE, 0x7F, 0x28, 0x04, 0xFE, 0x20, 0x30, 0x62, 0x3E, 0x2E, 0x18, 0x5E, 0x1C, 0x18, 0x59, 0x3E, 0x31, 0x11, 0x2E, 0x00, 0x12, 0xD5, 0xCD, 0xEA, 0xCB, 0xE3, 0x23, 0x46, 0x04, 0x23, 0xE5, 0x05, 0x28, 0x17, 0x7E, 0xCD, 0xD2, 0xC7, 0x77, 0x23, 0xFE, 0x22, 0x28, 0x04, 0xFE, 0x27, 0x20, 0xEF, 0x05, 0x28, 0x06, 0xBE, 0x23, 0x20, 0xF9, 0x18, 0xE6, 0x36, 0x00, 0xD1, 0xE1, 0xC9, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0xCD, 0xFC, 0xC7, 0xF1, 0x18, 0x05, 0x7C, 0xCD, 0xE7, 0xC7, 0x7D, 0xF5, 0x1F, 0x1F, 0x1F, 0x1F, 0xCD, 0xF0, 0xC7, 0xF1, 0xE6, 0x0F, 0xFE, 0x0A, 0x38, 0x02, 0xC6, 0x07, 0xC6, 0x30, 0x18, 0x02, 0x3E, 0x20, 0xF5, 0xE6, 0x7F, 0xCD, 0x99, 0xCC, 0xF1, 0xC9, 0xE3, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xFE, 0xC7, 0xF2, 0x07, 0xC8, 0xE3, 0xC9, 0x1A, 0xFE, 0x20, 0xC0, 0x13, 0x18, 0xF9, 0xCD, 0x14, 0xC8, 0xFE, 0x2C, 0xC0, 0x13, 0xCD, 0x14, 0xC8, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0xC3, 0xBD, 0xC1, 0x21, 0x80, 0x00, 0x44, 0x4D, 0xCD, 0x68, 0xC8, 0xE5, 0xC5, 0xCD, 0x1B, 0xC8, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0xCD, 0x27, 0xC8, 0xEB, 0xC1, 0xE1, 0xC9, 0xCD, 0x65, 0xC8, 0x18, 0xD9, 0xCD, 0x68, 0xC8, 0x18, 0xD4, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0x18, 0xCC, 0xE5, 0xCD, 0xAD, 0xC8, 0x38, 0x01, 0xE3, 0xE1, 0x18, 0xC2, 0x37, 0x18, 0x01, 0xB7, 0x08, 0xC5, 0xE5, 0xCD, 0xAD, 0xC8, 0x30, 0x08, 0x08, 0xDA, 0xBD, 0xC1, 0x08, 0xE1, 0x18, 0x01, 0xF1, 0xCD, 0x87, 0xC8, 0x30, 0x06, 0x08, 0xC1, 0xD0, 0xC3, 0xBD, 0xC1, 0xF1, 0xC9, 0xCD, 0x1B, 0xC8, 0x1A, 0xFE, 0x53, 0x20, 0x01, 0x13, 0xE5, 0xF5, 0xCD, 0xAD, 0xC8, 0x38, 0x0F, 0x44, 0x4D, 0xF1, 0xE1, 0x28, 0x07, 0x79, 0x95, 0x4F, 0x78, 0x9C, 0x47, 0x03, 0xB7, 0xC9, 0xF1, 0xE1, 0xCA, 0xBD, 0xC1, 0x37, 0xC9, 0xCD, 0x14, 0xC8, 0xCD, 0xE0, 0xC8, 0xD8, 0xD5, 0x13, 0xCD, 0xE0, 0xC8, 0x30, 0xFA, 0xD1, 0xFE, 0x2E, 0x28, 0x05, 0xCD, 0xCA, 0xC8, 0xA7, 0xC9, 0xCD, 0xF4, 0xC8, 0xA7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xE0, 0xC8, 0x38, 0x09, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x13, 0x18, 0xF2, 0xFE, 0x48, 0xC0, 0x13, 0xC9, 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x38, 0x09, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, 0x07, 0xD6, 0x30, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0x10, 0xC9, 0x13, 0x38, 0x0D, 0xC5, 0x29, 0x44, 0x4D, 0x29, 0x29, 0x09, 0xC1, 0xCD, 0x86, 0xC1, 0x18, 0xED, 0xFE, 0x2E, 0xC8, 0xC3, 0xBD, 0xC1, 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0x01, 0x18, 0x00, 0x11, 0x00, 0x01, 0x21, 0x29, 0xC9, 0xED, 0xB0, 0xC3, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x00, 0x10, 0x21, 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0x3E, 0x01, 0xD3, 0x40, 0xD1, 0xE1, 0xC1, 0xED, 0xB0, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x5A, 0xF5, 0x20, 0x01, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC4, 0x1B, 0xC9, 0xCD, 0x06, 0xC8, 0x0D, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0xBA, 0x01, 0x00, 0x10, 0xCD, 0xFC, 0xC7, 0x79, 0xCD, 0xF0, 0xC7, 0x0C, 0x10, 0xF6, 0xCD, 0x97, 0xCC, 0x06, 0x07, 0xCD, 0xFC, 0xC7, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0xFC, 0xC7, 0xE5, 0x11, 0x00, 0x10, 0x7C, 0xFE, 0xC9, 0x20, 0x09, 0x7D, 0xFE, 0x8D, 0x38, 0x04, 0xFE, 0x9C, 0x38, 0x12, 0x46, 0x3E, 0x55, 0x77, 0xBE, 0x20, 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x70, 0x3E, 0x58, 0x18, 0x09, 0x70, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xDA, 0x3E, 0x5E, 0xCD, 0xFE, 0xC7, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x28, 0x65, 0x67, 0x2C, 0x20, 0x41, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x3B, 0x29, 0xA0, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x97, 0xCC, 0xCD, 0xC1, 0xC2, 0xC2, 0xB9, 0xC1, 0xCD, 0x06, 0xC8, 0x53, 0x65, 0x65, 0x6B, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x3A, 0x8D, 0x06, 0x15, 0x21, 0x20, 0xCA, 0xDB, 0x31, 0x32, 0x7D, 0x00, 0x7E, 0xC5, 0xE5, 0xFE, 0xFF, 0x20, 0x2E, 0xCD, 0x06, 0xC8, 0x20, 0x52, 0x65, 0x73, 0x74, 0x6F, 0x72, 0x65, 0xBA, 0xCD, 0x04, 0xC6, 0xCD, 0x37, 0xCD, 0x08, 0xCD, 0x1E, 0xC6, 0x08, 0x18, 0x2C, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFE, 0x06, 0x07, 0x08, 0x09, 0x00, 0xFE, 0x27, 0x00, 0x15, 0x00, 0x01, 0xFE, 0xFF, 0xFE, 0x27, 0xFE, 0xFE, 0x20, 0x06, 0xCD, 0x97, 0xCC, 0xB7, 0x18, 0x12, 0x32, 0x75, 0x00, 0xCD, 0xDB, 0xC7, 0xCD, 0x06, 0xC8, 0xBA, 0xCD, 0xD4, 0xC4, 0xF5, 0xCD, 0x7A, 0xCB, 0xF1, 0xE1, 0xC1, 0xDA, 0x97, 0xCC, 0x23, 0x10, 0xA2, 0xCD, 0x06, 0xC8, 0x0D, 0x52, 0x65, 0x61, 0x64, 0x2F, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x8D, 0xCD, 0x94, 0xCB, 0xCD, 0xB0, 0xCB, 0xDA, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0D, 0x45, 0x53, 0x43, 0x3D, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x20, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x3D, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0xA0, 0xCD, 0xA3, 0xC7, 0x21, 0x00, 0x09, 0xCD, 0xA2, 0xCB, 0x01, 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF9, 0xCD, 0xC1, 0xCB, 0x30, 0x6E, 0xCD, 0x06, 0xC8, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0xF4, 0x1E, 0x01, 0xCD, 0xDE, 0xC5, 0xCD, 0x06, 0xC8, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6F, 0x79, 0x65, 0x64, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x20, 0x69, 0x73, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x30, 0x44, 0x30, 0x30, 0xF3, 0x2A, 0x79, 0x00, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x8D, 0xC9, 0x21, 0x00, 0x0B, 0xCD, 0xA2, 0xCB, 0xCD, 0xB0, 0xCB, 0x38, 0x87, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0x65, 0xA0, 0xED, 0x4B, 0x79, 0x00, 0x11, 0x00, 0x09, 0x21, 0x00, 0x0B, 0x1A, 0xBE, 0x20, 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x01, 0x37, 0xCD, 0xD1, 0xCB, 0xCD, 0x94, 0xCB, 0xCD, 0xC1, 0xCB, 0xDA, 0xC7, 0xCA, 0xC3, 0x97, 0xCC, 0x38, 0x06, 0xCD, 0x06, 0xC8, 0x4F, 0xCB, 0xC9, 0xCD, 0x06, 0xC8, 0x65, 0x72, 0x72, 0x6F, 0x72, 0xA0, 0x3A, 0x71, 0x00, 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x44, 0x61, 0x74, 0xE1, 0x21, 0x00, 0x0D, 0x22, 0x7B, 0x00, 0xC9, 0x22, 0x7B, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0xEE, 0xC9, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x72, 0x65, 0x61, 0x64, 0xA0, 0xCD, 0x52, 0xCD, 0x18, 0x10, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0xA0, 0xCD, 0xAB, 0xC5, 0xF5, 0xCD, 0x7A, 0xCB, 0xCD, 0x97, 0xCC, 0xF1, 0xC9, 0x3E, 0x27, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3E, 0x01, 0x32, 0x76, 0x00, 0xC3, 0xCB, 0xCD, 0xC5, 0xE5, 0x06, 0x00, 0x21, 0x02, 0x00, 0x19, 0xCD, 0x64, 0xCC, 0xFE, 0x10, 0xCC, 0xBB, 0xCC, 0x28, 0xF6, 0xFE, 0x1B, 0xCA, 0x8E, 0xCC, 0xFE, 0x08, 0x28, 0x04, 0xFE, 0x7F, 0x20, 0x11, 0xAF, 0xB0, 0x28, 0xE5, 0x2B, 0x05, 0xCD, 0x4B, 0xCC, 0x7E, 0xFE, 0x20, 0xDC, 0x4B, 0xCC, 0x18, 0xD8, 0xFE, 0x0D, 0xCC, 0x45, 0xCC, 0x28, 0x1B, 0xFE, 0x15, 0x20, 0x08, 0xCD, 0x45, 0xCC, 0xCD, 0x97, 0xCC, 0x18, 0xBF, 0x4F, 0x1A, 0x3D, 0xB8, 0x28, 0xBF, 0x79, 0xCD, 0x45, 0xCC, 0x77, 0x23, 0x04, 0x18, 0xB6, 0x36, 0x00, 0x78, 0x13, 0x12, 0x1B, 0xE1, 0xC1, 0xC9, 0xF5, 0xCD, 0x52, 0xCC, 0xF1, 0xC9, 0xCD, 0x06, 0xC8, 0x08, 0x20, 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x43, 0xFE, 0x0D, 0x28, 0x3F, 0xF5, 0xCD, 0x06, 0xC8, 0xDE, 0xF1, 0xC6, 0x40, 0x18, 0x35, 0xCD, 0x23, 0xCD, 0xCD, 0x76, 0xCC, 0x28, 0xF8, 0xCD, 0x76, 0xCC, 0x28, 0xFB, 0xDB, 0x01, 0xE6, 0x7F, 0xC9, 0xDB, 0x00, 0xE6, 0x40, 0xC9, 0xCD, 0x76, 0xCC, 0xC8, 0xCD, 0x6C, 0xCC, 0xFE, 0x13, 0xCC, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0x2E, 0x00, 0xCD, 0x97, 0xCC, 0xC3, 0x55, 0xC1, 0x3E, 0x0D, 0xF5, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0xCB, 0xCC, 0xCD, 0x23, 0xCD, 0xDB, 0x00, 0xE6, 0x80, 0x28, 0xFA, 0xF1, 0xD3, 0x01, 0xFE, 0x0D, 0xC0, 0x3E, 0x0A, 0xCD, 0x99, 0xCC, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0xC0, 0xF5, 0xD9, 0x3E, 0x80, 0xA8, 0x47, 0xCB, 0x78, 0xD9, 0xC4, 0xC9, 0xCC, 0xF1, 0xC9, 0x3E, 0x11, 0xF5, 0xE5, 0x21, 0x08, 0x00, 0xCD, 0xF1, 0xCF, 0xE1, 0xF1, 0xF5, 0xFE, 0x11, 0x28, 0x13, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0x20, 0x05, 0xCD, 0xBB, 0xCC, 0xF1, 0xC9, 0xDB, 0x54, 0x2F, 0xE6, 0x20, 0x28, 0xED, 0xF1, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4F, 0xC2, 0xBD, 0xC1, 0x13, 0x1A, 0xFE, 0x4E, 0x28, 0x0E, 0xFE, 0x46, 0xC2, 0xBD, 0xC1, 0xD9, 0xCB, 0xA8, 0xD9, 0x3E, 0xFF, 0xD3, 0x04, 0xC9, 0xD9, 0xCB, 0xE8, 0xD9, 0x3E, 0x01, 0x32, 0x72, 0x00, 0xC9, 0xD9, 0xCB, 0x68, 0xD9, 0xC8, 0xC5, 0xCD, 0xA8, 0xCF, 0xC1, 0xD3, 0x34, 0xCD, 0xD6, 0xCF, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0x16, 0x02, 0xD5, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0xD5, 0x3E, 0x0A, 0x32, 0x75, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0x15, 0x20, 0xE9, 0x18, 0x18, 0x16, 0x0A, 0xD5, 0xCD, 0x58, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x18, 0x0B, 0x16, 0x04, 0xD5, 0xCD, 0x9F, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x3A, 0x71, 0x00, 0x4F, 0x37, 0xC9, 0x97, 0x32, 0x75, 0x00, 0x32, 0x78, 0x00, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0xCD, 0x25, 0xCE, 0x38, 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, 0xF7, 0xC3, 0x3E, 0xCE, 0x3E, 0xC4, 0xD3, 0x30, 0xCD, 0xD6, 0xCF, 0xE6, 0x57, 0xD3, 0x04, 0xCD, 0xE4, 0xCF, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0x16, 0xCD, 0xCF, 0xCF, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x18, 0x1F, 0x30, 0xF7, 0x3E, 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0x50, 0xCE, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x05, 0x1F, 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0x97, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0x3A, 0x75, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x76, 0x00, 0xD3, 0x32, 0x3A, 0x7D, 0x00, 0xD3, 0x31, 0x91, 0xCA, 0xCF, 0xCF, 0xCD, 0x25, 0xCE, 0x38, 0x0F, 0xF6, 0x10, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x45, 0xCD, 0xD6, 0xCF, 0xE6, 0x4F, 0xD3, 0x04, 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, 0x30, 0xF7, 0xDB, 0x30, 0x2E, 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0xCD, 0xCF, 0xCF, 0x18, 0x2B, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x0A, 0xCB, 0x5F, 0x3E, 0x0E, 0x28, 0x02, 0x3E, 0x0C, 0xA7, 0xC9, 0xCB, 0x5F, 0x3E, 0x0F, 0x28, 0xF8, 0x37, 0xC9, 0xCD, 0xCF, 0xCF, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x98, 0x37, 0xC0, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xA7, 0xC9, 0xCD, 0x8F, 0xCE, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x16, 0xED, 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0x5D, 0xCE, 0xDB, 0x34, 0xCB, 0x4F, 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0xC8, 0x18, 0x08, 0xCD, 0xCF, 0xCF, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, 0xE4, 0xCF, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, 0xED, 0xA3, 0xC2, 0xB3, 0xCE, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0xCF, 0xCF, 0xCD, 0xE4, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x7E, 0x00, 0xCB, 0x4F, 0xC8, 0xCD, 0x00, 0xCF, 0x38, 0x0A, 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, 0xCD, 0x8F, 0xCE, 0xED, 0x4B, 0x79, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, 0x1E, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, 0xDB, 0x33, 0x3E, 0x80, 0xCD, 0x55, 0xCF, 0x2A, 0x79, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, 0x33, 0x2A, 0x7B, 0x00, 0xC9, 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x72, 0x00, 0xCD, 0xA8, 0xCF, 0xD3, 0x34, 0xF5, 0xE5, 0xCD, 0xD6, 0xCF, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x72, 0x00, 0xA7, 0x28, 0x21, 0x21, 0x90, 0x01, 0x3A, 0x73, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x74, 0x00, 0x67, 0x3A, 0x78, 0x00, 0x32, 0x74, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, 0xCD, 0xE4, 0xCF, 0x18, 0x06, 0x21, 0x20, 0x4E, 0xCD, 0xF1, 0xCF, 0xE1, 0x78, 0x32, 0x73, 0x00, 0x3E, 0x01, 0x32, 0x72, 0x00, 0xF1, 0xB1, 0xC9, 0x3A, 0x77, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, 0x10, 0xFD, 0x47, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, 0xCB, 0xF0, 0x78, 0xF6, 0x20, 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, 0xD6, 0xCF, 0xD3, 0x04, 0xAF, 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x78, 0x00, 0xA7, 0x28, 0x02, 0x06, 0x7D, 0x78, 0xC1, 0xC9, 0x21, 0x08, 0x00, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, 0x00, 0xC5, 0x2B, 0x06, 0x1C, 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0xFF }, { /* RDOS 3.12 */ 0xF3, 0x18, 0x36, 0xC3, 0x43, 0xC3, 0xC3, 0x75, 0xC3, 0xC3, 0x79, 0xC3, 0xC3, 0x7D, 0xC3, 0xC3, 0x06, 0xCA, 0xC3, 0x88, 0xC3, 0xC3, 0xAF, 0xC3, 0xC3, 0xC0, 0xC3, 0xC3, 0xD0, 0xC3, 0xC3, 0x81, 0xC3, 0xC3, 0xAA, 0xCF, 0xC3, 0x02, 0xD5, 0xC3, 0xF8, 0xD4, 0xC3, 0xA1, 0xD4, 0xC3, 0x1F, 0xD4, 0xC3, 0xE4, 0xD4, 0xC3, 0xF2, 0xD4, 0xC3, 0x80, 0xD4, 0xAF, 0x47, 0xD3, 0x03, 0xD3, 0xE1, 0x2F, 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x0E, 0x00, 0xD9, 0x21, 0x03, 0x00, 0x25, 0x20, 0xFD, 0x74, 0x2C, 0xF2, 0x4F, 0xC0, 0x31, 0xF0, 0x7F, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x3E, 0x20, 0x32, 0x10, 0x00, 0x22, 0x05, 0x00, 0x22, 0x03, 0x00, 0xCD, 0xAA, 0xCF, 0xDB, 0x34, 0xE6, 0x40, 0xCC, 0x74, 0xC4, 0xCD, 0xF2, 0xD4, 0x43, 0x72, 0x6F, 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, 0x53, 0x20, 0x30, 0x33, 0x2E, 0x31, 0x32, 0x8D, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x31, 0xF0, 0x7F, 0xCD, 0x58, 0xD3, 0x21, 0x15, 0x00, 0x3A, 0x07, 0x00, 0xCD, 0xCB, 0xC0, 0xCD, 0x1A, 0xD4, 0xCD, 0x45, 0xD3, 0xB7, 0x28, 0xE3, 0xD6, 0x41, 0xFE, 0x1A, 0x30, 0x4C, 0x21, 0x8D, 0xC0, 0xE5, 0x47, 0x13, 0xCD, 0x45, 0xD3, 0xFE, 0x3B, 0x78, 0x28, 0x76, 0x87, 0x21, 0x5D, 0xD5, 0xCD, 0x84, 0xD2, 0x7E, 0x23, 0x66, 0x6F, 0xE5, 0x21, 0x80, 0x00, 0xC3, 0x45, 0xD3, 0x28, 0x10, 0xFE, 0xFF, 0x28, 0x15, 0xCD, 0xF2, 0xD4, 0x53, 0xD4, 0xCD, 0xFE, 0xD2, 0x3E, 0x3A, 0x18, 0x17, 0xFE, 0xFF, 0x28, 0x05, 0xC6, 0x41, 0xCD, 0xA1, 0xD4, 0x3E, 0x3B, 0x28, 0x0A, 0xCB, 0x5E, 0xCC, 0xA1, 0xD4, 0xCB, 0x56, 0xCC, 0xA1, 0xD4, 0xC3, 0xA1, 0xD4, 0xCD, 0xF2, 0xD4, 0x3F, 0x8D, 0x18, 0x8F, 0xCD, 0x27, 0xC1, 0x1B, 0x1F, 0x41, 0x2D, 0xC4, 0x18, 0x0D, 0xCD, 0x27, 0xC1, 0x1B, 0x1F, 0x53, 0x54, 0x30, 0x2D, 0x53, 0x54, 0x33, 0xC6, 0xCD, 0xF2, 0xD4, 0x20, 0x4F, 0x6E, 0x6C, 0x79, 0x8D, 0x18, 0xDC, 0xCD, 0x27, 0xC1, 0x01, 0x07, 0xA0, 0xC9, 0xE3, 0xF5, 0xCD, 0xA9, 0xCC, 0xF1, 0xE3, 0xC9, 0xFE, 0x04, 0x30, 0xCB, 0xF5, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x04, 0x20, 0x03, 0x13, 0xAF, 0x47, 0xCD, 0x66, 0xC2, 0xD1, 0xF5, 0xAF, 0x32, 0x08, 0x00, 0x7A, 0x32, 0x16, 0x00, 0x32, 0x07, 0x00, 0xF1, 0x21, 0x15, 0x00, 0x70, 0x38, 0x02, 0xB0, 0x77, 0xF5, 0xCD, 0xAF, 0xC1, 0xCD, 0xB5, 0xC2, 0x3E, 0x48, 0x32, 0x09, 0x00, 0xCD, 0x88, 0xC3, 0xDA, 0xE6, 0xCB, 0xF1, 0xD0, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xCD, 0x60, 0xD6, 0xCD, 0xD2, 0xD5, 0x30, 0x1F, 0x3A, 0x21, 0x00, 0xB7, 0xF2, 0x96, 0xC1, 0x21, 0x64, 0x00, 0xCD, 0x7C, 0xD8, 0x18, 0xE3, 0xCD, 0xE6, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0xF2, 0xD4, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x8D, 0xC9, 0x21, 0xF8, 0x00, 0xCD, 0x9A, 0xC2, 0xCD, 0xB5, 0xC2, 0xAF, 0x32, 0x08, 0x00, 0x21, 0x27, 0x00, 0x3A, 0x16, 0x00, 0xCD, 0x84, 0xD2, 0x3A, 0x15, 0x00, 0xCB, 0xFF, 0x77, 0xC9, 0x13, 0xCD, 0xB2, 0xD3, 0xFE, 0x3A, 0xC2, 0xF7, 0xC0, 0x45, 0x7C, 0xB7, 0xC2, 0xF7, 0xC0, 0x13, 0xCD, 0xE9, 0xD2, 0x78, 0xF5, 0xFE, 0x40, 0xD2, 0x08, 0xC1, 0x32, 0x16, 0x00, 0x32, 0x07, 0x00, 0x3E, 0x80, 0x32, 0x08, 0x00, 0x3E, 0xD8, 0x32, 0x09, 0x00, 0xCD, 0xA3, 0xC3, 0xCD, 0x8A, 0xD8, 0xDA, 0x8D, 0xC0, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0x21, 0x00, 0x02, 0x22, 0x1D, 0x00, 0xCD, 0x37, 0xD9, 0xF1, 0x38, 0x0F, 0xCD, 0x51, 0xDA, 0xDC, 0xF4, 0xCB, 0xCC, 0x3D, 0xC2, 0xCD, 0x51, 0xDA, 0x11, 0xF8, 0x00, 0xCD, 0xEF, 0xC2, 0xCB, 0x77, 0xC8, 0x21, 0x1A, 0x00, 0x34, 0xCD, 0x7D, 0xD9, 0xCD, 0x51, 0xDA, 0xD0, 0xCD, 0xF4, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0xF2, 0xD4, 0x53, 0x75, 0x70, 0x65, 0x72, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x8D, 0xC9, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0x21, 0xE5, 0xE5, 0x22, 0xF8, 0x00, 0xCD, 0x88, 0xC3, 0xDA, 0xF4, 0xCB, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xCD, 0x7D, 0xD9, 0xCD, 0x51, 0xDA, 0xD0, 0xCD, 0xF4, 0xCB, 0xC3, 0x99, 0xC1, 0xE5, 0x0E, 0x00, 0x21, 0x10, 0x44, 0xCD, 0x85, 0xC2, 0x38, 0x0F, 0x2E, 0x01, 0xCD, 0x85, 0xC2, 0xDA, 0xF7, 0xC0, 0x21, 0x02, 0x58, 0xCD, 0x8F, 0xC2, 0xB7, 0x79, 0xE1, 0xC3, 0xE9, 0xD2, 0xCD, 0x8F, 0xC2, 0xD0, 0xD6, 0x53, 0x28, 0x0B, 0x37, 0xC9, 0xCD, 0x45, 0xD3, 0xBC, 0x37, 0xC0, 0x79, 0xB5, 0x4F, 0x13, 0xC9, 0x01, 0x02, 0x43, 0xCD, 0xA8, 0xC2, 0x01, 0x10, 0x44, 0xCD, 0xA8, 0xC2, 0x0E, 0x01, 0x7E, 0x23, 0x23, 0xB8, 0xC0, 0x3A, 0x15, 0x00, 0xB1, 0x32, 0x15, 0x00, 0xC9, 0x3A, 0x15, 0x00, 0x47, 0xCB, 0x40, 0x11, 0x10, 0x0A, 0x21, 0x00, 0x02, 0x20, 0x0D, 0xCB, 0x48, 0x11, 0x08, 0x05, 0x20, 0x06, 0x11, 0x1A, 0x12, 0x21, 0x80, 0x00, 0x22, 0x1D, 0x00, 0xCB, 0x50, 0x7B, 0x1E, 0x4C, 0x20, 0x03, 0x7A, 0x1E, 0x27, 0x32, 0x11, 0x00, 0xAF, 0x57, 0xED, 0x53, 0x12, 0x00, 0xCB, 0x60, 0x28, 0x01, 0x3C, 0x32, 0x14, 0x00, 0xC9, 0x3E, 0x80, 0x21, 0x08, 0x00, 0x77, 0x38, 0x35, 0x1A, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xF6, 0x13, 0x1A, 0xFE, 0x53, 0x20, 0x28, 0x13, 0x1A, 0xFE, 0x54, 0x20, 0x22, 0x21, 0xEE, 0xFF, 0x19, 0xC5, 0x56, 0x23, 0x5E, 0x23, 0xD5, 0x56, 0x23, 0x5E, 0x23, 0x4E, 0x06, 0x00, 0xEB, 0xCD, 0xBF, 0xD2, 0x7B, 0xB2, 0x28, 0x01, 0x23, 0xD1, 0x19, 0x79, 0x3D, 0x2B, 0xC1, 0x18, 0x05, 0x3E, 0x05, 0x21, 0x31, 0x01, 0x32, 0x14, 0x00, 0x22, 0x12, 0x00, 0x3E, 0x13, 0x32, 0x11, 0x00, 0x21, 0x00, 0x02, 0x22, 0x1D, 0x00, 0xC9, 0x32, 0x07, 0x00, 0xFE, 0x40, 0x30, 0x08, 0x47, 0x37, 0xCD, 0xD6, 0xC1, 0xC3, 0x57, 0xDA, 0xD6, 0x41, 0xFE, 0x04, 0x38, 0x07, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x37, 0xC9, 0x32, 0x07, 0x00, 0x32, 0x16, 0x00, 0x78, 0x32, 0x15, 0x00, 0xD9, 0xCB, 0xA8, 0xD9, 0xDB, 0x31, 0x32, 0x1F, 0x00, 0xC3, 0xAC, 0xC1, 0x32, 0x17, 0x00, 0xC9, 0x22, 0x18, 0x00, 0xC9, 0x32, 0x1A, 0x00, 0xC9, 0xCD, 0x51, 0xDA, 0x47, 0xC3, 0x57, 0xDA, 0xCD, 0xA3, 0xC3, 0x20, 0x06, 0xCD, 0x3B, 0xDA, 0xC3, 0x51, 0xDA, 0xD9, 0xCB, 0xA8, 0xD9, 0xCD, 0x12, 0xC4, 0xCD, 0x91, 0xD5, 0xF5, 0xCD, 0x30, 0xC4, 0xF1, 0xC9, 0xAF, 0x67, 0x6F, 0x32, 0x17, 0x00, 0x22, 0x18, 0x00, 0xC3, 0xE6, 0xCE, 0xCD, 0x58, 0xD3, 0x20, 0x06, 0xCD, 0x08, 0xC4, 0xC3, 0x60, 0xD6, 0xCD, 0x17, 0xDA, 0xC3, 0x51, 0xDA, 0xCD, 0xEA, 0xC3, 0xD8, 0xF5, 0xC4, 0x7D, 0xD9, 0xF1, 0x20, 0x18, 0xCD, 0xD2, 0xD5, 0x18, 0x0E, 0xCD, 0xEA, 0xC3, 0xD8, 0xF5, 0xC4, 0x04, 0xDA, 0xF1, 0x20, 0x08, 0xCD, 0xF8, 0xC3, 0xED, 0x4B, 0x1D, 0x00, 0xC9, 0xED, 0x4B, 0x1D, 0x00, 0xC3, 0x51, 0xDA, 0xCD, 0xAF, 0xC3, 0x3A, 0x08, 0x00, 0xCB, 0x7F, 0x20, 0x01, 0xD8, 0xE6, 0x80, 0xC9, 0x21, 0x15, 0x00, 0x46, 0xC5, 0xCB, 0xCE, 0xCD, 0xE7, 0xD5, 0xC1, 0x78, 0x32, 0x15, 0x00, 0xC9, 0x3A, 0x18, 0x00, 0x47, 0x3A, 0x17, 0x00, 0xB0, 0x20, 0x1E, 0xD9, 0xCB, 0x68, 0xD9, 0xC0, 0x3A, 0x15, 0x00, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, 0xC8, 0x32, 0x26, 0x00, 0xE6, 0xFC, 0x32, 0x15, 0x00, 0xD9, 0xCB, 0xE8, 0xD9, 0xC3, 0xB5, 0xC2, 0xD9, 0xCB, 0x68, 0xCB, 0xA8, 0xD9, 0xC8, 0x3A, 0x26, 0x00, 0x32, 0x15, 0x00, 0xC3, 0xB5, 0xC2, 0xB7, 0x28, 0x31, 0x13, 0xFE, 0x53, 0x20, 0x20, 0x1A, 0xCD, 0x2C, 0xD3, 0xFE, 0x54, 0xC2, 0x08, 0xC1, 0x13, 0xCD, 0xB2, 0xD3, 0xB7, 0xC2, 0x08, 0xC1, 0x7C, 0xA7, 0xC2, 0x08, 0xC1, 0x7D, 0xFE, 0x40, 0xD2, 0x08, 0xC1, 0xF6, 0x80, 0x18, 0x2F, 0xCD, 0xE9, 0xD2, 0xD6, 0x41, 0xFE, 0x04, 0xD2, 0xFE, 0xC0, 0x18, 0x23, 0xDB, 0x04, 0x2F, 0xC5, 0xE6, 0x17, 0x47, 0xE6, 0x03, 0xCB, 0x50, 0x28, 0x02, 0xCB, 0xFF, 0xCB, 0x60, 0x20, 0x0F, 0xCB, 0xEF, 0xCB, 0x48, 0x20, 0x09, 0xCB, 0xFF, 0x3D, 0xCB, 0x40, 0x28, 0x02, 0xC6, 0x1F, 0xC1, 0xF5, 0xCB, 0x7F, 0x28, 0x05, 0x3E, 0x01, 0xCD, 0xB3, 0xCB, 0xDB, 0x04, 0x2F, 0xE6, 0x08, 0xCC, 0x00, 0xCB, 0xCC, 0x80, 0xD4, 0xCD, 0xF2, 0xD4, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x8D, 0xF1, 0xCB, 0x7F, 0xF5, 0xF5, 0xCB, 0xBF, 0x32, 0x16, 0x00, 0x3E, 0x80, 0x20, 0x01, 0xAF, 0x32, 0x08, 0x00, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0xCD, 0xA3, 0xC3, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xF1, 0x20, 0x65, 0xCB, 0x6F, 0x28, 0x23, 0xCD, 0xF2, 0xD4, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x8D, 0xC3, 0x71, 0xC0, 0xCD, 0x33, 0xD8, 0x57, 0xD3, 0x34, 0xCD, 0x12, 0xC6, 0xCD, 0x2E, 0xC6, 0x32, 0x15, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1D, 0x00, 0x06, 0x02, 0xC5, 0xCD, 0x23, 0xC6, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0xCD, 0x91, 0xD5, 0xD4, 0x60, 0xD6, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xD4, 0xD2, 0xD5, 0xC1, 0x30, 0x53, 0xCD, 0x23, 0xC6, 0x10, 0xE1, 0xCD, 0x80, 0xD4, 0xCD, 0xE3, 0xCB, 0x18, 0x5E, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0x21, 0x00, 0x28, 0x22, 0x1D, 0x00, 0xCD, 0x8A, 0xD8, 0x38, 0x48, 0x01, 0x00, 0x00, 0xC5, 0xCD, 0x23, 0xC6, 0xCD, 0x37, 0xD9, 0xC1, 0xCD, 0x51, 0xDA, 0x28, 0x0B, 0xFE, 0x07, 0x20, 0x34, 0x0B, 0x78, 0xB1, 0x20, 0xEA, 0x18, 0x2D, 0xCD, 0x3B, 0xDA, 0xCD, 0x51, 0xDA, 0x20, 0x25, 0xCD, 0x23, 0xC6, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xCD, 0x6B, 0xD9, 0xCD, 0x51, 0xDA, 0x20, 0x15, 0xCD, 0x12, 0xC6, 0x3A, 0x80, 0x00, 0xFE, 0x40, 0x28, 0x04, 0xFE, 0xE5, 0x20, 0x31, 0xCD, 0xF2, 0xD4, 0x0D, 0xCE, 0x18, 0x12, 0xCD, 0x80, 0xD4, 0xCD, 0xF1, 0xCB, 0xCD, 0xF2, 0xD4, 0x0D, 0x55, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x20, 0xF4, 0xCD, 0xF2, 0xD4, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, 0xFF, 0xD3, 0x04, 0xCD, 0x80, 0xD4, 0xC3, 0x71, 0xC0, 0xCD, 0x58, 0xD3, 0xCC, 0x33, 0xD8, 0x57, 0xCD, 0x23, 0xC6, 0xF1, 0x08, 0xCD, 0xF2, 0xD4, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, 0x8D, 0xCD, 0x58, 0xD3, 0x37, 0xCA, 0x80, 0x00, 0x21, 0x80, 0x04, 0x11, 0x00, 0x00, 0x01, 0x00, 0x22, 0xED, 0xB0, 0x08, 0xE6, 0x7F, 0x32, 0x07, 0x04, 0x3E, 0x06, 0x32, 0x06, 0x04, 0x3E, 0xFF, 0xD3, 0xFF, 0x01, 0xE8, 0x03, 0xCD, 0x23, 0xC6, 0x21, 0x0A, 0x00, 0xCD, 0x7C, 0xD8, 0x0B, 0x78, 0xB1, 0x20, 0xF2, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0xFE, 0x1B, 0xC0, 0x18, 0x9D, 0x7A, 0xD3, 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, 0x01, 0x00, 0x02, 0xCD, 0x72, 0xC6, 0x3E, 0xD4, 0xD3, 0x30, 0xCD, 0x6B, 0xC6, 0x28, 0xDF, 0xDB, 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0x13, 0xFE, 0x41, 0xCA, 0x4B, 0xC7, 0x06, 0x52, 0x18, 0x03, 0x13, 0x06, 0x57, 0xFE, 0x44, 0xC2, 0xF7, 0xC0, 0xCD, 0x5F, 0xD3, 0x78, 0x32, 0x09, 0x00, 0xCD, 0x84, 0xD3, 0xAF, 0xB2, 0xC2, 0xF7, 0xC0, 0xB0, 0xB1, 0xCA, 0xF7, 0xC0, 0xCD, 0x58, 0xD3, 0x20, 0x05, 0xAF, 0xB3, 0xCA, 0xF7, 0xC0, 0x22, 0x1B, 0x00, 0x0B, 0xC5, 0xD5, 0xD4, 0x08, 0xC4, 0xD1, 0xFD, 0xE1, 0x3A, 0x11, 0x00, 0xBB, 0xDA, 0xF7, 0xC0, 0x7B, 0x32, 0x1A, 0x00, 0xCD, 0x28, 0xCE, 0xCD, 0x9F, 0xD4, 0xFD, 0xE5, 0x3A, 0x09, 0x00, 0xFE, 0x52, 0x28, 0x0A, 0xFE, 0x57, 0xC2, 0xF7, 0xC0, 0xCD, 0xD0, 0xC3, 0x18, 0x03, 0xCD, 0xC0, 0xC3, 0x30, 0x09, 0xCD, 0x58, 0xD3, 0xCA, 0xE3, 0xCB, 0xC3, 0xF1, 0xCB, 0x2A, 0x1B, 0x00, 0x09, 0x22, 0x1B, 0x00, 0xE1, 0xB7, 0xED, 0x42, 0xDA, 0x22, 0xCE, 0xE5, 0xFD, 0xE1, 0x21, 0x1A, 0x00, 0x34, 0x3A, 0x11, 0x00, 0xBE, 0x30, 0xC4, 0xCD, 0xE6, 0xCE, 0x2A, 0x18, 0x00, 0x7D, 0x32, 0x1F, 0x00, 0xE5, 0x23, 0x22, 0x18, 0x00, 0xED, 0x4B, 0x12, 0x00, 0x37, 0xED, 0x42, 0xE1, 0x38, 0xAB, 0x22, 0x18, 0x00, 0x3A, 0x11, 0x00, 0x32, 0x1A, 0x00, 0xCD, 0x22, 0xCE, 0xCD, 0xF2, 0xD4, 0x4E, 0x65, 0x78, 0x74, 0x20, 0x3D, 0xA0, 0x2A, 0x1B, 0x00, 0xCD, 0xF9, 0xD2, 0xCD, 0xF2, 0xD4, 0x0D, 0x45, 0x6E, 0x64, 0x20, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x8D, 0xC9, 0xCD, 0xE9, 0xD2, 0xCD, 0x5F, 0xD3, 0xCD, 0xF1, 0xCE, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xCD, 0xE6, 0xCE, 0x3E, 0x0D, 0xCD, 0x18, 0xD5, 0xCD, 0x13, 0xCE, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xCD, 0xC0, 0xC3, 0x38, 0x0A, 0xCD, 0x58, 0xD3, 0x28, 0x1B, 0xCD, 0x57, 0xDA, 0x28, 0x16, 0x3A, 0x1A, 0x00, 0xCD, 0x9C, 0xD4, 0xCD, 0x58, 0xD3, 0x20, 0x05, 0xCD, 0xE6, 0xCB, 0x18, 0x03, 0xCD, 0xF4, 0xCB, 0xCD, 0x13, 0xCE, 0xCD, 0xC1, 0xD4, 0xCD, 0x58, 0xD3, 0x20, 0x05, 0xCD, 0x9B, 0xCE, 0x20, 0xC8, 0xCD, 0x67, 0xCE, 0x20, 0xBB, 0xC3, 0x80, 0xD4, 0xFE, 0x54, 0xCA, 0xC3, 0xC1, 0xFE, 0x4D, 0xCA, 0x86, 0xD0, 0xF5, 0xCD, 0x5F, 0xD3, 0xF1, 0xFE, 0x53, 0x20, 0x0A, 0x13, 0xED, 0x4B, 0x18, 0x00, 0xCD, 0x72, 0xD3, 0x18, 0x16, 0xCD, 0xB2, 0xD3, 0x38, 0x1A, 0xE5, 0xED, 0x4B, 0x12, 0x00, 0x03, 0xED, 0x42, 0xC1, 0xD2, 0xF7, 0xC0, 0xCD, 0x79, 0xD3, 0x38, 0x10, 0xAF, 0xB4, 0xC2, 0xF7, 0xC0, 0x3A, 0x14, 0x00, 0xBD, 0xDA, 0xF7, 0xC0, 0x7D, 0x32, 0x17, 0x00, 0xED, 0x43, 0x18, 0x00, 0x3E, 0xD3, 0x32, 0x09, 0x00, 0xCD, 0xAF, 0xC3, 0xD0, 0xCD, 0x58, 0xD3, 0xCA, 0xE6, 0xCB, 0xC3, 0xF4, 0xCB, 0xFE, 0x4F, 0xC2, 0xF7, 0xC0, 0x13, 0xCD, 0x45, 0xD3, 0xFE, 0x4E, 0x28, 0x0D, 0xFE, 0x46, 0xC2, 0xF7, 0xC0, 0xD9, 0xCB, 0xB0, 0xD9, 0x3E, 0xFF, 0x18, 0x1F, 0xD9, 0xCB, 0xF0, 0xD9, 0x3E, 0x01, 0x32, 0x23, 0x00, 0xC9, 0xD9, 0xCB, 0x70, 0xD9, 0xC8, 0xCD, 0x58, 0xD3, 0xD8, 0xC5, 0xCD, 0x33, 0xD8, 0xC1, 0xD3, 0x34, 0xCD, 0x61, 0xD8, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0xFE, 0x58, 0x32, 0x2C, 0x00, 0x06, 0x81, 0x28, 0x16, 0xD6, 0x5A, 0x06, 0x01, 0x20, 0x11, 0xCD, 0x50, 0xCA, 0xC2, 0xF7, 0xC0, 0x47, 0x3A, 0x2B, 0x00, 0x20, 0x04, 0x3D, 0x32, 0x2C, 0x00, 0x13, 0xCD, 0xE9, 0xD2, 0x78, 0xB7, 0xF5, 0xC4, 0xB3, 0xCB, 0xAF, 0xCD, 0xFD, 0xCA, 0xF1, 0xFC, 0x8F, 0xCB, 0xCD, 0x54, 0xCA, 0xCD, 0x80, 0xD4, 0xCD, 0x78, 0xC9, 0xDA, 0x80, 0xD4, 0xCD, 0x27, 0xC1, 0x07, 0x2F, 0x08, 0x00, 0xF3, 0xCD, 0x80, 0xD4, 0xCD, 0xFE, 0xC9, 0xCD, 0x19, 0xCA, 0xDA, 0x80, 0xD4, 0xCD, 0x27, 0xC1, 0x08, 0x00, 0x20, 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x20, 0x98, 0xCD, 0xF2, 0xD4, 0x0D, 0x43, 0x52, 0x3D, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0x20, 0x45, 0x53, 0x43, 0x3D, 0x41, 0x62, 0x6F, 0x72, 0x74, 0xA0, 0xCD, 0x1A, 0xD4, 0x21, 0x00, 0x06, 0xCD, 0x0A, 0xCA, 0x01, 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF9, 0xCD, 0x27, 0xCA, 0x30, 0x60, 0xCD, 0xF2, 0xD4, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0xF4, 0xCD, 0x4C, 0xCE, 0xCD, 0xF2, 0xD4, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x45, 0x44, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x20, 0x69, 0x73, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x34, 0x30, 0x30, 0x48, 0xF3, 0x2A, 0x1D, 0x00, 0xCD, 0x15, 0xD3, 0xC3, 0x80, 0xD4, 0x21, 0x00, 0x08, 0xCD, 0x0A, 0xCA, 0xCD, 0x19, 0xCA, 0x38, 0x95, 0xCD, 0x0A, 0xCA, 0xCD, 0x27, 0xC1, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0xE5, 0xED, 0x4B, 0x1D, 0x00, 0x11, 0x00, 0x06, 0x21, 0x00, 0x08, 0x1A, 0xBE, 0x20, 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x0D, 0xCD, 0xF2, 0xD4, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x8D, 0x18, 0x03, 0xCD, 0x33, 0xCA, 0xCD, 0xFE, 0xC9, 0xCD, 0x27, 0xCA, 0xDA, 0xCE, 0xC8, 0x18, 0xB3, 0xCD, 0x27, 0xC1, 0x06, 0x00, 0xF3, 0x06, 0x17, 0x21, 0xD4, 0xC9, 0xDB, 0x31, 0x32, 0x1F, 0x00, 0x7E, 0x5F, 0x16, 0x00, 0xC5, 0xE5, 0xFE, 0xFF, 0x20, 0x06, 0xCD, 0x80, 0xD4, 0xB7, 0x18, 0x35, 0xFE, 0xFE, 0x20, 0x13, 0xCD, 0x27, 0xC1, 0x20, 0x19, 0x73, 0x74, 0x14, 0xE5, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0xCD, 0x88, 0xC3, 0x18, 0x19, 0xFE, 0xFD, 0x20, 0x04, 0xED, 0x5B, 0x12, 0x00, 0xED, 0x53, 0x18, 0x00, 0xEB, 0xCD, 0x15, 0xD3, 0xEB, 0x3E, 0xD3, 0x32, 0x09, 0x00, 0xCD, 0xAF, 0xC3, 0xF5, 0xCD, 0xEB, 0xC9, 0xF1, 0xE1, 0xC1, 0xD8, 0x23, 0x10, 0xB0, 0xC9, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0x06, 0x07, 0x08, 0x09, 0x00, 0xFF, 0xFD, 0x10, 0x20, 0x00, 0x01, 0xFF, 0xFE, 0xFF, 0xFD, 0xFF, 0x30, 0x09, 0xCD, 0x58, 0xD3, 0xCA, 0xE6, 0xCB, 0xC3, 0xF4, 0xCB, 0xCD, 0xF2, 0xD4, 0x20, 0x4F, 0x4B, 0xA0, 0xC9, 0xCD, 0x27, 0xC1, 0x20, 0x98, 0x21, 0x00, 0x04, 0x22, 0x1B, 0x00, 0xC9, 0x22, 0x1B, 0x00, 0xCD, 0x27, 0xC1, 0x20, 0x50, 0x61, 0x74, 0x74, 0x16, 0x6E, 0xA0, 0xC9, 0x3E, 0xD2, 0xCD, 0x3C, 0xCA, 0xCD, 0x27, 0xC1, 0x87, 0xCD, 0xC0, 0xC3, 0x18, 0x0C, 0x3E, 0xD7, 0xCD, 0x3C, 0xCA, 0xCD, 0x27, 0xC1, 0x88, 0xCD, 0xD0, 0xC3, 0xF5, 0xCD, 0xEB, 0xC9, 0xF1, 0xD2, 0x80, 0xD4, 0xC9, 0x32, 0x09, 0x00, 0x3A, 0x12, 0x00, 0x32, 0x18, 0x00, 0xDB, 0x31, 0x32, 0x1F, 0x00, 0x3E, 0x01, 0x32, 0x1A, 0x00, 0xC9, 0xDB, 0x44, 0x3C, 0xC9, 0xCD, 0xF2, 0xD4, 0x0D, 0x54, 0x79, 0x70, 0x65, 0x20, 0x28, 0x46, 0x2C, 0x20, 0x48, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x48, 0x20, 0x26, 0xCD, 0xF2, 0xD4, 0x55, 0x6E, 0x69, 0x74, 0x20, 0x28, 0x30, 0x2D, 0x33, 0x46, 0x29, 0xA0, 0xCD, 0x1A, 0xD4, 0xCD, 0xB2, 0xD3, 0xB7, 0xC2, 0x08, 0xC1, 0x7C, 0xA7, 0xC2, 0x08, 0xC1, 0x7D, 0x47, 0xCD, 0xD6, 0xC1, 0xC3, 0x51, 0xDA, 0xFE, 0x46, 0xC2, 0xF7, 0xC0, 0xCD, 0x27, 0xC1, 0x0F, 0x28, 0x41, 0x2D, 0x44, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xD6, 0x41, 0xFE, 0x04, 0xD2, 0xFE, 0xC0, 0x57, 0xCD, 0xF2, 0xD4, 0x53, 0x69, 0x7A, 0x65, 0x20, 0x28, 0x4C, 0x2C, 0x20, 0x53, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x4C, 0x06, 0x04, 0x28, 0x05, 0xD6, 0x53, 0x20, 0xCC, 0x47, 0xCD, 0xF2, 0xD4, 0x53, 0x70, 0x65, 0x65, 0x64, 0x20, 0x28, 0x46, 0x2C, 0x20, 0x53, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x53, 0x28, 0x06, 0xFE, 0x46, 0x20, 0xB0, 0xCB, 0xD8, 0x37, 0xC3, 0x4B, 0xC1, 0xCD, 0x02, 0xD5, 0xCD, 0xD1, 0xD4, 0xCD, 0x2C, 0xD3, 0xFE, 0x0D, 0xCA, 0xD8, 0xD4, 0xCD, 0x8C, 0xD4, 0xC3, 0x80, 0xD4, 0xCD, 0x80, 0xD4, 0xCD, 0xF2, 0xD4, 0x42, 0x61, 0x6E, 0x6B, 0xA0, 0x47, 0xCD, 0x07, 0xD3, 0xCD, 0x51, 0xD3, 0x78, 0xCD, 0x89, 0xD2, 0x08, 0x01, 0x00, 0x10, 0xAF, 0xC4, 0x9F, 0xD4, 0x79, 0xCD, 0x07, 0xD3, 0x0C, 0x10, 0xF6, 0xCD, 0x80, 0xD4, 0x06, 0x08, 0xCD, 0x9F, 0xD4, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0x9F, 0xD4, 0xE5, 0x01, 0x00, 0x10, 0xE5, 0xE5, 0x21, 0x00, 0x00, 0x11, 0x62, 0xCB, 0xA7, 0xED, 0x52, 0xEB, 0xE1, 0x19, 0xE1, 0x30, 0x11, 0xE5, 0xE5, 0x21, 0x00, 0x00, 0x11, 0x71, 0xCB, 0xA7, 0xED, 0x52, 0xEB, 0xE1, 0x19, 0xE1, 0x30, 0x1A, 0x08, 0xFE, 0x01, 0x28, 0x02, 0xD3, 0x40, 0x08, 0x56, 0x3E, 0x55, 0x77, 0xBE, 0x20, 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x72, 0x16, 0x58, 0x18, 0x09, 0x72, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xBE, 0x16, 0x5E, 0x08, 0x47, 0x08, 0x3E, 0x01, 0xB8, 0x28, 0x02, 0xD3, 0x40, 0x7A, 0xCD, 0xA1, 0xD4, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xA0, 0xC9, 0x21, 0xB2, 0xCB, 0x36, 0xAA, 0x01, 0x01, 0x06, 0x79, 0xCD, 0x89, 0xD2, 0xD3, 0x40, 0x3E, 0x55, 0xBE, 0x3E, 0x01, 0xD3, 0x40, 0x79, 0xC5, 0xE5, 0xCC, 0xFD, 0xCA, 0xE1, 0xC1, 0x0C, 0x10, 0xE8, 0x36, 0x55, 0xC9, 0x55, 0x01, 0x22, 0x00, 0x11, 0x5E, 0x00, 0x21, 0xC1, 0xCB, 0xED, 0xB0, 0xC3, 0x5E, 0x00, 0x01, 0x00, 0x20, 0x11, 0x00, 0x01, 0x21, 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xD1, 0xE1, 0xC1, 0xD3, 0x40, 0xED, 0xB0, 0x3E, 0x55, 0x32, 0xB2, 0xCB, 0x32, 0x2B, 0x00, 0x3E, 0x01, 0xD3, 0x40, 0xC9, 0xCD, 0x22, 0xCE, 0x3A, 0x21, 0x00, 0xCD, 0x0E, 0xCC, 0xCD, 0x1F, 0xCC, 0x18, 0x12, 0xCD, 0x22, 0xCE, 0x3A, 0x44, 0x00, 0xCD, 0x0E, 0xCC, 0x3A, 0x45, 0x00, 0xCD, 0xFE, 0xD2, 0xCD, 0x7C, 0xCC, 0xCD, 0x80, 0xD4, 0x3A, 0x09, 0x00, 0xB7, 0xF8, 0xC3, 0x8D, 0xC0, 0xF5, 0xCD, 0x27, 0xC1, 0x20, 0x02, 0xAD, 0x3A, 0x09, 0x00, 0xCD, 0xA1, 0xD4, 0xF1, 0xC3, 0xF3, 0xD2, 0x2E, 0xD9, 0x3A, 0x09, 0x00, 0xE6, 0x7F, 0xFE, 0x48, 0x28, 0x0F, 0xFE, 0x53, 0x28, 0x0B, 0x2E, 0x9F, 0xFE, 0x52, 0x28, 0x05, 0x2E, 0xFF, 0xFE, 0x57, 0xC0, 0xCD, 0x51, 0xD3, 0x3A, 0x21, 0x00, 0xA5, 0x21, 0x59, 0xCC, 0x17, 0xD4, 0x35, 0xD3, 0x30, 0x0C, 0xF5, 0xCD, 0xA9, 0xCC, 0xF1, 0xB7, 0xC8, 0xCD, 0xF2, 0xD4, 0x2C, 0xA0, 0xB7, 0xC8, 0x18, 0xEA, 0x0F, 0x10, 0x07, 0xF9, 0x08, 0x97, 0x08, 0x20, 0x9A, 0x19, 0x63, 0x14, 0x64, 0x20, 0x10, 0x46, 0x6F, 0x75, 0x6E, 0xE4, 0x0C, 0x82, 0x18, 0x4C, 0x6F, 0x73, 0xF4, 0x18, 0x19, 0x71, 0x75, 0x65, 0x73, 0xF4, 0x9E, 0xCD, 0x51, 0xD3, 0x3A, 0x44, 0x00, 0xF5, 0x21, 0xC8, 0xCC, 0xCD, 0x9F, 0xCC, 0xE1, 0x3A, 0x45, 0x00, 0x28, 0x0D, 0x6F, 0x3C, 0xC8, 0x84, 0xFE, 0x10, 0xC8, 0x7D, 0xCD, 0xF2, 0xD4, 0x3B, 0xA0, 0x21, 0x46, 0xCD, 0x3C, 0xC8, 0x3D, 0x28, 0x05, 0xCD, 0x35, 0xD3, 0x18, 0xF8, 0x7E, 0xE6, 0x7F, 0xFE, 0x20, 0xD4, 0xA1, 0xD4, 0x30, 0x0F, 0xE5, 0x21, 0x90, 0xCD, 0x3D, 0xF4, 0x35, 0xD3, 0xF2, 0xB7, 0xCC, 0xCD, 0xA9, 0xCC, 0xE1, 0x7E, 0x23, 0xB7, 0xF8, 0x18, 0xE1, 0x01, 0x06, 0x20, 0x26, 0x20, 0x07, 0x20, 0x8E, 0x01, 0x06, 0x11, 0x84, 0x03, 0x09, 0x0A, 0x86, 0x01, 0x06, 0x20, 0x43, 0x14, 0x72, 0x12, 0x20, 0x54, 0x72, 0x61, 0x63, 0xEB, 0x01, 0x07, 0x20, 0x8E, 0x01, 0x05, 0x11, 0x84, 0x03, 0x09, 0x0B, 0x85, 0x0F, 0x10, 0x07, 0xF9, 0x01, 0x08, 0x11, 0x03, 0x82, 0x01, 0x0D, 0x0B, 0x88, 0x01, 0x07, 0x11, 0x03, 0x82, 0x01, 0x07, 0x11, 0x0C, 0x82, 0x43, 0x61, 0x6E, 0x10, 0x4C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x12, 0x94, 0x0F, 0x08, 0x97, 0x01, 0x53, 0x65, 0x6C, 0x12, 0x20, 0x9B, 0x01, 0x53, 0x65, 0x6C, 0x12, 0x20, 0x48, 0x65, 0x61, 0xE4, 0x49, 0x6E, 0x64, 0x65, 0x78, 0x20, 0x50, 0x75, 0x6C, 0x73, 0x65, 0x20, 0x84, 0x06, 0x20, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x20, 0x82, 0x1D, 0x42, 0x75, 0x66, 0x66, 0x96, 0x1D, 0x13, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x9B, 0x0F, 0x1E, 0x11, 0x13, 0x4C, 0x6F, 0xF7, 0x04, 0x20, 0x09, 0x0A, 0x85, 0x03, 0x19, 0x70, 0x14, 0x74, 0x95, 0x01, 0x07, 0x11, 0x0C, 0x82, 0x0E, 0x20, 0x44, 0x6F, 0x65, 0x1F, 0x10, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x72, 0xE5, 0x01, 0x0D, 0x0B, 0x88, 0x18, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x82, 0x0E, 0x20, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x82, 0x20, 0x54, 0x65, 0x73, 0xF4, 0x46, 0x61, 0x69, 0x6C, 0x15, 0x20, 0x74, 0x6F, 0xA0, 0x45, 0x72, 0x72, 0x94, 0x1A, 0xA0, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0xF4, 0x19, 0x5A, 0x16, 0xEF, 0x53, 0x65, 0x65, 0xEB, 0x19, 0x61, 0xE4, 0x57, 0x1C, 0x74, 0xE5, 0x4F, 0x63, 0x63, 0x75, 0x72, 0x72, 0x95, 0x20, 0x44, 0x75, 0x1C, 0x6E, 0x67, 0xA0, 0x20, 0x41, 0x66, 0x74, 0x16, 0xA0, 0x43, 0x52, 0x43, 0xA0, 0x56, 0x16, 0x69, 0x66, 0xF9, 0x48, 0x65, 0x61, 0x64, 0x96, 0x1B, 0xA0, 0x6E, 0x6F, 0x74, 0xA0, 0x2C, 0xA0, 0x65, 0x63, 0xF4, 0x41, 0x43, 0x4B, 0xA0, 0x6F, 0xF2, 0x65, 0xE4, 0x65, 0xF2, 0x20, 0x50, 0x72, 0x6F, 0x74, 0x12, 0x95, 0x44, 0x61, 0x74, 0x61, 0xA0, 0x52, 0xE5, 0x46, 0x61, 0x75, 0x6C, 0xF4, 0x44, 0x1C, 0x76, 0xE5, 0x72, 0xE9, 0x4E, 0x6F, 0xA0, 0x42, 0x75, 0x73, 0xF9, 0x73, 0xA0, 0x21, 0x1A, 0x00, 0x7E, 0xF5, 0x36, 0xFF, 0xCD, 0x28, 0xCE, 0xF1, 0x32, 0x1A, 0x00, 0xC9, 0xCD, 0x28, 0xCE, 0xC3, 0x80, 0xD4, 0xCD, 0x58, 0xD3, 0x28, 0x1F, 0xCD, 0x9F, 0xD4, 0x3A, 0x17, 0x00, 0xCD, 0x07, 0xD3, 0xCD, 0x9F, 0xD4, 0x2A, 0x18, 0x00, 0x7C, 0xCD, 0x07, 0xD3, 0x7D, 0xCD, 0xFE, 0xD2, 0x3A, 0x1A, 0x00, 0xB7, 0xF8, 0xC3, 0xF3, 0xD2, 0xED, 0x5B, 0x1A, 0x00, 0x3A, 0x18, 0x00, 0x57, 0xCD, 0x9F, 0xD4, 0x3A, 0x17, 0x00, 0xCD, 0x07, 0xD3, 0xCD, 0x9F, 0xD4, 0x7A, 0xCD, 0xFE, 0xD2, 0x7B, 0x18, 0xE0, 0x21, 0x17, 0x00, 0x3A, 0x0C, 0x00, 0xFE, 0xFF, 0x46, 0x38, 0x09, 0x34, 0x3A, 0x14, 0x00, 0xBE, 0x3C, 0xD0, 0x36, 0x00, 0x2A, 0x18, 0x00, 0x7D, 0x32, 0x1F, 0x00, 0xE5, 0xED, 0x5B, 0x0A, 0x00, 0xB7, 0xED, 0x52, 0xE1, 0x28, 0x09, 0x23, 0x38, 0x02, 0x2B, 0x2B, 0x22, 0x18, 0x00, 0xC9, 0x78, 0x32, 0x17, 0x00, 0xC9, 0x3A, 0x11, 0x00, 0x3C, 0x47, 0x21, 0x1A, 0x00, 0xCD, 0x58, 0xD3, 0x28, 0x09, 0x3E, 0x01, 0x86, 0x77, 0xB8, 0xD8, 0x90, 0x77, 0xC9, 0x3A, 0x15, 0x00, 0xE6, 0x07, 0xE5, 0x21, 0xDE, 0xCE, 0xCD, 0x84, 0xD2, 0x7E, 0xE1, 0xF5, 0x86, 0xB8, 0x38, 0x02, 0x05, 0x90, 0x77, 0x47, 0xF1, 0xFE, 0x04, 0x28, 0x06, 0xFE, 0x06, 0x28, 0x02, 0x05, 0xC9, 0x3E, 0x02, 0x05, 0x28, 0x03, 0x05, 0xC0, 0x3D, 0x77, 0x3D, 0xC9, 0x05, 0x04, 0x02, 0x04, 0x06, 0x0B, 0x03, 0x06, 0x3A, 0x08, 0x00, 0x07, 0x2F, 0xE6, 0x01, 0x32, 0x1A, 0x00, 0xC9, 0xCD, 0xF2, 0xD4, 0x53, 0x74, 0x61, 0x72, 0xF4, 0x21, 0x00, 0x00, 0xCD, 0x73, 0xCF, 0x38, 0xF0, 0x22, 0x18, 0x00, 0xCD, 0xF2, 0xD4, 0x45, 0x6E, 0xE4, 0x2A, 0x12, 0x00, 0xCD, 0x73, 0xCF, 0x38, 0xF2, 0x22, 0x0A, 0x00, 0xAF, 0x32, 0x17, 0x00, 0x32, 0x0C, 0x00, 0x3A, 0x14, 0x00, 0xB7, 0xC8, 0xCD, 0xF2, 0xD4, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x20, 0xA8, 0x47, 0x04, 0x3E, 0x00, 0xF5, 0xFE, 0x10, 0x38, 0x05, 0xCD, 0xFE, 0xD2, 0x18, 0x03, 0xCD, 0x07, 0xD3, 0xCD, 0xF2, 0xD4, 0xAF, 0xF1, 0x3C, 0x10, 0xEB, 0xCD, 0xF2, 0xD4, 0x41, 0x6C, 0x6C, 0x29, 0x20, 0x5B, 0x41, 0x6C, 0x6C, 0x5D, 0xA0, 0xCD, 0x1A, 0xD4, 0x21, 0xFF, 0x00, 0xCD, 0x79, 0xD3, 0xAF, 0xB4, 0x20, 0xB4, 0x7D, 0x32, 0x0C, 0x00, 0xFE, 0xFF, 0xC8, 0x3A, 0x14, 0x00, 0xBD, 0x38, 0xA7, 0x7D, 0x32, 0x17, 0x00, 0xC9, 0xCD, 0x27, 0xC1, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x79, 0x6C, 0x69, 0x6E, 0x64, 0x16, 0x20, 0x28, 0x30, 0xAD, 0xE5, 0x2A, 0x12, 0x00, 0xCD, 0x15, 0xD3, 0xE1, 0xCD, 0xF2, 0xD4, 0x29, 0x20, 0xDB, 0xCD, 0x15, 0xD3, 0xCD, 0xF2, 0xD4, 0x5D, 0xA0, 0xCD, 0x1A, 0xD4, 0xCD, 0x79, 0xD3, 0xEB, 0x2A, 0x12, 0x00, 0xB7, 0xED, 0x52, 0xEB, 0xC9, 0xDB, 0x04, 0xE6, 0x08, 0x20, 0x14, 0x3E, 0x09, 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xD9, 0x0E, 0x00, 0xD9, 0x18, 0x3B, 0xCD, 0x33, 0xD0, 0xCD, 0x22, 0xD0, 0x3E, 0x0A, 0xCD, 0x06, 0xD0, 0x21, 0xD0, 0x07, 0xCD, 0x7C, 0xD8, 0x3E, 0x08, 0xCD, 0x06, 0xD0, 0x16, 0x64, 0x15, 0x28, 0xEB, 0x21, 0x2B, 0xD0, 0x3E, 0x19, 0x06, 0x09, 0xCD, 0x06, 0xD0, 0xD9, 0x79, 0xD9, 0x4F, 0xED, 0xA3, 0x28, 0xEB, 0xCD, 0x0F, 0xD0, 0xCD, 0x0F, 0xD0, 0x28, 0xE6, 0xFE, 0x0D, 0x3E, 0x09, 0x20, 0xE7, 0x3E, 0xFF, 0xCD, 0x18, 0xD5, 0x3E, 0x0D, 0xCD, 0x18, 0xD5, 0xC3, 0x80, 0xD4, 0xD9, 0x0C, 0x0C, 0xED, 0x79, 0x0D, 0x0D, 0xD9, 0xC9, 0xD5, 0x11, 0xA0, 0x8C, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0x20, 0x05, 0x1B, 0x7A, 0xB3, 0x20, 0xF3, 0xD1, 0xC9, 0x06, 0x08, 0xAF, 0xCD, 0x18, 0xD5, 0x10, 0xFA, 0xC9, 0x90, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x01, 0xCD, 0x79, 0xD3, 0xD8, 0x7D, 0xFE, 0x08, 0xD2, 0xF7, 0xC0, 0x21, 0x4F, 0xD0, 0xCD, 0x84, 0xD2, 0x4E, 0xED, 0x78, 0x3C, 0xCA, 0xF7, 0xC0, 0x79, 0xD9, 0x4F, 0xD9, 0xC9, 0x00, 0x20, 0x50, 0x60, 0x70, 0x80, 0x90, 0xF0, 0xCD, 0x84, 0xD3, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, 0xD1, 0xC1, 0x18, 0x03, 0xCD, 0x84, 0xD3, 0x1A, 0xBE, 0x28, 0x13, 0xCD, 0xF9, 0xD2, 0x7E, 0xCD, 0x9C, 0xD4, 0x1A, 0xCD, 0x9C, 0xD4, 0xEB, 0xCD, 0xF9, 0xD2, 0xEB, 0xCD, 0x80, 0xD4, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xE2, 0xC9, 0x13, 0x2A, 0x05, 0x00, 0xCD, 0x79, 0xD3, 0xCD, 0xF9, 0xD2, 0x7E, 0xCD, 0x9C, 0xD4, 0xCD, 0x1A, 0xD4, 0xCD, 0xFA, 0xD0, 0x30, 0x07, 0xCD, 0xF2, 0xD4, 0x3F, 0x8D, 0x18, 0xEA, 0xF5, 0xAF, 0x80, 0x20, 0x07, 0xF1, 0xB7, 0x20, 0x0B, 0x23, 0x18, 0x08, 0x48, 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0xF1, 0x22, 0x05, 0x00, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, 0xCC, 0x2B, 0x22, 0x05, 0x00, 0x18, 0xC6, 0xCD, 0x90, 0xD3, 0x38, 0x05, 0xC5, 0xCD, 0xFA, 0xD0, 0xD1, 0xDA, 0xF7, 0xC0, 0xC2, 0xF7, 0xC0, 0x80, 0xCA, 0xF7, 0xC0, 0xC5, 0xD5, 0xE5, 0x11, 0x5E, 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x6B, 0xD1, 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x5E, 0x00, 0xCD, 0x3E, 0xD3, 0xB7, 0x28, 0x2D, 0xFE, 0x2D, 0x28, 0x29, 0xFE, 0x2E, 0x28, 0x25, 0x13, 0x4F, 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xB2, 0xD3, 0x7D, 0xE1, 0x38, 0x13, 0x77, 0x23, 0x04, 0x18, 0xDA, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, 0xD2, 0x77, 0x23, 0x04, 0x18, 0xF3, 0xB7, 0x11, 0x5E, 0x00, 0xE1, 0xC9, 0xFE, 0x4D, 0x20, 0x01, 0x13, 0x01, 0x80, 0x00, 0x2A, 0x03, 0x00, 0xCD, 0x7F, 0xD3, 0x1E, 0x10, 0xAF, 0xB0, 0x20, 0x0A, 0x3E, 0x0F, 0xB9, 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x6B, 0xD1, 0x22, 0x03, 0x00, 0xC1, 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, 0xCD, 0xF9, 0xD2, 0xC5, 0xE5, 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xAD, 0xD1, 0xCD, 0xAD, 0xD1, 0x7E, 0xCD, 0xFE, 0xD2, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xAD, 0xD1, 0x3E, 0x3A, 0xBB, 0x20, 0xF8, 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x9D, 0xD1, 0x10, 0xF9, 0xC3, 0x80, 0xD4, 0xE6, 0x7F, 0xFE, 0x7F, 0x28, 0x05, 0xFE, 0x20, 0xD2, 0xA1, 0xD4, 0x3E, 0x2E, 0xC3, 0xA1, 0xD4, 0x1C, 0xC3, 0x9F, 0xD4, 0xCD, 0x90, 0xD3, 0x38, 0x04, 0xC5, 0xCD, 0xFA, 0xD0, 0xDA, 0xF7, 0xC0, 0xC2, 0xF7, 0xC0, 0x80, 0xCA, 0xF7, 0xC0, 0xC1, 0xE5, 0xEB, 0xED, 0xA0, 0xE2, 0xD3, 0xD1, 0x3D, 0x20, 0xF8, 0xE1, 0xED, 0xB0, 0xC9, 0xE1, 0xC9, 0xCD, 0x72, 0xD3, 0xE9, 0xCD, 0x72, 0xD3, 0x4D, 0xED, 0x78, 0xCD, 0xFE, 0xD2, 0xC3, 0x80, 0xD4, 0xCD, 0xB2, 0xD3, 0x45, 0xCD, 0x72, 0xD3, 0x4D, 0xED, 0x41, 0xC9, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xCD, 0x45, 0xD3, 0xA7, 0x28, 0x74, 0xE5, 0xFE, 0x2B, 0x28, 0x2D, 0xFE, 0x2D, 0x28, 0x36, 0xFE, 0x2A, 0x28, 0x41, 0xFE, 0x2F, 0x28, 0x50, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xCD, 0x45, 0xD3, 0xA7, 0xC2, 0xF7, 0xC0, 0xEB, 0xE1, 0xE5, 0xD5, 0x19, 0xCD, 0x76, 0xD2, 0xCD, 0xF2, 0xD4, 0x2C, 0xA0, 0xD1, 0xE1, 0xA7, 0xED, 0x52, 0x18, 0x42, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0x19, 0xD1, 0x18, 0xBB, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0xA7, 0xED, 0x52, 0xD1, 0x18, 0xAC, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0xC5, 0x42, 0x4B, 0xCD, 0xA6, 0xD2, 0xC1, 0xD1, 0x18, 0x99, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0xC5, 0x42, 0x4B, 0xCD, 0xBF, 0xD2, 0xC1, 0xD1, 0x18, 0x86, 0xCD, 0x76, 0xD2, 0xC3, 0x80, 0xD4, 0xCD, 0xF9, 0xD2, 0xCD, 0x9F, 0xD4, 0xCD, 0x96, 0xD2, 0x3E, 0x2E, 0xC3, 0xA1, 0xD4, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xC5, 0x47, 0x3E, 0x01, 0x05, 0xFA, 0x94, 0xD2, 0x07, 0x30, 0xF9, 0xC1, 0xC9, 0x01, 0x0A, 0x00, 0xCD, 0xBF, 0xD2, 0x7B, 0xF5, 0x7C, 0xB5, 0xC4, 0x96, 0xD2, 0xF1, 0x18, 0x61, 0x11, 0x00, 0x00, 0xEB, 0x3E, 0x10, 0xB7, 0xCB, 0x43, 0x28, 0x01, 0x09, 0xCB, 0x1C, 0xCB, 0x1D, 0xCB, 0x1A, 0xCB, 0x1B, 0x3D, 0x20, 0xEF, 0xEB, 0xC9, 0x11, 0x00, 0x00, 0x7A, 0xB8, 0x20, 0x02, 0x7B, 0xB9, 0x3F, 0xD8, 0xEB, 0x3E, 0x10, 0x37, 0xCB, 0x13, 0xCB, 0x12, 0xCB, 0x15, 0xCB, 0x14, 0x30, 0x05, 0xB7, 0xED, 0x42, 0x18, 0x06, 0xED, 0x42, 0x30, 0x02, 0x1D, 0x09, 0x3D, 0x20, 0xE7, 0xEB, 0xB7, 0xC9, 0xF5, 0xCD, 0x45, 0xD3, 0xB7, 0xC2, 0xF7, 0xC0, 0xF1, 0xC9, 0xCD, 0xF2, 0xD4, 0xA0, 0x18, 0x05, 0x7C, 0xCD, 0xFE, 0xD2, 0x7D, 0xF5, 0x0F, 0x0F, 0x0F, 0x0F, 0xCD, 0x07, 0xD3, 0xF1, 0xE6, 0x0F, 0xC6, 0x30, 0xFE, 0x3A, 0xDA, 0xA1, 0xD4, 0xC6, 0x07, 0xC3, 0xA1, 0xD4, 0x7C, 0xB7, 0x28, 0x0C, 0xCD, 0x07, 0xD3, 0x7D, 0xCD, 0xFE, 0xD2, 0xCD, 0xF2, 0xD4, 0xC8, 0xC9, 0x7D, 0xFE, 0x0A, 0x30, 0xF3, 0x18, 0xDB, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0x7E, 0x23, 0x3D, 0xF2, 0x36, 0xD3, 0xF1, 0xC9, 0xCD, 0x45, 0xD3, 0xFE, 0x2C, 0xC0, 0x13, 0x1A, 0xFE, 0x20, 0x28, 0x04, 0xFE, 0x09, 0x20, 0xDE, 0x13, 0x18, 0xF4, 0xCD, 0xF2, 0xD4, 0x20, 0x3E, 0xA0, 0xC9, 0x3A, 0x08, 0x00, 0xE6, 0x80, 0x07, 0xC9, 0x3A, 0x07, 0x00, 0xFE, 0xFF, 0xC0, 0xCD, 0x27, 0xC1, 0x1D, 0x0F, 0x53, 0x65, 0x6C, 0x12, 0x95, 0xC3, 0xDE, 0xD4, 0xCD, 0x79, 0xD3, 0xD0, 0xC3, 0xF7, 0xC0, 0xCD, 0xB2, 0xD3, 0xC3, 0xE9, 0xD2, 0xCD, 0x90, 0xD3, 0x18, 0xF8, 0xCD, 0x90, 0xD3, 0x38, 0xED, 0xE5, 0xCD, 0x72, 0xD3, 0xEB, 0xE1, 0xC9, 0xCD, 0xB2, 0xD3, 0xCD, 0x3E, 0xD3, 0xFE, 0x53, 0xE5, 0x20, 0x0A, 0x13, 0xCD, 0xB2, 0xD3, 0x38, 0xD5, 0x44, 0x4D, 0xE1, 0xC9, 0xCD, 0xB2, 0xD3, 0x38, 0xF9, 0xC1, 0xC5, 0xED, 0x42, 0x23, 0xB7, 0x18, 0xEF, 0xCD, 0x3E, 0xD3, 0xD5, 0xE5, 0xCD, 0xEE, 0xD3, 0xFE, 0x2E, 0xE1, 0xD1, 0x28, 0x0D, 0xCD, 0x09, 0xD4, 0xD8, 0xCD, 0xEE, 0xD3, 0xFE, 0x48, 0x28, 0x09, 0xB7, 0xC9, 0xCD, 0xFE, 0xD3, 0xD8, 0xCD, 0xD9, 0xD3, 0xCD, 0x4E, 0xD3, 0xB7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xFE, 0xD3, 0xD8, 0x13, 0xC5, 0x44, 0x4D, 0x29, 0x29, 0x09, 0x29, 0xC1, 0xCD, 0x84, 0xD2, 0x18, 0xEE, 0x21, 0x00, 0x00, 0xCD, 0x09, 0xD4, 0xD8, 0x13, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x18, 0xF3, 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0xCD, 0xFE, 0xD3, 0xD0, 0xCD, 0x2C, 0xD3, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, 0x37, 0xC9, 0x11, 0x5E, 0x00, 0x3E, 0x24, 0xC5, 0xE5, 0x4F, 0x06, 0x00, 0x62, 0x6B, 0xCD, 0x02, 0xD5, 0xCD, 0xC7, 0xD4, 0xFE, 0x10, 0x28, 0xF6, 0xFE, 0x05, 0xCC, 0x80, 0xD4, 0x28, 0xEF, 0xFE, 0x0D, 0x28, 0x3F, 0xFE, 0x08, 0x28, 0x04, 0xFE, 0x7F, 0x20, 0x10, 0x05, 0xFA, 0x22, 0xD4, 0x2B, 0xCD, 0x85, 0xD4, 0x7E, 0xFE, 0x20, 0xDC, 0x85, 0xD4, 0x18, 0xD3, 0xFE, 0x16, 0x20, 0x10, 0x05, 0xFA, 0x22, 0xD4, 0x2B, 0xCD, 0x85, 0xD4, 0x7E, 0xFE, 0x20, 0xDC, 0x85, 0xD4, 0x18, 0xF0, 0xF5, 0xCD, 0x8C, 0xD4, 0xF1, 0xFE, 0x15, 0xCC, 0x80, 0xD4, 0x28, 0xAF, 0x77, 0x23, 0x04, 0x79, 0xB8, 0x20, 0xAC, 0x36, 0x00, 0x78, 0xE1, 0xC1, 0xB7, 0xCD, 0xF2, 0xD4, 0x8D, 0xC9, 0xCD, 0xF2, 0xD4, 0x08, 0x20, 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x11, 0xFE, 0x0D, 0x28, 0x0D, 0xC6, 0x40, 0xCD, 0xF2, 0xD4, 0xDE, 0x18, 0x05, 0xCD, 0xF3, 0xD2, 0x3E, 0x20, 0xF5, 0xE6, 0x7F, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0x29, 0xD5, 0xD9, 0xCB, 0x60, 0xD9, 0xCC, 0x18, 0xD5, 0xFE, 0x0D, 0x3E, 0x0A, 0xCC, 0xA1, 0xD4, 0xCC, 0xC1, 0xD4, 0xCD, 0x21, 0xC8, 0xF1, 0xC9, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0xFE, 0x10, 0xCC, 0x4D, 0xD5, 0xFE, 0x13, 0xCC, 0x02, 0xD5, 0xFE, 0x03, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0xF0, 0x7F, 0xCD, 0x8C, 0xD4, 0xCD, 0x80, 0xD4, 0xC3, 0x8D, 0xC0, 0xF5, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xA1, 0xD4, 0xF2, 0xE5, 0xD4, 0xF1, 0xC9, 0xE3, 0xCD, 0xE4, 0xD4, 0xE3, 0xC9, 0xD9, 0xED, 0x78, 0xD9, 0xE6, 0x40, 0xC8, 0x3E, 0xFF, 0xC9, 0xCD, 0x21, 0xC8, 0xCD, 0xF8, 0xD4, 0x28, 0xF8, 0xCD, 0xF8, 0xD4, 0x28, 0xFB, 0xD9, 0x0C, 0xED, 0x78, 0x0D, 0xD9, 0xE6, 0x7F, 0xC9, 0xF5, 0xD9, 0xED, 0x78, 0xE6, 0x80, 0x28, 0xFA, 0x0C, 0xF1, 0xED, 0x79, 0xF5, 0x0D, 0xD9, 0xF1, 0xC9, 0xF5, 0xC5, 0x06, 0xF7, 0x10, 0xFE, 0xC1, 0xCD, 0xC1, 0xD4, 0xFE, 0x10, 0x28, 0x24, 0xDB, 0x54, 0xE6, 0x20, 0x20, 0xF3, 0xF1, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xF5, 0xD9, 0x3E, 0x80, 0xA8, 0x47, 0xD9, 0xCB, 0x7F, 0x3E, 0x11, 0xC4, 0x3E, 0xD5, 0xF1, 0xC9, 0xFD, 0xC7, 0x40, 0xC4, 0xF7, 0xC0, 0x39, 0xD1, 0xD9, 0xD1, 0xF7, 0xC0, 0xD5, 0xD1, 0xF0, 0xD1, 0xBE, 0xCF, 0xF7, 0xC0, 0xF7, 0xC0, 0xF7, 0xC0, 0x57, 0xD0, 0xF7, 0xC0, 0xE5, 0xD1, 0xF7, 0xC0, 0xC7, 0xD0, 0x7C, 0xC6, 0xA4, 0xC7, 0x39, 0xC8, 0xF7, 0xC0, 0x64, 0xD0, 0x86, 0xC6, 0xF7, 0xC0, 0xF7, 0xC0, 0xB1, 0xD1, 0x16, 0x02, 0xD5, 0xCD, 0x02, 0xD6, 0xD1, 0xD0, 0xD5, 0x3E, 0x0A, 0x32, 0x18, 0x00, 0xCD, 0x60, 0xD6, 0xCD, 0x02, 0xD6, 0xD1, 0xD0, 0x15, 0x20, 0xE9, 0x18, 0x50, 0xD5, 0x3A, 0x17, 0x00, 0xF5, 0x3A, 0x18, 0x00, 0xF5, 0x3A, 0x15, 0x00, 0xF5, 0xCB, 0x87, 0x32, 0x15, 0x00, 0xCD, 0x02, 0xD6, 0xF1, 0x32, 0x15, 0x00, 0xF1, 0x32, 0x18, 0x00, 0xF1, 0x32, 0x17, 0x00, 0xCD, 0x60, 0xD6, 0xD1, 0xC9, 0x1E, 0x02, 0x16, 0x0A, 0xD5, 0xCD, 0xE5, 0xD6, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x1D, 0x28, 0x1A, 0xCD, 0xAC, 0xD5, 0x18, 0xED, 0x1E, 0x02, 0x16, 0x04, 0xD5, 0xCD, 0x2B, 0xD7, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x1D, 0x28, 0x05, 0xCD, 0xAC, 0xD5, 0x18, 0xED, 0x3A, 0x21, 0x00, 0x4F, 0x37, 0xC9, 0x97, 0x32, 0x18, 0x00, 0x32, 0x17, 0x00, 0x32, 0x1F, 0x00, 0xCD, 0xE0, 0xD7, 0xD3, 0x34, 0xCD, 0xBA, 0xD6, 0x38, 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, 0xF7, 0xC3, 0xCB, 0xD6, 0x3E, 0xC4, 0xD3, 0x30, 0xCD, 0x61, 0xD8, 0xE6, 0x57, 0xD3, 0x04, 0xCD, 0x6F, 0xD8, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0x16, 0xCD, 0x5A, 0xD8, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x18, 0x1F, 0x30, 0xF7, 0x3E, 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0xDD, 0xD6, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x05, 0x1F, 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x21, 0x00, 0x37, 0xC9, 0x97, 0xCD, 0xE0, 0xD7, 0xD3, 0x34, 0x3A, 0x18, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x1A, 0x00, 0xD3, 0x32, 0x3A, 0x1F, 0x00, 0xD3, 0x31, 0x91, 0xCA, 0x5A, 0xD8, 0xCD, 0xBA, 0xD6, 0x38, 0x0F, 0xF6, 0x10, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x3D, 0xCD, 0x61, 0xD8, 0xE6, 0x4F, 0xD3, 0x04, 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, 0x30, 0xF7, 0xDB, 0x30, 0x2E, 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0xCD, 0x5A, 0xD8, 0x18, 0x23, 0x3A, 0x15, 0x00, 0xCB, 0x5F, 0x3E, 0x0E, 0x28, 0x06, 0xE6, 0x04, 0x37, 0xC0, 0x3E, 0x0C, 0xA7, 0xC9, 0xCD, 0x5A, 0xD8, 0x21, 0x64, 0x00, 0xCD, 0x7C, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0x98, 0x37, 0xC0, 0x3A, 0x18, 0x00, 0x32, 0x1F, 0x00, 0xA7, 0xC9, 0xCD, 0x1C, 0xD7, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x16, 0xED, 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0xEA, 0xD6, 0xDB, 0x34, 0xCB, 0x4F, 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0x5A, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0x9C, 0xC8, 0x18, 0x08, 0xCD, 0x5A, 0xD8, 0x3E, 0x80, 0x32, 0x21, 0x00, 0x37, 0xC9, 0xCD, 0xCB, 0xD7, 0x57, 0xCD, 0x51, 0xD8, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, 0x6F, 0xD8, 0xCD, 0xCB, 0xD7, 0x57, 0xCD, 0x51, 0xD8, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, 0xED, 0xA3, 0xC2, 0x3E, 0xD7, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0x5A, 0xD8, 0xCD, 0x6F, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x15, 0x00, 0xCB, 0x4F, 0xC8, 0xCD, 0x8B, 0xD7, 0x38, 0x0A, 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, 0x5A, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, 0xCD, 0x1C, 0xD7, 0xED, 0x4B, 0x1D, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, 0x1E, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, 0xDB, 0x33, 0x3E, 0x80, 0xCD, 0xE0, 0xD7, 0x2A, 0x1D, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, 0x33, 0x2A, 0x1B, 0x00, 0xC9, 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x23, 0x00, 0xCD, 0x33, 0xD8, 0xD3, 0x34, 0xF5, 0xE5, 0xCD, 0x61, 0xD8, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x23, 0x00, 0xA7, 0x28, 0x21, 0x21, 0x90, 0x01, 0x3A, 0x24, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x25, 0x00, 0x67, 0x3A, 0x17, 0x00, 0x32, 0x25, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, 0xCD, 0x6F, 0xD8, 0x18, 0x06, 0x21, 0x20, 0x4E, 0xCD, 0x7C, 0xD8, 0xE1, 0x78, 0x32, 0x24, 0x00, 0x3E, 0x01, 0x32, 0x23, 0x00, 0xF1, 0xB1, 0xC9, 0x3A, 0x16, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, 0x10, 0xFD, 0x47, 0x3A, 0x15, 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, 0xCB, 0xF0, 0x78, 0xF6, 0x20, 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, 0x61, 0xD8, 0xD3, 0x04, 0xAF, 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x17, 0x00, 0xA7, 0x28, 0x02, 0x06, 0x7D, 0x78, 0xC1, 0xC9, 0x21, 0x08, 0x00, 0x3A, 0x15, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, 0x00, 0xC5, 0x2B, 0x06, 0x1C, 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0x3A, 0x2D, 0x00, 0xA7, 0x28, 0x05, 0xFE, 0x01, 0xC8, 0x18, 0x4F, 0x3C, 0x32, 0x2D, 0x00, 0xDB, 0xF8, 0xA7, 0x28, 0x04, 0xFE, 0x01, 0x20, 0x42, 0x21, 0x29, 0xD9, 0x11, 0x2E, 0x00, 0x01, 0x0E, 0x00, 0xED, 0xB0, 0x21, 0x2E, 0x00, 0x7C, 0x32, 0x32, 0x00, 0x7D, 0x32, 0x34, 0x00, 0x06, 0x07, 0x0E, 0x00, 0x21, 0x2E, 0x00, 0x7E, 0x23, 0xD3, 0xF8, 0x81, 0x4F, 0xCD, 0x10, 0xD9, 0x38, 0x1A, 0x10, 0xF3, 0x79, 0xD3, 0xF8, 0x21, 0x2E, 0x00, 0x11, 0x2F, 0x00, 0x01, 0x2F, 0x00, 0x36, 0x00, 0xED, 0xB0, 0xDB, 0xF8, 0xA7, 0xC8, 0xFE, 0x01, 0x20, 0xF8, 0xCD, 0xF2, 0xD4, 0x43, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, 0x20, 0x53, 0x54, 0x44, 0x43, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x6C, 0x65, 0x72, 0x8D, 0x3E, 0xFF, 0x32, 0x2D, 0x00, 0x37, 0xC9, 0xD5, 0xC5, 0x4E, 0x23, 0x06, 0x0A, 0x11, 0x00, 0x00, 0xDB, 0xF8, 0xB9, 0x28, 0x08, 0x1B, 0x7A, 0xB3, 0x20, 0xF6, 0x10, 0xF1, 0x37, 0xC1, 0xD1, 0xC9, 0x11, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xCD, 0x5D, 0xDA, 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x3E, 0x12, 0xF5, 0x7E, 0xE6, 0x60, 0x77, 0xF1, 0xD3, 0xF8, 0xC5, 0xD5, 0x06, 0x1E, 0x11, 0x00, 0x00, 0x7E, 0xCB, 0x7F, 0x20, 0x13, 0x1B, 0x7A, 0xB3, 0x20, 0xF6, 0x10, 0xF1, 0xAF, 0x32, 0x45, 0x00, 0x3E, 0x07, 0x32, 0x44, 0x00, 0x3E, 0x01, 0x77, 0xD1, 0xC1, 0xC9, 0xCD, 0x5D, 0xDA, 0x3A, 0x16, 0x00, 0x32, 0x2F, 0x00, 0x3E, 0x81, 0x32, 0x2E, 0x00, 0x3E, 0x19, 0x18, 0xC4, 0xCD, 0xA5, 0xD9, 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x32, 0x2E, 0x00, 0x20, 0x11, 0xE5, 0xD5, 0xC5, 0x21, 0x4B, 0x00, 0x11, 0x0D, 0x00, 0x01, 0x03, 0x00, 0xED, 0xB0, 0xC1, 0xD1, 0xE1, 0x3E, 0x17, 0xCD, 0x41, 0xD9, 0xED, 0x53, 0x44, 0x00, 0xC9, 0xCD, 0x5D, 0xDA, 0xE5, 0x11, 0x0D, 0x00, 0x21, 0x47, 0x00, 0x06, 0x03, 0x1A, 0xBE, 0x20, 0x04, 0x23, 0x13, 0x10, 0xF8, 0xE1, 0x11, 0xFF, 0xFF, 0xC8, 0x0E, 0xFF, 0x06, 0x0A, 0x3E, 0x15, 0xCD, 0x41, 0xD9, 0xED, 0x5B, 0x44, 0x00, 0xE6, 0x02, 0xC0, 0x3E, 0xFF, 0xBA, 0x20, 0x02, 0xBB, 0xC8, 0x4A, 0x10, 0xEA, 0xBB, 0xC0, 0x79, 0xFE, 0x05, 0x20, 0x04, 0x3E, 0x03, 0x18, 0x1A, 0xFE, 0x04, 0x20, 0x04, 0x3E, 0x0B, 0x18, 0x12, 0xFE, 0x07, 0x38, 0x04, 0x3E, 0x0C, 0x18, 0x0A, 0xFE, 0x06, 0x20, 0x04, 0x3E, 0x09, 0x18, 0x02, 0x3E, 0x07, 0x32, 0x44, 0x00, 0x5F, 0x51, 0xA7, 0xC9, 0xCD, 0xA5, 0xD9, 0xC0, 0x3E, 0x80, 0x32, 0x2E, 0x00, 0x3E, 0x17, 0xCD, 0x41, 0xD9, 0x3E, 0x16, 0xC3, 0x41, 0xD9, 0xCD, 0x5D, 0xDA, 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x3E, 0x13, 0xCD, 0x41, 0xD9, 0xED, 0x5B, 0x44, 0x00, 0x3E, 0xFF, 0xBB, 0xC0, 0xBA, 0x20, 0x06, 0x06, 0x0A, 0xCD, 0xC2, 0xD9, 0xC8, 0x3E, 0x00, 0x32, 0x44, 0x00, 0xC9, 0xCD, 0x5D, 0xDA, 0x3E, 0x14, 0xCD, 0x41, 0xD9, 0x3A, 0x44, 0x00, 0xFE, 0xFF, 0xC0, 0x32, 0x2E, 0x00, 0x3E, 0x1B, 0xC3, 0x41, 0xD9, 0x3A, 0x44, 0x00, 0xFE, 0xFF, 0xC9, 0x3A, 0x45, 0x00, 0xFE, 0xFF, 0xC9, 0x21, 0x2E, 0x00, 0x11, 0x2F, 0x00, 0x01, 0x2F, 0x00, 0x36, 0x00, 0xED, 0xB0, 0xDD, 0xE5, 0xDD, 0x21, 0x2E, 0x00, 0x3A, 0x16, 0x00, 0xF6, 0x1F, 0xDD, 0x77, 0x01, 0x3A, 0x17, 0x00, 0xDD, 0x77, 0x19, 0x2A, 0x18, 0x00, 0xDD, 0x74, 0x1A, 0xDD, 0x75, 0x1B, 0x3A, 0x1A, 0x00, 0xDD, 0x77, 0x18, 0x2A, 0x1B, 0x00, 0xDD, 0x74, 0x0A, 0xDD, 0x75, 0x0B, 0x2A, 0x1D, 0x00, 0xDD, 0x74, 0x06, 0xDD, 0x75, 0x07, 0x3A, 0x10, 0x00, 0xE6, 0x60, 0xDD, 0x77, 0x15, 0xDD, 0xE1, 0x21, 0x43, 0x00, 0xC9, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; unsigned char ccs2422_rom[2048] = { 0xC3, 0x5B, 0xF0, 0xC3, 0x46, 0xF6, 0xC3, 0x56, 0xF6, 0xC3, 0x00, 0xF6, 0xC3, 0x7C, 0xF6, 0xC3, 0x10, 0xF6, 0xC3, 0x23, 0xF6, 0xC3, 0x6A, 0xF1, 0xC3, 0x65, 0xF1, 0xC3, 0x8A, 0xF0, 0xC3, 0x94, 0xF6, 0xC3, 0x94, 0xF6, 0xC3, 0xCF, 0xF3, 0xF8, 0xF0, 0x5E, 0xF5, 0x09, 0xF1, 0xAC, 0xF1, 0x09, 0xF1, 0x3C, 0xF1, 0xFD, 0xF1, 0xD0, 0xF5, 0x4D, 0xF2, 0x09, 0xF1, 0x09, 0xF1, 0x09, 0xF1, 0x5D, 0xF2, 0x09, 0xF1, 0x55, 0xF2, 0xA7, 0xF5, 0xBD, 0xF5, 0xF6, 0xF4, 0x67, 0xF2, 0x8F, 0xF2, 0x09, 0xF1, 0x91, 0xF1, 0xF7, 0xF4, 0xEC, 0xF2, 0x9F, 0xF4, 0x82, 0xF1, 0xF3, 0x31, 0x3F, 0x00, 0x21, 0x00, 0xC3, 0x11, 0xB2, 0xF6, 0x06, 0x10, 0xD5, 0xE5, 0x10, 0xFC, 0x31, 0x95, 0xF0, 0x3E, 0xC5, 0x01, 0x00, 0xF0, 0x21, 0xFF, 0xFF, 0x24, 0x7E, 0x2F, 0x77, 0xBE, 0x2F, 0x77, 0x20, 0x04, 0x7C, 0xB8, 0x20, 0xF3, 0x25, 0x01, 0xDE, 0xFF, 0x09, 0xC1, 0xC9, 0xE5, 0xCD, 0x6F, 0xF0, 0x7D, 0xD6, 0x3C, 0x30, 0x01, 0x25, 0x44, 0xE1, 0xC9, 0x99, 0xF0, 0xF9, 0x11, 0x45, 0xF4, 0xEB, 0x01, 0x1D, 0x00, 0xED, 0xB0, 0x01, 0x06, 0x00, 0xD5, 0xE1, 0x2B, 0xED, 0xB0, 0x21, 0xE8, 0xFF, 0x39, 0xE5, 0x23, 0x23, 0x22, 0x06, 0x00, 0x16, 0x0A, 0xC5, 0x15, 0x20, 0xFC, 0xCD, 0x59, 0xF5, 0xCD, 0x9F, 0xF4, 0xCD, 0x94, 0xF6, 0x21, 0x90, 0xF4, 0xCD, 0x95, 0xF6, 0x18, 0x43, 0x06, 0x01, 0x21, 0x00, 0x00, 0x18, 0x0C, 0x10, 0x79, 0x20, 0x32, 0x05, 0xC8, 0x21, 0x00, 0x00, 0xCD, 0x7B, 0xF3, 0x4F, 0xCD, 0xB0, 0xF3, 0x38, 0x08, 0x29, 0x29, 0x29, 0x29, 0xB5, 0x6F, 0x18, 0xEF, 0xE3, 0xE5, 0x79, 0xCD, 0xC3, 0xF3, 0x30, 0xE0, 0x10, 0x12, 0xC9, 0xCD, 0x7B, 0xF3, 0x21, 0x6E, 0xF1, 0x11, 0x05, 0x00, 0x06, 0x04, 0xBE, 0x28, 0x42, 0x19, 0x10, 0xFA, 0x21, 0x8C, 0xF4, 0xCD, 0x98, 0xF6, 0x2A, 0x06, 0x00, 0xF9, 0x21, 0x0F, 0xF1, 0xE5, 0x22, 0x01, 0x00, 0x3E, 0xC3, 0x32, 0x00, 0x00, 0xCD, 0xA9, 0xF6, 0xCD, 0x78, 0xF3, 0xD6, 0x41, 0x38, 0xE0, 0xFE, 0x1A, 0x30, 0xDC, 0x87, 0x5F, 0x16, 0x00, 0x06, 0x02, 0x21, 0x27, 0xF0, 0x19, 0x7E, 0x23, 0x66, 0x6F, 0xE9, 0xCD, 0x86, 0xF3, 0x71, 0xCD, 0x8F, 0xF3, 0x30, 0xFA, 0xD1, 0x18, 0xC7, 0x50, 0x06, 0x04, 0xCD, 0x78, 0xF3, 0x23, 0xBE, 0x20, 0x81, 0x68, 0x2D, 0x42, 0x26, 0x03, 0x05, 0x28, 0x04, 0x29, 0x29, 0x10, 0xFC, 0x3A, 0x03, 0x00, 0xB4, 0xAC, 0xB5, 0x4F, 0x79, 0x32, 0x03, 0x00, 0xC9, 0x3A, 0x03, 0x00, 0xC9, 0x4C, 0x32, 0x31, 0x4C, 0x54, 0x50, 0x32, 0x31, 0x50, 0x54, 0x52, 0x32, 0x31, 0x50, 0x54, 0x43, 0x31, 0x42, 0x43, 0x54, 0x06, 0x02, 0xCD, 0x8F, 0xF6, 0xFE, 0x07, 0x20, 0xF7, 0xCD, 0x7E, 0xF3, 0x10, 0xF4, 0xC9, 0xCD, 0x86, 0xF3, 0x0A, 0xC5, 0x46, 0xB8, 0x28, 0x0C, 0xF5, 0xCD, 0xFB, 0xF5, 0x78, 0xCD, 0xF4, 0xF5, 0xF1, 0xCD, 0xE6, 0xF5, 0xC1, 0xCD, 0x9B, 0xF3, 0x18, 0xE8, 0xCD, 0xA4, 0xF6, 0xCD, 0xFB, 0xF5, 0x7D, 0xCD, 0xF0, 0xF1, 0xE5, 0x7E, 0xCD, 0xE6, 0xF5, 0xCD, 0x8F, 0xF3, 0x38, 0x2A, 0xCD, 0xFE, 0xF5, 0x7D, 0xE6, 0x0F, 0x20, 0xEF, 0xE1, 0x7D, 0xE6, 0x0F, 0xCD, 0xF5, 0xF1, 0x7E, 0xE6, 0x7F, 0x4F, 0xFE, 0x20, 0x38, 0x04, 0xFE, 0x7E, 0x38, 0x02, 0x0E, 0x2E, 0xCD, 0x09, 0xF0, 0xCD, 0x9C, 0xF3, 0x7D, 0xE6, 0x0F, 0x20, 0xE7, 0x18, 0xC5, 0x93, 0xCD, 0xF0, 0xF1, 0x18, 0xD8, 0xE6, 0x0F, 0x47, 0x87, 0x80, 0x47, 0x04, 0xCD, 0xFE, 0xF5, 0x10, 0xFB, 0xC9, 0xCD, 0xC0, 0xF3, 0x38, 0x37, 0x28, 0x10, 0xCD, 0xCC, 0xF0, 0xD1, 0x21, 0x34, 0x00, 0x39, 0x72, 0x2B, 0x73, 0x79, 0xFE, 0x0D, 0x28, 0x25, 0x06, 0x02, 0x21, 0x35, 0x00, 0x39, 0xC5, 0xE5, 0x06, 0x02, 0xCD, 0xD7, 0xF0, 0xD1, 0xE1, 0x7A, 0xB3, 0x28, 0x0A, 0x73, 0x23, 0x72, 0x23, 0x1A, 0x77, 0x23, 0x3E, 0xCF, 0x12, 0x79, 0xFE, 0x0D, 0xC1, 0x28, 0x02, 0x10, 0xE1, 0xCD, 0xA9, 0xF6, 0xE1, 0x21, 0x43, 0xF4, 0xE5, 0x21, 0xCF, 0xF3, 0x22, 0x09, 0x00, 0x21, 0x18, 0x00, 0x39, 0xD1, 0xE9, 0xCD, 0xD7, 0xF0, 0xC1, 0xED, 0x58, 0x18, 0x51, 0xCD, 0xD9, 0xF0, 0xD1, 0xC1, 0xED, 0x59, 0xC9, 0xCD, 0x86, 0xF3, 0x7E, 0x02, 0xCD, 0x9B, 0xF3, 0x18, 0xF9, 0xCD, 0xD7, 0xF0, 0xE1, 0x7E, 0xCD, 0xF4, 0xF5, 0xCD, 0xC0, 0xF3, 0xD8, 0x28, 0x0F, 0xFE, 0x0A, 0x28, 0x0D, 0xE5, 0xCD, 0xCC, 0xF0, 0xD1, 0xE1, 0x73, 0x79, 0xFE, 0x0D, 0xC8, 0x23, 0x23, 0x2B, 0x7D, 0xE6, 0x07, 0xCC, 0xFB, 0xF5, 0x18, 0xDC, 0xCD, 0xA4, 0xF6, 0x7E, 0xF5, 0x2F, 0x77, 0xAE, 0xC4, 0xA1, 0xF2, 0xF1, 0x77, 0xCD, 0x9C, 0xF3, 0x18, 0xF1, 0xD5, 0x5F, 0xCD, 0xFB, 0xF5, 0x06, 0x08, 0x7B, 0x07, 0x5F, 0x3E, 0x18, 0x17, 0x4F, 0xCD, 0x09, 0xF0, 0x10, 0xF4, 0xD1, 0xC9, 0x23, 0x23, 0x34, 0xC8, 0xF2, 0xC1, 0xF2, 0xF6, 0x80, 0x18, 0x02, 0xE6, 0x7F, 0x35, 0xBE, 0x20, 0xEF, 0xCD, 0xFE, 0xF5, 0xCD, 0x15, 0xF3, 0xCD, 0xF7, 0xF5, 0xCD, 0xC0, 0xF3, 0xD8, 0x28, 0x12, 0xE5, 0xCD, 0xCC, 0xF0, 0xE1, 0x7D, 0x13, 0x12, 0xE3, 0x7E, 0xE3, 0x07, 0x30, 0x03, 0x13, 0x7C, 0x12, 0xE1, 0x79, 0xFE, 0x0D, 0xC8, 0x21, 0x3D, 0xF3, 0xCD, 0xC0, 0xF3, 0x38, 0x0B, 0x28, 0xF9, 0xFE, 0x27, 0x20, 0xBE, 0x21, 0x55, 0xF3, 0x18, 0xF0, 0x7E, 0x4F, 0x3C, 0xC8, 0xFC, 0xA9, 0xF6, 0xCD, 0x09, 0xF0, 0xCD, 0xF7, 0xF5, 0xCD, 0x15, 0xF3, 0xCD, 0xFE, 0xF5, 0x23, 0x18, 0xEA, 0x23, 0x7E, 0xE6, 0x3F, 0xC6, 0x02, 0xEB, 0x6F, 0x26, 0x00, 0x39, 0xEB, 0x7E, 0x06, 0x01, 0x07, 0x30, 0x0E, 0x04, 0x07, 0x30, 0x0A, 0xE5, 0x1A, 0x67, 0x1B, 0x1A, 0x6F, 0x7E, 0xE1, 0x10, 0x01, 0x1A, 0xCD, 0xE6, 0xF5, 0x1B, 0x10, 0xF9, 0xC9, 0xC1, 0x15, 0x42, 0x13, 0x43, 0x12, 0x44, 0x11, 0x45, 0x10, 0x46, 0x14, 0x48, 0x31, 0x4C, 0x30, 0xCD, 0xF1, 0x50, 0xB4, 0x53, 0x97, 0x49, 0x03, 0xC1, 0x09, 0x42, 0x0B, 0x43, 0x0A, 0x44, 0x0D, 0x45, 0x0C, 0x46, 0x08, 0x48, 0x0F, 0x4C, 0x0E, 0xCD, 0xCF, 0x58, 0x87, 0x59, 0x85, 0x52, 0x02, 0xFF, 0xE6, 0x0F, 0xC6, 0x90, 0x27, 0xCE, 0x40, 0x27, 0x4F, 0xC9, 0xCD, 0xF7, 0xF5, 0xCD, 0x8F, 0xF6, 0xC5, 0x4F, 0xCD, 0x09, 0xF0, 0x79, 0xC1, 0xC9, 0x04, 0xCD, 0xD9, 0xF0, 0xC1, 0xD1, 0xC3, 0xAA, 0xF6, 0x23, 0x7C, 0xB5, 0x37, 0xC8, 0x7B, 0x95, 0x7A, 0x9C, 0xC9, 0xD1, 0xC9, 0x03, 0xCD, 0x8F, 0xF3, 0x38, 0xF8, 0xCD, 0x12, 0xF0, 0xB7, 0xC8, 0xCD, 0x8F, 0xF6, 0xFE, 0x13, 0x20, 0xEC, 0xC3, 0x8F, 0xF6, 0xD6, 0x30, 0xD8, 0xFE, 0x17, 0x3F, 0xD8, 0xFE, 0x0A, 0x3F, 0xD0, 0xD6, 0x07, 0xFE, 0x0A, 0xC9, 0xCD, 0x7B, 0xF3, 0xFE, 0x20, 0xC8, 0xFE, 0x2C, 0xC8, 0xFE, 0x0D, 0x37, 0xC8, 0x3F, 0xC9, 0xE5, 0xD5, 0xC5, 0xF5, 0xCD, 0x6F, 0xF0, 0xEB, 0x21, 0x0A, 0x00, 0x39, 0x06, 0x04, 0xEB, 0x2B, 0x72, 0x2B, 0x73, 0xD1, 0x10, 0xF9, 0xC1, 0x0B, 0xF9, 0x21, 0x25, 0x00, 0x39, 0xD5, 0x16, 0x02, 0x7E, 0x91, 0x23, 0x7E, 0x98, 0x28, 0x06, 0x23, 0x23, 0x15, 0x20, 0xF4, 0x03, 0x21, 0x20, 0x00, 0xD1, 0x39, 0x73, 0x23, 0x72, 0xC5, 0x0E, 0x2A, 0xCD, 0x09, 0xF0, 0xD1, 0x3E, 0xF4, 0xBA, 0x28, 0x09, 0x23, 0x23, 0x73, 0x23, 0x72, 0xEB, 0xCD, 0xE1, 0xF5, 0x21, 0x25, 0x00, 0x39, 0x01, 0x00, 0x02, 0x5E, 0x71, 0x23, 0x56, 0x71, 0x23, 0x7B, 0xB2, 0x28, 0x02, 0x7E, 0x12, 0x23, 0x10, 0xF1, 0x08, 0xD9, 0xE5, 0xD5, 0xC5, 0xF5, 0xDD, 0xE5, 0xFD, 0xE5, 0xED, 0x57, 0x47, 0xED, 0x5F, 0x4F, 0xC5, 0xC3, 0x13, 0xF1, 0xE5, 0xCF, 0xC1, 0x79, 0xED, 0x4F, 0x78, 0xED, 0x47, 0xDD, 0xE1, 0xFD, 0xE1, 0xF1, 0xC1, 0xD1, 0xE1, 0x08, 0xD9, 0xD1, 0xC1, 0xF1, 0xE1, 0xF9, 0x00, 0x21, 0x00, 0x00, 0xC3, 0x00, 0x00, 0xAF, 0x32, 0x03, 0x00, 0x21, 0x6C, 0xF4, 0xC3, 0xB5, 0xF6, 0x49, 0x2F, 0x4F, 0x20, 0x45, 0x52, 0xD2, 0x44, 0x53, 0x4B, 0x20, 0x45, 0x52, 0x52, 0x3A, 0x20, 0x55, 0xAD, 0x20, 0x54, 0xAD, 0x20, 0x53, 0xAD, 0x20, 0x43, 0xAD, 0x20, 0x45, 0xAD, 0x0D, 0x8A, 0x3F, 0x3F, 0x3F, 0xBF, 0x4D, 0x4F, 0x53, 0x53, 0x20, 0x56, 0x45, 0x52, 0x53, 0x20, 0x32, 0x2E, 0x32, 0x0D, 0x8A, 0x3E, 0x0F, 0xD3, 0x24, 0x11, 0x40, 0x00, 0x62, 0x6A, 0xDB, 0x26, 0xA3, 0x28, 0xFB, 0xDB, 0x26, 0x23, 0xA3, 0xA3, 0xC2, 0xAD, 0xF4, 0xE5, 0x29, 0x5C, 0x19, 0x19, 0xE5, 0x29, 0x29, 0xDB, 0x20, 0x2B, 0x7D, 0xB4, 0xC2, 0xBD, 0xF4, 0xE1, 0x3E, 0x83, 0xD3, 0x23, 0x7C, 0xD3, 0x21, 0x7D, 0xD3, 0x20, 0x3E, 0x03, 0xD3, 0x23, 0xAF, 0xD3, 0x21, 0xD3, 0x25, 0xCD, 0xCE, 0xF6, 0xE6, 0x7F, 0xFE, 0x0D, 0xE1, 0xC8, 0x5D, 0x54, 0xCD, 0xEE, 0xF4, 0xCD, 0xEE, 0xF4, 0x19, 0xE5, 0x18, 0xD8, 0xB7, 0x7C, 0x1F, 0x67, 0x7D, 0x1F, 0x6F, 0xC9, 0x3E, 0xAF, 0x32, 0x4B, 0x00, 0x21, 0x80, 0x00, 0x22, 0x49, 0x00, 0xCD, 0xA4, 0xF6, 0xD5, 0x3A, 0x4B, 0x00, 0xB7, 0x20, 0x08, 0x22, 0x4C, 0x00, 0xCD, 0xEB, 0xF6, 0x18, 0x03, 0xCD, 0xE7, 0xF6, 0xD1, 0x20, 0x67, 0x3A, 0x44, 0x00, 0x47, 0xDB, 0x31, 0xB7, 0x20, 0x0B, 0x06, 0x1A, 0x3A, 0x4A, 0x00, 0xE6, 0x10, 0x20, 0x02, 0x06, 0x12, 0xE5, 0x21, 0x42, 0x00, 0x7E, 0xB8, 0x38, 0x1B, 0x3A, 0x45, 0x00, 0xB7, 0x28, 0x0B, 0x3A, 0x43, 0x00, 0xFE, 0xD0, 0x20, 0x04, 0x3E, 0x90, 0x18, 0x05, 0x3E, 0xD0, 0x2B, 0x34, 0x23, 0x32, 0x43, 0x00, 0x36, 0x00, 0x34, 0xE1, 0x2B, 0xCD, 0x9C, 0xF3, 0xD5, 0x18, 0xAC, 0xDB, 0x34, 0xE6, 0x40, 0xC0, 0x21, 0x00, 0x00, 0x22, 0x40, 0x00, 0x21, 0x01, 0xD0, 0x22, 0x42, 0x00, 0x21, 0x80, 0x00, 0x22, 0x49, 0x00, 0xCD, 0xE7, 0xF6, 0x20, 0x0B, 0x3E, 0x02, 0x32, 0x42, 0x00, 0xCD, 0xE7, 0xF6, 0xCA, 0x80, 0x00, 0x21, 0x73, 0xF4, 0xCD, 0x95, 0xF6, 0x3A, 0x40, 0x00, 0xCD, 0xA1, 0xF5, 0x3A, 0x41, 0x00, 0xCD, 0xA1, 0xF5, 0x3A, 0x42, 0x00, 0xCD, 0xA1, 0xF5, 0x3A, 0x48, 0x00, 0xCD, 0xA1, 0xF5, 0x3A, 0x47, 0x00, 0xCD, 0xE6, 0xF5, 0xC3, 0x98, 0xF6, 0xCD, 0x86, 0xF3, 0x7D, 0xB7, 0xFA, 0x09, 0xF1, 0xFE, 0x04, 0xD2, 0x09, 0xF1, 0x32, 0x40, 0x00, 0x6B, 0x61, 0x22, 0x44, 0x00, 0xC9, 0xCD, 0x86, 0xF3, 0x61, 0x22, 0x41, 0x00, 0x7B, 0xB7, 0x3E, 0xD0, 0x28, 0x02, 0x3E, 0x90, 0x32, 0x43, 0x00, 0xC9, 0xCD, 0xA4, 0xF6, 0xE5, 0x19, 0xCD, 0xFB, 0xF5, 0xE1, 0xB7, 0xED, 0x52, 0x18, 0x03, 0xCD, 0xA9, 0xF6, 0x7C, 0xCD, 0xE6, 0xF5, 0x7D, 0xF5, 0x0F, 0x0F, 0x0F, 0x0F, 0xCD, 0xEF, 0xF5, 0xF1, 0xCD, 0x6E, 0xF3, 0x18, 0x0C, 0xCD, 0xE6, 0xF5, 0x0E, 0x2D, 0x18, 0x05, 0xCD, 0xDE, 0xF5, 0x0E, 0x20, 0x3A, 0x03, 0x00, 0xE6, 0x03, 0xCA, 0xDE, 0xF6, 0xFE, 0x02, 0xFA, 0x62, 0xF4, 0xC2, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0xC0, 0xCA, 0xDE, 0xF6, 0xFE, 0x80, 0xFA, 0x62, 0xF4, 0xCA, 0x62, 0xF4, 0xC3, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0x03, 0xCA, 0xC6, 0xF6, 0xFE, 0x02, 0xFA, 0x62, 0xF4, 0xC2, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0x0C, 0xCA, 0xC6, 0xF6, 0xFE, 0x08, 0xFA, 0x62, 0xF4, 0xCA, 0x62, 0xF4, 0xC3, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0x03, 0xCA, 0xCE, 0xF6, 0xFE, 0x02, 0xFA, 0x62, 0xF4, 0xC2, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0x0C, 0xCA, 0xCE, 0xF6, 0xFE, 0x08, 0xFA, 0x62, 0xF4, 0xCA, 0x62, 0xF4, 0xC3, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0xC0, 0xCA, 0xD6, 0xF6, 0xFE, 0x80, 0xFA, 0x62, 0xF4, 0xCA, 0x62, 0xF4, 0xC3, 0x62, 0xF4, 0x3A, 0x03, 0x00, 0xE6, 0x30, 0xCA, 0xDE, 0xF6, 0xFE, 0x20, 0xFA, 0x62, 0xF4, 0xCA, 0x62, 0xF4, 0xC3, 0x62, 0xF4, 0xCD, 0x46, 0xF6, 0xE6, 0x7F, 0xC9, 0xCD, 0xA9, 0xF6, 0xC5, 0x4E, 0xCD, 0x00, 0xF6, 0x23, 0x79, 0x07, 0x30, 0xF7, 0xC1, 0xC9, 0xCD, 0xD9, 0xF0, 0xD1, 0xE1, 0xE5, 0x21, 0xC2, 0xF6, 0xCD, 0x98, 0xF6, 0xE1, 0xC9, 0x21, 0xBB, 0xF6, 0xCD, 0x95, 0xF6, 0xC3, 0x00, 0x00, 0x52, 0x53, 0x54, 0x20, 0x45, 0x52, 0xD2, 0x0D, 0x0A, 0x00, 0x80, 0xDB, 0x25, 0xE6, 0x01, 0xC8, 0xC6, 0xFE, 0xC9, 0xDB, 0x25, 0x1F, 0x30, 0xFB, 0xDB, 0x20, 0xC9, 0xDB, 0x25, 0xE6, 0x20, 0xC8, 0xC6, 0xBF, 0xC9, 0xCD, 0xD6, 0xF6, 0x28, 0xFB, 0x79, 0xD3, 0x20, 0xC9, 0x22, 0x4C, 0x00, 0x3E, 0xAF, 0x32, 0x4B, 0x00, 0x06, 0x0A, 0xC5, 0xCD, 0x3B, 0xF7, 0xCC, 0xFD, 0xF6, 0xC1, 0xC8, 0x10, 0xF5, 0xC9, 0x5F, 0x3A, 0x4B, 0x00, 0xB7, 0x7B, 0x28, 0x10, 0x32, 0x48, 0x00, 0xD3, 0x30, 0xED, 0xB2, 0x15, 0x20, 0xFB, 0xCD, 0x2E, 0xF7, 0xE6, 0x9C, 0xC9, 0xF6, 0x20, 0x32, 0x48, 0x00, 0xD3, 0x30, 0xED, 0xB3, 0x15, 0x20, 0xFB, 0x18, 0x0B, 0x06, 0x08, 0x3A, 0x46, 0x00, 0xB0, 0x32, 0x48, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xDB, 0x30, 0x32, 0x47, 0x00, 0xE6, 0xFC, 0xC9, 0xCD, 0x8E, 0xF7, 0xC4, 0x23, 0xF7, 0xF8, 0x3A, 0x42, 0x00, 0xD3, 0x32, 0xDB, 0x31, 0x4F, 0x3A, 0x41, 0x00, 0xB9, 0x28, 0x0C, 0xD3, 0x33, 0x06, 0x1C, 0xCD, 0x25, 0xF7, 0xE6, 0x98, 0xC0, 0xDB, 0x31, 0xB7, 0x21, 0x40, 0x00, 0x28, 0x03, 0x3A, 0x51, 0x00, 0x29, 0x3D, 0xF2, 0x65, 0xF7, 0xE5, 0x0E, 0x80, 0xCD, 0xC3, 0xF7, 0xDB, 0x34, 0xE6, 0x20, 0x3E, 0x04, 0x28, 0x01, 0xAF, 0xC6, 0x88, 0x2A, 0x4C, 0x00, 0xD1, 0x43, 0x15, 0x14, 0x20, 0x01, 0x14, 0x0E, 0x33, 0xBF, 0xC9, 0x06, 0x58, 0xCD, 0x25, 0xF7, 0x2A, 0x49, 0x00, 0x7C, 0xBD, 0xC8, 0x0E, 0x80, 0xCD, 0xC3, 0xF7, 0xCD, 0x33, 0xF7, 0xF8, 0xE5, 0x21, 0x4E, 0x00, 0x01, 0x33, 0x06, 0x16, 0x01, 0x3E, 0xC4, 0xCD, 0x05, 0xF7, 0xE1, 0x28, 0x08, 0x3E, 0x40, 0xBE, 0xD8, 0xB6, 0x77, 0x18, 0xD8, 0xDB, 0x32, 0xD3, 0x31, 0xB7, 0x28, 0xCC, 0x7E, 0x32, 0x49, 0x00, 0xAF, 0xC9, 0x21, 0x4A, 0x00, 0x7E, 0xB7, 0x20, 0x25, 0x3A, 0x40, 0x00, 0x47, 0x04, 0xAF, 0x37, 0x17, 0x10, 0xFD, 0xF6, 0x20, 0x77, 0xD3, 0x34, 0x11, 0x46, 0x00, 0x3E, 0x03, 0x12, 0xCD, 0x23, 0xF7, 0xF8, 0xDB, 0x04, 0x1F, 0x30, 0x07, 0x3E, 0x10, 0xB6, 0x77, 0x3E, 0x02, 0x12, 0xDB, 0x31, 0xB7, 0x7E, 0x20, 0x02, 0xE6, 0xBF, 0xB1, 0xD3, 0x34, 0x3A, 0x43, 0x00, 0xD3, 0x04, 0xC9 }; /* returns TRUE iff there exists a disk with 'property' */ static int32 cromfdc_hasProperty(uint32 property) { int32 i; for (i = 0; i < CROMFDC_MAX_DRIVES; i++) if (cromfdc_dev.units[i].flags & property) return TRUE; return FALSE; } static uint8 motor_timeout = 0; /* Unit service routine */ static t_stat cromfdc_svc (UNIT *uptr) { if(cromfdc_info->motor_on == 1) { motor_timeout ++; if(motor_timeout == MOTOR_TO_LIMIT) { cromfdc_info->motor_on = 0; TRACE_PRINT(DRIVE_MSG, ("CROMFDC: Motor OFF" NLP)) } } cromfdc_info->rtc ++; TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: " ADDRESS_FORMAT " Timer IRQ" NLP, PCX)); cromfdc_info->ipend |= CROMFDC_IRQ_TIMER3; /* sim_activate (cromfdc_unit, cromfdc_unit->wait); */ /* requeue! */ return SCPE_OK; } /* Reset routine */ static t_stat cromfdc_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ if (cromfdc_hasProperty(UNIT_CROMFDC_ROM)) { sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &cromfdcrom, TRUE); } /* Unmap I/O Ports (0x3-4,0x5-9,0x34,0x40 */ sim_map_resource(0x03, 2, RESOURCE_TYPE_IO, &cromfdc_ext, TRUE); sim_map_resource(0x05, 5, RESOURCE_TYPE_IO, &cromfdc_timer, TRUE); sim_map_resource(0x34, 1, RESOURCE_TYPE_IO, &cromfdc_control, TRUE); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &cromfdc_banksel, TRUE); if(crofdc_type == 50) { /* CCS2422 */ sim_map_resource(0x26, 1, RESOURCE_TYPE_IO, &ccs2810_uart_status, TRUE); } } else { /* Connect CROMFDC ROM at base address */ if (cromfdc_hasProperty(UNIT_CROMFDC_ROM)) { TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: ROM Enabled." NLP)); if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &cromfdcrom, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } } else { TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: ROM Disabled." NLP)) } /* Connect CROMFDC Interrupt, and Aux Disk Registers */ if(sim_map_resource(0x03, 0x02, RESOURCE_TYPE_IO, &cromfdc_ext, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect CROMFDC Timer Registers */ if(sim_map_resource(0x05, 0x05, RESOURCE_TYPE_IO, &cromfdc_timer, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect CROMFDC Disk Flags and Control Register */ if(sim_map_resource(0x34, 0x01, RESOURCE_TYPE_IO, &cromfdc_control, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect CROMFDC Bank Select Register */ if(sim_map_resource(0x40, 0x1, RESOURCE_TYPE_IO, &cromfdc_banksel, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect CCS 2810 UART Status Register (needed by MOSS 2.2 Monitor */ if(sim_map_resource(0x26, 0x01, RESOURCE_TYPE_IO, &ccs2810_uart_status, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x" NLP, __FUNCTION__, pnp->io_base); return SCPE_ARG; } else { TRACE_PRINT(VERBOSE_MSG, ("Mapped CCS2810 UART Status at 0x26" NLP)); } } cromfdc_info->rom_disabled = FALSE; /* sim_activate (cromfdc_unit, cromfdc_unit->wait); */ /* requeue! */ return SCPE_OK; } static t_stat cromfdc_boot(int32 unitno, DEVICE *dptr) { if((crofdc_type != 4) && (crofdc_type != 16) && (crofdc_type != 64) && (crofdc_type != 50)) { printf("Invalid fdc_type: %d, must be 4, 16, or 64 (or 50 for CCS2422.)" NLP, crofdc_type); return SCPE_ARG; } bootstrap &= 0x01; DBG_PRINT(("Booting %dFDC Controller, bootstrap=%d" NLP, crofdc_type, bootstrap)); /* Re-enable the ROM in case it was disabled */ cromfdc_info->rom_disabled = FALSE; /* Set the PC to C000, and go. */ *((int32 *) sim_PC->loc) = 0xC000; return SCPE_OK; } static int32 cromfdcrom(const int32 Addr, const int32 write, const int32 data) { /* DBG_PRINT(("CROMFDC: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ if(write) { cromfdcram[Addr & CROMFDC_ADDR_MASK] = data; return 0; } else { if(cromfdc_info->rom_disabled == FALSE) { return(cromfdc_rom[bootstrap & 0x01][Addr & CROMFDC_ADDR_MASK]); } else { return(cromfdcram[Addr & CROMFDC_ADDR_MASK]); } } } /* Disk Control/Flags Register, 0x34 / CCS2422 Control1/Status1 */ static int32 cromfdc_control(const int32 port, const int32 io, const int32 data) { int32 result = 0; if(io) { /* I/O Write */ switch(data & 0x0F) { case 0: wd179x_info->sel_drive = 0xFF; break; case CROMFDC_CTRL_DS1: wd179x_info->sel_drive = 0; break; case CROMFDC_CTRL_DS2: wd179x_info->sel_drive = 1; break; case CROMFDC_CTRL_DS3: wd179x_info->sel_drive = 2; break; case CROMFDC_CTRL_DS4: wd179x_info->sel_drive = 3; break; default: TRACE_PRINT(STATUS_MSG, ("CROMFDC: " ADDRESS_FORMAT " WR CTRL = 0x%02x: Invalid drive selected." NLP, PCX, data & 0xFF)); break; } if(data & CROMFDC_CTRL_MAXI) { wd179x_info->drivetype = 8; } else { wd179x_info->drivetype = 5; } if(data & CROMFDC_CTRL_MTRON) { cromfdc_info->motor_on = 1; motor_timeout = 0; } if(data & CROMFDC_CTRL_DDENS) { if(crofdc_type == 4) { /* 4FDC */ TRACE_PRINT(DRIVE_MSG, ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: Cannot set double density on 4FDC" NLP, PCX)); } else { wd179x_info->ddens = 1; } } else { wd179x_info->ddens = 0; } if(data & CROMFDC_CTRL_AUTOWAIT) { cromfdc_info->autowait = 1; } else { cromfdc_info->autowait = 0; } TRACE_PRINT(DRIVE_MSG, ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: sel_drive=%d, drivetype=%d, motor=%d, dens=%d, aw=%d" NLP, PCX, wd179x_info->sel_drive, wd179x_info->drivetype, cromfdc_info->motor_on, wd179x_info->ddens, cromfdc_info->autowait)); } else { /* I/O Read */ result = (crofdc_boot) ? 0 : CROMFDC_FLAG_BOOT; result |= (wd179x_info->intrq) ? CROMFDC_FLAG_EOJ : 0; result |= (wd179x_info->drq) ? CROMFDC_FLAG_DRQ : 0; if(crofdc_type != 50) { /* Cromemco Controller */ result |= (motor_timeout < MOTOR_TO_LIMIT) ? CROMFDC_FLAG_SEL_REQ : 0; if(crofdc_type > 4) { /* 16, 64FDC */ result |= (cromfdc_info->motor_on) ? CROMFDC_FLAG_MTRON : 0; result |= (motor_timeout == MOTOR_TO_LIMIT) ? CROMFDC_FLAG_MTO : 0; result |= (crofdc_inh_init) ? 0 : CROMFDC_FLAG_INH_INIT; } else { result |= 0x1E; /* Make unused bits '1' on 4FDC */ } } else { /* CCS 2422 Controller */ switch(wd179x_info->sel_drive) { case 1: result |= 0x02; break; case 2: result |= 0x04; break; case 3: result |= 0x08; break; case 4: result |= 0x10; break; } /* printf("CCS2422FDC: " ADDRESS_FORMAT " Read STATUS1=0x%02x" NLP, PCX, result); */ } TRACE_PRINT(STATUS_MSG, ("CROMFDC: " ADDRESS_FORMAT " Read DISK FLAGS, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) } return result; } /* * 64FDC Interrupt and Auxiliary Disk Status 0x03-04 * For CCS2422, this is the Control2/Status2 Register (0x04 only) */ static int32 cromfdc_ext(const int32 port, const int32 io, const int32 data) { int32 result; if(io) { /* I/O Write */ if(port == 0x4) { if(crofdc_type != 50) { /* Cromemco Controller */ if((data & CROMFDC_AUX_CMD_SIDE) == 0) { if(crofdc_type == 4) { /* 4FDC */ TRACE_PRINT(DRIVE_MSG, ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: Cannot set side 1 on 4FDC" NLP, PCX)); } else { wd179x_info->fdc_head = 1; } } else { wd179x_info->fdc_head = 0; } #if 0 /* hharte - nothing implemented for these */ if((data & CROMFDC_AUX_EJECT) == 0) { printf("CROMFDC: Eject" NLP); } if((data & CROMFDC_AUX_SEL_OVERRIDE) == 0) { printf("CROMFDC: Sel Override" NLP); } if((data & CROMFDC_AUX_CTRL_OUT) == 0) { printf("CROMFDC: Ctrl Out" NLP); } #endif /* 0 */ if(crofdc_type < 64) { if((data & CROMFDC_AUX_RESTORE) == 0) { wd179x_external_restore(); } } } else { /* CCS 2422 Controller */ if((data & CCSFDC_CMD_SIDE) == 0) { wd179x_info->fdc_head = 1; } else { wd179x_info->fdc_head = 0; } } } else if (port == 0x3) { /* Interrupt Address */ TRACE_PRINT(IRQ_MSG, ("CROMFDC: " ADDRESS_FORMAT " IRQ Mask=0x%02x" NLP, PCX, data)); cromfdc_info->imask = data; } else { } TRACE_PRINT(DRIVE_MSG, ("CROMFDC: " ADDRESS_FORMAT " AUX OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) result = 0; } else { /* I/O Read */ if(port == 0x4) { result = dipswitch & 0x1F; result |= 0x00; /* Bit 6 is Seek in Progress for Persci drives. */ result |= (cromfdc_info->rtc & 1) ? 0x80 : 0; if(crofdc_type == 50) { TRACE_PRINT(STATUS_MSG, ("CCS2422FDC: " ADDRESS_FORMAT " Read STATUS2=0x%02x" NLP, PCX, result)); } } else if (port == 0x3) { /* Interrupt Address */ result = ipend_to_rst_opcode(cromfdc_info->ipend); if(result != 0) { TRACE_PRINT(IRQ_MSG, ("CROMFDC: " ADDRESS_FORMAT " RST Opcode=%x, Vector=%04x" NLP, PCX, result, RST_OPCODE_TO_VECTOR(result))); } } else { result = 0xFF; } TRACE_PRINT(STATUS_MSG, ("CROMFDC: " ADDRESS_FORMAT " AUX IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) } return result; } /* 64FDC Timer registers */ static int32 cromfdc_timer(const int32 port, const int32 io, const int32 data) { static int32 result = 0; if(io) { TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: " ADDRESS_FORMAT " TIMER%d OUT, Port 0x%02x Data 0x%02x" NLP, PCX, (port-4), port, data)) result = 0; sim_activate(cromfdc_unit, (CROMFDC_SIM_64US * data)); } else { result++; TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: " ADDRESS_FORMAT " TIMER%d IN, Port 0x%02x Result 0x%02x" NLP, PCX, (port-4), port, result)) } return result; } /* 64FDC Bank Select (Write Disables boot ROM) */ static int32 cromfdc_banksel(const int32 port, const int32 io, const int32 data) { int32 result; if(io) { TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: " ADDRESS_FORMAT " BANKSEL OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) /* Unmap Boot ROM */ cromfdc_info->rom_disabled = TRUE; result = 0; } else { result = 0xFF; TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: " ADDRESS_FORMAT " BANKSEL IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) } return result; } static uint8 ccs2810_uart_status_reg = 0x00; /* CCS 2810 UART Status Register, needed by MOSS 2.2 Monitor */ static int32 ccs2810_uart_status(const int32 port, const int32 io, const int32 data) { if(io) { /* I/O Write */ return (0x00); } else { /* I/O Read */ ccs2810_uart_status_reg = ~ccs2810_uart_status_reg; return (ccs2810_uart_status_reg); } } simh-3.8.1/AltairZ80/flashwriter2.c0000644000175000017500000002625511111141270015136 0ustar vlmvlm/************************************************************************* * * * $Id: flashwriter2.c 1941 2008-06-13 05:31:03Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http:/*www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Vector Graphic, Inc. FlashWriter II module for SIMH * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG*/ #include "altairz80_defs.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif extern int32 sio0s(const int32 port, const int32 io, const int32 data); extern int32 sio0d(const int32 port, const int32 io, const int32 data); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); static char ansibuf[10]; #define FW2_MAX_BOARDS 4 #define UNIT_V_FW2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_FW2_VERBOSE (1 << UNIT_V_FW2_VERBOSE) #define FW2_CAPACITY (2048) /* FlashWriter II Memory Size */ typedef struct { UNIT *uptr; /* UNIT pointer */ uint8 cur_FL_Row; /* Current Flashwriter Row */ uint8 cur_FL_Col; /* Current Flashwriter Column */ uint8 FL_Row; uint8 FL_Col; uint8 reversevideo; /* Flag set if reverse video is currently on */ uint8 M[FW2_CAPACITY]; /* FlashWriter 2K Video Memory */ } FW2_INFO; static FW2_INFO *fw2_info[FW2_MAX_BOARDS]; static uint8 port_map[FW2_MAX_BOARDS] = { 0x11, 0x15, 0x17, 0x19 }; static int32 fw2dev(const int32 Addr, const int32 rw, const int32 data); static t_stat fw2_attach(UNIT *uptr, char *cptr); static t_stat fw2_detach(UNIT *uptr); static uint8 FW2_Read(const uint32 Addr); static uint8 FW2_Write(const uint32 Addr, uint8 cData); static t_stat get_base_address(char *cptr, uint32 *baseaddr); static UNIT fw2_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) } }; static MTAB fw2_mod[] = { /* quiet, no warning messages */ { UNIT_FW2_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_FW2_VERBOSE, UNIT_FW2_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; DEVICE fw2_dev = { "FWII", fw2_unit, NULL, fw2_mod, FW2_MAX_BOARDS, 10, 31, 1, FW2_MAX_BOARDS, FW2_MAX_BOARDS, NULL, NULL, NULL, NULL, &fw2_attach, &fw2_detach, NULL, (DEV_DISABLE | DEV_DIS), 0, NULL, NULL, "Vector Graphic Flashwriter 2 FWII" }; /* Attach routine */ static t_stat fw2_attach(UNIT *uptr, char *cptr) { t_stat r; unsigned int i = 0; uint32 baseaddr; char *tptr; r = get_base_address(cptr, &baseaddr); if(r != SCPE_OK) /* error? */ return r; DBG_PRINT(("%s\n", __FUNCTION__)); for(i = 0; i < FW2_MAX_BOARDS; i++) { if(&fw2_dev.units[i] == uptr) { if(uptr->flags & UNIT_FW2_VERBOSE) { printf("Attaching unit %d at %04x\n", i, baseaddr); } break; } } fw2_info[i] = calloc(1, sizeof(FW2_INFO)); fw2_info[i]->uptr = uptr; fw2_info[i]->uptr->u3 = baseaddr; if(sim_map_resource(baseaddr, FW2_CAPACITY, RESOURCE_TYPE_MEMORY, &fw2dev, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, baseaddr); return SCPE_ARG; } if(sim_map_resource(0x00, 1, RESOURCE_TYPE_IO, &sio0s, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, 0x00); return SCPE_ARG; } if(sim_map_resource(0x01, 1, RESOURCE_TYPE_IO, &sio0d, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, 0x01); return SCPE_ARG; } tptr = (char *) malloc (strlen (cptr) + 3); /* get string buf */ if (tptr == NULL) return SCPE_MEM; /* no more mem? */ sprintf(tptr, "0x%04x", baseaddr); /* copy base address */ uptr->filename = tptr; /* save */ uptr->flags = uptr->flags | UNIT_ATT; return SCPE_OK; } /* Detach routine */ static t_stat fw2_detach(UNIT *uptr) { uint8 i; DBG_PRINT(("%s\n", __FUNCTION__)); for(i = 0; i < FW2_MAX_BOARDS; i++) { if(&fw2_dev.units[i] == uptr) { break; } } if (i >= FW2_MAX_BOARDS) return SCPE_ARG; /* Disconnect FlashWriter2: unmap memory and I/O resources */ sim_map_resource(fw2_info[i]->uptr->u3, FW2_CAPACITY, RESOURCE_TYPE_MEMORY, &fw2dev, TRUE); sim_map_resource(0x00, 1, RESOURCE_TYPE_IO, &sio0s, TRUE); sim_map_resource(0x01, 1, RESOURCE_TYPE_IO, &sio0d, TRUE); if(fw2_info[i]) { free(fw2_info[i]); } free (uptr->filename); /* free base address string */ uptr->filename = NULL; uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ return SCPE_OK; } static t_stat get_base_address(char *cptr, uint32 *baseaddr) { uint32 b; sscanf(cptr, "%x", &b); if(b & (FW2_CAPACITY-1)) { printf("FWII must be on a %d-byte boundary.\n", FW2_CAPACITY); return SCPE_ARG; } *baseaddr = b & ~(FW2_CAPACITY-1); return SCPE_OK; } extern int32 getBankSelect(void); /* This is the main entry point into the Flashwriter2 emulation. */ static int32 fw2dev(const int32 Addr, const int32 rw, const int32 data) { int32 bank = getBankSelect(); if(bank == 0) { if(rw == 0) { /* Read */ return(FW2_Read(Addr)); } else { /* Write */ return(FW2_Write(Addr, data)); } } else return 0xff; } static uint8 FW2_Write(const uint32 Addr, uint8 Value) { FW2_INFO *fw2 = NULL; uint8 FL_Row; uint8 FL_Col; uint32 baseaddr = 0; uint8 i; uint8 outchar; uint8 port; for(i = 0; i < FW2_MAX_BOARDS; i++) { if(fw2_info[i] != NULL) { baseaddr = fw2_info[i]->uptr->u3; if((Addr >= baseaddr) && (Addr < (baseaddr + FW2_CAPACITY))) { break; } } } if(i == FW2_MAX_BOARDS) { return 0; } fw2 = fw2_info[i]; port = port_map[i]; fw2->M[Addr - baseaddr] = Value; /* Only print if it is in the visible part of the Flashwriter memory */ if((Addr >= baseaddr) && (Addr < (baseaddr + (80 * 24)))) { FL_Col = ((Addr-baseaddr) % 80) + 1; FL_Row = ((Addr-baseaddr) / 80) + 1; if(Value & 0x80) { /* reverse video */ if(fw2->reversevideo == 0) { fw2->reversevideo = 1; sprintf(ansibuf, "\x1b[07m"); for(i=0;ireversevideo == 1) { fw2->reversevideo = 0; sprintf(ansibuf, "\x1b[00m"); for(i=0;icur_FL_Row == FL_Row) && (FL_Col == fw2->cur_FL_Col + 1)) { sio0d(port, 1, outchar); } else { /* ESC[#;#H */ sprintf(ansibuf, "\x1b[%d;%dH%c", FL_Row, FL_Col, outchar); for(i=0;icur_FL_Col = FL_Col; fw2->cur_FL_Row = FL_Row; } return(1); } static uint8 FW2_Read(const uint32 Addr) { uint32 baseaddr = 0; uint8 i; for(i = 0; i < FW2_MAX_BOARDS; i++) { if(fw2_info[i] != NULL) { baseaddr = fw2_info[i]->uptr->u3; if((Addr >= baseaddr) && (Addr < (baseaddr + FW2_CAPACITY))) { break; } } } if(i == FW2_MAX_BOARDS) { return 0xFF; } return(fw2_info[i]->M[Addr - baseaddr]); } simh-3.8.1/AltairZ80/disasm.c0000644000175000017500000010424411043102700013774 0ustar vlmvlm/* disasm.c where all the _work_ gets done in the Netwide Disassembler * * The Netwide Assembler is copyright (C) 1996 Simon Tatham and * Julian Hall. All rights reserved. The software is * redistributable under the licence given in the file "Licence" * distributed in the NASM archive. * * initial version 27/iii/95 by Simon Tatham */ #include #include #include "nasm.h" #include "insns.h" /* names.c included source file defining instruction and register * names for the Netwide [Dis]Assembler * * The Netwide Assembler is copyright (C) 1996 Simon Tatham and * Julian Hall. All rights reserved. The software is * redistributable under the licence given in the file "Licence" * distributed in the NASM archive. */ static const char *conditions[] = { /* condition code names */ "a", "ae", "b", "be", "c", "e", "g", "ge", "l", "le", "na", "nae", "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no", "np", "ns", "nz", "o", "p", "pe", "po", "s", "z" }; /* Register names automatically generated from regs.dat */ /* automatically generated from ./regs.dat - do not edit */ static const char *reg_names[] = { "ah", "al", "ax", "bh", "bl", "bp", "bx", "ch", "cl", "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", "cs", "cx", "dh", "di", "dl", "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", "ds", "dx", "eax", "ebp", "ebx", "ecx", "edi", "edx", "es", "esi", "esp", "fs", "gs", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "segr6", "segr7", "si", "sp", "ss", "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" }; /* Instruction names automatically generated from insns.dat */ /* This file is auto-generated from insns.dat by insns.pl - don't edit it */ /* This file in included by names.c */ static const char *insn_names[] = { "aaa", "aad", "aam", "aas", "adc", "add", "addpd", "addps", "addsd", "addss", "addsubpd", "addsubps", "and", "andnpd", "andnps", "andpd", "andps", "arpl", "bound", "bsf", "bsr", "bswap", "bt", "btc", "btr", "bts", "call", "cbw", "cdq", "clc", "cld", "clflush", "cli", "clts", "cmc", "cmp", "cmpeqpd", "cmpeqps", "cmpeqsd", "cmpeqss", "cmplepd", "cmpleps", "cmplesd", "cmpless", "cmpltpd", "cmpltps", "cmpltsd", "cmpltss", "cmpneqpd", "cmpneqps", "cmpneqsd", "cmpneqss", "cmpnlepd", "cmpnleps", "cmpnlesd", "cmpnless", "cmpnltpd", "cmpnltps", "cmpnltsd", "cmpnltss", "cmpordpd", "cmpordps", "cmpordsd", "cmpordss", "cmppd", "cmpps", "cmpsb", "cmpsd", "cmpss", "cmpsw", "cmpunordpd", "cmpunordps", "cmpunordsd", "cmpunordss", "cmpxchg", "cmpxchg486", "cmpxchg8b", "comisd", "comiss", "cpuid", "cvtdq2pd", "cvtdq2ps", "cvtpd2dq", "cvtpd2pi", "cvtpd2ps", "cvtpi2pd", "cvtpi2ps", "cvtps2dq", "cvtps2pd", "cvtps2pi", "cvtsd2si", "cvtsd2ss", "cvtsi2sd", "cvtsi2ss", "cvtss2sd", "cvtss2si", "cvttpd2dq", "cvttpd2pi", "cvttps2dq", "cvttps2pi", "cvttsd2si", "cvttss2si", "cwd", "cwde", "daa", "das", "db", "dd", "dec", "div", "divpd", "divps", "divsd", "divss", "dq", "dt", "dw", "emms", "enter", "equ", "f2xm1", "fabs", "fadd", "faddp", "fbld", "fbstp", "fchs", "fclex", "fcmovb", "fcmovbe", "fcmove", "fcmovnb", "fcmovnbe", "fcmovne", "fcmovnu", "fcmovu", "fcom", "fcomi", "fcomip", "fcomp", "fcompp", "fcos", "fdecstp", "fdisi", "fdiv", "fdivp", "fdivr", "fdivrp", "femms", "feni", "ffree", "ffreep", "fiadd", "ficom", "ficomp", "fidiv", "fidivr", "fild", "fimul", "fincstp", "finit", "fist", "fistp", "fisttp", "fisub", "fisubr", "fld", "fld1", "fldcw", "fldenv", "fldl2e", "fldl2t", "fldlg2", "fldln2", "fldpi", "fldz", "fmul", "fmulp", "fnclex", "fndisi", "fneni", "fninit", "fnop", "fnsave", "fnstcw", "fnstenv", "fnstsw", "fpatan", "fprem", "fprem1", "fptan", "frndint", "frstor", "fsave", "fscale", "fsetpm", "fsin", "fsincos", "fsqrt", "fst", "fstcw", "fstenv", "fstp", "fstsw", "fsub", "fsubp", "fsubr", "fsubrp", "ftst", "fucom", "fucomi", "fucomip", "fucomp", "fucompp", "fwait", "fxam", "fxch", "fxrstor", "fxsave", "fxtract", "fyl2x", "fyl2xp1", "haddpd", "haddps", "hlt", "hsubpd", "hsubps", "ibts", "icebp", "idiv", "imul", "in", "inc", "incbin", "insb", "insd", "insw", "int", "int01", "int03", "int1", "int3", "into", "invd", "invlpg", "iret", "iretd", "iretw", "jcxz", "jecxz", "jmp", "jmpe", "lahf", "lar", "lddqu", "ldmxcsr", "lds", "lea", "leave", "les", "lfence", "lfs", "lgdt", "lgs", "lidt", "lldt", "lmsw", "loadall", "loadall286", "lodsb", "lodsd", "lodsw", "loop", "loope", "loopne", "loopnz", "loopz", "lsl", "lss", "ltr", "maskmovdqu", "maskmovq", "maxpd", "maxps", "maxsd", "maxss", "mfence", "minpd", "minps", "minsd", "minss", "monitor", "mov", "movapd", "movaps", "movd", "movddup", "movdq2q", "movdqa", "movdqu", "movhlps", "movhpd", "movhps", "movlhps", "movlpd", "movlps", "movmskpd", "movmskps", "movntdq", "movnti", "movntpd", "movntps", "movntq", "movq", "movq2dq", "movsb", "movsd", "movshdup", "movsldup", "movss", "movsw", "movsx", "movupd", "movups", "movzx", "mul", "mulpd", "mulps", "mulsd", "mulss", "mwait", "neg", "nop", "not", "or", "orpd", "orps", "out", "outsb", "outsd", "outsw", "packssdw", "packsswb", "packuswb", "paddb", "paddd", "paddq", "paddsb", "paddsiw", "paddsw", "paddusb", "paddusw", "paddw", "pand", "pandn", "pause", "paveb", "pavgb", "pavgusb", "pavgw", "pcmpeqb", "pcmpeqd", "pcmpeqw", "pcmpgtb", "pcmpgtd", "pcmpgtw", "pdistib", "pextrw", "pf2id", "pf2iw", "pfacc", "pfadd", "pfcmpeq", "pfcmpge", "pfcmpgt", "pfmax", "pfmin", "pfmul", "pfnacc", "pfpnacc", "pfrcp", "pfrcpit1", "pfrcpit2", "pfrsqit1", "pfrsqrt", "pfsub", "pfsubr", "pi2fd", "pi2fw", "pinsrw", "pmachriw", "pmaddwd", "pmagw", "pmaxsw", "pmaxub", "pminsw", "pminub", "pmovmskb", "pmulhriw", "pmulhrwa", "pmulhrwc", "pmulhuw", "pmulhw", "pmullw", "pmuludq", "pmvgezb", "pmvlzb", "pmvnzb", "pmvzb", "pop", "popa", "popad", "popaw", "popf", "popfd", "popfw", "por", "prefetch", "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2", "prefetchw", "psadbw", "pshufd", "pshufhw", "pshuflw", "pshufw", "pslld", "pslldq", "psllq", "psllw", "psrad", "psraw", "psrld", "psrldq", "psrlq", "psrlw", "psubb", "psubd", "psubq", "psubsb", "psubsiw", "psubsw", "psubusb", "psubusw", "psubw", "pswapd", "punpckhbw", "punpckhdq", "punpckhqdq", "punpckhwd", "punpcklbw", "punpckldq", "punpcklqdq", "punpcklwd", "push", "pusha", "pushad", "pushaw", "pushf", "pushfd", "pushfw", "pxor", "rcl", "rcpps", "rcpss", "rcr", "rdmsr", "rdpmc", "rdshr", "rdtsc", "resb", "resd", "resq", "rest", "resw", "ret", "retf", "retn", "rol", "ror", "rsdc", "rsldt", "rsm", "rsqrtps", "rsqrtss", "rsts", "sahf", "sal", "salc", "sar", "sbb", "scasb", "scasd", "scasw", "sfence", "sgdt", "shl", "shld", "shr", "shrd", "shufpd", "shufps", "sidt", "sldt", "smi", "smint", "smintold", "smsw", "sqrtpd", "sqrtps", "sqrtsd", "sqrtss", "stc", "std", "sti", "stmxcsr", "stosb", "stosd", "stosw", "str", "sub", "subpd", "subps", "subsd", "subss", "svdc", "svldt", "svts", "syscall", "sysenter", "sysexit", "sysret", "test", "ucomisd", "ucomiss", "ud0", "ud1", "ud2", "umov", "unpckhpd", "unpckhps", "unpcklpd", "unpcklps", "verr", "verw", "wait", "wbinvd", "wrmsr", "wrshr", "xadd", "xbts", "xchg", "xlat", "xlatb", "xor", "xorpd", "xorps", "xstore" }; /* Conditional instructions */ static const char *icn[] = { "cmov", "j", "set" }; /* and the corresponding opcodes */ static int ico[] = { I_CMOVcc, I_Jcc, I_SETcc }; #define INSN_MAX 32 /* one instruction can't be longer than this */ long disasm (unsigned char *data, char *output, int segsize, long offset); extern struct itemplate **itable[]; /* * Flags that go into the `segment' field of `insn' structures * during disassembly. */ #define SEG_RELATIVE 1 #define SEG_32BIT 2 #define SEG_RMREG 4 #define SEG_DISP8 8 #define SEG_DISP16 16 #define SEG_DISP32 32 #define SEG_NODISP 64 #define SEG_SIGNED 128 static int whichreg(long regflags, int regval) { /* automatically generated from ./regs.dat - do not edit */ static const int creg [] = {R_CR0,R_CR1,R_CR2,R_CR3,R_CR4,R_CR5,R_CR6,R_CR7}; static const int dreg [] = {R_DR0,R_DR1,R_DR2,R_DR3,R_DR4,R_DR5,R_DR6,R_DR7}; static const int fpureg [] = {R_ST0,R_ST1,R_ST2,R_ST3,R_ST4,R_ST5,R_ST6,R_ST7}; static const int mmxreg [] = {R_MM0,R_MM1,R_MM2,R_MM3,R_MM4,R_MM5,R_MM6,R_MM7}; static const int reg16 [] = {R_AX,R_CX,R_DX,R_BX,R_SP,R_BP,R_SI,R_DI}; static const int reg32 [] = {R_EAX,R_ECX,R_EDX,R_EBX,R_ESP,R_EBP,R_ESI,R_EDI}; static const int reg8 [] = {R_AL,R_CL,R_DL,R_BL,R_AH,R_CH,R_DH,R_BH}; static const int sreg [] = {R_ES,R_CS,R_SS,R_DS,R_FS,R_GS,R_SEGR6,R_SEGR7}; static const int treg [] = {R_TR0,R_TR1,R_TR2,R_TR3,R_TR4,R_TR5,R_TR6,R_TR7}; static const int xmmreg [] = {R_XMM0,R_XMM1,R_XMM2,R_XMM3,R_XMM4,R_XMM5,R_XMM6,R_XMM7}; if (!(REG_AL & ~regflags)) return R_AL; if (!(REG_AX & ~regflags)) return R_AX; if (!(REG_EAX & ~regflags)) return R_EAX; if (!(REG_DL & ~regflags)) return R_DL; if (!(REG_DX & ~regflags)) return R_DX; if (!(REG_EDX & ~regflags)) return R_EDX; if (!(REG_CL & ~regflags)) return R_CL; if (!(REG_CX & ~regflags)) return R_CX; if (!(REG_ECX & ~regflags)) return R_ECX; if (!(FPU0 & ~regflags)) return R_ST0; if (!(REG_CS & ~regflags)) return (regval == 1) ? R_CS : 0; if (!(REG_DESS & ~regflags)) return (regval == 0 || regval == 2 || regval == 3 ? sreg[regval] : 0); if (!(REG_FSGS & ~regflags)) return (regval == 4 || regval == 5 ? sreg[regval] : 0); if (!(REG_SEG67 & ~regflags)) return (regval == 6 || regval == 7 ? sreg[regval] : 0); /* All the entries below look up regval in an 8-entry array */ if (regval < 0 || regval > 7) return 0; if (!((REGMEM|BITS8) & ~regflags)) return reg8[regval]; if (!((REGMEM|BITS16) & ~regflags)) return reg16[regval]; if (!((REGMEM|BITS32) & ~regflags)) return reg32[regval]; if (!(REG_SREG & ~regflags)) return sreg[regval]; if (!(REG_CREG & ~regflags)) return creg[regval]; if (!(REG_DREG & ~regflags)) return dreg[regval]; if (!(REG_TREG & ~regflags)) return treg[regval]; if (!(FPUREG & ~regflags)) return fpureg[regval]; if (!(MMXREG & ~regflags)) return mmxreg[regval]; if (!(XMMREG & ~regflags)) return xmmreg[regval]; return 0; } static const char *whichcond(int condval) { static int conds[] = { C_O, C_NO, C_C, C_NC, C_Z, C_NZ, C_NA, C_A, C_S, C_NS, C_PE, C_PO, C_L, C_NL, C_NG, C_G }; return conditions[conds[condval]]; } /* * Process an effective address (ModRM) specification. */ static unsigned char *do_ea (unsigned char *data, int modrm, int asize, int segsize, operand *op) { int mod, rm, scale, index, base; mod = (modrm >> 6) & 03; rm = modrm & 07; if (mod == 3) { /* pure register version */ op->basereg = rm; op->segment |= SEG_RMREG; return data; } op->addr_size = 0; if (asize == 16) { /* * specifies the displacement size (none, byte or * word), and specifies the register combination. * Exception: mod=0,rm=6 does not specify [BP] as one might * expect, but instead specifies [disp16]. */ op->indexreg = op->basereg = -1; op->scale = 1; /* always, in 16 bits */ switch (rm) { case 0: op->basereg = R_BX; op->indexreg = R_SI; break; case 1: op->basereg = R_BX; op->indexreg = R_DI; break; case 2: op->basereg = R_BP; op->indexreg = R_SI; break; case 3: op->basereg = R_BP; op->indexreg = R_DI; break; case 4: op->basereg = R_SI; break; case 5: op->basereg = R_DI; break; case 6: op->basereg = R_BP; break; case 7: op->basereg = R_BX; break; } if (rm == 6 && mod == 0) { /* special case */ op->basereg = -1; if (segsize != 16) op->addr_size = 16; mod = 2; /* fake disp16 */ } switch (mod) { case 0: op->segment |= SEG_NODISP; break; case 1: op->segment |= SEG_DISP8; op->offset = (signed char) *data++; break; case 2: op->segment |= SEG_DISP16; op->offset = *data++; op->offset |= ((unsigned) *data++) << 8; break; } return data; } else { /* * Once again, specifies displacement size (this time * none, byte or *dword*), while specifies the base * register. Again, [EBP] is missing, replaced by a pure * disp32 (this time that's mod=0,rm=*5*). However, rm=4 * indicates not a single base register, but instead the * presence of a SIB byte... */ op->indexreg = -1; switch (rm) { case 0: op->basereg = R_EAX; break; case 1: op->basereg = R_ECX; break; case 2: op->basereg = R_EDX; break; case 3: op->basereg = R_EBX; break; case 5: op->basereg = R_EBP; break; case 6: op->basereg = R_ESI; break; case 7: op->basereg = R_EDI; break; } if (rm == 5 && mod == 0) { op->basereg = -1; if (segsize != 32) op->addr_size = 32; mod = 2; /* fake disp32 */ } if (rm == 4) { /* process SIB */ scale = (*data >> 6) & 03; index = (*data >> 3) & 07; base = *data & 07; data++; op->scale = 1 << scale; switch (index) { case 0: op->indexreg = R_EAX; break; case 1: op->indexreg = R_ECX; break; case 2: op->indexreg = R_EDX; break; case 3: op->indexreg = R_EBX; break; case 4: op->indexreg = -1; break; case 5: op->indexreg = R_EBP; break; case 6: op->indexreg = R_ESI; break; case 7: op->indexreg = R_EDI; break; } switch (base) { case 0: op->basereg = R_EAX; break; case 1: op->basereg = R_ECX; break; case 2: op->basereg = R_EDX; break; case 3: op->basereg = R_EBX; break; case 4: op->basereg = R_ESP; break; case 6: op->basereg = R_ESI; break; case 7: op->basereg = R_EDI; break; case 5: if (mod == 0) { mod = 2; op->basereg = -1; } else op->basereg = R_EBP; break; } } switch (mod) { case 0: op->segment |= SEG_NODISP; break; case 1: op->segment |= SEG_DISP8; op->offset = (signed char) *data++; break; case 2: op->segment |= SEG_DISP32; op->offset = *data++; op->offset |= ((unsigned) *data++) << 8; op->offset |= ((long) *data++) << 16; op->offset |= ((long) *data++) << 24; break; } return data; } } /* * Determine whether the instruction template in t corresponds to the data * stream in data. Return the number of bytes matched if so. */ static int matches (struct itemplate *t, unsigned char *data, int asize, int osize, int segsize, int rep, insn *ins) { unsigned char * r = (unsigned char *)(t->code); unsigned char * origdata = data; int a_used = FALSE, o_used = FALSE; int drep = 0; if ( rep == 0xF2 ) drep = P_REPNE; else if ( rep == 0xF3 ) drep = P_REP; while (*r) { int c = *r++; if (c >= 01 && c <= 03) { while (c--) if (*r++ != *data++) return FALSE; } if (c == 04) { switch (*data++) { case 0x07: ins->oprs[0].basereg = 0; break; case 0x17: ins->oprs[0].basereg = 2; break; case 0x1F: ins->oprs[0].basereg = 3; break; default: return FALSE; } } if (c == 05) { switch (*data++) { case 0xA1: ins->oprs[0].basereg = 4; break; case 0xA9: ins->oprs[0].basereg = 5; break; default: return FALSE; } } if (c == 06) { switch (*data++) { case 0x06: ins->oprs[0].basereg = 0; break; case 0x0E: ins->oprs[0].basereg = 1; break; case 0x16: ins->oprs[0].basereg = 2; break; case 0x1E: ins->oprs[0].basereg = 3; break; default: return FALSE; } } if (c == 07) { switch (*data++) { case 0xA0: ins->oprs[0].basereg = 4; break; case 0xA8: ins->oprs[0].basereg = 5; break; default: return FALSE; } } if (c >= 010 && c <= 012) { int t = *r++, d = *data++; if (d < t || d > t+7) return FALSE; else { ins->oprs[c-010].basereg = d-t; ins->oprs[c-010].segment |= SEG_RMREG; } } if (c == 017) if (*data++) return FALSE; if (c >= 014 && c <= 016) { ins->oprs[c-014].offset = (signed char) *data++; ins->oprs[c-014].segment |= SEG_SIGNED; } if (c >= 020 && c <= 022) ins->oprs[c-020].offset = *data++; if (c >= 024 && c <= 026) ins->oprs[c-024].offset = *data++; if (c >= 030 && c <= 032) { ins->oprs[c-030].offset = *data++; ins->oprs[c-030].offset |= (((unsigned) *data++) << 8); } if (c >= 034 && c <= 036) { ins->oprs[c-034].offset = *data++; ins->oprs[c-034].offset |= (((unsigned) *data++) << 8); if (osize == 32) { ins->oprs[c-034].offset |= (((long) *data++) << 16); ins->oprs[c-034].offset |= (((long) *data++) << 24); } if (segsize != asize) ins->oprs[c-034].addr_size = asize; } if (c >= 040 && c <= 042) { ins->oprs[c-040].offset = *data++; ins->oprs[c-040].offset |= (((unsigned) *data++) << 8); ins->oprs[c-040].offset |= (((long) *data++) << 16); ins->oprs[c-040].offset |= (((long) *data++) << 24); } if (c >= 044 && c <= 046) { ins->oprs[c-044].offset = *data++; ins->oprs[c-044].offset |= (((unsigned) *data++) << 8); if (asize == 32) { ins->oprs[c-044].offset |= (((long) *data++) << 16); ins->oprs[c-044].offset |= (((long) *data++) << 24); } if (segsize != asize) ins->oprs[c-044].addr_size = asize; } if (c >= 050 && c <= 052) { ins->oprs[c-050].offset = (signed char) *data++; ins->oprs[c-050].segment |= SEG_RELATIVE; } if (c >= 060 && c <= 062) { ins->oprs[c-060].offset = *data++; ins->oprs[c-060].offset |= (((unsigned) *data++) << 8); ins->oprs[c-060].segment |= SEG_RELATIVE; ins->oprs[c-060].segment &= ~SEG_32BIT; } if (c >= 064 && c <= 066) { ins->oprs[c-064].offset = *data++; ins->oprs[c-064].offset |= (((unsigned) *data++) << 8); if (osize == 32) { ins->oprs[c-064].offset |= (((long) *data++) << 16); ins->oprs[c-064].offset |= (((long) *data++) << 24); ins->oprs[c-064].segment |= SEG_32BIT; } else ins->oprs[c-064].segment &= ~SEG_32BIT; ins->oprs[c-064].segment |= SEG_RELATIVE; if (segsize != osize) { ins->oprs[c-064].type = (ins->oprs[c-064].type & NON_SIZE) | ((osize == 16) ? BITS16 : BITS32); } } if (c >= 070 && c <= 072) { ins->oprs[c-070].offset = *data++; ins->oprs[c-070].offset |= (((unsigned) *data++) << 8); ins->oprs[c-070].offset |= (((long) *data++) << 16); ins->oprs[c-070].offset |= (((long) *data++) << 24); ins->oprs[c-070].segment |= SEG_32BIT | SEG_RELATIVE; } if (c >= 0100 && c < 0130) { int modrm = *data++; ins->oprs[c & 07].basereg = (modrm >> 3) & 07; ins->oprs[c & 07].segment |= SEG_RMREG; data = do_ea (data, modrm, asize, segsize, &ins->oprs[(c >> 3) & 07]); } if (c >= 0130 && c <= 0132) { ins->oprs[c-0130].offset = *data++; ins->oprs[c-0130].offset |= (((unsigned) *data++) << 8); } if (c >= 0140 && c <= 0142) { ins->oprs[c-0140].offset = *data++; ins->oprs[c-0140].offset |= (((unsigned) *data++) << 8); ins->oprs[c-0140].offset |= (((long) *data++) << 16); ins->oprs[c-0140].offset |= (((long) *data++) << 24); } if (c >= 0200 && c <= 0277) { int modrm = *data++; if (((modrm >> 3) & 07) != (c & 07)) return FALSE; /* spare field doesn't match up */ data = do_ea (data, modrm, asize, segsize, &ins->oprs[(c >> 3) & 07]); } if (c >= 0300 && c <= 0302) { if (asize) ins->oprs[c-0300].segment |= SEG_32BIT; else ins->oprs[c-0300].segment &= ~SEG_32BIT; a_used = TRUE; } if (c == 0310) { if (asize == 32) return FALSE; else a_used = TRUE; } if (c == 0311) { if (asize == 16) return FALSE; else a_used = TRUE; } if (c == 0312) { if (asize != segsize) return FALSE; else a_used = TRUE; } if (c == 0320) { if (osize == 32) return FALSE; else o_used = TRUE; } if (c == 0321) { if (osize == 16) return FALSE; else o_used = TRUE; } if (c == 0322) { if (osize != segsize) return FALSE; else o_used = TRUE; } if (c == 0330) { int t = *r++, d = *data++; if (d < t || d > t+15) return FALSE; else ins->condition = d - t; } if (c == 0331) { if ( rep ) return FALSE; } if (c == 0332) { if (drep == P_REP) drep = P_REPE; } if (c == 0333) { if ( rep != 0xF3 ) return FALSE; drep = 0; } } /* * Check for unused rep or a/o prefixes. */ ins->nprefix = 0; if (drep) ins->prefixes[ins->nprefix++] = drep; if (!a_used && asize != segsize) ins->prefixes[ins->nprefix++] = (asize == 16 ? P_A16 : P_A32); if (!o_used && osize != segsize) ins->prefixes[ins->nprefix++] = (osize == 16 ? P_O16 : P_O32); return data - origdata; } long disasm (unsigned char *data, char *output, int segsize, long offset) { struct itemplate **p, **best_p; int length, best_length = 0; char *segover; int rep, lock, asize, osize, i, slen, colon; unsigned char *origdata; int works; insn tmp_ins, ins; unsigned long goodness, best; /* * Scan for prefixes. */ asize = osize = segsize; segover = NULL; ins.condition = ins.nprefix = rep = lock = 0; origdata = data; for (;;) { if (*data == 0xF3 || *data == 0xF2) rep = *data++; else if (*data == 0xF0) lock = *data++; else if (*data == 0x2E || *data == 0x36 || *data == 0x3E || *data == 0x26 || *data == 0x64 || *data == 0x65) { switch (*data++) { case 0x2E: segover = "cs"; break; case 0x36: segover = "ss"; break; case 0x3E: segover = "ds"; break; case 0x26: segover = "es"; break; case 0x64: segover = "fs"; break; case 0x65: segover = "gs"; break; } } else if (*data == 0x66) osize = 48 - segsize, data++; else if (*data == 0x67) asize = 48 - segsize, data++; else break; } tmp_ins.oprs[0].segment = tmp_ins.oprs[1].segment = tmp_ins.oprs[2].segment = tmp_ins.oprs[0].addr_size = tmp_ins.oprs[1].addr_size = tmp_ins.oprs[2].addr_size = (segsize == 16 ? 0 : SEG_32BIT); tmp_ins.condition = -1; best = ~0UL; /* Worst possible */ best_p = NULL; for (p = itable[*data]; *p; p++) { if ( (length = matches(*p, data, asize, osize, segsize, rep, &tmp_ins)) ) { works = TRUE; /* * Final check to make sure the types of r/m match up. */ for (i = 0; i < (*p)->operands; i++) { if ( /* If it's a mem-only EA but we have a register, die. */ ((tmp_ins.oprs[i].segment & SEG_RMREG) && !(MEMORY & ~(*p)->opd[i])) || /* If it's a reg-only EA but we have a memory ref, die. */ (!(tmp_ins.oprs[i].segment & SEG_RMREG) && !(REGNORM & ~(*p)->opd[i]) && !((*p)->opd[i] & REG_SMASK)) || /* Register type mismatch (eg FS vs REG_DESS): die. */ ((((*p)->opd[i] & (REGISTER | FPUREG)) || (tmp_ins.oprs[i].segment & SEG_RMREG)) && !whichreg ((*p)->opd[i], tmp_ins.oprs[i].basereg))) { works = FALSE; break; } } if (works) { goodness = (*p)->flags & IF_PFMASK; if ( goodness < best ) { /* This is the best one found so far */ best = goodness; best_p = p; best_length = length; ins = tmp_ins; } } } } if (!best_p) { /* no instruction was matched */ sprintf(output, "db 0%02xh", data[0]); return 1; } /* Pick the best match */ p = best_p; length = best_length; slen = 0; if (lock) slen += sprintf(output+slen, "lock "); for (i = 0; i < ins.nprefix; i++) switch (ins.prefixes[i]) { case P_REP: slen += sprintf(output+slen, "rep "); break; case P_REPE: slen += sprintf(output+slen, "repe "); break; case P_REPNE: slen += sprintf(output+slen, "repne "); break; case P_A16: slen += sprintf(output+slen, "a16 "); break; case P_A32: slen += sprintf(output+slen, "a32 "); break; case P_O16: slen += sprintf(output+slen, "o16 "); break; case P_O32: slen += sprintf(output+slen, "o32 "); break; } for (i = 0; i < (int)elements(ico); i++) if ((*p)->opcode == ico[i]) { slen += sprintf(output+slen, "%s%s", icn[i], whichcond(ins.condition)); break; } if (i >= (int)elements(ico)) slen += sprintf(output+slen, "%s", insn_names[(*p)->opcode]); colon = FALSE; length += data - origdata; /* fix up for prefixes */ for (i=0; i<(*p)->operands; i++) { output[slen++] = (colon ? ':' : i==0 ? ' ' : ','); if (ins.oprs[i].segment & SEG_RELATIVE) { ins.oprs[i].offset += offset + length; /* * sort out wraparound */ if (!(ins.oprs[i].segment & SEG_32BIT)) ins.oprs[i].offset &= 0xFFFF; } if ((*p)->opd[i] & COLON) colon = TRUE; else colon = FALSE; if (((*p)->opd[i] & (REGISTER | FPUREG)) || (ins.oprs[i].segment & SEG_RMREG)) { ins.oprs[i].basereg = whichreg ((*p)->opd[i], ins.oprs[i].basereg); if ( (*p)->opd[i] & TO ) slen += sprintf(output+slen, "to "); slen += sprintf(output+slen, "%s", reg_names[ins.oprs[i].basereg-EXPR_REG_START]); } else if (!(UNITY & ~(*p)->opd[i])) { output[slen++] = '1'; } else if ( (*p)->opd[i] & IMMEDIATE ) { if ( (*p)->opd[i] & BITS8 ) { slen += sprintf(output+slen, "byte "); if (ins.oprs[i].segment & SEG_SIGNED) { if (ins.oprs[i].offset < 0) { ins.oprs[i].offset *= -1; output[slen++] = '-'; } else output[slen++] = '+'; } } else if ( (*p)->opd[i] & BITS16 ) { slen += sprintf(output+slen, "word "); } else if ( (*p)->opd[i] & BITS32 ) { slen += sprintf(output+slen, "dword "); } else if ( (*p)->opd[i] & NEAR ) { slen += sprintf(output+slen, "near "); } else if ( (*p)->opd[i] & SHORT ) { slen += sprintf(output+slen, "short "); } slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); } else if ( !(MEM_OFFS & ~(*p)->opd[i]) ) { slen += sprintf(output+slen, "[%s%s%s0x%lx]", (segover ? segover : ""), (segover ? ":" : ""), (ins.oprs[i].addr_size == 32 ? "dword " : ins.oprs[i].addr_size == 16 ? "word " : ""), ins.oprs[i].offset); segover = NULL; } else if ( !(REGMEM & ~(*p)->opd[i]) ) { int started = FALSE; if ( (*p)->opd[i] & BITS8 ) slen += sprintf(output+slen, "byte "); if ( (*p)->opd[i] & BITS16 ) slen += sprintf(output+slen, "word "); if ( (*p)->opd[i] & BITS32 ) slen += sprintf(output+slen, "dword "); if ( (*p)->opd[i] & BITS64 ) slen += sprintf(output+slen, "qword "); if ( (*p)->opd[i] & BITS80 ) slen += sprintf(output+slen, "tword "); if ( (*p)->opd[i] & FAR ) slen += sprintf(output+slen, "far "); if ( (*p)->opd[i] & NEAR ) slen += sprintf(output+slen, "near "); output[slen++] = '['; if (ins.oprs[i].addr_size) slen += sprintf(output+slen, "%s", (ins.oprs[i].addr_size == 32 ? "dword " : ins.oprs[i].addr_size == 16 ? "word " : "")); if (segover) { slen += sprintf(output+slen, "%s:", segover); segover = NULL; } if (ins.oprs[i].basereg != -1) { slen += sprintf(output+slen, "%s", reg_names[(ins.oprs[i].basereg - EXPR_REG_START)]); started = TRUE; } if (ins.oprs[i].indexreg != -1) { if (started) output[slen++] = '+'; slen += sprintf(output+slen, "%s", reg_names[(ins.oprs[i].indexreg - EXPR_REG_START)]); if (ins.oprs[i].scale > 1) slen += sprintf(output+slen, "*%d", ins.oprs[i].scale); started = TRUE; } if (ins.oprs[i].segment & SEG_DISP8) { int sign = '+'; if (ins.oprs[i].offset & 0x80) { ins.oprs[i].offset = - (signed char) ins.oprs[i].offset; sign = '-'; } slen += sprintf(output+slen, "%c0x%lx", sign, ins.oprs[i].offset); } else if (ins.oprs[i].segment & SEG_DISP16) { if (started) output[slen++] = '+'; slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); } else if (ins.oprs[i].segment & SEG_DISP32) { if (started) output[slen++] = '+'; slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); } output[slen++] = ']'; } else { slen += sprintf(output+slen, "", i); } } output[slen] = '\0'; if (segover) { /* unused segment override */ char *p = output; int count = slen+1; while (count--) p[count+3] = p[count]; strncpy (output, segover, 2); output[2] = ' '; } return length; } simh-3.8.1/AltairZ80/i8272.h0000644000175000017500000000656711027330564013322 0ustar vlmvlm/************************************************************************* * * * $Id: i8272.h 1903 2008-05-18 02:21:23Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Generic Intel 8272 Disk Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ extern t_stat i8272_attach(UNIT *uptr, char *cptr); extern t_stat i8272_detach(UNIT *uptr); extern uint8 I8272_Set_DMA(const uint32 dma_addr); extern uint8 I8272_Read(const uint32 Addr); extern uint8 I8272_Write(const uint32 Addr, uint8 cData); #define I8272_FDC_MSR 0 /* R=FDC Main Status Register, W=Drive Select Register */ #define I8272_FDC_DATA 1 /* R/W FDC Data Register */ simh-3.8.1/AltairZ80/s100_adcs6.c0000644000175000017500000011015311111141264014257 0ustar vlmvlm/************************************************************************* * * * $Id: s100_adcs6.c 1970 2008-06-26 06:01:27Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Advanced Digital Corporation (ADC) Super-Six CPU Board * * module for SIMH. * * * * This module is a wrapper around the wd179x FDC module, and adds the * * ADC-specific registers as well as the Digitex Monitor Boot ROM. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #define DBG_MSG #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_defs.h" /* simulator definitions */ #include "wd179x.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define DRIVE_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define IRQ_MSG (1 << 8) #define DMA_MSG (1 << 9) #define ADCS6_MAX_DRIVES 4 #define ADCS6_ROM_SIZE (2 * 1024) #define ADCS6_ADDR_MASK (ADCS6_ROM_SIZE - 1) typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_addr; /* DMA Transfer Address */ uint8 rom_disabled; /* TRUE if ROM has been disabled */ uint8 head_sel; uint8 autowait; uint8 rtc; uint8 imask; /* Interrupt Mask Register */ uint8 ipend; /* Interrupt Pending Register */ uint8 s100_addr_u; /* A23:16 of S-100 bus */ } ADCS6_INFO; extern WD179X_INFO_PUB *wd179x_info; static ADCS6_INFO adcs6_info_data = { { 0xF000, ADCS6_ROM_SIZE, 0x3, 2 } }; static ADCS6_INFO *adcs6_info = &adcs6_info_data; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); static t_stat adcs6_svc (UNIT *uptr); extern REG *sim_PC; extern uint32 PCX; /* external view of PC */ #define UNIT_V_ADCS6_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_ADCS6_WLK (1 << UNIT_V_ADCS6_WLK) #define UNIT_V_ADCS6_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_ADCS6_VERBOSE (1 << UNIT_V_ADCS6_VERBOSE) #define UNIT_V_ADCS6_ROM (UNIT_V_UF + 2) /* boot ROM enabled */ #define UNIT_ADCS6_ROM (1 << UNIT_V_ADCS6_ROM) #define ADCS6_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ #define MOTOR_TO_LIMIT 128 static t_stat adcs6_reset(DEVICE *adcs6_dev); static t_stat adcs6_boot(int32 unitno, DEVICE *dptr); static t_stat adcs6_attach(UNIT *uptr, char *cptr); static t_stat adcs6_detach(UNIT *uptr); static int32 adcs6_dma(const int32 port, const int32 io, const int32 data); static int32 adcs6_timer(const int32 port, const int32 io, const int32 data); static int32 adcs6_control(const int32 port, const int32 io, const int32 data); static int32 adcs6_banksel(const int32 port, const int32 io, const int32 data); static int32 adcs6rom(const int32 port, const int32 io, const int32 data); static int32 dipswitch = 0x00; /* 5-position DIP switch on 64FDC card */ /* Disk Control/Flags Register, 0x34 (IN) */ #define ADCS6_FLAG_DRQ (1 << 7) /* DRQ (All controllers) */ #define ADCS6_FLAG_BOOT (1 << 6) /* boot# jumper (active low) (All controllers) */ #define ADCS6_FLAG_SEL_REQ (1 << 5) /* Head Load (4FDC, 16FDC) / Select Request (64FDC) */ #define ADCS6_FLAG_INH_INIT (1 << 4) /* Unassigned (4FDC) / Inhibit_Init# (16FDC, 64FDC) */ #define ADCS6_FLAG_MTRON (1 << 3) /* Unassigned (4FDC) / Motor On (16FDC, 64FDC) */ #define ADCS6_FLAG_MTO (1 << 2) /* Unassigned (4FDC) / Motor Timeout (16FDC, 64FDC) */ #define ADCS6_FLAG_ATO (1 << 1) /* Unassigned (4FDC) / Autowait Timeout (16FDC, 64FDC) */ #define ADCS6_FLAG_EOJ (1 << 0) /* End of Job (INTRQ) (All Controllers) (16FDC, 64FDC) */ /* Disk Control/Flags Register, 0x34 (OUT) */ #define ADCS6_CTRL_AUTOWAIT (1 << 7) /* Auto Wait Enable (All controllers) */ #define ADCS6_CTRL_DDENS (1 << 3) /* Unassigned (4FDC) / Double Density (16FDC, 64FDC) */ #define ADCS6_CTRL_HDS (1 << 2) /* Motor On (All controllers) */ #define ADCS6_CTRL_MINI (1 << 4) /* Mini (5.25") (All controllers) */ /* 64FDC Auxiliary Disk Command, 0x04 (OUT) */ #define ADCS6_AUX_RESERVED0 (1 << 0) /* Unused (All Controllers) */ #define ADCS6_AUX_CMD_SIDE (1 << 1) /* 16FDC, 64FDC: Side Select* Low=Side 1, High=Side 0. */ #define ADCS6_AUX_CTRL_OUT (1 << 2) /* Control Out* (All Controllers) */ #define ADCS6_AUX_RESTORE (1 << 3) /* 4FDC, 16FDC Restore* / 64FDC Unused */ #define ADCS6_AUX_FAST_SEEK (1 << 4) /* 4FDC, 16FDC Fast Seek* / 64FDC Unused */ #define ADCS6_AUX_SEL_OVERRIDE (1 << 5) /* 4FDC Eject Right* / 16FDC, 64FDC Drive Select Override */ #define ADCS6_AUX_EJECT (1 << 6) /* 4FDC Eject Left* / 16FDC Eject*, 64FDC Unused */ #define ADCS6_AUX_RESERVED7 (1 << 7) /* Unused (All Controllers) */ /* 64FDC Interrupt Mask Register, 0x03 (OUT) */ #define ADCS6_IRQ_TIMER1 (1 << 0) /* Timer1 Interrupt Mask */ #define ADCS6_IRQ_TIMER2 (1 << 1) /* Timer2 Interrupt Mask */ #define ADCS6_IRQ_EOJ (1 << 2) /* End of Job Interrupt Mask */ #define ADCS6_IRQ_TIMER3 (1 << 3) /* Timer3 Interrupt Mask */ #define ADCS6_IRQ_RDA (1 << 4) /* Read Data Available Interrupt Mask */ #define ADCS6_IRQ_TBE (1 << 5) /* Transmit Buffer Empty Interrupt Mask */ #define ADCS6_IRQ_TIMER4 (1 << 6) /* Timer4 Interrupt Mask */ #define ADCS6_IRQ_TIMER5 (1 << 7) /* Timer5 Interrupt Mask */ #define ADCS6_TIMER1_RST 0xC7 /* RST0 - 0xC7 */ #define ADCS6_TIMER2_RST 0xCF /* RST8 - 0xCF */ #define ADCS6_EOJ_RST 0xD7 /* RST10 - 0xD7 */ #define ADCS6_TIMER3_RST 0xDF /* RST18 - 0xDF */ #define ADCS6_RDA_RST 0xE7 /* RST20 - 0xE7 */ #define ADCS6_TBE_RST 0xEF /* RST28 - 0xEF */ #define ADCS6_TIMER4_RST 0xF7 /* RST30 - 0xF7 */ #define ADCS6_TIMER5_RST 0xFF /* RST38 - 0xFF */ #define RST_OPCODE_TO_VECTOR(x) (x & 0x38) /* The ADCS6 does not really have RAM associated with it, but for ease of integration with the * SIMH/AltairZ80 Resource Mapping Scheme, rather than Map and Unmap the ROM, simply implement our * own RAM that can be swapped in when the ADCS6 Boot ROM is disabled. */ static uint8 adcs6ram[ADCS6_ROM_SIZE]; static UNIT adcs6_unit[] = { { UDATA (&adcs6_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE + UNIT_ADCS6_ROM, ADCS6_CAPACITY), 1024 }, { UDATA (&adcs6_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ADCS6_CAPACITY) }, { UDATA (&adcs6_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ADCS6_CAPACITY) }, { UDATA (&adcs6_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ADCS6_CAPACITY) } }; static REG adcs6_reg[] = { { HRDATA (J7, dipswitch, 8), }, { NULL } }; static MTAB adcs6_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_ADCS6_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_ADCS6_WLK, UNIT_ADCS6_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_ADCS6_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_ADCS6_VERBOSE, UNIT_ADCS6_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { UNIT_ADCS6_ROM, 0, "NOROM", "NOROM", NULL }, { UNIT_ADCS6_ROM, UNIT_ADCS6_ROM, "ROM", "ROM", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(adcs6_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB adcs6_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "DRIVE", DRIVE_MSG }, { "VERBOSE",VERBOSE_MSG }, { "IRQ", IRQ_MSG }, { "DMA", DMA_MSG }, { NULL, 0 } }; DEVICE adcs6_dev = { "ADCS6", adcs6_unit, adcs6_reg, adcs6_mod, ADCS6_MAX_DRIVES, 10, 31, 1, ADCS6_MAX_DRIVES, ADCS6_MAX_DRIVES, NULL, NULL, &adcs6_reset, &adcs6_boot, &adcs6_attach, &adcs6_detach, &adcs6_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, adcs6_dt, NULL, "ADC Super-Six ADCS6" }; /* This is the DIGITEX Monitor version 1.2.A -- 10/06/83 * * MONITOR COMMANDS : * B = LOAD DISK BOOT LOADER * DSSSS,QQQQ = DUMP MEMORY IN HEX FROM S TO Q * FSSSS,QQQQ,BB = FILL MEMORY FROM S TO Q WITH B * GAAAA = GO TO ADDRESS A * IPP = INPUT FROM PORT P * LAAAA = LOAD MEMORY STARTING AT A * MSSSS,QQQQ,DDDD = MOVE STARTING AT S TO Q TO ADDR. D * OPP,DD = OUTPUT DATA D TO PORT P * ESC WILL TERMINATE ANY COMMAND */ static uint8 adcs6_rom[ADCS6_ROM_SIZE] = { 0xC3, 0x3C, 0xF0, 0xC3, 0xA4, 0xF0, 0xC3, 0xB6, 0xF0, 0xC3, 0xAF, 0xF0, 0xC3, 0xC9, 0xF0, 0xC3, 0xE1, 0xF0, 0xC3, 0xF0, 0xF0, 0xC3, 0x06, 0xF1, 0xC3, 0x14, 0xF1, 0xC3, 0x0B, 0xF1, 0xC3, 0x22, 0xF1, 0xC3, 0x2D, 0xF1, 0xC3, 0x4A, 0xF1, 0xC3, 0x77, 0xF1, 0xC3, 0xA1, 0xF1, 0xC3, 0xFA, 0xF1, 0xC3, 0xBC, 0xF3, 0xC3, 0x53, 0xF3, 0xC3, 0x8E, 0xF2, 0xC3, 0xA0, 0xF2, 0xDB, 0x15, 0xD3, 0x18, 0xCB, 0x77, 0x28, 0x10, 0xAF, 0xD3, 0x15, 0xD3, 0x40, 0xD3, 0x17, 0x3E, 0x40, 0xD3, 0x16, 0x21, 0x3E, 0x60, 0x18, 0x0A, 0xAF, 0xD3, 0x17, 0x3E, 0x4F, 0xD3, 0x16, 0x21, 0x3E, 0x6F, 0x22, 0x04, 0xEE, 0xAF, 0x32, 0x01, 0xEE, 0x31, 0x64, 0xEE, 0x21, 0xCA, 0xF3, 0x01, 0x01, 0x08, 0xED, 0xB3, 0x21, 0xD2, 0xF3, 0xCD, 0xE1, 0xF0, 0xC3, 0xB2, 0xF2, 0x31, 0x64, 0xEE, 0x21, 0x4D, 0xF4, 0xCD, 0xE1, 0xF0, 0xCD, 0xC9, 0xF0, 0x47, 0x21, 0xAF, 0xF6, 0x7E, 0xFE, 0xFF, 0x28, 0x08, 0xB8, 0x28, 0x0D, 0x23, 0x23, 0x23, 0x18, 0xF3, 0x21, 0x52, 0xF4, 0xCD, 0xE1, 0xF0, 0x18, 0xDB, 0x23, 0x5E, 0x23, 0x56, 0xEB, 0xE9, 0xF5, 0xDB, 0x01, 0xE6, 0x04, 0x28, 0xFA, 0xF1, 0xD3, 0x00, 0xC9, 0xDB, 0x01, 0xE6, 0x01, 0xC8, 0x18, 0x06, 0xDB, 0x01, 0xE6, 0x01, 0x28, 0xFA, 0xDB, 0x00, 0xE6, 0x7F, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xE6, 0x5F, 0xC9, 0x3E, 0xFF, 0x32, 0x00, 0xEE, 0xCD, 0xB6, 0xF0, 0xF5, 0x3A, 0x00, 0xEE, 0xA7, 0x20, 0x02, 0xF1, 0xC9, 0xF1, 0xFE, 0x20, 0xD4, 0xA4, 0xF0, 0xC9, 0xF5, 0xE5, 0x7E, 0xB7, 0x28, 0x06, 0xCD, 0xA4, 0xF0, 0x23, 0x18, 0xF6, 0xE1, 0xF1, 0xC9, 0xE5, 0x21, 0x14, 0xF5, 0xCD, 0xE1, 0xF0, 0xE1, 0xC9, 0xCD, 0xAF, 0xF0, 0xFE, 0x1B, 0xCA, 0x79, 0xF0, 0xFE, 0x08, 0xD0, 0x18, 0xF3, 0xF5, 0x3E, 0x20, 0x18, 0x12, 0xF5, 0x0F, 0x0F, 0x0F, 0x0F, 0xCD, 0x14, 0xF1, 0xF1, 0xF5, 0xE6, 0x0F, 0xC6, 0x90, 0x27, 0xCE, 0x40, 0x27, 0xCD, 0xA4, 0xF0, 0xF1, 0xC9, 0xF5, 0x7C, 0xCD, 0x0B, 0xF1, 0x7D, 0xCD, 0x0B, 0xF1, 0xF1, 0xC9, 0xCD, 0xC9, 0xF0, 0xFE, 0x2C, 0xC8, 0xFE, 0x20, 0xC8, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0xDA, 0x47, 0xF1, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, 0x07, 0xD6, 0x30, 0xC9, 0xC5, 0xD5, 0x0E, 0x00, 0x1E, 0x00, 0xCD, 0x2D, 0xF1, 0x30, 0x0E, 0xFE, 0x0D, 0x37, 0x20, 0x1A, 0x7B, 0xB7, 0x20, 0x15, 0x37, 0x3E, 0x0D, 0x18, 0x11, 0xFE, 0x10, 0x30, 0x0C, 0x1C, 0x47, 0x79, 0x87, 0x87, 0x87, 0x87, 0x80, 0x4F, 0xC3, 0x50, 0xF1, 0x79, 0xD1, 0xC1, 0xC9, 0xD5, 0x21, 0x00, 0x00, 0x37, 0x3F, 0xF5, 0xCD, 0x2D, 0xF1, 0x30, 0x0D, 0xFE, 0x0D, 0x20, 0x05, 0xCD, 0x06, 0xF1, 0x18, 0x12, 0xF1, 0x37, 0xD1, 0xC9, 0xFE, 0x10, 0x30, 0x0A, 0x29, 0x29, 0x29, 0x29, 0x5F, 0x16, 0x00, 0x19, 0x18, 0xE0, 0xF1, 0xD1, 0xC9, 0x77, 0xBE, 0xC8, 0xE5, 0x21, 0x63, 0xF4, 0xCD, 0xE1, 0xF0, 0xE1, 0xCD, 0x22, 0xF1, 0xC3, 0x79, 0xF0, 0xCD, 0x77, 0xF1, 0xD2, 0xBE, 0xF1, 0x21, 0x5D, 0xF4, 0xC3, 0x99, 0xF0, 0x22, 0x02, 0xEE, 0xCD, 0xF0, 0xF0, 0x2A, 0x02, 0xEE, 0xCD, 0x22, 0xF1, 0xCD, 0x06, 0xF1, 0x7E, 0xCD, 0x0B, 0xF1, 0xCD, 0x06, 0xF1, 0xCD, 0x4A, 0xF1, 0xDA, 0xE4, 0xF1, 0xCD, 0xA1, 0xF1, 0x2A, 0x02, 0xEE, 0x23, 0xC3, 0xBE, 0xF1, 0xFE, 0x0D, 0xCA, 0x79, 0xF0, 0xFE, 0x20, 0xCA, 0xDD, 0xF1, 0xFE, 0x2D, 0xC2, 0xB8, 0xF1, 0x2A, 0x02, 0xEE, 0x2B, 0xC3, 0xBE, 0xF1, 0xF5, 0x7A, 0x2F, 0x57, 0x7B, 0x2F, 0x5F, 0x13, 0xF1, 0xC9, 0xCD, 0x7E, 0xF2, 0xCD, 0x7E, 0xF2, 0xCD, 0xFA, 0xF1, 0xCD, 0xF0, 0xF0, 0xCD, 0x22, 0xF1, 0xCD, 0x06, 0xF1, 0xCD, 0x06, 0xF1, 0x7E, 0xCD, 0x0B, 0xF1, 0xCD, 0x3F, 0xF2, 0xCD, 0x86, 0xF2, 0xFE, 0x13, 0xCC, 0xF9, 0xF0, 0x7D, 0xE6, 0x0F, 0xCA, 0x0D, 0xF2, 0xC3, 0x16, 0xF2, 0xCD, 0x7E, 0xF2, 0xEB, 0xE9, 0x21, 0x17, 0xF5, 0xCD, 0xE1, 0xF0, 0xC3, 0x79, 0xF0, 0xE5, 0x19, 0xDA, 0x79, 0xF0, 0xE1, 0x23, 0xC9, 0xCD, 0x7E, 0xF2, 0xD5, 0xCD, 0x7E, 0xF2, 0xCD, 0x7E, 0xF2, 0xEB, 0xE3, 0x8D, 0xFA, 0xF1, 0x7E, 0xE3, 0xCD, 0xA1, 0xF1, 0x23, 0xE3, 0xCD, 0x3F, 0xF2, 0xCD, 0x86, 0xF2, 0xC3, 0x56, 0xF2, 0xCD, 0x7E, 0xF2, 0xCD, 0x7E, 0xF2, 0xCD, 0xFA, 0xF1, 0xCD, 0x4A, 0xF1, 0xDA, 0xB8, 0xF1, 0xCD, 0xA1, 0xF1, 0xCD, 0x3F, 0xF2, 0xC3, 0x75, 0xF2, 0xCD, 0x77, 0xF1, 0xDA, 0xB8, 0xF1, 0xEB, 0xC9, 0xCD, 0xAF, 0xF0, 0xB7, 0xC2, 0x79, 0xF0, 0xC9, 0xCD, 0x4A, 0xF1, 0xDA, 0xB8, 0xF1, 0x4F, 0xED, 0x78, 0xCD, 0xF0, 0xF0, 0xCD, 0x0B, 0xF1, 0xC3, 0x79, 0xF0, 0xCD, 0x4A, 0xF1, 0xDA, 0xB8, 0xF1, 0x4F, 0xCD, 0x4A, 0xF1, 0xDA, 0xB8, 0xF1, 0xED, 0x79, 0xC3, 0x79, 0xF0, 0xCD, 0x86, 0xF2, 0xCD, 0xC6, 0xF2, 0xCD, 0x6F, 0xF3, 0xCD, 0x86, 0xF2, 0xCD, 0xE8, 0xF2, 0xCD, 0x8F, 0xF3, 0x18, 0xF5, 0xDB, 0x15, 0x47, 0x3E, 0x18, 0xCB, 0x60, 0x20, 0x01, 0xAF, 0x32, 0x06, 0xEE, 0xD3, 0x14, 0x3E, 0x0B, 0xD3, 0x0C, 0x00, 0xDB, 0x14, 0xDB, 0x0C, 0xE6, 0x80, 0xC8, 0xAF, 0x32, 0x06, 0xEE, 0xD3, 0x14, 0xC9, 0xDB, 0x0C, 0x17, 0xD8, 0x21, 0xE8, 0x03, 0xDB, 0x0C, 0xE6, 0x02, 0x28, 0x06, 0x2B, 0x7D, 0xB4, 0x20, 0xF5, 0xC9, 0x06, 0x0A, 0x21, 0x80, 0x3E, 0xDB, 0x0C, 0xE6, 0x02, 0x20, 0x08, 0x2B, 0x7D, 0xB4, 0x20, 0xF5, 0x10, 0xF0, 0xC9, 0x3E, 0xFF, 0x32, 0x01, 0xEE, 0x3E, 0x01, 0x21, 0x00, 0xC0, 0x32, 0x08, 0xEE, 0xD3, 0x0E, 0x3E, 0x8C, 0xD3, 0x0C, 0x00, 0xDB, 0x14, 0xB7, 0xF2, 0x2E, 0xF3, 0xDB, 0x0F, 0x77, 0x23, 0x18, 0xF4, 0xDB, 0x0C, 0xB7, 0x20, 0x19, 0x3A, 0x08, 0xEE, 0x3C, 0xFE, 0x04, 0x20, 0xDD, 0x2A, 0x04, 0xEE, 0x22, 0xFC, 0xBF, 0x2A, 0x4A, 0xF3, 0x22, 0xFE, 0xBF, 0xC3, 0xFC, 0xBF, 0xD3, 0x16, 0xF5, 0x3A, 0x01, 0xEE, 0xB7, 0x28, 0x0D, 0x21, 0xB8, 0xF4, 0xCD, 0xE1, 0xF0, 0xF1, 0xCD, 0x0B, 0xF1, 0xC3, 0x79, 0xF0, 0xF1, 0x21, 0xD4, 0xF4, 0xCD, 0xE1, 0xF0, 0x3E, 0xFF, 0x32, 0x01, 0xEE, 0xC3, 0x79, 0xF0, 0xAF, 0xD3, 0xE6, 0x3E, 0x08, 0xD3, 0xE6, 0x3E, 0x10, 0xD3, 0xE6, 0x3E, 0x18, 0xD3, 0xE6, 0xAF, 0xD3, 0xE6, 0xD3, 0xE3, 0xD3, 0xE4, 0xD3, 0xE5, 0x3C, 0xD3, 0xE2, 0x3E, 0x70, 0xD3, 0xE7, 0xC9, 0xDB, 0xE7, 0xE6, 0x50, 0xFE, 0x50, 0xC0, 0xDB, 0xE7, 0xB7, 0xF8, 0xAF, 0xD3, 0xE6, 0x3E, 0x20, 0xD3, 0xE7, 0xDB, 0xE7, 0xB7, 0xFA, 0xA1, 0xF3, 0xE6, 0x01, 0x20, 0x11, 0x21, 0x00, 0x80, 0x01, 0xE0, 0x00, 0xED, 0xB2, 0x2A, 0x04, 0xEE, 0x22, 0x00, 0x80, 0xC3, 0x00, 0x80, 0x21, 0xF4, 0xF4, 0xCD, 0xE1, 0xF0, 0xDB, 0xE1, 0xCD, 0x0B, 0xF1, 0xC3, 0x79, 0xF0, 0x18, 0x04, 0x44, 0x03, 0xC1, 0x05, 0xEA, 0x00, 0x0D, 0x0A, 0x0A, 0x0A, 0x44, 0x49, 0x47, 0x49, 0x54, 0x45, 0x58, 0x20, 0x4D, 0x6F, 0x6E, 0x69, 0x74, 0x6F, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x31, 0x2E, 0x32, 0x2E, 0x41, 0x20, 0x2D, 0x2D, 0x20, 0x31, 0x30, 0x2F, 0x30, 0x36, 0x2F, 0x38, 0x33, 0x0D, 0x0A, 0x0A, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x22, 0x48, 0x22, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x70, 0x0D, 0x0A, 0x0A, 0x41, 0x74, 0x74, 0x65, 0x6D, 0x70, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x2E, 0x2E, 0x2E, 0x0D, 0x0A, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x2E, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x20, 0x3E, 0x00, 0x20, 0x55, 0x4E, 0x44, 0x45, 0x46, 0x49, 0x4E, 0x45, 0x44, 0x00, 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x0D, 0x0D, 0x0A, 0x4D, 0x45, 0x4D, 0x4F, 0x52, 0x59, 0x20, 0x57, 0x52, 0x49, 0x54, 0x45, 0x20, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x20, 0x41, 0x54, 0x20, 0x00, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x00, 0x20, 0x50, 0x41, 0x55, 0x53, 0x45, 0x00, 0x3F, 0x20, 0x00, 0x20, 0x41, 0x42, 0x4F, 0x52, 0x54, 0x45, 0x44, 0x00, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4E, 0x47, 0x20, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x3A, 0x00, 0x45, 0x4E, 0x44, 0x49, 0x4E, 0x47, 0x20, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x3A, 0x00, 0x0D, 0x0A, 0x46, 0x44, 0x43, 0x20, 0x43, 0x4F, 0x4C, 0x44, 0x20, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x20, 0x43, 0x4F, 0x44, 0x45, 0x20, 0x00, 0x0D, 0x0A, 0x49, 0x4E, 0x53, 0x45, 0x52, 0x54, 0x20, 0x44, 0x49, 0x53, 0x4B, 0x20, 0x26, 0x20, 0x50, 0x52, 0x45, 0x53, 0x53, 0x20, 0x42, 0x20, 0x54, 0x4F, 0x20, 0x42, 0x4F, 0x4F, 0x54, 0x00, 0x0D, 0x0A, 0x48, 0x44, 0x43, 0x31, 0x30, 0x30, 0x31, 0x20, 0x43, 0x4F, 0x4C, 0x44, 0x20, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x20, 0x43, 0x4F, 0x44, 0x45, 0x20, 0x00, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x4D, 0x4F, 0x4E, 0x49, 0x54, 0x4F, 0x52, 0x20, 0x43, 0x4F, 0x4D, 0x4D, 0x41, 0x4E, 0x44, 0x53, 0x20, 0x3A, 0x0D, 0x0A, 0xC2, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xBD, 0x20, 0x4C, 0x4F, 0x41, 0x44, 0x20, 0x44, 0x49, 0x53, 0x4B, 0x20, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x4C, 0x4F, 0x41, 0x44, 0x45, 0x52, 0x0D, 0x0A, 0x44, 0x53, 0x53, 0x53, 0x53, 0x2C, 0x51, 0x51, 0x51, 0x51, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x44, 0x55, 0x4D, 0x50, 0x20, 0x4D, 0x45, 0x4D, 0x4F, 0x52, 0x59, 0x20, 0x49, 0x4E, 0x20, 0x48, 0x45, 0x58, 0x20, 0x46, 0x52, 0x4F, 0x4D, 0x20, 0x53, 0x20, 0x54, 0x4F, 0x20, 0x51, 0x0D, 0x0A, 0x46, 0x53, 0x53, 0x53, 0x53, 0x2C, 0x51, 0x51, 0x51, 0x51, 0x2C, 0x42, 0x42, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x46, 0x49, 0x4C, 0x4C, 0x20, 0x4D, 0x45, 0x4D, 0x4F, 0x52, 0x59, 0x20, 0x46, 0x52, 0x4F, 0x4D, 0x20, 0x53, 0x20, 0x54, 0x4F, 0x20, 0x51, 0x20, 0x57, 0x49, 0x54, 0x48, 0x20, 0x42, 0x0D, 0x0A, 0x47, 0x41, 0x41, 0x41, 0x41, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x47, 0x4F, 0x20, 0x54, 0x4F, 0x20, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x20, 0x41, 0x0D, 0x0A, 0x49, 0x50, 0x50, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x49, 0x4E, 0x50, 0x55, 0x54, 0x20, 0x46, 0x52, 0x4F, 0x4D, 0x20, 0x50, 0x4F, 0x52, 0x54, 0x20, 0x50, 0x0D, 0x0A, 0x4C, 0x41, 0x41, 0x41, 0x41, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x4C, 0x4F, 0x41, 0x44, 0x20, 0x4D, 0x45, 0x4D, 0x4F, 0x52, 0x59, 0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4E, 0x47, 0x20, 0x41, 0x54, 0x20, 0x41, 0x0D, 0x0A, 0x4D, 0x53, 0x53, 0x53, 0x53, 0x2C, 0x51, 0x51, 0x51, 0x51, 0x2C, 0x44, 0x44, 0x44, 0x44, 0x20, 0x3D, 0x20, 0x4D, 0x4F, 0x56, 0x45, 0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4E, 0x47, 0x20, 0x41, 0x54, 0x20, 0x53, 0x20, 0x54, 0x4F, 0x20, 0x51, 0x20, 0x54, 0x4F, 0x20, 0x41, 0x44, 0x44, 0x52, 0x2E, 0x20, 0x44, 0x0D, 0x0A, 0x4F, 0x50, 0x50, 0x2C, 0x44, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3D, 0x20, 0x4F, 0x55, 0x54, 0x50, 0x55, 0x54, 0x20, 0x44, 0x41, 0x54, 0x41, 0x20, 0x44, 0x20, 0x54, 0x4F, 0x20, 0x50, 0x4F, 0x52, 0x54, 0x20, 0x50, 0x0D, 0x0A, 0x45, 0x53, 0x43, 0x20, 0x57, 0x49, 0x4C, 0x4C, 0x20, 0x54, 0x45, 0x52, 0x4D, 0x49, 0x4E, 0x41, 0x54, 0x45, 0x20, 0x41, 0x4E, 0x59, 0x20, 0x43, 0x4F, 0x4D, 0x4D, 0x41, 0x4E, 0x44, 0x00, 0x4C, 0xB2, 0xF1, 0x0D, 0x79, 0xF0, 0x2E, 0xC1, 0xF1, 0x2D, 0xF3, 0xF1, 0x44, 0x04, 0xF2, 0x49, 0x8E, 0xF2, 0x4F, 0xA0, 0xF2, 0x46, 0x66, 0xF2, 0x47, 0x31, 0xF2, 0x4D, 0x47, 0xF2, 0x48, 0x36, 0xF2, 0x42, 0xB2, 0xF2, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x10, 0x00, 0x13, 0x00, 0x16, 0x00, 0x19, 0x00, 0x1C, 0x00, 0x1F, 0x00, 0x22, 0x00, 0x25, 0x00, 0x28, 0x00, 0x2B, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x34, 0x00, 0x37, 0x00, 0x3A, 0x00, 0x69, 0x00, 0x71, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7D, 0x00, 0x80, 0x00, 0x83, 0x00, 0x87, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xCF, 0x00, 0xDE, 0x00, 0xE8, 0x00, 0xF2, 0x00, 0xF5, 0x00, 0xFA, 0x00, 0xFF, 0x00, 0x11, 0x01, 0x1E, 0x01, 0x25, 0x01, 0x29, 0x01, 0x2E, 0x01, 0x3C, 0x01, 0x51, 0x01, 0x71, 0x01, 0x7F, 0x01, 0x88, 0x01, 0xA6, 0x01, 0xA9, 0x01, 0xAD, 0x01, 0xB0, 0x01, 0xB3, 0x01, 0xB6, 0x01, 0xB9, 0x01, 0xBC, 0x01, 0xC2, 0x01, 0xC8, 0x01, 0xCB, 0x01, 0xCF, 0x01, 0xD2, 0x01, 0xD5, 0x01, 0xD8, 0x01, 0xDB, 0x01, 0xE2, 0x01, 0xE7, 0x01, 0xEC, 0x01, 0xF1, 0x01, 0xF8, 0x01, 0x05, 0x02, 0x08, 0x02, 0x0B, 0x02, 0x0E, 0x02, 0x11, 0x02, 0x14, 0x02, 0x17, 0x02, 0x1B, 0x02, 0x1E, 0x02, 0x21, 0x02, 0x26, 0x02, 0x2C, 0x02, 0x2F, 0x02, 0x32, 0x02, 0x37, 0x02, 0x3A, 0x02, 0x3D, 0x02, 0x42, 0x02, 0x48, 0x02, 0x4C, 0x02, 0x4F, 0x02, 0x54, 0x02, 0x59, 0x02, 0x5E, 0x02, 0x61, 0x02, 0x64, 0x02, 0x67, 0x02, 0x6A, 0x02, 0x6D, 0x02, 0x70, 0x02, 0x73, 0x02, 0x76, 0x02, 0x79, 0x02, 0x7C, 0x02, 0x7F, 0x02, 0x82, 0x02, 0x87, 0x02, 0x8B, 0x02, 0x8F, 0x02, 0x92, 0x02, 0x98, 0x02, 0x9B, 0x02, 0x9E, 0x02, 0xA1, 0x02, 0xA4, 0x02, 0xA8, 0x02, 0xAB, 0x02, 0xB0, 0x02, 0xB3, 0x02, 0xB6, 0x02, 0xB9, 0x02, 0xBC, 0x02, 0xBF, 0x02, 0xC2, 0x02, 0x26, 0x03, 0x42, 0x03, 0x54, 0x03, 0x57, 0x03, 0x5B, 0x03, 0x00 }; /* returns TRUE iff there exists a disk with VERBOSE */ static int32 adcs6_hasProperty(uint32 property) { int32 i; for (i = 0; i < ADCS6_MAX_DRIVES; i++) if (adcs6_dev.units[i].flags & property) return TRUE; return FALSE; } static uint8 motor_timeout = 0; /* Unit service routine */ static t_stat adcs6_svc (UNIT *uptr) { if(adcs6_info->head_sel == 1) { motor_timeout ++; if(motor_timeout == MOTOR_TO_LIMIT) { adcs6_info->head_sel = 0; TRACE_PRINT(DRIVE_MSG, ("ADCS6: Motor OFF" NLP)) } } adcs6_info->rtc ++; printf("Timer IRQ\n"); adcs6_info->ipend |= ADCS6_IRQ_TIMER3; /* sim_activate (adcs6_unit, adcs6_unit->wait); */ /* requeue! */ return SCPE_OK; } /* Reset routine */ static t_stat adcs6_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ if (adcs6_hasProperty(UNIT_ADCS6_ROM)) { sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &adcs6rom, TRUE); } /* Unmap I/O Ports (0x3-4,0x5-9,0x34,0x40 */ sim_map_resource(0x10, 4, RESOURCE_TYPE_IO, &adcs6_dma, TRUE); sim_map_resource(0x04, 8, RESOURCE_TYPE_IO, &adcs6_timer, TRUE); sim_map_resource(0x14, 1, RESOURCE_TYPE_IO, &adcs6_control, TRUE); sim_map_resource(0x15, 7, RESOURCE_TYPE_IO, &adcs6_banksel, TRUE); } else { /* Connect ADCS6 ROM at base address */ if (adcs6_hasProperty(UNIT_ADCS6_ROM)) { TRACE_PRINT(VERBOSE_MSG, ("ADCS6: ROM Enabled." NLP)) if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &adcs6rom, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } adcs6_info->rom_disabled = FALSE; } else { TRACE_PRINT(VERBOSE_MSG, ("ADCS6: ROM Disabled." NLP)) adcs6_info->rom_disabled = TRUE; } /* Connect ADCS6 FDC Synchronization / Drive / Density Register */ if(sim_map_resource(0x14, 0x01, RESOURCE_TYPE_IO, &adcs6_control, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } /*#define ADCS6 */ #ifdef ADCS6 /* Connect ADCS6 Interrupt, and Aux Disk Registers */ if(sim_map_resource(0x10, 0x04, RESOURCE_TYPE_IO, &adcs6_dma, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect ADCS6 Timer Registers */ if(sim_map_resource(0x04, 0x08, RESOURCE_TYPE_IO, &adcs6_timer, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } #endif /* Connect ADCS6 Memory Management / Bank Select Register */ if(sim_map_resource(0x15, 0x7, RESOURCE_TYPE_IO, &adcs6_banksel, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } /* sim_activate (adcs6_unit, adcs6_unit->wait); */ /* requeue! */ return SCPE_OK; } static t_stat adcs6_boot(int32 unitno, DEVICE *dptr) { DBG_PRINT(("Booting ADCS6 Controller" NLP)); /* Re-enable the ROM in case it was disabled */ adcs6_info->rom_disabled = FALSE; /* Set the PC to 0, and go. */ *((int32 *) sim_PC->loc) = 0xF000; return SCPE_OK; } /* Attach routine */ static t_stat adcs6_attach(UNIT *uptr, char *cptr) { t_stat r; r = wd179x_attach(uptr, cptr); return r; } /* Detach routine */ static t_stat adcs6_detach(UNIT *uptr) { t_stat r; r = wd179x_detach(uptr); return r; } static int32 adcs6rom(const int32 Addr, const int32 write, const int32 data) { /* DBG_PRINT(("ADCS6: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ if(write) { if(adcs6_info->rom_disabled == FALSE) { TRACE_PRINT(ERROR_MSG, ("ADCS6: " ADDRESS_FORMAT " Cannot write to ROM." NLP, PCX)); } else { adcs6ram[Addr & ADCS6_ADDR_MASK] = data; } return 0; } else { if(adcs6_info->rom_disabled == FALSE) { return(adcs6_rom[Addr & ADCS6_ADDR_MASK]); } else { return(adcs6ram[Addr & ADCS6_ADDR_MASK]); } } } /* Disk Control/Flags Register, 0x14 */ static int32 adcs6_control(const int32 port, const int32 io, const int32 data) { int32 result = 0; if(io) { /* I/O Write */ wd179x_info->sel_drive = data & 0x03; if(data & ADCS6_CTRL_MINI) { wd179x_info->drivetype = 5; } else { wd179x_info->drivetype = 8; } if(data & ADCS6_CTRL_HDS) { adcs6_info->head_sel = 1; wd179x_info->fdc_head = 1; } else { adcs6_info->head_sel = 0; wd179x_info->fdc_head = 0; } if(data & ADCS6_CTRL_DDENS) { wd179x_info->ddens = 1; } else { wd179x_info->ddens = 0; } if(data & ADCS6_CTRL_AUTOWAIT) { adcs6_info->autowait = 1; } else { adcs6_info->autowait = 0; } TRACE_PRINT(DRIVE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR CTRL: sel_drive=%d, drivetype=%d, head_sel=%d, dens=%d, aw=%d" NLP, PCX, wd179x_info->sel_drive, wd179x_info->drivetype, adcs6_info->head_sel, wd179x_info->ddens, adcs6_info->autowait)); } else { /* I/O Read */ result = wd179x_info->drq ? 0xFF : 0; if (wd179x_info->intrq) result &= 0x7F; /* TRACE_PRINT(VERBOSE_MSG, */ /* ("ADCS6: " ADDRESS_FORMAT " Read DISK FLAGS, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) */ } return result; } /* ADC Super Six DMA (Z80-DMA) */ static int32 adcs6_dma(const int32 port, const int32 io, const int32 data) { int32 result = 0xff; if(io) { /* I/O Write */ TRACE_PRINT(DMA_MSG, ("ADCS6: " ADDRESS_FORMAT " WR DMA: 0x%02x" NLP, PCX, data & 0xFF)); } else { /* I/O Read */ result = 0xFF; TRACE_PRINT(DMA_MSG, ("ADCS6: " ADDRESS_FORMAT " RD DMA: 0x%02x" NLP, PCX, result)); } return result; } /* ADC Super-Six PIO and CTC ports */ static int32 adcs6_timer(const int32 port, const int32 io, const int32 data) { static int32 result = 0xFF; if(io) { /* Write */ switch(port) { case 0x04: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR PIOA DATA=0x%02x" NLP, PCX, data)); break; case 0x05: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR PIOB DATA=0x%02x" NLP, PCX, data)); break; case 0x06: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR PIOA CTRL=0x%02x" NLP, PCX, data)); break; case 0x07: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR PIOB CTRL=0x%02x" NLP, PCX, data)); break; case 0x08: case 0x09: case 0x0A: case 0x0B: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR CTC%d: 0x%02x" NLP, PCX, port - 8, data)); break; default: TRACE_PRINT(ERROR_MSG, ("ADCS6: " ADDRESS_FORMAT " WR Unhandled Port: 0x%02x=0x%02x" NLP, PCX, port, data)); break; } } else { /* Read */ result = 0xFF; switch(port) { case 0x04: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD PIOA DATA=0x%02x" NLP, PCX, result)); break; case 0x05: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD PIOB DATA=0x%02x" NLP, PCX, result)); break; case 0x06: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD PIOA CTRL=0x%02x" NLP, PCX, result)); break; case 0x07: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD PIOB CTRL=0x%02x" NLP, PCX, result)); break; case 0x08: case 0x09: case 0x0A: case 0x0B: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD CTC%d: 0x%02x" NLP, PCX, port - 8, data)); break; default: TRACE_PRINT(ERROR_MSG, ("ADCS6: " ADDRESS_FORMAT " RD Unhandled Port: 0x%02x=0x%02x" NLP, PCX, port, data)); break; } } return result; } /* 64FDC Bank Select (Write Disables boot ROM) */ static int32 adcs6_banksel(const int32 port, const int32 io, const int32 data) { int32 result; if(io) { /* Write */ switch(port) { case 0x15: adcs6_info->s100_addr_u = data & 0xFF; TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR S100 A[23:16]=0x%02x" NLP, PCX, data)); break; case 0x16: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR MCTRL0: 0x%02x" NLP, PCX, data)); adcs6_info->rom_disabled = (data & 0x20) ? TRUE : FALSE; /* Unmap Boot ROM */ break; case 0x17: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR MCTRL1: 0x%02x" NLP, PCX, data)); break; case 0x18: case 0x19: case 0x1A: case 0x1B: TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " WR BAUD RATE=0x%02x" NLP, PCX, data)); break; default: TRACE_PRINT(ERROR_MSG, ("ADCS6: " ADDRESS_FORMAT " WR Unhandled Port: 0x%02x=0x%02x" NLP, PCX, port, data)); break; } result = 0; } else { /* Read */ result = 0xFF; switch(port) { case 0x15: /* These are the Jumpers at J7. * Bit 7=0 = double-sided disk, bit 7=1 = single sided. * Bit 6=0 = use on-board RAM, Bit 6=1 = use S-100 RAM cards. * Bit 5:0 = "Baud Rate" */ result = dipswitch; TRACE_PRINT(VERBOSE_MSG, ("ADCS6: " ADDRESS_FORMAT " RD BAUD RATE=0x%02x" NLP, PCX, result)); break; case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: default: TRACE_PRINT(ERROR_MSG, ("ADCS6: " ADDRESS_FORMAT " RD attempt from write-only 0x%02x=0x%02x" NLP, PCX, port, result)); break; } } return result; } simh-3.8.1/AltairZ80/s100_mdriveh.c0000644000175000017500000002662011043117044014725 0ustar vlmvlm/************************************************************************* * * * $Id: s100_mdriveh.c 1940 2008-06-13 05:28:57Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro M-DRIVE/H Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define VERBOSE_MSG (1 << 7) #define MDRIVEH_MAX_DRIVES 8 typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_addr; /* DMA Transfer Address */ UNIT uptr[MDRIVEH_MAX_DRIVES]; uint8 *storage[MDRIVEH_MAX_DRIVES]; } MDRIVEH_INFO; static MDRIVEH_INFO mdriveh_info_data = { { 0x0, 0, 0xC6, 2 } }; static MDRIVEH_INFO *mdriveh_info = &mdriveh_info_data; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern REG *sim_PC; #define UNIT_V_MDRIVEH_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_MDRIVEH_WLK (1 << UNIT_V_MDRIVEH_WLK) #define UNIT_V_MDRIVEH_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_MDRIVEH_VERBOSE (1 << UNIT_V_MDRIVEH_VERBOSE) #define MDRIVEH_CAPACITY (512 * 1000) /* Default M-DRIVE/H Capacity */ #define MDRIVEH_NONE 0 static t_stat mdriveh_reset(DEVICE *mdriveh_dev); static int32 mdrivehdev(const int32 port, const int32 io, const int32 data); static uint8 MDRIVEH_Read(const uint32 Addr); static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData); static UNIT mdriveh_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) } }; static REG mdriveh_reg[] = { { NULL } }; static MTAB mdriveh_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_MDRIVEH_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_MDRIVEH_WLK, UNIT_MDRIVEH_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_MDRIVEH_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_MDRIVEH_VERBOSE, UNIT_MDRIVEH_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(mdriveh_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB mdriveh_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE mdriveh_dev = { "MDRIVEH", mdriveh_unit, mdriveh_reg, mdriveh_mod, MDRIVEH_MAX_DRIVES, 10, 31, 1, MDRIVEH_MAX_DRIVES, MDRIVEH_MAX_DRIVES, NULL, NULL, &mdriveh_reset, NULL, NULL, NULL, &mdriveh_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, mdriveh_dt, NULL, "Compupro Memory Drive MDRIVEH" }; /* Reset routine */ static t_stat mdriveh_reset(DEVICE *dptr) { uint8 i; PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, TRUE); } else { /* Connect MDRIVEH at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } for(i=0; iuptr[i] = dptr->units[i]; if((dptr->flags & DEV_DIS) || (dptr->units[i].flags & UNIT_DIS)) { if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) printf("MDRIVEH: Unit %d disabled", i); if(mdriveh_info->storage[i] != NULL) { if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) printf(", freed 0x%p\n", mdriveh_info->storage[i]); free(mdriveh_info->storage[i]); mdriveh_info->storage[i] = NULL; } else if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) { printf(".\n"); } } else { if(mdriveh_info->storage[i] == NULL) { mdriveh_info->storage[i] = calloc(1, 524288); } if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) printf("MDRIVEH: Unit %d enabled, 512K at 0x%p\n", i, mdriveh_info->storage[i]); } } return SCPE_OK; } static int32 mdrivehdev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("MDRIVEH: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); if(io) { MDRIVEH_Write(port, data); return 0; } else { return(MDRIVEH_Read(port)); } } #define MDRIVEH_DATA 0 /* R=Drive Status Register / W=DMA Address Register */ #define MDRIVEH_ADDR 1 /* R=Unused / W=Motor Control Register */ static uint8 MDRIVEH_Read(const uint32 Addr) { uint8 cData; uint8 unit; uint32 offset; cData = 0xFF; /* default is High-Z Data */ switch(Addr & 0x1) { case MDRIVEH_ADDR: TRACE_PRINT(VERBOSE_MSG, ("MDRIVEH: " ADDRESS_FORMAT " RD Addr = 0x%02x" NLP, PCX, cData)); break; case MDRIVEH_DATA: unit = (mdriveh_info->dma_addr & 0x380000) >> 19; offset = mdriveh_info->dma_addr & 0x7FFFF; if(mdriveh_info->storage[unit] != NULL) { cData = mdriveh_info->storage[unit][offset]; } TRACE_PRINT(RD_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " RD Data [%x:%05x] = 0x%02x" NLP, PCX, unit, offset, cData)); /* Increment M-DRIVE/H Data Address */ mdriveh_info->dma_addr++; mdriveh_info->dma_addr &= 0x3FFFFF; break; } return (cData); } static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData) { uint8 result = 0; uint8 unit; uint32 offset; switch(Addr & 0x1) { case MDRIVEH_ADDR: mdriveh_info->dma_addr <<= 8; mdriveh_info->dma_addr &= 0x003FFF00; mdriveh_info->dma_addr |= cData; TRACE_PRINT(SEEK_MSG, ("MDRIVEH: " ADDRESS_FORMAT " DMA Address=%06x" NLP, PCX, mdriveh_info->dma_addr)); break; case MDRIVEH_DATA: unit = (mdriveh_info->dma_addr & 0x380000) >> 19; offset = mdriveh_info->dma_addr & 0x7FFFF; if(mdriveh_info->storage[unit] != NULL) { if(mdriveh_info->uptr[unit].flags & UNIT_MDRIVEH_WLK) { TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit Write Locked" NLP, PCX, unit, offset)); } else { TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = 0x%02x" NLP, PCX, unit, offset, cData)); mdriveh_info->storage[unit][offset] = cData; } } else { TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit OFFLINE" NLP, PCX, unit, offset)); } /* Increment M-DRIVE/H Data Address */ mdriveh_info->dma_addr++; mdriveh_info->dma_addr &= 0x3FFFFF; break; } return (result); } simh-3.8.1/AltairZ80/altairz80_net.c0000644000175000017500000003627311111142454015214 0ustar vlmvlm/* altairz80_net.c: networking capability Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. */ #include "altairz80_defs.h" #include "sim_sock.h" /* Debug flags */ #define ACCEPT_MSG (1 << 0) #define DROP_MSG (1 << 1) #define IN_MSG (1 << 2) #define OUT_MSG (1 << 3) extern uint32 PCX; #define UNIT_V_SERVER (UNIT_V_UF + 0) /* define machine as a server */ #define UNIT_SERVER (1 << UNIT_V_SERVER) #define NET_INIT_POLL_SERVER 16000 #define NET_INIT_POLL_CLIENT 15000 static t_stat net_attach (UNIT *uptr, char *cptr); static t_stat net_detach (UNIT *uptr); static t_stat net_reset (DEVICE *dptr); static t_stat net_svc (UNIT *uptr); static t_stat set_net (UNIT *uptr, int32 value, char *cptr, void *desc); int32 netStatus (const int32 port, const int32 io, const int32 data); int32 netData (const int32 port, const int32 io, const int32 data); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); #define MAX_CONNECTIONS 2 /* maximal number of server connections */ #define BUFFER_LENGTH 512 /* length of input and output buffer */ static struct { int32 Z80StatusPort; /* Z80 status port associated with this ioSocket, read only */ int32 Z80DataPort; /* Z80 data port associated with this ioSocket, read only */ SOCKET masterSocket; /* server master socket, only defined at [1] */ SOCKET ioSocket; /* accepted server socket or connected client socket, 0 iff free */ char inputBuffer[BUFFER_LENGTH]; /* buffer for input characters read from ioSocket */ int32 inputPosRead; /* position of next character to read from buffer */ int32 inputPosWrite; /* position of next character to append to input buffer from ioSocket */ int32 inputSize; /* number of characters in circular input buffer */ char outputBuffer[BUFFER_LENGTH];/* buffer for output characters to be written to ioSocket */ int32 outputPosRead; /* position of next character to write to ioSocket */ int32 outputPosWrite; /* position of next character to append to output buffer */ int32 outputSize; /* number of characters in circular output buffer */ } serviceDescriptor[MAX_CONNECTIONS+1] = { /* serviceDescriptor[0] holds the information for a client */ /* stat dat ms ios in inPR inPW inS out outPR outPW outS */ {0x32, 0x33, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* client Z80 port 50 and 51 */ {0x28, 0x29, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* server Z80 port 40 and 41 */ {0x2a, 0x2b, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0} /* server Z80 port 42 and 43 */ }; static UNIT net_unit = { UDATA (&net_svc, UNIT_ATTABLE, 0), 0, /* wait, set in attach */ 0, /* u3 = Port */ 0, /* u4 = IP of host */ 0, /* u5, unused */ 0, /* u6, unused */ }; static REG net_reg[] = { { DRDATA (POLL, net_unit.wait, 32) }, { HRDATA (IPHOST, net_unit.u4, 32), REG_RO }, { DRDATA (PORT, net_unit.u3, 32), REG_RO }, { NULL } }; static MTAB net_mod[] = { { UNIT_SERVER, 0, "CLIENT", "CLIENT", &set_net}, /* machine is a client */ { UNIT_SERVER, UNIT_SERVER, "SERVER", "SERVER", &set_net}, /* machine is a server */ { 0 } }; #define TRACE_PRINT(level, args) if (net_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB net_dt[] = { { "ACCEPT", ACCEPT_MSG }, { "DROP", DROP_MSG }, { "IN", IN_MSG }, { "OUT", OUT_MSG }, { NULL, 0 } }; DEVICE net_dev = { "NET", &net_unit, net_reg, net_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &net_reset, NULL, &net_attach, &net_detach, NULL, (DEV_DISABLE | DEV_DEBUG), 0, net_dt, NULL, "Network NET" }; static t_stat set_net(UNIT *uptr, int32 value, char *cptr, void *desc) { char temp[CBUFSIZE]; if ((net_unit.flags & UNIT_ATT) && ((net_unit.flags & UNIT_SERVER) != (uint32)value)) { strncpy(temp, net_unit.filename, CBUFSIZE); /* save name for later attach */ net_detach(&net_unit); net_unit.flags ^= UNIT_SERVER; /* now switch from client to server and vice versa */ net_attach(uptr, temp); return SCPE_OK; } return SCPE_OK; } static void serviceDescriptor_reset(const uint32 i) { serviceDescriptor[i].inputPosRead = 0; serviceDescriptor[i].inputPosWrite = 0; serviceDescriptor[i].inputSize = 0; serviceDescriptor[i].outputPosRead = 0; serviceDescriptor[i].outputPosWrite = 0; serviceDescriptor[i].outputSize = 0; } static t_stat net_reset(DEVICE *dptr) { uint32 i; if (net_unit.flags & UNIT_ATT) sim_activate(&net_unit, net_unit.wait); /* start poll */ for (i = 0; i <= MAX_CONNECTIONS; i++) { serviceDescriptor_reset(i); sim_map_resource(serviceDescriptor[i].Z80StatusPort, 1, RESOURCE_TYPE_IO, &netStatus, dptr->flags & DEV_DIS); sim_map_resource(serviceDescriptor[i].Z80DataPort, 1, RESOURCE_TYPE_IO, &netData, dptr->flags & DEV_DIS); } return SCPE_OK; } static t_stat net_attach(UNIT *uptr, char *cptr) { uint32 i, ipa, ipp; t_stat r = get_ipaddr(cptr, &ipa, &ipp); if (r != SCPE_OK) return SCPE_ARG; if (ipa == 0) ipa = 0x7F000001; /* localhost = 127.0.0.1 */ if (ipp == 0) ipp = 3000; net_unit.u3 = ipp; net_unit.u4 = ipa; net_reset(&net_dev); for (i = 0; i <= MAX_CONNECTIONS; i++) serviceDescriptor[i].ioSocket = 0; if (net_unit.flags & UNIT_SERVER) { net_unit.wait = NET_INIT_POLL_SERVER; serviceDescriptor[1].masterSocket = sim_master_sock(ipp); if (serviceDescriptor[1].masterSocket == INVALID_SOCKET) return SCPE_IOERR; } else { net_unit.wait = NET_INIT_POLL_CLIENT; serviceDescriptor[0].ioSocket = sim_connect_sock(ipa, ipp); if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR; } net_unit.flags |= UNIT_ATT; net_unit.filename = (char *) calloc(CBUFSIZE, sizeof (char)); /* alloc name buf */ if (net_unit.filename == NULL) return SCPE_MEM; strncpy(net_unit.filename, cptr, CBUFSIZE); /* save name */ return SCPE_OK; } static t_stat net_detach(UNIT *uptr) { uint32 i; if (!(net_unit.flags & UNIT_ATT)) return SCPE_OK; /* if not attached simply return */ if (net_unit.flags & UNIT_SERVER) sim_close_sock(serviceDescriptor[1].masterSocket, TRUE); for (i = 0; i <= MAX_CONNECTIONS; i++) if (serviceDescriptor[i].ioSocket) sim_close_sock(serviceDescriptor[i].ioSocket, FALSE); free(net_unit.filename); /* free port string */ net_unit.filename = NULL; net_unit.flags &= ~UNIT_ATT; /* not attached */ return SCPE_OK; } /* cannot use sim_check_conn to check whether read will return an error */ static t_stat net_svc(UNIT *uptr) { int32 i, j, k, r; SOCKET s; static char svcBuffer[BUFFER_LENGTH]; if (net_unit.flags & UNIT_ATT) { /* cannot remove due to following else */ sim_activate(&net_unit, net_unit.wait); /* continue poll */ if (net_unit.flags & UNIT_SERVER) { for (i = 1; i <= MAX_CONNECTIONS; i++) if (serviceDescriptor[i].ioSocket == 0) { s = sim_accept_conn(serviceDescriptor[1].masterSocket, NULL); if (s != INVALID_SOCKET) { serviceDescriptor[i].ioSocket = s; TRACE_PRINT(ACCEPT_MSG, ("NET: " ADDRESS_FORMAT " Accepted connection %i with socket %i." NLP, PCX, i, s)); } } } else if (serviceDescriptor[0].ioSocket == 0) { serviceDescriptor[0].ioSocket = sim_connect_sock(net_unit.u4, net_unit.u3); if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR; printf("\rWaiting for server ... Type g (possibly twice) when ready" NLP); return SCPE_STOP; } for (i = 0; i <= MAX_CONNECTIONS; i++) if (serviceDescriptor[i].ioSocket) { if (serviceDescriptor[i].inputSize < BUFFER_LENGTH) { /* there is space left in inputBuffer */ r = sim_read_sock(serviceDescriptor[i].ioSocket, svcBuffer, BUFFER_LENGTH - serviceDescriptor[i].inputSize); if (r == -1) { TRACE_PRINT(DROP_MSG, ("NET: " ADDRESS_FORMAT " Drop connection %i with socket %i." NLP, PCX, i, serviceDescriptor[i].ioSocket)); sim_close_sock(serviceDescriptor[i].ioSocket, FALSE); serviceDescriptor[i].ioSocket = 0; serviceDescriptor_reset(i); continue; } else { for (j = 0; j < r; j++) { serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosWrite++] = svcBuffer[j]; if (serviceDescriptor[i].inputPosWrite == BUFFER_LENGTH) serviceDescriptor[i].inputPosWrite = 0; } serviceDescriptor[i].inputSize += r; } } if (serviceDescriptor[i].outputSize > 0) { /* there is something to write in outputBuffer */ k = serviceDescriptor[i].outputPosRead; for (j = 0; j < serviceDescriptor[i].outputSize; j++) { svcBuffer[j] = serviceDescriptor[i].outputBuffer[k++]; if (k == BUFFER_LENGTH) k = 0; } r = sim_write_sock(serviceDescriptor[i].ioSocket, svcBuffer, serviceDescriptor[i].outputSize); if (r >= 0) { serviceDescriptor[i].outputSize -= r; serviceDescriptor[i].outputPosRead += r; if (serviceDescriptor[i].outputPosRead >= BUFFER_LENGTH) serviceDescriptor[i].outputPosRead -= BUFFER_LENGTH; } else printf("write %i" NLP, r); } } } return SCPE_OK; } int32 netStatus(const int32 port, const int32 io, const int32 data) { uint32 i; if ((net_unit.flags & UNIT_ATT) == 0) return 0; net_svc(&net_unit); if (io == 0) /* IN */ for (i = 0; i <= MAX_CONNECTIONS; i++) if (serviceDescriptor[i].Z80StatusPort == port) return (serviceDescriptor[i].inputSize > 0 ? 1 : 0) | (serviceDescriptor[i].outputSize < BUFFER_LENGTH ? 2 : 0); return 0; } int32 netData(const int32 port, const int32 io, const int32 data) { uint32 i; char result; if ((net_unit.flags & UNIT_ATT) == 0) return 0; net_svc(&net_unit); for (i = 0; i <= MAX_CONNECTIONS; i++) if (serviceDescriptor[i].Z80DataPort == port) if (io == 0) { /* IN */ if (serviceDescriptor[i].inputSize == 0) { printf("re-read from %i" NLP, port); result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead > 0 ? serviceDescriptor[i].inputPosRead - 1 : BUFFER_LENGTH - 1]; } else { result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead++]; if (serviceDescriptor[i].inputPosRead == BUFFER_LENGTH) serviceDescriptor[i].inputPosRead = 0; serviceDescriptor[i].inputSize--; } TRACE_PRINT(IN_MSG, ("NET: " ADDRESS_FORMAT " IN(%i)=%03xh (%c)" NLP, PCX, port, (result & 0xff), (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?')); return result; } else { /* OUT */ if (serviceDescriptor[i].outputSize == BUFFER_LENGTH) { printf("over-write %i to %i" NLP, data, port); serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite > 0 ? serviceDescriptor[i].outputPosWrite - 1 : BUFFER_LENGTH - 1] = data; } else { serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite++] = data; if (serviceDescriptor[i].outputPosWrite== BUFFER_LENGTH) serviceDescriptor[i].outputPosWrite = 0; serviceDescriptor[i].outputSize++; } TRACE_PRINT(OUT_MSG, ("NET: " ADDRESS_FORMAT " OUT(%i)=%03xh (%c)" NLP, PCX, port, data, (32 <= data) && (data <= 127) ? data : '?')); return 0; } return 0; } simh-3.8.1/AltairZ80/s100_scp300f.c0000644000175000017500000006501411043117044014445 0ustar vlmvlm/************************************************************************* * * * $Id: s100_scp300f.c 1940 2008-06-13 05:28:57Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Seattle Computer Products SCP300F Support Board module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ /* #define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define PIO_MSG (1 << 1) #define UART_MSG (1 << 2) #define RTC_MSG (1 << 3) #define ROM_MSG (1 << 5) #define VERBOSE_MSG (1 << 7) #define IRQ_MSG (1 << 8) #define SCP300F_MAX_DRIVES 1 #define SCP300F_ROM_SIZE (2048) #define SCP300F_ADDR_MASK (SCP300F_ROM_SIZE - 1) #define SCP300F_IO_SIZE (16) #define SCP300F_IO_MASK (SCP300F_IO_SIZE - 1) #define UNIT_V_SCP300F_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_SCP300F_VERBOSE (1 << UNIT_V_SCP300F_VERBOSE) typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 *ram; uint8 *rom; uint8 rom_enabled; } SCP300F_INFO; static SCP300F_INFO scp300f_info_data = { { 0xFF800, SCP300F_ROM_SIZE, 0xF0, SCP300F_IO_SIZE } }; static SCP300F_INFO *scp300f_info = &scp300f_info_data; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint32 PCX; extern int32 find_unit_index (UNIT *uptr); static t_stat scp300f_reset(DEVICE *scp300f_dev); static uint8 SCP300F_Read(const uint32 Addr); static uint8 SCP300F_Write(const uint32 Addr, uint8 cData); static int32 scp300fdev(const int32 port, const int32 io, const int32 data); static int32 scp300f_mem(const int32 port, const int32 io, const int32 data); static int32 scp300f_sr = 0x00; /* Sense Switch Register, 0=Monitor prompt, 1=disk boot */ static UNIT scp300f_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE, 0) } }; static REG scp300f_reg[] = { { HRDATA (SR, scp300f_sr, 8), }, { NULL } }; static MTAB scp300f_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, /* quiet, no warning messages */ { UNIT_SCP300F_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_SCP300F_VERBOSE, UNIT_SCP300F_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(scp300f_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB scp300f_dt[] = { { "ERROR", ERROR_MSG }, { "PIO", PIO_MSG }, { "UART", UART_MSG }, { "RTC", RTC_MSG }, { "ROM", ROM_MSG }, { "VERBOSE",VERBOSE_MSG }, { "IRQ", IRQ_MSG }, { NULL, 0 } }; DEVICE scp300f_dev = { "SCP300F", scp300f_unit, scp300f_reg, scp300f_mod, SCP300F_MAX_DRIVES, 10, 31, 1, SCP300F_MAX_DRIVES, SCP300F_MAX_DRIVES, NULL, NULL, &scp300f_reset, NULL, NULL, NULL, &scp300f_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, scp300f_dt, NULL, "SCP Support Board SCP300F" }; /* Reset routine */ static t_stat scp300f_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; TRACE_PRINT(VERBOSE_MSG, ("SCP300F: Reset." NLP)); if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &scp300fdev, TRUE); sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &scp300f_mem, TRUE); } else { /* Connect SCP300F at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &scp300fdev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect SCP300F Memory (512K RAM, 1MB FLASH) */ if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &scp300f_mem, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); return SCPE_ARG; } /* Re-enable ROM */ scp300f_info->rom_enabled = 1; } return SCPE_OK; } static uint8 scp300f_ram[SCP300F_ROM_SIZE]; /* ; Seattle Computer Products 8086 Monitor version 1.5 3-19-82. * ; by Tim Paterson * ; This software is not copyrighted. * * This was assembled from source (MON.ASM) using 86DOS ASM.COM running under Windows XP. * It is configured for a Cromemco 16FDC disk controller. */ static uint8 scp300f_rom[SCP300F_ROM_SIZE] = { 0xFC, 0x33, 0xC0, 0x8E, 0xD0, 0x8E, 0xD8, 0x8E, 0xC0, 0xBF, 0x9C, 0x01, 0xB9, 0x0E, 0x00, 0xF3, 0xAB, 0x80, 0x0E, 0xB7, 0x01, 0x02, 0xB1, 0x04, 0xB0, 0x40, 0xBF, 0xAC, 0x01, 0xF3, 0xAB, 0xC6, 0x06, 0xA5, 0x01, 0x0C, 0xBC, 0x9C, 0x01, 0xB0, 0x17, 0xE6, 0xF5, 0xB0, 0xF3, 0xE6, 0xF4, 0xB8, 0x84, 0x05, 0xE7, 0xF4, 0xBE, 0x33, 0x07, 0xBA, 0xF0, 0x00, 0x2E, 0xAC, 0x8A, 0xC8, 0xE3, 0x05, 0x2E, 0xAC, 0xEE, 0xE2, 0xFB, 0x42, 0x80, 0xFA, 0xF8, 0x75, 0xEF, 0xE8, 0x19, 0x00, 0xBE, 0xF5, 0x07, 0xB8, 0x23, 0xE8, 0xE7, 0xF4, 0xB0, 0x0D, 0xE6, 0xF5, 0x2E, 0xAD, 0xE6, 0xF4, 0x8A, 0xC4, 0xE6, 0xF4, 0xE8, 0x02, 0x00, 0xEB, 0xF3, 0xE8, 0x98, 0x00, 0xE8, 0x95, 0x00, 0x3C, 0x0D, 0x74, 0x01, 0xC3, 0xBF, 0x18, 0x01, 0xC6, 0x05, 0x0D, 0xE4, 0xFF, 0xA8, 0x01, 0x74, 0x03, 0xE9, 0xF5, 0x06, 0xBE, 0x51, 0x07, 0xE8, 0x8B, 0x00, 0xFC, 0x33, 0xC0, 0x8E, 0xD8, 0x8E, 0xC0, 0xBC, 0x9C, 0x01, 0xC7, 0x06, 0x64, 0x00, 0xBB, 0x06, 0x8C, 0x0E, 0x66, 0x00, 0xB0, 0x3E, 0xE8, 0xC8, 0x00, 0xE8, 0x1E, 0x00, 0xE8, 0x7F, 0x00, 0x74, 0xDF, 0x8A, 0x05, 0x2C, 0x42, 0x72, 0x10, 0x3C, 0x13, 0x73, 0x0C, 0x47, 0xD0, 0xE0, 0x98, 0x93, 0x2E, 0xFF, 0x97, 0x7D, 0x01, 0xEB, 0xC9, 0xE9, 0xA8, 0x02, 0xBF, 0x18, 0x01, 0x33, 0xC9, 0xE8, 0x39, 0x00, 0x3C, 0x20, 0x72, 0x1B, 0x3C, 0x7F, 0x74, 0x0E, 0xE8, 0x94, 0x00, 0x3C, 0x40, 0x74, 0x25, 0xAA, 0x41, 0x83, 0xF9, 0x50, 0x76, 0xE7, 0xE3, 0xE5, 0x4F, 0x49, 0xE8, 0x29, 0x00, 0xEB, 0xDE, 0x3C, 0x08, 0x74, 0xF3, 0x3C, 0x0D, 0x75, 0xD6, 0xAA, 0xBF, 0x18, 0x01, 0xB0, 0x0D, 0xE8, 0x6F, 0x00, 0xB0, 0x0A, 0xEB, 0x6B, 0xE8, 0xF4, 0xFF, 0xEB, 0x85, 0xFA, 0xE4, 0xF7, 0xA8, 0x02, 0x74, 0xF9, 0xE4, 0xF6, 0x24, 0x7F, 0xFB, 0xC3, 0xBE, 0x73, 0x07, 0x2E, 0xAC, 0xE8, 0x51, 0x00, 0xD0, 0xE0, 0x73, 0xF7, 0xC3, 0xE8, 0x06, 0x00, 0x82, 0x3D, 0x2C, 0x75, 0x0A, 0x47, 0xB0, 0x20, 0x51, 0xB1, 0xFF, 0xF3, 0xAE, 0x4F, 0x59, 0x82, 0x3D, 0x0D, 0xC3, 0x8C, 0xDA, 0xB4, 0x00, 0xE8, 0x78, 0x00, 0x03, 0xD6, 0xEB, 0x09, 0x8C, 0xC2, 0xB4, 0x00, 0xE8, 0x6D, 0x00, 0x03, 0xD7, 0x82, 0xD4, 0x00, 0xE8, 0x12, 0x00, 0x8A, 0xC6, 0xE8, 0x02, 0x00, 0x8A, 0xC2, 0x8A, 0xE0, 0x51, 0xB1, 0x04, 0xD2, 0xE8, 0x59, 0xE8, 0x02, 0x00, 0x8A, 0xC4, 0x24, 0x0F, 0x04, 0x90, 0x27, 0x14, 0x40, 0x27, 0x50, 0xE4, 0xF7, 0x24, 0x01, 0x74, 0xFA, 0x58, 0xE6, 0xF6, 0xC3, 0xB0, 0x20, 0xEB, 0xF1, 0xE8, 0xF9, 0xFF, 0xE2, 0xFB, 0xC3, 0x76, 0x07, 0x68, 0x03, 0x0D, 0x02, 0x88, 0x03, 0x97, 0x02, 0x6A, 0x06, 0x68, 0x03, 0x4C, 0x06, 0x68, 0x03, 0x68, 0x03, 0x68, 0x03, 0x6A, 0x02, 0x68, 0x03, 0x59, 0x06, 0x68, 0x03, 0x68, 0x03, 0x2F, 0x04, 0xBA, 0x02, 0x6A, 0x05, 0x8A, 0xC2, 0x24, 0x0F, 0xE8, 0x07, 0x00, 0x8A, 0xD0, 0x8A, 0xC6, 0x32, 0xF6, 0xC3, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, 0xD4, 0xC3, 0xB9, 0x05, 0x00, 0xE8, 0x22, 0x01, 0x50, 0x52, 0xE8, 0x4F, 0xFF, 0x82, 0x3D, 0x4C, 0x74, 0x1C, 0xBA, 0x80, 0x00, 0xE8, 0x30, 0x01, 0x72, 0x1B, 0xB9, 0x05, 0x00, 0xE8, 0x0A, 0x01, 0x8B, 0xCA, 0x5A, 0x5B, 0x2B, 0xCA, 0x1A, 0xE7, 0x75, 0x1D, 0x93, 0x41, 0xEB, 0x0B, 0x47, 0xB9, 0x04, 0x00, 0xE8, 0xF5, 0x00, 0x8B, 0xCA, 0x5A, 0x58, 0x8B, 0xDA, 0x81, 0xE3, 0x0F, 0x00, 0xE3, 0x04, 0x03, 0xD9, 0x73, 0x9E, 0x74, 0x9C, 0xB8, 0x52, 0x47, 0xE9, 0x1F, 0x03, 0xE8, 0xB2, 0xFF, 0x50, 0xE8, 0x4E, 0x01, 0x1F, 0x8B, 0xF2, 0xE8, 0x18, 0xFF, 0x56, 0xE8, 0x55, 0xFF, 0xAC, 0xE8, 0x31, 0xFF, 0x5A, 0x49, 0x74, 0x17, 0x8B, 0xC6, 0xA8, 0x0F, 0x74, 0x0C, 0x52, 0xA8, 0x07, 0x75, 0xEA, 0xB0, 0x2D, 0xE8, 0x32, 0xFF, 0xEB, 0xE6, 0xE8, 0x02, 0x00, 0xEB, 0xDA, 0x51, 0x8B, 0xC6, 0x8B, 0xF2, 0x2B, 0xC2, 0x8B, 0xD8, 0xD1, 0xE0, 0x03, 0xC3, 0xB9, 0x33, 0x00, 0x2B, 0xC8, 0xE8, 0x25, 0xFF, 0x8B, 0xCB, 0xAC, 0x24, 0x7F, 0x3C, 0x7F, 0x74, 0x04, 0x3C, 0x20, 0x73, 0x02, 0xB0, 0x2E, 0xE8, 0x04, 0xFF, 0xE2, 0xEE, 0x59, 0xE9, 0x8A, 0xFE, 0xE8, 0x55, 0xFF, 0x51, 0x50, 0x8B, 0xF2, 0xB9, 0x05, 0x00, 0xE8, 0x73, 0x00, 0xE8, 0xE8, 0x00, 0xE8, 0x26, 0xFF, 0x8B, 0xFA, 0x5B, 0x8E, 0xDB, 0x8E, 0xC0, 0x59, 0x3B, 0xFE, 0x1B, 0xC3, 0x72, 0x07, 0x49, 0x03, 0xF1, 0x03, 0xF9, 0xFD, 0x41, 0xA4, 0x49, 0xF3, 0xA4, 0xC3, 0xE8, 0x28, 0xFF, 0x51, 0x50, 0x52, 0xE8, 0xB4, 0x00, 0x5F, 0x07, 0x59, 0x3B, 0xD9, 0xBE, 0x18, 0x01, 0xE3, 0x02, 0x73, 0xE6, 0x2B, 0xCB, 0x87, 0xD9, 0x57, 0xF3, 0xA4, 0x5E, 0x8B, 0xCB, 0x06, 0x1F, 0xEB, 0xD8, 0xE8, 0x05, 0xFF, 0x51, 0x50, 0x52, 0xE8, 0x91, 0x00, 0x4B, 0x5F, 0x07, 0x59, 0x2B, 0xCB, 0xBE, 0x18, 0x01, 0xAC, 0xAE, 0xE0, 0xFD, 0x75, 0xC4, 0x53, 0x87, 0xCB, 0x57, 0xF3, 0xA6, 0x8B, 0xCB, 0x5F, 0x5B, 0x75, 0x08, 0x4F, 0xE8, 0x5B, 0xFE, 0x47, 0xE8, 0x0E, 0xFE, 0xE3, 0xAE, 0xEB, 0xDF, 0xE8, 0x2F, 0xFE, 0x33, 0xD2, 0x8A, 0xE6, 0xE8, 0x14, 0x00, 0x72, 0x73, 0x8A, 0xD0, 0x47, 0x49, 0xE8, 0x0B, 0x00, 0x72, 0x97, 0xE3, 0x68, 0xE8, 0xAD, 0xFE, 0x0A, 0xD0, 0xEB, 0xF0, 0x8A, 0x05, 0x2C, 0x30, 0x72, 0x88, 0x3C, 0x0A, 0xF5, 0x73, 0x83, 0x2C, 0x07, 0x3C, 0x0A, 0x72, 0x03, 0x3C, 0x10, 0xF5, 0xC3, 0xE8, 0xFC, 0xFD, 0xE8, 0xE5, 0xFF, 0x72, 0x0B, 0xB9, 0x02, 0x00, 0xE8, 0xBF, 0xFF, 0x88, 0x17, 0x43, 0xF8, 0xC3, 0x8A, 0x05, 0x3C, 0x27, 0x74, 0x06, 0x3C, 0x22, 0x74, 0x02, 0xF9, 0xC3, 0x8A, 0xE0, 0x47, 0x8A, 0x05, 0x47, 0x3C, 0x0D, 0x74, 0x23, 0x3A, 0xC4, 0x75, 0x05, 0x3A, 0x25, 0x75, 0xE0, 0x47, 0x88, 0x07, 0x43, 0xEB, 0xEB, 0xBB, 0x18, 0x01, 0xE8, 0xC3, 0xFF, 0x73, 0xFB, 0x81, 0xEB, 0x18, 0x01, 0x74, 0x07, 0xE8, 0xC0, 0xFD, 0x75, 0x02, 0xC3, 0x4F, 0x81, 0xEF, 0x17, 0x01, 0x8B, 0xCF, 0xE8, 0x05, 0xFE, 0xBE, 0x6A, 0x07, 0xE8, 0x9A, 0xFD, 0xE9, 0x0C, 0xFD, 0xE8, 0xD6, 0xFF, 0x5F, 0x07, 0xBE, 0x18, 0x01, 0x8B, 0xCB, 0xF3, 0xA4, 0xC3, 0xB9, 0x05, 0x00, 0xE8, 0x5C, 0xFF, 0xE8, 0x12, 0xFE, 0x82, 0xEC, 0x08, 0x80, 0xC6, 0x80, 0x50, 0x52, 0xE8, 0x89, 0xFD, 0x75, 0xDD, 0x5F, 0x07, 0xE8, 0x9A, 0xFD, 0xE8, 0xCD, 0xFD, 0x26, 0x8A, 0x05, 0xE8, 0xA7, 0xFD, 0xB0, 0x2E, 0xE8, 0xB7, 0xFD, 0xB9, 0x02, 0x00, 0xBA, 0x00, 0x00, 0xE8, 0x48, 0xFD, 0x8A, 0xE0, 0xE8, 0x4B, 0xFF, 0x86, 0xE0, 0x72, 0x0C, 0xE8, 0xA2, 0xFD, 0x8A, 0xF2, 0x8A, 0xD4, 0xE2, 0xEB, 0xE8, 0x33, 0xFD, 0x3C, 0x08, 0x74, 0x19, 0x3C, 0x7F, 0x74, 0x15, 0x3C, 0x2D, 0x74, 0x4D, 0x3C, 0x0D, 0x74, 0x2F, 0x3C, 0x20, 0x74, 0x31, 0xB0, 0x07, 0xE8, 0x80, 0xFD, 0xE3, 0xE2, 0xEB, 0xCB, 0x82, 0xF9, 0x02, 0x74, 0xC6, 0xFE, 0xC1, 0x8A, 0xD6, 0x8A, 0xF5, 0xE8, 0x15, 0xFD, 0xEB, 0xBB, 0x82, 0xF9, 0x02, 0x74, 0x0B, 0x51, 0xB1, 0x04, 0xD2, 0xE6, 0x59, 0x0A, 0xD6, 0x26, 0x88, 0x15, 0x47, 0xC3, 0xE8, 0xEB, 0xFF, 0xE9, 0xE0, 0xFC, 0xE8, 0xE5, 0xFF, 0x41, 0x41, 0xE8, 0x5B, 0xFD, 0x8B, 0xC7, 0x24, 0x07, 0x75, 0x84, 0xE8, 0xCF, 0xFC, 0xE9, 0x78, 0xFF, 0xE8, 0xD1, 0xFF, 0x4F, 0x4F, 0xEB, 0xF3, 0xE8, 0xEA, 0xFC, 0x74, 0x62, 0x8A, 0x15, 0x47, 0x8A, 0x35, 0x82, 0xFE, 0x0D, 0x74, 0x76, 0x47, 0xE8, 0x20, 0xFF, 0x82, 0xFE, 0x20, 0x74, 0x6D, 0xBF, 0xD7, 0x06, 0x92, 0x0E, 0x07, 0xB9, 0x0E, 0x00, 0xF2, 0xAF, 0x75, 0x3C, 0x0B, 0xC9, 0x75, 0x06, 0x4F, 0x4F, 0x2E, 0x8B, 0x45, 0xFE, 0xE8, 0x07, 0xFD, 0x8A, 0xC4, 0xE8, 0x02, 0xFD, 0xE8, 0x0A, 0xFD, 0x1E, 0x07, 0x8D, 0x9D, 0xC3, 0xFA, 0x8B, 0x17, 0xE8, 0xD8, 0xFC, 0xE8, 0x7D, 0xFC, 0xB0, 0x3A, 0xE8, 0xEC, 0xFC, 0xE8, 0x42, 0xFC, 0xE8, 0xA3, 0xFC, 0x74, 0x0B, 0xB9, 0x04, 0x00, 0xE8, 0x63, 0xFE, 0xE8, 0xD5, 0xFE, 0x89, 0x17, 0xC3, 0xB8, 0x42, 0x52, 0xE9, 0x96, 0x00, 0xBE, 0xD7, 0x06, 0xBB, 0x9C, 0x01, 0xB9, 0x08, 0x00, 0xE8, 0x65, 0x00, 0xE8, 0x4F, 0xFC, 0xB9, 0x05, 0x00, 0xE8, 0x5C, 0x00, 0xE8, 0xC5, 0xFC, 0xE8, 0x93, 0x00, 0xE9, 0x40, 0xFC, 0x82, 0xFA, 0x46, 0x75, 0xD7, 0xE8, 0x88, 0x00, 0xB0, 0x2D, 0xE8, 0xA7, 0xFC, 0xE8, 0xFD, 0xFB, 0xE8, 0x5E, 0xFC, 0x33, 0xDB, 0x8B, 0x16, 0xB6, 0x01, 0x8B, 0xF7, 0xAD, 0x3C, 0x0D, 0x74, 0x66, 0x82, 0xFC, 0x0D, 0x74, 0x66, 0xBF, 0xF3, 0x06, 0xB9, 0x20, 0x00, 0x0E, 0x07, 0xF2, 0xAF, 0x75, 0x5A, 0x8A, 0xE9, 0x80, 0xE1, 0x0F, 0xB8, 0x01, 0x00, 0xD3, 0xC0, 0x85, 0xC3, 0x75, 0x33, 0x0B, 0xD8, 0x0B, 0xD0, 0xF6, 0xC5, 0x10, 0x75, 0x02, 0x33, 0xD0, 0x8B, 0xFE, 0x1E, 0x07, 0xE8, 0x17, 0xFC, 0xEB, 0xC6, 0x2E, 0xAD, 0xE8, 0x5C, 0xFC, 0x8A, 0xC4, 0xE8, 0x57, 0xFC, 0xB0, 0x3D, 0xE8, 0x52, 0xFC, 0x8B, 0x17, 0x43, 0x43, 0xE8, 0x2F, 0xFC, 0xE8, 0x53, 0xFC, 0xE8, 0x50, 0xFC, 0xE2, 0xE2, 0xC3, 0xB8, 0x44, 0x46, 0xE8, 0x0E, 0x00, 0xE8, 0x39, 0xFC, 0x8A, 0xC4, 0xE8, 0x34, 0xFC, 0xBE, 0x6B, 0x07, 0xE9, 0x3B, 0xFE, 0x89, 0x16, 0xB6, 0x01, 0xC3, 0xB8, 0x42, 0x46, 0xEB, 0xE5, 0xBE, 0xF3, 0x06, 0xB9, 0x10, 0x00, 0x8B, 0x16, 0xB6, 0x01, 0x2E, 0xAD, 0xD1, 0xE2, 0x72, 0x04, 0x2E, 0x8B, 0x44, 0x1E, 0x0B, 0xC0, 0x74, 0x0B, 0xE8, 0x09, 0xFC, 0x8A, 0xC4, 0xE8, 0x04, 0xFC, 0xE8, 0x0C, 0xFC, 0xE2, 0xE5, 0xC3, 0xE8, 0xAF, 0xFB, 0xE8, 0x98, 0xFD, 0xBA, 0x01, 0x00, 0x72, 0x06, 0xB9, 0x04, 0x00, 0xE8, 0x6F, 0xFD, 0x89, 0x16, 0x02, 0x01, 0xE8, 0xE0, 0xFD, 0xC7, 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x0E, 0xB7, 0x01, 0x01, 0xC7, 0x06, 0x0C, 0x00, 0xD1, 0x05, 0x8C, 0x0E, 0x0E, 0x00, 0xC7, 0x06, 0x04, 0x00, 0xD8, 0x05, 0x8C, 0x0E, 0x06, 0x00, 0xFA, 0xC7, 0x06, 0x64, 0x00, 0xD8, 0x05, 0x8C, 0x0E, 0x66, 0x00, 0xBC, 0x9C, 0x01, 0x58, 0x5B, 0x59, 0x5A, 0x5D, 0x5D, 0x5E, 0x5F, 0x07, 0x07, 0x17, 0x8B, 0x26, 0xA4, 0x01, 0xFF, 0x36, 0xB6, 0x01, 0xFF, 0x36, 0xB2, 0x01, 0xFF, 0x36, 0xB4, 0x01, 0x8E, 0x1E, 0xAC, 0x01, 0xCF, 0xEB, 0xB1, 0x87, 0xEC, 0xFF, 0x4E, 0x00, 0x87, 0xEC, 0x2E, 0x89, 0x26, 0xA4, 0x09, 0x2E, 0x8C, 0x16, 0xB0, 0x09, 0x33, 0xE4, 0x8E, 0xD4, 0xBC, 0xB0, 0x01, 0x06, 0x1E, 0x57, 0x56, 0x55, 0x4C, 0x4C, 0x52, 0x51, 0x53, 0x50, 0x16, 0x1F, 0x8B, 0x26, 0xA4, 0x01, 0x8E, 0x16, 0xB0, 0x01, 0x8F, 0x06, 0xB4, 0x01, 0x8F, 0x06, 0xB2, 0x01, 0x58, 0x80, 0xE4, 0xFE, 0xA3, 0xB6, 0x01, 0x89, 0x26, 0xA4, 0x01, 0x1E, 0x07, 0x1E, 0x17, 0xBC, 0x9C, 0x01, 0xC7, 0x06, 0x64, 0x00, 0xBB, 0x06, 0xB0, 0x20, 0xE6, 0xF2, 0xFB, 0xFC, 0xE8, 0xCD, 0xFA, 0xE8, 0x6C, 0xFE, 0xFF, 0x0E, 0x02, 0x01, 0x75, 0x9F, 0xBE, 0x04, 0x01, 0x8B, 0x0E, 0x00, 0x01, 0xE3, 0x10, 0x8B, 0x54, 0x14, 0xAD, 0x50, 0xE8, 0x62, 0xFB, 0x8E, 0xC0, 0x8B, 0xFA, 0x58, 0xAA, 0xE2, 0xF0, 0xE9, 0x3B, 0xFA, 0xB9, 0x04, 0x00, 0xE8, 0x98, 0xFC, 0xEC, 0xE8, 0xFD, 0xFA, 0xE9, 0x9B, 0xFA, 0xB9, 0x04, 0x00, 0xE8, 0x8B, 0xFC, 0x52, 0xB9, 0x02, 0x00, 0xE8, 0x84, 0xFC, 0x92, 0x5A, 0xEE, 0xC3, 0xBB, 0x18, 0x01, 0x33, 0xF6, 0xE8, 0xAA, 0xFA, 0x74, 0x19, 0xB9, 0x05, 0x00, 0xE8, 0x70, 0xFC, 0x89, 0x17, 0x88, 0x67, 0xED, 0x43, 0x43, 0x46, 0x83, 0xFE, 0x0B, 0x75, 0xE8, 0xB8, 0x42, 0x50, 0xE9, 0x9F, 0xFE, 0x89, 0x36, 0x00, 0x01, 0xE8, 0xCE, 0xFC, 0x8B, 0xCE, 0xE3, 0x1A, 0xBE, 0x04, 0x01, 0x8B, 0x54, 0x14, 0xAD, 0xE8, 0x01, 0xFB, 0x8E, 0xD8, 0x8B, 0xFA, 0x8A, 0x05, 0xC6, 0x05, 0xCC, 0x06, 0x1F, 0x88, 0x44, 0xFE, 0xE2, 0xE9, 0xC7, 0x06, 0x02, 0x01, 0x01, 0x00, 0xE9, 0xD2, 0xFE, 0x50, 0xB0, 0x20, 0xE6, 0xF2, 0xE4, 0xF6, 0x24, 0x7F, 0x3C, 0x13, 0x75, 0x03, 0xE8, 0x37, 0xFA, 0x3C, 0x03, 0x74, 0x02, 0x58, 0xCF, 0xE8, 0x20, 0xFA, 0xE9, 0xB0, 0xF9, 0x41, 0x58, 0x42, 0x58, 0x43, 0x58, 0x44, 0x58, 0x53, 0x50, 0x42, 0x50, 0x53, 0x49, 0x44, 0x49, 0x44, 0x53, 0x45, 0x53, 0x53, 0x53, 0x43, 0x53, 0x49, 0x50, 0x50, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x56, 0x44, 0x4E, 0x45, 0x49, 0x00, 0x00, 0x4E, 0x47, 0x5A, 0x52, 0x00, 0x00, 0x41, 0x43, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x43, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x56, 0x55, 0x50, 0x44, 0x49, 0x00, 0x00, 0x50, 0x4C, 0x4E, 0x5A, 0x00, 0x00, 0x4E, 0x41, 0x00, 0x00, 0x50, 0x4F, 0x00, 0x00, 0x4E, 0x43, 0x01, 0x19, 0x04, 0x10, 0x02, 0x0F, 0xFD, 0x01, 0x19, 0x04, 0x18, 0x01, 0x0B, 0xFD, 0x06, 0x63, 0x0B, 0x07, 0x00, 0x06, 0x00, 0x02, 0x70, 0x05, 0x00, 0x04, 0xB7, 0x77, 0xCE, 0x37, 0x0D, 0x0A, 0x0A, 0x53, 0x43, 0x50, 0x20, 0x38, 0x30, 0x38, 0x36, 0x20, 0x4D, 0x6F, 0x6E, 0x69, 0x74, 0x6F, 0x72, 0x20, 0x31, 0x2E, 0x35, 0x0D, 0x8A, 0x5E, 0x20, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x0D, 0x8A, 0x08, 0x20, 0x88, 0x57, 0xB0, 0x01, 0xE6, 0x02, 0xB0, 0x84, 0xE6, 0x00, 0xB0, 0x7F, 0xE6, 0x04, 0xB6, 0x21, 0xB0, 0x30, 0xE6, 0x34, 0xB9, 0xC4, 0xAA, 0xD4, 0x0A, 0xD4, 0x0A, 0xE2, 0xFA, 0xB0, 0xD0, 0xE6, 0x30, 0xD4, 0x0A, 0xD4, 0x0A, 0xD4, 0x0A, 0xD4, 0x0A, 0x80, 0xF6, 0x10, 0x8A, 0xC6, 0xE6, 0x34, 0xBF, 0x00, 0x02, 0xB0, 0x0F, 0xE6, 0x30, 0xE4, 0x34, 0xD0, 0xC8, 0x73, 0xFA, 0xE4, 0x30, 0x24, 0x98, 0x75, 0xDA, 0xB0, 0x01, 0xE6, 0x32, 0x8A, 0xC6, 0x0C, 0x80, 0xE6, 0x34, 0xB2, 0x33, 0xB0, 0x8C, 0xE6, 0x30, 0xEB, 0x02, 0xEC, 0xAA, 0xE4, 0x34, 0xD0, 0xC8, 0x73, 0xF8, 0xE4, 0x30, 0x24, 0x9C, 0x75, 0xBA, 0xC7, 0x06, 0xB2, 0x01, 0x00, 0x00, 0xC7, 0x06, 0xB4, 0x01, 0x00, 0x02, 0x5F, 0xE9, 0x82, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x80, 0xFF, 0x0D, 0x00, 0x68, 0x00, 0xA0, 0x01, 0x40, 0x03, 0x78, 0x04, 0xFF }; static int32 scp300f_mem(const int32 Addr, const int32 write, const int32 data) { /* DBG_PRINT(("SCP300F: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ if(write) { if(scp300f_info->rom_enabled) { TRACE_PRINT(ROM_MSG, ("SCP300F: " ADDRESS_FORMAT " WR ROM[0x%05x]: Cannot write to ROM." NLP, PCX, Addr)); } else { } return 0; } else { if(scp300f_info->rom_enabled) { return scp300f_rom[Addr & SCP300F_ADDR_MASK]; } else { return scp300f_ram[Addr & SCP300F_ADDR_MASK]; } } } static int32 scp300fdev(const int32 port, const int32 io, const int32 data) { /* DBG_PRINT(("SCP300F: IO %s, Port %02x\n", io ? "WR" : "RD", port)); */ if(io) { SCP300F_Write(port, data); return 0; } else { return(SCP300F_Read(port)); } } #define SCP300F_MPIC_0 0x00 /* Master PIC */ #define SCP300F_MPIC_1 0x01 /* Master PIC */ #define SCP300F_SPIC_0 0x02 /* Slave PIC */ #define SCP300F_SPIC_1 0x03 /* Slave PIC */ #define SCP300F_9513_DATA 0x04 /* 9513 counter/timer Data Port */ #define SCP300F_9513_STATUS 0x05 /* 9513 counter/timer Status/Control Port */ #define SCP300F_UART_DATA 0x06 /* UART Data Register */ #define SCP300F_UART_STATUS 0x07 /* UART Status */ #define SCP300F_PIO_DATA 0x0C /* PIO Data */ #define SCP300F_PIO_STATUS 0x0D /* PIO Status */ #define SCP300F_EPROM_DIS 0x0E /* EPROM Disable */ #define SCP300F_SENSE_SW 0x0F /* Sense Switch */ extern int32 sio0d(const int32 port, const int32 io, const int32 data); extern int32 sio0s(const int32 port, const int32 io, const int32 data); static uint8 SCP300F_Read(const uint32 Addr) { uint8 cData = 0xFF; switch(Addr & SCP300F_IO_MASK) { case SCP300F_MPIC_0: case SCP300F_MPIC_1: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Master 8259 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_SPIC_0: case SCP300F_SPIC_1: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Slave 8259 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_9513_DATA: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_9513_STATUS: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 STAT RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_UART_DATA: /* UART is handled by the 2SIO, if this gets called, then the 2SIO was not */ case SCP300F_UART_STATUS: /* configured properly. */ TRACE_PRINT(VERBOSE_MSG, ("SCP300F: " ADDRESS_FORMAT " RD[%02x]: UART not configured properly." NLP, PCX, Addr)); break; case SCP300F_PIO_DATA: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO DATA RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_PIO_STATUS: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO STATUS RD[%02x]: not implemented." NLP, PCX, Addr)); break; case SCP300F_EPROM_DIS: TRACE_PRINT(ROM_MSG, ("SCP300F: " ADDRESS_FORMAT " EPROM DIS RD: EPROM Disabled." NLP, PCX)); scp300f_info->rom_enabled = 0; break; case SCP300F_SENSE_SW: /* Sense Switch */ cData = scp300f_sr; TRACE_PRINT(VERBOSE_MSG, ("SCP300F: " ADDRESS_FORMAT " RD: Sense Switch=0x%02x" NLP, PCX, cData)); break; default: TRACE_PRINT(VERBOSE_MSG, ("SCP300F: " ADDRESS_FORMAT " RD[%02x]: not Implemented." NLP, PCX, Addr)); break; } return (cData); } static uint8 SCP300F_Write(const uint32 Addr, uint8 cData) { switch(Addr & SCP300F_IO_MASK) { case SCP300F_MPIC_0: case SCP300F_MPIC_1: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Master 8259 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); break; case SCP300F_SPIC_0: case SCP300F_SPIC_1: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Slave 8259 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); break; case SCP300F_9513_DATA: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); break; case SCP300F_9513_STATUS: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 STAT WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); break; case SCP300F_UART_DATA: /* UART is handled by the 2SIO, if this gets called, then the 2SIO was not */ case SCP300F_UART_STATUS: /* configured properly. */ TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: UART not configured properly." NLP, PCX, Addr)); break; case SCP300F_PIO_DATA: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); break; case SCP300F_PIO_STATUS: TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: Cannot write to PIO STATUS." NLP, PCX, Addr)); break; case SCP300F_EPROM_DIS: TRACE_PRINT(ROM_MSG, ("SCP300F: " ADDRESS_FORMAT " EPROM DIS WR: EPROM Disabled." NLP, PCX)); scp300f_info->rom_enabled = 0; break; case SCP300F_SENSE_SW: TRACE_PRINT(VERBOSE_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: Cannot write to SR." NLP, PCX, Addr)); break; default: TRACE_PRINT(VERBOSE_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[0x%02x]=0x%02x: not Implemented." NLP, PCX, Addr, cData)); break; } return(0); } simh-3.8.1/AltairZ80/s100_disk3.c0000644000175000017500000006455611051674724014332 0ustar vlmvlm/************************************************************************* * * * $Id: s100_disk3.c 1997 2008-07-18 05:29:52Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro DISK3 Hard Disk Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define IRQ_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define SPECIFY_MSG (1 << 8) #define DISK3_MAX_DRIVES 4 #define DISK3_CSR 0 /* R=DISK3 Status / W=DISK3 Control Register */ #define DISK3_DATA 1 /* R=Step Pulse / W=Write Data Register */ #define DISK3_OP_DRIVE 0x00 #define DISK3_OP_CYL 0x01 #define DISK3_OP_HEAD 0x02 #define DISK3_OP_SECTOR 0x03 #define DISK3_CMD_NULL 0x00 #define DISK3_CMD_READ_DATA 0x01 #define DISK3_CMD_WRITE_DATA 0x02 #define DISK3_CMD_WRITE_HEADER 0x03 #define DISK3_CMD_READ_HEADER 0x04 #define DISK3_STATUS_BUSY 0 #define DISK3_STATUS_RANGE 1 #define DISK3_STATUS_NOT_READY 2 #define DISK3_STATUS_TIMEOUT 3 #define DISK3_STATUS_DAT_CRC 4 #define DISK3_STATUS_WR_FAULT 5 #define DISK3_STATUS_OVERRUN 6 #define DISK3_STATUS_HDR_CRC 7 #define DISK3_STATUS_MAP_FULL 8 #define DISK3_STATUS_COMPLETE 0xFF /* Complete with No Error */ #define DISK3_CODE_NOOP 0x00 #define DISK3_CODE_VERSION 0x01 #define DISK3_CODE_GLOBAL 0x02 #define DISK3_CODE_SPECIFY 0x03 #define DISK3_CODE_SET_MAP 0x04 #define DISK3_CODE_HOME 0x05 #define DISK3_CODE_SEEK 0x06 #define DISK3_CODE_READ_HDR 0x07 #define DISK3_CODE_READWRITE 0x08 #define DISK3_CODE_RELOCATE 0x09 #define DISK3_CODE_FORMAT 0x0A #define DISK3_CODE_FORMAT_BAD 0x0B #define DISK3_CODE_STATUS 0x0C #define DISK3_CODE_SELECT 0x0D #define DISK3_CODE_EXAMINE 0x0E #define DISK3_CODE_MODIFY 0x0F #define DISK3_CMD_MASK 0x3F #define DISK3_REQUEST_IRQ 0x80 #define DISK3_IOPB_LEN 16 #define DISK3_IOPB_CMD 0 #define DISK3_IOPB_STATUS 1 #define DISK3_IOPB_DRIVE 2 #define DISK3_IOPB_ARG1 3 #define DISK3_IOPB_ARG2 4 #define DISK3_IOPB_ARG3 5 #define DISK3_IOPB_ARG4 6 #define DISK3_IOPB_ARG5 7 #define DISK3_IOPB_ARG6 8 #define DISK3_IOPB_ARG7 9 #define DISK3_IOPB_DATA 10 #define DISK3_IOPB_LINK 13 #define DISK3_MODE_ABS 0xFF #define DISK3_MODE_LOGICAL 0x00 typedef struct { UNIT *uptr; DISK_INFO *imd; uint16 sectsize; /* sector size, not including pre/postamble */ uint16 nsectors; /* number of sectors/track */ uint16 nheads; /* number of heads */ uint16 ntracks; /* number of tracks */ uint16 res_tracks; /* Number of reserved tracks on drive. */ uint16 track; /* Current Track */ uint16 cur_sect; /* current starting sector of transfer */ uint16 cur_track; /* Current Track */ uint16 xfr_nsects; /* Number of sectors to transfer */ uint8 ready; /* Is drive ready? */ } DISK3_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 sel_drive; /* Currently selected drive */ uint8 mode; /* mode (0xFF=absolute, 0x00=logical) */ uint8 retries; /* Number of retries to attempt */ uint8 ndrives; /* Number of drives attached to the controller */ uint32 link_addr; /* Link Address for next IOPB */ uint32 dma_addr; /* DMA Address for the current IOPB */ DISK3_DRIVE_INFO drive[DISK3_MAX_DRIVES]; uint8 iopb[16]; } DISK3_INFO; static DISK3_INFO disk3_info_data = { { 0x0, 0, 0x90, 2 } }; static DISK3_INFO *disk3_info = &disk3_info_data; /* Disk geometries: * ST506 ST412 CMI5619 Q520 Q540 Q2080 * Sectsize: 1024 1024 1024 1024 1024 1024 * Sectors: 9 9 9 9 9 11 * Heads: 4 4 6 4 8 7 * Tracks: 153 306 306 512 512 1172 */ /* Default geometry for a 20MB hard disk. */ #define C20MB_SECTSIZE 1024 #define C20MB_NSECTORS 9 #define C20MB_NHEADS 4 #define C20MB_NTRACKS 512 static int32 ntracks = C20MB_NTRACKS; static int32 nheads = C20MB_NHEADS; static int32 nsectors = C20MB_NSECTORS; static int32 sectsize = C20MB_SECTSIZE; extern uint32 PCX; extern REG *sim_PC; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern int32 find_unit_index(UNIT *uptr); extern void raise_ss1_interrupt(uint8 intnum); /* These are needed for DMA. */ extern void PutByteDMA(const uint32 Addr, const uint32 Value); extern uint8 GetByteDMA(const uint32 Addr); #define UNIT_V_DISK3_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_DISK3_WLK (1 << UNIT_V_DISK3_WLK) #define UNIT_V_DISK3_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_DISK3_VERBOSE (1 << UNIT_V_DISK3_VERBOSE) #define DISK3_CAPACITY (C20MB_NTRACKS*C20MB_NHEADS*C20MB_NSECTORS*C20MB_SECTSIZE) /* Default Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ static t_stat disk3_reset(DEVICE *disk3_dev); static t_stat disk3_attach(UNIT *uptr, char *cptr); static t_stat disk3_detach(UNIT *uptr); static void raise_disk3_interrupt(void); static int32 disk3dev(const int32 port, const int32 io, const int32 data); /* static uint8 DISK3_Read(const uint32 Addr); */ static uint8 DISK3_Write(const uint32 Addr, uint8 cData); static UNIT disk3_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK3_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK3_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK3_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK3_CAPACITY) } }; static REG disk3_reg[] = { { DRDATA (NTRACKS, ntracks, 10), }, { DRDATA (NHEADS, nheads, 8), }, { DRDATA (NSECTORS, nsectors, 8), }, { DRDATA (SECTSIZE, sectsize, 11), }, { HRDATA (SEL_DRIVE, disk3_info_data.sel_drive, 3), }, { HRDATA (MODE, disk3_info_data.mode, 8), }, { HRDATA (RETRIES, disk3_info_data.retries, 8), }, { HRDATA (NDRIVES, disk3_info_data.ndrives, 8), }, { HRDATA (LINK_ADDR, disk3_info_data.link_addr, 32), }, { HRDATA (DMA_ADDR, disk3_info_data.dma_addr, 32), }, { BRDATA (IOPB, &disk3_info_data.iopb[DISK3_IOPB_CMD], 16, 8, 16), }, { NULL } }; static MTAB disk3_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_DISK3_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_DISK3_WLK, UNIT_DISK3_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_DISK3_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_DISK3_VERBOSE, UNIT_DISK3_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(disk3_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB disk3_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "IRQ", IRQ_MSG }, { "VERBOSE",VERBOSE_MSG }, { "SPECIFY",SPECIFY_MSG }, { NULL, 0 } }; DEVICE disk3_dev = { "DISK3", disk3_unit, disk3_reg, disk3_mod, DISK3_MAX_DRIVES, 10, 31, 1, DISK3_MAX_DRIVES, DISK3_MAX_DRIVES, NULL, NULL, &disk3_reset, NULL, &disk3_attach, &disk3_detach, &disk3_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, disk3_dt, NULL, "Compupro ST-506 Disk Controller DISK3" }; /* Reset routine */ static t_stat disk3_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk3dev, TRUE); } else { /* Connect DISK3 at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk3dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } disk3_info->link_addr = 0x50; /* After RESET, the link pointer is at 0x50. */ return SCPE_OK; } /* Attach routine */ static t_stat disk3_attach(UNIT *uptr, char *cptr) { t_stat r = SCPE_OK; DISK3_DRIVE_INFO *pDrive; char header[4]; unsigned int i = 0; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } pDrive = &disk3_info->drive[i]; pDrive->ready = 1; pDrive->track = 5; pDrive->ntracks = C20MB_NTRACKS; pDrive->nheads = C20MB_NHEADS; pDrive->nsectors = C20MB_NSECTORS; pDrive->sectsize = C20MB_SECTSIZE; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ if(sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize); } pDrive->uptr = uptr; /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "IMD")) { uptr->u3 = IMAGE_TYPE_IMD; } else if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; disk3_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_DISK3_VERBOSE) printf("DISK3%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if(uptr->capac < 318000) { printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); disk3_detach(uptr); return SCPE_OPENERR; } if (uptr->flags & UNIT_DISK3_VERBOSE) printf("--------------------------------------------------------\n"); disk3_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_DISK3_VERBOSE)); if (uptr->flags & UNIT_DISK3_VERBOSE) printf("\n"); } else { disk3_info->drive[i].imd = NULL; } return SCPE_OK; } /* Detach routine */ t_stat disk3_detach(UNIT *uptr) { DISK3_DRIVE_INFO *pDrive; t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } pDrive = &disk3_info->drive[i]; pDrive->ready = 0; if (uptr->flags & UNIT_DISK3_VERBOSE) printf("Detach DISK3%d\n", i); r = detach_unit(uptr); /* detach unit */ if ( r != SCPE_OK) return r; return SCPE_OK; } static int32 disk3dev(const int32 port, const int32 io, const int32 data) { TRACE_PRINT(VERBOSE_MSG, ("DISK3: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); if(io) { DISK3_Write(port, data); return 0; } else { return(0xFF); } } static uint8 DISK3_Write(const uint32 Addr, uint8 cData) { uint32 next_link; uint8 result = DISK3_STATUS_COMPLETE; uint8 i; uint8 cmd; DISK3_DRIVE_INFO *pDrive; for(i = 0; i < DISK3_IOPB_LEN; i++) { disk3_info->iopb[i] = GetByteDMA(disk3_info->link_addr + i); } cmd = disk3_info->iopb[DISK3_IOPB_CMD]; disk3_info->sel_drive = disk3_info->iopb[DISK3_IOPB_DRIVE] & 0x03; disk3_info->dma_addr = disk3_info->iopb[0x0A]; disk3_info->dma_addr |= disk3_info->iopb[0x0B] << 8; disk3_info->dma_addr |= disk3_info->iopb[0x0C] << 16; next_link = disk3_info->iopb[DISK3_IOPB_LINK+0]; next_link |= disk3_info->iopb[DISK3_IOPB_LINK+1] << 8; next_link |= disk3_info->iopb[DISK3_IOPB_LINK+2] << 16; TRACE_PRINT(VERBOSE_MSG, ("DISK3[%d]: LINK=0x%05x, NEXT=0x%05x, CMD=%x, %s DMA@0x%05x\n", disk3_info->sel_drive, disk3_info->link_addr, next_link, disk3_info->iopb[DISK3_IOPB_CMD] & DISK3_CMD_MASK, (disk3_info->iopb[DISK3_IOPB_CMD] & DISK3_REQUEST_IRQ) ? "IRQ" : "POLL", disk3_info->dma_addr)); pDrive = &disk3_info->drive[disk3_info->sel_drive]; if(pDrive->ready) { /* Perform command */ switch(cmd & DISK3_CMD_MASK) { case DISK3_CODE_NOOP: TRACE_PRINT(VERBOSE_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " NOOP" NLP, disk3_info->sel_drive, PCX)); break; case DISK3_CODE_VERSION: break; case DISK3_CODE_GLOBAL: TRACE_PRINT(CMD_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " GLOBAL" NLP, disk3_info->sel_drive, PCX)); disk3_info->mode = disk3_info->iopb[DISK3_IOPB_ARG1]; disk3_info->retries = disk3_info->iopb[DISK3_IOPB_ARG2]; disk3_info->ndrives = disk3_info->iopb[DISK3_IOPB_ARG3]; TRACE_PRINT(SPECIFY_MSG, (" Mode: 0x%02x" NLP, disk3_info->mode)); TRACE_PRINT(SPECIFY_MSG, (" # Retries: 0x%02x" NLP, disk3_info->retries)); TRACE_PRINT(SPECIFY_MSG, (" # Drives: 0x%02x" NLP, disk3_info->ndrives)); if(disk3_info->mode == DISK3_MODE_ABS) { TRACE_PRINT(ERROR_MSG, ("DISK3: Absolute addressing not supported." NLP)); } break; case DISK3_CODE_SPECIFY: { uint8 specify_data[22]; TRACE_PRINT(CMD_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " SPECIFY" NLP, disk3_info->sel_drive, PCX)); for(i = 0; i < 22; i++) { specify_data[i] = GetByteDMA(disk3_info->dma_addr + i); } pDrive->sectsize = specify_data[4] | (specify_data[5] << 8); pDrive->nsectors = specify_data[6] | (specify_data[7] << 8); pDrive->nheads = specify_data[8] | (specify_data[9] << 8); pDrive->ntracks = specify_data[10] | (specify_data[11] << 8); pDrive->res_tracks = specify_data[18] | (specify_data[19] << 8); TRACE_PRINT(SPECIFY_MSG, (" Sectsize: %d" NLP, pDrive->sectsize)); TRACE_PRINT(SPECIFY_MSG, (" Sectors: %d" NLP, pDrive->nsectors)); TRACE_PRINT(SPECIFY_MSG, (" Heads: %d" NLP, pDrive->nheads)); TRACE_PRINT(SPECIFY_MSG, (" Tracks: %d" NLP, pDrive->ntracks)); TRACE_PRINT(SPECIFY_MSG, (" Reserved: %d" NLP, pDrive->res_tracks)); break; } case DISK3_CODE_HOME: pDrive->track = 0; TRACE_PRINT(SEEK_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " HOME" NLP, disk3_info->sel_drive, PCX)); break; case DISK3_CODE_SEEK: pDrive->track = disk3_info->iopb[DISK3_IOPB_ARG1]; pDrive->track |= (disk3_info->iopb[DISK3_IOPB_ARG2] << 8); if(pDrive->track > pDrive->ntracks) { TRACE_PRINT(ERROR_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " SEEK ERROR %d not found" NLP, disk3_info->sel_drive, PCX, pDrive->track)); pDrive->track = pDrive->ntracks - 1; result = DISK3_STATUS_TIMEOUT; } else { TRACE_PRINT(SEEK_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " SEEK %d" NLP, disk3_info->sel_drive, PCX, pDrive->track)); } break; case DISK3_CODE_READ_HDR: { TRACE_PRINT(CMD_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " READ HEADER: %d" NLP, pDrive->track, PCX, pDrive->track >> 8)); PutByteDMA(disk3_info->dma_addr + 0, pDrive->track & 0xFF); PutByteDMA(disk3_info->dma_addr + 1, (pDrive->track >> 8) & 0xFF); PutByteDMA(disk3_info->dma_addr + 2, 0); PutByteDMA(disk3_info->dma_addr + 3, 1); break; } case DISK3_CODE_READWRITE: { uint32 track_len; uint32 xfr_len; uint32 file_offset; uint32 xfr_count = 0; uint8 *dataBuffer; if(disk3_info->mode == DISK3_MODE_ABS) { TRACE_PRINT(ERROR_MSG, ("DISK3: Absolute addressing not supported." NLP)); break; } pDrive->cur_sect = disk3_info->iopb[DISK3_IOPB_ARG2] | (disk3_info->iopb[DISK3_IOPB_ARG3] << 8); pDrive->cur_track = disk3_info->iopb[DISK3_IOPB_ARG4] | (disk3_info->iopb[DISK3_IOPB_ARG5] << 8); pDrive->xfr_nsects = disk3_info->iopb[DISK3_IOPB_ARG6] | (disk3_info->iopb[DISK3_IOPB_ARG7] << 8); track_len = pDrive->nsectors * pDrive->sectsize; file_offset = (pDrive->cur_track * track_len); /* Calculate offset based on current track */ file_offset += pDrive->cur_sect * pDrive->sectsize; xfr_len = pDrive->xfr_nsects * pDrive->sectsize; dataBuffer = malloc(xfr_len); sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET); if(disk3_info->iopb[DISK3_IOPB_ARG1] == 1) { /* Read */ TRACE_PRINT(RD_DATA_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " READ @0x%05x T:%04d/S:%04d/#:%d" NLP, disk3_info->sel_drive, PCX, disk3_info->dma_addr, pDrive->cur_track, pDrive->cur_sect, pDrive->xfr_nsects )); fread(dataBuffer, xfr_len, 1, (pDrive->uptr)->fileref); /* Perform DMA Transfer */ for(xfr_count = 0;xfr_count < xfr_len; xfr_count++) { PutByteDMA(disk3_info->dma_addr + xfr_count, dataBuffer[xfr_count]); } } else { /* Write */ TRACE_PRINT(WR_DATA_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " WRITE @0x%05x T:%04d/S:%04d/#:%d" NLP, disk3_info->sel_drive, PCX, disk3_info->dma_addr, pDrive->cur_track, pDrive->cur_sect, pDrive->xfr_nsects )); /* Perform DMA Transfer */ for(xfr_count = 0;xfr_count < xfr_len; xfr_count++) { dataBuffer[xfr_count] = GetByteDMA(disk3_info->dma_addr + xfr_count); } fwrite(dataBuffer, xfr_len, 1, (pDrive->uptr)->fileref); } free(dataBuffer); /* Update Track/Sector in IOPB */ pDrive->cur_sect += pDrive->xfr_nsects; if(pDrive->cur_sect >= pDrive->nsectors) { pDrive->cur_sect = pDrive->cur_sect % pDrive->nsectors; pDrive->cur_track++; } disk3_info->iopb[DISK3_IOPB_ARG2] = pDrive->cur_sect & 0xFF; disk3_info->iopb[DISK3_IOPB_ARG3] = (pDrive->cur_sect >> 8) & 0xFF; disk3_info->iopb[DISK3_IOPB_ARG4] = pDrive->cur_track & 0xFF; disk3_info->iopb[DISK3_IOPB_ARG5] = (pDrive->cur_track >> 8) & 0xFF; disk3_info->iopb[DISK3_IOPB_ARG6] = 0; disk3_info->iopb[DISK3_IOPB_ARG7] = 0; /* Update the DATA field in the IOPB */ disk3_info->dma_addr += xfr_len; disk3_info->iopb[DISK3_IOPB_DATA+0] = disk3_info->dma_addr & 0xFF; disk3_info->iopb[DISK3_IOPB_DATA+1] = (disk3_info->dma_addr >> 8) & 0xFF; disk3_info->iopb[DISK3_IOPB_DATA+2] = (disk3_info->dma_addr >> 16) & 0xFF; break; } case DISK3_CODE_FORMAT: { uint32 data_len; uint32 file_offset; uint8 *fmtBuffer; data_len = pDrive->nsectors * pDrive->sectsize; TRACE_PRINT(WR_DATA_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " FORMAT T:%d/H:%d/Fill=0x%02x/Len=%d" NLP, disk3_info->sel_drive, PCX, pDrive->track, disk3_info->iopb[DISK3_IOPB_ARG3], disk3_info->iopb[DISK3_IOPB_ARG2], data_len )); file_offset = (pDrive->track * (pDrive->nheads) * data_len); /* Calculate offset based on current track */ file_offset += (disk3_info->iopb[DISK3_IOPB_ARG3] * data_len); fmtBuffer = malloc(data_len); memset(fmtBuffer, disk3_info->iopb[DISK3_IOPB_ARG2], data_len); sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET); fwrite(fmtBuffer, data_len, 1, (pDrive->uptr)->fileref); free(fmtBuffer); break; } case DISK3_CODE_SET_MAP: break; case DISK3_CODE_RELOCATE: case DISK3_CODE_FORMAT_BAD: case DISK3_CODE_STATUS: case DISK3_CODE_SELECT: case DISK3_CODE_EXAMINE: case DISK3_CODE_MODIFY: default: TRACE_PRINT(ERROR_MSG, ("DISK3[%d]: " ADDRESS_FORMAT " CMD=%x Unsupported" NLP, disk3_info->sel_drive, PCX, cmd & DISK3_CMD_MASK)); break; } } else { /* Drive not ready */ result = DISK3_STATUS_NOT_READY; } /* Return status */ disk3_info->iopb[DISK3_IOPB_STATUS] = result; /* Update IOPB in host memory */ for(i = 0; i < DISK3_IOPB_LEN; i++) { PutByteDMA(disk3_info->link_addr + i, disk3_info->iopb[i]); } if(cmd & DISK3_REQUEST_IRQ) { raise_disk3_interrupt(); } disk3_info->link_addr = next_link; return 0; } #define SS1_VI1_INT 1 /* DISK2/DISK3 interrupts tied to VI1 */ static void raise_disk3_interrupt(void) { TRACE_PRINT(IRQ_MSG, ("DISK3: " ADDRESS_FORMAT " Interrupt" NLP, PCX)); raise_ss1_interrupt(SS1_VI1_INT); } simh-3.8.1/AltairZ80/sim_imd.h0000644000175000017500000001431411050420000014132 0ustar vlmvlm/************************************************************************* * * * $Id: sim_imd.h 1987 2008-07-08 03:25:57Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * ImageDisk Disk Image File access module for SIMH, definitions. * * See: http://www.classiccmp.org/dunfield/img/index.htm * * for details on the ImageDisk format and other utilities. * * * * Environment: * * User mode only * * * *************************************************************************/ typedef struct { uint8 mode; uint8 cyl; uint8 head; uint8 nsects; uint8 sectsize; } IMD_HEADER; #define IMD_FLAG_SECT_HEAD_MAP (1 << 6) #define IMD_FLAG_SECT_CYL_MAP (1 << 7) #define SECT_RECORD_UNAVAILABLE 0 /* Data could not be read from the original media */ #define SECT_RECORD_NORM 1 /* Normal Data */ #define SECT_RECORD_NORM_COMP 2 /* Compressed Normal Data */ #define SECT_RECORD_NORM_DAM 3 /* Normal Data with deleted address mark */ #define SECT_RECORD_NORM_DAM_COMP 4 /* Compressed Normal Data with deleted address mark */ #define SECT_RECORD_NORM_ERR 5 /* Normal Data */ #define SECT_RECORD_NORM_COMP_ERR 6 /* Compressed Normal Data */ #define SECT_RECORD_NORM_DAM_ERR 7 /* Normal Data with deleted address mark */ #define SECT_RECORD_NORM_DAM_COMP_ERR 8 /* Compressed Normal Data with deleted address mark */ #define MAX_CYL 80 #define MAX_HEAD 2 #define MAX_SPT 26 #define FD_FLAG_WRITELOCK 1 #define IMD_DISK_IO_ERROR_GENERAL (1 << 0) /* General data error. */ #define IMD_DISK_IO_ERROR_CRC (1 << 1) /* Data read/written, but got a CRC error. */ #define IMD_DISK_IO_DELETED_ADDR_MARK (1 << 2) /* Sector had a deleted address mark */ #define IMD_DISK_IO_COMPRESSED (1 << 3) /* Sector is compressed in the IMD file (Read Only) */ #define IMD_DISK_IO_ERROR_WPROT (1 << 4) /* Disk is write protected */ #define IMD_MODE_500K_FM 0 #define IMD_MODE_300K_FM 1 #define IMD_MODE_250K_FM 2 #define IMD_MODE_500K_MFM 3 #define IMD_MODE_300K_MFM 4 #define IMD_MODE_250K_MFM 5 #define IMD_MODE_FM(x) (x <= IMD_MODE_250K_FM) #define IMD_MODE_MFM(x) (x >= IMD_MODE_500K_MFM) typedef struct { uint8 mode; uint8 nsects; uint32 sectsize; uint32 sectorOffsetMap[MAX_SPT]; uint8 start_sector; uint8 logicalHead[MAX_SPT]; uint8 logicalCyl[MAX_SPT]; } TRACK_INFO; typedef struct { FILE *file; uint32 ntracks; uint8 nsides; uint8 flags; TRACK_INFO track[MAX_CYL][MAX_HEAD]; } DISK_INFO; extern DISK_INFO *diskOpen(FILE *fileref, uint32 isVerbose); extern t_stat diskClose(DISK_INFO **myDisk); extern t_stat diskCreate(FILE *fileref, char *ctlr_comment); extern uint32 imdGetSides(DISK_INFO *myDisk); extern uint32 imdIsWriteLocked(DISK_INFO *myDisk); extern t_stat sectSeek(DISK_INFO *myDisk, uint32 Cyl, uint32 Head); extern t_stat sectRead(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *readlen); extern t_stat sectWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *writelen); extern t_stat trackWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 numSectors, uint32 sectorLen, uint8 *sectorMap, uint8 mode, uint8 fillbyte, uint32 *flags); simh-3.8.1/AltairZ80/mfdc.c0000644000175000017500000006655311111141266013445 0ustar vlmvlm/************************************************************************* * * * $Id: mfdc.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Micropolis FD Control module for SIMH. * * See the "Vector Using MDOS Revision 8.4" manual at: * * www.hartetechnologies.com/manuals in the Vector Graphic section * * for details of the on-disk sector format and programming information. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #define USE_VGI /* Use 275-byte VGI-format sectors (includes all metadata) */ #include "altairz80_defs.h" #include "sim_imd.h" #if defined (_WIN32) #include #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define ORDERS_MSG (1 << 7) extern uint32 PCX; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern int32 find_unit_index(UNIT *uptr); static void MFDC_Command(uint8 cData); #define MFDC_MAX_DRIVES 4 #define JUMPER_W9 1 /* Not Installed (0) = 2MHz, Installed (1) = 4MHz. */ #define JUMPER_W10 0 #define MFDC_SECTOR_LEN 275 typedef union { struct { uint8 sync; uint8 header[2]; uint8 unused[10]; uint8 data[256]; uint8 checksum; uint8 ecc[4]; uint8 ecc_valid; /* Not used for Micropolis FDC, but is used by FDHD. */ } u; uint8 raw[MFDC_SECTOR_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; DISK_INFO *imd; uint8 track; uint8 wp; /* Disk write protected */ uint8 ready; /* Drive is ready */ uint8 sector; /* Current Sector number */ uint32 sector_wait_count; } MFDC_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 xfr_flag; /* Indicates controller is ready to send/receive data */ uint8 sel_drive; /* Currently selected drive */ uint8 selected; /* 1 if drive is selected */ uint8 track0; /* Set it selected drive is on track 0 */ uint8 head; /* Currently selected head */ uint8 wr_latch; /* Write enable latch */ uint8 int_enable; /* Interrupt Enable */ uint32 datacount; /* Number of data bytes transferred from controller for current sector */ uint8 read_in_progress; /* TRUE if a read is in progress */ MFDC_DRIVE_INFO drive[MFDC_MAX_DRIVES]; } MFDC_INFO; static MFDC_INFO mfdc_info_data = { { 0xF800, 1024, 0, 0 } }; static MFDC_INFO *mfdc_info = &mfdc_info_data; static SECTOR_FORMAT sdata; #define UNIT_V_MFDC_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_MFDC_WLK (1 << UNIT_V_MFDC_WLK) #define UNIT_V_MFDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_MFDC_VERBOSE (1 << UNIT_V_MFDC_VERBOSE) #define MFDC_CAPACITY (77*16*MFDC_SECTOR_LEN) /* Default Micropolis Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ static t_stat mfdc_reset(DEVICE *mfdc_dev); static t_stat mfdc_attach(UNIT *uptr, char *cptr); static t_stat mfdc_detach(UNIT *uptr); static uint8 MFDC_Read(const uint32 Addr); static uint8 MFDC_Write(const uint32 Addr, uint8 cData); static int32 mdskdev(const int32 Addr, const int32 rw, const int32 data); static UNIT mfdc_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) } }; static REG mfdc_reg[] = { { NULL } }; static MTAB mfdc_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { UNIT_MFDC_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_MFDC_WLK, UNIT_MFDC_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_MFDC_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_MFDC_VERBOSE, UNIT_MFDC_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(mfdc_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB mfdc_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "ORDERS", ORDERS_MSG }, { NULL, 0 } }; DEVICE mfdc_dev = { "MDSK", mfdc_unit, mfdc_reg, mfdc_mod, MFDC_MAX_DRIVES, 10, 31, 1, MFDC_MAX_DRIVES, MFDC_MAX_DRIVES, NULL, NULL, &mfdc_reset, NULL, &mfdc_attach, &mfdc_detach, &mfdc_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, mfdc_dt, NULL, "Micropolis FD Control MDSK" }; /* Micropolis FD Control Boot ROM * This ROM code is runtime-relocatable. See Appendix F of the "Vector Using MDOS Revision 8.4" * manual at www.hartetechnologies.com/manuals in the Vector Graphic section. */ static uint8 mfdc_rom[256] = { 0xF3, 0x21, 0xA2, 0x00, 0xF9, 0x36, 0xC9, 0xCD, 0xA2, 0x00, 0xEB, 0x2A, 0xA0, 0x00, 0x2E, 0x00, /* 0x00 */ 0xE5, 0x01, 0x1D, 0x00, 0x09, 0xE5, 0xE1, 0x0E, 0x1A, 0x09, 0x06, 0xBD, 0xEB, 0x3B, 0x3B, 0x1A, /* 0x10 */ 0x77, 0xBE, 0xC0, 0x23, 0x13, 0x05, 0xC0, 0xE1, 0x2A, 0xA0, 0x00, 0x11, 0x00, 0x02, 0x19, 0x22, /* 0x20 */ 0xA2, 0x00, 0x36, 0xA0, 0xC3, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xA2, 0x00, 0x7E, 0xE6, /* 0x30 */ 0x80, 0xCA, 0xA9, 0x00, 0x7E, 0xE6, 0x0F, 0xA8, 0xC2, 0xA9, 0x00, 0x23, 0xB6, 0xF2, 0xB7, 0x00, /* 0x40 */ 0x23, 0x7E, 0xAF, 0xEB, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x77, 0x23, 0x88, 0x47, 0x1A, 0x77, 0x23, /* 0x50 */ 0x88, 0x47, 0x0D, 0xC2, 0xC3, 0x00, 0x1A, 0xB8, 0xC9, 0x2A, 0xA2, 0x00, 0x36, 0x20, 0x23, 0x7E, /* 0x60 */ 0x2B, 0xE6, 0x24, 0xEE, 0x20, 0xC2, 0xD4, 0x00, 0x0E, 0x5E, 0xCD, 0x49, 0x01, 0x23, 0x7E, 0x2B, /* 0x70 */ 0xE6, 0x24, 0xEE, 0x20, 0xC2, 0xD4, 0x00, 0x23, 0x7E, 0xE6, 0x08, 0x2B, 0xCA, 0x07, 0x01, 0x06, /* 0x80 */ 0x08, 0x36, 0x61, 0x0E, 0x0F, 0xCD, 0x49, 0x01, 0x05, 0xC2, 0xFC, 0x00, 0x23, 0x7E, 0xE6, 0x08, /* 0x90 */ 0x2B, 0xC2, 0x19, 0x01, 0x36, 0x60, 0x0E, 0x0F, 0xCD, 0x49, 0x01, 0xC3, 0x07, 0x01, 0x21, 0x5F, /* 0xA0 */ 0x01, 0xCD, 0x37, 0x01, 0xC2, 0xD4, 0x00, 0x2A, 0x69, 0x02, 0x22, 0xA4, 0x00, 0xCD, 0x37, 0x01, /* 0xB0 */ 0xC2, 0xD4, 0x00, 0x2A, 0xA4, 0x00, 0x11, 0x0C, 0x00, 0x19, 0xD1, 0xE9, 0xE5, 0xEB, 0x01, 0x86, /* 0xC0 */ 0x00, 0xCD, 0xA6, 0x00, 0xE1, 0xC2, 0x37, 0x01, 0xE5, 0x7E, 0x23, 0xB6, 0xE1, 0xC9, 0x7E, 0xE6, /* 0xD0 */ 0x20, 0x79, 0xC2, 0x51, 0x01, 0x07, 0x4F, 0x3E, 0xFF, 0xD6, 0x01, 0xB7, 0xC2, 0x54, 0x01, 0x0D, /* 0xE0 */ 0xC2, 0x52, 0x01, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xA6, 0x00 /* 0xF0 */ }; /* Reset routine */ t_stat mfdc_reset(DEVICE *dptr) { uint8 i; PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdskdev, TRUE); } else { /* Connect MFDC at base address */ for(i = 0; i < MFDC_MAX_DRIVES; i++) { mfdc_info->drive[i].uptr = &mfdc_dev.units[i]; } if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdskdev, FALSE) != 0) { printf("%s: error mapping resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); dptr->flags |= DEV_DIS; return SCPE_ARG; } } return SCPE_OK; } /* Attach routine */ t_stat mfdc_attach(UNIT *uptr, char *cptr) { char header[4]; t_stat r; unsigned int i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ if(sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = MFDC_CAPACITY; } i = find_unit_index(uptr); /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "IMD")) { uptr->u3 = IMAGE_TYPE_IMD; } else if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; mfdc_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_MFDC_VERBOSE) printf("MDSK%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if(uptr->capac < 318000) { printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); mfdc_detach(uptr); return SCPE_OPENERR; } if (uptr->flags & UNIT_MFDC_VERBOSE) printf("--------------------------------------------------------\n"); mfdc_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_MFDC_VERBOSE)); if (uptr->flags & UNIT_MFDC_VERBOSE) printf("\n"); } else { mfdc_info->drive[i].imd = NULL; } return SCPE_OK; } /* Detach routine */ t_stat mfdc_detach(UNIT *uptr) { t_stat r; int8 i; for(i = 0; i < MFDC_MAX_DRIVES; i++) { if(mfdc_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= MFDC_MAX_DRIVES) return SCPE_ARG; DBG_PRINT(("Detach MFDC%d\n", i)); r = diskClose(&mfdc_info->drive[i].imd); if (r != SCPE_OK) return r; r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) return r; return SCPE_OK; } static uint8 cy; static uint8 adc(uint8 sum, uint8 a1) { uint32 total; total = sum + a1 + cy; if(total > 0xFF) { cy = 1; } else { cy = 0; } return(total & 0xFF); } /* Main Entry Point for Memory-Mapped I/O to the Micropolis FD Control Board * * The controller is typically located at 0xF800 in the Memory Map, and occupies * 1K of address space. Accesses are broken down as follows: * * 0xF800-0xF8FF: Bootstrap ROM * 0xF900-0xF9FF: Nothing (reads 0xFF) * 0xFA00-0xFBFF: Controller registers: there are four registers, which are shadowed * throughout this 512-byte range. * * The controller can be relocated on any 1K boundary in the memory map, and since the * boot ROM code is runtime relocatable, it moves with the controller registers. */ static int32 mdskdev(const int32 Addr, const int32 rw, const int32 data) { switch(Addr & 0x300) { case 0x000: /* Boot ROM */ if(rw == 0) { /* Read boot ROM */ return(mfdc_rom[Addr & 0xFF]); } else { printf("MFDC: Attempt to write to boot ROM." NLP); return (-1); } break; case 0x100: /* Nothing */ return(0xFF); break; case 0x200: case 0x300: /* Controller Registers */ if(rw == 0) { /* Read Register */ return(MFDC_Read(Addr)); } else { /* Write Register */ return(MFDC_Write(Addr, data)); } break; } return(-1); } static uint8 MFDC_Read(const uint32 Addr) { uint8 cData; MFDC_DRIVE_INFO *pDrive; cData = 0x00; pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; switch(Addr & 0x3) { case 0: if(mfdc_info->read_in_progress == FALSE) { pDrive->sector_wait_count++; if(pDrive->sector_wait_count > 10) { pDrive->sector++; pDrive->sector &= 0x0F; /* Max of 16 sectors */ mfdc_info->wr_latch = 0; /* on new sector, disable the write latch */ DBG_PRINT(("Head over sector %d" NLP, pDrive->sector)); pDrive->sector_wait_count = 0; } } cData = (pDrive->sector) & 0xF; /* [3:0] current sector */ cData |= (JUMPER_W10 << 4); cData |= ((~JUMPER_W9) & 1) << 5; cData |= (0 << 6); /* Sector Interrupt Flag, reset by RESET command or Interrupt Disable */ cData |= (1 << 7); /* Sector Flag */ mfdc_info->xfr_flag = 1; /* Drive has data */ mfdc_info->datacount = 0; TRACE_PRINT(STATUS_MSG, ("MFDC: " ADDRESS_FORMAT " RD Sector Register = 0x%02x" NLP, PCX, cData)); break; case 1: cData = (mfdc_info->sel_drive & 0x3); /* [1:0] selected drive */ cData |= (!mfdc_info->selected << 2); /* [2] drive is selected */ cData |= (pDrive->track == 0) ? 0x08 : 0; /* [3] TK0 */ pDrive->wp = ((pDrive->uptr)->flags & UNIT_MFDC_WLK) ? 1 : 0; cData |= (pDrive->wp << 4); /* [4] Write Protect */ cData |= (pDrive->ready << 5); /* [5] Drive Ready */ cData |= (0 << 6); /* [6] PINTE from S-100 Bus */ cData |= (mfdc_info->xfr_flag << 7); /* [7] Transfer Flag */ TRACE_PRINT(STATUS_MSG, ("MFDC: " ADDRESS_FORMAT " RD Status = 0x%02x" NLP, PCX, cData)); break; case 2: case 3: if(mfdc_info->datacount == 0) { unsigned int i, checksum; unsigned long sec_offset; unsigned int flags; unsigned int readlen; /* Clear out unused portion of sector. */ memset(&sdata.u.unused[0], 0x00, 10); sdata.u.sync = 0xFF; sdata.u.header[0] = pDrive->track; sdata.u.header[1] = pDrive->sector; TRACE_PRINT(RD_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " RD Data T:%d S:[%d]" NLP, PCX, pDrive->track, pDrive->sector)); #ifdef USE_VGI sec_offset = (pDrive->track * MFDC_SECTOR_LEN * 16) + \ (pDrive->sector * MFDC_SECTOR_LEN); #else sec_offset = (pDrive->track * 4096) + \ (pDrive->sector * 256); #endif /* USE_VGI */ if (!(pDrive->uptr->flags & UNIT_ATT)) { if (pDrive->uptr->flags & UNIT_MFDC_VERBOSE) printf("MFDC: " ADDRESS_FORMAT " MDSK%i not attached." NLP, PCX, mfdc_info->sel_drive); return 0x00; } switch((pDrive->uptr)->u3) { case IMAGE_TYPE_IMD: if(pDrive->imd == NULL) { printf(".imd is NULL!" NLP); } /* printf("%s: Read: imd=%p" NLP, __FUNCTION__, pDrive->imd); */ sectRead(pDrive->imd, pDrive->track, mfdc_info->head, pDrive->sector, sdata.u.data, 256, &flags, &readlen); break; case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); #ifdef USE_VGI fread(sdata.raw, MFDC_SECTOR_LEN, 1, (pDrive->uptr)->fileref); #else fread(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); #endif /* USE_VGI */ } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } /* printf("%d/%d @%04x Len=%04x" NLP, sdata.u.header[0], sdata.u.header[1], sdata.u.header[9]<<8|sdata.u.header[8], sdata.u.header[11]<<8|sdata.u.header[10]); */ adc(0,0); /* clear Carry bit */ checksum = 0; /* Checksum everything except the sync byte */ for(i=1;i<269;i++) { checksum = adc(checksum, sdata.raw[i]); } sdata.u.checksum = checksum & 0xFF; /* DBG_PRINT(("Checksum=%x" NLP, sdata.u.checksum)); */ mfdc_info->read_in_progress = TRUE; } cData = sdata.raw[mfdc_info->datacount]; mfdc_info->datacount++; if(mfdc_info->datacount == 270) { TRACE_PRINT(RD_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " Read sector [%d] complete" NLP, PCX, pDrive->sector)); mfdc_info->read_in_progress = FALSE; } /* DBG_PRINT(("MFDC: " ADDRESS_FORMAT " RD Data Sector %d[%03d]: 0x%02x" NLP, PCX, pDrive->sector, mfdc_info->datacount, cData)); */ break; } return (cData); } static uint8 MFDC_Write(const uint32 Addr, uint8 cData) { unsigned int sec_offset; unsigned int flags = 0; unsigned int writelen; MFDC_DRIVE_INFO *pDrive; pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; switch(Addr & 0x3) { case 0: case 1: MFDC_Command(cData); break; case 2: case 3: /* DBG_PRINT(("MFDC: " ADDRESS_FORMAT " WR Data" NLP, PCX)); */ if(mfdc_info->wr_latch == 0) { printf("MFDC: " ADDRESS_FORMAT " Error, attempt to write data when write latch is not set." NLP, PCX); } else { #ifdef USE_VGI sec_offset = (pDrive->track * MFDC_SECTOR_LEN * 16) + \ (pDrive->sector * MFDC_SECTOR_LEN); sdata.raw[mfdc_info->datacount] = cData; #else int data_index = mfdc_info->datacount - 13; sec_offset = (pDrive->track * 4096) + \ (pDrive->sector * 256); if((data_index >= 0) && (data_index < 256)) { DBG_PRINT(("writing data [%03d]=%02x" NLP, data_index, cData)); sdata.u.data[data_index] = cData; } #endif /* USE_VGI */ mfdc_info->datacount ++; if(mfdc_info->datacount == 270) { TRACE_PRINT(WR_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " WR Data T:%d S:[%d]" NLP, PCX, pDrive->track, pDrive->sector)); if (!(pDrive->uptr->flags & UNIT_ATT)) { if (pDrive->uptr->flags & UNIT_MFDC_VERBOSE) printf("MFDC: " ADDRESS_FORMAT " MDSK%i not attached." NLP, PCX, mfdc_info->sel_drive); return 0x00; } switch((pDrive->uptr)->u3) { case IMAGE_TYPE_IMD: if(pDrive->imd == NULL) { printf(".imd is NULL!" NLP); } sectWrite(pDrive->imd, pDrive->track, mfdc_info->head, pDrive->sector, sdata.u.data, 256, &flags, &writelen); break; case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); #ifdef USE_VGI fwrite(sdata.raw, MFDC_SECTOR_LEN, 1, (pDrive->uptr)->fileref); #else fwrite(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); #endif /* USE_VGI */ } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } } } break; } cData = 0x00; return (cData); } #define MFDC_CMD_NOP 0 #define MFDC_CMD_SELECT 1 #define MFDC_CMD_INTR 2 #define MFDC_CMD_STEP 3 #define MFDC_CMD_SET_WRITE 4 #define MFDC_CMD_RESET 5 static void MFDC_Command(uint8 cData) { uint8 cCommand; uint8 cModifier; MFDC_DRIVE_INFO *pDrive; pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; cCommand = cData >> 5; cModifier = cData & 0x1F; switch(cCommand) { case MFDC_CMD_NOP: TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " No Op." NLP, PCX)); break; case MFDC_CMD_SELECT: mfdc_info->sel_drive = cModifier & 0x03; mfdc_info->head = (cModifier & 0x10) >> 4; mfdc_info->selected = TRUE; if(pDrive->uptr->fileref != NULL) { pDrive->ready = 1; } else { pDrive->ready = 0; } TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Select Drive: %d, Head: %s" NLP, PCX, mfdc_info->sel_drive, (mfdc_info->head) ? "Upper" : "Lower")); break; case MFDC_CMD_INTR: mfdc_info->int_enable = cModifier & 1; /* 0=int disable, 1=enable */ TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Interrupts %s." NLP, PCX, mfdc_info->int_enable ? "Enabled" : "Disabled")); break; case MFDC_CMD_STEP: if(cModifier & 1) { /* Step IN */ pDrive->track++; } else { /* Step OUT */ if(pDrive->track != 0) { pDrive->track--; } } TRACE_PRINT(SEEK_MSG, ("MFDC: " ADDRESS_FORMAT " Step %s, Track=%d." NLP, PCX, (cModifier & 1) ? "IN" : "OUT", pDrive->track)); break; case MFDC_CMD_SET_WRITE: TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Set WRITE." NLP, PCX)); mfdc_info->wr_latch = 1; /* Allow writes for the current sector */ mfdc_info->datacount = 0; /* reset the byte counter */ break; case MFDC_CMD_RESET: TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Reset Controller." NLP, PCX)); mfdc_info->selected = 0; /* de-select the drive */ mfdc_info->wr_latch = 0; /* Disable the write latch */ mfdc_info->datacount = 0; /* reset the byte counter */ break; default: TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Unsupported command." NLP, PCX)); break; } } simh-3.8.1/AltairZ80/n8vem.c0000644000175000017500000004632511111141264013562 0ustar vlmvlm/************************************************************************* * * * $Id: n8vem.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * N8VEM Single-Board Computer I/O module for SIMH. * * http://groups.google.com/group/n8vem/web/n8vem-single-board-computer-home-page * * * * Environment: * * User mode only * * * *************************************************************************/ /* #define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define PIO_MSG (1 << 1) #define UART_MSG (1 << 2) #define RTC_MSG (1 << 3) #define MPCL_MSG (1 << 4) #define ROM_MSG (1 << 5) #define VERBOSE_MSG (1 << 7) #define N8VEM_MAX_DRIVES 2 typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 *ram; uint8 *rom; uint8 rom_attached; uint8 uart_scr; uint8 uart_lcr; uint8 mpcl_ram; uint8 mpcl_rom; } N8VEM_INFO; static N8VEM_INFO n8vem_info_data = { { 0x0, 0x8000, 0x60, 32 } }; static N8VEM_INFO *n8vem_info = &n8vem_info_data; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint32 PCX; extern REG *sim_PC; extern int32 find_unit_index (UNIT *uptr); static t_stat n8vem_reset(DEVICE *n8vem_dev); static t_stat n8vem_boot(int32 unitno, DEVICE *dptr); static t_stat n8vem_attach(UNIT *uptr, char *cptr); static t_stat n8vem_detach(UNIT *uptr); static uint8 N8VEM_Read(const uint32 Addr); static uint8 N8VEM_Write(const uint32 Addr, uint8 cData); static int32 n8vemdev(const int32 port, const int32 io, const int32 data); static int32 n8vem_mem(const int32 port, const int32 io, const int32 data); static int32 save_rom = 0x00; /* When set to 1, saves ROM back to file on disk at detach time */ static int32 save_ram = 0x00; /* When set to 1, saves RAM back to file on disk at detach time */ static int32 n8vem_pio1a = 0x00; /* 8255 PIO1A IN Port */ static int32 n8vem_pio1b = 0x00; /* 8255 PIO1B OUT Port */ static int32 n8vem_pio1c = 0x00; /* 8255 PIO1C IN Port */ static int32 n8vem_pio1ctrl = 0x00; /* 8255 PIO1 Control Port */ #define N8VEM_ROM_SIZE (1024 * 1024) #define N8VEM_RAM_SIZE (512 * 1024) #define N8VEM_RAM_SELECT (1 << 7) #define N8VEM_RAM_MASK 0x0F #define N8VEM_ROM_MASK 0x1F #define N8VEM_ADDR_MASK 0x7FFF static UNIT n8vem_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, N8VEM_ROM_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, N8VEM_RAM_SIZE) } }; static REG n8vem_reg[] = { { HRDATA (SAVEROM, save_rom, 1), }, { HRDATA (SAVERAM, save_ram, 1), }, { HRDATA (PIO1A, n8vem_pio1a, 8), }, { HRDATA (PIO1B, n8vem_pio1b, 8), }, { HRDATA (PIO1C, n8vem_pio1c, 8), }, { HRDATA (PIO1CTRL, n8vem_pio1ctrl, 8), }, { NULL } }; static MTAB n8vem_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(n8vem_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB n8vem_dt[] = { { "ERROR", ERROR_MSG }, { "PIO", PIO_MSG }, { "UART", UART_MSG }, { "RTC", RTC_MSG }, { "ROM", ROM_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE n8vem_dev = { "N8VEM", n8vem_unit, n8vem_reg, n8vem_mod, N8VEM_MAX_DRIVES, 10, 31, 1, N8VEM_MAX_DRIVES, N8VEM_MAX_DRIVES, NULL, NULL, &n8vem_reset, &n8vem_boot, &n8vem_attach, &n8vem_detach, &n8vem_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, n8vem_dt, NULL, "Single-Board Computer N8VEM" }; /* Reset routine */ static t_stat n8vem_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Reset." NLP)); if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &n8vemdev, TRUE); sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &n8vem_mem, TRUE); free(n8vem_info->ram); free(n8vem_info->rom); } else { /* Connect N8VEM at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &n8vemdev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } /* Connect N8VEM Memory (512K RAM, 1MB FLASH) */ if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &n8vem_mem, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); return SCPE_ARG; } n8vem_info->ram = calloc(1, (N8VEM_RAM_SIZE)); n8vem_info->rom = calloc(1, (N8VEM_ROM_SIZE)); /* Clear the RAM and ROM mapping registers */ n8vem_info->mpcl_ram = 0; n8vem_info->mpcl_rom = 0; } return SCPE_OK; } static t_stat n8vem_boot(int32 unitno, DEVICE *dptr) { TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Boot." NLP)); /* Clear the RAM and ROM mapping registers */ n8vem_info->mpcl_ram = 0; n8vem_info->mpcl_rom = 0; /* Set the PC to 0, and go. */ *((int32 *) sim_PC->loc) = 0; return SCPE_OK; } /* Attach routine */ static t_stat n8vem_attach(UNIT *uptr, char *cptr) { t_stat r; int32 i = 0; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ uptr->capac = sim_fsize(uptr->fileref); TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Attach %s." NLP, i == 0 ? "ROM" : "RAM")); if(i == 0) { /* Attaching ROM */ n8vem_info->rom_attached = TRUE; /* Erase ROM */ memset(n8vem_info->rom, 0xFF, N8VEM_ROM_SIZE); if(uptr->capac > 0) { /* Only read in enough of the file to fill the ROM. */ if (uptr->capac > N8VEM_ROM_SIZE) uptr->capac = N8VEM_ROM_SIZE; TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Reading %d bytes into ROM.\n", uptr->capac)); fread((void *)(n8vem_info->rom), uptr->capac, 1, uptr->fileref); } } else { /* attaching RAM */ /* Erase RAM */ memset(n8vem_info->ram, 0x00, N8VEM_RAM_SIZE); if(uptr->capac > 0) { /* Only read in enough of the file to fill the RAM. */ if(uptr->capac > N8VEM_RAM_SIZE) uptr->capac = N8VEM_RAM_SIZE; TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Reading %d bytes into RAM.\n", uptr->capac)); fread((void *)(n8vem_info->ram), uptr->capac, 1, uptr->fileref); } } return r; } /* Detach routine */ static t_stat n8vem_detach(UNIT *uptr) { t_stat r; int32 i = 0; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Detach %s." NLP, i == 0 ? "ROM" : "RAM")); /* rewind to the beginning of the file. */ sim_fseek(uptr->fileref, 0, SEEK_SET); if(i == 0) { /* ROM */ /* Save the ROM back to disk if SAVEROM is set. */ if(save_rom == 1) { TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Writing %d bytes into ROM image.\n", N8VEM_ROM_SIZE)); fwrite((void *)(n8vem_info->rom), N8VEM_ROM_SIZE, 1, uptr->fileref); } } else { /* RAM */ /* Save the RAM back to disk if SAVERAM is set. */ if(save_ram == 1) { TRACE_PRINT(VERBOSE_MSG, ("N8VEM: Writing %d bytes into RAM image.\n", N8VEM_RAM_SIZE)); fwrite((void *)(n8vem_info->ram), N8VEM_RAM_SIZE, 1, uptr->fileref); } } r = detach_unit(uptr); /* detach unit */ return r; } /* RAM MEMORY PAGE CONFIGURATION LATCH CONTROL PORT ( IO_Y3 ) INFORMATION * * 7 6 5 4 3 2 1 0 ONLY APPLICABLE TO THE LOWER MEMORY PAGE $0000-$7FFF * ^ ^ ^ ^ ^ ^ ^ ^ * : : : : : : : :--0 = A15 RAM ADDRESS LINE DEFAULT IS 0 * : : : : : : :----0 = A16 RAM ADDRESS LINE DEFAULT IS 0 * : : : : : :------0 = A17 RAM ADDRESS LINE DEFAULT IS 0 * : : : : :--------0 = A18 RAM ADDRESS LINE DEFAULT IS 0 * : : : :-----------0 = * : : :-------------0 = * : :---------------0 = * :-----------------0 = * * ROM MEMORY PAGE CONFIGURATION LATCH CONTROL PORT ( IO_Y3+$04 ) INFORMATION * * 7 6 5 4 3 2 1 0 ONLY APPLICABLE TO THE LOWER MEMORY PAGE $0000-$7FFF * ^ ^ ^ ^ ^ ^ ^ ^ * : : : : : : : :--0 = A15 ROM ADDRESS LINE DEFAULT IS 0 * : : : : : : :----0 = A16 ROM ADDRESS LINE DEFAULT IS 0 * : : : : : :------0 = A17 ROM ADDRESS LINE DEFAULT IS 0 * : : : : :--------0 = A18 ROM ADDRESS LINE DEFAULT IS 0 * : : : :-----------0 = A19 ROM ONLY ADDRESS LINE DEFAULT IS 0 * : : :-------------0 = * : :---------------0 = * :-----------------0 = ROM SELECT (0=ROM, 1=RAM) DEFAULT IS 0 */ static int32 n8vem_mem(const int32 Addr, const int32 write, const int32 data) { /* DBG_PRINT(("N8VEM: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ if(write) { if(n8vem_info->mpcl_rom & N8VEM_RAM_SELECT) { n8vem_info->ram[((n8vem_info->mpcl_ram & N8VEM_RAM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)] = data; } else { if(save_rom == 1) { n8vem_info->rom[((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)] = data; } else { TRACE_PRINT(ROM_MSG, ("N8VEM: " ADDRESS_FORMAT " WR ROM[0x%05x]: Cannot write to ROM." NLP, PCX, ((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK))); } } return 0; } else { if(n8vem_info->mpcl_rom & N8VEM_RAM_SELECT) { return n8vem_info->ram[((n8vem_info->mpcl_ram & N8VEM_RAM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)]; } else { return n8vem_info->rom[((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)]; } } } static int32 n8vemdev(const int32 port, const int32 io, const int32 data) { /* DBG_PRINT(("N8VEM: IO %s, Port %02x\n", io ? "WR" : "RD", port)); */ if(io) { N8VEM_Write(port, data); return 0; } else { return(N8VEM_Read(port)); } } #define N8VEM_PIO1A 0x00 /* (INPUT) IN 1-8 */ #define N8VEM_PIO1B 0x01 /* (OUTPUT) OUT TO LEDS */ #define N8VEM_PIO1C 0x02 /* (INPUT) */ #define N8VEM_PIO1CONT 0x03 /* CONTROL BYTE PIO 82C55 */ #define N8VEM_UART_DATA 0x08 #define N8VEM_UART_RSR 0x09 #define N8VEM_UART_INTR 0x0A #define N8VEM_UART_LCR 0x0B #define N8VEM_UART_MCR 0x0C #define N8VEM_UART_LSR 0x0D #define N8VEM_UART_MSR 0x0E #define N8VEM_UART_SCR 0x0F #define N8VEM_MPCL_RAM 0x18 /* RAM Address control port */ #define N8VEM_MPCL_RAM1 0x19 /* RAM Address control port */ #define N8VEM_MPCL_RAM2 0x1A /* RAM Address control port */ #define N8VEM_MPCL_RAM3 0x1B /* RAM Address control port */ #define N8VEM_MPCL_ROM 0x1C /* ROM Address control port */ #define N8VEM_MPCL_ROM1 0x1D /* ROM Address control port */ #define N8VEM_MPCL_ROM2 0x1E /* ROM Address control port */ #define N8VEM_MPCL_ROM3 0x1F /* ROM Address control port */ extern int32 sio0d(const int32 port, const int32 io, const int32 data); extern int32 sio0s(const int32 port, const int32 io, const int32 data); static uint8 N8VEM_Read(const uint32 Addr) { uint8 cData = 0xFF; switch(Addr & 0x1F) { case N8VEM_PIO1A: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1A" NLP, PCX)); cData = n8vem_pio1a; break; case N8VEM_PIO1B: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1B" NLP, PCX)); cData = n8vem_pio1b; break; case N8VEM_PIO1C: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1C" NLP, PCX)); cData = n8vem_pio1c; break; case N8VEM_PIO1CONT: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1CTRL" NLP, PCX)); cData = n8vem_pio1ctrl; break; case N8VEM_UART_LCR: cData = n8vem_info->uart_lcr; break; case N8VEM_UART_DATA: case N8VEM_UART_RSR: case N8VEM_UART_LSR: case N8VEM_UART_INTR: case N8VEM_UART_MCR: case N8VEM_UART_MSR: TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " RD[%02x]: UART not Implemented." NLP, PCX, Addr)); break; case N8VEM_UART_SCR: /* 16550 Scratchpad, implemented so software can detect UART is present */ cData = n8vem_info->uart_scr; break; case N8VEM_MPCL_RAM: case N8VEM_MPCL_RAM1: case N8VEM_MPCL_RAM2: case N8VEM_MPCL_RAM3: TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: MPCL_RAM not Implemented." NLP, PCX)); break; case N8VEM_MPCL_ROM: case N8VEM_MPCL_ROM1: case N8VEM_MPCL_ROM2: case N8VEM_MPCL_ROM3: TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: MPCL_ROM not Implemented." NLP, PCX)); break; default: TRACE_PRINT(VERBOSE_MSG, ("N8VEM: " ADDRESS_FORMAT " RD[%02x]: not Implemented." NLP, PCX, Addr)); break; } return (cData); } static uint8 N8VEM_Write(const uint32 Addr, uint8 cData) { switch(Addr & 0x1F) { case N8VEM_PIO1A: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1A=0x%02x" NLP, PCX, cData)); n8vem_pio1a = cData; break; case N8VEM_PIO1B: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1B=0x%02x" NLP, PCX, cData)); n8vem_pio1b = cData; break; case N8VEM_PIO1C: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1C=0x%02x" NLP, PCX, cData)); n8vem_pio1c = cData; break; case N8VEM_PIO1CONT: TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1_CTRL=0x%02x" NLP, PCX, cData)); n8vem_pio1ctrl = cData; break; case N8VEM_UART_LCR: TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: UART LCR=%02x." NLP, PCX, cData)); n8vem_info->uart_lcr = cData; break; case N8VEM_UART_DATA: case N8VEM_UART_RSR: case N8VEM_UART_INTR: case N8VEM_UART_MCR: case N8VEM_UART_LSR: case N8VEM_UART_MSR: TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " WR[%02x]: UART not Implemented." NLP, PCX, Addr)); break; case N8VEM_UART_SCR: /* 16550 Scratchpad, implemented so software can detect UART is present */ n8vem_info->uart_scr = cData; break; case N8VEM_MPCL_RAM: case N8VEM_MPCL_RAM1: case N8VEM_MPCL_RAM2: case N8VEM_MPCL_RAM3: TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: MPCL_RAM=0x%02x" NLP, PCX, cData)); n8vem_info->mpcl_ram = cData; break; case N8VEM_MPCL_ROM: case N8VEM_MPCL_ROM1: case N8VEM_MPCL_ROM2: case N8VEM_MPCL_ROM3: TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: MPCL_ROM=0x%02x" NLP, PCX, cData)); n8vem_info->mpcl_rom = cData; break; default: TRACE_PRINT(VERBOSE_MSG, ("N8VEM: " ADDRESS_FORMAT " WR[0x%02x]=0x%02x: not Implemented." NLP, PCX, Addr, cData)); break; } return(0); } simh-3.8.1/AltairZ80/wd179x.c0000644000175000017500000014300611111142444013563 0ustar vlmvlm/************************************************************************* * * * $Id: wd179x.c 1999 2008-07-22 04:25:28Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Generic WD179X Disk Controller module for SIMH. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" #include "wd179x.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif #define CROMFDC_SIM_100US 291 /* Number of "ticks" in 100uS, where does this come from? */ #define CROMFDC_8IN_ROT (167 * CROMFDC_SIM_100US) #define CROMFDC_5IN_ROT (200 * CROMFDC_SIM_100US) /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define FMT_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define WD179X_MAX_DRIVES 4 #define WD179X_SECTOR_LEN 8192 /* 2^(7 + WD179X_MAX_SEC_LEN) == WD179X_SECTOR_LEN */ #define WD179X_MAX_SEC_LEN 6 #define WD179X_MAX_SECTOR 26 #define CMD_PHASE 0 #define EXEC_PHASE 1 #define DATA_PHASE 2 /* Status Bits for Type I Commands */ #define WD179X_STAT_NOT_READY (1 << 7) #define WD179X_STAT_WPROT (1 << 6) #define WD179X_STAT_HLD (1 << 5) #define WD179X_STAT_SEEK_ERROR (1 << 4) #define WD179X_STAT_CRC_ERROR (1 << 3) #define WD179X_STAT_TRACK0 (1 << 2) #define WD179X_STAT_INDEX (1 << 1) #define WD179X_STAT_BUSY (1 << 0) /* Status Bits for Type II, III Commands */ /*#define WD179X_STAT_NOT_READY (1 << 7) */ /*#define WD179X_STAT_WPROT (1 << 6) */ #define WD179X_STAT_REC_TYPE (1 << 5) /* Also Write Fault */ #define WD179X_STAT_NOT_FOUND (1 << 4) /*#define WD179X_STAT_CRC_ERROR (1 << 3) */ #define WD179X_STAT_LOST_DATA (1 << 2) #define WD179X_STAT_DRQ (1 << 1) /*#define WD179X_STAT_BUSY (1 << 0) */ typedef union { uint8 raw[WD179X_SECTOR_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; DISK_INFO *imd; uint8 ntracks; /* number of tracks */ uint8 nheads; /* number of heads */ uint32 sectsize; /* sector size, not including pre/postamble */ uint8 track; /* Current Track */ uint8 ready; /* Is drive ready? */ } WD179X_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug-n-Play Information */ uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */ uint8 hld; /* WD179X Head Load Output */ uint8 drq; /* WD179X DMA Request Output */ uint8 ddens; /* WD179X Double-Density Input */ uint8 fdc_head; /* H Head Number */ uint8 sel_drive; /* Currently selected drive */ uint8 drivetype; /* 8 or 5 depending on disk type. */ uint8 fdc_status; /* WD179X Status Register */ uint8 verify; /* WD179X Type 1 command Verify flag */ uint8 fdc_data; /* WD179X Data Register */ uint8 fdc_read; /* TRUE when reading */ uint8 fdc_write; /* TRUE when writing */ uint8 fdc_write_track; /* TRUE when writing an entire track */ uint8 fdc_fmt_state; /* Format track statemachine state */ uint8 fdc_gap[4]; /* Gap I - Gap IV lengths */ uint8 fdc_fmt_sector_count; /* sector count for format track */ uint8 fdc_sectormap[WD179X_MAX_SECTOR]; /* Physical to logical sector map */ uint8 fdc_header_index; /* Index into header */ uint8 fdc_read_addr; /* TRUE when READ ADDRESS command is in progress */ uint8 fdc_multiple; /* TRUE for multi-sector read/write */ uint16 fdc_datacount; /* Read or Write data remaining transfer length */ uint16 fdc_dataindex; /* index of current byte in sector data */ uint8 index_pulse_wait; /* TRUE if waiting for interrupt on next index pulse. */ uint8 fdc_sector; /* R Record (Sector) */ uint8 fdc_sec_len; /* N Sector Length */ int8 step_dir; uint8 cmdtype; /* Type of current/former command */ WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES]; } WD179X_INFO; static SECTOR_FORMAT sdata; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); t_stat wd179x_svc (UNIT *uptr); /* These are needed for DMA. PIO Mode has not been implemented yet. */ extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); extern uint8 GetBYTEWrapper(const uint32 Addr); #define UNIT_V_WD179X_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_WD179X_WLK (1 << UNIT_V_WD179X_WLK) #define UNIT_V_WD179X_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_WD179X_VERBOSE (1 << UNIT_V_WD179X_VERBOSE) #define WD179X_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ #define WD179X_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ /* Write Track (format) Statemachine states */ #define FMT_GAP1 1 #define FMT_GAP2 2 #define FMT_GAP3 3 #define FMT_GAP4 4 #define FMT_HEADER 5 #define FMT_DATA 6 /* WD179X Commands */ #define WD179X_RESTORE 0x00 /* Type I */ #define WD179X_SEEK 0x10 /* Type I */ #define WD179X_STEP 0x20 /* Type I */ #define WD179X_STEP_U 0x30 /* Type I */ #define WD179X_STEP_IN 0x40 /* Type I */ #define WD179X_STEP_IN_U 0x50 /* Type I */ #define WD179X_STEP_OUT 0x60 /* Type I */ #define WD179X_STEP_OUT_U 0x70 /* Type I */ #define WD179X_READ_REC 0x80 /* Type II */ #define WD179X_READ_RECS 0x90 /* Type II */ #define WD179X_WRITE_REC 0xA0 /* Type II */ #define WD179X_WRITE_RECS 0xB0 /* Type II */ #define WD179X_READ_ADDR 0xC0 /* Type III */ #define WD179X_FORCE_INTR 0xD0 /* Type IV */ #define WD179X_READ_TRACK 0xE0 /* Type III */ #define WD179X_WRITE_TRACK 0xF0 /* Type III */ static int32 wd179xdev(const int32 port, const int32 io, const int32 data); static t_stat wd179x_reset(DEVICE *dptr); int32 find_unit_index (UNIT *uptr); uint8 floorlog2(unsigned int n); WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 } }; WD179X_INFO *wd179x_info = &wd179x_info_data; static UNIT wd179x_unit[] = { { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 } }; static MTAB wd179x_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_WD179X_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_WD179X_WLK, UNIT_WD179X_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_WD179X_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_WD179X_VERBOSE, UNIT_WD179X_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(wd179x_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB wd179x_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "FMT", FMT_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE wd179x_dev = { "WD179X", wd179x_unit, NULL, wd179x_mod, WD179X_MAX_DRIVES, 10, 31, 1, WD179X_MAX_DRIVES, WD179X_MAX_DRIVES, NULL, NULL, &wd179x_reset, NULL, &wd179x_attach, &wd179x_detach, &wd179x_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, wd179x_dt, NULL, "Western Digital FDC Core WD179X" }; /* Unit service routine */ /* Used to generate INDEX pulses in response to a FORCE_INTR command */ t_stat wd179x_svc (UNIT *uptr) { if(wd179x_info->index_pulse_wait == TRUE) { wd179x_info->index_pulse_wait = FALSE; wd179x_info->intrq = 1; } return SCPE_OK; } /* Reset routine */ static t_stat wd179x_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, TRUE); } else { /* Connect I/O Ports at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } extern int32 find_unit_index (UNIT *uptr); void wd179x_external_restore(void) { WD179X_DRIVE_INFO *pDrive; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { TRACE_PRINT(ERROR_MSG, ("WD179X: " ADDRESS_FORMAT " Illegal drive selected, cannot restore." NLP, PCX)) return; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { TRACE_PRINT(ERROR_MSG, ("WD179X: " ADDRESS_FORMAT " No drive selected, cannot restore." NLP, PCX)) return; } TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " External Restore drive to track 0" NLP, wd179x_info->sel_drive, PCX)) pDrive->track = 0; } /* Attach routine */ t_stat wd179x_attach(UNIT *uptr, char *cptr) { char header[4]; t_stat r; int32 i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ uptr->capac = sim_fsize(uptr->fileref); i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } DBG_PRINT(("Attach WD179X%d\n", i)); wd179x_info->drive[i].uptr = uptr; /* Default to drive not ready */ wd179x_info->drive[i].ready = 0; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(strncmp(header, "IMD", 3)) { printf("WD179X: Only IMD disk images are supported\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } } else { /* create a disk image file in IMD format. */ if (diskCreate(uptr->fileref, "$Id: wd179x.c 1999 2008-07-22 04:25:28Z hharte $") != SCPE_OK) { printf("WD179X: Failed to create IMD disk.\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } uptr->capac = sim_fsize(uptr->fileref); } uptr->u3 = IMAGE_TYPE_IMD; if (uptr->flags & UNIT_WD179X_VERBOSE) printf("WD179X%d: attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if (uptr->flags & UNIT_WD179X_VERBOSE) printf("--------------------------------------------------------\n"); wd179x_info->drive[i].imd = diskOpen(uptr->fileref, uptr->flags & UNIT_WD179X_VERBOSE); if (uptr->flags & UNIT_WD179X_VERBOSE) printf("\n"); if (wd179x_info->drive[i].imd == NULL) { printf("WD179X: IMD disk corrupt.\n"); wd179x_info->drive[i].uptr = NULL; return SCPE_OPENERR; } /* Write-protect the unit if IMD think's it's writelocked. */ if(imdIsWriteLocked(wd179x_info->drive[i].imd)) { uptr->flags |= UNIT_WD179X_WLK; } wd179x_info->drive[i].ready = 1; } else { wd179x_info->drive[i].imd = NULL; } wd179x_info->fdc_sec_len = 0; /* 128 byte sectors, fixme */ wd179x_info->sel_drive = 0; return SCPE_OK; } /* Detach routine */ t_stat wd179x_detach(UNIT *uptr) { t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return SCPE_IERR; } DBG_PRINT(("Detach WD179X%d\n", i)); r = diskClose(&wd179x_info->drive[i].imd); wd179x_info->drive[i].ready = 0; if (r != SCPE_OK) return r; r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) return r; return SCPE_OK; } static int32 wd179xdev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP, PCX, io ? "OUT" : " IN", port, data)); if(io) { WD179X_Write(port, data); return 0; } else { return(WD179X_Read(port)); } } uint8 floorlog2(unsigned int n) { /* Compute log2(n) */ uint8 r = 0; if (n >= 1<<16) { n >>=16; r += 16; } if (n >= 1<< 8) { n >>= 8; r += 8; } if (n >= 1<< 4) { n >>= 4; r += 4; } if (n >= 1<< 2) { n >>= 2; r += 2; } if (n >= 1<< 1) { r += 1; } return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */ } uint8 WD179X_Read(const uint32 Addr) { uint8 cData; WD179X_DRIVE_INFO *pDrive; unsigned int flags = 0; unsigned int readlen; int status; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } cData = 0x00; switch(Addr & 0x3) { case WD179X_STATUS: /* Fix up status based on Command Type */ if((wd179x_info->cmdtype == 1) || (wd179x_info->cmdtype == 4)) { wd179x_info->fdc_status ^= WD179X_STAT_INDEX; /* Generate Index pulses */ wd179x_info->fdc_status &= ~WD179X_STAT_TRACK0; wd179x_info->fdc_status |= (pDrive->track == 0) ? WD179X_STAT_TRACK0 : 0; } else if(wd179x_info->cmdtype == 4) { } else { wd179x_info->fdc_status &= ~WD179X_STAT_INDEX; /* Mask index pulses */ wd179x_info->fdc_status |= (wd179x_info->drq) ? WD179X_STAT_DRQ : 0; } cData = (pDrive->ready == 0) ? WD179X_STAT_NOT_READY : 0; cData |= wd179x_info->fdc_status; /* Status Register */ TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)) wd179x_info->intrq = 0; break; case WD179X_TRACK: cData = pDrive->track; TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " RD TRACK = 0x%02x" NLP, PCX, cData)) break; case WD179X_SECTOR: cData = wd179x_info->fdc_sector; TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " RD SECT = 0x%02x" NLP, PCX, cData)) break; case WD179X_DATA: cData = 0xFF; /* Return High-Z data */ if(wd179x_info->fdc_read == TRUE) { if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { cData = sdata.raw[wd179x_info->fdc_dataindex]; if(wd179x_info->fdc_read_addr == TRUE) { TRACE_PRINT(STATUS_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " READ_ADDR[%d] = 0x%02x" NLP, wd179x_info->sel_drive, PCX, wd179x_info->fdc_dataindex, cData)) } wd179x_info->fdc_dataindex++; if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { if(wd179x_info->fdc_multiple == FALSE) { wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } else { /* Compute Sector Size */ wd179x_info->fdc_sec_len = floorlog2( pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!" NLP, wd179x_info->sel_drive, PCX)); wd179x_info->fdc_sec_len = 0; return cData; } wd179x_info->fdc_sector ++; TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " MULTI_READ_REC, T:%d/S:%d/N:%d, %s, len=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, wd179x_info->ddens ? "DD" : "SD", 128 << wd179x_info->fdc_sec_len)); status = sectRead(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &readlen); if(status == SCPE_OK) { wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ wd179x_info->drq = 1; wd179x_info->intrq = 0; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = FALSE; } else { wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */ wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } } } } } break; } return (cData); } /* * Command processing happens in three stages: * 1. Flags and initial conditions are set up based on the Type of the command. * 2. The execution phase takes place. * 3. Status is updated based on the Type and outcome of the command execution. * * See the WD179x-02 Datasheet available on www.hartetechnologies.com/manuals/ * */ static uint8 Do1793Command(uint8 cCommand) { uint8 result = 0; WD179X_DRIVE_INFO *pDrive; unsigned int flags = 0; unsigned int readlen; int status; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { if(((cCommand & 0xF0) != WD179X_FORCE_INTR)) { /* && ((cCommand & 0xF0) != WD179X_RESTORE)) { */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " ERROR: Command 0x%02x ignored because controller is BUSY\n" NLP, wd179x_info->sel_drive, PCX, cCommand)); } return 0xFF; } wd179x_info->fdc_status &= ~WD179X_STAT_NOT_READY; /* Extract Type-specific command flags, and set initial conditions */ switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: case WD179X_SEEK: case WD179X_STEP: case WD179X_STEP_U: case WD179X_STEP_IN: case WD179X_STEP_IN_U: case WD179X_STEP_OUT: case WD179X_STEP_OUT_U: wd179x_info->cmdtype = 1; wd179x_info->fdc_status |= WD179X_STAT_BUSY; /* Set BUSY */ wd179x_info->fdc_status &= ~(WD179X_STAT_CRC_ERROR | WD179X_STAT_SEEK_ERROR | WD179X_STAT_DRQ); wd179x_info->intrq = 0; wd179x_info->hld = cCommand && 0x08; wd179x_info->verify = cCommand & 0x04; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: case WD179X_WRITE_REC: case WD179X_WRITE_RECS: wd179x_info->cmdtype = 2; wd179x_info->fdc_status = WD179X_STAT_BUSY; /* Set BUSY, clear all others */ wd179x_info->intrq = 0; wd179x_info->hld = 1; /* Load the head immediately, E Flag not checked. */ break; /* Type III Commands */ case WD179X_READ_ADDR: case WD179X_READ_TRACK: case WD179X_WRITE_TRACK: wd179x_info->cmdtype = 3; break; /* Type IV Commands */ case WD179X_FORCE_INTR: wd179x_info->cmdtype = 4; break; default: wd179x_info->cmdtype = 0; break; } switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=RESTORE %s" NLP, wd179x_info->sel_drive, PCX, wd179x_info->verify ? "[VERIFY]" : "")); pDrive->track = 0; wd179x_info->intrq = 1; break; case WD179X_SEEK: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=SEEK, track=%d, new=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_data)); pDrive->track = wd179x_info->fdc_data; break; case WD179X_STEP: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP" NLP, wd179x_info->sel_drive, PCX)); break; case WD179X_STEP_U: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_U dir=%d" NLP, wd179x_info->sel_drive, PCX, wd179x_info->step_dir)); if(wd179x_info->step_dir == 1) { if (pDrive->track < 255) pDrive->track++; } else if (wd179x_info->step_dir == -1) { if (pDrive->track > 0) pDrive->track--; } else { TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " ERROR: undefined direction for STEP" NLP, wd179x_info->sel_drive, PCX)); } break; case WD179X_STEP_IN: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN" NLP, wd179x_info->sel_drive, PCX)); break; case WD179X_STEP_IN_U: if (pDrive->track < 255) pDrive->track++; wd179x_info->step_dir = 1; TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN_U, Track=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track)); break; case WD179X_STEP_OUT: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT" NLP, wd179x_info->sel_drive, PCX)); break; case WD179X_STEP_OUT_U: TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT_U" NLP, wd179x_info->sel_drive, PCX)); if (pDrive->track > 0) pDrive->track--; wd179x_info->step_dir = -1; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: /* Compute Sector Size */ wd179x_info->fdc_sec_len = floorlog2( pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!" NLP, wd179x_info->sel_drive, PCX)); wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->intrq = 1; wd179x_info->drq = 0; wd179x_info->fdc_sec_len = 0; return 0xFF; } wd179x_info->fdc_multiple = (cCommand & 0x10) ? TRUE : FALSE; TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_REC, T:%d/S:%d/N:%d, %s, %s len=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, wd179x_info->fdc_multiple ? "Multiple" : "Single", wd179x_info->ddens ? "DD" : "SD", 128 << wd179x_info->fdc_sec_len)); if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) { wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->intrq = 1; wd179x_info->drq = 0; } else { status = sectRead(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &readlen); if(status == SCPE_OK) { wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = FALSE; } else { wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */ wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; wd179x_info->fdc_status &= ~WD179X_STAT_BUSY; wd179x_info->drq = 0; wd179x_info->intrq = 1; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; } } break; case WD179X_WRITE_RECS: TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Error: WRITE_RECS not implemented." NLP, wd179x_info->sel_drive, PCX)); break; case WD179X_WRITE_REC: /* Compute Sector Size */ wd179x_info->fdc_sec_len = floorlog2( pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!" NLP, wd179x_info->sel_drive, PCX)); wd179x_info->fdc_sec_len = 0; } TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_REC, T:%d/S:%d/N:%d, %s." NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, (cCommand & 0x10) ? "Multiple" : "Single")); wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = TRUE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data; break; /* Type III Commands */ case WD179X_READ_ADDR: TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_ADDR, T:%d/S:%d, %s" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->ddens ? "DD" : "SD")); /* For some reason 86-DOS tries to use this track, force it to 0. Need to investigate this more. */ if (pDrive->track == 0xFF) pDrive->track=0; /* Compute Sector Size */ wd179x_info->fdc_sec_len = floorlog2( pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!" NLP, wd179x_info->sel_drive, PCX)); wd179x_info->fdc_sec_len = 0; } if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) { wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->intrq = 1; } else { wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 6; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_read = TRUE; wd179x_info->fdc_read_addr = TRUE; sdata.raw[0] = pDrive->track; sdata.raw[1] = wd179x_info->fdc_head; sdata.raw[2] = wd179x_info->fdc_sector; sdata.raw[3] = wd179x_info->fdc_sec_len; sdata.raw[4] = 0xAA; /* CRC1 */ sdata.raw[5] = 0x55; /* CRC2 */ wd179x_info->fdc_sector = pDrive->track; wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ wd179x_info->intrq = 1; } break; case WD179X_READ_TRACK: TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_TRACK" NLP, wd179x_info->sel_drive, PCX)); TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Error: READ_TRACK not implemented." NLP, wd179x_info->sel_drive, PCX)); break; case WD179X_WRITE_TRACK: TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK" NLP, wd179x_info->sel_drive, PCX)); TRACE_PRINT(FMT_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK, T:%d/S:%d." NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head)); wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ wd179x_info->drq = 1; wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; wd179x_info->fdc_dataindex = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = TRUE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_read_addr = FALSE; wd179x_info->fdc_fmt_state = FMT_GAP1; /* TRUE when writing an entire track */ wd179x_info->fdc_fmt_sector_count = 0; break; /* Type IV Commands */ case WD179X_FORCE_INTR: TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=FORCE_INTR" NLP, wd179x_info->sel_drive, PCX)); if((cCommand & 0x0F) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */ wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_read = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_read_addr = FALSE; wd179x_info->fdc_datacount = 0; wd179x_info->fdc_dataindex = 0; } else { if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { /* Force Interrupt when command is pending */ } else { /* Command not pending, clear status */ wd179x_info->fdc_status = 0; } if(cCommand & 0x04) { wd179x_info->index_pulse_wait = TRUE; if(wd179x_info->sel_drive < WD179X_MAX_DRIVES) { sim_activate (wd179x_unit, ((wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks % 77) == 0) ? CROMFDC_8IN_ROT : CROMFDC_5IN_ROT); /* Generate INDEX pulse */ /* printf("Drive %d Num tracks=%d\n", wd179x_info->sel_drive, wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks); */ } } else { wd179x_info->intrq = 1; } wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ } break; default: TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " ERROR: Unknown command 0x%02x.\n" NLP, wd179x_info->sel_drive, PCX, cCommand)); break; } /* Post processing of Type-specific command */ switch(cCommand & 0xF0) { /* Type I Commands */ case WD179X_RESTORE: case WD179X_SEEK: case WD179X_STEP: case WD179X_STEP_U: case WD179X_STEP_IN: case WD179X_STEP_IN_U: case WD179X_STEP_OUT: case WD179X_STEP_OUT_U: if(wd179x_info->verify) { /* Verify the selected track/head is ok. */ TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Verify ", wd179x_info->sel_drive, PCX)); if(sectSeek(pDrive->imd, pDrive->track, wd179x_info->fdc_head) != SCPE_OK) { TRACE_PRINT(SEEK_MSG, ("FAILED" NLP)); wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; } else if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) { wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ TRACE_PRINT(SEEK_MSG, ("NOT FOUND" NLP)); } else { TRACE_PRINT(SEEK_MSG, ("Ok" NLP)); } } if(pDrive->track == 0) { wd179x_info->fdc_status |= WD179X_STAT_TRACK0; } else { wd179x_info->fdc_status &= ~(WD179X_STAT_TRACK0); } wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ wd179x_info->intrq = 1; break; /* Type II Commands */ case WD179X_READ_REC: case WD179X_READ_RECS: case WD179X_WRITE_REC: case WD179X_WRITE_RECS: /* Type III Commands */ case WD179X_READ_ADDR: case WD179X_READ_TRACK: case WD179X_WRITE_TRACK: /* Type IV Commands */ case WD179X_FORCE_INTR: default: break; } return result; } /* Maximum number of sectors per track for format */ uint8 max_sectors_per_track[2][7] = { /* 128, 256, 512, 1024, 2048, 4096, 8192 */ { 26, 15, 8, 4, 2, 1, 0 }, /* Single-density table */ { 26, 26, 15, 8, 4, 2, 1 } /* Double-density table */ }; uint8 WD179X_Write(const uint32 Addr, uint8 cData) { WD179X_DRIVE_INFO *pDrive; /* uint8 disk_read = 0; */ unsigned int flags = 0; unsigned int writelen; if(wd179x_info->sel_drive >= WD179X_MAX_DRIVES) { return 0xFF; } pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; if(pDrive->uptr == NULL) { return 0xFF; } switch(Addr & 0x3) { case WD179X_STATUS: TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " WR CMD = 0x%02x" NLP, PCX, cData)) wd179x_info->fdc_read = FALSE; wd179x_info->fdc_write = FALSE; wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_datacount = 0; wd179x_info->fdc_dataindex = 0; Do1793Command(cData); break; case WD179X_TRACK: TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " WR TRACK = 0x%02x" NLP, PCX, cData)) pDrive->track = cData; break; case WD179X_SECTOR: /* Sector Register */ TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " WR SECT = 0x%02x" NLP, PCX, cData)) wd179x_info->fdc_sector = cData; break; case WD179X_DATA: TRACE_PRINT(STATUS_MSG, ("WD179X: " ADDRESS_FORMAT " WR DATA = 0x%02x" NLP, PCX, cData)) if(wd179x_info->fdc_write == TRUE) { if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { sdata.raw[wd179x_info->fdc_dataindex] = cData; wd179x_info->fdc_dataindex++; if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->intrq = 1; TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Writing sector, T:%d/S:%d/N:%d, Len=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, 128 << wd179x_info->fdc_sec_len)); sectWrite(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, sdata.raw, 128 << wd179x_info->fdc_sec_len, &flags, &writelen); wd179x_info->fdc_write = FALSE; } } } if(wd179x_info->fdc_write_track == TRUE) { /* TRACE_PRINT(FMT_MSG, */ /* ("WD179X: " ADDRESS_FORMAT " FMT DATA[%d] = 0x%02x" NLP, PCX, wd179x_info->fdc_fmt_state, cData)); */ if(wd179x_info->fdc_fmt_state == FMT_GAP1) { if(cData != 0xFC) { wd179x_info->fdc_gap[0]++; } else { TRACE_PRINT(VERBOSE_MSG, ("WD179X: " ADDRESS_FORMAT " FMT GAP1 Length = %d" NLP, PCX, wd179x_info->fdc_gap[0])); wd179x_info->fdc_gap[1] = 0; wd179x_info->fdc_fmt_state = FMT_GAP2; } } else if(wd179x_info->fdc_fmt_state == FMT_GAP2) { if(cData != 0xFE) { wd179x_info->fdc_gap[1]++; } else { TRACE_PRINT(VERBOSE_MSG, ("WD179X: " ADDRESS_FORMAT " FMT GAP2 Length = %d" NLP, PCX, wd179x_info->fdc_gap[1])); wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_HEADER; wd179x_info->fdc_header_index = 0; } } else if(wd179x_info->fdc_fmt_state == FMT_HEADER) { if(wd179x_info->fdc_header_index == 5) { wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_GAP3; } else { TRACE_PRINT(VERBOSE_MSG, ("WD179X: " ADDRESS_FORMAT " HEADER[%d]=%02x" NLP, PCX, wd179x_info->fdc_header_index, cData)); switch(wd179x_info->fdc_header_index) { case 0: pDrive->track = cData; break; case 1: wd179x_info->fdc_head = cData; break; case 2: wd179x_info->fdc_sector = cData; break; case 3: if(cData != 0x00) { } break; case 4: if(cData != 0xF7) { } break; } wd179x_info->fdc_header_index++; } } else if(wd179x_info->fdc_fmt_state == FMT_GAP3) { if(cData != 0xFB) { wd179x_info->fdc_gap[2]++; } else { TRACE_PRINT(VERBOSE_MSG, ("WD179X: " ADDRESS_FORMAT " FMT GAP3 Length = %d" NLP, PCX, wd179x_info->fdc_gap[2])); wd179x_info->fdc_fmt_state = FMT_DATA; wd179x_info->fdc_dataindex = 0; } } else if(wd179x_info->fdc_fmt_state == FMT_DATA) { /* data bytes */ if(cData != 0xF7) { sdata.raw[wd179x_info->fdc_dataindex] = cData; wd179x_info->fdc_dataindex++; } else { wd179x_info->fdc_sec_len = floorlog2(wd179x_info->fdc_dataindex) - 7; if((wd179x_info->fdc_sec_len == 0xF8) || (wd179x_info->fdc_sec_len > WD179X_MAX_SEC_LEN)) { /* Error calculating N or N too large */ TRACE_PRINT(ERROR_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Invalid sector size!" NLP, wd179x_info->sel_drive, PCX)); wd179x_info->fdc_sec_len = 0; } if(wd179x_info->fdc_fmt_sector_count >= WD179X_MAX_SECTOR) { TRACE_PRINT(ERROR_MSG, ("WD179X: " ADDRESS_FORMAT " Illegal sector count" NLP, PCX)); wd179x_info->fdc_fmt_sector_count = 0; } wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count] = wd179x_info->fdc_sector; wd179x_info->fdc_fmt_sector_count++; TRACE_PRINT(VERBOSE_MSG, ("WD179X: " ADDRESS_FORMAT " FMT Data Length = %d" NLP, PCX, wd179x_info->fdc_dataindex)); TRACE_PRINT(FMT_MSG, ("WD179X: " ADDRESS_FORMAT " FORMAT T:%d/H:%d/N:%d=%d/L=%d[%d] Fill=0x%02x" NLP, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_fmt_sector_count, wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count], wd179x_info->fdc_dataindex, wd179x_info->fdc_sec_len, sdata.raw[0])); wd179x_info->fdc_gap[1] = 0; wd179x_info->fdc_fmt_state = FMT_GAP2; if(wd179x_info->fdc_fmt_sector_count == max_sectors_per_track[wd179x_info->ddens & 1][wd179x_info->fdc_sec_len]) { trackWrite(pDrive->imd, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_fmt_sector_count, wd179x_info->fdc_sec_len, wd179x_info->fdc_sectormap, wd179x_info->ddens ? 3 : 0, /* data mode */ sdata.raw[0], &flags); wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY | WD179X_STAT_LOST_DATA); /* Clear BUSY, LOST_DATA */ wd179x_info->drq = 0; wd179x_info->intrq = 1; /* Recalculate disk size */ pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref); } } } } wd179x_info->fdc_data = cData; break; } return 0; } simh-3.8.1/AltairZ80/mfdc.h0000644000175000017500000000611710766764762013473 0ustar vlmvlm/************************************************************************* * * * $Id: mfdc.h 1694 2007-12-14 05:23:11Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Micropolis FDC module for SIMH definitions * * * * Environment: * * User mode only * * * *************************************************************************/ extern uint8 MFDC_Read(const uint32 Addr); extern uint8 MFDC_Write(const uint32 Addr, uint8 cData); simh-3.8.1/AltairZ80/s100_hdc1001.c0000644000175000017500000006017411051674724014345 0ustar vlmvlm/************************************************************************* * * * $Id: s100_hdc1001.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Advanced Digital Corporation (ADC) HDC-1001 Hard Disk Controller * * module for SIMH. The HDC-1001 controller uses the standard IDE/ATA * * task-file, so this controller should be compatible with other con- * * trollers that use IDE, like the GIDE interface. * * * * Environment: * * User mode only * * * *************************************************************************/ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define VERBOSE_MSG (1 << 7) #define HDC1001_MAX_DRIVES 4 typedef struct { UNIT *uptr; DISK_INFO *imd; uint16 sectsize; /* sector size, not including pre/postamble */ uint16 nsectors; /* number of sectors/track */ uint16 nheads; /* number of heads */ uint16 ntracks; /* number of tracks */ uint16 res_tracks; /* Number of reserved tracks on drive. */ uint16 track; /* Current Track */ uint16 cur_sect; /* current starting sector of transfer */ uint16 cur_track; /* Current Track */ uint16 xfr_nsects; /* Number of sectors to transfer */ uint8 ready; /* Is drive ready? */ } HDC1001_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 sel_drive; /* Currently selected drive */ uint8 taskfile[8]; /* ATA Task File Registers */ uint8 mode; /* mode (0xFF=absolute, 0x00=logical) */ uint8 retries; /* Number of retries to attempt */ uint8 ndrives; /* Number of drives attached to the controller */ uint32 link_addr; /* Link Address for next IOPB */ uint32 dma_addr; /* DMA Address for the current IOPB */ HDC1001_DRIVE_INFO drive[HDC1001_MAX_DRIVES]; uint8 iopb[16]; } HDC1001_INFO; static HDC1001_INFO hdc1001_info_data = { { 0x0, 0, 0xC8, 8 } }; static HDC1001_INFO *hdc1001_info = &hdc1001_info_data; extern uint32 PCX; extern REG *sim_PC; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern int32 find_unit_index(UNIT *uptr); /* These are needed for DMA. */ extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); extern uint8 GetBYTEWrapper(const uint32 Addr); #define UNIT_V_HDC1001_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_HDC1001_WLK (1 << UNIT_V_HDC1001_WLK) #define UNIT_V_HDC1001_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_HDC1001_VERBOSE (1 << UNIT_V_HDC1001_VERBOSE) #define HDC1001_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ static t_stat hdc1001_reset(DEVICE *hdc1001_dev); static t_stat hdc1001_attach(UNIT *uptr, char *cptr); static t_stat hdc1001_detach(UNIT *uptr); static int32 hdc1001dev(const int32 port, const int32 io, const int32 data); static uint8 HDC1001_Read(const uint32 Addr); static uint8 HDC1001_Write(const uint32 Addr, uint8 cData); static UNIT hdc1001_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDC1001_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDC1001_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDC1001_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDC1001_CAPACITY) } }; static REG hdc1001_reg[] = { { NULL } }; static MTAB hdc1001_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_HDC1001_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_HDC1001_WLK, UNIT_HDC1001_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_HDC1001_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_HDC1001_VERBOSE, UNIT_HDC1001_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(hdc1001_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB hdc1001_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE hdc1001_dev = { "HDC1001", hdc1001_unit, hdc1001_reg, hdc1001_mod, HDC1001_MAX_DRIVES, 10, 31, 1, HDC1001_MAX_DRIVES, HDC1001_MAX_DRIVES, NULL, NULL, &hdc1001_reset, NULL, &hdc1001_attach, &hdc1001_detach, &hdc1001_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, hdc1001_dt, NULL, "ADC Hard Disk Controller HDC1001" }; /* Reset routine */ static t_stat hdc1001_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdc1001dev, TRUE); } else { /* Connect HDC1001 at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdc1001dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } hdc1001_info->link_addr = 0x50; /* After RESET, the link pointer is at 0x50. */ return SCPE_OK; } /* Attach routine */ static t_stat hdc1001_attach(UNIT *uptr, char *cptr) { t_stat r = SCPE_OK; HDC1001_DRIVE_INFO *pDrive; char header[4]; unsigned int i = 0; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } pDrive = &hdc1001_info->drive[i]; pDrive->ready = 1; pDrive->track = 5; pDrive->ntracks = 243; pDrive->nheads = 8; pDrive->nsectors = 11; pDrive->sectsize = 1024; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ if(sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize); } pDrive->uptr = uptr; /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "IMD")) { uptr->u3 = IMAGE_TYPE_IMD; } else if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; hdc1001_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_HDC1001_VERBOSE) printf("HDC1001%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if(uptr->capac < 318000) { printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); hdc1001_detach(uptr); return SCPE_OPENERR; } if (uptr->flags & UNIT_HDC1001_VERBOSE) printf("--------------------------------------------------------\n"); hdc1001_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_HDC1001_VERBOSE)); if (uptr->flags & UNIT_HDC1001_VERBOSE) printf("\n"); } else { hdc1001_info->drive[i].imd = NULL; } return SCPE_OK; } /* Detach routine */ t_stat hdc1001_detach(UNIT *uptr) { HDC1001_DRIVE_INFO *pDrive; t_stat r; int8 i; i = find_unit_index(uptr); if (i == -1) { return (SCPE_IERR); } pDrive = &hdc1001_info->drive[i]; pDrive->ready = 0; if (uptr->flags & UNIT_HDC1001_VERBOSE) printf("Detach HDC1001%d\n", i); r = detach_unit(uptr); /* detach unit */ if ( r != SCPE_OK) return r; return SCPE_OK; } static int32 hdc1001dev(const int32 port, const int32 io, const int32 data) { /* TRACE_PRINT(VERBOSE_MSG, ("HDC1001: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); */ if(io) { HDC1001_Write(port, data); return 0; } else { return(HDC1001_Read(port)); } } #define HDC1001_CSR 0 /* R=HDC1001 Status / W=HDC1001 Control Register */ #define HDC1001_DATA 1 /* R=Step Pulse / W=Write Data Register */ #define HDC1001_OP_DRIVE 0x00 #define HDC1001_OP_CYL 0x01 #define HDC1001_OP_HEAD 0x02 #define HDC1001_OP_SECTOR 0x03 #define HDC1001_CMD_NULL 0x00 #define HDC1001_CMD_READ_DATA 0x01 #define HDC1001_CMD_WRITE_DATA 0x02 #define HDC1001_CMD_WRITE_HEADER 0x03 #define HDC1001_CMD_READ_HEADER 0x04 #define HDC1001_STATUS_BUSY 0 #define HDC1001_STATUS_RANGE 1 #define HDC1001_STATUS_NOT_READY 2 #define HDC1001_STATUS_TIMEOUT 3 #define HDC1001_STATUS_DAT_CRC 4 #define HDC1001_STATUS_WR_FAULT 5 #define HDC1001_STATUS_OVERRUN 6 #define HDC1001_STATUS_HDR_CRC 7 #define HDC1001_STATUS_MAP_FULL 8 #define HDC1001_STATUS_COMPLETE 0xFF /* Complete with No Error */ #define HDC1001_CODE_NOOP 0x00 #define HDC1001_CODE_VERSION 0x01 #define HDC1001_CODE_GLOBAL 0x02 #define HDC1001_CODE_SPECIFY 0x03 #define HDC1001_CODE_SET_MAP 0x04 #define HDC1001_CODE_HOME 0x05 #define HDC1001_CODE_SEEK 0x06 #define HDC1001_CODE_READ_HDR 0x07 #define HDC1001_CODE_READWRITE 0x08 #define HDC1001_CODE_RELOCATE 0x09 #define HDC1001_CODE_FORMAT 0x0A #define HDC1001_CODE_FORMAT_BAD 0x0B #define HDC1001_CODE_STATUS 0x0C #define HDC1001_CODE_SELECT 0x0D #define HDC1001_CODE_EXAMINE 0x0E #define HDC1001_CODE_MODIFY 0x0F #define HDC1001_IOPB_LEN 16 #define TF_DATA 0 #define TF_ERROR 1 #define TF_SECNT 2 #define TF_SECNO 3 #define TF_CYLLO 4 #define TF_CYLHI 5 #define TF_SDH 6 #define TF_CMD 7 static uint8 HDC1001_Write(const uint32 Addr, uint8 cData) { /* uint8 result = HDC1001_STATUS_COMPLETE; */ HDC1001_DRIVE_INFO *pDrive; pDrive = &hdc1001_info->drive[hdc1001_info->sel_drive]; switch(Addr & 0x07) { case TF_SDH: hdc1001_info->sel_drive = (cData >> 3) & 0x03; pDrive = &hdc1001_info->drive[hdc1001_info->sel_drive]; case TF_DATA: case TF_ERROR: case TF_SECNT: case TF_SECNO: case TF_CYLLO: case TF_CYLHI: hdc1001_info->taskfile[Addr & 0x07] = cData; TRACE_PRINT(VERBOSE_MSG, ("HDC1001: " ADDRESS_FORMAT " WR TF[%d]=0x%02x" NLP, PCX, Addr & 7, cData)); break; case TF_CMD: pDrive->track = hdc1001_info->taskfile[TF_CYLLO] | (hdc1001_info->taskfile[TF_CYLHI] << 8); pDrive->xfr_nsects = hdc1001_info->taskfile[TF_SECNT]; TRACE_PRINT(CMD_MSG, ("HDC1001[%d]: Command=%d, T:%d/H:%d/S:%d N=%d" NLP, hdc1001_info->sel_drive, hdc1001_info->taskfile[TF_CMD], pDrive->track, hdc1001_info->taskfile[TF_SDH] & 0x07, hdc1001_info->taskfile[TF_SECNO], pDrive->xfr_nsects)); break; default: break; } return 0; } static uint8 HDC1001_Read(const uint32 Addr) { uint8 cData; cData = hdc1001_info->taskfile[Addr & 0x07]; TRACE_PRINT(VERBOSE_MSG, ("HDC1001: " ADDRESS_FORMAT " RD TF[%d]=0x%02x" NLP, PCX, Addr & 7, cData)); return (cData); } #if 0 for(i = 0; i < HDC1001_IOPB_LEN; i++) { hdc1001_info->iopb[i] = GetBYTEWrapper(hdc1001_info->link_addr + i); } cmd = hdc1001_info->iopb[0]; hdc1001_info->sel_drive = hdc1001_info->iopb[2]; hdc1001_info->dma_addr = hdc1001_info->iopb[0x0A]; hdc1001_info->dma_addr |= hdc1001_info->iopb[0x0B] << 8; hdc1001_info->dma_addr |= hdc1001_info->iopb[0x0C] << 16; next_link = hdc1001_info->iopb[0x0D]; next_link |= hdc1001_info->iopb[0x0E] << 8; next_link |= hdc1001_info->iopb[0x0F] << 16; TRACE_PRINT(VERBOSE_MSG, ("HDC1001[%d]: LINK=0x%05x, NEXT=0x%05x, CMD=%x, DMA@0x%05x\n", hdc1001_info->sel_drive, hdc1001_info->link_addr, next_link, hdc1001_info->iopb[0], hdc1001_info->dma_addr)); if(pDrive->ready) { /* Perform command */ switch(cmd) { case HDC1001_CODE_NOOP: TRACE_PRINT(VERBOSE_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " NOOP" NLP, hdc1001_info->sel_drive, PCX)); break; case HDC1001_CODE_VERSION: break; case HDC1001_CODE_GLOBAL: TRACE_PRINT(CMD_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " GLOBAL" NLP, hdc1001_info->sel_drive, PCX)); hdc1001_info->mode = hdc1001_info->iopb[3]; hdc1001_info->retries = hdc1001_info->iopb[4]; hdc1001_info->ndrives = hdc1001_info->iopb[5]; TRACE_PRINT(VERBOSE_MSG, (" Mode: 0x%02x" NLP, hdc1001_info->mode)); TRACE_PRINT(VERBOSE_MSG, (" # Retries: 0x%02x" NLP, hdc1001_info->retries)); TRACE_PRINT(VERBOSE_MSG, (" # Drives: 0x%02x" NLP, hdc1001_info->ndrives)); break; case HDC1001_CODE_SPECIFY: { uint8 specify_data[22]; TRACE_PRINT(CMD_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " SPECIFY" NLP, hdc1001_info->sel_drive, PCX)); for(i = 0; i < 22; i++) { specify_data[i] = GetBYTEWrapper(hdc1001_info->dma_addr + i); } pDrive->sectsize = specify_data[4] | (specify_data[5] << 8); pDrive->nsectors = specify_data[6] | (specify_data[7] << 8); pDrive->nheads = specify_data[8] | (specify_data[9] << 8); pDrive->ntracks = specify_data[10] | (specify_data[11] << 8); pDrive->res_tracks = specify_data[18] | (specify_data[19] << 8); TRACE_PRINT(VERBOSE_MSG, (" Sectsize: %d" NLP, pDrive->sectsize)); TRACE_PRINT(VERBOSE_MSG, (" Sectors: %d" NLP, pDrive->nsectors)); TRACE_PRINT(VERBOSE_MSG, (" Heads: %d" NLP, pDrive->nheads)); TRACE_PRINT(VERBOSE_MSG, (" Tracks: %d" NLP, pDrive->ntracks)); TRACE_PRINT(VERBOSE_MSG, (" Reserved: %d" NLP, pDrive->res_tracks)); break; } case HDC1001_CODE_HOME: pDrive->track = 0; TRACE_PRINT(SEEK_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " HOME" NLP, hdc1001_info->sel_drive, PCX)); break; case HDC1001_CODE_SEEK: pDrive->track = hdc1001_info->iopb[3]; pDrive->track |= (hdc1001_info->iopb[4] << 8); if(pDrive->track > pDrive->ntracks) { TRACE_PRINT(ERROR_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " SEEK ERROR %d not found" NLP, hdc1001_info->sel_drive, PCX, pDrive->track)); pDrive->track = pDrive->ntracks - 1; result = HDC1001_STATUS_TIMEOUT; } else { TRACE_PRINT(SEEK_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " SEEK %d" NLP, hdc1001_info->sel_drive, PCX, pDrive->track)); } break; case HDC1001_CODE_READ_HDR: { TRACE_PRINT(CMD_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " READ HEADER: %d" NLP, pDrive->track, PCX)); PutBYTEWrapper(hdc1001_info->dma_addr + 0, pDrive->track & 0xFF); PutBYTEWrapper(hdc1001_info->dma_addr + 1, (pDrive->track >> 8) & 0xFF); PutBYTEWrapper(hdc1001_info->dma_addr + 2, 0); PutBYTEWrapper(hdc1001_info->dma_addr + 3, 1); break; } case HDC1001_CODE_READWRITE: { uint32 track_len; uint32 xfr_len; uint32 file_offset; uint32 xfr_count = 0; uint8 *dataBuffer; pDrive->cur_sect = hdc1001_info->iopb[4] | (hdc1001_info->iopb[5] << 8); pDrive->cur_track = hdc1001_info->iopb[6] | (hdc1001_info->iopb[7] << 8); pDrive->xfr_nsects = hdc1001_info->iopb[8] | (hdc1001_info->iopb[9] << 8); track_len = pDrive->nsectors * pDrive->sectsize; file_offset = (pDrive->cur_track * track_len); /* Calculate offset based on current track */ file_offset += pDrive->cur_sect + pDrive->sectsize; xfr_len = pDrive->xfr_nsects * pDrive->sectsize; dataBuffer = malloc(xfr_len); sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET); if(hdc1001_info->iopb[3] == 1) { /* Read */ TRACE_PRINT(RD_DATA_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " READ @0x%05x T:%04d/S:%04d/#:%d" NLP, hdc1001_info->sel_drive, PCX, hdc1001_info->dma_addr, pDrive->cur_track, pDrive->cur_sect, pDrive->xfr_nsects )); fread(dataBuffer, xfr_len, 1, (pDrive->uptr)->fileref); /* Perform DMA Transfer */ for(xfr_count = 0;xfr_count < xfr_len; xfr_count++) { PutBYTEWrapper(hdc1001_info->dma_addr + xfr_count, dataBuffer[xfr_count]); } } else { /* Write */ TRACE_PRINT(WR_DATA_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " WRITE @0x%05x T:%04d/S:%04d/#:%d" NLP, hdc1001_info->sel_drive, PCX, hdc1001_info->dma_addr, pDrive->cur_track, pDrive->cur_sect, pDrive->xfr_nsects )); /* Perform DMA Transfer */ for(xfr_count = 0;xfr_count < xfr_len; xfr_count++) { dataBuffer[xfr_count] = GetBYTEWrapper(hdc1001_info->dma_addr + xfr_count); } fwrite(dataBuffer, xfr_len, 1, (pDrive->uptr)->fileref); } free(dataBuffer); break; } case HDC1001_CODE_FORMAT: { uint32 data_len; uint32 file_offset; uint8 *fmtBuffer; data_len = pDrive->nsectors * pDrive->sectsize; TRACE_PRINT(WR_DATA_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " FORMAT T:%d/H:%d/Fill=0x%02x/Len=%d" NLP, hdc1001_info->sel_drive, PCX, pDrive->track, hdc1001_info->iopb[5], hdc1001_info->iopb[4], data_len )); file_offset = (pDrive->track * (pDrive->nheads) * data_len); /* Calculate offset based on current track */ file_offset += (hdc1001_info->iopb[5] * data_len); fmtBuffer = malloc(data_len); memset(fmtBuffer, hdc1001_info->iopb[4], data_len); sim_fseek((pDrive->uptr)->fileref, file_offset, SEEK_SET); fwrite(fmtBuffer, data_len, 1, (pDrive->uptr)->fileref); free(fmtBuffer); break; } case HDC1001_CODE_SET_MAP: case HDC1001_CODE_RELOCATE: case HDC1001_CODE_FORMAT_BAD: case HDC1001_CODE_STATUS: case HDC1001_CODE_SELECT: case HDC1001_CODE_EXAMINE: case HDC1001_CODE_MODIFY: default: TRACE_PRINT(ERROR_MSG, ("HDC1001[%d]: " ADDRESS_FORMAT " CMD=%x Unsupported" NLP, hdc1001_info->sel_drive, PCX, cmd)); break; } } else { /* Drive not ready */ result = HDC1001_STATUS_NOT_READY; } /* Return status */ PutBYTEWrapper(hdc1001_info->link_addr + 1, result); hdc1001_info->link_addr = next_link; return 0; } #endif /* 0 */ simh-3.8.1/AltairZ80/i86.h0000644000175000017500000002424010766764756013170 0ustar vlmvlm/* * Dos/PC Emulator * Copyright (C) 1991 Jim Hudgens * * * The file is part of GDE. * * GDE is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * GDE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GDE; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* 8086 support structs and definitions */ /* definition of the registers */ /* general EAX,EBX,ECX, EDX type registers. Note that for portability, and speed, the issue of byte swapping is not addressed in the registers. All registers are stored in the default format available on the host machine. The only critical issue is that the registers should line up EXACTLY in the same manner as they do in the 386. That is: EAX & 0xff === AL EAX & 0xffff == AX etc. The result is that alot of the calculations can then be done using the native instruction set fully. */ /* Endian Logic Priority 1: If LOWFIRST is defined, use it. LOWFIRST must be 1 if the lower part of a 16 bit quantity comes first in memory, otherwise LOWFIRST must be 0 Priority 2: If __BIG_ENDIAN__ is defined, use it to define LOWFIRST accordingly Priority 3: OS 9 on Macintosh needs LOWFIRST 0 Priority 4: Use LOWFIRST 1 as default */ #ifndef LOWFIRST #ifdef __BIG_ENDIAN__ #if __BIG_ENDIAN__ #define LOWFIRST 0 #else #define LOWFIRST 1 #endif #elif defined (__MWERKS__) && defined (macintosh) #define LOWFIRST 0 #else #define LOWFIRST 1 #endif #endif #if LOWFIRST typedef struct { uint16 x_reg; } I16_reg_t; typedef struct { uint8 l_reg, h_reg; } I8_reg_t; #else typedef struct { uint16 x_reg; } I16_reg_t; typedef struct { uint8 h_reg, l_reg; } I8_reg_t; #endif typedef union { I16_reg_t I16_reg; I8_reg_t I8_reg; } i386_general_register; struct i386_general_regs { i386_general_register A, B, C, D; }; typedef struct i386_general_regs Gen_reg_t; struct i386_special_regs { i386_general_register SP, BP, SI, DI, IP; uint32 FLAGS; }; /* * segment registers here represent the 16 bit quantities * CS, DS, ES, SS * * segment pointers --- used to speed up the expressions: * q = m->R_CSP + m->R_IP; * fetched = *q; * m->R_IP += 1; * compared to: * fetched = GetBYTEExtended(((uint32)m->R_CS << 4) + (m->R_IP++)); * Save at least one shift, more if doing two byte moves. */ struct i386_segment_regs { uint16 CS, DS, SS, ES, FS, GS; }; /* 8 bit registers */ #define R_AH Gn_regs.A.I8_reg.h_reg #define R_AL Gn_regs.A.I8_reg.l_reg #define R_BH Gn_regs.B.I8_reg.h_reg #define R_BL Gn_regs.B.I8_reg.l_reg #define R_CH Gn_regs.C.I8_reg.h_reg #define R_CL Gn_regs.C.I8_reg.l_reg #define R_DH Gn_regs.D.I8_reg.h_reg #define R_DL Gn_regs.D.I8_reg.l_reg /* 16 bit registers */ #define R_AX Gn_regs.A.I16_reg.x_reg #define R_BX Gn_regs.B.I16_reg.x_reg #define R_CX Gn_regs.C.I16_reg.x_reg #define R_DX Gn_regs.D.I16_reg.x_reg /* special registers */ #define R_SP Sp_regs.SP.I16_reg.x_reg #define R_BP Sp_regs.BP.I16_reg.x_reg #define R_SI Sp_regs.SI.I16_reg.x_reg #define R_DI Sp_regs.DI.I16_reg.x_reg #define R_IP Sp_regs.IP.I16_reg.x_reg #define R_FLG Sp_regs.FLAGS /* segment registers */ #define R_CS Sg_regs.CS #define R_DS Sg_regs.DS #define R_SS Sg_regs.SS #define R_ES Sg_regs.ES /* 8088 has top 4 bits of the flags set to 1 */ /* Also, bit#1 is set. This is (not well) documented behavior. */ /* see note in userman.tex about the subtleties of dealing with */ /* code which attempts to detect the host processor. */ /* This is def'd as F_ALWAYS_ON */ #define F_ALWAYS_ON (0xf002) /* flag bits always on */ /* following bits masked in to a 16bit quantity */ #define F_CF 0x1 /* CARRY flag */ #define F_PF 0x4 /* PARITY flag */ #define F_AF 0x10 /* AUX flag */ #define F_ZF 0x40 /* ZERO flag */ #define F_SF 0x80 /* SIGN flag */ #define F_TF 0x100 /* TRAP flag */ #define F_IF 0x200 /* INTERRUPT ENABLE flag */ #define F_DF 0x400 /* DIR flag */ #define F_OF 0x800 /* OVERFLOW flag */ /* * DEFINE A MASK FOR ONLY THOSE FLAG BITS WE WILL EVER PASS BACK * (via PUSHF) */ #define F_MSK (F_CF|F_PF|F_AF|F_ZF|F_SF|F_TF|F_IF|F_DF|F_OF) #define TOGGLE_FLAG(M,FLAG) (M)->R_FLG ^= FLAG #define SET_FLAG(M,FLAG) (M)->R_FLG |= FLAG #define CLEAR_FLAG(M, FLAG) (M)->R_FLG &= ~FLAG #define ACCESS_FLAG(M,FLAG) ((M)->R_FLG & (FLAG)) #define CONDITIONAL_SET_FLAG(COND,M,FLAG) \ if (COND) SET_FLAG(M,FLAG); else CLEAR_FLAG(M,FLAG) /* emulator machine state. */ /* segment usage control */ #define SYSMODE_SEG_DS_SS 0x01 #define SYSMODE_SEGOVR_CS 0x02 #define SYSMODE_SEGOVR_DS 0x04 #define SYSMODE_SEGOVR_ES 0x08 #define SYSMODE_SEGOVR_SS 0x10 #define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | SYSMODE_SEGOVR_CS | \ SYSMODE_SEGOVR_DS | SYSMODE_SEGOVR_ES | SYSMODE_SEGOVR_SS) #define SYSMODE_PREFIX_REPE 0x20 #define SYSMODE_PREFIX_REPNE 0x40 #define INTR_SYNCH 0x1 #define INTR_HALTED 0x4 #define INTR_ILLEGAL_OPCODE 0x8 /* INSTRUCTION DECODING STUFF */ #define FETCH_DECODE_MODRM(m,mod,rh,rl) fetch_decode_modrm(m,&mod,&rh,&rl) #define DECODE_RM_BYTE_REGISTER(m,r) decode_rm_byte_register(m,r) #define DECODE_RM_WORD_REGISTER(m,r) decode_rm_word_register(m,r) #define DECODE_CLEAR_SEGOVR(m) m->sysmode &= ~(SYSMODE_SEGMASK) typedef struct pc_env PC_ENV; struct pc_env { /* The registers!! */ struct i386_general_regs Gn_regs; struct i386_special_regs Sp_regs; struct i386_segment_regs Sg_regs; /* our flags structrure. This contains information on REPE prefix 2 bits repe,repne SEGMENT overrides 5 bits normal,DS,SS,CS,ES Delayed flag set 3 bits (zero, signed, parity) reserved 6 bits interrupt # 8 bits instruction raised interrupt BIOS video segregs 4 bits Interrupt Pending 1 bits Extern interrupt 1 bits Halted 1 bits */ long sysmode; uint8 intno; }; /* GLOBAL */ volatile int intr; void halt_sys (PC_ENV *sys); void fetch_decode_modrm (PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl); uint8 *decode_rm_byte_register (PC_ENV *m, int reg); uint16 *decode_rm_word_register (PC_ENV *m, int reg); uint16 *decode_rm_seg_register (PC_ENV *m, int reg); uint8 fetch_byte_imm (PC_ENV *m); uint16 fetch_word_imm (PC_ENV *m); uint16 decode_rm00_address (PC_ENV *m, int rm); uint16 decode_rm01_address (PC_ENV *m, int rm); uint16 decode_rm10_address (PC_ENV *m, int rm); uint8 fetch_data_byte (PC_ENV *m, uint16 offset); uint8 fetch_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset); uint16 fetch_data_word (PC_ENV *m, uint16 offset); uint16 fetch_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset); void store_data_byte (PC_ENV *m, uint16 offset, uint8 val); void store_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset, uint8 val); void store_data_word (PC_ENV *m, uint16 offset, uint16 val); void store_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset, uint16 val); typedef void (*OP)(PC_ENV *m); extern OP i86_optab[256]; /* PRIMITIVE OPERATIONS */ uint8 aad_word (PC_ENV *m, uint16 d); uint16 aam_word (PC_ENV *m, uint8 d); uint8 adc_byte (PC_ENV *m, uint8 d, uint8 s); uint16 adc_word (PC_ENV *m, uint16 d, uint16 s); uint8 add_byte (PC_ENV *m, uint8 d, uint8 s); uint16 add_word (PC_ENV *m, uint16 d, uint16 s); uint8 and_byte (PC_ENV *m, uint8 d, uint8 s); uint16 and_word (PC_ENV *m, uint16 d, uint16 s); uint8 cmp_byte (PC_ENV *m, uint8 d, uint8 s); uint16 cmp_word (PC_ENV *m, uint16 d, uint16 s); uint8 dec_byte (PC_ENV *m, uint8 d); uint16 dec_word (PC_ENV *m, uint16 d); uint8 inc_byte (PC_ENV *m, uint8 d); uint16 inc_word (PC_ENV *m, uint16 d); uint8 or_byte (PC_ENV *m, uint8 d, uint8 s); uint16 or_word (PC_ENV *m, uint16 d, uint16 s); uint8 neg_byte (PC_ENV *m, uint8 s); uint16 neg_word (PC_ENV *m, uint16 s); uint8 not_byte (PC_ENV *m, uint8 s); uint16 not_word (PC_ENV *m, uint16 s); uint16 mem_access_word (PC_ENV *m, int addr); void push_word (PC_ENV *m, uint16 w); uint16 pop_word (PC_ENV *m); uint8 rcl_byte (PC_ENV *m, uint8 d, uint8 s); uint16 rcl_word (PC_ENV *m, uint16 d, uint16 s); uint8 rcr_byte (PC_ENV *m, uint8 d, uint8 s); uint16 rcr_word (PC_ENV *m, uint16 d, uint16 s); uint8 rol_byte (PC_ENV *m, uint8 d, uint8 s); uint16 rol_word (PC_ENV *m, uint16 d, uint16 s); uint8 ror_byte (PC_ENV *m, uint8 d, uint8 s); uint16 ror_word (PC_ENV *m, uint16 d, uint16 s); uint8 shl_byte (PC_ENV *m, uint8 d, uint8 s) ; uint16 shl_word (PC_ENV *m, uint16 d, uint16 s); uint8 shr_byte (PC_ENV *m, uint8 d, uint8 s); uint16 shr_word (PC_ENV *m, uint16 d, uint16 s); uint8 sar_byte (PC_ENV *m, uint8 d, uint8 s); uint16 sar_word (PC_ENV *m, uint16 d, uint16 s); uint8 sbb_byte (PC_ENV *m, uint8 d, uint8 s); uint16 sbb_word (PC_ENV *m, uint16 d, uint16 s); uint8 sub_byte (PC_ENV *m, uint8 d, uint8 s); uint16 sub_word (PC_ENV *m, uint16 d, uint16 s); void test_byte (PC_ENV *m, uint8 d, uint8 s); void test_word (PC_ENV *m, uint16 d, uint16 s); uint8 xor_byte (PC_ENV *m, uint8 d, uint8 s); uint16 xor_word (PC_ENV *m, uint16 d, uint16 s); void imul_byte (PC_ENV *m, uint8 s); void imul_word (PC_ENV *m, uint16 s); void mul_byte (PC_ENV *m, uint8 s); void mul_word (PC_ENV *m, uint16 s); void idiv_byte (PC_ENV *m, uint8 s); void idiv_word (PC_ENV *m, uint16 s); void div_byte (PC_ENV *m, uint8 s); void div_word (PC_ENV *m, uint16 s); simh-3.8.1/AltairZ80/s100_disk1a.c0000644000175000017500000020170411111141264014436 0ustar vlmvlm/************************************************************************* * * * $Id: s100_disk1a.c 1996 2008-07-16 05:31:21Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro DISK1A Floppy Controller module for SIMH. * * This module is a wrapper around the i8272 FDC module, and adds the * * CompuPro-specific registers as well as the CompuPro Boot ROM. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_defs.h" /* simulator definitions */ #include "i8272.h" #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define VERBOSE_MSG (1 << 7) #define IRQ_MSG (1 << 8) #define DISK1A_MAX_DRIVES 4 typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 dma_addr; /* DMA Transfer Address */ uint8 rom_disabled; /* TRUE if ROM has been disabled */ } DISK1A_INFO; static DISK1A_INFO disk1a_info_data = { { 0x0, 512, 0xC0, 4 } }; static DISK1A_INFO *disk1a_info = &disk1a_info_data; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern void raise_ss1_interrupt(uint8 intnum); extern REG *sim_PC; extern uint32 PCX; /* external view of PC */ #define UNIT_V_DISK1A_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_DISK1A_WLK (1 << UNIT_V_DISK1A_WLK) #define UNIT_V_DISK1A_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_DISK1A_VERBOSE (1 << UNIT_V_DISK1A_VERBOSE) #define UNIT_V_DISK1A_ROM (UNIT_V_UF + 2) /* boot ROM enabled */ #define UNIT_DISK1A_ROM (1 << UNIT_V_DISK1A_ROM) #define DISK1A_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ static t_stat disk1a_reset(DEVICE *disk1a_dev); static t_stat disk1a_boot(int32 unitno, DEVICE *dptr); static t_stat disk1a_attach(UNIT *uptr, char *cptr); static t_stat disk1a_detach(UNIT *uptr); static int32 disk1adev(const int32 port, const int32 io, const int32 data); static int32 disk1arom(const int32 port, const int32 io, const int32 data); static uint8 DISK1A_Read(const uint32 Addr); static uint8 DISK1A_Write(const uint32 Addr, uint8 cData); void raise_disk1a_interrupt(void); static int32 bootstrap = 0; /* The DISK1A does not really have RAM associated with it, but for ease of integration with the * SIMH/AltairZ80 Resource Mapping Scheme, rather than Map and Unmap the ROM, simply implement our * own RAM that can be swapped in when the DISK1A Boot ROM is disabled. */ static uint8 disk1aram[512]; static UNIT disk1a_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) } }; static REG disk1a_reg[] = { { DRDATA (BOOTSTRAP, bootstrap, 10), }, { NULL } }; static MTAB disk1a_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_DISK1A_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_DISK1A_WLK, UNIT_DISK1A_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_DISK1A_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_DISK1A_VERBOSE, UNIT_DISK1A_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { UNIT_DISK1A_ROM, 0, "NOROM", "NOROM", NULL }, { UNIT_DISK1A_ROM, UNIT_DISK1A_ROM, "ROM", "ROM", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(disk1a_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB disk1a_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "VERBOSE",VERBOSE_MSG }, { "IRQ", IRQ_MSG }, { NULL, 0 } }; DEVICE disk1a_dev = { "DISK1A", disk1a_unit, disk1a_reg, disk1a_mod, DISK1A_MAX_DRIVES, 10, 31, 1, DISK1A_MAX_DRIVES, DISK1A_MAX_DRIVES, NULL, NULL, &disk1a_reset, &disk1a_boot, &disk1a_attach, &disk1a_detach, &disk1a_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, disk1a_dt, NULL, "Compupro Floppy Controller DISK1A" }; /* This is the DISK1A Boot ROM. * It consists of an 8K ROM, broken down into 16 bootstraps of * 512-bytes each. See the DISK1A Manual for details of each * bootstrap. Bootstrap 0 is the default, but others can be * selected with 'd disk1a bootstrap ' at the SIMH SCP Prompt. * * Bootstraps 0- 3 8085/8088/Z80 * Bootstraps 4- 7 8086/80286 * Bootstraps 8-11 68000 * Bootstraps 12-15 32016 * * 0,4, 8,12: Attempt to boot 8" drive 0, if not ready boot from DISK3. * 1,5, 9,13: Always boot from DISK3. * 2,6,10,14: Attempt to boot 8" drive 0, if not ready, attempt to boot 5.25" drive 2. * 3,7,11,15: Attempt to boot 5.25" drive 0, if not ready, attempt to boot DISK3. */ static uint8 disk1a_rom[16][512] = { { 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 0 */ 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0x11, 0x5D, 0x00, 0x3E, 0x85, 0x12, 0x13, 0x3E, 0x81, 0x12, 0x13, 0x3E, 0x00, 0x12, 0x11, 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x49, 0x80, 0x01, 0x00, 0xC0, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, 0x40, 0x80, 0xC9, 0x3E, 0xFE, 0xD3, 0xC3, 0x11, 0x70, 0x81, 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x52, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x52, 0x80, 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x62, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x62, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0x70, 0x80, 0x3E, 0x08, 0xD3, 0xC1, 0xCD, 0x3D, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0x7D, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, 0xC0, 0xB7, 0xF2, 0x88, 0x80, 0xDB, 0xC1, 0xB1, 0xC2, 0xDD, 0x80, 0x06, 0x03, 0x1A, 0xD3, 0xC2, 0x13, 0x05, 0xC2, 0x96, 0x80, 0x06, 0x09, 0xDB, 0xC0, 0xB7, 0xF2, 0xA0, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0xA0, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0xAE, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0xB4, 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0x67, 0xDB, 0xC0, 0xB7, 0xF2, 0xBF, 0x80, 0xDB, 0xC1, 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, 0xCC, 0x80, 0xDB, 0xC1, 0x05, 0xC2, 0xCC, 0x80, 0x7D, 0xB4, 0xCA, 0x53, 0x81, 0x3E, 0x01, 0xD3, 0x90, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x13, 0x81, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x13, 0x81, 0xC3, 0x49, 0x80, 0xAF, 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x08, 0x81, 0x07, 0xF8, 0xC1, 0xC3, 0x49, 0x80, 0x11, 0x96, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xA6, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xB6, 0x81, 0x2A, 0xBF, 0x81, 0xCD, 0x04, 0x81, 0x11, 0x81, 0x81, 0x06, 0x04, 0x1A, 0xBE, 0xC2, 0x49, 0x80, 0x23, 0x13, 0x05, 0xC2, 0x2D, 0x81, 0x11, 0xC6, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xD6, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xE6, 0x81, 0x2A, 0xEF, 0x81, 0xCD, 0x04, 0x81, 0x7E, 0xFE, 0xE5, 0xCA, 0x49, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8F, 0x24, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x81, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x81, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xC5, 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0xD5, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xE5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A }, { 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x01, 0x00, 0x40, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, /* 1 */ 0x0A, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, 0x7D, 0xB4, 0xC2, 0x18, 0x00, 0x11, 0x5D, 0x00, 0x3E, 0xD2, 0x12, 0x13, 0x3E, 0x80, 0x12, 0x13, 0x3E, 0x00, 0x12, 0x11, 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x39, 0x80, 0x3E, 0xFE, 0xD3, 0xC3, 0x3E, 0x01, 0xD3, 0x90, 0xAF, 0x32, 0xD3, 0x80, 0xD3, 0x90, 0x01, 0x00, 0x14, 0x0B, 0x78, 0xB1, 0xC2, 0x4A, 0x80, 0x3A, 0xD3, 0x80, 0xB7, 0xFA, 0x7F, 0x80, 0xAF, 0x32, 0xD3, 0x80, 0xD3, 0x90, 0x01, 0x00, 0x14, 0x0B, 0x78, 0xB1, 0xC2, 0x60, 0x80, 0x3A, 0xD3, 0x80, 0xB7, 0xFA, 0x7F, 0x80, 0xC3, 0x39, 0x80, 0xAF, 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x74, 0x80, 0x07, 0xF8, 0xC1, 0xC3, 0x39, 0x80, 0x11, 0xE3, 0x80, 0xCD, 0x70, 0x80, 0x11, 0xF3, 0x80, 0xCD, 0x70, 0x80, 0x11, 0x03, 0x81, 0x2A, 0x0C, 0x81, 0xCD, 0x70, 0x80, 0x11, 0xCE, 0x80, 0x06, 0x04, 0x1A, 0xBE, 0xC2, 0x39, 0x80, 0x23, 0x13, 0x05, 0xC2, 0x99, 0x80, 0x11, 0x13, 0x81, 0xCD, 0x70, 0x80, 0x11, 0x23, 0x81, 0xCD, 0x70, 0x80, 0x11, 0x33, 0x81, 0x2A, 0x3C, 0x81, 0xCD, 0x70, 0x80, 0x7E, 0xFE, 0xE5, 0xCA, 0x39, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x32, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x12, 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x22, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x32, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 2 */ 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0xC3, 0x34, 0x80, 0x01, 0x00, 0xC0, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, 0x2B, 0x80, 0xC9, 0x11, 0x00, 0x81, 0x3E, 0xFE, 0xD3, 0xC3, 0x1A, 0xD3, 0xC0, 0xCD, 0x28, 0x80, 0x13, 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x44, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x44, 0x80, 0xCD, 0x28, 0x80, 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x57, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x57, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0x65, 0x80, 0x3E, 0x08, 0xD3, 0xC1, 0xCD, 0x28, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0x72, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, 0xC0, 0xB7, 0xF2, 0x7D, 0x80, 0xDB, 0xC1, 0xB1, 0xE6, 0xFC, 0xC2, 0xD6, 0x80, 0x06, 0x03, 0x1A, 0xD3, 0xC2, 0x13, 0x05, 0xC2, 0x8D, 0x80, 0x06, 0x09, 0xDB, 0xC0, 0xB7, 0xF2, 0x97, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x97, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0xA5, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0xAB, 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0xE6, 0xFC, 0x67, 0xDB, 0xC0, 0xB7, 0xF2, 0xB8, 0x80, 0xDB, 0xC1, 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, 0xC5, 0x80, 0xDB, 0xC1, 0x05, 0xC2, 0xC5, 0x80, 0x7D, 0xB4, 0xCA, 0xE7, 0x80, 0x7B, 0xFE, 0x18, 0xCA, 0x34, 0x80, 0xFE, 0x24, 0xCA, 0x34, 0x80, 0x11, 0x12, 0x81, 0xC3, 0x37, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8F, 0x24, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x28, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x35, 0xFF, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x72, 0x79, 0x2E, 0x20, 0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x38, 0x33, 0x20, 0x62, 0x79, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x52, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2E, 0x00, 0x24, 0x4C, 0x53, 0x54, 0x3A, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xC4, 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0x09, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x2E, 0x2C, 0x3D, 0x3A, 0x7C, 0x5B, 0x5D, 0x2A, 0x0A, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x63, 0x68, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x0A, 0x00, 0x43, 0x6F, 0x75, 0x6C, 0x64, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x6C, 0x6F, 0x67, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65 }, { 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 3 */ 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0x11, 0x5D, 0x00, 0x3E, 0x85, 0x12, 0x13, 0x3E, 0x81, 0x12, 0x13, 0x3E, 0x00, 0x12, 0x11, 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x49, 0x80, 0x01, 0x00, 0x80, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, 0x40, 0x80, 0xC9, 0x3E, 0xFE, 0xD3, 0xC3, 0x3E, 0x28, 0xD3, 0xC0, 0xCD, 0x3D, 0x80, 0x11, 0x70, 0x81, 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x59, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x59, 0x80, 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x69, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x69, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0x77, 0x80, 0x3E, 0x08, 0xD3, 0xC1, 0xCD, 0x3D, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0x84, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, 0xC0, 0xB7, 0xF2, 0x8F, 0x80, 0xDB, 0xC1, 0xB1, 0xE6, 0xFC, 0xC2, 0xE8, 0x80, 0x06, 0x03, 0x1A, 0xD3, 0xC2, 0x13, 0x05, 0xC2, 0x9F, 0x80, 0x06, 0x09, 0xDB, 0xC0, 0xB7, 0xF2, 0xA9, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0xA9, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0xB7, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0xBD, 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0xE6, 0xFC, 0x67, 0xDB, 0xC0, 0xB7, 0xF2, 0xCA, 0x80, 0xDB, 0xC1, 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, 0xD7, 0x80, 0xDB, 0xC1, 0x05, 0xC2, 0xD7, 0x80, 0x7D, 0xB4, 0xCA, 0x5E, 0x81, 0x3E, 0x01, 0xD3, 0x90, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x1E, 0x81, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x1E, 0x81, 0xC3, 0x49, 0x80, 0xAF, 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x13, 0x81, 0x07, 0xF8, 0xC1, 0xC3, 0x49, 0x80, 0x11, 0x96, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xA6, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xB6, 0x81, 0x2A, 0xBF, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0x81, 0x81, 0x06, 0x04, 0x1A, 0xBE, 0xC2, 0x49, 0x80, 0x23, 0x13, 0x05, 0xC2, 0x38, 0x81, 0x11, 0xC6, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xD6, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xE6, 0x81, 0x2A, 0xEF, 0x81, 0xCD, 0x0F, 0x81, 0x7E, 0xFE, 0xE5, 0xCA, 0x49, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x34, 0x00, 0x00, 0x00, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x81, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x81, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xC5, 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0xD5, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xE5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A }, { 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0x33, /* 4 */ 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBA, 0x90, 0x00, 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xEB, 0x0F, 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xAC, 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0xB0, 0xFE, 0xE6, 0xC3, 0xB9, 0x03, 0x00, 0xBE, 0xC6, 0x00, 0xE8, 0xE4, 0xFF, 0xB9, 0x02, 0x00, 0xE8, 0xDE, 0xFF, 0xEB, 0x13, 0x52, 0x52, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, 0xC1, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x74, 0x03, 0xE9, 0x84, 0x00, 0xB9, 0x03, 0x00, 0xAC, 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x8F, 0xFF, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x2C, 0x40, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x80, 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0xE2, 0xF6, 0x0A, 0xFB, 0x74, 0x24, 0xE9, 0x4A, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x02, 0xAC, 0x3C, 0xE5, 0x75, 0x03, 0xE9, 0x51, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x00, 0x12, 0xF3, 0xA5, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, 0x43, 0x6F, 0x6D, 0x70, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, 0xFB, 0x79, 0x01, 0xC3, 0x58, 0xBE, 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x13, 0x88, 0x44, 0x01, 0xEF, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x03, 0xE9, 0xFD, 0xFE, 0xC7, 0x04, 0x05, 0x00, 0xE8, 0xC2, 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0xB6, 0xFF, 0xC7, 0x44, 0x04, 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, 0x01, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x9C, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x04, 0x00, 0xF3, 0xA6, 0x75, 0xC3, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x02, 0xC7, 0x04, 0x03, 0x00, 0xE8, 0x80, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x06, 0xC7, 0x04, 0x04, 0x00, 0xE8, 0x74, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, 0x01, 0x33, 0xC0, 0x89, 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x57, 0xFF, 0xE9, 0x2C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, { 0xDB, 0xFD, 0x33, 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x1D, 0x00, 0xBA, 0x90, 0x00, /* 5 */ 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xFC, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xEB, 0x18, 0x43, 0x6F, 0x6D, 0x70, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0xC3, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, 0xFB, 0x79, 0x01, 0xC3, 0x58, 0xB0, 0xFE, 0xE6, 0xC3, 0xBE, 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xEB, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xE8, 0xB8, 0xFF, 0x0A, 0x44, 0x01, 0x75, 0x0C, 0x88, 0x44, 0x01, 0xEE, 0xE8, 0xAC, 0xFF, 0x0A, 0x44, 0x01, 0x74, 0xBB, 0xC7, 0x04, 0x05, 0x00, 0xE8, 0xA8, 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0x9C, 0xFF, 0xC7, 0x44, 0x04, 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x01, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, 0x01, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x82, 0xFF, 0xBE, 0x00, 0x01, 0xBF, 0x1D, 0x00, 0xB9, 0x02, 0x00, 0xF3, 0xA7, 0x75, 0x81, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x01, 0xC7, 0x04, 0x03, 0x00, 0xE8, 0x66, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x05, 0xC7, 0x04, 0x04, 0x00, 0xE8, 0x5A, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x01, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, 0x01, 0x33, 0xC0, 0x89, 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x3D, 0xFF, 0x33, 0xC0, 0xEB, 0x04, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, { 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0x33, /* 6 */ 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xB9, 0x80, 0x00, 0xF3, 0xA5, 0xEB, 0x17, 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xAC, 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0xC3, 0xBE, 0xCA, 0x00, 0xB0, 0xFE, 0xE6, 0xC3, 0xFC, 0xAC, 0xE6, 0xC0, 0xE8, 0xEA, 0xFF, 0xB9, 0x03, 0x00, 0xE8, 0xD5, 0xFF, 0xB9, 0x02, 0x00, 0xE8, 0xCF, 0xFF, 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, 0xC1, 0xE8, 0xD1, 0xFF, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x24, 0xFC, 0x74, 0x03, 0xE9, 0x39, 0x00, 0xB9, 0x03, 0x00, 0xAC, 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x97, 0xFF, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x2C, 0x40, 0x24, 0xFC, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x80, 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0xE2, 0xF6, 0x0A, 0xFB, 0x74, 0x38, 0x8B, 0xC6, 0x3D, 0xE2, 0x00, 0x7D, 0x06, 0xBE, 0xDC, 0x00, 0xE9, 0x79, 0xFF, 0xE9, 0x73, 0xFF, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x28, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x35, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x52, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, { 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xA0, 0x50, 0x58, 0xE2, 0xFC, 0x33, /* 7 */ 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBA, 0x90, 0x00, 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xB0, 0xFE, 0xE6, 0xC3, 0xB0, 0x28, 0xE6, 0xC0, 0xB9, 0x00, 0xA0, 0x50, 0x58, 0xE2, 0xFC, 0xB9, 0x03, 0x00, 0xBE, 0xCA, 0x00, 0xE8, 0xC1, 0x00, 0xB9, 0x02, 0x00, 0xE8, 0xBB, 0x00, 0xEB, 0x19, 0x52, 0x52, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, 0xC1, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x24, 0xFC, 0x74, 0x03, 0xE9, 0x91, 0x00, 0xB9, 0x03, 0x00, 0xAC, 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x64, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x2C, 0x40, 0x24, 0xFC, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x80, 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0xE2, 0xF6, 0x0A, 0xFB, 0x74, 0x20, 0xE9, 0x55, 0x00, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x02, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0xBE, 0x00, 0x02, 0xAC, 0x3C, 0xE5, 0x75, 0x03, 0xE9, 0x40, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x00, 0x12, 0xF3, 0xA5, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xAC, 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0x43, 0x6F, 0x6D, 0x70, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, 0xFB, 0x79, 0x01, 0xC3, 0x58, 0xBE, 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x13, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x03, 0xE9, 0xDD, 0xFE, 0xC7, 0x04, 0x05, 0x00, 0xE8, 0xC2, 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0xB6, 0xFF, 0xC7, 0x44, 0x04, 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, 0x01, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x9C, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x0F, 0x01, 0xB9, 0x04, 0x00, 0xA6, 0x74, 0x03, 0xE9, 0xA1, 0xFE, 0xE2, 0xF8, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x02, 0xC7, 0x04, 0x03, 0x00, 0xE8, 0x7C, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x06, 0xC7, 0x04, 0x04, 0x00, 0xE8, 0x70, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, 0x01, 0x33, 0xC0, 0x89, 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x53, 0xFF, 0xE9, 0x19, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x43, 0xF8, /* 8 */ 0x00, 0x00, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x24, 0xD9, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x49, 0xF9, 0x00, 0xFF, 0x00, 0xC0, 0x4B, 0xEC, 0x00, 0x01, 0x4D, 0xEC, 0x00, 0x02, 0x43, 0xEC, 0x00, 0x02, 0x19, 0x7C, 0x00, 0x80, 0x00, 0x03, 0x76, 0x07, 0x20, 0x7C, 0x00, 0x00, 0x81, 0xCC, 0x42, 0x14, 0x42, 0x16, 0x42, 0x16, 0x42, 0x16, 0x70, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xC8, 0xFF, 0xF8, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x94, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xA4, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xB4, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x50, 0x80, 0x00, 0x07, 0x16, 0x67, 0xFC, 0x1A, 0xBC, 0x00, 0x08, 0x3C, 0x3C, 0x00, 0x03, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x51, 0xCE, 0xFF, 0xF2, 0x07, 0x14, 0x67, 0xFC, 0x14, 0x15, 0x94, 0x3C, 0x00, 0x20, 0x07, 0x14, 0x67, 0xFC, 0x84, 0x15, 0x66, 0x00, 0x00, 0x48, 0x74, 0x08, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xCA, 0xFF, 0xF8, 0x07, 0x16, 0x67, 0xFC, 0x07, 0x14, 0x67, 0xFC, 0x10, 0x15, 0x90, 0x3C, 0x00, 0x40, 0x07, 0x14, 0x67, 0xFC, 0xD0, 0x15, 0x90, 0x3C, 0x00, 0x80, 0x74, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1E, 0x15, 0x51, 0xCA, 0xFF, 0xF8, 0x4A, 0x00, 0x66, 0x00, 0x00, 0x12, 0x1E, 0x11, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x4D, 0xF9, 0x00, 0xFF, 0x00, 0x90, 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x64, 0x21, 0xFC, 0x00, 0x64, 0x80, 0x00, 0x00, 0x5C, 0x42, 0x2D, 0x00, 0x01, 0x1C, 0xBC, 0x00, 0x01, 0x42, 0x16, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x4A, 0x2D, 0x00, 0x01, 0x6C, 0x00, 0xFE, 0xC2, 0x42, 0x2D, 0x00, 0x11, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x11, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xB2, 0x42, 0x2D, 0x00, 0x21, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x21, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xA2, 0x0C, 0xB8, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x66, 0x00, 0xFE, 0x96, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x31, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x86, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x41, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x76, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x51, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x66, 0x60, 0x00, 0xFF, 0x60, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0xF9, 0x00, 0xFF, 0x00, 0x90, 0x20, 0x3C, /* 9 */ 0x00, 0x00, 0x00, 0x7F, 0x45, 0xF8, 0x00, 0x00, 0x26, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x26, 0xDA, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x34, 0x41, 0xF9, 0x00, 0xFF, 0x00, 0xC3, 0x42, 0x10, 0x4E, 0xD2, 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x66, 0x21, 0xFC, 0x00, 0x66, 0x80, 0x00, 0x00, 0x5C, 0x42, 0x2D, 0x00, 0x01, 0x12, 0xBC, 0x00, 0x01, 0x42, 0x11, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF4, 0x4A, 0x2D, 0x00, 0x01, 0x6C, 0xD2, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x96, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xA6, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xB6, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x50, 0x80, 0x00, 0x42, 0x2D, 0x00, 0x11, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x11, 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x60, 0x42, 0x2D, 0x00, 0x21, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x21, 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x50, 0x20, 0x38, 0x00, 0x00, 0xB0, 0xBC, 0x43, 0x6F, 0x6D, 0x70, 0x66, 0x00, 0xFF, 0x42, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x31, 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x32, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x41, 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x22, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x51, 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x12, 0x53, 0x88, 0x1E, 0x10, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x41, 0xF8, /* A */ 0x00, 0x00, 0x22, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x22, 0xD8, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x41, 0xF9, 0x00, 0xFF, 0x00, 0xC0, 0x43, 0xE8, 0x00, 0x01, 0x45, 0xE8, 0x00, 0x02, 0x47, 0xE8, 0x00, 0x03, 0x17, 0x7C, 0x00, 0x80, 0x00, 0x00, 0x70, 0x07, 0x42, 0x10, 0x28, 0x7C, 0x00, 0x00, 0x81, 0x42, 0x42, 0x12, 0x42, 0x12, 0x42, 0x12, 0x72, 0x02, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x72, 0xFF, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xFC, 0x72, 0x01, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x12, 0xBC, 0x00, 0x08, 0x72, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xF4, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x11, 0x92, 0x3C, 0x00, 0x20, 0x01, 0x10, 0x67, 0xFC, 0x82, 0x11, 0x67, 0x00, 0x00, 0x5C, 0xD9, 0xFC, 0x00, 0x00, 0x00, 0x09, 0x10, 0xBC, 0x00, 0x28, 0x72, 0x02, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x72, 0xFF, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xFC, 0x72, 0x01, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x12, 0xBC, 0x00, 0x08, 0x72, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xF4, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x11, 0x92, 0x3C, 0x00, 0x20, 0x01, 0x10, 0x67, 0xFC, 0x82, 0x11, 0xC2, 0x3C, 0x00, 0xFC, 0x66, 0x00, 0xFF, 0x4E, 0x72, 0x08, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x01, 0x10, 0x67, 0xFC, 0x14, 0x11, 0x94, 0x3C, 0x00, 0x40, 0x01, 0x10, 0x67, 0xFC, 0xD4, 0x11, 0x94, 0x3C, 0x00, 0x80, 0x72, 0x04, 0x01, 0x10, 0x67, 0xFC, 0x1E, 0x11, 0x51, 0xC9, 0xFF, 0xF8, 0xC4, 0x3C, 0x00, 0xF8, 0x66, 0x00, 0xFF, 0x16, 0x1E, 0x12, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x43, 0xF8, /* B */ 0x00, 0x00, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x24, 0xD9, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x49, 0xF9, 0x00, 0xFF, 0x00, 0xC0, 0x4B, 0xEC, 0x00, 0x01, 0x4D, 0xEC, 0x00, 0x02, 0x43, 0xEC, 0x00, 0x02, 0x19, 0x7C, 0x00, 0x80, 0x00, 0x03, 0x76, 0x07, 0x18, 0xBC, 0x00, 0x28, 0x20, 0x7C, 0x00, 0x00, 0x81, 0xCE, 0x42, 0x16, 0x42, 0x16, 0x42, 0x16, 0x70, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xC8, 0xFF, 0xF8, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x96, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xA6, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xB6, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x07, 0x16, 0x67, 0xFC, 0x1A, 0xBC, 0x00, 0x08, 0x3C, 0x3C, 0x00, 0x03, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x51, 0xCE, 0xFF, 0xF2, 0x07, 0x14, 0x67, 0xFC, 0x14, 0x15, 0x94, 0x3C, 0x00, 0x22, 0x07, 0x14, 0x67, 0xFC, 0x84, 0x15, 0x66, 0x00, 0x00, 0x48, 0x74, 0x08, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xCA, 0xFF, 0xF8, 0x07, 0x16, 0x67, 0xFC, 0x07, 0x14, 0x67, 0xFC, 0x10, 0x15, 0x90, 0x3C, 0x00, 0x42, 0x07, 0x14, 0x67, 0xFC, 0xD0, 0x15, 0x90, 0x3C, 0x00, 0x80, 0x74, 0x03, 0x07, 0x14, 0x67, 0xFC, 0x1E, 0x15, 0x51, 0xCA, 0xFF, 0xF8, 0x4A, 0x00, 0x66, 0x00, 0x00, 0x12, 0x1E, 0x11, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x4D, 0xF9, 0x00, 0xFF, 0x00, 0x90, 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x66, 0x21, 0xFC, 0x00, 0x66, 0x80, 0x00, 0x00, 0x5C, 0x42, 0x2D, 0x00, 0x01, 0x1C, 0xBC, 0x00, 0x01, 0x42, 0x16, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x4A, 0x2D, 0x00, 0x01, 0x6C, 0x00, 0xFE, 0xC0, 0x42, 0x2D, 0x00, 0x11, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x11, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xB0, 0x42, 0x2D, 0x00, 0x21, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x21, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xA0, 0x0C, 0xB8, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x66, 0x00, 0xFE, 0x94, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x31, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x84, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x41, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x74, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x51, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x64, 0x60, 0x00, 0xFF, 0x60, 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, /* C */ 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, 0xDC, 0x1B, 0x5C, 0x60, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0xCD, 0x5C, 0x70, 0x00, 0x5C, 0x71, 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xEA, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x20, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0x1C, 0x10, 0x1A, 0xC0, 0x00, 0x00, 0x56, 0x94, 0xA0, 0x09, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, 0xA0, 0x40, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, 0x00, 0xCC, 0x17, 0x78, 0x1C, 0x00, 0x1A, 0xC0, 0x00, 0x00, 0x16, 0xCE, 0xD8, 0x71, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0xA7, 0xA9, 0xC0, 0xFE, 0x00, 0x90, 0x5C, 0xA8, 0x80, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, 0xA2, 0xCD, 0x07, 0x7D, 0x9C, 0xAF, 0x80, 0x61, 0x1A, 0xBE, 0xB5, 0x5C, 0xA8, 0x80, 0x71, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x71, 0x1A, 0xBE, 0xA1, 0x5C, 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x81, 0x1A, 0xBE, 0x8D, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, 0x1A, 0xBE, 0x82, 0x5C, 0xA8, 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x91, 0x1A, 0xBE, 0x6E, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xA1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xA1, 0x1A, 0xBE, 0x5A, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0x46, 0xEA, 0xBF, 0x61, 0x03, 0x8F, 0x1E, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0xA7, 0xA9, 0xC0, 0xFE, 0x00, 0x90, 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, /* D */ 0x0E, 0x03, 0x00, 0x67, 0xA8, 0xC0, 0xFE, 0x00, 0xC3, 0x5C, 0x48, 0x00, 0x5C, 0xA8, 0xC0, 0x00, 0x00, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, 0xA2, 0xCD, 0x07, 0x7D, 0x9C, 0xAF, 0xC0, 0x00, 0x00, 0x61, 0x1A, 0x66, 0xEA, 0xC0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x5C, 0xA8, 0x80, 0x71, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x71, 0x1A, 0xBF, 0x4B, 0x5C, 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x81, 0x1A, 0xBF, 0x37, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, 0x1A, 0xBF, 0x2C, 0x5C, 0xA8, 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x91, 0x1A, 0xBF, 0x18, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xA1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xA1, 0x1A, 0xBF, 0x04, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0xF0, 0x8F, 0x0F, 0xCE, 0xD8, 0x49, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, /* E */ 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, 0xDC, 0x1B, 0x5C, 0x60, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0x33, 0x5C, 0x70, 0x00, 0x5C, 0x71, 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xDC, 0x07, 0xA2, 0xA2, 0xCC, 0x07, 0x7E, 0x5C, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x20, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0x1C, 0x10, 0x0A, 0xC0, 0x00, 0x00, 0x60, 0xC1, 0xA1, 0x00, 0x09, 0x14, 0xA3, 0x28, 0x00, 0xDC, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xDC, 0x07, 0xA2, 0xA2, 0xCC, 0x07, 0x7E, 0x5C, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x20, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0xA8, 0xA0, 0xFC, 0x1C, 0x10, 0x1A, 0xBF, 0x44, 0x94, 0xA0, 0x09, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, 0xA0, 0x40, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, 0x00, 0xCC, 0x17, 0x78, 0x28, 0xA0, 0xF8, 0x1C, 0x00, 0x1A, 0xBF, 0x03, 0xCE, 0xD8, 0x71, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, /* F */ 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, 0xDC, 0x1B, 0x14, 0xA3, 0x28, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0xCD, 0x5C, 0x70, 0x00, 0x5C, 0x71, 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xEA, 0xC0, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x22, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0x1C, 0x10, 0x1A, 0xC0, 0x00, 0x00, 0x56, 0x94, 0xA0, 0x09, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, 0xA0, 0x42, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, 0x00, 0xCC, 0x17, 0x78, 0x1C, 0x00, 0x1A, 0xC0, 0x00, 0x00, 0x16, 0xCE, 0xD8, 0x71, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0xA7, 0xA9, 0xC0, 0xFE, 0x00, 0x90, 0x5C, 0xA8, 0x80, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, 0xA2, 0xCD, 0x07, 0x7D, 0x9C, 0xAF, 0x80, 0x61, 0x1A, 0xBE, 0xB5, 0x5C, 0xA8, 0x80, 0x71, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x71, 0x1A, 0xBE, 0xA1, 0x5C, 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x81, 0x1A, 0xBE, 0x8D, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, 0x1A, 0xBE, 0x82, 0x5C, 0xA8, 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x91, 0x1A, 0xBE, 0x6E, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xA1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xA1, 0x1A, 0xBE, 0x5A, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0x46, 0xEA, 0xBF, 0x61, 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; /* returns TRUE iff there exists a disk with VERBOSE */ static int32 disk1a_hasProperty(uint32 property) { int32 i; for (i = 0; i < DISK1A_MAX_DRIVES; i++) if (disk1a_dev.units[i].flags & property) return TRUE; return FALSE; } /* Reset routine */ static t_stat disk1a_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ if (disk1a_hasProperty(UNIT_DISK1A_ROM)) sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &disk1arom, TRUE); sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk1adev, TRUE); } else { /* Connect DISK1A ROM at base address */ if (disk1a_hasProperty(UNIT_DISK1A_ROM)) if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &disk1arom, FALSE) != 0) { printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); return SCPE_ARG; } /* Connect DISK1A at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk1adev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } static t_stat disk1a_boot(int32 unitno, DEVICE *dptr) { bootstrap &= 0xF; DBG_PRINT(("Booting DISK1A Controller, bootstrap=%d" NLP, bootstrap)); /* Re-enable the ROM in case it was disabled */ disk1a_info->rom_disabled = FALSE; /* Set the PC to 0, and go. */ *((int32 *) sim_PC->loc) = 0; return SCPE_OK; } /* Attach routine */ static t_stat disk1a_attach(UNIT *uptr, char *cptr) { t_stat r; r = i8272_attach(uptr, cptr); return r; } /* Detach routine */ static t_stat disk1a_detach(UNIT *uptr) { t_stat r; r = i8272_detach(uptr); return r; } static int32 disk1arom(const int32 Addr, const int32 write, const int32 data) { /* DBG_PRINT(("DISK1A: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ if(write) { disk1aram[Addr & 0x1FF] = data; return 0; } else { bootstrap &= 0xF; if(disk1a_info->rom_disabled == FALSE) { return(disk1a_rom[bootstrap][Addr & 0x1FF]); } else { return(disk1aram[Addr & 0x1FF]); } } } static int32 disk1adev(const int32 port, const int32 io, const int32 data) { int32 result; if(io) { TRACE_PRINT(VERBOSE_MSG, ("DISK1A: " ADDRESS_FORMAT " OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) DISK1A_Write(port, data); result = 0; } else { result = DISK1A_Read(port); TRACE_PRINT(VERBOSE_MSG, ("DISK1A: " ADDRESS_FORMAT " IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) } return result; } #define DISK1A_DRIVE_STATUS 2 /* R=Drive Status Register / W=DMA Address Register */ #define DISK1A_MOTOR 3 /* R=Unused / W=Motor Control Register */ #define BOOT_PROM_DISABLE 0x01 #define FLOPPY_MOTORS 0xF0 extern uint8 i8272_irq; static uint8 DISK1A_Read(const uint32 Addr) { uint8 cData; cData = 0x00; switch(Addr & 0x3) { case I8272_FDC_MSR: case I8272_FDC_DATA: cData = I8272_Read(Addr); break; case DISK1A_DRIVE_STATUS: cData = i8272_irq ? 0x81 : 0x01; /* Ready */ TRACE_PRINT(STATUS_MSG, ("DISK1A: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)) break; case DISK1A_MOTOR: TRACE_PRINT(VERBOSE_MSG, ("DISK1A: " ADDRESS_FORMAT " Error, can't read from MOTOR register." NLP, PCX)) cData = 0xFF; /* Return High-Z data */ break; } return (cData); } static uint8 DISK1A_Write(const uint32 Addr, uint8 cData) { uint8 result = 0; switch(Addr & 0x3) { case I8272_FDC_MSR: case I8272_FDC_DATA: result = I8272_Write(Addr, cData); break; case DISK1A_DRIVE_STATUS: /* DMA Address */ disk1a_info->dma_addr <<= 8; disk1a_info->dma_addr &= 0x00FFFF00; disk1a_info->dma_addr |= cData; TRACE_PRINT(RD_DATA_MSG, ("DISK1A: " ADDRESS_FORMAT " DMA Address=%06x" NLP, PCX, disk1a_info->dma_addr)) I8272_Set_DMA(disk1a_info->dma_addr); break; case DISK1A_MOTOR: TRACE_PRINT(CMD_MSG, ("DISK1A: " ADDRESS_FORMAT " write Motor Reg=0x%02x" NLP, PCX, cData)) if((cData & BOOT_PROM_DISABLE) == 0) { TRACE_PRINT(CMD_MSG, ("DISK1A: " ADDRESS_FORMAT " Boot ROM disabled" NLP, PCX)) /* Unmap Boot ROM */ disk1a_info->rom_disabled = TRUE; } TRACE_PRINT(CMD_MSG, ("DISK1A: " ADDRESS_FORMAT " Motors = %x" NLP, PCX, (cData & FLOPPY_MOTORS) >> 4)) break; } return (result); } #define SS1_VI4_INT 4 /* IF3 Tx interrupts tied to VI3 */ void raise_disk1a_interrupt(void) { TRACE_PRINT(IRQ_MSG, ("DISK1A: " ADDRESS_FORMAT " Interrupt" NLP, PCX)); raise_ss1_interrupt(SS1_VI4_INT); } simh-3.8.1/AltairZ80/altairz80_defs.h0000644000175000017500000001333611043117052015347 0ustar vlmvlm/* altairz80_defs.h: MITS Altair simulator definitions Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 */ #include "sim_defs.h" /* simulator definitions */ #define MAXBANKSIZE 65536 /* maximum memory size, a power of 2 */ #define MAXBANKSIZELOG2 16 /* log2 of MAXBANKSIZE */ #define MAXBANKS 16 /* max number of memory banks, a power of 2 */ #define MAXBANKSLOG2 4 /* log2 of MAXBANKS */ #define MAXMEMORY (MAXBANKS * MAXBANKSIZE) /* maximum, total memory size */ #define ADDRMASK (MAXBANKSIZE - 1) /* address mask */ #define ADDRMASKEXTENDED (MAXMEMORY - 1) /* extended address mask */ #define BANKMASK (MAXBANKS - 1) /* bank mask */ #define MEMORYSIZE (cpu_unit.capac) /* actual memory size */ #define KB 1024 /* kilo byte */ #define KBLOG2 10 /* log2 of KB */ #define ALTAIR_ROM_LOW 0xff00 /* start address of regular Altair ROM */ #define RESOURCE_TYPE_MEMORY 1 #define RESOURCE_TYPE_IO 2 #define NUM_OF_DSK 8 /* NUM_OF_DSK must be power of two */ #define LDA_INSTRUCTION 0x3e /* op-code for LD A,<8-bit value> instruction */ #define UNIT_NO_OFFSET_1 0x37 /* LD A, */ #define UNIT_NO_OFFSET_2 0xb4 /* LD a,80h | */ #define CHIP_TYPE_8080 0 #define CHIP_TYPE_Z80 1 #define CHIP_TYPE_8086 2 #define MAX_CHIP_TYPE CHIP_TYPE_8086 /* simulator stop codes */ #define STOP_HALT 0 /* HALT */ #define STOP_IBKPT 1 /* breakpoint (program counter) */ #define STOP_MEM 2 /* breakpoint (memory access) */ #define STOP_OPCODE 3 /* invalid operation encountered (8080, Z80, 8086) */ #define UNIT_CPU_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */ #define UNIT_CPU_OPSTOP (1 << UNIT_CPU_V_OPSTOP) #define UNIT_CPU_V_BANKED (UNIT_V_UF+1) /* banked memory is used */ #define UNIT_CPU_BANKED (1 << UNIT_CPU_V_BANKED) #define UNIT_CPU_V_ALTAIRROM (UNIT_V_UF+2) /* ALTAIR ROM exists */ #define UNIT_CPU_ALTAIRROM (1 << UNIT_CPU_V_ALTAIRROM) #define UNIT_CPU_V_VERBOSE (UNIT_V_UF+3) /* warn if ROM is written to */ #define UNIT_CPU_VERBOSE (1 << UNIT_CPU_V_VERBOSE) #define UNIT_CPU_V_MMU (UNIT_V_UF+4) /* use MMU and slower CPU */ #define UNIT_CPU_MMU (1 << UNIT_CPU_V_MMU) #define UNIT_CPU_V_STOPONHALT (UNIT_V_UF+5) /* stop simulation on HALT */ #define UNIT_CPU_STOPONHALT (1 << UNIT_CPU_V_STOPONHALT) #define UNIT_CPU_V_SWITCHER (UNIT_V_UF+6) /* switcher 8086 <--> 8080/Z80 enabled */ #define UNIT_CPU_SWITCHER (1 << UNIT_CPU_V_SWITCHER) #define UNIX_PLATFORM (defined (__linux) || defined(__NetBSD__) \ || defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__APPLE__)) #define ADDRESS_FORMAT "[0x%05x]" /* use NLP for new line printing while the simulation is running */ #if UNIX_PLATFORM #define NLP "\r\n" #else #define NLP "\n" #endif #if defined (__MWERKS__) && defined (macintosh) #define __FUNCTION__ __FILE__ #endif typedef struct { uint32 mem_base; /* Memory Base Address */ uint32 mem_size; /* Memory Address space requirement */ uint32 io_base; /* I/O Base Address */ uint32 io_size; /* I/O Address Space requirement */ } PNP_INFO; simh-3.8.1/AltairZ80/i86_decode.c0000644000175000017500000007734611111141270014442 0ustar vlmvlm/* * Dos/PC Emulator * Copyright (C) 1991 Jim Hudgens * * * The file is part of GDE. * * GDE is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * GDE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GDE; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "altairz80_defs.h" #include "i86.h" extern uint32 GetBYTEExtended(register uint32 Addr); extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value); extern int32 AX_S; /* AX register (8086) */ extern int32 BX_S; /* BX register (8086) */ extern int32 CX_S; /* CX register (8086) */ extern int32 DX_S; /* DX register (8086) */ extern int32 CS_S; /* CS register (8086) */ extern int32 DS_S; /* DS register (8086) */ extern int32 ES_S; /* ES register (8086) */ extern int32 SS_S; /* SS register (8086) */ extern int32 DI_S; /* DI register (8086) */ extern int32 SI_S; /* SI register (8086) */ extern int32 BP_S; /* BP register (8086) */ extern int32 SPX_S; /* SP register (8086) */ extern int32 IP_S; /* IP register (8086) */ extern int32 FLAGS_S; /* flags register (8086) */ extern int32 PCX_S; /* PC register (8086), 20 bit */ extern int32 sim_interval; extern uint32 PCX; /* external view of PC */ extern uint32 sim_brk_summ; extern UNIT cpu_unit; void i86_intr_raise(PC_ENV *m,uint8 intrnum); void cpu8086reset(void); t_stat sim_instr_8086(void); void cpu8086_intr(uint8 intrnum); /* $Log: $ * Revision 0.05 1992/04/12 23:16:42 hudgens * Many changes. Added support for the QUICK_FETCH option, * so that memory accesses are faster. Now compiles with gcc -Wall * and gcc -traditional and Sun cc. * * Revision 0.04 1991/07/30 01:59:56 hudgens * added copyright. * * Revision 0.03 1991/06/03 01:02:09 hudgens * fixed minor problems due to unsigned to signed short integer * promotions. * * Revision 0.02 1991/03/31 01:29:39 hudgens * Fixed segment handling (overrides, default segment accessed) in * routines decode_rmXX_address and the {fetch,store}_data_{byte,word}. * * Revision 0.01 1991/03/30 21:59:49 hudgens * Initial checkin. * * */ /* this file includes subroutines which do: stuff involving decoding instruction formats. stuff involving accessess of immediate data via IP. etc. */ static void i86_intr_handle(PC_ENV *m) { uint16 tmp; uint8 intno; if (intr & INTR_SYNCH) /* raised by something */ { intno = m->intno; tmp = (uint16) mem_access_word(m, intno * 4); { tmp = m->R_FLG; push_word(m, tmp); CLEAR_FLAG(m, F_IF); CLEAR_FLAG(m, F_TF); /* [JCE] If we're interrupting between a segment override (or REP override) * and the following instruction, decrease IP to get back to the prefix */ if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { --m->R_IP; } /* [JCE] CS and IP were the wrong way round... */ push_word(m, m->R_CS); push_word(m, m->R_IP); tmp = mem_access_word(m, intno * 4); m->R_IP = tmp; tmp = mem_access_word(m, intno * 4 + 2); m->R_CS = tmp; } intr &= ~INTR_SYNCH; /* [JCE] Dealt with, reset flag */ } /* The interrupt code can't pick up the segment override status. */ DECODE_CLEAR_SEGOVR(m); } void i86_intr_raise(PC_ENV *m,uint8 intrnum) { m->intno = intrnum; intr |= INTR_SYNCH; } static PC_ENV cpu8086; void cpu8086_intr(uint8 intrnum) { i86_intr_raise(&cpu8086, intrnum); } static void setViewRegisters(void) { FLAGS_S = cpu8086.R_FLG; AX_S = cpu8086.R_AX; BX_S = cpu8086.R_BX; CX_S = cpu8086.R_CX; DX_S = cpu8086.R_DX; SPX_S = cpu8086.R_SP; BP_S = cpu8086.R_BP; SI_S = cpu8086.R_SI; DI_S = cpu8086.R_DI; ES_S = cpu8086.R_ES; CS_S = cpu8086.R_CS; SS_S = cpu8086.R_SS; DS_S = cpu8086.R_DS; IP_S = cpu8086.R_IP; } static void setCPURegisters(void) { cpu8086.R_FLG = FLAGS_S; cpu8086.R_AX = AX_S; cpu8086.R_BX = BX_S; cpu8086.R_CX = CX_S; cpu8086.R_DX = DX_S; cpu8086.R_SP = SPX_S; cpu8086.R_BP = BP_S; cpu8086.R_SI = SI_S; cpu8086.R_DI = DI_S; cpu8086.R_ES = ES_S; cpu8086.R_CS = CS_S; cpu8086.R_SS = SS_S; cpu8086.R_DS = DS_S; cpu8086.R_IP = IP_S; } void cpu8086reset(void) { cpu8086.R_AX = 0x1961; if ((cpu8086.R_AH != 0x19) || (cpu8086.R_AL != 0x61)) { printf("Fatal endian error - make sure to compile with '#define LOWFIRST %i'\n", 1 - LOWFIRST); exit(1); } /* 16 bit registers */ cpu8086.R_AX = 0; cpu8086.R_BX = 0; cpu8086.R_CX = 0; cpu8086.R_DX = 0; /* special registers */ cpu8086.R_SP = 0; cpu8086.R_BP = 0; cpu8086.R_SI = 0; cpu8086.R_DI = 0; cpu8086.R_IP = 0; cpu8086.R_FLG = F_ALWAYS_ON; /* segment registers */ cpu8086.R_CS = 0; cpu8086.R_DS = 0; cpu8086.R_SS = 0; cpu8086.R_ES = 0; setViewRegisters(); } static uint32 getFullPC(void) { return cpu8086.R_IP + (cpu8086.R_CS << 4); } extern int32 switch_cpu_now; /* hharte */ t_stat sim_instr_8086(void) { t_stat reason = SCPE_OK; uint8 op1; int32 newIP; setCPURegisters(); intr = 0; newIP = PCX_S - 16 * CS_S; switch_cpu_now = TRUE; /* hharte */ if ((0 <= newIP) && (newIP <= 0xffff)) cpu8086.R_IP = newIP; else { if (CS_S != ((PCX_S & 0xf0000) >> 4)) { cpu8086.R_CS = (PCX_S & 0xf0000) >> 4; if (cpu_unit.flags & UNIT_CPU_VERBOSE) printf("CPU: " ADDRESS_FORMAT " Segment register CS set to %04x" NLP, PCX, cpu8086.R_CS); } cpu8086.R_IP = PCX_S & 0xffff; } while (switch_cpu_now == TRUE) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ #if !UNIX_PLATFORM if ((reason = sim_os_poll_kbd()) == SCPE_STOP) /* poll on platforms without reliable signalling */ break; #endif if ( (reason = sim_process_event()) ) break; } if (sim_brk_summ && sim_brk_test(getFullPC(), SWMASK('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } PCX = getFullPC(); op1 = GetBYTEExtended((((uint32)cpu8086.R_CS<<4) + cpu8086.R_IP) & 0xFFFFF); if (sim_brk_summ && sim_brk_test(op1, (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } sim_interval--; cpu8086.R_IP++; (*(i86_optab[op1]))(&cpu8086); if (intr & INTR_HALTED) { reason = STOP_HALT; intr &= ~INTR_HALTED; break; } if (intr & INTR_ILLEGAL_OPCODE) { intr &= ~INTR_ILLEGAL_OPCODE; if (cpu_unit.flags & UNIT_CPU_OPSTOP) { reason = STOP_OPCODE; break; } } if (((intr & INTR_SYNCH) && (cpu8086.intno == 0 || cpu8086.intno == 2)) || (ACCESS_FLAG(&cpu8086, F_IF))) { /* [JCE] Reversed the sense of this ACCESS_FLAG; it's set for interrupts enabled, not interrupts blocked i.e. either not blockable (intr 0 or 2) or the IF flag not set so interrupts not blocked */ /* hharte: if a segment override exists, then treat that as "atomic" and do not handle * an interrupt until the override is cleared. * Not sure if this is the way an 8086 really works, need to find out for sure. * Also, what about the REPE prefix? */ if ((cpu8086.sysmode & SYSMODE_SEGMASK) == 0) { i86_intr_handle(&cpu8086); } } } /* It we stopped processing instructions because of a switch to the other * CPU, then fixup the reason code. */ if (switch_cpu_now == FALSE) { reason = SCPE_OK; PCX += 2; PCX_S = PCX; } else { PCX_S = (reason == STOP_HALT) | (reason == STOP_OPCODE) ? PCX : getFullPC(); } setViewRegisters(); return reason; } void halt_sys(PC_ENV *m) { intr |= INTR_HALTED; } /* once the instruction is fetched, an optional byte follows which has 3 fields encoded in it. This routine fetches the byte and breaks into the three fields. This has been changed, in an attempt to reduce the amount of executed code for this frequently executed subroutine. If this works, then it may pay to somehow inline it. */ #ifdef NOTDEF /* this code generated the following table */ main() { int i; printf("\n\nstruct modrm{ uint8 mod,rh,rl;} modrmtab[] = {\n"); for (i=0; i<256; i++) { printf("{%d,%d,%d}, ",((i&0xc0)>>6),((i&0x38)>>3),(i&0x07)); if (i%4==3) printf("/* %d to %d */\n",i&0xfc,i); } printf("};\n\n"); } #endif struct modrm { uint16 mod, rh, rl; }; static struct modrm modrmtab[] = { {0,0,0}, {0,0,1}, {0,0,2}, {0,0,3}, /* 0 to 3 */ {0,0,4}, {0,0,5}, {0,0,6}, {0,0,7}, /* 4 to 7 */ {0,1,0}, {0,1,1}, {0,1,2}, {0,1,3}, /* 8 to 11 */ {0,1,4}, {0,1,5}, {0,1,6}, {0,1,7}, /* 12 to 15 */ {0,2,0}, {0,2,1}, {0,2,2}, {0,2,3}, /* 16 to 19 */ {0,2,4}, {0,2,5}, {0,2,6}, {0,2,7}, /* 20 to 23 */ {0,3,0}, {0,3,1}, {0,3,2}, {0,3,3}, /* 24 to 27 */ {0,3,4}, {0,3,5}, {0,3,6}, {0,3,7}, /* 28 to 31 */ {0,4,0}, {0,4,1}, {0,4,2}, {0,4,3}, /* 32 to 35 */ {0,4,4}, {0,4,5}, {0,4,6}, {0,4,7}, /* 36 to 39 */ {0,5,0}, {0,5,1}, {0,5,2}, {0,5,3}, /* 40 to 43 */ {0,5,4}, {0,5,5}, {0,5,6}, {0,5,7}, /* 44 to 47 */ {0,6,0}, {0,6,1}, {0,6,2}, {0,6,3}, /* 48 to 51 */ {0,6,4}, {0,6,5}, {0,6,6}, {0,6,7}, /* 52 to 55 */ {0,7,0}, {0,7,1}, {0,7,2}, {0,7,3}, /* 56 to 59 */ {0,7,4}, {0,7,5}, {0,7,6}, {0,7,7}, /* 60 to 63 */ {1,0,0}, {1,0,1}, {1,0,2}, {1,0,3}, /* 64 to 67 */ {1,0,4}, {1,0,5}, {1,0,6}, {1,0,7}, /* 68 to 71 */ {1,1,0}, {1,1,1}, {1,1,2}, {1,1,3}, /* 72 to 75 */ {1,1,4}, {1,1,5}, {1,1,6}, {1,1,7}, /* 76 to 79 */ {1,2,0}, {1,2,1}, {1,2,2}, {1,2,3}, /* 80 to 83 */ {1,2,4}, {1,2,5}, {1,2,6}, {1,2,7}, /* 84 to 87 */ {1,3,0}, {1,3,1}, {1,3,2}, {1,3,3}, /* 88 to 91 */ {1,3,4}, {1,3,5}, {1,3,6}, {1,3,7}, /* 92 to 95 */ {1,4,0}, {1,4,1}, {1,4,2}, {1,4,3}, /* 96 to 99 */ {1,4,4}, {1,4,5}, {1,4,6}, {1,4,7}, /* 100 to 103 */ {1,5,0}, {1,5,1}, {1,5,2}, {1,5,3}, /* 104 to 107 */ {1,5,4}, {1,5,5}, {1,5,6}, {1,5,7}, /* 108 to 111 */ {1,6,0}, {1,6,1}, {1,6,2}, {1,6,3}, /* 112 to 115 */ {1,6,4}, {1,6,5}, {1,6,6}, {1,6,7}, /* 116 to 119 */ {1,7,0}, {1,7,1}, {1,7,2}, {1,7,3}, /* 120 to 123 */ {1,7,4}, {1,7,5}, {1,7,6}, {1,7,7}, /* 124 to 127 */ {2,0,0}, {2,0,1}, {2,0,2}, {2,0,3}, /* 128 to 131 */ {2,0,4}, {2,0,5}, {2,0,6}, {2,0,7}, /* 132 to 135 */ {2,1,0}, {2,1,1}, {2,1,2}, {2,1,3}, /* 136 to 139 */ {2,1,4}, {2,1,5}, {2,1,6}, {2,1,7}, /* 140 to 143 */ {2,2,0}, {2,2,1}, {2,2,2}, {2,2,3}, /* 144 to 147 */ {2,2,4}, {2,2,5}, {2,2,6}, {2,2,7}, /* 148 to 151 */ {2,3,0}, {2,3,1}, {2,3,2}, {2,3,3}, /* 152 to 155 */ {2,3,4}, {2,3,5}, {2,3,6}, {2,3,7}, /* 156 to 159 */ {2,4,0}, {2,4,1}, {2,4,2}, {2,4,3}, /* 160 to 163 */ {2,4,4}, {2,4,5}, {2,4,6}, {2,4,7}, /* 164 to 167 */ {2,5,0}, {2,5,1}, {2,5,2}, {2,5,3}, /* 168 to 171 */ {2,5,4}, {2,5,5}, {2,5,6}, {2,5,7}, /* 172 to 175 */ {2,6,0}, {2,6,1}, {2,6,2}, {2,6,3}, /* 176 to 179 */ {2,6,4}, {2,6,5}, {2,6,6}, {2,6,7}, /* 180 to 183 */ {2,7,0}, {2,7,1}, {2,7,2}, {2,7,3}, /* 184 to 187 */ {2,7,4}, {2,7,5}, {2,7,6}, {2,7,7}, /* 188 to 191 */ {3,0,0}, {3,0,1}, {3,0,2}, {3,0,3}, /* 192 to 195 */ {3,0,4}, {3,0,5}, {3,0,6}, {3,0,7}, /* 196 to 199 */ {3,1,0}, {3,1,1}, {3,1,2}, {3,1,3}, /* 200 to 203 */ {3,1,4}, {3,1,5}, {3,1,6}, {3,1,7}, /* 204 to 207 */ {3,2,0}, {3,2,1}, {3,2,2}, {3,2,3}, /* 208 to 211 */ {3,2,4}, {3,2,5}, {3,2,6}, {3,2,7}, /* 212 to 215 */ {3,3,0}, {3,3,1}, {3,3,2}, {3,3,3}, /* 216 to 219 */ {3,3,4}, {3,3,5}, {3,3,6}, {3,3,7}, /* 220 to 223 */ {3,4,0}, {3,4,1}, {3,4,2}, {3,4,3}, /* 224 to 227 */ {3,4,4}, {3,4,5}, {3,4,6}, {3,4,7}, /* 228 to 231 */ {3,5,0}, {3,5,1}, {3,5,2}, {3,5,3}, /* 232 to 235 */ {3,5,4}, {3,5,5}, {3,5,6}, {3,5,7}, /* 236 to 239 */ {3,6,0}, {3,6,1}, {3,6,2}, {3,6,3}, /* 240 to 243 */ {3,6,4}, {3,6,5}, {3,6,6}, {3,6,7}, /* 244 to 247 */ {3,7,0}, {3,7,1}, {3,7,2}, {3,7,3}, /* 248 to 251 */ {3,7,4}, {3,7,5}, {3,7,6}, {3,7,7}, /* 252 to 255 */ }; void fetch_decode_modrm(PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl) { uint8 fetched; register struct modrm *p; /* do the fetch in real mode. Shift the CS segment register over by 4 bits, and add in the IP register. Index into the system memory. */ /* [JCE] Wrap at 1Mb (the A20 gate) */ fetched = GetBYTEExtended(((m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); #ifdef NOTDEF *mod = ((fetched&0xc0)>>6); *regh= ((fetched&0x38)>>3); *regl= (fetched&0x7); #else p = modrmtab + fetched; *mod = p->mod; *regh= p->rh; *regl= p->rl; #endif } /* return a pointer to the register given by the R/RM field of the modrm byte, for byte operands. Also enables the decoding of instructions. */ uint8 *decode_rm_byte_register(PC_ENV *m, int reg) { switch(reg) { case 0: return &m->R_AL; break; case 1: return &m->R_CL; break; case 2: return &m->R_DL; break; case 3: return &m->R_BL; break; case 4: return &m->R_AH; break; case 5: return &m->R_CH; break; case 6: return &m->R_DH; break; case 7: return &m->R_BH; break; } halt_sys(m); return NULL; /* NOT REACHED OR REACHED ON ERROR */ } /* return a pointer to the register given by the R/RM field of the modrm byte, for word operands. Also enables the decoding of instructions. */ uint16 *decode_rm_word_register(PC_ENV *m, int reg) { switch(reg) { case 0: return &m->R_AX; break; case 1: return &m->R_CX; break; case 2: return &m->R_DX; break; case 3: return &m->R_BX; break; case 4: return &m->R_SP; break; case 5: return &m->R_BP; break; case 6: return &m->R_SI; break; case 7: return &m->R_DI; break; } halt_sys(m); return NULL; /* NOTREACHED OR REACHED ON ERROR*/ } /* return a pointer to the register given by the R/RM field of the modrm byte, for word operands, modified from above for the weirdo special case of segreg operands. Also enables the decoding of instructions. */ uint16 *decode_rm_seg_register(PC_ENV *m, int reg) { switch(reg) { case 0: return &m->R_ES; break; case 1: return &m->R_CS; break; case 2: return &m->R_SS; break; case 3: return &m->R_DS; break; case 4: case 5: case 6: case 7: break; } halt_sys(m); return NULL; /* NOT REACHED OR REACHED ON ERROR */ } /* once the instruction is fetched, an optional byte follows which has 3 fields encoded in it. This routine fetches the byte and breaks into the three fields. */ uint8 fetch_byte_imm(PC_ENV *m) { uint8 fetched; /* do the fetch in real mode. Shift the CS segment register over by 4 bits, and add in the IP register. Index into the system memory. */ /* [JCE] Wrap at 1Mb (the A20 gate) */ fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); return fetched; } uint16 fetch_word_imm(PC_ENV *m) { uint16 fetched; /* do the fetch in real mode. Shift the CS segment register over by 4 bits, and add in the IP register. Index into the system PC_ENVory. */ /* [JCE] Wrap at 1Mb (the A20 gate) */ fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); fetched |= (GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF) << 8); return fetched; } /* return the offset given by mod=00 addressing. Also enables the decoding of instructions. */ uint16 decode_rm00_address(PC_ENV *m, int rm) { uint16 offset; /* note the code which specifies the corresponding segment (ds vs ss) below in the case of [BP+..]. The assumption here is that at the point that this subroutine is called, the bit corresponding to SYSMODE_SEG_DS_SS will be zero. After every instruction except the segment override instructions, this bit (as well as any bits indicating segment overrides) will be clear. So if a SS access is needed, set this bit. Otherwise, DS access occurs (unless any of the segment override bits are set). */ switch(rm) { case 0: return (int16)m->R_BX + (int16)m->R_SI; break; case 1: return (int16)m->R_BX + (int16)m->R_DI; break; case 2: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_SI; break; case 3: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_DI; break; case 4: return m->R_SI; break; case 5: return m->R_DI; break; case 6: offset = (int16)fetch_word_imm(m); return offset; break; case 7: return m->R_BX; } halt_sys(m); return 0; } /* return the offset given by mod=01 addressing. Also enables the decoding of instructions. */ uint16 decode_rm01_address(PC_ENV *m, int rm) { int8 displacement; /* note comment on decode_rm00_address above */ displacement = (int8)fetch_byte_imm(m); /* !!!! Check this */ switch(rm) { case 0: return (int16)m->R_BX + (int16)m->R_SI + displacement; break; case 1: return (int16)m->R_BX + (int16)m->R_DI + displacement; break; case 2: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_SI + displacement; break; case 3: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_DI + displacement; break; case 4: return (int16)m->R_SI + displacement; break; case 5: return (int16)m->R_DI + displacement; break; case 6: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + displacement; break; case 7: return (int16)m->R_BX + displacement; break; } halt_sys(m); return 0; /* SHOULD NOT HAPPEN */ } /* return the offset given by mod=01 addressing. Also enables the decoding of instructions. */ uint16 decode_rm10_address(PC_ENV *m, int rm) { int16 displacement; /* note comment on decode_rm00_address above */ displacement = (int16)fetch_word_imm(m); switch(rm) { case 0: return (int16)m->R_BX + (int16)m->R_SI + displacement; break; case 1: return (int16)m->R_BX + (int16)m->R_DI + displacement; break; case 2: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_SI + displacement; break; case 3: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + (int16)m->R_DI + displacement; break; case 4: return (int16)m->R_SI + displacement; break; case 5: return (int16)m->R_DI + displacement; break; case 6: m->sysmode |= SYSMODE_SEG_DS_SS; return (int16)m->R_BP + displacement; break; case 7: return (int16)m->R_BX + displacement; break; } halt_sys(m); return 0; /*NOTREACHED */ } /* fetch a byte of data, given an offset, the current register set, and a descriptor for memory. */ uint8 fetch_data_byte(PC_ENV *m, uint16 offset) { register uint8 value; /* this code originally completely broken, and never showed up since the DS segments === SS segment in all test cases. It had been originally assumed, that all access to data would involve the DS register unless there was a segment override. Not so. Address modes such as -3[BP] or 10[BP+SI] all refer to addresses relative to the SS. So, at the minimum, all decodings of addressing modes would have to set/clear a bit describing whether the access is relative to DS or SS. That is the function of the cpu-state-varible m->sysmode. There are several potential states: repe prefix seen (handled elsewhere) repne prefix seen (ditto) cs segment override ds segment override es segment override ss segment override ds/ss select (in absense of override) Each of the above 7 items are handled with a bit in the sysmode field. The latter 5 can be implemented as a simple state machine: */ switch(m->sysmode & SYSMODE_SEGMASK) { case 0: /* default case: use ds register */ value = GetBYTEExtended(((uint32)m->R_DS<<4) + offset); break; case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); break; case SYSMODE_SEGOVR_CS: /* ds overridden */ case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: /* ss overridden, use cs register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF); break; case SYSMODE_SEGOVR_DS: /* ds overridden --- shouldn't happen, but hey. */ case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: /* ss overridden, use ds register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF); break; case SYSMODE_SEGOVR_ES: /* ds overridden */ case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: /* ss overridden, use es register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF); break; case SYSMODE_SEGOVR_SS: /* ds overridden */ case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: /* ss overridden, use ss register === should not happen */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); break; default: printf("error: should not happen: multiple overrides. " NLP); value = 0; halt_sys(m); } return value; } /* fetch a byte of data, given an offset, the current register set, and a descriptor for memory. */ uint8 fetch_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset) { register uint8 value; uint32 addr; /* note, cannot change this, since we do not know the ID of the segment. */ /* [JCE] Simulate wrap at top of memory (the A20 gate) */ /* addr = (segment << 4) + offset; */ addr = ((segment << 4) + offset) & 0xFFFFF; value = GetBYTEExtended(addr); return value; } /* fetch a byte of data, given an offset, the current register set, and a descriptor for memory. */ uint16 fetch_data_word(PC_ENV *m, uint16 offset) { uint16 value; /* See note above in fetch_data_byte. */ switch(m->sysmode & SYSMODE_SEGMASK) { case 0: /* default case: use ds register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_DS << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_SS << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; case SYSMODE_SEGOVR_CS: /* ds overridden */ case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: /* ss overridden, use cs register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_CS << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; case SYSMODE_SEGOVR_DS: /* ds overridden --- shouldn't happen, but hey. */ case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: /* ss overridden, use ds register */ /* [JCE] Wrap at 1Mb (the A20 gate) */ value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_DS << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; case SYSMODE_SEGOVR_ES: /* ds overridden */ case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: /* ss overridden, use es register */ value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_ES << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; case SYSMODE_SEGOVR_SS: /* ds overridden */ case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: /* ss overridden, use ss register === should not happen */ value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) | (GetBYTEExtended((((uint32)m->R_SS << 4) + (uint16)(offset + 1)) & 0xFFFFF) << 8); break; default: printf("error: should not happen: multiple overrides. " NLP); value = 0; halt_sys(m); } return value; } /* fetch a byte of data, given an offset, the current register set, and a descriptor for memory. */ uint16 fetch_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset) { uint16 value; uint32 addr; /* [JCE] Simulate wrap at top of memory (the A20 gate) */ /* addr = (segment << 4) + offset; */ addr = ((segment << 4) + offset) & 0xFFFFF; value = GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); return value; } /* Store a byte of data, given an offset, the current register set, and a descriptor for memory. */ void store_data_byte(PC_ENV *m, uint16 offset, uint8 val) { /* See note above in fetch_data_byte. */ uint32 addr; register uint16 segment; switch(m->sysmode & SYSMODE_SEGMASK) { case 0: /* default case: use ds register */ segment = m->R_DS; break; case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */ segment = m->R_SS; break; case SYSMODE_SEGOVR_CS: /* ds overridden */ case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: /* ss overridden, use cs register */ segment = m->R_CS; break; case SYSMODE_SEGOVR_DS: /* ds overridden --- shouldn't happen, but hey. */ case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: /* ss overridden, use ds register */ segment = m->R_DS; break; case SYSMODE_SEGOVR_ES: /* ds overridden */ case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: /* ss overridden, use es register */ segment = m->R_ES; break; case SYSMODE_SEGOVR_SS: /* ds overridden */ case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: /* ss overridden, use ss register === should not happen */ segment = m->R_SS; break; default: printf("error: should not happen: multiple overrides. " NLP); segment = 0; halt_sys(m); } /* [JCE] Simulate wrap at top of memory (the A20 gate) */ /* addr = (segment << 4) + offset; */ addr = (((uint32)segment << 4) + offset) & 0xFFFFF; PutBYTEExtended(addr, val); } void store_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset, uint8 val) { register uint32 addr; /* [JCE] Simulate wrap at top of memory (the A20 gate) */ /* addr = (segment << 4) + offset; */ addr = (((uint32)segment << 4) + offset) & 0xFFFFF; PutBYTEExtended(addr, val); } /* Store a word of data, given an offset, the current register set, and a descriptor for memory. */ void store_data_word(PC_ENV *m, uint16 offset, uint16 val) { register uint32 addr; register uint16 segment; /* See note above in fetch_data_byte. */ switch(m->sysmode & SYSMODE_SEGMASK) { case 0: /* default case: use ds register */ segment = m->R_DS; break; case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */ segment = m->R_SS; break; case SYSMODE_SEGOVR_CS: /* ds overridden */ case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: /* ss overridden, use cs register */ segment = m->R_CS; break; case SYSMODE_SEGOVR_DS: /* ds overridden --- shouldn't happen, but hey. */ case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: /* ss overridden, use ds register */ segment = m->R_DS; break; case SYSMODE_SEGOVR_ES: /* ds overridden */ case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: /* ss overridden, use es register */ segment = m->R_ES; break; case SYSMODE_SEGOVR_SS: /* ds overridden */ case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: /* ss overridden, use ss register === should not happen */ segment = m->R_SS; break; default: printf("error: should not happen: multiple overrides." NLP); segment = 0; halt_sys(m); } /* [JCE] Simulate wrap at top of memory (the A20 gate) */ /* addr = (segment << 4) + offset; */ addr = (((uint32)segment << 4) + offset) & 0xFFFFF; PutBYTEExtended(addr, val & 0xff); PutBYTEExtended(addr + 1, val >> 8); } void store_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset, uint16 val) { register uint32 addr; /* [JCE] Wrap at top of memory */ addr = ((segment << 4) + offset) & 0xFFFFF; PutBYTEExtended(addr, val & 0xff); PutBYTEExtended(addr + 1, val >> 8); } simh-3.8.1/AltairZ80/altairz80_dsk.c0000644000175000017500000006316111111141272015200 0ustar vlmvlm/* altairz80_dsk.c: MITS Altair 88-DISK Simulator Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 The 88_DISK is a 8-inch floppy controller which can control up to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. Each diskette has physically 77 tracks of 32 137-byte sectors each. The controller is interfaced to the CPU by use of 3 I/O addresses, standardly, these are device numbers 10, 11, and 12 (octal). Address Mode Function ------- ---- -------- 10 Out Selects and enables Controller and Drive 10 In Indicates status of Drive and Controller 11 Out Controls Disk Function 11 In Indicates current sector position of disk 12 Out Write data 12 In Read data Drive Select Out (Device 10 OUT): +---+---+---+---+---+---+---+---+ | C | X | X | X | Device | +---+---+---+---+---+---+---+---+ C = If this bit is 1, the disk controller selected by 'device' is cleared. If the bit is zero, 'device' is selected as the device being controlled by subsequent I/O operations. X = not used Device = value zero thru 15, selects drive to be controlled. Drive Status In (Device 10 IN): +---+---+---+---+---+---+---+---+ | R | Z | I | X | X | H | M | W | +---+---+---+---+---+---+---+---+ W - When 0, write circuit ready to write another byte. M - When 0, head movement is allowed H - When 0, indicates head is loaded for read/write X - not used (will be 0) I - When 0, indicates interrupts enabled (not used by this simulator) Z - When 0, indicates head is on track 0 R - When 0, indicates that read circuit has new byte to read Drive Control (Device 11 OUT): +---+---+---+---+---+---+---+---+ | W | C | D | E | U | H | O | I | +---+---+---+---+---+---+---+---+ I - When 1, steps head IN one track O - When 1, steps head OUT one track H - When 1, loads head to drive surface U - When 1, unloads head E - Enables interrupts (ignored by this simulator) D - Disables interrupts (ignored by this simulator) C - When 1 lowers head current (ignored by this simulator) W - When 1, starts Write Enable sequence: W bit on device 10 (see above) will go 1 and data will be read from port 12 until 137 bytes have been read by the controller from that port. The W bit will go off then, and the sector data will be written to disk. Before you do this, you must have stepped the track to the desired number, and waited until the right sector number is presented on device 11 IN, then set this bit. Sector Position (Device 11 IN): As the sectors pass by the read head, they are counted and the number of the current one is available in this register. +---+---+---+---+---+---+---+---+ | X | X | Sector Number | T | +---+---+---+---+---+---+---+---+ X = Not used Sector number = binary of the sector number currently under the head, 0-31. T = Sector True, is a 1 when the sector is positioned to read or write. */ #include "altairz80_defs.h" #include /* Debug flags */ #define IN_MSG (1 << 0) #define OUT_MSG (1 << 1) #define READ_MSG (1 << 2) #define WRITE_MSG (1 << 3) #define SECTOR_STUCK_MSG (1 << 4) #define TRACK_STUCK_MSG (1 << 5) #define VERBOSE_MSG (1 << 6) #define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) #define DSK_SECTSIZE 137 /* size of sector */ #define DSK_SECT 32 /* sectors per track */ #define MAX_TRACKS 254 /* number of tracks, original Altair has 77 tracks only */ #define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT) #define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS) #define NUM_OF_DSK_MASK (NUM_OF_DSK - 1) #define BOOTROM_SIZE_DSK 256 /* size of boot rom */ int32 dsk10(const int32 port, const int32 io, const int32 data); int32 dsk11(const int32 port, const int32 io, const int32 data); int32 dsk12(const int32 port, const int32 io, const int32 data); static t_stat dsk_boot(int32 unitno, DEVICE *dptr); static t_stat dsk_reset(DEVICE *dptr); extern REG *sim_PC; extern UNIT cpu_unit; extern uint32 PCX; extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); void install_ALTAIRbootROM(void); /* global data on status */ /* currently selected drive (values are 0 .. NUM_OF_DSK) current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */ static int32 current_disk = NUM_OF_DSK; static int32 current_track [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 current_sector [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 current_byte [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 current_flag [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static uint8 tracks [NUM_OF_DSK] = { MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS }; static int32 in9_count = 0; static int32 in9_message = FALSE; static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */ static int32 warnLevelDSK = 3; static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 warnDSK10 = 0; static int32 warnDSK11 = 0; static int32 warnDSK12 = 0; static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */ /* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */ int32 bootrom_dsk[BOOTROM_SIZE_DSK] = { 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */ 0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */ 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */ 0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */ 0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */ 0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */ 0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */ 0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */ 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */ 0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */ 0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */ 0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */ 0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */ 0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */ 0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */ 0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */ 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */ 0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */ 0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */ 0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */ 0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */ 0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */ 0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */ 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */ }; /* 88DSK Standard I/O Data Structures */ static UNIT dsk_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } }; static REG dsk_reg[] = { { DRDATA (DISK, current_disk, 4) }, { BRDATA (CURTRACK, current_track, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (CURSECTOR, current_sector, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (CURBYTE, current_byte, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (CURFLAG, current_flag, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (TRACKS, tracks, 10, 8, NUM_OF_DSK), REG_CIRC }, { DRDATA (IN9COUNT, in9_count, 4), REG_RO }, { DRDATA (IN9MESSAGE, in9_message, 4), REG_RO }, { DRDATA (DIRTY, dirty, 4), REG_RO }, { DRDATA (DSKWL, warnLevelDSK, 32) }, { BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { DRDATA (WARNDSK10, warnDSK10, 4), REG_RO }, { DRDATA (WARNDSK11, warnDSK11, 4), REG_RO }, { DRDATA (WARNDSK12, warnDSK12, 4), REG_RO }, { BRDATA (DISKBUFFER, dskbuf, 10, 8, DSK_SECTSIZE), REG_CIRC + REG_RO }, { NULL } }; static MTAB dsk_mod[] = { { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if (dsk_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB dsk_dt[] = { { "IN", IN_MSG }, { "OUT", OUT_MSG }, { "READ", READ_MSG }, { "WRITE", WRITE_MSG }, { "SECTOR_STUCK", SECTOR_STUCK_MSG }, { "TRACK_STUCK", TRACK_STUCK_MSG }, { "VERBOSE", VERBOSE_MSG }, { NULL, 0 } }; DEVICE dsk_dev = { "DSK", dsk_unit, dsk_reg, dsk_mod, 8, 10, 31, 1, 8, 8, NULL, NULL, &dsk_reset, &dsk_boot, NULL, NULL, NULL, (DEV_DISABLE | DEV_DEBUG), 0, dsk_dt, NULL, "Altair Floppy Disk DSK" }; static char* selectInOut(const int32 io) { return io == 0 ? "IN" : "OUT"; } /* service routines to handle simulator functions */ /* reset routine */ static t_stat dsk_reset(DEVICE *dptr) { int32 i; for (i = 0; i < NUM_OF_DSK; i++) { warnLock[i] = 0; warnAttached[i] = 0; } warnDSK10 = 0; warnDSK11 = 0; warnDSK12 = 0; current_disk = NUM_OF_DSK; in9_count = 0; in9_message = FALSE; sim_map_resource(0x08, 1, RESOURCE_TYPE_IO, &dsk10, dptr->flags & DEV_DIS); sim_map_resource(0x09, 1, RESOURCE_TYPE_IO, &dsk11, dptr->flags & DEV_DIS); sim_map_resource(0x0A, 1, RESOURCE_TYPE_IO, &dsk12, dptr->flags & DEV_DIS); return SCPE_OK; } void install_ALTAIRbootROM(void) { assert(install_bootrom(bootrom_dsk, BOOTROM_SIZE_DSK, ALTAIR_ROM_LOW, TRUE) == SCPE_OK); } /* The boot routine modifies the boot ROM in such a way that subsequently the specified disk is used for boot purposes. */ static t_stat dsk_boot(int32 unitno, DEVICE *dptr) { if (cpu_unit.flags & (UNIT_CPU_ALTAIRROM | UNIT_CPU_BANKED)) { /* check whether we are really modifying an LD A,<> instruction */ if ((bootrom_dsk[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION) && (bootrom_dsk[UNIT_NO_OFFSET_2 - 1] == LDA_INSTRUCTION)) { bootrom_dsk[UNIT_NO_OFFSET_1] = unitno & 0xff; /* LD A, */ bootrom_dsk[UNIT_NO_OFFSET_2] = 0x80 | (unitno & 0xff); /* LD a,80h | */ } else { /* Attempt to modify non LD A,<> instructions is refused. */ printf("Incorrect boot ROM offsets detected.\n"); return SCPE_IERR; } install_ALTAIRbootROM(); /* install modified ROM */ } *((int32 *) sim_PC->loc) = ALTAIR_ROM_LOW; return SCPE_OK; } static int32 dskseek(const UNIT *xptr) { return fseek(xptr -> fileref, DSK_TRACSIZE * current_track[current_disk] + DSK_SECTSIZE * current_sector[current_disk], SEEK_SET); } /* precondition: current_disk < NUM_OF_DSK */ static void writebuf(void) { int32 i, rtn; UNIT *uptr; i = current_byte[current_disk]; /* null-fill rest of sector if any */ while (i < DSK_SECTSIZE) dskbuf[i++] = 0; uptr = dsk_dev.units + current_disk; if (((uptr -> flags) & UNIT_DSK_WLK) == 0) { /* write enabled */ TRACE_PRINT(WRITE_MSG, ("DSK%i: " ADDRESS_FORMAT " OUT 0x0a (WRITE) D%d T%d S%d" NLP, current_disk, PCX, current_disk, current_track[current_disk], current_sector[current_disk])); if (dskseek(uptr)) { printf("DSK%i: " ADDRESS_FORMAT " fseek failed D%d T%d S%d" NLP, current_disk, PCX, current_disk, current_track[current_disk], current_sector[current_disk]); } rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref); if (rtn != 1) { printf("DSK%i: " ADDRESS_FORMAT " fwrite failed T%d S%d Return=%d" NLP, current_disk, PCX, current_track[current_disk], current_sector[current_disk], rtn); } } else if ( (dsk_dev.dctrl & VERBOSE_MSG) && (warnLock[current_disk] < warnLevelDSK) ) { /* write locked - print warning message if required */ warnLock[current_disk]++; /*05*/ printf("DSK%i: " ADDRESS_FORMAT " Attempt to write to locked DSK%d - ignored." NLP, current_disk, PCX, current_disk); } current_flag[current_disk] &= 0xfe; /* ENWD off */ current_byte[current_disk] = 0xff; dirty = FALSE; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. Each function is passed an 'io' flag, where 0 means a read from the port, and 1 means a write to the port. On input, the actual input is passed as the return value, on output, 'data' is written to the device. */ /* Disk Controller Status/Select */ /* IMPORTANT: The status flags read by port 8 IN instruction are INVERTED, that is, 0 is true and 1 is false. To handle this, the simulator keeps it's own status flags as 0=false, 1=true; and returns the COMPLEMENT of the status flags when read. This makes setting/testing of the flag bits more logical, yet meets the simulation requirement that they are reversed in hardware. */ int32 dsk10(const int32 port, const int32 io, const int32 data) { int32 current_disk_flags; in9_count = 0; if (io == 0) { /* IN: return flags */ if (current_disk >= NUM_OF_DSK) { if ((dsk_dev.dctrl & VERBOSE_MSG) && (warnDSK10 < warnLevelDSK)) { warnDSK10++; /*01*/ printf("DSK%i: " ADDRESS_FORMAT " Attempt of IN 0x08 on unattached disk - ignored." NLP, current_disk, PCX); } return 0xff; /* no drive selected - can do nothing */ } return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */ } /* OUT: Controller set/reset/enable/disable */ if (dirty) /* implies that current_disk < NUM_OF_DSK */ writebuf(); TRACE_PRINT(OUT_MSG, ("DSK%i: " ADDRESS_FORMAT " OUT 0x08: %x" NLP, current_disk, PCX, data)); current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */ current_disk_flags = (dsk_dev.units + current_disk) -> flags; if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */ if ( (dsk_dev.dctrl & VERBOSE_MSG) && (warnAttached[current_disk] < warnLevelDSK) ) { warnAttached[current_disk]++; /*02*/ printf("DSK%i: " ADDRESS_FORMAT " Attempt to select unattached DSK%d - ignored." NLP, current_disk, PCX, current_disk); } current_disk = NUM_OF_DSK; } else { current_sector[current_disk] = 0xff; /* reset internal counters */ current_byte[current_disk] = 0xff; current_flag[current_disk] = data & 0x80 ? 0 /* disable drive */ : (current_track[current_disk] == 0 ? 0x5a /* enable: head move true, track 0 if there */ : 0x1a); /* enable: head move true */ } return 0; /* ignored since OUT */ } /* Disk Drive Status/Functions */ int32 dsk11(const int32 port, const int32 io, const int32 data) { if (current_disk >= NUM_OF_DSK) { if ((dsk_dev.dctrl & VERBOSE_MSG) && (warnDSK11 < warnLevelDSK)) { warnDSK11++; /*03*/ printf("DSK%i: " ADDRESS_FORMAT " Attempt of %s 0x09 on unattached disk - ignored." NLP, current_disk, PCX, selectInOut(io)); } return 0; /* no drive selected - can do nothing */ } /* now current_disk < NUM_OF_DSK */ if (io == 0) { /* read sector position */ in9_count++; if ((dsk_dev.dctrl & SECTOR_STUCK_MSG) && (in9_count > 2 * DSK_SECT) && (!in9_message)) { in9_message = TRUE; printf("DSK%i: " ADDRESS_FORMAT " Looping on sector find." NLP, current_disk, PCX); } TRACE_PRINT(IN_MSG, ("DSK%i: " ADDRESS_FORMAT " IN 0x09" NLP, current_disk, PCX)); if (dirty) /* implies that current_disk < NUM_OF_DSK */ writebuf(); if (current_flag[current_disk] & 0x04) { /* head loaded? */ current_sector[current_disk]++; if (current_sector[current_disk] >= DSK_SECT) current_sector[current_disk] = 0; current_byte[current_disk] = 0xff; return (((current_sector[current_disk] << 1) & 0x3e) /* return 'sector true' bit = 0 (true) */ | 0xc0); /* set on 'unused' bits */ } else return 0; /* head not loaded - return 0 */ } in9_count = 0; /* drive functions */ TRACE_PRINT(OUT_MSG, ("DSK%i: " ADDRESS_FORMAT " OUT 0x09: %x" NLP, current_disk, PCX, data)); if (data & 0x01) { /* step head in */ if ((dsk_dev.dctrl & TRACK_STUCK_MSG) && (current_track[current_disk] == (tracks[current_disk] - 1))) { printf("DSK%i: " ADDRESS_FORMAT " Unnecessary step in." NLP, current_disk, PCX); } current_track[current_disk]++; if (current_track[current_disk] > (tracks[current_disk] - 1)) current_track[current_disk] = (tracks[current_disk] - 1); if (dirty) /* implies that current_disk < NUM_OF_DSK */ writebuf(); current_sector[current_disk] = 0xff; current_byte[current_disk] = 0xff; } if (data & 0x02) { /* step head out */ if ((dsk_dev.dctrl & TRACK_STUCK_MSG) && (current_track[current_disk] == 0)) { printf("DSK%i: " ADDRESS_FORMAT " Unnecessary step out." NLP, current_disk, PCX); } current_track[current_disk]--; if (current_track[current_disk] < 0) { current_track[current_disk] = 0; current_flag[current_disk] |= 0x40; /* track 0 if there */ } if (dirty) /* implies that current_disk < NUM_OF_DSK */ writebuf(); current_sector[current_disk] = 0xff; current_byte[current_disk] = 0xff; } if (dirty) /* implies that current_disk < NUM_OF_DSK */ writebuf(); if (data & 0x04) { /* head load */ current_flag[current_disk] |= 0x04; /* turn on head loaded bit */ current_flag[current_disk] |= 0x80; /* turn on 'read data available' */ } if (data & 0x08) { /* head unload */ current_flag[current_disk] &= 0xfb; /* turn off 'head loaded' bit */ current_flag[current_disk] &= 0x7f; /* turn off 'read data available' */ current_sector[current_disk] = 0xff; current_byte[current_disk] = 0xff; } /* interrupts & head current are ignored */ if (data & 0x80) { /* write sequence start */ current_byte[current_disk] = 0; current_flag[current_disk] |= 0x01; /* enter new write data on */ } return 0; /* ignored since OUT */ } /* Disk Data In/Out */ int32 dsk12(const int32 port, const int32 io, const int32 data) { int32 i; UNIT *uptr; if (current_disk >= NUM_OF_DSK) { if ((dsk_dev.dctrl & VERBOSE_MSG) && (warnDSK12 < warnLevelDSK)) { warnDSK12++; /*04*/ printf("DSK%i: " ADDRESS_FORMAT " Attempt of %s 0x0a on unattached disk - ignored." NLP, current_disk, PCX, selectInOut(io)); } return 0; } /* now current_disk < NUM_OF_DSK */ in9_count = 0; uptr = dsk_dev.units + current_disk; if (io == 0) { if (current_byte[current_disk] >= DSK_SECTSIZE) { /* physically read the sector */ TRACE_PRINT(READ_MSG, ("DSK%i: " ADDRESS_FORMAT " IN 0x0a (READ) D%d T%d S%d" NLP, current_disk, PCX, current_disk, current_track[current_disk], current_sector[current_disk])); for (i = 0; i < DSK_SECTSIZE; i++) dskbuf[i] = 0; dskseek(uptr); fread(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref); current_byte[current_disk] = 0; } return dskbuf[current_byte[current_disk]++] & 0xff; } else { if (current_byte[current_disk] >= DSK_SECTSIZE) writebuf(); /* from above we have that current_disk < NUM_OF_DSK */ else { dirty = TRUE; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */ dskbuf[current_byte[current_disk]++] = data & 0xff; } return 0; /* ignored since OUT */ } } simh-3.8.1/AltairZ80/altairz80_sio.c0000644000175000017500000023331611111377140015217 0ustar vlmvlm/* altairz80_sio.c: MITS Altair serial I/O card Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 These functions support a simulated MITS 2SIO interface card. The card had two physical I/O ports which could be connected to any serial I/O device that would connect to a current loop, RS232, or TTY interface. Available baud rates were jumper selectable for each port from 110 to 9600. All I/O is via programmed I/O. Each device has a status port and a data port. A write to the status port can select some options for the device (0x03 will reset the port). A read of the status port gets the port status: +---+---+---+---+---+---+---+---+ | X | X | X | X | X | X | O | I | +---+---+---+---+---+---+---+---+ I - A 1 in this bit position means a character has been received on the data port and is ready to be read. O - A 1 in this bit means the port is ready to receive a character on the data port and transmit it out over the serial line. A read to the data port gets the buffered character, a write to the data port writes the character to the device. */ #include #include "altairz80_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include #include #if UNIX_PLATFORM #include #elif defined (_WIN32) #include #endif /* Debug flags */ #define IN_MSG (1 << 0) #define OUT_MSG (1 << 1) #define CMD_MSG (1 << 2) #define VERBOSE_MSG (1 << 3) #define UNIT_V_SIO_ANSI (UNIT_V_UF + 0) /* ANSI mode, strip bit 8 on output */ #define UNIT_SIO_ANSI (1 << UNIT_V_SIO_ANSI) #define UNIT_V_SIO_UPPER (UNIT_V_UF + 1) /* upper case mode */ #define UNIT_SIO_UPPER (1 << UNIT_V_SIO_UPPER) #define UNIT_V_SIO_BS (UNIT_V_UF + 2) /* map delete to backspace */ #define UNIT_SIO_BS (1 << UNIT_V_SIO_BS) #define UNIT_V_SIO_VERBOSE (UNIT_V_UF + 3) /* verbose mode, i.e. show error messages */ #define UNIT_SIO_VERBOSE (1 << UNIT_V_SIO_VERBOSE) #define UNIT_V_SIO_MAP (UNIT_V_UF + 4) /* mapping mode on */ #define UNIT_SIO_MAP (1 << UNIT_V_SIO_MAP) #define UNIT_V_SIO_BELL (UNIT_V_UF + 5) /* ^G (bell character) rings bell */ #define UNIT_SIO_BELL (1 << UNIT_V_SIO_BELL) #define UNIT_V_SIO_INTERRUPT (UNIT_V_UF + 6) /* create keyboard interrupts */ #define UNIT_SIO_INTERRUPT (1 << UNIT_V_SIO_INTERRUPT) #define UNIT_V_SIO_SLEEP (UNIT_V_UF + 7) /* sleep after keyboard status check */ #define UNIT_SIO_SLEEP (1 << UNIT_V_SIO_SLEEP) #define UNIT_V_SIMH_TIMERON (UNIT_V_UF + 1) /* SIMH pseudo device timer generate interrupts */ #define UNIT_SIMH_TIMERON (1 << UNIT_V_SIMH_TIMERON) #define TERMINALS 33 /* lines per mux (increased to 33 for IF3 board)*/ #define SIO_CAN_READ 0x01 /* bit 0 is set iff character available */ #define SIO_CAN_WRITE 0x02 /* bit 1 is set iff character can be sent */ #define SIO_RESET 0x03 /* Command to reset SIO */ #define VGSIO_CAN_READ 0x02 /* bit 1 is set iff character available */ #define VGSIO_CAN_WRITE 0x01 /* bit 0 is set iff character can be sent */ #define KBD_HAS_CHAR 0x40 /* bit 6 is set iff character available */ #define KBD_HAS_NO_CHAR 0x01 /* bit 0 is set iff no character is available */ #define BACKSPACE_CHAR 0x08 /* backspace character */ #define DELETE_CHAR 0x7f /* delete character */ #define CONTROLC_CHAR 0x03 /* control C character */ #define CONTROLG_CHAR 0x07 /* control G char., rings bell when displayed */ #define CONTROLZ_CHAR 0x1a /* control Z character */ #define PORT_TABLE_SIZE 256 /* size of port mapping table */ #define SLEEP_ALLOWED_START_DEFAULT 100 /* default initial value for sleepAllowedCounter*/ static t_stat sio_set_verbose (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat simh_dev_set_timeron (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat simh_dev_set_timeroff (UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat sio_reset(DEVICE *dptr); static t_stat sio_attach(UNIT *uptr, char *cptr); static t_stat sio_detach(UNIT *uptr); static t_stat ptr_reset(DEVICE *dptr); static t_stat ptp_reset(DEVICE *dptr); static t_stat toBool(char tf, int *result); static t_stat sio_dev_set_port(UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat sio_dev_show_port(FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat sio_dev_set_interrupton(UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat sio_dev_set_interruptoff(UNIT *uptr, int32 value, char *cptr, void *desc); static t_stat sio_svc(UNIT *uptr); static t_stat simh_dev_reset(DEVICE *dptr); static t_stat simh_svc(UNIT *uptr); static void mapAltairPorts(void); int32 nulldev (const int32 port, const int32 io, const int32 data); int32 sr_dev (const int32 port, const int32 io, const int32 data); int32 simh_dev (const int32 port, const int32 io, const int32 data); int32 sio0d (const int32 port, const int32 io, const int32 data); int32 sio0s (const int32 port, const int32 io, const int32 data); int32 sio1d (const int32 port, const int32 io, const int32 data); int32 sio1s (const int32 port, const int32 io, const int32 data); void do_SIMH_sleep(void); static void pollConnection(void); static int32 mapCharacter(int32 ch); static void checkSleep(void); static void voidSleep(void); extern int32 getBankSelect(void); extern void setBankSelect(const int32 b); extern uint32 getCommon(void); extern uint8 GetBYTEWrapper(const uint32 Addr); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern int32 chiptype; extern const t_bool rtc_avail; extern uint32 PCX; extern int32 sim_switches; extern int32 sim_quiet; extern const char *scp_error_messages[]; extern int32 SR; extern UNIT cpu_unit; extern volatile int32 stop_cpu; extern int32 sim_interval; /* SIMH pseudo device status registers */ /* ZSDOS clock definitions */ static time_t ClockZSDOSDelta = 0; /* delta between real clock and Altair clock */ static int32 setClockZSDOSPos = 0; /* determines state for receiving address of parameter block */ static int32 setClockZSDOSAdr = 0; /* address in M of 6 byte parameter block for setting time */ static int32 getClockZSDOSPos = 0; /* determines state for sending clock information */ /* CPM3 clock definitions */ static time_t ClockCPM3Delta = 0; /* delta between real clock and Altair clock */ static int32 setClockCPM3Pos = 0; /* determines state for receiving address of parameter block */ static int32 setClockCPM3Adr = 0; /* address in M of 5 byte parameter block for setting time */ static int32 getClockCPM3Pos = 0; /* determines state for sending clock information */ static int32 daysCPM3SinceOrg = 0; /* days since 1 Jan 1978 */ /* interrupt related */ static uint32 timeOfNextInterrupt; /* time when next interrupt is scheduled */ int32 timerInterrupt = FALSE; /* timer interrupt pending */ int32 timerInterruptHandler = 0x0fc00; /* default address of interrupt handling routine */ static int32 setTimerInterruptAdrPos= 0; /* determines state for receiving timerInterruptHandler */ static int32 timerDelta = 100; /* interrupt every 100 ms */ static int32 setTimerDeltaPos = 0; /* determines state for receiving timerDelta */ /* stop watch and timer related */ static uint32 stopWatchDelta = 0; /* stores elapsed time of stop watch */ static int32 getStopWatchDeltaPos = 0; /* determines the state for receiving stopWatchDelta */ static uint32 stopWatchNow = 0; /* stores starting time of stop watch */ static int32 markTimeSP = 0; /* stack pointer for timer stack */ /* default time in microseconds to sleep for SIMHSleepCmd */ #if defined (_WIN32) static uint32 SIMHSleep = 1000; /* Sleep uses milliseconds */ #elif defined (__MWERKS__) && defined (macintosh) static uint32 SIMHSleep = 0; /* no sleep on Macintosh OS9 */ #else static uint32 SIMHSleep = 100; /* on other platforms 100 micro seconds is good enough */ #endif static uint32 sleepAllowedCounter = 0; /* only sleep on no character available when == 0 */ static uint32 sleepAllowedStart = SLEEP_ALLOWED_START_DEFAULT; /* default start for above counter */ /* miscellaneous */ static int32 versionPos = 0; /* determines state for sending device identifier */ static int32 lastCPMStatus = 0; /* result of last attachCPM command */ static int32 lastCommand = 0; /* most recent command processed on port 0xfeh */ static int32 getCommonPos = 0; /* determines state for sending the 'common' register */ /* support for wild card expansion */ #if UNIX_PLATFORM static glob_t globS; static uint32 globPosNameList = 0; static int32 globPosName = 0; static int32 globValid = FALSE; static int32 globError = 0; #elif defined (_WIN32) static WIN32_FIND_DATA FindFileData; static HANDLE hFind = INVALID_HANDLE_VALUE; static int32 globFinished = FALSE; static int32 globValid = FALSE; static int32 globPosName = 0; static int32 lastPathSeparator = 0; static int32 firstPathCharacter = 0; #endif /* SIO status registers */ static int32 warnLevelSIO = 3; /* display at most 'warnLevelSIO' times the same warning */ static int32 warnUnattachedPTP = 0; /* display a warning message if < warnLevel and SIO set to VERBOSE and output to PTP without an attached file */ static int32 warnUnattachedPTR = 0; /* display a warning message if < warnLevel and SIO set to VERBOSE and attempt to read from PTR without an attached file */ static int32 warnPTREOF = 0; /* display a warning message if < warnLevel and SIO set to VERBOSE and attempt to read from PTR past EOF */ static int32 warnUnassignedPort = 0; /* display a warning message if < warnLevel and SIO set to VERBOSE and attempt to perform IN or OUT on an unassigned PORT */ int32 keyboardInterrupt = FALSE; /* keyboard interrupt pending */ uint32 keyboardInterruptHandler = 0x0038;/* address of keyboard interrupt handler */ static TMLN TerminalLines[TERMINALS] = { /* four terminals */ { 0 } }; static TMXR altairTMXR = { /* mux descriptor */ TERMINALS, 0, 0, TerminalLines }; static UNIT sio_unit = { UDATA (&sio_svc, UNIT_ATTABLE | UNIT_SIO_MAP | UNIT_SIO_SLEEP, 0), 100000, /* wait */ FALSE, /* u3 = FALSE, no character available in buffer */ FALSE, /* u4 = FALSE, terminal input is not attached to a file */ FALSE, /* u5 = FALSE, terminal input has not yet reached EOF */ 0 /* u6 = 0, not used */ }; static REG sio_reg[] = { { DRDATA (SIOWLEV, warnLevelSIO, 32) }, { DRDATA (WRNUPTP, warnUnattachedPTP, 32) }, { DRDATA (WRNUPTR, warnUnattachedPTR, 32) }, { DRDATA (WRNPTRE, warnPTREOF, 32) }, { DRDATA (WRUPORT, warnUnassignedPort, 32) }, { HRDATA (FILEATT, sio_unit.u4, 8), REG_RO }, /* TRUE iff terminal input is attached to a file */ { HRDATA (FILEEOF, sio_unit.u5, 8), REG_RO }, /* TRUE iff terminal input file has reached EOF */ { HRDATA (TSTATUS, sio_unit.u3, 8) }, /* TRUE iff a character available in sio_unit.buf */ { DRDATA (TBUFFER, sio_unit.buf, 8) }, /* input buffer for one character */ { DRDATA (KEYBDI, keyboardInterrupt, 3), REG_RO }, { HRDATA (KEYBDH, keyboardInterruptHandler, 16) }, { NULL } }; static MTAB sio_mod[] = { { UNIT_SIO_ANSI, 0, "TTY", "TTY", NULL }, /* keep bit 8 as is for output */ { UNIT_SIO_ANSI, UNIT_SIO_ANSI, "ANSI", "ANSI", NULL }, /* set bit 8 to 0 before output */ { UNIT_SIO_UPPER, 0, "ALL", "ALL", NULL }, /* do not change case of input characters */ { UNIT_SIO_UPPER, UNIT_SIO_UPPER, "UPPER", "UPPER", NULL }, /* change input characters to upper case */ { UNIT_SIO_BS, 0, "BS", "BS", NULL }, /* map delete to backspace */ { UNIT_SIO_BS, UNIT_SIO_BS, "DEL", "DEL", NULL }, /* map backspace to delete */ { UNIT_SIO_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* quiet, no error messages */ { UNIT_SIO_VERBOSE, UNIT_SIO_VERBOSE, "VERBOSE", "VERBOSE", &sio_set_verbose }, /* verbose, display warning messages */ { UNIT_SIO_MAP, 0, "NOMAP", "NOMAP", NULL }, /* disable character mapping */ { UNIT_SIO_MAP, UNIT_SIO_MAP, "MAP", "MAP", NULL }, /* enable all character mapping */ { UNIT_SIO_BELL, 0, "BELL", "BELL", NULL }, /* enable bell character */ { UNIT_SIO_BELL, UNIT_SIO_BELL, "NOBELL", "NOBELL", NULL }, /* suppress ringing the bell */ { UNIT_SIO_SLEEP, 0, "NOSLEEP", "NOSLEEP", NULL }, /* no sleep after keyboard status check */ { UNIT_SIO_SLEEP, UNIT_SIO_SLEEP, "SLEEP", "SLEEP", NULL }, /* sleep after keyboard status check */ /* no keyboard interrupts */ { UNIT_SIO_INTERRUPT, 0, "NOINTERRUPT","NOINTERRUPT",&sio_dev_set_interruptoff }, /* create keyboard interrupts */ { UNIT_SIO_INTERRUPT, UNIT_SIO_INTERRUPT, "INTERRUPT","INTERRUPT",&sio_dev_set_interrupton }, { MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT", &sio_dev_set_port, &sio_dev_show_port }, { 0 } }; DEVICE sio_dev = { "SIO", &sio_unit, sio_reg, sio_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &sio_reset, NULL, &sio_attach, &sio_detach, NULL, 0, 0, NULL, NULL, "Serial Input Output SIO" }; static UNIT ptr_unit = { UDATA (NULL, UNIT_SEQ | UNIT_ATTABLE | UNIT_ROABLE, 0) }; static REG ptr_reg[] = { { HRDATA (STAT, ptr_unit.u3, 8) }, { NULL } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, NULL, NULL, NULL, NULL, DEV_DISABLE, 0, NULL, NULL, "Paper Tape Reader PTR" }; static UNIT ptp_unit = { UDATA (NULL, UNIT_SEQ + UNIT_ATTABLE, 0) }; DEVICE ptp_dev = { "PTP", &ptp_unit, NULL, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, NULL, DEV_DISABLE, 0, NULL, NULL, "Paper Tape Puncher PTP" }; /* Synthetic device SIMH for communication between Altair and SIMH environment using port 0xfe */ static UNIT simh_unit = { UDATA (&simh_svc, 0, 0), KBD_POLL_WAIT }; static REG simh_reg[] = { { DRDATA (CZD, ClockZSDOSDelta, 32) }, { DRDATA (SCZP, setClockZSDOSPos, 8), REG_RO }, { HRDATA (SCZA, setClockZSDOSAdr, 16), REG_RO }, { DRDATA (GCZP, getClockZSDOSPos, 8), REG_RO }, { DRDATA (CC3D, ClockCPM3Delta, 32) }, { DRDATA (SC3DP, setClockCPM3Pos, 8), REG_RO }, { HRDATA (SC3DA, setClockCPM3Adr, 16), REG_RO }, { DRDATA (GC3DP, getClockCPM3Pos, 8), REG_RO }, { DRDATA (D3DO, daysCPM3SinceOrg, 32), REG_RO }, { DRDATA (TOFNI, timeOfNextInterrupt, 32), REG_RO }, { DRDATA (TIMI, timerInterrupt, 3) }, { HRDATA (TIMH, timerInterruptHandler, 16) }, { DRDATA (STIAP, setTimerInterruptAdrPos,8), REG_RO }, { DRDATA (TIMD, timerDelta, 32) }, { DRDATA (STDP, setTimerDeltaPos, 8), REG_RO }, { DRDATA (SLEEP, SIMHSleep, 32) }, { DRDATA (VOSLP, sleepAllowedStart, 32) }, { DRDATA (STPDT, stopWatchDelta, 32), REG_RO }, { DRDATA (STPOS, getStopWatchDeltaPos, 8), REG_RO }, { DRDATA (STPNW, stopWatchNow, 32), REG_RO }, { DRDATA (MTSP, markTimeSP, 8), REG_RO }, { DRDATA (VPOS, versionPos, 8), REG_RO }, { DRDATA (LCPMS, lastCPMStatus, 8), REG_RO }, { DRDATA (LCMD, lastCommand, 8), REG_RO }, { DRDATA (CPOS, getCommonPos, 8), REG_RO }, { NULL } }; static MTAB simh_mod[] = { /* timer generated interrupts are off */ { UNIT_SIMH_TIMERON, 0, "TIMEROFF", "TIMEROFF", &simh_dev_set_timeroff }, /* timer generated interrupts are on */ { UNIT_SIMH_TIMERON, UNIT_SIMH_TIMERON, "TIMERON", "TIMERON", &simh_dev_set_timeron }, { 0 } }; #define TRACE_PRINT(level, args) if (simh_device.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB simh_dt[] = { { "IN", IN_MSG }, { "OUT", OUT_MSG }, { "CMD", CMD_MSG }, { "VERBOSE", VERBOSE_MSG }, { NULL, 0 } }; DEVICE simh_device = { "SIMH", &simh_unit, simh_reg, simh_mod, 1, 10, 31, 1, 16, 4, NULL, NULL, &simh_dev_reset, NULL, NULL, NULL, NULL, (DEV_DISABLE | DEV_DEBUG), 0, simh_dt, NULL, "Pseudo Device SIMH" }; static void resetSIOWarningFlags(void) { warnUnattachedPTP = warnUnattachedPTR = warnPTREOF = warnUnassignedPort = 0; } static t_stat sio_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) { resetSIOWarningFlags(); return SCPE_OK; } static t_stat sio_attach(UNIT *uptr, char *cptr) { t_stat r = SCPE_IERR; sio_unit.u3 = FALSE; /* no character in terminal input buffer */ get_uint(cptr, 10, 65535, &r); /* attempt to get port, discard result */ if (r == SCPE_OK) { /* string can be interpreted as port number */ sio_unit.u4 = FALSE; /* terminal input is not attached to a file */ return tmxr_attach(&altairTMXR, uptr, cptr); /* attach mux */ } sio_unit.u4 = TRUE; /* terminal input is attached to a file */ sio_unit.u5 = FALSE; /* EOF not yet reached */ return attach_unit(uptr, cptr); } static t_stat sio_detach(UNIT *uptr) { sio_unit.u3 = FALSE; /* no character in terminal input buffer */ if (sio_unit.u4) { /* is terminal input attached to a file? */ sio_unit.u4 = FALSE; /* not anymore, detach */ return detach_unit(uptr); } return tmxr_detach(&altairTMXR, uptr); } static void pollConnection(void) { if (sio_unit.flags & UNIT_ATT) { int32 temp = tmxr_poll_conn(&altairTMXR); /* poll connection */ if (temp >= 0) TerminalLines[temp].rcve = 1; /* enable receive */ tmxr_poll_rx(&altairTMXR); /* poll input */ tmxr_poll_tx(&altairTMXR); /* poll output */ } } /* reset routines */ static t_stat sio_reset(DEVICE *dptr) { int32 i; sio_unit.u3 = FALSE; /* no character in terminal input buffer */ resetSIOWarningFlags(); if (sio_unit.u4) { /* is terminal input attached to a file? */ rewind(sio_unit.fileref); /* yes, rewind input */ sio_unit.u5 = FALSE; /* EOF not yet reached */ } else if (sio_unit.flags & UNIT_ATT) for (i = 0; i < TERMINALS; i++) if (TerminalLines[i].conn) tmxr_reset_ln(&TerminalLines[i]); mapAltairPorts(); return SCPE_OK; } static t_stat ptr_reset(DEVICE *dptr) { resetSIOWarningFlags(); ptr_unit.u3 = FALSE; /* End Of File not yet reached */ if (ptr_unit.flags & UNIT_ATT) /* attached? */ rewind(ptr_unit.fileref); sim_map_resource(0x12, 1, RESOURCE_TYPE_IO, &sio1s, dptr->flags & DEV_DIS); sim_map_resource(0x13, 1, RESOURCE_TYPE_IO, &sio1d, dptr->flags & DEV_DIS); return SCPE_OK; } static t_stat ptp_reset(DEVICE *dptr) { resetSIOWarningFlags(); sim_map_resource(0x12, 1, RESOURCE_TYPE_IO, &sio1s, dptr->flags & DEV_DIS); sim_map_resource(0x13, 1, RESOURCE_TYPE_IO, &sio1d, dptr->flags & DEV_DIS); return SCPE_OK; } static int32 mapCharacter(int32 ch) { ch &= 0xff; if (sio_unit.flags & UNIT_SIO_MAP) { if (sio_unit.flags & UNIT_SIO_BS) { if (ch == BACKSPACE_CHAR) return DELETE_CHAR; } else if (ch == DELETE_CHAR) return BACKSPACE_CHAR; if (sio_unit.flags & UNIT_SIO_UPPER) return toupper(ch); } return ch; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. Each function is passed an 'io' flag, where 0 means a read from the port, and 1 means a write to the port. On input, the actual input is passed as the return value, on output, 'data' is written to the device. Port 1 controls console I/O. We distinguish three cases: 1) SIO attached to a file (i.e. input taken from a file ) 2) SIO attached to a port (i.e. Telnet console I/O ) 3) SIO not attached to a port (i.e. "regular" console I/O ) */ typedef struct { int32 port; /* this information belongs to port number 'port' */ int32 terminalLine; /* map to this 'terminalLine' */ int32 sio_can_read; /* bit mask to indicate that one can read from this port */ int32 sio_cannot_read; /* bit mask to indicate that one cannot read from this port */ int32 sio_can_write; /* bit mask to indicate that one can write to this port */ int32 hasReset; /* TRUE iff SIO has reset command */ int32 sio_reset; /* reset command */ int32 hasOUT; /* TRUE iff port supports OUT command */ int32 isBuiltin; /* TRUE iff mapping is built in */ } SIO_PORT_INFO; static SIO_PORT_INFO port_table[PORT_TABLE_SIZE] = { {0x00, 0, KBD_HAS_CHAR, KBD_HAS_NO_CHAR, SIO_CAN_WRITE, FALSE, 0, FALSE, TRUE }, {0x01, 0, 0, 0, 0, FALSE, 0, FALSE, TRUE }, {0x02, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, TRUE, TRUE }, {0x03, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, FALSE, TRUE }, {0x10, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, {0x11, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, {0x14, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, {0x15, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, {0x16, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, {0x17, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, {0x18, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, {0x19, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, /* CompuPro System Support 1 Board */ {0x5c, 0, 0x0, 0, 0, FALSE,0, TRUE, TRUE }, {0x5d, 0, 0xC2, 0, 0xC5, FALSE,0, FALSE, TRUE }, /* CompuPro Interfacer 3 (IF3) Board 0 */ {0x300, 1, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x301, 1, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x302, 2, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x303, 2, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x304, 3, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x305, 3, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x306, 4, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x307, 4, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x308, 5, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x309, 5, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x30a, 6, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x30b, 6, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x30c, 7, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x30d, 7, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x30e, 8, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x30f, 8, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, /* CompuPro Interfacer 3 (IF3) Board 1 */ {0x310, 9, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x311, 9, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x312, 10, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x313, 10, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x314, 11, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x315, 11, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x316, 12, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x317, 12, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x318, 13, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x319, 13, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x31a, 14, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x31b, 14, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x31c, 15, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x31d, 15, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x31e, 16, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x31f, 16, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, /* CompuPro Interfacer 3 (IF3) Board 2 */ {0x320, 17, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x321, 17, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x322, 18, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x323, 18, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x324, 19, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x325, 19, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x326, 20, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x327, 20, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x328, 21, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x329, 21, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x32a, 22, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x32b, 22, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x32c, 23, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x32d, 23, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x32e, 24, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x32f, 24, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, /* CompuPro Interfacer 3 (IF3) Board 3 */ {0x330, 25, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x331, 25, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x332, 26, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x333, 26, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x334, 27, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x335, 27, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x336, 28, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x337, 28, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x338, 29, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x339, 29, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x33a, 30, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x33b, 30, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x33c, 31, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x33d, 31, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {0x33e, 32, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE }, {0x33f, 32, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE }, {-1, 0, 0, 0, 0, 0, 0, 0, 0} /* must be last */ }; static SIO_PORT_INFO lookupPortInfo(const int32 port, int32 *position) { int32 i = 0; while ((port_table[i].port != -1) && (port_table[i].port != port)) i++; *position = i; return port_table[i]; } /* keyboard idle detection: sleep when feature enabled, no character available (duty of caller) and operation not voided (e.g. when output is available) */ static void checkSleep(void) { if (sio_unit.flags & UNIT_SIO_SLEEP) { if (sleepAllowedCounter) sleepAllowedCounter--; else do_SIMH_sleep(); } } /* void sleep for next 'sleepAllowedStart' tests */ static void voidSleep(void) { sleepAllowedCounter = sleepAllowedStart; } /* generic status port for keyboard input / terminal output */ int32 sio0s(const int32 port, const int32 io, const int32 data) { int32 ch, result; SIO_PORT_INFO spi = lookupPortInfo(port, &ch); assert(spi.port == port); pollConnection(); if (io == 0) { /* IN */ if (sio_unit.u4) /* attached to a file? */ if (sio_unit.u5) /* EOF reached? */ sio_detach(&sio_unit); /* detach file and switch to keyboard input */ else return spi.sio_can_read | spi.sio_can_write; if (sio_unit.flags & UNIT_ATT) { /* attached to a port? */ if (tmxr_rqln(&TerminalLines[spi.terminalLine])) result = spi.sio_can_read; else { result = spi.sio_cannot_read; checkSleep(); } return result | /* read possible if character available */ (TerminalLines[spi.terminalLine].conn && TerminalLines[spi.terminalLine].xmte ? spi.sio_can_write : 0x00); /* write possible if connected and transmit enabled */ } if (sio_unit.u3) /* character available? */ return spi.sio_can_read | spi.sio_can_write; ch = sim_poll_kbd(); /* no, try to get a character */ if (ch) { /* character available? */ if (ch == SCPE_STOP) { /* stop CPU in case ^E (default) was typed */ stop_cpu = TRUE; sim_interval = 0; /* detect stop condition as soon as possible*/ return spi.sio_can_write | spi.sio_cannot_read; /* do not consume stop character */ } sio_unit.u3 = TRUE; /* indicate character available */ sio_unit.buf = ch; /* store character in buffer */ return spi.sio_can_read | spi.sio_can_write; } checkSleep(); return spi.sio_can_write | spi.sio_cannot_read; } /* OUT follows, no fall-through from IN */ if (spi.hasReset && (data == spi.sio_reset)) /* reset command */ sio_unit.u3 = FALSE; /* indicate that no character is available */ return 0x00; /* ignored since OUT */ } /* generic data port for keyboard input / terminal output */ int32 sio0d(const int32 port, const int32 io, const int32 data) { int32 ch; SIO_PORT_INFO spi = lookupPortInfo(port, &ch); assert(spi.port == port); pollConnection(); if (io == 0) { /* IN */ if (sio_unit.u4) { /* attached to a file? */ if (sio_unit.u5) { /* EOF reached? */ sio_detach(&sio_unit); /* detach file and switch to keyboard input */ return CONTROLC_CHAR; /* this time return ^C after all */ } if ((ch = getc(sio_unit.fileref)) == EOF) { /* end of file? */ sio_unit.u5 = TRUE; /* terminal input file has reached EOF */ return CONTROLC_CHAR; /* result is ^C (= CP/M interrupt) */ } return mapCharacter(ch); /* return mapped character */ } if (sio_unit.flags & UNIT_ATT) return mapCharacter(tmxr_getc_ln(&TerminalLines[spi.terminalLine])); sio_unit.u3 = FALSE; /* no character is available any more */ return mapCharacter(sio_unit.buf); /* return previous character */ } /* OUT follows, no fall-through from IN */ if (spi.hasOUT) { ch = sio_unit.flags & UNIT_SIO_ANSI ? data & 0x7f : data; /* clear highest bit in ANSI mode */ if ((ch != CONTROLG_CHAR) || !(sio_unit.flags & UNIT_SIO_BELL)) { voidSleep(); if ((sio_unit.flags & UNIT_ATT) && (!sio_unit.u4)) /* attached to a port and not to a file */ tmxr_putc_ln(&TerminalLines[spi.terminalLine], ch); /* status ignored */ else sim_putchar(ch); } } return 0x00; /* ignored since OUT */ } /* PTR/PTP status port */ int32 sio1s(const int32 port, const int32 io, const int32 data) { if (io == 0) { /* IN */ /* reset I bit iff PTR unit not attached or no more data available. O bit is always set since write always possible. */ if ((ptr_unit.flags & UNIT_ATT) == 0) { /* PTR is not attached */ if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTR < warnLevelSIO)) { warnUnattachedPTR++; /*06*/ printf("PTR: " ADDRESS_FORMAT " Attempt to test status of unattached PTR[0x%02x]. 0x02 returned." NLP, PCX, port); } return SIO_CAN_WRITE; } /* if EOF then SIO_CAN_WRITE else (SIO_CAN_WRITE and SIO_CAN_READ) */ return ptr_unit.u3 ? SIO_CAN_WRITE : (SIO_CAN_READ | SIO_CAN_WRITE); } /* OUT follows */ if (data == SIO_RESET) ptr_unit.u3 = FALSE; /* reset EOF indicator */ return 0x00; /* ignored since OUT */ } /* PTR/PTP data port */ int32 sio1d(const int32 port, const int32 io, const int32 data) { int32 ch; if (io == 0) { /* IN */ if (ptr_unit.u3) { /* EOF reached, no more data available */ if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnPTREOF < warnLevelSIO)) { warnPTREOF++; /*07*/ printf("PTR: " ADDRESS_FORMAT " PTR[0x%02x] attempted to read past EOF. 0x00 returned." NLP, PCX, port); } return 0x00; } if ((ptr_unit.flags & UNIT_ATT) == 0) { /* not attached */ if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTR < warnLevelSIO)) { warnUnattachedPTR++; /*08*/ printf("PTR: " ADDRESS_FORMAT " Attempt to read from unattached PTR[0x%02x]. 0x00 returned." NLP, PCX, port); } return 0x00; } if ((ch = getc(ptr_unit.fileref)) == EOF) { /* end of file? */ ptr_unit.u3 = TRUE; /* remember EOF reached */ return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */ } return ch & 0xff; } /* OUT follows */ if (ptp_unit.flags & UNIT_ATT) /* unit must be attached */ putc(data, ptp_unit.fileref); /* else ignore data */ else if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTP < warnLevelSIO)) { warnUnattachedPTP++; /*09*/ printf("PTP: " ADDRESS_FORMAT " Attempt to output '0x%02x' to unattached PTP[0x%02x] - ignored." NLP, PCX, data, port); } return 0x00; /* ignored since OUT */ } static t_stat toBool(char tf, int *result) { if (tf == 'T') { *result = TRUE; return SCPE_OK; } if (tf == 'F') { *result = FALSE; return SCPE_OK; } return SCPE_ARG; } static void show_sio_port_info(FILE *st, SIO_PORT_INFO sip) { if (sio_unit.flags & UNIT_SIO_VERBOSE) fprintf(st, "(Port=%02x/Terminal=%1i/Read=0x%02x/NotRead=0x%02x/" "Write=0x%02x/Reset?=%s/Reset=0x%02x/Data?=%s)", sip.port, sip.terminalLine, sip.sio_can_read, sip.sio_cannot_read, sip.sio_can_write, sip.hasReset ? "True" : "False", sip.sio_reset, sip.hasOUT ? "True" : "False"); else fprintf(st, "(%02x/%1i/%02x/%02x/%02x/%s/%02x/%s)", sip.port, sip.terminalLine, sip.sio_can_read, sip.sio_cannot_read, sip.sio_can_write, sip.hasReset ? "T" : "F", sip.sio_reset, sip.hasOUT ? "T" : "F"); } static uint32 equalSIP(SIO_PORT_INFO x, SIO_PORT_INFO y) { /* isBuiltin is not relevant for equality, only for display */ return (x.port == y.port) && (x.terminalLine == y.terminalLine) && (x.sio_can_read == y.sio_can_read) && (x.sio_cannot_read == y.sio_cannot_read) && (x.sio_can_write == y.sio_can_write) && (x.hasReset == y.hasReset) && (x.sio_reset == y.sio_reset) && (x.hasOUT == y.hasOUT); } static t_stat sio_dev_set_port(UNIT *uptr, int32 value, char *cptr, void *desc) { int32 result, n, position; SIO_PORT_INFO sip = { 0 }, old; char hasReset, hasOUT; if (cptr == NULL) return SCPE_ARG; result = sscanf(cptr, "%x%n", &sip.port, &n); if ((result == 1) && (cptr[n] == 0)) { old = lookupPortInfo(sip.port, &position); if (old.port == -1) { printf("No mapping for port 0x%02x exists - cannot remove.\n", sip.port); return SCPE_ARG; } do { port_table[position] = port_table[position + 1]; position++; } while (port_table[position].port != -1); sim_map_resource(sip.port, 1, RESOURCE_TYPE_IO, &nulldev, FALSE); if (sio_unit.flags & UNIT_SIO_VERBOSE) { printf("Removing mapping for port 0x%02x.\n\t", sip.port); show_sio_port_info(stdout, old); } return SCPE_OK; } result = sscanf(cptr, "%x/%d/%x/%x/%x/%1c/%x/%1c%n", &sip.port, &sip.terminalLine, &sip.sio_can_read, &sip.sio_cannot_read, &sip.sio_can_write, &hasReset, &sip.sio_reset, &hasOUT, &n); if ((result != 8) || (result == EOF) || (cptr[n] != 0)) return SCPE_ARG; result = toBool(hasReset, &sip.hasReset); if (result != SCPE_OK) return result; result = toBool(hasOUT, &sip.hasOUT); if (result != SCPE_OK) return result; if (sip.port != (sip.port & 0xff)) { printf("Truncating port 0x%x to 0x%02x.\n", sip.port, sip.port & 0xff); sip.port &= 0xff; } old = lookupPortInfo(sip.port, &position); if (old.port == sip.port) { if (sio_unit.flags & UNIT_SIO_VERBOSE) { printf("Replacing mapping for port 0x%02x.\n\t", sip.port); show_sio_port_info(stdout, old); printf("-> "); show_sio_port_info(stdout, sip); if (equalSIP(sip, old)) printf("[identical]"); } } else { port_table[position + 1] = old; if (sio_unit.flags & UNIT_SIO_VERBOSE) { printf("Adding mapping for port 0x%02x.\n\t", sip.port); show_sio_port_info(stdout, sip); } } if (sio_unit.flags & UNIT_SIO_VERBOSE) printf("\n"); port_table[position] = sip; sim_map_resource(sip.port, 1, RESOURCE_TYPE_IO, (sip.hasOUT || (sip.sio_can_read == 0) && (sip.sio_cannot_read == 0) && (sip.sio_can_write == 0)) ? &sio0d : &sio0s, FALSE); return SCPE_OK; } static t_stat sio_dev_show_port(FILE *st, UNIT *uptr, int32 val, void *desc) { int32 i, first = TRUE; for (i = 0; port_table[i].port != -1; i++) if (!port_table[i].isBuiltin) { if (first) first = FALSE; else fprintf(st, " "); show_sio_port_info(st, port_table[i]); } if (first) fprintf(st, "no extra port"); return SCPE_OK; } static t_stat sio_dev_set_interrupton(UNIT *uptr, int32 value, char *cptr, void *desc) { keyboardInterrupt = FALSE; return sim_activate(&sio_unit, sio_unit.wait); /* activate unit */ } static t_stat sio_dev_set_interruptoff(UNIT *uptr, int32 value, char *cptr, void *desc) { keyboardInterrupt = FALSE; sim_cancel(&sio_unit); return SCPE_OK; } static t_stat sio_svc(UNIT *uptr) { if (sio0s(0, 0, 0) & KBD_HAS_CHAR) { keyboardInterrupt = TRUE; } if (sio_unit.flags & UNIT_SIO_INTERRUPT) sim_activate(&sio_unit, sio_unit.wait); /* activate unit */ return SCPE_OK; } static void mapAltairPorts(void) { int32 i = 0; SIO_PORT_INFO spi; do { spi = port_table[i++]; if ((0x02 <= spi.port) && (spi.port <= 0x19)) sim_map_resource(spi.port, 1, RESOURCE_TYPE_IO, spi.hasOUT ? &sio0d : &sio0s, FALSE); } while (spi.port >= 0); } int32 nulldev(const int32 port, const int32 io, const int32 data) { if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnassignedPort < warnLevelSIO)) { warnUnassignedPort++; if (io == 0) { printf("SIO: " ADDRESS_FORMAT " Attempt to input from unassigned port 0x%04x - ignored." NLP, PCX, port); } else { printf("SIO: " ADDRESS_FORMAT " Attempt to output 0x%02x to unassigned port 0x%04x - ignored." NLP, PCX, data, port); } } return io == 0 ? 0xff : 0; } int32 sr_dev(const int32 port, const int32 io, const int32 data) { return io == 0 ? SR : 0; } static int32 toBCD(const int32 x) { return (x / 10) * 16 + (x % 10); } static int32 fromBCD(const int32 x) { return 10 * ((0xf0 & x) >> 4) + (0x0f & x); } /* Z80 or 8080 programs communicate with the SIMH pseudo device via port 0xfe. The following principles apply: 1) For commands that do not require parameters and do not return results ld a, out (0feh),a Special case is the reset command which needs to be send 128 times to make sure that the internal state is properly reset. 2) For commands that require parameters and do not return results ld a, out (0feh),a ld a, out (0feh),a ld a, out (0feh),a ... Note: The calling program must send all parameter bytes. Otherwise the pseudo device is left in an undefined state. 3) For commands that do not require parameters and return results ld a, out (0feh),a in a,(0feh) ; contains first byte of result in a,(0feh) ; contains second byte of result ... Note: The calling program must request all bytes of the result. Otherwise the pseudo device is left in an undefined state. 4) Commands requiring parameters and returning results do not exist currently. */ enum simhPseudoDeviceCommands { /* do not change order or remove commands, add only at the end */ printTimeCmd, /* 0 print the current time in milliseconds */ startTimerCmd, /* 1 start a new timer on the top of the timer stack */ stopTimerCmd, /* 2 stop timer on top of timer stack and show time difference */ resetPTRCmd, /* 3 reset the PTR device */ attachPTRCmd, /* 4 attach the PTR device */ detachPTRCmd, /* 5 detach the PTR device */ getSIMHVersionCmd, /* 6 get the current version of the SIMH pseudo device */ getClockZSDOSCmd, /* 7 get the current time in ZSDOS format */ setClockZSDOSCmd, /* 8 set the current time in ZSDOS format */ getClockCPM3Cmd, /* 9 get the current time in CP/M 3 format */ setClockCPM3Cmd, /* 10 set the current time in CP/M 3 format */ getBankSelectCmd, /* 11 get the selected bank */ setBankSelectCmd, /* 12 set the selected bank */ getCommonCmd, /* 13 get the base address of the common memory segment */ resetSIMHInterfaceCmd, /* 14 reset the SIMH pseudo device */ showTimerCmd, /* 15 show time difference to timer on top of stack */ attachPTPCmd, /* 16 attach PTP to the file with name at beginning of CP/M command line*/ detachPTPCmd, /* 17 detach PTP */ hasBankedMemoryCmd, /* 18 determines whether machine has banked memory */ setZ80CPUCmd, /* 19 set the CPU to a Z80 */ set8080CPUCmd, /* 20 set the CPU to an 8080 */ startTimerInterruptsCmd, /* 21 start timer interrupts */ stopTimerInterruptsCmd, /* 22 stop timer interrupts */ setTimerDeltaCmd, /* 23 set the timer interval in which interrupts occur */ setTimerInterruptAdrCmd, /* 24 set the address to call by timer interrupts */ resetStopWatchCmd, /* 25 reset the millisecond stop watch */ readStopWatchCmd, /* 26 read the millisecond stop watch */ SIMHSleepCmd, /* 27 let SIMH sleep for SIMHSleep microseconds */ getHostOSPathSeparatorCmd, /* 28 obtain the file path separator of the OS under which SIMH runs */ getHostFilenamesCmd /* 29 perform wildcard expansion and obtain list of file names */ }; static int32 lastSIMHCommand = getHostFilenamesCmd; static char *cmdNames[] = { "printTime", "startTimer", "stopTimer", "resetPTR", "attachPTR", "detachPTR", "getSIMHVersion", "getClockZSDOS", "setClockZSDOS", "getClockCPM3", "setClockCPM3", "getBankSelect", "setBankSelect", "getCommon", "resetSIMHInterface", "showTimer", "attachPTP", "detachPTP", "hasBankedMemory", "setZ80CPU", "set8080CPU", "startTimerInterrupts", "stopTimerInterrupts", "setTimerDelta", "setTimerInterruptAdr", "resetStopWatch", "readStopWatch", "SIMHSleep", "getHostOSPathSeparator", "getHostFilenames" }; #define CPM_COMMAND_LINE_LENGTH 128 #define TIMER_STACK_LIMIT 10 /* stack depth of timer stack */ static uint32 markTime[TIMER_STACK_LIMIT]; /* timer stack */ static struct tm currentTime; static int32 currentTimeValid = FALSE; static char version[] = "SIMH003"; static t_stat simh_dev_reset(DEVICE *dptr) { sim_map_resource(0xfe, 1, RESOURCE_TYPE_IO, &simh_dev, dptr->flags & DEV_DIS); currentTimeValid = FALSE; ClockZSDOSDelta = 0; setClockZSDOSPos = 0; getClockZSDOSPos = 0; ClockCPM3Delta = 0; setClockCPM3Pos = 0; getClockCPM3Pos = 0; getStopWatchDeltaPos = 0; getCommonPos = 0; setTimerDeltaPos = 0; setTimerInterruptAdrPos = 0; markTimeSP = 0; versionPos = 0; lastCommand = 0; lastCPMStatus = SCPE_OK; timerInterrupt = FALSE; if (simh_unit.flags & UNIT_SIMH_TIMERON) simh_dev_set_timeron(NULL, 0, NULL, NULL); return SCPE_OK; } static void warnNoRealTimeClock(void) { TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Sorry - no real time clock available." NLP, PCX)); } static t_stat simh_dev_set_timeron(UNIT *uptr, int32 value, char *cptr, void *desc) { if (rtc_avail) { timeOfNextInterrupt = sim_os_msec() + timerDelta; return sim_activate(&simh_unit, simh_unit.wait); /* activate unit */ } warnNoRealTimeClock(); return SCPE_ARG; } static t_stat simh_dev_set_timeroff(UNIT *uptr, int32 value, char *cptr, void *desc) { timerInterrupt = FALSE; sim_cancel(&simh_unit); return SCPE_OK; } static t_stat simh_svc(UNIT *uptr) { uint32 n = sim_os_msec(); if (n >= timeOfNextInterrupt) { timerInterrupt = TRUE; timeOfNextInterrupt += timerDelta; if (n >= timeOfNextInterrupt) /* time of next interrupt is not in the future */ timeOfNextInterrupt = n + timerDelta; /* make sure it is in the future! */ } if (simh_unit.flags & UNIT_SIMH_TIMERON) sim_activate(&simh_unit, simh_unit.wait); /* activate unit */ return SCPE_OK; } static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH]; static void createCPMCommandLine(void) { int32 i, len = (GetBYTEWrapper(0x80) & 0x7f); /* 0x80 contains length of command line, discard first char */ for (i = 0; i < len - 1; i++) cpmCommandLine[i] = (char)GetBYTEWrapper(0x82 + i); /* the first char, typically ' ', is discarded */ cpmCommandLine[i] = 0; /* make C string */ } #if defined (_WIN32) static void setLastPathSeparator(void) { int32 i = 0; while (cpmCommandLine[i]) i++; while ((i >= 0) && (cpmCommandLine[i] != '\\')) i--; lastPathSeparator = i; firstPathCharacter = 0; } #endif /* The CP/M command line is used as the name of a file and UNIT* uptr is attached to it. */ static void attachCPM(UNIT *uptr) { createCPMCommandLine(); if (uptr == &ptr_unit) sim_switches = SWMASK('R') | SWMASK('Q'); else if (uptr == &ptp_unit) sim_switches = SWMASK('W') | SWMASK('C') | SWMASK('Q'); /* 'C' option makes sure that file is properly truncated if it had existed before */ sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */ lastCPMStatus = attach_unit(uptr, cpmCommandLine); if ((lastCPMStatus != SCPE_OK) && (simh_device.dctrl & VERBOSE_MSG)) printf("SIMH: " ADDRESS_FORMAT " Cannot open '%s' (%s)." NLP, PCX, cpmCommandLine, scp_error_messages[lastCPMStatus - SCPE_BASE]); } /* setClockZSDOSAdr points to 6 byte block in M: YY MM DD HH MM SS in BCD notation */ static void setClockZSDOS(void) { struct tm newTime; int32 year = fromBCD(GetBYTEWrapper(setClockZSDOSAdr)); newTime.tm_year = year < 50 ? year + 100 : year; newTime.tm_mon = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 1)) - 1; newTime.tm_mday = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 2)); newTime.tm_hour = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 3)); newTime.tm_min = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 4)); newTime.tm_sec = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 5)); ClockZSDOSDelta = mktime(&newTime) - time(NULL); } #define SECONDS_PER_MINUTE 60 #define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE) #define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR) static time_t mkCPM3Origin(void) { struct tm date; date.tm_year = 77; date.tm_mon = 11; date.tm_mday = 31; date.tm_hour = 0; date.tm_min = 0; date.tm_sec = 0; return mktime(&date); } /* setClockCPM3Adr points to 5 byte block in M: 0 - 1 int16: days since 31 Dec 77 2 BCD byte: HH 3 BCD byte: MM 4 BCD byte: SS */ static void setClockCPM3(void) { ClockCPM3Delta = mkCPM3Origin() + (GetBYTEWrapper(setClockCPM3Adr) + GetBYTEWrapper(setClockCPM3Adr + 1) * 256) * SECONDS_PER_DAY + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 2)) * SECONDS_PER_HOUR + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 3)) * SECONDS_PER_MINUTE + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 4)) - time(NULL); } static int32 simh_in(const int32 port) { int32 result = 0; switch(lastCommand) { case getHostFilenamesCmd: #if UNIX_PLATFORM if (globValid) if (globPosNameList < globS.gl_pathc) { if (!(result = globS.gl_pathv[globPosNameList][globPosName++])) { globPosNameList++; globPosName = 0; } } else { globValid = FALSE; lastCommand = 0; globfree(&globS); } #elif defined (_WIN32) if (globValid) if (globFinished) globValid = FALSE; else if (firstPathCharacter <= lastPathSeparator) { result = cpmCommandLine[firstPathCharacter++]; } else if (!(result = FindFileData.cFileName[globPosName++])) { globPosName = firstPathCharacter = 0; if (!FindNextFile(hFind, &FindFileData)) { globFinished = TRUE; FindClose(hFind); hFind = INVALID_HANDLE_VALUE; } } #else lastCommand = 0; #endif break; case attachPTRCmd: case attachPTPCmd: result = lastCPMStatus; lastCommand = 0; break; case getClockZSDOSCmd: if (currentTimeValid) switch(getClockZSDOSPos) { case 0: result = toBCD(currentTime.tm_year > 99 ? currentTime.tm_year - 100 : currentTime.tm_year); getClockZSDOSPos = 1; break; case 1: result = toBCD(currentTime.tm_mon + 1); getClockZSDOSPos = 2; break; case 2: result = toBCD(currentTime.tm_mday); getClockZSDOSPos = 3; break; case 3: result = toBCD(currentTime.tm_hour); getClockZSDOSPos = 4; break; case 4: result = toBCD(currentTime.tm_min); getClockZSDOSPos = 5; break; case 5: result = toBCD(currentTime.tm_sec); getClockZSDOSPos = lastCommand = 0; break; } else result = getClockZSDOSPos = lastCommand = 0; break; case getClockCPM3Cmd: if (currentTimeValid) switch(getClockCPM3Pos) { case 0: result = daysCPM3SinceOrg & 0xff; getClockCPM3Pos = 1; break; case 1: result = (daysCPM3SinceOrg >> 8) & 0xff; getClockCPM3Pos = 2; break; case 2: result = toBCD(currentTime.tm_hour); getClockCPM3Pos = 3; break; case 3: result = toBCD(currentTime.tm_min); getClockCPM3Pos = 4; break; case 4: result = toBCD(currentTime.tm_sec); getClockCPM3Pos = lastCommand = 0; break; } else result = getClockCPM3Pos = lastCommand = 0; break; case getSIMHVersionCmd: result = version[versionPos++]; if (result == 0) versionPos = lastCommand = 0; break; case getBankSelectCmd: if (cpu_unit.flags & UNIT_CPU_BANKED) result = getBankSelect(); else { result = 0; TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Get selected bank ignored for non-banked memory." NLP, PCX)); } lastCommand = 0; break; case getCommonCmd: if (getCommonPos == 0) { result = getCommon() & 0xff; getCommonPos = 1; } else { result = (getCommon() >> 8) & 0xff; getCommonPos = lastCommand = 0; } break; case hasBankedMemoryCmd: result = cpu_unit.flags & UNIT_CPU_BANKED ? MAXBANKS : 0; lastCommand = 0; break; case readStopWatchCmd: if (getStopWatchDeltaPos == 0) { result = stopWatchDelta & 0xff; getStopWatchDeltaPos = 1; } else { result = (stopWatchDelta >> 8) & 0xff; getStopWatchDeltaPos = lastCommand = 0; } break; case getHostOSPathSeparatorCmd: #if defined (__MWERKS__) && defined (macintosh) result = ':'; /* colon on Macintosh OS 9 */ #elif defined (_WIN32) result = '\\'; /* back slash in Windows */ #else result = '/'; /* slash in UNIX */ #endif break; default: TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Undefined IN from SIMH pseudo device on port %03xh ignored." NLP, PCX, port)); result = lastCommand = 0; } return result; } void do_SIMH_sleep(void) { #if defined (_WIN32) if ((SIMHSleep / 1000) && !sio_unit.u4) /* time to sleep and SIO not attached to a file */ Sleep(SIMHSleep / 1000); #else if (SIMHSleep && !sio_unit.u4) /* time to sleep and SIO not attached to a file */ usleep(SIMHSleep); #endif } static int32 simh_out(const int32 port, const int32 data) { time_t now; switch(lastCommand) { case setClockZSDOSCmd: if (setClockZSDOSPos == 0) { setClockZSDOSAdr = data; setClockZSDOSPos = 1; } else { setClockZSDOSAdr |= (data << 8); setClockZSDOS(); setClockZSDOSPos = lastCommand = 0; } break; case setClockCPM3Cmd: if (setClockCPM3Pos == 0) { setClockCPM3Adr = data; setClockCPM3Pos = 1; } else { setClockCPM3Adr |= (data << 8); setClockCPM3(); setClockCPM3Pos = lastCommand = 0; } break; case setBankSelectCmd: if (cpu_unit.flags & UNIT_CPU_BANKED) setBankSelect(data & BANKMASK); else { TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Set selected bank to %i ignored for non-banked memory." NLP, PCX, data & 3)); } lastCommand = 0; break; case setTimerDeltaCmd: if (setTimerDeltaPos == 0) { timerDelta = data; setTimerDeltaPos = 1; } else { timerDelta |= (data << 8); setTimerDeltaPos = lastCommand = 0; } break; case setTimerInterruptAdrCmd: if (setTimerInterruptAdrPos == 0) { timerInterruptHandler = data; setTimerInterruptAdrPos = 1; } else { timerInterruptHandler |= (data << 8); setTimerInterruptAdrPos = lastCommand = 0; } break; default: TRACE_PRINT(CMD_MSG, ("SIMH: " ADDRESS_FORMAT " CMD(0x%02x) <- %i (0x%02x, '%s')" NLP, PCX, port, data, data, (0 <= data) && (data <= lastSIMHCommand) ? cmdNames[data] : "Unknown command")); lastCommand = data; switch(data) { case getHostFilenamesCmd: #if UNIX_PLATFORM if (!globValid) { globValid = TRUE; globPosNameList = globPosName = 0; createCPMCommandLine(); globError = glob(cpmCommandLine, GLOB_ERR, NULL, &globS); if (globError) { TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Cannot expand '%s'. Error is %i." NLP, PCX, cpmCommandLine, globError)); globfree(&globS); globValid = FALSE; } } #elif defined (_WIN32) if (!globValid) { globValid = TRUE; globPosName = 0; globFinished = FALSE; createCPMCommandLine(); setLastPathSeparator(); hFind = FindFirstFile(cpmCommandLine, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { TRACE_PRINT(VERBOSE_MSG, ("SIMH: " ADDRESS_FORMAT " Cannot expand '%s'. Error is %lu." NLP, PCX, cpmCommandLine, GetLastError())); globValid = FALSE; } } #endif break; case SIMHSleepCmd: do_SIMH_sleep(); break; case printTimeCmd: /* print time */ if (rtc_avail) { printf("SIMH: " ADDRESS_FORMAT " Current time in milliseconds = %d." NLP, PCX, sim_os_msec()); } else { warnNoRealTimeClock(); } break; case startTimerCmd: /* create a new timer on top of stack */ if (rtc_avail) if (markTimeSP < TIMER_STACK_LIMIT) markTime[markTimeSP++] = sim_os_msec(); else { printf("SIMH: " ADDRESS_FORMAT " Timer stack overflow." NLP, PCX); } else warnNoRealTimeClock(); break; case stopTimerCmd: /* stop timer on top of stack and show time difference */ if (rtc_avail) if (markTimeSP > 0) { uint32 delta = sim_os_msec() - markTime[--markTimeSP]; printf("SIMH: " ADDRESS_FORMAT " Timer stopped. Elapsed time in milliseconds = %d." NLP, PCX, delta); } else { printf("SIMH: " ADDRESS_FORMAT " No timer active." NLP, PCX); } else warnNoRealTimeClock(); break; case resetPTRCmd: /* reset ptr device */ ptr_reset(NULL); break; case attachPTRCmd: /* attach ptr to the file with name at beginning of CP/M command line */ attachCPM(&ptr_unit); break; case detachPTRCmd: /* detach ptr */ detach_unit(&ptr_unit); break; case getSIMHVersionCmd: versionPos = 0; break; case getClockZSDOSCmd: time(&now); now += ClockZSDOSDelta; currentTime = *localtime(&now); currentTimeValid = TRUE; getClockZSDOSPos = 0; break; case setClockZSDOSCmd: setClockZSDOSPos = 0; break; case getClockCPM3Cmd: time(&now); now += ClockCPM3Delta; currentTime = *localtime(&now); currentTimeValid = TRUE; daysCPM3SinceOrg = (int32) ((now - mkCPM3Origin()) / SECONDS_PER_DAY); getClockCPM3Pos = 0; break; case setClockCPM3Cmd: setClockCPM3Pos = 0; break; case getBankSelectCmd: case setBankSelectCmd: case getCommonCmd: case hasBankedMemoryCmd: case getHostOSPathSeparatorCmd: break; case resetSIMHInterfaceCmd: markTimeSP = 0; lastCommand = 0; #if UNIX_PLATFORM if (globValid) { globValid = FALSE; globfree(&globS); } #elif defined (_WIN32) if (globValid) { globValid = FALSE; if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); } } #endif break; case showTimerCmd: /* show time difference to timer on top of stack */ if (rtc_avail) if (markTimeSP > 0) { uint32 delta = sim_os_msec() - markTime[markTimeSP - 1]; printf("SIMH: " ADDRESS_FORMAT " Timer running. Elapsed in milliseconds = %d." NLP, PCX, delta); } else { printf("SIMH: " ADDRESS_FORMAT " No timer active." NLP, PCX); } else warnNoRealTimeClock(); break; case attachPTPCmd: /* attach ptp to the file with name at beginning of CP/M command line */ attachCPM(&ptp_unit); break; case detachPTPCmd: /* detach ptp */ detach_unit(&ptp_unit); break; case setZ80CPUCmd: chiptype = CHIP_TYPE_Z80; break; case set8080CPUCmd: chiptype = CHIP_TYPE_8080; break; case startTimerInterruptsCmd: if (simh_dev_set_timeron(NULL, 0, NULL, NULL) == SCPE_OK) { timerInterrupt = FALSE; simh_unit.flags |= UNIT_SIMH_TIMERON; } break; case stopTimerInterruptsCmd: simh_unit.flags &= ~UNIT_SIMH_TIMERON; simh_dev_set_timeroff(NULL, 0, NULL, NULL); break; case setTimerDeltaCmd: setTimerDeltaPos = 0; break; case setTimerInterruptAdrCmd: setTimerInterruptAdrPos = 0; break; case resetStopWatchCmd: stopWatchNow = rtc_avail ? sim_os_msec() : 0; break; case readStopWatchCmd: getStopWatchDeltaPos = 0; stopWatchDelta = rtc_avail ? sim_os_msec() - stopWatchNow : 0; break; default: TRACE_PRINT(CMD_MSG, ("SIMH: " ADDRESS_FORMAT " Unknown command (%i) to SIMH pseudo device on port %03xh ignored." NLP, PCX, data, port)); } } return 0x00; /* ignored, since OUT */ } /* port 0xfe is a device for communication SIMH <--> Altair machine */ int32 simh_dev(const int32 port, const int32 io, const int32 data) { int32 result = 0; if (io == 0) { result = simh_in(port); TRACE_PRINT(IN_MSG, ("SIMH: " ADDRESS_FORMAT " IN(0x%02x) -> %i (0x%02x, '%c')" NLP, PCX, port, result, result, (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?')); } else { TRACE_PRINT(OUT_MSG, ("SIMH: " ADDRESS_FORMAT " OUT(0x%02x) <- %i (0x%02x, '%c')" NLP, PCX, port, data, data, (32 <= (data & 0xff)) && ((data & 0xff) <= 127) ? (data & 0xff) : '?')); simh_out(port, data); } return result; } simh-3.8.1/AltairZ80/altairz80_hdsk.c0000644000175000017500000007467011111377140015364 0ustar vlmvlm/* altairz80_hdsk.c: simulated hard disk device to increase capacity Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Contains code from Howard M. Harte for defining and changing disk geometry. */ #include "altairz80_defs.h" #include /* Debug flags */ #define READ_MSG (1 << 0) #define WRITE_MSG (1 << 1) #define VERBOSE_MSG (1 << 2) /* The following routines are based on work from Howard M. Harte */ static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc); static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc); static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat hdsk_reset(DEVICE *dptr); static t_stat hdsk_attach(UNIT *uptr, char *cptr); #define UNIT_V_HDSK_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_HDSK_WLK (1 << UNIT_V_HDSK_WLK) #define HDSK_MAX_SECTOR_SIZE 1024 /* maximum size of a sector */ #define HDSK_SECTOR_SIZE u5 /* size of sector */ #define HDSK_SECTORS_PER_TRACK u4 /* sectors per track */ #define HDSK_NUMBER_OF_TRACKS u3 /* number of tracks */ #define HDSK_FORMAT_TYPE u6 /* Disk Format Type */ #define HDSK_CAPACITY (2048*32*128) /* Default Altair HDSK Capacity */ #define HDSK_NUMBER 8 /* number of hard disks */ #define CPM_OK 0 /* indicates to CP/M everything ok */ #define CPM_ERROR 1 /* indicates to CP/M an error condition */ #define CPM_EMPTY 0xe5 /* default value for non-existing bytes */ #define HDSK_NONE 0 #define HDSK_RESET 1 #define HDSK_READ 2 #define HDSK_WRITE 3 #define HDSK_PARAM 4 #define HDSK_BOOT_ADDRESS 0x5c00 #define DPB_NAME_LENGTH 15 #define BOOTROM_SIZE_HDSK 256 extern uint32 PCX; extern REG *sim_PC; extern UNIT cpu_unit; extern void install_ALTAIRbootROM(void); extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); extern uint8 GetBYTEWrapper(const uint32 Addr); extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); extern int32 bootrom_dsk[]; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); static t_stat hdsk_boot(int32 unitno, DEVICE *dptr); int32 hdsk_io(const int32 port, const int32 io, const int32 data); static int32 hdskLastCommand = HDSK_NONE; static int32 hdskCommandPosition = 0; static int32 paramcount = 0; static int32 selectedDisk; static int32 selectedSector; static int32 selectedTrack; static int32 selectedDMA; typedef struct { char name[DPB_NAME_LENGTH + 1]; /* name of CP/M disk parameter block */ t_addr capac; /* capacity */ uint16 spt; /* sectors per track */ uint8 bsh; /* data allocation block shift factor */ uint8 blm; /* data allocation block mask */ uint8 exm; /* extent mask */ uint16 dsm; /* maximum data block number */ uint16 drm; /* total number of directory entries */ uint8 al0; /* determine reserved directory blocks */ uint8 al1; /* determine reserved directory blocks */ uint16 cks; /* size of directory check vector */ uint16 off; /* number of reserved tracks */ uint8 psh; /* physical record shift factor, CP/M 3 */ uint8 phm; /* physical record mask, CP/M 3 */ } DPB; typedef struct { PNP_INFO pnp; /* Plug and Play */ } HDSK_INFO; static HDSK_INFO hdsk_info_data = { { 0x0000, 0, 0xFD, 1 } }; /* static HDSK_INFO *hdsk_info = &hdsk_info_data; */ /* Note in the following CKS = 0 for fixed media which are not supposed to be changed while CP/M is executing */ static DPB dpb[] = { /* name capac spt bsh blm exm dsm drm al0 al1 cks off psh phm */ { "HDSK", HDSK_CAPACITY, 32, 0x05, 0x1F, 0x01, 0x07f9, 0x03FF, 0xFF, 0x00, 0x0000, 0x0006, 0x00, 0x00 }, /* AZ80 HDSK */ { "EZ80FL", 131072, 32, 0x03, 0x07, 0x00, 127, 0x003E, 0xC0, 0x00, 0x0000, 0x0000, 0x02, 0x03 }, /* 128K FLASH */ { "P112", 1474560, 72, 0x04, 0x0F, 0x00, 710, 0x00FE, 0xF0, 0x00, 0x0000, 0x0002, 0x02, 0x03 }, /* 1.44M P112 */ { "SU720", 737280, 36, 0x04, 0x0F, 0x00, 354, 0x007E, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* 720K Super I/O */ { "OSB1", 102400, 20, 0x04, 0x0F, 0x01, 45, 0x003F, 0x80, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS SD */ { "OSB2", 204800, 40, 0x03, 0x07, 0x00, 184, 0x003F, 0xC0, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS DD */ { "NSSS1", 179200, 40, 0x03, 0x07, 0x00, 0xA4, 0x003F, 0xC0, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 1 */ { "NSSS2", 179200, 40, 0x04, 0x0F, 0x01, 0x51, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 2 */ { "NSDS2", 358400, 40, 0x04, 0x0F, 0x01, 0xA9, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar DSDD Format 2 */ { "VGSS", 315392, 32, 0x04, 0x0F, 0x00, 149, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* Vector SS SD */ { "VGDS", 632784, 32, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0004, 0x02, 0x03 }, /* Vector DS SD */ /* Note on DISK1A Images: this is a bit of a mess. The first track on the disk is 128x26 bytes (SD) and to make this work I had to "move" the data from 0x2d00 in the DSK image file down to 0x4000 (2-tracks in). I used WinHex to do it. */ { "DISK1A", 630784, 64, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* CompuPro Disk1A 8" SS SD */ { "SSSD8", 256256, 26, 0x03, 0x07, 0x00, 242, 0x003F, 0xC0, 0x00, 0x0000, 0x0002, 0x00, 0x00 }, /* Standard 8" SS SD */ { "", 0 } }; static UNIT hdsk_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) } }; static REG hdsk_reg[] = { { DRDATA (HDCMD, hdskLastCommand, 32), REG_RO }, { DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO }, { DRDATA (HDDSK, selectedDisk, 32), REG_RO }, { DRDATA (HDSEC, selectedSector, 32), REG_RO }, { DRDATA (HDTRK, selectedTrack, 32), REG_RO }, { DRDATA (HDDMA, selectedDMA, 32), REG_RO }, { NULL } }; static MTAB hdsk_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &set_format, &show_format, NULL }, { UNIT_HDSK_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_HDSK_WLK, UNIT_HDSK_WLK, "WRTLCK", "WRTLCK", NULL }, { MTAB_XTD|MTAB_VUN, 0, "GEOM", "GEOM", &set_geom, &show_geom, NULL }, { 0 } }; #define TRACE_PRINT(level, args) if (hdsk_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB hdsk_dt[] = { { "READ", READ_MSG }, { "WRITE", WRITE_MSG }, { "VERBOSE", VERBOSE_MSG }, { NULL, 0 } }; DEVICE hdsk_dev = { "HDSK", hdsk_unit, hdsk_reg, hdsk_mod, 8, 10, 31, 1, 8, 8, NULL, NULL, &hdsk_reset, &hdsk_boot, &hdsk_attach, NULL, &hdsk_info_data, (DEV_DISABLE | DEV_DEBUG), 0, hdsk_dt, NULL, "Hard Disk HDSK" }; /* Reset routine */ static t_stat hdsk_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if (dptr->flags & DEV_DIS) { sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, TRUE); } else { /* Connect HDSK at base address */ if (sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); dptr->flags |= DEV_DIS; return SCPE_ARG; } } return SCPE_OK; } /* Attach routine */ static t_stat hdsk_attach(UNIT *uptr, char *cptr) { t_stat r; uint32 i; char unitChar; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Step 1: Determine capacity of this disk */ uptr -> capac = sim_fsize(uptr -> fileref); /* the file length is a good candidate */ if (uptr -> capac == 0) { /* file does not exist or has length 0 */ uptr -> capac = uptr -> HDSK_NUMBER_OF_TRACKS * uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE; if (uptr -> capac == 0) uptr -> capac = HDSK_CAPACITY; } /* post condition: uptr -> capac > 0 */ assert(uptr -> capac); /* Step 2: Determine format based on disk capacity */ uptr -> HDSK_FORMAT_TYPE = -1; /* default to unknown format type */ for (i = 0; dpb[i].capac != 0; i++) { /* find disk parameter block */ if (dpb[i].capac == uptr -> capac) { /* found if correct capacity */ uptr -> HDSK_FORMAT_TYPE = i; break; } } /* Step 3: Set number of sectors per track and sector size */ if (uptr -> HDSK_FORMAT_TYPE == -1) { /* Case 1: no disk parameter block found*/ for (i = 0; i < hdsk_dev.numunits; i++) /* find affected unit number */ if (&hdsk_unit[i] == uptr) break; /* found */ unitChar = '0' + i; uptr -> HDSK_FORMAT_TYPE = 0; printf("HDSK%c: WARNING: Unsupported disk capacity, assuming HDSK type with capacity %iKB.\n", unitChar, uptr -> capac / 1000); uptr -> flags |= UNIT_HDSK_WLK; printf("HDSK%c: WARNING: Forcing WRTLCK.\n", unitChar); /* check whether capacity corresponds to setting of tracks, sectors per track and sector size */ if (uptr -> capac != (uint32)(uptr -> HDSK_NUMBER_OF_TRACKS * uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE)) { printf("HDSK%c: WARNING: Fixing geometry.\n", unitChar); if (uptr -> HDSK_SECTORS_PER_TRACK == 0) uptr -> HDSK_SECTORS_PER_TRACK = 32; if (uptr -> HDSK_SECTOR_SIZE == 0) uptr -> HDSK_SECTOR_SIZE = 128; } } else { /* Case 2: disk parameter block found */ uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh; uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh); } assert(uptr -> HDSK_SECTORS_PER_TRACK && uptr -> HDSK_SECTOR_SIZE); /* Step 4: Number of tracks is smallest number to accomodate capacity */ uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE - 1) / (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE); assert( ( (t_addr) ((uptr -> HDSK_NUMBER_OF_TRACKS - 1) * uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) < uptr -> capac) && (uptr -> capac <= (t_addr) (uptr -> HDSK_NUMBER_OF_TRACKS * uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) ) ); return SCPE_OK; } /* Set disk geometry routine */ static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 numberOfTracks, numberOfSectors, sectorSize; int result, n; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; result = sscanf(cptr, "%d/%d/%d%n", &numberOfTracks, &numberOfSectors, §orSize, &n); if ((result != 3) || (result == EOF) || (cptr[n] != 0)) { result = sscanf(cptr, "T:%d/N:%d/S:%d%n", &numberOfTracks, &numberOfSectors, §orSize, &n); if ((result != 3) || (result == EOF) || (cptr[n] != 0)) return SCPE_ARG; } uptr -> HDSK_NUMBER_OF_TRACKS = numberOfTracks; uptr -> HDSK_SECTORS_PER_TRACK = numberOfSectors; uptr -> HDSK_SECTOR_SIZE = sectorSize; uptr -> capac = numberOfTracks * numberOfSectors * sectorSize; return SCPE_OK; } /* Show disk geometry routine */ static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc) { if (uptr == NULL) return SCPE_IERR; fprintf(st, "T:%d/N:%d/S:%d", uptr -> HDSK_NUMBER_OF_TRACKS, uptr -> HDSK_SECTORS_PER_TRACK, uptr -> HDSK_SECTOR_SIZE); return SCPE_OK; } #define QUOTE1(text) #text #define QUOTE2(text) QUOTE1(text) /* Set disk format routine */ static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc) { char fmtname[DPB_NAME_LENGTH + 1]; int32 i; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; if (sscanf(cptr, "%" QUOTE2(DPB_NAME_LENGTH) "s", fmtname) == 0) return SCPE_ARG; for (i = 0; dpb[i].capac != 0; i++) { if (strncmp(fmtname, dpb[i].name, strlen(fmtname)) == 0) { uptr -> HDSK_FORMAT_TYPE = i; uptr -> capac = dpb[i].capac; /* Set capacity */ /* Configure physical disk geometry */ uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh); uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh; uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE - 1) / (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE); return SCPE_OK; } } return SCPE_ARG; } /* Show disk format routine */ static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc) { if (uptr == NULL) return SCPE_IERR; fprintf(st, "%s", dpb[uptr -> HDSK_FORMAT_TYPE].name); return SCPE_OK; } static int32 bootrom_hdsk[BOOTROM_SIZE_HDSK] = { 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */ 0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */ 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */ 0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */ 0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */ 0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */ 0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */ 0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */ 0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */ 0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */ 0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */ 0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */ 0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */ 0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */ 0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */ }; static t_stat hdsk_boot(int32 unitno, DEVICE *dptr) { if (MEMORYSIZE < 24*KB) { printf("Need at least 24KB RAM to boot from hard disk.\n"); return SCPE_ARG; } if (cpu_unit.flags & (UNIT_CPU_ALTAIRROM | UNIT_CPU_BANKED)) { /* check whether we are really modifying an LD A,<> instruction */ if (bootrom_dsk[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION) bootrom_dsk[UNIT_NO_OFFSET_1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A, */ else { /* Attempt to modify non LD A,<> instructions is refused. */ printf("Incorrect boot ROM offset detected.\n"); return SCPE_IERR; } install_ALTAIRbootROM(); /* install modified ROM */ } assert(install_bootrom(bootrom_hdsk, BOOTROM_SIZE_HDSK, HDSK_BOOT_ADDRESS, FALSE) == SCPE_OK); *((int32 *) sim_PC->loc) = HDSK_BOOT_ADDRESS; return SCPE_OK; } /* The hard disk port is 0xfd. It understands the following commands. 1. Reset ld b,32 ld a,HDSK_RESET l: out (0fdh),a dec b jp nz,l 2. Read / write ; parameter block cmd: db HDSK_READ or HDSK_WRITE hd: db 0 ; 0 .. 7, defines hard disk to be used sector: db 0 ; 0 .. 31, defines sector track: dw 0 ; 0 .. 2047, defines track dma: dw 0 ; defines where result is placed in memory ; routine to execute ld b,7 ; size of parameter block ld hl,cmd ; start address of parameter block l: ld a,(hl) ; get byte of parameter block out (0fdh),a ; send it to port inc hl ; point to next byte dec b ; decrement counter jp nz,l ; again, if not done in a,(0fdh) ; get result code 3. Retrieve Disk Parameters from controller (Howard M. Harte) Reads a 19-byte parameter block from the disk controller. This parameter block is in CP/M DPB format for the first 17 bytes, and the last two bytes are the lsb/msb of the disk's physical sector size. ; routine to execute ld a,hdskParam ; hdskParam = 4 out (hdskPort),a ; Send 'get parameters' command, hdskPort = 0fdh ld a,(diskno) out (hdskPort),a ; Send selected HDSK number ld b,17 1: in a,(hdskPort) ; Read 17-bytes of DPB ld (hl), a inc hl djnz 1 in a,(hdskPort) ; Read LSB of disk's physical sector size. ld (hsecsiz), a in a,(hdskPort) ; Read MSB of disk's physical sector size. ld (hsecsiz+1), a */ /* check the parameters and return TRUE iff parameters are correct or have been repaired */ static int32 checkParameters(void) { UNIT *uptr = &hdsk_dev.units[selectedDisk]; if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Disk %i does not exist, will use HDSK0 instead." NLP, selectedDisk, PCX, selectedDisk)); selectedDisk = 0; } if ((hdsk_dev.units[selectedDisk].flags & UNIT_ATT) == 0) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Disk %i is not attached." NLP, selectedDisk, PCX, selectedDisk)); return FALSE; /* cannot read or write */ } if ((selectedSector < 0) || (selectedSector >= uptr -> HDSK_SECTORS_PER_TRACK)) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Constraint violation 0 <= Sector=%02d < %d, will use sector 0 instead." NLP, selectedDisk, PCX, selectedSector, uptr -> HDSK_SECTORS_PER_TRACK)); selectedSector = 0; } if ((selectedTrack < 0) || (selectedTrack >= uptr -> HDSK_NUMBER_OF_TRACKS)) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Constraint violation 0 <= Track=%04d < %04d, will use track 0 instead." NLP, selectedDisk, PCX, selectedTrack, uptr -> HDSK_NUMBER_OF_TRACKS)); selectedTrack = 0; } selectedDMA &= ADDRMASK; if ((hdsk_dev.dctrl & READ_MSG) && (hdskLastCommand == HDSK_READ)) printf("HDSK%d " ADDRESS_FORMAT " Read Track=%04d Sector=%02d Len=%04d DMA=%04x" NLP, selectedDisk, PCX, selectedTrack, selectedSector, uptr -> HDSK_SECTOR_SIZE, selectedDMA); if ((hdsk_dev.dctrl & WRITE_MSG) && (hdskLastCommand == HDSK_WRITE)) printf("HDSK%d " ADDRESS_FORMAT " Write Track=%04d Sector=%02d Len=%04d DMA=%04x" NLP, selectedDisk, PCX, selectedTrack, selectedSector, uptr -> HDSK_SECTOR_SIZE, selectedDMA); return TRUE; } static int32 doSeek(void) { UNIT *uptr = &hdsk_dev.units[selectedDisk]; if (fseek(uptr -> fileref, (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) * selectedTrack + (uptr -> HDSK_SECTOR_SIZE * selectedSector), SEEK_SET)) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not access Sector=%02d Track=%04d." NLP, selectedDisk, PCX, selectedSector, selectedTrack)); return CPM_ERROR; } else return CPM_OK; } uint8 hdskbuf[HDSK_MAX_SECTOR_SIZE] = { 0 }; /* data buffer */ static int32 doRead(void) { int32 i; UNIT *uptr = &hdsk_dev.units[selectedDisk]; if (doSeek()) return CPM_ERROR; if (fread(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) { for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = CPM_EMPTY; TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not read Sector=%02d Track=%04d." NLP, selectedDisk, PCX, selectedSector, selectedTrack)); return CPM_OK; /* allows the creation of empty hard disks */ } for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) PutBYTEWrapper(selectedDMA + i, hdskbuf[i]); return CPM_OK; } static int32 doWrite(void) { int32 i; UNIT *uptr = &hdsk_dev.units[selectedDisk]; if (((uptr -> flags) & UNIT_HDSK_WLK) == 0) { /* write enabled */ if (doSeek()) return CPM_ERROR; for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = GetBYTEWrapper(selectedDMA + i); if (fwrite(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not write Sector=%02d Track=%04d." NLP, selectedDisk, PCX, selectedSector, selectedTrack)); return CPM_ERROR; } } else { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Could not write to locked disk Sector=%02d Track=%04d." NLP, selectedDisk, PCX, selectedSector, selectedTrack)); return CPM_ERROR; } return CPM_OK; } static int32 hdsk_in(const int32 port) { UNIT *uptr = &hdsk_dev.units[selectedDisk]; int32 result; if ((hdskCommandPosition == 6) && ((hdskLastCommand == HDSK_READ) || (hdskLastCommand == HDSK_WRITE))) { result = checkParameters() ? ((hdskLastCommand == HDSK_READ) ? doRead() : doWrite()) : CPM_ERROR; hdskLastCommand = HDSK_NONE; hdskCommandPosition = 0; return result; } else if (hdskLastCommand == HDSK_PARAM) { DPB current = dpb[uptr -> HDSK_FORMAT_TYPE]; uint8 params[17]; params[ 0] = current.spt & 0xff; params[ 1] = (current.spt >> 8) & 0xff; params[ 2] = current.bsh; params[ 3] = current.blm; params[ 4] = current.exm; params[ 5] = current.dsm & 0xff; params[ 6] = (current.dsm >> 8) & 0xff; params[ 7] = current.drm & 0xff; params[ 8] = (current.drm >> 8) & 0xff; params[ 9] = current.al0; params[10] = current.al1; params[11] = current.cks & 0xff; params[12] = (current.cks >> 8) & 0xff; params[13] = current.off & 0xff; params[14] = (current.off >> 8) & 0xff; params[15] = current.psh; params[16] = current.phm; if (++paramcount >= 19) hdskLastCommand = HDSK_NONE; if (paramcount <= 17) return params[paramcount - 1]; else if (paramcount == 18) return (uptr -> HDSK_SECTOR_SIZE & 0xff); else if (paramcount == 19) return (uptr -> HDSK_SECTOR_SIZE >> 8); else { printf("HDSK%d: " ADDRESS_FORMAT " Get parameter error." NLP, selectedDisk, PCX); } } else { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Illegal IN command detected (port=%03xh, cmd=%d, pos=%d)." NLP, selectedDisk, PCX, port, hdskLastCommand, hdskCommandPosition)); } return CPM_OK; } static int32 hdsk_out(const int32 port, const int32 data) { switch(hdskLastCommand) { case HDSK_PARAM: paramcount = 0; selectedDisk = data; break; case HDSK_READ: case HDSK_WRITE: switch(hdskCommandPosition) { case 0: selectedDisk = data; hdskCommandPosition++; break; case 1: selectedSector = data; hdskCommandPosition++; break; case 2: selectedTrack = data; hdskCommandPosition++; break; case 3: selectedTrack += (data << 8); hdskCommandPosition++; break; case 4: selectedDMA = data; hdskCommandPosition++; break; case 5: selectedDMA += (data << 8); hdskCommandPosition++; break; default: hdskLastCommand = HDSK_NONE; hdskCommandPosition = 0; } break; default: if ((HDSK_RESET <= data) && (data <= HDSK_PARAM)) hdskLastCommand = data; else { TRACE_PRINT(VERBOSE_MSG, ("HDSK%d: " ADDRESS_FORMAT " Illegal OUT command detected (port=%03xh, cmd=%d)." NLP, selectedDisk, PCX, port, data)); hdskLastCommand = HDSK_RESET; } hdskCommandPosition = 0; } return 0; /* ignored, since OUT */ } int32 hdsk_io(const int32 port, const int32 io, const int32 data) { return io == 0 ? hdsk_in(port) : hdsk_out(port, data); } simh-3.8.1/AltairZ80/vfdhd.h0000644000175000017500000000606210766764756013657 0ustar vlmvlm/************************************************************************* * * * $Id: vfdhd.h 1694 2007-12-14 05:23:11Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Micropolis FDC module for SIMH definitions * * * * Environment: * * User mode only * * * *************************************************************************/ extern int32 mfdhd_dev(const int32 port, const int32 io, const int32 data); simh-3.8.1/AltairZ80/i86_ops.c0000644000175000017500000045004411111141266014013 0ustar vlmvlm/* * Dos/PC Emulator * Copyright (C) 1991 Jim Hudgens * * * The file is part of GDE. * * GDE is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * GDE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GDE; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "altairz80_defs.h" #include "i86.h" extern void out(const uint32 Port, const uint32 Value); extern uint32 in(const uint32 Port); /* $Log: i86_ops.c,v $ * Revision 0.11 1991/07/30 02:02:04 hudgens * added copyright. * * Revision 0.10 1991/07/21 18:22:08 hudgens * Fixed problem with scans, which was the result of the loop break * condition being incorrect when used in conjunction with the repe * or repne prefixes. Eureka. pkzip/pkunzip now compress/decompress * correctly. * * Revision 0.9 1991/07/21 03:33:18 hudgens * fixed popf so that it appears to be the same as an 8088 popf, and always * sets the high 4 bits of the flags. * * Revision 0.8 1991/07/21 01:44:11 hudgens * fixed aad and aam instructions. * * Revision 0.7 1991/07/21 00:31:24 hudgens * Fixed iret so that it works correctly. * * Revision 0.6 1991/07/20 16:54:50 hudgens * removed the 8087 coprocessor operations. Moved to i87_ops.c * * Revision 0.5 1991/07/17 03:50:10 hudgens * Minor modifications. * * Revision 0.4 1991/06/18 02:48:41 hudgens * Fixed a problem with scasb and scasw. * * Revision 0.3 1991/06/03 01:01:10 hudgens * fixed minor problems due to unsigned to signed short integer * promotions. * * Revision 0.2 1991/03/31 01:32:10 hudgens * fixed segment handling. Added calls to DECODE_CLEAR_SEGOVR in * many places in the code. Should work much better now. * * Revision 0.1 1991/03/30 21:15:48 hudgens * Initial checkin to RCS. * * */ /* 2/23/91 fixed decode for operand x87. */ /* partially mechanically generated file....(based on the optable) */ /* There are approximately 250 subroutines in here, which correspond to the 256 byte-"opcodes" found on the 8086. The table which dispatches this is found in the files optab.[ch]. Each opcode proc has a comment preceeding it which gives it's table address. Several opcodes are missing (undefined) in the table. Each proc includes information for decoding (DECODE_PRINTF and and misc functions ( Many of the procedures are *VERY* similar in coding. This has allowed for a very large amount of code to be generated in a fairly short amount of time (i.e. cut, paste, and modify). The result is that much of the code below could have been folded into subroutines for a large reduction in size of this file. The downside would be that there would be a penalty in execution speed. The file could also have been *MUCH* larger by inlining certain functions which were called. This could have resulted even faster execution. The prime directive I used to decide whether to inline the code or to modularize it, was basically: 1) no unnecessary subroutine calls, 2) no routines more than about 200 lines in size, and 3) modularize any code that I might not get right the first time. The fetch_* subroutines fall into the latter category. The The decode_* fall into the second category. The coding of the "switch(mod){ .... }" in many of the subroutines below falls into the first category. Especially, the coding of {add,and,or,sub,...}_{byte,word} subroutines are an especially glaring case of the third guideline. Since so much of the code is cloned from other modules (compare opcode #00 to opcode #01), making the basic operations subroutine calls is especially important; otherwise mistakes in coding an "add" would represent a nightmare in maintenance. So, without further ado, ... */ extern char parity_tab[]; static void i86op_illegal_op(PC_ENV *m) { intr |= INTR_ILLEGAL_OPCODE; } /*opcode=0x00*/ static void i86op_add_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = add_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = add_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = add_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = add_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x01*/ static void i86op_add_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = add_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = add_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = add_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = add_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x02*/ static void i86op_add_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = add_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = add_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = add_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = add_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x03*/ static void i86op_add_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = add_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = add_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = add_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = add_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x04*/ static void i86op_add_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = add_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x05*/ static void i86op_add_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = add_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x06*/ static void i86op_push_ES(PC_ENV *m) { push_word(m,m->R_ES); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x07*/ static void i86op_pop_ES(PC_ENV *m) { m->R_ES = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x08*/ static void i86op_or_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = or_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = or_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = or_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = or_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x09*/ static void i86op_or_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = or_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = or_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = or_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = or_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0a*/ static void i86op_or_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = or_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = or_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = or_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = or_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0b*/ static void i86op_or_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = or_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = or_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = or_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = or_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0c*/ static void i86op_or_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = or_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0d*/ static void i86op_or_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = or_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0e*/ static void i86op_push_CS(PC_ENV *m) { push_word(m,m->R_CS); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x0f === ILLEGAL OP*/ /*opcode=0x10*/ static void i86op_adc_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = adc_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = adc_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = adc_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = adc_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x11*/ static void i86op_adc_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = adc_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = adc_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = adc_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = adc_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x12*/ static void i86op_adc_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = adc_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = adc_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = adc_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = adc_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x13*/ static void i86op_adc_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = adc_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = adc_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = adc_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = adc_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x14*/ static void i86op_adc_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = adc_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x15*/ static void i86op_adc_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = adc_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x16*/ static void i86op_push_SS(PC_ENV *m) { push_word(m,m->R_SS); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x17*/ static void i86op_pop_SS(PC_ENV *m) { m->R_SS = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x18*/ static void i86op_sbb_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sbb_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sbb_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sbb_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = sbb_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x19*/ static void i86op_sbb_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sbb_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sbb_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sbb_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = sbb_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1a*/ static void i86op_sbb_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sbb_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sbb_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sbb_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = sbb_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1b*/ static void i86op_sbb_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sbb_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sbb_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sbb_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = sbb_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1c*/ static void i86op_sbb_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = sbb_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1d*/ static void i86op_sbb_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = sbb_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1e*/ static void i86op_push_DS(PC_ENV *m) { push_word(m,m->R_DS); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x1f*/ static void i86op_pop_DS(PC_ENV *m) { m->R_DS = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x20*/ static void i86op_and_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = and_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = and_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = and_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = and_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x21*/ static void i86op_and_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = and_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = and_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = and_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = and_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x22*/ static void i86op_and_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = and_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = and_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = and_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = and_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x23*/ static void i86op_and_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = and_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = and_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = and_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = and_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x24*/ static void i86op_and_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = and_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x25*/ static void i86op_and_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = and_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x26*/ static void i86op_segovr_ES(PC_ENV *m) { m->sysmode |= SYSMODE_SEGOVR_ES; /* note the lack of DECODE_CLEAR_SEGOVR(r) since, here is one of 4 opcode subroutines we do not want to do this. */ } /*opcode=0x27*/ static void i86op_daa(PC_ENV *m) { uint16 dbyte; dbyte = m->R_AL; if (ACCESS_FLAG(m,F_AF)|| (dbyte&0xf) > 9) { dbyte += 6; if (dbyte&0x100) SET_FLAG(m, F_CF); SET_FLAG(m, F_AF); } else CLEAR_FLAG(m, F_AF); if (ACCESS_FLAG(m,F_CF) || (dbyte&0xf0) > 0x90) { dbyte += 0x60; SET_FLAG(m, F_CF); } else CLEAR_FLAG(m, F_CF); m->R_AL = (uint8) dbyte; CONDITIONAL_SET_FLAG((m->R_AL & 0x80),m,F_SF); CONDITIONAL_SET_FLAG((m->R_AL == 0), m,F_ZF); CONDITIONAL_SET_FLAG((parity_tab[m->R_AL]),m,F_PF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x28*/ static void i86op_sub_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sub_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sub_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = sub_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = sub_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x29*/ static void i86op_sub_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sub_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sub_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = sub_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = sub_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x2a*/ static void i86op_sub_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sub_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sub_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = sub_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = sub_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x2b*/ static void i86op_sub_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sub_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sub_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = sub_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = sub_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x2c*/ static void i86op_sub_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = sub_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x2d*/ static void i86op_sub_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = sub_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x2e*/ static void i86op_segovr_CS(PC_ENV *m) { m->sysmode |= SYSMODE_SEGOVR_CS; /* note no DECODE_CLEAR_SEGOVER here. */ } /*opcode=0x2f*/ static void i86op_das(PC_ENV *m) { uint16 dbyte; dbyte = m->R_AL; if ( ACCESS_FLAG(m,F_AF) || (dbyte&0xf) > 9) { dbyte -= 6; if (dbyte&0x100) /* XXXXX --- this is WRONG */ SET_FLAG(m, F_CF); SET_FLAG(m, F_AF); } else CLEAR_FLAG(m, F_AF); if (ACCESS_FLAG(m,F_CF) || (dbyte&0xf0) > 0x90) { dbyte -= 0x60; SET_FLAG(m, F_CF); } else CLEAR_FLAG(m, F_CF); m->R_AL = (uint8) dbyte; CONDITIONAL_SET_FLAG(m->R_AL & 0x80,m,F_SF); CONDITIONAL_SET_FLAG(m->R_AL == 0,m,F_ZF); CONDITIONAL_SET_FLAG(parity_tab[m->R_AL],m,F_PF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x30*/ static void i86op_xor_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = xor_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = xor_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); destval = xor_byte(m, destval, *srcreg); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = xor_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x31*/ static void i86op_xor_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = xor_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = xor_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); destval = xor_word(m, destval, *srcreg); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = xor_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x32*/ static void i86op_xor_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = xor_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = xor_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = xor_byte(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = xor_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x33*/ static void i86op_xor_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = xor_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = xor_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = xor_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = xor_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x34*/ static void i86op_xor_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); m->R_AL = xor_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x35*/ static void i86op_xor_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); m->R_AX = xor_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x36*/ static void i86op_segovr_SS(PC_ENV *m) { m->sysmode |= SYSMODE_SEGOVR_SS; /* no DECODE_CLEAR_SEGOVER ! */ } /*opcode=0x37*/ static void i86op_aaa(PC_ENV *m) { if ( (m->R_AL & 0xf) > 0x9 || ACCESS_FLAG(m,F_AF)) { m->R_AL += 0x6; m->R_AH += 1; SET_FLAG(m, F_AF); SET_FLAG(m, F_CF); } else { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_AF); } m->R_AL &= 0xf; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x38*/ static void i86op_cmp_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); cmp_byte(m, destval, *srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); cmp_byte(m, destval, *srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); cmp_byte(m, destval, *srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); cmp_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x39*/ static void i86op_cmp_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); cmp_word(m, destval, *srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); cmp_word(m, destval, *srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); cmp_word(m, destval, *srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); cmp_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x3a*/ static void i86op_cmp_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); cmp_byte(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); cmp_byte(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); cmp_byte(m, * destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); cmp_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x3b*/ static void i86op_cmp_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); cmp_word(m, *destreg, srcval); break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); cmp_word(m, *destreg, srcval); break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); cmp_word(m, *destreg, srcval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); cmp_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x3c*/ static void i86op_cmp_byte_AL_IMM(PC_ENV *m) { uint8 srcval; srcval = fetch_byte_imm(m); cmp_byte(m, m->R_AL, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x3d*/ static void i86op_cmp_word_AX_IMM(PC_ENV *m) { uint16 srcval; srcval = fetch_word_imm(m); cmp_word(m, m->R_AX, srcval); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x3e*/ static void i86op_segovr_DS(PC_ENV *m) { m->sysmode |= SYSMODE_SEGOVR_DS; /* NO DECODE_CLEAR_SEGOVR! */ } /*opcode=0x3f*/ static void i86op_aas(PC_ENV *m) { /* ???? Check out the subtraction here. Will this ?ever? cause the contents of R_AL or R_AH to be affected incorrectly since they are being subtracted from *and* are unsigned. Should put an assertion in here. */ if ( (m->R_AL & 0xf) > 0x9 || ACCESS_FLAG(m,F_AF)) { m->R_AL -= 0x6; m->R_AH -= 1; SET_FLAG(m, F_AF); SET_FLAG(m, F_CF); } else { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_AF); } m->R_AL &= 0xf; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x40*/ static void i86op_inc_AX(PC_ENV *m) { m->R_AX = inc_word(m,m->R_AX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x41*/ static void i86op_inc_CX(PC_ENV *m) { m->R_CX = inc_word(m,m->R_CX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x42*/ static void i86op_inc_DX(PC_ENV *m) { m->R_DX = inc_word(m,m->R_DX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x43*/ static void i86op_inc_BX(PC_ENV *m) { m->R_BX = inc_word(m,m->R_BX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x44*/ static void i86op_inc_SP(PC_ENV *m) { m->R_SP = inc_word(m,m->R_SP); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x45*/ static void i86op_inc_BP(PC_ENV *m) { m->R_BP = inc_word(m,m->R_BP); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x46*/ static void i86op_inc_SI(PC_ENV *m) { m->R_SI = inc_word(m,m->R_SI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x47*/ static void i86op_inc_DI(PC_ENV *m) { m->R_DI = inc_word(m,m->R_DI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x48*/ static void i86op_dec_AX(PC_ENV *m) { m->R_AX = dec_word(m,m->R_AX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x49*/ static void i86op_dec_CX(PC_ENV *m) { m->R_CX = dec_word(m,m->R_CX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4a*/ static void i86op_dec_DX(PC_ENV *m) { m->R_DX = dec_word(m,m->R_DX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4b*/ static void i86op_dec_BX(PC_ENV *m) { m->R_BX = dec_word(m,m->R_BX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4c*/ static void i86op_dec_SP(PC_ENV *m) { m->R_SP = dec_word(m,m->R_SP); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4d*/ static void i86op_dec_BP(PC_ENV *m) { m->R_BP = dec_word(m,m->R_BP); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4e*/ static void i86op_dec_SI(PC_ENV *m) { m->R_SI = dec_word(m,m->R_SI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x4f*/ static void i86op_dec_DI(PC_ENV *m) { m->R_DI = dec_word(m,m->R_DI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x50*/ static void i86op_push_AX(PC_ENV *m) { push_word(m,m->R_AX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x51*/ static void i86op_push_CX(PC_ENV *m) { push_word(m,m->R_CX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x52*/ static void i86op_push_DX(PC_ENV *m) { push_word(m,m->R_DX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x53*/ static void i86op_push_BX(PC_ENV *m) { push_word(m,m->R_BX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x54*/ static void i86op_push_SP(PC_ENV *m) { /* .... Note this weirdness: One book I have access to claims that the value pushed here is actually sp-2. I.e. it decrements the stackpointer, and then pushes it. The 286 I have does it this way. Changing this causes many problems.*/ /* changed to push SP-2, since this *IS* how a 8088 does this */ push_word(m,m->R_SP-2); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x55*/ static void i86op_push_BP(PC_ENV *m) { push_word(m,m->R_BP); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x56*/ static void i86op_push_SI(PC_ENV *m) { push_word(m,m->R_SI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x57*/ static void i86op_push_DI(PC_ENV *m) { push_word(m,m->R_DI); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x58*/ static void i86op_pop_AX(PC_ENV *m) { m->R_AX = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x59*/ static void i86op_pop_CX(PC_ENV *m) { m->R_CX = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5a*/ static void i86op_pop_DX(PC_ENV *m) { m->R_DX = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5b*/ static void i86op_pop_BX(PC_ENV *m) { m->R_BX = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5c*/ static void i86op_pop_SP(PC_ENV *m) { m->R_SP = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5d*/ static void i86op_pop_BP(PC_ENV *m) { m->R_BP = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5e*/ static void i86op_pop_SI(PC_ENV *m) { m->R_SI = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x5f*/ static void i86op_pop_DI(PC_ENV *m) { m->R_DI = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x60 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x61 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x62 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x63 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x64 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x65 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x66 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x67 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x68 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x69 ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6a ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6b ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6c ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6d ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6e ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x6f ILLEGAL OP, calls i86op_illegal_op() */ /*opcode=0x70*/ static void i86op_jump_near_O(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if overflow flag is set */ offset = (int8) fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m,F_OF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x71*/ static void i86op_jump_near_NO(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if overflow is not set */ offset = (int8) fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (!ACCESS_FLAG(m,F_OF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x72*/ static void i86op_jump_near_B(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if carry flag is set. */ offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m, F_CF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x73*/ static void i86op_jump_near_NB(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if carry flag is clear. */ offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ target = (int16)(m->R_IP) + offset; if (!ACCESS_FLAG(m,F_CF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x74*/ static void i86op_jump_near_Z(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if zero flag is set. */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m, F_ZF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x75*/ static void i86op_jump_near_NZ(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if zero flag is clear. */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (!ACCESS_FLAG(m, F_ZF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x76*/ static void i86op_jump_near_BE(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if carry flag is set or if the zero flag is set. */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m,F_CF) || ACCESS_FLAG(m,F_ZF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x77*/ static void i86op_jump_near_NBE(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if carry flag is clear and if the zero flag is clear */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (!(ACCESS_FLAG(m,F_CF)||ACCESS_FLAG(m,F_ZF))) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x78*/ static void i86op_jump_near_S(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if sign flag is set */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m,F_SF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x79*/ static void i86op_jump_near_NS(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if sign flag is clear */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (!ACCESS_FLAG(m,F_SF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x7a*/ static void i86op_jump_near_P(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if parity flag is set (even parity) */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (ACCESS_FLAG(m, F_PF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x7b*/ static void i86op_jump_near_NP(PC_ENV *m) { int8 offset; uint16 target; /* jump to byte offset if parity flag is clear (odd parity) */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; if (!ACCESS_FLAG(m, F_PF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /* JHH fixed till here... */ /*opcode=0x7c*/ static void i86op_jump_near_L(PC_ENV *m) { int8 offset; uint16 target; int sf,of; /* jump to byte offset if sign flag not equal to overflow flag. */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; /* note: * this is the simplest expression i could think of which * expresses SF != OF. m->R_FLG&F_SF either equals x80 or x00. * Similarly m->R_FLG&F_OF either equals x800 or x000. * The former shifted right by 7 puts a 1 or 0 in bit 0. * The latter shifter right by 11 puts a 1 or 0 in bit 0. * if the two expressions are the same, i.e. equal, then * a zero results from the xor. If they are not equal, * then a 1 results, and the jump is taken. */ sf = ACCESS_FLAG(m,F_SF) != 0; of = ACCESS_FLAG(m,F_OF) != 0; /* was: if ( ((m->R_FLG & F_SF)>>7) ^ ((m->R_FLG & F_OF) >> 11))*/ if (sf ^ of) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x7d*/ static void i86op_jump_near_NL(PC_ENV *m) { int8 offset; uint16 target; int sf,of; /* jump to byte offset if sign flag not equal to overflow flag. */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; sf = ACCESS_FLAG(m,F_SF) != 0; of = ACCESS_FLAG(m,F_OF) != 0; /* note: inverse of above, but using == instead of xor. */ /* was: if (((m->R_FLG & F_SF)>>7) == ((m->R_FLG & F_OF) >> 11))*/ if (sf == of) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x7e*/ static void i86op_jump_near_LE(PC_ENV *m) { int8 offset; uint16 target; int sf,of; /* jump to byte offset if sign flag not equal to overflow flag or the zero flag is set */ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; sf = ACCESS_FLAG(m,F_SF) != 0; of = ACCESS_FLAG(m,F_OF) != 0; /* note: modification of JL */ /* sf != of */ /* was: if ((((m->R_FLG & F_SF)>>7) ^ ((m->R_FLG & F_OF) >> 11)) || (m->R_FLG & F_ZF) ) */ if ( (sf ^ of) || ACCESS_FLAG(m,F_ZF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x7f*/ static void i86op_jump_near_NLE(PC_ENV *m) { int8 offset; uint16 target; int sf,of; /* jump to byte offset if sign flag equal to overflow flag. and the zero flag is clear*/ offset = (int8)fetch_byte_imm(m); target = (int16)(m->R_IP) + offset; sf = ACCESS_FLAG(m,F_SF) != 0; of = ACCESS_FLAG(m,F_OF) != 0; /* if (((m->R_FLG & F_SF)>>7) == ((m->R_FLG & F_OF) >> 11) && (!(m->R_FLG & F_ZF))) */ if ( ( sf == of ) && !ACCESS_FLAG(m,F_ZF)) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } static uint8 (*opc80_byte_operation[])(PC_ENV *m,uint8 d,uint8 s) = { add_byte,/*00*/ or_byte, /*01*/ adc_byte,/*02*/ sbb_byte,/*03*/ and_byte,/*04*/ sub_byte,/*05*/ xor_byte,/*06*/ cmp_byte,/*07*/ }; /*opcode=0x80*/ static void i86op_opc80_byte_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 imm; uint8 destval; /* weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc80_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc80_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc80_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); imm = fetch_byte_imm(m); destval = (*opc80_byte_operation[rh])(m, *destreg, imm); if (rh != 7) *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } static uint16 (*opc81_word_operation[])(PC_ENV *m,uint16 d,uint16 s) = { add_word,/*00*/ or_word, /*01*/ adc_word,/*02*/ sbb_word,/*03*/ and_word,/*04*/ sub_word,/*05*/ xor_word,/*06*/ cmp_word,/*07*/ }; /*opcode=0x81*/ static void i86op_opc81_word_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 imm; uint16 destval; /* weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); imm = fetch_word_imm(m); destval = (*opc81_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); imm = fetch_word_imm(m); destval = (*opc81_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); imm = fetch_word_imm(m); destval = (*opc81_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); imm = fetch_word_imm(m); destval = (*opc81_word_operation[rh])(m, *destreg, imm); if (rh != 7) *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } static uint8 (*opc82_byte_operation[])(PC_ENV *m,uint8 s,uint8 d) = { add_byte,/*00*/ or_byte, /*01*/ /*YYY UNUSED ????*/ adc_byte,/*02*/ sbb_byte,/*03*/ and_byte,/*04*/ /*YYY UNUSED ????*/ sub_byte,/*05*/ xor_byte,/*06*/ /*YYY UNUSED ????*/ cmp_byte,/*07*/ }; /*opcode=0x82*/ static void i86op_opc82_byte_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 imm; uint8 destval; /* weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction Similar to opcode 81, except that the immediate byte is sign extended to a word length. */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc82_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc82_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); imm = fetch_byte_imm(m); destval = (*opc82_byte_operation[rh])(m, destval, imm); if (rh != 7) store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); imm = fetch_byte_imm(m); destval = (*opc82_byte_operation[rh])(m, *destreg, imm); if (rh != 7) *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } static uint16 (*opc83_word_operation[])(PC_ENV *m,uint16 s,uint16 d) = { add_word,/*00*/ or_word, /*01*/ /*YYY UNUSED ????*/ adc_word,/*02*/ sbb_word,/*03*/ and_word,/*04*/ /*YYY UNUSED ????*/ sub_word,/*05*/ xor_word,/*06*/ /*YYY UNUSED ????*/ cmp_word,/*07*/ }; /*opcode=0x83*/ static void i86op_opc83_word_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 imm; uint16 destval; /* weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction Similar to opcode 81, except that the immediate byte is sign extended to a word length. */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); imm = (int8)fetch_byte_imm(m); destval = (*opc83_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); imm = (int8)fetch_byte_imm(m); destval = (*opc83_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); imm = (int8) fetch_byte_imm(m); destval = (*opc83_word_operation[rh])(m, destval, imm); if (rh != 7) store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); imm = (int8) fetch_byte_imm(m); destval = (*opc83_word_operation[rh])(m, *destreg, imm); if (rh != 7) *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x84*/ static void i86op_test_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); test_byte(m, destval, *srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); test_byte(m, destval, *srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); test_byte(m, destval, *srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); test_byte(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x85*/ static void i86op_test_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); test_word(m, destval, *srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); test_word(m, destval, *srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); test_word(m, destval, *srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); test_word(m, *destreg, *srcreg); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x86*/ static void i86op_xchg_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; uint8 destval; uint8 tmp; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); tmp = *srcreg; *srcreg = *destreg; *destreg = tmp; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x87*/ static void i86op_xchg_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; uint16 tmp; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); srcreg = DECODE_RM_WORD_REGISTER(m,rh); tmp = *srcreg; *srcreg = destval; destval = tmp; store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); tmp = *srcreg; *srcreg = *destreg; *destreg = tmp; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x88*/ static void i86op_mov_byte_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 destoffset; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); store_data_byte(m,destoffset,*srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); store_data_byte(m,destoffset,*srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); store_data_byte(m,destoffset,*srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); srcreg = DECODE_RM_BYTE_REGISTER(m,rh); *destreg = *srcreg; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x89*/ static void i86op_mov_word_RM_R(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); store_data_word(m,destoffset,*srcreg); break; case 1: destoffset=decode_rm01_address(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); store_data_word(m,destoffset,*srcreg); break; case 2: destoffset=decode_rm10_address(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); store_data_word(m,destoffset,*srcreg); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = DECODE_RM_WORD_REGISTER(m,rh); *destreg = *srcreg; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8a*/ static void i86op_mov_byte_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg,*srcreg; uint16 srcoffset; uint8 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = srcval; break; case 1: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = srcval; break; case 2: destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_byte(m,srcoffset); *destreg = srcval; break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rh); srcreg = DECODE_RM_BYTE_REGISTER(m,rl); *destreg = *srcreg; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8b*/ static void i86op_mov_word_R_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 1: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 2: destreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = *srcreg; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8c*/ static void i86op_mov_word_RM_SR(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); srcreg = decode_rm_seg_register(m,rh); destval = *srcreg; store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); srcreg = decode_rm_seg_register(m,rh); destval = *srcreg; store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); srcreg = decode_rm_seg_register(m,rh); destval = *srcreg; store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); srcreg = decode_rm_seg_register(m,rh); *destreg = *srcreg; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8d*/ static void i86op_lea_word_R_M(PC_ENV *m) { uint16 mod,rl,rh; uint16 *srcreg; uint16 destoffset; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: srcreg = DECODE_RM_WORD_REGISTER(m,rh); destoffset=decode_rm00_address(m,rl); *srcreg = destoffset; break; case 1: srcreg = DECODE_RM_WORD_REGISTER(m,rh); destoffset=decode_rm01_address(m,rl); *srcreg = destoffset; break; case 2: srcreg = DECODE_RM_WORD_REGISTER(m,rh); destoffset=decode_rm10_address(m,rl); *srcreg = destoffset; break; case 3: /* register to register */ /* undefined. Do nothing. */ break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8e*/ static void i86op_mov_word_SR_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg,*srcreg; uint16 srcoffset; uint16 srcval; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destreg = decode_rm_seg_register(m,rh); srcoffset=decode_rm00_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 1: destreg = decode_rm_seg_register(m,rh); srcoffset=decode_rm01_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 2: destreg = decode_rm_seg_register(m,rh); srcoffset = decode_rm10_address(m,rl); srcval = fetch_data_word(m,srcoffset); *destreg = srcval; break; case 3: /* register to register */ destreg = decode_rm_seg_register(m,rh); srcreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = *srcreg; break; } /*\ * clean up, and reset all the R_xSP pointers to the correct * locations. This is about 3x too much overhead (doing all the * segreg ptrs when only one is needed, but this instruction * *cannot* be that common, and this isn't too much work anyway. \*/ DECODE_CLEAR_SEGOVR(m); } /*opcode=0x8f*/ static void i86op_pop_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 destval; FETCH_DECODE_MODRM(m,mod,rh,rl); if (rh != 0) { halt_sys(m); } switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = pop_word( m); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = pop_word(m); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = pop_word(m); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = pop_word(m); break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x90*/ static void i86op_nop(PC_ENV *m) { DECODE_CLEAR_SEGOVR(m); } /*opcode=0x91*/ static void i86op_xchg_word_AX_CX(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_CX; m->R_CX = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x92*/ static void i86op_xchg_word_AX_DX(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_DX; m->R_DX = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x93*/ static void i86op_xchg_word_AX_BX(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_BX; m->R_BX = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x94*/ static void i86op_xchg_word_AX_SP(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_SP; m->R_SP = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x95*/ static void i86op_xchg_word_AX_BP(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_BP; m->R_BP = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x96*/ static void i86op_xchg_word_AX_SI(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_SI; m->R_SI = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x97*/ static void i86op_xchg_word_AX_DI(PC_ENV *m) { uint16 tmp; tmp = m->R_AX; m->R_AX = m->R_DI; m->R_DI = tmp; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x98*/ static void i86op_cbw(PC_ENV *m) { if (m->R_AL & 0x80) { m->R_AH = 0xff; } else { m->R_AH = 0x0; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x99*/ static void i86op_cwd(PC_ENV *m) { if (m->R_AX & 0x8000) { m->R_DX = 0xffff; } else { m->R_DX = 0x0; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9a*/ static void i86op_call_far_IMM(PC_ENV *m) { uint16 farseg,faroff; faroff = fetch_word_imm(m); farseg = fetch_word_imm(m); /* XXX HOOKED INTERRUPT VECTORS CALLING INTO OUR "BIOS" WILL CAUSE PROBLEMS UNLESS ALL INTERSEGMENT STUFF IS CHECKED FOR BIOS ACCESS. CHECK NEEDED HERE. FOR MOMENT, LET IT ALONE. */ push_word(m,m->R_CS); m->R_CS = farseg; push_word(m,m->R_IP); m->R_IP = faroff; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9b*/ static void i86op_wait(PC_ENV *m) { /* NADA. */ DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9c*/ static void i86op_pushf_word(PC_ENV *m) { uint16 flags; flags = m->R_FLG; /* clear out *all* bits not representing flags */ flags &= F_MSK; /* TURN ON CHARACTERISTIC BITS OF FLAG FOR 8088 */ flags |= F_ALWAYS_ON; push_word(m,flags); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9d*/ static void i86op_popf_word(PC_ENV *m) { m->R_FLG = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9e*/ static void i86op_sahf(PC_ENV *m) { /* clear the lower bits of the flag register */ m->R_FLG &= 0xffffff00; /* or in the AH register into the flags register */ m->R_FLG |= m->R_AH; DECODE_CLEAR_SEGOVR(m); } /*opcode=0x9f*/ static void i86op_lahf(PC_ENV *m) { m->R_AH = m->R_FLG & 0xff; /*undocumented TC++ behavior??? Nope. It's documented, but you have too look real hard to notice it. */ m->R_AH |= 0x2; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa0*/ static void i86op_mov_AL_M_IMM(PC_ENV *m) { uint16 offset; uint8 destval; offset = fetch_word_imm(m); destval = fetch_data_byte(m,offset); m->R_AL = destval; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa1*/ static void i86op_mov_AX_M_IMM(PC_ENV *m) { uint16 offset; uint16 destval; offset = fetch_word_imm(m); destval = fetch_data_word(m,offset); m->R_AX = destval; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa2*/ static void i86op_mov_M_AL_IMM(PC_ENV *m) { uint16 offset; offset = fetch_word_imm(m); store_data_byte(m,offset,m->R_AL); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa3*/ static void i86op_mov_M_AX_IMM(PC_ENV *m) { uint16 offset; offset = fetch_word_imm(m); store_data_word(m,offset,m->R_AX); DECODE_CLEAR_SEGOVR(m); } /* JHH CLEANED */ /*opcode=0xa4*/ static void i86op_movs_byte(PC_ENV *m) { uint8 val; int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -1; else inc = 1; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val = fetch_data_byte(m,m->R_SI); store_data_byte_abs(m,m->R_ES,m->R_DI,val); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { val = fetch_data_byte(m,m->R_SI); store_data_byte_abs(m,m->R_ES,m->R_DI,val); m->R_SI += inc; m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa5*/ static void i86op_movs_word(PC_ENV *m) { int16 val; int inc; if (ACCESS_FLAG(m, F_DF)) /* down */ inc = -2; else inc = 2; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val = fetch_data_word(m,m->R_SI); store_data_word_abs(m,m->R_ES,m->R_DI,val); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { val = fetch_data_word(m,m->R_SI); store_data_word_abs(m,m->R_ES,m->R_DI,val); m->R_SI += inc; m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa6*/ static void i86op_cmps_byte(PC_ENV *m) { int8 val1,val2; int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -1; else inc = 1; if (m->sysmode & SYSMODE_PREFIX_REPE) { /* REPE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val1 = fetch_data_byte(m,m->R_SI); val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, val1,val2); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)==0) break; } m->sysmode &= ~SYSMODE_PREFIX_REPE; } else if (m->sysmode & SYSMODE_PREFIX_REPNE) { /* REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val1 = fetch_data_byte(m,m->R_SI); val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, val1,val2); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ } m->sysmode &= ~SYSMODE_PREFIX_REPNE; } else { val1 = fetch_data_byte(m,m->R_SI); val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, val1,val2); m->R_SI += inc; m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa7*/ static void i86op_cmps_word(PC_ENV *m) { int16 val1,val2; int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -2; else inc = 2; if (m->sysmode & SYSMODE_PREFIX_REPE) { /* REPE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val1 = fetch_data_word(m,m->R_SI); val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m, val1,val2); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)==0) break; } m->sysmode &= ~SYSMODE_PREFIX_REPE; } else if (m->sysmode & SYSMODE_PREFIX_REPNE) { /* REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val1 = fetch_data_word(m,m->R_SI); val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m, val1,val2); m->R_CX -= 1; m->R_SI += inc; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ } m->sysmode &= ~SYSMODE_PREFIX_REPNE; } else { val1 = fetch_data_word(m,m->R_SI); val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m, val1,val2); m->R_SI += inc; m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa8*/ static void i86op_test_AL_IMM(PC_ENV *m) { int imm; imm = fetch_byte_imm(m); test_byte(m, m->R_AL, imm); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xa9*/ static void i86op_test_AX_IMM(PC_ENV *m) { int imm; imm = fetch_word_imm(m); test_word(m, m->R_AX, imm); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xaa*/ static void i86op_stos_byte(PC_ENV *m) { int inc; if (ACCESS_FLAG(m, F_DF)) /* down */ inc = -1; else inc = 1; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { store_data_byte_abs(m,m->R_ES,m->R_DI,m->R_AL); m->R_CX -= 1; m->R_DI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { store_data_byte_abs(m,m->R_ES,m->R_DI,m->R_AL); m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xab*/ static void i86op_stos_word(PC_ENV *m) { int inc; if (ACCESS_FLAG(m, F_DF)) /* down */ inc = -2; else inc = 2; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { store_data_word_abs(m,m->R_ES,m->R_DI,m->R_AX); m->R_CX -= 1; m->R_DI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { store_data_word_abs(m,m->R_ES,m->R_DI,m->R_AX); m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xac*/ static void i86op_lods_byte(PC_ENV *m) { int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -1; else inc = 1; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { m->R_AL = fetch_data_byte(m,m->R_SI); m->R_CX -= 1; m->R_SI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { m->R_AL = fetch_data_byte(m,m->R_SI); m->R_SI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xad*/ static void i86op_lods_word(PC_ENV *m) { int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -2; else inc = 2; if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { /* dont care whether REPE or REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { m->R_AX = fetch_data_word(m,m->R_SI); m->R_CX -= 1; m->R_SI += inc; } m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); } else { m->R_AX = fetch_data_word(m,m->R_SI); m->R_SI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xae*/ static void i86op_scas_byte(PC_ENV *m) { int8 val2; int inc; if (ACCESS_FLAG(m,F_DF)) /* down */ inc = -1; else inc = 1; if (m->sysmode & SYSMODE_PREFIX_REPE) { /* REPE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, m->R_AL,val2); m->R_CX -= 1; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)==0) break; } m->sysmode &= ~SYSMODE_PREFIX_REPE; } else if (m->sysmode & SYSMODE_PREFIX_REPNE) { /* REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, m->R_AL,val2); m->R_CX -= 1; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ } m->sysmode &= ~SYSMODE_PREFIX_REPNE; } else { val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); cmp_byte(m, m->R_AL,val2); m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xaf*/ static void i86op_scas_word(PC_ENV *m) { int16 val2; int inc; if (ACCESS_FLAG(m, F_DF)) /* down */ inc = -2; else inc = 2; if (m->sysmode & SYSMODE_PREFIX_REPE) { /* REPE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m,m->R_AX,val2); m->R_CX -= 1; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)==0) break; } m->sysmode &= ~SYSMODE_PREFIX_REPE; } else if (m->sysmode & SYSMODE_PREFIX_REPNE) { /* REPNE */ /* move them until CX is ZERO. */ while (m->R_CX != 0) { val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m, m->R_AX,val2); m->R_CX -= 1; m->R_DI += inc; if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ } m->sysmode &= ~SYSMODE_PREFIX_REPNE; } else { val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); cmp_word(m, m->R_AX,val2); m->R_DI += inc; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb0*/ static void i86op_mov_byte_AL_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_AL = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb1*/ static void i86op_mov_byte_CL_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_CL = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb2*/ static void i86op_mov_byte_DL_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_DL = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb3*/ static void i86op_mov_byte_BL_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_BL = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb4*/ static void i86op_mov_byte_AH_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_AH = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb5*/ static void i86op_mov_byte_CH_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_CH = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb6*/ static void i86op_mov_byte_DH_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_DH = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb7*/ static void i86op_mov_byte_BH_IMM(PC_ENV *m) { uint8 imm; imm = fetch_byte_imm(m); m->R_BH = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb8*/ static void i86op_mov_word_AX_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_AX = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xb9*/ static void i86op_mov_word_CX_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_CX = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xba*/ static void i86op_mov_word_DX_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_DX = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xbb*/ static void i86op_mov_word_BX_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_BX = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xbc*/ static void i86op_mov_word_SP_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_SP = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xbd*/ static void i86op_mov_word_BP_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_BP = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xbe*/ static void i86op_mov_word_SI_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_SI = imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xbf*/ static void i86op_mov_word_DI_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_DI = imm; DECODE_CLEAR_SEGOVR(m); } /* c0 === ILLEGAL OPERAND */ /* c1 === ILLEGAL OPERAND */ /*opcode=0xc2*/ static void i86op_ret_near_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_IP = pop_word(m); m->R_SP += imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc3*/ static void i86op_ret_near(PC_ENV *m) { m->R_IP = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc4*/ static void i86op_les_R_IMM(PC_ENV *m) { uint16 mod,rh,rl; uint16 *dstreg; uint16 srcoffset; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_ES = fetch_data_word(m,srcoffset+2); break; case 1: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_ES = fetch_data_word(m,srcoffset+2); break; case 2: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_ES = fetch_data_word(m,srcoffset+2); break; case 3: /* register to register */ /* UNDEFINED! */ ; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc5*/ static void i86op_lds_R_IMM(PC_ENV *m) { uint16 mod,rh,rl; uint16 *dstreg; uint16 srcoffset; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm00_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_DS = fetch_data_word(m,srcoffset+2); break; case 1: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm01_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_DS = fetch_data_word(m,srcoffset+2); break; case 2: dstreg = DECODE_RM_WORD_REGISTER(m,rh); srcoffset=decode_rm10_address(m,rl); *dstreg = fetch_data_word(m,srcoffset); m->R_DS = fetch_data_word(m,srcoffset+2); break; case 3: /* register to register */ /* UNDEFINED! */ ; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc6*/ static void i86op_mov_byte_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 imm; FETCH_DECODE_MODRM(m,mod,rh,rl); if (rh != 0) { halt_sys(m); } switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); imm = fetch_byte_imm(m); store_data_byte(m,destoffset,imm); break; case 1: destoffset=decode_rm01_address(m,rl); imm = fetch_byte_imm(m); store_data_byte(m,destoffset,imm); break; case 2: destoffset=decode_rm10_address(m,rl); imm = fetch_byte_imm(m); store_data_byte(m,destoffset,imm); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); imm = fetch_byte_imm(m); *destreg = imm; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc7*/ static void i86op_mov_word_RM_IMM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 imm; FETCH_DECODE_MODRM(m,mod,rh,rl); if (rh != 0) { halt_sys(m); } switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); imm = fetch_word_imm(m); store_data_word(m,destoffset,imm); break; case 1: destoffset=decode_rm01_address(m,rl); imm = fetch_word_imm(m); store_data_word(m,destoffset,imm); break; case 2: destoffset=decode_rm10_address(m,rl); imm = fetch_word_imm(m); store_data_word(m,destoffset,imm); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); imm = fetch_word_imm(m); *destreg = imm; break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xc8 ILLEGAL OP*/ /*opcode=0xc9 ILLEGAL OP*/ /*opcode=0xca*/ static void i86op_ret_far_IMM(PC_ENV *m) { uint16 imm; imm = fetch_word_imm(m); m->R_IP = pop_word(m); m->R_CS = pop_word(m); m->R_SP += imm; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xcb*/ static void i86op_ret_far(PC_ENV *m) { m->R_IP = pop_word(m); m->R_CS = pop_word(m); DECODE_CLEAR_SEGOVR(m); } /* opcode=0xcc*/ static void i86op_int3(PC_ENV *m) { uint16 tmp; tmp = (uint16) mem_access_word(m, 3 * 4); /* access the segment register */ { tmp = m->R_FLG; push_word(m, tmp); CLEAR_FLAG(m, F_IF); CLEAR_FLAG(m, F_TF); /* [JCE] If we're interrupting between a segment override (or REP override) * and the following instruction, decrease IP to get back to the prefix */ if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { --m->R_IP; } push_word(m, m->R_CS); push_word(m, m->R_IP); /* [JCE] CS and IP were the wrong way round... */ tmp = mem_access_word(m, 3 * 4); m->R_IP = tmp; tmp = mem_access_word(m, 3 * 4 + 2); m->R_CS = tmp; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xcd*/ static void i86op_int_IMM(PC_ENV *m) { uint16 tmp; uint8 intnum; intnum = fetch_byte_imm(m); tmp = mem_access_word(m, intnum * 4); { tmp = m->R_FLG; push_word(m, tmp); CLEAR_FLAG(m, F_IF); CLEAR_FLAG(m, F_TF); /* [JCE] If we're interrupting between a segment override (or REP override) * and the following instruction, decrease IP to get back to the prefix */ if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { --m->R_IP; } push_word(m, m->R_CS); push_word(m, m->R_IP); /* [JCE] CS and IP were the wrong way round... */ tmp = mem_access_word(m, intnum * 4); m->R_IP = tmp; tmp = mem_access_word(m, intnum * 4 + 2); m->R_CS = tmp; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xce*/ static void i86op_into(PC_ENV *m) { uint16 tmp; if (ACCESS_FLAG(m,F_OF)) { tmp = mem_access_word(m, 4 * 4); { tmp = m->R_FLG; push_word(m, tmp); CLEAR_FLAG(m, F_IF); CLEAR_FLAG(m, F_TF); /* [JCE] If we're interrupting between a segment override (or REP override) * and the following instruction, decrease IP to get back to the prefix */ if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { --m->R_IP; } push_word(m, m->R_CS); push_word(m, m->R_IP); /* [JCE] CS and IP were the wrong way round... */ tmp = mem_access_word(m, 4 * 4); m->R_IP = tmp; tmp = mem_access_word(m, 4 * 4 + 2); m->R_CS = tmp; } } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xcf*/ static void i86op_iret(PC_ENV *m) { m->R_IP = pop_word(m); m->R_CS = pop_word(m); m->R_FLG = pop_word(m); DECODE_CLEAR_SEGOVR(m); } static uint8 (*opcD0_byte_operation[])(PC_ENV *m,uint8 d, uint8 s) = /* used by opcodes d0 and d2. */ { rol_byte, ror_byte, rcl_byte, rcr_byte, shl_byte, shr_byte, shl_byte, /* sal_byte === shl_byte by definition */ sar_byte, }; /* opcode=0xd0*/ static void i86op_opcD0_byte_RM_1(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 destval; /* Yet another weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval,1); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval, 1); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval, 1); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); destval = (*opcD0_byte_operation[rh])(m, *destreg, 1); *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } static uint16 (*opcD1_word_operation[])(PC_ENV *m,uint16 s,uint16 d) = /* used by opcodes d1 and d3. */ { rol_word, ror_word, rcl_word, rcr_word, shl_word, shr_word, shl_word, /* sal_byte === shl_byte by definition */ sar_word, }; /* opcode=0xd1*/ static void i86op_opcD1_word_RM_1(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 destval; /* Yet another weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval,1); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval, 1); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval, 1); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); destval = (*opcD1_word_operation[rh])(m, *destreg, 1); *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd2*/ static void i86op_opcD2_byte_RM_CL(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 destval; uint8 amt; /* Yet another weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); amt = m->R_CL; /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval,amt); store_data_byte(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval, amt); store_data_byte(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = (*opcD0_byte_operation[rh])(m, destval, amt); store_data_byte(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_BYTE_REGISTER(m,rl); destval = (*opcD0_byte_operation[rh])(m, *destreg, amt); *destreg = destval; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd3*/ static void i86op_opcD3_word_RM_CL(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 destval; uint8 amt; /* Yet another weirdo special case instruction format. Part of the opcode held below in "RH". Doubly nested case would result, except that the decoded instruction */ FETCH_DECODE_MODRM(m,mod,rh,rl); amt = m->R_CL; /* know operation, decode the mod byte to find the addressing mode. */ switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval, amt); store_data_word(m,destoffset,destval); break; case 1: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval, amt); store_data_word(m,destoffset,destval); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); destval = (*opcD1_word_operation[rh])(m, destval, amt); store_data_word(m,destoffset,destval); break; case 3: /* register to register */ destreg = DECODE_RM_WORD_REGISTER(m,rl); *destreg = (*opcD1_word_operation[rh])(m, *destreg, amt); break; } DECODE_CLEAR_SEGOVR(m); } static void sys_fatal(int error, char *fmt, ...) { va_list p; va_start(p, fmt); fprintf(stderr, "Fatal error: "); if (error != 0) { fprintf(stderr, "<%d>",error); fprintf(stderr,"%s",strerror(error)); } vfprintf(stderr, fmt, p); va_end(p); fprintf(stderr, NLP "Exiting..." NLP); exit(1); } /* opcode=0xd4*/ static void i86op_aam(PC_ENV *m) { uint8 a; a = fetch_byte_imm(m); /* this is a stupid encoding. */ if (a != 10) sys_fatal(0,"error decoding aam" NLP); /* note the type change here --- returning AL and AH in AX. */ m->R_AX = aam_word(m,m->R_AL); DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd5*/ static void i86op_aad(PC_ENV *m) { uint8 a; a = fetch_byte_imm(m); m->R_AX = aad_word(m,m->R_AX); DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd6 ILLEGAL OPCODE */ /* opcode=0xd7 */ static void i86op_xlat(PC_ENV *m) { uint16 addr; addr = m->R_BX + (uint8)m->R_AL; m->R_AL = fetch_data_byte(m,addr); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe0*/ static void i86op_loopne(PC_ENV *m) { int16 ip; ip = (int8)fetch_byte_imm(m); ip += (int16)m->R_IP; m->R_CX -= 1; if (m->R_CX != 0 && !ACCESS_FLAG(m,F_ZF)) /* CX != 0 and !ZF */ m->R_IP = ip; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe1*/ static void i86op_loope(PC_ENV *m) { int16 ip; ip = (int8)fetch_byte_imm(m); ip += (int16)m->R_IP; m->R_CX -= 1; if (m->R_CX != 0 && ACCESS_FLAG(m,F_ZF)) /* CX != 0 and ZF */ m->R_IP = ip; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe2*/ static void i86op_loop(PC_ENV *m) { int16 ip; ip = (int8)fetch_byte_imm(m); ip += (int16)m->R_IP; m->R_CX -= 1; if (m->R_CX != 0) m->R_IP = ip; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe3*/ static void i86op_jcxz(PC_ENV *m) { int16 offset,target; /* jump to byte offset if overflow flag is set */ offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ target = (int16)m->R_IP + offset; if (m->R_CX == 0) m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe4*/ static void i86op_in_byte_AL_IMM(PC_ENV *m) { uint8 port; port = (uint8)fetch_byte_imm(m); m->R_AL = in(port); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe5*/ static void i86op_in_word_AX_IMM(PC_ENV *m) { uint8 port; port = (uint8)fetch_byte_imm(m); m->R_AX = in(port); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe6*/ static void i86op_out_byte_IMM_AL(PC_ENV *m) { uint8 port; port = (uint8)fetch_byte_imm(m); out(port, m->R_AL); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe7*/ static void i86op_out_word_IMM_AX(PC_ENV *m) { uint8 port; port = (uint8)fetch_byte_imm(m); out(port, m->R_AX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe8*/ static void i86op_call_near_IMM(PC_ENV *m) { int16 ip; /* weird. Thought this was a signed disp! */ ip = (int16)fetch_word_imm(m); ip += (int16)m->R_IP; /* CHECK SIGN */ push_word(m,m->R_IP); m->R_IP = ip; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xe9*/ static void i86op_jump_near_IMM(PC_ENV *m) { int ip; /* weird. Thought this was a signed disp too! */ ip = (int16)fetch_word_imm(m); ip += (int16)m->R_IP; /* CHECK SIGN */ m->R_IP = ip; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xea*/ static void i86op_jump_far_IMM(PC_ENV *m) { uint16 cs,ip; ip = fetch_word_imm(m); cs = fetch_word_imm(m); m->R_IP = ip; m->R_CS = cs; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xeb*/ static void i86op_jump_byte_IMM(PC_ENV *m) { int8 offset; uint16 target; offset = (int8) fetch_byte_imm(m); /* CHECK */ /* printf("jump byte imm offset=%d\n",offset);*/ target = (int16) m->R_IP + offset; m->R_IP = target; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xec*/ static void i86op_in_byte_AL_DX(PC_ENV *m) { m->R_AL = in(m->R_DX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xed*/ static void i86op_in_word_AX_DX(PC_ENV *m) { m->R_AX = in(m->R_DX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xee*/ static void i86op_out_byte_DX_AL(PC_ENV *m) { out(m->R_DX, m->R_AL); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xef*/ static void i86op_out_word_DX_AX(PC_ENV *m) { out(m->R_DX, m->R_AX); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf0*/ static void i86op_lock(PC_ENV *m) { DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf1 ILLEGAL OPERATION*/ /*opcode=0xf2*/ static void i86op_repne(PC_ENV *m) { m->sysmode |= SYSMODE_PREFIX_REPNE; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf3*/ static void i86op_repe(PC_ENV *m) { m->sysmode |= SYSMODE_PREFIX_REPE; DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf4*/ static void i86op_halt(PC_ENV *m) { halt_sys(m); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf5*/ static void i86op_cmc(PC_ENV *m) { /* complement the carry flag. */ TOGGLE_FLAG(m,F_CF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf6*/ static void i86op_opcF6_byte_RM(PC_ENV *m) { uint16 mod,rl,rh; uint8 *destreg; uint16 destoffset; uint8 destval,srcval; /* long, drawn out code follows. Double switch for a total of 32 cases. */ FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: /* mod=00 */ switch(rh) { case 0: /* test byte imm */ destoffset=decode_rm00_address(m,rl); srcval = fetch_byte_imm(m); destval = fetch_data_byte(m,destoffset); test_byte(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = not_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 3: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = neg_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 4: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); mul_byte(m, destval); break; case 5: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); imul_byte(m, destval); break; case 6: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); div_byte(m, destval); break; case 7: destoffset=decode_rm00_address(m,rl); destval = fetch_data_byte(m,destoffset); idiv_byte(m, destval); break; } break; /* end mod==00 */ case 1: /* mod=01 */ switch(rh) { case 0: /* test byte imm */ destoffset=decode_rm01_address(m,rl); srcval = fetch_byte_imm(m); destval = fetch_data_byte(m,destoffset); test_byte(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = not_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 3: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = neg_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 4: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); mul_byte(m, destval); break; case 5: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); imul_byte(m, destval); break; case 6: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); div_byte(m, destval); break; case 7: destoffset=decode_rm01_address(m,rl); destval = fetch_data_byte(m,destoffset); idiv_byte(m, destval); break; } break; /* end mod==01 */ case 2: /* mod=10 */ switch(rh) { case 0: /* test byte imm */ destoffset=decode_rm10_address(m,rl); srcval = fetch_byte_imm(m); destval = fetch_data_byte(m,destoffset); test_byte(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = not_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 3: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); destval = neg_byte(m, destval); store_data_byte(m,destoffset,destval); break; case 4: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); mul_byte(m, destval); break; case 5: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); imul_byte(m, destval); break; case 6: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); div_byte(m, destval); break; case 7: destoffset=decode_rm10_address(m,rl); destval = fetch_data_byte(m,destoffset); idiv_byte(m, destval); break; } break; /* end mod==10 */ case 3: /* mod=11 */ switch(rh) { case 0: /* test byte imm */ destreg=DECODE_RM_BYTE_REGISTER(m,rl); srcval = fetch_byte_imm(m); test_byte(m, *destreg, srcval); break; case 1: halt_sys(m); break; case 2: destreg=DECODE_RM_BYTE_REGISTER(m,rl); *destreg = not_byte(m, *destreg); break; case 3: destreg=DECODE_RM_BYTE_REGISTER(m,rl); *destreg = neg_byte(m, *destreg); break; case 4: destreg=DECODE_RM_BYTE_REGISTER(m,rl); mul_byte(m, *destreg); /*!!! */ break; case 5: destreg=DECODE_RM_BYTE_REGISTER(m,rl); imul_byte(m, *destreg); break; case 6: destreg=DECODE_RM_BYTE_REGISTER(m,rl); div_byte(m, *destreg); break; case 7: destreg=DECODE_RM_BYTE_REGISTER(m,rl); idiv_byte(m, *destreg); break; } break; /* end mod==11 */ } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf7*/ static void i86op_opcF7_word_RM(PC_ENV *m) { uint16 mod,rl,rh; uint16 *destreg; uint16 destoffset; uint16 destval,srcval; /* long, drawn out code follows. Double switch for a total of 32 cases. */ FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: /* mod=00 */ switch(rh) { case 0: /* test word imm */ destoffset=decode_rm00_address(m,rl); srcval = fetch_word_imm(m); destval = fetch_data_word(m,destoffset); test_word(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); destval = not_word(m, destval); store_data_word(m,destoffset,destval); break; case 3: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); destval = neg_word(m, destval); store_data_word(m,destoffset,destval); break; case 4: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); mul_word(m, destval); break; case 5: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); imul_word(m, destval); break; case 6: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); div_word(m, destval); break; case 7: destoffset=decode_rm00_address(m,rl); destval = fetch_data_word(m,destoffset); idiv_word(m, destval); break; } break; /* end mod==00 */ case 1: /* mod=01 */ switch(rh) { case 0: /* test word imm */ destoffset=decode_rm01_address(m,rl); srcval = fetch_word_imm(m); destval = fetch_data_word(m,destoffset); test_word(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); destval = not_word(m, destval); store_data_word(m,destoffset,destval); break; case 3: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); destval = neg_word(m, destval); store_data_word(m,destoffset,destval); break; case 4: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); mul_word(m, destval); break; case 5: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); imul_word(m, destval); break; case 6: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); div_word(m, destval); break; case 7: destoffset=decode_rm01_address(m,rl); destval = fetch_data_word(m,destoffset); idiv_word(m, destval); break; } break; /* end mod==01 */ case 2: /* mod=10 */ switch(rh) { case 0: /* test word imm */ destoffset=decode_rm10_address(m,rl); srcval = fetch_word_imm(m); destval = fetch_data_word(m,destoffset); test_word(m, destval, srcval); break; case 1: halt_sys(m); break; case 2: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); destval = not_word(m, destval); store_data_word(m,destoffset,destval); break; case 3: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); destval = neg_word(m, destval); store_data_word(m,destoffset,destval); break; case 4: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); mul_word(m, destval); break; case 5: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); imul_word(m, destval); break; case 6: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); div_word(m, destval); break; case 7: destoffset=decode_rm10_address(m,rl); destval = fetch_data_word(m,destoffset); idiv_word(m, destval); break; } break; /* end mod==10 */ case 3: /* mod=11 */ switch(rh) { case 0: /* test word imm */ destreg=DECODE_RM_WORD_REGISTER(m,rl); srcval = fetch_word_imm(m); test_word(m, *destreg, srcval); break; case 1: halt_sys(m); break; case 2: destreg=DECODE_RM_WORD_REGISTER(m,rl); *destreg = not_word(m, *destreg); break; case 3: destreg=DECODE_RM_WORD_REGISTER(m,rl); *destreg = neg_word(m, *destreg); break; case 4: destreg=DECODE_RM_WORD_REGISTER(m,rl); mul_word(m, *destreg); /*!!! */ break; case 5: destreg=DECODE_RM_WORD_REGISTER(m,rl); imul_word(m, *destreg); break; case 6: destreg=DECODE_RM_WORD_REGISTER(m,rl); div_word(m, *destreg); break; case 7: destreg=DECODE_RM_WORD_REGISTER(m,rl); idiv_word(m, *destreg); break; } break; /* end mod==11 */ } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf8*/ static void i86op_clc(PC_ENV *m) { /* clear the carry flag. */ CLEAR_FLAG(m, F_CF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xf9*/ static void i86op_stc(PC_ENV *m) { /* set the carry flag. */ SET_FLAG(m, F_CF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xfa*/ static void i86op_cli(PC_ENV *m) { /* clear interrupts. */ CLEAR_FLAG(m, F_IF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xfb*/ static void i86op_sti(PC_ENV *m) { /* enable interrupts. */ SET_FLAG(m, F_IF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xfc*/ static void i86op_cld(PC_ENV *m) { /* clear interrupts. */ CLEAR_FLAG(m, F_DF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xfd*/ static void i86op_std(PC_ENV *m) { /* clear interrupts. */ SET_FLAG(m, F_DF); DECODE_CLEAR_SEGOVR(m); } /*opcode=0xfe*/ static void i86op_opcFE_byte_RM(PC_ENV *m) { /* Yet another damned special case instruction. */ uint16 mod,rh,rl; uint8 destval; uint16 destoffset; uint8 *destreg; /* ARRGH, ANOTHER GODDAMN SPECIAL CASE INSTRUCTION!!! */ FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); switch (rh) { case 0: /* inc word ptr ... */ destval = fetch_data_byte(m,destoffset); destval = inc_byte(m,destval); store_data_byte(m,destoffset,destval); break; case 1: /* dec word ptr ... */ destval = fetch_data_byte(m,destoffset); destval = dec_byte(m,destval); store_data_byte(m,destoffset,destval); break; } break; case 1: destoffset=decode_rm01_address(m,rl); switch (rh) { case 0: destval = fetch_data_byte(m,destoffset); destval = inc_byte(m,destval); store_data_byte(m,destoffset,destval); break; case 1: destval = fetch_data_byte(m,destoffset); destval = dec_byte(m,destval); store_data_byte(m,destoffset,destval); break; } break; case 2: destoffset=decode_rm10_address(m,rl); switch (rh) { case 0: destval = fetch_data_byte(m,destoffset); destval = inc_byte(m,destval); store_data_byte(m,destoffset,destval); break; case 1: destval = fetch_data_byte(m,destoffset); destval = dec_byte(m,destval); store_data_byte(m,destoffset,destval); break; } break; case 3: destreg = DECODE_RM_BYTE_REGISTER(m,rl); switch (rh) { case 0: *destreg = inc_byte(m,*destreg); break; case 1: *destreg = dec_byte(m,*destreg); break; } break; } DECODE_CLEAR_SEGOVR(m); } /*opcode=0xff*/ static void i86op_opcFF_word_RM(PC_ENV *m) { uint16 mod,rh,rl; uint16 destval,destval2; uint16 destoffset; uint16 *destreg; /* ANOTHER DAMN SPECIAL CASE INSTRUCTION!!! */ FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); switch (rh) { case 0: /* inc word ptr ... */ destval = fetch_data_word(m,destoffset); destval = inc_word(m,destval); store_data_word(m,destoffset,destval); break; case 1: /* dec word ptr ... */ destval = fetch_data_word(m,destoffset); destval = dec_word(m,destval); store_data_word(m,destoffset,destval); break; case 2: /* call word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,m->R_IP); m->R_IP = destval; break; case 3: /* call far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); push_word(m,m->R_CS); m->R_CS = destval2; push_word(m,m->R_IP); m->R_IP = destval; break; case 4: /* jmp word ptr ... */ destval = fetch_data_word(m,destoffset); m->R_IP = destval; break; case 5: /* jmp far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); m->R_IP = destval; m->R_CS = destval2; break; case 6: /* push word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,destval); break; } break; case 1: destoffset=decode_rm01_address(m,rl); switch (rh) { case 0: destval = fetch_data_word(m,destoffset); destval = inc_word(m,destval); store_data_word(m,destoffset,destval); break; case 1: destval = fetch_data_word(m,destoffset); destval = dec_word(m,destval); store_data_word(m,destoffset,destval); break; case 2: /* call word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,m->R_IP); m->R_IP = destval; break; case 3: /* call far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); push_word(m,m->R_CS); m->R_CS = destval2; push_word(m,m->R_IP); m->R_IP = destval; break; case 4: /* jmp word ptr ... */ destval = fetch_data_word(m,destoffset); m->R_IP = destval; break; case 5: /* jmp far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); m->R_IP = destval; m->R_CS = destval2; break; case 6: /* push word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,destval); break; } break; case 2: destoffset=decode_rm10_address(m,rl); switch (rh) { case 0: destval = fetch_data_word(m,destoffset); destval = inc_word(m,destval); store_data_word(m,destoffset,destval); break; case 1: destval = fetch_data_word(m,destoffset); destval = dec_word(m,destval); store_data_word(m,destoffset,destval); break; case 2: /* call word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,m->R_IP); m->R_IP = destval; break; case 3: /* call far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); push_word(m,m->R_CS); m->R_CS = destval2; push_word(m,m->R_IP); m->R_IP = destval; break; case 4: /* jmp word ptr ... */ destval = fetch_data_word(m,destoffset); m->R_IP = destval; break; case 5: /* jmp far ptr ... */ destval = fetch_data_word(m,destoffset); destval2 = fetch_data_word(m,destoffset+2); m->R_IP = destval; m->R_CS = destval2; break; case 6: /* push word ptr ... */ destval = fetch_data_word(m,destoffset); push_word(m,destval); break; } break; case 3: destreg = DECODE_RM_WORD_REGISTER(m,rl); switch (rh) { case 0: *destreg = inc_word(m,*destreg); break; case 1: *destreg = dec_word(m,*destreg); break; case 2: /* call word ptr ... */ push_word(m,m->R_IP); m->R_IP = *destreg; break; case 3: /* jmp far ptr ... */ halt_sys(m); break; case 4: /* jmp ... */ m->R_IP = (uint16)(*destreg); break; case 5: /* jmp far ptr ... */ halt_sys(m); break; case 6: push_word(m,*destreg); break; } break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd8*/ static void i86op_esc_coprocess_d8(PC_ENV *m) { DECODE_CLEAR_SEGOVR(m); } /* opcode=0xd9*/ static void i86op_esc_coprocess_d9(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xda*/ static void i86op_esc_coprocess_da(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xdb*/ static void i86op_esc_coprocess_db(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; /* uint8 stkelem;*/ FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xdc*/ static void i86op_esc_coprocess_dc(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xdd*/ static void i86op_esc_coprocess_dd(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xde*/ static void i86op_esc_coprocess_de(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* opcode=0xdf*/ static void i86op_esc_coprocess_df(PC_ENV *m) { uint16 mod,rl,rh; uint16 destoffset; uint8 stkelem; FETCH_DECODE_MODRM(m,mod,rh,rl); switch (mod) { case 0: destoffset=decode_rm00_address(m,rl); break; case 1: destoffset=decode_rm01_address(m,rl); break; case 2: destoffset=decode_rm10_address(m,rl); break; case 3: /* register to register */ stkelem = (uint8) rl; break; } DECODE_CLEAR_SEGOVR(m); } /* OPERAND TABLE */ OP i86_optab[256] = { /* 0x00 */ i86op_add_byte_RM_R, /* 0x01 */ i86op_add_word_RM_R, /* 0x02 */ i86op_add_byte_R_RM, /* 0x03 */ i86op_add_word_R_RM, /* 0x04 */ i86op_add_byte_AL_IMM, /* 0x05 */ i86op_add_word_AX_IMM, /* 0x06 */ i86op_push_ES, /* 0x07 */ i86op_pop_ES, /* 0x08 */ i86op_or_byte_RM_R, /* 0x09 */ i86op_or_word_RM_R, /* 0x0a */ i86op_or_byte_R_RM, /* 0x0b */ i86op_or_word_R_RM, /* 0x0c */ i86op_or_byte_AL_IMM, /* 0x0d */ i86op_or_word_AX_IMM, /* 0x0e */ i86op_push_CS, /* 0x0f */ i86op_illegal_op, /* 0x10 */ i86op_adc_byte_RM_R, /* 0x11 */ i86op_adc_word_RM_R, /* 0x12 */ i86op_adc_byte_R_RM, /* 0x13 */ i86op_adc_word_R_RM, /* 0x14 */ i86op_adc_byte_AL_IMM, /* 0x15 */ i86op_adc_word_AX_IMM, /* 0x16 */ i86op_push_SS, /* 0x17 */ i86op_pop_SS, /* 0x18 */ i86op_sbb_byte_RM_R, /* 0x19 */ i86op_sbb_word_RM_R, /* 0x1a */ i86op_sbb_byte_R_RM, /* 0x1b */ i86op_sbb_word_R_RM, /* 0x1c */ i86op_sbb_byte_AL_IMM, /* 0x1d */ i86op_sbb_word_AX_IMM, /* 0x1e */ i86op_push_DS, /* 0x1f */ i86op_pop_DS, /* 0x20 */ i86op_and_byte_RM_R, /* 0x21 */ i86op_and_word_RM_R, /* 0x22 */ i86op_and_byte_R_RM, /* 0x23 */ i86op_and_word_R_RM, /* 0x24 */ i86op_and_byte_AL_IMM, /* 0x25 */ i86op_and_word_AX_IMM, /* 0x26 */ i86op_segovr_ES, /* 0x27 */ i86op_daa, /* 0x28 */ i86op_sub_byte_RM_R, /* 0x29 */ i86op_sub_word_RM_R, /* 0x2a */ i86op_sub_byte_R_RM, /* 0x2b */ i86op_sub_word_R_RM, /* 0x2c */ i86op_sub_byte_AL_IMM, /* 0x2d */ i86op_sub_word_AX_IMM, /* 0x2e */ i86op_segovr_CS, /* 0x2f */ i86op_das, /* 0x30 */ i86op_xor_byte_RM_R, /* 0x31 */ i86op_xor_word_RM_R, /* 0x32 */ i86op_xor_byte_R_RM, /* 0x33 */ i86op_xor_word_R_RM, /* 0x34 */ i86op_xor_byte_AL_IMM, /* 0x35 */ i86op_xor_word_AX_IMM, /* 0x36 */ i86op_segovr_SS, /* 0x37 */ i86op_aaa, /* 0x38 */ i86op_cmp_byte_RM_R, /* 0x39 */ i86op_cmp_word_RM_R, /* 0x3a */ i86op_cmp_byte_R_RM, /* 0x3b */ i86op_cmp_word_R_RM, /* 0x3c */ i86op_cmp_byte_AL_IMM, /* 0x3d */ i86op_cmp_word_AX_IMM, /* 0x3e */ i86op_segovr_DS, /* 0x3f */ i86op_aas, /* 0x40 */ i86op_inc_AX, /* 0x41 */ i86op_inc_CX, /* 0x42 */ i86op_inc_DX, /* 0x43 */ i86op_inc_BX, /* 0x44 */ i86op_inc_SP, /* 0x45 */ i86op_inc_BP, /* 0x46 */ i86op_inc_SI, /* 0x47 */ i86op_inc_DI, /* 0x48 */ i86op_dec_AX, /* 0x49 */ i86op_dec_CX, /* 0x4a */ i86op_dec_DX, /* 0x4b */ i86op_dec_BX, /* 0x4c */ i86op_dec_SP, /* 0x4d */ i86op_dec_BP, /* 0x4e */ i86op_dec_SI, /* 0x4f */ i86op_dec_DI, /* 0x50 */ i86op_push_AX, /* 0x51 */ i86op_push_CX, /* 0x52 */ i86op_push_DX, /* 0x53 */ i86op_push_BX, /* 0x54 */ i86op_push_SP, /* 0x55 */ i86op_push_BP, /* 0x56 */ i86op_push_SI, /* 0x57 */ i86op_push_DI, /* 0x58 */ i86op_pop_AX, /* 0x59 */ i86op_pop_CX, /* 0x5a */ i86op_pop_DX, /* 0x5b */ i86op_pop_BX, /* 0x5c */ i86op_pop_SP, /* 0x5d */ i86op_pop_BP, /* 0x5e */ i86op_pop_SI, /* 0x5f */ i86op_pop_DI, /* 0x60 */ i86op_illegal_op, /* 0x61 */ i86op_illegal_op, /* 0x62 */ i86op_illegal_op, /* 0x63 */ i86op_illegal_op, /* 0x64 */ i86op_illegal_op, /* 0x65 */ i86op_illegal_op, /* 0x66 */ i86op_illegal_op, /* 0x67 */ i86op_illegal_op, /* 0x68 */ i86op_illegal_op, /* 0x69 */ i86op_illegal_op, /* 0x6a */ i86op_illegal_op, /* 0x6b */ i86op_illegal_op, /* 0x6c */ i86op_illegal_op, /* 0x6d */ i86op_illegal_op, /* 0x6e */ i86op_illegal_op, /* 0x6f */ i86op_illegal_op, /* 0x70 */ i86op_jump_near_O, /* 0x71 */ i86op_jump_near_NO, /* 0x72 */ i86op_jump_near_B, /* 0x73 */ i86op_jump_near_NB, /* 0x74 */ i86op_jump_near_Z, /* 0x75 */ i86op_jump_near_NZ, /* 0x76 */ i86op_jump_near_BE, /* 0x77 */ i86op_jump_near_NBE, /* 0x78 */ i86op_jump_near_S, /* 0x79 */ i86op_jump_near_NS, /* 0x7a */ i86op_jump_near_P, /* 0x7b */ i86op_jump_near_NP, /* 0x7c */ i86op_jump_near_L, /* 0x7d */ i86op_jump_near_NL, /* 0x7e */ i86op_jump_near_LE, /* 0x7f */ i86op_jump_near_NLE, /* 0x80 */ i86op_opc80_byte_RM_IMM, /* 0x81 */ i86op_opc81_word_RM_IMM, /* 0x82 */ i86op_opc82_byte_RM_IMM, /* 0x83 */ i86op_opc83_word_RM_IMM, /* 0x84 */ i86op_test_byte_RM_R, /* 0x85 */ i86op_test_word_RM_R, /* 0x86 */ i86op_xchg_byte_RM_R, /* 0x87 */ i86op_xchg_word_RM_R, /* 0x88 */ i86op_mov_byte_RM_R, /* 0x89 */ i86op_mov_word_RM_R, /* 0x8a */ i86op_mov_byte_R_RM, /* 0x8b */ i86op_mov_word_R_RM, /* 0x8c */ i86op_mov_word_RM_SR, /* 0x8d */ i86op_lea_word_R_M, /* 0x8e */ i86op_mov_word_SR_RM, /* 0x8f */ i86op_pop_RM, /* 0x90 */ i86op_nop, /* 0x91 */ i86op_xchg_word_AX_CX, /* 0x92 */ i86op_xchg_word_AX_DX, /* 0x93 */ i86op_xchg_word_AX_BX, /* 0x94 */ i86op_xchg_word_AX_SP, /* 0x95 */ i86op_xchg_word_AX_BP , /* 0x96 */ i86op_xchg_word_AX_SI , /* 0x97 */ i86op_xchg_word_AX_DI , /* 0x98 */ i86op_cbw, /* 0x99 */ i86op_cwd, /* 0x9a */ i86op_call_far_IMM, /* 0x9b */ i86op_wait, /* 0x9c */ i86op_pushf_word, /* 0x9d */ i86op_popf_word, /* 0x9e */ i86op_sahf, /* 0x9f */ i86op_lahf, /* 0xa0 */ i86op_mov_AL_M_IMM, /* 0xa1 */ i86op_mov_AX_M_IMM, /* 0xa2 */ i86op_mov_M_AL_IMM, /* 0xa3 */ i86op_mov_M_AX_IMM, /* 0xa4 */ i86op_movs_byte, /* 0xa5 */ i86op_movs_word, /* 0xa6 */ i86op_cmps_byte, /* 0xa7 */ i86op_cmps_word, /* 0xa8 */ i86op_test_AL_IMM, /* 0xa9 */ i86op_test_AX_IMM, /* 0xaa */ i86op_stos_byte, /* 0xab */ i86op_stos_word, /* 0xac */ i86op_lods_byte, /* 0xad */ i86op_lods_word, /* 0xac */ i86op_scas_byte, /* 0xad */ i86op_scas_word, /* 0xb0 */ i86op_mov_byte_AL_IMM, /* 0xb1 */ i86op_mov_byte_CL_IMM, /* 0xb2 */ i86op_mov_byte_DL_IMM, /* 0xb3 */ i86op_mov_byte_BL_IMM, /* 0xb4 */ i86op_mov_byte_AH_IMM, /* 0xb5 */ i86op_mov_byte_CH_IMM, /* 0xb6 */ i86op_mov_byte_DH_IMM, /* 0xb7 */ i86op_mov_byte_BH_IMM, /* 0xb8 */ i86op_mov_word_AX_IMM, /* 0xb9 */ i86op_mov_word_CX_IMM, /* 0xba */ i86op_mov_word_DX_IMM, /* 0xbb */ i86op_mov_word_BX_IMM, /* 0xbc */ i86op_mov_word_SP_IMM, /* 0xbd */ i86op_mov_word_BP_IMM, /* 0xbe */ i86op_mov_word_SI_IMM, /* 0xbf */ i86op_mov_word_DI_IMM, /* 0xc0 */ i86op_illegal_op, /* 0xc1 */ i86op_illegal_op, /* 0xc2 */ i86op_ret_near_IMM, /* 0xc3 */ i86op_ret_near, /* 0xc4 */ i86op_les_R_IMM, /* 0xc5 */ i86op_lds_R_IMM, /* 0xc6 */ i86op_mov_byte_RM_IMM, /* 0xc7 */ i86op_mov_word_RM_IMM, /* 0xc8 */ i86op_illegal_op, /* 0xc9 */ i86op_illegal_op, /* 0xca */ i86op_ret_far_IMM, /* 0xcb */ i86op_ret_far, /* 0xcc */ i86op_int3, /* 0xcd */ i86op_int_IMM, /* 0xce */ i86op_into, /* 0xcf */ i86op_iret, /* 0xd0 */ i86op_opcD0_byte_RM_1, /* 0xd1 */ i86op_opcD1_word_RM_1, /* 0xd2 */ i86op_opcD2_byte_RM_CL, /* 0xd3 */ i86op_opcD3_word_RM_CL, /* 0xd4 */ i86op_aam, /* 0xd5 */ i86op_aad, /* 0xd6 */ i86op_illegal_op, /* 0xd7 */ i86op_xlat, /* 0xd8 */ i86op_esc_coprocess_d8, /* 0xd9 */ i86op_esc_coprocess_d9, /* 0xda */ i86op_esc_coprocess_da, /* 0xdb */ i86op_esc_coprocess_db, /* 0xdc */ i86op_esc_coprocess_dc, /* 0xdd */ i86op_esc_coprocess_dd, /* 0xde */ i86op_esc_coprocess_de, /* 0xdf */ i86op_esc_coprocess_df, /* 0xe0 */ i86op_loopne, /* 0xe1 */ i86op_loope, /* 0xe2 */ i86op_loop, /* 0xe3 */ i86op_jcxz, /* 0xe4 */ i86op_in_byte_AL_IMM, /* 0xe5 */ i86op_in_word_AX_IMM, /* 0xe6 */ i86op_out_byte_IMM_AL, /* 0xe7 */ i86op_out_word_IMM_AX, /* 0xe8 */ i86op_call_near_IMM, /* 0xe9 */ i86op_jump_near_IMM, /* 0xea */ i86op_jump_far_IMM, /* 0xeb */ i86op_jump_byte_IMM, /* 0xec */ i86op_in_byte_AL_DX, /* 0xed */ i86op_in_word_AX_DX, /* 0xee */ i86op_out_byte_DX_AL, /* 0xef */ i86op_out_word_DX_AX, /* 0xf0 */ i86op_lock, /* 0xf1 */ i86op_illegal_op, /* 0xf2 */ i86op_repne, /* 0xf3 */ i86op_repe, /* 0xf4 */ i86op_halt, /* 0xf5 */ i86op_cmc, /* 0xf6 */ i86op_opcF6_byte_RM, /* 0xf7 */ i86op_opcF7_word_RM, /* 0xf8 */ i86op_clc, /* 0xf9 */ i86op_stc, /* 0xfa */ i86op_cli, /* 0xfb */ i86op_sti, /* 0xfc */ i86op_cld, /* 0xfd */ i86op_std, /* 0xfe */ i86op_opcFE_byte_RM, /* 0xff */ i86op_opcFF_word_RM, }; simh-3.8.1/AltairZ80/s100_if3.c0000644000175000017500000003567511043450360013763 0ustar vlmvlm/************************************************************************* * * * $Id: s100_if3.c 1991 2008-07-10 16:06:12Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * CompuPro System Support 1 module for SIMH. * * Note this does not include the Boot ROM on the System Support 1 Card * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define TRACE_MSG (1 << 1) #define RXIRQ_MSG (1 << 2) #define TXIRQ_MSG (1 << 3) #define UART_MSG (1 << 4) #define USER_MSG (1 << 5) #define IF3_MAX_BOARDS 4 #define UNIT_V_IF3_CONNECT (UNIT_V_UF + 1) /* Connect/Disconnect IF3 unit */ #define UNIT_IF3_CONNECT (1 << UNIT_V_IF3_CONNECT) #define IF3_PORT_BASE 0x300 typedef struct { PNP_INFO pnp; /* Plug and Play */ } IF3_INFO; static IF3_INFO if3_info_data = { { 0x0, 0, 0x10, 8 } }; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint32 PCX; extern int32 sio0d(const int32 port, const int32 io, const int32 data); extern int32 sio0s(const int32 port, const int32 io, const int32 data); static t_stat set_if3_connect(UNIT *uptr, int32 val, char *cptr, void *desc); static t_stat if3_reset(DEVICE *if3_dev); static t_stat if3_svc (UNIT *uptr); static uint8 IF3_Read(const uint32 Addr); static uint8 IF3_Write(const uint32 Addr, uint8 cData); static int32 if3dev(const int32 port, const int32 io, const int32 data); static t_stat update_rx_tx_isr (UNIT *uptr); static UNIT if3_unit[] = { { UDATA (&if3_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE | UNIT_IF3_CONNECT, 0) }, { UDATA (&if3_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, { UDATA (&if3_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) }, { UDATA (&if3_svc, UNIT_FIX | UNIT_DISABLE | UNIT_ROABLE, 0) } }; static uint8 if3_user = 0; static uint8 if3_board = 0; static uint8 if3_rimr[IF3_MAX_BOARDS] = { 0, 0, 0, 0 }; static uint8 if3_timr[IF3_MAX_BOARDS] = { 0, 0, 0, 0 }; static uint8 if3_risr[IF3_MAX_BOARDS] = { 0, 0, 0, 0 }; static uint8 if3_tisr[IF3_MAX_BOARDS] = { 0, 0, 0, 0 }; static REG if3_reg[] = { { HRDATA (USER, if3_user, 3), }, { HRDATA (BOARD, if3_board, 2), }, { BRDATA (RIMR, &if3_rimr[0], 16, 8, 4), }, { BRDATA (RISR, &if3_risr[0], 16, 8, 4), }, { BRDATA (TIMR, &if3_timr[0], 16, 8, 4), }, { BRDATA (TISR, &if3_tisr[0], 16, 8, 4), }, { NULL } }; static MTAB if3_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_IF3_CONNECT, UNIT_IF3_CONNECT,"INSTALLED", "INSTALLED", &set_if3_connect, NULL, NULL }, { UNIT_IF3_CONNECT, 0, "UNINSTALLED","UNINSTALLED", &set_if3_connect, NULL, NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(if3_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB if3_dt[] = { { "ERROR", ERROR_MSG }, { "TRACE", TRACE_MSG }, { "RXIRQ", RXIRQ_MSG }, { "TXIRQ", TXIRQ_MSG }, { "UART", UART_MSG }, { "USER", USER_MSG }, { NULL, 0 } }; DEVICE if3_dev = { "IF3", if3_unit, if3_reg, if3_mod, IF3_MAX_BOARDS, 10, 31, 1, IF3_MAX_BOARDS, IF3_MAX_BOARDS, NULL, NULL, &if3_reset, NULL, NULL, NULL, &if3_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, if3_dt, NULL, "Compupro Interfacer 3 IF3" }; static t_stat set_if3_connect(UNIT *uptr, int32 val, char *cptr, void *desc) { if(uptr->flags & UNIT_DISABLE) { TRACE_PRINT(ERROR_MSG, ("IF3[%d]: not enabled." NLP, uptr->u3)); return SCPE_OK; } if(val & UNIT_IF3_CONNECT) { TRACE_PRINT((RXIRQ_MSG|TXIRQ_MSG), ("IF3[%d]: IRQ polling started..." NLP, uptr->u3)); sim_activate(uptr, 100000); } else { TRACE_PRINT((RXIRQ_MSG|TXIRQ_MSG), ("IF3[%d]: IRQ polling stopped." NLP, uptr->u3)); sim_cancel(uptr); } return (SCPE_OK); } /* Reset routine */ static t_stat if3_reset(DEVICE *dptr) { uint8 i; PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ for(i=0;iio_base, pnp->io_size, RESOURCE_TYPE_IO, &if3dev, TRUE); } else { /* Connect IF3 at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &if3dev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } for(i=0;i> 3; /* guarantees that if3_board < IF3_MAX_BOARDS */ if3_user = cData & 0x7; TRACE_PRINT(USER_MSG, ("IF3[%d]: " ADDRESS_FORMAT " WR UART_SEL=0x%02x (Board=%d, Rel_User=%d, User=%d)" NLP, if3_board, PCX, cData, if3_board, if3_user, cData)); break; } return(0); } #define SS1_VI2_INT 2 /* IF3 Rx interrupts tied to VI2 */ #define SS1_VI3_INT 3 /* IF3 Tx interrupts tied to VI3 */ #define IF3_NUM_PORTS 8 /* Number of ports per IF3 board */ extern void raise_ss1_interrupt(uint8 isr_index); /* Unit service routine */ static t_stat if3_svc (UNIT *uptr) { uint8 pending_rx_irqs; uint8 pending_tx_irqs; uint8 board = uptr->u3; update_rx_tx_isr(uptr); pending_rx_irqs = if3_risr[board] & if3_rimr[board]; if(pending_rx_irqs) { TRACE_PRINT(RXIRQ_MSG, ("IF3[%d]: " ADDRESS_FORMAT " Rx IRQ Pending: 0x%02x" NLP, board, PCX, pending_rx_irqs)); raise_ss1_interrupt(SS1_VI2_INT); } pending_tx_irqs = if3_tisr[board] & if3_timr[board]; if(pending_tx_irqs) { TRACE_PRINT(TXIRQ_MSG, ("IF3[%d]: " ADDRESS_FORMAT " Tx IRQ Pending: 0x%02x" NLP, board, PCX, pending_tx_irqs)); raise_ss1_interrupt(SS1_VI3_INT); } sim_activate(&if3_unit[board], 200000); return SCPE_OK; } static t_stat update_rx_tx_isr (UNIT *uptr) { uint8 i; uint8 cData; uint8 board = uptr->u3; if3_risr[board] = 0; if3_tisr[board] = 0; for(i=0;i #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define VERBOSE_MSG (1 << 1) #define DMA_MSG (1 << 2) #define SELCHAN_MAX_DRIVES 1 #define UNIT_V_SELCHAN_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_SELCHAN_VERBOSE (1 << UNIT_V_SELCHAN_VERBOSE) typedef struct { PNP_INFO pnp; /* Plug and Play */ uint32 selchan; /* Selector Channel Register */ uint32 dma_addr; /* DMA Transfer Address */ uint32 dma_mode; /* DMA Mode register */ uint8 reg_cnt; /* Counter for selchan register */ } SELCHAN_INFO; static SELCHAN_INFO selchan_info_data = { { 0x0, 0, 0xF0, 1 } }; static SELCHAN_INFO *selchan_info = &selchan_info_data; int32 selchan_dma(uint8 *buf, uint32 len); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint32 PCX; /* These are needed for DMA. */ extern void PutByteDMA(const uint32 Addr, const uint32 Value); extern uint8 GetByteDMA(const uint32 Addr); static t_stat selchan_reset(DEVICE *selchan_dev); static int32 selchandev(const int32 port, const int32 io, const int32 data); static UNIT selchan_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, 0) } }; static REG selchan_reg[] = { { HRDATA (DMA_MODE, selchan_info_data.dma_mode, 8), }, { HRDATA (DMA_ADDR, selchan_info_data.dma_addr, 24), }, { NULL } }; static MTAB selchan_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, /* quiet, no warning messages */ { UNIT_SELCHAN_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_SELCHAN_VERBOSE, UNIT_SELCHAN_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(selchan_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB selchan_dt[] = { { "ERROR", ERROR_MSG }, { "VERBOSE",VERBOSE_MSG }, { "DMA", DMA_MSG }, { NULL, 0 } }; DEVICE selchan_dev = { "SELCHAN", selchan_unit, selchan_reg, selchan_mod, SELCHAN_MAX_DRIVES, 10, 31, 1, SELCHAN_MAX_DRIVES, SELCHAN_MAX_DRIVES, NULL, NULL, &selchan_reset, NULL, NULL, NULL, &selchan_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, selchan_dt, NULL, "Compupro Selector Channel SELCHAN" }; /* Reset routine */ static t_stat selchan_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &selchandev, TRUE); } else { /* Connect SELCHAN at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &selchandev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } #define SELCHAN_MODE_WRITE 0x80 /* Selector Channel Memory or I/O Write */ #define SELCHAN_MODE_IO 0x40 /* Set if I/O Access, otherwise memory */ #define SELCHAN_MODE_CNT_UP 0x20 /* Set = DMA Address Count Up, otherwise down. (Mem only */ #define SELCHAN_MODE_WAIT 0x10 /* Insert one wait state. */ #define SELCHAN_MODE_DMA_MASK 0x0F /* Mask for DMA Priority field */ static int32 selchandev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("SELCHAN: IO %s, Port %02x" NLP, io ? "WR" : "RD", port)); if(io) { selchan_info->selchan <<= 8; selchan_info->selchan &= 0xFFFFFF00; selchan_info->selchan |= data; selchan_info->dma_addr = (selchan_info->selchan & 0xFFFFF00) >> 8; selchan_info->dma_mode = (selchan_info->selchan & 0xFF); selchan_info->reg_cnt ++; if(selchan_info->reg_cnt == 4) { TRACE_PRINT(VERBOSE_MSG, ("SELCHAN: " ADDRESS_FORMAT " DMA=0x%06x, Mode=0x%02x (%s, %s, %s)" NLP, PCX, selchan_info->dma_addr, selchan_info->dma_mode, selchan_info->dma_mode & SELCHAN_MODE_WRITE ? "WR" : "RD", selchan_info->dma_mode & SELCHAN_MODE_IO ? "I/O" : "MEM", selchan_info->dma_mode & SELCHAN_MODE_IO ? "FIX" : selchan_info->dma_mode & SELCHAN_MODE_CNT_UP ? "INC" : "DEC")); } return 0; } else { TRACE_PRINT(VERBOSE_MSG, ("SELCHAN: " ADDRESS_FORMAT " Reset" NLP, PCX)); selchan_info->reg_cnt = 0; return(0xFF); } } int32 selchan_dma(uint8 *buf, uint32 len) { uint32 i; if(selchan_info->reg_cnt != 4) { printf("SELCHAN: " ADDRESS_FORMAT " Programming error: selector channel disabled." NLP, PCX); return (-1); } if(selchan_info->dma_mode & SELCHAN_MODE_IO) { printf("SELCHAN: " ADDRESS_FORMAT " I/O Not supported" NLP, PCX); return (-1); } else { TRACE_PRINT(DMA_MSG, ("SELCHAN: " ADDRESS_FORMAT " DMA %s Transfer, len=%d" NLP, PCX, (selchan_info->dma_mode & SELCHAN_MODE_WRITE) ? "WR" : "RD", len)); for(i=0;idma_mode & SELCHAN_MODE_WRITE) { PutByteDMA(selchan_info->dma_addr + i, buf[i]); } else { buf[i] = GetByteDMA(selchan_info->dma_addr + i); } } if(selchan_info->dma_mode & SELCHAN_MODE_CNT_UP) { selchan_info->dma_addr += i; } else { selchan_info->dma_addr -= i; } } return(0); } simh-3.8.1/AltairZ80/sim_imd.c0000644000175000017500000006622011111141262014140 0ustar vlmvlm/************************************************************************* * * * $Id: sim_imd.c 1999 2008-07-22 04:25:28Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * ImageDisk (IMD) Disk Image File access module for SIMH. * * see: http://www.classiccmp.org/dunfield/img/index.htm * * for details on the ImageDisk format and other utilities. * * * * Environment: * * User mode only * * * *************************************************************************/ /* Change log: - 06-Aug-2008, Tony Nicholson, Add support for logical Head and Cylinder maps in the .IMD image file (AGN) */ #include "sim_defs.h" #include "sim_imd.h" #include #ifdef _WIN32 #include /* for _chsize() */ #else #include #endif /* #define DBG_MSG */ #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* use NLP for new line printing while the simulation is running */ #define UNIX_PLATFORM (defined (__linux) || defined(__NetBSD__) \ || defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__APPLE__)) #if UNIX_PLATFORM #define NLP "\r\n" #else #define NLP "\n" #endif #if defined (__MWERKS__) && defined (macintosh) #define __FUNCTION__ __FILE__ #endif static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen); static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose); static t_stat diskFormat(DISK_INFO *myDisk); /* Open an existing IMD disk image. It will be opened and parsed, and after this * call, will be ready for sector read/write. The result is the corresponding * DISK_INFO or NULL if an error occurred. */ DISK_INFO *diskOpen(FILE *fileref, uint32 isVerbose) { DISK_INFO *myDisk = NULL; myDisk = (DISK_INFO *)malloc(sizeof(DISK_INFO)); myDisk->file = fileref; if (diskParse(myDisk, isVerbose) != SCPE_OK) { free(myDisk); myDisk = NULL; } return myDisk; } /* Scans the IMD file for the comment string, and returns it in comment buffer. * After this function returns, the file pointer is placed after the comment and * the 0x1A "EOF" marker. * * The comment parameter is optional, and if NULL, then the ocmment will not * be extracted from the IMD file, but the file position will still be advanced * to the end of the comment. */ static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen) { uint8 cData; uint32 commentLen = 0; /* rewind to the beginning of the file. */ rewind(myDisk->file); cData = fgetc(myDisk->file); while ((!feof(myDisk->file)) && (cData != 0x1a)) { if ((comment != NULL) && (commentLen < buffLen)) { comment[commentLen++] = cData; } cData = fgetc(myDisk->file); } if (comment != NULL) { if (commentLen == buffLen) commentLen--; comment[commentLen] = 0; } return SCPE_OK; } static uint32 headerOk(IMD_HEADER imd) { return (imd.cyl < MAX_CYL) && (imd.head < MAX_HEAD); } /* Parse an IMD image. This sets up sim_imd to be able to do sector read/write and * track write. */ static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose) { uint8 comment[256]; uint8 sectorMap[256]; uint8 sectorHeadMap[256]; uint8 sectorCylMap[256]; uint32 sectorSize, sectorHeadwithFlags, sectRecordType; uint32 i; uint8 start_sect; uint32 TotalSectorCount = 0; IMD_HEADER imd; if(myDisk == NULL) { return (SCPE_OPENERR); } memset(myDisk->track, 0, (sizeof(TRACK_INFO)*MAX_CYL*MAX_HEAD)); if (commentParse(myDisk, comment, sizeof(comment)) != SCPE_OK) { return (SCPE_OPENERR); } if(isVerbose) printf("%s" NLP, comment); myDisk->nsides = 1; myDisk->ntracks = 0; myDisk->flags = 0; /* Make sure all flags are clear. */ if(feof(myDisk->file)) { printf("SIM_IMD: Disk image is blank, it must be formatted." NLP); return (SCPE_OPENERR); } do { DBG_PRINT(("start of track %d at file offset %ld" NLP, myDisk->ntracks, ftell(myDisk->file))); fread(&imd, 1, 5, myDisk->file); if (feof(myDisk->file)) break; sectorSize = 128 << imd.sectsize; sectorHeadwithFlags = imd.head; /*AGN save the head and flags */ imd.head &= 1 ; /*AGN mask out flag bits to head 0 or 1 */ DBG_PRINT(("Track %d:" NLP, myDisk->ntracks)); DBG_PRINT(("\tMode=%d, Cyl=%d, Head=%d(%d), #sectors=%d, sectsize=%d (%d bytes)" NLP, imd.mode, imd.cyl, sectorHeadwithFlags, imd.head, imd.nsects, imd.sectsize, sectorSize)); if (!headerOk(imd)) { printf("SIM_IMD: Corrupt header." NLP); return (SCPE_OPENERR); } if((imd.head + 1) > myDisk->nsides) { myDisk->nsides = imd.head + 1; } myDisk->track[imd.cyl][imd.head].mode = imd.mode; myDisk->track[imd.cyl][imd.head].nsects = imd.nsects; myDisk->track[imd.cyl][imd.head].sectsize = sectorSize; fread(sectorMap, 1, imd.nsects, myDisk->file); myDisk->track[imd.cyl][imd.head].start_sector = imd.nsects; DBG_PRINT(("\tSector Map: ")); for(i=0;itrack[imd.cyl][imd.head].start_sector) { myDisk->track[imd.cyl][imd.head].start_sector = sectorMap[i]; } } DBG_PRINT((", Start Sector=%d", myDisk->track[imd.cyl][imd.head].start_sector)); if(sectorHeadwithFlags & IMD_FLAG_SECT_HEAD_MAP) { fread(sectorHeadMap, 1, imd.nsects, myDisk->file); DBG_PRINT(("\tSector Head Map: ")); for(i=0;ifile); DBG_PRINT(("\tSector Cyl Map: ")); for(i=0;ifile))); /* Build the table with location 0 being the start sector. */ start_sect = myDisk->track[imd.cyl][imd.head].start_sector; /* Now read each sector */ for(i=0;ifile); /* AGN Logical head mapping */ myDisk->track[imd.cyl][imd.head].logicalHead[i] = sectorHeadMap[i]; /* AGN Logical cylinder mapping */ myDisk->track[imd.cyl][imd.head].logicalCyl[i] = sectorCylMap[i]; switch(sectRecordType) { case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ if (sectorMap[i]-start_sect < MAX_SPT) myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = 0xBADBAD; else { printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; case SECT_RECORD_NORM: /* Normal Data */ case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ /* DBG_PRINT(("Uncompressed Data" NLP)); */ if (sectorMap[i]-start_sect < MAX_SPT) { myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); sim_fseek(myDisk->file, sectorSize, SEEK_CUR); } else { printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ if (sectorMap[i]-start_sect < MAX_SPT) { myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); myDisk->flags |= FD_FLAG_WRITELOCK; /* Write-protect the disk if any sectors are compressed. */ #ifdef VERBOSE_DEBUG DBG_PRINT(("Compressed Data = 0x%02x" NLP, fgetc(myDisk->file))); #else fgetc(myDisk->file); #endif } else { printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect); return (SCPE_OPENERR); } break; default: printf("SIM_IMD: ERROR: unrecognized sector record type %d" NLP, sectRecordType); return (SCPE_OPENERR); break; } DBG_PRINT((NLP)); } myDisk->ntracks++; } while (!feof(myDisk->file)); DBG_PRINT(("Processed %d sectors" NLP, TotalSectorCount)); #ifdef VERBOSE_DEBUG for(i=0;intracks;i++) { DBG_PRINT(("Track %02d: ", i)); for(j=0;jtrack[i][0].sectorOffsetMap[j])); } DBG_PRINT((NLP)); } #endif if(myDisk->flags & FD_FLAG_WRITELOCK) { printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress." NLP); } return SCPE_OK; } /* * This function closes the IMD image. After closing, the sector read/write operations are not * possible. * * The IMD file is not actually closed, we leave that to SIMH. */ t_stat diskClose(DISK_INFO **myDisk) { if(*myDisk == NULL) return SCPE_OPENERR; free(*myDisk); *myDisk = NULL; return SCPE_OK; } #define MAX_COMMENT_LEN 256 /* * Create an ImageDisk (IMD) file. This function just creates the comment header, and allows * the user to enter a comment. After the IMD is created, it must be formatted with a format * program on the simulated operating system, ie CP/M, CDOS, 86-DOS. * * If the IMD file already exists, the user will be given the option of overwriting it. */ t_stat diskCreate(FILE *fileref, char *ctlr_comment) { DISK_INFO *myDisk = NULL; char *comment; char *curptr; uint8 answer; int32 len, remaining; if(fileref == NULL) { return (SCPE_OPENERR); } if(sim_fsize(fileref) != 0) { printf("SIM_IMD: Disk image already has data, do you want to overwrite it? "); answer = getchar(); if((answer != 'y') && (answer != 'Y')) { return (SCPE_OPENERR); } } if((curptr = comment = calloc(1, MAX_COMMENT_LEN)) == 0) { printf("Memory allocation failure.\n"); return (SCPE_MEM); } printf("SIM_IMD: Enter a comment for this disk.\n" "SIM_IMD: Terminate with a '.' on an otherwise blank line.\n"); remaining = MAX_COMMENT_LEN; do { printf("IMD> "); fgets(curptr, remaining - 3, stdin); if (strcmp(curptr, ".\n") == 0) { remaining = 0; } else { len = strlen(curptr) - 1; if (curptr[len] != '\n') len++; remaining -= len; curptr += len; *curptr++ = 0x0d; *curptr++ = 0x0a; } } while (remaining > 4); *curptr = 0x00; /* rewind to the beginning of the file. */ rewind(fileref); /* Erase the contents of the IMD file in case we are overwriting an existing image. */ #ifdef _WIN32 /* This might work under UNIX and/or VMS since this POSIX, but I haven't tried it. */ _chsize(_fileno(fileref), ftell (fileref)); #else ftruncate(fileno(fileref), ftell (fileref)); #endif fprintf(fileref, "IMD SIMH %s %s\n", __DATE__, __TIME__); fputs(comment, fileref); free(comment); fprintf(fileref, "\n\n$Id: sim_imd.c 1999 2008-07-22 04:25:28Z hharte $\n"); fprintf(fileref, "%s\n", ctlr_comment); fputc(0x1A, fileref); /* EOF marker for IMD comment. */ fflush(fileref); if((myDisk = diskOpen(fileref, 0)) == NULL) { printf("SIM_IMD: Error opening disk for format.\n"); return(SCPE_OPENERR); } if(diskFormat(myDisk) != SCPE_OK) { printf("SIM_IMD: error formatting disk.\n"); } return diskClose(&myDisk); } t_stat diskFormat(DISK_INFO *myDisk) { uint8 i; uint8 sector_map[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; uint32 flags; printf("SIM_IMD: Formatting disk in IBM 3740 SS/SD Format.\n"); for(i=0;i<77;i++) { if((trackWrite(myDisk, i, 0, 26, 128, sector_map, IMD_MODE_500K_FM, 0xE5, &flags)) != 0) { printf("SIM_IMD: Error formatting track %d\n", i); return SCPE_IOERR; } else { putchar('.'); } } printf("\nSIM_IMD: Format Complete.\n"); return SCPE_OK; } uint32 imdGetSides(DISK_INFO *myDisk) { if(myDisk != NULL) { return(myDisk->nsides); } return (0); } uint32 imdIsWriteLocked(DISK_INFO *myDisk) { if(myDisk != NULL) { return((myDisk->flags & FD_FLAG_WRITELOCK) ? 1 : 0); } return (0); } /* Check that the given track/sector exists on the disk */ t_stat sectSeek(DISK_INFO *myDisk, uint32 Cyl, uint32 Head) { if(Cyl >= myDisk->ntracks) { return(SCPE_IOERR); } if(Head >= myDisk->nsides) { return(SCPE_IOERR); } if(myDisk->track[Cyl][Head].nsects == 0) { DBG_PRINT(("%s: invalid track/head" NLP, __FUNCTION__)); return(SCPE_IOERR); } return(SCPE_OK); } /* Read a sector from an IMD image. */ t_stat sectRead(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *readlen) { uint32 sectorFileOffset; uint8 sectRecordType; uint8 start_sect; *readlen = 0; *flags = 0; /* Check parameters */ if(myDisk == NULL) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(sectSeek(myDisk, Cyl, Head) != SCPE_OK) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(Sector > myDisk->track[Cyl][Head].nsects) { DBG_PRINT(("%s: invalid sector" NLP, __FUNCTION__)); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(buflen < myDisk->track[Cyl][Head].sectsize) { printf("%s: Reading C:%d/H:%d/S:%d, len=%d: user buffer too short, need %d" NLP, __FUNCTION__, Cyl, Head, Sector, buflen, myDisk->track[Cyl][Head].sectsize); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } start_sect = myDisk->track[Cyl][Head].start_sector; sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; DBG_PRINT(("Reading C:%d/H:%d/S:%d, len=%d, offset=0x%08x" NLP, Cyl, Head, Sector, buflen, sectorFileOffset)); sim_fseek(myDisk->file, sectorFileOffset-1, 0); sectRecordType = fgetc(myDisk->file); switch(sectRecordType) { case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ *flags |= IMD_DISK_IO_ERROR_GENERAL; break; case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ *flags |= IMD_DISK_IO_ERROR_CRC; case SECT_RECORD_NORM: /* Normal Data */ case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ /* DBG_PRINT(("Uncompressed Data" NLP)); */ fread(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); *readlen = myDisk->track[Cyl][Head].sectsize; break; case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ *flags |= IMD_DISK_IO_ERROR_CRC; case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ /* DBG_PRINT(("Compressed Data" NLP)); */ memset(buf, fgetc(myDisk->file), myDisk->track[Cyl][Head].sectsize); *readlen = myDisk->track[Cyl][Head].sectsize; *flags |= IMD_DISK_IO_COMPRESSED; break; default: printf("ERROR: unrecognized sector record type %d" NLP, sectRecordType); break; } /* Set flags for deleted address mark. */ switch(sectRecordType) { case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ *flags |= IMD_DISK_IO_DELETED_ADDR_MARK; default: break; } return(SCPE_OK); } /* Write a sector to an IMD image. */ t_stat sectWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *writelen) { uint32 sectorFileOffset; uint8 sectRecordType; uint8 start_sect; *writelen = 0; DBG_PRINT(("Writing C:%d/H:%d/S:%d, len=%d" NLP, Cyl, Head, Sector, buflen)); /* Check parameters */ if(myDisk == NULL) { *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(sectSeek(myDisk, Cyl, Head) != 0) { *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(Sector > myDisk->track[Cyl][Head].nsects) { DBG_PRINT(("%s: invalid sector" NLP, __FUNCTION__)); *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(myDisk->flags & FD_FLAG_WRITELOCK) { printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress." NLP); *flags = IMD_DISK_IO_ERROR_WPROT; return(SCPE_IOERR); } if(buflen < myDisk->track[Cyl][Head].sectsize) { printf("%s: user buffer too short [buflen %i < sectsize %i]" NLP, __FUNCTION__, buflen, myDisk->track[Cyl][Head].sectsize); *flags = IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } start_sect = myDisk->track[Cyl][Head].start_sector; sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; sim_fseek(myDisk->file, sectorFileOffset-1, 0); if (*flags & IMD_DISK_IO_ERROR_GENERAL) { sectRecordType = SECT_RECORD_UNAVAILABLE; } else if (*flags & IMD_DISK_IO_ERROR_CRC) { if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK) sectRecordType = SECT_RECORD_NORM_DAM_ERR; else sectRecordType = SECT_RECORD_NORM_ERR; } else { if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK) sectRecordType = SECT_RECORD_NORM_DAM; else sectRecordType = SECT_RECORD_NORM; } fputc(sectRecordType, myDisk->file); fwrite(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); *writelen = myDisk->track[Cyl][Head].sectsize; return(SCPE_OK); } /* Format an entire track. The new track to be formatted must be after any existing tracks on * the disk. * * This routine should be enhanced to re-format an existing track to the same format (this * does not involve changing the disk image size.) * * Any existing data on the disk image will be destroyed when Track 0, Head 0 is formatted. * At that time, the IMD file is truncated. So for the trackWrite to be used to sucessfully * format a disk image, then format program must format tracks starting with Cyl 0, Head 0, * and proceed sequentially through all tracks/heads on the disk. * * Format programs that are known to work include: * Cromemco CDOS "INIT.COM" * ADC Super-Six (CP/M-80) "FMT8.COM" * 86-DOS "INIT.COM" * */ t_stat trackWrite(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 numSectors, uint32 sectorLen, uint8 *sectorMap, uint8 mode, uint8 fillbyte, uint32 *flags) { FILE *fileref; IMD_HEADER track_header; uint8 *sectorData; unsigned long i; unsigned long dataLen; *flags = 0; /* Check parameters */ if(myDisk == NULL) { *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } if(myDisk->flags & FD_FLAG_WRITELOCK) { printf("Disk write-protected, cannot format tracks." NLP); *flags |= IMD_DISK_IO_ERROR_WPROT; return(SCPE_IOERR); } fileref = myDisk->file; DBG_PRINT(("Formatting C:%d/H:%d/N:%d, len=%d, Fill=0x%02x" NLP, Cyl, Head, numSectors, sectorLen, fillbyte)); /* Truncate the IMD file when formatting Cyl 0, Head 0 */ if((Cyl == 0) && (Head == 0)) { /* Skip over IMD comment field. */ commentParse(myDisk, NULL, 0); /* Truncate the IMD file after the comment field. */ #ifdef _WIN32 /* This might work under UNIX and/or VMS since this POSIX, but I haven't tried it. */ _chsize(_fileno(fileref), ftell (fileref)); #else ftruncate(fileno(fileref), ftell (fileref)); #endif /* Flush and re-parse the IMD file. */ fflush(fileref); diskParse(myDisk, 0); } /* Check to make sure the Cyl / Head is not already formatted. */ if(sectSeek(myDisk, Cyl, Head) == 0) { printf("SIM_IMD: ERROR: Not Formatting C:%d/H:%d, track already exists." NLP, Cyl, Head); *flags |= IMD_DISK_IO_ERROR_GENERAL; return(SCPE_IOERR); } track_header.mode = mode; track_header.cyl = Cyl; track_header.head = Head; track_header.nsects = numSectors; track_header.sectsize = sectorLen; /* Forward to end of the file, write track header and sector map. */ fseek(myDisk->file, 0, SEEK_END); fwrite(&track_header, sizeof(IMD_HEADER), 1, fileref); fwrite(sectorMap, 1, numSectors, fileref); /* Compute data length, and fill a sector buffer with the * sector record type as the first byte, and fill the sector * data with the fillbyte. */ dataLen = (128 << sectorLen)+1; sectorData = malloc(dataLen); memset(sectorData, fillbyte, dataLen); sectorData[0] = SECT_RECORD_NORM; /* For each sector on the track, write the record type and sector data. */ for(i=0;i>8)&0xff; lb = (d&0xff); l = lb + 10 * hb; CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); return (uint8) l; } uint16 aam_word(PC_ENV *m, uint8 d) { uint16 h,l; h = d / 10; l = d % 10; l |= (h<<8); CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); return l; } uint8 adc_byte(PC_ENV *m, uint8 d, uint8 s) { register uint16 res; /* all operands in native machine order */ register uint16 cc; if (ACCESS_FLAG(m,F_CF) ) res = 1 + d + s; else res = d + s; CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = (s & d) | ((~res) & (s | d)); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return (uint8) res; } uint16 adc_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ register uint32 cc; if (ACCESS_FLAG(m,F_CF) ) res = 1 + d + s; else res = d + s; /* set the carry flag to be bit 8 */ CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = (s & d) | ((~res) & (s | d)); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return res; } /* Given flags=f, and bytes d (dest) and s (source) perform the add and set the flags and the result back to *d. USE NATIVE MACHINE ORDER... */ uint8 add_byte(PC_ENV *m, uint8 d, uint8 s) { register uint16 res; /* all operands in native machine order */ register uint16 cc; res = d + s; /* set the carry flag to be bit 8 */ CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = (s & d) | ((~res) & (s | d)); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return (uint8) res; } /* Given flags=f, and bytes d (dest) and s (source) perform the add and set the flags and the result back to *d. USE NATIVE MACHINE ORDER... */ uint16 add_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ register uint32 cc; res = d + s; /* set the carry flag to be bit 8 */ CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = (s & d) | ((~res) & (s | d)); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return res; } /* Flags m->R_FLG, dest *d, source *s, do a bitwise and of the source and destination, and then store back to the destination. Size=byte. */ uint8 and_byte(PC_ENV *m, uint8 d, uint8 s) { register uint8 res; /* all operands in native machine order */ res = d & s; /* set the flags */ CLEAR_FLAG(m, F_OF); CLEAR_FLAG(m, F_CF); CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); return res; } /* Flags m->R_FLG, dest *d, source *s, do a bitwise and of the source and destination, and then store back to the destination. Size=byte. */ uint16 and_word(PC_ENV *m, uint16 d, uint16 s) { register uint16 res; /* all operands in native machine order */ res = d & s; /* set the flags */ CLEAR_FLAG(m, F_OF); CLEAR_FLAG(m, F_CF); CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); return res; } uint8 cmp_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - s; CLEAR_FLAG(m, F_CF); CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return d; /* long story why this is needed. Look at opcode 0x80 in ops.c, for an idea why this is necessary.*/ } uint16 cmp_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - s; CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return d; } uint8 dec_byte(PC_ENV *m, uint8 d) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - 1; CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ /* based on sub_byte, uses s==1. */ bc= (res&(~d|1))|(~d&1); /* carry flag unchanged */ CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res; } uint16 dec_word(PC_ENV *m, uint16 d) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - 1; CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ /* based on the sub_byte routine, with s==1 */ bc= (res&(~d|1))|(~d&1); /* carry flag unchanged */ CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res; } /* Given flags=f, and byte d (dest) perform the inc and set the flags and the result back to d. USE NATIVE MACHINE ORDER... */ uint8 inc_byte(PC_ENV *m, uint8 d) { register uint32 res; /* all operands in native machine order */ register uint32 cc; res = d + 1; CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = ((1 & d) | (~res)) & (1 | d); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return res; } /* Given flags=f, and byte d (dest) perform the inc and set the flags and the result back to *d. USE NATIVE MACHINE ORDER... */ uint16 inc_word(PC_ENV *m, uint16 d) { register uint32 res; /* all operands in native machine order */ register uint32 cc; res = d + 1; CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the carry chain SEE NOTE AT TOP.*/ cc = (1 & d) | ((~res) & (1 | d)); CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); return res ; } uint8 or_byte(PC_ENV *m, uint8 d, uint8 s) { register uint8 res; /* all operands in native machine order */ res = d | s; CLEAR_FLAG(m, F_OF); CLEAR_FLAG(m, F_CF); CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); return res; } uint16 or_word(PC_ENV *m, uint16 d, uint16 s) { register uint16 res; /* all operands in native machine order */ res = d | s; /* set the carry flag to be bit 8 */ CLEAR_FLAG(m, F_OF); CLEAR_FLAG(m, F_CF); CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); return res; } uint8 neg_byte(PC_ENV *m, uint8 s) { register uint8 res; register uint8 bc; CONDITIONAL_SET_FLAG(s!=0, m, F_CF); res = -s; CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); /* calculate the borrow chain --- modified such that d=0. substitutiing d=0 into bc= res&(~d|s)|(~d&s); (the one used for sub) and simplifying, since ~d=0xff..., ~d|s == 0xffff..., and res&0xfff... == res. Similarly ~d&s == s. So the simplified result is:*/ bc= res|s; CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res; } uint16 neg_word(PC_ENV *m, uint16 s) { register uint16 res; register uint16 bc; CONDITIONAL_SET_FLAG(s!=0, m, F_CF); res = -s; CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain --- modified such that d=0. substitutiing d=0 into bc= res&(~d|s)|(~d&s); (the one used for sub) and simplifying, since ~d=0xff..., ~d|s == 0xffff..., and res&0xfff... == res. Similarly ~d&s == s. So the simplified result is:*/ bc= res|s; CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res; } uint8 not_byte(PC_ENV *m, uint8 s) { return ~s; } uint16 not_word(PC_ENV *m, uint16 s) { return ~s; } /* access stuff from absolute location in memory. no segment registers are involved. */ uint16 mem_access_word(PC_ENV *m, int addr) { /* Load in two steps. Native byte order independent */ return GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); } /* given the register_set r, and memory descriptor m, and word w, push w onto the stack. w ASSUMED IN NATIVE MACHINE ORDER. Doesn't matter in this case??? */ void push_word(PC_ENV *m, uint16 w) { m->R_SP --; PutBYTEExtended((m->R_SS << 4) + m->R_SP, w >> 8); m->R_SP --; PutBYTEExtended((m->R_SS << 4) + m->R_SP, w & 0xff); } /* given the memory descriptor m, and word w, pop word from the stack. */ uint16 pop_word(PC_ENV *m) { register uint16 res; res = GetBYTEExtended((m->R_SS << 4) + m->R_SP); m->R_SP++; res |= GetBYTEExtended((m->R_SS << 4) + m->R_SP) << 8; m->R_SP++; return res; } /***************************************************************** BEGIN region consisting of bit shifts and rotates, much of which may be wrong. Large hirsute factor. *****************************************************************/ uint8 rcl_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res, cnt, mask,cf; /* s is the rotate distance. It varies from 0 - 8. */ /* have CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 want to rotate through the carry by "s" bits. We could loop, but that's inefficient. So the width is 9, and we split into three parts: The new carry flag (was B_n) the stuff in B_n-1 .. B_0 the stuff in B_7 .. B_n+1 The new rotate is done mod 9, and given this, for a rotation of n bits (mod 9) the new carry flag is then located n bits from the MSB. The low part is then shifted up cnt bits, and the high part is or'd in. Using CAPS for new values, and lowercase for the original values, this can be expressed as: IF n > 0 1) CF <- b_(8-n) 2) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 3) B_(n-1) <- cf 4) B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) I think this is correct. */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ { /* extract the new CARRY FLAG. */ /* CF <- b_(8-n) */ cf = (d >> (8-cnt)) & 0x1; /* get the low stuff which rotated into the range B_7 .. B_cnt */ /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 */ /* note that the right hand side done by the mask */ res = (d << cnt) & 0xff; /* now the high stuff which rotated around into the positions B_cnt-2 .. B_0 */ /* B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) */ /* shift it downward, 7-(n-2) = 9-n positions. and mask off the result before or'ing in. */ mask = (1<<(cnt-1)) - 1; res |= (d >> (9-cnt)) & mask; /* if the carry flag was set, or it in. */ if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ { /* B_(n-1) <- cf */ res |= 1 << (cnt-1); } /* set the new carry flag, based on the variable "cf" */ CONDITIONAL_SET_FLAG(cf, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ /* parenthesized this expression since it appears to be causing OF to be missed. */ CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>6)&0x2)], m, F_OF); } return res & 0xff; } uint16 rcl_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res, cnt, mask,cf; /* see analysis above. */ /* width here is 16 bits + carry bit */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ { /* extract the new CARRY FLAG. */ /* CF <- b_(16-n) */ cf = (d >> (16-cnt)) & 0x1; /* get the low stuff which rotated into the range B_15 .. B_cnt */ /* B_(15) .. B_(n) <- b_(16-(n+1)) .. b_0 */ /* note that the right hand side done by the mask */ res = (d << cnt) & 0xffff; /* now the high stuff which rotated around into the positions B_cnt-2 .. B_0 */ /* B_(n-2) .. B_0 <- b_15 .. b_(16-(n-1)) */ /* shift it downward, 15-(n-2) = 17-n positions. and mask off the result before or'ing in. */ mask = (1<<(cnt-1)) - 1; res |= (d >> (17-cnt)) & mask; /* if the carry flag was set, or it in. */ if (ACCESS_FLAG(m, F_CF)) /* carry flag is set */ { /* B_(n-1) <- cf */ res |= 1 << (cnt-1); } /* set the new carry flag, based on the variable "cf" */ CONDITIONAL_SET_FLAG(cf, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. Note that we're forming a 2 bit word here to index into the table. The expression cf+(res>>14)&0x2 represents the two bit word b_15 CF. */ /* parenthesized following expression... */ CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>14)&0x2)], m, F_OF); } return res & 0xffff; } uint8 rcr_byte(PC_ENV *m, uint8 d, uint8 s) { uint8 res, cnt; uint8 mask, cf, ocf = 0; /* rotate right through carry */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 The new rotate is done mod 9, and given this, for a rotation of n bits (mod 9) the new carry flag is then located n bits from the LSB. The low part is then shifted up cnt bits, and the high part is or'd in. Using CAPS for new values, and lowercase for the original values, this can be expressed as: IF n > 0 1) CF <- b_(n-1) 2) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) 3) B_(8-n) <- cf 4) B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) I think this is correct. */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ { /* extract the new CARRY FLAG. */ /* CF <- b_(n-1) */ if (cnt == 1) { cf = d & 0x1; /* note hackery here. Access_flag(..) evaluates to either 0 if flag not set non-zero if flag is set. doing access_flag(..) != 0 casts that into either 0..1 in any representation of the flags register (i.e. packed bit array or unpacked.) */ ocf = ACCESS_FLAG(m,F_CF) != 0; } else cf = (d >> (cnt-1)) & 0x1; /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_n */ /* note that the right hand side done by the mask This is effectively done by shifting the object to the right. The result must be masked, in case the object came in and was treated as a negative number. Needed???*/ mask = (1<<(8-cnt))-1; res = (d >> cnt) & mask; /* now the high stuff which rotated around into the positions B_cnt-2 .. B_0 */ /* B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) */ /* shift it downward, 7-(n-2) = 9-n positions. and mask off the result before or'ing in. */ res |= (d << (9-cnt)); /* if the carry flag was set, or it in. */ if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ { /* B_(8-n) <- cf */ res |= 1 << (8 - cnt); } /* set the new carry flag, based on the variable "cf" */ CONDITIONAL_SET_FLAG(cf, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ /* parenthesized... */ if (cnt == 1) { /* [JCE] Explicit braces to stop gcc -Wall moaning */ CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>6)&0x2)], m, F_OF); } } return res; } uint16 rcr_word(PC_ENV *m, uint16 d, uint16 s) { uint16 res, cnt; uint16 mask, cf, ocf = 0; /* rotate right through carry */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have CF B_15 ... B_0 The new rotate is done mod 17, and given this, for a rotation of n bits (mod 17) the new carry flag is then located n bits from the LSB. The low part is then shifted up cnt bits, and the high part is or'd in. Using CAPS for new values, and lowercase for the original values, this can be expressed as: IF n > 0 1) CF <- b_(n-1) 2) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) 3) B_(16-n) <- cf 4) B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) I think this is correct. */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ { /* extract the new CARRY FLAG. */ /* CF <- b_(n-1) */ if (cnt==1) { cf = d & 0x1; /* see note above on teh byte version */ ocf = ACCESS_FLAG(m,F_CF) != 0; } else cf = (d >> (cnt-1)) & 0x1; /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_n */ /* note that the right hand side done by the mask This is effectively done by shifting the object to the right. The result must be masked, in case the object came in and was treated as a negative number. Needed???*/ mask = (1<<(16-cnt))-1; res = (d >> cnt) & mask; /* now the high stuff which rotated around into the positions B_cnt-2 .. B_0 */ /* B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) */ /* shift it downward, 15-(n-2) = 17-n positions. and mask off the result before or'ing in. */ res |= (d << (17-cnt)); /* if the carry flag was set, or it in. */ if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ { /* B_(16-n) <- cf */ res |= 1 << (16 - cnt); } /* set the new carry flag, based on the variable "cf" */ CONDITIONAL_SET_FLAG(cf, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ if (cnt==1) { /* [JCE] Explicit braces to stop gcc -Wall moaning */ CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>14)&0x2)], m, F_OF); } } return res; } uint8 rol_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res, cnt, mask; /* rotate left */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have CF B_7 ... B_0 The new rotate is done mod 8. Much simpler than the "rcl" or "rcr" operations. IF n > 0 1) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) 2) B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) I think this is correct. */ res =d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ { /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) */ res = (d << cnt); /* B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) */ mask = (1 << cnt) - 1; res |= (d >> (8-cnt)) & mask; /* set the new carry flag, Note that it is the low order bit of the result!!! */ CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>6)&0x2)], m, F_OF); } return res&0xff; } uint16 rol_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res, cnt, mask; /* rotate left */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have CF B_15 ... B_0 The new rotate is done mod 8. Much simpler than the "rcl" or "rcr" operations. IF n > 0 1) B_(15) .. B_(n) <- b_(16-(n+1)) .. b_(0) 2) B_(n-1) .. B_(0) <- b_(16) .. b_(16-n) I think this is correct. */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ { /* B_(16) .. B_(n) <- b_(16-(n+1)) .. b_(0) */ res = (d << cnt); /* B_(n-1) .. B_(0) <- b_(15) .. b_(16-n) */ mask = (1 << cnt) - 1; res |= (d >> (16-cnt)) & mask; /* set the new carry flag, Note that it is the low order bit of the result!!! */ CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>14)&0x2)], m, F_OF); } return res&0xffff; } uint8 ror_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res, cnt, mask; /* rotate right */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have B_7 ... B_0 The rotate is done mod 8. IF n > 0 1) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) 2) B_(7) .. B_(8-n) <- b_(n-1) .. b_(0) */ res = d; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ { /* B_(7) .. B_(8-n) <- b_(n-1) .. b_(0)*/ res = (d << (8-cnt)); /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) */ mask = (1 << (8-cnt)) - 1; res |= (d >> (cnt)) & mask; /* set the new carry flag, Note that it is the low order bit of the result!!! */ CONDITIONAL_SET_FLAG(res&0x80, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of the two most significant bits. Blecck. */ CONDITIONAL_SET_FLAG(cnt==1&& xor_0x3_tab[(res>>6)&0x3], m, F_OF); } return res&0xff; } uint16 ror_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res, cnt, mask; /* rotate right */ /* s is the rotate distance. It varies from 0 - 8. d is the byte object rotated. have B_15 ... B_0 The rotate is done mod 16. IF n > 0 1) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) 2) B_(15) .. B_(16-n) <- b_(n-1) .. b_(0) I think this is correct. */ res =d ; /* [JCE] Extra brackets to stop gcc -Wall moaning */ if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ { /* B_(15) .. B_(16-n) <- b_(n-1) .. b_(0)*/ res = (d << (16-cnt)); /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) */ mask = (1 << (16-cnt)) - 1; res |= (d >> (cnt)) & mask; /* set the new carry flag, Note that it is the low order bit of the result!!! */ CONDITIONAL_SET_FLAG(res&0x8000, m, F_CF); /* OVERFLOW is set *IFF* cnt==1, then it is the xor of CF and the most significant bit. Blecck. */ CONDITIONAL_SET_FLAG(cnt==1 && xor_0x3_tab[(res>>14)&0x3], m, F_OF); } return res & 0xffff; } uint8 shl_byte(PC_ENV *m, uint8 d, uint8 s) { uint32 cnt,res,cf; if (s < 8) { cnt = s % 8; /* last bit shifted out goes into carry flag */ if (cnt>0) { res = d << cnt; cf = d & (1<<(8-cnt)); CONDITIONAL_SET_FLAG(cf, m, F_CF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); } else { res = (uint8)d; } if (cnt == 1) { /* Needs simplification. */ CONDITIONAL_SET_FLAG( (((res&0x80)==0x80) ^ (ACCESS_FLAG(m,F_CF) != 0)) , /* was (m->R_FLG&F_CF)==F_CF)), */ m, F_OF); } else { CLEAR_FLAG(m,F_OF); } } else { res = 0; /* CLEAR_FLAG(m,F_CF);*/ CONDITIONAL_SET_FLAG((s == 8) && (d & 1), m, F_CF); /* Peter Schorn bug fix */ CLEAR_FLAG(m,F_OF); CLEAR_FLAG(m,F_SF); CLEAR_FLAG(m,F_PF); SET_FLAG(m,F_ZF); } return res & 0xff; } uint16 shl_word(PC_ENV *m, uint16 d, uint16 s) { uint32 cnt,res,cf; if (s < 16) { cnt = s % 16; if (cnt > 0) { res = d << cnt; /* last bit shifted out goes into carry flag */ cf = d & (1<<(16-cnt)); CONDITIONAL_SET_FLAG(cf, m, F_CF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); } else { res = (uint16)d; } if (cnt == 1) { /* Needs simplification. */ CONDITIONAL_SET_FLAG( (((res&0x8000)==0x8000) ^ (ACCESS_FLAG(m,F_CF) != 0)), /*((m&F_CF)==F_CF)),*/ m, F_OF); } else { CLEAR_FLAG(m,F_OF); } } else { res = 0; /* CLEAR_FLAG(m,F_CF);*/ CONDITIONAL_SET_FLAG((s == 16) && (d & 1), m, F_CF); /* Peter Schorn bug fix */ CLEAR_FLAG(m,F_OF); SET_FLAG(m,F_ZF); CLEAR_FLAG(m,F_SF); CLEAR_FLAG(m,F_PF); } return res & 0xffff; } uint8 shr_byte(PC_ENV *m, uint8 d, uint8 s) { uint32 cnt,res,cf,mask; if (s < 8) { cnt = s % 8; if (cnt > 0) { mask = (1<<(8-cnt))-1; cf = d & (1<<(cnt-1)); res = (d >> cnt) & mask; CONDITIONAL_SET_FLAG(cf, m, F_CF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); } else { res = (uint8) d; } if (cnt == 1) { CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>6)&0x3], m, F_OF); } else { CLEAR_FLAG(m,F_OF); } } else { res = 0; /* CLEAR_FLAG(m,F_CF);*/ CONDITIONAL_SET_FLAG((s == 8) && (d & 0x80), m, F_CF); /* Peter Schorn bug fix */ CLEAR_FLAG(m,F_OF); SET_FLAG(m,F_ZF); CLEAR_FLAG(m,F_SF); CLEAR_FLAG(m,F_PF); } return res & 0xff; } uint16 shr_word(PC_ENV *m, uint16 d, uint16 s) { uint32 cnt,res,cf,mask; res = d; if (s < 16) { cnt = s % 16; if (cnt > 0) { mask = (1<<(16-cnt))-1; cf = d & (1<<(cnt-1)); res = (d >> cnt) & mask; CONDITIONAL_SET_FLAG(cf, m, F_CF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); } else { res = d; } if (cnt == 1) { CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>14)&0x3], m, F_OF); } else { CLEAR_FLAG(m,F_OF); } } else { res = 0; /* CLEAR_FLAG(m,F_CF);*/ CONDITIONAL_SET_FLAG((s == 16) && (d & 0x8000), m, F_CF); /* Peter Schorn bug fix */ CLEAR_FLAG(m,F_OF); SET_FLAG(m,F_ZF); CLEAR_FLAG(m,F_SF); CLEAR_FLAG(m,F_PF); } return res & 0xffff; } /* XXXX ??? flags may be wrong??? */ uint8 sar_byte(PC_ENV *m, uint8 d, uint8 s) { uint32 cnt,res,cf,mask,sf; res = d; sf = d & 0x80; cnt = s % 8; if (cnt > 0 && cnt < 8) { mask = (1<<(8-cnt))-1; cf = d & (1<<(cnt-1)); res = (d >> cnt) & mask; CONDITIONAL_SET_FLAG(cf, m, F_CF); if (sf) { res |= ~mask; } CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); } else if (cnt >= 8) { if (sf) { res = 0xff; SET_FLAG(m,F_CF); CLEAR_FLAG(m,F_ZF); SET_FLAG(m, F_SF); SET_FLAG(m, F_PF); } else { res = 0; CLEAR_FLAG(m,F_CF); SET_FLAG(m,F_ZF); CLEAR_FLAG(m, F_SF); CLEAR_FLAG(m, F_PF); } } return res&0xff; } uint16 sar_word(PC_ENV *m, uint16 d, uint16 s) { uint32 cnt, res, cf, mask, sf; sf = d & 0x8000; cnt = s % 16; res = d; if (cnt > 0 && cnt < 16) { mask = (1<<(16-cnt))-1; cf = d & (1<<(cnt-1)); res = (d >> cnt) & mask; CONDITIONAL_SET_FLAG(cf, m, F_CF); if (sf) { res |= ~mask; } CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); } else if (cnt >= 16) { if (sf) { res = 0xffff; SET_FLAG(m,F_CF); CLEAR_FLAG(m,F_ZF); SET_FLAG(m, F_SF); SET_FLAG(m, F_PF); } else { res = 0; CLEAR_FLAG(m,F_CF); SET_FLAG(m,F_ZF); CLEAR_FLAG(m, F_SF); CLEAR_FLAG(m, F_PF); } } return res & 0xffff; } uint8 sbb_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; if (ACCESS_FLAG(m,F_CF) ) res = d - s - 1; else res = d - s; CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res & 0xff; } uint16 sbb_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; if (ACCESS_FLAG(m,F_CF)) res = d - s - 1; else res = d - s; CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res & 0xffff; } uint8 sub_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - s; CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res & 0xff; } uint16 sub_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ register uint32 bc; res = d - s; CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* calculate the borrow chain. See note at top */ bc= (res&(~d|s))|(~d&s); CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); return res & 0xffff; } void test_byte(PC_ENV *m, uint8 d, uint8 s) { register uint32 res; /* all operands in native machine order */ res = d & s; CLEAR_FLAG(m, F_OF); CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* AF == dont care*/ CLEAR_FLAG(m, F_CF); } void test_word(PC_ENV *m, uint16 d, uint16 s) { register uint32 res; /* all operands in native machine order */ res = d & s; CLEAR_FLAG(m, F_OF); CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); /* AF == dont care*/ CLEAR_FLAG(m, F_CF); } uint8 xor_byte(PC_ENV *m, uint8 d, uint8 s) { register uint8 res; /* all operands in native machine order */ res = d ^ s; CLEAR_FLAG(m, F_OF); CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); CLEAR_FLAG(m, F_CF); return res; } uint16 xor_word(PC_ENV *m, uint16 d, uint16 s) { register uint16 res; /* all operands in native machine order */ res = d ^ s; /* set the carry flag to be bit 8 */ CLEAR_FLAG(m, F_OF); CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); CONDITIONAL_SET_FLAG(res==0, m, F_ZF); CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); CLEAR_FLAG(m, F_CF); return res; } void imul_byte(PC_ENV *m, uint8 s) { int16 res = (int8)m->R_AL * (int8)s; m->R_AX = res; /* Undef --- Can't hurt */ CONDITIONAL_SET_FLAG(res&0x8000,m,F_SF); CONDITIONAL_SET_FLAG(res==0,m,F_ZF); if (m->R_AH == 0 || m->R_AH == 0xff) { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_OF); } else { SET_FLAG(m, F_CF); SET_FLAG(m, F_OF); } } void imul_word(PC_ENV *m, uint16 s) { int32 res = (int16)m->R_AX * (int16)s; m->R_AX = res & 0xffff; m->R_DX = (res >> 16) & 0xffff; /* Undef --- Can't hurt */ CONDITIONAL_SET_FLAG(res&0x80000000,m,F_SF); CONDITIONAL_SET_FLAG(res==0,m,F_ZF); if (m->R_DX == 0 || m->R_DX == 0xffff) { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_OF); } else { SET_FLAG(m, F_CF); SET_FLAG(m, F_OF); } } void mul_byte(PC_ENV *m, uint8 s) { uint16 res = m->R_AL * s; m->R_AX = res; /* Undef --- Can't hurt */ CLEAR_FLAG(m,F_SF); CONDITIONAL_SET_FLAG(res==0,m,F_ZF); if (m->R_AH == 0) { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_OF); } else { SET_FLAG(m, F_CF); SET_FLAG(m, F_OF); } } void mul_word(PC_ENV *m, uint16 s) { uint32 res = m->R_AX * s; /* Undef --- Can't hurt */ CLEAR_FLAG(m,F_SF); CONDITIONAL_SET_FLAG(res==0,m,F_ZF); m->R_AX = res & 0xffff; m->R_DX = (res >> 16) & 0xffff; if (m->R_DX == 0) { CLEAR_FLAG(m, F_CF); CLEAR_FLAG(m, F_OF); } else { SET_FLAG(m, F_CF); SET_FLAG(m, F_OF); } } void idiv_byte(PC_ENV *m, uint8 s) { int32 dvd,div,mod; dvd = (int16)m->R_AX; if (s == 0) { i86_intr_raise(m,0); return; } div = dvd / (int8)s; mod = dvd % (int8)s; if (abs(div) > 0x7f) { i86_intr_raise(m,0); return; } /* Undef --- Can't hurt */ CONDITIONAL_SET_FLAG(div&0x80,m,F_SF); CONDITIONAL_SET_FLAG(div==0,m,F_ZF); m->R_AL = (int8)div; m->R_AH = (int8)mod; } void idiv_word(PC_ENV *m, uint16 s) { int32 dvd,dvs,div,mod; dvd = m->R_DX; dvd = (dvd << 16) | m->R_AX; if (s == 0) { i86_intr_raise(m,0); return; } dvs = (int16)s; div = dvd / dvs; mod = dvd % dvs; if (abs(div) > 0x7fff) { i86_intr_raise(m,0); return; } /* Undef --- Can't hurt */ CONDITIONAL_SET_FLAG(div&0x8000,m,F_SF); CONDITIONAL_SET_FLAG(div==0,m,F_ZF); /* debug_printf(m, "\n%d/%d=%d,%d\n",dvd,dvs,div,mod); */ m->R_AX = div; m->R_DX = mod; } void div_byte(PC_ENV *m, uint8 s) { uint32 dvd,dvs,div,mod; dvs = s; dvd = m->R_AX; if (s == 0) { i86_intr_raise(m,0); return; } div = dvd / dvs; mod = dvd % dvs; if (abs(div) > 0xff) { i86_intr_raise(m,0); return; } /* Undef --- Can't hurt */ CLEAR_FLAG(m,F_SF); CONDITIONAL_SET_FLAG(div==0,m,F_ZF); m->R_AL = (uint8)div; m->R_AH = (uint8)mod; } void div_word(PC_ENV *m, uint16 s) { uint32 dvd,dvs,div,mod; dvd = m->R_DX; dvd = (dvd << 16) | m->R_AX; dvs = s; if (dvs == 0) { i86_intr_raise(m,0); return; } div = dvd / dvs; mod = dvd % dvs; /* printf("dvd=%x dvs=%x -> div=%x mod=%x\n",dvd, dvs,div, mod);*/ if (abs(div) > 0xffff) { i86_intr_raise(m,0); return; } /* Undef --- Can't hurt */ CLEAR_FLAG(m,F_SF); CONDITIONAL_SET_FLAG(div==0,m,F_ZF); m->R_AX = div; m->R_DX = mod; } simh-3.8.1/AltairZ80/altairz80_cpu_nommu.c0000644000175000017500000053001711111141274016422 0ustar vlmvlm/* altairz80_cpu_opt.c: MITS Altair CPU (8080 and Z80) Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 Code for Z80 CPU from Frank D. Cringle ((c) 1995 under GNU license) */ #include "altairz80_defs.h" #define FLAG_C 1 #define FLAG_N 2 #define FLAG_P 4 #define FLAG_H 16 #define FLAG_Z 64 #define FLAG_S 128 #define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f #define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) #define LOW_DIGIT(x) ((x) & 0xf) #define HIGH_DIGIT(x) (((x) >> 4) & 0xf) #define LOW_REGISTER(x) ((x) & 0xff) #define HIGH_REGISTER(x) (((x) >> 8) & 0xff) #define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) #define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) #define PARITY(x) parityTable[(x) & 0xff] /* SET_PV and SET_PV2 are used to provide correct PARITY flag semantics for the 8080 in cases where the Z80 uses the overflow flag */ #define SET_PVS(s) ((chiptype == CHIP_TYPE_Z80) ? (((cbits >> 6) ^ (cbits >> 5)) & 4) : (PARITY(s))) #define SET_PV (SET_PVS(sum)) #define SET_PV2(x) ((chiptype == CHIP_TYPE_Z80) ? (((temp == (x)) << 2)) : (PARITY(temp))) /* CHECK_CPU_8080 must be invoked whenever a Z80 only instruction is executed In case a Z80 instruction is executed on an 8080 the following two cases exist: 1) Trapping is enabled: execution stops 2) Trapping is not enabled: decoding continues with the next byte */ #define CHECK_CPU_8080 \ if (chiptype == CHIP_TYPE_8080) { \ if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ reason = STOP_OPCODE; \ goto end_decode; \ } \ else continue; \ } /* CHECK_CPU_Z80 must be invoked whenever a non Z80 instruction is executed */ #define CHECK_CPU_Z80 \ if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ reason = STOP_OPCODE; \ goto end_decode; \ } #define POP(x) { \ register uint32 y = RAM_PP(SP); \ x = y + (RAM_PP(SP) << 8); \ } #define JPC(cond) { \ if (cond) { \ PC = GET_WORD(PC); \ } \ else { \ PC += 2; \ } \ } #define CALLC(cond) { \ if (cond) { \ register uint32 adrr = GET_WORD(PC); \ PUSH(PC + 2); \ PC = adrr; \ } \ else { \ PC += 2; \ } \ } /* function prototypes */ t_stat sim_instr_nommu(void); extern void out(const uint32 Port, const uint32 Value); extern uint32 in(const uint32 Port); extern UNIT cpu_unit; extern uint32 PCX; /* external view of PC */ extern int32 AF_S; /* AF register */ extern int32 BC_S; /* BC register */ extern int32 DE_S; /* DE register */ extern int32 HL_S; /* HL register */ extern int32 IX_S; /* IX register */ extern int32 IY_S; /* IY register */ extern int32 PC_S; /* program counter */ extern int32 SP_S; /* SP register */ extern int32 AF1_S; /* alternate AF register */ extern int32 BC1_S; /* alternate BC register */ extern int32 DE1_S; /* alternate DE register */ extern int32 HL1_S; /* alternate HL register */ extern int32 IFF_S; /* Interrupt Flip Flop */ extern int32 IR_S; /* Interrupt (upper) / Refresh (lower) register */ extern int32 chiptype; /* the following tables precompute some common subexpressions parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8) cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8) negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) */ /* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ static const uint8 parityTable[256] = { 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, }; /* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ static const uint8 incTable[257] = { 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 }; /* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ static const uint8 decTable[256] = { 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, }; /* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ static const uint8 cbitsTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, }; /* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ static const uint16 cbitsDup8Table[512] = { 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, }; /* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ static const uint8 cbitsDup16Table[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, }; /* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ static const uint8 cbits2Table[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ static const uint16 rrcaTable[256] = { 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, }; /* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ static const uint16 rraTable[256] = { 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, }; /* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ static const uint16 addTable[512] = { 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, }; /* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ static const uint16 subTable[256] = { 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, }; /* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ static const uint16 andTable[256] = { 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, }; /* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ static const uint16 xororTable[256] = { 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, }; /* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ static const uint8 rotateShiftTable[256] = { 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, }; /* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ static const uint8 incZ80Table[257] = { 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, }; /* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ static const uint8 decZ80Table[256] = { 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, }; /* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ static const uint8 cbitsZ80Table[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, }; /* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ static const uint8 cbitsZ80DupTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, }; /* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ static const uint8 cbits2Z80Table[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8), i = 0..511 */ static const uint8 cbits2Z80DupTable[512] = { 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, }; /* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ static const uint8 negTable[256] = { 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, }; /* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ static const uint16 rrdrldTable[256] = { 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, }; /* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ static const uint8 cpTable[256] = { 64, 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, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, }; /* Memory management */ uint8 MOPT[MAXBANKSIZE]; /* RAM which is present */ static uint8 GET_BYTE(register uint32 Addr) { return MOPT[Addr & ADDRMASK]; } static void PUT_BYTE(register uint32 Addr, register uint32 Value) { MOPT[Addr & ADDRMASK] = Value; } static void PUT_WORD(register uint32 Addr, register uint32 Value) { MOPT[Addr & ADDRMASK] = Value; MOPT[(Addr + 1) & ADDRMASK] = Value >> 8; } static uint16 GET_WORD(register uint32 a) { return GET_BYTE(a) | (GET_BYTE(a + 1) << 8); } #define RAM_MM(a) GET_BYTE(a--) #define RAM_PP(a) GET_BYTE(a++) #define PUT_BYTE_PP(a,v) PUT_BYTE(a++, v) #define PUT_BYTE_MM(a,v) PUT_BYTE(a--, v) #define MM_PUT_BYTE(a,v) PUT_BYTE(--a, v) #define PUSH(x) do { \ MM_PUT_BYTE(SP, (x) >> 8); \ MM_PUT_BYTE(SP, x); \ } while (0) /* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR Pre condition temp == value of register B at entry of the instruction acu == value of transferred byte (IN or OUT) Post condition F is set correctly Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where x == (C + 1) & 0xff for INIR x == L for OTIR and OTDR x == (C - 1) & 0xff for INDR Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where x == (C + 1) & 0xff for INI x == L for OUTI and OUTD x == (C - 1) & 0xff for IND */ #define INOUTFLAGS(syxz, x) \ AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ ((acu & 0x80) >> 6) | /* NF */ \ ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ parityTable[((acu + (x)) & 7) ^ temp] /* PF */ #define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) #define INOUTFLAGS_NONZERO(x) \ INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) t_stat sim_instr_nommu(void) { extern int32 sim_interval; extern uint32 sim_brk_summ; int32 reason = SCPE_OK; register uint32 AF; register uint32 BC; register uint32 DE; register uint32 HL; register uint32 PC; register uint32 SP; register uint32 IX; register uint32 IY; register uint32 temp = 0; register uint32 acu = 0; register uint32 sum; register uint32 cbits; register uint32 op; register uint32 adr; register int32 l_sim_brk_summ; AF = AF_S; BC = BC_S; DE = DE_S; HL = HL_S; PC = PC_S & ADDRMASK; SP = SP_S; IX = IX_S; IY = IY_S; l_sim_brk_summ = sim_brk_summ; /* main instruction fetch/decode loop */ while (TRUE) { /* loop until halted */ if (sim_interval <= 0) { /* check clock queue */ #if !UNIX_PLATFORM if ((reason = sim_poll_kbd()) == SCPE_STOP) break; /* poll on platforms without reliable signalling */ #endif if ((reason = sim_process_event())) break; } if (l_sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) {/* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } PCX = PC; sim_interval--; switch(RAM_PP(PC)) { case 0x00: /* NOP */ break; case 0x01: /* LD BC,nnnn */ BC = GET_WORD(PC); PC += 2; break; case 0x02: /* LD (BC),A */ PUT_BYTE(BC, HIGH_REGISTER(AF)); break; case 0x03: /* INC BC */ ++BC; break; case 0x04: /* INC B */ BC += 0x100; temp = HIGH_REGISTER(BC); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x05: /* DEC B */ BC -= 0x100; temp = HIGH_REGISTER(BC); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x06: /* LD B,nn */ SET_HIGH_REGISTER(BC, RAM_PP(PC)); break; case 0x07: /* RLCA */ AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | (AF & 0xc4) | ((AF >> 15) & 1); break; case 0x08: /* EX AF,AF' */ CHECK_CPU_8080; temp = AF; AF = AF1_S; AF1_S = temp; break; case 0x09: /* ADD HL,BC */ HL &= ADDRMASK; BC &= ADDRMASK; sum = HL + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; HL = sum; break; case 0x0a: /* LD A,(BC) */ SET_HIGH_REGISTER(AF, GET_BYTE(BC)); break; case 0x0b: /* DEC BC */ --BC; break; case 0x0c: /* INC C */ temp = LOW_REGISTER(BC) + 1; SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x0d: /* DEC C */ temp = LOW_REGISTER(BC) - 1; SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x0e: /* LD C,nn */ SET_LOW_REGISTER(BC, RAM_PP(PC)); break; case 0x0f: /* RRCA */ AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; break; case 0x10: /* DJNZ dd */ CHECK_CPU_8080; if ((BC -= 0x100) & 0xff00) PC += (int8) GET_BYTE(PC) + 1; else PC++; break; case 0x11: /* LD DE,nnnn */ DE = GET_WORD(PC); PC += 2; break; case 0x12: /* LD (DE),A */ PUT_BYTE(DE, HIGH_REGISTER(AF)); break; case 0x13: /* INC DE */ ++DE; break; case 0x14: /* INC D */ DE += 0x100; temp = HIGH_REGISTER(DE); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x15: /* DEC D */ DE -= 0x100; temp = HIGH_REGISTER(DE); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x16: /* LD D,nn */ SET_HIGH_REGISTER(DE, RAM_PP(PC)); break; case 0x17: /* RLA */ AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | (AF & 0xc4) | ((AF >> 15) & 1); break; case 0x18: /* JR dd */ CHECK_CPU_8080; PC += (int8) GET_BYTE(PC) + 1; break; case 0x19: /* ADD HL,DE */ HL &= ADDRMASK; DE &= ADDRMASK; sum = HL + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; HL = sum; break; case 0x1a: /* LD A,(DE) */ SET_HIGH_REGISTER(AF, GET_BYTE(DE)); break; case 0x1b: /* DEC DE */ --DE; break; case 0x1c: /* INC E */ temp = LOW_REGISTER(DE) + 1; SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x1d: /* DEC E */ temp = LOW_REGISTER(DE) - 1; SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x1e: /* LD E,nn */ SET_LOW_REGISTER(DE, RAM_PP(PC)); break; case 0x1f: /* RRA */ AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; break; case 0x20: /* JR NZ,dd */ CHECK_CPU_8080; if (TSTFLAG(Z)) PC++; else PC += (int8) GET_BYTE(PC) + 1; break; case 0x21: /* LD HL,nnnn */ HL = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),HL */ temp = GET_WORD(PC); PUT_WORD(temp, HL); PC += 2; break; case 0x23: /* INC HL */ ++HL; break; case 0x24: /* INC H */ HL += 0x100; temp = HIGH_REGISTER(HL); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x25: /* DEC H */ HL -= 0x100; temp = HIGH_REGISTER(HL); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x26: /* LD H,nn */ SET_HIGH_REGISTER(HL, RAM_PP(PC)); break; case 0x27: /* DAA */ acu = HIGH_REGISTER(AF); temp = LOW_DIGIT(acu); cbits = TSTFLAG(C); if (TSTFLAG(N)) { /* last operation was a subtract */ int hd = cbits || acu > 0x99; if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ if (temp > 5) SETFLAG(H, 0); acu -= 6; acu &= 0xff; } if (hd) acu -= 0x160; /* adjust high digit */ } else { /* last operation was an add */ if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ SETFLAG(H, (temp > 9)); acu += 6; } if (cbits || ((acu & 0x1f0) > 0x90)) acu += 0x60; /* adjust high digit */ } AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; break; case 0x28: /* JR Z,dd */ CHECK_CPU_8080; if (TSTFLAG(Z)) PC += (int8) GET_BYTE(PC) + 1; else PC++; break; case 0x29: /* ADD HL,HL */ HL &= ADDRMASK; sum = HL + HL; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; HL = sum; break; case 0x2a: /* LD HL,(nnnn) */ temp = GET_WORD(PC); HL = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC HL */ --HL; break; case 0x2c: /* INC L */ temp = LOW_REGISTER(HL) + 1; SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x2d: /* DEC L */ temp = LOW_REGISTER(HL) - 1; SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x2e: /* LD L,nn */ SET_LOW_REGISTER(HL, RAM_PP(PC)); break; case 0x2f: /* CPL */ AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; break; case 0x30: /* JR NC,dd */ CHECK_CPU_8080; if (TSTFLAG(C)) PC++; else PC += (int8) GET_BYTE(PC) + 1; break; case 0x31: /* LD SP,nnnn */ SP = GET_WORD(PC); PC += 2; break; case 0x32: /* LD (nnnn),A */ temp = GET_WORD(PC); PUT_BYTE(temp, HIGH_REGISTER(AF)); PC += 2; break; case 0x33: /* INC SP */ ++SP; break; case 0x34: /* INC (HL) */ temp = GET_BYTE(HL) + 1; PUT_BYTE(HL, temp); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); break; case 0x35: /* DEC (HL) */ temp = GET_BYTE(HL) - 1; PUT_BYTE(HL, temp); AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); break; case 0x36: /* LD (HL),nn */ PUT_BYTE(HL, RAM_PP(PC)); break; case 0x37: /* SCF */ AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; break; case 0x38: /* JR C,dd */ CHECK_CPU_8080; if (TSTFLAG(C)) PC += (int8) GET_BYTE(PC) + 1; else PC++; break; case 0x39: /* ADD HL,SP */ HL &= ADDRMASK; SP &= ADDRMASK; sum = HL + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; HL = sum; break; case 0x3a: /* LD A,(nnnn) */ temp = GET_WORD(PC); SET_HIGH_REGISTER(AF, GET_BYTE(temp)); PC += 2; break; case 0x3b: /* DEC SP */ --SP; break; case 0x3c: /* INC A */ AF += 0x100; temp = HIGH_REGISTER(AF); AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ break; case 0x3d: /* DEC A */ AF -= 0x100; temp = HIGH_REGISTER(AF); AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ break; case 0x3e: /* LD A,nn */ SET_HIGH_REGISTER(AF, RAM_PP(PC)); break; case 0x3f: /* CCF */ AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); break; case 0x40: /* LD B,B */ break; case 0x41: /* LD B,C */ BC = (BC & 0xff) | ((BC & 0xff) << 8); break; case 0x42: /* LD B,D */ BC = (BC & 0xff) | (DE & ~0xff); break; case 0x43: /* LD B,E */ BC = (BC & 0xff) | ((DE & 0xff) << 8); break; case 0x44: /* LD B,H */ BC = (BC & 0xff) | (HL & ~0xff); break; case 0x45: /* LD B,L */ BC = (BC & 0xff) | ((HL & 0xff) << 8); break; case 0x46: /* LD B,(HL) */ SET_HIGH_REGISTER(BC, GET_BYTE(HL)); break; case 0x47: /* LD B,A */ BC = (BC & 0xff) | (AF & ~0xff); break; case 0x48: /* LD C,B */ BC = (BC & ~0xff) | ((BC >> 8) & 0xff); break; case 0x49: /* LD C,C */ break; case 0x4a: /* LD C,D */ BC = (BC & ~0xff) | ((DE >> 8) & 0xff); break; case 0x4b: /* LD C,E */ BC = (BC & ~0xff) | (DE & 0xff); break; case 0x4c: /* LD C,H */ BC = (BC & ~0xff) | ((HL >> 8) & 0xff); break; case 0x4d: /* LD C,L */ BC = (BC & ~0xff) | (HL & 0xff); break; case 0x4e: /* LD C,(HL) */ SET_LOW_REGISTER(BC, GET_BYTE(HL)); break; case 0x4f: /* LD C,A */ BC = (BC & ~0xff) | ((AF >> 8) & 0xff); break; case 0x50: /* LD D,B */ DE = (DE & 0xff) | (BC & ~0xff); break; case 0x51: /* LD D,C */ DE = (DE & 0xff) | ((BC & 0xff) << 8); break; case 0x52: /* LD D,D */ break; case 0x53: /* LD D,E */ DE = (DE & 0xff) | ((DE & 0xff) << 8); break; case 0x54: /* LD D,H */ DE = (DE & 0xff) | (HL & ~0xff); break; case 0x55: /* LD D,L */ DE = (DE & 0xff) | ((HL & 0xff) << 8); break; case 0x56: /* LD D,(HL) */ SET_HIGH_REGISTER(DE, GET_BYTE(HL)); break; case 0x57: /* LD D,A */ DE = (DE & 0xff) | (AF & ~0xff); break; case 0x58: /* LD E,B */ DE = (DE & ~0xff) | ((BC >> 8) & 0xff); break; case 0x59: /* LD E,C */ DE = (DE & ~0xff) | (BC & 0xff); break; case 0x5a: /* LD E,D */ DE = (DE & ~0xff) | ((DE >> 8) & 0xff); break; case 0x5b: /* LD E,E */ break; case 0x5c: /* LD E,H */ DE = (DE & ~0xff) | ((HL >> 8) & 0xff); break; case 0x5d: /* LD E,L */ DE = (DE & ~0xff) | (HL & 0xff); break; case 0x5e: /* LD E,(HL) */ SET_LOW_REGISTER(DE, GET_BYTE(HL)); break; case 0x5f: /* LD E,A */ DE = (DE & ~0xff) | ((AF >> 8) & 0xff); break; case 0x60: /* LD H,B */ HL = (HL & 0xff) | (BC & ~0xff); break; case 0x61: /* LD H,C */ HL = (HL & 0xff) | ((BC & 0xff) << 8); break; case 0x62: /* LD H,D */ HL = (HL & 0xff) | (DE & ~0xff); break; case 0x63: /* LD H,E */ HL = (HL & 0xff) | ((DE & 0xff) << 8); break; case 0x64: /* LD H,H */ break; case 0x65: /* LD H,L */ HL = (HL & 0xff) | ((HL & 0xff) << 8); break; case 0x66: /* LD H,(HL) */ SET_HIGH_REGISTER(HL, GET_BYTE(HL)); break; case 0x67: /* LD H,A */ HL = (HL & 0xff) | (AF & ~0xff); break; case 0x68: /* LD L,B */ HL = (HL & ~0xff) | ((BC >> 8) & 0xff); break; case 0x69: /* LD L,C */ HL = (HL & ~0xff) | (BC & 0xff); break; case 0x6a: /* LD L,D */ HL = (HL & ~0xff) | ((DE >> 8) & 0xff); break; case 0x6b: /* LD L,E */ HL = (HL & ~0xff) | (DE & 0xff); break; case 0x6c: /* LD L,H */ HL = (HL & ~0xff) | ((HL >> 8) & 0xff); break; case 0x6d: /* LD L,L */ break; case 0x6e: /* LD L,(HL) */ SET_LOW_REGISTER(HL, GET_BYTE(HL)); break; case 0x6f: /* LD L,A */ HL = (HL & ~0xff) | ((AF >> 8) & 0xff); break; case 0x70: /* LD (HL),B */ PUT_BYTE(HL, HIGH_REGISTER(BC)); break; case 0x71: /* LD (HL),C */ PUT_BYTE(HL, LOW_REGISTER(BC)); break; case 0x72: /* LD (HL),D */ PUT_BYTE(HL, HIGH_REGISTER(DE)); break; case 0x73: /* LD (HL),E */ PUT_BYTE(HL, LOW_REGISTER(DE)); break; case 0x74: /* LD (HL),H */ PUT_BYTE(HL, HIGH_REGISTER(HL)); break; case 0x75: /* LD (HL),L */ PUT_BYTE(HL, LOW_REGISTER(HL)); break; case 0x76: /* HALT */ PC--; if (cpu_unit.flags & UNIT_CPU_STOPONHALT) { reason = STOP_HALT; goto end_decode; } sim_interval = 0; break; case 0x77: /* LD (HL),A */ PUT_BYTE(HL, HIGH_REGISTER(AF)); break; case 0x78: /* LD A,B */ AF = (AF & 0xff) | (BC & ~0xff); break; case 0x79: /* LD A,C */ AF = (AF & 0xff) | ((BC & 0xff) << 8); break; case 0x7a: /* LD A,D */ AF = (AF & 0xff) | (DE & ~0xff); break; case 0x7b: /* LD A,E */ AF = (AF & 0xff) | ((DE & 0xff) << 8); break; case 0x7c: /* LD A,H */ AF = (AF & 0xff) | (HL & ~0xff); break; case 0x7d: /* LD A,L */ AF = (AF & 0xff) | ((HL & 0xff) << 8); break; case 0x7e: /* LD A,(HL) */ SET_HIGH_REGISTER(AF, GET_BYTE(HL)); break; case 0x7f: /* LD A,A */ break; case 0x80: /* ADD A,B */ temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x81: /* ADD A,C */ temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x82: /* ADD A,D */ temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x83: /* ADD A,E */ temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x84: /* ADD A,H */ temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x85: /* ADD A,L */ temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x86: /* ADD A,(HL) */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x87: /* ADD A,A */ cbits = 2 * HIGH_REGISTER(AF); AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); break; case 0x88: /* ADC A,B */ temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x89: /* ADC A,C */ temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8a: /* ADC A,D */ temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8b: /* ADC A,E */ temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8c: /* ADC A,H */ temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8d: /* ADC A,L */ temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8e: /* ADC A,(HL) */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0x8f: /* ADC A,A */ cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); break; case 0x90: /* SUB B */ temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x91: /* SUB C */ temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x92: /* SUB D */ temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x93: /* SUB E */ temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x94: /* SUB H */ temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x95: /* SUB L */ temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x96: /* SUB (HL) */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x97: /* SUB A */ AF = (chiptype == CHIP_TYPE_Z80) ? 0x42 : 0x46; break; case 0x98: /* SBC A,B */ temp = HIGH_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x99: /* SBC A,C */ temp = LOW_REGISTER(BC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9a: /* SBC A,D */ temp = HIGH_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9b: /* SBC A,E */ temp = LOW_REGISTER(DE); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9c: /* SBC A,H */ temp = HIGH_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9d: /* SBC A,L */ temp = LOW_REGISTER(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9e: /* SBC A,(HL) */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0x9f: /* SBC A,A */ cbits = -TSTFLAG(C); AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); break; case 0xa0: /* AND B */ AF = andTable[((AF & BC) >> 8) & 0xff]; break; case 0xa1: /* AND C */ AF = andTable[((AF >> 8) & BC) & 0xff]; break; case 0xa2: /* AND D */ AF = andTable[((AF & DE) >> 8) & 0xff]; break; case 0xa3: /* AND E */ AF = andTable[((AF >> 8) & DE) & 0xff]; break; case 0xa4: /* AND H */ AF = andTable[((AF & HL) >> 8) & 0xff]; break; case 0xa5: /* AND L */ AF = andTable[((AF >> 8) & HL) & 0xff]; break; case 0xa6: /* AND (HL) */ AF = andTable[((AF >> 8) & GET_BYTE(HL)) & 0xff]; break; case 0xa7: /* AND A */ AF = andTable[(AF >> 8) & 0xff]; break; case 0xa8: /* XOR B */ AF = xororTable[((AF ^ BC) >> 8) & 0xff]; break; case 0xa9: /* XOR C */ AF = xororTable[((AF >> 8) ^ BC) & 0xff]; break; case 0xaa: /* XOR D */ AF = xororTable[((AF ^ DE) >> 8) & 0xff]; break; case 0xab: /* XOR E */ AF = xororTable[((AF >> 8) ^ DE) & 0xff]; break; case 0xac: /* XOR H */ AF = xororTable[((AF ^ HL) >> 8) & 0xff]; break; case 0xad: /* XOR L */ AF = xororTable[((AF >> 8) ^ HL) & 0xff]; break; case 0xae: /* XOR (HL) */ AF = xororTable[((AF >> 8) ^ GET_BYTE(HL)) & 0xff]; break; case 0xaf: /* XOR A */ AF = 0x44; break; case 0xb0: /* OR B */ AF = xororTable[((AF | BC) >> 8) & 0xff]; break; case 0xb1: /* OR C */ AF = xororTable[((AF >> 8) | BC) & 0xff]; break; case 0xb2: /* OR D */ AF = xororTable[((AF | DE) >> 8) & 0xff]; break; case 0xb3: /* OR E */ AF = xororTable[((AF >> 8) | DE) & 0xff]; break; case 0xb4: /* OR H */ AF = xororTable[((AF | HL) >> 8) & 0xff]; break; case 0xb5: /* OR L */ AF = xororTable[((AF >> 8) | HL) & 0xff]; break; case 0xb6: /* OR (HL) */ AF = xororTable[((AF >> 8) | GET_BYTE(HL)) & 0xff]; break; case 0xb7: /* OR A */ AF = xororTable[(AF >> 8) & 0xff]; break; case 0xb8: /* CP B */ temp = HIGH_REGISTER(BC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xb9: /* CP C */ temp = LOW_REGISTER(BC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xba: /* CP D */ temp = HIGH_REGISTER(DE); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbb: /* CP E */ temp = LOW_REGISTER(DE); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbc: /* CP H */ temp = HIGH_REGISTER(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbd: /* CP L */ temp = LOW_REGISTER(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbe: /* CP (HL) */ temp = GET_BYTE(HL); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xbf: /* CP A */ SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | (chiptype == CHIP_TYPE_Z80 ? 0x42 : 0x46)); break; case 0xc0: /* RET NZ */ if (!(TSTFLAG(Z))) POP(PC); break; case 0xc1: /* POP BC */ POP(BC); break; case 0xc2: /* JP NZ,nnnn */ JPC(!TSTFLAG(Z)); break; case 0xc3: /* JP nnnn */ JPC(1); break; case 0xc4: /* CALL NZ,nnnn */ CALLC(!TSTFLAG(Z)); break; case 0xc5: /* PUSH BC */ PUSH(BC); break; case 0xc6: /* ADD A,nn */ temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu + temp; cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0xc7: /* RST 0 */ PUSH(PC); PC = 0; break; case 0xc8: /* RET Z */ if (TSTFLAG(Z)) POP(PC); break; case 0xc9: /* RET */ POP(PC); break; case 0xca: /* JP Z,nnnn */ JPC(TSTFLAG(Z)); break; case 0xcb: /* CB prefix */ CHECK_CPU_8080; adr = HL; switch ((op = GET_BYTE(PC)) & 7) { case 0: ++PC; acu = HIGH_REGISTER(BC); break; case 1: ++PC; acu = LOW_REGISTER(BC); break; case 2: ++PC; acu = HIGH_REGISTER(DE); break; case 3: ++PC; acu = LOW_REGISTER(DE); break; case 4: ++PC; acu = HIGH_REGISTER(HL); break; case 5: ++PC; acu = LOW_REGISTER(HL); break; case 6: ++PC; acu = GET_BYTE(adr); break; case 7: ++PC; acu = HIGH_REGISTER(AF); break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ switch (op & 0x38) { case 0x00:/* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg1; case 0x08:/* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg1; case 0x10:/* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg1; case 0x18:/* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg1; case 0x20:/* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg1; case 0x28:/* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg1; case 0x30:/* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg1; case 0x38:/* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg1: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; } break; case 0x40: /* BIT */ if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PUT_BYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xcc: /* CALL Z,nnnn */ CALLC(TSTFLAG(Z)); break; case 0xcd: /* CALL nnnn */ CALLC(1); break; case 0xce: /* ADC A,nn */ temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); break; case 0xcf: /* RST 8 */ PUSH(PC); PC = 8; break; case 0xd0: /* RET NC */ if (!(TSTFLAG(C))) POP(PC); break; case 0xd1: /* POP DE */ POP(DE); break; case 0xd2: /* JP NC,nnnn */ JPC(!TSTFLAG(C)); break; case 0xd3: /* OUT (nn),A */ out(RAM_PP(PC), HIGH_REGISTER(AF)); break; case 0xd4: /* CALL NC,nnnn */ CALLC(!TSTFLAG(C)); break; case 0xd5: /* PUSH DE */ PUSH(DE); break; case 0xd6: /* SUB nn */ temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0xd7: /* RST 10H */ PUSH(PC); PC = 0x10; break; case 0xd8: /* RET C */ if (TSTFLAG(C)) POP(PC); break; case 0xd9: /* EXX */ CHECK_CPU_8080; temp = BC; BC = BC1_S; BC1_S = temp; temp = DE; DE = DE1_S; DE1_S = temp; temp = HL; HL = HL1_S; HL1_S = temp; break; case 0xda: /* JP C,nnnn */ JPC(TSTFLAG(C)); break; case 0xdb: /* IN A,(nn) */ SET_HIGH_REGISTER(AF, in(RAM_PP(PC))); break; case 0xdc: /* CALL C,nnnn */ CALLC(TSTFLAG(C)); break; case 0xdd: /* DD prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x09: /* ADD IX,BC */ IX &= ADDRMASK; BC &= ADDRMASK; sum = IX + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; IX = sum; break; case 0x19: /* ADD IX,DE */ IX &= ADDRMASK; DE &= ADDRMASK; sum = IX + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; IX = sum; break; case 0x21: /* LD IX,nnnn */ IX = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),IX */ temp = GET_WORD(PC); PUT_WORD(temp, IX); PC += 2; break; case 0x23: /* INC IX */ ++IX; break; case 0x24: /* INC IXH */ IX += 0x100; AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; break; case 0x25: /* DEC IXH */ IX -= 0x100; AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; break; case 0x26: /* LD IXH,nn */ SET_HIGH_REGISTER(IX, RAM_PP(PC)); break; case 0x29: /* ADD IX,IX */ IX &= ADDRMASK; sum = IX + IX; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; IX = sum; break; case 0x2a: /* LD IX,(nnnn) */ temp = GET_WORD(PC); IX = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC IX */ --IX; break; case 0x2c: /* INC IXL */ temp = LOW_REGISTER(IX) + 1; SET_LOW_REGISTER(IX, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x2d: /* DEC IXL */ temp = LOW_REGISTER(IX) - 1; SET_LOW_REGISTER(IX, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x2e: /* LD IXL,nn */ SET_LOW_REGISTER(IX, RAM_PP(PC)); break; case 0x34: /* INC (IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr) + 1; PUT_BYTE(adr, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x35: /* DEC (IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr) - 1; PUT_BYTE(adr, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x36: /* LD (IX+dd),nn */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, RAM_PP(PC)); break; case 0x39: /* ADD IX,SP */ IX &= ADDRMASK; SP &= ADDRMASK; sum = IX + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; IX = sum; break; case 0x44: /* LD B,IXH */ SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); break; case 0x45: /* LD B,IXL */ SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); break; case 0x46: /* LD B,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_HIGH_REGISTER(BC, GET_BYTE(adr)); break; case 0x4c: /* LD C,IXH */ SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); break; case 0x4d: /* LD C,IXL */ SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); break; case 0x4e: /* LD C,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_LOW_REGISTER(BC, GET_BYTE(adr)); break; case 0x54: /* LD D,IXH */ SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); break; case 0x55: /* LD D,IXL */ SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); break; case 0x56: /* LD D,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_HIGH_REGISTER(DE, GET_BYTE(adr)); break; case 0x5c: /* LD E,IXH */ SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); break; case 0x5d: /* LD E,IXL */ SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); break; case 0x5e: /* LD E,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_LOW_REGISTER(DE, GET_BYTE(adr)); break; case 0x60: /* LD IXH,B */ SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); break; case 0x61: /* LD IXH,C */ SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); break; case 0x62: /* LD IXH,D */ SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); break; case 0x63: /* LD IXH,E */ SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); break; case 0x64: /* LD IXH,IXH */ break; case 0x65: /* LD IXH,IXL */ SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); break; case 0x66: /* LD H,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_HIGH_REGISTER(HL, GET_BYTE(adr)); break; case 0x67: /* LD IXH,A */ SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); break; case 0x68: /* LD IXL,B */ SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); break; case 0x69: /* LD IXL,C */ SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); break; case 0x6a: /* LD IXL,D */ SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); break; case 0x6b: /* LD IXL,E */ SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); break; case 0x6c: /* LD IXL,IXH */ SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); break; case 0x6d: /* LD IXL,IXL */ break; case 0x6e: /* LD L,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_LOW_REGISTER(HL, GET_BYTE(adr)); break; case 0x6f: /* LD IXL,A */ SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); break; case 0x70: /* LD (IX+dd),B */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(BC)); break; case 0x71: /* LD (IX+dd),C */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(BC)); break; case 0x72: /* LD (IX+dd),D */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(DE)); break; case 0x73: /* LD (IX+dd),E */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(DE)); break; case 0x74: /* LD (IX+dd),H */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(HL)); break; case 0x75: /* LD (IX+dd),L */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(HL)); break; case 0x77: /* LD (IX+dd),A */ adr = IX + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(AF)); break; case 0x7c: /* LD A,IXH */ SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); break; case 0x7d: /* LD A,IXL */ SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); break; case 0x7e: /* LD A,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); SET_HIGH_REGISTER(AF, GET_BYTE(adr)); break; case 0x84: /* ADD A,IXH */ temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x85: /* ADD A,IXL */ temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x86: /* ADD A,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8c: /* ADC A,IXH */ temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8d: /* ADC A,IXL */ temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8e: /* ADC A,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x96: /* SUB (IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x94: /* SUB IXH */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9c: /* SBC A,IXH */ temp = HIGH_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x95: /* SUB IXL */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9d: /* SBC A,IXL */ temp = LOW_REGISTER(IX); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x9e: /* SBC A,(IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xa4: /* AND IXH */ AF = andTable[((AF & IX) >> 8) & 0xff]; break; case 0xa5: /* AND IXL */ AF = andTable[((AF >> 8) & IX) & 0xff]; break; case 0xa6: /* AND (IX+dd) */ adr = IX + (int8) RAM_PP(PC); AF = andTable[((AF >> 8) & GET_BYTE(adr)) & 0xff]; break; case 0xac: /* XOR IXH */ AF = xororTable[((AF ^ IX) >> 8) & 0xff]; break; case 0xad: /* XOR IXL */ AF = xororTable[((AF >> 8) ^ IX) & 0xff]; break; case 0xae: /* XOR (IX+dd) */ adr = IX + (int8) RAM_PP(PC); AF = xororTable[((AF >> 8) ^ GET_BYTE(adr)) & 0xff]; break; case 0xb4: /* OR IXH */ AF = xororTable[((AF | IX) >> 8) & 0xff]; break; case 0xb5: /* OR IXL */ AF = xororTable[((AF >> 8) | IX) & 0xff]; break; case 0xb6: /* OR (IX+dd) */ adr = IX + (int8) RAM_PP(PC); AF = xororTable[((AF >> 8) | GET_BYTE(adr)) & 0xff]; break; case 0xbc: /* CP IXH */ temp = HIGH_REGISTER(IX); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbd: /* CP IXL */ temp = LOW_REGISTER(IX); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbe: /* CP (IX+dd) */ adr = IX + (int8) RAM_PP(PC); temp = GET_BYTE(adr); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xcb: /* CB prefix */ adr = IX + (int8) RAM_PP(PC); switch ((op = GET_BYTE(PC)) & 7) { case 0: ++PC; acu = HIGH_REGISTER(BC); break; case 1: ++PC; acu = LOW_REGISTER(BC); break; case 2: ++PC; acu = HIGH_REGISTER(DE); break; case 3: ++PC; acu = LOW_REGISTER(DE); break; case 4: ++PC; acu = HIGH_REGISTER(HL); break; case 5: ++PC; acu = LOW_REGISTER(HL); break; case 6: ++PC; acu = GET_BYTE(adr); break; case 7: ++PC; acu = HIGH_REGISTER(AF); break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ switch (op & 0x38) { case 0x00:/* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg2; case 0x08:/* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg2; case 0x10:/* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg2; case 0x18:/* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg2; case 0x20:/* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg2; case 0x28:/* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg2; case 0x30:/* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg2; case 0x38:/* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg2: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; } break; case 0x40: /* BIT */ if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PUT_BYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xe1: /* POP IX */ POP(IX); break; case 0xe3: /* EX (SP),IX */ temp = IX; POP(IX); PUSH(temp); break; case 0xe5: /* PUSH IX */ PUSH(IX); break; case 0xe9: /* JP (IX) */ PC = IX; break; case 0xf9: /* LD SP,IX */ SP = IX; break; default: /* ignore DD */ CHECK_CPU_Z80; PC--; } break; case 0xde: /* SBC A,nn */ temp = RAM_PP(PC); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); cbits = acu ^ temp ^ sum; AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); break; case 0xdf: /* RST 18H */ PUSH(PC); PC = 0x18; break; case 0xe0: /* RET PO */ if (!(TSTFLAG(P))) POP(PC); break; case 0xe1: /* POP HL */ POP(HL); break; case 0xe2: /* JP PO,nnnn */ JPC(!TSTFLAG(P)); break; case 0xe3: /* EX (SP),HL */ temp = HL; POP(HL); PUSH(temp); break; case 0xe4: /* CALL PO,nnnn */ CALLC(!TSTFLAG(P)); break; case 0xe5: /* PUSH HL */ PUSH(HL); break; case 0xe6: /* AND nn */ AF = andTable[((AF >> 8) & RAM_PP(PC)) & 0xff]; break; case 0xe7: /* RST 20H */ PUSH(PC); PC = 0x20; break; case 0xe8: /* RET PE */ if (TSTFLAG(P)) POP(PC); break; case 0xe9: /* JP (HL) */ PC = HL; break; case 0xea: /* JP PE,nnnn */ JPC(TSTFLAG(P)); break; case 0xeb: /* EX DE,HL */ temp = HL; HL = DE; DE = temp; break; case 0xec: /* CALL PE,nnnn */ CALLC(TSTFLAG(P)); break; case 0xed: /* ED prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x40: /* IN B,(C) */ temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(BC, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x41: /* OUT (C),B */ out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); break; case 0x42: /* SBC HL,BC */ HL &= ADDRMASK; BC &= ADDRMASK; sum = HL - BC - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x43: /* LD (nnnn),BC */ temp = GET_WORD(PC); PUT_WORD(temp, BC); PC += 2; break; case 0x44: /* NEG */ case 0x4C: /* NEG, unofficial */ case 0x54: /* NEG, unofficial */ case 0x5C: /* NEG, unofficial */ case 0x64: /* NEG, unofficial */ case 0x6C: /* NEG, unofficial */ case 0x74: /* NEG, unofficial */ case 0x7C: /* NEG, unofficial */ temp = HIGH_REGISTER(AF); AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; break; case 0x45: /* RETN */ case 0x55: /* RETN, unofficial */ case 0x5D: /* RETN, unofficial */ case 0x65: /* RETN, unofficial */ case 0x6D: /* RETN, unofficial */ case 0x75: /* RETN, unofficial */ case 0x7D: /* RETN, unofficial */ IFF_S |= IFF_S >> 1; POP(PC); break; case 0x46: /* IM 0 */ /* interrupt mode 0 */ break; case 0x47: /* LD I,A */ IR_S = (IR_S & 0xff) | (AF & ~0xff); break; case 0x48: /* IN C,(C) */ temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(BC, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x49: /* OUT (C),C */ out(LOW_REGISTER(BC), LOW_REGISTER(BC)); break; case 0x4a: /* ADC HL,BC */ HL &= ADDRMASK; BC &= ADDRMASK; sum = HL + BC + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; HL = sum; break; case 0x4b: /* LD BC,(nnnn) */ temp = GET_WORD(PC); BC = GET_WORD(temp); PC += 2; break; case 0x4d: /* RETI */ IFF_S |= IFF_S >> 1; POP(PC); break; case 0x4f: /* LD R,A */ IR_S = (IR_S & ~0xff) | ((AF >> 8) & 0xff); break; case 0x50: /* IN D,(C) */ temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(DE, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x51: /* OUT (C),D */ out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); break; case 0x52: /* SBC HL,DE */ HL &= ADDRMASK; DE &= ADDRMASK; sum = HL - DE - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x53: /* LD (nnnn),DE */ temp = GET_WORD(PC); PUT_WORD(temp, DE); PC += 2; break; case 0x56: /* IM 1 */ /* interrupt mode 1 */ break; case 0x57: /* LD A,I */ AF = (AF & 0x29) | (IR_S & ~0xff) | ((IR_S >> 8) & 0x80) | (((IR_S & ~0xff) == 0) << 6) | ((IFF_S & 2) << 1); break; case 0x58: /* IN E,(C) */ temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(DE, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x59: /* OUT (C),E */ out(LOW_REGISTER(BC), LOW_REGISTER(DE)); break; case 0x5a: /* ADC HL,DE */ HL &= ADDRMASK; DE &= ADDRMASK; sum = HL + DE + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; HL = sum; break; case 0x5b: /* LD DE,(nnnn) */ temp = GET_WORD(PC); DE = GET_WORD(temp); PC += 2; break; case 0x5e: /* IM 2 */ /* interrupt mode 2 */ break; case 0x5f: /* LD A,R */ AF = (AF & 0x29) | ((IR_S & 0xff) << 8) | (IR_S & 0x80) | (((IR_S & 0xff) == 0) << 6) | ((IFF_S & 2) << 1); break; case 0x60: /* IN H,(C) */ temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(HL, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x61: /* OUT (C),H */ out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); break; case 0x62: /* SBC HL,HL */ HL &= ADDRMASK; sum = HL - HL - TSTFLAG(C); AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80DupTable[(sum >> 8) & 0x1ff]; HL = sum; break; case 0x63: /* LD (nnnn),HL */ temp = GET_WORD(PC); PUT_WORD(temp, HL); PC += 2; break; case 0x67: /* RRD */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); PUT_BYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); break; case 0x68: /* IN L,(C) */ temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(HL, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x69: /* OUT (C),L */ out(LOW_REGISTER(BC), LOW_REGISTER(HL)); break; case 0x6a: /* ADC HL,HL */ HL &= ADDRMASK; sum = HL + HL + TSTFLAG(C); AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80DupTable[sum >> 8]; HL = sum; break; case 0x6b: /* LD HL,(nnnn) */ temp = GET_WORD(PC); HL = GET_WORD(temp); PC += 2; break; case 0x6f: /* RLD */ temp = GET_BYTE(HL); acu = HIGH_REGISTER(AF); PUT_BYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); break; case 0x70: /* IN (C) */ temp = in(LOW_REGISTER(BC)); SET_LOW_REGISTER(temp, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x71: /* OUT (C),0 */ out(LOW_REGISTER(BC), 0); break; case 0x72: /* SBC HL,SP */ HL &= ADDRMASK; SP &= ADDRMASK; sum = HL - SP - TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; HL = sum; break; case 0x73: /* LD (nnnn),SP */ temp = GET_WORD(PC); PUT_WORD(temp, SP); PC += 2; break; case 0x78: /* IN A,(C) */ temp = in(LOW_REGISTER(BC)); SET_HIGH_REGISTER(AF, temp); AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; break; case 0x79: /* OUT (C),A */ out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); break; case 0x7a: /* ADC HL,SP */ HL &= ADDRMASK; SP &= ADDRMASK; sum = HL + SP + TSTFLAG(C); AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; HL = sum; break; case 0x7b: /* LD SP,(nnnn) */ temp = GET_WORD(PC); SP = GET_WORD(temp); PC += 2; break; case 0xa0: /* LDI */ acu = RAM_PP(HL); PUT_BYTE_PP(DE, acu); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | (((--BC & ADDRMASK) != 0) << 2); break; case 0xa1: /* CPI */ acu = HIGH_REGISTER(AF); temp = RAM_PP(HL); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | ((--BC & ADDRMASK) != 0) << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. INI/INIR/IND/INDR use the C flag in stead of the L register. There is a catch though, because not the value of C is used, but C + 1 if it's INI/INIR or C - 1 if it's IND/INDR. So, first of all INI/INIR: HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ case 0xa2: /* INI */ acu = in(LOW_REGISTER(BC)); PUT_BYTE(HL, acu); ++HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L after the increment or decrement of HL; add the value written to the I/O port to; call that k for now. If k > 255, then the CF and HF flags are set. The PF flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. HF and CF Both set if ((HL) + L > 255) PF The parity of ((((HL) + L) & 7) xor B) */ case 0xa3: /* OUTI */ acu = GET_BYTE(HL); out(LOW_REGISTER(BC), acu); ++HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); break; case 0xa8: /* LDD */ acu = RAM_MM(HL); PUT_BYTE_MM(DE, acu); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | (((--BC & ADDRMASK) != 0) << 2); break; case 0xa9: /* CPD */ acu = HIGH_REGISTER(AF); temp = RAM_MM(HL); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | ((--BC & ADDRMASK) != 0) << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. NF flag A is copy of bit 7 of the value read from or written to an I/O port. INI/INIR/IND/INDR use the C flag in stead of the L register. There is a catch though, because not the value of C is used, but C + 1 if it's INI/INIR or C - 1 if it's IND/INDR. And last IND/INDR: HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ case 0xaa: /* IND */ acu = in(LOW_REGISTER(BC)); PUT_BYTE(HL, acu); --HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); break; case 0xab: /* OUTD */ acu = GET_BYTE(HL); out(LOW_REGISTER(BC), acu); --HL; temp = HIGH_REGISTER(BC); BC -= 0x100; INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); break; case 0xb0: /* LDIR */ acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { acu = RAM_PP(HL); PUT_BYTE_PP(DE, acu); } while (--BC); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); break; case 0xb1: /* CPIR */ acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { temp = RAM_PP(HL); op = --BC != 0; sum = acu - temp; } while (op && sum != 0); cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | op << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; case 0xb2: /* INIR */ temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { acu = in(LOW_REGISTER(BC)); PUT_BYTE(HL, acu); ++HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); break; case 0xb3: /* OTIR */ temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { acu = GET_BYTE(HL); out(LOW_REGISTER(BC), acu); ++HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO(LOW_REGISTER(HL)); break; case 0xb8: /* LDDR */ BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { acu = RAM_MM(HL); PUT_BYTE_MM(DE, acu); } while (--BC); acu += HIGH_REGISTER(AF); AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); break; case 0xb9: /* CPDR */ acu = HIGH_REGISTER(AF); BC &= ADDRMASK; if (BC == 0) BC = 0x10000; do { temp = RAM_MM(HL); op = --BC != 0; sum = acu - temp; } while (op && sum != 0); cbits = acu ^ temp ^ sum; AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | op << 2 | 2; if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; break; case 0xba: /* INDR */ temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { acu = in(LOW_REGISTER(BC)); PUT_BYTE(HL, acu); --HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); break; case 0xbb: /* OTDR */ temp = HIGH_REGISTER(BC); if (temp == 0) temp = 0x100; do { acu = GET_BYTE(HL); out(LOW_REGISTER(BC), acu); --HL; } while (--temp); temp = HIGH_REGISTER(BC); SET_HIGH_REGISTER(BC, 0); INOUTFLAGS_ZERO(LOW_REGISTER(HL)); break; default: /* ignore ED and following byte */ CHECK_CPU_Z80; } break; case 0xee: /* XOR nn */ AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; break; case 0xef: /* RST 28H */ PUSH(PC); PC = 0x28; break; case 0xf0: /* RET P */ if (!(TSTFLAG(S))) POP(PC); break; case 0xf1: /* POP AF */ POP(AF); break; case 0xf2: /* JP P,nnnn */ JPC(!TSTFLAG(S)); break; case 0xf3: /* DI */ IFF_S = 0; break; case 0xf4: /* CALL P,nnnn */ CALLC(!TSTFLAG(S)); break; case 0xf5: /* PUSH AF */ PUSH(AF); break; case 0xf6: /* OR nn */ AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; break; case 0xf7: /* RST 30H */ PUSH(PC); PC = 0x30; break; case 0xf8: /* RET M */ if (TSTFLAG(S)) POP(PC); break; case 0xf9: /* LD SP,HL */ SP = HL; break; case 0xfa: /* JP M,nnnn */ JPC(TSTFLAG(S)); break; case 0xfb: /* EI */ IFF_S = 3; break; case 0xfc: /* CALL M,nnnn */ CALLC(TSTFLAG(S)); break; case 0xfd: /* FD prefix */ CHECK_CPU_8080; switch (op = RAM_PP(PC)) { case 0x09: /* ADD IY,BC */ IY &= ADDRMASK; BC &= ADDRMASK; sum = IY + BC; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; IY = sum; break; case 0x19: /* ADD IY,DE */ IY &= ADDRMASK; DE &= ADDRMASK; sum = IY + DE; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; IY = sum; break; case 0x21: /* LD IY,nnnn */ IY = GET_WORD(PC); PC += 2; break; case 0x22: /* LD (nnnn),IY */ temp = GET_WORD(PC); PUT_WORD(temp, IY); PC += 2; break; case 0x23: /* INC IY */ ++IY; break; case 0x24: /* INC IYH */ IY += 0x100; AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; break; case 0x25: /* DEC IYH */ IY -= 0x100; AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; break; case 0x26: /* LD IYH,nn */ SET_HIGH_REGISTER(IY, RAM_PP(PC)); break; case 0x29: /* ADD IY,IY */ IY &= ADDRMASK; sum = IY + IY; AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; IY = sum; break; case 0x2a: /* LD IY,(nnnn) */ temp = GET_WORD(PC); IY = GET_WORD(temp); PC += 2; break; case 0x2b: /* DEC IY */ --IY; break; case 0x2c: /* INC IYL */ temp = LOW_REGISTER(IY) + 1; SET_LOW_REGISTER(IY, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x2d: /* DEC IYL */ temp = LOW_REGISTER(IY) - 1; SET_LOW_REGISTER(IY, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x2e: /* LD IYL,nn */ SET_LOW_REGISTER(IY, RAM_PP(PC)); break; case 0x34: /* INC (IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr) + 1; PUT_BYTE(adr, temp); AF = (AF & ~0xfe) | incZ80Table[temp]; break; case 0x35: /* DEC (IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr) - 1; PUT_BYTE(adr, temp); AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; break; case 0x36: /* LD (IY+dd),nn */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, RAM_PP(PC)); break; case 0x39: /* ADD IY,SP */ IY &= ADDRMASK; SP &= ADDRMASK; sum = IY + SP; AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; IY = sum; break; case 0x44: /* LD B,IYH */ SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); break; case 0x45: /* LD B,IYL */ SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); break; case 0x46: /* LD B,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_HIGH_REGISTER(BC, GET_BYTE(adr)); break; case 0x4c: /* LD C,IYH */ SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); break; case 0x4d: /* LD C,IYL */ SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); break; case 0x4e: /* LD C,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_LOW_REGISTER(BC, GET_BYTE(adr)); break; case 0x54: /* LD D,IYH */ SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); break; case 0x55: /* LD D,IYL */ SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); break; case 0x56: /* LD D,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_HIGH_REGISTER(DE, GET_BYTE(adr)); break; case 0x5c: /* LD E,IYH */ SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); break; case 0x5d: /* LD E,IYL */ SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); break; case 0x5e: /* LD E,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_LOW_REGISTER(DE, GET_BYTE(adr)); break; case 0x60: /* LD IYH,B */ SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); break; case 0x61: /* LD IYH,C */ SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); break; case 0x62: /* LD IYH,D */ SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); break; case 0x63: /* LD IYH,E */ SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); break; case 0x64: /* LD IYH,IYH */ break; case 0x65: /* LD IYH,IYL */ SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); break; case 0x66: /* LD H,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_HIGH_REGISTER(HL, GET_BYTE(adr)); break; case 0x67: /* LD IYH,A */ SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); break; case 0x68: /* LD IYL,B */ SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); break; case 0x69: /* LD IYL,C */ SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); break; case 0x6a: /* LD IYL,D */ SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); break; case 0x6b: /* LD IYL,E */ SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); break; case 0x6c: /* LD IYL,IYH */ SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); break; case 0x6d: /* LD IYL,IYL */ break; case 0x6e: /* LD L,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_LOW_REGISTER(HL, GET_BYTE(adr)); break; case 0x6f: /* LD IYL,A */ SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); break; case 0x70: /* LD (IY+dd),B */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(BC)); break; case 0x71: /* LD (IY+dd),C */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(BC)); break; case 0x72: /* LD (IY+dd),D */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(DE)); break; case 0x73: /* LD (IY+dd),E */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(DE)); break; case 0x74: /* LD (IY+dd),H */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(HL)); break; case 0x75: /* LD (IY+dd),L */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, LOW_REGISTER(HL)); break; case 0x77: /* LD (IY+dd),A */ adr = IY + (int8) RAM_PP(PC); PUT_BYTE(adr, HIGH_REGISTER(AF)); break; case 0x7c: /* LD A,IYH */ SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); break; case 0x7d: /* LD A,IYL */ SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); break; case 0x7e: /* LD A,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); SET_HIGH_REGISTER(AF, GET_BYTE(adr)); break; case 0x84: /* ADD A,IYH */ temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x85: /* ADD A,IYL */ temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x86: /* ADD A,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp; AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8c: /* ADC A,IYH */ temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8d: /* ADC A,IYL */ temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x8e: /* ADC A,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu + temp + TSTFLAG(C); AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; break; case 0x96: /* SUB (IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x94: /* SUB IYH */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9c: /* SBC A,IYH */ temp = HIGH_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x95: /* SUB IYL */ SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ case 0x9d: /* SBC A,IYL */ temp = LOW_REGISTER(IY); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0x9e: /* SBC A,(IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr); acu = HIGH_REGISTER(AF); sum = acu - temp - TSTFLAG(C); AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xa4: /* AND IYH */ AF = andTable[((AF & IY) >> 8) & 0xff]; break; case 0xa5: /* AND IYL */ AF = andTable[((AF >> 8) & IY) & 0xff]; break; case 0xa6: /* AND (IY+dd) */ adr = IY + (int8) RAM_PP(PC); AF = andTable[((AF >> 8) & GET_BYTE(adr)) & 0xff]; break; case 0xac: /* XOR IYH */ AF = xororTable[((AF ^ IY) >> 8) & 0xff]; break; case 0xad: /* XOR IYL */ AF = xororTable[((AF >> 8) ^ IY) & 0xff]; break; case 0xae: /* XOR (IY+dd) */ adr = IY + (int8) RAM_PP(PC); AF = xororTable[((AF >> 8) ^ GET_BYTE(adr)) & 0xff]; break; case 0xb4: /* OR IYH */ AF = xororTable[((AF | IY) >> 8) & 0xff]; break; case 0xb5: /* OR IYL */ AF = xororTable[((AF >> 8) | IY) & 0xff]; break; case 0xb6: /* OR (IY+dd) */ adr = IY + (int8) RAM_PP(PC); AF = xororTable[((AF >> 8) | GET_BYTE(adr)) & 0xff]; break; case 0xbc: /* CP IYH */ temp = HIGH_REGISTER(IY); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbd: /* CP IYL */ temp = LOW_REGISTER(IY); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xbe: /* CP (IY+dd) */ adr = IY + (int8) RAM_PP(PC); temp = GET_BYTE(adr); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; break; case 0xcb: /* CB prefix */ adr = IY + (int8) RAM_PP(PC); switch ((op = GET_BYTE(PC)) & 7) { case 0: ++PC; acu = HIGH_REGISTER(BC); break; case 1: ++PC; acu = LOW_REGISTER(BC); break; case 2: ++PC; acu = HIGH_REGISTER(DE); break; case 3: ++PC; acu = LOW_REGISTER(DE); break; case 4: ++PC; acu = HIGH_REGISTER(HL); break; case 5: ++PC; acu = LOW_REGISTER(HL); break; case 6: ++PC; acu = GET_BYTE(adr); break; case 7: ++PC; acu = HIGH_REGISTER(AF); break; } switch (op & 0xc0) { case 0x00: /* shift/rotate */ switch (op & 0x38) { case 0x00:/* RLC */ temp = (acu << 1) | (acu >> 7); cbits = temp & 1; goto cbshflg3; case 0x08:/* RRC */ temp = (acu >> 1) | (acu << 7); cbits = temp & 0x80; goto cbshflg3; case 0x10:/* RL */ temp = (acu << 1) | TSTFLAG(C); cbits = acu & 0x80; goto cbshflg3; case 0x18:/* RR */ temp = (acu >> 1) | (TSTFLAG(C) << 7); cbits = acu & 1; goto cbshflg3; case 0x20:/* SLA */ temp = acu << 1; cbits = acu & 0x80; goto cbshflg3; case 0x28:/* SRA */ temp = (acu >> 1) | (acu & 0x80); cbits = acu & 1; goto cbshflg3; case 0x30:/* SLIA */ temp = (acu << 1) | 1; cbits = acu & 0x80; goto cbshflg3; case 0x38:/* SRL */ temp = acu >> 1; cbits = acu & 1; cbshflg3: AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; } break; case 0x40: /* BIT */ if (acu & (1 << ((op >> 3) & 7))) AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); else AF = (AF & ~0xfe) | 0x54; if ((op & 7) != 6) AF |= (acu & 0x28); temp = acu; break; case 0x80: /* RES */ temp = acu & ~(1 << ((op >> 3) & 7)); break; case 0xc0: /* SET */ temp = acu | (1 << ((op >> 3) & 7)); break; } switch (op & 7) { case 0: SET_HIGH_REGISTER(BC, temp); break; case 1: SET_LOW_REGISTER(BC, temp); break; case 2: SET_HIGH_REGISTER(DE, temp); break; case 3: SET_LOW_REGISTER(DE, temp); break; case 4: SET_HIGH_REGISTER(HL, temp); break; case 5: SET_LOW_REGISTER(HL, temp); break; case 6: PUT_BYTE(adr, temp); break; case 7: SET_HIGH_REGISTER(AF, temp); break; } break; case 0xe1: /* POP IY */ POP(IY); break; case 0xe3: /* EX (SP),IY */ temp = IY; POP(IY); PUSH(temp); break; case 0xe5: /* PUSH IY */ PUSH(IY); break; case 0xe9: /* JP (IY) */ PC = IY; break; case 0xf9: /* LD SP,IY */ SP = IY; break; default: /* ignore FD */ CHECK_CPU_Z80; PC--; } break; case 0xfe: /* CP nn */ temp = RAM_PP(PC); AF = (AF & ~0x28) | (temp & 0x28); acu = HIGH_REGISTER(AF); sum = acu - temp; cbits = acu ^ temp ^ sum; AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | (SET_PV) | cbits2Table[cbits & 0x1ff]; break; case 0xff: /* RST 38H */ PUSH(PC); PC = 0x38; } } end_decode: /* simulation halted */ PC_S = (reason == STOP_OPCODE) ? PCX : PC; AF_S = AF; BC_S = BC; DE_S = DE; HL_S = HL; IX_S = IX; IY_S = IY; SP_S = SP; return reason; } simh-3.8.1/AltairZ80/vfdhd.c0000644000175000017500000006374611051674724013645 0ustar vlmvlm/************************************************************************* * * * $Id: vfdhd.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Micropolis FDC module for SIMH * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG */ #define USE_VGI /* Use 275-byte VGI-format sectors (includes all metadata) */ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #include "sim_imd.h" /* #define DBG_MSG */ #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define ORDERS_MSG (1 << 7) static void VFDHD_Command(void); #define VFDHD_MAX_DRIVES 4 #define VFDHD_SECTOR_LEN 275 #define VFDHD_RAW_LEN (40 + VFDHD_SECTOR_LEN + 128) typedef union { struct { uint8 preamble[40]; /* Hard disk uses 30 bytes of preamble, floppy uses 40. */ uint8 sync; uint8 header[2]; uint8 unused[10]; uint8 data[256]; uint8 checksum; uint8 ecc[4]; uint8 ecc_valid; /* 0xAA indicates ECC is being used. */ uint8 postamble[128]; } u; uint8 raw[VFDHD_RAW_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; DISK_INFO *imd; uint16 ntracks; /* number of tracks */ uint8 nheads; /* number of heads */ uint8 nspt; /* number of sectors per track */ uint8 npre_len; /* preamble length */ uint32 sectsize; /* sector size, not including pre/postamble */ uint16 track; uint8 wp; /* Disk write protected */ uint8 ready; /* Drive is ready */ uint8 write_fault; uint8 seek_complete; uint8 sync_lost; uint32 sector_wait_count; } VFDHD_DRIVE_INFO; typedef struct { PNP_INFO pnp; /* Plug and Play */ uint8 xfr_flag; /* Indicates controller is ready to send/receive data */ uint8 sel_drive; /* Currently selected drive */ uint8 selected; /* 1 if drive is selected */ uint8 track0; /* Set it selected drive is on track 0 */ uint8 head; /* Currently selected head */ uint8 wr_latch; /* Write enable latch */ uint8 int_enable; /* Interrupt Enable */ uint32 datacount; /* Number of data bytes transferred from controller for current sector */ uint8 step; uint8 direction; uint8 rwc; uint8 sector; uint8 read; uint8 ecc_enable; uint8 precomp; uint8 floppy_sel; uint8 controller_busy; uint8 motor_on; uint8 hdsk_type; VFDHD_DRIVE_INFO drive[VFDHD_MAX_DRIVES]; } VFDHD_INFO; static VFDHD_INFO vfdhd_info_data = { { 0x0, 0, 0xC0, 4 } }; static VFDHD_INFO *vfdhd_info = &vfdhd_info_data; static SECTOR_FORMAT sdata; extern uint32 PCX; extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); #define UNIT_V_VFDHD_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_VFDHD_WLK (1 << UNIT_V_VFDHD_WLK) #define UNIT_V_VFDHD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_VFDHD_VERBOSE (1 << UNIT_V_VFDHD_VERBOSE) #define VFDHD_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ static t_stat vfdhd_reset(DEVICE *vfdhd_dev); static t_stat vfdhd_attach(UNIT *uptr, char *cptr); static t_stat vfdhd_detach(UNIT *uptr); static int32 vfdhddev(const int32 port, const int32 io, const int32 data); static uint8 VFDHD_Read(const uint32 Addr); static uint8 VFDHD_Write(const uint32 Addr, uint8 cData); static int32 hdSize = 5; static UNIT vfdhd_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) } }; static REG vfdhd_reg[] = { { DRDATA (HDSIZE, hdSize, 10), }, { NULL } }; static MTAB vfdhd_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_VFDHD_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_VFDHD_WLK, UNIT_VFDHD_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_VFDHD_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_VFDHD_VERBOSE, UNIT_VFDHD_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(vfdhd_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB vfdhd_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "ORDERS", ORDERS_MSG }, { NULL, 0 } }; DEVICE vfdhd_dev = { "VFDHD", vfdhd_unit, vfdhd_reg, vfdhd_mod, VFDHD_MAX_DRIVES, 10, 31, 1, VFDHD_MAX_DRIVES, VFDHD_MAX_DRIVES, NULL, NULL, &vfdhd_reset, NULL, &vfdhd_attach, &vfdhd_detach, &vfdhd_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, vfdhd_dt, NULL, "Vector Graphic FD-HD Controller VFDHD" }; /* Reset routine */ static t_stat vfdhd_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &vfdhddev, TRUE); } else { /* Connect MFDC at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &vfdhddev, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); return SCPE_ARG; } } return SCPE_OK; } /* Attach routine */ static t_stat vfdhd_attach(UNIT *uptr, char *cptr) { char header[4]; t_stat r; unsigned int i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if ( r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ uptr->capac = sim_fsize(uptr->fileref); for(i = 0; i < VFDHD_MAX_DRIVES; i++) { vfdhd_info->drive[i].uptr = &vfdhd_dev.units[i]; } for(i = 0; i < VFDHD_MAX_DRIVES; i++) { if(vfdhd_dev.units[i].fileref == uptr->fileref) { break; } } if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "IMD")) { uptr->u3 = IMAGE_TYPE_IMD; } else if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; vfdhd_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } else { /* creating file, must be DSK format. */ uptr->u3 = IMAGE_TYPE_DSK; } if (uptr->flags & UNIT_VFDHD_VERBOSE) printf("VFDHD%d: attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); if(uptr->u3 == IMAGE_TYPE_IMD) { if(uptr->capac < 318000) { printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); vfdhd_detach(uptr); return SCPE_OPENERR; } if (uptr->flags & UNIT_VFDHD_VERBOSE) printf("--------------------------------------------------------\n"); vfdhd_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_VFDHD_VERBOSE)); if (uptr->flags & UNIT_VFDHD_VERBOSE) printf("\n"); } else { vfdhd_info->drive[i].imd = NULL; } if(i>0) { /* Floppy Disk, Unit 1-3 */ vfdhd_info->drive[i].ntracks = 77; /* number of tracks */ vfdhd_info->drive[i].nheads = 2; /* number of heads */ vfdhd_info->drive[i].nspt = 16; /* number of sectors per track */ vfdhd_info->drive[i].npre_len = 40; /* preamble length */ vfdhd_info->drive[i].sectsize = VFDHD_SECTOR_LEN; /* sector size, not including pre/postamble */ } else { /* Hard Disk, Unit 0 */ if(hdSize == 10) { vfdhd_info->drive[i].ntracks = 153; /* number of tracks */ vfdhd_info->drive[i].nheads = 6; /* number of heads */ vfdhd_info->hdsk_type = 1; printf("10MB\n"); } else if (hdSize == 5) { vfdhd_info->drive[i].ntracks = 153; /* number of tracks */ vfdhd_info->drive[i].nheads = 4; /* number of heads */ vfdhd_info->hdsk_type = 0; printf("5MB\n"); } else { vfdhd_info->drive[i].ntracks = 512; /* number of tracks */ vfdhd_info->drive[i].nheads = 8; /* number of heads */ vfdhd_info->hdsk_type = 1; printf("32MB\n"); } vfdhd_info->drive[i].nheads = 4; /* number of heads */ vfdhd_info->drive[i].nspt = 32; /* number of sectors per track */ vfdhd_info->drive[i].npre_len = 30; /* preamble length */ vfdhd_info->drive[i].sectsize = VFDHD_SECTOR_LEN; /* sector size, not including pre/postamble */ vfdhd_info->drive[i].ready = 1; vfdhd_info->drive[i].seek_complete = 1; vfdhd_info->drive[i].sync_lost = 1; /* Active LOW */ } vfdhd_info->motor_on = 1; return SCPE_OK; } /* Detach routine */ static t_stat vfdhd_detach(UNIT *uptr) { t_stat r; int8 i; for(i = 0; i < VFDHD_MAX_DRIVES; i++) { if(vfdhd_dev.units[i].fileref == uptr->fileref) { break; } } DBG_PRINT(("Detach VFDHD%d\n", i)); r = diskClose(&vfdhd_info->drive[i].imd); if (r != SCPE_OK) return r; r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) return r; return SCPE_OK; } static uint8 cy; static uint8 adc(uint8 sum, uint8 a1) { uint32 total; total = sum + a1 + cy; if(total > 0xFF) { cy = 1; } else { cy = 0; } return(total & 0xFF); } static int32 vfdhddev(const int32 port, const int32 io, const int32 data) { DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); if(io) { VFDHD_Write(port, data); return 0; } else { return(VFDHD_Read(port)); } } #define FDHD_CTRL_STATUS0 0 /* R=Status Port 0, W=Control Port 0 */ #define FDHD_CTRL_STATUS1 1 /* R=Status Port 1, W=Control Port 0 */ #define FDHD_DATA 2 /* R/W=Data Port */ #define FDHD_RESET_START 3 /* R=RESET, W=START */ static uint8 VFDHD_Read(const uint32 Addr) { uint8 cData; VFDHD_DRIVE_INFO *pDrive; pDrive = &vfdhd_info->drive[vfdhd_info->sel_drive]; cData = 0x00; switch(Addr & 0x3) { case FDHD_CTRL_STATUS0: cData = (pDrive->wp & 1); /* [0] Write Protect (FD) */ cData |= (pDrive->ready & 1) << 1; /* [1] Drive ready (HD) */ cData |= (pDrive->track == 0) ? 0x04 : 0; /* [2] TK0 (FD/HD) */ cData |= (pDrive->write_fault & 1) << 3; /* [3] Write Fault (HD) */ cData |= (pDrive->seek_complete & 1) << 4; /* [4] Seek Complete (HD) */ cData |= (pDrive->sync_lost & 1) << 5; /* [5] Loss of Sync (HD) */ cData |= 0xC0; /* [7:6] Reserved (pulled up) */ TRACE_PRINT(STATUS_MSG, ("VFDHD: " ADDRESS_FORMAT " RD S0 = 0x%02x" NLP, PCX, cData)); break; case FDHD_CTRL_STATUS1: vfdhd_info->floppy_sel = (vfdhd_info->sel_drive == 0) ? 0 : 1; cData = (vfdhd_info->floppy_sel & 0x1); /* [0] Floppy Selected */ cData |= (vfdhd_info->controller_busy & 0x1) << 1; /* [1] Controller busy */ cData |= (vfdhd_info->motor_on & 0x1) << 2; /* [2] Motor On (FD) */ cData |= (vfdhd_info->hdsk_type & 0x1) << 3; /* [3] Hard Disk Type (0=5MB, 1=10MB) */ cData |= 0xF0; /* [7:4] Reserved (pulled up) */ if(vfdhd_info->sel_drive == 0) { /* cData &= 0xF0; */ } vfdhd_info->controller_busy = 0; TRACE_PRINT(STATUS_MSG, ("VFDHD: " ADDRESS_FORMAT " RD S1 = 0x%02x" NLP, PCX, cData)); break; case FDHD_DATA: /* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data" NLP, PCX)); */ if(vfdhd_info->datacount+40 >= VFDHD_RAW_LEN) { TRACE_PRINT(ERROR_MSG, ("VFDHD: " ADDRESS_FORMAT " Illegal data count %d." NLP, PCX, vfdhd_info->datacount)); vfdhd_info->datacount = 0; } cData = sdata.raw[vfdhd_info->datacount+40]; vfdhd_info->datacount++; /* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data Sector %d[%03d]: 0x%02x" NLP, PCX, pDrive->sector, vfdhd_info->datacount, cData)); */ break; case FDHD_RESET_START: /* Reset */ TRACE_PRINT(CMD_MSG, ("VFDHD: " ADDRESS_FORMAT " Reset" NLP, PCX)); vfdhd_info->datacount = 0; cData = 0xFF; /* Return High-Z data */ break; } return (cData); } static uint8 VFDHD_Write(const uint32 Addr, uint8 cData) { VFDHD_DRIVE_INFO *pDrive; pDrive = &vfdhd_info->drive[vfdhd_info->sel_drive]; switch(Addr & 0x3) { case FDHD_CTRL_STATUS0: vfdhd_info->sel_drive = cData & 0x03; vfdhd_info->head = (cData >> 2) & 0x7; vfdhd_info->step = (cData >> 5) & 1; vfdhd_info->direction = (cData >> 6) & 1; vfdhd_info->rwc = (cData >> 7) & 1; TRACE_PRINT(WR_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " WR C0=%02x: sel_drive=%d, head=%d, step=%d, dir=%d, rwc=%d" NLP, PCX, cData, vfdhd_info->sel_drive, vfdhd_info->head, vfdhd_info->step, vfdhd_info->direction, vfdhd_info->rwc)); if(vfdhd_info->step == 1) { if(vfdhd_info->direction == 1) { /* Step IN */ pDrive->track++; } else { /* Step OUT */ if(pDrive->track != 0) { pDrive->track--; } } TRACE_PRINT(SEEK_MSG, ("VFDHD: " ADDRESS_FORMAT " Drive %d on track %d" NLP, PCX, vfdhd_info->sel_drive, pDrive->track)); } break; case FDHD_CTRL_STATUS1: vfdhd_info->sector = (cData & 0x1f); vfdhd_info->read = (cData >> 5) & 1; vfdhd_info->ecc_enable = (cData >> 6) & 1; vfdhd_info->precomp = (cData >> 7) & 1; if(cData == 0xFF) { TRACE_PRINT(SEEK_MSG, ("VFDHD: " ADDRESS_FORMAT " Home Disk %d" NLP, PCX, vfdhd_info->sel_drive)); pDrive->track = 0; } DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR C1=%02x: sector=%d, read=%d, ecc_en=%d, precomp=%d" NLP, PCX, cData, vfdhd_info->sector, vfdhd_info->read, vfdhd_info->ecc_enable, vfdhd_info->precomp)); break; case FDHD_DATA: /* Data Port */ DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR Data" NLP, PCX)); #ifdef USE_VGI if(vfdhd_info->sel_drive > 0) { /* Floppy */ if(vfdhd_info->datacount >= VFDHD_RAW_LEN) { TRACE_PRINT(ERROR_MSG, ("VFDHD: " ADDRESS_FORMAT " Illegal data count %d." NLP, PCX, vfdhd_info->datacount)); vfdhd_info->datacount = 0; } sdata.raw[vfdhd_info->datacount] = cData; } else { /* Hard */ if(vfdhd_info->datacount+10 >= VFDHD_RAW_LEN) { TRACE_PRINT(ERROR_MSG, ("VFDHD: " ADDRESS_FORMAT " Illegal data count %d." NLP, PCX, vfdhd_info->datacount)); vfdhd_info->datacount = 0; } sdata.raw[vfdhd_info->datacount+10] = cData; } #else if((vfdhd_info->datacount-13 >= VFDHD_RAW_LEN) || (vfdhd_info->datacount < 13)) { TRACE_PRINT(ERROR_MSG, ("VFDHD: " ADDRESS_FORMAT " Illegal data count %d." NLP, PCX, vfdhd_info->datacount)); vfdhd_info->datacount = 13; } sdata.u.data[vfdhd_info->datacount-13] = cData; #endif /* USE_VGI */ vfdhd_info->datacount ++; break; case FDHD_RESET_START: TRACE_PRINT(CMD_MSG, ("VFDHD: " ADDRESS_FORMAT " Start Command" NLP, PCX)); VFDHD_Command(); break; } cData = 0x00; return (cData); } static void VFDHD_Command(void) { VFDHD_DRIVE_INFO *pDrive; uint32 bytesPerTrack; uint32 bytesPerHead; uint32 sec_offset; uint32 flags; pDrive = &(vfdhd_info->drive[vfdhd_info->sel_drive]); bytesPerTrack = pDrive->sectsize * pDrive->nspt; bytesPerHead = bytesPerTrack * pDrive->ntracks; sec_offset = (pDrive->track * bytesPerTrack) + \ (vfdhd_info->head * bytesPerHead) + \ (vfdhd_info->sector * pDrive->sectsize); vfdhd_info->controller_busy = 1; if(vfdhd_info->read == 1) { /* Perform a Read operation */ unsigned int i, checksum; unsigned int readlen; TRACE_PRINT(RD_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " RD: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, PCX, vfdhd_info->sel_drive, pDrive->track, vfdhd_info->head, vfdhd_info->sector)); /* Clear out unused portion of sector. */ memset(&sdata.u.unused[0], 0x00, 10); sdata.u.sync = 0xFF; sdata.u.header[0] = pDrive->track & 0xFF; sdata.u.header[1] = vfdhd_info->sector; switch((pDrive->uptr)->u3) { case IMAGE_TYPE_IMD: if(pDrive->imd == NULL) { printf(".imd is NULL!" NLP); } printf("%s: Read: imd=%p" NLP, __FUNCTION__, pDrive->imd); sectRead(pDrive->imd, pDrive->track, vfdhd_info->head, vfdhd_info->sector, sdata.u.data, 256, &flags, &readlen); adc(0,0); /* clear Carry bit */ checksum = 0; /* Checksum everything except the sync byte */ for(i=1;i<269;i++) { checksum = adc(checksum, sdata.raw[i+40]); } sdata.u.checksum = checksum & 0xFF; sdata.u.ecc_valid = 0xAA; break; case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); fread(&sdata.u.sync, 274, /*VFDHD_SECTOR_LEN,*/ 1, (pDrive->uptr)->fileref); memset(&sdata.u.preamble, 0, 40); memset(&sdata.u.ecc, 0, 5); /* Clear out the ECC and ECC Valid bytes */ sdata.u.ecc_valid = 0xAA; for(vfdhd_info->datacount = 0; sdata.raw[vfdhd_info->datacount] == 0x00; vfdhd_info->datacount++) { } DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " READ: Sync found at offset %d" NLP, PCX, vfdhd_info->datacount)); } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } } else { /* Perform a Write operation */ unsigned int writelen; TRACE_PRINT(WR_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " WR: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, PCX, vfdhd_info->sel_drive, pDrive->track, vfdhd_info->head, vfdhd_info->sector)); #ifdef USE_VGI #else int data_index = vfdhd_info->datacount - 13; sec_offset = (pDrive->track * 4096) + \ (vfdhd_info->head * 315392) + \ (vfdhd_info->sector * 256); #endif /* USE_VGI */ switch((pDrive->uptr)->u3) { case IMAGE_TYPE_IMD: if(pDrive->imd == NULL) { printf(".imd is NULL!" NLP); } sectWrite(pDrive->imd, pDrive->track, vfdhd_info->head, vfdhd_info->sector, sdata.u.data, 256, &flags, &writelen); break; case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR drive=%d, track=%d, head=%d, sector=%d" NLP, PCX, vfdhd_info->sel_drive, pDrive->track, vfdhd_info->head, vfdhd_info->sector)); sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); #ifdef USE_VGI fwrite(&sdata.u.sync, VFDHD_SECTOR_LEN, 1, (pDrive->uptr)->fileref); #else fwrite(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); #endif /* USE_VGI */ } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } } } simh-3.8.1/AltairZ80/s100_fif.c0000644000175000017500000003544511111141264014035 0ustar vlmvlm/* $Id: s100_fif.c 1995 2008-07-15 03:59:13Z hharte $ IMSAI FIF Disk Controller by Ernie Price Based on altairz80_dsk.c, Copyright (c) 2002-2008, Peter Schorn Plug-n-Play added by 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. */ #include "altairz80_defs.h" #define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) #define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE) #define DSK_SECTSIZE 137 /* size of sector */ #define DSK_SECT 32 /* sectors per track */ #define MAX_TRACKS 254 /* number of tracks, original Altair has 77 tracks only */ #define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT) #define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS) static t_stat fif_reset(DEVICE *dptr); static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc); static int32 fif_io(const int32 port, const int32 io, const int32 data); extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern uint8 GetBYTEWrapper(const uint32 Addr); extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); extern uint32 PCX; /* global data on status */ /* currently selected drive (values are 0 .. NUM_OF_DSK) current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */ static int32 current_disk = NUM_OF_DSK; static int32 warnLevelDSK = 3; static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; static int32 warnDSK11 = 0; /* 88DSK Standard I/O Data Structures */ typedef struct { PNP_INFO pnp; /* Plug and Play */ } FIF_INFO; FIF_INFO fif_info_data = { { 0x0000, 0, 0xFD, 1 } }; FIF_INFO *fif_info = &fif_info_data; static UNIT fif_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } }; static REG fif_reg[] = { { DRDATA (DISK, current_disk, 4) }, { DRDATA (DSKWL, warnLevelDSK, 32) }, { BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, { DRDATA (WARNDSK11, warnDSK11, 4), REG_RO }, { NULL } }; static MTAB fif_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &fif_set_verbose }, { 0 } }; DEVICE fif_dev = { "FIF", fif_unit, fif_reg, fif_mod, 8, 10, 31, 1, 8, 8, NULL, NULL, &fif_reset, NULL, NULL, NULL, &fif_info_data, (DEV_DISABLE | DEV_DIS), 0, NULL, NULL, "IMSAI FIF" }; static void resetDSKWarningFlags(void) { int32 i; for (i = 0; i < NUM_OF_DSK; i++) { warnLock[i] = 0; warnAttached[i] = 0; } warnDSK11 = 0; } static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) { resetDSKWarningFlags(); return SCPE_OK; } /* returns TRUE iff there exists a disk with VERBOSE */ static int32 hasVerbose(void) { int32 i; for (i = 0; i < NUM_OF_DSK; i++) { if (((fif_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) { return TRUE; } } return FALSE; } /* service routines to handle simulator functions */ /* Reset routine */ static t_stat fif_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; resetDSKWarningFlags(); current_disk = NUM_OF_DSK; if(dptr->flags & DEV_DIS) { sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, TRUE); } else { /* Connect HDSK at base address */ if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, FALSE) != 0) { printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); dptr->flags |= DEV_DIS; return SCPE_ARG; } } return SCPE_OK; } typedef struct desc_t { uint8 cmd_unit, /* (cmd << 4) | unit : 1 = A: */ result, /* result: 0 == busy, 1 = normal completion, */ nn, /* number of secs ? */ track, /* track */ sector, /* sector */ addr_l, /* low (transfer address) */ addr_h; /* high (transfer address) */ } desc_t; static desc_t mydesc; enum {NONE, WRITE_SEC, READ_SEC, FMT_TRACK}; #define SEC_SZ 128 #define SPT 26 #define UMASK 0xf static uint8 blanksec[SEC_SZ]; /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ static const uint8 utrans[] = {0,1,2,0,3,0,0,0,4,0,0,0,0,0,0,0}; /************************************************** Translate an IMSAI FIF disk request into an access into the harddrive file */ static int DoDiskOperation(desc_t *dsc, uint8 val) { int32 current_disk_flags; int kt, addr; FILE *cpx; UNIT *uptr; #if 0 printf("%02x %02x %02x %02x %02x %02x %02x %02x \n", val, dsc->cmd_unit, dsc->result, dsc->nn, dsc->track, dsc->sector, dsc->addr_l, dsc->addr_h); #endif current_disk = (utrans[dsc->cmd_unit & UMASK]) - 1; /* 0 <= current_disk < NUM_OF_DSK */ if (current_disk >= NUM_OF_DSK) { if (hasVerbose() && (warnDSK11 < warnLevelDSK)) { warnDSK11++; /*03*/ printf("FIF%i: " ADDRESS_FORMAT " Attempt disk io on illegal disk %d - ignored." NLP, current_disk, PCX, current_disk); } return 0; /* no drive selected - can do nothing */ } current_disk_flags = (fif_dev.units + current_disk) -> flags; if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */ if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) { warnAttached[current_disk]++; /*02*/printf("FIF%i: " ADDRESS_FORMAT " Attempt to select unattached FIF%d - ignored." NLP, current_disk, PCX, current_disk); } current_disk = NUM_OF_DSK; return 2; } uptr = fif_dev.units + current_disk; cpx = uptr->fileref; /* decode request: */ switch (dsc->cmd_unit >> 4) { case FMT_TRACK: /*printf("%c", dsc->track % 10 ? '*' : '0' + + dsc->track / 10); */ /*Sleep(250); */ memset(blanksec, 0, SEC_SZ); addr = dsc->track * SPT; sim_fseek(cpx, addr * SEC_SZ, SEEK_SET); /* write a track worth of sectors */ for (kt=0; kt < SPT; kt++) { fwrite(blanksec, 1, sizeof(blanksec), cpx); } break; case READ_SEC: addr = (dsc->track * SPT) + dsc->sector - 1; sim_fseek(cpx, addr * SEC_SZ, SEEK_SET); fread(blanksec, 1, SEC_SZ, cpx); addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */ for (kt = 0; kt < SEC_SZ; kt++) { PutBYTEWrapper(addr++, blanksec[kt]); } break; case WRITE_SEC: addr = (dsc->track * SPT) + dsc->sector - 1; sim_fseek(cpx, addr * SEC_SZ, SEEK_SET); addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */ for (kt = 0; kt < SEC_SZ; kt++) { blanksec[kt] = GetBYTEWrapper(addr++); } fwrite(blanksec, 1, SEC_SZ, cpx); break; default: ; } return 1; } /********************************************************************** Copy the disk descriptor from target RAM */ static void getdesc(uint16 addr) { uint32 x; uint8 *p1 = (uint8*)&mydesc; for (x = 0; x < sizeof(mydesc); x++) { *p1++ = GetBYTEWrapper(addr++); } } /********************************************************************** handle the IMSAI FIF floppy controller */ static int32 fif_io(const int32 port, const int32 io, const int32 data) { static int32 fdstate = 0; /* chan 0xfd state */ static int32 desc; static uint16 fdAdr[16]; /* disk descriptor address in 8080/z80 RAM */ /* cmd | desc# */ /* cmd == 0x00 do operation */ /* cmd == 0x10 next 2 transfers are desc address */ /* desc# is one of 16 0x0 - 0xf */ if (!io) { return 0; } switch (fdstate) { case 0: desc = data & 0xf; if ((data & 0x10) != 0) { /* prefix 0x10 */ fdstate++; /* means desc address is next 2 out (fd),a */ } else { /* do what descriptor says */ getdesc(fdAdr[desc]); PutBYTEWrapper(fdAdr[desc] + 1, (uint8)DoDiskOperation(&mydesc, (uint8)data)); } break; case 1: /*printf("D1 %02x %02x\n", desc, data); */ fdAdr[desc] = data; /* LSB of descriptor address */ fdstate++; break; case 2: /*printf("D2 %02x %02x\n", desc, data); */ fdAdr[desc] |= data << 8; /* MSB of descriptor address */ fdstate = 0; break; } return 0; } #define ERNIES_FTP 0 #if ERNIES_FTP #define WRK_BUF_SZ 150 #define FCB_SIZE 32 #define NAME_LTH 8 #define EXT_LTH 3 /************************************************** */ static void xfero(int32 addr, char *src, int32 lth) { while (lth--) { PutBYTEWrapper(addr++, *src++); } } /************************************************** */ static void xferi(int32 addr, char *dst, int32 lth) { while (lth--) { *dst++ = GetBYTEWrapper(addr++); } } #if !defined (_WIN32) static void strupr(char *fn) { while (*fn) { if (('a' <= *fn) && (*fn <= 'z')) *fn -= 'a' - 'A'; fn++; } } #endif /************************************************** */ static void initfcb(char *fcb, char *fn, int32 flg) { char *p1 = fcb; if (flg) { strupr(fn); } memset (fcb, 0 , FCB_SIZE); memset (fcb + 1, ' ', NAME_LTH + EXT_LTH); p1++; while (*fn && (*fn != '.')) { *p1++ = *fn++; } if (*fn == '.') { fn++; } p1 = fcb + NAME_LTH + 1; while (*fn && (*fn != '.')) { *p1++ = *fn++; } } /************************************************** FTP interface - most of the work is done here The IMDOS/CPM application only does minimal work */ char message[WRK_BUF_SZ]; char temp [WRK_BUF_SZ]; FILE * myfile; uint8 FTP(int32 BC, int32 DE) { char *p1, *p2; int32 retval; xferi(DE, temp, SEC_SZ); p1 = temp; switch (BC & 0x7f) { case 0: memcpy(message, p1 + 2, *(p1 + 1)); *(message + *(p1 + 1)) = 0; p2 = strtok(message, " \t"); if (!strcmp(p2, "get")) { p2 = strtok(NULL, " \t"); if (myfile = fopen(p2, "rb")) { initfcb(temp, p2, 1); xfero(DE + 2, temp, 32); retval = 0; break; } } if (!strcmp(p2, "era")) { p2 = strtok(NULL, " \t"); initfcb(temp, p2, 0); xfero(DE + 2, temp, 32); retval = 1; break; } retval = 0xff; break; case 20: memset(temp, 0x1a, SEC_SZ); retval = fread(temp, 1, SEC_SZ, myfile) ? 0 : 1; xfero( DE, temp, SEC_SZ); if (retval) { fclose(myfile); } break; } return retval; } #endif /* ERNIES_FTP */ /* end of the source */ simh-3.8.1/AltairZ80/s100_mdsad.c0000644000175000017500000010203611111141262014346 0ustar vlmvlm/************************************************************************* * * * $Id: s100_mdsad.c 1995 2008-07-15 03:59:13Z hharte $ * * * * Copyright (c) 2007-2008 Howard M. Harte. * * http://www.hartetec.com * * * * 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 * * Howard M. Harte. * * * * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * * * * Module Description: * * Northstar MDS-AD Disk Controller module for SIMH * * Only Double-Density is supported for now. * * * * Environment: * * User mode only * * * *************************************************************************/ /*#define DBG_MSG*/ #include "altairz80_defs.h" #if defined (_WIN32) #include #endif #ifdef DBG_MSG #define DBG_PRINT(args) printf args #else #define DBG_PRINT(args) #endif /* Debug flags */ #define ERROR_MSG (1 << 0) #define SEEK_MSG (1 << 1) #define CMD_MSG (1 << 2) #define RD_DATA_MSG (1 << 3) #define WR_DATA_MSG (1 << 4) #define STATUS_MSG (1 << 5) #define ORDERS_MSG (1 << 6) #define VERBOSE_MSG (1 << 7) #define RD_DATA_DETAIL_MSG (1 << 8) #define WR_DATA_DETAIL_MSG (1 << 9) extern uint32 PCX; extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), uint8 unmap); extern REG *sim_PC; #define MDSAD_MAX_DRIVES 4 #define MDSAD_SECTOR_LEN 512 #define MDSAD_SECTORS_PER_TRACK 10 #define MDSAD_TRACKS 35 #define MDSAD_RAW_LEN (32 + 2 + MDSAD_SECTOR_LEN + 1) typedef union { struct { uint8 zeros[32]; uint8 sync[2]; uint8 data[MDSAD_SECTOR_LEN]; uint8 checksum; } u; uint8 raw[MDSAD_RAW_LEN]; } SECTOR_FORMAT; typedef struct { UNIT *uptr; uint8 track; uint8 wp; /* Disk write protected */ uint8 sector; /* Current Sector number */ uint32 sector_wait_count; } MDSAD_DRIVE_INFO; typedef struct { uint8 dd; /* Controls density on write DD=1 for double density and DD=0 for single density. */ uint8 ss; /* Specifies the side of a double-sided diskette. The bottom side (and only side of a single-sided diskette) is selected when SS=0. The second (top) side is selected when SS=1. */ uint8 dp; /* has shared use. During stepping operations, DP=O specifies a step out and DP=1 specifies a step in. During write operations, write procompensation is invoked if and only if DP=1. */ uint8 st; /* controls the level of the head step signal to the disk drives. */ uint8 ds; /* is the drive select field, encoded as follows: */ /* 0=no drive selected * 1=drive 1 selected * 2=drive 2 selected * 4=drive 3 selected * 8=drive 4 selected */ } ORDERS; typedef struct { uint8 sf; /* Sector Flag: set when sector hole detected, reset by software. */ uint8 ix; /* Index Detect: true if index hole detected during previous sector. */ uint8 dd; /* Double Density Indicator: true if data being read is encoded in double density. */ uint8 mo; /* Motor On: true while motor(s) are on. */ } COM_STATUS; typedef struct { uint8 wi; /* Window: true during 96-microsecond window at beginning of sector. */ uint8 re; /* Read Enable: true while phase-locked loop is enabled. */ uint8 sp; /* Spare: reserved for future use. */ uint8 bd; /* Body: set when sync character is detected. */ } A_STATUS; typedef struct { uint8 wr; /* Write: true during valid write operation. */ uint8 sp; /* Spare: reserved for future use. */ uint8 wp; /* Write Protect: true while the diskette installed in the selected drive is write protected. */ uint8 t0; /* Track 0: true if selected drive is at track zero. */ } B_STATUS; typedef struct { uint8 sc; /* Sector Counter: indicates the current sector position. */ } C_STATUS; typedef struct { PNP_INFO pnp; /* Plug and Play */ ORDERS orders; COM_STATUS com_status; A_STATUS a_status; B_STATUS b_status; C_STATUS c_status; uint8 int_enable; /* Interrupt Enable */ uint32 datacount; /* Number of data bytes transferred from controller for current sector */ MDSAD_DRIVE_INFO drive[MDSAD_MAX_DRIVES]; } MDSAD_INFO; static MDSAD_INFO mdsad_info_data = { { 0xE800, 1024, 0, 0 } }; static MDSAD_INFO *mdsad_info = &mdsad_info_data; static SECTOR_FORMAT sdata; #define UNIT_V_MDSAD_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_MDSAD_WLK (1 << UNIT_V_MDSAD_WLK) #define UNIT_V_MDSAD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_MDSAD_VERBOSE (1 << UNIT_V_MDSAD_VERBOSE) #define MDSAD_CAPACITY (70*10*MDSAD_SECTOR_LEN) /* Default North Star Disk Capacity */ #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ /* MDS-AD Controller Subcases */ #define MDSAD_READ_ROM 0 #define MDSAD_WRITE_DATA 1 #define MDSAD_CTLR_ORDERS 2 #define MDSAD_CTLR_COMMAND 3 /* MDS-AD Controller Commands */ #define MDSAD_CMD_NOP 0 #define MDSAD_CMD_RESET_SF 1 #define MDSAD_CMD_INTR_DIS 2 #define MDSAD_CMD_INTR_ARM 3 #define MDSAD_CMD_SET_BODY 4 #define MDSAD_CMD_MOTORS_ON 5 #define MDSAD_CMD_BEGIN_WR 6 #define MDSAD_CMD_RESET 7 /* MDS-AD Data returned on DI bus */ #define MDSAD_A_STATUS 1 #define MDSAD_B_STATUS 2 #define MDSAD_C_STATUS 3 #define MDSAD_READ_DATA 4 /* MDS-AD status byte masks */ /* A-Status */ #define MDSAD_A_SF 0x80 #define MDSAD_A_IX 0x40 #define MDSAD_A_DD 0x20 #define MDSAD_A_MO 0x10 #define MDSAD_A_WI 0x08 #define MDSAD_A_RE 0x04 #define MDSAD_A_SP 0x02 #define MDSAD_A_BD 0x01 /* B-Status */ #define MDSAD_B_SF 0x80 #define MDSAD_B_IX 0x40 #define MDSAD_B_DD 0x20 #define MDSAD_B_MO 0x10 #define MDSAD_B_WR 0x08 #define MDSAD_B_SP 0x04 #define MDSAD_B_WP 0x02 #define MDSAD_B_T0 0x01 /* C-Status */ #define MDSAD_C_SF 0x80 #define MDSAD_C_IX 0x40 #define MDSAD_C_DD 0x20 #define MDSAD_C_MO 0x10 #define MDSAD_C_SC 0x0f /* Local function prototypes */ static t_stat mdsad_reset(DEVICE *mdsad_dev); static t_stat mdsad_attach(UNIT *uptr, char *cptr); static t_stat mdsad_detach(UNIT *uptr); static t_stat mdsad_boot(int32 unitno, DEVICE *dptr); static uint8 MDSAD_Read(const uint32 Addr); static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data); static UNIT mdsad_unit[] = { { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) } }; static REG mdsad_reg[] = { { NULL } }; static MTAB mdsad_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, { UNIT_MDSAD_WLK, 0, "WRTENB", "WRTENB", NULL }, { UNIT_MDSAD_WLK, UNIT_MDSAD_WLK, "WRTLCK", "WRTLCK", NULL }, /* quiet, no warning messages */ { UNIT_MDSAD_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* verbose, show warning messages */ { UNIT_MDSAD_VERBOSE, UNIT_MDSAD_VERBOSE, "VERBOSE", "VERBOSE", NULL }, { 0 } }; #define TRACE_PRINT(level, args) if(mdsad_dev.dctrl & level) { \ printf args; \ } /* Debug Flags */ static DEBTAB mdsad_dt[] = { { "ERROR", ERROR_MSG }, { "SEEK", SEEK_MSG }, { "CMD", CMD_MSG }, { "RDDATA", RD_DATA_MSG }, { "WRDATA", WR_DATA_MSG }, { "STATUS", STATUS_MSG }, { "ORDERS", ORDERS_MSG }, { "RDDETAIL", RD_DATA_DETAIL_MSG }, { "WRDETAIL", WR_DATA_DETAIL_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE mdsad_dev = { "MDSAD", mdsad_unit, mdsad_reg, mdsad_mod, MDSAD_MAX_DRIVES, 10, 31, 1, MDSAD_MAX_DRIVES, MDSAD_MAX_DRIVES, NULL, NULL, &mdsad_reset, &mdsad_boot, &mdsad_attach, &mdsad_detach, &mdsad_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG, mdsad_dt, NULL, "North Star Floppy Controller MDSAD" }; /* Reset routine */ t_stat mdsad_reset(DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; if(dptr->flags & DEV_DIS) { sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdsaddev, TRUE); } else { /* Connect MDSAD at base address */ if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdsaddev, FALSE) != 0) { printf("%s: error mapping resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); dptr->flags |= DEV_DIS; return SCPE_ARG; } } return SCPE_OK; } /* Attach routine */ t_stat mdsad_attach(UNIT *uptr, char *cptr) { char header[4]; t_stat r; unsigned int i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if(r != SCPE_OK) /* error? */ return r; /* Determine length of this disk */ if(sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = MDSAD_CAPACITY; } for(i = 0; i < MDSAD_MAX_DRIVES; i++) { mdsad_info->drive[i].uptr = &mdsad_dev.units[i]; } for(i = 0; i < MDSAD_MAX_DRIVES; i++) { if(mdsad_dev.units[i].fileref == uptr->fileref) { break; } } /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; if(uptr->capac > 0) { fgets(header, 4, uptr->fileref); if(!strcmp(header, "CPT")) { printf("CPT images not yet supported\n"); uptr->u3 = IMAGE_TYPE_CPT; mdsad_detach(uptr); return SCPE_OPENERR; } else { uptr->u3 = IMAGE_TYPE_DSK; } } if (uptr->flags & UNIT_MDSAD_VERBOSE) printf("MDSAD%d, attached to '%s', type=%s, len=%d\n", i, cptr, uptr->u3 == uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", uptr->capac); return SCPE_OK; } /* Detach routine */ t_stat mdsad_detach(UNIT *uptr) { t_stat r; int8 i; for(i = 0; i < MDSAD_MAX_DRIVES; i++) { if(mdsad_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= MDSAD_MAX_DRIVES) return SCPE_ARG; DBG_PRINT(("Detach MDSAD%d\n", i)); r = detach_unit(uptr); /* detach unit */ if(r != SCPE_OK) return r; mdsad_dev.units[i].fileref = NULL; return SCPE_OK; } static t_stat mdsad_boot(int32 unitno, DEVICE *dptr) { PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; DBG_PRINT(("Booting MDSAD Controller at 0x%04x, unit %d" NLP, pnp->mem_base+1+(unitno&3), unitno & 3)); /* Unit 3 can't be booted yet. This involves modifying the A register. */ *((int32 *) sim_PC->loc) = pnp->mem_base+1+(unitno&3); return SCPE_OK; } static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data) { if(rw == 0) { /* Read */ return(MDSAD_Read(Addr)); } else { /* Write */ DBG_PRINT(("MDSAD: write attempt at 0x%04x ignored." NLP, Addr)); return (-1); } } /* This ROM image is taken from the Solace Emulator, which uses */ /* a ROM from a "Micro Complex Phase Lock II" dual- */ /* density controller card. It is supposedly compatible with the */ /* Northstar-designed dual density floppy controller. It has the */ /* interesting property that by jumping to base_addr+0 (or +1) and */ /* it boots from floppy 0; jump to base_addr+2 you boot from floppy 1; */ /* jump to base_addr+3 and you boot from floppy 2. You can boot from */ /* floppy 3 by loading A with 08H and jumping to base_addr+7. */ static uint8 mdsad_rom[] = { 0x44, 0x01, 0x01, 0x01, 0x82, 0x84, 0x78, 0xE6, 0x07, 0x4F, 0x00, 0x31, 0x30, 0x00, 0x21, 0x29, /* 0x00 */ 0x00, 0xE5, 0x21, 0x2C, 0xC2, 0xE5, 0x21, 0x77, 0x13, 0xE5, 0x21, 0xC9, 0x1A, 0xE5, 0xCD, 0x28, /* 0x10 */ 0x00, 0x21, 0x30, 0x00, 0x5B, 0x52, 0x44, 0x54, 0x5D, 0x3A, 0x27, 0x00, 0x57, 0xC3, 0x29, 0x00, /* 0x20 */ 0x14, 0x14, 0x1E, 0x15, 0x1A, 0x26, 0x30, 0xCD, 0xD9, 0x00, 0x42, 0x05, 0x0A, 0xCD, 0xD4, 0x00, /* 0x30 */ 0x2E, 0x0D, 0x2D, 0xCA, 0x43, 0x00, 0xCD, 0xD7, 0x00, 0x1A, 0xE6, 0x40, 0xCA, 0x42, 0x00, 0x3E, /* 0x40 */ 0x0A, 0xF5, 0xCD, 0xC1, 0x00, 0x1E, 0x20, 0x1A, 0xE6, 0x01, 0xC2, 0x63, 0x00, 0xCD, 0xC5, 0x00, /* 0x50 */ 0xC3, 0x55, 0x00, 0x2E, 0x04, 0xCD, 0xE7, 0x00, 0x1E, 0x10, 0x1A, 0xE6, 0x04, 0xCA, 0x68, 0x00, /* 0x60 */ 0x3E, 0x09, 0x3D, 0xC2, 0x72, 0x00, 0x1A, 0xE6, 0x20, 0xC2, 0x84, 0x00, 0xCD, 0xC1, 0x00, 0x2E, /* 0x70 */ 0x08, 0xCD, 0xE7, 0x00, 0x06, 0xA3, 0x1E, 0x10, 0x05, 0xCA, 0xF4, 0x00, 0x1A, 0x0F, 0xD2, 0x88, /* 0x80 */ 0x00, 0x1E, 0x40, 0x1A, 0x67, 0x2E, 0x00, 0x36, 0x59, 0x07, 0x47, 0x23, 0x1A, 0x77, 0xA8, 0x07, /* 0x90 */ 0x47, 0x2C, 0xC2, 0x9C, 0x00, 0x24, 0x1A, 0x77, 0xA8, 0x07, 0x47, 0x2C, 0xC2, 0xA6, 0x00, 0x1A, /* 0xA0 */ 0xA8, 0xC2, 0xF4, 0x00, 0x25, 0x2E, 0x03, 0x71, 0x2D, 0x36, 0x59, 0xC2, 0xB8, 0x00, 0x2E, 0x0A, /* 0xB0 */ 0xE9, 0x3E, 0x20, 0x81, 0x4F, 0x0A, 0x3E, 0x10, 0x81, 0x4F, 0x0A, 0x3E, 0xF0, 0x81, 0x4F, 0x0A, /* 0xC0 */ 0x79, 0xE6, 0x0F, 0x4F, 0xCD, 0xD7, 0x00, 0x26, 0x01, 0x1E, 0x11, 0x1A, 0x1D, 0x1A, 0xB7, 0xF2, /* 0xD0 */ 0xDD, 0x00, 0x25, 0xC2, 0xD9, 0x00, 0xC9, 0xCD, 0xD7, 0x00, 0x1E, 0x35, 0x1A, 0xE6, 0x0F, 0xBD, /* 0xE0 */ 0xC2, 0xE7, 0x00, 0xC9, 0xF1, 0x3D, 0xF5, 0xC2, 0x55, 0x00, 0xC3, 0xFA, 0x00, 0x52, 0x44, 0x54 /* 0xF0 */ }; static void showdata(int32 isRead) { int32 i; printf("MDSAD: " ADDRESS_FORMAT " %s Sector =" NLP "\t", PCX, isRead ? "Read" : "Write"); for(i=0; i < MDSAD_SECTOR_LEN; i++) { printf("%02X ", sdata.u.data[i]); if(((i+1) & 0xf) == 0) printf(NLP "\t"); } printf(NLP); } static int checksum; static uint32 sec_offset; static uint32 calculate_mdsad_sec_offset(uint8 track, uint8 head, uint8 sector) { if(mdsad_info->orders.ss == 0) { return ((track * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) + (sector * MDSAD_SECTOR_LEN)); } else { return ((((MDSAD_TRACKS-1) - track) * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) + ((MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK) * MDSAD_TRACKS) + /* Skip over side 0 */ (sector * MDSAD_SECTOR_LEN)); /* Sector offset from beginning of track. */ } } static uint8 MDSAD_Read(const uint32 Addr) { uint8 cData; uint8 ds; MDSAD_DRIVE_INFO *pDrive; cData = 0x00; pDrive = &mdsad_info->drive[mdsad_info->orders.ds]; switch( (Addr & 0x300) >> 8 ) { case MDSAD_READ_ROM: cData = mdsad_rom[Addr & 0xFF]; break; case MDSAD_WRITE_DATA: { if(mdsad_info->datacount == 0) { TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " WRITE Start: Drive: %d, Track=%d, Head=%d, Sector=%d" NLP, PCX, mdsad_info->orders.ds, pDrive->track, mdsad_info->orders.ss, pDrive->sector)); sec_offset = calculate_mdsad_sec_offset(pDrive->track, mdsad_info->orders.ss, pDrive->sector); } DBG_PRINT(("MDSAD: " ADDRESS_FORMAT " WRITE-DATA[offset:%06x+%03x]=%02x" NLP, PCX, sec_offset, mdsad_info->datacount, Addr & 0xFF)); mdsad_info->datacount++; if(mdsad_info->datacount < MDSAD_RAW_LEN) sdata.raw[mdsad_info->datacount] = Addr & 0xFF; if(mdsad_info->datacount == (MDSAD_RAW_LEN - 1)) { TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " Write Complete" NLP, PCX)); if ((pDrive->uptr == NULL) || (pDrive->uptr->fileref == NULL)) { TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " Drive: %d not attached - write ignored." NLP, PCX, mdsad_info->orders.ds)); return 0x00; } if(mdsad_dev.dctrl & WR_DATA_DETAIL_MSG) showdata(FALSE); switch((pDrive->uptr)->u3) { case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); fwrite(sdata.u.data, MDSAD_SECTOR_LEN, 1, (pDrive->uptr)->fileref); } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } } break; } case MDSAD_CTLR_ORDERS: mdsad_info->orders.dd = (Addr & 0x80) >> 7; mdsad_info->orders.ss = (Addr & 0x40) >> 6; mdsad_info->orders.dp = (Addr & 0x20) >> 5; mdsad_info->orders.st = (Addr & 0x10) >> 4; mdsad_info->orders.ds = (Addr & 0x0F); ds = mdsad_info->orders.ds; switch(mdsad_info->orders.ds) { case 0: case 1: mdsad_info->orders.ds = 0; break; case 2: mdsad_info->orders.ds = 1; break; case 4: mdsad_info->orders.ds = 2; break; case 8: mdsad_info->orders.ds = 3; break; } if(mdsad_info->orders.ds != (mdsad_info->orders.ds & 0x03)) { TRACE_PRINT(ERROR_MSG, ("MDSAD: " ADDRESS_FORMAT " Controller Orders update drive %x" NLP, PCX, mdsad_info->orders.ds)); mdsad_info->orders.ds &= 0x03; } TRACE_PRINT(ORDERS_MSG, ("MDSAD: " ADDRESS_FORMAT " Controller Orders: Drive=%x[%x], DD=%d, SS=%d, DP=%d, ST=%d" NLP, PCX, mdsad_info->orders.ds, ds, mdsad_info->orders.dd, mdsad_info->orders.ss, mdsad_info->orders.dp, mdsad_info->orders.st)); /* use latest selected drive */ pDrive = &mdsad_info->drive[mdsad_info->orders.ds]; if(mdsad_info->orders.st == 1) { if(mdsad_info->orders.dp == 0) { TRACE_PRINT(SEEK_MSG, ("MDSAD: " ADDRESS_FORMAT " Step out: Track=%d%s" NLP, PCX, pDrive->track, pDrive->track == 0 ? "[Warn: already at 0]" : "")); if(pDrive->track > 0) /* anything to do? */ pDrive->track--; } else { TRACE_PRINT(SEEK_MSG, ("MDSAD: " ADDRESS_FORMAT " Step in: Track=%d%s" NLP, PCX, pDrive->track, pDrive->track == (MDSAD_TRACKS - 1) ? "[Warn: already at highest track]" : "")); if(pDrive->track < (MDSAD_TRACKS - 1)) /* anything to do? */ pDrive->track++; } } /* always update t0 */ mdsad_info->b_status.t0 = (pDrive->track == 0); break; case MDSAD_CTLR_COMMAND: /* TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " DM=%x" NLP, PCX, (Addr & 0xF0) >> 4)); */ switch(Addr & 0x0F) { case MDSAD_CMD_MOTORS_ON: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Motors On" NLP, PCX)); mdsad_info->com_status.mo = 1; /* Turn motors on */ break; case MDSAD_CMD_NOP: pDrive->sector_wait_count++; switch(pDrive->sector_wait_count) { case 10: { mdsad_info->com_status.sf = 1; mdsad_info->a_status.wi = 0; mdsad_info->a_status.re = 0; mdsad_info->a_status.bd = 0; pDrive->sector_wait_count = 0; pDrive->sector++; if(pDrive->sector >= MDSAD_SECTORS_PER_TRACK) { pDrive->sector = 0; mdsad_info->com_status.ix = 1; } else { mdsad_info->com_status.ix = 0; } break; } case 2: mdsad_info->a_status.wi = 1; break; case 3: mdsad_info->a_status.re = 1; mdsad_info->a_status.bd = 1; break; default: break; } break; case MDSAD_CMD_RESET_SF: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Reset Sector Flag" NLP, PCX)); mdsad_info->com_status.sf = 0; mdsad_info->datacount = 0; break; case MDSAD_CMD_INTR_DIS: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Disarm Interrupt" NLP, PCX)); mdsad_info->int_enable = 0; break; case MDSAD_CMD_INTR_ARM: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Arm Interrupt" NLP, PCX)); mdsad_info->int_enable = 1; break; case MDSAD_CMD_SET_BODY: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Set Body (Diagnostic)" NLP, PCX)); break; case MDSAD_CMD_BEGIN_WR: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Begin Write" NLP, PCX)); break; case MDSAD_CMD_RESET: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " CMD=Reset Controller" NLP, PCX)); mdsad_info->com_status.mo = 0; /* Turn motors off */ break; default: TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " Unsupported CMD=0x%x" NLP, PCX, Addr & 0x0F)); break; } /* Always Double-Density for now... */ mdsad_info->com_status.dd = 1; cData = (mdsad_info->com_status.sf & 1) << 7; cData |= (mdsad_info->com_status.ix & 1) << 6; cData |= (mdsad_info->com_status.dd & 1) << 5; cData |= (mdsad_info->com_status.mo & 1) << 4; mdsad_info->c_status.sc = pDrive->sector; switch( (Addr & 0xF0) >> 4) { case MDSAD_A_STATUS: /* A-STATUS */ cData |= (mdsad_info->a_status.wi & 1) << 3; cData |= (mdsad_info->a_status.re & 1) << 2; cData |= (mdsad_info->a_status.sp & 1) << 1; cData |= (mdsad_info->a_status.bd & 1); TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT " A-Status = <%s %s %s %s %s %s %s %s>" NLP, PCX, cData & MDSAD_A_SF ? "SF" : " ", cData & MDSAD_A_IX ? "IX" : " ", cData & MDSAD_A_DD ? "DD" : " ", cData & MDSAD_A_MO ? "MO" : " ", cData & MDSAD_A_WI ? "WI" : " ", cData & MDSAD_A_RE ? "RE" : " ", cData & MDSAD_A_SP ? "SP" : " ", cData & MDSAD_A_BD ? "BD" : " ")); break; case MDSAD_B_STATUS: /* B-STATUS */ cData |= (mdsad_info->b_status.wr & 1) << 3; cData |= (mdsad_info->b_status.sp & 1) << 2; cData |= (mdsad_info->b_status.wp & 1) << 1; cData |= (mdsad_info->b_status.t0 & 1); TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT " B-Status = <%s %s %s %s %s %s %s %s>" NLP, PCX, cData & MDSAD_B_SF ? "SF" : " ", cData & MDSAD_B_IX ? "IX" : " ", cData & MDSAD_B_DD ? "DD" : " ", cData & MDSAD_B_MO ? "MO" : " ", cData & MDSAD_B_WR ? "WR" : " ", cData & MDSAD_B_SP ? "SP" : " ", cData & MDSAD_B_WP ? "WP" : " ", cData & MDSAD_B_T0 ? "T0" : " ")); break; case MDSAD_C_STATUS: /* C-STATUS */ cData |= (mdsad_info->c_status.sc & 0xF); TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT " C-Status = <%s %s %s %s %i>" NLP, PCX, cData & MDSAD_C_SF ? "SF" : " ", cData & MDSAD_C_IX ? "IX" : " ", cData & MDSAD_C_DD ? "DD" : " ", cData & MDSAD_C_MO ? "MO" : " ", cData & MDSAD_C_SC)); break; case MDSAD_READ_DATA: /* READ DATA */ { if(mdsad_info->datacount == 0) { TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " READ Start: Drive: %d, Track=%d, Head=%d, Sector=%d" NLP, PCX, mdsad_info->orders.ds, pDrive->track, mdsad_info->orders.ss, pDrive->sector)); checksum = 0; sec_offset = calculate_mdsad_sec_offset(pDrive->track, mdsad_info->orders.ss, pDrive->sector); if ((pDrive->uptr == NULL) || (pDrive->uptr->fileref == NULL)) { TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " Drive: %d not attached - read ignored." NLP, PCX, mdsad_info->orders.ds)); return 0xe5; } switch((pDrive->uptr)->u3) { case IMAGE_TYPE_DSK: if(pDrive->uptr->fileref == NULL) { printf(".fileref is NULL!" NLP); } else { sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); fread(&sdata.u.data[0], MDSAD_SECTOR_LEN, 1, (pDrive->uptr)->fileref); } break; case IMAGE_TYPE_CPT: printf("%s: CPT Format not supported" NLP, __FUNCTION__); break; default: printf("%s: Unknown image Format" NLP, __FUNCTION__); break; } if(mdsad_dev.dctrl & RD_DATA_DETAIL_MSG) showdata(TRUE); } if(mdsad_info->datacount < MDSAD_SECTOR_LEN) { cData = sdata.u.data[mdsad_info->datacount]; /* Exclusive OR */ checksum ^= cData; /* Rotate Left Circular */ checksum = ((checksum << 1) | ((checksum & 0x80) != 0)) & 0xff; DBG_PRINT(("MDSAD: " ADDRESS_FORMAT " READ-DATA[offset:%06x+%03x]=%02x" NLP, PCX, sec_offset, mdsad_info->datacount, cData)); } else { /* checksum */ cData = checksum; TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT " READ-DATA: Checksum is: 0x%02x" NLP, PCX, cData)); } mdsad_info->datacount++; break; } default: DBG_PRINT(("MDSAD: " ADDRESS_FORMAT " Invalid DM=%x" NLP, PCX, Addr & 0xF)); break; } break; } return (cData); } simh-3.8.1/AltairZ80/altairz80_sys.c0000644000175000017500000014160611111142452015237 0ustar vlmvlm/* altairz80_sys.c: MITS Altair system interface Copyright (c) 2002-2008, Peter Schorn 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 PETER SCHORN 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 Peter Schorn shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Peter Schorn. Based on work by Charles E Owen (c) 1997 Disassembler from Marat Fayzullin ((c) 1995, 1996, 1997 - Commercial use prohibited) */ #include #include "altairz80_defs.h" #define SIM_EMAX 6 extern UNIT cpu_unit; extern REG cpu_reg[]; extern DEVICE cpu_dev; extern DEVICE sio_dev; extern DEVICE simh_device; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE dsk_dev; extern DEVICE hdsk_dev; extern DEVICE net_dev; extern DEVICE mfdc_dev; extern DEVICE fw2_dev; extern DEVICE fif_dev; extern DEVICE vfdhd_dev; extern DEVICE mdsad_dev; extern DEVICE nsfpb_dev; extern DEVICE disk1a_dev; extern DEVICE disk2_dev; extern DEVICE disk3_dev; extern DEVICE selchan_dev; extern DEVICE ss1_dev; extern DEVICE if3_dev; extern DEVICE i8272_dev; extern DEVICE mdriveh_dev; extern DEVICE switchcpu_dev; extern DEVICE adcs6_dev; extern DEVICE hdc1001_dev; extern DEVICE cromfdc_dev; extern DEVICE wd179x_dev; extern DEVICE n8vem_dev; extern DEVICE wdi2_dev; extern DEVICE scp300f_dev; #ifdef USE_FPC extern DEVICE fpc_dev; #endif /* USE_FPC */ extern int32 chiptype; extern long disasm (unsigned char *data, char *output, int segsize, long offset); t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); t_stat parse_sym(char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); /* SCP data structures sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax number of words needed for examine sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages */ char sim_name[] = "Altair 8800 (Z80)"; REG *sim_PC = &cpu_reg[6]; int32 sim_emax = SIM_EMAX; DEVICE *sim_devices[] = { /* AltairZ80 Devices */ &cpu_dev, &sio_dev, &simh_device, &ptr_dev, &ptp_dev, &dsk_dev, &hdsk_dev, &net_dev, /* Advanced Digital (ADC) Devices */ &adcs6_dev, &hdc1001_dev, /* Compupro Devices */ &disk1a_dev, &disk2_dev, &disk3_dev, &ss1_dev, &mdriveh_dev, &selchan_dev, &if3_dev, /* Cromemco Devices */ &cromfdc_dev, /* IMSAI Devices */ &fif_dev, /* Micropolis Devices */ &mfdc_dev, /* North Star Devices */ &mdsad_dev, /* Seattle Computer Products Devices */ &scp300f_dev, /* Vector Graphic Devices */ &fw2_dev, &vfdhd_dev, /* Single-Board Computers */ &n8vem_dev, /* Floppy Controller Cores */ &i8272_dev, &wd179x_dev, NULL }; char memoryAccessMessage[80]; const char *sim_stop_messages[] = { "HALT instruction", "Breakpoint", memoryAccessMessage, "Invalid Opcode" }; static char *const Mnemonics8080[] = { /* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "NOP", "LXI B,#h", "STAX B", "INX B", "INR B", "DCR B", "MVI B,*h", "RLC", /* 00-07 */ "DB 09h", "DAD B", "LDAX B", "DCX B", "INR C", "DCR C", "MVI C,*h", "RRC", /* 08-0f */ "DB 10h", "LXI D,#h", "STAX D", "INX D", "INR D", "DCR D", "MVI D,*h", "RAL", /* 10-17 */ "DB 18h", "DAD D", "LDAX D", "DCX D", "INR E", "DCR E", "MVI E,*h", "RAR", /* 18-1f */ "DB 20h", "LXI H,#h", "SHLD #h", "INX H", "INR H", "DCR H", "MVI H,*h", "DAA", /* 20-27 */ "DB 28h", "DAD H", "LHLD #h", "DCX H", "INR L", "DCR L", "MVI L,*h", "CMA", /* 28-2f */ "DB 30h", "LXI SP,#h", "STA #h", "INX SP", "INR M", "DCR M", "MVI M,*h", "STC", /* 30-37 */ "DB 38h", "DAD SP", "LDA #h", "DCX SP", "INR A", "DCR A", "MVI A,*h", "CMC", /* 38-3f */ "MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", "MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 40-47 */ "MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", "MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 48-4f */ "MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", "MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 50-57 */ "MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", "MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 58-5f */ "MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", "MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 60-67 */ "MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", "MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 68-6f */ "MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", "MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 70-77 */ "MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", "MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 78-7f */ "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD M", "ADD A", /* 80-87 */ "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC M", "ADC A", /* 88-8f */ "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB M", "SUB A", /* 90-97 */ "SBB B", "SBB C", "SBB D", "SBB E", "SBB H", "SBB L", "SBB M", "SBB A", /* 98-9f */ "ANA B", "ANA C", "ANA D", "ANA E", "ANA H", "ANA L", "ANA M", "ANA A", /* a0-a7 */ "XRA B", "XRA C", "XRA D", "XRA E", "XRA H", "XRA L", "XRA M", "XRA A", /* a8-af */ "ORA B", "ORA C", "ORA D", "ORA E", "ORA H", "ORA L", "ORA M", "ORA A", /* b0-b7 */ "CMP B", "CMP C", "CMP D", "CMP E", "CMP H", "CMP L", "CMP M", "CMP A", /* b8-bf */ "RNZ", "POP B", "JNZ #h", "JMP #h", "CNZ #h", "PUSH B", "ADI *h", "RST 0", /* c0-c7 */ "RZ", "RET", "JZ #h", "DB CBh", "CZ #h", "CALL #h", "ACI *h", "RST 1", /* c8-cf */ "RNC", "POP D", "JNC #h", "OUT *h", "CNC #h", "PUSH D", "SUI *h", "RST 2", /* d0-d7 */ "RC", "DB D9h", "JC #h", "IN *h", "CC #h", "DB DDh", "SBI *h", "RST 3", /* d8-df */ "RPO", "POP H", "JPO #h", "XTHL", "CPO #h", "PUSH H", "ANI *h", "RST 4", /* e0-e7 */ "RPE", "PCHL", "JPE #h", "XCHG", "CPE #h", "DB EDh", "XRI *h", "RST 5", /* e8-ef */ "RP", "POP PSW", "JP #h", "DI", "CP #h", "PUSH PSW", "ORI *h", "RST 6", /* f0-f7 */ "RM", "SPHL", "JM #h", "EI", "CM #h", "DB FDh", "CPI *h", "RST 7" /* f8-ff */ }; static char *const MnemonicsZ80[256] = { /* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ "EX AF,AF'", "ADD HL,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ "JR $h", "ADD HL,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ "JR NZ,$h", "LD HL,#h", "LD (#h),HL", "INC HL", "INC H", "DEC H", "LD H,*h", "DAA", /* 20-27 */ "JR Z,$h", "ADD HL,HL", "LD HL,(#h)", "DEC HL", "INC L", "DEC L", "LD L,*h", "CPL", /* 28-2f */ "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL),*h", "SCF", /* 30-37 */ "JR C,$h", "ADD HL,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,(HL)", "LD B,A", /* 40-47 */ "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,(HL)", "LD C,A", /* 48-4f */ "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,(HL)", "LD D,A", /* 50-57 */ "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,(HL)", "LD E,A", /* 58-5f */ "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,(HL)", "LD H,A", /* 60-67 */ "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,(HL)", "LD L,A", /* 68-6f */ "LD (HL),B", "LD (HL),C", "LD (HL),D", "LD (HL),E", "LD (HL),H", "LD (HL),L", "HALT", "LD (HL),A", /* 70-77 */ "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,(HL)", "LD A,A", /* 78-7f */ "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,H", "ADD A,L", "ADD A,(HL)", "ADD A,A", /* 80-87 */ "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,H", "ADC A,L", "ADC A,(HL)", "ADC A,A", /* 88-8f */ "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 90-97 */ "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,H", "SBC A,L", "SBC A,(HL)", "SBC A,A", /* 98-9f */ "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* a0-a7 */ "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* a8-af */ "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* b0-b7 */ "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* b8-bf */ "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c0-c7 */ "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ "RET PO", "POP HL", "JP PO,#h", "EX (SP),HL", "CALL PO,#h", "PUSH HL", "AND *h", "RST 20h", /* e0-e7 */ "RET PE", "LD PC,HL", "JP PE,#h", "EX DE,HL", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ "RET M", "LD SP,HL", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ }; static char *const MnemonicsCB[256] = { /* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 00-07 */ "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 08-0f */ "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 10-17 */ "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 18-1f */ "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 20-27 */ "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 28-2f */ "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", /* 30-37 */ "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 38-3f */ "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(HL)", "BIT 0,A", /* 40-47 */ "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(HL)", "BIT 1,A", /* 48-4f */ "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(HL)", "BIT 2,A", /* 50-57 */ "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(HL)", "BIT 3,A", /* 58-5f */ "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(HL)", "BIT 4,A", /* 60-67 */ "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(HL)", "BIT 5,A", /* 68-6f */ "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(HL)", "BIT 6,A", /* 70-77 */ "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(HL)", "BIT 7,A", /* 78-7f */ "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(HL)", "RES 0,A", /* 80-87 */ "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(HL)", "RES 1,A", /* 88-8f */ "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(HL)", "RES 2,A", /* 90-97 */ "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(HL)", "RES 3,A", /* 98-9f */ "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(HL)", "RES 4,A", /* a0-a7 */ "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(HL)", "RES 5,A", /* a8-af */ "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(HL)", "RES 6,A", /* b0-b7 */ "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(HL)", "RES 7,A", /* b8-bf */ "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(HL)", "SET 0,A", /* c0-c7 */ "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(HL)", "SET 1,A", /* c8-cf */ "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(HL)", "SET 2,A", /* d0-d7 */ "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(HL)", "SET 3,A", /* d8-df */ "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(HL)", "SET 4,A", /* e0-e7 */ "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(HL)", "SET 5,A", /* e8-ef */ "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(HL)", "SET 6,A", /* f0-f7 */ "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(HL)", "SET 7,A" /* f8-ff */ }; static char *const MnemonicsED[256] = { /* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "DB EDh,00h", "DB EDh,01h", "DB EDh,02h", "DB EDh,03h", "DB EDh,04h", "DB EDh,05h", "DB EDh,06h", "DB EDh,07h", /* 00-07 */ "DB EDh,08h", "DB EDh,09h", "DB EDh,0Ah", "DB EDh,0Bh", "DB EDh,0Ch", "DB EDh,0Dh", "DB EDh,0Eh", "DB EDh,0Fh", /* 08-0f */ "DB EDh,10h", "DB EDh,11h", "DB EDh,12h", "DB EDh,13h", "DB EDh,14h", "DB EDh,15h", "DB EDh,16h", "DB EDh,17h", /* 10-17 */ "DB EDh,18h", "DB EDh,19h", "DB EDh,1Ah", "DB EDh,1Bh", "DB EDh,1Ch", "DB EDh,1Dh", "DB EDh,1Eh", "DB EDh,1Fh", /* 18-1f */ "DB EDh,20h", "DB EDh,21h", "DB EDh,22h", "DB EDh,23h", "DB EDh,24h", "DB EDh,25h", "DB EDh,26h", "DB EDh,27h", /* 20-27 */ "DB EDh,28h", "DB EDh,29h", "DB EDh,2Ah", "DB EDh,2Bh", "DB EDh,2Ch", "DB EDh,2Dh", "DB EDh,2Eh", "DB EDh,2Fh", /* 28-2f */ "DB EDh,30h", "DB EDh,31h", "DB EDh,32h", "DB EDh,33h", "DB EDh,34h", "DB EDh,35h", "DB EDh,36h", "DB EDh,37h", /* 30-37 */ "DB EDh,38h", "DB EDh,39h", "DB EDh,3Ah", "DB EDh,3Bh", "DB EDh,3Ch", "DB EDh,3Dh", "DB EDh,3Eh", "DB EDh,3Fh", /* 38-3f */ "IN B,(C)", "OUT (C),B", "SBC HL,BC", "LD (#h),BC", "NEG", "RETN", "IM 0", "LD I,A", /* 40-47 */ "IN C,(C)", "OUT (C),C", "ADC HL,BC", "LD BC,(#h)", "DB EDh,4Ch", "RETI", "DB EDh,4Eh", "LD R,A", /* 48-4f */ "IN D,(C)", "OUT (C),D", "SBC HL,DE", "LD (#h),DE", "DB EDh,54h", "DB EDh,55h", "IM 1", "LD A,I", /* 50-57 */ "IN E,(C)", "OUT (C),E", "ADC HL,DE", "LD DE,(#h)", "DB EDh,5Ch", "DB EDh,5Dh", "IM 2", "LD A,R", /* 58-5f */ "IN H,(C)", "OUT (C),H", "SBC HL,HL", "LD (#h),HL", "DB EDh,64h", "DB EDh,65h", "DB EDh,66h", "RRD", /* 60-67 */ "IN L,(C)", "OUT (C),L", "ADC HL,HL", "LD HL,(#h)", "DB EDh,6Ch", "DB EDh,6Dh", "DB EDh,6Eh", "RLD", /* 68-6f */ "IN F,(C)", "DB EDh,71h", "SBC HL,SP", "LD (#h),SP", "DB EDh,74h", "DB EDh,75h", "DB EDh,76h", "DB EDh,77h", /* 70-77 */ "IN A,(C)", "OUT (C),A", "ADC HL,SP", "LD SP,(#h)", "DB EDh,7Ch", "DB EDh,7Dh", "DB EDh,7Eh", "DB EDh,7Fh", /* 78-7f */ "DB EDh,80h", "DB EDh,81h", "DB EDh,82h", "DB EDh,83h", "DB EDh,84h", "DB EDh,85h", "DB EDh,86h", "DB EDh,87h", /* 80-87 */ "DB EDh,88h", "DB EDh,89h", "DB EDh,8Ah", "DB EDh,8Bh", "DB EDh,8Ch", "DB EDh,8Dh", "DB EDh,8Eh", "DB EDh,8Fh", /* 88-8f */ "DB EDh,90h", "DB EDh,91h", "DB EDh,92h", "DB EDh,93h", "DB EDh,94h", "DB EDh,95h", "DB EDh,96h", "DB EDh,97h", /* 90-97 */ "DB EDh,98h", "DB EDh,99h", "DB EDh,9Ah", "DB EDh,9Bh", "DB EDh,9Ch", "DB EDh,9Dh", "DB EDh,9Eh", "DB EDh,9Fh", /* 98-9f */ "LDI", "CPI", "INI", "OUTI", "DB EDh,A4h", "DB EDh,A5h", "DB EDh,A6h", "DB EDh,A7h", /* a0-a7 */ "LDD", "CPD", "IND", "OUTD", "DB EDh,ACh", "DB EDh,ADh", "DB EDh,AEh", "DB EDh,AFh", /* a8-af */ "LDIR", "CPIR", "INIR", "OTIR", "DB EDh,B4h", "DB EDh,B5h", "DB EDh,B6h", "DB EDh,B7h", /* b0-b7 */ "LDDR", "CPDR", "INDR", "OTDR", "DB EDh,BCh", "DB EDh,BDh", "DB EDh,BEh", "DB EDh,BFh", /* b8-bf */ "DB EDh,C0h", "DB EDh,C1h", "DB EDh,C2h", "DB EDh,C3h", "DB EDh,C4h", "DB EDh,C5h", "DB EDh,C6h", "DB EDh,C7h", /* c0-c7 */ "DB EDh,C8h", "DB EDh,C9h", "DB EDh,CAh", "DB EDh,CBh", "DB EDh,CCh", "DB EDh,CDh", "DB EDh,CEh", "DB EDh,CFh", /* c8-cf */ "DB EDh,D0h", "DB EDh,D1h", "DB EDh,D2h", "DB EDh,D3h", "DB EDh,D4h", "DB EDh,D5h", "DB EDh,D6h", "DB EDh,D7h", /* d0-d7 */ "DB EDh,D8h", "DB EDh,D9h", "DB EDh,DAh", "DB EDh,DBh", "DB EDh,DCh", "DB EDh,DDh", "DB EDh,DEh", "DB EDh,DFh", /* d8-df */ "DB EDh,E0h", "DB EDh,E1h", "DB EDh,E2h", "DB EDh,E3h", "DB EDh,E4h", "DB EDh,E5h", "DB EDh,E6h", "DB EDh,E7h", /* e0-e7 */ "DB EDh,E8h", "DB EDh,E9h", "DB EDh,EAh", "DB EDh,EBh", "DB EDh,ECh", "DB EDh,EDh", "DB EDh,EEh", "DB EDh,EFh", /* e8-ef */ "DB EDh,F0h", "DB EDh,F1h", "DB EDh,F2h", "DB EDh,F3h", "DB EDh,F4h", "DB EDh,F5h", "DB EDh,F6h", "DB EDh,F7h", /* f0-f7 */ "DB EDh,F8h", "DB EDh,F9h", "DB EDh,FAh", "DB EDh,FBh", "DB EDh,FCh", "DB EDh,FDh", "DB EDh,FEh", "DB EDh,FFh" /* f8-ff */ }; static char *const MnemonicsXX[256] = { /* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ "EX AF,AF'", "ADD I%,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ "JR $h", "ADD I%,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ "JR NZ,$h", "LD I%,#h", "LD (#h),I%", "INC I%", "INC I%H", "DEC I%H", "LD I%H,*h", "DAA", /* 20-27 */ "JR Z,$h", "ADD I%,I%", "LD I%,(#h)", "DEC I%", "INC I%L", "DEC I%L", "LD I%L,*h", "CPL", /* 28-2f */ "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (I%+^h)", "DEC (I%+^h)", "LD (I%+^h),*h","SCF", /* 30-37 */ "JR C,$h", "ADD I%,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,I%H", "LD B,I%L", "LD B,(I%+^h)", "LD B,A", /* 40-47 */ "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,I%H", "LD C,I%L", "LD C,(I%+^h)", "LD C,A", /* 48-4f */ "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,I%H", "LD D,I%L", "LD D,(I%+^h)", "LD D,A", /* 50-57 */ "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,I%H", "LD E,I%L", "LD E,(I%+^h)", "LD E,A", /* 58-5f */ "LD I%H,B", "LD I%H,C", "LD I%H,D", "LD I%H,E", "LD I%H,I%H", "LD I%H,I%L", "LD H,(I%+^h)", "LD I%H,A", /* 60-67 */ "LD I%L,B", "LD I%L,C", "LD I%L,D", "LD I%L,E", "LD I%L,I%H", "LD I%L,I%L", "LD L,(I%+^h)", "LD I%L,A", /* 68-6f */ "LD (I%+^h),B", "LD (I%+^h),C", "LD (I%+^h),D", "LD (I%+^h),E", "LD (I%+^h),H", "LD (I%+^h),L", "HALT", "LD (I%+^h),A", /* 70-77 */ "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,I%H", "LD A,I%L", "LD A,(I%+^h)", "LD A,A", /* 78-7f */ "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,I%H", "ADD A,I%L", "ADD A,(I%+^h)","ADD A,A", /* 80-87 */ "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,I%H", "ADC A,I%L", "ADC A,(I%+^h)","ADC A,A", /* 88-8f */ "SUB B", "SUB C", "SUB D", "SUB E", "SUB I%H", "SUB I%L", "SUB (I%+^h)", "SUB A", /* 90-97 */ "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,I%H", "SBC A,I%L", "SBC A,(I%+^h)","SBC A,A", /* 98-9f */ "AND B", "AND C", "AND D", "AND E", "AND I%H", "AND I%L", "AND (I%+^h)", "AND A", /* a0-a7 */ "XOR B", "XOR C", "XOR D", "XOR E", "XOR I%H", "XOR I%L", "XOR (I%+^h)", "XOR A", /* a8-af */ "OR B", "OR C", "OR D", "OR E", "OR I%H", "OR I%L", "OR (I%+^h)", "OR A", /* b0-b7 */ "CP B", "CP C", "CP D", "CP E", "CP I%H", "CP I%L", "CP (I%+^h)", "CP A", /* b8-bf */ "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c8-cf */ "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ "RET PO", "POP I%", "JP PO,#h", "EX (SP),I%", "CALL PO,#h", "PUSH I%", "AND *h", "RST 20h", /* e0-e7 */ "RET PE", "LD PC,I%", "JP PE,#h", "EX DE,I%", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ "RET M", "LD SP,I%", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ }; static char *const MnemonicsXCB[256] = { /*0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (I%@h)", "RLC A", /* 00-07 */ "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (I%@h)", "RRC A", /* 08-0f */ "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (I%@h)", "RL A", /* 10-17 */ "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (I%@h)", "RR A", /* 18-1f */ "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (I%@h)", "SLA A", /* 20-27 */ "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (I%@h)", "SRA A", /* 28-2f */ "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (I%@h)", "SLL A", /* 30-37 */ "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (I%@h)", "SRL A", /* 38-3f */ "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(I%@h)", "BIT 0,A", /* 40-47 */ "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(I%@h)", "BIT 1,A", /* 48-4f */ "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(I%@h)", "BIT 2,A", /* 50-57 */ "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(I%@h)", "BIT 3,A", /* 58-5f */ "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(I%@h)", "BIT 4,A", /* 60-67 */ "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(I%@h)", "BIT 5,A", /* 68-6f */ "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(I%@h)", "BIT 6,A", /* 70-77 */ "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(I%@h)", "BIT 7,A", /* 78-7f */ "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(I%@h)", "RES 0,A", /* 80-87 */ "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(I%@h)", "RES 1,A", /* 88-8f */ "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(I%@h)", "RES 2,A", /* 90-97 */ "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(I%@h)", "RES 3,A", /* 98-9f */ "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(I%@h)", "RES 4,A", /* a0-a7 */ "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(I%@h)", "RES 5,A", /* a8-af */ "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(I%@h)", "RES 6,A", /* b0-b7 */ "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(I%@h)", "RES 7,A", /* b8-bf */ "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(I%@h)", "SET 0,A", /* c0-c7 */ "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(I%@h)", "SET 1,A", /* c8-cf */ "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(I%@h)", "SET 2,A", /* d0-d7 */ "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(I%@h)", "SET 3,A", /* d8-df */ "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(I%@h)", "SET 4,A", /* e0-e7 */ "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(I%@h)", "SET 5,A", /* e8-ef */ "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(I%@h)", "SET 6,A", /* f0-f7 */ "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(I%@h)", "SET 7,A" /* f8-ff */ }; /* Symbolic disassembler Inputs: *val = instructions to disassemble useZ80Mnemonics = > 0 iff Z80 mnemonics are to be used addr = current PC Outputs: *S = output text DAsm is Copyright (C) Marat Fayzullin 1995,1996,1997 You are not allowed to distribute this software commercially. */ static int32 DAsm(char *S, const uint32 *val, const int32 useZ80Mnemonics, const int32 addr) { char R[128], H[10], C = '\0', *T, *P; uint8 J = 0, Offset = 0; uint16 B = 0; if (useZ80Mnemonics) switch(val[B]) { case 0xcb: B++; T = MnemonicsCB[val[B++]]; break; case 0xed: B++; T = MnemonicsED[val[B++]]; break; case 0xdd: case 0xfd: C = (val[B++] == 0xdd) ? 'X' : 'Y'; if (val[B] == 0xcb) { B++; Offset = val[B++]; J = 1; T = MnemonicsXCB[val[B++]]; } else T = MnemonicsXX[val[B++]]; break; default: T = MnemonicsZ80[val[B++]]; } else T = Mnemonics8080[val[B++]]; if ( (P = strchr(T, '^')) ) { strncpy(R, T, P - T); R[P - T] = '\0'; sprintf(H, "%02X", val[B++]); strcat(R, H); strcat(R, P + 1); } else strcpy(R, T); if ( (P = strchr(R, '%')) ) { *P = C; if ( (P = strchr(P + 1, '%')) ) *P = C; } if ( (P = strchr(R, '*')) ) { strncpy(S, R, P - R); S[P - R] = '\0'; sprintf(H, "%02X", val[B++]); strcat(S, H); strcat(S, P + 1); } else if ( (P = strchr(R, '@')) ) { strncpy(S, R, P - R); S[P - R] = '\0'; if (!J) Offset = val[B++]; strcat(S, Offset & 0x80 ? "-" : "+"); J = Offset & 0x80 ? 256 - Offset : Offset; sprintf(H, "%02X", J); strcat(S, H); strcat(S, P + 1); } else if ( (P = strchr(R, '$')) ) { strncpy(S, R, P - R); S[P - R] = '\0'; Offset = val[B++]; sprintf(H, "%04X", (addr + 2 + (Offset & 0x80 ? (Offset - 256) : Offset)) & 0xFFFF); strcat(S, H); strcat(S, P + 1); } else if ( (P = strchr(R, '#')) ) { strncpy(S, R, P - R); S[P - R] = '\0'; sprintf(H, "%04X", val[B] + 256 * val[B + 1]); strcat(S, H); strcat(S, P + 1); B += 2; } else strcpy(S, R); return(B); } /* Symbolic output Inputs: *of = output stream addr = current PC *val = pointer to values *uptr = pointer to unit sw = switches Outputs: status = error code */ t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { char disasm_result[128]; int32 ch = val[0] & 0x7f; long r; unsigned char vals[SIM_EMAX]; int32 i; if (sw & (SWMASK('A') | SWMASK('C'))) { fprintf(of, ((0x20 <= ch) && (ch < 0x7f)) ? "'%c'" : "%02x", ch); return SCPE_OK; } if (!(sw & SWMASK('M'))) return SCPE_ARG; if (chiptype == CHIP_TYPE_8086) { for (i = 0; i < SIM_EMAX; i++) vals[i] = val[i] & 0xff; r = disasm(vals, disasm_result, 16, addr); } else r = DAsm(disasm_result, val, chiptype == CHIP_TYPE_Z80, addr); fprintf(of, "%s", disasm_result); return 1 - r; } /* checkbase determines the base of the number (ch, *numString) and returns FALSE if the number is bad */ static int32 checkbase(char ch, const char *numString) { int32 decimal = (ch <= '9'); if (toupper(ch) == 'H') return FALSE; while (isxdigit(ch = *numString++)) if (ch > '9') decimal = FALSE; return toupper(ch) == 'H' ? 16 : (decimal ? 10 : FALSE); } static int32 numok(char ch, const char **numString, const int32 minvalue, const int32 maxvalue, const int32 requireSign, int32 *result) { int32 sign = 1, value = 0, base; if (requireSign) if (ch == '+') ch = *(*numString)++; else if (ch == '-') { sign = -1; ch = *(*numString)++; } else return FALSE; if (!(base = checkbase(ch, *numString))) return FALSE; while (isxdigit(ch)) { value = base * value + ((ch <= '9') ? (ch - '0') : (toupper(ch) - 'A' + 10)); ch = *(*numString)++; } if (toupper(ch) != 'H') (*numString)--; *result = value * sign; return (minvalue <= value) && (value <= maxvalue); } static int32 match(const char *pattern, const char *input, char *xyFirst, char *xy, int32 *number, int32 *star, int32 *at, int32 *hat, int32 *dollar) { char pat = *pattern++; char inp = *input++; while ((pat) && (inp)) { switch(pat) { case '_': /* patterns containing '_' should never match */ return FALSE; case ',': if (inp == ' ') { inp = *input++; continue; } /* otherwise fall through */ case ' ': if (inp != pat) return FALSE; pat = *pattern++; inp = *input++; while (inp == ' ') inp = *input++; continue; case '%': inp = toupper(inp); if ((inp == 'X') || (inp == 'Y')) if (*xyFirst) /* make sure that second '%' corresponds to first */ if (*xyFirst == inp) *xy = inp; else return FALSE; else { /* take note of first '%' for later */ *xyFirst = inp; *xy = inp; } else return FALSE; break; case '#': if (numok(inp, &input, 0, 65535, FALSE, number)) pattern++; /* skip h */ else return FALSE; break; case '*': if (numok(inp, &input, 0, 255, FALSE, star)) pattern++; /* skip h */ else return FALSE; break; case '@': if (numok(inp, &input, -128, 65535, TRUE, at)) pattern++; /* skip h */ else return FALSE; break; case '$': if (numok(inp, &input, 0, 65535, FALSE, dollar)) pattern++; /* skip h */ else return FALSE; break; case '^': if (numok(inp, &input, 0, 255, FALSE, hat)) pattern++; /* skip h */ else return FALSE; break; default: if (toupper(pat) != toupper(inp)) return FALSE; } pat = *pattern++; inp = *input++; } while (inp == ' ') inp = *input++; return (pat == 0) && (inp == 0); } static int32 checkXY(const char xy) { return xy == 'X' ? 0xdd : 0xfd; /* else is 'Y' */ } static int32 parse_X80(const char *cptr, const int32 addr, uint32 *val, char *const Mnemonics[]) { char xyFirst = 0, xy; int32 op, number, star, at, hat, dollar; for (op = 0; op < 256; op++) { number = star = at = dollar = -129; if (match(Mnemonics[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { val[0] = op; if (number >= 0) { val[1] = (0xff) & number; val[2] = (0xff) & (number >> 8); return -2; /* two additional bytes returned */ } else if (star >= 0) { val[1] = (0xff) & star; return -1; /* one additional byte returned */ } else if (at > -129) if ((-128 <= at) && (at <= 127)) { val[1] = (int8)(at); return -1; /* one additional byte returned */ } else return SCPE_ARG; else if (dollar >= 0) { dollar -= addr + 2; /* relative translation */ if ((-128 <= dollar) && (dollar <= 127)) { val[1] = (int8)(dollar); return -1; /* one additional byte returned */ } else return SCPE_ARG; } else return SCPE_OK; } } if (Mnemonics == Mnemonics8080) return SCPE_ARG; for (op = 0; op < 256; op++) if (match(MnemonicsCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { val[0] = 0xcb; val[1] = op; return -1; /* one additional byte returned */ } for (op = 0; op < 256; op++) { number = -1; if (match(MnemonicsED[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { val[0] = 0xed; val[1] = op; if (number >= 0) { val[2] = (0xff) & number; val[3] = (0xff) & (number >> 8); return -3; /* three additional bytes returned */ } else return -1; /* one additional byte returned */ } } for (op = 0; op < 256; op++) { number = star = hat = -1; xy = 0; if (match(MnemonicsXX[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { /* all matches must have contained a '%' character */ if (!(val[0] = checkXY(xy))) return SCPE_ARG; val[1] = op; if (number >= 0) { val[2] = (0xff) & number; val[3] = (0xff) & (number >> 8); return -3; /* three additional bytes returned */ } else if ((star >= 0) && (hat >= 0)) { val[2] = (0xff) & hat; val[3] = (0xff) & star; return -3; /* three additional bytes returned */ } else if (star >= 0) { val[2] = (0xff) & star; return -2; /* two additional bytes returned */ } else if (hat >= 0) { val[2] = (0xff) & hat; return -2; /* two additional bytes returned */ } else return -1; /* one additional byte returned */ } } for (op = 0; op < 256; op++) { at = -129; xy = 0; if (match(MnemonicsXCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { /* all matches must have contained a '%' character */ if (!(val[0] = checkXY(xy))) return SCPE_ARG; val[1] = 0xcb; if (at > -129) val[2] = (int8) (at); else { printf("Offset expected.\n"); return SCPE_ARG; } val[3] = op; return -3; /* three additional bytes returned */ } } return SCPE_ARG; } /* 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) { while (isspace(*cptr)) cptr++; /* absorb spaces */ if ((sw & (SWMASK('A') | SWMASK('C'))) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) return SCPE_ARG; /* must have one char */ val[0] = (uint32) cptr[0]; return SCPE_OK; } return parse_X80(cptr, addr, val, chiptype == CHIP_TYPE_Z80 ? MnemonicsZ80 : Mnemonics8080); } /* Set Memory Base Address routine */ t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; PNP_INFO *pnp; uint32 newba; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; pnp = (PNP_INFO *) dptr->ctxt; if (pnp == NULL) return SCPE_IERR; newba = get_uint (cptr, 16, 0xFFFF, &r); if (r != SCPE_OK) return r; if ((newba > 0xFFFF) || (newba % pnp->mem_size)) return SCPE_ARG; if (dptr->flags & DEV_DIS) { printf("device not enabled yet.\n"); pnp->mem_base = newba & ~(pnp->mem_size-1); } else { dptr->flags |= DEV_DIS; dptr->reset(dptr); pnp->mem_base = newba & ~(pnp->mem_size-1); dptr->flags &= ~DEV_DIS; dptr->reset(dptr); } return SCPE_OK; } /* Show Base Address routine */ t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; PNP_INFO *pnp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; pnp = (PNP_INFO *) dptr->ctxt; if (pnp == NULL) return SCPE_IERR; fprintf(st, "MEM=0x%04X-0x%04X", pnp->mem_base, pnp->mem_base+pnp->mem_size-1); return SCPE_OK; } /* Set Memory Base Address routine */ t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; PNP_INFO *pnp; uint32 newba; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; pnp = (PNP_INFO *) dptr->ctxt; if (pnp == NULL) return SCPE_IERR; newba = get_uint (cptr, 16, 0xFF, &r); if (r != SCPE_OK) return r; if ((newba > 0xFF) || (newba % pnp->io_size)) return SCPE_ARG; if (dptr->flags & DEV_DIS) { printf("device not enabled yet.\n"); pnp->io_base = newba & ~(pnp->io_size-1); } else { dptr->flags |= DEV_DIS; dptr->reset(dptr); pnp->io_base = newba & ~(pnp->io_size-1); dptr->flags &= ~DEV_DIS; dptr->reset(dptr); } return SCPE_OK; } /* Show I/O Base Address routine */ t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; PNP_INFO *pnp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; pnp = (PNP_INFO *) dptr->ctxt; if (pnp == NULL) return SCPE_IERR; fprintf(st, "I/O=0x%02X-0x%02X", pnp->io_base, pnp->io_base+pnp->io_size-1); return SCPE_OK; } simh-3.8.1/LGP/0000755000175000017500000000000011113544526011265 5ustar vlmvlmsimh-3.8.1/LGP/lgp_cpu.c0000644000175000017500000007036311112035410013055 0ustar vlmvlm/* lgp_cpu.c: LGP CPU simulator Copyright (c) 2004-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu LGP-30 [LGP-21] CPU 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 04-Sep-05 RMS Fixed missing returns (found by Peter Schorn) 04-Jan-05 RMS Modified VM pointer setup The system state for the LGP-30 [LGP-21] is: A<0:31> accumulator C<0:11> counter (PC) OVF overflow flag [LGP-21 only] The LGP-30 [LGP-21] has just one instruction format: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |S| |opcode | | operand address | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ LGP-30 instructions: <0,12:15> operation 0 stop 1 A <- M[ea] 2 M[ea] <- A 3 M[ea] <- C + 1 4 input 5 A <- A / M[ea] 6 A <- A * M[ea], low result 7 A <- A * M[ea], high result 8 output 9 A <- A & M[ea] A C <- ea B C <- ea if A < 0 -B C <- ea if (A < 0) || T-switch set C M[ea] <- A D M[ea] <- A, A <- 0 E A <- A + M[ea] F A <- A - M[ea] LGP-21 instructions: <0,12:15> operation 0 stop; sense and skip -0 stop; sense overflow and skip 1 A <- M[ea] 2 M[ea] <- A 3 M[ea] <- C + 1 4 6b input -4 4b input 5 A <- A / M[ea] 6 A <- A * M[ea], low result 7 A <- A * M[ea], high result 8 6b output -8 4b output 9 A <- A & M[ea] A C <- ea B C <- ea if A < 0 -B C <- ea if (A < 0) || T-switch set C M[ea] <- A D M[ea] <- A, A <- 0 E A <- A + M[ea] F A <- A - M[ea] The LGP-30 [LGP-21] has 4096 32b words of memory. The low order bit is always read and stored as 0. The LGP-30 uses a drum for memory, with 64 tracks of 64 words. The LGP-21 uses a disk for memory, with 32 tracks of 128 words. This routine is the instruction decode routine for the LGP-30 [LGP-21]. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: STOP instruction breakpoint encountered overflow [LGP-30] I/O error in I/O simulator 2. Interrupts. There are no interrupts. 3. Non-existent memory. All of memory always exists. 4. Adding I/O devices. The LGP-30 could not support additional I/O devices. The LGP-21 could but none are known. */ #include "lgp_defs.h" #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 - 1) & AMASK; #define M16 0xFFFF #define M32 0xFFFFFFFF #define NEG(x) ((~(x) + 1) & DMASK) #define ABS(x) (((x) & SIGN)? NEG (x): (x)) uint32 M[MEMSIZE] = { 0 }; /* memory */ uint32 PC = 0; /* counter */ uint32 A = 0; /* accumulator */ uint32 IR = 0; /* instr register */ uint32 OVF = 0; /* overflow indicator */ uint32 t_switch = 0; /* transfer switch */ uint32 bp32 = 0; /* BP32 switch */ uint32 bp16 = 0; /* BP16 switch */ uint32 bp8 = 0; /* BP8 switch */ uint32 bp4 = 0; /* BP4 switch */ uint32 inp_strt = 0; /* input started */ uint32 inp_done = 0; /* input done */ uint32 out_strt = 0; /* output started */ uint32 out_done = 0; /* output done */ uint32 lgp21_sov = 0; /* LGP-21 sense pending */ int32 delay = 0; int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern int32 sim_step; 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_model (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_one_inst (uint32 opc, uint32 ir); uint32 Mul64 (uint32 a, uint32 b, uint32 *low); t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q); uint32 I_delay (uint32 opc, uint32 ea, uint32 op); uint32 shift_in (uint32 a, uint32 dat, uint32 sh4); extern t_stat op_p (uint32 dev, uint32 ch); extern t_stat op_i (uint32 dev, uint32 ch, uint32 sh4); extern void lgp_vm_init (void); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifiers list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_IN4B+UNIT_TTSS_D, MEMSIZE) }; REG cpu_reg[] = { { DRDATA (C, PC, 12), REG_VMAD }, { HRDATA (A, A, 32), REG_VMIO }, { HRDATA (IR, IR, 32), REG_VMIO }, { FLDATA (OVF, OVF, 0) }, { FLDATA (TSW, t_switch, 0) }, { FLDATA (BP32, bp32, 0) }, { FLDATA (BP16, bp16, 0) }, { FLDATA (BP8, bp8, 0) }, { FLDATA (BP4, bp4, 0) }, { FLDATA (INPST, inp_strt, 0) }, { FLDATA (INPDN, inp_done, 0) }, { FLDATA (OUTST, out_strt, 0) }, { FLDATA (OUTDN, out_done, 0) }, { DRDATA (DELAY, delay, 7) }, { BRDATA (CQ, pcq, 16, 12, PCQ_SIZE), REG_RO + REG_CIRC }, { HRDATA (CQP, pcq_p, 6), REG_HRO }, { HRDATA (WRU, sim_int_char, 8) }, { NULL } }; MTAB cpu_mod[] = { { UNIT_LGP21, UNIT_LGP21, "LGP-21", "LGP21", &cpu_set_model, &cpu_show_model }, { UNIT_LGP21, 0, "LGP-30", "LGP30", &cpu_set_model, &cpu_show_model }, { UNIT_TTSS_D, UNIT_TTSS_D, 0, "TRACK" }, { UNIT_TTSS_D, 0, 0, "NORMAL" }, { UNIT_LGPH_D, UNIT_LGPH_D, 0, "LGPHEX" }, { UNIT_LGPH_D, 0, 0, "STANDARDHEX" }, { UNIT_MANI, UNIT_MANI, NULL, "MANUAL" }, { UNIT_MANI, 0, NULL, "TAPE" }, { UNIT_IN4B, UNIT_IN4B, NULL, "4B", &cpu_set_30opt }, { UNIT_IN4B, 0, NULL, "6B", &cpu_set_30opt }, { MTAB_XTD|MTAB_VDV, 0, NULL, "INPUT", &cpu_set_30opt_i }, { MTAB_XTD|MTAB_VDV, 0, NULL, "OUTPUT", &cpu_set_30opt_o }, { MTAB_XTD|MTAB_VDV, 0, NULL, "EXECUTE", &cpu_set_exec }, { MTAB_XTD|MTAB_VDV, 0, NULL, "FILL", &cpu_set_fill }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 10, 12, 1, 16, 32, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL }; /* Timing tables */ /* Optimization minima and maxima Z B Y R I D N M P E U T H C A S */ static const int32 min_30[16] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; static const int32 max_30[16] = { 7, 7, 7, 7, 7, 5, 8, 6, 7, 7, 0, 0, 7, 7, 7, 7 }; static const int32 min_21[16] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; static const int32 max_21[16] = { 0, 16, 16, 16, 0, 58, 81, 79, 0, 16, 0, 0, 16, 16, 16, 16 }; static const uint32 log_to_phys_30[NSC_30] = { /* drum interlace chart */ 0, 57, 50, 43, 36, 29, 22, 15, 8 , 1, 58, 51, 44, 37, 30, 23, 16, 9 , 2, 59, 52, 45, 38, 31, 24, 17, 10, 3, 60, 53, 46, 39, 32, 25, 18, 11, 4, 61, 54, 47, 40, 33, 26, 19, 12, 5, 62, 55, 48, 41, 32, 27, 20, 13, 6, 63, 56, 49, 42, 33, 28, 21, 14, 7 }; static const uint32 log_to_phys_21[NSC_21] = { /* disk interlace chart */ 0, 64, 57, 121, 50, 114, 43, 107, 36, 100, 29, 93, 22, 86, 15, 79, 8, 72, 1, 65, 58, 122, 51, 115, 44, 108, 37, 101, 30, 94, 23, 87, 16, 80, 9, 73, 2, 66, 59, 123, 52, 116, 45, 109, 38, 102, 31, 95, 24, 88, 17, 81, 10, 74, 3, 67, 60, 124, 53, 117, 46, 110, 39, 103, 32, 96, 25, 89, 18, 82, 11, 75, 4, 68, 61, 125, 54, 118, 47, 111, 40, 104, 33, 97, 26, 90, 19, 83, 12, 76, 5, 69, 62, 126, 55, 119, 48, 112, 41, 105, 34, 98, 27, 91, 20, 84, 12, 77, 6, 70, 63, 127, 56, 120, 49, 113, 42, 106, 35, 99, 28, 92, 21, 85, 13, 78, 7, 71 }; t_stat sim_instr (void) { t_stat r = 0; uint32 oPC; /* Restore register state */ PC = PC & AMASK; /* mask PC */ sim_cancel_step (); /* defang SCP step */ if (lgp21_sov) { /* stop sense pending? */ lgp21_sov = 0; if (!OVF) /* ovf off? skip */ PC = (PC + 1) & AMASK; else OVF = 0; /* on? reset */ } /* Main instruction fetch/decode loop */ do { if (sim_interval <= 0) { /* check clock queue */ if (r = sim_process_event ()) break; } if (delay > 0) { /* delay to next instr */ delay = delay - 1; /* count down delay */ sim_interval = sim_interval - 1; continue; /* skip execution */ } if (sim_brk_summ && /* breakpoint? */ sim_brk_test (PC, SWMASK ('E'))) { r = STOP_IBKPT; /* stop simulation */ break; } IR = Read (oPC = PC); /* get instruction */ PC = (PC + 1) & AMASK; /* increment PC */ sim_interval = sim_interval - 1; if (r = cpu_one_inst (oPC, IR)) { /* one instr; error? */ if (r == STOP_STALL) { /* stall? */ PC = oPC; /* back up PC */ delay = r = 0; /* no delay */ } else break; } if (sim_step && (--sim_step <= 0)) /* do step count */ r = SCPE_STOP; } while (r == 0); /* loop until halted */ pcq_r->qptr = pcq_p; /* update pc q ptr */ return r; } /* Execute one instruction */ t_stat cpu_one_inst (uint32 opc, uint32 ir) { uint32 ea, op, dat, res, dev, sh4, ch; t_bool ovf_this_cycle = FALSE; t_stat reason = 0; op = I_GETOP (ir); /* opcode */ ea = I_GETEA (ir); /* address */ switch (op) { /* case on opcode */ /* Loads, stores, transfers instructions */ case OP_B: /* bring */ A = Read (ea); /* A <- M[ea] */ delay = I_delay (opc, ea, op); break; case OP_H: /* hold */ Write (ea, A); /* M[ea] <- A */ delay = I_delay (opc, ea, op); break; case OP_C: /* clear */ Write (ea, A); /* M[ea] <- A */ A = 0; /* A <- 0 */ delay = I_delay (opc, ea, op); break; case OP_Y: /* store address */ dat = Read (ea); /* get operand */ dat = (dat & ~I_EA) | (A & I_EA); /* merge address */ Write (ea, dat); delay = I_delay (opc, ea, op); break; case OP_R: /* return address */ dat = Read (ea); /* get operand */ dat = (dat & ~I_EA) | (((PC + 1) & AMASK) << I_V_EA); Write (ea, dat); delay = I_delay (opc, ea, op); break; case OP_U: /* uncond transfer */ PCQ_ENTRY; PC = ea; /* transfer */ delay = I_delay (opc, ea, op); break; case OP_T: /* conditional transfer */ if ((A & SIGN) || /* A < 0 or */ ((ir & SIGN) && t_switch)) { /* -T and Tswitch set? */ PCQ_ENTRY; PC = ea; /* transfer */ } delay = I_delay (opc, ea, op); break; /* Arithmetic and logical instructions */ case OP_A: /* add */ dat = Read (ea); /* get operand */ res = (A + dat) & DMASK; /* add */ if ((~A ^ dat) & (dat ^ res) & SIGN) /* calc overflow */ ovf_this_cycle = TRUE; A = res; /* save result */ delay = I_delay (opc, ea, op); break; case OP_S: /* sub */ dat = Read (ea); /* get operand */ res = (A - dat) & DMASK; /* subtract */ if ((A ^ dat) & (~dat ^ res) & SIGN) /* calc overflow */ ovf_this_cycle = TRUE; A = res; delay = I_delay (opc, ea, op); break; case OP_M: /* multiply high */ dat = Read (ea); /* get operand */ A = (Mul64 (A, dat, NULL) << 1) & DMASK; /* multiply */ delay = I_delay (opc, ea, op); break; case OP_N: /* multiply low */ dat = Read (ea); /* get operand */ Mul64 (A, dat, &res); /* multiply */ A = res; /* keep low result */ delay = I_delay (opc, ea, op); /* total delay */ break; case OP_D: /* divide */ dat = Read (ea); /* get operand */ if (Div32 (A, dat, &A)) /* divide; overflow? */ ovf_this_cycle = TRUE; delay = I_delay (opc, ea, op); break; case OP_E: /* extract */ dat = Read (ea); /* get operand */ A = A & dat; /* and */ delay = I_delay (opc, ea, op); break; /* IO instructions */ case OP_P: /* output */ if (Q_LGP21) { /* LGP-21 */ ch = A >> 26; /* char, 6b */ if (ir & SIGN) /* 4b? convert */ ch = (ch & 0x3C) | 2; dev = I_GETTK (ir); /* device select */ } else { /* LGP-30 */ ch = I_GETTK (ir); /* char, always 6b */ dev = Q_OUTPT? DEV_PT: DEV_TT; /* device select */ } reason = op_p (dev & DEV_MASK, ch); /* output */ delay = I_delay (sim_grtime (), ea, op); /* next instruction */ break; case OP_I: /* input */ if (Q_LGP21) { /* LGP-21 */ ch = 0; /* initial shift */ sh4 = ir & SIGN; /* 4b/6b select */ dev = I_GETTK (ir); /* device select */ } else { /* LGP-30 */ ch = I_GETTK (ir); /* initial shift */ sh4 = Q_IN4B; /* 4b/6b select */ dev = Q_INPT? DEV_PT: DEV_TT; /* device select */ } if (dev == DEV_SHIFT) /* shift? */ A = shift_in (A, 0, sh4); /* shift 4/6b */ else reason = op_i (dev & DEV_MASK, ch, sh4); /* input */ delay = I_delay (sim_grtime (), ea, op); /* next instruction */ break; case OP_Z: if (Q_LGP21) { /* LGP-21 */ if (ea & 0xF80) { /* no stop? */ if (((ea & 0x800) && !bp32) || /* skip if any */ ((ea & 0x400) && !bp16) || /* selected switch */ ((ea & 0x200) && !bp8) || /* is off */ ((ea & 0x100) && !bp4) || /* or if */ ((ir & SIGN) && !OVF)) /* ovf sel and off */ PC = (PC + 1) & AMASK; if (ir & SIGN) /* -Z? clr overflow */ OVF = 0; } else { /* stop */ lgp21_sov = (ir & SIGN)? 1: 0; /* pending sense? */ reason = STOP_STOP; /* stop */ } } else { /* LGP-30 */ if (out_done) /* P complete? */ out_done = 0; else if (((ea & 0x800) && bp32) || /* bpt switch set? */ ((ea & 0x400) && bp16) || ((ea & 0x200) && bp8) || ((ea & 0x100) && bp4)) ; /* don't stop or stall */ else if (out_strt) /* P pending? stall */ reason = STOP_STALL; else reason = STOP_STOP; /* no, stop */ } delay = I_delay (sim_grtime (), ea, op); /* next instruction */ break; /* end switch */ } if (ovf_this_cycle) { if (Q_LGP21) /* LGP-21? set OVF */ OVF = 1; else reason = STOP_OVF; /* LGP-30? stop */ } return reason; } /* Support routines */ uint32 Read (uint32 ea) { return M[ea] & MMASK; } void Write (uint32 ea, uint32 dat) { M[ea] = dat & MMASK; return; } /* Input shift */ uint32 shift_in (uint32 a, uint32 dat, uint32 sh4) { if (sh4) return (((a << 4) | (dat >> 2)) & DMASK); return (((a << 6) | dat) & DMASK); } /* 32b * 32b multiply, signed */ uint32 Mul64 (uint32 a, uint32 b, uint32 *low) { uint32 sgn = a ^ b; uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; if ((a == 0) || (b == 0)) { /* zero argument? */ if (low) *low = 0; return 0; } a = ABS (a); b = ABS (b); ah = (a >> 16) & M16; /* split operands */ bh = (b >> 16) & M16; /* into 16b chunks */ al = a & M16; bl = b & M16; rhi = ah * bh; /* high result */ rmid1 = ah * bl; rmid2 = al * bh; rlo = al * bl; rhi = rhi + ((rmid1 >> 16) & M16) + ((rmid2 >> 16) & M16); rmid1 = (rlo + (rmid1 << 16)) & M32; /* add mid1 to lo */ if (rmid1 < rlo) /* carry? incr hi */ rhi = rhi + 1; rmid2 = (rmid1 + (rmid2 << 16)) & M32; /* add mid2 to to */ if (rmid2 < rmid1) /* carry? incr hi */ rhi = rhi + 1; if (sgn & SIGN) { /* result negative? */ rmid2 = NEG (rmid2); /* negate */ rhi = (~rhi + (rmid2 == 0)) & M32; } if (low) /* low result */ *low = rmid2; return rhi & M32; } /* 32b/32b divide (done as 32b'0/32b) */ t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q) { uint32 sgn = dvd ^ dvr; uint32 i, quo; dvd = ABS (dvd); dvr = ABS (dvr); if (dvd >= dvr) return TRUE; for (i = quo = 0; i < 31; i++) { /* 31 iterations */ quo = quo << 1; /* shift quotient */ dvd = dvd << 1; /* shift dividend */ if (dvd >= dvr) { /* step work? */ dvd = (dvd - dvr) & M32; /* subtract dvr */ quo = quo + 1; } } quo = (quo + 1) & MMASK; /* round low bit */ if (sgn & SIGN) /* result -? */ quo = NEG (quo); if (q) /* return quo */ *q = quo; return FALSE; /* no overflow */ } /* Rotational delay */ uint32 I_delay (uint32 opc, uint32 ea, uint32 op) { uint32 tmin = Q_LGP21? min_21[op]: min_30[op]; uint32 tmax = Q_LGP21? max_21[op]: max_30[op]; uint32 nsc, curp, newp, oprp, pcdelta, opdelta; if (Q_LGP21) { /* LGP21 */ nsc = NSC_21; /* full rotation delay */ curp = log_to_phys_21[opc & SCMASK_21]; /* current phys pos */ newp = log_to_phys_21[PC & SCMASK_21]; /* new PC phys pos */ oprp = log_to_phys_21[ea & SCMASK_21]; /* ea phys pos */ pcdelta = (newp - curp + NSC_21) & SCMASK_21; opdelta = (oprp - curp + NSC_21) & SCMASK_21; } else { nsc = NSC_30; curp = log_to_phys_30[opc & SCMASK_30]; newp = log_to_phys_30[PC & SCMASK_30]; oprp = log_to_phys_30[ea & SCMASK_30]; pcdelta = (newp - curp + NSC_30) & SCMASK_30; opdelta = (oprp - curp + NSC_30) & SCMASK_30; } if (tmax == 0) { /* skip ea calc? */ if (pcdelta >= tmin) /* new PC >= min? */ return pcdelta - 1; return pcdelta + nsc - 1; } if ((opdelta >= tmin) && (opdelta <= tmax)) return pcdelta - 1; return pcdelta + nsc - 1; } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { OVF = 0; inp_strt = 0; inp_done = 0; out_strt = 0; out_done = 0; lgp21_sov = 0; delay = 0; lgp_vm_init (); pcq_r = find_reg ("CQ", NULL, dptr); if (pcq_r) pcq_r->qptr = 0; else return SCPE_IERR; sim_brk_types = sim_brk_dflt = SWMASK ('E'); return SCPE_OK; } /* Validate option, must be LGP30 */ t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc) { if (Q_LGP21) return SCPE_ARG; return SCPE_OK; } /* Validate input option, must be LGP30 */ t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc) { if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG; if (strcmp (cptr, "TTI") == 0) uptr->flags = uptr->flags & ~UNIT_INPT; else if (strcmp (cptr, "PTR") == 0) uptr->flags = uptr->flags | UNIT_INPT; else return SCPE_ARG; return SCPE_OK; } /* Validate output option, must be LGP30 */ t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc) { if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG; if (strcmp (cptr, "TTO") == 0) uptr->flags = uptr->flags & ~UNIT_OUTPT; else if (strcmp (cptr, "PTP") == 0) uptr->flags = uptr->flags | UNIT_OUTPT; else return SCPE_ARG; return SCPE_OK; } /* Set CPU to LGP21 or LPG30 */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val) uptr->flags = uptr->flags & ~(UNIT_IN4B|UNIT_INPT|UNIT_OUTPT); return reset_all (0); } /* Show CPU type and all options */ t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) { fputs (Q_LGP21? "LGP-21": "LGP-30", st); if (uptr->flags & UNIT_TTSS_D) fputs (", track/sector", st); if (uptr->flags & UNIT_LGPH_D) fputs (", LGP hex", st); fputs (Q_MANI? ", manual": ", tape", st); if (!Q_LGP21) { fputs (Q_IN4B? ", 4b": ", 6b", st); fputs (Q_INPT? ", in=PTR": ", in=TTI", st); fputs (Q_OUTPT? ", out=PTP": ", out=TTO", st); } 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 = Read (addr); 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; Write (addr, val); return SCPE_OK; } /* Execute */ t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 inst; t_stat r; if (cptr) { inst = get_uint (cptr, 16, DMASK, &r); if (r != SCPE_OK) return r; } else inst = IR; while ((r = cpu_one_inst (PC, inst)) == STOP_STALL) { sim_interval = 0; if (r = sim_process_event ()) return r; } return r; } /* Fill */ t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 inst; t_stat r; if (cptr) { inst = get_uint (cptr, 16, DMASK, &r); if (r != SCPE_OK) return r; IR = inst; } else IR = A; return SCPE_OK; } simh-3.8.1/LGP/lgp_sys.c0000644000175000017500000003230111112035410013072 0ustar vlmvlm/* lgp_sys.c: LGP-30 simulator interface Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 04-Jan-05 RMS Modified VM pointer setup */ #include "lgp_defs.h" #include t_stat parse_sym_m (char *cptr, t_value *val, int32 sw); void lgp_init (void); extern DEVICE cpu_dev; extern UNIT cpu_unit; extern DEVICE tti_dev, tto_dev; extern DEVICE ptr_dev, ptp_dev; extern REG cpu_reg[]; extern uint32 M[]; extern uint32 PC; extern uint32 ts_flag; extern int32 sim_switches; extern int32 flex_to_ascii[128], ascii_to_flex[128]; extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr); extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr); /* SCP data structures and interface routines sim_name simulator name string sim_PC pointer to saved PC register descriptor sim_emax maximum number of words for examine/deposit sim_devices array of pointers to simulated devices sim_stop_messages array of pointers to stop messages sim_load binary loader */ char sim_name[] = "LGP30"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, &tti_dev, &tto_dev, &ptr_dev, &ptp_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "STOP", "Breakpoint", "Arithmetic overflow" }; /* Binary loader - implements a restricted form of subroutine 10.4 Switches: -t, input file is transposed Flex -n, no checksums on v commands (10.0 compatible) default is ASCII encoded Flex Commands (in bits 0-3): (blank) instruction + command (not supported) ; start fill / set modifier . stop and transfer , hex words v hex fill (checksummed unless -n) 8 negative instruction */ /* Utility routine - read characters until ' (conditional stop) */ t_stat load_getw (FILE *fi, uint32 *wd) { int32 flex, c; *wd = 0; while ((c = fgetc (fi)) != EOF) { if (sim_switches & SWMASK ('T')) flex = ((c << 1) | (c >> 5)) & 0x3F; else flex = ascii_to_flex[c & 0x7F]; if ((flex == FLEX_CR) || (flex == FLEX_DEL) || (flex == FLEX_UC) || (flex == FLEX_LC) || (flex == FLEX_BS) || (flex < 0)) continue; if (flex == FLEX_CSTOP) return SCPE_OK; *wd = (*wd << 4) | ((flex >> 2) & 0xF); } return SCPE_FMT; } /* Utility routine - convert ttss decimal address to binary */ t_stat load_geta (uint32 wd, uint32 *ad) { uint32 n1, n2, n3, n4, tr, sc; n1 = (wd >> 12) & 0xF; n2 = (wd >> 8) & 0xF; n3 = (wd >> 4) & 0xF; n4 = wd & 0xF; if ((n2 > 9) || (n4 > 9)) return SCPE_ARG; tr = (n1 * 10) + n2; sc = (n3 * 10) + n4; if ((tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG; *ad = (tr * NSC_30) + sc; return SCPE_OK; } /* Loader proper */ t_stat sim_load (FILE *fi, char *cptr, char *fnam, int flag) { uint32 wd, origin, amod, csum, cnt, tr, sc, ad, cmd; origin = amod = 0; for (;;) { /* until stopped */ if (load_getw (fi, &wd)) /* get ctrl word */ break; cmd = (wd >> 28) & 0xF; /* get <0:3> */ switch (cmd) { /* decode <0:3> */ case 0x2: /* + command */ return SCPE_FMT; case 0x3: /* ; start fill */ if (load_geta (wd, &origin)) /* origin = addr */ return SCPE_FMT; break; case 0x4: /* / set modifier */ if (load_geta (wd, &amod)) /* modifier = addr */ return SCPE_FMT; break; case 0x5: /* . transfer */ if (load_geta (wd, &PC)) /* PC = addr */ return SCPE_FMT; return SCPE_OK; /* done! */ case 0x6: /* hex words */ if (load_geta (wd, &cnt)) /* count = addr */ return SCPE_FMT; if ((cnt == 0) || (cnt > 63)) return SCPE_FMT; while (cnt--) { /* fill hex words */ if (load_getw (fi, &wd)) return SCPE_FMT; Write (origin, wd); origin = (origin + 1) & AMASK; } break; case 0x7: /* hex fill */ cnt = (wd >> 16) & 0xFFF; /* hex count */ tr = (wd >> 8) & 0xFF; /* hex track */ sc = wd & 0xFF; /* hex sector */ if ((cnt == 0) || (cnt > 0x7FF) || /* validate */ (tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG; ad = (tr * NSC_30) + sc; /* decimal addr */ for (csum = 0; cnt; cnt--) { /* fill words */ if (load_getw (fi, &wd)) return SCPE_FMT; Write (ad, wd); csum = (csum + wd) & MMASK; ad = (ad + 1) & AMASK; } if (!(sim_switches & SWMASK ('N'))) { /* unless -n, csum */ if (load_getw (fi, &wd)) return SCPE_FMT; /* if ((csum ^wd) & MMASK) return SCPE_CSUM; */ } break; case 0x0: case 0x8: /* instructions */ if (load_geta (wd, &ad)) /* get address */ return SCPE_FMT; if ((wd & 0x00F00000) != 0x00900000) /* if not x, */ ad = (ad + amod) & AMASK; /* modify */ wd = (wd & (SIGN|I_OP)) + (ad << I_V_EA); /* instruction */ default: /* data word */ Write (origin, wd); origin = (origin + 1) & AMASK; break; } /* end case */ } /* end for */ return SCPE_OK; } /* Symbol tables */ static const char opcode[] = "ZBYRIDNMPEUTHCAS"; static const char hex_decode[] = "0123456789FGJKQW"; void lgp_fprint_addr (FILE *st, DEVICE *dptr, t_addr addr) { if ((dptr == sim_devices[0]) && ((sim_switches & SWMASK ('T')) || ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N'))))) fprintf (st, "%02d%02d", addr >> 6, addr & SCMASK_30); else fprint_val (st, addr, dptr->aradix, dptr->awidth, PV_LEFT); return; } t_addr lgp_parse_addr (DEVICE *dptr, char *cptr, char **tptr) { t_addr ad, ea; if ((dptr == sim_devices[0]) && ((sim_switches & SWMASK ('T')) || ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N'))))) { ad = (t_addr) strtotv (cptr, tptr, 10); if (((ad / 100) >= NTK_30) || ((ad % 100) >= NSC_30)) { *tptr = cptr; return 0; } ea = ((ad / 100) * NSC_30) | (ad % 100); } else ea = (t_addr) strtotv (cptr, tptr, dptr->aradix); return ea; } void lgp_vm_init (void) { sim_vm_fprint_addr = &lgp_fprint_addr; sim_vm_parse_addr = &lgp_parse_addr; return; } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = pointer to data *uptr = pointer to unit sw = switches Outputs: return = status code */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 i, c; uint32 inst, op, ea; inst = val[0]; if (sw & SWMASK ('A')) { /* alphabetic? */ if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG; if (uptr->flags & UNIT_FLEX) { /* Flex file? */ c = flex_to_ascii[inst]; /* get ASCII equiv */ if (c <= 0) return SCPE_ARG; } else c = inst & 0x7F; /* ASCII file */ fputc (c, of); return SCPE_OK; } if (uptr && (uptr != &cpu_unit)) /* must be CPU */ return SCPE_ARG; if ((sw & SWMASK ('M')) && /* symbolic decode? */ ((inst & ~(SIGN|I_OP|I_EA)) == 0)) { op = I_GETOP (inst); ea = I_GETEA (inst); if (inst & SIGN) fputc ('-', of); fprintf (of, "%c ", opcode[op]); lgp_fprint_addr (of, sim_devices[0], ea); return SCPE_OK; } if ((sw & SWMASK ('L')) || /* LGP hex? */ ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) { for (i = 0; i < 8; i++) { c = (inst >> (4 * (7 - i))) & 0xF; fputc (hex_decode[c], of); } return SCPE_OK; } return SCPE_ARG; } /* 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) { int32 i, c; char *tptr; while (isspace (*cptr)) /* absorb spaces */ cptr++; if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG; if (uptr->flags & UNIT_FLEX) { /* Flex file? */ c = ascii_to_flex[*cptr & 0x7F]; /* get Flex equiv */ if (c < 0) return SCPE_ARG; val[0] = ((c >> 1) | (c << 5)) & 0x3F; /* transpose */ } else val[0] = *cptr & 0x7F; /* ASCII file */ return SCPE_OK; } if (uptr && (uptr != &cpu_unit)) /* must be CPU */ return SCPE_ARG; if (!parse_sym_m (cptr, val, sw)) /* symbolic parse? */ return SCPE_OK; if ((sw & SWMASK ('L')) || /* LGP hex? */ ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) { val[0] = 0; while (isspace (*cptr)) cptr++; /* absorb spaces */ for (i = 0; i < 8; i++) { c = *cptr++; /* get char */ if (c == 0) return SCPE_OK; if (islower (c)) c = toupper (c); if (tptr = strchr (hex_decode, c)) val[0] = (val[0] << 4) | (tptr - hex_decode); else return SCPE_ARG; } if (*cptr == 0) return SCPE_OK; } return SCPE_ARG; } /* Instruction parse */ t_stat parse_sym_m (char *cptr, t_value *val, int32 sw) { uint32 ea, sgn; char *tptr, gbuf[CBUFSIZE]; if (*cptr == '-') { cptr++; sgn = SIGN; } else sgn = 0; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ if (gbuf[1] != 0) return SCPE_ARG; if (tptr = strchr (opcode, gbuf[0])) val[0] = ((tptr - opcode) << I_V_OP) | sgn; /* merge opcode */ else return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get address */ ea = lgp_parse_addr (sim_devices[0], gbuf, &tptr); if ((tptr == gbuf) || (*tptr != 0) || (ea > AMASK)) return SCPE_ARG; val[0] = val[0] | (ea << I_V_EA); /* merge address */ if (*cptr != 0) return SCPE_2MARG; return SCPE_OK; } simh-3.8.1/LGP/lgp_defs.h0000644000175000017500000001233211111662476013225 0ustar vlmvlm/* lgp_defs.h: LGP simulator definitions Copyright (c) 2004-2008, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #ifndef _LGP_DEFS_H_ #define _LGP_DEFS_H_ 0 #include "sim_defs.h" /* simulator defns */ /* Simulator stop codes */ #define STOP_STOP 1 /* STOP */ #define STOP_IBKPT 2 /* breakpoint */ #define STOP_OVF 3 /* overflow */ #define STOP_NXDEV 4 /* non-existent device */ #define STOP_STALL 5 /* IO stall */ /* Memory */ #define MEMSIZE 4096 /* memory size */ #define AMASK 0xFFF /* addr mask */ #define NTK_30 64 #define NSC_30 64 #define SCMASK_30 0x03F /* sector mask */ #define NTK_21 32 #define NSC_21 128 #define SCMASK_21 0x07F #define RPM 4000 /* rev/minutes */ #define WPS ((NSC_30 * RPM) / 60) /* words/second */ /* Architectural constants */ #define SIGN 0x80000000 /* sign */ #define DMASK 0xFFFFFFFF /* data mask */ #define MMASK 0xFFFFFFFE /* memory mask */ /* Instruction format */ #define I_M_OP 0xF /* opcode */ #define I_V_OP 16 #define I_OP (I_M_OP << I_V_OP) #define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) #define I_M_EA AMASK /* address */ #define I_V_EA 2 #define I_EA (I_M_EA << I_V_EA) #define I_GETEA(x) (((x) >> I_V_EA) & I_M_EA) #define I_M_TK 0x3F /* LGP-30 char */ #define I_V_TK 8 /* LGP-21 device */ #define I_GETTK(x) (((x) >> I_V_TK) & I_M_TK) /* Unit flags */ #define UNIT_V_LGP21 (UNIT_V_UF + 0) #define UNIT_V_MANI (UNIT_V_UF + 1) #define UNIT_V_INPT (UNIT_V_UF + 2) #define UNIT_V_OUTPT (UNIT_V_UF + 3) #define UNIT_V_IN4B (UNIT_V_UF + 4) #define UNIT_V_TTSS_D (UNIT_V_UF + 5) #define UNIT_V_LGPH_D (UNIT_V_UF + 6) #define UNIT_V_FLEX_D (UNIT_V_UF + 7) /* Flex default */ #define UNIT_V_FLEX (UNIT_V_UF + 8) /* Flex format */ #define UNIT_V_NOCS (UNIT_V_UF + 9) /* ignore cond stop */ #define UNIT_LGP21 (1u << UNIT_V_LGP21) #define UNIT_MANI (1u << UNIT_V_MANI) #define UNIT_INPT (1u << UNIT_V_INPT) #define UNIT_OUTPT (1u << UNIT_V_OUTPT) #define UNIT_IN4B (1u << UNIT_V_IN4B) #define UNIT_TTSS_D (1u << UNIT_V_TTSS_D) #define UNIT_LGPH_D (1u << UNIT_V_LGPH_D) #define UNIT_FLEX_D (1u << UNIT_V_FLEX_D) #define UNIT_FLEX (1u << UNIT_V_FLEX) #define UNIT_NOCS (1u << UNIT_V_NOCS) #define Q_LGP21 (cpu_unit.flags & UNIT_LGP21) #define Q_MANI (cpu_unit.flags & UNIT_MANI) #define Q_INPT (cpu_unit.flags & UNIT_INPT) #define Q_OUTPT (cpu_unit.flags & UNIT_OUTPT) #define Q_IN4B (cpu_unit.flags & UNIT_IN4B) /* IO return */ #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ /* Significant characters */ #define FLEX_LC 0x04 #define FLEX_UC 0x08 #define FLEX_CR 0x10 #define FLEX_BS 0x14 #define FLEX_CSTOP 0x20 #define FLEX_DEL 0x3F /* LGP-21 device assignments */ #define DEV_PT 0 #define DEV_TT 2 #define DEV_MASK 0x1F #define DEV_SHIFT 62 /* Instructions */ enum opcodes { OP_Z, OP_B, OP_Y, OP_R, OP_I, OP_D, OP_N, OP_M, OP_P, OP_E, OP_U, OP_T, OP_H, OP_C, OP_A, OP_S }; /* Prototypes */ uint32 Read (uint32 ea); void Write (uint32 ea, uint32 dat); #endif simh-3.8.1/LGP/lgp_stddev.c0000644000175000017500000005511311113544526013571 0ustar vlmvlm/* lgp_stddev.c: LGP-30 standard devices Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti typewriter input (keyboard and reader) tto typewriter output (printer and punch) ptr high speed reader ptpp high speed punch 26-Nov-2008 RMS Changed encode character from # to ! */ #include "lgp_defs.h" #include uint32 tt_wait = WPS / 10; uint32 tti_buf = 0; uint32 tti_rdy = 0; uint32 tto_uc = 0; uint32 tto_buf = 0; uint32 ttr_stopioe = 1; uint32 ptr_rdy = 0; uint32 ptr_stopioe = 1; uint32 ptp_stopioe = 1; extern uint32 A; extern uint32 inp_strt, inp_done; extern uint32 out_strt, out_done; extern UNIT cpu_unit; extern int32 sim_switches; t_stat tti_svc (UNIT *uptr); t_stat ttr_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tti_reset (DEVICE *uptr); t_stat tto_reset (DEVICE *uptr); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *uptr); t_stat ptp_reset (DEVICE *uptr); t_stat tap_attach (UNIT *uptr, char *cptr); t_stat tap_attable (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat read_reader (UNIT *uptr, int32 stop, int32 *c); t_stat write_tto (int32 flex); t_stat write_punch (UNIT *uptr, int32 flex); t_stat tti_rdrss (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat punch_feed (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat send_start (UNIT *uptr, int32 val, char *cptr, void *desc); extern uint32 shift_in (uint32 a, uint32 dat, uint32 sh4); /* Conversion tables */ const int32 flex_to_ascii[128] = { -1 , 'z', '0', ' ', '>', 'b', '1', '-', '<' , 'y', '2', '+', '|', 'r', '3', ';', '\r', 'i', '4', '/','\\', 'd', '5', '.', '\t', 'n', '6', ',', -1 , 'm', '7', 'v', '\'', 'p', '8', 'o', -1 , 'e', '9', 'x', -1 , 'u', 'f', -1 , -1 , 't', 'g', -1 , -1 , 'h', 'j', -1 , -1 , 'c', 'k', -1 , -1 , 'a', 'q', -1 , -1 , 's', 'w', 0 , -1 , 'Z', ')', ' ', -1 , 'B', 'L', '_', -1 , 'Y', '*', '=', '|', 'R', '"', ':', '\r', 'I', '^', '?','\\', 'D', '%', ']', '\t', 'N', '$', '[', -1 , 'M', '~', 'V', '\'', 'P', '#', 'O', -1 , 'E', '(', 'X', -1 , 'U', 'F', -1 , -1 , 'T', 'G', -1 , -1 , 'H', 'J', -1 , -1 , 'C', 'K', -1 , -1 , 'A', 'Q', -1 , -1 , 'S', 'W', 0 }; const int32 ascii_to_flex[128] = { -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 024, 030, -1 , -1 , -1 , 020, -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 003, -1 , 016, 042, 032, 026, -1 , 040, 046, 001, 012, 013, 033, 007, 027, 023, 002, 006, 012, 016, 022, 026, 032, 036, 042, 046, 017, 017, 004, 013, 010, 023, -1 , 071, 005, 065, 025, 045, 052, 056, 061, 021, 062, 066, 006, 035, 031, 043, 041, 072, 015, 075, 055, 051, 037, 076, 047, 011, 001, 033, -1 , 027, 022, 007, -1, 071, 005, 065, 025, 045, 052, 056, 061, 021, 062, 066, 006, 035, 031, 043, 041, 072, 015, 075, 055, 051, 037, 076, 047, 011, 001, -1 , 014, -1 , 036, 077 }; static const uint8 flex_inp_valid[64] = { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 }; /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_mod TTI modifier list tti_reg TTI register list */ UNIT tti_unit[] = { { UDATA (&tti_svc, 0, 0) }, { UDATA (&ttr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) } }; REG tti_reg[] = { { HRDATA (BUF, tti_buf, 6) }, { FLDATA (RDY, tti_rdy, 0) }, { DRDATA (KPOS, tti_unit[0].pos, T_ADDR_W), PV_LEFT }, { DRDATA (RPOS, tti_unit[1].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tt_wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, ttr_stopioe, 0) }, { NULL } }; MTAB tti_mod[] = { { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, "file is Flex", NULL }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT, "file is ASCII", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, "default is Flex", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, "default is ASCII", NULL }, { UNIT_ATTABLE+UNIT_NOCS, UNIT_ATTABLE+UNIT_NOCS, "ignore conditional stop", "NOCSTOP", &tap_attable }, { UNIT_ATTABLE+UNIT_NOCS, UNIT_ATTABLE , NULL, "CSTOP", &tap_attable }, { MTAB_XTD|MTAB_VDV, 0, NULL, "START", &send_start }, { MTAB_XTD|MTAB_VDV, 1, NULL, "RSTART", &tti_rdrss }, { MTAB_XTD|MTAB_VDV, 0, NULL, "RSTOP", &tti_rdrss }, { 0 } }; DEVICE tti_dev = { "TTI", tti_unit, tti_reg, tti_mod, 2, 10, 31, 1, 16, 7, NULL, NULL, &tti_reset, NULL, &tap_attach, NULL }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_mod TTO modifier list tto_reg TTO register list */ UNIT tto_unit[] = { { UDATA (&tto_svc, 0, 0) }, { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } }; REG tto_reg[] = { { HRDATA (BUF, tto_buf, 6) }, { FLDATA (UC, tto_uc, 0) }, { DRDATA (TPOS, tto_unit[0].pos, T_ADDR_W), PV_LEFT }, { DRDATA (PPOS, tto_unit[1].pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tt_wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, "file is Flex", NULL }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT, "file is ASCII", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, "default is Flex", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, "default is ASCII", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "FEED", &punch_feed }, { 0 } }; DEVICE tto_dev = { "TTO", tto_unit, tto_reg, tto_mod, 2, 10, 31, 1, 16, 7, NULL, NULL, &tto_reset, NULL, &tap_attach, NULL }; /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_mod PTR modifier list ptr_reg PTR register list */ UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), WPS / 200 }; REG ptr_reg[] = { { HRDATA (BUF, ptr_unit.buf, 6) }, { FLDATA (RDY, ptr_rdy, 0) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { NULL } }; MTAB ptr_mod[] = { { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, "file is Flex", NULL }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT, "file is ASCII", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, "default is Flex", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, "default is ASCII", NULL }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 16, 7, NULL, NULL, &ptr_reset, NULL, &tap_attach, NULL }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_mod PTP modifier list ptp_reg PTP register list */ UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), WPS / 20 }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { 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[] = { { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, "file is Flex", NULL }, { UNIT_ATT+UNIT_FLEX, UNIT_ATT, "file is ASCII", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, "default is Flex", NULL }, { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, "default is ASCII", NULL }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 16, 7, NULL, NULL, &ptp_reset, NULL, &tap_attach, NULL }; /* Input instruction */ void op_i_strt (uint32 dev) { switch (dev) { /* case on device */ case DEV_PT: /* ptr */ sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ break; case DEV_TT: /* tti/ttr */ if (Q_MANI) /* manual input? */ sim_putchar ('`'); else sim_activate (&tti_unit[1], tt_wait); /* no, must be ttr */ break; } return; } t_stat op_i (uint32 dev, uint32 ch, uint32 sh4) { if (Q_LGP21 && out_strt) /* LGP-21? must be idle */ return STOP_STALL; if (!inp_strt) { /* input started? */ inp_strt = 1; /* no, set start */ inp_done = 0; /* clear done */ A = shift_in (A, ch, sh4); tti_rdy = ptr_rdy = 0; /* no input */ if (Q_LGP21 || Q_INPT) /* LGP-21 or PTR? start */ op_i_strt (dev); } switch (dev) { /* case on device */ case DEV_PT: /* ptr */ if (ptr_rdy) { /* char ready? */ ptr_rdy = 0; /* reset ready */ if ((ptr_unit.buf != FLEX_DEL) && /* ignore delete and */ (!Q_LGP21 || ((ptr_unit.buf & 3) == 2))) /* LGP-21 4b? zone != 2 */ A = shift_in (A, ptr_unit.buf, sh4); /* shift data in */ } break; case DEV_TT: /* tti/ttr */ if (tti_rdy) { /* char ready? */ tti_rdy = 0; /* reset ready */ if ((tti_buf != FLEX_DEL) && /* ignore delete and */ (!Q_LGP21 || ((tti_buf & 3) != 0))) /* LGP-21 4b? zone == 0 */ A = shift_in (A, tti_buf, sh4); /* shift data in */ } break; default: /* nx device */ return STOP_NXDEV; /* return error */ } if (inp_done) { /* done? */ inp_strt = inp_done = 0; /* clear start, done */ return SCPE_OK; /* no stall */ } return STOP_STALL; /* stall */ } /* Terminal keyboard unit service */ t_stat tti_svc (UNIT *uptr) { int32 c, flex; sim_activate (uptr, tt_wait); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; flex = ascii_to_flex[c & 0x1FF]; /* cvt to flex */ if (flex > 0) { /* it's a typewriter... */ write_tto (flex); /* always echos */ if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ write_punch (&tto_unit[1], tto_buf); /* punch to ttp */ } else write_tto ('\a'); /* don't echo bad */ if (Q_MANI && (flex > 0) && flex_inp_valid[flex]) { /* wanted, valid? */ if (flex == FLEX_CSTOP) /* conditional stop? */ inp_done = 1; else tti_rdy = 1; /* no, set ready */ tti_buf = flex; /* save char */ uptr->pos = uptr->pos + 1; } return SCPE_OK; } /* Terminal reader unit service */ t_stat ttr_svc (UNIT *uptr) { t_stat r; if (r = read_reader (uptr, ttr_stopioe, (int32 *) &tti_buf)) return r; if (!(uptr->flags & UNIT_NOCS) && /* cstop enable? */ (tti_buf == FLEX_CSTOP)) /* cond stop? */ inp_done = 1; else { tti_rdy = 1; /* no, set ready */ sim_activate (uptr, tt_wait); /* cont reading */ } write_tto (tti_buf); /* echo to tto */ if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ return write_punch (&tto_unit[1], tti_buf); /* punch to ttp */ return SCPE_OK; } /* Paper tape reader unit service */ t_stat ptr_svc (UNIT *uptr) { t_stat r; if (r = read_reader (uptr, ptr_stopioe, &uptr->buf)) return r; if (uptr->buf == FLEX_CSTOP) /* cond stop? */ inp_done = 1; else { ptr_rdy = 1; /* no, set ready */ sim_activate (uptr, uptr->wait); /* cont reading */ } return SCPE_OK; } /* Output instruction */ t_stat op_p (uint32 dev, uint32 ch) { switch (dev) { /* case on device */ case DEV_PT: /* paper tape punch */ if (sim_is_active (&ptp_unit)) /* busy? */ return (Q_LGP21? STOP_STALL: SCPE_OK); /* LGP-21: stall */ ptp_unit.buf = ch; /* save char */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate ptp */ break; case DEV_TT: /* typewriter */ if (ch == 0) { /* start input? */ if (!Q_LGP21 && !Q_INPT) /* ignore if LGP-21, ptr */ op_i_strt (DEV_TT); /* start tti */ return SCPE_OK; /* no stall */ } if (sim_is_active (&tto_unit[0])) /* busy? */ return (Q_LGP21? STOP_STALL: SCPE_OK); /* LGP-21: stall */ tto_buf = ch; /* save char */ sim_activate (&tto_unit[0], tt_wait); /* activate tto */ break; default: /* unknown */ return STOP_NXDEV; /* return error */ } if (out_strt == 0) { /* output started? */ out_strt = 1; /* flag start */ out_done = 0; /* clear done */ } return SCPE_OK; /* no stall */ } /* Terminal printer unit service */ t_stat tto_svc (UNIT *uptr) { t_stat r; if ((r = write_tto (tto_buf)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, tt_wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } out_strt = 0; out_done = 1; if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ return write_punch (&tto_unit[1], tto_buf); /* punch to ttp */ return SCPE_OK; } /* Paper tape punch unit service */ t_stat ptp_svc (UNIT *uptr) { out_strt = 0; out_done = 1; if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); /* error */ return write_punch (uptr, uptr->buf); /* write to ptp */ } /* Utility routines */ t_stat read_reader (UNIT *uptr, int32 stop, int32 *fl) { int32 ch, flex; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (stop, SCPE_UNATT); do { if ((ch = getc (uptr->fileref)) == EOF) { /* read char */ if (feof (uptr->fileref)) { /* err or eof? */ if (stop) printf ("Reader end of file\n"); else return SCPE_OK; } else perror ("Reader I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } if (uptr->flags & UNIT_FLEX) /* transposed flex? */ flex = ((ch << 1) | (ch >> 5)) & 0x3F; /* undo 612345 */ else if (ch == '!') { /* encoded? */ int32 d1 = getc (uptr->fileref); /* get 2 digits */ int32 d2 = getc (uptr->fileref); if ((d1 == EOF) || (d2 == EOF)) { /* error? */ if (feof (uptr->fileref)) { /* eof? */ if (stop) printf ("Reader end of file\n"); else return SCPE_OK; } else perror ("Reader I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } flex = (((d1 - '0') * 10) + (d2 - '0')) & 0x3F; uptr->pos = uptr->pos + 2; } else flex = ascii_to_flex[ch & 0x7F]; /* convert */ uptr->pos = uptr->pos + 1; } while (flex < 0); /* until valid */ *fl = flex; /* return char */ return SCPE_OK; } t_stat write_tto (int32 flex) { int32 ch; t_stat r; if (flex == FLEX_UC) /* UC? set state */ tto_uc = 1; else if (flex == FLEX_LC) /* LC? set state */ tto_uc = 0; else { if (flex == FLEX_BS) /* backspace? */ ch = '\b'; else ch = flex_to_ascii[flex | (tto_uc << 6)]; /* cvt flex to ascii */ if (ch > 0) { /* legit? */ if (r = sim_putchar_s (ch)) /* write char */ return r; tto_unit[0].pos = tto_unit[0].pos + 1; if (flex == FLEX_CR) { /* cr? */ sim_putchar ('\n'); /* add lf */ tto_unit[0].pos = tto_unit[0].pos + 1; } } } return SCPE_OK; } t_stat write_punch (UNIT *uptr, int32 flex) { int32 c, sta; if (uptr->flags & UNIT_FLEX) /* transposed flex? */ c = ((flex >> 1) | (flex << 5)) & 0x3F; /* reorder to 612345 */ else c = flex_to_ascii[flex]; /* convert to ASCII */ if (c >= 0) /* valid? */ sta = fputc (c, uptr->fileref); else sta = fprintf (uptr->fileref, "!%02d", flex); /* no, encode */ if (sta == EOF) { /* error? */ perror ("Punch I/O error"); /* error? */ clearerr (uptr->fileref); return SCPE_IOERR; } uptr->pos = uptr->pos + ((c >= 0)? 1: 3); /* incr position */ return SCPE_OK; } /* Reset routines */ t_stat tti_reset (DEVICE *dptr) { sim_activate (&tti_unit[0], tt_wait); sim_cancel (&tti_unit[1]); tti_buf = 0; tti_rdy = 0; return SCPE_OK; } t_stat tto_reset (DEVICE *dptr) { sim_cancel (&tto_unit[0]); tto_buf = 0; tto_uc = 0; return SCPE_OK; } t_stat ptr_reset (DEVICE *dptr) { sim_cancel (&ptr_unit); ptr_unit.buf = 0; ptr_rdy = 0; return SCPE_OK; } t_stat ptp_reset (DEVICE *dptr) { sim_cancel (&ptp_unit); ptp_unit.buf = 0; return SCPE_OK; } /* Attach paper tape unit */ t_stat tap_attach (UNIT *uptr, char *cptr) { t_stat r; if ((r = attach_unit (uptr,cptr)) != SCPE_OK) return r; if ((sim_switches & SWMASK ('F')) || ((uptr->flags & UNIT_FLEX_D) && !(sim_switches & SWMASK ('A')))) uptr->flags = uptr->flags | UNIT_FLEX; else uptr->flags = uptr->flags & ~UNIT_FLEX; return SCPE_OK; } /* Validate unit is attachable */ t_stat tap_attable (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATTABLE) return SCPE_OK; return SCPE_NOFNC; } /* Typewriter reader start/stop */ t_stat tti_rdrss (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val) { if ((tti_unit[1].flags & UNIT_ATT) == 0) return SCPE_UNATT; sim_activate (&tti_unit[1], tt_wait); } else sim_cancel (&tti_unit[1]); return SCPE_OK; } /* Punch feed routine */ t_stat punch_feed (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 cnt; t_stat r; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; if (cptr) { cnt = (int32) get_uint (cptr, 10, 512, &r); if ((r != SCPE_OK) || (cnt == 0)) return SCPE_ARG; } else cnt = 10; while (cnt-- > 0) { r = write_punch (uptr, 0); if (r != SCPE_OK) return r; } return SCPE_OK; } /* Send start signal */ t_stat send_start (UNIT *uptr, int32 val, char *cptr, void *desc) { if (inp_strt) inp_done = 1; else if (out_strt) out_done = 1; return SCPE_OK; } simh-3.8.1/PDP11/0000755000175000017500000000000011143602146011424 5ustar vlmvlmsimh-3.8.1/PDP11/pdp11_cpu.c0000644000175000017500000036212611126236314013377 0ustar vlmvlm/* pdp11_cpu.c: PDP-11 CPU simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. cpu PDP-11 CPU 29-Dec-08 RMS Fixed failure to clear cpu_bme on RESET (found by Walter Mueller) 22-Apr-08 RMS Fixed MMR0 treatment in RESET (found by Walter Mueller) 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) 28-Apr-07 RMS Removed clock initialization 27-Oct-06 RMS Added idle support 18-Oct-06 RMS Fixed bug in ASH -32 C value 24-May-06 RMS Added instruction history 03-May-06 RMS Fixed XOR operand fetch order for 11/70-style systems 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 19-May-05 RMS Replaced WAIT clock queue check with API call 19-Jan-05 RMS Fixed bug(s) in RESET for 11/70 (reported by Tim Chapman) 22-Dec-04 RMS Fixed WAIT to work in all modes (from John Dundas) 02-Oct-04 RMS Added model emulation 25-Jan-04 RMS Removed local debug logging support 29-Dec-03 RMS Formalized 18b Qbus support 21-Dec-03 RMS Added autoconfiguration controls 05-Jun-03 RMS Fixed bugs in memory size table 12-Mar-03 RMS Added logical name support 01-Feb-03 RMS Changed R display to follow PSW, added SP display 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict 05-Jan-03 RMS Added memory size restore support 17-Oct-02 RMS Fixed bug in examine/deposit (found by Hans Pufal) 08-Oct-02 RMS Revised to build dib_tab dynamically Added SHOW IOSPACE 09-Sep-02 RMS Added KW11P support 14-Jul-02 RMS Fixed bug in MMR0 error status load 03-Jun-02 RMS Fixed relocation add overflow, added PS<15:12> = 1111 special case logic to MFPI and removed it from MTPI (found by John Dundas) 29-Apr-02 RMS More fixes to DIV and ASH/ASHC (found by John Dundas) 28-Apr-02 RMS Fixed bugs in illegal instruction 000010 and in write-only memory pages (found by Wolfgang Helbig) 21-Apr-02 RMS Fixed bugs in DIV by zero, DIV overflow, TSTSET, RTS, ASHC -32, and red zone trap (found by John Dundas) 04-Mar-02 RMS Changed double operand evaluation order for M+ 23-Feb-02 RMS Fixed bug in MAINT, CPUERR, MEMERR read 28-Jan-02 RMS Revised for multiple timers; fixed calc_MMR1 macros 06-Jan-02 RMS Revised enable/disable support 30-Dec-01 RMS Added old PC queue 25-Dec-01 RMS Cleaned up sim_inst declarations 11-Dec-01 RMS Moved interrupt debug code 07-Dec-01 RMS Revised to use new breakpoint package 08-Nov-01 RMS Moved I/O to external module 26-Oct-01 RMS Revised to use symbolic definitions for IO page 15-Oct-01 RMS Added debug logging 08-Oct-01 RMS Fixed bug in revised interrupt logic 07-Sep-01 RMS Revised device disable and interrupt mechanisms 26-Aug-01 RMS Added DZ11 support 10-Aug-01 RMS Removed register from declarations 17-Jul-01 RMS Fixed warning from VC++ 6.0 01-Jun-01 RMS Added DZ11 interrupts 23-Apr-01 RMS Added RK611 support 05-Apr-01 RMS Added TS11/TSV05 support 05-Mar-01 RMS Added clock calibration support 11-Feb-01 RMS Added DECtape support 25-Jan-01 RMS Fixed 4M memory definition (found by Eric Smith) 14-Apr-99 RMS Changed t_addr to unsigned 18-Aug-98 RMS Added CIS support 09-May-98 RMS Fixed bug in DIV overflow test 19-Jan-97 RMS Added RP/RM support 06-Apr-96 RMS Added dynamic memory sizing 29-Feb-96 RMS Added TM11 support 17-Jul-94 RMS Corrected updating of MMR1 if MMR0 locked The register state for the PDP-11 is: REGFILE[0:5][0] general register set REGFILE[0:5][1] alternate general register set STACKFILE[4] stack pointers for kernel, supervisor, unused, user PC program counter PSW processor status word <15:14> = CM current processor mode <13:12> = PM previous processor mode <11> = RS register set select <8> = FPD first part done (CIS) <7:5> = IPL interrupt priority level <4> = TBIT trace trap enable <3:0> = NZVC condition codes FR[0:5] floating point accumulators FPS floating point status register FEC floating exception code FEA floating exception address MMR0,1,2,3 memory management control registers APRFILE[0:63] memory management relocation registers for kernel, supervisor, unused, user <31:16> = PAR processor address registers <15:0> = PDR processor data registers PIRQ processor interrupt request register CPUERR CPU error register MEMERR memory system error register CCR cache control register MAINT maintenance register HITMISS cache status register SR switch register DR display register The PDP-11 has many instruction formats: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ double operand | opcode | source spec | dest spec | 010000:067777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 110000:167777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand | opcode | src reg| dest spec | 004000:004777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 070000:077777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single operand | opcode | dest spec | 000100:000177 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000300:000377 005000:007777 105000:107777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single register | opcode |dest reg| 000200:000207 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000230:000237 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand | opcode | 000000:000007 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ branch | opcode | branch displacement | 000400:003477 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 100000:103477 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ EMT/TRAP | opcode | trap code | 104000:104777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ cond code operator | opcode | immediate | 000240:000277 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ An operand specifier consists of an addressing mode and a register. The addressing modes are: 0 register direct R op = R 1 register deferred (R) op = M[R] 2 autoincrement (R)+ op = M[R]; R = R + length 3 autoincrement deferred @(R)+ op = M[M[R]]; R = R + 2 4 autodecrement -(R) R = R - length; op = M[R] 5 autodecrement deferred @-(R) R = R - 2; op = M[M[R]] 6 displacement d(R) op = M[R + disp] 7 displacement deferred @d(R) op = M[M[R + disp]] There are eight general registers, R0-R7. R6 is the stack pointer, R7 the PC. The combination of addressing modes with R7 yields: 27 immediate #n op = M[PC]; PC = PC + 2 37 absolute @#n op = M[M[PC]]; PC = PC + 2 67 relative d(PC) op = M[PC + disp] 77 relative deferred @d(PC) op = M[M[PC + disp]] This routine is the instruction decode routine for the PDP-11. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until an enabled exception is encountered. General notes: 1. Virtual address format. PDP-11 memory management uses the 16b virtual address, the type of reference (instruction or data), and the current mode, to construct the 22b physical address. To package this conveniently, the simulator uses a 19b pseudo virtual address, consisting of the 16b virtual address prefixed with the current mode and ispace/dspace indicator. These are precalculated as isenable and dsenable for ispace and dspace, respectively, and must be recalculated whenever MMR0, MMR3, or PSW changes. 2. Traps and interrupts. Variable trap_req bit-encodes all possible traps. In addition, an interrupt pending bit is encoded as the lowest priority trap. Traps are processed by trap_vec and trap_clear, which provide the vector and subordinate traps to clear, respectively. Array int_req[0:7] bit encodes all possible interrupts. It is masked under the interrupt priority level, ipl. If any interrupt request is not masked, the interrupt bit is set in trap_req. While most interrupts are handled centrally, a device can supply an interrupt acknowledge routine. 3. PSW handling. The PSW is kept as components, for easier access. Because the PSW can be explicitly written as address 17777776, all instructions must update PSW before executing their last write. 4. Adding I/O devices. These modules must be modified: pdp11_defs.h add device address and interrupt definitions pdp11_sys.c add to sim_devices table entry */ /* Definitions */ #include "pdp11_defs.h" #include "pdp11_cpumod.h" #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 calc_is(md) ((md) << VA_V_MODE) #define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0)) #define calc_MMR1(val) ((MMR1)? (((val) << 8) | MMR1): (val)) #define GET_SIGN_W(v) (((v) >> 15) & 1) #define GET_SIGN_B(v) (((v) >> 7) & 1) #define GET_Z(v) ((v) == 0) #define JMP_PC(x) PCQ_ENTRY; PC = (x) #define BRANCH_F(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) & 0377)) & 0177777 #define BRANCH_B(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) | 0177400)) & 0177777 #define last_pa (cpu_unit.u4) /* auto save/rest */ #define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy */ #define UNIT_MSIZE (1u << UNIT_V_MSIZE) #define HIST_MIN 64 #define HIST_MAX (1u << 18) #define HIST_VLD 1 /* make PC odd */ #define HIST_ILNT 4 /* max inst length */ typedef struct { uint16 pc; uint16 psw; uint16 src; uint16 dst; uint16 inst[HIST_ILNT]; } InstHistory; /* Global state */ extern FILE *sim_log; uint16 *M = NULL; /* memory */ int32 REGFILE[6][2] = { 0 }; /* R0-R5, two sets */ int32 STACKFILE[4] = { 0 }; /* SP, four modes */ int32 saved_PC = 0; /* program counter */ int32 R[8] = { 0 }; /* working registers */ int32 PSW = 0; /* PSW */ int32 cm = 0; /* current mode */ int32 pm = 0; /* previous mode */ int32 rs = 0; /* register set */ int32 fpd = 0; /* first part done */ int32 ipl = 0; /* int pri level */ int32 tbit = 0; /* trace flag */ int32 N = 0, Z = 0, V = 0, C = 0; /* condition codes */ int32 wait_state = 0; /* wait state */ int32 trap_req = 0; /* trap requests */ int32 int_req[IPL_HLVL] = { 0 }; /* interrupt requests */ int32 PIRQ = 0; /* programmed int req */ int32 STKLIM = 0; /* stack limit */ fpac_t FR[6] = { 0 }; /* fp accumulators */ int32 FPS = 0; /* fp status */ int32 FEC = 0; /* fp exception code */ int32 FEA = 0; /* fp exception addr */ int32 APRFILE[64] = { 0 }; /* PARs/PDRs */ int32 MMR0 = 0; /* MMR0 - status */ int32 MMR1 = 0; /* MMR1 - R+/-R */ int32 MMR2 = 0; /* MMR2 - saved PC */ int32 MMR3 = 0; /* MMR3 - 22b status */ int32 cpu_bme = 0; /* bus map enable */ int32 cpu_astop = 0; /* address stop */ int32 isenable = 0, dsenable = 0; /* i, d space flags */ int32 stop_trap = 1; /* stop on trap */ int32 stop_vecabort = 1; /* stop on vec abort */ int32 stop_spabort = 1; /* stop on SP abort */ int32 wait_enable = 0; /* wait state enable */ int32 autcon_enb = 1; /* autoconfig enable */ uint32 cpu_model = MOD_1173; /* CPU model */ uint32 cpu_type = 1u << MOD_1173; /* model as bit mask */ uint32 cpu_opt = SOP_1173; /* CPU options */ uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ jmp_buf save_env; /* abort handler */ int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ int32 dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS }; /* dspace enables */ t_addr cpu_memsize = INIMEMSIZE; /* last mem addr */ extern int32 CPUERR, MAINT; extern int32 sim_interval; extern int32 sim_int_char; extern uint32 sim_switches; extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ extern t_bool sim_idle_enab; extern DEVICE *sim_devices[]; extern CPUTAB cpu_tab[]; /* Function declarations */ 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_hist (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); int32 GeteaB (int32 spec); int32 GeteaW (int32 spec); int32 relocR (int32 addr); int32 relocW (int32 addr); void relocR_test (int32 va, int32 apridx); void relocW_test (int32 va, int32 apridx); t_bool PLF_test (int32 va, int32 apr); void reloc_abort (int32 err, int32 apridx); int32 ReadE (int32 addr); int32 ReadW (int32 addr); int32 ReadB (int32 addr); int32 ReadMW (int32 addr); int32 ReadMB (int32 addr); void WriteW (int32 data, int32 addr); void WriteB (int32 data, int32 addr); void PWriteW (int32 data, int32 addr); void PWriteB (int32 data, int32 addr); void set_r_display (int32 rs, int32 cm); t_stat CPU_wr (int32 data, int32 addr, int32 access); void set_stack_trap (int32 adr); int32 get_PSW (void); void put_PSW (int32 val, t_bool prot); void put_PIRQ (int32 val); extern void fp11 (int32 IR); extern t_stat cis11 (int32 IR); extern t_stat fis11 (int32 IR); extern t_stat build_dib_tab (void); extern t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat iopageR (int32 *data, uint32 addr, int32 access); extern t_stat iopageW (int32 data, uint32 addr, int32 access); extern int32 calc_ints (int32 nipl, int32 trq); extern int32 get_vector (int32 nipl); /* Trap data structures */ int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */ VEC_RED, VEC_ODD, VEC_MME, VEC_NXM, VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT, VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC, VEC_YEL, VEC_PWRFL, VEC_FPE }; int32 trap_clear[TRAP_V_MAX] = { /* trap clears */ TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_ODD+TRAP_NXM, TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC, TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC, TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC, TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC, TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC, TRAP_TRAP+TRAP_TRC, TRAP_TRC, TRAP_YEL, TRAP_PWRFL, TRAP_FPE }; /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifier list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK, INIMEMSIZE) }; REG cpu_reg[] = { { ORDATA (PC, saved_PC, 16) }, { ORDATA (R0, REGFILE[0][0], 16) }, { ORDATA (R1, REGFILE[1][0], 16) }, { ORDATA (R2, REGFILE[2][0], 16) }, { ORDATA (R3, REGFILE[3][0], 16) }, { ORDATA (R4, REGFILE[4][0], 16) }, { ORDATA (R5, REGFILE[5][0], 16) }, { ORDATA (SP, STACKFILE[MD_KER], 16) }, { ORDATA (R00, REGFILE[0][0], 16) }, { ORDATA (R01, REGFILE[1][0], 16) }, { ORDATA (R02, REGFILE[2][0], 16) }, { ORDATA (R03, REGFILE[3][0], 16) }, { ORDATA (R04, REGFILE[4][0], 16) }, { ORDATA (R05, REGFILE[5][0], 16) }, { ORDATA (R10, REGFILE[0][1], 16) }, { ORDATA (R11, REGFILE[1][1], 16) }, { ORDATA (R12, REGFILE[2][1], 16) }, { ORDATA (R13, REGFILE[3][1], 16) }, { ORDATA (R14, REGFILE[4][1], 16) }, { ORDATA (R15, REGFILE[5][1], 16) }, { ORDATA (KSP, STACKFILE[MD_KER], 16) }, { ORDATA (SSP, STACKFILE[MD_SUP], 16) }, { ORDATA (USP, STACKFILE[MD_USR], 16) }, { ORDATA (PSW, PSW, 16) }, { GRDATA (CM, PSW, 8, 2, PSW_V_CM) }, { GRDATA (PM, PSW, 8, 2, PSW_V_PM) }, { FLDATA (RS, PSW, PSW_V_RS) }, { FLDATA (FPD, PSW, PSW_V_FPD) }, { GRDATA (IPL, PSW, 8, 3, PSW_V_IPL) }, { FLDATA (T, PSW, PSW_V_TBIT) }, { FLDATA (N, PSW, PSW_V_N) }, { FLDATA (Z, PSW, PSW_V_Z) }, { FLDATA (V, PSW, PSW_V_V) }, { FLDATA (C, PSW, PSW_V_C) }, { ORDATA (PIRQ, PIRQ, 16) }, { ORDATA (STKLIM, STKLIM, 16) }, { ORDATA (FAC0H, FR[0].h, 32) }, { ORDATA (FAC0L, FR[0].l, 32) }, { ORDATA (FAC1H, FR[1].h, 32) }, { ORDATA (FAC1L, FR[1].l, 32) }, { ORDATA (FAC2H, FR[2].h, 32) }, { ORDATA (FAC2L, FR[2].l, 32) }, { ORDATA (FAC3H, FR[3].h, 32) }, { ORDATA (FAC3L, FR[3].l, 32) }, { ORDATA (FAC4H, FR[4].h, 32) }, { ORDATA (FAC4L, FR[4].l, 32) }, { ORDATA (FAC5H, FR[5].h, 32) }, { ORDATA (FAC5L, FR[5].l, 32) }, { ORDATA (FPS, FPS, 16) }, { ORDATA (FEA, FEA, 16) }, { ORDATA (FEC, FEC, 4) }, { ORDATA (MMR0, MMR0, 16) }, { ORDATA (MMR1, MMR1, 16) }, { ORDATA (MMR2, MMR2, 16) }, { ORDATA (MMR3, MMR3, 16) }, { GRDATA (KIPAR0, APRFILE[000], 8, 16, 16) }, { GRDATA (KIPDR0, APRFILE[000], 8, 16, 0) }, { GRDATA (KIPAR1, APRFILE[001], 8, 16, 16) }, { GRDATA (KIPDR1, APRFILE[001], 8, 16, 0) }, { GRDATA (KIPAR2, APRFILE[002], 8, 16, 16) }, { GRDATA (KIPDR2, APRFILE[002], 8, 16, 0) }, { GRDATA (KIPAR3, APRFILE[003], 8, 16, 16) }, { GRDATA (KIPDR3, APRFILE[003], 8, 16, 0) }, { GRDATA (KIPAR4, APRFILE[004], 8, 16, 16) }, { GRDATA (KIPDR4, APRFILE[004], 8, 16, 0) }, { GRDATA (KIPAR5, APRFILE[005], 8, 16, 16) }, { GRDATA (KIPDR5, APRFILE[005], 8, 16, 0) }, { GRDATA (KIPAR6, APRFILE[006], 8, 16, 16) }, { GRDATA (KIPDR6, APRFILE[006], 8, 16, 0) }, { GRDATA (KIPAR7, APRFILE[007], 8, 16, 16) }, { GRDATA (KIPDR7, APRFILE[007], 8, 16, 0) }, { GRDATA (KDPAR0, APRFILE[010], 8, 16, 16) }, { GRDATA (KDPDR0, APRFILE[010], 8, 16, 0) }, { GRDATA (KDPAR1, APRFILE[011], 8, 16, 16) }, { GRDATA (KDPDR1, APRFILE[011], 8, 16, 0) }, { GRDATA (KDPAR2, APRFILE[012], 8, 16, 16) }, { GRDATA (KDPDR2, APRFILE[012], 8, 16, 0) }, { GRDATA (KDPAR3, APRFILE[013], 8, 16, 16) }, { GRDATA (KDPDR3, APRFILE[013], 8, 16, 0) }, { GRDATA (KDPAR4, APRFILE[014], 8, 16, 16) }, { GRDATA (KDPDR4, APRFILE[014], 8, 16, 0) }, { GRDATA (KDPAR5, APRFILE[015], 8, 16, 16) }, { GRDATA (KDPDR5, APRFILE[015], 8, 16, 0) }, { GRDATA (KDPAR6, APRFILE[016], 8, 16, 16) }, { GRDATA (KDPDR6, APRFILE[016], 8, 16, 0) }, { GRDATA (KDPAR7, APRFILE[017], 8, 16, 16) }, { GRDATA (KDPDR7, APRFILE[017], 8, 16, 0) }, { GRDATA (SIPAR0, APRFILE[020], 8, 16, 16) }, { GRDATA (SIPDR0, APRFILE[020], 8, 16, 0) }, { GRDATA (SIPAR1, APRFILE[021], 8, 16, 16) }, { GRDATA (SIPDR1, APRFILE[021], 8, 16, 0) }, { GRDATA (SIPAR2, APRFILE[022], 8, 16, 16) }, { GRDATA (SIPDR2, APRFILE[022], 8, 16, 0) }, { GRDATA (SIPAR3, APRFILE[023], 8, 16, 16) }, { GRDATA (SIPDR3, APRFILE[023], 8, 16, 0) }, { GRDATA (SIPAR4, APRFILE[024], 8, 16, 16) }, { GRDATA (SIPDR4, APRFILE[024], 8, 16, 0) }, { GRDATA (SIPAR5, APRFILE[025], 8, 16, 16) }, { GRDATA (SIPDR5, APRFILE[025], 8, 16, 0) }, { GRDATA (SIPAR6, APRFILE[026], 8, 16, 16) }, { GRDATA (SIPDR6, APRFILE[026], 8, 16, 0) }, { GRDATA (SIPAR7, APRFILE[027], 8, 16, 16) }, { GRDATA (SIPDR7, APRFILE[027], 8, 16, 0) }, { GRDATA (SDPAR0, APRFILE[030], 8, 16, 16) }, { GRDATA (SDPDR0, APRFILE[030], 8, 16, 0) }, { GRDATA (SDPAR1, APRFILE[031], 8, 16, 16) }, { GRDATA (SDPDR1, APRFILE[031], 8, 16, 0) }, { GRDATA (SDPAR2, APRFILE[032], 8, 16, 16) }, { GRDATA (SDPDR2, APRFILE[032], 8, 16, 0) }, { GRDATA (SDPAR3, APRFILE[033], 8, 16, 16) }, { GRDATA (SDPDR3, APRFILE[033], 8, 16, 0) }, { GRDATA (SDPAR4, APRFILE[034], 8, 16, 16) }, { GRDATA (SDPDR4, APRFILE[034], 8, 16, 0) }, { GRDATA (SDPAR5, APRFILE[035], 8, 16, 16) }, { GRDATA (SDPDR5, APRFILE[035], 8, 16, 0) }, { GRDATA (SDPAR6, APRFILE[036], 8, 16, 16) }, { GRDATA (SDPDR6, APRFILE[036], 8, 16, 0) }, { GRDATA (SDPAR7, APRFILE[037], 8, 16, 16) }, { GRDATA (SDPDR7, APRFILE[037], 8, 16, 0) }, { GRDATA (UIPAR0, APRFILE[060], 8, 16, 16) }, { GRDATA (UIPDR0, APRFILE[060], 8, 16, 0) }, { GRDATA (UIPAR1, APRFILE[061], 8, 16, 16) }, { GRDATA (UIPDR1, APRFILE[061], 8, 16, 0) }, { GRDATA (UIPAR2, APRFILE[062], 8, 16, 16) }, { GRDATA (UIPDR2, APRFILE[062], 8, 16, 0) }, { GRDATA (UIPAR3, APRFILE[063], 8, 16, 16) }, { GRDATA (UIPDR3, APRFILE[063], 8, 16, 0) }, { GRDATA (UIPAR4, APRFILE[064], 8, 16, 16) }, { GRDATA (UIPDR4, APRFILE[064], 8, 16, 0) }, { GRDATA (UIPAR5, APRFILE[065], 8, 16, 16) }, { GRDATA (UIPDR5, APRFILE[065], 8, 16, 0) }, { GRDATA (UIPAR6, APRFILE[066], 8, 16, 16) }, { GRDATA (UIPDR6, APRFILE[066], 8, 16, 0) }, { GRDATA (UIPAR7, APRFILE[067], 8, 16, 16) }, { GRDATA (UIPDR7, APRFILE[067], 8, 16, 0) }, { GRDATA (UDPAR0, APRFILE[070], 8, 16, 16) }, { GRDATA (UDPDR0, APRFILE[070], 8, 16, 0) }, { GRDATA (UDPAR1, APRFILE[071], 8, 16, 16) }, { GRDATA (UDPDR1, APRFILE[071], 8, 16, 0) }, { GRDATA (UDPAR2, APRFILE[072], 8, 16, 16) }, { GRDATA (UDPDR2, APRFILE[072], 8, 16, 0) }, { GRDATA (UDPAR3, APRFILE[073], 8, 16, 16) }, { GRDATA (UDPDR3, APRFILE[073], 8, 16, 0) }, { GRDATA (UDPAR4, APRFILE[074], 8, 16, 16) }, { GRDATA (UDPDR4, APRFILE[074], 8, 16, 0) }, { GRDATA (UDPAR5, APRFILE[075], 8, 16, 16) }, { GRDATA (UDPDR5, APRFILE[075], 8, 16, 0) }, { GRDATA (UDPAR6, APRFILE[076], 8, 16, 16) }, { GRDATA (UDPDR6, APRFILE[076], 8, 16, 0) }, { GRDATA (UDPAR7, APRFILE[077], 8, 16, 16) }, { GRDATA (UDPDR7, APRFILE[077], 8, 16, 0) }, { BRDATA (IREQ, int_req, 8, 32, IPL_HLVL), REG_RO }, { ORDATA (TRAPS, trap_req, TRAP_V_MAX) }, { FLDATA (WAIT, wait_state, 0) }, { FLDATA (WAIT_ENABLE, wait_enable, 0), REG_HIDDEN }, { ORDATA (STOP_TRAPS, stop_trap, TRAP_V_MAX) }, { FLDATA (STOP_VECA, stop_vecabort, 0) }, { FLDATA (STOP_SPA, stop_spabort, 0) }, { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { ORDATA (WRU, sim_int_char, 8) }, { ORDATA (MODEL, cpu_model, 16), REG_HRO }, { ORDATA (OPTIONS, cpu_opt, 32), REG_HRO }, { NULL} }; MTAB cpu_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL, NULL, &cpu_show_model }, { MTAB_XTD|MTAB_VDV, MOD_1103, NULL, "11/03", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1104, NULL, "11/04", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1105, NULL, "11/05", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1120, NULL, "11/20", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1123, NULL, "11/23", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1123P, NULL, "11/23+", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1124, NULL, "11/24", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1134, NULL, "11/34", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1140, NULL, "11/40", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1144, NULL, "11/44", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1145, NULL, "11/45", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1153, NULL, "11/53", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1160, NULL, "11/60", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "11/70", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "11/73", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1173B, NULL, "11/73B", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1183, NULL, "11/83", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "11/84", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1193, NULL, "11/93", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1194, NULL, "11/94", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "Q22", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "URH11", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "URH70", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, MOD_1145, NULL, "U18", &cpu_set_model }, { MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "EIS", &cpu_set_opt }, { MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "NOEIS", &cpu_clr_opt }, { MTAB_XTD|MTAB_VDV, OPT_FIS, NULL, "FIS", &cpu_set_opt }, { MTAB_XTD|MTAB_VDV, OPT_FIS, NULL, "NOFIS", &cpu_clr_opt }, { MTAB_XTD|MTAB_VDV, OPT_FPP, NULL, "FPP", &cpu_set_opt }, { MTAB_XTD|MTAB_VDV, OPT_FPP, NULL, "NOFPP", &cpu_clr_opt }, { MTAB_XTD|MTAB_VDV, OPT_CIS, NULL, "CIS", &cpu_set_opt }, { MTAB_XTD|MTAB_VDV, OPT_CIS, NULL, "NOCIS", &cpu_clr_opt }, { MTAB_XTD|MTAB_VDV, OPT_MMU, NULL, "MMU", &cpu_set_opt }, { MTAB_XTD|MTAB_VDV, OPT_MMU, NULL, "NOMMU", &cpu_clr_opt }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size}, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size}, { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size}, { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size}, { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size}, { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size}, { UNIT_MSIZE, 196608, NULL, "192K", &cpu_set_size}, { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size}, { UNIT_MSIZE, 393216, NULL, "384K", &cpu_set_size}, { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size}, { UNIT_MSIZE, 786432, NULL, "768K", &cpu_set_size}, { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size}, { UNIT_MSIZE, 2097152, NULL, "2048K", &cpu_set_size}, { UNIT_MSIZE, 3145728, NULL, "3072K", &cpu_set_size}, { UNIT_MSIZE, 4186112, NULL, "4096K", &cpu_set_size}, { UNIT_MSIZE, 1048576, NULL, "1M", &cpu_set_size}, { UNIT_MSIZE, 2097152, NULL, "2M", &cpu_set_size}, { UNIT_MSIZE, 3145728, NULL, "3M", &cpu_set_size}, { UNIT_MSIZE, 4186112, NULL, "4M", &cpu_set_size}, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, NULL, &show_iospace }, { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", &set_autocon, &show_autocon }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", &set_autocon, NULL }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, NULL, &cpu_show_virt }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 22, 2, 8, 16, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, DEV_DYNM, 0, NULL, &cpu_set_size, NULL }; t_stat sim_instr (void) { int abortval, i; volatile int32 trapea; /* used by setjmp */ t_stat reason; /* Restore register state 1. PSW components 2. Active register file based on PSW 3. Active stack pointer based on PSW 4. Memory management control flags 5. Interrupt system */ reason = build_dib_tab (); /* build, chk dib_tab */ if (reason != SCPE_OK) return reason; if (MEMSIZE < cpu_tab[cpu_model].maxm) /* mem size < max? */ cpu_memsize = MEMSIZE; /* then okay */ else cpu_memsize = cpu_tab[cpu_model].maxm - IOPAGESIZE;/* max - io page */ cpu_type = 1u << cpu_model; /* reset type mask */ cpu_bme = (MMR3 & MMR3_BME) && (cpu_opt & OPT_UBM); /* map enabled? */ PC = saved_PC; put_PSW (PSW, 0); /* set PSW, call calc_xs */ for (i = 0; i < 6; i++) R[i] = REGFILE[i][rs]; SP = STACKFILE[cm]; isenable = calc_is (cm); dsenable = calc_ds (cm); put_PIRQ (PIRQ); /* rewrite PIRQ */ STKLIM = STKLIM & STKLIM_RW; /* clean up STKLIM */ MMR0 = MMR0 | MMR0_IC; /* usually on */ trap_req = calc_ints (ipl, trap_req); /* upd int req */ trapea = 0; reason = 0; /* Abort handling If an abort occurs in memory management or memory access, the lower level routine executes a longjmp to this area OUTSIDE the main simulation loop. The longjmp specifies a trap mask which is OR'd into the trap_req register. Simulation then resumes at the fetch phase, and the trap is sprung. Aborts which occur within a trap sequence (trapea != 0) require special handling. If the abort occured on the stack pushes, and the mode (encoded in trapea) is kernel, an "emergency" kernel stack is created at 4, and a red zone stack trap taken. All variables used in setjmp processing, or assumed to be valid after setjmp, must be volatile or global. */ abortval = setjmp (save_env); /* set abort hdlr */ if (abortval != 0) { trap_req = trap_req | abortval; /* or in trap flag */ if ((trapea > 0) && stop_vecabort) reason = STOP_VECABORT; if ((trapea < 0) && /* stack push abort? */ (CPUT (STOP_STKA) || stop_spabort)) reason = STOP_SPABORT; if (trapea == ~MD_KER) { /* kernel stk abort? */ setTRAP (TRAP_RED); setCPUERR (CPUE_RED); STACKFILE[MD_KER] = 4; if (cm == MD_KER) SP = 4; } } /* Main instruction fetch/decode loop Check for traps or interrupts. If trap, locate the vector and check for stop condition. If interrupt, locate the vector. */ while (reason == 0) { int32 IR, srcspec, srcreg, dstspec, dstreg; int32 src, src2, dst, ea; int32 i, t, sign, oldrs, trapnum; if (cpu_astop) { cpu_astop = 0; reason = SCPE_STOP; break; } if (sim_interval <= 0) { /* intv cnt expired? */ reason = sim_process_event (); /* process events */ trap_req = calc_ints (ipl, trap_req); /* recalc int req */ continue; } /* end if sim_interval */ if (trap_req) { /* check traps, ints */ trapea = 0; /* assume srch fails */ if (t = trap_req & TRAP_ALL) { /* if a trap */ for (trapnum = 0; trapnum < TRAP_V_MAX; trapnum++) { if ((t >> trapnum) & 1) { /* trap set? */ trapea = trap_vec[trapnum]; /* get vec, clr */ trap_req = trap_req & ~trap_clear[trapnum]; if ((stop_trap >> trapnum) & 1) /* stop on trap? */ reason = trapnum + 1; break; } /* end if t & 1 */ } /* end for */ } /* end if t */ else { trapea = get_vector (ipl); /* get int vector */ trapnum = TRAP_V_MAX; /* defang stk trap */ } /* end else t */ if (trapea == 0) { /* nothing to do? */ trap_req = calc_ints (ipl, 0); /* recalculate */ continue; /* back to fetch */ } /* end if trapea */ /* Process a trap or interrupt 1. Exit wait state 2. Save the current SP and PSW 3. Read the new PC, new PSW from trapea, kernel data space 4. Get the mode and stack selected by the new PSW 5. Push the old PC and PSW on the new stack 6. Update SP, PSW, and PC 7. If not stack overflow, check for stack overflow */ wait_state = 0; /* exit wait state */ STACKFILE[cm] = SP; PSW = get_PSW (); /* assemble PSW */ oldrs = rs; if (CPUT (HAS_MMTR)) { /* 45,70? */ if (update_MM) /* save vector */ MMR2 = trapea; MMR0 = MMR0 & ~MMR0_IC; /* clear IC */ } src = ReadW (trapea | calc_ds (MD_KER)); /* new PC */ src2 = ReadW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */ t = (src2 >> PSW_V_CM) & 03; /* new cm */ trapea = ~t; /* flag pushes */ WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t)); WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t)); trapea = 0; /* clear trap flag */ src2 = (src2 & ~PSW_PM) | (cm << PSW_V_PM); /* insert prv mode */ put_PSW (src2, 0); /* call calc_is,ds */ if (rs != oldrs) { /* if rs chg, swap */ for (i = 0; i < 6; i++) { REGFILE[i][oldrs] = R[i]; R[i] = REGFILE[i][rs]; } } SP = (STACKFILE[cm] - 4) & 0177777; /* update SP, PC */ isenable = calc_is (cm); dsenable = calc_ds (cm); trap_req = calc_ints (ipl, trap_req); JMP_PC (src); if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)) && (trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) set_stack_trap (SP); MMR0 = MMR0 | MMR0_IC; /* back to instr */ continue; /* end if traps */ } /* Fetch and decode next instruction */ if (tbit) setTRAP (TRAP_TRC); if (wait_state) { /* wait state? */ if (sim_idle_enab) /* idle enabled? */ sim_idle (TMR_CLK, TRUE); else if (wait_enable) /* old style idle? */ sim_interval = 0; /* force check */ else sim_interval = sim_interval - 1; /* count cycle */ continue; } if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ continue; } if (update_MM) { /* if mm not frozen */ MMR1 = 0; MMR2 = PC; } IR = ReadE (PC | isenable); /* fetch instruction */ sim_interval = sim_interval - 1; srcspec = (IR >> 6) & 077; /* src, dst specs */ dstspec = IR & 077; srcreg = (srcspec <= 07); /* src, dst = rmode? */ dstreg = (dstspec <= 07); if (hst_lnt) { /* record history? */ t_value val; uint32 i; hst[hst_p].pc = PC | HIST_VLD; hst[hst_p].psw = get_PSW (); hst[hst_p].src = R[srcspec & 07]; hst[hst_p].dst = R[dstspec & 07]; hst[hst_p].inst[0] = IR; for (i = 1; i < HIST_ILNT; i++) { if (cpu_ex (&val, (PC + (i << 1)) & 0177777, &cpu_unit, SWMASK ('V'))) hst[hst_p].inst[i] = 0; else hst[hst_p].inst[i] = (uint16) val; } hst_p = (hst_p + 1); if (hst_p >= hst_lnt) hst_p = 0; } PC = (PC + 2) & 0177777; /* incr PC, mod 65k */ switch ((IR >> 12) & 017) { /* decode IR<15:12> */ /* Opcode 0: no operands, specials, branches, JSR, SOPs */ case 000: switch ((IR >> 6) & 077) { /* decode IR<11:6> */ case 000: /* no operand */ if (IR >= 000010) { /* 000010 - 000077 */ setTRAP (TRAP_ILL); /* illegal */ break; } switch (IR) { /* decode IR<2:0> */ case 0: /* HALT */ if ((cm == MD_KER) && (!CPUT (CPUT_J) || ((MAINT & MAINT_HTRAP) == 0))) reason = STOP_HALT; else if (CPUT (HAS_HALT4)) { /* priv trap? */ setTRAP (TRAP_PRV); setCPUERR (CPUE_HALT); } else setTRAP (TRAP_ILL); /* no, ill inst */ break; case 1: /* WAIT */ wait_state = 1; break; case 3: /* BPT */ setTRAP (TRAP_BPT); break; case 4: /* IOT */ setTRAP (TRAP_IOT); break; case 5: /* RESET */ if (cm == MD_KER) { reset_all (2); /* skip CPU, sys reg */ PIRQ = 0; /* clear PIRQ */ STKLIM = 0; /* clear STKLIM */ MMR0 = 0; /* clear MMR0 */ MMR3 = 0; /* clear MMR3 */ cpu_bme = 0; /* (also clear bme) */ for (i = 0; i < IPL_HLVL; i++) int_req[i] = 0; trap_req = trap_req & ~TRAP_INT; dsenable = calc_ds (cm); } break; case 6: /* RTT */ if (!CPUT (HAS_RTT)) { setTRAP (TRAP_ILL); break; } case 2: /* RTI */ src = ReadW (SP | dsenable); src2 = ReadW (((SP + 2) & 0177777) | dsenable); STACKFILE[cm] = SP = (SP + 4) & 0177777; oldrs = rs; put_PSW (src2, (cm != MD_KER)); /* store PSW, prot */ if (rs != oldrs) { for (i = 0; i < 6; i++) { REGFILE[i][oldrs] = R[i]; R[i] = REGFILE[i][rs]; } } SP = STACKFILE[cm]; isenable = calc_is (cm); dsenable = calc_ds (cm); trap_req = calc_ints (ipl, trap_req); JMP_PC (src); if (CPUT (HAS_RTT) && tbit && /* RTT impl? */ (IR == 000002)) setTRAP (TRAP_TRC); /* RTI immed trap */ break; case 7: /* MFPT */ if (CPUT (HAS_MFPT)) /* implemented? */ R[0] = cpu_tab[cpu_model].mfpt; /* get type */ else setTRAP (TRAP_ILL); break; } /* end switch no ops */ break; /* end case no ops */ case 001: /* JMP */ if (dstreg) setTRAP (CPUT (HAS_JREG4)? TRAP_PRV: TRAP_ILL); else { dst = GeteaW (dstspec) & 0177777; /* get eff addr */ if (CPUT (CPUT_05|CPUT_20) && /* 11/05, 11/20 */ ((dstspec & 070) == 020)) /* JMP (R)+? */ dst = R[dstspec & 07]; /* use post incr */ JMP_PC (dst); } break; /* end JMP */ case 002: /* RTS et al*/ if (IR < 000210) { /* RTS */ dstspec = dstspec & 07; JMP_PC (R[dstspec]); R[dstspec] = ReadW (SP | dsenable); if (dstspec != 6) SP = (SP + 2) & 0177777; break; } /* end if RTS */ if (IR < 000230) { setTRAP (TRAP_ILL); break; } if (IR < 000240) { /* SPL */ if (CPUT (HAS_SPL)) { if (cm == MD_KER) ipl = IR & 07; trap_req = calc_ints (ipl, trap_req); } else setTRAP (TRAP_ILL); break; } /* end if SPL */ if (IR < 000260) { /* clear CC */ if (IR & 010) N = 0; if (IR & 004) Z = 0; if (IR & 002) V = 0; if (IR & 001) C = 0; break; } /* end if clear CCs */ if (IR & 010) /* set CC */ N = 1; if (IR & 004) Z = 1; if (IR & 002) V = 1; if (IR & 001) C = 1; break; /* end case RTS et al */ case 003: /* SWAB */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = ((dst & 0377) << 8) | ((dst >> 8) & 0377); N = GET_SIGN_B (dst & 0377); Z = GET_Z (dst & 0377); if (!CPUT (CPUT_20)) V = 0; C = 0; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; /* end SWAB */ case 004: case 005: /* BR */ BRANCH_F (IR); break; case 006: case 007: /* BR */ BRANCH_B (IR); break; case 010: case 011: /* BNE */ if (Z == 0) { BRANCH_F (IR); } break; case 012: case 013: /* BNE */ if (Z == 0) { BRANCH_B (IR); } break; case 014: case 015: /* BEQ */ if (Z) { BRANCH_F (IR); } break; case 016: case 017: /* BEQ */ if (Z) { BRANCH_B (IR); } break; case 020: case 021: /* BGE */ if ((N ^ V) == 0) { BRANCH_F (IR); } break; case 022: case 023: /* BGE */ if ((N ^ V) == 0) { BRANCH_B (IR); } break; case 024: case 025: /* BLT */ if (N ^ V) { BRANCH_F (IR); } break; case 026: case 027: /* BLT */ if (N ^ V) { BRANCH_B (IR); } break; case 030: case 031: /* BGT */ if ((Z | (N ^ V)) == 0) { BRANCH_F (IR); } break; case 032: case 033: /* BGT */ if ((Z | (N ^ V)) == 0) { BRANCH_B (IR); } break; case 034: case 035: /* BLE */ if (Z | (N ^ V)) { BRANCH_F (IR); } break; case 036: case 037: /* BLE */ if (Z | (N ^ V)) { BRANCH_B (IR); } break; case 040: case 041: case 042: case 043: /* JSR */ case 044: case 045: case 046: case 047: if (dstreg) setTRAP (CPUT (HAS_JREG4)? TRAP_PRV: TRAP_ILL); else { srcspec = srcspec & 07; dst = GeteaW (dstspec); if (CPUT (CPUT_05|CPUT_20) && /* 11/05, 11/20 */ ((dstspec & 070) == 020)) /* JSR (R)+? */ dst = R[dstspec & 07]; /* use post incr */ SP = (SP - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0366); WriteW (R[srcspec], SP | dsenable); if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) set_stack_trap (SP); R[srcspec] = PC; JMP_PC (dst & 0177777); } break; /* end JSR */ case 050: /* CLR */ N = V = C = 0; Z = 1; if (dstreg) R[dstspec] = 0; else WriteW (0, GeteaW (dstspec)); break; case 051: /* COM */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = dst ^ 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; C = 1; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 052: /* INC */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (dst + 1) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = (dst == 0100000); if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 053: /* DEC */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (dst - 1) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = (dst == 077777); if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 054: /* NEG */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (-dst) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = (dst == 0100000); C = Z ^ 1; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 055: /* ADC */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (dst + C) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = (C && (dst == 0100000)); C = C & Z; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 056: /* SBC */ dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (dst - C) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = (C && (dst == 077777)); C = (C && (dst == 0177777)); if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 057: /* TST */ dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); N = GET_SIGN_W (dst); Z = GET_Z (dst); V = C = 0; break; case 060: /* ROR */ src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (src >> 1) | (C << 15); N = GET_SIGN_W (dst); Z = GET_Z (dst); C = (src & 1); V = N ^ C; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 061: /* ROL */ src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = ((src << 1) | C) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); C = GET_SIGN_W (src); V = N ^ C; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 062: /* ASR */ src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (src >> 1) | (src & 0100000); N = GET_SIGN_W (dst); Z = GET_Z (dst); C = (src & 1); V = N ^ C; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 063: /* ASL */ src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); dst = (src << 1) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); C = GET_SIGN_W (src); V = N ^ C; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; /* Notes: - MxPI must mask GeteaW returned address to force ispace - MxPI must set MMR1 for SP recovery in case of fault */ case 064: /* MARK */ if (CPUT (HAS_MARK)) { i = (PC + dstspec + dstspec) & 0177777; JMP_PC (R[5]); R[5] = ReadW (i | dsenable); SP = (i + 2) & 0177777; } else setTRAP (TRAP_ILL); break; case 065: /* MFPI */ if (CPUT (HAS_MXPY)) { if (dstreg) { if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; else dst = R[dstspec]; } else { i = ((cm == pm) && (cm == MD_USR))? calc_ds (pm): calc_is (pm); dst = ReadW ((GeteaW (dstspec) & 0177777) | i); } N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; SP = (SP - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0366); WriteW (dst, SP | dsenable); if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) set_stack_trap (SP); } else setTRAP (TRAP_ILL); break; case 066: /* MTPI */ if (CPUT (HAS_MXPY)) { dst = ReadW (SP | dsenable); N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; SP = (SP + 2) & 0177777; if (update_MM) MMR1 = 026; if (dstreg) { if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; else R[dstspec] = dst; } else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_is (pm)); } else setTRAP (TRAP_ILL); break; case 067: /* SXT */ if (CPUT (HAS_SXS)) { dst = N? 0177777: 0; Z = N ^ 1; V = 0; if (dstreg) R[dstspec] = dst; else WriteW (dst, GeteaW (dstspec)); } else setTRAP (TRAP_ILL); break; case 070: /* CSM */ if (CPUT (HAS_CSM) && (MMR3 & MMR3_CSM) || (cm != MD_KER)) { dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); PSW = get_PSW () & ~PSW_CC; /* PSW, cc = 0 */ STACKFILE[cm] = SP; WriteW (PSW, ((SP - 2) & 0177777) | calc_ds (MD_SUP)); WriteW (PC, ((SP - 4) & 0177777) | calc_ds (MD_SUP)); WriteW (dst, ((SP - 6) & 0177777) | calc_ds (MD_SUP)); SP = (SP - 6) & 0177777; pm = cm; cm = MD_SUP; tbit = 0; isenable = calc_is (cm); dsenable = calc_ds (cm); PC = ReadW (010 | isenable); } else setTRAP (TRAP_ILL); break; case 072: /* TSTSET */ if (CPUT (HAS_TSWLK) && !dstreg) { dst = ReadMW (GeteaW (dstspec)); N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; C = (dst & 1); R[0] = dst; /* R[0] <- dst */ PWriteW (R[0] | 1, last_pa); /* dst <- R[0] | 1 */ } else setTRAP (TRAP_ILL); break; case 073: /* WRTLCK */ if (CPUT (HAS_TSWLK) && !dstreg) { N = GET_SIGN_W (R[0]); Z = GET_Z (R[0]); V = 0; WriteW (R[0], GeteaW (dstspec)); } else setTRAP (TRAP_ILL); break; default: setTRAP (TRAP_ILL); break; } /* end switch SOPs */ break; /* end case 000 */ /* Opcodes 01 - 06: double operand word instructions J-11 (and F-11) optimize away register source operand decoding. As a result, dop R,+/-(R) use the modified version of R as source. Most (but not all) other PDP-11's fetch the source operand before any destination operand decoding. Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] */ case 001: /* MOV */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ ea = GeteaW (dstspec); dst = R[srcspec]; } else { dst = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); if (!dstreg) ea = GeteaW (dstspec); } N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = dst; else WriteW (dst, ea); break; case 002: /* CMP */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); } dst = (src - src2) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = GET_SIGN_W ((src ^ src2) & (~src2 ^ dst)); C = (src < src2); break; case 003: /* BIT */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); } dst = src2 & src; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; break; case 004: /* BIC */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); } dst = src2 & ~src; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 005: /* BIS */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); } dst = src2 | src; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; case 006: /* ADD */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); } dst = (src2 + src) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = GET_SIGN_W ((~src ^ src2) & (src ^ dst)); C = (dst < src); if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; /* Opcode 07: EIS, FIS, CIS Notes: - The code assumes that the host int length is at least 32 bits. - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. - Divide has three error cases: 1. Divide by zero. 2. Divide largest negative number by -1. 3. (Signed) quotient doesn't fit in 16 bits. Cases 1 and 2 must be tested in advance, to avoid C runtime errors. - ASHx left: overflow if the bits shifted out do not equal the sign of the result (convert shift out to 1/0, xor against sign). - ASHx right: if right shift sign extends, then the shift and conditional or of shifted -1 is redundant. If right shift zero extends, then the shift and conditional or does sign extension. */ case 007: srcspec = srcspec & 07; switch ((IR >> 9) & 07) { /* decode IR<11:9> */ case 0: /* MUL */ if (!CPUO (OPT_EIS)) { setTRAP (TRAP_ILL); break; } src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); src = R[srcspec]; if (GET_SIGN_W (src2)) src2 = src2 | ~077777; if (GET_SIGN_W (src)) src = src | ~077777; dst = src * src2; R[srcspec] = (dst >> 16) & 0177777; R[srcspec | 1] = dst & 0177777; N = (dst < 0); Z = GET_Z (dst); V = 0; C = ((dst > 077777) || (dst < -0100000)); break; case 1: /* DIV */ if (!CPUO (OPT_EIS)) { setTRAP (TRAP_ILL); break; } src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); src = (((uint32) R[srcspec]) << 16) | R[srcspec | 1]; if (src2 == 0) { N = 0; /* J11,11/70 compat */ Z = V = C = 1; /* N = 0, Z = 1 */ break; } if ((src == 020000000000) && (src2 == 0177777)) { V = 1; /* J11,11/70 compat */ N = Z = C = 0; /* N = Z = 0 */ break; } if (GET_SIGN_W (src2)) src2 = src2 | ~077777; if (GET_SIGN_W (R[srcspec])) src = src | ~017777777777; dst = src / src2; N = (dst < 0); /* N set on 32b result */ if ((dst > 077777) || (dst < -0100000)) { V = 1; /* J11,11/70 compat */ Z = C = 0; /* Z = C = 0 */ break; } R[srcspec] = dst & 0177777; R[srcspec | 1] = (src - (src2 * dst)) & 0177777; Z = GET_Z (dst); V = C = 0; break; case 2: /* ASH */ if (!CPUO (OPT_EIS)) { setTRAP (TRAP_ILL); break; } src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); src2 = src2 & 077; sign = GET_SIGN_W (R[srcspec]); src = sign? R[srcspec] | ~077777: R[srcspec]; if (src2 == 0) { /* [0] */ dst = src; V = C = 0; } else if (src2 <= 15) { /* [1,15] */ dst = src << src2; i = (src >> (16 - src2)) & 0177777; V = (i != ((dst & 0100000)? 0177777: 0)); C = (i & 1); } else if (src2 <= 31) { /* [16,31] */ dst = 0; V = (src != 0); C = (src << (src2 - 16)) & 1; } else if (src2 == 32) { /* [32] = -32 */ dst = -sign; V = 0; C = sign; } else { /* [33,63] = -31,-1 */ dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); V = 0; C = ((src >> (63 - src2)) & 1); } dst = R[srcspec] = dst & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); break; case 3: /* ASHC */ if (!CPUO (OPT_EIS)) { setTRAP (TRAP_ILL); break; } src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); src2 = src2 & 077; sign = GET_SIGN_W (R[srcspec]); src = (((uint32) R[srcspec]) << 16) | R[srcspec | 1]; if (src2 == 0) { /* [0] */ dst = src; V = C = 0; } else if (src2 <= 31) { /* [1,31] */ dst = ((uint32) src) << src2; i = (src >> (32 - src2)) | (-sign << src2); V = (i != ((dst & 020000000000)? -1: 0)); C = (i & 1); } else if (src2 == 32) { /* [32] = -32 */ dst = -sign; V = 0; C = sign; } else { /* [33,63] = -31,-1 */ dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); V = 0; C = ((src >> (63 - src2)) & 1); } i = R[srcspec] = (dst >> 16) & 0177777; dst = R[srcspec | 1] = dst & 0177777; N = GET_SIGN_W (i); Z = GET_Z (dst | i); break; case 4: /* XOR */ if (CPUT (HAS_SXS)) { if (CPUT (IS_SDSD) && !dstreg) { /* R,not R */ src2 = ReadMW (GeteaW (dstspec)); src = R[srcspec]; } else { src = R[srcspec]; src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); } dst = src ^ src2; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); } else setTRAP (TRAP_ILL); break; case 5: /* FIS */ if (CPUO (OPT_FIS)) fis11 (IR); else setTRAP (TRAP_ILL); break; case 6: /* CIS */ if (CPUT (CPUT_60) && (cm == MD_KER) && /* 11/60 MED? */ (IR == 076600)) { ReadE (PC | isenable); /* read immediate */ PC = (PC + 2) & 0177777; } else if (CPUO (OPT_CIS)) /* CIS option? */ reason = cis11 (IR); else setTRAP (TRAP_ILL); break; case 7: /* SOB */ if (CPUT (HAS_SXS)) { R[srcspec] = (R[srcspec] - 1) & 0177777; if (R[srcspec]) { JMP_PC ((PC - dstspec - dstspec) & 0177777); } } else setTRAP (TRAP_ILL); break; } /* end switch EIS */ break; /* end case 007 */ /* Opcode 10: branches, traps, SOPs */ case 010: switch ((IR >> 6) & 077) { /* decode IR<11:6> */ case 000: case 001: /* BPL */ if (N == 0) { BRANCH_F (IR); } break; case 002: case 003: /* BPL */ if (N == 0) { BRANCH_B (IR); } break; case 004: case 005: /* BMI */ if (N) { BRANCH_F (IR); } break; case 006: case 007: /* BMI */ if (N) { BRANCH_B (IR); } break; case 010: case 011: /* BHI */ if ((C | Z) == 0) { BRANCH_F (IR); } break; case 012: case 013: /* BHI */ if ((C | Z) == 0) { BRANCH_B (IR); } break; case 014: case 015: /* BLOS */ if (C | Z) { BRANCH_F (IR); } break; case 016: case 017: /* BLOS */ if (C | Z) { BRANCH_B (IR); } break; case 020: case 021: /* BVC */ if (V == 0) { BRANCH_F (IR); } break; case 022: case 023: /* BVC */ if (V == 0) { BRANCH_B (IR); } break; case 024: case 025: /* BVS */ if (V) { BRANCH_F (IR); } break; case 026: case 027: /* BVS */ if (V) { BRANCH_B (IR); } break; case 030: case 031: /* BCC */ if (C == 0) { BRANCH_F (IR); } break; case 032: case 033: /* BCC */ if (C == 0) { BRANCH_B (IR); } break; case 034: case 035: /* BCS */ if (C) { BRANCH_F (IR); } break; case 036: case 037: /* BCS */ if (C) { BRANCH_B (IR); } break; case 040: case 041: case 042: case 043: /* EMT */ setTRAP (TRAP_EMT); break; case 044: case 045: case 046: case 047: /* TRAP */ setTRAP (TRAP_TRAP); break; case 050: /* CLRB */ N = V = C = 0; Z = 1; if (dstreg) R[dstspec] = R[dstspec] & 0177400; else WriteB (0, GeteaB (dstspec)); break; case 051: /* COMB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (dst ^ 0377) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; C = 1; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 052: /* INCB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (dst + 1) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = (dst == 0200); if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 053: /* DECB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (dst - 1) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = (dst == 0177); if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 054: /* NEGB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (-dst) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = (dst == 0200); C = (Z ^ 1); if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 055: /* ADCB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (dst + C) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = (C && (dst == 0200)); C = C & Z; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 056: /* SBCB */ dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (dst - C) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = (C && (dst == 0177)); C = (C && (dst == 0377)); if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 057: /* TSTB */ dst = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); N = GET_SIGN_B (dst); Z = GET_Z (dst); V = C = 0; break; case 060: /* RORB */ src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = ((src & 0377) >> 1) | (C << 7); N = GET_SIGN_B (dst); Z = GET_Z (dst); C = (src & 1); V = N ^ C; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 061: /* ROLB */ src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = ((src << 1) | C) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); C = GET_SIGN_B (src & 0377); V = N ^ C; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 062: /* ASRB */ src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = ((src & 0377) >> 1) | (src & 0200); N = GET_SIGN_B (dst); Z = GET_Z (dst); C = (src & 1); V = N ^ C; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 063: /* ASLB */ src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); dst = (src << 1) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); C = GET_SIGN_B (src & 0377); V = N ^ C; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; /* Notes: - MTPS cannot alter the T bit - MxPD must mask GeteaW returned address, dspace is from cm not pm - MxPD must set MMR1 for SP recovery in case of fault */ case 064: /* MTPS */ if (CPUT (HAS_MXPS)) { dst = dstreg? R[dstspec]: ReadB (GeteaB (dstspec)); if (cm == MD_KER) { ipl = (dst >> PSW_V_IPL) & 07; trap_req = calc_ints (ipl, trap_req); } N = (dst >> PSW_V_N) & 01; Z = (dst >> PSW_V_Z) & 01; V = (dst >> PSW_V_V) & 01; C = (dst >> PSW_V_C) & 01; } else setTRAP (TRAP_ILL); break; case 065: /* MFPD */ if (CPUT (HAS_MXPY)) { if (dstreg) { if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; else dst = R[dstspec]; } else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_ds (pm)); N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; SP = (SP - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0366); WriteW (dst, SP | dsenable); if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) set_stack_trap (SP); } else setTRAP (TRAP_ILL); break; case 066: /* MTPD */ if (CPUT (HAS_MXPY)) { dst = ReadW (SP | dsenable); N = GET_SIGN_W (dst); Z = GET_Z (dst); V = 0; SP = (SP + 2) & 0177777; if (update_MM) MMR1 = 026; if (dstreg) { if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; else R[dstspec] = dst; } else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_ds (pm)); } else setTRAP (TRAP_ILL); break; case 067: /* MFPS */ if (CPUT (HAS_MXPS)) { dst = get_PSW () & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; else WriteB (dst, GeteaB (dstspec)); } else setTRAP (TRAP_ILL); break; default: setTRAP (TRAP_ILL); break; } /* end switch SOPs */ break; /* end case 010 */ /* Opcodes 11 - 16: double operand byte instructions Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] */ case 011: /* MOVB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ ea = GeteaB (dstspec); dst = R[srcspec] & 0377; } else { dst = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); if (!dstreg) ea = GeteaB (dstspec); } N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; else WriteB (dst, ea); break; case 012: /* CMPB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadB (GeteaB (dstspec)); src = R[srcspec] & 0377; } else { src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); } dst = (src - src2) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = GET_SIGN_B ((src ^ src2) & (~src2 ^ dst)); C = (src < src2); break; case 013: /* BITB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadB (GeteaB (dstspec)); src = R[srcspec] & 0377; } else { src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); } dst = (src2 & src) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; break; case 014: /* BICB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMB (GeteaB (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); } dst = (src2 & ~src) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 015: /* BISB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMB (GeteaB (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); } dst = (src2 | src) & 0377; N = GET_SIGN_B (dst); Z = GET_Z (dst); V = 0; if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; else PWriteB (dst, last_pa); break; case 016: /* SUB */ if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ src2 = ReadMW (GeteaW (dstspec)); src = R[srcspec]; } else { src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); } dst = (src2 - src) & 0177777; N = GET_SIGN_W (dst); Z = GET_Z (dst); V = GET_SIGN_W ((src ^ src2) & (~src ^ dst)); C = (src2 < src); if (dstreg) R[dstspec] = dst; else PWriteW (dst, last_pa); break; /* Opcode 17: floating point */ case 017: if (CPUO (OPT_FPP)) fp11 (IR); /* call fpp */ else setTRAP (TRAP_ILL); break; /* end case 017 */ } /* end switch op */ } /* end main loop */ /* Simulation halted */ PSW = get_PSW (); for (i = 0; i < 6; i++) REGFILE[i][rs] = R[i]; STACKFILE[cm] = SP; saved_PC = PC & 0177777; pcq_r->qptr = pcq_p; /* update pc q ptr */ set_r_display (rs, cm); return reason; } /* Effective address calculations Inputs: spec = specifier <5:0> Outputs: ea = effective address <15:0> = virtual address <16> = instruction/data data space <18:17> = mode Data space calculation: the PDP-11 features both instruction and data spaces. Instruction space contains the instruction and any sequential add ons (eg, immediates, absolute addresses). Data space contains all data operands and indirect addresses. If data space is enabled, then memory references are directed according to these rules: Mode Index ref Indirect ref Direct ref 10..16 na na data 17 na na instruction 20..26 na na data 27 na na instruction 30..36 na data data 37 na instruction (absolute) data 40..46 na na data 47 na na instruction 50..56 na data data 57 na instruction data 60..67 instruction na data 70..77 instruction data data According to the PDP-11 Architecture Handbook, MMR1 records all autoincrement and autodecrement operations, including those which explicitly reference the PC. For the J-11, this is only true for autodecrement operands, autodecrement deferred operands, and autoincrement destination operands that involve a write to memory. The simulator follows the Handbook, for simplicity. Notes: - dsenable will direct a reference to data space if data space is enabled - ds will direct a reference to data space if data space is enabled AND if the specifier register is not PC; this is used for 17, 27, 37, 47, 57 - Modes 2x, 3x, 4x, and 5x must update MMR1 if updating enabled - Modes 46 and 56 must check for stack overflow if kernel mode */ /* Effective address calculation for words */ int32 GeteaW (int32 spec) { int32 adr, reg, ds; reg = spec & 07; /* register number */ ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ switch (spec >> 3) { /* decode spec<5:3> */ default: /* can't get here */ case 1: /* (R) */ return (R[reg] | ds); case 2: /* (R)+ */ R[reg] = ((adr = R[reg]) + 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (020 | reg); return (adr | ds); case 3: /* @(R)+ */ R[reg] = ((adr = R[reg]) + 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (020 | reg); adr = ReadW (adr | ds); return (adr | dsenable); case 4: /* -(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0360 | reg); if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); return (adr | ds); case 5: /* @-(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0360 | reg); if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); adr = ReadW (adr | ds); return (adr | dsenable); case 6: /* d(r) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; return (((R[reg] + adr) & 0177777) | dsenable); case 7: /* @d(R) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); return (adr | dsenable); } /* end switch */ } /* Effective address calculation for bytes */ int32 GeteaB (int32 spec) { int32 adr, reg, ds, delta; reg = spec & 07; /* reg number */ ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ switch (spec >> 3) { /* decode spec<5:3> */ default: /* can't get here */ case 1: /* (R) */ return (R[reg] | ds); case 2: /* (R)+ */ delta = 1 + (reg >= 6); /* 2 if R6, PC */ R[reg] = ((adr = R[reg]) + delta) & 0177777; if (update_MM) MMR1 = calc_MMR1 ((delta << 3) | reg); return (adr | ds); case 3: /* @(R)+ */ R[reg] = ((adr = R[reg]) + 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (020 | reg); adr = ReadW (adr | ds); return (adr | dsenable); case 4: /* -(R) */ delta = 1 + (reg >= 6); /* 2 if R6, PC */ adr = R[reg] = (R[reg] - delta) & 0177777; if (update_MM) MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg); if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); return (adr | ds); case 5: /* @-(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; if (update_MM) MMR1 = calc_MMR1 (0360 | reg); if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); adr = ReadW (adr | ds); return (adr | dsenable); case 6: /* d(r) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; return (((R[reg] + adr) & 0177777) | dsenable); case 7: /* @d(R) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); return (adr | dsenable); } /* end switch */ } /* Read byte and word routines, read only and read-modify-write versions Inputs: va = virtual address, <18:16> = mode, I/D space Outputs: data = data read from memory or I/O space */ int32 ReadE (int32 va) { int32 pa, data; if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ setCPUERR (CPUE_ODD); ABORT (TRAP_ODD); } pa = relocR (va); /* relocate */ if (ADDR_IS_MEM (pa)) /* memory address? */ return (M[pa >> 1]); if ((pa < IOPAGEBASE) || /* not I/O address */ (CPUT (CPUT_J) && (pa >= IOBA_CPU))) { /* or J11 int reg? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return data; } int32 ReadW (int32 va) { int32 pa, data; if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ setCPUERR (CPUE_ODD); ABORT (TRAP_ODD); } pa = relocR (va); /* relocate */ if (ADDR_IS_MEM (pa)) /* memory address? */ return (M[pa >> 1]); if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return data; } int32 ReadB (int32 va) { int32 pa, data; pa = relocR (va); /* relocate */ if (ADDR_IS_MEM (pa)) return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377; if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return ((va & 1)? data >> 8: data) & 0377; } int32 ReadMW (int32 va) { int32 data; if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ setCPUERR (CPUE_ODD); ABORT (TRAP_ODD); } last_pa = relocW (va); /* reloc, wrt chk */ if (ADDR_IS_MEM (last_pa)) /* memory address? */ return (M[last_pa >> 1]); if (last_pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return data; } int32 ReadMB (int32 va) { int32 data; last_pa = relocW (va); /* reloc, wrt chk */ if (ADDR_IS_MEM (last_pa)) return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377; if (last_pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return ((va & 1)? data >> 8: data) & 0377; } /* Write byte and word routines Inputs: data = data to be written va = virtual address, <18:16> = mode, I/D space, or pa = physical address Outputs: none */ void WriteW (int32 data, int32 va) { int32 pa; if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ setCPUERR (CPUE_ODD); ABORT (TRAP_ODD); } pa = relocW (va); /* relocate */ if (ADDR_IS_MEM (pa)) { /* memory address? */ M[pa >> 1] = data; return; } if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return; } void WriteB (int32 data, int32 va) { int32 pa; pa = relocW (va); /* relocate */ if (ADDR_IS_MEM (pa)) { /* memory address? */ if (va & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; return; } if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return; } void PWriteW (int32 data, int32 pa) { if (ADDR_IS_MEM (pa)) { /* memory address? */ M[pa >> 1] = data; return; } if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return; } void PWriteB (int32 data, int32 pa) { if (ADDR_IS_MEM (pa)) { /* memory address? */ if (pa & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; return; } if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); } if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } return; } /* Relocate virtual address, read access Inputs: va = virtual address, <18:16> = mode, I/D space Outputs: pa = physical address On aborts, this routine aborts back to the top level simulator with an appropriate trap code. Notes: - The 'normal' read codes (010, 110) are done in-line; all others in a subroutine - APRFILE[UNUSED] is all zeroes, forcing non-resident abort - Aborts must update MMR0<15:13,6:1> if updating is enabled */ int32 relocR (int32 va) { int32 apridx, apr, pa; if (MMR0 & MMR0_MME) { /* if mmgt */ apridx = (va >> VA_V_APF) & 077; /* index into APR */ apr = APRFILE[apridx]; /* with va<18:13> */ if ((apr & PDR_PRD) != 2) /* not 2, 6? */ relocR_test (va, apridx); /* long test */ if (PLF_test (va, apr)) /* pg lnt error? */ reloc_abort (MMR0_PL, apridx); pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; if ((MMR3 & MMR3_M22E) == 0) { pa = pa & 0777777; if (pa >= 0760000) pa = 017000000 | pa; } } else { pa = va & 0177777; /* mmgt off */ if (pa >= 0160000) pa = 017600000 | pa; } return pa; } /* Read relocation, access control field != read only or read/write ACF value 11/45,11/70 all others 0 abort NR abort NR 1 trap - 2 ok ok 3 abort NR - 4 trap abort NR 5 ok - 6 ok ok 7 abort NR - */ void relocR_test (int32 va, int32 apridx) { int32 apr, err; err = 0; /* init status */ apr = APRFILE[apridx]; /* get APR */ switch (apr & PDR_ACF) { /* case on ACF */ case 1: case 4: /* trap read */ if (CPUT (HAS_MMTR)) { /* traps implemented? */ APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ if (MMR0 & MMR0_TENB) { /* traps enabled? */ if (update_MM) /* update MMR0 */ MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ setTRAP (TRAP_MME); /* set trap */ } return; /* continue op */ } /* not impl, abort NR */ case 0: case 3: case 7: /* non-resident */ err = MMR0_NR; /* set MMR0 */ break; /* go test PLF, abort */ case 2: case 5: case 6: /* readable */ return; /* continue */ } /* end switch */ if (PLF_test (va, apr)) /* pg lnt error? */ err = err | MMR0_PL; reloc_abort (err, apridx); return; } t_bool PLF_test (int32 va, int32 apr) { int32 dbn = va & VA_BN; /* extr block num */ int32 plf = (apr & PDR_PLF) >> 2; /* extr page length */ return ((apr & PDR_ED)? (dbn < plf): (dbn > plf)); /* pg lnt error? */ } void reloc_abort (int32 err, int32 apridx) { if (update_MM) MMR0 = /* update MMR0 */ (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ MMR0 = MMR0 | err; /* set aborts */ ABORT (TRAP_MME); /* abort ref */ return; } /* Relocate virtual address, write access Inputs: va = virtual address, <18:16> = mode, I/D space Outputs: pa = physical address On aborts, this routine aborts back to the top level simulator with an appropriate trap code. Notes: - The 'normal' write code (110) is done in-line; all others in a subroutine - APRFILE[UNUSED] is all zeroes, forcing non-resident abort - Aborts must update MMR0<15:13,6:1> if updating is enabled */ int32 relocW (int32 va) { int32 apridx, apr, pa; if (MMR0 & MMR0_MME) { /* if mmgt */ apridx = (va >> VA_V_APF) & 077; /* index into APR */ apr = APRFILE[apridx]; /* with va<18:13> */ if ((apr & PDR_ACF) != 6) /* not writeable? */ relocW_test (va, apridx); /* long test */ if (PLF_test (va, apr)) /* pg lnt error? */ reloc_abort (MMR0_PL, apridx); APRFILE[apridx] = apr | PDR_W; /* set W */ pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; if ((MMR3 & MMR3_M22E) == 0) { pa = pa & 0777777; if (pa >= 0760000) pa = 017000000 | pa; } } else { pa = va & 0177777; /* mmgt off */ if (pa >= 0160000) pa = 017600000 | pa; } return pa; } /* Write relocation, access control field != read/write ACF value 11/45,11/70 all others 0 abort NR abort NR 1 abort RO - 2 abort RO abort RO 3 abort NR - 4 trap abort NR 5 trap - 6 ok ok 7 abort NR - */ void relocW_test (int32 va, int32 apridx) { int32 apr, err; err = 0; /* init status */ apr = APRFILE[apridx]; /* get APR */ switch (apr & PDR_ACF) { /* case on ACF */ case 4: case 5: /* trap write */ if (CPUT (HAS_MMTR)) { /* traps implemented? */ APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ if (MMR0 & MMR0_TENB) { /* traps enabled? */ if (update_MM) /* update MMR0 */ MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ setTRAP (TRAP_MME); /* set trap */ } return; /* continue op */ } /* not impl, abort NR */ case 0: case 3: case 7: /* non-resident */ err = MMR0_NR; /* MMR0 status */ break; /* go test PLF, abort */ case 1: case 2: /* read only */ err = MMR0_RO; /* MMR0 status */ break; case 6: /* read/write */ return; /* continue */ } /* end switch */ if (PLF_test (va, apr)) /* pg lnt error? */ err = err | MMR0_PL; reloc_abort (err, apridx); return; } /* Relocate virtual address, console access Inputs: va = virtual address sw = switches Outputs: pa = physical address On aborts, this routine returns MAXMEMSIZE */ int32 relocC (int32 va, int32 sw) { int32 mode, dbn, plf, apridx, apr, pa; if (MMR0 & MMR0_MME) { /* if mmgt */ if (sw & SWMASK ('K')) mode = MD_KER; else if (sw & SWMASK ('S')) mode = MD_SUP; else if (sw & SWMASK ('U')) mode = MD_USR; else if (sw & SWMASK ('P')) mode = (PSW >> PSW_V_PM) & 03; else mode = (PSW >> PSW_V_CM) & 03; va = va | ((sw & SWMASK ('D'))? calc_ds (mode): calc_is (mode)); apridx = (va >> VA_V_APF) & 077; /* index into APR */ apr = APRFILE[apridx]; /* with va<18:13> */ dbn = va & VA_BN; /* extr block num */ plf = (apr & PDR_PLF) >> 2; /* extr page length */ if ((apr & PDR_PRD) == 0) /* not readable? */ return MAXMEMSIZE; if ((apr & PDR_ED)? dbn < plf: dbn > plf) return MAXMEMSIZE; pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; if ((MMR3 & MMR3_M22E) == 0) { pa = pa & 0777777; if (pa >= 0760000) pa = 017000000 | pa; } } else { pa = va & 0177777; /* mmgt off */ if (pa >= 0160000) pa = 017600000 | pa; } return pa; } /* Memory management registers MMR0 17777572 read/write, certain bits unimplemented or read only MMR1 17777574 read only MMR2 17777576 read only MMR3 17777516 read/write, certain bits unimplemented */ t_stat MMR012_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 3) { /* decode pa<2:1> */ case 0: /* SR */ return SCPE_NXM; case 1: /* MMR0 */ *data = MMR0 & cpu_tab[cpu_model].mm0; break; case 2: /* MMR1 */ *data = MMR1; break; case 3: /* MMR2 */ *data = MMR2; break; } /* end switch pa */ return SCPE_OK; } t_stat MMR012_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 3) { /* decode pa<2:1> */ case 0: /* DR */ return SCPE_NXM; case 1: /* MMR0 */ if (access == WRITEB) data = (pa & 1)? (MMR0 & 0377) | (data << 8): (MMR0 & ~0377) | data; data = data & cpu_tab[cpu_model].mm0; MMR0 = (MMR0 & ~MMR0_WR) | (data & MMR0_WR); return SCPE_OK; default: /* MMR1, MMR2 */ return SCPE_OK; } /* end switch pa */ } t_stat MMR3_rd (int32 *data, int32 pa, int32 access) /* MMR3 */ { *data = MMR3 & cpu_tab[cpu_model].mm3; return SCPE_OK; } t_stat MMR3_wr (int32 data, int32 pa, int32 access) /* MMR3 */ { if (pa & 1) return SCPE_OK; MMR3 = data & cpu_tab[cpu_model].mm3; cpu_bme = (MMR3 & MMR3_BME) && (cpu_opt & OPT_UBM); dsenable = calc_ds (cm); return SCPE_OK; } /* PARs and PDRs. These are grouped in I/O space as follows: 17772200 - 17772276 supervisor block 17772300 - 17772376 kernel block 17777600 - 17777676 user block Within each block, the subblocks are I PDR's, D PDR's, I PAR's, D PAR's Thus, the algorithm for converting between I/O space addresses and APRFILE indices is as follows: idx<3:0> = dspace'page = pa<4:1> par = PDR vs PAR = pa<5> idx<5:4> = ker/sup/user = pa<8>'~pa<6> Note: the A,W bits are read only; they are cleared by any write to an APR */ t_stat APR_rd (int32 *data, int32 pa, int32 access) { t_stat left, idx; idx = (pa >> 1) & 017; /* dspace'page */ left = (pa >> 5) & 1; /* PDR vs PAR */ if ((pa & 0100) == 0) /* 1 for super, user */ idx = idx | 020; if (pa & 0400) /* 1 for user only */ idx = idx | 040; if (left) *data = (APRFILE[idx] >> 16) & cpu_tab[cpu_model].par; else *data = APRFILE[idx] & cpu_tab[cpu_model].pdr; return SCPE_OK; } t_stat APR_wr (int32 data, int32 pa, int32 access) { int32 left, idx, curr; idx = (pa >> 1) & 017; /* dspace'page */ left = (pa >> 5) & 1; /* PDR vs PAR */ if ((pa & 0100) == 0) /* 1 for super, user */ idx = idx | 020; if (pa & 0400) /* 1 for user only */ idx = idx | 040; if (left) curr = (APRFILE[idx] >> 16) & cpu_tab[cpu_model].par; else curr = APRFILE[idx] & cpu_tab[cpu_model].pdr; if (access == WRITEB) data = (pa & 1)? (curr & 0377) | (data << 8): (curr & ~0377) | data; if (left) APRFILE[idx] = ((APRFILE[idx] & 0177777) | (((uint32) (data & cpu_tab[cpu_model].par)) << 16)) & ~(PDR_A|PDR_W); else APRFILE[idx] = ((APRFILE[idx] & ~0177777) | (data & cpu_tab[cpu_model].pdr)) & ~(PDR_A|PDR_W); return SCPE_OK; } /* Explicit PSW read */ t_stat PSW_rd (int32 *data, int32 pa, int32 access) { if (access == READC) *data = PSW; else *data = get_PSW (); return SCPE_OK; } /* Assemble PSW from pieces */ int32 get_PSW (void) { return (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) | (fpd << PSW_V_FPD) | (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | (N << PSW_V_N) | (Z << PSW_V_Z) | (V << PSW_V_V) | (C << PSW_V_C); } /* Explicit PSW write - T-bit may be protected */ t_stat PSW_wr (int32 data, int32 pa, int32 access) { int32 i, curr, oldrs; if (access == WRITEC) { /* console access? */ PSW = data & cpu_tab[cpu_model].psw; return SCPE_OK; } curr = get_PSW (); /* get current */ oldrs = rs; /* save reg set */ STACKFILE[cm] = SP; /* save curr SP */ if (access == WRITEB) data = (pa & 1)? (curr & 0377) | (data << 8): (curr & ~0377) | data; if (!CPUT (HAS_EXPT)) /* expl T writes? */ data = (data & ~PSW_TBIT) | (curr & PSW_TBIT); /* no, use old T */ put_PSW (data, 0); /* call calc_is,ds */ if (rs != oldrs) { /* switch reg set */ for (i = 0; i < 6; i++) { REGFILE[i][oldrs] = R[i]; R[i] = REGFILE[i][rs]; } } SP = STACKFILE[cm]; /* switch SP */ isenable = calc_is (cm); dsenable = calc_ds (cm); return SCPE_OK; } /* Store pieces of new PSW - implements RTI/RTT protection */ void put_PSW (int32 val, t_bool prot) { val = val & cpu_tab[cpu_model].psw; /* mask off invalid bits */ if (prot) { /* protected? */ cm = cm | ((val >> PSW_V_CM) & 03); /* or to cm,pm,rs */ pm = pm | ((val >> PSW_V_PM) & 03); /* can't change ipl */ rs = rs | ((val >> PSW_V_RS) & 01); } else { cm = (val >> PSW_V_CM) & 03; /* write cm,pm,rs,ipl */ pm = (val >> PSW_V_PM) & 03; rs = (val >> PSW_V_RS) & 01; ipl = (val >> PSW_V_IPL) & 07; } fpd = (val >> PSW_V_FPD) & 01; /* always writeable */ tbit = (val >> PSW_V_TBIT) & 01; N = (val >> PSW_V_N) & 01; Z = (val >> PSW_V_Z) & 01; V = (val >> PSW_V_V) & 01; C = (val >> PSW_V_C) & 01; return; } /* PIRQ write routine */ void put_PIRQ (int32 val) { int32 pl; PIRQ = val & PIRQ_RW; pl = 0; if (PIRQ & PIRQ_PIR1) { SET_INT (PIR1); pl = 0042; } else CLR_INT (PIR1); if (PIRQ & PIRQ_PIR2) { SET_INT (PIR2); pl = 0104; } else CLR_INT (PIR2); if (PIRQ & PIRQ_PIR3) { SET_INT (PIR3); pl = 0146; } else CLR_INT (PIR3); if (PIRQ & PIRQ_PIR4) { SET_INT (PIR4); pl = 0210; } else CLR_INT (PIR4); if (PIRQ & PIRQ_PIR5) { SET_INT (PIR5); pl = 0252; } else CLR_INT (PIR5); if (PIRQ & PIRQ_PIR6) { SET_INT (PIR6); pl = 0314; } else CLR_INT (PIR6); if (PIRQ & PIRQ_PIR7) { SET_INT (PIR7); pl = 0356; } else CLR_INT (PIR7); PIRQ = PIRQ | pl; return; } /* Stack trap routine */ void set_stack_trap (int32 adr) { if (CPUT (HAS_STKLF)) { /* fixed stack? */ setTRAP (TRAP_YEL); /* always yellow trap */ setCPUERR (CPUE_YEL); } else if (CPUT (HAS_STKLR)) { /* register limit? */ if (adr >= (STKLIM + STKL_R)) { /* yellow zone? */ setTRAP (TRAP_YEL); /* still yellow trap */ setCPUERR (CPUE_YEL); } else { /* red zone abort */ setCPUERR (CPUE_RED); STACKFILE[MD_KER] = 4; SP = 4; ABORT (TRAP_RED); } } return; /* no stack limit */ } /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { PIRQ = 0; STKLIM = 0; PSW = 000340; MMR0 = 0; MMR1 = 0; MMR2 = 0; MMR3 = 0; trap_req = 0; wait_state = 0; if (M == NULL) M = (uint16 *) calloc (MEMSIZE >> 1, sizeof (uint16)); if (M == NULL) return SCPE_MEM; 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'); set_r_display (0, MD_KER); return SCPE_OK; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { int32 iodata; t_stat stat; if (vptr == NULL) return SCPE_ARG; if (sw & SWMASK ('V')) { /* -v */ if (addr >= VASIZE) return SCPE_NXM; addr = relocC (addr, sw); /* relocate */ if (addr >= MAXMEMSIZE) return SCPE_REL; } if (addr < MEMSIZE) { *vptr = M[addr >> 1] & 0177777; return SCPE_OK; } if (addr < IOPAGEBASE) return SCPE_NXM; stat = iopageR (&iodata, addr, READC); *vptr = iodata; return stat; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if (sw & SWMASK ('V')) { /* -v */ if (addr >= VASIZE) return SCPE_NXM; addr = relocC (addr, sw); /* relocate */ if (addr >= MAXMEMSIZE) return SCPE_REL; } if (addr < MEMSIZE) { M[addr >> 1] = val & 0177777; return SCPE_OK; } if (addr < IOPAGEBASE) return SCPE_NXM; return iopageW ((int32) val, addr, WRITEC); } /* Set R, SP register display addresses */ void set_r_display (int32 rs, int32 cm) { extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); REG *rptr; int32 i; rptr = find_reg ("R0", NULL, &cpu_dev); if (rptr == NULL) return; for (i = 0; i < 6; i++, rptr++) rptr->loc = (void *) ®FILE[i][rs]; rptr->loc = (void *) &STACKFILE[cm]; return; } /* 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 j, k, di, lnt, ir; char *cptr = (char *) desc; t_value sim_eval[HIST_ILNT]; t_stat r; InstHistory *h; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; 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 PSW src dst IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(di++) % hst_lnt]; /* entry pointer */ if (h->pc & HIST_VLD) { /* instruction? */ ir = h->inst[0]; fprintf (st, "%06o %06o|", h->pc & ~HIST_VLD, h->psw); if (((ir & 0070000) != 0) || /* dops, eis, fpp */ ((ir & 0177000) == 0004000)) /* jsr */ fprintf (st, "%06o %06o ", h->src, h->dst); else if ((ir >= 0000100) && /* not no opnd */ (((ir & 0007700) < 0000300) || /* not branch */ ((ir & 0007700) >= 0004000))) fprintf (st, " %06o ", h->dst); else fprintf (st, " "); for (j = 0; j < HIST_ILNT; j++) sim_eval[j] = h->inst[j]; if ((fprint_sym (st, h->pc & ~HIST_VLD, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %06o", h->inst[0]); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } /* Virtual address translation */ t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) { t_stat r; char *cptr = (char *) desc; uint32 va, pa; if (cptr) { va = (uint32) get_uint (cptr, 8, VAMASK, &r); if (r == SCPE_OK) { pa = relocC (va, sim_switches); /* relocate */ if (pa < MAXMEMSIZE) fprintf (of, "Virtual %-o = physical %-o\n", va, pa); else fprintf (of, "Virtual %-o is not valid\n", va); return SCPE_OK; } } fprintf (of, "Invalid argument\n"); return SCPE_OK; } simh-3.8.1/PDP11/pdp11_sys.c0000644000175000017500000011505011112125662013414 0ustar vlmvlm/* pdp11_sys.c: PDP-11 simulator interface Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 19-Nov-08 RMS Moved I/O support routines to I/O library 15-May-08 RMS Added KE11-A, DC11 support Renamed DL11 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices 25-Jan-08 RMS Added RC11, KG11A support from John Dundas 10-Sep-07 RMS Cleaned up binary loader 20-Dec-06 RMS Added TA11 support 12-Nov-06 RMS Fixed operand order in EIS instructions (found by W.F.J. Mueller) 14-Jul-06 RMS Reordered device list 06-Jul-06 RMS Added multiple KL11/DL11 support 26-Jun-06 RMS Added RF11 support 17-May-06 RMS Added CR11/CD11 support (from John Dundas) 16-Aug-05 RMS Fixed C++ declaration and cast problems 22-Jul-05 RMS Fixed missing , in initializer (from Doug Gwyn) 22-Dec-03 RMS Added second DEUNA/DELUA support 18-Oct-03 RMS Added DECtape off reel message 06-May-03 RMS Added support for second DEQNA/DELQA 09-Jan-03 RMS Added DELUA/DEUNA support 17-Oct-02 RMS Fixed bugs in branch, SOB address parsing 09-Oct-02 RMS Added DELQA support 12-Sep-02 RMS Added TMSCP, KW11P, RX211 support, RAD50 examine 29-Nov-01 RMS Added read only unit support 17-Sep-01 RMS Removed multiconsole support 26-Aug-01 RMS Added DZ11 20-Aug-01 RMS Updated bad block inquiry 17-Jul-01 RMS Fixed warning from VC++ 6.0 27-May-01 RMS Added multiconsole support 05-Apr-01 RMS Added support for TS11/TSV05 14-Mar-01 RMS Revised load/dump interface (again) 11-Feb-01 RMS Added DECtape support 30-Oct-00 RMS Added support for examine to file 14-Apr-99 RMS Changed t_addr to unsigned 09-Nov-98 RMS Fixed assignments of ROR/ROL (John Wilson) 27-Oct-98 RMS V2.4 load interface 08-Oct-98 RMS Fixed bug in bad block routine 30-Mar-98 RMS Fixed bug in floating point display 12-Nov-97 RMS Added bad block table routine */ #include "pdp11_defs.h" #include extern DEVICE cpu_dev; extern DEVICE sys_dev; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE tti_dev; extern DEVICE tto_dev; extern DEVICE lpt_dev; extern DEVICE cr_dev; extern DEVICE clk_dev; extern DEVICE pclk_dev; extern DEVICE dli_dev; extern DEVICE dlo_dev; extern DEVICE dci_dev; extern DEVICE dco_dev; extern DEVICE dz_dev; extern DEVICE vh_dev; extern DEVICE dt_dev; extern DEVICE rc_dev; extern DEVICE rf_dev; extern DEVICE rk_dev; extern DEVICE rl_dev; extern DEVICE hk_dev; extern DEVICE rx_dev; extern DEVICE ry_dev; extern DEVICE mba_dev[]; extern DEVICE rp_dev; extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; extern DEVICE tm_dev; extern DEVICE tq_dev; extern DEVICE ts_dev; extern DEVICE tu_dev; extern DEVICE ta_dev; extern DEVICE xq_dev, xqb_dev; extern DEVICE xu_dev, xub_dev; extern DEVICE ke_dev; extern DEVICE kg_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint16 *M; extern int32 saved_PC; /* 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[] = "PDP-11"; REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 4; DEVICE *sim_devices[] = { &cpu_dev, &sys_dev, &mba_dev[0], &mba_dev[1], &clk_dev, &pclk_dev, &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, &cr_dev, &lpt_dev, &dli_dev, &dlo_dev, &dci_dev, &dco_dev, &dz_dev, &vh_dev, &rc_dev, &rf_dev, &rk_dev, &rl_dev, &hk_dev, &rx_dev, &ry_dev, &rp_dev, &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev, &dt_dev, &tm_dev, &ts_dev, &tq_dev, &tu_dev, &ta_dev, &xq_dev, &xqb_dev, &xu_dev, &xub_dev, &ke_dev, &kg_dev, NULL }; const char *sim_stop_messages[] = { "Unknown error", "Red stack trap", "Odd address trap", "Memory management trap", "Non-existent memory trap", "Parity error trap", "Privilege trap", "Illegal instruction trap", "BPT trap", "IOT trap", "EMT trap", "TRAP trap", "Trace trap", "Yellow stack trap", "Powerfail trap", "Floating point exception", "HALT instruction", "Breakpoint", "Wait state", "Trap vector fetch abort", "Trap stack push abort", "RQDX3 consistency error", "Sanity timer expired", "DECtape off reel" }; /* Binary loader. Loader format consists of blocks, optionally preceded, separated, and followed by zeroes. Each block consists of: 001 --- xxx | lo_count | hi_count | lo_origin > count bytes hi_origin | data byte | : | data byte --- checksum If the byte count is exactly six, the block is the last on the tape, and there is no checksum. If the origin is not 000001, then the origin is the PC at which to start the program. */ t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 c[6], d, i, cnt, csum; uint32 org; if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; do { /* block loop */ csum = 0; /* init checksum */ for (i = 0; i < 6; ) { /* 6 char header */ if ((c[i] = getc (fileref)) == EOF) return SCPE_FMT; if ((i != 0) || (c[i] == 1)) /* 1st must be 1 */ csum = csum + c[i++]; /* add into csum */ } cnt = (c[3] << 8) | c[2]; /* count */ org = (c[5] << 8) | c[4]; /* origin */ if (cnt < 6) /* invalid? */ return SCPE_FMT; if (cnt == 6) { /* end block? */ if (org != 1) /* set PC? */ saved_PC = org & 0177776; return SCPE_OK; } for (i = 6; i < cnt; i++) { /* exclude hdr */ if ((d = getc (fileref)) == EOF) /* data char */ return SCPE_FMT; csum = csum + d; /* add into csum */ if (org >= MEMSIZE) /* invalid addr? */ return SCPE_NXM; M[org >> 1] = (org & 1)? /* store data */ (M[org >> 1] & 0377) | (d << 8): (M[org >> 1] & 0177400) | d; org = (org + 1) & 0177777; /* inc origin */ } if ((d = getc (fileref)) == EOF) /* get csum */ return SCPE_FMT; csum = csum + d; /* add in */ } while ((csum & 0377) == 0); /* result mbz */ return SCPE_CSUM; } /* Symbol tables */ #define I_V_L 16 /* long mode */ #define I_V_D 17 /* double mode */ #define I_L (1 << I_V_L) #define I_D (1 << I_V_D) /* Warning: for literals, the class number MUST equal the field width!! */ #define I_V_CL 18 /* class bits */ #define I_M_CL 037 /* class mask */ #define I_V_NPN 0 /* no operands */ #define I_V_REG 1 /* reg */ #define I_V_SOP 2 /* operand */ #define I_V_3B 3 /* 3b literal */ #define I_V_FOP 4 /* flt operand */ #define I_V_AFOP 5 /* fac, flt operand */ #define I_V_6B 6 /* 6b literal */ #define I_V_BR 7 /* cond branch */ #define I_V_8B 8 /* 8b literal */ #define I_V_SOB 9 /* reg, disp */ #define I_V_RSOP 10 /* reg, operand */ #define I_V_ASOP 11 /* fac, operand */ #define I_V_ASMD 12 /* fac, moded int op */ #define I_V_DOP 13 /* double operand */ #define I_V_CCC 14 /* CC clear */ #define I_V_CCS 15 /* CC set */ #define I_V_SOPR 16 /* operand, reg */ #define I_NPN (I_V_NPN << I_V_CL) #define I_REG (I_V_REG << I_V_CL) #define I_3B (I_V_3B << I_V_CL) #define I_SOP (I_V_SOP << I_V_CL) #define I_FOP (I_V_FOP << I_V_CL) #define I_6B (I_V_6B << I_V_CL) #define I_BR (I_V_BR << I_V_CL) #define I_8B (I_V_8B << I_V_CL) #define I_AFOP (I_V_AFOP << I_V_CL) #define I_ASOP (I_V_ASOP << I_V_CL) #define I_RSOP (I_V_RSOP << I_V_CL) #define I_SOB (I_V_SOB << I_V_CL) #define I_ASMD (I_V_ASMD << I_V_CL) #define I_DOP (I_V_DOP << I_V_CL) #define I_CCC (I_V_CCC << I_V_CL) #define I_CCS (I_V_CCS << I_V_CL) #define I_SOPR (I_V_SOPR << I_V_CL) static const int32 masks[] = { 0177777, 0177770, 0177700, 0177770, 0177700+I_D, 0177400+I_D, 0177700, 0177400, 0177400, 0177000, 0177000, 0177400, 0177400+I_D+I_L, 0170000, 0177777, 0177777, 0177000 }; static const char *opcode[] = { "HALT","WAIT","RTI","BPT", "IOT","RESET","RTT","MFPT", "JMP","RTS","SPL", "NOP","CLC","CLV","CLV CLC", "CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", "CLN","CLN CLC","CLN CLV","CLN CLV CLC", "CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", "NOP","SEC","SEV","SEV SEC", "SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", "SEN","SEN SEC","SEN SEV","SEN SEV SEC", "SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", "SWAB","BR","BNE","BEQ", "BGE","BLT","BGT","BLE", "JSR", "CLR","COM","INC","DEC", "NEG","ADC","SBC","TST", "ROR","ROL","ASR","ASL", "MARK","MFPI","MTPI","SXT", "CSM", "TSTSET","WRTLCK", "MOV","CMP","BIT","BIC", "BIS","ADD", "MUL","DIV","ASH","ASHC", "XOR", "FADD","FSUB","FMUL","FDIV", "L2DR", "MOVC","MOVRC","MOVTC", "LOCC","SKPC","SCANC","SPANC", "CMPC","MATC", "ADDN","SUBN","CMPN","CVTNL", "CVTPN","CVTNP","ASHN","CVTLN", "L3DR", "ADDP","SUBP","CMPP","CVTPL", "MULP","DIVP","ASHP","CVTLP", "MOVCI","MOVRCI","MOVTCI", "LOCCI","SKPCI","SCANCI","SPANCI", "CMPCI","MATCI", "ADDNI","SUBNI","CMPNI","CVTNLI", "CVTPNI","CVTNPI","ASHNI","CVTLNI", "ADDPI","SUBPI","CMPPI","CVTPLI", "MULPI","DIVPI","ASHPI","CVTLPI", "SOB", "BPL","BMI","BHI","BLOS", "BVC","BVS","BCC","BCS", "BHIS","BLO", /* encode only */ "EMT","TRAP", "CLRB","COMB","INCB","DECB", "NEGB","ADCB","SBCB","TSTB", "RORB","ROLB","ASRB","ASLB", "MTPS","MFPD","MTPD","MFPS", "MOVB","CMPB","BITB","BICB", "BISB","SUB", "CFCC","SETF","SETI","SETD","SETL", "LDFPS","STFPS","STST", "CLRF","CLRD","TSTF","TSTD", "ABSF","ABSD","NEGF","NEGD", "MULF","MULD","MODF","MODD", "ADDF","ADDD","LDF","LDD", "SUBF","SUBD","CMPF","CMPD", "STF","STD","DIVF","DIVD", "STEXP", "STCFI","STCDI","STCFL","STCDL", "STCFD","STCDF", "LDEXP", "LDCIF","LDCID","LDCLF","LDCLD", "LDCFD","LDCDF", NULL }; static const int32 opc_val[] = { 0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, 0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, 0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, 0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, 0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, 0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, 0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, 0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, 0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, 0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, 0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, 0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, 0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, 0004000+I_RSOP, 0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, 0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, 0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, 0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, 0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, 0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, 0050000+I_DOP, 0060000+I_DOP, 0070000+I_SOPR, 0071000+I_SOPR, 0072000+I_SOPR, 0073000+I_SOPR, 0074000+I_RSOP, 0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, 0076020+I_REG, 0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, 0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, 0076044+I_NPN, 0076045+I_NPN, 0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, 0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, 0076060+I_REG, 0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, 0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, 0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, 0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, 0076144+I_NPN, 0076145+I_NPN, 0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, 0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, 0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, 0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, 0077000+I_SOB, 0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, 0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, 0103000+I_BR, 0103400+I_BR, 0104000+I_8B, 0104400+I_8B, 0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, 0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, 0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, 0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, 0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, 0150000+I_DOP, 0160000+I_DOP, 0170000+I_NPN, 0170001+I_NPN, 0170002+I_NPN, 0170011+I_NPN, 0170012+I_NPN, 0170100+I_SOP, 0170200+I_SOP, 0170300+I_SOP, 0170400+I_FOP, 0170400+I_FOP+I_D, 0170500+I_FOP, 0170500+I_FOP+I_D, 0170600+I_FOP, 0170600+I_FOP+I_D, 0170700+I_FOP, 0170700+I_FOP+I_D, 0171000+I_AFOP, 0171000+I_AFOP+I_D, 0171400+I_AFOP, 0171400+I_AFOP+I_D, 0172000+I_AFOP, 0172000+I_AFOP+I_D, 0172400+I_AFOP, 0172400+I_AFOP+I_D, 0173000+I_AFOP, 0173000+I_AFOP+I_D, 0173400+I_AFOP, 0173400+I_AFOP+I_D, 0174000+I_AFOP, 0174000+I_AFOP+I_D, 0174400+I_AFOP, 0174400+I_AFOP+I_D, 0175000+I_ASOP, 0175400+I_ASMD, 0175400+I_ASMD+I_D, 0175400+I_ASMD+I_L, 0175400+I_ASMD+I_D+I_L, 0176000+I_AFOP, 0176000+I_AFOP+I_D, 0176400+I_ASOP, 0177000+I_ASMD, 0177000+I_ASMD+I_D, 0177000+I_ASMD+I_L, 0177000+I_ASMD+I_D+I_L, 0177400+I_AFOP, 0177400+I_AFOP+I_D, -1 }; static const char *rname [] = { "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" }; static const char *fname [] = { "F0", "F1", "F2", "F3", "F4", "F5", "?6", "?7" }; static const char r50_to_asc[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; /* Specifier decode Inputs: *of = output stream addr = current PC spec = specifier nval = next word flag = TRUE if decoding for CPU iflag = TRUE if decoding integer instruction Outputs: count = -number of extra words retired */ int32 fprint_spec (FILE *of, t_addr addr, int32 spec, t_value nval, int32 flag, int32 iflag) { int32 reg, mode; static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 }; static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 }; reg = spec & 07; mode = ((spec >> 3) & 07); switch (mode) { case 0: if (iflag) fprintf (of, "%s", rname[reg]); else fprintf (of, "%s", fname[reg]); break; case 1: fprintf (of, "(%s)", rname[reg]); break; case 2: if (reg != 7) fprintf (of, "(%s)+", rname[reg]); else fprintf (of, "#%-o", nval); break; case 3: if (reg != 7) fprintf (of, "@(%s)+", rname[reg]); else fprintf (of, "@#%-o", nval); break; case 4: fprintf (of, "-(%s)", rname[reg]); break; case 5: fprintf (of, "@-(%s)", rname[reg]); break; case 6: if ((reg != 7) || !flag) fprintf (of, "%-o(%s)", nval, rname[reg]); else fprintf (of, "%-o", (nval + addr + 4) & 0177777); break; case 7: if ((reg != 7) || !flag) fprintf (of, "@%-o(%s)", nval, rname[reg]); else fprintf (of, "@%-o", (nval + addr + 4) & 0177777); break; } /* end case */ return ((reg == 07)? pcwd[mode]: rgwd[mode]); } /* Symbolic decode Inputs: *of = output stream addr = current PC *val = values to decode *uptr = pointer to unit sw = switches Outputs: return = if >= 0, error code if < 0, number of extra words retired */ t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { int32 cflag, i, j, c1, c2, c3, inst, fac, srcm, srcr, dstm, dstr; int32 bflag, l8b, brdisp, wd1, wd2; extern int32 FPS; bflag = 0; /* assume 16b */ cflag = (uptr == NULL) || (uptr == &cpu_unit); /* cpu? */ if (!cflag) { /* not cpu? */ DEVICE *dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) bflag = 1; } if (sw & SWMASK ('A')) { /* ASCII? */ if (bflag) c1 = val[0] & 0177; else c1 = (val[0] >> ((addr & 1)? 8: 0)) & 0177; fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); return 0; } if (sw & SWMASK ('B')) { /* byte? */ if (bflag) c1 = val[0] & 0177; else c1 = (val[0] >> ((addr & 1)? 8: 0)) & 0377; fprintf (of, "%o", c1); return 0; } if (bflag) /* 16b only */ return SCPE_ARG; if (sw & SWMASK ('C')) { /* character? */ c1 = val[0] & 0177; c2 = (val[0] >> 8) & 0177; fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); return -1; } if (sw & SWMASK ('R')) { /* radix 50? */ if (val[0] > 0174777) /* max value */ return SCPE_ARG; c3 = val[0] % 050; c2 = (val[0] / 050) % 050; c1 = val[0] / (050 * 050); fprintf (of, "%c%c%c", r50_to_asc[c1], r50_to_asc[c2], r50_to_asc[c3]); return -1; } if (!(sw & SWMASK ('M'))) return SCPE_ARG; inst = val[0] | ((FPS << (I_V_L - FPS_V_L)) & I_L) | ((FPS << (I_V_D - FPS_V_D)) & I_D); /* inst + fp mode */ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */ srcm = (inst >> 6) & 077; /* opr fields */ srcr = srcm & 07; fac = srcm & 03; dstm = inst & 077; dstr = dstm & 07; l8b = inst & 0377; wd1 = wd2 = 0; switch (j) { /* case on class */ case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ fprintf (of, "%s", opcode[i]); break; case I_V_REG: /* reg */ fprintf (of, "%s %-s", opcode[i], rname[dstr]); break; case I_V_SOP: /* sop */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); break; case I_V_3B: /* 3b */ fprintf (of, "%s %-o", opcode[i], dstr); break; case I_V_FOP: /* fop */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, FALSE); break; case I_V_AFOP: /* afop */ fprintf (of, "%s %s,", opcode[i], fname[fac]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, FALSE); break; case I_V_6B: /* 6b */ fprintf (of, "%s %-o", opcode[i], dstm); break; case I_V_BR: /* cond branch */ fprintf (of, "%s ", opcode[i]); brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; if (cflag) fprintf (of, "%-o", (addr + brdisp) & 0177777); else if (brdisp < 01000) fprintf (of, ".+%-o", brdisp); else fprintf (of, ".-%-o", 0200000 - brdisp); break; case I_V_8B: /* 8b */ fprintf (of, "%s %-o", opcode[i], l8b); break; case I_V_SOB: /* sob */ fprintf (of, "%s %s,", opcode[i], rname[srcr]); brdisp = (dstm * 2) - 2; if (cflag) fprintf (of, "%-o", (addr - brdisp) & 0177777); else if (brdisp <= 0) fprintf (of, ".+%-o", -brdisp); else fprintf (of, ".-%-o", brdisp); break; case I_V_RSOP: /* rsop */ fprintf (of, "%s %s,", opcode[i], rname[srcr]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); break; case I_V_SOPR: /* sopr */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); fprintf (of, ",%s", rname[srcr]); break; case I_V_ASOP: case I_V_ASMD: /* asop, asmd */ fprintf (of, "%s %s,", opcode[i], fname[fac]); wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); break; case I_V_DOP: /* dop */ fprintf (of, "%s ", opcode[i]); wd1 = fprint_spec (of, addr, srcm, val[1], cflag, TRUE); fprintf (of, ","); wd2 = fprint_spec (of, addr - wd1 - wd1, dstm, val[1 - wd1], cflag, TRUE); break; } /* end case */ return ((wd1 + wd2) * 2) - 1; } /* end if */ } /* end for */ return SCPE_ARG; /* no match */ } #define A_PND 100 /* # seen */ #define A_MIN 040 /* -( seen */ #define A_PAR 020 /* (Rn) seen */ #define A_REG 010 /* Rn seen */ #define A_PLS 004 /* + seen */ #define A_NUM 002 /* number seen */ #define A_REL 001 /* relative addr seen */ /* Register number Inputs: *cptr = pointer to input string *strings = pointer to register names mchar = character to match after register name Outputs: rnum = 0..7 if a legitimate register < 0 if error */ int32 get_reg (char *cptr, const char *strings[], char mchar) { int32 i; if (*(cptr + 2) != mchar) return -1; for (i = 0; i < 8; i++) { if (strncmp (cptr, strings[i], 2) == 0) return i; } return -1; } /* Number or memory address Inputs: *cptr = pointer to input string *dptr = pointer to output displacement *pflag = pointer to accumulating flags Outputs: cptr = pointer to next character in input string NULL if parsing error Flags: 0 (no result), A_NUM (number), A_REL (relative) */ char *get_addr (char *cptr, int32 *dptr, int32 *pflag) { int32 val, minus; char *tptr; minus = 0; if (*cptr == '.') { /* relative? */ *pflag = *pflag | A_REL; cptr++; } if (*cptr == '+') { /* +? */ *pflag = *pflag | A_NUM; cptr++; } if (*cptr == '-') { /* -? */ *pflag = *pflag | A_NUM; minus = 1; cptr++; } errno = 0; val = strtoul (cptr, &tptr, 8); if (cptr == tptr) { /* no number? */ if (*pflag == (A_REL + A_NUM)) /* .+, .-? */ return NULL; *dptr = 0; return cptr; } if (errno || (*pflag == A_REL)) /* .n? */ return NULL; *dptr = (minus? -val: val) & 0177777; *pflag = *pflag | A_NUM; return tptr; } /* Specifier decode Inputs: *cptr = pointer to input string addr = current PC n1 = 0 if no extra word used -1 if extra word used in prior decode *sptr = pointer to output specifier *dptr = pointer to output displacement cflag = true if parsing for the CPU iflag = true if integer specifier Outputs: status = = -1 extra word decoded = 0 ok = +1 error */ t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr, int32 cflag, int32 iflag) { int32 reg, indir, pflag, disp; indir = 0; /* no indirect */ pflag = 0; if (*cptr == '@') { /* indirect? */ indir = 010; cptr++; } if (*cptr == '#') { /* literal? */ pflag = pflag | A_PND; cptr++; } if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ pflag = pflag | A_MIN; cptr++; } else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1; if (*cptr == '(') { /* register index? */ pflag = pflag | A_PAR; if ((reg = get_reg (cptr + 1, rname, ')')) < 0) return 1; cptr = cptr + 4; if (*cptr == '+') { /* autoincrement? */ pflag = pflag | A_PLS; cptr++; } } else if ((reg = get_reg (cptr, iflag? rname: fname, 0)) >= 0) { pflag = pflag | A_REG; cptr = cptr + 2; } if (*cptr != 0) /* all done? */ return 1; switch (pflag) { /* case on syntax */ case A_REG: /* Rn, @Rn */ *sptr = indir + reg; return 0; case A_PAR: /* (Rn), @(Rn) */ if (indir) { /* @(Rn) = @0(Rn) */ *sptr = 070 + reg; *dptr = 0; return -1; } else *sptr = 010 + reg; return 0; case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ *sptr = 020 + indir + reg; return 0; case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ *sptr = 040 + indir + reg; return 0; case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ *sptr = 060 + indir + reg; *dptr = disp; return -1; case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ if (!cflag) return 1; disp = (disp + addr) & 0177777; /* fall through */ case A_PND+A_NUM: /* #n, @#n */ *sptr = 027 + indir; *dptr = disp; return -1; case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ *sptr = 067 + indir; *dptr = (disp - 4 + (2 * n1)) & 0177777; return -1; case A_NUM: /* n, @n */ if (cflag) { /* CPU - use rel */ *sptr = 067 + indir; *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; } else { if (indir) return 1; /* other - use abs */ *sptr = 037; *dptr = disp; } return -1; default: return 1; } /* end case */ } /* Symbolic input Inputs: *cptr = pointer to input string addr = current PC *uptr = pointer to unit *val = pointer to output values sw = switches Outputs: status = > 0 error code <= 0 -number of extra words */ t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { int32 bflag, cflag, d, i, j, reg, spec, n1, n2, disp, pflag; t_value by; t_stat r; char *tptr, gbuf[CBUFSIZE]; bflag = 0; /* assume 16b */ cflag = (uptr == NULL) || (uptr == &cpu_unit); /* cpu? */ if (!cflag) { /* not cpu? */ DEVICE *dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; if (dptr->dwidth < 16) bflag = 1; } while (isspace (*cptr)) cptr++; /* absorb spaces */ if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; if (bflag) val[0] = (t_value) cptr[0]; else val[0] = (addr & 1)? (val[0] & 0377) | (((t_value) cptr[0]) << 8): (val[0] & ~0377) | ((t_value) cptr[0]); return 0; } if (sw & SWMASK ('B')) { /* byte? */ by = get_uint (cptr, 8, 0377, &r); /* get byte */ if (r != SCPE_OK) return SCPE_ARG; if (bflag) val[0] = by; else val[0] = (addr & 1)? (val[0] & 0377) | (by << 8): (val[0] & ~0377) | by; return 0; } if (bflag) return SCPE_ARG; if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ if (cptr[0] == 0) /* must have 1 char */ return SCPE_ARG; val[0] = ((t_value) cptr[1] << 8) | (t_value) cptr[0]; return -1; } if (sw & SWMASK ('R')) /* radix 50 */ return SCPE_ARG; cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ n1 = n2 = pflag = 0; for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; if (opcode[i] == NULL) return SCPE_ARG; val[0] = opc_val[i] & 0177777; /* get value */ j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ switch (j) { /* case on class */ case I_V_NPN: /* no operand */ break; case I_V_REG: /* register */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; val[0] = val[0] | reg; break; case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ cptr = get_glyph (cptr, gbuf, 0); /* get literal */ d = get_uint (gbuf, 8, (1 << j) - 1, &r); if (r != SCPE_OK) return SCPE_ARG; val[0] = val[0] | d; /* put in place */ break; case I_V_BR: /* cond br */ cptr = get_glyph (cptr, gbuf, 0); /* get address */ tptr = get_addr (gbuf, &disp, &pflag); /* parse */ if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; if ((pflag & A_REL) == 0) { if (cflag) disp = (disp - addr) & 0177777; else return SCPE_ARG; } if ((disp & 1) || (disp > 0400) && (disp < 0177402)) return SCPE_ARG; val[0] = val[0] | (((disp - 2) >> 1) & 0377); break; case I_V_SOB: /* sob */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); cptr = get_glyph (cptr, gbuf, 0); /* get address */ tptr = get_addr (gbuf, &disp, &pflag); /* parse */ if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; if ((pflag & A_REL) == 0) { if (cflag) disp = (disp - addr) & 0177777; else return SCPE_ARG; } if ((disp & 1) || ((disp > 2) && (disp < 0177604))) return SCPE_ARG; val[0] = val[0] | (((2 - disp) >> 1) & 077); break; case I_V_RSOP: /* reg, sop */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); /* fall through */ case I_V_SOP: /* sop */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) return SCPE_ARG; val[0] = val[0] | spec; break; case I_V_SOPR: /* dop, reg */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) return SCPE_ARG; val[0] = val[0] | spec; cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; val[0] = val[0] | (reg << 6); break; case I_V_AFOP: case I_V_ASOP: case I_V_ASMD: /* fac, (s)fop */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((reg = get_reg (gbuf, fname, 0)) < 0) return SCPE_ARG; if (reg > 3) return SCPE_ARG; val[0] = val[0] | (reg << 6); /* fall through */ case I_V_FOP: /* fop */ cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, (j == I_V_ASOP) || (j == I_V_ASMD))) > 0) return SCPE_ARG; val[0] = val[0] | spec; break; case I_V_DOP: /* double op */ cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) return SCPE_ARG; val[0] = val[0] | (spec << 6); cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ if ((n2 = get_spec (gbuf, addr, n1, &spec, &val[1 - n1], cflag, TRUE)) > 0) return SCPE_ARG; val[0] = val[0] | spec; break; case I_V_CCC: case I_V_CCS: /* cond code oper */ 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 ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || (opcode[i] == NULL)) return SCPE_ARG; val[0] = val[0] | (opc_val[i] & 0177777); } break; default: return SCPE_ARG; } if (*cptr != 0) /* junk at end? */ return SCPE_ARG; return ((n1 + n2) * 2) - 1; } simh-3.8.1/PDP11/pdp11_rh.c0000644000175000017500000007650311112123700013207 0ustar vlmvlm/* pdp11_rh.c: PDP-11 Massbus adapter simulator Copyright (c) 2005-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rha, rhb RH11/RH70 Massbus adapter 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) 17-May-07 RMS Moved CS1 drive enable to devices 21-Nov-05 RMS Added enable/disable routine 07-Jul-05 RMS Removed extraneous externs WARNING: The interupt logic of the RH11/RH70 is unusual and must be simulated with great precision. The RH11 has an internal interrupt request flop, CSTB INTR, which is controlled as follows: - Writing IE and DONE simultaneously sets CSTB INTR - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR (and also clear IE) - A transition of DONE from 0 to 1 sets CSTB INTR from IE The output of CSTB INTR is OR'd with the AND of RPCS1 to create the interrupt request signal. Thus, - The DONE interrupt is edge sensitive, but the SC interrupt is level sensitive. - The DONE interrupt, once set, is not disabled if IE is cleared, but the SC interrupt is. */ #if defined (VM_PDP10) /* PDP10 version */ #error "PDP-10 uses pdp10_rp.c and pdp10_tu.c!" #elif defined (VM_VAX) /* VAX version */ #error "VAX uses vax780_mba.c!" #else /* PDP-11 version */ #include "pdp11_defs.h" #endif /* CS1 - base + 000 - control/status 1 */ #define CS1_OF 0 #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) #define FNC_XFER 024 /* >=? data xfr */ #define CS1_IE CSR_IE /* int enable */ #define CS1_DONE CSR_DONE /* ready */ #define CS1_V_UAE 8 /* Unibus addr ext */ #define CS1_M_UAE 03 #define CS1_UAE (CS1_M_UAE << CS1_V_UAE) #define CS1_MCPE 0020000 /* Mbus par err NI */ #define CS1_TRE 0040000 /* transfer err */ #define CS1_SC 0100000 /* special cond */ #define CS1_MBZ 0012000 #define CS1_DRV (CS1_FNC | CS1_GO) #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) /* WC - base + 002 - word count */ #define WC_OF 1 /* BA - base + 004 - base address */ #define BA_OF 2 #define BA_MBZ 0000001 /* must be zero */ /* CS2 - base + 010 - control/status 2 */ #define CS2_OF 3 #define CS2_V_UNIT 0 /* unit pos */ #define CS2_M_UNIT 07 /* unit mask */ #define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) #define CS2_UAI 0000010 /* addr inhibit */ #define CS2_PAT 0000020 /* parity test NI */ #define CS2_CLR 0000040 /* controller clear */ #define CS2_IR 0000100 /* input ready */ #define CS2_OR 0000200 /* output ready */ #define CS2_MDPE 0000400 /* Mbus par err NI */ #define CS2_MXF 0001000 /* missed xfer NI */ #define CS2_PGE 0002000 /* program err */ #define CS2_NEM 0004000 /* nx mem err */ #define CS2_NED 0010000 /* nx drive err */ #define CS2_PE 0020000 /* parity err NI */ #define CS2_WCE 0040000 /* write check err */ #define CS2_DLT 0100000 /* data late NI */ #define CS2_MBZ (CS2_CLR) #define CS2_RW (CS2_UNIT | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) #define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ CS2_NED | CS2_PE | CS2_WCE | CS2_DLT) #define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) /* DB - base + 022 - data buffer */ #define DB_OF 4 /* BAE - base + 050/34 - bus address extension */ #define BAE_OF 5 #define AE_M_MAE 0 /* addr ext pos */ #define AE_V_MAE 077 /* addr ext mask */ #define AE_MBZ 0177700 /* CS3 - base + 052/36 - control/status 3 */ #define CS3_OF 6 #define CS3_APE 0100000 /* addr perr - NI */ #define CS3_DPO 0040000 /* data perr odd - NI */ #define CS3_DPE 0020000 /* data perr even - NI */ #define CS3_WCO 0010000 /* wchk err odd */ #define CS3_WCE 0004000 /* wchk err even */ #define CS3_DBL 0002000 /* dbl word xfer - NI */ #define CS3_IPCK 0000017 /* wrong par - NI */ #define CS3_ERR (CS3_APE|CS3_DPO|CS3_DPE|CS3_WCO|CS3_WCE) #define CS3_MBZ 0001660 #define CS3_RW (CS1_IE | CS3_IPCK) #define MBA_OFSMASK 077 /* max 32 reg */ #define INT 0000 /* int reg flag */ #define EXT 0100 /* ext reg flag */ /* Declarations */ #define RH11 (cpu_opt & OPT_RH11) typedef struct { uint32 cs1; /* ctrl/status 1 */ uint32 wc; /* word count */ uint32 ba; /* bus addr */ uint32 cs2; /* ctrl/status 2 */ uint32 db; /* data buffer */ uint32 bae; /* addr ext */ uint32 cs3; /* ctrl/status 3 */ uint32 iff; /* int flip flop */ } MBACTX; MBACTX massbus[MBA_NUM]; extern int32 cpu_opt, cpu_bme; extern uint16 *M; extern int32 int_req[IPL_HLVL]; extern t_addr cpu_memsize; extern FILE *sim_deb; extern FILE *sim_log; extern int32 sim_switches; t_stat mba_reset (DEVICE *dptr); t_stat mba_rd (int32 *val, int32 pa, int32 access); t_stat mba_wr (int32 val, int32 pa, int32 access); t_stat mba_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat mba_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); int32 mba0_inta (void); int32 mba1_inta (void); void mba_set_int (uint32 mb); void mba_clr_int (uint32 mb); void mba_upd_cs1 (uint32 set, uint32 clr, uint32 mb); void mba_set_cs2 (uint32 flg, uint32 mb); uint32 mba_map_pa (int32 pa, int32 *ofs); DEVICE mba0_dev, mba1_dev; extern uint32 Map_Addr (uint32 ba); /* Massbus register dispatches */ static t_stat (*mbregR[MBA_NUM])(int32 *dat, int32 ad, int32 md); static t_stat (*mbregW[MBA_NUM])(int32 dat, int32 ad, int32 md); static int32 (*mbabort[MBA_NUM])(void); /* Unibus to register offset map */ static int32 mba_mapofs[(MBA_OFSMASK + 1) >> 1] = { INT|0, INT|1, INT|2, EXT|5, INT|3, EXT|1, EXT|2, EXT|4, EXT|7, INT|4, EXT|3, EXT|6, EXT|8, EXT|9, EXT|10, EXT|11, EXT|12, EXT|13, EXT|14, EXT|15, EXT|16, EXT|17, EXT|18, EXT|19, EXT|20, EXT|21, EXT|22, EXT|23, EXT|24, EXT|25, EXT|26, EXT|27 }; /* Massbus adapter data structures mbax_dev RHx device descriptor mbax_unit RHx units mbax_reg RHx register list */ DIB mba0_dib = { IOBA_RP, IOLN_RP, &mba_rd, &mba_wr, 1, IVCL (RP), VEC_RP, { &mba0_inta } }; UNIT mba0_unit = { UDATA (NULL, 0, 0) }; REG mba0_reg[] = { { ORDATA (CS1, massbus[0].cs1, 16) }, { ORDATA (WC, massbus[0].wc, 16) }, { ORDATA (BA, massbus[0].ba, 16) }, { ORDATA (CS2, massbus[0].cs2, 16) }, { ORDATA (DB, massbus[0].db, 16) }, { ORDATA (BAE, massbus[0].bae, 6) }, { ORDATA (CS3, massbus[0].cs3, 16) }, { FLDATA (IFF, massbus[0].iff, 0) }, { FLDATA (INT, IREQ (RP), INT_V_RP) }, { FLDATA (SC, massbus[0].cs1, CSR_V_ERR) }, { FLDATA (DONE, massbus[0].cs1, CSR_V_DONE) }, { FLDATA (IE, massbus[0].cs1, CSR_V_IE) }, { ORDATA (DEVADDR, mba0_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, mba0_dib.vec, 16), REG_HRO }, { NULL } }; MTAB mba0_mod[] = { { MTAB_XTD|MTAB_VDV, 0100, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DIB mba1_dib = { IOBA_TU, IOLN_TU, &mba_rd, &mba_wr, 1, IVCL (TU), VEC_TU, { &mba1_inta } }; UNIT mba1_unit = { UDATA (NULL, 0, 0) }; REG mba1_reg[] = { { ORDATA (CS1, massbus[1].cs1, 16) }, { ORDATA (WC, massbus[1].wc, 16) }, { ORDATA (BA, massbus[1].ba, 16) }, { ORDATA (CS2, massbus[1].cs2, 16) }, { ORDATA (DB, massbus[1].db, 16) }, { ORDATA (BAE, massbus[1].bae, 6) }, { ORDATA (CS3, massbus[1].cs3, 16) }, { FLDATA (IFF, massbus[1].iff, 0) }, { FLDATA (INT, IREQ (TU), INT_V_TU) }, { FLDATA (SC, massbus[1].cs1, CSR_V_ERR) }, { FLDATA (DONE, massbus[1].cs1, CSR_V_DONE) }, { FLDATA (IE, massbus[1].cs1, CSR_V_IE) }, { ORDATA (DEVADDR, mba1_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, mba1_dib.vec, 16), REG_HRO }, { NULL } }; MTAB mba1_mod[] = { { MTAB_XTD|MTAB_VDV, 0040, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE mba_dev[] = { { "RHA", &mba0_unit, mba0_reg, mba0_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &mba_reset, NULL, NULL, NULL, &mba0_dib, DEV_DEBUG | DEV_DISABLE | DEV_UBUS | DEV_QBUS }, { "RHB", &mba1_unit, mba1_reg, mba1_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &mba_reset, NULL, NULL, NULL, &mba1_dib, DEV_DEBUG | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS } }; /* Read Massbus adapter register */ t_stat mba_rd (int32 *val, int32 pa, int32 mode) { int32 ofs, dat, mb, drv; t_stat r; mb = mba_map_pa (pa, &ofs); /* get mb number */ if ((mb < 0) || (ofs < 0)) /* valid? */ return SCPE_NXM; drv = GET_UNIT (massbus[mb].cs2); /* get drive */ mba_upd_cs1 (0, 0, mb); /* update CS1 */ if (ofs & EXT) { /* external? */ if (!mbregR[mb]) /* device there? */ return SCPE_NXM; r = mbregR[mb] (val, ofs & ~EXT, drv); /* call device */ if (r == MBE_NXD) /* nx drive? */ mba_set_cs2 (CS2_NED, mb); else if (r == MBE_NXR) /* nx reg? */ return SCPE_NXM; return SCPE_OK; } switch (ofs) { /* case on reg */ case CS1_OF: /* CS1 */ if (!mbregR[mb]) /* nx device? */ return SCPE_NXM; r = mbregR[mb] (&dat, ofs, drv); /* get dev cs1 */ if (r == MBE_NXD) /* nx drive? */ mba_set_cs2 (CS2_NED, mb); *val = massbus[mb].cs1 | dat; break; case WC_OF: /* WC */ *val = massbus[mb].wc; break; case BA_OF: /* BA */ *val = massbus[mb].ba & ~BA_MBZ; break; case CS2_OF: /* CS2 */ *val = massbus[mb].cs2 = (massbus[mb].cs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; break; case DB_OF: /* DB */ *val = massbus[mb].db; break; case BAE_OF: /* BAE */ *val = massbus[mb].bae = massbus[mb].bae & ~AE_MBZ; break; case CS3_OF: /* CS3 */ *val = massbus[mb].cs3 = (massbus[mb].cs3 & ~(CS1_IE | CS3_MBZ)) | (massbus[mb].cs1 & CS1_IE); break; default: /* huh? */ return SCPE_NXM; } return SCPE_OK; } t_stat mba_wr (int32 val, int32 pa, int32 access) { int32 ofs, cs1f, drv, mb; t_stat r; t_bool cs1dt; mb = mba_map_pa (pa, &ofs); /* get mb number */ if ((mb < 0) || (ofs < 0)) /* valid? */ return SCPE_NXM; drv = GET_UNIT (massbus[mb].cs2); /* get drive */ if (ofs & EXT) { /* external? */ if (!mbregW[mb]) /* device there? */ return SCPE_NXM; if ((access == WRITEB) && (pa & 1)) /* byte writes */ val = val << 8; /* don't work */ r = mbregW[mb] (val, ofs & ~EXT, drv); /* write dev reg */ if (r == MBE_NXD) /* nx drive? */ mba_set_cs2 (CS2_NED, mb); else if (r == MBE_NXR) /* nx reg? */ return SCPE_NXM; mba_upd_cs1 (0, 0, mb); /* update status */ return SCPE_OK; } cs1f = 0; /* no int on cs1 upd */ switch (ofs) { /* case on reg */ case CS1_OF: /* CS1 */ if (!mbregW[mb]) /* device exist? */ return SCPE_NXM; if ((access == WRITEB) && (pa & 1)) val = val << 8; if (val & CS1_TRE) { /* error clear? */ massbus[mb].cs1 &= ~CS1_TRE; /* clr CS1 */ massbus[mb].cs2 &= ~CS2_ERR; /* clr CS2<15:8> */ massbus[mb].cs3 &= ~CS3_ERR; /* clr CS3<15:11> */ } if ((access == WRITE) || (pa & 1)) { /* hi byte write? */ if (massbus[mb].cs1 & CS1_DONE) /* done set? */ massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_UAE) | (val & CS1_UAE); } if ((access == WRITE) || !(pa & 1)) { /* lo byte write? */ if ((val & CS1_DONE) && (val & CS1_IE)) /* to DONE+IE? */ massbus[mb].iff = 1; /* set CSTB INTR */ massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_IE) | (val & CS1_IE); cs1dt = (val & CS1_GO) && (GET_FNC (val) >= FNC_XFER); if (cs1dt && ((massbus[mb].cs1 & CS1_DONE) == 0)) /* dt, done clr? */ mba_set_cs2 (CS2_PGE, mb); /* prgm error */ else { r = mbregW[mb] (val & 077, ofs, drv); /* write dev CS1 */ if (r == MBE_NXD) /* nx drive? */ mba_set_cs2 (CS2_NED, mb); else if (r == MBE_NXR) /* nx reg? */ return SCPE_NXM; else if (cs1dt && (r == SCPE_OK)) { /* xfer, no err? */ massbus[mb].cs1 &= ~(CS1_TRE | CS1_MCPE | CS1_DONE); massbus[mb].cs2 &= ~CS2_ERR; /* clear errors */ massbus[mb].cs3 &= ~(CS3_ERR | CS3_DBL); } } } massbus[mb].cs3 = (massbus[mb].cs3 & ~CS1_IE) | /* update CS3 */ (massbus[mb].cs1 & CS1_IE); massbus[mb].bae = (massbus[mb].bae & ~CS1_M_UAE) | /* update BAE */ ((massbus[mb].cs1 >> CS1_V_UAE) & CS1_M_UAE); break; case WC_OF: /* WC */ if (access == WRITEB) val = (pa & 1)? (massbus[mb].wc & 0377) | (val << 8): (massbus[mb].wc & ~0377) | val; massbus[mb].wc = val; break; case BA_OF: /* BA */ if (access == WRITEB) val = (pa & 1)? (massbus[mb].ba & 0377) | (val << 8): (massbus[mb].ba & ~0377) | val; massbus[mb].ba = val & ~BA_MBZ; break; case CS2_OF: /* CS2 */ if ((access == WRITEB) && (pa & 1)) val = val << 8; if (val & CS2_CLR) /* init? */ mba_reset (&mba_dev[mb]); else { if ((val & ~massbus[mb].cs2) & (CS2_PE | CS2_MXF)) cs1f = CS1_SC; /* diagn intr */ if (access == WRITEB) /* merge val */ val = (massbus[mb].cs2 & ((pa & 1)? 0377: 0177400)) | val; massbus[mb].cs2 = (massbus[mb].cs2 & ~CS2_RW) | (val & CS2_RW) | CS2_IR | CS2_OR; } break; case DB_OF: /* DB */ if (access == WRITEB) val = (pa & 1)? (massbus[mb].db & 0377) | (val << 8): (massbus[mb].db & ~0377) | val; massbus[mb].db = val; break; case BAE_OF: /* BAE */ if ((access == WRITEB) && (pa & 1)) break; massbus[mb].bae = val & ~AE_MBZ; massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_UAE) | /* update CS1 */ ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); break; case CS3_OF: /* CS3 */ if ((access == WRITEB) && (pa & 1)) break; massbus[mb].cs3 = (massbus[mb].cs3 & ~CS3_RW) | (val & CS3_RW); massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_IE) | /* update CS1 */ (massbus[mb].cs3 & CS1_IE); break; default: return SCPE_NXM; } mba_upd_cs1 (cs1f, 0, mb); /* update status */ return SCPE_OK; } /* Massbus I/O routines mb_rdbufW - fetch word buffer from memory mb_wrbufW - store word buffer into memory mb_chbufW - compare word buffer with memory Returns number of bytes successfully transferred/checked */ int32 mba_rdbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa; bc = bc & ~1; /* bc even */ if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (RH11 && cpu_bme) /* map addr */ pa = Map_Addr (ba); else pa = ba; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_set_cs2 (CS2_NEM, mb); /* set error */ break; } pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; for (j = 0; j < pbc; j = j + 2) { /* loop by words */ *buf++ = M[pa >> 1]; /* fetch word */ if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ ba = ba + 2; /* incr ba, pa */ pa = pa + 2; } } } massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ massbus[mb].ba = ba & DMASK; /* update ba */ massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); return i; } int32 mba_wrbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa; bc = bc & ~1; /* bc even */ if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (RH11 && cpu_bme) /* map addr */ pa = Map_Addr (ba); else pa = ba; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_set_cs2 (CS2_NEM, mb); /* set error */ break; } pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; for (j = 0; j < pbc; j = j + 2) { /* loop by words */ M[pa >> 1] = *buf++; /* put word */ if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ ba = ba + 2; /* incr ba, pa */ pa = pa + 2; } } } massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ massbus[mb].ba = ba & DMASK; /* update ba */ massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); return i; } int32 mba_chbufW (uint32 mb, int32 bc, uint16 *buf) { int32 i, j, ba, mbc, pbc; uint32 pa; bc = bc & ~1; /* bc even */ if (mb >= MBA_NUM) /* valid MBA? */ return 0; ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ if (bc > mbc) /* use smaller */ bc = mbc; for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ if (RH11 && cpu_bme) pa = Map_Addr (ba); /* map addr */ else pa = ba; if (!ADDR_IS_MEM (pa)) { /* NXM? */ mba_set_cs2 (CS2_NEM, mb); /* set error */ break; } pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ if (pbc > (bc - i)) /* limit to rem xfr */ pbc = bc - i; for (j = 0; j < pbc; j = j + 2) { /* loop by words */ massbus[mb].db = *buf++; /* get dev word */ if (M[pa >> 1] != massbus[mb].db) { /* miscompare? */ mba_set_cs2 (CS2_WCE, mb); /* set error */ massbus[mb].cs3 = massbus[mb].cs3 | /* set even/odd */ ((pa & 1)? CS3_WCO: CS3_WCE); break; } if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ ba = ba + 2; /* incr ba, pa */ pa = pa + 2; } } } massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ massbus[mb].ba = ba & DMASK; /* update ba */ massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); return i; } /* Device access, status, and interrupt routines */ void mba_set_don (uint32 mb) { mba_upd_cs1 (CS1_DONE, 0, mb); return; } void mba_upd_ata (uint32 mb, uint32 val) { if (val) mba_upd_cs1 (CS1_SC, 0, mb); else mba_upd_cs1 (0, CS1_SC, mb); return; } void mba_set_exc (uint32 mb) { mba_upd_cs1 (CS1_TRE | CS1_DONE, 0, mb); return; } int32 mba_get_bc (uint32 mb) { if (mb >= MBA_NUM) return 0; return ((0200000 - massbus[mb].wc) << 1); } int32 mba_get_csr (uint32 mb) { DIB *dibp; if (mb >= MBA_NUM) return 0; dibp = (DIB *) mba_dev[mb].ctxt; return dibp->ba; } void mba_set_int (uint32 mb) { DIB *dibp; if (mb >= MBA_NUM) return; dibp = (DIB *) mba_dev[mb].ctxt; int_req[dibp->vloc >> 5] |= (1 << (dibp->vloc & 037)); return; } void mba_clr_int (uint32 mb) { DIB *dibp; if (mb >= MBA_NUM) return; dibp = (DIB *) mba_dev[mb].ctxt; int_req[dibp->vloc >> 5] &= ~(1 << (dibp->vloc & 037)); return; } void mba_upd_cs1 (uint32 set, uint32 clr, uint32 mb) { if (mb >= MBA_NUM) return; if ((set & ~massbus[mb].cs1) & CS1_DONE) /* DONE 0 to 1? */ massbus[mb].iff = (massbus[mb].cs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ massbus[mb].cs1 = (massbus[mb].cs1 & ~(clr | CS1_MCPE | CS1_MBZ | CS1_DRV)) | set; if (massbus[mb].cs2 & CS2_ERR) massbus[mb].cs1 = massbus[mb].cs1 | CS1_TRE | CS1_SC; else if (massbus[mb].cs1 & CS1_TRE) massbus[mb].cs1 = massbus[mb].cs1 | CS1_SC; if (massbus[mb].iff || ((massbus[mb].cs1 & CS1_SC) && (massbus[mb].cs1 & CS1_DONE) && (massbus[mb].cs1 & CS1_IE))) mba_set_int (mb); else mba_clr_int (mb); return; } void mba_set_cs2 (uint32 flag, uint32 mb) { if (mb >= MBA_NUM) return; massbus[mb].cs2 = massbus[mb].cs2 | flag; mba_upd_cs1 (0, 0, mb); return; } /* Interrupt acknowledge */ int32 mba0_inta (void) { massbus[0].cs1 &= ~CS1_IE; /* clear int enable */ massbus[0].cs3 &= ~CS1_IE; /* in both registers */ massbus[0].iff = 0; /* clear CSTB INTR */ return mba0_dib.vec; /* acknowledge */ } int32 mba1_inta (void) { massbus[1].cs1 &= ~CS1_IE; /* clear int enable */ massbus[1].cs3 &= ~CS1_IE; /* in both registers */ massbus[1].iff = 0; /* clear CSTB INTR */ return mba1_dib.vec; /* acknowledge */ } /* Map physical address to Massbus number, offset */ uint32 mba_map_pa (int32 pa, int32 *ofs) { int32 i, uo, ba, lnt; DIB *dibp; for (i = 0; i < MBA_NUM; i++) { /* loop thru ctrls */ dibp = (DIB *) mba_dev[i].ctxt; /* get DIB */ ba = dibp->ba; lnt = dibp->lnt; if ((pa >= ba) && /* in range? */ (pa < (ba + lnt))) { if (pa < (ba + (lnt - 4))) { /* not last two? */ uo = ((pa - ba) & MBA_OFSMASK) >> 1; /* get Unibus offset */ *ofs = mba_mapofs[uo]; /* map thru PROM */ return i; /* return ctrl idx */ } else if (RH11) /* RH11? done */ return -1; else { /* RH70 */ uo = (pa - (ba + (lnt - 4))) >> 1; /* offset relative */ *ofs = BAE_OF + uo; /* to BAE */ return i; } } } return -1; } /* Reset Massbus adapter */ t_stat mba_reset (DEVICE *dptr) { uint32 mb; mb = dptr - mba_dev; if (mb >= MBA_NUM) return SCPE_NOFNC; massbus[mb].cs1 = CS1_DONE; massbus[mb].wc = 0; massbus[mb].ba = 0; massbus[mb].cs2 = 0; massbus[mb].db = 0; massbus[mb].bae= 0; massbus[mb].cs3 = 0; massbus[mb].iff = 0; mba_clr_int (mb); if (mbabort[mb]) mbabort[mb] (); return SCPE_OK; } /* Enable/disable Massbus adapter */ void mba_set_enbdis (uint32 mb, t_bool dis) { if (mb >= MBA_NUM) /* valid MBA? */ return; if (dis) mba_dev[mb].flags |= DEV_DIS; else mba_dev[mb].flags &= ~DEV_DIS; return; } /* Show Massbus adapter number */ t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr = find_dev_from_unit (uptr); DIB *dibp; if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "Massbus adapter %d", dibp->ba); return SCPE_OK; } /* Init Mbus tables */ void init_mbus_tab (void) { uint32 i; for (i = 0; i < MBA_NUM; i++) { mbregR[i] = NULL; mbregW[i] = NULL; mbabort[i] = NULL; } return; } /* Build dispatch tables */ t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp) { uint32 idx; if ((dptr == NULL) || (dibp == NULL)) /* validate args */ return SCPE_IERR; idx = dibp->ba; /* Mbus # */ if (idx >= MBA_NUM) return SCPE_STOP; if ((mbregR[idx] && dibp->rd && /* conflict? */ (mbregR[idx] != dibp->rd)) || (mbregW[idx] && dibp->wr && (mbregW[idx] != dibp->wr)) || (mbabort[idx] && dibp->ack[0] && (mbabort[idx] != dibp->ack[0]))) { printf ("Massbus %s assignment conflict at %d\n", sim_dname (dptr), dibp->ba); if (sim_log) fprintf (sim_log, "Massbus %s assignment conflict at %d\n", sim_dname (dptr), dibp->ba); return SCPE_STOP; } if (dibp->rd) /* set rd dispatch */ mbregR[idx] = dibp->rd; if (dibp->wr) /* set wr dispatch */ mbregW[idx] = dibp->wr; if (dibp->ack[0]) /* set abort dispatch */ mbabort[idx] = dibp->ack[0]; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rk.c0000644000175000017500000007724711110160622013222 0ustar vlmvlm/* pdp11_rk.c: RK11/RKV11 cartridge disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rk RK11/RKV11/RK05 cartridge disk 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 30-Sep-04 RMS Revised Unibus interface 24-Jan-04 RMS Added increment inhibit, overrun detection, formatting 29-Dec-03 RMS Added RKV11 support 29-Sep-02 RMS Added variable address support to bootstrap Added vector change/display support Revised mapping mnemonics New data structures 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted FLG to array 09-Nov-01 RMS Added bus map support 07-Sep-01 RMS Revised device disable and interrupt mechanisms 26-Apr-01 RMS Added device enable/disable support 25-Mar-01 RMS Fixed block fill calculation 15-Feb-01 RMS Corrected bootstrap string 29-Jun-96 RMS Added unit disable support The RK11 is an eight drive cartridge disk subsystem. An RK05 drive consists of 203 cylinders, each with 2 surfaces containing 12 sectors of 512 bytes. The most complicated part of the RK11 controller is the concept of interrupt "polling". While only one read or write can occur at a time, the controller supports multiple seeks. When a seek completes, if done is set the drive attempts to interrupt. If an interrupt is already pending, the interrupt is "queued" until it can be processed. When an interrupt occurs, RKDS<15:13> is loaded with the number of the interrupting drive. To implement this structure, and to assure that read/write interrupts take priority over seek interrupts, the controller contains an interrupt queue, rkintq, with a bit for a controller interrupt and then one for each drive. In addition, the drive number of the last non-seeking drive is recorded in last_drv. */ #include "pdp11_defs.h" /* Constants */ #define RK_NUMWD 256 /* words/sector */ #define RK_NUMSC 12 /* sectors/surface */ #define RK_NUMSF 2 /* surfaces/cylinder */ #define RK_NUMCY 203 /* cylinders/drive */ #define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */ #define RK_NUMDR 8 /* drives/controller */ #define RK_M_NUMDR 07 #define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */ #define RK_CTLI 1 /* controller int */ #define RK_SCPI(x) (2u << (x)) /* drive int */ #define RK_MAXFR (1 << 16) /* max transfer */ /* Flags in the unit flags word */ #define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ #define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ #define UNIT_HWLK (1u << UNIT_V_HWLK) #define UNIT_SWLK (1u << UNIT_V_SWLK) #define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write prot */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ #define FUNC u4 /* function */ /* RKDS */ #define RKDS_SC 0000017 /* sector counter */ #define RKDS_ON_SC 0000020 /* on sector */ #define RKDS_WLK 0000040 /* write locked */ #define RKDS_RWS 0000100 /* rd/wr/seek ready */ #define RKDS_RDY 0000200 /* drive ready */ #define RKDS_SC_OK 0000400 /* SC valid */ #define RKDS_INC 0001000 /* seek incomplete */ #define RKDS_UNSAFE 0002000 /* unsafe */ #define RKDS_RK05 0004000 /* RK05 */ #define RKDS_PWR 0010000 /* power low */ #define RKDS_ID 0160000 /* drive ID */ #define RKDS_V_ID 13 /* RKER */ #define RKER_WCE 0000001 /* write check */ #define RKER_CSE 0000002 /* checksum */ #define RKER_NXS 0000040 /* nx sector */ #define RKER_NXC 0000100 /* nx cylinder */ #define RKER_NXD 0000200 /* nx drive */ #define RKER_TE 0000400 /* timing error */ #define RKER_DLT 0001000 /* data late */ #define RKER_NXM 0002000 /* nx memory */ #define RKER_PGE 0004000 /* programming error */ #define RKER_SKE 0010000 /* seek error */ #define RKER_WLK 0020000 /* write lock */ #define RKER_OVR 0040000 /* overrun */ #define RKER_DRE 0100000 /* drive error */ #define RKER_IMP 0177743 /* implemented */ #define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */ #define RKER_HARD 0177740 /* hard errors */ /* RKCS */ #define RKCS_M_FUNC 0000007 /* function */ #define RKCS_CTLRESET 0 #define RKCS_WRITE 1 #define RKCS_READ 2 #define RKCS_WCHK 3 #define RKCS_SEEK 4 #define RKCS_RCHK 5 #define RKCS_DRVRESET 6 #define RKCS_WLK 7 #define RKCS_V_FUNC 1 #define RKCS_MEX 0000060 /* memory extension */ #define RKCS_V_MEX 4 #define RKCS_SSE 0000400 /* stop on soft err */ #define RKCS_FMT 0002000 /* format */ #define RKCS_INH 0004000 /* inhibit increment */ #define RKCS_SCP 0020000 /* search complete */ #define RKCS_HERR 0040000 /* hard error */ #define RKCS_ERR 0100000 /* error */ #define RKCS_REAL 0026776 /* kept here */ #define RKCS_RW 0006576 /* read/write */ #define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC) /* RKDA */ #define RKDA_V_SECT 0 /* sector */ #define RKDA_M_SECT 017 #define RKDA_V_TRACK 4 /* track */ #define RKDA_M_TRACK 0777 #define RKDA_V_CYL 5 /* cylinder */ #define RKDA_M_CYL 0377 #define RKDA_V_DRIVE 13 /* drive */ #define RKDA_M_DRIVE 07 #define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE) #define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT) #define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL) #define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK) #define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE) #define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x)) /* RKBA */ #define RKBA_IMP 0177776 /* implemented */ #define RK_MIN 10 #define MAX(x,y) (((x) > (y))? (x): (y)) extern uint16 *M; /* memory */ extern int32 int_req[IPL_HLVL]; uint16 *rkxb = NULL; /* xfer buffer */ int32 rkcs = 0; /* control/status */ int32 rkds = 0; /* drive status */ int32 rkba = 0; /* memory address */ int32 rkda = 0; /* disk address */ int32 rker = 0; /* error status */ int32 rkwc = 0; /* word count */ int32 rkintq = 0; /* interrupt queue */ int32 last_drv = 0; /* last r/w drive */ int32 rk_stopioe = 1; /* stop on error */ int32 rk_swait = 10; /* seek time */ int32 rk_rwait = 10; /* rotate time */ DEVICE rk_dev; t_stat rk_rd (int32 *data, int32 PA, int32 access); t_stat rk_wr (int32 data, int32 PA, int32 access); int32 rk_inta (void); t_stat rk_svc (UNIT *uptr); t_stat rk_reset (DEVICE *dptr); void rk_go (void); void rk_set_done (int32 error); void rk_clr_done (void); t_stat rk_boot (int32 unitno, DEVICE *dptr); /* RK11 data structures rk_dev RK device descriptor rk_unit RK unit list rk_reg RK register list rk_mod RK modifier list */ DIB rk_dib = { IOBA_RK, IOLN_RK, &rk_rd, &rk_wr, 1, IVCL (RK), VEC_RK, { &rk_inta } }; UNIT rk_unit[] = { { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) }, { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE, RK_SIZE) } }; REG rk_reg[] = { { ORDATA (RKCS, rkcs, 16) }, { ORDATA (RKDA, rkda, 16) }, { ORDATA (RKBA, rkba, 16) }, { ORDATA (RKWC, rkwc, 16) }, { ORDATA (RKDS, rkds, 16) }, { ORDATA (RKER, rker, 16) }, { ORDATA (INTQ, rkintq, 9) }, { ORDATA (DRVN, last_drv, 3) }, { FLDATA (INT, IREQ (RK), INT_V_RK) }, { FLDATA (ERR, rkcs, CSR_V_ERR) }, { FLDATA (DONE, rkcs, CSR_V_DONE) }, { FLDATA (IE, rkcs, CSR_V_IE) }, { DRDATA (STIME, rk_swait, 24), PV_LEFT }, { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, { FLDATA (STOP_IOE, rk_stopioe, 0) }, { ORDATA (DEVADDR, rk_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, rk_dib.vec, 16), REG_HRO }, { NULL } }; MTAB rk_mod[] = { { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE rk_dev = { "RK", rk_unit, rk_reg, rk_mod, RK_NUMDR, 8, 24, 1, 8, 16, NULL, NULL, &rk_reset, &rk_boot, NULL, NULL, &rk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 }; /* I/O dispatch routine, I/O addresses 17777400 - 17777416 17777400 RKDS read only, constructed from "id'd drive" plus current drive status flags 17777402 RKER read only, set as operations progress, cleared by INIT or CONTROL RESET 17777404 RKCS read/write 17777406 RKWC read/write 17777410 RKBA read/write 17777412 RKDA read/write 17777414 RKMR read/write, unimplemented 17777416 RKDB read only, unimplemented */ t_stat rk_rd (int32 *data, int32 PA, int32 access) { UNIT *uptr; switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RKDS: read only */ rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK | (rand () % RK_NUMSC); /* random sector */ uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */ if (uptr->flags & UNIT_ATT) /* attached? */ rkds = rkds | RKDS_RDY; if (!sim_is_active (uptr)) /* idle? */ rkds = rkds | RKDS_RWS; if (uptr->flags & UNIT_WPRT) rkds = rkds | RKDS_WLK; if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC; *data = rkds; return SCPE_OK; case 1: /* RKER: read only */ *data = rker & RKER_IMP; return SCPE_OK; case 2: /* RKCS */ rkcs = rkcs & RKCS_REAL; if (rker) /* update err flags */ rkcs = rkcs | RKCS_ERR; if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; *data = rkcs; return SCPE_OK; case 3: /* RKWC */ *data = rkwc; return SCPE_OK; case 4: /* RKBA */ *data = rkba & RKBA_IMP; return SCPE_OK; case 5: /* RKDA */ *data = rkda; return SCPE_OK; default: *data = 0; return SCPE_OK; } /* end switch */ } t_stat rk_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RKDS: read only */ return SCPE_OK; case 1: /* RKER: read only */ return SCPE_OK; case 2: /* RKCS */ rkcs = rkcs & RKCS_REAL; if (access == WRITEB) data = (PA & 1)? (rkcs & 0377) | (data << 8): (rkcs & ~0377) | data; if ((data & CSR_IE) == 0) { /* int disable? */ rkintq = 0; /* clr int queue */ CLR_INT (RK); /* clr int request */ } else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) { rkintq = rkintq | RK_CTLI; /* queue ctrl int */ SET_INT (RK); /* set int request */ } rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW); if ((rkcs & CSR_DONE) && (data & CSR_GO)) /* new function? */ rk_go (); return SCPE_OK; case 3: /* RKWC */ if (access == WRITEB) data = (PA & 1)? (rkwc & 0377) | (data << 8): (rkwc & ~0377) | data; rkwc = data; return SCPE_OK; case 4: /* RKBA */ if (access == WRITEB) data = (PA & 1)? (rkba & 0377) | (data << 8): (rkba & ~0377) | data; rkba = data & RKBA_IMP; return SCPE_OK; case 5: /* RKDA */ if ((rkcs & CSR_DONE) == 0) return SCPE_OK; if (access == WRITEB) data = (PA & 1)? (rkda & 0377) | (data << 8): (rkda & ~0377) | data; rkda = data; return SCPE_OK; default: return SCPE_OK; } /* end switch */ } /* Initiate new function */ void rk_go (void) { int32 i, sect, cyl, func; UNIT *uptr; func = GET_FUNC (rkcs); /* get function */ if (func == RKCS_CTLRESET) { /* control reset? */ rker = 0; /* clear errors */ rkda = 0; rkba = 0; rkcs = CSR_DONE; rkintq = 0; /* clr int queue */ CLR_INT (RK); /* clr int request */ return; } rker = rker & ~RKER_SOFT; /* clear soft errors */ if (rker == 0) /* redo summary */ rkcs = rkcs & ~RKCS_ERR; rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/ rk_clr_done (); /* clear done */ last_drv = GET_DRIVE (rkda); /* get drive no */ uptr = rk_dev.units + last_drv; /* select unit */ if (uptr->flags & UNIT_DIS) { /* not present? */ rk_set_done (RKER_NXD); return; } if (((uptr->flags & UNIT_ATT) == 0) || /* not att or busy? */ sim_is_active (uptr)) { rk_set_done (RKER_DRE); return; } if ((rkcs & RKCS_FMT) && /* format and */ (func != RKCS_READ) && (func != RKCS_WRITE)) { /* not read or write? */ rk_set_done (RKER_PGE); return; } if ((func == RKCS_WRITE) && /* write and locked? */ (uptr->flags & UNIT_WPRT)) { rk_set_done (RKER_WLK); return; } if (func == RKCS_WLK) { /* write lock? */ uptr->flags = uptr->flags | UNIT_SWLK; rk_set_done (0); return; } if (func == RKCS_DRVRESET) { /* drive reset? */ uptr->flags = uptr->flags & ~UNIT_SWLK; cyl = sect = 0; func = RKCS_SEEK; } else { sect = GET_SECT (rkda); cyl = GET_CYL (rkda); } if (sect >= RK_NUMSC) { /* bad sector? */ rk_set_done (RKER_NXS); return; } if (cyl >= RK_NUMCY) { /* bad cyl? */ rk_set_done (RKER_NXC); return; } i = abs (cyl - uptr->CYL) * rk_swait; /* seek time */ if (func == RKCS_SEEK) { /* seek? */ rk_set_done (0); /* set done */ sim_activate (uptr, MAX (RK_MIN, i)); /* schedule */ } else sim_activate (uptr, i + rk_rwait); uptr->FUNC = func; /* save func */ uptr->CYL = cyl; /* put on cylinder */ return; } /* Service unit timeout If seek in progress, complete seek command Else complete data transfer command The unit control block contains the function and disk address for the current command. */ t_stat rk_svc (UNIT *uptr) { int32 i, drv, err, awc, wc, cma, cda, t; int32 da, cyl, track, sect; uint32 ma; uint16 comp; drv = (int32) (uptr - rk_dev.units); /* get drv number */ if (uptr->FUNC == RKCS_SEEK) { /* seek */ rkcs = rkcs | RKCS_SCP; /* set seek done */ if (rkcs & CSR_IE) { /* ints enabled? */ rkintq = rkintq | RK_SCPI (drv); /* queue request */ if (rkcs & CSR_DONE) SET_INT (RK); } else { rkintq = 0; /* clear queue */ CLR_INT (RK); /* clear interrupt */ } return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ rk_set_done (RKER_DRE); return IORETURN (rk_stopioe, SCPE_UNATT); } sect = GET_SECT (rkda); /* get sector, cyl */ cyl = GET_CYL (rkda); if (sect >= RK_NUMSC) { /* bad sector? */ rk_set_done (RKER_NXS); return SCPE_OK; } if (cyl >= RK_NUMCY) { /* bad cyl? */ rk_set_done (RKER_NXC); return SCPE_OK; } ma = ((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba; /* get mem addr */ da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */ wc = 0200000 - rkwc; /* get wd cnt */ if ((da + wc) > (int32) uptr->capac) { /* overrun? */ wc = uptr->capac - da; /* trim transfer */ rker = rker | RKER_OVR; /* set overrun err */ } err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); if (wc && (err == 0)) { /* seek ok? */ switch (uptr->FUNC) { /* case on function */ case RKCS_READ: /* read */ if (rkcs & RKCS_FMT) { /* format? */ for (i = 0, cda = da; i < wc; i++) { /* fill buffer with cyl #s */ if (cda >= (int32) uptr->capac) { /* overrun? */ rker = rker | RKER_OVR; /* set overrun err */ wc = i; /* trim transfer */ break; } rkxb[i] = (cda / RK_NUMWD) / (RK_NUMSF * RK_NUMSC); cda = cda + RK_NUMWD; /* next sector */ } /* end for wc */ } /* end if format */ else { /* normal read */ i = fxread (rkxb, sizeof (int16), wc, uptr->fileref); err = ferror (uptr->fileref); /* read file */ for ( ; i < wc; i++) /* fill buf */ rkxb[i] = 0; } if (rkcs & RKCS_INH) { /* incr inhibit? */ if (t = Map_WriteW (ma, 2, &rkxb[wc - 1])) { /* store last */ rker = rker | RKER_NXM; /* NXM? set flag */ wc = 0; /* no transfer */ } } else { /* normal store */ if (t = Map_WriteW (ma, wc << 1, rkxb)) { /* store buf */ rker = rker | RKER_NXM; /* NXM? set flag */ wc = wc - t; /* adj wd cnt */ } } break; /* end read */ case RKCS_WRITE: /* write */ if (rkcs & RKCS_INH) { /* incr inhibit? */ if (t = Map_ReadW (ma, 2, &comp)) { /* get 1st word */ rker = rker | RKER_NXM; /* NXM? set flag */ wc = 0; /* no transfer */ } for (i = 0; i < wc; i++) /* all words same */ rkxb[i] = comp; } else { /* normal fetch */ if (t = Map_ReadW (ma, wc << 1, rkxb)) { /* get buf */ rker = rker | RKER_NXM; /* NXM? set flg */ wc = wc - t; /* adj wd cnt */ } } if (wc) { /* any xfer? */ awc = (wc + (RK_NUMWD - 1)) & ~(RK_NUMWD - 1); /* clr to */ for (i = wc; i < awc; i++) /* end of blk */ rkxb[i] = 0; fxwrite (rkxb, sizeof (int16), awc, uptr->fileref); err = ferror (uptr->fileref); } break; /* end write */ case RKCS_WCHK: /* write check */ i = fxread (rkxb, sizeof (int16), wc, uptr->fileref); if (err = ferror (uptr->fileref)) { /* read error? */ wc = 0; /* no transfer */ break; } for ( ; i < wc; i++) /* fill buf */ rkxb[i] = 0; awc = wc; /* save wc */ for (wc = 0, cma = ma; wc < awc; wc++) { /* loop thru buf */ if (Map_ReadW (cma, 2, &comp)) { /* mem wd */ rker = rker | RKER_NXM; /* NXM? set flg */ break; } if (comp != rkxb[wc]) { /* match to disk? */ rker = rker | RKER_WCE; /* no, err */ if (rkcs & RKCS_SSE) break; } if (!(rkcs & RKCS_INH)) /* next mem addr */ cma = cma + 2; } /* end for */ break; /* end wcheck */ default: /* read check */ break; } /* end switch */ } /* end else */ rkwc = (rkwc + wc) & 0177777; /* final word count */ if (!(rkcs & RKCS_INH)) /* final byte addr */ ma = ma + (wc << 1); rkba = ma & RKBA_IMP; /* lower 16b */ rkcs = (rkcs & ~RKCS_MEX) | ((ma >> (16 - RKCS_V_MEX)) & RKCS_MEX); if ((uptr->FUNC == RKCS_READ) && (rkcs & RKCS_FMT)) /* read format? */ da = da + (wc * RK_NUMWD); /* count by sectors */ else da = da + wc + (RK_NUMWD - 1); /* count by words */ track = (da / RK_NUMWD) / RK_NUMSC; sect = (da / RK_NUMWD) % RK_NUMSC; rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT); rk_set_done (0); if (err != 0) { /* error? */ perror ("RK I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Interrupt state change routines rk_set_done set done and possibly errors rk_clr_done clear done rk_inta acknowledge intererupt */ void rk_set_done (int32 error) { rkcs = rkcs | CSR_DONE; /* set done */ if (error != 0) { rker = rker | error; /* update error */ if (rker) /* update err flags */ rkcs = rkcs | RKCS_ERR; if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; } if (rkcs & CSR_IE) { /* int enable? */ rkintq = rkintq | RK_CTLI; /* set ctrl int */ SET_INT (RK); /* request int */ } else { rkintq = 0; /* clear queue */ CLR_INT (RK); } return; } void rk_clr_done (void) { rkcs = rkcs & ~CSR_DONE; /* clear done */ rkintq = rkintq & ~RK_CTLI; /* clear ctl int */ CLR_INT (RK); /* clear int req */ return; } int32 rk_inta (void) { int32 i; for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */ if (rkintq & (1u << i)) { /* bit i set? */ rkintq = rkintq & ~(1u << i); /* clear bit i */ if (rkintq) /* queue next */ SET_INT (RK); rkds = (rkds & ~RKDS_ID) | /* id drive */ (((i == 0)? last_drv: i - 1) << RKDS_V_ID); return rk_dib.vec; /* return vector */ } } rkintq = 0; /* clear queue */ return 0; /* passive release */ } /* Device reset */ t_stat rk_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rkcs = CSR_DONE; rkda = rkba = rker = rkds = 0; rkintq = last_drv = 0; CLR_INT (RK); for (i = 0; i < RK_NUMDR; i++) { uptr = rk_dev.units + i; sim_cancel (uptr); uptr->CYL = uptr->FUNC = 0; uptr->flags = uptr->flags & ~UNIT_SWLK; } if (rkxb == NULL) rkxb = (uint16 *) calloc (RK_MAXFR, sizeof (uint16)); if (rkxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Device bootstrap */ #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 032) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0042113, /* "KD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012700, 0000000, /* MOV #unit, R0 ; unit number */ 0010003, /* MOV R0, R3 */ 0000303, /* SWAB R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0012701, 0177412, /* MOV #RKDA, R1 ; csr */ 0010311, /* MOV R3, (R1) ; load da */ 0005041, /* CLR -(R1) ; clear ba */ 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */ 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0105711, /* TSTB (R1) */ 0100376, /* BPL .-2 */ 0105011, /* CLRB (R1) */ 0005007 /* CLR PC */ }; t_stat rk_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR; M[BOOT_CSR >> 1] = (rk_dib.ba & DMASK) + 012; saved_PC = BOOT_ENTRY; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_tq.c0000644000175000017500000027261411112257240013232 0ustar vlmvlm/* pdp11_tq.c: TMSCP tape controller simulator Copyright (c) 2002-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tq TQK50 tape controller 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread 16-Feb-06 RMS Revised for new magtape capacity checking 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) 30-Sep-04 RMS Revised Unibus interface 12-Jun-04 RMS Fixed bug in reporting write protect (reported by Lyle Bickley) 18-Apr-04 RMS Fixed TQK70 media ID and model byte (found by Robert Schaffrath) 26-Mar-04 RMS Fixed warnings with -std=c99 25-Jan-04 RMS Revised for device debug support 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Added variable controller, user-defined drive support 26-Feb-03 RMS Fixed bug in vector calculation for VAXen 22-Feb-03 RMS Fixed ordering bug in queue process Fixed flags table to allow MD_CSE everywhere 09-Jan-03 RMS Fixed bug in transfer end packet status 17-Oct-02 RMS Fixed bug in read reverse (found by Hans Pufal) */ #if defined (VM_PDP10) /* PDP10 version */ #error "TQK50 not supported on PDP-10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #if (UNIBUS) #define INIT_TYPE TQ8_TYPE #define INIT_CAP TQ8_CAP #else #define INIT_TYPE TQ5_TYPE #define INIT_CAP TQ5_CAP #endif #else /* PDP-11 version */ #include "pdp11_defs.h" #define INIT_TYPE TQ5_TYPE #define INIT_CAP TQ5_CAP extern int32 cpu_opt; #endif #include "pdp11_uqssp.h" #include "pdp11_mscp.h" #include "sim_tape.h" #define UF_MSK (UF_SCH|UF_VSS|UF_CMR|UF_CMW) /* settable flags */ #define TQ_SH_MAX 24 /* max display wds */ #define TQ_SH_PPL 8 /* wds per line */ #define TQ_SH_DPL 4 /* desc per line */ #define TQ_SH_RI 001 /* show rings */ #define TQ_SH_FR 002 /* show free q */ #define TQ_SH_RS 004 /* show resp q */ #define TQ_SH_UN 010 /* show unit q's */ #define TQ_SH_ALL 017 /* show all */ #define TQ_CLASS 1 /* TQK50 class */ #define TQ_DHTMO 0 /* def host timeout */ #define TQ_DCTMO 120 /* def ctrl timeout */ #define TQ_NUMDR 4 /* # drives */ #define TQ_MAXFR (1 << 16) /* max xfer */ #define UNIT_V_ONL (MTUF_V_UF + 0) /* online */ #define UNIT_V_ATP (MTUF_V_UF + 1) /* attn pending */ #define UNIT_V_SXC (MTUF_V_UF + 2) /* serious exc */ #define UNIT_V_POL (MTUF_V_UF + 3) /* position lost */ #define UNIT_V_TMK (MTUF_V_UF + 4) /* tape mark seen */ #define UNIT_ONL (1 << UNIT_V_ONL) #define UNIT_ATP (1 << UNIT_V_ATP) #define UNIT_SXC (1 << UNIT_V_SXC) #define UNIT_POL (1 << UNIT_V_POL) #define UNIT_TMK (1 << UNIT_V_TMK) #define cpkt u3 /* current packet */ #define pktq u4 /* packet queue */ #define uf buf /* settable unit flags */ #define objp wait /* object position */ #define TQ_WPH(u) ((sim_tape_wrp (u))? UF_WPH: 0) #define CST_S1 0 /* init stage 1 */ #define CST_S1_WR 1 /* stage 1 wrap */ #define CST_S2 2 /* init stage 2 */ #define CST_S3 3 /* init stage 3 */ #define CST_S3_PPA 4 /* stage 3 sa wait */ #define CST_S3_PPB 5 /* stage 3 ip wait */ #define CST_S4 6 /* stage 4 */ #define CST_UP 7 /* online */ #define CST_DEAD 8 /* fatal error */ #define tq_comm tq_rq.ba #define ERR 0 /* must be SCPE_OK! */ #define OK 1 #define CMF_IMM 0x10000 /* immediate */ #define CMF_SEQ 0x20000 /* sequential */ #define CMF_WR 0x40000 /* write */ #define CMF_RW 0x80000 /* resp to GCS */ /* Internal packet management */ #define TQ_NPKTS 32 /* # packets (pwr of 2) */ #define TQ_M_NPKTS (TQ_NPKTS - 1) /* mask */ #define TQ_PKT_SIZE_W 32 /* payload size (wds) */ #define TQ_PKT_SIZE (TQ_PKT_SIZE_W * sizeof (int16)) struct tqpkt { int16 link; /* link to next */ uint16 d[TQ_PKT_SIZE_W]; /* data */ }; /* Packet payload extraction and insertion */ #define GETP(p,w,f) ((tq_pkt[p].d[w] >> w##_V_##f) & w##_M_##f) #define GETP32(p,w) (((uint32) tq_pkt[p].d[w]) | \ (((uint32) tq_pkt[p].d[(w)+1]) << 16)) #define PUTP32(p,w,x) tq_pkt[p].d[w] = (x) & 0xFFFF; \ tq_pkt[p].d[(w)+1] = ((x) >> 16) & 0xFFFF /* Controller and device types - TQK50 must be swre rev 5 or later */ #define TQ5_TYPE 0 /* TK50 */ #define TQ5_UQPM 3 /* UQ port ID */ #define TQ5_CMOD 9 /* ctrl ID */ #define TQ5_UMOD 3 /* unit ID */ #define TQ5_MED 0x6D68B032 /* media ID */ #define TQ5_CREV ((1 << 8) | 5) /* ctrl revs */ #define TQ5_FREV 0 /* formatter revs */ #define TQ5_UREV 0 /* unit revs */ #define TQ5_CAP (94 * (1 << 20)) /* capacity */ #define TQ5_FMT (TF_CTP|TF_CTP_LO) /* menu */ #define TQ7_TYPE 1 /* TK70 */ #define TQ7_UQPM 14 /* UQ port ID */ #define TQ7_CMOD 14 /* ctrl ID */ #define TQ7_UMOD 14 /* unit ID */ #define TQ7_MED 0x6D68B046 /* media ID */ #define TQ7_CREV ((1 << 8) | 5) /* ctrl revs */ #define TQ7_FREV 0 /* formatter revs */ #define TQ7_UREV 0 /* unit revs */ #define TQ7_CAP (300 * (1 << 20)) /* capacity */ #define TQ7_FMT (TF_CTP|TF_CTP_LO) /* menu */ #define TQ8_TYPE 2 /* TU81 */ #define TQ8_UQPM 5 /* UQ port ID */ #define TQ8_CMOD 5 /* ctrl ID */ #define TQ8_UMOD 2 /* unit ID */ #define TQ8_MED 0x6D695051 /* media ID */ #define TQ8_CREV ((1 << 8) | 5) /* ctrl revs */ #define TQ8_FREV 0 /* formatter revs */ #define TQ8_UREV 0 /* unit revs */ #define TQ8_CAP (180 * (1 << 20)) /* capacity */ #define TQ8_FMT (TF_9TK|TF_9TK_GRP) /* menu */ #define TQU_TYPE 3 /* TKuser defined */ #define TQU_UQPM 3 /* UQ port ID */ #define TQU_CMOD 9 /* ctrl ID */ #define TQU_UMOD 3 /* unit ID */ #define TQU_MED 0x6D68B032 /* media ID */ #define TQU_CREV ((1 << 8) | 5) /* ctrl revs */ #define TQU_FREV 0 /* formatter revs */ #define TQU_UREV 0 /* unit revs */ #define TQU_CAP (94 * (1 << 20)) /* capacity */ #define TQU_FMT (TF_CTP|TF_CTP_LO) /* menu */ #define TQU_MINC 30 /* min cap MB */ #define TQU_MAXC 2000 /* max cap MB */ #define TQU_EMAXC 2000000000 /* ext max cap MB */ #define TQ_DRV(d) \ d##_UQPM, \ d##_CMOD, d##_MED, d##_FMT, d##_CAP, \ d##_UMOD, d##_CREV, d##_FREV, d##_UREV #define TEST_EOT(u) (sim_tape_eot (u)) struct drvtyp { uint32 uqpm; /* UQ port model */ uint32 cmod; /* ctrl model */ uint32 med; /* MSCP media */ uint32 fmt; /* flags */ t_addr cap; /* capacity */ uint32 umod; /* unit model */ uint32 cver; uint32 fver; uint32 uver; char *name; }; static struct drvtyp drv_tab[] = { { TQ_DRV (TQ5), "TK50" }, { TQ_DRV (TQ7), "TK70" }, { TQ_DRV (TQ8), "TU81" }, { TQ_DRV (TQU), "TKUSER" }, }; /* Data */ extern int32 int_req[IPL_HLVL]; extern int32 tmr_poll, clk_tps; extern UNIT cpu_unit; extern FILE *sim_deb; extern uint32 sim_taddr_64; uint8 *tqxb = NULL; /* xfer buffer */ uint32 tq_sa = 0; /* status, addr */ uint32 tq_saw = 0; /* written data */ uint32 tq_s1dat = 0; /* S1 data */ uint32 tq_csta = 0; /* ctrl state */ uint32 tq_perr = 0; /* last error */ uint32 tq_cflgs = 0; /* ctrl flags */ uint32 tq_prgi = 0; /* purge int */ uint32 tq_pip = 0; /* poll in progress */ struct uq_ring tq_cq = { 0 }; /* cmd ring */ struct uq_ring tq_rq = { 0 }; /* rsp ring */ struct tqpkt tq_pkt[TQ_NPKTS]; /* packet queue */ int32 tq_freq = 0; /* free list */ int32 tq_rspq = 0; /* resp list */ uint32 tq_pbsy = 0; /* #busy pkts */ uint32 tq_credits = 0; /* credits */ uint32 tq_hat = 0; /* host timer */ uint32 tq_htmo = TQ_DHTMO; /* host timeout */ int32 tq_itime = 200; /* init time, except */ int32 tq_itime4 = 10; /* stage 4 */ int32 tq_qtime = 200; /* queue time */ int32 tq_xtime = 500; /* transfer time */ int32 tq_typ = INIT_TYPE; /* device type */ /* Command table - legal modifiers (low 16b) and flags (high 16b) */ static uint32 tq_cmf[64] = { 0, /* 0 */ CMF_IMM, /* abort */ CMF_IMM|MD_CSE, /* get cmd status */ CMF_IMM|MD_CSE|MD_NXU, /* get unit status */ CMF_IMM|MD_CSE, /* set ctrl char */ 0, 0, 0, /* 5-7 */ CMF_SEQ|MD_ACL|MD_CDL|MD_CSE|MD_EXA|MD_UNL, /* available */ CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* online */ CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* set unit char */ CMF_IMM, /* define acc paths */ 0, 0, 0, 0, /* 12-15 */ CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* access */ MD_SCH|MD_SEC|MD_SER, 0, /* 17 */ CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase */ CMF_SEQ|CMF_WR|MD_CDL|MD_CSE, /* flush */ 0, 0, /* 20-21 */ CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase gap */ 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 22-31 */ CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* compare */ MD_SCH|MD_SEC|MD_SER, CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV|MD_CMP| /* read */ MD_SCH|MD_SEC|MD_SER, CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */ MD_CMP|MD_ERW|MD_SEC|MD_SER, 0, /* 35 */ CMF_SEQ|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */ MD_REV|MD_RWD|MD_DLE| MD_SCH|MD_SEC|MD_SER, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 38-47 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Forward references */ DEVICE tq_dev; t_stat tq_rd (int32 *data, int32 PA, int32 access); t_stat tq_wr (int32 data, int32 PA, int32 access); t_stat tq_inta (void); t_stat tq_svc (UNIT *uptr); t_stat tq_tmrsvc (UNIT *uptr); t_stat tq_quesvc (UNIT *uptr); t_stat tq_reset (DEVICE *dptr); t_stat tq_attach (UNIT *uptr, char *cptr); t_stat tq_detach (UNIT *uptr); t_stat tq_boot (int32 unitno, DEVICE *dptr); t_stat tq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tq_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); t_bool tq_step4 (void); t_bool tq_mscp (int32 pkt, t_bool q); t_bool tq_abo (int32 pkt); t_bool tq_avl (int32 pkt); t_bool tq_erase (int32 pkt); t_bool tq_flu (int32 pkt); t_bool tq_gcs (int32 pkt); t_bool tq_gus (int32 pkt); t_bool tq_onl (int32 pkt); t_bool tq_pos (int32 pkt); t_bool tq_rw (int32 pkt); t_bool tq_scc (int32 pkt); t_bool tq_suc (int32 pkt); t_bool tq_wtm (int32 pkt); t_bool tq_plf (uint32 err); t_bool tq_dte (UNIT *uptr, uint32 err); t_bool tq_hbe (UNIT *uptr, uint32 ba); t_bool tq_una (UNIT *uptr); uint32 tq_map_status (UNIT *uptr, t_stat st); uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped); uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc); uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped); uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc); t_bool tq_deqf (int32 *pkt); int32 tq_deqh (int32 *lh); void tq_enqh (int32 *lh, int32 pkt); void tq_enqt (int32 *lh, int32 pkt); t_bool tq_getpkt (int32 *pkt); t_bool tq_putpkt (int32 pkt, t_bool qt); t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc); t_bool tq_putdesc (struct uq_ring *ring, uint32 desc); int32 tq_mot_valid (UNIT *uptr, uint32 cmd); t_stat tq_mot_err (UNIT *uptr, uint32 rsiz); t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz); void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ); void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all); void tq_setf_unit (int32 pkt, UNIT *uptr); uint32 tq_efl (UNIT *uptr); void tq_init_int (void); void tq_ring_int (struct uq_ring *ring); t_bool tq_fatal (uint32 err); UNIT *tq_getucb (uint32 lu); /* TQ data structures tq_dev TQ device descriptor tq_unit TQ unit list tq_reg TQ register list tq_mod TQ modifier list */ DIB tq_dib = { IOBA_TQ, IOLN_TQ, &tq_rd, &tq_wr, 1, IVCL (TQ), 0, { &tq_inta } }; UNIT tq_unit[] = { { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, { UDATA (&tq_tmrsvc, UNIT_DIS, 0) }, { UDATA (&tq_quesvc, UNIT_DIS, 0) } }; #define TQ_TIMER (TQ_NUMDR) #define TQ_QUEUE (TQ_TIMER + 1) REG tq_reg[] = { { GRDATA (SA, tq_sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, tq_saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, tq_s1dat, DEV_RDX, 16, 0) }, { GRDATA (CQBA, tq_cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, tq_cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, tq_cq.idx, DEV_RDX, 8, 2) }, { GRDATA (TQBA, tq_rq.ba, DEV_RDX, 22, 0) }, { GRDATA (TQLNT, tq_rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (TQIDX, tq_rq.idx, DEV_RDX, 8, 2) }, { DRDATA (FREE, tq_freq, 5) }, { DRDATA (RESP, tq_rspq, 5) }, { DRDATA (PBSY, tq_pbsy, 5) }, { GRDATA (CFLGS, tq_cflgs, DEV_RDX, 16, 0) }, { GRDATA (CSTA, tq_csta, DEV_RDX, 4, 0) }, { GRDATA (PERR, tq_perr, DEV_RDX, 9, 0) }, { DRDATA (CRED, tq_credits, 5) }, { DRDATA (HAT, tq_hat, 17) }, { DRDATA (HTMO, tq_htmo, 17) }, { URDATA (CPKT, tq_unit[0].cpkt, 10, 5, 0, TQ_NUMDR, 0) }, { URDATA (PKTQ, tq_unit[0].pktq, 10, 5, 0, TQ_NUMDR, 0) }, { URDATA (UFLG, tq_unit[0].uf, DEV_RDX, 16, 0, TQ_NUMDR, 0) }, { URDATA (POS, tq_unit[0].pos, 10, T_ADDR_W, 0, TQ_NUMDR, 0) }, { URDATA (OBJP, tq_unit[0].objp, 10, 32, 0, TQ_NUMDR, 0) }, { FLDATA (PRGI, tq_prgi, 0), REG_HIDDEN }, { FLDATA (PIP, tq_pip, 0), REG_HIDDEN }, { FLDATA (INT, IREQ (TQ), INT_V_TQ) }, { DRDATA (ITIME, tq_itime, 24), PV_LEFT + REG_NZ }, { DRDATA (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ }, { DRDATA (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ }, { DRDATA (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ }, { BRDATA (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) }, { DRDATA (DEVTYPE, tq_typ, 2), REG_HRO }, { DRDATA (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO }, { GRDATA (DEVADDR, tq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, tq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB tq_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV, TQ5_TYPE, NULL, "TK50", &tq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VDV, TQ7_TYPE, NULL, "TK70", &tq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VDV, TQ8_TYPE, NULL, "TU81", &tq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VDV, TQU_TYPE, NULL, "TKUSER", &tq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &tq_show_type, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RI, "RINGS", NULL, NULL, &tq_show_ctrl, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_FR, "FREEQ", NULL, NULL, &tq_show_ctrl, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RS, "RESPQ", NULL, NULL, &tq_show_ctrl, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_UN, "UNITQ", NULL, NULL, &tq_show_ctrl, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_ALL, "ALL", NULL, NULL, &tq_show_ctrl, NULL }, { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, NULL, &tq_show_unitq, NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, #if defined (VM_PDP11) { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, #else { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, NULL, &show_addr, NULL }, #endif { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE tq_dev = { "TQ", tq_unit, tq_reg, tq_mod, TQ_NUMDR + 2, 10, T_ADDR_W, 1, DEV_RDX, 8, NULL, NULL, &tq_reset, &tq_boot, &tq_attach, &tq_detach, &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; /* I/O dispatch routines, I/O addresses 17772150 - 17772152 17772150 IP read/write 17772152 SA read/write */ t_stat tq_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ *data = 0; /* reads zero */ if (tq_csta == CST_S3_PPB) /* waiting for poll? */ tq_step4 (); else if (tq_csta == CST_UP) { /* if up */ tq_pip = 1; /* poll host */ sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); } break; case 1: /* SA */ *data = tq_sa; break; } return SCPE_OK; } t_stat tq_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ tq_reset (&tq_dev); /* init device */ if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, ">>TQ: initialization started, time=%.0f\n", sim_gtime ()); break; case 1: /* SA */ tq_saw = data; if (tq_csta < CST_S4) /* stages 1-3 */ sim_activate (&tq_unit[TQ_QUEUE], tq_itime); else if (tq_csta == CST_S4) /* stage 4 (fast) */ sim_activate (&tq_unit[TQ_QUEUE], tq_itime4); break; } return SCPE_OK; } /* Transition to step 4 - init communications region */ t_bool tq_step4 (void) { int32 i, lnt; uint32 base; uint16 zero[SA_COMM_MAX >> 1]; tq_rq.ioff = SA_COMM_RI; /* set intr offset */ tq_rq.ba = tq_comm; /* set rsp q base */ tq_rq.lnt = SA_S1H_RQ (tq_s1dat) << 2; /* get resp q len */ tq_cq.ioff = SA_COMM_CI; /* set intr offset */ tq_cq.ba = tq_comm + tq_rq.lnt; /* set cmd q base */ tq_cq.lnt = SA_S1H_CQ (tq_s1dat) << 2; /* get cmd q len */ tq_cq.idx = tq_rq.idx = 0; /* clear q idx's */ if (tq_prgi) base = tq_comm + SA_COMM_QQ; else base = tq_comm + SA_COMM_CI; lnt = tq_comm + tq_cq.lnt + tq_rq.lnt - base; /* comm lnt */ if (lnt > SA_COMM_MAX) /* paranoia */ lnt = SA_COMM_MAX; for (i = 0; i < (lnt >> 1); i++) /* clr buffer */ zero[i] = 0; if (Map_WriteW (base, lnt, zero)) /* zero comm area */ return tq_fatal (PE_QWE); /* error? */ tq_sa = SA_S4 | (drv_tab[tq_typ].uqpm << SA_S4C_V_MOD) |/* send step 4 */ ((drv_tab[tq_typ].cver & 0xFF) << SA_S4C_V_VER); tq_csta = CST_S4; /* set step 4 */ tq_init_int (); /* poke host */ return OK; } /* Queue service - invoked when any of the queues (host queue, unit queues, response queue) require servicing. Also invoked during initialization to provide some delay to the next step. Process at most one item off each unit queue If the unit queues were empty, process at most one item off the host queue Process at most one item off the response queue If all queues are idle, terminate thread */ t_stat tq_quesvc (UNIT *uptr) { int32 i, cnid; int32 pkt = 0; UNIT *nuptr; if (tq_csta < CST_UP) { /* still init? */ switch (tq_csta) { /* controller state? */ case CST_S1: /* need S1 reply */ if (tq_saw & SA_S1H_VL) { /* valid? */ if (tq_saw & SA_S1H_WR) { /* wrap? */ tq_sa = tq_saw; /* echo data */ tq_csta = CST_S1_WR; /* endless loop */ } else { tq_s1dat = tq_saw; /* save data */ tq_dib.vec = (tq_s1dat & SA_S1H_VEC) << 2; /* get vector */ if (tq_dib.vec) /* if nz, bias */ tq_dib.vec = tq_dib.vec + VEC_Q; tq_sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (tq_s1dat); tq_csta = CST_S2; /* now in step 2 */ tq_init_int (); /* intr if req */ } } /* end if valid */ break; case CST_S1_WR: /* wrap mode */ tq_sa = tq_saw; /* echo data */ break; case CST_S2: /* need S2 reply */ tq_comm = tq_saw & SA_S2H_CLO; /* get low addr */ tq_prgi = tq_saw & SA_S2H_PI; /* get purge int */ tq_sa = SA_S3 | SA_S3C_EC (tq_s1dat); tq_csta = CST_S3; /* now in step 3 */ tq_init_int (); /* intr if req */ break; case CST_S3: /* need S3 reply */ tq_comm = ((tq_saw & SA_S3H_CHI) << 16) | tq_comm; if (tq_saw & SA_S3H_PP) { /* purge/poll test? */ tq_sa = 0; /* put 0 */ tq_csta = CST_S3_PPA; /* wait for 0 write */ } else tq_step4 (); /* send step 4 */ break; case CST_S3_PPA: /* need purge test */ if (tq_saw) /* data not zero? */ tq_fatal (PE_PPF); else tq_csta = CST_S3_PPB; /* wait for poll */ break; case CST_S4: /* need S4 reply */ if (tq_saw & SA_S4H_GO) { /* go set? */ if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, ">>TQ: initialization complete\n"); tq_csta = CST_UP; /* we're up */ tq_sa = 0; /* clear SA */ sim_activate (&tq_unit[TQ_TIMER], tmr_poll * clk_tps); if ((tq_saw & SA_S4H_LF) && tq_perr) tq_plf (tq_perr); tq_perr = 0; } break; } /* end switch */ return SCPE_OK; } /* end if */ for (i = 0; i < TQ_NUMDR; i++) { /* chk unit q's */ nuptr = tq_dev.units + i; /* ptr to unit */ if (nuptr->cpkt || (nuptr->pktq == 0)) continue; pkt = tq_deqh (&nuptr->pktq); /* get top of q */ if (!tq_mscp (pkt, FALSE)) /* process */ return SCPE_OK; } if ((pkt == 0) && tq_pip) { /* polling? */ if (!tq_getpkt (&pkt)) /* get host pkt */ return SCPE_OK; if (pkt) { /* got one? */ if (DEBUG_PRS (tq_dev)) { UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); fprintf (sim_deb, ">>TQ: cmd=%04X, mod=%04X, unit=%d, ", tq_pkt[pkt].d[CMD_OPC], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]); fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X", tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); if (up) fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); else fprintf (sim_deb, "\n"); fflush (sim_deb); } if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ return tq_fatal (PE_PIE); /* no, term thread */ cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ if (cnid == UQ_CID_TMSCP) { /* TMSCP packet? */ if (!tq_mscp (pkt, TRUE)) /* proc, q non-seq */ return SCPE_OK; } else if (cnid == UQ_CID_DUP) { /* DUP packet? */ tq_putr (pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); if (!tq_putpkt (pkt, TRUE)) /* ill cmd */ return SCPE_OK; } else return tq_fatal (PE_ICI); /* no, term thread */ } /* end if pkt */ else tq_pip = 0; /* discontinue poll */ } /* end if pip */ if (tq_rspq) { /* resp q? */ pkt = tq_deqh (&tq_rspq); /* get top of q */ if (!tq_putpkt (pkt, FALSE)) /* send to host */ return SCPE_OK; } /* end if resp q */ if (pkt) /* more to do? */ sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); return SCPE_OK; /* done */ } /* Clock service (roughly once per second) */ t_stat tq_tmrsvc (UNIT *uptr) { int32 i; UNIT *nuptr; sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ for (i = 0; i < TQ_NUMDR; i++) { /* poll */ nuptr = tq_dev.units + i; if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ (nuptr->flags & UNIT_ATT) && /* still online? */ (tq_cflgs & CF_ATN)) { /* wanted? */ if (!tq_una (nuptr)) return SCPE_OK; } nuptr->flags = nuptr->flags & ~UNIT_ATP; } if ((tq_hat > 0) && (--tq_hat == 0)) /* host timeout? */ tq_fatal (PE_HAT); /* fatal err */ return SCPE_OK; } /* MSCP packet handling */ t_bool tq_mscp (int32 pkt, t_bool q) { uint32 sts; uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* command */ uint32 flg = GETP (pkt, CMD_OPC, FLG); /* flags */ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ UNIT *uptr; if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */ cmd = OP_END; /* set end op */ sts = ST_CMD | I_OPCD; /* ill op */ } else if (flg) { /* flags? */ cmd = cmd | OP_END; /* set end flag */ sts = ST_CMD | I_FLAG; /* ill flags */ } else if (mdf & ~tq_cmf[cmd]) { /* invalid mod? */ cmd = cmd | OP_END; /* set end flag */ sts = ST_CMD | I_MODF; /* ill mods */ } else { /* valid cmd */ if (uptr = tq_getucb (lu)) { /* valid unit? */ if (q && (tq_cmf[cmd] & CMF_SEQ) && /* queueing, seq, */ (uptr->cpkt || uptr->pktq)) { /* and active? */ tq_enqt (&uptr->pktq, pkt); /* do later */ return OK; } /* if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */ /* uptr->flags = uptr->flags & ~UNIT_CDL; */ if (tq_cmf[cmd] & MD_CSE) /* clr ser exc? */ uptr->flags = uptr->flags & ~UNIT_SXC; } switch (cmd) { case OP_ABO: /* abort */ return tq_abo (pkt); case OP_AVL: /* avail */ return tq_avl (pkt); case OP_GCS: /* get cmd status */ return tq_gcs (pkt); case OP_GUS: /* get unit status */ return tq_gus (pkt); case OP_ONL: /* online */ return tq_onl (pkt); case OP_SCC: /* set ctrl char */ return tq_scc (pkt); case OP_SUC: /* set unit char */ return tq_suc (pkt); case OP_ERS: /* erase */ case OP_ERG: /* erase gap */ return tq_erase (pkt); case OP_FLU: /* flush */ return tq_flu (pkt); case OP_POS: /* position */ return tq_pos (pkt); case OP_WTM: /* write tape mark */ return tq_wtm (pkt); case OP_ACC: /* access */ case OP_CMP: /* compare */ case OP_RD: /* read */ case OP_WR: /* write */ return tq_rw (pkt); case OP_DAP: cmd = cmd | OP_END; /* set end flag */ sts = ST_SUC; /* success */ break; default: cmd = OP_END; /* set end op */ sts = ST_CMD | I_OPCD; /* ill op */ break; } /* end switch */ } /* end else */ tq_putr (pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Abort a command - 1st parameter is ref # of cmd to abort */ t_bool tq_abo (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ int32 tpkt, prv; UNIT *uptr; tpkt = 0; /* set no mtch */ if (uptr = tq_getucb (lu)) { /* get unit */ if (uptr->cpkt && /* curr pkt? */ (GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */ tpkt = uptr->cpkt; /* save match */ uptr->cpkt = 0; /* gonzo */ sim_cancel (uptr); /* cancel unit */ sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); } else if (uptr->pktq && /* head of q? */ (GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */ tpkt = uptr->pktq; /* save match */ uptr->pktq = tq_pkt[tpkt].link; /* unlink */ } else if (prv = uptr->pktq) { /* srch pkt q */ while (tpkt = tq_pkt[prv].link) { /* walk list */ if (GETP32 (tpkt, RSP_REFL) == ref) { /* match ref? */ tq_pkt[prv].link = tq_pkt[tpkt].link; /* unlink */ break; } } } if (tpkt) { /* found target? */ uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ tq_putr (tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); if (!tq_putpkt (tpkt, TRUE)) return ERR; } } /* end if unit */ tq_putr (pkt, OP_ABO | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Unit available - set unit status to available Deferred if q'd cmds, bypassed if ser exc */ t_bool tq_avl (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ if (uptr->flags & UNIT_SXC) /* ser exc pending? */ sts = ST_SXC; else { uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); sim_tape_rewind (uptr); /* rewind */ uptr->uf = uptr->objp = 0; /* clr flags */ if (uptr->flags & UNIT_ATT) { /* attached? */ sts = ST_SUC; /* success */ if (mdf & MD_UNL) /* unload? */ tq_detach (uptr); } else sts = ST_OFL | SB_OFL_NV; /* no, offline */ } } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Get command status - only interested in active xfr cmd */ t_bool tq_gcs (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ int32 tpkt; UNIT *uptr; if ((uptr = tq_getucb (lu)) && /* valid lu? */ (tpkt = uptr->cpkt) && /* queued pkt? */ (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ (tq_cmf[GETP (tpkt, CMD_OPC, OPC)] & CMF_RW)) { /* rd/wr cmd? */ tq_pkt[pkt].d[GCS_STSL] = tq_pkt[tpkt].d[RW_BCL]; tq_pkt[pkt].d[GCS_STSH] = tq_pkt[tpkt].d[RW_BCH]; } else tq_pkt[pkt].d[GCS_STSL] = tq_pkt[pkt].d[GCS_STSH] = 0; tq_putr (pkt, OP_GCS | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Get unit status */ t_bool tq_gus (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ if (lu >= TQ_NUMDR) { /* end of range? */ lu = 0; /* reset to 0 */ tq_pkt[pkt].d[RSP_UN] = lu; } } if (uptr = tq_getucb (lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else if (uptr->flags & UNIT_ONL) /* online */ sts = ST_SUC; else sts = ST_AVL; /* avail */ tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ tq_pkt[pkt].d[GUS_MENU] = drv_tab[tq_typ].fmt; /* format menu */ tq_pkt[pkt].d[GUS_CAP] = 0; /* free capacity */ tq_pkt[pkt].d[GUS_FVER] = drv_tab[tq_typ].fver; /* formatter version */ tq_pkt[pkt].d[GUS_UVER] = drv_tab[tq_typ].uver; /* unit version */ } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_GUS | OP_END, tq_efl (uptr), sts, GUS_LNT_T, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Unit online - deferred if q'd commands */ t_bool tq_onl (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else if (uptr->flags & UNIT_ONL) /* already online? */ sts = ST_SUC | SB_SUC_ON; else { sts = ST_SUC; /* mark online */ sim_tape_rewind (uptr); /* rewind */ uptr->objp = 0; /* clear flags */ uptr->flags = (uptr->flags | UNIT_ONL) & ~(UNIT_TMK | UNIT_POL); /* onl, pos ok */ tq_setf_unit (pkt, uptr); /* hack flags */ } tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_ONL | OP_END, tq_efl (uptr), sts, ONL_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Set controller characteristics */ t_bool tq_scc (int32 pkt) { if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */ tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ); else { tq_cflgs = (tq_cflgs & CF_RPL) | /* hack ctrl flgs */ tq_pkt[pkt].d[SCC_CFL]; if (tq_htmo = tq_pkt[pkt].d[SCC_TMO]) /* set timeout */ tq_htmo = tq_htmo + 2; /* if nz, round up */ tq_pkt[pkt].d[SCC_CFL] = tq_cflgs; /* return flags */ tq_pkt[pkt].d[SCC_TMO] = TQ_DCTMO; /* ctrl timeout */ tq_pkt[pkt].d[SCC_VER] = drv_tab[tq_typ].cver; /* ctrl version */ tq_pkt[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ tq_pkt[pkt].d[SCC_CIDB] = 0; tq_pkt[pkt].d[SCC_CIDC] = 0; tq_pkt[pkt].d[SCC_CIDD] = (TQ_CLASS << SCC_CIDD_V_CLS) | (drv_tab[tq_typ].cmod << SCC_CIDD_V_MOD); PUTP32 (pkt, SCC_MBCL, TQ_MAXFR); /* max bc */ tq_putr (pkt, OP_SCC | OP_END, 0, ST_SUC, SCC_LNT, UQ_TYP_SEQ); } return tq_putpkt (pkt, TRUE); } /* Set unit characteristics - defer if q'd commands */ t_bool tq_suc (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else { sts = ST_SUC; /* avail or onl */ tq_setf_unit (pkt, uptr); /* hack flags */ } tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ } else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_SUC | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Flush - sequential nop - deferred if q'd cmds, bypassed if ser exc */ t_bool tq_flu (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) /* unit exist? */ sts = tq_mot_valid (uptr, OP_FLU); /* validate req */ else sts = ST_OFL; /* offline */ tq_putr (pkt, OP_FLU | OP_END, tq_efl (uptr), sts, FLU_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Erase, erase gap - deferred if q'd cmds, bypassed if ser exc */ t_bool tq_erase (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ sts = tq_mot_valid (uptr, cmd); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ sim_activate (uptr, tq_xtime); /* activate */ return OK; /* done */ } } else sts = ST_OFL; /* offline */ tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, ERS_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Write tape mark - deferred if q'd cmds, bypassed if ser exc */ t_bool tq_wtm (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts, objp = 0; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ sim_activate (uptr, tq_xtime); /* activate */ return OK; /* done */ } } else sts = ST_OFL; /* offline */ PUTP32 (pkt, WTM_POSL, objp); /* set obj pos */ tq_putr (pkt, OP_WTM | OP_END, tq_efl (uptr), sts, WTM_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Position - deferred if q'd cmds, bypassed if ser exc */ t_bool tq_pos (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 sts, objp = 0; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, OP_POS); /* validity checks */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ sim_activate (uptr, tq_xtime); /* activate */ return OK; /* done */ } } else sts = ST_OFL; /* offline */ PUTP32 (pkt, POS_RCL, 0); /* clear #skipped */ PUTP32 (pkt, POS_TMCL, 0); PUTP32 (pkt, POS_POSL, objp); /* set obj pos */ tq_putr (pkt, OP_POS | OP_END, tq_efl (uptr), sts, POS_LNT, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Data transfer commands - deferred if q'd commands, bypassed if ser exc */ t_bool tq_rw (int32 pkt) { uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */ uint32 sts, objp = 0; UNIT *uptr; if (uptr = tq_getucb (lu)) { /* unit exist? */ objp = uptr->objp; /* position op */ sts = tq_mot_valid (uptr, cmd); /* validity checks */ if (sts == ST_SUC) { /* ok? */ if ((bc == 0) || (bc > TQ_MAXFR)) { /* invalid? */ uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ sts = ST_CMD | I_BCNT; } else { uptr->cpkt = pkt; /* op in progress */ sim_activate (uptr, tq_xtime); /* activate */ return OK; /* done */ } } } else sts = ST_OFL; /* offline */ PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ PUTP32 (pkt, RW_POSL, objp); /* set obj pos */ PUTP32 (pkt, RW_RSZL, 0); /* clr rec size */ tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, RW_LNT_T, UQ_TYP_SEQ); return tq_putpkt (pkt, TRUE); } /* Validity checks */ int32 tq_mot_valid (UNIT *uptr, uint32 cmd) { if (uptr->flags & UNIT_SXC) /* ser exc pend? */ return ST_SXC; if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return (ST_OFL | SB_OFL_NV); /* offl no vol */ if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ return ST_AVL; /* only avail */ if (tq_cmf[cmd] & CMF_WR) { /* write op? */ if (uptr->uf & UF_WPS) { /* swre wlk? */ uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ return (ST_WPR | SB_WPR_SW); } if (TQ_WPH (uptr)) { /* hwre wlk? */ uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ return (ST_WPR | SB_WPR_HW); } } return ST_SUC; /* success! */ } /* Unit service for motion commands */ t_stat tq_svc (UNIT *uptr) { uint32 t, sts, sktmk, skrec; t_mtrlnt i, tbc, wbc; int32 pkt = uptr->cpkt; /* get packet */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */ t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */ uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */ uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */ if (pkt == 0) /* what??? */ return SCPE_IERR; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ tq_mot_end (uptr, 0, ST_OFL | SB_OFL_NV, 0); /* offl no vol */ return SCPE_OK; } if (tq_cmf[cmd] & CMF_WR) { /* write op? */ if (TQ_WPH (uptr)) { /* hwre write prot? */ uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ tq_mot_end (uptr, 0, ST_WPR | SB_WPR_HW, 0); return SCPE_OK; } if (uptr->uf & UF_WPS) { /* swre write prot? */ uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ tq_mot_end (uptr, 0, ST_WPR | SB_WPR_SW, 0); return SCPE_OK; } } sts = ST_SUC; /* assume success */ tbc = 0; /* assume zero rec */ switch (cmd) { /* case on command */ case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */ if (mdf & MD_REV) /* read record */ sts = tq_rdbufr (uptr, &tbc); else sts = tq_rdbuff (uptr, &tbc); if (sts == ST_DRV) { /* read error? */ PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ return tq_mot_err (uptr, tbc); /* log, done */ } if ((sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ break; } if (tbc > bc) { /* tape rec > buf? */ uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ sts = ST_RDT; /* data truncated */ wbc = bc; /* set working bc */ } else wbc = tbc; if (cmd == OP_RD) { /* read? */ if (t = Map_WriteB (ba, wbc, tqxb)) { /* store, nxm? */ PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */ if (tq_hbe (uptr, ba + wbc - t)) /* post err log */ tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); return SCPE_OK; /* end if nxm */ } } /* end if read */ else { /* compare */ uint8 mby, dby; uint32 mba; for (i = 0; i < wbc; i++) { /* loop */ if (mdf & MD_REV) { /* reverse? */ mba = ba + bc - 1 - i; /* mem addr */ dby = tqxb[tbc - 1 - i]; /* byte */ } else { mba = ba + i; dby = tqxb[i]; } if (Map_ReadB (mba, 1, &mby)) { /* fetch, nxm? */ PUTP32 (pkt, RW_BCL, i); /* adj bc */ if (tq_hbe (uptr, mba)) /* post err log */ tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); return SCPE_OK; } if (mby != dby) { /* cmp err? */ uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */ PUTP32 (pkt, RW_BCL, i); /* adj bc */ tq_mot_end (uptr, 0, ST_CMP, tbc); return SCPE_OK; /* exit */ } } /* end for */ } /* end if compare */ PUTP32 (pkt, RW_BCL, wbc); /* bytes read/cmp'd */ break; case OP_WR: /* write */ if (t = Map_ReadB (ba, bc, tqxb)) { /* fetch buf, nxm? */ PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */ if (tq_hbe (uptr, ba + bc - t)) /* post err log */ tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc); return SCPE_OK; /* end else wr */ } if (sim_tape_wrrecf (uptr, tqxb, bc)) /* write rec fwd, err? */ return tq_mot_err (uptr, bc); /* log, end */ uptr->objp = uptr->objp + 1; /* upd obj pos */ if (TEST_EOT (uptr)) /* EOT on write? */ uptr->flags = uptr->flags | UNIT_SXC; uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ tbc = bc; /* RW_BC is ok */ break; case OP_WTM: /* write tape mark */ if (sim_tape_wrtmk (uptr)) /* write tmk, err? */ return tq_mot_err (uptr, 0); /* log, end */ uptr->objp = uptr->objp + 1; /* incr obj cnt */ case OP_ERG: /* erase gap */ if (TEST_EOT (uptr)) /* EOT on write? */ uptr->flags = uptr->flags | UNIT_SXC; uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ break; case OP_ERS: /* erase */ if (sim_tape_wreom (uptr)) /* write eom, err? */ return tq_mot_err (uptr, 0); /* log, end */ sim_tape_rewind (uptr); /* rewind */ uptr->objp = 0; uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); break; case OP_POS: /* position */ sktmk = skrec = 0; /* clr skipped */ if (mdf & MD_RWD) { /* rewind? */ sim_tape_rewind (uptr); uptr->objp = 0; /* clr flags */ uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); } if (mdf & MD_OBC) { /* skip obj? */ if (mdf & MD_REV) /* reverse? */ sts = tq_spacer (uptr, nrec, &skrec, FALSE); else sts = tq_spacef (uptr, nrec, &skrec, FALSE); } else { /* skip tmk, rec */ if (mdf & MD_REV) sts = tq_skipfr (uptr, ntmk, &sktmk); else sts = tq_skipff (uptr, ntmk, &sktmk); if (sts == ST_SUC) { /* tmk succeed? */ if (mdf & MD_REV) /* reverse? */ sts = tq_spacer (uptr, nrec, &skrec, TRUE); else sts = tq_spacef (uptr, nrec, &skrec, TRUE); if (sts == ST_TMK) sktmk = sktmk + 1; } } PUTP32 (pkt, POS_RCL, skrec); /* #rec skipped */ PUTP32 (pkt, POS_TMCL, sktmk); /* #tmk skipped */ break; default: return SCPE_IERR; } tq_mot_end (uptr, 0, sts, tbc); /* done */ return SCPE_OK; } /* Motion command drive error */ t_stat tq_mot_err (UNIT *uptr, uint32 rsiz) { uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_TMK; /* serious exception */ if (tq_dte (uptr, ST_DRV)) /* post err log */ tq_mot_end (uptr, EF_LOG, ST_DRV, rsiz); /* if ok, report err */ perror ("TQ I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } /* Motion command complete */ t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz) { int32 pkt = uptr->cpkt; /* packet */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 lnt = RW_LNT_T; /* assume rw */ if (cmd == OP_ERG) /* set pkt lnt */ lnt = ERG_LNT; else if (cmd == OP_ERS) lnt = ERS_LNT; else if (cmd == OP_WTM) lnt = WTM_LNT; else if (cmd == OP_POS) lnt = POS_LNT; uptr->cpkt = 0; /* done */ if (lnt > ERG_LNT) { /* xfer cmd? */ PUTP32 (pkt, RW_POSL, uptr->objp); /* position */ PUTP32 (pkt, RW_RSZL, rsiz); /* record size */ } tq_putr (pkt, cmd | OP_END, flg | tq_efl (uptr), sts, lnt, UQ_TYP_SEQ); if (!tq_putpkt (pkt, TRUE)) /* send pkt */ return ERR; if (uptr->pktq) /* more to do? */ sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate thread */ return OK; } /* Tape motion routines */ uint32 tq_map_status (UNIT *uptr, t_stat st) { switch (st) { case MTSE_OK: break; case MTSE_UNATT: uptr->flags = uptr->flags | UNIT_SXC; return (ST_OFL | SB_OFL_NV); case MTSE_FMT: uptr->flags = uptr->flags | UNIT_SXC; return ST_MFE; case MTSE_TMK: uptr->flags = uptr->flags | UNIT_SXC; return ST_TMK; case MTSE_INVRL: uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; return ST_FMT; case MTSE_RECE: case MTSE_IOERR: uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; return ST_DRV; case MTSE_EOM: uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; return ST_DAT; case MTSE_BOT: uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_POL; return ST_BOT; case MTSE_WRP: uptr->flags = uptr->flags | UNIT_SXC; return ST_WPR; } return ST_SUC; } uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) { t_stat st; t_mtrlnt tbc; *skipped = 0; while (*skipped < cnt) { /* loop */ st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ return tq_map_status (uptr, st); /* map status */ uptr->objp = uptr->objp + 1; /* upd obj cnt */ if (st == MTSE_TMK) { /* tape mark? */ int32 pkt = uptr->cpkt; /* get pkt */ if ((tq_pkt[pkt].d[CMD_MOD] & MD_DLE) && /* LEOT? */ (uptr->flags & UNIT_TMK)) { sim_tape_sprecr (uptr, &tbc); /* rev over tmk */ uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ return ST_LED; } uptr->flags = uptr->flags | UNIT_TMK; /* set TM seen */ if (qrec) /* rec spc? stop */ return ST_TMK; } else uptr->flags = uptr->flags & ~UNIT_TMK; /* clr TM seen */ *skipped = *skipped + 1; /* # obj skipped */ } return ST_SUC; } uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped) { uint32 st, skrec; *skipped = 0; while (*skipped < cnt) { /* loop */ st = tq_spacef (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc fwd */ if (st == ST_TMK) /* count files */ *skipped = *skipped + 1; else if (st != ST_SUC) return st; } return ST_SUC; } uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) { t_stat st; t_mtrlnt tbc; *skipped = 0; while (*skipped < cnt) { /* loop */ st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ return tq_map_status (uptr, st); /* map status */ uptr->objp = uptr->objp - 1; /* upd obj cnt */ if ((st == MTSE_TMK) && qrec) /* tape mark, stop? */ return ST_TMK; *skipped = *skipped + 1; /* # obj skipped */ } return ST_SUC; } uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped) { uint32 st, skrec; *skipped = 0; while (*skipped < cnt) { /* loopo */ st = tq_spacer (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc rev */ if (st == ST_TMK) /* tape mark? */ *skipped = *skipped + 1; else if (st != 0) /* error? */ return st; } return ST_SUC; } /* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */ uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc) { t_stat st; st = sim_tape_rdrecf (uptr, tqxb, tbc, MT_MAXFR); /* read rec fwd */ if (st == MTSE_TMK) { /* tape mark? */ uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */ uptr->objp = uptr->objp + 1; /* update obj cnt */ return ST_TMK; } if (st != MTSE_OK) /* other error? */ return tq_map_status (uptr, st); uptr->flags = uptr->flags & ~UNIT_TMK; /* clr tape mark */ uptr->objp = uptr->objp + 1; /* upd obj cnt */ return ST_SUC; } uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc) { t_stat st; st = sim_tape_rdrecr (uptr, tqxb, tbc, MT_MAXFR); /* read rec rev */ if (st == MTSE_TMK) { /* tape mark? */ uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ uptr->objp = uptr->objp - 1; /* update obj cnt */ return ST_TMK; } if (st != MTSE_OK) /* other error? */ return tq_map_status (uptr, st); uptr->objp = uptr->objp - 1; /* upd obj cnt */ return ST_SUC; } /* Data transfer error log packet */ t_bool tq_dte (UNIT *uptr, uint32 err) { int32 pkt, tpkt; uint32 lu; if ((tq_cflgs & CF_THS) == 0) /* logging? */ return OK; if (!tq_deqf (&pkt)) /* get log pkt */ return ERR; tpkt = uptr->cpkt; /* rw pkt */ lu = tq_pkt[tpkt].d[CMD_UN]; /* unit # */ tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ tq_pkt[pkt].d[ELP_UN] = lu; /* copy unit */ tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ tq_pkt[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ tq_pkt[pkt].d[DTE_CIDB] = 0; tq_pkt[pkt].d[DTE_CIDC] = 0; tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); tq_pkt[pkt].d[DTE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ tq_pkt[pkt].d[DTE_MLUN] = lu; /* MLUN */ tq_pkt[pkt].d[DTE_UIDA] = lu; /* unit ID */ tq_pkt[pkt].d[DTE_UIDB] = 0; tq_pkt[pkt].d[DTE_UIDC] = 0; tq_pkt[pkt].d[DTE_UIDD] = (UID_TAPE << DTE_UIDD_V_CLS) | (drv_tab[tq_typ].umod << DTE_UIDD_V_MOD); tq_pkt[pkt].d[DTE_UVER] = drv_tab[tq_typ].uver; /* unit ver */ PUTP32 (pkt, DTE_POSL, uptr->objp); /* position */ tq_pkt[pkt].d[DTE_FVER] = drv_tab[tq_typ].fver; /* fmtr ver */ tq_putr (pkt, FM_TAP, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); return tq_putpkt (pkt, TRUE); } /* Host bus error log packet */ t_bool tq_hbe (UNIT *uptr, uint32 ba) { int32 pkt, tpkt; if ((tq_cflgs & CF_THS) == 0) /* logging? */ return OK; if (!tq_deqf (&pkt)) /* get log pkt */ return ERR; tpkt = uptr->cpkt; /* rw pkt */ tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ tq_pkt[pkt].d[ELP_UN] = tq_pkt[tpkt].d[CMD_UN]; /* copy unit */ tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ tq_pkt[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ tq_pkt[pkt].d[HBE_CIDB] = 0; tq_pkt[pkt].d[HBE_CIDC] = 0; tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); tq_pkt[pkt].d[HBE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ tq_pkt[pkt].d[HBE_RSV] = 0; PUTP32 (pkt, HBE_BADL, ba); /* bad addr */ tq_putr (pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); return tq_putpkt (pkt, TRUE); } /* Port last failure error log packet */ t_bool tq_plf (uint32 err) { int32 pkt; if (!tq_deqf (&pkt)) /* get log pkt */ return ERR; tq_pkt[pkt].d[ELP_REFL] = tq_pkt[pkt].d[ELP_REFH] = 0; /* ref = 0 */ tq_pkt[pkt].d[ELP_UN] = tq_pkt[pkt].d[ELP_SEQ] = 0; /* no unit, seq */ tq_pkt[pkt].d[PLF_CIDA] = 0; /* cntl ID */ tq_pkt[pkt].d[PLF_CIDB] = 0; tq_pkt[pkt].d[PLF_CIDC] = 0; tq_pkt[pkt].d[PLF_CIDD] = (TQ_CLASS << PLF_CIDD_V_CLS) | (drv_tab[tq_typ].cmod << PLF_CIDD_V_MOD); tq_pkt[pkt].d[PLF_VER] = drv_tab[tq_typ].cver; tq_pkt[pkt].d[PLF_ERR] = err; tq_putr (pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); tq_pkt[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); return tq_putpkt (pkt, TRUE); } /* Unit now available attention packet */ int32 tq_una (UNIT *uptr) { int32 pkt; uint32 lu; if (!tq_deqf (&pkt)) /* get log pkt */ return ERR; lu = (uint32) (uptr - tq_dev.units); /* get unit */ tq_pkt[pkt].d[RSP_REFL] = tq_pkt[pkt].d[RSP_REFH] = 0; /* ref = 0 */ tq_pkt[pkt].d[RSP_UN] = lu; tq_pkt[pkt].d[RSP_RSV] = 0; tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ tq_putr (pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ return tq_putpkt (pkt, TRUE); } /* List handling tq_deqf - dequeue head of free list (fatal err if none) tq_deqh - dequeue head of list tq_enqh - enqueue at head of list tq_enqt - enqueue at tail of list */ t_bool tq_deqf (int32 *pkt) { if (tq_freq == 0) /* no free pkts?? */ return tq_fatal (PE_NSR); tq_pbsy = tq_pbsy + 1; /* cnt busy pkts */ *pkt = tq_freq; /* head of list */ tq_freq = tq_pkt[tq_freq].link; /* next */ return OK; } int32 tq_deqh (int32 *lh) { int32 ptr = *lh; /* head of list */ if (ptr) /* next */ *lh = tq_pkt[ptr].link; return ptr; } void tq_enqh (int32 *lh, int32 pkt) { if (pkt == 0) /* any pkt? */ return; tq_pkt[pkt].link = *lh; /* link is old lh */ *lh = pkt; /* pkt is new lh */ return; } void tq_enqt (int32 *lh, int32 pkt) { if (pkt == 0) /* any pkt? */ return; tq_pkt[pkt].link = 0; /* it will be tail */ if (*lh == 0) /* if empty, enqh */ *lh = pkt; else { uint32 ptr = *lh; /* chase to end */ while (tq_pkt[ptr].link) ptr = tq_pkt[ptr].link; tq_pkt[ptr].link = pkt; /* enq at tail */ } return; } /* Packet and descriptor handling */ /* Get packet from command ring */ t_bool tq_getpkt (int32 *pkt) { uint32 addr, desc; if (!tq_getdesc (&tq_cq, &desc)) /* get cmd desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* none */ *pkt = 0; /* pkt = 0 */ return OK; /* no error */ } if (!tq_deqf (pkt)) /* get cmd pkt */ return ERR; tq_hat = 0; /* dsbl hst timer */ addr = desc & UQ_ADDR; /* get Q22 addr */ if (Map_ReadW (addr + UQ_HDR_OFF, TQ_PKT_SIZE, tq_pkt[*pkt].d)) return tq_fatal (PE_PRE); /* read pkt */ return tq_putdesc (&tq_cq, desc); /* release desc */ } /* Put packet to response ring - note the clever hack about credits. The controller sends all its credits to the host. Thereafter, it supplies one credit for every response packet sent over. Simple! */ t_bool tq_putpkt (int32 pkt, t_bool qt) { uint32 addr, desc, lnt, cr; if (pkt == 0) /* any packet? */ return OK; if (DEBUG_PRS (tq_dev)) { UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); fprintf (sim_deb, ">>TQ: rsp=%04X, sts=%04X", tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); if (up) fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); else fprintf (sim_deb, "\n"); fflush (sim_deb); } if (!tq_getdesc (&tq_rq, &desc)) /* get rsp desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ if (qt) /* normal? q tail */ tq_enqt (&tq_rspq, pkt); else tq_enqh (&tq_rspq, pkt); /* resp q call */ sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate q thrd */ return OK; } addr = desc & UQ_ADDR; /* get Q22 addr */ lnt = tq_pkt[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ (GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */ cr = (tq_credits >= 14)? 14: tq_credits; /* max 14 credits */ tq_credits = tq_credits - cr; /* decr credits */ tq_pkt[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); } if (Map_WriteW (addr + UQ_HDR_OFF, lnt, tq_pkt[pkt].d)) return tq_fatal (PE_PWE); /* write pkt */ tq_enqh (&tq_freq, pkt); /* pkt is free */ tq_pbsy = tq_pbsy - 1; /* decr busy cnt */ if (tq_pbsy == 0) /* idle? strt hst tmr */ tq_hat = tq_htmo; return tq_putdesc (&tq_rq, desc); /* release desc */ } /* Get a descriptor from the host */ t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc) { uint32 addr = ring->ba + ring->idx; uint16 d[2]; if (Map_ReadW (addr, 4, d)) /* fetch desc */ return tq_fatal (PE_QRE); /* err? dead */ *desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); return OK; } /* Return a descriptor to the host, clearing owner bit If rings transitions from "empty" to "not empty" or "full" to "not full", and interrupt bit was set, interrupt the host. Actually, test whether previous ring entry was owned by host. */ t_bool tq_putdesc (struct uq_ring *ring, uint32 desc) { uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; uint32 prva, addr = ring->ba + ring->idx; uint16 d[2]; d[0] = newd & 0xFFFF; /* 32b to 16b */ d[1] = (newd >> 16) & 0xFFFF; if (Map_WriteW (addr, 4, d)) /* store desc */ return tq_fatal (PE_QWE); /* err? dead */ if (desc & UQ_DESC_F) { /* was F set? */ if (ring->lnt <= 4) /* lnt = 1? intr */ tq_ring_int (ring); else { prva = ring->ba + /* prv desc */ ((ring->idx - 4) & (ring->lnt - 1)); if (Map_ReadW (prva, 4, d)) /* read prv */ return tq_fatal (PE_QRE); prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); if (prvd & UQ_DESC_OWN) tq_ring_int (ring); } } ring->idx = (ring->idx + 4) & (ring->lnt - 1); return OK; } /* Get unit descriptor for logical unit - trivial now, but eventually, hide multiboard complexities here */ UNIT *tq_getucb (uint32 lu) { UNIT *uptr; if (lu >= TQ_NUMDR) return NULL; uptr = tq_dev.units + lu; if (uptr->flags & UNIT_DIS) return NULL; return uptr; } /* Hack unit flags */ void tq_setf_unit (int32 pkt, UNIT *uptr) { uptr->uf = tq_pkt[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ if ((tq_pkt[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ (tq_pkt[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ uptr->uf = uptr->uf | UF_WPS; /* simon says... */ return; } /* Hack end flags */ uint32 tq_efl (UNIT *uptr) { uint32 t = 0; if (uptr) { /* any unit? */ if (uptr->flags & UNIT_POL) /* note pos lost */ t = t | EF_PLS; if (uptr->flags & UNIT_SXC) /* note ser exc */ t = t | EF_SXC; if (TEST_EOT (uptr)) /* note EOT */ t = t | EF_EOT; } return t; } /* Unit response fields */ void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all) { tq_pkt[pkt].d[ONL_MLUN] = lu; /* multi-unit */ tq_pkt[pkt].d[ONL_UFL] = uptr->uf | TQ_WPH (uptr); /* unit flags */ tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */ tq_pkt[pkt].d[ONL_UIDA] = lu; /* UID low */ tq_pkt[pkt].d[ONL_UIDB] = 0; tq_pkt[pkt].d[ONL_UIDC] = 0; tq_pkt[pkt].d[ONL_UIDD] = (UID_TAPE << ONL_UIDD_V_CLS) | (drv_tab[tq_typ].umod << ONL_UIDD_V_MOD); /* UID hi */ PUTP32 (pkt, ONL_MEDL, drv_tab[tq_typ].med); /* media type */ if (all) { /* if long form */ tq_pkt[pkt].d[ONL_FMT] = drv_tab[tq_typ].fmt; /* format */ tq_pkt[pkt].d[ONL_SPD] = 0; /* speed */ PUTP32 (pkt, ONL_MAXL, TQ_MAXFR); /* max xfr */ tq_pkt[pkt].d[ONL_NREC] = 0; /* noise rec */ tq_pkt[pkt].d[ONL_RSVE] = 0; /* reserved */ } return; } /* UQ_HDR and RSP_OP fields */ void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ) { tq_pkt[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ (flg << RSP_OPF_V_FLG); tq_pkt[pkt].d[RSP_STS] = sts; tq_pkt[pkt].d[UQ_HLNT] = lnt; /* length */ tq_pkt[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ (UQ_CID_TMSCP << UQ_HCTC_V_CID); /* clr credits */ return; } /* Post interrupt during init */ void tq_init_int (void) { if ((tq_s1dat & SA_S1H_IE) && tq_dib.vec) SET_INT (TQ); return; } /* Post interrupt during putpkt - note that NXMs are ignored! */ void tq_ring_int (struct uq_ring *ring) { uint32 iadr = tq_comm + ring->ioff; /* addr intr wd */ uint16 flag = 1; Map_WriteW (iadr, 2, &flag); /* write flag */ if (tq_dib.vec) /* if enb, intr */ SET_INT (TQ); return; } /* Return interrupt vector */ int32 tq_inta (void) { return tq_dib.vec; /* prog vector */ } /* Fatal error */ t_bool tq_fatal (uint32 err) { if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, ">>TQ: fatal err=%X\n", err); tq_reset (&tq_dev); /* reset device */ tq_sa = SA_ER | err; /* SA = dead code */ tq_csta = CST_DEAD; /* state = dead */ tq_perr = err; /* save error */ return ERR; } /* Device attach */ t_stat tq_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; if (tq_csta == CST_UP) uptr->flags = (uptr->flags | UNIT_ATP) & ~(UNIT_SXC | UNIT_POL | UNIT_TMK); return SCPE_OK; } /* Device detach */ t_stat tq_detach (UNIT *uptr) { t_stat r; r = sim_tape_detach (uptr); /* detach unit */ if (r != SCPE_OK) return r; uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP | UNIT_SXC | UNIT_POL | UNIT_TMK); uptr->uf = 0; /* clr unit flgs */ return SCPE_OK; } /* Device reset */ t_stat tq_reset (DEVICE *dptr) { int32 i, j; UNIT *uptr; tq_csta = CST_S1; /* init stage 1 */ tq_s1dat = 0; /* no S1 data */ tq_dib.vec = 0; /* no vector */ if (UNIBUS) /* Unibus? */ tq_sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; else tq_sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ tq_cflgs = CF_RPL; /* ctrl flgs off */ tq_htmo = TQ_DHTMO; /* default timeout */ tq_hat = tq_htmo; /* default timer */ tq_cq.ba = tq_cq.lnt = tq_cq.idx = 0; /* clr cmd ring */ tq_rq.ba = tq_rq.lnt = tq_rq.idx = 0; /* clr rsp ring */ tq_credits = (TQ_NPKTS / 2) - 1; /* init credits */ tq_freq = 1; /* init free list */ for (i = 0; i < TQ_NPKTS; i++) { /* all pkts free */ if (i) tq_pkt[i].link = (i + 1) & TQ_M_NPKTS; else tq_pkt[i].link = 0; for (j = 0; j < TQ_PKT_SIZE_W; j++) tq_pkt[i].d[j] = 0; } tq_rspq = 0; /* no q'd rsp pkts */ tq_pbsy = 0; /* all pkts free */ tq_pip = 0; /* not polling */ CLR_INT (TQ); /* clr intr req */ for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */ uptr = tq_dev.units + i; sim_cancel (uptr); /* clr activity */ sim_tape_reset (uptr); uptr->flags = uptr->flags & /* not online */ ~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK); uptr->uf = 0; /* clr unit flags */ uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ } if (tqxb == NULL) tqxb = (uint8 *) calloc (TQ_MAXFR, sizeof (uint8)); if (tqxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Device bootstrap */ #if defined (VM_PDP11) #define BOOT_START 016000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 014) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) /* Data structure definitions */ #define B_CMDINT (BOOT_START - 01000) /* cmd int */ #define B_RSPINT (B_CMDINT + 002) /* rsp int */ #define B_RING (B_RSPINT + 002) /* ring base */ #define B_RSPH (B_RING + 010) /* resp pkt hdr */ #define B_TKRSP (B_RSPH + 004) /* resp pkt */ #define B_CMDH (B_TKRSP + 060) /* cmd pkt hdr */ #define B_TKCMD (B_CMDH + 004) /* cmd pkt */ #define B_UNIT (B_TKCMD + 004) /* unit # */ static const uint16 boot_rom[] = { 0046525, /* ST: "UM" */ 0012706, 0016000, /* mov #st,sp */ 0012700, 0000000, /* mov #unitno,r0 */ 0012701, 0174500, /* mov #174500,r1 ; ip addr */ 0005021, /* clr (r1)+ ; init */ 0012704, 0004000, /* mov #4000,r4 ; s1 mask */ 0005002, /* clr r2 */ 0005022, /* 10$: clr (r2)+ ; clr up to boot */ 0020237, BOOT_START - 2, /* cmp r2,#st-2 */ 0103774, /* blo 10$ */ 0012705, BOOT_START+0312, /* mov #cmdtbl,r5 ; addr of tbl */ /* Four step init process */ 0005711, /* 20$: tst (r1) ; err? */ 0100001, /* bpl 30$ */ 0000000, /* halt */ 0030411, /* 30$: bit r4,(r1) ; step set? */ 0001773, /* beq 20$ ; wait */ 0012511, /* mov (r5)+,(r1) ; send next */ 0006304, /* asl r4 ; next mask */ 0100370, /* bpl 20$ ; s4 done? */ /* Set up rings, issue ONLINE, REWIND, READ */ 0012737, 0000400, B_CMDH + 2, /* mov #400,cmdh+2 ; VCID = 1 */ 0012737, 0000044, B_CMDH, /* mov #36.,cmdh ; cmd pkt lnt */ 0010037, B_UNIT, /* mov r0,unit ; unit # */ 0012737, 0000011, B_TKCMD + 8, /* mov #11,tkcmd+8. ; online op */ 0012737, 0020000, B_TKCMD + 10, /* mov #20000,tkcmd+10. ; clr ser ex */ 0012702, B_RING, /* mov #ring,r2 ; init rings */ 0012722, B_TKRSP, /* mov #tkrsp,(r2)+ ; rsp pkt addr */ 0010203, /* mov r2,r3 ; save ring+2 */ 0010423, /* mov r4,(r3)+ ; set TK own */ 0012723, B_TKCMD, /* mov #tkcmd,(r3)+ ; cmd pkt addr */ 0010423, /* mov r4,(r3)+ ; set TK own */ 0005741, /* tst -(r1) ; start poll */ 0005712, /* 40$: tst (r2) ; wait for resp */ 0100776, /* bmi 40$ */ 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ 0001401, /* beq 50$ */ 0000000, /* halt */ 0012703, B_TKCMD + 8, /* 50$: mov #tkcmd+8.,r3 */ 0012723, 0000045, /* mov #45,(r3)+ ; reposition */ 0012723, 0020002, /* mov #20002,(r3)+ ; rew, clr exc */ 0012723, 0000001, /* mov #1,(r3)+ ; lo rec skp */ 0005023, /* clr (r3)+ ; hi rec skp */ 0005023, /* clr (r3)+ ; lo tmk skp */ 0005023, /* clr (r3)+ ; hi tmk skp */ 0010412, /* mov r4,(r2) ; TK own rsp */ 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ 0005711, /* tst (r1) ; start poll */ 0005712, /* 60$: tst (r2) ; wait for resp */ 0100776, /* bmi 60$ */ 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ 0001401, /* beq 70$ */ 0000000, /* halt */ 0012703, B_TKCMD + 8, /* 70$: mov #tkcmd+8.,r3 */ 0012723, 0000041, /* mov #41,(r3)+ ; read */ 0012723, 0020000, /* mov #20000,(r3)+ ; clr exc */ 0012723, 0001000, /* mov #512.,(r3)+ ; bc = 512 */ 0005023, /* clr (r3)+ ; clr args */ 0005023, /* clr (r3)+ ; ba = 0 */ 0010412, /* mov r4,(r2) ; TK own rsp */ 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ 0005711, /* tst (r1) ; start poll */ 0005712, /* 80$: tst (r2) ; wait for resp */ 0100776, /* bmi 80$ */ 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ 0001401, /* beq 90$ */ 0000000, /* halt */ /* Boot block read in, jump to 0 - leave controller init'd */ 0005003, /* clr r3 */ 0012704, BOOT_START+020, /* mov #st+020,r4 */ 0005005, /* clr r5 */ 0005007, /* clr pc */ 0100000, /* cmdtbl: init step 1 */ B_RING, /* ring base */ 0000000, /* high ring base */ 0000001 /* go */ }; t_stat tq_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & 3; M[BOOT_CSR >> 1] = tq_dib.ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat tq_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif /* Special show commands */ void tq_show_ring (FILE *st, struct uq_ring *rp) { uint32 i, desc; uint16 d[2]; #if defined (VM_PDP11) fprintf (st, "ring, base = %o, index = %d, length = %d\n", rp->ba, rp->idx >> 2, rp->lnt >> 2); #else fprintf (st, "ring, base = %x, index = %d, length = %d\n", rp->ba, rp->idx >> 2, rp->lnt >> 2); #endif for (i = 0; i < (rp->lnt >> 2); i++) { if (Map_ReadW (rp->ba + (i << 2), 4, d)) { fprintf (st, " %3d: non-existent memory\n", i); break; } desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); #if defined (VM_PDP11) fprintf (st, " %3d: %011o\n", i, desc); #else fprintf (st, " %3d: %08x\n", i, desc); #endif } return; } void tq_show_pkt (FILE *st, int32 pkt) { int32 i, j; uint32 cr = GETP (pkt, UQ_HCTC, CR); uint32 typ = GETP (pkt, UQ_HCTC, TYP); uint32 cid = GETP (pkt, UQ_HCTC, CID); fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", pkt, cr, typ, cid); for (i = 0; i < TQ_SH_MAX; i = i + TQ_SH_PPL) { fprintf (st, " %2d:", i); for (j = i; j < (i + TQ_SH_PPL); j++) #if defined (VM_PDP11) fprintf (st, " %06o", tq_pkt[pkt].d[j]); #else fprintf (st, " %04x", tq_pkt[pkt].d[j]); #endif fprintf (st, "\n"); } return; } t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 pkt, u = uptr - tq_dev.units; if (tq_csta != CST_UP) { fprintf (st, "Controller is not initialized\n"); return SCPE_OK; } if ((uptr->flags & UNIT_ONL) == 0) { if (uptr->flags & UNIT_ATT) fprintf (st, "Unit %d is available\n", u); else fprintf (st, "Unit %d is offline\n", u); return SCPE_OK; } if (uptr->cpkt) { fprintf (st, "Unit %d current ", u); tq_show_pkt (st, uptr->cpkt); if (pkt = uptr->pktq) { do { fprintf (st, "Unit %d queued ", u); tq_show_pkt (st, pkt); } while (pkt = tq_pkt[pkt].link); } } else fprintf (st, "Unit %d queues are empty\n", u); return SCPE_OK; } t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 i, pkt; if (tq_csta != CST_UP) { fprintf (st, "Controller is not initialized\n"); return SCPE_OK; } if (val & TQ_SH_RI) { if (tq_pip) fprintf (st, "Polling in progress, host timer = %d\n", tq_hat); else fprintf (st, "Host timer = %d\n", tq_hat); fprintf (st, "Command "); tq_show_ring (st, &tq_cq); fprintf (st, "Response "); tq_show_ring (st, &tq_rq); } if (val & TQ_SH_FR) { if (pkt = tq_freq) { for (i = 0; pkt != 0; i++, pkt = tq_pkt[pkt].link) { if (i == 0) fprintf (st, "Free queue = %d", pkt); else if ((i % 16) == 0) fprintf (st, ",\n %d", pkt); else fprintf (st, ", %d", pkt); } fprintf (st, "\n"); } else fprintf (st, "Free queue is empty\n"); } if (val & TQ_SH_RS) { if (pkt = tq_rspq) { do { fprintf (st, "Response "); tq_show_pkt (st, pkt); } while (pkt = tq_pkt[pkt].link); } else fprintf (st, "Response queue is empty\n"); } if (val & TQ_SH_UN) { for (i = 0; i < TQ_NUMDR; i++) tq_show_unitq (st, &tq_unit[i], 0, NULL); } return SCPE_OK; } /* Set controller type (and capacity for user-defined type) */ t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i, cap; uint32 max = sim_taddr_64? TQU_EMAXC: TQU_MAXC; t_stat r; if ((val < 0) || (val > TQU_TYPE) || ((val != TQU_TYPE) && cptr)) return SCPE_ARG; for (i = 0; i < TQ_NUMDR; i++) { if (tq_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } if (cptr) { cap = (uint32) get_uint (cptr, 10, max, &r); if ((r != SCPE_OK) || (cap < TQU_MINC)) return SCPE_ARG; drv_tab[TQU_TYPE].cap = ((t_addr) cap) << 20; } tq_typ = val; for (i = 0; i < TQ_NUMDR; i++) tq_unit[i].capac = drv_tab[tq_typ].cap; return SCPE_OK; } /* Show controller type and capacity */ t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "%s (%dMB)", drv_tab[tq_typ].name, (uint32) (drv_tab[tq_typ].cap >> 20)); return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rf.c0000644000175000017500000004533311110161454013210 0ustar vlmvlm/* pdp11_rf.c: RF11 fixed head disk simulator Copyright (c) 2006-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rf RF11 fixed head disk 25-Dec-06 RMS Fixed bug in unit mask (found by John Dundas) 26-Jun-06 RMS Cloned from RF08 simulator The RF11 is a head-per-track disk. To minimize overhead, the entire RF11 is buffered in memory. Two timing parameters are provided: rf_time Interword timing, must be non-zero rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, DMA occurs in a burst */ #include "pdp11_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT (RF_NUMDK - 1) #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define RF_NUMWD 2048 /* words/track */ #define RF_NUMTR 128 /* tracks/disk */ #define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ #define RF_NUMDK 8 /* disks/controller */ #define RF_WMASK (RF_NUMWD - 1) /* word mask */ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ /* Status register */ #define RFCS_ERR (CSR_ERR) /* error */ #define RFCS_FRZ 0040000 /* error freeze */ #define RFCS_WCHK 0020000 /* write check */ #define RFCS_DPAR 0010000 /* data parity (ni) */ #define RFCS_NED 0004000 /* nx disk */ #define RFCS_WLK 0002000 /* write lock */ #define RFCS_MXFR 0001000 /* missed xfer (ni) */ #define RFCS_CLR 0000400 /* clear */ #define RFCS_DONE (CSR_DONE) #define RFCS_IE (CSR_IE) #define RFCS_M_MEX 0000003 /* memory extension */ #define RFCS_V_MEX 4 #define RFCS_MEX (RFCS_M_MEX << RFCS_V_MEX) #define RFCS_MAINT 0000010 /* maint */ #define RFCS_M_FUNC 0000003 /* function */ #define RFNC_NOP 0 #define RFNC_WRITE 1 #define RFNC_READ 2 #define RFNC_WCHK 3 #define RFCS_V_FUNC 1 #define RFCS_FUNC (RFCS_M_FUNC << RFCS_V_FUNC) #define RFCS_GO 0000001 #define RFCS_ALLERR (RFCS_FRZ|RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR) #define RFCS_W (RFCS_IE|RFCS_MEX|RFCS_FUNC) /* Current memory address */ #define RFCMA_RW 0177776 /* Address extension */ #define RFDAE_ALLERR 0176000 #define RFDAE_NXM 0002000 #define RFDAE_INH 0000400 /* addr inhibit */ #define RFDAE_RLAT 0000200 /* req late */ #define RFDAE_DAE 0000077 /* extension */ #define RFDAE_R 0176677 #define RFDAE_W 0000677 #define GET_FUNC(x) (((x) >> RFCS_V_FUNC) & RFCS_M_FUNC) #define GET_MEX(x) (((x) & RFCS_MEX) << (16 - RFCS_V_MEX)) #define GET_DEX(x) (((x) & RFDAE_DAE) << 16) #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) RF_NUMWD))) extern uint16 *M; extern int32 int_req[IPL_HLVL]; extern FILE *sim_deb; uint32 rf_cs = 0; /* status register */ uint32 rf_cma = 0; uint32 rf_wc = 0; uint32 rf_da = 0; /* disk address */ uint32 rf_dae = 0; uint32 rf_dbr = 0; uint32 rf_maint = 0; uint32 rf_wlk = 0; /* write lock */ uint32 rf_time = 10; /* inter-word time */ uint32 rf_burst = 1; /* burst mode flag */ uint32 rf_stopioe = 1; /* stop on error */ DEVICE rf_dev; t_stat rf_rd (int32 *data, int32 PA, int32 access); t_stat rf_wr (int32 data, int32 PA, int32 access); int32 rf_inta (void); t_stat rf_svc (UNIT *uptr); t_stat rf_reset (DEVICE *dptr); t_stat rf_boot (int32 unitno, DEVICE *dptr); t_stat rf_attach (UNIT *uptr, char *cptr); t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); uint32 update_rfcs (uint32 newcs, uint32 newdae); /* RF11 data structures rf_dev RF device descriptor rf_unit RF unit descriptor rf_reg RF register list */ DIB rf_dib = { IOBA_RF, IOLN_RF, &rf_rd, &rf_wr, 1, IVCL (RF), VEC_RF, NULL }; UNIT rf_unit = { UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+ UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE) }; REG rf_reg[] = { { ORDATA (RFCS, rf_cs, 16) }, { ORDATA (RFWC, rf_wc, 16) }, { ORDATA (RFCMA, rf_cma, 16) }, { ORDATA (RFDA, rf_da, 16) }, { ORDATA (RFDAE, rf_dae, 16) }, { ORDATA (RFDBR, rf_dbr, 16) }, { ORDATA (RFMR, rf_maint, 16) }, { ORDATA (RFWLK, rf_wlk, 32) }, { FLDATA (INT, IREQ (RF), INT_V_RF) }, { FLDATA (ERR, rf_cs, CSR_V_ERR) }, { FLDATA (DONE, rf_cs, CSR_V_DONE) }, { FLDATA (IE, rf_cs, CSR_V_IE) }, { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT }, { FLDATA (BURST, rf_burst, 0) }, { FLDATA (STOP_IOE, rf_stopioe, 0) }, { ORDATA (DEVADDR, rf_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, rf_dib.vec, 16), REG_HRO }, { NULL } }; MTAB rf_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &rf_set_size }, { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &rf_set_size }, { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &rf_set_size }, { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &rf_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE rf_dev = { "RF", &rf_unit, rf_reg, rf_mod, 1, 8, 21, 1, 8, 16, NULL, NULL, &rf_reset, &rf_boot, &rf_attach, NULL, &rf_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG }; /* I/O dispatch routine, I/O addresses 17777460 - 17777476 */ t_stat rf_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RFCS */ *data = update_rfcs (0, 0); /* update RFCS */ break; case 1: /* RFWC */ *data = rf_wc; break; case 2: /* RFCMA */ *data = rf_cma & RFCMA_RW; break; case 3: /* RFDA */ *data = rf_da; break; case 4: /* RFDAE */ *data = rf_dae & RFDAE_R; break; case 5: /* RFDBR */ *data = rf_dbr; break; case 6: /* RFMR */ *data = rf_maint; break; case 7: /* RFADS */ *data = GET_POS (rf_time); break; } /* end switch */ return SCPE_OK; } t_stat rf_wr (int32 data, int32 PA, int32 access) { int32 t, fnc; switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RFCS */ if (access == WRITEB) data = (PA & 1)? (rf_cs & 0377) | (data << 8): (rf_cs & ~0377) | data; if (data & RFCS_CLR) /* clear? */ rf_reset (&rf_dev); if ((data & RFCS_IE) == 0) /* int disable? */ CLR_INT (RF); /* clr int request */ else if ((rf_cs & (RFCS_DONE + RFCS_IE)) == RFCS_DONE) SET_INT (RF); /* set int request */ rf_cs = (rf_cs & ~RFCS_W) | (data & RFCS_W); /* merge */ if ((rf_cs & RFCS_DONE) && (data & RFCS_GO) && /* new function? */ ((fnc = GET_FUNC (rf_cs)) != RFNC_NOP)) { rf_unit.FUNC = fnc; /* save function */ t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ if (t < 0) /* wrap around? */ t = t + RF_NUMWD; sim_activate (&rf_unit, t * rf_time); /* schedule op */ rf_cs &= ~(RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR|RFCS_DONE); CLR_INT (RF); if (DEBUG_PRS (rf_dev)) fprintf (sim_deb, ">>RF start: cs = %o, da = %o, ma = %o\n", update_rfcs (0, 0), GET_DEX (rf_dae) | rf_da, GET_MEX (rf_cs) | rf_cma); } break; case 1: /* RFWC */ if (access == WRITEB) data = (PA & 1)? (rf_wc & 0377) | (data << 8): (rf_wc & ~0377) | data; rf_wc = data; break; case 2: /* RFCMA */ if (access == WRITEB) data = (PA & 1)? (rf_cma & 0377) | (data << 8): (rf_cma & ~0377) | data; rf_cma = data & RFCMA_RW; break; case 3: /* RFDA */ if (access == WRITEB) data = (PA & 1)? (rf_da & 0377) | (data << 8): (rf_da & ~0377) | data; rf_da = data; break; case 4: /* RFDAE */ if (access == WRITEB) data = (PA & 1)? (rf_dae & 0377) | (data << 8): (rf_dae & ~0377) | data; rf_dae = (rf_dae & ~RFDAE_W) | (data & RFDAE_W); break; case 5: /* RFDBR */ rf_dbr = data; break; case 6: /* RFMR */ rf_maint = data; break; case 7: /* RFADS */ break; /* read only */ } /* end switch */ update_rfcs (0, 0); return SCPE_OK; } /* Unit service Note that for reads and writes, memory addresses wrap around in the current field. This code assumes the entire disk is buffered. */ t_stat rf_svc (UNIT *uptr) { uint32 ma, da, t; uint16 dat; uint16 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ update_rfcs (RFCS_NED|RFCS_DONE, 0); /* nx disk */ return IORETURN (rf_stopioe, SCPE_UNATT); } ma = GET_MEX (rf_cs) | rf_cma; /* 18b mem addr */ da = GET_DEX (rf_dae) | rf_da; /* 22b disk addr */ do { if (da >= rf_unit.capac) { /* disk overflow? */ update_rfcs (RFCS_NED, 0); break; } if (uptr->FUNC == RFNC_READ) { /* read? */ dat = fbuf[da]; /* get disk data */ rf_dbr = dat; if (Map_WriteW (ma, 2, &dat)) { /* store mem, nxm? */ update_rfcs (0, RFDAE_NXM); break; } } else if (uptr->FUNC == RFNC_WCHK) { /* write check? */ rf_dbr = fbuf[da]; /* get disk data */ if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ update_rfcs (0, RFDAE_NXM); break; } if (rf_dbr != dat) { /* miscompare? */ update_rfcs (RFCS_WCHK, 0); break; } } else { /* write */ t = (da >> 15) & 037; if ((rf_wlk >> t) & 1) { /* write locked? */ update_rfcs (RFCS_WLK, 0); break; } else { /* not locked */ if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ update_rfcs (0, RFDAE_NXM); break; } fbuf[da] = dat; /* write word */ rf_dbr = dat; if (da >= uptr->hwmark) uptr->hwmark = da + 1; } } da = (da + 1) & 017777777; /* incr disk addr */ if ((rf_dae & RFDAE_INH) == 0) /* inhibit clear? */ ma = (ma + 2) & UNIMASK; /* incr mem addr */ rf_wc = (rf_wc + 1) & DMASK; /* incr word count */ } while ((rf_wc != 0) && (rf_burst != 0)); /* brk if wc, no brst */ rf_da = da & DMASK; /* split da */ rf_dae = (rf_dae & ~RFDAE_DAE) | ((rf_da >> 16) && RFDAE_DAE); rf_cma = ma & DMASK; /* split ma */ rf_cs = (rf_cs & ~RFCS_MEX) | ((ma >> (16 - RFCS_V_MEX)) & RFCS_MEX); if ((rf_wc != 0) && ((rf_cs & RFCS_ERR) == 0)) /* more to do? */ sim_activate (&rf_unit, rf_time); /* sched next */ else { update_rfcs (RFCS_DONE, 0); if (DEBUG_PRS (rf_dev)) fprintf (sim_deb, ">>RF done: cs = %o, dae = %o, da = %o, ma = %o, wc = %o\n", rf_cs, rf_dae, rf_da, rf_cma, rf_wc); } return SCPE_OK; } /* Update CS register */ uint32 update_rfcs (uint32 newcs, uint32 newdae) { uint32 oldcs = rf_cs; uint32 da = GET_DEX (rf_dae) | rf_da; rf_dae |= newdae; /* update DAE */ rf_cs |= newcs; /* update CS */ if (da >= rf_unit.capac) /* update CS */ rf_cs |= RFCS_NED; else rf_cs &= ~RFCS_NED; if (rf_dae & RFDAE_ALLERR) /* update CS */ rf_cs |= RFCS_FRZ; else rf_cs &= ~RFCS_FRZ; if (rf_cs & RFCS_ALLERR) /* update CS */ rf_cs |= RFCS_ERR; else rf_cs &= ~RFCS_ERR; if ((rf_cs & RFCS_IE) && /* IE and */ (rf_cs & RFCS_DONE) &&!(oldcs & RFCS_DONE)) /* done 0->1? */ SET_INT (RF); return rf_cs; } /* Reset routine */ t_stat rf_reset (DEVICE *dptr) { rf_cs = RFCS_DONE; rf_da = rf_dae = 0; rf_dbr = 0; rf_cma = 0; rf_wc = 0; rf_maint = 0; CLR_INT (RF); sim_cancel (&rf_unit); return SCPE_OK; } /* Bootstrap routine */ /* Device bootstrap */ #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_CSR (BOOT_START + 032) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) static const uint16 boot_rom[] = { 0043113, /* "FD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012701, 0177472, /* MOV #RFDAE+2, R1 ; csr block */ 0005041, /* CLR -(R1) ; clear dae */ 0005041, /* CLR -(R1), ; clear da */ 0005041, /* CLR -(R1), ; clear cma */ 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */ 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0105711, /* TSTB (R1) */ 0100376, /* BPL .-2 */ 0105011, /* CLRB (R1) */ 0005007 /* CLR PC */ }; t_stat rf_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_CSR >> 1] = (rf_dib.ba & DMASK) + 012; saved_PC = BOOT_ENTRY; return SCPE_OK; } /* Attach routine */ t_stat rf_attach (UNIT *uptr, char *cptr) { uint32 sz, p; uint32 ds_bytes = RF_DKSIZE * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= RF_NUMDK) p = RF_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; return attach_unit (uptr, cptr); } /* Change disk size */ t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = UNIT_GETP (val) * RF_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rx.c0000644000175000017500000005726411112125462013241 0ustar vlmvlm/* pdp11_rx.c: RX11/RX01 floppy disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rx RX11/RX01 floppy disk 07-Jul-05 RMS Removed extraneous externs 12-Oct-02 RMS Added autoconfigure support 08-Oct-02 RMS Added variable address support to bootstrap Added vector change/display support Revised state machine based on RX211 New data structures Fixed reset of disabled device 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted FLG to array 07-Sep-01 RMS Revised device disable and interrupt mechanisms 17-Jul-01 RMS Fixed warning from VC++ 6.0 26-Apr-01 RMS Added device enable/disable support 13-Apr-01 RMS Revised for register arrays 15-Feb-01 RMS Corrected bootstrap string 14-Apr-99 RMS Changed t_addr to unsigned An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. Tracks are numbered 0-76, sectors 1-26. */ #include "pdp11_defs.h" #define RX_NUMTR 77 /* tracks/disk */ #define RX_M_TRACK 0377 #define RX_NUMSC 26 /* sectors/track */ #define RX_M_SECTOR 0177 #define RX_NUMBY 128 /* bytes/sector */ #define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ #define RX_NUMDR 2 /* drives/controller */ #define RX_M_NUMDR 01 #define UNIT_V_WLK (UNIT_V_UF) /* write locked */ #define UNIT_WLK (1u << UNIT_V_UF) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define IDLE 0 /* idle state */ #define RWDS 1 /* rw, sect next */ #define RWDT 2 /* rw, track next */ #define RWXFR 3 /* rw, transfer */ #define FILL 4 /* fill buffer */ #define EMPTY 5 /* empty buffer */ #define CMD_COMPLETE 6 /* set done next */ #define INIT_COMPLETE 7 /* init compl next */ #define RXCS_V_FUNC 1 /* function */ #define RXCS_M_FUNC 7 #define RXCS_FILL 0 /* fill buffer */ #define RXCS_EMPTY 1 /* empty buffer */ #define RXCS_WRITE 2 /* write sector */ #define RXCS_READ 3 /* read sector */ #define RXCS_RXES 5 /* read status */ #define RXCS_WRDEL 6 /* write del data */ #define RXCS_ECODE 7 /* read error code */ #define RXCS_V_DRV 4 /* drive select */ #define RXCS_V_DONE 5 /* done */ #define RXCS_V_IE 6 /* intr enable */ #define RXCS_V_TR 7 /* xfer request */ #define RXCS_V_INIT 14 /* init */ #define RXCS_V_ERR 15 /* error */ #define RXCS_FUNC (RXCS_M_FUNC << RXCS_V_FUNC) #define RXCS_DRV (1u << RXCS_V_DRV) #define RXCS_DONE (1u << RXCS_V_DONE) #define RXCS_IE (1u << RXCS_V_IE) #define RXCS_TR (1u << RXCS_V_TR) #define RXCS_INIT (1u << RXCS_V_INIT) #define RXCS_ERR (1u << RXCS_V_ERR) #define RXCS_ROUT (RXCS_ERR+RXCS_TR+RXCS_IE+RXCS_DONE) #define RXCS_IMP (RXCS_ROUT+RXCS_DRV+RXCS_FUNC) #define RXCS_RW (RXCS_IE) /* read/write */ #define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC) #define RXES_CRC 0001 /* CRC error */ #define RXES_PAR 0002 /* parity error */ #define RXES_ID 0004 /* init done */ #define RXES_WLK 0010 /* write protect */ #define RXES_DD 0100 /* deleted data */ #define RXES_DRDY 0200 /* drive ready */ #define TRACK u3 /* current track */ #define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY extern int32 int_req[IPL_HLVL]; int32 rx_csr = 0; /* control/status */ int32 rx_dbr = 0; /* data buffer */ int32 rx_esr = 0; /* error status */ int32 rx_ecode = 0; /* error code */ int32 rx_track = 0; /* desired track */ int32 rx_sector = 0; /* desired sector */ int32 rx_state = IDLE; /* controller state */ int32 rx_stopioe = 1; /* stop on error */ int32 rx_cwait = 100; /* command time */ int32 rx_swait = 10; /* seek, per track */ int32 rx_xwait = 1; /* tr set time */ uint8 rx_buf[RX_NUMBY] = { 0 }; /* sector buffer */ int32 rx_bptr = 0; /* buffer pointer */ int32 rx_enb = 1; /* device enable */ DEVICE rx_dev; t_stat rx_rd (int32 *data, int32 PA, int32 access); t_stat rx_wr (int32 data, int32 PA, int32 access); t_stat rx_svc (UNIT *uptr); t_stat rx_reset (DEVICE *dptr); t_stat rx_boot (int32 unitno, DEVICE *dptr); void rx_done (int esr_flags, int new_ecode); /* RX11 data structures rx_dev RX device descriptor rx_unit RX unit list rx_reg RX register list rx_mod RX modifier list */ DIB rx_dib = { IOBA_RX, IOLN_RX, &rx_rd, &rx_wr, 1, IVCL (RX), VEC_RX, { NULL } }; UNIT rx_unit[] = { { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) }, { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) } }; REG rx_reg[] = { { ORDATA (RXCS, rx_csr, 16) }, { ORDATA (RXDB, rx_dbr, 8) }, { ORDATA (RXES, rx_esr, 8) }, { ORDATA (RXERR, rx_ecode, 8) }, { ORDATA (RXTA, rx_track, 8) }, { ORDATA (RXSA, rx_sector, 8) }, { DRDATA (STAPTR, rx_state, 3), REG_RO }, { DRDATA (BUFPTR, rx_bptr, 7) }, { FLDATA (INT, IREQ (RX), INT_V_RX) }, { FLDATA (ERR, rx_csr, RXCS_V_ERR) }, { FLDATA (TR, rx_csr, RXCS_V_TR) }, { FLDATA (IE, rx_csr, RXCS_V_IE) }, { FLDATA (DONE, rx_csr, RXCS_V_DONE) }, { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, { DRDATA (STIME, rx_swait, 24), PV_LEFT }, { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, { FLDATA (STOP_IOE, rx_stopioe, 0) }, { BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) }, { ORDATA (DEVADDR, rx_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, rx_dib.vec, 16), REG_HRO }, { NULL } }; MTAB rx_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", &set_addr_flt, NULL, NULL }, { 0 } }; DEVICE rx_dev = { "RX", rx_unit, rx_reg, rx_mod, RX_NUMDR, 8, 20, 1, 8, 8, NULL, NULL, &rx_reset, &rx_boot, NULL, NULL, &rx_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS }; /* I/O dispatch routine, I/O addresses 17777170 - 17777172 17777170 floppy CSR 17777172 floppy data register */ t_stat rx_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 1) { /* decode PA<1> */ case 0: /* RXCS */ rx_csr = rx_csr & RXCS_IMP; /* clear junk */ *data = rx_csr & RXCS_ROUT; break; case 1: /* RXDB */ if ((rx_state == EMPTY) && (rx_csr & RXCS_TR)) {/* empty? */ sim_activate (&rx_unit[0], rx_xwait); rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */ } *data = rx_dbr; /* return data */ break; } /* end switch PA */ return SCPE_OK; } t_stat rx_wr (int32 data, int32 PA, int32 access) { int32 drv; switch ((PA >> 1) & 1) { /* decode PA<1> */ /* Writing RXCS, three cases: 1. Writing INIT, reset device 2. Idle and writing new function - clear error, done, transfer ready, int req - save int enable, function, drive - start new function 3. Otherwise, write IE and update interrupts */ case 0: /* RXCS */ rx_csr = rx_csr & RXCS_IMP; /* clear junk */ if (access == WRITEB) data = (PA & 1)? /* write byte? */ (rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data; if (data & RXCS_INIT) { /* initialize? */ rx_reset (&rx_dev); /* reset device */ return SCPE_OK; /* end if init */ } if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */ rx_csr = data & (RXCS_IE + RXCS_DRV + RXCS_FUNC); drv = ((rx_csr & RXCS_DRV)? 1: 0); /* reselect drive */ rx_bptr = 0; /* clear buf pointer */ switch (RXCS_GETFNC (data)) { /* case on func */ case RXCS_FILL: rx_state = FILL; /* state = fill */ rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ break; case RXCS_EMPTY: rx_state = EMPTY; /* state = empty */ sim_activate (&rx_unit[drv], rx_xwait); break; case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: rx_state = RWDS; /* state = get sector */ rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ rx_esr = rx_esr & RXES_ID; /* clear errors */ break; default: rx_state = CMD_COMPLETE; /* state = cmd compl */ sim_activate (&rx_unit[drv], rx_cwait); break; } /* end switch func */ return SCPE_OK; } /* end if GO */ if ((data & RXCS_IE) == 0) CLR_INT (RX); else if ((rx_csr & (RXCS_DONE + RXCS_IE)) == RXCS_DONE) SET_INT (RX); rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW); break; /* end case RXCS */ /* Accessing RXDB, two cases: 1. Write idle, write 2. Write not idle and TR set, state dependent */ case 1: /* RXDB */ if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0))) return SCPE_OK; /* if ~IDLE, need tr */ rx_dbr = data & 0377; /* save data */ if ((rx_state != IDLE) && (rx_state != EMPTY)) { drv = ((rx_csr & RXCS_DRV)? 1: 0); /* select drive */ sim_activate (&rx_unit[drv], rx_xwait); /* sched event */ rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */ } break; /* end case RXDB */ } /* end switch PA */ return SCPE_OK; } /* Unit service; the action to be taken depends on the transfer state: IDLE Should never get here RWDS Save sector, set TR, set RWDT RWDT Save track, set RWXFR RWXFR Read/write buffer FILL copy ir to rx_buf[rx_bptr], advance ptr if rx_bptr > max, finish command, else set tr EMPTY if rx_bptr > max, finish command, else copy rx_buf[rx_bptr] to ir, advance ptr, set tr CMD_COMPLETE copy requested data to ir, finish command INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command For RWDT and CMD_COMPLETE, the input argument is the selected drive; otherwise, it is drive 0. */ t_stat rx_svc (UNIT *uptr) { int32 i, func; uint32 da; int8 *fbuf = uptr->filebuf; func = RXCS_GETFNC (rx_csr); /* get function */ switch (rx_state) { /* case on state */ case IDLE: /* idle */ return SCPE_IERR; /* done */ case EMPTY: /* empty buffer */ if (rx_bptr >= RX_NUMBY) /* done all? */ rx_done (0, 0); else { rx_dbr = rx_buf[rx_bptr]; /* get next */ rx_bptr = rx_bptr + 1; rx_csr = rx_csr | RXCS_TR; /* set xfer */ } break; case FILL: /* fill buffer */ rx_buf[rx_bptr] = rx_dbr; /* write next */ rx_bptr = rx_bptr + 1; if (rx_bptr < RX_NUMBY) /* more? set xfer */ rx_csr = rx_csr | RXCS_TR; else rx_done (0, 0); /* else done */ break; case RWDS: /* wait for sector */ rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ rx_csr = rx_csr | RXCS_TR; /* set xfer */ rx_state = RWDT; /* advance state */ return SCPE_OK; case RWDT: /* wait for track */ rx_track = rx_dbr & RX_M_TRACK; /* save track */ rx_state = RWXFR; sim_activate (uptr, /* sched done */ rx_swait * abs (rx_track - uptr->TRACK)); return SCPE_OK; case RWXFR: if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ rx_done (0, 0110); /* done, error */ return IORETURN (rx_stopioe, SCPE_UNATT); } if (rx_track >= RX_NUMTR) { /* bad track? */ rx_done (0, 0040); /* done, error */ break; } uptr->TRACK = rx_track; /* now on track */ if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ rx_done (0, 0070); /* done, error */ break; } da = CALC_DA (rx_track, rx_sector); /* get disk address */ if (func == RXCS_WRDEL) /* del data? */ rx_esr = rx_esr | RXES_DD; if (func == RXCS_READ) { /* read? */ for (i = 0; i < RX_NUMBY; i++) rx_buf[i] = fbuf[da + i]; } else { if (uptr->flags & UNIT_WPRT) { /* write and locked? */ rx_done (RXES_WLK, 0100); /* done, error */ break; } for (i = 0; i < RX_NUMBY; i++) /* write */ fbuf[da + i] = rx_buf[i]; da = da + RX_NUMBY; if (da > uptr->hwmark) uptr->hwmark = da; } rx_done (0, 0); /* done */ break; case CMD_COMPLETE: /* command complete */ if (func == RXCS_ECODE) { /* read ecode? */ rx_dbr = rx_ecode; /* set dbr */ rx_done (0, -1); /* don't update */ } else rx_done (0, 0); break; case INIT_COMPLETE: /* init complete */ rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ rx_done (RXES_ID, 0010); /* init done, error */ break; } da = CALC_DA (1, 1); /* track 1, sector 1 */ for (i = 0; i < RX_NUMBY; i++) /* read sector */ rx_buf[i] = fbuf[da + i]; rx_done (RXES_ID, 0); /* set done */ if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; break; } /* end case state */ return SCPE_OK; } /* Command complete. Set done and put final value in interface register, request interrupt if needed, return to IDLE state. */ void rx_done (int32 esr_flags, int32 new_ecode) { int32 drv = (rx_csr & RXCS_DRV)? 1: 0; rx_state = IDLE; /* now idle */ rx_csr = rx_csr | RXCS_DONE; /* set done */ if (rx_csr & RXCS_IE) SET_INT (RX); /* if ie, intr */ rx_esr = (rx_esr | esr_flags) & ~RXES_DRDY; if (rx_unit[drv].flags & UNIT_ATT) rx_esr = rx_esr | RXES_DRDY; if (new_ecode > 0) /* test for error */ rx_csr = rx_csr | RXCS_ERR; if (new_ecode < 0) /* don't update? */ return; rx_ecode = new_ecode; /* update ecode */ rx_dbr = rx_esr; /* update RXDB */ return; } /* Device initialization. The RX is one of the few devices that schedules an I/O transfer as part of its initialization. */ t_stat rx_reset (DEVICE *dptr) { rx_csr = rx_dbr = 0; /* clear regs */ rx_esr = rx_ecode = 0; /* clear error */ rx_track = rx_sector = 0; /* clear addr */ rx_state = IDLE; /* ctrl idle */ CLR_INT (RX); /* clear int req */ sim_cancel (&rx_unit[1]); /* cancel drive 1 */ if (dptr->flags & DEV_DIS) /* disabled? */ sim_cancel (&rx_unit[0]); else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ rx_state = INIT_COMPLETE; /* yes, sched init */ sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); } else rx_done (0, 0010); /* no, error */ return auto_config (0, 0); /* run autoconfig */ } /* Device bootstrap */ #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 026) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 042130, /* "XD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012700, 0000000, /* MOV #unit, R0 ; unit number */ 0010003, /* MOV R0, R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0012701, 0177170, /* MOV #RXCS, R1 ; csr */ 0032711, 0000040, /* BITB #40, (R1) ; ready? */ 0001775, /* BEQ .-4 */ 0052703, 0000007, /* BIS #READ+GO, R3 */ 0010311, /* MOV R3, (R1) ; read & go */ 0105711, /* TSTB (R1) ; xfr ready? */ 0100376, /* BPL .-2 */ 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */ 0105711, /* TSTB (R1) ; xfr ready? */ 0100376, /* BPL .-2 */ 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ 0005003, /* CLR R3 */ 0032711, 0000040, /* BITB #40, (R1) ; ready? */ 0001775, /* BEQ .-4 */ 0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */ 0105711, /* TSTB (R1) ; xfr, done? */ 0001776, /* BEQ .-2 */ 0100003, /* BPL .+010 */ 0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */ 0000772, /* BR .-012 */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0005007 /* CLR R7 */ }; t_stat rx_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; M[BOOT_CSR >> 1] = rx_dib.ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rp.c0000644000175000017500000013035011112124116013210 0ustar vlmvlm/* pdp11_rp.c - RP04/05/06/07 RM02/03/05/80 Massbus disk controller Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rp RH/RP/RM moving head disks 17-May-07 RMS CS1 DVA resides in device, not MBA 21-Nov-05 RMS Enable/disable device also enables/disables Massbus adapter 12-Nov-05 RMS Fixed DriveClear, does not clear disk address 16-Aug-05 RMS Fixed C++ declaration and cast problems 18-Mar-05 RMS Added attached test to detach routine 12-Sep-04 RMS Cloned from pdp11_rp.c Note: The VMS driver and the RP controller documentation state that ER2 = offset 8 SN = offset 12 But the TOPS-10 and TOPS-20 drivers, and the RP schematics state that SN = offset 8 ER2 = offset 12 The simulation follows the schematics. The VMS drivers defines but does not use these offsets, and the error logger follows the schematics. */ #if defined (VM_PDP10) #error "PDP-10 uses pdp10_rp.c!" #elif defined (VM_PDP11) #include "pdp11_defs.h" #define INIT_DTYPE RM03_DTYPE #define INIT_SIZE RM03_SIZE #elif defined (VM_VAX) #include "vax_defs.h" #define INIT_DTYPE RP06_DTYPE #define INIT_SIZE RP06_SIZE #define DMASK 0xFFFF #if (!UNIBUS) #error "Qbus not supported!" #endif #endif #include #define RP_CTRL 0 /* ctrl is RP */ #define RM_CTRL 1 /* ctrl is RM */ #define RP_NUMDR 8 /* #drives */ #define RP_NUMWD 256 /* words/sector */ #define RP_MAXFR (1 << 16) /* max transfer */ #define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) drv_tab[d].sect))) #define RM_OF (MBA_RMASK + 1) /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_M_DTYPE 7 #define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ #define UNIT_V_DUMMY (UNIT_V_UF + 5) /* dummy flag */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_DUMMY (1 << UNIT_V_DUMMY) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ /* RPCS1, RMCS1 - control/status 1 - offset 0 */ #define RP_CS1_OF 0 #define RM_CS1_OF (0 + RM_OF) #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_N_FNC (CS1_M_FNC + 1) #define FNC_NOP 000 /* no operation */ #define FNC_UNLOAD 001 /* unload */ #define FNC_SEEK 002 /* seek */ #define FNC_RECAL 003 /* recalibrate */ #define FNC_DCLR 004 /* drive clear */ #define FNC_RELEASE 005 /* port release */ #define FNC_OFFSET 006 /* offset */ #define FNC_RETURN 007 /* return to center */ #define FNC_PRESET 010 /* read-in preset */ #define FNC_PACK 011 /* pack acknowledge */ #define FNC_SEARCH 014 /* search */ #define FNC_XFER 024 /* >=? data xfr */ #define FNC_WCHK 024 /* write check */ #define FNC_WRITE 030 /* write */ #define FNC_WRITEH 031 /* write w/ headers */ #define FNC_READ 034 /* read */ #define FNC_READH 035 /* read w/ headers */ #define CS1_RW 076 #define CS1_DVA 04000 /* drive avail */ #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) /* RPDS, RMDS - drive status - offset 1 */ #define RP_DS_OF 1 #define RM_DS_OF (1 + RM_OF) #define DS_OFM 0000001 /* offset mode */ #define DS_VV 0000100 /* volume valid */ #define DS_RDY 0000200 /* drive ready */ #define DS_DPR 0000400 /* drive present */ #define DS_PGM 0001000 /* programable NI */ #define DS_LST 0002000 /* last sector */ #define DS_WRL 0004000 /* write locked */ #define DS_MOL 0010000 /* medium online */ #define DS_PIP 0020000 /* pos in progress */ #define DS_ERR 0040000 /* error */ #define DS_ATA 0100000 /* attention active */ #define DS_MBZ 0000076 /* RPER1, RMER1 - error status 1 - offset 2 */ #define RP_ER1_OF 2 #define RM_ER1_OF (2 + RM_OF) #define ER1_ILF 0000001 /* illegal func */ #define ER1_ILR 0000002 /* illegal register */ #define ER1_RMR 0000004 /* reg mod refused */ #define ER1_PAR 0000010 /* parity err */ #define ER1_FER 0000020 /* format err NI */ #define ER1_WCF 0000040 /* write clk fail NI */ #define ER1_ECH 0000100 /* ECC hard err NI */ #define ER1_HCE 0000200 /* hdr comp err NI */ #define ER1_HCR 0000400 /* hdr CRC err NI */ #define ER1_AOE 0001000 /* addr ovflo err */ #define ER1_IAE 0002000 /* invalid addr err */ #define ER1_WLE 0004000 /* write lock err */ #define ER1_DTE 0010000 /* drive time err NI */ #define ER1_OPI 0020000 /* op incomplete */ #define ER1_UNS 0040000 /* drive unsafe */ #define ER1_DCK 0100000 /* data check NI */ /* RPMR, RMMR - maintenace register - offset 3*/ #define RP_MR_OF 3 #define RM_MR_OF (3 + RM_OF) /* RPAS, RMAS - attention summary - offset 4 */ #define RP_AS_OF 4 #define RM_AS_OF (4 + RM_OF) #define AS_U0 0000001 /* unit 0 flag */ /* RPDA, RMDA - sector/track - offset 5 */ #define RP_DA_OF 5 #define RM_DA_OF (5 + RM_OF) #define DA_V_SC 0 /* sector pos */ #define DA_M_SC 077 /* sector mask */ #define DA_V_SF 8 /* track pos */ #define DA_M_SF 077 /* track mask */ #define DA_MBZ 0140300 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) /* RPDT, RMDT - drive type - offset 6 */ #define RP_DT_OF 6 #define RM_DT_OF (6 + RM_OF) /* RPLA, RMLA - look ahead register - offset 7 */ #define RP_LA_OF 7 #define RM_LA_OF (7 + RM_OF) #define LA_V_SC 6 /* sector pos */ /* RPSN, RMSN - serial number - offset 8 */ #define RP_SN_OF 8 #define RM_SN_OF (8 + RM_OF) /* RPOF, RMOF - offset register - offset 9 */ #define RP_OF_OF 9 #define RM_OF_OF (9 + RM_OF) #define OF_HCI 0002000 /* hdr cmp inh NI */ #define OF_ECI 0004000 /* ECC inhibit NI */ #define OF_F22 0010000 /* format NI */ #define OF_MBZ 0161400 /* RPDC, RMDC - desired cylinder - offset 10 */ #define RP_DC_OF 10 #define RM_DC_OF (10 + RM_OF) #define DC_V_CY 0 /* cylinder pos */ #define DC_M_CY 01777 /* cylinder mask */ #define DC_MBZ 0176000 #define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) #define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \ GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs)) /* RPCC - current cylinder - offset 11 RMHR - holding register - offset 11 */ #define RP_CC_OF 11 #define RM_HR_OF (11 + RM_OF) /* RPER2 - error status 2 - drive unsafe conditions - unimplemented - offset 12 RMMR2 - maintenance register - unimplemented - offset 12 */ #define RP_ER2_OF 12 #define RM_MR2_OF (12 + RM_OF) /* RPER3 - error status 3 - more unsafe conditions - unimplemented - offset 13 RMER2 - error status 2 - unimplemented - offset 13 */ #define RP_ER3_OF 13 #define RM_ER2_OF (13 + RM_OF) /* RPEC1, RMEC1 - ECC status 1 - unimplemented - offset 14 */ #define RP_EC1_OF 14 #define RM_EC1_OF (14 + RM_OF) /* RPEC2, RMEC1 - ECC status 2 - unimplemented - offset 15 */ #define RP_EC2_OF 15 #define RM_EC2_OF (15 + RM_OF) /* This controller supports many different disk drive types: type #sectors/ #surfaces/ #cylinders/ surface cylinder drive RM02/3 32 5 823 =67MB RP04/5 22 19 411 =88MB RM80 31 14 559 =124MB RP06 22 19 815 =176MB RM05 32 19 823 =256MB RP07 50 32 630 =516MB In theory, each drive can be a different type. The size field in each unit selects the drive capacity for each drive and thus the drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. Note: the RP07, despite its designation, belongs to the RM family */ #define RM03_DTYPE 0 #define RM03_SECT 32 #define RM03_SURF 5 #define RM03_CYL 823 #define RM03_DEV 020024 #define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD) #define RP04_DTYPE 1 #define RP04_SECT 22 #define RP04_SURF 19 #define RP04_CYL 411 #define RP04_DEV 020020 #define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) #define RM80_DTYPE 2 #define RM80_SECT 31 #define RM80_SURF 14 #define RM80_CYL 559 #define RM80_DEV 020026 #define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD) #define RP06_DTYPE 3 #define RP06_SECT 22 #define RP06_SURF 19 #define RP06_CYL 815 #define RP06_DEV 020022 #define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) #define RM05_DTYPE 4 #define RM05_SECT 32 #define RM05_SURF 19 #define RM05_CYL 823 #define RM05_DEV 020027 #define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD) #define RP07_DTYPE 5 #define RP07_SECT 50 #define RP07_SURF 32 #define RP07_CYL 630 #define RP07_DEV 020042 #define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) #define RP_CTRL 0 #define RM_CTRL 1 struct drvtyp { int32 sect; /* sectors */ int32 surf; /* surfaces */ int32 cyl; /* cylinders */ int32 size; /* #blocks */ int32 devtype; /* device type */ int32 ctrl; /* ctrl type */ }; static struct drvtyp drv_tab[] = { { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, RM_CTRL }, { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, RP_CTRL }, { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, RM_CTRL }, { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, RP_CTRL }, { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, RM_CTRL }, { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, RM_CTRL }, { 0 } }; uint16 *rpxb = NULL; /* xfer buffer */ uint16 rpcs1[RP_NUMDR] = { 0 }; /* control/status 1 */ uint16 rpda[RP_NUMDR] = { 0 }; /* track/sector */ uint16 rpds[RP_NUMDR] = { 0 }; /* drive status */ uint16 rper1[RP_NUMDR] = { 0 }; /* error status 1 */ uint16 rmhr[RP_NUMDR] = { 0 }; /* holding reg */ uint16 rpmr[RP_NUMDR] = { 0 }; /* maint reg */ uint16 rmmr2[RP_NUMDR] = { 0 }; /* maint reg 2 */ uint16 rpof[RP_NUMDR] = { 0 }; /* offset */ uint16 rpdc[RP_NUMDR] = { 0 }; /* cylinder */ uint16 rper2[RP_NUMDR] = { 0 }; /* error status 2 */ uint16 rper3[RP_NUMDR] = { 0 }; /* error status 3 */ uint16 rpec1[RP_NUMDR] = { 0 }; /* ECC correction 1 */ uint16 rpec2[RP_NUMDR] = { 0 }; /* ECC correction 2 */ int32 rp_stopioe = 1; /* stop on error */ int32 rp_swait = 10; /* seek time */ int32 rp_rwait = 10; /* rotate time */ static const char *rp_fname[CS1_N_FNC] = { "NOP", "UNLD", "SEEK", "RECAL", "DCLR", "RLS", "OFFS", "RETN", "PRESET", "PACK", "12", "13", "SCH", "15", "16", "17", "20", "21", "22", "23", "WRCHK", "25", "26", "27", "WRITE", "WRHDR", "32", "33", "READ", "RDHDR", "36", "37" }; extern FILE *sim_deb; t_stat rp_mbrd (int32 *data, int32 ofs, int32 drv); t_stat rp_mbwr (int32 data, int32 ofs, int32 drv); t_stat rp_svc (UNIT *uptr); t_stat rp_reset (DEVICE *dptr); t_stat rp_attach (UNIT *uptr, char *cptr); t_stat rp_detach (UNIT *uptr); t_stat rp_boot (int32 unitno, DEVICE *dptr); void rp_set_er (int32 flg, int32 drv); void rp_clr_as (int32 mask); void rp_update_ds (int32 flg, int32 drv); t_stat rp_go (int32 drv); t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rp_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); int32 rp_abort (void); /* RP data structures rp_dev RP device descriptor rp_unit RP unit list rp_reg RP register list rp_mod RP modifier list */ DIB rp_dib = { MBA_RP, 0, &rp_mbrd, &rp_mbwr, 0, 0, 0, { &rp_abort } }; UNIT rp_unit[] = { { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) } }; REG rp_reg[] = { { BRDATA (CS1, rpcs1, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (DA, rpda, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (DS, rpds, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (ER1, rper1, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (HR, rmhr, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (OF, rpof, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (DC, rpdc, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (ER2, rper2, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (ER3, rper3, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (EC1, rpec1, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (EC2, rpec2, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (MR, rpmr, DEV_RDX, 16, RP_NUMDR) }, { BRDATA (MR2, rmmr2, DEV_RDX, 16, RP_NUMDR) }, { DRDATA (STIME, rp_swait, 24), REG_NZ + PV_LEFT }, { DRDATA (RTIME, rp_rwait, 24), REG_NZ + PV_LEFT }, { URDATA (CAPAC, rp_unit[0].capac, 10, T_ADDR_W, 0, RP_NUMDR, PV_LEFT | REG_HRO) }, { FLDATA (STOP_IOE, rp_stopioe, 0) }, { GRDATA (CTRLTYPE, rp_dib.lnt, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB rp_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MASSBUS", "MASSBUS", NULL, &mba_show_num }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rp_set_bad }, { (UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM03", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP04", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM80", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP06", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RM05", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, "RP07", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE), "RM03", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE), "RP04", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE), "RM80", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE), "RP06", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE), "RM05", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE), "RP07", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), (RM03_DTYPE << UNIT_V_DTYPE), NULL, "RM03", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP04_DTYPE << UNIT_V_DTYPE), NULL, "RP04", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RM80_DTYPE << UNIT_V_DTYPE), NULL, "RM80", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP06_DTYPE << UNIT_V_DTYPE), NULL, "RP06", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RM05_DTYPE << UNIT_V_DTYPE), NULL, "RM05", &rp_set_size }, { (UNIT_AUTO+UNIT_DTYPE), (RP07_DTYPE << UNIT_V_DTYPE), NULL, "RP07", &rp_set_size }, { 0 } }; DEVICE rp_dev = { "RP", rp_unit, rp_reg, rp_mod, RP_NUMDR, DEV_RDX, 30, 1, DEV_RDX, 16, NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, &rp_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_MBUS | DEV_DEBUG }; /* Massbus register read */ t_stat rp_mbrd (int32 *data, int32 ofs, int32 drv) { uint32 val, dtype, i; UNIT *uptr; rp_update_ds (0, drv); /* update ds */ uptr = rp_dev.units + drv; /* get unit */ if (uptr->flags & UNIT_DIS) { /* nx disk */ *data = 0; return MBE_NXD; } dtype = GET_DTYPE (uptr->flags); /* get drive type */ ofs = ofs & MBA_RMASK; /* mask offset */ if (drv_tab[dtype].ctrl == RM_CTRL) /* RM? convert */ ofs = ofs + RM_OF; switch (ofs) { /* decode offset */ case RP_CS1_OF: case RM_CS1_OF: /* RPCS1 */ val = (rpcs1[drv] & CS1_RW) | CS1_DVA; /* DVA always set */ break; case RP_DA_OF: case RM_DA_OF: /* RPDA */ val = rpda[drv] = rpda[drv] & ~DA_MBZ; break; case RP_DS_OF: case RM_DS_OF: /* RPDS */ val = rpds[drv]; break; case RP_ER1_OF: case RM_ER1_OF: /* RPER1 */ val = rper1[drv]; break; case RP_AS_OF: case RM_AS_OF: /* RPAS */ val = 0; for (i = 0; i < RP_NUMDR; i++) { if (rpds[i] & DS_ATA) val |= (AS_U0 << i); } break; case RP_LA_OF: case RM_LA_OF: /* RPLA */ val = GET_SECTOR (rp_rwait, dtype) << LA_V_SC; break; case RP_MR_OF: case RM_MR_OF: /* RPMR */ val = rpmr[drv]; break; case RP_DT_OF: case RM_DT_OF: /* RPDT */ val = drv_tab[dtype].devtype; break; case RP_SN_OF: case RM_SN_OF: /* RPSN */ val = 020 | (drv + 1); break; case RP_OF_OF: case RM_OF_OF: /* RPOF */ val = rpof[drv] = rpof[drv] & ~OF_MBZ; break; case RP_DC_OF: case RM_DC_OF: /* RPDC */ val = rpdc[drv] = rpdc[drv] & ~DC_MBZ; break; case RP_CC_OF: /* RPCC */ val = rp_unit[drv].CYL; break; case RP_ER2_OF: case RM_ER2_OF: /* RPER2 */ val = rper2[drv]; break; case RP_ER3_OF: /* RPER3 */ val = rper3[drv]; break; case RP_EC1_OF: case RM_EC1_OF: /* RPEC1 */ val = rpec1[drv]; break; case RP_EC2_OF: case RM_EC2_OF: /* RPEC2 */ val = rpec2[drv]; break; case RM_HR_OF: /* RMHR */ val = rmhr[drv] ^ DMASK; break; case RM_MR2_OF: /* RHMR2 */ val = rmmr2[drv]; break; default: /* all others */ *data = 0; return MBE_NXR; } *data = val; return SCPE_OK; } /* Massbus register write */ t_stat rp_mbwr (int32 data, int32 ofs, int32 drv) { int32 dtype; UNIT *uptr; uptr = rp_dev.units + drv; /* get unit */ if (uptr->flags & UNIT_DIS) /* nx disk */ return MBE_NXD; if ((ofs != RP_AS_OF) && sim_is_active (uptr)) { /* unit busy? */ rp_set_er (ER1_RMR, drv); /* won't write */ rp_update_ds (0, drv); return SCPE_OK; } rmhr[drv] = data; /* save write */ dtype = GET_DTYPE (uptr->flags); /* get drive type */ ofs = ofs & MBA_RMASK; /* mask offset */ if (drv_tab[dtype].ctrl == RM_CTRL) /* RM? convert */ ofs = ofs + RM_OF; switch (ofs) { /* decode PA<5:1> */ case RP_CS1_OF: case RM_CS1_OF: /* RPCS1 */ rpcs1[drv] = data & CS1_RW; if (data & CS1_GO) /* start op */ return rp_go (drv); break; case RP_DA_OF: case RM_DA_OF: /* RPDA */ rpda[drv] = data & ~DA_MBZ; break; case RP_AS_OF: case RM_AS_OF: /* RPAS */ rp_clr_as (data); break; case RP_MR_OF: case RM_MR_OF: /* RPMR */ rpmr[drv] = data; break; case RP_OF_OF: case RM_OF_OF: /* RPOF */ rpof[drv] = data & ~OF_MBZ; break; case RP_DC_OF: case RM_DC_OF: /* RPDC */ rpdc[drv] = data & ~DC_MBZ; break; case RM_MR2_OF: /* RMMR2 */ rmmr2[drv] = data; break; case RP_ER1_OF: case RM_ER1_OF: /* RPER1 */ case RP_DS_OF: case RM_DS_OF: /* RPDS */ case RP_LA_OF: case RM_LA_OF: /* RPLA */ case RP_DT_OF: case RM_DT_OF: /* RPDT */ case RP_SN_OF: case RM_SN_OF: /* RPSN */ case RP_CC_OF: /* RPCC */ case RP_ER2_OF: case RM_ER2_OF: /* RPER2 */ case RP_ER3_OF: /* RPER3 */ case RP_EC1_OF: case RM_EC1_OF: /* RPEC1 */ case RP_EC2_OF: case RM_EC2_OF: /* RPEC2 */ case RM_HR_OF: /* RMHR */ break; /* read only */ default: /* all others */ return MBE_NXR; } /* end switch */ rp_update_ds (0, drv); /* update status */ return SCPE_OK; } /* Initiate operation - unit not busy, function set */ t_stat rp_go (int32 drv) { int32 dc, fnc, dtype, t; UNIT *uptr; fnc = GET_FNC (rpcs1[drv]); /* get function */ if (DEBUG_PRS (rp_dev)) fprintf (sim_deb, ">>RP%d STRT: fnc=%s, ds=%o, cyl=%o, da=%o, er=%o\n", drv, rp_fname[fnc], rpds[drv], rpdc[drv], rpda[drv], rper1[drv]); uptr = rp_dev.units + drv; /* get unit */ rp_clr_as (AS_U0 << drv); /* clear attention */ dtype = GET_DTYPE (uptr->flags); /* get drive type */ dc = rpdc[drv]; /* assume seek, sch */ if ((fnc != FNC_DCLR) && (rpds[drv] & DS_ERR)) { /* err & ~clear? */ rp_set_er (ER1_ILF, drv); /* not allowed */ rp_update_ds (DS_ATA, drv); /* set attention */ return MBE_GOE; } switch (fnc) { /* case on function */ case FNC_DCLR: /* drive clear */ rper1[drv] = rper2[drv] = rper3[drv] = 0; /* clear errors */ rpec2[drv] = 0; /* clear EC2 */ if (drv_tab[dtype].ctrl == RM_CTRL) /* RM? */ rpmr[drv] = 0; /* clear maint */ else rpec1[drv] = 0; /* RP, clear EC1 */ case FNC_NOP: /* no operation */ case FNC_RELEASE: /* port release */ return SCPE_OK; case FNC_PRESET: /* read-in preset */ rpdc[drv] = 0; /* clear disk addr */ rpda[drv] = 0; rpof[drv] = 0; /* clear offset */ case FNC_PACK: /* pack acknowledge */ rpds[drv] = rpds[drv] | DS_VV; /* set volume valid */ return SCPE_OK; case FNC_OFFSET: /* offset mode */ case FNC_RETURN: if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_set_er (ER1_UNS, drv); /* unsafe */ break; } rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ sim_activate (uptr, rp_swait); /* time operation */ return SCPE_OK; case FNC_UNLOAD: /* unload */ case FNC_RECAL: /* recalibrate */ dc = 0; /* seek to 0 */ case FNC_SEEK: /* seek */ case FNC_SEARCH: /* search */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_set_er (ER1_UNS, drv); /* unsafe */ break; } if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ rp_set_er (ER1_IAE, drv); break; } rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ t = abs (dc - uptr->CYL); /* cyl diff */ if (t == 0) /* min time */ t = 1; sim_activate (uptr, rp_swait * t); /* schedule */ uptr->CYL = dc; /* save cylinder */ return SCPE_OK; case FNC_WRITEH: /* write headers */ case FNC_WRITE: /* write */ case FNC_WCHK: /* write check */ case FNC_READ: /* read */ case FNC_READH: /* read headers */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_set_er (ER1_UNS, drv); /* unsafe */ break; } if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ rp_set_er (ER1_IAE, drv); break; } rpds[drv] = rpds[drv] & ~DS_RDY; /* clear drive rdy */ sim_activate (uptr, rp_rwait + (rp_swait * abs (dc - uptr->CYL))); uptr->CYL = dc; /* save cylinder */ return SCPE_OK; default: /* all others */ rp_set_er (ER1_ILF, drv); /* not supported */ break; } rp_update_ds (DS_ATA, drv); /* set attn, req int */ return MBE_GOE; } /* Abort opertion - there is a data transfer in progress */ int32 rp_abort (void) { return rp_reset (&rp_dev); } /* Service unit timeout Complete movement or data transfer command Unit must exist - can't remove an active unit Unit must be attached - detach cancels in progress operations */ t_stat rp_svc (UNIT *uptr) { int32 i, fnc, dtype, drv, err; int32 wc, abc, awc, mbc, da; dtype = GET_DTYPE (uptr->flags); /* get drive type */ drv = (int32) (uptr - rp_dev.units); /* get drv number */ da = GET_DA (rpdc[drv], rpda[drv], dtype) * RP_NUMWD; /* get disk addr */ fnc = GET_FNC (rpcs1[drv]); /* get function */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rp_set_er (ER1_UNS, drv); /* set drive error */ if (fnc >= FNC_XFER) /* xfr? set done */ mba_set_don (rp_dib.ba); rp_update_ds (DS_ATA, drv); /* set attn */ return (rp_stopioe? SCPE_UNATT: SCPE_OK); } rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ switch (fnc) { /* case on function */ case FNC_OFFSET: /* offset */ rp_update_ds (DS_OFM | DS_ATA, drv); break; case FNC_RETURN: /* return to centerline */ rpds[drv] = rpds[drv] & ~DS_OFM; /* clear offset, set attn */ rp_update_ds (DS_ATA, drv); break; case FNC_UNLOAD: /* unload */ rp_detach (uptr); /* detach unit */ break; case FNC_RECAL: /* recalibrate */ case FNC_SEARCH: /* search */ case FNC_SEEK: /* seek */ rp_update_ds (DS_ATA, drv); break; case FNC_WRITE: /* write */ if (uptr->flags & UNIT_WPRT) { /* write locked? */ rp_set_er (ER1_WLE, drv); /* set drive error */ mba_set_exc (rp_dib.ba); /* set exception */ rp_update_ds (DS_ATA, drv); /* set attn */ return SCPE_OK; } case FNC_WCHK: /* write check */ case FNC_READ: /* read */ case FNC_READH: /* read headers */ err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); mbc = mba_get_bc (rp_dib.ba); /* get byte count */ wc = (mbc + 1) >> 1; /* convert to words */ if ((da + wc) > drv_tab[dtype].size) { /* disk overrun? */ rp_set_er (ER1_AOE, drv); /* set err */ wc = drv_tab[dtype].size - da; /* trim xfer */ mbc = wc << 1; /* trim mb count */ if (da >= drv_tab[dtype].size) { /* none left? */ mba_set_exc (rp_dib.ba); /* set exception */ rp_update_ds (DS_ATA, drv); /* set attn */ break; } } if (fnc == FNC_WRITE) { /* write? */ abc = mba_rdbufW (rp_dib.ba, mbc, rpxb); /* get buffer */ wc = (abc + 1) >> 1; /* actual # wds */ awc = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); for (i = wc; i < awc; i++) /* fill buf */ rpxb[i] = 0; if (wc && !err) { /* write buf */ fxwrite (rpxb, sizeof (uint16), awc, uptr->fileref); err = ferror (uptr->fileref); } } /* end if wr */ else { /* read or wchk */ awc = fxread (rpxb, sizeof (uint16), wc, uptr->fileref); err = ferror (uptr->fileref); for (i = awc; i < wc; i++) /* fill buf */ rpxb[i] = 0; if (fnc == FNC_WCHK) /* write check? */ mba_chbufW (rp_dib.ba, mbc, rpxb); /* check vs mem */ else mba_wrbufW (rp_dib.ba, mbc, rpxb); /* store in mem */ } /* end if read */ da = da + wc + (RP_NUMWD - 1); if (da >= drv_tab[dtype].size) rpds[drv] = rpds[drv] | DS_LST; da = da / RP_NUMWD; rpda[drv] = da % drv_tab[dtype].sect; da = da / drv_tab[dtype].sect; rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); rpdc[drv] = da / drv_tab[dtype].surf; uptr->CYL = rpdc[drv]; if (err != 0) { /* error? */ rp_set_er (ER1_PAR, drv); /* set drive error */ mba_set_exc (rp_dib.ba); /* set exception */ rp_update_ds (DS_ATA, drv); perror ("RP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } case FNC_WRITEH: /* write headers stub */ mba_set_don (rp_dib.ba); /* set done */ rp_update_ds (0, drv); /* update ds */ break; } /* end case func */ if (DEBUG_PRS (rp_dev)) fprintf (sim_deb, ">>RP%d DONE: fnc=%s, ds=%o, cyl=%o, da=%o, er=%d\n", drv, rp_fname[fnc], rpds[drv], rpdc[drv], rpda[drv], rper1[drv]); return SCPE_OK; } /* Set drive error */ void rp_set_er (int32 flag, int32 drv) { rper1[drv] = rper1[drv] | flag; rpds[drv] = rpds[drv] | DS_ATA; mba_upd_ata (rp_dib.ba, 1); return; } /* Clear attention flags */ void rp_clr_as (int32 mask) { uint32 i, as; for (i = as = 0; i < RP_NUMDR; i++) { if (mask & (AS_U0 << i)) rpds[i] &= ~DS_ATA; if (rpds[i] & DS_ATA) as = 1; } mba_upd_ata (rp_dib.ba, as); return; } /* Drive status update */ void rp_update_ds (int32 flag, int32 drv) { if (rp_unit[drv].flags & UNIT_DIS) rpds[drv] = rper1[drv] = 0; else rpds[drv] = (rpds[drv] | DS_DPR) & ~DS_PGM; if (rp_unit[drv].flags & UNIT_ATT) rpds[drv] = rpds[drv] | DS_MOL; else rpds[drv] = rpds[drv] & ~(DS_MOL | DS_VV | DS_RDY); if (rper1[drv] | rper2[drv] | rper3[drv]) rpds[drv] = rpds[drv] | DS_ERR; else rpds[drv] = rpds[drv] & ~DS_ERR; rpds[drv] = rpds[drv] | flag; if (flag & DS_ATA) mba_upd_ata (rp_dib.ba, 1); return; } /* Device reset */ t_stat rp_reset (DEVICE *dptr) { int32 i; UNIT *uptr; mba_set_enbdis (MBA_RP, rp_dev.flags & DEV_DIS); for (i = 0; i < RP_NUMDR; i++) { uptr = rp_dev.units + i; sim_cancel (uptr); uptr->CYL = 0; if (uptr->flags & UNIT_ATT) rpds[i] = (rpds[i] & DS_VV) | DS_DPR | DS_RDY | DS_MOL | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); else if (uptr->flags & UNIT_DIS) rpds[i] = 0; else rpds[i] = DS_DPR; rpcs1[i] = 0; rper1[i] = 0; rpof[i] = 0; rpdc[i] = 0; rpda[i] = 0; rpmr[i] = 0; rper2[i] = 0; rper3[i] = 0; rpec1[i] = 0; rpec2[i] = 0; rmmr2[i] = 0; rmhr[i] = 0; } if (rpxb == NULL) rpxb = (uint16 *) calloc (RP_MAXFR, sizeof (uint16)); if (rpxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Device attach */ t_stat rp_attach (UNIT *uptr, char *cptr) { int32 drv, i, p; t_stat r; uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = DS_MOL | DS_RDY | DS_DPR | /* upd drv status */ ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); rper1[drv] = 0; rp_update_ds (DS_ATA, drv); /* upd ctlr status */ if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ if (uptr->flags & UNIT_RO) return SCPE_OK; return pdp11_bad_block (uptr, drv_tab[GET_DTYPE (uptr->flags)].sect, RP_NUMWD); } if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; for (i = 0; drv_tab[i].sect != 0; i++) { if (p <= (drv_tab[i].size * (int) sizeof (int16))) { uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); uptr->capac = drv_tab[i].size; return SCPE_OK; } } return SCPE_OK; } /* Device detach */ t_stat rp_detach (UNIT *uptr) { int32 drv; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; drv = (int32) (uptr - rp_dev.units); /* get drv number */ rpds[drv] = rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OFM); rp_update_ds (DS_ATA, drv); /* request intr */ return detach_unit (uptr); } /* Set size command validation routine */ t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 dtype = GET_DTYPE (val); if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = drv_tab[dtype].size; return SCPE_OK; } /* Set bad block routine */ t_stat rp_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) { return pdp11_bad_block (uptr, drv_tab[GET_DTYPE (uptr->flags)].sect, RP_NUMWD); } /* Boot routine */ #if defined (VM_PDP11) #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 014) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) static const uint16 boot_rom[] = { 0042102, /* "BD" */ 0012706, BOOT_START, /* mov #boot_start, sp */ 0012700, 0000000, /* mov #unit, r0 */ 0012701, 0176700, /* mov #RPCS1, r1 */ 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ 0010061, 0000010, /* mov r0, 10(r1) ; set unit */ 0012711, 0000021, /* mov #RIP+GO, (r1) ; pack ack */ 0012761, 0010000, 0000032, /* mov #FMT16B, 32(r1) ; 16b mode */ 0012761, 0177000, 0000002, /* mov #-512., 2(r1) ; set wc */ 0005061, 0000004, /* clr 4(r1) ; clr ba */ 0005061, 0000006, /* clr 6(r1) ; clr da */ 0005061, 0000034, /* clr 34(r1) ; clr cyl */ 0012711, 0000071, /* mov #READ+GO, (r1) ; read */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0005002, /* clr R2 */ 0005003, /* clr R3 */ 0012704, BOOT_START+020, /* mov #start+020, r4 */ 0005005, /* clr R5 */ 0105011, /* clrb (r1) */ 0005007 /* clr PC */ }; t_stat rp_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; UNIT *uptr = rp_dev.units + unitno; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & (RP_NUMDR - 1); M[BOOT_CSR >> 1] = mba_get_csr (rp_dib.ba) & DMASK; if (drv_tab[GET_DTYPE (uptr->flags)].ctrl == RP_CTRL) M[BOOT_START >> 1] = 042102; /* "BD" */ else M[BOOT_START >> 1] = 042122; /* "RD" */ saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat rp_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif simh-3.8.1/PDP11/pdp11_tc.c0000644000175000017500000015763411112256570013225 0ustar vlmvlm/* pdp11_tc.c: PDP-11 DECtape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tc TC11/TU56 DECtape 23-Jun-06 RMS Fixed switch conflict in ATTACH 10-Feb-06 RMS READ sets extended data bits in TCST (found by Alan Frisbie) 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 30-Sep-04 RMS Revised Unibus interface 25-Jan-04 RMS Revised for device debug support 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR 29-Dec-03 RMS Changed initial status to disabled (in Qbus system) 18-Oct-03 RMS Fixed reverse checksum in read all Added DECtape off reel message Simplified timing 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 29-Sep-02 RMS Added variable address support to bootstrap Added vector change/display support Added 16b format support New data structures 30-May-02 RMS Widened POS to 32b 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted POS, STATT, LASTT to arrays 09-Nov-01 RMS Added bus map support 15-Sep-01 RMS Integrated debug logging 27-Sep-01 RMS Fixed interrupt after stop for RSTS/E 07-Sep-01 RMS Revised device disable and interrupt mechanisms 29-Aug-01 RMS Added casts to PDP-8 unpack routine 17-Jul-01 RMS Moved function prototype 11-May-01 RMS Fixed bug in reset 26-Apr-01 RMS Added device enable/disable support 18-Apr-01 RMS Changed to rewind tape before boot 16-Mar-01 RMS Fixed bug in interrupt after stop 15-Mar-01 RMS Added 129th word to PDP-8 format PDP-11 DECtapes are represented in memory by fixed length buffer of 32b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the TD8E formatter) is: reverse end zone 8192 reverse end zone codes ~ 10 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 8192 forward end zone codes ~ 10 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of read all and write all. Read all assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write all writes only the data words and dumps the interblock words in the bit bucket. */ #include "pdp11_defs.h" #define DT_NUMDR 8 /* #drives */ #define DT_M_NUMDR (DT_NUMDR - 1) #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_BLKWD 1 /* blk no word in h/t */ #define DT_CSMWD 4 /* checksum word in h/t */ #define DT_HTWRD 5 /* header/trailer words */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word size in lines */ #define D18_BSIZE 256 /* block size in 18b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D16_FILSIZ (D18_TSIZE * D18_BSIZE * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 86 /* block size in 18b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) /* This controller */ #define DT_CAPAC D18_CAPAC /* default */ #define DT_WSIZE D18_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) #define DT_QREZ(u) (((u)->pos) < DT_EZLIN) #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) /* TCST - 177340 - status register */ #define STA_END 0100000 /* end zone */ #define STA_PAR 0040000 /* parity err */ #define STA_MRK 0020000 /* mark trk err */ #define STA_ILO 0010000 /* illegal op */ #define STA_SEL 0004000 /* select err */ #define STA_BLKM 0002000 /* block miss err */ #define STA_DATM 0001000 /* data miss err */ #define STA_NXM 0000400 /* nx mem err */ #define STA_UPS 0000200 /* up to speed */ #define STA_V_XD 0 /* extended data */ #define STA_M_XD 03 #define STA_ALLERR (STA_END | STA_PAR | STA_MRK | STA_ILO | \ STA_SEL | STA_BLKM | STA_DATM | STA_NXM ) #define STA_RWERR (STA_END | STA_PAR | STA_MRK | \ STA_BLKM | STA_DATM | STA_NXM ) #define STA_RW 0000003 #define STA_GETXD(x) (((x) >> STA_V_XD) & STA_M_XD) /* TCCM - 177342 - command register */ /* #define CSR_ERR 0100000 */ #define CSR_MNT 0020000 /* maint (unimpl) */ #define CSR_INH 0010000 /* delay inhibit */ #define CSR_DIR 0004000 /* reverse */ #define CSR_V_UNIT 8 /* unit select */ #define CSR_M_UNIT 07 #define CSR_UNIT (CSR_M_UNIT << CSR_V_UNIT) /* #define CSR_DONE 0000200 */ /* #define CSR_IE 0000100 */ #define CSR_V_MEX 4 /* mem extension */ #define CSR_M_MEX 03 #define CSR_MEX (CSR_M_MEX << CSR_V_MEX) #define CSR_V_FNC 1 /* function */ #define CSR_M_FNC 07 #define FNC_STOP 00 /* stop all */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_RALL 03 /* read all */ #define FNC_SSEL 04 /* stop selected */ #define FNC_WMRK 05 /* write */ #define FNC_WRIT 06 /* write all */ #define FNC_WALL 07 /* write timing */ /* define CSR_GO 0000001 */ #define CSR_RW 0117576 /* read/write */ #define CSR_GETUNIT(x) (((x) >> CSR_V_UNIT) & CSR_M_UNIT) #define CSR_GETMEX(x) (((x) >> CSR_V_MEX) & CSR_M_MEX) #define CSR_GETFNC(x) (((x) >> CSR_V_FNC) & CSR_M_FNC) #define CSR_INCMEX(x) (((x) & ~CSR_MEX) | (((x) + (1 << CSR_V_MEX)) & CSR_MEX)) /* TCWC - 177344 - word count */ /* TCBA - 177346 - bus address */ /* TCDT - 177350 - data */ /* DECtape state */ #define DTS_V_MOT 3 /* motion */ #define DTS_M_MOT 07 #define DTS_STOP 0 /* stopped */ #define DTS_DECF 2 /* decel, fwd */ #define DTS_DECR 3 /* decel, rev */ #define DTS_ACCF 4 /* accel, fwd */ #define DTS_ACCR 5 /* accel, rev */ #define DTS_ATSF 6 /* @speed, fwd */ #define DTS_ATSR 7 /* @speed, rev */ #define DTS_DIR 01 /* dir mask */ #define DTS_V_FNC 0 /* function */ #define DTS_M_FNC 07 #define DTS_OFR FNC_WMRK /* "off reel" */ #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) #define DTS_V_2ND 6 /* next state */ #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ ((DTS_STA (y, z)) << DTS_V_2ND) #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ ((DTS_STA (y, z)) << DTS_V_3RD) #define DTS_NXTSTA(x) (x >> DTS_V_2ND) /* Logging */ #define LOG_MS 0x1 #define LOG_RW 0x2 #define LOG_BL 0x4 #define DT_SETDONE tccm = tccm | CSR_DONE; \ if (tccm & CSR_IE) \ SET_INT (DTA) #define DT_CLRDONE tccm = tccm & ~CSR_DONE; \ CLR_INT (DTA) #define ABS(x) (((x) < 0)? (-(x)): (x)) extern uint16 *M; /* memory */ extern int32 int_req[IPL_HLVL]; extern UNIT cpu_unit; extern int32 sim_switches; extern FILE *sim_deb; int32 tcst = 0; /* status */ int32 tccm = 0; /* command */ int32 tcwc = 0; /* word count */ int32 tcba = 0; /* bus address */ int32 tcdt = 0; /* data */ int32 dt_ctime = 100; /* fast cmd time */ int32 dt_ltime = 12; /* interline time */ int32 dt_dctime = 40000; /* decel time */ int32 dt_substate = 0; int32 dt_logblk = 0; int32 dt_stopoffr = 0; DEVICE dt_dev; t_stat dt_rd (int32 *data, int32 PA, int32 access); t_stat dt_wr (int32 data, int32 PA, int32 access); t_stat dt_svc (UNIT *uptr); t_stat dt_svcdone (UNIT *uptr); t_stat dt_reset (DEVICE *dptr); t_stat dt_attach (UNIT *uptr, char *cptr); t_stat dt_detach (UNIT *uptr); t_stat dt_boot (int32 unitno, DEVICE *dptr); void dt_deselect (int32 oldf); void dt_newsa (int32 newf); void dt_newfnc (UNIT *uptr, int32 newsta); t_bool dt_setpos (UNIT *uptr); void dt_schedez (UNIT *uptr, int32 dir); void dt_seterr (UNIT *uptr, int32 e); void dt_stopunit (UNIT *uptr); int32 dt_comobv (int32 val); int32 dt_csum (UNIT *uptr, int32 blk); int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); extern int32 sim_is_running; /* DT data structures dt_dev DT device descriptor dt_unit DT unit list dt_reg DT register list dt_mod DT modifier list */ DIB dt_dib = { IOBA_TC, IOLN_TC, &dt_rd, &dt_wr, 1, IVCL (DTA), VEC_DTA, { NULL } }; UNIT dt_unit[] = { { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, { UDATA (&dt_svcdone, UNIT_DIS, 0) } }; #define DT_TIMER (DT_NUMDR) REG dt_reg[] = { { ORDATA (TCST, tcst, 16) }, { ORDATA (TCCM, tccm, 16) }, { ORDATA (TCWC, tcwc, 16) }, { ORDATA (TCBA, tcba, 16) }, { ORDATA (TCDT, tcdt, 16) }, { FLDATA (INT, IREQ (DTA), INT_V_DTA) }, { FLDATA (ERR, tccm, CSR_V_ERR) }, { FLDATA (DONE, tccm, CSR_V_DONE) }, { FLDATA (IE, tccm, CSR_V_DONE) }, { DRDATA (CTIME, dt_ctime, 31), REG_NZ }, { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, { ORDATA (SUBSTATE, dt_substate, 1) }, { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO) }, { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO) }, { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, DT_NUMDR, REG_HRO) }, { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, { ORDATA (DEVADDR, dt_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, dt_dib.vec, 16), REG_HRO }, { NULL } }; MTAB dt_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEBTAB dt_deb[] = { { "MOTION", LOG_MS }, { "DATA", LOG_RW }, { "BLOCK", LOG_BL }, { NULL, 0 } }; DEVICE dt_dev = { "TC", dt_unit, dt_reg, dt_mod, DT_NUMDR + 1, 8, 24, 1, 8, 18, NULL, NULL, &dt_reset, &dt_boot, &dt_attach, &dt_detach, &dt_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dt_deb, NULL, NULL }; /* IO dispatch routines, I/O addresses 17777340 - 17777350 */ t_stat dt_rd (int32 *data, int32 PA, int32 access) { int32 j, unum, mot, fnc; j = (PA >> 1) & 017; /* get reg offset */ unum = CSR_GETUNIT (tccm); /* get drive */ switch (j) { case 000: /* TCST */ mot = DTS_GETMOT (dt_unit[unum].STATE); /* get motion */ if (mot >= DTS_ATSF) /* set/clr speed */ tcst = tcst | STA_UPS; else tcst = tcst & ~STA_UPS; *data = tcst; break; case 001: /* TCCM */ if (tcst & STA_ALLERR) /* set/clr error */ tccm = tccm | CSR_ERR; else tccm = tccm & ~CSR_ERR; *data = tccm; break; case 002: /* TCWC */ *data = tcwc; break; case 003: /* TCBA */ *data = tcba; break; case 004: /* TCDT */ fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ if (fnc == FNC_RALL) { /* read all? */ DT_CLRDONE; /* clear done */ } *data = tcdt; break; } return SCPE_OK; } t_stat dt_wr (int32 data, int32 PA, int32 access) { int32 i, j, unum, old_tccm, fnc; UNIT *uptr; j = (PA >> 1) & 017; /* get reg offset */ switch (j) { case 000: /* TCST */ if ((access == WRITEB) && (PA & 1)) break; tcst = (tcst & ~STA_RW) | (data & STA_RW); break; case 001: /* TCCM */ old_tccm = tccm; /* save prior */ if (access == WRITEB) data = (PA & 1)? (tccm & 0377) | (data << 8): (tccm & ~0377) | data; if ((data & CSR_IE) == 0) CLR_INT (DTA); else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) || (data & CSR_DONE)) SET_INT (DTA); tccm = (tccm & ~CSR_RW) | (data & CSR_RW); if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */ tcst = tcst & ~STA_ALLERR; /* clear errors */ tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */ CLR_INT (DTA); /* clear int */ if ((old_tccm ^ tccm) & CSR_UNIT) dt_deselect (old_tccm); unum = CSR_GETUNIT (tccm); /* get drive */ fnc = CSR_GETFNC (tccm); /* get function */ if (fnc == FNC_STOP) { /* stop all? */ sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); for (i = 0; i < DT_NUMDR; i++) dt_stopunit (dt_dev.units + i); /* stop unit */ break; } uptr = dt_dev.units + unum; if (uptr->flags & UNIT_DIS) /* disabled? */ dt_seterr (uptr, STA_SEL); /* select err */ if ((fnc == FNC_WMRK) || /* write mark? */ ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) dt_seterr (uptr, STA_ILO); /* illegal op */ if (!(tccm & CSR_ERR)) dt_newsa (tccm); } else if ((tccm & CSR_ERR) == 0) { /* clear err? */ tcst = tcst & ~STA_RWERR; if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; } break; case 002: /* TCWC */ tcwc = data; /* word write only! */ break; case 003: /* TCBA */ tcba = data; /* word write only! */ break; case 004: /* TCDT */ unum = CSR_GETUNIT (tccm); /* get drive */ fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ if (fnc == FNC_WALL) { /* write all? */ DT_CLRDONE; /* clear done */ } tcdt = data; /* word write only! */ break; } return SCPE_OK; } /* Unit deselect */ void dt_deselect (int32 oldf) { int32 old_unit = CSR_GETUNIT (oldf); UNIT *uptr = dt_dev.units + old_unit; int32 old_mot = DTS_GETMOT (uptr->STATE); if (old_mot >= DTS_ATSF) /* at speed? */ dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); else if (old_mot >= DTS_ACCF) /* accelerating? */ DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); return; } /* New operation 1. If function = stop - if not already stopped or decelerating, schedule deceleration - schedule command completion 2. If change in direction, - if not decelerating, schedule deceleration - set accelerating (other dir) as next state - set function as next next state 3. If not accelerating or at speed, - schedule acceleration - set function as next state 4. If not yet at speed, - set function as next state 5. If at speed, - set function as current state, schedule function */ void dt_newsa (int32 newf) { int32 new_unit, prev_mot, new_fnc; int32 prev_dir, new_dir; UNIT *uptr; new_unit = CSR_GETUNIT (newf); /* new, old units */ uptr = dt_dev.units + new_unit; if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ dt_seterr (uptr, STA_SEL); /* no, error */ return; } prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ prev_dir = prev_mot & DTS_DIR; /* previous dir */ new_fnc = CSR_GETFNC (newf); /* new function */ new_dir = (newf & CSR_DIR) != 0; /* new di? */ if (new_fnc == FNC_SSEL) { /* stop unit? */ sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ dt_stopunit (uptr); /* stop unit */ return; } if (prev_mot == DTS_STOP) { /* start? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_dir ^ new_dir) { /* dir chg? */ dt_stopunit (uptr); /* stop unit */ DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ return; } if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* cancel cur */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mot < DTS_ATSF) { /* not at speed? */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ return; } /* Schedule new DECtape function This routine is only called if - the selected unit is attached - the selected unit is at speed (forward or backward) This routine - updates the selected unit's position - updates the selected unit's state - schedules the new operation */ void dt_newfnc (UNIT *uptr, int32 newsta) { int32 fnc, dir, blk, unum, relpos, newpos; uint32 oldpos; oldpos = uptr->pos; /* save old pos */ if (dt_setpos (uptr)) /* update pos */ return; uptr->STATE = newsta; /* update state */ fnc = DTS_GETFNC (uptr->STATE); /* set variables */ dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; unum = (int32) (uptr - dt_dev.units); if (oldpos == uptr->pos) uptr->pos = uptr->pos + (dir? -1: 1); blk = DT_LIN2BL (uptr->pos, uptr); if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ dt_seterr (uptr, STA_END); /* set ez flag, stop */ return; } dt_substate = 0; /* substate = normal */ sim_cancel (uptr); /* cancel cur op */ switch (fnc) { /* case function */ case DTS_OFR: /* off reel */ if (dir) /* rev? < start */ newpos = -1000; else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ break; case FNC_SRCH: /* search */ if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; else newpos = DT_BLK2LN ((DT_QREZ (uptr)? 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", unum, (dir? "backward": "forward")); break; case FNC_WRIT: /* write */ case FNC_READ: /* read */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dt_seterr (uptr, STA_BLKM); return; } if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: %s block %d %s\n", unum, ((fnc == FNC_READ)? "read": "write"), blk, (dir? "backward": "forward")); break; case FNC_RALL: /* read all */ case FNC_WALL: /* write all */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; else newpos = DT_EZLIN + (DT_WSIZE - 1); } else { relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if (dir? (relpos < (DTU_LPERB (uptr) - DT_CSMLN)): /* switch in time? */ (relpos >= DT_CSMLN)) { dt_seterr (uptr, STA_BLKM); return; } if (dir) newpos = DT_BLK2LN (blk + 1, uptr) - DT_CSMLN - DT_WSIZE; else newpos = DT_BLK2LN (blk, uptr) + DT_CSMLN + (DT_WSIZE - 1); } if (fnc == FNC_WALL) sim_activate /* write all? */ (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: read all block %d %s\n", unum, blk, (dir? "backward": "forward")); break; default: dt_seterr (uptr, STA_SEL); /* bad state */ return; } sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/dt_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool dt_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 mot = DTS_GETMOT (uptr->STATE); int32 unum, delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (mot & ~DTS_DIR) { /* case on motion */ case DTS_STOP: /* stop */ delta = 0; break; case DTS_DECF: /* slowing */ ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case DTS_ACCF: /* accelerating */ ulin = ut / (uint32) dt_ltime; udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; delta = (ulin * ulin) / (2 * udelt); break; case DTS_ATSF: /* at speed */ delta = ut / (uint32) dt_ltime; break; } if (mot & DTS_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel? */ uptr->STATE = uptr->pos = 0; unum = (int32) (uptr - dt_dev.units); if ((unum == CSR_GETUNIT (tccm)) && (CSR_GETFNC (tccm) != FNC_STOP)) dt_seterr (uptr, STA_SEL); /* error */ return TRUE; } return FALSE; } /* Command timer service after stop - set done */ t_stat dt_svcdone (UNIT *uptr) { DT_SETDONE; return SCPE_OK; } /* Unit service Unit must be attached, detach cancels operation */ t_stat dt_svc (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; int32 fnc = DTS_GETFNC (uptr->STATE); int32 *fbuf = (int32 *) uptr->filebuf; int32 blk, wrd, relpos, dat; uint32 ba, ma; uint16 wbuf; /* Motion cases Decelerating - if next state != stopped, must be accel reverse Accelerating - next state must be @speed, schedule function At speed - do functional processing */ switch (mot) { case DTS_DECF: case DTS_DECR: /* decelerating */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ if (uptr->STATE) /* not stopped? */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ return SCPE_OK; case DTS_ACCF: case DTS_ACCR: /* accelerating */ dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ return SCPE_OK; case DTS_ATSF: case DTS_ATSR: /* at speed */ break; /* check function */ default: /* other */ dt_seterr (uptr, STA_SEL); /* state error */ return SCPE_OK; } /* Functional cases Search - transfer block number, schedule next block Off reel - detach unit (it must be deselected) */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); if (DT_QEZ (uptr)) { /* in end zone? */ dt_seterr (uptr, STA_END); /* end zone error */ return SCPE_OK; } blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ switch (fnc) { /* at speed, check fnc */ case FNC_SRCH: /* search */ tcdt = blk; /* set block # */ dt_schedez (uptr, dir); /* sched end zone */ DT_SETDONE; /* set done */ break; case DTS_OFR: /* off reel */ detach_unit (uptr); /* must be deselected */ uptr->STATE = uptr->pos = 0; /* no visible action */ break; /* Read If wc ovf has not occurred, inc ma, wc and copy word from tape to memory If wc ovf, set flag If not end of block, schedule next word If end of block and not wc ovf, schedule next block If end of block and wc ovf, set done, schedule end zone */ case FNC_READ: /* read */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ if (!dt_substate) { /* !wc ovf? */ ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ tcdt = wbuf = fbuf[ba] & DMASK; /* read word */ tcst = (tcst & ~STA_M_XD) | ((fbuf[ma] >> 16) & STA_M_XD); if (Map_WriteW (ma, 2, &wbuf)) { /* store, nxm? */ dt_seterr (uptr, STA_NXM); break; } tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ tcba = (tcba + 2) & DMASK; if (tcba <= 1) tccm = CSR_INCMEX (tccm); if (tcwc == 0) dt_substate = 1; } if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else if (dt_substate) { /* wc ovf? */ dt_schedez (uptr, dir); /* sched end zone */ DT_SETDONE; /* set done */ } else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); break; /* Write If wc ovf has not occurred, inc ma, wc Copy word from memory (or 0, to fill block) to tape If wc ovf, set flag If not end of block, schedule next word If end of block and not wc ovf, schedule next block If end of block and wc ovf, set done, schedule end zone */ case FNC_WRIT: /* write */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ if (dt_substate) /* wc ovf? fill */ tcdt = 0; else { ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ if (Map_ReadW (ma, 2, &wbuf)) { /* fetch word */ dt_seterr (uptr, STA_NXM); break; } tcdt = wbuf; /* get word */ tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ tcba = (tcba + 2) & DMASK; if (tcba <= 1) tccm = CSR_INCMEX (tccm); } ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ fbuf[ba] = tcdt; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (tcwc == 0) dt_substate = 1; if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else if (dt_substate) { /* wc ovf? */ dt_schedez (uptr, dir); /* sched end zone */ DT_SETDONE; } else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); break; /* Read all - read current header or data word */ case FNC_RALL: if (tccm & CSR_DONE) { /* done set? */ dt_seterr (uptr, STA_DATM); /* data miss */ break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dat = fbuf[ba]; /* get tape word */ } else dat = dt_gethdr (uptr, blk, relpos); /* get hdr */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); tcdt = dat & DMASK; /* low 16b */ tcst = (tcst & ~STA_M_XD) | ((dat >> 16) & STA_M_XD); sim_activate (uptr, DT_WSIZE * dt_ltime); DT_SETDONE; /* set done */ break; /* Write all - write current header or data word */ case FNC_WALL: if (tccm & CSR_DONE) { /* done set? */ dt_seterr (uptr, STA_DATM); /* data miss */ break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); dat = (STA_GETXD (tcst) << 16) | tcdt; /* get data word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ fbuf[ba] = dat; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; } /* else /* ignore hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); DT_SETDONE; /* set done */ break; default: dt_seterr (uptr, STA_SEL); /* impossible state */ break; } return SCPE_OK; } /* Utility routines */ /* Set error flag */ void dt_seterr (UNIT *uptr, int32 e) { int32 mot = DTS_GETMOT (uptr->STATE); tcst = tcst | e; /* set error flag */ tccm = tccm | CSR_ERR; if (!(tccm & CSR_DONE)) { /* not done? */ DT_SETDONE; } if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ sim_cancel (uptr); /* cancel activity */ if (dt_setpos (uptr)) /* update position */ return; sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ } return; } /* Stop unit */ void dt_stopunit (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; if (mot == DTS_STOP) return; /* already stopped? */ if ((mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | dir, 0); /* state = decel */ return; } /* Schedule end zone */ void dt_schedez (UNIT *uptr, int32 dir) { int32 newpos; if (dir) /* rev? rev ez */ newpos = DT_EZLIN - DT_WSIZE; else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Complement obverse routine (18b) */ int32 dt_comobv (int32 dat) { dat = dat ^ 0777777; /* compl obverse */ dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | ((dat >> 3) & 0700) | ((dat & 0700) << 3) | ((dat & 070) << 9) | ((dat & 07) << 15); return dat; } /* Checksum routine */ int32 dt_csum (UNIT *uptr, int32 blk) { int32 *fbuf = (int32 *) uptr->filebuf; int32 ba = blk * DTU_BSIZE (uptr); int32 i, csum, wrd; csum = 077; /* init csum */ for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i] ^ 0777777; /* get ~word */ csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; } return (csum & 077); } /* Get header word (18b) */ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) { int32 wrd = relpos / DT_WSIZE; if (wrd == DT_BLKWD) /* fwd blknum */ return blk; if (wrd == DT_CSMWD) /* rev csum */ return 077; if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ return (dt_csum (uptr, blk) << 12); if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ return dt_comobv (blk); return 0; /* all others */ } /* Reset routine */ t_stat dt_reset (DEVICE *dptr) { int32 i, prev_mot; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ uptr = dt_dev.units + i; if (sim_is_running) { /* RESET? */ prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ if (dt_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } tcst = tcwc = tcba = tcdt = 0; /* clear reg */ tccm = CSR_DONE; CLR_INT (DTA); /* clear int req */ return SCPE_OK; } /* Device bootstrap */ #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 020) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0042124, /* "TD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012700, 0000000, /* MOV #unit, R0 ; unit number */ 0010003, /* MOV R0, R3 */ 0000303, /* SWAB R3 */ 0012701, 0177342, /* MOV #TCCM, R1 ; csr */ 0012702, 0004003, /* RW: MOV #4003, R2 ; rev+rnum+go */ 0050302, /* BIS R3, R2 */ 0010211, /* MOV R2, (R1) ; load csr */ 0032711, 0100200, /* BIT #100200, (R1) ; wait */ 0001775, /* BEQ .-4 */ 0100370, /* BPL RW ; no err, cont */ 0005761, 0177776, /* TST -2(R1) ; end zone? */ 0100036, /* BPL ER ; no, err */ 0012702, 0000003, /* MOV #3, R2 ; rnum+go */ 0050302, /* BIS R3, R2 */ 0010211, /* MOV R2, (R1) ; load csr */ 0032711, 0100200, /* BIT #100200, (R1) ; wait */ 0001775, /* BEQ .-4 */ 0100426, /* BMI ER ; err, die */ 0005761, 0000006, /* TST 6(R1) ; blk 0? */ 0001023, /* BNE ER ; no, die */ 0012761, 0177000, 0000002, /* MOV #-256.*2, 2(R1) ; load wc */ 0005061, 0000004, /* CLR 4(R1) ; clear ba */ 0012702, 0000005, /* MOV #READ+GO, R2 ; read & go */ 0050302, /* BIS R3, R2 */ 0010211, /* MOV R2, (R1) ; load csr */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0032711, 0100200, /* BIT #100200, (R1) ; wait */ 0001775, /* BEQ .-4 */ 0100401, /* BMI ER ; err, die */ 0005007, /* CLR PC */ 0012711, 0000001, /* ER: MOV #1, (R1) ; stop all */ 0000000 /* HALT */ }; t_stat dt_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; dt_unit[unitno].pos = DT_EZLIN; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & DT_M_NUMDR; M[BOOT_CSR >> 1] = (dt_dib.ba & DMASK) + 02; saved_PC = BOOT_ENTRY; return SCPE_OK; } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 12b, read 12b format and convert to 18b in buffer If 16b, read 16b format and convert to 18b in buffer If 18b/36b, read data into buffer */ t_stat dt_attach (UNIT *uptr, char *cptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, sz, k, *fbuf; int32 u = uptr - dt_dev.units; t_stat r; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) /* fail? */ return r; if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default 16b */ if (sim_switches & SWMASK ('T')) /* att 12b? */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; else if (sim_switches & SWMASK ('F')) /* att 18b? */ uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ ((sz = sim_fsize (uptr->fileref)) > D16_FILSIZ)) { if (sz <= D8_FILSIZ) uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; else uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ printf ("%s%d: ", sim_dname (&dt_dev), u); if (uptr->flags & UNIT_8FMT) printf ("12b format"); else if (uptr->flags & UNIT_11FMT) printf ("16b format"); else printf ("18b/36b format"); printf (", buffering file in memory\n"); if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | ((uint32) (pdp8b[k + 1] >> 6) & 077); fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | ((uint32) pdp8b[k + 2] & 07777); ba = ba + 2; } /* end blk loop */ } /* end file loop */ uptr->hwmark = ba; } /* end if */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; for (k = 0; k < D18_BSIZE; k++) fbuf[ba++] = pdp11b[k]; } uptr->hwmark = ba; } /* end elif */ else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), uptr->capac, uptr->fileref); uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ return SCPE_OK; } /* Detach routine Cancel in progress operation If 12b, convert 18b buffer to 12b and write to file If 16b, convert 18b buffer to 16b and write to file If 18b/36b, write buffer to file Deallocate buffer */ t_stat dt_detach (UNIT* uptr) { uint16 pdp8b[D8_NBSIZE]; uint16 pdp11b[D18_BSIZE]; uint32 ba, k, *fbuf; int32 u = uptr - dt_dev.units; if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; if (sim_is_active (uptr)) { /* active? cancel op */ sim_cancel (uptr); if ((u == CSR_GETUNIT (tccm)) && ((tccm & CSR_DONE) == 0)) { tcst = tcst | STA_SEL; tccm = tccm | CSR_ERR | CSR_DONE; if (tccm & CSR_IE) SET_INT (DTA); } uptr->STATE = uptr->pos = 0; } fbuf = (uint32 *) uptr->filebuf; /* file buffer */ if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ pdp8b[k] = (fbuf[ba] >> 6) & 07777; pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | ((fbuf[ba + 1] >> 12) & 077); pdp8b[k + 2] = fbuf[ba + 1] & 07777; ba = ba + 2; } /* end loop blk */ fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 12b */ else if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ for (k = 0; k < D18_BSIZE; k++) /* loop blk */ pdp11b[k] = fbuf[ba++] & DMASK; fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); if (ferror (uptr->fileref)) break; } /* end loop file */ } /* end if 16b */ else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ uptr->hwmark, uptr->fileref); if (ferror (uptr->fileref)) perror ("I/O error"); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ return detach_unit (uptr); } simh-3.8.1/PDP11/pdp11_pt.c0000644000175000017500000002657611107654410013241 0ustar vlmvlm/* pdp11_pt.c: PC11 paper tape reader/punch simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr paper tape reader ptp paper tape punch 07-Jul-05 RMS Removed extraneous externs 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 12-Sep-02 RMS Split off from pdp11_stddev.c */ #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" #define PT_DIS DEV_DIS extern int32 int_req; #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define PT_DIS DEV_DIS extern int32 int_req[IPL_HLVL]; #else /* PDP-11 version */ #include "pdp11_defs.h" #define PT_DIS 0 extern int32 int_req[IPL_HLVL]; #endif #define PTRCSR_IMP (CSR_ERR+CSR_BUSY+CSR_DONE+CSR_IE) /* paper tape reader */ #define PTRCSR_RW (CSR_IE) #define PTPCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* paper tape punch */ #define PTPCSR_RW (CSR_IE) int32 ptr_csr = 0; /* control/status */ int32 ptr_stopioe = 0; /* stop on error */ int32 ptp_csr = 0; /* control/status */ int32 ptp_stopioe = 0; /* stop on error */ DEVICE ptr_dev, ptp_dev; t_stat ptr_rd (int32 *data, int32 PA, int32 access); t_stat ptr_wr (int32 data, int32 PA, int32 access); t_stat ptr_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptr_attach (UNIT *uptr, char *ptr); t_stat ptr_detach (UNIT *uptr); t_stat ptp_rd (int32 *data, int32 PA, int32 access); t_stat ptp_wr (int32 data, int32 PA, int32 access); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); t_stat ptp_attach (UNIT *uptr, char *ptr); t_stat ptp_detach (UNIT *uptr); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_reg PTR register list */ DIB ptr_dib = { IOBA_PTR, IOLN_PTR, &ptr_rd, &ptr_wr, 1, IVCL (PTR), VEC_PTR, { NULL } }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { GRDATA (BUF, ptr_unit.buf, DEV_RDX, 8, 0) }, { GRDATA (CSR, ptr_csr, DEV_RDX, 16, 0) }, { FLDATA (INT, int_req, INT_V_PTR) }, { FLDATA (ERR, ptr_csr, CSR_V_ERR) }, { FLDATA (BUSY, ptr_csr, CSR_V_BUSY) }, { FLDATA (DONE, ptr_csr, CSR_V_DONE) }, { FLDATA (IE, ptr_csr, CSR_V_IE) }, { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, { FLDATA (DEVDIS, ptr_dev.flags, DEV_V_DIS), REG_HRO }, { NULL } }; MTAB ptr_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, DEV_RDX, 8, NULL, NULL, &ptr_reset, NULL, &ptr_attach, &ptr_detach, &ptr_dib, DEV_DISABLE | PT_DIS | DEV_UBUS | DEV_QBUS }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_reg PTP register list */ DIB ptp_dib = { IOBA_PTP, IOLN_PTP, &ptp_rd, &ptp_wr, 1, IVCL (PTP), VEC_PTP, { NULL } }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { GRDATA (BUF, ptp_unit.buf, DEV_RDX, 8, 0) }, { GRDATA (CSR, ptp_csr, DEV_RDX, 16, 0) }, { FLDATA (INT, int_req, INT_V_PTP) }, { FLDATA (ERR, ptp_csr, CSR_V_ERR) }, { FLDATA (DONE, ptp_csr, CSR_V_DONE) }, { FLDATA (IE, ptp_csr, CSR_V_IE) }, { 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[] = { { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, DEV_RDX, 8, NULL, NULL, &ptp_reset, NULL, &ptp_attach, &ptp_detach, &ptp_dib, DEV_DISABLE | PT_DIS | DEV_UBUS | DEV_QBUS }; /* Paper tape reader I/O address routines */ t_stat ptr_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* ptr csr */ *data = ptr_csr & PTRCSR_IMP; return SCPE_OK; case 1: /* ptr buf */ ptr_csr = ptr_csr & ~CSR_DONE; CLR_INT (PTR); *data = ptr_unit.buf & 0377; return SCPE_OK; } return SCPE_NXM; /* can't get here */ } t_stat ptr_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* ptr csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) CLR_INT (PTR); else if (((ptr_csr & CSR_IE) == 0) && (ptr_csr & (CSR_ERR | CSR_DONE))) SET_INT (PTR); if (data & CSR_GO) { ptr_csr = (ptr_csr & ~CSR_DONE) | CSR_BUSY; CLR_INT (PTR); if (ptr_unit.flags & UNIT_ATT) /* data to read? */ sim_activate (&ptr_unit, ptr_unit.wait); else sim_activate (&ptr_unit, 0); /* error if not */ } ptr_csr = (ptr_csr & ~PTRCSR_RW) | (data & PTRCSR_RW); return SCPE_OK; case 1: /* ptr buf */ return SCPE_OK; } /* end switch PA */ return SCPE_NXM; /* can't get here */ } /* Paper tape reader service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; ptr_csr = (ptr_csr | CSR_ERR) & ~CSR_BUSY; if (ptr_csr & CSR_IE) SET_INT (PTR); if ((ptr_unit.flags & UNIT_ATT) == 0) return IORETURN (ptr_stopioe, SCPE_UNATT); if ((temp = getc (ptr_unit.fileref)) == EOF) { if (feof (ptr_unit.fileref)) { if (ptr_stopioe) printf ("PTR end of file\n"); else return SCPE_OK; } else perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } ptr_csr = (ptr_csr | CSR_DONE) & ~CSR_ERR; ptr_unit.buf = temp & 0377; ptr_unit.pos = ptr_unit.pos + 1; return SCPE_OK; } /* Paper tape reader support routines */ t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; ptr_csr = 0; if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; CLR_INT (PTR); sim_cancel (&ptr_unit); return SCPE_OK; } t_stat ptr_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; else ptr_csr = ptr_csr & ~CSR_ERR; return reason; } t_stat ptr_detach (UNIT *uptr) { ptr_csr = ptr_csr | CSR_ERR; return detach_unit (uptr); } /* Paper tape punch I/O address routines */ t_stat ptp_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* ptp csr */ *data = ptp_csr & PTPCSR_IMP; return SCPE_OK; case 1: /* ptp buf */ *data = ptp_unit.buf; return SCPE_OK; } return SCPE_NXM; /* can't get here */ } t_stat ptp_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* ptp csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) CLR_INT (PTP); else if (((ptp_csr & CSR_IE) == 0) && (ptp_csr & (CSR_ERR | CSR_DONE))) SET_INT (PTP); ptp_csr = (ptp_csr & ~PTPCSR_RW) | (data & PTPCSR_RW); return SCPE_OK; case 1: /* ptp buf */ if ((PA & 1) == 0) ptp_unit.buf = data & 0377; ptp_csr = ptp_csr & ~CSR_DONE; CLR_INT (PTP); if (ptp_unit.flags & UNIT_ATT) /* file to write? */ sim_activate (&ptp_unit, ptp_unit.wait); else sim_activate (&ptp_unit, 0); /* error if not */ return SCPE_OK; } /* end switch PA */ return SCPE_NXM; /* can't get here */ } /* Paper tape punch service */ t_stat ptp_svc (UNIT *uptr) { ptp_csr = ptp_csr | CSR_ERR | CSR_DONE; if (ptp_csr & CSR_IE) SET_INT (PTP); if ((ptp_unit.flags & UNIT_ATT) == 0) return IORETURN (ptp_stopioe, SCPE_UNATT); if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_csr = ptp_csr & ~CSR_ERR; ptp_unit.pos = ptp_unit.pos + 1; return SCPE_OK; } /* Paper tape punch support routines */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; ptp_csr = CSR_DONE; if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; CLR_INT (PTP); sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } t_stat ptp_attach (UNIT *uptr, char *cptr) { t_stat reason; reason = attach_unit (uptr, cptr); if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; else ptp_csr = ptp_csr & ~CSR_ERR; return reason; } t_stat ptp_detach (UNIT *uptr) { ptp_csr = ptp_csr | CSR_ERR; return detach_unit (uptr); } simh-3.8.1/PDP11/pdp11_cpumod.h0000644000175000017500000002527211003357450014101 0ustar vlmvlm/* pdp11_cpumod.h: PDP-11 CPU model definitions Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 22-Apr-08 RMS Added 11/70 MBRK register 30-Aug-05 RMS Added additional 11/60 registers */ #ifndef _PDP11_CPUMOD_H_ #define _PDP11_CPUMOD_H_ 0 #define SOP_1103 (BUS_Q) #define OPT_1103 (OPT_EIS|OPT_FIS) #define PSW_1103 0000377 #define SOP_1104 (BUS_U) #define OPT_1104 0 #define PSW_1104 0000377 #define SOP_1105 (BUS_U) #define OPT_1105 0 #define PSW_1105 0000377 #define SOP_1120 (BUS_U) #define OPT_1120 0 #define PSW_1120 0000377 #define SOP_1123 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1123 (OPT_FPP|OPT_CIS) #define PSW_F 0170777 #define PAR_F 0177777 #define PDR_F 0077516 #define MM0_F 0160157 #define MM3_F 0000060 #define SOP_1123P (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1123P (OPT_FPP|OPT_CIS) #define SOP_1124 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) #define OPT_1124 (OPT_FPP|OPT_CIS) #define SOP_1134 (BUS_U|OPT_EIS|OPT_MMU) #define OPT_1134 (OPT_FPP) #define PSW_1134 0170377 #define PAR_1134 0007777 #define PDR_1134 0077516 #define MM0_1134 0160557 #define SOP_1140 (BUS_U|OPT_EIS|OPT_MMU) #define OPT_1140 (OPT_FIS) #define PSW_1140 0170377 #define PAR_1140 0007777 #define PDR_1140 0077516 #define MM0_1140 0160557 #define SOP_1144 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) #define OPT_1144 (OPT_FPP|OPT_CIS) #define PSW_1144 0170777 #define PAR_1144 0177777 #define PDR_1144 0177516 #define MM0_1144 0160557 #define MM3_1144 0000077 #define SOP_1145 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_RH11) #define OPT_1145 (OPT_FPP) #define PSW_1145 0174377 #define PAR_1145 0007777 #define PDR_1145 0077717 #define MM0_1145 0171777 #define MM3_1145 0000007 #define SOP_1160 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1160 0 #define PSW_1160 0170377 #define PAR_1160 0007777 #define PDR_1160 0077516 #define MM0_1160 0160557 #define SOP_1170 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) #define OPT_1170 (OPT_FPP|OPT_RH11) #define PSW_1170 0174377 #define PAR_1170 0177777 #define PDR_1170 0077717 #define MM0_1170 0171777 #define MM3_1170 0000067 #define SOP_1173 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1173 (OPT_CIS) #define PSW_J 0174777 #define PAR_J 0177777 #define PDR_J 0177516 #define MM0_J 0160177 #define MM3_J 0000077 #define SOP_1153 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1153 (OPT_CIS) #define SOP_1173B (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1173B (OPT_CIS) #define SOP_1183 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1183 (OPT_CIS) #define SOP_1184 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM|OPT_RH11) #define OPT_1184 (OPT_CIS) #define SOP_1193 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) #define OPT_1193 (OPT_CIS) #define SOP_1194 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM|OPT_RH11) #define OPT_1194 (OPT_CIS) #define MOD_MAX 20 /* MFPT codes */ #define MFPT_44 1 #define MFPT_F 3 #define MFPT_T 4 #define MFPT_J 5 /* KDF11B specific register */ #define PCRFB_RW 0037477 /* page ctrl reg */ #define CDRFB_RD 0000377 /* config reg */ #define CDRFB_WR 0000017 /* KT24 Unibus map specific registers */ #define LMAL_RD 0177777 /* last mapped low */ #define LMAH_RD 0000177 /* last mapped high */ #define LMAH_WR 0000100 /* 11/44 specific registers */ #define CCR44_RD 0033315 /* cache control */ #define CCR44_WR 0003315 #define CMR44_RD 0177437 /* cache maint */ #define CMR44_WR 0000037 #define CPUE44_BUSE 0004000 /* 11/60 specific registers */ #define WCS60_RD 0161776 /* WCS control */ #define WCS60_WR 0061676 #define MEME60_RD 0100340 /* memory error */ #define CCR60_RD 0000315 /* cache control */ #define CCR60_WR 0000115 #define MBRK60_WR 0007777 /* microbreak */ #define CPUE60_RD (CPUE_ODD|CPUE_TMO|CPUE_RED) /* 11/70 specific registers */ #define MBRK70_WR 0000377 /* microbreak */ /* J11 specific registers */ /* Maintenance register */ #define MAINT_V_UQ 9 /* Q/U flag */ #define MAINT_Q (0 << MAINT_V_UQ) /* Qbus */ #define MAINT_U (1 << MAINT_V_UQ) #define MAINT_V_FPA 8 /* FPA flag */ #define MAINT_NOFPA (0 << MAINT_V_FPA) #define MAINT_FPA (1 << MAINT_V_FPA) #define MAINT_V_TYP 4 /* system type */ #define MAINT_KDJA (1 << MAINT_V_TYP) /* KDJ11A */ #define MAINT_KDJB (2 << MAINT_V_TYP) /* KDJ11B */ #define MAINT_KDJD (4 << MAINT_V_TYP) /* KDJ11D */ #define MAINT_KDJE (5 << MAINT_V_TYP) /* KDJ11E */ #define MAINT_V_HTRAP 3 /* trap 4 on HALT */ #define MAINT_HTRAP (1 << MAINT_V_HTRAP) #define MAINT_V_POM 1 /* power on option */ #define MAINT_POODT (0 << MAINT_V_POM) /* power up ODT */ #define MAINT_POROM (2 << MAINT_V_POM) /* power up ROM */ #define MAINT_V_BPOK 0 /* power OK */ #define MAINT_BPOK (1 << MAINT_V_BPOK) /* KDJ11B control */ #define CSRJB_RD 0177767 #define CSRJB_WR 0037767 #define CSRJ_LTCI 0020000 /* force LTC int */ #define CSRJ_LTCD 0010000 /* disable LTC reg */ #define CSRJ_V_LTCSEL 10 #define CSRJ_M_LTCSEL 03 #define CSRJ_LTCSEL(x) (((x) >> CSRJ_V_LTCSEL) & CSRJ_M_LTCSEL) #define CSRJ_HBREAK 0001000 /* halt on break */ #define PCRJB_RW 0077176 /* page ctrl reg */ #define CDRJB_RD 0000377 /* config register */ #define CDRJB_WR 0000377 /* KDJ11D control */ #define CSRJD_RD 0157777 /* native register */ #define CSRJD_WR 0000377 #define CSRJD_15M 0040000 /* 1.5M mem on board */ /* KDJ11E control */ #define CSRJE_RD 0137360 /* control reg */ #define CSRJE_WR 0037370 #define PCRJE_RW 0177376 /* page ctrl reg */ #define CDRJE_RD 0000377 /* config register */ #define CDRJE_WR 0000077 #define ASRJE_RW 0030462 /* additional status */ #define ASRJE_V_TOY 8 #define ASRJE_TOY (1u << ASRJE_V_TOY) /* TOY serial bit */ #define ASRJE_TOYBIT(x) (((x) >> ASRJE_V_TOY) & 1) /* KDJ11E TOY clock */ #define TOY_HSEC 0 #define TOY_SEC 1 #define TOY_MIN 2 #define TOY_HR 3 #define TOY_DOW 4 #define TOY_DOM 5 #define TOY_MON 6 #define TOY_YR 7 #define TOY_LNT 8 /* KTJ11B Unibus map */ #define DCRKTJ_RD 0100616 /* diag control */ #define DCRKTJ_WR 0000416 #define DDRKTJ_RW 0177777 /* diag data */ #define MCRKTJ_RD 0000377 /* control register */ #define MCRKTJ_WR 0000177 /* Data tables */ struct cpu_table { char *name; /* model name */ uint32 std; /* standard flags */ uint32 opt; /* set/clear flags */ uint32 maxm; /* max memory */ uint32 psw; /* PSW mask */ uint32 mfpt; /* MFPT code */ uint32 par; /* PAR mask */ uint32 pdr; /* PDR mask */ uint32 mm0; /* MMR0 mask */ uint32 mm3; /* MMR3 mask */ }; typedef struct cpu_table CPUTAB; struct conf_table { uint32 cpum; uint32 optm; DIB *dib; }; typedef struct conf_table CNFTAB; /* Prototypes */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat cpu_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_clr_opt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_set_bus (int32 opt); #endif simh-3.8.1/PDP11/pdp11_mscp.h0000644000175000017500000006122511111666660013560 0ustar vlmvlm/* pdp11_mscp.h: DEC MSCP and TMSCP definitionsn Copyright (c) 2001-2008, Robert M Supnik Derived from work by Stephen F. Shirron Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 09-Jan-03 RMS Tape read/write end pkt is longer than disk read/write 20-Sep-02 RMS Merged TMSCP definitions */ #ifndef _PDP11_MSCP_H_ #define _PDP11_MSCP_H_ 0 /* Misc constants */ #define UID_DISK 2 /* disk class */ #define UID_TAPE 3 /* tape class */ /* Opcodes */ #define OP_ABO 1 /* b: abort */ #define OP_GCS 2 /* b: get command status */ #define OP_GUS 3 /* b: get unit status */ #define OP_SCC 4 /* b: set controller char */ #define OP_AVL 8 /* b: available */ #define OP_ONL 9 /* b: online */ #define OP_SUC 10 /* b: set unit char */ #define OP_DAP 11 /* b: det acc paths - nop */ #define OP_ACC 16 /* b: access */ #define OP_CCD 17 /* d: compare - nop */ #define OP_ERS 18 /* b: erase */ #define OP_FLU 19 /* d: flush - nop */ #define OP_ERG 22 /* t: erase gap */ #define OP_CMP 32 /* b: compare */ #define OP_RD 33 /* b: read */ #define OP_WR 34 /* b: write */ #define OP_WTM 36 /* t: write tape mark */ #define OP_POS 37 /* t: reposition */ #define OP_FMT 47 /* d: format */ #define OP_AVA 64 /* b: unit now avail */ #define OP_END 0x80 /* b: end flag */ /* Modifiers */ #define MD_EXP 0x8000 /* d: express NI */ #define MD_CMP 0x4000 /* b: compare NI */ #define MD_CSE 0x2000 /* b: clr ser err */ #define MD_ERR 0x1000 /* d: force error NI*/ #define MD_CDL 0x1000 /* t: clr data lost NI*/ #define MD_SCH 0x0800 /* t: supr cache NI */ #define MD_SEC 0x0200 /* b: supr err corr NI */ #define MD_SER 0x0100 /* b: supr err rec NI */ #define MD_DLE 0x0080 /* t: detect LEOT */ #define MD_IMM 0x0040 /* t: immediate NI */ #define MD_EXA 0x0020 /* b: excl access NI */ #define MD_SHD 0x0010 /* d: shadow NI */ #define MD_UNL 0x0010 /* t avl: unload */ #define MD_ERW 0x0008 /* t wr: enb rewrite */ #define MD_REV 0x0008 /* t rd, pos: reverse */ #define MD_SWP 0x0004 /* b suc: enb set wrp */ #define MD_OBC 0x0004 /* t: pos: obj count */ #define MD_IMF 0x0002 /* d onl: ign fmte NI */ #define MD_RWD 0x0002 /* t pos: rewind */ #define MD_ACL 0x0002 /* t avl: all class NI */ #define MD_NXU 0x0001 /* b gus: next unit */ #define MD_RIP 0x0001 /* d onl: allow rip NI */ /* End flags */ #define EF_LOG 0x0020 /* b: error log */ #define EF_SXC 0x0010 /* b: serious exc */ #define EF_EOT 0x0008 /* end of tape */ #define EF_PLS 0x0004 /* pos lost */ #define EF_DLS 0x0002 /* cached data lost NI */ /* Controller flags */ #define CF_RPL 0x8000 /* ctrl bad blk repl */ #define CF_ATN 0x0080 /* enb attention */ #define CF_MSC 0x0040 /* enb misc msgs */ #define CF_OTH 0x0020 /* enb othr host msgs */ #define CF_THS 0x0010 /* enb this host msgs */ #define CF_MSK (CF_ATN|CF_MSC|CF_OTH|CF_THS) /* Unit flags */ #define UF_RPL 0x8000 /* d: ctrl bad blk repl */ #define UF_CAC 0x8000 /* t: cache write back */ #define UF_WPH 0x2000 /* b: wr prot hwre */ #define UF_WPS 0x1000 /* b: wr prot swre */ #define UF_SCH 0x0800 /* t: supr cache NI */ #define UF_EXA 0x0400 /* b: exclusive NI */ #define UF_WPD 0x0100 /* b: wr prot data NI */ #define UF_RMV 0x0080 /* d: removable */ #define UF_WBN 0x0040 /* t: write back NI */ #define UF_VSS 0x0020 /* t: supr var speed NI */ #define UF_VSU 0x0010 /* t: var speed unit NI */ #define UF_EWR 0x0008 /* t: enh wr recovery NI */ #define UF_CMW 0x0002 /* cmp writes NI */ #define UF_CMR 0x0001 /* cmp reads NI */ /* Error log flags */ #define LF_SUC 0x0080 /* b: successful */ #define LF_CON 0x0040 /* b: continuing */ #define LF_BBR 0x0020 /* d: bad blk repl NI */ #define LF_RCT 0x0010 /* d: err in repl NI */ #define LF_SNR 0x0001 /* b: seq # reset */ /* Error log formats */ #define FM_CNT 0 /* b: port lf err */ #define FM_BAD 1 /* b: bad host addr */ #define FM_DSK 2 /* d: disk xfer */ #define FM_SDI 3 /* d: SDI err */ #define FM_SDE 4 /* d: sm disk err */ #define FM_TAP 5 /* t: tape errors */ #define FM_RPL 9 /* d: bad blk repl */ /* Status codes */ #define ST_SUC 0 /* b: successful */ #define ST_CMD 1 /* b: invalid cmd */ #define ST_ABO 2 /* b: aborted cmd */ #define ST_OFL 3 /* b: unit offline */ #define ST_AVL 4 /* b: unit avail */ #define ST_MFE 5 /* b: media fmt err */ #define ST_WPR 6 /* b: write prot err */ #define ST_CMP 7 /* b: compare err */ #define ST_DAT 8 /* b: data err */ #define ST_HST 9 /* b: host acc err */ #define ST_CNT 10 /* b: ctrl err */ #define ST_DRV 11 /* b: drive err */ #define ST_FMT 12 /* t: formatter err */ #define ST_BOT 13 /* t: BOT encountered */ #define ST_TMK 14 /* t: tape mark */ #define ST_RDT 16 /* t: record trunc */ #define ST_POL 17 /* t: pos lost */ #define ST_SXC 18 /* b: serious exc */ #define ST_LED 19 /* t: LEOT detect */ #define ST_BBR 20 /* d: bad block */ #define ST_DIA 31 /* b: diagnostic */ #define ST_V_SUB 5 /* subcode */ #define ST_V_INV 8 /* invalid op */ /* Status subcodes */ #define SB_SUC_IGN (1 << ST_V_SUB) /* t: unload ignored */ #define SB_SUC_ON (8 << ST_V_SUB) /* b: already online */ #define SB_SUC_EOT (32 << ST_V_SUB) /* t: EOT encountered */ #define SB_SUC_RO (128 << ST_V_SUB) /* t: read only */ #define SB_OFL_NV (1 << ST_V_SUB) /* b: no volume */ #define SB_OFL_INOP (2 << ST_V_SUB) /* t: inoperative */ #define SB_AVL_INU (32 << ST_V_SUB) /* b: in use */ #define SB_WPR_SW (128 << ST_V_SUB) /* b: swre wlk */ #define SB_WPR_HW (256 << ST_V_SUB) /* b: hwre wlk */ #define SB_HST_OA (1 << ST_V_SUB) /* b: odd addr */ #define SB_HST_OC (2 << ST_V_SUB) /* d: odd count */ #define SB_HST_NXM (3 << ST_V_SUB) /* b: nx memory */ #define SB_HST_PAR (4 << ST_V_SUB) /* b: parity err */ #define SB_HST_PTE (5 << ST_V_SUB) /* b: mapping err */ #define SB_DAT_RDE (7 << ST_V_SUB) /* t: read err */ /* Status invalid command subcodes */ #define I_OPCD (8 << ST_V_INV) /* inv opcode */ #define I_FLAG (9 << ST_V_INV) /* inv flags */ #define I_MODF (10 << ST_V_INV) /* inv modifier */ #define I_BCNT (12 << ST_V_INV) /* inv byte cnt */ #define I_LBN (28 << ST_V_INV) /* inv LBN */ #define I_VRSN (12 << ST_V_INV) /* inv version */ #define I_FMTI (28 << ST_V_INV) /* inv format */ /* Tape format flags */ #define TF_9TK 0x0100 /* 9 track */ #define TF_9TK_NRZ 0x0001 /* 800 bpi */ #define TF_9TK_PE 0x0002 /* 1600 bpi */ #define TF_9TK_GRP 0x0004 /* 6250 bpi */ #define TF_CTP 0x0200 /* TK50 */ #define TF_CTP_LO 0x0001 /* low density */ #define TF_CTP_HI 0x0002 /* hi density */ #define TF_3480 0x0300 /* 3480 */ #define TF_WOD 0x0400 /* RV80 */ /* Packet formats - note that all packet lengths must be multiples of 4 bytes */ /* Command packet header */ #define CMD_REFL 2 /* ref # */ #define CMD_REFH 3 #define CMD_UN 4 /* unit # */ /* 5 /* reserved */ #define CMD_OPC 6 /* opcode */ #define CMD_MOD 7 /* modifier */ #define CMD_OPC_V_OPC 0 /* opcode */ #define CMD_OPC_M_OPC 0xFF #define CMD_OPC_V_CAA 8 /* cache NI */ #define CMD_OPC_M_CAA 0xFF #define CMD_OPC_V_FLG 8 /* flags */ #define CMD_OPC_M_FLG 0xFF /* Response packet header */ #define RSP_LNT 12 #define RSP_REFL 2 /* ref # */ #define RSP_REFH 3 #define RSP_UN 4 /* unit # */ #define RSP_RSV 5 /* reserved */ #define RSP_OPF 6 /* opcd,flg */ #define RSP_STS 7 /* modifiers */ #define RSP_OPF_V_OPC 0 /* opcode */ #define RSP_OPF_V_FLG 8 /* flags */ /* Abort packet - 2 W parameter, 2 W status */ #define ABO_LNT 16 #define ABO_REFL 8 /* ref # */ #define ABO_REFH 9 /* Avail packet - min size */ #define AVL_LNT 12 /* Erase packet - min size */ #define ERS_LNT 12 /* Erase gap - min size */ #define ERG_LNT 12 /* Flush - 10 W status (8 undefined) */ #define FLU_LNT 32 /* 8 - 15 /* reserved */ #define FLU_POSL 16 /* position */ #define FLU_POSH 17 /* Write tape mark - 10W status (8 undefined) */ #define WTM_LNT 32 /* 8 - 15 /* reserved */ #define WTM_POSL 16 /* position */ #define WTM_POSH 17 /* Get command status packet - 2 W parameter, 4 W status */ #define GCS_LNT 20 #define GCS_REFL 8 /* ref # */ #define GCS_REFH 9 #define GCS_STSL 10 /* status */ #define GCS_STSH 11 /* Format packet - 8 W parameters, none returned */ #define FMT_LNT 12 #define FMT_IH 17 /* magic bit */ /* Get unit status packet - 18 W status (disk), 16W status (tape) */ #define GUS_LNT_D 48 #define GUS_LNT_T 44 #define GUS_MLUN 8 /* mlun */ #define GUS_UFL 9 /* flags */ #define GUS_RSVL 10 /* reserved */ #define GUS_RSVH 11 #define GUS_UIDA 12 /* unit ID */ #define GUS_UIDB 13 #define GUS_UIDC 14 #define GUS_UIDD 15 #define GUS_MEDL 16 /* media ID */ #define GUS_MEDH 17 #define GUS_UVER 23 /* unit version */ /* Disk specific status */ #define GUS_SHUN 18 /* shadowing */ #define GUS_SHST 19 #define GUS_TRK 20 /* track */ #define GUS_GRP 21 /* group */ #define GUS_CYL 22 /* cylinder */ #define GUS_RCTS 24 /* RCT size */ #define GUS_RBSC 25 /* RBNs, copies */ /* Tape specific status */ #define GUS_FMT 18 /* format */ #define GUS_SPEED 19 /* speed */ #define GUS_MENU 20 /* menu */ #define GUS_CAP 21 /* capacity */ #define GUS_FVER 22 /* fmtr version */ #define GUS_UIDD_V_MOD 0 /* unit model */ #define GUS_UIDD_V_CLS 8 /* unit class */ #define GUS_RB_V_RBNS 0 /* RBNs/track */ #define GUS_RB_V_RCTC 8 /* RCT copies */ /* Unit online - 2 W parameter, 16 W status (disk or tape) */ #define ONL_LNT 44 #define ONL_MLUN 8 /* mlun */ #define ONL_UFL 9 /* flags */ #define ONL_RSVL 10 /* reserved */ #define ONL_RSVH 11 #define ONL_UIDA 12 /* unit ID */ #define ONL_UIDB 13 #define ONL_UIDC 14 #define ONL_UIDD 15 #define ONL_MEDL 16 /* media ID */ #define ONL_MEDH 17 /* Disk specific status */ #define ONL_SHUN 18 /* shadowing */ #define ONL_SHST 19 #define ONL_SIZL 20 /* size */ #define ONL_SIZH 21 #define ONL_VSNL 22 /* vol ser # */ #define ONL_VSNH 23 /* Tape specific status */ #define ONL_FMT 18 /* format */ #define ONL_SPD 19 /* speed */ #define ONL_MAXL 20 /* max rec size */ #define ONL_MAXH 21 #define ONL_NREC 22 /* noise rec */ #define ONL_RSVE 23 /* reserved */ #define ONL_UIDD_V_MOD 0 /* unit model */ #define ONL_UIDD_V_CLS 8 /* unit class */ /* Set controller characteristics packet - 8 W parameters, 10 W status */ #define SCC_LNT 32 #define SCC_MSV 8 /* MSCP version */ #define SCC_CFL 9 /* flags */ #define SCC_TMO 10 /* timeout */ #define SCC_VER 11 /* ctrl version */ #define SCC_CIDA 12 /* ctrl ID */ #define SCC_CIDB 13 #define SCC_CIDC 14 #define SCC_CIDD 15 #define SCC_MBCL 16 /* max byte count */ #define SCC_MBCH 17 #define SCC_VER_V_SVER 0 /* swre vrsn */ #define SCC_VER_V_HVER 8 /* hwre vrsn */ #define SCC_CIDD_V_MOD 0 /* ctrl model */ #define SCC_CIDD_V_CLS 8 /* ctrl class */ /* Set unit characteristics - 2 W parameter, 16 W status - same as ONL */ #define SUC_LNT 44 /* Reposition - 4 W parameters, 10 W status */ #define POS_LNT 32 #define POS_RCL 8 /* record cnt */ #define POS_RCH 9 #define POS_TMCL 10 /* tape mk cnt */ #define POS_TMCH 11 /* reserved 12 - 15 */ #define POS_POSL 16 /* position */ #define POS_POSH 17 /* Data transfer packet - 10 W parameters (disk), 6W parameters (tape), 10 W status (disk), 12W status (tape) */ #define RW_LNT_D 32 #define RW_LNT_T 36 #define RW_BCL 8 /* byte count */ #define RW_BCH 9 #define RW_BAL 10 /* buff desc */ #define RW_BAH 11 #define RW_MAPL 12 /* map table */ #define RW_MAPH 13 /* 14 /* reserved */ /* 15 /* reserved */ /* Disk specific parameters */ #define RW_LBNL 16 /* LBN */ #define RW_LBNH 17 #define RW_WBCL 18 /* working bc */ #define RW_WBCH 19 #define RW_WBAL 20 /* working ba */ #define RW_WBAH 21 #define RW_WBLL 22 /* working lbn */ #define RW_WBLH 23 /* Tape specific status */ #define RW_POSL 16 /* position */ #define RW_POSH 17 #define RW_RSZL 18 /* record size */ #define RW_RSZH 19 /* Error log packet header */ #define ELP_REFL 2 /* ref # */ #define ELP_REFH 3 #define ELP_UN 4 /* unit */ #define ELP_SEQ 5 #define ELP_FF 6 /* fmt,flg */ #define ELP_EVT 7 /* event */ #define ELP_EV_V_FMT 0 /* format */ #define ELP_EV_V_FLG 8 /* flag */ /* Port last failure error log packet - 6 W status */ #define PLF_LNT 24 /* length */ #define PLF_CIDA 8 /* ctrl ID */ #define PLF_CIDB 9 #define PLF_CIDC 10 #define PLF_CIDD 11 #define PLF_VER 12 /* ctrl version */ #define PLF_ERR 13 /* err */ #define PLF_CIDD_V_MOD 0 /* ctrl model */ #define PLF_CIDD_V_CLS 8 /* ctrl class */ #define PLF_VER_V_SVER 0 /* swre ver */ #define PLF_VER_V_HVER 8 /* hwre ver */ /* Disk transfer error log packet - 18 W status */ #define DTE_LNT 48 #define DTE_CIDA 8 /* ctrl ID */ #define DTE_CIDB 9 #define DTE_CIDC 10 #define DTE_CIDD 11 #define DTE_VER 12 /* version */ #define DTE_MLUN 13 /* mlun */ #define DTE_UIDA 14 /* unit ID */ #define DTE_UIDB 15 #define DTE_UIDC 16 #define DTE_UIDD 17 #define DTE_UVER 18 #define DTE_D2 23 #define DTE_D3 24 #define DTE_D4 25 /* Disk specific status */ #define DTE_SCYL 19 /* cylinder */ #define DTE_VSNL 20 /* vol ser # */ #define DTE_VSNH 21 #define DTE_D1 22 /* dev params */ /* Tape specific status */ #define DTE_RETR 19 /* retry */ #define DTE_POSL 20 /* position */ #define DTE_POSH 21 #define DTE_FVER 22 /* formatter ver */ #define DTE_CIDD_V_MOD 0 /* ctrl model */ #define DTE_CIDD_V_CLS 8 /* ctrl class */ #define DTE_VER_V_SVER 0 /* ctrl swre ver */ #define DTE_VER_V_HVER 8 /* ctrl hwre ver */ #define DTE_UIDD_V_MOD 0 /* unit model */ #define DTE_UIDD_V_CLS 8 /* unit class */ #define DTE_D2_V_SECT 8 #define DTE_D3_V_SURF 0 #define DTE_D3_V_CYL 8 /* Host bus error log packet - 8 W status */ #define HBE_LNT 28 #define HBE_CIDA 8 /* ctrl ID */ #define HBE_CIDB 9 #define HBE_CIDC 10 #define HBE_CIDD 11 #define HBE_VER 12 /* ctrl version */ #define HBE_RSV 13 /* reserved */ #define HBE_BADL 14 /* bad address */ #define HBE_BADH 15 #define HBE_CIDD_V_MOD 0 /* ctrl model */ #define HBE_CIDD_V_CLS 8 /* ctrl class */ #define HBE_VER_V_SVER 0 /* ctrl swre ver */ #define HBE_VER_V_HVER 8 /* ctrl hwre ver */ /* Unit now available attention message - 10 W status, same as first 10 W of status from get unit status */ #define UNA_LNT 32 #endif simh-3.8.1/PDP11/pdp11_io.c0000644000175000017500000003323511111051070013177 0ustar vlmvlm/* pdp11_io.c: PDP-11 I/O simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 19-Nov-08 RMS Moved I/O support routines to I/O library 16-May-08 RMS Added multiple DC11 support Renamed DL11 in autoconfigure 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) 06-Jul-06 RMS Added multiple KL11/DL11 support 15-Oct-05 RMS Fixed bug in autoconfiguration (missing XU) 25-Jul-05 RMS Revised autoconfiguration algorithm and interface 30-Sep-04 RMS Revised Unibus interface 28-May-04 RMS Revised I/O dispatching (from John Dundas) 25-Jan-04 RMS Removed local debug logging support 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner) 12-Mar-03 RMS Added logical name support 08-Oct-02 RMS Trimmed I/O bus addresses Added support for dynamic tables Added show I/O space, autoconfigure routines 12-Sep-02 RMS Added support for TMSCP, KW11P, RX211 26-Jan-02 RMS Revised for multiple DZ's 06-Jan-02 RMS Revised I/O access, enable/disable support 11-Dec-01 RMS Moved interrupt debug code 08-Nov-01 RMS Cloned from cpu sources */ #include "pdp11_defs.h" extern uint16 *M; extern int32 int_req[IPL_HLVL]; extern int32 ub_map[UBM_LNT_LW]; extern int32 cpu_opt, cpu_bme; extern int32 trap_req, ipl; extern int32 cpu_log; extern int32 autcon_enb; extern int32 uba_last; extern FILE *sim_log; extern DEVICE *sim_devices[], cpu_dev; extern t_addr cpu_memsize; int32 calc_ints (int32 nipl, int32 trq); extern t_stat cpu_build_dib (void); extern void init_mbus_tab (void); extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp); /* I/O data structures */ t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); int32 int_vec[IPL_HLVL][32]; /* int req to vector */ int32 (*int_ack[IPL_HLVL][32])(void); /* int ack routines */ static const int32 pirq_bit[7] = { INT_V_PIR1, INT_V_PIR2, INT_V_PIR3, INT_V_PIR4, INT_V_PIR5, INT_V_PIR6, INT_V_PIR7 }; /* I/O page lookup and linkage routines Inputs: *data = pointer to data to read, if READ data = data to store, if WRITE or WRITEB pa = address access = READ, WRITE, or WRITEB Outputs: status = SCPE_OK or SCPE_NXM */ t_stat iopageR (int32 *data, uint32 pa, int32 access) { int32 idx; t_stat stat; idx = (pa & IOPAGEMASK) >> 1; if (iodispR[idx]) { stat = iodispR[idx] (data, pa, access); trap_req = calc_ints (ipl, trap_req); return stat; } return SCPE_NXM; } t_stat iopageW (int32 data, uint32 pa, int32 access) { int32 idx; t_stat stat; idx = (pa & IOPAGEMASK) >> 1; if (iodispW[idx]) { stat = iodispW[idx] (data, pa, access); trap_req = calc_ints (ipl, trap_req); return stat; } return SCPE_NXM; } /* Calculate interrupt outstanding */ int32 calc_ints (int32 nipl, int32 trq) { int32 i; for (i = IPL_HLVL - 1; i > nipl; i--) { if (int_req[i]) return (trq | TRAP_INT); } return (trq & ~TRAP_INT); } /* Find vector for highest priority interrupt */ int32 get_vector (int32 nipl) { int32 i, j, t, vec; for (i = IPL_HLVL - 1; i > nipl; i--) { /* loop thru lvls */ t = int_req[i]; /* get level */ for (j = 0; t && (j < 32); j++) { /* srch level */ if ((t >> j) & 1) { /* irq found? */ int_req[i] = int_req[i] & ~(1u << j); /* clr irq */ if (int_ack[i][j]) vec = int_ack[i][j](); else vec = int_vec[i][j]; return vec; /* return vector */ } /* end if t */ } /* end for j */ } /* end for i */ return 0; } /* Read and write Unibus map registers In any even/odd pair even = low 16b, bit <0> clear odd = high 6b The Unibus map is stored as an array of longwords. These routines are only reachable if a Unibus map is configured. */ t_stat ubm_rd (int32 *data, int32 addr, int32 access) { int32 pg = (addr >> 2) & UBM_M_PN; *data = (addr & 2)? ((ub_map[pg] >> 16) & 077): (ub_map[pg] & 0177776); return SCPE_OK; } t_stat ubm_wr (int32 data, int32 addr, int32 access) { int32 sc, pg = (addr >> 2) & UBM_M_PN; if (access == WRITEB) { sc = (addr & 3) << 3; ub_map[pg] = (ub_map[pg] & ~(0377 << sc)) | ((data & 0377) << sc); } else { sc = (addr & 2) << 3; ub_map[pg] = (ub_map[pg] & ~(0177777 << sc)) | ((data & 0177777) << sc); } ub_map[pg] = ub_map[pg] & 017777776; return SCPE_OK; } /* Mapped memory access routines for DMA devices */ #define BUSMASK ((UNIBUS)? UNIMASK: PAMASK) /* Map I/O address to memory address - caller checks cpu_bme */ uint32 Map_Addr (uint32 ba) { int32 pg = UBM_GETPN (ba); /* map entry */ int32 off = UBM_GETOFF (ba); /* offset */ if (pg != UBM_M_PN) /* last page? */ uba_last = (ub_map[pg] + off) & PAMASK; /* no, use map */ else uba_last = (IOPAGEBASE + off) & PAMASK; /* yes, use fixed */ return uba_last; } /* I/O buffer routines, aligned access Map_ReadB - fetch byte buffer from memory Map_ReadW - fetch word buffer from memory Map_WriteB - store byte buffer into memory Map_WriteW - store word buffer into memory These routines are used only for Unibus and Qbus devices. Massbus devices have their own IO routines. As a result, the historic 'map' parameter is no longer needed. - In a U18 configuration, the map is always disabled. Device addresses are trimmed to 18b. - In a U22 configuration, the map is always configured (although it may be disabled). Device addresses are trimmed to 18b. - In a Qbus configuration, the map is always disabled. Device addresses are trimmed to 22b. */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) { uint32 alim, lim, ma; ba = ba & BUSMASK; /* trim address */ lim = ba + bc; if (cpu_bme) { /* map enabled? */ for ( ; ba < lim; ba++) { /* by bytes */ ma = Map_Addr (ba); /* map addr */ if (!ADDR_IS_MEM (ma)) /* NXM? err */ return (lim - ba); if (ma & 1) /* get byte */ *buf++ = (M[ma >> 1] >> 8) & 0377; else *buf++ = M[ma >> 1] & 0377; } return 0; } else { /* physical */ if (ADDR_IS_MEM (lim)) /* end ok? */ alim = lim; else if (ADDR_IS_MEM (ba)) /* no, strt ok? */ alim = cpu_memsize; else return bc; /* no, err */ for ( ; ba < alim; ba++) { /* by bytes */ if (ba & 1) *buf++ = (M[ba >> 1] >> 8) & 0377; /* get byte */ else *buf++ = M[ba >> 1] & 0377; } return (lim - alim); } } int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) { uint32 alim, lim, ma; ba = (ba & BUSMASK) & ~01; /* trim, align addr */ lim = ba + (bc & ~01); if (cpu_bme) { /* map enabled? */ for (; ba < lim; ba = ba + 2) { /* by words */ ma = Map_Addr (ba); /* map addr */ if (!ADDR_IS_MEM (ma)) /* NXM? err */ return (lim - ba); *buf++ = M[ma >> 1]; } return 0; } else { /* physical */ if (ADDR_IS_MEM (lim)) /* end ok? */ alim = lim; else if (ADDR_IS_MEM (ba)) /* no, strt ok? */ alim = cpu_memsize; else return bc; /* no, err */ for ( ; ba < alim; ba = ba + 2) { /* by words */ *buf++ = M[ba >> 1]; } return (lim - alim); } } int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) { uint32 alim, lim, ma; ba = ba & BUSMASK; /* trim address */ lim = ba + bc; if (cpu_bme) { /* map enabled? */ for ( ; ba < lim; ba++) { /* by bytes */ ma = Map_Addr (ba); /* map addr */ if (!ADDR_IS_MEM (ma)) /* NXM? err */ return (lim - ba); if (ma & 1) M[ma >> 1] = (M[ma >> 1] & 0377) | ((uint16) *buf++ << 8); else M[ma >> 1] = (M[ma >> 1] & ~0377) | *buf++; } return 0; } else { /* physical */ if (ADDR_IS_MEM (lim)) /* end ok? */ alim = lim; else if (ADDR_IS_MEM (ba)) /* no, strt ok? */ alim = cpu_memsize; else return bc; /* no, err */ for ( ; ba < alim; ba++) { /* by bytes */ if (ba & 1) M[ba >> 1] = (M[ba >> 1] & 0377) | ((uint16) *buf++ << 8); else M[ba >> 1] = (M[ba >> 1] & ~0377) | *buf++; } return (lim - alim); } } int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) { uint32 alim, lim, ma; ba = (ba & BUSMASK) & ~01; /* trim, align addr */ lim = ba + (bc & ~01); if (cpu_bme) { /* map enabled? */ for (; ba < lim; ba = ba + 2) { /* by words */ ma = Map_Addr (ba); /* map addr */ if (!ADDR_IS_MEM (ma)) /* NXM? err */ return (lim - ba); M[ma >> 1] = *buf++; } return 0; } else { /* physical */ if (ADDR_IS_MEM (lim)) /* end ok? */ alim = lim; else if (ADDR_IS_MEM (ba)) /* no, strt ok? */ alim = cpu_memsize; else return bc; /* no, err */ for ( ; ba < alim; ba = ba + 2) { /* by words */ M[ba >> 1] = *buf++; } return (lim - alim); } } /* Build tables from device list */ t_stat build_dib_tab (void) { int32 i; DEVICE *dptr; DIB *dibp; t_stat r; init_ubus_tab (); /* init Unibus tables */ init_mbus_tab (); /* init Massbus tables */ for (i = 0; i < 7; i++) /* seed PIRQ intr */ int_vec[i + 1][pirq_bit[i]] = VEC_PIRQ; if (r = cpu_build_dib ()) /* build CPU entries */ return r; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ if (dptr->flags & DEV_MBUS) { /* Massbus? */ if (r = build_mbus_tab (dptr, dibp)) /* add to Mbus tab */ return r; } else { /* no, Unibus */ if (r = build_ubus_tab (dptr, dibp)) /* add to Unibus tab */ return r; } } /* end if enabled */ } /* end for */ return SCPE_OK; } simh-3.8.1/PDP11/pdp11_dz.c0000644000175000017500000007051011126242614013216 0ustar vlmvlm/* pdp11_dz.c: DZ11 terminal multiplexor simulator Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dz DZ11 terminal multiplexor 29-Dec-08 RMS Added MTAB_NC to SET LOG command (found by Walter Mueller) 19-Nov-08 RMS Revised for common TMXR show routines 18-Jun-07 RMS Added UNIT_IDLE flag 29-Oct-06 RMS Synced poll and clock 22-Nov-05 RMS Revised for new terminal processing routines 07-Jul-05 RMS Removed extraneous externs 15-Jun-05 RMS Revised for new autoconfigure interface 04-Apr-04 RMS Added per-line logging 05-Jan-04 RMS Revised for tmxr library changes 19-May-03 RMS Revised for new conditional compilation scheme 09-May-03 RMS Added network device flag 22-Dec-02 RMS Added break (framing error) support 31-Oct-02 RMS Added 8b support 12-Oct-02 RMS Added autoconfigure support 29-Sep-02 RMS Fixed bug in set number of lines routine Added variable vector support New data structures 22-Apr-02 RMS Updated for changes in sim_tmxr 28-Apr-02 RMS Fixed interrupt acknowledge, fixed SHOW DZ ADDRESS 14-Jan-02 RMS Added multiboard support 30-Dec-01 RMS Added show statistics, set disconnect Removed statistics registers 03-Dec-01 RMS Modified for extended SET/SHOW 09-Nov-01 RMS Added VAX support 20-Oct-01 RMS Moved getchar from sim_tmxr, changed interrupt logic to use tmxr_rqln 06-Oct-01 RMS Fixed bug in carrier detect logic 03-Oct-01 RMS Added support for BSD-style "ringless" modems 27-Sep-01 RMS Fixed bug in xmte initialization 17-Sep-01 RMS Added separate autodisconnect switch 16-Sep-01 RMS Fixed modem control bit offsets */ #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" #define RANK_DZ 0 /* no autoconfig */ #define DZ_8B_DFLT 0 extern int32 int_req; #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define DZ_8B_DFLT TT_MODE_8B extern int32 int_req[IPL_HLVL]; #else /* PDP-11 version */ #include "pdp11_defs.h" #define DZ_8B_DFLT TT_MODE_8B extern int32 int_req[IPL_HLVL]; #endif #include "sim_sock.h" #include "sim_tmxr.h" #if !defined (DZ_MUXES) #define DZ_MUXES 1 #endif #if !defined (DZ_LINES) #define DZ_LINES 8 #endif #define DZ_MNOMASK (DZ_MUXES - 1) /* mask for mux no */ #define DZ_LNOMASK (DZ_LINES - 1) /* mask for lineno */ #define DZ_LMASK ((1 << DZ_LINES) - 1) /* mask of lines */ #define DZ_SILO_ALM 16 /* silo alarm level */ /* DZCSR - 160100 - control/status register */ #define CSR_MAINT 0000010 /* maint - NI */ #define CSR_CLR 0000020 /* clear */ #define CSR_MSE 0000040 /* master scan enb */ #define CSR_RIE 0000100 /* rcv int enb */ #define CSR_RDONE 0000200 /* rcv done - RO */ #define CSR_V_TLINE 8 /* xmit line - RO */ #define CSR_TLINE (DZ_LNOMASK << CSR_V_TLINE) #define CSR_SAE 0010000 /* silo alm enb */ #define CSR_SA 0020000 /* silo alm - RO */ #define CSR_TIE 0040000 /* xmit int enb */ #define CSR_TRDY 0100000 /* xmit rdy - RO */ #define CSR_RW (CSR_MSE | CSR_RIE | CSR_SAE | CSR_TIE) #define CSR_MBZ (0004003 | CSR_CLR | CSR_MAINT) #define CSR_GETTL(x) (((x) >> CSR_V_TLINE) & DZ_LNOMASK) #define CSR_PUTTL(x,y) x = ((x) & ~CSR_TLINE) | (((y) & DZ_LNOMASK) << CSR_V_TLINE) /* DZRBUF - 160102 - receive buffer, read only */ #define RBUF_CHAR 0000377 /* rcv char */ #define RBUF_V_RLINE 8 /* rcv line */ #define RBUF_PARE 0010000 /* parity err - NI */ #define RBUF_FRME 0020000 /* frame err */ #define RBUF_OVRE 0040000 /* overrun err - NI */ #define RBUF_VALID 0100000 /* rcv valid */ #define RBUF_MBZ 0004000 /* DZLPR - 160102 - line parameter register, write only, word access only */ #define LPR_V_LINE 0 /* line */ #define LPR_LPAR 0007770 /* line pars - NI */ #define LPR_RCVE 0010000 /* receive enb */ #define LPR_GETLN(x) (((x) >> LPR_V_LINE) & DZ_LNOMASK) /* DZTCR - 160104 - transmission control register */ #define TCR_V_XMTE 0 /* xmit enables */ #define TCR_V_DTR 8 /* DTRs */ /* DZMSR - 160106 - modem status register, read only */ #define MSR_V_RI 0 /* ring indicators */ #define MSR_V_CD 8 /* carrier detect */ /* DZTDR - 160106 - transmit data, write only */ #define TDR_CHAR 0000377 /* xmit char */ #define TDR_V_TBR 8 /* xmit break - NI */ extern int32 IREQ (HLVL); extern int32 sim_switches; extern FILE *sim_log; extern int32 tmxr_poll; /* calibrated delay */ uint16 dz_csr[DZ_MUXES] = { 0 }; /* csr */ uint16 dz_rbuf[DZ_MUXES] = { 0 }; /* rcv buffer */ uint16 dz_lpr[DZ_MUXES] = { 0 }; /* line param */ uint16 dz_tcr[DZ_MUXES] = { 0 }; /* xmit control */ uint16 dz_msr[DZ_MUXES] = { 0 }; /* modem status */ uint16 dz_tdr[DZ_MUXES] = { 0 }; /* xmit data */ uint8 dz_sae[DZ_MUXES] = { 0 }; /* silo alarm enabled */ uint32 dz_rxi = 0; /* rcv interrupts */ uint32 dz_txi = 0; /* xmt interrupts */ int32 dz_mctl = 0; /* modem ctrl enabled */ int32 dz_auto = 0; /* autodiscon enabled */ TMLN dz_ldsc[DZ_MUXES * DZ_LINES] = { 0 }; /* line descriptors */ TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */ DEVICE dz_dev; t_stat dz_rd (int32 *data, int32 PA, int32 access); t_stat dz_wr (int32 data, int32 PA, int32 access); int32 dz_rxinta (void); int32 dz_txinta (void); t_stat dz_svc (UNIT *uptr); t_stat dz_reset (DEVICE *dptr); t_stat dz_attach (UNIT *uptr, char *cptr); t_stat dz_detach (UNIT *uptr); t_stat dz_clear (int32 dz, t_bool flag); int32 dz_getc (int32 dz); void dz_update_rcvi (void); void dz_update_xmti (void); void dz_clr_rxint (int32 dz); void dz_set_rxint (int32 dz); void dz_clr_txint (int32 dz); void dz_set_txint (int32 dz); t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, void *desc); /* DZ data structures dz_dev DZ device descriptor dz_unit DZ unit list dz_reg DZ register list */ DIB dz_dib = { IOBA_DZ, IOLN_DZ * DZ_MUXES, &dz_rd, &dz_wr, 2, IVCL (DZRX), VEC_DZRX, { &dz_rxinta, &dz_txinta } }; UNIT dz_unit = { UDATA (&dz_svc, UNIT_IDLE|UNIT_ATTABLE|DZ_8B_DFLT, 0) }; REG dz_reg[] = { { BRDATA (CSR, dz_csr, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (RBUF, dz_rbuf, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (LPR, dz_lpr, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (TCR, dz_tcr, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (MSR, dz_msr, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (TDR, dz_tdr, DEV_RDX, 16, DZ_MUXES) }, { BRDATA (SAENB, dz_sae, DEV_RDX, 1, DZ_MUXES) }, { GRDATA (RXINT, dz_rxi, DEV_RDX, DZ_MUXES, 0) }, { GRDATA (TXINT, dz_txi, DEV_RDX, DZ_MUXES, 0) }, { FLDATA (MDMCTL, dz_mctl, 0) }, { FLDATA (AUTODS, dz_auto, 0) }, { GRDATA (DEVADDR, dz_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, dz_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB dz_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dz_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &dz_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &dz_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &dz_desc }, { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, DZ_LINES, "VECTOR", "VECTOR", &set_vec, &show_vec_mux, (void *) &dz_desc }, #if !defined (VM_PDP10) { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", &set_addr_flt, NULL, NULL }, #endif { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &dz_setnl, &tmxr_show_lines, (void *) &dz_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "LOG", &dz_set_log, NULL, &dz_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", &dz_set_nolog, NULL, &dz_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LOG", NULL, NULL, &dz_show_log, &dz_desc }, { 0 } }; DEVICE dz_dev = { "DZ", &dz_unit, dz_reg, dz_mod, 1, DEV_RDX, 8, 1, DEV_RDX, 8, &tmxr_ex, &tmxr_dep, &dz_reset, NULL, &dz_attach, &dz_detach, &dz_dib, DEV_FLTA | DEV_DISABLE | DEV_NET | DEV_UBUS | DEV_QBUS }; /* IO dispatch routines, I/O addresses 177601x0 - 177601x7 */ t_stat dz_rd (int32 *data, int32 PA, int32 access) { int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ switch ((PA >> 1) & 03) { /* case on PA<2:1> */ case 00: /* CSR */ *data = dz_csr[dz] = dz_csr[dz] & ~CSR_MBZ; break; case 01: /* RBUF */ dz_csr[dz] = dz_csr[dz] & ~CSR_SA; /* clr silo alarm */ if (dz_csr[dz] & CSR_MSE) { /* scanner on? */ dz_rbuf[dz] = dz_getc (dz); /* get top of silo */ if (!dz_rbuf[dz]) /* empty? re-enable */ dz_sae[dz] = 1; tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* update rx intr */ } else { dz_rbuf[dz] = 0; /* no data */ dz_update_rcvi (); /* no rx intr */ } *data = dz_rbuf[dz]; break; case 02: /* TCR */ *data = dz_tcr[dz]; break; case 03: /* MSR */ *data = dz_msr[dz]; break; } return SCPE_OK; } t_stat dz_wr (int32 data, int32 PA, int32 access) { int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ int32 i, c, line; TMLN *lp; switch ((PA >> 1) & 03) { /* case on PA<2:1> */ case 00: /* CSR */ if (access == WRITEB) data = (PA & 1)? /* byte? merge */ (dz_csr[dz] & 0377) | (data << 8): (dz_csr[dz] & ~0377) | data; if (data & CSR_CLR) /* clr? reset */ dz_clear (dz, FALSE); if (data & CSR_MSE) /* MSE? start poll */ sim_activate (&dz_unit, clk_cosched (tmxr_poll)); else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); if ((data & CSR_RIE) == 0) /* RIE = 0? */ dz_clr_rxint (dz); else if (((dz_csr[dz] & CSR_IE) == 0) && /* RIE 0->1? */ ((dz_csr[dz] & CSR_SAE)? (dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE))) dz_set_rxint (dz); if ((data & CSR_TIE) == 0) /* TIE = 0? */ dz_clr_txint (dz); else if (((dz_csr[dz] & CSR_TIE) == 0) && (dz_csr[dz] & CSR_TRDY)) dz_set_txint (dz); dz_csr[dz] = (dz_csr[dz] & ~CSR_RW) | (data & CSR_RW); break; case 01: /* LPR */ dz_lpr[dz] = data; line = (dz * DZ_LINES) + LPR_GETLN (data); /* get line num */ lp = &dz_ldsc[line]; /* get line desc */ if (dz_lpr[dz] & LPR_RCVE) /* rcv enb? on */ lp->rcve = 1; else lp->rcve = 0; /* else line off */ tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* update rx intr */ break; case 02: /* TCR */ if (access == WRITEB) data = (PA & 1)? /* byte? merge */ (dz_tcr[dz] & 0377) | (data << 8): (dz_tcr[dz] & ~0377) | data; if (dz_mctl) { /* modem ctl? */ dz_msr[dz] |= ((data & 0177400) & /* dcd |= dtr & ring */ ((dz_msr[dz] & DZ_LMASK) << MSR_V_CD)); dz_msr[dz] &= ~(data >> TCR_V_DTR); /* ring &= ~dtr */ if (dz_auto) { /* auto disconnect? */ int32 drop; drop = (dz_tcr[dz] & ~data) >> TCR_V_DTR; /* drop = dtr & ~data */ for (i = 0; i < DZ_LINES; i++) { /* drop hangups */ line = (dz * DZ_LINES) + i; /* get line num */ lp = &dz_ldsc[line]; /* get line desc */ if (lp->conn && (drop & (1 << i))) { tmxr_linemsg (lp, "\r\nLine hangup\r\n"); tmxr_reset_ln (lp); /* reset line, cdet */ dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); } /* end if drop */ } /* end for */ } /* end if auto */ } /* end if modem */ dz_tcr[dz] = data; tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* update int */ break; case 03: /* TDR */ if (PA & 1) { /* odd byte? */ dz_tdr[dz] = (dz_tdr[dz] & 0377) | (data << 8); /* just save */ break; } dz_tdr[dz] = data; if (dz_csr[dz] & CSR_MSE) { /* enabled? */ line = (dz * DZ_LINES) + CSR_GETTL (dz_csr[dz]); lp = &dz_ldsc[line]; /* get line desc */ c = sim_tt_outcvt (dz_tdr[dz], TT_GET_MODE (dz_unit.flags)); if (c >= 0) /* store char */ tmxr_putc_ln (lp, c); tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* update int */ } break; } return SCPE_OK; } /* Unit service routine The DZ11 polls to see if asynchronous activity has occurred and now needs to be processed. The polling interval is controlled by the clock simulator, so for most environments, it is calibrated to real time. Typical polling intervals are 50-60 times per second. The simulator assumes that software enables all of the multiplexors, or none of them. */ t_stat dz_svc (UNIT *uptr) { int32 dz, t, newln; for (dz = t = 0; dz < DZ_MUXES; dz++) /* check enabled */ t = t | (dz_csr[dz] & CSR_MSE); if (t) { /* any enabled? */ newln = tmxr_poll_conn (&dz_desc); /* poll connect */ if ((newln >= 0) && dz_mctl) { /* got a live one? */ dz = newln / DZ_LINES; /* get mux num */ if (dz_tcr[dz] & (1 << (newln + TCR_V_DTR))) /* DTR set? */ dz_msr[dz] |= (1 << (newln + MSR_V_CD)); /* set cdet */ else dz_msr[dz] |= (1 << newln); /* set ring */ } tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* upd rcv intr */ tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* upd xmt intr */ sim_activate (uptr, tmxr_poll); /* reactivate */ } return SCPE_OK; } /* Get first available character for mux, if any */ int32 dz_getc (int32 dz) { uint32 i, line, c; for (i = c = 0; (i < DZ_LINES) && (c == 0); i++) { /* loop thru lines */ line = (dz * DZ_LINES) + i; /* get line num */ c = tmxr_getc_ln (&dz_ldsc[line]); /* test for input */ if (c & SCPE_BREAK) /* break? frame err */ c = RBUF_VALID | RBUF_FRME; if (c) /* or in line # */ c = c | (i << RBUF_V_RLINE); } /* end for */ return c; } /* Update receive interrupts */ void dz_update_rcvi (void) { int32 i, dz, line, scnt[DZ_MUXES]; TMLN *lp; for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ scnt[dz] = 0; /* clr input count */ for (i = 0; i < DZ_LINES; i++) { /* poll lines */ line = (dz * DZ_LINES) + i; /* get line num */ lp = &dz_ldsc[line]; /* get line desc */ scnt[dz] = scnt[dz] + tmxr_rqln (lp); /* sum buffers */ if (dz_mctl && !lp->conn) /* if disconn */ dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); /* reset car det */ } } for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ if (scnt[dz] && (dz_csr[dz] & CSR_MSE)) { /* input & enabled? */ dz_csr[dz] |= CSR_RDONE; /* set done */ if (dz_sae[dz] && (scnt[dz] >= DZ_SILO_ALM)) { /* alm enb & cnt hi? */ dz_csr[dz] |= CSR_SA; /* set status */ dz_sae[dz] = 0; /* disable alarm */ } } else dz_csr[dz] &= ~CSR_RDONE; /* no, clear done */ if ((dz_csr[dz] & CSR_RIE) && /* int enable */ ((dz_csr[dz] & CSR_SAE)? (dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE))) dz_set_rxint (dz); /* and alm/done? */ else dz_clr_rxint (dz); /* no, clear int */ } return; } /* Update transmit interrupts */ void dz_update_xmti (void) { int32 dz, linemask, i, j, line; for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ linemask = dz_tcr[dz] & DZ_LMASK; /* enabled lines */ dz_csr[dz] &= ~CSR_TRDY; /* assume not rdy */ j = CSR_GETTL (dz_csr[dz]); /* start at current */ for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ j = (j + 1) & DZ_LNOMASK; /* next line */ line = (dz * DZ_LINES) + j; /* get line num */ if ((linemask & (1 << j)) && dz_ldsc[line].xmte) { CSR_PUTTL (dz_csr[dz], j); /* put ln in csr */ dz_csr[dz] |= CSR_TRDY; /* set xmt rdy */ break; } } if ((dz_csr[dz] & CSR_TIE) && (dz_csr[dz] & CSR_TRDY)) /* ready plus int? */ dz_set_txint (dz); else dz_clr_txint (dz); /* no int req */ } return; } /* Interrupt routines */ void dz_clr_rxint (int32 dz) { dz_rxi = dz_rxi & ~(1 << dz); /* clr mux rcv int */ if (dz_rxi == 0) /* all clr? */ CLR_INT (DZRX); else SET_INT (DZRX); /* no, set intr */ return; } void dz_set_rxint (int32 dz) { dz_rxi = dz_rxi | (1 << dz); /* set mux rcv int */ SET_INT (DZRX); /* set master intr */ return; } int32 dz_rxinta (void) { int32 dz; for (dz = 0; dz < DZ_MUXES; dz++) { /* find 1st mux */ if (dz_rxi & (1 << dz)) { dz_clr_rxint (dz); /* clear intr */ return (dz_dib.vec + (dz * 010)); /* return vector */ } } return 0; } void dz_clr_txint (int32 dz) { dz_txi = dz_txi & ~(1 << dz); /* clr mux xmt int */ if (dz_txi == 0) /* all clr? */ CLR_INT (DZTX); else SET_INT (DZTX); /* no, set intr */ return; } void dz_set_txint (int32 dz) { dz_txi = dz_txi | (1 << dz); /* set mux xmt int */ SET_INT (DZTX); /* set master intr */ return; } int32 dz_txinta (void) { int32 dz; for (dz = 0; dz < DZ_MUXES; dz++) { /* find 1st mux */ if (dz_txi & (1 << dz)) { dz_clr_txint (dz); /* clear intr */ return (dz_dib.vec + 4 + (dz * 010)); /* return vector */ } } return 0; } /* Device reset */ t_stat dz_clear (int32 dz, t_bool flag) { int32 i, line; dz_csr[dz] = 0; /* clear CSR */ dz_rbuf[dz] = 0; /* silo empty */ dz_lpr[dz] = 0; /* no params */ if (flag) /* INIT? clr all */ dz_tcr[dz] = 0; else dz_tcr[dz] &= ~0377; /* else save dtr */ dz_tdr[dz] = 0; dz_sae[dz] = 1; /* alarm on */ dz_clr_rxint (dz); /* clear int */ dz_clr_txint (dz); for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ line = (dz * DZ_LINES) + i; if (!dz_ldsc[line].conn) /* set xmt enb */ dz_ldsc[line].xmte = 1; dz_ldsc[line].rcve = 0; /* clr rcv enb */ } return SCPE_OK; } t_stat dz_reset (DEVICE *dptr) { int32 i, ndev; for (i = 0; i < DZ_MUXES; i++) /* init muxes */ dz_clear (i, TRUE); dz_rxi = dz_txi = 0; /* clr master int */ CLR_INT (DZRX); CLR_INT (DZTX); sim_cancel (&dz_unit); /* stop poll */ ndev = ((dptr->flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); return auto_config (dptr->name, ndev); /* auto config */ } /* Attach */ t_stat dz_attach (UNIT *uptr, char *cptr) { t_stat r; extern int32 sim_switches; dz_mctl = dz_auto = 0; /* modem ctl off */ r = tmxr_attach (&dz_desc, uptr, cptr); /* attach mux */ if (r != SCPE_OK) /* error? */ return r; if (sim_switches & SWMASK ('M')) { /* modem control? */ dz_mctl = 1; printf ("Modem control activated\n"); if (sim_log) fprintf (sim_log, "Modem control activated\n"); if (sim_switches & SWMASK ('A')) { /* autodisconnect? */ dz_auto = 1; printf ("Auto disconnect activated\n"); if (sim_log) fprintf (sim_log, "Auto disconnect activated\n"); } } return SCPE_OK; } /* Detach */ t_stat dz_detach (UNIT *uptr) { return tmxr_detach (&dz_desc, uptr); } /* SET LINES processor */ t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t, ndev; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); if ((r != SCPE_OK) || (newln == dz_desc.lines)) return r; if ((newln == 0) || (newln % DZ_LINES)) return SCPE_ARG; if (newln < dz_desc.lines) { for (i = newln, t = 0; i < dz_desc.lines; i++) t = t | dz_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < dz_desc.lines; i++) { if (dz_ldsc[i].conn) { tmxr_linemsg (&dz_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&dz_ldsc[i]); /* reset line */ } if ((i % DZ_LINES) == (DZ_LINES - 1)) dz_clear (i / DZ_LINES, TRUE); /* reset mux */ } } dz_dib.lnt = (newln / DZ_LINES) * IOLN_DZ; /* set length */ dz_desc.lines = newln; ndev = ((dz_dev.flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); return auto_config (dz_dev.name, ndev); /* auto config */ } /* SET LOG processor */ t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) { char *tptr; t_stat r; int32 ln; if (cptr == NULL) return SCPE_ARG; tptr = strchr (cptr, '='); if ((tptr == NULL) || (*tptr == 0)) return SCPE_ARG; *tptr++ = 0; ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; return tmxr_set_log (NULL, ln, tptr, desc); } /* SET NOLOG processor */ t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) { t_stat r; int32 ln; if (cptr == NULL) return SCPE_ARG; ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; return tmxr_set_nolog (NULL, ln, NULL, desc); } /* SHOW LOG processor */ t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) { int32 i; for (i = 0; i < dz_desc.lines; i++) { fprintf (st, "line %d: ", i); tmxr_show_log (st, NULL, i, desc); fprintf (st, "\n"); } return SCPE_OK; } simh-3.8.1/PDP11/pdp11_hk.c0000644000175000017500000016175011112123172013202 0ustar vlmvlm/* pdp11_hk.c - RK611/RK06/RK07 disk controller Copyright (c) 1993-2008, 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 PUHKOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. hk RK611/RK06/RK07 disk 29-Apr-07 RMS NOP and DCLR (at least) do not check drive type MR2 and MR3 only updated on NOP 17-Nov-05 RMS Removed unused variable 13-Nov-05 RMS Fixed overlapped seek interaction with NOP, DCLR, PACK 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 18-Mar-05 RMS Added attached test to detach routine 03-Oct-04 RMS Revised Unibus interface RMS Fixed state of output ready for M+ 26-Mar-04 RMS Fixed warnings with -std=c99 25-Jan-04 RMS Revised for device debug support 04-Jan-04 RMS Changed sim_fsize calling sequence 29-Dec-03 RMS Added 18b Qbus support 25-Apr-03 RMS Revised for extended file support This is a somewhat abstracted implementation of the RK611, more closely modelled on third party clones than DEC's own implementation. In particular, the drive-to-controller serial communications system is simulated only at a level equal to the Emulex SC21. The RK611 functions only in 18b Unibus systems with I/O maps. The Emulex SC02/C was a Qbus work-alike with a unique extension to 22b addressing. It was only supported in Ultrix-11 and other third party software. This module includes ideas from a previous implementation by Fred Van Kempen. */ #if defined (VM_PDP10) /* PDP10 version */ #error "RK611 is not supported on the PDP-10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #else /* PDP-11 version */ #include "pdp11_defs.h" extern int32 cpu_opt; #endif extern uint16 *M; #define HK_NUMDR 8 /* #drives */ #define HK_NUMCY6 411 /* cyl/drive */ #define HK_NUMCY7 815 /* cyl/drive */ #define HK_NUMSF 3 /* tracks/cyl */ #define HK_NUMSC 22 /* sectors/track */ #define HK_NUMWD 256 /* words/sector */ #define RK06_SIZE (HK_NUMCY6*HK_NUMSF*HK_NUMSC*HK_NUMWD) #define RK07_SIZE (HK_NUMCY7*HK_NUMSF*HK_NUMSC*HK_NUMWD) #define HK_SIZE(x) (((x)->flags & UNIT_DTYPE)? RK07_SIZE: RK06_SIZE) #define HK_CYL(x) (((x)->flags & UNIT_DTYPE)? HK_NUMCY7: HK_NUMCY6) #define HK_MAXFR (1 << 16) /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ #define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_DTYPE (1 << UNIT_V_DTYPE) #define UNIT_RK06 (0 << UNIT_V_DTYPE) #define UNIT_RK07 (1 << UNIT_V_DTYPE) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_DUMMY (1 << UNIT_V_DUMMY) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ /* Parameters in the unit descriptor */ #define CYL u3 /* current cylinder */ #define FNC u4 /* function */ /* HKCS1 - 177440 - control/status 1 */ #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 017 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) #define FNC_NOP 000 /* no operation */ #define FNC_PACK 001 /* pack acknowledge */ #define FNC_DCLR 002 /* drive clear */ #define FNC_UNLOAD 003 /* unload */ #define FNC_START 004 /* start */ #define FNC_RECAL 005 /* recalibrate */ #define FNC_OFFSET 006 /* offset */ #define FNC_SEEK 007 /* seek */ #define FNC_XFER 010 #define FNC_READ 010 /* read */ #define FNC_WRITE 011 /* write */ #define FNC_WRITEH 013 /* write w/ headers */ #define FNC_READH 012 /* read w/ headers */ #define FNC_WCHK 014 /* write check */ #define FNC_2ND 020 /* 2nd state flag */ #define CS1_SPA 0000040 /* spare */ #define CS1_IE CSR_IE /* int enable */ #define CS1_DONE CSR_DONE /* ready */ #define CS1_V_UAE 8 /* Unibus addr ext */ #define CS1_M_UAE 03 #define CS1_UAE (CS1_M_UAE << CS1_V_UAE) #define CS1_DT 0002000 /* drive type */ #define CS1_CTO 0004000 /* ctrl timeout NI */ #define CS1_FMT 0010000 /* 16b/18b NI */ #define CS1_PAR 0020000 /* par err NI */ #define CS1_DI 0040000 /* drive intr */ #define CS1_ERR 0100000 /* error */ #define CS1_CCLR 0100000 /* ctrl clear */ #define CS1_RW (CS1_DT|CS1_UAE|CS1_IE|CS1_SPA|CS1_FNC) #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) #define GET_UAE(x) (((x) >> CS1_V_UAE) & CS1_M_UAE) #define PUT_UAE(x,n) (((x) & ~ CS1_UAE) | (((n) << CS1_V_UAE) & CS1_UAE)) /* HKWC - 177442 - word count */ /* HKBA - 177444 - base address */ #define BA_MBZ 0000001 /* must be zero */ /* HKDA - 177446 - sector/track */ #define DA_V_SC 0 /* sector pos */ #define DA_M_SC 037 /* sector mask */ #define DA_V_SF 8 /* track pos */ #define DA_M_SF 007 /* track mask */ #define DA_MBZ 0174340 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) /* HKCS2 - 177450 - control/status 2 */ #define CS2_V_UNIT 0 /* unit pos */ #define CS2_M_UNIT 07 /* unit mask */ #define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) #define CS2_RLS 0000010 /* release NI */ #define CS2_UAI 0000020 /* addr inhibit */ #define CS2_CLR 0000040 /* controller clear */ #define CS2_IR 0000100 /* input ready */ #define CS2_OR 0000200 /* output ready */ #define CS2_UFE 0000400 /* unit field err NI */ #define CS2_MDS 0001000 /* multidrive sel NI */ #define CS2_PGE 0002000 /* program err */ #define CS2_NEM 0004000 /* nx mem err */ #define CS2_NED 0010000 /* nx drive err */ #define CS2_PE 0020000 /* parity err NI */ #define CS2_WCE 0040000 /* write check err */ #define CS2_DLT 0100000 /* data late NI */ #define CS2_MBZ (CS2_CLR) #define CS2_RW 0000037 #define CS2_ERR (CS2_UFE | CS2_MDS | CS2_PGE | CS2_NEM | \ CS2_NED | CS2_PE | CS2_WCE | CS2_DLT ) #define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) /* HKDS - 177452 - drive status ^ = calculated dynamically */ #define DS_DRA 0000001 /* ^drive avail */ #define DS_OF 0000004 /* ^offset mode */ #define DS_ACLO 0000010 /* ^AC LO NI */ #define DS_SPLS 0000020 /* ^speed loss NI */ #define DS_DOT 0000040 /* ^off track NI */ #define DS_VV 0000100 /* volume valid */ #define DS_RDY 0000200 /* ^drive ready */ #define DS_DT 0000400 /* ^drive type */ #define DS_WRL 0004000 /* ^write locked */ #define DS_PIP 0020000 /* pos in progress */ #define DS_ATA 0040000 /* attention active */ #define DS_VLD 0100000 /* ^status valid */ #define DS_MBZ 0013002 /* HKER - 177454 - error status */ #define ER_ILF 0000001 /* illegal func */ #define ER_SKI 0000002 /* seek incomp */ #define ER_NXF 0000004 /* non-exec func */ #define ER_PAR 0000010 /* parity err */ #define ER_FER 0000020 /* format err NI */ #define ER_DTY 0000040 /* drive type err */ #define ER_ECH 0000100 /* ECC hard err NI */ #define ER_BSE 0000200 /* bad sector err NI */ #define ER_HCR 0000400 /* hdr CRC err NI */ #define ER_AOE 0001000 /* addr ovflo err */ #define ER_IAE 0002000 /* invalid addr err */ #define ER_WLE 0004000 /* write lock err */ #define ER_DTE 0010000 /* drive time err NI */ #define ER_OPI 0020000 /* op incomplete */ #define ER_UNS 0040000 /* drive unsafe */ #define ER_DCK 0100000 /* data check NI */ /* HKAS - 177456 - attention summary/offset */ #define AS_U0 0000400 /* unit 0 flag */ #define AS_OF 0000277 /* offset mask */ /* HKDC - 177460 - desired cylinder */ #define DC_V_CY 0 /* cylinder pos */ #define DC_M_CY 0001777 /* cylinder mask */ #define DC_MBZ 0176000 #define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) #define GET_DA(c,fs) ((((GET_CY (c) * HK_NUMSF) + \ GET_SF (fs)) * HK_NUMSC) + GET_SC (fs)) /* Spare - 177462 - read/write */ #define XM_KMASK 0177700 /* Qbus XM key mask */ #define XM_KEY 0022000 /* Qbus XM "key" */ #define XM_MMASK 0000077 /* Qbus XM mask */ #define SC02C (!UNIBUS && ((hkspr & XM_KMASK) == XM_KEY)) /* HKDB - 177464 - read/write */ /* HKMR - 177466 - maintenance register 1 */ #define MR_V_MS 0 /* message select */ #define MR_M_MS 03 #define MR_MS (MR_M_MS << MR_V_MS) #define GET_MS(x) (((x) >> MR_V_MS) & MR_M_MS) #define MR_PAR 0000020 /* force even parity */ #define MR_DMD 0000040 /* diagnostic mode */ #define MR_RW 0001777 /* HKEC1 - 177470 - ECC status 1 - always reads as 0 */ /* HKEC2 - 177472 - ECC status 2 - always reads as 0 */ /* HKMR2 - 177474 - maintenance register 2 */ #define AX_V_UNIT 0 /* unit #, all msgs */ #define AX_PAR 0100000 /* parity, all msgs */ #define A0_DRA 0000040 /* drive avail */ #define A0_VV 0000100 /* vol valid */ #define A0_RDY 0000200 /* drive ready */ #define A0_DT 0000400 /* drive type */ #define A0_FMT 0001000 /* format NI */ #define A0_OF 0002000 /* offset mode */ #define A0_WRL 0004000 /* write lock */ #define A0_SPO 0010000 /* spindle on */ #define A0_PIP 0020000 /* pos in prog */ #define A0_ATA 0040000 /* attention */ #define A1_SRV 0000020 /* servo */ #define A1_HHM 0000040 /* heads home */ #define A1_BHM 0000100 /* brushes home */ #define A1_DOR 0000200 /* door latched */ #define A1_CAR 0000400 /* cartridge present */ #define A1_SPD 0001000 /* speed ok */ #define A1_FWD 0002000 /* seek fwd */ #define A1_REV 0004000 /* seek rev */ #define A1_LDH 0010000 /* loading heads NI */ #define A1_RTZ 0020000 /* return to zero */ #define A1_UNL 0040000 /* unloading heads */ #define A2_V_DIF 4 /* cyl diff */ #define A2_M_DIF 0777 #define A3_V_SNO 3 /* serial # */ /* HKMR3 - 177476 - maintenance register 3 */ #define B0_IAE 0000040 /* invalid addr */ #define B0_ACLO 0000100 /* AC LO NI */ #define B0_FLT 0000200 /* fault */ #define B0_NXF 0000400 /* non exec fnc */ #define B0_CDP 0001000 /* msg parity err */ #define B0_SKI 0002000 /* seek incomp */ #define B0_WLE 0004000 /* write lock err */ #define B0_SLO 0010000 /* speed low NI */ #define B0_OFT 0020000 /* off track NI */ #define B0_UNS 0040000 /* rw unsafe NI */ #define B1_SCE 0000020 /* sector err NI */ #define B1_NWC 0000040 /* no write curr NI */ #define B1_NWT 0000100 /* no write trans NI */ #define B1_HFL 0000200 /* head fault NI */ #define B1_MHS 0000400 /* multiselect NI */ #define B1_IDX 0001000 /* index err NI */ #define B1_TRI 0002000 /* tribit err NI */ #define B1_SVE 0004000 /* servo err NI */ #define B1_SKI 0010000 /* seek no mot */ #define B1_LIM 0020000 /* seek limit NI */ #define B1_SVU 0040000 /* servo unsafe NI */ #define B2_V_CYL 4 /* cylinder */ #define B3_V_SEC 4 /* sector */ #define B3_V_DHA 9 /* decoded head */ /* Read header */ #define RDH1_V_CYL 0 /* cylinder */ #define RDH2_V_SEC 0 /* sector */ #define RDH2_V_DHA 5 /* decoded head */ #define RDH2_GOOD 0140000 /* good sector flags */ /* Debug detail levels */ #define HKDEB_OPS 001 /* transactions */ #define HKDEB_RRD 002 /* reg reads */ #define HKDEB_RWR 004 /* reg writes */ extern int32 int_req[IPL_HLVL]; extern FILE *sim_deb; uint16 *hkxb = NULL; /* xfer buffer */ int32 hkcs1 = 0; /* control/status 1 */ int32 hkwc = 0; /* word count */ int32 hkba = 0; /* bus address */ int32 hkda = 0; /* track/sector */ int32 hkcs2 = 0; /* control/status 2 */ int32 hkds[HK_NUMDR] = { 0 }; /* drive status */ int32 hker[HK_NUMDR] = { 0 }; /* error status */ int32 hkof = 0; /* offset */ int32 hkmr = 0; /* maint registers */ int32 hkmr2 = 0; int32 hkmr3 = 0; int32 hkdc = 0; /* cylinder */ int32 hkspr = 0; /* spare */ int32 hk_cwait = 5; /* command time */ int32 hk_swait = 10; /* seek time */ int32 hk_rwait = 10; /* rotate time */ int32 hk_min2wait = 300; /* min time to 2nd int */ int16 hkdb[3] = { 0 }; /* data buffer silo */ int16 hk_off[HK_NUMDR] = { 0 }; /* saved offset */ int16 hk_dif[HK_NUMDR] = { 0 }; /* cylinder diff */ static uint8 reg_in_drive[16] = { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; DEVICE hk_dev; t_stat hk_rd (int32 *data, int32 PA, int32 access); t_stat hk_wr (int32 data, int32 PA, int32 access); t_stat hk_svc (UNIT *uptr); t_stat hk_reset (DEVICE *dptr); t_stat hk_boot (int32 unitno, DEVICE *dptr); t_stat hk_attach (UNIT *uptr, char *cptr); t_stat hk_detach (UNIT *uptr); int32 hk_rdmr2 (int32 msg); int32 hk_rdmr3 (int32 msg); void update_hkcs (int32 flags, int32 drv); void update_hkds (int32 drv); void hk_cmderr (int32 err, int32 drv); void hk_go (int32 drv); t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); /* HK data structures hk_dev HK device descriptor hk_unit HK unit list hk_reg HK register list hk_mod HK modifier list */ DIB hk_dib = { IOBA_HK, IOLN_HK, &hk_rd, &hk_wr, 1, IVCL (HK), VEC_HK, { NULL } }; UNIT hk_unit[] = { { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ UNIT_ROABLE+UNIT_RK06, RK06_SIZE) } }; REG hk_reg[] = { { GRDATA (HKCS1, hkcs1, DEV_RDX, 16, 0) }, { GRDATA (HKWC, hkwc, DEV_RDX, 16, 0) }, { GRDATA (HKBA, hkba, DEV_RDX, 16, 0) }, { GRDATA (HKDA, hkda, DEV_RDX, 16, 0) }, { GRDATA (HKCS2, hkcs2, DEV_RDX, 16, 0) }, { BRDATA (HKDS, hkds, DEV_RDX, 16, HK_NUMDR) }, { BRDATA (HKER, hker, DEV_RDX, 16, HK_NUMDR) }, { BRDATA (HKDB, hkdb, DEV_RDX, 16, 3) }, { GRDATA (HKDC, hkdc, DEV_RDX, 16, 0) }, { GRDATA (HKOF, hkof, DEV_RDX, 8, 0) }, { GRDATA (HKMR, hkmr, DEV_RDX, 16, 0) }, { GRDATA (HKMR2, hkmr2, DEV_RDX, 16, 0), REG_RO }, { GRDATA (HKMR3, hkmr3, DEV_RDX, 16, 0), REG_RO }, { GRDATA (HKSPR, hkspr, DEV_RDX, 16, 0) }, { FLDATA (INT, IREQ (HK), INT_V_HK) }, { FLDATA (ERR, hkcs1, CSR_V_ERR) }, { FLDATA (DONE, hkcs1, CSR_V_DONE) }, { FLDATA (IE, hkcs1, CSR_V_IE) }, { DRDATA (CTIME, hk_cwait, 24), REG_NZ + PV_LEFT }, { DRDATA (STIME, hk_swait, 24), REG_NZ + PV_LEFT }, { DRDATA (RTIME, hk_rwait, 24), REG_NZ + PV_LEFT }, { DRDATA (MIN2TIME, hk_min2wait, 24), REG_NZ + PV_LEFT + REG_HRO }, { URDATA (FNC, hk_unit[0].FNC, DEV_RDX, 5, 0, HK_NUMDR, REG_HRO) }, { URDATA (CYL, hk_unit[0].CYL, DEV_RDX, 10, 0, HK_NUMDR, REG_HRO) }, { BRDATA (OFFSET, hk_off, DEV_RDX, 16, HK_NUMDR), REG_HRO }, { BRDATA (CYLDIF, hk_dif, DEV_RDX, 16, HK_NUMDR), REG_HRO }, { URDATA (CAPAC, hk_unit[0].capac, 10, T_ADDR_W, 0, HK_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, hk_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, hk_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB hk_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_DUMMY, 0, NULL, "BADBLOCK", &hk_set_bad }, { (UNIT_DTYPE+UNIT_ATT), UNIT_RK06 + UNIT_ATT, "RK06", NULL, NULL }, { (UNIT_DTYPE+UNIT_ATT), UNIT_RK07 + UNIT_ATT, "RK07", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK06, "RK06", NULL, NULL }, { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK07, "RK07", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK06, NULL, "RK06", &hk_set_size }, { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK07, NULL, "RK07", &hk_set_size }, { MTAB_XTD|MTAB_VDV, 0040, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEBTAB hk_deb[] = { { "OPS", HKDEB_OPS }, { "RRD", HKDEB_RRD }, { "RWR", HKDEB_RWR }, { NULL, 0 } }; DEVICE hk_dev = { "HK", hk_unit, hk_reg, hk_mod, HK_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16, NULL, NULL, &hk_reset, &hk_boot, &hk_attach, &hk_detach, &hk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG, 0, hk_deb, NULL, 0 }; /* I/O dispatch routines, I/O addresses 17777440 - 17777476 */ t_stat hk_rd (int32 *data, int32 PA, int32 access) { int32 drv, i, j; drv = GET_UNIT (hkcs2); /* get current unit */ j = (PA >> 1) & 017; /* get reg offset */ if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */ hkcs2 = hkcs2 | CS2_NED; /* set error flag */ update_hkcs (0, drv); *data = 0; return SCPE_OK; } update_hkcs (0, drv); /* update status */ switch (j) { /* decode PA<4:1> */ case 000: /* HKCS1 */ *data = hkcs1; break; case 001: /* HKWC */ *data = hkwc; break; case 002: /* HKBA */ *data = hkba = hkba & ~BA_MBZ; break; case 003: /* HKDA */ *data = hkda = hkda & ~DA_MBZ; break; case 004: /* HKCS2 */ *data = hkcs2 = (hkcs2 & ~CS2_MBZ) | CS2_IR; break; case 005: /* HKDS */ *data = hkds[drv]; break; case 006: /* HKER */ *data = hker[drv]; break; case 007: /* HKAS */ *data = hkof; for (i = 0; i < HK_NUMDR; i++) { if (hkds[i] & DS_ATA) *data = *data | (AS_U0 << i); } break; case 010: /* HKDC */ *data = hkdc = hkdc & ~DC_MBZ; break; case 011: /* spare */ *data = hkspr; break; case 012: /* HKDB */ *data = hkdb[0]; /* top of silo */ hkdb[0] = hkdb[1]; /* ripple silo */ hkdb[1] = hkdb[2]; hkdb[2] = 0; /* just for READH */ break; case 013: /* HKMR */ *data = hkmr; break; case 014: /* HKEC1 */ case 015: /* HKEC2 */ *data = 0; /* no ECC */ break; case 016: /* HKMR2 */ *data = hkmr2; break; case 017: /* HKMR3 */ *data = hkmr3; break; } if (DEBUG_PRI (hk_dev, HKDEB_RRD)) fprintf (sim_deb, ">>HK%d read: reg%d=%o\n", drv, j, *data); return SCPE_OK; } t_stat hk_wr (int32 data, int32 PA, int32 access) { int32 drv, i, j; UNIT *uptr; drv = GET_UNIT (hkcs2); /* get current unit */ uptr = hk_dev.units + drv; /* get unit */ j = (PA >> 1) & 017; /* get reg offset */ if ((hkcs1 & CS1_GO) && /* busy? */ !(((j == 0) && (data & CS1_CCLR)) || /* not cclr or sclr? */ ((j == 4) && (data & CS2_CLR)))) { hkcs2 = hkcs2 | CS2_PGE; /* prog error */ update_hkcs (0, drv); return SCPE_OK; } if (DEBUG_PRI (hk_dev, HKDEB_RWR)) fprintf (sim_deb, ">>HK%d write: reg%d=%o\n", drv, j, data); switch (j) { /* decode PA<4:1> */ case 000: /* HKCS1 */ if (data & CS1_CCLR) { /* controller reset? */ hkcs1 = CS1_DONE; /* CS1 = done */ hkcs2 = CS2_IR; /* CS2 = ready */ hkmr = hkmr2 = hkmr3 = 0; /* maint = 0 */ hkda = hkdc = 0; hkba = hkwc = 0; hkspr = hkof = 0; CLR_INT (HK); /* clr int */ for (i = 0; i < HK_NUMDR; i++) { /* stop data xfr */ if (sim_is_active (&hk_unit[i]) && ((uptr->FNC & CS1_M_FNC) >= FNC_XFER)) sim_cancel (&hk_unit[i]); } drv = 0; break; } if (data & CS1_IE) { /* setting IE? */ if (data & CS1_DONE) /* write to DONE+IE? */ SET_INT (HK); } else CLR_INT (HK); /* no, clr intr */ hkcs1 = (hkcs1 & ~CS1_RW) | (data & CS1_RW); /* merge data */ if (SC02C) hkspr = (hkspr & ~CS1_M_UAE) | GET_UAE (hkcs1); if ((data & CS1_GO) && !(hkcs1 & CS1_ERR)) /* go? */ hk_go (drv); break; case 001: /* HKWC */ hkwc = data; break; case 002: /* HKBA */ hkba = data & ~BA_MBZ; break; case 003: /* HKDA */ hkda = data & ~DA_MBZ; break; case 004: /* HKCS2 */ if (data & CS2_CLR) /* init? */ hk_reset (&hk_dev); else hkcs2 = (hkcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR; drv = GET_UNIT (hkcs2); break; case 007: /* HKAS */ hkof = data & AS_OF; break; case 010: /* HKDC */ hkdc = data & ~DC_MBZ; break; case 011: /* spare */ hkspr = data; if (SC02C) /* SC02C? upd UAE */ hkcs1 = PUT_UAE (hkcs1, hkspr & 03); break; case 012: /* HKDB */ hkdb[0] = data; break; case 013: /* HKMR */ hkmr = data & MR_RW; break; default: /* all others RO */ break; } /* end switch */ update_hkcs (0, drv); /* update status */ return SCPE_OK; } /* Initiate operation - go set, not previously set */ void hk_go (int32 drv) { int32 fnc, t; UNIT *uptr; static uint8 fnc_cdt[16] = { 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; static uint8 fnc_nxf[16] = { 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0 }; static uint8 fnc_att[16] = { 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }; static uint8 fnc_rdy[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }; static uint8 fnc_cyl[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 }; fnc = GET_FNC (hkcs1); if (DEBUG_PRI (hk_dev, HKDEB_OPS)) fprintf (sim_deb, ">>HK%d strt: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", drv, fnc, hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); uptr = hk_dev.units + drv; /* get unit */ if (fnc != FNC_NOP) /* !nop, clr msg sel */ hkmr = hkmr & ~MR_MS; if (uptr->flags & UNIT_DIS) { /* nx unit? */ hkcs2 = hkcs2 | CS2_NED; /* set error flag */ update_hkcs (CS1_DONE, drv); /* done */ return; } if (fnc_cdt[fnc] && /* need dtype match? */ (((hkcs1 & CS1_DT) != 0) != ((uptr->flags & UNIT_DTYPE) != 0))) { hk_cmderr (ER_DTY, drv); /* type error */ return; } if (fnc_nxf[fnc] && ((hkds[drv] & DS_VV) == 0)) { /* need vol valid? */ hk_cmderr (ER_NXF, drv); /* non exec func */ return; } if (fnc_att[fnc] && !(uptr->flags & UNIT_ATT)) { /* need attached? */ hk_cmderr (ER_UNS, drv); /* unsafe */ return; } if (fnc_rdy[fnc] && sim_is_active (uptr)) /* need inactive? */ return; if (fnc_cyl[fnc] && /* need valid cyl */ ((GET_CY (hkdc) >= HK_CYL (uptr)) || /* bad cylinder */ (GET_SF (hkda) >= HK_NUMSF) || /* bad surface */ (GET_SC (hkda) >= HK_NUMSC))) { /* or bad sector? */ hk_cmderr (ER_IAE, drv); /* illegal addr */ return; } hkcs1 = (hkcs1 | CS1_GO) & ~CS1_DONE; /* set go, clear done */ switch (fnc) { /* case on function */ /* Instantaneous functions (unit may be busy, can't schedule thread) */ case FNC_NOP: /* no operation */ hkmr2 = hk_rdmr2 (GET_MS (hkmr)); /* get serial msgs */ hkmr3 = hk_rdmr3 (GET_MS (hkmr)); update_hkcs (CS1_DONE, drv); /* done */ break; case FNC_DCLR: /* drive clear */ hkds[drv] &= ~DS_ATA; /* clr ATA */ hker[drv] = 0; /* clear errors */ update_hkcs (CS1_DONE, drv); /* done */ break; case FNC_PACK: /* pack acknowledge */ hkds[drv] = hkds[drv] | DS_VV; /* set volume valid */ update_hkcs (CS1_DONE, drv); /* done */ break; /* "Fast functions" finish in less than 15 usec */ case FNC_START: /* start spindle */ case FNC_UNLOAD: /* unload */ uptr->FNC = fnc; /* save function */ sim_activate (uptr, hk_cwait); /* schedule */ return; /* Positioning functions provide two interrupts - an immediate interrupt on ctrl done and a second one (if ctrl ready) when the seek is complete */ case FNC_OFFSET: /* offset mode */ case FNC_RECAL: /* recalibrate */ case FNC_SEEK: /* seek */ hkds[drv] = hkds[drv] | DS_PIP; /* set positioning */ uptr->FNC = fnc; /* save function */ sim_activate (uptr, hk_cwait); /* schedule */ return; /* Data transfer functions lock the controller for the duration */ case FNC_WRITEH: /* write headers */ case FNC_WRITE: /* write */ hk_off[drv] = 0; /* clr offset */ case FNC_WCHK: /* write check */ case FNC_READ: /* read */ case FNC_READH: /* read headers */ hk_dif[drv] = hkdc - uptr->CYL; /* cyl diff */ t = abs (hk_dif[drv]); /* |cyl diff| */ sim_activate (uptr, hk_rwait + (hk_swait * t)); /* schedule */ uptr->FNC = fnc; /* save function */ uptr->CYL = hkdc; /* update cyl */ return; default: hk_cmderr (ER_ILF, drv); /* not supported */ break; } return; } /* Service unit timeout Complete movement or data transfer command Unit must exist - can't remove an active unit Unit must be attached - detach cancels in progress operations */ t_stat hk_svc (UNIT *uptr) { int32 i, t, dc, fnc, err; int32 wc, awc, da; uint32 drv, ba; uint16 comp; drv = (uint32) (uptr - hk_dev.units); /* get drv number */ fnc = uptr->FNC & CS1_M_FNC; /* get function */ switch (fnc) { /* case on function */ /* Fast commands - start spindle only provides one interrupt because ATTACH implicitly spins up the drive */ case FNC_UNLOAD: /* unload */ hk_detach (uptr); /* detach unit */ case FNC_START: /* start spindle */ update_hkcs (CS1_DONE, drv); /* done */ break; /* Positioning commands provide two interrupts, an immediate controller done and a delayed drive interrupt */ case FNC_OFFSET: /* offset */ if (uptr->FNC & FNC_2ND) { /* 2nd int? */ hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ update_hkcs (CS1_DI, drv); /* drive intr */ } else { uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ hk_off[drv] = hkof & AS_OF; /* save offset */ sim_activate (uptr, hk_min2wait); /* wait for compl */ update_hkcs (CS1_DONE, drv); /* done */ } break; case FNC_RECAL: /* recalibrate */ case FNC_SEEK: /* seek */ if (uptr->FNC & FNC_2ND) { /* 2nd int? */ hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ update_hkcs (CS1_DI, drv); /* drive intr */ } else { uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ hk_off[drv] = 0; /* clr offset */ dc = (fnc == FNC_SEEK)? hkdc: 0; /* get cyl */ hk_dif[drv] = dc - uptr->CYL; /* cyl diff */ t = abs (hk_dif[drv]) * hk_swait; /* |cyl diff| */ if (t < hk_min2wait) /* min time */ t = hk_min2wait; uptr->CYL = dc; /* save cyl */ sim_activate (uptr, t); /* schedule */ update_hkcs (CS1_DONE, drv); /* done */ } break; /* Data transfer commands only generate one interrupt */ case FNC_READH: hkdb[0] = uptr->CYL << RDH1_V_CYL; /* first word */ hkdb[1] = (GET_SC (hkda) << RDH2_V_SEC) | /* second word */ (1 << (GET_SF (hkda) + RDH2_V_DHA)) | RDH2_GOOD; hkdb[2] = hkdb[0] ^ hkdb[1]; /* checksum */ update_hkcs (CS1_DONE, drv); /* done */ break; case FNC_WRITE: /* write */ if (uptr->flags & UNIT_WPRT) { /* write locked? */ hk_cmderr (ER_WLE, drv); /* command error */ return SCPE_OK; } case FNC_WCHK: /* write check */ case FNC_READ: /* read */ if (SC02C) /* 22b addr? */ ba = ((hkspr & XM_MMASK) << 16) | hkba; else ba = (GET_UAE (hkcs1) << 16) | hkba; /* no, 18b addr */ da = GET_DA (hkdc, hkda) * HK_NUMWD; /* get disk addr */ wc = 0200000 - hkwc; /* get true wc */ if ((da + wc) > HK_SIZE (uptr)) { /* disk overrun? */ hker[drv] = hker[drv] | ER_AOE; /* set err */ hkds[drv] = hkds[drv] | DS_ATA; /* set attn */ wc = HK_SIZE (uptr) - da; /* trim xfer */ if (da >= HK_SIZE (uptr)) { /* none left? */ update_hkcs (CS1_DONE, drv); /* then done */ break; } } err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); if (uptr->FNC == FNC_WRITE) { /* write? */ if (hkcs2 & CS2_UAI) { /* no addr inc? */ if (t = Map_ReadW (ba, 2, &comp)) { /* get 1st wd */ wc = 0; /* NXM, no xfr */ hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ } for (i = 0; i < wc; i++) hkxb[i] = comp; } else { /* normal */ if (t = Map_ReadW (ba, wc << 1, hkxb)) { /* get buf */ wc = wc - (t >> 1); /* NXM, adj wc */ hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ } ba = ba + (wc << 1); /* adv ba */ } awc = (wc + (HK_NUMWD - 1)) & ~(HK_NUMWD - 1); for (i = wc; i < awc; i++) /* fill buf */ hkxb[i] = 0; if (wc && !err) { /* write buf */ fxwrite (hkxb, sizeof (uint16), wc, uptr->fileref); err = ferror (uptr->fileref); } } /* end if wr */ else if (uptr->FNC == FNC_READ) { /* read? */ i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref); err = ferror (uptr->fileref); for ( ; i < wc; i++) /* fill buf */ hkxb[i] = 0; if (hkcs2 & CS2_UAI) { /* no addr inc? */ if (t = Map_WriteW (ba, 2, &hkxb[wc - 1])) { wc = 0; /* NXM, no xfr */ hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ } } else { /* normal */ if (t = Map_WriteW (ba, wc << 1, hkxb)) { /* put buf */ wc = wc - (t >> 1); /* NXM, adj wc */ hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ } ba = ba + (wc << 1); /* adv ba */ } } /* end if read */ else { /* wchk */ i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref); err = ferror (uptr->fileref); for ( ; i < wc; i++) /* fill buf */ hkxb[i] = 0; awc = wc; for (wc = 0; wc < awc; wc++) { /* loop thru buf */ if (Map_ReadW (ba, 2, &comp)) { /* read word */ hkcs2 = hkcs2 | CS2_NEM; /* set error */ break; } if (comp != hkxb[wc]) { /* compare wd */ hkcs2 = hkcs2 | CS2_WCE; /* set error */ break; } if ((hkcs2 & CS2_UAI) == 0) ba = ba + 2; } } /* end else wchk */ hkwc = (hkwc + wc) & 0177777; /* final word count */ hkba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ hkcs1 = PUT_UAE (hkcs1, ba >> 16); /* upper 2b */ if (SC02C) /* SC02C? upper 6b */ hkspr = (hkspr & ~XM_MMASK) | ((ba >> 16) & XM_MMASK); da = da + wc + (HK_NUMWD - 1); da = da / HK_NUMWD; hkda = da % HK_NUMSC; da = da / HK_NUMSC; hkda = hkda | ((da % HK_NUMSF) << DA_V_SF); hkdc = da / HK_NUMSF; if (err != 0) { /* error? */ hk_cmderr (ER_PAR, drv); /* set drive error */ perror ("HK I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } case FNC_WRITEH: /* write headers stub */ update_hkcs (CS1_DONE, drv); /* set done */ break; } /* end case func */ return SCPE_OK; } /* Controller status update Check for done transition Update drive status Update HKCS1 Update interrupt request */ void update_hkcs (int32 flag, int32 drv) { int32 i; update_hkds (drv); /* upd drv status */ if (flag & CS1_DONE) /* clear go */ hkcs1 = hkcs1 & ~CS1_GO; if (hkcs1 & CS1_IE) { /* intr enable? */ if (((flag & CS1_DONE) && ((hkcs1 & CS1_DONE) == 0)) || ((flag & CS1_DI) && (hkcs1 & CS1_DONE))) /* done 0->1 or DI? */ SET_INT (HK); } else CLR_INT (HK); hkcs1 = (hkcs1 & (CS1_DT|CS1_UAE|CS1_DONE|CS1_IE|CS1_SPA|CS1_FNC|CS1_GO)) | flag; for (i = 0; i < HK_NUMDR; i++) { /* if ATA, set DI */ if (hkds[i] & DS_ATA) hkcs1 = hkcs1 | CS1_DI; } if (hker[drv] | (hkcs1 & (CS1_PAR | CS1_CTO)) | /* if err, set ERR */ (hkcs2 & CS2_ERR)) hkcs1 = hkcs1 | CS1_ERR; if ((flag & CS1_DONE) && /* set done && debug? */ (DEBUG_PRI (hk_dev, HKDEB_OPS))) fprintf (sim_deb, ">>HK%d done: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", drv, GET_FNC (hkcs1), hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); return; } /* Drive status update */ void update_hkds (int32 drv) { if (hk_unit[drv].flags & UNIT_DIS) { /* disabled? */ hkds[drv] = hker[drv] = 0; /* all clear */ return; } hkds[drv] = (hkds[drv] & (DS_VV | DS_PIP | DS_ATA)) | DS_VLD | DS_DRA; if (hk_unit[drv].flags & UNIT_ATT) { /* attached? */ if (!sim_is_active (&hk_unit[drv])) /* not busy? */ hkds[drv] = hkds[drv] | DS_RDY; /* set RDY */ if (hker[drv]) /* err? set ATA */ hkds[drv] = hkds[drv] | DS_ATA; if (hk_off[drv]) /* offset? set OF */ hkds[drv] = hkds[drv] | DS_OF; if (hk_unit[drv].flags & UNIT_WPRT) /* write locked? */ hkds[drv] = hkds[drv] | DS_WRL; /* set WRL */ } else { hkds[drv] = hkds[drv] & ~(DS_PIP | DS_VV); /* no, clr PIP,VV */ hker[drv] = 0; /* no errors */ } if (hk_unit[drv].flags & UNIT_RK07) hkds[drv] = hkds[drv] | DS_DT; return; } /* Set error and abort command */ void hk_cmderr (int32 err, int32 drv) { hker[drv] = hker[drv] | err; /* set error */ hkds[drv] = hkds[drv] | DS_ATA; /* set attn */ update_hkcs (CS1_DONE, drv); /* set done */ return; } /* Diagnostic registers It's unclear whether the drivers actually use these values, but the Emulex controller bothers to implement them, so we will too */ int32 hk_mrpar (int32 v) { int32 bit, wrk; wrk = v & 077777; /* par on 15b */ v = wrk | ((hkmr & MR_PAR)? 0: AX_PAR); /* even/odd */ while (wrk) { /* while 1's */ bit = wrk & (-wrk); /* lowest 1 */ wrk = wrk & ~bit; /* clear */ v = v ^ AX_PAR; /* xor parity */ } return v; } int32 hk_rdmr2 (int32 msg) { int32 drv = GET_UNIT (hkcs2); int32 v = drv << AX_V_UNIT; UNIT *uptr = hk_dev.units + drv; int32 fnc = uptr->FNC & CS1_M_FNC; switch (msg) { case 0: /* message A0 */ v = v | ((hkds[drv] & DS_ATA)? A0_ATA: 0) | ((hkds[drv] & DS_PIP)? A0_PIP: 0) | ((uptr->flags & UNIT_WPRT)? A0_WRL: 0) | ((hk_off[drv])? A0_OF: 0) | ((uptr->flags & UNIT_RK07)? A0_DT: 0) | ((hkds[drv] & DS_VV)? A0_VV: 0) | A0_DRA; if (uptr->flags & UNIT_ATT) v = v | A0_SPO | (!sim_is_active (uptr)? A0_RDY: 0); break; case 1: /* message A1 */ if (uptr->flags & UNIT_ATT) { if (sim_is_active (uptr)) { if (fnc == FNC_UNLOAD) v = v | A1_UNL; else if (fnc == FNC_RECAL) v = v | A1_RTZ; else if (fnc == FNC_SEEK) { if (hk_dif[drv] < 0) v = v | A1_REV; if (hk_dif[drv] > 0) v = v | A1_FWD; } } v = v | (A1_SPD|A1_CAR|A1_DOR|A1_HHM|A1_SRV); } else v = v | A1_HHM; break; case 2: /* message A2 */ if (hkds[drv] & DS_OF) v = v | ((hk_off[drv] & A2_M_DIF) << A2_V_DIF); else v = v | ((hk_dif[drv] & A2_M_DIF) << A2_V_DIF); break; case 3: /* message A3 */ v = v | ((012340 + v) << A3_V_SNO); break; } return hk_mrpar (v); } int32 hk_rdmr3 (int32 msg) { int32 drv = GET_UNIT (hkcs2); int32 v = msg & 03; switch (msg) { case 0: /* message B0 */ v = v | ((hker[drv] & ER_WLE)? (B0_WLE | B0_FLT): 0) | ((hker[drv] & ER_SKI)? (B0_SKI | B0_FLT): 0) | ((hker[drv] & ER_NXF)? (B0_NXF | B0_FLT): 0) | ((hker[drv] & ER_IAE)? (B0_IAE | B0_FLT): 0); break; case 1: /* message B1 */ v = v | ((hker[drv] & ER_SKI)? B1_SKI: 0) | ((hker[drv] & ER_UNS)? B1_SVE: 0); break; case 2: /* message B2 */ v = v | (hk_unit[drv].CYL << B2_V_CYL); break; case 3: /* message B3 */ v = v | (GET_SC (hkda) << B3_V_SEC) | (1 << (GET_SF (hkda) + B3_V_DHA)); break; } return hk_mrpar (v); } /* Device reset */ t_stat hk_reset (DEVICE *dptr) { int32 i; UNIT *uptr; hkcs1 = CS1_DONE; /* set done */ hkcs2 = CS2_IR; /* clear state */ hkmr = hkmr2 = hkmr3 = 0; hkda = hkdc = 0; hkba = hkwc = 0; hkof = hkspr = 0; CLR_INT (HK); /* clear intr req */ for (i = 0; i < HK_NUMDR; i++) { /* stop operations */ uptr = hk_dev.units + i; sim_cancel (uptr); if (uptr->flags & UNIT_ATT) hkds[i] = hkds[i] & DS_VV; else hkds[i] = 0; uptr->CYL = uptr->FNC = 0; /* clear state */ hk_dif[i] = 0; hk_off[i] = 0; hker[i] = 0; } /* clear errors */ if (hkxb == NULL) hkxb = (uint16 *) calloc (HK_MAXFR, sizeof (uint16)); if (hkxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Device attach */ t_stat hk_attach (UNIT *uptr, char *cptr) { uint32 drv, p; t_stat r; uptr->capac = HK_SIZE (uptr); r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; drv = (uint32) (uptr - hk_dev.units); /* get drv number */ hkds[drv] = DS_ATA | DS_RDY | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); hker[drv] = 0; /* upd drv status */ hk_off[drv] = 0; hk_dif[drv] = 0; uptr->CYL = 0; update_hkcs (CS1_DI, drv); /* upd ctlr status */ p = sim_fsize (uptr->fileref); /* get file size */ if (p == 0) { /* new disk image? */ if (uptr->flags & UNIT_RO) return SCPE_OK; return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD); } if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; if (p > (RK06_SIZE * sizeof (uint16))) { uptr->flags = uptr->flags | UNIT_RK07; uptr->capac = RK07_SIZE; } else { uptr->flags = uptr->flags & ~UNIT_RK07; uptr->capac = RK06_SIZE; } return SCPE_OK; } /* Device detach */ t_stat hk_detach (UNIT *uptr) { uint32 drv; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; drv = (uint32) (uptr - hk_dev.units); /* get drv number */ hkds[drv] = (hkds[drv] & ~(DS_RDY | DS_WRL | DS_VV | DS_OF)) | DS_ATA; if (sim_is_active (uptr)) { /* unit active? */ sim_cancel (uptr); /* cancel operation */ hker[drv] = hker[drv] | ER_OPI; /* set drive error */ if ((uptr->FNC & FNC_2ND) == 0) /* expecting done? */ update_hkcs (CS1_DONE, drv); /* set done */ } update_hkcs (CS1_DI, drv); /* request intr */ return detach_unit (uptr); } /* Set size command validation routine */ t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = val? RK07_SIZE: RK06_SIZE; return SCPE_OK; } /* Set bad block routine */ t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) { return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD); } #if defined (VM_PDP11) /* Device bootstrap - does not clear CSR when done */ #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 014) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0042115, /* "MD" */ 0012706, BOOT_START, /* mov #boot_start, sp */ 0012700, 0000000, /* mov #unit, r0 */ 0012701, 0177440, /* mov #HKCS1, r1 */ 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ 0010061, 0000010, /* mov r0, 10(r1) ; set unit */ 0016102, 0000012, /* mov 12(r1), r2 ; drv typ */ 0100375, /* bpl .-4 ; valid? */ 0042702, 0177377, /* bic #177377, r2 ; clr rest */ 0006302, /* asl r2 ; move */ 0006302, /* asl r2 */ 0012703, 0000003, /* mov #pack+go, r3 */ 0050203, /* bis r2, r3 ; merge type */ 0010311, /* mov r3, (r1); ; pack ack */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0012761, 0177000, 0000002, /* mov #-512.,2(r1) ; set wc */ 0005061, 0000004, /* clr 4(r1) ; clr ba */ 0005061, 0000006, /* clr 6(r1) ; clr da */ 0005061, 0000020, /* clr 20(r1) ; clr cyl */ 0012703, 0000021, /* mov #read+go, r3 */ 0050203, /* bis r2, r3 ; merge type */ 0010311, /* mov r3, (r1); ; read */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0005002, /* clr R2 */ 0005003, /* clr R3 */ 0012704, BOOT_START+020, /* mov #start+020, r4 */ 0005005, /* clr R5 */ 0005007 /* clr PC */ }; t_stat hk_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & CS2_M_UNIT; M[BOOT_CSR >> 1] = hk_dib.ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat hk_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif simh-3.8.1/PDP11/pdp11_dc.c0000644000175000017500000005753011111105220013157 0ustar vlmvlm/* pdp11_dc.c: PDP-11 DC11 multiple terminal interface simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dci,dco DC11 terminal input/output 19-Nov-2008 RMS Revised for common TMXR show routines Revised to autoconfigure vectors The simulator supports both hardwired and modem-like behavior. If modem control is not enabled, carrier detect, ring, and carrier change are never set. */ #if defined (VM_PDP10) /* PDP10 version */ #error "DC11 is not supported on the PDP-10!" #elif defined (VM_VAX) /* VAX version */ #error "DC11 is not supported on the VAX!" #else /* PDP-11 version */ #include "pdp11_defs.h" #endif #include "sim_sock.h" #include "sim_tmxr.h" #define DCX_MASK (DCX_LINES - 1) /* Parity and modem control */ #define DCX_V_OPAR (TTUF_V_UF + 0) #define DCX_V_EPAR (TTUF_V_UF + 1) #define DCX_V_MDM (TTUF_V_UF + 2) #define DCX_OPAR (1u << DCX_V_OPAR) #define DCX_EPAR (1u << DCX_V_EPAR) #define DCX_MDM (1u << DCX_V_MDM) /* registers */ #define DCICSR_RD 0173777 #define DCICSR_WR 0003533 #define DCICSR_DTR 0000001 /* DTR (RW) */ #define DCICSR_XBR 0000002 /* xmit brk (RWNI) */ #define DCICSR_CDT 0000004 /* car det (RO) */ #define DCICSR_PAR 0000040 /* odd par (RO) */ #define DCICSR_OVR 0010000 /* overrun (RO) */ #define DCICSR_RNG 0020000 /* ring (RO) */ #define DCICSR_CCH 0040000 /* car change (RO) */ #define DCICSR_ALLERR (DCICSR_OVR|DCICSR_RNG|DCICSR_CCH) #define DCICSR_ERR 0100000 /* error */ #define DCOCSR_RD 0100737 #define DCOCSR_WR 0000535 #define DCOCSR_RTS 0000001 /* req to send (RW) */ #define DCOCSR_CTS 0000002 /* clr to send (RO) */ #define DCOCSR_MNT 0000004 /* maint (RWNI) */ extern int32 int_req[IPL_HLVL]; extern int32 tmxr_poll; uint16 dci_csr[DCX_LINES] = { 0 }; /* control/status */ uint8 dci_buf[DCX_LINES] = { 0 }; uint32 dci_ireq = 0; uint16 dco_csr[DCX_LINES] = { 0 }; /* control/status */ uint8 dco_buf[DCX_LINES] = { 0 }; uint32 dco_ireq = 0; TMLN dcx_ldsc[DCX_LINES] = { 0 }; /* line descriptors */ TMXR dcx_desc = { DCX_LINES, 0, 0, dcx_ldsc }; /* mux descriptor */ static const uint8 odd_par[] = { 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 00 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 10 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 20 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 30 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 40 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 50 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 60 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 70 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 80 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 90 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* A0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* B0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* C0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* D0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* E0 */ 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* F0 */ 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80 }; t_stat dcx_rd (int32 *data, int32 PA, int32 access); t_stat dcx_wr (int32 data, int32 PA, int32 access); t_stat dcx_reset (DEVICE *dptr); t_stat dci_svc (UNIT *uptr); t_stat dco_svc (UNIT *uptr); t_stat dcx_attach (UNIT *uptr, char *cptr); t_stat dcx_detach (UNIT *uptr); t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc); void dcx_enbdis (int32 dis); void dci_clr_int (int32 ln); void dci_set_int (int32 ln); int32 dci_iack (void); void dco_clr_int (int32 ln); void dco_set_int (int32 ln); int32 dco_iack (void); void dcx_reset_ln (int32 ln); /* DCI data structures dci_dev DCI device descriptor dci_unit DCI unit descriptor dci_reg DCI register list */ DIB dci_dib = { IOBA_DC, IOLN_DC, &dcx_rd, &dcx_wr, 2, IVCL (DCI), VEC_DCI, { &dci_iack, &dco_iack } }; UNIT dci_unit = { UDATA (&dci_svc, 0, 0), KBD_POLL_WAIT }; REG dci_reg[] = { { BRDATA (BUF, dci_buf, DEV_RDX, 8, DCX_LINES) }, { BRDATA (CSR, dci_csr, DEV_RDX, 16, DCX_LINES) }, { GRDATA (IREQ, dci_ireq, DEV_RDX, DCX_LINES, 0) }, { DRDATA (LINES, dcx_desc.lines, 6), REG_HRO }, { GRDATA (DEVADDR, dci_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVIOLN, dci_dib.lnt, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, dci_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB dci_mod[] = { { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dcx_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &dcx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &dcx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &dcx_desc }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 1, "VECTOR", NULL, &set_vec, &show_vec_mux, (void *) &dcx_desc }, { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &dcx_set_lines, &tmxr_show_lines, (void *) &dcx_desc }, { 0 } }; DEVICE dci_dev = { "DCI", &dci_unit, dci_reg, dci_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &dcx_reset, NULL, &dcx_attach, &dcx_detach, &dci_dib, DEV_FLTA | DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS }; /* DCO data structures dco_dev DCO device descriptor dco_unit DCO unit descriptor dco_reg DCO register list */ UNIT dco_unit[] = { { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT } }; REG dco_reg[] = { { BRDATA (BUF, dco_buf, DEV_RDX, 8, DCX_LINES) }, { BRDATA (CSR, dco_csr, DEV_RDX, 16, DCX_LINES) }, { GRDATA (IREQ, dco_ireq, DEV_RDX, DCX_LINES, 0) }, { URDATA (TIME, dco_unit[0].wait, 10, 31, 0, DCX_LINES, PV_LEFT) }, { NULL } }; MTAB dco_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { DCX_OPAR+DCX_EPAR, 0, "no parity", "NOPARITY", NULL }, { DCX_OPAR+DCX_EPAR, DCX_OPAR, "odd parity", "ODDPARITY", NULL }, { DCX_OPAR+DCX_EPAR, DCX_EPAR, "even parity", "EVENPARITY", NULL }, { DCX_MDM, 0, "no dataset", "NODATASET", NULL }, { DCX_MDM, DCX_MDM, "dataset", "DATASET", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dcx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &dcx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &dcx_desc }, { 0 } }; DEVICE dco_dev = { "DCO", dco_unit, dco_reg, dco_mod, DCX_LINES, 10, 31, 1, 8, 8, NULL, NULL, &dcx_reset, NULL, NULL, NULL, NULL, DEV_UBUS | DEV_DISABLE | DEV_DIS }; /* Terminal input routines */ t_stat dcx_rd (int32 *data, int32 PA, int32 access) { int32 ln = ((PA - dci_dib.ba) >> 3) & DCX_MASK; switch ((PA >> 1) & 03) { /* decode PA<2:1> */ case 00: /* dci csr */ if (dci_csr[ln] & DCICSR_ALLERR) dci_csr[ln] |= DCICSR_ERR; else dci_csr[ln] &= ~DCICSR_ERR; *data = dci_csr[ln] & DCICSR_RD; dci_csr[ln] &= ~(CSR_DONE|DCICSR_ALLERR|DCICSR_ERR); return SCPE_OK; case 01: /* dci buf */ dci_clr_int (ln); *data = dci_buf[ln]; return SCPE_OK; case 02: /* dco csr */ *data = dco_csr[ln] & DCOCSR_RD; return SCPE_OK; case 03: /* dco buf */ *data = dco_buf[ln]; return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } t_stat dcx_wr (int32 data, int32 PA, int32 access) { int32 ln = ((PA - dci_dib.ba) >> 3) & DCX_MASK; TMLN *lp = &dcx_ldsc[ln]; switch ((PA >> 1) & 03) { /* decode PA<2:1> */ case 00: /* dci csr */ if (access == WRITEB) /* byte write? */ data = (PA & 1)? (dci_csr[ln] & 0377) | (data << 8): (dci_csr[ln] & ~0377) | data; if ((data & CSR_IE) == 0) /* clr ie? */ dci_clr_int (ln); /* clr int req */ else if ((dci_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) dci_set_int (ln); if (((data ^ dci_csr[ln]) & DCICSR_DTR) && /* DTR change? */ (dco_unit[ln].flags & DCX_MDM)) { /* modem ctl? */ if (data & DCICSR_DTR) { /* setting DTR? */ if (lp->conn) { /* ringing? */ dci_csr[ln] = (dci_csr[ln] & ~DCICSR_RNG) | (DCICSR_CDT|DCICSR_CCH|DCICSR_ERR); dco_csr[ln] |= DCOCSR_CTS; /* set CDT,CCH,CTS */ if (data & CSR_IE) /* if ie, req int */ dci_set_int (ln); } } /* end DTR 0->1 */ else { /* clearing DTR */ if (lp->conn) { /* connected? */ tmxr_linemsg (lp, "\r\nLine hangup\r\n"); tmxr_reset_ln (lp); /* reset line */ if (dci_csr[ln] & DCICSR_CDT) { /* carrier det? */ dci_csr[ln] |= (DCICSR_CCH|DCICSR_ERR); if (data & CSR_IE) /* if ie, req int */ dci_set_int (ln); } } dci_csr[ln] &= ~(DCICSR_CDT|DCICSR_RNG); dco_csr[ln] &= ~DCOCSR_CTS; /* clr CDT,RNG,CTS */ } /* end DTR 1->0 */ } /* end DTR chg+modem */ dci_csr[ln] = (uint16) ((dci_csr[ln] & ~DCICSR_WR) | (data & DCICSR_WR)); return SCPE_OK; case 01: /* dci buf */ return SCPE_OK; case 02: /* dco csr */ if (access == WRITEB) /* byte write? */ data = (PA & 1)? (dco_csr[ln] & 0377) | (data << 8): (dco_csr[ln] & ~0377) | data; if ((data & CSR_IE) == 0) /* clr ie? */ dco_clr_int (ln); /* clr int req */ else if ((dco_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) dco_set_int (ln); dco_csr[ln] = (uint16) ((dco_csr[ln] & ~DCOCSR_WR) | (data & DCOCSR_WR)); return SCPE_OK; case 03: /* dco buf */ if ((PA & 1) == 0) dco_buf[ln] = data & 0377; dco_csr[ln] &= ~CSR_DONE; /* clr done */ dco_clr_int (ln); /* clr int req */ sim_activate (&dco_unit[ln], dco_unit[ln].wait); return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } /* Terminal input service */ t_stat dci_svc (UNIT *uptr) { int32 ln, c, temp; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; sim_activate (uptr, tmxr_poll); /* continue poll */ ln = tmxr_poll_conn (&dcx_desc); /* look for connect */ if (ln >= 0) { /* got one? */ dcx_ldsc[ln].rcve = 1; /* set rcv enb */ if (dco_unit[ln].flags & DCX_MDM) { /* modem control? */ if (dci_csr[ln] & DCICSR_DTR) /* DTR already set? */ dci_csr[ln] |= (DCICSR_CDT|DCICSR_CCH|DCICSR_ERR); else dci_csr[ln] |= (DCICSR_RNG|DCICSR_ERR); /* no, ring */ if (dci_csr[ln] & CSR_IE) /* if ie, */ dci_set_int (ln); /* req int */ } else dco_csr[ln] |= DCOCSR_CTS; /* just connect */ } tmxr_poll_rx (&dcx_desc); /* poll for input */ for (ln = 0; ln < DCX_LINES; ln++) { /* loop thru lines */ if (dcx_ldsc[ln].conn) { /* connected? */ if ((temp = tmxr_getc_ln (&dcx_ldsc[ln])) && /* get char */ !(temp & SCPE_BREAK)) { /* not break? */ c = sim_tt_inpcvt (temp, TT_GET_MODE (dco_unit[ln].flags)); if (dci_csr[ln] & CSR_DONE) /* overrun? */ dci_csr[ln] |= DCICSR_OVR; else dci_csr[ln] |= CSR_DONE; /* set done */ if (dci_csr[ln] & CSR_IE) /* if ie, */ dci_set_int (ln); /* req int */ if (dco_unit[ln].flags & DCX_OPAR) /* odd parity */ c = (c & 0177) | odd_par[c & 0177]; else if (dco_unit[ln].flags & DCX_EPAR) /* even parity */ c = (c & 0177) | (odd_par[c & 0177] ^ 0200); dci_buf[ln] = c; if ((c & 0200) == odd_par[c & 0177]) /* odd par? */ dci_csr[ln] |= DCICSR_PAR; else dci_csr[ln] &= ~DCICSR_PAR; } } else { /* disconnected */ if ((dco_unit[ln].flags & DCX_MDM) && /* modem control? */ (dci_csr[ln] & DCICSR_CDT)) { /* carrier detect? */ dci_csr[ln] |= (DCICSR_CCH|DCICSR_ERR); /* carrier change */ if (dci_csr[ln] & CSR_IE) /* if ie, */ dci_set_int (ln); /* req int */ } dci_csr[ln] &= ~(DCICSR_CDT|DCICSR_RNG); /* clr CDT,RNG,CTS */ dco_csr[ln] &= ~DCOCSR_CTS; } } return SCPE_OK; } /* Terminal output service */ t_stat dco_svc (UNIT *uptr) { int32 c; int32 ln = uptr - dco_unit; /* line # */ if (dcx_ldsc[ln].conn) { /* connected? */ if (dcx_ldsc[ln].xmte) { /* tx enabled? */ TMLN *lp = &dcx_ldsc[ln]; /* get line */ c = sim_tt_outcvt (dco_buf[ln], TT_GET_MODE (dco_unit[ln].flags)); if (c >= 0) /* output char */ tmxr_putc_ln (lp, c); tmxr_poll_tx (&dcx_desc); /* poll xmt */ } else { tmxr_poll_tx (&dcx_desc); /* poll xmt */ sim_activate (uptr, dco_unit[ln].wait); /* wait */ return SCPE_OK; } } dco_csr[ln] |= CSR_DONE; /* set done */ if (dco_csr[ln] & CSR_IE) /* ie set? */ dco_set_int (ln); /* req int */ return SCPE_OK; } /* Interrupt routines */ void dci_clr_int (int32 ln) { dci_ireq &= ~(1 << ln); /* clr mux rcv int */ if (dci_ireq == 0) /* all clr? */ CLR_INT (DCI); else SET_INT (DCI); /* no, set intr */ return; } void dci_set_int (int32 ln) { dci_ireq |= (1 << ln); /* clr mux rcv int */ SET_INT (DCI); /* set master intr */ return; } int32 dci_iack (void) { int32 ln; for (ln = 0; ln < DCX_LINES; ln++) { /* find 1st line */ if (dci_ireq & (1 << ln)) { dci_clr_int (ln); /* clear intr */ return (dci_dib.vec + (ln * 010)); /* return vector */ } } return 0; } void dco_clr_int (int32 ln) { dco_ireq &= ~(1 << ln); /* clr mux rcv int */ if (dco_ireq == 0) /* all clr? */ CLR_INT (DCO); else SET_INT (DCO); /* no, set intr */ return; } void dco_set_int (int32 ln) { dco_ireq |= (1 << ln); /* clr mux rcv int */ SET_INT (DCO); /* set master intr */ return; } int32 dco_iack (void) { int32 ln; for (ln = 0; ln < DCX_LINES; ln++) { /* find 1st line */ if (dco_ireq & (1 << ln)) { dco_clr_int (ln); /* clear intr */ return (dci_dib.vec + (ln * 010) + 4); /* return vector */ } } return 0; } /* Reset */ t_stat dcx_reset (DEVICE *dptr) { int32 ln; dcx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ sim_cancel (&dci_unit); /* assume stop */ if (dci_unit.flags & UNIT_ATT) /* if attached, */ sim_activate (&dci_unit, tmxr_poll); /* activate */ for (ln = 0; ln < DCX_LINES; ln++) /* for all lines */ dcx_reset_ln (ln); return auto_config (dci_dev.name, dcx_desc.lines); /* auto config */ } /* Reset individual line */ void dcx_reset_ln (int32 ln) { dci_buf[ln] = 0; /* clear buf */ dci_csr[ln] = 0; dco_buf[ln] = 0; /* clear buf */ dco_csr[ln] = CSR_DONE; sim_cancel (&dco_unit[ln]); /* deactivate */ dci_clr_int (ln); dco_clr_int (ln); return; } /* Attach master unit */ t_stat dcx_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&dcx_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error? */ return r; sim_activate (uptr, tmxr_poll); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat dcx_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&dcx_desc, uptr); /* detach */ for (i = 0; i < DCX_LINES; i++) /* all lines, */ dcx_ldsc[i].rcve = 0; /* disable rcv */ sim_cancel (uptr); /* stop poll */ return r; } /* Enable/disable device */ void dcx_enbdis (int32 dis) { if (dis) { dci_dev.flags = dci_dev.flags | DEV_DIS; dco_dev.flags = dco_dev.flags | DEV_DIS; } else { dci_dev.flags = dci_dev.flags & ~DEV_DIS; dco_dev.flags = dco_dev.flags & ~DEV_DIS; } return; } /* Change number of lines */ t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, DCX_LINES, &r); if ((r != SCPE_OK) || (newln == dcx_desc.lines)) return r; if (newln == 0) return SCPE_ARG; if (newln < dcx_desc.lines) { for (i = newln, t = 0; i < dcx_desc.lines; i++) t = t | dcx_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < dcx_desc.lines; i++) { if (dcx_ldsc[i].conn) { tmxr_linemsg (&dcx_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&dcx_ldsc[i]); /* reset line */ } dco_unit[i].flags |= UNIT_DIS; dcx_reset_ln (i); } } else { for (i = dcx_desc.lines; i < newln; i++) { dco_unit[i].flags &= ~UNIT_DIS; dcx_reset_ln (i); } } dcx_desc.lines = newln; dci_dib.lnt = newln * 010; /* upd IO page lnt */ return auto_config (dci_dev.name, newln); /* auto config */ } simh-3.8.1/PDP11/txt2cbn.c0000644000175000017500000000205010432630264013153 0ustar vlmvlm#include #define ERROR 00404 #include "pdp11_cr_dat.h" static int colStart = 1; /* starting column */ static int colEnd = 80; /* ending column */ main () { int col, c; while (1) { for (col = colStart; col <= colEnd; ) { switch (c = fgetc (stdin)) { case EOF: /* fall through */ case '\n': while (col <= colEnd) { fputc (o29_code[' '] & 077, stdout); fputc ((o29_code[' '] >> 6) & 077, stdout); col++; } break; case '\t': do { fputc (o29_code[' '] & 077, stdout); fputc ((o29_code[' '] >> 6) & 077, stdout); col++; } while (((col & 07) != 1) && (col <= colEnd)); break; default: fputc (o29_code[c] & 077, stdout); fputc ((o29_code[c] >> 6) & 077, stdout); col++; break; } } /* flush long lines, or flag over-length card */ if (c != '\n' && c != EOF) { printf ("overlength line\n"); do c = fgetc (stdin); while ((c != EOF) && (c != '\n')); } if (c == EOF) break; } exit (1); } simh-3.8.1/PDP11/pdp11_defs.h0000644000175000017500000011227511111051070013520 0ustar vlmvlm/* pdp11_defs.h: PDP-11 simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. The author gratefully acknowledges the help of Max Burnet, Megan Gentry, and John Wilson in resolving questions about the PDP-11 19-Nov-08 RMS Moved I/O support routines to I/O library 16-May-08 RMS Added KE11A, DC11 support 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) 25-Jan-08 RMS Added RC11, KG11A support (from John Dundas) 16-Dec-06 RMS Added TA11 support 29-Oct-06 RMS Added clock coscheduling 06-Jul-06 RMS Added multiple KL11/DL11 support 26-Jun-06 RMS Added RF11 support 24-May-06 RMS Added 11/44 DR support (from CIS diagnostic) 17-May-06 RMS Added CR11/CD11 support (from John Dundas) 30-Sep-04 RMS Added Massbus support Removed Map_Addr prototype Removed map argument from Unibus routines Added framework for model selection 28-May-04 RMS Added DHQ support 25-Jan-04 RMS Removed local debug logging support 22-Dec-03 RMS Added second DEUNA/DELUA support 18-Oct-03 RMS Added DECtape off reel message 19-May-03 RMS Revised for new conditional compilation 05-Apr-03 RMS Fixed bug in MMR1 update (found by Tim Stark) 28-Feb-03 RMS Added TM logging support 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict 11-Nov-02 RMS Changed log definitions to be VAX compatible 10-Oct-02 RMS Added vector information to DIB Changed DZ11 vector to Unibus standard Added DEQNA/DELQA, DEUNA/DELUA support Added multiple RQDX3, autoconfigure support 12-Sep-02 RMS Added TMSCP, KW11P,and RX211 support 28-Apr-02 RMS Clarified PDF ACF mnemonics 22-Apr-02 RMS Added HTRAP, BPOK maint register flags, MT_MAXFR 06-Mar-02 RMS Changed system type to KDJ11A 20-Jan-02 RMS Added multiboard DZ11 support 09-Nov-01 RMS Added bus map support 07-Nov-01 RMS Added RQDX3 support 26-Oct-01 RMS Added symbolic definitions for IO page 19-Oct-01 RMS Added DZ definitions 15-Oct-01 RMS Added logging capabilities 07-Sep-01 RMS Revised for multilevel interrupts 01-Jun-01 RMS Added DZ11 support 23-Apr-01 RMS Added RK611 support 05-Apr-01 RMS Added TS11/TSV05 support 10-Feb-01 RMS Added DECtape support */ #ifndef _PDP11_DEFS_H #define _PDP11_DEFS_H 0 #ifndef VM_PDP11 #define VM_PDP11 0 #endif #include "sim_defs.h" /* simulator defns */ #include /* Architectural constants */ #define STKL_R 0340 /* stack limit */ #define STKL_Y 0400 #define VASIZE 0200000 /* 2**16 */ #define VAMASK (VASIZE - 1) /* 2**16 - 1 */ #define MEMSIZE64K 0200000 /* 2**16 */ #define INIMEMSIZE 001000000 /* 2**18 */ #define UNIMEMSIZE 001000000 /* 2**18 */ #define UNIMASK (UNIMEMSIZE - 1) /* 2**18 - 1 */ #define IOPAGEBASE 017760000 /* 2**22 - 2**13 */ #define IOPAGESIZE 000020000 /* 2**13 */ #define IOPAGEMASK (IOPAGESIZE - 1) /* 2**13 - 1 */ #define MAXMEMSIZE 020000000 /* 2**22 */ #define PAMASK (MAXMEMSIZE - 1) /* 2**22 - 1 */ #define MEMSIZE (cpu_unit.capac) #define ADDR_IS_MEM(x) (((t_addr) (x)) < cpu_memsize) /* use only in sim! */ #define DMASK 0177777 /* CPU models */ #define MOD_1103 0 #define MOD_1104 1 #define MOD_1105 2 #define MOD_1120 3 #define MOD_1123 4 #define MOD_1123P 5 #define MOD_1124 6 #define MOD_1134 7 #define MOD_1140 8 #define MOD_1144 9 #define MOD_1145 10 #define MOD_1160 11 #define MOD_1170 12 #define MOD_1173 13 #define MOD_1153 14 #define MOD_1173B 15 #define MOD_1183 16 #define MOD_1184 17 #define MOD_1193 18 #define MOD_1194 19 #define MOD_T 20 #define CPUT_03 (1u << MOD_1103) /* LSI-11 */ #define CPUT_04 (1u << MOD_1104) /* 11/04 */ #define CPUT_05 (1u << MOD_1105) /* 11/05 */ #define CPUT_20 (1u << MOD_1120) /* 11/20 */ #define CPUT_23 (1u << MOD_1123) /* 11/23 */ #define CPUT_23P (1u << MOD_1123P) /* 11/23+ */ #define CPUT_24 (1u << MOD_1124) /* 11/24 */ #define CPUT_34 (1u << MOD_1134) /* 11/34 */ #define CPUT_40 (1u << MOD_1140) /* 11/40 */ #define CPUT_44 (1u << MOD_1144) /* 11/44 */ #define CPUT_45 (1u << MOD_1145) /* 11/45 */ #define CPUT_60 (1u << MOD_1160) /* 11/60 */ #define CPUT_70 (1u << MOD_1170) /* 11/70 */ #define CPUT_73 (1u << MOD_1173) /* 11/73 */ #define CPUT_53 (1u << MOD_1153) /* 11/53 */ #define CPUT_73B (1u << MOD_1173B) /* 11/73B */ #define CPUT_83 (1u << MOD_1183) /* 11/83 */ #define CPUT_84 (1u << MOD_1184) /* 11/84 */ #define CPUT_93 (1u << MOD_1193) /* 11/93 */ #define CPUT_94 (1u << MOD_1194) /* 11/94 */ #define CPUT_T (1u << MOD_T) /* T-11 */ #define CPUT_F (CPUT_23|CPUT_23P|CPUT_24) /* all F11's */ #define CPUT_J (CPUT_53|CPUT_73|CPUT_73B| \ CPUT_83|CPUT_84|CPUT_93|CPUT_94) #define CPUT_JB (CPUT_73B|CPUT_83|CPUT_84) /* KDJ11B */ #define CPUT_JE (CPUT_93|CPUT_94) /* KDJ11E */ #define CPUT_JU (CPUT_84|CPUT_94) /* KTJ11B UBA */ #define CPUT_ALL 0xFFFFFFFF /* CPU options */ #define BUS_U (1u << 0) /* Unibus */ #define BUS_Q (0) /* Qbus */ #define OPT_EIS (1u << 1) /* EIS */ #define OPT_FIS (1u << 2) /* FIS */ #define OPT_FPP (1u << 3) /* FPP */ #define OPT_CIS (1u << 4) /* CIS */ #define OPT_MMU (1u << 5) /* MMU */ #define OPT_RH11 (1u << 6) /* RH11 */ #define OPT_PAR (1u << 7) /* parity */ #define OPT_UBM (1u << 8) /* UBM */ #define CPUT(x) ((cpu_type & (x)) != 0) #define CPUO(x) ((cpu_opt & (x)) != 0) #define UNIBUS (cpu_opt & BUS_U) /* Feature sets SDSD source addr, dest addr, source fetch, dest fetch SR switch register DR display register RTT RTT instruction SXS SXT, XOR, SOB instructions MARK MARK instruction SPL SPL instruction MXPY MTPI, MTPD, MFPI, MFPD instructions MXPS MTPS, MFPS instructions MFPT MFPT instruction CSM CSM instruction TSWLK TSTSET, WRLCK instructions PSW PSW register EXPT explicit PSW writes can alter T-bit IOSR general registers readable from programs in IO space 2REG dual register set MMR3 MMR3 register MMTR mem mgt traps STKLR STKLIM register STKLF fixed stack limit SID supervisor mode, I/D spaces ODD odd address trap HALT4 halt in kernel mode traps to 4 JREG4 JMP/JSR R traps to 4 STKA stop on stack abort LTCR LTC CSR LTCM LTC CSR<7> */ #define IS_SDSD (CPUT_20|CPUT_F|CPUT_40|CPUT_60|CPUT_J|CPUT_T) #define HAS_SR (CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_40| \ CPUT_44|CPUT_45|CPUT_60|CPUT_70) #define HAS_DR (CPUT_04|CPUT_05|CPUT_20|CPUT_24|CPUT_34| \ CPUT_40|CPUT_44|CPUT_45|CPUT_60|CPUT_70) #define HAS_RTT (CPUT_03|CPUT_04|CPUT_F|CPUT_34|CPUT_40| \ CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J|CPUT_T) #define HAS_SXS (CPUT_03|CPUT_F|CPUT_34|CPUT_40|CPUT_44| \ CPUT_45|CPUT_60|CPUT_70|CPUT_J|CPUT_T) #define HAS_MARK (CPUT_03|CPUT_F|CPUT_34|CPUT_40|CPUT_44| \ CPUT_45|CPUT_60|CPUT_70|CPUT_J) #define HAS_SPL (CPUT_44|CPUT_45|CPUT_70|CPUT_J) #define HAS_MXPY (CPUT_F|CPUT_34|CPUT_40|CPUT_44|CPUT_45| \ CPUT_60|CPUT_70|CPUT_J) #define HAS_MXPS (CPUT_03|CPUT_F|CPUT_34|CPUT_J|CPUT_T) #define HAS_MFPT (CPUT_F|CPUT_44|CPUT_J|CPUT_T) #define HAS_CSM (CPUT_44|CPUT_J) #define HAS_TSWLK (CPUT_J) #define HAS_PSW (CPUT_04|CPUT_05|CPUT_20|CPUT_F|CPUT_34|CPUT_40| \ CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) #define HAS_EXPT (CPUT_04|CPUT_05|CPUT_20) #define HAS_IOSR (CPUT_04|CPUT_05) #define HAS_2REG (CPUT_45|CPUT_70|CPUT_J) #define HAS_MMR3 (CPUT_F|CPUT_44|CPUT_45|CPUT_70|CPUT_J) #define HAS_MMTR (CPUT_45|CPUT_70) #define HAS_STKLR (CPUT_45|CPUT_60|CPUT_70) #define HAS_STKLF (CPUT_04|CPUT_05|CPUT_20|CPUT_F|CPUT_34| \ CPUT_40|CPUT_44|CPUT_J) #define HAS_SID (CPUT_44|CPUT_45|CPUT_70|CPUT_J) #define HAS_ODD (CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_40| \ CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) #define HAS_HALT4 (CPUT_44|CPUT_45|CPUT_70|CPUT_J) #define HAS_JREG4 (CPUT_03|CPUT_04|CPUT_05|CPUT_20|CPUT_F| \ CPUT_34|CPUT_40|CPUT_60|CPUT_T) #define STOP_STKA (CPUT_03|CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_44) #define HAS_LTCR (CPUT_04|CPUT_05|CPUT_20|CPUT_23P|CPUT_24| \ CPUT_34|CPUT_40|CPUT_44|CPUT_45|CPUT_60| \ CPUT_70|CPUT_J) #define HAS_LTCM (CPUT_04|CPUT_05|CPUT_20|CPUT_24|CPUT_34| \ CPUT_40|CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) /* Protection modes */ #define MD_KER 0 #define MD_SUP 1 #define MD_UND 2 #define MD_USR 3 /* I/O access modes */ #define READ 0 #define READC 1 /* read console */ #define WRITE 2 #define WRITEC 3 /* write console */ #define WRITEB 4 /* PSW */ #define PSW_V_C 0 /* condition codes */ #define PSW_V_V 1 #define PSW_V_Z 2 #define PSW_V_N 3 #define PSW_V_TBIT 4 /* trace trap */ #define PSW_V_IPL 5 /* int priority */ #define PSW_V_FPD 8 /* first part done */ #define PSW_V_RS 11 /* register set */ #define PSW_V_PM 12 /* previous mode */ #define PSW_V_CM 14 /* current mode */ #define PSW_CC 017 #define PSW_TBIT (1 << PSW_V_TBIT) #define PSW_PM (3 << PSW_V_PM) /* FPS */ #define FPS_V_C 0 /* condition codes */ #define FPS_V_V 1 #define FPS_V_Z 2 #define FPS_V_N 3 #define FPS_V_T 5 /* truncate */ #define FPS_V_L 6 /* long */ #define FPS_V_D 7 /* double */ #define FPS_V_IC 8 /* ic err int */ #define FPS_V_IV 9 /* overflo err int */ #define FPS_V_IU 10 /* underflo err int */ #define FPS_V_IUV 11 /* undef var err int */ #define FPS_V_ID 14 /* int disable */ #define FPS_V_ER 15 /* error */ /* PIRQ */ #define PIRQ_PIR1 0001000 #define PIRQ_PIR2 0002000 #define PIRQ_PIR3 0004000 #define PIRQ_PIR4 0010000 #define PIRQ_PIR5 0020000 #define PIRQ_PIR6 0040000 #define PIRQ_PIR7 0100000 #define PIRQ_IMP 0177356 /* implemented bits */ #define PIRQ_RW 0177000 /* read/write bits */ /* STKLIM */ #define STKLIM_RW 0177400 /* MMR0 */ #define MMR0_MME 0000001 /* mem mgt enable */ #define MMR0_V_PAGE 1 /* offset to pageno */ #define MMR0_M_PAGE 077 /* mask for pageno */ #define MMR0_PAGE (MMR0_M_PAGE << MMR0_V_PAGE) #define MMR0_IC 0000200 /* instr complete */ #define MMR0_MAINT 0000400 /* maintenance */ #define MMR0_TENB 0001000 /* trap enable */ #define MMR0_TRAP 0010000 /* mem mgt trap */ #define MMR0_RO 0020000 /* read only error */ #define MMR0_PL 0040000 /* page lnt error */ #define MMR0_NR 0100000 /* no access error */ #define MMR0_FREEZE 0160000 /* if set, no update */ #define MMR0_WR 0171401 /* writeable bits */ /* MMR3 */ #define MMR3_UDS 001 /* user dspace enbl */ #define MMR3_SDS 002 /* super dspace enbl */ #define MMR3_KDS 004 /* krnl dspace enbl */ #define MMR3_CSM 010 /* CSM enable */ #define MMR3_M22E 020 /* 22b mem mgt enbl */ #define MMR3_BME 040 /* DMA bus map enbl */ /* PAR */ #define PAR_18B 0007777 /* 18b addressing */ #define PAR_22B 0177777 /* 22b addressing */ /* PDR */ #define PDR_ACF 0000007 /* access control */ #define PDR_ACS 0000006 /* 2b access control */ #define PDR_ED 0000010 /* expansion dir */ #define PDR_W 0000100 /* written flag */ #define PDR_A 0000200 /* access flag */ #define PDR_PLF 0077400 /* page lnt field */ #define PDR_NOC 0100000 /* don't cache */ #define PDR_PRD 0000003 /* page readable if 2 */ /* Virtual address */ #define VA_DF 0017777 /* displacement */ #define VA_BN 0017700 /* block number */ #define VA_V_APF 13 /* offset to APF */ #define VA_V_DS 16 /* offset to space */ #define VA_V_MODE 17 /* offset to mode */ #define VA_DS (1u << VA_V_DS) /* data space flag */ /* Unibus map (if present) */ #define UBM_LNT_LW 32 /* size in LW */ #define UBM_V_PN 13 /* page number */ #define UBM_M_PN 037 #define UBM_V_OFF 0 /* offset */ #define UBM_M_OFF 017777 #define UBM_PAGSIZE (UBM_M_OFF + 1) /* page size */ #define UBM_GETPN(x) (((x) >> UBM_V_PN) & UBM_M_PN) #define UBM_GETOFF(x) ((x) & UBM_M_OFF) /* CPUERR */ #define CPUE_RED 0004 /* red stack */ #define CPUE_YEL 0010 /* yellow stack */ #define CPUE_TMO 0020 /* IO page nxm */ #define CPUE_NXM 0040 /* memory nxm */ #define CPUE_ODD 0100 /* odd address */ #define CPUE_HALT 0200 /* HALT not kernel */ #define CPUE_IMP 0374 /* implemented bits */ /* Floating point accumulators */ typedef struct { uint32 l; /* low 32b */ uint32 h; /* high 32b */ } fpac_t; /* Device CSRs */ #define CSR_V_GO 0 /* go */ #define CSR_V_IE 6 /* interrupt enable */ #define CSR_V_DONE 7 /* done */ #define CSR_V_BUSY 11 /* busy */ #define CSR_V_ERR 15 /* error */ #define CSR_GO (1u << CSR_V_GO) #define CSR_IE (1u << CSR_V_IE) #define CSR_DONE (1u << CSR_V_DONE) #define CSR_BUSY (1u << CSR_V_BUSY) #define CSR_ERR (1u << CSR_V_ERR) /* Trap masks, descending priority order, following J-11 An interrupt summary bit is kept with traps, to minimize overhead */ #define TRAP_V_RED 0 /* red stk abort 4 */ #define TRAP_V_ODD 1 /* odd address 4 */ #define TRAP_V_MME 2 /* mem mgt 250 */ #define TRAP_V_NXM 3 /* nx memory 4 */ #define TRAP_V_PAR 4 /* parity err 114 */ #define TRAP_V_PRV 5 /* priv inst 4 */ #define TRAP_V_ILL 6 /* illegal inst 10 */ #define TRAP_V_BPT 7 /* BPT 14 */ #define TRAP_V_IOT 8 /* IOT 20 */ #define TRAP_V_EMT 9 /* EMT 30 */ #define TRAP_V_TRAP 10 /* TRAP 34 */ #define TRAP_V_TRC 11 /* T bit 14 */ #define TRAP_V_YEL 12 /* stack 4 */ #define TRAP_V_PWRFL 13 /* power fail 24 */ #define TRAP_V_FPE 14 /* fpe 244 */ #define TRAP_V_MAX 15 /* intr = max trp # */ #define TRAP_RED (1u << TRAP_V_RED) #define TRAP_ODD (1u << TRAP_V_ODD) #define TRAP_MME (1u << TRAP_V_MME) #define TRAP_NXM (1u << TRAP_V_NXM) #define TRAP_PAR (1u << TRAP_V_PAR) #define TRAP_PRV (1u << TRAP_V_PRV) #define TRAP_ILL (1u << TRAP_V_ILL) #define TRAP_BPT (1u << TRAP_V_BPT) #define TRAP_IOT (1u << TRAP_V_IOT) #define TRAP_EMT (1u << TRAP_V_EMT) #define TRAP_TRAP (1u << TRAP_V_TRAP) #define TRAP_TRC (1u << TRAP_V_TRC) #define TRAP_YEL (1u << TRAP_V_YEL) #define TRAP_PWRFL (1u << TRAP_V_PWRFL) #define TRAP_FPE (1u << TRAP_V_FPE) #define TRAP_INT (1u << TRAP_V_MAX) #define TRAP_ALL ((1u << TRAP_V_MAX) - 1) /* all traps */ #define VEC_RED 0004 /* trap vectors */ #define VEC_ODD 0004 #define VEC_MME 0250 #define VEC_NXM 0004 #define VEC_PAR 0114 #define VEC_PRV 0004 #define VEC_ILL 0010 #define VEC_BPT 0014 #define VEC_IOT 0020 #define VEC_EMT 0030 #define VEC_TRAP 0034 #define VEC_TRC 0014 #define VEC_YEL 0004 #define VEC_PWRFL 0024 #define VEC_FPE 0244 /* Simulator stop codes; codes 1:TRAP_V_MAX correspond to traps 0:TRAPMAX-1 */ #define STOP_HALT (TRAP_V_MAX + 1) /* HALT instruction */ #define STOP_IBKPT (TRAP_V_MAX + 2) /* instruction bkpt */ #define STOP_WAIT (TRAP_V_MAX + 3) /* wait, no events */ #define STOP_VECABORT (TRAP_V_MAX + 4) /* abort vector read */ #define STOP_SPABORT (TRAP_V_MAX + 5) /* abort trap push */ #define STOP_RQ (TRAP_V_MAX + 6) /* RQDX3 panic */ #define STOP_SANITY (TRAP_V_MAX + 7) /* sanity timer exp */ #define STOP_DTOFF (TRAP_V_MAX + 8) /* DECtape off reel */ #define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ /* Timers */ #define TMR_CLK 0 /* line clock */ #define TMR_PCLK 1 /* KW11P */ /* IO parameters */ #define DZ_MUXES 4 /* max # of DZ muxes */ #define DZ_LINES 8 /* lines per DZ mux */ #define VH_MUXES 4 /* max # of VH muxes */ #define DLX_LINES 16 /* max # of KL11/DL11's */ #define DCX_LINES 16 /* max # of DC11's */ #define MT_MAXFR (1 << 16) /* magtape max rec */ #define AUTO_LNT 34 /* autoconfig ranks */ #define DIB_MAX 100 /* max DIBs */ #define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ #define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ #define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus with <= 256KB */ #define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ #define DEV_V_MBUS (DEV_V_UF + 4) /* Massbus */ #define DEV_V_FFUF (DEV_V_UF + 5) /* first free flag */ #define DEV_UBUS (1u << DEV_V_UBUS) #define DEV_QBUS (1u << DEV_V_QBUS) #define DEV_Q18 (1u << DEV_V_Q18) #define DEV_FLTA (1u << DEV_V_FLTA) #define DEV_MBUS (1u << DEV_V_MBUS) #define DEV_RDX 8 /* default device radix */ /* Device information block */ #define VEC_DEVMAX 4 /* max device vec */ struct pdp_dib { uint32 ba; /* base addr */ uint32 lnt; /* length */ t_stat (*rd)(int32 *dat, int32 ad, int32 md); t_stat (*wr)(int32 dat, int32 ad, int32 md); int32 vnum; /* vectors: number */ int32 vloc; /* locator */ int32 vec; /* value */ int32 (*ack[VEC_DEVMAX])(void); /* ack routines */ }; typedef struct pdp_dib DIB; /* I/O page layout - XUB, RQB,RQC,RQD float based on number of DZ's */ #define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ #define IOLN_DZ 010 #define IOBA_XUB (IOPAGEBASE + 000330 + (020 * (DZ_MUXES / 2))) #define IOLN_XUB 010 #define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) #define IOLN_RQB 004 #define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) #define IOLN_RQC 004 #define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) #define IOLN_RQD 004 #define IOBA_VH (IOPAGEBASE + 000440) /* DHQ11 */ #define IOLN_VH 020 #define IOBA_UBM (IOPAGEBASE + 010200) /* Unibus map */ #define IOLN_UBM (UBM_LNT_LW * sizeof (int32)) #define IOBA_KG (IOPAGEBASE + 010700) /* KG11-A */ #define IOLN_KG 006 #define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */ #define IOLN_RQ 004 #define IOBA_SUP (IOPAGEBASE + 012200) /* supervisor APR's */ #define IOLN_SUP 0100 #define IOBA_KIPDR (IOPAGEBASE + 012300) /* kernel APR's */ #define IOLN_KIPDR 020 #define IOBA_KDPDR (IOPAGEBASE + 012320) #define IOLN_KDPDR 020 #define IOBA_KIPAR (IOPAGEBASE + 012340) #define IOLN_KIPAR 020 #define IOBA_KDPAR (IOPAGEBASE + 012360) #define IOLN_KDPAR 020 #define IOBA_TU (IOPAGEBASE + 012440) /* TU */ #define IOLN_TU 040 #define IOBA_MMR3 (IOPAGEBASE + 012516) /* MMR3 */ #define IOLN_MMR3 002 #define IOBA_TM (IOPAGEBASE + 012520) /* TM11 */ #define IOLN_TM 014 #define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ #define IOLN_TS 004 #define IOBA_PCLK (IOPAGEBASE + 012540) /* KW11P */ #define IOLN_PCLK 006 #define IOBA_DC (IOPAGEBASE + 014000) /* DC11 */ #define IOLN_DC (DCX_LINES * 010) #define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ #define IOLN_RL 012 #define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ #define IOLN_XQ 020 #define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ #define IOLN_XQB 020 #define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ #define IOLN_TQ 004 #define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ #define IOLN_XU 010 #define IOBA_DL (IOPAGEBASE + 016500) /* extra KL11/DL11 */ #define IOLN_DL (DLX_LINES * 010) #define IOBA_RP (IOPAGEBASE + 016700) /* RP/RM */ #define IOLN_RP 054 #define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ #define IOLN_CR 010 #define IOBA_RX (IOPAGEBASE + 017170) /* RX11 */ #define IOLN_RX 004 #define IOBA_RY (IOPAGEBASE + 017170) /* RY11 */ #define IOLN_RY 004 #define IOBA_KE (IOPAGEBASE + 017300) /* KE11-A */ #define IOLN_KE 020 #define IOBA_TC (IOPAGEBASE + 017340) /* TC11 */ #define IOLN_TC 012 #define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ #define IOLN_QDSS 002 #define IOBA_RK (IOPAGEBASE + 017400) /* RK11 */ #define IOLN_RK 020 #define IOBA_RC (IOPAGEBASE + 017440) /* RC11/RS64 */ #define IOLN_RC 020 #define IOBA_HK (IOPAGEBASE + 017440) /* RK611 */ #define IOLN_HK 040 #define IOBA_RF (IOPAGEBASE + 017460) /* RF11 */ #define IOLN_RF 020 #define IOBA_TA (IOPAGEBASE + 017500) /* TA11 */ #define IOLN_TA 004 #define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ #define IOLN_LPT 004 #define IOBA_CTL (IOPAGEBASE + 017520) /* board ctrl */ #define IOLN_CTL 010 #define IOBA_CLK (IOPAGEBASE + 017546) /* KW11L */ #define IOLN_CLK 002 #define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ #define IOLN_PTR 004 #define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ #define IOLN_PTP 004 #define IOBA_TTI (IOPAGEBASE + 017560) /* DL11 rcv */ #define IOLN_TTI 004 #define IOBA_TTO (IOPAGEBASE + 017564) /* DL11 xmt */ #define IOLN_TTO 004 #define IOBA_SR (IOPAGEBASE + 017570) /* SR */ #define IOLN_SR 002 #define IOBA_MMR012 (IOPAGEBASE + 017572) /* MMR0-2 */ #define IOLN_MMR012 006 #define IOBA_UIPDR (IOPAGEBASE + 017600) /* user APR's */ #define IOLN_UIPDR 020 #define IOBA_UDPDR (IOPAGEBASE + 017620) #define IOLN_UDPDR 020 #define IOBA_UIPAR (IOPAGEBASE + 017640) #define IOLN_UIPAR 020 #define IOBA_UDPAR (IOPAGEBASE + 017660) #define IOLN_UDPAR 020 #define IOBA_GPR (IOPAGEBASE + 017700) /* GPR's */ #define IOLN_GPR 010 #define IOBA_UCTL (IOPAGEBASE + 017730) /* UBA ctrl */ #define IOLN_UCTL 010 #define IOBA_CPU (IOPAGEBASE + 017740) /* CPU reg */ #define IOLN_CPU 036 #define IOBA_PSW (IOPAGEBASE + 017776) /* PSW */ #define IOLN_PSW 002 /* Interrupt assignments; within each level, priority is right to left */ #define IPL_HLVL 8 /* # int levels */ #define INT_V_PIR7 0 /* BR7 */ #define INT_V_CLK 0 /* BR6 */ #define INT_V_PCLK 1 #define INT_V_DTA 2 #define INT_V_TA 3 #define INT_V_PIR6 4 #define INT_V_RK 0 /* BR5 */ #define INT_V_RL 1 #define INT_V_RX 2 #define INT_V_TM 3 #define INT_V_RP 4 #define INT_V_TS 5 #define INT_V_HK 6 #define INT_V_RQ 7 #define INT_V_DZRX 8 #define INT_V_DZTX 9 #define INT_V_TQ 10 #define INT_V_RY 11 #define INT_V_XQ 12 #define INT_V_XU 13 #define INT_V_TU 14 #define INT_V_RF 15 #define INT_V_RC 16 #define INT_V_PIR5 17 #define INT_V_TTI 0 /* BR4 */ #define INT_V_TTO 1 #define INT_V_PTR 2 #define INT_V_PTP 3 #define INT_V_LPT 4 #define INT_V_VHRX 5 #define INT_V_VHTX 6 #define INT_V_CR 7 #define INT_V_DLI 8 #define INT_V_DLO 9 #define INT_V_DCI 10 #define INT_V_DCO 11 #define INT_V_PIR4 12 #define INT_V_PIR3 0 /* BR3 */ #define INT_V_PIR2 0 /* BR2 */ #define INT_V_PIR1 0 /* BR1 */ #define INT_PIR7 (1u << INT_V_PIR7) #define INT_CLK (1u << INT_V_CLK) #define INT_PCLK (1u << INT_V_PCLK) #define INT_DTA (1u << INT_V_DTA) #define INT_TA (1u << INT_V_TA) #define INT_PIR6 (1u << INT_V_PIR6) #define INT_RK (1u << INT_V_RK) #define INT_RL (1u << INT_V_RL) #define INT_RX (1u << INT_V_RX) #define INT_TM (1u << INT_V_TM) #define INT_RP (1u << INT_V_RP) #define INT_TS (1u << INT_V_TS) #define INT_HK (1u << INT_V_HK) #define INT_RQ (1u << INT_V_RQ) #define INT_DZRX (1u << INT_V_DZRX) #define INT_DZTX (1u << INT_V_DZTX) #define INT_TQ (1u << INT_V_TQ) #define INT_RY (1u << INT_V_RY) #define INT_XQ (1u << INT_V_XQ) #define INT_XU (1u << INT_V_XU) #define INT_TU (1u << INT_V_TU) #define INT_RF (1u << INT_V_RF) #define INT_RC (1u << INT_V_RC) #define INT_PIR5 (1u << INT_V_PIR5) #define INT_PTR (1u << INT_V_PTR) #define INT_PTP (1u << INT_V_PTP) #define INT_TTI (1u << INT_V_TTI) #define INT_TTO (1u << INT_V_TTO) #define INT_LPT (1u << INT_V_LPT) #define INT_VHRX (1u << INT_V_VHRX) #define INT_VHTX (1u << INT_V_VHTX) #define INT_CR (1u << INT_V_CR) #define INT_DLI (1u << INT_V_DLI) #define INT_DLO (1u << INT_V_DLO) #define INT_DCI (1u << INT_V_DCI) #define INT_DCO (1u << INT_V_DCO) #define INT_PIR4 (1u << INT_V_PIR4) #define INT_PIR3 (1u << INT_V_PIR3) #define INT_PIR2 (1u << INT_V_PIR2) #define INT_PIR1 (1u << INT_V_PIR1) #define IPL_CLK 6 /* int pri levels */ #define IPL_PCLK 6 #define IPL_DTA 6 #define IPL_TA 6 #define IPL_RK 5 #define IPL_RL 5 #define IPL_RX 5 #define IPL_TM 5 #define IPL_RP 5 #define IPL_TS 5 #define IPL_HK 5 #define IPL_RQ 5 #define IPL_DZRX 5 #define IPL_DZTX 5 #define IPL_TQ 5 #define IPL_RY 5 #define IPL_XQ 5 #define IPL_XU 5 #define IPL_TU 5 #define IPL_RF 5 #define IPL_RC 5 #define IPL_PTR 4 #define IPL_PTP 4 #define IPL_TTI 4 #define IPL_TTO 4 #define IPL_LPT 4 #define IPL_VHRX 4 #define IPL_VHTX 4 #define IPL_CR 4 #define IPL_DLI 4 #define IPL_DLO 4 #define IPL_DCI 4 #define IPL_DCO 4 #define IPL_PIR7 7 #define IPL_PIR6 6 #define IPL_PIR5 5 #define IPL_PIR4 4 #define IPL_PIR3 3 #define IPL_PIR2 2 #define IPL_PIR1 1 /* Device vectors */ #define VEC_Q 0000 /* vector base */ #define VEC_PIRQ 0240 #define VEC_TTI 0060 #define VEC_TTO 0064 #define VEC_PTR 0070 #define VEC_PTP 0074 #define VEC_CLK 0100 #define VEC_PCLK 0104 #define VEC_XQ 0120 #define VEC_XU 0120 #define VEC_RQ 0154 #define VEC_RL 0160 #define VEC_LPT 0200 #define VEC_RF 0204 #define VEC_HK 0210 #define VEC_RC 0210 #define VEC_RK 0220 #define VEC_DTA 0214 #define VEC_TM 0224 #define VEC_TS 0224 #define VEC_TU 0224 #define VEC_CR 0230 #define VEC_RP 0254 #define VEC_TQ 0260 #define VEC_TA 0260 #define VEC_RX 0264 #define VEC_RY 0264 #define VEC_DLI 0300 #define VEC_DLO 0304 #define VEC_DCI 0300 #define VEC_DCO 0304 #define VEC_DZRX 0300 #define VEC_DZTX 0304 #define VEC_VHRX 0310 #define VEC_VHTX 0314 /* Interrupt macros */ #define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) #define IREQ(dv) int_req[IPL_##dv] #define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) #define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) /* Massbus definitions */ #define MBA_NUM 2 /* number of MBA's */ #define MBA_RP 0 /* MBA for RP */ #define MBA_TU 1 /* MBA for TU */ #define MBA_RMASK 037 /* max 32 reg */ #define MBE_NXD 1 /* nx drive */ #define MBE_NXR 2 /* nx reg */ #define MBE_GOE 3 /* err on GO */ /* CPU and FPU macros */ #define update_MM ((MMR0 & MMR0_FREEZE) == 0) #define setTRAP(name) trap_req = trap_req | (name) #define setCPUERR(name) CPUERR = CPUERR | (name) #define ABORT(val) longjmp (save_env, (val)) #define SP R[6] #define PC R[7] /* Function prototypes */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); int32 mba_rdbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_wrbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_chbufW (uint32 mbus, int32 bc, uint16 *buf); int32 mba_get_bc (uint32 mbus); int32 mba_get_csr (uint32 mbus); void mba_upd_ata (uint32 mbus, uint32 val); void mba_set_exc (uint32 mbus); void mba_set_don (uint32 mbus); void mba_set_enbdis (uint32 mb, t_bool dis); t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc); int32 clk_cosched (int32 wait); #include "pdp11_io_lib.h" #endif simh-3.8.1/PDP11/pdp11_uqssp.h0000644000175000017500000001750311111666660013771 0ustar vlmvlm/* pdp11_uqssp.h: Unibus/Qbus storage systems port definitions file Copyright (c) 2001-2008, Robert M Supnik Derived from work by Stephen F. Shirron Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 30-Aug-02 RMS Added TMSCP support */ #ifndef _PDP11_UQSSP_H_ #define _PDP11_UQSSP_H_ 0 /* IP register - initialization and polling read - controller polls command queue write - controller re-initializes */ /* SA register - status, address, and purge read - data and error information write - host startup information, purge complete */ #define SA_ER 0x8000 /* error */ #define SA_S4 0x4000 /* init step 4 */ #define SA_S3 0x2000 /* init step 3 */ #define SA_S2 0x1000 /* init step 2 */ #define SA_S1 0x0800 /* init step 1 */ /* Init step 1, controller to host */ #define SA_S1C_NV 0x0400 /* fixed vec NI */ #define SA_S1C_Q22 0x0200 /* Q22 device */ #define SA_S1C_DI 0x0100 /* ext diags */ #define SA_S1C_OD 0x0080 /* odd addrs NI */ #define SA_S1C_MP 0x0040 /* mapping */ #define SA_S1C_SM 0x0020 /* spec fncs NI */ #define SA_S1C_CN 0x0010 /* node name NI */ /* Init step 1, host to controller */ #define SA_S1H_VL 0x8000 /* valid */ #define SA_S1H_WR 0x4000 /* wrap mode */ #define SA_S1H_V_CQ 11 /* cmd q len */ #define SA_S1H_M_CQ 0x7 #define SA_S1H_V_RQ 8 /* resp q len */ #define SA_S1H_M_RQ 0x7 #define SA_S1H_IE 0x0080 /* int enb */ #define SA_S1H_VEC 0x007F /* vector */ #define SA_S1H_CQ(x) (1 << (((x) >> SA_S1H_V_CQ) & SA_S1H_M_CQ)) #define SA_S1H_RQ(x) (1 << (((x) >> SA_S1H_V_RQ) & SA_S1H_M_RQ)) /* Init step 2, controller to host */ #define SA_S2C_PT 0x0000 /* port type */ #define SA_S2C_V_EC 8 /* info to echo */ #define SA_S2C_M_EC 0xFF #define SA_S2C_EC(x) (((x) >> SA_S2C_V_EC) & SA_S2C_M_EC) /* Init step 2, host to controller */ #define SA_S2H_CLO 0xFFFE /* comm addr lo */ #define SA_S2H_PI 0x0001 /* adp prg int */ /* Init step 3, controller to host */ #define SA_S3C_V_EC 0 /* info to echo */ #define SA_S3C_M_EC 0xFF #define SA_S3C_EC(x) (((x) >> SA_S3C_V_EC) & SA_S3C_M_EC) /* Init step 3, host to controller */ #define SA_S3H_PP 0x8000 /* purge, poll test */ #define SA_S3H_CHI 0x7FFF /* comm addr hi */ /* Init step 4, controller to host */ #define SA_S4C_V_MOD 4 /* adapter # */ #define SA_S4C_V_VER 0 /* version # */ /* Init step 4, host to controller */ #define SA_S4H_CS 0x0400 /* host scrpad NI */ #define SA_S4H_NN 0x0200 /* snd node name NI */ #define SA_S4H_SF 0x0100 /* spec fnc NI */ #define SA_S4H_LF 0x0002 /* send last fail */ #define SA_S4H_GO 0x0001 /* go */ /* Fatal error codes (generic through 32) */ #define PE_PRE 1 /* packet read err */ #define PE_PWE 2 /* packet write err */ #define PE_QRE 6 /* queue read err */ #define PE_QWE 7 /* queue write err */ #define PE_HAT 9 /* host access tmo */ #define PE_ICI 14 /* inv conn ident */ #define PE_PIE 20 /* prot incompat */ #define PE_PPF 21 /* prg/poll err */ #define PE_MRE 22 /* map reg rd err */ #define PE_T11 475 /* T11 err NI */ #define PE_SND 476 /* SND err NI */ #define PE_RCV 477 /* RCV err NI */ #define PE_NSR 478 /* no such rsrc */ /* Comm region offsets */ #define SA_COMM_QQ -8 /* unused */ #define SA_COMM_PI -6 /* purge int */ #define SA_COMM_CI -4 /* cmd int */ #define SA_COMM_RI -2 /* resp int */ #define SA_COMM_MAX ((4 << SA_S1H_M_CQ) + (4 << SA_S1H_M_RQ) - SA_COMM_QQ) /* Command/response rings */ struct uq_ring { int32 ioff; /* intr offset */ uint32 ba; /* base addr */ uint32 lnt; /* size in bytes */ uint32 idx; /* current index */ }; /* Ring descriptor entry */ #define UQ_DESC_OWN 0x80000000 /* ownership */ #define UQ_DESC_F 0x40000000 /* flag */ #define UQ_ADDR 0x003FFFFE /* addr, word aligned */ /* Packet header */ #define UQ_HDR_OFF -4 /* offset */ #define UQ_HLNT 0 /* length */ #define UQ_HCTC 1 /* credits, type, CID */ #define UQ_HCTC_V_CR 0 /* credits */ #define UQ_HCTC_M_CR 0xF #define UQ_HCTC_V_TYP 4 /* type */ #define UQ_HCTC_M_TYP 0xF #define UQ_TYP_SEQ 0 /* sequential */ #define UQ_TYP_DAT 1 /* datagram */ #define UQ_HCTC_V_CID 8 /* conn ID */ #define UQ_HCTC_M_CID 0xFF #define UQ_CID_MSCP 0 /* MSCP */ #define UQ_CID_TMSCP 1 /* TMSCP */ #define UQ_CID_DUP 2 /* DUP */ #define UQ_CID_DIAG 0xFF /* diagnostic */ #endif simh-3.8.1/PDP11/pdp11_io_lib.c0000644000175000017500000004704211111072376014042 0ustar vlmvlm/* pdp11_io_lib.c: Unibus/Qbus common support routines Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #else /* PDP-11 version */ #include "pdp11_defs.h" #endif #include "sim_sock.h" #include "sim_tmxr.h" extern FILE *sim_log; extern DEVICE *sim_devices[]; extern int32 autcon_enb; extern int32 int_vec[IPL_HLVL][32]; extern int32 (*int_ack[IPL_HLVL][32])(void); extern t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); extern t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); extern t_stat build_dib_tab (void); static DIB *iodibp[IOPAGESIZE >> 1]; /* Enable/disable autoconfiguration */ t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr != NULL) return SCPE_ARG; autcon_enb = val; return auto_config (NULL, 0); } /* Show autoconfiguration status */ t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "autoconfiguration "); fprintf (st, autcon_enb? "enabled": "disabled"); return SCPE_OK; } /* Change device address */ t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newba; t_stat r; if (cptr == NULL) return SCPE_ARG; if ((val == 0) || (uptr == NULL)) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newba = (uint32) get_uint (cptr, DEV_RDX, IOPAGEBASE+IOPAGEMASK, &r); /* get new */ if (r != SCPE_OK) return r; if ((newba <= IOPAGEBASE) || /* > IO page base? */ (newba % ((uint32) val))) /* check modulus */ return SCPE_ARG; dibp->ba = newba; /* store */ dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ autcon_enb = 0; /* autoconfig off */ return SCPE_OK; } /* Show device address */ t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; fprintf (st, "address="); fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); if (dibp->lnt > 1) { fprintf (st, "-"); fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT); } if (dptr->flags & DEV_FLTA) fprintf (st, "*"); return SCPE_OK; } /* Set address floating */ t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr; if (cptr != NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dptr->flags = dptr->flags | DEV_FLTA; /* floating */ return auto_config (NULL, 0); /* autoconfigure */ } /* Change device vector */ t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newvec; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newvec = (uint32) get_uint (cptr, DEV_RDX, VEC_Q + 01000, &r); if ((r != SCPE_OK) || (newvec == VEC_Q) || ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) || (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; dibp->vec = newvec; dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ autcon_enb = 0; /* autoconfig off */ return SCPE_OK; } /* Show device vector */ t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) { DEVICE *dptr; DIB *dibp; uint32 vec, numvec; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; vec = dibp->vec; if (arg) numvec = arg; else numvec = dibp->vnum; if (vec == 0) fprintf (st, "no vector"); else { fprintf (st, "vector="); fprint_val (st, (t_value) vec, DEV_RDX, 16, PV_LEFT); if (numvec > 1) { fprintf (st, "-"); fprint_val (st, (t_value) vec + (4 * (numvec - 1)), DEV_RDX, 16, PV_LEFT); } } return SCPE_OK; } /* Show vector for terminal multiplexor */ t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 arg, void *desc) { TMXR *mp = (TMXR *) desc; if ((mp == NULL) || (arg == 0)) return SCPE_IERR; return show_vec (st, uptr, ((mp->lines * 2) / arg), desc); } /* Init Unibus tables */ void init_ubus_tab (void) { int32 i, j; for (i = 0; i < IPL_HLVL; i++) { /* clear intr tab */ for (j = 0; j < 32; j++) { int_vec[i][j] = 0; int_ack[i][j] = NULL; } } for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */ iodispR[i] = NULL; iodispW[i] = NULL; iodibp[i] = NULL; } return; } /* Build Unibus tables */ t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp) { int32 i, idx, vec, ilvl, ibit; if ((dptr == NULL) || (dibp == NULL)) /* validate args */ return SCPE_IERR; if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */ idx = dibp->vloc + i; /* vector index */ vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */ ilvl = idx / 32; ibit = idx % 32; if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ (int_ack[ilvl][ibit] != dibp->ack[i])) || (int_vec[ilvl][ibit] && vec && (int_vec[ilvl][ibit] != vec))) { printf ("Device %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); if (sim_log) fprintf (sim_log, "Device %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); return SCPE_STOP; } if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i]; else if (vec) int_vec[ilvl][ibit] = vec; } for (i = 0; i < (int32) dibp->lnt; i = i + 2) { /* create entries */ idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */ if ((iodispR[idx] && dibp->rd && /* conflict? */ (iodispR[idx] != dibp->rd)) || (iodispW[idx] && dibp->wr && (iodispW[idx] != dibp->wr))) { printf ("Device %s address conflict at \n", sim_dname (dptr)); fprint_val (stdout, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); if (sim_log) { fprintf (sim_log, "Device %s address conflict at \n", sim_dname (dptr)); fprint_val (sim_log, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); } return SCPE_STOP; } if (dibp->rd) /* set rd dispatch */ iodispR[idx] = dibp->rd; if (dibp->wr) /* set wr dispatch */ iodispW[idx] = dibp->wr; iodibp[idx] = dibp; /* remember DIB */ } return SCPE_OK; } /* Show IO space */ t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 i, j; DEVICE *dptr; DIB *dibp; if (build_dib_tab ()) /* build IO page */ return SCPE_OK; for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */ if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */ dibp = iodibp[i]; /* DIB for block */ for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { if (((DIB*) sim_devices[j]->ctxt) == dibp) { dptr = sim_devices[j]; /* locate device */ break; } /* end if */ } /* end for j */ fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); fprintf (st, " - "); fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT); fprintf (st, "%c\t%s\n", /* print block entry */ (dptr && (dptr->flags & DEV_FLTA))? '*': ' ', dptr? sim_dname (dptr): "CPU"); } /* end if */ } /* end for i */ return SCPE_OK; } /* Autoconfiguration The table reflects the MicroVAX 3900 microcode, with one addition - the number of controllers field handles devices where multiple instances are simulated through a single DEVICE structure (e.g., DZ, VH). A minus number of vectors indicates a field that should be calculated but not placed in the DIB (RQ, TQ dynamic vectors) */ #define AUTO_MAXC 4 #define AUTO_CSRBASE 0010 #define AUTO_VECBASE 0300 typedef struct { char *dnam[AUTO_MAXC]; int32 numc; int32 numv; uint32 amod; uint32 vmod; uint32 fixa[AUTO_MAXC]; uint32 fixv[AUTO_MAXC]; } AUTO_CON; AUTO_CON auto_tab[] = { { { "DCI" }, DCX_LINES, 2, 0, 8, { 0 } }, /* DC11 - fx CSRs */ { { "DLI" }, DLX_LINES, 2, 0, 8, { 0 } }, /* KL11/DL11/DLV11 - fx CSRs */ { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */ { { NULL }, 1, 2, 8, 8 }, /* DJ11 */ { { NULL }, 1, 2, 16, 8 }, /* DH11 */ { { NULL }, 1, 2, 8, 8 }, /* DQ11 */ { { NULL }, 1, 2, 8, 8 }, /* DU11 */ { { NULL }, 1, 2, 8, 8 }, /* DUP11 */ { { NULL }, 10, 2, 8, 8 }, /* LK11A */ { { NULL }, 1, 2, 8, 8 }, /* DMC11 */ { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */ { { NULL }, 1, 2, 8, 8 }, /* KMC11 */ { { NULL }, 1, 2, 8, 8 }, /* LPP11 */ { { NULL }, 1, 2, 8, 8 }, /* VMV21 */ { { NULL }, 1, 2, 16, 8 }, /* VMV31 */ { { NULL }, 1, 2, 8, 8 }, /* DWR70 */ { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */ { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */ {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12}, {VEC_TS} }, { { NULL }, 1, 2, 16, 8 }, /* LPA11K */ { { NULL }, 1, 2, 8, 8 }, /* KW11C */ { { NULL }, 1, 1, 8, 8 }, /* reserved */ { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */ { { NULL }, 1, 1, 8, 4 }, /* DR11W */ { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */ { { NULL }, 1, 2, 8, 8 }, /* DMP11 */ { { NULL }, 1, 2, 8, 8 }, /* DPV11 */ { { NULL }, 1, 2, 8, 8 }, /* ISB11 */ { { NULL }, 1, 2, 16, 8 }, /* DMV11 */ { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */ { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */ {IOBA_XQ,IOBA_XQB}, {VEC_XQ} }, { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */ {IOBA_RQ}, {VEC_RQ} }, { { NULL }, 1, 8, 32, 4 }, /* DMF32 */ { { NULL }, 1, 2, 16, 8 }, /* KMS11 */ { { NULL }, 1, 1, 16, 4 }, /* VS100 */ { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */ { { NULL }, 1, 2, 16, 8 }, /* KMV11 */ { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */ { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */ { { NULL }, 1, 6, 32, 4 }, /* CP132 */ { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */ { { NULL }, 1, 1, 8, 4 }, /* VS31 */ { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */ { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */ { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */ { { NULL }, 1, 1, 8, 4 }, /* DSV11 */ { { NULL }, 1, 2, 8, 8 }, /* CSAM */ { { NULL }, 1, 2, 8, 8 }, /* ADV11C */ { { NULL }, 1, 0, 8, 0 }, /* AAV11C */ { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */ { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */ { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */ { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */ { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */ { { NULL }, -1 } /* end table */ }; t_stat auto_config (char *name, int32 nctrl) { uint32 csr = IOPAGEBASE + AUTO_CSRBASE; uint32 vec = VEC_Q + AUTO_VECBASE; AUTO_CON *autp; DEVICE *dptr; DIB *dibp; uint32 j, k, vmask, amask; if (autcon_enb == 0) /* enabled? */ return SCPE_OK; if (name) { /* updating? */ if (nctrl < 0) return SCPE_ARG; for (autp = auto_tab; autp->numc >= 0; autp++) { for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { if (strcmp (name, autp->dnam[j]) == 0) autp->numc = nctrl; } } } for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */ if (autp->amod) { /* floating csr? */ amask = autp->amod - 1; csr = (csr + amask) & ~amask; /* align csr */ } for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { if (autp->dnam[j] == NULL) /* no device? */ continue; dptr = find_dev (autp->dnam[j]); /* find ctrl */ if ((dptr == NULL) || /* enabled, floating? */ (dptr->flags & DEV_DIS) || !(dptr->flags & DEV_FLTA)) continue; dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp == NULL) /* not there??? */ return SCPE_IERR; if (autp->amod) { /* dyn csr needed? */ if (autp->fixa[k]) /* fixed csr avail? */ dibp->ba = autp->fixa[k]; /* use it */ else { /* no fixed left */ dibp->ba = csr; /* set CSR */ csr += (autp->numc * autp->amod); /* next CSR */ } /* end else */ } /* end if dyn csr */ if (autp->numv && autp->vmod) { /* dyn vec needed? */ uint32 numv = abs (autp->numv); /* get num vec */ if (autp->fixv[k]) { /* fixed vec avail? */ if (autp->numv > 0) dibp->vec = autp->fixv[k]; /* use it */ } else { /* no fixed left */ vmask = autp->vmod - 1; vec = (vec + vmask) & ~vmask; /* align vector */ if (autp->numv > 0) dibp->vec = vec; /* set vector */ vec += (autp->numc * numv * 4); } /* end else */ } /* end if dyn vec */ k++; /* next instance */ } /* end for j */ if (autp->amod) /* flt CSR? gap */ csr = csr + 2; } /* end for i */ return SCPE_OK; } /* Factory bad block table creation routine This routine writes a DEC standard 044 compliant bad block table on the last track of the specified unit. The bad block table consists of 10 repetitions of the same table, formatted as follows: words 0-1 pack id number words 2-3 cylinder/sector/surface specifications : words n-n+1 end of table (-1,-1) Inputs: uptr = pointer to unit sec = number of sectors per surface wds = number of words per sector Outputs: sta = status code */ t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) { int32 i; t_addr da; uint16 *buf; if ((sec < 2) || (wds < 16)) return SCPE_ARG; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; if (uptr->flags & UNIT_RO) return SCPE_RO; if (!get_yn ("Overwrite last track? [N]", FALSE)) return SCPE_OK; da = (uptr->capac - (sec * wds)) * sizeof (uint16); if (sim_fseek (uptr->fileref, da, SEEK_SET)) return SCPE_IOERR; if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) return SCPE_MEM; buf[0] = buf[1] = 012345u; buf[2] = buf[3] = 0; for (i = 4; i < wds; i++) buf[i] = 0177777u; for (i = 0; (i < sec) && (i < 10); i++) sim_fwrite (buf, sizeof (uint16), wds, uptr->fileref); free (buf); if (ferror (uptr->fileref)) return SCPE_IOERR; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_xu.c0000644000175000017500000015021210635476604013247 0ustar vlmvlm/* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator ------------------------------------------------------------------------------ Copyright (c) 2003-2007, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ This DEUNA/DELUA simulation is based on: Digital DELUA Users Guide, Part# EK-DELUA-UG-002 Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001 These manuals can be found online at: http://www.spies.com/~aek/pdf/dec/unibus Testing performed: 1) Receives/Transmits single packet under custom RSX driver 2) Passes RSTS 10.1 controller probe diagnostics during boot 3) VMS 7.2 on VAX780 summary: (May/2007: WinXP x64 host; MS VC++ 2005; SIMH v3.7-0 base; WinPcap 4.0) LAT - SET HOST/LAT in/out DECNET - SET HOST in/out, COPY in/out TCP/IP - PING in/out; SET HOST/TELNET in/out, COPY/FTP in/out Clustering - Successfully clustered with AlphaVMS 8.2 4) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail Known issues: 1) Most auxiliary commands are not implemented yet. 2) System_ID broadcast is not implemented. 3) There are residual Map_ReadB and Map_WriteB from the FvK version that probably need to be converted to Map_ReadW and Map_WriteW calls. 4) Some jerkiness seen during interactive I/O with remote systems; this is probably attributable to changed polling times from when the poll duration was standardized for idling support. ------------------------------------------------------------------------------ Modification history: 18-Jun-07 RMS Added UNIT_IDLE flag 03-May-07 DTH Added missing FC_RMAL command; cleared multicast on write 29-Oct-06 RMS Synced poll and clock 08-Dec-05 DTH Implemented ancilliary functions 022/023/024/025 18-Nov-05 DTH Corrected time between system ID packets 07-Sep-05 DTH Corrected runt packet processing (found by Tim Chapman), Removed unused variable 16-Aug-05 RMS Fixed C++ declaration and cast problems 10-Mar-05 RMS Fixed equality test in RCSTAT (from Mark Hittinger) 16-Jan-04 DTH Added more info to SHOW MOD commands 09-Jan-04 DTH Made XU floating address so that XUB will float correctly 08-Jan-04 DTH Added system_id message 06-Jan-04 DTH Added protection against changing mac and type if attached 05-Jan-04 DTH Moved most of xu_setmac to sim_ether Implemented auxiliary function 12/13 Added SET/SHOW XU STATS 31-Dec-03 DTH RSTS 10.1 accepts controller during boot tests Implemented chained buffers in transmit/receive processing 29-Dec-03 DTH Primitive RSX packet sending succeeds 23-Dec-03 DTH Implemented write function 17-Dec-03 DTH Implemented read function 05-May-03 DTH Started XU simulation - core logic pirated from unreleased FvK PDP10 variant ------------------------------------------------------------------------------ */ #include "pdp11_xu.h" extern int32 tmxr_poll, tmr_poll, clk_tps, cpu_astop; extern FILE *sim_log; t_stat xu_rd(int32* data, int32 PA, int32 access); t_stat xu_wr(int32 data, int32 PA, int32 access); t_stat xu_svc(UNIT * uptr); t_stat xu_reset (DEVICE * dptr); t_stat xu_attach (UNIT * uptr, char * cptr); t_stat xu_detach (UNIT * uptr); t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc); int32 xu_int (void); t_stat xu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat xu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void xua_read_callback(int status); void xub_read_callback(int status); void xua_write_callback(int status); void xub_write_callback(int status); void xu_setint (CTLR* xu); void xu_clrint (CTLR* xu); void xu_process_receive(CTLR* xu); void xu_dump_rxring(CTLR* xu); void xu_dump_txring(CTLR* xu); DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr, 1, IVCL (XU), VEC_XU, {&xu_int} }; UNIT xua_unit[] = { { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */ }; struct xu_device xua = { xua_read_callback, /* read callback routine */ xua_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xCC, 0xDD, 0xEE}, /* mac */ XU_T_DELUA /* type */ }; MTAB xu_mod[] = { #if defined (VM_PDP11) { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, #else { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, NULL, &show_addr, NULL }, #endif { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", &xu_setmac, &xu_showmac, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", NULL, ð_show, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xu_set_stats, &xu_show_stats, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}", &xu_set_type, &xu_show_type, NULL }, { 0 }, }; REG xua_reg[] = { { NULL } }; DEBTAB xu_debug[] = { {"TRACE", DBG_TRC}, {"WARN", DBG_WRN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, {"ETH", DBG_ETH}, {0} }; DEVICE xu_dev = { "XU", xua_unit, xua_reg, xu_mod, 1, XU_RDX, 8, 1, XU_RDX, 8, &xu_ex, &xu_dep, &xu_reset, NULL, &xu_attach, &xu_detach, &xua_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, xu_debug }; /* XUB does not exist in the PDP10 simulation */ #if defined(IOBA_XUB) DIB xub_dib = { IOBA_XUB, IOLN_XUB, &xu_rd, &xu_wr, 1, IVCL (XU), 0, { &xu_int } }; UNIT xub_unit[] = { { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */ }; struct xu_device xub = { xub_read_callback, /* read callback routine */ xub_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xDD, 0xEE, 0xFF}, /* mac */ XU_T_DELUA /* type */ }; REG xub_reg[] = { { NULL } }; DEVICE xub_dev = { "XUB", xub_unit, xub_reg, xu_mod, 1, XU_RDX, 8, 1, XU_RDX, 8, &xu_ex, &xu_dep, &xu_reset, NULL, &xu_attach, &xu_detach, &xub_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, xu_debug }; #define XU_MAX_CONTROLLERS 2 CTLR xu_ctrl[] = { {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */ ,{&xub_dev, xub_unit, &xub_dib, &xub} /* XUB controller */ }; #else /* IOBA_XUB */ #define XU_MAX_CONTROLLERS 1 CTLR xu_ctrl[] = { {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */ }; #endif /* IOBA_XUB */ /*============================================================================*/ /* Multicontroller support */ CTLR* xu_unit2ctlr(UNIT* uptr) { int i; unsigned int j; for (i=0; inumunits; j++) if (&xu_ctrl[i].unit[j] == uptr) return &xu_ctrl[i]; /* not found */ return 0; } CTLR* xu_dev2ctlr(DEVICE* dptr) { int i; for (i=0; i= xu_ctrl[i].dib->ba) && (PA < (xu_ctrl[i].dib->ba + xu_ctrl[i].dib->lnt))) return &xu_ctrl[i]; /* not found */ return 0; } /*============================================================================*/ /* stop simh from reading non-existant unit data stream */ t_stat xu_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw) { return SCPE_NOFNC; } /* stop simh from writing non-existant unit data stream */ t_stat xu_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw) { return SCPE_NOFNC; } t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xu = xu_unit2ctlr(uptr); char buffer[20]; eth_mac_fmt((ETH_MAC*)xu->var->mac, buffer); fprintf(st, "MAC=%s", buffer); return SCPE_OK; } t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc) { t_stat status; CTLR* xu = xu_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; status = eth_mac_scan(&xu->var->mac, cptr); return status; } t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) { CTLR* xu = xu_unit2ctlr(uptr); /* set stats to zero, regardless of passed parameter */ memset(&xu->var->stats, 0, sizeof(struct xu_stats)); return SCPE_OK; } t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) { char* fmt = " %-24s%d\n"; CTLR* xu = xu_unit2ctlr(uptr); struct xu_stats* stats = &xu->var->stats; fprintf(st, "Ethernet statistics:\n"); fprintf(st, fmt, "Seconds since cleared:", stats->secs); fprintf(st, fmt, "Recv frames:", stats->frecv); fprintf(st, fmt, "Recv dbytes:", stats->rbytes); fprintf(st, fmt, "Xmit frames:", stats->ftrans); fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); return SCPE_OK; } t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xu = xu_unit2ctlr(uptr); fprintf(st, "type="); switch (xu->var->type) { case XU_T_DEUNA: fprintf(st, "DEUNA"); break; case XU_T_DELUA: fprintf(st, "DELUA"); break; } return SCPE_OK; } t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) { CTLR* xu = xu_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEUNA")) xu->var->type = XU_T_DEUNA; else if (!strcmp(cptr, "DELUA")) xu->var->type = XU_T_DELUA; else return SCPE_ARG; return SCPE_OK; } /*============================================================================*/ void upd_stat16(uint16* stat, uint16 add) { *stat += add; /* did stat roll over? latches at maximum */ if (*stat < add) *stat = 0xFFFF; } void upd_stat32(uint32* stat, uint32 add) { *stat += add; /* did stat roll over? latches at maximum */ if (*stat < add) *stat = 0xFFFFFFFF; } void bit_stat16(uint16* stat, uint16 bits) { *stat |= bits; } t_stat xu_process_local (CTLR* xu, ETH_PACK* pack) { return SCPE_NOFNC; /* not implemented yet */ } void xu_read_callback(CTLR* xu, int status) { /* process any packets locally that can be */ status = xu_process_local (xu, &xu->var->read_buffer); /* add packet to read queue */ if (status != SCPE_OK) ethq_insert(&xu->var->ReadQ, 2, &xu->var->read_buffer, 0); } void xua_read_callback(int status) { xu_read_callback(&xu_ctrl[0], status); } void xub_read_callback(int status) { xu_read_callback(&xu_ctrl[1], status); } t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id) { static uint16 receipt = 0; ETH_PACK system_id; uint8* const msg = &system_id.msg[0]; t_stat status; sim_debug(DBG_TRC, xu->dev, "xu_system_id()\n"); memset (&system_id, 0, sizeof(system_id)); memcpy (&msg[0], dest, sizeof(ETH_MAC)); memcpy (&msg[6], xu->var->setup.macs[0], sizeof(ETH_MAC)); msg[12] = 0x60; /* type */ msg[13] = 0x02; /* type */ msg[14] = 0x1C; /* character count */ msg[15] = 0x00; /* character count */ msg[16] = 0x07; /* code */ msg[17] = 0x00; /* zero pad */ if (receipt_id) { msg[18] = receipt_id & 0xFF; /* receipt number */ msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */ } else { msg[18] = receipt & 0xFF; /* receipt number */ msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */ } /* MOP VERSION */ msg[20] = 0x01; /* type */ msg[21] = 0x00; /* type */ msg[22] = 0x03; /* length */ msg[23] = 0x03; /* version */ msg[24] = 0x00; /* eco */ msg[25] = 0x00; /* user eco */ /* FUNCTION */ msg[26] = 0x02; /* type */ msg[27] = 0x00; /* type */ msg[28] = 0x02; /* length */ msg[29] = 0x05; /* value 1 */ msg[30] = 0x00; /* value 2 */ /* HARDWARE ADDRESS */ msg[31] = 0x07; /* type */ msg[32] = 0x00; /* type */ msg[33] = 0x06; /* length */ memcpy (&msg[34], xu->var->mac, sizeof(ETH_MAC)); /* ROM address */ /* DEVICE TYPE */ msg[40] = 0x64; /* type */ msg[41] = 0x00; /* type */ msg[42] = 0x01; /* length */ if (xu->var->type == XU_T_DEUNA) msg[43] = 1; /* value (1=DEUNA) */ else msg[43] = 11; /* value (11=DELUA) */ /* write system id */ system_id.len = 60; status = eth_write(xu->var->etherface, &system_id, NULL); return status; } t_stat xu_svc(UNIT* uptr) { int queue_size; t_stat status; CTLR* xu = xu_unit2ctlr(uptr); const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; const int one_second = clk_tps * tmr_poll; /* First pump any queued packets into the system */ if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)) xu_process_receive(xu); /* Now read and queue packets that have arrived */ /* This is repeated as long as they are available and we have room */ do { queue_size = xu->var->ReadQ.count; /* read a packet from the ethernet - processing is via the callback */ status = eth_read (xu->var->etherface, &xu->var->read_buffer, xu->var->rcallback); } while (queue_size != xu->var->ReadQ.count); /* Now pump any still queued packets into the system */ if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)) xu_process_receive(xu); /* send identity packet when timer expires */ if (--xu->var->idtmr <= 0) { if ((xu->var->mode & MODE_DMNT) == 0) /* if maint msg is not disabled */ status = xu_system_id(xu, mop_multicast, 0); /* then send ID packet */ xu->var->idtmr = XU_ID_TIMER_VAL * one_second; /* reset timer */ } /* has one second timer expired? if so, update stats and reset timer */ if (++xu->var->sectmr >= XU_SERVICE_INTERVAL) { upd_stat16 (&xu->var->stats.secs, 1); xu->var->sectmr = 0; } /* resubmit service timer if controller not halted */ switch (xu->var->pcsr1 & PCSR1_STATE) { case STATE_READY: case STATE_RUNNING: sim_activate(&xu->unit[0], tmxr_poll); break; }; return SCPE_OK; } void xu_write_callback (CTLR* xu, int status) { xu->var->write_buffer.status = status; } void xua_write_callback (int status) { xu_write_callback(&xu_ctrl[0], status); } void xub_write_callback (int status) { xu_write_callback(&xu_ctrl[1], status); } void xu_setclrint(CTLR* xu, int32 bits) { if (xu->var->pcsr0 & 0xFF00) { /* if any interrupt bits on, */ xu->var->pcsr0 |= PCSR0_INTR; /* turn master bit on */ xu_setint(xu); /* and trigger interrupt */ } else { xu->var->pcsr0 &= ~PCSR0_INTR; /* ... or off */ xu_clrint(xu); /* and clear interrupt if needed*/ } } t_stat xu_sw_reset (CTLR* xu) { t_stat status; sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n"); /* Clear the registers. */ xu->var->pcsr0 = PCSR0_DNI | PCSR0_INTR; xu->var->pcsr1 = STATE_READY; switch (xu->var->type) { case XU_T_DELUA: xu->var->pcsr1 |= TYPE_DELUA; break; case XU_T_DEUNA: xu->var->pcsr1 |= TYPE_DEUNA; if (!xu->var->etherface) /* if not attached, set transceiver powerfail */ xu->var->pcsr1 |= PCSR1_XPWR; break; } xu->var->pcsr2 = 0; xu->var->pcsr3 = 0; /* Clear the parameters. */ xu->var->mode = 0; xu->var->pcbb = 0; xu->var->stat = 0; /* clear read queue */ ethq_clear(&xu->var->ReadQ); /* clear setup info */ memset(&xu->var->setup, 0, sizeof(struct xu_setup)); /* clear network statistics */ memset(&xu->var->stats, 0, sizeof(struct xu_stats)); /* reset ethernet interface */ memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC)); xu->var->setup.mac_count = 1; if (xu->var->etherface) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, &xu->var->mac, xu->var->setup.multicast, xu->var->setup.promiscuous); /* activate device if not disabled */ if ((xu->dev->flags & DEV_DIS) == 0) { sim_activate_abs(&xu->unit[0], clk_cosched (tmxr_poll)); } /* clear load_server address */ memset(xu->var->load_server, 0, sizeof(ETH_MAC)); return SCPE_OK; } /* Reset device. */ t_stat xu_reset(DEVICE* dptr) { t_stat status; CTLR* xu = xu_dev2ctlr(dptr); sim_debug(DBG_TRC, xu->dev, "xu_reset()\n"); /* init read queue (first time only) */ status = ethq_init (&xu->var->ReadQ, XU_QUE_MAX); if (status != SCPE_OK) return status; /* software reset controller */ xu_sw_reset(xu); return SCPE_OK; } /* Perform one of the defined ancillary functions. */ int32 xu_command(CTLR* xu) { uint32 udbb; int fnc, mtlen, i, j; uint16 value, pltlen; t_stat status, rstatus, wstatus, wstatus2, wstatus3; struct xu_stats* stats = &xu->var->stats; uint16* udb = xu->var->udb; uint16* mac_w = (uint16*) xu->var->mac; static const ETH_MAC zeros = {0,0,0,0,0,0}; static const ETH_MAC mcast_load_server = {0xAB, 0x00, 0x00, 0x01, 0x00, 0x00}; static char* command[] = { "NO-OP", "Start Microaddress", "Read Default Physical Address", "NO-OP", "Read Physical Address", "Write Physical Address", "Read Multicast Address List", "Write Multicast Address List", "Read Descriptor Ring Format", "Write Descriptor Ring Format", "Read Counters", "Read/Clear Counters", "Read Mode Register", "Write Mode Register", "Read Status", "Read/Clear Status", "Dump Internal Memory", "Load Internal Memory", "Read System ID", "Write System ID", "Read Load Server Address", "Write Load Server Address" }; /* Grab the PCB from the host. */ rstatus = Map_ReadW(xu->var->pcbb, 8, xu->var->pcb); if (rstatus != 0) return PCSR0_PCEI + 1; /* High 8 bits are defined as MBZ. */ if (xu->var->pcb[0] & 0177400) return PCSR0_PCEI; /* Decode the function to be performed. */ fnc = xu->var->pcb[0] & 0377; sim_debug(DBG_TRC, xu->dev, "xu_command(), Command: %s [0%o]\n", command[fnc], fnc); switch (fnc) { case FC_NOOP: break; case FC_RDPA: /* read default physical address */ wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->mac); if (wstatus) return PCSR0_PCEI + 1; break; case FC_RPA: /* read current physical address */ wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]); if (wstatus) return PCSR0_PCEI + 1; break; case FC_WPA: /* write current physical address */ rstatus = Map_ReadB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]); if (xu->var->pcb[1] & 1) return PCSR0_PCEI; break; case FC_RMAL: /* read multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[1]); break; case FC_WMAL: /* write multicast address list */ mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); if (mtlen > 10) return PCSR0_PCEI; udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); /* clear existing multicast list */ for (i=1; ivar->setup.macs[i][j] = 0; } /* get multicast list from host */ rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[1]); if (rstatus == 0) { xu->var->setup.mac_count = mtlen + 1; status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, xu->var->setup.macs, xu->var->setup.multicast, xu->var->setup.promiscuous); } else { xu->var->pcsr0 |= PCSR0_PCEI; } break; case FC_RRF: /* read ring format */ if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) return PCSR0_PCEI; xu->var->udb[0] = xu->var->tdrb & 0177776; xu->var->udb[1] = (xu->var->telen << 8) + ((xu->var->tdrb >> 16) & 3); xu->var->udb[2] = xu->var->trlen; xu->var->udb[3] = xu->var->rdrb & 0177776; xu->var->udb[4] = (xu->var->relen << 8) + ((xu->var->rdrb >> 16) & 3); xu->var->udb[5] = xu->var->rrlen; /* Write UDB to host memory. */ udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); wstatus = Map_WriteW(udbb, 12, xu->var->pcb); if (wstatus != 0) return PCSR0_PCEI+1; break; case FC_WRF: /* write ring format */ if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) return PCSR0_PCEI; if ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING) return PCSR0_PCEI; /* Read UDB into local memory. */ udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); rstatus = Map_ReadW(udbb, 12, xu->var->udb); if (rstatus) return PCSR0_PCEI+1; if ((xu->var->udb[0] & 1) || (xu->var->udb[1] & 0374) || (xu->var->udb[3] & 1) || (xu->var->udb[4] & 0374) || (xu->var->udb[5] < 2)) { return PCSR0_PCEI; } xu->var->tdrb = ((xu->var->udb[1] & 3) << 16) + (xu->var->udb[0] & 0177776); xu->var->telen = (xu->var->udb[1] >> 8) & 0377; xu->var->trlen = xu->var->udb[2]; xu->var->rdrb = ((xu->var->udb[4] & 3) << 16) + (xu->var->udb[3] & 0177776); xu->var->relen = (xu->var->udb[4] >> 8) & 0377; xu->var->rrlen = xu->var->udb[5]; xu->var->rxnext = 0; xu->var->txnext = 0; // xu_dump_rxring(xu); // xu_dump_txring(xu); break; case FC_RDCTR: /* read counters */ case FC_RDCLCTR: /* read and clear counters */ /* prepare udb for stats transfer */ memset(xu->var->udb, 0, sizeof(xu->var->udb)); /* place stats in udb */ udb[0] = 68; /* udb length */ udb[1] = stats->secs; /* seconds since zeroed */ udb[2] = stats->frecv & 0xFFFF; /* frames received <15:00> */ udb[3] = stats->frecv >> 16; /* frames received <31:16> */ udb[4] = stats->mfrecv & 0xFFFF; /* multicast frames received <15:00> */ udb[5] = stats->mfrecv >> 16; /* multicast frames received <31:16> */ udb[6] = stats->rxerf; /* receive error status bits */ udb[7] = stats->frecve; /* frames received with error */ udb[8] = stats->rbytes & 0xFFFF; /* data bytes received <15:00> */ udb[9] = stats->rbytes >> 16; /* data bytes received <31:16> */ udb[10] = stats->mrbytes & 0xFFFF; /* multicast data bytes received <15:00> */ udb[11] = stats->mrbytes >> 16; /* multicast data bytes received <31:16> */ udb[12] = stats->rlossi; /* received frames lost - internal buffer */ udb[13] = stats->rlossl; /* received frames lost - local buffer */ udb[14] = stats->ftrans & 0xFFFF; /* frames transmitted <15:00> */ udb[15] = stats->ftrans >> 16; /* frames transmitted <31:16> */ udb[16] = stats->mftrans & 0xFFFF; /* multicast frames transmitted <15:00> */ udb[17] = stats->mftrans >> 16; /* multicast frames transmitted <31:16> */ udb[18] = stats->ftrans3 & 0xFFFF; /* frames transmitted 3+ tries <15:00> */ udb[19] = stats->ftrans3 >> 16; /* frames transmitted 3+ tries <31:16> */ udb[20] = stats->ftrans2 & 0xFFFF; /* frames transmitted 2 tries <15:00> */ udb[21] = stats->ftrans2 >> 16; /* frames transmitted 2 tries <31:16> */ udb[22] = stats->ftransd & 0xFFFF; /* frames transmitted deferred <15:00> */ udb[23] = stats->ftransd >> 16; /* frames transmitted deferred <31:16> */ udb[24] = stats->tbytes & 0xFFFF; /* data bytes transmitted <15:00> */ udb[25] = stats->tbytes >> 16; /* data bytes transmitted <31:16> */ udb[26] = stats->mtbytes & 0xFFFF; /* multicast data bytes transmitted <15:00> */ udb[27] = stats->mtbytes >> 16; /* multicast data bytes transmitted <31:16> */ udb[28] = stats->txerf; /* transmit frame error status bits */ udb[29] = stats->ftransa; /* transmit frames aborted */ udb[30] = stats->txccf; /* transmit collision check failure */ udb[31] = 0; /* MBZ */ udb[32] = stats->porterr; /* port driver error */ udb[33] = stats->bablcnt; /* babble counter */ /* transfer udb to host */ udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); wstatus = Map_WriteW(udbb, 68, xu->var->udb); if (wstatus) { xu->var->pcsr0 |= PCSR0_PCEI; } /* if clear function, clear network stats */ if (fnc == FC_RDCLCTR) memset(stats, 0, sizeof(struct xu_stats)); break; case FC_RMODE: /* read mode register */ value = xu->var->mode; wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value); if (wstatus) return PCSR0_PCEI + 1; break; case FC_WMODE: /* write mode register */ value = xu->var->mode; xu->var->mode = xu->var->pcb[1]; sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); /* set promiscuous and multicast flags */ xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0; xu->var->setup.multicast = (xu->var->mode & MODE_ENAL) ? 1 : 0; /* if promiscuous or multicast flags changed, change filter */ if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL)) status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, &xu->var->mac, xu->var->setup.multicast, xu->var->setup.promiscuous); break; case FC_RSTAT: /* read extended status */ case FC_RCSTAT: /* read and clear extended status */ value = xu->var->stat; wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value); value = 10; wstatus2 = Map_WriteW(xu->var->pcbb+4, 2, &value); value = 32; wstatus3 = Map_WriteW(xu->var->pcbb+6, 2, &value); if (wstatus + wstatus2 + wstatus3) return PCSR0_PCEI + 1; if (fnc == FC_RCSTAT) xu->var->stat &= 0377; /* clear high byte */ break; case FC_RSID: /* read system id parameters */ /* prepare udb for transfer */ memset(xu->var->udb, 0, sizeof(xu->var->udb)); udb[11] = 0x260; /* type */ udb[12] = 28/* + parameter size */; /* ccount */ udb[13] = 7; /* code */ udb[14] = 0; /* recnum */ /* mop information */ udb[15] = 1; /* mvtype */ udb[16] = 0x0303; /* mvver + mvlen */ udb[17] = 0; /* mvueco + mveco */ /* function information */ udb[18] = 2; /* ftype */ udb[19] = 0x0502; /* fval1 + flen */ udb[20] = 0x0700; /* hatype<07:00> + fval2 */ udb[21] = 0x0600; /* halen + hatype<15:08> */ /* built-in MAC address */ udb[21] = mac_w[0]; /* HA<15:00> */ udb[22] = mac_w[1]; /* HA<31:16> */ udb[23] = mac_w[2]; /* HA<47:32> */ udb[24] = 0x64; /* dtype */ udb[25] = (11 << 8) + 1; /* dvalue + dlen */ /* transfer udb to host */ udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); wstatus = Map_WriteW(udbb, 52, xu->var->udb); if (wstatus) xu->var->pcsr0 |= PCSR0_PCEI; break; case FC_WSID: /* write system id parameters */ /* get udb base */ udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); /* get udb length */ pltlen = xu->var->pcb[3]; /* transfer udb from host */ rstatus = Map_ReadW(udbb, pltlen * 2, xu->var->udb); if (rstatus) return PCSR0_PCEI + 1; /* decode and store system ID fields , if we ever need to. for right now, just return "success" */ break; case FC_RLSA: /* read load server address */ if (memcmp(xu->var->load_server, zeros, sizeof(ETH_MAC))) { /* not set, use default multicast load address */ wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*) mcast_load_server); } else { /* is set, use load_server */ wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->load_server); } if (wstatus) return PCSR0_PCEI + 1; break; case FC_WLSA: /* write load server address */ rstatus = Map_ReadB(xu->var->pcbb + 2, 6, xu->var->load_server); if (rstatus) return PCSR0_PCEI + 1; break; default: /* Unknown (unimplemented) command. */ printf("%s: unknown ancilliary command 0%o requested !\n", xu->dev->name, fnc); return PCSR0_PCEI; break; } /* switch */ return PCSR0_DNI; } /* Transfer received packets into receive ring. */ void xu_process_receive(CTLR* xu) { uint32 segb, ba; int slen, wlen, off; t_stat rstatus, wstatus; ETH_ITEM* item = 0; int state = xu->var->pcsr1 & PCSR1_STATE; int no_buffers = xu->var->pcsr0 & PCSR0_RCBI; sim_debug(DBG_TRC, xu->dev, "xu_process_receive(), buffers: %d\n", xu->var->rrlen); /* xu_dump_rxring(xu); /* debug receive ring */ /* process only when in the running state, and host buffers are available */ if ((state != STATE_RUNNING) || no_buffers) return; /* check read queue for buffer loss */ if (xu->var->ReadQ.loss) { upd_stat16(&xu->var->stats.rlossl, (uint16) xu->var->ReadQ.loss); xu->var->ReadQ.loss = 0; } /* while there are still packets left to process in the queue */ while (xu->var->ReadQ.count > 0) { /* get next receive buffer */ ba = xu->var->rdrb + (xu->var->relen * 2) * xu->var->rxnext; rstatus = Map_ReadW (ba, 8, xu->var->rxhdr); if (rstatus) { /* tell host bus read failed */ xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; xu->var->pcsr0 |= PCSR0_SERI; break; } /* if buffer not owned by controller, exit [at end of ring] */ if (!(xu->var->rxhdr[2] & RXR_OWN)) { /* tell the host there are no more buffers */ /* xu->var->pcsr0 |= PCSR0_RCBI; */ /* I don't think this is correct 08-dec-2005 dth */ break; } /* set buffer length and address */ slen = xu->var->rxhdr[0]; segb = xu->var->rxhdr[1] + ((xu->var->rxhdr[2] & 3) << 16); /* get first packet from receive queue */ if (!item) { item = &xu->var->ReadQ.item[xu->var->ReadQ.head]; /* * 2.11BSD does not seem to like small packets. * For example.. an incoming ARP packet is: * ETH dstaddr [6] * ETH srcaddr [6] * ETH type [2] * ARP arphdr [8] * ARP dstha [6] * ARP dstpa [4] * ARP srcha [6] * ARP srcpa [4] * * for a total of 42 bytes. According to the 2.11BSD * driver for DEUNA (if_de.c), this is not a legal size, * and the packet is dropped. Therefore, we pad the * thing to minimum size here. Stupid runts... */ if (item->packet.len < ETH_MIN_PACKET) { int len = item->packet.len; memset (&item->packet.msg[len], 0, ETH_MIN_PACKET - len); item->packet.len = ETH_MIN_PACKET; } } /* is this the start of frame? */ if (item->packet.used == 0) { xu->var->rxhdr[2] |= RXR_STF; off = 0; } /* figure out chained packet size */ wlen = item->packet.crc_len - item->packet.used; if (wlen > slen) wlen = slen; /* transfer chained packet to host buffer */ wstatus = Map_WriteB (segb, wlen, &item->packet.msg[off]); if (wstatus) { /* error during write */ xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; xu->var->pcsr0 |= PCSR0_SERI; break; } /* update chained counts */ item->packet.used += wlen; off += wlen; /* Is this the end-of-frame? */ if (item->packet.used == item->packet.crc_len) { /* mark end-of-frame */ xu->var->rxhdr[2] |= RXR_ENF; /* * Fill in the Received Message Length field. * The documenation notes that the DEUNA actually performs * a full CRC check on the data buffer, and adds this CRC * value to the data, in the last 4 bytes. The question * is: does MLEN include these 4 bytes, or not??? --FvK * * A quick look at the RSX Process Software driver shows * that the CRC byte count(4) is added to MLEN, but does * not show if the DEUNA/DELUA actually transfers the * CRC bytes to the host buffers, since the driver never * tries to use them. However, since the host max buffer * size is only 1514, not 1518, I doubt the CRC is actually * transferred in normal mode. Maybe CRC is transferred * and used in Loopback mode.. -- DTH * * The VMS XEDRIVER indicates that CRC is transferred as * part of the packet, and is included in the MLEN count. -- DTH */ xu->var->rxhdr[3] &= ~RXR_MLEN; xu->var->rxhdr[3] |= (item->packet.crc_len); if (xu->var->mode & MODE_DRDC) /* data chaining disabled */ xu->var->rxhdr[3] |= RXR_NCHN; /* update stats */ upd_stat32(&xu->var->stats.frecv, 1); upd_stat32(&xu->var->stats.rbytes, item->packet.len - 14); if (item->packet.msg[0] & 1) { /* multicast? */ upd_stat32(&xu->var->stats.mfrecv, 1); upd_stat32(&xu->var->stats.mrbytes, item->packet.len - 14); } /* remove processed packet from the receive queue */ ethq_remove (&xu->var->ReadQ); item = 0; /* tell host we received a packet */ xu->var->pcsr0 |= PCSR0_RXI; } /* if end-of-frame */ /* give buffer back to host */ xu->var->rxhdr[2] &= ~RXR_OWN; /* clear ownership flag */ /* update the ring entry in host memory. */ wstatus = Map_WriteW (ba, 8, xu->var->rxhdr); if (wstatus) { /* tell host bus write failed */ xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; xu->var->pcsr0 |= PCSR0_SERI; /* if this was end-of-frame, log frame loss */ if (xu->var->rxhdr[2] & RXR_ENF) upd_stat16(&xu->var->stats.rlossi, 1); } /* set to next receive ring buffer */ xu->var->rxnext += 1; if (xu->var->rxnext == xu->var->rrlen) xu->var->rxnext = 0; } /* while */ /* if we failed to finish receiving the frame, flush the packet */ if (item) { ethq_remove(&xu->var->ReadQ); upd_stat16(&xu->var->stats.rlossl, 1); } /* set or clear interrupt, depending on what happened */ xu_setclrint(xu, 0); // xu_dump_rxring(xu); /* debug receive ring */ } void xu_process_transmit(CTLR* xu) { uint32 segb, ba; int slen, wlen, i, off, giant, runt; t_stat rstatus, wstatus; sim_debug(DBG_TRC, xu->dev, "xu_process_transmit()\n"); /* xu_dump_txring(xu); /* debug receive ring */ for (;;) { /* get next transmit buffer */ ba = xu->var->tdrb + (xu->var->telen * 2) * xu->var->txnext; rstatus = Map_ReadW (ba, 8, xu->var->txhdr); if (rstatus) { /* tell host bus read failed */ xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG; xu->var->pcsr0 |= PCSR0_SERI; break; } /* if buffer not owned by controller, exit [at end of ring] */ if (!(xu->var->txhdr[2] & TXR_OWN)) break; /* set buffer length and address */ slen = xu->var->txhdr[0]; segb = xu->var->txhdr[1] + ((xu->var->txhdr[2] & 3) << 16); wlen = slen; /* prepare to accumulate transmit information if start of frame */ if (xu->var->txhdr[2] & TXR_STF) { memset(&xu->var->write_buffer, 0, sizeof(ETH_PACK)); off = giant = runt = 0; } /* get packet data from host */ if (xu->var->write_buffer.len + slen > ETH_MAX_PACKET) { wlen = ETH_MAX_PACKET - xu->var->write_buffer.len; giant = 1; } if (wlen > 0) { rstatus = Map_ReadB(segb, wlen, &xu->var->write_buffer.msg[off]); if (rstatus) { /* tell host bus read failed */ xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG; xu->var->pcsr0 |= PCSR0_SERI; break; } } off += wlen; xu->var->write_buffer.len += wlen; /* transmit packet when end-of-frame is reached */ if (xu->var->txhdr[2] & TXR_ENF) { /* make sure packet is minimum length */ if (xu->var->write_buffer.len < ETH_MIN_PACKET) { xu->var->write_buffer.len = ETH_MIN_PACKET; /* pad packet to minimum length */ if ((xu->var->mode & MODE_TPAD) == 0) /* if pad mode is NOT on, set runt error flag */ runt = 1; } /* are we in internal loopback mode ? */ if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) { /* just put packet in receive buffer */ ethq_insert (&xu->var->ReadQ, 1, &xu->var->write_buffer, 0); } else { /* transmit packet synchronously - write callback sets status */ wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback); if (wstatus) xu->var->pcsr0 |= PCSR0_PCEI; } /* update transmit status in transmit buffer */ if (xu->var->write_buffer.status != 0) { /* failure */ const uint16 tdr = 100 + wlen * 8; /* arbitrary value */ xu->var->txhdr[3] |= TXR_RTRY; xu->var->txhdr[3] |= tdr & TXR_TDR; xu->var->txhdr[2] |= TXR_ERRS; } /* was packet too big or too small? */ if (giant || runt) { xu->var->txhdr[3] |= TXR_BUFL; xu->var->txhdr[2] |= TXR_ERRS; } /* was packet self-addressed? */ for (i=0; ivar->write_buffer.msg, xu->var->setup.macs[i], sizeof(ETH_MAC)) == 0) xu->var->txhdr[2] |= TXR_MTCH; /* tell host we transmitted a packet */ xu->var->pcsr0 |= PCSR0_TXI; /* update stats */ upd_stat32(&xu->var->stats.ftrans, 1); upd_stat32(&xu->var->stats.tbytes, xu->var->write_buffer.len - 14); if (xu->var->write_buffer.msg[0] & 1) { /* multicast? */ upd_stat32(&xu->var->stats.mftrans, 1); upd_stat32(&xu->var->stats.mtbytes, xu->var->write_buffer.len - 14); } if (giant) bit_stat16(&xu->var->stats.txerf, 0x10); } /* if end-of-frame */ /* give buffer ownership back to host */ xu->var->txhdr[2] &= ~TXR_OWN; /* update transmit buffer */ wstatus = Map_WriteW (ba, 8, xu->var->txhdr); if (wstatus) { /* tell host bus write failed */ xu->var->pcsr0 |= PCSR0_PCEI; /* update stats */ upd_stat16(&xu->var->stats.ftransa, 1); break; } /* set to next transmit ring buffer */ xu->var->txnext += 1; if (xu->var->txnext == xu->var->trlen) xu->var->txnext = 0; } /* while */ } void xu_port_command (CTLR* xu) { char* msg; int command = xu->var->pcsr0 & PCSR0_PCMD; int state = xu->var->pcsr1 & PCSR1_STATE; int bits; static char* commands[] = { "NO-OP", "GET PCBB", "GET CMD", "SELFTEST", "START", "BOOT", "Reserved NO-OP", "Reserved NO-OP", "PDMD", "Reserved NO-OP", "Reserved NO-OP", "Reserved NO-OP", "Reserved NO-OP", "Reserved NO-OP", "HALT", "STOP" }; sim_debug(DBG_TRC, xu->dev, "xu_port_command(), Command = %s [0%o]\n", commands[command], command); switch (command) { /* cases in order of most used to least used */ case CMD_PDMD: /* POLLING DEMAND */ /* process transmit buffers, receive buffers are done in the service timer */ xu_process_transmit(xu); xu->var->pcsr0 |= PCSR0_DNI; break; case CMD_GETCMD: /* GET COMMAND */ bits = xu_command(xu); xu->var->pcsr0 |= PCSR0_DNI; break; case CMD_GETPCBB: /* GET PCB-BASE */ xu->var->pcbb = (xu->var->pcsr3 << 16) | xu->var->pcsr2; xu->var->pcsr0 |= PCSR0_DNI; break; case CMD_SELFTEST: /* SELFTEST */ xu_sw_reset(xu); xu->var->pcsr0 |= PCSR0_DNI; break; case CMD_START: /* START */ if (state == STATE_READY) { xu->var->pcsr1 &= ~PCSR1_STATE; xu->var->pcsr1 |= STATE_RUNNING; xu->var->pcsr0 |= PCSR0_DNI; /* reset ring pointers */ xu->var->rxnext = 0; xu->var->txnext = 0; } else xu->var->pcsr0 |= PCSR0_PCEI; break; case CMD_HALT: /* HALT */ if ((state == STATE_READY) || (state == STATE_RUNNING)) { sim_cancel (&xu->unit[0]); /* cancel service timer */ xu->var->pcsr1 &= ~PCSR1_STATE; xu->var->pcsr1 |= STATE_HALT; xu->var->pcsr0 |= PCSR0_DNI; } else xu->var->pcsr0 |= PCSR0_PCEI; break; case CMD_STOP: /* STOP */ if (state == STATE_RUNNING) { xu->var->pcsr1 &= ~PCSR1_STATE; xu->var->pcsr1 |= STATE_READY; xu->var->pcsr0 |= PCSR0_DNI; } else xu->var->pcsr0 |= PCSR0_PCEI; break; case CMD_BOOT: /* BOOT */ /* not implemented */ msg = "%s: BOOT command not implemented!\n"; printf (msg, xu->dev->name); if (sim_log) fprintf(sim_log, msg, xu->dev->name); xu->var->pcsr0 |= PCSR0_PCEI; break; case CMD_NOOP: /* NO-OP */ /* NOOP does NOT set DNI */ break; case CMD_RSV06: /* RESERVED */ case CMD_RSV07: /* RESERVED */ case CMD_RSV11: /* RESERVED */ case CMD_RSV12: /* RESERVED */ case CMD_RSV13: /* RESERVED */ case CMD_RSV14: /* RESERVED */ case CMD_RSV15: /* RESERVED */ /* all reserved commands act as a no-op but set DNI */ xu->var->pcsr0 |= PCSR0_DNI; break; } /* switch */ /* set interrupt if needed */ xu_setclrint(xu, 0); } t_stat xu_rd(int32 *data, int32 PA, int32 access) { CTLR* xu = xu_pa2ctlr(PA); int reg = (PA >> 1) & 03; switch (reg) { case 00: *data = xu->var->pcsr0; break; case 01: *data = xu->var->pcsr1; break; case 02: *data = xu->var->pcsr2; break; case 03: *data = xu->var->pcsr3; break; } sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); if (PA & 1) sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg); return SCPE_OK; } t_stat xu_wr(int32 data, int32 PA, int32 access) { CTLR* xu = xu_pa2ctlr(PA); int reg = (PA >> 1) & 03; char desc[10]; switch (access) { case WRITE : strcpy(desc, "Word"); break; case WRITEB: if (PA & 1) { strcpy(desc, "ByteHi"); } else { strcpy(desc, "ByteLo"); } break; default : strcpy(desc, "Unknown"); break; } sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); switch (reg) { case 00: /* Clear write-one-to-clear interrupt bits */ if (access == WRITEB) { data &= 0377; if (PA & 1) { /* Handle WriteOneToClear trick. */ xu->var->pcsr0 &= ~((data << 8) & 0177400); /* set/reset interrupt */ xu_setclrint(xu, 0); /* Bail out early to avoid PCMD crap. */ return SCPE_OK; } } else { /* access == WRITE [Word] */ uint16 mask = data & 0xFF00; /* only interested in high byte */ xu->var->pcsr0 &= ~mask; /* clear write-one-to-clear bits */ } /* RESET function requested? */ if (data & PCSR0_RSET) { xu_sw_reset(xu); xu_setclrint(xu, 0); return SCPE_OK; /* nothing else to do on reset */ } /* Handle the INTE interlock; if INTE changes state, no commands can occur */ if ((xu->var->pcsr0 ^ data) & PCSR0_INTE) { xu->var->pcsr0 ^= PCSR0_INTE; xu->var->pcsr0 |= PCSR0_DNI; if (xu->var->pcsr0 & PCSR0_INTE) { sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Enabled\n"); } else { sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Disabled\n"); } } else { /* Normal write, no interlock. */ xu->var->pcsr0 &= ~PCSR0_PCMD; xu->var->pcsr0 |= (data & PCSR0_PCMD); xu_port_command(xu); } /* We might have changed the interrupt sys. */ xu_setclrint(xu, 0); break; case 01: sim_debug(DBG_WRN, xu->dev, "xu_wr(), invalid write access on PCSR1!\n"); break; case 02: xu->var->pcsr2 = data & 0177776; /* store word, but not MBZ LSB */ break; case 03: xu->var->pcsr3 = data & 0000003; /* store significant bits */ break; } return SCPE_OK; } /* attach device: */ t_stat xu_attach(UNIT* uptr, char* cptr) { t_stat status; char* tptr; CTLR* xu = xu_unit2ctlr(uptr); sim_debug(DBG_TRC, xu->dev, "xu_attach(cptr=%s)\n", cptr); tptr = (char *) malloc(strlen(cptr) + 1); if (tptr == NULL) return SCPE_MEM; strcpy(tptr, cptr); xu->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV)); if (!xu->var->etherface) return SCPE_MEM; status = eth_open(xu->var->etherface, cptr, xu->dev, DBG_ETH); if (status != SCPE_OK) { free(tptr); free(xu->var->etherface); xu->var->etherface = 0; return status; } uptr->filename = tptr; uptr->flags |= UNIT_ATT; eth_setcrc(xu->var->etherface, 1); /* enable CRC */ /* reset the device with the new attach info */ xu_reset(xu->dev); return SCPE_OK; } /* detach device: */ t_stat xu_detach(UNIT* uptr) { t_stat status; CTLR* xu = xu_unit2ctlr(uptr); sim_debug(DBG_TRC, xu->dev, "xu_detach()\n"); if (uptr->flags & UNIT_ATT) { status = eth_close (xu->var->etherface); free(xu->var->etherface); xu->var->etherface = 0; free(uptr->filename); uptr->filename = NULL; uptr->flags &= ~UNIT_ATT; } return SCPE_OK; } void xu_setint(CTLR* xu) { if (xu->var->pcsr0 & PCSR0_INTE) { xu->var->irq = 1; SET_INT(XU); } return; } void xu_clrint(CTLR* xu) { int i; xu->var->irq = 0; /* set controller irq off */ /* clear master interrupt? */ for (i=0; iirq) { /* if any irqs enabled */ SET_INT(XU); /* set master interrupt on */ return; } CLR_INT(XU); /* clear master interrupt */ return; } int32 xu_int (void) { int i; for (i=0; ivar->irq) { /* if interrupt pending */ xu_clrint(xu); /* clear interrupt */ return xu->dib->vec; /* return vector */ } } return 0; /* no interrupt request active */ } /*============================================================================== / debugging routines /=============================================================================*/ void xu_dump_rxring (CTLR* xu) { int i; int rrlen = xu->var->rrlen; printf ("receive ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->rdrb, xu->var->rrlen, xu->var->relen, xu->var->rxnext); for (i=0; ivar->rdrb + (xu->var->relen * 2) * i; t_stat rstatus = Map_ReadW (ba, 8, rxhdr); /* get rxring entry[i] */ int own = (rxhdr[2] & RXR_OWN) >> 15; int len = rxhdr[0]; uint32 addr = rxhdr[1] + ((rxhdr[2] & 3) << 16); printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, rxhdr[0], rxhdr[1], rxhdr[2], rxhdr[3]); } } void xu_dump_txring (CTLR* xu) { int i; int trlen = xu->var->trlen; printf ("transmit ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->tdrb, xu->var->trlen, xu->var->telen, xu->var->txnext); for (i=0; ivar->tdrb + (xu->var->telen * 2) * i; t_stat tstatus = Map_ReadW (ba, 8, txhdr); /* get rxring entry[i] */ int own = (txhdr[2] & RXR_OWN) >> 15; int len = txhdr[0]; uint32 addr = txhdr[1] + ((txhdr[2] & 3) << 16); printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, txhdr[0], txhdr[1], txhdr[2], txhdr[3]); } } simh-3.8.1/PDP11/pdp11_kg.c0000644000175000017500000004346210746646204013222 0ustar vlmvlm/* pdp11_kg.c - Communications Arithmetic Option KG11-A Copyright (c) 2007-2008, John A. Dundas III Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of 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 author. kg KG11-A Communications Arithmetic Option (M7251) 08-Jan-08 JAD First public release integrated with SIMH V3.7-3. 09-Dec-07 JAD SIMH-style debugging. Finished validating against real hardware. Support for up to 8 units, the maximum. Keep all module data in the UNIT structure. Clean up bit and mask definitions. 01-Dec-07 JAD Now work on the corner cases that the diagnostic does not check. CLR does not clear the QUO bit. Correct SR write logic. 29-Nov-07 JAD Original implementation and testing based on an idea from 07-Jul-03. Passes the ZKGAB0 diagnostic. Information necessary to create this simulation was gathered from a number of sources including: KG11-A Exclusive-OR and CRC block check manual, DEC-11-HKGAA-B-D Maintenance print set A Painless Guide to CRC Error Detection Algorithms, Ross N. Williams The original PDP-11 instruction set, as implemented in the /20, /15, /10, and /5, did not include XOR. [One of the differences tables incorrectly indicates the /04 does not implement this instruction.] This device implements XOR, XORB, and a variety of CRCs. The maintenance prints indicate that the device was probably available starting in late 1971. May need to check further. The first edition of the manual was May 1972. The device was still sold at least as late as mid-1982 according to the PDP-11 Systems and Options Summary. RSTS/E included support for up to 8 units in support of the 2780 emulation or for use with DP11, DU11, or DUP11. The device appears to have been retired by 1983-03, and possibly earlier. I/O Page Registers SR 7707x0 (read-write) status BCC 7707x2 (read-only) BCC (block check character) DR 7707x4 (write-only) data Vector: none Priority: none The KG11-A is a programmed-I/O, non-interrupting device. Therefore no vector or bus request level are necessary. It is a Unibus device but since it does not address memory itself (it only presents registers in the I/O page) it is compatible with extended Unibus machines (22-bit) as well as traditional Unibus. Implements 5 error detection codes: LRC-8 LRC-16 CRC-12 CRC-16 CRC-CCITT */ #if !defined (VM_PDP11) #error "KG11 is not supported!" #endif #include "pdp11_defs.h" extern FILE *sim_deb; extern REG cpu_reg[]; extern int32 R[]; #ifndef KG_UNITS #define KG_UNITS (8) #endif /* Control and Status Register */ #define KGSR_V_QUO (8) /* RO */ #define KGSR_V_DONE (7) /* RO */ #define KGSR_V_SEN (6) /* R/W shift enable */ #define KGSR_V_STEP (5) /* W */ #define KGSR_V_CLR (4) /* W */ #define KGSR_V_DDB (3) /* R/W double data byte */ #define KGSR_V_CRCIC (2) /* R/W */ #define KGSR_V_LRC (1) /* R/W */ #define KGSR_V_16 (0) /* R/W */ #define KGSR_M_QUO (1u << KGSR_V_QUO) #define KGSR_M_DONE (1u << KGSR_V_DONE) #define KGSR_M_SEN (1u << KGSR_V_SEN) #define KGSR_M_STEP (1u << KGSR_V_STEP) #define KGSR_M_CLR (1u << KGSR_V_CLR) #define KGSR_M_DDB (1u << KGSR_V_DDB) #define KGSR_M_CRCIC (1u << KGSR_V_CRCIC) #define KGSR_M_LRC (1u << KGSR_V_LRC) #define KGSR_M_16 (1u << KGSR_V_16) #define KG_SR_RDMASK (KGSR_M_QUO | KGSR_M_DONE | KGSR_M_SEN | KGSR_M_DDB | \ KGSR_M_CRCIC | KGSR_M_LRC | KGSR_M_16) #define KG_SR_WRMASK (KGSR_M_SEN | KGSR_M_DDB | KGSR_M_CRCIC | \ KGSR_M_LRC | KGSR_M_16) #define KG_SR_POLYMASK (KGSR_M_CRCIC|KGSR_M_LRC|KGSR_M_16) /* Unit structure redefinitions */ #define SR u3 #define BCC u4 #define DR u5 #define PULSCNT u6 #define POLY_LRC8 (0x0008) #define POLY_LRC16 (0x0080) #define POLY_CRC12 (0x0f01) #define POLY_CRC16 (0xa001) #define POLY_CCITT (0x8408) static const struct { uint16 poly; uint16 pulses; const char * const name; } config[] = { /* DDB=0 */ { POLY_CRC12, 6, "CRC-12" }, { POLY_CRC16, 8, "CRC-16" }, { POLY_LRC8, 8, "LRC-8" }, { POLY_LRC16, 8, "LRC-16" }, { 0, 0, "undefined" }, { POLY_CCITT, 8, "CRC-CCITT" }, { 0, 0, "undefined" }, { 0, 0, "undefined" }, /* DDB=1 */ { POLY_CRC12, 12, "CRC-12" }, { POLY_CRC16, 16, "CRC-16" }, { POLY_LRC8, 16, "LRC-8" }, { POLY_LRC16, 16, "LRC-16" }, { 0, 0, "undefined" }, { POLY_CCITT, 16, "CRC-CCITT" }, { 0, 0, "undefined" }, { 0, 0, "undefined" } }; /* Forward declarations */ static t_stat kg_rd (int32 *, int32, int32); static t_stat kg_wr (int32, int32, int32); static t_stat kg_reset (DEVICE *); static void do_poly (int, t_bool); static t_stat set_units (UNIT *, int32, char *, void *); /* 16-bit rotate right */ #define ROR(n,v) (((v >> n) & DMASK) | (v << (16 - n)) & DMASK) /* 8-bit rotate right */ #define RORB(n,v) (((v & 0377) >> n) | ((v << (8 - n)) & 0377)) /* KG data structures kg_dib KG PDP-11 device information block kg_unit KG unit descriptor kg_reg KG register list kg_mod KG modifiers table kg_debug KG debug names table kg_dev KG device descriptor */ static DIB kg_dib = { IOBA_KG, (IOLN_KG + 2) * KG_UNITS, &kg_rd, &kg_wr, 0, 0, 0, { NULL } }; static UNIT kg_unit[] = { { UDATA (NULL, 0, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, }; static const REG kg_reg[] = { { ORDATA (SR0, kg_unit[0].SR, 16) }, { ORDATA (SR1, kg_unit[1].SR, 16) }, { ORDATA (SR2, kg_unit[2].SR, 16) }, { ORDATA (SR3, kg_unit[3].SR, 16) }, { ORDATA (SR4, kg_unit[4].SR, 16) }, { ORDATA (SR5, kg_unit[5].SR, 16) }, { ORDATA (SR6, kg_unit[6].SR, 16) }, { ORDATA (SR7, kg_unit[7].SR, 16) }, { ORDATA (BCC0, kg_unit[0].BCC, 16) }, { ORDATA (BCC1, kg_unit[1].BCC, 16) }, { ORDATA (BCC2, kg_unit[2].BCC, 16) }, { ORDATA (BCC3, kg_unit[3].BCC, 16) }, { ORDATA (BCC4, kg_unit[4].BCC, 16) }, { ORDATA (BCC5, kg_unit[5].BCC, 16) }, { ORDATA (BCC6, kg_unit[6].BCC, 16) }, { ORDATA (BCC7, kg_unit[7].BCC, 16) }, { ORDATA (DR0, kg_unit[0].DR, 16) }, { ORDATA (DR1, kg_unit[1].DR, 16) }, { ORDATA (DR2, kg_unit[2].DR, 16) }, { ORDATA (DR3, kg_unit[3].DR, 16) }, { ORDATA (DR4, kg_unit[4].DR, 16) }, { ORDATA (DR5, kg_unit[5].DR, 16) }, { ORDATA (DR6, kg_unit[6].DR, 16) }, { ORDATA (DR7, kg_unit[7].DR, 16) }, { ORDATA (PULSCNT0, kg_unit[0].PULSCNT, 16) }, { ORDATA (PULSCNT1, kg_unit[1].PULSCNT, 16) }, { ORDATA (PULSCNT2, kg_unit[2].PULSCNT, 16) }, { ORDATA (PULSCNT3, kg_unit[3].PULSCNT, 16) }, { ORDATA (PULSCNT4, kg_unit[4].PULSCNT, 16) }, { ORDATA (PULSCNT5, kg_unit[5].PULSCNT, 16) }, { ORDATA (PULSCNT6, kg_unit[6].PULSCNT, 16) }, { ORDATA (PULSCNT7, kg_unit[7].PULSCNT, 16) }, { ORDATA (DEVADDR, kg_dib.ba, 32), REG_HRO }, { NULL } }; static const MTAB kg_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, NULL, "UNITS=0..8", &set_units, NULL, NULL }, { 0 } }; #define DBG_REG (01) #define DBG_POLY (02) #define DBG_CYCLE (04) static const DEBTAB kg_debug[] = { {"REG", DBG_REG}, {"POLY", DBG_POLY}, {"CYCLE", DBG_CYCLE}, {0}, }; DEVICE kg_dev = { "KG", (UNIT *) &kg_unit, (REG *) kg_reg, (MTAB *) kg_mod, KG_UNITS, 8, 16, 2, 8, 16, NULL, /* examine */ NULL, /* deposit */ &kg_reset, /* reset */ NULL, /* boot */ NULL, /* attach */ NULL, /* detach */ &kg_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, /* debug control */ (DEBTAB *) &kg_debug, /* debug flags */ NULL, /* memory size chage */ NULL /* logical name */ }; /* KG I/O address routines */ static t_stat kg_rd (int32 *data, int32 PA, int32 access) { int unit = (PA >> 3) & 07; if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS)) return (SCPE_NXM); switch ((PA >> 1) & 03) { case 00: /* SR */ if (DEBUG_PRI(kg_dev, DBG_REG)) fprintf (sim_deb, ">>KG%d: rd SR %06o, PC %06o\n", unit, kg_unit[unit].SR, PC); *data = kg_unit[unit].SR & KG_SR_RDMASK; break; case 01: /* BCC */ if (DEBUG_PRI(kg_dev, DBG_REG)) fprintf (sim_deb, ">>KG%d rd BCC %06o, PC %06o\n", unit, kg_unit[unit].BCC, PC); *data = kg_unit[unit].BCC & DMASK; break; case 02: /* DR */ break; default: break; } return (SCPE_OK); } static t_stat kg_wr (int32 data, int32 PA, int32 access) { int setup; int unit = (PA >> 3) & 07; if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS)) return (SCPE_NXM); switch ((PA >> 1) & 03) { case 00: /* SR */ if (access == WRITEB) data = (PA & 1) ? (kg_unit[unit].SR & 0377) | (data << 8) : (kg_unit[unit].SR & ~0377) | data; if (DEBUG_PRI(kg_dev, DBG_REG)) fprintf (sim_deb, ">>KG%d: wr SR %06o, PC %06o\n", unit, data, PC); if (data & KGSR_M_CLR) { kg_unit[unit].PULSCNT = 0; /* not sure about this */ kg_unit[unit].BCC = 0; kg_unit[unit].SR |= KGSR_M_DONE; } setup = (kg_unit[unit].SR & 017) ^ (data & 017); kg_unit[unit].SR = (kg_unit[unit].SR & ~KG_SR_WRMASK) | (data & KG_SR_WRMASK); /* if low 4b changed, reset C1 & C2 */ if (setup) { kg_unit[unit].PULSCNT = 0; if (DEBUG_PRI(kg_dev, DBG_POLY)) fprintf (sim_deb, ">>KG%d poly %s %d\n", unit, config[data & 017].name, config[data & 017].pulses); } if (data & KGSR_M_SEN) break; if (data & KGSR_M_STEP) { do_poly (unit, TRUE); break; } break; case 01: /* BCC */ break; /* ignored */ case 02: /* DR */ if (access == WRITEB) data = (PA & 1) ? (kg_unit[unit].DR & 0377) | (data << 8) : (kg_unit[unit].DR & ~0377) | data; kg_unit[unit].DR = data & DMASK; if (DEBUG_PRI(kg_dev, DBG_REG)) fprintf (sim_deb, ">>KG%d: wr DR %06o, data %06o, PC %06o\n", unit, kg_unit[unit].DR, data, PC); kg_unit[unit].SR &= ~KGSR_M_DONE; /* In a typical device, this is normally where we would use sim_activate() to initiate an I/O to be completed later. The KG is a little different. When it was first introduced, it's computation operation completed before another instruction could execute (on early PDP-11s), and software often took "advantage" of this fact by not bothering to check the status of the DONE bit. In reality, the execution time of the polynomial is dependent upon the width of the poly; if 8 bits 1us, if 16 bits, 2us. Since known existing software will break if we actually defer the computation, it is performed immediately instead. However this could easily be made into a run-time option, if there were software to validate correct operation. */ if (kg_unit[unit].SR & KGSR_M_SEN) do_poly (unit, FALSE); break; default: break; } return (SCPE_OK); } /* KG reset */ static t_stat kg_reset (DEVICE *dptr) { int i; if (DEBUG_PRI(kg_dev, DBG_REG)) fprintf (sim_deb, ">>KG: reset PC %06o\n", PC); for (i = 0; i < KG_UNITS; i++) { kg_unit[i].SR = KGSR_M_DONE; kg_unit[i].BCC = 0; kg_unit[i].PULSCNT = 0; } return (SCPE_OK); } static void cycleOneBit (int unit) { int quo; if (DEBUG_PRI(kg_dev, DBG_CYCLE)) fprintf (sim_deb, ">>KG%d: cycle s BCC %06o DR %06o\n", unit, kg_unit[unit].BCC, kg_unit[unit].DR); if (kg_unit[unit].SR & KGSR_M_DONE) return; if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0) kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) | ((kg_unit[unit].BCC >> 2) & 07700); kg_unit[unit].SR &= ~KGSR_M_QUO; quo = (kg_unit[unit].BCC & 01) ^ (kg_unit[unit].DR & 01); kg_unit[unit].BCC = (kg_unit[unit].BCC & ~01) | quo; if (kg_unit[unit].SR & KGSR_M_LRC) kg_unit[unit].BCC = (kg_unit[unit].SR & KGSR_M_16) ? ROR(1, kg_unit[unit].BCC) : RORB(1, kg_unit[unit].BCC); else kg_unit[unit].BCC = (kg_unit[unit].BCC & 01) ? (kg_unit[unit].BCC >> 1) ^ config[kg_unit[unit].SR & 07].poly : kg_unit[unit].BCC >> 1; kg_unit[unit].DR >>= 1; kg_unit[unit].SR |= quo << KGSR_V_QUO; if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0) kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) | ((kg_unit[unit].BCC & 07700) << 2); kg_unit[unit].PULSCNT++; if (kg_unit[unit].PULSCNT >= config[kg_unit[unit].SR & 017].pulses) kg_unit[unit].SR |= KGSR_M_DONE; if (DEBUG_PRI(kg_dev, DBG_CYCLE)) fprintf (sim_deb, ">>KG%d: cycle e BCC %06o DR %06o\n", unit, kg_unit[unit].BCC, kg_unit[unit].DR); } static void do_poly (int unit, t_bool step) { if (kg_unit[unit].SR & KGSR_M_DONE) return; if (step) cycleOneBit (unit); else { while (!(kg_unit[unit].SR & KGSR_M_DONE)) cycleOneBit (unit); } } static t_stat set_units (UNIT *u, int32 val, char *s, void *desc) { int32 i, units; t_stat stat; if (s == NULL) return (SCPE_ARG); units = get_uint (s, 10, KG_UNITS, &stat); if (stat != SCPE_OK) return (stat); for (i = 0; i < KG_UNITS; i++) { if (i < units) kg_unit[i].flags &= ~UNIT_DIS; else kg_unit[i].flags |= UNIT_DIS; } kg_dev.numunits = units; return (SCPE_OK); } simh-3.8.1/PDP11/pdp11_lp.c0000644000175000017500000001477611110156420013220 0ustar vlmvlm/* pdp11_lp.c: PDP-11 line printer simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. lpt LP11 line printer 19-Jan-07 RMS Added UNIT_TEXT flag 07-Jul-05 RMS Removed extraneous externs 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 29-Sep-02 RMS Added vector change/display support New data structures 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Added enable/disable support 09-Nov-01 RMS Added VAX support 07-Sep-01 RMS Revised interrupt mechanism 30-Oct-00 RMS Standardized register naming */ #if defined (VM_PDP10) /* PDP10 version */ #error "LP11 is not supported on the PDP-10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #else /* PDP-11 version */ #include "pdp11_defs.h" #endif #define LPTCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* implemented */ #define LPTCSR_RW (CSR_IE) /* read/write */ extern int32 int_req[IPL_HLVL]; int32 lpt_csr = 0; /* control/status */ int32 lpt_stopioe = 0; /* stop on error */ DEVICE lpt_dev; t_stat lpt_rd (int32 *data, int32 PA, int32 access); t_stat lpt_wr (int32 data, int32 PA, int32 access); t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, char *ptr); t_stat lpt_detach (UNIT *uptr); /* LPT data structures lpt_dev LPT device descriptor lpt_unit LPT unit descriptor lpt_reg LPT register list */ DIB lpt_dib = { IOBA_LPT, IOLN_LPT, &lpt_rd, &lpt_wr, 1, IVCL (LPT), VEC_LPT, { NULL } }; UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT }; REG lpt_reg[] = { { GRDATA (BUF, lpt_unit.buf, DEV_RDX, 8, 0) }, { GRDATA (CSR, lpt_csr, DEV_RDX, 16, 0) }, { FLDATA (INT, IREQ (LPT), INT_V_LPT) }, { FLDATA (ERR, lpt_csr, CSR_V_ERR) }, { FLDATA (DONE, lpt_csr, CSR_V_DONE) }, { FLDATA (IE, lpt_csr, CSR_V_IE) }, { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, { GRDATA (DEVADDR, lpt_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, lpt_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB lpt_mod[] = { { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE lpt_dev = { "LPT", &lpt_unit, lpt_reg, lpt_mod, 1, 10, 31, 1, DEV_RDX, 8, NULL, NULL, &lpt_reset, NULL, &lpt_attach, &lpt_detach, &lpt_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS }; /* Line printer routines lpt_rd I/O page read lpt_wr I/O page write lpt_svc process event (printer ready) lpt_reset process reset lpt_attach process attach lpt_detach process detach */ t_stat lpt_rd (int32 *data, int32 PA, int32 access) { if ((PA & 02) == 0) /* csr */ *data = lpt_csr & LPTCSR_IMP; else *data = lpt_unit.buf; /* buffer */ return SCPE_OK; } t_stat lpt_wr (int32 data, int32 PA, int32 access) { if ((PA & 02) == 0) { /* csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) CLR_INT (LPT); else if ((lpt_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (LPT); lpt_csr = (lpt_csr & ~LPTCSR_RW) | (data & LPTCSR_RW); } else { /* buffer */ if ((PA & 1) == 0) lpt_unit.buf = data & 0177; lpt_csr = lpt_csr & ~CSR_DONE; CLR_INT (LPT); if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || (lpt_unit.buf == 012)) sim_activate (&lpt_unit, lpt_unit.wait); else sim_activate (&lpt_unit, 0); } return SCPE_OK; } t_stat lpt_svc (UNIT *uptr) { lpt_csr = lpt_csr | CSR_ERR | CSR_DONE; if (lpt_csr & CSR_IE) SET_INT (LPT); if ((uptr->flags & UNIT_ATT) == 0) return IORETURN (lpt_stopioe, SCPE_UNATT); fputc (uptr->buf & 0177, uptr->fileref); uptr->pos = ftell (uptr->fileref); if (ferror (uptr->fileref)) { perror ("LPT I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } lpt_csr = lpt_csr & ~CSR_ERR; return SCPE_OK; } t_stat lpt_reset (DEVICE *dptr) { lpt_unit.buf = 0; lpt_csr = CSR_DONE; if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; CLR_INT (LPT); sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; } t_stat lpt_attach (UNIT *uptr, char *cptr) { t_stat reason; lpt_csr = lpt_csr & ~CSR_ERR; reason = attach_unit (uptr, cptr); if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; return reason; } t_stat lpt_detach (UNIT *uptr) { lpt_csr = lpt_csr | CSR_ERR; return detach_unit (uptr); } simh-3.8.1/PDP11/pdp11_xq.c0000644000175000017500000021632110635475674013255 0ustar vlmvlm/* pdp11_xq.c: DEQNA/DELQA ethernet controller simulator ------------------------------------------------------------------------------ Copyright (c) 2002-2007, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ This DEQNA/DELQA simulation is based on: Digital DELQA Users Guide, Part# EK-DELQA-UG-002 Digital DEQNA Users Guide, Part# EK-DEQNA-UG-001 These manuals can be found online at: http://www.spies.com/~aek/pdf/dec/qbus Certain adaptations have been made because this is an emulation: Ethernet transceiver power flag CSR<12> is ON when attached. External Loopback does not go out to the physical adapter, it is implemented more like an extended Internal Loopback Time Domain Reflectometry (TDR) numbers are faked The 10-second approx. hardware/software reset delay does not exist Some physical ethernet receive events like Runts, Overruns, etc. are never reported back, since the packet-level driver never sees them Certain advantages are derived from this emulation: If the real ethernet controller is faster than 10Mbit/sec, the speed is seen by the simulated cpu since there are no minimum response times. Known Bugs or Unsupported features, in priority order: 1) PDP11 bootstrap 2) MOP functionality not implemented 3) Local packet processing not implemented Regression Tests: VAX: 1. Console SHOW DEVICE 2. VMS v7.2 boots/initializes/shows device 3. VMS DECNET - SET HOST and COPY tests 4. VMS MultiNet - SET HOST/TELNET and FTP tests 5. VMS LAT - SET HOST/LAT tests 6. VMS Cluster - SHOW CLUSTER, SHOW DEVICE, and cluster COPY tests 7. Console boot into VMSCluster (>>>B XQAO) PDP11: 1. RT-11 v5.3 - FTPSB copy test 2. RSTS/E v10.1 - detects/enables device ------------------------------------------------------------------------------ Modification history: 18-Jun-07 RMS Added UNIT_IDLE flag 29-Oct-06 RMS Synced poll and clock 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) 07-Jan-06 RMS Fixed unaligned access bugs (found by Doug Carman) 07-Sep-05 DTH Removed unused variable 16-Aug-05 RMS Fixed C++ declaration and cast problems 01-Dec-04 DTH Added runtime attach prompt 27-Feb-04 DTH Removed struct timeb deuggers 31-Jan-04 DTH Replaced #ifdef debuggers with inline debugging 19-Jan-04 DTH Combined service timers into one for efficiency 16-Jan-04 DTH Added more info to SHOW MOD commands, added SET/SHOW XQ DEBUG 13-Jan-04 DTH Corrected interrupt code with help from Tom Evans 06-Jan-04 DTH Added protection against changing mac and type if attached 05-Jan-04 DTH Moved most of xq_setmac to sim_ether 26-Dec-03 DTH Moved ethernet show and queue functions to sim_ether 03-Dec-03 DTH Added minimum name length to show xq eth 25-Nov-03 DTH Reworked interrupts to fix broken XQB implementation 19-Nov-03 MP Rearranged timer reset sequencing to allow for a device to be disabled after it had been enabled. 17-Nov-03 DTH Standardized #include of timeb.h 28-Sep-03 MP - Fixed bug in xq_process_setup which would leave the device in promiscuous or all multicast mode once it ever had been there. - Fixed output format in show_xq_sanity to end in "\n" - Added display of All Multicase and promiscuous to xq_show_filters - The stuck in All Multicast or Promiscuous issue is worse than previously thought. See comments in xq_process_setup. - Change xq_setmac to also allow ":" as a address separator character, since sim_ether's eth_mac_fmt formats them with this separator character. - Changed xq_sw_reset to behave more like the set of actions described in Table 3-6 of the DELQA manua. The manual mentions "N/A" which I'm interpreting to mean "Not Affected". 05-Jun-03 DTH Added receive packet splitting 03-Jun-03 DTH Added SHOW XQ FILTERS 02-Jun-03 DTH Added SET/SHOW XQ STATS (packet statistics), runt & giant processing 28-May-03 DTH Modified message queue for dynamic size to shrink executable 28-May-03 MP Fixed bug in xq_setmac 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 Removed SET ADDRESS functionality 05-May-03 DTH Added second controller 26-Mar-03 DTH Added PDP11 bootrom loader Adjusted xq_ex and xq_dev to allow pdp11 to look at bootrom Patched bootrom to allow "pass" of diagnostics on RSTS/E 06-Mar-03 DTH Corrected interrupts on IE state transition (code by Tom Evans) Added interrupt clear on soft reset (first noted by Bob Supnik) Removed interrupt when setting XL or RL (multiple people) 16-Jan-03 DTH Merged Mark Pizzolato's enhancements with main source Corrected PDP11 XQ_DEBUG compilation 15-Jan-03 MP Fixed the number of units in the xq device structure. 13-Jan-03 MP Reworked the timer management logic which initiated the system id broadcast messages. The original implementation triggered this on the CSR transition of Receiver Enabled. This was an issue since the it seems that at least VMS's XQ driver makes this transition often and the resulting overhead reduces the simulated CPU instruction execution throughput by about 40%. I start the system id timer on device reset and it fires once a second so that it can leverage the reasonably recalibrated tmr_poll value. 13-Jan-03 MP Changed the scheduling of xq_svc to leverage the dynamically computed clock values to achieve an approximate interval of 100 per second. This is more than sufficient for normal system behaviour expecially since we service receives with every transmit. The previous fixed value of 2500 attempted to get 200/sec but it was a guess that didn't adapt. On faster host systems (possibly most of them) the 2500 number spends too much time polling. 10-Jan-03 DTH Removed XQ_DEBUG dependency from Borland #pragmas Added SET XQ BOOTROM command for PDP11s 07-Jan-03 DTH Added pointer to online manuals 02-Jan-03 DTH Added local packet processing 30-Dec-02 DTH Added automatic system id broadcast 27-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source 20-Dec-02 MP Fix bug that caused VMS system crashes when attempting cluster operations. Added additional conditionally compiled debug info needed to track down the issue. 17-Dec-02 MP Added SIMH "registers" describing the Ethernet state so this information can be recorded in a "saved" snapshot. 05-Dec-02 MP Adjusted the rtime value from 100 to 2500 which increased the available CPU cycles for Instruction execution by almost 100%. This made sense after the below enhancements which, in general caused the draining of the received data stream much more agressively with less overhead. 05-Dec-02 MP Added a call to xq_svc after all successful calls to eth_write to allow receive processing to happen before the next event service time. 05-Dec-02 MP Restructured the flow of processing in xq_svc so that eth_read is called repeatedly until either a packet isn't found or there is no room for another one in the queue. Once that has been done, xq_process_rdbl is called to pass the queued packets into the simulated system as space is available there. xq_process_rdbl is also called at the beginning of xq_svc to drain the queue into the simulated system, making more room available in the queue. No processing is done at all in xq_svc if the receiver is disabled. 04-Dec-02 MP Changed interface and usage to xq_insert_queue to pass the packet to be inserted by reference. This avoids 3K bytes of buffer copy operations for each packet received. Now only copy actual received packet data. 31-Oct-02 DTH Cleaned up pointer warnings (found by Federico Schwindt) Corrected unattached and no network behavior Added message when SHOW XQ ETH finds no devices 23-Oct-02 DTH Beta 5 released 22-Oct-02 DTH Added all_multicast and promiscuous support 21-Oct-02 DTH Added write buffer max size check (code by Jason Thorpe) Corrected copyright again Implemented NXM testing and recovery 16-Oct-02 DTH Beta 4 released Added and debugged Sanity Timer code Corrected copyright 15-Oct-02 DTH Rollback to known good Beta3 and roll forward; TCP broken 12-Oct-02 DTH Fixed VAX network bootstrap; setup packets must return TDR > 0 11-Oct-02 DTH Added SET/SHOW XQ TYPE and SET/SHOW XQ SANITY commands 10-Oct-02 DTH Beta 3 released; Integrated with 2.10-0b1 Fixed off-by-1 bug on xq->setup.macs[7..13] Added xq_make_checksum Added rejection of multicast addresses in SET XQ MAC 08-Oct-02 DTH Beta 2 released; Integrated with 2.10-0p4 Added variable vector (fixes PDP11) and copyrights 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 24-Sep-02 DTH Moved more code to Sim_Ether module, added SHOW ETH command 23-Sep-02 DTH Added SET/SHOW MAC command 22-Sep-02 DTH Multinet TCP/IP loaded, tests OK via SET HOST/TELNET 20-Sep-02 DTH Cleaned up code fragments, fixed non-DECNET MAC use 19-Sep-02 DTH DECNET finally stays up; successful SET HOST to another node 15-Sep-02 DTH Added ethernet packet read/write 13-Sep-02 DTH DECNET starts, but circuit keeps going up & down 26-Aug-02 DTH DECNET loaded, returns device timeout 22-Aug-02 DTH VMS 7.2 recognizes device as XQA0 18-Aug-02 DTH VAX sees device as XQA0; shows hardcoded MAC correctly 15-Aug-02 DTH Started XQ simulation ------------------------------------------------------------------------------ */ #include #include "pdp11_xq.h" #include "pdp11_xq_bootrom.h" extern int32 tmxr_poll; extern FILE* sim_deb; extern char* read_line (char *ptr, int32 size, FILE *stream); /* forward declarations */ t_stat xq_rd(int32* data, int32 PA, int32 access); t_stat xq_wr(int32 data, int32 PA, int32 access); t_stat xq_svc(UNIT * uptr); t_stat xq_reset (DEVICE * dptr); t_stat xq_attach (UNIT * uptr, char * cptr); t_stat xq_detach (UNIT * uptr); t_stat xq_showmac (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_process_xbdl(CTLR* xq); t_stat xq_dispatch_xbdl(CTLR* xq); void xq_start_receiver(void); void xq_sw_reset(CTLR* xq); t_stat xq_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat xq_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void xq_reset_santmr(CTLR* xq); t_stat xq_boot_host(CTLR* xq); t_stat xq_system_id(CTLR* xq, const ETH_MAC dst, uint16 receipt_id); void xqa_read_callback(int status); void xqb_read_callback(int status); void xqa_write_callback(int status); void xqb_write_callback(int status); void xq_setint (CTLR* xq); void xq_clrint (CTLR* xq); int32 xq_int (void); void xq_csr_set_clr(CTLR* xq, uint16 set_bits, uint16 clear_bits); struct xq_device xqa = { xqa_read_callback, /* read callback routine */ xqa_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xAA, 0xBB, 0xCC}, /* mac */ XQ_T_DELQA, /* type */ XQ_SERVICE_INTERVAL, /* poll */ {0} /* sanity */ }; struct xq_device xqb = { xqb_read_callback, /* read callback routine */ xqb_write_callback, /* write callback routine */ {0x08, 0x00, 0x2B, 0xBB, 0xCC, 0xDD}, /* mac */ XQ_T_DELQA, /* type */ XQ_SERVICE_INTERVAL, /* poll */ {0} /* sanity */ }; /* SIMH device structures */ DIB xqa_dib = { IOBA_XQ, IOLN_XQ, &xq_rd, &xq_wr, 1, IVCL (XQ), 0, { &xq_int } }; UNIT xqa_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ }; REG xqa_reg[] = { { GRDATA ( SA0, xqa.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA1, xqa.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA2, xqa.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA3, xqa.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA4, xqa.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA5, xqa.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqa.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqa.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqa.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDH, xqa.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( VAR, xqa.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqa.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqa.irq, 0) }, { GRDATA ( SETUP_PRM, xqa.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqa.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqa.setup.l1, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L2, xqa.setup.l2, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L3, xqa.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqa.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqa.setup.macs, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, { NULL }, }; DIB xqb_dib = { IOBA_XQB, IOLN_XQB, &xq_rd, &xq_wr, 1, IVCL (XQ), 0, { &xq_int } }; UNIT xqb_unit[] = { { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ }; REG xqb_reg[] = { { GRDATA ( SA0, xqb.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA1, xqb.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA2, xqb.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA3, xqb.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA4, xqb.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( SA5, xqb.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, { GRDATA ( RBDL, xqb.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( RBDH, xqb.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDL, xqb.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( XBDH, xqb.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( VAR, xqb.var, XQ_RDX, 16, 0), REG_FIT }, { GRDATA ( CSR, xqb.csr, XQ_RDX, 16, 0), REG_FIT }, { FLDATA ( INT, xqb.irq, 0) }, { GRDATA ( SETUP_PRM, xqb.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_MLT, xqb.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L1, xqb.setup.l1, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L2, xqb.setup.l2, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_L3, xqb.setup.l3, XQ_RDX, 32, 0), REG_HRO}, { GRDATA ( SETUP_SAN, xqb.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, { BRDATA ( SETUP_MACS, &xqb.setup.macs, XQ_RDX, 8, sizeof(xqb.setup.macs)), REG_HRO}, { NULL }, }; MTAB xq_mod[] = { { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", &xq_setmac, &xq_showmac, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", NULL, ð_show, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "FILTERS", "FILTERS", NULL, &xq_show_filters, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", &xq_set_stats, &xq_show_stats, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA}", &xq_set_type, &xq_show_type, NULL }, { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|4..2500]", &xq_set_poll, &xq_show_poll, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", &xq_set_sanity, &xq_show_sanity, NULL }, { 0 }, }; DEBTAB xq_debug[] = { {"TRACE", DBG_TRC}, {"CSR", DBG_CSR}, {"VAR", DBG_VAR}, {"WARN", DBG_WRN}, {"SETUP", DBG_SET}, {"SANITY", DBG_SAN}, {"REG", DBG_REG}, {"PACKET", DBG_PCK}, {"ETH", DBG_ETH}, {0} }; DEVICE xq_dev = { "XQ", xqa_unit, xqa_reg, xq_mod, 1, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqa_dib, DEV_DISABLE | DEV_QBUS | DEV_DEBUG, 0, xq_debug }; DEVICE xqb_dev = { "XQB", xqb_unit, xqb_reg, xq_mod, 1, XQ_RDX, 11, 1, XQ_RDX, 16, &xq_ex, &xq_dep, &xq_reset, NULL, &xq_attach, &xq_detach, &xqb_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, 0, xq_debug }; CTLR xq_ctrl[] = { {&xq_dev, xqa_unit, &xqa_dib, &xqa}, /* XQA controller */ {&xqb_dev, xqb_unit, &xqb_dib, &xqb} /* XQB controller */ }; const char* const xq_recv_regnames[] = { "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "VAR", "CSR" }; const char* const xq_xmit_regnames[] = { "", "", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" }; const char* const xq_csr_bits[] = { "RE", "SR", "NI", "BD", "XL", "RL", "IE", "XI", "IL", "EL", "SE", "RR", "OK", "CA", "PE", "RI" }; const char* const xq_var_bits[] = { "ID", "RR", "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "S1", "S2", "S3", "RS", "OS", "MS" }; /* internal debugging routines */ void xq_debug_setup(CTLR* xq); /*============================================================================*/ /* Multicontroller support */ CTLR* xq_unit2ctlr(UNIT* uptr) { unsigned int i,j; for (i=0; inumunits; j++) if (&xq_ctrl[i].unit[j] == uptr) return &xq_ctrl[i]; /* not found */ return 0; } CTLR* xq_dev2ctlr(DEVICE* dptr) { int i; for (i=0; i= xq_ctrl[i].dib->ba) && (PA < (xq_ctrl[i].dib->ba + xq_ctrl[i].dib->lnt))) return &xq_ctrl[i]; /* not found */ return 0; } /*============================================================================*/ /* stop simh from reading non-existant unit data stream */ t_stat xq_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw) { /* on PDP-11, allow EX command to look at bootrom */ #ifdef VM_PDP11 if (addr <= sizeof(xq_bootrom)/2) *vptr = xq_bootrom[addr]; else *vptr = 0; return SCPE_OK; #else return SCPE_NOFNC; #endif } /* stop simh from writing non-existant unit data stream */ t_stat xq_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw) { return SCPE_NOFNC; } t_stat xq_showmac (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); char buffer[20]; eth_mac_fmt((ETH_MAC*)xq->var->mac, buffer); fprintf(st, "MAC=%s", buffer); return SCPE_OK; } void xq_make_checksum(CTLR* xq) { /* checksum calculation routine detailed in vaxboot.zip/xqbtdrivr.mar */ uint32 checksum = 0; const uint32 wmask = 0xFFFF; int i; for (i = 0; i < sizeof(ETH_MAC); i += 2) { checksum <<= 1; if (checksum > wmask) checksum -= wmask; checksum += (xq->var->mac[i] << 8) | xq->var->mac[i+1]; if (checksum > wmask) checksum -= wmask; } if (checksum == wmask) checksum = 0; /* set checksum bytes */ xq->var->mac_checksum[0] = checksum & 0xFF; xq->var->mac_checksum[1] = checksum >> 8; } t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc) { t_stat status; CTLR* xq = xq_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; status = eth_mac_scan(&xq->var->mac, cptr); if (status != SCPE_OK) return status; /* calculate mac checksum */ xq_make_checksum(xq); return SCPE_OK; } t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) { /* this sets all ints in the stats structure to the integer passed */ CTLR* xq = xq_unit2ctlr(uptr); if (cptr) { /* set individual stats to passed parameter value */ int init = atoi(cptr); int* stat_array = (int*) &xq->var->stats; int elements = sizeof(struct xq_stats)/sizeof(int); int i; for (i=0; ivar->stats, 0, sizeof(struct xq_stats)); } return SCPE_OK; } t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) { char* fmt = " %-15s%d\n"; CTLR* xq = xq_unit2ctlr(uptr); fprintf(st, "Ethernet statistics:\n"); fprintf(st, fmt, "Recv:", xq->var->stats.recv); fprintf(st, fmt, "Filtered:", xq->var->stats.filter); fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); fprintf(st, fmt, "Runts:", xq->var->stats.runt); fprintf(st, fmt, "Oversize:", xq->var->stats.giant); fprintf(st, fmt, "Setup:", xq->var->stats.setup); fprintf(st, fmt, "Loopback:", xq->var->stats.loop); fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); return SCPE_OK; } t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); char buffer[20]; int i; fprintf(st, "Filters:\n"); for (i=0; ivar->setup.macs[i], buffer); fprintf(st, " [%2d]: %s\n", i, buffer); }; if (xq->var->setup.multicast) fprintf(st, "All Multicast Receive Mode\n"); if (xq->var->setup.promiscuous) fprintf(st, "Promiscuous Receive Mode\n"); return SCPE_OK; } t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); fprintf(st, "type="); switch (xq->var->type) { case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; case XQ_T_DELQA: fprintf(st, "DELQA"); break; } return SCPE_OK; } t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEQNA")) xq->var->type = XQ_T_DEQNA; else if (!strcmp(cptr, "DELQA")) xq->var->type = XQ_T_DELQA; else return SCPE_ARG; return SCPE_OK; } t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); fprintf(st, "poll=%d", xq->var->poll); return SCPE_OK; } t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "DEFAULT")) xq->var->poll = XQ_SERVICE_INTERVAL; else { int newpoll = 0; sscanf(cptr, "%d", &newpoll); if ((newpoll >= 4) && (newpoll <= 2500)) xq->var->poll = newpoll; else return SCPE_ARG; } return SCPE_OK; } t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); fprintf(st, "sanity="); switch (xq->var->sanity.enabled) { case 2: fprintf(st, "ON\n"); break; default: fprintf(st, "OFF\n"); break; } return SCPE_OK; } t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc) { CTLR* xq = xq_unit2ctlr(uptr); if (!cptr) return SCPE_IERR; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; /* this assumes that the parameter has already been upcased */ if (!strcmp(cptr, "ON")) xq->var->sanity.enabled = 2; else if (!strcmp(cptr, "OFF")) xq->var->sanity.enabled = 0; else return SCPE_ARG; return SCPE_OK; } /*============================================================================*/ t_stat xq_nxm_error(CTLR* xq) { const uint16 set_bits = XQ_CSR_NI | XQ_CSR_XI | XQ_CSR_XL | XQ_CSR_RL; sim_debug(DBG_WRN, xq->dev, "Non Existent Memory Error!\n"); /* set NXM and associated bits in CSR */ xq_csr_set_clr(xq, set_bits , 0); return SCPE_OK; } /* ** write callback */ void xq_write_callback (CTLR* xq, int status) { t_stat rstatus; int32 wstatus; const uint16 TDR = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ uint16 write_success[2] = {0}; uint16 write_failure[2] = {XQ_DSC_C}; write_success[1] = TDR & 0x03FF; /* Does TDR get set on successful packets ?? */ write_failure[1] = TDR & 0x03FF; /* TSW2<09:00> */ xq->var->stats.xmit += 1; /* update write status words */ if (status == 0) { /* success */ wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_success); } else { /* failure */ sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); xq->var->stats.fail += 1; wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_failure); } if (wstatus) { xq_nxm_error(xq); return; } /* update csr */ xq_csr_set_clr(xq, XQ_CSR_XI, 0); /* reset sanity timer */ xq_reset_santmr(xq); /* clear write buffer */ xq->var->write_buffer.len = 0; /* next descriptor (implicit) */ xq->var->xbdl_ba += 12; /* finish processing xbdl */ rstatus = xq_process_xbdl(xq); } void xqa_write_callback (int status) { xq_write_callback(&xq_ctrl[0], status); } void xqb_write_callback (int status) { xq_write_callback(&xq_ctrl[1], status); } /* read registers: */ t_stat xq_rd(int32* data, int32 PA, int32 access) { CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, xq_recv_regnames[index], access); switch (index) { case 0: case 1: /* return checksum in external loopback mode */ if (xq->var->csr & XQ_CSR_EL) *data = 0xFF00 | xq->var->mac_checksum[index]; else *data = 0xFF00 | xq->var->mac[index]; break; case 2: case 3: case 4: case 5: *data = 0xFF00 | xq->var->mac[index]; break; case 6: sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); *data = xq->var->var; break; case 7: sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, xq->var->csr, xq->var->csr, 1); *data = xq->var->csr; break; } return SCPE_OK; } /* dispatch ethernet read request procedure documented in sec. 3.2.2 */ t_stat xq_process_rbdl(CTLR* xq) { int32 rstatus, wstatus; uint16 b_length, w_length, rbl; uint32 address; ETH_ITEM* item; uint8* rbuf; sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl\n"); /* process buffer descriptors */ while(1) { /* get receive bdl from memory */ xq->var->rbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); if (rstatus || wstatus) return xq_nxm_error(xq); /* invalid buffer? */ if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; } /* explicit chain buffer? */ if (xq->var->rbdl_buf[1] & XQ_DSC_C) { xq->var->rbdl_ba = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; continue; } /* stop processing if nothing in read queue */ if (!xq->var->ReadQ.count) break; /* get status words */ rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (rstatus) return xq_nxm_error(xq); /* get host memory address */ address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; /* decode buffer length - two's complement (in words) */ w_length = ~xq->var->rbdl_buf[3] + 1; b_length = w_length * 2; if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; rbl = item->packet.len; rbuf = item->packet.msg; /* see if packet must be size-adjusted or is splitting */ if (item->packet.used) { int used = item->packet.used; rbl -= used; rbuf = &item->packet.msg[used]; } else { /* adjust runt packets */ if (rbl < ETH_MIN_PACKET) { xq->var->stats.runt += 1; sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) processing of those weird short ARP packets that seem to occur occasionally */ memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl); rbl = ETH_MIN_PACKET; }; /* adjust oversized packets */ if (rbl > ETH_MAX_PACKET) { xq->var->stats.giant += 1; sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); /* trim giants down to maximum size - no documentation on how to handle the data loss */ item->packet.len = ETH_MAX_PACKET; rbl = ETH_MAX_PACKET; }; }; /* make sure entire packet fits in buffer - if not, will need to split into multiple buffers */ if (rbl > b_length) rbl = b_length; item->packet.used += rbl; /* send data to host */ wstatus = Map_WriteB(address, rbl, rbuf); if (wstatus) return xq_nxm_error(xq); /* set receive size into RBL - RBL<10:8> maps into Status1<10:8>, RBL<7:0> maps into Status2<7:0>, and Status2<15:8> (copy) */ xq->var->rbdl_buf[4] = 0; switch (item->type) { case 0: /* setup packet */ xq->var->stats.setup += 1; xq->var->rbdl_buf[4] = 0x2700; /* set esetup and RBL 10:8 */ break; case 1: /* loopback packet */ xq->var->stats.loop += 1; xq->var->rbdl_buf[4] = 0x2000; /* loopback flag */ xq->var->rbdl_buf[4] |= (rbl & 0x0700); /* high bits of rbl */ break; case 2: /* normal packet */ rbl -= 60; /* keeps max packet size in 11 bits */ xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */ break; } if (item->packet.used < item->packet.len) xq->var->rbdl_buf[4] |= 0xC000; /* not last segment */ xq->var->rbdl_buf[5] = ((rbl & 0x00FF) << 8) | (rbl & 0x00FF); if (xq->var->ReadQ.loss) { sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ xq->var->ReadQ.loss = 0; /* reset loss counter */ } /* update read status words*/ wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (wstatus) return xq_nxm_error(xq); /* remove packet from queue */ if (item->packet.used >= item->packet.len) ethq_remove(&xq->var->ReadQ); /* mark transmission complete */ xq_csr_set_clr(xq, XQ_CSR_RI, 0); /* set to next bdl (implicit chain) */ xq->var->rbdl_ba += 12; } /* while */ return SCPE_OK; } t_stat xq_process_mop(CTLR* xq) { uint32 address; uint16 size; int32 wstatus; struct xq_meb* meb = (struct xq_meb*) &xq->var->write_buffer.msg[0200]; const struct xq_meb* limit = (struct xq_meb*) &xq->var->write_buffer.msg[0400]; sim_debug(DBG_TRC, xq->dev, "xq_process_mop()\n"); if (xq->var->type == XQ_T_DEQNA) /* DEQNA's don't MOP */ return SCPE_NOFNC; while ((meb->type != 0) && (meb < limit)) { address = (meb->add_hi << 16) || (meb->add_mi << 8) || meb->add_lo; size = (meb->siz_hi << 8) || meb->siz_lo; /* MOP stuff here - NOT YET FULLY IMPLEMENTED */ sim_debug (DBG_WRN, xq->dev, "Processing MEB type: %d\n", meb->type); switch (meb->type) { case 0: /* MOP Termination */ break; case 1: /* MOP Read Ethernet Address */ wstatus = Map_WriteB(address, sizeof(ETH_MAC), (uint8*) &xq->var->setup.macs[0]); if (wstatus) return xq_nxm_error(xq); break; case 2: /* MOP Reset System ID */ break; case 3: /* MOP Read Last MOP Boot */ break; case 4: /* MOP Read Boot Password */ break; case 5: /* MOP Write Boot Password */ break; case 6: /* MOP Read System ID */ break; case 7: /* MOP Write System ID */ break; case 8: /* MOP Read Counters */ break; case 9: /* Mop Read/Clear Counters */ break; } /* switch */ /* process next meb */ meb += sizeof(struct xq_meb); } /* while */ return SCPE_OK; } t_stat xq_process_setup(CTLR* xq) { int i,j; int count = 0; float secs; t_stat status; ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; ETH_MAC filters[XQ_FILTER_MAX + 1]; sim_debug(DBG_TRC, xq->dev, "xq_process_setup()\n"); /* extract filter addresses from setup packet */ memset(xq->var->setup.macs, '\0', sizeof(xq->var->setup.macs)); for (i = 0; i < 7; i++) for (j = 0; j < 6; j++) { xq->var->setup.macs[i] [j] = xq->var->write_buffer.msg[(i + 01) + (j * 8)]; if (xq->var->write_buffer.len > 112) xq->var->setup.macs[i+7][j] = xq->var->write_buffer.msg[(i + 0101) + (j * 8)]; } /* Under VMS the setup packet that is passed to turn promiscuous off after it has been on doesn't seem to follow the rules documented in both the DEQNA and DELQA manuals. These rules seem to say that setup packets less than 128 should only modify the address filter set and probably not the All-Multicast and Promiscuous modes, however, VMS V5-5 and V7.3 seem to send a 127 byte packet to turn this functionality off. I'm not sure how real hardware behaves in this case, since the only consequence is extra interrupt load. To realize and retain the benefits of the newly added BPF functionality in sim_ether, I've modified the logic implemented here to disable Promiscuous mode when a "small" setup packet is processed. I'm deliberately not modifying the All-Multicast mode the same way since I don't have an observable case of its behavior. These two different modes come from very different usage situations: 1) Promiscuous mode is usually entered for relatively short periods of time due to the needs of a specific application program which is doing some sort of management/monitoring function (i.e. tcpdump) 2) All-Multicast mode is only entered by the OS Kernel Port Driver when it happens to have clients (usually network stacks or service programs) which as a group need to listen to more multicast ethernet addresses than the 12 (or so) which the hardware supports directly. so, I believe that the All-Multicast mode, is first rarely used, and if it ever is used, once set, it will probably be set either forever or for long periods of time, and the additional interrupt processing load to deal with the distinctly lower multicast traffic set is clearly lower than that of the promiscuous mode. */ xq->var->setup.promiscuous = 0; /* process high byte count */ if (xq->var->write_buffer.len > 128) { uint16 len = xq->var->write_buffer.len; uint16 led, san; xq->var->setup.multicast = (0 != (len & XQ_SETUP_MC)); xq->var->setup.promiscuous = (0 != (len & XQ_SETUP_PM)); if (led = (len & XQ_SETUP_LD) >> 2) { switch (led) { case 1: xq->var->setup.l1 = 0; break; case 2: xq->var->setup.l2 = 0; break; case 3: xq->var->setup.l3 = 0; break; } /* switch */ } /* if led */ /* set sanity timer timeout */ san = (len & XQ_SETUP_ST) >> 4; switch(san) { case 0: secs = 0.25; break; /* 1/4 second */ case 1: secs = 1; break; /* 1 second */ case 2: secs = 4; break; /* 4 seconds */ case 3: secs = 16; break; /* 16 seconds */ case 4: secs = 1 * 60; break; /* 1 minute */ case 5: secs = 4 * 60; break; /* 4 minutes */ case 6: secs = 16 * 60; break; /* 16 minutes */ case 7: secs = 64 * 60; break; /* 64 minutes */ } xq->var->sanity.quarter_secs = (int) (secs * 4); xq->var->sanity.max = (int) (secs * xq->var->poll); } /* finalize sanity timer state */ xq->var->sanity.timer = xq->var->sanity.max; if (xq->var->sanity.enabled != 2) { if (xq->var->csr & XQ_CSR_SE) xq->var->sanity.enabled = 1; else xq->var->sanity.enabled = 0; } /* set ethernet filter */ /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ for (i = 0; i < XQ_FILTER_MAX; i++) if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); status = eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); /* process MOP information */ if (xq->var->write_buffer.msg[0]) status = xq_process_mop(xq); /* mark setup block valid */ xq->var->setup.valid = 1; if (sim_deb && (xq->dev->dctrl & DBG_SET)) xq_debug_setup(xq); return SCPE_OK; } /* Dispatch Write Operation The DELQA manual does not explicitly state whether or not multiple packets can be written in one transmit operation, so a maximum of 1 packet is assumed. */ t_stat xq_process_xbdl(CTLR* xq) { const uint16 implicit_chain_status[2] = {XQ_DSC_V | XQ_DSC_C, 1}; const uint16 write_success[2] = {0, 1 /*Non-Zero TDR*/}; uint16 b_length, w_length; int32 rstatus, wstatus; uint32 address; t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_process_xbdl()\n"); /* clear write buffer */ xq->var->write_buffer.len = 0; /* process buffer descriptors until not valid */ while (1) { /* Get transmit bdl from memory */ rstatus = Map_ReadW (xq->var->xbdl_ba, 12, &xq->var->xbdl_buf[0]); xq->var->xbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->xbdl_ba, 2, &xq->var->xbdl_buf[0]); if (rstatus || wstatus) return xq_nxm_error(xq); /* invalid buffer? */ if (~xq->var->xbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_XL, 0); sim_debug(DBG_WRN, xq->dev, "XBDL List empty\n"); return SCPE_OK; } /* compute host memory address */ address = ((xq->var->xbdl_buf[1] & 0x3F) << 16) | xq->var->xbdl_buf[2]; /* decode buffer length - two's complement (in words) */ w_length = ~xq->var->xbdl_buf[3] + 1; b_length = w_length * 2; if (xq->var->xbdl_buf[1] & XQ_DSC_H) b_length -= 1; if (xq->var->xbdl_buf[1] & XQ_DSC_L) b_length -= 1; /* explicit chain buffer? */ if (xq->var->xbdl_buf[1] & XQ_DSC_C) { xq->var->xbdl_ba = address; sim_debug(DBG_WRN, xq->dev, "XBDL chained buffer encountered: %d\n", b_length); continue; } /* add to transmit buffer, making sure it's not too big */ if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) b_length = sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len; rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (rstatus) return xq_nxm_error(xq); xq->var->write_buffer.len += b_length; /* end of message? */ if (xq->var->xbdl_buf[1] & XQ_DSC_E) { if (((~xq->var->csr & XQ_CSR_RE) && ((~xq->var->csr & XQ_CSR_IL) || (xq->var->csr & XQ_CSR_EL))) || /* loopback */ (xq->var->xbdl_buf[1] & XQ_DSC_S)) { /* or setup packet (forces loopback regardless of state) */ if (xq->var->xbdl_buf[1] & XQ_DSC_S) { /* setup packet */ status = xq_process_setup(xq); /* put packet in read buffer */ ethq_insert (&xq->var->ReadQ, 0, &xq->var->write_buffer, status); } else { /* loopback */ /* put packet in read buffer */ ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); } /* update write status */ wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, (uint16*) write_success); if (wstatus) return xq_nxm_error(xq); /* clear write buffer */ xq->var->write_buffer.len = 0; /* reset sanity timer */ xq_reset_santmr(xq); /* mark transmission complete */ xq_csr_set_clr(xq, XQ_CSR_XI, 0); /* now trigger "read" of setup or loopback packet */ if (~xq->var->csr & XQ_CSR_RL) status = xq_process_rbdl(xq); } else { /* not loopback */ status = eth_write(xq->var->etherface, &xq->var->write_buffer, xq->var->wcallback); if (status != SCPE_OK) /* not implemented or unattached */ xq_write_callback(xq, 1); /* fake failure */ #if 0 else xq_svc(&xq->unit[0]); /* service any received data */ #endif sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); return SCPE_OK; } /* loopback/non-loopback */ } else { /* not at end-of-message */ sim_debug(DBG_WRN, xq->dev, "XBDL processing implicit chain buffer segment\n"); /* update bdl status words */ wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, (uint16*) implicit_chain_status); if(wstatus) return xq_nxm_error(xq); } /* set to next bdl (implicit chain) */ xq->var->xbdl_ba += 12; } /* while */ } t_stat xq_dispatch_rbdl(CTLR* xq) { int i; int32 rstatus, wstatus; t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_dispatch_rbdl()\n"); /* mark receive bdl valid */ xq_csr_set_clr(xq, 0, XQ_CSR_RL); /* init receive bdl buffer */ for (i=0; i<6; i++) xq->var->rbdl_buf[i] = 0; /* get address of first receive buffer */ xq->var->rbdl_ba = ((xq->var->rbdl[1] & 0x3F) << 16) | (xq->var->rbdl[0] & ~01); /* get first receive buffer */ xq->var->rbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); if (rstatus || wstatus) return xq_nxm_error(xq); /* is buffer valid? */ if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; } /* process any waiting packets in receive queue */ if (xq->var->ReadQ.count) status = xq_process_rbdl(xq); return SCPE_OK; } t_stat xq_dispatch_xbdl(CTLR* xq) { int i; t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_dispatch_xbdl()\n"); /* mark transmit bdl valid */ xq_csr_set_clr(xq, 0, XQ_CSR_XL); /* initialize transmit bdl buffers */ for (i=0; i<6; i++) xq->var->xbdl_buf[i] = 0; /* clear transmit buffer */ xq->var->write_buffer.len = 0; /* get base address of first transmit descriptor */ xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01); /* process xbdl */ status = xq_process_xbdl(xq); return status; } t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) { ETH_PACK reply; ETH_MAC physical_address; t_stat status; int offset = pack->msg[14] | (pack->msg[15] << 8); int function = pack->msg[offset] | (pack->msg[offset+1] << 8); sim_debug(DBG_TRC, xq->dev, "xq_process_loopback()\n"); if (function != 2 /*forward*/) return SCPE_NOFNC; /* create reply packet */ memcpy (&reply, pack, sizeof(ETH_PACK)); memcpy (physical_address, xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); memcpy (&reply.msg[0], &reply.msg[offset+2], sizeof(ETH_MAC)); memcpy (&reply.msg[6], physical_address, sizeof(ETH_MAC)); memcpy (&reply.msg[offset+2], physical_address, sizeof(ETH_MAC)); reply.msg[offset] = 0x01; offset += 8; reply.msg[14] = offset & 0xFF; reply.msg[15] = (offset >> 8) & 0xFF; /* send reply packet */ status = eth_write(xq->var->etherface, &reply, NULL); return status; } t_stat xq_process_remote_console (CTLR* xq, ETH_PACK* pack) { t_stat status; ETH_MAC source; uint16 receipt; int code = pack->msg[16]; sim_debug(DBG_TRC, xq->dev, "xq_process_remote_console()\n"); switch (code) { case 0x05: /* request id */ receipt = pack->msg[18] | (pack->msg[19] << 8); memcpy(source, &pack->msg[6], sizeof(ETH_MAC)); /* send system id to requestor */ status = xq_system_id (xq, source, receipt); return status; break; case 0x06: /* boot */ /* NOTE: the verification field should be checked here against the verification value established in the setup packet. If they match the reboot should occur, otherwise nothing happens, and the packet is passed on to the host. Verification is not implemented, since the setup packet processing code isn't complete yet. Various values are also passed: processor, control, and software id. These control the various boot parameters, however SIMH does not have a mechanism to pass these to the host, so just reboot. */ status = xq_boot_host(xq); return status; break; } /* switch */ return SCPE_NOFNC; } t_stat xq_process_local (CTLR* xq, ETH_PACK* pack) { /* returns SCPE_OK if local processing occurred, otherwise returns SCPE_NOFNC or some other code */ int protocol; sim_debug(DBG_TRC, xq->dev, "xq_process_local()\n"); /* DEQNA's have no local processing capability */ if (xq->var->type == XQ_T_DEQNA) return SCPE_NOFNC; protocol = pack->msg[12] | (pack->msg[13] << 8); switch (protocol) { case 0x0090: /* ethernet loopback */ return xq_process_loopback(xq, pack); break; case 0x0260: /* MOP remote console */ return xq_process_remote_console(xq, pack); break; } return SCPE_NOFNC; } void xq_read_callback(CTLR* xq, int status) { xq->var->stats.recv += 1; if (xq->var->csr & XQ_CSR_RE) { /* receiver enabled */ /* process any packets locally that can be */ t_stat status = xq_process_local (xq, &xq->var->read_buffer); /* add packet to read queue */ if (status != SCPE_OK) ethq_insert(&xq->var->ReadQ, 2, &xq->var->read_buffer, status); } else { sim_debug(DBG_WRN, xq->dev, "packet received with receiver disabled\n"); } } void xqa_read_callback(int status) { xq_read_callback(&xq_ctrl[0], status); } void xqb_read_callback(int status) { xq_read_callback(&xq_ctrl[1], status); } void xq_sw_reset(CTLR* xq) { const uint16 set_bits = XQ_CSR_XL | XQ_CSR_RL; int i; sim_debug(DBG_TRC, xq->dev, "xq_sw_reset()\n"); /* reset csr bits */ xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); if (xq->var->etherface) xq_csr_set_clr(xq, XQ_CSR_OK, 0); /* clear interrupt unconditionally */ xq_clrint(xq); /* flush read queue */ ethq_clear(&xq->var->ReadQ); /* clear setup info */ xq->var->setup.multicast = 0; xq->var->setup.promiscuous = 0; if (xq->var->etherface) { int count = 0; ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; ETH_MAC filters[XQ_FILTER_MAX + 1]; /* set ethernet filter */ /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ for (i = 0; i < XQ_FILTER_MAX; i++) if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); } } /* write registers: */ t_stat xq_wr_var(CTLR* xq, int32 data) { uint16 save_var = xq->var->var; sim_debug(DBG_REG, xq->dev, "xq_wr_var(data= 0x%08X\n", data); switch (xq->var->type) { case XQ_T_DEQNA: xq->var->var = (data & XQ_VEC_IV); break; case XQ_T_DELQA: xq->var->var = (xq->var->var & XQ_VEC_RO) | (data & XQ_VEC_RW); /* if switching to DEQNA-LOCK mode clear VAR<14:10> */ if (~xq->var->var & XQ_VEC_MS) xq->var->var &= ~(XQ_VEC_OS | XQ_VEC_RS | XQ_VEC_ST); break; } /* set vector of SIMH device */ if (data & XQ_VEC_IV) xq->dib->vec = (data & XQ_VEC_IV) + VEC_Q; else xq->dib->vec = 0; sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, save_var, xq->var->var, 1); return SCPE_OK; } #ifdef VM_PDP11 t_stat xq_process_bootrom (CTLR* xq) { /* NOTE: BOOT ROMs are a PDP-11ism, since they contain PDP-11 binary code. the host is responsible for creating two *2KB* receive buffers. RSTS/E v10.1 source (INIONE.MAR/XHLOOK:) indicates that both the DEQNA and DELQA will set receive status word 1 bits 15 & 14 on both packets. It also states that a hardware bug in the DEQNA will set receive status word 1 bit 15 (only) in the *third* receive buffer (oops!). RSTS/E v10.1 will run the Citizenship test from the bootrom after loading it. Documentation on the Boot ROM can be found in INIQNA.MAR. */ int32 rstatus, wstatus; uint16 b_length, w_length; uint32 address; uint8* bootrom = (uint8*) xq_bootrom; int i, checksum; sim_debug(DBG_TRC, xq->dev, "xq_process_bootrom()\n"); /* RSTS/E v10.1 invokes the Citizenship tests in the Bootrom. For some reason, the current state of the XQ emulator cannot pass these. So, to get moving on RSTE/E support, we will replace the following line in INIQNA.MAR/CITQNA:: 70$: MOV (R2),R0 ;get the status word with 70$: CLR R0 ;force success to cause the Citizenship test to return success to RSTS/E. At some point, the real problem (failure to pass citizenship diagnostics) does need to be corrected to find incompatibilities in the emulation, and to ultimately allow it to pass Digital hardware diagnostic tests. */ for (i=0; ivar->rbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); if (rstatus || wstatus) return xq_nxm_error(xq); /* invalid buffer? */ if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; } /* get status words */ rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (rstatus) return xq_nxm_error(xq); /* get host memory address */ address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; /* decode buffer length - two's complement (in words) */ w_length = ~xq->var->rbdl_buf[3] + 1; b_length = w_length * 2; if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; /* make sure entire packet fits in buffer */ assert(b_length >= sizeof(xq_bootrom)/2); /* send data to host */ wstatus = Map_WriteB(address, sizeof(xq_bootrom)/2, bootrom); if (wstatus) return xq_nxm_error(xq); /* update read status words */ xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ xq->var->rbdl_buf[5] = 0; /* update read status words*/ wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (wstatus) return xq_nxm_error(xq); /* set to next bdl (implicit chain) */ xq->var->rbdl_ba += 12; /* --------------------------- bootrom part 2 -----------------------------*/ /* get receive bdl from memory */ xq->var->rbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); if (rstatus || wstatus) return xq_nxm_error(xq); /* invalid buffer? */ if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; } /* get status words */ rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (rstatus) return xq_nxm_error(xq); /* get host memory address */ address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; /* decode buffer length - two's complement (in words) */ w_length = ~xq->var->rbdl_buf[3] + 1; b_length = w_length * 2; if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; /* make sure entire packet fits in buffer */ assert(b_length >= sizeof(xq_bootrom)/2); /* send data to host */ wstatus = Map_WriteB(address, sizeof(xq_bootrom)/2, &bootrom[2048]); if (wstatus) return xq_nxm_error(xq); /* update read status words */ xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ xq->var->rbdl_buf[5] = 0; /* update read status words*/ wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (wstatus) return xq_nxm_error(xq); /* set to next bdl (implicit chain) */ xq->var->rbdl_ba += 12; /* --------------------------- bootrom part 3 -----------------------------*/ switch (xq->var->type) { case XQ_T_DEQNA: /* get receive bdl from memory */ xq->var->rbdl_buf[0] = 0xFFFF; wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); if (rstatus || wstatus) return xq_nxm_error(xq); /* invalid buffer? */ if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { xq_csr_set_clr(xq, XQ_CSR_RL, 0); return SCPE_OK; } /* get status words */ rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (rstatus) return xq_nxm_error(xq); /* update read status words */ xq->var->rbdl_buf[4] = XQ_DSC_V; /* valid */ xq->var->rbdl_buf[5] = 0; /* update read status words*/ wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); if (wstatus) return xq_nxm_error(xq); /* set to next bdl (implicit chain) */ xq->var->rbdl_ba += 12; break; } /* switch */ /* --------------------------- Done, finish up -----------------------------*/ /* mark transmission complete */ xq_csr_set_clr(xq, XQ_CSR_RI, 0); /* reset sanity timer */ xq_reset_santmr(xq); return SCPE_OK; } #endif /* ifdef VM_PDP11 */ t_stat xq_wr_csr(CTLR* xq, int32 data) { uint16 set_bits = data & XQ_CSR_RW; /* set RW set bits */ uint16 clr_bits = ((data ^ XQ_CSR_RW) & XQ_CSR_RW) /* clear RW cleared bits */ | (data & XQ_CSR_W1) /* write 1 to clear bits */ | ((data & XQ_CSR_XI) ? XQ_CSR_NI : 0); /* clearing XI clears NI */ sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X)\n", data); /* reset controller when SR transitions to cleared */ if (xq->var->csr & XQ_CSR_SR & ~data) { xq_sw_reset(xq); return SCPE_OK; } #if 0 /* controller should ALWAYS have an active timer if enabled (for HW sanity) */ /* start/stop receive timer when RE transitions */ if ((xq->var->csr ^ data) & XQ_CSR_RE) { if (data & XQ_CSR_RE) sim_activate(&xq->unit[0], clock_cosched (tmxr_poll)); else sim_cancel(&xq->unit[0]); } #endif /* update CSR bits */ xq_csr_set_clr (xq, set_bits, clr_bits); #ifdef VM_PDP11 /* request boot/diagnostic rom? [PDP-11 only] */ if ((xq->var->csr & XQ_CSR_BP) == XQ_CSR_BP) /* all bits must be on */ xq_process_bootrom(xq); #endif return SCPE_OK; } t_stat xq_wr(int32 data, int32 PA, int32 access) { t_stat status; CTLR* xq = xq_pa2ctlr(PA); int index = (PA >> 1) & 07; /* word index */ sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, xq_xmit_regnames[index], access); switch (index) { case 0: /* these should not be written */ case 1: break; case 2: /* receive bdl low bits */ xq->var->rbdl[0] = data; break; case 3: /* receive bdl high bits */ xq->var->rbdl[1] = data; status = xq_dispatch_rbdl(xq); /* start receive operation */ break; case 4: /* transmit bdl low bits */ xq->var->xbdl[0] = data; break; case 5: /* transmit bdl high bits */ xq->var->xbdl[1] = data; status = xq_dispatch_xbdl(xq); /* start transmit operation */ break; case 6: /* vector address register */ status = xq_wr_var(xq, data); break; case 7: /* control and status register */ status = xq_wr_csr(xq, data); break; } return SCPE_OK; } /* reset device */ t_stat xq_reset(DEVICE* dptr) { t_stat status; CTLR* xq = xq_dev2ctlr(dptr); const uint16 set_bits = XQ_CSR_RL | XQ_CSR_XL; sim_debug(DBG_TRC, xq->dev, "xq_reset()\n"); /* calculate MAC checksum */ xq_make_checksum(xq); /* init vector address register */ switch (xq->var->type) { case XQ_T_DEQNA: xq->var->var = 0; break; case XQ_T_DELQA: xq->var->var = XQ_VEC_MS | XQ_VEC_OS; break; } xq->dib->vec = 0; /* init control status register */ xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); /* clear interrupts unconditionally */ xq_clrint(xq); /* init read queue (first time only) */ status = ethq_init(&xq->var->ReadQ, XQ_QUE_MAX); if (status != SCPE_OK) return status; /* clear read queue */ ethq_clear(&xq->var->ReadQ); /* reset ethernet interface */ if (xq->var->etherface) { status = eth_filter (xq->var->etherface, 1, &xq->var->mac, 0, 0); xq_csr_set_clr(xq, XQ_CSR_OK, 0); /* start service timer */ sim_activate_abs(&xq->unit[0], tmxr_poll); } /* set hardware sanity controls */ if (xq->var->sanity.enabled) { xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; xq->var->sanity.max = XQ_HW_SANITY_SECS * xq->var->poll; } return SCPE_OK; } void xq_reset_santmr(CTLR* xq) { sim_debug(DBG_TRC, xq->dev, "xq_reset_santmr()\n"); if (xq->var->sanity.enabled) { sim_debug(DBG_SAN, xq->dev, "SANITY TIMER RESETTING, qsecs: %d\n", xq->var->sanity.quarter_secs); /* reset sanity countdown timer to max count */ xq->var->sanity.timer = xq->var->sanity.max; } } t_stat xq_boot_host(CTLR* xq) { sim_debug(DBG_TRC, xq->dev, "xq_boot_host()\n"); /* The manual says the hardware should force the Qbus BDCOK low for 3.6 microseconds, which will cause the host to reboot. Since the SIMH Qbus emulator does not have this functionality, we call a special STOP_ code, and let the CPU stop dispatch routine decide what the appropriate cpu-specific behavior should be. */ return STOP_SANITY; } t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) { static uint16 receipt = 0; ETH_PACK system_id; uint8* const msg = &system_id.msg[0]; t_stat status; sim_debug(DBG_TRC, xq->dev, "xq_system_id()\n"); if (xq->var->type != XQ_T_DELQA) /* DELQA-only function */ return SCPE_NOFNC; memset (&system_id, 0, sizeof(system_id)); memcpy (&msg[0], dest, sizeof(ETH_MAC)); memcpy (&msg[6], xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); msg[12] = 0x60; /* type */ msg[13] = 0x02; /* type */ msg[14] = 0x1C; /* character count */ msg[15] = 0x00; /* character count */ msg[16] = 0x07; /* code */ msg[17] = 0x00; /* zero pad */ if (receipt_id) { msg[18] = receipt_id & 0xFF; /* receipt number */ msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */ } else { msg[18] = receipt & 0xFF; /* receipt number */ msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */ } /* MOP VERSION */ msg[20] = 0x01; /* type */ msg[21] = 0x00; /* type */ msg[22] = 0x03; /* length */ msg[23] = 0x03; /* version */ msg[24] = 0x01; /* eco */ msg[25] = 0x00; /* user eco */ /* FUNCTION */ msg[26] = 0x02; /* type */ msg[27] = 0x00; /* type */ msg[28] = 0x02; /* length */ msg[29] = 0x00; /* value 1 ??? */ msg[30] = 0x00; /* value 2 */ /* HARDWARE ADDRESS */ msg[31] = 0x07; /* type */ msg[32] = 0x00; /* type */ msg[33] = 0x06; /* length */ memcpy (&msg[34], xq->var->mac, sizeof(ETH_MAC)); /* ROM address */ /* DEVICE TYPE */ msg[40] = 37; /* type */ msg[41] = 0x00; /* type */ msg[42] = 0x01; /* length */ msg[43] = 0x11; /* value (0x11=DELQA) */ /* write system id */ system_id.len = 60; status = eth_write(xq->var->etherface, &system_id, NULL); return status; } /* ** service routine - used for ethernet reading loop */ t_stat xq_svc(UNIT* uptr) { CTLR* xq = xq_unit2ctlr(uptr); /* if the receiver is enabled */ if (xq->var->csr & XQ_CSR_RE) { t_stat status; int queue_size; /* First pump any queued packets into the system */ if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) status = xq_process_rbdl(xq); /* Now read and queue packets that have arrived */ /* This is repeated as long as they are available and we have room */ do { queue_size = xq->var->ReadQ.count; /* read a packet from the ethernet - processing is via the callback */ status = eth_read (xq->var->etherface, &xq->var->read_buffer, xq->var->rcallback); } while (queue_size != xq->var->ReadQ.count); /* Now pump any still queued packets into the system */ if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) status = xq_process_rbdl(xq); } /* has sanity timer expired? if so, reboot */ if (xq->var->sanity.enabled) if (--xq->var->sanity.timer <= 0) xq_boot_host(xq); /* has system id timer expired? if so, do system id */ if (--xq->var->idtmr <= 0) { const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; xq_system_id(xq, mop_multicast, 0); /* reset system ID counter for next event */ xq->var->idtmr = XQ_SYSTEM_ID_SECS * xq->var->poll; } /* resubmit service timer */ sim_activate(&xq->unit[0], tmxr_poll); return SCPE_OK; } /* attach device: */ t_stat xq_attach(UNIT* uptr, char* cptr) { t_stat status; char* tptr; CTLR* xq = xq_unit2ctlr(uptr); char buffer[80]; /* buffer for runtime input */ sim_debug(DBG_TRC, xq->dev, "xq_attach(cptr=%s)\n", cptr); /* runtime selection of ethernet port? */ if (*cptr == '?') { /* I/O style derived from main() */ memset (buffer, 0, sizeof(buffer)); /* clear read buffer */ eth_show (stdout, uptr, 0, NULL); /* show ETH devices */ printf ("Select device (ethX or )? "); /* prompt for device */ cptr = read_line (buffer, sizeof(buffer), stdin); /* read command line */ if (cptr == NULL) return SCPE_ARG; /* ignore EOF */ if (*cptr == 0) return SCPE_ARG; /* ignore blank */ } /* resume attaching */ tptr = (char *) malloc(strlen(cptr) + 1); if (tptr == NULL) return SCPE_MEM; strcpy(tptr, cptr); xq->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV)); if (!xq->var->etherface) return SCPE_MEM; status = eth_open(xq->var->etherface, cptr, xq->dev, DBG_ETH); if (status != SCPE_OK) { free(tptr); free(xq->var->etherface); xq->var->etherface = 0; return status; } uptr->filename = tptr; uptr->flags |= UNIT_ATT; /* turn on transceiver power indicator */ xq_csr_set_clr(xq, XQ_CSR_OK, 0); /* reset the device with the new attach info */ xq_reset(xq->dev); return SCPE_OK; } /* detach device: */ t_stat xq_detach(UNIT* uptr) { CTLR* xq = xq_unit2ctlr(uptr); sim_debug(DBG_TRC, xq->dev, "xq_detach()\n"); if (uptr->flags & UNIT_ATT) { eth_close (xq->var->etherface); free(xq->var->etherface); xq->var->etherface = 0; free(uptr->filename); uptr->filename = NULL; uptr->flags &= ~UNIT_ATT; /* cancel service timer */ sim_cancel(&xq->unit[0]); } /* turn off transceiver power indicator */ xq_csr_set_clr(xq, 0, XQ_CSR_OK); return SCPE_OK; } void xq_setint(CTLR* xq) { xq->var->irq = 1; SET_INT(XQ); return; } void xq_clrint(CTLR* xq) { int i; xq->var->irq = 0; /* set controller irq off */ /* clear master interrupt? */ for (i=0; iirq) { /* if any irqs enabled */ SET_INT(XQ); /* set master interrupt on */ return; } CLR_INT(XQ); /* clear master interrupt */ return; } int32 xq_int (void) { int i; for (i=0; ivar->irq) { /* if interrupt pending */ xq_clrint(xq); /* clear interrupt */ return xq->dib->vec; /* return vector */ } } return 0; /* no interrupt request active */ } void xq_csr_set_clr (CTLR* xq, uint16 set_bits, uint16 clear_bits) { uint16 saved_csr = xq->var->csr; /* set the bits in the csr */ xq->var->csr = (xq->var->csr | set_bits) & ~clear_bits; sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, saved_csr, xq->var->csr, 1); /* check and correct the state of controller interrupt */ /* if IE is transitioning, process it */ if ((saved_csr ^ xq->var->csr) & XQ_CSR_IE) { /* if IE transitioning low and interrupt set, clear interrupt */ if ((clear_bits & XQ_CSR_IE) && xq->var->irq) xq_clrint(xq); /* if IE transitioning high, and XI or RI is high, set interrupt if interrupt is off */ if ((set_bits & XQ_CSR_IE) && (xq->var->csr & XQ_CSR_XIRI) && !xq->var->irq) xq_setint(xq); } else { /* IE is not transitioning */ /* if interrupts are enabled */ if (xq->var->csr & XQ_CSR_IE) { /* if XI or RI transitioning high and interrupt off, set interrupt */ if (((saved_csr ^ xq->var->csr) & (set_bits & XQ_CSR_XIRI)) && !xq->var->irq) { xq_setint(xq); } else { /* if XI or RI transitioning low, and both XI and RI are now low, clear interrupt if interrupt is on */ if (((saved_csr ^ xq->var->csr) & (clear_bits & XQ_CSR_XIRI)) && !(xq->var->csr & XQ_CSR_XIRI) && xq->var->irq) xq_clrint(xq); } } /* IE enabled */ } /* IE transitioning */ } /*============================================================================== / debugging routines /=============================================================================*/ void xq_debug_setup(CTLR* xq) { int i; char buffer[20]; if (xq->var->write_buffer.msg[0]) printf ("%s: setup> MOP info present!\n", xq->dev->name); for (i = 0; i < XQ_FILTER_MAX; i++) { eth_mac_fmt(&xq->var->setup.macs[i], buffer); printf ("%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); } if (xq->var->write_buffer.len > 128) { char buffer[20] = {0}; uint16 len = xq->var->write_buffer.len; if (len & XQ_SETUP_MC) strcat(buffer, "MC "); if (len & XQ_SETUP_PM) strcat(buffer, "PM "); if (len & XQ_SETUP_LD) strcat(buffer, "LD "); if (len & XQ_SETUP_ST) strcat(buffer, "ST "); printf ("%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", xq->dev->name, len, len, (len & XQ_SETUP_LD) >> 2, (len & XQ_SETUP_ST) >> 4, buffer); } } simh-3.8.1/PDP11/pdp11_ke.c0000644000175000017500000003417211012430576013204 0ustar vlmvlm/* pdp11_ke.c: PDP-11/20 extended arithmetic element Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ke_ACTION OF CONTRke_ACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This code draws on prior work by Tim Shoppa and Brad Parker. My thanks for to them for letting me use their work. EAE PDP-11/20 extended arithmetic element */ #include "pdp11_defs.h" #define GET_SIGN_L(v) (((v) >> 31) & 1) #define GET_SIGN_W(v) (((v) >> 15) & 1) #define GET_SIGN_B(v) (((v) >> 7) & 1) /* KE11A I/O address offsets 0177300 - 0177316 */ #define KE_DIV 000 /* divide */ #define KE_AC 002 /* accumulator */ #define KE_MQ 004 /* MQ */ #define KE_MUL 006 /* multiply */ #define KE_SC 010 /* step counter */ #define KE_NOR 012 /* normalize */ #define KE_LSH 014 /* logical shift */ #define KE_ASH 016 /* arithmetic shift */ /* Status register */ #define KE_SR_C 0001 /* carry */ #define KE_SR_SXT 0002 /* AC<15:0> = MQ<15> */ #define KE_SR_Z 0004 /* AC = MQ = 0 */ #define KE_SR_MQZ 0010 /* MQ = 0 */ #define KE_SR_ACZ 0020 /* AC = 0 */ #define KE_SR_ACM1 0040 /* AC = 177777 */ #define KE_SR_N 0100 /* last op negative */ #define KE_SR_NXV 0200 /* last op ovf XOR N */ #define KE_SR_DYN (KE_SR_SXT|KE_SR_Z|KE_SR_MQZ|KE_SR_ACZ|KE_SR_ACM1) /* Visible state */ uint32 ke_AC = 0; uint32 ke_MQ = 0; uint32 ke_SC = 0; uint32 ke_SR = 0; DEVICE ke_dev; t_stat ke_rd (int32 *data, int32 PA, int32 access); t_stat ke_wr (int32 data, int32 PA, int32 access); t_stat ke_reset (DEVICE *dptr); uint32 ke_set_SR (void); DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 }; UNIT ke_unit = { UDATA (NULL, UNIT_DISABLE, 0) }; REG ke_reg[] = { { ORDATA (AC, ke_AC, 16) }, { ORDATA (MQ, ke_MQ, 16) }, { ORDATA (SC, ke_SC, 6) }, { ORDATA (SR, ke_SR, 8) }, { NULL } }; MTAB ke_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { 0 } }; DEVICE ke_dev = { "KE", &ke_unit, ke_reg, ke_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ke_reset, NULL, NULL, NULL, &ke_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS }; /* KE read - reads are always 16b, to even addresses */ t_stat ke_rd (int32 *data, int32 PA, int32 access) { switch (PA & 016) { /* decode PA<3:1> */ case KE_AC: /* AC */ *data = ke_AC; break; case KE_MQ: /* MQ */ *data = ke_MQ; break; case KE_NOR: /* norm (SC) */ *data = ke_SC; break; case KE_SC: /* SR/SC */ *data = (ke_set_SR () << 8) | ke_SC; break; default: *data = 0; break; } return SCPE_OK; } /* KE write - writes trigger actual arithmetic */ t_stat ke_wr (int32 data, int32 PA, int32 access) { int32 quo, t32, sout, sign; uint32 absd, absr; switch (PA & 017) { /* decode PA<3:0> */ case KE_DIV: /* divide */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ data |= 0177400; /* sext data to 16b */ ke_SR = 0; /* N = V = C = 0 */ t32 = (ke_AC << 16) | ke_MQ; /* 32b divd */ if (GET_SIGN_W (ke_AC)) /* sext (divd) */ t32 = t32 | ~017777777777; if (GET_SIGN_W (data)) /* sext (divr) */ data = data | ~077777; absd = abs (t32); absr = abs (data); if ((absd >> 16) >= absr) { /* divide fails? */ /* Based on the documentation, here's what has happened: SC = 16. SR = (AC<15> == data<15>) AC'MQ = (AC'MQ << 1) | SR AC = SR? AC - data: AC + data SR = (AC<15> == data<15>) SC = SC - 1 stop */ sign = GET_SIGN_W (ke_AC ^ data) ^ 1; /* 1 if signs match */ ke_AC = (ke_AC << 1) | (ke_MQ >> 15); ke_AC = (sign? ke_AC - data: ke_AC + data) & DMASK; ke_MQ = ((ke_MQ << 1) | sign) & DMASK; if (GET_SIGN_W (ke_AC ^ data) == 0) /* 0 if signs match */ ke_SR |= KE_SR_C; ke_SC = 15; /* SC clocked once */ ke_SR |= KE_SR_NXV; /* set overflow */ } else { ke_SC = 0; quo = t32 / data; ke_MQ = quo & DMASK; /* MQ has quo */ ke_AC = (t32 % data) & DMASK; /* AC has rem */ if ((quo > 32767) || (quo < -32768)) /* quo overflow? */ ke_SR |= KE_SR_NXV; /* set overflow */ } if (GET_SIGN_W (ke_MQ)) /* result negative? */ ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ break; case KE_AC: /* AC */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ data |= 0177400; /* sext data to 16b */ ke_AC = data; break; case KE_AC + 1: /* AC odd byte */ ke_AC = (ke_AC & 0377) | (data << 8); break; case KE_MQ: /* MQ */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ data |= 0177400; /* sext data to 16b */ ke_MQ = data; if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */ ke_AC = 0177777; else ke_AC = 0; break; case KE_MQ + 1: /* MQ odd byte */ ke_MQ = (ke_MQ & 0377) | (data << 8); if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */ ke_AC = 0177777; else ke_AC = 0; break; case KE_MUL: /* multiply */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ data |= 0177400; /* sext data to 16b */ ke_SC = 0; if (GET_SIGN_W (data)) /* sext operands */ data |= ~077777; t32 = ke_MQ; if (GET_SIGN_W (t32)) t32 |= ~077777; t32 = t32 * data; ke_AC = (t32 >> 16) & DMASK; ke_MQ = t32 & DMASK; if (GET_SIGN_W (ke_AC)) /* result negative? */ ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */ else ke_SR = 0; /* N = 0, V = C = 0 */ break; case KE_SC: /* SC */ if (access == WRITEB) /* ignore byte writes */ return SCPE_OK; ke_SR = (data >> 8) & (KE_SR_NXV|KE_SR_N|KE_SR_C); ke_SC = data & 077; break; case KE_NOR: /* normalize */ for (ke_SC = 0; ke_SC < 31; ke_SC++) { /* max 31 shifts */ if (((ke_AC == 0140000) && (ke_MQ == 0)) || /* special case? */ (GET_SIGN_W (ke_AC ^ (ke_AC << 1)))) /* AC<15> != AC<14>? */ break; ke_AC = ((ke_AC << 1) | (ke_MQ >> 15)) & DMASK; ke_MQ = (ke_MQ << 1) & DMASK; } if (GET_SIGN_W (ke_AC)) /* result negative? */ ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */ else ke_SR = 0; /* N = 0, V = C = 0 */ break; case KE_LSH: /* logical shift */ ke_SC = 0; ke_SR = 0; /* N = V = C = 0 */ data = data & 077; /* 6b shift count */ if (data != 0) { t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */ if (sign = GET_SIGN_W (ke_AC)) /* sext operand */ t32 = t32 | ~017777777777; if (data < 32) { /* [1,31] - left */ sout = (t32 >> (32 - data)) | (-sign << data); t32 = ((uint32) t32) << data; /* do shift (zext) */ if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */ ke_SR |= KE_SR_NXV; /* no, V = 1 */ if (sout & 1) /* last bit lost = 1? */ ke_SR |= KE_SR_C; /* yes, C = 1 */ } else { /* [32,63] = -32,-1 */ if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */ ke_SR |= KE_SR_C; /* yes, C = 1*/ t32 = (data != 32)? ((uint32) t32) >> (64 - data): 0; } ke_AC = (t32 >> 16) & DMASK; ke_MQ = t32 & DMASK; } if (GET_SIGN_W (ke_AC)) /* result negative? */ ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ break; /* EAE ASH differs from EIS ASH and cannot use the same overflow test */ case KE_ASH: /* arithmetic shift */ ke_SC = 0; ke_SR = 0; /* N = V = C = 0 */ data = data & 077; /* 6b shift count */ if (data != 0) { t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */ if (sign = GET_SIGN_W (ke_AC)) /* sext operand */ t32 = t32 | ~017777777777; if (data < 32) { /* [1,31] - left */ sout = (t32 >> (31 - data)) | (-sign << data); t32 = (t32 & 020000000000) | ((t32 << data) & 017777777777); if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */ ke_SR |= KE_SR_NXV; /* no, V = 1 */ if (sout & 1) /* last bit lost = 1? */ ke_SR |= KE_SR_C; /* yes, C = 1 */ } else { /* [32,63] = -32,-1 */ if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */ ke_SR |= KE_SR_C; /* yes, C = 1 */ t32 = (data != 32)? /* special case 32 */ (((uint32) t32) >> (64 - data)) | (-sign << (data - 32)): -sign; } ke_AC = (t32 >> 16) & DMASK; ke_MQ = t32 & DMASK; } if (GET_SIGN_W (ke_AC)) /* result negative? */ ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ break; default: /* all others ignored */ return SCPE_OK; } /* end switch PA */ ke_set_SR (); return SCPE_OK; } /* Update status register based on current AC, MQ */ uint32 ke_set_SR (void) { ke_SR &= ~KE_SR_DYN; /* clr dynamic bits */ if (ke_MQ == 0) /* MQ == 0? */ ke_SR |= KE_SR_MQZ; if (ke_AC == 0) { /* AC == 0? */ ke_SR |= KE_SR_ACZ; if (GET_SIGN_W (ke_MQ) == 0) /* MQ positive? */ ke_SR |= KE_SR_SXT; if (ke_MQ == 0) /* MQ zero? */ ke_SR |= KE_SR_Z; } if (ke_AC == 0177777) { /* AC == 177777? */ ke_SR |= KE_SR_ACM1; if (GET_SIGN_W (ke_MQ) == 1) /* MQ negative? */ ke_SR |= KE_SR_SXT; } return ke_SR; } /* Reset routine */ t_stat ke_reset (DEVICE *dptr) { ke_SR = 0; ke_SC = 0; ke_AC = 0; ke_MQ = 0; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_xu.h0000644000175000017500000004555111111666660013256 0ustar vlmvlm/* pdp11_xu.h: DEUNA/DELUA ethernet controller information ------------------------------------------------------------------------------ Copyright (c) 2003-2008, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ Modification history: 08-Dec-05 DTH Added load_server, increased UDBSIZE for system ID parameters 07-Jul-05 RMS Removed extraneous externs 05-Jan-04 DTH Added network statistics 31-Dec-03 DTH Added reserved states 28-Dec-03 DTH Corrected MODE bitmasks 23-Dec-03 DTH Corrected TXR and RXR bitmasks 03-Dec-03 DTH Refitted to SIMH v3.0 platform 05-May-03 DTH Started XU simulation ------------------------------------------------------------------------------ */ #ifndef _PDP11_XU_H #define _PDP11_XU_H #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" #define XU_RDX 8 #define XU_WID 16 extern int32 int_req; #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define XU_RDX 8 #define XU_WID 16 extern int32 int_req[IPL_HLVL]; #else /* PDP-11 version */ #include "pdp11_defs.h" #define XU_RDX 8 #define XU_WID 16 extern int32 int_req[IPL_HLVL]; #endif /* VM_PDP10 */ #include "sim_ether.h" #define XU_QUE_MAX 500 /* message queue array */ #define XU_FILTER_MAX 11 /* mac + 10 multicast addrs */ #define XU_SERVICE_INTERVAL 100 /* times per second */ #define XU_ID_TIMER_VAL 540 /* 9 min * 60 sec */ #define UDBSIZE 200 /* max size of UDB (in words) */ enum xu_type {XU_T_DEUNA, XU_T_DELUA}; struct xu_setup { int promiscuous; /* promiscuous mode enabled */ int multicast; /* enable all multicast addresses */ int mac_count; /* number of multicast mac addresses */ ETH_MAC macs[XU_FILTER_MAX]; /* MAC addresses to respond to */ }; /* Network Statistics - some of these will always be zero in the simulated environment, since there is no ability for the sim_ether network driver to see things like incoming runts, collision tests, babbling, etc. */ struct xu_stats { uint16 secs; /* seconds since last clear */ uint32 frecv; /* frames received */ uint32 mfrecv; /* multicast frames received */ uint16 rxerf; /* receive error flags */ uint32 frecve; /* frames received with errors */ uint32 rbytes; /* data bytes received */ uint32 mrbytes; /* multicast data bytes received */ uint16 rlossi; /* received frames lost - internal err */ uint16 rlossl; /* received frames lost - local buffers */ uint32 ftrans; /* frames transmitted */ uint32 mftrans; /* multicast frames transmitted */ uint32 ftrans3; /* frames transmitted with 3+ tries */ uint32 ftrans2; /* frames transmitted - two tries */ uint32 ftransd; /* frames transmitted - deferred */ uint32 tbytes; /* data bytes transmitted */ uint32 mtbytes; /* multicast data bytes transmitted */ uint16 txerf; /* transmit error flags summary */ uint16 ftransa; /* transmit frames aborted */ uint16 txccf; /* transmit collision test failure */ uint16 porterr; /* port driver errors */ uint16 bablcnt; /* babble counter */ }; struct xu_device { /*+ initialized values - DO NOT MOVE */ ETH_PCALLBACK rcallback; /* read callback routine */ ETH_PCALLBACK wcallback; /* write callback routine */ ETH_MAC mac; /* MAC address */ enum xu_type type; /* controller type */ /*- initialized values - DO NOT MOVE */ /* I/O register storage */ uint32 irq; /* interrupt request flag */ /* buffers, etc. */ ETH_DEV* etherface; ETH_PACK read_buffer; ETH_PACK write_buffer; ETH_QUE ReadQ; ETH_MAC load_server; /* load server address */ int idtmr; /* countdown for ID Timer */ int sectmr; /* countup for one second timer */ struct xu_setup setup; struct xu_stats stats; /* reportable network statistics */ /* copied from dec_deuna.h */ uint16 pcsr0; /* primary DEUNA registers */ uint16 pcsr1; uint16 pcsr2; uint16 pcsr3; uint32 mode; /* mode register */ uint32 pcbb; /* port command block base */ uint32 stat; /* extended port status */ uint32 tdrb; /* transmit desc ring base */ uint32 telen; /* transmit desc ring entry len */ uint32 trlen; /* transmit desc ring length */ uint32 txnext; /* transmit buffer pointer */ uint32 rdrb; /* receive desc ring base */ uint32 relen; /* receive desc ring entry len */ uint32 rrlen; /* receive desc ring length */ uint32 rxnext; /* receive buffer pointer */ uint16 pcb[4]; /* copy of Port Command Block */ uint16 udb[UDBSIZE]; /* copy of Unibus Data Block */ uint16 rxhdr[4]; /* content of RX ring entry, during wait */ uint16 txhdr[4]; /* content of TX ring entry, during xmit */ }; struct xu_controller { DEVICE* dev; /* device block */ UNIT* unit; /* unit block */ DIB* dib; /* device interface block */ struct xu_device* var; /* controller-specific variables */ }; typedef struct xu_controller CTLR; /* PCSR0 register definitions */ #define PCSR0_SERI 0100000 /* <15> Status Error Intr */ #define PCSR0_PCEI 0040000 /* <14> Port Command Error Intr */ #define PCSR0_RXI 0020000 /* <13> Receive Interrupt */ #define PCSR0_TXI 0010000 /* <12> Transmit Interrupt */ #define PCSR0_DNI 0004000 /* <11> Done Interrupt */ #define PCSR0_RCBI 0002000 /* <10> Recv Buffer Unavail Intr */ #define PCSR0_USCI 0000400 /* <08> Unsolicited State Chg Inter */ #define PCSR0_INTR 0000200 /* <07> Interrupt Summary */ #define PCSR0_INTE 0000100 /* <06> Interrupt Enable */ #define PCSR0_RSET 0000040 /* <05> Reset */ #define PCSR0_PCMD 0000017 /* <03:00> Port Command field */ /* PCSR0 Port Commands */ #define CMD_NOOP 000 /* No-op */ #define CMD_GETPCBB 001 /* Get PCB base */ #define CMD_GETCMD 002 /* Get Command */ #define CMD_SELFTEST 003 /* Self-test init */ #define CMD_START 004 /* Start xmit/recv */ #define CMD_BOOT 005 /* Boot */ #define CMD_RSV06 006 /* Reserved */ #define CMD_RSV07 007 /* Reserved */ #define CMD_PDMD 010 /* Polling Demand */ #define CMD_RSV11 011 /* Reserved */ #define CMD_RSV12 012 /* Reserved */ #define CMD_RSV13 013 /* Reserved */ #define CMD_RSV14 014 /* Reserved */ #define CMD_RSV15 015 /* Reserved */ #define CMD_HALT 016 /* Halt */ #define CMD_STOP 017 /* Stop */ /* PCSR1 register definitions */ #define PCSR1_XPWR 0100000 /* <15> Tranceiver power failure */ #define PCSR1_ICAB 0040000 /* <14> Port/Link cable failure */ #define PCSR1_ECOD 0037400 /* <13:08> Self-test error code */ #define PCSR1_PCTO 0000200 /* <07> Port Command Timeout */ #define PCSR1_TYPE 0000160 /* <06:04> Interface type */ #define PCSR1_STATE 0000017 /* <03:00> State: */ /* PCSR1 Types */ #define TYPE_DEUNA (0 << 4) /* Controller is a DEUNA */ #define TYPE_DELUA (1 << 4) /* Controller is a DELUA */ /* PCSR1 States */ #define STATE_RESET 000 /* Reset */ #define STATE_PLOAD 001 /* Primary Load */ #define STATE_READY 002 /* Ready */ #define STATE_RUNNING 003 /* Running */ #define STATE_UHALT 005 /* UNIBUS Halted */ #define STATE_NHALT 006 /* NI Halted */ #define STATE_NUHALT 007 /* NI and UNIBUS Halted */ #define STATE_HALT 010 /* Halted */ #define STATE_SLOAD 017 /* Secondary Load */ /* Status register definitions */ #define STAT_ERRS 0100000 /* <15> error summary */ #define STAT_MERR 0040000 /* <14> multiple errors */ #define STAT_BABL 0020000 /* <13> Transmitter on too long [DELUA only] */ #define STAT_CERR 0010000 /* <12> collision test error */ #define STAT_TMOT 0004000 /* <11> UNIBUS timeout */ #define STAT_RRNG 0001000 /* <09> receive ring error */ #define STAT_TRNG 0000400 /* <08> transmit ring error */ #define STAT_PTCH 0000200 /* <07> ROM patch */ #define STAT_RRAM 0000100 /* <06> running from RAM */ #define STAT_RREV 0000077 /* <05:00> ROM version */ /* Mode definitions */ #define MODE_PROM 0100000 /* <15> Promiscuous Mode */ #define MODE_ENAL 0040000 /* <14> Enable All Multicasts */ #define MODE_DRDC 0020000 /* <13> Disable Data Chaining */ #define MODE_TPAD 0010000 /* <12> Transmit Msg Pad Enable */ #define MODE_ECT 0004000 /* <11> Enable Collision Test */ #define MODE_DMNT 0001000 /* <09> Disable Maint Message */ #define MODE_INTL 0000200 /* <07> Internal Loopback [DELUA only] */ #define MODE_DTCR 0000010 /* <03> Disable Transmit CRC */ #define MODE_LOOP 0000004 /* <02> Internal Loopback Mode */ #define MODE_HDPX 0000001 /* <00> Half-Duplex Mode */ /* Function Code definitions */ #define FC_NOOP 0000000 /* no-op */ #define FC_LSM 0000001 /* Load and Start Microaddress */ #define FC_RDPA 0000002 /* Read Default Physical Address */ #define FC_RPA 0000004 /* Read Physical Address */ #define FC_WPA 0000005 /* Write Physical Address */ #define FC_RMAL 0000006 /* Read Multicast Address List */ #define FC_WMAL 0000007 /* Write Multicast Address List */ #define FC_RRF 0000010 /* Read Ring Format */ #define FC_WRF 0000011 /* Write Ring Format */ #define FC_RDCTR 0000012 /* Read Counters */ #define FC_RDCLCTR 0000013 /* Read and Clear Counters */ #define FC_RMODE 0000014 /* Read Mode */ #define FC_WMODE 0000015 /* Write Mode */ #define FC_RSTAT 0000016 /* Read Status */ #define FC_RCSTAT 0000017 /* Read and Clear Status */ #define FC_DIM 0000020 /* Dump Internal Memory */ #define FC_LIM 0000021 /* Load Internal Memory */ #define FC_RSID 0000022 /* Read System ID parameters */ #define FC_WSID 0000023 /* Write System ID parameters */ #define FC_RLSA 0000024 /* Read Load Server Address */ #define FC_WLSA 0000025 /* Write Load Server Address */ /* Transmitter Ring definitions */ #define TXR_OWN 0100000 /* <15> we own it (1) */ #define TXR_ERRS 0040000 /* <14> error summary */ #define TXR_MTCH 0020000 /* <13> Station Match */ #define TXR_MORE 0010000 /* <12> Mult Retries Needed */ #define TXR_ONE 0004000 /* <11> One Collision */ #define TXR_DEF 0002000 /* <10> Deferred */ #define TXR_STF 0001000 /* <09> Start Of Frame */ #define TXR_ENF 0000400 /* <08> End Of Frame */ #define TXR_BUFL 0100000 /* <15> Buffer Length Error */ #define TXR_UBTO 0040000 /* <14> UNIBUS TimeOut */ #define TXR_UFLO 0020000 /* <13> Underflow Error */ #define TXR_LCOL 0010000 /* <12> Late Collision */ #define TXR_LCAR 0004000 /* <11> Lost Carrier */ #define TXR_RTRY 0002000 /* <10> Retry Failure (16x) */ #define TXR_TDR 0001777 /* <9:0> TDR value if RTRY=1 */ /* Receiver Ring definitions */ #define RXR_OWN 0100000 /* <15> we own it (1) */ #define RXR_ERRS 0040000 /* <14> Error Summary */ #define RXR_FRAM 0020000 /* <13> Frame Error */ #define RXR_OFLO 0010000 /* <12> Message Overflow */ #define RXR_CRC 0004000 /* <11> CRC Check Error */ #define RXR_STF 0001000 /* <09> Start Of Frame */ #define RXR_ENF 0000400 /* <08> End Of Frame */ #define RXR_BUFL 0100000 /* <15> Buffer Length error */ #define RXR_UBTO 0040000 /* <14> UNIBUS TimeOut */ #define RXR_NCHN 0020000 /* <13> No Data Chaining */ #define RXR_OVRN 0010000 /* <12> Overrun Error [DELUA only] */ #define RXR_MLEN 0007777 /* <11:0> Message Length */ /* debugging bitmaps */ #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ #define DBG_WRN 0x0004 /* display warnings */ #define DBG_PCK 0x0080 /* display packets */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XU_H */ simh-3.8.1/PDP11/pdp11_ry.c0000644000175000017500000007631711112125520013235 0ustar vlmvlm/* pdp11_ry.c: RX211/RXV21/RX02 floppy disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ry RX211/RXV21/RX02 floppy disk 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) 07-Jul-05 RMS Removed extraneous externs 18-Feb-05 RMS Fixed bug in boot code (reported by Graham Toal) 30-Sep-04 RMS Revised Unibus interface 21-Mar-04 RMS Added VAX support 29-Dec-03 RMS Added RXV21 support 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 03-Mar-03 RMS Fixed autosizing 12-Oct-02 RMS Added autoconfigure support An RX02 diskette consists of 77 tracks, each with 26 sectors of 256B. Tracks are numbered 0-76, sectors 1-26. */ #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" extern int32 int_req; #define DEV_DISI DEV_DIS #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" extern int32 int_req[IPL_HLVL]; #define DEV_DISI 0 #else /* PDP-11 version */ #include "pdp11_defs.h" extern int32 int_req[IPL_HLVL]; #define DEV_DISI DEV_DIS #endif #define RX_NUMTR 77 /* tracks/disk */ #define RX_M_TRACK 0377 #define RX_NUMSC 26 /* sectors/track */ #define RX_M_SECTOR 0177 #define RX_NUMBY 128 #define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) #define RY_NUMBY 256 /* bytes/sector */ #define RY_SIZE (RX_NUMTR * RX_NUMSC * RY_NUMBY) #define RX_NUMDR 2 /* drives/controller */ #define RX_M_NUMDR 01 #define UNIT_V_WLK (UNIT_V_UF) /* write locked */ #define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ #define UNIT_WLK (1u << UNIT_V_WLK) #define UNIT_DEN (1u << UNIT_V_DEN) #define UNIT_AUTO (1u << UNIT_V_AUTO) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ #define IDLE 0 /* idle state */ #define RWDS 1 /* rw, sect next */ #define RWDT 2 /* rw, track next */ #define RWXFR 3 /* rw, transfer */ #define FEWC 4 /* fill empty, wc next */ #define FEBA 5 /* fill empty, ba next */ #define FEXFR 6 /* fill empty, transfer */ #define SDCNF 7 /* set dens, conf next */ #define SDXFR 8 /* set dens, transfer */ #define ESBA 9 /* ext sta, ba next */ #define ESXFR 10 /* ext sta, transfer */ #define CMD_COMPLETE 11 /* set done next */ #define INIT_COMPLETE 12 /* init compl next */ #define RYCS_V_FUNC 1 /* function */ #define RYCS_M_FUNC 7 #define RYCS_FILL 0 /* fill buffer */ #define RYCS_EMPTY 1 /* empty buffer */ #define RYCS_WRITE 2 /* write sector */ #define RYCS_READ 3 /* read sector */ #define RYCS_SDEN 4 /* set density */ #define RYCS_RYES 5 /* read status */ #define RYCS_WRDEL 6 /* write del data */ #define RYCS_ESTAT 7 /* read ext status */ #define RYCS_V_DRV 4 /* drive select */ #define RYCS_V_DONE 5 /* done */ #define RYCS_V_IE 6 /* int enable */ #define RYCS_V_TR 7 /* xfer request */ #define RYCS_V_DEN 8 /* density select */ #define RYCS_V_RY 11 /* RX02 flag */ #define RYCS_V_UAE 12 /* addr ext */ #define RYCS_M_UAE 03 #define RYCS_V_INIT 14 /* init */ #define RYCS_V_ERR 15 /* error */ #define RYCS_FUNC (RYCS_M_FUNC << RYCS_V_FUNC) #define RYCS_DRV (1u << RYCS_V_DRV) #define RYCS_DONE (1u << RYCS_V_DONE) #define RYCS_IE (1u << RYCS_V_IE) #define RYCS_TR (1u << RYCS_V_TR) #define RYCS_DEN (1u << RYCS_V_DEN) #define RYCS_RY (1u << RYCS_V_RY) #define RYCS_UAE (RYCS_M_UAE << RYCS_V_UAE) #define RYCS_INIT (1u << RYCS_V_INIT) #define RYCS_ERR (1u << RYCS_V_ERR) #define RYCS_IMP (RYCS_ERR+RYCS_UAE+RYCS_DEN+RYCS_TR+RYCS_IE+\ RYCS_DONE+RYCS_DRV+RYCS_FUNC) #define RYCS_RW (RYCS_UAE+RYCS_DEN+RYCS_IE+RYCS_DRV+RYCS_FUNC) #define RYCS_GETFNC(x) (((x) >> RYCS_V_FUNC) & RYCS_M_FUNC) #define RYCS_GETUAE(x) (((x) >> RYCS_V_UAE) & RYCS_M_UAE) #define RYES_CRC 00001 /* CRC error NI */ #define RYES_ID 00004 /* init done */ #define RYES_ACLO 00010 /* ACLO NI */ #define RYES_DERR 00020 /* density err */ #define RYES_DDEN 00040 /* drive density */ #define RYES_DD 00100 /* deleted data */ #define RYES_DRDY 00200 /* drive ready */ #define RYES_USEL 00400 /* unit selected */ #define RYES_WCO 02000 /* wc overflow */ #define RYES_NXM 04000 /* nxm */ #define RYES_ERR (RYES_NXM|RYES_WCO|RYES_DERR|RYES_ACLO|RYES_CRC) #define TRACK u3 /* current track */ #define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b int32 ry_csr = 0; /* control/status */ int32 ry_dbr = 0; /* data buffer */ int32 ry_esr = 0; /* error status */ int32 ry_ecode = 0; /* error code */ int32 ry_track = 0; /* desired track */ int32 ry_sector = 0; /* desired sector */ int32 ry_ba = 0; /* bus addr */ int32 ry_wc = 0; /* word count */ int32 ry_state = IDLE; /* controller state */ int32 ry_stopioe = 1; /* stop on error */ int32 ry_cwait = 100; /* command time */ int32 ry_swait = 10; /* seek, per track */ int32 ry_xwait = 1; /* tr set time */ uint8 rx2xb[RY_NUMBY] = { 0 }; /* sector buffer */ DEVICE ry_dev; t_stat ry_rd (int32 *data, int32 PA, int32 access); t_stat ry_wr (int32 data, int32 PA, int32 access); t_stat ry_svc (UNIT *uptr); t_stat ry_reset (DEVICE *dptr); t_stat ry_boot (int32 unitno, DEVICE *dptr); void ry_done (int esr_flags, int new_ecode); t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat ry_attach (UNIT *uptr, char *cptr); /* RY11 data structures ry_dev RY device descriptor ry_unit RY unit list ry_reg RY register list ry_mod RY modifier list */ DIB ry_dib = { IOBA_RY, IOLN_RY, &ry_rd, &ry_wr, 1, IVCL (RY), VEC_RY, { NULL } }; UNIT ry_unit[] = { { UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RY_SIZE) }, { UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RY_SIZE) } }; REG ry_reg[] = { { GRDATA (RYCS, ry_csr, DEV_RDX, 16, 0) }, { GRDATA (RYBA, ry_ba, DEV_RDX, 16, 0) }, { GRDATA (RYWC, ry_wc, DEV_RDX, 8, 0) }, { GRDATA (RYDB, ry_dbr, DEV_RDX, 16, 0) }, { GRDATA (RYES, ry_esr, DEV_RDX, 12, 0) }, { GRDATA (RYERR, ry_ecode, DEV_RDX, 8, 0) }, { GRDATA (RYTA, ry_track, DEV_RDX, 8, 0) }, { GRDATA (RYSA, ry_sector, DEV_RDX, 8, 0) }, { DRDATA (STAPTR, ry_state, 4), REG_RO }, { FLDATA (INT, IREQ (RY), INT_V_RY) }, { FLDATA (ERR, ry_csr, RYCS_V_ERR) }, { FLDATA (TR, ry_csr, RYCS_V_TR) }, { FLDATA (IE, ry_csr, RYCS_V_IE) }, { FLDATA (DONE, ry_csr, RYCS_V_DONE) }, { DRDATA (CTIME, ry_cwait, 24), PV_LEFT }, { DRDATA (STIME, ry_swait, 24), PV_LEFT }, { DRDATA (XTIME, ry_xwait, 24), PV_LEFT }, { BRDATA (SBUF, rx2xb, 8, 8, RY_NUMBY) }, { FLDATA (STOP_IOE, ry_stopioe, 0) }, { URDATA (CAPAC, ry_unit[0].capac, 10, T_ADDR_W, 0, RX_NUMDR, REG_HRO | PV_LEFT) }, { GRDATA (DEVADDR, ry_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, ry_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB ry_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &ry_set_size }, { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &ry_set_size }, { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, #if defined (VM_PDP11) { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", &set_addr_flt, NULL, NULL }, #endif { 0 } }; DEVICE ry_dev = { "RY", ry_unit, ry_reg, ry_mod, RX_NUMDR, DEV_RDX, 20, 1, DEV_RDX, 8, NULL, NULL, &ry_reset, &ry_boot, &ry_attach, NULL, &ry_dib, DEV_FLTA | DEV_DISABLE | DEV_DISI | DEV_UBUS | DEV_Q18 }; /* I/O dispatch routine, I/O addresses 17777170 - 17777172 17777170 floppy CSR 17777172 floppy data register */ t_stat ry_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 1) { /* decode PA<1> */ case 0: /* RYCS */ ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ *data = ry_csr; break; case 1: /* RYDB */ *data = ry_dbr; /* return data */ break; } /* end switch PA */ return SCPE_OK; } t_stat ry_wr (int32 data, int32 PA, int32 access) { int32 drv; switch ((PA >> 1) & 1) { /* decode PA<1> */ /* Writing RYCS, three cases: 1. Writing INIT, reset device 2. Idle and writing new function - clear error, done, transfer ready, int req - save int enable, function, drive - start new function 3. Otherwise, write IE and update interrupts */ case 0: /* RYCS */ ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ if (access == WRITEB) data = (PA & 1)? /* write byte? */ (ry_csr & 0377) | (data << 8): (ry_csr & ~0377) | data; if (data & RYCS_INIT) { /* initialize? */ ry_reset (&ry_dev); /* reset device */ return SCPE_OK; /* end if init */ } if ((data & CSR_GO) && (ry_state == IDLE)) { /* new function? */ ry_csr = (data & RYCS_RW) | RYCS_RY; drv = ((ry_csr & RYCS_DRV)? 1: 0); /* reselect drv */ switch (RYCS_GETFNC (data)) { case RYCS_FILL: case RYCS_EMPTY: ry_state = FEWC; /* state = get wc */ ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ break; case RYCS_SDEN: ry_state = SDCNF; /* state = get conf */ ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ break; case RYCS_ESTAT: ry_state = ESBA; /* state = get ba */ ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ break; case RYCS_READ: case RYCS_WRITE: case RYCS_WRDEL: ry_state = RWDS; /* state = get sector */ ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ ry_esr = ry_esr & RYES_ID; /* clear errors */ ry_ecode = 0; break; default: ry_state = CMD_COMPLETE; /* state = cmd compl */ sim_activate (&ry_unit[drv], ry_cwait); break; } /* end switch func */ return SCPE_OK; } /* end if GO */ if ((data & RYCS_IE) == 0) CLR_INT (RY); else if ((ry_csr & (RYCS_DONE + RYCS_IE)) == RYCS_DONE) SET_INT (RY); ry_csr = (ry_csr & ~RYCS_RW) | (data & RYCS_RW); break; /* end case RYCS */ /* Accessing RYDB, two cases: 1. Write idle, write 2. Write not idle and TR set, state dependent */ case 1: /* RYDB */ if ((PA & 1) || ((ry_state != IDLE) && ((ry_csr & RYCS_TR) == 0))) return SCPE_OK; /* if ~IDLE, need tr */ ry_dbr = data; /* save data */ if (ry_state != IDLE) { drv = ((ry_csr & RYCS_DRV)? 1: 0); /* select drv */ sim_activate (&ry_unit[drv], ry_xwait); /* sched event */ ry_csr = ry_csr & ~RYCS_TR; /* clear xfer */ } break; /* end case RYDB */ } /* end switch PA */ return SCPE_OK; } /* Unit service; the action to be taken depends on the transfer state: IDLE Should never get here FEWC Save word count, set TR, set FEBA FEBA Save bus address, set FEXFR FEXFR Fill/empty buffer RWDS Save sector, set TR, set RWDT RWDT Save track, set RWXFR RWXFR Read/write buffer SDCNF Check confirmation, set SDXFR SDXFR Erase disk CMD_COMPLETE copy requested data to ir, finish command INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command */ t_stat ry_svc (UNIT *uptr) { int32 i, t, func, bps; static uint8 estat[8]; uint32 ba, da; int8 *fbuf = uptr->filebuf; func = RYCS_GETFNC (ry_csr); /* get function */ bps = (ry_csr & RYCS_DEN)? RY_NUMBY: RX_NUMBY; /* get sector size */ ba = (RYCS_GETUAE (ry_csr) << 16) | ry_ba; /* get mem addr */ switch (ry_state) { /* case on state */ case IDLE: /* idle */ return SCPE_IERR; case FEWC: /* word count */ ry_wc = ry_dbr & 0377; /* save WC */ ry_csr = ry_csr | RYCS_TR; /* set TR */ ry_state = FEBA; /* next state */ return SCPE_OK; case FEBA: /* buffer address */ ry_ba = ry_dbr; /* save buf addr */ ry_state = FEXFR; /* next state */ sim_activate (uptr, ry_cwait); /* schedule xfer */ return SCPE_OK; case FEXFR: /* transfer */ if ((ry_wc << 1) > bps) { /* wc too big? */ ry_done (RYES_WCO, 0230); /* error */ break; } if (func == RYCS_FILL) { /* fill? read */ for (i = 0; i < RY_NUMBY; i++) rx2xb[i] = 0; t = Map_ReadB (ba, ry_wc << 1, rx2xb); } else t = Map_WriteB (ba, ry_wc << 1, rx2xb); ry_wc = t >> 1; /* adjust wc */ ry_done (t? RYES_NXM: 0, 0); /* done */ break; case RWDS: /* wait for sector */ ry_sector = ry_dbr & RX_M_SECTOR; /* save sector */ ry_csr = ry_csr | RYCS_TR; /* set xfer */ ry_state = RWDT; /* advance state */ return SCPE_OK; case RWDT: /* wait for track */ ry_track = ry_dbr & RX_M_TRACK; /* save track */ ry_state = RWXFR; /* next state */ sim_activate (uptr, /* sched xfer */ ry_swait * abs (ry_track - uptr->TRACK)); return SCPE_OK; case RWXFR: /* read/write */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ ry_done (0, 0110); /* done, error */ return IORETURN (ry_stopioe, SCPE_UNATT); } if (ry_track >= RX_NUMTR) { /* bad track? */ ry_done (0, 0040); /* done, error */ break; } uptr->TRACK = ry_track; /* now on track */ if ((ry_sector == 0) || (ry_sector > RX_NUMSC)) { /* bad sect? */ ry_done (0, 0070); /* done, error */ break; } if (((uptr->flags & UNIT_DEN) != 0) ^ ((ry_csr & RYCS_DEN) != 0)) { /* densities agree? */ ry_done (RYES_DERR, 0240); /* no, error */ break; } da = CALC_DA (ry_track, ry_sector, bps); /* get disk address */ if (func == RYCS_WRDEL) /* del data? */ ry_esr = ry_esr | RYES_DD; if (func == RYCS_READ) { /* read? */ for (i = 0; i < bps; i++) rx2xb[i] = fbuf[da + i]; } else { if (uptr->flags & UNIT_WPRT) { /* write and locked? */ ry_done (0, 0100); /* done, error */ break; } for (i = 0; i < bps; i++) /* write */ fbuf[da + i] = rx2xb[i]; da = da + bps; if (da > uptr->hwmark) uptr->hwmark = da; } ry_done (0, 0); /* done */ break; case SDCNF: /* confirm set density */ if ((ry_dbr & 0377) != 0111) { /* confirmed? */ ry_done (0, 0250); /* no, error */ break; } ry_state = SDXFR; /* next state */ sim_activate (uptr, ry_cwait * 100); /* schedule operation */ break; case SDXFR: /* erase disk */ for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0; uptr->hwmark = (uint32) uptr->capac; if (ry_csr & RYCS_DEN) uptr->flags = uptr->flags | UNIT_DEN; else uptr->flags = uptr->flags & ~UNIT_DEN; ry_done (0, 0); break; case ESBA: ry_ba = ry_dbr; /* save WC */ ry_state = ESXFR; /* next state */ sim_activate (uptr, ry_cwait); /* schedule xfer */ return SCPE_OK; case ESXFR: estat[0] = ry_ecode; /* fill 8B status */ estat[1] = ry_wc; estat[2] = ry_unit[0].TRACK; estat[3] = ry_unit[1].TRACK; estat[4] = ry_track; estat[5] = ry_sector; estat[6] = ((ry_csr & RYCS_DRV)? 0200: 0) | ((ry_unit[1].flags & UNIT_DEN)? 0100: 0) | ((uptr->flags & UNIT_ATT)? 0040: 0) | ((ry_unit[0].flags & UNIT_DEN)? 0020: 0) | ((ry_csr & RYCS_DEN)? 0001: 0); estat[7] = uptr->TRACK; t = Map_WriteB (ba, 8, estat); /* DMA to memory */ ry_done (t? RYES_NXM: 0, 0); /* done */ break; case CMD_COMPLETE: /* command complete */ ry_done (0, 0); break; case INIT_COMPLETE: /* init complete */ ry_unit[0].TRACK = 1; /* drive 0 to trk 1 */ ry_unit[1].TRACK = 0; /* drive 1 to trk 0 */ if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ ry_done (RYES_ID, 0010); /* init done, error */ break; } da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ for (i = 0; i < bps; i++) /* read sector */ rx2xb[i] = fbuf[da + i]; ry_done (RYES_ID, 0); /* set done */ if ((ry_unit[1].flags & UNIT_ATT) == 0) ry_ecode = 0020; break; } /* end case state */ return SCPE_OK; } /* Command complete. Set done and put final value in interface register, request interrupt if needed, return to IDLE state. */ void ry_done (int32 esr_flags, int32 new_ecode) { int32 drv = (ry_csr & RYCS_DRV)? 1: 0; ry_state = IDLE; /* now idle */ ry_csr = ry_csr | RYCS_DONE; /* set done */ if (ry_csr & CSR_IE) /* if ie, intr */ SET_INT (RY); ry_esr = (ry_esr | esr_flags) & ~(RYES_USEL|RYES_DDEN|RYES_DRDY); if (drv) /* updates RYES */ ry_esr = ry_esr | RYES_USEL; if (ry_unit[drv].flags & UNIT_ATT) { ry_esr = ry_esr | RYES_DRDY; if (ry_unit[drv].flags & UNIT_DEN) ry_esr = ry_esr | RYES_DDEN; } if ((new_ecode > 0) || (ry_esr & RYES_ERR)) /* test for error */ ry_csr = ry_csr | RYCS_ERR; ry_ecode = new_ecode; /* update ecode */ ry_dbr = ry_esr; /* update RYDB */ return; } /* Device initialization. The RY is one of the few devices that schedules an I/O transfer as part of its initialization. */ t_stat ry_reset (DEVICE *dptr) { ry_csr = ry_dbr = 0; /* clear registers */ ry_esr = ry_ecode = 0; /* clear error */ ry_ba = ry_wc = 0; /* clear wc, ba */ ry_track = ry_sector = 0; /* clear trk, sector */ ry_state = IDLE; /* ctrl idle */ CLR_INT (RY); /* clear int req */ sim_cancel (&ry_unit[1]); /* cancel drive 1 */ if (dptr->flags & UNIT_DIS) /* disabled? */ sim_cancel (&ry_unit[0]); else if (ry_unit[0].flags & UNIT_BUF) { /* attached? */ ry_state = INIT_COMPLETE; /* yes, sched init */ sim_activate (&ry_unit[0], ry_swait * abs (1 - ry_unit[0].TRACK)); } else ry_done (RYES_ID, 0010); /* no, error */ return auto_config (0, 0); /* run autoconfig */ } /* Attach routine */ t_stat ry_attach (UNIT *uptr, char *cptr) { uint32 sz; if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { if (sz > RX_SIZE) uptr->flags = uptr->flags | UNIT_DEN; else uptr->flags = uptr->flags & ~UNIT_DEN; } uptr->capac = (uptr->flags & UNIT_DEN)? RY_SIZE: RX_SIZE; return attach_unit (uptr, cptr); } /* Set size routine */ t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = val? RY_SIZE: RX_SIZE; return SCPE_OK; } /* Device bootstrap */ #if defined (VM_PDP11) #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 026) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 042131, /* "YD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012700, 0000000, /* MOV #unit, R0 ; unit number */ 0010003, /* MOV R0, R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0006303, /* ASL R3 */ 0012701, 0177170, /* MOV #RYCS, R1 ; csr */ 0005002, /* CLR R2 ; ba */ 0005004, /* CLR R4 ; density */ 0012705, 0000001, /* MOV #1, R5 ; sector */ 0005104, /* DN: COM R4 ; compl dens */ 0042704, 0177377, /* BIC #177377, R4 ; clr rest */ 0032711, 0000040, /* RD: BIT #40, (R1) ; ready? */ 0001775, /* BEQ .-4 */ 0012746, 0000007, /* MOV #READ+GO, -(SP) */ 0050316, /* BIS R3, (SP) ; or unit */ 0050416, /* BIS R4, (SP) ; or density */ 0012611, /* MOV (SP)+, (R1) ; read & go */ 0105711, /* TSTB (R1) ; xfr ready? */ 0100376, /* BPL .-2 */ 0010561, 0000002, /* MOV R5, 2(R1) ; sector */ 0105711, /* TSTB (R1) ; xfr ready? */ 0100376, /* BPL .-2 */ 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ 0032711, 0000040, /* BIT #40, (R1) ; ready? */ 0001775, /* BEQ .-4 */ 0005711, /* TST (R1) ; error? */ 0100003, /* BEQ OK */ 0005704, /* TST R4 ; single? */ 0001345, /* BNE DN ; no, try again */ 0000000, /* HALT ; dead */ 0012746, 0000003, /* OK: MOV #EMPTY+GO, -(SP); empty & go */ 0050416, /* BIS R4, (SP) ; or density */ 0012611, /* MOV (SP)+, (R1) ; read & go */ 0105711, /* TSTB (R1) ; xfr, done? */ 0001776, /* BPL .-2 */ 0012746, 0000100, /* MOV #100, -(SP) ; assume sd */ 0005704, /* TST R4 ; test dd */ 0001401, /* BEQ .+4 */ 0006316, /* ASL (SP) ; dd, double */ 0011661, 0000002, /* MOV (SP), 2(R1) ; wc */ 0105711, /* TSTB (R1) ; xfr, done? */ 0001776, /* BPL .-2 */ 0010261, 0000002, /* MOV R2, 2(R1) ; ba */ 0032711, 0000040, /* BIT #40, (R1) ; ready? */ 0001775, /* BEQ .-4 */ 0061602, /* ADD (SP), R2 ; cvt wd to byte */ 0062602, /* ADD (SP)+, R2 ; adv buf addr */ 0122525, /* CMPB (R5)+, (R5)+ ; sect += 2 */ 0020527, 0000007, /* CMP R5, #7 ; end? */ 0101715, /* BLOS RD ; read next */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0005007 /* CLR R7 */ }; t_stat ry_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; if ((ry_unit[unitno & RX_M_NUMDR].flags & UNIT_DEN) == 0) return SCPE_NOFNC; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; M[BOOT_CSR >> 1] = ry_dib.ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat ry_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif simh-3.8.1/PDP11/pdp11_xq_bootrom.h0000644000175000017500000004751711111667046015016 0ustar vlmvlm/* pdp11_xq_bootrom.h: DEQNA/DELQA bootrom data ------------------------------------------------------------------------------ Copyright (c) 2003-2008, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ Modification history: 26-Mar-03 DTH Removed 'static' declaration 23-Mar-03 DTH Created by extracting from merged DEQNA bootrom dumps ------------------------------------------------------------------------------ */ #ifndef _PDP11_XQ_BOOTROM_H #define _PDP11_XQ_BOOTROM_H #ifdef VM_PDP11 /* Bootrom code is from merged file 23-334e5.bin, offset 050000, for 4096. bytes. Word 0: NOP Word 1: Branch to extended primary boot Word 2: Branch/Vector to Citizenship tests Word 3: Offset from beginning to checksum word See INIQNA.MAR for further information on format and contents. */ uint16 xq_bootrom[] = { 0000240,0000423,0000546,0007776,0000520,0000000,0100000,0100000, 0002000,0176000,0000000,0000000,0100000,0100000,0006000,0176000, 0000000,0000000,0100000,0020000,0140000,0012706,0001776,0010046, 0012761,0000014,0000004,0005061,0000006,0012761,0001010,0000016, 0005000,0005300,0001376,0005061,0000016,0005000,0005300,0001376, 0012761,0000002,0000016,0005061,0000016,0042767,0037777,0177664, 0026767,0177660,0177702,0001057,0042767,0037777,0177652,0026767, 0177646,0177664,0001050,0042767,0037777,0177650,0026767,0177644, 0177646,0001041,0012704,0007776,0005003,0005002,0116200,0002000, 0005202,0042700,0177400,0060003,0005304,0001370,0013700,0000006, 0026003,0002000,0001020,0000137,0002010,0012702,0012000,0004767, 0000040,0005700,0001010,0011602,0001002,0000167,0004530,0022702, 0000777,0103001,0000112,0013703,0000012,0001401,0000113,0000000, 0000776,0010637,0000764,0062701,0000016,0032761,0100000,0177776, 0001421,0052761,0020000,0177776,0012703,0000777,0005000,0005300, 0001376,0032761,0016000,0177776,0001405,0005303,0001370,0012700, 0000200,0000454,0004767,0000136,0052711,0000002,0042711,0000002, 0012703,0017777,0005303,0001376,0004567,0003514,0177700,0001014, 0005712,0001014,0032762,0002000,0000050,0001402,0052711,0002000, 0004567,0003464,0177622,0001402,0052712,0000100,0012711,0000002, 0005011,0012703,0017777,0005303,0001376,0011100,0042700,0064000, 0022700,0010060,0001402,0052712,0000100,0011200,0162701,0000016, 0000207,0052400,0177652,0013746,0000034,0013746,0000036,0010703, 0062703,0000210,0010337,0000034,0012737,0000340,0000036,0104400, 0012637,0000036,0012637,0000034,0013700,0000762,0052700,0000340, 0062703,0000010,0010337,0000004,0010037,0000006,0010637,0000766, 0010137,0000772,0010237,0000770,0062703,0000012,0010337,0000024, 0010037,0000026,0062703,0000022,0012761,0000774,0177776,0052761, 0100000,0177776,0010337,0000774,0010037,0000776,0005062,0000002, 0005012,0012700,0000162,0060200,0012704,0000112,0005020,0005304, 0001375,0004567,0003202,0177666,0001434,0005262,0000002,0022762, 0000002,0000002,0003355,0000207,0016637,0000002,0000762,0000006, 0052712,0002000,0013706,0000764,0000207,0052712,0020000,0013706, 0000766,0013701,0000772,0013702,0000770,0000207,0052712,0004000, 0000002,0106427,0000000,0010103,0162703,0000016,0010204,0062704, 0000012,0012705,0000006,0012300,0110024,0005305,0001374,0010204, 0062704,0000012,0010405,0005724,0001004,0005724,0001002,0005714, 0001421,0010504,0012700,0177777,0020024,0001016,0020024,0001014, 0020014,0001410,0001011,0010504,0022724,0000252,0001003,0122714, 0000004,0103002,0052712,0000001,0012700,0177777,0004767,0003314, 0013705,0000774,0010703,0062703,0000044,0010337,0000774,0052711, 0000100,0010461,0177772,0005000,0010061,0177774,0012703,0010000, 0005303,0001376,0052712,0004000,0000207,0062706,0000004,0010537, 0000774,0005200,0001767,0011100,0032700,0000200,0001763,0011400, 0042700,0037777,0022700,0140000,0001355,0005764,0000010,0001752, 0005764,0000012,0001747,0052711,0000002,0042711,0000002,0012711, 0002000,0106437,0000762,0004567,0002576,0177666,0001402,0000207, 0010703,0062703,0177160,0010362,0000002,0010362,0000006,0062703, 0000005,0010362,0000004,0005062,0000010,0010203,0062703,0000162, 0012700,0000002,0012705,0000006,0105023,0012704,0000007,0026262, 0000004,0000006,0003003,0016262,0000002,0000006,0117223,0000006, 0005262,0000006,0005304,0001363,0005305,0001356,0012704,0000020, 0105023,0005304,0001375,0005300,0001345,0004567,0002432,0177705, 0001403,0052712,0000002,0000207,0005262,0000010,0022762,0000764, 0000010,0003323,0042761,0100000,0177776,0005062,0000006,0010204, 0062704,0000163,0010462,0000010,0005304,0012703,0000060,0105024, 0005303,0001375,0062762,0000010,0000002,0016262,0000010,0000002, 0012762,0000060,0000004,0105062,0000012,0000261,0106162,0000012, 0103041,0106162,0000012,0062762,0000010,0000002,0000433,0016204, 0000010,0005304,0012703,0000060,0112724,0000377,0005303,0001374, 0012762,0000060,0000004,0016262,0000010,0000002,0112762,0000377, 0000012,0000241,0106162,0000012,0103405,0106162,0000012,0062762, 0000010,0000002,0016204,0000002,0012703,0000007,0105064,0177770, 0116224,0000012,0005303,0001372,0004567,0002154,0177750,0001402, 0000167,0000414,0005762,0000006,0001011,0000241,0106172,0000002, 0103010,0106072,0000002,0106072,0000002,0000403,0000261,0106172, 0000002,0016204,0000010,0010203,0062703,0004362,0012700,0000006, 0111423,0062704,0000010,0005300,0001373,0012711,0000001,0012700, 0177775,0004767,0002352,0010461,0177766,0005061,0177770,0004767, 0002372,0010461,0177772,0005061,0177774,0012700,0077777,0032711, 0100000,0001003,0005300,0001373,0000523,0005000,0004567,0002412, 0000000,0040000,0001115,0016204,0000010,0005204,0010203,0062703, 0004362,0012700,0000006,0111423,0062704,0000010,0005300,0001373, 0042711,0100200,0012700,0177775,0004767,0002224,0010461,0177766, 0005061,0177770,0012700,0177775,0004767,0002240,0010461,0177772, 0005061,0177774,0012700,0077777,0032711,0100000,0001003,0005300, 0001373,0000454,0005000,0004567,0002260,0000000,0000000,0001040, 0042711,0000001,0010204,0062704,0001362,0010205,0062705,0004362, 0012700,0000006,0122425,0001024,0005300,0001374,0005362,0000004, 0001007,0005762,0000006,0001034,0005262,0000006,0000167,0177256, 0005762,0000006,0001002,0000167,0177222,0000261,0000167,0177304, 0052712,0000004,0000405,0052712,0004000,0000402,0052712,0001004, 0052761,0100000,0177776,0000207,0000074,0001422,0002752,0177777, 0052761,0100000,0177776,0052711,0001000,0010703,0062703,0176046, 0010362,0000002,0010362,0000006,0062703,0000004,0010362,0000004, 0010703,0062703,0177726,0010362,0000010,0010203,0062703,0004362, 0017205,0000010,0026262,0000004,0000006,0003003,0016262,0000002, 0000006,0117223,0000006,0005262,0000006,0005305,0001363,0017200, 0000010,0004567,0001536,0103425,0017200,0000010,0004567,0001752, 0000000,0020000,0001401,0000003,0010204,0062704,0001362,0010205, 0062705,0004362,0017200,0000010,0122425,0001003,0005300,0001374, 0000403,0052712,0000010,0000207,0062762,0000002,0000010,0022772, 0177777,0000010,0001402,0000167,0177620,0012700,0177770,0004767, 0001536,0010461,0177766,0005061,0177770,0010203,0062703,0000040, 0010304,0012700,0000010,0012723,0100000,0012723,0100000,0010213, 0062723,0000012,0012723,0177777,0005023,0005023,0005300,0001363, 0010403,0052763,0000200,0000002,0052763,0000300,0000016,0012763, 0177776,0000022,0052763,0000100,0000032,0062763,0000002,0000034, 0062763,0000004,0000050,0012763,0040000,0000062,0010363,0000064, 0062763,0000074,0000064,0010363,0000100,0062763,0000070,0000100, 0012763,0177776,0000102,0012763,0120000,0000112,0012763,0177775, 0000116,0012763,0020000,0000126,0010461,0177772,0005061,0177774, 0012700,0077777,0032711,0100000,0001005,0005300,0001373,0052712, 0001000,0000411,0012700,0000020,0004567,0001376,0100000,0020000, 0001405,0052712,0040000,0052712,0000020,0000207,0010203,0062703, 0000040,0012700,0000010,0016305,0000000,0042705,0037777,0022705, 0140000,0001357,0022700,0000004,0001403,0022700,0000001,0001007, 0005763,0000010,0001346,0005763,0000012,0001343,0000424,0022700, 0000002,0001405,0032763,0100000,0000010,0001733,0000414,0016305, 0000010,0042705,0026417,0022705,0000000,0001323,0016305,0000012, 0042705,0176000,0001716,0062703,0000014,0005300,0001324,0010203, 0062703,0000012,0010204,0062704,0001362,0010405,0022324,0001303, 0022324,0001301,0022324,0001277,0005724,0001275,0005724,0001273, 0022524,0001271,0022524,0001267,0022524,0001265,0010203,0062703, 0000162,0010305,0012700,0000113,0005023,0005300,0001375,0010204, 0062704,0000012,0004767,0000046,0062705,0000020,0004767,0000036, 0004567,0000444,0177674,0001401,0000207,0012700,0177777,0032711, 0020000,0001423,0005300,0001373,0052712,0100000,0000207,0010446, 0012700,0000006,0005205,0012703,0000007,0111425,0005303,0001375, 0005204,0005300,0001367,0012604,0000207,0005712,0001017,0052711, 0001400,0012700,0000056,0004767,0000132,0012700,0000074,0004567, 0000522,0103005,0042712,0001000,0052712,0100000,0000207,0012700, 0000074,0004767,0000156,0001761,0001403,0052712,0000040,0000207, 0012700,0000074,0004767,0000232,0001370,0012700,0002734,0004767, 0000042,0012700,0002752,0004567,0000432,0103757,0012700,0002752, 0004767,0000100,0001766,0001351,0012700,0002752,0004767,0000162, 0001344,0000207,0010203,0062703,0004362,0010204,0062704,0000012, 0010405,0012423,0012423,0012423,0012523,0012523,0012523,0012723, 0000220,0005023,0012723,0000001,0110023,0005300,0001375,0005062, 0000002,0000207,0004567,0000542,0000000,0020000,0001004,0062716, 0000002,0005712,0000207,0016200,0000050,0042700,0137777,0001010, 0016200,0000030,0032700,0137777,0001003,0042712,0040000,0000757, 0005262,0000002,0022762,0000144,0000002,0003751,0042712,0040000, 0000750,0010204,0062704,0001362,0010205,0062705,0004362,0122425, 0001002,0005300,0001374,0000207,0010200,0062700,0000162,0010046, 0011500,0005300,0004767,0000270,0010461,0177766,0005061,0177770, 0012500,0004767,0000306,0012764,0130000,0000002,0011664,0000004, 0010461,0177772,0005061,0177774,0012704,0017777,0032711,0100000, 0001010,0005304,0001373,0052712,0001000,0005726,0052712,0010000, 0000430,0016500,0177776,0006300,0005400,0004567,0000274,0000000, 0020000,0001363,0016500,0177776,0006300,0005400,0010204,0062704, 0001362,0012603,0122423,0001352,0005300,0001374,0022714,0051343, 0001345,0000205,0005046,0006000,0005516,0061600,0005400,0004767, 0000076,0010461,0177766,0005061,0177770,0004767,0000116,0005726, 0001403,0052764,0000200,0000002,0010461,0177772,0005061,0177774, 0012703,0000777,0005000,0032711,0100000,0001010,0005300,0001376, 0005303,0001371,0052712,0001000,0000261,0000401,0000241,0000205, 0010203,0062703,0001362,0012704,0000200,0012723,0051343,0005304, 0001374,0004567,0000020,0000020,0001362,0000207,0004567,0000006, 0000040,0004362,0000207,0012503,0060203,0010304,0012723,0100000, 0012723,0120000,0012513,0060223,0010023,0012723,0100000,0012723, 0100000,0012723,0100000,0005023,0000205,0010046,0005000,0011104, 0052711,0100200,0042704,0077401,0022704,0100260,0001401,0010700, 0016204,0000040,0042704,0037777,0022704,0140000,0001401,0010700, 0016204,0000050,0100002,0042704,0077777,0042704,0076417,0022504, 0001401,0010700,0016204,0000052,0042704,0176000,0001001,0010700, 0016204,0000020,0042704,0037777,0022704,0140000,0001401,0010700, 0016204,0000030,0010446,0042704,0007777,0022504,0001401,0010700, 0012604,0042704,0174377,0022762,0177775,0000046,0001002,0005726, 0000415,0032762,0010000,0000042,0001401,0005004,0016203,0000032, 0042703,0177400,0060304,0022604,0001401,0010700,0010003,0001402, 0052712,0040000,0000205,0000005,0012706,0017776,0010616,0011646, 0162716,0003056,0010703,0062703,0000014,0010337,0000004,0011100, 0000401,0000000,0004767,0000230,0011605,0012725,0022410,0012725, 0000401,0105025,0105025,0012725,0000621,0112725,0000002,0012702, 0002752,0110225,0000302,0110225,0012702,0000013,0005000,0004767, 0000452,0001350,0012702,0002756,0004767,0000660,0001046,0011603, 0112304,0005302,0120427,0000002,0001404,0105704,0001335,0162702, 0000004,0105713,0001402,0121300,0001030,0112300,0105200,0005302, 0003410,0012305,0005723,0162702,0000004,0003403,0112325,0005302, 0003375,0105704,0001417,0005003,0011605,0112725,0000012,0110025, 0110325,0005005,0012702,0000003,0000722,0105700,0001673,0012703, 0000001,0000762,0004767,0001232,0112346,0112366,0000001,0000207, 0042761,0000002,0000016,0016605,0000002,0010504,0062704,0177720, 0010466,0000004,0012702,0000020,0005024,0077202,0010504,0062704, 0177760,0005065,0177722,0010465,0177724,0005065,0177742,0010465, 0177744,0052765,0100000,0177722,0012702,0002756,0006202,0005402, 0010265,0177726,0052765,0120000,0177742,0016604,0000004,0010467, 0001324,0005067,0001322,0062704,0000020,0010467,0001314,0005067, 0001312,0116167,0000000,0001262,0116167,0000002,0001255,0116167, 0000004,0001250,0116167,0000006,0001243,0116167,0000010,0001236, 0116167,0000012,0001231,0105267,0001232,0042761,0000002,0000016, 0052761,0000400,0000016,0004767,0001104,0005065,0000002,0016744, 0001174,0016744,0001166,0016744,0001160,0012744,0000000,0012744, 0001000,0012744,0000253,0004767,0000046,0000207,0016605,0000002, 0010504,0010244,0012744,0000540,0016744,0001122,0016744,0001114, 0016744,0001106,0016744,0001060,0016744,0001052,0016744,0001044, 0062705,0177740,0062702,0000016,0020227,0000074,0002003,0012702, 0000074,0000407,0032702,0000001,0001404,0052765,0000200,0000002, 0005202,0006202,0005402,0010265,0000006,0005065,0000010,0005065, 0000012,0016761,0001024,0000010,0016761,0001020,0000012,0012704, 0000204,0004767,0000610,0103012,0001404,0032765,0001000,0000010, 0001354,0042765,0000200,0000002,0000244,0000207,0042765,0000200, 0000002,0032702,0040004,0001401,0000000,0000207,0016605,0000002, 0062705,0177720,0005065,0000010,0005065,0000012,0016761,0000706, 0000004,0016761,0000702,0000006,0052761,0000001,0000016,0012704, 0100004,0004767,0000470,0103030,0001355,0052761,0000002,0000016, 0012767,0000253,0000576,0012767,0000400,0000572,0012767,0000000, 0000566,0105267,0000616,0005000,0042761,0000002,0000016,0052761, 0000400,0000016,0000244,0000207,0042761,0000001,0000016,0052761, 0000002,0000016,0016605,0000002,0016502,0177776,0042761,0000002, 0000016,0052761,0000400,0000016,0022765,0000540,0177774,0001041, 0105767,0000520,0001015,0026765,0000456,0177772,0001267,0026765, 0000444,0177770,0001263,0026765,0000432,0177766,0001257,0000207, 0122715,0000003,0001253,0016567,0177766,0000410,0016567,0177770, 0000404,0016567,0177772,0000400,0105067,0000430,0000244,0005000, 0000207,0022765,0000220,0177774,0001423,0022765,0001140,0177774, 0001225,0122715,0000005,0001222,0004767,0000262,0016464,0177776, 0177770,0016464,0177774,0177766,0016464,0177772,0177764,0000437, 0010504,0060204,0010503,0062703,0177720,0016302,0000010,0042702, 0174377,0156302,0000012,0062702,0000056,0022724,0000002,0001027, 0062765,0000010,0177776,0032714,0000001,0001021,0010503,0062703, 0177760,0012423,0012423,0012423,0010504,0062704,0177774,0016744, 0000234,0016744,0000226,0016744,0000220,0004767,0177122,0000167, 0177272,0016737,0000156,0000030,0016737,0000152,0000032,0016737, 0000146,0000034,0052761,0000002,0000016,0000264,0000207,0012703, 0037777,0000241,0012702,0000220,0030461,0000016,0001006,0005303, 0001376,0005302,0001371,0000261,0000207,0016102,0000016,0010261, 0000016,0032765,0040000,0000010,0001401,0000261,0000207,0010546, 0010703,0062703,0000050,0012702,0000030,0012325,0005725,0112325, 0005302,0001375,0012605,0010504,0012702,0000034,0010244,0012744, 0001140,0000207,0000253,0000400,0000000,0000007,0000001,0001403, 0000000,0000002,0000402,0003400,0003000,0000000,0000000,0000000, 0000144,0022401,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0102206 }; #endif /* VM_PDP11 */ #endif /* _PDP11_XQ_BOOTROM_H */ simh-3.8.1/PDP11/pdp11_cpumod.c0000644000175000017500000012207611112123012014056 0ustar vlmvlm/* pdp11_cpumod.c: PDP-11 CPU model-specific features Copyright (c) 2004-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. system PDP-11 model-specific registers 20-May-08 RMS Added JCSR default for KDJ11B, KDJ11E 22-Apr-08 RMS Fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (found by Walter Mueller) 29-Apr-07 RMS Don't run bus setup routine during RESTORE 30-Aug-05 RMS Added additional 11/60 registers 16-Aug-05 RMS Fixed C++ declaration and cast problems 15-Feb-05 RMS Fixed bug in SHOW MODEL (from Sergey Okhapkin) 19-Jan-05 RMS Added variable SYSID, MBRK write (from Tim Chapman) This module includes CPU- and system-specific registers, such as the Unibus map and control registers on 22b Unibus systems, the board registers for the F11- and J11-based systems, and the system registers for the PDP-11/44, PDP-11/45, PDP-11/60, and PDP-11/70. Most registers are implemented at a minimum level: just enough to satisfy the machine identification code in the various operating systems. */ #include "pdp11_defs.h" #include "pdp11_cpumod.h" #include /* Byte write macros for system registers */ #define ODD_IGN(cur) \ if ((access == WRITEB) && (pa & 1)) \ return SCPE_OK #define ODD_WO(cur) \ if ((access == WRITEB) && (pa & 1)) \ cur = cur << 8 #define ODD_MRG(prv,cur) \ if (access == WRITEB) \ cur =((pa & 1)? (((prv) & 0377) | ((cur) & 0177400)) : \ (((prv) & 0177400) | ((cur) & 0377))) int32 SR = 0; /* switch register */ int32 DR = 0; /* display register */ int32 MBRK = 0; /* 11/70 microbreak */ int32 SYSID = 0x1234; /* 11/70 system ID */ int32 WCS = 0; /* 11/60 WCS control */ int32 CPUERR = 0; /* CPU error reg */ int32 MEMERR = 0; /* memory error reg */ int32 CCR = 0; /* cache control reg */ int32 HITMISS = 0; /* hit/miss reg */ int32 MAINT = 0; /* maint reg */ int32 JCSR = 0; /* J11 control */ int32 JCSR_dflt = 0; /* J11 boot ctl def */ int32 JPCR = 0; /* J11 page ctrl */ int32 JASR = 0; /* J11 addtl status */ int32 UDCR = 0; /* UBA diag ctrl */ int32 UDDR = 0; /* UBA diag data */ int32 UCSR = 0; /* UBA control */ int32 uba_last = 0; /* UBA last mapped */ int32 ub_map[UBM_LNT_LW] = { 0 }; /* UBA map array */ int32 toy_state = 0; uint8 toy_data[TOY_LNT] = { 0 }; static int32 clk_tps_map[4] = { 60, 60, 50, 800 }; extern uint16 *M; extern int32 R[8]; extern DEVICE cpu_dev, *sim_devices[]; extern UNIT cpu_unit; extern FILE *sim_log; extern int32 STKLIM, PIRQ; extern uint32 cpu_model, cpu_type, cpu_opt; extern int32 clk_fie, clk_fnxm, clk_tps, clk_default; extern int32 sim_switches; t_stat CPU24_rd (int32 *data, int32 addr, int32 access); t_stat CPU24_wr (int32 data, int32 addr, int32 access); t_stat CPU44_rd (int32 *data, int32 addr, int32 access); t_stat CPU44_wr (int32 data, int32 addr, int32 access); t_stat CPU45_rd (int32 *data, int32 addr, int32 access); t_stat CPU45_wr (int32 data, int32 addr, int32 access); t_stat CPU60_rd (int32 *data, int32 addr, int32 access); t_stat CPU60_wr (int32 data, int32 addr, int32 access); t_stat CPU70_rd (int32 *data, int32 addr, int32 access); t_stat CPU70_wr (int32 data, int32 addr, int32 access); t_stat CPUJ_rd (int32 *data, int32 addr, int32 access); t_stat CPUJ_wr (int32 data, int32 addr, int32 access); t_stat REG_rd (int32 *data, int32 addr, int32 access); t_stat REG_wr (int32 data, int32 addr, int32 access); t_stat SR_rd (int32 *data, int32 addr, int32 access); t_stat DR_wr (int32 data, int32 addr, int32 access); t_stat CTLFB_rd (int32 *data, int32 addr, int32 access); t_stat CTLFB_wr (int32 data, int32 addr, int32 access); t_stat CTLJB_rd (int32 *data, int32 addr, int32 access); t_stat CTLJB_wr (int32 data, int32 addr, int32 access); t_stat CTLJD_rd (int32 *data, int32 addr, int32 access); t_stat CTLJD_wr (int32 data, int32 addr, int32 access); t_stat CTLJE_rd (int32 *data, int32 addr, int32 access); t_stat CTLJE_wr (int32 data, int32 addr, int32 access); t_stat UBA24_rd (int32 *data, int32 addr, int32 access); t_stat UBA24_wr (int32 data, int32 addr, int32 access); t_stat UBAJ_rd (int32 *data, int32 addr, int32 access); t_stat UBAJ_wr (int32 data, int32 addr, int32 access); t_stat sys_reset (DEVICE *dptr); int32 toy_read (void); void toy_write (int32 bit); uint8 toy_set (int32 val); t_stat sys_set_jclk_dflt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat sys_show_jclk_dflt (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat PSW_rd (int32 *data, int32 addr, int32 access); extern t_stat PSW_wr (int32 data, int32 addr, int32 access); extern t_stat APR_rd (int32 *data, int32 addr, int32 access); extern t_stat APR_wr (int32 data, int32 addr, int32 access); extern t_stat MMR012_rd (int32 *data, int32 addr, int32 access); extern t_stat MMR012_wr (int32 data, int32 addr, int32 access); extern t_stat MMR3_rd (int32 *data, int32 addr, int32 access); extern t_stat MMR3_wr (int32 data, int32 addr, int32 access); extern t_stat ubm_rd (int32 *data, int32 addr, int32 access); extern t_stat ubm_wr (int32 data, int32 addr, int32 access); extern void put_PIRQ (int32 val); /* Fixed I/O address table entries */ DIB psw_dib = { IOBA_PSW, IOLN_PSW, &PSW_rd, &PSW_wr, 0 }; DIB cpuj_dib = { IOBA_CPU, IOLN_CPU, &CPUJ_rd, &CPUJ_wr, 0 }; DIB cpu24_dib = { IOBA_CPU, IOLN_CPU, &CPU24_rd, &CPU24_wr, 0 }; DIB cpu44_dib = { IOBA_CPU, IOLN_CPU, &CPU44_rd, &CPU44_wr, 0 }; DIB cpu45_dib = { IOBA_CPU, IOLN_CPU, &CPU45_rd, &CPU45_wr, 0 }; DIB cpu60_dib = { IOBA_CPU, IOLN_CPU, &CPU60_rd, &CPU60_wr, 0 }; DIB cpu70_dib = { IOBA_CPU, IOLN_CPU, &CPU70_rd, &CPU70_wr, 0 }; DIB reg_dib = { IOBA_GPR, IOLN_GPR, ®_rd, ®_wr, 0 }; DIB ctlfb_dib = { IOBA_CTL, IOLN_CTL, &CTLFB_rd, &CTLFB_wr }; DIB ctljb_dib = { IOBA_CTL, IOLN_CTL, &CTLJB_rd, &CTLJB_wr }; DIB ctljd_dib = { IOBA_CTL, IOLN_CTL, &CTLJD_rd, &CTLJD_wr }; DIB ctlje_dib = { IOBA_CTL, IOLN_CTL, &CTLJE_rd, &CTLJE_wr }; DIB uba24_dib = { IOBA_UCTL, IOLN_UCTL, &UBA24_rd, &UBA24_wr }; DIB ubaj_dib = {IOBA_UCTL, IOLN_UCTL, &UBAJ_rd, &UBAJ_wr }; DIB supv_dib = { IOBA_SUP, IOLN_SUP, &APR_rd, &APR_wr, 0 }; DIB kipdr_dib = { IOBA_KIPDR, IOLN_KIPDR, &APR_rd, &APR_wr, 0 }; DIB kdpdr_dib = { IOBA_KDPDR, IOLN_KDPDR, &APR_rd, &APR_wr, 0 }; DIB kipar_dib = { IOBA_KIPAR, IOLN_KIPAR, &APR_rd, &APR_wr, 0 }; DIB kdpar_dib = { IOBA_KDPAR, IOLN_KDPAR, &APR_rd, &APR_wr, 0 }; DIB uipdr_dib = { IOBA_UIPDR, IOLN_UIPDR, &APR_rd, &APR_wr, 0 }; DIB udpdr_dib = { IOBA_UDPDR, IOLN_UDPDR, &APR_rd, &APR_wr, 0 }; DIB uipar_dib = { IOBA_UIPAR, IOLN_UIPAR, &APR_rd, &APR_wr, 0 }; DIB udpar_dib = { IOBA_UDPAR, IOLN_UDPAR, &APR_rd, &APR_wr, 0 }; DIB sr_dib = { IOBA_SR, IOLN_SR, &SR_rd, NULL, 0 }; DIB dr_dib = { IOBA_SR, IOLN_SR, NULL, &DR_wr, 0 }; DIB mmr012_dib = { IOBA_MMR012, IOLN_MMR012, &MMR012_rd, &MMR012_wr, 0 }; DIB mmr3_dib = { IOBA_MMR3, IOLN_MMR3, &MMR3_rd, &MMR3_wr, 0 }; DIB ubm_dib = { IOBA_UBM, IOLN_UBM, &ubm_rd, &ubm_wr, 0 }; CPUTAB cpu_tab[MOD_MAX] = { { "11/03", SOP_1103, OPT_1103, MEMSIZE64K, PSW_1103, 0, 0, 0, 0, 0 }, { "11/04", SOP_1104, OPT_1104, MEMSIZE64K, PSW_1104, 0, 0, 0, 0, 0 }, { "11/05", SOP_1105, OPT_1105, MEMSIZE64K, PSW_1105, 0, 0, 0, 0, 0 }, { "11/20", SOP_1120, OPT_1120, MEMSIZE64K, PSW_1120, 0, 0, 0, 0, 0 }, { "11/23", SOP_1123, OPT_1123, MAXMEMSIZE, PSW_F, MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, { "11/23+", SOP_1123P, OPT_1123P, MAXMEMSIZE, PSW_F, MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, { "11/24", SOP_1124, OPT_1124, MAXMEMSIZE, PSW_F, MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, { "11/34", SOP_1134, OPT_1134, UNIMEMSIZE, PSW_1134, 0, PAR_1134, PDR_1134, MM0_1134, 0 }, { "11/40", SOP_1140, OPT_1140, UNIMEMSIZE, PSW_1140, 0, PAR_1140, PDR_1140, MM0_1140, 0 }, { "11/44", SOP_1144, OPT_1144, MAXMEMSIZE, PSW_1144, MFPT_44, PAR_1144, PDR_1144, MM0_1144, MM3_1144 }, { "11/45", SOP_1145, OPT_1145, UNIMEMSIZE, PSW_1145, 0, PAR_1145, PDR_1145, MM0_1145, MM3_1145 }, { "11/60", SOP_1160, OPT_1160, UNIMEMSIZE, PSW_1160, 0, PAR_1160, PDR_1160, MM0_1160, 0 }, { "11/70", SOP_1170, OPT_1170, MAXMEMSIZE, PSW_1170, 0, PAR_1170, PDR_1170, MM0_1170, MM3_1170 }, { "11/73", SOP_1173, OPT_1173, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/53", SOP_1153, OPT_1153, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/73B", SOP_1173B, OPT_1173B, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/83", SOP_1183, OPT_1183, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/84", SOP_1184, OPT_1184, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/93", SOP_1193, OPT_1193, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, { "11/94", SOP_1194, OPT_1194, MAXMEMSIZE, PSW_J, MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J } }; CNFTAB cnf_tab[] = { { HAS_PSW, 0, &psw_dib }, /* PSW */ { CPUT_J, 0, &cpuj_dib }, /* CPU control */ { CPUT_24, 0, &cpu24_dib }, { CPUT_44, 0, &cpu44_dib }, { CPUT_45, 0, &cpu45_dib }, { CPUT_60, 0, &cpu60_dib }, { CPUT_70, 0, &cpu70_dib }, { HAS_IOSR, 0, ®_dib }, { CPUT_23P, 0, &ctlfb_dib }, /* board ctls */ { CPUT_JB, 0, &ctljb_dib }, { CPUT_53, 0, &ctljd_dib }, { CPUT_JE, 0, &ctlje_dib }, { CPUT_24, 0, &uba24_dib }, /* UBA */ { CPUT_JU, 0, &ubaj_dib }, { 0, OPT_MMU, &kipdr_dib }, /* MMU */ { 0, OPT_MMU, &kipar_dib }, { 0, OPT_MMU, &uipdr_dib }, { 0, OPT_MMU, &uipar_dib }, { 0, OPT_MMU, &mmr012_dib }, /* MMR0-2 */ { HAS_MMR3, 0, &mmr3_dib }, /* MMR3 */ { 0, OPT_UBM, &ubm_dib }, /* Unibus map */ { HAS_SID, 0, &kdpdr_dib }, /* supv, I/D */ { HAS_SID, 0, &kdpar_dib }, { HAS_SID, 0, &supv_dib }, { HAS_SID, 0, &udpdr_dib }, { HAS_SID, 0, &udpar_dib }, { HAS_SR, 0, &sr_dib }, /* SR */ { HAS_DR, 0, &dr_dib }, /* DR */ { 0, 0, NULL } }; static const char *opt_name[] = { "Unibus", "Qbus", "EIS", "NOEIS", "FIS", "NOFIS", "FPP", "NOFPP", "CIS", "NOCIS", "MMU", "NOMMU", "RH11", "RH70", "PARITY", "NOPARITY", "Unibus map", "No map", NULL }; static const char *jcsr_val[4] = { "LINE", "50HZ", "60HZ", "800HZ" }; /* SYSTEM data structures sys_dev SYSTEM device descriptor sys_unit SYSTEM unit descriptor sys_reg SYSTEM register list */ UNIT sys_unit = { UDATA (NULL, 0, 0) }; REG sys_reg[] = { { ORDATA (SR, SR, 16) }, { ORDATA (DR, DR, 16) }, { ORDATA (MEMERR, MEMERR, 16) }, { ORDATA (CCR, CCR, 16) }, { ORDATA (MAINT, MAINT, 16) }, { ORDATA (HITMISS, HITMISS, 16) }, { ORDATA (CPUERR, CPUERR, 16) }, { ORDATA (MBRK, MBRK, 16) }, { ORDATA (WCS, WCS, 16) }, { ORDATA (SYSID, SYSID, 16) }, { ORDATA (JCSR, JCSR, 16) }, { ORDATA (JCSR_DFLT, JCSR_dflt, 16), REG_HRO }, { ORDATA (JPCR, JPCR, 16) }, { ORDATA (JASR, JASR, 16) }, { ORDATA (UDCR, UDCR, 16) }, { ORDATA (UDDR, UDDR, 16) }, { ORDATA (UCSR, UCSR, 16) }, { ORDATA (ULAST, uba_last, 23) }, { BRDATA (UBMAP, ub_map, 8, 22, UBM_LNT_LW) }, { DRDATA (TOY_STATE, toy_state, 6), REG_HRO }, { BRDATA (TOY_DATA, toy_data, 8, 8, TOY_LNT), REG_HRO }, { NULL} }; MTAB sys_mod[] = { { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "JCLK_DFLT", "JCLK_DFLT", &sys_set_jclk_dflt, &sys_show_jclk_dflt }, { 0 } }; DEVICE sys_dev = { "SYSTEM", &sys_unit, sys_reg, sys_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &sys_reset, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL }; /* Switch and display registers - many */ t_stat SR_rd (int32 *data, int32 pa, int32 access) { *data = SR; return SCPE_OK; } t_stat DR_wr (int32 data, int32 pa, int32 access) { DR = data; return SCPE_OK; } /* GPR's - 11/04, 11/05 */ t_stat REG_rd (int32 *data, int32 pa, int32 access) { *data = R[pa & 07]; return SCPE_OK; } t_stat REG_wr (int32 data, int32 pa, int32 access) { int32 reg = pa & 07; if (access == WRITE) R[reg] = data; else if (pa & 1) R[reg] = (R[reg] & 0377) | (data << 8); else R[reg] = (R[reg] & ~0377) | data; return SCPE_OK; } /* CPU control registers - 11/24 */ t_stat CPU24_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 013: /* CPUERR */ *data = 0; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPU24_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 013: /* CPUERR */ return SCPE_OK; } /* end switch pa */ return SCPE_NXM; /* unimplemented */ } /* CPU control registers - 11/44 */ t_stat CPU44_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ *data = MEMERR; return SCPE_OK; case 003: /* CCR */ *data = CCR & CCR44_RD; return SCPE_OK; case 004: /* MAINT */ *data = MAINT & CMR44_RD; return SCPE_OK; case 005: /* Hit/miss */ *data = HITMISS; return SCPE_OK; case 006: /* CDR */ *data = 0; return SCPE_OK; case 013: /* CPUERR */ if (CPUERR & CPUE_YEL) /* 11/44 stack err */ CPUERR = (CPUERR & ~CPUE_YEL) | CPUE_RED; /* in <2> not <3> */ if (CPUERR & (CPUE_ODD|CPUE_NXM|CPUE_TMO)) /* additional flag */ CPUERR = CPUERR | CPUE44_BUSE; *data = CPUERR & CPUE_IMP; return SCPE_OK; case 015: /* PIRQ */ *data = PIRQ; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPU44_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ MEMERR = 0; return SCPE_OK; case 003: /* CCR */ ODD_MRG (CCR, data); CCR = data & CCR44_WR; return SCPE_OK; case 004: /* MAINT */ ODD_MRG (MAINT, data); MAINT = data & CMR44_WR; return SCPE_OK; case 005: /* Hit/miss */ return SCPE_OK; case 013: /* CPUERR */ CPUERR = 0; return SCPE_OK; case 015: /* PIRQ */ ODD_WO (data); put_PIRQ (data); return SCPE_OK; } return SCPE_NXM; /* unimplemented */ } /* CPU control registers - 11/45 */ t_stat CPU45_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 014: /* MBRK */ *data = MBRK; return SCPE_OK; case 015: /* PIRQ */ *data = PIRQ; return SCPE_OK; case 016: /* STKLIM */ *data = STKLIM & STKLIM_RW; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPU45_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 015: /* PIRQ */ ODD_WO (data); put_PIRQ (data); return SCPE_OK; case 016: /* STKLIM */ ODD_WO (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ return SCPE_NXM; /* unimplemented */ } /* CPU control registers - 11/60 */ t_stat CPU60_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 000: /* WCS */ *data = WCS & WCS60_RD; return SCPE_OK; case 002: /* MEMERR */ *data = MEMERR & MEME60_RD; return SCPE_OK; case 003: /* CCR */ *data = CCR & CCR60_RD; return SCPE_OK; case 005: /* Hit/miss */ *data = HITMISS; return SCPE_OK; case 013: /* CPUERR */ if (CPUERR & CPUE_NXM) /* TMO only */ CPUERR = (CPUERR & ~CPUE_NXM) | CPUE_TMO; *data = CPUERR & CPUE60_RD; return SCPE_OK; case 016: /* STKLIM */ *data = STKLIM & STKLIM_RW; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPU60_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 000: /* WCS */ WCS = data & WCS60_WR; return SCPE_OK; case 002: /* MEMERR */ MEMERR = 0; return SCPE_OK; case 003: /* CCR */ ODD_IGN (data); CCR = data & CCR60_WR; return SCPE_OK; case 005: /* Hit/miss */ return SCPE_OK; case 013: /* CPUERR */ CPUERR = 0; return SCPE_OK; case 014: /* MBRK */ MBRK = data & MBRK60_WR; return SCPE_OK; case 016: /* STKLIM */ ODD_WO (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ return SCPE_NXM; /* unimplemented */ } /* CPU control registers - 11/70 */ t_stat CPU70_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 000: /* low error */ *data = 0; return SCPE_OK; case 001: /* high error */ *data = 0; return SCPE_OK; case 002: /* MEMERR */ *data = MEMERR; return SCPE_OK; case 003: /* CCR */ *data = CCR; return SCPE_OK; case 004: /* MAINT */ *data = 0; return SCPE_OK; case 005: /* Hit/miss */ *data = HITMISS; return SCPE_OK; case 010: /* low size */ *data = (MEMSIZE >> 6) - 1; return SCPE_OK; case 011: /* high size */ *data = 0; return SCPE_OK; case 012: /* system ID */ *data = SYSID; return SCPE_OK; case 013: /* CPUERR */ *data = CPUERR & CPUE_IMP; return SCPE_OK; case 014: /* MBRK */ *data = MBRK; return SCPE_OK; case 015: /* PIRQ */ *data = PIRQ; return SCPE_OK; case 016: /* STKLIM */ *data = STKLIM & STKLIM_RW; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPU70_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ ODD_WO (data); MEMERR = MEMERR & ~data; return SCPE_OK; case 003: /* CCR */ ODD_MRG (CCR, data); CCR = data; return SCPE_OK; case 004: /* MAINT */ return SCPE_OK; case 005: /* Hit/miss */ return SCPE_OK; case 010: /* low size */ return SCPE_OK; case 011: /* high size */ return SCPE_OK; case 013: /* CPUERR */ CPUERR = 0; return SCPE_OK; case 014: /* MBRK */ ODD_IGN (data); MBRK = data & MBRK70_WR; return SCPE_OK; case 015: /* PIRQ */ ODD_WO (data); put_PIRQ (data); return SCPE_OK; case 016: /* STKLIM */ ODD_WO (data); STKLIM = data & STKLIM_RW; return SCPE_OK; } /* end switch pa */ return SCPE_NXM; /* unimplemented */ } /* CPU control registers - J11 */ t_stat CPUJ_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ *data = MEMERR; return SCPE_OK; case 003: /* CCR */ *data = CCR; return SCPE_OK; case 004: /* MAINT */ *data = MAINT | MAINT_NOFPA | MAINT_BPOK | (UNIBUS? MAINT_U: MAINT_Q); if (CPUT (CPUT_53)) *data |= MAINT_KDJD | MAINT_POROM; if (CPUT (CPUT_73)) *data |= MAINT_KDJA | MAINT_POODT; if (CPUT (CPUT_73B|CPUT_83|CPUT_84)) *data |= MAINT_KDJB | MAINT_POROM; if (CPUT (CPUT_93|CPUT_94)) *data |= MAINT_KDJE | MAINT_POROM; return SCPE_OK; case 005: /* Hit/miss */ if (CPUT (CPUT_73B)) /* must be 0 for 73B */ *data = 0; else *data = HITMISS | 010; /* must be nz for 11/8X */ return SCPE_OK; case 013: /* CPUERR */ *data = CPUERR & CPUE_IMP; return SCPE_OK; case 015: /* PIRQ */ *data = PIRQ; return SCPE_OK; } /* end switch PA */ *data = 0; return SCPE_NXM; /* unimplemented */ } t_stat CPUJ_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 017) { /* decode pa<4:1> */ case 002: /* MEMERR */ MEMERR = 0; return SCPE_OK; case 003: /* CCR */ ODD_MRG (CCR, data); CCR = data; return SCPE_OK; case 004: /* MAINT */ return SCPE_OK; case 005: /* Hit/miss */ return SCPE_OK; case 013: /* CPUERR */ CPUERR = 0; return SCPE_OK; case 015: /* PIRQ */ ODD_WO (data); put_PIRQ (data); return SCPE_OK; } /* end switch pa */ return SCPE_NXM; /* unimplemented */ } /* Board control registers - KDF11B */ t_stat CTLFB_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* PCR */ *data = JPCR & PCRFB_RW; return SCPE_OK; case 1: /* MAINT */ *data = MAINT; return SCPE_OK; case 2: /* CDR */ *data = SR & CDRFB_RD; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat CTLFB_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* PCR */ ODD_MRG (JPCR, data); JPCR = data & PCRFB_RW; return SCPE_OK; case 1: /* MAINT */ ODD_MRG (MAINT, data); MAINT = data; return SCPE_OK; case 2: /* CDR */ ODD_WO (data); DR = data & CDRFB_WR; return SCPE_OK; } return SCPE_NXM; } /* Board control registers - KDJ11B */ t_stat CTLJB_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ *data = JCSR & CSRJB_RD; return SCPE_OK; case 1: /* PCR */ *data = JPCR & PCRJB_RW; return SCPE_OK; case 2: /* CDR */ *data = SR & CDRJB_RD; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat CTLJB_wr (int32 data, int32 pa, int32 access) { int32 t; switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ ODD_MRG (JCSR, data); JCSR = (JCSR & ~CSRJB_WR) | (data & CSRJB_WR); if (JCSR & CSRJ_LTCI) /* force LTC int enb? */ clk_fie = 1; else clk_fie = 0; if (JCSR & CSRJ_LTCD) /* force LTC reg nxm? */ clk_fnxm = 1; else clk_fnxm = 0; t = CSRJ_LTCSEL (JCSR); /* get freq sel */ if (t) clk_tps = clk_tps_map[t]; else clk_tps = clk_default; return SCPE_OK; case 1: /* PCR */ ODD_MRG (JPCR, data); JPCR = data & PCRJB_RW; return SCPE_OK; case 2: /* CDR */ ODD_WO (data); DR = data & CDRJB_WR; return SCPE_OK; } return SCPE_NXM; } /* Board control registers - KDJ11D */ t_stat CTLJD_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ *data = JCSR & CSRJD_RD; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat CTLJD_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ ODD_MRG (JCSR, data); JCSR = (JCSR & ~CSRJD_WR) | (data & CSRJD_WR); return SCPE_OK; } return SCPE_NXM; } /* Board control registers - KDJ11E */ t_stat CTLJE_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ *data = JCSR & CSRJE_RD; return SCPE_OK; case 1: /* PCR */ *data = JPCR & PCRJE_RW; return SCPE_OK; case 2: /* CDR */ *data = SR & CDRJE_RD; return SCPE_OK; case 3: /* ASR */ JASR = (JASR & ~ASRJE_TOY) | (toy_read () << ASRJE_V_TOY); *data = JASR & ASRJE_RW; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat CTLJE_wr (int32 data, int32 pa, int32 access) { int32 t; switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* CSR */ ODD_MRG (JCSR, data); JCSR = (JCSR & ~CSRJE_WR) | (data & CSRJE_WR); if (JCSR & CSRJ_LTCI) /* force LTC int enb? */ clk_fie = 1; else clk_fie = 0; if (JCSR & CSRJ_LTCD) /* force LTC reg nxm? */ clk_fnxm = 1; else clk_fnxm = 0; t = CSRJ_LTCSEL (JCSR); /* get freq sel */ if (t) clk_tps = clk_tps_map[t]; else clk_tps = clk_default; return SCPE_OK; case 1: /* PCR */ ODD_MRG (JPCR, data); JPCR = data & PCRJE_RW; return SCPE_OK; case 2: /* CDR */ ODD_WO (data); DR = data & CDRJE_WR; return SCPE_OK; case 3: /* ASR */ ODD_MRG (JASR, data); JASR = data & ASRJE_RW; toy_write (ASRJE_TOYBIT (JASR)); return SCPE_OK; } return SCPE_NXM; } /* Unibus adapter registers - KT24 */ t_stat UBA24_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 2: /* LMAL */ *data = uba_last & LMAL_RD; return SCPE_OK; case 3: /* LMAH */ *data = uba_last & LMAH_RD; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat UBA24_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 3: /* ASR */ ODD_IGN (data); uba_last = (uba_last & ~LMAH_WR) | ((data & LMAH_WR) << 16); return SCPE_OK; } return SCPE_NXM; } /* Unibus registers - KTJ11B */ t_stat UBAJ_rd (int32 *data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* DCR */ *data = UDCR & DCRKTJ_RD; return SCPE_OK; case 1: /* DDR */ *data = UDDR & DDRKTJ_RW; return SCPE_OK; case 2: /* CSR */ *data = UCSR & MCRKTJ_RD; return SCPE_OK; } *data = 0; return SCPE_NXM; } t_stat UBAJ_wr (int32 data, int32 pa, int32 access) { switch ((pa >> 1) & 03) { /* decode pa<2:1> */ case 0: /* DCR */ ODD_MRG (UDCR, data); UDCR = (UDCR & ~DCRKTJ_WR) | (data & DCRKTJ_WR); return SCPE_OK; case 1: /* DDR */ ODD_MRG (UDDR, data); UDDR = data & DDRKTJ_RW;; return SCPE_OK; case 2: /* CSR */ ODD_MRG (UCSR, data); UCSR = (UCSR & ~MCRKTJ_WR) | (data & MCRKTJ_WR); return SCPE_OK; } return SCPE_NXM; } /* KDJ11E TOY routines */ int32 toy_read (void) { time_t curr; struct tm *ctm; int32 bit; if (toy_state == 0) { curr = time (NULL); /* get curr time */ if (curr == (time_t) -1) /* error? */ return 0; ctm = localtime (&curr); /* decompose */ if (ctm == NULL) /* error? */ return 0; toy_data[TOY_HSEC] = 0x50; toy_data[TOY_SEC] = toy_set (ctm->tm_sec); toy_data[TOY_MIN] = toy_set (ctm->tm_min); toy_data[TOY_HR] = toy_set (ctm->tm_hour); toy_data[TOY_DOW] = toy_set (ctm->tm_wday); toy_data[TOY_DOM] = toy_set (ctm->tm_mday); toy_data[TOY_MON] = toy_set (ctm->tm_mon + 1); toy_data[TOY_YR] = toy_set (ctm->tm_year % 100); } bit = toy_data[toy_state >> 3] >> (toy_state & 07); toy_state = (toy_state + 1) % (TOY_LNT * 8); return (bit & 1); } void toy_write (int32 bit) { toy_state = 0; return; } uint8 toy_set (int32 val) { uint32 d1, d2; d1 = val / 10; d2 = val % 10; return (uint8) ((d1 << 4) | d2); } /* Build I/O space entries for CPU */ t_stat cpu_build_dib (void) { int32 i; t_stat r; for (i = 0; cnf_tab[i].dib != NULL; i++) { /* loop thru config tab */ if (((cnf_tab[i].cpum == 0) || (cpu_type & cnf_tab[i].cpum)) && ((cnf_tab[i].optm == 0) || (cpu_opt & cnf_tab[i].optm))) { if (r = build_ubus_tab (&cpu_dev, cnf_tab[i].dib)) /* add to dispatch tab */ return r; } } return SCPE_OK; } /* Set/show CPU model */ t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr != NULL) return SCPE_ARG; if (val >= MOD_MAX) return SCPE_IERR; if (val == (int32) cpu_model) return SCPE_OK; if (MEMSIZE > cpu_tab[val].maxm) cpu_set_size (uptr, cpu_tab[val].maxm, NULL, NULL); if (MEMSIZE > cpu_tab[val].maxm) return SCPE_INCOMP; cpu_model = val; cpu_type = 1u << cpu_model; cpu_opt = cpu_tab[cpu_model].std; cpu_set_bus (cpu_opt); reset_all (0); /* reset world */ return SCPE_OK; } t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 i, all_opt; fprintf (st, "%s", cpu_tab[cpu_model].name); all_opt = cpu_tab[cpu_model].opt; for (i = 0; opt_name[2 * i] != NULL; i++) { if ((all_opt >> i) & 1) fprintf (st, ", %s", ((cpu_opt >> i) & 1)? opt_name[2 * i]: opt_name[(2 * i) + 1]); } return SCPE_OK; } /* Set/clear CPU option */ t_stat cpu_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val & cpu_tab[cpu_model].opt) == 0) return SCPE_ARG; cpu_opt = cpu_opt | val; return SCPE_OK; } t_stat cpu_clr_opt (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val & cpu_tab[cpu_model].opt) == 0) return SCPE_ARG; cpu_opt = cpu_opt & ~val; return SCPE_OK; } /* Memory allocation */ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 mc = 0; uint32 i, clim; uint16 *nM; if ((val <= 0) || (val > (int32) cpu_tab[cpu_model].maxm) || ((val & 07777) != 0)) return SCPE_ARG; for (i = val; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1]; if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) return SCPE_OK; nM = (uint16 *) calloc (val >> 1, sizeof (uint16)); if (nM == NULL) return SCPE_MEM; clim = (((t_addr) val) < MEMSIZE)? val: MEMSIZE; for (i = 0; i < clim; i = i + 2) nM[i >> 1] = M[i >> 1]; free (M); M = nM; MEMSIZE = val; if (!(sim_switches & SIM_SW_REST)) /* unless restore, */ cpu_set_bus (cpu_opt); /* alter periph config */ return SCPE_OK; } /* Bus configuration, disable Unibus or Qbus devices */ t_stat cpu_set_bus (int32 opt) { DEVICE *dptr; uint32 i, mask; if (opt & BUS_U) /* Unibus variant? */ mask = DEV_UBUS; else if (MEMSIZE <= UNIMEMSIZE) /* 18b Qbus devices? */ mask = DEV_QBUS | DEV_Q18; else mask = DEV_QBUS; /* must be 22b */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { if ((dptr->flags & DEV_DISABLE) && /* disable-able? */ !(dptr->flags & DEV_DIS) && /* enabled? */ ((dptr->flags & mask) == 0)) { /* not allowed? */ printf ("Disabling %s\n", sim_dname (dptr)); if (sim_log) fprintf (sim_log, "Disabling %s\n", sim_dname (dptr)); dptr->flags = dptr->flags | DEV_DIS; } } return SCPE_OK; } /* System reset */ t_stat sys_reset (DEVICE *dptr) { int32 i; CCR = 0; HITMISS = 0; CPUERR = 0; MEMERR = 0; if (!CPUT (CPUT_J)) MAINT = 0; MBRK = 0; WCS = 0; if (CPUT (CPUT_JB|CPUT_JE)) JCSR = JCSR_dflt; else JCSR = 0; JPCR = 0; JASR = 0; UDCR = 0; UDDR = 0; UCSR = 0; uba_last = 0; DR = 0; toy_state = 0; for (i = 0; i < UBM_LNT_LW; i++) ub_map[i] = 0; for (i = 0; i < TOY_LNT; i++) toy_data[i] = 0; return SCPE_OK; } /* Set/show JCLK default values */ t_stat sys_set_jclk_dflt (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 i; if ((CPUT (CPUT_JB|CPUT_JE)) && cptr) { for (i = 0; i < 4; i++) { if (strncmp (cptr, jcsr_val[i], strlen (cptr)) == 0) { JCSR_dflt = i << CSRJ_V_LTCSEL; return SCPE_OK; } } } return SCPE_ARG; } t_stat sys_show_jclk_dflt (FILE *st, UNIT *uptr, int32 val, void *desc) { if (CPUT (CPUT_JB|CPUT_JE)) fprintf (st, "JCLK default=%s\n", jcsr_val[CSRJ_LTCSEL (JCSR_dflt)]); else fprintf (st, "Not implemented\n"); return SCPE_OK; } simh-3.8.1/PDP11/pdp11_ta.c0000644000175000017500000005704611112126000013177 0ustar vlmvlm/* pdp11_ta.c: PDP-11 cassette tape simulator Copyright (c) 2007-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ATAION OF CONTRATA, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNETAION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ta TA11/TU60 cassette tape 06-Aug-07 RMS Foward op at BOT skips initial file gap Magnetic tapes are represented as a series of variable records of the form: 32b byte count byte 0 byte 1 : byte n-2 byte n-1 32b byte count If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a byte count of 0. Cassette format differs in one very significant way: it has file gaps rather than file marks. If the controller spaces or reads into a file gap and then reverses direction, the file gap is not seen again. This is in contrast to magnetic tapes, where the file mark is a character sequence and is seen again if direction is reversed. In addition, cassettes have an initial file gap which is automatically skipped on forward operations from beginning of tape. */ #include "pdp11_defs.h" #include "sim_tape.h" #define TA_NUMDR 2 /* #drives */ #define FNC u3 /* unit function */ #define UST u4 /* unit status */ #define TA_SIZE 93000 /* chars/tape */ #define TA_MAXFR (TA_SIZE) /* max record lnt */ /* Control/status - TACS */ #define TACS_ERR (1 << CSR_V_ERR) /* error */ #define TACS_CRC 0040000 /* CRC */ #define TACS_BEOT 0020000 /* BOT/EOT */ #define TACS_WLK 0010000 /* write lock */ #define TACS_EOF 0004000 /* end file */ #define TACS_TIM 0002000 /* timing */ #define TACS_EMP 0001000 /* empty */ #define TACS_V_UNIT 8 /* unit */ #define TACS_M_UNIT (TA_NUMDR - 1) #define TACS_UNIT (TACS_M_UNIT << TACS_V_UNIT) #define TACS_TR (1 << CSR_V_DONE) /* transfer req */ #define TACS_IE (1 << CSR_V_IE) /* interrupt enable */ #define TACS_RDY 0000040 /* ready */ #define TACS_ILBS 0000020 /* start CRC */ #define TACS_V_FNC 1 /* function */ #define TACS_M_FNC 07 #define TACS_WFG 00 #define TACS_WRITE 01 #define TACS_READ 02 #define TACS_SRF 03 #define TACS_SRB 04 #define TACS_SFF 05 #define TACS_SFB 06 #define TACS_REW 07 #define TACS_2ND 010 #define TACS_3RD 030 #define TACS_FNC (TACS_M_FNC << TACS_V_FNC) #define TACS_GO (1 << CSR_V_GO) /* go */ #define TACS_W (TACS_UNIT|TACS_IE|TACS_ILBS|TACS_FNC) #define TACS_XFRERR (TACS_ERR|TACS_CRC|TACS_WLK|TACS_EOF|TACS_TIM) #define GET_UNIT(x) (((x) >> TACS_V_UNIT) & TACS_M_UNIT) #define GET_FNC(x) (((x) >> TACS_V_FNC) & TACS_M_FNC) /* Function code flags */ #define OP_WRI 01 /* op is a write */ #define OP_REV 02 /* op is rev motion */ #define OP_FWD 04 /* op is fwd motion */ /* Unit status flags */ #define UST_REV (OP_REV) /* last op was rev */ #define UST_GAP 01 /* last op hit gap */ extern int32 int_req[IPL_HLVL]; extern FILE *sim_deb; uint32 ta_cs = 0; /* control/status */ uint32 ta_idb = 0; /* input data buf */ uint32 ta_odb = 0; /* output data buf */ uint32 ta_write = 0; /* TU60 write flag */ uint32 ta_bptr = 0; /* buf ptr */ uint32 ta_blnt = 0; /* buf length */ int32 ta_stime = 1000; /* start time */ int32 ta_ctime = 100; /* char latency */ uint32 ta_stopioe = 1; /* stop on error */ uint8 *ta_xb = NULL; /* transfer buffer */ static uint8 ta_fnc_tab[TACS_M_FNC + 1] = { OP_WRI|OP_FWD, OP_WRI|OP_FWD, OP_FWD, OP_REV, OP_REV , OP_FWD, OP_FWD, 0 }; DEVICE ta_dev; t_stat ta_rd (int32 *data, int32 PA, int32 access); t_stat ta_wr (int32 data, int32 PA, int32 access); t_stat ta_svc (UNIT *uptr); t_stat ta_reset (DEVICE *dptr); t_stat ta_attach (UNIT *uptr, char *cptr); t_stat ta_detach (UNIT *uptr); void ta_go (void); t_stat ta_map_err (UNIT *uptr, t_stat st); UNIT *ta_busy (void); void ta_set_tr (void); uint32 ta_updsta (UNIT *uptr); uint32 ta_crc (uint8 *buf, uint32 cnt); /* TA data structures ta_dev TA device descriptor ta_unit TA unit list ta_reg TA register list ta_mod TA modifier list */ DIB ta_dib = { IOBA_TA, IOLN_TA, &ta_rd, &ta_wr, 1, IVCL (TA), VEC_TA, { NULL } }; UNIT ta_unit[] = { { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, }; REG ta_reg[] = { { ORDATA (TACS, ta_cs, 16) }, { ORDATA (TAIDB, ta_idb, 8) }, { ORDATA (TAODB, ta_odb, 8) }, { FLDATA (WRITE, ta_write, 0) }, { FLDATA (INT, IREQ (TA), INT_V_TA) }, { FLDATA (ERR, ta_cs, CSR_V_ERR) }, { FLDATA (TR, ta_cs, CSR_V_DONE) }, { FLDATA (IE, ta_cs, CSR_V_IE) }, { DRDATA (BPTR, ta_bptr, 17) }, { DRDATA (BLNT, ta_blnt, 17) }, { DRDATA (STIME, ta_stime, 24), PV_LEFT + REG_NZ }, { DRDATA (CTIME, ta_ctime, 24), PV_LEFT + REG_NZ }, { FLDATA (STOP_IOE, ta_stopioe, 0) }, { URDATA (UFNC, ta_unit[0].FNC, 8, 5, 0, TA_NUMDR, 0), REG_HRO }, { URDATA (UST, ta_unit[0].UST, 8, 2, 0, TA_NUMDR, 0), REG_HRO }, { URDATA (POS, ta_unit[0].pos, 10, T_ADDR_W, 0, TA_NUMDR, PV_LEFT | REG_RO) }, { ORDATA (DEVADDR, ta_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, ta_dib.vec, 16), REG_HRO }, { NULL } }; MTAB ta_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, NULL, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, IOLN_TA, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE ta_dev = { "TA", ta_unit, ta_reg, ta_mod, TA_NUMDR, 10, 31, 1, 8, 8, NULL, NULL, &ta_reset, NULL, &ta_attach, &ta_detach, &ta_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG }; /* I/O dispatch routines, I/O addresses 17777500 - 17777503 17777500 TACS read/write 17777502 TADB read/write */ t_stat ta_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* TACSR */ *data = ta_updsta (NULL); /* update status */ break; case 1: /* TADB */ *data = ta_idb; /* return byte */ ta_cs &= ~TACS_TR; /* clear tra req */ ta_updsta (NULL); break; } return SCPE_OK; } t_stat ta_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* TACS */ if (access == WRITEB) data = (PA & 1)? /* byte write? */ (ta_cs & 0377) | (data << 8): /* merge old */ (ta_cs & ~0377) | data; ta_cs = (ta_cs & ~TACS_W) | (data & TACS_W); /* merge new */ if ((data & CSR_GO) && !ta_busy ()) /* go, not busy? */ ta_go (); /* start operation */ if (ta_cs & TACS_ILBS) /* ILBS inhibits TR */ ta_cs &= ~TACS_TR; break; case 1: /* TADB */ if (PA & 1) /* ignore odd byte */ break; ta_odb = data; /* return byte */ ta_cs &= ~TACS_TR; /* clear tra req */ break; } /* end switch */ ta_updsta (NULL); /* update status */ return SCPE_OK; } /* Start a new operation - cassette is not busy */ void ta_go (void) { UNIT *uptr = ta_dev.units + GET_UNIT (ta_cs); uint32 fnc = GET_FNC (ta_cs); uint32 flg = ta_fnc_tab[fnc]; uint32 old_ust = uptr->UST; if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, ">>TA start: op=%o, old_sta = %o, pos=%d\n", fnc, uptr->UST, uptr->pos); ta_cs &= ~(TACS_XFRERR|TACS_EMP|TACS_TR|TACS_RDY); /* clr err, tr, rdy */ ta_bptr = 0; /* init buffer */ ta_blnt = 0; if ((uptr->flags & UNIT_ATT) == 0) { ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; return; } if (flg & OP_WRI) { /* write op? */ if (sim_tape_wrp (uptr)) { /* locked? */ ta_cs |= TACS_ERR|TACS_WLK|TACS_RDY; /* don't start */ return; } ta_odb = 0; ta_write = 1; } else { ta_idb = 0; ta_write = 0; } ta_cs &= ~TACS_BEOT; /* tape in motion */ uptr->FNC = fnc; /* save function */ if ((fnc != TACS_REW) && !(flg & OP_WRI)) { /* spc/read cmd? */ t_mtrlnt t; t_stat st; uptr->UST = flg & UST_REV; /* save direction */ if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ st = sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); /* skip file gap */ if (st != MTSE_TMK) /* not there? */ sim_tape_rewind (uptr); /* restore tap pos */ else old_ust = 0; /* defang next */ } if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* reverse in gap? */ if (uptr->UST) /* skip file gap */ sim_tape_rdrecr (uptr, ta_xb, &t, TA_MAXFR); else sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, ">>TA skip gap: op=%o, old_sta = %o, pos=%d\n", fnc, uptr->UST, uptr->pos); } } else uptr->UST = 0; sim_activate (uptr, ta_stime); /* schedule op */ return; } /* Unit service */ t_stat ta_svc (UNIT *uptr) { uint32 i, crc; uint32 flg = ta_fnc_tab[uptr->FNC & TACS_M_FNC]; t_mtrlnt tbc; t_stat st, r; if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; ta_updsta (uptr); /* update status */ return (ta_stopioe? SCPE_UNATT: SCPE_OK); } if (((flg & OP_FWD) && sim_tape_eot (uptr)) || /* illegal motion? */ ((flg & OP_REV) && sim_tape_bot (uptr))) { ta_cs |= TACS_ERR|TACS_BEOT|TACS_RDY; /* error */ ta_updsta (uptr); return SCPE_OK; } r = SCPE_OK; switch (uptr->FNC) { /* case on function */ case TACS_READ: /* read start */ st = sim_tape_rdrecf (uptr, ta_xb, &ta_blnt, TA_MAXFR); /* get rec */ if (st == MTSE_RECE) /* rec in err? */ ta_cs |= TACS_ERR|TACS_CRC; else if (st != MTSE_OK) { /* other error? */ r = ta_map_err (uptr, st); /* map error */ break; } crc = ta_crc (ta_xb, ta_blnt); /* calculate CRC */ ta_xb[ta_blnt++] = (crc >> 8) & 0377; /* append to buffer */ ta_xb[ta_blnt++] = crc & 0377; uptr->FNC |= TACS_2ND; /* next state */ sim_activate (uptr, ta_ctime); /* sched next char */ return SCPE_OK; case TACS_READ|TACS_2ND: /* read char */ if (ta_bptr < ta_blnt) /* more chars? */ ta_idb = ta_xb[ta_bptr++]; else { /* no */ ta_idb = 0; ta_cs |= TACS_ERR|TACS_CRC; /* overrun */ break; /* tape stops */ } if (ta_cs & TACS_ILBS) { /* CRC seq? */ uptr->FNC |= TACS_3RD; /* next state */ sim_activate (uptr, ta_stime); /* sched CRC chk */ } else { ta_set_tr (); /* set tra req */ sim_activate (uptr, ta_ctime); /* sched next char */ } return SCPE_OK; case TACS_READ|TACS_3RD: /* second read CRC */ if (ta_bptr != ta_blnt) { /* partial read? */ crc = ta_crc (ta_xb, ta_bptr + 2); /* actual CRC */ if (crc != 0) /* must be zero */ ta_cs |= TACS_ERR|TACS_CRC; } break; /* read done */ case TACS_WRITE: /* write start */ for (i = 0; i < TA_MAXFR; i++) /* clear buffer */ ta_xb[i] = 0; ta_set_tr (); /* set tra req */ uptr->FNC |= TACS_2ND; /* next state */ sim_activate (uptr, ta_ctime); /* sched next char */ return SCPE_OK; case TACS_WRITE|TACS_2ND: /* write char */ if (ta_cs & TACS_ILBS) { /* CRC seq? */ uptr->FNC |= TACS_3RD; /* next state */ sim_activate (uptr, ta_stime); /* sched wri done */ } else { if ((ta_bptr < TA_MAXFR) && /* room in buf? */ ((uptr->pos + ta_bptr) < uptr->capac)) /* room on tape? */ ta_xb[ta_bptr++] = ta_odb; /* store char */ ta_set_tr (); /* set tra req */ sim_activate (uptr, ta_ctime); /* sched next char */ } return SCPE_OK; case TACS_WRITE|TACS_3RD: /* write CRC */ if (ta_bptr) { /* anything to write? */ if (st = sim_tape_wrrecf (uptr, ta_xb, ta_bptr)) /* write, err? */ r = ta_map_err (uptr, st); /* map error */ } break; /* op done */ case TACS_WFG: /* write file gap */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = ta_map_err (uptr, st); /* map error */ break; case TACS_REW: /* rewind */ sim_tape_rewind (uptr); ta_cs |= TACS_BEOT; /* bot, no error */ break; case TACS_SRB: /* space rev blk */ if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ r = ta_map_err (uptr, st); /* map error */ break; case TACS_SRF: /* space rev file */ while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; if (st == MTSE_TMK) /* if tape mark, */ ta_cs |= TACS_EOF; /* set EOF, no err */ else r = ta_map_err (uptr, st); /* else map error */ break; case TACS_SFB: /* space fwd blk */ if (st = sim_tape_sprecf (uptr, &tbc)) /* space rev, err? */ r = ta_map_err (uptr, st); /* map error */ ta_cs |= TACS_CRC; /* CRC sets, no err */ break; case TACS_SFF: /* space fwd file */ while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; if (st == MTSE_TMK) /* if tape mark, */ ta_cs |= TACS_EOF; /* set EOF, no err */ else r = ta_map_err (uptr, st); /* else map error */ break; default: /* never get here! */ return SCPE_IERR; } /* end case */ ta_cs |= TACS_RDY; /* set ready */ ta_updsta (uptr); /* update status */ if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, ">>TA done: op=%o, status = %o, pos=%d\n", uptr->FNC, ta_cs, uptr->pos); return r; } /* Update controller status */ uint32 ta_updsta (UNIT *uptr) { if (uptr == NULL) { /* unit specified? */ if ((uptr = ta_busy ()) == NULL) /* use busy */ uptr = ta_dev.units + GET_UNIT (ta_cs); /* use sel unit */ } else if (ta_cs & TACS_EOF) /* save EOF */ uptr->UST |= UST_GAP; if (uptr->flags & UNIT_ATT) /* attached? */ ta_cs &= ~TACS_EMP; else ta_cs |= TACS_EMP|TACS_RDY; /* no, empty, ready */ if ((ta_cs & TACS_IE) && /* int enabled? */ (ta_cs & (TACS_TR|TACS_RDY))) /* req or ready? */ SET_INT (TA); /* set int req */ else CLR_INT (TA); /* no, clr int req */ return ta_cs; } /* Set transfer request */ void ta_set_tr (void) { if (ta_cs & TACS_TR) /* flag still set? */ ta_cs |= (TACS_ERR|TACS_TIM); else ta_cs |= TACS_TR; /* set xfr req */ if (ta_cs & TACS_IE) /* if ie, int req */ SET_INT (TA); return; } /* Test if controller busy */ UNIT *ta_busy (void) { uint32 u; UNIT *uptr; for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ uptr = ta_dev.units + u; if (sim_is_active (uptr)) return uptr; } return NULL; } /* Calculate CRC on buffer */ uint32 ta_crc (uint8 *buf, uint32 cnt) { uint32 crc, i, j; crc = 0; for (i = 0; i < cnt; i++) { crc = crc ^ (((uint32) buf[i]) << 8); for (j = 0; j < 8; j++) { if (crc & 1) crc = (crc >> 1) ^ 0xA001; else crc = crc >> 1; } } return crc; } /* Map error status */ t_stat ta_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* unattached */ ta_cs |= TACS_ERR|TACS_CRC; case MTSE_OK: /* no error */ return SCPE_IERR; /* never get here! */ case MTSE_TMK: /* end of file */ ta_cs |= TACS_ERR|TACS_EOF; break; case MTSE_IOERR: /* IO error */ ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ if (ta_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ case MTSE_EOM: /* end of medium */ ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ break; case MTSE_BOT: /* reverse into BOT */ ta_cs |= TACS_ERR|TACS_BEOT; /* set bot */ break; case MTSE_WRP: /* write protect */ ta_cs |= TACS_ERR|TACS_WLK; /* set wlk err */ break; } return SCPE_OK; } /* Reset routine */ t_stat ta_reset (DEVICE *dptr) { uint32 u; UNIT *uptr; ta_cs = 0; ta_idb = 0; ta_odb = 0; ta_write = 0; ta_bptr = 0; ta_blnt = 0; CLR_INT (TA); /* clear interrupt */ for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ uptr = ta_dev.units + u; sim_cancel (uptr); /* cancel activity */ sim_tape_reset (uptr); /* reset tape */ } if (ta_xb == NULL) ta_xb = (uint8 *) calloc (TA_MAXFR + 2, sizeof (uint8)); if (ta_xb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat ta_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; ta_updsta (NULL); uptr->UST = 0; return r; } /* Detach routine */ t_stat ta_detach (UNIT* uptr) { t_stat r; if (!(uptr->flags & UNIT_ATT)) /* check attached */ return SCPE_OK; r = sim_tape_detach (uptr); ta_updsta (NULL); uptr->UST = 0; return r; } simh-3.8.1/PDP11/pdp11_cr.c0000644000175000017500000013627711143602146013221 0ustar vlmvlm/* pdp11_cr.c: CR/CM/CD-11 card reader simulator Copyright (c) 2005-2007, John A. Dundas III Portions derived from work by Douglas W. Jones, jones@cs.uiowa.edu Portions derived from work by 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 THE AUTHOR 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 Author. ------------------------------------------------------------------------------ cr CR11/CD11 punched and mark sense card reader for SIMH The CR11 controller is also compatible with the CM11-F, CME11, and CMS11. Information necessary to create this simulation was gathered from a number of sources including: CR11 Card Reader System Manual, DEC-11-HCRB-D http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HCRB-D_CR11_Mar72.pdf Various editions of the Peripherals Handbook OpenVMS VAX Card Reader, Line Printer, and LPA11-K I/O User's Reference Manual, AA-PVXGA-TE http://h71000.www7.hp.com/DOC/73final/documentation/pdf/OVMS_VAX_CARD_LP_REF.pdf OpenVMS System Manager's Manual, Volume 1: Essentials http://h71000.www7.hp.com/DOC/732FINAL/aa-pv5mh-tk/aa-pv5mh-tk.PDF CRDRIVER.LIS - CR11 Card Reader Driver, X-9, graciously made available by HP Various RSTS manuals RT-11 Software Support Manual RT-11 System Reference Manual, DEC-11-ORUGA-C-D Professor Douglas W. Jones's web site: http://www.cs.uiowa.edu/~jones/cards/ Paul Mattes' x026 keypunch simulator http://x3270.bgp.nu/x026.html CD2SER.MAC - TOPS card reader driver source http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cd2ser.mac The Card Image format code and documentation is adapted from Prof. Jones's site, with his permission. Please see his site for additional documentation as well as the card image utilities referenced in his documentation (cardmake, cardlist, etc.). http://www.cs.uiowa.edu/~jones/cards/format.html Known limitations: 1. Need a copy of the CR bootstrap (and some way to test it) 2. Need a copy of the XXDP+ test deck 3. No testing under RSX; volunteers needed 4. No testing under Ultrix or Unix for PDP-11; volunteers needed 5. No testing under Ultrix or Unix for VAX; volunteers needed 6. The simulator implements a single controller/reader combination Operating System Notes RT-11 (and CTS-300) support one CR11 or CM11, but no CD11. VMS supports multiple CR11 controllers, but no CD11. RSTS/E supports either the CR11/CM11 or CD11 but not both in the same SIL. It appears to support only one unit. For RSX there exists a CR/CM task handler. Is there a CD handler? Don't have any information about Unix or Ultrix-11 yet. Same for VAX Unices. TOPS: only the CD11 is supported, under the name CD20. Revision History: 01-Feb-07 RMS Added PDP-10 support 12-May-06 JAD Modify the DEBUG code to use the SIMH DEBUG_x macros. Modify the UNIT structure to include the DEBUG bit. Mark the trans[] array contents constant. Make device data structures static and constant as appropriate. 18-Mar-05 JAD Slight optimization for blank punches recognizing that blank is 0 in all character encodings. 17-Mar-05 JAD Completely initialize ascii_code correctly. Define the end of deck punch code separately from the cardcode.i file. Make initTranslation() set a pointer to the correct punch code table to use. Modify card read functions to use this table pointer. 16-Mar-05 JAD Make certain switches passed to the ATTACH command are valid; return error on any others. Make default unit wait time compatible with default device specification. Implement SET TRANSLATION=value. Still need to modify the H2ASCII table used for text files; currently hard-coded to 029. 24-Feb-05 JAD Allow the maintenance bits in CRM to clear as well as set status bits. Not sure this is the correct behavior, though, without more documentation. Catch three more places to spin down the blower correctly. Zero the CDDB and CRM at INIT. 17-Feb-05 JAD When the hopper empties, a pick check should be generated 300ms later. They are simultaneous for now. Make sure readColumnBinary() generates a complete EOF card. 08-Feb-05 JAD Replace blowerWait with different times for blower spin up and down. 06-Feb-05 JAD After DETACH: mark CD offline, set appropriate blower state. Make sure unit wait time is recalculated every time cpm is set. 04-Feb-05 JAD Better tracking of blower state throughout driver. Make sure IE gets cleared for CR at INIT. Normalize error response in read routines. Finish condition handling for column binary. 02-Feb-05 JAD Remove Qbus support; Unibus only. Support ATTACH switches: A - ASCII, B - column binary, I - Card Image If none given, check for .TXT or .CBN; if none, examine file for magic header. Finer granularity to blower state. Expose this variable to examine/deposit from SIMH. Preliminary implementation of support for column binary format. 24-Jan-05 JAD Make AUTOEOF work as a surrogate for the EOF button of a CD11 reader. May need to separate this later, though. Partial implementation of DATAERR for CD11. Implement the Rev. J mods (as best I understand them) to the CD11 affecting the CDDB used as a second status register. 23-Jan-05 JAD Preliminary clean-up of CD state transitions. Tested with RSTS/E (V9.1-05). 22-Jan-05 JAD Finish CR state transitions; should be close now. Tested with RSTS/E (V9.1-05), RT-11 (V5.3), and VAX/VMS (V7.2). 19-Jan-05 JAD Add bounds to the RATE command; also default and help a la the XQ driver. Improved handling of empty files. 17-Jan-05 JAD Add the CR maintenance register. 16-Jan-05 JAD Add preliminary CD11 support. Simulate the STOP and RESET switches. 14-Jan-05 JAD Add the ability to automatically generate an 'EOF' card recognized by DEC operating systems when reading ASCII files. 08-Jan-05 JAD Original creation and testing */ #if defined (VM_PDP10) /* PDP10 version */ #include "pdp10_defs.h" extern int32 int_req; #define DFLT_DIS (DEV_DIS) #define DFLT_CR11 (0) /* CD11 only */ #define DFLT_CPM 1000 #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" extern int32 int_req[IPL_HLVL]; #define DFLT_DIS (0) #define DFLT_CR11 (UNIT_CR11) #define DFLT_CPM 285 #else /* PDP-11 version */ #include "pdp11_defs.h" extern int32 int_req[IPL_HLVL]; #define DFLT_DIS (0) #define DFLT_CR11 (UNIT_CR11) #define DFLT_CPM 285 #endif extern FILE *sim_deb; /* sim_console.c */ /* create a int32 constant from four characters */ #define I4C(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) #define I4C_CBN I4C ('C','B','N',' ') #define I4C_H80 I4C ('H','8','0',' ') #define I4C_H82 I4C ('H','8','2',' ') #define I4C_H40 I4C ('H','4','0',' ') #define UNIT_V_CR11 (UNIT_V_UF + 0) #define UNIT_CR11 (1u << UNIT_V_CR11) #define UNIT_V_AUTOEOF (UNIT_V_UF + 1) #define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF) #include #define ERROR (00404) #include "pdp11_cr_dat.h" #define PUNCH_EOD (07417) #define PUNCH_SPACE (0) /* same for all encodings */ /* CR */ /* also use CSR_ERR, CSR_IE, and CSR_GO */ #define CRCSR_V_CRDDONE 14 /* card done */ #define CRCSR_V_SUPPLY 13 /* supply error */ #define CRCSR_V_RDCHK 12 /* reader check */ #define CRCSR_V_TIMERR 11 /* timing error */ #define CRCSR_V_ONLINE 10 /* on line */ #define CRCSR_V_BUSY 9 /* busy reading */ #define CRCSR_V_OFFLINE 8 /* off line AKA READY? */ #define CRCSR_V_COLRDY 7 /* column ready */ #define CRCSR_V_EJECT 1 /* ignore card */ #define CRCSR_CRDDONE (1u << CRCSR_V_CRDDONE) #define CRCSR_SUPPLY (1u << CRCSR_V_SUPPLY) #define CRCSR_RDCHK (1u << CRCSR_V_RDCHK) #define CRCSR_TIMERR (1u << CRCSR_V_TIMERR) #define CRCSR_ONLINE (1u << CRCSR_V_ONLINE) #define CRCSR_BUSY (1u << CRCSR_V_BUSY) #define CRCSR_OFFLINE (1u << CRCSR_V_OFFLINE) #define CRCSR_COLRDY (1u << CRCSR_V_COLRDY) #define CRCSR_EJECT (1u << CRCSR_V_EJECT) #define CRCSR_IMP (CSR_ERR | CRCSR_CRDDONE | CRCSR_SUPPLY | \ CRCSR_RDCHK | CRCSR_TIMERR | CRCSR_ONLINE | \ CRCSR_BUSY | CRCSR_OFFLINE | CRCSR_COLRDY | \ CSR_IE | CRCSR_EJECT) #define CRCSR_RW (CSR_IE | CRCSR_EJECT | CSR_GO) /* read/write */ #define CRM_V_MAINT 15 /* enable maint funct */ #define CRM_V_BUSY 14 #define CRM_V_READY 13 #define CRM_V_HOPPER 12 #define CRM_MAINT (1u << CRM_V_MAINT) #define CRM_BUSY (1u << CRM_V_BUSY) #define CRM_READY (1u << CRM_V_READY) #define CRM_HOPPER (1u << CRM_V_HOPPER) /* CD */ /* also use CSR_ERR, CSR_IE, and CSR_GO */ #define CDCSR_V_RDRCHK 14 /* reader check */ #define CDCSR_V_EOF 13 /* CD11-E EOF button */ #define CDCSR_V_OFFLINE 12 /* off line */ #define CDCSR_V_DATAERR 11 /* data error */ #define CDCSR_V_LATE 10 /* data late */ #define CDCSR_V_NXM 9 /* non-existent memory */ #define CDCSR_V_PWRCLR 8 /* power clear */ #define CDCSR_V_RDY 7 /* ready */ #define CDCSR_V_XBA17 5 #define CDCSR_V_XBA16 4 #define CDCSR_V_ONLINE 3 /* on line transition */ #define CDCSR_V_HOPPER 2 /* hopper check */ #define CDCSR_V_PACK 1 /* data packing */ #define CDCSR_RDRCHK (1u << CDCSR_V_RDRCHK) #define CDCSR_EOF (1u << CDCSR_V_EOF) #define CDCSR_OFFLINE (1u << CDCSR_V_OFFLINE) #define CDCSR_DATAERR (1u << CDCSR_V_DATAERR) #define CDCSR_LATE (1u << CDCSR_V_LATE) #define CDCSR_NXM (1u << CDCSR_V_NXM) #define CDCSR_PWRCLR (1u << CDCSR_V_PWRCLR) #define CDCSR_RDY (1u << CDCSR_V_RDY) #define CDCSR_XBA17 (1u << CDCSR_V_XBA17) #define CDCSR_XBA16 (1u << CDCSR_V_XBA16) #define CDCSR_ONLINE (1u << CDCSR_V_ONLINE) #define CDCSR_HOPPER (1u << CDCSR_V_HOPPER) #define CDCSR_PACK (1u << CDCSR_V_PACK) #define CDCSR_IMP (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | \ CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | \ CDCSR_PWRCLR | CDCSR_RDY | CSR_IE | \ CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | \ CDCSR_HOPPER | CDCSR_PACK | CSR_GO) #define CDCSR_RW (CDCSR_PWRCLR | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | \ CDCSR_PACK | CSR_GO) /* Blower state values */ #define BLOW_OFF (0) /* steady state off */ #define BLOW_START (1) /* starting up */ #define BLOW_ON (2) /* steady state on */ #define BLOW_STOP (3) /* shutting down */ /* Card Reader state */ static char *cardFormat = "unknown"; static t_bool (*readRtn)(FILE *, int16 *, char *, char *); static char ascii_code[4096]; /* 2^12 possible values */ static int currCol; /* current column when reading */ static int colStart; /* starting column */ static int colEnd; /* ending column */ static int table = 3; /* character translation table */ static const int *codeTbl = o29_code; /* punch translation table */ static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */ static int32 spinUp = 3000; /* blower spin-up time: 3 seconds */ static int32 spinDown = 2000; /* blower spin-down time: 2 seconds */ static t_bool EOFcard = FALSE; /* played special card yet? */ static int32 cpm = DFLT_CPM; /* reader rate: cards per minute */ /* card image in various formats */ static int16 hcard[82]; /* Hollerith format */ static char ccard[82]; /* DEC compressed format */ static char acard[82]; /* ASCII format */ /* CR/CM registers */ static int32 crs = 0; /* control/status */ static int32 crb1 = 0; /* 12-bit Hollerith characters */ static int32 crb2 = 0; /* 8-bit compressed characters */ static int32 crm = 0; /* CMS maintenance register */ /* CD registers */ static int32 cdst = 0; /* control/status */ static int32 cdcc = 0; /* column count */ static int32 cdba = 0; /* current address, low 16 bits */ static int32 cddb = 0; /* data, 2nd status */ /* forward references */ DEVICE cr_dev; static void setupCardFile (UNIT *, int32); t_stat cr_rd (int32 *, int32, int32); t_stat cr_wr (int32, int32, int32); t_stat cr_svc (UNIT *); t_stat cr_reset (DEVICE *); t_stat cr_attach (UNIT *, char *); t_stat cr_detach (UNIT *); t_stat cr_set_type (UNIT *, int32, char *, void *); t_stat cr_show_format (FILE *, UNIT *, int32, void *); t_stat cr_set_rate (UNIT *, int32, char *, void *); t_stat cr_show_rate (FILE *, UNIT *, int32, void *); t_stat cr_set_reset (UNIT *, int32, char *, void *); t_stat cr_set_stop (UNIT *, int32, char *, void *); t_stat cr_set_trans (UNIT *, int32, char*, void *); t_stat cr_show_trans (FILE *, UNIT *, int32, void *); /* CR data structures cr_dib CR device information block cr_unit CR unit descriptor cr_reg CR register list cr_mod CR modifier table cr_dev CR device descriptor */ static DIB cr_dib = { IOBA_CR, IOLN_CR, &cr_rd, &cr_wr, 1, IVCL (CR), VEC_CR, { NULL } }; static UNIT cr_unit = { UDATA (&cr_svc, UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+ DFLT_CR11+UNIT_AUTOEOF, 0), (60 * 1000) / DFLT_CPM }; static const REG cr_reg[] = { { GRDATA (BUF, cr_unit.buf, DEV_RDX, 8, 0) }, { GRDATA (CRS, crs, DEV_RDX, 16, 0) }, { GRDATA (CRB1, crb1, DEV_RDX, 16, 0) }, { GRDATA (CRB2, crb2, DEV_RDX, 16, 0) }, { GRDATA (CRM, crm, DEV_RDX, 16, 0) }, { GRDATA (CDST, cdst, DEV_RDX, 16, 0) }, { GRDATA (CDCC, cdcc, DEV_RDX, 16, 0) }, { GRDATA (CDBA, cdba, DEV_RDX, 16, 0) }, { GRDATA (CDDB, cddb, DEV_RDX, 16, 0) }, { GRDATA (BLOWER, blowerState, DEV_RDX, 2, 0) }, { FLDATA (INT, IREQ (CR), INT_V_CR) }, { FLDATA (ERR, crs, CSR_V_ERR) }, { FLDATA (IE, crs, CSR_V_IE) }, { DRDATA (POS, cr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, cr_unit.wait, 24), PV_LEFT }, { GRDATA (DEVADDR, cr_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, cr_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; static const MTAB cr_mod[] = { #if defined (VM_PDP11) { UNIT_CR11, UNIT_CR11, "CR11", "CR11", &cr_set_type }, { UNIT_CR11, 0, "CD11", "CD11", &cr_set_type }, #else { UNIT_CR11, UNIT_CR11, "CR11", NULL }, { UNIT_CR11, 0, "CD11", NULL }, #endif { UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF", NULL }, { UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF", NULL }, /* card reader RESET switch */ { MTAB_XTD|MTAB_VDV, 0, NULL, "RESET", &cr_set_reset, NULL, NULL }, /* card reader STOP switch */ { MTAB_XTD|MTAB_VDV, 0, NULL, "STOP", &cr_set_stop, NULL, NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL, NULL, &cr_show_format, NULL }, { MTAB_XTD|MTAB_VDV, 006, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}", &cr_set_rate, &cr_show_rate, NULL }, { MTAB_XTD|MTAB_VDV, 0, "TRANSLATION", "TRANSLATION={DEFAULT|026|026FTN|029|EBCDIC}", &cr_set_trans, &cr_show_trans, NULL }, { 0 } }; DEVICE cr_dev = { "CR", &cr_unit, (REG *) &cr_reg, (MTAB *) &cr_mod, 1, 10, 31, 1, DEV_RDX, 8, NULL, NULL, &cr_reset, NULL, &cr_attach, &cr_detach, &cr_dib, DEV_DISABLE | DFLT_DIS | DEV_UBUS | DEV_DEBUG }; /* Utility routines */ /* These functions read a "card" from a virtual deck file (passed in fp) and fill in three arrays. The first array 'hcard' contains the 12-bit binary image of the punch in each column; the second array 'ccard' contains the 8-bit DEC encoded representation of the corresponding column; the third array 'acard' contains the ASCII representation (if possible) of the character. The routines return TRUE if a card was read (possibly with errors) and FALSE if the "hopper is empty" (EOF) or fatal file errors prevented any portion of a card from being read. Errors other than EOF are signaled out of band in the controller state variables. Possible errors are data in columns 0 or 81 (signalled as read check; currently these columns are ignored), or any file errors (signalled as motion check). Might rethink this. Should probably treat file errors as "pick check". Retry 3 times. After that, give up with error. */ static t_bool readCardImage ( FILE *fp, int16 *hcard, char *ccard, char *acard ) { int c1, c2, c3, col; if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "readCardImage pos %d\n", (int) ftell (fp)); /* get card header bytes */ c1 = fgetc (fp); c2 = fgetc (fp); c3 = fgetc (fp); cr_unit.pos = ftell (fp); /* check for EOF */ if (c1 == EOF) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "hopper empty\n"); if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { EOFcard = TRUE; for (col = 1; col <= 8; col++) { hcard[col] = PUNCH_EOD; ccard[col] = h2c_code[PUNCH_EOD]; acard[col] = ' '; } while (col <= colEnd) { hcard[col] = PUNCH_SPACE; ccard[col] = PUNCH_SPACE; acard[col] = ' '; col++; } return (TRUE); } crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; if (cr_unit.flags & UNIT_AUTOEOF) cdst |= CDCSR_EOF; blowerState = BLOW_STOP; return (FALSE); } /* check for valid header */ if ((c2 == EOF) || (c3 == EOF) || ((c1 & 0x80) == 0) || ((c2 & 0x80) == 0) || ((c3 & 0x80) == 0)) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "header error\n"); /* unexpected EOF or format problems */ crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; crs &= ~CRCSR_ONLINE; cdst |= CSR_ERR | CDCSR_RDRCHK; blowerState = BLOW_STOP; return (FALSE); } assert (colStart < colEnd); assert (colStart >= 0); assert (colEnd <= 81); for (col = colStart; col < colEnd; ) { int16 i; /* get 3 bytes */ c1 = fgetc (fp); c2 = fgetc (fp); c3 = fgetc (fp); cr_unit.pos = ftell (fp); if (ferror (fp) || feof (fp)) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "file error\n"); /* signal error; unexpected EOF, format problems, or file error(s) */ crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; crs &= ~CRCSR_ONLINE; cdst |= CSR_ERR | CDCSR_RDRCHK; blowerState = BLOW_STOP; return (FALSE); } /* convert to 2 columns */ i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF; hcard[col] = i; ccard[col] = h2c_code[i]; acard[col] = ascii_code[i]; col++; i = (((c2 & 017) << 8) | c3) & 0xFFF; hcard[col] = i; ccard[col] = h2c_code[i]; acard[col] = ascii_code[i]; col++; } if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "successfully loaded card\n"); return (TRUE); } static t_bool readColumnBinary ( FILE *fp, int16 *hcard, char *ccard, char *acard ) { int col; for (col = colStart; col <= colEnd; col++) { int16 i; i = fgetc (fp) & 077; i |= ((fgetc (fp) & 077) << 6); cr_unit.pos = ftell (fp); if (feof (fp)) { if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { EOFcard = TRUE; for (col = 1; col <= 8; col++) { hcard[col] = PUNCH_EOD; ccard[col] = h2c_code[PUNCH_EOD]; acard[col] = ' '; } while (col <= colEnd) { hcard[col] = PUNCH_SPACE; ccard[col] = PUNCH_SPACE; acard[col] = ' '; col++; } return (TRUE); } crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; if (cr_unit.flags & UNIT_AUTOEOF) cdst |= CDCSR_EOF; blowerState = BLOW_STOP; return (FALSE); } if (ferror (fp)) { /* signal error */ crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; crs &= ~CRCSR_ONLINE; cdst |= CSR_ERR | CDCSR_RDRCHK; blowerState = BLOW_STOP; return (FALSE); } hcard[col] = i; ccard[col] = h2c_code[i]; acard[col] = ascii_code[i]; } return (TRUE); } /* Should this routine perform special handling of non-printable, (e.g., control) characters or characters that have no encoded representation? */ static t_bool readCardASCII ( FILE *fp, int16 *hcard, char *ccard, char *acard ) { int c, col; assert (colStart < colEnd); assert (colStart >= 1); assert (colEnd <= 80); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "readCardASCII\n"); for (col = colStart; col <= colEnd; ) { switch (c = fgetc (fp)) { case EOF: if (ferror (fp)) { /* signal error */ crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; crs &= ~CRCSR_ONLINE; cdst |= CSR_ERR | CDCSR_RDRCHK; blowerState = BLOW_STOP; cr_unit.pos = ftell (fp); return (FALSE); } if (col == colStart) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "hopper empty\n"); if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { EOFcard = TRUE; for (col = 1; col <= 8; col++) { hcard[col] = PUNCH_EOD; ccard[col] = h2c_code[PUNCH_EOD]; acard[col] = ' '; } c = '\n'; goto fill_card; } crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; if (cr_unit.flags & UNIT_AUTOEOF) cdst |= CDCSR_EOF; blowerState = BLOW_STOP; cr_unit.pos = ftell (fp); return (FALSE); } /* fall through */ case '\r': case '\n': fill_card: while (col <= colEnd) { hcard[col] = PUNCH_SPACE; ccard[col] = PUNCH_SPACE; acard[col] = ' '; col++; } break; case '\t': do { hcard[col] = PUNCH_SPACE; ccard[col] = PUNCH_SPACE; acard[col] = ' '; col++; } while (((col & 07) != 1) && (col <= colEnd)); break; default: hcard[col] = codeTbl[c & 0177]; /* check for unrepresentable ASCII characters */ if (hcard[col] == ERROR) { cdst |= CDCSR_DATAERR; if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "error character at column %d\n", col); } ccard[col] = h2c_code[hcard[col]]; acard[col] = c; col++; break; } } /* silently truncate/flush long lines, or flag over-length card? */ if (c != '\n') { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "truncating card\n"); do c = fgetc (fp); while ((c != EOF) && (c != '\n') && (c != '\r')); } if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "successfully loaded card\n"); cr_unit.pos = ftell (fp); return (TRUE); } /* Initialize the binary translation table. Generally called when a new deck is attached but could be set manually as well. */ static void initTranslation (void) { int32 i; memset (ascii_code, '~', sizeof (ascii_code)); switch (table) { case 1: codeTbl = o26_comm_code; for (i = ' '; i < '`'; i++) ascii_code[o26_comm_code[i]] = i; break; case 2: codeTbl = o26_ftn_code; for (i = ' '; i < '`'; i++) ascii_code[o26_ftn_code[i]] = i; break; case 3: codeTbl = o29_code; for (i = ' '; i < '`'; i++) ascii_code[o29_code[i]] = i; break; case 4: codeTbl = EBCDIC_code; for (i = 0; i < 0177; i++) ascii_code[EBCDIC_code[i]] = i; break; default: /* can't happen */ if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "bad CR translation initialization value\n"); break; } } /* Examine the command switches, file extension, and virtual card deck file to determine the format. Set up the global variables appropriately. Rewind ASCII files to the beginning */ static void setupCardFile ( UNIT *uptr, int32 switches ) { int32 i; if (switches & SWMASK ('A')) i = 0; else if (switches & SWMASK ('B')) i = I4C_CBN; else if (switches & SWMASK ('I')) goto read_header; else if (match_ext (uptr->filename, "TXT")) i = 0; else if (match_ext (uptr->filename, "CBN")) i = I4C_CBN; else { read_header: /* look for card image magic file number */ i = fgetc (uptr->fileref); i = (i << 8) | fgetc (uptr->fileref); i = (i << 8) | fgetc (uptr->fileref); i = (i << 8) | ' '; } switch (i) { case I4C_H80: colStart = 1; colEnd = 80; cardFormat = "card image"; readRtn = readCardImage; break; case I4C_H82: colStart = 0; colEnd = 81; cardFormat = "card image"; readRtn = readCardImage; break; case I4C_H40: colStart = 1; colEnd = 40; cardFormat = "card image"; readRtn = readCardImage; break; case I4C_CBN: colStart = 1; colEnd = 80; cardFormat = "column binary"; readRtn = readColumnBinary; break; default: colStart = 1; colEnd = 80; cardFormat = "ASCII"; readRtn = readCardASCII; fseek (uptr->fileref, 0L, SEEK_SET); break; } initTranslation (); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "colStart = %d, colEnd = %d\n", colStart, colEnd); cr_unit.pos = ftell (uptr->fileref); } /* Card reader routines cr_rd I/O page read cr_wr I/O page write cr_svc process event (reader ready) cr_reset process reset cr_attach process attach cr_detach process detach */ t_stat cr_rd ( int32 *data, int32 PA, int32 access ) { switch ((PA >> 1) & 03) { case 0: /* CSR */ if (cdst & (077000)) cdst |= CSR_ERR; else cdst &= ~CSR_ERR; *data = (cr_unit.flags & UNIT_CR11) ? crs & CRCSR_IMP : cdst & CDCSR_IMP; /* CR: if error removed, clear 15, 14, 11, 10 */ if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_rd crs %06o cdst %06o\n", crs, cdst); break; case 1: *data = (cr_unit.flags & UNIT_CR11) ? crb1 : cdcc; /* Does crb1 clear after read? Implied by VMS driver. */ crb1 = 0; crs &= ~CRCSR_COLRDY; if (DEBUG_PRS (cr_dev)) { if (cr_unit.flags & UNIT_CR11) fprintf (sim_deb, "cr_rd crb1 %06o '%c' %d\n", crb1, cr_unit.buf, cr_unit.buf); else fprintf (sim_deb, "cr_rd cdcc %06o\n", cdcc); } break; case 2: *data = (cr_unit.flags & UNIT_CR11) ? crb2 : cdba; crb2 = 0; /* see note for crb1 */ crs &= ~CRCSR_COLRDY; if (DEBUG_PRS (cr_dev)) { if (cr_unit.flags & UNIT_CR11) fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2); else fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba); } break; case 3: default: if (cr_unit.flags & UNIT_CR11) *data = crm; else *data = 0100000 | (cdst & CDCSR_RDRCHK) | (cdst & CDCSR_OFFLINE) ? cddb & 0777 : 0777; if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_rd crm %06o cddb %06o data %06o\n", crm, cddb, *data); break; } return (SCPE_OK); } t_stat cr_wr ( int32 data, int32 PA, int32 access ) { switch ((PA >> 1) & 03) { case 0: if (cr_unit.flags & UNIT_CR11) { /* ignore high-byte writes */ if (PA & 1) break; /* fixup data for low byte write */ if (access == WRITEB) data = (crs & ~0377) | (data & 0377); if (!(data & CSR_IE)) CLR_INT (CR); crs = (crs & ~CRCSR_RW) | (data & CRCSR_RW); crs &= ~(CSR_ERR | CRCSR_CRDDONE | CRCSR_TIMERR); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_wr data %06o crs %06o\n", data, crs); if (data & CSR_GO) { if (blowerState != BLOW_ON) { sim_activate (&cr_unit, spinUp); blowerState = BLOW_START; } else sim_activate (&cr_unit, cr_unit.wait); } } else { if (data & CDCSR_PWRCLR) { CLR_INT (CR); sim_cancel (&cr_unit); cdst &= ~(CDCSR_RDRCHK |CDCSR_OFFLINE | CDCSR_RDY | CDCSR_HOPPER); cdst |= CDCSR_RDY; cdcc = 0; cdba = 0; break; } if (!(data & CSR_IE)) CLR_INT (CR); cdst = (cdst & ~CDCSR_RW) | (data & CDCSR_RW); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_wr data %06o cdst %06o\n", data, cdst); if (data & CSR_GO) { if (blowerState != BLOW_ON) { sim_activate (&cr_unit, spinUp); blowerState = BLOW_START; } else sim_activate (&cr_unit, cr_unit.wait); } } break; case 1: if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_wr cdcc %06o\n", data); if (cr_unit.flags & UNIT_CR11) break; cdcc = data & 0177777; break; case 2: if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_wr crba %06o\n", data); if (cr_unit.flags & UNIT_CR11) break; cdba = data & 0177777; break; case 3: if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_wr cddb/crm %06o\n", data); /* ignore writes to cddb */ if (!(cr_unit.flags & UNIT_CR11)) break; /* fixup data for byte writes and read-modify-write */ if (access == WRITEB) data = (PA & 1) ? (crm & 0377) | (data << 8) : (crm & ~0377) | (data & 0377); crm = data & 0177777; /* not 100% certain how these work */ if (!(crm & CRM_MAINT)) break; crs = (crm & CRM_BUSY) ? (crs | CRCSR_BUSY) : (crs & ~CRCSR_BUSY); crs = (crm & CRM_READY) ? (crs | CRCSR_OFFLINE) : (crs & ~CRCSR_OFFLINE); crs = (crm & CRM_HOPPER) ? (crs | CRCSR_SUPPLY | CRCSR_RDCHK) : (crs & ~(CRCSR_SUPPLY | CRCSR_RDCHK)); crb1 = crm & 07777; /* load low 12 bits */ break; default: /* can't happen */ break; } return (SCPE_OK); } /* Enter the service routine once for each column read from the card. CR state bits drive this primarily (see _BUSY and _CRDDONE). However, when in CD mode, also execute one column of DMA input. */ t_stat cr_svc ( UNIT *uptr ) { uint32 pa; uint8 c; uint16 w; if (blowerState == BLOW_STOP) { blowerState = BLOW_OFF; return (SCPE_OK); } if (blowerState == BLOW_START) blowerState = BLOW_ON; /* (almost) anything we do now will cause a CR interrupt */ if (crs & CSR_IE) SET_INT (CR); if (!(uptr->flags & UNIT_ATT) || (crs & CSR_ERR) || (cdst & CSR_ERR)) return (SCPE_OK); if ((crs & CRCSR_BUSY) && (currCol > colEnd)) { crs &= ~(CRCSR_BUSY | CSR_GO | CRCSR_COLRDY); crs |= CRCSR_CRDDONE; if (cdst & (CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM)) cdst |= CSR_ERR; if (cdst & CSR_IE) SET_INT (CR); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_svc card done\n"); return (SCPE_OK); } if (!(crs & CRCSR_BUSY)) { /* try to read a card */ /* crs &= ~CRCSR_CRDDONE; */ if (!readRtn (uptr->fileref, hcard, ccard, acard)) { sim_activate (uptr, spinDown); return (SCPE_OK); } currCol = colStart; crs |= CRCSR_BUSY; /* indicate reader busy */ } /* check for overrun (timing error) */ if ((uptr->flags & UNIT_CR11) && (crs & CRCSR_COLRDY)) crs |= CSR_ERR | CRCSR_TIMERR; crb1 = hcard[currCol] & 07777; crb2 = ccard[currCol] & 0377; uptr->buf = acard[currCol] & 0377; /* helpful for debugging */ if (!(uptr->flags & UNIT_CR11)) { pa = cdba | ((cdst & 060) << 12); /* The implementation of _NXM here is not quite the same as I interpret the (limited) documentaiton I have to indicate. However the effect should be similar. Documentation indicates that once _NXM is set, further NPR requests are inhibited though the card is allowed to read until completion. This implies that CDBA and the XBA bits are incremented accordingly, even though no data transfer occurs. This code detects and flags the NXM condition but allows attempts at subsequent memory writes, thus insuring the address registers are incremented properly. If this causes problems, I'll fix it. */ if (cdst & CDCSR_PACK) { c = cddb = ccard[currCol] & 0377; if (Map_WriteB (pa, 1, &c)) cdst |= CDCSR_NXM; pa = (pa + 1) & 0777777; } else { w = cddb = hcard[currCol] & 07777; if (Map_WriteW (pa, 2, &w)) cdst |= CDCSR_NXM; pa = (pa + 2) & 0777777; } cdba = pa & 0177777; cdst = (cdst & ~(CDCSR_XBA17|CDCSR_XBA16)) | ((pa & 0600000) >> 12); cdcc = (cdcc + 1) & 0177777; #if 0 if (!(cdst & CSR_IE) && !(crs & CRCSR_CRDDONE)) CLR_INT (CR); #endif } currCol++; /* advance the column counter */ if (!(crs & CRCSR_EJECT)) crs |= CRCSR_COLRDY; else CLR_INT (CR); sim_activate (uptr, uptr->wait); return (SCPE_OK); } t_stat cr_reset ( DEVICE *dptr ) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_reset\n"); cr_unit.buf = 0; currCol = 1; crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_TIMERR|CRCSR_ONLINE|CRCSR_BUSY| CRCSR_COLRDY|CSR_IE|CRCSR_EJECT|CSR_GO); crb1 = 0; crb2 = 0; crm = 0; cdst &= ~(CSR_ERR|CDCSR_RDRCHK|CDCSR_EOF|CDCSR_DATAERR|CDCSR_LATE| CDCSR_NXM|CSR_IE|CDCSR_XBA17|CDCSR_XBA16|CDCSR_ONLINE| CDCSR_PACK|CSR_GO); cdst |= CDCSR_RDY; cdcc = 0; cdba = 0; cddb = 0; if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) { crs |= CRCSR_ONLINE; /* non-standard */ crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE); cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); } else { cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; crs = CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; } sim_cancel (&cr_unit); /* deactivate unit */ if (blowerState != BLOW_OFF) { blowerState = BLOW_STOP; sim_activate (&cr_unit, spinDown); } EOFcard = FALSE; CLR_INT (CR); /* TBD: flush current card */ /* init uptr->wait ? */ return (SCPE_OK); } /* Handle the interface status and SIMH portion of the ATTACH. Another routine is used to evaluate the file and initialize other state globals correctly. */ #define MASK (SWMASK('A')|SWMASK('B')|SWMASK('I')|SWMASK('R')) t_stat cr_attach ( UNIT *uptr, char *cptr ) { t_stat reason; extern int32 sim_switches; if (sim_switches & ~MASK) return (SCPE_INVSW); /* file must previously exist; kludge */ sim_switches |= SWMASK ('R'); reason = attach_unit (uptr, cptr); if (!(uptr->flags & UNIT_ATT)) { crs &= ~CRCSR_ONLINE; crs |= CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY; cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; } else { setupCardFile (uptr, sim_switches); crs |= CRCSR_ONLINE; crs &= ~(CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY); cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); EOFcard = FALSE; } return (reason); } t_stat cr_detach ( UNIT *uptr ) { crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; /* interrupt? */ crs &= ~CRCSR_ONLINE; cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_OFFLINE; cardFormat = "unknown"; if (blowerState != BLOW_OFF) { blowerState = BLOW_STOP; sim_activate (uptr, spinDown); } return (detach_unit (uptr)); } t_stat cr_set_type ( UNIT *uptr, int32 val, char *cptr, void *desc ) { /* disallow type change if currently attached */ if (uptr->flags & UNIT_ATT) return (SCPE_NOFNC); cpm = (val & UNIT_CR11) ? 285 : 1000; uptr->wait = (60 * 1000) / cpm; return (SCPE_OK); } t_stat cr_show_format ( FILE *st, UNIT *uptr, int32 val, void *desc ) { fprintf (st, "%s format", cardFormat); return (SCPE_OK); } t_stat cr_set_rate ( UNIT *uptr, int32 val, char *cptr, void *desc ) { t_stat status = SCPE_OK; int32 i; if (!cptr) return (SCPE_MISVAL); if (strcmp (cptr, "DEFAULT") == 0) i = (uptr->flags & UNIT_CR11) ? 285 : 1000; else i = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &status); if (status == SCPE_OK) { if (i < 200 || i > 1200) status = SCPE_ARG; else { cpm = i; uptr->wait = (60 * 1000) / cpm; } } return (status); } t_stat cr_show_rate ( FILE *st, UNIT *uptr, int32 val, void *desc ) { fprintf (st, "%d cards per minute", cpm); return (SCPE_OK); } /* simulate pressing the card reader RESET button */ t_stat cr_set_reset ( UNIT *uptr, int32 val, char *cptr, void *desc ) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_set_reset\n"); /* Ignore the RESET switch while a read cycle is in progress or the unit simply is not attached. */ if ((crs & CRCSR_BUSY) || !(uptr->flags & UNIT_ATT)) return (SCPE_OK); /* if no errors, signal transition to on line */ crs |= CRCSR_ONLINE; crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR| CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO); cdst |= CDCSR_ONLINE; cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_EOF); if ((crs & CSR_IE) || (cdst & CSR_IE)) { SET_INT (CR); if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "cr_set_reset setting interrupt\n"); } /* start up the blower if the hopper is not empty */ if (blowerState != BLOW_ON) blowerState = BLOW_START; return (SCPE_OK); } /* simulate pressing the card reader STOP button */ t_stat cr_set_stop ( UNIT *uptr, int32 val, char *cptr, void *desc ) { if (DEBUG_PRS (cr_dev)) fprintf (sim_deb, "set_stop\n"); crs &= ~CRCSR_ONLINE; crs |= CSR_ERR | CRCSR_OFFLINE; cdst |= CDCSR_OFFLINE; /* CD11 does not appear to interrupt on STOP. */ if (crs & CSR_IE) SET_INT (CR); if (blowerState != BLOW_OFF) { blowerState = BLOW_STOP; /* set timer to turn it off completely */ sim_activate (uptr, spinDown); } return (SCPE_OK); } static const char * const trans[] = { "unknown", "026", "026FTN", "029", "EBCDIC" }; t_stat cr_set_trans ( UNIT *uptr, int32 val, char *cptr, void *desc ) { int i; if (!cptr) return (SCPE_MISVAL); if (strcmp (cptr, "DEFAULT") == 0) i = 3; else { for (i = 1; i < 5; i++) { if (strcmp (cptr, trans[i]) == 0) break; } } if (i < 1 || i > 4) return (SCPE_ARG); table = i; initTranslation (); /* reinitialize tables */ return (SCPE_OK); } t_stat cr_show_trans ( FILE *st, UNIT *uptr, int32 val, void *desc ) { fprintf (st, "translation %s", trans[table]); return (SCPE_OK); } simh-3.8.1/PDP11/pdp11_stddev.c0000644000175000017500000004101511110156420014060 0ustar vlmvlm/* pdp11_stddev.c: PDP-11 standard I/O devices simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti,tto DL11 terminal input/output clk KW11L (and other) line frequency clock 20-May-08 RMS Standardized clock delay at 1mips 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock 29-Oct-06 RMS Synced keyboard and clock Added clock coscheduling support 05-Jul-06 RMS Added UC only support for early DOS/RSTS 22-Nov-05 RMS Revised for new terminal processing routines 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 07-Jul-05 RMS Removed extraneous externs 11-Oct-04 RMS Added clock model dependencies 28-May-04 RMS Removed SET TTI CTRL-C 29-Dec-03 RMS Added console backpressure support 25-Apr-03 RMS Revised for extended file support 01-Mar-03 RMS Added SET/SHOW CLOCK FREQ, SET TTI CTRL-C 22-Nov-02 RMS Changed terminal default to 7B for UNIX 01-Nov-02 RMS Added 7B/8B support to terminal 29-Sep-02 RMS Added vector display support Split out paper tape Split DL11 dibs 30-May-02 RMS Widened POS to 32b 26-Jan-02 RMS Revised for multiple timers 09-Jan-02 RMS Fixed bugs in KW11L (found by John Dundas) 06-Jan-02 RMS Split I/O address routines, revised enable/disable support 29-Nov-01 RMS Added read only unit support 09-Nov-01 RMS Added RQDX3 support 07-Oct-01 RMS Upgraded clock to full KW11L for RSTS/E autoconfigure 07-Sep-01 RMS Moved function prototypes, revised interrupt mechanism 17-Jul-01 RMS Moved function prototype 04-Jul-01 RMS Added DZ11 support 05-Mar-01 RMS Added clock calibration support 30-Oct-00 RMS Standardized register order 25-Jun-98 RMS Fixed bugs in paper tape error handling */ #include "pdp11_defs.h" #define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ #define TTICSR_RW (CSR_IE) #define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ #define TTOCSR_RW (CSR_IE) #define CLKCSR_IMP (CSR_DONE + CSR_IE) /* real-time clock */ #define CLKCSR_RW (CSR_IE) #define CLK_DELAY 16667 extern int32 int_req[IPL_HLVL]; extern uint32 cpu_type; int32 tti_csr = 0; /* control/status */ int32 tto_csr = 0; /* control/status */ int32 clk_csr = 0; /* control/status */ int32 clk_tps = 60; /* ticks/second */ int32 clk_default = 60; /* default ticks/second */ int32 clk_fie = 0; /* force IE = 1 */ int32 clk_fnxm = 0; /* force NXM on reg */ int32 tmxr_poll = CLK_DELAY; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* timer poll */ t_stat tti_rd (int32 *data, int32 PA, int32 access); t_stat tti_wr (int32 data, int32 PA, int32 access); t_stat tti_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_rd (int32 *data, int32 PA, int32 access); t_stat tto_wr (int32 data, int32 PA, int32 access); t_stat tto_svc (UNIT *uptr); t_stat tto_reset (DEVICE *dptr); t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_rd (int32 *data, int32 PA, int32 access); t_stat clk_wr (int32 data, int32 PA, int32 access); t_stat clk_svc (UNIT *uptr); int32 clk_inta (void); t_stat clk_reset (DEVICE *dptr); t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list */ DIB tti_dib = { IOBA_TTI, IOLN_TTI, &tti_rd, &tti_wr, 1, IVCL (TTI), VEC_TTI, { NULL } }; UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE, 0), 0 }; REG tti_reg[] = { { ORDATA (BUF, tti_unit.buf, 8) }, { ORDATA (CSR, tti_csr, 16) }, { FLDATA (INT, IREQ (TTI), INT_V_TTI) }, { FLDATA (ERR, tti_csr, CSR_V_ERR) }, { FLDATA (DONE, tti_csr, CSR_V_DONE) }, { FLDATA (IE, tti_csr, CSR_V_IE) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tti_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, DEV_UBUS | DEV_QBUS }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { IOBA_TTO, IOLN_TTO, &tto_rd, &tto_wr, 1, IVCL (TTO), VEC_TTO, { NULL } }; UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_7P, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATA (BUF, tto_unit.buf, 8) }, { ORDATA (CSR, tto_csr, 16) }, { FLDATA (INT, IREQ (TTO), INT_V_TTO) }, { FLDATA (ERR, tto_csr, CSR_V_ERR) }, { FLDATA (DONE, tto_csr, CSR_V_DONE) }, { FLDATA (IE, tto_csr, CSR_V_IE) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, DEV_UBUS | DEV_QBUS }; /* CLK data structures clk_dev CLK device descriptor clk_unit CLK unit descriptor clk_reg CLK register list */ DIB clk_dib = { IOBA_CLK, IOLN_CLK, &clk_rd, &clk_wr, 1, IVCL (CLK), VEC_CLK, { &clk_inta } }; UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; REG clk_reg[] = { { ORDATA (CSR, clk_csr, 16) }, { FLDATA (INT, IREQ (CLK), INT_V_CLK) }, { FLDATA (DONE, clk_csr, CSR_V_DONE) }, { FLDATA (IE, clk_csr, CSR_V_IE) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, { DRDATA (TPS, clk_tps, 16), PV_LEFT + REG_HRO }, { DRDATA (DEFTPS, clk_default, 16), PV_LEFT + REG_HRO }, { FLDATA (FIE, clk_fie, 0), REG_HIDDEN }, { FLDATA (FNXM, clk_fnxm, 0), REG_HIDDEN }, { NULL } }; MTAB clk_mod[] = { { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", &clk_set_freq, NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, NULL, &clk_show_freq, NULL }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { 0 } }; DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, &clk_dib, DEV_UBUS | DEV_QBUS }; /* Terminal input address routines */ t_stat tti_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 00: /* tti csr */ *data = tti_csr & TTICSR_IMP; return SCPE_OK; case 01: /* tti buf */ tti_csr = tti_csr & ~CSR_DONE; CLR_INT (TTI); *data = tti_unit.buf & 0377; return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } t_stat tti_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 00: /* tti csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) CLR_INT (TTI); else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (TTI); tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); return SCPE_OK; case 01: /* tti buf */ return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } /* Terminal input service */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) /* break? */ uptr->buf = 0; else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); uptr->pos = uptr->pos + 1; tti_csr = tti_csr | CSR_DONE; if (tti_csr & CSR_IE) SET_INT (TTI); return SCPE_OK; } /* Terminal input reset */ t_stat tti_reset (DEVICE *dptr) { tti_unit.buf = 0; tti_csr = 0; CLR_INT (TTI); sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); return SCPE_OK; } /* Terminal output address routines */ t_stat tto_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 00: /* tto csr */ *data = tto_csr & TTOCSR_IMP; return SCPE_OK; case 01: /* tto buf */ *data = tto_unit.buf; return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } t_stat tto_wr (int32 data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 00: /* tto csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) CLR_INT (TTO); else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (TTO); tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); return SCPE_OK; case 01: /* tto buf */ if ((PA & 1) == 0) tto_unit.buf = data & 0377; tto_csr = tto_csr & ~CSR_DONE; CLR_INT (TTO); sim_activate (&tto_unit, tto_unit.wait); return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } /* Terminal output service */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags)); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ } } tto_csr = tto_csr | CSR_DONE; if (tto_csr & CSR_IE) SET_INT (TTO); uptr->pos = uptr->pos + 1; return SCPE_OK; } /* Terminal output reset */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; tto_csr = CSR_DONE; CLR_INT (TTO); sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; return SCPE_OK; } /* The line time clock has a few twists and turns through the history of 11's LSI-11 no CSR LSI-11/23 (KDF11A) no CSR PDP-11/23+ (KDF11B) no monitor bit PDP-11/24 (KDF11U) monitor bit clears on IAK */ /* Clock I/O address routines */ t_stat clk_rd (int32 *data, int32 PA, int32 access) { if (clk_fnxm) /* not there??? */ return SCPE_NXM; if (CPUT (HAS_LTCM)) /* monitor bit? */ *data = clk_csr & CLKCSR_IMP; else *data = clk_csr & (CLKCSR_IMP & ~CSR_DONE); /* no, just IE */ return SCPE_OK; } t_stat clk_wr (int32 data, int32 PA, int32 access) { if (clk_fnxm) /* not there??? */ return SCPE_NXM; if (PA & 1) return SCPE_OK; clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); if (CPUT (HAS_LTCM) && ((data & CSR_DONE) == 0)) /* monitor bit? */ clk_csr = clk_csr & ~CSR_DONE; /* clr if zero */ if ((((clk_csr & CSR_IE) == 0) && !clk_fie) || /* unless IE+DONE */ ((clk_csr & CSR_DONE) == 0)) /* clr intr */ CLR_INT (CLK); return SCPE_OK; } /* Clock service */ t_stat clk_svc (UNIT *uptr) { int32 t; clk_csr = clk_csr | CSR_DONE; /* set done */ if ((clk_csr & CSR_IE) || clk_fie) SET_INT (CLK); t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, t); /* reactivate unit */ tmr_poll = t; /* set timer poll */ tmxr_poll = t; /* set mux poll */ return SCPE_OK; } /* Clock interrupt acknowledge */ int32 clk_inta (void) { if (CPUT (CPUT_24)) clk_csr = clk_csr & ~CSR_DONE; return clk_dib.vec; } /* Clock coscheduling routine */ int32 clk_cosched (int32 wait) { int32 t; t = sim_is_active (&clk_unit); return (t? t - 1: wait); } /* Clock reset */ t_stat clk_reset (DEVICE *dptr) { if (CPUT (HAS_LTCR)) /* reg there? */ clk_fie = clk_fnxm = 0; else clk_fie = clk_fnxm = 1; /* no, BEVENT */ clk_tps = clk_default; /* set default tps */ clk_csr = CSR_DONE; /* set done */ CLR_INT (CLK); sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init line clock */ sim_activate_abs (&clk_unit, clk_unit.wait); /* activate unit */ tmr_poll = clk_unit.wait; /* set timer poll */ tmxr_poll = clk_unit.wait; /* set mux poll */ return SCPE_OK; } /* Set frequency */ t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) { if (cptr) return SCPE_ARG; if ((val != 50) && (val != 60)) return SCPE_IERR; clk_tps = clk_default = val; return SCPE_OK; } /* Show frequency */ t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "%dHz", clk_tps); return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rc.c0000644000175000017500000005654010746650030013216 0ustar vlmvlm/* pdp11_rc.c: RC11/RS64 fixed head disk simulator Copyright (c) 2007-2008, John A. Dundas III Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of 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 author. rc RC11/RS64 fixed head disk 28-Dec-07 JAD Correct extraction of unit number from da in rc_svc. Clear _all_ error bits when a new operation starts. Passes all diagnostics in all configurations. 25-Dec-07 JAD Compute the CRC-16 of the last sector read via a READ or WCHK. 20-Dec-07 JAD Correctly simulate rotation over the selected track for RCLA. Also update the register correctly during I/O operations. Insure function activation time is non-zero. Handle unit number wrap correctly. 19-Dec-07 JAD Iterate over a full sector regardless of the actual word count so that RCDA ends correctly. Honor the read-only vs. read-write status of the attached file. 16-Dec-07 JAD The RCDA must be checked for validity when it is written to, not just when GO is received. 15-Dec-07 JAD Better handling of disk address errors and the RCLA register. Add more registers to the visible device state. 07-Jan-07 JAD Initial creation and testing. Adapted from pdp11_rf.c. The RS64 is a head-per-track disk. To minimize overhead, the entire RC11 is buffered in memory. Up to 4 RS64 "platters" may be controlled by one RC11 for a total of 262,144 words (65536kW/platter). [Later in time the RK611 was assigned the same CSR address.] Diagnostic routines: ZRCAB0.BIC - passes w/1-4 platters ZRCBB0.BIC - passes w/1-4 platters ZRCCB0.BIC - passes w/1-4 platters Note that the diagnostics require R/W disks (i.e., will destroy any existing data). For regression, must pass all three diagnostics configured for 1-4 platters for a total of 12 tests. Information necessary to create this simulation was gathered from the PDP11 Peripherals Handbook, 1973-74 edition. One timing parameter is provided: rc_time Minimum I/O operation time, must be non-zero */ #if !defined (VM_PDP11) #error "RC11 is not supported!" #endif #include "pdp11_defs.h" #include #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ #define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ #define UNIT_M_PLAT 03 #define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) #define UNIT_AUTO (1 << UNIT_V_AUTO) #define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) /* Constants */ #define RC_NUMWD (32*64) /* words/track */ #define RC_NUMTR 32 /* tracks/disk */ #define RC_DKSIZE (RC_NUMTR * RC_NUMWD) /* words/disk */ #define RC_NUMDK 4 /* disks/controller */ #define RC_WMASK (RC_NUMWD - 1) /* word mask */ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ /* Control and status register (RCCS) */ #define RCCS_ERR (CSR_ERR) /* error */ #define RCCS_DATA 0040000 /* data error */ #define RCCS_ADDR 0020000 /* address error */ #define RCCS_WLK 0010000 /* write lock */ #define RCCS_NED 0004000 /* nx disk */ #define RCCS_WCHK 0002000 /* write check */ #define RCCS_INH 0001000 /* inhibit CA incr */ #define RCCS_ABO 0000400 /* abort */ #define RCCS_DONE (CSR_DONE) #define RCCS_IE (CSR_IE) #define RCCS_M_MEX 0000003 /* memory extension */ #define RCCS_V_MEX 4 #define RCCS_MEX (RCCS_M_MEX << RCCS_V_MEX) #define RCCS_MAINT 0000010 /* maint */ #define RCCS_M_FUNC 0000003 /* function */ #define RFNC_LAH 0 #define RFNC_WRITE 1 #define RFNC_READ 2 #define RFNC_WCHK 3 #define RCCS_V_FUNC 1 #define RCCS_FUNC (RCCS_M_FUNC << RCCS_V_FUNC) #define RCCS_GO 0000001 #define RCCS_ALLERR (RCCS_DATA|RCCS_ADDR|RCCS_WLK|RCCS_NED|RCCS_WCHK) #define RCCS_W (RCCS_INH | RCCS_ABO |RCCS_IE | RCCS_MEX | RCCS_MAINT | \ RCCS_FUNC | RCCS_GO) /* Disk error status register (RCER) */ #define RCER_DLT 0100000 /* data late */ #define RCER_CHK 0040000 /* block check */ #define RCER_SYNC 0020000 /* data sync */ #define RCER_NXM 0010000 /* nonexistant memory */ #define RCER_TRK 0001000 /* track error */ #define RCER_APAR 0000200 /* address parity */ #define RCER_SADDR 0000100 /* sync address */ #define RCER_OVFL 0000040 /* disk overflow */ #define RCER_MIS 0000020 /* missed transfer */ /* Lood Ahead Register (RCLA) */ #define RCLA_BADD 0100000 /* bad address */ /* extract device operation code */ #define GET_FUNC(x) (((x) >> RCCS_V_FUNC) & RCCS_M_FUNC) /* extract memory extension address (bits 17,18) */ #define GET_MEX(x) (((x) & RCCS_MEX) << (16 - RCCS_V_MEX)) #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) RC_NUMWD))) extern int32 int_req[IPL_HLVL]; extern FILE *sim_deb; extern int32 R[]; static uint32 rc_la = 0; /* look-ahead */ static uint32 rc_da = 0; /* disk address */ static uint32 rc_er = 0; /* error status */ static uint32 rc_cs = 0; /* command and status */ static uint32 rc_wc = 0; /* word count */ static uint32 rc_ca = 0; /* current address */ static uint32 rc_maint = 0; /* maintenance */ static uint32 rc_db = 0; /* data buffer */ static uint32 rc_wlk = 0; /* write lock */ static uint32 rc_time = 16; /* inter-word time: 16us */ static uint32 rc_stopioe = 1; /* stop on error */ /* forward references */ DEVICE rc_dev; static t_stat rc_rd (int32 *, int32, int32); static t_stat rc_wr (int32, int32, int32); static t_stat rc_svc (UNIT *); static t_stat rc_reset (DEVICE *); static t_stat rc_attach (UNIT *, char *); static t_stat rc_set_size (UNIT *, int32, char *, void *); static uint32 update_rccs (uint32, uint32); /* RC11 data structures rc_dev RC device descriptor rc_unit RC unit descriptor rc_reg RC register list */ static DIB rc_dib = { IOBA_RC, IOLN_RC, &rc_rd, &rc_wr, 1, IVCL (RC), VEC_RC, { NULL } }; static UNIT rc_unit = { UDATA (&rc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_ROABLE + UNIT_BINK, RC_DKSIZE) }; static const REG rc_reg[] = { { ORDATA (RCLA, rc_la, 16) }, { ORDATA (RCDA, rc_da, 16) }, { ORDATA (RCER, rc_er, 16) }, { ORDATA (RCCS, rc_cs, 16) }, { ORDATA (RCWC, rc_wc, 16) }, { ORDATA (RCCA, rc_ca, 16) }, { ORDATA (RCMN, rc_maint, 16) }, { ORDATA (RCDB, rc_db, 16) }, { ORDATA (RCWLK, rc_wlk, 32) }, { FLDATA (INT, IREQ (RC), INT_V_RC) }, { FLDATA (ERR, rc_cs, CSR_V_ERR) }, { FLDATA (DONE, rc_cs, CSR_V_DONE) }, { FLDATA (IE, rc_cs, CSR_V_IE) }, { DRDATA (TIME, rc_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, rc_stopioe, 0) }, { ORDATA (DEVADDR, rc_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, rc_dib.vec, 16), REG_HRO }, { NULL } }; static const MTAB rc_mod[] = { { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rc_set_size }, { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rc_set_size }, { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rc_set_size }, { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rc_set_size }, { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE rc_dev = { "RC", &rc_unit, (REG *) rc_reg, (MTAB *) rc_mod, 1, 8, 21, 1, 8, 16, NULL, /* examine */ NULL, /* deposit */ &rc_reset, /* reset */ NULL, /* boot */ &rc_attach, /* attach */ NULL, /* detach */ &rc_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG }; /* I/O dispatch routine, I/O addresses 17777440 - 17777456 */ static t_stat rc_rd (int32 *data, int32 PA, int32 access) { uint32 t; switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RCLA */ t = rc_la & 017777; if ((rc_cs & RCCS_NED) || (rc_er & RCER_OVFL)) t |= RCLA_BADD; *data = t; /* simulate sequential rotation about the current track */ rc_la = (rc_la & ~077) | ((rc_la + 1) & 077); if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCLA %06o\n", rc_la); break; case 1: /* RCDA */ *data = rc_da; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCDA %06o, PC %06o\n", rc_da, PC); break; case 2: /* RCER */ *data = rc_er; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCER %06o\n", rc_er); break; case 3: /* RCCS */ *data = update_rccs (0, 0) & ~(RCCS_ABO | RCCS_GO); if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCCS %06o\n", *data); break; case 4: /* RCWC */ *data = rc_wc; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCWC %06o\n", rc_wc); break; case 5: /* RCCA */ *data = rc_ca; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCCA %06o\n", rc_ca); break; case 6: /* RCMN */ *data = rc_maint; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCMN %06o\n", rc_maint); break; case 7: /* RCDB */ *data = rc_db; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC rd: RCDB %06o\n", rc_db); break; default: return (SCPE_NXM); /* can't happen */ } /* end switch */ return (SCPE_OK); } static t_stat rc_wr (int32 data, int32 PA, int32 access) { int32 t; switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* RCLA */ if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCLA\n"); break; /* read only */ case 1: /* RCDA */ if (access == WRITEB) data = (PA & 1) ? (rc_da & 0377) | (data << 8) : (rc_da & ~0377) | data; rc_da = data & 017777; rc_cs &= ~RCCS_NED; update_rccs (0, 0); /* perform unit select */ if (((rc_da >> 11) & 03) >= UNIT_GETP(rc_unit.flags)) update_rccs (RCCS_NED, 0); else rc_la = rc_da; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCDA %06o, PC %06o\n", rc_da, PC); break; case 2: /* RCER */ if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCER\n"); break; /* read only */ case 3: /* RCCS */ if (access == WRITEB) data = (PA & 1) ? (rc_cs & 0377) | (data << 8) : (rc_cs & ~0377) | data; if (data & RCCS_ABO) { update_rccs (RCCS_DONE, 0); sim_cancel (&rc_unit); } if ((data & RCCS_IE) == 0) /* int disable? */ CLR_INT (RC); /* clr int request */ else if ((rc_cs & (RCCS_DONE | RCCS_IE)) == RCCS_DONE) SET_INT (RC); /* set int request */ rc_cs = (rc_cs & ~RCCS_W) | (data & RCCS_W); /* merge */ if ((rc_cs & RCCS_DONE) && (data & RCCS_GO)) { /* new function? */ rc_unit.FUNC = GET_FUNC (data); /* save function */ t = (rc_da & RC_WMASK) - GET_POS (rc_time); /* delta to new loc */ if (t <= 0) /* wrap around? */ t = t + RC_NUMWD; sim_activate (&rc_unit, t * rc_time); /* schedule op */ /* clear error indicators for new operation */ rc_cs &= ~(RCCS_ALLERR | RCCS_ERR | RCCS_DONE); rc_er = 0; CLR_INT (RC); if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC start: cs = %o, da = %o, ma = %o, wc = %o\n", update_rccs (0, 0), rc_da, GET_MEX (rc_cs) | rc_ca, rc_wc); } break; case 4: /* RCWC */ if (access == WRITEB) data = (PA & 1) ? (rc_wc & 0377) | (data << 8) : (rc_wc & ~0377) | data; rc_wc = data & DMASK; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCWC %06o, PC %06o\n", rc_wc, PC); break; case 5: /* RCCA */ /* TBD: write byte fixup? */ rc_ca = data & 0177776; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCCA %06o\n", rc_ca); break; case 6: /* RCMN */ /* TBD: write byte fixup? */ rc_maint = data & 0177700; if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCMN %06o\n", rc_maint); break; case 7: /* RCDB */ if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC wr: RCDB\n"); break; /* read only */ default: /* can't happen */ return (SCPE_NXM); } /* end switch */ update_rccs (0, 0); return (SCPE_OK); } /* sector (32W) CRC-16 */ static uint32 sectorCRC (const uint16 *data) { uint32 crc, i, j, d; crc = 0; for (i = 0; i < 32; i++) { d = *data++; /* cribbed from KG11-A */ for (j = 0; j < 16; j++) { crc = (crc & ~01) | ((crc & 01) ^ (d & 01)); crc = (crc & 01) ? (crc >> 1) ^ 0120001 : crc >> 1; d >>= 1; } } return (crc); } /* Unit service Note that for reads and writes, memory addresses wrap around in the current field. This code assumes the entire disk is buffered. */ static t_stat rc_svc (UNIT *uptr) { uint32 ma, da, t, u_old, u_new, last_da; uint16 dat; uint16 *fbuf = uptr->filebuf; if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ update_rccs (RCCS_NED | RCCS_DONE, 0); /* nx disk */ return (IORETURN (rc_stopioe, SCPE_UNATT)); } ma = GET_MEX (rc_cs) | rc_ca; /* 18b mem addr */ da = rc_da * RC_NUMTR; /* sector->word offset */ u_old = (da >> 16) & 03; /* save starting unit# */ do { u_new = (da >> 16) & 03; if (u_new < u_old) { /* unit # overflow? */ update_rccs (RCCS_NED, RCER_OVFL); break; } if (u_new >= UNIT_GETP(uptr->flags)) { /* disk overflow? */ update_rccs (RCCS_NED, 0); break; } if (uptr->FUNC == RFNC_READ) { /* read? */ last_da = da & ~037; dat = fbuf[da]; /* get disk data */ rc_db = dat; if (Map_WriteW (ma, 2, &dat)) { /* store mem, nxm? */ update_rccs (0, RCER_NXM); break; } } else if (uptr->FUNC == RFNC_WCHK) { /* write check? */ last_da = da & ~037; rc_db = fbuf[da]; /* get disk data */ if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ update_rccs (0, RCER_NXM); break; } if (rc_db != dat) { /* miscompare? */ update_rccs (RCCS_WCHK, 0); break; } } else if (uptr->FUNC == RFNC_WRITE) { /* write */ t = (da >> 15) & 037; if (((rc_wlk >> t) & 1) || (uptr->flags & UNIT_RO)) { /* write locked? */ update_rccs (RCCS_WLK, 0); break; } /* not locked */ if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ update_rccs (0, RCER_NXM); break; } fbuf[da] = dat; /* write word */ rc_db = dat; if (da >= uptr->hwmark) uptr->hwmark = da + 1; } else { /* look ahead */ break; /* no op for now */ } rc_wc = (rc_wc + 1) & DMASK; /* incr word count */ da = (da + 1) & 0777777; /* incr disk addr */ if ((rc_cs & RCCS_INH) == 0) /* inhibit clear? */ ma = (ma + 2) & UNIMASK; /* incr mem addr */ } while (rc_wc != 0); /* brk if wc */ rc_ca = ma & DMASK; /* split ma */ rc_cs = (rc_cs & ~RCCS_MEX) | ((ma >> (16 - RCCS_V_MEX)) & RCCS_MEX); da += 31; rc_da = (da >> 5) & 017777; /* CRC of last 32W, if necessary */ if ((uptr->FUNC == RFNC_READ) || (uptr->FUNC == RFNC_WCHK)) rc_db = sectorCRC (&fbuf[last_da]); if (uptr->FUNC != RFNC_LAH) rc_la = rc_da; update_rccs (RCCS_DONE, 0); if (DEBUG_PRS (rc_dev)) fprintf (sim_deb, ">>RC done: cs = %o, da = %o, ma = %o, wc = %o\n", rc_cs, rc_da, rc_ca, rc_wc); return (SCPE_OK); } /* Update CS register */ static uint32 update_rccs (uint32 newcs, uint32 newer) { uint32 oldcs = rc_cs; rc_er |= newer; /* update RCER */ rc_cs |= newcs; /* update CS */ if ((rc_cs & RCCS_ALLERR) || (rc_er != 0)) /* update CS */ rc_cs |= RCCS_ERR; else rc_cs &= ~RCCS_ERR; if ((rc_cs & RCCS_IE) && /* IE and */ (rc_cs & RCCS_DONE) && !(oldcs & RCCS_DONE)) /* done 0->1? */ SET_INT (RC); return (rc_cs); } /* Reset routine */ static t_stat rc_reset (DEVICE *dptr) { rc_cs = RCCS_DONE; rc_la = rc_da = 0; rc_er = 0; rc_wc = 0; rc_ca = 0; rc_maint = 0; rc_db = 0; CLR_INT (RC); sim_cancel (&rc_unit); return (SCPE_OK); } /* Attach routine */ static t_stat rc_attach (UNIT *uptr, char *cptr) { uint32 sz, p; static const uint32 ds_bytes = RC_DKSIZE * sizeof (int16); if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { p = (sz + ds_bytes - 1) / ds_bytes; if (p >= RC_NUMDK) p = RC_NUMDK - 1; uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); } uptr->capac = UNIT_GETP (uptr->flags) * RC_DKSIZE; return (attach_unit (uptr, cptr)); } /* Change disk size */ static t_stat rc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val < 0) return (SCPE_IERR); if (uptr->flags & UNIT_ATT) return (SCPE_ALATT); uptr->capac = UNIT_GETP (val) * RC_DKSIZE; uptr->flags = uptr->flags & ~UNIT_AUTO; return (SCPE_OK); } simh-3.8.1/PDP11/pdp11_ts.c0000644000175000017500000013471111112257374013237 0ustar vlmvlm/* pdp11_ts.c: TS11/TSV05 magnetic tape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ts TS11/TSV05 magtape 16-Feb-06 RMS Added tape capacity checking 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 18-Mar-05 RMS Added attached test to detach routine 07-Dec-04 RMS Added read-only file support 30-Sep-04 RMS Revised Unibus interface 25-Jan-04 RMS Revised for device debug support 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised to use magtape library 30-Sep-02 RMS Added variable address support to bootstrap Added vector change/display support Fixed CTL unload/clean decode Implemented XS0_MOT in extended status New data structures, revamped error recovery 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Added maximum record length protection 04-Apr-02 RMS Fixed bug in residual frame count after space operation 16-Feb-02 RMS Fixed bug in message header logic 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 09-Nov-01 RMS Added bus map, VAX support 15-Oct-01 RMS Integrated debug logging across simulator 27-Sep-01 RMS Implemented extended characteristics and status Fixed bug in write characteristics status return 19-Sep-01 RMS Fixed bug in bootstrap 15-Sep-01 RMS Fixed bug in NXM test 07-Sep-01 RMS Revised device disable and interrupt mechanism 13-Jul-01 RMS Fixed bug in space reverse (found by Peter Schorn) Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. The TS11 functions in three environments: - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to go through the I/O map - PDP-11 Unibus 22b systems - the TS11 behaves as an 18b Unibus peripheral and must go through the I/O map - VAX Q22 systems - the TS11 must go through the I/O map */ #if defined (VM_PDP10) /* PDP10 version */ #error "TS11 not supported on PDP10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define TS_DIS 0 /* on by default */ #define DMASK 0xFFFF #else /* PDP-11 version */ #include "pdp11_defs.h" #define TS_DIS DEV_DIS /* off by default */ extern int32 cpu_opt; #endif #include "sim_tape.h" #define ADDRTEST (UNIBUS? 0177774: 0177700) /* TSBA/TSDB - 17772520: base address/data buffer register read: most recent memory address write word: initiate command write byte: diagnostic use */ /* TSSR - 17772522: subsystem status register TSDBX - 17772523: extended address register read: return status write word: initialize write byte: if odd, set extended packet address register */ #define TSSR_SC 0100000 /* special condition */ #define TSSR_RMR 0010000 /* reg mod refused */ #define TSSR_NXM 0004000 /* nxm */ #define TSSR_NBA 0002000 /* need buf addr */ #define TSSR_V_EMA 8 /* mem addr<17:16> */ #define TSSR_EMA 0001400 #define TSSR_SSR 0000200 /* subsystem ready */ #define TSSR_OFL 0000100 /* offline */ #define TSSR_V_TC 1 /* term class */ #define TSSR_M_TC 07 #define TSSR_TC (TSSR_M_TC << TSSR_V_TC) #define TC0 (0 << TSSR_V_TC) /* ok */ #define TC1 (1 << TSSR_V_TC) /* attention */ #define TC2 (2 << TSSR_V_TC) /* status alert */ #define TC3 (3 << TSSR_V_TC) /* func reject */ #define TC4 (4 << TSSR_V_TC) /* retry, moved */ #define TC5 (5 << TSSR_V_TC) /* retry */ #define TC6 (6 << TSSR_V_TC) /* pos lost */ #define TC7 (7 << TSSR_V_TC) /* fatal err */ #define TSSR_MBZ 0060060 #define GET_TC(x) (((x) >> TSSR_V_TC) & TSSR_M_TC) #define TSDBX_M_XA 017 /* ext addr */ #define TSDBX_BOOT 0000200 /* boot */ /* Command packet offsets */ #define CMD_PLNT 4 /* cmd pkt length */ #define cmdhdr tscmdp[0] /* header */ #define cmdadl tscmdp[1] /* address low */ #define cmdadh tscmdp[2] /* address high */ #define cmdlnt tscmdp[3] /* length */ /* Command packet header */ #define CMD_ACK 0100000 /* acknowledge */ #define CMD_CVC 0040000 /* clear vol chk */ #define CMD_OPP 0020000 /* opposite */ #define CMD_SWP 0010000 /* swap bytes */ #define CMD_V_MODE 8 /* mode */ #define CMD_M_MODE 017 #define CMD_IE 0000200 /* int enable */ #define CMD_V_FNC 0 /* function */ #define CMD_M_FNC 037 /* function */ #define CMD_N_FNC (CMD_M_FNC + 1) #define FNC_READ 001 /* read */ #define FNC_WCHR 004 /* write char */ #define FNC_WRIT 005 /* write */ #define FNC_WSSM 006 /* write mem */ #define FNC_POS 010 /* position */ #define FNC_FMT 011 /* format */ #define FNC_CTL 012 /* control */ #define FNC_INIT 013 /* init */ #define FNC_GSTA 017 /* get status */ #define CMD_MBZ 0000140 #define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) #define GET_MOD(x) (((x) >> CMD_V_MODE) & CMD_M_MODE) /* Function test flags */ #define FLG_MO 001 /* motion */ #define FLG_WR 002 /* write */ #define FLG_AD 004 /* addr mem */ /* Message packet offsets */ #define MSG_PLNT 8 /* packet length */ #define msghdr tsmsgp[0] /* header */ #define msglnt tsmsgp[1] /* length */ #define msgrfc tsmsgp[2] /* residual frame */ #define msgxs0 tsmsgp[3] /* ext status 0 */ #define msgxs1 tsmsgp[4] /* ext status 1 */ #define msgxs2 tsmsgp[5] /* ext status 2 */ #define msgxs3 tsmsgp[6] /* ext status 3 */ #define msgxs4 tsmsgp[7] /* ext status 4 */ /* Message packet header */ #define MSG_ACK 0100000 /* acknowledge */ #define MSG_MATN 0000000 /* attention */ #define MSG_MILL 0000400 /* illegal */ #define MSG_MNEF 0001000 /* non exec fnc */ #define MSG_CEND 0000020 /* end */ #define MSG_CFAIL 0000021 /* fail */ #define MSG_CERR 0000022 /* error */ #define MSG_CATN 0000023 /* attention */ /* Extended status register 0 */ #define XS0_TMK 0100000 /* tape mark */ #define XS0_RLS 0040000 /* rec lnt short */ #define XS0_LET 0020000 /* log end tape */ #define XS0_RLL 0010000 /* rec lnt long */ #define XS0_WLE 0004000 /* write lock err */ #define XS0_NEF 0002000 /* non exec fnc */ #define XS0_ILC 0001000 /* illegal cmd */ #define XS0_ILA 0000400 /* illegal addr */ #define XS0_MOT 0000200 /* tape has moved */ #define XS0_ONL 0000100 /* online */ #define XS0_IE 0000040 /* int enb */ #define XS0_VCK 0000020 /* volume check */ #define XS0_PET 0000010 /* 1600 bpi */ #define XS0_WLK 0000004 /* write lock */ #define XS0_BOT 0000002 /* BOT */ #define XS0_EOT 0000001 /* EOT */ #define XS0_ALLCLR 0177600 /* clear at start */ /* Extended status register 1 */ #define XS1_UCOR 0000002 /* uncorrectable */ /* Extended status register 2 */ #define XS2_XTF 0000200 /* ext features */ /* Extended status register 3 */ #define XS3_OPI 0000100 /* op incomplete */ #define XS3_REV 0000040 /* reverse */ #define XS3_RIB 0000001 /* reverse to BOT */ /* Extended status register 4 */ #define XS4_HDS 0100000 /* high density */ /* Write characteristics packet offsets */ #define WCH_PLNT 5 /* packet length */ #define wchadl tswchp[0] /* address low */ #define wchadh tswchp[1] /* address high */ #define wchlnt tswchp[2] /* length */ #define wchopt tswchp[3] /* options */ #define wchxopt tswchp[4] /* ext options */ /* Write characteristics options */ #define WCH_ESS 0000200 /* stop dbl tmk */ #define WCH_ENB 0000100 /* BOT = tmk */ #define WCH_EAI 0000040 /* enb attn int */ #define WCH_ERI 0000020 /* enb mrls int */ /* Write characteristics extended options */ #define WCHX_HDS 0000040 /* high density */ #define MAX(a,b) (((a) >= (b))? (a): (b)) #define MAX_PLNT 8 /* max pkt length */ extern int32 int_req[IPL_HLVL]; extern UNIT cpu_unit; extern FILE *sim_deb; uint8 *tsxb = NULL; /* xfer buffer */ int32 tssr = 0; /* status register */ int32 tsba = 0; /* mem addr */ int32 tsdbx = 0; /* data buf ext */ int32 tscmdp[CMD_PLNT] = { 0 }; /* command packet */ int32 tsmsgp[MSG_PLNT] = { 0 }; /* message packet */ int32 tswchp[WCH_PLNT] = { 0 }; /* wr char packet */ int32 ts_ownc = 0; /* tape owns cmd */ int32 ts_ownm = 0; /* tape owns msg */ int32 ts_qatn = 0; /* queued attn */ int32 ts_bcmd = 0; /* boot cmd */ int32 ts_time = 10; /* record latency */ static uint16 cpy_buf[MAX_PLNT]; /* copy buffer */ DEVICE ts_dev; t_stat ts_rd (int32 *data, int32 PA, int32 access); t_stat ts_wr (int32 data, int32 PA, int32 access); t_stat ts_svc (UNIT *uptr); t_stat ts_reset (DEVICE *dptr); t_stat ts_attach (UNIT *uptr, char *cptr); t_stat ts_detach (UNIT *uptr); t_stat ts_boot (int32 unitno, DEVICE *dptr); int32 ts_updtssr (int32 t); int32 ts_updxs0 (int32 t); void ts_cmpendcmd (int32 s0, int32 s1); void ts_endcmd (int32 ssf, int32 xs0f, int32 msg); int32 ts_map_status (t_stat st); /* TS data structures ts_dev TS device descriptor ts_unit TS unit list ts_reg TS register list ts_mod TS modifier list */ DIB ts_dib = { IOBA_TS, IOLN_TS, &ts_rd, &ts_wr, 1, IVCL (TS), VEC_TS, { NULL } }; UNIT ts_unit = { UDATA (&ts_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }; REG ts_reg[] = { { GRDATA (TSSR, tssr, DEV_RDX, 16, 0) }, { GRDATA (TSBA, tsba, DEV_RDX, 22, 0) }, { GRDATA (TSDBX, tsdbx, DEV_RDX, 8, 0) }, { GRDATA (CHDR, cmdhdr, DEV_RDX, 16, 0) }, { GRDATA (CADL, cmdadl, DEV_RDX, 16, 0) }, { GRDATA (CADH, cmdadh, DEV_RDX, 16, 0) }, { GRDATA (CLNT, cmdlnt, DEV_RDX, 16, 0) }, { GRDATA (MHDR, msghdr, DEV_RDX, 16, 0) }, { GRDATA (MRFC, msgrfc, DEV_RDX, 16, 0) }, { GRDATA (MXS0, msgxs0, DEV_RDX, 16, 0) }, { GRDATA (MXS1, msgxs1, DEV_RDX, 16, 0) }, { GRDATA (MXS2, msgxs2, DEV_RDX, 16, 0) }, { GRDATA (MXS3, msgxs3, DEV_RDX, 16, 0) }, { GRDATA (MSX4, msgxs4, DEV_RDX, 16, 0) }, { GRDATA (WADL, wchadl, DEV_RDX, 16, 0) }, { GRDATA (WADH, wchadh, DEV_RDX, 16, 0) }, { GRDATA (WLNT, wchlnt, DEV_RDX, 16, 0) }, { GRDATA (WOPT, wchopt, DEV_RDX, 16, 0) }, { GRDATA (WXOPT, wchxopt, DEV_RDX, 16, 0) }, { FLDATA (INT, IREQ (TS), INT_V_TS) }, { FLDATA (ATTN, ts_qatn, 0) }, { FLDATA (BOOT, ts_bcmd, 0) }, { FLDATA (OWNC, ts_ownc, 0) }, { FLDATA (OWNM, ts_ownm, 0) }, { DRDATA (TIME, ts_time, 24), PV_LEFT + REG_NZ }, { DRDATA (POS, ts_unit.pos, T_ADDR_W), PV_LEFT + REG_RO }, { GRDATA (DEVADDR, ts_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, ts_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB ts_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE ts_dev = { "TS", &ts_unit, ts_reg, ts_mod, 1, 10, T_ADDR_W, 1, DEV_RDX, 8, NULL, NULL, &ts_reset, &ts_boot, &ts_attach, &ts_detach, &ts_dib, DEV_DISABLE | TS_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; /* I/O dispatch routines, I/O addresses 17772520 - 17772522 17772520 TSBA read/write 17772522 TSSR read/write */ t_stat ts_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* TSBA */ *data = tsba & DMASK; /* low 16b of ba */ break; case 1: /* TSSR */ *data = tssr = ts_updtssr (tssr); /* update tssr */ break; } return SCPE_OK; } t_stat ts_wr (int32 data, int32 PA, int32 access) { int32 i, t; switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* TSDB */ if ((tssr & TSSR_SSR) == 0) { /* ready? */ tssr = tssr | TSSR_RMR; /* no, refuse */ break; } tsba = ((tsdbx & TSDBX_M_XA) << 18) | /* form pkt addr */ ((data & 03) << 16) | (data & 0177774); tsdbx = 0; /* clr tsdbx */ tssr = ts_updtssr (tssr & TSSR_NBA); /* clr ssr, err */ msgxs0 = ts_updxs0 (msgxs0 & ~XS0_ALLCLR); /* clr, upd xs0 */ msgrfc = msgxs1 = msgxs2 = msgxs3 = msgxs4 = 0; /* clr status */ CLR_INT (TS); /* clr int req */ t = Map_ReadW (tsba, CMD_PLNT << 1, cpy_buf); /* read cmd pkt */ tsba = tsba + ((CMD_PLNT << 1) - t); /* incr tsba */ if (t) { /* nxm? */ ts_endcmd (TSSR_NXM + TC5, 0, MSG_ACK|MSG_MNEF|MSG_CFAIL); return SCPE_OK; } for (i = 0; i < CMD_PLNT; i++) /* copy packet */ tscmdp[i] = cpy_buf[i]; ts_ownc = ts_ownm = 1; /* tape owns all */ sim_activate (&ts_unit, ts_time); /* activate */ break; case 1: /* TSSR */ if (PA & 1) { /* TSDBX */ if (UNIBUS) /* not in TS11 */ return SCPE_OK; if (tssr & TSSR_SSR) { /* ready? */ tsdbx = data; /* save */ if (data & TSDBX_BOOT) { ts_bcmd = 1; sim_activate (&ts_unit, ts_time); } } else tssr = tssr | TSSR_RMR; /* no, err */ } else if (access == WRITE) /* reset */ ts_reset (&ts_dev); break; } return SCPE_OK; } /* Tape motion routines */ #define XTC(x,t) (((unsigned) (x) << 16) | (t)) #define GET_X(x) (((x) >> 16) & 0177777) #define GET_T(x) ((x) & 0177777) int32 ts_map_status (t_stat st) { switch (st) { case MTSE_OK: break; case MTSE_TMK: msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ return (XTC (XS0_TMK | XS0_RLS, TC2)); case MTSE_RECE: /* record in error */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ case MTSE_INVRL: /* invalid rec lnt */ case MTSE_IOERR: /* IO error */ msgxs1 = msgxs1 | XS1_UCOR; /* uncorrectable */ return (XTC (XS0_RLS, TC6)); /* pos lost */ case MTSE_FMT: case MTSE_UNATT: case MTSE_EOM: /* end of medium */ msgxs3 = msgxs3 | XS3_OPI; /* incomplete */ return (XTC (XS0_RLS, TC6)); /* pos lost */ case MTSE_BOT: /* reverse into BOT */ msgxs3 = msgxs3 | XS3_RIB; /* set status */ return (XTC (XS0_BOT | XS0_RLS, TC2)); /* tape alert */ case MTSE_WRP: /* write protect */ msgxs0 = msgxs0 | XS0_WLE | XS0_NEF; /* can't execute */ return (XTC (XS0_WLE | XS0_NEF, TC3)); } return 0; } int32 ts_spacef (UNIT *uptr, int32 fc, t_bool upd) { t_stat st; t_mtrlnt tbc; do { fc = (fc - 1) & DMASK; /* decr wc */ if (upd) msgrfc = fc; if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ return ts_map_status (st); /* map status */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ } while (fc != 0); return 0; } int32 ts_skipf (UNIT *uptr, int32 fc) { t_stat st; t_mtrlnt tbc; t_bool tmkprv = FALSE; msgrfc = fc; if (sim_tape_bot (uptr) && (wchopt & WCH_ENB)) tmkprv = TRUE; do { st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ if (st == MTSE_TMK) { /* tape mark? */ msgrfc = (msgrfc - 1) & DMASK; /* decr count */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ return (XTC ((msgrfc? XS0_RLS: 0) | XS0_TMK | XS0_LET, TC2)); tmkprv = TRUE; /* flag tmk */ } else if (st != MTSE_OK) return ts_map_status (st); else tmkprv = FALSE; /* not a tmk */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ } while (msgrfc != 0); return 0; } int32 ts_spacer (UNIT *uptr, int32 fc, t_bool upd) { int32 st; t_mtrlnt tbc; do { fc = (fc - 1) & DMASK; /* decr wc */ if (upd) msgrfc = fc; if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ return ts_map_status (st); /* map status */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ } while (fc != 0); return 0; } int32 ts_skipr (UNIT *uptr, int32 fc) { t_stat st; t_mtrlnt tbc; t_bool tmkprv = FALSE; msgrfc = fc; do { st = sim_tape_sprecr (uptr, &tbc); /* space rec rev */ if (st == MTSE_TMK) { /* tape mark? */ msgrfc = (msgrfc - 1) & DMASK; /* decr count */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ return (XTC ((msgrfc? XS0_RLS: 0) | XS0_TMK | XS0_LET, TC2)); tmkprv = TRUE; /* flag tmk */ } else if (st != MTSE_OK) return ts_map_status (st); else tmkprv = FALSE; /* not a tmk */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ } while (msgrfc != 0); return 0; } int32 ts_readf (UNIT *uptr, uint32 fc) { t_stat st; t_mtrlnt i, t, tbc, wbc; int32 wa; msgrfc = fc; st = sim_tape_rdrecf (uptr, tsxb, &tbc, MT_MAXFR); /* read rec fwd */ if (st != MTSE_OK) /* error? */ return ts_map_status (st); if (fc == 0) /* byte count */ fc = 0200000; tsba = (cmdadh << 16) | cmdadl; /* buf addr */ wbc = (tbc > fc)? fc: tbc; /* cap buf size */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ if (cmdhdr & CMD_SWP) { /* swapped? */ for (i = 0; i < wbc; i++) { /* copy buffer */ wa = tsba ^ 1; /* apply OPP */ if (Map_WriteB (tsba, 1, &tsxb[i])) { /* store byte, nxm? */ tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ return (XTC (XS0_RLS, TC4)); } tsba = tsba + 1; msgrfc = (msgrfc - 1) & DMASK; } } else { t = Map_WriteB (tsba, wbc, tsxb); /* store record */ tsba = tsba + (wbc - t); /* update tsba */ if (t) { /* nxm? */ tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ return (XTC (XS0_RLS, TC4)); } msgrfc = (msgrfc - (wbc - t)) & DMASK; /* update fc */ } if (msgrfc) /* buf too big? */ return (XTC (XS0_RLS, TC2)); if (tbc > wbc) /* rec too big? */ return (XTC (XS0_RLL, TC2)); return 0; } int32 ts_readr (UNIT *uptr, uint32 fc) { t_stat st; t_mtrlnt i, tbc, wbc; int32 wa; msgrfc = fc; st = sim_tape_rdrecr (uptr, tsxb, &tbc, MT_MAXFR); /* read rec rev */ if (st != MTSE_OK) /* error? */ return ts_map_status (st); if (fc == 0) /* byte count */ fc = 0200000; tsba = (cmdadh << 16) | cmdadl + fc; /* buf addr */ wbc = (tbc > fc)? fc: tbc; /* cap buf size */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ for (i = wbc; i > 0; i--) { /* copy buffer */ tsba = tsba - 1; wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */ if (Map_WriteB (wa, 1, &tsxb[i - 1])) { /* store byte, nxm? */ tssr = ts_updtssr (tssr | TSSR_NXM); return (XTC (XS0_RLS, TC4)); } msgrfc = (msgrfc - 1) & DMASK; } if (msgrfc) /* buf too big? */ return (XTC (XS0_RLS, TC2)); if (tbc > wbc) /* rec too big? */ return (XTC (XS0_RLL, TC2)); return 0; } int32 ts_write (UNIT *uptr, int32 fc) { int32 i, t; uint32 wa; t_stat st; msgrfc = fc; if (fc == 0) /* byte count */ fc = 0200000; tsba = (cmdadh << 16) | cmdadl; /* buf addr */ if (cmdhdr & CMD_SWP) { /* swapped? */ for (i = 0; i < fc; i++) { /* copy mem to buf */ wa = tsba ^ 1; /* apply OPP */ if (Map_ReadB (wa, 1, &tsxb[i])) { /* fetch byte, nxm? */ tssr = ts_updtssr (tssr | TSSR_NXM); return TC5; } tsba = tsba + 1; } } else { t = Map_ReadB (tsba, fc, tsxb); /* fetch record */ tsba = tsba + (fc - t); /* update tsba */ if (t) { /* nxm? */ tssr = ts_updtssr (tssr | TSSR_NXM); return TC5; } } if (st = sim_tape_wrrecf (uptr, tsxb, fc)) /* write rec, err? */ return ts_map_status (st); /* return status */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ msgrfc = 0; if (sim_tape_eot (&ts_unit)) /* EOT on write? */ return XTC (XS0_EOT, TC2); return 0; } int32 ts_wtmk (UNIT *uptr) { t_stat st; if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ return ts_map_status (st); /* return status */ msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ if (sim_tape_eot (&ts_unit)) /* EOT on write? */ return XTC (XS0_EOT, TC2); return XTC (XS0_TMK, TC0); } /* Unit service */ t_stat ts_svc (UNIT *uptr) { int32 i, t, bc, fnc, mod, st0, st1; static const int32 fnc_mod[CMD_N_FNC] = { /* max mod+1 0 ill */ 0, 4, 0, 0, 1, 2, 1, 0, /* 00 - 07 */ 5, 3, 5, 1, 0, 0, 0, 1, /* 10 - 17 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ }; static const int32 fnc_flg[CMD_N_FNC] = { 0, FLG_MO+FLG_AD, 0, 0, 0, FLG_MO+FLG_WR+FLG_AD, FLG_AD, 0, FLG_MO, FLG_MO+FLG_WR, FLG_MO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ }; static const char *fnc_name[CMD_N_FNC] = { "0", "READ", "2", "3", "WCHR", "WRITE", "WSSM", "7", "POS", "FMT", "CTL", "INIT", "14", "15", "16", "GSTA", "20", "21", "22", "23", "24", "25", "26", "27", "30", "31", "32", "33", "34", "35", "36", "37" }; if (ts_bcmd) { /* boot? */ ts_bcmd = 0; /* clear flag */ sim_tape_rewind (uptr); /* rewind */ if (uptr->flags & UNIT_ATT) { /* attached? */ cmdlnt = cmdadh = cmdadl = 0; /* defang rd */ ts_spacef (uptr, 1, FALSE); /* space fwd */ ts_readf (uptr, 512); /* read blk */ tssr = ts_updtssr (tssr | TSSR_SSR); } else tssr = ts_updtssr (tssr | TSSR_SSR | TC3); if (cmdhdr & CMD_IE) SET_INT (TS); return SCPE_OK; } if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */ tssr = ts_updtssr (tssr | TSSR_SSR); /* set rdy, int */ if (cmdhdr & CMD_IE) SET_INT (TS); ts_ownc = ts_ownm = 0; /* CPU owns all */ return SCPE_OK; } fnc = GET_FNC (cmdhdr); /* get fnc+mode */ mod = GET_MOD (cmdhdr); if (DEBUG_PRS (ts_dev)) fprintf (sim_deb, ">>TS: cmd=%s, mod=%o, buf=%o, lnt=%d, pos=%d\n", fnc_name[fnc], mod, cmdadl, cmdlnt, ts_unit.pos); if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */ ts_endcmd (TC3, 0, 0); /* error */ return SCPE_OK; } if (ts_qatn && (wchopt & WCH_EAI)) { /* attn pending? */ ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn msg */ SET_INT (TS); /* set interrupt */ ts_qatn = 0; /* not pending */ return SCPE_OK; } if (cmdhdr & CMD_CVC) /* cvc? clr vck */ msgxs0 = msgxs0 & ~XS0_VCK; if ((cmdhdr & CMD_MBZ) || (mod >= fnc_mod[fnc])) { /* test mbz */ ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); return SCPE_OK; } if ((fnc_flg[fnc] & FLG_MO) && /* mot+(vck|!att)? */ ((msgxs0 & XS0_VCK) || !(uptr->flags & UNIT_ATT))) { ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); return SCPE_OK; } if ((fnc_flg[fnc] & FLG_WR) && /* write? */ sim_tape_wrp (uptr)) { /* write lck? */ ts_endcmd (TC3, XS0_WLE | XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); return SCPE_OK; } if ((((fnc == FNC_READ) && (mod == 1)) || /* read rev */ ((fnc == FNC_POS) && (mod & 1))) && /* space rev */ sim_tape_bot (uptr)) { /* BOT? */ ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); return SCPE_OK; } if ((fnc_flg[fnc] & FLG_AD) && (cmdadh & ADDRTEST)) { /* buf addr > 22b? */ ts_endcmd (TC3, XS0_ILA, MSG_ACK | MSG_MILL | MSG_CFAIL); return SCPE_OK; } st0 = st1 = 0; switch (fnc) { /* case on func */ case FNC_INIT: /* init */ if (!sim_tape_bot (uptr)) /* set if tape moves */ msgxs0 = msgxs0 | XS0_MOT; sim_tape_rewind (uptr); /* rewind */ case FNC_WSSM: /* write mem */ case FNC_GSTA: /* get status */ ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* send end packet */ return SCPE_OK; case FNC_WCHR: /* write char */ if ((cmdadh & ADDRTEST) || (cmdadl & 1) || (cmdlnt < 6)) { ts_endcmd (TSSR_NBA | TC3, XS0_ILA, 0); break; } tsba = (cmdadh << 16) | cmdadl; bc = ((WCH_PLNT << 1) > cmdlnt)? cmdlnt: WCH_PLNT << 1; t = Map_ReadW (tsba, bc, cpy_buf); /* fetch packet */ tsba = tsba + (bc - t); /* inc tsba */ if (t) { /* nxm? */ ts_endcmd (TSSR_NBA | TSSR_NXM | TC5, 0, 0); return SCPE_OK; } for (i = 0; i < (bc / 2); i++) /* copy packet */ tswchp[i] = cpy_buf[i]; if ((wchlnt < ((MSG_PLNT - 1) * 2)) || (wchadh & 0177700) || (wchadl & 1)) ts_endcmd (TSSR_NBA | TC3, 0, 0); else { msgxs2 = msgxs2 | XS2_XTF | 1; tssr = ts_updtssr (tssr & ~TSSR_NBA); ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); } return SCPE_OK; case FNC_CTL: /* control */ switch (mod) { /* case mode */ case 00: /* msg buf rls */ tssr = ts_updtssr (tssr | TSSR_SSR); /* set SSR */ if (wchopt & WCH_ERI) SET_INT (TS); ts_ownc = 0; ts_ownm = 1; /* keep msg */ break; case 01: /* rewind and unload */ if (!sim_tape_bot (uptr)) /* if tape moves */ msgxs0 = msgxs0 | XS0_MOT; sim_tape_detach (uptr); /* unload */ ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); break; case 02: /* clean */ ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* nop */ break; case 03: /* undefined */ ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); return SCPE_OK; case 04: /* rewind */ if (!sim_tape_bot (uptr)) /* if tape moves */ msgxs0 = msgxs0 | XS0_MOT; sim_tape_rewind (uptr); ts_endcmd (TC0, XS0_BOT, MSG_ACK | MSG_CEND); break; } break; case FNC_READ: /* read */ switch (mod) { /* case mode */ case 00: /* fwd */ st0 = ts_readf (uptr, cmdlnt); /* read */ break; case 01: /* back */ st0 = ts_readr (uptr, cmdlnt); /* read */ break; case 02: /* reread fwd */ if (cmdhdr & CMD_OPP) { /* opposite? */ st0 = ts_readr (uptr, cmdlnt); st1 = ts_spacef (uptr, 1, FALSE); } else { st0 = ts_spacer (uptr, 1, FALSE); st1 = ts_readf (uptr, cmdlnt); } break; case 03: /* reread back */ if (cmdhdr & CMD_OPP) { /* opposite */ st0 = ts_readf (uptr, cmdlnt); st1 = ts_spacer (uptr, 1, FALSE); } else { st0 = ts_spacef (uptr, 1, FALSE); st1 = ts_readr (uptr, cmdlnt); } break; } ts_cmpendcmd (st0, st1); break; case FNC_WRIT: /* write */ switch (mod) { /* case mode */ case 00: /* write */ st0 = ts_write (uptr, cmdlnt); break; case 01: /* rewrite */ st0 = ts_spacer (uptr, 1, FALSE); st1 = ts_write (uptr, cmdlnt); break; } ts_cmpendcmd (st0, st1); break; case FNC_FMT: /* format */ switch (mod) { /* case mode */ case 00: /* write tmk */ st0 = ts_wtmk (uptr); break; case 01: /* erase */ break; case 02: /* retry tmk */ st0 = ts_spacer (uptr, 1, FALSE); st1 = ts_wtmk (uptr); break; } ts_cmpendcmd (st0, st1); break; case FNC_POS: /* position */ switch (mod) { /* case mode */ case 00: /* space fwd */ st0 = ts_spacef (uptr, cmdadl, TRUE); break; case 01: /* space rev */ st0 = ts_spacer (uptr, cmdadl, TRUE); break; case 02: /* space ffwd */ st0 = ts_skipf (uptr, cmdadl); break; case 03: /* space frev */ st0 = ts_skipr (uptr, cmdadl); break; case 04: /* rewind */ if (!sim_tape_bot (uptr)) /* if tape moves */ msgxs0 = msgxs0 | XS0_MOT; sim_tape_rewind (uptr); break; } ts_cmpendcmd (st0, 0); break; } return SCPE_OK; } /* Utility routines */ int32 ts_updtssr (int32 t) { t = (t & ~TSSR_EMA) | ((tsba >> (16 - TSSR_V_EMA)) & TSSR_EMA); if (ts_unit.flags & UNIT_ATT) t = t & ~TSSR_OFL; else t = t | TSSR_OFL; return (t & ~TSSR_MBZ); } int32 ts_updxs0 (int32 t) { t = (t & ~(XS0_ONL | XS0_WLK | XS0_BOT | XS0_IE)) | XS0_PET; if (ts_unit.flags & UNIT_ATT) { t = t | XS0_ONL; if (sim_tape_wrp (&ts_unit)) t = t | XS0_WLK; if (sim_tape_bot (&ts_unit)) t = (t | XS0_BOT) & ~XS0_EOT; if (sim_tape_eot (&ts_unit)) t = (t | XS0_EOT) & ~XS0_BOT; } else t = t & ~XS0_EOT; if (cmdhdr & CMD_IE) t = t | XS0_IE; return t; } void ts_cmpendcmd (int32 s0, int32 s1) { int32 xs0, ssr, tc; static const int32 msg[8] = { MSG_ACK | MSG_CEND, MSG_ACK | MSG_MATN | MSG_CATN, MSG_ACK | MSG_CEND, MSG_ACK | MSG_CFAIL, MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR }; xs0 = GET_X (s0) | GET_X (s1); /* or XS0 errs */ s0 = GET_T (s0); /* get SSR errs */ s1 = GET_T (s1); ssr = (s0 | s1) & ~TSSR_TC; /* or SSR errs */ tc = MAX (GET_TC (s0), GET_TC (s1)); /* max term code */ ts_endcmd (ssr | (tc << TSSR_V_TC), xs0, msg[tc]); /* end cmd */ return; } void ts_endcmd (int32 tc, int32 xs0, int32 msg) { int32 i, t; msgxs0 = ts_updxs0 (msgxs0 | xs0); /* update XS0 */ if (wchxopt & WCHX_HDS) /* update XS4 */ msgxs4 = msgxs4 | XS4_HDS; if (msg && !(tssr & TSSR_NBA)) { /* send end pkt */ msghdr = msg; msglnt = wchlnt - 4; /* exclude hdr, bc */ tsba = (wchadh << 16) | wchadl; for (i = 0; (i < MSG_PLNT) && (i < (wchlnt / 2)); i++) cpy_buf[i] = (uint16) tsmsgp[i]; /* copy buffer */ t = Map_WriteW (tsba, i << 1, cpy_buf); /* write to mem */ tsba = tsba + ((i << 1) - t); /* incr tsba */ if (t) { /* nxm? */ tssr = tssr | TSSR_NXM; tc = (tc & ~TSSR_TC) | TC4; } } tssr = ts_updtssr (tssr | tc | TSSR_SSR | (tc? TSSR_SC: 0)); if (cmdhdr & CMD_IE) SET_INT (TS); ts_ownm = 0; ts_ownc = 0; if (DEBUG_PRS (ts_dev)) fprintf (sim_deb, ">>TS: sta=%o, tc=%o, rfc=%d, pos=%d\n", msgxs0, GET_TC (tssr), msgrfc, ts_unit.pos); return; } /* Device reset */ t_stat ts_reset (DEVICE *dptr) { int32 i; sim_tape_rewind (&ts_unit); tsba = tsdbx = 0; ts_ownc = ts_ownm = 0; ts_bcmd = 0; ts_qatn = 0; tssr = ts_updtssr (TSSR_NBA | TSSR_SSR); for (i = 0; i < CMD_PLNT; i++) tscmdp[i] = 0; for (i = 0; i < WCH_PLNT; i++) tswchp[i] = 0; for (i = 0; i < MSG_PLNT; i++) tsmsgp[i] = 0; msgxs0 = ts_updxs0 (XS0_VCK); CLR_INT (TS); if (tsxb == NULL) tsxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); if (tsxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach */ t_stat ts_attach (UNIT *uptr, char *cptr) { t_stat r; r = sim_tape_attach (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; tssr = tssr & ~TSSR_OFL; /* clr offline */ if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) /* attn msg? */ return r; if (ts_ownm) { /* own msg buf? */ ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ SET_INT (TS); /* set interrupt */ ts_qatn = 0; /* don't queue */ } else ts_qatn = 1; /* else queue */ return r; } /* Detach routine */ t_stat ts_detach (UNIT* uptr) { t_stat r; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; r = sim_tape_detach (uptr); /* detach unit */ if (r != SCPE_OK) return r; /* error? */ tssr = tssr | TSSR_OFL; /* set offline */ if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) /* attn msg? */ return r; if (ts_ownm) { /* own msg buf? */ ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ SET_INT (TS); /* set interrupt */ ts_qatn = 0; /* don't queue */ } else ts_qatn = 1; /* else queue */ return r; } /* Boot */ #if defined (VM_PDP11) #define BOOT_START 01000 #define BOOT_CSR0 (BOOT_START + 006) #define BOOT_CSR1 (BOOT_START + 012) #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0012706, 0001000, /* mov #boot_start, sp */ 0012700, 0172520, /* mov #tsba, r0 */ 0012701, 0172522, /* mov #tssr, r1 */ 0005011, /* clr (r1) ; init, rew */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0012710, 0001070, /* mov #pkt1, (r0) ; set char */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0012710, 0001110, /* mov #pkt2, (r0) ; read, skip */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0012710, 0001110, /* mov #pkt2, (r0) ; read */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0005711, /* tst (r1) ; err? */ 0100421, /* bmi hlt */ 0005000, /* clr r0 */ 0012704, 0001066+020, /* mov #sgnt+20, r4 */ 0005007, /* clr r7 */ 0046523, /* sgnt: "SM" */ 0140004, /* pkt1: 140004, wcpk, 0, 8. */ 0001100, 0000000, 0000010, 0001122, /* wcpk: msg, 0, 14., 0 */ 0000000, 0000016, 0000000, 0140001, /* pkt2: 140001, 0, 0, 512. */ 0000000, 0000000, 0001000, 0000000 /* hlt: halt */ /* msg: .blk 4 */ }; t_stat ts_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; sim_tape_rewind (&ts_unit); for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_CSR0 >> 1] = ts_dib.ba & DMASK; M[BOOT_CSR1 >> 1] = (ts_dib.ba & DMASK) + 02; saved_PC = BOOT_START; return SCPE_OK; } #else t_stat ts_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif simh-3.8.1/PDP11/pdp11_tm.c0000644000175000017500000007303011110157574013224 0ustar vlmvlm/* pdp11_tm.c: PDP-11 magnetic tape simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tm TM11/TU10 magtape 16-Feb-06 RMS Added tape capacity checking 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 18-Mar-05 RMS Added attached test to detach routine 07-Dec-04 RMS Added read-only file support 30-Sep-04 RMS Revised Unibus interface 25-Jan-04 RMS Revised for device debug support 29-Dec-03 RMS Added 18b Qbus support 25-Apr-03 RMS Revised for extended file support 28-Mar-03 RMS Added multiformat support 28-Feb-03 RMS Revised for magtape library, added logging 30-Oct-02 RMS Revised BOT handling, added error record handling 30-Sep-02 RMS Added variable address support to bootstrap Added vector change/display support Changed mapping mnemonics New data structures Updated error handling 28-Aug-02 RMS Added end of medium support 30-May-02 RMS Widened POS to 32b 22-Apr-02 RMS Fixed max record length, first block bootstrap (found by Jonathan Engdahl) 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Converted UST, POS, FLG to arrays 09-Nov-01 RMS Added bus map support 18-Oct-01 RMS Added stub diagnostic register (found by Thord Nilson) 07-Sep-01 RMS Revised device disable and interrupt mechanisms 26-Apr-01 RMS Added device enable/disable support 18-Apr-01 RMS Changed to rewind tape before boot 14-Apr-99 RMS Changed t_addr to unsigned 04-Oct-98 RMS V2.4 magtape format 10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz) 09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz) 10-Apr-98 RMS Added 2nd block bootstrap (from John Holden, University of Sydney) 31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State) 22-Jan-97 RMS V2.3 magtape format 18-Jan-97 RMS Fixed double interrupt, error flag bugs 29-Jun-96 RMS Added unit disable support Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. */ #include "pdp11_defs.h" #include "sim_tape.h" #define TM_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ /* Command - tm_cmd */ #define MTC_ERR (1 << CSR_V_ERR) /* error */ #define MTC_V_DEN 13 /* density */ #define MTC_M_DEN 03 #define MTC_DEN (MTC_M_DEN << MTC_V_DEN) #define MTC_INIT 0010000 /* init */ #define MTC_LPAR 0004000 /* parity select */ #define MTC_V_UNIT 8 /* unit */ #define MTC_M_UNIT 07 #define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT) #define MTC_DONE (1 << CSR_V_DONE) /* done */ #define MTC_IE (1 << CSR_V_IE) /* interrupt enable */ #define MTC_V_EMA 4 /* ext mem address */ #define MTC_M_EMA 03 #define MTC_EMA (MTC_M_EMA << MTC_V_EMA) #define MTC_V_FNC 1 /* function */ #define MTC_M_FNC 07 #define MTC_UNLOAD 00 #define MTC_READ 01 #define MTC_WRITE 02 #define MTC_WREOF 03 #define MTC_SPACEF 04 #define MTC_SPACER 05 #define MTC_WREXT 06 #define MTC_REWIND 07 #define MTC_FNC (MTC_M_FNC << MTC_V_FNC) #define MTC_GO (1 << CSR_V_GO) /* go */ #define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \ MTC_EMA | MTC_FNC) #define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA)) #define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT) #define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC) /* Status - stored in tm_sta or (*) uptr->USTAT or (+) calculated */ #define STA_ILL 0100000 /* illegal */ #define STA_EOF 0040000 /* *end of file */ #define STA_CRC 0020000 /* CRC error */ #define STA_PAR 0010000 /* parity error */ #define STA_DLT 0004000 /* data late */ #define STA_EOT 0002000 /* +end of tape */ #define STA_RLE 0001000 /* rec lnt error */ #define STA_BAD 0000400 /* bad tape error */ #define STA_NXM 0000200 /* non-existent mem */ #define STA_ONL 0000100 /* *online */ #define STA_BOT 0000040 /* *start of tape */ #define STA_7TK 0000020 /* 7 track */ #define STA_SDN 0000010 /* settle down */ #define STA_WLK 0000004 /* *write locked */ #define STA_REW 0000002 /* *rewinding */ #define STA_TUR 0000001 /* +unit ready */ #define STA_CLR (STA_7TK | STA_SDN) /* always clear */ #define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \ STA_WLK | STA_REW | STA_TUR) /* dynamic */ #define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \ STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM) /* set error */ /* Read lines - tm_rdl */ #define RDL_CLK 0100000 /* 10 Khz clock */ extern uint16 *M; /* memory */ extern int32 int_req[IPL_HLVL]; extern FILE *sim_deb; uint8 *tmxb = NULL; /* xfer buffer */ int32 tm_sta = 0; /* status register */ int32 tm_cmd = 0; /* command register */ int32 tm_ca = 0; /* current address */ int32 tm_bc = 0; /* byte count */ int32 tm_db = 0; /* data buffer */ int32 tm_rdl = 0; /* read lines */ int32 tm_time = 10; /* record latency */ int32 tm_stopioe = 1; /* stop on error */ DEVICE tm_dev; t_stat tm_rd (int32 *data, int32 PA, int32 access); t_stat tm_wr (int32 data, int32 PA, int32 access); t_stat tm_svc (UNIT *uptr); t_stat tm_reset (DEVICE *dptr); t_stat tm_attach (UNIT *uptr, char *cptr); t_stat tm_detach (UNIT *uptr); t_stat tm_boot (int32 unitno, DEVICE *dptr); void tm_go (UNIT *uptr); int32 tm_updcsta (UNIT *uptr); void tm_set_done (void); t_stat tm_map_err (UNIT *uptr, t_stat st); t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); /* MT data structures tm_dev MT device descriptor tm_unit MT unit list tm_reg MT register list tm_mod MT modifier list */ DIB tm_dib = { IOBA_TM, IOLN_TM, &tm_rd, &tm_wr, 1, IVCL (TM), VEC_TM, { NULL } }; UNIT tm_unit[] = { { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) } }; REG tm_reg[] = { { ORDATA (MTS, tm_sta, 16) }, { ORDATA (MTC, tm_cmd, 16) }, { ORDATA (MTBRC, tm_bc, 16) }, { ORDATA (MTCMA, tm_ca, 16) }, { ORDATA (MTD, tm_db, 8) }, { ORDATA (MTRD, tm_rdl, 16) }, { FLDATA (INT, IREQ (TM), INT_V_TM) }, { FLDATA (ERR, tm_cmd, CSR_V_ERR) }, { FLDATA (DONE, tm_cmd, CSR_V_DONE) }, { FLDATA (IE, tm_cmd, CSR_V_IE) }, { FLDATA (STOP_IOE, tm_stopioe, 0) }, { DRDATA (TIME, tm_time, 24), PV_LEFT }, { URDATA (UST, tm_unit[0].USTAT, 8, 16, 0, TM_NUMDR, 0) }, { URDATA (POS, tm_unit[0].pos, 10, T_ADDR_W, 0, TM_NUMDR, PV_LEFT | REG_RO) }, { ORDATA (DEVADDR, tm_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, tm_dib.vec, 16), REG_HRO }, { NULL } }; MTAB tm_mod[] = { { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &tm_vlock }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &tm_vlock }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE tm_dev = { "TM", tm_unit, tm_reg, tm_mod, TM_NUMDR, 10, T_ADDR_W, 1, 8, 8, NULL, NULL, &tm_reset, &tm_boot, &tm_attach, &tm_detach, &tm_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG }; /* I/O dispatch routines, I/O addresses 17772520 - 17772532 17772520 MTS read only, constructed from tm_sta plus current drive status flags 17772522 MTC read/write 17772524 MTBRC read/write 17772526 MTCMA read/write 17772530 MTD read/write 17772532 MTRD read only */ t_stat tm_rd (int32 *data, int32 PA, int32 access) { UNIT *uptr; uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */ switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* MTS */ *data = tm_updcsta (uptr); /* update status */ break; case 1: /* MTC */ tm_updcsta (uptr); /* update status */ *data = tm_cmd; /* return command */ break; case 2: /* MTBRC */ *data = tm_bc; /* return byte count */ break; case 3: /* MTCMA */ *data = tm_ca; /* return mem addr */ break; case 4: /* MTD */ *data = tm_db; /* return data buffer */ break; case 5: /* MTRD */ tm_rdl = tm_rdl ^ RDL_CLK; /* "clock" ticks */ *data = tm_rdl; break; default: /* unimplemented */ *data = 0; break; } return SCPE_OK; } t_stat tm_wr (int32 data, int32 PA, int32 access) { UNIT *uptr; switch ((PA >> 1) & 07) { /* decode PA<3:1> */ case 0: /* MTS: read only */ break; case 1: /* MTC */ uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */ if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL; else { if (access == WRITEB) data = (PA & 1)? (tm_cmd & 0377) | (data << 8): (tm_cmd & ~0377) | data; if (data & MTC_INIT) { /* init? */ tm_reset (&tm_dev); /* reset device */ return SCPE_OK; } if ((data & MTC_IE) == 0) /* int disable? */ CLR_INT (TM); /* clr int request */ else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE)) SET_INT (TM); /* set int request */ tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW); uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */ if (data & MTC_GO) /* new function? */ tm_go (uptr); } tm_updcsta (uptr); /* update status */ break; case 2: /* MTBRC */ if (access == WRITEB) data = (PA & 1)? (tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data; tm_bc = data; break; case 3: /* MTCMA */ if (access == WRITEB) data = (PA & 1)? (tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data; tm_ca = data; break; case 4: /* MTD */ if ((access == WRITEB) && (PA & 1)) return SCPE_OK; tm_db = data & 0377; break; } /* end switch */ return SCPE_OK; } /* New magtape command */ void tm_go (UNIT *uptr) { int32 f; f = GET_FNC (tm_cmd); /* get function */ if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ sim_is_active (uptr) || /* busy? */ (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) && sim_tape_wrp (uptr))) { /* write locked? */ tm_sta = tm_sta | STA_ILL; /* illegal */ tm_set_done (); /* set done */ return; } uptr->USTAT = uptr->USTAT & (STA_WLK | STA_ONL); /* clear status */ tm_sta = 0; /* clear errors */ if (f == MTC_UNLOAD) { /* unload? */ uptr->USTAT = (uptr->USTAT | STA_REW) & ~STA_ONL; detach_unit (uptr); /* set offline */ } else if (f == MTC_REWIND) /* rewind */ uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */ /* else /* uncomment this else if rewind/unload don't set done */ tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */ CLR_INT (TM); /* clear int */ sim_activate (uptr, tm_time); /* start io */ return; } /* Unit service If rewind done, reposition to start of tape, set status else, do operation, set done, interrupt */ t_stat tm_svc (UNIT *uptr) { int32 f, t, u; uint32 xma; t_mtrlnt tbc, cbc; t_stat st, r = SCPE_OK; u = (int32) (uptr - tm_dev.units); /* get unit number */ f = GET_FNC (tm_cmd); /* get command */ xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */ cbc = 0200000 - tm_bc; /* get bc */ if (uptr->USTAT & STA_REW) { /* rewind? */ sim_tape_rewind (uptr); /* update position */ if (uptr->flags & UNIT_ATT) /* still on line? */ uptr->USTAT = STA_ONL | STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); else uptr->USTAT = 0; if (u == GET_UNIT (tm_cmd)) { /* selected? */ tm_set_done (); /* set done */ tm_updcsta (uptr); /* update status */ } return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ uptr->USTAT = 0; /* unit off line */ tm_sta = tm_sta | STA_ILL; /* illegal operation */ tm_set_done (); /* set done */ tm_updcsta (uptr); /* update status */ return IORETURN (tm_stopioe, SCPE_UNATT); } if (DEBUG_PRS (tm_dev)) fprintf (sim_deb, ">>TM: op=%o, ma=%o, bc=%o, pos=%d\n", f, xma, tm_bc, uptr->pos); switch (f) { /* case on function */ case MTC_READ: /* read */ st = sim_tape_rdrecf (uptr, tmxb, &tbc, MT_MAXFR); /* read rec */ if (st == MTSE_RECE) /* rec in error? */ tm_sta = tm_sta | STA_PAR; else if (st != MTSE_OK) { /* other error? */ r = tm_map_err (uptr, st); /* map error */ break; } if (tbc > cbc) /* wrong size? */ tm_sta = tm_sta | STA_RLE; if (tbc < cbc) /* use smaller */ cbc = tbc; if (t = Map_WriteB (xma, cbc, tmxb)) { /* copy buf to mem */ tm_sta = tm_sta | STA_NXM; /* NXM, set err */ cbc = cbc - t; /* adj byte cnt */ } xma = (xma + cbc) & 0777777; /* inc bus addr */ tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */ break; case MTC_WRITE: /* write */ case MTC_WREXT: /* write ext gap */ if (t = Map_ReadB (xma, cbc, tmxb)) { /* copy mem to buf */ tm_sta = tm_sta | STA_NXM; /* NXM, set err */ cbc = cbc - t; /* adj byte cnt */ if (cbc == 0) /* no xfr? done */ break; } if (st = sim_tape_wrrecf (uptr, tmxb, cbc)) /* write rec, err? */ r = tm_map_err (uptr, st); /* map error */ else { xma = (xma + cbc) & 0777777; /* inc bus addr */ tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */ } break; case MTC_WREOF: /* write eof */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = tm_map_err (uptr, st); /* map error */ break; case MTC_SPACEF: /* space forward */ do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* spc rec fwd, err? */ r = tm_map_err (uptr, st); /* map error */ break; } } while (tm_bc != 0); break; case MTC_SPACER: /* space reverse */ do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* spc rec rev, err? */ r = tm_map_err (uptr, st); /* map error */ break; } } while (tm_bc != 0); break; } /* end case */ tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA); tm_ca = xma & 0177777; /* update mem addr */ tm_set_done (); /* set done */ tm_updcsta (uptr); /* update status */ if (DEBUG_PRS (tm_dev)) fprintf (sim_deb, ">>TM: sta=%o, ma=%o, bc=%o, pos=%d\n", tm_sta, tm_ca, tm_bc, uptr->pos); return r; } /* Update controller status */ int32 tm_updcsta (UNIT *uptr) { tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN); if (sim_tape_eot (uptr)) tm_sta = tm_sta | STA_EOT; if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR; else tm_sta = tm_sta | STA_TUR; if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR; else tm_cmd = tm_cmd & ~MTC_ERR; if ((tm_cmd & MTC_IE) == 0) CLR_INT (TM); return tm_sta; } /* Set done */ void tm_set_done (void) { tm_cmd = tm_cmd | MTC_DONE; if (tm_cmd & MTC_IE) SET_INT (TM); return; } /* Map tape error status */ t_stat tm_map_err (UNIT *uptr, t_stat st) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ tm_sta = tm_sta | STA_ILL; case MTSE_OK: /* no error */ return SCPE_IERR; case MTSE_TMK: /* tape mark */ uptr->USTAT = uptr->USTAT | STA_EOF; /* end of file */ break; case MTSE_IOERR: /* IO error */ tm_sta = tm_sta | STA_PAR; /* parity error */ if (tm_stopioe) return SCPE_IOERR; break; case MTSE_INVRL: /* invalid rec lnt */ tm_sta = tm_sta | STA_PAR; /* parity error */ return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ tm_sta = tm_sta | STA_PAR; /* parity error */ break; case MTSE_EOM: /* end of medium */ tm_sta = tm_sta | STA_BAD; /* bad tape */ break; case MTSE_BOT: /* reverse into BOT */ uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ break; case MTSE_WRP: /* write protect */ tm_sta = tm_sta | STA_ILL; /* illegal operation */ break; } return SCPE_OK; } /* Reset routine */ t_stat tm_reset (DEVICE *dptr) { int32 u; UNIT *uptr; tm_cmd = MTC_DONE; /* set done */ tm_bc = tm_ca = tm_db = tm_sta = tm_rdl = 0; CLR_INT (TM); /* clear interrupt */ for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */ uptr = tm_dev.units + u; sim_tape_reset (uptr); /* reset tape */ sim_cancel (uptr); /* cancel activity */ if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_ONL | (sim_tape_bot (uptr)? STA_BOT: 0) | (sim_tape_wrp (uptr)? STA_WLK: 0); else uptr->USTAT = 0; } if (tmxb == NULL) tmxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); if (tmxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat tm_attach (UNIT *uptr, char *cptr) { t_stat r; int32 u = uptr - tm_dev.units; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->USTAT = STA_ONL | STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); return r; } /* Detach routine */ t_stat tm_detach (UNIT* uptr) { int32 u = uptr - tm_dev.units; if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; if (!sim_is_active (uptr)) uptr->USTAT = 0; if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); return sim_tape_detach (uptr); } /* Write lock/enable routine */ t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 u = uptr - tm_dev.units; if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) uptr->USTAT = uptr->USTAT | STA_WLK; else uptr->USTAT = uptr->USTAT & ~STA_WLK; if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); return SCPE_OK; } /* Device bootstrap Magtape boot format changed over time. Originally, a boot tape contained a boot loader in the first block. Eventually, the first block was reserved for a tape label, and the second block was expected to contain a boot loader. BSD and DEC operating systems use the second block scheme, so it is the default. To boot from the first block, use boot -o (old). */ #define BOOT_START 016000 #define BOOT_ENTRY (BOOT_START + 2) #define BOOT_UNIT (BOOT_START + 010) #define BOOT_CSR (BOOT_START + 014) #define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int16)) #define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16)) static const uint16 boot1_rom[] = { 0046524, /* boot_start: "TM" */ 0012706, BOOT_START, /* mov #boot_start, sp */ 0012700, 0000000, /* mov #unit_num, r0 */ 0012701, 0172526, /* mov #172526, r1 ; mtcma */ 0005011, /* clr (r1) */ 0010141, /* mov r1, -(r1) ; mtbrc */ 0010002, /* mov r0,r2 */ 0000302, /* swab r2 */ 0062702, 0060003, /* add #60003, r2 */ 0010241, /* mov r2, -(r1) ; read + go */ 0105711, /* tstb (r1) ; mtc */ 0100376, /* bpl .-2 */ 0005002, /* clr r2 */ 0005003, /* clr r3 */ 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */ 0005005, /* clr r5 */ 0005007 /* clr r7 */ }; static const uint16 boot2_rom[] = { 0046524, /* boot_start: "TM" */ 0012706, BOOT_START, /* mov #boot_start, sp */ 0012700, 0000000, /* mov #unit_num, r0 */ 0012701, 0172526, /* mov #172526, r1 ; mtcma */ 0005011, /* clr (r1) */ 0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */ 0010002, /* mov r0,r2 */ 0000302, /* swab r2 */ 0062702, 0060011, /* add #60011, r2 */ 0010241, /* mov r2, -(r1) ; space + go */ 0105711, /* tstb (r1) ; mtc */ 0100376, /* bpl .-2 */ 0010002, /* mov r0,r2 */ 0000302, /* swab r2 */ 0062702, 0060003, /* add #60003, r2 */ 0010211, /* mov r2, (r1) ; read + go */ 0105711, /* tstb (r1) ; mtc */ 0100376, /* bpl .-2 */ 0005002, /* clr r2 */ 0005003, /* clr r3 */ 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */ 0005005, /* clr r5 */ 0005007 /* clr r7 */ }; t_stat tm_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern int32 sim_switches; sim_tape_rewind (&tm_unit[unitno]); if (sim_switches & SWMASK ('O')) { for (i = 0; i < BOOT1_LEN; i++) M[(BOOT_START >> 1) + i] = boot1_rom[i]; } else { for (i = 0; i < BOOT2_LEN; i++) M[(BOOT_START >> 1) + i] = boot2_rom[i]; } M[BOOT_UNIT >> 1] = unitno; M[BOOT_CSR >> 1] = (tm_dib.ba & DMASK) + 06; saved_PC = BOOT_ENTRY; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_rl.c0000644000175000017500000007307011111053056013214 0ustar vlmvlm/* pdp11_rl.c: RL11 (RLV12) cartridge disk simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rl RL11(RLV12)/RL01/RL02 cartridge disk 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 07-Jul-05 RMS Removed extraneous externs 30-Sep-04 RMS Revised Unibus interface 04-Jan-04 RMS Changed sim_fsize calling sequence 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 29-Sep-02 RMS Added variable address support to bootstrap Added vector change/display support Revised mapping nomenclature New data structures 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Nov-01 RMS Added read only, extended SET/SHOW support 26-Nov-01 RMS Fixed per-drive error handling 24-Nov-01 RMS Converted FLG, CAPAC to arrays 19-Nov-01 RMS Fixed signed/unsigned mismatch in write check 09-Nov-01 RMS Added bus map, VAX support 07-Sep-01 RMS Revised device disable and interrupt mechanisms 20-Aug-01 RMS Added bad block option in attach 17-Jul-01 RMS Fixed warning from VC++ 6.0 26-Apr-01 RMS Added device enable/disable support 25-Mar-01 RMS Fixed block fill calculation 15-Feb-01 RMS Corrected bootstrap string 12-Nov-97 RMS Added bad block table command 25-Nov-96 RMS Default units to autosize 29-Jun-96 RMS Added unit disable support The RL11 is a four drive cartridge disk subsystem. An RL01 drive consists of 256 cylinders, each with 2 surfaces containing 40 sectors of 256 bytes. An RL02 drive has 512 cylinders. The RLV12 is a controller variant which supports 22b direct addressing. The most complicated part of the RL11 controller is the way it does seeks. Seeking is relative to the current disk address; this requires keeping accurate track of the current cylinder. The RL11 will not switch heads or cross cylinders during transfers. The RL11 functions in three environments: - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to go through the I/O map - PDP-11 Unibus 22b systems - the RL11 behaves as an 18b Unibus peripheral and must go through the I/O map - VAX Q22 systems - the RL11 must go through the I/O map */ #if defined (VM_PDP10) /* PDP10 version */ #error "RL11 is not supported on the PDP-10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #else /* PDP-11 version */ #include "pdp11_defs.h" extern uint32 cpu_opt; #endif /* Constants */ #define RL_NUMWD 128 /* words/sector */ #define RL_NUMSC 40 /* sectors/surface */ #define RL_NUMSF 2 /* surfaces/cylinder */ #define RL_NUMCY 256 /* cylinders/drive */ #define RL_NUMDR 4 /* drives/controller */ #define RL_MAXFR (1 << 16) /* max transfer */ #define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD) /* words/drive */ #define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ /* Flags in the unit flags word */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */ #define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ #define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ #define UNIT_DUMMY (1 << UNIT_V_DUMMY) #define UNIT_WLK (1u << UNIT_V_WLK) #define UNIT_RL02 (1u << UNIT_V_RL02) #define UNIT_AUTO (1u << UNIT_V_AUTO) #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protected */ /* Parameters in the unit descriptor */ #define TRK u3 /* current track */ #define STAT u4 /* status */ /* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ #define RLDS_LOAD 0 /* no cartridge */ #define RLDS_LOCK 5 /* lock on */ #define RLDS_BHO 0000010 /* brushes home NI */ #define RLDS_HDO 0000020 /* heads out NI */ #define RLDS_CVO 0000040 /* cover open NI */ #define RLDS_HD 0000100 /* head select ^ */ #define RLDS_RL02 0000200 /* RL02 */ #define RLDS_DSE 0000400 /* drv sel err NI */ #define RLDS_VCK 0001000 /* vol check * */ #define RLDS_WGE 0002000 /* wr gate err * */ #define RLDS_SPE 0004000 /* spin err * */ #define RLDS_STO 0010000 /* seek time out NI */ #define RLDS_WLK 0020000 /* wr locked */ #define RLDS_HCE 0040000 /* hd curr err NI */ #define RLDS_WDE 0100000 /* wr data err NI */ #define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ #define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ #define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ RLDS_VCK+RLDS_DSE) /* errors bits */ /* RLCS */ #define RLCS_DRDY 0000001 /* drive ready */ #define RLCS_M_FUNC 0000007 /* function */ #define RLCS_NOP 0 #define RLCS_WCHK 1 #define RLCS_GSTA 2 #define RLCS_SEEK 3 #define RLCS_RHDR 4 #define RLCS_WRITE 5 #define RLCS_READ 6 #define RLCS_RNOHDR 7 #define RLCS_V_FUNC 1 #define RLCS_M_MEX 03 /* memory extension */ #define RLCS_V_MEX 4 #define RLCS_MEX (RLCS_M_MEX << RLCS_V_MEX) #define RLCS_M_DRIVE 03 #define RLCS_V_DRIVE 8 #define RLCS_INCMP 0002000 /* incomplete */ #define RLCS_CRC 0004000 /* CRC error */ #define RLCS_HDE 0010000 /* header error */ #define RLCS_NXM 0020000 /* non-exist memory */ #define RLCS_DRE 0040000 /* drive error */ #define RLCS_ERR 0100000 /* error summary */ #define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP) #define RLCS_RW 0001776 /* read/write */ #define GET_FUNC(x) (((x) >> RLCS_V_FUNC) & RLCS_M_FUNC) #define GET_DRIVE(x) (((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE) /* RLDA */ #define RLDA_SK_DIR 0000004 /* direction */ #define RLDA_GS_CLR 0000010 /* clear errors */ #define RLDA_SK_HD 0000020 /* head select */ #define RLDA_V_SECT 0 /* sector */ #define RLDA_M_SECT 077 #define RLDA_V_TRACK 6 /* track */ #define RLDA_M_TRACK 01777 #define RLDA_HD0 (0 << RLDA_V_TRACK) #define RLDA_HD1 (1u << RLDA_V_TRACK) #define RLDA_V_CYL 7 /* cylinder */ #define RLDA_M_CYL 0777 #define RLDA_TRACK (RLDA_M_TRACK << RLDA_V_TRACK) #define RLDA_CYL (RLDA_M_CYL << RLDA_V_CYL) #define GET_SECT(x) (((x) >> RLDA_V_SECT) & RLDA_M_SECT) #define GET_CYL(x) (((x) >> RLDA_V_CYL) & RLDA_M_CYL) #define GET_TRACK(x) (((x) >> RLDA_V_TRACK) & RLDA_M_TRACK) #define GET_DA(x) ((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x)) /* RLBA */ #define RLBA_IMP 0177776 /* implemented */ /* RLBAE */ #define RLBAE_IMP 0000077 /* implemented */ extern int32 int_req[IPL_HLVL]; uint16 *rlxb = NULL; /* xfer buffer */ int32 rlcs = 0; /* control/status */ int32 rlba = 0; /* memory address */ int32 rlbae = 0; /* mem addr extension */ int32 rlda = 0; /* disk addr */ int32 rlmp = 0, rlmp1 = 0, rlmp2 = 0; /* mp register queue */ int32 rl_swait = 10; /* seek wait */ int32 rl_rwait = 10; /* rotate wait */ int32 rl_stopioe = 1; /* stop on error */ DEVICE rl_dev; t_stat rl_rd (int32 *data, int32 PA, int32 access); t_stat rl_wr (int32 data, int32 PA, int32 access); t_stat rl_svc (UNIT *uptr); t_stat rl_reset (DEVICE *dptr); void rl_set_done (int32 error); t_stat rl_boot (int32 unitno, DEVICE *dptr); t_stat rl_attach (UNIT *uptr, char *cptr); t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); /* RL11 data structures rl_dev RL device descriptor rl_unit RL unit list rl_reg RL register list rl_mod RL modifier list */ DIB rl_dib = { IOBA_RL, IOLN_RL, &rl_rd, &rl_wr, 1, IVCL (RL), VEC_RL, { NULL } }; UNIT rl_unit[] = { { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) } }; REG rl_reg[] = { { GRDATA (RLCS, rlcs, DEV_RDX, 16, 0) }, { GRDATA (RLDA, rlda, DEV_RDX, 16, 0) }, { GRDATA (RLBA, rlba, DEV_RDX, 16, 0) }, { GRDATA (RLBAE, rlbae, DEV_RDX, 6, 0) }, { GRDATA (RLMP, rlmp, DEV_RDX, 16, 0) }, { GRDATA (RLMP1, rlmp1, DEV_RDX, 16, 0) }, { GRDATA (RLMP2, rlmp2, DEV_RDX, 16, 0) }, { FLDATA (INT, IREQ (RL), INT_V_RL) }, { FLDATA (ERR, rlcs, CSR_V_ERR) }, { FLDATA (DONE, rlcs, CSR_V_DONE) }, { FLDATA (IE, rlcs, CSR_V_IE) }, { DRDATA (STIME, rl_swait, 24), PV_LEFT }, { DRDATA (RTIME, rl_rwait, 24), PV_LEFT }, { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0, RL_NUMDR, PV_LEFT + REG_HRO) }, { FLDATA (STOP_IOE, rl_stopioe, 0) }, { GRDATA (DEVADDR, rl_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rl_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB rl_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE rl_dev = { "RL", rl_unit, rl_reg, rl_mod, RL_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16, NULL, NULL, &rl_reset, &rl_boot, &rl_attach, NULL, &rl_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS }; /* I/O dispatch routines, I/O addresses 17774400 - 17774407 17774400 RLCS read/write 17774402 RLBA read/write 17774404 RLDA read/write 17774406 RLMP read/write 17774410 RLBAE read/write */ t_stat rl_rd (int32 *data, int32 PA, int32 access) { UNIT *uptr; switch ((PA >> 1) & 07) { /* decode PA<2:1> */ case 0: /* RLCS */ rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; uptr = rl_dev.units + GET_DRIVE (rlcs); if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; else rlcs = rlcs | RLCS_DRDY; /* see if ready */ *data = rlcs; break; case 1: /* RLBA */ *data = rlba & RLBA_IMP; break; case 2: /* RLDA */ *data = rlda; break; case 3: /* RLMP */ *data = rlmp; rlmp = rlmp1; /* ripple data */ rlmp1 = rlmp2; break; case 4: /* RLBAE */ if (UNIBUS) /* not in RL11 */ return SCPE_NXM; *data = rlbae & RLBAE_IMP; break; } /* end switch */ return SCPE_OK; } t_stat rl_wr (int32 data, int32 PA, int32 access) { int32 curr, offs, newc, maxc; UNIT *uptr; switch ((PA >> 1) & 07) { /* decode PA<2:1> */ case 0: /* RLCS */ rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; uptr = rl_dev.units + GET_DRIVE (data); /* get new drive */ if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; else rlcs = rlcs | RLCS_DRDY; /* see if ready */ if (access == WRITEB) data = (PA & 1)? (rlcs & 0377) | (data << 8): (rlcs & ~0377) | data; rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW); rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX); if (data & CSR_DONE) { /* ready set? */ if ((data & CSR_IE) == 0) CLR_INT (RL); else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE) SET_INT (RL); return SCPE_OK; } CLR_INT (RL); /* clear interrupt */ rlcs = rlcs & ~RLCS_ALLERR; /* clear errors */ switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */ case RLCS_NOP: /* nop */ rl_set_done (0); break; case RLCS_SEEK: /* seek */ curr = GET_CYL (uptr->TRK); /* current cylinder */ offs = GET_CYL (rlda); /* offset */ if (rlda & RLDA_SK_DIR) { /* in or out? */ newc = curr + offs; /* out */ maxc = (uptr->flags & UNIT_RL02)? RL_NUMCY * 2: RL_NUMCY; if (newc >= maxc) newc = maxc - 1; } else { newc = curr - offs; /* in */ if (newc < 0) newc = 0; } uptr->TRK = (newc << RLDA_V_CYL) | /* put on track */ ((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0); sim_activate (uptr, rl_swait * abs (newc - curr)); break; default: /* data transfer */ sim_activate (uptr, rl_swait); /* activate unit */ break; } /* end switch func */ break; /* end case RLCS */ case 1: /* RLBA */ if (access == WRITEB) data = (PA & 1)? (rlba & 0377) | (data << 8): (rlba & ~0377) | data; rlba = data & RLBA_IMP; break; case 2: /* RLDA */ if (access == WRITEB) data = (PA & 1)? (rlda & 0377) | (data << 8): (rlda & ~0377) | data; rlda = data; break; case 3: /* RLMP */ if (access == WRITEB) data = (PA & 1)? (rlmp & 0377) | (data << 8): (rlmp & ~0377) | data; rlmp = rlmp1 = rlmp2 = data; break; case 4: /* RLBAE */ if (UNIBUS) /* not in RL11 */ return SCPE_NXM; if (PA & 1) return SCPE_OK; rlbae = data & RLBAE_IMP; rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); break; } /* end switch */ return SCPE_OK; } /* Service unit timeout If seek in progress, complete seek command Else complete data transfer command The unit control block contains the function and cylinder for the current command. */ t_stat rl_svc (UNIT *uptr) { int32 err, wc, maxwc, t; int32 i, func, da, awc; uint32 ma; uint16 comp; func = GET_FUNC (rlcs); /* get function */ if (func == RLCS_GSTA) { /* get status */ if (rlda & RLDA_GS_CLR) uptr->STAT = uptr->STAT & ~RLDS_ERR; rlmp = uptr->STAT | (uptr->TRK & RLDS_HD) | ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); if (uptr->flags & UNIT_RL02) rlmp = rlmp | RLDS_RL02; if (uptr->flags & UNIT_WPRT) rlmp = rlmp | RLDS_WLK; rlmp2 = rlmp1 = rlmp; rl_set_done (0); /* done */ return SCPE_OK; } if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ rlcs = rlcs & ~RLCS_DRDY; /* clear drive ready */ uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ rl_set_done (RLCS_ERR | RLCS_INCMP); /* flag error */ return IORETURN (rl_stopioe, SCPE_UNATT); } if ((func == RLCS_WRITE) && (uptr->flags & UNIT_WPRT)) { uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ rl_set_done (RLCS_ERR | RLCS_DRE); return SCPE_OK; } if (func == RLCS_SEEK) { /* seek? */ rl_set_done (0); /* done */ return SCPE_OK; } if (func == RLCS_RHDR) { /* read header? */ rlmp = (uptr->TRK & RLDA_TRACK) | GET_SECT (rlda); rlmp1 = rlmp2 = 0; rl_set_done (0); /* done */ return SCPE_OK; } if (((func != RLCS_RNOHDR) && ((uptr->TRK & RLDA_CYL) != (rlda & RLDA_CYL))) || (GET_SECT (rlda) >= RL_NUMSC)) { /* bad cyl or sector? */ rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP); /* wrong cylinder? */ return SCPE_OK; } ma = (rlbae << 16) | rlba; /* get mem addr */ da = GET_DA (rlda) * RL_NUMWD; /* get disk addr */ wc = 0200000 - rlmp; /* get true wc */ maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD; /* max transfer */ if (wc > maxwc) /* track overrun? */ wc = maxwc; err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */ i = fxread (rlxb, sizeof (int16), wc, uptr->fileref); err = ferror (uptr->fileref); for ( ; i < wc; i++) /* fill buffer */ rlxb[i] = 0; if (t = Map_WriteW (ma, wc << 1, rlxb)) { /* store buffer */ rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ wc = wc - t; /* adjust wc */ } } /* end read */ if ((func == RLCS_WRITE) && (err == 0)) { /* write? */ if (t = Map_ReadW (ma, wc << 1, rlxb)) { /* fetch buffer */ rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ wc = wc - t; /* adj xfer lnt */ } if (wc) { /* any xfer? */ awc = (wc + (RL_NUMWD - 1)) & ~(RL_NUMWD - 1); /* clr to */ for (i = wc; i < awc; i++) /* end of blk */ rlxb[i] = 0; fxwrite (rlxb, sizeof (int16), awc, uptr->fileref); err = ferror (uptr->fileref); } } /* end write */ if ((func == RLCS_WCHK) && (err == 0)) { /* write check? */ i = fxread (rlxb, sizeof (int16), wc, uptr->fileref); err = ferror (uptr->fileref); for ( ; i < wc; i++) /* fill buffer */ rlxb[i] = 0; awc = wc; /* save wc */ for (wc = 0; (err == 0) && (wc < awc); wc++) { /* loop thru buf */ if (Map_ReadW (ma + (wc << 1), 2, &comp)) { /* mem wd */ rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ break; } if (comp != rlxb[wc]) /* check to buf */ rlcs = rlcs | RLCS_ERR | RLCS_CRC; } /* end for */ } /* end wcheck */ rlmp = (rlmp + wc) & 0177777; /* final word count */ if (rlmp != 0) /* completed? */ rlcs = rlcs | RLCS_ERR | RLCS_INCMP; ma = ma + (wc << 1); /* final byte addr */ rlbae = (ma >> 16) & RLBAE_IMP; /* upper 6b */ rlba = ma & RLBA_IMP; /* lower 16b */ rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD); rl_set_done (0); if (err != 0) { /* error? */ perror ("RL I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } return SCPE_OK; } /* Set done and possibly errors */ void rl_set_done (int32 status) { rlcs = rlcs | status | CSR_DONE; /* set done */ if (rlcs & CSR_IE) SET_INT (RL); else CLR_INT (RL); return; } /* Device reset Note that the RL11 does NOT recalibrate its drives on RESET */ t_stat rl_reset (DEVICE *dptr) { int32 i; UNIT *uptr; rlcs = CSR_DONE; rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0; CLR_INT (RL); for (i = 0; i < RL_NUMDR; i++) { uptr = rl_dev.units + i; sim_cancel (uptr); uptr->STAT = 0; } if (rlxb == NULL) rlxb = (uint16 *) calloc (RL_MAXFR, sizeof (uint16)); if (rlxb == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat rl_attach (UNIT *uptr, char *cptr) { uint32 p; t_stat r; uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; r = attach_unit (uptr, cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return r; uptr->TRK = 0; /* cylinder 0 */ uptr->STAT = RLDS_VCK; /* new volume */ if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ if (uptr->flags & UNIT_RO) /* if ro, done */ return SCPE_OK; return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); } if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ return SCPE_OK; if (p > (RL01_SIZE * sizeof (int16))) { uptr->flags = uptr->flags | UNIT_RL02; uptr->capac = RL02_SIZE; } else { uptr->flags = uptr->flags & ~UNIT_RL02; uptr->capac = RL01_SIZE; } return SCPE_OK; } /* Set size routine */ t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) { if (uptr->flags & UNIT_ATT) return SCPE_ALATT; uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; return SCPE_OK; } /* Set bad block routine */ t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) { return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); } /* Device bootstrap */ #if defined (VM_PDP11) #define BOOT_START 02000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 020) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0042114, /* "LD" */ 0012706, BOOT_START, /* MOV #boot_start, SP */ 0012700, 0000000, /* MOV #unit, R0 */ 0010003, /* MOV R0, R3 */ 0000303, /* SWAB R3 */ 0012701, 0174400, /* MOV #RLCS, R1 ; csr */ 0012761, 0000013, 0000004, /* MOV #13, 4(R1) ; clr err */ 0052703, 0000004, /* BIS #4, R3 ; unit+gstat */ 0010311, /* MOV R3, (R1) ; issue cmd */ 0105711, /* TSTB (R1) ; wait */ 0100376, /* BPL .-2 */ 0105003, /* CLRB R3 */ 0052703, 0000010, /* BIS #10, R3 ; unit+rdhdr */ 0010311, /* MOV R3, (R1) ; issue cmd */ 0105711, /* TSTB (R1) ; wait */ 0100376, /* BPL .-2 */ 0016102, 0000006, /* MOV 6(R1), R2 ; get hdr */ 0042702, 0000077, /* BIC #77, R2 ; clr sector */ 0005202, /* INC R2 ; magic bit */ 0010261, 0000004, /* MOV R2, 4(R1) ; seek to 0 */ 0105003, /* CLRB R3 */ 0052703, 0000006, /* BIS #6, R3 ; unit+seek */ 0010311, /* MOV R3, (R1) ; issue cmd */ 0105711, /* TSTB (R1) ; wait */ 0100376, /* BPL .-2 */ 0005061, 0000002, /* CLR 2(R1) ; clr ba */ 0005061, 0000004, /* CLR 4(R1) ; clr da */ 0012761, 0177000, 0000006, /* MOV #-512., 6(R1) ; set wc */ 0105003, /* CLRB R3 */ 0052703, 0000014, /* BIS #14, R3 ; unit+read */ 0010311, /* MOV R3, (R1) ; issue cmd */ 0105711, /* TSTB (R1) ; wait */ 0100376, /* BPL .-2 */ 0042711, 0000377, /* BIC #377, (R1) */ 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0012704, BOOT_START+020, /* MOV #START+20, R4 */ 0005005, /* CLR R5 */ 0005007 /* CLR PC */ }; t_stat rl_boot (int32 unitno, DEVICE *dptr) { int32 i; extern uint16 *M; extern int32 saved_PC; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE; M[BOOT_CSR >> 1] = rl_dib.ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat rl_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif simh-3.8.1/PDP11/pdp11_cis.c0000644000175000017500000020515611110273120013352 0ustar vlmvlm/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. This module simulates the PDP-11 commercial instruction set (CIS). 16-Oct-08 RMS Fixed overflow bug in ASHx (Word/NibbleLShift) Fixed bug in DIVx (LntDstr calculation) 30-May-06 RMS Added interrupt tests to character instructions Added 11/44 stack probe test to MOVCx (only) 22-May-06 RMS Fixed bug in decode table (found by John Dundas) Fixed bug in ASHP (reported by John Dundas) Fixed bug in write decimal string with mmgt enabled Fixed bug in 0-length strings in multiply/divide 16-Sep-04 RMS Fixed bug in CMPP/N of negative strings 17-Oct-02 RMS Fixed compiler warning (found by Hans Pufal) 08-Oct-02 RMS Fixed macro definitions The commercial instruction set consists of three instruction formats: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register operands | 0 1 1 1 1 1| 0 0 0 0| opcode | 076030:076057 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076070:076077 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ inline operands | 0 1 1 1 1 1| 0 0 0 1| opcode | 076130:076157 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076170:076177 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ load descriptors | 0 1 1 1 1 1| 0 0 0 0|op| 1 0| reg | 076020:076027 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076060:076067 The CIS instructions operate on character strings, packed (decimal) strings, and numeric (decimal) strings. Strings are described by a two word descriptor: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | length in bytes | char string +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor | starting byte address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |str type| | length | decimal string +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor | starting byte address | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ Decimal string types are: <14:12> data type bytes occupied by n digits 0 signed zoned n 1 unsigned zone n 2 trailing overpunch n 3 leading overpunch n 4 trailing separate n+1 5 leading separate n+1 6 signed packed n/2 +1 7 unsigned packed n/2 +1 Zero length character strings occupy no memory; zero length decimal strings require either zero bytes (zoned, overpunch) or one byte (separate, packed). CIS instructions can run for a very long time, so they are interruptible and restartable. In the simulator, all instructions run to completion. The code is unoptimized. */ #include "pdp11_defs.h" /* Opcode bits */ #define INLINE 0100 /* inline */ #define PACKED 0020 /* packed */ #define NUMERIC 0000 /* numeric */ /* Interrupt test latency */ #define INT_TEST 100 /* Operand type definitions */ #define R0_DESC 1 /* descr in R0:R1 */ #define R2_DESC 2 /* descr in R2:R3 */ #define R4_DESC 3 /* descr in R4:R5 */ #define R4_ARG 4 /* argument in R4 */ #define IN_DESC 5 /* inline descriptor */ #define IN_ARG 6 /* inline argument */ #define MAXOPN 4 /* max # operands */ /* Decimal data type definitions */ #define XZ 0 /* signed zoned */ #define UZ 1 /* unsigned zoned */ #define TO 2 /* trailing overpunch */ #define LO 3 /* leading overpunch */ #define TS 4 /* trailing separate */ #define LS 5 /* leading separate */ #define XP 6 /* signed packed */ #define UP 7 /* unsigned packed */ /* Decimal descriptor definitions */ #define DTYP_M 07 /* type mask */ #define DTYP_V 12 /* type position */ #define DLNT_M 037 /* length mask */ #define DLNT_V 0 /* length position */ #define GET_DTYP(x) (((x) >> DTYP_V) & DTYP_M) #define GET_DLNT(x) (((x) >> DLNT_V) & DLNT_M) /* Shift operand definitions */ #define ASHRND_M 017 /* round digit mask */ #define ASHRND_V 8 /* round digit pos */ #define ASHLNT_M 0377 /* shift count mask */ #define ASHLNT_V 0 /* shift length pos */ #define ASHSGN 0200 /* shift sign */ #define GET_ASHRND(x) (((x) >> ASHRND_V) & ASHRND_M) #define GET_ASHLNT(x) (((x) >> ASHLNT_V) & ASHLNT_M) /* Operand array aliases */ #define A1LNT arg[0] #define A1ADR arg[1] #define A2LNT arg[2] #define A2ADR arg[3] #define A3LNT arg[4] #define A3ADR arg[5] #define A1 &arg[0] #define A2 &arg[2] #define A3 &arg[4] /* Condition code macros */ #define GET_BIT(ir,n) (((ir) >> (n)) & 1) #define GET_SIGN_L(ir) GET_BIT((ir), 31) #define GET_SIGN_W(ir) GET_BIT((ir), 15) #define GET_SIGN_B(ir) GET_BIT((ir), 7) #define GET_Z(ir) ((ir) == 0) /* Decimal string structure */ #define DSTRLNT 4 #define DSTRMAX (DSTRLNT - 1) #define MAXDVAL 429496730 /* 2^32 / 10 */ typedef struct { uint32 sign; uint32 val[DSTRLNT]; } DSTR; static DSTR Dstr0 = { 0, 0, 0, 0, 0 }; extern int32 isenable, dsenable; extern int32 N, Z, V, C, fpd, ipl; extern int32 R[8], trap_req; extern int32 sim_interval; extern uint32 cpu_type; extern FILE *sim_deb; int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag); void WriteDstr (int32 *dscr, DSTR *dec, int32 flag); int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); int32 CmpDstr (DSTR *src1, DSTR *src2); int32 TestDstr (DSTR *dsrc); int32 LntDstr (DSTR *dsrc, int32 nz); uint32 NibbleLshift (DSTR *dsrc, int32 sc); uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); int32 WordLshift (DSTR *dsrc, int32 sc); void WordRshift (DSTR *dsrc, int32 sc); void CreateTable (DSTR *dsrc, DSTR mtable[10]); t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st); int32 movx_setup (int32 op, int32 *arg); void movx_cleanup (int32 op); extern int32 ReadW (int32 addr); extern void WriteW (int32 data, int32 addr); extern int32 ReadB (int32 addr); extern int32 ReadMB (int32 addr); extern void WriteB (int32 data, int32 addr); extern int32 calc_ints (int32 nipl, int32 trq); /* Table of instruction operands */ static int32 opntab[128][MAXOPN] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 007 */ 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, /* 010 - 017 */ 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, /* LD2R */ 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, /* MOVC */ 0, 0, 0, 0, /* MOVRC */ 0, 0, 0, 0, /* MOVTC */ 0, 0, 0, 0, /* 033 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 034 - 037 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* LOCC */ 0, 0, 0, 0, /* SKPC */ 0, 0, 0, 0, /* SCANC */ 0, 0, 0, 0, /* SPANC */ 0, 0, 0, 0, /* CMPC */ 0, 0, 0, 0, /* MATC */ 0, 0, 0, 0, 0, 0, 0, 0, /* 046 - 047 */ R0_DESC, R2_DESC, R4_DESC, 0, /* ADDN */ R0_DESC, R2_DESC, R4_DESC, 0, /* SUBN */ R0_DESC, R2_DESC, 0, 0, /* CMPN */ R0_DESC, 0, 0, 0, /* CVTNL */ R0_DESC, R2_DESC, 0, 0, /* CVTPN */ R0_DESC, R2_DESC, 0, 0, /* CVTNP */ R0_DESC, R2_DESC, R4_ARG, 0, /* ASHN */ R0_DESC, 0, 0, 0, /* CVTLN */ 0, 0, 0, 0, 0, 0, 0, 0, /* LD3R */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, R0_DESC, R2_DESC, R4_DESC, 0, /* ADDP */ R0_DESC, R2_DESC, R4_DESC, 0, /* SUBP */ R0_DESC, R2_DESC, 0, 0, /* CMPP */ R0_DESC, 0, 0, 0, /* CVTPL */ R0_DESC, R2_DESC, R4_DESC, 0, /* MULP */ R0_DESC, R2_DESC, R4_DESC, 0, /* DIVP */ R0_DESC, R2_DESC, R4_ARG, 0, /* ASHP */ R0_DESC, 0, 0, 0, /* CVTLP */ 0, 0, 0, 0, 0, 0, 0, 0, /* 100 - 107 */ 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, /* 110 - 117 */ 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, /* 120 - 127 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IN_DESC, IN_DESC, IN_ARG, 0, /* MOVCI */ IN_DESC, IN_DESC, IN_ARG, 0, /* MOVRCI */ IN_DESC, IN_DESC, IN_ARG, IN_ARG, /* MOVTCI */ 0, 0, 0, 0, /* 133 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 134 - 137 */ 0, 0, 0, 0, 0, 0, 0, 0, IN_DESC, IN_ARG, 0, 0, /* LOCCI */ IN_DESC, IN_ARG, 0, 0, /* SKPCI */ IN_DESC, IN_DESC, 0, 0, /* SCANCI */ IN_DESC, IN_DESC, 0, 0, /* SPANCI */ IN_DESC, IN_DESC, IN_ARG, 0, /* CMPCI */ IN_DESC, IN_DESC, 0, 0, /* MATCI */ 0, 0, 0, 0, 0, 0, 0, 0, /* 146 - 147 */ IN_DESC, IN_DESC, IN_DESC, 0, /* ADDNI */ IN_DESC, IN_DESC, IN_DESC, 0, /* SUBNI */ IN_DESC, IN_DESC, 0, 0, /* CMPNI */ IN_DESC, IN_ARG, 0, 0, /* CVTNLI */ IN_DESC, IN_DESC, 0, 0, /* CVTPNI */ IN_DESC, IN_DESC, 0, 0, /* CVTNPI */ IN_DESC, IN_DESC, IN_ARG, 0, /* ASHNI */ IN_DESC, IN_DESC, 0, 0, /* CVTLNI */ 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 167 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IN_DESC, IN_DESC, IN_DESC, 0, /* ADDPI */ IN_DESC, IN_DESC, IN_DESC, 0, /* SUBPI */ IN_DESC, IN_DESC, 0, 0, /* CMPPI */ IN_DESC, IN_ARG, 0, 0, /* CVTPLI */ IN_DESC, IN_DESC, IN_DESC, 0, /* MULPI */ IN_DESC, IN_DESC, IN_DESC, 0, /* DIVPI */ IN_DESC, IN_DESC, IN_ARG, 0, /* ASHPI */ IN_DESC, IN_DESC, 0, 0 /* CVTLPI */ }; /* ASCII to overpunch table: sign is <7>, digit is <4:0> */ static int32 overbin[128] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 037 */ 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, 0x80, 0, 0, 0, 0, 0, 0, /* 040 - 077 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x80, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, /* 100 - 137 */ 8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 140 - 177 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0 }; /* Overpunch to ASCII table: indexed by sign and digit */ static int32 binover[2][16] = { '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '0', '0', '0', '0', '0', '0', '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '0', '0', '0', '0', '0', '0' }; static unsigned char movbuf[65536]; /* CIS emulator */ t_stat cis11 (int32 IR) { int32 c, i, j, t, op, rn, addr; int32 match, limit, mvlnt, shift; int32 spc, ldivd, ldivr; int32 arg[6]; /* operands */ int32 old_PC; uint32 nc, digit, result; t_stat st; static DSTR accum, src1, src2, dst; static DSTR mptable[10]; static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 }; old_PC = (PC - 2) & 0177777; /* original PC */ op = IR & 0177; /* IR <6:0> */ for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) { /* parse operands */ switch (opntab[op][i]) { /* case on op type */ case R0_DESC: arg[j++] = R[0]; arg[j++] = R[1]; break; case R2_DESC: arg[j++] = R[2]; arg[j++] = R[3]; break; case R4_DESC: arg[j++] = R[4]; arg[j++] = R[5]; break; case R4_ARG: arg[j++] = R[4]; break; case IN_DESC: addr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; arg[j++] = ReadW (addr | dsenable); arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable); break; case IN_ARG: arg[j++] = ReadW (PC | isenable); PC = (PC + 2) & 0177777; break; default: return SCPE_IERR; } /* end case */ } /* end for */ switch (op) { /* case on opcode */ /* MOVC, MOVTC, MOVCI, MOVTCI Operands (MOVC, MOVTC): R0, R1 = source string descriptor R2, R3 = dest string descriptor R4<7:0> = fill character R5 = translation table address (MOVTC only) Operands (MOVCI, MOVTCI): A1LNT, A1ADR = source string descriptor A2LNT, A2ADR = dest string descriptor A3LNT<7:0> = fill character A3ADR = translation table address (MOVTCI only) Condition codes: NZVC = set from src.lnt - dst.lnt Registers (MOVC, MOVTC only) R0 = max (0, src.len - dst.len) R1:R3 = 0 R4:R5 = unchanged Notes: - If either the source or destination lengths are zero, the move loops exit immediately. - If the source length does not exceed the destination length, the fill loop exits immediately. */ case 030: case 032: case 0130: case 0132: if (!fpd) { /* first time? */ mvlnt = movx_setup (op, arg); /* set up reg */ if (R[1] < R[3]) { /* move backwards? */ R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ R[3] = (R[3] + mvlnt) & 0177777; } } /* At this point, R0-R5 = arguments M[SP] = move length */ if (R[0] && R[2]) { /* move to do? */ if (R[1] < R[3]) { /* backwards? */ for (i = 0; R[0] && R[2]; ) { /* move loop */ t = ReadB (((R[1] - 1) & 0177777) | dsenable); if (op & 2) t = ReadB (((R[5] + t) & 0177777) | dsenable); WriteB (t, ((R[3] - 1) & 0177777) | dsenable); R[0]--; R[1] = (R[1] - 1) & 0177777; R[2]--; R[3] = (R[3] - 1) & 0177777; if ((++i >= INT_TEST) && R[0] && R[2]) { if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } /* end for lnts */ mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ R[3] = (R[3] + mvlnt) & 0177777; /* end of dst str */ } /* end if bkwd */ else { /* forward */ for (i = 0; R[0] && R[2]; ) { /* move loop */ t = ReadB ((R[1] & 0177777) | dsenable); if (op & 2) t = ReadB (((R[5] + t) & 0177777) | dsenable); WriteB (t, (R[3] & 0177777) | dsenable); R[0]--; R[1] = (R[1] + 1) & 0177777; R[2]--; R[3] = (R[3] + 1) & 0177777; if ((++i >= INT_TEST) && R[0] && R[2]) { if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } /* end for lnts */ } /* end else fwd */ } /* end if move */ for (i = 0; i < R[2]; i++) { WriteB (R[4], ((R[3] + i) & 0177777) | dsenable); } movx_cleanup (op); /* cleanup */ return SCPE_OK; /* MOVRC, MOVRCI Operands (MOVC, MOVTC): R0, R1 = source string descriptor R2, R3 = dest string descriptor R4<7:0> = fill character Operands (MOVCI, MOVTCI): A1LNT, A1ADR = source string descriptor A2LNT, A2ADR = dest string descriptor A3LNT<7:0> = fill character Condition codes: NZVC = set from src.lnt - dst.lnt Registers (MOVRC only) R0 = max (0, src.len - dst.len) R1:R3 = 0 R4:R5 = unchanged Notes: see MOVC, MOVCI */ case 031: case 0131: if (!fpd) { /* first time? */ mvlnt = movx_setup (op, arg); /* set up reg */ R[1] = (R[1] + R[0] - mvlnt) & 0177777; /* eff move start */ R[3] = (R[3] + R[2] - mvlnt) & 0177777; if (R[1] < R[3]) { /* move backwards? */ R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ R[3] = (R[3] + mvlnt) & 0177777; } } /* At this point, R0-R5 = arguments M[SP] = move length */ if (R[0] && R[2]) { /* move to do? */ if (R[1] < R[3]) { /* backwards? */ for (i = 0; R[0] && R[2]; ) { /* move loop */ t = ReadB (((R[1] - 1) & 0177777) | dsenable); WriteB (t, ((R[3] - 1) & 0177777) | dsenable); R[0]--; R[1] = (R[1] - 1) & 0177777; R[2]--; R[3] = (R[3] - 1) & 0177777; if ((++i >= INT_TEST) && R[0] && R[2]) { if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } /* end for lnts */ } /* end if bkwd */ else { /* forward */ for (i = 0; R[0] && R[2]; ) { /* move loop */ t = ReadB ((R[1] & 0177777) | dsenable); WriteB (t, (R[3] & 0177777) | dsenable); R[0]--; R[1] = (R[1] + 1) & 0177777; R[2]--; R[3] = (R[3] + 1) & 0177777; if ((++i >= INT_TEST) && R[0] && R[2]) { if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } /* end for lnts */ mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ R[3] = (R[3] - mvlnt) & 0177777; /* start of dst str */ } /* end else fwd */ } /* end if move */ for (i = 0; i < R[2]; i++) { WriteB (R[4], ((R[3] - R[2] + i) & 0177777) | dsenable); } movx_cleanup (op); /* cleanup */ return SCPE_OK; /* Load descriptors - no operands */ case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027: case 060: case 061: case 062: case 063: case 064: case 065: case 066: case 067: limit = (op & 040)? 6: 4; rn = IR & 07; /* get register */ t = R[rn]; spc = (rn == 7)? isenable: dsenable; for (j = 0; j < limit; j = j + 2) { /* loop for 2,3 dscr */ addr = ReadW (((t + j) & 0177777) | spc); R[j] = ReadW (addr | dsenable); R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable); } if (rn >= limit) R[rn] = (R[rn] + limit) & 0177777; return SCPE_OK; /* LOCC, SKPC, LOCCI, SKPCI Operands (LOCC, SKPC): R0, R1 = source string descriptor R4<7:0> = match character Operands (LOCCI, SKPCI): A1LNT, A1ADR = source string descriptor A2LNT<7:0> = match character Condition codes: NZ = set from R0 VC = 0 Registers: R0:R1 = substring descriptor where operation terminated */ case 0140: case 0141: /* inline */ if (!fpd) { /* FPD clear? */ WriteW (R[4], ((SP - 2) & 0177777) | dsenable); SP = (SP - 2) & 0177777; /* push R4 */ R[0] = A1LNT; /* args to registers */ R[1] = A1ADR; R[4] = A2LNT; } /* fall through */ case 040: case 041: /* register */ fpd = 1; /* set FPD */ R[4] = R[4] & 0377; /* match character */ for (i = 0; R[0] != 0;) { /* loop */ c = ReadB (R[1] | dsenable); /* get char */ if ((c == R[4]) ^ (op & 1)) /* = + LOC, != + SKP? */ break; R[0]--; /* decr count, */ R[1] = (R[1] + 1) & 0177777; /* incr addr */ if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } N = GET_SIGN_W (R[0]); Z = GET_Z (R[0]); V = C = 0; fpd = 0; /* instr done */ if (op & INLINE) { /* inline? */ R[4] = ReadW (SP | dsenable); /* restore R4 */ SP = (SP + 2) & 0177777; } return SCPE_OK; /* SCANC, SPANC, SCANCI, SPANCI Operands (SCANC, SPANC): R0, R1 = source string descriptor R4<7:0> = mask R5 = table address Operands (SCANCI, SPANCI): A1LNT, A1ADR = source string descriptor A2LNT<7:0> = match character A2ADR = table address Condition codes: NZ = set from R0 VC = 0 Registers: R0:R1 = substring descriptor where operation terminated */ case 0142: case 0143: /* inline */ if (!fpd) { /* FPD clear? */ WriteW (R[4], ((SP - 4) & 0177777) | dsenable); WriteW (R[5], ((SP - 2) & 0177777) | dsenable); SP = (SP - 4) & 0177777; /* push R4, R5 */ R[0] = A1LNT; /* args to registers */ R[1] = A1ADR; R[4] = A2LNT; R[5] = A2ADR; } /* fall through */ case 042: case 043: /* register */ fpd = 1; /* set FPD */ R[4] = R[4] & 0377; /* match character */ for (i = 0; R[0] != 0;) { /* loop */ t = ReadB (R[1] | dsenable); /* get char as index */ c = ReadB (((R[5] + t) & 0177777) | dsenable); if (((c & R[4]) != 0) ^ (op & 1)) /* != + SCN, = + SPN? */ break; R[0]--; /* decr count, */ R[1] = (R[1] + 1) & 0177777; /* incr addr */ if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } N = GET_SIGN_W (R[0]); Z = GET_Z (R[0]); V = C = 0; fpd = 0; /* instr done */ if (op & INLINE) { /* inline? */ R[4] = ReadW (SP | dsenable); /* restore R4, R5 */ R[5] = ReadW (((SP + 2) & 0177777) | dsenable); SP = (SP + 4) & 0177777; } return SCPE_OK; /* CMPC, CMPCI Operands (CMPC): R0, R1 = source1 string descriptor R2, R3 = source2 string descriptor R4<7:0> = fill character Operands (CMPCI): A1LNT, A1ADR = source1 string descriptor A2LNT, A2ADR = source2 string descriptor A3LNT<7:0> = fill character Condition codes: NZVC = set from src1 - src2 at mismatch, or = 0100 if equal Registers (CMPC only): R0:R1 = unmatched source1 substring descriptor R2:R3 = unmatched source2 substring descriptor */ case 0144: /* inline */ if (!fpd) { /* FPD clear? */ WriteW (R[0], ((SP - 10) & 0177777) | dsenable); WriteW (R[1], ((SP - 8) & 0177777) | dsenable); WriteW (R[2], ((SP - 6) & 0177777) | dsenable); WriteW (R[3], ((SP - 4) & 0177777) | dsenable); WriteW (R[4], ((SP - 2) & 0177777) | dsenable); SP = (SP - 10) & 0177777; /* push R0 - R4 */ R[0] = A1LNT; /* args to registers */ R[1] = A1ADR; R[2] = A2LNT; R[3] = A2ADR; R[4] = A3LNT; } /* fall through */ case 044: /* register */ fpd = 1; /* set FPD */ R[4] = R[4] & 0377; /* mask fill */ c = t = 0; for (i = 0; (R[0] || R[2]); ) { /* until cnts == 0 */ if (R[0]) /* get src1 or fill */ c = ReadB (R[1] | dsenable); else c = R[4]; if (R[2]) /* get src2 or fill */ t = ReadB (R[3] | dsenable); else t = R[4]; if (c != t) /* if diff, done */ break; if (R[0]) { /* if more src1 */ R[0]--; /* decr count, */ R[1] = (R[1] + 1) & 0177777; /* incr addr */ } if (R[2]) { /* if more src2 */ R[2]--; /* decr count, */ R[3] = (R[3] + 1) & 0177777; /* incr addr */ } if ((++i >= INT_TEST) && (R[0] || R[2])) { /* test for intr? */ if (cis_int_test (i, old_PC, &st)) return st; i = 0; } } j = c - t; /* last chars read */ N = GET_SIGN_B (j); /* set cc's */ Z = GET_Z (j); V = GET_SIGN_B ((c ^ t) & (~t ^ j)); C = (c < t); fpd = 0; /* instr done */ if (op & INLINE) { /* inline? */ R[0] = ReadW (SP | dsenable); /* restore R0 - R4 */ R[1] = ReadW (((SP + 2) & 0177777) | dsenable); R[2] = ReadW (((SP + 4) & 0177777) | dsenable); R[3] = ReadW (((SP + 6) & 0177777) | dsenable); R[4] = ReadW (((SP + 8) & 0177777) | dsenable); SP = (SP + 10) & 0177777; } return SCPE_OK; /* MATC, MATCI Operands (MATC): R0, R1 = source string descriptor R2, R3 = substring descriptor Operands (MATCI): A1LNT, A1ADR = source1 string descriptor A2LNT, A2ADR = source2 string descriptor Condition codes: NZ = set from R0 VC = 0 Registers: R0:R1 = source substring descriptor for match Notes: - If the string is zero length, and the substring is not, the outer loop exits immediately, and the result is "no match" - If the substring is zero length, the inner loop always exits immediately, and the result is a "match" - If the string is zero length, and the substring is as well, the outer loop executes, the inner loop exits immediately, and the result is a match, but the result is the length of the string (zero), or "no match" */ case 0145: /* inline */ if (!fpd) { /* FPD clear? */ WriteW (R[2], ((SP - 4) & 0177777) | dsenable); WriteW (R[3], ((SP - 2) & 0177777) | dsenable); SP = (SP - 4) & 0177777; /* push R2, R3 */ R[0] = A1LNT; /* args to registers */ R[1] = A1ADR; R[2] = A2LNT; R[3] = A2ADR; } /* fall through */ case 0045: /* register */ fpd = 1; for (match = 0; R[0] >= R[2]; ) { /* loop thru string */ for (i = 0, match = 1; match && (i < R[2]); i++) { c = ReadB (((R[1] + i) & 0177777) | dsenable); t = ReadB (((R[3] + i) & 0177777) | dsenable); match = (c == t); /* end for substring */ } if (match) /* exit if match */ break; R[0]--; /* on to next char */ R[1] = (R[1] + 1) & 0177777; if (cis_int_test (i, old_PC, &st)) return st; } if (!match) { /* if no match */ R[1] = (R[1] + R[0]) & 0177777; R[0] = 0; } N = GET_SIGN_W (R[0]); Z = GET_Z (R[0]); V = C = 0; fpd = 0; /* instr done */ if (op & INLINE) { /* inline? */ R[2] = ReadW (SP | dsenable); /* restore R2, R3 */ R[3] = ReadW (((SP + 2) & 0177777) | dsenable); SP = (SP + 4) & 0177777; } return SCPE_OK; /* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI Operands: A1LNT, A1ADR = source1 string descriptor A2LNT, A2ADR = source2 string descriptor A3LNT, A3ADR = destination string descriptor Condition codes: NZV = set from result C = 0 Registers (ADDN, ADDP, SUBN, SUBP only): R0:R3 = 0 */ case 050: case 051: case 070: case 071: case 0150: case 0151: case 0170: case 0171: ReadDstr (A1, &src1, op); /* get source1 */ ReadDstr (A2, &src2, op); /* get source2 */ if (op & 1) /* sub? invert sign */ src1.sign = src1.sign ^ 1; if (src1.sign ^ src2.sign) { /* opp signs? sub */ if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ SubDstr (&src1, &src2, &dst); /* src2 - src1 */ dst.sign = src2.sign; /* sign = src2 */ } else { SubDstr (&src2, &src1, &dst); /* src1 - src2 */ dst.sign = src1.sign; /* sign = src1 */ } V = 0; /* can't carry */ } else { /* addition */ V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ dst.sign = src1.sign; /* set result sign */ } C = 0; WriteDstr (A3, &dst, op); /* store result */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = R[2] = R[3] = 0; return SCPE_OK; /* MULP, MULPI Operands: A1LNT, A1ADR = source1 string descriptor A2LNT, A2ADR = source2 string descriptor A3LNT, A3ADR = destination string descriptor Condition codes: NZV = set from result C = 0 Registers (MULP only): R0:R3 = 0 */ case 074: case 0174: dst = Dstr0; /* clear result */ if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) { dst.sign = src1.sign ^ src2.sign; /* sign of result */ accum = Dstr0; /* clear accum */ NibbleRshift (&src1, 1, 0); /* shift out sign */ CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (digit > 0) /* add in digit*mpcnd */ AddDstr (&mptable[digit], &accum, &accum, 0); nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ NibbleRshift (&dst, 1, nc); /* result right 4 */ } V = TestDstr (&accum) != 0; /* if ovflo, set V */ } else V = 0; /* result = 0 */ C = 0; /* C = 0 */ WriteDstr (A3, &dst, op); /* store result */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = R[2] = R[3] = 0; return SCPE_OK; /* DIVP, DIVPI Operands: A1LNT, A1ADR = divisor string descriptor A2LNT, A2ADR = dividend string descriptor A3LNT, A3ADR = destination string descriptor Condition codes: NZV = set from result C = set if divide by zero Registers (DIVP only): R0:R3 = 0 */ case 075: case 0175: ldivr = ReadDstr (A1, &src1, op); /* get divisor */ if (ldivr == 0) { /* divisor = 0? */ V = C = 1; /* set cc's */ return SCPE_OK; } ldivr = LntDstr (&src1, ldivr); /* get exact length */ ldivd = ReadDstr (A2, &src2, op); /* get dividend */ ldivd = LntDstr (&src2, ldivd); /* get exact length */ dst = Dstr0; /* clear dest */ NibbleRshift (&src1, 1, 0); /* right justify ops */ NibbleRshift (&src2, 1, 0); if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ WordLshift (&src1, t / 8); /* align divr to divd */ NibbleLshift (&src1, t % 8); CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 0; i <= t; i++) { /* divide loop */ for (digit = 9; digit > 0; digit--) { /* find digit */ if (CmpDstr (&src2, &mptable[digit]) >= 0) { SubDstr (&mptable[digit], &src2, &src2); dst.val[0] = dst.val[0] | digit; break; } /* end if */ } /* end for */ NibbleLshift (&src2, 1); /* shift dividend */ NibbleLshift (&dst, 1); /* shift quotient */ } /* end divide loop */ dst.sign = src1.sign ^ src2.sign; /* calculate sign */ } /* end if */ V = C = 0; WriteDstr (A3, &dst, op); /* store result */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = R[2] = R[3] = 0; return SCPE_OK; /* CMPN, CMPP, CMPNI, CMPPI Operands: A1LNT, A1ADR = source1 string descriptor A2LNT, A2ADR = source2 string descriptor Condition codes: NZ = set from comparison VC = 0 Registers (CMPN, CMPP only): R0:R3 = 0 */ case 052: case 072: case 0152: case 0172: ReadDstr (A1, &src1, op); /* get source1 */ ReadDstr (A2, &src2, op); /* get source2 */ N = Z = V = C = 0; if (src1.sign != src2.sign) N = src1.sign; else { t = CmpDstr (&src1, &src2); /* compare strings */ if (t < 0) N = (src1.sign? 0: 1); else if (t > 0) N = (src1.sign? 1: 0); else Z = 1; } if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = R[2] = R[3] = 0; return SCPE_OK; /* ASHN, ASHP, ASHNI, ASHPI Operands: A1LNT, A1ADR = source string descriptor A2LNT, A2ADR = destination string descriptor A3LNT<11:8> = rounding digit A3LNT<7:0> = shift count Condition codes: NZV = set from result C = 0 Registers (ASHN, ASHP only): R0:R1, R4 = 0 */ case 056: case 076: case 0156: case 0176: ReadDstr (A1, &src1, op); /* get source */ V = C = 0; /* init cc's */ shift = GET_ASHLNT (A3LNT); /* get shift count */ if (shift & ASHSGN) { /* right shift? */ shift = (ASHLNT_M + 1 - shift); /* !shift! */ WordRshift (&src1, shift / 8); /* do word shifts */ NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ t = GET_ASHRND (A3LNT); /* get rounding digit */ if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ AddDstr (&src1, &Dstr1, &src1, 0); /* round */ src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ } /* end right shift */ else if (shift) { /* left shift? */ if (WordLshift (&src1, shift / 8)) /* do word shifts */ V = 1; if (NibbleLshift (&src1, shift % 8)) /* do nibble shifts */ V = 1; } /* end left shift */ WriteDstr (A2, &src1, op); /* store result */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = R[4] = 0; return SCPE_OK; /* CVTPN, CVTPNI Operands: A1LNT, A1ADR = source string descriptor A2LNT, A2ADR = destination string descriptor Condition codes: NZV = set from result C = 0 Registers (CVTPN only): R0:R1 = 0 */ case 054: case 0154: ReadDstr (A1, &src1, PACKED); /* get source */ V = C = 0; /* init cc's */ WriteDstr (A2, &src1, NUMERIC); /* write dest */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = 0; return SCPE_OK; /* CVTNP, CVTNPI Operands: A1LNT, A1ADR = source string descriptor A2LNT, A2ADR = destination string descriptor Condition codes: NZV = set from result C = 0 Registers (CVTNP only): R0:R1 = 0 */ case 055: case 0155: ReadDstr (A1, &src1, NUMERIC); /* get source */ V = C = 0; /* init cc's */ WriteDstr (A2, &src1, PACKED); /* write dest */ if ((op & INLINE) == 0) /* if reg, clr reg */ R[0] = R[1] = 0; return SCPE_OK; /* CVTNL, CVTPL, CVTNLI, CVTPLI Operands: A1LNT, A1ADR = source string descriptor A2LNT = destination address (inline only) Condition codes: NZV = set from result C = source < 0 and result != 0 Registers (CVTNL, CVTPL only): R0:R1 = 0 R2:R3 = result */ case 053: case 073: case 0153: case 0173: ReadDstr (A1, &src1, op); /* get source */ V = result = 0; /* clear V, result */ for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (digit || result || V) { /* skip initial 0's */ if (result >= MAXDVAL) V = 1; result = (result * 10) + digit; if (result < digit) V = 1; } /* end if */ } /* end for */ if (src1.sign) result = (~result + 1) & 0xFFFFFFFF; N = GET_SIGN_L (result); Z = GET_Z (result); V = V | (N ^ src1.sign); /* overflow if +2**31 */ C = src1.sign && (Z == 0); /* set C based on std */ if (op & INLINE) { /* inline? */ WriteW (result & 0177777, A2LNT | dsenable); WriteW ((result >> 16) & 0177777, ((A2LNT + 2) & 0177777) | dsenable); } else { R[0] = R[1] = 0; R[2] = (result >> 16) & 0177777; R[3] = result & 0177777; } return SCPE_OK; /* CVTLN, CVTLP, CVTLNI, CVTLPI Operands: A1LNT, A1ADR = destination string descriptor A2LNT, A2ADR = source long (CVTLNI, CVTLPI) - VAX format R2:R3 = source long (CVTLN, CVTLP) - EIS format Condition codes: NZV = set from result C = 0 Registers (CVTLN, CVTLP only) R2:R3 = 0 */ case 057: case 077: result = (R[2] << 16) | R[3]; /* op in EIS format */ R[2] = R[3] = 0; /* clear registers */ goto CVTLx; /* join common code */ case 0157: case 0177: result = (A2ADR << 16) | A2LNT; /* op in VAX format */ CVTLx: dst = Dstr0; /* clear result */ if (dst.sign = GET_SIGN_L (result)) result = (~result + 1) & 0xFFFFFFFF; for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { digit = result % 10; result = result / 10; dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4)); } V = C = 0; WriteDstr (A1, &dst, op); /* write result */ return SCPE_OK; default: setTRAP (TRAP_ILL); break; } /* end case */ return SCPE_OK; } /* end cis */ /* Get decimal string Arguments: dscr = decimal string descriptor src = decimal string structure flag = numeric/packed flag The routine returns the length in int32's of the non-zero part of the string. This routine plays fast and loose with operand checking, as did the original 11/23 microcode (half of which I wrote). In particular, - If the flag specifies packed, the type is not checked at all. The sign of an unsigned string is assumed to be 0xF (an alternative for +). - If the flag specifies numeric, packed types will be treated as unsigned zoned. - For separate, only the '-' sign is checked, not the '+'. However, to simplify the code elsewhere, digits are range checked, and bad digits are replaced with 0's. */ int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag) { int32 c, i, end, lnt, type, t; *src = Dstr0; /* clear result */ type = GET_DTYP (dscr[0]); /* get type */ lnt = GET_DLNT (dscr[0]); /* get string length */ if (flag & PACKED) { /* packed? */ end = lnt / 2; /* last byte */ for (i = 0; i <= end; i++) { /* loop thru string */ c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable); if (i == 0) /* save sign */ t = c & 0xF; if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; if (c >= 0xA0) /* check hi digit */ c = c & 0xF; if ((c & 0xF) >= 0xA) /* check lo digit */ c = c & 0xF0; src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); } /* end for */ if ((t == 0xB) || (t == 0xD)) /* if -, set sign */ src->sign = 1; src->val[0] = src->val[0] & ~0xF; /* clear sign */ } /* end packed */ else { /* numeric */ if (type >= TS) src->sign = (ReadB ((((type == TS)? dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-'); for (i = 1; i <= lnt; i++) { /* loop thru string */ c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable); if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70)) src->sign = 1; /* signed zoned */ else if (((i == 1) && (type == TO)) || ((i == lnt) && (type == LO))) { c = overbin[c & 0177]; /* get sign and digit */ src->sign = c >> 7; /* set sign */ } c = c & 0xF; /* get digit */ if (c > 9) /* range check */ c = 0; src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4)); } /* end for */ } /* end numeric */ return TestDstr (src); /* clean -0 */ } /* Store decimal string Arguments: dsrc = decimal string descriptor src = decimal string structure flag = numeric/packed flag PSW.NZ are also set to their proper values PSW.V will be set on overflow; it must be initialized elsewhere (to allow for external overflow calculations) The rules for the stored sign and the PSW sign are: - Stored sign is negative if input is negative, string type is signed, and the result is non-zero or there was overflow - PSW sign is negative if input is negative, string type is signed, and the result is non-zero Thus, the stored sign and the PSW sign will differ in one case: a negative zero generated by overflow is stored with a negative sign, but PSW.N is clear */ void WriteDstr (int32 *dscr, DSTR *dst, int32 flag) { int32 c, i, limit, end, type, lnt; uint32 mask; static uint32 masktab[8] = { 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 }; static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 }; type = GET_DTYP (dscr[0]); /* get type */ lnt = GET_DLNT (dscr[0]); /* get string length */ mask = 0; /* can't ovflo */ Z = 1; /* assume all 0's */ limit = lnt / 8; /* limit for test */ for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ if (i == limit) /* at limit, get mask */ mask = masktab[lnt % 8]; else if (i > limit) /* beyond, all ovflo */ mask = 0xFFFFFFFF; if (dst->val[i] & mask) /* test for ovflo */ V = 1; if (dst->val[i] = dst->val[i] & ~mask) /* test nz */ Z = 0; } dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V); N = dst->sign & ~Z; /* N = sign, if ~zero */ if (flag & PACKED) { /* packed? */ end = lnt / 2; /* end of string */ if (type == UP) dst->val[0] = dst->val[0] | 0xF; else dst->val[0] = dst->val[0] | 0xC | dst->sign; for (i = 0; i <= end; i++) { /* store string */ c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; WriteB (c, ((dscr[1] + end - i) & 0177777) | dsenable); } /* end for */ } /* end packed */ else { if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)? dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable); for (i = 1; i <= lnt; i++) { /* store string */ c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ if ((i == 1) && (type == XZ) && dst->sign) c = c | 0x70; /* signed zoned */ else if (((i == 1) && (type == TO)) || ((i == lnt) && (type == LO))) c = binover[dst->sign][c]; /* get sign and digit */ else c = c | 0x30; /* default */ WriteB (c, ((dscr[1] + lnt - i) & 0177777) |dsenable ); } /* end for */ } /* end numeric */ return; } /* Add decimal string magnitudes Arguments: s1 = source1 decimal string s2 = source2 decimal string ds = destination decimal string cy = carry in Output = 1 if carry, 0 if no carry This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. We trace the history of a pair of adjacent digits to see how the carry is fixed; each parenthesized item is a 4b digit. Assume we are adding: (a)(b) I + (x)(y) J First compute I^J: (a^x)(b^y) TMP Note that the low bit of each digit is the same as the low bit of the sum of the digits, ignoring the cary, since the low bit of the sum is the xor of the bits. Now compute I+J+66 to get decimal addition with carry forced left one digit: (a+x+6+carry mod 16)(b+y+6 mod 16) SUM Note that if there was a carry from b+y+6, then the low bit of the left digit is different from the expected low bit from the xor. If we xor this SUM into TMP, then the low bit of each digit is 1 if there was a carry, and 0 if not. We need to subtract 6 from each digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift it right 4 to the digits that are affected, and subtract 6*adjustment (actually, shift it right 3 and subtract 3*adjustment). */ int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) { int32 i; uint32 sm1, sm2, tm1, tm2, tm3, tm4; for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ sm2 = sm1 + 0x66666666; /* force carry out */ cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ tm2 = tm1 ^ sm2; /* get carry flags */ tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ tm4 = 0x22222222 & ~tm3; /* clear where carry */ ds->val[i] = sm2 - (3 * tm4); /* final result */ } return cy; } /* Subtract decimal string magnitudes Arguments: s1 = source1 decimal string s2 = source2 decimal string ds = destination decimal string Outputs: s2 - s1 in ds Note: the routine assumes that s1 <= s2 */ void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) { int32 i; DSTR compl; for (i = 0; i < DSTRLNT; i++) compl.val[i] = 0x99999999 - s1->val[i]; AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ return; } /* Compare decimal string magnitudes Arguments: s1 = source1 decimal string s2 = source2 decimal string Output = 1 if >, 0 if =, -1 if < */ int32 CmpDstr (DSTR *s1, DSTR *s2) { int32 i; for (i = DSTRMAX; i >=0; i--) { if (s1->val[i] > s2->val[i]) return 1; if (s1->val[i] < s2->val[i]) return -1; } return 0; } /* Test decimal string for zero Arguments: dsrc = decimal string structure Returns the non-zero length of the string, in int32 units If the string is zero, the sign is cleared */ int32 TestDstr (DSTR *dsrc) { int32 i; for (i = DSTRMAX; i >= 0; i--) { if (dsrc->val[i]) return (i + 1); } dsrc->sign = 0; return 0; } /* Get exact length of decimal string Arguments: dsrc = decimal string structure nz = result from TestDstr */ int32 LntDstr (DSTR *dsrc, int32 nz) { int32 i; if (nz == 0) return 0; for (i = 7; i >= 0; i--) { if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; } return ((nz - 1) * 8) + i; } /* Create table of multiples Arguments: dsrc = base decimal string structure mtable[10] = array of decimal string structures Note that dsrc has a high order zero nibble; this guarantees that the largest multiple won't overflow. Also note that mtable[0] is not filled in. */ void CreateTable (DSTR *dsrc, DSTR mtable[10]) { int32 (i); mtable[1] = *dsrc; for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); return; } /* Word shift right Arguments: dsrc = decimal string structure sc = shift count */ void WordRshift (DSTR *dsrc, int32 sc) { int32 i; if (sc) { for (i = 0; i < DSTRLNT; i++) { if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc]; else dsrc->val[i] = 0; } } return; } /* Word shift left Arguments: dsrc = decimal string structure sc = shift count */ int32 WordLshift (DSTR *dsrc, int32 sc) { int32 i, c; c = 0; if (sc) { for (i = DSTRMAX; i >= 0; i--) { if (i >= sc) dsrc->val[i] = dsrc->val[i - sc]; else { c |= dsrc->val[i]; dsrc->val[i] = 0; } } } return c; } /* Nibble shift decimal string right Arguments: dsrc = decimal string structure sc = shift count cin = carry in */ uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) { int32 i, s, nc; if (s = sc * 4) { for (i = DSTRMAX; i >= 0; i--) { nc = (dsrc->val[i] << (32 - s)) & 0xFFFFFFFF; dsrc->val[i] = ((dsrc->val[i] >> s) | cin) & 0xFFFFFFFF; cin = nc; } return cin; } return 0; } /* Nibble shift decimal string left Arguments: dsrc = decimal string structure sc = shift count */ uint32 NibbleLshift (DSTR *dsrc, int32 sc) { int32 i, s; uint32 nc, cin; cin = 0; if (s = sc * 4) { for (i = 0; i < DSTRLNT; i++) { nc = dsrc->val[i] >> (32 - s); dsrc->val[i] = ((dsrc->val[i] << s) | cin) & 0xFFFFFFFF; cin = nc; } return cin; } return 0; } /* Common setup routine for MOVC class instructions */ int32 movx_setup (int32 op, int32 *arg) { int32 mvlnt, t; if (CPUT (CPUT_44)) { /* 11/44? */ ReadMB (((SP - 0200) & 0177777) | dsenable); /* probe both blocks */ ReadMB (((SP - 0100) & 0177777) | dsenable); /* in 64W stack area */ } if (op & INLINE) { /* inline */ mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; WriteW (mvlnt, ((SP - 14) & 0177777) | dsenable); /* push move length */ WriteW (R[0], ((SP - 12) & 0177777) | dsenable); /* push R0 - R5 */ WriteW (R[1], ((SP - 10) & 0177777) | dsenable); WriteW (R[2], ((SP - 8) & 0177777) | dsenable); WriteW (R[3], ((SP - 6) & 0177777) | dsenable); WriteW (R[4], ((SP - 4) & 0177777) | dsenable); WriteW (R[5], ((SP - 2) & 0177777) | dsenable); SP = (SP - 14) & 0177777; R[0] = A1LNT; /* args to registers */ R[1] = A1ADR; R[2] = A2LNT; R[3] = A2ADR; R[4] = A3LNT; R[5] = A3ADR & 0177777; } else { /* register */ mvlnt = (R[0] < R[2])? R[0]: R[2]; WriteW (mvlnt, ((SP - 2) & 0177777) | dsenable); /* push move length */ SP = (SP - 2) & 0177777; } fpd = 1; t = R[0] - R[2]; /* src.lnt - dst.lnt */ N = GET_SIGN_W (t); /* set cc's from diff */ Z = GET_Z (t); V = GET_SIGN_W ((R[0] ^ R[2]) & (~R[2] ^ t)); C = (R[0] < R[2]); return mvlnt; } /* Common cleanup routine for MOVC class instructions */ void movx_cleanup (int32 op) { SP = (SP + 2) & 0177777; /* discard mvlnt */ if (op & INLINE) { /* inline? */ R[0] = ReadW (SP | dsenable); /* restore R0 - R5 */ R[1] = ReadW (((SP + 2) & 0177777) | dsenable); R[2] = ReadW (((SP + 4) & 0177777) | dsenable); R[3] = ReadW (((SP + 6) & 0177777) | dsenable); R[4] = ReadW (((SP + 8) & 0177777) | dsenable); R[5] = ReadW (((SP + 10) & 0177777) | dsenable); SP = (SP + 12) & 0177777; } else R[1] = R[2] = R[3] = 0; /* reg, clear R1 - R3 */ fpd = 0; /* instr done */ return; } /* Test for CIS mid-instruction interrupt */ t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st) { while (cycles >= 0) { /* until delay done */ if (sim_interval > cycles) { /* event > delay */ sim_interval = sim_interval - cycles; break; } else { /* event <= delay */ cycles = cycles - sim_interval; /* decr delay */ sim_interval = 0; /* process event */ *st = sim_process_event (); trap_req = calc_ints (ipl, trap_req); /* recalc int req */ if ((*st != SCPE_OK) || /* bad status or */ trap_req & TRAP_INT) { /* interrupt? */ PC = oldpc; /* back out */ return TRUE; } /* end if stop */ } /* end else event */ } /* end while delay */ return FALSE; } simh-3.8.1/PDP11/pdp11_rq.c0000644000175000017500000033021311112125416013215 0ustar vlmvlm/* pdp11_rq.c: MSCP disk controller simulator Copyright (c) 2002-2008, Robert M Supnik Derived from work by Stephen F. Shirron Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. rq RQDX3 disk controller 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) 17-Jan-05 RMS Added more RA and RD disks 31-Oct-04 RMS Added -L switch (LBNs) to RAUSER size specification 01-Oct-04 RMS Revised Unibus interface Changed to identify as UDA50 in Unibus configurations Changed width to be 16b in all configurations Changed default timing for VAX 24-Jul-04 RMS VAX controllers luns start with 0 (from Andreas Cejna) 05-Feb-04 RMS Revised for file I/O library 25-Jan-04 RMS Revised for device debug support 12-Jan-04 RMS Fixed bug in interrupt control (found by Tom Evans) 07-Oct-03 RMS Fixed problem with multiple RAUSER drives 17-Sep-03 RMS Fixed MB to LBN conversion to be more accurate 11-Jul-03 RMS Fixed bug in user disk size (found by Chaskiel M Grundman) 19-May-03 RMS Revised for new conditional compilation scheme 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed variable size interaction with save/restore 27-Feb-03 RMS Added user-defined drive support 26-Feb-03 RMS Fixed bug in vector calculation for VAXen 22-Feb-03 RMS Fixed ordering bug in queue process 12-Oct-02 RMS Added multicontroller support 29-Sep-02 RMS Changed addressing to 18b in Unibus mode Added variable address support to bootstrap Added vector display support Fixed status code in HBE error log Consolidated MSCP/TMSCP header file New data structures 16-Aug-02 RMS Removed unused variables (found by David Hittner) 04-May-02 RMS Fixed bug in polling loop for queued operations 26-Mar-02 RMS Fixed bug, reset routine cleared UF_WPH 09-Mar-02 RMS Adjusted delays for M+ timing bugs 04-Mar-02 RMS Added delays to initialization for M+, RSTS/E 16-Feb-02 RMS Fixed bugs in host timeout logic, boot 26-Jan-02 RMS Revised bootstrap to conform to M9312 06-Jan-02 RMS Revised enable/disable support 30-Dec-01 RMS Revised show routines 19-Dec-01 RMS Added bigger drives 17-Dec-01 RMS Added queue process */ #if defined (VM_PDP10) /* PDP10 version */ #error "RQDX3 not supported on PDP-10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define RQ_QTIME 100 #define RQ_XTIME 200 #define OLDPC fault_PC extern int32 fault_PC; #else /* PDP-11 version */ #include "pdp11_defs.h" #define RQ_QTIME 200 #define RQ_XTIME 500 #define OLDPC MMR2 extern int32 MMR2; extern int32 cpu_opt; #endif #if !defined (RQ_NUMCT) #define RQ_NUMCT 4 #elif (RQ_NUMCT > 4) #error "Assertion failure: RQ_NUMCT exceeds 4" #endif #include "pdp11_uqssp.h" #include "pdp11_mscp.h" #define UF_MSK (UF_CMR|UF_CMW) /* settable flags */ #define RQ_SH_MAX 24 /* max display wds */ #define RQ_SH_PPL 8 /* wds per line */ #define RQ_SH_DPL 4 /* desc per line */ #define RQ_SH_RI 001 /* show rings */ #define RQ_SH_FR 002 /* show free q */ #define RQ_SH_RS 004 /* show resp q */ #define RQ_SH_UN 010 /* show unit q's */ #define RQ_SH_ALL 017 /* show all */ #define RQ_CLASS 1 /* RQ class */ #define RQU_UQPM 6 /* UB port model */ #define RQQ_UQPM 19 /* QB port model */ #define RQ_UQPM (UNIBUS? RQU_UQPM: RQQ_UQPM) #define RQU_MODEL 6 /* UB MSCP ctrl model */ #define RQQ_MODEL 19 /* QB MSCP ctrl model */ #define RQ_MODEL (UNIBUS? RQU_MODEL: RQQ_MODEL) #define RQ_HVER 1 /* hardware version */ #define RQ_SVER 3 /* software version */ #define RQ_DHTMO 60 /* def host timeout */ #define RQ_DCTMO 120 /* def ctrl timeout */ #define RQ_NUMDR 4 /* # drives */ #define RQ_NUMBY 512 /* bytes per block */ #define RQ_MAXFR (1 << 16) /* max xfer */ #define UNIT_V_ONL (UNIT_V_UF + 0) /* online */ #define UNIT_V_WLK (UNIT_V_UF + 1) /* hwre write lock */ #define UNIT_V_ATP (UNIT_V_UF + 2) /* attn pending */ #define UNIT_V_DTYPE (UNIT_V_UF + 3) /* drive type */ #define UNIT_M_DTYPE 0xF #define UNIT_ONL (1 << UNIT_V_ONL) #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_ATP (1 << UNIT_V_ATP) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define cpkt u3 /* current packet */ #define pktq u4 /* packet queue */ #define uf buf /* settable unit flags */ #define cnum wait /* controller index */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ #define RQ_RMV(u) ((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RMV)? \ UF_RMV: 0) #define RQ_WPH(u) (((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RO) || \ (u->flags & UNIT_WPRT))? UF_WPH: 0) #define CST_S1 0 /* init stage 1 */ #define CST_S1_WR 1 /* stage 1 wrap */ #define CST_S2 2 /* init stage 2 */ #define CST_S3 3 /* init stage 3 */ #define CST_S3_PPA 4 /* stage 3 sa wait */ #define CST_S3_PPB 5 /* stage 3 ip wait */ #define CST_S4 6 /* stage 4 */ #define CST_UP 7 /* online */ #define CST_DEAD 8 /* fatal error */ #define ERR 0 /* must be SCPE_OK! */ #define OK 1 #define RQ_TIMER (RQ_NUMDR) #define RQ_QUEUE (RQ_TIMER + 1) /* Internal packet management. The real RQDX3 manages its packets as true linked lists. However, use of actual addresses in structures won't work with save/restore. Accordingly, the packets are an arrayed structure, and links are actually subscripts. To minimize complexity, packet[0] is not used (0 = end of list), and the number of packets must be a power of two. */ #define RQ_NPKTS 32 /* # packets (pwr of 2) */ #define RQ_M_NPKTS (RQ_NPKTS - 1) /* mask */ #define RQ_PKT_SIZE_W 32 /* payload size (wds) */ #define RQ_PKT_SIZE (RQ_PKT_SIZE_W * sizeof (int16)) struct rqpkt { int16 link; /* link to next */ uint16 d[RQ_PKT_SIZE_W]; /* data */ }; /* Packet payload extraction and insertion; cp defines controller */ #define GETP(p,w,f) ((cp->pak[p].d[w] >> w##_V_##f) & w##_M_##f) #define GETP32(p,w) (((uint32) cp->pak[p].d[w]) | \ (((uint32) cp->pak[p].d[(w)+1]) << 16)) #define PUTP32(p,w,x) cp->pak[p].d[w] = (x) & 0xFFFF; \ cp->pak[p].d[(w)+1] = ((x) >> 16) & 0xFFFF /* Disk formats. An RQDX3 disk consists of the following regions: XBNs Extended blocks - contain information about disk format, also holds track being reformatted during bad block repl. Size = sectors/track + 1, replicated 3 times. DBNs Diagnostic blocks - used by diagnostics. Sized to pad out the XBNs to a cylinder boundary. LBNs Logical blocks - contain user information. RCT Replacement control table - first block contains status, second contains data from block being replaced, remaining contain information about replaced bad blocks. Size = RBNs/128 + 3, replicated 4-8 times. RBNs Replacement blocks - used to replace bad blocks. The simulator does not need to perform bad block replacement; the information below is for simulating RCT reads, if required. Note that an RA drive has a different order: LBNs, RCT, XBN, DBN; the RBNs are spare blocks at the end of every track. */ #define RCT_OVHD 2 /* #ovhd blks */ #define RCT_ENTB 128 /* entries/blk */ #define RCT_END 0x80000000 /* marks RCT end */ /* The RQDX3 supports multiple disk drive types (x = not implemented): type sec surf cyl tpg gpc RCT LBNs RX50 10 1 80 5 16 - 800 RX33 15 2 80 2 1 - 2400 RD51 18 4 306 4 1 36*4 21600 RD31 17 4 615 4 1 3*8 41560 RD52 17 8 512 8 1 4*8 60480 x RD32 17 6 820 ? ? ? 83236 x RD33 17 7 1170 ? ? ? 138565 RD53 17 7 1024 7 1 5*8 138672 RD54 17 15 1225 15 1 7*8 311200 The simulator also supports larger drives that only existed on SDI controllers. RA60 42(+1) 6 1600 6 1 1008 400176 x RA70 33(+1) 11 1507+ 11 1 ? 547041 RA81 51(+1) 14 1258 14 1 2856 891072 RA82 57(+1) 15 1435 15 1 3420 1216665 RA71 51(+1) 14 1921 14 1 1428 1367310 RA72 51(+1) 20 1921 20 1 2040 1953300 RA90 69(+1) 13 2656 13 1 1794 2376153 RA92 73(+1) 13 3101 13 1 949 2940951 x RA73 70(+1) 21 2667+ 21 1 ? 3920490 Each drive can be a different type. The drive field in the unit flags specified the drive type and thus, indirectly, the drive size. */ #define RQDF_RMV 01 /* removable */ #define RQDF_RO 02 /* read only */ #define RQDF_SDI 04 /* SDI drive */ #define RX50_DTYPE 0 #define RX50_SECT 10 #define RX50_SURF 1 #define RX50_CYL 80 #define RX50_TPG 5 #define RX50_GPC 16 #define RX50_XBN 0 #define RX50_DBN 0 #define RX50_LBN 800 #define RX50_RCTS 0 #define RX50_RCTC 0 #define RX50_RBN 0 #define RX50_MOD 7 #define RX50_MED 0x25658032 #define RX50_FLGS RQDF_RMV #define RX33_DTYPE 1 #define RX33_SECT 15 #define RX33_SURF 2 #define RX33_CYL 80 #define RX33_TPG 2 #define RX33_GPC 1 #define RX33_XBN 0 #define RX33_DBN 0 #define RX33_LBN 2400 #define RX33_RCTS 0 #define RX33_RCTC 0 #define RX33_RBN 0 #define RX33_MOD 10 #define RX33_MED 0x25658021 #define RX33_FLGS RQDF_RMV #define RD51_DTYPE 2 #define RD51_SECT 18 #define RD51_SURF 4 #define RD51_CYL 306 #define RD51_TPG 4 #define RD51_GPC 1 #define RD51_XBN 57 #define RD51_DBN 87 #define RD51_LBN 21600 #define RD51_RCTS 36 #define RD51_RCTC 4 #define RD51_RBN 144 #define RD51_MOD 6 #define RD51_MED 0x25644033 #define RD51_FLGS 0 #define RD31_DTYPE 3 #define RD31_SECT 17 #define RD31_SURF 4 #define RD31_CYL 615 /* last unused */ #define RD31_TPG RD31_SURF #define RD31_GPC 1 #define RD31_XBN 54 #define RD31_DBN 14 #define RD31_LBN 41560 #define RD31_RCTS 3 #define RD31_RCTC 8 #define RD31_RBN 100 #define RD31_MOD 12 #define RD31_MED 0x2564401F #define RD31_FLGS 0 #define RD52_DTYPE 4 /* Quantum params */ #define RD52_SECT 17 #define RD52_SURF 8 #define RD52_CYL 512 #define RD52_TPG RD52_SURF #define RD52_GPC 1 #define RD52_XBN 54 #define RD52_DBN 82 #define RD52_LBN 60480 #define RD52_RCTS 4 #define RD52_RCTC 8 #define RD52_RBN 168 #define RD52_MOD 8 #define RD52_MED 0x25644034 #define RD52_FLGS 0 #define RD53_DTYPE 5 #define RD53_SECT 17 #define RD53_SURF 8 #define RD53_CYL 1024 /* last unused */ #define RD53_TPG RD53_SURF #define RD53_GPC 1 #define RD53_XBN 54 #define RD53_DBN 82 #define RD53_LBN 138672 #define RD53_RCTS 5 #define RD53_RCTC 8 #define RD53_RBN 280 #define RD53_MOD 9 #define RD53_MED 0x25644035 #define RD53_FLGS 0 #define RD54_DTYPE 6 #define RD54_SECT 17 #define RD54_SURF 15 #define RD54_CYL 1225 /* last unused */ #define RD54_TPG RD54_SURF #define RD54_GPC 1 #define RD54_XBN 54 #define RD54_DBN 201 #define RD54_LBN 311200 #define RD54_RCTS 7 #define RD54_RCTC 8 #define RD54_RBN 609 #define RD54_MOD 13 #define RD54_MED 0x25644036 #define RD54_FLGS 0 #define RA82_DTYPE 7 /* SDI drive */ #define RA82_SECT 57 /* +1 spare/track */ #define RA82_SURF 15 #define RA82_CYL 1435 /* 0-1422 user */ #define RA82_TPG RA82_SURF #define RA82_GPC 1 #define RA82_XBN 3480 /* cyl 1427-1430 */ #define RA82_DBN 3480 /* cyl 1431-1434 */ #define RA82_LBN 1216665 /* 57*15*1423 */ #define RA82_RCTS 3420 /* cyl 1423-1426 */ #define RA82_RCTC 1 #define RA82_RBN 21345 /* 1 *15*1423 */ #define RA82_MOD 11 #define RA82_MED 0x25641052 #define RA82_FLGS RQDF_SDI #define RRD40_DTYPE 8 #define RRD40_SECT 128 #define RRD40_SURF 1 #define RRD40_CYL 10400 #define RRD40_TPG RRD40_SURF #define RRD40_GPC 1 #define RRD40_XBN 0 #define RRD40_DBN 0 #define RRD40_LBN 1331200 #define RRD40_RCTS 0 #define RRD40_RCTC 0 #define RRD40_RBN 0 #define RRD40_MOD 26 #define RRD40_MED 0x25652228 #define RRD40_FLGS (RQDF_RMV | RQDF_RO) #define RA72_DTYPE 9 /* SDI drive */ #define RA72_SECT 51 /* +1 spare/trk */ #define RA72_SURF 20 #define RA72_CYL 1921 /* 0-1914 user */ #define RA72_TPG RA72_SURF #define RA72_GPC 1 #define RA72_XBN 2080 /* cyl 1917-1918? */ #define RA72_DBN 2080 /* cyl 1920-1921? */ #define RA72_LBN 1953300 /* 51*20*1915 */ #define RA72_RCTS 2040 /* cyl 1915-1916? */ #define RA72_RCTC 1 #define RA72_RBN 38300 /* 1 *20*1915 */ #define RA72_MOD 37 #define RA72_MED 0x25641048 #define RA72_FLGS RQDF_SDI #define RA90_DTYPE 10 /* SDI drive */ #define RA90_SECT 69 /* +1 spare/trk */ #define RA90_SURF 13 #define RA90_CYL 2656 /* 0-2648 user */ #define RA90_TPG RA90_SURF #define RA90_GPC 1 #define RA90_XBN 1820 /* cyl 2651-2652? */ #define RA90_DBN 1820 /* cyl 2653-2654? */ #define RA90_LBN 2376153 /* 69*13*2649 */ #define RA90_RCTS 1794 /* cyl 2649-2650? */ #define RA90_RCTC 1 #define RA90_RBN 34437 /* 1 *13*2649 */ #define RA90_MOD 19 #define RA90_MED 0x2564105A #define RA90_FLGS RQDF_SDI #define RA92_DTYPE 11 /* SDI drive */ #define RA92_SECT 73 /* +1 spare/trk */ #define RA92_SURF 13 #define RA92_CYL 3101 /* 0-3098 user */ #define RA92_TPG RA92_SURF #define RA92_GPC 1 #define RA92_XBN 174 /* cyl 3100? */ #define RA92_DBN 788 #define RA92_LBN 2940951 /* 73*13*3099 */ #define RA92_RCTS 949 /* cyl 3099? */ #define RA92_RCTC 1 #define RA92_RBN 40287 /* 1 *13*3099 */ #define RA92_MOD 29 #define RA92_MED 0x2564105C #define RA92_FLGS RQDF_SDI #define RA8U_DTYPE 12 /* user defined */ #define RA8U_SECT 57 /* from RA82 */ #define RA8U_SURF 15 #define RA8U_CYL 1435 /* from RA82 */ #define RA8U_TPG RA8U_SURF #define RA8U_GPC 1 #define RA8U_XBN 0 #define RA8U_DBN 0 #define RA8U_LBN 1216665 /* from RA82 */ #define RA8U_RCTS 400 #define RA8U_RCTC 8 #define RA8U_RBN 21345 #define RA8U_MOD 11 /* RA82 */ #define RA8U_MED 0x25641052 /* RA82 */ #define RA8U_FLGS RQDF_SDI #define RA8U_MINC 10000 /* min cap LBNs */ #define RA8U_MAXC 4000000 /* max cap LBNs */ #define RA8U_EMAXC 2000000000 /* ext max cap */ #define RA60_DTYPE 13 /* SDI drive */ #define RA60_SECT 42 /* +1 spare/track */ #define RA60_SURF 6 #define RA60_CYL 1600 /* 0-1587 user */ #define RA60_TPG RA60_SURF #define RA60_GPC 1 #define RA60_XBN 1032 /* cyl 1592-1595 */ #define RA60_DBN 1032 /* cyl 1596-1599 */ #define RA60_LBN 400176 /* 42*6*1588 */ #define RA60_RCTS 1008 /* cyl 1588-1591 */ #define RA60_RCTC 1 #define RA60_RBN 9528 /* 1 *6*1588 */ #define RA60_MOD 4 #define RA60_MED 0x22A4103C #define RA60_FLGS (RQDF_RMV | RQDF_SDI) #define RA81_DTYPE 14 /* SDI drive */ #define RA81_SECT 51 /* +1 spare/track */ #define RA81_SURF 14 #define RA81_CYL 1258 /* 0-1247 user */ #define RA81_TPG RA81_SURF #define RA81_GPC 1 #define RA81_XBN 2436 /* cyl 1252-1254? */ #define RA81_DBN 2436 /* cyl 1255-1256? */ #define RA81_LBN 891072 /* 51*14*1248 */ #define RA81_RCTS 2856 /* cyl 1248-1251? */ #define RA81_RCTC 1 #define RA81_RBN 17472 /* 1 *14*1248 */ #define RA81_MOD 5 #define RA81_MED 0x25641051 #define RA81_FLGS RQDF_SDI #define RA71_DTYPE 15 /* SDI drive */ #define RA71_SECT 51 /* +1 spare/track */ #define RA71_SURF 14 #define RA71_CYL 1921 /* 0-1914 user */ #define RA71_TPG RA71_SURF #define RA71_GPC 1 #define RA71_XBN 1456 /* cyl 1917-1918? */ #define RA71_DBN 1456 /* cyl 1919-1920? */ #define RA71_LBN 1367310 /* 51*14*1915 */ #define RA71_RCTS 1428 /* cyl 1915-1916? */ #define RA71_RCTC 1 #define RA71_RBN 26810 /* 1 *14*1915 */ #define RA71_MOD 40 #define RA71_MED 0x25641047 #define RA71_FLGS RQDF_SDI struct drvtyp { int32 sect; /* sectors */ int32 surf; /* surfaces */ int32 cyl; /* cylinders */ int32 tpg; /* trk/grp */ int32 gpc; /* grp/cyl */ int32 xbn; /* XBN size */ int32 dbn; /* DBN size */ uint32 lbn; /* LBN size */ int32 rcts; /* RCT size */ int32 rctc; /* RCT copies */ int32 rbn; /* RBNs */ int32 mod; /* MSCP model */ int32 med; /* MSCP media */ int32 flgs; /* flags */ char *name; /* name */ }; #define RQ_DRV(d) \ d##_SECT, d##_SURF, d##_CYL, d##_TPG, \ d##_GPC, d##_XBN, d##_DBN, d##_LBN, \ d##_RCTS, d##_RCTC, d##_RBN, d##_MOD, \ d##_MED, d##_FLGS #define RQ_SIZE(d) (d##_LBN * RQ_NUMBY) static struct drvtyp drv_tab[] = { { RQ_DRV (RX50), "RX50" }, { RQ_DRV (RX33), "RX33" }, { RQ_DRV (RD51), "RD51" }, { RQ_DRV (RD31), "RD31" }, { RQ_DRV (RD52), "RD52" }, { RQ_DRV (RD53), "RD53" }, { RQ_DRV (RD54), "RD54" }, { RQ_DRV (RA82), "RA82" }, { RQ_DRV (RRD40), "RRD40" }, { RQ_DRV (RA72), "RA72" }, { RQ_DRV (RA90), "RA90" }, { RQ_DRV (RA92), "RA92" }, { RQ_DRV (RA8U), "RAUSER" }, { RQ_DRV (RA60), "RA60" }, { RQ_DRV (RA81), "RA81" }, { RQ_DRV (RA71), "RA71" }, { 0 } }; extern int32 int_req[IPL_HLVL]; extern int32 tmr_poll, clk_tps; extern UNIT cpu_unit; extern FILE *sim_deb; extern uint32 sim_taddr_64; extern int32 sim_switches; uint16 *rqxb = NULL; /* xfer buffer */ int32 rq_itime = 200; /* init time, except */ int32 rq_itime4 = 10; /* stage 4 */ int32 rq_qtime = RQ_QTIME; /* queue time */ int32 rq_xtime = RQ_XTIME; /* transfer time */ typedef struct { uint32 cnum; /* ctrl number */ uint32 ubase; /* unit base */ uint32 sa; /* status, addr */ uint32 saw; /* written data */ uint32 s1dat; /* S1 data */ uint32 comm; /* comm region */ uint32 csta; /* ctrl state */ uint32 perr; /* last error */ uint32 cflgs; /* ctrl flags */ uint32 irq; /* intr request */ uint32 prgi; /* purge int */ uint32 pip; /* poll in progress */ int32 freq; /* free list */ int32 rspq; /* resp list */ uint32 pbsy; /* #busy pkts */ uint32 credits; /* credits */ uint32 hat; /* host timer */ uint32 htmo; /* host timeout */ struct uq_ring cq; /* cmd ring */ struct uq_ring rq; /* rsp ring */ struct rqpkt pak[RQ_NPKTS]; /* packet queue */ } MSC; DEVICE rq_dev, rqb_dev, rqc_dev,rqd_dev; t_stat rq_rd (int32 *data, int32 PA, int32 access); t_stat rq_wr (int32 data, int32 PA, int32 access); t_stat rq_svc (UNIT *uptr); t_stat rq_tmrsvc (UNIT *uptr); t_stat rq_quesvc (UNIT *uptr); t_stat rq_reset (DEVICE *dptr); t_stat rq_attach (UNIT *uptr, char *cptr); t_stat rq_detach (UNIT *uptr); t_stat rq_boot (int32 unitno, DEVICE *dptr); t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc); t_bool rq_step4 (MSC *cp); t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q); t_bool rq_abo (MSC *cp, int32 pkt, t_bool q); t_bool rq_avl (MSC *cp, int32 pkt, t_bool q); t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q); t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q); t_bool rq_gus (MSC *cp, int32 pkt, t_bool q); t_bool rq_onl (MSC *cp, int32 pkt, t_bool q); t_bool rq_rw (MSC *cp, int32 pkt, t_bool q); t_bool rq_scc (MSC *cp, int32 pkt, t_bool q); t_bool rq_suc (MSC *cp, int32 pkt, t_bool q); t_bool rq_plf (MSC *cp, uint32 err); t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err); t_bool rq_hbe (MSC *cp, UNIT *uptr); t_bool rq_una (MSC *cp, int32 un); t_bool rq_deqf (MSC *cp, int32 *pkt); int32 rq_deqh (MSC *cp, int32 *lh); void rq_enqh (MSC *cp, int32 *lh, int32 pkt); void rq_enqt (MSC *cp, int32 *lh, int32 pkt); t_bool rq_getpkt (MSC *cp, int32 *pkt); t_bool rq_putpkt (MSC *cp, int32 pkt, t_bool qt); t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc); t_bool rq_putdesc (MSC *cp, struct uq_ring *ring, uint32 desc); int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd); t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts); void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ); void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all); void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr); void rq_init_int (MSC *cp); void rq_ring_int (MSC *cp, struct uq_ring *ring); t_bool rq_fatal (MSC *cp, uint32 err); UNIT *rq_getucb (MSC *cp, uint32 lu); int32 rq_map_pa (uint32 pa); void rq_setint (MSC *cp); void rq_clrint (MSC *cp); int32 rq_inta (void); /* RQ data structures rq_dev RQ device descriptor rq_unit RQ unit list rq_reg RQ register list rq_mod RQ modifier list */ MSC rq_ctx = { 0 }; DIB rq_dib = { IOBA_RQ, IOLN_RQ, &rq_rd, &rq_wr, 1, IVCL (RQ), 0, { &rq_inta } }; UNIT rq_unit[] = { { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RX50_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RX50)) }, { UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, { UDATA (&rq_quesvc, UNIT_DIS, 0) } }; REG rq_reg[] = { { GRDATA (SA, rq_ctx.sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, rq_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rq_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rq_ctx.comm, DEV_RDX, 22, 0) }, { GRDATA (CQBA, rq_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rq_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rq_ctx.cq.idx, DEV_RDX, 8, 2) }, { GRDATA (RQBA, rq_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rq_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rq_ctx.rq.idx, DEV_RDX, 8, 2) }, { DRDATA (FREE, rq_ctx.freq, 5) }, { DRDATA (RESP, rq_ctx.rspq, 5) }, { DRDATA (PBSY, rq_ctx.pbsy, 5) }, { GRDATA (CFLGS, rq_ctx.cflgs, DEV_RDX, 16, 0) }, { GRDATA (CSTA, rq_ctx.csta, DEV_RDX, 4, 0) }, { GRDATA (PERR, rq_ctx.perr, DEV_RDX, 9, 0) }, { DRDATA (CRED, rq_ctx.credits, 5) }, { DRDATA (HAT, rq_ctx.hat, 17) }, { DRDATA (HTMO, rq_ctx.htmo, 17) }, { FLDATA (PRGI, rq_ctx.prgi, 0), REG_HIDDEN }, { FLDATA (PIP, rq_ctx.pip, 0), REG_HIDDEN }, { FLDATA (INT, rq_ctx.irq, 0) }, { DRDATA (ITIME, rq_itime, 24), PV_LEFT + REG_NZ }, { DRDATA (I4TIME, rq_itime4, 24), PV_LEFT + REG_NZ }, { DRDATA (QTIME, rq_qtime, 24), PV_LEFT + REG_NZ }, { DRDATA (XTIME, rq_xtime, 24), PV_LEFT + REG_NZ }, { BRDATA (PKTS, rq_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, { URDATA (CPKT, rq_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rq_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rq_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, { URDATA (CAPAC, rq_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { DRDATA (DEVLBN, drv_tab[RA8U_DTYPE].lbn, 22), REG_HRO }, { NULL } }; MTAB rq_mod[] = { { UNIT_WLK, 0, NULL, "WRITEENABLED", &rq_set_wlk }, { UNIT_WLK, UNIT_WLK, NULL, "LOCKED", &rq_set_wlk }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RI, "RINGS", NULL, NULL, &rq_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_FR, "FREEQ", NULL, NULL, &rq_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RS, "RESPQ", NULL, NULL, &rq_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_UN, "UNITQ", NULL, NULL, &rq_show_ctrl, 0 }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_ALL, "ALL", NULL, NULL, &rq_show_ctrl, 0 }, { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, NULL, &rq_show_unitq, 0 }, { MTAB_XTD | MTAB_VUN, 0, "WRITE", NULL, NULL, &rq_show_wlk, NULL }, { MTAB_XTD | MTAB_VUN, RX50_DTYPE, NULL, "RX50", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RX33_DTYPE, NULL, "RX33", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RD31_DTYPE, NULL, "RD31", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RD51_DTYPE, NULL, "RD51", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RD52_DTYPE, NULL, "RD52", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RD53_DTYPE, NULL, "RD53", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RD54_DTYPE, NULL, "RD54", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA60_DTYPE, NULL, "RA60", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA81_DTYPE, NULL, "RA81", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA82_DTYPE, NULL, "RA82", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "RRD40", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "CDROM", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA71_DTYPE, NULL, "RA71", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA72_DTYPE, NULL, "RA72", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA90_DTYPE, NULL, "RA90", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA92_DTYPE, NULL, "RA92", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, RA8U_DTYPE, NULL, "RAUSER", &rq_set_type, NULL, NULL }, { MTAB_XTD | MTAB_VUN, 0, "TYPE", NULL, NULL, &rq_show_type, NULL }, #if defined (VM_PDP11) { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, #else { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, NULL, &show_addr, NULL }, #endif { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec, NULL }, { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", &set_addr_flt, NULL, NULL }, { 0 } }; DEVICE rq_dev = { "RQ", rq_unit, rq_reg, rq_mod, RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, &rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; /* RQB data structures rqb_dev RQB device descriptor rqb_unit RQB unit list rqb_reg RQB register list rqb_mod RQB modifier list */ MSC rqb_ctx = { 1 }; DIB rqb_dib = { IOBA_RQB, IOLN_RQB, &rq_rd, &rq_wr, 1, IVCL (RQ), 0, { &rq_inta } }; UNIT rqb_unit[] = { { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, { UDATA (&rq_quesvc, UNIT_DIS, 0) } }; REG rqb_reg[] = { { GRDATA (SA, rqb_ctx.sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, rqb_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqb_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqb_ctx.comm, DEV_RDX, 22, 0) }, { GRDATA (CQBA, rqb_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqb_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqb_ctx.cq.idx, DEV_RDX, 8, 2) }, { GRDATA (RQBA, rqb_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqb_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqb_ctx.rq.idx, DEV_RDX, 8, 2) }, { DRDATA (FREE, rqb_ctx.freq, 5) }, { DRDATA (RESP, rqb_ctx.rspq, 5) }, { DRDATA (PBSY, rqb_ctx.pbsy, 5) }, { GRDATA (CFLGS, rqb_ctx.cflgs, DEV_RDX, 16, 0) }, { GRDATA (CSTA, rqb_ctx.csta, DEV_RDX, 4, 0) }, { GRDATA (PERR, rqb_ctx.perr, DEV_RDX, 9, 0) }, { DRDATA (CRED, rqb_ctx.credits, 5) }, { DRDATA (HAT, rqb_ctx.hat, 17) }, { DRDATA (HTMO, rqb_ctx.htmo, 17) }, { FLDATA (PRGI, rqb_ctx.prgi, 0), REG_HIDDEN }, { FLDATA (PIP, rqb_ctx.pip, 0), REG_HIDDEN }, { FLDATA (INT, rqb_ctx.irq, 0) }, { BRDATA (PKTS, rqb_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, { URDATA (CPKT, rqb_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqb_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqb_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, { URDATA (CAPAC, rqb_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqb_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqb_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; DEVICE rqb_dev = { "RQB", rqb_unit, rqb_reg, rq_mod, RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, &rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; /* RQC data structures rqc_dev RQC device descriptor rqc_unit RQC unit list rqc_reg RQC register list rqc_mod RQC modifier list */ MSC rqc_ctx = { 2 }; DIB rqc_dib = { IOBA_RQC, IOLN_RQC, &rq_rd, &rq_wr, 1, IVCL (RQ), 0, { &rq_inta } }; UNIT rqc_unit[] = { { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, { UDATA (&rq_quesvc, UNIT_DIS, 0) } }; REG rqc_reg[] = { { GRDATA (SA, rqc_ctx.sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, rqc_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqc_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqc_ctx.comm, DEV_RDX, 22, 0) }, { GRDATA (CQBA, rqc_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqc_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqc_ctx.cq.idx, DEV_RDX, 8, 2) }, { GRDATA (RQBA, rqc_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqc_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqc_ctx.rq.idx, DEV_RDX, 8, 2) }, { DRDATA (FREE, rqc_ctx.freq, 5) }, { DRDATA (RESP, rqc_ctx.rspq, 5) }, { DRDATA (PBSY, rqc_ctx.pbsy, 5) }, { GRDATA (CFLGS, rqc_ctx.cflgs, DEV_RDX, 16, 0) }, { GRDATA (CSTA, rqc_ctx.csta, DEV_RDX, 4, 0) }, { GRDATA (PERR, rqc_ctx.perr, DEV_RDX, 9, 0) }, { DRDATA (CRED, rqc_ctx.credits, 5) }, { DRDATA (HAT, rqc_ctx.hat, 17) }, { DRDATA (HTMO, rqc_ctx.htmo, 17) }, { FLDATA (PRGI, rqc_ctx.prgi, 0), REG_HIDDEN }, { FLDATA (PIP, rqc_ctx.pip, 0), REG_HIDDEN }, { FLDATA (INT, rqc_ctx.irq, 0) }, { BRDATA (PKTS, rqc_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, { URDATA (CPKT, rqc_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqc_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqc_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, { URDATA (CAPAC, rqc_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqc_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqc_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; DEVICE rqc_dev = { "RQC", rqc_unit, rqc_reg, rq_mod, RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, &rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; /* RQD data structures rqd_dev RQ device descriptor rqd_unit RQ unit list rqd_reg RQ register list rqd_mod RQ modifier list */ MSC rqd_ctx = { 3 }; DIB rqd_dib = { IOBA_RQD, IOLN_RQD, &rq_rd, &rq_wr, 1, IVCL (RQ), 0, { &rq_inta } }; UNIT rqd_unit[] = { { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, { UDATA (&rq_quesvc, UNIT_DIS, 0) } }; REG rqd_reg[] = { { GRDATA (SA, rqd_ctx.sa, DEV_RDX, 16, 0) }, { GRDATA (SAW, rqd_ctx.saw, DEV_RDX, 16, 0) }, { GRDATA (S1DAT, rqd_ctx.s1dat, DEV_RDX, 16, 0) }, { GRDATA (COMM, rqd_ctx.comm, DEV_RDX, 22, 0) }, { GRDATA (CQBA, rqd_ctx.cq.ba, DEV_RDX, 22, 0) }, { GRDATA (CQLNT, rqd_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (CQIDX, rqd_ctx.cq.idx, DEV_RDX, 8, 2) }, { GRDATA (RQBA, rqd_ctx.rq.ba, DEV_RDX, 22, 0) }, { GRDATA (RQLNT, rqd_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, { GRDATA (RQIDX, rqd_ctx.rq.idx, DEV_RDX, 8, 2) }, { DRDATA (FREE, rqd_ctx.freq, 5) }, { DRDATA (RESP, rqd_ctx.rspq, 5) }, { DRDATA (PBSY, rqd_ctx.pbsy, 5) }, { GRDATA (CFLGS, rqd_ctx.cflgs, DEV_RDX, 16, 0) }, { GRDATA (CSTA, rqd_ctx.csta, DEV_RDX, 4, 0) }, { GRDATA (PERR, rqd_ctx.perr, DEV_RDX, 9, 0) }, { DRDATA (CRED, rqd_ctx.credits, 5) }, { DRDATA (HAT, rqd_ctx.hat, 17) }, { DRDATA (HTMO, rqd_ctx.htmo, 17) }, { FLDATA (PRGI, rqd_ctx.prgi, 0), REG_HIDDEN }, { FLDATA (PIP, rqd_ctx.pip, 0), REG_HIDDEN }, { FLDATA (INT, rqd_ctx.irq, 0) }, { BRDATA (PKTS, rqd_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, { URDATA (CPKT, rqd_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (PKTQ, rqd_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, { URDATA (UFLG, rqd_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, { URDATA (CAPAC, rqd_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, { GRDATA (DEVADDR, rqd_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, rqd_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; DEVICE rqd_dev = { "RQD", rqd_unit, rqd_reg, rq_mod, RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, NULL, NULL, &rq_reset, &rq_boot, &rq_attach, &rq_detach, &rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG }; static DEVICE *rq_devmap[RQ_NUMCT] = { &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev }; static MSC *rq_ctxmap[RQ_NUMCT] = { &rq_ctx, &rqb_ctx, &rqc_ctx, &rqd_ctx }; /* I/O dispatch routines, I/O addresses 17772150 - 17772152 base + 0 IP read/write base + 2 SA read/write */ t_stat rq_rd (int32 *data, int32 PA, int32 access) { int32 cidx = rq_map_pa ((uint32) PA); MSC *cp = rq_ctxmap[cidx]; DEVICE *dptr = rq_devmap[cidx]; if (cidx < 0) return SCPE_IERR; switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ *data = 0; /* reads zero */ if (cp->csta == CST_S3_PPB) /* waiting for poll? */ rq_step4 (cp); else if (cp->csta == CST_UP) { /* if up */ if (DEBUG_PRD (dptr)) fprintf (sim_deb, ">>RQ%c: poll started, PC=%X\n", 'A' + cp->cnum, OLDPC); cp->pip = 1; /* poll host */ sim_activate (dptr->units + RQ_QUEUE, rq_qtime); } break; case 1: /* SA */ *data = cp->sa; break; } return SCPE_OK; } t_stat rq_wr (int32 data, int32 PA, int32 access) { int32 cidx = rq_map_pa ((uint32) PA); MSC *cp = rq_ctxmap[cidx]; DEVICE *dptr = rq_devmap[cidx]; if (cidx < 0) return SCPE_IERR; switch ((PA >> 1) & 01) { /* decode PA<1> */ case 0: /* IP */ rq_reset (rq_devmap[cidx]); /* init device */ if (DEBUG_PRD (dptr)) fprintf (sim_deb, ">>RQ%c: initialization started\n", 'A' + cp->cnum); break; case 1: /* SA */ cp->saw = data; if (cp->csta < CST_S4) /* stages 1-3 */ sim_activate (dptr->units + RQ_QUEUE, rq_itime); else if (cp->csta == CST_S4) /* stage 4 (fast) */ sim_activate (dptr->units + RQ_QUEUE, rq_itime4); break; } return SCPE_OK; } /* Map physical address to device context */ int32 rq_map_pa (uint32 pa) { int32 i; DEVICE *dptr; DIB *dibp; for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrls */ dptr = rq_devmap[i]; /* get device */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if ((pa >= dibp->ba) && /* in range? */ (pa < (dibp->ba + dibp->lnt))) return i; /* return ctrl idx */ } return -1; } /* Transition to step 4 - init communications region */ t_bool rq_step4 (MSC *cp) { int32 i, lnt; uint32 base; uint16 zero[SA_COMM_MAX >> 1]; cp->rq.ioff = SA_COMM_RI; /* set intr offset */ cp->rq.ba = cp->comm; /* set rsp q base */ cp->rq.lnt = SA_S1H_RQ (cp->s1dat) << 2; /* get resp q len */ cp->cq.ioff = SA_COMM_CI; /* set intr offset */ cp->cq.ba = cp->comm + cp->rq.lnt; /* set cmd q base */ cp->cq.lnt = SA_S1H_CQ (cp->s1dat) << 2; /* get cmd q len */ cp->cq.idx = cp->rq.idx = 0; /* clear q idx's */ if (cp->prgi) base = cp->comm + SA_COMM_QQ; else base = cp->comm + SA_COMM_CI; lnt = cp->comm + cp->cq.lnt + cp->rq.lnt - base; /* comm lnt */ if (lnt > SA_COMM_MAX) /* paranoia */ lnt = SA_COMM_MAX; for (i = 0; i < (lnt >> 1); i++) /* clr buffer */ zero[i] = 0; if (Map_WriteW (base, lnt, zero)) /* zero comm area */ return rq_fatal (cp, PE_QWE); /* error? */ cp->sa = SA_S4 | (RQ_UQPM << SA_S4C_V_MOD) | /* send step 4 */ (RQ_SVER << SA_S4C_V_VER); cp->csta = CST_S4; /* set step 4 */ rq_init_int (cp); /* poke host */ return OK; } /* Queue service - invoked when any of the queues (host queue, unit queues, response queue) require servicing. Also invoked during initialization to provide some delay to the next step. Process at most one item off each unit queue If the unit queues were empty, process at most one item off the host queue Process at most one item off the response queue If all queues are idle, terminate thread */ t_stat rq_quesvc (UNIT *uptr) { int32 i, cnid; int32 pkt = 0; UNIT *nuptr; MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; DIB *dibp = (DIB *) dptr->ctxt; if (cp->csta < CST_UP) { /* still init? */ switch (cp->csta) { /* controller state? */ case CST_S1: /* need S1 reply */ if (cp->saw & SA_S1H_VL) { /* valid? */ if (cp->saw & SA_S1H_WR) { /* wrap? */ cp->sa = cp->saw; /* echo data */ cp->csta = CST_S1_WR; /* endless loop */ } else { cp->s1dat = cp->saw; /* save data */ dibp->vec = (cp->s1dat & SA_S1H_VEC) << 2; /* get vector */ if (dibp->vec) /* if nz, bias */ dibp->vec = dibp->vec + VEC_Q; cp->sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (cp->s1dat); cp->csta = CST_S2; /* now in step 2 */ rq_init_int (cp); /* intr if req */ } } /* end if valid */ break; case CST_S1_WR: /* wrap mode */ cp->sa = cp->saw; /* echo data */ break; case CST_S2: /* need S2 reply */ cp->comm = cp->saw & SA_S2H_CLO; /* get low addr */ cp->prgi = cp->saw & SA_S2H_PI; /* get purge int */ cp->sa = SA_S3 | SA_S3C_EC (cp->s1dat); cp->csta = CST_S3; /* now in step 3 */ rq_init_int (cp); /* intr if req */ break; case CST_S3: /* need S3 reply */ cp->comm = ((cp->saw & SA_S3H_CHI) << 16) | cp->comm; if (cp->saw & SA_S3H_PP) { /* purge/poll test? */ cp->sa = 0; /* put 0 */ cp->csta = CST_S3_PPA; /* wait for 0 write */ } else rq_step4 (cp); /* send step 4 */ break; case CST_S3_PPA: /* need purge test */ if (cp->saw) /* data not zero? */ rq_fatal (cp, PE_PPF); else cp->csta = CST_S3_PPB; /* wait for poll */ break; case CST_S4: /* need S4 reply */ if (cp->saw & SA_S4H_GO) { /* go set? */ if (DEBUG_PRD (dptr)) fprintf (sim_deb, ">>RQ%c: initialization complete\n", 'A' + cp->cnum); cp->csta = CST_UP; /* we're up */ cp->sa = 0; /* clear SA */ sim_activate (dptr->units + RQ_TIMER, tmr_poll * clk_tps); if ((cp->saw & SA_S4H_LF) && cp->perr) rq_plf (cp, cp->perr); cp->perr = 0; } break; } /* end switch */ return SCPE_OK; } /* end if */ for (i = 0; i < RQ_NUMDR; i++) { /* chk unit q's */ nuptr = dptr->units + i; /* ptr to unit */ if (nuptr->cpkt || (nuptr->pktq == 0)) continue; pkt = rq_deqh (cp, &nuptr->pktq); /* get top of q */ if (!rq_mscp (cp, pkt, FALSE)) /* process */ return SCPE_OK; } if ((pkt == 0) && cp->pip) { /* polling? */ if (!rq_getpkt (cp, &pkt)) /* get host pkt */ return SCPE_OK; if (pkt) { /* got one? */ if (DEBUG_PRD (dptr)) { fprintf (sim_deb, ">>RQ%c: cmd=%04X, mod=%04X, unit=%d, ", 'A' + cp->cnum, cp->pak[pkt].d[CMD_OPC], cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN]); fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n", cp->pak[pkt].d[RW_BCH], cp->pak[pkt].d[RW_BCL], cp->pak[pkt].d[RW_BAH], cp->pak[pkt].d[RW_BAL], cp->pak[pkt].d[RW_LBNH], cp->pak[pkt].d[RW_LBNL]); } if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ return rq_fatal (cp, PE_PIE); /* no, term thread */ cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ if (cnid == UQ_CID_MSCP) { /* MSCP packet? */ if (!rq_mscp (cp, pkt, TRUE)) /* proc, q non-seq */ return SCPE_OK; } else if (cnid == UQ_CID_DUP) { /* DUP packet? */ rq_putr (cp, pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); if (!rq_putpkt (cp, pkt, TRUE)) /* ill cmd */ return SCPE_OK; } else return rq_fatal (cp, PE_ICI); /* no, term thread */ } /* end if pkt */ else cp->pip = 0; /* discontinue poll */ } /* end if pip */ if (cp->rspq) { /* resp q? */ pkt = rq_deqh (cp, &cp->rspq); /* get top of q */ if (!rq_putpkt (cp, pkt, FALSE)) /* send to host */ return SCPE_OK; } /* end if resp q */ if (pkt) /* more to do? */ sim_activate (uptr, rq_qtime); return SCPE_OK; /* done */ } /* Clock service (roughly once per second) */ t_stat rq_tmrsvc (UNIT *uptr) { int32 i; UNIT *nuptr; MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ for (i = 0; i < RQ_NUMDR; i++) { /* poll */ nuptr = dptr->units + i; if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ (nuptr->flags & UNIT_ATT) && /* still online? */ (cp->cflgs & CF_ATN)) { /* wanted? */ if (!rq_una (cp, i)) return SCPE_OK; } nuptr->flags = nuptr->flags & ~UNIT_ATP; } if ((cp->hat > 0) && (--cp->hat == 0)) /* host timeout? */ rq_fatal (cp, PE_HAT); /* fatal err */ return SCPE_OK; } /* MSCP packet handling */ t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q) { uint32 sts, cmd = GETP (pkt, CMD_OPC, OPC); switch (cmd) { case OP_ABO: /* abort */ return rq_abo (cp, pkt, q); case OP_AVL: /* avail */ return rq_avl (cp, pkt, q); case OP_FMT: /* format */ return rq_fmt (cp, pkt, q); case OP_GCS: /* get cmd status */ return rq_gcs (cp, pkt, q); case OP_GUS: /* get unit status */ return rq_gus (cp, pkt, q); case OP_ONL: /* online */ return rq_onl (cp, pkt, q); case OP_SCC: /* set ctrl char */ return rq_scc (cp, pkt, q); case OP_SUC: /* set unit char */ return rq_suc (cp, pkt, q); case OP_ACC: /* access */ case OP_CMP: /* compare */ case OP_ERS: /* erase */ case OP_RD: /* read */ case OP_WR: /* write */ return rq_rw (cp, pkt, q); case OP_CCD: /* nops */ case OP_DAP: case OP_FLU: cmd = cmd | OP_END; /* set end flag */ sts = ST_SUC; /* success */ break; default: cmd = OP_END; /* set end op */ sts = ST_CMD | I_OPCD; /* ill op */ break; } rq_putr (cp, pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Abort a command - 1st parameter is ref # of cmd to abort */ t_bool rq_abo (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ int32 tpkt, prv; UNIT *uptr; DEVICE *dptr = rq_devmap[cp->cnum]; tpkt = 0; /* set no mtch */ if (uptr = rq_getucb (cp, lu)) { /* get unit */ if (uptr->cpkt && /* curr pkt? */ (GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */ tpkt = uptr->cpkt; /* save match */ uptr->cpkt = 0; /* gonzo */ sim_cancel (uptr); /* cancel unit */ sim_activate (dptr->units + RQ_QUEUE, rq_qtime); } else if (uptr->pktq && /* head of q? */ (GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */ tpkt = uptr->pktq; /* save match */ uptr->pktq = cp->pak[tpkt].link; /* unlink */ } else if (prv = uptr->pktq) { /* srch pkt q */ while (tpkt = cp->pak[prv].link) { /* walk list */ if (GETP32 (tpkt, RSP_REFL) == ref) { /* match? unlink */ cp->pak[prv].link = cp->pak[tpkt].link; break; } } } if (tpkt) { /* found target? */ uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ rq_putr (cp, tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); if (!rq_putpkt (cp, tpkt, TRUE)) return ERR; } } /* end if unit */ rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Unit available - set unit status to available - defer if q'd cmds */ t_bool rq_avl (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } uptr->flags = uptr->flags & ~UNIT_ONL; /* not online */ uptr->uf = 0; /* clr flags */ sts = ST_SUC; /* success */ } else sts = ST_OFL; /* offline */ rq_putr (cp, pkt, cmd | OP_END, 0, sts, AVL_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Get command status - only interested in active xfr cmd */ t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ int32 tpkt; UNIT *uptr; if ((uptr = rq_getucb (cp, lu)) && /* valid lu? */ (tpkt = uptr->cpkt) && /* queued pkt? */ (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ (GETP (tpkt, CMD_OPC, OPC) >= OP_ACC)) { /* rd/wr cmd? */ cp->pak[pkt].d[GCS_STSL] = cp->pak[tpkt].d[RW_WBCL]; cp->pak[pkt].d[GCS_STSH] = cp->pak[tpkt].d[RW_WBCH]; } else { cp->pak[pkt].d[GCS_STSL] = 0; /* return 0 */ cp->pak[pkt].d[GCS_STSH] = 0; } rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Get unit status */ t_bool rq_gus (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 dtyp, sts, rbpar; UNIT *uptr; if (cp->pak[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ if (lu >= (cp->ubase + RQ_NUMDR)) { /* end of range? */ lu = 0; /* reset to 0 */ cp->pak[pkt].d[RSP_UN] = lu; } } if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else if (uptr->flags & UNIT_ONL) /* online */ sts = ST_SUC; else sts = ST_AVL; /* avail */ rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ dtyp = GET_DTYPE (uptr->flags); /* get drive type */ if (drv_tab[dtyp].rcts) /* ctrl bad blk? */ rbpar = 1; else rbpar = 0; /* fill geom, bblk */ cp->pak[pkt].d[GUS_TRK] = drv_tab[dtyp].sect; cp->pak[pkt].d[GUS_GRP] = drv_tab[dtyp].tpg; cp->pak[pkt].d[GUS_CYL] = drv_tab[dtyp].gpc; cp->pak[pkt].d[GUS_UVER] = 0; cp->pak[pkt].d[GUS_RCTS] = drv_tab[dtyp].rcts; cp->pak[pkt].d[GUS_RBSC] = (rbpar << GUS_RB_V_RBNS) | (rbpar << GUS_RB_V_RCTC); } else sts = ST_OFL; /* offline */ cp->pak[pkt].d[GUS_SHUN] = lu; /* shadowing */ cp->pak[pkt].d[GUS_SHST] = 0; rq_putr (cp, pkt, cmd | OP_END, 0, sts, GUS_LNT_D, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Unit online - defer if q'd commands */ t_bool rq_onl (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else if (uptr->flags & UNIT_ONL) /* already online? */ sts = ST_SUC | SB_SUC_ON; else { /* mark online */ sts = ST_SUC; uptr->flags = uptr->flags | UNIT_ONL; rq_setf_unit (cp, pkt, uptr); /* hack flags */ } rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ } else sts = ST_OFL; /* offline */ cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ cp->pak[pkt].d[ONL_SHST] = 0; rq_putr (cp, pkt, cmd | OP_END, 0, sts, ONL_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Set controller characteristics */ t_bool rq_scc (MSC *cp, int32 pkt, t_bool q) { int32 sts, cmd; if (cp->pak[pkt].d[SCC_MSV]) { /* MSCP ver = 0? */ sts = ST_CMD | I_VRSN; /* no, lose */ cmd = 0; } else { sts = ST_SUC; /* success */ cmd = GETP (pkt, CMD_OPC, OPC); /* get opcode */ cp->cflgs = (cp->cflgs & CF_RPL) | /* hack ctrl flgs */ cp->pak[pkt].d[SCC_CFL]; if (cp->htmo = cp->pak[pkt].d[SCC_TMO]) /* set timeout */ cp->htmo = cp->htmo + 2; /* if nz, round up */ cp->pak[pkt].d[SCC_CFL] = cp->cflgs; /* return flags */ cp->pak[pkt].d[SCC_TMO] = RQ_DCTMO; /* ctrl timeout */ cp->pak[pkt].d[SCC_VER] = (RQ_HVER << SCC_VER_V_HVER) | (RQ_SVER << SCC_VER_V_SVER); cp->pak[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ cp->pak[pkt].d[SCC_CIDB] = 0; cp->pak[pkt].d[SCC_CIDC] = 0; cp->pak[pkt].d[SCC_CIDD] = (RQ_CLASS << SCC_CIDD_V_CLS) | (RQ_MODEL << SCC_CIDD_V_MOD); cp->pak[pkt].d[SCC_MBCL] = 0; /* max bc */ cp->pak[pkt].d[SCC_MBCH] = 0; } rq_putr (cp, pkt, cmd | OP_END, 0, sts, SCC_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Set unit characteristics - defer if q'd commands */ t_bool rq_suc (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ sts = ST_OFL | SB_OFL_NV; /* offl no vol */ else { /* avail or onl */ sts = ST_SUC; rq_setf_unit (cp, pkt, uptr); /* hack flags */ } rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ } else sts = ST_OFL; /* offline */ cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ cp->pak[pkt].d[ONL_SHST] = 0; rq_putr (cp, pkt, cmd | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Format command - floppies only */ t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } if (GET_DTYPE (uptr->flags) != RX33_DTYPE) /* RX33? */ sts = ST_CMD | I_OPCD; /* no, err */ else if ((cp->pak[pkt].d[FMT_IH] & 0100000) == 0) /* magic bit set? */ sts = ST_CMD | I_FMTI; /* no, err */ else if ((uptr->flags & UNIT_ATT) == 0) /* offline? */ sts = ST_OFL | SB_OFL_NV; /* no vol */ else if (uptr->flags & UNIT_ONL) { /* online? */ uptr->flags = uptr->flags & ~UNIT_ONL; uptr->uf = 0; /* clear flags */ sts = ST_AVL | SB_AVL_INU; /* avail, in use */ } else if (RQ_WPH (uptr)) /* write prot? */ sts = ST_WPR | SB_WPR_HW; /* can't fmt */ else sts = ST_SUC; /*** for now ***/ } else sts = ST_OFL; /* offline */ rq_putr (cp, pkt, cmd | OP_END, 0, sts, FMT_LNT, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Data transfer commands */ t_bool rq_rw (MSC *cp, int32 pkt, t_bool q) { uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ uint32 sts; UNIT *uptr; if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ if (q && uptr->cpkt) { /* need to queue? */ rq_enqt (cp, &uptr->pktq, pkt); /* do later */ return OK; } sts = rq_rw_valid (cp, pkt, uptr, cmd); /* validity checks */ if (sts == 0) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ cp->pak[pkt].d[RW_WBAL] = cp->pak[pkt].d[RW_BAL]; cp->pak[pkt].d[RW_WBAH] = cp->pak[pkt].d[RW_BAH]; cp->pak[pkt].d[RW_WBCL] = cp->pak[pkt].d[RW_BCL]; cp->pak[pkt].d[RW_WBCH] = cp->pak[pkt].d[RW_BCH]; cp->pak[pkt].d[RW_WBLL] = cp->pak[pkt].d[RW_LBNL]; cp->pak[pkt].d[RW_WBLH] = cp->pak[pkt].d[RW_LBNH]; sim_activate (uptr, rq_xtime); /* activate */ return OK; /* done */ } } else sts = ST_OFL; /* offline */ cp->pak[pkt].d[RW_BCL] = cp->pak[pkt].d[RW_BCH] = 0; /* bad packet */ rq_putr (cp, pkt, cmd | OP_END, 0, sts, RW_LNT_D, UQ_TYP_SEQ); return rq_putpkt (cp, pkt, TRUE); } /* Validity checks */ int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd) { uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ uint32 lbn = GETP32 (pkt, RW_LBNL); /* get lbn */ uint32 bc = GETP32 (pkt, RW_BCL); /* get byte cnt */ uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return (ST_OFL | SB_OFL_NV); /* offl no vol */ if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ return ST_AVL; /* only avail */ if ((cmd != OP_ACC) && (cmd != OP_ERS) && /* 'real' xfer */ (cp->pak[pkt].d[RW_BAL] & 1)) /* odd address? */ return (ST_HST | SB_HST_OA); /* host buf odd */ if (bc & 1) /* odd byte cnt? */ return (ST_HST | SB_HST_OC); if (bc & 0xF0000000) /* 'reasonable' bc? */ return (ST_CMD | I_BCNT); /* if (lbn & 0xF0000000) return (ST_CMD | I_LBN); /* 'reasonable' lbn? */ if (lbn >= maxlbn) { /* accessing RCT? */ if (lbn >= (maxlbn + drv_tab[dtyp].rcts)) /* beyond copy 1? */ return (ST_CMD | I_LBN); /* lbn err */ if (bc != RQ_NUMBY) /* bc must be 512 */ return (ST_CMD | I_BCNT); } else if ((lbn + ((bc + (RQ_NUMBY - 1)) / RQ_NUMBY)) > maxlbn) return (ST_CMD | I_BCNT); /* spiral to RCT */ if ((cmd == OP_WR) || (cmd == OP_ERS)) { /* write op? */ if (lbn >= maxlbn) /* accessing RCT? */ return (ST_CMD | I_LBN); /* lbn err */ if (uptr->uf & UF_WPS) /* swre wlk? */ return (ST_WPR | SB_WPR_SW); if (RQ_WPH (uptr)) /* hwre wlk? */ return (ST_WPR | SB_WPR_HW); } return 0; /* success! */ } /* Unit service for data transfer commands */ t_stat rq_svc (UNIT *uptr) { MSC *cp = rq_ctxmap[uptr->cnum]; uint32 i, t, tbc, abc, wwc; uint32 err = 0; int32 pkt = uptr->cpkt; /* get packet */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 ba = GETP32 (pkt, RW_WBAL); /* buf addr */ uint32 bc = GETP32 (pkt, RW_WBCL); /* byte count */ uint32 bl = GETP32 (pkt, RW_WBLL); /* block addr */ t_addr da = ((t_addr) bl) * RQ_NUMBY; /* disk addr */ if ((cp == NULL) || (pkt == 0)) /* what??? */ return STOP_RQ; tbc = (bc > RQ_MAXFR)? RQ_MAXFR: bc; /* trim cnt to max */ if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ rq_rw_end (cp, uptr, 0, ST_OFL | SB_OFL_NV); /* offl no vol */ return SCPE_OK; } if (bc == 0) { /* no xfer? */ rq_rw_end (cp, uptr, 0, ST_SUC); /* ok by me... */ return SCPE_OK; } if ((cmd == OP_ERS) || (cmd == OP_WR)) { /* write op? */ if (RQ_WPH (uptr)) { rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_HW); return SCPE_OK; } if (uptr->uf & UF_WPS) { rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_SW); return SCPE_OK; } } if (cmd == OP_ERS) { /* erase? */ wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; for (i = 0; i < wwc; i++) /* clr buf */ rqxb[i] = 0; err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); err = ferror (uptr->fileref); /* end if erase */ } else if (cmd == OP_WR) { /* write? */ t = Map_ReadW (ba, tbc, rqxb); /* fetch buffer */ if (abc = tbc - t) { /* any xfer? */ wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; for (i = (abc >> 1); i < wwc; i++) rqxb[i] = 0; err = sim_fseek (uptr->fileref, da, SEEK_SET); if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); err = ferror (uptr->fileref); } if (t) { /* nxm? */ PUTP32 (pkt, RW_WBCL, bc - abc); /* adj bc */ PUTP32 (pkt, RW_WBAL, ba + abc); /* adj ba */ if (rq_hbe (cp, uptr)) /* post err log */ rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); return SCPE_OK; /* end else wr */ } } else { err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ if (!err) { i = sim_fread (rqxb, sizeof (int16), tbc >> 1, uptr->fileref); for ( ; i < (tbc >> 1); i++) /* fill */ rqxb[i] = 0; err = ferror (uptr->fileref); } if ((cmd == OP_RD) && !err) { /* read? */ if (t = Map_WriteW (ba, tbc, rqxb)) { /* store, nxm? */ PUTP32 (pkt, RW_WBCL, bc - (tbc - t)); /* adj bc */ PUTP32 (pkt, RW_WBAL, ba + (tbc - t)); /* adj ba */ if (rq_hbe (cp, uptr)) /* post err log */ rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); return SCPE_OK; } } else if ((cmd == OP_CMP) && !err) { /* compare? */ uint8 dby, mby; for (i = 0; i < tbc; i++) { /* loop */ if (Map_ReadB (ba + i, 1, &mby)) { /* fetch, nxm? */ PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ PUTP32 (pkt, RW_WBAL, bc - i); /* adj ba */ if (rq_hbe (cp, uptr)) /* post err log */ rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); return SCPE_OK; } dby = (rqxb[i >> 1] >> ((i & 1)? 8: 0)) & 0xFF; if (mby != dby) { /* cmp err? */ PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ rq_rw_end (cp, uptr, 0, ST_CMP); /* done */ return SCPE_OK; /* exit */ } /* end if */ } /* end for */ } /* end else if */ } /* end else read */ if (err != 0) { /* error? */ if (rq_dte (cp, uptr, ST_DRV)) /* post err log */ rq_rw_end (cp, uptr, EF_LOG, ST_DRV); /* if ok, report err */ perror ("RQ I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } ba = ba + tbc; /* incr bus addr */ bc = bc - tbc; /* decr byte cnt */ bl = bl + ((tbc + (RQ_NUMBY - 1)) / RQ_NUMBY); /* incr blk # */ PUTP32 (pkt, RW_WBAL, ba); /* update pkt */ PUTP32 (pkt, RW_WBCL, bc); PUTP32 (pkt, RW_WBLL, bl); if (bc) /* more? resched */ sim_activate (uptr, rq_xtime); else rq_rw_end (cp, uptr, 0, ST_SUC); /* done! */ return SCPE_OK; } /* Transfer command complete */ t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts) { int32 pkt = uptr->cpkt; /* packet */ uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ uint32 bc = GETP32 (pkt, RW_BCL); /* init bc */ uint32 wbc = GETP32 (pkt, RW_WBCL); /* work bc */ DEVICE *dptr = rq_devmap[uptr->cnum]; uptr->cpkt = 0; /* done */ PUTP32 (pkt, RW_BCL, bc - wbc); /* bytes processed */ cp->pak[pkt].d[RW_WBAL] = 0; /* clear temps */ cp->pak[pkt].d[RW_WBAH] = 0; cp->pak[pkt].d[RW_WBCL] = 0; cp->pak[pkt].d[RW_WBCH] = 0; cp->pak[pkt].d[RW_WBLL] = 0; cp->pak[pkt].d[RW_WBLH] = 0; rq_putr (cp, pkt, cmd | OP_END, flg, sts, RW_LNT_D, UQ_TYP_SEQ); /* fill pkt */ if (!rq_putpkt (cp, pkt, TRUE)) /* send pkt */ return ERR; if (uptr->pktq) /* more to do? */ sim_activate (dptr->units + RQ_QUEUE, rq_qtime); /* activate thread */ return OK; } /* Data transfer error log packet */ t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err) { int32 pkt, tpkt; uint32 lu, dtyp, lbn, ccyl, csurf, csect, t; if ((cp->cflgs & CF_THS) == 0) /* logging? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ return ERR; tpkt = uptr->cpkt; /* rw pkt */ lu = cp->pak[tpkt].d[CMD_UN]; /* unit # */ lbn = GETP32 (tpkt, RW_WBLL); /* recent LBN */ dtyp = GET_DTYPE (uptr->flags); /* drv type */ if (drv_tab[dtyp].flgs & RQDF_SDI) /* SDI? ovhd @ end */ t = 0; else t = (drv_tab[dtyp].xbn + drv_tab[dtyp].dbn) / /* ovhd cylinders */ (drv_tab[dtyp].sect * drv_tab[dtyp].surf); ccyl = t + (lbn / drv_tab[dtyp].cyl); /* curr real cyl */ t = lbn % drv_tab[dtyp].cyl; /* trk relative blk */ csurf = t / drv_tab[dtyp].surf; /* curr surf */ csect = t % drv_tab[dtyp].surf; /* curr sect */ cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; cp->pak[pkt].d[ELP_UN] = lu; /* copy unit */ cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ cp->pak[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ cp->pak[pkt].d[DTE_CIDB] = 0; cp->pak[pkt].d[DTE_CIDC] = 0; cp->pak[pkt].d[DTE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | (RQ_MODEL << DTE_CIDD_V_MOD); cp->pak[pkt].d[DTE_VER] = (RQ_HVER << DTE_VER_V_HVER) | (RQ_SVER << DTE_VER_V_SVER); cp->pak[pkt].d[DTE_MLUN] = lu; /* MLUN */ cp->pak[pkt].d[DTE_UIDA] = lu; /* unit ID */ cp->pak[pkt].d[DTE_UIDB] = 0; cp->pak[pkt].d[DTE_UIDC] = 0; cp->pak[pkt].d[DTE_UIDD] = (UID_DISK << DTE_UIDD_V_CLS) | (drv_tab[dtyp].mod << DTE_UIDD_V_MOD); cp->pak[pkt].d[DTE_UVER] = 0; /* unit versn */ cp->pak[pkt].d[DTE_SCYL] = ccyl; /* cylinder */ cp->pak[pkt].d[DTE_VSNL] = 01234 + lu; /* vol ser # */ cp->pak[pkt].d[DTE_VSNH] = 0; cp->pak[pkt].d[DTE_D1] = 0; cp->pak[pkt].d[DTE_D2] = csect << DTE_D2_V_SECT; /* geometry */ cp->pak[pkt].d[DTE_D3] = (ccyl << DTE_D3_V_CYL) | (csurf << DTE_D3_V_SURF); rq_putr (cp, pkt, FM_SDE, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); return rq_putpkt (cp, pkt, TRUE); } /* Host bus error log packet */ t_bool rq_hbe (MSC *cp, UNIT *uptr) { int32 pkt, tpkt; if ((cp->cflgs & CF_THS) == 0) /* logging? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ return ERR; tpkt = uptr->cpkt; /* rw pkt */ cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; cp->pak[pkt].d[ELP_UN] = cp->pak[tpkt].d[CMD_UN]; /* copy unit */ cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ cp->pak[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ cp->pak[pkt].d[HBE_CIDB] = 0; cp->pak[pkt].d[HBE_CIDC] = 0; cp->pak[pkt].d[HBE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | (RQ_MODEL << DTE_CIDD_V_MOD); cp->pak[pkt].d[HBE_VER] = (RQ_HVER << HBE_VER_V_HVER) | /* versions */ (RQ_SVER << HBE_VER_V_SVER); cp->pak[pkt].d[HBE_RSV] = 0; cp->pak[pkt].d[HBE_BADL] = cp->pak[tpkt].d[RW_WBAL]; /* bad addr */ cp->pak[pkt].d[HBE_BADH] = cp->pak[tpkt].d[RW_WBAH]; rq_putr (cp, pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); return rq_putpkt (cp, pkt, TRUE); } /* Port last failure error log packet */ t_bool rq_plf (MSC *cp, uint32 err) { int32 pkt; if (!rq_deqf (cp, &pkt)) /* get log pkt */ return ERR; cp->pak[pkt].d[ELP_REFL] = 0; /* ref = 0 */ cp->pak[pkt].d[ELP_REFH] = 0; cp->pak[pkt].d[ELP_UN] = 0; /* no unit */ cp->pak[pkt].d[ELP_SEQ] = 0; /* no seq */ cp->pak[pkt].d[PLF_CIDA] = 0; /* cntl ID */ cp->pak[pkt].d[PLF_CIDB] = 0; cp->pak[pkt].d[PLF_CIDC] = 0; cp->pak[pkt].d[PLF_CIDD] = (RQ_CLASS << PLF_CIDD_V_CLS) | (RQ_MODEL << PLF_CIDD_V_MOD); cp->pak[pkt].d[PLF_VER] = (RQ_SVER << PLF_VER_V_SVER) | (RQ_HVER << PLF_VER_V_HVER); cp->pak[pkt].d[PLF_ERR] = err; rq_putr (cp, pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); cp->pak[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); return rq_putpkt (cp, pkt, TRUE); } /* Unit now available attention packet */ int32 rq_una (MSC *cp, int32 un) { int32 pkt; uint32 lu = cp->ubase + un; UNIT *uptr = rq_getucb (cp, lu); if (uptr == NULL) /* huh? */ return OK; if (!rq_deqf (cp, &pkt)) /* get log pkt */ return ERR; cp->pak[pkt].d[RSP_REFL] = 0; /* ref = 0 */ cp->pak[pkt].d[RSP_REFH] = 0; cp->pak[pkt].d[RSP_UN] = lu; cp->pak[pkt].d[RSP_RSV] = 0; rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ rq_putr (cp, pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ return rq_putpkt (cp, pkt, TRUE); } /* List handling rq_deqf - dequeue head of free list (fatal err if none) rq_deqh - dequeue head of list rq_enqh - enqueue at head of list rq_enqt - enqueue at tail of list */ t_bool rq_deqf (MSC *cp, int32 *pkt) { if (cp->freq == 0) /* no free pkts?? */ return rq_fatal (cp, PE_NSR); cp->pbsy = cp->pbsy + 1; /* cnt busy pkts */ *pkt = cp->freq; /* head of list */ cp->freq = cp->pak[cp->freq].link; /* next */ return OK; } int32 rq_deqh (MSC *cp, int32 *lh) { int32 ptr = *lh; /* head of list */ if (ptr) /* next */ *lh = cp->pak[ptr].link; return ptr; } void rq_enqh (MSC *cp, int32 *lh, int32 pkt) { if (pkt == 0) /* any pkt? */ return; cp->pak[pkt].link = *lh; /* link is old lh */ *lh = pkt; /* pkt is new lh */ return; } void rq_enqt (MSC *cp, int32 *lh, int32 pkt) { if (pkt == 0) /* any pkt? */ return; cp->pak[pkt].link = 0; /* it will be tail */ if (*lh == 0) /* if empty, enqh */ *lh = pkt; else { uint32 ptr = *lh; /* chase to end */ while (cp->pak[ptr].link) ptr = cp->pak[ptr].link; cp->pak[ptr].link = pkt; /* enq at tail */ } return; } /* Packet and descriptor handling */ /* Get packet from command ring */ t_bool rq_getpkt (MSC *cp, int32 *pkt) { uint32 addr, desc; if (!rq_getdesc (cp, &cp->cq, &desc)) /* get cmd desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* none */ *pkt = 0; /* pkt = 0 */ return OK; /* no error */ } if (!rq_deqf (cp, pkt)) /* get cmd pkt */ return ERR; cp->hat = 0; /* dsbl hst timer */ addr = desc & UQ_ADDR; /* get Q22 addr */ if (Map_ReadW (addr + UQ_HDR_OFF, RQ_PKT_SIZE, cp->pak[*pkt].d)) return rq_fatal (cp, PE_PRE); /* read pkt */ return rq_putdesc (cp, &cp->cq, desc); /* release desc */ } /* Put packet to response ring - note the clever hack about credits. The controller sends all its credits to the host. Thereafter, it supplies one credit for every response packet sent over. Simple! */ t_bool rq_putpkt (MSC *cp, int32 pkt, t_bool qt) { uint32 addr, desc, lnt, cr; DEVICE *dptr = rq_devmap[cp->cnum]; if (pkt == 0) /* any packet? */ return OK; if (DEBUG_PRD (dptr)) fprintf (sim_deb, ">>RQ%c: rsp=%04X, sts=%04X\n", 'A' + cp->cnum, cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]); if (!rq_getdesc (cp, &cp->rq, &desc)) /* get rsp desc */ return ERR; if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ if (qt) /* normal? q tail */ rq_enqt (cp, &cp->rspq, pkt); else rq_enqh (cp, &cp->rspq, pkt); /* resp q call */ sim_activate (dptr->units + RQ_QUEUE, rq_qtime); /* activate q thrd */ return OK; } addr = desc & UQ_ADDR; /* get Q22 addr */ lnt = cp->pak[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ (GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */ cr = (cp->credits >= 14)? 14: cp->credits; /* max 14 credits */ cp->credits = cp->credits - cr; /* decr credits */ cp->pak[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); } if (Map_WriteW (addr + UQ_HDR_OFF, lnt, cp->pak[pkt].d)) return rq_fatal (cp, PE_PWE); /* write pkt */ rq_enqh (cp, &cp->freq, pkt); /* pkt is free */ cp->pbsy = cp->pbsy - 1; /* decr busy cnt */ if (cp->pbsy == 0) /* idle? strt hst tmr */ cp->hat = cp->htmo; return rq_putdesc (cp, &cp->rq, desc); /* release desc */ } /* Get a descriptor from the host */ t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc) { uint32 addr = ring->ba + ring->idx; uint16 d[2]; if (Map_ReadW (addr, 4, d)) /* fetch desc */ return rq_fatal (cp, PE_QRE); /* err? dead */ *desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); return OK; } /* Return a descriptor to the host, clearing owner bit If rings transitions from "empty" to "not empty" or "full" to "not full", and interrupt bit was set, interrupt the host. Actually, test whether previous ring entry was owned by host. */ t_bool rq_putdesc (MSC *cp, struct uq_ring *ring, uint32 desc) { uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; uint32 prva, addr = ring->ba + ring->idx; uint16 d[2]; d[0] = newd & 0xFFFF; /* 32b to 16b */ d[1] = (newd >> 16) & 0xFFFF; if (Map_WriteW (addr, 4, d)) /* store desc */ return rq_fatal (cp, PE_QWE); /* err? dead */ if (desc & UQ_DESC_F) { /* was F set? */ if (ring->lnt <= 4) /* lnt = 1? intr */ rq_ring_int (cp, ring); else { /* prv desc */ prva = ring->ba + ((ring->idx - 4) & (ring->lnt - 1)); if (Map_ReadW (prva, 4, d)) /* read prv */ return rq_fatal (cp, PE_QRE); prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); if (prvd & UQ_DESC_OWN) rq_ring_int (cp, ring); } } ring->idx = (ring->idx + 4) & (ring->lnt - 1); return OK; } /* Get unit descriptor for logical unit */ UNIT *rq_getucb (MSC *cp, uint32 lu) { DEVICE *dptr = rq_devmap[cp->cnum]; UNIT *uptr; if ((lu < cp->ubase) || (lu >= (cp->ubase + RQ_NUMDR))) return NULL; uptr = dptr->units + (lu % RQ_NUMDR); if (uptr->flags & UNIT_DIS) return NULL; return uptr; } /* Hack unit flags */ void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr) { uptr->uf = cp->pak[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ if ((cp->pak[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ (cp->pak[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ uptr->uf = uptr->uf | UF_WPS; /* simon says... */ return; } /* Unit response fields */ void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all) { uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ cp->pak[pkt].d[ONL_MLUN] = lu; /* unit */ cp->pak[pkt].d[ONL_UFL] = uptr->uf | UF_RPL | RQ_WPH (uptr) | RQ_RMV (uptr); cp->pak[pkt].d[ONL_RSVL] = 0; /* reserved */ cp->pak[pkt].d[ONL_RSVH] = 0; cp->pak[pkt].d[ONL_UIDA] = lu; /* UID low */ cp->pak[pkt].d[ONL_UIDB] = 0; cp->pak[pkt].d[ONL_UIDC] = 0; cp->pak[pkt].d[ONL_UIDD] = (UID_DISK << ONL_UIDD_V_CLS) | (drv_tab[dtyp].mod << ONL_UIDD_V_MOD); /* UID hi */ PUTP32 (pkt, ONL_MEDL, drv_tab[dtyp].med); /* media type */ if (all) { /* if long form */ PUTP32 (pkt, ONL_SIZL, maxlbn); /* user LBNs */ cp->pak[pkt].d[ONL_VSNL] = 01234 + lu; /* vol serial # */ cp->pak[pkt].d[ONL_VSNH] = 0; } return; } /* UQ_HDR and RSP_OP fields */ void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ) { cp->pak[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ (flg << RSP_OPF_V_FLG); cp->pak[pkt].d[RSP_STS] = sts; cp->pak[pkt].d[UQ_HLNT] = lnt; /* length */ cp->pak[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ (UQ_CID_MSCP << UQ_HCTC_V_CID); /* clr credits */ return; } /* Post interrupt during init */ void rq_init_int (MSC *cp) { if ((cp->s1dat & SA_S1H_IE) && /* int enab & */ (cp->s1dat & SA_S1H_VEC)) /* ved set? int */ rq_setint (cp); return; } /* Post interrupt during putpkt - note that NXMs are ignored! */ void rq_ring_int (MSC *cp, struct uq_ring *ring) { uint32 iadr = cp->comm + ring->ioff; /* addr intr wd */ uint16 flag = 1; Map_WriteW (iadr, 2, &flag); /* write flag */ if (cp->s1dat & SA_S1H_VEC) /* if enb, intr */ rq_setint (cp); return; } /* Set RQ interrupt */ void rq_setint (MSC *cp) { cp->irq = 1; /* set ctrl int */ SET_INT (RQ); /* set master int */ return; } /* Clear RQ interrupt */ void rq_clrint (MSC *cp) { int32 i; MSC *ncp; cp->irq = 0; /* clr ctrl int */ for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrls */ ncp = rq_ctxmap[i]; /* get context */ if (ncp->irq) { /* other interrupt? */ SET_INT (RQ); /* yes, set master */ return; } } CLR_INT (RQ); /* no, clr master */ return; } /* Return interrupt vector */ int32 rq_inta (void) { int32 i; MSC *ncp; DEVICE *dptr; DIB *dibp; for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrl */ ncp = rq_ctxmap[i]; /* get context */ if (ncp->irq) { /* ctrl int set? */ dptr = rq_devmap[i]; /* get device */ dibp = (DIB *) dptr->ctxt; /* get DIB */ rq_clrint (ncp); /* clear int req */ return dibp->vec; /* return vector */ } } return 0; /* no intr req */ } /* Fatal error */ t_bool rq_fatal (MSC *cp, uint32 err) { DEVICE *dptr = rq_devmap[cp->cnum]; if (DEBUG_PRD (dptr)) fprintf (sim_deb, ">>RQ%c: fatal err=%X\n", 'A' + cp->cnum, err); rq_reset (rq_devmap[cp->cnum]); /* reset device */ cp->sa = SA_ER | err; /* SA = dead code */ cp->csta = CST_DEAD; /* state = dead */ cp->perr = err; /* save error */ return ERR; } /* Set/clear hardware write lock */ t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ if (drv_tab[dtyp].flgs & RQDF_RO) /* not on read only */ return SCPE_NOFNC; return SCPE_OK; } /* Show write lock status */ t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc) { uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ if (drv_tab[dtyp].flgs & RQDF_RO) fprintf (st, "read only"); else if (uptr->flags & UNIT_WPRT) fprintf (st, "write locked"); else fprintf (st, "write enabled"); return SCPE_OK; } /* Set unit type (and capacity if user defined) */ t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) { uint32 cap; uint32 max = sim_taddr_64? RA8U_EMAXC: RA8U_MAXC; t_stat r; if ((val < 0) || ((val != RA8U_DTYPE) && cptr)) return SCPE_ARG; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; if (cptr) { cap = (uint32) get_uint (cptr, 10, 0xFFFFFFFF, &r); if ((sim_switches & SWMASK ('L')) == 0) cap = cap * 1954; if ((r != SCPE_OK) || (cap < RA8U_MINC) || (cap >= max)) return SCPE_ARG; drv_tab[val].lbn = cap; } uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE); uptr->capac = ((t_addr) drv_tab[val].lbn) * RQ_NUMBY; return SCPE_OK; } /* Show unit type */ t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) { fprintf (st, "%s", drv_tab[GET_DTYPE (uptr->flags)].name); return SCPE_OK; } /* Device attach */ t_stat rq_attach (UNIT *uptr, char *cptr) { MSC *cp = rq_ctxmap[uptr->cnum]; t_stat r; r = attach_unit (uptr, cptr); if (r != SCPE_OK) return r; if (cp->csta == CST_UP) uptr->flags = uptr->flags | UNIT_ATP; return SCPE_OK; } /* Device detach */ t_stat rq_detach (UNIT *uptr) { t_stat r; r = detach_unit (uptr); /* detach unit */ if (r != SCPE_OK) return r; uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); /* clr onl, atn pend */ uptr->uf = 0; /* clr unit flgs */ return SCPE_OK; } /* Device reset */ t_stat rq_reset (DEVICE *dptr) { int32 i, j, cidx; UNIT *uptr; MSC *cp; DIB *dibp = (DIB *) dptr->ctxt; for (i = 0, cidx = -1; i < RQ_NUMCT; i++) { /* find ctrl num */ if (rq_devmap[i] == dptr) cidx = i; } if (cidx < 0) /* not found??? */ return SCPE_IERR; cp = rq_ctxmap[cidx]; /* get context */ cp->cnum = cidx; /* init index */ #if defined (VM_VAX) /* VAX */ cp->ubase = 0; /* unit base = 0 */ #else /* PDP-11 */ cp->ubase = cidx * RQ_NUMDR; /* init unit base */ #endif cp->csta = CST_S1; /* init stage 1 */ cp->s1dat = 0; /* no S1 data */ dibp->vec = 0; /* no vector */ cp->comm = 0; /* no comm region */ if (UNIBUS) /* Unibus? */ cp->sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; else cp->sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ cp->cflgs = CF_RPL; /* ctrl flgs off */ cp->htmo = RQ_DHTMO; /* default timeout */ cp->hat = cp->htmo; /* default timer */ cp->cq.ba = cp->cq.lnt = cp->cq.idx = 0; /* clr cmd ring */ cp->rq.ba = cp->rq.lnt = cp->rq.idx = 0; /* clr rsp ring */ cp->credits = (RQ_NPKTS / 2) - 1; /* init credits */ cp->freq = 1; /* init free list */ for (i = 0; i < RQ_NPKTS; i++) { /* all pkts free */ if (i) cp->pak[i].link = (i + 1) & RQ_M_NPKTS; else cp->pak[i].link = 0; for (j = 0; j < RQ_PKT_SIZE_W; j++) cp->pak[i].d[j] = 0; } cp->rspq = 0; /* no q'd rsp pkts */ cp->pbsy = 0; /* all pkts free */ cp->pip = 0; /* not polling */ rq_clrint (cp); /* clr intr req */ for (i = 0; i < (RQ_NUMDR + 2); i++) { /* init units */ uptr = dptr->units + i; sim_cancel (uptr); /* clr activity */ uptr->cnum = cidx; /* set ctrl index */ uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); uptr->uf = 0; /* clr unit flags */ uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ } if (rqxb == NULL) rqxb = (uint16 *) calloc (RQ_MAXFR >> 1, sizeof (uint16)); if (rqxb == NULL) return SCPE_MEM; return auto_config (0, 0); /* run autoconfig */ } /* Device bootstrap */ #if defined (VM_PDP11) #define BOOT_START 016000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 014) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 0042125, /* st: "UD" */ /* Four step init process */ 0012706, 0016000, /* mov #st,sp */ 0012700, 0000000, /* mov #unit,r0 */ 0012701, 0172150, /* mov #172150, r1 ; ip addr */ 0012704, 0016162, /* mov #it, r4 */ 0012705, 0004000, /* mov #4000,r5 ; s1 mask */ 0010102, /* mov r1,r2 */ 0005022, /* clr (r2)+ ; init */ 0005712, /* 10$: tst (r2) ; err? */ 0100001, /* bpl 20$ */ 0000000, /* halt */ 0030512, /* 20$: bit r5,(r2) ; step set? */ 0001773, /* beq 10$ ; wait */ 0012412, /* mov (r4)+,(r2) ; send next */ 0006305, /* asl r5 ; next mask */ 0100370, /* bpl 10$ ; s4 done? */ /* Send ONL, READ commands */ 0105714, /* 30$: tstb (r4) ; end tbl? */ 0001434, /* beq done ; 0 = yes */ 0012702, 0007000, /* mov #rpkt-4,r2 ; clr pkts */ 0005022, /* 40$: clr (r2)+ */ 0020227, 0007204, /* cmp r2,#comm */ 0103774, /* blo 40$ */ 0112437, 0007100, /* movb (r4)+,cpkt-4 ; set lnt */ 0110037, 0007110, /* movb r0,cpkt+4 ; set unit */ 0112437, 0007114, /* movb (r4)+,cpkt+10 ; set op */ 0112437, 0007121, /* movb (r4)+,cpkt+15 ; set param */ 0012722, 0007004, /* mov #rpkt,(r2)+ ; rq desc */ 0010522, /* mov r5,(r2)+ ; rq own */ 0012722, 0007104, /* mov #ckpt,(r2)+ ; cq desc */ 0010512, /* mov r5,(r2) ; cq own */ 0024242, /* cmp -(r2),-(r2) ; back up */ 0005711, /* tst (r1) ; wake ctrl */ 0005712, /* 50$: tst (r2) ; rq own clr? */ 0100776, /* bmi 50$ ; wait */ 0005737, 0007016, /* tst rpkt+12 ; stat ok? */ 0001743, /* beq 30$ ; next cmd */ 0000000, /* halt */ /* Boot block read in, jump to 0 */ 0005011, /* done: clr (r1) ; for M+ */ 0005003, /* clr r3 */ 0012704, BOOT_START+020, /* mov #st+020,r4 */ 0005005, /* clr r5 */ 0005007, /* clr pc */ /* Data */ 0100000, /* it: no ints, ring sz = 1 */ 0007204, /* .word comm */ 0000000, /* .word 0 */ 0000001, /* .word 1 */ 0004420, /* .byte 20,11 */ 0020000, /* .byte 0,40 */ 0001041, /* .byte 41,2 */ 0000000 }; t_stat rq_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; DIB *dibp = (DIB *) dptr->ctxt; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & 3; M[BOOT_CSR >> 1] = dibp->ba & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat rq_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif /* Special show commands */ void rq_show_ring (FILE *st, struct uq_ring *rp) { uint32 i, desc; uint16 d[2]; #if defined (VM_PDP11) fprintf (st, "ring, base = %o, index = %d, length = %d\n", rp->ba, rp->idx >> 2, rp->lnt >> 2); #else fprintf (st, "ring, base = %x, index = %d, length = %d\n", rp->ba, rp->idx >> 2, rp->lnt >> 2); #endif for (i = 0; i < (rp->lnt >> 2); i++) { if (Map_ReadW (rp->ba + (i << 2), 4, d)) { fprintf (st, " %3d: non-existent memory\n", i); break; } desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); #if defined (VM_PDP11) fprintf (st, " %3d: %011o\n", i, desc); #else fprintf (st, " %3d: %08x\n", i, desc); #endif } return; } void rq_show_pkt (FILE *st, MSC *cp, int32 pkt) { int32 i, j; uint32 cr = GETP (pkt, UQ_HCTC, CR); uint32 typ = GETP (pkt, UQ_HCTC, TYP); uint32 cid = GETP (pkt, UQ_HCTC, CID); fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", pkt, cr, typ, cid); for (i = 0; i < RQ_SH_MAX; i = i + RQ_SH_PPL) { fprintf (st, " %2d:", i); for (j = i; j < (i + RQ_SH_PPL); j++) #if defined (VM_PDP11) fprintf (st, " %06o", cp->pak[pkt].d[j]); #else fprintf (st, " %04x", cp->pak[pkt].d[j]); #endif fprintf (st, "\n"); } return; } t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc) { MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; int32 pkt, u; u = (int32) (uptr - dptr->units); if (cp->csta != CST_UP) { fprintf (st, "Controller is not initialized\n"); return SCPE_OK; } if ((uptr->flags & UNIT_ONL) == 0) { if (uptr->flags & UNIT_ATT) fprintf (st, "Unit %d is available\n", u); else fprintf (st, "Unit %d is offline\n", u); return SCPE_OK; } if (uptr->cpkt) { fprintf (st, "Unit %d current ", u); rq_show_pkt (st, cp, uptr->cpkt); if (pkt = uptr->pktq) { do { fprintf (st, "Unit %d queued ", u); rq_show_pkt (st, cp, pkt); } while (pkt = cp->pak[pkt].link); } } else fprintf (st, "Unit %d queues are empty\n", u); return SCPE_OK; } t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) { MSC *cp = rq_ctxmap[uptr->cnum]; DEVICE *dptr = rq_devmap[uptr->cnum]; int32 i, pkt; if (cp->csta != CST_UP) { fprintf (st, "Controller is not initialized\n"); return SCPE_OK; } if (val & RQ_SH_RI) { if (cp->pip) fprintf (st, "Polling in progress, host timer = %d\n", cp->hat); else fprintf (st, "Host timer = %d\n", cp->hat); fprintf (st, "Command "); rq_show_ring (st, &cp->cq); fprintf (st, "Response "); rq_show_ring (st, &cp->rq); } if (val & RQ_SH_FR) { if (pkt = cp->freq) { for (i = 0; pkt != 0; i++, pkt = cp->pak[pkt].link) { if (i == 0) fprintf (st, "Free queue = %d", pkt); else if ((i % 16) == 0) fprintf (st, ",\n %d", pkt); else fprintf (st, ", %d", pkt); } fprintf (st, "\n"); } else fprintf (st, "Free queue is empty\n"); } if (val & RQ_SH_RS) { if (pkt = cp->rspq) { do { fprintf (st, "Response "); rq_show_pkt (st, cp, pkt); } while (pkt = cp->pak[pkt].link); } else fprintf (st, "Response queue is empty\n"); } if (val & RQ_SH_UN) { for (i = 0; i < RQ_NUMDR; i++) rq_show_unitq (st, dptr->units + i, 0, desc); } return SCPE_OK; } simh-3.8.1/PDP11/pdp11_xq.h0000644000175000017500000003203611111667046013243 0ustar vlmvlm/* pdp11_xq.h: DEQNA/DELQA ethernet controller information ------------------------------------------------------------------------------ Copyright (c) 2002-2008, David T. Hittner 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 AUTHOR 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 author. ------------------------------------------------------------------------------ Modification history: 07-Jul-05 RMS Removed extraneous externs 20-Jan-04 DTH Added new sanity timer and system id timer 19-Jan-04 DTH Added XQ_SERVICE_INTERVAL, poll 09-Jan-04 DTH Added Boot PDP diagnostic definition, XI/RI combination 26-Dec-03 DTH Moved ethernet queue definitions to sim_ether 25-Nov-03 DTH Added interrupt request flag 02-Jun-03 DTH Added struct xq_stats 28-May-03 DTH Made xq_msg_que.item dynamic 28-May-03 MP Optimized structures, removed rtime variable 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 28-Apr-03 DTH Added callbacks for multicontroller identification 25-Mar-03 DTH Removed bootrom field - no longer needed; Updated copyright 15-Jan-03 DTH Merged Mark Pizzolato's changes into main source 13-Jan-03 MP Added countdown for System Id multicast packets 10-Jan-03 DTH Added bootrom field 30-Dec-02 DTH Added setup valid field 21-Oct-02 DTH Corrected copyright again 15-Oct-02 DTH Fixed copyright, added sanity timer support 10-Oct-02 DTH Added more setup fields and bitmasks 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 15-Aug-02 DTH Started XQ simulation ------------------------------------------------------------------------------ */ #ifndef _PDP11_XQ_H #define _PDP11_XQ_H #if defined (VM_PDP10) /* PDP10 version */ #error "DEQNA/DELQA not supported on PDP10!" #elif defined (VM_VAX) /* VAX version */ #include "vax_defs.h" #define XQ_RDX 16 #define XQ_WID 32 extern int32 PSL; /* PSL */ extern int32 fault_PC; /* fault PC */ extern int32 int_req[IPL_HLVL]; #else /* PDP-11 version */ #include "pdp11_defs.h" #define XQ_RDX 8 #define XQ_WID 16 extern int32 int_req[IPL_HLVL]; #endif #include "sim_ether.h" #define XQ_QUE_MAX 500 /* read queue size in packets */ #define XQ_FILTER_MAX 14 /* number of filters allowed */ #define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */ #define XQ_SYSTEM_ID_SECS 540 /* seconds before system ID timer expires */ #define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ #define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ enum xq_type {XQ_T_DEQNA, XQ_T_DELQA}; struct xq_sanity { int enabled; /* sanity timer enabled? 2=HW, 1=SW, 0=off */ int quarter_secs; /* sanity timer value in 1/4 seconds */ int max; /* maximum timeout (based on poll) */ int timer; /* countdown timer */ }; struct xq_setup { int valid; /* is the setup block valid? */ int promiscuous; /* promiscuous mode enabled */ int multicast; /* enable all multicast addresses */ int l1; /* first diagnostic led state */ int l2; /* second diagnostic led state */ int l3; /* third diagnostic led state */ int sanity_timer; /* sanity timer value (encoded) */ ETH_MAC macs[XQ_FILTER_MAX]; /* MAC addresses to respond to */ }; struct xq_stats { int recv; /* received packets */ int filter; /* filtered packets */ int xmit; /* transmitted packets */ int fail; /* transmit failed */ int runt; /* runts */ int giant; /* oversize packets */ int setup; /* setup packets */ int loop; /* loopback packets */ }; struct xq_meb { /* MEB block */ uint8 type; uint8 add_lo; uint8 add_mi; uint8 add_hi; uint8 siz_lo; uint8 siz_hi; }; struct xq_device { /*+ initialized values - DO NOT MOVE */ ETH_PCALLBACK rcallback; /* read callback routine */ ETH_PCALLBACK wcallback; /* write callback routine */ ETH_MAC mac; /* MAC address */ enum xq_type type; /* controller type */ int poll; /* poll ethernet times/sec */ struct xq_sanity sanity; /* sanity timer information */ /*- initialized values - DO NOT MOVE */ /* I/O register storage */ uint16 addr[6]; uint16 rbdl[2]; uint16 xbdl[2]; uint16 var; uint16 csr; uint32 irq; /* interrupt request flag */ /* buffers, etc. */ struct xq_setup setup; struct xq_stats stats; uint8 mac_checksum[2]; uint16 rbdl_buf[6]; uint16 xbdl_buf[6]; uint32 rbdl_ba; uint32 xbdl_ba; ETH_DEV* etherface; int receiving; ETH_PACK read_buffer; ETH_PACK write_buffer; ETH_QUE ReadQ; int idtmr; /* countdown for ID Timer */ }; struct xq_controller { DEVICE* dev; /* device block */ UNIT* unit; /* unit block */ DIB* dib; /* device interface block */ struct xq_device* var; /* controller-specific variables */ }; typedef struct xq_controller CTLR; #define XQ_CSR_RI 0x8000 /* Receive Interrupt Request (RI) [RO/W1] */ #define XQ_CSR_PE 0x4000 /* Parity Error in Host Memory (PE) [RO] */ #define XQ_CSR_CA 0x2000 /* Carrier from Receiver Enabled (CA) [RO] */ #define XQ_CSR_OK 0x1000 /* Ethernet Transceiver Power (OK) [RO] */ #define XQ_CSR_RR 0x0800 /* Reserved : Set to Zero (RR) [RO] */ #define XQ_CSR_SE 0x0400 /* Sanity Timer Enable (SE) [RW] */ #define XQ_CSR_EL 0x0200 /* External Loopback (EL) [RW] */ #define XQ_CSR_IL 0x0100 /* Internal Loopback (IL) [RW] */ #define XQ_CSR_XI 0x0080 /* Transmit Interrupt Request (XI) [RO/W1] */ #define XQ_CSR_IE 0x0040 /* Interrupt Enable (IE) [RW] */ #define XQ_CSR_RL 0x0020 /* Receive List Invalid/Empty (RL) [RO] */ #define XQ_CSR_XL 0x0010 /* Transmit List Invalid/Empty (XL) [RO] */ #define XQ_CSR_BD 0x0008 /* Boot/Diagnostic ROM Load (BD) [RW] */ #define XQ_CSR_NI 0x0004 /* NonExistant Memory Timeout (NXM) [RO] */ #define XQ_CSR_SR 0x0002 /* Software Reset (SR) [RW] */ #define XQ_CSR_RE 0x0001 /* Receiver Enable (RE) [RW] */ /* special access bitmaps */ #define XQ_CSR_RO 0xF8B4 /* Read-Only bits */ #define XQ_CSR_RW 0x074B /* Read/Write bits */ #define XQ_CSR_W1 0x8080 /* Write-one-to-clear bits */ #define XQ_CSR_BP 0x0208 /* Boot PDP diagnostic ROM */ #define XQ_CSR_XIRI 0X8080 /* Transmit & Receive Interrupts */ #define XQ_VEC_MS 0x8000 /* Mode Select (MO) [RW] */ #define XQ_VEC_OS 0x4000 /* Option Switch Setting (OS) [RO] */ #define XQ_VEC_RS 0x2000 /* Request Self-Test (RS) [RW] */ #define XQ_VEC_S3 0x1000 /* Self-Test Status (S3) [RO] */ #define XQ_VEC_S2 0x0800 /* Self-Test Status (S2) [RO] */ #define XQ_VEC_S1 0x0400 /* Self-Test Status (S1) [RO] */ #define XQ_VEC_ST 0x1C00 /* Self-Test (S1 + S2 + S3) [RO] */ #define XQ_VEC_IV 0x03FC /* Interrupt Vector (IV) [RW] */ #define XQ_VEC_RR 0x0002 /* Reserved (RR) [RO] */ #define XQ_VEC_ID 0x0001 /* Identity Test Bit (ID) [RW] */ /* special access bitmaps */ #define XQ_VEC_RO 0x5C02 /* Read-Only bits */ #define XQ_VEC_RW 0xA3FD /* Read/Write bits */ #define XQ_DSC_V 0x8000 /* Valid bit */ #define XQ_DSC_C 0x4000 /* Chain bit */ #define XQ_DSC_E 0x2000 /* End of Message bit [Transmit only] */ #define XQ_DSC_S 0x1000 /* Setup bit [Transmit only] */ #define XQ_DSC_L 0x0080 /* Low Byte Termination bit [Transmit only] */ #define XQ_DSC_H 0x0040 /* High Byte Start bit [Transmit only] */ #define XQ_SETUP_MC 0x0001 /* multicast bit */ #define XQ_SETUP_PM 0x0002 /* promiscuous bit */ #define XQ_SETUP_LD 0x000C /* led bits */ #define XQ_SETUP_ST 0x0070 /* sanity timer bits */ /* debugging bitmaps */ #define DBG_TRC 0x0001 /* trace routine calls */ #define DBG_REG 0x0002 /* trace read/write registers */ #define DBG_CSR 0x0004 /* watch CSR */ #define DBG_VAR 0x0008 /* watch VAR */ #define DBG_WRN 0x0010 /* display warnings */ #define DBG_SAN 0x0020 /* display sanity timer info */ #define DBG_SET 0x0040 /* display setup info */ #define DBG_PCK 0x0080 /* display packets */ #define DBG_ETH 0x8000 /* debug ethernet device */ #endif /* _PDP11_XQ_H */ simh-3.8.1/PDP11/pdp11_fp.c0000644000175000017500000013333211112123012013171 0ustar vlmvlm/* pdp11_fp.c: PDP-11 floating point simulator (32b version) Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) 04-Oct-04 RMS Added FIS instructions 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict 08-Oct-02 RMS Fixed macro definitions 05-Jun-98 RMS Fixed implementation specific shift bugs 20-Apr-98 RMS Fixed bug in MODf integer truncation 17-Apr-98 RMS Fixed bug in STCfi range check 16-Apr-98 RMS Fixed bugs in STEXP, STCfi, round/pack 09-Apr-98 RMS Fixed bug in LDEXP 04-Apr-98 RMS Fixed bug in MODf condition codes This module simulates the PDP-11 floating point unit (FP11 series). It is called from the instruction decoder for opcodes 170000:177777. The floating point unit recognizes three instruction formats: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand | 1 1 1 1| 0 0 0 0 0 0| opcode | 170000: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170077 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ one operand | 1 1 1 1| 0 0 0| opcode | dest spec | 170100: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170777 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand | 1 1 1 1| opcode | fac | dest spec | 171000: +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 177777 The instruction space is further extended through use of the floating point status register (FPS) mode bits. Three mode bits affect how instructions are interpreted: FPS_D if 0, floating registers are single precision if 1, floating registers are double precision FPS_L if 0, integer operands are word if 1, integer operands are longword FPS_T if 0, floating operations are rounded if 1, floating operations are truncated FPS also contains the condition codes for the floating point unit, and exception enable bits for individual error conditions. Exceptions cause a trap through 0244, unless the individual exception, or all exceptions, are disabled. Illegal address mode, undefined variable, and divide by zero abort the current instruction; all other exceptions permit the instruction to complete. (Aborts are implemented as traps that request an "interrupt" trap. If an interrupt is pending, it is serviced; if not, trap_req is updated and processing continues.) Floating point specifiers are similar to integer specifiers, with the length of the operand being up to 8 bytes. In two specific cases, the floating point unit reads or writes only two bytes, rather than the length specified by the operand type: register for integers, only 16b are accessed; if the operand is 32b, these are the high order 16b of the operand immediate for integers or floating point, only 16b are accessed; if the operand is 32b or 64b, these are the high order 16b of the operand */ #include "pdp11_defs.h" /* Floating point status register */ #define FPS_ER (1u << FPS_V_ER) /* error */ #define FPS_ID (1u << FPS_V_ID) /* interrupt disable */ #define FPS_IUV (1u << FPS_V_IUV) /* int on undef var */ #define FPS_IU (1u << FPS_V_IU) /* int on underflow */ #define FPS_IV (1u << FPS_V_IV) /* int on overflow */ #define FPS_IC (1u << FPS_V_IC) /* int on conv error */ #define FPS_D (1u << FPS_V_D) /* single/double */ #define FPS_L (1u << FPS_V_L) /* word/long */ #define FPS_T (1u << FPS_V_T) /* round/truncate */ #define FPS_N (1u << FPS_V_N) #define FPS_Z (1u << FPS_V_Z) #define FPS_V (1u << FPS_V_V) #define FPS_C (1u << FPS_V_C) #define FPS_CC (FPS_N + FPS_Z + FPS_V + FPS_C) #define FPS_RW (FPS_ER + FPS_ID + FPS_IUV + FPS_IU + FPS_IV + \ FPS_IC + FPS_D + FPS_L + FPS_T + FPS_CC) /* Floating point exception codes */ #define FEC_OP 2 /* illegal op/mode */ #define FEC_DZRO 4 /* divide by zero */ #define FEC_ICVT 6 /* conversion error */ #define FEC_OVFLO 8 /* overflow */ #define FEC_UNFLO 10 /* underflow */ #define FEC_UNDFV 12 /* undef variable */ /* Floating point format, all assignments 32b relative */ #define FP_V_SIGN (63 - 32) /* high lw: sign */ #define FP_V_EXP (55 - 32) /* exponent */ #define FP_V_HB FP_V_EXP /* hidden bit */ #define FP_V_F0 (48 - 32) /* fraction 0 */ #define FP_V_F1 (32 - 32) /* fraction 1 */ #define FP_V_FROUND (31 - 32) /* f round point */ #define FP_V_F2 16 /* low lw: fraction 2 */ #define FP_V_F3 0 /* fraction 3 */ #define FP_V_DROUND (-1) /* d round point */ #define FP_M_EXP 0377 #define FP_SIGN (1u << FP_V_SIGN) #define FP_EXP (FP_M_EXP << FP_V_EXP) #define FP_HB (1u << FP_V_HB) #define FP_FRACH ((1u << FP_V_HB) - 1) #define FP_FRACL 0xFFFFFFFF #define FP_BIAS 0200 /* exponent bias */ #define FP_GUARD 3 /* guard bits */ /* Data lengths */ #define WORD 2 #define LONG 4 #define QUAD 8 /* Double precision operations on 64b quantities */ #define F_LOAD(qd,ac,ds) \ ds.h = ac.h; ds.l = (qd)? ac.l: 0 #define F_LOAD_P(qd,ac,ds) \ ds->h = ac.h; ds->l = (qd)? ac.l: 0 #define F_LOAD_FRAC(qd,ac,ds) \ ds.h = (ac.h & FP_FRACH) | FP_HB; \ ds.l = (qd)? ac.l: 0 #define F_STORE(qd,sr,ac) \ ac.h = sr.h; if ((qd)) ac.l = sr.l #define F_STORE_P(qd,sr,ac) \ ac.h = sr->h; if ((qd)) ac.l = sr->l #define F_GET_FRAC_P(sr,ds) \ ds.l = sr->l; \ ds.h = (sr->h & FP_FRACH) | FP_HB #define F_ADD(s2,s1,ds) \ ds.l = (s1.l + s2.l) & 0xFFFFFFFF; \ ds.h = (s1.h + s2.h + (ds.l < s2.l)) & 0xFFFFFFFF #define F_SUB(s2,s1,ds) \ ds.h = (s1.h - s2.h - (s1.l < s2.l)) & 0xFFFFFFFF; \ ds.l = (s1.l - s2.l) & 0xFFFFFFFF #define F_LT(x,y) ((x.h < y.h) || ((x.h == y.h) && (x.l < y.l))) #define F_LT_AP(x,y) (((x->h & ~FP_SIGN) < (y->h & ~FP_SIGN)) || \ (((x->h & ~FP_SIGN) == (y->h & ~FP_SIGN)) && (x->l < y->l))) #define F_LSH_V(sr,n,ds) \ ds.h = (((n) >= 32)? (sr.l << ((n) - 32)): \ (sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ & 0xFFFFFFFF; \ ds.l = ((n) >= 32)? 0: (sr.l << (n)) & 0xFFFFFFFF #define F_RSH_V(sr,n,ds) \ ds.l = (((n) >= 32)? (sr.h >> ((n) - 32)) & and_mask[64 - (n)]: \ ((sr.l >> (n)) & and_mask[32 - (n)]) | \ (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ ds.h = ((n) >= 32)? 0: \ ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF /* For the constant shift macro, arguments must in the range [2,31] */ #define F_LSH_1(ds) ds.h = ((ds.h << 1) | ((ds.l >> 31) & 1)) & 0xFFFFFFFF; \ ds.l = (ds.l << 1) & 0xFFFFFFFF #define F_RSH_1(ds) ds.l = ((ds.l >> 1) & 0x7FFFFFFF) | ((ds.h & 1) << 31); \ ds.h = ((ds.h >> 1) & 0x7FFFFFFF) #define F_LSH_K(sr,n,ds) \ ds.h = ((sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ & 0xFFFFFFFF; \ ds.l = (sr.l << (n)) & 0xFFFFFFFF #define F_RSH_K(sr,n,ds) \ ds.l = (((sr.l >> (n)) & and_mask[32 - (n)]) | \ (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ ds.h = ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF #define F_LSH_GUARD(ds) F_LSH_K(ds,FP_GUARD,ds) #define F_RSH_GUARD(ds) F_RSH_K(ds,FP_GUARD,ds) #define GET_BIT(ir,n) (((ir) >> (n)) & 1) #define GET_SIGN(ir) GET_BIT((ir), FP_V_SIGN) #define GET_EXP(ir) (((ir) >> FP_V_EXP) & FP_M_EXP) #define GET_SIGN_L(ir) GET_BIT((ir), 31) #define GET_SIGN_W(ir) GET_BIT((ir), 15) extern jmp_buf save_env; extern uint32 cpu_type; extern int32 FEC, FEA, FPS; extern int32 CPUERR, trap_req; extern int32 N, Z, V, C; extern int32 R[8]; extern int32 STKLIM; extern int32 cm, isenable, dsenable, MMR0, MMR1; extern fpac_t FR[6]; fpac_t zero_fac = { 0, 0 }; fpac_t one_fac = { 1, 0 }; fpac_t fround_fac = { (1u << (FP_V_FROUND + 32)), 0 }; fpac_t fround_guard_fac = { 0, (1u << (FP_V_FROUND + FP_GUARD)) }; fpac_t dround_guard_fac = { (1u << (FP_V_DROUND + FP_GUARD)), 0 }; fpac_t fmask_fac = { 0xFFFFFFFF, (1u << (FP_V_HB + FP_GUARD + 1)) - 1 }; static const uint32 and_mask[33] = { 0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF }; int32 backup_PC; int32 fpnotrap (int32 code); int32 GeteaFP (int32 spec, int32 len); uint32 ReadI (int32 addr, int32 spec, int32 len); void ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len); void WriteI (int32 data, int32 addr, int32 spec, int32 len); void WriteFP (fpac_t *data, int32 addr, int32 spec, int32 len); int32 setfcc (int32 old_status, int32 result_high, int32 newV); int32 addfp11 (fpac_t *src1, fpac_t *src2); int32 mulfp11 (fpac_t *src1, fpac_t *src2); int32 divfp11 (fpac_t *src1, fpac_t *src2); int32 modfp11 (fpac_t *src1, fpac_t *src2, fpac_t *frac); void frac_mulfp11 (fpac_t *src1, fpac_t *src2); int32 roundfp11 (fpac_t *src); int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r); extern int32 GeteaW (int32 spec); extern int32 ReadW (int32 addr); extern void WriteW (int32 data, int32 addr); extern void set_stack_trap (int32 adr); /* Set up for instruction decode and execution */ void fp11 (int32 IR) { int32 dst, ea, ac, dstspec; int32 i, qdouble, lenf, leni; int32 newV, exp, sign; fpac_t fac, fsrc, modfrac; static const uint32 i_limit[2][2] = { { 0x80000000, 0x80010000 }, { 0x80000000, 0x80000001 } }; backup_PC = PC; /* save PC for FEA */ ac = (IR >> 6) & 03; /* fac is IR<7:6> */ dstspec = IR & 077; qdouble = FPS & FPS_D; lenf = qdouble? QUAD: LONG; switch ((IR >> 8) & 017) { /* decode IR<11:8> */ case 000: switch (ac) { /* decode IR<7:6> */ case 0: /* specials */ if (IR == 0170000) { /* CFCC */ N = (FPS >> PSW_V_N) & 1; Z = (FPS >> PSW_V_Z) & 1; V = (FPS >> PSW_V_V) & 1; C = (FPS >> PSW_V_C) & 1; } else if (IR == 0170001) /* SETF */ FPS = FPS & ~FPS_D; else if (IR == 0170002) /* SETI */ FPS = FPS & ~FPS_L; else if (IR == 0170011) /* SETD */ FPS = FPS | FPS_D; else if (IR == 0170012) /* SETL */ FPS = FPS | FPS_L; else fpnotrap (FEC_OP); break; case 1: /* LDFPS */ dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); FPS = dst & FPS_RW; break; case 2: /* STFPS */ FPS = FPS & FPS_RW; if (dstspec <= 07) R[dstspec] = FPS; else WriteW (FPS, GeteaW (dstspec)); break; case 3: /* STST */ if (dstspec <= 07) R[dstspec] = FEC; else WriteI ((FEC << 16) | FEA, GeteaFP (dstspec, LONG), dstspec, LONG); break; } /* end switch <7:6> */ break; /* end case 0 */ case 001: switch (ac) { /* decode IR<7:6> */ case 0: /* CLRf */ WriteFP (&zero_fac, GeteaFP (dstspec, lenf), dstspec, lenf); FPS = (FPS & ~FPS_CC) | FPS_Z; break; case 1: /* TSTf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); FPS = setfcc (FPS, fsrc.h, 0); break; case 2: /* ABSf */ ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; else fsrc.h = fsrc.h & ~FP_SIGN; WriteFP (&fsrc, ea, dstspec, lenf); FPS = setfcc (FPS, fsrc.h, 0); break; case 3: /* NEGf */ ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; else fsrc.h = fsrc.h ^ FP_SIGN; WriteFP (&fsrc, ea, dstspec, lenf); FPS = setfcc (FPS, fsrc.h, 0); break; } /* end switch <7:6> */ break; /* end case 1 */ case 005: /* LDf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_STORE (qdouble, fsrc, FR[ac]); FPS = setfcc (FPS, fsrc.h, 0); break; case 010: /* STf */ F_LOAD (qdouble, FR[ac], fac); WriteFP (&fac, GeteaFP (dstspec, lenf), dstspec, lenf); break; case 017: /* LDCff' */ ReadFP (&fsrc, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; if ((FPS & (FPS_D + FPS_T)) == 0) newV = roundfp11 (&fsrc); else newV = 0; F_STORE (qdouble, fsrc, FR[ac]); FPS = setfcc (FPS, fsrc.h, newV); break; case 014: /* STCff' */ F_LOAD (qdouble, FR[ac], fac); if (GET_EXP (fac.h) == 0) fac = zero_fac; if ((FPS & (FPS_D + FPS_T)) == FPS_D) newV = roundfp11 (&fac); else newV = 0; WriteFP (&fac, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); FPS = setfcc (FPS, fac.h, newV); break; case 007: /* CMPf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; if (GET_EXP (fac.h) == 0) fac = zero_fac; if ((fsrc.h == fac.h) && (fsrc.l == fac.l)) { /* equal? */ FPS = (FPS & ~FPS_CC) | FPS_Z; if ((fsrc.h | fsrc.l) == 0) { /* zero? */ F_STORE (qdouble, zero_fac, FR[ac]); } break; } FPS = (FPS & ~FPS_CC) | ((fsrc.h >> (FP_V_SIGN - PSW_V_N)) & FPS_N); if ((GET_SIGN (fsrc.h ^ fac.h) == 0) && (fac.h != 0) && F_LT (fsrc, fac)) FPS = FPS ^ FPS_N; break; case 015: /* LDEXP */ dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); F_LOAD (qdouble, FR[ac], fac); fac.h = (fac.h & ~FP_EXP) | (((dst + FP_BIAS) & FP_M_EXP) << FP_V_EXP); newV = 0; if ((dst > 0177) && (dst <= 0177600)) { if (dst < 0100000) { if (fpnotrap (FEC_OVFLO)) fac = zero_fac; newV = FPS_V; } else { if (fpnotrap (FEC_UNFLO)) fac = zero_fac; } } F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, newV); break; case 012: /* STEXP */ dst = (GET_EXP (FR[ac].h) - FP_BIAS) & 0177777; N = GET_SIGN_W (dst); Z = (dst == 0); V = 0; C = 0; FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z); if (dstspec <= 07) R[dstspec] = dst; else WriteW (dst, GeteaW (dstspec)); break; case 016: /* LDCif */ leni = FPS & FPS_L? LONG: WORD; if (dstspec <= 07) fac.l = R[dstspec] << 16; else fac.l = ReadI (GeteaFP (dstspec, leni), dstspec, leni); fac.h = 0; if (fac.l) { if (sign = GET_SIGN_L (fac.l)) fac.l = (fac.l ^ 0xFFFFFFFF) + 1; for (i = 0; GET_SIGN_L (fac.l) == 0; i++) fac.l = fac.l << 1; exp = ((FPS & FPS_L)? FP_BIAS + 32: FP_BIAS + 16) - i; fac.h = (sign << FP_V_SIGN) | (exp << FP_V_EXP) | ((fac.l >> (31 - FP_V_HB)) & FP_FRACH); fac.l = (fac.l << (FP_V_HB + 1)) & FP_FRACL; if ((FPS & (FPS_D + FPS_T)) == 0) roundfp11 (&fac); } F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, 0); break; case 013: /* STCfi */ sign = GET_SIGN (FR[ac].h); /* get sign, */ exp = GET_EXP (FR[ac].h); /* exponent, */ F_LOAD_FRAC (qdouble, FR[ac], fac); /* fraction */ if (FPS & FPS_L) { leni = LONG; i = FP_BIAS + 32; } else { leni = WORD; i = FP_BIAS + 16; } C = 0; if (exp <= FP_BIAS) dst = 0; else if (exp > i) { dst = 0; C = 1; } else { F_RSH_V (fac, FP_V_HB + 1 + i - exp, fsrc); if (leni == WORD) fsrc.l = fsrc.l & ~0177777; if (fsrc.l >= i_limit[leni == LONG][sign]) { dst = 0; C = 1; } else { dst = fsrc.l; if (sign) dst = -dst; } } N = GET_SIGN_L (dst); Z = (dst == 0); V = 0; if (C) fpnotrap (FEC_ICVT); FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z) | (C << PSW_V_C); if (dstspec <= 07) R[dstspec] = (dst >> 16) & 0177777; else WriteI (dst, GeteaFP (dstspec, leni), dstspec, leni); break; case 002: /* MULf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); newV = mulfp11 (&fac, &fsrc); F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, newV); break; case 003: /* MODf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); newV = modfp11 (&fac, &fsrc, &modfrac); F_STORE (qdouble, fac, FR[ac | 1]); F_STORE (qdouble, modfrac, FR[ac]); FPS = setfcc (FPS, modfrac.h, newV); break; case 004: /* ADDf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); newV = addfp11 (&fac, &fsrc); F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, newV); break; case 006: /* SUBf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); if (GET_EXP (fsrc.h) != 0) fsrc.h = fsrc.h ^ FP_SIGN; newV = addfp11 (&fac, &fsrc); F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, newV); break; case 011: /* DIVf */ ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); F_LOAD (qdouble, FR[ac], fac); if (GET_EXP (fsrc.h) == 0) { /* divide by zero? */ fpnotrap (FEC_DZRO); ABORT (TRAP_INT); } newV = divfp11 (&fac, &fsrc); F_STORE (qdouble, fac, FR[ac]); FPS = setfcc (FPS, fac.h, newV); break; } /* end switch fop */ return; } /* Effective address calculation for fp operands Inputs: spec = specifier len = length Outputs: VA = virtual address Warnings: - Do not call this routine for integer mode 0 operands - Do not call this routine more than once per instruction */ int32 GeteaFP (int32 spec, int32 len) { int32 adr, reg, ds; reg = spec & 07; /* reg number */ ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ switch (spec >> 3) { /* case on spec */ case 0: /* floating AC */ if (reg >= 06) { fpnotrap (FEC_OP); ABORT (TRAP_INT); } return 0; case 1: /* (R) */ return (R[reg] | ds); case 2: /* (R)+ */ if (reg == 7) len = 2; R[reg] = ((adr = R[reg]) + len) & 0177777; if (update_MM) MMR1 = (len << 3) | reg; return (adr | ds); case 3: /* @(R)+ */ R[reg] = ((adr = R[reg]) + 2) & 0177777; if (update_MM) MMR1 = 020 | reg; adr = ReadW (adr | ds); return (adr | dsenable); case 4: /* -(R) */ adr = R[reg] = (R[reg] - len) & 0177777; if (update_MM) MMR1 = (((-len) & 037) << 3) | reg; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); return (adr | ds); case 5: /* @-(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; if (update_MM) MMR1 = 0360 | reg; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); adr = ReadW (adr | ds); return (adr | dsenable); case 6: /* d(r) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; return (((R[reg] + adr) & 0177777) | dsenable); case 7: /* @d(R) */ adr = ReadW (PC | isenable); PC = (PC + 2) & 0177777; adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); return (adr | dsenable); } /* end switch */ return 0; } /* Read integer operand Inputs: VA = virtual address, VA<18:16> = mode, I/D space spec = specifier len = length (2/4 bytes) Outputs: data = data read from memory or I/O space */ uint32 ReadI (int32 VA, int32 spec, int32 len) { if ((len == WORD) || (spec == 027)) return (ReadW (VA) << 16); return ((ReadW (VA) << 16) | ReadW ((VA & ~0177777) | ((VA + 2) & 0177777))); } /* Read floating operand Inputs: fptr = pointer to output VA = virtual address, VA<18:16> = mode, I/D space spec = specifier len = length (4/8 bytes) */ void ReadFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) { int32 exta; if (spec <= 07) { F_LOAD_P (len == QUAD, FR[spec], fptr); return; } if (spec == 027) { fptr->h = (ReadW (VA) << FP_V_F0); fptr->l = 0; } else { exta = VA & ~0177777; fptr->h = (ReadW (VA) << FP_V_F0) | (ReadW (exta | ((VA + 2) & 0177777)) << FP_V_F1); if (len == QUAD) fptr->l = (ReadW (exta | ((VA + 4) & 0177777)) << FP_V_F2) | (ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3); else fptr->l = 0; } if ((GET_SIGN (fptr->h) != 0) && (GET_EXP (fptr->h) == 0) && (fpnotrap (FEC_UNDFV) == 0)) ABORT (TRAP_INT); return; } /* Write integer result Inputs: data = data to be written VA = virtual address, VA<18:16> = mode, I/D space spec = specifier len = length Outputs: none */ void WriteI (int32 data, int32 VA, int32 spec, int32 len) { WriteW ((data >> 16) & 0177777, VA); if ((len == WORD) || (spec == 027)) return; WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777)); return; } /* Write floating result Inputs: fptr = pointer to data to be written VA = virtual address, VA<18:16> = mode, I/D space spec = specifier len = length Outputs: none */ void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) { int32 exta; if (spec <= 07) { F_STORE_P (len == QUAD, fptr, FR[spec]); return; } WriteW ((fptr->h >> FP_V_F0) & 0177777, VA); if (spec == 027) return; exta = VA & ~0177777; WriteW ((fptr->h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777)); if (len == LONG) return; WriteW ((fptr->l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777)); WriteW ((fptr->l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777)); return; } /* FIS instructions */ t_stat fis11 (int32 IR) { int32 reg, exta; fpac_t fac, fsrc; reg = IR & 07; /* isolate reg */ if (reg == 7) /* choose I,D */ exta = isenable; else exta = dsenable; if (IR & 000740) { /* defined? */ if (CPUT (CPUT_03)) /* 11/03 reads word */ ReadW (exta | R[reg]); ABORT (TRAP_ILL); } FEC = 0; /* no errors */ FPS = FPS_IU|FPS_IV; /* trap ovf,unf */ fsrc.h = (ReadW (exta | R[reg]) << FP_V_F0) | (ReadW (exta | ((R[reg] + 2) & 0177777)) << FP_V_F1); fsrc.l = 0; fac.h = (ReadW (exta | ((R[reg] + 4) & 0177777)) << FP_V_F0) | (ReadW (exta | ((R[reg] + 6) & 0177777)) << FP_V_F1); fac.l = 0; if (GET_SIGN (fsrc.h) && (GET_EXP (fsrc.h) == 0)) /* clean 0's */ fsrc.h = fsrc.l = 0; if (GET_SIGN (fac.h) && (GET_EXP (fac.l) == 0)) fac.h = fac.l = 0; N = Z = V = C = 0; /* clear cc's */ switch ((IR >> 3) & 3) { /* case IR<5:3> */ case 0: /* FAD */ addfp11 (&fac, &fsrc); break; case 1: /* FSUB */ if (fsrc.h != 0) /* invert sign */ fsrc.h = fsrc.h ^ FP_SIGN; addfp11 (&fac, &fsrc); break; case 2: /* FMUL */ mulfp11 (&fac, &fsrc); break; case 3: /* FDIV */ if (fsrc.h == 0) { /* div by 0? */ V = N = C = 1; /* set cc's */ setTRAP (TRAP_FPE); /* set trap */ return SCPE_OK; } else divfp11 (&fac, &fsrc); break; } if (FEC == 0) { /* no err? */ WriteW ((fac.h >> FP_V_F0) & 0177777, exta | ((R[reg] + 4) & 0177777)); WriteW ((fac.h >> FP_V_F1) & 0177777, exta | ((R[reg] + 6) & 0177777)); R[reg] = (R[reg] + 4) & 0177777; /* pop stack */ N = (GET_SIGN (fac.h) != 0); /* set N,Z */ Z = (fac.h == 0); } else if (FEC == FEC_OVFLO) /* ovf? trap set */ V = 1; else if (FEC == FEC_UNFLO) /* unf? trap set */ V = N = 1; else return SCPE_IERR; /* what??? */ return SCPE_OK; } /* Floating point add Inputs: facp = pointer to src1 (output) fsrcp = pointer to src2 Outputs: ovflo = overflow variable */ int32 addfp11 (fpac_t *facp, fpac_t *fsrcp) { int32 facexp, fsrcexp, ediff; fpac_t facfrac, fsrcfrac; if (F_LT_AP (facp, fsrcp)) { /* if !fac! < !fsrc! */ facfrac = *facp; *facp = *fsrcp; /* swap operands */ *fsrcp = facfrac; } facexp = GET_EXP (facp->h); /* get exponents */ fsrcexp = GET_EXP (fsrcp->h); if (facexp == 0) { /* fac = 0? */ *facp = fsrcexp? *fsrcp: zero_fac; /* result fsrc or 0 */ return 0; } if (fsrcexp == 0) /* fsrc = 0? no op */ return 0; ediff = facexp - fsrcexp; /* exponent diff */ if (ediff >= 60) /* too big? no op */ return 0; F_GET_FRAC_P (facp, facfrac); /* get fractions */ F_GET_FRAC_P (fsrcp, fsrcfrac); F_LSH_GUARD (facfrac); /* guard fractions */ F_LSH_GUARD (fsrcfrac); if (GET_SIGN (facp->h) != GET_SIGN (fsrcp->h)) { /* signs different? */ if (ediff) { /* sub, shf fsrc */ F_RSH_V (fsrcfrac, ediff, fsrcfrac); } F_SUB (fsrcfrac, facfrac, facfrac); /* sub fsrc from fac */ if ((facfrac.h | facfrac.l) == 0) { /* result zero? */ *facp = zero_fac; /* no overflow */ return 0; } if (ediff <= 1) { /* big normalize? */ if ((facfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { F_LSH_K (facfrac, 24, facfrac); facexp = facexp - 24; } if ((facfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { F_LSH_K (facfrac, 12, facfrac); facexp = facexp - 12; } if ((facfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { F_LSH_K (facfrac, 6, facfrac); facexp = facexp - 6; } } while (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { F_LSH_1 (facfrac); facexp = facexp - 1; } } else { if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); /* add, shf fsrc */ } F_ADD (fsrcfrac, facfrac, facfrac); /* add fsrc to fac */ if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD + 1)) { F_RSH_1 (facfrac); /* carry out, shift */ facexp = facexp + 1; } } return round_and_pack (facp, facexp, &facfrac, 1); } /* Floating point multiply Inputs: facp = pointer to src1 (output) fsrcp = pointer to src2 Outputs: ovflo = overflow indicator */ int32 mulfp11 (fpac_t *facp, fpac_t *fsrcp) { int32 facexp, fsrcexp; fpac_t facfrac, fsrcfrac; facexp = GET_EXP (facp->h); /* get exponents */ fsrcexp = GET_EXP (fsrcp->h); if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ *facp = zero_fac; return 0; } F_GET_FRAC_P (facp, facfrac); /* get fractions */ F_GET_FRAC_P (fsrcp, fsrcfrac); facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ facp->h = facp->h ^ fsrcp->h; /* calculate sign */ frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ /* Multiplying two numbers in the range [.5,1) produces a result in the range [.25,1). Therefore, at most one bit of normalization is required to bring the result back to the range [.5,1). */ if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { F_LSH_1 (facfrac); facexp = facexp - 1; } return round_and_pack (facp, facexp, &facfrac, 1); } /* Floating point mod Inputs: facp = pointer to src1 (integer result) fsrcp = pointer to src2 fracp = pointer to fractional result Outputs: ovflo = overflow indicator See notes on multiply for initial operation */ int32 modfp11 (fpac_t *facp, fpac_t *fsrcp, fpac_t *fracp) { int32 facexp, fsrcexp; fpac_t facfrac, fsrcfrac, fmask; facexp = GET_EXP (facp->h); /* get exponents */ fsrcexp = GET_EXP (fsrcp->h); if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ *fracp = zero_fac; *facp = zero_fac; return 0; } F_GET_FRAC_P (facp, facfrac); /* get fractions */ F_GET_FRAC_P (fsrcp, fsrcfrac); facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ fracp->h = facp->h = facp->h ^ fsrcp->h; /* calculate sign */ frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ /* Multiplying two numbers in the range [.5,1) produces a result in the range [.25,1). Therefore, at most one bit of normalization is required to bring the result back to the range [.5,1). */ if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { F_LSH_1 (facfrac); facexp = facexp - 1; } /* There are three major cases of MODf: 1. Exp <= FP_BIAS (all fraction). Return 0 as integer, product as fraction. Underflow can occur. 2. Exp > FP_BIAS + #fraction bits (all integer). Return product as integer, 0 as fraction. Overflow can occur. 3. FP_BIAS < exp <= FP_BIAS + #fraction bits. Separate integer and fraction and return both. Neither overflow nor underflow can occur. */ if (facexp <= FP_BIAS) { /* case 1 */ *facp = zero_fac; return round_and_pack (fracp, facexp, &facfrac, 1); } if (facexp > ((FPS & FPS_D)? FP_BIAS + 56: FP_BIAS + 24)) { *fracp = zero_fac; /* case 2 */ return round_and_pack (facp, facexp, &facfrac, 0); } F_RSH_V (fmask_fac, facexp - FP_BIAS, fmask); /* shift mask */ fsrcfrac.l = facfrac.l & fmask.l; /* extract fraction */ fsrcfrac.h = facfrac.h & fmask.h; if ((fsrcfrac.h | fsrcfrac.l) == 0) *fracp = zero_fac; else { F_LSH_V (fsrcfrac, facexp - FP_BIAS, fsrcfrac); fsrcexp = FP_BIAS; if ((fsrcfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { F_LSH_K (fsrcfrac, 24, fsrcfrac); fsrcexp = fsrcexp - 24; } if ((fsrcfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { F_LSH_K (fsrcfrac, 12, fsrcfrac); fsrcexp = fsrcexp - 12; } if ((fsrcfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { F_LSH_K (fsrcfrac, 6, fsrcfrac); fsrcexp = fsrcexp - 6; } while (GET_BIT (fsrcfrac.h, FP_V_HB + FP_GUARD) == 0) { F_LSH_1 (fsrcfrac); fsrcexp = fsrcexp - 1; } round_and_pack (fracp, fsrcexp, &fsrcfrac, 1); } facfrac.l = facfrac.l & ~fmask.l; facfrac.h = facfrac.h & ~fmask.h; return round_and_pack (facp, facexp, &facfrac, 0); } /* Fraction multiply Inputs: f1p = pointer to multiplier (output) f2p = pointer to multiplicand fraction Note: the inputs are unguarded; the output is guarded. This routine performs a classic shift-and-add multiply. The low order bit of the multiplier is tested; if 1, the multiplicand is added into the high part of the double precision result. The result and the multiplier are both shifted right 1. For the 24b x 24b case, this routine develops 48b of result. For the 56b x 56b case, this routine only develops the top 64b of the the result. Because the inputs are normalized fractions, the interesting part of the result is the high 56+guard bits. Everything shifted off to the right, beyond 64b, plays no part in rounding or the result. There are many possible optimizations in this routine: scanning for groups of zeroes, particularly in the 56b x 56b case; using "extended multiply" capability if available in the hardware. */ void frac_mulfp11 (fpac_t *f1p, fpac_t *f2p) { fpac_t result, mpy, mpc; int32 i; result = zero_fac; /* clear result */ mpy = *f1p; /* get operands */ mpc = *f2p; F_LSH_GUARD (mpc); /* guard multipicand */ if ((mpy.l | mpc.l) == 0) { /* 24b x 24b? */ for (i = 0; i < 24; i++) { if (mpy.h & 1) result.h = result.h + mpc.h; F_RSH_1 (result); mpy.h = mpy.h >> 1; } } else { if (mpy.l != 0) { /* 24b x 56b? */ for (i = 0; i < 32; i++) { if (mpy.l & 1) { F_ADD (mpc, result, result); } F_RSH_1 (result); mpy.l = mpy.l >> 1; } } for (i = 0; i < 24; i++) { if (mpy.h & 1) { F_ADD (mpc, result, result); } F_RSH_1 (result); mpy.h = mpy.h >> 1; } } *f1p = result; return; } /* Floating point divide Inputs: facp = pointer to dividend (output) fsrcp = pointer to divisor Outputs: ovflo = overflow indicator Source operand must be checked for zero by caller! */ int32 divfp11 (fpac_t *facp, fpac_t *fsrcp) { int32 facexp, fsrcexp, i, count, qd; fpac_t facfrac, fsrcfrac, quo; fsrcexp = GET_EXP (fsrcp->h); /* get divisor exp */ facexp = GET_EXP (facp->h); /* get dividend exp */ if (facexp == 0) { /* test for zero */ *facp = zero_fac; /* result zero */ return 0; } F_GET_FRAC_P (facp, facfrac); /* get fractions */ F_GET_FRAC_P (fsrcp, fsrcfrac); F_LSH_GUARD (facfrac); /* guard fractions */ F_LSH_GUARD (fsrcfrac); facexp = facexp - fsrcexp + FP_BIAS + 1; /* calculate exp */ facp->h = facp->h ^ fsrcp->h; /* calculate sign */ qd = FPS & FPS_D; count = FP_V_HB + FP_GUARD + (qd? 33: 1); /* count = 56b/24b */ quo = zero_fac; for (i = count; (i > 0) && ((facfrac.h | facfrac.l) != 0); i--) { F_LSH_1 (quo); /* shift quotient */ if (!F_LT (facfrac, fsrcfrac)) { /* divd >= divr? */ F_SUB (fsrcfrac, facfrac, facfrac); /* divd - divr */ if (qd) /* double or single? */ quo.l = quo.l | 1; else quo.h = quo.h | 1; } F_LSH_1 (facfrac); /* shift divd */ } if (i > 0) { /* early exit? */ F_LSH_V (quo, i, quo); } /* Dividing two numbers in the range [.5,1) produces a result in the range [.5,2). Therefore, at most one bit of normalization is required to bring the result back to the range [.5,1). The choice of counts and quotient bit positions makes this work correctly. */ if (GET_BIT (quo.h, FP_V_HB + FP_GUARD) == 0) { F_LSH_1 (quo); facexp = facexp - 1; } return round_and_pack (facp, facexp, &quo, 1); } /* Update floating condition codes Note that FC is only set by STCfi via the integer condition codes Inputs: oldst = current status result = high result newV = new V Outputs: newst = new status */ int32 setfcc (int32 oldst, int32 result, int32 newV) { oldst = (oldst & ~FPS_CC) | newV; if (GET_SIGN (result)) oldst = oldst | FPS_N; if (GET_EXP (result) == 0) oldst = oldst | FPS_Z; return oldst; } /* Round (in place) floating point number to f_floating Inputs: fptr = pointer to floating number Outputs: ovflow = overflow */ int32 roundfp11 (fpac_t *fptr) { fpac_t outf; outf = *fptr; /* get argument */ F_ADD (fround_fac, outf, outf); /* round */ if (GET_SIGN (outf.h ^ fptr->h)) { /* flipped sign? */ outf.h = (outf.h ^ FP_SIGN) & 0xFFFFFFFF; /* restore sign */ if (fpnotrap (FEC_OVFLO)) /* if no int, clear */ *fptr = zero_fac; else *fptr = outf; /* return rounded */ return FPS_V; /* overflow */ } *fptr = outf; /* round was ok */ return 0; /* no overflow */ } /* Round result of calculation, test overflow, pack Input: facp = pointer to result, sign in place exp = result exponent, right justified fracp = pointer to result fraction, right justified with guard bits r = round (1) or truncate (0) Outputs: ovflo = overflow indicator */ int32 round_and_pack (fpac_t *facp, int32 exp, fpac_t *fracp, int r) { fpac_t frac; frac = *fracp; /* get fraction */ if (r && ((FPS & FPS_T) == 0)) { if (FPS & FPS_D) { F_ADD (dround_guard_fac, frac, frac); } else { F_ADD (fround_guard_fac, frac, frac); } if (GET_BIT (frac.h, FP_V_HB + FP_GUARD + 1)) { F_RSH_1 (frac); exp = exp + 1; } } F_RSH_GUARD (frac); facp->l = frac.l & FP_FRACL; facp->h = (facp->h & FP_SIGN) | ((exp & FP_M_EXP) << FP_V_EXP) | (frac.h & FP_FRACH); if (exp > 0377) { if (fpnotrap (FEC_OVFLO)) *facp = zero_fac; return FPS_V; } if ((exp <= 0) && (fpnotrap (FEC_UNFLO))) *facp = zero_fac; return 0; } /* Process floating point exception Inputs: code = exception code Outputs: int = FALSE if interrupt enabled, TRUE if disabled */ int32 fpnotrap (int32 code) { static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV }; if ((code >= FEC_ICVT) && (code <= FEC_UNDFV) && ((FPS & test_code[code >> 1]) == 0)) return TRUE; FPS = FPS | FPS_ER; FEC = code; FEA = (backup_PC - 2) & 0177777; if ((FPS & FPS_ID) == 0) setTRAP (TRAP_FPE); return FALSE; } simh-3.8.1/PDP11/pdp11_dl.c0000644000175000017500000005330511111105312013166 0ustar vlmvlm/* pdp11_dl.c: PDP-11 multiple terminal interface simulator Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dli,dlo DL11 terminal input/output 19-Nov-2008 RMS Revised for common TMXR show routines Revised to autoconfigure vectors 20-May-2008 RMS Added modem control support */ #if defined (VM_PDP10) /* PDP10 version */ #error "DL11 is not supported on the PDP-10!" #elif defined (VM_VAX) /* VAX version */ #error "DL11 is not supported on the VAX!" #else /* PDP-11 version */ #include "pdp11_defs.h" #endif #include "sim_sock.h" #include "sim_tmxr.h" #define DLX_MASK (DLX_LINES - 1) #define DLI_RCI 0 /* rcv ints */ #define DLI_DSI 1 /* dset ints */ /* Modem control */ #define DLX_V_MDM (TTUF_V_UF + 0) #define DLX_MDM (1u << DLX_V_MDM) /* registers */ #define DLICSR_DSI 0100000 /* dataset int, RO */ #define DLICSR_RNG 0040000 /* ring, RO */ #define DLICSR_CTS 0020000 /* CTS, RO */ #define DLICSR_CDT 0010000 /* CDT, RO */ #define DLICSR_SEC 0002000 /* sec rcv, RONI */ #define DLICSR_DSIE 0000040 /* DSI ie, RW */ #define DLICSR_SECX 0000010 /* sec xmt, RWNI */ #define DLICSR_RTS 0000004 /* RTS, RW */ #define DLICSR_DTR 0000002 /* DTR, RW */ #define DLICSR_RD (CSR_DONE|CSR_IE) /* DL11C */ #define DLICSR_WR (CSR_IE) #define DLICSR_RD_M (DLICSR_DSI|DLICSR_RNG|DLICSR_CTS|DLICSR_CDT|DLICSR_SEC| \ CSR_DONE|CSR_IE|DLICSR_DSIE|DLICSR_SECX|DLICSR_RTS|DLICSR_DTR) #define DLICSR_WR_M (CSR_IE|DLICSR_DSIE|DLICSR_SECX|DLICSR_RTS|DLICSR_DTR) #define DLIBUF_ERR 0100000 #define DLIBUF_OVR 0040000 #define DLIBUF_RBRK 0020000 #define DLIBUF_RD (DLIBUF_ERR|DLIBUF_OVR|DLIBUF_RBRK|0377) #define DLOCSR_MNT 0000004 /* maint, RWNI */ #define DLOCSR_XBR 0000001 /* xmit brk, RWNI */ #define DLOCSR_RD (CSR_DONE|CSR_IE|DLOCSR_MNT|DLOCSR_XBR) #define DLOCSR_WR (CSR_IE|DLOCSR_MNT|DLOCSR_XBR) extern int32 int_req[IPL_HLVL]; extern int32 tmxr_poll; uint16 dli_csr[DLX_LINES] = { 0 }; /* control/status */ uint16 dli_buf[DLX_LINES] = { 0 }; uint32 dli_ireq[2] = { 0, 0}; uint16 dlo_csr[DLX_LINES] = { 0 }; /* control/status */ uint8 dlo_buf[DLX_LINES] = { 0 }; uint32 dlo_ireq = 0; TMLN dlx_ldsc[DLX_LINES] = { 0 }; /* line descriptors */ TMXR dlx_desc = { DLX_LINES, 0, 0, dlx_ldsc }; /* mux descriptor */ t_stat dlx_rd (int32 *data, int32 PA, int32 access); t_stat dlx_wr (int32 data, int32 PA, int32 access); t_stat dlx_reset (DEVICE *dptr); t_stat dli_svc (UNIT *uptr); t_stat dlo_svc (UNIT *uptr); t_stat dlx_attach (UNIT *uptr, char *cptr); t_stat dlx_detach (UNIT *uptr); t_stat dlx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc); void dlx_enbdis (int32 dis); void dli_clr_int (int32 ln, uint32 wd); void dli_set_int (int32 ln, uint32 wd); int32 dli_iack (void); void dlo_clr_int (int32 ln); void dlo_set_int (int32 ln); int32 dlo_iack (void); void dlx_reset_ln (int32 ln); /* DLI data structures dli_dev DLI device descriptor dli_unit DLI unit descriptor dli_reg DLI register list */ DIB dli_dib = { IOBA_DL, IOLN_DL, &dlx_rd, &dlx_wr, 2, IVCL (DLI), VEC_DLI, { &dli_iack, &dlo_iack } }; UNIT dli_unit = { UDATA (&dli_svc, 0, 0), KBD_POLL_WAIT }; REG dli_reg[] = { { BRDATA (BUF, dli_buf, DEV_RDX, 16, DLX_LINES) }, { BRDATA (CSR, dli_csr, DEV_RDX, 16, DLX_LINES) }, { GRDATA (IREQ, dli_ireq[DLI_RCI], DEV_RDX, DLX_LINES, 0) }, { GRDATA (DSI, dli_ireq[DLI_DSI], DEV_RDX, DLX_LINES, 0) }, { DRDATA (LINES, dlx_desc.lines, 6), REG_HRO }, { GRDATA (DEVADDR, dli_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVIOLN, dli_dib.lnt, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, dli_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; MTAB dli_mod[] = { { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dlx_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &dlx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &dlx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &dlx_desc }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 1, "VECTOR", NULL, &set_vec, &show_vec_mux, (void *) &dlx_desc }, { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", &dlx_set_lines, &tmxr_show_lines, (void *) &dlx_desc }, { 0 } }; DEVICE dli_dev = { "DLI", &dli_unit, dli_reg, dli_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &dlx_reset, NULL, &dlx_attach, &dlx_detach, &dli_dib, DEV_FLTA | DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS }; /* DLO data structures dlo_dev DLO device descriptor dlo_unit DLO unit descriptor dlo_reg DLO register list */ UNIT dlo_unit[] = { { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT } }; REG dlo_reg[] = { { BRDATA (BUF, dlo_buf, DEV_RDX, 8, DLX_LINES) }, { BRDATA (CSR, dlo_csr, DEV_RDX, 16, DLX_LINES) }, { GRDATA (IREQ, dlo_ireq, DEV_RDX, DLX_LINES, 0) }, { URDATA (TIME, dlo_unit[0].wait, 10, 31, 0, DLX_LINES, PV_LEFT) }, { NULL } }; MTAB dlo_mod[] = { { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, { DLX_MDM, 0, "no dataset", "NODATASET", NULL }, { DLX_MDM, DLX_MDM, "dataset", "DATASET", NULL }, { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dlx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &dlx_desc }, { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &dlx_desc }, { 0 } }; DEVICE dlo_dev = { "DLO", dlo_unit, dlo_reg, dlo_mod, DLX_LINES, 10, 31, 1, 8, 8, NULL, NULL, &dlx_reset, NULL, NULL, NULL, NULL, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS }; /* Terminal input routines */ t_stat dlx_rd (int32 *data, int32 PA, int32 access) { int32 ln = ((PA - dli_dib.ba) >> 3) & DLX_MASK; switch ((PA >> 1) & 03) { /* decode PA<2:1> */ case 00: /* tti csr */ *data = dli_csr[ln] & ((dlo_unit[ln].flags & DLX_MDM)? DLICSR_RD_M: DLICSR_RD); dli_csr[ln] &= ~DLICSR_DSI; /* clr DSI flag */ dli_clr_int (ln, DLI_DSI); /* clr dset int req */ return SCPE_OK; case 01: /* tti buf */ *data = dli_buf[ln] & DLIBUF_RD; dli_csr[ln] &= ~CSR_DONE; /* clr rcv done */ dli_clr_int (ln, DLI_RCI); /* clr rcv int req */ return SCPE_OK; case 02: /* tto csr */ *data = dlo_csr[ln] & DLOCSR_RD; return SCPE_OK; case 03: /* tto buf */ *data = dlo_buf[ln]; return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } t_stat dlx_wr (int32 data, int32 PA, int32 access) { int32 ln = ((PA - dli_dib.ba) >> 3) & DLX_MASK; TMLN *lp = &dlx_ldsc[ln]; switch ((PA >> 1) & 03) { /* decode PA<2:1> */ case 00: /* tti csr */ if (PA & 1) /* odd byte RO */ return SCPE_OK; if ((data & CSR_IE) == 0) dli_clr_int (ln, DLI_RCI); else if ((dli_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) dli_set_int (ln, DLI_RCI); if (dlo_unit[ln].flags & DLX_MDM) { /* modem control */ if ((data & DLICSR_DSIE) == 0) dli_clr_int (ln, DLI_DSI); else if ((dli_csr[ln] & (DLICSR_DSI|DLICSR_DSIE)) == DLICSR_DSI) dli_set_int (ln, DLI_DSI); if ((data ^ dli_csr[ln]) & DLICSR_DTR) { /* DTR change? */ if ((data & DLICSR_DTR) && lp->conn) { /* setting DTR? */ dli_csr[ln] = (dli_csr[ln] & ~DLICSR_RNG) | (DLICSR_CDT|DLICSR_CTS|DLICSR_DSI); if (data & DLICSR_DSIE) /* if ie, req int */ dli_set_int (ln, DLI_DSI); } /* end DTR 0->1 + ring */ else { /* clearing DTR */ if (lp->conn) { /* connected? */ tmxr_linemsg (lp, "\r\nLine hangup\r\n"); tmxr_reset_ln (lp); /* reset line */ if (dli_csr[ln] & DLICSR_CDT) { /* carrier det? */ dli_csr[ln] |= DLICSR_DSI; if (data & DLICSR_DSIE) /* if ie, req int */ dli_set_int (ln, DLI_DSI); } } dli_csr[ln] &= ~(DLICSR_CDT|DLICSR_RNG|DLICSR_CTS); /* clr CDT,RNG,CTS */ } /* end DTR 1->0 */ } /* end DTR chg */ dli_csr[ln] = (uint16) ((dli_csr[ln] & ~DLICSR_WR_M) | (data & DLICSR_WR_M)); } /* end modem */ dli_csr[ln] = (uint16) ((dli_csr[ln] & ~DLICSR_WR) | (data & DLICSR_WR)); return SCPE_OK; case 01: /* tti buf */ return SCPE_OK; case 02: /* tto csr */ if (PA & 1) return SCPE_OK; if ((data & CSR_IE) == 0) dlo_clr_int (ln); else if ((dlo_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) dlo_set_int (ln); dlo_csr[ln] = (uint16) ((dlo_csr[ln] & ~DLOCSR_WR) | (data & DLOCSR_WR)); return SCPE_OK; case 03: /* tto buf */ if ((PA & 1) == 0) dlo_buf[ln] = data & 0377; dlo_csr[ln] &= ~CSR_DONE; dlo_clr_int (ln); sim_activate (&dlo_unit[ln], dlo_unit[ln].wait); return SCPE_OK; } /* end switch PA */ return SCPE_NXM; } /* Terminal input service */ t_stat dli_svc (UNIT *uptr) { int32 ln, c, temp; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; sim_activate (uptr, tmxr_poll); /* continue poll */ ln = tmxr_poll_conn (&dlx_desc); /* look for connect */ if (ln >= 0) { /* got one? rcv enb */ dlx_ldsc[ln].rcve = 1; if (dlo_unit[ln].flags & DLX_MDM) { /* modem control? */ if (dli_csr[ln] & DLICSR_DTR) /* DTR already set? */ dli_csr[ln] |= (DLICSR_CDT|DLICSR_CTS|DLICSR_DSI); else dli_csr[ln] |= (DLICSR_RNG|DLICSR_DSI); /* no, ring */ if (dli_csr[ln] & DLICSR_DSIE) /* if ie, */ dli_set_int (ln, DLI_DSI); /* req int */ } /* end modem */ } /* end new conn */ tmxr_poll_rx (&dlx_desc); /* poll for input */ for (ln = 0; ln < DLX_LINES; ln++) { /* loop thru lines */ if (dlx_ldsc[ln].conn) { /* connected? */ if (temp = tmxr_getc_ln (&dlx_ldsc[ln])) { /* get char */ if (temp & SCPE_BREAK) /* break? */ c = DLIBUF_ERR|DLIBUF_RBRK; else c = sim_tt_inpcvt (temp, TT_GET_MODE (dlo_unit[ln].flags)); if (dli_csr[ln] & CSR_DONE) c |= DLIBUF_ERR|DLIBUF_OVR; else dli_csr[ln] |= CSR_DONE; if (dli_csr[ln] & CSR_IE) dli_set_int (ln, DLI_RCI); dli_buf[ln] = c; } } else if (dlo_unit[ln].flags & DLX_MDM) { /* discpnn & modem? */ if (dli_csr[ln] & DLICSR_CDT) { /* carrier detect? */ dli_csr[ln] |= DLICSR_DSI; /* dataset change */ if (dli_csr[ln] & DLICSR_DSIE) /* if ie, */ dli_set_int (ln, DLI_DSI); /* req int */ } dli_csr[ln] &= ~(DLICSR_CDT|DLICSR_RNG|DLICSR_CTS); /* clr CDT,RNG,CTS */ } } return SCPE_OK; } /* Terminal output service */ t_stat dlo_svc (UNIT *uptr) { int32 c; int32 ln = uptr - dlo_unit; /* line # */ if (dlx_ldsc[ln].conn) { /* connected? */ if (dlx_ldsc[ln].xmte) { /* tx enabled? */ TMLN *lp = &dlx_ldsc[ln]; /* get line */ c = sim_tt_outcvt (dlo_buf[ln], TT_GET_MODE (dlo_unit[ln].flags)); if (c >= 0) /* output char */ tmxr_putc_ln (lp, c); tmxr_poll_tx (&dlx_desc); /* poll xmt */ } else { tmxr_poll_tx (&dlx_desc); /* poll xmt */ sim_activate (uptr, dlo_unit[ln].wait); /* wait */ return SCPE_OK; } } dlo_csr[ln] |= CSR_DONE; /* set done */ if (dlo_csr[ln] & CSR_IE) dlo_set_int (ln); return SCPE_OK; } /* Interrupt routines */ void dli_clr_int (int32 ln, uint32 wd) { dli_ireq[wd] &= ~(1 << ln); /* clr rcv/dset int */ if ((dli_ireq[DLI_RCI] | dli_ireq[DLI_DSI]) == 0) /* all clr? */ CLR_INT (DLI); /* all clr? */ else SET_INT (DLI); /* no, set intr */ return; } void dli_set_int (int32 ln, uint32 wd) { dli_ireq[wd] |= (1 << ln); /* set rcv/dset int */ SET_INT (DLI); /* set master intr */ return; } int32 dli_iack (void) { int32 ln; for (ln = 0; ln < DLX_LINES; ln++) { /* find 1st line */ if ((dli_ireq[DLI_RCI] | dli_ireq[DLI_DSI]) & (1 << ln)) { dli_clr_int (ln, DLI_RCI); /* clr both req */ dli_clr_int (ln, DLI_DSI); return (dli_dib.vec + (ln * 010)); /* return vector */ } } return 0; } void dlo_clr_int (int32 ln) { dlo_ireq &= ~(1 << ln); /* clr xmit int */ if (dlo_ireq == 0) /* all clr? */ CLR_INT (DLO); else SET_INT (DLO); /* no, set intr */ return; } void dlo_set_int (int32 ln) { dlo_ireq |= (1 << ln); /* set xmit int */ SET_INT (DLO); /* set master intr */ return; } int32 dlo_iack (void) { int32 ln; for (ln = 0; ln < DLX_LINES; ln++) { /* find 1st line */ if (dlo_ireq & (1 << ln)) { dlo_clr_int (ln); /* clear intr */ return (dli_dib.vec + (ln * 010) + 4); /* return vector */ } } return 0; } /* Reset */ t_stat dlx_reset (DEVICE *dptr) { int32 ln; dlx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ sim_cancel (&dli_unit); /* assume stop */ if (dli_unit.flags & UNIT_ATT) /* if attached, */ sim_activate (&dli_unit, tmxr_poll); /* activate */ for (ln = 0; ln < DLX_LINES; ln++) /* for all lines */ dlx_reset_ln (ln); return auto_config (dli_dev.name, dlx_desc.lines); /* auto config */ } /* Reset individual line */ void dlx_reset_ln (int32 ln) { dli_buf[ln] = 0; /* clear buf */ if (dlo_unit[ln].flags & DLX_MDM) /* modem */ dli_csr[ln] &= DLICSR_DTR; /* dont clr DTR */ else dli_csr[ln] = 0; dlo_buf[ln] = 0; /* clear buf */ dlo_csr[ln] = CSR_DONE; sim_cancel (&dlo_unit[ln]); /* deactivate */ dli_clr_int (ln, DLI_RCI); dli_clr_int (ln, DLI_DSI); dlo_clr_int (ln); return; } /* Attach master unit */ t_stat dlx_attach (UNIT *uptr, char *cptr) { t_stat r; r = tmxr_attach (&dlx_desc, uptr, cptr); /* attach */ if (r != SCPE_OK) /* error */ return r; sim_activate (uptr, tmxr_poll); /* start poll */ return SCPE_OK; } /* Detach master unit */ t_stat dlx_detach (UNIT *uptr) { int32 i; t_stat r; r = tmxr_detach (&dlx_desc, uptr); /* detach */ for (i = 0; i < DLX_LINES; i++) /* all lines, */ dlx_ldsc[i].rcve = 0; /* disable rcv */ sim_cancel (uptr); /* stop poll */ return r; } /* Enable/disable device */ void dlx_enbdis (int32 dis) { if (dis) { dli_dev.flags = dli_dev.flags | DEV_DIS; dlo_dev.flags = dlo_dev.flags | DEV_DIS; } else { dli_dev.flags = dli_dev.flags & ~DEV_DIS; dlo_dev.flags = dlo_dev.flags & ~DEV_DIS; } return; } /* Change number of lines */ t_stat dlx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 newln, i, t; t_stat r; if (cptr == NULL) return SCPE_ARG; newln = get_uint (cptr, 10, DLX_LINES, &r); if ((r != SCPE_OK) || (newln == dlx_desc.lines)) return r; if (newln == 0) return SCPE_ARG; if (newln < dlx_desc.lines) { for (i = newln, t = 0; i < dlx_desc.lines; i++) t = t | dlx_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; for (i = newln; i < dlx_desc.lines; i++) { if (dlx_ldsc[i].conn) { tmxr_linemsg (&dlx_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_reset_ln (&dlx_ldsc[i]); /* reset line */ } dlo_unit[i].flags |= UNIT_DIS; dlx_reset_ln (i); } } else { for (i = dlx_desc.lines; i < newln; i++) { dlo_unit[i].flags &= ~UNIT_DIS; dlx_reset_ln (i); } } dlx_desc.lines = newln; dli_dib.lnt = newln * 010; /* upd IO page lnt */ return auto_config (dli_dev.name, newln); /* auto config */ } simh-3.8.1/PDP11/pdp11_io_lib.h0000644000175000017500000000435711111072272014044 0ustar vlmvlm/* pdp11_io_lib.h: Unibus/Qbus common support routines header file Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. */ #ifndef _PDP11_IO_LIB_H_ #define _PDP11_IO_LIB_H_ 0 t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc); t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc); t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 arg, void *desc); t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat auto_config (char *name, int32 nctrl); t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); void init_ubus_tab (void); t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); #endif simh-3.8.1/PDP11/pdp11_vh.c0000644000175000017500000011131311111105312013176 0ustar vlmvlm/* pdp11_vh.c: DHQ11 asynchronous terminal multiplexor simulator Copyright (c) 2004-2008, John A. Dundas III Portions derived from work by 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 THE AUTHOR 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 Author. vh DHQ11 asynch multiplexor for SIMH 19-Nov-08 RMS Revised for common TMXR show routines 18-Jun-07 RMS Added UNIT_IDLE flag 29-Oct-06 RMS Synced poll and clock 07-Jul-05 RMS Removed extraneous externs 15-Jun-05 RMS Revised for new autoconfigure interface Fixed bug in vector display routine 12-Jun-04 RMS Repair MS2SIMH macro to avoid divide by 0 bug 08-Jun-04 JAD Repair vh_dev initialization; remove unused variables, cast to avoid conversion confusion 07-Jun-04 JAD Complete function prototypes of forward declarations. Repair broken prototypes of vh_rd() and vh_wr() Explicitly size integer declarations 4-Jun-04 JAD Preliminary code: If operating in a PDP-11 Unibus environment, force DHU mode 29-May-04 JAD Make certain RX.TIMER is within allowable range 25-May-04 JAD All time-based operations are scaled by tmxr_poll units 23-May-04 JAD Change to fifo_get() and dq_tx_report() to avoid gratuitous stack manipulation 20-May-04 JAD Made modem control and auto-hangup unit flags 19-May-04 JAD Fix problem with modem status where the line number was not being included 12-May-04 JAD Revised for updated tmxr interfaces 28-Jan-04 JAD Original creation and testing I/O Page Registers CSR 17 760 440 (float) Vector: 300 (float) Priority: BR4 Rank: 32 */ /* MANY constants needed! */ #if defined (VM_VAX) #include "vax_defs.h" extern int32 int_req[IPL_HLVL]; #endif #if defined (VM_PDP11) #include "pdp11_defs.h" extern int32 int_req[IPL_HLVL]; extern int32 cpu_opt; #endif #include "sim_sock.h" #include "sim_tmxr.h" /* imports from pdp11_stddev.c: */ extern int32 tmxr_poll, clk_tps; /* convert ms to SIMH time units based on tmxr_poll polls per second */ #define MS2SIMH(ms) (((ms) * clk_tps) / 1000) extern FILE *sim_log; #ifndef VH_MUXES #define VH_MUXES (4) #endif #define VH_MNOMASK (VH_MUXES - 1) #define VH_LINES (8) #define UNIT_V_MODEDHU (UNIT_V_UF + 0) #define UNIT_V_FASTDMA (UNIT_V_UF + 1) #define UNIT_V_MODEM (UNIT_V_UF + 2) #define UNIT_V_HANGUP (UNIT_V_UF + 3) #define UNIT_MODEDHU (1 << UNIT_V_MODEDHU) #define UNIT_FASTDMA (1 << UNIT_V_FASTDMA) #define UNIT_MODEM (1 << UNIT_V_MODEM) #define UNIT_HANGUP (1 << UNIT_V_HANGUP) /* VHCSR - 160440 - Control and Status Register */ #define CSR_M_IND_ADDR (017) #define CSR_SKIP (1 << 4) #define CSR_MASTER_RESET (1 << 5) #define CSR_RXIE (1 << 6) #define CSR_RX_DATA_AVAIL (1 << 7) #define CSR_M_TX_LINE (017) #define CSR_V_TX_LINE (8) #define CSR_TX_DMA_ERR (1 << 12) #define CSR_DIAG_FAIL (1 << 13) #define CSR_TXIE (1 << 14) #define CSR_TX_ACTION (1 << 15) #define CSR_GETCHAN(x) ((x) & CSR_M_IND_ADDR) #define CSR_RW \ (CSR_TXIE|CSR_RXIE|CSR_SKIP|CSR_M_IND_ADDR|CSR_MASTER_RESET) #define RESET_ABORT (052525) /* Receive Buffer (RBUF) */ #define FIFO_SIZE (256) #define FIFO_ALARM (191) #define FIFO_HALF (FIFO_SIZE / 2) #define RBUF_M_RX_CHAR (0377) #define RBUF_M_RX_LINE (07) #define RBUF_V_RX_LINE (8) #define RBUF_PARITY_ERR (1 << 12) #define RBUF_FRAME_ERR (1 << 13) #define RBUF_OVERRUN_ERR (1 << 14) #define RBUF_DATA_VALID (1 << 15) #define RBUF_GETLINE(x) (((x) >> RBUF_V_RX_LINE) & RBUF_M_RX_LINE) #define RBUF_PUTLINE(x) ((x) << RBUF_V_RX_LINE) #define RBUF_DIAG \ (RBUF_PARITY_ERR|RBUF_FRAME_ERR|RBUF_OVERRUN_ERR) #define XON (021) #define XOFF (023) /* Transmit Character Register (TXCHAR) */ #define TXCHAR_M_CHAR (0377) #define TXCHAR_TX_DATA_VALID (1 << 15) /* Receive Timer Register (RXTIMER) */ #define RXTIMER_M_RX_TIMER (0377) /* Line-Parameter Register (LPR) */ #define LPR_DISAB_XRPT (1 << 0) /* not impl. in real DHU */ #define LPR_V_DIAG (1) #define LPR_M_DIAG (03) #define LPR_V_CHAR_LGTH (3) #define LPR_M_CHAR_LGTH (03) #define LPR_PARITY_ENAB (1 << 5) #define LPR_EVEN_PARITY (1 << 6) #define LPR_STOP_CODE (1 << 7) #define LPR_V_RX_SPEED (8) #define LPR_M_RX_SPEED (017) #define LPR_V_TX_SPEED (12) #define LPR_M_TX_SPEED (017) #define RATE_50 (0) #define RATE_75 (1) #define RATE_110 (2) #define RATE_134 (3) #define RATE_150 (4) #define RATE_300 (5) #define RATE_600 (6) #define RATE_1200 (7) #define RATE_1800 (8) #define RATE_2000 (9) #define RATE_2400 (10) #define RATE_4800 (11) #define RATE_7200 (12) #define RATE_9600 (13) #define RATE_19200 (14) #define RATE_38400 (15) /* Line-Status Register (STAT) */ #define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */ #define STAT_MDL (1 << 9) /* always 0, has modem support */ #define STAT_CTS (1 << 11) /* CTS from modem */ #define STAT_DCD (1 << 12) /* DCD from modem */ #define STAT_RI (1 << 13) /* RI from modem */ #define STAT_DSR (1 << 15) /* DSR from modem */ /* FIFO Size Register (FIFOSIZE) */ #define FIFOSIZE_M_SIZE (0377) /* FIFO Data Register (FIFODATA) */ #define FIFODATA_W0 (0377) #define FIFODATA_V_W1 (8) #define FIFODATA_M_W1 (0377) /* Line-Control Register (LNCTRL) */ #define LNCTRL_TX_ABORT (1 << 0) #define LNCTRL_IAUTO (1 << 1) #define LNCTRL_RX_ENA (1 << 2) #define LNCTRL_BREAK (1 << 3) #define LNCTRL_OAUTO (1 << 4) #define LNCTRL_FORCE_XOFF (1 << 5) #define LNCTRL_V_MAINT (6) #define LNCTRL_M_MAINT (03) #define LNCTRL_LINK_TYPE (1 << 8) /* 0=data leads only, 1=modem */ #define LNCTRL_DTR (1 << 9) /* DTR to modem */ #define LNCTRL_RTS (1 << 12) /* RTS to modem */ /* Transmit Buffer Address Register Number 1 (TBUFFAD1) */ /* Transmit Buffer Address Register Number 2 (TBUFFAD2) */ #define TB2_M_TBUFFAD (077) #define TB2_TX_DMA_START (1 << 7) #define TB2_TX_ENA (1 << 15) /* Transmit DMA Buffer Counter (TBUFFCT) */ /* Self-Test Error Codes */ #define SELF_NULL (0201) #define SELF_SKIP (0203) #define SELF_OCT (0211) #define SELF_RAM (0225) #define SELF_RCD (0231) #define SELF_DRD (0235) #define BMP_OK (0305) #define BMP_BAD (0307) /* Loopback types */ #define LOOP_NONE (0) #define LOOP_H325 (1) #define LOOP_H3101 (2) /* p.2-13 DHQ manual */ /* Local storage */ static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */ static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */ static uint16 vh_mcount[VH_MUXES] = { 0 }; static uint32 vh_timeo[VH_MUXES] = { 0 }; static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */ /* XOFF'd channels, one bit/channel */ static uint32 vh_stall[VH_MUXES] = { 0 }; static uint16 vh_loop[VH_MUXES] = { 0 }; /* loopback status */ /* One bit per controller: */ static uint32 vh_rxi = 0; /* rcv interrupts */ static uint32 vh_txi = 0; /* xmt interrupts */ static uint32 vh_crit = 0; /* FIFO.CRIT */ static const int32 bitmask[4] = { 037, 077, 0177, 0377 }; /* RX FIFO state */ static int32 rbuf_idx[VH_MUXES] = { 0 };/* index into vh_rbuf */ static uint32 vh_rbuf[VH_MUXES][FIFO_SIZE] = { 0 }; /* TXQ state */ #define TXQ_SIZE (16) static int32 txq_idx[VH_MUXES] = { 0 }; static uint32 vh_txq[VH_MUXES][TXQ_SIZE] = { 0 }; /* Need to extend the TMLN structure */ typedef struct { TMLN *tmln; uint16 lpr; /* line parameters */ uint16 lnctrl; /* line control */ uint16 lstat; /* line modem status */ uint16 tbuffct; /* remaining character count */ uint16 tbuf1; uint16 tbuf2; uint16 txchar; /* single character I/O */ } TMLX; static TMLN vh_ldsc[VH_MUXES * VH_LINES] = { 0 }; static TMXR vh_desc = { VH_MUXES * VH_LINES, 0, 0, vh_ldsc }; static TMLX vh_parm[VH_MUXES * VH_LINES] = { 0 }; /* Forward references */ static t_stat vh_rd (int32 *data, int32 PA, int32 access); static t_stat vh_wr (int32 data, int32 PA, int32 access); static t_stat vh_svc (UNIT *uptr); static int32 vh_rxinta (void); static int32 vh_txinta (void); static t_stat vh_clear (int32 vh, t_bool flag); static t_stat vh_reset (DEVICE *dptr); static t_stat vh_attach (UNIT *uptr, char *cptr); static t_stat vh_detach (UNIT *uptr); static t_stat vh_show_debug (FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat vh_show_rbuf (FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat vh_show_txq (FILE *st, UNIT *uptr, int32 val, void *desc); static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data); static void doDMA (int32 vh, int32 chan); int32 tmxr_send_buffered_data (TMLN *lp); /* SIMH I/O Structures */ static DIB vh_dib = { IOBA_VH, IOLN_VH * VH_MUXES, &vh_rd, /* read */ &vh_wr, /* write */ 2, /* # of vectors */ IVCL (VHRX), VEC_VHRX, { &vh_rxinta, &vh_txinta } /* int. ack. routines */ }; static UNIT vh_unit[VH_MUXES] = { { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, }; static const REG vh_reg[] = { { BRDATA (CSR, vh_csr, DEV_RDX, 16, VH_MUXES) }, { GRDATA (DEVADDR, vh_dib.ba, DEV_RDX, 32, 0), REG_HRO }, { GRDATA (DEVVEC, vh_dib.vec, DEV_RDX, 16, 0), REG_HRO }, { NULL } }; static const MTAB vh_mod[] = { { UNIT_MODEDHU, 0, "DHV mode", "DHV", NULL }, { UNIT_MODEDHU, UNIT_MODEDHU, "DHU mode", "DHU", NULL }, { UNIT_FASTDMA, 0, NULL, "NORMAL", NULL }, { UNIT_FASTDMA, UNIT_FASTDMA, "fast DMA", "FASTDMA", NULL }, { UNIT_MODEM, 0, NULL, "NOMODEM", NULL }, { UNIT_MODEM, UNIT_MODEM, "modem", "MODEM", NULL }, { UNIT_HANGUP, 0, NULL, "NOHANGUP", NULL }, { UNIT_HANGUP, UNIT_HANGUP, "hangup", "HANGUP", NULL }, { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, VH_LINES, "VECTOR", "VECTOR", &set_vec, &show_vec_mux, (void *) &vh_desc }, { MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE", &set_addr_flt, NULL, NULL }, /* this one is dangerous, don't use yet */ { MTAB_XTD|MTAB_VDV, 0, "LINES", "LINES", NULL, &tmxr_show_lines, (void *) &vh_desc }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &vh_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &vh_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &vh_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &vh_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEBUG", NULL, NULL, &vh_show_debug, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "RBUF", NULL, NULL, &vh_show_rbuf, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "TXQ", NULL, NULL, &vh_show_txq, NULL }, { 0 } }; DEVICE vh_dev = { "VH", /* name */ vh_unit, /* units */ (REG *)vh_reg, /* registers */ (MTAB *)vh_mod, /* modifiers */ VH_MUXES, /* # units */ DEV_RDX, /* address radix */ 8, /* address width */ 1, /* address increment */ DEV_RDX, /* data radix */ 8, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &vh_reset, /* reset routine */ NULL, /* boot routine */ &vh_attach, /* attach routine */ &vh_detach, /* detach routine */ (void *)&vh_dib, /* context */ DEV_FLTA | DEV_DISABLE | DEV_DIS |DEV_NET | DEV_QBUS | DEV_UBUS, /* flags */ }; /* Interrupt routines */ static void vh_clr_rxint ( int32 vh ) { vh_rxi &= ~(1 << vh); if (vh_rxi == 0) CLR_INT (VHRX); else SET_INT (VHRX); } static void vh_set_rxint ( int32 vh ) { vh_rxi |= (1 << vh); SET_INT (VHRX); } /* RX interrupt ack. (bus cycle) */ static int32 vh_rxinta (void) { int32 vh; for (vh = 0; vh < VH_MUXES; vh++) { if (vh_rxi & (1 << vh)) { vh_clr_rxint (vh); return (vh_dib.vec + (vh * 010)); } } return (0); } static void vh_clr_txint ( int32 vh ) { vh_txi &= ~(1 << vh); if (vh_txi == 0) CLR_INT (VHTX); else SET_INT (VHTX); } static void vh_set_txint ( int32 vh ) { vh_txi |= (1 << vh); SET_INT (VHTX); } /* TX interrupt ack. (bus cycle) */ static int32 vh_txinta (void) { int32 vh; for (vh = 0; vh < VH_MUXES; vh++) { if (vh_txi & (1 << vh)) { vh_clr_txint (vh); return (vh_dib.vec + 4 + (vh * 010)); } } return (0); } /* RX FIFO get/put routines */ /* return 0 on success, -1 on FIFO overflow */ static int32 fifo_put ( int32 vh, TMLX *lp, int32 data ) { int32 status = 0; if (lp == NULL) goto override; /* this might have to move to vh_getc() */ if ((lp->lnctrl & LNCTRL_OAUTO) && ((data & RBUF_DIAG) == 0)) { TMLX *l0p; /* implement transmitted data flow control */ switch (data & 0377) { case XON: lp->tbuf2 |= TB2_TX_ENA; goto common; case XOFF: lp->tbuf2 &= ~TB2_TX_ENA; common: /* find line 0 for this controller */ l0p = &vh_parm[vh * VH_LINES]; if (l0p->lpr & LPR_DISAB_XRPT) return (0); break; default: break; } } /* BUG: which of the following 2 is correct? */ /* if ((data & RBUF_DIAG) == RBUF_DIAG) */ if (data & RBUF_DIAG) goto override; if (((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) == 2) goto override; if (!(lp->lnctrl & LNCTRL_RX_ENA)) return (0); override: vh_csr[vh] |= CSR_RX_DATA_AVAIL; if (rbuf_idx[vh] < FIFO_SIZE) { vh_rbuf[vh][rbuf_idx[vh]] = data; rbuf_idx[vh] += 1; } else { vh_ovrrun[vh] |= (1 << RBUF_GETLINE (data)); status = -1; } if (vh_csr[vh] & CSR_RXIE) { if (vh_unit[vh].flags & UNIT_MODEDHU) { /* was it a modem status change? */ if ((data & RBUF_DIAG) == RBUF_DIAG) vh_set_rxint (vh); /* look for FIFO alarm @ 3/4 full */ else if (rbuf_idx[vh] == FIFO_ALARM) vh_set_rxint (vh); else if (vh_timer[vh] == 0) ; /* nothing, infinite timeout */ else if (vh_timer[vh] == 1) vh_set_rxint (vh); else if (vh_timeo[vh] == 0) vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; } else { /* Interrupt on transition _from_ an empty FIFO */ if (rbuf_idx[vh] == 1) vh_set_rxint (vh); } } if (rbuf_idx[vh] > FIFO_ALARM) vh_crit |= (1 << vh); /* Implement RX FIFO-level flow control */ if (lp != NULL) { if ((lp->lnctrl & LNCTRL_FORCE_XOFF) || ((vh_crit & (1 << vh)) && (lp->lnctrl & LNCTRL_IAUTO))) { int32 chan = RBUF_GETLINE(data); vh_stall[vh] ^= (1 << chan); /* send XOFF every other character received */ if (vh_stall[vh] & (1 << chan)) vh_putc (vh, lp, chan, XOFF); } } return (status); } static int32 fifo_get ( int32 vh ) { int32 data, i; if (rbuf_idx[vh] == 0) { vh_csr[vh] &= ~CSR_RX_DATA_AVAIL; return (0); } /* pick off the first character, mark valid */ data = vh_rbuf[vh][0] | RBUF_DATA_VALID; /* move the remainder up */ rbuf_idx[vh] -= 1; for (i = 0; i < rbuf_idx[vh]; i++) vh_rbuf[vh][i] = vh_rbuf[vh][i + 1]; /* rbuf_idx[vh] -= 1; */ /* look for any previous overruns */ if (vh_ovrrun[vh]) { for (i = 0; i < VH_LINES; i++) { if (vh_ovrrun[vh] & (1 << i)) { fifo_put (vh, NULL, RBUF_OVERRUN_ERR | RBUF_PUTLINE (i)); vh_ovrrun[vh] &= ~(1 << i); break; } } } /* recompute FIFO alarm condition */ if ((rbuf_idx[vh] < FIFO_HALF) && (vh_crit & (1 << vh))) { vh_crit &= ~(1 << vh); /* send XON to all XOFF'd channels on this controller */ for (i = 0; i < VH_LINES; i++) { TMLX *lp = &vh_parm[(vh * VH_LINES) + i]; if (lp->lnctrl & LNCTRL_FORCE_XOFF) continue; if (vh_stall[vh] & (1 << i)) { vh_putc (vh, NULL, i, XON); vh_stall[vh] &= ~(1 << i); } } } return (data & 0177777); } /* TX Q manipulation */ static int32 dq_tx_report ( int32 vh ) { int32 data, i; if (txq_idx[vh] == 0) return (0); data = vh_txq[vh][0]; txq_idx[vh] -= 1; for (i = 0; i < txq_idx[vh]; i++) vh_txq[vh][i] = vh_txq[vh][i + 1]; /* txq_idx[vh] -= 1; */ return (data & 0177777); } static void q_tx_report ( int32 vh, int32 data ) { if (vh_csr[vh] & CSR_TXIE) vh_set_txint (vh); if (txq_idx[vh] >= TXQ_SIZE) { /* BUG: which of the following 2 is correct? */ dq_tx_report (vh); /* return; */ } vh_txq[vh][txq_idx[vh]] = CSR_TX_ACTION | data; txq_idx[vh] += 1; } /* Channel get/put routines */ static void HangupModem ( int32 vh, TMLX *lp, int32 chan ) { if (vh_unit[vh].flags & UNIT_MODEM) lp->lstat &= ~(STAT_DCD|STAT_DSR|STAT_CTS|STAT_RI); if (lp->lnctrl & LNCTRL_LINK_TYPE) /* RBUF<0> = 0 for modem status */ fifo_put (vh, lp, RBUF_DIAG | RBUF_PUTLINE (chan) | ((lp->lstat >> 8) & 0376)); /* BUG: check for overflow above */ } /* TX a character on a line, regardless of the TX enable state */ static t_stat vh_putc ( int32 vh, TMLX *lp, int32 chan, int32 data ) { int32 val; t_stat status = SCPE_OK; /* truncate to desired character length */ data &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH]; switch ((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) { case 0: /* normal */ #if 0 /* check for (external) loopback setting */ switch (vh_loop[vh]) { default: case LOOP_NONE: break; } #endif status = tmxr_putc_ln (lp->tmln, data); if (status == SCPE_LOST) { tmxr_reset_ln (lp->tmln); HangupModem (vh, lp, chan); } else if (status == SCPE_STALL) { /* let's flush and try again */ tmxr_send_buffered_data (lp->tmln); status = tmxr_putc_ln (lp->tmln, data); } break; case 1: /* auto echo */ break; case 2: /* local loopback */ if (lp->lnctrl & LNCTRL_BREAK) val = fifo_put (vh, lp, RBUF_FRAME_ERR | RBUF_PUTLINE (chan)); else val = fifo_put (vh, lp, RBUF_PUTLINE (chan) | data); status = (val < 0) ? SCPE_TTMO : SCPE_OK; break; default: /* remote loopback */ break; } return (status); } /* Retrieve all stored input from TMXR and place in RX FIFO */ static void vh_getc ( int32 vh ) { uint32 i, c; TMLX *lp; for (i = 0; i < VH_LINES; i++) { lp = &vh_parm[(vh * VH_LINES) + i]; while (c = tmxr_getc_ln (lp->tmln)) { if (c & SCPE_BREAK) { fifo_put (vh, lp, RBUF_FRAME_ERR | RBUF_PUTLINE (i)); /* BUG: check for overflow above */ } else { c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH]; fifo_put (vh, lp, RBUF_PUTLINE (i) | c); /* BUG: check for overflow above */ } } } } /* I/O dispatch routines */ static t_stat vh_rd ( int32 *data, int32 PA, int32 access ) { int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; TMLX *lp; switch ((PA >> 1) & 7) { case 0: /* CSR */ *data = vh_csr[vh] | dq_tx_report (vh); vh_csr[vh] &= ~0117400; /* clear the read-once bits */ break; case 1: /* RBUF */ *data = fifo_get (vh); break; case 2: /* LPR */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = lp->lpr; break; case 3: /* STAT/FIFOSIZE */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = (lp->lstat & ~0377) | /* modem status */ #if 0 (64 - tmxr_tqln (lp->tmln)); fprintf (stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln)); #else 64; #endif break; case 4: /* LNCTRL */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = lp->lnctrl; break; case 5: /* TBUFFAD1 */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = lp->tbuf1; break; case 6: /* TBUFFAD2 */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = lp->tbuf2; break; case 7: /* TBUFFCT */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { *data = 0; break; } line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; *data = lp->tbuffct; break; default: /* can't happen */ break; } return (SCPE_OK); } static t_stat vh_wr ( int32 data, int32 PA, int32 access ) { int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; TMLX *lp; switch ((PA >> 1) & 7) { case 0: /* CSR, but no read-modify-write */ if (access == WRITEB) data = (PA & 1) ? (vh_csr[vh] & 0377) | (data << 8) : (vh_csr[vh] & ~0377) | data & 0377; if (data & CSR_MASTER_RESET) { if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) data &= ~CSR_MASTER_RESET; sim_activate (&vh_unit[vh], clk_cosched (tmxr_poll)); /* vh_mcount[vh] = 72; */ /* 1.2 seconds */ vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ } if ((data & CSR_RXIE) == 0) vh_clr_rxint (vh); /* catch the RXIE transition if the FIFO is not empty */ else if (((vh_csr[vh] & CSR_RXIE) == 0) && (rbuf_idx[vh] != 0)) { if (vh_unit[vh].flags & UNIT_MODEDHU) { if (rbuf_idx[vh] > FIFO_ALARM) vh_set_rxint (vh); else if (vh_timer[vh] == 0) ; else if (vh_timer[vh] == 1) vh_set_rxint (vh); else if (vh_timeo[vh] == 0) vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; } else { vh_set_rxint (vh); } } if ((data & CSR_TXIE) == 0) vh_clr_txint (vh); else if (((vh_csr[vh] & CSR_TXIE) == 0) && (txq_idx[vh] != 0)) vh_set_txint (vh); vh_csr[vh] = (vh_csr[vh] & ~((uint16) CSR_RW)) | (data & (uint16) CSR_RW); break; case 1: /* TXCHAR/RXTIMER */ if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (vh_unit[vh].flags & UNIT_MODEDHU) { if (CSR_GETCHAN (vh_csr[vh]) != 0) break; if (access == WRITEB) data = (PA & 1) ? (vh_timer[vh] & 0377) | (data << 8) : (vh_timer[vh] & ~0377) | (data & 0377); vh_timer[vh] = data & 0377; #if 0 if (vh_csr[vh] & CSR_RXIE) { if (rbuf_idx[vh] > FIFO_ALARM) vh_set_rxint (vh); else if (vh_timer[vh] == 0) ; else if (vh_timer[vh] == 1) vh_set_rxint (vh); else if (vh_timeo[vh] == 0) vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; } #endif } else { line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->txchar & 0377) | (data << 8) : (lp->txchar & ~0377) | (data & 0377); lp->txchar = data; /* TXCHAR */ if (lp->txchar & TXCHAR_TX_DATA_VALID) { if (lp->tbuf2 & TB2_TX_ENA) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), lp->txchar); q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); lp->txchar &= ~TXCHAR_TX_DATA_VALID; } } break; case 2: /* LPR */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->lpr & 0377) | (data << 8) : (lp->lpr & ~0377) | data & 0377; /* Modify only if CSR<3:0> == 0 */ if (CSR_GETCHAN (vh_csr[vh]) != 0) data &= ~LPR_DISAB_XRPT; lp->lpr = data; if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) { fifo_put (vh, lp, RBUF_DIAG | RBUF_PUTLINE (CSR_GETCHAN (vh_csr[vh])) | BMP_OK); /* BUG: check for overflow above */ lp->lpr &= ~(LPR_M_DIAG << LPR_V_DIAG); } break; case 3: /* STAT/FIFODATA */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (vh_unit[vh].flags & UNIT_MODEDHU) { /* high byte writes not allowed */ if (PA & 1) break; /* transmit 1 or 2 characters */ if (!(lp->tbuf2 & TB2_TX_ENA)) break; vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data); q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); if (access != WRITEB) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data >> 8); } break; case 4: /* LNCTRL */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->lnctrl & 0377) | (data << 8) : (lp->lnctrl & ~0377) | data & 0377; /* catch the abort TX transition */ if (!(lp->lnctrl & LNCTRL_TX_ABORT) && (data & LNCTRL_TX_ABORT)) { if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) { lp->tbuf2 &= ~TB2_TX_DMA_START; q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); } } /* Implement program-initiated flow control */ if ( (data & LNCTRL_FORCE_XOFF) && !(lp->lnctrl & LNCTRL_FORCE_XOFF) ) { if (!(lp->lnctrl & LNCTRL_IAUTO)) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF); } else if ( !(data & LNCTRL_FORCE_XOFF) && (lp->lnctrl & LNCTRL_FORCE_XOFF) ) { if (!(lp->lnctrl & LNCTRL_IAUTO)) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); else if (!(vh_crit & (1 << vh)) && (vh_stall[vh] & (1 << CSR_GETCHAN (vh_csr[vh])))) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); } if ( (data & LNCTRL_IAUTO) && /* IAUTO 0->1 */ !(lp->lnctrl & LNCTRL_IAUTO) ) { if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) { if (vh_crit & (1 << vh)) { vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF); vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])); } } else { /* vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])) */; } } else if ( !(data & LNCTRL_IAUTO) && (lp->lnctrl & LNCTRL_IAUTO) ) { if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); } /* check modem control bits */ if ( !(data & LNCTRL_DTR) && /* DTR 1->0 */ (lp->lnctrl & LNCTRL_DTR)) { if ((lp->tmln->conn) && (vh_unit[vh].flags & UNIT_HANGUP)) { tmxr_linemsg (lp->tmln, "\r\nLine hangup\r\n"); tmxr_reset_ln (lp->tmln); } HangupModem (vh, lp, CSR_GETCHAN (vh_csr[vh])); } lp->lnctrl = data; lp->tmln->rcve = (data & LNCTRL_RX_ENA) ? 1 : 0; tmxr_poll_rx (&vh_desc); vh_getc (vh); if (lp->lnctrl & LNCTRL_BREAK) vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), 0); break; case 5: /* TBUFFAD1 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->tbuf1 & 0377) | (data << 8) : (lp->tbuf1 & ~0377) | data & 0377; lp->tbuf1 = data; break; case 6: /* TBUFFAD2 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->tbuf2 & 0377) | (data << 8) : (lp->tbuf2 & ~0377) | data & 0377; lp->tbuf2 = data; /* if starting a DMA, clear DMA_ERR */ if (vh_unit[vh].flags & UNIT_FASTDMA) { doDMA (vh, CSR_GETCHAN (vh_csr[vh])); tmxr_send_buffered_data (lp->tmln); } break; case 7: /* TBUFFCT */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) break; line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); lp = &vh_parm[line]; if (access == WRITEB) data = (PA & 1) ? (lp->tbuffct & 0377) | (data << 8) : (lp->tbuffct & ~0377) | data & 0377; lp->tbuffct = data; break; default: /* can't happen */ break; } return (SCPE_OK); } static void doDMA ( int32 vh, int32 chan ) { int32 line, status; uint32 pa; TMLX *lp; line = (vh * VH_LINES) + chan; lp = &vh_parm[line]; if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) { /* BUG: should compare against available xmit buffer space */ pa = lp->tbuf1; pa |= (lp->tbuf2 & TB2_M_TBUFFAD) << 16; status = chan << CSR_V_TX_LINE; while (lp->tbuffct) { uint8 buf; if (Map_ReadB (pa, 1, &buf)) { status |= CSR_TX_DMA_ERR; lp->tbuffct = 0; break; } if (vh_putc (vh, lp, chan, buf) != SCPE_OK) break; /* pa = (pa + 1) & PAMASK; */ pa = (pa + 1) & ((1 << 22) - 1); lp->tbuffct--; } lp->tbuf1 = pa & 0177777; lp->tbuf2 = (lp->tbuf2 & ~TB2_M_TBUFFAD) | ((pa >> 16) & TB2_M_TBUFFAD); if (lp->tbuffct == 0) { lp->tbuf2 &= ~TB2_TX_DMA_START; q_tx_report (vh, status); } } } /* Perform many of the functions of PROC2 */ static t_stat vh_svc ( UNIT *uptr ) { int32 vh, newln, i; /* scan all muxes for countdown reset */ for (vh = 0; vh < VH_MUXES; vh++) { if (vh_csr[vh] & CSR_MASTER_RESET) { if (vh_mcount[vh] != 0) vh_mcount[vh] -= 1; else vh_clear (vh, FALSE); } } /* sample every 10ms for modem changes (new connections) */ newln = tmxr_poll_conn (&vh_desc); if (newln >= 0) { TMLX *lp; int32 line; vh = newln / VH_LINES; /* determine which mux */ line = newln - (vh * VH_LINES); lp = &vh_parm[newln]; lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; if (!(lp->lnctrl & LNCTRL_DTR)) lp->lstat |= STAT_RI; if (lp->lnctrl & LNCTRL_LINK_TYPE) fifo_put (vh, lp, RBUF_DIAG | RBUF_PUTLINE (line) | ((lp->lstat >> 8) & 0376)); /* BUG: should check for overflow above */ } /* scan all muxes, lines for DMA to complete; start every 3.12ms */ for (vh = 0; vh < VH_MUXES; vh++) { for (i = 0; i < VH_LINES; i++) doDMA (vh, i); } /* interrupt driven in a real DHQ */ tmxr_poll_rx (&vh_desc); for (vh = 0; vh < VH_MUXES; vh++) vh_getc (vh); tmxr_poll_tx (&vh_desc); /* scan all DHU-mode muxes for RX FIFO timeout */ for (vh = 0; vh < VH_MUXES; vh++) { if (vh_unit[vh].flags & UNIT_MODEDHU) { if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) { vh_timeo[vh] -= 1; if ((vh_timeo[vh] == 0) && rbuf_idx[vh]) vh_set_rxint (vh); } } } sim_activate (uptr, tmxr_poll); /* requeue ourselves */ return (SCPE_OK); } /* init a channel on a controller */ /* set for: send/receive 9600 8 data bits 1 stop bit no parity parity odd auto-flow off RX disabled TX enabled no break on line no loopback link type set to data-leads only DTR & RTS off DMA character counter 0 DMA start address registers 0 TX_DMA_START 0 TX_ABORT 0 auto-flow reports enabled FIFO size set to 64 */ static void vh_init_chan ( int32 vh, int32 chan ) { int32 line; TMLX *lp; line = (vh * VH_LINES) + chan; lp = &vh_parm[line]; lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) | (RATE_9600 << LPR_V_RX_SPEED) | (03 << LPR_V_CHAR_LGTH); lp->lnctrl = 0; lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI); if (vh_unit[vh].flags & UNIT_MODEDHU) lp->lstat |= STAT_DHUID | 64; if (!(vh_unit[vh].flags & UNIT_MODEM)) lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; lp->tmln->xmte = 1; lp->tmln->rcve = 0; lp->tbuffct = 0; lp->tbuf1 = 0; lp->tbuf2 = TB2_TX_ENA; lp->txchar = 0; } /* init a controller; flag true if BINIT, false if master.reset */ static t_stat vh_clear ( int32 vh, t_bool flag ) { int32 i; txq_idx[vh] = 0; rbuf_idx[vh] = 0; /* put 8 diag bytes in FIFO: 6 SELF_x, 2 circuit revision codes */ if (vh_csr[vh] & CSR_SKIP) { fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_SKIP); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); } else { fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_NULL); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_NULL); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_NULL); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_NULL); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_NULL); fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_NULL); /* PROC2 ver. 1 */ fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); /* PROC1 ver. 1 */ fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); } vh_csr[vh] &= ~(CSR_TX_ACTION|CSR_DIAG_FAIL|CSR_MASTER_RESET); if (flag) vh_csr[vh] &= ~(CSR_TXIE|CSR_RXIE|CSR_SKIP); vh_csr[vh] |= CSR_TX_DMA_ERR | (CSR_M_TX_LINE << CSR_V_TX_LINE); vh_clr_rxint (vh); vh_clr_txint (vh); vh_timer[vh] = 1; vh_timeo[vh] = 0; vh_ovrrun[vh] = 0; for (i = 0; i < VH_LINES; i++) vh_init_chan (vh, i); vh_crit &= ~(1 << vh); vh_stall[vh] = 0; vh_loop[vh] = LOOP_NONE; return (SCPE_OK); } /* Reset all controllers. Used by BINIT and RESET. */ static t_stat vh_reset ( DEVICE *dptr ) { int32 i; for (i = 0; i < (VH_MUXES * VH_LINES); i++) vh_parm[i].tmln = &vh_ldsc[i]; for (i = 0; i < VH_MUXES; i++) { #if defined (VM_PDP11) /* if Unibus, force DHU mode */ if (UNIBUS) vh_unit[i].flags |= UNIT_MODEDHU; #endif vh_clear (i, TRUE); } vh_rxi = vh_txi = 0; CLR_INT (VHRX); CLR_INT (VHTX); for (i = 0; i < VH_MUXES; i++) sim_cancel (&vh_unit[i]); return (auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : VH_MUXES)); } static t_stat vh_attach ( UNIT *uptr, char *cptr ) { if (uptr == &vh_unit[0]) return (tmxr_attach (&vh_desc, uptr, cptr)); return (SCPE_NOATT); } static t_stat vh_detach ( UNIT *uptr ) { return (tmxr_detach (&vh_desc, uptr)); } static void debug_line ( FILE *st, int32 vh, int32 chan ) { int32 line; TMLX *lp; line = (vh * VH_LINES) + chan; lp = &vh_parm[line]; fprintf (st, "\tline %d\tlpr %06o, lnctrl %06o, lstat %06o\n", chan, lp->lpr, lp->lnctrl, lp->lstat); fprintf (st, "\t\ttbuffct %06o, tbuf1 %06o, tbuf2 %06o, txchar %06o\n", lp->tbuffct, lp->tbuf1, lp->tbuf2, lp->txchar); fprintf (st, "\t\ttmln rcve %d xmte %d\n", lp->tmln->rcve, lp->tmln->xmte); } static t_stat vh_show_debug ( FILE *st, UNIT *uptr, int32 val, void *desc ) { int32 i, j; fprintf (st, "VH:\trxi %d, txi %d\n", vh_rxi, vh_txi); for (i = 0; i < VH_MUXES; i++) { fprintf (st, "VH%d:\tmode %s, crit %d\n", i, vh_unit[i].flags & UNIT_MODEDHU ? "DHU" : "DHV", vh_crit & (1 << i)); fprintf (st, "\tCSR %06o, mcount %d, rbuf_idx %d, txq_idx %d\n", vh_csr[i], vh_mcount[i], rbuf_idx[i], txq_idx[i]); for (j = 0; j < VH_LINES; j++) debug_line (st, i, j); } return (SCPE_OK); } static t_stat vh_show_rbuf ( FILE *st, UNIT *uptr, int32 val, void *desc ) { int32 i; for (i = 0; i < rbuf_idx[0]; i++) fprintf (st, "%03d: %06o\n", i, vh_rbuf[0][i]); return (SCPE_OK); } static t_stat vh_show_txq ( FILE *st, UNIT *uptr, int32 val, void *desc ) { int32 i; for (i = 0; i < txq_idx[0]; i++) fprintf (st, "%02d: %06o\n\r", i, vh_txq[0][i]); return (SCPE_OK); } simh-3.8.1/PDP11/pdp11_pclk.c0000644000175000017500000003030111110156650013521 0ustar vlmvlm/* pdp11_pclk.c: KW11P programmable clock simulator Copyright (c) 1993-2008, Robert M Supnik Written by John Dundas, used with his gracious permission Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. pclk KW11P line frequency clock 20-May-08 RMS Standardized clock delay at 1mips 18-Jun-07 RMS Added UNIT_IDLE flag 07-Jul-05 RMS Removed extraneous externs KW11-P Programmable Clock I/O Page Registers: CSR 17 772 540 CSB 17 772 542 CNT 17 772 544 Vector: 0104 Priority: BR6 ** Theory of Operation ** A real KW11-P is built around the following major components: - 16-bit up/down counter - 16-bit count set buffer - 9-bit control and status register - clocks: crystal controlled (1) 100 kHz and (2) 10 kHz clocks, (3) a 50/60 Hz line frequency clock, and (4) an analog signal input trigger This software emulator for SIMH implements all of the above with the exception of the external input trigger, which is arbitrarily wired to 10Hz. Operation of this emulator is rather simplistic as compared to the actual device. The register read and write routines are responsible for copying internal state from the simulated device to the operating program. Clock state variables are altered in the write routine as well as the desired clock ticking rate. Possible rates are given in the table below. Rate Bit 2 Bit 1 100 kHz 0 0 10 kHz 0 1 Line frequency 1 0 External 1 1 I think SIMH would have a hard time actually keeping up with a 100 kHz ticking rate. I haven't tried this to verify, though. The clock service routine (pclk_svc) is responsible for ticking the clock. The routine does implement up/down, repeat vs. single-interrupt, and single clocking (maintenance). The routine updates the internal state according to the options selected and signals interrupts when appropriate. For a complete description of the device, please see DEC-11-HPWB-D KW11-P Programmable Real-Time Clock Manual. ** Notes ** 1. The device is disabled by default. 2. Use XXDP V2.5 test program ZKWBJ1.BIC; loads at 1000, starts at 1100? Seems to execute the first few tests correctly then waits for input from the console. I don't have a description of how this diagnostic works and thus don't know how to proceed from that point. 3. The read and write routines don't do anything with odd address accesses. The manual says that byte writes don't work. 4. RSTS can use this clock in place of the standard KW11-L line frequency clock. In order to do this, use the DEFAULT response in the OPTION: dialog. To the Preferred clock prompt answer "P". Then you have the option of line frequency "L" or some multiple of 50 between 50 and 1000 to use the programmable portion of the clock. 5. This is really a Unibus peripheral and thus doesn't actually make sense within a J-11 system as there never was a Qbus version of this to the best of my knowledge. However the OSs I have tried don't appear to exhibit any dissonance between this option and the processor/bus emulation. I think the options that would make somewhat more sense in a Qbus environment the KWV11-C and/or KWV11-S. I don't know if any of the -11 OSs contained support for using these as the system clock, though. */ #include "pdp11_defs.h" #define PCLKCSR_RDMASK 0100377 /* readable */ #define PCLKCSR_WRMASK 0000137 /* writeable */ #define UNIT_V_LINE50HZ (UNIT_V_UF + 0) #define UNIT_LINE50HZ (1 << UNIT_V_LINE50HZ) /* CSR - 17772540 */ #define CSR_V_FIX 5 /* single tick */ #define CSR_V_UPDN 4 /* down/up */ #define CSR_V_MODE 3 /* single/repeat */ #define CSR_FIX (1u << CSR_V_FIX) #define CSR_UPDN (1u << CSR_V_UPDN) #define CSR_MODE (1u << CSR_V_MODE) #define CSR_V_RATE 1 /* rate */ #define CSR_M_RATE 03 #define CSR_GETRATE(x) (((x) >> CSR_V_RATE) & CSR_M_RATE) extern int32 int_req[IPL_HLVL]; uint32 pclk_csr = 0; /* control/status */ uint32 pclk_csb = 0; /* count set buffer */ uint32 pclk_ctr = 0; /* counter */ static uint32 rate[4] = { 100000, 10000, 60, 10 }; /* ticks per second */ static uint32 xtim[4] = { 10, 100, 16667, 100000 }; /* nominal time delay */ DEVICE pclk_dev; t_stat pclk_rd (int32 *data, int32 PA, int32 access); t_stat pclk_wr (int32 data, int32 PA, int32 access); t_stat pclk_svc (UNIT *uptr); t_stat pclk_reset (DEVICE *dptr); t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc); void pclk_tick (void); /* PCLK data structures pclk_dev PCLK device descriptor pclk_unit PCLK unit descriptor pclk_reg PCLK register list */ DIB pclk_dib = { IOBA_PCLK, IOLN_PCLK, &pclk_rd, &pclk_wr, 1, IVCL (PCLK), VEC_PCLK, { NULL } }; UNIT pclk_unit = { UDATA (&pclk_svc, UNIT_IDLE, 0) }; REG pclk_reg[] = { { ORDATA (CSR, pclk_csr, 16) }, { ORDATA (CSB, pclk_csb, 16) }, { ORDATA (CNT, pclk_ctr, 16) }, { FLDATA (INT, IREQ (PCLK), INT_V_PCLK) }, { FLDATA (OVFL, pclk_csr, CSR_V_ERR) }, { FLDATA (DONE, pclk_csr, CSR_V_DONE) }, { FLDATA (IE, pclk_csr, CSR_V_IE) }, { FLDATA (UPDN, pclk_csr, CSR_V_UPDN) }, { FLDATA (MODE, pclk_csr, CSR_V_MODE) }, { FLDATA (RUN, pclk_csr, CSR_V_GO) }, { BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT }, { BRDATA (TPS, rate, 10, 32, 4), REG_NZ + PV_LEFT }, { DRDATA (CURTIM, pclk_unit.wait, 32), REG_HRO }, { ORDATA (DEVADDR, pclk_dib.ba, 32), REG_HRO }, { ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO }, { NULL } }; MTAB pclk_mod[] = { { UNIT_LINE50HZ, UNIT_LINE50HZ, "50 Hz", "50HZ", &pclk_set_line }, { UNIT_LINE50HZ, 0, "60 Hz", "60HZ", &pclk_set_line }, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL }, { 0 } }; DEVICE pclk_dev = { "PCLK", &pclk_unit, pclk_reg, pclk_mod, 1, 0, 0, 0, 0, 0, NULL, NULL, &pclk_reset, NULL, NULL, NULL, &pclk_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS }; /* Clock I/O address routines */ t_stat pclk_rd (int32 *data, int32 PA, int32 access) { switch ((PA >> 1) & 03) { case 00: /* CSR */ *data = pclk_csr & PCLKCSR_RDMASK; /* return CSR */ pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */ CLR_INT (PCLK); /* clr intr */ break; case 01: /* buffer */ *data = 0; /* read only */ break; case 02: /* counter */ *data = pclk_ctr & DMASK; /* return counter */ break; } return SCPE_OK; } t_stat pclk_wr (int32 data, int32 PA, int32 access) { int32 old_csr = pclk_csr; int32 rv; switch ((PA >> 1) & 03) { case 00: /* CSR */ pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */ CLR_INT (PCLK); /* clr intr */ rv = CSR_GETRATE (pclk_csr); /* new rate */ pclk_unit.wait = xtim[rv]; /* new delay */ if ((pclk_csr & CSR_GO) == 0) { /* stopped? */ sim_cancel (&pclk_unit); /* cancel */ if (data & CSR_FIX) /* fix? tick */ pclk_tick (); } else if (((old_csr & CSR_GO) == 0) || /* run 0 -> 1? */ (rv != CSR_GETRATE (old_csr))) { /* rate change? */ sim_cancel (&pclk_unit); /* cancel */ sim_activate (&pclk_unit, /* start clock */ sim_rtcn_init (pclk_unit.wait, TMR_PCLK)); } break; case 01: /* buffer */ pclk_csb = pclk_ctr = data; /* store ctr */ pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */ CLR_INT (PCLK); /* clr intr */ break; case 02: /* counter */ break; /* read only */ } return SCPE_OK; } /* Clock tick (automatic or manual) */ void pclk_tick (void) { if (pclk_csr & CSR_UPDN) /* up or down? */ pclk_ctr = (pclk_ctr + 1) & DMASK; /* 1 = up */ else pclk_ctr = (pclk_ctr - 1) & DMASK; /* 0 = down */ if (pclk_ctr == 0) { /* reached zero? */ if (pclk_csr & CSR_DONE) /* done already set? */ pclk_csr = pclk_csr | CSR_ERR; /* set error */ else pclk_csr = pclk_csr | CSR_DONE; /* else set done */ if (pclk_csr & CSR_IE) /* if IE, set int */ SET_INT (PCLK); if (pclk_csr & CSR_MODE) /* if rpt, reload */ pclk_ctr = pclk_csb; else { pclk_csb = 0; /* else clr ctr */ pclk_csr = pclk_csr & ~CSR_GO; /* and clr go */ } } return; } /* Clock service */ t_stat pclk_svc (UNIT *uptr) { int32 rv; pclk_tick (); /* tick clock */ if ((pclk_csr & CSR_GO) == 0) /* done? */ return SCPE_OK; rv = CSR_GETRATE (pclk_csr); /* get rate */ sim_activate (&pclk_unit, sim_rtcn_calb (rate[rv], TMR_PCLK)); return SCPE_OK; } /* Clock reset */ t_stat pclk_reset (DEVICE *dptr) { pclk_csr = 0; /* clear reg */ pclk_csb = 0; pclk_ctr = 0; CLR_INT (PCLK); /* clear int */ sim_cancel (&pclk_unit); /* cancel */ pclk_unit.wait = xtim[0]; /* reset delay */ return SCPE_OK; } /* Set line frequency */ t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc) { if (val == UNIT_LINE50HZ) rate[2] = 50; else rate[2] = 60; return SCPE_OK; } simh-3.8.1/PDP11/pdp11_cr_dat.h0000644000175000017500000007547210432637204014060 0ustar vlmvlm/* pdp11_cr_dat.h * * card code arrays are indexed by 7-bit ASCII code, and * give corresponding 12-bit card codes using the indicated * collating sequence. * * ERROR should be externally defined, either as an illegal * card code (on conversion from ASCII to card codes) or as * a code with a bit set outside the least significant 12. * * author: Douglas Jones, jones@cs.uiowa.edu * revisions: * March 5, 1996 * Feb 18, 1997 to add 026 and EBCDIC converstion tables * Jan 10, 2005, (JAD) Added 'static const' to the array * definitions. * Jan 11, 2005, (JAD) Create the h2c_code array. * Jan 14, 2005, (JAD) Added the special DEC code for 'end of deck' * (12-11-0-1-6-7-8-9) to the o29_code array at position 26. (^Z). * Should I add this to the other arrays? */ /* DEC's version of the IBM 029 kepunch encoding, (thus avoiding IBM's use of non-ASCII punctuation), based on that given in the appendix to Digital's "Small Computer Handbook, 1973", and augmented to translate lower case to upper case. As a result of this modification, inversion of this table should be done with care! */ static const int o29_code[] = { ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,07417,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ }; /* Bare bones 026 kepunch encodings */ static const int o26_ftn_code[] = { ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ 00000,ERROR,ERROR,ERROR,02102,ERROR,ERROR,00042, /* !"#$%&' */ 01042,04042,02042,04000,01102,02000,04102,01400, /* ()*+,-./ */ 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ 00002,00001,ERROR,ERROR,ERROR,00102,ERROR,ERROR, /* 89:;<=>? */ ERROR,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ }; static const int o26_comm_code[] = { ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ 00000,ERROR,ERROR,00102,02102,01042,04000,ERROR, /* !"#$%&' */ ERROR,ERROR,02042,ERROR,01102,02000,04102,01400, /* ()*+,-./ */ 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ 00002,00001,ERROR,ERROR,04042,ERROR,ERROR,ERROR, /* 89:;<=>? */ 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ }; /* FULL EBCDIC, from Appendix C of System 360 Programming by Alex Thomas, 1977, Reinhart Press, San Francisco. Codes not in that table have been left compatable with DEC's 029 table. Some control codes have been left out */ static const int EBCDIC_code[] = { 05403,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ 02011,04021,01021,ERROR,04041,02021,ERROR,ERROR, /* chars */ ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ ERROR,ERROR,ERROR,ERROR,01201,ERROR,ERROR,ERROR, /* chars */ 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ ERROR,05400,05200,05100,05040,05020,05010,05004, /* `abcdefg */ 05002,05001,06400,06200,06100,06040,06020,06010, /* hijklmno */ 06004,06002,06001,03200,03100,03040,03020,03010, /* pqrstuvw */ 03004,03002,03001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ }; static const int h2c_code[4096] = { 0000, 0020, 0010, 0030, 0007, 0027, 0017, 0037, 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0004, 0024, 0014, 0034, 0007, 0027, 0017, 0037, 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0002, 0022, 0012, 0032, 0007, 0027, 0017, 0037, 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0001, 0021, 0011, 0031, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, 0040, 0060, 0050, 0070, 0047, 0067, 0057, 0077, 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0044, 0064, 0054, 0074, 0047, 0067, 0057, 0077, 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0042, 0062, 0052, 0072, 0047, 0067, 0057, 0077, 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0041, 0061, 0051, 0071, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, 0100, 0120, 0110, 0130, 0107, 0127, 0117, 0137, 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0104, 0124, 0114, 0134, 0107, 0127, 0117, 0137, 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0102, 0122, 0112, 0132, 0107, 0127, 0117, 0137, 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0101, 0121, 0111, 0131, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, 0140, 0160, 0150, 0170, 0147, 0167, 0157, 0177, 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0144, 0164, 0154, 0174, 0147, 0167, 0157, 0177, 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0142, 0162, 0152, 0172, 0147, 0167, 0157, 0177, 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0141, 0161, 0151, 0171, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, 0200, 0220, 0210, 0230, 0207, 0227, 0217, 0237, 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0204, 0224, 0214, 0234, 0207, 0227, 0217, 0237, 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0202, 0222, 0212, 0232, 0207, 0227, 0217, 0237, 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0201, 0221, 0211, 0231, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, 0240, 0260, 0250, 0270, 0247, 0267, 0257, 0277, 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0244, 0264, 0254, 0274, 0247, 0267, 0257, 0277, 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0242, 0262, 0252, 0272, 0247, 0267, 0257, 0277, 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0241, 0261, 0251, 0271, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, 0300, 0320, 0310, 0330, 0307, 0327, 0317, 0337, 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0304, 0324, 0314, 0334, 0307, 0327, 0317, 0337, 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0302, 0322, 0312, 0332, 0307, 0327, 0317, 0337, 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0301, 0321, 0311, 0331, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, 0340, 0360, 0350, 0370, 0347, 0367, 0357, 0377, 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0344, 0364, 0354, 0374, 0347, 0367, 0357, 0377, 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0342, 0362, 0352, 0372, 0347, 0367, 0357, 0377, 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0341, 0361, 0351, 0371, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, }; simh-3.8.1/PDP11/pdp11_tu.c0000644000175000017500000012166611112257566013251 0ustar vlmvlm/* pdp11_tu.c - PDP-11 TM02/TU16 TM03/TU45/TU77 Massbus magnetic tape controller Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tu TM02/TM03 magtape 17-May-07 RMS CS1 DVA resides in device, not MBA 29-Apr-07 RMS Fixed bug in setting FCE on TMK (found by Naoki Hamada) 16-Feb-06 RMS Added tape capacity checking 12-Nov-05 RMS Changed default formatter to TM03 (for VMS) 31-Oct-05 RMS Fixed address width for large files 16-Aug-05 RMS Fixed C++ declaration and cast problems 31-Mar-05 RMS Fixed inaccuracies in error reporting 18-Mar-05 RMS Added attached test to detach routine 10-Sep-04 RMS Cloned from pdp10_tu.c Magnetic tapes are represented as a series of variable 8b records of the form: 32b record length in bytes - exact number, sign = error byte 0 byte 1 : byte n-2 byte n-1 32b record length in bytes - exact number, sign = error If the byte count is odd, the record is padded with an extra byte of junk. File marks are represented by a single record length of 0. End of tape is two consecutive end of file marks. */ #if defined (VM_PDP10) #error "PDP-10 uses pdp10_tu.c!" #elif defined (VM_PDP11) #include "pdp11_defs.h" #define DEV_DIS_INIT DEV_DIS #elif defined (VM_VAX) #include "vax_defs.h" #define DEV_DIS_INIT 0 #if (!UNIBUS) #error "Qbus not supported!" #endif #endif #include "sim_tape.h" #define TU_NUMFM 1 /* #formatters */ #define TU_NUMDR 8 /* #drives */ #define USTAT u3 /* unit status */ #define UDENS u4 /* unit density */ #define UD_UNK 0 /* unknown */ #define MT_MAXFR (1 << 16) /* max data buf */ #define DEV_V_TM03 (DEV_V_FFUF + 0) /* TM02/TM03 */ #define DEV_TM03 (1 << DEV_V_TM03) #define UNIT_V_TYPE (MTUF_V_UF + 0) #define UNIT_M_TYPE 03 #define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE) #define UNIT_TE16 (0 << UNIT_V_TYPE) #define UNIT_TU45 (1 << UNIT_V_TYPE) #define UNIT_TU77 (2 << UNIT_V_TYPE) #define GET_TYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE) /* CS1 - offset 0 */ #define CS1_OF 0 #define CS1_GO CSR_GO /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_N_FNC (CS1_M_FNC + 1) #define FNC_NOP 000 /* no operation */ #define FNC_UNLOAD 001 /* unload */ #define FNC_REWIND 003 /* rewind */ #define FNC_FCLR 004 /* formatter clear */ #define FNC_RIP 010 /* read in preset */ #define FNC_ERASE 012 /* erase tape */ #define FNC_WREOF 013 /* write tape mark */ #define FNC_SPACEF 014 /* space forward */ #define FNC_SPACER 015 /* space reverse */ #define FNC_XFER 024 /* >=? data xfr */ #define FNC_WCHKF 024 /* write check */ #define FNC_WCHKR 027 /* write check rev */ #define FNC_WRITE 030 /* write */ #define FNC_READF 034 /* read forward */ #define FNC_READR 037 /* read reverse */ #define CS1_RW 077 #define CS1_DVA 04000 /* drive avail */ #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) /* TUFS - formatter status - offset 1 + indicates kept in drive status ^ indicates calculated on the fly */ #define FS_OF 1 #define FS_SAT 0000001 /* slave attention */ #define FS_BOT 0000002 /* ^beginning of tape */ #define FS_TMK 0000004 /* end of file */ #define FS_ID 0000010 /* ID burst detected */ #define FS_SLOW 0000020 /* slowing down NI */ #define FS_PE 0000040 /* ^PE status */ #define FS_SSC 0000100 /* slave stat change */ #define FS_RDY 0000200 /* ^formatter ready */ #define FS_FPR 0000400 /* formatter present */ #define FS_EOT 0002000 /* +end of tape */ #define FS_WRL 0004000 /* ^write locked */ #define FS_MOL 0010000 /* ^medium online */ #define FS_PIP 0020000 /* +pos in progress */ #define FS_ERR 0040000 /* ^error */ #define FS_ATA 0100000 /* attention active */ #define FS_REW 0200000 /* +rewinding */ #define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ FS_RDY | FS_PE | FS_BOT) /* TUER - error register - offset 2 */ #define ER_OF 2 #define ER_ILF 0000001 /* illegal func */ #define ER_ILR 0000002 /* illegal register */ #define ER_RMR 0000004 /* reg mod refused */ #define ER_MCP 0000010 /* Mbus cpar err NI */ #define ER_FER 0000020 /* format sel err */ #define ER_MDP 0000040 /* Mbus dpar err NI */ #define ER_VPE 0000100 /* vert parity err */ #define ER_CRC 0000200 /* CRC err NI */ #define ER_NSG 0000400 /* non std gap err NI */ #define ER_FCE 0001000 /* frame count err */ #define ER_ITM 0002000 /* inv tape mark NI */ #define ER_NXF 0004000 /* wlock or fnc err */ #define ER_DTE 0010000 /* time err NI */ #define ER_OPI 0020000 /* op incomplete */ #define ER_UNS 0040000 /* drive unsafe */ #define ER_DCK 0100000 /* data check NI */ /* TUMR - maintenance register - offset 03 */ #define MR_OF 3 #define MR_RW 0177637 /* read/write */ /* TUAS - attention summary - offset 4 */ #define AS_OF 4 #define AS_U0 0000001 /* unit 0 flag */ /* TUFC - offset 5 */ #define FC_OF 5 /* TUDT - drive type - offset 6 */ #define DT_OF 6 #define DT_NSA 0100000 /* not sect addr */ #define DT_TAPE 0040000 /* tape */ #define DT_PRES 0002000 /* slave present */ #define DT_TM03 0000040 /* TM03 formatter */ #define DT_OFF 0000010 /* drive off */ #define DT_TU16 0000011 /* TE16 */ #define DT_TU45 0000012 /* TU45 */ #define DT_TU77 0000014 /* TU77 */ /* TUCC - check character, read only - offset 7 */ #define CC_OF 7 #define CC_MBZ 0177000 /* must be zero */ /* TUSN - serial number - offset 8 */ #define SN_OF 8 /* TUTC - tape control register - offset 9 */ #define TC_OF 9 #define TC_V_UNIT 0 /* unit select */ #define TC_M_UNIT 07 #define TC_V_EVN 0000010 /* even parity */ #define TC_V_FMT 4 /* format select */ #define TC_M_FMT 017 #define TC_STD 014 /* standard */ #define TC_CDUMP 015 /* core dump */ #define TC_V_DEN 8 /* density select */ #define TC_M_DEN 07 #define TC_800 3 /* 800 bpi */ #define TC_1600 4 /* 1600 bpi */ #define TC_AER 0010000 /* abort on error */ #define TC_SAC 0020000 /* slave addr change */ #define TC_FCS 0040000 /* frame count status */ #define TC_ACC 0100000 /* accelerating NI */ #define TC_RW 0013777 #define TC_MBZ 0004000 #define TC_RIP ((TC_800 << TC_V_DEN) | (TC_STD << TC_V_FMT)) #define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) #define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) #define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) int32 tucs1 = 0; /* control/status 1 */ int32 tufc = 0; /* frame count */ int32 tufs = 0; /* formatter status */ int32 tuer = 0; /* error status */ int32 tucc = 0; /* check character */ int32 tumr = 0; /* maint register */ int32 tutc = 0; /* tape control */ int32 tu_time = 10; /* record latency */ int32 tu_stopioe = 1; /* stop on error */ static uint8 *xbuf = NULL; /* xfer buffer */ static uint16 *wbuf = NULL; static int32 fmt_test[16] = { /* fmt valid */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }; static int32 dt_map[3] = { DT_TU16, DT_TU45, DT_TU77 }; static char *tu_fname[CS1_N_FNC] = { "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7", "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17", "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR", "WRITE", "31", "32", "33", "READF", "35", "36" "READR" }; extern int32 sim_switches; extern FILE *sim_deb; t_stat tu_mbrd (int32 *data, int32 PA, int32 fmtr); t_stat tu_mbwr (int32 data, int32 PA, int32 fmtr); t_stat tu_svc (UNIT *uptr); t_stat tu_reset (DEVICE *dptr); t_stat tu_attach (UNIT *uptr, char *cptr); t_stat tu_detach (UNIT *uptr); t_stat tu_boot (int32 unitno, DEVICE *dptr); t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tu_go (int32 drv); int32 tu_abort (void); void tu_set_er (int32 flg); void tu_clr_as (int32 mask); void tu_update_fs (int32 flg, int32 drv); t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt); /* TU data structures tu_dev TU device descriptor tu_unit TU unit list tu_reg TU register list tu_mod TU modifier list */ DIB tu_dib = { MBA_TU, 0, &tu_mbrd, &tu_mbwr,0, 0, 0, { &tu_abort } }; UNIT tu_unit[] = { { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } }; REG tu_reg[] = { { GRDATA (CS1, tucs1, DEV_RDX, 6, 0) }, { GRDATA (FC, tufc, DEV_RDX, 16, 0) }, { GRDATA (FS, tufs, DEV_RDX, 16, 0) }, { GRDATA (ER, tuer, DEV_RDX, 16, 0) }, { GRDATA (CC, tucc, DEV_RDX, 16, 0) }, { GRDATA (MR, tumr, DEV_RDX, 16, 0) }, { GRDATA (TC, tutc, DEV_RDX, 16, 0) }, { FLDATA (STOP_IOE, tu_stopioe, 0) }, { DRDATA (TIME, tu_time, 24), PV_LEFT }, { URDATA (UST, tu_unit[0].USTAT, DEV_RDX, 17, 0, TU_NUMDR, 0) }, { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0, TU_NUMDR, PV_LEFT | REG_RO) }, { NULL } }; MTAB tu_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "MASSBUS", "MASSBUS", NULL, &mba_show_num }, #if defined (VM_PDP11) { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", "TM02", &tu_set_fmtr, &tu_show_fmtr }, { MTAB_XTD|MTAB_VDV, 1, NULL, "TM03", &tu_set_fmtr, NULL }, #else { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", NULL, NULL, &tu_show_fmtr }, #endif { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { UNIT_TYPE, UNIT_TE16, "TE16", "TE16", NULL }, { UNIT_TYPE, UNIT_TU45, "TU45", "TU45", NULL }, { UNIT_TYPE, UNIT_TU77, "TU77", "TU77", NULL }, { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", &sim_tape_set_capac, &sim_tape_show_capac, NULL }, { 0 } }; DEVICE tu_dev = { "TU", tu_unit, tu_reg, tu_mod, TU_NUMDR, 10, T_ADDR_W, 1, DEV_RDX, 8, NULL, NULL, &tu_reset, &tu_boot, &tu_attach, &tu_detach, &tu_dib, DEV_MBUS|DEV_UBUS|DEV_QBUS|DEV_DEBUG|DEV_DISABLE|DEV_DIS_INIT|DEV_TM03 }; /* Massbus register read */ t_stat tu_mbrd (int32 *data, int32 ofs, int32 fmtr) { int32 drv; if (fmtr != 0) { /* only one fmtr */ *data = 0; return MBE_NXD; } drv = GET_DRV (tutc); /* get current unit */ tu_update_fs (0, drv); /* update status */ switch (ofs) { /* decode offset */ case CS1_OF: /* MTCS1 */ *data = (tucs1 & CS1_RW) | CS1_DVA; /* DVA always set */ break; case FC_OF: /* MTFC */ *data = tufc; break; case FS_OF: /* MTFS */ *data = tufs & 0177777; /* mask off rewind */ break; case ER_OF: /* MTER */ *data = tuer; break; case AS_OF: /* MTAS */ *data = (tufs & FS_ATA)? AS_U0: 0; break; case CC_OF: /* MTCC */ *data = tucc = tucc & ~CC_MBZ; break; case MR_OF: /* MTMR */ *data = tumr; break; case DT_OF: /* MTDT */ *data = DT_NSA | DT_TAPE | /* fmtr flags */ ((tu_dev.flags & DEV_TM03)? DT_TM03: 0); if (tu_unit[drv].flags & UNIT_DIS) *data |= DT_OFF; else *data |= DT_PRES | dt_map[GET_TYPE (tu_unit[drv].flags)]; break; case SN_OF: /* MTSN */ *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); break; case TC_OF: /* MTTC */ *data = tutc = tutc & ~TC_MBZ; break; default: /* all others */ return MBE_NXR; } return SCPE_OK; } /* Massbus register write */ t_stat tu_mbwr (int32 data, int32 ofs, int32 fmtr) { int32 drv; if (fmtr != 0) /* only one fmtr */ return MBE_NXD; drv = GET_DRV (tutc); /* get current unit */ switch (ofs) { /* decode PA<4:1> */ case CS1_OF: /* MTCS1 */ if (tucs1 & CS1_GO) tu_set_er (ER_RMR); else { tucs1 = data & CS1_RW; if (tucs1 & CS1_GO) return tu_go (drv); } break; case FC_OF: /* MTFC */ if (tucs1 & CS1_GO) tu_set_er (ER_RMR); else { tufc = data; tutc = tutc | TC_FCS; /* set fc flag */ } break; case AS_OF: /* MTAS */ tu_clr_as (data); break; case MR_OF: /* MTMR */ tumr = (tumr & ~MR_RW) | (data & MR_RW); break; case TC_OF: /* MTTC */ if (tucs1 & CS1_GO) tu_set_er (ER_RMR); else { tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; drv = GET_DRV (tutc); } break; case FS_OF: /* MTFS */ case ER_OF: /* MTER */ case CC_OF: /* MTCC */ case DT_OF: /* MTDT */ case SN_OF: /* MTSN */ if (tucs1 & CS1_GO) tu_set_er (ER_RMR); break; /* read only */ default: /* all others */ return MBE_NXR; } /* end switch */ tu_update_fs (0, drv); return SCPE_OK; } /* New magtape command */ t_stat tu_go (int32 drv) { int32 fnc, den; UNIT *uptr; fnc = GET_FNC (tucs1); /* get function */ den = GET_DEN (tutc); /* get density */ uptr = tu_dev.units + drv; /* get unit */ if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=%d\n", drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); if ((fnc != FNC_FCLR) && /* not clear & err */ ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ tu_set_er (ER_ILF); /* set err */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ tu_update_fs (FS_ATA, drv); /* set attn */ return MBE_GOE; } tu_clr_as (AS_U0); /* clear ATA */ tutc = tutc & ~TC_SAC; /* clear addr change */ switch (fnc) { /* case on function */ case FNC_FCLR: /* drive clear */ tuer = 0; /* clear errors */ tutc = tutc & ~TC_FCS; /* clear fc status */ tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR); sim_cancel (uptr); /* reset drive */ uptr->USTAT = 0; case FNC_NOP: tucs1 = tucs1 & ~CS1_GO; /* no operation */ return SCPE_OK; case FNC_RIP: /* read-in preset */ tutc = TC_RIP; /* set tutc */ sim_tape_rewind (&tu_unit[0]); /* rewind unit 0 */ tu_unit[0].USTAT = 0; tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return SCPE_OK; case FNC_UNLOAD: /* unload */ if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ tu_set_er (ER_UNS); break; } detach_unit (uptr); uptr->USTAT = FS_REW; sim_activate (uptr, tu_time); tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return SCPE_OK; case FNC_REWIND: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ tu_set_er (ER_UNS); break; } uptr->USTAT = FS_PIP | FS_REW; sim_activate (uptr, tu_time); tucs1 = tucs1 & ~CS1_GO; tufs = tufs & ~FS_TMK; return SCPE_OK; case FNC_SPACEF: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ tu_set_er (ER_UNS); break; } if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) { tu_set_er (ER_NXF); break; } uptr->USTAT = FS_PIP; goto GO_XFER; case FNC_SPACER: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ tu_set_er (ER_UNS); break; } if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) { tu_set_er (ER_NXF); break; } uptr->USTAT = FS_PIP; goto GO_XFER; case FNC_WCHKR: /* wchk = read */ case FNC_READR: /* read rev */ if (tufs & FS_BOT) { /* beginning of tape? */ tu_set_er (ER_NXF); break; } goto DATA_XFER; case FNC_WRITE: /* write */ if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ tu_set_er (ER_NXF); break; } case FNC_WREOF: /* write tape mark */ case FNC_ERASE: /* erase */ if (sim_tape_wrp (uptr)) { /* write locked? */ tu_set_er (ER_NXF); break; } case FNC_WCHKF: /* wchk = read */ case FNC_READF: /* read */ DATA_XFER: if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ tu_set_er (ER_UNS); break; } if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ tu_set_er (ER_FER); break; } if (uptr->UDENS == UD_UNK) /* set dens */ uptr->UDENS = den; uptr->USTAT = 0; GO_XFER: tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ sim_activate (uptr, tu_time); return SCPE_OK; default: /* all others */ tu_set_er (ER_ILF); /* not supported */ break; } /* end case function */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ tu_update_fs (FS_ATA, drv); /* set attn */ return MBE_GOE; } /* Abort transfer */ t_stat tu_abort (void) { return tu_reset (&tu_dev); } /* Unit service Complete movement or data transfer command Unit must exist - can't remove an active unit Unit must be attached - detach cancels in progress operations */ t_stat tu_svc (UNIT *uptr) { int32 fnc, fmt, j, xbc; int32 fc, drv; t_mtrlnt i, tbc; t_stat st, r = SCPE_OK; drv = (int32) (uptr - tu_dev.units); /* get drive # */ if (uptr->USTAT & FS_REW) { /* rewind or unload? */ sim_tape_rewind (uptr); /* rewind tape */ uptr->USTAT = 0; /* clear status */ tu_update_fs (FS_ATA | FS_SSC, drv); return SCPE_OK; } fnc = GET_FNC (tucs1); /* get command */ fmt = GET_FMT (tutc); /* get format */ fc = 0200000 - tufc; /* get frame count */ uptr->USTAT = 0; /* clear status */ if ((uptr->flags & UNIT_ATT) == 0) { tu_set_er (ER_UNS); /* set formatter error */ if (fnc >= FNC_XFER) mba_set_don (tu_dib.ba); tu_update_fs (FS_ATA, drv); return (tu_stopioe? SCPE_UNATT: SCPE_OK); } switch (fnc) { /* case on function */ /* Non-data transfer commands - set ATA when done */ case FNC_SPACEF: /* space forward */ do { tufc = (tufc + 1) & 0177777; /* incr fc */ if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ r = tu_map_err (drv, st, 0); /* map error */ break; } } while ((tufc != 0) && !sim_tape_eot (uptr)); if (tufc) tu_set_er (ER_FCE); else tutc = tutc & ~TC_FCS; break; case FNC_SPACER: /* space reverse */ do { tufc = (tufc + 1) & 0177777; /* incr wc */ if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ r = tu_map_err (drv, st, 0); /* map error */ break; } } while (tufc != 0); if (tufc) tu_set_er (ER_FCE); else tutc = tutc & ~TC_FCS; break; case FNC_WREOF: /* write end of file */ if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ r = tu_map_err (drv, st, 0); /* map error */ break; case FNC_ERASE: if (sim_tape_wrp (uptr)) /* write protected? */ r = tu_map_err (drv, MTSE_WRP, 0); /* map error */ break; /* Unit service - data transfer commands */ case FNC_READF: /* read */ case FNC_WCHKF: /* wcheck = read */ tufc = 0; /* clear frame count */ if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr)) tufs = tufs | FS_ID; /* PE BOT? ID burst */ if (st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR)) { /* read fwd */ if (st == MTSE_TMK) /* tmk also sets FCE */ tu_set_er (ER_FCE); r = tu_map_err (drv, st, 1); /* map error */ break; /* done */ } for (i = tbc; i < tbc + 4; i++) /* pad with 0's */ xbuf[i] = 0; if (fmt == TC_CDUMP) { /* core dump? */ for (i = j = 0; i < tbc; i = i + 4) { wbuf[j++] = ((uint16) xbuf[i] & 0xF) | (((uint16) (xbuf[i + 1] & 0xF)) << 4) | (((uint16) (xbuf[i + 2] & 0xF)) << 8) | (((uint16) (xbuf[i + 3] & 0xf)) << 12); } xbc = (tbc + 1) >> 1; } else { /* standard */ for (i = j = 0; i < tbc; i = i + 2) { wbuf[j++] = ((uint16) xbuf[i]) | (((uint16) xbuf[i + 1]) << 8); } xbc = tbc; } if (mba_get_bc (tu_dib.ba) > xbc) /* record short? */ tu_set_er (ER_FCE); /* set FCE, ATN */ if (fnc == FNC_WCHKF) mba_chbufW (tu_dib.ba, xbc, wbuf); else mba_wrbufW (tu_dib.ba, xbc, wbuf); tufc = tbc & 0177777; break; case FNC_WRITE: /* write */ xbc = mba_rdbufW (tu_dib.ba, fc, wbuf); /* read buffer */ if (xbc == 0) /* anything?? */ break; if (fmt == TC_CDUMP) { /* core dump? */ for (i = j = 0; j < xbc; j = j + 1) { xbuf[i++] = wbuf[j] & 0xF; xbuf[i++] = (wbuf[j] >> 4) & 0xF; xbuf[i++] = (wbuf[j] >> 8) & 0xF; xbuf[i++] = (wbuf[j] >> 12) & 0xF; } tbc = (xbc + 1) >> 1; } else { /* standard */ for (i = j = 0; j < xbc; j = j + 1) { xbuf[i++] = wbuf[j] & 0377; xbuf[i++] = (wbuf[j] >> 8) & 0377; } tbc = xbc; } if (st = sim_tape_wrrecf (uptr, xbuf, tbc)) /* write rec, err? */ r = tu_map_err (drv, st, 1); /* map error */ else { tufc = (tufc + tbc) & 0177777; if (tufc == 0) tutc = tutc & ~TC_FCS; } break; case FNC_READR: /* read reverse */ case FNC_WCHKR: /* wcheck = read */ tufc = 0; /* clear frame count */ if (st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR)) { /* read rev */ if (st == MTSE_TMK) /* tmk also sets FCE */ tu_set_er (ER_FCE); r = tu_map_err (drv, st, 1); /* map error */ break; /* done */ } for (i = 0; i < 4; i++) xbuf[i] = 0; /* pad with 0's */ if (fmt == TC_CDUMP) { /* core dump? */ for (i = tbc + 3, j = 0; i > 3; i = i - 4) { wbuf[j++] = ((uint16) xbuf[i] & 0xF) | (((uint16) (xbuf[i - 1] & 0xF)) << 4) | (((uint16) (xbuf[i - 2] & 0xF)) << 8) | (((uint16) (xbuf[i - 3] & 0xf)) << 12); } xbc = (tbc + 1) >> 1; } else { /* standard */ for (i = tbc + 3, j = 0; i > 3; i = i - 2) { wbuf[j++] = ((uint16) xbuf[i]) | (((uint16) xbuf[i - 1]) << 8); } xbc = tbc; } if (mba_get_bc (tu_dib.ba) > xbc) /* record short? */ tu_set_er (ER_FCE); /* set FCE, ATN */ if (fnc == FNC_WCHKR) mba_chbufW (tu_dib.ba, xbc, wbuf); else mba_wrbufW (tu_dib.ba, xbc, wbuf); tufc = tbc & 0177777; break; } /* end case */ tucs1 = tucs1 & ~CS1_GO; /* clear go */ if (fnc >= FNC_XFER) { /* data xfer? */ mba_set_don (tu_dib.ba); /* set done */ tu_update_fs (0, drv); /* update fs */ } else tu_update_fs (FS_ATA, drv); /* no, set attn */ if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, ">>TU%d DONE: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=%d\n", drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); return SCPE_OK; } /* Set formatter error */ void tu_set_er (int32 flg) { tuer = tuer | flg; tufs = tufs | FS_ATA; mba_upd_ata (tu_dib.ba, 1); return; } /* Clear attention */ void tu_clr_as (int32 mask) { if (mask & AS_U0) tufs = tufs & ~FS_ATA; mba_upd_ata (tu_dib.ba, tufs & FS_ATA); return; } /* Formatter update status */ void tu_update_fs (int32 flg, int32 drv) { int32 act = sim_is_active (&tu_unit[drv]); tufs = (tufs & ~FS_DYN) | FS_FPR | flg; if (tu_unit[drv].flags & UNIT_ATT) { tufs = tufs | FS_MOL | tu_unit[drv].USTAT; if (tu_unit[drv].UDENS == TC_1600) tufs = tufs | FS_PE; if (sim_tape_wrp (&tu_unit[drv])) tufs = tufs | FS_WRL; if (!act) { if (sim_tape_bot (&tu_unit[drv])) tufs = tufs | FS_BOT; if (sim_tape_eot (&tu_unit[drv])) tufs = tufs | FS_EOT; } } if (tuer) tufs = tufs | FS_ERR; if (tufs && !act) tufs = tufs | FS_RDY; if (flg & FS_ATA) mba_upd_ata (tu_dib.ba, 1); return; } /* Map tape error status */ t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt) { switch (st) { case MTSE_FMT: /* illegal fmt */ case MTSE_UNATT: /* not attached */ tu_set_er (ER_NXF); /* can't execute */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); break; case MTSE_TMK: /* end of file */ tufs = tufs | FS_TMK; break; case MTSE_IOERR: /* IO error */ tu_set_er (ER_VPE); /* flag error */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); return (tu_stopioe? SCPE_IOERR: SCPE_OK); case MTSE_INVRL: /* invalid rec lnt */ tu_set_er (ER_VPE); /* flag error */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); return SCPE_MTRLNT; case MTSE_RECE: /* record in error */ tu_set_er (ER_CRC); /* set crc err */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); break; case MTSE_EOM: /* end of medium */ tu_set_er (ER_OPI); /* incomplete */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); break; case MTSE_BOT: /* reverse into BOT */ return SCPE_OK; case MTSE_WRP: /* write protect */ tu_set_er (ER_NXF); /* can't execute */ if (qdt) /* set exception */ mba_set_exc (tu_dib.ba); break; default: /* unknown error */ return SCPE_IERR; } return SCPE_OK; } /* Reset routine */ t_stat tu_reset (DEVICE *dptr) { int32 u; UNIT *uptr; mba_set_enbdis (MBA_TU, tu_dev.flags & DEV_DIS); tucs1 = 0; tufc = 0; tuer = 0; tufs = FS_FPR | FS_RDY; if (sim_switches & SWMASK ('P')) /* powerup? clr TC */ tutc = 0; else tutc = tutc & ~TC_FCS; /* no, clr */ for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ uptr = tu_dev.units + u; sim_tape_reset (uptr); /* clear pos flag */ sim_cancel (uptr); /* cancel activity */ uptr->USTAT = 0; } if (xbuf == NULL) xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8)); if (xbuf == NULL) return SCPE_MEM; if (wbuf == NULL) wbuf = (uint16 *) calloc ((MT_MAXFR + 4) >> 1, sizeof (uint16)); if (wbuf == NULL) return SCPE_MEM; return SCPE_OK; } /* Attach routine */ t_stat tu_attach (UNIT *uptr, char *cptr) { int32 drv = uptr - tu_dev.units, flg; t_stat r; r = sim_tape_attach (uptr, cptr); if (r != SCPE_OK) return r; uptr->USTAT = 0; /* clear unit status */ uptr->UDENS = UD_UNK; /* unknown density */ flg = FS_ATA | FS_SSC; /* set attention */ if (GET_DRV (tutc) == drv) /* sel drv? set SAT */ flg = flg | FS_SAT; tu_update_fs (flg, drv); /* update status */ return r; } /* Detach routine */ t_stat tu_detach (UNIT* uptr) { int32 drv = uptr - tu_dev.units; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; uptr->USTAT = 0; /* clear status flags */ tu_update_fs (FS_ATA | FS_SSC, drv); /* update status */ return sim_tape_detach (uptr); } /* Set/show formatter type */ t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc) { DEVICE *dptr = find_dev_from_unit (uptr); if (cptr != NULL) return SCPE_ARG; if (dptr == NULL) return SCPE_IERR; if (val) dptr->flags = dptr->flags | DEV_TM03; else dptr->flags = dptr->flags & ~DEV_TM03; return SCPE_OK; } t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc) { DEVICE *dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; fprintf (st, "TM0%d", (dptr->flags & DEV_TM03? 3: 2)); return SCPE_OK; } /* Device bootstrap */ #if defined (PDP11) #elif defined (VM_PDP11) #define BOOT_START 016000 /* start */ #define BOOT_ENTRY (BOOT_START + 002) /* entry */ #define BOOT_UNIT (BOOT_START + 010) /* unit number */ #define BOOT_CSR (BOOT_START + 014) /* CSR */ #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) static const uint16 boot_rom[] = { 0046515, /* "MM" */ 0012706, BOOT_START, /* mov #boot_start, sp */ 0012700, 0000000, /* mov #unit, r0 */ 0012701, 0172440, /* mov #TUCS1, r1 */ 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ 0012711, 0000021, /* mov #RIP+GO, (r1) ; rip */ 0010004, /* mov r0, r4 */ 0052704, 0002300, /* bis #2300, r4 ; set den */ 0010461, 0000032, /* mov r4, 32(r1) ; set unit */ 0012761, 0177777, 0000006, /* mov #-1, 6(r1) ; set fc */ 0012711, 0000031, /* mov #SPCF+GO, (r1) ; skip rec */ 0105761, 0000012, /* tstb 12 (r1) ; fmtr rdy? */ 0100375, /* bpl .-4 */ 0012761, 0177000, 0000002, /* mov #-1000, 2(r1) ; set wc */ 0005061, 0000004, /* clr 4(r1) ; clr ba */ 0005061, 0000006, /* clr 6(r1) ; clr fc */ 0012711, 0000071, /* mov #READ+GO, (r1) ; read */ 0105711, /* tstb (r1) ; wait */ 0100376, /* bpl .-2 */ 0005002, /* clr R2 */ 0005003, /* clr R3 */ 0012704, BOOT_START+020, /* mov #start+020, r4 */ 0005005, /* clr R5 */ 0105011, /* clrb (r1) */ 0005007 /* clr PC */ }; t_stat tu_boot (int32 unitno, DEVICE *dptr) { int32 i; extern int32 saved_PC; extern uint16 *M; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & (TU_NUMDR - 1); M[BOOT_CSR >> 1] = mba_get_csr (tu_dib.ba) & DMASK; saved_PC = BOOT_ENTRY; return SCPE_OK; } #else t_stat tu_boot (int32 unitno, DEVICE *dptr) { return SCPE_NOFNC; } #endif